# Roblox Creator Documentation — Full Content > This file contains the full content of all Roblox Creator Documentation pages, suitable for use as LLM context. --- title: "Creator Affiliate Program" url: /docs/en-us/affiliates last_updated: 2026-06-29T19:33:52Z description: "Learn how to earn Robux by driving users to experiences." --- # Creator Affiliate Program > **Info:** Effective July 24, 2025, the Creator Affiliate program is deprecated and has been replaced by the [Creator Rewards](/docs/en-us/creator-rewards.md) program. The [Creator Affiliate Program](https://create.roblox.com/affiliate) is a pilot program that lets you earn Robux using affiliate links. With it, you can: - Generate affiliate share links for any Roblox experience, including other developers' experiences, through the Creator Hub. - Promote your affiliate links off-platform on social media channels. - Earn revenue in Robux whenever new users create a Roblox account and purchase Robux using your affiliate link. A new user is someone who doesn't already have an alternative Roblox account. - Exchange your Robux earnings for real-world money using the Developer Exchange program. When a user creates a Roblox account using your affiliate link and then buys Robux, you can receive up to 50% of the value of their Robux purchases during their first six months on the platform, for a maximum revenue of $100 USD per new user. For example, if a new user joins Roblox and purchases $100 USD of Robux, you can earn up to $50 USD, minus certain fees like taxes and VAT. You receive your revenue in Robux, which you can exchange for real-world money through Developer Exchange. --- title: "AI on Roblox" url: /docs/en-us/ai/accelerated-workflows last_updated: 2026-06-29T19:33:52Z description: "Learn how to accelerate your creation workflows with AI." --- # AI on Roblox AI on Roblox From ideas to published games, faster than ever Move faster with tools that help you script, build, and generate assets, while having full control over creative decisions. [Download Studio](https://www.roblox.com/download/studio) [See how to create](#watch-and-learn) Bring your own client Roblox Studio 1 Enable AI features Enable MCP and AI features in Studio.▸ Open **Assistant** → **…** → **Manage MCP Servers** and switch on the **Enable Studio as MCP server** toggle. Open **File → Beta Features** and turn on the features you want. Features that are generally available are always available by default. 2 Connect your client Let your MCP-enabled tools interact with Studio.▸ Point your MCP-aware client at the Studio [MCP server](/docs/en-us/studio/mcp.md#connect-your-client). When connected, your client can read the data model, create and run scripts, edit instances, playtest, and more. 3 Build your first game Plan and build your first game using your favorite coding harness.▸ Download the starter project that contains LLM-friendly markdown files for context and have your first game ready in minutes. 4 Customize your setup Build with community and professional tools.▸ With [Rojo and Git](/docs/en-us/projects/external-tools.md), you can build and version control your Roblox projects via the file system. Use [tools](/docs/en-us/art/overview-dcc.md) like Blender and other DCC apps to round out your creation workflow. 1 Enable AI features We're always adding new beta features, so make sure they're enabled.▸ Open **File → Beta Features** and turn on the features you want. Features that are generally available are always available by default. 2 Prompt the Assistant Describe what you want in the Assistant panel.▸ Tell Assistant to build 3D objects, write and run Luau, generate meshes and materials, and playtest alongside you. 3 Build your first game Learn core concepts and build an obby with the Assistant.▸ Our [core curriculum](/docs/en-us/tutorials/curriculums/core.md) teaches you the basics of building, scripting, and polishing an obby game using the Assistant as your trusted companion. 4 Customize your setup Bring your own API keys to use the model you prefer.▸ Open **Assistant** → **…** → **Manage API Keys** to configure your preferred model from Anthropic, OpenAI, or Google. Watch and Learn See how to accelerate your creation workflows Follow along with resources from the Roblox team that show how to set up AI workflows and recently released features. ![AI-assisted Studio workflow](https://img.youtube.com/vi/v8r1d80DxOY/maxresdefault.jpg) × ![Feature walkthrough](https://img.youtube.com/vi/XsY8xhluuZM/maxresdefault.jpg) × ![Creator workflow demo](https://img.youtube.com/vi/ZlXA6QwJrHM/maxresdefault.jpg) × [Build with a coding harness](/docs/en-us/ai/build.md) [Build with the Assistant in Studio](/docs/en-us/tutorials/curriculums/core.md) 3D and AVATAR CREATION Generate 3D content from a phrase Turn a text prompt into a wide array of 3D content that you can drop right into Studio. [

Procedural models

Generate 3D models that automatically adjust to customizable attributes you define.](/docs/en-us/parts/procedural-models.md) [![Interactive 3D models](/assets/modeling/model-generation/Green-Dragon-Car.jpg)
Interactive 3D models
Through text prompts and preset schemas, let your players generate both interactive 3D models in your game, such as vehicles that drive, planes that fly, and weapons that shoot.](/parts/model-generation) [![Avatar Auto-Setup](/assets/art/resources/Archer-Girl-Preview.png)
Avatar Auto-Setup
Automatically rigs, cages, and segments 3D models into fully functional, animatable Roblox avatars.](/avatar-setup/auto-setup) [![Mesh generation](/assets/ai-landing/mesh-gen.jpg)
Mesh generation
Describe what you need in Assistant or with an MCP command and get a textured `MeshPart`.](/assistant/guide#generate-meshes) [![Material generator](/assets/ai-landing/material-generator.png)
Material generator
Seamless, tiling 2D images that can be saved as `MaterialVariants`, ideal for large surfaces like terrain, floors, and walls.](/studio/material-generator) [![Texture generator](/assets/ai-landing/texture-gen.png)
Texture generator
Smart, context-aware textures that understand the geometry of your mesh to apply realistic patterns and wear.](/studio/texture-generator) NATURAL LANGUAGE APIs Process text and speech in real time Bridge the gap between text and voice, enabling more immersive social interactions and dynamic storytelling. ##### Text-to-speech Turn written text into spoken audio at runtime with `AudioTextToSpeech`, whether for narration, UI prompts, or giving your characters a voice. ##### Speech-to-text Capture what players say through their microphone and turn it into text for voice commands, chat, or captions. ##### Text generation Use large language models (LLMs) to power dynamic NPC dialogue. All generated text must be processed through the `TextService:FilterStringAsync()` method. DEVELOPMENT ASSISTANCE Develop faster with coding and debugging tools Keep your momentum while you code. These tools meet you wherever you work, from quick help in the script editor to an agent you can drive from your own editor. ##### Code Assist Get real-time code completions with suggestions based on the context of your script and comments. ![Assistant](/assets/ai-landing/assistant-planning-mode.png) ##### Assistant A conversational partner in Roblox Studio that can run and write code, generate materials, meshes, and models, simulate virtual inputs, and playtest your game. ![Studio MCP server](/assets/ai-landing/mcp-server.png) ##### Studio MCP server Connect any MCP client to your open Studio session, then read, edit, and playtest your place from wherever you work. Policies and guidelines We value your safety and privacy Roblox's AI tools are built with creator trust in mind. You remain in control of how your work and data are used, and every generation runs through the same moderation and [Community Standards](https://about.roblox.com/community-standards) that apply to the rest of the platform. Review the guidelines below to understand your responsibilities as a creator and to configure how your assets contribute to model training. ##### Safety best practices Generative AI accelerates creation, but you are responsible for the content generated within your experiences. Follow these guidelines to stay compliant with Roblox Community Standards. ##### Data and privacy Roblox is committed to transparency about how creator data is used for AI training. Manage your data sharing preferences on Creator Hub.
--- title: "Build with a coding harness" url: /docs/en-us/ai/build last_updated: 2026-06-29T19:33:52Z description: "Connect an AI-enabled text editor to Roblox Studio with Script Sync and MCP." --- # Build with a coding harness This guide and accompanying video walks you through connecting an AI-enabled coding hardness to Roblox Studio via MCP, so that your agent can read, write, and playtest on your behalf. By the end, you'll have a project folder that syncs to Studio through **Script Sync** (a feature that lets you use your own editor to write code), an **MCP** connection that lets your agent drive Studio directly, and a Git repository ready for development. ## Step 1: Install the prerequisites You'll need the following components before getting started: 1. **An AI-enabled text editor** - The most popular options are [Visual Studio Code](https://code.visualstudio.com) and [Cursor](https://cursor.com). Cursor has its AI assistant built in, while VS Code needs an extension like [Claude Code](https://marketplace.visualstudio.com/items?itemName=anthropic.claude-code). Both work well here, so use whichever you prefer. 2. **Roblox Studio** - Download it from [create.roblox.com](https://create.roblox.com/docs/studio/setup). 3. **Git** - Install it from [git-scm.com](https://git-scm.com/install). - On Windows, you can use [WinGet](https://learn.microsoft.com/en-us/windows/package-manager/winget/): `winget install --id Git.Git -e --source winget`. - On macOS, you can use [Homebrew](https://brew.sh/): `brew install git`. ## Step 2: Sign in to everything 1. **Your text editor:** - **VS Code** — Signing in varies by extension. For Claude Code, click the Claude icon in the activity bar, sign in with your Anthropic account, and confirm the chat panel opens. Other providers are similar. - **Cursor** — Open Cursor, sign in with your Cursor account, and confirm the chat panel opens. You can use any model you prefer. 2. **Roblox Studio** — Open Studio, sign in with your Roblox account, and confirm you can create a new place. ## Step 3: Download the starter repository The starter repository gives you the folder layout and a `.gitignore` file so Git only commits the parts of your project that matter. Most of the manual setup is done for you. 1. Download the [starter repository](../assets/solutions/WaveSurvival.zip) and unzip it. It already contains a folder named `WaveSurvival`. 2. In your editor, choose **File → Open Folder** and select `WaveSurvival`. You should see three folders inside: - `ServerScriptService/` holds server scripts, such as game logic, enemy AI, and wave management. - `ReplicatedStorage/` holds shared modules that both the server and client can use. - `StarterPlayerScripts/` holds client scripts that run for each player. The `.gitkeep` files inside each folder are placeholders so that Git tracks the folders while they are still empty. ## Step 4: Create a blank project in Roblox Studio 1. Open Roblox Studio and click **New Experience**. 2. Choose **File → Save to File**, navigate to your `WaveSurvival` folder, and save the place as `WaveSurvival.rbxlx`. The starter repository's `.gitignore` already excludes `.rbxlx` files from Git. 3. Keep Studio open. You will need it for Script Sync and MCP. ## Step 5: Enable the Script Sync beta Script Sync is a beta feature, so you need to turn it on before you can use it. > **Info:** Studio updates often. If you do not see **Script Sync** in the list, install the [latest version of Studio](https://create.roblox.com/download) and try again. 1. In Studio, choose **File → Beta Features**. 2. Find **Script Sync**, enable it, and click **Save**. 3. Restart Studio when prompted. ## Step 6: Connect Script Sync You connect Script Sync once per project by linking each Studio service to its matching folder on disk. 1. In the Explorer, right-click **ServerScriptService**. 2. Choose **Script Sync → Sync to**, then browse to your `WaveSurvival` folder. 3. Repeat for **ReplicatedStorage** and **StarterPlayerScripts**. > **Warning:** Select the top-level `WaveSurvival` folder, not the folders inside it. Because the folders already have the correct names, Script Sync matches each service to the right one automatically. Any `.luau` files you create in those folders now appear as scripts in Studio automatically. ## Step 7: Verify Script Sync In your editor's chat panel, prompt your agent: > Create a test script at `ServerScriptService/Test.server.luau` that prints 'Hello from Script Sync!' to the output. In Studio, check the Explorer. You should see `Test` appear under **ServerScriptService**. You will run a deeper, agent-driven check after MCP is connected. ## Step 8: Enable the MCP connection The Studio MCP server lets your AI agent interact with Studio directly, reading the game tree, creating objects, running scripts, and checking output. It covers anything Script Sync cannot reach, such as **StarterGui** and **StarterPack**. 1. In Studio, open the Assistant window using the **Assistant** button in the upper right. 2. Click **⋯ → Manage MCP Servers**. 3. Confirm MCP is enabled. 4. Under **Quick connect**, enable Cursor or Visual Studio Code. If you installed your editor while Studio was open, you may need to restart Studio, your editor, or both to establish the connection. For more detail, see [Connect to the Roblox Studio MCP server](/docs/en-us/studio/mcp.md). ## Step 9: Verify the MCP connection Prompt your agent: > Use the Roblox MCP to read the current game tree in Roblox Studio. List what's in Workspace. You might need to approve the command or add the MCP tool to an allowlist. For a fresh Baseplate template, the response describes the default `Camera`, `Terrain`, `Baseplate`, and `SpawnLocation` and confirms there is no other content yet. ## Step 10: Run an end-to-end check Prompt your agent to verify the full setup at once: > Use the Roblox MCP connection to verify Script Sync is working properly. Make some changes to Test.server.luau, add new scripts in the other two folders, and verify the output of each. Run `game:GetService("InstanceFileSyncService"):GetStatus(instance)` in each script to verify the sync status. When you're done, delete all three scripts. Watch in Studio and you will see the agent start a playtest to confirm the output. ## Step 11: Commit to Git You now have a clean, working configuration, which makes this a good moment to commit. From your terminal: ```bash git add . git commit -m "Script sync connected and MCP verified" ``` ## Next steps Your project is now synced to Studio, your AI agent can talk to it directly, and you have a Git repository to track changes and progress. From here, you can use the `WAVE_SURVIVAL.md` file from the starter project as context for your agent to build a wave survival game. Follow along with companion video to build the game one tested phase at a time. --- title: "AI data sharing" url: /docs/en-us/ai-data-sharing last_updated: 2026-06-29T19:33:52Z description: "Understand AI data sharing policies and how to update your preferences." --- # AI data sharing Roblox has a suite of AI-powered solutions, including [Code Assist](/docs/en-us/studio/script-editor.md#code-assist), [Material Generator](/docs/en-us/studio/material-generator.md), [Assistant](/docs/en-us/assistant/guide.md), in-experience chat translation, [Texture Generator](/docs/en-us/studio/texture-generator.md), and [Avatar Setup](/docs/en-us/avatar-setup.md). These tools help you quickly find information, accelerate the creation process, and complement your existing skillset. To improve these tools and build new ones, Roblox uses data to train its generative AI models. When you publish an experience in Studio, upload an avatar item, or upload a **paid** asset to the [Creator Store](/docs/en-us/production/creator-store.md), you have the option to share your data. The default choice in the user interface is based on a setting in the [Creator Hub](https://create.roblox.com/). ![An image of the publish experience dialog showing the data sharing toggle.](assets/misc/Data-Share-Dialog.png) - The data sharing setting is **off by default** for experiences, avatar items, and paid assets published prior to July 10, 2024. - The data sharing setting is **on by default** for experiences, avatar items, and paid assets published on or after July 10, 2024. You can change your default choice at any time and override it as desired whenever you publish. > **Info:** Because they're distributed with the intent of others using them, **free** [Creator Store](/docs/en-us/production/creator-store.md) assets are shared by default, with no ability to disable sharing. ## Manage data sharing preferences To set your global data sharing preferences: 1. On the [Creator Hub](https://create.roblox.com/), click your user name and **Settings**. 2. Select **Data Sharing**. 3. Enable or disable **Make sharing data the default option when publishing experiences**. 4. (Optional) Choose individual experiences, avatar items, and paid Creator Store assets to share or not share. ## How Roblox uses data All data shared with Roblox is for AI-enhancement purposes only and is not shared with third parties. Data is processed and stored in accordance with strict security protocols, contains no personal information, is split into tokens (small snippets) for further anonymization, and is retained for only as long as it is beneficial for model improvement. Roblox regularly reviews and purges shared data. If you share something and then later stop sharing it, Roblox will remove it from the training dataset within 30 days. Any AI models that were trained using your data will be updated within 365 days. ##### Sources and ownership of the datasets Roblox's generative AI systems made available to the public are trained on the following, with particular data sources varying for each system: - Data from publicly accessible repositories under permissive licenses or released in the public domain - Non-public data obtained from third-party providers via commercial arrangements - Data from user activity on our platform and from our creators who have not opted-out from model training - Data generated internally, including synthetic data - Data provided by annotation services and other contractors ##### Purpose and methodology of data collection, cleaning, processing, or modification Roblox uses training data to develop or enhance its AI offerings to its creators. Data enables contextual understanding and generation with respect to language capabilities, as well as within virtual, 3D environments, including with respect to materials, textures, geometry, and structures, to enhance details, interactions, and relevance among objects. Data is retained for only as long as it is beneficial for model improvement. Roblox regularly reviews and purges data. ##### Data points included in the datasets, such as types, labels, and counters Training data incorporates various types of (a) written and audio text including in a variety of languages; (b) visual content; and (c) annotations, rating, and preference data. The size of datasets varies by system type and version, but the amount of training can range up to over 3 million distinct assets. ##### Licensing status, indicating whether datasets are copyright-protected, purchased, licensed, or in the public domain Training data may include data subject to third party intellectual property rights. Third party data is derived from data repositories under (a) permissive licenses or released in the public domain; (b) third-party providers via commercial arrangements; and (c) our creators on our platform who have not opted-out from model training. Roblox conducts diligence on training data, including from third-party sources. ##### Inclusion of personal or aggregate consumer information, as defined by the California Consumer Privacy Act Roblox implements processes including filtering to exclude personal information from training generative AI systems available for public use. Creator data is also tokenized to ensure it is not associated with an individual. To the extent data may contain aggregate consumer information, Roblox does not use it to identify or target individuals. Creators on our platform can control their data sharing preferences, including opting-out of their content being used for training, and all users may request deletion of personal information generally. ##### Cleaning, processing, or other modification of datasets by the developer and the respective purpose for each modification Roblox employs various data processing techniques to enhance efficiency, quality, and safety, and to protect sensitive information, including deduplication, classification, and safety and privacy filtering. ##### Use of synthetic data in development and/or training of the system Generated synthetic data is included in datasets used in development and training. ##### Dates when datasets were first used and the time frame during which the data was collected Roblox started compiling and using datasets for generative AI systems in 2023. Such use is ongoing. ##### Additional information For additional information, refer to our model cards and research papers related to our published models. This information is provided pursuant to California Civil Code Section 3111 (AB 2013) and is subject to update. ## Public Luau Previously, Roblox launched a program that allowed you to share your scripts with Roblox, a public Luau dataset, or both. Roblox no longer uses the script data that was shared only with Roblox. Instead, you now decide if you want to share entire experiences, including their scripts. The Luau public dataset is not affected by this change. You can set your sharing preferences on the Creator Hub in the **Public Luau** tab. For more details on the public dataset, see [Empower Luau creation](https://create.roblox.com/data-collection). --- title: "Animation capture" url: /docs/en-us/animation/capture last_updated: 2026-06-29T19:33:52Z description: "Animation Capture in the Animation Editor allows you to generate high-quality, realistic animations for faces and bodies." --- # Animation capture You can record or upload video content to the [Animation Editor](/docs/en-us/animation/editor.md) to quickly capture movement and expressions as animation keyframes. These tools can create unique and realistic animations within minutes while providing access to the animation keyframes for additional adjustments. The animation capture tools allow you to: - [Record your face](#face) with a webcam to puppeteer characters with [animation compatible heads](/docs/en-us/avatar/dynamic-heads.md). - [Upload full-body video content](#body) to generate high quality realistic animations for an entire character. ## Face **Animation Capture - Face** allows you to use your webcam to puppeteer rigs with [animation compatible heads](/docs/en-us/avatar/dynamic-heads.md) and generate corresponding keyframes to your movement. With the ability to record up to 60‑second animations, you can quickly provide both your playable and nonplayable characters the means to realistically grin, raise their eyebrows, drop their jaw, or any other expression necessary for your experience. > **Success:** To ensure that you're able to utilize your camera to record and create facial animations, you must first enable the beta feature through **File** ⟩ **Beta Features** ⟩ **Face Capture**. Before you begin to record your face, ensure that you're in a well lit room close enough to your camera so that your face is in the center of your camera's recording frame. This increases your camera's ability to distinguish you from your surroundings so that you can accurately puppeteer your avatar and create high-quality animations. 1. From the toolbar's **Avatar** tab, click **Clip Editor**. The [Animation Editor](/docs/en-us/animation/editor.md) window displays. 2. In the 3D viewport or the [Explorer](/docs/en-us/studio/explorer.md) hierarchy, select the rig you want your video to animate. Enter a new animation name in the dialog window and click the **Create** button. 3. Navigate to the **track list** and click the **Face Capture** button. A popup displays asking you to acknowledge that you consent to the collection of your facial movements to enable the feature and camera access. 4. **OPTIONAL** If you have multiple cameras, choose which camera you want to use to record. 5. In the controls widget in the 3D viewport, click the red circle. Your camera begins recording your facial movements. 6. When you finish your recording, click either the red square or countdown timer. Keyframes that correspond to your facial movement display within the timeline. 7. **OPTIONAL** If you'd like to delete the recording and try again, click **Re‑Record**. After you finish your recording, you can [fine-tune](/docs/en-us/animation/editor.md#keyframes) the keyframes, [save](/docs/en-us/animation/editor.md#save-an-animation) your animation, then [export](/docs/en-us/animation/editor.md#export-an-animation) it to use across all of your experiences. ## Body The **Animation Capture - Body** allows you to quickly generate high-quality, realistic full-body animations for your R15 rigs by uploading videos to use to track the body's movement and generate corresponding keyframes. This significantly speeds up the manual process of creating animations, and allows you to personalize your avatars and NPCs with life-like mannerisms in a few minutes. > **Success:** To import a video for animation creation, you must first enable the beta feature through **File** ⟩ **Beta Features** ⟩ **Live Animation Creator**. Before you import a video to the **Animation Editor**, ensure the `.mp4` or `.mov` file meets the following requirements: - It includes just one person who is well-lit and visible throughout the video. - It only contains a continuous single shot that's less than 15 seconds from a stable camera. - It adheres to Roblox's [Community Rules](https://en.help.roblox.com/hc/articles/203313410) and [Terms of Use](https://en.help.roblox.com/hc/articles/115004647846). To import a video to create an animation: 1. From the toolbar's **Avatar** tab, click **Clip Editor**. The [Animation Editor](/docs/en-us/animation/editor.md) window displays. 2. In the 3D viewport or the [Explorer](/docs/en-us/studio/explorer.md) hierarchy, select the R15 rig you want your video to animate. Enter a new animation name in the dialog window and click the **Create** button. 3. Navigate to the **media and playback controls** and click the **⋯** button, then navigate to **Import** ⟩ **Live Animation Creator**. 4. In the popup dialog, click the **Choose Video** button. 5. Select the video you want to import from your local machine, then click the **Open** button. After about a minute, keyframes that correspond to movement in your video display within the timeline. After your import is successful, you can [save](/docs/en-us/animation/editor.md#save-an-animation) and [export](/docs/en-us/animation/editor.md#export-an-animation) your animation to use across all of your experiences. --- title: "Curve Editor" url: /docs/en-us/animation/curve-editor last_updated: 2026-06-29T19:33:52Z description: "Curve Editor is a curve-based animation editing interface that lets you see and modify a rig's position and orientation." --- # Curve Editor The **Curve Editor** is a curve-based animation editing interface within the [Animation Editor](/docs/en-us/animation/editor.md) that allows you to see and modify how a rig's position and orientation changes between keyframes through color‑coded curve graphs. It allows you to define independent tracks for the **X**, **Y** and **Z** angles, providing additional levels of control to make your animations more fluid and realistic. Instead of using the default dope sheet editor's method of manually moving the scrubber from one frame to another to see how a rig's position and orientation change over time, the Curve Editor lets you quickly reference position and orientation values of your selected tracks through the **position ruler** on the left side of the timeline and the **rotation ruler** on the right side of the timeline. ![Curve Editor Overview](../assets/animation/curve-editor/UI-Overview.png) ## Open the Curve Editor You can switch the editor's timeline between the dope sheet editor and the Curve Editor at any time. 1. Open the [Animation Editor](/docs/en-us/animation/editor.md) from Studio's **Avatar** tab or the **Window** ⟩ **Avatar** menu.![Animation Editor indicated in Studio's toolbar](../assets/studio/general/Toolbar-Animation.png) 2. **IMPORTANT** Studio automatically converts [quaternions](https://en.wikipedia.org/wiki/Quaternion) to [Euler angles](https://en.wikipedia.org/wiki/Euler_angles) when you open the Curve Editor, so it's important that you verify your rotation type **before** you switch to the Curve Editor. Once you convert quaternions to Euler angle tracks, it's impossible to convert them back into quaternions. 1. In the top-right corner of the editor window, click the settings icon. A contextual menu displays. 2. Hover over **Default Rotation Type**, then select either **Euler Angles** or **Quaternions**. Your rotation type sets to your choice and becomes the default rotation type for future projects. 3. In the top-left corner of the **timeline**, click the **Curve Editor** icon. A popup window displays to confirm that your `Class.KeyframeSequence` clip will convert to a `Class.CurveAnimation` clip.![Opening Curve Editor](../assets/animation/curve-editor/Opening-Curve-Editor.png) > **Info:** If you kept the default rotation type of Euler angles, Studio automatically converts any pre-existing quaternions to Euler angles following the [Euler angles order](#euler-angles-order) setting. Because `Class.KeyframeSequence|KeyframeSequences` work with quaternions and curve animations work with Euler angles by default, your animation might appear slightly different between keyframes, especially if any of your Euler angles approach [Gimbal lock](https://en.wikipedia.org/wiki/Gimbal_lock). > > If you set the rotation type to quaternions, the conversion to curves preserves the quaternions and the animation remains the same. ## Interpolation Interpolation is Studio's process of "filling in" position and orientation values between your keyframes. This allows the rig to seamlessly animate from one orientation or position to another instead of disjointedly jumping between keyframes. While Euler angle curves interpolate between the values of each keyframe, quaternion curves interpolate from `0` to `1` over a set time. To illustrate this, the following images display the same `Class.KeyframeSequence` after you open the Curve Editor with either a Euler angle or a quaternion rotation type: _Euler Angle Curve_ _Quaternion Curve_ For **Euler angle curves**, the height of each keyframe represents its orientation value over time. For **quaternion curves**, each segment represents orientation changing between keyframes, and the line displays the interpolation between them. You can modify how the Curve Editor handles interpolation for both Euler angle curves and quaternion curves by [setting tangents](#set), [changing the interpolation mode](#interpolation-mode), or by [generating interpolation curves](#generate-interpolation-curves) between two or more keyframes. ### Tangents The Curve Editor provides **tangents**, or handles, that let you quickly adjust the interpolation either before or after a keyframe. Euler angle curves have a tangent both before and after the keyframe while quaternion curves have one tangent at the beginning and another at the end of a segment. The initial tangent controls the interpolation after the keyframe and the secondary tangent controls the interpolation before the keyframe. For example: _Euler Angle Curve Tangents_ _Quaternion Curve Tangents_ > **Info:** For quaternion curve tangents, no matter how much you adjust each tangent, each segment always begins at `0` and ends at `1`. Although each segment of a quaternion curve graph is disconnected from subsequent segments, the two keyframes within the same frame always represent the same orientation. Tangents change appearance when you set them to a new position; an unset tangent displays as a clear circle, and a set tangent displays as a white circle. Studio automatically calculates the position for unset tangents based on the position of any set tangents. For instance, if you only set one tangent and leave the other unset, the unset tangent aligns with its opposite tangent. #### Set To set a tangent: 1. Click and drag the tangent in the direction you want to pull the curve. The curve adjusts based on the location of your cursor. 2. Release the tangent. The interpolation between keyframes adjusts accordingly. 3. **OPTIONAL** If you set one tangent and want to align the unset tangent horizontally, right-click the unset tangent and select **Set Tangent to Zero** from the contextual menu. #### Reset If you ever set a tangent to a new position and the resulting interpolation is undesirable, you can always use "auto‑tangent mode" to clear a tangent's value and reset it back to its default interpolation behavior. 1. Right-click on a tangent. A contextual menu displays. 2. Select **Set Tangent To Zero**. The tangent resets to its default interpolation behavior. ### Interpolation mode Interpolation mode is the rate at which an animation moves between different keyframe positions within the animation. There are three interpolation modes available for curve animations: - **Linear** (default) — Moves at a constant speed from one keyframe to another. - **Constant** — Removes interpolation between the selected keyframe and next keyframe, causing the animation to "snap" from keyframe to keyframe. - **Cubic** — Eases in and/or out of the animation. Cubic is the only mode that allows you to define tangents. To change interpolation mode: 1. Right-click a keyframe. A contextual menu displays. 2. Hover over **Interpolation Mode**, then select either **Linear**, **Constant**, or **Cubic**. The interpolation mode changes the curve following the keyframe. ### Generate interpolation curves For curve animations, bounce and elastic easing styles are not available as standard interpolations like they are for `Class.KeyframeSequence|KeyframeSequences`. However, when you convert a `Class.KeyframeSequence` to a curve animation, Studio automatically adds additional keyframes to your animation to keep the animation intact, and you can select two or more keyframes and generate an **interpolation curve** that removes and replaces any of their intermediate keyframes to mimic bounce and elastic easing behavior. To generate interpolation curves: 1. Select two or more keyframes. Each keyframe you select highlights. 2. Right-click one of these keyframes. A contextual menu displays. 3. Hover over **Generate Curve**, then over **Bounce** or **Elastic**, and then select either **In**, **Out**, or **InOut**. An interpolation curve between your selected keyframes generates according to your settings. - **In** — The motion is slower at the beginning and faster toward the end of the keyframe range. - **Out** — The motion is faster at the beginning and slower toward the end of the keyframe range. - **InOut** — **In** and **Out** on the same tween, with **In** at the beginning and **Out** taking effect halfway through the keyframe range. ## Euler angles order When you're working with Euler angles, Studio represents the **X**, **Y**, and **Z** axes through three channel values that follow a specific order to move your rig from its starting orientation to its next orientation. Each order is named after the [matrix multiplications](https://en.wikipedia.org/wiki/Matrix_multiplication) to get to the final orientation. For example, an **XYZ** order (**X**×**Y**×**Z**) means that the rig moves starting on the **Z** axis, then the **Y** axis, then the **X** axis. How an animation changes when you set Euler angles order is dependent on **when** you change the order: - If you load a `Class.KeyframeSequence` or a `Class.CurveAnimation` with a set quaternion rotation type then change the order, the new order doesn't affect the animation. This is because Studio uses quaternions to configure changes in your rig's orientation using the shortest path possible between its starting orientation to its next orientation. However, this method does affect how the quaternions of each keyframe transform into **X**, **Y**, and **Z** angles in the track list, which might visually affect the values within the timeline without affecting the animation itself. - If you change the order of an existing Euler angle track, the animation might change since Studio preserves the values of each keyframe. This means that the ordered path Studio takes to change your rig's orientation from its starting orientation to its next orientation changes with the Euler angles order. To set the Euler angles order: 1. In the top-right corner of the editor window, click the settings icon. A contextual menu displays. 2. Hover over **Default Euler's Angles Order**, then select either **XYZ**, **XZY**, **YXZ**, **YZX**, **ZXY**, or **ZYX**. Your Euler angles order changes accordingly. --- title: "Animation Editor" url: /docs/en-us/animation/editor last_updated: 2026-06-29T19:33:52Z description: "Animation Editor allows you to design and publish custom animations on rigs." --- # Animation Editor The **Animation Editor**, accessible from Studio's **Avatar** tab or **Window** ⟩ **Avatar** menu, allows you to design and publish custom animations on rigs. A rig is an object with individual sections connected by joints. You can move these joints to [create poses](#create-poses) and then smoothly animate the rig from pose to pose. ![Animation Editor indicated in Studio's toolbar](../assets/studio/general/Toolbar-Animation.png) ## Interface The editor's window is divided into three primary sections: #### Media / Playback Controls Animation name. Media and playback options. Position indicator which displays the timeline unit for the position of the scrubber in **seconds:frames**. #### Track List Name of rig. Opens the **Manage IK** window. Opens a contextual menu of additional tracks you can add to the track list. Opens a contextual menu with options such as **Add Keyframe** and **Delete Track**. #### Timeline Scrubber that marks the current time and/or frame. Time and/or frame units. Opens a contextual menu of options including **Timeline Unit**, **Frame Rate**, toggling [Animation Events](/docs/en-us/animation/events.md) on/off, and more. Draggers to expand or contract the time/frame unit marks. ## Create an animation You can only create animations using **rigs**. 1. If you don't already have a rig in the workspace, insert a pre-built rig using the [Rig Generator](/docs/en-us/studio/rig-builder.md) tool. The pre-built rigs have all of the basic parts and mechanisms to build a character animation.![Character button highlighted in Studio's toolbar.](../assets/studio/general/Toolbar-Character.png) 2. In the 3D viewport or the [Explorer](/docs/en-us/studio/explorer.md), select the rig. From here, you can begin [creating poses](#create-poses) for your rig and modifying the animation settings, such as [looping the animation](#loop-an-animation) or [setting its priority](#set-a-priority). ## Create poses An animation consists of different **poses**, or specific positions and orientations of `Class.Bone` or `Class.MeshPart` objects within a rig. You can create poses by moving or rotating bones or meshes, such as the rig's hands, feet, or torso. After you create multiple poses on different positions of the timeline, the animation editor runs between them with your [easing settings](#easing) to smoothly animate the rig from pose-to-pose. For example, a simple animation where a humanoid character turns to look 45° to the left has two poses: The initial position looking forward, and the turned position looking left. To create a pose: 1. In the [Explorer](/docs/en-us/studio/explorer.md) window, select the rig and expand its child instances to access the bones or meshes. 2. In the 3D viewport or [Explorer](/docs/en-us/studio/explorer.md), select a bone or mesh. 3. In the timeline, click-and-move the **scrubber** to the frame position where you want to set the pose. By default, Roblox represents timeline units as **seconds:frames** and animations run at 30 frames per second. For example, **0:15** indicates ½ second. > **Info:** By default, the timeline displays a duration of 1 second (1:00). To add more time to the timeline, enter a new value in the right-side box of the position indicator. 4. Move or rotate the bone or mesh to a new orientation. For bones, you can rotate them to pose the rig along the created bone joints. Whenever you adjust a specific bone or mesh, a new track displays in the track list and a new **keyframe** displays on the timeline for that bone or mesh at that specific frame position. > **Info:** When creating poses, you can toggle between **Move** and **Rotate** modes by pressing `R`. These modes work exactly like moving and rotating base objects, including the snap settings and incremental values located in Studio's toolbar. 5. When you're ready to preview the animation, click the **Play** button in the editor's playback controls or press the `Spacebar`. ## Keyframes Once you [create basic poses](#create-poses) for a rig, fine-tuning individual keyframes can significantly improve the final animation. You can either: - Operate on a specific track or keyframe by clicking or right-clicking on that track/keyframe. - Operate on **all** tracks by clicking or right-clicking in the upper region of the track list. | Operation | Workflow | | --- | --- | | **Insert keyframe(s)** | Right-click and select **Add Keyframe Here**. Note that the keyframe(s) insert at the time position closest to where you click, not at the position of the scrubber. | | **Move keyframe(s)** | Select one or more keyframes and then drag them to a new time position. | | **Duplicate keyframe(s)** | Select one or more keyframes and copy them (`Ctrl``C` or `⌘``C`). Then move the scrubber to the target time position and paste the copied frames (`Ctrl``V` or `⌘``V`). | | **Delete keyframe(s)** | Select one or more keyframes and press `Delete` or `Backspace`. | ### Easing For each keyframe in the animation editor, you can choose both an [easing style](#easing-style) and an [easing direction](#easing-direction). #### Easing style Easing **style** is the rate at which an animation moves between different frame positions within the animation. By default, a part will move and/or rotate from one keyframe to the next in an even, steady motion known as **linear easing**. In the following video, linear easing makes the character's turning animation appear stiff and robotic. _Linear easing_ While that may look appropriate for some motions, compare the following video where **cubic easing** makes the animation of the character's motion appear more natural. _Cubic easing_ To change the easing style for one or more keyframes: 1. Select one or more keyframes. Every selected keyframe is now surrounded by a blue border. 2. Right-click on a keyframe with a border. A pop-up menu displays. 3. Hover over **Easing Style** and choose from the following options: - **Linear** — Moves at a constant speed. - **Constant** — Removes interpolation between the selected keyframe and next keyframe. The animation will "snap" from keyframe to keyframe. - **CubicV2** — Eases in or out with cubic interpolation. - **Elastic** — Moves as if the object is attached to a rubber band. - **Bounce** — Moves as if the start or end position of the tween is bouncy. #### Easing direction Easing **direction** defines which end of the animation movement is affected by the [easing style](#easing-style). By default, the motion is slower at the beginning and faster toward the end of the animation. To change the easing direction for one or more keyframes: 1. Select one or more keyframes. Every selected keyframe is now surrounded by a blue border. 2. Right-click on a keyframe with a border. A pop-up menu displays. 3. Hover over **Easing Direction** and choose from the following options: - **In** — The motion will be slower at the beginning and faster toward the end. - **Out** — The motion will be faster at the beginning and slower toward the end. - **InOut** — **In** and **Out** on the same tween, with **In** at the beginning and **Out** taking effect halfway through. ### Optimize keyframes Animators can often generate many keyframes during the course of animation, especially when using various animation tools and features. To help reduce the number of unnecessary keyframes and make it easier to animate on the timeline, the animation editor provides tools for [automatic](#automatic) and [on-demand](#on-demand) keyframe optimization. #### Automatic The animation editor automatically detects and removes unnecessary keyframes when creating [facial animations](/docs/en-us/art/characters/facial-animation/animate-heads.md) and when [promoting a keyframe animation to a curve animation](/docs/en-us/animation/curve-editor.md#open-the-curve-editor). If 3 or more consecutive keyframes have the same value in a track, the editor removes the intermediary keyframes and keeps only the first and last keyframes. If the track only contains keyframes with default values, such as an `Datatype.CFrame.identity|identity` coordinate frame, or a `0` value for a curve animation, the entire track is removed from the animation. #### On-demand During animating, you can use the editor's **Keyframe Optimization** tool to quickly reduce the number of unnecessary keyframes. Keyframe Optimization prioritizes the least impactful keyframes first. You can adjust the number of keyframes using the slider. While using the slider, you can preview the animation and scrub through the timeline to check your animation but you can not perform editing operations, such as changing keyframe values or adding tracks. To access the **Optimize Keyframes** tool: 1. From the editor's media/playback controls, click the **⋯** button and select **Optimize Keyframes**. 2. In the popup window, move the slider to the desired number of keyframes. You can preview and play back the animation to verify the optimization. 3. Click **OK** when complete. ## Loop an animation To make an animation automatically loop, navigate to the media/playback controls and click the **Looping** button. > **Info:** A looping animation doesn't interpolate between the final keyframes and first keyframes. To make the animation loop smoothly, duplicate the first keyframes and use them as the final keyframes. ## Set a priority An animation's **priority** (`Enum.AnimationPriority`) dictates when it will play in an experience. For example, if you play an animation with a higher priority than another animation that's already playing, the new animation will override the old. For example, a "jump" animation should take priority over an "idle" animation so that a character doesn't perform both at the same time. Roblox uses seven levels of priority, ordered here from highest to lowest: **Action4** **Action3** **Action2** **Action** **Movement** **Idle** **Core** To set an animation to a different priority: 1. Navigate to the editor's media/playback controls and click the **⋯** button. 2. Hover over **Set Animation Priority** and choose the desired priority setting. ## Save an animation When you save an animation, Studio saves it as a `Class.KeyframeSequence` object in `Class.ServerStorage` and adds a reference to your rig object. Saving your animation is meant to preserve your animation progress and work. If you intend to use an animation, [export it](#export-an-animation) before referencing the published animation in your experience. To save an animation: 1. Navigate to the editor's media/playback controls and click the **⋯** button. 2. Select **Save** or **Save As** to save the animation in `Class.ServerStorage`. ** Accessing Local Animation Data** Roblox saves animation data locally to `Class.ServerStorage` to preserve your animation work. In most cases, your experience shouldn't directly access this local data and instead should reference a published animation. In the rare cases that your experience requires accessing local data, reference the value of the `Class.ObjectValue` in your rig's `AnimSaves` folder rather than directly accessing the `Class.ServerStorage`. ```lua local rig = workspace.Rig -- Access your local animation data with the value reference in your rig local anim = rig.AnimSaves.Value.[SavedAnimation] ``` > **Warning:** Since local data is stored in `Class.ServerStorage`, it doesn't replicate and isn't available from clients. If your clients need access to that data, you must move the `Class.KeyframeSequence` or `Class.CurveAnimation` objects and their descendants to `Class.ReplicatedStorage`. ## Export an animation When you export an animation to Studio, it becomes available for use in all experiences. This means that you only need to create an animation once, then you can reuse it as many times as you want. > **Warning:** If your animation will be used for a **default** Roblox character animation like jumping or running, ensure that you rename the final keyframe as outlined in the first step below. To export an animation: 1. **IMPORTANT** If the animation will be used to [replace a default character animation](/docs/en-us/animation/using.md#replace-default-animations) like running or jumping, rename the final keyframe `End` as follows: 1. Right-click the final keyframe symbol in the upper bar region and choose **Rename Key Keyframe** from the contextual menu. 2. Type `End` (**case-sensitive**) into the input field. 3. Click the **Save** button. 2. Navigate to the editor's media/playback controls and click the **⋯** button. 3. Select **Publish to Roblox** from the contextual menu. 4. In the **Asset Configuration** window, enter an animation title and optional description. 5. **IMPORTANT** If the animation will be used in any [group-owned](/docs/en-us/projects/groups.md) experience, select the group from the **Creator** field. 6. Click the **Submit** button. Once the upload is complete, you can copy the animation's asset ID from the [Toolbox](/docs/en-us/projects/assets/toolbox.md) for scripting custom animations or to replace default character animations, as outlined in [Use animations](/docs/en-us/animation/using.md). --- title: "Animation events" url: /docs/en-us/animation/events last_updated: 2026-06-29T19:33:52Z description: "Animation Events are markers across the timeline span in the Animation Editor." --- # Animation events You can define animation **event markers** across the timeline span and use `Class.AnimationTrack:GetMarkerReachedSignal()|GetMarkerReachedSignal()` to detect those markers as the animation runs. By default, the event track isn't visible. To show the event track: 1. Navigate to the right of the timeline and click the settings icon. A pop-up menu displays. 2. Select **Show Animation Events**. This opens the **Animation Events** bar directly below the media and playback controls. ## Create events Event markers are visual indicators of where an animation event begins. After you create an event marker, you can move it to any frame position on the timeline. To create a new event marker: 1. Navigate to the timeline, then click-and-drag the scrubber to the frame position where the event should occur. 2. Navigate to the **event track**, then click the **Edit Animation Events** button. 3. In the dialog, click **+ Add Event**, then enter an event name. 4. **OPTIONAL** In the **Parameter** field, enter a parameter string for the event. 5. Click the **Save** button. In the events bar within the timeline, a new marker symbol displays at the frame position. ## Detect events To detect animation events in a `Class.LocalScript`, connect a function to the `Class.AnimationTrack:GetMarkerReachedSignal()|GetMarkerReachedSignal()` function of `Class.AnimationTrack`. For example: ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local character = player.Character or player.CharacterAdded:Wait() local humanoid = character:WaitForChild("Humanoid") local animator = humanoid:WaitForChild("Animator") -- Create new "Animation" instance local walkAnim = Instance.new("Animation") -- Set its "AnimationId" to the corresponding animation asset ID walkAnim.AnimationId = "rbxassetid://5432167890" -- Load animation onto the animator local walkAnimTrack = animator:LoadAnimation(walkAnim) -- Connect "GetMarkerReachedSignal" event to a specific named keyframe walkAnimTrack:GetMarkerReachedSignal("FootStep"):Connect(function(paramString) print(paramString) end) ``` > **Info:** You can specify a **Parameter** value for any event marker within the animation editor. This lets you pass a custom **string** (single value, comma-separated string, etc.) to the `Class.AnimationTrack:GetMarkerReachedSignal()|GetMarkerReachedSignal()` method, as illustrated by the `paramString` argument in the code example above. This string can then be parsed or converted, if necessary, and used for whatever action you wish to perform in the event. ## Duplicate events As you create events, they become available for usage throughout the whole animation, not only at the frame position where you originally created them. For instance, you can create a `FootStep` event marker at the point where a character's **left** foot touches down, then use the same event when the character's **right** foot touches down. To duplicate an event: 1. Right-click an **event marker** in the event bar, then select **Copy Selected** from the contextual menu. 2. Right-click at another time along the timeline and select **Paste Events**. The copied event inserts at the time position closest to where you click. 3. **OPTIONAL** If the original event uses a **parameter** but the duplicated event should use a modified parameter, perform the following steps: 1. Right-click the duplicated event marker and select **Edit Animation Event** from the contextual menu. The **Edit Animation Events** dialog displays. 2. Make your changes and click the **Save** button. --- title: "Animation Graph Editor" url: /docs/en-us/animation/graph-editor last_updated: 2026-06-29T19:33:52Z description: "The animation graph editor is a visual, node-based tool that lets you build complex animation logic." --- # Animation Graph Editor > **Success:** This tool is currently in beta. Enable it through **File** ⟩ **Beta Features** ⟩ **Animation Graph**. The **Animation Graph Editor** is a visual, node-based tool that empowers technical artists and animators to build complex animation logic directly within Roblox Studio. By providing a streamlined interface for creating behaviors like blend trees, it removes the traditional dependency on manual scripting for character motion. This system works in tandem with your existing animation workflow: - **Animation Editor**: Continue using the [Animation Editor](/docs/en-us/animation/editor.md) as your primary tool for authoring individual clips and fine-tuning keyframes and curves. - **Animation Graph Editor**: Use this new workspace to take those clips and organize them into a logic tree to drive sophisticated gameplay behavior. Designed to enhance collaboration, the visual graph allows developers to quickly inspect, debug, and understand the logic created by animators. While artists focus on refining interactive motion, developers can still access animation graph nodes programmatically for direct control over blended animations and states. ## Build a graph To begin building logic for an animatable character, access the Animation Graph editor via the Avatar tab in the Studio ribbon. The following steps demonstrate how to initialize a rig and construct a basic node network using default walking and waving animations. For a deeper dive into practical applications, you can explore the [Animation Graph Reference File](https://www.roblox.com/games/92493993350916/Animation-Graph-Editor-Simple-Demo), which contains both foundational and complex implementation examples. To create your own animation graph, similar to the basic example provided in the reference, use the following steps: 1. In Studio, add an animatable rig by navigating to the **Avatar** tab and selecting **Character** ⟩ **My Avatar**.![Character button highlighted in Studio's toolbar.](../assets/studio/general/Toolbar-Character.png) 2. Open the Animation Graph Editor by navigating to **Graph Editor** in the **Avatar** tab.![Animation Graph Editor indicated in Studio's toolbar.](../assets/studio/general/Toolbar-Graph-Editor.png) 3. Select the animatable rig in the 3D viewport and select **Create Graph**. 4. In the Graph Editor, right-click and select **Clip**.![List of nodes in a right-click menu](../assets/animation/graph-editor/Node-List.png) 5. In the new Clip node, set the Animation ID. 1. Select the **Animation ID** dropdown. 2. To submit a specific animation asset ID, click **Import**.![Indicated a field on the animation import module to add Animation ID](../assets/animation/graph-editor/Import-AnimationID.png) 3. In the **Animation ID** field, add the default Walk animation: `507777826`. 4. Select **Import**. 6. Add another clip node by repeating steps 4-5 using the default Wave animation: `507770239`.![Two clip nodes with a populated Animation ID](../assets/animation/graph-editor/Clip-Nodes-With-IDs.png) 7. In the Graph Editor, right-click and select **Add**.![Add node](../assets/animation/graph-editor/Add-Node.png) 8. **Connect the Clip nodes to the Add node** by dragging the top-right output connector to the appropriate port: 1. Connect the **Clip** node with the Walking animation to the **Base** port. 2. Connect the **Clip** node with the Waving animation to the **Additive** port.![Both clip nodes attached to add node](../assets/animation/graph-editor/Connect-To-Add.png) 9. From the Add node, connect the top-right output connector to the Graph Output **Pose** port.![All nodes connected to a final output node](../assets/animation/graph-editor/Connect-The-Clip-Nodes.png) 10. **(Optional)** Assign a parameter to your `Speed` variable. 1. Click and drag the green Speed port to an empty area. A new parameter node displays.![Click and dragging connector from Speed port of Clip node.](../assets/animation/graph-editor/Parameter-Noodle.png) 2. In the top left of the graph editor, use the parameter pane to quickly modify parameters in your nodes. You can also access this [programmatically](#api-integration).![Parameter pane, parameter node, and clip node showing shared values.](../assets/animation/graph-editor/Parameter-Pane.png) 11. Test the animation by pressing the **play button**.![Play button icon for previewing animations](../assets/animation/graph-editor/Play-Preview-Button.png) 12. Try testing various weights, speeds, playmodes, and other animations. For more information on individual nodes, see the [Node reference](#node-reference). > **Success:** Check out the [reference place](https://www.roblox.com/games/92493993350916/Animation-Graph-Editor-Simple-Demo) for different examples of various animation graph configurations. ## API integration Creating and deploying an Animation Graph follows the standard Roblox animation pipeline. After selecting a rig in the Animation Graph Editor, a new `AnimationGraphDefinition` asset is created. This asset serves as the container for your nodes, connections, and parameters. Once your logic is finalized, you publish the graph to receive a standard Asset ID. In your scripts, you interact with these graphs by loading them onto an `Class.Animator` as you would a traditional animation. To drive the graph's internal logic, use `Class.AnimationTrack:SetParameter` to pass real-time values—such as movement speed or state booleans—directly into the graph's variables. ```lua local animation = Instance.new("Animation") animation.AnimationId = "rbxassetid://123456789" -- Your Published Graph ID local animationTrack = animator:LoadAnimation(animation) animationTrack:Play() -- Dynamic parameter updates via RunService game:GetService("RunService").Stepped:Connect(function(_, dt) local currentSpeed = humanoidRootPart.AssemblyLinearVelocity.Magnitude animationTrack:SetParameter("humanoidSpeed", currentSpeed) end) ``` ## Node reference Every animation graph node serves as a logic gate or data source that processes animation data before it reaches the character rig. This section provides a technical breakdown of the functional blocks within the Animation Graph Editor. All nodes currently output an animation pose. Each node section includes: - **Definition** – A summary of the node's purpose and role within the graph. - **Inputs** – The data streams entering the node. Multiple inputs are represented as Input1, Input2, ..., InputN. - **Input properties** – Settings tied directly to a specific input (an Idle input assigned to a Position of 0.5 on a Blend1D node). - **Event Data** – Events emitted or consumed by the node to trigger internal graph logic or external Luau scripts. This behavior may change over the course of the beta development. ### Global event rules For all nodes and [transitions](#transitions), the following rules apply by default: - Events propagate upward from their source node through the graph. Each event carries a weight representing its source's influence in the final blend. If the weight reaches zero at any point, the event is silenced. - Nodes without custom event logic pass all events through unchanged; nodes that blend or select between inputs may scale the weight or block events from non-primary inputs (see per-node Event section). - Marker events that reach the top of the graph can be observed via `Class.AnimationTrack:GetMarkerReachedSignal`. ### Clip ![Clip node](../assets/animation/graph-editor/Clip-Node.png) A reference to an `Class.AnimationClip` asset. This serves as a leaf node in the graph, generating the raw animation data that feeds into other nodes for blending, selection, or modification.
**Inputs**
- None
**Properties**
| Property | Type | Description | | --- | --- | --- | | **Clip** | Asset ID | The animation asset to play (e.g. `rbxassetid://12345`). | | **PlayMode** | `Enum.AnimationNodePlayMode` | Defines the behavior of the clip once it reaches the end of its duration.
  • **Loop (default)**: Automatically restarts from the beginning once the clip finishes.
  • **PingPong**: Plays from start to end, then immediately plays in reverse from end to start.
  • **OnceAndHold**: Plays once and maintains the final pose upon completion.
  • **OnceAndReset**: Plays once and snaps back to the initial start pose upon completion.
| | **Reverse** | Boolean | Controls the direction of playback. | | **Speed** | Number | A multiplier for the playback rate. `0.0` pauses the graph, `1.0` is normal speed, and `2.0` is double speed. | | **Trim** | Boolean | Toggles whether the clip's duration should be truncated. | | **TrimStart** | Number | The absolute timestamp (in seconds) where playback should begin. | | **TrimEnd** | Number | The absolute timestamp (in seconds) where playback should terminate. |
**Event data**
- **Event Handling:** None. This is a leaf node with no children. - **Event Emission:** Reads custom markers embedded in the animation clip (e.g., "Footstep", "WeaponSwing") and emits them as named events at the precise frame they occur. If the clip is trimmed, only markers within the defined `[TrimStart, TrimEnd]` interval (inclusive) are emitted.
### Select ![Select node](../assets/animation/graph-editor/Select-Node.png) Selects between any number of inputs via the Selection property. Whenever the current selection changes, it triggers a new [transition](#transitions).
**Inputs**
- **Input1...InputN**
**Properties**
| Property | Type | Description | | --- | --- | --- | | **Selection** | String | The unique ID of the input to be selected, matching the name of the input connection (e.g., "Walk"). |
**Event data**
- **Event Handling:** Events from the currently selected input pass through with their weight unchanged. - **Event Emission:** Passes through all events from the currently selected input only. During a transition, event emission follows [global event rules](#global-event-rules).
### PrioritySelect ![Priority Select node](../assets/animation/graph-editor/Priority-Select-Node.png) Evaluates a list of connected inputs from top to bottom and plays the first one whose condition evaluates to true. This allows for hierarchical animation selection based on specific logic. Whenever the current selection changes, it triggers a new [transition](#transitions).
**Inputs**
- **Input1...InputN** - **Trigger** (Boolean): A logical condition that must be true for this input to activate. In current versions, this is wired to a boolean parameter. - **Interruptible** (`Enum.AnimationNodeInterruptible`): Defines the rule for when this active animation can be interrupted by a higher-priority input. - **Always (default)**: The input can be interrupted at any time by a higher-priority condition. - **Finished**: The current animation must complete its playback before a higher-priority input can take over. - **Trigger**: The input is only interruptible when the `InterruptibleTrigger` is set to true. - **InterruptibleTrigger** (Boolean): Only available if **Interruptible** is set to **Trigger**. The input can be interrupted when this specific expression is true.
**Properties**
| Property | Type | Description | | --- | --- | --- | | DefaultInterruptible | `Enum.AnimationNodeInterruptible` | The baseline interruption rule applied to all inputs. Each input can override this with its own **Interruptible** input property. | | DefaultInterruptibleTrigger | Boolean | The baseline trigger value used when an input’s resolved **Interruptible** mode is **Trigger**. Each input can override this with its own **InterruptibleTrigger** input property.
**Event data**
- **Event Handling:** Events from the currently selected input pass through with their weight unchanged. - **Event Emission:** Passes through all events from the currently selected input only. During a transition, event emission follows [global event rules](#global-event-rules).
### Sequence ![Sequence node](../assets/animation/graph-editor/Sequence-Node.png) Activates connected inputs in a specific sequential order based on defined wait conditions. Whenever the current selection changes, it triggers a new [transition](#transitions).
**Inputs**
- **Input1...InputN** - **WaitFor** (`Enum.AnimationNodeWaitFor`): Specifies the condition that must be met before the sequence advances to the next input. - **Finished (default)**: Advances to the next input when the current input completes a cycle. The advancement behavior depends on the connected input: - **Play-once clip**: Advances when the clip finishes. - **Looping clip**: Advances after one full loop completes. - **Sequence with infinite loops**: Advances after one full cycle through all inputs. - **Sequence with infinite loops**: Advances after all loops complete. - **Trigger**: Activates the next input when a custom logical expression evaluates to true. - **WaitForTrigger** (Boolean): Only available when `WaitFor` is set to `Trigger`.
**Properties**
| Property | Type | Description | | --- | --- | --- | | **LoopCount** | Number | The number of times to cycle through the entire sequence (default is 1). A value of 0 indicates an infinite loop. Once the count is reached, the node respects the looping or hold setting of the final input. | | **DefaultWaitFor** | `Enum.AnimationNodeWaitFor` | The baseline wait condition applied to all inputs. Each input can override this with its own **WaitFor** input property. |
**Event data**
- **Event Handling:** Events from the currently active input pass through with their weight unchanged. - **Event Emission:** Passes through all events from the currently active input in the sequence only. During a transition, event emission follows [global event rules](#global-event-rules).
### RandomSequence ![Random Sequence node](../assets/animation/graph-editor/Random-Sequence-Node.png) Selects and plays one of its connected inputs at random. When the currently selected animation completes, the node randomly picks another input to play. Assign each input a specific weight to influence the probability of it being chosen. Whenever the current selection changes, it triggers a new [transition](#transitions).
**Inputs**
- **Input1...InputN** - **Weight** (Number): Determines the probability of this input being selected; higher weights increase the chance of selection.
**Properties**
| Property | Type | Description | | --- | --- | --- | | **PlayCount** | Number | The number of inputs the node will play through before stopping. Once reached, the node respects the looping or hold setting of the final input. Default is 0 for infinite | | **Seed** | Number | A value used to initialize the Random Number Generator (RNG), ensuring the sequence remains consistent across different clients. Defaults to -1 for random Seed. |
**Event data**
- **Event Handling:** Events from the currently active input pass through with their weight unchanged. - **Event Emission:** Passes through all events from the currently active input in the sequence only. During a transition, event emission follows [global event rules](#global-event-rules).
### Over ![Over node](../assets/animation/graph-editor/Over-Node.png) Layers the **Over** pose on top of the Base pose. When combined with a Mask node, masked-out joints in the **Over** pose reveal the **Base** pose entirely, creating a transparent overlay effect.
**Inputs**
- **Base**: The background or bottom layer, typically a full-body animation like locomotion or an idle state. - **Over**: The foreground or top layer to be applied over the base, such as a hand gesture or tool-use animation.
**Properties**
| Property | Type | Description | | --- | --- | --- | | **Weight** | Number | The blend weight used to attenuate the **Over** pose. Defaults to `1.0` (full override) and is unclamped. |
**Event data**
- **Event Handling:** - The node listens for events from both the **Base** and **Over** inputs. - **Event Emission:** - **Base Events**: All events from the Base input are passed through unmodified. - **Over Events**: Events from the Over input are scaled by the Weight property. - At Weight `0.5`, **Over** events propagate at half weight - At Weight `0`, they are silenced.
### Add ![Add node](../assets/animation/graph-editor/Add-Node.png) Adds the **Additive** pose to the **Base** pose, attenuated by a specific **Weight** (unclamped).
**Inputs**
- **Base**: The primary animation pose. - **Additive**: The pose to be layered onto the base.
**Properties**
| Property | Type | Description | | --- | --- | --- | | **Weight** | Number | Determines the strength of the additive pose applied to the base. |
**Event data**
- **Event Handling:** The node listens for events from both the **Base** and **Additive** inputs. - **Event Emission:** All events from both the Base and Additive inputs are passed through unmodified.
### Subtract ![Subtract node](../assets/animation/graph-editor/Subtract-Node.png) Converts an animation into an additive pose by subtracting a relative base pose from the target pose ( A − B A - BA−B). The result is attenuated by a specific **Weight** (unclamped).
**Inputs**
- **A**: The target animation pose. - **B**: The relative base pose to be subtracted.
**Properties**
| Property | Type | Description | | --- | --- | --- | | **Weight** | Number | Determines the strength of the subtraction effect applied to the result. |
**Event data**
- **Event Handling:** The node listens for events from both **Input A** and **Input B**. - **Event Emission:** All events from both **Input A** and **Input B** are passed through unmodified.
### Blend1D ![Blend1D node](../assets/animation/graph-editor/Blend1D-Node.png) Linearly interpolates between the two animation poses closest to the current input position on a single axis.
**Inputs**
- **Input1...InputN** - **Position** (Number): The specific coordinate for each subsequent input on the blend axis.
**Properties**
| Property | Type | Description | | --- | --- | --- | | **Position** | Number | The current active value on the blend axis used to sample the animations. If **Position** is outside the range of defined input positions, the node extrapolates using the two nearest inputs. | | **PhaseSync** | `Enum.AnimationNodePhaseSync` | Configures whether the timing of children inputs should be synchronized.
  • **Synced (default)**: Normalized synchronization. The node calculates a "virtual duration" based on the weighted average of active inputs. Each input node’s time step is adjusted so that all children converge to the same phase, keeping animations of different lengths in lockstep.
  • **Unsynced**: Standard blending where clips advance independently at their own playback rates.
|
**Event data**
- **Event Handling:** The node listens for events from all currently active child nodes. - **Event Emission:** Only events from the highest-weight active input propagate, scaled by its blend weight. Events from the secondary input are silenced.
### Blend2D ![Blend2D node](../assets/animation/graph-editor/Blend2D-Node.png) Blends multiple animation poses together based on two input parameters within a 2D coordinate space. This generalizes the Blend1D node to handle complex scenarios, such as blending based on both movement direction and speed simultaneously.
**Inputs**
- **Input1...InputN** - **X** (Number): The X-coordinate for each subsequent input. - **Y** (Number): The Y-coordinate for each subsequent input.
**Properties**
| Property | Type | Description | | --- | --- | --- | | **InputMode** | `Enum.AnimationNodeBlend2DInputMode` | Defines the coordinate system used to evaluate the blendspace:
  • **Cartesian (default)**: Uses standard 2D grid coordinates. X and Y represent the current position within the blendspace.
  • **Polar**: Uses angular and magnitude values. X represents the direction in radians, while Y represents the magnitude or strength of movement. Direction is weighted more heavily than magnitude, so inputs at similar angles but different magnitudes blend more smoothly than inputs at different angles.
| | **X** | Number | The current X-coordinate (Cartesian) or Direction in radians (Polar). | | **Y** | Number | The current Y-coordinate (Cartesian) or Magnitude (Polar). | | **PhaseSync** | `Enum.AnimationNodePhaseSync` | Configures whether the timing of children inputs should be synchronized.
  • **Synced (default)**: Normalized synchronization. The node calculates a "virtual duration" based on the weighted average of active inputs. Each input node’s time step is adjusted so that all children converge to the same phase, keeping animations of different lengths in lockstep.
  • **Unsynced**: Standard blending where clips advance independently at their own playback rates.
|
**Event data**
- **Event Handling:** The node listens for events from all currently active child nodes. - **Event Emission:** Only events from the highest-weight active input propagate, scaled by its blend weight. Events from the secondary input are silenced.
### Mask ![Mask node](../assets/animation/graph-editor/Mask-Node.png) Applies a predefined mask to the input Pose. A mask is defined by a weight per object (e.g. joint) in the rig hierarchy, allowing for precise control or "feathering" of the animation.
**Inputs**
- **Pose**: The animation pose to be masked.
**Properties**
| Property | Type | Description | | --- | --- | --- | | **Mask** | `Class.ObjectValue` | An `Class.ObjectValue` (created as a direct child of the Mask `AnimationNodeDefinition`) that defines the mask weights. It can function in one of two ways:
  • **Directly**: The `Class.ObjectValue` itself contains attributes mapping rig object names to specific weight values.
  • **By Reference**: The `Class.ObjectValue` references another `Class.Instance` that contains the mapping attributes. This allows for shared masks across different nodes in the graph.
When creating a mask, you can choose a rig schema (`Class.HumanoidRigDescription` or **Picker**) to populate the hierarchy.
  • `Class.HumanoidRigDescription`: Standardizes the mask for humanoid characters.
  • **Picker**: Allows the user to select any specific rig in the workspace to populate the mask hierarchy.
| | **Invert** | Boolean | When true, applies weight values as 1 - weight. This allows for reusing a mask inversely without creating a new asset. |
**Event data**
- **Event Handling:** The node listens for all events from the input **Pose**. - **Event Emission:** All events from the input **Pose** are passed through unmodified.
### Speed ![Speed node](../assets/animation/graph-editor/Speed-Node.png) Modifies the playback rate of an incoming animation pose.
**Inputs**
- **Pose**: The animation pose or subgraph whose playback speed will be modified.
**Properties**
| Property | Type | Description | | --- | --- | --- | | **Speed** | Number | A multiplier applied to the time delta ( d t dtdt). `0.0` pauses the graph, `1.0` is normal speed, and `2.0` is double speed. |
**Event data**
- **Event Handling:** The node listens for events from the input **Pose**. - **Event Emission:** All events from the input **Pose** are passed through unmodified. Note that while the visual playback speed changes, the timing of emitted events (such as markers) will scale accordingly with the modified playback rate.
### GraphOutput ![GraphOutput node](../assets/animation/graph-editor/Graph-Output-Node.png) Represents the final evaluated pose of the graph. This node is automatically included in all new graphs in the Animation Graph Editor. Its presence ensures that the graph is always valid and consistently produces an animation pose.
**Inputs**
- **Pose**: The final processed animation data to be applied to the rig.
**Properties**
- None
**Event data**
- **Event Handling:** The node listens for all events passed through the final connected input. - **Event Emission:** This node serves as the exit point for the animation pipeline and does not emit signals back into the graph.
## Transitions Several nodes in the Animation Graph (such as **Select**, **Priority Select**, **Sequence**, and **Random Sequence**) manage how animations blend when switching between active inputs. To prevent redundancy in the node reference, these behaviors are defined by standardized transition property groups. ### Default transition ![Default Transition](../assets/animation/graph-editor/Transition-Default.png) The baseline blending behavior applied to the node whenever it switches to a new active input. - **DefaultTransitionDuration** (number): The time (in seconds) it takes to fully blend into the new pose. - **DefaultTransitionCurve** (`Enum.PoseEasingStyle`): The easing function applied during the blend. Currently only supports `Enum.PoseEasingStyle.Linear` and `Enum.PoseEasingStyle.CubicV2`. ### Transition override ![Transition Override](../assets/animation/graph-editor/Transition-Override.png) Input-specific link properties that supersede the default transition. These are applied when the node transitions **to** that specific input. - **TransitionOverrideDuration** (number): Overrides the default transition duration. - **TransitionOverrideCurve** (`Enum.PoseEasingStyle`): Overrides the default transition curve. Currently only supports `Enum.PoseEasingStyle.Linear` and `Enum.PoseEasingStyle.CubicV2` ## Replication Animation graph parameters are local to each `Class.AnimationTrack` and are not automatically replicated. This may change in future updates, but the currently recommended approach uses: - **Client → Server**: A `Class.RemoteEvent` to send parameters from the owning client to the server at a fixed tick rate. - **Server → Other Clients**: Instance **attributes** which automatically replicate to all clients. ### Player characters A client script drives player characters. Since only the owning client knows the current gameplay state (movement input, humanoid state, etc.), parameters must flow from the owning client to the server and then to all other clients. #### Setup Under `Class.StarterCharacterScripts`, create: - A `Class.LocalScript` for client-side animation and parameter driving. - A `Class.Script` for server-side replication. - A `Class.RemoteEvent` for client-to-server communication. #### Client script The owning client drives parameters from gameplay events and sends them to the server periodically. Other clients read replicated attributes from the server script instead. ```lua local Players = game:GetService("Players") local RunService = game:GetService("RunService") local character = script.Parent local humanoid = character:WaitForChild("Humanoid") local animator = humanoid:FindFirstChildOfClass("Animator") or humanoid -- Load the animation graph local animation = Instance.new("Animation") animation.AnimationId = "rbxassetid://YOUR_GRAPH_ID" -- Replace with your published graph ID local track = animator:LoadAnimation(animation) track:Play() local isLocalCharacter = Players:GetPlayerFromCharacter(character) == Players.LocalPlayer if not isLocalCharacter then -- Read replicated parameters from the server script local animateServer = script.Parent:WaitForChild("AnimateServer") for attribute, value in animateServer:GetAttributes() do track:SetParameter(attribute, value) end animateServer.AttributeChanged:Connect(function(attribute) track:SetParameter(attribute, animateServer:GetAttribute(attribute)) end) return end -- Local player: drive parameters from gameplay local params = {} local function setParam(key, value) track:SetParameter(key, value) params[key] = value end -- Example: drive parameters from humanoid state humanoid.Running:Connect(function(speed) setParam("Speed", speed) setParam("State", if speed < 0.1 then "Idle" else "Moving") end) -- Send parameters to the server at a fixed tick rate local replicateEvent = script.Parent:WaitForChild("ReplicateEvent") local TICK_RATE = 1 / 20 local lastSendTime = 0 RunService.Stepped:Connect(function(gameTime) if gameTime - lastSendTime > TICK_RATE then lastSendTime = gameTime replicateEvent:FireServer(params) end end) ``` #### Server script Receives parameters from the owning client, drives the server-side animation graph, and propagates values as attributes for replication to other clients. ```lua local Players = game:GetService("Players") local character = script.Parent local humanoid = character:WaitForChild("Humanoid") local animator = humanoid:FindFirstChildOfClass("Animator") or humanoid -- Load the same animation graph on the server local animation = Instance.new("Animation") animation.AnimationId = "rbxassetid://YOUR_GRAPH_ID" -- Same graph ID as client animation.Parent = script local track = animator:LoadAnimation(animation) track:Play() -- Receive parameters from owning client and replicate via attributes local replicateEvent = script.Parent:WaitForChild("ReplicateEvent") replicateEvent.OnServerEvent:Connect(function(player, params) if Players:GetPlayerFromCharacter(character) ~= player then return end for key, value in params do track:SetParameter(key, value) script:SetAttribute(key, value) end end) ``` ### NPCs Since the server owns NPC animation, only server-to-client replication is needed. Drive the animation graph on the server, write parameters as attributes on a server Script, and read them on clients using the same `Class.Instance.AttributeChanged` pattern shown above. ### Latency Other clients see animations with a small delay equal to the tick interval (up to 50 ms at 20 Hz) plus network latency. You can reduce the tick interval by increasing the frequency (such as 1/30), at the cost of higher network traffic. 20 Hz is a reasonable default; animation blending is forgiving enough that small delays are hard to notice.
--- title: "Animation in Roblox" url: /docs/en-us/animation last_updated: 2026-06-29T19:33:52Z description: "An overview of all animation tools in Roblox Studio." --- # Animation in Roblox **Animation** is the process of applying motion to your characters, objects, and environments to create an engaging and dynamic experience. While there are many ways to make objects move and interact, animation in Roblox typically refers to customizing an expressive movement of a specific character or group of parts. Animation can apply to any Roblox part or group of parts. Different types of objects can utilize various animation features. The following are the general categories of animatable objects: - **Simple objects**, such as a basic part, can only animate changes across the part's single position or rotation property. - **Rigs**, or parts connected by joints like `Class.AnimationConstraint|AnimationConstraints` or `Class.Bone|Bones`, can articulate positional and rotational movement between their joints, like elbows and wrists. Rigged models can take advantage of animation features like [inverse kinematics](#inverse-kinematics) to quickly and programmatically apply movement in response to environments and events. - **R15 rigs**, rigs that incorporate the standardized [avatar character model](/docs/en-us/characters.md#avatar-character-components), can use animations from Roblox's character animation library and other movement features, even if it's a player or non-player character model. ## Animation Editor The [Animation Editor](/docs/en-us/animation/editor.md) allows you to design and publish custom animations on rigs. You can move the joints that connect individual sections of a rig to create poses, and the editor smoothly animates the rig from pose-to-pose. ## Inverse kinematics You can use [inverse kinematics](/docs/en-us/animation/inverse-kinematics.md) APIs to automatically create animations and poses based on environmental and external events. With inverse kinematics, you can procedurally generate various animation events, such as having a character's head track a bird flying in the sky, or making a character's arm automatically reach for a door knob when nearby. ## Animation events An [animation event](/docs/en-us/animation/events.md) is a specific point in an animation that triggers an action. You can define animation event markers across the [Animation Editor](/docs/en-us/animation/editor.md) timeline span, then use `Class.AnimationTrack:GetMarkerReachedSignal()` to detect those markers as the animation runs to perform specific actions, such as playing a sound effect whenever the user's foot touches the ground. --- title: "Inverse Kinematics" url: /docs/en-us/animation/inverse-kinematics last_updated: 2026-06-29T19:33:52Z description: "Inverse Kinematics is a technique in computer animation to make characters move and interact with their environment." --- # Inverse Kinematics [Inverse Kinematics](https://en.wikipedia.org/wiki/Inverse_kinematics) (IK) is a common technique in computer animation to efficiently make characters move and interact realistically with their environment. The process of creating a realistic movement for a character often requires many iterations and minor adjustments of the various joints. With IK, you can pose and animate multiple character parts by posing or adjusting a single object. This animation technique can provide solutions to the following examples: _Grab and pose a character's hand while automatically adjusting the related limbs, such as the wrist, elbow, and shoulder._ _Make a character's feet interact realistically on different surfaces and slopes._ _Grab and move a single target object to quickly create realistic interactions with your character and props._ ## IKControl You can use an `Class.IKControl` to procedurally add IK to your character rigs outside of the **Animation Editor**. Studio allows you to programmatically apply IK to all characters, such as R15, Rthro, and custom imported skinned characters, to create realistic movement and interactions in your experience. When adding an `Class.IKControl`, set the [required properties](#required-properties) correctly to avoid unexpected and unnatural animation results. As with all animation, [test your IKControls](#test-ikcontrols) to ensure that you achieve the desired behavior. ### Required properties When adding a `Class.IKControl` to your character's `Class.Humanoid` or `Class.AnimationController`, you must set the following required properties to enable IK: | Property | Description | | --- | --- | | `Class.IKControl.Type\|Type` | Specifies the behavior type of the IK control. See `Enum.IKControlType` for the list of behavior options. Common behavior types are `Position` or `Transform`. | | `Class.IKControl.EndEffector\|EndEffector` | A `Class.BasePart` or `Class.Bone` in your character rig that tracks toward the `Class.IKControl.Target\|Target`. For example, you can set a **LeftHand** bone as a `Class.IKControl.EndEffector\|EndEffector` to reach a doorknob object set as the `Class.IKControl.Target\|Target`. | | `Class.IKControl.Target\|Target` | The object the `Class.IKControl.EndEffector\|EndEffector` reaches or points toward. A `Class.IKControl.Target\|Target` can be any object with a world position. | | `Class.IKControl.ChainRoot\|ChainRoot` | Defines the chain of `Class.BasePart\|BaseParts` or `Class.Bone\|Bones` that the `Class.IKControl` affects. All connected parts between the `Class.IKControl.ChainRoot\|ChainRoot` and the `Class.IKControl.EndEffector\|EndEffector` are affected by the `Class.IKControl` using the behavior type defined.

For example, if your character's **LeftHand** is set as the `Class.IKControl.Target\|Target`, you can apply IK to the entire left arm by setting `Class.IKControl.ChainRoot\|ChainRoot` to the **LeftUpperArm**. To apply IK just to the parts below the elbow, set `Class.IKControl.ChainRoot\|ChainRoot` to the **LeftLowerArm**. | ### Test IKControls You can add and edit `Class.IKControl` programmatically or directly through the **Explorer**. You can even add and make changes to `Class.IKControl` during a Play test to quickly check how various properties affect a character's movements. To quickly test your `Class.IKControl` using an `Class.Attachment` as a target: 1. From Studio's mezzanine, [initiate a playtest](/docs/en-us/studio/testing-modes.md#playtesting). 2. In the **Explorer**, navigate to **Workspace** → your user's `Class.Model`. This `Class.Model` instance is named as your current Roblox account. 3. Click the **⊕** icon next to your character model's `HumanoidRootPart` and add an **Attachment**. 4. Select the **Attachment** and use the **Move** tool to position the object in front of your character in the viewport. 5. In the **Explorer** window, select the **⊕** icon next to your character's `Class.Humanoid` and add an **IKControl**. 6. Select the `Class.IKControl` and set the following property values in the **Properties** window: 1. **Type**: Select `Transform` from the dropdown. 2. **EndEffector**: Select your model's **LeftHand** `Class.MeshPart` in the **Explorer**. 3. **Target**: Select the newly created **Attachment** object in the **Explorer**. 4. **ChainRoot**: Select your model's **LeftUpperArm** `Class.MeshPart` in the **Explorer**._IKControl Properties__Explorer - Character Model_ Your character's left arm should now reach for the target `Class.Attachment`. You can experiment with moving the `Class.Attachment` or editing the `Class.IKControl` properties to achieve different results. ### Add constraints You can use `Class.Constraint|Constraints` to restrict how joints can move when reaching its target. Constraints can ensure joints like the elbows and knees bend naturally, or to make the mechanical joints rotate in a specific orientation. _Elbow bending unnaturally_ _Elbow bending correctly_ To add constraints to your character using `Class.IKControl`, your `Class.IKControl` and constraint must meet the following conditions: - The attachments referenced in the constraint's `Class.Constraint.Attachment0|Attachment0`/`Class.Constraint.Attachment1|Attachment1` properties attaches to the same parts as the `Class.Motor6D` `Class.Motor6D.Part0|Part0`/`Class.Motor6D.Part1|Part1`. - The relative positions of `Class.Constraint.Attachment0|Attachment0`/`Class.Constraint.Attachment1|Attachment1` must equal the corresponding positions of the `Class.Motor6D` `Class.Motor6D.C0|C0`/`Class.Motor6D.C1|C1` CFrames. - The constraint and the IKControl share the same parent `Class.Model`. The following instructions describe the process of adding a `Class.HingeConstraint` to restrict the rotation of a character's elbow and adding a `Class.BallSocketConstraint` to the wrist to limit the rotation angle. #### Elbow Roblox R15 characters already include attachments in their joints that you can use to apply the elbow constraint. For the elbow, both the LeftUpperArm and the LeftLowerArm include a `LeftElbowRigAttachment`. Along with adding a constraint, you also need to add additional child attachments to each part's `LeftElbowRigAttachment` to specify which axis the elbow can rotate about. To add the `Class.HingeConstraint` and child attachments: 1. In the **Explorer**, locate your model's **LeftLowerArm** and click the **⊕ button**. 2. Add a **HingeConstraint** with the name `LeftElbowConstraint`. 3. In the **Explorer**, navigate to the **LeftUpperArm.LeftElbowRigAttachment** and add an attachment: 1. Click the **⊕ button** to add an **Attachment** with the name `LeftElbowConstraintAttachment0`. 2. In the viewport, select the attachment and use the **Rotate tool** to rotate the attachment so the yellow **PrimaryAxis** is the axis of your elbow's expected rotation. 3. Set the **LeftElbowConstraint.Attachment0** property to this new attachment. 4. In the **Explorer**, navigate to your model's **LeftLowerArm.LeftElbowRigAttachment** add an attachment: 1. Click the **⊕ button** and add an **Attachment** with the name `LeftElbowConstraintAttachment1`. 2. Set the **LeftUpperArm.LeftElbowConstraint.Attachment1** property to this new attachment. 3. Copy the **LeftElbowConstraintAttachment0.CFrameOrientation** property and paste it as the **LeftElbowConstraint.Attachment1.CFrameOrientation** value. > **Error:** If you see a red arrow on the constraint's visualization, that means your attachment orientations are violating the hinge constraint. Go back and make sure the **LeftElbowConstraintAttachment1** has the same orientation as the **LeftElbowConstraintAttachment0**. Test your IKControl to verify the elbow only rotates about its hinge axis: #### Wrists Even with the elbow constraint, the IKControl can still produce unrealistic poses with the wrists. _The wrist bends unnaturally at certain orientations_ You can improve this by adding a `Class.BallSocketConstraint` to limit the rotation of the wrist. While this is similar to the process for adding a `Class.HingeConstraint` to the elbow, you can use the `Class.BallSocketConstraint.LimitsEnabled|LimitsEnabled` property on this constraint to further control the range of motion of the wrist. To add a `Class.BallSocketConstraint` for the wrist: 1. In the **Explorer**, locate your model's **LeftHand** and click the **⊕ button**. 1. Add a **BallSocketConstraint** with the name `LeftWristConstraint`. 2. Locate your model's **LeftLowerArm.LeftWristRigAttachment** and add an attachment: 1. Click the **⊕ button** and add an **Attachment** with the name `LeftWristConstraintAttachment0`. 2. In the viewport, select the attachment and use the **Rotate tool** to rotate the attachment so the yellow PrimaryAxis points toward the model's fingertips. 3. Set the **LeftWristConstraint.Attachment0** property to the new `LeftWristConstraintAttachment0`. 3. Locate your model's **LeftHand.LeftWristRigAttachment** and add an attachment: 1. Click the **⊕ button** and add an **Attachment** with the name `LeftWristConstraintAttachment1`. 2. Copy the **LeftWristConstraintAttachment0.CFrameOrientation** property and paste it as the **LeftWristConstraintAttachment1.CFrameOrientation** property. 3. Set the **LeftWristConstraint.Attachment1** property to this new **LeftWristConstraintAttachment1**. 4. In the **Explorer**, select the **LeftWristConstraint**. 5. In the **Properties** window, set the following: 1. Enable **LimitsEnabled**. 2. Set **UpperAngle** to `80`. This controls how much the constraint's axis can rotate, and 80 degrees is approximately how much the wrist should be able to bend. 6. Depending on your character you may want to tweak the direction the cone is pointing in. You can do this by using the **Rotate tool** to rotate the constraint's **Attachment0**. When selecting the **LeftWristConstraint**, a green cone appears visualizing the wrist's range of motion. With the constraint set up, test the IKControl with the hand pointing down in front of the character and the wrist should rotate and bend more realistically.
--- title: "Use animations" url: /docs/en-us/animation/using last_updated: 2026-06-29T19:33:52Z description: "Explains the process of playing animations through scripts, and replacing default animations." --- # Use animations Once you have [created an animation](/docs/en-us/animation/editor.md), you need to use scripts to include them in your experience. You can either [play animations manually](#play-animations-from-scripts) from scripts or [replace default animations](#replace-default-animations) for player characters. ## Play animations from scripts In some cases, you'll need to play an animation directly from inside a script, such as when a user presses a certain key or picks up a special item. ### Humanoids To play an animation on a rig containing a `Class.Humanoid` object, such as typical playable characters, follow this basic pattern: 1. Ensure that the local player's `Class.Humanoid` contains an `Class.Animator` object. 2. Create a new `Class.Animation` instance with the proper `Class.Animation.AnimationId|AnimationId`. 3. Load the animation via `Class.Animator:LoadAnimation()` to create an `Class.AnimationTrack`. 4. Play the track with `Class.AnimationTrack:Play()`. For example, the following `Class.LocalScript`, when placed in `Class.StarterPlayerScripts`, loads a "kick" animation onto the player's character and plays it. The script also utilizes the `Class.AnimationTrack:GetMarkerReachedSignal()|GetMarkerReachedSignal()` method to detect when a specific [animation event](/docs/en-us/animation/events.md) occurs. ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local character = player.Character or player.CharacterAdded:Wait() -- Ensure that the character's humanoid contains an "Animator" object local humanoid = character:WaitForChild("Humanoid") local animator = humanoid:WaitForChild("Animator") -- Create a new "Animation" instance and assign an animation asset ID local kickAnimation = Instance.new("Animation") kickAnimation.AnimationId = "rbxassetid://2515090838" -- Load the animation onto the animator local kickAnimationTrack = animator:LoadAnimation(kickAnimation) -- If a named event was defined for the animation, connect it to "GetMarkerReachedSignal()" kickAnimationTrack:GetMarkerReachedSignal("KickEnd"):Connect(function(paramString) print(paramString) end) task.wait(4) -- Play the animation track kickAnimationTrack:Play() ``` ### Non-humanoids To play animations on rigs that do **not** contain a `Class.Humanoid`, you must create an `Class.AnimationController` with a child `Class.Animator`. For example, the following `Class.Script` (assumed to be a direct child of the rig) loads a "kick" animation and plays it. ```lua local rig = script.Parent -- Create a new "Animation" instance and assign an animation asset ID local kickAnimation = Instance.new("Animation") kickAnimation.AnimationId = "rbxassetid://2515090838" -- Create a new "AnimationController" and "Animator" local animationController = Instance.new("AnimationController") animationController.Parent = rig local animator = Instance.new("Animator") animator.Parent = animationController -- Load the animation onto the animator local kickAnimationTrack = animator:LoadAnimation(kickAnimation) -- If a named event was defined for the animation, connect it to "GetMarkerReachedSignal()" kickAnimationTrack:GetMarkerReachedSignal("KickEnd"):Connect(function(paramString) print(paramString) end) task.wait(4) -- Play the animation track kickAnimationTrack:Play() ``` ## Replace default animations By default, Roblox player characters include common animations like running, climbing, swimming, and jumping. You can replace these [default animations](#default-character-animations) with animations from the [catalog](#catalog-animations) or with your own [custom](/docs/en-us/animation/editor.md) animations. 1. Obtain the **asset ID** of the new animation as follows: - For a custom animation built with the [Animation Editor](/docs/en-us/animation/editor.md), follow the [export](/docs/en-us/animation/editor.md#exporting-an-animation) instructions. - Copy an appropriate ID from the [catalog animation reference](#catalog-animations) below. For example, to replace the default run animation with the [Ninja Run](https://www.roblox.com/catalog/658830056/Ninja-Run) variant, use `656118852`. 2. In the [Explorer](/docs/en-us/studio/explorer.md) window, add a new `Class.Script` to **ServerScriptService**. 1. Hover over **ServerScriptService** and click the ⊕ button. 2. From the contextual menu, insert a **Script**. 3. In the new script, paste the following code:```lua local Players = game:GetService("Players") local function onCharacterAdded(character) -- Get animator on humanoid local humanoid = character:WaitForChild("Humanoid") local animator = humanoid:WaitForChild("Animator") -- Stop all animation tracks for _, playingTrack in animator:GetPlayingAnimationTracks() do playingTrack:Stop(0) end local animateScript = character:WaitForChild("Animate") --animateScript.run.RunAnim.AnimationId = "rbxassetid://" --animateScript.walk.WalkAnim.AnimationId = "rbxassetid://" --animateScript.jump.JumpAnim.AnimationId = "rbxassetid://" --animateScript.idle.Animation1.AnimationId = "rbxassetid://" --animateScript.idle.Animation2.AnimationId = "rbxassetid://" --animateScript.fall.FallAnim.AnimationId = "rbxassetid://" --animateScript.swim.Swim.AnimationId = "rbxassetid://" --animateScript.swimidle.SwimIdle.AnimationId = "rbxassetid://" --animateScript.climb.ClimbAnim.AnimationId = "rbxassetid://" end local function onPlayerAdded(player) player.CharacterAppearanceLoaded:Connect(onCharacterAdded) end Players.PlayerAdded:Connect(onPlayerAdded) ``` 4. For each line that references a [default character animation](#default-character-animations), uncomment it and paste the replacement ID after `rbxassetid://`. For example, to change the default run animation to the [Ninja Run](https://www.roblox.com/catalog/658830056/Ninja-Run) variant:```lua local Players = game:GetService("Players") local function onCharacterAdded(character) -- Get animator on humanoid local humanoid = character:WaitForChild("Humanoid") local animator = humanoid:WaitForChild("Animator") -- Stop all animation tracks for _, playingTrack in animator:GetPlayingAnimationTracks() do playingTrack:Stop(0) end local animateScript = character:WaitForChild("Animate") animateScript.run.RunAnim.AnimationId = "rbxassetid://656118852" --animateScript.walk.WalkAnim.AnimationId = "rbxassetid://" --animateScript.jump.JumpAnim.AnimationId = "rbxassetid://" --animateScript.idle.Animation1.AnimationId = "rbxassetid://" --animateScript.idle.Animation2.AnimationId = "rbxassetid://" --animateScript.fall.FallAnim.AnimationId = "rbxassetid://" --animateScript.swim.Swim.AnimationId = "rbxassetid://" --animateScript.swimidle.SwimIdle.AnimationId = "rbxassetid://" --animateScript.climb.ClimbAnim.AnimationId = "rbxassetid://" end local function onPlayerAdded(player) player.CharacterAppearanceLoaded:Connect(onCharacterAdded) end Players.PlayerAdded:Connect(onPlayerAdded) ``` ## Set animation weights You can use multiple animations for the same action. For example, there are two `idle` variations in the code sample for [replacing default animations](#replace-default-animations). When multiple animations exist for a character state, the **Animate** script randomly chooses which one to play, but you can influence the outcome by setting the animation's `Weight` value under the following formula: - **animation weight / total weight of all state animations** In the following example, `idle.Animation1` will play ⅓ of the time the character is idle, while `idle.Animation2` will play ⅔ of the time. ```lua animateScript.idle.Animation1.AnimationId = "rbxassetid://656117400" animateScript.idle.Animation2.AnimationId = "rbxassetid://656118341" animateScript.idle.Animation1.Weight.Value = 5 animateScript.idle.Animation2.Weight.Value = 10 ``` ## Animation references ### Default character animations The following table contains all of the default character animations that you can [replace](#replace-default-animations) with [catalog](#catalog-animations) animations or your own [custom](/docs/en-us/animation/editor.md) animations. Note that **Idle** has two variations which you can [weight](#set-animation-weights) to play more or less frequently. | Character action | Animate script reference | | --- | --- | | **Run** | `animateScript.run.RunAnim.AnimationId` | | **Walk** | `animateScript.walk.WalkAnim.AnimationId` | | **Jump** | `animateScript.jump.JumpAnim.AnimationId` | | **Idle** | `animateScript.idle.Animation1.AnimationId`
`animateScript.idle.Animation2.AnimationId` | | **Fall** | `animateScript.fall.FallAnim.AnimationId` | | **Swim** | `animateScript.swim.Swim.AnimationId` | | **Swim (Idle)** | `animateScript.swimidle.SwimIdle.AnimationId` | | **Climb** | `animateScript.climb.ClimbAnim.AnimationId` | ### Catalog animations When using avatar animation bundles to [replace default animations](#replace-default-animations), use the following references for the respective asset IDs. For example, if you want to apply the [Ninja Jump](https://www.roblox.com/catalog/658832070/Ninja-Jump) animation, use `656117878`. Note that **Idle** has multiple variations. | [**Astronaut**](https://www.roblox.com/bundles/34/Astronaut-Animation-Pack) | **Run**
891636393**Walk**
891636393**Jump**
891627522**Idle**
891621366, 891633237, 1047759695**Fall**
891617961**Swim**
891639666**Swim (Idle)**
891663592**Climb**
891609353 | | --- | --- | | [**Bubbly**](https://www.roblox.com/bundles/39/Bubbly-Animation-Package) | **Run**
910025107**Walk**
910034870**Jump**
910016857**Idle**
910004836, 910009958, 1018536639**Fall**
910001910**Swim**
910028158**Swim (Idle)**
910030921**Climb**
909997997 | | [**Cartoony**](https://www.roblox.com/bundles/56/Cartoony-Animation-Package) | **Run**
742638842**Walk**
742640026**Jump**
742637942**Idle**
742637544, 742638445, 885477856**Fall**
742637151**Swim**
742639220**Swim (Idle)**
742639812**Climb**
742636889 | | [**Elder**](https://www.roblox.com/bundles/48/Elder-Animation-Package) | **Run**
845386501**Walk**
845403856**Jump**
845398858**Idle**
845397899, 845400520, 901160519**Fall**
845396048**Swim**
845401742**Swim (Idle)**
845403127**Climb**
845392038 | | [**Knight**](https://www.roblox.com/bundles/68/Knight-Animation-Package) | **Run**
657564596**Walk**
657552124**Jump**
658409194**Idle**
657595757, 657568135, 885499184**Fall**
657600338**Swim**
657560551**Swim (Idle)**
657557095**Climb**
658360781 | | [**Levitation**](https://www.roblox.com/bundles/79/Levitation-Animation-Pack) | **Run**
616010382**Walk**
616013216**Jump**
616008936**Idle**
616006778, 616008087, 886862142**Fall**
616005863**Swim**
616011509**Swim (Idle)**
616012453**Climb**
616003713 | | [**Mage**](https://www.roblox.com/bundles/63/Mage-Animation-Package) | **Run**
707861613**Walk**
707897309**Jump**
707853694**Idle**
707742142, 707855907, 885508740**Fall**
707829716**Swim**
707876443**Swim (Idle)**
707894699**Climb**
707826056 | | [**Ninja**](https://www.roblox.com/bundles/75/Ninja-Animation-Package) | **Run**
656118852**Walk**
656121766**Jump**
656117878**Idle**
656117400, 656118341, 886742569**Fall**
656115606**Swim**
656119721**Swim (Idle)**
656121397**Climb**
656114359 | | [**Pirate**](https://www.roblox.com/bundles/55/Pirate-Animation-Package) | **Run**
750783738**Walk**
750785693**Jump**
750782230**Idle**
750781874, 750782770, 885515365**Fall**
750780242**Swim**
750784579**Swim (Idle)**
750785176**Climb**
750779899 | | [**Robot**](https://www.roblox.com/bundles/82/Robot-Animation-Pack) | **Run**
616091570**Walk**
616095330**Jump**
616090535**Idle**
616088211, 616089559, 885531463**Fall**
616087089**Swim**
616092998**Swim (Idle)**
616094091**Climb**
616086039 | | [**Rthro**](https://www.roblox.com/bundles/356/Rthro-Animation-Package) | **Run**
2510198475**Walk**
2510202577**Jump**
2510197830**Idle**
2510197257, 2510196951, 3711062489**Fall**
2510195892**Swim**
2510199791**Swim (Idle)**
2510201162**Climb**
2510192778 | | [**Stylish**](https://www.roblox.com/bundles/83/Stylish-Animation-Pack) | **Run**
616140816**Walk**
616146177**Jump**
616139451**Idle**
616136790, 616138447, 886888594**Fall**
616134815**Swim**
616143378**Swim (Idle)**
616144772**Climb**
616133594 | | [**Superhero**](https://www.roblox.com/bundles/81/Superhero-Animation-Pack) | **Run**
616117076**Walk**
616122287**Jump**
616115533**Idle**
616111295, 616113536, 885535855**Fall**
616108001**Swim**
616119360**Swim (Idle)**
616120861**Climb**
616104706 | | [**Toy**](https://www.roblox.com/bundles/43/Toy-Animation-Pack) | **Run**
782842708**Walk**
782843345**Jump**
782847020**Idle**
782841498, 782845736, 980952228**Fall**
782846423**Swim**
782844582**Swim (Idle)**
782845186**Climb**
782843869 | | [**Vampire**](https://www.roblox.com/bundles/33/Vampire-Animation-Pack) | **Run**
1083462077**Walk**
1083473930**Jump**
1083455352**Idle**
1083445855, 1083450166, 1088037547**Fall**
1083443587**Swim**
1083464683**Swim (Idle)**
1083467779**Climb**
1083439238 | | [**Werewolf**](https://www.roblox.com/bundles/32/Werewolf-Animation-Pack) | **Run**
1083216690**Walk**
1083178339**Jump**
1083218792**Idle**
1083195517, 1083214717, 1099492820**Fall**
1083189019**Swim**
1083222527**Swim (Idle)**
1083225406**Climb**
1083182000 | | [**Zombie**](https://www.roblox.com/bundles/80/Zombie-Animation-Pack) | **Run**
616163682**Walk**
616168032**Jump**
616161997**Idle**
616158929, 616160636, 885545458**Fall**
616157476**Swim**
616165109**Swim (Idle)**
616166655**Climb**
616156119 |
--- title: "Body scale and proportions" url: /docs/en-us/art/accessories/body-scale last_updated: 2026-06-29T19:33:52Z description: "Avatars come in 3 different standard sizes, Classic, Rthro, Rthro Slender." --- # Body scale and proportions Roblox supports three common avatar sizes, known as **Classic**, **Rthro**, and **Rthro Slender**. This standard sizing helps keep avatar characters consistent, allowing developers to create experiences and environments that can fit commonly sized character models. When modeling rigid accessories, like a helmet that contours around a character's head, it's important to use a mannequin to help ensure your proportions fit. The asset provided in this tutorial example was originally modeled using **Rthro** proportions. Layered clothing, such as clothing, don't require body scale specification, since layered assets automatically stretch and deform over any body scale. If you are modeling your asset, visit the [references resources](/docs/en-us/avatar/resources.md#references) to download any of the following mesh mannequin: _ Classic blocky body scale. Roughly 4.75 studs tall._ _ Normal body scale. Roughly 5.75-6.5 studs tall, with wider shoulders and narrower hips._ _ Slender body scale. Roughly 5.25-6.25 studs tall, with narrower shoulders and wider hips._ See the [accessory specifications](/docs/en-us/avatar/rigid-accessories/specifications.md#body-scale) and the [body specifications](/docs/en-us/avatar/character-bodies/specifications.md#body-scale) for specific range values and other geometry requirements. ## AvatarPartScaleType Roblox represents body scale in the engine by an `AvatarPartScaleType` `Class.StringValue` object within each avatar character part and associated accessory. In most cases, asset creators do not need to directly access or modify this value object since tools, such as the [Accessory Fitting Tool](/docs/en-us/avatar/accessory-fitting-tool.md), automatically generate the appropriate value object on accessory creation. In cases where you may need to identify the current body scale of a body part or accessory, the following table defines the `AvatarPartScaleType` value for each type of body size. When importing your model, use the Importer's [Rig Scale](/docs/en-us/studio/importer.md) to automatically apply a scale to your custom asset. | Body Scale | AvatarPartScaleType value | | --- | --- | | Classic | `Classic` | | Normal | `ProportionsNormal` | | Slender | `ProportionsSlender` | --- title: "Armature setup" url: /docs/en-us/art/accessories/creating/armature-setup last_updated: 2026-06-29T19:33:52Z description: "Transfer and parent an armature template to your custom clothing Blender project." --- # Armature setup **Rigging** is the process that enables the clothing object to move and deform with a Roblox character's R15 rig. In this tutorial, you'll parent the clothing item to Roblox's provided R15 armature and verify the Automatic Skinning Transfer data. After rigging, be sure to test out some basic poses to ensure that your clothes move and stretch correctly with any character body. _Clothing mesh with no rigging data_ _Clothing mesh with rigging data performing pose tests_ The rigging process requires the following: 1. Download and append an R15 armature to your project. 2. Parent the rig with Blender's automatic weights. 3. Test poses. ## Transfer armature Roblox provides an R15 base armature that you can import into your own project. While it is possible to create your own R15 armature rig, importing a premade rig saves you time and reduces the potential for error. To import the R15 character armature into your file: 1. Download Roblox's [Rig_and_Attachments_Template.blend](../../../assets/modeling/meshes/reference-files/Rig_and_Attachments_Templates.zip). Do not open this project. 2. In your current clothing project, return to **Object Mode**. 3. Navigate to **File** > **Append**, and select the saved **Rig_And_Attachment.blend** file. An additional folder structure appears. 4. Select **Armature** > **Armature** and press **Append**. An armature object is added to your workspace. 5. The armature may need reorientation with the following steps: 1. With the armature selected, open the **Item tool** sidebar. 2. Adjust the rotation so the armature is correctly aligned with your mesh. 3. After alignment, navigate to **Object** > **Apply** > **All Transforms** to freeze your new rotation values. ## Parent armature With the armature rig in place, you can use Blender's **Parent with Automatic Weights** functionality to quickly set your clothing mesh as a child of the armature. This feature also applies vertex weighting, or **skinning**, automatically to your mesh, which can save you significant time over skinning your clothing manually. To parent the clothing to the rig: 1. Select the clothing mesh object. 2. Hold shift and click the **Armature** object. Ensure that the armature object is the last object selected. 3. Right-click and select **Parent** > **With Automatic Weights**. --- title: "Caging setup" url: /docs/en-us/art/accessories/creating/caging-setup last_updated: 2026-06-29T19:33:52Z description: "Isolate your outer cage meshes and hide extra objects before modifying your clothing cage." --- # Caging setup **Caging** is the process of setting the clothing's interior and exterior surfaces, referred to as the inner and outer cages respectively. This enables your clothing to layer over existing clothing and bodies, and additional clothes to layer on top. Since the shirt in this tutorial was created using the cage templates as a mannequin, there is no need to adjust the inner cage where your clothing asset already fits over. **Only the outer cage needs to be adjusted** to fit over the new clothing item. _Clothing and cage meshes before caging_ _Outer cage mesh after caging process_ The caging process uses the following steps: 1. Set up your project by isolating the outer cage and the clothing mesh in your project. 2. Modify the outer cage to wrap over the clothing mesh using sculpting tools. To set up your project: 1. Switch to **Object mode**. 2. In the Outliner: 1. Hide the Armature. 2. Unhide your LongSleeve_OuterCage object. 3. In the Outliner, navigate to **Armature** > **Long Sleeve** and toggle the **Disable Selection** icon. This prevents accidental edits to the clothing mesh. 4. Select the OuterCage object, and navigate to Object Properties and enable **Wireframe**. This helps easily visualize and access the mesh. --- title: "Convert" url: /docs/en-us/art/accessories/creating/converting last_updated: 2026-06-29T19:33:52Z description: "Use the Accessory Fitting Tool to convert a Model object to an Accessory." --- # Convert With the `Class.Model` in your project, the last step in the process of clothing creation requires you to convert this object to a standard `Class.Accessory` that avatars can equip. Using the [Accessory Fitting Tool](/docs/en-us/avatar/accessory-fitting-tool.md) (AFT), convert the model object to a `Class.Accessory` that you can then use in your experience or publish to the Marketplace. To generate the accessory object: 1. In the toolbar's **Avatar** tab, click **Accessory** to open the AFT. 2. Select the `Class.Model` of the clothing item in the viewport. The tool's text field populates with the name of the object selected. Alternatively, you can select the object within the **Explorer** window. 3. Test out various sample characters, clothing, and animations. See [Test accessories](/docs/en-us/avatar/accessory-fitting-tool.md#test-accessories) for additional information. - If required, make minor cage adjustments using the editing features. Larger cage changes may require returning to your third-party modeling software and re-exporting the asset. 4. When ready to generate your accessory, click **Generate MeshPart Accessory**. The accessory object with your model populates in your workspace. _Clothing accessory in viewport_ _Clothing accessory in Explorer_ > **Success:** Congratulations, you've completed your clothing tutorial. With this accessory, you can: - Equip the accessory on an avatar-ready character by drag and dropping the accessory on an existing model, or using [HumanoidDescription](/docs/en-us/characters/appearance.md#manually-modify-appearance). - Save the accessory as an [avatar asset](/docs/en-us/projects/assets.md#for-avatars) for use in an experience later. - If you meet certain account requirements, you can [upload your asset](/docs/en-us/marketplace/publish-to-marketplace.md) for moderation and start selling it on the Marketplace. --- title: "Export" url: /docs/en-us/art/accessories/creating/exporting last_updated: 2026-06-29T19:33:52Z description: "Use Blender's FBX export with specific settings when exporting a clothing asset." --- # Export It's important to follow the Blender export settings to ensure a Studio-compatible `.fbx` file. Before exporting your file, ensure that you've removed extra objects, such as lights, cameras, or mannequins, and applied or removed any active modifiers. To export your file from Blender: 1. In the topbar, click **File**. A pop-up menu displays. 2. Select **Export**, then **FBX (.fbx)**. The **Blender File View** window displays. 3. On the right-hand side, change the **Path Mode** property to **Copy**, then toggle the **Embed Textures** button. 4. Set the **Transform** > **Apply Scalings** to **FBX Unit Scale**. If you run into scaling issues on import, see [Blender FBX scaling](/docs/en-us/art/blender.md#adjust-scale-fbx) for alternative approaches. 5. Under the **Armature** section, disable **Add Leaf Bones**. 6. Disable **Bake Animation**. 7. Click the **Export FBX** button. > **Success:** You've completed the exporting section of this tutorial. If desired, download a [reference sample](../../../assets/art/accessories/creating/Long_Sleeve_Export.fbx) of this exported file for comparison. You can use this reference in the next importing step. --- title: "Import" url: /docs/en-us/art/accessories/creating/importing last_updated: 2026-06-29T19:33:52Z description: "Use the Importer to import your custom accessory into Studio." --- # Import After creating the clothing item in your third-party modeling tool, import the `.fbx` using Studio's Importer tool to add the object to your project as a `Class.Model`. Use the Importer to import your `.fbx` into Studio: 1. From Studio's **File** menu, select **Import**. 2. Select your exported `.fbx` and verify any possible warnings or errors. 1. Warnings or errors related to the clothing mesh may require returning to Blender to resolve. 3. Select **Import** to add the model to your workspace. --- title: "Basic clothing creation" url: /docs/en-us/art/accessories/creating last_updated: 2026-06-29T19:33:52Z description: "This tutorial covers the basic steps required in Blender to Studio-ready clothing assets from scratch." --- # Basic clothing creation You can create your own custom avatar clothing using Blender and Roblox's downloadable project templates. These project templates include [components required for layered clothing](/docs/en-us/avatar/layered-accessories.md#components-of-a-layered-clothing-accessory), and can double as a mannequin to quickly begin shaping and sculpting your clothing assets. By the end of this tutorial, you will have a clothing asset that contains all the required components for a layered clothing accessory on the Marketplace. > **Info:** While this content and the provided examples cover the Blender workflow and tools, you can apply the same concepts to other third-party modeling applications. _Provided Blender mannequin template_ _Final Studio-ready clothing asset_ This tutorial is intended for creators with moderate Blender experience and uses the following processes to create a clothing item: 1. Modeling basic clothing using an existing mannequin shape. 2. Texturing your mesh to change its surface appearance and color. 3. Caging your clothing mesh using Roblox's template cages. 4. Rigging your clothing mesh using Roblox's armature templates. 5. Exporting your asset from Blender. 6. Importing and converting the model to an accessory in Studio. > **Info:** This tutorial covers a **basic workflow** for 3D clothing creation. There are many external resources for different techniques, processes, and refinements you can incorporate for creating clothing, such as using Blender's various sewing and cloth simulation tools and PBR textures. --- title: "Modeling setup" url: /docs/en-us/art/accessories/creating/modeling-setup last_updated: 2026-06-29T19:33:52Z description: "Setup your Blender environment with the correct files and templates to model efficiently." --- # Modeling setup Modeling, sometimes known as **sculpting**, is the process of shaping 3D geometry. In this tutorial, use one of Roblox's template files to create the initial shape of the clothing and add unique and clothing-specific modifications to its geometry. You can further apply the techniques and processes in this tutorial to create any other type of clothing, such as pants, skirts, shoes, and more. _Base Mannequin using Roblox provided cage meshes_ _Sculpted clothing mesh for a long sleeve shirt_ This modeling tutorial covers the following processes: 1. Setting up your project and creating your base mesh. 2. Clearing the duplicated mesh of extra attributes. 3. Trimming the shape to match the desired clothing type. 4. Adding and smoothing the vertices of your clothing shape. 5. Scaling and positioning the clothes onto the mannequin. 6. Sculpting fabric and other details onto the mesh object. 7. Closing the holes in your mesh, making it watertight. ## Set up mesh To get your project started, download and open Roblox's [Clothing_Cage.blend](../../../assets/modeling/meshes/reference-files/Clothing_Cage_Templates.zip) project and begin setting up your basic project objects. _3D viewport - duplicated cage mesh object_ _Outliner - duplicated object named "LongSleeve"_ To set up your project and your initial mesh object: 1. Download the [Clothing_Cage.blend](../../../assets/modeling/meshes/reference-files/Clothing_Cage_Templates.zip) project. This project includes the inner and outer cage mesh that you will use as temporary mannequins. 2. Open this file, click **Save As**, and save the project with a new name. This will be your main Blender project for the clothing accessory. 3. In the Outliner, copy and paste **InnerCage** object to duplicate it. 4. With the duplicated object highlighted, right-click in the viewport and select **Parent** > **Parent and Keep Transformation**. 5. In the Outliner, right-click and delete the extra **Cage.001 data object**. 6. Rename the duplicated object as "LongSleeve". 7. Rename the original cages as "LongSleeve_OuterCage" and "LongSleeve_InnerCage", respectively. 8. **Hide** the original _OuterCage and _InnerCage objects. You use these later in the Caging step. ## Clear extra attributes The cage mesh objects in the template include some helper vertex color properties that need to be removed from your clothing mesh. If left in, the vertex colors may clash with the texture of the object after importing in Studio. To remove extra attribute data: 1. With the LongSleeve object selected, navigate to the **Properties** panel > **Object Data Properties** > **Color Attributes**. 2. With **colorSet1** selected, remove it by pressing the **–** button. --- title: "Modify outer cage" url: /docs/en-us/art/accessories/creating/modify-cage last_updated: 2026-06-29T19:33:52Z description: "Modify the outer cage to fit tightly over your clothing item." --- # Modify outer cage With your project environment set, you can now adjust your outer cage using sculpting and editing tools. Set your outer cage as close as possible to your clothing mesh to ensure the best deformation and layering effects. > **Error:****Do not delete any vertices or faces of the provided cages.** Destructive modification of the cages can cause import issues and prevent the clothing from importing and displaying as expected. To modify your outer cage: 1. With the OuterCage mesh selected, switch to **Sculpt Mode**. 2. Enable **X Axis Symmetry**. 3. Switch to **Inflate tool**. 4. Press `F` to adjust the radius of the brush. 5. Starting with one side, expand the outer cage to fit precisely over the mesh 6. After expanding the cage mesh over a majority of the mesh, disable **X Axis Symmetry** and address individual spots. Keep the following in mind: 1. Your outer cage should always cover your clothing mesh. 2. Your outer cage should be as close to the clothing mesh as possible to ensure accurate layering. 3. For small angular areas, like the armpit, you may need to switch back to **Edit Mode** and grab the individual vertices to avoid crowding your cage mesh vertices. 7. Perform additional passes in **Edit Mode** with wireframe enabled to ensure your outer cage vertices fit as closely as possible. A tight fit ensures that your clothing item layers as accurately as possible. > **Success:** You've completed the caging section of this tutorial. > > If desired, download a [reference sample](../../../assets/art/reference-files/checkpoint/4_LongSleeve-Caging-Complete.blend) of this project for comparison before exporting your project and bringing it into Studio. --- title: "Sculpt detail" url: /docs/en-us/art/accessories/creating/sculpting last_updated: 2026-06-29T19:33:52Z description: "Use Blender's sculpting and simulation tools to add detail to your clothing shape." --- # Sculpt detail After scaling and positioning your clothing mesh, begin applying sculpting detail to add cloth and fabric detail to your mesh. While there are many ways to sculpt an object in Blender, this tutorial primarily uses the **Elastic Deform**, **Inflate**, and **Clothing** tools to make your mesh look more like realistic clothing. _Clothing mesh after sculpting details_ To add clothing details: 1. With the shirt highlighted, switch to **Sculpt Mode**. 2. Disable **X-Ray mode**, if required. 3. Enable **X Mirror** to perform symmetrical edits. 4. Select the **Elastic Deform** tool at `.5` strength to stretch parts of vertices to completely cover the mannequin. 1. Use `F` to change the radius of the brush. 2. You can hide the mannequin to access hard-to-reach areas. 5. Select the **Cloth** tool. 6. Disable **X Mirror**. The cloth tool can produce unexpected results with symmetry enabled. 7. Using the Cloth tool, click and drag on your mesh to add a cloth-like surface to your mesh. Adjust the settings to change the strength of the deformations. 8. Using the **Elastic Deform**, **Inflate**, and **Cloth** tools, make the final adjustments to your mesh so that it sits on top of the mannequin with the final desired shape. --- title: "Test poses" url: /docs/en-us/art/accessories/creating/test-poses last_updated: 2026-06-29T19:33:52Z description: "Test your poses after rigging a clothing item to ensure the clothes bend and deform correctly." --- # Test poses With the clothing mesh parented to an armature, you can now perform quick tests to verify that your clothing deforms correctly. > **Warning:** If you see issues with the deformation of your clothing, you may need to correct the issue with weight-painting, a technique for manually applying skinning data to your meshes. > > This tutorial doesn't cover the process of weight-painting. For additional resources on manually weight painting and instructions on updating skinning data on meshes, see the following resources: - [Skin a simple mesh](/docs/en-us/art/modeling/skin-a-simple-mesh.md) - [Skin a humanoid mesh](/docs/en-us/art/modeling/skin-a-humanoid-model.md) To test your clothing's movement: 1. With your armature selected, navigate to the **Properties** panel > **Armature properties**. 2. In **Viewport Display** > **Show**, enable **In Front**. The **In Front** property enables you to easily see and access the bones for posing. 3. In the viewport, select your armature and navigate to **Pose** mode. 4. Click on various bones and press `R` to rotate and set a pose. After pressing `R`: 1. Click to save the rotation. 2. Right-click to cancel an unsaved rotation. 3. If you have saved a rotation, reset your pose in the viewport with right-click and select **Clear User Transforms**. 5. Try various natural poses of your character to ensure that your clothing stretches and fits correctly. > **Success:** You've completed the rigging section of this tutorial. If desired, download a [reference sample](../../../assets/art/reference-files/checkpoint/3_LongSleeve-Rigging-Complete.blend) of this project for comparison. --- title: "Create texture map" url: /docs/en-us/art/accessories/creating/texture-map last_updated: 2026-06-29T19:33:52Z description: "Generate a 2D texture map image that you can save your texture painting to." --- # Create texture map Creating seams and unwrapping tells Blender how to project the 3D surface of the clothing item in 2D. Before you can apply any texture appearance changes, you need to create a texture map, which is the specific 2D image you apply and save your textures to. To create a new texture map: 1. With the clothing mesh selected, navigate to the **Properties** panel > **Material Preview** tab. 2. Select the **+ New** button. 3. Next to Base Color, select the **yellow dot** and click **Image Texture**. 4. Click the **+ New** button. 1. Name the texture image the same as your clothing mesh with a `_TXT` affix. 2. Set the color to black, or any base color of your preference. 3. Keep the rest of the default settings. 4. Press **OK** when completed. --- title: "Texture painting" url: /docs/en-us/art/accessories/creating/texture-painting last_updated: 2026-06-29T19:33:52Z description: "Use Blender's Texture Painting tools to apply a surface appearance to your clothing item." --- # Texture painting With a new image ready to apply textures to, use Blender's Texture Paint mode to quickly brush texture colors onto your mesh: 1. Navigate to **Texture Paint mode**. 2. On the viewport, select the clothing object. 3. Open up the sidebar tools to access brush settings. 4. Select a color, brush size, and falloff to apply. You might need to adjust settings, depending on the modifications you intend to apply. 5. Draw on either the UV 2D map, or the 3D mesh. 6. Save the `.png` of your texture map by selecting **Image** > **Save**. > **Success:** You've completed the texturing section of this tutorial. If desired, download a [reference sample](../../../assets/art/reference-files/checkpoint/2_LongSleeve-Texturing-Complete.blend) of this project and texture image for comparison. > > There are a lot of ways to texture and apply a unique appearance to your meshes. For additional suggestions, try utilizing alpha transparencies, unique seams, [PBR textures](/docs/en-us/art/modeling/surface-appearance.md), or Blender's other texturing tools and techniques. --- title: "Trim clothing shape" url: /docs/en-us/art/accessories/creating/trimming last_updated: 2026-06-29T19:33:52Z description: "Create the general shape of your clothing item by removing unnecessary sections of your duplicated mesh." --- # Trim clothing shape With a clean mesh object to work with, cut the basic shape of the type of clothing item you intend to create and prepare the mesh for additional sculpting detail. In this tutorial, you create a long-sleeve shirt shape by removing the leg, arm, and head sections, smoothing out the mesh to create a flat canvas, and repositioning the mesh onto your temporary mannequin. _Long-sleeve trimming of the full-body mesh._ ## Trim clothing shape Create the general shape of your clothing type by trimming sections of your duplicated mannequin mesh. To trim your clothing shape: 1. Select the **LongSleeve** object. 2. Switch to **Edit Mode**. 3. Enable **X-Ray mode** in the top right corner of the viewport. 4. Click and drag over parts of the mesh that you do not want to include in your shirt. 5. Press `X` and select **Vertices** to delete these sections of your mesh. 6. **Repeat step 4** until you reach your desired clothing shape. ## Add and smooth vertices With the basic shape created, subdivide the surface of your clothing mesh to add vertices and smooth out the mesh. This process removes the grid-like surface and allows you to apply more complex sculpting detail later. _Before applying Subdivision modifier and Shade Smooth_ _After applying Subdivision modifier and Shade Smooth_ To add and smooth your vertices: 1. Switch back to **Object Mode**. 2. With the clothing mesh selected, navigate to the **Modifier Properties** panel. 3. Select **Add Modifier** > **Subdivision Surface Modifier** and click **Apply** with the default settings. 4. In the viewport, right-click the object and select **Shade Smooth** to eliminate the creases on your clothing article. ## Scale and position With your base clothing shape created, the next steps are to scale out the mesh and reposition it on top of the mannequin. _Clothing mesh should fit over mannequin after applying a reposition and scale_ To set up and scale your clothing to your mannequin: 1. Return to **Object Mode**. 2. **Unhide** one of your original cage meshes. 3. In the filter dropdown, enable **Selectable** toggles, and set your body mesh to unselectable. Disabling the **Selectable** toggle prevents accidental edits to your mannequin. 4. Select your shirt mesh and lightly **scale** and **position** your asset to rest over the mannequin. 5. Press `S` and use your mouse to scale. In most cases, the scaling should be a small change. 6. Press `G` and click to grab your shirt, make sure the shirt rests loosely over the mannequin. The shirt does not need to fit perfectly at this point. --- title: "Create seams and unwrap" url: /docs/en-us/art/accessories/creating/unwrapping last_updated: 2026-06-29T19:33:52Z description: "Use Blender's seams and unwrapping functionality to project your 3D surface on a 2D texture plane." --- # Create seams and unwrap **Texturing** is the process of customizing the color, tone, and shading of your model's surface. Custom meshes and models use a 2D image, known as a texture map, to project various surface appearance elements onto your 3D object. In this tutorial, apply a complete color to your shirt and add a smaller design using Blender's Texture Paint mode. > **Warning:** This tutorial does not cover [PBR textures](/docs/en-us/art/modeling/surface-appearance.md), which are advanced textures that can replicate real-world texture properties, like reflectivity and surface roughness, and are recommended to give your clothing items an extra dimension of creativity and visual pop. PBR textures often require a third-party application such as Substance Painter. _Clothing mesh after sculpting_ _Clothing mesh after texturing_ The texturing process requires the following steps: 1. Create seams in your mesh to define how Blender unwraps your 3D object. 2. Unwrap the UV of your model, creating a distinct front and back 2D surface to apply textures on based on your seams. 3. Create a new texture image to save as your 2D map. 4. Paint a custom texture using Blender's Texture Paint tools. ## Create seams To begin texturing, you must first generate a **UV map**, or 2D projection, of the surface of your mesh. To set up this UV map, you tell Blender which edges of your mesh to use as seams when creating the projection. To create seams that naturally separate the front and back of the shirt: 1. With your clothing object selected, switch to **Edit mode**. 2. In the top-left, navigate to the Select modes and select **Edge select**. 3. Hold `Alt` and click on the center vertical edges of your shirt. The detected edge highlights. 4. Whenever a complete edge is selected, **right-click** and select **Make Seam**. The edge highlights to indicate the seam in your model. 5. **Repeat steps 3-4** to create a continuous seam across your mesh. ## UV unwrap After applying the seams, Blender now knows how to "unwrap" the mesh surface onto a 2D plane as a front and back surface. _UV-mapping of your 3D clothing mesh._ To UV Unwrap your object by your selected seams: 1. While in Edit mode, press `A` to highlight all vertices. 2. At the top of the viewport, select **UV** > **Unwrap**. 3. Switch to **Texture Paint** mode, your UVs display on the left window when your object is selected. --- title: "Make mesh watertight" url: /docs/en-us/art/accessories/creating/watertight last_updated: 2026-06-29T19:33:52Z description: "Seal off your geometry to ensure that there are no angles where the interior backfaces of your object are exposed." --- # Make mesh watertight With the shirt shape finalized, make your mesh **watertight** by "sealing" the holes at the neck, waist, and wrist. A watertight shape ensures that only the top visible surface of the mesh is exposed at any angle. If an asset is not watertight, it may expose backfaces, or single sided faces, that could affect the rendering of your asset and performance when equipped. _Watertight top view_ _Watertight bottom view_ To make your mesh watertight: 1. Select your mesh and switch to **Edit Mode**. 2. Starting with any hole in the mesh, hold `Alt` and click the **last edge** on the mesh. The entire edge highlights. 3. Press `E` to extrude the mesh and click after a small length is added. 4. With the new edge still selected, right-click and select **Merge Vertices** > **At Center**. 5. Press `G` to grab the new vertex and reposition it within your clothing mesh. 6. **Repeat steps 2-5** until all the interior-exposing holes of your mesh are closed. > **Success:** You've completed the modeling section of this tutorial. If desired, download a [reference project](../../../assets/art/reference-files/checkpoint/1_LongSleeve-Modeling-Complete.blend) of this stage and compare it against your work. > > There are a lot of techniques to create clothing. Try experimenting with the following techniques, tools, and processes to create additional unique assets: - Making asymmetrical clothing. - Blender's [cloth simulation](https://docs.blender.org/manual/en/latest/physics/cloth/examples.html#using-simulation-to-shape-sculpt-a-mesh) and other sculpting tools. - Various community sewing and fabric techniques for creating clothing on Blender. --- title: "Use the Accessory Fitting Tool" url: /docs/en-us/art/accessories/creating-rigid/converting last_updated: 2026-06-29T19:33:52Z description: "Use the Accessory Fitting Tool to convert your model to a rigid accessory." --- # Use the Accessory Fitting Tool After importing your asset into Studio, you can begin **fitting** your imported object to a mannequin and **converting** the `Class.Model` object into a `Class.Accessory`. When fitting and converting your accessory it's important to use the [Accessory Fitting Tool](/docs/en-us/avatar/accessory-fitting-tool.md) (AFT) to correctly preview the placement and apply the correct configurations to your accessory. To fit and generate your accessory: 1. In the toolbar's **Avatar** tab, click **Accessory** to open the AFT. 2. In the new AFT panel, select the **Part** field and, in the workspace, select the accessory `Class.MeshPart` object in the workspace and press **Next**. 3. Set **type** of asset to **Back**. Press **Next** when complete. 4. Using both the AFT preview window and the workspace, adjust the position, scale, and rotation of the accessory. 1. Use the AFT preview window and your mannequin as an accurate preview of how your asset fits on the character. The clothing mannequin in the workspace does not accurately portray how rigid accessories attach. 2. In the workspace, use the **Move**, **Scale**, and **Rotate** tools to adjust the positioning of your rigid accessory. 3. If you accidentally select something else, click back into the AFT panel to reselect the accessory and resume your adjustments using the transformation tools. 5. After previewing and fitting your asset, select the **dropdown** next to **Generate** button to select **Generate Legacy Accessory**. The accessory object populates in your viewport and in the **Explorer**. - For rigid accessories that you intend to sell on the Marketplace you must use the [generate legacy accessory](/docs/en-us/avatar/accessory-fitting-tool.md) option when creating your Marketplace accessories.![A dropdown appears above the GenerateMeshPartAccessory when expanded, displaying a Generate Legacy Accessory option.](../../../assets/accessories/accessory-fitting-tool/Generate-Legacy-Accessory.png) 6. Test your accessory by equipping it to a character and using the **Avatar Setup** tool. > **Success:** After successful fitting and converting, your 3D model should populate in your project as a `Class.Accessory`. With this `Class.Accessory` you can perform any of the following: - [Upload the accessory](/docs/en-us/art/accessories/creating-rigid/publishing.md) to the Marketplace. - Use the accessory in your current experience by equipping it to character models with [HumanoidDescription](/docs/en-us/characters/appearance.md#manually-modify-appearance), or by dragging and dropping the accessory under the appropriate character `Class.Model` object. - Save the accessory to your [Toolbox](/docs/en-us/projects/assets/toolbox.md) to share or use within any of your experiences. --- title: "Export from Blender" url: /docs/en-us/art/accessories/creating-rigid/exporting last_updated: 2026-06-29T19:33:52Z description: "Use Blender's .fbx exporter with the correct settings to create a Studio-ready asset." --- # Export from Blender After modeling and texturing your asset, you can begin the process of **exporting** your Blender project as a `.fbx` or `.gltf`. For up-to-date settings, see [Export settings](/docs/en-us/art/modeling/export-requirements.md). > **Warning:** If you are creating your own accessory object, it's important to clean up your project, which can involve deleting or removing any extra objects, such as lights, cameras, or mannequins, to ensure you only export the accessory mesh, and applying any modifiers to your mesh object. To export your model as a `.fbx`: 1. In the topbar, click **File**. 2. Select **Export**, then **FBX (.fbx)**. 3. On the right-hand side of the file view window, change the **Path Mode** property to **Copy**, then toggle the **Embed Textures** button. 4. Set the **Transform** > **Apply Scalings** to **FBX Unit Scale**. If you run into scaling issues on import, see [Blender FBX scaling](/docs/en-us/art/blender.md#adjust-scale-fbx) for alternative approaches. 5. Click the **Export FBX** button. > **Success:** You've completed the exporting section of this tutorial. If desired, download a [reference sample](../../../assets/art/accessories/creating-rigid/Rigid_Mask_Export.fbx) of this exported file for comparison. You can use this reference in the next importing step. --- title: "Use Studio's Importer" url: /docs/en-us/art/accessories/creating-rigid/importing last_updated: 2026-06-29T19:33:52Z description: "Use Studio's Importer to import a third-party 3D model into Studio." --- # Use Studio's Importer Studio's Importer provides a quick and easy way to import third-party 3D `.fbx` or `.gltf` assets into your projects. The importer provides object previews and error-checking to ensure that your asset meets Studio's general 3D requirements. To import your asset ([downloadable reference](../../../assets/art/accessories/creating-rigid/Chest-Texturing-Complete.fbx)): 1. From Studio's **File** menu, select **Importer**. 2. In the file browser, select the `.fbx` file saved locally. The Importer loads a preview of the object. 1. If textures don't load for your asset, continue to the next step and add textures manually later. 3. Select **Import**. 1. The asset populates in your workspace as a `Class.Model` that contains a `Class.MeshPart` with the appropriate textures applied as a `Class.MeshPart.TextureID`. 2. If textures were not imported correctly, follow the instructions below to add the file manually. **Add textures manually** If textures didn't load correctly, add them manually. You may need to save and publish your experience in order to access the [Asset Manager](/docs/en-us/projects/assets/manager.md). 1. In the **Asset Manager**, click the **Import** button. 2. Upload your image file. 3. After moderation clears for your image, select the `Class.MeshPart` parented within your imported `Class.Model`. 4. In the **TextureID** property, select the value field and add the asset ID of the texture image. > **Success:** After successful import, your model object should populate in your project as a `Class.Model` with the appropriate textures applied. See [Importer](/docs/en-us/studio/importer.md) for additional information on import settings and troubleshooting. --- title: "Rigid accessory creation" url: /docs/en-us/art/accessories/creating-rigid last_updated: 2026-06-29T19:33:52Z description: "This tutorial covers the basic steps required to publish a custom asset on the Marketplace." --- # Rigid accessory creation Rigid accessories are 3D objects that users can equip to their avatar characters in an experience or through Roblox's [Marketplace](https://www.roblox.com/catalog) and [Avatar Editor](https://www.roblox.com/my/avatar). Unlike clothing or bodies, rigid accessories do not require additional configuration in a third-party application beyond modeling and texturing, rigid accessories are typically the most basic type of 3D avatar item to create. This tutorial covers the basics of each step in the workflow to create your own simple 3D model in Blender and import it into Studio. From there, you can upload the accessory to the Marketplace to sell, save the asset to your toolbox, or use the asset in your experiences. > **Warning:** Creating, building, and sharing on Roblox is free. However, the last step of listing your item to sell requires a Roblox Plus or Premium account, an upload fee, and a publishing advance. For more information, see the [Marketplace policy](/docs/en-us/marketplace/marketplace-policy.md#creator-requirements). _ 3D mesh object created in Blender_ _ Mesh object equipped as an `Class.Accessory` in Studio_ > **Info:** While this content covers the Blender workflow with a provided reference example, you can apply the same concepts to other third-party modeling applications and custom assets. --- title: "Box modeling" url: /docs/en-us/art/accessories/creating-rigid/modeling-setup last_updated: 2026-06-29T19:33:53Z description: "Create your basic rigid accessory model in Blender." --- # Box modeling ![A screenshot of blender showing the final 3d shape of a treasure chest in the viewport](../../../assets/art/accessories/creating-rigid/Modeling-Complete.png) **Modeling** is the process of creating and shaping the 3D geometry of an object. **Box modeling** is a fundamental technique in 3D modeling combining basic shapes and steps to create a more complex object. The following instructions are based off of the [Box Modeling 101 staff article](https://devforum.roblox.com/t/modeling-101-in-blender-box-modeling/2963814). The original article includes additional information, as well as Blender setup instructions, best practices, and advanced modeling examples that are not included in this accessory tutorial. > **Warning:** Whether you are using an existing shape or creating your own 3D object, it's important to consider [technical requirements](/docs/en-us/avatar/rigid-accessories/specifications.md), such as keeping your geometry within a polycount budget, and [policy requirements](/docs/en-us/marketplace/marketplace-policy.md), such as ensuring your design does not infringe on other creator's IP both within and outside of the Roblox ecosystem. ## General shape In a new Blender file, delete everything except the starter cube, then create the basic shape of the treasure chest. 1. In a new project, select the non-cube objects and press `X` to delete. 2. Select the cube and press `S` for scale. 1. Press `Y` to lock scaling to the y-axis. 2. Drag with the mouse to create a rectangle. 3. Switch to Edit mode (`Tab`). 4. Near the Edit mode dropdown, select the Face selection. 5. Select the top face of the box and press `E` to extrude. 6. Drag your mouse to extrude the lid. Extend the lid to about half the height of the main body. ## Rounded lid Create the rounded lid shape using segmentation: 1. Near the Edit mode dropdown, select the Line selection. 2. Holding `Shift`, click the top front and back lines of your chest. 3. With both lines selected, press `Ctrl``B`/`⌘``B` to bevel. 4. In the context menu at the bottom right, set the number of segments to `6` and enable **Clamp Overlap**. ### Merge vertices Sometimes tools like the **Bevel** tool may move vertices very close to each other without merging them. Use the Merge Vertices function to ensure that your object shares vertices wherever possible. 1. Near the Edit mode dropdown, select the Vertices selection. 2. Press `A` to select all vertices. 3. Right-click and select **Merge Vertices by Distance**. 4. In the pop-up modal, set the distance to `.01`. ## Side insets Create the left and right insets of your chest: 1. Near the Edit mode dropdown, select the Face selection. 2. Hold `Shift` and click both sides of your shape. This selects both the left and right faces. 3. Press `I` for inset. Drag the mouse to adjust the amount of inset for the new face. 1. Set the inset about the size of your bevel segmentations. 4. With your new insets selected, right-click and select **Extrude Along Normals**. 5. Use the mouse to drag and adjust the distance of extrusion into the chest. ## Front insets Create the front and back insets of your chest by creating new lines and then extruding your faces between those lines. ### Vertical loop cuts To create your vertical loop cuts: 1. Near the Edit mode dropdown, select the Line selection. 2. Press `A` to select the entire chest. 3. Use `Ctrl``R`/`⌘``R` to create a loop cut. 4. Using your mouse, hover over the object until the highlighted cut is vertical. Click to confirm. 5. In the context menu, set the number of cuts to `2`. 6. With the new lines selected, press `S` to scale and `Y` to scale within the y-axis. 7. Use the mouse to drag the lines until they nearly reach the edges of your chest. Click to confirm. ### Horizontal loop cuts To create your horizontal loop cuts: 1. Select the chest, and press `Ctrl``R`/`⌘``R` to loop cut. 2. Using the mouse, click to confirm a horizontal cut. 3. Using the context menu, set the number of cuts to `2`. 4. To straighten each line across your object, select the one of your new lines: 1. Press `S` for scale. 2. Press `Z` to scale within the z-axis. 3. Press `0` to set the scale value to `0` across the z-axis. 5. Repeat step 4 with the other line. 6. Position the top line near the top below the segmentations. 7. Position the bottom line near the bottom. 1. Enable **Magnet Snapping** so the bottom line merges with the existing vertices and lines from the side insets. ### Extrusions Create the extrusions for the front, top, and back of the chest. 1. Near the Edit mode dropdown, select the Face selection. 2. Hold `Alt`/`⌥` and click on your front faces to select the front, top, and back faces. 3. With the faces selected, hold `Shift` and click on the metal borders to deselect them. The front and back faces, and the top segmentations, should remain selected. 4. Right click and select **Extrude Faces Along Normals**. Drag the mouse to extrude the faces about the same width as the metal borders. ## Complete border The chest is almost ready, but is still missing the continuous metal border across the top edge. Remove the existing faces and add new geometry to the treasure chest. ### Delete faces Delete the faces of the top side metal borders: 1. Near the Edit mode dropdown, select the Face selection. 2. Starting on any side, shift click the three faces of the top metal border. 3. Press `X` to delete. 4. Repeat steps 2-3 on the other side. ### Add faces Add new faces to the sides that complete the geometry of the treasure chest box. 1. Near the Edit mode dropdown, select the Line selection. 2. Starting with any side, Hold `Shift` and click the two top corner edges of the missing face. 3. Right click and select **New Face from Edges** to create a top face. 4. Repeat steps 2-3 with the bottom corner edges to create a bottom face. 5. On the other side of the chest, repeat steps 2-4 to complete the metal border. > **Success:** You've completed the modeling section of this tutorial. If desired, download a [reference version](../../../assets/art/accessories/creating-rigid/Chest-Modeling-Complete.blend) of this stage of the project for comparison. > > This tutorial represents an extremely basic overview of the 3D modeling process. Tools like Blender offer many features, workflows, and techniques to create unique and complex models. Check out Blender's official and community tutorials for additional instructional content. --- title: "Upload and publish" url: /docs/en-us/art/accessories/creating-rigid/publishing last_updated: 2026-06-29T19:33:53Z description: "Before selling your accessory, upload the accessory for validation and moderation." --- # Upload and publish After generating your `Class.Accessory` item, you can now begin the process of **publishing** the asset to the Marketplace. This step is optional and only applicable for creators who intend to sell their asset. For full information on the publishing process, see [uploading and publishing to the Marketplace](/docs/en-us/marketplace/publish-to-marketplace.md). > **Warning:** Users must meet [creator requirements](/docs/en-us/marketplace/marketplace-policy.md#creator-requirements) to be eligible to sell assets on the Marketplace. Creators must also provide an upload fee and a payment advance during the upload and publishing process. Adding your asset to the Marketplace involves 2 steps: 1. **Uploading** - Uploading the asset to Roblox servers for moderation. 2. **Publishing** - Listing the item on sale on the Marketplace. ## Upload To upload your accessory: 1. **(Recommended)** Create a thumbnail of your accessory to ensure your thumbnail correctly captures the look of your accessory. For instructions, see [custom thumbnails](/docs/en-us/marketplace/custom-thumbnails.md). 2. In the **Explorer**, right-click your accessory object and select **Save to Roblox…** from the contextual menu. 3. In the **Asset Configuration** window, set the **Content Type** to **Avatar Item**. 4. Complete the following fields (you can adjust these later): 1. **Title**: The name of your accessory. 2. **Description**: A short description of your asset. 3. **Asset Category**: The type of accessory. This should match the Accessory Type selected during the [fitting and conversion](/docs/en-us/art/accessories/creating-rigid/converting.md) process. 4. **Creator**: Use the dropdown to select if you'd like to publish this asset as an individual or as part of an associated group. 5. After you select the **Asset Category**, Studio begins validating the asset to ensure that it matches Roblox's accessory technical requirements. 6. If the validation is successful, you can submit the asset to the upload and moderation queue for a fee. See [Fees and commissions](/docs/en-us/marketplace/marketplace-fees-and-commissions.md) for current fee information. ## Publish After uploading your accessory, the asset is added to the moderation queue and may take up to 24 hours to clear. You can check your accessory's moderation status in the [Creator Hub](https://create.roblox.com/dashboard/creations). After moderation completes, your item's publishing details become available to edit and enable for sale. See the following for an overview on the various sale options available: After listing an asset to the Marketplace, you can return to this page to edit your item's sale details, or take the item offsale. --- title: "Texture painting" url: /docs/en-us/art/accessories/creating-rigid/texturing last_updated: 2026-06-29T19:33:53Z description: "Covers the process of associating PBR textures to rigid accessories in Blender." --- # Texture painting ![A screenshot of blender showing the final 3d shape and color of a treasure chest in the viewport](../../../assets/art/accessories/creating-rigid/Texturing-Complete.png) **Texturing** is the process of applying a surface appearance to a 3D object. **Texture painting** is a technique that allows you to digitally brush your surface colors onto your 3D object, or onto the 2D image that represents the surface of your object. This tutorial covers basic texture painting in Blender. Common workflows in the industry utilize additional third-party tools to create textures, especially high-definition PBR textures that mimic realistic lighting and texture properties. > **Warning:** While PBR textures are not required for accessories and is not covered in this tutorial, adding PBR textures can add extra visual flair and realism to elevate your creations. This typically requires additional software. For more information, see [PBR textures](/docs/en-us/art/modeling/surface-appearance.md). ## UV projecting Use Blender's automatic UV Project feature to "unwrap" your 3D object onto a 2D plane. This allows you to associate a 2D image to the 3D surface of your object. 1. In Edit mode, press `A` to select your object. 2. In the top UV menu, select **Smart UV Project**. 3. Set Island Margin to `.02`. ## Add new material Add a new material for Blender to associate this new texture, and assign it to a new blank 2D image. 1. In the bottom right panel, select the red **Materials** tab. 2. Select the **+ New** button. Additional material options display below. 3. In **Base Color**, select the dot. A dropdown appears. 1. Select **Image Texture**. 4. Under Base Color, select the **+ New** button. 1. Name the texture image file. Using an affix like "_TXT" can help organize your files later. 2. Select the color and pick a color. This tutorial recommends using a metallic color for your metal borders to save time. ## Texture painting Texture painting allows you to paint directly on the 3D object or the 2D mapping of the surface. Since Blender automatically mapped the 2D atlas of the texture, it's not easy to tell what parts of the 2D image maps to the 3D object. First mark the sections of the 3D object you want to paint solid before completing your texture by painting the 2D map. > **Info:** There are many ways to texture within Blender. To keep this process simple, the tutorial used Blender's [Smart UV Project](https://docs.blender.org/manual/en/2.79/editors/uv_image/uv/editing/unwrapping/mapping_types.html), but there are many ways to manually create your 2D texture islands and organize your mesh and texture. ### Mark 3D object Switch to Texture Paint mode and use the paintbrush to track the "wood" parts of the treasure chest. 1. In Edit mode, select your object. 2. Switch to **Texture Paint** mode. A side-by-side panel displays with your 2D atlas on the left and the 3D object on the right. 3. Expand the Tool submenu in the top-right of either window. 4. Select your brush settings and mark the wooden areas of your chest. 1. Use `Ctrl``Z`/`⌘``Z` to undo any accidental brushes on the metal border. 2. Hold `F` and drag the mouse to adjust brush size. ### Paint 2D map With all of the wooden areas of your chest marked, you can now quickly texture your 2D atlas. First adjust your brush settings to have a hard edge and then begin painting the 2D image. 1. On the left window, access to **Tool** menu in the top right of the window. 1. Set the **Falloff** shape to the flattest icon to ensure the edges are sharp. 2. Set the color or any other brush settings here. 2. In the left window, begin coloring in each island of your texture. You can quickly preview the changes on your 3D object on the right side. 1. Use `Ctrl``Z`/`⌘``Z` to undo any accidental brushes on the metal border. 2. Hold `F` and drag the mouse to adjust brush size. 3. After completion, navigate to **Image** > **Save** to save your image file. > **Success:** You've completed the texturing section of this tutorial. If desired, download a [reference version](../../../assets/art/accessories/creating-rigid/Chest-Texturing-Complete.blend) of this stage of the project for comparison. --- title: "Accessories from an existing model" url: /docs/en-us/art/accessories/from-existing last_updated: 2026-06-29T19:33:53Z description: "This tutorial covers the basic steps required to publish a custom asset on the Marketplace." --- # Accessories from an existing model In many cases, you may want to convert a premade model from your modeling software into an accessory on Roblox. If you are looking to create your accessory from scratch, see [Create accessories](/docs/en-us/art/accessories/creating-rigid.md). Using a provided 3D reference file, this tutorial covers each step in the workflow to properly configure and export a 3D model with PBR textures from Blender and generate your own **rigid accessory** in Studio. After you create the accessory, you can upload it to the Marketplace, save it to your toolbox, and use it in your own experiences. > **Info:** This tutorial only covers rigid accessories, and does not cover the process of converting more advanced clothing or character body models. _ Mask asset as an untextured mesh object in Blender_ _ Mask asset equipped as an `Class.Accessory` in Studio_ Using a provided reference 3D asset, this tutorial covers the following rigid accessory workflow: 1. Modeling overview and requirements in Blender. 2. Texturing setup using PBR textures in Blender. 3. Exporting your asset as a `.fbx` from Blender. 4. Importing the asset into Studio. 5. Fitting and converting the imported model to an `Class.Accessory` object. 6. Publishing and validating the accessory for Marketplace upload. > **Info:** While this content covers the Blender workflow with a provided reference example, you can apply the same concepts to other third-party modeling applications and custom assets. ## Setup model Whether you are using an existing shape or creating your own 3D object, it's important to consider [technical requirements](/docs/en-us/avatar/rigid-accessories.md), such as keeping your geometry within a polycount budget, and [policy requirements](/docs/en-us/marketplace/marketplace-policy.md), such as ensuring your design does not infringe on other creator's IP both within and outside of the Roblox ecosystem. Correctly setting up your asset in Blender helps reduce importing and rendering issues later in Studio. When importing Roblox-related `.fbx` files, such as the provided [mask asset](../../assets/art/accessories/creating-rigid/Rigid_Mask_Model-Only.fbx), you might discover that your asset imports at a 1/100 scale due to the `.fbx` conversion. In your Blender project, you can quickly reset the scale to make the asset easier to work with in the Blender environment. > **Info:** If you are creating your own rigid accessory from scratch, it's important to understand Roblox's [standard avatar sizes](/docs/en-us/art/accessories/body-scale.md), especially for rigid accessories that contour around a body part, such as a hat or bangle. Using the [Sci Fi Mask](../../assets/art/accessories/creating-rigid/Rigid_Mask_Model-Only.fbx) reference as an example, use the following instructions to import and set up your rigid accessory model in Blender: 1. Open a new Blender project. 2. Press `A` to highlight all and `X` to delete the default starting cube and cameras. 3. Navigate to **File** > **Import** > **FBX** and select the downloaded reference model. 4. If the object imports at a small scale, **select** the object and navigate to the **Properties** panel > **Object Properties** > **Transform** and adjust the **X**, **Y**, **Z** to `1.000`. 5. If you are sculpting your asset from scratch, orient the object in your workspace. If you are importing, you may not need to make any adjustment. 1. Make sure your asset is facing **-Y forward**. 2. Ideally your accessory should be moved to `0`,`0`,`0` in the world to ensure it imports at the center of the camera in Studio. > **Success:** You've completed the modeling section of this tutorial. If desired, download a [reference version](../../assets/art/accessories/creating-rigid/Rigid_Mask_Texturing-Completed.blend) of this stage of the project for comparison. > > There are many tools and workflows to create your own unique asset. For additional suggestions, try creating different asset types, such as shoulder pads or belts, or importing a reference model into Blender as a mannequin to sculpt and shape your cosmetics from scratch. ## Apply texture **Texturing** is the process of applying a surface appearance to a 3D object. Blender provides various tools and features to create and connect your own texture maps to your asset, allowing you to preview your model's final appearance and link the texture images to your exported file. The mask example asset uses [physically-based rendering (PBR) textures](/docs/en-us/art/modeling/surface-appearance.md), which are advanced textures that create realistic surfaces under different lighting environments. PBR textures use multiple image files, or **maps**, to represent the various surface properties of your 3D object. _ Color (Albedo) Map_ _ Normal Map_ _ Roughness Map_ _ Metalness Map_ This tutorial doesn't cover the PBR texture creation process, which typically involves using third-party software such as ZBrush or Substance 3D Painter. Instead, this section goes over the process of bringing premade PBR image files into Blender and properly associating them to your asset on export. > **Warning:** While PBR textures are not required for accessories, adding PBR textures can add extra visual flair and realism to elevate your creations. For an example on using Blender to create a basic, non-PBR texture, see [Texture basic clothing](/docs/en-us/art/accessories/creating/unwrapping.md). To configure and link your PBR textures to your model: 1. Download [Rigid_Mask_Textures.zip](../../assets/art/accessories/creating-rigid/Rigid_Mask_Textures.zip) and unzip the textures images locally in the same directory as your Blender project. 2. In Blender, navigate to the **Shading** tab. Ensure that your object is selected. 1. If you don't see the **PrincipledBSDF node**, select the **+New** button to create a new material. 3. From your file browser, drag and drop your texture `.png` files into the node section. A new image node appears with each file. 4. In the newly created node, click and drag the following image nodes to their appropriate connection on the Principled BSDF main node: 1. **_ALB texture**: Connect the **Color** node to **Principled BSDF** > **Base Color**. 2. **_MTL texture**: Connect the **Color** node to **Principled BSDF** > **Metallic**. 3. **_RGH texture**: Connect the **Color** node to **Principled BSDF** > **Roughness**. 4. **_NOR texture**: 1. Click **Add** > **Vector** > **Normal Map** to generate a NormalMap node. This node is required to convert Normal PBR image maps. 2. Connect the _NOR node's **Color** to the NormalMap node's **Color** connection. 3. Connect the NormalMap's **Normal** to the **Principled BSDF** > **Normal**. 5. Test your textures by changing the viewport viewing mode to **Viewport Shading > Material Preview Mode**. > **Success:** You've completed the texturing section of this tutorial. If desired, download a [reference sample](../../assets/art/accessories/creating-rigid/Rigid_Mask_Texturing-Completed.blend) of this stage of the project for comparison. > > If you are creating your own PBR textures, check out [Material references](/docs/en-us/art/modeling/material-reference.md) for various PBR material examples you can apply to your next accessory. ## Clean up After modeling and texturing your asset, you can begin the process of **exporting** your Blender project as a `.fbx`. The start of this process includes cleaning up your project, which can involve deleting or removing any extra objects, such as lights, cameras, or mannequin meshes, to ensure you only export the accessory mesh, and applying any modifiers to your mesh object. An often forgotten cleanup step involves **applying your transformations**, also known as **freezing your transforms**, by setting your orientation, rotation, and scale deltas to zero. Failure to apply any transformations can result in unexpected behavior and orientation when importing the mesh in Studio. To freeze your transforms: 1. In Object mode, select your mesh object. 2. Navigate to **Object** > **Apply** > **All Transforms**. ## Export from modeling tool After modeling and texturing your asset, you can begin the process of **exporting** your Blender project as a `.fbx`. The start of this process includes cleaning up your project, which can involve deleting or removing any extra objects, such as lights, cameras, or mannequins, to ensure you only export the accessory mesh, and applying any modifiers to your mesh object. For the latest export settings for Blender, see [rigid accessory export settings](/docs/en-us/avatar/rigid-accessories/export.md). > **Success:** You've completed the exporting section of this tutorial. If desired, download a [reference sample](../../assets/art/accessories/creating-rigid/Rigid_Mask_Export.fbx) of this exported file for comparison. You can use this reference in the next importing step. ## Import to Studio Studio's Importer provides a quick and easy way to import third-party 3D assets into your projects. The importer provides object previews and error-checking to ensure that your asset meets Studio's general 3D requirements. To import your asset, check out the latest information on [importing rigid accessories](/docs/en-us/avatar/rigid-accessories/import.md). ## Convert After importing your asset into Studio, you can begin **fitting** your imported object to a mannequin and **converting** the `Class.Model` object into a `Class.Accessory`. When fitting and converting your accessory it's important to use the [Accessory Fitting Tool](/docs/en-us/avatar/accessory-fitting-tool.md) (AFT) to correctly preview the placement and apply the correct configurations to your accessory. To convert your asset into a `Class.Accessory`, check out the latest information on [converting rigid accessories](/docs/en-us/avatar/rigid-accessories/import.md#convert-rigid-accessories). ## Upload and publish After generating your `Class.Accessory` item, you can now begin the process of **publishing** the asset to the Marketplace. This step is optional and only applicable for creators who intend to sell their asset. For additional information and instructions on this process, see [uploading and publishing instructions](/docs/en-us/marketplace/publish-to-marketplace.md). You now have your accessory added to the Marketplace catalog! Use the item's Marketplace link to view your listing at any time, or to send to your friends and followers for additional engagement. > **Info:** See the following resources for additional Marketplace policy and related information: - [Marketplace policy](/docs/en-us/marketplace/marketplace-policy.md) - [Fees and commissions](/docs/en-us/marketplace/marketplace-fees-and-commissions.md) - [Intellectual property](/docs/en-us/marketplace/intellectual-property.md) - [Moderation](/docs/en-us/marketplace/moderation.md) --- title: "Publish bodies with eyelashes and eyebrows" url: /docs/en-us/art/accessories/publish-eyebrows-eyelashes last_updated: 2026-06-29T19:33:53Z description: "Eyelashes and eyebrows are optional accessory items you can equip to an avatar to publish in the Marketplace." --- # Publish bodies with eyelashes and eyebrows Eyelashes and eyebrows are cosmetics that can help emphasize facial features and create a unique way of customizing a character. Similar to clothing, eyelashes and eyebrows are [3D layered clothing](/docs/en-us/avatar/layered-accessories.md) that can stretch and fit over different face shapes and can deform and stretch with character face animations. _Character body without eyelashes and eyebrows_ _Character body with eyelashes and eyebrows_ _Standalone eyelashes and eyebrows_ When publishing eyelashes and eyebrows, you must parent these `Class.Accessory` objects under the avatar character's `Class.Model` prior to uploading the character `Model` to the Marketplace. These `Accessory` face objects must adhere to Roblox's accessory requirements and include layered components such as caging, rigging, and skinning components. For an overview on creating your own eyelashes and eyebrows, see [Create face accessories](/docs/en-us/art/characters/facial-animation/create-face-accessories.md). Eyebrows and eyelashes must use [Automatic Skinning Transfer](/docs/en-us/avatar/automatic-skinning-transfer.md) with the `RBX_Leader` and `RBX_Follower` special joints to be published on the marketplace. > **Warning:** At this time, you can only bundle **eyelashes**, **eyebrows**, and **hair** with your body on Marketplace upload. Other facial accessories, such as horns, makeup, glitter, or other cosmetics are not acceptable. See [Marketplace policy](/docs/en-us/marketplace/marketplace-policy.md) for specific information on allowable cosmetics and other policy regulations and guidelines. ## Configure eyelash and eyebrow accessories Use the following steps to import your face accessory and body assets into Studio. After converting the eyelashes and eyebrows to `Accessory` objects, parent the `Accessory` objects within the character `Model` before uploading the character to the Marketplace. You can download reference `.fbx` models of [eyelashes](../../assets/art/reference-files/Eyelashes.fbx), [eyebrows](../../assets/art/reference-files/Eyebrows.fbx), and an [avatar body](../../assets/art/reference-files/AnimeFemale-Studio-Ready.fbx) to follow along this process. 1. In the [Importer](/docs/en-us/studio/importer.md), import the `.fbx` files of your face accessory and body. The eyelashes, eyebrows, and body each populate in your Workspace as a `Model`. 1. If you are using the reference body, set **Rig General** > **Rig Type** to **Rthro**. 2. Use the [Accessory Fitting Tool](/docs/en-us/avatar/accessory-fitting-tool.md) and select the **Eyelash** or **Eyebrow** model and click **Next**. 3. Select the appropriate **Accessory** > **Eyelash** or **Accessory** > **Eyebrow** asset type and click **Next**. 4. (Optional) Preview the fit of your face accessory. 1. If the face accessory was modeled on a specific character body, you may want to import that character body into Studio and use it as a mannequin in the fitting tool's preview. See [Accessory Fitting Tool](/docs/en-us/avatar/accessory-fitting-tool.md) for more information on adding custom characters. 2. If using the provided reference body, you can continue to step 5. 5. Click **Generate MeshPart Accessory** to create your MeshPart. 6. **Repeat steps 2-5** for the other face accessory. 7. In the Outliner, **drag and drop** the face `Accessory` objects within the character `Model` instance. The accessories automatically equip and attach to the correct areas of your avatar character._Workspace preview of body with face accessories__Outliner hierarchy_ 8. If required, rename the accessory objects to `EyelashAccessory` or `EyebrowAccessory` accordingly. 9. Once you are ready to upload the avatar model with your face accessories, see [Upload an asset](/docs/en-us/marketplace/publish-to-marketplace.md#upload-an-asset) for instructions on uploading and publishing your avatar body with eyelashes and eyebrows to the Marketplace. --- title: "Rigging and caging models (Blender)" url: /docs/en-us/art/accessories/rig-and-cage-existing-models last_updated: 2026-06-29T19:33:53Z description: "Convert a 3D model into a layered 3D model in Blender before importing and converting the model into Studio as an Accessory." --- # Rigging and caging models (Blender) You can use a third party modeling tool, such as [Blender](https://www.blender.org) or [Maya](https://www.autodesk.com/products/maya/overview), to modify an existing 3D object into a layerable 3D model that you can [import and convert](/docs/en-us/avatar/accessory-fitting-tool.md) into Studio as an `Class.Accessory`. To create a layered model, you must **parent** (Blender) or **bind** (Maya) a 3D model to a mannequin armature and then modify two additional meshes to fit around the inner and outer surface of the accessory model. This guide covers the basic workflow for converting a 3D model into a layerable model in Blender using the following steps: 1. [Importing](#import) a 3D asset into a template Blender project file. 2. [Parenting](#parent-and-weight) the imported mesh object to the project file's armature rig with Blender's Automatic Weights feature. 1. Manual skinning is not covered, see [Skin a simple mesh](/docs/en-us/art/modeling/skin-a-simple-mesh.md) for an overview of manual skinning. 3. [Editing](#edit-inner-and-outer-cage-meshes) the Inner and Outer Cage meshes to represent the inner and outer surface of the model. A full list of asset requirements for a layered model can be found in [Layered model specifications](/docs/en-us/avatar/rigid-accessories/specifications.md). > **Info:** This guide uses [Blender version 3.0](https://www.blender.org/download/releases/3-0/). If you are using another version of Blender, there may be minor differences in UI and settings. ## Import Set up the project by opening the [rig template project](../../assets/modeling/meshes/reference-files/Rig_and_Attachments_Templates.zip) and importing the [sample accessory model](../../assets/accessories/reference-files/Tshirt-model.fbx). To import and parent the accessory model: 1. In Blender, open the [rig template project](../../assets/modeling/meshes/reference-files/Rig_and_Attachments_Templates.zip). This file contains the default armature required for layered clothing.![Blender Mannequin Template Project](../../assets/accessories/lc-blender-template-opened.png) 2. In the Outliner, toggle the Hide icon to visually remove any non-armature objects in your workspace, such as Attachments. 3. In the File Menu, select **Import** and click **FBX (.fbx)**. Select the accessory model file you intend to fit onto the mannequin. The accessory populates in the workspace and, depending on how the asset was modeled, may need resizing or adjustment to fit on the mannequin. ## Parent and weight For this basic accessory, parent the imported mesh to the template mannequin armature using Blender's [Automatic Weights](https://docs.blender.org/manual/en/latest/animation/armatures/skinning/parenting.html#with-automatic-weights) feature. Parenting the accessory model mesh to the armature in this way enables the 3D model to move and deform naturally with the character in Studio. > **Warning:** Complex clothing and accessory items with moving parts may require more precise skinning with Blender's [Weight Painting](https://docs.blender.org/manual/en/3.5/grease_pencil/modes/weight_paint/introduction.html#weight-paint) or other skinning tools. See [Skin a simple mesh](/docs/en-us/art/modeling/skin-a-simple-mesh.md) for an example of manual skinning. To parent the mesh object with the mannequin armature: 1. In the Outliner, temporarily hide both of the Cage objects to expose the Armature by toggling the Hide icon. 2. In the Workspace, click the mesh object, then hold `Shift` and **click** the mannequin's armature. 3. Right click, or use `Ctrl``P` (`⌘``P`) to parent the accessory to the armature by selecting **Automatic Weights**. 4. Reduce visual clutter by hiding all objects except the **cages** and **model**. Click on the symbol next to the object in the Outliner panel. ## Edit inner and outer cage meshes After parenting and weighting your clothing item, you can start adding the cage meshes to your clothing to indicate the inner and outer surfaces of your clothing. When setting up the cages, the accessory model needs to fit on top of the inner cage and then the outer cage must be adjusted to fit tightly over the model object. You can quickly add these two full-body meshes from the [Clothing_Cage_Template.blend](../../assets/modeling/meshes/reference-files/Clothing_Cage_Templates.zip) into your existing project. > **Error:** Don't delete vertices or alter the UVs on the Inner or Outer Cages as this can cause errors when importing in Studio or when equipping onto a character. To quickly add clothing cages to your project: 1. Download the [Clothing_Cage_Template.blend](../../assets/modeling/meshes/reference-files/Clothing_Cage_Templates.zip) project. 2. In your current Blender project, switch to Object Mode and navigate to **File** > **Append**. A file browser displays. 3. Click on the Clothing_Cage_Template.blend file and navigate to the **Object** folder. 4. In the Object folder, hold `Shift` and select both `YourClothingName_InnerCage` and `YourClothingName_OuterCage` and click **Append**. The two cage mesh objects populate in your project. To modify cages for your accessory model: 1. In the Outliner panel, rename the **_InnerCage** and **_OuterCage** meshes to match the imported mesh object (ex. Tshirt_InnerCage, Tshirt_OuterCage). Make sure this exactly matches the mesh name that you imported. 2. In the Outliner window, select the _OuterCage mesh and switch to **Edit Mode** `Tab`. 3. In the Viewport, enable **X-Ray** view and **Materials View** to improve visibility and access to the vertices of the outer cage mesh. 4. While in Edit Mode, adjust the vertices with the Edit Mode tools so that the Outer Cage completely covers the top of the accessory. > **Info:** When manipulating vertices, try to keep each face (the space between vertices) consistently sized and retain symmetry wherever possible. Use the symmetry toggles on the top right of the Viewport when applicable. A finished outer cage should completely cover the accessory with minimal extra space. For comparison, you can download a completed caged version of the Tshirt model [here](../../assets/accessories/reference-files/Tshirt-caged.fbx). When finished with the caging and modeling of your asset, see [Exporting requirements](/docs/en-us/art/modeling/export-requirements.md#blender) for details on exporting the model from Blender. After exporting, see [Accessory Fitting Tool](/docs/en-us/avatar/accessory-fitting-tool.md) and [Converting clothing accessories tutorial](/docs/en-us/art/accessories/creating/converting.md) for instructions on importing and converting the model into a usable accessory. ## Tips for editing cages The following tips may be helpful when manipulating your cage vertices: - On the top-right of the Viewport, toggle different [Viewport Shading](https://docs.blender.org/manual/en/latest/editors/3dview/display/shading.html) options, such as **Material Preview** and **X-Ray**, when necessary to better access vertices and view your caging progress. - You can hide vertices you're currently not adjusting by using **Hide** (`H`) and **Unhide** (`Alt``H`; `⌥``H`). - Multiple vertices can be selected and adjusted at the same time. Use **Move**, **Scale**, or any other Blender tools to make adjustments to multiple vertices at once. --- title: "Clothing Validation Tool" url: /docs/en-us/art/accessories/validation-tool last_updated: 2026-06-29T19:33:53Z description: "The Layered Clothing Validation Tool checks for common clothing creation issues in Blender or Maya before exporting." --- # Clothing Validation Tool The **Layered Clothing Validation Tool** is a supplemental tool you can install in either [Blender](https://www.blender.org/download/releases/3-0/) or [Maya](https://www.autodesk.com/products/maya/overview) to help quickly identify and fix common issues with layered clothing assets before you export them. _Blender Validation Tool_ _Maya Validation Tool_ While this tool can save you time in the typical iteration process between your 3D modeling program and Studio, the tool doesn't provide comprehensive verification of all aspects of layered clothing assets. You must ensure your layered clothing model meets both the [general mesh requirements](/docs/en-us/art/modeling/specifications.md) and Roblox-specific [layered clothing requirements](/docs/en-us/avatar/layered-accessories/specifications.md) prior to importing it into Studio. ## Install the Clothing Validation Tool There are two separate installation files and instructions for Blender and Maya. > **Info:** The Layered Clothing Validation Tool is designed for both [Blender 3.0](https://www.blender.org/download/releases/3-0/) and [Maya 2022](https://www.autodesk.com/products/maya/overview). You can download and modify the tool for any use. ### Blender To install the Clothing Validation Tool in Blender: 1. Download [ValidationTool_Blender.zip](../../assets/accessories/validation-tool/ValidationTool_Blender.zip) and save the `.zip` file locally. 2. In Blender, navigate to **Edit** > **Preferences**. 3. In Preferences, go to the **Add-Ons** section on the left side-bar. 4. Click the **Install...** button. A file browser displays. 5. Select the downloaded `.zip` file and click **Install Add-On**. 6. In the **Add-Ons section**, find the **Validation Tool** and **enable the add-on**. 7. Return to your workspace and **expand the side toolbar** in the Viewport to access the add-on._Sidebar indicator__Expanded sidebar_ ### Maya Before you begin the process of installing the Validation Tool in Maya, you must install [Python 3.0](https://www.python.org/downloads/) or higher. If you already have Python installed, you can proceed to installing the Validation Tool. To install Python 3.0+ on your device: 1. Download the appropriate installer from the official [Python website](https://www.python.org/downloads/). 2. Open the installer and follow the installation instructions. - Enable **Add Python ### to PATH** before selecting **Install Now**. To install the Clothing Validation Tool in Maya: 1. Download [ValidationTool_Maya.zip](../../assets/accessories/validation-tool/ValidationTool_Maya.zip) and unzip the content in a local directory. 2. Open the contents of the `.zip` file in a file browser. - If on Windows, **double-click** `install.bat`. This runs a batch script that enables you to quickly run the Validation Tool in Maya. - If on Mac, **right-click** the `install.command` file and select **Open With** > **Terminal**. This runs a terminal script that enables you to quickly run the Validation Tool in Maya. > **Warning:** If on Mac, you may need to adjust the read/write permissions for the `install.command` file to run successfully. This step requires administrator access. > > To allow the `install.command` file to have full read and write access: 1. Open **terminal** and use the `cd` command to navigate to the directory of the saved `install.command` file. 2. Type the command `chmod 777 install.command` and press **Enter**. This should enable all read and write privileges for this file. 3. When prompted, type your Maya version and press **Enter**. For example, if using Maya 2020, input **2020** as your version. 4. When installation is successful, the following message displays: 5. Open Maya and navigate to **Windows** > **General Editors** > **Script Editor**. 6. When using the plugin for the first time, in the Script Editor, type `import ValidationTool` then press the **Play** button in the top bar of the Script Editor. The plugin UI displays. - When using the plugin after the initial launch, launch the plugin by typing `ValidationTool.validationTool()` and clicking the **Play** button. - Alternatively, you can set up a shortcut from the plugin UI after initial launch by clicking the **Create shortcut button on Maya shelf** button. ## Validate assets With the plugin active and a layered asset in your workspace, you can begin validating content. After the check, results with issues change to **red** (Blender) or **yellow** (Maya). You can resolve some failed checks by clicking the check button. See [Checks and Troubleshooting Steps](#checks-and-troubleshooting-steps) for details on each validation check. _Checks without issues display as grey (Blender) or green (Maya)._ _In this example, potential intersection issues are highlighted in the Viewport._ To use the Validation Tool on your asset: 1. **Select** the object in your scene. 2. Open the Validation Tool and click **Check Assets**. After a few moments, the UI updates and produces an output message. 3. Results with issues are highlighted with red or yellow. Some issues can be automatically corrected by clicking the highlighted button. > **Info:** This Validation Tool only checks for common clothing creation issues. Ensure your layered clothing model meets both the [general mesh specifications](/docs/en-us/art/modeling/specifications.md) and Roblox-specific [layered clothing specifications](/docs/en-us/avatar/layered-accessories/specifications.md) before uploading to Studio. ## Checks and troubleshooting steps Refer to the table below for details on the specific checks and troubleshooting steps:
**Layers**
Checks for extra layers in the scene. Automatic fix attempts to remove extra layers. If the automatic fix fails, manually remove extra layers.
**Transforms**
Checks that geometry location, rotation, and scale are frozen (`0`,`0`,`0`). Automatic fix attempts to freeze the transforms. This may cause changes to your rig. If the automatic fix fails, manually freeze your geometry.
**Unused Material**
Checks for any unused data in the scene, such as [orphan data](https://docs.blender.org/manual/en/latest/editors/outliner/interface.html?highlight=orphan) (Blender) or [construction history](https://knowledge.autodesk.com/support/maya/learn-explore/caas/CloudHelp/cloudhelp/2022/ENU/Maya-Basics/files/GUID-503E227B-EF49-4A78-B3CA-7EAC588017C9-htm.html) (Maya), or unused materials. Automatic fix attempts to remove extra data. If automatic fix fails, manually remove unnecessary data and objects.
**Keyframes**
Checks for any unnecessary keyframes. Automatic fix attempts to remove keyframes. If automatic fix fails, manually remove keyframe data.
**Attachment Points**
Checks that attachment points are present and use the correct naming convention. Automatic fix attempts to parent any unexpected attachment points.
**Intersection**
Checks for any intersection between character geometry and the inner/outer cage mesh geometry. In Blender, clicking the button switches to **Edit Mode** and highlights vertices that are intersecting the inner or outer mesh. Some edges, such as edges of the holes for the sleeves, neck and torso edges, may be highlighted by default behavior and can be ignored.
**Texture Format**
Checks that textures match texture requirements, such as resolution size. Users can also select an external image file. Refer to the output messages to manually resolve any texture related issues.
--- title: "Blender" url: /docs/en-us/art/blender last_updated: 2026-06-29T19:33:53Z description: "Learn about Blender tools." --- # Blender Blender is a free, open-source 3D creation suite with a broad range of modeling, sculpting, texturing, and animation tools. Running on Linux, macOS, and Windows systems, this cross-platform application is a popular choice for creators who want to make avatars, accessories, and 3D objects for Roblox experiences. While this is by no means an exhaustive list, the following guide offers high-level information on essential Blender tools and features for 3D creation, as well as best practice guidance on designing 3D art between Blender and Studio. > **Info:** To download the latest version of Blender, visit [Blender.org](https://www.blender.org/download/). ## File setup Before you begin creating 3D art on Blender for the Roblox platform, it's important to configure both Blender and Studio settings so that your 3D objects maintain the same position, orientation, and scale as you iterate and move them between the two applications. ### Configure units By default, Blender and Studio use different primary units to measure length: Blender defaults to the metric scale, and Studio defaults to [studs](/docs/en-us/physics/units.md). To ensure that your 3D objects retain the same measurements when you move or scale them in either application, you must configure Blender's units to be consistent with studs. To set Blender units to be compatible with Roblox's stud units: 1. Navigate to the **Properties editor**, then in the left-hand navigation, select the **Scene** tab.![Blender's Properties editor with the Scene tab highlighted.](../assets/art/blender-ui/scene-tab.jpg) 2. Click the **Units** dropdown menu to expand the container, then: 1. Set **Unit System** to **None**. 2. Set **Rotation** to **Degrees**. ### Import settings 3D software and applications use coordinate systems to represent the position and orientation of objects in the 3D space. These coordinate systems typically consist of three axes: - One axis represents horizontal position (left and right movement). - One axis represents vertical position (up and down movement). - One axis represents depth (forward and backward movement). Blender and Studio use **different** coordinate systems, specifically for the axis that represents the "up" direction. Like other modeling software, Blender uses the **Z** axis because 3D objects move up out of the 2D plane to become a 3D object; conversely, like other engines, Studio uses the **Y** axis because characters move on the ground plane and jump up for vertical movement. This difference is important to keep in mind as you import your 3D art into Blender or Studio because you must make adjustments to the import settings to ensure your object maintains the correct orientation in either application. _Blender's Navigation Gizmo_ _Studio's View Selector_ #### Studio to Blender To set Blender import settings for 3D objects from Studio: 1. In the top left-hand corner, click the hamburger menu. A popup menu displays. 2. Navigate to **File** > **Import** > **Wavefront (.obj)**, **FBX (.fbx)**, or **glTF 2.0 (.glb/.gltf)**. The **Blender File View** window displays. 3. Select one or multiple `.obj`, `.fbx`, or `.gltf` files that you want to import. 4. In the right-hand panel, navigate to the **General** section: 1. Set **Scale** to **1** to keep the same scale from Studio. 2. Set **Forward Axis** to **Z** to keep the same "forward" axis as Studio. 3. Set **Up Axis** to **Y** to keep the same "up" axis as Studio.![The Blender File View window with the General section highlighted.](../assets/art/3p-software/blender/BlenderFileView-Import.png) 5. In the bottom right-hand corner, click the **Import** button. #### Blender to Studio To set Studio import settings for 3D objects from Blender: 1. Navigate to **File** > **Import**. Your local file browser displays. 2. Select and then confirm the 3D object's `.obj`, `.fbx`, or `.gltf` file(s) you want to import from your local system. The Importer's **Import Preview** window displays. 3. In the right-hand panel, navigate to the **File General** section, then: 1. Enable **Import Only as a Model** if you have multiple objects that you want to group into a `Class.Model` object. 2. Enable **Upload to Roblox** if you want to create an asset with an asset ID that you can reference across projects. 3. Set **Creator** to **Me** if you are the only one who needs to access the object, or to the group that owns the project you're working on. This latter setting ensures all eligible group members have permission to use the 3D object within the project. 4. Enable **Insert Using Scene Position** so that the object retains the position you set in Blender.![The Import Preview window with the File General section highlighted.](../assets/art/3p-software/blender/Studio-Import.png) 4. Navigate to the **File Transform** section, then set the following settings so that the object retains the same orientation from Blender: 1. Set **World Forward** to **Front** to keep the same "forward" axis as Blender. 2. Set **World Up** to **Top** to keep the same "up" axis as Blender. 5. Navigate to the **File Geometry** section, then set **Scale Unit** to **Stud** to keep the same scale from Blender. 6. At the bottom of the window, click the **Import** button. Your 3D object imports with the same scale and orientation from Blender. ### Export settings Similar to the previous file setup section, it's important to consider Blender and Studio's different coordinate systems when you are ready to export your 3D art from Blender. By taking a little extra time in configuring your export settings, you can ensure your 3D objects maintain the correct orientation, scale, and position when you import them into Studio. > **Warning:** Avatar items, or other assets using specialized components, require unique export settings. See the following links for additional information: - For rigid accessories, see [accessory specifications](/docs/en-us/avatar/rigid-accessories/specifications.md) and [accessory export settings](/docs/en-us/avatar/rigid-accessories/export.md). - For layered accessories, see [layered accessory specifications](/docs/en-us/avatar/layered-accessories.md) and [layered export settings](/docs/en-us/avatar/layered-accessories/export.md). - For avatar characters, see [avatar specifications](/docs/en-us/avatar/character-bodies/specifications.md) and [avatar export settings](/docs/en-us/avatar/character-bodies/export.md). To set Blender settings for exporting 3D objects for Studio: 1. In the top left-hand corner, click the hamburger menu. A popup menu displays. 2. Navigate to **File** > **Export** > **Wavefront (.obj)**, **FBX (.fbx)**, or **glTF 2.0 (.glb/.gltf)**. The **Blender File View** window displays. 3. In the right-hand panel, navigate to the **Include** section, then enable **Limit to Selected Objects** to only export your selected objects.![The Blender File View window with the General section highlighted.](../assets/art/3p-software/blender/BlenderFileView-Export.png) 4. In the **Transform** section 1. If exporting `.fbx`, set **Apply Scalings** to **FBX Unit Scale** so that your object(s) keep the same scale in Studio. For more scaling information, see [Adjust scale](#adjust-scale-fbx). 2. Set **Forward** to **Z Forward** to keep the same "forward" axis as Blender. 3. Set **Up** to **Y Up** to keep the same "up" axis as Blender. 5. In the bottom right-hand corner, click the **Export** button. Your 3D object is now ready to [import into Studio](#blender-to-studio). ### Adjust scale (FBX) The FBX (`.fbx`) file format is widely used in 3D workflows due to its broad support across modeling tools and engines. However, when exporting from Blender using its default `.fbx` settings, models often import at an unexpectedly large scale in Roblox Studio. > **Info:** Roblox Studio also supports GLTF (`.gltf`) formats which do not require these additional scaling configurations. ![Two default Blender cubes imported in Studio with different scales.](../assets/modeling/meshes/Blender-Scale-Examples.png) If you rely on `.fbx` in your workflow, proper export settings or project settings are essential to ensure that your models appear at the correct size and maintain their intended proportions across platforms. There are many ways to configure your model so your `.fbx` model scales as expected in Studio. See the following popular settings to manage scaling: | Blender setting | UI | Description | | --- | --- | --- | | During export, set **Transform** > **Apply Scaling** to **FBX Unit Scale**. | ![Two default Blender cubes imported in Studio with different scales.](../assets/modeling/skinned-meshes/Blender-Export-Settings-5.png) | Models exported with this setting import into Studio or back into Blender at the same scale.

Make sure all other scaling values in project or export settings are set to default (`1.0`).

This is the recommended way to export `.fbx` files at the same scale. | | During export, set **Transform** > **Scale** to **.01**. | ![Two default Blender cubes imported in Studio with different scales.](../assets/modeling/skinned-meshes/Blender-Export-Settings-2.png) | This scales down your export so that the model imports into Studio at the expected scale.

If you intend on importing this model back in Blender or any non-Studio tool, you might need to scale up the model on import, otherwise the model will import unexpectedly small. | | In the **Scene Properties** panel, set **Units** > **Unit Scale** to .**01**. | ![Two default Blender cubes imported in Studio with different scales.](../assets/modeling/skinned-meshes/Blender-Scene-Units-Settings.png) | This scales down your entire scene so that the model imports into Studio at the expected scale. If you adjusted your scene units, you can export models using default settings into Studio.

Working on models at `.01` scale can cause unexpected Blender issues, such as camera difficulties, issues with modifiers, or other complications.

If you intend on importing this model back in Blender, you might need to scale up the model on import, otherwise the model will import unexpectedly small. | > **Warning:** **Note on existing resources** Many reference files, demo projects, and downloadable assets may use older or inconsistent scale settings. Likewise, tutorials and guides might recommend different approaches for handling scale between Blender and Studio. Always test and validate any external resources to ensure they work correctly with your current export workflow. ## Fundamentals Before you take a look at all of the common modeling, sculpting, and texturing tools for making 3D art for Studio, let's review Blender's fundamental interface elements that are important for navigating through the application and finding the appropriate menus and controls for your specific 3D creation task. ### Workspaces ![Blender's UI with workspaces highlighted.](../assets/art/blender-ui/Workspaces.png) **Workspaces** are preset window layouts with specialized UI configurations and tooling for different 3D creation work like modeling, sculpting, or texturing. You can use these workspace configurations as-is, or you can customize them to work for you as you quickly swap between different tasks. > **Info:** For information on additional workspaces, see Blender's official [Workspaces](https://docs.blender.org/manual/en/2.81/interface/window_system/workspaces.html) documentation. There are many default workspaces, but the following are the most common for creating 3D art for the Roblox platform. #### Layout ![Blender's Topbar with the Layout workspace highlighted.](../assets/art/3p-software/blender/Layout_Workspace.png) The **Layout** workspace is the default workspace when you load a Blender file, and it provides basic tools for previewing and transforming your 3D objects, such as the Move, Scale, and Rotate tools. The default layout of this workspace includes the following UI for easy access as you set up your 3D art: - [3D Viewport](https://docs.blender.org/manual/en/latest/editors/3dview/introduction.html) - Displays the entire scene. - [Outliner](https://docs.blender.org/manual/en/latest/editors/outliner/introduction.html) - Displays all objects in the scene, comparable to Studio's **Explorer** window. - [Properties Editor](https://docs.blender.org/manual/en/latest/editors/properties_editor.html) - Displays editable data for the active object, comparable to Studio's **Properties** window. - [Timeline Editor](https://docs.blender.org/manual/en/latest/editors/timeline.html) - Displays all animation keyframes, comparable to the **Animation Editor** timeline. #### Modeling ![Blender's Topbar with the Modeling workspace highlighted.](../assets/art/3p-software/blender/Modeling_Workspace.png) The **Modeling** workspace is very similar to the **Layout** workspace, but it offers more space for the 3D viewport so that you can focus on modifying the geometry of your 3D objects. If you swap between the Modeling and Layout workspaces, you can see the **Properties Editor** expand, the **Outliner** shrink in size, and the **Timeline Editor** disappear altogether. The default layout of this workspace includes the following UI for modeling your 3D art: - [3D Viewport](https://docs.blender.org/manual/en/latest/editors/3dview/introduction.html) - Displays the entire scene. - [Outliner](https://docs.blender.org/manual/en/latest/editors/outliner/introduction.html) - Displays all objects in the scene, comparable to Studio's **Explorer** window. - [Properties Editor](https://docs.blender.org/manual/en/latest/editors/properties_editor.html) - Displays editable data for the active object, comparable to Studio's **Properties** window. The most common Roblox creator use case for this workspace is to create the geometrical shape of the 3D art before texturing or animating the geometry. #### Sculpting ![Blender's Topbar with the Sculpting workspace highlighted.](../assets/art/3p-software/blender/Sculpting_Workspace.png) The **Sculpting** workspace provides sculpting tools for modifying meshes with over 20 unique brush types, such as Clay, Inflate, Smooth, and Flatten. Like other common sculpting software, this workspace also provides tooling to mask specific geometry so you can focus on sculpting specific areas without affecting the rest of the mesh. The default layout of this workspace includes the following UI for sculpting your 3D art: - [3D Viewport](https://docs.blender.org/manual/en/latest/editors/3dview/introduction.html) - Displays the entire scene. - [Outliner](https://docs.blender.org/manual/en/latest/editors/outliner/introduction.html) - Displays all objects in the scene, comparable to Studio's **Explorer** window. - [Properties Editor](https://docs.blender.org/manual/en/latest/editors/properties_editor.html) - Displays editable data for the active object, comparable to Studio's **Properties** window. The most common Roblox creator use case for this workspace is to create organic 3D art like humans, animals, and plants. #### UV Editing ![Blender's Topbar with the UV Editing workspace highlighted.](../assets/art/3p-software/blender/UVEditing_Workspace.png) The **UV Editing** workspace provides tools for UV mapping texture coordinates to 3D surfaces. The most striking visual change in this workspace is the addition of the UV Editor to allow you to view both your texture and your object on the same screen as you unwrap and adjust UVs. The default layout of this workspace includes the following UI for UV mapping your 3D art: - [UV Editor](https://docs.blender.org/manual/en/latest/editors/uv/introduction.html) - Displays the image texture that you're mapping onto your 3D object. - [3D Viewport](https://docs.blender.org/manual/en/latest/editors/3dview/introduction.html) - Displays the entire scene. - [Outliner](https://docs.blender.org/manual/en/latest/editors/outliner/introduction.html) - Displays all objects in the scene, comparable to Studio's **Explorer** window. - [Properties Editor](https://docs.blender.org/manual/en/latest/editors/properties_editor.html) - Displays editable data for the active object, comparable to Studio's **Properties** window. The most common Roblox creator use case for this workspace is to create and use trim sheets that you can apply to multiple 3D objects at once. This allows you to add significantly more visual complexity to your experiences without having to import additional textures, saving you a negative impact on memory. For more information on this process, see [Develop polished assets - Trim sheets](/docs/en-us/tutorials/curriculums/environmental-art/develop-polished-assets.md#trim-sheets) in the Environmental art curriculum. > **Info:** For more information on UV unwrapping, see Blender's official [Unwrapping Introduction](https://docs.blender.org/manual/en/latest/modeling/meshes/uv/unwrapping/introduction.html) documentation. #### Texture Paint ![Blender's Topbar with the Texture Painting workspace highlighted.](../assets/art/3p-software/blender/TexturePaint_Workspace.png) The **Texture Paint** workspace provides tools for coloring image textures onto geometry. Similar to the UV Editing workspace, the most striking visual change in this workspace is the addition of the Image Editor in Paint mode to allow you to view both your texture and your object on the same screen as you paint. The default layout of this workspace includes the following UI for texture painting your 3D art: - [Image Editor](https://docs.blender.org/manual/en/latest/editors/image/introduction.html) - Displays tools to create, view, and edit images that you can apply to the active object. - [3D Viewport](https://docs.blender.org/manual/en/latest/editors/3dview/introduction.html) - Displays the entire scene. - [Outliner](https://docs.blender.org/manual/en/latest/editors/outliner/introduction.html) - Displays all objects in the scene, comparable to Studio's **Explorer** window. - [Properties Editor](https://docs.blender.org/manual/en/latest/editors/properties_editor.html) - Displays editable data for the active object, comparable to Studio's **Properties** window. The most common Roblox creator use case for this workspace is to create a unique texture for characters, accessories, or important 3D objects that players regualarly interact with in experiences. > **Info:** For more information on texture painting, see Blender's official [Texture Paint Introduction](https://docs.blender.org/manual/en/latest/sculpt_paint/texture_paint/introduction.html) documentation. ### 3D Viewport ![Blender's UI with the 3D Viewport highlighted.](../assets/art/blender-ui/3DViewport.png) Comparable to Studio's viewport, the **3D Viewport** lets you view and interact with your 3D objects as they exist in the 3D space. You can navigate through the scene, transform objects with your mouse, and see your changes in real time as you design your 3D art. > **Info:** For more information, see Blender's official [3D Viewport](https://docs.blender.org/manual/en/latest/editors/3dview/introduction.html) documentation. ### Modes ![Blender's UI with the Modes selector highlighted.](../assets/art/blender-ui/Modes.png) **Modes** offer additional tooling for editing 3D objects in the 3D Viewport. When you select a new mode from the Modes selector: - The Header displays new menu options. - The Toolbar displays a new set of tools. - Editors and their buttons and panels enable or disable appropriately. Depending on which mode is active, your cursor can change into a brush, such as in paint or sculpt modes, and the 3D Viewport can change how it displays objects for that particular task, such as darkening an object so you can more easily see your paint strokes. As you learn Blender, it's useful to experiment with different modes to see what tools are available for your particular 3D creation task. > **Info:** For more information on modes, see Blender's official [Object Modes](https://docs.blender.org/manual/en/latest/editors/3dview/modes.html) documentation. #### Object ![Blender's Modes selector with the Object Mode menu item highlighted.](../assets/art/blender-ui/Object-Mode.jpg) **Object mode** is the default mode, and it provides tooling that's available for all object types, such as positioning vertices, edges, and faces, rotating and scaling objects, and measuring distance and angles. This mode is useful for high-level object transformations. #### Edit ![Blender's Modes selector with the Edit Mode menu item highlighted.](../assets/art/blender-ui/Edit-Mode.jpg) **Edit mode** provides tooling for editing an object's shape. This mode is useful for more detailed object transformations, such as insetting faces, extruding regions, creating loop cuts, and beveling. #### Sculpt ![Blender's Modes selector with the Sculpt Mode menu item highlighted.](../assets/art/blender-ui/Sculpt-Mode.jpg) **Sculpt mode** provides tooling for editing a mesh's shape. This mode is useful for creating more organic 3D art, such as humans, animals, and plants. #### Vertex Paint ![Blender's Modes selector with the Vertex Paint Mode menu item highlighted.](../assets/art/blender-ui/VertexPaint-Mode.jpg) **Vertex Paint** mode provides tooling to set a mesh's vertices to specific colors. This mode is useful for when you want to paint the object itself instead of applying a texture onto the geometry. #### Weight Paint ![Blender's Object Mode dropdown with the Weight Paint Mode menu item highlighted.](../assets/art/blender-ui/WeightPaint-Mode.jpg) **Weight Paint** mode provides tooling for vertex group weight painting. This mode is useful when you're creating avatars because it allows you to specify which parts of their body are influenced by parts of their armature. #### Texture Paint ![Blender's Object Mode dropdown with the Texture Paint Mode menu item highlighted.](../assets/art/blender-ui/TexturePaint-Mode.jpg) **Texture Paint** mode provides tooling to paint a texture directly on a 3D object. This mode is useful for when you want to apply a texture onto the geometry instead of painting the object's vertices. ### Toolbar ![Blender's UI with the Toolbar highlighted.](../assets/art/blender-ui/Toolbar.png) The **Toolbar** is a vertical menu of tools on the left-hand side of the 3D Viewport. Each time you switch modes, the Toolbar responds by displaying a new unique set of tools for that particular mode. > **Info:** For more information on the Toolbar, see Blender's official [Toolbar](https://docs.blender.org/manual/en/latest/editors/3dview/toolbar/index.html) documentation. ### 3D Cursor ![Blender's UI with the 3D Cursor highlighted.](../assets/art/blender-ui/3DCursor.png) The **3D Cursor** is a movable reference point in the 3D space that has both location and rotation data. While this tool has many different uses, the most common are using its position and orientation to: - Create precise transformations. - Place new objects into the scene. - Move objects or their vertices to new points in the 3D space. - Reposition pivot point locations. > **Info:** For more information on the 3D Cursor, see Blender's official [3D Cursor](https://docs.blender.org/manual/en/latest/editors/3dview/3d_cursor.html) documentation. ## Modeling tools Now that you know how to navigate the user interface and change tools according to your specific 3D creation task, let's take a closer look at the most common modeling tools that allow you to change the shape of 3D objects by either impacting the entire mesh or one of the three basic elements of meshes: - **Vertex** - A single point on the mesh. - **Edge** - A line that connects two vertices. - **Face** - A surface area between three or more vertices. Each of the following sections details how you can use each tool for objects and/or mesh elements, the hotkeys you can use to activate the tool, and their most common use cases for creating 3D art for the Roblox platform. ![A single active vertex on a cube mesh.](../assets/art/3p-software/blender/Vertex.png)_Vertex_ ![A single active edge on a cube mesh.](../assets/art/3p-software/blender/Edge.png)_Edge_ ![A single active face on a cube mesh.](../assets/art/3p-software/blender/Face.png)_Face_ > **Info:** For more information on any of these mesh elements, see Blender's official [Mesh Structure](https://docs.blender.org/manual/en/latest/modeling/meshes/structure.html) documentation. ### Grab The **Grab** tool lets you move objects, vertices, edges, and faces from the 3D space, and it's one of the most essential tools for positioning objects or mesh elements in a scene. Many Roblox creators use this tool for editing purposes, such as positioning vertices, edges, and faces to a particular stud unit in the 3D space. To use the Grab tool: 1. In either **Object** or **Edit** mode, select one or multiple objects, vertices, edges, or faces. 2. Press `G` to activate the tool. 3. Move the mouse to reposition your selection. For further precision: - Press `X`, `Y`, or `Z` after you press `G` to constrain movement to the **X**, **Y**, or **Z** axis, respectively. - Double-press an axis key to slide vertices or edges along their natural path. - Hold `Shift` while moving your mouse to slow down movement for fine adjustments. 4. Left-click or press `Enter` to confirm the new position. > **Info:** For more information on this tool, see Blender's official [Move](https://docs.blender.org/manual/en/latest/scene_layout/object/editing/transform/move.html) documentation. ### Snap The **Snap** tool lets you align objects and mesh elements by snapping them to other objects, mesh elements, or the 3D space's grid. Many Roblox creators use this tool to precisely position multiple objects together in the scene so that they can evaluate how they work together in an environment, particularly in regard to position, orientation, and scale. To use the Snap tool: 1. In **Object** or **Edit** mode, navigate to the header, then click the **Snapping** button. A contextual menu displays. ![Blender's header with the Snapping button highlighted.](../assets/art/3p-software/blender/Snapping.png) 1. In the contextual menu, 1. Set **Snap Base** to one of the following: - **Closest** - Snaps using the vertex that's closest to the target. - **Center** - Snaps using the pivot point. - **Median** - Snaps using the median of the selection. - **Active** - In Object mode, this setting snaps using the origin of the active element; in Edit mode, this setting snaps using the center of the active element. 2. Set **Snap Target** to one of the following: - **Increment** - Snaps to grid points from the selection's location. - **Grid** - Snaps to the grid in the 3D viewport. - **Vertex** - Snaps to the vertex that's closest to the mouse cursor. - **Edge** - Snaps to the edge that's closest to the mouse cursor. - **Face** - Snaps to the face that's closest to the mouse cursor. - **Volume** - Snaps the selection to a depth that's centered inside the object under the cursor. - **Edge Center** - Snaps to the centerpoint of the edge that's closest to the mouse cursor. - **Edge Perpendicular** - Snaps to a specific point on the edge so that the line from the selection's original location to its new location is perpendicular to that edge. 3. Set **Affect** to one of the following: - **Move** - Snaps while moving the selection. - **Rotate** - Snaps while rotating the selection. - **Scale** - Snaps while scaling the selection. 2. Press `Shift``Tab` to activate the tool. 3. For further precision, hold `Shift` to snap the selection in finer increments. 4. Move, rotate, or scale an object or mesh element according to your settings. > **Info:** For more information on this tool, see Blender's official [Snapping](https://docs.blender.org/manual/en/latest/editors/3dview/controls/snapping.html) documentation. ### Inset The **Inset** tool lets you create an inset with adjustable thickness and depth from a face or group of faces. Many Roblox creators use this tool to create uniform fine details in their meshes while maintaining a clean edge flow for their topology. To use the Inset tool: 1. In **Edit** mode, select one or multiple faces. 2. Press `I` to activate the tool. 3. Move the mouse to adjust your inset's size. For further precision: - Hold `Ctrl` to adjust the depth of the inset. - Hold `Shift` while moving your mouse to slow down movement for fine adjustments. - Press `I` again to inset each active face. 4. Left-click or press `Enter` to confirm your inset(s). > **Info:** For more information on this tool, see Blender's official [Inset Faces](https://docs.blender.org/manual/en/latest/modeling/meshes/editing/face/inset_faces.html) documentation. ### Extrude The **Extrude** tool lets you create new geometry by pulling out new faces, edges, or vertices from existing geometry. Many Roblox creators use this tool to create depth, volume, and complex shapes from Blender's primitive meshes. To use the Extrude tool: 1. In **Edit** mode, select one or multiple vertices, edges, or faces. 2. Press `E` to activate the tool. 3. Move the mouse to adjust your extrusion's length. For further precision, press `X`, `Y`, or `Z` after you press `E` to constrain movement to the **X**, **Y**, or **Z** axis, respectively. 4. Left-click or press `Enter` to confirm your extrusion(s). > **Info:** For more information on this tool, see the following official Blender documentation: - [Extrude](https://docs.blender.org/manual/en/latest/modeling/meshes/editing/mesh/extrude.html) - [Extrude Vertices](https://docs.blender.org/manual/en/latest/modeling/meshes/editing/vertex/extrude_vertices.html) - [Extrude Edges](https://docs.blender.org/manual/en/latest/modeling/meshes/editing/edge/extrude_edges.html) - [Extrude Faces](https://docs.blender.org/manual/en/latest/modeling/meshes/editing/face/extrude_faces.html) ### Subdivide The **Subdivide** tool lets you cut edges or faces into smaller divisions, a process that adds new vertices and resolution to your meshes. Many Roblox creators use this tool to create smooth curves, add fine details to surfaces, and prepare meshes before applying additional modifiers. To use the Subdivide tool: 1. In **Edit** mode, select one or edges or faces. 2. Right-click to display a contextual menu for your active edges or faces, then select **Subdivide** to activate the tool. The Subdivide panel displays. 3. Set **Number of Cuts** to the number of subdivisions you want for your edges or faces.![The Subdivide panel with the Number of Cuts setting highlighted.](../assets/art/3p-software/blender/Subdivide-Panel.png) 4. Left-click to confirm your subdivision(s). > **Info:** For more information on this tool, see Blender's official [Subdivide](https://docs.blender.org/manual/en/latest/modeling/meshes/editing/edge/subdivide.html) documentation. ### Bridge Edge Loops The **Bridge Edge Loops** tool lets you connect multiple edge loops with faces. Many creators use this tool to fill gaps without manually creating new faces, merge complex sections of their meshes, and maintain clean topology for smooth deformations. To use the Bridge Edge Loops tool: 1. In **Edit** mode, select two or more edge loops that you want to connect. 2. Press `Ctrl``E`/`⌘``E` to display a contextual menu for your active edge loops, then select **Bridge Edge Loops** to activate the tool.The **Bridge Edge Loops** panel displays. 3. Set **Number of Cuts** to the number of subdivisions you want for your new bridge. 4. **(Optional)** For further precision for curved bridges, increase **Smoothness** to create a more rounded bridge. 5. Left-click to confirm your bridge. > **Info:** For more information on this tool, see Blender's official [Bridge Edge Loops](https://docs.blender.org/manual/en/latest/modeling/meshes/editing/edge/bridge_edge_loops.html) documentation. ### Fill The **Fill** tool lets you create triangular faces between any active edges or vertices, as long as they form one or more complete perimeters. Many Roblox creators use this tool to close gaps in their meshes so that they're watertight, or without exposed holes. To use the Fill tool: 1. In **Edit** mode, select at least three vertices or two or more edges that form at least one complete perimeter. 2. Press `Alt``F`/`⌥``F` to activate the tool. 3. **(Optional)** In the **Fill** panel, enable **Beauty** to arrange the triangles nicely. 4. Left-click to confirm your new face. > **Info:** For more information on this tool, see Blender's official [Fill](https://docs.blender.org/manual/en/latest/modeling/meshes/editing/face/fill.html) documentation. ### New Faces from Edges The **New Faces from Edges** tool lets you either create an edge if only two vertices are active, otherwise it creates a face between the active mesh elements. Many Roblox creators use this tool to close many gaps at once in their meshes so that the meshes are watertight, or to create geometry between many solitary vertices. To use the New Faces from Edges tool: 1. In **Edit** mode, select at least three vertices, or two or more edges that form one or more complete perimeters. 2. Press `F` to activate the tool. 3. Left-click to confirm your new face. > **Info:** For more information on this tool, see Blender's official [New Edge/Face from Vertices](https://docs.blender.org/manual/en/latest/modeling/meshes/editing/vertex/make_face_edge.html) documentation. ### Dissolve The **Dissolve** tool lets you remove geometry without leaving holes in your meshes. Many Roblox creators use this tool to optimize their 3D art, merge geometry, or remove unnecessary mesh elements while retaining the overall structure of the mesh. To use the Dissolve tool: 1. In **Edit** mode, select the vertices, edges, or faces that you want to remove from your mesh. 2. Right-click to display a contextual menu for your active mesh elements, then: 1. If your selection is made up of vertices, select **Dissolve Vertices** to remove the active vertices and merge their neighboring edges. 2. If your selection is made up of edges, select **Dissolve Edges** to remove the active edges and join the surrounding faces to maintain the edge's outline. 3. If your selection is made up of faces, select **Dissolve Faces** to remove the active face(s) and fill the gap with a new face, if necessary. > **Info:** For more information on this tool, see Blender's official [Deleting & Dissolving](https://docs.blender.org/manual/en/latest/modeling/meshes/editing/mesh/delete.html#dissolve) documentation. ### Delete The **Delete** tool lets you completely remove geometry from your meshes, leaving one or many holes wherever geometry was removed. Many Roblox creators use this tool instead of the Dissolve tool whenever they want to restructure their meshes during the iteration process. To use the Delete tool: 1. In **Edit** mode, select the vertices, edges, or faces that you want to remove from your mesh. 2. Press `X` to display a contextual menu for your selection. 3. Choose one of the following menu items: 1. Select **Vertices** to delete all active vertices, removing any faces or edges they are connected to. 2. Select **Edges** to delete all active edges, removing any faces that the edge shares with it. 3. Select **Faces** to remove all active faces, removing any edges they are connected to. 4. Select **Only Edges and Faces** to remove only the active edges and adjacent faces. 5. Select **Only Faces** to remove all active faces without also affecting active edges in the selection. > **Info:** For more information on this tool, see Blender's official [Deleting & Dissolving](https://docs.blender.org/manual/en/latest/modeling/meshes/editing/mesh/delete.html#delete) documentation. ### Mirror Modifier The **Mirror modifier** lets you mirror geometry across one or multiple axes so that you can create symmetrical 3D art with minimal effort. Many Roblox creators use this tool while modeling avatars or architectural objects that are important to be perfectly symmetrical. To use the Mirror modifier: 1. In **Object** mode, select the object that you want to mirror. 2. Navigate to the **Properties editor**, then in the lefthand navigation, select the **Modifiers** tab.![Blender's Properties editor with the Modifiers tab highlighted.](../assets/art/blender-ui/Modifiers-Tab.png) 3. Click the **Add Modifier** button, then insert the **Mirror** modifier. 4. Set **Axis** to **X**, **Y**, and/or **Z** to mirror along one or many axes. 5. In **Edit** mode, modify your object or any of its mesh elements to see your modifications mirror along an axis or multiple axes. ## Texturing tools After you finish modeling your 3D object, it's time to apply a texture to your mesh so that it has additional visual characteristics, such as color, depth, and roughness through a [texture map](/docs/en-us/art/modeling/surface-appearance.md). These are details that modeling alone can't provide, transforming your object from a blank shape to something that looks either realistic or stylized according to your experience's art requirements. _Model without a texture_ _Model with a texture_ Each of the following sections details how you can use each tool for either UV editing or vertex painting objects, the hotkeys you can use to activate the tool, and their most common use cases for texturing 3D art for the Roblox platform. ### UV editing UV editing is the process of unwrapping your 3D object's faces and mapping them onto a 2D image texture. This process allows you to use a single texture to apply visual characteristics to multiple objects in Blender, saving you a negative impact on memory in Studio. For example, the following door frame, ceiling, and card reader meshes in the [Laser Tag](/docs/en-us/resources/templates.md#laser-tag) template all use the same UV map to add metal adornments. ![An doorway with trim sheet textures applied.](../assets/tutorials/environmental-art-curriculum/Section2/TrimSheets-Doorway.jpg) ![A group of ceiling tiles with trim sheet textures applied.](../assets/tutorials/environmental-art-curriculum/Section2/TrimSheets-Ceiling.jpg) ![A futuristic card reader with trim sheet textures applied.](../assets/tutorials/environmental-art-curriculum/Section2/TrimSheets-Reader.jpg) The "UV" in UV editing stands for the axes on the 2D image map that you use during the mapping process: - U axis - The horizontal position (left and right movement). - V axis - The vertical position (up and down movement). Because 3D objects use the X, Y, and Z axes in the 3D space, 3D creation applications typically use U and V to avoid confusion when referring to coordinates in 2D image space. That being said, many Blender UI workflows still use X and Y, so it's helpful to know the 2D space equivalent axis. When you unwrap a 3D object, all active faces flatten into the 2D space in the UV Editor to make up the **UV map**, and different sections of the model split into separate groupings, commonly referred to as **UV islands**. For example, if you were to unwrap a standard Blender cube, each face would become its own UV island in the UV map. ![A UV map of a 3D cube.](../assets/art/3p-software/blender/CubeMap.png)_UV map in the UV Editor_ ![The 3D cube that is being unwrapped.](../assets/art/3p-software/blender/UV-Cube.png)_Cube in the 3D Viewport_ Every point, line, and face in the UV map corresponds to a vertex, edge, and face in the mesh. This means that when you move UV islands to different parts of the texture, their corresponding vertices, edges, and faces update to reflect the area of the texture that they now overlap. Blender often creates UV islands along **seams**, or connected edges, to minimize distortion and make it easier for you to apply your texture. While this default UV island configuration is a great place to start, it's almost always necessary to modify each island's position, orientation, and scale to overlap the area of the texture you want to project, or mark your own seams to focus on texturing specific areas at a time. By investing the time to unwrap, arrange, and map your UV islands, you can improve your texture quality and reduce stretching or distortion on all of your 3D art. The following subsections highlight the most common tools for this process, specifically in regards to unwrapping and mapping your objects. > **Info:** For more high-level information on this process, see Blender's official [UV Editor](https://docs.blender.org/manual/en/latest/editors/uv/introduction.html) and [Getting Started with UVs](https://docs.blender.org/manual/en/latest/modeling/meshes/uv/unwrapping/introduction.html#getting-started) documentation. #### Mark Seam The **Mark Seam** tool lets you manually break up a 3D object's UV map into smaller, manageable sections. While this step in the unwrapping process is technically optional, many Roblox creators find it useful for concentrating on collections of related faces for complex objects that require a lot of detailed texture work. For example, the following rivet gun requires a metal texture for the barrel, and a leather texture for the grip. To make it easier to texture this object, you can mark the seams of the grip, unwrap just the faces between those seams, then map it to a leather texture image. ![A UV map of a grip on a rivet gun.](../assets/art/3p-software/blender/MS-UVMap-Gun.png)_Grip UV map in the UV Editor_ ![The marked seams and enclosed faces on the rivet gun that are being unwrapped.](../assets/art/3p-software/blender/MS-Seams-Gun.png)_Marked seams in the 3D Viewport_ To use the Mark Seam tool: 1. Open the **UV Editing** workspace. The UV Editor displays on the lefthand side of the screen, and the 3D viewport displays on the righthand side of the screen in Edit mode. 2. In the **3D Viewport**, navigate to your 3D object, then select the edges where you want to create seams. 3. Press `Ctrl``E`/`⌘``E` to open the **Edge** contextual menu. 4. Select **Mark Seam**. The seam turns red and is ready for the Unwrap tool. When you are ready to unwrap your object, Blender will unwrap the enclosed faces of the seam as its own island. > **Info:** For more information on this tool, see Blender's official [Seams](https://docs.blender.org/manual/en/latest/modeling/meshes/uv/unwrapping/seams.html) documentation. #### Unwrap The **Unwrap** tool lets you unwrap and clean up the UV map of any marked seams or active faces of a 3D object so that you have a solid foundation of UV islands to work with. This can quickly take UV coordinates from being a complete mess to something more manageable. Many Roblox creators use this tool for complex shapes that they need full control over when applying textures, such as characters, clothes, and curvy objects, because it works quickly and avoids stretching and distortion. ![A chaotic with disorganized islands.](../assets/art/3p-software/blender/Before-Unwrap.png)_Before using the Unwrap tool_ ![A clean UV map with organized islands.](../assets/art/3p-software/blender/After-Unwrap.png)_After using the Unwrap tool_ To use the Unwrap tool: 1. Open the **UV Editing** workspace. The UV Editor displays on the lefthand side of the screen, and the 3D viewport displays on the righthand side of the screen in Edit mode. 2. In the **3D Viewport**, navigate to your 3D object, then either use the **Mark Seam** tool to create seams, and/or select the specific faces you want to texture. 3. Press `U` to open the **UV Mapping** contextual menu. 4. Select **Unwrap**. Your selection's UV islands display neatly in the UV Editor. 5. In the **UV Editor**, move, scale, or rotate UV islands to the appropriate position, orientation, and scale on your texture. > **Info:** For more information on this tool, see Blender's official [UV Operators - Unwrap](https://docs.blender.org/manual/en/latest/modeling/meshes/editing/uv.html#Unwrap) documentation. #### Follow Active Quads The **Follow Active Quads** tool lets you generate clean and organized UV islands according to the UV coordinates of a previously unwrapped active face. This is particularly useful for grid-like topology, such as when you're unwrapping walls, floors, or mechanical parts using trim sheets. Many Roblox creators use this tool to apply a consistent texture to curved 3D objects, such as sidewalks for experiences or belts for accessories. To use the Follow Active Quads tool: 1. Open the **UV Editing** workspace. The UV Editor displays on the lefthand side of the screen, and the 3D viewport displays on the righthand side of the screen in Edit mode. 2. In the **3D Viewport**, navigate to your 3D object, then unwrap every face you want to texture using an active face. 3. **(Optional)** Configure your active face. 1. In the **UV Editor**, select the face you want to be your active face, then press `Alt``M`/`⌥``M` to open the **Split** contextual menu. 2. Select **Selection**. The active face separates from the UV island. 3. Move, scale, or rotate your active face to the appropriate position, orientation, and scale that you want other faces in the UV map to follow. 4. In the **UV Editor**, select all faces you want to follow the active face. Make sure to select your active face last so that Blender knows to use its layout as the guide. 5. Press `U` to open the **Unwrap** contextual menu, then select **Follow Active Quads**. Blender aligns your selection's UV coordinates to match the active face's shape and orientation. > **Info:** For more information on this tool, see Blender's official [UV Operators - Follow Active Quads](https://docs.blender.org/manual/en/latest/modeling/meshes/editing/uv.html#follow-active-quads) documentation. #### Projection Projection tools are tools that project a 3D object's surface onto the 2D image texture plane. Each projection tool controls how the object's shape unwraps and maps onto the 2D texture: - **Cube Projection** - Projects the object's faces onto all six sides of a cube; useful for boxy shapes like crates. - **Sphere Projection** - Projects the object's surface onto a sphere; useful for round shapes like eyes. - **Cylinder Projection** - Projects the object onto a cylindrical shape; useful for tubes, pipes, and limbs. - **Project from View** - Projects the object's selected faces according to the current camera or viewport angle; useful for flat surfaces and decals. - **Smart UV Project** - Automatically unwraps the model and generates islands according to a set angle between faces. It's useful to consider how you can utilize projection tools to save time in the texturing process, especially for simple objects that require repetitive textures. For example, many Roblox creators strategically use these tools to efficiently create UV islands for basic geometric shapes without needing to mark seams from edges. ![A UV map using Smart UV Project.](../assets/art/3p-software/blender/SmartUVProject.png)_A UV map using Smart UV Project._ ![A UV map using Cube Projection.](../assets/art/3p-software/blender/CubeProjection.png)_A UV map using Cube Projection._ To use a projection tool: 1. Open the **UV Editing** workspace. The UV Editor displays on the lefthand side of the screen, and the 3D viewport displays on the righthand side of the screen in Edit mode. 2. In the **3D Viewport**, navigate to your 3D object, then select every face. 3. Press `U` to open the **UV Mapping** contextual menu, then select one of the following options: - **Cube Projection** - **Sphere Projection** - **Cylinder Projection** - **Project from View** - **Smart UV Project** > **Info:** For more information, see the following official Blender documentation: - [Cube Projection](https://docs.blender.org/manual/en/latest/modeling/meshes/editing/uv.html#cube-projection) - [Sphere Projection](https://docs.blender.org/manual/en/latest/modeling/meshes/editing/uv.html#sphere-projection) - [Cylinder Projection](https://docs.blender.org/manual/en/latest/modeling/meshes/editing/uv.html#cylinder-projection) - [Project from View](https://docs.blender.org/manual/en/latest/modeling/meshes/editing/uv.html#project-from-view) - [Smart UV Project](https://docs.blender.org/manual/en/latest/modeling/meshes/editing/uv.html#smart-uv-project) ### Vertex painting Vertex painting is the process of storing color information directly on the vertices of your 3D object rather than through traditional textures or materials. In this approach, each vertex holds color data that Blender interpolates across the faces of a mesh, creating either smooth gradients or solid blocks of color without the need for UV mapping or image textures. Vertex painting is useful for adding color variation to your assets in a lightweight, efficient way, especially in stylized workflows or experiences where performance is a concern, as it can reduce texture memory usage and draw calls. Many Roblox creators use vertex painting in conjunction with Studio's default materials, `Class.MaterialVariant` objects, and `Class.SurfaceAppearance` objects to create visual complexity on otherwise simple 3D objects. ![A rocket launcher object in Blender without any color.](../assets/art/3p-software/blender/VP-NoColor.png)_Object without color_ ![A rocket launcher object in Blender with vertex paint on the handle.](../assets/art/3p-software/blender/VP-WithColor.png)_Object with vertex paint_ ![A rocket launcher object in Studio with vertex paint and a SurfaceAppearance texture](../assets/art/3p-software/blender/VP-WithTexture.png)_Object with vertex paint and image texture_ To vertex paint: 1. Add a color attribute to store color information to your object's vertices. 1. In the **3D Viewport**, select your 3D object. 2. Navigate to the **Properties editor**, then in the left-hand navigation, select the **Data** tab.![Blender's Properties editor with the Data tab highlighted.](../assets/art/blender-ui/Data-Tab.png) 3. Click the **Color Attributes** dropdown menu to expand the container, then click the **+** button. The **Add Color Attribute** contextual menu displays. 4. Set **Domain** to **Vertex**, **Data Type** to **Color**, then click the **Add** button. Your object now has a color attribute. 2. Configure the 3D Viewport to display your color attribute. 1. In the **3D Viewport**, navigate to the top right-hand corner **Viewport Shading** options, then click the button for the **Solid** shading mode.![Blender's Viewport Shading options with the Solid shading mode highlighted.](../assets/art/blender-ui/ViewportShading-Solid.png) 2. Click the **Viewport Shading** dropdown arrow, then in the contextual menu, set **Color** to **Attribute**. The 3D Viewport updates to display your color attribute on your object.![Blender's Viewport Shading dropdown menu with the dropdown arrow and Color settings highlighted.](../assets/art/3p-software/blender/ColorAttribute.png) 3. Select a color for your brush. 1. In **Vertex Paint** mode, navigate to the top left-hand corner, then click on the active color swatch. A contextual menu displays. 2. Select your color on the color wheel, or with a RGB, HSV, or Hex code.![Blender's active swatch UI highlighted.](../assets/art/blender-ui/ActiveSwatch.png) 4. Apply color to your object. 1. To apply color to individual vertices: 1. In the **3D Viewport**, click and drag over your object's vertices to apply your color with a smooth gradient. 2. For further precision, press `[` or `]` to increase or decrease the brush size, respectively. 2. To flood fill your color, 1. In **Edit** mode, select the vertices or faces that you want to apply your color. 2. In **Vertex Paint** mode, press `Ctrl``X`/`⌘``X`. Your selected vertices or faces display the new color. > **Info:** For more information on this process, see Blender's official [Vertex Paint](https://docs.blender.org/manual/en/latest/sculpt_paint/vertex_paint/index.html) documentation.
--- title: "Blender configuration" url: /docs/en-us/art/characters/creating/blender-configurations last_updated: 2026-06-29T19:33:53Z description: "Roblox avatar template projects have several helper configurations and settings to help expedite the character creation process." --- # Blender configuration The Blender project files of each template include additional helper configurations that are not included in the raw `.fbx` template files. If you intend on using Blender for your character creation process, it's important to understand the additions included in the `.blend` versions of the template and what they do. ### Hierarchy The following is a breakdown of what is included in each Blender template file: - **Cage**: Contains the character's 15 outer body cage meshes. - **Joints**: The parent armature object that includes the following objects: - The **bones** that make up the character's rigging armature. - The **_Geo** mesh objects that make up the avatar appearance. - The **_Att** attachment objects that define where accessories attach when equipped. - The **animation data** for saving facial animation data and poses. - **Fill** and **Sun**: These objects are used to provide lighting, allowing you to preview your characters in a neutral lighting similar to a Studio baseplate. These are not part of the avatar character. ![Head-Related Meshes](../../../assets/art/blender-ui/Blender-Project-Objects.png)_Blender Project Hierarchy_ ### Disabled objects When using the template project files for Blender, you might notice some objects, like attachment objects, are permanently hidden in the Viewport even when toggling the **Hide In Viewport** icon. Since attachment objects are often not modified until the end of the character creation process, these objects have **Disable In Viewport** enabled to make the organization of your project more efficient, especially when bulk toggling object visibility. If you need access to disabled objects, use the following instructions to access the **Disable In Viewport** toggle: 1. In your Outliner, click the **Filter** dropdown. 2. Enable the **Disable In Viewport** filter. 3. The **Disable In Viewport** icon now appears next to every object in the Outliner. Toggle the icon to change the Disabled status for your objects. ### Custom skin tones The Blender project files include a shader configuration that allows you to preview custom skin tones, similar to how they display in Roblox if users customize their skin tones. Textures with full or partial transparency allow the underlying `Class.Part.Color` to reveal through the applied texture, enabling a user to personalize their avatar character with custom skin tones. _A texture map example that uses low opacity to reveal underlying skin color and full opacity to apply a fully-opaque color to the undergarments, eyebrows, mouth, and eyes._ _The same avatar character using the same texture map. Each model has different underlying part colors except where the texture map applies a full color._ #### Preview skin tones > **Warning:** You must use **Blender 3.4+** to ensure that the helper shader configurations render correctly. If you are using a previous version of Blender, textures may not render as expected. The Blender project file includes shader configurations that allow you to preview custom skin tones similar to how they appear in Studio. These custom colors do not export with the model, but can provide you a quick visual reference for how a custom skin color and texture might look in Roblox. For this Blender shader configuration, keep in mind the following requirements: - You must switch to **Viewport Shading** mode for textures to render as expected. - When swapping out textures, the normal map's colorspace reverts to **SRGB** instead of **NonColor**. If this occurs, you might need to change it back in order for the normal map to render correctly. To preview your character's skin tone in Blender: 1. In **Layout**, select any geometry object, such as **Head_Geo**. 2. Go to the **Shading** tab. 3. In the Node panel, ensure **Object** is selected. 4. Find the **PartColor** node that is attached to the **Mix** node. 5. Pick a color and value on the node to apply a reference custom skin tone. #### Export setup While the skin tone preview is a critical element to verifying your templates color and tone compatibility in Roblox, it prevents your color texture map from automatically being packaged with the final `.fbx` file. There are two ways to resolve this before exporting your model: 1. In the Shading tab, disconnect the **Mix** node and replace it with the **ColorMap** node. 2. Export your textures manually as separate image files and add them later in Studio. For instructions on how to perform either export workflow, see [Export textures](/docs/en-us/art/characters/creating/export-textures.md). ### Scene scale > **Warning:** You can skip this section if you are using the Blender downloadable templates. The `.blend` versions of the templates already contain many of the project settings required when setting up a Blender project configuration. When creating Roblox assets in a modeling application, it's important to ensure your `.fbx` exports scale correctly. During export, set **Transform** > **Apply Scalings** to **FBX Unit Scale**. If you run into scaling issues on import, see [Blender FBX scaling](/docs/en-us/art/blender.md#adjust-scale-fbx) for alternative approaches. ### Animation range > **Warning:** You can skip this section if you are using the Blender downloadable templates. The `.blend` versions of the templates already contain many of the project settings required when setting up a Blender project configuration. For custom characters with facial animation, ensure that the timeline range is set between 0 and 330: 1. In the top right of the Animation Panel, click and set **Start** to `0`. 2. In the top right of the Animation Panel, click and set **End** to `330`. > **Info:** After you change your project settings, you can set these as your default Blender project settings by navigating to **File** > **Defaults** > **Save Startup File**. --- title: "Caging character head" url: /docs/en-us/art/characters/creating/caging last_updated: 2026-06-29T19:33:53Z description: "Cage your avatar characters in Blender to support layerable accessories and clothing." --- # Caging character head **Caging** is the process of updating the [cage mesh component](/docs/en-us/avatar/character-bodies.md#cage-meshes) of your avatar character. To allow your character to correctly wear layered clothing and accessories, you must update the default template cage mesh object to match the sculpting changes you made to your custom character. Since this tutorial only applies modeling changes to the head, the caging instructions below only apply to the **Head_OuterCage** object. If you make geometry changes to other parts of the character, you must also adjust those specific **_OuterCage** objects to match your sculpting changes. > **Error:** Do not delete any vertices or faces of the provided cages. Destructive modification of the cages can cause import issues and prevent the character model from equipping clothing and cosmetics. _Default head cage mesh no longer fits over sculpted head_ _Head cage mesh after adjustment_ > **Info:** If your character body shape incorporates many changes with different body parts, it may be more efficient to use the [Blender Cage Template](../../../assets/modeling/meshes/reference-files/Body_Cage_Templates.zip). This Blender project file includes a helper full-body `std_cage_deformable` mesh to automatically apply vertex changes to the individual body-part cages at the same time. To begin caging your character: 1. Starting from the Layout Tab, hide everything in the Outliner except the **Head_OuterCage** and the **Head_Geo** objects. 2. With the **Head_OuterCage** selected, navigate to **Object Properties** > **Viewport Display** and set the **Display As** to **Wire**. Toggle this setting back to **Solid** when you are finished. 3. Click on the **Head_OuterCage** object and switch to **Edit Mode**. 4. Enable **X-Axis symmetry** and **Topology Mirror** to ensure you are making symmetrical vertex changes to your cage. 5. Switch to **Edit Mode**. 6. Using the **Grab tool** (`G`), click and grab parts of the cage mesh and align it to fit tightly over the goblin head mesh. Keep the following in mind: 1. **Never delete any of the cage vertices.** Missing vertices can cause errors and issues with equipping clothing accessories. 2. If editing the base of your head cage, ensure that the base of the Head cage aligns with the top of the UpperTorso cage. 3. In different Select modes, hold `Shift` and click multiple vertices/edges/faces to select and edit the geometry. 4. You can verify a tight fit by grabbing vertices and moving them into the Head_Geo mesh to check where the meshes intersect and moving the cage vertices until the cage mesh just covers the head mesh. 5. Toggle visibility on your different mesh objects to improve visibility and access to vertices. 6. After the wireframe is tightly fitted over the head mesh, set the **Display As** back to **Solid** and spot check and fix any intersecting vertices. Your final product should feature a cage mesh that sits directly over the head mesh without any of the Head_Geo geometry intersecting through the solid sections of the cage. > **Success:** For a comparison reference, you can download [this version of the tutorial project with caging completed](../../../assets/art/reference-files/checkpoint/3_Goblin-caged.blend). --- title: "Combine head geometry" url: /docs/en-us/art/characters/creating/combine-head-geometry last_updated: 2026-06-29T19:33:53Z description: "When creating characters in Blender, you must combine the head geometry to a single object." --- # Combine head geometry Whenever you are ready to export your model, it's important to clean up your project to ensure that your model is ready to export. This includes the following tasks that ensure the customized template model and all of the avatar components can import into Studio successfully: - Combining extra head geometry - Removing extra head bones - Verifying attachment points - Final technical checks > **Error:** Failure to combine head geometry and [remove head bones](/docs/en-us/art/characters/creating/remove-extra-bones.md) will cause issues with validation, as the character will no longer adhere to the expected R15 geometry and joint hierarchy. ## Combine head geometry At this point, you should also combine the separate head and face objects within the single Head_Geo object. Some templates don't include some face objects, like lashes. To combine the head and face meshes: 1. In the Outliner, hold `Ctrl`/`⌘` and click following objects, ending with the Head_Geo: 1. UpperTeeth_Geo 2. LowerTeeth_Geo 3. Tongue_Geo 4. RightLash_Geo 5. RightEye_Geo 6. LeftLash_Geo 7. LeftEye_Geo 8. Head_Geo > **Error:** When selecting multiple head objects, make sure that the **Head_Geo** is highlighted as yellow, indicating it as the main object that the others merge into. This correctly preserves the custom properties stored in the head mesh. You can achieve this by selecting the head last in the Outliner. 2. While in Object mode, right-click in the Viewport and select **Join**. The objects combine as a single **Head_Geo**. --- title: "Export character model" url: /docs/en-us/art/characters/creating/export-character last_updated: 2026-06-29T19:33:53Z description: "Export your character model from Blender using the correct settings and processes." --- # Export character model > **Warning:** It's important to test your assets multiple times at every point of the asset creation process, whether it is within Blender or after importing into Studio. See [Test characters](/docs/en-us/art/characters/testing.md) for more information. Whether you are exporting your character for testing, or are performing a final export out of Blender, you must apply the appropriate export settings to ensure that Blender exports the proper character data. To export your character: 1. In the topbar, click **File** > **Export** > **FBX (.fbx)**. The Blender file browser window displays. 2. Set **Path Mode** to **Copy** and enable the **Embed Textures** icon. 3. In the Include section, enable **Custom Properties**. 4. Expand the Armature section and uncheck **Add Leaf Bones**. 5. Enable **Bake Animation**. 6. Expand Bake Animation and **uncheck NLA Strips**, **All Actions**, and **Force Start/End Keyframes**. 7. In Bake Animation, set **Simplify** to **0.0**. 8. Click the **Export FBX** button. Save the `.fbx` to the directory of your choice. > **Warning:** After exporting your .fbx file, see [Test characters](/docs/en-us/art/characters/testing.md) for steps on importing your character model into a test place and verifying your avatar and related components. > **Success:** After importing the `Class.Model` character to Studio, you can now perform the following with this asset: - [Upload the character](/docs/en-us/art/accessories/creating-rigid/publishing.md) to the Marketplace. - Use the humanoid character on an existing experience by applying a [HumanoidDescription](/docs/en-us/characters/appearance.md#manually-modify-appearance)to the `Class.Model` object. - Save the asset to your [Toolbox](/docs/en-us/projects/assets/toolbox.md) to share or use within any of your experiences. --- title: "Export textures" url: /docs/en-us/art/characters/creating/export-textures last_updated: 2026-06-29T19:33:53Z description: "Export your character textures from Blender using the correct settings and processes." --- # Export textures > **Warning:** It's important to test your assets multiple times at every point of the asset creation process, whether it is within Blender or after importing into Studio. See [Test characters](/docs/en-us/art/characters/testing.md) for more information. Whether you are exporting your character for testing, or are performing a final export out of Blender, you must apply the appropriate export settings to ensure that Blender exports the proper character data. ## Export textures Including the default PBR textures, your template character includes four separate image maps that make up its surface appearance. Depending on your workflow, you can choose to embed the image maps to your export file or export the textures separately as image files. Both methods have advantages: - [Embedding textures](#embed-textures) simplifies your export by packing all of your textures within the single `.fbx` file. - [Exporting texture images](#export-textures) directly allows you direct access to the image textures, so you can test and swap them more quickly. ### Embed textures Embedding the texture maps to your `.fbx` export can simplify the Blender export and Studio import process. When embedding your textures with Roblox's template files, you need to make a quick adjustment to custom skin tone shader nodes in the Blender file. _Default node configuration: Mix node connected to Base Color_ _Exportable node configuration: Color node connected directly to Base Color_ To prepare your textures to embed with export: 1. In Object mode, select any part of the character. 2. Navigate to the **Shading** tab. 3. Ensure the **Type of Data** dropdown is set to **Object**. 4. Find the node that connects to the **Principled BSDF's Base Color**. 5. Click and drag the line from **Base Color** to disconnect the node. 6. Find the **file26** node with the color texture map and click and drag the **Color** to **Principled BSDF's color** node. ### Unpack image files As an alternative to embedding textures, you can export your texture files as separate `.png` image files, which allows you to quickly access and swap image texture maps. To export your texture image files: 1. Navigate to **File** > **External Data** > **Unpack Resources**. 2. Select **Use files in current directory** to save in the same directory as your project. Blender exports your image files to a textures directory in your project's parent directory. --- title: "Final checks" url: /docs/en-us/art/characters/creating/final-checks last_updated: 2026-06-29T19:33:53Z description: "Before exporting, verify that your model meets Studio's requirements." --- # Final checks Depending on the types of customizations you made to your template project or model, the following are additional tasks to look out for when cleaning up your project: - Ensure that your project doesn't contain additional mesh objects that aren't part of your character model geometry. - Verify that there isn't additional animation data, such as additional keyframes or saved poses and orientations that are not related to the original facial animation data. - Double-check that your geometry, rig, and texture adhere to the [avatar technical specifications](/docs/en-us/avatar/character-bodies/specifications.md). --- title: "Template head structure" url: /docs/en-us/art/characters/creating/head-objects last_updated: 2026-06-29T19:33:53Z description: "Each Roblox avatar template contains modular separate pieces that must later be combined or removed." --- # Template head structure In each template file, each avatar body includes extra head mesh objects and face armature bones. Separating these objects within the templates allow you to make easier changes to each of these separate objects, as demonstrated during the texturing step. To avoid validation errors, you must [join](/docs/en-us/art/characters/creating/combine-head-geometry.md) and [remove](/docs/en-us/art/characters/creating/remove-extra-bones.md) these extra objects during the cleanup process prior to exporting. The extra head mesh objects are the following: - Head_Geo - UpperTeeth_Geo - LowerTeeth_Geo - Tongue_Geo - RightLash_Geo - RightEye_Geo - LeftLash_Geo - LeftEye_Geo ![Head-Related Meshes](../../../assets/art/blender-ui/Face-Objects.png)_Extra head-related meshes in Blender's Outliner_ The extra head bone children are the following and may differ between templates: - Tongue - LowerTeeth - UpperTeeth - LeftEye - RightEye ![Head-Related Meshes](../../../assets/art/blender-ui/Face-Bones.png)_Extra head-related bones in Blender's Outliner_ While these extra objects exist to aid the creation and modification process, leaving them in your project on final export will cause validation issues when uploading these assets to the Marketplace. --- title: "Create with templates" url: /docs/en-us/art/characters/creating last_updated: 2026-06-29T19:33:53Z description: "Use Roblox's supplied template models to create your own unique avatar character in Blender." --- # Create with templates You can create your own custom avatar character using Blender and one of Roblox's downloadable template models. Using templates can save you a lot of time by skipping the complex processes of setting up your armature, rigging, skinning, and configuring your character for face animations. This tutorial is intended for creators of all skill levels with moderate Blender experience to create a unique character by: 1. Selecting a base template that include time-saving Blender configurations. 2. Modeling using non-destructive sculpting workflows 3. Texturing using Blender's texture paint tools 4. Caging an asset by editing the template's cage mesh objects 5. Cleaning and prepping the project for export 6. Exporting your asset for use or testing in Studio _Starting template model_ _Example model after modifications_ > **Info:** This guide uses [Blender 3.4+](https://www.blender.org/download/releases/3-4/) as a practical example for customizing a character template. Before you begin, you should have a basic knowledge using Blender's interface, tools, and viewing controls. > > If you are using another program, you can still apply the general workflow of this tutorial with your program's similar tools. --- title: "Modeling best practices" url: /docs/en-us/art/characters/creating/modeling-best-practices last_updated: 2026-06-29T19:33:53Z description: "Understand important modeling concepts to prevent major issues before any modeling steps." --- # Modeling best practices Modeling, sometimes known as **sculpting**, is the process of shaping the geometry of your model or mesh. This guide covers important concepts and tips you should review before creating your own unique character shape. By understanding how to make non-destructive sculpting changes to the head, the character part that contains the most complex components, you can continue to apply these techniques and concepts to other parts of the character model body. _Starting template model_ _Model after custom sculpting_ > **Error:** When editing templates, **don't delete or add vertices to your character body**. This ensures that your character's skinning and facial animation data remains unaltered and fully functional. ## Non-destructive modeling Non-destructive modeling means using a process that doesn't alter the physical shape or construction of the base mesh object. When modifying a template model, don't use tools or functions that delete or add vertices to your meshes. Instead, use Blender's sculpting tools to alter the shape of your character by **only changing the position of existing vertices**. This ensures that vertices and faces that have skinning or animation data associated with them retain that important character data. > **Warning:** Extreme geometric changes, even if the vertices are not deleted, can still adversely affect your rigging and skinning. It's important to choose a starting template file close to your final design and to make consistent and proportional changes when sculpting. ## Edge flow Edge flow is a common modeling concept of ensuring that the vertices of your model naturally follow the organic curvature of your model's shape. When making changes to your model's topography, you should maintain a natural edge flow by ensuring that your vertices remain a proportionate distance from each other and follow the common muscle groups and contours of your model. Even when maintaining edge flow, you should avoid sculpting certain regions of the character model. The following are examples of important sections of the face that follow a natural edge flow and should be kept in a similar shape: | Round head region | Edge flow notes | | --- | --- | | #### Round #### Narrow | **Forehead Wrinkles and Nasolabial Edges**: The edge lines for the forehead wrinkles and nasolabial edges are vital for various expressions involving the mouth, forehead furrowing, eyebrow movement and cheeks. If you modify these sections of your topography, ensure that you retain the original shape whenever possible, and that they maintain a similar relative relationship to each other. | | #### Round #### Narrow | **Mouth and Lip Edges**: The topography surrounding the lips and mouth have circular mesh structures for the mouth to open and close. This structure can fold the mesh to shape the lips to naturally visualize _a_, _e_, _i_, _o_, and _u_ vowel sounds. The neutral shape is a closed mouth, and the edge flows around the mouth in a continuous line to accurately fold and deform the expected mouth shapes.

In this tutorial, we don't recommend modifying this area of the facial topography because of the risk of adversely affecting the underlying tongue, upper and lower jaw, and saved facial data. You can use a sculpting mask to ensure that this area is not affected by any modeling changes. | | #### Round #### Narrow | **Eyes**: The eyelids have enough mesh lines on the eyelids to close the eyes. The continuous lines on the eyelids enable the eyelids to deform and fold as expected when blinking or widening.

In this tutorial, it isn't recommended to modify this area of the facial topography because of the risk of adversely affecting the underlying eyeball meshes and saved facial data that contribute to accurate expressions. You can use a sculpting mask to ensure that this area is not affected by any modeling changes. | | #### Round #### Narrow | **Eyelids and Eyebrows**: The eyelids and eyebrows require adequate space between the two regions.

If modifying the topography above the eyes, keep in mind that the eyelids and eyebrows require a natural space between the two regions. Both the eyelids and eyebrows can shift and change positions with various facial expressions and, if improperly modeled, can clash into each other during a facial pose. | Not following edge flow concepts can result in the topology of your model conflicting with each other during animations, sometimes known as crashing. _In this example, the top of the eyelid and bottom eyelid travel past each other when closing causing jagged artifacts where the vertices collide and crash._ By carefully sculpting your template mesh, you can avoid having to correct these topology collisions later, which can often require manually correcting rig, skinning, and/or facial animation data.
--- title: "Modeling tips" url: /docs/en-us/art/characters/creating/modeling-tips last_updated: 2026-06-29T19:33:53Z description: "Use these Blender modeling tips to expedite your character creation." --- # Modeling tips There are many tips and tricks during the modeling process that can save you time and prevent major issues in your workflow. There are many resources online that can help you make your modeling process more efficient. The following are various beginner-friendly tips and settings in Blender that allow you to visualize and configure objects efficiently in your project. ### Hide inactive objects In your Outliner, hide objects you aren't editing by enabling the **Hide In Viewport** icon. This cleans up your workspace and helps ensure you don't make unintentional changes to another object or collection. You can quickly bulk hide and unhide any parent and child objects that are not [disabled](/docs/en-us/art/characters/creating.md#disabled-objects). To bulk hide objects: 1. In the Outliner, navigate to a parent object, such as **Cage** or **Joints**. 2. Hold `Shift` and click the **Hide In Viewport** icon to toggle the object's visibility. ### Enable wireframe view Whenever you use a modeling application, switch between different viewport options, like X-Ray and Wireframe, to gain a better perspective on your vertices and shapes. This tutorial uses a wireframe view over the mesh shape, and many examples use various material settings and viewport options. Enable wireframes on your material view by setting the following: 1. Select the **Head_Geo**. 2. In the **Properties** panel, navigate to **Object Properties**. 3. In Viewport Display, enable **Wireframe**. --- title: "Remove extra bones" url: /docs/en-us/art/characters/creating/remove-extra-bones last_updated: 2026-06-29T19:33:53Z description: "When creating characters in Blender, you must remove the extra head bones included in the template." --- # Remove extra bones The helper bones used to help place the head geometries also require removal to validate correctly. While these extra bones do not include any skinning data, do not delete any of the facial animation bones parented within DynamicHead, as these include important skinning data that drives facial animation. > **Error:** Failure to [combine head geometry](/docs/en-us/art/characters/creating/combine-head-geometry.md) and remove head bones will cause issues with validation, as the character will no longer adhere to the expected R15 geometry and joint hierarchy. Remove the additional head bones by selecting them and deleting them in edit mode: 1. If required, toggle visibility on your **Armature** object. The bones display in the viewport. 2. In the Outliner, expand the character's armature object and find the **Joints** object that parents the bone structure of the character. 3. Expand the Joints hierarchy by holding **shift** and clicking the expansion dropdown. 4. In the viewport, select any bone and switch to **Edit Mode**. 5. Under the Head joint, hold **shift** and select all the head children joints except DynamicHead. 6. With the extra head bones selected, right click in the viewport and select **Delete Selected Bones**. --- title: "Sculpt the head" url: /docs/en-us/art/characters/creating/sculpting last_updated: 2026-06-29T19:33:53Z description: "Use Blender's sculpting tools to make unique changes to the shape of your character." --- # Sculpt the head After reviewing the modeling best-practices and sculpting tips, you can begin sculpting your character. This tutorial demonstrates sculpting changes to the [RoundMale template](../../../assets/art/reference-files/RoundMale.zip), creating a goblin-like character. The sculpting instructions utilize the following Sculpting tools: - **Grab** - Selects and pulls a group of vertices. - **Smooth** - Eliminates irregularities in the area of the brush's influence. - **Flatten** - Averages out the vertices within the brush's influence on a common plane. - **Elastic Deform** - Similar to Grab, but adds an organic stretch and elasticity to the neighboring vertices. > **Info:** You can use Blender's other sculpting tools, though it's important to use a tool that aligns with the [Non-Destructive Modeling](/docs/en-us/art/characters/creating/modeling-best-practices.md#non-destructive-modeling) concepts to preserve the vertices of the model. To begin sculpting the head: 1. With the template project opened, hide the other Geo objects to isolate the head mesh. 2. Select the **Head_Geo** mesh object and switch to **Sculpting** mode. 3. At the top right of the viewport, set the following options: 1. Enable **X-Axis Symmetry**. 2. Enable **Wireframe** view. 4. Using the **Mask** tool, paint over the mouth and eyes where you do not want to sculpt unexpectedly. 5. With the **Grab** and **Smooth** tools, make the following edits to your template head: 1. Reshape the head, flattening and enlarging the top. 2. Elongate the ears. 1. Expand the surface near the ear to broaden the base 2. Grab and extend each ear, ensuring it connects smoothly to the base and vertices remain proportionally spaced when possible. 3. Try the Flatten tool to straighten and align a region. 4. Use the Elastic Deform tool to stretch and pull multiple vertices. 3. Extend the bridge of the nose and expand the size of the brow 4. Broaden the chin so it prominently protrudes. 6. Perform a refinement pass to add final details, polish, and accentuate your main features, such as: - Adding details in the outer and inner ears - Adding more detail to chin and cheeks - Improving edge lines and spacing in areas with crowded vertices > **Success:** For a comparison reference, you can download [this version of the tutorial project with sculpting completed](../../../assets/art/reference-files/checkpoint/1_Goblin-sculpted.blend). --- title: "Template files" url: /docs/en-us/art/characters/creating/template-files last_updated: 2026-06-29T19:33:53Z description: "Roblox provides template models to create your own unique avatar character in Blender." --- # Template files Each template comes pre-baked with the [necessary components of an avatar character](/docs/en-us/avatar/character-bodies.md#components-of-an-avatar) to save you time and effort in creating a custom avatar character. When creating characters from scratch, these individual components typically take a lot of time and a deep technical background in your modeling application. Templates allow you to skip several steps and work from a variety of base character shapes. | Template prototype | | Sample customizations | | --- | --- | --- | |
Muzzle head template | |
| |
Round head template | |
| You can choose from a variety of templates that best match your final design and save time on creating the technical components. The examples in the template tutorials utilize the [Round Blender template](../../../assets/art/reference-files/RoundMale.zip). Experiment with other templates to create your own unique character and final result. Each `.zip` contains a `.blend`, `.fbx`, and PBR texture `.png` files for that template model. If using Blender or following the [template creation guides](/docs/en-us/art/characters/creating.md), use the `.blend` project. > **Warning:** Some of the template files include additional mesh assets for eyebrows and eyelashes. These eyebrows and eyelashes versions may not be fully compatible with the template body. For the latest information on eyebrows and eyelashes, see [Makeup](/docs/en-us/avatar/makeup.md#components-of-makeup). #### Head Shapes _ Round_ Female: [RoundFemale.zip](../../../assets/art/reference-files/RoundFemale.zip) Male: [RoundMale.zip](../../../assets/art/reference-files/RoundMale.zip) _ Square_ Female: [SquareFemale.zip](../../../assets/art/reference-files/SquareFemale.zip) Male: [SquareMale.zip](../../../assets/art/reference-files/SquareMale.zip) _ Muzzle_ Female: [MuzzleFemale.zip](../../../assets/art/reference-files/MuzzleFemale.zip) Male: [MuzzleMale.zip](../../../assets/art/reference-files/MuzzleMale.zip) #### Realistic _ Realistic_ Female: [SemiRealisticFemale.zip](../../../assets/art/reference-files/SemiRealisticFemale.zip) Male: [SemiRealisticMale.zip](../../../assets/art/reference-files/SemiRealisticMale.zip) #### Anime _ Anime_ Female: [AnimeFemale.zip](../../../assets/art/reference-files/AnimeFemale.zip) Male: [AnimeMale.zip](../../../assets/art/reference-files/AnimeMale.zip) #### Stylized _ Stylized_ Single body: [Stylized.zip](../../../assets/art/reference-files/StylizedHuman.zip) #### Caricature _ Caricature_ Single body: [Caricature.zip](../../../assets/art/reference-files/Caricature.zip) After downloading your template file, review the [unique head object structure](/docs/en-us/art/characters/creating/head-objects.md) and [additional Blender settings](/docs/en-us/art/characters/creating/blender-configurations.md) that require adjustment before exporting. It's crucial to understand these concepts prior to committing to any alterations as these can conflict with Roblox's avatar requirements if not addressed.
--- title: "Texturing eyes" url: /docs/en-us/art/characters/creating/texturing-eyes last_updated: 2026-06-29T19:33:53Z description: "Use Blender's texture painting tools to apply a custom surface appearance on your character's eyes." --- # Texturing eyes You can apply a fully opaque texture to the eyes to achieve a more non-human visual effect for your character. Before editing the eye textures, join the two eye objects to texture them both at the same time. Then texture paint the entire eye, starting with the outer eye, moving to the inner eye, and finishing with the pupil. To texture your eye mesh objects: 1. Increase the texture image resolution. 2. In the Outliner, toggle the **Hide in Viewport** option for the **Head_Geo**, **LowerTeeth_Geo**, **Tongue_Geo**, and **UpperTeeth_Geo**. Leave the eye mesh objects visible. 3. In Object Mode, hold `Shift` and click on both the **LeftEye_Geo** and the **RightEye_Geo**. Then right-click and select **Join** (`Ctrl``J`; `⌘``J`) to join the objects. The two objects merge using the name of the last object selected. 4. Switch to **Texture Paint** mode. 5. With the Draw tool active, set the following brush settings for the outer eye shadow: 1. In Brush Settings, set **Radius** to **50px** and **Strength** to **1.0**. 2. In Color Picker, select **black**. 6. In the Texture Paint **3D view window**, completely paint the eye objects black. This serves as the basis for the outer-eye shadow. 7. With the Draw tool active, set the following brush settings for the outer eye shadow: 1. In Brush Settings, set **Radius** to **75px** and **Strength** to **1.0**. 2. In Color Picker, select a **deep yellow**. 8. In the Texture Paint **2D view window**, line up your cursor to the center of the UV map that corresponds to the eye texture. Click 4-6 times on the center of each eye to create an outer eye color over the base shadow. 9. For visibility, enable Head_Geo visibility and reorient the camera to a front-facing view. 10. Paint the inner eye color using the following suggestions: 1. Click the **X Symmetry** button to enable symmetry. Toggle this off if creating asymmetrical assets. 2. In the Overlay view options, **enable Wireframe geometry view**. You can toggle this overlay off when spot checking your visual elements. 3. Update your brush settings for the inner eye: 1. In Brush Settings, set **Radius** to **5px**. You can quickly change this radius as you paint by holding `F`. 2. In Color Picker, select a strong **red** shade for the inner eye. 4. Periodically make the head mesh visible to ensure your eye texture works with the rest of the model. 11. Update your brush settings for the pupil: 1. In Brush Settings, set Radius to **5px**. You can quickly change this radius as you paint by holding `F`. 2. In Color Picker, select a bright **red** shade for the pupils. 12. Using the Brush tool, paint the pupils of your model. 13. After completing your texture painting, switch back to **Edit Mode**. 14. Shift click both of your eye objects, and press `P` and select **By Loose Parts** to separate the two meshes. 15. Rename your remaining mesh to the original **RightEye_Geo** or **LeftEye_Geo** name. --- title: "Texturing face" url: /docs/en-us/art/characters/creating/texturing-face last_updated: 2026-06-29T19:33:53Z description: "Use Blender to apply a custom surface appearance on your character's face." --- # Texturing face After texturing the eyes, you can begin texturing the face. The concepts are similar to texturing the eyes, though you only use an opaque texture to ensure that the custom skin tone can remain visible. To texture the face: 1. In the Texture Paint tab, resize your texture resolution to **2048** or **4098**: 1. In the left Paint window, select **Image** > **Resize**. 2. Set the texture size to **2048 x 2048**. 2. In the Tool sidebar, update your brush settings for the facial features. 1. In Brush Settings, start with the following Brush recommendations: 1. **Radius** to **5px**. You can change this radius as you paint. 2. **Strength** to **0.300**. Modify this value when applicable. 3. In Symmetry, enable **X Axis Mirroring**. 2. In Color Picker, select a neutral **dark** shade for the shadows. 3. Apply texture to your character's face features, such as the nostrils, wrinkles, ears, and chin. Keep in mind that the painted textures will look softer and blended with the surface once the texture resolution is resized. 4. When complete, resize your texture resolution back to **1024 x 1024**: 1. In the left Paint window, select **Image** > **Resize**. 2. Set the texture size back to **1024 x 1024**. Your final result should include various facial textures that help accentuate the features of your character and work well with any custom skin tone. Try exploring various amounts of texturing and the types of surfaces you could expect on skin, such as wrinkles, scars, or dirt/grunge. You should also verify your textures between several skin tones to ensure that your texturing looks acceptable with various skin types. _Default Green_ _Pink_ _Blue_ > **Success:** For a comparison reference, you can download [this version of the tutorial project with texturing completed](../../../assets/art/reference-files/checkpoint/2_Goblin-textured.blend). --- title: "PBR textures (optional)" url: /docs/en-us/art/characters/creating/texturing-pbr last_updated: 2026-06-29T19:33:53Z description: "PBR textures are high-definition texture maps that can elevate your custom characters." --- # PBR textures (optional) Physically-based rendering (PBR) textures use multiple texture maps to define how a surface reacts under different lighting and environmental conditions. PBR textures can elevate the look and feel of your assets, including your avatar, accessories, and clothing. Typically, PBR textures are created in a texturing tool, such as [Substance 3D](https://www.adobe.com/products/substance3d-painter) or [ZBrush](https://www.maxon.net/en/zbrush). Many third-party 3D models available online include PBR textures. _Characters and accessories can make use of PBR textures to apply visual pop and material realism._ _PBR textures allow for virtually any type of material surface, such as leather and denim._ Although each template includes PBR textures, for the purpose of demonstrating the basic texturing process within Blender, this tutorial doesn't cover the PBR texturing process. For more information and references on advanced texturing, see [PBR textures](/docs/en-us/art/modeling/surface-appearance.md). --- title: "Texturing setup" url: /docs/en-us/art/characters/creating/texturing-setup last_updated: 2026-06-29T19:33:53Z description: "Set up Blender to optimize your project for texturing the eyes and face of your template." --- # Texturing setup **Texturing** is the process of customizing the color, tone, and shading of your model's surface. Custom meshes and models use a 2D image, known as a texture map, to project various surface appearance elements onto your 3D object. _Model after custom sculpting_ _Model after texturing_ Each template comes with a color texture map, which you can alter and modify using Blender's texture editing tools. Since most Roblox avatars can take advantage of custom skin tones, it's important to understand how to [preview custom skin tones](/docs/en-us/art/characters/creating.md#preview-skin-tones) in Blender to verify how your final asset will look in a Roblox experience. For the purpose of demonstrating the basic texturing process, this tutorial goes over basic texture painting setup, applies a completely opaque texture over parts of the character eyes, and partially opaque details over the face. You can apply these same techniques to other parts of your character geometry. > **Warning:** When texturing parts of your character model's body, ensure that your character model includes a modesty layer over sensitive regions. See [Community Standards](/docs/en-us/marketplace/marketplace-policy.md) for more information on Roblox's policies. ## Set texture resolution Roblox Studio supports a texture resolution of **1024 x 1024** for albedo texture maps. When using applications like Blender or Maya to texture paint directly on a model, extremely fine details may not paint as expected due to the resolution and level of detail of the painting. _Texture painting at 1024 x 1024 resolution_ _Texture painting at 2048 x 2048 resolution_ When adding fine details to your texture, increase the image resolution of your texture map temporarily to **2048 x 2048** or **4096 x 4096**. After completing your texturing, change the image resolution back to **1024 x 1024** to bake in the changes. To change your texture image resolution: 1. Starting from Layout mode, select the **Head_Geo**. 2. Switch to **Texture Paint** mode. 3. In the left Paint window, select **Image** > **Resize**. 4. Set the texture size: 1. Use **2048** or **4096** for a higher res for detailed texture work. 2. Use **1024** for the default Roblox-supported resolution. --- title: "Verify attachment placement" url: /docs/en-us/art/characters/creating/verify-attachments last_updated: 2026-06-29T19:33:53Z description: "When creating characters in Blender, the character model requires minor attachment adjustments to be Studio-ready." --- # Verify attachment placement Attachment points are non-rendered objects of your avatar where rigid accessories attach to. Each template includes the required attachments at their expected locations ending with **_Att**. Before exporting, it's important to verify the attachment position and, if necessary, adjust the location if you made any changes to the shape of your model. _Original template attachment positions_ _Attachments after adjustment_ Center attachment points at their respective locations. Each attachment should overlap approximately halfway with the character's mesh. This tutorial only changes the head of your model, so you only need to adjust the **Hat**, **Hair** and **FaceFront** attachments. To enable visibility and verify your head attachment point: 1. If you haven't already, enable [disabled objects](/docs/en-us/art/characters/creating.md#disabled-objects) in your Outliner. 2. In the Outliner, find the Joints parent object. 3. Hold `Shift` and click the **Hide** icon for the Joints object to hide everything. 4. Expand the Joints object and un-hide the Head_Geo and Hat_Att and Hair_Att. 1. If required, disable **Disable In Viewport** next to the _Att objects. 5. Toggle Visibility for the **Hat_Att**, **Hair_Att**, and **FaceFront_Att**. 6. Verify the placement of the attachments. The goblin's head is shorter than the starting template, so the attachments require minor vertical adjustment. 7. Select the attachments 8. Switch to **Edit** Mode. 9. Using the Grab tool, position the attachments vertically along the y-axis until they are about halfway embedded in the head. --- title: "Export avatar animations from Maya" url: /docs/en-us/art/characters/export-avatar-animations-from-maya last_updated: 2026-06-29T19:33:53Z description: "Explains the process for exporting avatar animations from Maya." --- # Export avatar animations from Maya **Avatar animations** are animations that you can create and assign to play for any avatar action, such as walking, swimming, or dancing. In addition to using the [Animation Editor](/docs/en-us/animation/editor.md) or [Animation Capture](/docs/en-us/animation/capture.md) tools within Studio, you can create avatar animations through third-party modeling and animation tools like [Maya](https://www.autodesk.com/products/maya/overview), then import your avatar animations directly into Studio to apply to any avatar with Roblox. By using this guide as a checklist of the specific settings your rig and animation must meet for the export process, you can ensure that your animations import successfully. ![A humanoid mannequin in Maya with IK controls.](../../assets/animation/exporting-avatar-animations-from-maya/Overview-Mannequin.jpg)_R15 mannequin reference_ _Example walking animation_ > **Info:** This guide uses a downloadable [R15-Mannequin-Rig.ma](../../assets/animation/exporting-avatar-animations-from-maya/R15-Mannequin-Rig.ma) file that contains an R15 mannequin that you can use as a reference to create and export animations. If you choose to animate on your own R15 rig, the same rig configuration and export settings apply. ## Configure the rig Before you export your avatar animation from Maya, you must configure your rig to be compatible with Studio's import requirements, including making sure that the rig follows a specific hierarchy and naming conventions so that Studio is able to recognize the file as an avatar animation, and that the rig's translation channel is muted so that the root node doesn't move with the animation. ### Hierarchy and naming conventions Studio requires a specific hierarchy and naming conventions for a humanoid rig's internal joint structure so that it can recognize what you're importing as an avatar animation. If you aren't using the downloadable R15 mannequin reference rig to create your animations, make sure your rig uses the following joint hierarchy and naming convention exactly as it is: - Root - HumanoidRootPart - LowerTorso - UpperTorso - Head (representing the base of the neck) - LeftUpperArm - LeftLowerArm - LeftHand - RightUpperArm - RightLowerArm - RightHand - LeftUpperLeg - LeftLowerLeg - LeftFoot - RightUpperLeg - RightLowerLeg - RightFoot ### Mute the translation channel Roblox avatar animations cannot move a character when they play; instead, avatar animations animate rigs at their world position. If your avatar animation is a locomotion-type of animation where the root node moves in space, such as a character that moves forward as their walk animation plays, you must mute the rig's translation channel so that the animation plays without moving the rig on export. To mute the translation channel: 1. Open the **Graph Editor**. 1. In the menu bar, click **Windows**. A contextual menu displays. 2. Hover over **Workspaces**, then click **Animation**. The **Graph Editor** displays.![A close up view of the Windows dropdown menu in Maya. The Workspaces menu item is highlighted, as well as the Animation submenu.](../../assets/animation/exporting-avatar-animations-from-maya/Opening-GraphEditor-and-TimeSlider.jpg) 2. Move the **Time Marker** to frame 0.![A close up view of the Graph Editor in Maya. The Time marker for a character's right hand is at frame 0, and is highlighted.](../../assets/animation/exporting-avatar-animations-from-maya/Time-Marker.jpg) 3. In the **Outliner**, select the root node. If you are using the reference file, this is **TSM3_root**. 4. In the **Channel Box**, right-click on the **Translate Z** value. A contextual menu displays. 5. Select **Mute Selected**. The Translate Z value's channel box color changes to orange to confirm that it's muted.![A close up view of the Channel Box in Maya. The Mute Selected menu item in the contextual menu from step 4 is highlighted.](../../assets/animation/exporting-avatar-animations-from-maya/Mute-Selected.jpg)![A close up view of the Channel Box in Maya. The Translate Z property channel box color is orange to signify it is muted. The rest of the values are in cyan blue to signify that they remain unmuted.](../../assets/animation/exporting-avatar-animations-from-maya/Muted-TranslateZ.jpg) When you play the animation, the character's root node doesn't move in space anymore, and the character animates in place at the origin of the scene. ## Export the animation Now that your rig animates in place and has a hierarchy and naming data that is compatible with Studio, you can export the animation into a `.fbx` format. Maya doesn't load this capability by default, so you need to enable an FBX plugin to gain the additional settings necessary to export the file in a format Studio can recognize. To export your avatar animation from Maya: 1. Install the FBX plugin. 1. In the menu bar, click **Windows**. A contextual menu displays. 2. Hover over **Settings/Preferences**, then click **Plug-in Manager**. The **Plug-in Manager** window displays.![A close up view of the Windows dropdown menu in Maya. The Settings/Preferences menu item is highlighted, as well as the Plug-in Manager submenu.](../../assets/animation/exporting-avatar-animations-from-maya/Plugin-Manager.jpg) 3. In the search field, input **fbxmaya**. The **fbxmaya.mll** plugin displays. 4. Enable the **Loaded** and **Auto load** options.![The Plug-in Manager with a close up view of the fbxmaya file. Its Loaded and Auto load settings are highlighted.](../../assets/animation/exporting-avatar-animations-from-maya/Loaded-and-AutoLoad-Radio-Buttons.jpg) 2. In the menu bar, click **File**. A contextual menu displays. 3. Select **Export All**. The **Export All** window displays.![A close up view of the File dropdown menu in Maya. The Export All menu item is highlighted.](../../assets/animation/exporting-avatar-animations-from-maya/Export-All.png) 4. At the bottom of the window, 1. In the **File name** field, input a name for your avatar animation. 2. Click the **Files of Type** dropdown menu, then select **FBX export**. The **Options pane** updates its settings. 5. In the **Options** pane, click the **Animation** dropdown for additional animation settings, then in the **Bake Animation** section, 1. Enable **Bake Animation**. 2. Verify that the **Start** and **End** fields correlate to the frames you want to export for your animation loop. 3. Set **Step** to **1**.![A close up view of the Options pane. The Bake Animation settings are highlighted.](../../assets/animation/exporting-avatar-animations-from-maya/Bake-Animation.jpg) 6. Click the **Advanced Options** dropdown for additional export settings, then in the **FBX File Format** section, 1. Set **Type** to **Binary**. 2. If required, set **Version** to **FBX 2020**.![A close up view of the Options pane. The FBX File Format settings are highlighted.](../../assets/animation/exporting-avatar-animations-from-maya/FBX-File-Format.jpg) 7. In the bottom right corner, click the **Export All** button. After a moment, the avatar animation `.fbx` file displays in your file browser. ## Test the animation in Studio Once you have your avatar animation `.fbx` file, you can test it within Studio to make sure that the animation plays without any errors. To test the animation in Studio: 1. Open a pre-built character rig. 1. From the toolbar's **Avatar** tab, click **Character**. 2. Select any rig you want to use as a test. The rig displays within the viewport. 2. Connect the rig to the **Animation Editor**. 1. From the toolbar's **Avatar** tab, click **Clip Editor**. The [Animation Editor](/docs/en-us/animation/editor.md) window displays. 2. In the viewport, click your rig. A dialog displays in the editor. 3. In the **Animation Name** field, enter a new animation name, then click the **Create** button. The editor window displays the media and playback controls, timeline, and track list. 3. Import your avatar animation into the rig. 1. Navigate to the **Media and Playback Controls** and click the **⋯** button. A contextual menu displays. 2. Hover over **Import**, then select **From FBX Animation**. A file browser displays.![A close up view of the Animation Editor window. The ellipsis contextual menu displays, and the From FBX Animation submenu item is highlighted.](../../assets/animation/exporting-avatar-animations-from-maya/Importing-From-FBX-Animation.jpg) 3. Select the FBX animation you just exported from Maya, then click **Open**. The animation's keyframes load into the **Animation Editor**. 4. Navigate to the **Media and Playback Controls** and click the **Play button** to play the avatar animation. --- title: "Animate heads" url: /docs/en-us/art/characters/facial-animation/animate-heads last_updated: 2026-06-29T19:33:55Z description: "You can create facial animations for a character with live heads using the Animation Editor." --- # Animate heads > **Info:** If you are looking to quickly test a character's head animation, use the [Avatar Setup tool](/docs/en-us/avatar-setup.md) to load your character and use the presets to preview various facial animations and head accessories. You can use the [Animation Editor](/docs/en-us/animation/editor.md) to animate supported heads in the following ways: - You can manually set values to each facial pose on separate [animation tracks](#use-animation-tracks). - You can use the [Face Animation Editor](#use-the-face-animation-editor) to access facial sliders that let you quickly create and save unique expressions on the timeline. - You can use the [Animation Capture for Faces](/docs/en-us/animation/capture.md#face) extension to automatically track and record facial movements with a webcam that converts your movement into keyframes on the timeline. In all methods, creating and combining multiple facial expressions over a duration of time results in a face animation. Since you cannot import face animations, these methods are the only ways to create and utilize them. After you create and publish a head that supports facial animation to Roblox, you can play the animation from a script or replace a character's default animation with your new animation. These animations can make your characters feel more expressive and full of life, such as playing a silly expression when a character walks, or an annoyed expression when a character is idle. ![A view of Studio where the Face Animation Editor, Animation Editor, and viewport are visible. The viewport shows a close up view of a blocky male avatar blinking with his left eye.](../../../assets/avatar/dynamic-heads/animating-dynamic-heads/Overview.png) ## Create animations After you have [opened](/docs/en-us/animation/editor.md#open-the-animation-editor) the **Animation Editor** and selected the character model with a head you want to create an animation for, you can either create a head animation by using animation tracks or the **Face Animation Editor**. For details on using Animation Capture to track facial movement as keyframes, see [Animation Capture - Face](/docs/en-us/animation/capture.md#face). ### Use animation tracks Similar to inserting other objects like `Class.MeshPart|MeshParts` or `Class.Bone|Bones` as animation tracks, you can manually add one FACS value at a time to the track list to manipulate a single body part, such as the character's eyes, jaw, or tongue. The [Animation Editor](/docs/en-us/animation/editor.md) represents FACS values as a percentage between 0 and 1, and these values map directly to `Class.FaceControls` values. While this process provides precise control over individual values, the [Face Animation Editor](#use-the-face-animation-editor) enhances this workflow and lets you quickly change values to multiple facial features at once on the animation timeline. To create an animation by inserting individual FACS values: 1. In the **track list** of the **Animation Editor**, click the **plus icon**. A contextual menu displays additional tracks you can add to the track list. 2. Hover over **Brows**, **Eyes**, **Jaw**, **Mouth**, or **Tongue**. A contextual menu displays all mapped FACS poses for that region of the face.![A close up view of the Animation Editor with the contextual menu and its submenu expanded. The plus icon is highlighted to signify how to open the contextual menu.](../../../assets/avatar/dynamic-heads/animating-dynamic-heads/FACS-Popup.jpg) 3. Select a FACS pose. An animation track for that FACS pose displays in the track list. 4. In the values input, enter a number between **0** and **1** to manipulate the body part into a new position. For example, if you change `LeftEyeClosed` to 1, the character's left eye closes.![A close up view of the track list in the Animation Editor. The values input is highlighted.](../../../assets/avatar/dynamic-heads/animating-dynamic-heads/Animation-Track-Values-Input.jpg) ### Use the Face Animation Editor The **Face Animation Editor** is an intuitive, visual way to automatically create keyframes as you adjust **sliders** to achieve your desired facial expression. For example, when you drag the thumb of the `LeftEyeClosed` slider all the way down, the character's left eye closes and a new animation track for `LeftEyeClosed` displays with a value of `1` within the track list. Some sliders affect multiple FACS values in the same region depending on the value of the slider you are adjusting. For example, when you adjust the thumb of the `LeftEyeClosed` slider all the way up to have a value of `0`, a new animation track for `LeftEyeUpperLidRaiser` displays in the track list with a value of `1`. This allows you to manipulate the eye from closed to open, while also raising the upper lid of the eye, all within a single slider. You can set multiple sliders to create a complete face expression, then create multiple expressions at different frames of the timeline to create complex animations. For example, you can combine `LeftEyeClosed` and `RightEyeClosed` together to have the character slowly blink, or `LeftLipCornerPuller` or `RightLipCornerPuller` for a smile on one side of the face. When you click and drag the thumb of the drag box between the eyes, you can control the direction the character's eyes look at. This creates several FACS property tracks in the track list depending on how you adjust the eyes. At the bottom-left of the panel, the **Face Animation Editor** also includes the following controls: | Control | Description | | --- | --- | | Symmetry Toggle | Lets you use a slider on one side of the face to also adjust the corresponding slider on the other side of the face to the same value. This is useful to create symmetric facial expressions, such as a full smile. | | Focus Face Toggle | Focuses the viewport on the face of the avatar without having to manually adjust the camera. This is useful when your camera is either far away from the avatar you want to animate, or if you are navigating from animating one avatar to another. | | Reset All Button | Resets all FACS pose values at your current frame position back to their default value of 0. This is useful when you want to quickly reanimate a single frame. | To create an animation for your head using the **Face Animation Editor**: 1. In the **track list** of the **Animation Editor**, click the **Face** button. The **Face Animation Editor** displays to the left of the track list.![A close up view of the track list in the Animation Editor. The Face button is highlighted.](../../../assets/avatar/dynamic-heads/animating-dynamic-heads/Face-Button.jpg) 2. In the **Face Animation Editor**, adjust sliders for the body parts you want to manipulate. Animation tracks for each facial part you manipulate automatically display in the track list along with keyframes for your current position in the timeline. The character's face also updates in the viewport. - To undo a step on a slider, press `Ctrl``Z` (`⌘``Z`). - To redo a step on a slider, press `Ctrl``Y` (`⌘``Y`). - To reset a slider to its default value, right click on the slider. A contextual menu displays. Select **Reset Selected**. 3. (Optional) Adjust the scrubber to a new position further along the timeline, then navigate back to the **Face Animation Editor** and adjust sliders to create a new facial expression. When you play the animation, the first facial expression will ease into the second facial expression. 4. When you are finished creating your animation, navigate to the **Media and Playback Controls** and click the **…** button. A pop-up menu displays. 5. Select **Save** or **Save As** to save the animation. The animation displays in the **Explorer** window as a child of the **AnimSaves** object (itself a child of the rig). ## Export animations When you export a head that supports animation to Studio, it becomes available for use in all of your experiences. This means that you only need to create a head animation once, then you can reuse it as many times for as many characters as you want as long as the character has an [animatable head](/docs/en-us/avatar/dynamic-heads.md). You can export head animations using the same workflow outlined for [exporting animations](/docs/en-us/animation/editor.md#export-an-animation). ## Script animations Once you have created an animation, you need to use a script to play it in your experience. Like generic animations, you can either play animations for heads manually from your scripts or automatically by replacing default animations for player characters. For more information, see [Use animations](/docs/en-us/animation/using.md). --- title: "Create basic heads" url: /docs/en-us/art/characters/facial-animation/create-basic-heads last_updated: 2026-06-29T19:33:55Z description: "The process of creating a basic animatable head in Blender." --- # Create basic heads > **Warning:****This guide covers advanced topics.** Before you begin, you should have prior knowledge on modeling, UV mapping, rigging, animation, and how to set up a character in [Blender](https://www.blender.org) or [Maya](https://www.autodesk.com/products/maya/overview). You can create or modify an existing model to support animated heads in a third-party modeling software, such as [Blender](https://www.blender.org) or [Maya](https://www.autodesk.com/products/maya/overview). When creating a head, your character model must meet the following requirements: - The model follows standard [modeling requirements](#modeling-requirements), and includes head geometry, such as eyes, a mouth, and teeth. - The model's head must include a [rig](#rigging), or internal bone structure. These bones drive the various deformation of vertices to create facial expressions. You can create a [control system](#add-controls) to simplify the posing process. - The model has facial poses [saved to the animation timeline](#pose) and [mapped to the head mesh](#map). Typical animatable heads include [50 standard base poses](/docs/en-us/avatar/dynamic-heads/facs-poses-reference.md) that allow for a diverse range of expressions. To meet these requirements, you can apply the steps in this guide when designing and posing your own head. This guide covers the basic processes of adding facial bones, posing, and mapping 5 basic FACS poses in Blender on a simplified reference character (Cubie), then exporting the model. > **Info:** For reference, this guide uses [Blender version 3.0](https://www.blender.org/download/releases/3-0/). If you are using another version of Blender, there might be minor differences in UI and settings. ## Reference files The following are head reference files, including all example files from this guide: > **Warning:** The reference character model provided is meant for educational purposes and does not meet the avatar character [technical specifications](/docs/en-us/avatar/character-bodies/specifications.md) for general use. | Reference files | Description | | --- | --- | | [Cubie-Model-Only.blend](../../../assets/avatar/dynamic-heads/creating-dynamic-heads/reference-files/Cubie-Model-Only.blend) | A Blender project file containing a R15 Cubie character model without facial data. | | [Cubie-Eye-Bones-Skinned.blend](../../../assets/avatar/dynamic-heads/creating-dynamic-heads/reference-files/Cubie-Eye-Bones-Skinned.blend) | A Blender project file containing the example Cubie character model with skinned eye bones without posing or mapping data. | | [Cubie-Eye-Poses-Mapped.blend](../../../assets/avatar/dynamic-heads/creating-dynamic-heads/reference-files/Cubie-Eye-Poses-Mapped.blend) | A Blender project file containing the Cubie character model with the 5 example poses saved and mapped. | | [Cubie-Complete.fbx](../../../assets/avatar/dynamic-heads/creating-dynamic-heads/reference-files/Cubie-Complete.fbx) | A Cubie character model with a fully rigged face and over 50 poses saved and mapped. You can import this file into Studio, or open the file in your preferred 3D modeling software. | | [Cubie-Texture_ALB.png](../../../assets/avatar/dynamic-heads/creating-dynamic-heads/reference-files/Cubie_Head_ALB.png) | A Cubie texture image file. After you import the Cubie model into Studio, you can add this file as the head part's **TextureID**. | | [Cubie-Complete.ma](../../../assets/avatar/dynamic-heads/creating-dynamic-heads/reference-files/Cubie-Complete.ma) | A Cubie Maya project provided as additional reference. | ## Modeling requirements Many character models already include a head with distinct facial features, but they might require minor modeling adjustments for head compatibility. When modeling a character with a head, ensure the head mesh meets the [Avatar character specifications](/docs/en-us/avatar/character-bodies/specifications.md) for Studio import, as well as the following requirements: - **Face parts** - Ensure that you include distinct face features, such as eyes, upper teeth, lower teeth, and a tongue. - **Lip vertices** - If you want your character to use its mouth, separate the lip vertices so that the mouth can open. - **Inner components** - If your character has inner mouth components, such as a tongue and teeth, model a mouth bag within the head mesh to contain these features. If your character uses eye sockets, model a similar eye bag to contain these features. - **No extra data** - Ensure that all children face parts of the Head_Geo don't contain history or frozen transformations. - **Outer cage** - Make sure the character model has an outer cage to support face accessories and layered clothing. For more information, see [Cage Mesh Requirements](/docs/en-us/avatar/character-bodies/specifications.md#inner-and-outer-cages). You can follow along the rest of this head creation process using a [rigged Cubie character](../../../assets/avatar/dynamic-heads/creating-dynamic-heads/reference-files/Cubie-Model-Only.blend) that meets these modeling requirements. This version doesn't include any facial rigging or pose data so you can use it as reference in this guide. ## Rigging Your character must have an internal bone structure to drive the vertices of the face geometry and make facial poses. Unlike [rigging a humanoid model](/docs/en-us/art/modeling/rig-a-humanoid-model.md), Studio doesn't require a specific bone hierarchy for a head. However, in order for the facial rig to work properly, the rig must include a [RootFaceJoint](#rootfacejoint) bone and [additional face bones](#face-bones). ### RootFaceJoint The **RootFaceJoint** is a bone that is parented under the standard R15 head bone. This root bone must parent all other face bones. In Blender, you can quickly add a bone by **extruding** a child bone from the head bone and then map the bone name as a property in the Head_Geo mesh. The RootFaceJoint bone object is commonly named "DynamicHead" in the reference templates and examples, but you can use any name as long as you [map the root bone](#map) in custom properties. To add a RootFaceJoint bone: 1. In Blender, open the **Cubie-Model-Only.blend** project. 2. Click on any bones to select the armature, then switch to **Edit Mode**. 3. In the **Outliner**, click on the **Head** bone. 4. In the **Viewport**, press `E` and drag your mouse up to extrude an additional child bone from the **Head** bone. 5. In the **Outliner**, rename this bone **DynamicHead**. You will reference this bone by name later in the [mapping](#map) section. > **Warning:** You must [map](#map) the RootFaceJoint so Studio can properly locate that joint and hide all of the children joints and their bones within the `Class.FaceControls` instance on import. ### Face bones Face bones drive the bending and deformation of the face geometry. Each poseable face feature of your head typically requires at least one bone. Complex features, such as eyes and mouth, might require several bones to make certain poses possible. The rigging and skinning process depends on the character model and differs between tools and modelers. It's important to plan out the full range of facial poses your character requires to avoid additional adjustments to your head bones later. The following instructions describe a basic process of [adding face bones](#add-face-bones) and [skinning](#skin-face-bones), or applying influences, to the reference model's **eyes** and **eyelids**. You can continue to apply this technique to the rest of the facial features that need articulation, such as the character's mouth, cheeks, and jaw. After adding your bones and applying influences, you can create [controls](#add-controls) that can help make the next posing process more efficient. > **Info:** You can download a [reference Cubie model](../../../assets/avatar/dynamic-heads/creating-dynamic-heads/reference-files/Cubie-Complete.fbx) with a completed rig with 30+ facial bones added and skinned. You can use this for a reference for unique bone placements not covered in this guide, such as cheeks, lips, and jaws. > > See [Test heads in Studio](/docs/en-us/avatar/dynamic-heads/test.md) for download links of more complex characters. #### Add face bones The specific head bones your character requires depend on the poses you intend for it to use. The following examples cover the process for adding 1 bone for each eye and 4 bones for the eyelids to allow for blinking, winking, and gaze direction. When creating a face rig, use less than 50 face bones where possible and keep the bones organized and specific for their associated facial features. In general, high numbers of vertices and joints can impact your experience's performance. ##### Eyes Each eye requires one bone each, though you might opt to create a controller bone later that can control both eyes at the same time. To ensure that you position your bones symmetrically, you can enable **X-Axis Mirror** in the top-right of the Viewport. This moves bones that share the same name with opposing suffixes along the X-axis. To add bones to the character eyes: 1. In **Object Mode**, select the **armature** and switch to **Edit Mode**. 2. Click **Add**, then **Single Bone**. Do this twice. 3. Rename one bone **LeftEye**, and rename the other **RightEye**. 4. Enable **X-Axis Mirror**. 5. Position the left or right bone centered at the model's eyes. 6. Adjust the bones horizontally and front-facing. A quick method for setting bones horizontally: 1. In the top right of the **Viewport**, extend the **Tool Panel** to the **Transform** section. 2. With the bones selected, copy the **Head Y** location to the **Tail Y** location. 3. Set the **Tail Z** to **0.2** centimeters. This sets the Tail Z coordinates 0.2 centimeters offset from the Head Z coordinates. 7. Hold `Shift` and click both **Eye** bones. 8. While still holding `Shift`, click the **Face** bone. 9. Right-click in the viewport. A contextual menu displays. 10. Select **Parent**, **Make**, then **With Offset**. ##### Eyelids When adding bones, you can quickly create bones parented under the Face bone by selecting existing facial bones and duplicating them. This automatically creates bones parented under the Face bone. To add bones to the character eyelids: 1. In **Edit Mode**, hold `Shift` and click both **eye bones** to select them. 2. Press `Shift``D` and click to duplicate the bones in the same position. Do this four times to create 8 total new bones. 3. With **X-Axis mirror enabled**, position each bone at the corners of the eyes. 4. In the **Outliner**, rename all bones to reflect their specific position. Use the following names: 1. LeftUpperOuterEyelid 2. RightUpperOuterEyelid 3. LeftLowerOuterEyelid 4. RightLowerOuterEyelid 5. LeftUpperInnerEyelid 6. RightUpperInnerEyelid 7. LeftLowerInnerEyelid 8. RightLowerInnerEyelid #### Skin face bones You can apply [skinning](/docs/en-us/art/modeling/rigging.md) to a character rig using several methods. The following example uses Blender's **Weight Paint** mode to paint which vertices a single bone can control. Skinning is typically a time consuming step for complex characters and a background in skinning and facial posing is recommended. When applying detailed or shared influences for complex models, it's recommended to enable [Auto Normalize](/docs/en-us/art/modeling/skin-a-simple-mesh.md#auto-normalize) to prevent influence conflicts between bones. > **Info:** When parenting bones to the character using **Automatic Weights**, some of your vertices might already have influences applied. Always test your influences to make sure influence assignments are correct. ##### Eyes Both eyes in the example project require full influences on the pupil within the eye geometry. Depending on the design of the eyes, you might need to adjust influences to create realistic eye poses. To add influence to the LeftEye: 1. In **Object Mode**, click the head mesh, then hold `Shift` and click any bone to select the armature. 2. Switch to **Weight Paint Mode**. 3. Toggle to **X-Ray viewing mode** or **Material Preview Mode** to better visualize the vertices. 4. Hold `Shift` and click the **LeftEye** bone to select it. The name of the currently selected bone displays on the top left of the Viewport. 5. Paint influence on the vertices within the eye. For this example, ensure that the pupils are red, or fully influenced by the selected bone. 6. Switch to **Object Mode**, then click on any bone to select the armature. 7. Switch to **Pose Mode** with the armature selected. 8. Test the influences of the eye bone by grabbing the bone and moving it. The mesh below should follow. 9. Repeat these steps for the right eye. ##### Eyelids Eyelid bones require their own separate influences. While the example poses only manipulate the top eyelids, being able to control the bottom corners of the eyelids is important for other poses, such as `LeftCheekRaiser` and `RightCheekRaiser`. To add influence to the left eyelid: 1. In **Object Mode**, click the head mesh, then hold `Shift` and click any bone. 2. Switch to **Weight Paint Mode**. 3. Switch to **X-Ray viewing mode** or **Material Preview Mode** to better visualize the vertices. 4. Paint influences on the closest vertices to the bone. 5. When complete, hold `Shift` and click another bone to begin applying influences to the closest vertices. Perform this step for each eyelid bone. 6. Switch to **Object Mode**, select the character mesh, then switch to **Pose Mode** to test. You can download [a version of this project](../../../assets/avatar/dynamic-heads/creating-dynamic-heads/reference-files/Cubie-Eye-Bones-Skinned.blend) with the bones skinned up to this point as reference. #### Add controls Since creating heads requires saving several poses consecutively, while not required, controls can help you pose your bones quickly and effectively. You can create controls by adding external controller bones that influence the internal ones, making it easier to access and pose as well as reset to a neutral transformation. You can create controls in many different ways. The following example uses **Bone Constraints** and simple geometrical shapes included in the reference Blender project to quickly set up easy-to-access controller bones for the character's facial bones. For more information on rigging and constraints, see Blender's documentation for additional details on [character rigging](https://www.youtube.com/watch?v=-gIL6VZ-bkE) and [object constraints](https://www.youtube.com/watch?v=fx33sPEAZEk). > **Info:** Some facial features, such as lips, jaws, and tongue, might require a single controller bone that controls several facial bones at once. See the complete reference file for an example of this type of implementation. To start creating face bone controls: 1. In **Object Mode**, select the Joints armature and press `Shift``D` to duplicate the Joints armature object. 2. Rename the new armature to **Controller**. 3. In **Edit Mode**, select all non-face bones, right click and select **Delete Selected Bones**. 4. Rename the bones to include **_Con**, to denote them as controller bones. You can batch rename them with the following process: 1. In the **Outliner**, hold `Shift` and click all the controller bones. 2. Navigate to **Edit** > **Batch Rename**. A rename modal displays. 3. Set to **Selected** and use the dropdown to set to **Bones**. 4. Set **Type** to **Set Name**. 5. Set **Method** to **Suffix**. 6. In **Name**, add **_Con**. 7. Click the **OK** button. 5. Switch to **Object Mode** and select the original **Joints armature** object. 6. Switch to **Pose Mode**. 7. Click on the original joint bones, such as **LeftEye**, then navigate to the **Bone Constraints Property Panel**. 8. Add **Copy Location** and **Copy Rotation**, then set each original bone's constraint to target their corresponding Controller Bone, such as **LeftEye_Con**. 9. In the **Copy Rotation** constraint, set the following properties: 1. In **Axis**, disable the **X**, **Y**, and **Z**. 2. In **Inverted Axis**, enable the **X**, **Y**, and **Z**. 3. Set **Mix** to **Offset** (Legacy). 10. Perform steps 7-9 for each facial bone that requires a controller. To create custom bone shapes for easier control bone access: 1. Switch to **Object Mode** and select the **Controller** armature object. 2. Switch to **Pose Mode**. 3. Select a controller bone, such as **LeftEye_Con**. 4. Switch to the **Bones Properties** panel and expand the **Viewport Display** section. 5. Select one of the custom shapes included in the project. In the reference project, the shape objects share similar names to the face bones. 6. Set the **X** rotation to **-90**. Set any additional scaling if required. 7. Perform steps 3-6 for all your controller bones. You can hide the original face bones at this point. 8. (Optional) Set up **Bone Groups** to assign colors to your controller bones: 1. Navigate to the **Object Data Properties** panel. 2. Create a new bone group. 3. Change **Color Set** to a color theme of your choice. 4. Click **Assign** to assign the currently selected controller that color. You can perform this step in batches. 9. Test your controllers in **Pose Mode**. ## Pose **Posing** is the process of manipulating the bones of your head mesh into a specific position per animation frame. After the model's head has been rigged, you can begin the process of saving poses to the timeline. This data enables Studio to access each facial movement and animate or blend facial poses to create dynamic expressions. When posing the bones of your character's head mesh to new positions, follow the [Facial Action Coding System](/docs/en-us/avatar/dynamic-heads/facs-poses-reference.md) (FACS) as a reference for your facial expression poses. FACS is a comprehensive, anatomically-based system for describing all visually discernible facial movement, and it allows for all facial animations to be shareable between characters. This means that once you create a facial animation, you can reuse it for any character with an [animatable head](/docs/en-us/avatar/dynamic-heads.md) with a `Class.FaceControls` instance. Each frame within your modeling software's animation timeline can contain one unique FACS pose, so when you want to create multiple FACS poses, you must save each FACS pose to a different frame. You must also include a frame with your character having a neutral face with the face controllers and bones set to their default values. This ensures that Studio can calculate the bone position differences between your character's neutral expression and each FACS pose. For this reason, set Frame 0 as your character's neutral expression, and save FACS poses starting at Frame 1. The following image is an example of Blender's animation timeline with 5 frames. Frame 0 has the character's neutral expression, and frames 1-4 have FACS pose data. There are [50 base poses](/docs/en-us/avatar/dynamic-heads/facs-poses-reference.md) that you can use in Roblox to portray a wide range of face emotions for your characters. When you are deciding which poses you need, remember that FACS pose names are always based on the orientation of the character, not the camera. For example, `LeftEyeClosed` closes the character's left eye, which is to the right of the camera view. > **Warning:** If you intend to sell your character head to the Marketplace, you must include the minimum 17 base [FACS poses](/docs/en-us/avatar/dynamic-heads/facs-poses-reference.md). You might not require all 50 base poses for your character. For example, a simple robot that opens its mouth and blinks can just have `JawDrop`, `LeftEyeClosed`, and `RightEyeClosed`. Therefore, the more expressive you want your character to be, the more FACS poses you need to include in your animation timeline. It's recommended to save the base poses that you intend to use with your head in alphabetical order, then use any frames afterwards for [combination poses](#combination-poses). The following steps outline the process of posing 5 poses with the facial bones created in our reference, but you can apply these steps for any additional poses for a more expressive head. To pose your face bones in Blender: 1. Ensure that the animation timeline playhead is set to the correct frame. 1. If you are setting the character's **Neutral pose**, set it to **frame 0**. 2. If you are posing `EyesLookLeft`, set it to **frame 1**. 3. If you are posing `EyesLookRight`, set it to **frame 2**. 4. If you are posing `LeftEyeClosed`, set it to **frame 3.** 5. If you are posing `RightEyeClosed`, set it to **frame 4**. 2. In **Pose Mode**, set the pose to the maximum position you want your pose to realistically use: 1. If you are setting the character's `Neutral` pose, set the face controllers and bones to their default values, 2. If you are posing `EyesLookLeft`, select both eye controller bones and drag the eyes to the character's left. 3. If you are posing `EyesLookRight`, select both eye controller bones and drag the eyes to the character's right. 4. If you are posing `LeftEyeClosed`, select both eye controller bones and drag the left eyelids down to meet the bottom eyelids. 5. If you are posing `RightEyeClosed`, select both eye controller bones and drag the right eyelids down to meet the bottom eyelids. 3. In the **Viewport**, press `A` to select all bones. 4. Right-click and select **Insert Keyframe** > **Location and Rotation**. This ensures that each frame contains the positional and rotational information for all bones. When all the poses are saved in your timeline, set the Start and End of the animation timeline to represent the number of frames with saved poses. Always set **Start** to **0** and, in this specific example, you can set the **End** to **4** if you are only mapping the 4 non-neutral example poses. ### Combination poses You can combine 2-3 base FACS poses in a single animation frame to display complex facial expressions. However, when you combine FACS poses that control the **same** facial regions, the facial features might either collide or disfigure the character. For example, both `LeftEyeClosed` and `LeftCheekRaiser` control movement around the character's left eye: `LeftEyeClosed` closes the eye, and `LeftCheekRaiser` lifts the cheek up and pushes the lower eyelid up, causing a squint-like effect. When you combine both poses with one or more at 100% of their values, the lower eyelid collides with the upper eyelid: _Both FACS poses concurrently ease to 100% of their default values._ _LeftEyeClosed eases to 100% of its default value, and LeftCheekRaiser starts at 100%._ _LeftCheekRaiser eases to 100% of its default value, and LeftEyeClosed starts at 100%._ A **combination pose**, or **corrective**, is the combination of 2-3 FACS poses that control the same facial features in a single animation frame with a **corrective difference from 100% of their default values**. By defining and mapping a combination pose to your head, you can correct how you want the two or more FACS poses to combine. For example, if you add a corrective for each of the previous use cases, the lower and upper eyelids make contact with each other without colliding: _Using a corrective pose, both FACS poses concurrently ease to 100% of their default values._ _LeftEyeClosed eases to 100% of its default value, and LeftCheekRaiser starts at 100%._ _LeftCheekRaiser eases to 100% of its default value, and LeftEyeClosed starts at 100%._ On import, Studio calculates and stores the corrective difference for combination poses in the head's `Class.FaceControls` instance, and the `Class.FaceControls` instance corrects the base poses values as they combine in the [Animation Editor](/docs/en-us/animation/editor.md). ## Map After you finish posing each FACS pose that your character needs, you must map, or link, **each animation frame that you pose** to its corresponding FACS base or combination pose name. Mapping stores the bone positions and translations within the head `Class.MeshPart`, and when you begin to animate your head within the **Animation Editor**, the `Class.FaceControls` instance uses this stored data to transform your character's facial features to the applicable FACS pose. > **Warning:** If you intend to sell your character head to the Marketplace, you must include the minimum 17 base [FACS poses](/docs/en-us/avatar/dynamic-heads/facs-poses-reference.md). Aside from mapping each pose to its proper pose name, you also need to map the [RootFaceJoint](#rootfacejoint) so that Studio can properly locate that joint and hide all of the children joints and their bones within the `Class.FaceControls` instance on import. > **Info:** If you are using Maya, you can map the RootFaceJoint and facial pose data using the **Extra Attributes** in a similar format as Blender's Custom Properties. To add attributes: 1. Select the **Head_Geo** object and navigate to the Attribute Editor. 2. Under the Attributes dropdown, select **Add Attributes...**. > **Error:** If you leave any empty strings, or try to import the head into Studio with multiple frames per FACS pose name, the head will fail the [import process](/docs/en-us/avatar/dynamic-heads/test.md). To map your saved poses and the RootFaceJoint: 1. Switch to **Object Mode**. 2. Select the **Head_Geo mesh**. 3. In the **Object Properties** tab of the **Properties Editor**, navigate to the **Custom Properties** section, then click the **New** button. A new custom property displays underneath the **New** button. 4. To the right of the new custom property, click the **Gear Icon**. The **Edit Property** pop-up displays. 5. Click the **Type** dropdown, then select **String**. 6. In the **Property Name** field: 1. If you are mapping a pose, input the **frame number** you are mapping, **Frame0** for example. 2. If you are mapping the RootFaceJoint, input **RootFaceJoint**. 7. Leave the **Default Value** and **Description** fields empty. 8. Click the **OK** button. The new custom property updates with your new property name. 9. In the field to the right of the custom property name: 1. If you're mapping a base pose, input the corresponding [FACS base pose](/docs/en-us/avatar/dynamic-heads/facs-poses-reference.md) or combination pose name exactly as it is spelled. 2. If you're mapping a combination pose, input each base pose you are combining separated by an underscore, such as **Funneler_JawDrop_Pucker**. 3. If you're mapping the RootFaceJoint, input the name of the face root bone, commonly **DynamicHead**. 10. Press **Enter**. As you repeat this process, every additional custom property you create displays in the **Custom Properties** section of the **Object Properties** tab within the **Properties Editor**. You can download a [version of this project](../../../assets/avatar/dynamic-heads/creating-dynamic-heads/reference-files/Cubie-Eye-Poses-Mapped.blend) with the poses saved and mapped up to this point as reference. If importing a head `.fbx` with saved animation data into Blender, make sure to set the **Animation Offset** in the .FBX Import window to **0** to include the Frame 0 of the timeline. ## Export After you finish posing and mapping your head for your character, you can export the character model as a `.fbx` to import into Studio, allowing you to access the 4 eye poses using the `Class.FaceControls` instance in Studio. You can also reference the fully configured Cubie head `.fbx` to access all 50+ base poses. The export settings for animatable heads differ slightly from [standard third-party modeling export settings](/docs/en-us/art/modeling/export-requirements.md). To export the basic head model as a `.fbx`: 1. In the topbar, click **File**. A pop-up menu displays. 2. Select **Export**, then **FBX (.fbx)**. The **Blender File View** window displays. 3. Expand **Include** and enable **Limit To > Active Collection**. Note that this step is optional if you do not have additional collections in your Blender project. 4. In the **Include** section, enable **Custom Properties**. 5. Expand the **Armature** section and uncheck **Add Leaf Bones**. 6. Enable **Bake Animation**. 7. Expand **Bake Animation** and uncheck **NLA Strips**, **All Actions**, and **Force Start/End Keyframes**. 8. Click the **Export FBX** button. Save the FBX to the directory of your choice. At this point, you can now import the `.fbx` into Studio as a character with a supported animatable head. For model import and usage instructions, see [Test heads in Studio](/docs/en-us/avatar/dynamic-heads/test.md). > **Warning:** When importing Cubie into Studio, make sure to also save the head texture [Cubie-Texture_ALB.png](../../../assets/avatar/dynamic-heads/creating-dynamic-heads/reference-files/Cubie_Head_ALB.png) which you can apply to the head mesh of the imported character as a TextureID. --- title: "Create face accessories" url: /docs/en-us/art/characters/facial-animation/create-face-accessories last_updated: 2026-06-29T19:33:55Z description: "Face accessories are 3D accessories that move and animate with a live head." --- # Create face accessories > **Warning:****This guide covers advanced topics.** Before you begin, you should have prior knowledge on modeling, UV mapping, rigging, animation, and how to set up a character in [Blender](https://www.blender.org) or [Maya](https://www.autodesk.com/products/maya/overview). A face accessory is a cosmetic `Class.Accessory` that attaches to the head and can include items such as hair, eyebrows, glasses, and facial hair. To create a face accessory that is compatible with animatable heads, you use a similar design process as [layered clothing](/docs/en-us/avatar/layered-accessories.md#creation-process) to allow your accessory model to deform and stretch on a head when the head is posed or animated. To create a face accessory, use a third-party modeling tool, such as [Blender](https://www.blender.org) or [Maya](https://www.autodesk.com/products/maya/overview), to create a 3D model with the following requirements: - The accessory model must meet Studio's [mesh requirements](/docs/en-us/avatar/character-bodies/specifications.md). - The model must be parented (Blender) or bound (Maya) to an R15 character rig. - The model must include an inner and outer cage. This guide covers the basic process in Blender for applying rigging and cage data to a basic reference model using the Cubie model referenced in [Create basic heads](/docs/en-us/art/characters/facial-animation/create-basic-heads.md). > **Info:** This guide uses [Blender version 3.0](https://www.blender.org/download/releases/3-0/). If you are using another version of Blender, there might be minor differences in UI and settings. ## Reference files The following are face accessory reference files, including all example files from this guide: > **Warning:** The reference character model provided is meant for educational purposes and does not meet the avatar character [technical specifications](/docs/en-us/avatar/character-bodies/specifications.md) for general use. | Name | Description | | --- | --- | | [Cubie-Complete.fbx](../../../assets/avatar/dynamic-heads/creating-dynamic-heads/reference-files/Cubie-Complete.fbx) | The complete Cubie reference character, from [Creating Basic Heads](/docs/en-us/art/characters/facial-animation/create-basic-heads.md). This file is ready for import into Studio. | | [Cubie-Cage-Only.fbx](../../../assets/avatar/dynamic-heads/creating-face-accessories/reference-files/Cubie-Cage-Only.fbx) | The full body [cage mesh](/docs/en-us/avatar/layered-accessories/project-files.md#caging-meshes) of the Cubie reference character. This includes the inner and outer cage meshes. | | [Cubie-Eyebrow-Geo.fbx](../../../assets/avatar/dynamic-heads/creating-face-accessories/reference-files/CubieEyebrow_Geo.fbx) | A standalone eyebrow model, designed for the Cubie model. | | [Cubie-Eyebrow-Rigged-And-Caged.fbx](../../../assets/avatar/dynamic-heads/creating-face-accessories/reference-files/Cubie-Eyebrow-Rigged-And-Caged.fbx) | The Cubie eyebrow model, correctly rigged and caged following the instructions in this guide. This file is ready for import into Studio. | | [Cubie-Hawk-Hair.fbx](../../../assets/avatar/dynamic-heads/creating-face-accessories/reference-files/CubieHawkHair_Geo.fbx) | A hair model reference, designed for the Cubie model. | | [Creating-Face-Accessories-Reference-Files.zip](../../../assets/avatar/dynamic-heads/creating-face-accessories/reference-files/Creating-Face-Accessories-Reference-Files.zip) | A collection of all the provided reference files. | ## Modeling This guide uses a simple eyebrow reference as a demonstration for creating a face accessory. If rigging and caging a different model, ensure that your model meets the [character specifications](/docs/en-us/avatar/character-bodies/specifications.md) for Studio import. If creating your own model, consider the following guidelines: - Model your accessory with your character model for best results and fit. - When possible, try to match up the edges of your model geometry with the edges/vertices of the underlying head topology. This improves the deformation of the accessory with the underlying head model. - Some accessories, like eyebrows, can extrude or slightly intersect the character model mesh to achieve a certain cosmetic look. - When working with an accessory with multiple pieces, such as eyebrows, combine the meshes to a single mesh once the modeling and fitting is completed. - Face accessory meshes do not require a **_Geo** naming convention. ## Rigging You must [rig](/docs/en-us/art/modeling/rigging.md) your accessory to the character's bone structure so the accessory can bend and deform along with your character's facial poses. After rigging, you can skin your model in your modeling tool, or you can transfer skinning data from your character to the accessory at runtime by using [Automatic skinning transfer](/docs/en-us/avatar/automatic-skinning-transfer.md). ### Project setup As an example in this guide, we are using the completed [basic Head model](../../../assets/avatar/dynamic-heads/creating-dynamic-heads/reference-files/Cubie-Complete.fbx) from [Create a basic head](/docs/en-us/art/characters/facial-animation/create-basic-heads.md) and a [simple eyebrow model](../../../assets/avatar/dynamic-heads/creating-face-accessories/reference-files/CubieEyebrow_Geo.fbx) in a new Blender project. To set up your Blender project: 1. Open a new **General** project in Blender. 2. Select the default shape, camera, and lights, then press `Delete`. 3. Import the character rig you intend to parent the accessory to, in this example: [Cubie-Complete.fbx](../../../assets/avatar/dynamic-heads/creating-dynamic-heads/reference-files/Cubie-Complete.fbx). 4. To simplify the workspace, you can delete the R15 inner and outer cage mesh objects since you will later import a full-body cage in the [Caging](#caging) step. 5. Import your accessory model, in this example: [CubieEyebrow_Geo.fbx](../../../assets/avatar/dynamic-heads/creating-face-accessories/reference-files/CubieEyebrow_Geo.fbx). 1. If required, reposition the accessory model on the face. 2. You can hide the armature temporarily to verify model placement. ### Parent armature Connect the mesh object to the character's armature by parenting the armature to the mesh object. To parent the armature: 1. In Object Mode, hold `Shift` and **click** the accessory model and then any of the character bones. 2. Right click and select **Parent**, then select **With Automatic Weights**. > **Warning:** Parenting with Automatic Weights automatically applies some influences to your model which can save some time during the [optional skinning](#optional-skinning) step. You can alternatively **Parent** with **Empty Weights** to not apply any skinning influence to your accessory mesh. See Blender's documentation on [Automatic Weights](https://docs.blender.org/manual/en/latest/animation/armatures/skinning/parenting.html#with-automatic-weights) for more information. ### Optional skinning In many cases, you can skip the [skinning](/docs/en-us/art/modeling/rigging.md) process for your accessory and use Roblox's [automatic skinning transfer](/docs/en-us/avatar/automatic-skinning-transfer.md) instead. You can still apply manual skinning through a modeling software and opt to use automatic skinning transfer later. With automatic skinning transfer, your end result and quality may vary. If you do not intend to apply skinning manually, continue directly to [caging](#caging). > **Info:** If you are skinning your accessory in your modeling software on a character model with a fully posed head, you can test the accessory on various FACS poses saved to the timeline within your modeling software before importing into Studio. ## Caging The caging process for face accessories is similar to caging layered clothing accessories and shares the same modeling and caging requirements. After rigging, import a full-body inner and outer cage to your project, rename the cages, then stretch the vertices of the Outer Cage to cover the accessory model with minimal space. To cage the eyebrow accessory: 1. With Blender's .fbx importer, import [`Cubie-Cage-Only.fbx`](../../../assets/avatar/dynamic-heads/creating-face-accessories/reference-files/Cubie-Cage-Only.fbx). This includes a single full-body inner and outer cage mesh for the Cubie model. 2. Rename cages to begin with "CubieEyebrow" before the **_InnerCage** and **_OuterCage** affix. 3. In Edit mode, extend the outer cage to fit over the accessory with minimal space. Use various material and viewing options to easily manipulate the correct vertices on the outer cage. If you are using automatic skinning transfer, you can ensure that automatic skinning transfer only applies to specific parts of the accessory by removing unnecessary sections of the cage. For more information, see [Automatic Skinning Transfer - Modify character cages](/docs/en-us/avatar/automatic-skinning-transfer.md#modify-character-cages). ## Export Export your model when ready to test your accessory model in Studio or when setting up for final export. When exporting face accessories, keep in mind the following guidelines: - Ensure that the final accessory model follows [Studio's Modeling Requirements](/docs/en-us/avatar/character-bodies/specifications.md), including properly named mesh and cage objects. - Do not export any unnecessary data, such as animation data, or light and camera objects. - If exporting PBR textures, follow [texture modeling requirements](/docs/en-us/avatar/character-bodies/specifications.md#surfaceappearance) when exporting texture images from your texture software. To export: 1. Ensure only the **accessory mesh**, **armature object** and **cages meshes** are exported. Delete all other objects in the workspace. 1. You can quickly filter out **Geo** and **Att** named objects in your workspace to quickly delete them. 2. Follow Studio's [Export Requirements for Blender](/docs/en-us/avatar/rigid-accessories/export.md) and save the file to your preferred location. The final export of the eyebrow `.fbx` is available for reference. ## Test in Studio To use your exported model into Studio as an `Class.Accessory`, use the [Accessory Fitting Tool](/docs/en-us/avatar/accessory-fitting-tool.md) to test and generate the accessory object. At this point, you can equip the accessory to a humanoid character. If you intend to transfer skinning data from your character to the accessory model at runtime, you can enable [automatic skinning transfer](/docs/en-us/avatar/automatic-skinning-transfer.md) for your accessory. > **Info:** If following the references provided by this guide, only **EnabledOverride** applies the skinning transfer in Studio, since some skinning data will have been applied to the model during the [parenting](#parent-armature) process. --- title: "Moods" url: /docs/en-us/art/characters/facial-animation/moods last_updated: 2026-06-29T19:33:55Z description: "Moods are a type of facial animation that loop indefinitely, allowing users to express persistent facial emotion." --- # Moods A **mood** is a type of facial animation for [animatable heads](/docs/en-us/avatar/dynamic-heads.md) that loops indefinitely, allowing users to express themselves and react to others with a persistent facial emotion. Moods play simultaneously with other character [default animations](/docs/en-us/animation/using.md#default-character-animations), such as walking, climbing, and swimming, and if the default animation has a facial animation, the default animation blends with the character's mood. _Default mood_ _Open mouth mood_ > **Info:** A mood is a type of facial animation, but not all facial animations are moods. While a mood refers to a specific animation slot that belongs to each character, a facial animation refers to **any** animation that modifies the face channels. ## Create moods If you have a character model with an animatable head, you can create any mood animation you can think of using the [Face Animation Editor](/docs/en-us/art/characters/facial-animation/animate-heads.md#use-the-face-animation-editor). If you don't want to use the [Blocky](../../../assets/avatar/dynamic-heads/reference-files/BlockyCharacter.fbx) or [Goblin](../../../assets/avatar/dynamic-heads/reference-files/GoblinCharacter.zip) reference character models, you can create or modify an existing model to support animated heads in a third-party modeling software, such as Blender or Maya. For information on how to create an animatable head, see [Create basic heads](/docs/en-us/art/characters/facial-animation/create-basic-heads.md). To create a mood: 1. Add a character model with an animatable head to the viewport. 2. Open the **Face Animation Editor**. 1. From the toolbar's **Avatar** tab, click **Clip Editor**. The [Animation Editor](/docs/en-us/animation/editor.md) window displays. 2. In the viewport, select your character model with an animatable head. 3. In the editor's [track list](/docs/en-us/animation/editor.md#interface), click the **Face** button. The **Face Animation Editor** displays to the left of the track list. 3. In the **Face Animation Editor**, adjust sliders for the facial parts you want to manipulate. Animation tracks for each facial part you manipulate automatically display in the track list along with keyframes for your current position in the timeline. The character's face also updates in the viewport. - To undo a step on a slider, press `Ctrl``Z` (`⌘``Z`). - To redo a step on a slider, press `Ctrl``Y` (`⌘``Y`). - To reset a slider to its default value, right click on the slider. A contextual menu displays. Select **Reset Selected**. 4. When you are finished creating your animation, navigate to the **Media and Playback Controls** and click the **…** button. A pop-up menu displays. 5. Select **Save** or **Save As** to save the mood animation. The animation displays in the **Explorer** window as a child of the **AnimSaves** object (itself a child of the rig). 6. **(Optional)** To assign an asset ID to your mood animation and save it to the [Toolbox](/docs/en-us/projects/assets/toolbox.md) to use across your experiences, 1. In the **Explorer** window, right-click on your new mood animation. A contextual menu displays. 2. Select **Save to Roblox**. The **Asset Configuration** window displays. 3. Fill in the following fields: - **Title**: A name for your plugin. - **Description**: A description that describes what a potential user should expect the plugin to do. - **Creator**: The creator you'd like to attribute as the creator of the plugin. 4. Click the **Submit** button. After a moment, the Asset Configuration dialog displays your mood's `Class.Animation.AnimationID` that you can use to set the mood to characters within your experiences. ## Set moods Every character with an animatable head has a child **Animate** `Class.LocalScript` with a child **mood** `Class.StringValue` that contains the mood animation that plays on the character's head. The mood animation's default `Class.Animation.AnimationID` plays a smiling animation, but you can change the character's mood to something else by either directly editing the `Class.Animation.AnimationID` within the mood `Class.StringValue`, or using the `Class.HumanoidDescription` system. > **Warning:** The code for playing moods doesn't occur in the **Animate** `Class.LocalScript`, but in a hidden, internal Roblox script. While you can't edit this internal script, the **mood** `Class.StringValue` allows you to interact with it in order to customize moods within your experiences. ### Edit AnimationIds You can set a specific mood for each character within your experience by editing their mood's `Class.Animation.AnimationID` whenever a user triggers an event. For example, the following `Class.Script` edits any previously set mood to an animation that [opens the character's mouth](https://www.roblox.com/library/7715145252/moods-11-FaceAnimation) as soon as the user enters the experience: ```lua local Players = game:GetService("Players") local function onCharacterAdded(character) local humanoid = character:WaitForChild("Humanoid") local animateScript = character:WaitForChild("Animate") animateScript.mood.Animation1.AnimationId = "rbxassetid://7715145252" -- Mood end local function onPlayerAdded(player) player.CharacterAppearanceLoaded:Connect(onCharacterAdded) end Players.PlayerAdded:Connect(onPlayerAdded) ``` ### Use the HumanoidDescription You can also use the `Class.HumanoidDescription` system to find user characters and edit their `Class.Animation.AnimationID|AnimationIDs` for any default animation. For example, the following `Class.Script` edits any previously set mood to an animation that gives the character a [half-smile](https://www.roblox.com/catalog/10725833199/Chiseled-Good-Looks-Mood) on the left-side of their face whenever their character is idling: ```lua local humanoid = player.Character and player.Character:FindFirstChildWhichIsA("Humanoid") if humanoid then local descriptionClone = humanoid:GetAppliedDescription() descriptionClone.IdleAnimation = 10725833199 -- Apply modified "descriptionClone" to humanoid humanoid:ApplyDescription(descriptionClone) end ``` ## Disable moods To disable moods from your experience, you can delete the mood object underneath the **Animate** `Class.LocalScript`. For example, the following `Class.Script` removes every character's **mood** `Class.StringValue` as soon as they join the experience: ```lua local Players = game:GetService("Players") local function onCharacterAdded(character) local humanoid = character:WaitForChild("Humanoid") local animateScript = character:WaitForChild("Animate") animateScript.mood:Destroy() end local function onPlayerAdded(player) player.CharacterAppearanceLoaded:Connect(onCharacterAdded) end Players.PlayerAdded:Connect(onPlayerAdded) ``` --- title: "Classic and Dynamic head comparison" url: /docs/en-us/art/characters/head-comparison last_updated: 2026-06-29T19:33:55Z description: "The Avatar Validation Tool checks for common character model issues in Blender." --- # Classic and Dynamic head comparison The following table compares the classic decals with the dynamic head equivalent. > **Warning:** The references below are not final and are subject to change. We encourage you to provide feedback on specific heads on the [DevForum announcement](https://devforum.roblox.com/t/completing-the-dynamic-head-migration/4301387). | **Asset Name** | **Classic** | **Dynamic** | | --- | --- | --- | | **$.$** | | | **-_-** | | | **8-Bit Heart Face** | | | **:'(** | | | **:-/** | | | **:-?** | | | **:-[** | | | **:-o** | | | **:/** | | | **:D** | | | **:P** | | | **=)** | | | **>_<** | | | **>_>** | | | **^_^** | | | **Adorable Puppy** | | | **Adoration** | | | **Aghast** | | | **Alexandra Ninniflip - Face** | | | **Alien** | | | **Alien Ambassador** | | | **Alright** | | | **Amelia Face** | | | **And then we'll take over the world!** | | | **Angelic** | | | **Angry Zombie** | | | **Anguished** | | | **Anime Surprise** | | | **Astral Isle Clan: Windsor the Blue - Face** | | | **Atlantean Warrior Face** | | | **Aurora Spark - Face** | | | **Awesome Face** | | | **Awkward Eyeroll** | | | **Awkward Grin** | | | **Bad Dog** | | | **Bad News Face** | | | **Bakonette** | | | **Ballerina Face** | | | **Bandage** | | | **Battle Ready Kenji Face** | | | **Beast Mode** | | | **Beekeeper - Face** | | | **Beekeeper - Face** | | | **Big Grin - Tai Verdes** | | | **Big Sad Eyes** | | | **Biohazard First Responder - Face** | | | **Bird of Prey** | | | **BiteyMcFace** | | | **Blerg!** | | | **Bling** | | | **Blinky** | | | **Blizzard Beast Mode** | | | **Blue Amazeface** | | | **Blue Bubble Trouble** | | | **Blue Eyeroll** | | | **Blue Galaxy Gaze** | | | **Blue Goof** | | | **Blue Moonstruck** | | | **Blue Rock Star Smile** | | | **Blue Starface** | | | **Blue Starry Sight** | | | **Blue Trance** | | | **Blue Ultimate Dragon Face** | | | **Blue Wistful Wink** | | | **Blue-eyed Awesome Face** | | | **Bluffing** | | | **Bored** | | | **Braces** | | | **Bubble Trouble** | | | **Butterfly** | | | **C.Y.N.D.I - Face** | | | **Camoface** | | | **Casey's Face** | | | **Cat Eye - Zara Larsson** | | | **Catching Snowflakes** | | | **Champion Of The Tide Face** | | | **Cheerful Grin** | | | **Cheerful Hello** | | | **Chester Finkleton - Face** | | | **Chichiri the Wise Youkai Face** | | | **Chill** | | | **Chill McCool** | | | **Chippy McTooth** | | | **Chubs** | | | **City Life Man - Face** | | | **City Life Woman - Face** | | | **Claire's Face** | | | **Classic Alien Face** | | | **Classic Female - Face** | | | **Classic Female v2 - Face** | | | **Classic Goof** | | | **Classic Male - Face** | | | **Classic Vampire** | | | **Clown Face** | | | **Clown School Dropout** | | | **Commando** | | | **Concerned** | | | **Country Morning** | | | **Crank and Zap Mech - Face** | | | **Crazy Abstract Artist** | | | **Crazy Happy** | | | **Crazybot 10000** | | | **Crimson Evil Eye** | | | **Crimson Laser Vision** | | | **Crimson Starface** | | | **Crybaby** | | | **Cuckookrazybot 10000** | | | **Cute Kitty** | | | **Cutiemouse** | | | **Cyanskeleface** | | | **D:** | | | **D=** | | | **Daring** | | | **Daring Beard** | | | **Daring Blonde Beard Face** | | | **Dark Age Apprentice - Face** | | | **Daydreaming** | | | **DDotty Smile** | | | **Death's Grin** | | | **Demented Mouse** | | | **Desert Commando** | | | **Devil Nas X - Lil Nas X (LNX)** | | | **Devilish Smile - 24kGoldn** | | | **Dex's Face** | | | **Diamond Grill - Lil Nas X (LNX)** | | | **Digital Artist - Face** | | | **Digital Artist - Face** | | | **Disapproving Unibrow** | | | **Disbelief** | | | **Disbelief Face** | | | **Distaught Alien Invader** | | | **DIY Cardboard Knight - Face** | | | **Dizzy Face** | | | **DJ Databaze - Face** | | | **Domino Deckard - Face** | | | **Don't Wake Me Up** | | | **Downcast** | | | **Dr. Bunton Madmind - Face** | | | **Dr. Fia Tyfoid - Face** | | | **Dr. Lauren, Artifact Excavator - Face** | | | **Dragonface** | | | **Drill Sergeant** | | | **Drooling Noob** | | | **Druid of the Stag Face** | | | **Egg Crazed** | | | **Egg on your Face** | | | **Eita the Envious Youkai Face** | | | **Elegant Evening Dress - Face** | | | **Elf Guardian of the Northern Border - Face** | | | **Embarrassed** | | | **Emerald Ambassador** | | | **Emerald Archfey Visage** | | | **Emerald Evil Eye** | | | **Emerald Laser Vision** | | | **Emotionally Distressed Zombie** | | | **Emperor** | | | **Epic Face** | | | **Epic Vampire Face** | | | **Erisyphia - Face** | | | **Err...** | | | **Evil Skeptic** | | | **Exclamation Face** | | | **Existential Angst** | | | **eXtreme** | | | **Eyes of Everflame** | | | **Eyes of Everfrost** | | | **Ezebel Face** | | | **Facepalm** | | | **Fanciful Leprechaun Face** | | | **Fang** | | | **Fashion Face** | | | **Fast Car** | | | **Fawkes Face** | | | **Fearless** | | | **Finn McCool** | | | **Football Player - Face** | | | **Freckled Cheeks** | | | **Freckles** | | | **Friendly Cyclops** | | | **Friendly Grin** | | | **Friendly Pirate** | | | **Friendly Puppy** | | | **Friendly Smile** | | | **Friendly Trusting Smile** | | | **Frightening Unibrow** | | | **Frightful** | | | **Furious Finn Face** | | | **Furious George** | | | **Futureglam Bounty Hunter Face** | | | **Genni the Snail Knight-Errant - Face** | | | **Ghostface** | | | **Gigglypuff** | | | **Glee** | | | **Glided Diver - Face** | | | **Glided Diver - Face** | | | **Glory on the Gridiron** | | | **Golden Bling Braces** | | | **Golden Evil Eye** | | | **Golden Eyes - 24kGoldn** | | | **Golden Lightning Speaker** | | | **Golden Shiny Teeth** | | | **Good Intentioned** | | | **Goofball** | | | **Grandma's Lipstick** | | | **Green Amazeface** | | | **Green Bubble Trouble** | | | **Green Drool Angry Zombie** | | | **Green Galaxy Gaze** | | | **Green Glowing Eyes** | | | **Green Goof** | | | **Green Starface** | | | **Green Super Happy Joy** | | | **Green Trance** | | | **Green Ultimate Dragon Face** | | | **Green Whatchoo Talkin' Bout** | | | **Green Wistful Wink** | | | **Green-eyed Awesome Face** | | | **Gritty Bombo** | | | **Grumpy Blox** | | | **Gwen "Axe Angel" Rosewood - Face** | | | **Happy Girl Face** | | | **Happy Wink** | | | **Heart Gaze - Zara Larsson** | | | **Heeeeeey...** | | | **Hilarious** | | | **Hmmm...** | | | **Hockey Face** | | | **Hold It In** | | | **Huh?** | | | **Hut Hut Hike!** | | | **Hypnoface** | | | **I <3 New Site Theme** | | | **I Am Not Amused** | | | **I Didn't Eat That Cookie** | | | **I Hate Noobs** | | | **I Lack Personal Confidence** | | | **I wuv u** | | | **iFace** | | | **ILOVEFOOTBOLL!** | | | **Imagine** | | | **Isabella** | | | **It's Go Time!** | | | **It's so beautiful!** | | | **Jack Frost Face** | | | **Jacob: The Storm Breaker Face** | | | **Jester Equinox - Face** | | | **John's Face** | | | **Joyful Smile** | | | **Joyous Surprise** | | | **Jungle Commando** | | | **Just Trouble** | | | **Kandi's Sprinkle Face** | | | **Knight of Chivalry - Face** | | | **Knight of Courage - Face** | | | **Knight of Splintered Skies Ascendant - Face** | | | **Know-It-All Grin** | | | **Krezak - Face** | | | **Kroma Blitz - Face** | | | **Lady Darkshade - Face** | | | **Lady Lashes** | | | **Laughing Fun** | | | **Lavender Amazeface** | | | **Lazy Eye** | | | **Lightning Speaker** | | | **Lin's Face** | | | **Lion** | | | **Look At My Nose** | | | **Lynn - Face** | | | **Madbot 10000** | | | **Magical Dragon** | | | **Man Face** | | | **Manicbot 10000** | | | **Manlier Face** | | | **Masque** | | | **McLaren Big Grin** | | | **McLaren Smile** | | | **Meanie** | | | **Meow?** | | | **Merciless Ninja - Face** | | | **Mermaid Mystique** | | | **Mick McCann** | | | **Mildly Irritated Face** | | | **Minerva Bright - Face** | | | **Mischievous** | | | **Miss Scarlet** | | | **Mixologist's Smile** | | | **Mon Cheri Face** | | | **Monarch Butterfly Smile** | | | **Monster Face** | | | **Monster Grumpy Face** | | | **Monster Smile** | | | **Mr. Bubbles** | | | **Mr. Chuckles** | | | **Mr. Oinkers** | | | **Mr. Toilet - Face** | | | **Muttdawg** | | | **Mysterious** | | | **NeoClassic Female v2 - Face** | | | **Nervous** | | | **NetHack Addict** | | | **Nibbles, Devourer of Worlds** | | | **Ninja** | | | **No Z** | | | **Noriko the Gentle Youkai Face** | | | **Not Again!** | | | **Not So Friendly Eviscerator** | | | **Not sure if...** | | | **NOWAI!** | | | **O.o** | | | **O_o** | | | **Oakley's Face** | | | **Obvious Wink** | | | **Ochre Ogre** | | | **Octavia, The Ivory Spider Girl - Face** | | | **Ogre Face** | | | **Oh Deer** | | | **Oh Noes Another Dog** | | | **Old Man Jenkins** | | | **Old Timer** | | | **Oli Zigzag - Face** | | | **Optimist** | | | **Orange Starface** | | | **Orange Trance** | | | **Otakufaic** | | | **Overjoyed Smile** | | | **Overseer: Assassin - Face** | | | **Overseer: Prophet Face** | | | **Owl** | | | **Paintball Enthusiast** | | | **Performing Mime** | | | **Persephone's Girl Glam** | | | **Pieface Jellyfreckles** | | | **Pink Galaxy Gaze** | | | **Pink Mermaid Princess** | | | **Pink Moonstruck** | | | **Pink Shades McCool** | | | **Pink Wistful Wink** | | | **Pizza Face** | | | **Playful Vampire** | | | **Poisonous Beast Mode** | | | **Police Officer Nash - Face** | | | **Pony Face** | | | **Poor Man** | | | **Pop Queen Smilestar Spectacusmile** | | | **Prankster** | | | **Prideful Smile** | | | **Princess Alexis** | | | **Puck** | | | **Punk Face** | | | **Purple Alien** | | | **Purple Bubble Trouble** | | | **Purple Butterfly Smile** | | | **Purple Galaxy Gaze** | | | **Purple Mermaid Princess** | | | **Purple Moonstruck** | | | **Purple Starface** | | | **Purple Super Happy Joy** | | | **Purple Trance** | | | **Purple Wistful Wink** | | | **Pwnda Face** | | | **Quackface McGraw** | | | **Quijibo** | | | **Raccoon** | | | **Rach - Face** | | | **Radioactive Beast Mode** | | | **Raig Face** | | | **Rainbow Barf Face** | | | **Rainbow Spirit Face** | | | **RAWR!** | | | **Really Embarrassed** | | | **Red Fang** | | | **Red Glowing Eyes** | | | **Red Goof** | | | **Red Lip - Tate McRae** | | | **Red RAWR** | | | **Red Rock Star Smile** | | | **Red Serious Scar Face** | | | **Red Tango** | | | **Red Ultimate Dragon Face** | | | **Red White and Starface** | | | **Redonkulous** | | | **Retro Smiley** | | | **ROAR!!!** | | | **ROBLOX Madness Face** | | | **Rock Star Singer - Face** | | | **Rodeo Vampire - Lil Nas X (LNX)** | | | **Rogue Era Magus - Face** | | | **Rogueish Good Looks** | | | **Rose Amazeface** | | | **Rosey Smile** | | | **Royal Eye of Horus Face** | | | **Ruby Archfey Visage** | | | **Rudolph** | | | **Sad** | | | **Sad Clown** | | | **Sad Zombie** | | | **Sadfaic** | | | **Sai-eye Tyler Joseph - Twenty One Pilots** | | | **Samantha - Face** | | | **Sammy "Slick" Witter - Face** | | | **Sapphire Archfey Visage** | | | **Sapphire Evil Eye** | | | **Sapphire Laser Vision** | | | **Scarecrow** | | | **Scarecrow Face** | | | **Secret Service** | | | **Seeing Stars** | | | **Semi Colon Open Paren** | | | **Serena’s Face** | | | **Serious Cat** | | | **Serious Dog** | | | **Serious Red Eye Scar** | | | **Serious Scar Face** | | | **Sharkbait - Face** | | | **Sharpnine's Face of Disappointment** | | | **Sharpnine's Face of Joy** | | | **Sheriff Buffington - Face** | | | **Shhh...** | | | **Shiny Teeth** | | | **Shocked** | | | **Shutter Shades: The Face** | | | **Shy Lady** | | | **Sick Day** | | | **Silence** | | | **Silly Fun** | | | **Silver Punk Face** | | | **Singing** | | | **Sir Rich McMoneyston, III** | | | **Skater Boi - Face** | | | **Skater Gurl - Face** | | | **Skeletar** | | | **Skeptic** | | | **Slickfang** | | | **Slithering Smile** | | | **Slobbery Villain** | | | **Sly Cat** | | | **Sly Guy Face** | | | **Smil Nas X - Lil Nas X (LNX)** | | | **Smile** | | | **Smiling Girl** | | | **Smith McCool** | | | **Sneaky Green-Eyed Snake** | | | **Sneaky Steve** | | | **Sniffles** | | | **Snow Queen Smile** | | | **Snowflake Eyes** | | | **Snowman Face** | | | **So Funny** | | | **So Super Excited - Blue** | | | **So Super Excited - Pink** | | | **So Super Excited - Purple** | | | **Sophisticated Spectacles** | | | **Sorority Star Face** | | | **Sparkle Time Sparkle Eyes** | | | **Sparkling's Friendly Wink** | | | **Specter Informant - Face** | | | **Spring Bunny** | | | **Squad Ghouls: Zoe Saberhagen - Face** | | | **Square Eyes** | | | **Squiggle Mouth** | | | **Squinty Assassin Face** | | | **Stare** | | | **Starface** | | | **Starry Eyed** | | | **Starry Eyes Sparkling** | | | **Steampunk Inventor - Face** | | | **Steampunk Inventor - Face** | | | **Stink Eye** | | | **Stitchface** | | | **Sunny Fun** | | | **Sunrise Eyes - Tai Verdes** | | | **Sunstar - Face** | | | **Super Happy Joy** | | | **Super Pink Heart Makeup** | | | **Super Super Happy Face** | | | **Surprise!** | | | **Suspicious** | | | **Sweat It Out** | | | **Tango** | | | **Tattletale** | | | **Teal Mermaid Queen** | | | **Teal Rock Star Smile** | | | **Tears of Sorrow** | | | **Terrain Assault Specialist - Face** | | | **The Big Dog** | | | **The Birdcaller - Face** | | | **The Dog Whisperer** | | | **The First Time I Ever Played ROBLOX...** | | | **The Friendly Eviscerator** | | | **The Phantom Phalanx: Cygnus-34 - Face** | | | **THE SOUP IS DRY** | | | **The Winning Smile** | | | **Tiger Chase Fear Face** | | | **Timmy McPwnage** | | | **Tired Face** | | | **Tix Vision** | | | **Too Much Candy** | | | **Toothless** | | | **Toothy Drool** | | | **Toothy Grin** | | | **Torque the Blue Orc** | | | **Torque the Green Orc** | | | **Torque the Red Orc** | | | **Trance** | | | **Troublemaker** | | | **True Love Smile** | | | **Tsundere Expression** | | | **Two Guys on a Boat** | | | **Uh Oh** | | | **Up To Something** | | | **Upside Down Face** | | | **Valkyrie of the Splintered Skies - Face** | | | **Valorous Knight - Face** | | | **Vampire** | | | **Vans Checkerboard Blue Eyes** | | | **Violet Fang** | | | **Violet Starry Sight** | | | **Visual Studio Seized Up For 45 Seconds... Again** | | | **Walk the Plank You Scurvy Dogs!** | | | **Warpaint Josh Dun - Twenty One Pilots** | | | **WHAAAaaa!** | | | **Whatchoo Talkin Bout** | | | **Where are the eggs?** | | | **Whistle** | | | **Whuut?** | | | **Wicked Webbed Berserker Face** | | | **William "Big Bill" Conner - Face** | | | **Wink-Blink** | | | **Winning Smile** | | | **Winter** | | | **Woebegone** | | | **Woman Face** | | | **Wonder Woman's Golden Armor - Face** | | | **WWE - Seth Rollins Face** | | | **Wyldfire Fairy - Face** | | | **x_x** | | | **XD** | | | **XD 2.0** | | | **Xtreme Happy** | | | **YAAAWWN.** | | | **Yawn** | | | **Yellow Glowing Eyes** | | | **Yellow Starface** | | | **You ated my caik!** | | | **Yuck!** | | | **Zed Face** | | | **Zip It!** | | | **Zoey Face** | | | **Zombie Face** | | | **ZOMG** | | | **zOMG Hat Selling!** | | | **¬_¬** | | | **¬_¬ 2.0** | | --- title: "Manually Update Catalog Heads" url: /docs/en-us/art/characters/manually-update-catalog-heads last_updated: 2026-06-29T19:33:55Z description: "Instructions for creators who need to re-upload their head for validation." --- # Manually Update Catalog Heads > **Warning:** The following content applies to **creators with head assets on the Marketplace** who received a notice that their head assets **no longer pass dynamic head validation.** > **Info:** This document is intended to be updated frequently to include the latest information to help guide you through the head update process. If your head asset no longer passes the dynamic head validation, you need to fix and reupload your head asset to the Marketplace. **You do not need to update the body when the head was published as part of a body bundle**. You just need to update the head asset. This guide provides detailed information on how to perform a manual fix by re-caging your asset using a provided cage template using the following 5 main steps: 1. [Re-validate](#1-verify-the-catalog-head-asset-fails-validation) your catalog head asset to confirm issues. 2. [Export your head](#2-export-your-head-from-roblox) (or body bundle) from Roblox as a glTF. 3. [Fix your Head](#3-fix-your-head-asset-in-blender) asset in Blender. 4. [Import](#4-import-your-fixed-head-back-into-studio) your fixed head back into Studio. 5. [Provide the asset ID](#5-provide-the-assetid-of-your-updated-head) of your updated head to Roblox. > **Info:** While these instructions are provided for Blender, it's possible to perform equivalent steps in your preferred 3D modeling software. It's recommended to familiarize yourself with Roblox's head validation process before making changes to your head assets. ## 1. Verify the catalog head asset fails validation Roblox recently updated the dynamic head validation parameters to allow more existing catalog heads to pass validation while still enforcing that all heads in the catalog are dynamic. It's important to double check that your head still fails validation before proceeding with the rest of the steps. 1. Open a new Roblox Studio and verify you have the beta feature **glTF Export** enabled (you might need to restart Studio to pick up the new setting) 1. Verify your beta settings in **File** > **Beta Features**. 2. Run the following script in the command bar. 1. You have the option to provide either the head id (asset ID) or the bundle id (for a full body bundle) . Just replace `my_asset_id` with the correct ID and remove the leading `--` to run 2. Uncomment `createExportFromHeadAssetId(your_asset_id)` to retrieve only the head asset 3. Uncomment `createExportFromBodyBundleId(your_bundle_id)` to retrieve the full body as a bundle (includes the head)```lua local AssetService = game:GetService("AssetService") local Players = game:GetService("Players") local function createExportFromHeadAssetId(assetId: number) local hd = Instance.new("HumanoidDescription") hd.Head = assetId local char = Players:CreateHumanoidModelFromDescriptionAsync(hd, Enum.HumanoidRigType.R15) local headMesh = char.Head :: MeshPart headMesh.Size = headMesh.MeshSize headMesh.CFrame = CFrame.identity headMesh.Locked = false local exportModel = Instance.new("Model", workspace) exportModel.Name = "ExportThisAsGLTF" headMesh.Parent = exportModel end local function getOutfitId(bundleInfo) for _, item in pairs(bundleInfo.Items) do if item.Type == "UserOutfit" then return item.Id end end error("Could not find outfit ID") end local function createExportFromBodyBundleId(bundleId: number) local bundleInfo = AssetService:GetBundleDetailsAsync(bundleId) local outfitId = getOutfitId(bundleInfo) local hd = Players:GetHumanoidDescriptionFromOutfitIdAsync(outfitId) local char = Players:CreateHumanoidModelFromDescriptionAsync(hd, Enum.HumanoidRigType.R15) char:PivotTo(CFrame.identity) char.Name = "ExportThisAsGLTF" char.Parent = workspace end -- createExportFromHeadAssetId(your_asset_id) -- createExportFromBodyBundleId(your_bundle_id) ``` 3. Once the script runs, you will see a "ExportThisAsGLTF" model in workspace. Right-click and select **Save To Roblox**. This will popup the publish window: 1. In Content type, select **Avatar Item**. 2. In Asset Category, select **Head or Body** (if you used a body bundle). 1. Wait for the validation result. 3. If the dynamic head validation passes then you don't have to update this asset. 4. **Cancel** the publish operation (so you don't incur fees). If your asset failed the dynamic head validation then you need to continue with the following steps. ## 2. Export Your Head from Roblox Export your head or entire body from Blender as a glTF file: 1. In the explorer, right-click on the "ExportThisAsGLTF" model and select **Save/Export** > **Save as glTF**. ## 3. Fix your Head asset in Blender Fix your asset in Blender, or preferred 3D modeling software. In many cases, you may save time using a brand new head cage and fitting the cage over your character head. You may also see other validation issues that need to be addressed from the first step. 1. In Blender, open a new empty scene and import the glTF file. 1. In the glTF import settings, disable **Guess Original Bind Pose**. 2. Ensure everything you need for the head is present, including the cage, attachment points, facial joints and facial animations. 3. Roblox Studio flips avatars by 180 degrees on import, so you need to rotate your asset back by 180 in Blender (Hotkeys: `R`+`Z` then type `180`). 4. Most often heads fail validation due to cage issues. For example, the cage may be completely inside the head, has incorrect UVs or looks completely misplaced in the model. In most cases we recommend you do not spend time editing the original cage and instead: 1. Delete it from the asset. 2. Select the cage from the head template in this folder that best matches the shape of your head asset. These are [Roblox provided templates for cages with the correct UV settings](../../assets/art/reference-files/HeadCages.zip). 5. Ensure the Blender animation start value is set to 0. 6. Remove vertex colors. In Blender this can be done by clicking on the mesh and then under the data tab selecting color attributes and removing any that exist. 7. If needed, remove vertex color shading nodes for fbx export. The blender fbx exporter is more picky then glTF and won't accept nodes in between the texture input and the shader. 8. Modify your head asset to [pass validation](/docs/en-us/avatar/dynamic-heads/validate.md): 1. Align cage eyes and mouth regions to the head. 2. Ensure your head includes a rig, with poses keyframed and mapped to the FACs standard so that: 1. Eyes must blink. 2. Mouth must open and close. 3. Face must express happy and sad. 9. Export your updated head asset to FBX file format using the correct settings explained here (If you have PBR, you may want to use glTF). 1. Setup textures for FBX export (if needed). Blender will import the glTF textures in such a way that they won't be able to be exported in an FBX file by default. 1. To fix this go through each image and save it to your disk locally. ## 4. Import your fixed head back into Studio Import your head back into Studio and verify that it passes validation. If validation fails, check the error messages and address the issues in your 3D modeling software again. If validation passes, use the following steps to upload as a **Development Item** (not Avatar item). 1. Import the FBX/glTF file with your (fixed) head asset back into Studio. 1. Your fixed head must contain: head mesh, cage mesh, face rig, and updated textures (if you modified them). 2. Later, you will save these assets as a `Class.Model` in Studio (not as an individual asset such as the head mesh). 2. Make sure your head asset passes validation. 1. The easiest way to accomplish this is: 1. Select **Save to Roblox** on your asset. 2. Select **Avatar Item**. 3. Set its **Asset Category** to Head. 4. Wait for the Dynamic Head validation result (pass or fail). 5. **Cancel** the save operation so you don't incur publishing fees at this time. 2. If your head doesn't pass validation then you need to go back to Blender or Maya and make the appropriate fixes to pass validation. 3. If your head passes validation, publish your asset as a Development Item. 1. In the Explorer, select the `Class.Model` of your avatar, ensure that it includes the head mesh, face rig, and the cage data. 1. If you save the model for the entire character body, the upload process automatically extracts the head data. 2. Select **Save to Roblox** on your asset. 3. Select **Development Item**. 4. Set its **Asset Category** to Model. 5. Set Asset Privacy to **Public**. 1. If you are enrolled in **Asset Privacy beta**, disable the beta before uploading your fixed asset. 6. Save the model to Roblox (no publishing fees are incurred). 7. After saving to Roblox, save the asset ID provided. 1. While you can submit either the head or body asset, there is no distinction between head and body asset when saving as a development item. 2. For submission to the web form, we prefer that you get the asset ID of the head, but you can use either the head or body asset ID and Roblox will replace the head accordingly. ## 5. Provide the assetID of your updated head Use the following steps to provide Roblox the updated asset ID to replace your head with: 1. Use the link to the web form in the email notification corresponding to your asset to communicate back the asset ID of your updated head. 1. You will be asked to enter your date of birth for each update. This is a legal requirement we can't bypass. 2. You can submit multiple times. We will use your last submission as the "final" version to the update. 2. Roblox will complete the update of your catalog asset with the modifications you provided. ## Additional Resources Roblox has published a series of documentation and video tutorials on best practices for creating dynamic head assets. - [Dynamic head overview](/docs/en-us/avatar/dynamic-heads.md) - [Head validation process](/docs/en-us/avatar/dynamic-heads/validate.md) - [Head validation video guide](https://www.youtube.com/watch?v=OwhkWzSBnf0) - [Avatar Setup](/docs/en-us/avatar-setup.md) - [Caging Best Practices](/docs/en-us/avatar/dynamic-heads/caging-best-practices.md) --- title: "Test in Blender" url: /docs/en-us/art/characters/testing/blender last_updated: 2026-06-29T19:33:55Z description: "Verify the important aspects of your character model in Blender before importing into Studio." --- # Test in Blender You can quickly verify many of your components in Blender, allowing you to catch out problems that may cause issues later on. You can use a mix of Blender native tools and Roblox's helper plugins to check for many common issues in your character model. ## Facial animation data Facial animation uses multiple modeling components to work effectively. If using a template, or making changes to a model with existing facial animation data, it's sometimes possible to make a change that affects the saved poses, either in the animation timeline, or in the custom property of the head mesh. Use the following steps to help verify the integrity of your facial animation data: 1. With the Armature object selected, check the animation frame in the **timeline**: 1. The timeline typically ranges between **0–330**, though not all templates use the entire range, and there may be gaps between poses. 2. The **neutral pose** should be on **frame 0**. 3. There shouldn't be any keyframe gaps between the frames with animation data. 2. Verify the **Object Properties** > **Custom Properties** panel for the Head_Geo object: 1. There should be a **RootFaceJoint** attribute set to the name of the first facial animation bone. 1. This bone should be a child of the **Head** bone, one of the 15 required bones in an avatar's armature. 2. In templates and some reference models, this bone is commonly named `DynamicHead` and indicates the beginning of the facial animation rig. 2. There should be about the same number of custom properties as unique animation frames, with each animation pose having its own mapped custom property. 3. The names for the mapped poses here should match the spelling and capitalization of the [FACS pose reference](/docs/en-us/avatar/dynamic-heads/facs-poses-reference.md). > **Error:** If you discover issues with your face animation data, you may need to repeat the skinning, posing, or mapping processes for your character's head. See [Creating Heads](/docs/en-us/art/characters/facial-animation/create-basic-heads.md) for a general overview of the avatar head creation process. > > If you are using a template and run into issues, it's possible the facial data was overridden by a saved animation, or the rigging or mapped data was mistakenly deleted. If it's difficult to quickly identify and resolve the issue, it may be quicker to restart your character customization from the original template file. ## Body skin Properly skinning your character models ensures natural joint movements and realistic poses and expressions. There are several ways to verify skinning data, like using Blender's Pose Mode to pose the joints and examine how certain orientations can affect the joint skinning. Use the following steps in Blender to verify your joint skinning in Pose mode: 1. With the **Armature** object selected, navigate to the **Object Properties** > **Viewport Display** and enable **Show In Front**. This allows you to see and access bones within your character. 2. With the armature selected, and switch to **Pose Mode**. 3. Select all the face bones of your model and press `H` to hide. You can reveal these again later with `Alt``H` (`⌥``H`) in Pose mode. 4. Select any body bone and press `R` to rotate. Verify that your body bones correctly deform and bend the character mesh as expected in natural poses. 1. While actively rotating a bone, right-click to cancel the rotation and set the bone to its original position. 2. While actively rotating a bone, you can **click to confirm** the rotation and save the current position. You can combine this to make various poses of your character by changing the rotation of various limbs at once. 3. If you've saved a rotation, press `Alt``R` (`⌥``R`) to clear any rotation in Pose Mode. Make sure to reset your pose whenever you complete testing. You can also run through common poses and movements using Roblox's [Calisthenics Tool](/docs/en-us/art/modeling/calisthenics-tool.md) add-on, which applies several common animations to an avatar armature that you can play back and review in Blender's animation timeline. > **Warning:** The [Calisthenics Tool](/docs/en-us/art/modeling/calisthenics-tool.md) adds animations to your timeline to quickly preview body animations. Be careful when saving or exporting your character when using this tool, as it may conflict with saved facial animation data that uses the same timeline. > **Error:** If you discover issues with your skinning, you may need to troubleshoot and reapply rigging and skinning steps to your humanoid. See [Rigging and skinning](/docs/en-us/art/modeling/rigging.md) for an overview of the rigging and skinning processes. > > If you are using a template, it's possible the skinning data was affected by a destructive modeling change. This may require reskinning, or starting over your character customization from the original template file. ## Technical specifications Your avatar components should match the requirements provided in the [avatar specifications](/docs/en-us/avatar/character-bodies/specifications.md). When using templates and following appropriate non-destructive modeling practices, most of these components should not be touched, but you should still double-check that each component is compliant with the technical requirements. > **Success:** If you are using Roblox character templates and are ready to export into Studio, see [Exporting instructions](/docs/en-us/art/characters/creating/export-textures.md) for specific export instructions for template files. --- title: "Testing characters" url: /docs/en-us/art/characters/testing last_updated: 2026-06-29T19:33:56Z description: "Verify the important aspects of your character model in Blender and in Studio." --- # Testing characters It's important to constantly test your models during and after the creation process to ensure that your character can interact with various social and environmental elements in any experience. Whether you are using a template character, or your own custom assets, use the information provided in [Test in Blender](/docs/en-us/art/characters/testing/blender.md) and [Test in Studio](/docs/en-us/art/characters/testing/studio.md) throughout your workflow to comprehensively test your assets and quality check your models. Use the following table as an overview of common tests and resources to verify each component of your character model. #### Blender > **Info:** You can apply the following workflows with any modeling software, though certain interfaces and instructions may differ between platforms. See [Testing in Blender](/docs/en-us/art/characters/testing/blender.md) for additional information. | Component | Testing recommendations | | --- | --- | |
**Body Parts** | When modeling, confirm that your geometry follows [avatar geometry specifications](/docs/en-us/avatar/character-bodies/specifications.md#geometry). | |
**Textures** | When texturing, confirm that your geometry follows [avatar texture specifications](/docs/en-us/avatar/character-bodies/specifications.md#textures). | |
**Rigging** | Use [Blender's posing tools](/docs/en-us/art/characters/testing/blender.md#body-skinning) to check the rigging and skinning quality of your asset.
| |
**Face Animation** | Confirm that [your FACS data](/docs/en-us/art/characters/testing/blender.md#facial-animation-data) is present and correctly configured. | |
**Cage Mesh** | Manually verify that your cage mesh completely and tightly covers your character model and follows [avatar outer cage specifications](/docs/en-us/avatar/character-bodies/specifications.md#outer-cages). | |
**Attachments** | Manually verify that your attachment points are correctly positioned and follow [avatar attachment specifications](/docs/en-us/avatar/character-bodies/specifications.md#attachments). | #### Studio > **Info:** Roblox provides an uncopylocked testing place that includes quick access to various environments and testing features. See [Testing in Studio](/docs/en-us/art/characters/testing/studio.md) for additional information. | Component | Testing recommendations | | --- | --- | |
**Body Parts** | Verify your character's geometry while in [movement and animation](/docs/en-us/art/characters/testing/studio.md#movement-and-animation). | |
**Textures** | Test your character in [various lighting environments and skin tone colors](/docs/en-us/art/characters/testing/studio.md#lighting-and-color). | |
**Rigging** | Use [animations and testing environments](/docs/en-us/art/characters/testing/studio.md#movement-and-animation) to verify your character's rigging, skinning, and movement. | |
**Face Animation** | [Cycle through various facial animations](/docs/en-us/art/characters/testing/studio.md#movement-and-animation) to ensure your character can express common expressions. | |
**Cage Mesh** | [Equip various clothing items](/docs/en-us/art/characters/testing/studio.md#clothing-and-accessories) to check for fit and for any unexpected cosmetic issues. | |
**Attachments** | [Equip various accessory items](/docs/en-us/art/characters/testing/studio.md#clothing-and-accessories) to verify rigid accessory placement and orientation. |
--- title: "Test in Studio" url: /docs/en-us/art/characters/testing/studio last_updated: 2026-06-29T19:33:56Z description: "Verify the important aspects of your character model in a custom Studio test experience." --- # Test in Studio Test your character model in Studio to understand how it looks and feels within a Roblox experience. To test your custom character in Studio, first [import the character model](/docs/en-us/avatar/character-bodies/import.md) into your Studio project. You can test in Studio using the [Avatar Setup](/docs/en-us/avatar-setup.md) tool to preview and test various components of your avatar character. You can also playtest your character in the [Avatar Test experience](https://www.roblox.com/games/13176231501/Avatar-Test-Place). This experience provides a wide variety of environmental and clothing tests to ensure that your character model and related components work as expected. ## Set up test experience The following is the general testing process setting up the Avatar Test experience: 1. Download and open the [Avatar Test experience](https://www.roblox.com/games/13176231501/Avatar-Test-Place) in Studio. 2. Import your avatar model into Studio. 3. Set your model as the starter character. 4. Launch a playtest of the experience. ### Open in Studio In order to use the [Avatar Test experience](https://www.roblox.com/games/13176231501/Avatar-Test-Place), you must first download it from Roblox. You can save the experience locally and make any changes that may help your testing workflow. Roblox may update this experience periodically to add or improve features. To download the Avatar Test experience: 1. Navigate to the [Avatar Test experience](https://www.roblox.com/games/13176231501/Avatar-Test-Place). 2. In the button next to the experience title, select **…** > **Edit**. The page navigates to the Studio launcher. ### Import After you open the test experience in Studio, import your custom character model. 1. From Studio's **File** menu, select **Import**. A file browser displays. 2. Select your custom character `.fbx` file. 3. Verify the following: 1. With the highest level object selected, ensure **Rig Type** is set to R15. 2. If using the template files, set **Rig Scale** to **Rthro**. Otherwise, use the appropriate rig scale for your [character body type](/docs/en-us/avatar/character-bodies/specifications.md#body-scale). 3. Check for any red errors that may block import into Studio. 4. Verify any yellow warnings that may indicate a quality issue with your avatar components. > **Warning:** The Importer may incorrectly report some cage meshes as not watertight. You can ignore these warnings for those cage objects. 4. Click **Import**. The character's model populates into the workspace. > **Warning:** If an error or warning message displays in the **Output** window, see [Troubleshooting](/docs/en-us/avatar/dynamic-heads/test.md#troubleshooting) for guidance on how to handle your specific error or warning message. ### Add SurfaceAppearance If you exported your textures separately, or if the Importer fails to pick up your PBR textures, you can add them manually. To add individual image texture files: 1. Open the **Asset Manager**. You may need to publish your experience first. 2. **Bulk Upload** the image map assets. 3. In the **Explorer**, expand the **Head_Geo** and check if a `Class.SurfaceAppearance` object exists. - If `Class.SurfaceAppearance` is present, continue to step 4. - If `Class.SurfaceAppearance` is not present, select the ⊕ next to the Head_Geo and add a `Class.SurfaceAppearance` object. 4. Set each property to the corresponding uploaded image map 5. Copy the `Class.SurfaceAppearance` with the populated image map with `Ctrl``C` (`⌘``C`). 6. Hold `Shift` and click all of your model's MeshParts to select all of your character's geometry. 7. Use `Ctrl``Shift``V` (`⌘``Shift``V`) to paste into all of the head geometries. An updated `Class.SurfaceAppearance` populates in each `Class.MeshPart` object. ### Set model as StarterPlayer In order to play as the character during a playtest, rename and move the model instance: 1. In the **Explorer**, rename the character **model** as `StarterCharacter`. 2. In the **Explorer**, navigate to the `Class.StarterPlayer` directory. If a `StarterCharacter` model already exists, remove it. 3. Click and drag the model under the directory. After you rename and add your model, [playtest](/docs/en-us/studio/testing-modes.md#playtesting) the experience as the custom model. ## Use test experience The Avatar Test experience includes several features in the interface and environment to quickly perform comprehensive tests of your avatar characters. You can use the [test checklist](#checklist) to ensure you are reviewing all the important aspects of your avatar character. When playtesting the experience, use the following UI buttons on the right side to open various testing tools and options: | Button | Description | | --- | --- | |
Avatars and facial animation | Use this menu to swap between the custom avatar and control avatars. An additional button option allows you to loop through various facial animations to test your character's face expressions. | |
Emotes and animations | Use this menu to test various animations on your avatar character. | |
Skin tone color palette | Use this menu to select a new skin tone for your character. See [Custom Skin Tones](../../../assets/art/avatar/basic-creation/Testing-UI-Emote.png) for more details on this implementation with template character models. | |
Clothes and accessories | Use this menu to select various clothing items to try on. | |
Movement settings | Use this menu to change movement settings for your character model, such as **jump height** and **walk speed**. | |
Body proportion settings | Use this menu to change various aspects of your character's body proportions. | |
Lighting presets | Use this menu to change the environment lighting to various presets, such as **Mountains - Sunset**, or **Indoor - Midday**. | ### Checklist Use the following checklists to ensure that you are comprehensively testing each component of your avatar. Use the Avatar menu to switch between your custom avatar and a control avatar to verify expected behavior. #### Movement and animation _Testing guitar animation emote_ _Testing water movement_ _Testing ramp movement_ Check the following for any anomalies: | Function | How to test | | --- | --- | | Walking | Move the character around the environment, ramps, and platforms. | | Sitting | Move to any of the seats in the environment and press `E` to sit. | | Climbing | Move to any of the trusses and ladders. Your character automatically begins climbing when near. | | Swimming | Use the pool of water at the top of the ramp area to verify your swimming animation. | | Jumping | Press `Space` to jump. | | Dying | Use any of the death cubes to set your character to a ragdoll state and respawn. | | Facial Animation | Use the **Face UI menu** to toggle a face animation cycle. | | Emotes and Animation | Use the **Emote UI menu** to access and play the available emotes. | If you see unexpected behavior with your character's movement, there may be some issues with the rigging or skinning of your character. It may require revisiting the rigging and armature components of your avatar model in your modeling application. #### Lighting and color _Testing skin tones_ _Testing environmental lighting_ Check the following for any anomalies: | Function | How to test | | --- | --- | | Skin tone | Use the **Skin Tone UI** menu to test various skin tones for texturing and lighting compatibility. | | Environmental lighting | Use the **Lighting UI menu** and the environment to check how your character's surface appearance displays in various conditions. | If you see unexpected behavior with your character's appearance in various lighting and color selections, there may be some issues with the texturing of your character. It may require re-adjusting the texturing components of your avatar model in your modeling application. #### Clothing and accessories _Test a mix of clothing and accessories_ _Use the UI menu or pedestals to equip or reset clothing and accessories_ Check the following for any anomalies: | Function | How to test | | --- | --- | | Equipping accessories | Use the **Clothes and Accessories UI menu** or environmental pedestals to equip rigid accessories to your character. | | Equipping clothing | Use the **Clothes and Accessories UI menu** or environmental pedestals to equip rigid accessories to your character. Test your clothing in motion and in animations to verify the fit. | If you see unexpected behavior with your character wearing clothes or equipping rigid accessories, there may be issues with the caging or attachment points of your character. You may need to re-adjust the cage mesh shapes or attachment objects of your avatar model in your modeling application. > **Warning:** Clothes can layer in unconventional orders, such as a jacket worn below a tshirt. You can reset the equipped clothing and try various combinations of clothing options and ordering.
--- title: "Blender validation tool" url: /docs/en-us/art/characters/validation-tool last_updated: 2026-06-29T19:33:56Z description: "The Avatar Validation Tool checks for common character model issues in Blender." --- # Blender validation tool > **Warning:** This tool is no longer in active development. You are free to use the tool for any purpose. The tool may be out of date or incompatible with modern Roblox assets. The **Avatar Validation Tool** is a supplemental Blender add-on you can use to quickly identify common issues with your character model. This tool checks that your model meets most of Roblox's avatar [technical specifications](/docs/en-us/avatar/character-bodies/specifications.md) and, when possible, attempts to automatically resolve them. This tool can save you time by checking for common validation specifications before you export your model, but it doesn't provide a comprehensive verification of all aspects of your character model. You should always continually test your character model, including testing your characters [in Studio](/docs/en-us/art/characters/testing/studio.md) and verifying on your own if your model meets Roblox's [character specifications](/docs/en-us/avatar/character-bodies/specifications.md). ## Install > **Warning:** The Validation Tool plugin is compatible with Blender 3.5.1+. You may experience unexpected behavior if using earlier versions of Blender. To install the Validation Tool in Blender: 1. Download [ValidationTool_Blender.zip](https://github.com/Roblox/avatar/tree/main/Tools/ClothingValidationTool) and save the .zip file locally. Do not open the .zip file. 2. In Blender, navigate to **Edit** > **Preferences**. 3. In Preferences, go to the **Add-Ons** section on the left sidebar. 4. Click the **Install...** button. A file browser displays. 5. Select the downloaded .zip file and click **Install Add-On**. 6. In the Add-Ons section, find the Validation Tool and **enable** the add-on. 7. Return to your workspace and expand the side toolbar in the Viewport to access the add-on. _Sidebar indicator_ _Expanded sidebar_ ## Validate After installing the plugin, you can begin validating your character models. When validating, results with issues display as red. In some cases, you can apply an automatic fix to checks that have issues. See [Checks and Troubleshooting Steps](#checks-and-troubleshooting) for details on each validation check. To use the validation tool on your asset: 1. In the Target Armature field, select the **eyedropper**. 2. With the eyedropper active, select your character's **armature object**. This object is often named Joints or Armature. 3. Select **Run All Checks** to go through each individual check. Alternatively, you can click individual buttons to run a specific check. 4. Checks that detect issues display as red and an error output displays at the bottom of the tool. > **Info:** This validation tool only checks for common validation issues. You should always continually test your character model, including testing your characters [in Studio](/docs/en-us/art/characters/testing/studio.md) and verifying on your own if your model meets Roblox's [character specifications](/docs/en-us/avatar/character-bodies/specifications.md). ## Checks and troubleshooting Each button on the validation tool performs a different check against the expected [character specifications](/docs/en-us/avatar/character-bodies/specifications.md). The following are the specific types of verification each check performs, as well as general troubleshooting steps to resolve discovered issues:
**Layers**
Checks for extra layers in the scene. Automatic fix attempts to remove extra layers. If the automatic fix fails, manually remove extra layers.
**Transforms**
Checks that geometry location, rotation, and scale are frozen (`0`,`0`,`0`). Automatic fix button attempts to freeze the transforms. This may cause changes to your rig. If the automatic fix fails, manually freeze your geometry.
**UV**
Checks if the body Geometry UVs are within `0`-`1`. You must resolve issues manually by setting your body object UVs within a `0`-`1` range.
**Cage UV Modifications**
Checks if cage object UVs have incorrect modifications. Automatic fix attempts to resolve UV coordinates. If automatic fix fails, you may want to use a new template cage.
**Asset Size**
Checks if the character total size and individual parts are within expected bounds. You must resolve issues manually by resizing your character model or individual parts.
**Unused Data**
Checks for any unused data in the scene, such as orphan data, or unused materials. Automatic fix attempts to remove extra data. If automatic fix fails, manually remove unnecessary data and objects.
**Keyframes**
Checks for any unnecessary keyframes. Keyframes related to facial expressions are expected and ignored. Automatic fix attempts to remove keyframes associated with non-face objects. If automatic fix fails, manually remove keyframe data from non-face objects. Do not delete keyframes that also contain facial data.
**Attachment Points**
Checks that attachment points are parented to the armature and use the correct naming convention. Automatic fix attempts to parent any unexpected attachment points.
**Joint Positions**
Checks for the following: - Root and HumanoidRootNode bones exist - Root bone is at `0`,`0`,`0` world space - Bones with Left/Right naming conventions are on correct axes. Refer to the output messages to manually resolve joint position issues.
**FACS Data**
Checks for the following FACs data: - `Neutral Pose` exists as a mapped pose - Mapped Custom Properties under the Head_Geo exists - Property name and values correctly set - Corrective poses must include existing base poses - Corrective poses do not use more than 3 base poses Refer to the output messages to manually resolve FACS data issues.
**Head Joint Names**
Checks for duplicate head bone names, including duplicates with Blender's duplicate affixes .001, .002, .003, and such. Refer to the output messages to manually resolve face joint names.
**Intersection**
Checks for any intersection between character geometry and cage mesh geometry. The tool switches to Edit mode and highlights areas where potential intersections are detected.
**Polygon Data**
Checks for common polygon issues: - Meshes are watertight - Meshes are within polycount budgets - Meshes don't use N-Gons Meshes - Meshes are non-manifold - Meshes don't have vertex color information Refer to the output messages to manually resolve any polygon data issues.
**Texture Format**
Checks that textures match texture requirements, such as resolution size. Users can also select an external image file. Refer to the output messages to manually resolve any texture related issues.
--- title: "3D art in Roblox" url: /docs/en-us/art last_updated: 2026-06-29T19:33:56Z description: "An overview of 3D art tools, resources, and workflows for creators of all experience levels." --- # 3D art in Roblox 3D art in Roblox refers to the creation and integration of models, environments, effects, sounds, and more to create an engaging and immersive experience. 3D artists bring the world, objects, and characters to life with a variety of tools and techniques using Roblox Studio and external applications. No matter your creative background, Roblox provides a variety of tools, guides, tutorials, and reference files to help inspire and level up your 3D art creation in your experiences and beyond. ## Quick reference Whether you're just getting started with Roblox or need a quick refresher, use the following popular resources to get up to speed. #### 3D art in Roblox Studio Get up to speed with the many powerful tools and functions in Roblox Studio! [
Studio for artists
](/docs/en-us/overview-studio.md) #### Use third-party applications Understand how to use incorporate your creations from off-platform into Studio. [
Overview
](/docs/en-us/overview-dcc.md) [
General modeling requirements
](/docs/en-us/modeling/specifications.md) [
Downloadable references
](/docs/en-us/modeling/project-files.md) #### Create avatar items Create accessories, clothing, and bodies that you can sell on the Marketplace. [
Overview
](/docs/en-us/avatar.md) [
Marketplace overview
](/docs/en-us/marketplace.md) [
Fees and commissions
](/docs/en-us/marketplace/marketplace-fees-and-commissions.md) ## Tutorials To dive deeper, follow along with guided curriculum that teaches you the skills you need to create and monetize your experiences. #### Environmental art Environmental art teaches you how to recreate a high-quality environment for a first-person laser tag experience. #### Avatar item tutorials Check out our tutorials on creating your own Avatar items ready for the Marketplace. [
Create accessories
](/docs/en-us/accessories/creating-rigid.md) [
Create clothes
](/docs/en-us/accessories/creating.md) [
Create characters from templates
](/docs/en-us/characters/creating.md) ## Showcases Check out the following showcases made by Roblox and the community. Each of these highlight amazing creativity and technical accomplishments by talented creators. #### Roblox creations Check out various experiences created by Roblox. Click to see documentation, downloadable resources, and links to try out the experiences yourself! [
Mystery of Duvall Drive
](/docs/en-us/resources/the-mystery-of-duvall-drive.md) [
Beyond the Dark
](/docs/en-us/resources/beyond-the-dark.md) [
Modern City template
](/docs/en-us/tutorials/use-case-tutorials/modeling/assemble-modular-environments.md) #### Community creations Check out these amazing showcase experiences created members of the community! [
Toyokawa Inaru Shrine by @nezko
](https://www.roblox.com/games/3158922185/Toyokawa-Inari-Shrine-Showcase) [
Hotel Resort by @ChooShu_Cho
](https://www.roblox.com/games/6519709626/Hotel-Resort) [
Lab by @thisfall
](https://www.roblox.com/games/5415692770/Lab) ## From the staff The following articles are from the staff, covering a broad array of topics that can help you on your creative journey both within Studio and beyond. #### Create buttes and mesas with Studio terrain Create beautiful and striking landscapes using Studio's powerful terrain and material tools. #### Custom skyboxes 101 Apply your own custom skyboxes to your experience to add some flair and elevate your world. #### Use alpha masks to customize colors Save time and optimize for performance by using alpha masks in your PBR textures. #### Create perfect audio loops Learn a quick technique to make seamless loops in your audio files. Great for ambience, music, sound effects, and more. #### Use radial symmetry with Zbrush Radial symmetry is an extremely versatile sculpting technique applicable in many third-party tools. This article is a primer on radial symmetry in Zbrush. #### Looking for more? Stay up to date with new articles written by our staff on the [DevForum](https://devforum.roblox.com/c/resources/roblox-staff/278). Topics can range from third-party tools, behind-the-scenes, tutorials, and more. ## Additional resources The following is a list of common 3D art resources ordered by subject, as well as other Studio and Avatar documentation. You can also use the side navigation bar, search bar, or assistant to find specific articles and topics. ##### Get started and tutorials ##### Topic overviews [Get started in Studio](/docs/en-us/overview-studio.md) [Get started with third-party software](/docs/en-us/overview-dcc.md) [Get started with avatar items](/docs/en-us/avatar.md) [Create your first experience](/docs/en-us/tutorials/first-experience.md) [Creator Store overview](/docs/en-us/production/creator-store.md) [Marketplace overview](/docs/en-us/marketplace.md) ##### 3D art tutorials [Core curriculum](/docs/en-us/tutorials/curriculums/core.md) [Environmental art](/docs/en-us/tutorials/curriculums/environmental-art.md) [Gameplay scripting](/docs/en-us/tutorials/curriculums/gameplay-scripting.md) See [Engine tutorials](/docs/en-us/tutorials.md) for additional 3D Art tutorials. ##### Avatar tutorials [Creating accessories](/docs/en-us/art/accessories/creating-rigid.md) [Create clothing](/docs/en-us/art/accessories/creating.md) [Create avatar characters from templates](/docs/en-us/art/characters/creating.md) See [Avatar tutorials](/docs/en-us/avatar/tutorials.md) for additional avatar tutorials. ##### Specifications and references ##### Technical specs [Blender and Maya .FBX export settings](/docs/en-us/modeling/export-requirements.md) [General mesh specifications](/docs/en-us/modeling/specifications.md) [Accessory specifications](/docs/en-us/avatar/rigid-accessories/specifications.md) [Clothing specifications](/docs/en-us/avatar/layered-accessories/specifications.md) [Avatar character specifications](/docs/en-us/avatar/character-bodies/specifications.md) [Marketplace policy](/docs/en-us/marketplace/marketplace-policy.md) ##### Reference files [Example models](/docs/en-us/modeling/project-files.md) [Reference avatar items](/docs/en-us/modeling/project-files.md#avatar-and-accessories) [Add-ons and tools](/docs/en-us/modeling/project-files.md#add-ons-and-tools) ##### Texturing [Texturing requirements](/docs/en-us/modeling/texture-specifications.md) [PBR textures](/docs/en-us/modeling/surface-appearance.md) [Create customizable colors with alpha masks (staff)](https://devforum.roblox.com/t/using-alpha-masks-in-color-maps-to-customizetint-base-colors/2883381) ##### Guides and workflows ##### Studio tools [Importer](/docs/en-us/studio/importer.md) [Accessory Fitting Tool](/docs/en-us/avatar/accessory-fitting-tool.md) [Animation Editor](/docs/en-us/animation/editor.md) [Animation Capture](/docs/en-us/animation/capture.md) [Avatar Setup](/docs/en-us/avatar-setup.md) [Material Generator](/docs/en-us/parts/materials.md) [Terrain Editor](/docs/en-us/parts/terrain.md) [Texture Generator](/docs/en-us/studio/texture-generator.md) [Toolbox](/docs/en-us/projects/assets/toolbox.md) and [Asset Manager](/docs/en-us/projects/assets/manager.md) See [Studio overview](/docs/en-us/studio.md) for more. ##### Environment, lighting, and effects [Environmental art curriculum](/docs/en-us/tutorials/curriculums/environmental-art.md) [Assemble modular environments](/docs/en-us/tutorials/use-case-tutorials/modeling/assemble-modular-environments.md) [Light with props](/docs/en-us/effects/light-sources.md) [Add 3D audio](/docs/en-us/tutorials/use-case-tutorials/audio/add-3D-audio.md) [Add 2D audio](/docs/en-us/tutorials/use-case-tutorials/audio/add-2D-audio.md) [Add text-to-speech](/docs/en-us/tutorials/use-case-tutorials/audio/add-text-to-speech.md) [Enhance outdoor environments with realistic lighting](/docs/en-us/tutorials/use-case-tutorials/lighting/enhance-outdoor-environments.md) [Custom skyboxes 101 (Staff)](https://devforum.roblox.com/t/custom-skyboxes-101/2849003) See [Lighting and effects](/docs/en-us/environment.md) for additional environment resources. ##### Avatar item creation [Avatar overview](/docs/en-us/avatar.md) [Accessories overview](/docs/en-us/avatar/rigid-accessories.md) [Clothing overview](/docs/en-us/avatar/layered-accessories.md) [Character bodies overview](/docs/en-us/avatar/character-bodies.md) [Marketplace overview](/docs/en-us/marketplace.md) ##### Animation [Animation Capture](/docs/en-us/animation/capture.md) [Animation Editor](/docs/en-us/animation/editor.md) [Export animations from Maya](/docs/en-us/characters/export-avatar-animations-from-maya.md) [Inverse kinematics](/docs/en-us/animation/inverse-kinematics.md) See [Animation](/docs/en-us/animation.md) for additional resources. ##### Facial animation and live heads [Basic head creation](/docs/en-us/characters/facial-animation/create-basic-heads.md) [Create face accessories](/docs/en-us/characters/facial-animation/create-face-accessories.md) [FACS pose references](/docs/en-us/avatar/dynamic-heads/facs-poses-reference.md) ##### Rigging and skinning [Rigging and skinning overview](/docs/en-us/modeling/rigging.md) [Avatar rig requirements](/docs/en-us/avatar/character-bodies/specifications.md#rigging) [Rig basic meshes](/docs/en-us/modeling/rig-a-simple-mesh.md) [Rig facial bones](/docs/en-us/characters/facial-animation/create-basic-heads.md#rigging) [Skin facial bones](/docs/en-us/characters/facial-animation/create-basic-heads.md#skin-face-bones) [Auto skin transfer](/docs/en-us/avatar/automatic-skinning-transfer.md) ##### Publish to Creator Store and Marketplace [Creator Store overview](/docs/en-us/production/creator-store.md) [Marketplace overview](/docs/en-us/marketplace.md) [Marketplace policy](/docs/en-us/marketplace/marketplace-policy.md) [Publish avatar items to Marketplace](/docs/en-us/marketplace/publish-to-marketplace.md) [Marketplace fees and commissions](/docs/en-us/marketplace/marketplace-fees-and-commissions.md)
--- title: "Blender and Maya texture settings" url: /docs/en-us/art/modeling/assign-textures last_updated: 2026-06-29T19:33:56Z description: "Learn to assign basic or PBR textures in Blender or Maya to natively import into Studio." --- # Blender and Maya texture settings This guide covers the process to assign texture images in Blender or Maya to your 3D model so that they import seamlessly into Studio. The import process supports both basic textures and realistic PBR textures, as long as they meet Roblox's [texture specifications](/docs/en-us/art/modeling/texture-specifications.md). > **Warning:** The following information is **specific for applying makeup textures generic meshes**. For more information on reassigning textures for makeup assets, see [Reassign textures in Blender and Maya](/docs/en-us/avatar/makeup/reassign-textures.md). ## Blender To assign Roblox-supported textures to your 3D model in Blender: 1. In the Outliner, select your mesh object. 2. In the Properties panel, navigate to the **Materials tab**. 3. Verify that your object has a Material assigned with the Surface set to `Principled BSDF`. 4. If no material is assigned, click the + symbol to add a material. 1. Click the New button. A new `Principled BSDF` material populates. 5. Navigate to the **Shading tab**. 6. With your mesh object selected, check the node graph. There may already be texture image nodes connected to the `Principled BSDF` node. 7. In the node graph area, add a texture image node with right-click > **Add** > **Texture** > **Image Texture**. 8. In the new Image Texture node, click Open to open a file browser and insert your custom texture. > **Info:** Alternatively to steps 7-8, you can **drag and drop** texture file from another window into the graph editor space. 9. After adding image texture nodes and assigning the appropriate files, drag the `Image Texture` sockets to the appropriate sockets in the `Principled BSDF` node: - **Basic texture** or **PBR color/albedo**: connect `Color` to `Base Color`. - **Metallic texture**: connect `Color` to `Metallic`. - **Roughness texture**: connect `Color` to `Roughness`. - **Normal texture** images need to be added to a NormalMap node: 1. Right-click in the graph-editor area and select **Add** > **Vector** > **Normal Map**. 2. Connect Image Texture `Color` to NormalMap's `Color`. 3. Connect NormalMap's `Normal` to PrincipledBSDF's `Normal` socket. > **Success:** After replacing your PBR textures, [export the entire model](/docs/en-us/art/modeling/export-requirements.md#blender) and follow instructions to [import into Studio](/docs/en-us/studio/importer.md) ## Maya To assign Roblox-supported textures to your 3D model in Maya: 1. In the Status Line near the top of the default layout, click on the Hypershade icon. 2. In the Create Window panel on the Hypershade popup, navigate to **Maya** > **Surface** and click **Standard Surface**. A surface node appears in the node editor. 3. If adding a normal map, navigate to **Maya** > **Utilities** and click **Bump 2d**. A node required for normal maps appears in the node editor. 1. Drag and drop your PBR textures to the graph network. 1. If adding a normal map, select the Bump 2d node, and change to Tangent Space Normals. 2. Select your model and navigate to your material you intend to assign. Right-click and hold over the material and select **Assign Material to Selection**. > **Success:** After replacing your PBR textures, [export the model](/docs/en-us/art/modeling/export-requirements.md#maya) and follow instructions to [import into Studio](/docs/en-us/studio/importer.md). --- title: "Calisthenics Tool" url: /docs/en-us/art/modeling/calisthenics-tool last_updated: 2026-06-29T19:33:56Z description: "Calisthenics Tool is a Blender plugin that you can use to verify skinning quality of an asset." --- # Calisthenics Tool > **Warning:** This tool is no longer in active development. You are free to use the tool for any purpose. The tool may be out of date or incompatible with modern Roblox assets. The **Calisthenics Tool** is a supplemental [Blender](https://www.blender.org/) add-on that allows you to quickly test your asset through a set of animation cycles to verify your skinning data. At any point during the animation testing, you can pause and use Blender's skinning Tools, such as [Weight Painting brushes](https://docs.blender.org/manual/en/latest/sculpt_paint/weight_paint/introduction.html), to resolve any skinning imperfections. Skinning your clothing and characters is a critical and often time-intensive process to create high quality assets that move and fit with different character bodies. Similar to the [Layered Clothing Validation Tool](/docs/en-us/art/accessories/validation-tool.md), the Calisthenics Tool can save you time when testing your character models after rigging and skinning. > **Info:** The Calisthenics Tool doesn't provide complete verification of all potential skinning issues. You must also ensure that your custom models meet Studio's [avatar character specifications](/docs/en-us/avatar/character-bodies/specifications.md) and any applicable [layered clothing specifications](/docs/en-us/avatar/layered-accessories/specifications.md) for the best results in your experience. _Walking Animation_ _Running Animation_ _Generic Movements_ ## Installation > **Info:** The Calisthenics Tool plugin is designed for [Blender 3.0+](https://www.blender.org/download/). You can download and modify the tool for any use. To install the Calisthenics Tool in Blender: 1. Download the [CalisthenicsTool.zip](https://github.com/Roblox/avatar/tree/main/Tools/CalisthenicsTool) and save the `.zip` file locally. 2. In Blender, navigate to **Edit** > **Preferences**. 3. In the left sidebar of the **Preferences** window, navigate to the **Add-Ons** section. 4. Click the **Install…** button. A file browser displays. 5. Select the `.zip` and click **Install Add-On**. 6. Using the search bar, search for "Calisthenics Tool" and select the add-on. 7. Enable the add-on by checking the box next to the add-on name. 8. Close the Preferences window. You can now access the Calisthenics Tool by expanding the tool sidebar in the Viewport. _Use the arrow on the side of the Viewport to open the sidebar._ _Access the Calisthenics Tool by selecting the tab in the sidebar tool menu._ ## Use the Calisthenics Tool After installation, you can use the Calisthenics Tool whenever you want to test an R15 character rig with skinning data. With the Calisthenics Tool, you can check how a character would move with generic animations, as well as attach rigid reference accessories to verify attachment during these movements. The tool also includes an option to automatically export your character, removing reference animation and accessory data. You can try out the Calisthenics Tool using an [example character model](../../assets/modeling/skinned-meshes/calisthenic-tool/Fish-Character-No-FACS.fbx). > **Info:** For demonstration purposes, this reference model doesn't contain FACS data. If you require a reference with facial animation, you can download the same model with FACS data from our [reference models](/docs/en-us/avatar/character-bodies/specifications.md#reference-files). ### Test animations When testing your character model, first set the armature in the Calisthenics Tool then select one of the reference animations. It is important to visually verify your skinning quality using various movements and angles to ensure the best results for your model. > **Warning:** If you encounter any errors or warnings when setting armature or playing animations, verify that your model uses an appropriate bone hierarchy and naming convention outlined in Roblox's [custom mesh requirements](/docs/en-us/avatar/character-bodies/specifications.md). To test animations: 1. Open a Blender project with an existing R15 character model, or import an appropriate character model `.fbx` using **File** > **Import** > **FBX (.fbx)**. 2. For easier visualization, hide non-rendered mesh objects, such as armature, cages, and attachments to better preview the reference animations. 3. Set which rig you want to test a reference animation on: 1. In the add-on's **Armature** field, select the Eyedropper. 2. In the Outliner, select the **Armature** object that contains your character's rig data. 4. Click on an animation cycle to preview, such as **Walk**, **Run**, **Move** or **Idle**. 5. Press **Stop Animation** to clear animation data. 6. If you notice any unexpected skinning deformations, pause on the frame and switch to **Weight Paint mode** or use Blender's other skinning tools to resolve. See [Skin a humanoid model](/docs/en-us/art/modeling/skin-a-humanoid-model.md) for additional instruction. ### Test accessory attachments You can add reference attachments to your character model using the **Attach Test Accessory** buttons. These test accessories help preview how rigid accessories attach to your character and how they can move with your model. The tool includes two sets of rigid accessories to apply to your character. _**Normal Size** - Loads normal accessory references._ _**Slender Size** - Loads slender accessory references._ To test accessories: 1. Use the following **Attach Test Accessory** buttons to add or remove sample accessories to your character's attachment points: - **Normal Size**: Adds normal scale test accessories. - **Slender Size**: Adds slender scale test accessories. - **Detach Test Accessories**: Removes any test accessories from your character. 2. If an accessory is not attaching at an expected location, reposition the associated **Attachment object** in your character rig. ### Export models When you are ready to export your model, you can export your character directly through the Calisthenics Tool. The tool automatically clears any of the test data and applies appropriate export settings. > **Info:** Using this tool to export models with [facial animations](/docs/en-us/avatar/dynamic-heads.md) may result in unexpected behavior because the stored FACS data can conflict with the tool's reference animations. To export from the Calisthenics Tool: 1. In the **Export Path** field, set the export directory by clicking the folder icon and browsing to the appropriate file directory. 2. Click the **Export Model** button. The character model `.fbx` populates in the designated folder as `Character_Model_Export.fbx`. > **Warning:** If you are manually exporting your model with Blender's `.fbx` importer instead of the Calisthenics Tool, ensure the following before export: - Detach the Calisthenics Tool's test accessories. - Use the **Stop Animation** button to clear test animations from the timeline. - Return the model to the default pose with no additional pose data. - Verify your settings follow Roblox's [Blender export settings](/docs/en-us/art/modeling/export-requirements.md). --- title: "Export settings" url: /docs/en-us/art/modeling/export-requirements last_updated: 2026-06-29T19:33:56Z description: "Use the appropriate export settings in Maya and Blender to generate Studio-ready .fbx files." --- # Export settings Export your mesh or model as a `.fbx` to take advantage of all of Studio's 3D import features. When rigging or skinning a model, a `.fbx` export contains both the rig and influence data you need to later [import](/docs/en-us/parts/meshes.md#import-meshes) into Studio. Check that your model meets Roblox's [modeling specifications](/docs/en-us/art/modeling/specifications.md) before exporting to ensure Studio compatibility. Specific types of assets, like characters and accessories, have additional specifications: > **Warning:** **If creating other types of 3D models:**- For rigid accessories, see [accessory specifications](/docs/en-us/avatar/rigid-accessories/specifications.md) and [accessory export settings](/docs/en-us/avatar/rigid-accessories/export.md). - For layered accessories, see [layered accessory specifications](/docs/en-us/avatar/layered-accessories/specifications.md) and [layered export settings](/docs/en-us/avatar/layered-accessories/export.md). - For avatar characters, see [avatar specifications](/docs/en-us/avatar/character-bodies/specifications.md) and [avatar export settings](/docs/en-us/avatar/character-bodies/export.md). ## Blender To export the `.fbx` file in Blender: 1. In the topbar, click **File**. A pop-up menu displays. 2. Select **Export**, then **FBX (.fbx)**. The **Blender File View** window displays. 3. On the right-hand side, change the **Path Mode** property to **Copy**, then toggle the **Embed Textures** button.![Blender export sidebar showing Path Mode set to Copy and the Embed Textures button enabled.](../../assets/modeling/skinned-meshes/Blender-Export-Settings-1.png) 4. Set the **Transform** > **Apply Scalings** to **FBX Unit Scale**. If you run into scaling issues on import, see [Blender FBX scaling](/docs/en-us/art/blender.md#adjust-scale-fbx) for alternative approaches.![Blender export sidebar showing Apply Scalings set to FBX Unit Scale.](../../assets/modeling/skinned-meshes/Blender-Export-Settings-5.png) 5. Under the **Armature** section, disable **Add Leaf Bones**.![Blender export sidebar showing Transform Scale set to .01](../../assets/modeling/skinned-meshes/Blender-Export-Settings-3.png) 6. Unless exporting content with animation keyframes, such as a walk cycle or a character with [facial animation data](/docs/en-us/avatar/dynamic-heads.md), disable **Bake Animation**.![Blender export sidebar showing Add Leaf Bones as unchecked](../../assets/modeling/skinned-meshes/Blender-Export-Settings-4.png) 7. Click the **Export FBX** button. ## Maya To export a mesh in Maya as a `.fbx` file: 1. In the topbar, click **File**. A pop-up menu displays. 2. Select **Export All**. The **Export All** window displays. 3. Near the bottom of the window, click the **Files of type** dropdown, then select **FBX export**. 4. On the right-hand side of the window, navigate to the **Options...** section. 5. In the **Geometry** section, enable **Smooth Mesh** and **Referenced Asset Content**. 6. In the **Animation** section, disable **Animation**, unless you need to import an animation to Studio. Some avatar assets require animation. 1. If you are exporting animation, enable **Bake Animation**. 7. If you need to import textures as a `.png`, in the **Embed Media** section, enable **Embed Media**. 8. In the **Advanced Options** section, - Navigate to **Units**, then enable **Automatic**. - Navigate to **Axis Conversion**, then set the **Up Axis** property to **Y**. 9. Click the **Export All** button.![Maya export sidebar showing Bake Animation as unchecked](../../assets/accessories/lc-requirements-maya-settings.png) --- title: "3D Export" url: /docs/en-us/art/modeling/gltf-export last_updated: 2026-06-29T19:33:56Z description: "Importer imports third-party .fbx, .gltf, and .obj 3D model assets into Studio." --- # 3D Export > **Success:** This feature is currently in beta. Enable it through **File** ⟩ **Beta Features** ⟩ **glTF Export**. You can export your 3D Roblox assets as a **.glTF** ([GL Transmission Format](https://en.wikipedia.org/wiki/GlTF)) file format. This open standard format allows for efficient transfer of 3D assets and associated data between Studio and other software, such as Blender or Maya. Some common use cases for .glTF export includes: - Running [Avatar auto setup](/docs/en-us/avatar-setup.md#auto-setup), exporting the output and finalizing the meshes, cages, or rigging in a third-party tool. - Exporting your entire place for high-fidelity scene renders in another 3D application. - Creating 3D content in Studio, such as part-based models or editable meshes and making modifications in an external application. Keep in mind that glTF supports specific 3D [components](#supported-components), has some [limitations](#limitations), and requires correct [permissions](#limitations) to export assets from Studio. Some of these specifications may change over the course of the beta. ## Supported components glTF export supports the following types of 3D data: - Meshes - Textures - Rigging and skinning data - Vertex colors - Cages - Dynamic head FACS data. ## Limitations > **Warning:** See the beta announcement for the latest information on limitations and currently known bugs. At this time, glTF has the following limitations: - Does not support animation data. - Inconsistent support for layered clothing — clothing accessories may not always position correctly and won't deform to avatar bodies outside of Roblox. ## Permissions > **Warning:** See the beta announcement for the latest information on permissions and upcoming changes. You must have the correct permissions to export assets as .glTF. When exporting .glTF, Studio verifies that you have permissions to export that asset and may warn you if there are any conflicts. You can export the following assets: - Assets directly uploaded to your account - Assets directly uploaded to one of your group accounts - Mesh and image assets that have been explicitly shared with you or your group You cannot export the following assets: - Avatar Marketplace assets - Acquired Creator Store assets - Shared collaborator assets where the underlying meshes/images have not been explicitly shared - Other `Open Use` (public) assets that have not been explicitly shared with you or your group ## How to use .glTF export > **Success:** This feature is currently in beta. Enable it through **File** ⟩ **Beta Features** ⟩ **glTF Export**. You can export selected items from the Explorer, or export the entire place. To export from the Explorer: 1. Select the items you intend to export. 2. Right-click and navigate to **Save / Export** > **Export as glTF**. 3. Follow the prompts on screen to fine-tune your export components. Click **Export** when ready. 1. After clicking Export, Studio runs a permission check. If there are any permission conflicts, a permission prompt appears with additional details. 4. In the file browser, select a location to save the .glTF file. To export an entire place: 1. Navigate to **File** > **Export as glTF**. 2. Follow the prompts on screen to fine-tune your export components. Click **Export** when ready. 1. After clicking Export, Studio runs a permission check. If there are any permission conflicts, a permission prompt appears with additional details. 3. In the file browser, select a location to save the .glTF file. --- title: "Custom 3D assets" url: /docs/en-us/art/modeling last_updated: 2026-06-29T19:33:56Z description: "External modeling is the use of a third-party modeling software to create custom 3D assets." --- # Custom 3D assets Meshes are **custom 3D assets** that let you personalize and stylize your game's environment and characters. Unlike parts, which you can directly create in Studio, you need to use a third-party modeling application like [Blender](https://www.blender.org) or [Maya](https://www.autodesk.com/products/maya/overview) to design and model meshes, then import them into Studio. Studio represents imported meshes as `Class.MeshPart` objects, descendants of the `Class.BasePart` class. > **Info:** Many 3D assets are made up of several meshes that are parented to a single `Class.Model` object. Meshes typically fall into one of three types of application in games: - Meshes for **decorating your game's environment**, such as plants, buildings, and vehicles. - Meshes for **individual body parts** for avatars or non-player characters (NPCs). - Meshes for **cosmetic accessories** that characters can equip and wear, such as clothing, props, and weapons. Each mesh type must meet specifications according to how you are going to use the mesh on the platform. For example, while all meshes must meet general mesh specifications, meshes for characters must also meet character specifications that account for rigging and skinning data. For a full list of specifications for different types of meshes, see [Resources](#resources). In addition, if you meet certain account requirements, you can publish and sell collections of meshes that are parented to a single `Class.Model` object. For example, you can publish avatar-ready models on the Marketplace and earn a commission for each sale, or you can sell models on the Creator Store and earn 100% of net proceeds on each transaction. > **Info:** For more information on monetizing meshes, see [Monetize avatar items](/docs/en-us/monetize-avatar.md) and [Creator Store - Distribute and sell assets](/docs/en-us/production/creator-store.md#distribute-and-sell-assets). ![A cluttered interior room with a candle in the forefront and spooky interior lighting from the Mystery of Duvall Drive experience](../../assets/modeling/meshes/Mystery-of-Duvall-Example.png)_Use custom meshes and models to give your environments ambiance and depth_ ![A floating alien creature in space, from the Beyond The Dark experience](../../assets/modeling/meshes/Beyond-The-Dark-Example.png)_Design a custom character unique to your experience_ ![A Roblox avatar wearing a glossy puffy jacket and jeans](../../assets/modeling/surface-appearance/Layered-Clothing-Example.png)_Create custom accessories, clothing, and characters that anyone can wear and use_ ## Supported 3D assets Roblox supports many types of custom 3D assets, as long as they adhere to the [general mesh specifications](/docs/en-us/art/modeling/specifications.md). A basic custom 3D asset consists of at least one `Class.MeshPart` object and one texture: ![A plain white tree mesh without a texture](../../assets/art/Basic-Mesh-Example.png)_A `Class.MeshPart` object sets the shape and geometry of the 3D asset_ ![Various color maps used to apply textures to the tree bark, and inner, middle, and outer leaves](../../assets/art/Basic-Texture-Example.png)_A texture image map applies a surface appearance and color_ ![The tree mesh and the image maps combined to create a single tree](../../assets/art/Basic-Mesh-Combined-Example.png)_The mesh and texture combine to make a unique custom 3D asset_ Studio also supports custom 3D assets that include [rigging and skinning](#rigging-and-skinning) data, [PBR textures](#pbr-textures), and other [Studio-related objects](#studio-related-objects) like cage meshes and attachments. Many of these various components are required if you are creating avatar character models or accessories. ### Rigging and skinning A **rigged mesh** is a `Class.MeshPart` object with an internal poseable skeleton rig and bone structure. Rigged meshes allow mesh surfaces to rotate and move where internal bone joints are placed within a model, such as a character's knee or elbow. Skinning a rigged mesh allows the mesh to bend organically, imitating the natural way joints would move in real life. _Without skinning, the entire head mesh rotates on a single axis_ _With skinning, the head mesh bends naturally at the neck, and the bottom of the neck stays connected to the torso_ For more information on rigging and skinning, see [Rigging and skinning](/docs/en-us/art/modeling/rigging.md). ### PBR textures **Physically-Based rendering** (PBR) textures allow you to represent realistic shading and lighting by using multiple types of texture images, or **maps**, on a single 3D asset. Combining multiple texture maps can more accurately simulate color, roughness, and reflectivity in any lighting environment, and can enhance the visual elements of your assets and environment. ![A realistic looking leafy bush with shadows and depth.](../../assets/modeling/surface-appearance/SurfaceAppearance-Example-1.jpg)![A realistic looking rock with moss on the top.](../../assets/modeling/surface-appearance/SurfaceAppearance-Example-3.jpg) For more information on PBR textures, see [PBR textures](/docs/en-us/art/modeling/surface-appearance.md). ### Studio-related objects Studio automatically converts certain types of objects found in 3D modeling files as specific workspace objects in the experience. These are typically used when creating a character or accessory, and configuring these in your modeling software can sometimes be the primary way of correctly setting up these Studio objects. The following objects are automatically created in Studio if they are detected by the 3D Importer: - `Class.Attachment` - Created when Studio detects mesh objects that include `_Att` at the end of their name. - `Class.WrapTarget` - Created when Studio detects mesh objects that include `_OuterCage` at the end of their name. - `Class.WrapLayer` - Created when Studio detects a mesh object with `_InnerCage` and similarly named mesh object with `_OuterCage` at the end of their names. - `Class.FaceControls` - Created when Studio detects an avatar character head and the appropriate facial animation data in the model. ## Resources There are a variety of resources available for creators of all backgrounds to get started with custom 3D assets. If you are interested in specific avatar creation topics, use the following table to find guides and resources that best match your needs: | Topic | Resources | | --- | --- | | Reference files | [Example models, project kits, and templates](/docs/en-us/art/modeling/project-files.md) | | Technical specs | [.FBX export settings](/docs/en-us/art/modeling/export-requirements.md)

[General mesh specifications](/docs/en-us/art/modeling/specifications.md)

[Avatar specifications](/docs/en-us/avatar/character-bodies/specifications.md)

[Accessory specifications](/docs/en-us/avatar/rigid-accessories.md)

[Marketplace policy](/docs/en-us/marketplace/marketplace-policy.md) | | Bodies and clothing creation | [Accessories overview](/docs/en-us/avatar/rigid-accessories.md)

[Bodies overview](/docs/en-us/avatar/character-bodies.md)

[Face accessory creation](/docs/en-us/art/characters/facial-animation/create-face-accessories.md)

[Accessory Fitting Tool](/docs/en-us/avatar/accessory-fitting-tool.md)

[Accessory specifications](/docs/en-us/avatar/rigid-accessories/specifications.md)

[Marketplace requirements](/docs/en-us/marketplace/marketplace-policy.md) | | Texturing | [Texture specifications](/docs/en-us/art/modeling/texture-specifications.md)

[PBR textures](/docs/en-us/art/modeling/surface-appearance.md) | | Rigging and skinning | [Rigging and skinning overview](/docs/en-us/art/modeling/rigging.md)

[Basic mesh rigging](/docs/en-us/art/modeling/rig-a-simple-mesh.md)

[Facial bone rigging](/docs/en-us/art/characters/facial-animation/create-basic-heads.md#rigging)

[Facial bone skinning](/docs/en-us/art/characters/facial-animation/create-basic-heads.md#skin-face-bones)

[Avatar rigging requirements](/docs/en-us/avatar/character-bodies/specifications.md#rigging)

[Automatic skinning transfer](/docs/en-us/avatar/automatic-skinning-transfer.md) | | Facial animation and live heads | [Basic head creation](/docs/en-us/art/characters/facial-animation/create-basic-heads.md)

[Face accessory creation](/docs/en-us/art/characters/facial-animation/create-face-accessories.md)

[FACS pose references](/docs/en-us/avatar/dynamic-heads/facs-poses-reference.md) | | Testing and validation | [Calisthenics Tool](/docs/en-us/art/modeling/calisthenics-tool.md)

[Clothing Validation Tool](/docs/en-us/art/accessories/validation-tool.md) | | Publishing and Marketplace | [Upload to Marketplace](/docs/en-us/marketplace/publish-to-marketplace.md)

[Marketplace policy](/docs/en-us/marketplace/marketplace-policy.md)

[Fees and commissions](/docs/en-us/marketplace/marketplace-fees-and-commissions.md) |
--- title: "Material references" url: /docs/en-us/art/modeling/material-reference last_updated: 2026-06-29T19:33:56Z description: "PBR texture examples and various texture map values." --- # Material references When combining different combinations of metalness and roughness values with your PBR textures, Roblox can render virtually every type of surface with a high level of realism. The following includes various material values for common materials and [clothing examples](#clothing-examples) that you can use when creating your own PBR surfaces. The following figure illustrates the various base surface types you can achieve with various roughness and metallic values: ## Common materials You can use the following material reference values as a baseline for creating your own custom surfaces: _**Glossy Plastic**Roughness: 0.1 Metalness: 0.0_ _**Matte Plastic**Roughness: 0.4 Metalness: 0.0_ _**Rubber**Roughness: 0.5-0.6 Metalness: 0.0_ _**Latex**Roughness: 0.0 Metalness: 0.0_ _**Glossy Wood**Roughness: 0.2-0.3 Metalness: 0.0_ _**Worn Wood**Roughness: 0.7-0.9 Metalness: 0.0_ _**Cotton, Canvas, Denim**Roughness: 0.8-0.9 Metalness: 0.0_ _**Polyester, Nylon**Roughness: 0.6-0.8 Metalness: 0.0_ _**Wool, Linen**Roughness: 0.9-0.096 Metalness: 0.0_ _**Silk, Satin**Roughness: 0.4-0.6 Metalness: 0.4-0.6_ _**Velvet, Suede**Roughness: 0.6-0.8 Metalness: 0.4-0.6_ _**Glossy Leather**Roughness: 0.2-0.4 Metalness: 0.0_ _**Worn Leather**Roughness: 0.5-0.7 Metalness: 0.0_ _**Fur**Roughness: 0.65-0.75 Metalness: 0.0_ _**Scales**Roughness: 0.5-0.6 Metalness: 0.0_ _**Bone**Roughness: 0.5 Metalness: 0.0_ _**Skin**Roughness: 0.65 Metalness: 0.0_ _**Wax**Roughness: 0.38 Metalness: 0.0_ _**Marble**Roughness: 0.375 Metalness: 0.0_ _**Jade**Roughness: 0.15 Metalness: 0.0_ _**Painted Metal**Roughness: 0.3 Metalness: 0.0_ _**Pure Metals (Gold, Silver, Bronze)**Roughness: 0.2-0.3 Metalness: 1.0_ _**Worn Metals**Roughness: 0.2-0.4 Metalness: 1.0_ _**Glass Mirror**Roughness: 0.0 Metalness: 0.1_ ## Clothing examples Layered clothing can take advantage of PBR textures to achieve realistic and visually popping cosmetics for avatar characters. Use the following reference examples to compare how various material values are used to achieve a certain cosmetic effect and its comparable real-time rendering in Substance Painter and Roblox Studio: | Substance Painter render | Roblox Studio render | Material values | | --- | --- | --- | | | | **Leather fabric:**
Roughness: 0.62
Metalness: 0.0

**Brass zipper:**
Roughness: 0.25
Metalness: 1.0
| | | | **Metallic puffer fabric:**
Roughness: 0.62
Metalness: 1.0

**Brass zipper and buttons:**
Roughness: 0.482
Metalness: 1.0

**Fur hood:**
Roughness: 0.75
Metalness: 0.0

**Cotton band:**
Roughness: 0.8
Metalness: 0.0
| | | | **Silk fabric:**
Roughness: 0.52
Metalness: 0.3

**Steel zipper and buttons:**
Roughness: 0.25
Metalness: 1.0

**Cotton band:**
Roughness: 0.9
Metalness: 0.0

| | | | **Denim fabric:**
Roughness: 0.9
Metalness: 0.0

**Tarnished steel buttons:**
Roughness: 0.33
Metalness: 1.0
|
--- title: "Modeling project files and references" url: /docs/en-us/art/modeling/project-files last_updated: 2026-06-29T19:33:56Z description: "Download various modeling-related project files and reference files." --- # Modeling project files and references The following `.fbx`, `.blend`, and `.ma` project files are available to use as templates or as reference: Maple leaf tree ![A green maple tree mesh](../../assets/art/resources/Tree-Resource.png) A skinned tree model with the soil, branches, and leaves as separate meshes bound to a single armature. This is packaged with UV textures that can be applied with `Class.SurfaceAppearance`. This is an advanced version of the tree model used in the [Skin a simple mesh](/docs/en-us/art/modeling/skin-a-simple-mesh.md) guide. [Download](../../assets/modeling/skinned-meshes/MapleLeafTree_S3.zip) Shoebot ![A white round robot with arms and orange trim.](../../assets/art/resources/Shoebot-Resource.png) A robot non-player character used in the [Beyond The Dark](https://www.roblox.com/games/7208091524/Beyond-the-Dark-Vistech-Showcase) showcase. [Download](../../assets/modeling/meshes/reference-files/shoebot-base-model.fbx) Creature ![A black and blue alien creature with tentacles.](../../assets/art/resources/Creature-Resource.png) Creature model from the [Beyond The Dark](https://www.roblox.com/games/7208091524/Beyond-the-Dark-Vistech-Showcase) showcase. This model is made up of 11 meshes and over 50 bones. [Download](../../assets/modeling/skinned-meshes/CreatureModel.fbx) ## Avatar and accessories The following include examples and templates utilizing Roblox's required [avatar components](/docs/en-us/avatar/character-bodies.md#components-of-an-avatar). Many of these project files and references require additional configuration in your modeling software before importing into Studio as a complete avatar character. See [Character bodies](/docs/en-us/avatar/character-bodies.md) for additional resources and template usage instruction. ### References Fish person ![A blue-green scaly creature with a bulb-ish head ornament.](../../assets/art/resources/Fish-Person-Resource.png) A rigged and skinned humanoid character model with a full body cage, facial animation rig, and associated PBR texture maps. If this asset were to be uploaded to the Marketplace, the bulb antenna must be a separate head accessory and not part of the character's head geometry. [Download](../../assets/avatar/dynamic-heads/reference-files/Fish-Person.zip) Goblin ![A brown goblin with bright green eyes.](../../assets/art/resources/Goblin-Resource.png) A rigged and skinned humanoid character model with a full body cage, facial animation rig. [Download](../../assets/avatar/dynamic-heads/reference-files/GoblinCharacter.zip) Blocky ![A colorless blocky-like character.](../../assets/art/resources/Blocky-Resource.png) A Blocky character model with an animatable head and a full body cage. [Download](../../assets/avatar/dynamic-heads/reference-files/BlockyCharacter.fbx) Lola ![A pink-haired character complete with brown jacket, goggles, leggings, and pants.](../../assets/art/resources/Lola-Resource.png) A skinned R15 character created from the [Skin a humanoid model](/docs/en-us/art/modeling/skin-a-humanoid-model.md) guide. Since this reference model doesn't yet have [inner and outer cage mesh data](/docs/en-us/avatar/character-bodies/specifications.md#outer-cages), this model can't equip layered clothing or accessories. [Download](../../assets/modeling/skinned-meshes/Lola.fbx) Tshirt - uncaged ![A tie-dye tshirt with a rainbow of colors in a general spiral toward the torso.](../../assets/art/resources/Tshirt-Resource.png) Uncaged example clothing ready for caging in a 3D modeling software. [Download](../../assets/accessories/reference-files/Tshirt-model.fbx) Clothing examples Caged 3D accessory models and associated PBR textures. Ready for import into Studio or in a modeling tool. [Download](../../assets/accessories/reference-files/Additional-FBX-assets.zip) Caging Examples Additional caged clothing items from How to cage Roblox's 3D [clothing video guide](https://www.youtube.com/watch?v=QwZaA9Gc-WQ). [Download](../../assets/accessories/reference-files/Caging-examples.zip) Classic mannequin ![A white untextured mannequin in the blocky style.](../../assets/art/resources/Body-Scale-Classic.png) A blank mannequin using Roblox's [Classic](/docs/en-us/avatar/character-bodies/specifications.md#classic) avatar proportions. Use this reference to aid your creation process for accessories, clothing, and characters in third-party applications. The caged `.fbx` contains the individual outer body cages for the body and may not import correctly into Studio without modification. [Model](../../assets/art/reference-files/ClassicMannequin.fbx) [With cage](../../assets/art/reference-files/ClassicMannequin_With-Cages.fbx) Rthro mannequin ![A white untextured mannequin in a larger build humanoid style.](../../assets/art/resources/Body-Scale-Rthro-Normal.png) A blank mannequin using Roblox's [Rthro Normal](/docs/en-us/avatar/character-bodies/specifications.md#normal) avatar proportions. Use this reference to aid your creation process for accessories, clothing, and characters in third-party applications. The caged `.fbx` contains the individual outer body cages for the body and may not import correctly into Studio without modification. [Model](../../assets/art/reference-files/RthroMannequin.fbx) [With cage](../../assets/art/reference-files/RthroMannequin_With-Cages.fbx) Rthro slender mannequin ![A white untextured mannequin in a slimmer build humanoid style.](../../assets/art/resources/Body-Scale-Rthro-Slender.png) A blank mannequin using Roblox's [Rthro Slender](/docs/en-us/avatar/character-bodies/specifications.md#slender) proportions. Use this reference to aid your creation process for accessories, clothing, and characters in third-party applications. The caged `.fbx` contains the individual outer body cages for the body and may not import correctly into Studio without modification. [Model](../../assets/art/reference-files/RthroSlenderMannequin.fbx) [With cage](../../assets/art/reference-files/RthroSlenderMannequin_With-Cages.fbx) ## Templates These project files require additional configuration before they are ready for Studio import. See [Create with templates](/docs/en-us/art/characters/creating.md) for additional information. > **Warning:** If you are using Roblox's avatar template files, you must perform the [cleanup steps](/docs/en-us/art/characters/creating/combine-head-geometry.md) in order for the assets to properly validate before publishing to the Marketplace. ![A male and female presenting pair with a prominently round shaped head.](../../assets/art/avatar/templates/Round-Head-Templates.png) Blender and .fbx template files with pre-baked avatar components. [Male](../../assets/art/reference-files/RoundMale.zip) [Female](../../assets/art/reference-files/RoundFemale.zip) ![A male and female presenting pair with a prominently square shaped head.](../../assets/art/avatar/templates/Square-Head-Templates.png) Blender and .fbx template files with pre-baked avatar components. [Male](../../assets/art/reference-files/SquareMale.zip) [Female](../../assets/art/reference-files/SquareFemale.zip) ![A male and female presenting pair with a prominently elongated snout and mouth.](../../assets/art/avatar/templates/Muzzle-Head-Templates.png) Blender and .fbx template files with pre-baked avatar components. [Male](../../assets/art/reference-files/MuzzleMale.zip) [Female](../../assets/art/reference-files/MuzzleFemale.zip) ![A male and female presenting pair with realistic body proportions and facial shape.](../../assets/art/avatar/templates/SemiRealistic-Templates.png) Blender and .fbx template files with pre-baked avatar components. [Male](../../assets/art/reference-files/SemiRealisticMale.zip) [Female](../../assets/art/reference-files/SemiRealisticFemale.zip) ![A male and female presenting pair with larger anime-like eyes and prominently oval shaped faces.](../../assets/art/avatar/templates/Anime-Templates.png) Blender and .fbx template files with pre-baked avatar components. [Male](../../assets/art/reference-files/AnimeMale.zip) [Female](../../assets/art/reference-files/AnimeFemale.zip) ![A humanoid cartoonish figure with exaggerated features, including a prominent forehead, nose, and chin.](../../assets/art/avatar/templates/Caricature-Templates.png) Blender and .fbx template files with pre-baked avatar components. [Download](../../assets/art/reference-files/Caricature.zip) ![A cartoonish figure with a round head and exaggerated forehead.](../../assets/art/avatar/templates/Stylized-Templates.png) Blender and .fbx template files with pre-baked avatar components. [Download](../../assets/art/reference-files/StylizedHuman.zip) ### Project files R15 rig and attachments ![A Blender visualization of bones and attachment meshes in a neutral humanoid pose.](../../assets/art/resources/Rig-And-Attachments-Resource.png) Standard armature rig template for Blender. Use this template for rigging **bodies** and **clothing** items. [Download (.blend, .ma, .fbx)](../../assets/modeling/meshes/reference-files/Rig_and_Attachments_Templates.zip) Cages for clothing ![A humanoid shaped mesh in a neutral A-pose.](../../assets/art/resources/Clothing-Cage-Resource.png) Project files for creation, includes a full-body inner and outer cage mesh for creation of **clothing** items. [Download (.blend, .ma, .fbx)](../../assets/modeling/meshes/reference-files/Clothing_Cage_Templates.zip) Cages for bodies ![A humanoid shaped mesh in a neutral A-pose. Each section of the mesh is colored differently based on the body part.](../../assets/art/resources/Body-Cages-Resource.png) Project files for creation, includes the 15 individual body part cages required for caging your avatar **bodies**. [Download (.blend, .ma, .fbx)](../../assets/modeling/meshes/reference-files/Body_Cage_Templates.zip) Combined project files Template file containing all content from previous templates, includes rig skeleton, body cages, attachment points. Use this template to rig and cage bodies and clothing. [Download (.blend, .ma, .fbx)](../../assets/modeling/meshes/reference-files/Combined_Templates.zip) ### Add-ons and tools Blender Studio plugin Open-source Blender Studio add-on that allows you to upload assets directly from Blender to Studio. [Link to Github](/docs/en-us/art/modeling/roblox-blender-plugin.md) Blender validation tool Blender add-on for verifying avatar technical compatibility before importing into Studio. [Link to instructions](/docs/en-us/art/accessories/validation-tool.md) Blender Calisthenics tool Blender add-on for checking skinning data on characters and clothing. [Link to instructions](../../assets/modeling/skinned-meshes/calisthenic-tool/CalisthenicsTool.zip) --- title: "Reimport" url: /docs/en-us/art/modeling/reimport last_updated: 2026-06-29T19:33:56Z description: "Reimport allows you to update an existing custom mesh and textures without generating a new instance." --- # Reimport **Reimport** allows you to update existing custom models and their textures from an external 3D file. Unlike regular [import](/docs/en-us/studio/importer.md#import-files), which creates brand new instances, the reimport function updates existing objects non-destructively to help support the iterative nature of 3D development. Along with asset updates, reimport remembers the file and settings used to import the original asset. This means you can easily update an imported asset with a single click or hotkey without going through all the initial import settings. This memory persists across Studio sessions. ## Supported instances Reimport supports the following entry points in the Explorer. Reimport also supports [packages](/docs/en-us/projects/assets/packages.md), allowing you to reimport batches of asset instances at once. - `Class.Model` - The reimport feature scans the container, updating any changed `Class.MeshPart` and `Class.SurfaceAppearance` instances and adding newly created ones, while leaving your existing properties unchanged. - `Class.MeshPart` - Reimport replaces meshes based off of mesh names saved in Studio and the external 3D file. For specific behavior, see [MeshPart detection](#meshpart-detection). - `Class.SurfaceAppearance` - Reimport replaces images for PBR textures based off of filenames. For specific behavior, see [SurfaceAppearance detection](#surfaceappearance-detection). - `Class.SurfaceAppearance` or `Class.Decal` (selected directly) - Reimport uploads all texture maps found in the same folder as the selected file in one operation. > **Info:** Reimport memory is local to your device. A Reimport ID is stored on the `Class.Model` instance and referenced locally. Keep in mind the following: - Filepaths are never saved in the place file. - Reimport supports different reimport paths on different devices for the same instance. - Reimport supports copy-pasting, saving and loading from rbxm/rbxl, and Asset Manager workflows. ### MeshPart detection Reimport updates meshes based off of the path and name of the 3D source file, and the path and name of the mesh object in Studio. Reimport only updates the mesh content and transform properties and preserves other Roblox-specific properties, including `Class.MeshPart.CollisionFidelity|CollisionFidelity` and `Class.MeshPart.RenderFidelity|RenderFidelity`. If the incoming mesh has no texture assigned, reimport keeps the existing `Class.MeshPart` texture rather than clearing it. Reimport relies on matching mesh names between the Studio and the 3D source file: - If a mesh's `path/name` in the 3D source file **matches** a `Class.MeshPart` `path/name` in Studio, reimport updates the matching `Class.MeshPart`. - If a mesh's `path/name` in the 3D source file **doesn't exist** as `Class.MeshPart` `path/name` in Studio, reimport creates a new `Class.MeshPart` within the `Class.Model`. - If a `Class.MeshPart` `path/name` in Studio **doesn't exist** in the 3D source file, reimport does not modify the `Class.MeshPart` in Studio. #### Known Asset Cache Studio uses a **Known Asset Cache**, which helps detect duplicate imports in the same Studio session. If an imported or reimported asset is detected, Roblox reuses the existing asset ID instead of creating and uploading a new asset. If you attempt to reimport a single mesh object, but the 3D file contains a larger number of meshes or textures, Roblox only applies a single asset upload for that object. #### Duplicates Reimport detects duplicate meshes, and doesn't reupload matching meshes that are the same in both Studio and the 3D source file. In the scenario where a mesh in Blender is renamed from "Mesh1" to "Mesh2", reimport creates a new `Class.MeshPart` named "Mesh2" and doesn't apply changes to the original "Mesh1". #### Pivot points In cases where you may want to use the pivot point of the `Class.MeshPart` and not the `Class.Model`, right-click any `Class.MeshPart` with reimport information and select **Reimport** > **Reimport relative to this**. **Reimport relative to this** triggers a reimport for the entire model using the pivot point of right-clicked `Class.MeshPart` as an anchor instead of the `Class.Model` pivot point. You can use this for models that have custom pivots by reimporting relative to a mesh that has not changed position in the 3D file. ### SurfaceAppearance detection Reimport can quickly batch update the textures of a model with [PBR textures](/docs/en-us/art/modeling/surface-appearance.md) based off of the naming convention of the image files. Reimport detects a set of known suffixes for color, metalness, normal, and roughness maps. You can find the full list of supported suffixes in the following table: | Map Type | Suffixes | Example | | --- | --- | --- | | Color | diffuse, diff, albedo, base, col, color, alb | Tree_**Color**.png | | Metalness | metallic, metalness, metal, mtl, met | Tree_**Metal**.png | | Roughness | roughness, rough, rgh | Tree_**Rough**.png | | Normal | normal, nor, nrm, nrml, norm | Tree_**Normal**.png | When reimporting an asset with a `Class.SurfaceAppearance` selected, a file dialog displays allowing you to select a new image file to use as a PBR texture. Reimport automatically detects other PBR images in the directory based off of naming and maps them to the `Class.SurfaceAppearance` object. Texture files without a recognized material map suffix are accepted as the color map for `Class.SurfaceAppearance` reimport. ## Reimport models When you bring an asset into Studio through the [importer](/docs/en-us/studio/importer.md#import-files), reimport automatically stores the filepath, import preset, and upload inventory, so subsequent reimports require no manual setup. To reimport a configured `Class.Model`: 1. Right-click the `Class.Model` and select **Reimport** > **Reimport**. ![Right-click menu of an imported model, displaying reimport options](../../assets/modeling/meshes/Reimport-menu.png) 1. Alternatively, when selecting the object, use the reimport hotkey `Alt``Shift``R`. 2. A loading indicator appears after selecting reimport. If there are any issues, an error dialog displays with a **Configure Reimport Settings** button that opens the Configure dialog. If a `Class.Model` has no saved reimport configuration (for example, a model imported on a different device or before reimport was available), triggering **Reimport** opens the Configure dialog instead of starting the reimport. You can also open it directly at any time through **Reimport** > **Configure**. In this Configure dialog, you can set the file path, upload target, and import presets. > **Warning:** Since reimport relies on matching mesh paths/names to update a `Class.Model` from a 3D file, it’s important to maintain unique naming of meshes and their paths in your external 3D tools. > > In Studio, you should also maintain the naming/tree structure of the `Class.MeshPart` objects within your model in order for reimport to update correctly. --- title: "Rig a humanoid model" url: /docs/en-us/art/modeling/rig-a-humanoid-model last_updated: 2026-06-29T19:33:56Z description: "Explains the process for rigging a humanoid R15 model in Blender." --- # Rig a humanoid model An R15 humanoid character model, such as those that make up user avatar characters, is made up of 15 individual mesh objects. Similar to rigging a simple mesh, you can bind or parent a group of meshes to an internal rig. Models made up of multiple meshes require additional steps to rig in a third-party modeling tool such as [Blender](https://www.blender.org) or [Maya](https://www.autodesk.com/products/maya/overview). This advanced guide covers the process of rigging a humanoid model in Blender using a provided template and humanoid model. This rigging step is required before [skinning the humanoid model](/docs/en-us/art/modeling/skin-a-humanoid-model.md). You should be familiar with [rigging a basic model](/docs/en-us/art/modeling/rig-a-simple-mesh.md) before continuing. To rig a humanoid model in Blender: - [Import a character model](#import-model) into a template file to easily access a premade R15 bone structure. - [Create, size, and position bones](#set-bones-and-armature) symmetrically for a humanoid character. - [Parent](#parent-armature) multiple meshes to a single armature to bind the skeleton rig. - [Assign full influence](#assign-meshes-to-bones) to each mesh object and bone by assigning influences. > **Info:** This guide uses a downloadable [Rig and Attachment Template Blender Project](../../assets/modeling/meshes/reference-files/Rig_and_Attachments_Templates.zip), a reference [Lola Character model](../../assets/modeling/meshes/reference-files/lola-base-model.fbx), and [Blender version 3.0](https://www.blender.org/download/releases/3-0/). If you are using another version of Blender, there may be minor differences in UI and settings. ## Set up Blender To begin creating a humanoid rigged mesh, first set up the following in your Blender project: - [Import](#import-model) the Lola character model in the Mannequin Template Blender project. - Set up your [Viewport visualizations](#viewport-visualizations) to optimize the rigging process. ### Import model When rigging a model, make sure the character model you are using follows Studio's [Avatar character specifications](/docs/en-us/avatar/character-bodies/specifications.md). For this guide, import a [Lola reference model](../../assets/modeling/meshes/reference-files/lola-base-model.fbx) into the mannequin template project. To import your model: 1. In Blender, navigate to **File** > **Open** and select `Rig_and_Attachments_Template.blend`. 2. Navigate to **File** > **Import** > **FBX (.fbx)** and import the reference Lola model file. 3. If required, scale the model to roughly match the scale of the armature bone structure. You can select all the mesh geometries in the Outliner and press `G` to reposition and `S` to scale the mesh objects by dragging your mouse. ### Viewport visualizations For better visualization and access to your bone objects, set your bone objects to always be displayed in front of the Viewport. To set the bone visualization: 1. In the **Viewport**, click on any of the bones in your armature. 2. In the **Properties Editor** panel, select the **Object Data Properties** tab. 3. Expand **Viewport Display**, navigate to the **Show** property, then enable **In Front**. At any point during the skinning process, you can also toggle one of the various material previews options in the top right of your 3D Viewport to change the visualization of your character model, such as enabling X-ray or texture view. ## Set bones and armature In this guide, set up X-Axis Mirroring to make symmetrical changes to the left and right bones before repositioning the bones to the imported model. Try to maintain symmetry whenever possible when rigging a model. > **Warning:** If modifying bones or creating a new bone structure, keep in mind the [specific bone hierarchy and naming requirements](/docs/en-us/avatar/character-bodies/specifications.md#humanoid-rigs) for R15 character models. ### Enable x-axis mirror The X-Axis Mirror setting allows you to maintain symmetry with your left and right bones by mirroring changes to their positions. To reduce issues with posing and animating, it is important to retain symmetry with your bone structure whenever possible. To set up X-Axis Mirror: 1. In **Object Mode**, select your **armature** by clicking on any bone. 2. Switch to **Edit Mode** (`Tab`). 3. In the Viewport's right sidebar, extend the Tool panel and enable **X-Axis Mirror**. ### Position bones With the model added to the project, you can reposition the bones provided in the template to match the character structure. In most cases, each bone should be positioned in the center of their corresponding mesh objects, such as the Head bone being centered within the Head_Geo mesh object. Bone positioning may differ based on use-case and may need to be revisited after weight painting and testing if a certain pose or animation doesn't work as intended. To position your individual bones: 1. In Object Mode, click on the **bone** you intend to reposition to highlight it. 2. From the Mode dropdown, switch to **Edit Mode**. 3. Click on the **tip** of the bone so that the tip is highlighted and press `G`. The top of the bone moves with your cursor. 4. Pull this bone to align with the interior center of the model, then click to set the bone's position. 5. Click and hold your mouse's scroll wheel or use one of the various perspective views to verify that the bone is within the mesh object. ### Parent armature After you position the bone structure, you need to parent the bone armature to the model. You must parent all 15 mesh objects to one armature. For this example, the mesh objects are parented to the armature with Empty Groups to illustrate the vertex group assignment and weight painting process in the following [Skin a humanoid model](/docs/en-us/art/modeling/skin-a-humanoid-model.md) guide. To parent the model to the armature: 1. Switch to **Object Mode**. 2. In the Outliner, filter your mesh objects by typing **"geo"** in the search bar. 3. Select all of the mesh objects in the Outliner by holding `Shift` and clicking on the first and last mesh objects. 4. With the meshes highlighted, hold `Shift` and click on the Armature object in the Viewport or Outliner. 5. Right click in the Viewport and select **Parent** > **With Empty Groups**. ## Assign meshes to bones Now that you have connected your armature to the mesh object, you can assign the vertices of your individual limbs to be fully influenced by one corresponding bone. After this process is completed, the model will be ready for skinning. See [Skin a humanoid model](/docs/en-us/art/modeling/skin-a-humanoid-model.md) for instructions on applying multiple bone influences to a mesh. > **Warning:** The **Root** and **HumanoidRootNode** parent bones in a humanoid rig should not have any influences applied to them. Any added influences are dropped when importing into Studio. To assign full influence to the head mesh: 1. In **Object Mode**, click on the **head mesh object** to highlight it. 2. Switch to **Edit Mode**. 3. Select all the vertices of your mesh object by pressing `A`. 4. After all the head vertices are highlighted, navigate to the **Object Properties** panel on the right side of the screen. 5. In the **Vertex Group** section of the panel, select the name of the bone you want to assign the Head geometry to and click **Assign**. You can quickly iterate through all of your mesh objects in this mode by selecting the next mesh object in the Outliner and assigning it to the appropriate Vertex Group. For this guide, **assign all of your character's mesh objects to their appropriate bone**. To quickly assign vertex groups to the rest of the body meshes: 1. In the **Outliner** panel, **click** the dot next to the object you intend to edit. The dot switches to the active edit icon when pressed. 2. If the vertices are not highlighted, press `A` to select all vertices. 3. In the **Object Properties** panel > **Vertex Groups** section, select the appropriate vertex group and click **Assign**. You can use the search bar to find the specific vertex group name. ## Test You can test and pose bones and their assigned mesh objects in Pose Mode. It is important to test your model after applying or editing any influences. To navigate to Pose mode and test your poses: 1. In **Object Mode**, select any part of your model. 2. Click on the Mode dropdown, then switch to **Pose** mode. 3. Hold `Shift` and click the bone you want to test to highlight it, then press `R` to test the rotation. 4. Press `Alt``A` (`⌥``A`) to deselect the current bone, then reselect and test another bone. > **Info:** To reset the rotation of a selected bone to neutral, press `Alt`+`R`. While rotating, you can also press your mouse's scroll wheel to change the axes of your rotation. At this stage, if all of your mesh objects are influenced by their corresponding bones, you can [export](/docs/en-us/art/modeling/export-requirements.md) this rigged model as an `.fbx` for use in Studio or continue on to the next stage of [Skin a humanoid model](/docs/en-us/art/modeling/skin-a-humanoid-model.md). --- title: "Rig a simple mesh" url: /docs/en-us/art/modeling/rig-a-simple-mesh last_updated: 2026-06-29T19:33:56Z description: "Explains the process for rigging a basic model in Blender." --- # Rig a simple mesh You can create a **rigged mesh** using a third party modeling tool such as [Blender](https://www.blender.org) or [Maya](https://www.autodesk.com/products/maya/overview). You should rig a model after designing it, but prior to [exporting](/docs/en-us/art/modeling/export-requirements.md). To rig a simple model in Blender: - [Set up Blender](#set-up-blender) to Studio's relative scene units before importing a model to rig. - [Create and reposition bones](#create-a-bone-structure) within the mesh object. - [Parent the mesh to the armature](#parent-armature) to bind the bone armature to the mesh object. - [Assign vertices to specific bones](#assign-vertices-to-bones) to define which parts of the mesh is driven by which bones. - [Test your rigged mesh](#test) to ensure that the bones are properly positioned and influenced within the mesh. > **Info:** This guide uses a downloadable [example robot model](../../assets/modeling/meshes/reference-files/shoebot-base-model.fbx) and [Blender version 3.0](https://www.blender.org/download/releases/3-0/). If you are using another version of Blender, there may be minor differences in UI and settings. ## Set up Blender To start the process of creating a rigged mesh, first set up the following in your Blender project: 1. Open a new **General** project in Blender. 2. Select the default shape, camera, and lights, then press `Delete`. ### Import a model For this example, import a [robot model](../../assets/modeling/meshes/reference-files/shoebot-base-model.fbx) into Blender as your mesh object. To import an existing `.fbx` model: 1. In the topbar, click **File**. A pop-up menu displays. 2. Select **Import**, then the file format of the model you're importing. For this example, select **FBX (.fbx)** and the `.fbx` reference model. The model displays on the 3D viewport.![Imported Model](../../assets/modeling/meshes/rigging-simple/rigging-r1-imported.png) ## Create a bone structure Now that your model is within Blender, you must add an **armature** and **bones** to your mesh object. An **armature** is a skeleton-like rigging object that acts as a container for bones, while **bones** are objects that control the movement and deformation of the group of vertices, or **vertex group**, that surround the bone. ### Add an armature An armature is a structure required to add bones to your mesh. After you add in an armature, you can create and reposition any number of bones inside of your mesh object. To add an armature: 1. At the top of the 3D viewport, select **Add** → **Armature**. 2. For better visualization of the bones, in the left-hand navigation of the **Properties Editor**, navigate to **Armature Object Properties**. 3. In the **Viewport Display** section, navigate to the **Show** property, then enable **In Front**. ### Add and position bones When you add an armature to your project, Blender automatically adds one bone to the armature at a default position and scale which will act as the root bone. To add and position two additional bones: 1. Click on the **bone** to highlight it. 2. At the top of the 3D viewport, click on the Mode dropdown, then switch to **Edit Mode**. 3. In the Viewport, click **Add** → **Single Bone**. Perform this step twice. 4. In the Viewport or Outliner, click on **a newly created bone** to highlight it. 5. Press `G` and use your mouse to drag the bone onto the **right arm**. 6. Click on the second bone and press `G` to drag the bone on the **left arm**. 7. Click on the top of the **right bone** so that the tip is highlighted. Press `G` to drag and orient the bone horizontally toward the right. 8. Click on the top of the **left bone** so that the tip is highlighted. Press `G` to drag and orient the bone horizontally toward the left. 9. In the Outliner, double-click and **rename** your bones so they can be easily referenced later. ## Parent armature After you create and position the bone structure, you need to connect the armature to the mesh object by parenting the armature to the mesh object. In this guide, you should also use Blender's **Automatic Weights** function when you are parenting an armature, as it automatically adds weights and influences to your mesh. To parent an armature to a mesh: 1. At the top of the 3D viewport, click on the Mode dropdown, then switch back to **Object Mode**. 2. Press `Alt``A` (Windows) or `⌥``A` (Mac) to deselect every object. 3. Hold `Shift` and select the **mesh object** and then the **armature**. The selection order is important. 4. In the **Viewport**, right-click on the mesh object. A pop-up menu displays. 5. Select **Parent**, then **With Automatic Weights**. ## Assign vertices to bones With your armature connected to the mesh object, you can now assign the mesh vertices of your limbs to be fully influenced by their corresponding bones. As a rigid model, each limb will completely bend and articulate when the bones are rotated, which is ideal for a non-organic character like a robot. To assign bone influence to the left and right arms: 1. In Object Mode, select the **robot mesh object**. 2. In the Mode dropdown, switch to **Edit Mode**. 3. At the top-right of the Viewport, switch to **X-Ray** view using the Material Preview options. 4. Drag and select the vertices you want to move with the right bone. 5. Navigate to the **Object Properties** panel. 6. In the **Vertex Group** section, select the name of the bone you want to assign and click **Assign**. 7. **Repeat steps 4-6** for the other bone and arm vertices. 8. Drag and select the rest of the vertices in the center of the robot. 9. Navigate to the **Object Properties** panel. 10. In the Vertex Group section, select the **Middle bone** and click **Assign**. ## Test You can test your rig in different poses in Pose mode. It's important to test your rigs after applying or changing influences before exporting. To test your applied influences: 1. In **Object Mode**, select any part of your model. 2. At the top of the 3D viewport, click on the Mode dropdown, then switch to **Pose** mode. 3. Hold `Shift` and click the bone you want to test to highlight it, then press `R` to test the rotation. 4. Press `Alt``A` (`⌥``A`) to deselect the current bone, then reselect and test another bone. > **Info:** To reset the rotation of a selected bone to neutral, press `Alt`+`R`. While rotating, you can also press your mouse's scroll wheel to change the axes of your rotation. The robot model is now a rigged mesh and ready to [export](/docs/en-us/art/modeling/export-requirements.md) to Studio. For reference, you can download the [final export](../../assets/modeling/meshes/reference-files/shoebot-rigged-model.fbx) (`.fbx`) of the model rigged in this guide. --- title: "Rigging and skinning" url: /docs/en-us/art/modeling/rigging last_updated: 2026-06-29T19:33:56Z description: "Rigging and skinning is a modeling process that connects an armature to a mesh, allowing it to be animated or posed in Studio." --- # Rigging and skinning **Rigging** is the process of connecting a mesh with an internal poseable skeleton rig and bone structure. Rigged meshes allow mesh surfaces to rotate and move where internal bones are placed within the model during the modeling process. Rigging is often performed in conjunction with **skinning**, creating a natural looking stretch and bend when models are animated or repositioned. You can create rigged meshes with third-party software such as [Blender](https://www.blender.org) or [Maya](https://www.autodesk.com/products/maya/overview). _A normal model can only rotate on its pivot point._ _A rigged model (or mesh) can rotate with any bone of its associated rig._ _A rigged model with no skinning data._ _A skinned model that bends organically with the bone rotation._ See the following resources to learn the basics and intermediate steps required to skin models in Blender: - [Rig a simple mesh](/docs/en-us/art/modeling/rig-a-simple-mesh.md) - [Skin a simple mesh](/docs/en-us/art/modeling/skin-a-simple-mesh.md) - [Rig a humanoid model](/docs/en-us/art/modeling/rig-a-humanoid-model.md) - [Skin a humanoid model](/docs/en-us/art/modeling/skin-a-humanoid-model.md) ## Rigs and bones The **rig**, or bone structure, within a rigged mesh creates additional poseable points for the 3D model in Studio. By rotating or moving the bones of a mesh, the parts of the mesh assigned to those bones can move independently from the rest of the mesh. The assignment of influence between meshes and bones, such as a **LowerLeftArm** bone driving the movement of the **LowerLeftArm** geometry, is set in a third-party application like Blender or Maya. When imported into Studio, Roblox saves this influence assignment data to the `Class.MeshPart` asset data. Once you successfully [import](/docs/en-us/parts/meshes.md#import-meshes) a rigged mesh model into Studio, Studio represents this rig structure with `Class.Bone` instances that you can then pose and animate. You can view bones in Studio by enabling **Show Constraint Details** from the **View** menu, or when you are using the [Animation Editor](/docs/en-us/animation/editor.md). > **Info:** When `Class.Bone` objects are used in animation, they affect the appearance of the parts but don't change the physical shape in cases such as [collision detection](/docs/en-us/workspace/collisions.md). ![A translucent humanoid outline, showing various sphere points where the constraints exist.](../../assets/modeling/skinned-meshes/Rig-Constraint-Details.jpg)_Constraint Details enabled_ ![A translucent humanoid outline, showing 15 pink bones objects over various limbs.](../../assets/modeling/skinned-meshes/Rig-Bone-Visualization.jpg)_When using the Animation Editor_ ## Types of rigs Rigged models use a naming convention starting with "R" and ending with the number of individual meshes that make up the model. This is used to quickly identify the type and number of meshes that a model has. Although R6 and R15 models are common, a model can be of varying subjects, sizes and can have any number of individual meshes, such as a R5, R20, or R200. - **R1** refers to a single mesh that is rigged, or associated with an internal skeleton structure. Many models, such as a tree or accessory item, are good candidates to be made into an R1 model. Even humanoid characters, such as NPCs, can be created as R1 models but they will not be able to take full advantage of the animation and humanoid options available for R15 characters. See [Rig a simple mesh](/docs/en-us/art/modeling/rig-a-simple-mesh.md) for instructions on turning a basic mesh into an R1 model in Blender. - **R15** typically refers to humanoid models used as player or avatar characters. An R15 model is made up of 15 specific meshes that are parented to a single rig. A R15 character model often includes skinning data to allow the model to bend and pose naturally. Roblox uses the R15 standard for all avatars, and requires the [R15 technical specifications](/docs/en-us/avatar/character-bodies/specifications.md) to ensure universal behavior and quality. See [Rig a humanoid model](/docs/en-us/art/modeling/rig-a-humanoid-model.md) for instructions on turning a character model into an R15 humanoid model in Blender. --- title: "Roblox Blender plugin" url: /docs/en-us/art/modeling/roblox-blender-plugin last_updated: 2026-06-29T19:33:56Z description: "The Roblox Blender plugin allows you to transfer assets directly from Blender to Studio." --- # Roblox Blender plugin The Roblox Blender plugin is a Blender add-on that allows you to link your Roblox account and quickly transfer 3D modeling objects directly from Blender to your Studio session. This tool helps you save time and reduce errors by skipping the process of exporting and importing third-party modeling files between applications. The Roblox Blender plugin is an open-source implementation of Roblox's [Open Cloud API](/docs/en-us/cloud/guides.md) and developers are encouraged to extend and build upon this tool for their own projects. For installation, use, licensing, and contribution details, see the [Roblox Blender plugin GitHub page](https://github.com/Roblox/roblox-blender-plugin). --- title: "Skin a humanoid model" url: /docs/en-us/art/modeling/skin-a-humanoid-model last_updated: 2026-06-29T19:33:56Z description: "Explains the process for skinning a humanoid R15 model in Blender." --- # Skin a humanoid model A humanoid skinned mesh is a character model that, when posed or animated, bends and stretches naturally at its joints. You can create a skinned mesh using a third party modeling tool such as [Blender](https://www.blender.org) or [Maya](https://www.autodesk.com/products/maya/overview). This is an advanced guide on skinning a humanoid model into an R15 model in Blender using the humanoid model completed in [Rig a humanoid model](/docs/en-us/art/modeling/rig-a-humanoid-model.md). Before skinning a humanoid model, you should also familiarize yourself with the basic concepts in [Skin a simple mesh](/docs/en-us/art/modeling/skin-a-simple-mesh.md). To skin a humanoid model in Blender, you need to: - [Set up Blender](#set-up-blender) with a rigged model to optimize the weight painting process with bone visualization settings and Auto Normalize. - [Weight paint](#weight-painting) vertices of your mesh objects by balancing influence of your vertices between two or more bones of a humanoid rig architecture. > **Info:** This guide uses the humanoid model rigged in [Rig a humanoid model](/docs/en-us/art/modeling/rig-a-humanoid-model.md) and [Blender version 3.0](https://www.blender.org/download/releases/3-0/). If you are using another version of Blender, there may be minor differences in UI and settings. ## Set up Blender This guide uses the rigged model completed in [Rig a humanoid model](/docs/en-us/art/modeling/rig-a-humanoid-model.md). You can also download the rigged model [Blender project](../../assets/modeling/meshes/reference-files/lola-rigged-r15.blend) to follow along with this guide. To optimize the process of skinning a rigid character, first set up the following in your Blender project: - Open the Blender project and configure the [bone visualization](#bone-visualization) settings. - Enable [Auto Normalize](#auto-normalize) to help optimize the weight-painting process. ### Bone visualization By default, Blender displays bone objects as octahedral shapes. This original shape is useful when positioning bones but can get in the way while weight painting. To help visualization during the weight painting process, change your bone visualization to sticks. To update the visualization of your bones: 1. In Object Mode, click the **armature**. 2. In the **Object Data Properties**, change the **Display As** value to **Sticks**. ### Auto normalize The Auto Normalize setting forces the influence on your vertices to equal one. This makes weight painting multiple meshes and bones more efficient by ensuring that each vertex in your character is fully influenced by at least one bone. See Blender's documentation on the [Auto Normalize](https://docs.blender.org/manual/en/latest/sculpt_paint/weight_paint/tool_settings/options.html) for more information. Auto Normalize also prevents cases where a vertex is fully influenced by multiple bones and can prevent common weight painting mistakes. _Without Auto Normalize, it is not clear until testing that this fully influenced head is also influenced by another bone due to a painting mistake._ _With Auto Normalize, painting influence to an incorrect bone removes the existing influence from the original bone. This makes mistakes easier to catch and resolve._ > **Warning:** Studio doesn't support more than 4 bones to influence any single vertex. This may occur in complex rigs or inadvertently with Auto Normalize disabled. To enable Auto Normalize: 1. In **Object Mode**, select the **armature**. 2. Hold `Shift` and select **any mesh object** on your model. 3. At the top of the 3D viewport, click on the Mode dropdown and switch to **Weight Paint** mode. 4. On the right side of the viewport, expand the **Tool** tab. 5. Under **Options**, enable **Auto Normalize**. ## Weight painting Weight painting applies specific weights, or influences, that bones will have over parts of a mesh using a painting workflow. There are also additional ways to apply influences through weight painting or other tools, some which are covered in [Skin a simple mesh](/docs/en-us/art/modeling/skin-a-simple-mesh.md). See additional online resources such as Blender's [Character Rigging](https://www.youtube.com/watch?v=f2pTkW-1JkE) and [Vertex Groups](https://www.youtube.com/watch?v=dKZrzG5r13g) fundamental guides for more information on skinning and applying weights. This guide will cover one process of weight painting the head and arm meshes of a humanoid model. These weight painting techniques can be used to weight paint the rest of the model. > **Warning:** Do not apply influences on the Root or HumanoidRootNode parent bones in a humanoid rig. Any added influences are dropped when importing into Studio. ### Paint the head mesh The head mesh connects to the upper torso at the bottom vertices of the neck. To create a realistic bend in the model, the head mesh needs to share influence with the Head bone and the Upper Torso bone near the neckline. To begin weight painting the Head mesh object: 1. In Object Mode, click the **armature** then hold `Shift` and click the **Head mesh object**. 2. In the Mode dropdown, switch to **Weight Paint** mode. 3. Hold `Shift` and click the **Upper Torso bone**. The head should be completely blue as the Upper Torso doesn't yet influence any head vertices. > **Info:** A common error is to apply weights with the incorrect bone selected. Make sure to `Shift` select and un-select the correct bones when applying weights. The name of the bone currently being painted will populate in the Viewport. 4. In the top right of the Viewport, open the **Tool** menu and set the **brush strength** to **1**. 5. With the Upper Torso bone selected, **paint** the Head of your model at the neckline. You can temporarily **Hide** the Upper Torso mesh in the Outliner to have direct access to the bottom of the neck. 6. At any point, check the applied influences by holding `Shift` and deselecting the Upper Torso bone and selecting the Head bone. Press `R` to rotate the Head bone with your mouse and test how the mesh object shares influences between the Head and Upper Torso bone. 7. If additional painting is required, hold `Shift` and select the bone you want to add or remove influence from, and use the Brush tool to apply. The final result of weight painting the head of the model should balance the head mesh influences with the Head bone and the Upper Torso bone: _The Head bone fully influences the Head_Geo mesh from the neck up._ _The Upper Torso bone fully influences the Head_Geo mesh from the neck down._ ### Paint the arm meshes Similar to the process for balancing influences between the Head bone and Upper Torso bone, arms and legs require a similar weight painting process. The right arm includes the Right Hand, Right Lower Arm, and Right Upper Arm, each with their corresponding bones. The following instructions provide guidance on painting influences to all of the arm mesh objects. You can then apply these techniques to the rest of the meshes in your model. #### Hand Starting with the Right Hand, you can balance the influences of the mesh between the **Hand bone** and the **Lower Arm bone** to create a natural bend at the wrist. To weight paint influences to the right hand: 1. From Object Mode, click the **armature**, hold `Shift`, and click the right **hand** geometry. 2. In the mode dropdown, switch to **Weight Paint** mode. With the hand bone selected in this mode, you can press `R` and test the current rotation of the hand mesh object. 3. In the top right of the Viewport, open the **Tool** menu and set the **brush strength** to **1**. 4. With the Lower Arm bone selected, **paint** the hand of your model at the wrist. You can temporarily **Hide** the Lower Arm mesh in the Outliner to get better access to the wrist. 5. During and after weight painting the hand, press `R` with the bone selected to test the rotation and flexibility. > **Info:** You can add and skin additional bones, such as fingers, if you want additional controls to your skinned mesh. > > Extra bone objects that are skinned to a mesh but do not share a Humanoid bone object name (such as LowerTorso, LeftFoot, Right UpperArm) are imported into Studio as a `Class.Bone` with the same name it was assigned with in the 3D modeling software. #### Lower arm After the wrist of the right hand is weight painted, you can proceed to balance the influences of the Lower Arm mesh between the **Lower Arm bone** and the **Upper Arm bone** to create a natural bend at the elbow. To weight paint influences to the lower arm: 1. Switch back to Object Mode. 2. Click the **armature** and hold `Shift` and click the right **lower arm** geometry. 3. In the mode dropdown, switch to **Weight Paint** mode. 4. Hold `Shift` and deselect other bones besides the Lower Arm bone. You can test the current rotation of the highlighted Lower Arm bone by pressing `R`. 5. Hold `Shift` and select the Upper Arm bone. 6. With the Upper Arm bone selected, **paint** the lower arm of your model at the elbow. You can temporarily **Hide** the Upper Arm mesh in the Outliner to get better access to the elbow. 7. During and after weight painting the lower arm, press `R` with the lower arm bone selected to test the rotation and flexibility. #### Upper arm After the elbow of the right lower arm is weight painted, you can proceed to balance the influences of the Upper Arm mesh between the **Upper Arm bone** and the **Upper Torso bone** to create a natural bend at the underarm. To weight paint influences to the upper arm: 1. Switch back to Object Mode. 2. Click the **armature** and hold `Shift` and click the right **upper arm** geometry. 3. In the mode dropdown, switch to **Weight Paint** mode. 4. Hold `Shift` and deselect other bones besides the Upper Arm bone. You can test the current rotation of the highlighted Upper Arm bone by pressing `R`. 5. Press `R` and drag your mouse to rotate the Upper Arm so that the arm is horizontal. This makes it easier to access the vertices of the underarm. 6. Hold `Shift` and select the Upper Torso bone. 7. With the Upper Torso bone selected, **paint** the upper arm of your model at the underarm. You can temporarily **Hide** the Upper Torso mesh in the Outliner to get better access to the elbow. 8. During and after weight painting the lower arm, press `R` with the upper arm bone selected to test the rotation and flexibility. #### Shoulder After the underarm is weight painted, you can proceed to balance the influences of the Upper Torso mesh between the **Upper Torso bone** and the **Upper Arm bone** to create a natural bend at the shoulder when the arm is moved. Since this movement is subtle, you only need to apply a partial strength influence to these vertices. To weight paint a partial strength influence to the upper torso: 1. Switch back to Object Mode. 2. Click the **armature** and hold `Shift` and click the **upper torso** geometry. 3. In the mode dropdown, switch to **Weight Paint** mode. 4. Hold `Shift` and deselect other bones besides the Upper Arm bone. You can test the current rotation of the highlighted Upper Arm bone by pressing `R`. 5. In the top right of the Viewport, open the **Tool** menu and set the **brush strength** to **.25**. You may want to adjust this based on the results when testing. 6. With the Upper Torso bone selected, **paint** the upper torso of your model at the shoulder. 7. During and after weight painting the lower arm, press `R` with the upper arm bone selected to test the rotation and flexibility of your influences to the upper torso. > **Warning:** > > When rotating arms to their full range of motion, a candy-wrapping effect may occur between the shoulder and torso where the skinning loses volume and doesn't deform naturally. This effect may not be avoidable in many cases. You can continue to weight paint the rest of the limbs of your model using these processes. When completed, you can [export](/docs/en-us/art/modeling/export-requirements.md) your skinned model as a `.fbx` using the Blender export settings for use in Studio. For reference, you can download a [Blender project](../../assets/modeling/meshes/reference-files/lola-skinned-s15.blend) with the model fully skinned. > **Warning:** Since this reference model doesn't yet have [inner and outer cage mesh data](/docs/en-us/avatar/character-bodies/specifications.md#inner-and-outer-cages), this model can't equip layered clothing or accessories. --- title: "Skin a simple mesh" url: /docs/en-us/art/modeling/skin-a-simple-mesh last_updated: 2026-06-29T19:33:56Z description: "Explains the process for skinning a basic model in Blender." --- # Skin a simple mesh A skinned mesh is a rigged mesh that bends and flexes naturally when the internal skeleton rig is posed or animated. You can create a skinned mesh using a third party modeling tool such as [Blender](https://www.blender.org) or [Maya](https://www.autodesk.com/products/maya/overview). Skinning must be completed after the model is [rigged](/docs/en-us/art/modeling/rigging.md). This guide covers the process for rigging and then skinning a simple tree model in Blender with 3 bones. For basics on rigging, see [Rig a simple mesh](/docs/en-us/art/modeling/rig-a-simple-mesh.md) before continuing this guide. To skin a simple mesh, you need to: - [Set up Blender](#set-up-blender) to Studio's relative scene units before importing a model. - [Create, size, and position bones](#position-bones) inside of an armature. - [Parent a mesh to an armature](#parent-armature) to bind the skeleton rig to the mesh object. - [Weight paint](#weight-paint) the mesh to assign which parts of the mesh will move with which bone. > **Info:** This guide uses a downloadable [example tree model](../../assets/modeling/skinned-meshes/MapleLeafTree.fbx) and [Blender version 3.0](https://www.blender.org/download/releases/3-0/). If you are using another version of Blender, there may be minor differences in UI and settings. ## Set up Blender To start the process of creating a skinned mesh, first set up the following in your Blender project: 1. Open a new **General** project in Blender. 2. Select the default shape, camera, and lights, then press `Delete`. ### Import a model For this guide, you'll import a [maple tree model](../../assets/modeling/skinned-meshes/MapleLeafTree.fbx) into Blender as your mesh object. To import a model into Blender: 1. In the topbar, click **File**. A pop-up menu displays. 2. Select **Import**, then the file format of the model you're importing. For this example, select **FBX (.fbx)** and the `.fbx` reference model. The model displays on the 3D viewport.![Viewport Display Settings](../../assets/modeling/skinned-meshes/Import-Model.jpg) ## Create a bone structure Now that your model is within Blender, add an armature and **bones** to your mesh object. An **armature** is a skeleton-like rigging object that acts as a container for bones, while **bones** are objects that control the movement and deformation of the group of vertices, or **vertex group**, that surround the bone. ### Add an armature An armature is a structure required to add bones to your mesh. After you add in an armature, you can create and reposition any number of bones inside of your mesh object. To add an armature: 1. At the top of the 3D viewport, select **Add** → **Armature**. 2. For better visualization of the bones, in the left-hand navigation of the **Properties Editor**, navigate to **Armature Object Properties**. 3. In the **Viewport Display** section, navigate to the **Show** property, then enable **In Front**. ### Position bones When you add an armature to your project, Blender also adds one bone to the armature at a default position and scale. You can rotate and scale the bone to more accurately represent the internal structure of your mesh object. To reposition the bone: 1. Click on the **bone object** to highlight it. 2. At the top of the 3D viewport, click on the Mode dropdown, then switch to **Edit Mode**. 3. Click on the **top of the bone** and press `G`. The top of the bone moves with your cursor. 4. Pull this bone to align with the interior center of the model, then click to set the bone's position. 5. Press and hold your mouse's scroll wheel to move the camera around the mesh object to see different views and angles to ensure that the bone is centered within the mesh object. ### Add additional bones In this guide, you need 3 bones within your mesh so the tree can move and rotate at three points. To add additional bones into the armature: 1. While still in **Edit Mode**, click on the tip of the bone. 2. Press `E` and drag your mouse upward. This extends an additional bone out of the original root bone. 3. Repeat this process for the second bone. 4. Press and hold your mouse's scroll wheel to move the camera around the mesh object to see different views and angles to ensure that the bones are within the mesh object. [Reposition the bones](#position-bones) as necessary. 5. In the Outliner, expand the **Armature** object. The armature's bone hierarchy displays. 6. In the Outliner, double-click and **rename** each bone. Bone object names remain the same when you import the mesh object into Studio. ## Parent armature After the bone structure is created and positioned, you need to connect the armature to the mesh object by parenting the armature to the mesh object. This guide uses Blender's **Automatic Weights** function when you are parenting an armature, as it automatically adds weights and influences to your mesh. This can save you time during the [weight painting](#weight-paint) process. To parent an armature to a mesh: 1. At the top of the 3D viewport, click on the Mode dropdown, then switch back to **Object Mode**. 2. Press `Alt``A` (Windows) or `⌥``A` (Mac) to deselect every object. 3. Hold `shift` and select the **mesh object** and then the **armature**. The selection order is important. 4. In the viewport, right-click on the **mesh object**. A pop-up menu displays. 5. Select **Parent**, then **With Automatic Weights**. ## Weight paint With your armature connected to the mesh object, you can use the process of **weight painting** to change the amount of influence (weight) the bones will have on specific vertices to allow for more natural movement and flexibility. Blender represents weight strength with a [blue to red gradient](https://docs.blender.org/manual/en/latest/sculpt_paint/weight_paint/introduction.html#the-weighting-color-code), where **blue** represents a value of **0** and **red** represents a fully influenced value of **1**. Vertices on a mesh object that are painted red will be fully influenced by that specific bone's rotation while vertices that are yellow or green will be partially influenced by the bone's rotation. You can quickly assign influence with the [Brush tool](https://docs.blender.org/manual/en/latest/sculpt_paint/weight_paint/tools.html) in Blender's **Weight Paint** mode using the **Draw**, **Add**, or **Subtract** brush tools. For each brush, you can set the influence weight and radius in the **Tools** setting to the right of the **Viewport**. To optimize the painting process, [set up visualization and brush settings](#setup) before [painting influences](#paint-influence) to the mesh. ### Setup When assigning influences for complex models, you can configure the following Blender settings to optimize the Weight Painting process. #### Bone visualization By default, Blender displays bone objects as octahedral shapes. This original shape is useful when positioning bones within the rig but can get in the way while painting. To help visualize the weight painting process, change your bone visualization to sticks. To update the visualization of your bones: 1. In **Object Mode**, select the **Armature**. 2. In the left-hand navigation of the **Properties Editor**, navigate to **Object Data Properties**. 3. Change the **Display As** value to **Stick**. #### Auto normalize The [Auto Normalize](https://docs.blender.org/manual/en/latest/sculpt_paint/weight_paint/tool_settings/options.html) setting forces the influence on your vertices to equal one. This makes weight painting more efficient by ensuring that each vertex in your character is fully influenced by at least one bone. In this guide, Auto Normalize allows you to first apply full influence to the entire mesh quickly and then make minor adjustments with each bone. To enable Auto Normalize: 1. In **Object Mode**, select the **Armature**. 2. Hold `shift` and select the **mesh object**. 3. At the top of the 3D viewport, click on the Mode dropdown, then switch to **Weight Paint** mode. > **Info:** You must select the appropriate object to access specific modes, such as selecting a mesh object to access **Weight Paint** mode. If you are unfamiliar with switching to different modes, refer to [Object Modes](https://docs.blender.org/manual/en/latest/editors/3dview/modes.html). 4. On the right side of the viewport, click the **Tool** tab. 5. Under the **Options** section, enable **Auto Normalize**. #### Projected brush A projected brush allows you to easily apply influence through a mesh, instead of just the surface. This is useful when painting influences on meshes that may be geometrically layered, such as the foliage of the tree. To set up a projected brush: 1. In **Object Mode**, select the **Armature**. 2. Hold `shift` and select the **mesh object**. 3. At the top of the 3D viewport, click on the Mode dropdown, then switch to **Weight Paint** mode. 4. On the right side of the viewport, click the **Tool** tab. 5. Expand the **Advanced** section, then uncheck **Front Faces Only**. 6. Expand the **Falloff** section, then select **Projected**. ### Paint influence With the [projected brush](#projected-brush) set up, you can now begin applying influence to the tree. Take advantage of the [Auto Normalize](#auto-normalize) setting by painting full red influence to the entire tree to the bottom bone and then assigning a partial influence on the middle and top bones. #### Bottom bone The bottom bone should fully influence the movement of the entire tree starting from the trunk. To start painting influence to the bottom bone: 1. In **Object Mode**, hold `shift`, select the bones and then the mesh object. 2. At the top of the 3D viewport, click on the Mode dropdown, then switch to **Weight Paint** mode. 3. Hold `shift` and click on the bottom bone. 4. Use the brush tools to paint a red area of influence from the base of the bone and the rest of the tree. > **Info:** You can adjust the radius of the brush in the **Tool** tab, or by holding `F` and dragging your mouse to change the size. 5. With the bone highlighted, test that the bone is influencing the entire model by pressing `R` and rotating the mouse. Paint any vertices that are not influenced correctly to apply influence to them. #### Middle bone The middle bone represents most of the foliage above the bottom bone. For a more subtle effect, apply a 50% influence (green) from the middle bone upwards. To paint influence to the middle bone: 1. In **Weight Paint** mode, press `Alt``A` (Windows) or `⌥``A` (Mac) to deselect the current bone object. 2. Hold `shift` and `click` on the middle bone. 3. On the right side of the viewport, click the **Tool** tab. 4. Change the **Weight** value to **.5**. 5. Starting from the area at the middle bone, paint the middle and top area of the tree. 6. Press `R` to rotate the bone and test the influence at different angles. #### Top bone The top bone should influence the top leaf area above the middle bone. Paint this top area using 25% influence (teal) for a more natural effect. To paint influence to the top bone: 1. In **Weight Paint** mode, press `Alt``A` (Windows) or `⌥``A` (Mac) to deselect the current bone object. 2. Hold `shift` and click on the top bone. 3. On the right side of the viewport, click the **Tool** tab. 4. Change the **Weight** value to **0.25**. 5. Starting from the area at the top bone, paint the top area of the tree. 6. Press `R` to rotate the bone and test the influence at different angles. After weight painting each bone, each section of this tree should now be influenced by the bottom, middle, or top bones. The bottom bone fully controls the trunk of the tree while the middle and top bones have a reduced influence on the branches and leaves. ### Test It is important to constantly test the influences of your bone **throughout** the weight painting process. Blender allows you to test and pose bones **while** painting if both the armature and mesh are selected in Object Mode **before** switching to Weight Paint mode. This hybrid pose and weight paint functionality doesn't work unless you select both armature and mesh before entering Weight Paint mode. To test your weight painting: 1. In **Object Mode**, select the **Armature**. 2. Hold `shift` and select the **mesh object**. 3. At the top of the 3D viewport, click on the Mode dropdown, then switch to **Weight Paint** mode. 4. Hold `shift` and click the bone you want to test, then press `R` to test the rotation. 5. Press `Alt``A` (`⌥``A`) to deselect the current bone, then reselect and test another bone. > **Info:** To reset the rotation of a selected bone to neutral, press `Alt`+`R`. While rotating, you can also press your mouse's scroll wheel to change the axes of your rotation. It is recommended to prioritize testing positions you expect to use in your animations and poses in Studio. Your S1 tree model is now a skinned mesh and is ready for [export](/docs/en-us/art/modeling/export-requirements.md) for Studio. The [Blender project file](../../assets/modeling/skinned-meshes/MapleTreeS1.blend) and [final export](../../assets/modeling/skinned-meshes/MapleLeafTree.fbx) (`.fbx`) are available for reference. --- title: "General specifications" url: /docs/en-us/art/modeling/specifications last_updated: 2026-06-29T19:33:56Z description: "Lists the specific technical requirements for custom models created outside of Studio." --- # General specifications Roblox supports a wide variety of mesh configurations created from third-party software such as [Blender](https://www.blender.org) or [Maya](https://www.autodesk.com/products/maya/overview). Check that your model meets the following modeling specifications and guidelines before exporting to ensure Studio compatibility. Specific types of assets, like characters and accessories, have additional specifications: > **Warning:** **If creating other types of 3D models:**- For rigid accessories, see [accessory specifications](/docs/en-us/avatar/rigid-accessories/specifications.md) and [accessory export settings](/docs/en-us/avatar/rigid-accessories/export.md). - For layered accessories, see [layered accessory specifications](/docs/en-us/avatar/layered-accessories/specifications.md) and [layered export settings](/docs/en-us/avatar/layered-accessories/export.md). - For avatar characters, see [avatar specifications](/docs/en-us/avatar/character-bodies/specifications.md) and [avatar export settings](/docs/en-us/avatar/character-bodies/export.md). When ready to export, see the [export settings](/docs/en-us/art/modeling/export-requirements.md) for mesh export settings for Blender and Maya. > **Info:** If you meet certain account requirements, you can sell your custom meshes as accessories on the Marketplace. See [Rigid accessories](/docs/en-us/avatar/rigid-accessories.md) for an overview on the creation process for these types of accessories. ## Geometry See the following specifications for general geometry: - **Budgets** - Individual meshes can not exceed 20,000 triangles. Avatar items have their own individual budget requirements for [character bodies](/docs/en-us/avatar/character-bodies/specifications.md), [rigid accessories](/docs/en-us/avatar/rigid-accessories/specifications.md), and [layered accessories](/docs/en-us/avatar/layered-accessories/specifications.md). - **Watertight** - All geometry must be watertight without exposed holes or backfaces. - **No N-gons** - Meshes must be in quads where possible. - **Volume** - Meshes cannot be 0 thickness and must have some volume. ## Rigging and skinning Roblox supports third-party meshes with an internal rig, or skeleton structure that can be used as additional articulation points in your model. See [Character rigs](/docs/en-us/avatar/character-bodies/specifications.md#rigging) for specific standards for a standard or higher-fidelity character rig. ![A cartoon-ish low polygon model of a puffer fish.](../../assets/animation/importing-custom-3d-rigs/Rig-Custom-Puffer-Blender.png)_Generic rig model._ ![The Outliner in Blender of the puffer fish model, showing the armature bone structure.](../../assets/animation/importing-custom-3d-rigs/Rig-Hierarchy-Custom-Blender-Bones.png)_Generic rig bone structure (Blender)._ See the following requirements for general rigging and skinning: - **Transformations** - All bones (Blender) or joints (Maya) must be frozen and have scale values set to `1`, `1`, `1` and rotation values set to `0`, `0`, `0`. - **Symmetry** - When possible, maintain symmetry when applying influences to a rig - **Root joint** - The root bone or joint should always be set to `0`, `0`, `0`. - **Max influences** - A vertex can not be influenced by more than 4 bones or joints. - **No Root influences** - Do not apply influences to the Root bone or joint. ## Textures - Roblox supports basic color textures and modern [PBR textures](/docs/en-us/art/modeling/surface-appearance.md). - For instructions on assigning texture images to natively import with your mesh, see [Assign textures in modeling tools](/docs/en-us/art/modeling/assign-textures.md). - For technical requirements and best practices when generating individual texture images, see [Texture specifications](/docs/en-us/art/modeling/texture-specifications.md). ## Animations An animation can be included on any `.fbx` mesh export. For information on prepping a character animation from a modeling software for export, see [Export animations from Maya](/docs/en-us/art/characters/export-avatar-animations-from-maya.md). See the following requirements for assets with animation: - **Single track animation** - Only a single animation track can be exported with a mesh or model. If you want to export multiple animations, you need to create separate exports for each animation you want to import. ## Inner and outer cages Inner and Outer cages are non-rendered meshes that Roblox uses to define the inner and outer surfaces of a mesh using a `Class.WrapLayer` or `Class.WrapTarget` instance. These cages are most often used with characters and accessories, though you can use cage meshes for any mesh object. > **Warning:** Character models must include an outer cage in order to properly equip layered clothing and accessories. See [Character body specifications](/docs/en-us/avatar/character-bodies/specifications.md) for additional information. For general use, see the following requirements for adding inner and outer cage meshes to your model: - **Naming conventions** - The inner and outer cage must be named after the primary mesh object with **_InnerCage** and **_OuterCage** affixed.![The Outliner in Blender showing two mesh objects called Tshirt_InnerCage and Tshirt_OuterCage.](../../assets/accessories/lc-blender-selecting-cage-in-outlier.png) - **Outer cage** - Models, such as a playable character, that aren't expected deform but are the target of meshes that will stretch over it, only require an Outer Cage. - For more information on applying and implementing cages on non-Humanoid targets, see [layered clothing on non-R15](/docs/en-us/characters/appearance.md#layered-clothing-on-non-r15). - **Vertices and UV map** - Don't delete vertices or alter the UVs on the Inner or Outer Cages as this can cause errors when importing in Studio or when equipping onto a character. - **Symmetry and consistency** - Keep each face (the space between vertices) consistently sized and retain symmetry wherever possible. Use symmetry tools in your modeling software whenever possible. --- title: "PBR textures" url: /docs/en-us/art/modeling/surface-appearance last_updated: 2026-06-29T19:33:56Z description: "PBR textures are advanced textures using multiple texture maps." --- # PBR textures **Physically-Based Rendering** (PBR) textures allow you to represent realistic shading and lighting by using multiple types of texture images, or **maps**, on a single object. Combining multiple texture maps can more accurately simulate color, roughness, and reflectivity in any lighting environment and can enhance the visual elements of your assets and environment. #### Clothing ![A humanoid avatar with dreadlocks, a glossy jacket, jeans with decals, and boots.](../../assets/modeling/surface-appearance/Layered-Clothing-Example.png)![A humanoid avatar with a short haircut, leather jacket, capri pants, and orange shoes.](../../assets/modeling/surface-appearance/Layered-Clothing-Example-2.jpg) #### Environment ![A realistic leafy bush](../../assets/modeling/surface-appearance/SurfaceAppearance-Example-1.jpg)![A realistic mossy rock](../../assets/modeling/surface-appearance/SurfaceAppearance-Example-3.jpg) Various applications and workflows are available for creating PBR textures. You can use these during the modeling and texturing phases of custom 3D object creation, provided that Roblox Studio [supports](/docs/en-us/avatar/character-bodies/specifications.md#textures) the specific texture maps you're using. > **Info:** For information on how to properly connect PBR textures to your model in Blender and Maya for import into Studio, see [Blender / Maya texture settings](/docs/en-us/art/modeling/assign-textures.md). This guide provides instructions on [setting up](#enable-surface-appearance) your mesh objects to use PBR texture maps, and describes common use-cases and best practices for Roblox's supported PBR [texture maps](#texture-maps). When creating your own surfaces, see [material references](/docs/en-us/art/modeling/material-reference.md) for common material values, image comparisons and clothing examples. ## Enable surface appearance You can add PBR textures to any `Class.MeshPart` by adding a `Class.SurfaceAppearance` object which overwrites the original assigned texture. In general, you can't modify `Class.SurfaceAppearance` properties by scripts during an experience because the engine requires some pre-processing to display these graphics. Similar to adding a basic texture, each texture image map must point to the appropriate uploaded image asset ID. To enable surface appearance for a `Class.MeshPart`: 1. In the [Explorer](/docs/en-us/studio/explorer.md) window, hover over the `Class.MeshPart` and click the ⊕ button. 2. Insert a `Class.SurfaceAppearance` from the contextual menu.![A SurfaceAppearance object parented within a MeshPart in Studio's Explorer window.](../../assets/studio/explorer/MeshPart-SurfaceAppearance.png) When you're ready to add texture maps to the `Class.SurfaceAppearance` object, you can click each map property in the [Properties](/docs/en-us/studio/properties.md) window and enter an asset ID. ## Texture maps Studio currently supports 4 types of PBR texture maps: [color](#color-albedo), [normal](#normal), [roughness](#roughness), and [metalness](#metalness). Each of these maps correspond to an important aspect of the object's surface appearance. Texture maps only change visual appearance and don't affect the geometry of the `Class.MeshPart` object. > **Warning:** Avoid adjusting material values to look better in one specific lighting situation. You should base your material values on the physical characteristics and test and iterate to achieve a result that remains visually accurate in various lighting environments. See the following examples for an overview of Roblox's supported texture maps and additional resources: #### Color The `Class.SurfaceAppearance.ColorMap|ColorMap` property sets the color data of the surface, including any transparency present in the map. See [color (albedo)](#color-albedo) for additional information. ![A completely filled image, mostly red with various black splotches and white scratches.](../../assets/modeling/surface-appearance/SurfaceAppearance-ColorMap.jpg)_Example map_ ![A blank white sphere with a dark background.](../../assets/modeling/surface-appearance/SurfaceAppearance-ColorMap-Before.jpg)_Example mesh_ ![A red sphere with black splotches and white scratches with a dark background.](../../assets/modeling/surface-appearance/SurfaceAppearance-ColorMap-After.jpg)_Mesh and texture_ #### Normal The `Class.SurfaceAppearance.NormalMap|NormalMap` texture property defines the topography of the surface allowing you to add visual textures such as bumps, dents, or cracks to your surface. See [normal](#normal) for additional information. ![A completely filled image, mostly cyan with streaks and bumps across the surface giving the impression of a tactile surface.](../../assets/modeling/surface-appearance/SurfaceAppearance-NormalMap.jpg)_Example map_ ![A blank teal sphere with a dark background.](../../assets/modeling/surface-appearance/SurfaceAppearance-NormalMap-Before.jpg)_Example mesh_ ![A teal sphere with tactile streaks and bumps across the surface.](../../assets/modeling/surface-appearance/SurfaceAppearance-NormalMap-After.jpg)_Mesh and texture_ #### Roughness The grayscale `Class.SurfaceAppearance.RoughnessMap|RoughnessMap` texture property defines the smoothness of the surface, allowing for a glossy or matte visual appearance. See [roughness](#roughness) for additional information. ![A completely filled image of black diamond shapes over a light grey background.](../../assets/modeling/surface-appearance/SurfaceAppearance-RoughnessMap.png)_Example map_ ![A blank purple sphere with a dark background.](../../assets/modeling/surface-appearance/SurfaceAppearance-RoughnessMap-Before.jpg)_Example mesh_ ![A purple sphere that has glossy diamond shapes over its surface.](../../assets/modeling/surface-appearance/SurfaceAppearance-RoughnessMap-After.jpg)_Mesh and texture_ #### Metalness The grayscale `Class.SurfaceAppearance.MetalnessMap|MetalnessMap` texture property defines the metallic visual properties of the surface. See [metalness](#metalness) for additional information. ![A completely filled image with a completely black border and scratch-like streaks revealing a white center.](../../assets/modeling/surface-appearance/SurfaceAppearance-MetalnessMap.jpg)_Example map_ ![A blank light blue sphere with a dark background.](../../assets/modeling/surface-appearance/SurfaceAppearance-MetalnessMap-Before.jpg)_Example mesh_ ![A light blue sphere with parts of its surface looking scratched. The scratched areas reveal a metal-ness quality that is more reflective.](../../assets/modeling/surface-appearance/SurfaceAppearance-MetalnessMap-After.jpg)_Mesh and texture_ > **Info:** For technical details on texture file requirements, see [texture requirements](/docs/en-us/art/modeling/texture-specifications.md). ### Color (albedo) The **color**, or **albedo**, map determines the color of your texture and consists of mostly color information with little to no lighting or textural information. For additional customization, you can also add [transparency](#alpha-modes) in your albedo texture by adding opacity to your image map. > **Info:** While albedo maps and generic [non-PBR texture maps](/docs/en-us/parts/textures-decals.md), commonly known as **diffuse maps**, contain very similar base color data for a surface, diffuse maps often include shading and lighting values to imitate a specific visual element that are more effectively handled by PBR's [normal](#normal), [roughness](#roughness), and [metalness](#metalness) maps. Using a typical diffuse map instead of an albedo map may often look incorrect when the lighting doesn't match with these added baked-in texture elements. #### Alpha modes For objects that require partial or complete sections of transparency, such as grass, leaves, lace, or decals like dirt or grunge, you can use various **alpha modes** to apply transparency to your color map. If your color map image format supports alpha channels, you can apply a grayscale alpha map where `0.0` is transparent and `1.0` is opaque. Similarly, when using an image format such as a `.png`, any opacity on the color map applies as transparency on the asset. You can apply transparency in two different behaviors by setting the following `Class.SurfaceAppearance.AlphaMode|AlphaMode` values: - [Opaque](#opaque) — Ignores the alpha channel of the `Class.SurfaceAppearance.ColorMap|ColorMap` and uses the color value directly from the color map. - [Overlay](#overlay) — Overlays the `Class.SurfaceAppearance.ColorMap|ColorMap` over the underlying mesh's `Class.MeshPart.Color`. Color maps using overlay mode reveal the base color of the mesh anywhere transparency is present. This is the default setting. - [Transparency](#alpha-modes) — Removes the visible mesh based on transparency in the `ColorMap`. This renders the mesh see-through and does not reveal the original mesh color whenever transparency is present. - [TintMask](#tintmask) — Blends the tinted `Class.SurfaceAppearance.ColorMap|ColorMap` over the un-tinted `Class.SurfaceAppearance.ColorMap|ColorMap`. ##### Opaque > **Success:** This mode is currently in beta. Enable it through **File** ⟩ **Beta Features** ⟩ **MaterialVariant Alpha Mode**. You can use `Enum.AlphaMode.Opaque` mode to ignore the alpha channel of the `Class.SurfaceAppearance.ColorMap|ColorMap` altogether. In this case the alpha value is assumed to be 1 (fully opaque). The following example demonstrates how the `Enum.AlphaMode.Opaque|Opaque` mode works using a white object as reference: ![A polkadot texture with transparency. The background is completely transparent.](../../assets/modeling/surface-appearance/SurfaceAppearance-Opaque-ColorMap.jpg)_Example color/albedo map with transparency_ ![A white torus on a plane.](../../assets/modeling/surface-appearance/SurfaceAppearance-Opaque-Before.jpg)_Example mesh object (white)_ ![The torus has the polkadot patterns scattered over its surface. Transparency value is ignored.](../../assets/modeling/surface-appearance/SurfaceAppearance-Opaque-After.jpg)_`Class.SurfaceAppearance.AlphaMode|AlphaMode` set to `Enum.AlphaMode.Opaque|Opaque` using reference map and material_ ##### Overlay You can use `Enum.AlphaMode.Overlay` to reveal sections of the mesh's original color. Since the transparent areas of the color map expose the underlying color, you can design a unique texture map that partially or fully reveals the mesh's `Class.MeshPart.Color|Color` property for custom skin tones or other situations with unique colors. The following example demonstrates how the `Enum.AlphaMode.Overlay|Overlay` mode works using a white sphere reference: ![A completely filled image of a semi-repeating pattern with 8 by 8 small light blue squares. The background is checkered to indicate transparency.](../../assets/modeling/surface-appearance/SurfaceAppearance-AlphaMode-ColorMap.png)_Example color/albedo map with transparency_ ![A blank white sphere over a dark background.](../../assets/modeling/surface-appearance/SurfaceAppearance-ColorMap-Before.jpg)_Example mesh object (white)_ ![A white sphere with small blue squares scattered over the surface.](../../assets/modeling/surface-appearance/SurfaceAppearance-AlphaMode-Overlay.jpg)_`Class.SurfaceAppearance.AlphaMode|AlphaMode` set to `Enum.AlphaMode.Overlay|Overlay` using reference map and material_ The following example uses the `Enum.AlphaMode.Overlay|Overlay` mode for custom characters, revealing the character's original skin-tone: ![A completely filled image of a texture map for a 3D humanoid model. Various parts of the image is checkered or partially checkered to indicate varying levels of transparency.](../../assets/modeling/surface-appearance/Custom-Skin-Tone-Map.png)_Color map containing transparency in sections to expose the original mesh color_ ![Four of the same humanoid models with different skin tones: light brown, black, white, dark green.](../../assets/modeling/surface-appearance/Custom-Skin-Tone-Example.png)_Several variations of characters with a single color map using `Enum.AlphaMode.Overlay|Overlay` mode_ See [custom skin tone](/docs/en-us/avatar/character-bodies/specifications.md#custom-skin-tone) for additional details on optimizing an overlay for skin and similar applications. ##### Transparency You can use the `Enum.AlphaMode.Transparency` mode to create complex or extremely fine objects, such as lace or netting, by removing visible parts of the mesh as an alternative to sculpting the mesh geometry. Since this does not affect the geometry of the mesh object, this can allow you to create detailed objects without the performance impact of an intricate mesh model. The following example demonstrates how a partial and full transparency in this mode visually removes sections of the mesh: #### Sample Meshes ![A sphere with a basket-like surface.](../../assets/modeling/surface-appearance/Opacity-0.png)_ No transparency_ ![A sphere with regular triangular patterns on its surface partially invisible.](../../assets/modeling/surface-appearance/Opacity-Half.png)_ Half transparency in sections_ ![A sphere with regular triangular patterns on its surface completely invisible.](../../assets/modeling/surface-appearance/Opacity-1.png)_ Full transparency in sections_ #### Image Maps ![A completely filled image of a thatched basket-like surface.](../../assets/modeling/surface-appearance/OpacityTexture-0.png)_ No transparency_ ![A completely filled image with a thatched basket-like surface with a regular triangle pattern partially transparent.](../../assets/modeling/surface-appearance/OpacityTexture-Half.png)_ Half transparency in sections_ ![A completely filled image with a thatched basket-like surface with a regular triangle pattern fully transparent.](../../assets/modeling/surface-appearance/OpacityTexture-1.png)_ Full transparency in sections_ There are two outcomes of `Enum.AlphaMode.Transparency|Transparency` mode, depending on whether the `Class.MeshPart.Transparency` is set to `0` or **at least** `0.02`, although numerical precision may result in small values such as `0.01` triggering the opaque transparency. - When alpha is used to cut out the shape of mostly opaque objects with hard edges such as foliage, lace fabric, and netting, set the `Class.MeshPart.Transparency` to `0`. When parts of the surface are fully opaque, the Roblox engine can render them with proper depth‑based occlusion. Opaque surfaces also generally work better with depth‑based effects like `Class.DepthOfFieldEffect`, glass and water refraction, and water reflection. - When alpha is used to add detail to semi‑transparent objects or objects with smooth gradients of transparency, set the `Class.MeshPart.Transparency` to **at least** `0.02`. This improves the quality of blending for objects such as dirty windows and soft‑edged feathers, but it will not work with all effects. ##### TintMask You can use the `Enum.AlphaMode.TintMask` mode to apply `Class.SurfaceAppearance.Color` tinting to selective areas of a surface. The tinting is strongest where the alpha channel is fully visible and not applied where the alpha channel is transparent. #### ColorMap RGB ![A warm, light colored brick texture with darker grey textured grout between the bricks.](../../assets/modeling/surface-appearance/SurfaceAppearance-AlphaMode-TintMask-Bricks-Albedo.jpg) _Example brick texture, showing the color detail on brick faces as well as in the grout between the bricks._ #### ColorMap Alpha ![A monochrome brick texture showing white in the brick face areas and solid black in between the bricks.](../../assets/modeling/surface-appearance/SurfaceAppearance-AlphaMode-TintMask-Bricks-Alpha.jpg) _Example brick texture alpha, with areas of the brick faces opaque (white) and the grout areas in between bricks transparent (black)._ #### Example Results ![Four square panels with a brick texture arranged in a vertical stack. They are tinted blue, yellow, purple, and red.](../../assets/modeling/surface-appearance/SurfaceAppearance-AlphaMode-TintMask-Bricks.jpg) _Example brick texture with four different tint colors applied. The tint is affecting only the raised brick faces and not the grout between them._ > **Info:** When creating `Class.SurfaceAppearance.ColorMap` textures for use with a tint mask, preserve color in the transparent regions and turn off options such as pre-multiplied alpha that darken the color in transparent areas. The alpha values can also be semi-transparent to apply a partial tint to areas of the texture. #### Color tinting You can apply a tint to your color map by modifying the `Class.SurfaceAppearance.Color` property. Tinting does not affect performance and you can save on memory by reusing a single color map with different tints. Use color tinting to create additional low-cost variation between your `Class.MeshPart` PBR textures or to programmatically modify your PBR surface colors in real-time. ![A statue with a default yellow tint and reflective jade-like properties.](../../assets/modeling/surface-appearance/Tinting-None.png)_Statue mesh from [The Mystery of Duvall Drive](/docs/en-us/resources/the-mystery-of-duvall-drive.md) with no tinting._ ![A statue with a red tint and continues to have a shiny reflective properties from its other texture maps.](../../assets/modeling/surface-appearance/Tinting-Red.png)_Statue mesh with `Class.SurfaceAppearance.Color|Color` set to red._ ![A statue with a green tint and continues to have a shiny reflective properties from its other texture maps.](../../assets/modeling/surface-appearance/Tinting-Green.png)_Statue mesh with `Class.SurfaceAppearance.Color|Color` set to green._ `Class.SurfaceAppearance.Color` tinting applies as a multiplier, so the final appearance is a function of `Datatype.Color3` (texel color) times `Class.SurfaceAppearance.Color`. This means that authoring your original `Class.SurfaceAppearance.ColorMap` in near-white grayscale colors creates the strongest tinting effect when this property is applied. Tinting only applies to the `Class.SurfaceAppearance.ColorMap` and not the `Class.MeshPart.Color`. You can continue to use alpha channels when applying [transparency](#transparency). When `Class.SurfaceAppearance.AlphaMode` is set to `Enum.AlphaMode|Overlay` and an alpha channel is present, the underlying `Class.MeshPart.Color` is revealed and `Class.SurfaceAppearance.Color` tinting only applies to the visible `Class.SurfaceAppearance` color map. When `Class.SurfaceAppearance.AlphaMode` is set to `Enum.AlphaMode|TintMask` and an alpha channel is present, the alpha channel controls the amount of `Class.SurfaceAppearance.Color` tinting. The tinting is strongest where the alpha channel is fully visible and not applied where the alpha channel is transparent. #### Transparency AlphaMode ![A floating square plane with a cyan slate texture, that gradually changes from fully transparent on the left to fully opaque on the right. A thin horizontal bar below the floating plane has a gradient with black on the left and white on the right.](../../assets/modeling/surface-appearance/SurfaceAppearance-AlphaMode-Gradient-Transparency.jpg) _`Class.SurfaceAppearance.AlphaMode|AlphaMode` set to `Enum.AlphaMode|Transparency`. The greyscale gradient bar at the bottom indicates the alpha value. `Class.MeshPart.Color` is set to red and `Class.SurfaceAppearance.Color` is set to cyan._ #### Overlay AlphaMode ![A floating square plane that gradually turns from a solid red color on the left to a cyan slate texture on the right. A thin horizontal bar below the floating plane has a gradient with black on the left and white on the right.](../../assets/modeling/surface-appearance/SurfaceAppearance-AlphaMode-Gradient-Overlay.jpg) _`Class.SurfaceAppearance.AlphaMode|AlphaMode` set to `Enum.AlphaMode|Overlay`. The greyscale gradient bar at the bottom indicates the alpha value. `Class.MeshPart.Color` is set to red and `Class.SurfaceAppearance.Color` is set to cyan._ #### TintMask AlphaMode ![A floating square plane that gradually turns from a white slate texture on the left to a cyan slate texture on the right. A thin horizontal bar below the floating plane has a gradient with black on the left and white on the right.](../../assets/modeling/surface-appearance/SurfaceAppearance-AlphaMode-Gradient-TintMask.jpg) _`Class.SurfaceAppearance.AlphaMode|AlphaMode` set to `Enum.AlphaMode|TintMask`. The greyscale gradient bar at the bottom indicates the alpha value. `Class.MeshPart.Color` is set to red and `Class.SurfaceAppearance.Color` is set to cyan. Tint masking is not affected by `Class.MeshPart.Color`._ ### Normal The **normal**, or **surface**, map adds texture depth to your surface and behaves similarly to a [height map](/docs/en-us/parts/terrain.md#heightmaps). As a result, the effect may fade or intensify depending on the viewing angle and lighting environment. When a normal map is not present, the value is set to `0.0`. In the following figure, you can switch between the mesh reference and the map reference for comparisons of normal map values: #### Sample Meshes ![A clay colored smooth sphere.](../../assets/modeling/surface-appearance/Normal-0.png)_ 0.0_ ![A clay colored sphere with a moderately rocky surface.](../../assets/modeling/surface-appearance/Normal-Half.png)_ 0.5_ ![A clay colored sphere with an extremely rocky surface](../../assets/modeling/surface-appearance/Normal-1.png)_ 1.0_ #### Image Maps ![A completely filled teal image.](../../assets/modeling/surface-appearance/Normal-Map-0.png)_ 0.0_ ![A completely filled teal image with a moderate amount of surface aberrations in slightly lighter and darker shades](../../assets/modeling/surface-appearance/Normal-Map-Half.png)_ 0.5_ ![A completely filled teal image with a heavy amount of surface aberrations in slightly lighter and darker shades](../../assets/modeling/surface-appearance/Normal-Map-1.png)_ 1.0_ The **R**, **G**, and **B** channels of the image correspond respectively to the **X**, **Y**, and **Z** components of the local surface vector. A uniform image of color `[127, 127, 255]` translates to a completely flat normal map. Roblox only supports **OpenGL format - Tangent Space** normal maps. Normal maps prominently affect the visual surface of a mesh and can accentuate awkward seams in your texture. Whenever possible, keep your texture seams hidden to avoid visual issues with your mesh. ### Roughness **Roughness**, or **microsurface**, maps determine how light is spread across your model's surface. When roughness is at `0.0`, the surface doesn't scatter light at all, resulting in a much sharper and brighter reflection and glossiness on your material. At `1.0`, light and reflections evenly scatter over the model resulting in a less reflective matte-like surface. Roughness may impact how reflective an object is at different angles, referred to as the **Fresnel** effect. See [fresnel](#fresnel) for more details and best-practices to maintain consistent reflective behavior. The following figure shows comparisons of various roughness map values: ![A blue glossy sphere with a legend indicating the roughness map is completely black.](../../assets/modeling/surface-appearance/Roughness-0.png)_ 0.0_ ![A blue semi-glossy sphere with a legend indicating the roughness map is exactly grey.](../../assets/modeling/surface-appearance/Roughness-Half.png)_ 0.5_ ![A blue matte sphere with a legend indicating the roughness map is completely white.](../../assets/modeling/surface-appearance/Roughness-1.png)_ 1.0_ > **Info:** Various combinations of the roughness and metalness can represent almost every possible real-world material surface. See [material references](/docs/en-us/art/modeling/material-reference.md) for examples and references of how combinations of material values can create various surface appearances. #### Fresnel **Fresnel** refers to the amount of reflection of a surface in reference to the current viewing angle. Studio's Fresnel processing aims for physical real-world accuracy, although you may get unexpected specular contribution at certain angles even with rough surfaces. In some cases, you can compensate by making your roughness map around `0.1` more rough to achieve a consistent lighting response with your materials. Even though Roblox renders this lighting effect accurately, the brightness and reflectivity of a surface may not respond consistently between your texture content creating software, such as Substance Painter, and Studio. See [clothing examples](/docs/en-us/art/modeling/material-reference.md#clothing-examples) for differences in rendering between applications. ### Metalness **Metalness** determines the reflectivity of a surface. Metalness values range between `0.0` and `1.0`. Roblox sets the default value to `0.0` if a metalness map is not present. See the following figure for comparisons of various metalness map values: ![A brown sphere with light splotches. A legend indicates the metalness map is completely black.](../../assets/modeling/surface-appearance/Metalness-0.png)_ 0.0_ ![A brown sphere with light splotches and some reflectiveness. A legend indicates the metalness map is exactly grey.](../../assets/modeling/surface-appearance/Metalness-Half.png)_ 0.5_ ![A brown sphere with moderate splotches and lots of reflectiveness. A legend indicates the metalness map is completely white.](../../assets/modeling/surface-appearance/Metalness-1.png)_ 1.0_ Different PBR renderers use various workflows for processing reflectiveness. Studio only uses the **metalness workflow** which determines whether a material is a **nonmetal** or a **metal**, sometimes referred to as an **insulator** or **conductor**. In most cases, you should set this value to either `0.0` (non-metal) or `1.0` (metal). You can use partial metalness values when creating more uncommon surfaces with moderate reflective properties, like satin or silk. This practice can subtly fake the reflections in the material to highlight the color from the [color/albedo map](#color-albedo) over colors reflected in the environment. > **Info:** Various combinations of the roughness and metalness can represent almost every possible real-world material surface. See [material references](/docs/en-us/art/modeling/material-reference.md) for examples and references of how combinations of material values can create various surface appearances. --- title: "Texture specifications" url: /docs/en-us/art/modeling/texture-specifications last_updated: 2026-06-29T19:33:56Z description: "Texture Specifications lists the specific technical requirements and best practices for custom textures created outside of Studio." --- # Texture specifications A texture is a digital image applied to the surface of a 3D object to simulate and enhance its visual appearance. Roblox supports various texture types that you import with a custom 3D object, or upload directly as individual image files. For details on implementing basic textures, see [Textures and decals](/docs/en-us/parts/textures-decals.md). Roblox also supports [physically-based rendering (PBR) textures](/docs/en-us/art/modeling/surface-appearance.md) which override a mesh's existing `Class.MeshPart.TextureID|TextureID` and can be used to create immersive environments and objects. > **Info:** For information on how to properly connect textures to your model in Blender and Maya for import into Studio, see [Blender / Maya texture settings](/docs/en-us/art/modeling/assign-textures.md). See the following requirements when creating your own basic and advanced textures: - **File formats** — File formats for textures that are uploaded separately in Studio must be submitted as a `.png`, `.jpg`, `.tga`, or `.bmp`. - **Resolution** — Roblox supports up to 4096x4096 pixel texture resolutions (4K). As an experience loads, the engine automatically starts with lower quality versions of the texture and ramps up quality based on device resources. Even still, manually sizing textures before you upload them can improve memory usage in some situations. Consider the following general guidance: - For smaller 5×5 stud objects, use 256×256 texture resolutions. - For medium 10×10 stud objects, use 512×512 texture resolutions. - For larger 20×20 stud objects, use 1024×1024 texture resolutions. - See [PBR texture budgets](#surfaceappearance) for similar guidances with PBR texture maps. - **Single material** - Mesh objects can only have one material assigned. > **Info:** Roblox default [materials](/docs/en-us/parts/materials.md) follow a similar texel resolution pattern: - Parts display a 1024×1024 on an 8×8 stud face. - Terrain displays 512×512 across 8×8 studs. ## SurfaceAppearance You can add a [surface appearance](/docs/en-us/art/modeling/surface-appearance.md) to your mesh in Studio to add PBR texture options. The `Class.SurfaceAppearance` instance uses UV mapping, a form of texture mapping, to accurately map up to four 2D images onto the mesh object. > **Info:** For information on how to properly connect PBR textures to your model in Blender and Maya for import into Studio, see [Blender / Maya texture settings](/docs/en-us/art/modeling/assign-textures.md). See the following requirements for `Class.SurfaceAppearance` assets: - **Texture files** — Ensuring your textures follow consistent conventions optimizes assets for tools like the [Importer](/docs/en-us/studio/importer.md) and helps with organization. When possible, texture files should include the appropriate name affix and follow the appropriate image details: | Texture type | Texture schema suffix | Texture image details | | --- | --- | --- | | Albedo | _ALB | RGB (24-bit) | | Metalness | _MET | Single Channel Grayscale (8-bit) | | Normal | _NOR | RGB (24-bit) - Roblox only supports **OpenGL format - Tangent Space** normal maps. | | Roughness | _RGH | Single Channel Grayscale (8-bit) | - **PBR Texture Budget** — Use an appropriate texture map size based on the asset you are texturing to optimize for visual quality and performance. As a general guideline, each asset should have a texture resolution of 256×256 for every 2×2×2 unit space the asset occupies. If larger than a 2×2×2 cube, use the next highest resolution. The following are some baseline texture sizing examples: | Recommended map size | Approximate asset size (unit cube) | Example assets | | --- | --- | --- | | 64×64 - 128×128 | 1×1×1 | Jewelry, sunglasses, eyebrows, mustaches. | | 256×256 | 2×2×2 | Hair, shoes, tank tops, t-shirts, shorts, short skirts.

256x256 is the **maximum budget** for non-albedo maps (RGH, MTL, NOR) for [rigid accessories](/docs/en-us/avatar/rigid-accessories/specifications.md#textures). | | 512×512 | 4×4×4 | Jackets, pants, overalls, long-sleeve shirts, long skirts. | | 1024×1024 (maximum) | 8×8×8 | Full body clothing (onesies, robes), humanoid characters. | > **Info:** When rendering many textures at the same time, Roblox may downsample textures to optimize for performance. ## UV mapping All textures use UV mapping, a 3D modeling process to project a 3D model's surface to a 2D image, or UV atlas. See the following requirements for UV mapping, especially if you're manually editing or optimizing your UV atlas: - **Single UV Set** — Use a single UV set for each component, such as a humanoid or accessory. Studio doesn't allow for multiple UV sets. - **Coordinates** — All UVs must be created within a 0:1 space. - **Overlaps** — Overlapping UVs are allowed. - **Maximum Texture Resolution** — Roblox supports up to 1024×1024 pixel spaces for texture maps.
--- title: "Work with third-party software" url: /docs/en-us/art/overview-dcc last_updated: 2026-06-29T19:33:56Z description: "An overview of how to incorporate content created from other software into Studio." --- # Work with third-party software Creators on Roblox often leverage third-party creation tools to make custom assets and content. These external tools typically specialize in areas like **3D modeling**, **texture creation**, or **audio editing**, and offer additional features and flexibility that complement the Roblox Engine. Check out [Roblox staff 3D art articles](https://devforum.roblox.com/tags/c/resources/roblox-staff/278/art) for guides, tips, and tutorials on using various third-party software. In most workflows, you create custom assets in [third-party software](#examples-of-third-party-software), export them as a common file type, and then [import the file into Studio](#import-to-studio). ## Examples of third-party software The following are examples of third-party software that creators commonly use: - **3D modeling software** - Creators use external 3D modeling software such as [Blender](https://www.blender.org/), [Maya](https://www.autodesk.com/products/maya/overview), or [Cinema 4D](https://www.maxon.net/en/cinema-4d) to design intricate and detailed 3D models. These models can include characters, props, and environmental assets. - **Image editors and texture creation tools** - Graphics designers and texture artists can use tools like [Photoshop](https://www.adobe.com/products/photoshop) or [Substance Painter](https://www.adobe.com/products/substance3d-painter) to create high-quality images. You can use these images for graphic interfaces or as textures for your 3D models. These tools allow for detailed painting, mapping, and manipulation of textures to achieve realistic or stylized effects. - **Audio editing software** - Creators working on sound design or music for their Roblox experiences might use external audio editing software like [Audacity](https://www.audacityteam.org/) or [Reaper](https://www.reaper.fm/). These tools allow for precise control when creating audio assets. By using third-party software, creators can tap into specialized features, workflows, and expertise, while expanding their creative possibilities and enhancing the overall quality of their Roblox experiences. ## Import to Studio Studio provides several tools to get your third-party content into Studio depending on the type of asset you upload: - Models - Uploaded models must follow Studio's [modeling specifications](/docs/en-us/art/modeling/specifications.md). - Studio's [Importer](/docs/en-us/studio/importer.md) supports `.gltf`, `.fbx`, `.obj` file formats. - You can also use tools like the [Roblox Blender plugin](/docs/en-us/art/modeling/roblox-blender-plugin.md) to connect your modeling software to Studio and directly upload your modeling assets between applications. - Images - Uploaded textures must follow Studio's [texture specifications](/docs/en-us/art/modeling/specifications.md). - Studio's [Asset Manager](/docs/en-us/projects/assets/manager.md) supports `.png`, `.jpg`, `.gif`, `.tga`, or `.bmp` file formats and allows for bulk file imports. - Sounds - Uploaded sounds must follow Roblox's [audio asset requirements](/docs/en-us/audio/assets.md#import-audio). - Studio's [Asset Manager](/docs/en-us/projects/assets/manager.md) supports `.ogg`, `.mp3`, `.flac`, and `.wav` file formats and allows for bulk file imports. - Video - You can import video assets in either `.mp4` or `.mov` format if all of the [requirements](/docs/en-us/ui/video-frames.md) are met. > **Warning:** Roblox performs asset moderation on upload of any asset type. Roblox can also moderate assets after the initial upload. For more information, see [Asset moderation](/docs/en-us/projects/assets.md#asset-moderation). --- title: "3D art and Studio" url: /docs/en-us/art/overview-studio last_updated: 2026-06-29T19:33:56Z description: "An overview of fundamental 3D art tools, concepts, workflows, and resources for Roblox Studio." --- # 3D art and Studio [Roblox Studio](/docs/en-us/studio/setup.md) includes free powerful 3D creation tools that enable you to effectively contribute to asset creation, world building, and more. Whether you are just getting started, or are looking for a refresher on Studio tools, this guide provides an overview on using Studio for 3D art creation. Each of the following sections include a brief description as well as additional links for further learning: - [Basic layout of Studio's interface](#studio-interface) — Familiarize yourself with the various panels and tools available in Studio to navigate effectively. - [How to use assets and packages](#assets-and-packages) — Learn to import, edit, and share assets and packages to enhance your workflow and collaboration. - [3D modeling within Studio](#3d-modeling) — Explore the fundamentals of creating and importing 3D models within Studio. - [Materials and textures](#materials-and-textures) — Discover how Roblox uses materials and textures with your models to enhance the surface appearance of your objects. - [Environments](#environments) — Craft immersive environments using terrain tools, lighting effects, and atmospheric elements. ## Studio interface After [installing Studio](/docs/en-us/studio/setup.md), familiarize yourself with Studio's many 3D art tools and interfaces. While you can customize Studio to show various layouts and tools, Studio opens with the most commonly used windows by default. - [Viewport](/docs/en-us/studio/ui-overview.md#3d-viewport) — Represents the workspace of a place. The items here are rendered similarly to how a user may view the environment they were to play your experience. - [Explorer](/docs/en-us/studio/explorer.md) — Shows a hierarchical list of all objects and services in a place. This includes visible objects, like parts and lights, and non-rendered objects like scripts, sounds, and folders. - [Properties](/docs/en-us/studio/properties.md) — Populates all the properties of a selected object such as size, position, material, and much more. Many properties can be set through this window, or programmatically through a script. - [Toolbox](/docs/en-us/projects/assets/toolbox.md) — Contains a selection of [models](/docs/en-us/parts/models.md), [images](/docs/en-us/parts/textures-decals.md), [meshes](/docs/en-us/parts/meshes.md), [audio](/docs/en-us/audio/assets.md), [plugins](/docs/en-us/studio/plugins.md), [videos](/docs/en-us/ui/video-frames.md), and fonts made by Roblox or Roblox community members. - [Asset Manager](/docs/en-us/projects/assets/manager.md) — Lets you manage [places](/docs/en-us/production/publishing/publish-games-and-places.md) and bulk import assets into your experience. - [Output](/docs/en-us/studio/output.md) — Displays all error, warning, or manually returned messages from the Roblox engine. This window is useful for troubleshooting behavior in your experience. For more information on the Studio interface and individual tools, see the [Studio overview](/docs/en-us/studio.md). ## Assets and packages Before you begin creating, it's important to understand how Roblox handles **assets** and **packages**, the most basic object or group of objects that you can save, use, and share. - [Assets](/docs/en-us/projects/assets.md) are a cloud-based version of an object that you can access any time to use, share, and modify. You can make nearly any object into an asset, such as a prop, building, character, sound, image, script, and more. - [Packages](/docs/en-us/projects/assets/packages.md) are the Roblox equivalent of Unity **Prefabs** or Unreal **Blueprints**, or a group of assets bundled together to reuse in one or more experiences. Packages can update and propagate their changes to any of their copies across one or more experiences, letting you take advantage of powerful versioning and asset management controls. Roblox recommends that most developers work with packages whenever possible. All assets and packages save to your account's [Toolbox](/docs/en-us/projects/assets/toolbox.md) for you or any associated [groups](/docs/en-us/projects/groups.md) to access. You can also manage your saved assets and packages in the **Creations** section of your [Creator Dashboard](https://create.roblox.com/dashboard/creations?activeTab=Model). You can also upload your assets to share on the [Creator Store](#creator-store), and sell certain avatar assets to Roblox's [Marketplace](#marketplace) for users to purchase and equip. ### Creator Store The [Creator Store](/docs/en-us/production/creator-store.md), accessible through the [web](https://create.roblox.com/store/) and directly inside Studio's [Toolbox](/docs/en-us/projects/assets/toolbox.md), contains millions of assets by Roblox and the Roblox community for use in Studio and your experiences. Anyone can contribute their assets to the store, including models, images, meshes, audio, plugins, and more. Roblox also provides [free high quality assets](https://create.roblox.com/store/models?creatorName=Roblox), many of which are used in the following showcases: #### [Mystery of Duvall Drive](https://www.roblox.com/games/7902470429/The-Mystery-of-Duvall-Drive) _⟩  [Behind-the-scenes](/docs/en-us/resources/the-mystery-of-duvall-drive.md)__⟩  [Official model packs](https://create.roblox.com/store/models?creatorName=Roblox&includeOnlyVerifiedCreators=true&keyword=duvall+drive&pageNumber=1&querySource=0)_ #### [Beyond the Dark](https://www.roblox.com/games/7208091524/Beyond-the-Dark-Vistech-Showcase) _⟩  [Behind-the-scenes](/docs/en-us/resources/beyond-the-dark.md)__⟩  [Official model packs](https://create.roblox.com/store/models?creatorName=Roblox&includeOnlyVerifiedCreators=true&keyword=beyond+the+dark&pageNumber=1&querySource=0)_ #### [Realistic Forest Demo](https://www.roblox.com/games/5326950832/Roblox-Realistic-Forest-Demo) ### Marketplace The **Marketplace** is where all Roblox users can purchase virtual cosmetics, such as accessories and clothing, and various character bodies that they can save to their Roblox account and set as their Roblox avatar character. To sell assets on the Marketplace, your creation must follow specific avatar specifications and meet certain policies and guidelines. For more information, see [Avatar](/docs/en-us/avatar.md) and [Marketplace](/docs/en-us/marketplace.md). ## 3D modeling 3D modeling is the process of creating three-dimensional digital representations of objects or environments. Roblox supports [basic 3D modeling](#basic-parts) in Studio, using primitive parts and allowing you to combine, subtract, and intersect the basic parts into more complex models. Roblox also supports [custom 3D models](#mesh-parts) created from 3D modeling software such as Maya and Blender. This includes advanced support for custom models and meshes that include modern textures, animation, and avatar-related features. ![A single gray sphere part](../assets/modeling/parts/Basic-Part-Sphere.png)_Basic sphere part_ ![A bright blue hollow bowl that was made with solid modeling operations.](../assets/modeling/parts/Part-Example-CSG.jpg)_Bowl created with solid modeling_ ![A high-quality treasure chest mesh with a texture.](../assets/modeling/parts/Mesh-Example.jpg)_[Mesh](#mesh-parts) with texture_ ### Basic parts Studio natively supports basic parts that allow you to quickly add simple geometry to your experience. This is ideal for creating simple environments or even greyboxing or prototyping a more complex 3D space. Basic parts include the following benefits: - You can quickly add and modify parts directly in Studio. - Parts are performant and work well with Roblox's native physics behavior. - You can quicly modify the surface appearance of parts using materials and textures. - In many cases, a mix of basic parts and advanced custom mesh and models can work together to quickly create an immersive and performant environment. For more information on using basic parts, as well as solid modeling operations, see [Parts](/docs/en-us/parts.md). ### Mesh parts Meshes are collections of vertices, edges, and faces that make up a 3D object. Unlike parts, which you can directly create in Studio, you first create meshes in a third-party modeling application like Blender or Maya before importing the model file into Studio. After importing a mesh, Studio represents the mesh object as a `Class.MeshPart`. ![A Roblox avatar wearing a glossy puffy jacket and jeans](../assets/modeling/surface-appearance/Layered-Clothing-Example.png)_Create custom accessories, clothing, and characters that anyone can wear and use._ ![A floating alien creature in space, from the Beyond The Dark experience](../assets/modeling/meshes/Beyond-The-Dark-Example.png)_Design a custom character unique to your experience._ ![A cluttered interior room with a candle in the forefront and spooky interior lighting from the Mystery of Duvall Drive experience](../assets/modeling/meshes/Mystery-of-Duvall-Example.png)_Use custom meshes and models to give your environments ambiance and depth._ For more information on creating custom meshes, see [Custom meshes](/docs/en-us/art/modeling.md). ## Materials and textures It's always important to consider the surface appearance of your objects, as this empowers you to create cohesive and engaging assets and environments. Studio includes many tools to manage the look and feel of your assets, primarily by customizing your objects with [materials](#materials) and [textures](#textures). ### Materials Materials allow you to quickly set the visual and physical properties of objects. While Roblox provides dozens of preset materials that you can set your parts to, you can also use the [Material Generator](/docs/en-us/parts/materials.md#material-generator) to create your own custom materials using a prompt. _ Materials generated by [Material Generator](/docs/en-us/parts/materials.md#material-generator) using a natural language prompt_ Materials also affect physical properties beyond appearance. See [Materials](/docs/en-us/parts/materials.md) for more information. ### Textures Textures are 2D images that you can apply to the surface of an object to change its appearance. Unlike materials, textures are purely visual and never affect the physical properties of the object. In general, you create texture images with third-party software and import them into Studio directly as image files, or baked-in with an imported custom 3D object. You can also use Studio's [Texture Generator](/docs/en-us/studio/texture-generator.md) to create brand new textures for your mesh using a natural language prompt. _A mesh object sets the shape and geometry of the 3D object_ _A texture image map applies a surface appearance and color_ _The mesh and texture combine to make a unique custom 3D object_ > **Info:** Textures are different from user interface objects which can apply customizable text or an image to the [surface](/docs/en-us/ui/in-experience-containers.md) of a part, like a billboard or computer screen. There are two types of textures in Roblox, single image **basic textures** and **advanced PBR textures** that dynamically react to different lighting and environment scenarios. Whether creating assets directly for the [Creator Store](#creator-store) or [Marketplace](#marketplace) or designing content for an experience, you should incorporate PBR textures whenever you need realistic and impressive surface textures. > **Warning:** Loading many unique PBR textures at once can affect performance in your experience. When working with many similar meshes, you can save processing power and memory by using a single set of PBR textures and [modifying alpha channels in combination with your mesh object base colors](https://devforum.roblox.com/t/using-alpha-masks-in-color-maps-to-customizetint-base-colors). > > For additional tips on improving performance with your assets, see [Improve performance](/docs/en-us/performance-optimization/improve.md#rendering). ![A humanoid avatar with dreadlocks, a glossy jacket, jeans with decals, and boots.](../assets/modeling/surface-appearance/Layered-Clothing-Example.png)_A character model wearing cosmetic assets with PBR textures._ ![A realistic leafy bush](../assets/modeling/surface-appearance/SurfaceAppearance-Example-1.jpg)_An environmental prop with PBR textures._ For more information see [Textures and decals](/docs/en-us/parts/textures-decals.md) and [PBR textures](/docs/en-us/art/modeling/surface-appearance.md). ## Environments Environments refer to the visual and spatial world within your experience. Environments are a combination of elements, such as [terrain](#terrain), [lighting](#lighting), architecture, effects, and more. Roblox provides many tools and services to quickly create high definition environments to elevate your experiences. ### Terrain **Terrain** refers to the landscape or outdoor environment of a Roblox experience. Features such as mountains, valleys, hills, bodies of water, and vegetation all make up terrain. To quickly generate terrain, Studio provides a [Terrain Editor](/docs/en-us/studio/terrain-editor.md) that allows you to quickly create various types of environments, such as mountains, grass-colored hills, deserts and more. While many experiences are include interior spaces, such as a city building or spaceship, it's important to use terrain for outdoor environments whenever possible to ensure performance and expected lighting and environmental behavior. For more information, see [Environmental terrain](/docs/en-us/parts/terrain.md). ### Lighting ![A campfire in a glade with a setting sun in the background illuminating the campsite.](../assets/tutorials/enhancing-outdoor-environments/Intro-After.png) **Lighting** refers to the illumination and visual effect applied to the environment and objects in your experience. It plays a crucial role in setting the mood, atmosphere, and general visual quality of your creations. Lighting can be categorized into the following: - **Global lighting** — Refers to the luminescence from either the sun or moon in an outdoor environment. You can set the properties of your global lighting by adjusting the `Class.Lighting` service. For additional hands-on instructions on adjusting global lighting, see the [Customize global lighting](/docs/en-us/tutorials/curriculums/core/building/customize-global-lighting.md) lesson of our Core Curriculum. - **Local lighting** — Refers to light sources placed within your experiences, such as pointlights, spotlights, and surface lights. You can modify lighting scenarios based on different interior rooms or sections of your experience by using one or more light sources. For additional information on modifying your environment's lighting, see [Global lighting](/docs/en-us/environment/lighting.md). --- title: "Reaper" url: /docs/en-us/art/reaper last_updated: 2026-06-29T19:33:56Z description: "Learn about Reaper tools." --- # Reaper Reaper is a Digital Audio Workstation (DAW) with a broad range of audio editing tools that allow you to create, manipulate, and export audio streams. Running on Linux, macOS, and Windows systems, this cross-platform application is a popular choice for creators who want to make custom music and sound effects for Roblox experiences. While this is by no means an exhaustive list, the following guide offers high-level information on essential Reaper tools and features for audio editing, as well as best practice guidance on designing audio files for Studio. > **Info:** Reaper’s free trial mode is fully functional and extendable, allowing you to try the software and see if you’d like to purchase a license for its use in commercial projects. > > To download the latest version of Reaper, visit [Reaper.fm](https://www.reaper.fm/download.php). ## Fundamentals Before you take a look at all of the common workflows for creating custom audio for Studio, let's review Reaper's fundamental interface elements that are important for navigating through the application and finding the appropriate menus and controls for your specific audio editing task. ### Toolbar ![Reaper's UI with the main toolbar highlighted.](../assets/art/3p-software/reaper/Toolbar.png) The **Toolbar** contains various tools that let you perform actions common to most audio workflows, such as: - Opening or saving a project. - Accessing your project's settings. - Enabling a metronome. - Configuring ripple editing. - Disabling audio track snapping behavior. As you get more familiar with Reaper, you can customize the Toolbar with additional commands and actions for your own workflows. ### Track Control Panel ![Reaper's UI with the Track Control Panel highlighted.](../assets/art/3p-software/reaper/TrackControlPanel.png) The **Track Control Panel** controls the behavior of your audio tracks, such as each track's volume, gain, and effects from plugins, as well as their relationships to other audio tracks. There is only one audio track in the example image, but you can add as many audio tracks as you need for your project. ### Timeline ![Reaper's UI with the Timeline highlighted.](../assets/art/3p-software/reaper/Timeline.png) The **Timeline** measures the length of your project and provides visual markers to help you position entire or segments of audio tracks in relation to each other. The Timeline in the example image measures time in both measures/beats and minutes/seconds, but you can customize its units of measurement for your own project requirements. ### Arrange Area ![Reaper's UI with the Arrange Area highlighted.](../assets/art/3p-software/reaper/ArrangeArea.png) The **Arrange Area** lets you view and edit audio tracks within your project using the green scrubber, also known as the cursor. This area represents the main workspace for audio manipulation. ### Transport Bar ![Reaper's UI with the Transport Bar highlighted.](../assets/art/3p-software/reaper/TransportBar.png) The **Transport Bar** lets you control audio recordings and playback, such as: - Jumping to the start or end of an audio track. - Recording additional audio. - Modifying the time signature, tempo, or playback rate. It's recommended to experiment with hotkeys for these actions to make your workflows for efficient. ### Mixer Control Panel ![Reaper's UI with the Mixer Control Panel highlighted.](../assets/art/3p-software/reaper/MixerControlPanel.png) The **Mixer Control Panel** offers a different way to view your tracks. Having the volume meters laid out horizontally in the mixer can allow for more visual clarity on what elements are the loudest in your arrangement, but this is simply a visual preference. All features of Reaper can be utilized with the mixer closed, and many sound designers don't even use the view. ### FX Plugins ![Reaper's UI with the FX Plugins window highlighted.](../assets/art/3p-software/reaper/FXPlugins.png) Similar to plugins in Studio, the **Plugins Window** lets you add additional features or functionality to Reaper to make audio editing and workflows easier. Reaper offers over 200 fx plugins with the creation suite for your convenience, and you can import additional plugins from third-party creators. ## Common workflows Now that you know how to navigate the user interface, let's take a closer look at the most common audio editing workflows that allow you to create, manipulate, and export audio streams for Roblox experiences. > **Info:** For more information on any of these workflows, see Reaper's official [Up and Running: A Reaper User Guide](https://www.reaper.fm/userguide.php) documentation. ### Create fade ins and outs In audio design, it's common to trim a small section off of an audio file, then fade it in and out to help smooth the sound's transition from start to finish. This process is important because ambient sounds in the real world don't often start and stop suddenly; if they did, it would feel extremely jarring. For example, let's review the following two audio clips of a wind gust; the first audio clip includes fade ins and outs to help the sound feel more natural while the second keeps the initial cut. As you listen to both, notice how the first clip feels realistic while the other feels abrupt. Without these transitions, the sound would break a player's sense of immersion in an experience. | | | | --- | --- | | Audio with transitions | Audio without transitions | To create fades ins and outs: 1. Import your audio file. 1. Select a `.mp3`, `.ogg`, or `.wav` file on your computer. 2. Drag-and-drop the audio file into the **Arrange Area**. 2. Crop the sample until you have a portion of the sound you want to play in your experience. 1. Hover over the left-side of your audio file until your cursor changes to a double-sided arrow icon. 2. Click-and-drag the audio sample to where you want to start your fade in. 3. In a similar process, click-and-drag the right-side of your audio file to where you want to finish your fade out. 3. Transition the sample in and out. 1. Hover over the top-left corner of your audio sample until your cursor changes to a curve icon. 2. Click-and-drag the audio sample to where you want to finish your fade in. 3. In a similar process, click-and-drag the top-right corner of your audio sample to where you want to start your fade out. 4. **(Optional)** Change the transition's curve type. 1. Right-click either your fade in or out curve. A pop-up menu displays with multiple curve types. 2. Select a new curve. The sound fades in or out according to the new curve type. ### Compress audio In audio design, the dynamics of a sound refer to the sound's volume and how that volume changes over time. For example: - An explosion's sound is **dynamic** because it starts extremely loud, then fades to a quiet echo in a few seconds. - A waterfall's sound is **non-dynamic** because even if you were to record the waterfall for a few minutes, it would likely remain at roughly the same volume. While both of these examples have their time and place in projects, compressing audio allows you to reduce the dynamic range of a sound by lowering the volume of loud peaks within a set ratio. This results in the loudest parts of a sound becoming more similar in volume to the quietest parts of a sound. | | | | --- | --- | | Audio with compression | Audio without compression | This is important because without compression, some sounds can be significantly louder than others and drown out important audio cues throughout your experiences, especially when they play through speakers that struggle with wide dynamic ranges, like mobile devices. To compress audio: 1. In the **Track Control Panel**, navigate to the audio track that you want to compress, then click the **FX** button. The **Plugins window** displays. 2. Search for **ReaComp**, short for Reaper Compressor, select the result, then click the **Add** button. A new compressor window displays.![Reaper's Plugins Window with the ReaComp plugin highlighted.](../assets/art/3p-software/reaper/Compress-ReaComp.png) 3. Lower the **Threshold** to the highest peak you want your audio to be able to have before it starts being compressed.![Reaper's Compressor UI with the Threshold toggle highlighted.](../assets/art/3p-software/reaper/Compress-Threshold.png) 4. Play your audio while looking at the compressor window. A bright red section displays when sounds go over your set threshold. In the example image, the red meter reads roughly -8; this means that the loud parts of the sound have been turned down by about 8 decibels.![Reaper's Compressor UI with the compression bar highlighted.](../assets/art/3p-software/reaper/Compress-CompressionBar.png) 5. For further compression configurations: 1. Increase the **Ratio** to make the effect of volume reduction more pronounced. 2. Customize your **Attack** and **Release** times to change how quickly the compressor starts and finishs compressing your audio track. ### Equalize frequencies As you are editing your audio, it occasionally becomes necessary to make adjustments to its frequency balance so that you can do things like: - Increase the volume of its bright, high frequencies. - Lower the volume of its bassy, low frequencies. - Remove a shrill, harsh tone from the middle range of frequencies. This process is called **equalizing**, and it's important because it allows you to mimic realistic acoustics for different gameplay areas. For example, if a player is underwater, high frequencies would naturally be muffled and low frequencies would be easily audible, but if the player moved to an open space outside, no muffling would occur, and high frequencies would be audible again. Let's look at this in practice with two audio clips of a rain sound; the first audio clip includes a lowered high shelf while the second doesn't include any equalizing. As you listen to both, notice how the first clip feels muted and warm like it's hitting the window while you are inside, and the second clip feels cold like you are outside. | | | | --- | --- | | Audio with equalizing frequencies | Audio without equalizing frequencies | > **Info:** Equalizing frequencies is an art form, and it takes time to figure out what configurations work to meet the needs of your experiences. It's recommended to experiment often until you find what sounds best to you. To equalize frequencies in a sound: 1. In the **Track Control Panel**, navigate to the audio track that you want to equalize its frequencies, then click the **FX** button. The **Plugins window** displays. 2. Search for **ReaEQ**, short for Reaper Equalizer, select the result, then click the **Add** button. A new equalizer window displays with four enabled bands.![Reaper's Plugins Window with the ReaEQ plugin highlighted.](../assets/art/3p-software/reaper/Equalize-ReaEQ.png) 3. Select any of the enabled bands, then set **Type** according to what frequencies you want to equalize. Most use case use one of the following: - **Low Shelf** - Moves the gain below the frequency setting. This is typically the first band. - **High Shelf** - Moves the gain above the frequency setting. This is typically the last band. - **Band** - The volume is raised or lowered on either side of the frequency, and the range is determined by the bandwidth setting. - **Low Pass** - Filters frequencies above the frequency setting. - **High Pass** - Filters frequencies below the frequency setting.![Reaper's Equalizer UI with the first band highlighted and the Type dropdown set to Low Shelf](../assets/art/3p-software/reaper/Equalize-Type.png) 4. Play your audio while looking at the equalizer window, and make any necessary adjustments for the sound quality you want. ### Reduce noise When you record audio, it is almost impossible to completely avoid capturing additional undesirable noise, such as the hum of an air conditioning unit, street traffic, or planes flying overhead. For this reason, it's important to reduce noise in your audio clips so that players can focus on what matters most without distraction. | | | | --- | --- | | Audio with noise reduction | Audio without noise reduction | While subtle noise reduction may seem unnecessary, when multiple layered sounds play at the same time, such as a character's footsteps, dialogue, and an ambient backdrop, the subtle noise becomes much more noticeable. This process ensures that all of our audio stands out, especially when players are wearing headphones. To reduce noise: 1. In the **Track Control Panel**, navigate to the audio track that you want to equalize its frequencies, then click the **FX** button. The **Plugins window** displays. 2. Search for **ReaFir**, short for Reaper Finite Impulse Responser, select the result, then click the **Add** button. A new finite impulse response window displays.![Reaper's Plugins Window with the ReaFir plugin highlighted.](../assets/art/3p-software/reaper/Noise-ReaFir.png) 3. Configure the following settings: 1. Set **Mode** to **Subtract**. New settings display. 2. Enable **Automatically build noise profile**.![Reaper's Plugins Window with the noise reduction settings highlighted.](../assets/art/3p-software/reaper/Noise-Subtract.png) 4. Play the part of your sound that contains the noise you want to reduce for several seconds, but do **not** play the part of the sound you want to keep. This step teaches the plugin what the undesirable frequencies are. 5. When the plugin has learned the spectrum of your noise, disable **Automatically build noise profile**. 6. Play your audio to hear it again to hear it with reduced noise. ### Create audio variations For audio design in experiences, it's important to create variations of sounds that players will hear often during their gameplay, such as footsteps, sword swings, or collection feedback. In the same way that real world sounds almost never repeat exactly the same, variations in sound improves player immersion and leads to a dynamic player experience. | | | | --- | --- | | Audio with variations | Audio without variations | There are many ways to create variation in your audio, but the following step-by-step instruction will focus on a few simple steps you can take to create three alternatives in less than three minutes. To create variations in your audio: 1. In the **Arrange Area**, duplicate your audio clip until you have three exact copies. The first copy will remain your original sound while the other two will become your variations.![Reaper's Arrange Area with three copies of an audio segment.](../assets/art/3p-software/reaper/Variations-Copies.png) 2. Right-click the second copy, then from the contextual menu, select **Item properties…**. The **Media Item Properties** window displays for the second copy. 3. In the **Pitch Adjustment** field, raise or lower the pitch of the sound. 4. Click the **Apply** button. 5. Right-click the third copy, then from the contextual menu, select **Item properties…**. The **Media Item Properties** window displays for the third copy. 6. In the **Playback rate** field, increase or decrease the playback rate. 7. Click the **Apply** button, then play your original and both variations to hear the difference between all three. ### Loop audio > **Info:** For a step-by-step tutorial from an official Roblox Sound Designer on looping sounds for experiences, see [Creating Perfect Audio Loops](https://devforum.roblox.com/t/creating-perfect-audio-loops/2849057). When you record an ambient sound in the real world, it almost never loops perfectly. Even monotonous, non-dynamic sounds like the hum of an air conditioner will click and pop as they loop, breaking player immersion and pulling them out of their gameplay. | | | | --- | --- | | Audio with seamless loops | Audio without seamless loops | This becomes more obvious when there are multiple looping ambient tracks that layer on top of one another. For example, let's say you have an exploration experience with frog croaking, insects chirping, wind gently breezing by, and an upbeat background track. As these sounds loops, there may be harsh volume and/or frequency jumps. In the best case scenario, this just annoys players, but in the worst case scenario, it can cause them to leave the experience altogether. With a little extra effort, you can create seamless loops that keep your intended mood without disruption. To create a loop: 1. Ensure your audio clip is long enough to avoid the risk of being too obvious if it loops. For most use cases, it should be at least 15 seconds. 2. In the **Arrange Area**, find a small point in the sound where your waveform(s) cross the Z axis, or `0`. This means that the sound has no amplitude, so it won't pop or click when the sound loops.![An audio track with the cursor over the loop point.](../assets/art/3p-software/reaper/Loop-LoopPoint.png) 3. Press `S` to split the track. Your audio track is now cut into two segments.![An audio track with two segments, seperated at the loop point.](../assets/art/3p-software/reaper/Loop-Split.png) 4. Move the second clip so that it plays before the first clip and has a slight overlap indicated by the red outline. 5. Play your audio to hear it loop seamlessly. ### Export audio After you finish editing your audio, you can export it for use in Studio. The following settings meet the requirements of most use cases, but as you become a more advanced user of Reaper, you can customize the settings for your own projects To export audio: 1. In the **Arrange Area**, click-and-drag over your audio sample to highlight what you want to export. 2. In the menu bar, navigate to **File** > **Render…**. The **Render to File** window displays. 3. Configure the following settings: 1. Set **Source** to **Master mix**. 2. Set **Bounds** to **Time selection** to export your selection in the Arrange Area. 3. Set **Directory** to the location you want to download your file. 4. Set **Name** to what you want to call your file. 5. Set **Sample Rate** to `48000` **Hz** to ensure a high quality level at standard pitch, while also keeping the file size optimally small for lower memory footprint. > **Info:** If you want a higher quality and plan to pitch the sound down at runtime dynamically, you can increase the quality to `96,000` **Hz**. 6. Set **Channels** to **Stereo** for 2 channels or **Mono** for 1 channel. 7. Set **Primary output format** to **OGG vorbis**. This is the preferred file type for experiences as it doesn't add any additional silence to the start or end of your audio. > **Warning:** Do not use the `.mp3` output format as it will add a few milliseconds of silence to the beginning of your audio. 8. Set **VBR quality** to `0.9` to increase the quality. 9. At the bottom of the window, click **Render 1 file**. A new window displays with a preview of what your audio looks like. > **Success:** For information on how to use your new custom audio files in experiences, see the [Audio](/docs/en-us/audio.md) guides. --- title: "Create and access millions of assets" url: /docs/en-us/assets last_updated: 2026-06-29T19:33:56Z description: "Learn how to create and sell assets on Roblox." --- # Create and access millions of assets Assets are anything you publish to the Roblox cloud, like 3D models with scripts, textures, multimedia files, Studio plugins, and much more. You can reuse assets across all of your creations and share them with the community. [Learn more about assets](/docs/en-us/projects/assets.md) ## Create in Studio, other third-party tools, or in experiences Roblox Studio provides drag-and-drop tools and programmatic access to building and modeling with engine primitives such as parts and constraints. You can also import industry standard asset types for 3D modeling, audio, and more directly into Studio. Lastly, you can allow users to create assets directly in your experiences and save them to their own accounts. [Roblox Studio](/docs/en-us/art/overview-studio.md) [Third-party](/docs/en-us/art/overview-dcc.md) [In-experience](/docs/en-us/projects/assets/in-experience-asset-creation.md) ![Create in Studio, other third-party tools, or in experiences](/assets/getting-started/platform-overview/Everything-You-Need.jpg) ## Reuse and update with packages Packages are reusable sets of assets that can contain models, scripts, multimedia files, and more. They let you bundle, share, maintain, and update them across everywhere they're used in your projects, automatically. [Learn more](/docs/en-us/projects/assets/packages.md) ## Publish with privacy and moderation included You can create assets for exclusive use in your projects or share them with the community. Our moderation policies protect your creations from unapproved usage and our users from assets that violate Roblox policies. [Privacy](/docs/en-us/projects/assets/privacy.md) [Moderation](/docs/en-us/projects/assets.md#asset-moderation) [Manage and publish](/docs/en-us/projects/assets/manager.md) ![Publish with privacy and moderation included ](./assets/landing/assets.png) ## Sell on the Creator Store The Creator Store is full of assets made by the community for people to use within their experiences. You can become a seller on the Creator Store to monetize any models or plugins that you create for United States Dollars (USD). Creator Store's real-world pricing model allows us to give 100% of net proceeds on transactions to creators, bypassing platform fees and DevEx rates. [Creator Store](/docs/en-us/production/creator-store.md) [Monetize models and plugins](/docs/en-us/production/creator-store.md#distribute-and-sell-assets) ![Sell on the Creator Store](./assets/landing/creator-store.png) --- title: "Assistant for Studio" url: /docs/en-us/assistant/guide last_updated: 2026-06-29T19:33:56Z description: "How to use Assistant to help build, grow, and monetize your creations in Studio." --- # Assistant for Studio **Assistant** is an AI helper that helps you build experiences faster by answering questions, generating content, and performing actions directly in Studio. It can do the following and much more: - Answer questions about Roblox development - Create and modify objects and scripts in your place's data model - Insert assets from the Creator Store - Explain selected code in the Script Editor - Generate materials to restyle your objects - Generate 3D meshes and procedural models to populate your scene In Studio, Assistant combines a large language model (LLM) with a run-command system that can act directly on your data model. It can insert and modify objects, create scripts, and automate repetitive tasks such as updating properties in bulk. For a more in-depth look at what Assistant can do and how to use it, see the [Prompt guide and examples](/docs/en-us/assistant/prompt-engineering.md) and the following Roblox Staff livestream for tips, tricks, and inspiration. ## Access Assistant from Studio To access Assistant from Studio: 1. Click **Assistant** on the right side of the mezzanine bar.![Assistant button indicated on the right side of the mezzanine bar.](../assets/studio/general/Toolbar-Assistant.png) 2. Enter a request in the Assistant window. - To generate a new response, click the redo icon. - To rate the response and improve future results, click thumbs up or thumbs down.![General user interface for Assistant in Studio.](../assets/assistant/Studio-General-UI.png) ## Ask questions and explain code If you need general knowledge or help while creating an experience, you can ask Assistant questions like how to make a team system, how to design a game loop, how to use specific Studio tools, and much more. It can even explain code that it generated or that you wrote yourself. ![Code explanation provided by Assistant in Studio.](../assets/assistant/Studio-Explain-Code.png) ## Edit your place ### Scripts Assistant can insert new scripts, modify existing ones, and perform these actions across multiple objects as necessary. For example, if you ask it to create a remote event, it can create a local script, a server script, and the event all at once. If you enter a script generation request like "Make the player's character jump when they touch this part," Assistant shows the script's contents and adds the new script instance to the currently selected object in the data model. If nothing is selected, Assistant decides where to place it based on your prompts. ![Requested script inserted into selected part.](../assets/assistant/Studio-Script-Insert.png) The generated script might not function flawlessly. In these cases, you can either make further edits in the [Script Editor](/docs/en-us/studio/script-editor.md) or ask Assistant to edit the script it just created. It can even act on existing scripts that it didn't create if you need help with code that you've written yourself. ### Objects and Creator Store assets Assistant can create, edit, delete and iterate on instances in your data model, including inserting items from the Creator Store. ![Assistant adding objects through the Studio prompt.](../assets/assistant/Studio-Object-Insert.png) If you examine the code, you can see that Assistant calls `Class.InsertService:GetFreeModels()` to query the Creator Store for a wheelbarrow model and uses `Class.Model:PivotTo()` to place it near a tree. ## Generate content ### Materials When given a request to generate a material, Assistant in Studio can quickly style existing parts with a lightweight version of the [Material Generator](/docs/en-us/studio/material-generator.md). ![Material variations shown in Assistant for quick styling.](../assets/assistant/Studio-Quick-Styling-Material.png) ### Meshes To generate a textured 3D mesh: - Use the `/generate` command followed by your prompt. For example, `/generate_mesh a red buggy with knobby tires`. - Ask Assistant to create a mesh directly. For example, "Make a futuristic crate." - Prompt with a reference image instead of text. `/generate_mesh` accepts either a text prompt or an image, but not both in the same request. You can use a Part in your workspace as a **bounding box**. If you select a Part before entering a prompt, Assistant uses the Part's size and location as input and ensures the generated mesh fits within the defined space. You can also define a **maximum triangle count** for the returned model. Lower values result in more faceted and low-poly generations. This is an optional input, which defaults to 10,000. ![A generated green dragon provided by Assistant in Studio.](../assets/assistant/Studio-Generated-Green-Dragon.jpg) ### Procedural models > **Info:** You can generate up to **50 procedural models within a 24-hour rolling window**. This limit doesn't reset at a fixed time; instead, it updates continuously based on the number of requests made in the past 24 hours. Use the `/generate_procedural_model` command to generate [procedural models](/docs/en-us/parts/procedural-models.md) that scale and adapt automatically. Procedural models let you: - Create flexible 3D models with minimal input and adjust parameters without having to rebuild your models. - Automatically integrate with engine features like undo/redo, Team Create, network replication, scaling, and dragger tools. - Maintain high performance, with models behaving like standard objects until their parameters change. This means these models add almost no overhead to your experience. - Keep generated content organized in a dedicated `GeneratedFolder`, separating source and output. To generate a procedural model, type a command such as `/generate_procedural_model a stack of books` or attach a reference image to your prompt. Assistant then generates the procedural model and adds it directly to your workspace for further customization. ![A generated ferris wheel with Assistant in Studio.](../assets/assistant/Studio-Procedural-Generation-Ferris-Wheel.png) ### Segmentation Segmentation divides a generated asset into individual parts, giving you more control over customization. It lets you apply different materials, attach scripts, or replace individual components without regenerating the entire model. When you run `/generate_mesh` or `/generate_procedural_model`, Assistant suggests a segmentation plan that defines how the model will be divided. Before generation, you can edit the proposed part list under **Part Names** by adding or removing entries, or by regenerating the suggested segmentation. Each generated asset can have a maximum of eight parts. When the configuration is ready, click **Generate** to start generation or **Cancel** to discard the request. For example, if you generate a skateboard and define five parts (`body`, `left rear wheel`, `right rear wheel`, `left front wheel`, and `right front wheel`), Assistant segments the model into those components, allowing you to modify each one independently. ![...](../assets/assistant/Segmentation_Example.png) ![...](../assets/assistant/Segmentation_Generation.png) ![...](../assets/assistant/Segmentation_List.png) #### Recommended part names While any generated asset can be segmented, the recommended part names below currently produce the most reliable results for their corresponding object types. When defining your own segmentation, use these part names as a starting point and customize them as needed. | **Object type** | **Recommended part names** | | --- | --- | | car | body, left front wheel, right front wheel, left rear wheel, right rear wheel | | ambulance / police car | body, left front wheel, right front wheel, left rear wheel, right rear wheel, siren light bar | | combat car | body, left front wheel, right front wheel, left rear wheel, right rear wheel, gun | | tow truck | body, left front wheel, right front wheel, left rear wheel, right rear wheel, tow hook | | trailer | body, left wheel, right wheel, hitch | | motorcycle | frame, handlebars, front fork, front wheel, rear wheel | | skateboard | body, left front wheel, right front wheel, left rear wheel, right rear wheel | | prop plane | body, propeller | | combat prop plane | body, propeller, gun | | jet plane | body, thruster | | helicopter | body, main rotor blade | | combat helicopter | body, main rotor blade, gun | | drone | body, left front propeller, right front propeller, left rear propeller, right rear propeller | | jetpack | pack, thruster | | motor boat | body, motor | | combat motor boat | body, motor, cannon | | submarine | hull, propeller | | flashlight | handle body, head, lens | | treasure chest | body, lid | | pickaxe | handle, head | | gun | grip, frame, barrel | | sniper rifle | grip, frame, barrel, scope | | turret | tripod base, swivel mount, gun body, barrel, shield plate | | cannon | carriage base, barrel, left wheel, right wheel | | wand | handle, tip | | sword | handle, hand guard, blade, pommel | | baseball bat | handle, barrel | | grenade | grenade body, safety lever, pull pin | For more examples of working with segmented models, see the [Behaviors Reference Experience](https://www.roblox.com/games/105116507559995/Behaviors-Reference-Experience) `.rbxl` file. ## Planning Mode Planning Mode lets you preview Assistant's next steps before execution. When you submit a complex request, Assistant generates a plan that you can review and adjust. You can activate Planning Mode by either: - Clicking the dropdown in the Assistant input panel and selecting **Plan**. - Entering the command `/plan` in Assistant. ![Example of Planning Mode in Assistant.](../assets/assistant/PlanningMode.png) ## Skills In Studio, Assistant uses a number of skills to help it perform tasks more consistently and comprehensively. Assistant chooses the appropriate skills automatically based on your request. Skills are just folders with a `SKILL.md` file and any supporting resources (scripts, a `commands` directory with supplemental Markdown files, etc.). See the full list of Assistant skills in the open source repository. #### Assistant skills View all of Assistant's skill files. --- title: "MCP servers" url: /docs/en-us/assistant/mcp last_updated: 2026-06-29T19:33:56Z description: "Use both the built-in Studio MCP server and third-party MCP servers to give Assistant context from external tools." --- # MCP servers Model Context Protocol (MCP) servers connect AI models to your local data, tools, and systems. They allow large language models (LLMs) to securely access files, databases, and APIs, providing additional context that helps generate more accurate and useful results. With [Roblox's open source Studio MCP server](https://github.com/Roblox/studio-rust-mcp-server), you can access and edit your experience directly from external AI coding tools. These AI agents can plan, write, test, and modify your code and data with little intervention and effort on your part. You can integrate and leverage models from the following LLMs into your workflow: - [Anthropic](https://platform.claude.com/settings/keys) - [OpenAI](https://platform.openai.com/api-keys) - [Google Gemini](https://ai.google.dev/gemini-api/docs/api-key) To connect and use an external LLM: 1. Get an API key from the LLM you want to use. 2. In Studio, open Assistant. 3. Click **⋯** ⟩ **Manage API Keys**. 4. Select a provider and enter your API key. 5. In the **Model** drop-down, select the model you want to use. ![BYOK in Assistant Settings.](./../assets/assistant/BYOK.png) --- title: "Assistant" url: /docs/en-us/assistant/overview last_updated: 2026-06-29T19:33:56Z description: "Overview of Assistant, Roblox's AI helper." --- # Assistant Assistant is an AI helper that accelerates content creation by helping you get started, supplementing your skills, and assisting with ongoing development. ## Assistant for Studio In Roblox Studio, Assistant can act directly on the data model to write scripts, insert and modify objects, and automate repetitive tasks so that you can focus on creation. [Assistant for Studio guide](/docs/en-us/assistant/assistant/guide.md) ![Assistant for Studio](/assets/assistant/AssistantStudioExample.png) ## Assistant in the documentation Assistant is available directly from the documentation. Ask it to clarify concepts, provide sample code for a use case, or compare and contrast approaches to a problem. ![Assistant in the documentation](/assets/assistant/AssistantDocumentationSite.png) --- title: "Assistant prompt guide and examples" url: /docs/en-us/assistant/prompt-engineering last_updated: 2026-06-29T19:33:56Z description: "Get tips and tricks on how best to prompt Roblox's AI Assistant to get the best results." --- # Assistant prompt guide and examples > **Success:**[Share your prompts and experiences](https://devforum.roblox.com/t/check-out-the-generative-ai-stream-and-let-us-know-your-best-prompts/3215844) in the Roblox forum so that we can make Assistant better! This document gives guidance on prompting Assistant and provides examples and inspiration on what Assistant can do. ## Best practices These best practices can help you get better results when using Assistant. Try them out and see what works best for your workflow. ### Be specific If Assistant fails, add more details to your prompt and try again. Use instance names exactly as they're spelled, specify which function you want Assistant to use, and tell Assistant which type it's working with, such as a part or a model. | **Not specific** | **More specific** | | --- | --- | | Place the trees in a circle. | Place 10 copies of the "pine_tree" model in ReplicatedStorage in a 30 stud radius circle around the SpawnLocation. | | Make the part move. | Add a script to make "my_part" move up and down 10 studs repeatedly using TweenService. | | How can I design a core loop? | Help me design a fun core loop. I want to build a game where players can hunt treasure as pirates. They can also buy and upgrade pirate ships and battle each other. | ### Use your selection Selection is a great way of specifying an object or set of objects you want to refer to in your prompt. Select multiple objects and ask Assistant to modify them, make duplicates and place them somewhere, or continue a position pattern like maintaining placement in a line. ### Edit time and run time Assistant sometimes confuses the difference between edit time and run time. If you ask Assistant to take an action, it will likely bias towards doing it at edit time. If you want the action to happen during run time, ask Assistant to insert a script that takes the action. For example, tell Assistant "add a script to make the time of day 8:00 AM" instead of "make the time of day 8:00 AM." ### Multi-step prompts Consider using multi-step prompts to encourage Assistant to think. Instead of asking "Can you explain this script to me?" and pasting in the script, ask it to explain step-by-step by separating the question from the script with `###`. This separation helps Assistant deconstruct and organize the information to provide better answers. #### Prompt > _Can you explain what this script is doing step by step?_ > > **###** > > _local part = workspace.Part_ > > _local function onTouched(otherPart)_     _print(part.Name .. " collided with " .. otherPart.Name)_ _end_ > > _part.Touched:Connect(onTouched)_ > > **###** #### Assistant response > _Certainly! This script is an example of how to handle collisions between two parts in Roblox. Let's break it down step by step:_1. `local part = workspace.Part`: _This line of code creates a local variable named `part` and assigns it the value of the `Part` object located in the workspace. You can replace `Part` with the name of the specific part you want to detect collisions with._2. `local function onTouched(otherPart)`: _This line of code defines a function named `onTouched` that takes in a parameter `otherPart`. This function will be called whenever the part collides with another part._3. `print(part.Name .. " collided with " .. otherPart.Name)`: _This line of code prints a message to the output console, indicating that the `part` has collided with `otherPart`. The `part.Name` and `otherPart.Name` are concatenated using the `..` operator to form the message._4. `part.Touched:Connect(onTouched)`: _This line of code connects the `onTouched` function to the `Touched` event of `part`. Whenever the part collides with another part, the `onTouched` function will be called._ ### Prompting for procedural models Procedural model generation can take longer than text responses. To continue working efficiently, you can keep typing your next prompt while Assistant is generating your model. When writing prompts for procedural models, make sure to explicitly specify the properties and parameters you want to be able to edit. For example, you can ask Assistant to "add properties to change the colors of the cabin." It's also helpful to define how different parts of the model should respond to changes in scale or count. For example, you can say "when I scale the table longer or wider, the model should add more chairs proportionally." You can even include specific technical details, like measurements of spacing between parts, like "add a property to control the distance between the rails of the railroad." ### Reference images for procedural models When uploading images for procedural model generation, use `.png` images. Choose images with good lighting, a clear focus on the object of interest, and a simple background. When possible, use images with good spatial information, like isometric or 3/4 angles, instead of direct front, side, or back views. Avoid low-poly style images. For objects that can't be fully captured from a single angle, you can include multiple viewpoints in a single image to improve the accuracy of the procedural model generation. ### Keep trying Don't get discouraged if Assistant doesn't work exactly the way you want the first time. Often, making small tweaks and trying again can lead to better results. Many AI tools are non-deterministic, meaning they don't create the exact same output each time you ask them to do something. There's some variance, which you can tap into and control using tweaks to your prompt. AI in its current state requires work. It takes time to understand how to best speak to Assistant, what it can do, and how to get what you want out of it. ## Examples These examples demonstrate the vast variety of tasks that Assistant can help with today. Some of them might take a few tries for you to reproduce, but give them a try and see what you can come up with yourself. ### Game mechanics - Shoot fireballs that explode **Prompt:** Propel a fireball away from the player in the direction the player is facing when the player presses "e". Make the fireball explode when it hits something. ### Game mechanics - Set up a teams system and assign players **Prompt:** Add a script to make a system of teams (red, blue, green, yellow) and assign each player randomly to a team. ### Game mechanics - Temple run style constant running **Prompt:** Make my character run forward constantly like in a temple run game. ### Game mechanics - Fire power up **Prompt:** Add a script that makes the player light on fire and jump 3 time as high if they press "q". The fire should be attached to the player and move with them. If they press "q" again, turn off the fire and reset the jump power. ### Game mechanics - NPC lasers **Prompt:** Add a script that makes this shoot a laser using a long thin part that's blue and neon at the player if they're within 30 studs. Add a script to make this look towards and slowly move towards the closest player if they're within 50 studs. Only move in the x and z axes, keep the y axis constant. ### Game mechanics - Interactive NPC **Prompt:** This is an NPC. Add a prompt that lets the player interact with it. If the player interacts, have the NPC say "hello [player's name]" where player's name is the actual player's name, in a text pop up. Add a script that makes this npc always look towards the closest player. ### Game mechanics - NPC patrolling **Prompt:** Add a script to make this NPC move slowly between startpart and endpart repeatedly. It should face the part and then move to it, then face the other part and move to it. If the player comes within 10 studs, turn the NPC red and make it chase the player. ### Game mechanics - Update scoreboard **Prompt:** Add a script that deletes this and adds 100 to the players score. Add a script to show the player's score on the scoreboard. ### Camera - Lock to top down **Prompt:** Add a script that locks the camera in a top down view on the player and follow the player, making sure the player remains in the center of the screen. ### Camera - Lock to first person **Prompt:** Create a script to lock the camera in first person view. ### UI - Drop-down menu **Prompt:** Create a drop down menu with 5 items. ### UI - Health bar **Prompt:** Add a part on the ground, when a player touches it, it decreases health by 10%. Put a UI health bar in the upper center of the screen that turns red when the players health is less than 20%. ### UI - Simple HUD **Prompt:** Create a heads up display in StarterGui. Add a healthbar to the top right, and a text label with the player's name underneath it. In the bottom right, add 4 buttons in a diamond shape, with "Potions" on top, "Weapons" on the left, "Inventory" on the bottom, and "Special" on the right. Make the colors fall color themed. ### Building - Scatter objects with randomization **Prompt:** Add 0-5 of the selected instance "Mushroom" around each "RedwoodTree-Var01". ### Building - Night / day cycle with street lights **Prompt:** Add a script that changes the time of day by 1 hour every second. Start at 3pm. At 7pm, turn every spotlight's brightness up to 10. At 8am turn every spotlight's brightness down to 0. ### Building - Physics-based suspension bridge **Prompt:** Create a rope bridge. Make 10 wood planks that are 5 studs wide and 2 studs long. Place them in a row. Add rop constraints on each side of the parts, connecting each one to the part before it and after it. Make all the rope constraints visible, anchor the 1st and 10th part, and add a drag detector on the 1st and 10th part. ### Building - Add smoke to chimneys **Prompt:** Insert an invisible brick that is non-collidable into every chimney in every house. The brick should have a particle inside it that makes smoke that flows upwards, and the smoke must be white. ### Building - Rename instances **Prompt:** Rename all the "emptyscripts" objects to "Script+uniqueID". ### Building - Create terrain **Prompt:** Create a terrain region with rolling hills. ### Building - Add behavior at scale **Prompt:** Add a script to make the spotlights in the folder StreetLights flicker on and off randomly. ### Building - Replace greyboxes with assets **Prompt:** Replace each of the selected parts with a model of the same name currently in the data model inside the AssetLibrary Folder under workspace. For example, if the part is called "Suburban House", look for a model called "Suburban House" and replace the part with that model. ### Building - Create a scalable ferris wheel **Prompt:** /generate_procedural_model Create a cartoon-themed ferris wheel that has a base with a fence and a gate around it. Add properties to edit the number of cabins on the ferris wheel. When the number of cabins changes, the spacing between them should also change proportionally. Also add properties to change the color of each individual cabin. ### Building - Create a modular train track **Prompt:** /generate_procedural_model Create a modular piece of train track that can be reused across different sections. Add a property to control the curvature of the track from 0-90 degrees to allow for bends. Include properties to adjust the distance between the rails, the gauge (rail size), the spacing between the railroad ties, and the overall length of the track. ### Building - Create a modular medieval castle **Prompt:** /generate_procedural_model A stylized, round medieval castle features a red-brick base, a white-plastered upper tier, and a large terracotta conical roof. A single, dominant landmark tower stands beside numerous small, slender turrets, all anchored by green shrubbery. ### Building - Create a modular suspension bridge **Prompt:** /generate_procedural_model A procedural bridge that adjusts the number of suspension cables based on its length and has a 'Rust' material attribute to change the material type. ### Building - Create a scalable dining set **Prompt:** /generate_procedural_model A cyberpunk-inspired dining set with table and chairs. When I scale the table and make it longer or wider, add more chairs proportionally. --- title: "Audio assets" url: /docs/en-us/audio/assets last_updated: 2026-06-29T19:33:56Z description: "Explore how to find and import audio assets for use in your experiences." --- # Audio assets You can [find](#find-audio) a wide variety of free‑to‑use audio assets in the Creator Store, or you can [import](#import-audio) audio assets that you're certain you have permission to use, such as audio that you make yourself. The [asset privacy](/docs/en-us/projects/assets/privacy.md) system automatically ensures that the IDs of your imported audio can't be accessed by users without proper permissions. ## Find audio The [Creator Store](/docs/en-us/production/creator-store.md) tab of the [Toolbox](/docs/en-us/projects/assets/toolbox.md) contains a wide variety of free-to-use audio assets made by Roblox and the Roblox community for creators to use within their experiences, including more than 100,000 professionally-produced sound effects and music tracks from top audio and music partners. To find audio assets on the Creator Store: 1. In the **Toolbox**, navigate to the **Creator Store** tab, then use the category selector dropdown to select **Audio**.![Audio section of Creator Store in Studio's Toolbox](../assets/studio/toolbox/Creator-Store-Audio.png) 2. Use the keyword search, quick filter options, and/or advanced filters to narrow down the results.![Audio discovery options in Studio's Toolbox](../assets/studio/toolbox/Creator-Store-Audio-Discovery.png) 3. Right-click any audio asset, then select **Copy Asset ID** to copy the asset ID to your clipboard. You can now paste this asset ID into a `Class.AudioPlayer` object to [play the audio](/docs/en-us/audio/objects.md). > **Warning:** If you click any audio asset, Studio inserts it as a new `Class.Sound` instance into the **Explorer** window. However, `Class.Sound` objects don't have the same dynamic functionality as `Class.AudioPlayer` objects. ## Import audio You can import custom audio through the [Asset Manager](/docs/en-us/projects/assets/manager.md), Creator Dashboard, and Open Cloud API as long as your audio meets the following requirements: - You have the legal rights to that audio asset. - It adheres to the [Community Rules](https://en.help.roblox.com/hc/articles/203313410) and [Terms of Use](https://en.help.roblox.com/hc/articles/115004647846). - It's a single track/stream in either `.mp3`, `.ogg`, `.wav` or `.flac` format. - It's less than 20 MB in size and 7 minutes in duration. - Its sample rate is less than or equal to 48 kHz. - Its channels are set to mono or stereo 2.0, 3.0, or 5.1 surround. If you're [ID verified](/docs/en-us/production/publishing/account-verification.md), you can import 2,000 free audio assets per 30 days; if you're unverified, you can import 100 free audio assets per 30 days. If a daily import limit exists, it's removed and replaced with a simplified monthly limit. Studio assigns each new audio asset a **unique asset ID** that you can use within your experiences to [play the audio](/docs/en-us/audio/objects.md). #### Asset Manager To import audio through the **Asset Manager**: 1. Click the **Import** button. 2. Select and then confirm the audio files you want to import from your local system. 3. Once you confirm the imports and the files import successfully, they display with a green checkmark and a completed status. > **Info:** To standardize the playback format and check for corrupt or invalid files, Studio transcodes imported audio before saving it as an asset. If you receive an error while importing audio, Studio is likely rejecting it during the transcode process. In addition, some older audio tools generate invalid file headers or frames when exporting. > > If Studio continues to reject the audio you're trying to import, ensure your audio tools are updated, then verify that the audio files meet the import requirements. #### Creator Dashboard To import audio through the [Creator Dashboard](https://create.roblox.com/dashboard/creations): 1. In the horizontal navigation, select **Development Items**. All assets you have previously imported display within their respective category. 2. In the sub-horizontal navigation, select **Audio**. 3. Click the **Upload Asset** button. The **Upload Asset** page displays. 4. In the **Upload Asset** page, 1. Click the **Upload** button, then select and then confirm the audio file you want to import from your local system. 2. Fill out the **Name** and **Description** fields with the audio file's relevant information. 3. At the bottom of the page, click the **Upload** button. Your audio assets are now within the moderation queue and are only visible to you within the **Audio** tab of the Creator Dashboard's **Development Items** page. #### Open Cloud API To import audio through an HTTP request: 1. Copy the API key to the `x-api-key` request header of the [Create Asset](/docs/en-us/reference/cloud/assets/v1.md#POST-v1-assets) endpoint. 2. In your request: 1. Specify that the target asset type is **Audio**. 2. Add your audio asset's name and description. 3. Add the creator information. - If you want to create the audio asset **on your own behalf**, add your user ID. You can find your user ID on the URL of your Roblox profile. For example, for `https://www.roblox.com/users/1234567/profile`, your user ID is `1234567`. - If you want to create the audio asset **as a group asset**, add the group ID of your group. You can find the group ID on the URL of your group's page. For example, for `https://www.roblox.com/groups/7654321/example-group#!/`, the group ID is `7654321`. 4. Add the file path and content type of your asset.```bash curl --location 'https://apis.roblox.com/assets/v1/assets' \ --header 'x-api-key: ${ApiKey}' \ --form 'request="{ \"assetType\": \"Audio\", \"displayName\": \"Name\", \"description\": \"This is a description\", \"creationContext\": { \"creator\": { \"userId\": \"${userId}\" # Use groupId for creating a group asset } } }"' \ --form 'fileContent=@"/filepath/audio.mp3";type=audio/mpeg' ``` After the audio assets pass moderation, you can reuse them in any project on the platform. Although you are initially the only one who can view and use private audio assets, the [asset privacy](/docs/en-us/projects/assets/privacy.md) system lets you grant usage permissions to specific friends and experiences. --- title: "Audio effects" url: /docs/en-us/audio/effects last_updated: 2026-06-29T19:33:56Z description: "Audio effects modify or enhance audio streams in your experiences." --- # Audio effects Audio effects non-destructively modify or enhance audio streams. You can apply these effects to make your audio more immersive within your experience, such as using an `Class.AudioEqualizer` object to make rain sound muffled, `Class.AudioCompressor` object to control a sound's maximum volume, or `Class.AudioReverb` to add more realistic reflections of sound in interior spaces. ## Apply audio effects You can apply audio effects to your audio streams by wiring up the effect within your 2D audio or 3D audio setup before it reaches the player's ears. For example, to review the setup instructions in [Audio objects](/docs/en-us/audio/objects.md#2d-audio), 2D audio requires an audio player to produce the stream, a physical hardware device like a speaker or headphones, and a wire to carry the audio stream from the audio player to the output device. If you keep this setup the way it is, the audio stream from the asset ID plays as it was originally recorded. However, if you want to apply an audio effect to this audio stream, you must introduce the audio effect in the middle of the configuration so that the audio stream transmits through the effect before it transmits to the output device. Roblox's modular audio objects also allow you to wire multiple audio players through an audio effect, meaning that you don't have to have multiple audio effect objects with the same settings. This provides a lot of flexibility in how you customize multiple audio sources at once. In addition, you can apply multiple audio effects to the same audio stream to further customize your audio. As the audio stream transmits from the audio player to the device output, it's important to note that the order of the audio effects impacts the custom sound. For example, if you were to apply an `Class.AudioChorus` effect first, then an `Class.AudioDistortion` effect second, your custom sound may sound different from a configuration that applies the `Class.AudioDistortion` effect before the `Class.AudioChorus` effect. Each audio effect you apply has additional properties you can adjust until you achieve the type of sound you desire. For more information on these properties, see each audio effect's API page. ## Types ### Equalizer `Class.AudioEqualizer` allows for control of the volume of various frequency ranges. This audio effect is useful to highlight or minimize particular elements of audio, such as muffling all audio in your experience when a player goes underwater. _Without `Class.AudioEqualizer` effect_ _With `Class.AudioEqualizer` effect_ ### Compressor `Class.AudioCompressor` reduces the dynamic range of audio by lowering the volume of the highest parts of a source while at the same time raising the overall volume. This audio effect is useful to set a consistent volume for players to hear even if you record audio while whispering or speaking loudly. You can also use this dynamic effect to prioritize audio streams over another through the process of ducking. _Without `Class.AudioCompressor` effect_ _With `Class.AudioCompressor` effect_ ### Reverb `Class.AudioReverb` simulates the effect of sounds bouncing off of several surfaces. This audio effect is useful to simulate more realistic reflections of sounds in interior spaces, such as bouncing a basketball inside a gymnasium, or playing rock music in a stadium. _Without `Class.AudioReverb` effect_ _With `Class.AudioReverb` effect_ ### Chorus `Class.AudioChorus` simulates the effect of multiple vocals or instruments playing simultaneously by taking the original sound and overlaying copies, each with slight variations in pitch. This audio effect is useful for simulating a robotic or futuristic quality to your audio. _Without `Class.AudioChorus` effect_ _With `Class.AudioChorus` effect_ ### Distortion `Class.AudioDistortion` simulates the effect that would occur when overdriving older style audio equipment. This audio effect causes clipping in the sound and adds a general "fuzziness," which is useful for either adding intensity and character to musical instruments. _Without `Class.AudioDistortion` effect_ _With `Class.AudioDistortion` effect_ ### Echo `Class.AudioEcho` causes a sound to repeat on a delay with diminishing volume, simulating the real effect of an echo that bounces off of hard surfaces, such as walls and caves. _Without `Class.AudioEcho` effect_ _With `Class.AudioEcho` effect_ ### Flanger `Class.AudioFlanger` creates a sweeping or whooshing effect by copying the original audio signal and playing it slightly offset and modulated on top of the original. This audio effect is useful for adding strange, otherworldly digital quality to your audio. For example, you can use it on an air conditioner recording to make a sci‑fi spaceship engine rumble. _Without `Class.AudioFlanger` effect_ _With `Class.AudioFlanger` effect_ ### Pitch Shift `Class.AudioPitchShifter` raises or lowers pitch without changing the playback speed. This audio effect is useful for changing the scale of your sounds. You can use it to make small sounds feel huge by pitching them down or conversely pitch sounds up to make an explosion sound more like a balloon pop. _Without `Class.AudioPitchShifter` effect_ _With `Class.AudioPitchShifter` effect_ ### Tremolo `Class.AudioTremolo` creates a trembling effect on a sound by varying its volume up and down. This dynamic effect is useful to transform instrument sounds into a wavy, dreamlike quality, or to create swells and dips in weather‑related audio. _Without `Class.AudioTremolo` effect_ _With `Class.AudioTremolo` effect_ ### Fader The `Class.AudioFader` adjusts the volume of audio streams to be either higher or lower. This audio effect is useful to control the volume and audio effects properties of multiple audio streams at once. ### Analyzer The `Class.AudioAnalyzer` analyzes volume and frequency contents. This audio effect is useful for getting information to understanding how your audio is working together, both in volume and frequency content. --- title: "Audio" url: /docs/en-us/audio last_updated: 2026-06-29T19:33:56Z description: "An overview of audio features on Roblox." --- # Audio Adding audio to your experiences is crucial for elevating your experiences to new heights. By strategically using positional and non-positional audio, you can immerse players into your worlds, provide them useful feedback for their actions, and direct their attention to what they need to do to be successful in their objectives. ## Audio assets You can import [audio assets](/docs/en-us/audio/assets.md) that you're certain you have permission to use, such as audio that you make yourself. However, Roblox's [Creator Store](/docs/en-us/production/creator-store.md) provides you a wide variety of free-to-use audio assets, including more than 100,000 professionally-produced sound effects and music tracks from top audio and music partners. ## Audio objects Roblox offers many different types of [audio objects](/docs/en-us/audio/objects.md) that you can use to play and modify your audio until it meets your experience's design requirements: - The `Class.AudioPlayer` object loads and plays the **audio file** using a set [audio asset ID](/docs/en-us/audio/assets.md). - The `Class.AudioEmitter` object is a **virtual speaker** that emits audio into the 3D environment. - The `Class.AudioListener` object is a **virtual microphone** that picks up audio from the 3D environment. - The `Class.AudioDeviceOutput` object is a **physical hardware device** within the real world, such as a speaker or headphones. - The `Class.AudioDeviceInput` object is a **physical microphone** within the real world. - The `Class.AudioTextToSpeech` object converts **text to audio** using an artificial voice. - The `Class.AudioSpeechToText` object converts **audio to text**. - `Class.Wire|Wires` carry audio streams from one object to another. Using these objects, you can either set audio to play automatically at runtime, or trigger it to play from scripts. For practical applications of these audio objects, see the [Add 2D audio](/docs/en-us/tutorials/use-case-tutorials/audio/add-2D-audio.md), [Add 3D audio](/docs/en-us/tutorials/use-case-tutorials/audio/add-3D-audio.md), [Add text-to-speech](/docs/en-us/tutorials/use-case-tutorials/audio/add-text-to-speech.md), [Add speech-to-text](/docs/en-us/tutorials/use-case-tutorials/audio/speech-to-text.md), and [Add voice chat](/docs/en-us/tutorials/use-case-tutorials/audio/add-voice-chat.md) tutorials. ## Audio effects [Audio effects](/docs/en-us/audio/effects.md) non-destructively modify or enhance audio streams from `Class.AudioPlayer` objects. You can apply these effects to make your environments more captivating and immersive, such as using a `Class.AudioEqualizer` object to make rain sound muffled, `Class.AudioCompressor` object to control a sound's maximum volume, or `Class.AudioReverb` to add more realistic reflections of sound in interior spaces. --- title: "Audio objects" url: /docs/en-us/audio/objects last_updated: 2026-06-29T19:33:56Z description: "An overview of modular audio objects on Roblox." --- # Audio objects Roblox's modular audio objects allow you to have dynamic control over sound and voice chat in your experiences. Almost every audio object corresponds to a real-world audio device, and they all function together to capture and play audio like their physical counterparts. For example, every audio object conceptually falls into the following categories: - Objects that **produce** audio streams, such as audio players. - Objects that **consume** audio streams, such as audio emitters. - Objects that **modify** audio streams, such as audio effects. - Objects that **carry** audio streams from one audio object to another, such as wires. As you read through this guide and learn about how all of these audio objects work together to emit sound, you will learn how to accurately capture and feed music, sound effects, and the human voice from the experience to the player and vice-versa. > **Info:**`Class.Sound`, `Class.SoundGroup`, and `Class.SoundEffect` objects are now discouraged in favor of the more robust functionality of audio objects. ## Play audio To play audio within your experience, it's important to understand the role of each available audio object: - An `Class.AudioPlayer` loads and plays the **audio file** using a set audio asset ID. - An `Class.AudioEmitter` is a **virtual speaker** that emits audio into the 3D environment. - An `Class.AudioListener` is a **virtual microphone** that picks up audio from the 3D environment. - An `Class.AudioDeviceOutput` is a **physical hardware device** within the real world, such as a speaker or headphones. - An `Class.AudioDeviceInput` is a **physical microphone** within the real world. - The `Class.AudioTextToSpeech` object converts **text to audio** using an artificial human voice. - The `Class.AudioSpeechToText` object converts **spoken audio to text**. - A `Class.Wire` **carries audio streams** from one audio object to another. How you pair these audio objects together depends on if you want to emit audio directly to the player's speaker or headphones or from objects in the 3D space. The following sections detail both scenarios. ### 2D audio **2D audio** is non-directional sound that plays from no particular location, remaining at the same volume regardless of the player's position or orientation in the 3D space. This type of audio requires three audio objects: - An audio player to produce an audio stream. - A physical hardware device to play the audio stream in the real world. - A wire to carry the audio stream from the audio player to the output device. To demonstrate how to configure these audio objects in Studio for 2D audio, the following diagram compares each object with their real world audio device counterpart. In summary: - The `Class.AudioPlayer` loads and plays your audio asset with specified settings. - The `Class.AudioDeviceOutput` allows the player to hear the audio through their speaker or headphones. - The `Class.Wire` connects to the audio player with its `Class.Wire.SourceInstance|SourceInstance` property, and to the physical hardware device with its `Class.Wire.TargetInstance|TargetInstance` property. It then acts as a bridge to carry the audio stream from the audio player to the player's output device. To play non-directional audio: 1. In the **Explorer** window, go to **SoundService** and insert the following: 1. An **AudioPlayer** object to create an audio source. 2. An **AudioDeviceOutput** object to create a speaker that plays throughout the experience. 3. A **Wire** object to connect the stream from the audio player to the speaker. 2. In the **Properties** window of the **AudioPlayer** object: 1. Set **AssetID** to a valid audio asset ID. If you don't have your own custom audio, you can find free-to-use audio assets in the Creator Store. 2. Enable **Looping** if you want your audio to continuously repeat. 3. Set **Volume** to the unit of amplitude you want to play your audio. 3. In the **Properties** window of the **Wire** object: 1. Set **SourceInstance** to the **AudioPlayer** to specify that you want to play the audio within this specific audio player. 2. Set **TargetInstance** to the **AudioDeviceOutput** to specify that you want to play the audio from this specific speaker. From here, you can trigger your non-directional audio with scripts to either play as players join the experience or as a result of a gameplay event or UI interaction. For code sample references for these use cases, see the [Add 2D audio](/docs/en-us/tutorials/use-case-tutorials/audio/add-2D-audio.md) tutorial. ### 3D audio **3D audio** is directional sound that plays from a particular location in the 3D space, increasing or decreasing in volume depending on the player's position and orientation to the sound. This type of audio requires six audio objects: - An audio player to produce an audio stream. - An audio emitter to emit the audio stream within the environment. - A listener to pick up the audio stream from the environment. - A physical hardware device to play the audio stream in the real world. - Two wires: one to carry the audio stream from the audio player to the emitter, and another to carry it from the listener to the output device. To demonstrate how to configure these audio objects in Studio for 3D audio, the following diagram compares each object with their real world audio device counterpart. In summary: - The `Class.AudioPlayer` loads and plays your audio asset with specified settings. - The `Class.AudioEmitter`'s parent position in the 3D space determines where that audio emits within the environment. - The `Class.AudioListener` either picks up audio from the emitter from the local camera or within the player character's `Class.Humanoid.RootPart`, depending on where you set the default listener position. - The `Class.AudioDeviceOutput` allows the player to hear the audio through their speaker or headphones. - The first `Class.Wire` connects to the audio player with its `Class.Wire.SourceInstance|SourceInstance` property, and to the emitter with its `Class.Wire.TargetInstance|TargetInstance` property. It then acts as a bridge to carry the audio stream from the audio player to the emitter. - The second `Class.Wire` connects to the listener with its `Class.Wire.SourceInstance|SourceInstance` property, and to the physical hardware device with its `Class.Wire.TargetInstance|TargetInstance` property. It then acts as a bridge to carry the audio stream from the listener to the player's output device. To play positional audio: 1. Choose where you want to create an **AudioListener** when players spawn into the experience. 1. In the **Explorer** window, select **SoundService**. 2. In the **Properties** window, set **ListenerLocation** to one of the following: - **Default** - Creates and parents the listener to `Class.Workspace.CurrentCamera` in experiences that enable voice chat. - **None** - Does not create a listener. This option is useful if you want to create a listener through a script. - **Character** - Creates and parents a listener to the local player's character. - **Camera** - Creates and parents the listener to `Class.Workspace.CurrentCamera`. > **Info:** When you set **ListenerLocation** to either **Character** or **Camera**, the engine automatically creates an `Class.AudioDeviceOutput` object under `Class.SoundService` at runtime. 2. In the **Explorer** window, go to the 3D object that you want to emit audio and insert the following: 1. An **AudioPlayer** object to create an audio source. 2. An **AudioEmitter** object to emit a positional stream from the 3D object. 3. A **Wire** object to connect the stream from the audio player to the audio emitter. 3. In the **Properties** window of the **AudioPlayer** object: 1. Set **AssetID** to a valid audio asset ID. If you don't have your own custom audio, you can find free-to-use audio assets in the Creator Store. 2. Enable **Looping** if you want your audio to continuously repeat. 3. Set **Volume** to the unit of amplitude you want to play your audio. 4. In the **Properties** window of the **AudioEmitter** object, set **DistanceAttenuation** to a volume-over-distance curve that determines how loudly the listener hears the emitter according to the distance between them. For example, the following curve decreases the audio's volume in half when the listener is 50 studs away from the emitter, then it sharply decreases the volume to zero when the listener is 70 studs away. 5. In the **Properties** window of the **Wire** object: 1. Set **SourceInstance** to the **AudioPlayer** to specify that you want to play the audio within this specific audio player. 2. Set **TargetInstance** to the **AudioEmitter** to specify that you want to play the audio from this specific audio emitter. From here, you can trigger your directional audio with scripts to either play as players join the experience or as a result of a gameplay event or UI interaction. For code sample references for these use cases, see the [Add 3D audio](/docs/en-us/tutorials/use-case-tutorials/audio/add-3D-audio.md) tutorial. ### Text-to-speech > **Warning:** All text for `Class.AudioTextToSpeech` objects must adhere to Roblox's [Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards) and [Terms of Use](https://en.help.roblox.com/hc/articles/115004647846). **Text-to-speech** (**TTS**) is a form of assistive technology that converts text strings into speech sounds using an artificial voice. This type of audio requires five audio objects: - An audio speech generator to load and convert text into audio. - An audio emitter to emit the audio stream within the environment. - A listener to pick up the audio stream from the environment. - A physical hardware device to play the audio stream in the real world. - For 2D audio - A wire to carry the audio stream from the audio speech generator to the output device. - For 3D audio - Two wires: one to carry the audio stream from the audio speech generator to the emitter, and another to carry it from the listener to the output device. How you configure these objects depends on if you want to create 2D TTS audio or 3D TTS audio. For more details on either process, click between the following tabs. #### 2D Text-to-Speech To demonstrate how to configure these audio objects in Studio for 2D TTS audio, the following diagram compares each object with their real world audio device counterpart. In summary: - The `Class.AudioTextToSpeech` object loads and converts text strings into speech sounds. - The `Class.AudioDeviceOutput` allows the player to hear the audio through their speaker or headphones. - The `Class.Wire` connects to the audio speech generator with its `Class.Wire.SourceInstance|SourceInstance` property, and to the physical hardware device with its `Class.Wire.TargetInstance|TargetInstance` property. It then acts as a bridge to carry the audio stream from the audio speech generator to the player's output device. To play 2D text-to-speech audio: 1. In the **Explorer** window, go to **SoundService** and insert the following: 1. An **AudioTextToSpeech** object to create an audio speech generator. 2. An **AudioDeviceOutput** object to create a speaker that plays throughout the experience. 3. A **Wire** object to connect the stream from the audio speech generator to the speaker. 2. In the **Properties** window of the **AudioTextToSpeech** object: 1. Set **Text** to anything you want the voice to say. There is a limit of 300 characters per request. 2. Set **VoiceId** to a number between `1` and `10`, depending on which artificial voice you want to use in the following table.** List of artificial voices**| VoiceID | Voice Description | Audio Example | | --- | --- | --- | | 1 | British male | | | 2 | British female | | | 3 | United States male #1 | | | 4 | United States female #1 | | | 5 | United States male #2 | | | 6 | United States female #2 | | | 7 | Australian male | | | 8 | Australian female | | | 9 | Retro voice #1 | | | 10 | Retro voice #2 | | | 11 | Host voice | | | 101 | Spanish male | | | 102 | Spanish female | | | 201 | German male | | | 202 | German female | | | 301 | Italian male | | | 302 | Italian female | | | 401 | French male | | | 402 | French female | | 3. Set **Volume** to the unit of amplitude you want to play your audio. 3. In the **Properties** window of the **Wire** object: 1. Set **SourceInstance** to the **AudioTextToSpeech** to specify that you want to play the audio within this specific audio speech generator. 2. Set **TargetInstance** to the **AudioDeviceOutput** to specify that you want to play the audio from this specific speaker. #### 3D Text-to-Speech To demonstrate how to configure these audio objects in Studio for 3D TTS audio, the following diagram compares each object with their real world audio device counterpart. In summary: - The `Class.AudioTextToSpeech` object loads and converts text strings into speech sounds. - The `Class.AudioEmitter`'s parent position in the 3D space determines where that audio emits within the environment. - The `Class.AudioListener` either picks up audio from the emitter from the local camera or within the player character's `Class.Humanoid.RootPart`, depending on where you set the default listener position. - The `Class.AudioDeviceOutput` allows the player to hear the audio through their speaker or headphones. - The first `Class.Wire` connects to the audio speech generator with its `Class.Wire.SourceInstance|SourceInstance` property, and to the emitter with its `Class.Wire.TargetInstance|TargetInstance` property. It then acts as a bridge to carry the audio stream from the audio speech generator to the emitter. - The second `Class.Wire` connects to the listener with its `Class.Wire.SourceInstance|SourceInstance` property, and to the physical hardware device with its `Class.Wire.TargetInstance|TargetInstance` property. It then acts as a bridge to carry the audio stream from the listener to the player's output device. To play 3D text-to-speech audio: 1. Choose where you want to create an **AudioListener** when players spawn into the experience. 1. In the **Explorer** window, select **SoundService**. 2. In the **Properties** window, set **ListenerLocation** to one of the following: - **Default** - Creates and parents the listener to `Class.Workspace.CurrentCamera` in experiences that enable voice chat. - **None** - Does not create a listener. This option is useful if you want to create a listener through a script. - **Character** - Creates and parents a listener to the local player's character. - **Camera** - Creates and parents the listener to `Class.Workspace.CurrentCamera`. > **Info:** When you set **ListenerLocation** to either **Character** or **Camera**, the engine automatically creates an `Class.AudioDeviceOutput` object under `Class.SoundService` at runtime. 2. In the **Explorer** window, go to the 3D object that you want to emit audio and insert the following: 1. Aan **AudioTextToSpeech** object to create an audio speech generator. 2. An **AudioEmitter** object to emit a positional stream from the 3D object. 3. A **Wire** object to connect the stream from the audio speech generator to the audio emitter. 3. In the **Properties** window of the **AudioTextToSpeech** object: 1. Set **Text** to anything you want the voice to say. There is a limit of 300 characters per request. 2. Set **VoiceId** to a number between `1` and `10`, depending on which artificial voice you want to use in the following table.** List of artificial voices**| VoiceID | Voice Description | Audio Example | | --- | --- | --- | | 1 | British male | | | 2 | British female | | | 3 | United States male #1 | | | 4 | United States female #1 | | | 5 | United States male #2 | | | 6 | United States female #2 | | | 7 | Australian male | | | 8 | Australian female | | | 9 | Retro voice #1 | | | 10 | Retro voice #2 | | 3. Set **Volume** to the unit of amplitude you want to play your audio. 4. In the **Properties** window of the of the **AudioEmitter**, set **DistanceAttenuation** to a volume-over-distance curve that determines how loudly the listener hears the emitter according to the distance between them. For example, the following curve decreases the audio's volume in half when the listener is 50 studs away from the emitter, then it sharply decreases the volume to zero when the listener is 70 studs away. 5. In the **Properties** window of the **Wire** object: 1. Set **SourceInstance** to the **AudioTextToSpeech** to specify that you want to play the audio within this specific audio speech generator. 2. Set **TargetInstance** to the **AudioEmitter** to specify that you want to play the audio from this specific audio emitter. From here, you can trigger your TTS audio with scripts. For code sample references for TTS audio, including how to configure context-aware TTS that adapts in relation to the player, the state of their environment, or gameplay status, see the [Add text-to-speech](/docs/en-us/tutorials/use-case-tutorials/audio/add-text-to-speech.md) tutorial. ### Speech-to-text > **Warning:** All audio for `Class.AudioSpeechToText` objects must adhere to Roblox's [Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards) and [Terms of Use](https://en.help.roblox.com/hc/articles/115004647846). **Speech-to-text** (**STT**) is a form of technology that automatically generates text strings from speech sounds. This type of audio requires three audio objects: - A text generator to load and convert audio into text. - A physical hardware device like a microphone to capture the audio input. - A wire to carry the audio stream from the input device to the text generator. All of these audio objects work together to generate STT text in response to player actions. For example, if the player is wearing a headset while playing an experience with their laptop: - The `Class.AudioDeviceInput` captures the player's speech, as spoken into their microphone and as a stream of audio. - A `Class.Wire` carries the audio stream from the `Class.AudioDeviceInput` to the `Class.AudioSpeechToText`. - The `Class.AudioSpeechToText` converts the player's speech into text. - The experience reads this text and performs an action, such as displaying the spoken text on the screen or opening a door at the player's command. To implement speech-to-text in your experience: 1. Enable the use of the latest API for voice. 1. In the **Explorer** window, select the **VoiceChatService**. 2. In the **Properties** window, set **UseAudioApi** to **Enabled**. 2. In the **Explorer** window, go to **SoundService** and insert the following: 1. An **AudioDeviceInput** to capture speech. 2. An **AudioSpeechToText** to convert the speech into text. 3. A **Wire** to carry the stream from the audio device input to the STT instance. 3. In the **Properties** window of the **AudioSpeechToText** object, set the **Enabled** state to on. 4. In the **Properties** window of the **Wire** object: 1. Set **SourceInstance** to your new **AudioDeviceInput** to specify that you want the wire to carry audio from this specific audio instance. 2. Set **TargetInstance** to your new **AudioSpeechToText** to specify that you want the wire to carry audio to this specific audio instance. 5. Set the **Player** property of the audio device input to the local player at runtime with `audioDeviceInput.Player = game.Players.LocalPlayer`. This tells Roblox which user's microphone to capture audio from. > **Info:** By default, enabling the microphone also enables spatial voice chat in your experience. If you want players to use speech-to-text without broadcasting their voice to other players, turn off the **EnableDefaultVoice** property under **VoiceChatService**. After setting up STT in your experience, you can trigger it with scripts. For code sample references, see the [Add speech-to-text](/docs/en-us/tutorials/use-case-tutorials/audio/speech-to-text.md) tutorial. ##### Supported languages No configuration is required to enable supported languages. Roblox automatically detects the spoken language from the audio and transcribes it. STT supports the following languages: - Arabic - Chinese (Simplified) - Chinese (Traditional) - English - French - German - Indonesian - Italian - Japanese - Korean - Polish - Portuguese - Spanish - Russian - Turkish - Thai - Vietnamese ##### Filter for similar words When you implement STT in your experience, you might want to improve matching accuracy by filtering for words that sound similar to the words you actually want the player to say. To do this, you can compare the words recognized by `AudioSpeechToText` with known word lists: 1. Sanitize and tokenize the `Text` output of `AudioSpeechToText` to create a table of lowercase strings that can be parsed and compared. 1. Remove punctuation characters. 2. Convert the entire string to lowercase. 3. Split the string by whitespace to produce a table of words. 2. Generate candidate tables to prepare your strings for comparison. 1. Sanitize and tokenize each reference string. 2. Store these processed words in a separate table. 3. Compare and match the words in both tables to recognize the speech inputs that are close to your target phrases, even if they include small variations. - For simple checks, check if the strings are the exact same. - For more flexible matching, you can write custom logic to accept substitutions (like "colour" instead of "color") or match a subset of words and calculate a similarity score. ## Customize audio Audio effects allow you to non-destructively modify or enhance audio streams before they reach a player's ears. You can apply these effects to make your audio more immersive within experiences, such as using an `Class.AudioEqualizer` object to make rain sound muffled, `Class.AudioCompressor` object to control a sound's maximum volume, or `Class.AudioReverb` to add more realistic reflections of sound in interior spaces. For instructions on how to configure audio effects, as well as side-by-side comparisons of before and after you customize your audio, see [Audio effects](/docs/en-us/audio/effects.md). ## Trigger audio You can trigger audio contextually from a script by calling `Class.AudioPlayer.Play|Play()` on an `Class.AudioPlayer` object that's correctly wired up. For example, if you parent a script to an audio player, you can trigger the audio asset through something like this: ```lua local audio = script.Parent local something = ... something.SomeEvent:Connect(function() audio:Play() end) ``` For more complex code samples to trigger audio, such as for gameplay feedback, UI interactions, and looping background noise, see the [Add 2D audio](/docs/en-us/tutorials/use-case-tutorials/audio/add-2D-audio.md), [Add 3D audio](/docs/en-us/tutorials/use-case-tutorials/audio/add-3D-audio.md), and [Add text-to-speech](/docs/en-us/tutorials/use-case-tutorials/audio/add-text-to-speech.md) tutorials. --- title: "Accessory Fitting Tool" url: /docs/en-us/avatar/accessory-fitting-tool last_updated: 2026-06-29T19:33:56Z description: "The Accessory Fitting Tool lets you adjust and test custom accessory models on different body types, animations, or custom assets." --- # Accessory Fitting Tool The **Accessory Fitting Tool** (AFT) is a built-in Studio tool that allows you to test your custom models on multiple combinations of character bodies, animations, and accessories before generating the final `Class.Accessory` object. When testing your accessories, you can make minor fit and positional changes to ensure that you get the best result possible. The AFT automatically handles the conversion of the custom `Class.Model` or `Class.MeshPart` based on the user menu selections, allowing you to create **layerable clothing accessories** or **rigid accessories**. After generating your accessory, the AFT creates the correct `Class.Accessory` object hierarchy with any updated fit edits, sets the appropriate `AccessoryType` property, and generates any required body attachment points. _Test and edit the cages of your layered clothing assets._ _Test and edit the orientation and placement of your rigid assets._ > **Warning:** If you are intending to sell your accessory on the Marketplace, make sure your accessory model design adheres to the [Marketplace Requirements](/docs/en-us/marketplace/marketplace-policy.md). ## Set up accessories The first stage of the fitting workflow allows you to configure the type of accessory to correctly populate the correct fitting tools and generate the appropriate accessory object. When selecting the type of accessory, the following options are available: - **Clothing**: Layerable accessories that use an inner and outer cage to stretch and wrap around a body and other clothing items. - **Accessory**: Rigid accessories that attach to a specific attachment point of a character and remain static in that position and orientation. Before using the tool, ensure that you have the `Class.MeshPart` or `Class.Model` you intend to create into an accessory selectable in your project. As a reference, you can test the AFT using a reference [clothing](../../assets/accessories/reference-files/Additional-FBX-assets.zip) or [rigid accessory](../../assets/accessories/reference-files/Bow-rigid.rbxm) custom model. To setup your accessories: 1. Ensure that your custom asset is selectable in your project. See [Importer](/docs/en-us/studio/importer.md) for instructions on importing a custom model into your experience. 2. In the toolbar's **Avatar** tab, click **Accessory** to open the AFT. 3. Select the **Part** field and click on the `Class.MeshPart` or `Class.Model` in the viewport that you intend to preview. The text field populates with the name of the object selected. 4. Click **Next**. The Asset Type menu screen displays. 5. Select the correct Asset Type for your accessory. An additional dropdown displays for a specific `AssetType` selection. 6. Use the dropdown to select the specific type of accessory to preview. This sets the correct `AssetType` and attachment points when creating the accessory. 7. Select the expected scaling of an accessory. This only affects rigid accessories if the specific body part has a different `AvatarPartScaleType` `Class.StringValue` object. This does not affect clothing accessories. 1. **Classic**: Sets the scaling of the accessory to classic R15 proportions. 2. **Proportions Slender**: Sets the `AvatarPartScaleType` value to `ProportionsSlender`. 3. **Proportions Normal**: Sets the `AvatarPartScaleType` value to `ProportionsNormal`. 8. Click **Next** to continue. A preview panel and workspace tools display. ## Test accessories After you provide the initial accessory details, the tool displays a preview panel. With the preview panel, you can test how your accessory looks on different combinations of character bodies, clothing items, animations, or even custom assets in your experience. > **Info:** At any point of the testing process, you can [initiate a playtest](/docs/en-us/studio/testing-modes.md#playtesting) to launch an instance of your experience where your avatar is replaced with the currently selected character body and accessories from the AFT. If you notice any fitting issues with your accessory, you can use the [edit](#edit-accessory-fit) tools to make minor adjustments to your accessory. ### With different bodies You can select different bodies to test the fit and wear of your accessories. The AFT supplies several default character models you can use to ensure your accessories fit as expected. _Bazooka Bones character preview_ _Goblin character preview_ To test your accessory with a different body: 1. In the tool's catalog, navigate to **Avatars** > **Default**. 2. Click one of the character model tiles. The preview loads with the selected character model. 1. If two character tiles are selected, click a selected tile to deselect it. 2. In the character preview, **click** and **drag** to rotate and **right-click** to pan to inspect your character. ### With different clothing You can select different clothing accessories to test the fit and layering of your caged accessories. The AFT supplies several default character models you can use to ensure your accessories fit as expected. _Goblin character preview with reference clothing_ _Magma Fiend character preview with reference clothing_ To test your accessory with a different accessory: 1. In the tool's catalog, navigate to **Clothing** > **Default**. 2. Click one or more of the available catalog items. The character preview loads with the selected clothing accessory. 1. In the catalog, click an active tile to deselect the asset. 2. In the character preview, **drag** and **drop** the accessory boxes to change the layer order. 3. In the character preview, **click** and **drag** to rotate and **right-click** to pan to inspect your character. ### With animations You can select different animations to test the movement of your accessory asset. The AFT supplies several default animation assets you can use to ensure your accessories fit as expected when a model is performing various movements. _Walking animation reference_ _Shy emote reference_ To test your accessory with different animations: 1. In the tool's catalog, navigate to **Animations** > **Default**. 2. Click one of the animation asset tiles. 1. In the catalog, press the play and pause icon to control the playback. 2. In the character preview, **click** and **drag** to rotate and **right-click** to pan to inspect your character. ### With custom assets You can add custom character models, clothing accessories, and animations that are part of your workspace to the AFT preview catalog. Use this functionality to verify that your accessory works with any other custom models or accessories they may interact with in your experience. To add custom assets: 1. Click the ⊕ icon next to the catalog search. A prompt appears, allowing you to choose a supported object. 2. Select any `Class.Accessory`, `Class.Model`, `Class.MeshPart`, `Class.Animation` or `Class.Folder` within the 3D viewport or **Explorer**. The asset displays in the corresponding **Custom** category. 1. If no selection is made, click the tool panel again to exit the prompt. ## Edit accessory fit The AFT populates different fitting tools depending on the type of accessory being created. ### Layered clothing When editing clothing items, the following tools populate in the viewport: | Icon | Description | | --- | --- | | | Toggles the [Cage Editing](#cage-editing) interface in the viewport for making minor inner or outer cage changes to your clothing. | | | Toggles [auto-skinning](/docs/en-us/avatar/automatic-skinning-transfer.md#enable-automatic-skinning-transfer) between `EnabledPreserved` and `EnabledOverride`. Depending on the asset and skinning quality, auto-skinning may provide better results.

EnabledPreserved uses the asset's original skinning data applied in a modeling software.
`EnabledOverride` transfers skinning data from the avatar character instead of using the asset's original skinning data.
| | | Displays a button to **Bring Mannequin in View** which centers mannequin in front of the camera. | | | Centers the camera on the mannequin. | #### Cage editing When the Cage Editing interface is enabled, additional tools display in the viewport. The viewport also displays the vertices of the selected cage over the mannequin, allowing you to make positional edits to the cage and change how a clothing item can fit on a body. > **Warning:** You can use these Cage Editing tools for minor to moderate cage edits. If your asset requires major fit or sculpting changes, edit the cage meshes directly in a third-party modeling software, such as Blender or Maya, and import the updated model into Studio. Use the following cage editing tools to help visualize and edit any cage vertices: | Icon | Description | | --- | --- | | | Toggles the selection for the inner and outer cage vertices. When selected, the vertices of that specific cage are available to edit. | | | **Falloff Distance** sets the radius of influence when editing vertices of the cage mesh. When editing a cage vertex, nearby vertices follow the changes for efficient cage editing.

A higher Falloff Distance applies influence to vertices further away from the origin. | | | Displays additional buttons: | | | Use the slider to set the opacity of the mesh or the cage vertices. Setting the opacity allows you to better see and access certain vertices and angles of your clothing item. | To make changes to the vertices of the currently selected cage: 1. In the Studio toolbar, disable **Move** snapping. This enables you to make detailed changes to a vertex's position. 2. Select a vertex and use the **Move** tool to reposition. Changes to the cage apply immediately and display in the preview panel. 1. Use the opacity sliders to better visualize the changes to your cage. 2. Set the **Falloff Distance** depending on the number of vertices being adjusted at once. ### Rigid accessories When fitting rigid accessories, a bounding box appears around the mannequin indicating the possible placement of that specific type of accessory. You can **position**, **rotate**, and **scale** objects within this bounding box to ensure your accessory fits on different character models. _Adjust your rigid accessory fit within the bounding box._ _If the accessory is outside the appropriate space, the bounding box turns red._ ### Create accessory You can create the accessory at any time. The tool applies any fit changes and generates the appropriate Accessory instance in the workspace depending on the type of accessory selected and any configurations applied. When you are ready to generate your accessory, select **Generate MeshPart Accessory**. _Hierarchy generated for layered clothing._ _Hierarchy generated for rigid accessories._ With an accessory successfully created, you can now try the following: - Equip the accessory on an avatar-ready character by drag and dropping the accessory on an existing model, or using [HumanoidDescription](/docs/en-us/characters/appearance.md#manually-modify-appearance). - Save the accessory as an [avatar asset](/docs/en-us/projects/assets.md#for-avatars) for use in an experience later. - If you meet certain account requirements, you can [upload your asset](/docs/en-us/marketplace/publish-to-marketplace.md) for moderation and start selling it on the Marketplace.
--- title: "Automatic Skinning Transfer" url: /docs/en-us/avatar/automatic-skinning-transfer last_updated: 2026-06-29T19:33:56Z description: "Automatic Skinning Transfer can create deformation data for character accessories without manual skinning." --- # Automatic Skinning Transfer **Automatic Skinning Transfer** allows layered clothing and facial accessories to deform accurately along with the character model it's attached to without having to skin the accessory itself. Instead of the complex task of manually [rigging and skinning](/docs/en-us/art/modeling/rigging.md) models in 3D modeling software, you can use this feature to transfer or generate skinning data to the accessory. When using Automatic Skinning Transfer, the Roblox Engine creates and applies skinning at runtime. **No skinning of accessory geometry is required to use Automatic Skinning Transfer**. In fact, auto-skinned accessories work well with most characters they're attached to, even if those characters have a different number of joints, bones, or use a different kind of skinning, and the accessories move accurately with characters as they animate. Skinning is still an important concept for character creation, and if you're creating custom characters, you may want to apply skinning data to the model to create a character with more natural looking poses and animations. For information on how to skin a mesh, see [Skin a humanoid model](/docs/en-us/art/modeling/skin-a-humanoid-model.md). > **Info:** Because the skinning transfer process uses a character's outer cage to help calculate skinning data to apply onto the accessory, it's important that all [layered clothing requirements](/docs/en-us/avatar/layered-accessories/specifications.md) are still met when creating accessories. ## Enable Automatic Skinning Transfer To enable the Automatic skinning transfer process, you must enable the `Class.WrapLayer.AutoSkin|AutoSkin` property within the `Class.WrapLayer` instance of the layered `Class.Accessory` you want to automatically skin, then set it to one of the following values: - `Disabled`: Disables the Automatic Skinning Transfer process. This is the default value. - `EnabledOverride`: Enables the Automatic Skinning Transfer process, and allows it to override any existing skinning information found on the accessory at runtime. - `EnabledPreserve`: Enables the Automatic Skinning Transfer process, but doesn't allow it to override any existing skinning information found on the accessory at runtime. If there isn't any skinning to maintain, the Automatic Skinning Transfer process automatically creates new skinning data. When there isn't any skinning data on the accessory, or if you choose to override any existing skinning data associated with the accessory, the Roblox Engine calculates skinning data from the character's geometry and cages, then the new skinning data and rig associated with the accessory drives the accessory's deformations and motions in sync with the source geometry itself. ## Best practices While Automatic Skinning Transfer often works better than manual skinning, there are some best practices to reduce unexpected behavior with the accessory skinning. Like all modeling processes, constantly test your layered clothing and facial accessories on different avatar types to achieve the results you want. ### Special skinning transfer joints Automatic skinning transfer may not work well for certain detailed accessory types, like eyelashes or eyebrows. To get a more controlled skinning transfer result, you have the option to skin accessory geometry to one of two specially named joints: `RBX_Leader` and `RBX_Follower`. > **Info:** It's best to create these joints/bones directly beneath the Root joint in your hierarchy for simplicity and clarity. These joints/bones won't be detected as a part of your character model's R15 rig on import. Any vertices skinned to `RBX_Leader` will undergo the same transfer process that exists today. However, any vertices skinned to `RBX_Follower` will actually transfer based on their nearest leader vertex. This allows for better results in situations like an eyelash where the tip of an eyelash strand would normally transfer to somewhere on the brow area, rather than follow the base of the eyelash strand when moving. For eyelashes, good candidates for RBX_Leader are the ones that are intended to sit right on the eyelid of the character. The remaining vertices can be skinned to RBX_Follower. ![Screenshot of vertices assigned as leader.](../../assets/avatar/dynamic-heads/creating-face-accessories/Vertices-Group-Leader.png)_Vertices of an eyelash assigned to a RBX_Leader bone vertex group._ ![Screenshot of vertices assigned as follower.](../../assets/avatar/dynamic-heads/creating-face-accessories/Vertices-Group-Follower.png)_Vertices of an eyelash assigned to a RBX_Follower bone vertex group._ For eyebrows, good candidates for RBX_Leader are vertices along the edges that span the browline. ![Screenshot of vertices assigned as leader.](../../assets/avatar/dynamic-heads/creating-face-accessories/Eyebrows-Follower-Vertices.png)_Vertices of an eyebrow assigned to a RBX_Leader bone vertex group._ ![Screenshot of vertices assigned as follower.](../../assets/avatar/dynamic-heads/creating-face-accessories/Eyebrows-Leader-Vertices.png)_Vertices of an eyebrow assigned to a RBX_Follower bone vertex group._ In Blender, user the **Object Data Properties** > **Vertex Groups** to manage and view your vertex group assignments. ![Screenshot of properties panel with vertex group assignments](../../assets/avatar/dynamic-heads/creating-face-accessories/Vertices-Group-Assign.png) > **Info:** In Blender, vertex groups automatically created after you make a bone object. This allows you to quickly set vertices to a specific bone vertex group. _Eyelashes without transfer joints. Notice how the upper eyelashes near the bridge of the nose doesn't follow the eyelids as expected._ _Eyelashes with transfer joints. All eyelashes follow the leading vertices closest to the eyes._ No additional work is needed in Studio to support this method. If your accessory is set to `Class.WrapLayer.AutoSkin.EnabledOverride` and these joints exist with vertices assigned to them, then this skinning transfer variation will be in effect. If you wish to upload an accessory using these joints as a UGC item, there are a few rules to be aware of: - Vertices cannot be partially weighted to these joints. If you want to use them you must skin the vertex to the joint with a weight of `1.0`. - If these joints are present in the mesh, then the accessory must be set to `WrapLayerAutoSkin.EnabledOverride`. - Body part meshes containing these joints will be rejected by validation. - Eyebrow and Eyelash accessory types are required to have the special skinning joints and be set to `WrapLayerAutoSkin.EnabledOverride`. ### Modify character cages > **Error:** You can't upload assets with a partial cage to the Marketplace. You may use partial cages for assets intended for in-experience use, but the Marketplace validation process will reject assets with partial cages. You can modify character cages for the accessories to deform accurately to the expected character surfaces using the Automatic Skinning Transfer. For example, auto-skinning may cause layered clothing to deform based on an incorrect body part because the transfer process is based on the closest distance between the accessory and its inner cage. In the following instance, a beard accessory was modeled using a blocky-type full-body cage. This causes the beard to deform incorrectly because parts of the beard are closer to the character cage's upper chest instead of the chin: To prevent a layered accessory from using skinning data from an undesired area of the character's geometry, you can model your asset on a different character mannequin cage. For example, a blocky-type character will struggle with a beard skinning to the torso, but a character with an actual neck, like a humanoid, won't have this problem. Alternatively, you can remove parts of the outer cage that the layered accessory shouldn't be skinned to. **This is not a valid workflow for assets intended for the Marketplace**, but you can use this for in-experience assets or assets for other use. For example, the following image shows how the outer cage was modified so that it only includes the head geometry. With this improvement to the outer cage, when you automatically transfer skinning data, the beard and partial cage now correctly transfer skinning only from the head geometry. It's important to note that you could also solve the previous example's deformation issue by using a different cage altogether. For example, if you use a more humanoid cage with more space between the chest and the chin, the beard is closest to the head instead of being near the chest or neck area, so the Automatic Skinning Transfer wouldn't transfer skinning data from those regions. By modifying different regions of the character's cage, you can ensure that your layered clothing and facial accessories deform in relation to the correct region of the body or head, such as modifying the character model's arms so dresses don't incorrectly attach to them while a character is running, or modifying pants that incorrectly map to feet. The following video demonstrates how a jacket's collar incorrectly moves with the head, as the layered clothing is deforming in the collar region, which is closest to the head portion of the cage. To resolve this, you can remove the head portion of the cage so that the jacket won't incorrectly deform in relation to the head at all. Instead, it will deform in relation to the shoulder region, which is much more appropriate for this article of clothing. > **Info:** During the asset creation process, it's important to verify what skinning solution works best for your design by testing your individual assets on multiple models and animations. You can always skin your assets manually and choose to use the Automatic Skinning Transfer later. ### Different accessory categories Automatic skinning transfer may not work well for certain accessory categories, such as hat, and glasses-type accessories. For example, hat or glasses accessories might introduce deformation in areas that should typically be rigid. In general, those accessories should remain rigid, and you shouldn't associate any skinning data with them. For a summary of suggested `Class.WrapLayer.AutoSkin` parameters for different accessory categories, see the following table: | Accessory Category | Suggested Parameter | | --- | --- | | Beard | `EnabledOverride` | | Eyebrow | `EnabledOverride` | | Eyelash | `EnabledOverride` | | Hair | `Disabled` | | Hat | `Disabled` | | Glasses | `Disabled` | | Shirt | `EnabledOverride` or `EnabledPreserve` | | Pants | `EnabledOverride` or `EnabledPreserve` | | Shoes | `EnabledOverride` or `EnabledPreserve` | --- title: "Photo-to-Avatar generation" url: /docs/en-us/avatar/avatar-generation last_updated: 2026-06-29T19:33:56Z description: "Explains how to use AvatarCreationService AvatarGeneration APIs to prompt users to generate Avatars from photos in experience." --- # Photo-to-Avatar generation > **Success:** The Photo-to-Avatar feature is currently in alpha. Please refer to the [DevForum announcement](https://devforum.roblox.com/t/early-preview-alpha-release-of-photo-to-avatar-apis/3931624) for latest information. > **Warning:** The following guide applies to creators who are familiar with scripting and Roblox Studio and intend to develop experiences that allow user-generated avatar items. You can create an experience that allows players to generate a fully functional avatar character using a photo and a text prompt. This process involves the following steps: 1. Request an AvatarGeneration session 2. Prompt the user to take a selfie and generate a 2D preview image of the avatar 3. Generate the full Avatar character using `Class.HumanoidDescription` ### Request an AvatarGeneration session To start a photo-to-avatar generation, call `Class.AvatarCreationService:RequestAvatarGenerationSessionAsync()` from the server to request a session for the player. A session provides a `Class.Player` with a certain number of Avatar previews and Avatar generations. As it may take some time for a session to become available, `Class.AvatarCreationService:RequestAvatarGenerationSessionAsync()` returns a `Datatype.RBXScriptConnection` and a waitTime in seconds. The waitTime can be used to provide the `Class.Player` with information on how long it will take them to be able to start their generations. Once a session is ready, the callback is invoked with a `Dictionary` of information about the session. The `Dictionary` includes: - `SessionId` — passed as an argument when calling `Class.AvatarCreationService:GenerateAvatar2DPreviewAsync` and `Class.AvatarCreationService:GenerateAvatarAsync` - `Allowed2DGenerations` — the number of 2D preview generations allowed in a session - `Allowed3DGenerations` — the number of 3D avatar generations allowed in a session - `SessionTime` — the maximum time for a session in seconds ### Prompt selfie and generate 2D preview After a session is started, prompt the user to take a selfie and get the fileId `string` by calling the `Class.AvatarCreationService:PromptSelectAvatarGenerationImageAsync` method on the Server. This fileId will be passed to the `Class.AvatarCreationService:GenerateAvatar2DPreviewAsync` method. To create a 2D preview image of the Avatar call the following methods: 1. `Class.AvatarCreationService:GenerateAvatar2DPreviewAsync` on the Server 2. `Class.AvatarCreationService:LoadAvatar2DPreviewAsync` on the Client The `Class.AvatarCreationService:GenerateAvatar2DPreviewAsync` takes the SessionId, fileId and an optional text prompt as input to generate a 2D Avatar preview. This method returns a previewId. This previewId should be sent to the client and can then be used to retrieve the preview as an `Class.EditableImage`. If the user is not satisfied with the generated preview this workflow can be repeated. ### Generate the 3D avatar Once the user is satisfied with the preview, you can generate the complete 3D avatar character. To generate an avatar character: 1. Call `Class.AvatarCreationService:GenerateAvatarAsync` on the Server with a `Dictionary` containing the SessionId and PreviewId. 1. This method call returns a `string` generationId. 2. Retrieve the generated avatar data as a `Class.HumanoidDescription` using the `Class.AvatarCreationService:LoadGeneratedAvatarAsync` method on both the experience server and the client. 3. You can use the `Players.CreateHumanoidModelFromDescription` method to create an avatar model from the `Class.HumanoidDescription` to display to the `Class.Player`. Mesh and texture assets will be provided as `Class.EditableMesh` and `Class.EditableImage` objects, respectively, to allow continued editing of the generated avatar. Edits should be made on both the experience server and the client copy to keep them in sync for publish. ### Publish If the user is satisfied with the resulting avatar it can be published using the `Class.AvatarCreationService:PromptCreateAvatarAsync` method. For more details on see [in-experience creation](/docs/en-us/avatar/in-experience-creation.md). --- title: "Export character bodies" url: /docs/en-us/avatar/character-bodies/export last_updated: 2026-06-29T19:33:56Z description: "Use the appropriate export settings in Maya and Blender to generate Studio-ready .fbx files." --- # Export character bodies > **Warning:** It's important to test your assets multiple times at every point of the asset creation process, whether it is within your modeling application or after importing into Studio. See [Test characters](/docs/en-us/art/characters/testing.md) for more information. Export your model as a `.fbx` or `.gltf` to take advantage of all of Studio's 3D import features. When rigging or skinning a layerable model, these file types contain all the mesh and texture data, including the rig and influence data, you need to later [import](/docs/en-us/studio/importer.md) into Studio. Check that your model meets Roblox's [avatar character specifications](/docs/en-us/avatar/character-bodies/specifications.md) before exporting to ensure Studio compatibility. > **Warning:** **If creating other types of 3D models:**- For generic meshes, see [general mesh specifications](/docs/en-us/art/modeling/specifications.md) and [general export settings](/docs/en-us/art/modeling/export-requirements.md). - For rigid accessories, see [accessory specifications](/docs/en-us/avatar/rigid-accessories/specifications.md) and [accessory export settings](/docs/en-us/avatar/rigid-accessories/export.md). - For layered accessories, see [layered accessory specifications](/docs/en-us/avatar/layered-accessories/specifications.md) and [layered export settings](/docs/en-us/avatar/layered-accessories/export.md). > **Error:** If you are using Roblox's avatar template files, you must perform the [cleanup steps](/docs/en-us/art/characters/creating/combine-head-geometry.md) in order for the assets to properly validate. ## Before exporting Before exporting, ensure that you are only exporting the Roblox supported objects related to your model. If you have any modifiers to your mesh or project objects, make sure to apply or delete them before export. Use the following guidance before exporting: - While your mesh objects must be parented within an armature object, you can also parent your outer cage objects to a single empty object to simplify your workspace.![Screenshot of a Blender project Outliner with parent objects collapsed.](../../assets/art/avatar/Character-Data-Model-Collapsed.png) - In the cage parent object, ensure you have an [outer cage](/docs/en-us/avatar/character-bodies/specifications.md#outer-cages) for each of your 15 body parts with appropriate naming affix.![Screenshot of the 15 outer cage meshes that you must include with a character export.](../../assets/art/avatar/Character-Data-Model-Cages.png) - In the armature object, ensure that you include the all [15 mesh objects](/docs/en-us/avatar/character-bodies/specifications.md#body-parts) and [19 attachments](/docs/en-us/avatar/character-bodies/specifications.md#attachments) with the appropriate naming affix.![Screenshot of 15 child objects parented within an Armature object.](../../assets/art/avatar/Character-Data-Model-Joints.png) > **Info:** For more information on supported modeling objects and proper configuration, see [Character specifications](/docs/en-us/avatar/character-bodies/specifications.md). - When exporting characters with animation or FACS animation, ensure that your animation timeline **Start** and **End** include the entire range of your animations. ![Zoom-in of Blender animation track indicating a Start value of 0 and End value of 308.](../../assets/art/avatar/basic-creation/Blender-Animation-Start-End.png) ## Blender Blender allows you to export in `.fbx` or `.gltf` as well as other formats. If you are using `.fbx` export, familiarize yourself with [Blender's FBX scaling](/docs/en-us/art/blender.md#adjust-scale-fbx) to ensure that you successfully import the model into Studio at the correct scale. ### Export settings 1. In the topbar, click **File** > **Export** > **FBX (.fbx)**. The Blender file browser window displays. 2. Set **Path Mode** to **Copy** and enable the **Embed Textures** icon. 3. In the Include section, enable **Custom Properties**. 4. Set the **Transform** > **Apply Scalings** to **FBX Unit Scale**. If you run into scaling issues on import, see [Blender FBX scaling](/docs/en-us/art/blender.md#adjust-scale-fbx) for alternative approaches. 5. Expand the Armature section and uncheck **Add Leaf Bones**. 6. Enable **Bake Animation**. 7. Expand Bake Animation and **uncheck NLA Strips**, **All Actions**, and **Force Start/End Keyframes**. 1. Ensure your project animation timeline has the correct **Start** and **End** range of all your keyframes. 8. In Bake Animation, set **Simplify** to **0.0**. 9. Click the **Export FBX** button. Save the `.fbx` to the directory of your choice. ![Screenshot of Blender export settings](../../assets/art/avatar/basic-creation/Export-Settings.png) ## Maya export settings To export a mesh in Maya as a `.fbx` file: 1. In the topbar, click **File**. A pop-up menu displays. 2. Select **Export All**. The **Export All** window displays. 3. Near the bottom of the window, click the **Files of type** dropdown, then select **FBX export**. 4. On the right-hand side of the window, navigate to the **Options...** section. 5. In the **Geometry** section, enable **Smooth Mesh** and **Referenced Asset Content**. 6. In the **Animation** section, enable **Animation**. Avatar characters with [animatable heads](/docs/en-us/avatar/dynamic-heads.md) require animation data. 7. Enable **Bake Animation**. 8. If you need to import textures as a `.png`, in the **Embed Media** section, enable **Embed Media**. 9. In the **Advanced Options** section, - Navigate to **Units**, then enable **Automatic**. - Navigate to **Axis Conversion**, then set the **Up Axis** property to **Y**. 10. Click the **Export All** button.![Screenshot of Maya export settings for exports with animation.](../../assets/accessories/lc-requirements-maya-settings-with-animation.png) 11. After exporting, use Studio's [Importer](/docs/en-us/studio/importer.md) to import your model. See [Test characters in Studio](/docs/en-us/art/characters/testing/studio.md) for additional importing and testing information. > **Success:** After importing the `Class.Model` character to Studio, you can now perform the following with this asset: - [Test your character model](/docs/en-us/art/characters/testing.md) in Studio using various tools and workflows. - [Upload the character](/docs/en-us/art/accessories/creating-rigid/publishing.md) to the Marketplace. - Use the humanoid character on an existing experience by applying a [HumanoidDescription](/docs/en-us/characters/appearance.md#manually-modify-appearance)to the `Class.Model` object. - Save the asset to your [Toolbox](/docs/en-us/projects/assets/toolbox.md) to share or use within any of your experiences. --- title: "Import character bodies" url: /docs/en-us/avatar/character-bodies/import last_updated: 2026-06-29T19:33:56Z description: "Use the Importer to add third-party models to Studio before testing, using, or uploading the character model." --- # Import character bodies Studio's Importer lets you import 3D assets into your projects, such as avatar clothing, accessories, and character bodies. The following instructions provide guidance on how to import a full character model into Studio as a `Model` object that you can save to use in your experience, share with others, or upload to the Marketplace. > **Info:** If you are using a single-mesh character body for the Roblox Auto-Setup tool, see [Avatar auto-setup](/docs/en-us/avatar-setup.md). While the 3D Importer provides object previews and error-checking to ensure that your asset meets Roblox's [general 3D requirements](/docs/en-us/art/modeling/specifications.md), it's important to ensure that your character model meets Roblox's [avatar character specifications](/docs/en-us/avatar/character-bodies/specifications.md) to use or sell this asset as an avatar-ready character model, otherwise you can encounter errors at different points in the workflow. To import your asset as a character model: 1. In the **Home** tab, click the **Import** button to open the 3D Importer. A file browser opens. 2. Select your rigged character's `.fbx` or `.gltf` file. The 3D Importer loads a preview of the character model. - If textures don't load for your asset, you can manually import your textures later. - For additional information on import settings and troubleshooting, see [3D importer](/docs/en-us/studio/importer.md). 3. In the **Rig General** section, 1. Set **Rig Scale** to the appropriate [Body Scale](/docs/en-us/avatar/character-bodies/specifications.md#body-scale) of your character. 2. Set **Constraint Type** to **Joint Upgrade**. 4. **OPTIONAL** If you are importing a [higher-fidelity rig](/docs/en-us/avatar/character-bodies/specifications.md#higher-fidelity-rigs), set **Rig Type** to **Custom Humanoid**. 5. Select **Import**. The asset populates in your workspace as a `Class.Model` with the appropriate textures applied as a `Class.SurfaceAppearance` or `Class.MeshPart.TextureID`. **Manually add textures** If textures didn't load correctly, add them manually. You may need to save and publish your experience in order to access the [Asset Manager](/docs/en-us/projects/assets/manager.md). 1. In the **Asset Manager**, click the **Import** button. 2. Upload your image files. 3. After moderation clears for your image, select the `Class.MeshPart` parented within your imported `Class.Model`. 4. If you are using a single basic texture, set the `Class.MeshPart.TextureID` property to your uploaded texture image. 5. If you are using PBR textures: 1. Add a `Class.SurfaceAppearance` child to your `Class.MeshPart`. 2. In the `Class.SurfaceAppearance` properties, click each property value and assign the appropriate texture image from the asset dropdown: 1. Set the **ColorMap** to the **_ALB** texture image. 2. Set the **MetalnessMap** to the **_MTL** texture image. 3. Set the **NormalMap** to the **_NOR** texture image. 4. Set the **RoughnessMap** to the **_RGH** texture image. > **Success:** After successful import, your model object should populate in your project as a `Class.Model` with the appropriate textures applied. After successful fitting and converting, your 3D model should populate in your project as an `Class.Model`. _Avatar-ready character model in Workspace_ _Avatar-ready character model in Explorer_ > **Warning:** Character creation is a complex process that requires lots of testing and iteration. See [Test characters](/docs/en-us/art/characters/testing.md) for steps on importing your character model into a test place and verifying your avatar and related components. With this new character `Class.Model`, you can perform any of the following: - If your character includes a [higher-fidelity rig](/docs/en-us/avatar/character-bodies/specifications.md#higher-fidelity-rigs), add a `Class.HumanoidRigDescription` and/or `Class.DigitsRigDescription` objects for Marketplace and animation requirements. You may need to use the [Adaptive Animation](/docs/en-us/characters/adaptive-animation.md) plugin to remap your joints and set a baseline t-pose reference to better support your animations. - Begin the process of [uploading and publishing](/docs/en-us/marketplace/publish-to-marketplace.md#upload-an-asset) the character model to the Marketplace. This involves some additional validation and moderation steps. - Use the model in your current experience and modify the model's appearance with [HumanoidDescription](/docs/en-us/characters/appearance.md#manually-modify-appearance). - Playtest as the character by renaming the `Class.Model` to `StarterCharacter` and moving the `Class.Model` to the `Class.StarterPlayer` folder in the **Explorer** before play testing. - Save the model to your [Toolbox](/docs/en-us/projects/assets/toolbox.md) or make it public on the [Creator Store](/docs/en-us/production/creator-store.md) to share or use within any of your experiences. --- title: "Character bodies" url: /docs/en-us/avatar/character-bodies last_updated: 2026-06-29T19:33:56Z description: "All Roblox users are represented by an avatar character which can be customized with body parts, accessories, and clothes from the Marketplace or within experiences." --- # Character bodies Every Roblox user is represented by a customizable character called an **avatar**. Avatars are character models with many specialized features that allow users to interact with the world and customize themselves with a wide range of clothing and accessories from the Marketplace. Custom avatars are first created in 3D modeling programs, such as [Blender](https://www.blender.org/) or [Maya](https://www.autodesk.com/products/maya/overview), before importing into Studio. To create a custom Roblox avatar character for your own experience, it's important to start with the following: - An advanced background with 3D modeling tools such as [Blender](https://www.blender.org/) or [Maya](https://www.autodesk.com/products/maya/overview). - An understanding of the [components that make up an avatar character](#components-of-an-avatar). - An understanding of the general [character creation process](#creation-process). - Review the [basic character creation tutorial](/docs/en-us/art/characters/creating.md) to get started on creating your first avatar character using Roblox's templates. - [Various tools, resources, and guides](#resources) provided by Roblox to standardize and expedite the creation process. ## Components of an avatar All avatar character models are made up of several fundamental components that provide users the functionality and flexibility to interact with their world. Many of these components are never made visible to the user, but they enable powerful avatar features that enhance social and environmental interactions. When creating avatar characters, all of these components are typically created first in your modeling software and then converted to their appropriate Roblox Studio instance on import. Each avatar character is made up of the following rendered and non-rendered components: #### Rendered _15 body part meshes make up the physical aspects of the character model_ _Texture image maps apply a surface color and appearance. Transparency allows underlying part colors to be exposed, such as custom skin tones._ #### Non-Rendered _A rigging armature allows the body parts to properly connect with each other, driving movement, expressions, and animations_ _Facial animation data allows avatars to express virtually all types of facial expressions for chatting and socializing_ _Cage meshes set the outer boundary of the character, defining the surface where clothing and other layerable items can wrap and stretch over._ _Attachment points define where rigid accessories attach to a character_ ### Body parts _Each avatar character is made up of 15 separate mesh objects_ _These meshes must follow a standard naming convention_ Roblox avatar characters are made up of 15 body parts, pieces of geometry that define the shape and contours of your avatar character. In Studio, these geometries are represented as `Class.MeshPart` objects and are nested under a single `Class.Model`. ### Textures _Textures apply color and surface details to your character model._ _A texture image map's opacity can combine with the `Class.MeshPart.Color` to apply custom skin tones to characters._ Textures are image files that define the surface appearance of your character. You can create textures using a texture painting program or a 3D modeling software. In Studio, you must import textures as image files and access them through the `Class.SurfaceAppearance` instance, or set as a `Class.MeshPart.TextureID` property. > **Warning:** When texturing parts of your character model's body, ensure that your character model includes a modesty layer over sensitive regions. See [Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410#safety) for more information on Roblox's policies. ### Rigging armature _The armature is made up of 16 bones, 1 for each body part geometry and a root bone_ _Bones must follow a specific hierarchy and naming convention_ An armature allows each character to articulate its limbs and move naturally through the environment. Often referred to as bones or joints, this rigging character information includes skinning data which allows connected limbs, like the knees or elbows to bend organically. In Studio, each bone of the character armature is represented by `Class.Bone` objects that connect the character `Class.MeshPart` objects together. [Standard character rigs](/docs/en-us/avatar/specifications.md#standard-rigs) require 15 poseable `Class.Bone` objects, and higher-fidelity rigs support up to 37 additional `Class.Bone` objects for a greater level of realism with articulated hands, shoulders, and spine movements. All `Class.Bone` objects must follow a specific hierarchy and naming convention to function properly in Studio and on the Marketplace. For more information, see [Rigging](/docs/en-us/avatar/specifications.md#rigging). ### Face animation data _Each avatar character face is rigged and skinned to create various expressions_ _Each pose is mapped to a pose name within the Custom Properties (Extra Attributes in Maya) of the Head_Geo object_ _Each required face pose is saved as a keyframe in the animation timeline._ Facial animation data allow each character to use global facial expressions. Each character includes facial bones and skinning, animation timeline data, and mapped pose data that allows it. In Studio, these facial animation elements are represented by a `Class.FaceControls` instance. ### Cage meshes _Head and Upper Torso cage mesh objects (wireframe)_ _Cage objects must exist for each of the 15 body parts_ This outer cage sets the invisible surface on which layerable accessories, such as clothing, will stretch and fit over the body. These cage meshes allow clothes to fit over models of different shapes and sizes without having to remodel the clothing item. In Studio, outer cage mesh objects are represented by a `Class.WrapTarget` instance. If you are caging your own non-template character model, it's important to use one of Roblox's body cage [project files](/docs/en-us/avatar/resources.md#mannequin-models) to ensure you are using a Roblox standard cage mesh. Removing or adding vertices from this standard mesh can cause issues with clothing fit and import. ### Attachments _Each character has common attachment points for equipping rigid cosmetics_ _Each avatar character must include their associated 19 attachment points_ Attachment points define where rigid 3D accessories and equipables attach to the character's body. These are not rendered to the end-user but are represented as sphere geometries in 3D modeling software and, when imported into Studio, these geometries are created as `Class.Attachment` instances using the standardized names. When wearing layered clothing, the clothing isn't attached directly to the attachment, but it does reference the associated attachment point during ragdoll and dismemberment animations. ## Creation process When designing an avatar model, you must export all of the avatar components in a single `.fbx` or `.gltf` for import into Studio. Since 3D creation isn't a linear process and always requires reiteration and testing, the process of creating an avatar character model can differ between individuals and various creation workflows. In general, the creation process follows these typical workflows: _ Basic Creation with Templates__Customize a Roblox template character that already includes all the necessary components. See [Create with templates](/docs/en-us/art/characters/creating.md) for guides and instructions._ _ Advanced Creation from Scratch__Create characters from scratch, allowing full customization of the avatar character's components._ > **Warning:** Ensure that all assets you create follow any applicable [Marketplace policies](/docs/en-us/marketplace/marketplace-policy.md) and [Roblox Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards). ## Resources There are a variety of resources available for creators of all backgrounds to get started with character creation. If you are interested in specific avatar creation topics, use the following table to find guides and resources that best match your needs: | Topic | Resources | | --- | --- | | Tutorials | [Basic character creation tutorial](/docs/en-us/art/characters/creating.md) | | Reference files | [Avatar references and project files](/docs/en-us/avatar/project-files.md)

[Example mesh/model objects](/docs/en-us/art/modeling/project-files.md) | | Technical specs | [.FBX export settings](/docs/en-us/avatar/export.md)

[Avatar specifications](/docs/en-us/avatar/specifications.md)

[General mesh specifications](/docs/en-us/art/modeling/specifications.md)

[Accessory specifications](/docs/en-us/rigid-accessories/specifications.md)

[Marketplace policy](/docs/en-us/marketplace/marketplace-policy.md) | | Cosmetic creation | [Accessories overview](/docs/en-us/rigid-accessories.md)

[Creating face accessories](/docs/en-us/art/characters/facial-animation/create-face-accessories.md)

[Accessory Fitting Tool](/docs/en-us/accessory-fitting-tool.md)

[Accessory specifications](/docs/en-us/rigid-accessories/specifications.md)

[Marketplace requirements](/docs/en-us/marketplace/marketplace-policy.md) | | Texturing | [Texturing requirements](/docs/en-us/art/modeling/texture-specifications.md)

[PBR textures](/docs/en-us/art/modeling/surface-appearance.md) | | Rigging and skinning | [Rigging and skinning overview](/docs/en-us/art/modeling/rigging.md)

[Humanoid rig requirements](/docs/en-us/avatar/specifications.md#rig)

[Rigging facial bones](/docs/en-us/art/characters/facial-animation/create-basic-heads.md#rigging)

[Automatic Skinning Transfer](/docs/en-us/automatic-skinning-transfer.md)

[Skinning facial bones](/docs/en-us/art/characters/facial-animation/create-basic-heads.md#skin-face-bones) | | Facial animation and live heads | [Basic head creation](/docs/en-us/art/characters/facial-animation/create-basic-heads.md)

[Face accessory creation](/docs/en-us/art/characters/facial-animation/create-face-accessories.md)

[FACS pose references](/docs/en-us/dynamic-heads/facs-poses-reference.md) | | Testing and validation | [Calisthenics Tool](/docs/en-us/art/modeling/calisthenics-tool.md)

[Clothing Validation Tool](/docs/en-us/art/accessories/validation-tool.md) | | Publishing and Marketplace | [Uploading to Marketplace](/docs/en-us/marketplace/publish-to-marketplace.md)

[Marketplace policy](/docs/en-us/marketplace/marketplace-policy.md)

[Fees and commissions](/docs/en-us/marketplace/marketplace-fees-and-commissions.md) |
--- title: "Avatar project files and references" url: /docs/en-us/avatar/character-bodies/project-files last_updated: 2026-06-29T19:33:56Z description: "Download various character-related project files and reference files." --- # Avatar project files and references > **Info:** See [Resources](/docs/en-us/avatar/resources.md) for a complete list of avatar-related downloadable content. ## Project files The following `.fbx`, `.blend`, and `.ma` project files are available to use as examples, boilerplate, or reference: #### Models **Higher-fidelity rigs** The following [higher-fidelity](/docs/en-us/avatar/character-bodies/specifications.md#higher-fidelity-rigs) humanoid rigs include additional bones or joints for a higher level of realism with articulated hands, shoulders, and spine movements. | **File name** | **Description** | | --- | --- | | [Mannequin-HigherFidelity.zip](../../assets/art/reference-files/Mannequin-HigherFidelity.zip) | A comprehensive `.zip` folder of a AA avatar-ready character body, including a dynamic head, fingers, layered clothing, and 2K PBR textures. | | [Robuta-HigherFidelity.zip](../../assets/art/reference-files/Robuta-HigherFidelity.zip) | A comprehensive `.zip` folder of an anime-style, avatar-ready character body, including a dynamic head, fingers, layered clothing and rigid accessory assets, and 2K PBR textures. | | [HipToBeSquare-HigherFidelity.zip](../../assets/art/reference-files/HipToBeSquare-HigherFidelity.zip) | A comprehensive `.zip` folder of a blocky-style, avatar-ready character body, including a dynamic head, fingers, layered clothing and rigid accessory assets, and color, roughness, and metal PBR textures. | | [Roxie-HigherFidelity.zip](../../assets/art/reference-files/Roxie-HigherFidelity.zip) | A comprehensive `.zip` folder of an avatar-ready character with an [Rthro Normal](/docs/en-us/avatar/character-bodies/specifications.md#normal) body, including a dynamic head, fingers, layered clothing and rigid accessory assets, and PBR textures. When you import this model, set **Rig Scale** to **Rthro Narrow**. | **Standard rigs** The following [standard](/docs/en-us/avatar/character-bodies/specifications.md#standard-rigs) humanoid rigs include the standard amount of bones or joints. | **File name** | **Description** | | --- | --- | | [Lola.fbx](../../assets/modeling/skinned-meshes/Lola.fbx) | A skinned R15 character created from the [Skin a humanoid model](/docs/en-us/art/modeling/skin-a-humanoid-model.md) guide. Since this reference model doesn't yet have [inner and outer cage mesh data](/docs/en-us/avatar/character-bodies/specifications.md#inner-and-outer-cages), this model can't equip layered clothing or accessories. | | [Fish-Person.zip](../../assets/avatar/dynamic-heads/reference-files/Fish-Person.zip) | A rigged and skinned humanoid character model with a full body cage, facial animation rig, and associated PBR texture maps. | | [BlockyCharacter.fbx](../../assets/avatar/dynamic-heads/reference-files/BlockyCharacter.fbx) | A Blocky character model with an [animatable head](/docs/en-us/avatar/dynamic-heads.md) and a full body cage. | | [GoblinCharacter.zip](../../assets/avatar/dynamic-heads/reference-files/GoblinCharacter.zip) | A Goblin character model with an [animatable head](/docs/en-us/avatar/dynamic-heads.md) and a full body cage. | | [ClassicMannequin.fbx](../../assets/art/reference-files/ClassicMannequin.fbx) | A [classic](/docs/en-us/avatar/character-bodies/specifications.md#classic) body type blank mannequin to use in Studio or your modeling application. | | [ClassicMannequin_With-Cages](../../assets/art/reference-files/ClassicMannequin_With-Cages.fbx) | A [classic](/docs/en-us/avatar/character-bodies/specifications.md#classic) body type blank mannequin with **body cages** to use in Studio or your modeling application for clothing design. Due to unconfigured cage objects, this file may not import correctly into Studio until modified. | | [RthroMannequin.fbx](../../assets/art/reference-files/RthroMannequin.fbx) | An [Rthro Normal](/docs/en-us/avatar/character-bodies/specifications.md#normal) body type blank mannequin to use in Studio or your modeling application. | | [RthroMannequin_With-Cages.fbx](../../assets/art/reference-files/RthroMannequin_With-Cages.fbx) | An [Rthro Normal](/docs/en-us/avatar/character-bodies/specifications.md#normal) body type blank mannequin with **body cages** to use in Studio or your modeling application for clothing design to use in Studio or your modeling application. Due to unconfigured cage objects, this file may not import correctly into Studio until modified. | | [RthroSlenderMannequin.fbx](../../assets/art/reference-files/RthroSlenderMannequin.fbx) | An [Rthro Slender](/docs/en-us/avatar/character-bodies/specifications.md#slender) body type blank mannequin to use in Studio or your modeling application. | | [RthroSlenderMannequin_With-Cages.fbx](../../assets/art/reference-files/RthroSlenderMannequin_With-Cages.fbx) | An [Rthro Slender](/docs/en-us/avatar/character-bodies/specifications.md#slender) body type blank mannequin with **body cages** to use in Studio or your modeling application to use in Studio or your modeling application for clothing design. Due to unconfigured cage objects, this file may not import correctly into Studio until modified. | #### Blender | **File** | **Description** | | --- | --- | | [Rig_and_Attachments_Template.blend](../../assets/modeling/meshes/reference-files/Rig_and_Attachments_Templates.zip) | Starting armature rig template for Blender. Contains an armature with correct R15 naming conventions and attachment points. Use this template for rigging bodies and clothing items. | | [Body_Cage_Template.blend](../../assets/modeling/meshes/reference-files/Body_Cage_Templates.zip) | Starting template for Blender, includes individual body part cages for each 15 humanoid parts. Use this template for caging your avatar bodies.

> **Info:** To save time, edit the full-body `std_cage_deformable` mesh to automatically apply vertex changes to the individual body-part meshes. When importing the `.fbx` into Studio, you can remove this helper mesh. | | [Combined-Template.blend](../../assets/modeling/meshes/reference-files/Combined_Templates.zip) | Template file containing all content from previous templates, includes rig skeleton, body cages, attachment points. Use this template to rig and cage bodies and accessories. | #### Maya | **File** | **Description** | | --- | --- | | [Rig_and_Attachments_Template.ma](../../assets/modeling/meshes/reference-files/Rig_and_Attachments_Templates.zip) | Starting armature rig template for Maya. Contains an armature with correct R15 naming conventions and attachment points. Use this template for creating bodies and clothing items. | | [Body_Cage_Template.ma](../../assets/modeling/meshes/reference-files/Body_Cage_Templates.zip) | Starting template for Maya, includes individual body part cages for each 15 humanoid parts. Use this template for caging your avatar bodies.

> **Info:** To save time, edit the full-body `std_cage_deformable` mesh to automatically apply vertex changes to the individual body-part meshes. When importing the `.fbx` into Studio, you can remove this helper mesh. | | [Combined-Template.ma](../../assets/modeling/meshes/reference-files/Combined_Templates.zip) | Template file containing all content from previous templates, includes rig skeleton, body cages, attachment points. Use this template to rig and cage bodies and accessories. | #### General > **Warning:** The following `.fbx` files were exported from Maya for use in modeling applications. If you are using Blender or Maya in your workflow, use the specific template files for those programs instead of the following files. | **File** | **Description** | | --- | --- | | [Rig_and_Attachments_Template.fbx](../../assets/modeling/meshes/reference-files/Rig_and_Attachments_Templates.zip) | Starting armature rig template for general modeling software. Contains an armature with correct R15 naming conventions and attachment points. Use this template for creating bodies and clothing items. | | [Body_Cage_Template.fbx](../../assets/modeling/meshes/reference-files/Body_Cage_Templates.zip) | Starting template for general modeling software, includes individual body part cages for each 15 humanoid parts. Use this template for caging your avatar bodies.

> **Info:** To save time, edit the full-body `std_cage_deformable` mesh to automatically apply vertex changes to the individual body-part meshes. When importing the `.fbx` into Studio, you can remove this helper mesh. | ## Templates If you are creating character models, you can choose from a variety of starting templates that best match your final design and save time on creating the [avatar components](/docs/en-us/avatar/character-bodies.md#components-of-an-avatar). See [Create with templates](/docs/en-us/art/characters/creating.md#template-files) for important information on using these template files. Each `.zip` contains a `.blend`, `.fbx`, and PBR texture `.png` files for that template model. If using Blender or following the [template creation guides](/docs/en-us/art/characters/creating.md), use the `.blend` project. > **Warning:** If you are using Roblox's avatar template files, you must perform the [cleanup steps](/docs/en-us/art/characters/creating/combine-head-geometry.md) in order for the assets to properly validate before publishing to the Marketplace. #### Head Shapes _ Round_ Female: [RoundFemale.zip](../../assets/art/reference-files/RoundFemale.zip) Male: [RoundMale.zip](../../assets/art/reference-files/RoundMale.zip) _ Square_ Female: [SquareFemale.zip](../../assets/art/reference-files/SquareFemale.zip) Male: [SquareMale.zip](../../assets/art/reference-files/SquareMale.zip) _ Muzzle_ Female: [MuzzleFemale.zip](../../assets/art/reference-files/MuzzleFemale.zip) Male: [MuzzleMale.zip](../../assets/art/reference-files/MuzzleMale.zip) #### Realistic _ Realistic_ Female: [SemiRealisticFemale.zip](../../assets/art/reference-files/SemiRealisticFemale.zip) Male: [SemiRealisticMale.zip](../../assets/art/reference-files/SemiRealisticMale.zip) #### Anime _ Anime_ Female: [AnimeFemale.zip](../../assets/art/reference-files/AnimeFemale.zip) Male: [AnimeMale.zip](../../assets/art/reference-files/AnimeMale.zip) #### Stylized _ Stylized_ Single body: [Stylized.zip](../../assets/art/reference-files/StylizedHuman.zip) #### Caricature _ Caricature_ Single body: [Caricature.zip](../../assets/art/reference-files/Caricature.zip)
--- title: "Character specifications" url: /docs/en-us/avatar/character-bodies/specifications last_updated: 2026-06-29T19:33:57Z description: "Character specification lists the specific technical requirements for custom characters created outside of Studio." --- # Character specifications Character models require a specific set of components and configuration standards to ensure all avatar features work as expected. Check that your model meets the following modeling specifications and guidelines before exporting to ensure Studio compatibility. When ready to export, see the [export requirements](/docs/en-us/avatar/character-bodies/export.md) for mesh export settings for Blender and Maya. > **Warning:** **If creating other types of 3D models:**- For generic meshes, see [general mesh specifications](/docs/en-us/art/modeling/specifications.md) and [general export settings](/docs/en-us/art/modeling/export-requirements.md). - For rigid accessories, see [accessory specifications](/docs/en-us/avatar/rigid-accessories/specifications.md) and [accessory export settings](/docs/en-us/avatar/rigid-accessories/export.md). - For layered accessories, see [layered accessory specifications](/docs/en-us/avatar/layered-accessories/specifications.md) and [layered export settings](/docs/en-us/avatar/layered-accessories/export.md). ## Geometry Avatar character models are made up of 15 separate mesh objects and require additional geometry requirements to import into Studio and publish successfully. In addition to the body [scale types](#body-scale), [parts](#body-parts), and [triangle budget](#triangle-budgets) specifications, ensure that your models also fulfill the following general requirements when modeling: - **Watertight** - All geometry must be watertight without exposed holes or backfaces. Meshes used as outer cages do not need to be watertight. - **No N-gons** - Model your assets in quads where possible. - **Transformations** - All translation, rotation, and scale values must be frozen and pivots set to `0`, `0`, `0`. - **Orientation** - Characters must face positive Z and stand up in positive Y. - **Pose** - Before exporting, set your character to an I-Pose, A-Pose, or T-Pose when possible. ### Body scale Roblox supports 3 standards of body scales: **Normal**, **Slender**, and **Classic**. These standards allow developers to create experiences and spaces with consistent body sizes for standardizing movement and interaction. You can select a body scale on import using [Rig Scale options](/docs/en-us/studio/importer.md), or modify the [body scale manually](/docs/en-us/art/accessories/body-scale.md). Body scale persists when the asset is [uploaded to the Marketplace](/docs/en-us/marketplace/publish-to-marketplace.md#upload-an-asset). _ A visualization of how Roblox combines the 15 body parts into 6 distinct assets._ > **Warning:** Since body assets partially overlap with each other, the total body dimensions do not represent the sum of the avatar's asset dimensions. The total body dimension is the most important factor of each body scale to ensure a standard avatar size and scale. #### Normal _ A Rthro Normal body scale [downloadable mannequin](/docs/en-us/avatar/resources.md#references)._ In the [Importer](/docs/en-us/studio/importer.md#avatar-general), use **Rig Type** > **Rthro** to import your model as a Normal body scale. _ Minimum (in studs)_ | Part | X (width) | Y (height) | Z (depth) | | --- | --- | --- | --- | | Head | .5 | .5 | .5 | | Arm | .25 | 1.5 | .25 | | Torso | .85 | 1.7 | .7 | | Leg | .25 | 1.4 | .5 | | **Total** | **1.35** | **3.6** | **.7** | _ Maximum (in studs)_ | Part | X (width) | Y (height) | Z (depth) | | --- | --- | --- | --- | | Head | 3 | 2 | 2 | | Arm | 2 | 4.5 | 2 | | Torso | 4.6 | 3.5 | 2.25 | | Leg | 1.5 | 4 | 2 | | **Total** | **8.6** | **9.5** | **2.25** | #### Slender _ A Rthro Slender (Narrow) body scale [downloadable mannequin](/docs/en-us/avatar/resources.md#references)._ In the [Importer](/docs/en-us/studio/importer.md#avatar-general), use **Rig Type** > **Rthro Narrow** to import your model as a Slender body scale. _ Minimum (in studs)_ | Part | X (width) | Y (height) | Z (depth) | | --- | --- | --- | --- | | Head | .5 | .5 | .5 | | Arm | .25 | 1.5 | .25 | | Torso | .85 | 1.7 | .7 | | Leg | .25 | 1.4 | .5 | | **Total** | **1.35** | **3.6** | **.7** | _ Maximum (in studs)_ | Part | X (width) | Y (height) | Z (depth) | | --- | --- | --- | --- | | Head | 2 | 2 | 2 | | Arm | 1.5 | 4 | 2 | | Torso | 3 | 3.5 | 2 | | Leg | 1.5 | 4 | 2 | | **Total** | **6** | **9.5** | **2** | #### Classic _ A Classic body scale [downloadable mannequin](/docs/en-us/avatar/resources.md#references)._ In the [Importer](/docs/en-us/studio/importer.md#avatar-general), use **Rig Type** > **Default** to import your model as a Classic body scale. _ Minimum (in studs)_ | Part | X (width) | Y (height) | Z (depth) | | --- | --- | --- | --- | | Head | .5 | .5 | .5 | | Arm | .25 | 1.5 | .25 | | Torso | .85 | 1.7 | .7 | | Leg | .25 | 1.4 | .5 | | **Total** | **1.35** | **3.6** | **.7** | _ Maximum (in studs)_ | Part | X (width) | Y (height) | Z (depth) | | --- | --- | --- | --- | | Head | 1.5 | 1.8 | 2 | | Arm | 2 | 3 | 2 | | Torso | 4 | 3.8 | 2 | | Leg | 1.5 | 3.5 | 2 | | **Total** | **8** | **9.1** | **2** | ### Triangle budgets Although model geometries are typically created using quads, the Roblox Engine converts imported assets into tris. Each asset of your character model must not exceed our maximum tri budget. To quickly get the number of expected tris in your third-party modeling application, you can double the number of quads in your model. > **Info:** When character models are uploaded to Studio and created into assets for the Marketplace, the body is split into 6 individual assets: **DynamicHead**, **Torso**, **LeftArm**, **RightArm**, **LeftLeg**, **RightLeg**. | Asset type | Included mesh objects | Maximum triangles | | --- | --- | --- | | DynamicHead | Head | 4000 | | Torso | UpperTorso, LowerTorso | 1750 | | LeftArm | LeftUpperArm, LeftLowerArm, LeftHand | 1248 | | Right Arm | RightUpperArm, RightLowerArm, RightHand | 1248 | | LeftLeg | LeftUpperLeg, LeftLowerLeg, LeftFoot | 1248 | | Right Leg | RightUpperLeg, RightLowerLeg, RightFoot | 1248 | | Total | Head, UpperTorso, LowerTorso, LeftUpperArm, LeftLowerArm, LeftHand, RightUpperArm, RightLowerArm, RightHand, LeftUpperLeg, LeftLowerLeg, LeftFoot, RightUpperLeg, RightLowerLeg, RightFoot | 10,742 | ### Body parts See the following specifications for the individual mesh objects that make up a character model: - **Naming Convention** - Limbs must all be specifically named: - UpperTorso_Geo - LowerTorso_Geo - LeftUpperArm_Geo - LeftLowerArm_Geo - LeftHand_Geo - RightUpperArm_Geo - RightLowerArm_Geo - RightHand_Geo - LeftUpperLeg_Geo - LeftLowerLeg_Geo - LeftFoot_Geo - RightUpperLeg_Geo - RightLowerLeg_Geo - RightFoot_Geo - Head_Geo - **Caps** - Limbs must be capped, so that they form a full watertight mesh when separated from the body. Caps can be rounded or flat depending on design. > **Info:** **Interested in creating a higher-fidelity avatar?** > > Higher-fidelity character models require the same 15 mesh objects and naming structure. The only difference is that the mesh objects in higher-fidelity character models can have additional internal bones or joints. When these bones or joints are present in a character model, their corresponding mesh objects need to be [skinned](#skinning) for accurate deformation. ### Face accessories Face accessories, such as hair, eyebrows, and eyelashes are unique accessories that you can bundle with an avatar body upload. At this time, eyebrows and eyelashes can not be uploaded as standalone accessories and must be bundled with an avatar body. See [Accessory specifications](/docs/en-us/avatar/rigid-accessories/specifications.md#face-accessories) for additional information on face accessories. ### Visibility To ensure that avatar sizes are visually consistent, you must standardize body part visibility with the following requirements: - **Opacity** - Body parts must be fully opaque. - **Bounding Boxes** - Body part assets must take up a significant visible portion of their bounding box in a front, side, and back view. - Body parts, such as Torso, Left Arm, Right Leg, must take up at least 50% of body part's bounding box. - The head part must take up at least 50% of the mesh's bounding box._An acceptable head mesh example that takes up a majority of the bounding box in the front view__A non-acceptable head mesh example that does not take up the appropriate amount of bounding box space in the front view_ ## Attachments Attachments are points on the humanoid model where rigid accessories attach. Like many [character components](/docs/en-us/avatar/character-bodies.md#components-of-an-avatar), attachment points are set up in third-party modeling software and imported as `Class.Attachment` objects. The Importer automatically recognizes and converts mesh objects as attachment points if the objects include the affix `\_Att` and follow the naming conventions below. This only applies when importing meshes with caging data, such as bodies (accessory attachment points are created using the Accessory Fitting Tool). Check out examples of this implementation in any of the [downloadable reference models](/docs/en-us/avatar/resources.md#references). Generally, when placing attachment points, position them so they overlap halfway with the character model's mesh part. _Incorrect placement of collar attachments_ _Correct placement of collar attachments_ Attachments must follow a specific naming convention and positional consistency: | Mesh part | Attachment name | Details | | --- | --- | --- | | Head | FaceCenter_Att | Anywhere within mesh bounding box | | FaceFront_Att | Front half of mesh bounding box | | Hat_Att | Top half of mesh bounding box, can overlap with Hair_Att | | Hair_Att | Top half of mesh bounding box, can overlap with Hat_Att | | UpperTorso | LeftCollar_Att | Left top half of mesh bounding box | | RightCollar_Att | Right top half of mesh bounding box | | Neck_Att | Top half of mesh bounding box | | BodyBack_Att | Back half of mesh bounding box | | BodyFront_Att | Front half of mesh bounding box | | LowerTorso | Root_Att | Must set to `0`, `0`, `0` position | | WaistFront_Att | Front half of mesh bounding box, can overlap with WaistCenter_Att | | WaistBack_Att | Back half of mesh bounding box | | WaistCenter_Att | Anywhere in mesh bounding box, can overlap with WaistFront_Att | | RightUpperArm | RightShoulder_Att | Top half of mesh bounding box | | RightHand | RightGrip_Att | Anywhere in mesh bounding box.

Orient grip attachments **perpendicular** to the lower arm bone. For example, for I-Pose (arms pointing down), set the grip attachment to `90`, `0`, `0` (grip attachment pointing forward).

| | LeftUpperArm | LeftShoulder_Att | Top half of mesh bounding box | | LeftHand | LeftGrip_Att | Anywhere in mesh bounding box.

Orient grip attachments **perpendicular** to the lower arm bone. For example, for I-Pose (arms pointing down), set the grip attachment to `90`, `0`, `0` (grip attachment pointing forward).

| | RightFoot | RightFoot_Att | Anywhere in mesh bounding box | | LeftFoot | LeftFoot_Att | Anywhere in mesh bounding box | ## Rigging Unlike generic rigs, humanoid models require a specific hierarchy and naming conventions for the internal bone or joint structure. _Humanoid rig model_ _Humanoid rig bone structure (Blender)_ ### Standard rigs The following requirements are for standard humanoid rigging. - **Rig Hierarchy** - Humanoid rigs require a specific bone or joint hierarchy and naming convention: - Root - HumanoidRootNode - LowerTorso - UpperTorso - Head (representing the base of the neck) - LeftUpperArm - LeftLowerArm - LeftHand - RightUpperArm - RightLowerArm - RightHand - LeftUpperLeg - LeftLowerLeg - LeftFoot - RightUpperLeg - RightLowerLeg - RightFoot ** Blender standard humanoid rig hierarchy with all bones** ** Maya standard humanoid rig hierarchy with all joints** - **LowerTorso and Root** - The LowerTorso and Root bone or joint position must be set to `0`, `0`, `0`. - **Pose** - Export your character model in an I-Pose, A-Pose, or T-Pose for the best Studio compatibility. The LeftUpperArm and RightUpperArm bones can be exported with rotation values to meet this requirement. ### Higher-fidelity rigs Higher-fidelity humanoid rigs have the same requirements as standard rigs, but they support up to 37 additional optional bones or joints for a higher level of realism with articulated hands, shoulders, and spine movements. You do not need to include every optional bone or joint for a higher-fidelity humanoid rig, but each optional bone or joint you include must follow a specific hierarchy and naming convention for the respective body part. #### Torso | Hierarchy | Example Rig Setup | | --- | --- | | | | #### LeftHand | Hierarchy | Example Rig Setup | | --- | --- | | | | #### RightHand | Hierarchy | Example Rig Setup | | --- | --- | | | | #### LeftFoot | Hierarchy | Example Rig Setup | | --- | --- | | | | #### RightFoot | Hierarchy | Example Rig Setup | | --- | --- | | | | ** Blender humanoid rig hierarchy with all optional bones** ** Maya humanoid rig hierarchy with all optional joints** > **Info:** Make sure that you skin each additional bone or joint's corresponding mesh object for accurate deformation. After you [import a higher-fidelity character rig](/docs/en-us/avatar/character-bodies/import.md) into Studio, you **must** insert a `Class.HumanoidRigDescription` and/or `Class.DigitsRigDescription` objects into your rig to be able to sell your character on the Marketplace, and for your animations to work properly: - `Class.HumanoidRigDescription` objects are necessary for animating individual body parts and adjusting behavioral characteristics of the rig, such as each bone or joint's size and range of motion. - `Class.DigitsRigDescription` objects are necessary for hand articulation. Your character needs one for each hand with optional bone or joints. Both of these object types detect bones or joints by their naming conventions, so it is very important to ensure every optional bone or joint is named correctly following the table above **before** you import your character rig into Studio. For a set of higher-fidelity rigs that you can reference, see [Resources - Higher-fidelity rigs](/docs/en-us/avatar/resources.md#higher-fidelity-rigs). ## Skinning Roblox supports skinning for rigged meshes. See [Rigging and Skinning](/docs/en-us/art/modeling/rigging.md) for more details on implementing skinned meshes in your experience. See the following requirements for skinning: - **Max Influences** - A vertex can not be influenced by more than 4 bones or joints. - **No Root Influences** - Do not apply influences to the Root bone or joint. - **Symmetry** - When possible, maintain symmetry when applying influences to a rig. ## Facial animation For information on required facial animation support, see [head specifications](/docs/en-us/avatar/dynamic-heads/specifications.md#facs-animation). ## Textures - Textures for Marketplace assets can't exceed 2048x2048 resolution. - Textures created for accessories must meet Roblox's [texture specifications](/docs/en-us/art/modeling/texture-specifications.md). High resolution textures are automatically converted to lower-resolution textures to optimize performance. - Avatar characters must follow Roblox's [texture specifications](/docs/en-us/art/modeling/texture-specifications.md). Characters created for the Marketplace can take advantage of [custom skin tones](#custom-skin-tone) which use alpha layers allow users to select their own base color. ### Custom skin tone When texturing an avatar character model where the user can set their own skin tone, set the exposed skin areas as transparent so a default color or texture is not baked into the texture. When rendering, the Studio engine applies the transparent layer on top without any layer effects. This step applies mostly to human-like characters with exposed skin that you intend users to customize. The following are examples of skin tone shading for the face, and you can apply this process with any part of an avatar with exposed skin: - Use 100% opacity for areas not related to a customized skin tone, such as eyes and brows. - When applying shading, use black at low opacity and adjust opacity when needed. - For areas that are partially translucent, such as shading contours or certain cosmetic features, leave some transparency for partial visibility of the skin tone. - You should consistently test out how various colors layers can interact with your shading. _Beige_ _Blue_ _Brown_ _Pink_ ## Outer cages Outer cages on your character model allow your character to wear layerable accessories, such as clothing. Models, such as an avatar character, that are the target of meshes that will stretch over it, only require an outer cage, but meshes that deform, like [layered clothing](/docs/en-us/avatar/layered-accessories/specifications.md#cage-meshes), require an inner and outer cage. For general use, see the following requirements for adding outer cage meshes to your model: - Use Roblox's [Avatar Setup](/docs/en-us/avatar-setup.md) or Roblox's [body cage templates](/docs/en-us/avatar/resources.md#project-files) to ensure naming convention and other configurations are correct. - **Naming Conventions** - The outer cage of a body part must be named after the primary mesh object with **_OuterCage** affixed, for example `LeftUpperArm_OuterCage`. - **Vertices and UV Map** - Don't delete vertices or alter the UVs on the Outer Cages as this can cause errors when importing in Studio or when equipping onto a character. Use the Roblox provided templates for the cage meshes to ensure compatibility with other layered assets. - **Symmetry and consistency** - Keep each face (the space between vertices) consistently sized and retain symmetry wherever possible. Use symmetry tools in your modeling software whenever possible. - For information on head cages and best practices, see [head specifications](/docs/en-us/avatar/dynamic-heads/specifications.md#head-cage). ## Marketplace requirements Along with the other technical requirements listed, your items must meet the following additional specifications before uploading them to the Marketplace to sell: - Ensure that your items adhere to the [Marketplace program guidelines](/docs/en-us/marketplace/marketplace-policy.md). - Whenever possible, ensure that your items adhere to the following modeling requirements: - [Custom mesh specifications](/docs/en-us/art/modeling/specifications.md) - Any applicable [avatar specifications](/docs/en-us/avatar/character-bodies/specifications.md) - Object `Class.MeshPart.Material|Material` is set to `Plastic`. - Object `Class.MeshPart.Transparency|Transparency` is set to `0`. - Object `Class.MeshPart.VertexColor|VertexColor` is the default `1, 1, 1`. - If your head includes separate eyelash and eyebrow assets, you must add them to your character model as `Class.Accessory` objects. - See [accessory specifications](/docs/en-us/avatar/rigid-accessories/specifications.md) for additional technical requirements. - Your `Class.Model` instance doesn't contain extraneous objects, like `Class.Script` or additional `Class.Part` instances.
--- title: "Classic clothing" url: /docs/en-us/avatar/classic-clothing last_updated: 2026-06-29T19:33:57Z description: "Classic clothing are decals you can apply to a classic character model's surface. You can create classic clothing items on Roblox and upload them to the Marketplace." --- # Classic clothing > **Warning:** Many user-generated avatars on the Marketplace do not support 2D classic clothing. For information on creating modern 3D cosmetics, including rigid accessories and clothing items, see [Get started](/docs/en-us/avatar.md). Create and sell your first 2D classic clothing item. A high-level overview of avatar items on Roblox. Classic clothing are the most basic type of avatar item available to purchase and sell on Roblox. With any image editor and web browser, you can create classic clothing items and sell them on the [Marketplace](https://www.roblox.com/catalog). > **Warning:** To publish and sell assets on the Marketplace, you must meet [creator requirements](/docs/en-us/marketplace/marketplace-policy.md#creator-requirements). To create classic clothing items: 1. Decide on the [type of classic clothing](#types-of-classic-clothing) you'd like to create. 2. [Create](#create-classic-clothing) an image for your asset using an image editor of your choice. 3. [Test](#test-classic-clothing) your creation in Studio to ensure it looks as intended. 4. [Upload](#upload-classic-clothing) the image file to the Marketplace through a web browser. 1. Adding an item to the Marketplace requires a Robux [fee](/docs/en-us/marketplace/marketplace-fees-and-commissions.md#classic-clothing). 2. Upon successful moderation, you gain a copy of the asset in your inventory. ## Types of classic clothing The three types of classic clothing items are **T-shirts**, **Shirts**, and **Pants**. Each has different design and format requirements. ![3 different examples of blocky characters with a different type of image displayed on its surface.](../assets/accessories/classic-clothing/Types-of-classic-clothing.png) ### T-shirts T-shirts are a square image, such as 512x512 pixels. When properly configured, classic t-shirts display on the front torso of a blocky character. ### Shirts and pants Classic shirts and pants are image assets that wrap around an avatar's body. Because classic shirts and pants have specific size requirements, download and modify the provided template files to begin creating these assets. _[Shirt (torso and arms)](../assets/accessories/classic-clothing/Classic-Clothing-Templates.zip)_ _[Pants (torso and legs)](../assets/accessories/classic-clothing/Classic-Clothing-Templates.zip)_ ** Table of sizes for each template part** | Shape | Pixel size (width x height) | Clothing parts | | --- | --- | --- | | Large square | 128 × 128 | **FRONT** and **BACK** | | Tall rectangle | 64 × 128 | Sides of torso (**R**, **L**)
Sides of arms and legs (**L**, **B**, **R**, **F**) | | Wide rectangle | 128 × 64 | **UP** and **DOWN** | | Small square | 64 × 64 | Top and bottom of arms and legs (**U**, **D**) | ## Create classic clothing To begin creating classic clothing: 1. If you are creating a classic shirt or pants, [download and unzip](../assets/accessories/classic-clothing/Classic-Clothing-Templates.zip) the following clothing templates to use as a canvas for your art. 2. Modify the image in an image editor of your choice. 3. Export the image as a `.png` or `.jpg` before testing and uploading it in Studio. When applying clothing to avatars in Roblox, some limits exist with the templates and may require some testing to get right, as shown in the following examples: _The shoe designs in this example are near the bottom of the leg regions (L, B, R, F) but don't extend too far up. This provides a distinct separation between the shoes and the bottom of the jeans._ _The shoe designs in this example extend too far up the leg regions (L, B, R, F) which make them appear as if they're part of the pants. The same issue may occur near other joints on the avatar's body._ > **Success:** After creating your classic clothing, test your creation in Roblox Studio before uploading and selling your asset on the Marketplace. ## Test classic clothing To ensure the best quality results, you can test your classic clothing before uploading or selling. You can test your clothes without needing to pay any fees by using [Roblox Studio](/docs/en-us/studio/setup.md). 1. From the toolbar's **Avatar** tab, click **Character**. 2. Choose a **Block Avatar** rig type from the popup menu. An avatar mannequin named **Rig** appears in the 3D workspace. 3. In the **Explorer** window, hover over the **Rig** object, click the **⊕** button and insert either a **ShirtGraphic** (T‑shirt), **Shirt**, or **Pants** object according to the clothing you want to test. 4. Locate and select the new **Shirt Graphic** or **Clothing** object parented under **Rig**._Shirt Graphic (T-shirt)__Clothing (Shirt/Pants)_ 5. In the **Properties** window, locate the associated property as follows: | Clothing Type | Parent Class | Property | | --- | --- | --- | | **T-Shirt** | `Class.ShirtGraphic` | `Class.ShirtGraphic.Graphic\|Graphic` | | **Shirt** | `Class.Shirt` | `Class.Shirt.ShirtTemplate\|ShirtTemplate` | | **Pants** | `Class.Pants` | `Class.Pants.PantsTemplate\|PantsTemplate` | 6. Click inside the property's row and select the image you uploaded to Roblox. This applies the clothing texture to the **Rig** character. ## Upload classic clothing After creating and testing your classic clothing design, as long as you meet the [creator requirements](/docs/en-us/marketplace/marketplace-policy.md#creator-requirements), you can upload and sell your clothing item on the Marketplace for a [fee](/docs/en-us/marketplace/marketplace-fees-and-commissions.md#classic-clothing). To upload a custom clothing item: 1. Navigate to your [Creator Dashboard](https://create.roblox.com/dashboard/creations). 2. Navigate to **Avatar Items**. 3. Select the **Classics** tab. 4. Drag and drop your image file into the upload box. Alternatively, you can click **Upload Asset** and upload your file on the next page. 5. On the Upload Asset page, set the Asset Type dropdown to **T-Shirt**, **Shirt**, or **Pants**. 6. Complete the **Name** and **Description** fields. These fields help users find your assets and allow you to organize your creations. 7. Click the **Upload** button at the bottom of the page. 1. See [Fees and commissions](/docs/en-us/marketplace/marketplace-fees-and-commissions.md#classic-clothing) for more information on the fees for listing avatar items on the Marketplace. 2. See [Manage existing assets](#manage-existing-assets) for information on modifying and selling your creations. ## Manage classic clothing To see your moderation status, place your item on sale, or modify your item settings: 1. Navigate to your [Creator Dashboard](https://create.roblox.com/dashboard/creations). 2. Navigate to **Avatar Items**. 3. Select the **Classics** tab. 4. Use the **Classic Type** dropdown to filter by the type of classic clothing creation. 5. Click on the asset to navigate to the asset's Configure page. 6. You can modify the following fields: 1. **Item Name** — sets the public-facing name of the asset. 2. **Item Description** — sets the public-facing description of the asset. 3. **On Sale** — when enabled, users can view and purchase this item on the Marketplace. 4. **Set a price** — value of Robux that the creation sells for on the Marketplace. 1. See [Fees and commissions](/docs/en-us/marketplace/marketplace-fees-and-commissions.md#classic-clothing) for more information.
--- title: "Face caging best practices" url: /docs/en-us/avatar/dynamic-heads/caging-best-practices last_updated: 2026-06-29T19:33:57Z description: "Tips on optimizing face cages for your avatars." --- # Face caging best practices > **Info:** The following information applies to character creators who are looking to improve or resolve errors with their character head cages. This improves compatibility with Marketplace items, such as accessories and makeup. A character's head cage helps define the surface of the 3D model. The cage also plays an important role in Marketplace [head validation](/docs/en-us/avatar/dynamic-heads/specifications.md#facial-landmarks), so it's important properly construct cages to ensure accurate fitting with head accessories and animations. The following information and examples are best practices that are applicable to face caging as well as other caging processes. For additional best practices for caging, see [layered clothing caging best practices](/docs/en-us/avatar/layered-accessories/caging-best-practices.md). ## General _Realistic face model reference_ _Realistic face model caging reference_ ### Equidistant geometry Try to keep geometry equidistant whenever possible. This helps mitigate texture and layered clothing distortion. Keep in mind that this isn't always possible. ### Vertex colored areas The template file includes vertex colors in the cages at important areas. Lining up vertex colored areas to the lips, eyebrows, and hairline areas help ensure quality makeup transfer and additionally any hair, facial hair, eyebrows, or eyelash accessories. ### Important non-vertex colored areas Line up areas that aren't as obviously defined to the nose, eyes, and ear areas to correctly ensure quality makeup transfer and any hair, facial hair, eyebrows and eyelash accessory fit. ### Middle axis at origin Try to keep it at origin if possible. This just keeps some consistency in the middle. ### Line up neck cage part Cage parts need to line up to the render mesh objects. This doesn't have to be exact but should be close. This ensures that HSR (Hidden Surface Removal) can accurately hide the underlying surface when layered accessories are worn on top. Not something you have to worry about if using auto-setup, but may help with troubleshooting issues near the neck. Keep in mind that HSR is disabled on the head, but not on the body where the neck attaches. ### Cage should barely cover render mesh The cage needs to be close to the render mesh without actually intersecting it. Because the cage is a different topology and lower resolution than your render mesh, this can be difficult. This helps with the fit of layered accessories and with the accuracy of the makeup transfer. ## Eyes ### Establish the inner and outer eye You should have three verts for the upper eyelid and three verts for the lower eyelid, and they should be equidistant from each other. ### Enclose the cage When applicable enclose the open edge of the eye section right up to the eye mesh. ## Eyebrows It's a little easier to visualize the eyebrow shape using the vertex color provided in the template. Once you've established the head and tail of the eyebrow, you should place the three upper and lower verts equidistant from each other. ### Establish the head and tail It's a little easier to visualize the eyebrow shape because of the vertex color. Once you've established the head and tail of the eyebrow, you should place the three upper and lower verts equidistant from each other. ### Missing eyebrows For missing eyebrows, it's important to rely on basic rules of facial proportions to figure out the placement. Take in account of stylization and face shape to account for how to shape them. ### Layered clothing eyebrows For layered eyebrows try to match up the vertex colored area as best as possible to the shape of the eyebrow. ### Textured eyebrows Similar rule to layered eyebrows, try to match up the vertex colored area as best as possible to the shape of the eyebrow. Equipable eyebrows should fit right on top. ## Mouth Outline the outside and inside of the lips. Green vertex color should be on the upper lip, and purple on the lower lip with an equidistant spacing. ## Nose The bridge of the nose starts at the inner part of the eyes and ends two faces above the lips. Try to keep this generally in the area of where the nose should be. ## Ears Establish the top of the ear with the triangle portion of the ear, the bottom with the quad. Keep the shape of the hair line around the ear. ## Hairline Establish from eyebrows the forehead shape and hairline. If working with sideburns, define the shape of the sideburns in front and around the ears and consider where the hairline should end at the base of the neck. This will help determine how layered hair accessories fits to the head. ## Caging examples of other facial types ## Missing features For faces that are missing major facial features, use the following guidance. These tips may not always applicable, but they provide a good framework to understand how to cage faces with missing features. - The face is divided horizontally into three equal parts - Hairline to eyebrows - Eyebrows to the bottom of the nose - Bottom of the nose to the bottom of the chin. - Eyes are halfway down the head - Space between eyes is approximately eye width - The head is about five eyes wide - Tear ducts of the eyes line up with the edges of the nostrils - The corners of the mouth and pupils of the eyes are aligned - The bottom of the ears lines up With the bottom of the nose --- title: "FACS poses reference" url: /docs/en-us/avatar/dynamic-heads/facs-poses-reference last_updated: 2026-06-29T19:33:57Z description: "The Facial Action Coding System (FACS) ensures that all face animations have a common pose reference and can create similar face animations." --- # FACS poses reference The [Facial Action Coding System](https://en.wikipedia.org/wiki/Facial_Action_Coding_System) (FACS) is a comprehensive, anatomically-based system for describing all visually discernible facial movement. This system breaks down all facial expressions into individual types of muscle movement, such as `LeftEyeClosed` or `MouthLeft`. You can [configure](/docs/en-us/art/characters/facial-animation/create-basic-heads.md#pose) and store these muscle movements, or **poses**, within the head model through a third-party modeling software, such as [Blender](https://www.blender.org) or [Maya](https://www.autodesk.com/products/maya/overview). When you import the head model into Studio, you can then access and [animate](/docs/en-us/art/characters/facial-animation/animate-heads.md) these poses to create lively facial expressions. The following is a list of 50 base poses that you can use in Roblox to portray a wide range of face emotions. Except for the first neutral pose, the order of poses you save to your character model doesn't matter since this information is set during the [mapping](/docs/en-us/art/characters/facial-animation/create-basic-heads.md#map) process as a custom property. > **Warning:** If you intend to publish your avatar to the Marketplace, your avatar head must include the required [17 facial base poses](/docs/en-us/avatar/character-bodies/specifications.md#facial-animations). Marketplace validation rejects assets that do not include these 17 required base poses. You can combine multiple FACS base poses together in one animation frame to create complex facial expressions. However, some combinations of poses might collide unless you also add a [corrective](/docs/en-us/art/characters/facial-animation/create-basic-heads.md#combination-poses) to their full default values. ## LeftEyeClosed > **Warning:** If publishing to the Marketplace, your avatar asset must include this pose. `Class.FaceControls.LeftEyeClosed|LeftEyeClosed` closes the character's left eyelid. ## RightEyeClosed > **Warning:** If publishing to the Marketplace, your avatar asset must include this pose. `Class.FaceControls.RightEyeClosed|RightEyeClosed` closes the character's right eyelid. ## EyesLookDown > **Warning:** If publishing to the Marketplace, your avatar asset must include this pose. `Class.FaceControls.EyesLookDown|EyesLookDown` makes the eyes gaze down. ## JawDrop > **Warning:** If publishing to the Marketplace, your avatar asset must include this pose. `Class.FaceControls.JawDrop|JawDrop` lowers the jaw downward, opening the mouth. ## Pucker > **Warning:** If publishing to the Marketplace, your avatar asset must include this pose. `Class.FaceControls.Pucker|Pucker` makes a kiss-like shape with the mouth. ## LeftLipCornerPuller > **Warning:** If publishing to the Marketplace, your avatar asset must include this pose. `Class.FaceControls.LeftLipCornerPuller|LeftLipCornerPuller` raises the corners of the mouth upwards in a smile. ## RightLipCornerPuller > **Warning:** If publishing to the Marketplace, your avatar asset must include this pose. `Class.FaceControls.RightLipCornerPuller|RightLipCornerPuller` raises the corners of the mouth upwards in a smile. ## ChinRaiser > **Warning:** If publishing to the Marketplace, your avatar asset must include this pose. `Class.FaceControls.ChinRaiser|ChinRaiser` raises the chin up; moves the lower lip upwards. When you use `Class.FaceControls.ChinRaiser|ChinRaiser` and the character's mouth is closed, the character's lower lip collides with their upper lip. When you need to keep the mouth closed while raising the chin, use [`ChinRaiserUpperLip`](#chinraiserupperlip) and `Class.FaceControls.ChinRaiser|ChinRaiser` together to raise both the lower and upper lip, and avoid a collision. Note that if you use this method, you must set both `Class.FaceControls.ChinRaiser|ChinRaiser` and [`ChinRaiserUpperLip`](#chinraiserupperlip) to the same value so the lips move the same distance upwards. ## ChinRaiserUpperLip > **Warning:** If publishing to the Marketplace, your avatar asset must include this pose. `Class.FaceControls.ChinRaiserUpperLip|ChinRaiserUpperLip` raises the upper lip up when ChinRaiser is engaged and it is touching the upper lip. ## LeftCheekRaiser > **Warning:** If publishing to the Marketplace, your avatar asset must include this pose. `Class.FaceControls.LeftCheekRaiser|LeftCheekRaiser` squints the character's left eye. ## RightCheekRaiser > **Warning:** If publishing to the Marketplace, your avatar asset must include this pose. `Class.FaceControls.RightCheekRaiser|RightCheekRaiser` squints the character's right eye. ## LeftInnerBrowRaiser > **Warning:** If publishing to the Marketplace, your avatar asset must include this pose. `Class.FaceControls.LeftInnerBrowRaiser|LeftInnerBrowRaiser` raises the interior half of the character's left brow upwards. ## RightInnerBrowRaiser > **Warning:** If publishing to the Marketplace, your avatar asset must include this pose. `Class.FaceControls.RightInnerBrowRaiser|RightInnerBrowRaiser` raises the interior half of the character's right brow upwards. ## LeftLipCornerDown > **Warning:** If publishing to the Marketplace, your avatar asset must include this pose. `Class.FaceControls.LeftLipCornerDown|LeftLipCornerDown` lowers the corners of the mouth downwards in a frown. ## RightLipCornerDown > **Warning:** If publishing to the Marketplace, your avatar asset must include this pose. `Class.FaceControls.RightLipCornerDown|RightLipCornerDown` lowers the corners of the mouth downwards in a frown. ## LeftLowerLipDepressor > **Warning:** If publishing to the Marketplace, your avatar asset must include this pose. `Class.FaceControls.LeftLowerLipDepressor|LeftLowerLipDepressor` lowers the lower lip downwards away from the upper lip, revealing the lower teeth. ## RightLowerLipDepressor > **Warning:** If publishing to the Marketplace, your avatar asset must include this pose. `Class.FaceControls.RightLowerLipDepressor|RightLowerLipDepressor` lowers the lower lip down away from the upper lip revealing the lower teeth. ## EyesLookLeft `Class.FaceControls.EyesLookLeft|EyesLookLeft` makes the eyes gaze left. ## EyesLookRight `Class.FaceControls.EyesLookRight|EyesLookRight` makes the eyes gaze right. ## EyesLookUp `Class.FaceControls.EyesLookUp|EyesLookUp` makes the eyes gaze up. ## LeftLipStretcher `Class.FaceControls.LeftLipStretcher|LeftLipStretcher` stretches the corners of the mouth apart. ## LeftUpperLipRaiser `Class.FaceControls.LeftUpperLipRaiser|LeftUpperLipRaiser` raises the character's left upper lip away from the lower lip revealing the upper teeth. ## LipsTogether `Class.FaceControls.LipsTogether|LipsTogether` brings the character's lips together. However, if the character's mouth is closed, their lips are already together and they collide. This pose's main use case is in connection with the [`JawDrop`](#jawdrop) property. If you set [`JawDrop`](#jawdrop) and `Class.FaceControls.LipsTogether|LipsTogether` to 100% of their default values, the character's lips stay together as the jaw drops. In addition, if you set [`JawDrop`](#jawdrop) to 100% of its default value, and ease `Class.FaceControls.LipsTogether|LipsTogether` in and out of 100% of its default value, the character's lips open and close. ## RightLipStretcher `Class.FaceControls.RightLipStretcher|RightLipStretcher` stretches the corners of the mouth apart. ## RightUpperLipRaiser `Class.FaceControls.RightUpperLipRaiser|RightUpperLipRaiser` raises the right upper lip away from the lower lip, revealing the upper teeth. ## FlatPucker `Class.FaceControls.FlatPucker|FlatPucker` brings the corners of the mouth inward and presses the lips back against the teeth. This pose is also known as lip tightener. ## Funneler `Class.FaceControls.Funneler|Funneler` makes an 'O' shape with the mouth. ## LowerLipSuck `Class.FaceControls.LowerLipSuck|LowerLipSuck` rolls the lower lip up over the teeth. ## LipPresser `Class.FaceControls.LipPresser|LipPresser` presses the lips together. ## MouthLeft `Class.FaceControls.MouthLeft|MouthLeft` moves the mouth to the character's left. This property doesn't move the character's jaw, teeth, or tongue, only their mouth. If you want to move those additional facial features, use [`JawLeft`](#jawleft) instead. ## MouthRight `Class.FaceControls.MouthRight|MouthRight` moves the mouth to the character's right. This property doesn't move the character's jaw, teeth, or tongue to the right, only their mouth. If you want to move those additional facial features, use [`JawRight`](#jawright) instead. ## UpperLipSuck `Class.FaceControls.UpperLipSuck|UpperLipSuck` rolls the upper lip around the teeth. ## LeftCheekPuff `Class.FaceControls.LeftCheekPuff|LeftCheekPuff` puffs up the character's left cheek. ## LeftDimpler `Class.FaceControls.LeftDimpler|LeftDimpler` moves the corners of the left side of the mouth back toward the teeth. ## RightCheekPuff `Class.FaceControls.RightCheekPuff|RightCheekPuff` puffs up the character's right cheek. ## RightDimpler `Class.FaceControls.RightDimpler|RightDimpler` moves the corners of the right side of the mouth back toward the teeth. ## JawLeft `Class.FaceControls.JawLeft|JawLeft` moves the character's jaw, teeth, tongue, and lower lip to their left. If you just want to move the character's mouth to their left, use [`MouthLeft`](#mouthleft) instead. ## JawRight `Class.FaceControls.JawRight|JawRight` moves the character's jaw, teeth, tongue, and lower lip to their right. If you just want to move the character's mouth to their right, use [`MouthRight`](#mouthright) instead. ## Corrugator `Class.FaceControls.Corrugator|Corrugator` brings the left and right brows inward together. ## LeftBrowLowerer `Class.FaceControls.LeftBrowLowerer|LeftBrowLowerer` lowers the character's left brow down. ## LeftOuterBrowRaiser `Class.FaceControls.LeftOuterBrowRaiser|LeftOuterBrowRaiser` raises the outer part of the character's left brow upwards. ## LeftNoseWrinkler `Class.FaceControls.LeftNoseWrinkler|LeftNoseWrinkler` raises the character's left nostril, pulls the brow down slightly, and wrinkles the left side of the nose. ## RightBrowLowerer `Class.FaceControls.RightBrowLowerer|RightBrowLowerer` lowers the character's right brow down. ## RightOuterBrowRaiser `Class.FaceControls.RightOuterBrowRaiser|RightOuterBrowRaiser` raises the outer part of the character's right brow upwards. ## RightNoseWrinkler `Class.FaceControls.RightNoseWrinkler|RightNoseWrinkler` raises the character's right nostril, pulls the brow down slightly, and wrinkles the right side of the nose. ## LeftEyeUpperLidRaiser `Class.FaceControls.LeftEyeUpperLidRaiser|LeftEyeUpperLidRaiser` raises the character's left eyelid upwards to reveal more of the eye white above the iris. ## RightEyeUpperLidRaiser `Class.FaceControls.RightEyeUpperLidRaiser|RightEyeUpperLidRaiser` raises the character's right eyelid upwards to reveal more of the eye white above the iris. ## TongueDown `Class.FaceControls.TongueDown|TongueDown` bends the tongue down. This pose is only visible if you combine it with [`TongueOut`](#tongueout), otherwise the tongue only bends down within the character's mouth. ## TongueOut `Class.FaceControls.TongueOut|TongueOut` sticks the tip of the tongue out of the mouth. ## TongueUp `Class.FaceControls.TongueUp|TongueUp` bends the tongue up. This pose is only visible if you combine it with [`TongueOut`](#tongueout), otherwise the tongue only bends up within the character's mouth. --- title: "Heads" url: /docs/en-us/avatar/dynamic-heads last_updated: 2026-06-29T19:33:57Z description: "Facial animations enable avatars to create dynamic facial expressions." --- # Heads Heads are a fundamental component of an avatar character body. Roblox avatar heads, such as those available on the Marketplace, contains components that allow you to: - Set default custom facial expressions. - Trigger emotes that combine both face and body animations. - Equip face accessories that deform with your facial expressions. For example, facial hair moves accordingly when the character changes facial expressions. ## Create basic heads > **Warning:** When creating heads intended for the Marketplace, ensure that your head meets Roblox's [head specifications](/docs/en-us/avatar/specifications.md). Using third-party modeling tools, you can either modify a rigged character model to meet head requirements, or create a head from scratch. For step-by-step instructions on how to add face bones, apply skinning, posing, and mapping poses on a head model in Blender, see [Create basic heads](/docs/en-us/art/characters/facial-animation/create-basic-heads.md). This guide includes several reference files you can use to compare and track your progress. ### Resources Heads designated for the Marketplace require specific components to ensure universal support across Roblox's platform. Heads are often the most complex part of an avatar character. Use the following docs to best equip your assets to pass validation and work well with other assets: - Understand how Roblox tests facial animations in the [validation process](/docs/en-us/avatar/validate.md) and learn common issues that you can self-resolve. - Similar to character bodies and clothing, Marketplace heads must also follow [Roblox's specifications](/docs/en-us/avatar/specifications.md). - Ensure that you are following Roblox's [head caging best practices](/docs/en-us/avatar/caging-best-practices.md) to ensure the best results for clothing, validation, and makeup. - Use the [FACS poses reference](/docs/en-us/avatar/facs-poses-reference.md) when posing and mapping your head. ## Create face accessories Similar to creating heads, you can design and model face accessories using a third-party modeling software and import the models as a cosmetic `Class.Accessory` in Studio. When created correctly, equipped accessories stretch and deform as the head animates and emotes. See [Create face accessories](/docs/en-us/art/characters/facial-animation/create-face-accessories.md) for step-by-step instructions on setting up a reference model as a face accessory. This guide includes several project files you can use for reference and covers details on time-saving steps, such as auto-skin transfer. ## Test heads Testing is an important part of the iterative creation process. To test heads in Studio, see [Testing in Studio](/docs/en-us/avatar/test.md). ## Animate heads If you're interested in animating a head, for use in-experience or to further test your character's face rig, see [Animate heads](/docs/en-us/art/characters/facial-animation/animate-heads.md). --- title: "Head specifications" url: /docs/en-us/avatar/dynamic-heads/specifications last_updated: 2026-06-29T19:33:57Z description: "In-depth details on expected head schemas for Marketplace" --- # Head specifications > **Info:** This content is for creators who intend to create character heads designated for the Marketplace, or any character heads that support assets from the Marketplace. Character models designed to be sold on the Marketplace require a specific set of components and configuration standards to ensure all avatar features work as expected. The following guidance applies to character model heads to ensure universal support for head accessories and facial animations. > **Warning:** Validation for heads use a specialized process that may require adjustment of your existing head asset. For more information, see [validation process](/docs/en-us/avatar/dynamic-heads/validate.md). > **Info:** **Avatar Setup recommendation** > > [Avatar Setup](/docs/en-us/avatar-setup.md) is an automated tool that automatically generates components for your avatar character, **including the head cage and facial animation** required for Marketplace character models. You can save time by submitting character models without avatar components into the Avatar Setup tool and modifying the output. > > You can test and refine your Avatar Setup output by using the Avatar Setup tool previews. You can even download your output as a `.gltf` and adjust the FACS or cage data in your DCC tools. > > You can alternatively manually modify your own cages and FACS animations within your DCC tools, which may require more time or additional iteration in your workflow, such as weight-painting adjustments, to ensure it meets Marketplace policies. > > To submit issues or feedback regarding the Avatar Setup tool, submit a [bug report](https://devforum.roblox.com/c/bug-reports/10). ## Head cage > **Warning:** The head cage is used to verify animations during validation. see [validation process](/docs/en-us/avatar/dynamic-heads/validate.md). Similar to other avatar body parts, an outer cage is required for the head. [High-quality cages](#caging-quality) ensure accessories and clothing items fit properly over the head. Head cages also serves to establish [facial landmarks](#facial-landmarks) for the eyes and mouth of the face for animation validation. ### Caging quality For details on improving the quality of your head cage, see [Face cage optimizations](/docs/en-us/avatar/dynamic-heads/caging-best-practices.md). ### Facial landmarks During validation, Roblox projects the cage eye/mouth regions to the base mesh to identify the eyes and mouth regions of your character head. Even for non-humanoid faces that don't include visual eyes or mouths, it's important to ensure that these landmarks still exist and can be projected over your base mesh. For more information on this process, see [validation process](/docs/en-us/avatar/dynamic-heads/validate.md). Roblox expects the following 3 distinct landmarks in your cages: - Left eye landmark - Right eye landmark - Mouth landmark _ Example face cage UV_ _ Highlighted vertices of cage are used to track eyes/mouth on base mesh_ _ Landmarks from cage applied to base mesh, used as an indicator for basic animation support during validation_ > **Error:** The mouth landmark vertices are on the **second loop** of vertices from the mouth opening. This precision may help prevent issues with [landmark validation](/docs/en-us/avatar/dynamic-heads/validate.md#landmark-projection). ## FACS animation Roblox supports facial animation on character heads and can support more than 50 base poses. If you are creating an avatar character, it must, at minimum, include the following 17 [FACS reference poses](/docs/en-us/avatar/dynamic-heads/facs-poses-reference.md) to support avatar chat: - LeftEyeClosed - RightEyeClosed - EyesLookDown - JawDrop - Pucker - LeftLipCornerPuller - RightLipCornerPuller - ChinRaiser - ChinRaiserUpperLip - LeftCheekRaiser - RightCheekRaiser - LeftInnerBrowRaiser - RightInnerBrowRaiser - LeftLipCornerDown - RightLipCornerDown - LeftLowerLipDepressor - RightLowerLipDepressor While the 17 poses are a minimum requirement, it's recommended to include as many facial poses as possible in your asset to improve expressiveness and facial animation fidelity for facial animation and avatar chat. To [pass validation](/docs/en-us/avatar/dynamic-heads/validate.md), the character head must successfully perform the following 5 facial actions. If the head can't deform at the points of the [cage landmarks](#head-cage) for these facial actions, validation fails for the asset. | Action Unit Test | Non-Zero Control Values | | --- | --- | | Left eye blink | LeftEyeClosed = `1`
EyesLookDown = `1` | | Right eye blink | RightEyeClosed = `1`
EyesLookDown = `1` | | Mouth opens | JawDrop = `1` | | Happy expression | Pucker = `1`
LeftLipCornerPuller = `1`
RightLipCornerPuller = `1` | | Sad expression | ChinRaiser = `1`
ChinRaiserUpperLip = `1`
LeftCheekRaiser = `0.85`
RightCheekRaiser = `0.85`
LeftInnerBrowRaiser = `1`
RightInnerBrowRaiser = `1`
LeftLipCornerDown = `1`
RightLipCornerDown = `1`
LeftLowerLipDepressor = `1`
RightLowerLipDepressor = `1` | ## Marketplace requirements Along with the other technical requirements listed, your items must meet the following additional specifications before uploading them to the Marketplace to sell: - Ensure that your items adhere to the [Marketplace program guidelines](/docs/en-us/marketplace/marketplace-policy.md). - Whenever possible, ensure that your items adhere to the following modeling requirements: - [Custom mesh specifications](/docs/en-us/art/modeling/specifications.md) - Any applicable [avatar specifications](/docs/en-us/avatar/character-bodies/specifications.md) - Object `Class.MeshPart.Material|Material` is set to `Plastic`. - Object `Class.MeshPart.Transparency|Transparency` is set to `0`. - Object `Class.MeshPart.VertexColor|VertexColor` is the default `1, 1, 1`. - If your head includes separate eyelash and eyebrow assets, you must add them to your character model as `Class.Accessory` objects. - See [accessory specifications](/docs/en-us/avatar/rigid-accessories.md) for additional technical requirements. - Your `Class.Model` instance doesn't contain extraneous objects, like `Class.Script` or additional `Class.Part` instances.
--- title: "Test heads in Studio" url: /docs/en-us/avatar/dynamic-heads/test last_updated: 2026-06-29T19:33:57Z description: "If you have a model with a live head, you can import the model into Studio, equip face accessories, and create and save animations." --- # Test heads in Studio > **Info:** If you are looking to quickly test a character's head animation, use the [Avatar Setup tool](/docs/en-us/avatar-setup.md) to load your character and use the presets to preview various facial animations and head accessories. Manually test your head creations by importing character models with animatable heads into Studio and use the generated `Class.FaceControls` instance to pose or animate the face. When importing a head that supports facial animation into Studio, Studio creates a `Class.FaceControls` instance you can use to access and combine these individual poses to create expressions and animations. Unlike typical rigged models, you cannot access the facial `Class.Bone|Bones` of a head mesh directly in Studio. To set up heads with facial animation in your experience: 1. [Import a model with an animatable head](/docs/en-us/art/characters/testing/studio.md#import). You can either create your own or use one of the provided reference model files. 2. (Optional) [Import face accessories](#import-face-accessories) you want to deform with the facial expressions of your head. You can either create your own or use one of the provided reference accessory files. 3. [Animate the head](#animate-heads) in the **Animation Editor** by either adding in individual animation tracks, or by using the **Face Animation Editor**. If you want to experiment with pre-made heads before [making your own](/docs/en-us/art/characters/facial-animation/create-basic-heads.md), Roblox has a reference experience you can access to see how heads interact with Studio's interface, as well as two reference models and accessories you can import directly into your own experience: | Reference files | Description | | --- | --- | | [Head Animation In-Experience Editor](../../../assets/avatar/dynamic-heads/reference-files/Dynamic-Head-Editor.rbxl) | A reference experience that allows you to preview a Goblin and Blocky head with various accessories using an editor. | | [Head Schema Reference](../../../assets/avatar/dynamic-heads/reference-files/Dynamic-Head-Schema.rbxl) | A reference experience with a head Blocky model with `Class.FaceControls`. | | [Goblin Character Model](../../../assets/avatar/dynamic-heads/reference-files/GoblinCharacter.zip) | A Goblin character model with an animatable head. | | [Blocky Character Model](../../../assets/avatar/dynamic-heads/reference-files/BlockyCharacter.fbx) | A Blocky character model with an animatable head. | | [Blocky Face Accessories and Clothing](../../../assets/avatar/dynamic-heads/reference-files/Blocky-Face-Accessories.zip) | A `.zip` file of Blocky-specific accessories, including: | ## Import face accessories You can import and equip face accessories that you want to deform with the facial expressions of your head. For example, when you import and equip eyebrows as a face accessory, you can animate the eyebrows to move with the character's eyes. By default, when you import a face accessory, it imports as a `Class.MeshPart` object. Using the [Accessory Fitting Tool](/docs/en-us/avatar/accessory-fitting-tool.md) (AFT), you can convert the `Class.MeshPart` into an `Class.Accessory` instance that is compatible with the head. To import a face accessory: 1. From Studio's **File** menu, use the [Importer](/docs/en-us/studio/importer.md) to import the custom model into the workspace. 2. Using the AFT, convert the `Class.MeshPart` into an `Class.Accessory` instance. 1. In the toolbar's **Avatar** tab, click **Accessory** to open the AFT. 2. In the **Explorer** window, select the **MeshPart** you imported as a face accessory. Its name displays in the **Part** field. 3. Click the **Next** button. 4. In the **Clothing** category, select **Face**, then click the **Next** button. 5. (Optional) In the **Edit** panel, use the adjustment options to ensure the accessory fits your model. 6. In the **Preview** panel, preview the new facial accessory on your head, then click the **Next** button. The tool transforms the `Class.MeshPart` into an `Class.Accessory` instance, and it displays in the **Explorer** window. You can preview the accessory on your imported model by making the `Class.Accessory` instance a child of your character `Class.Model`. You can only equip character-specific face accessories with their designed character. _Equipped accessory in viewport_ _Equipped Accessory in Explorer_ You can also save the `Class.Accessory` instance to your toolbox and use the asset ID at any time in your experiences. For information on equipping accessories by applying a `Class.HumanoidDescription`, see [Customize characters with humanoid description](/docs/en-us/characters/appearance.md#set-multiple-accessories). ### AutoSkin property You can enable the `Class.WrapLayer.AutoSkin` property in the accessory's `Class.WrapLayer` instance to apply the skinning of the head to the face accessory. This allows a face accessory to fit and follow a character's expressions without having to apply any skinning influences to it during the 3D modeling process. The following options are available for the `Class.WrapLayer.AutoSkin` property: - **Disabled**: Disables the Automatic Skinning Transfer process. This is the default value. - **EnabledOverride**: Enables the Auto-Skin Transfer process and allows Studio to override any existing skinning information found on the accessory. Choose this setting when you want to set or replace the existing skinning of an accessory using the Auto-Skin Transfer version. - **EnabledPreserve**: Enables the Automatic Skinning Transfer process but **doesn't** allow it to override any existing skinning information found on the accessory. Choose this setting when you want to preserve and maintain any existing skinning of an accessory. If there isn't any skinning to maintain, the Auto-Skin Transfer creates new skinning automatically. ## Animate heads Animatable head `Class.MeshPart|MeshParts` include a `Class.FaceControls` instance which allows you to access your facial pose properties. _FaceControls in Explorer_ _FaceControls Property Panel_ You can adjust these properties in the **Animation Editor** to animate your head. For more information, see [Animate heads](/docs/en-us/art/characters/facial-animation/animate-heads.md). ## Troubleshooting When importing custom head models, the [Output window](/docs/en-us/studio/output.md) displays an error or warning message if there were any issues during the configuration process. ### Error messages Error messages indicate a failure to properly import a model with a head. Reference the following table for a summary of all head error messages and troubleshooting tips: | Error message | Troubleshooting | | --- | --- | | Failed importing head with facial animation. | The `.fbx` file is corrupted. | | Found more than one joint named X. All joints must have unique names. | Ensure that every joint that you import has a unique name. | | Failed importing head with facial animation: Correctives can be for at most 3 control names. | Studio found a corrective with more than 3 corrective poses. This isn't supported, so you need to delete the corrective. | | Failed importing head with facial animation: Unrecognized FACS control name X. | X is an unrecognized FACS pose name. Ensure that X is renamed to one of the [standard FACS poses](/docs/en-us/avatar/dynamic-heads/facs-poses-reference.md). | | Failed importing head with facial animation: Invalid corrective name X: Neutral cannot be part of a corrective. | X is an invalid corrective name. Ensure you are following the correct naming format for correctives. | | Failed importing head with facial animation: Found duplicate FACS control name X for corrective Y. | X is an invalid corrective name. You can only include a base pose that is listed as part of a corrective once in the corrective name. | | Failed importing head with facial animation: Couldn't parse the frame number in Extra Attribute named X. | You must name any extra attributes "Frame0", "Frame1", etc. | | Failed importing head with facial animation: Empty value found for Extra Attribute named X. | The extra attribute X has no value. Add a base pose name or corrective, or delete the extra attribute. | | Failed importing head with facial animation: Duplicate control name found. | You set a FACS pose or corrective to multiple frames. Ensure that each FACS pose or corrective is set to only a single frame. | | Failed importing head with facial animation: Head_Geo has no Extra Attributes that map FACS controls to animation frames. | The "Head_Geo" mesh in the FBX file must have extra attributes in order to animate facial expressions. The imported avatar will not animate facial expressions. | | Failed importing head with facial animation: No frame was set to "Neutral" in animation. | Must have one frame set to "Neutral" in the animation timeline. | | Failed importing head with facial animation: X has not been specified. This is needed for corrective Y. | All base poses for a corrective must be set in the extra attributes. For example, if the `Funneler_JawDrop` corrective is set, then there **must** be poses for `Funneler` and `JawDrop` alone. | | Failed importing head with facial animation: Missing key frame X for Y. | There is an extra attribute "FrameX" set to Y, but the animation timeline has no frame X. | | Failed importing head with facial animation: No joints were found under DynamicHead. | The `.fbx` file doesn't have any joints under the RootFaceJoint joint. | | Could not load FACS control data. | There are corrupted joint parameters in the `.fbx` file. | ### Warning messages Warning messages indicate a potential issue with an imported head model. Reference the following table for a summary of all head warning messages and troubleshooting tips: | Error message | Troubleshooting | | --- | --- | | You have corrective X, but missing corrective Y to support it. This may cause odd deformations. | You set a 3-corrective without setting the base 2-corrective. The imported head will animate, but there might be strange deformations. | | Skipping attribute X: Y | Studio found an extra attribute named X, but since it didn't match the format of "Frame#", it was ignored. | | Control X is nearly identical to Neutral. | The pose for X is very similar to Neutral. Studio reports this in case this was a mistake, but the import will be successful as long as there are no other failures. | | Found only N base poses. Your character may not be able to express itself. | You only defined a subset of base poses in the `.fbx` file. The import will still be successful as long as there are no other failures. | --- title: "Head validation" url: /docs/en-us/avatar/dynamic-heads/validate last_updated: 2026-06-29T19:33:57Z description: "In-depth details on how Roblox validates head assets" --- # Head validation > **Info:** This content is for creators who intend to create character heads designated for the Marketplace, or any character heads that support assets from the Marketplace. Roblox runs a rigorous validation to ensure the asset being uploaded to the Marketplace is compatible with all other assets on the Marketplace. For heads, validation primarily ensures that the head asset includes: - A correctly **configured cage** — to wear clothing and accessories. - Correctly configured **facial animation poses** — to perform basic universal expressions, such as blinking and smiling. The primary validation check for heads [uses the cage to detect facial animation](#facial-animation-detection). See below for a reference of how validation processes, as well as [common issues](#common-issues). > **Warning:** Validation also checks for other issues, such as poor caging or missing required FACS poses. For more information, see [common issues](#common-issues). > **Info:** **Avatar Setup recommendation** > > [Avatar Setup](/docs/en-us/avatar-setup.md) is an automated tool that automatically generates components for your avatar character, **including the head cage and facial animation** required for Marketplace character models. You can save time by submitting character models without avatar components into the Avatar Setup tool and modifying the output. > > You can test and refine your Avatar Setup output by using the Avatar setup tool previews. You can even download your output as a `.gltf` and adjust the FACS or cage data in your DCC tools. > > You can alternatively manually modify your own cages and FACS animations within your DCC tools, which may require more time or additional iteration in your workflow, such as weight-painting adjustments, to ensure it meets Marketplace policies. > > To submit issues or feedback regarding the avatar auto setup tool, submit a [bug report](https://devforum.roblox.com/c/bug-reports/10). ## Facial animation detection One of the primary dynamic head validation checks verifies if your head asset has **visually discernable** animations. To achieve this, the validation tool uses the head cage to ["project" landmarks](#landmark-projection) onto your base mesh. After these landmarks created, validation [tests various face animation poses](#facial-animation-detection) to ensure that these landmarks move and deform appropriately. Validation succeeds when it detects appropriate movement of the landmarks for eyes closed, mouth open, and basic happy/sad expressions. Failure to either assign landmarks onto the base mesh, or failure to deform landmarks appropriately can occur for several reasons. For more information, see [common issues](#common-issues). ### Landmark projection Validation uses the head cage to project landmarks onto the head mesh. This helps Roblox establish where the eyes and mouth are and helps guide the facial animation test. _Cage mesh: Specific vertices of the cage mark eyes and mouth._ _Roblox validation projects these points onto the head mesh._ _ Head mesh: All landmarks projected from cage mesh to head mesh._ > **Success:** Once validation verifies that projection is successful and all landmarks established onto the head mesh, it can move onto testing facial animations. ### Test face animations Now that the validation system knows where to check for the eyes and mouth, the validation checks for facial animations to ensure that the following basic actions are possible: - Left and right eyes can close - Mouth can open - Basic happy and sad expressions - For a full list of basic action units and poses, see [FACS animations](/docs/en-us/avatar/dynamic-heads/specifications.md#facs-animation). The validation looks for the **relative change in the landmarks**. For example, when testing for right eye closing (FACS pose: `RightEyeClosed`), validation checks if the landmarks move from their original neutral state accordingly. _> **Success:** Face mesh with all projected landmarks applied from cage._ _> **Success:** Validation verifies that the right eye can close based off of displacement of landmarks._ ## Common issues Validation checks for several aspects of your head asset. If you are receiving an error related to dynamic head animation, use the following table for potential culprits: | Issue | Potential fixes | | --- | --- | | The validation tool is **unable to successfully apply landmarks**.

See [Unable to project landmarks](#unable-to-project-landmarks). | - Verify that the **cage landmark vertices are positioned directly over the corresponding areas of the face**.

- Verify that the **cage is not intersecting with the head mesh**. Landmark projection expects the cage to be over the head mesh.

- **Check for any gaps, holes, or difficult surfaces** of the face where landmarks can fail to project. | | The validation tool is able to apply landmarks, but **can not detect animation**.

See [Animations not displacing landmarks](#animations-not-displacing-landmarks). | - Verify that **landmarks are on the areas of the eyes and mouth that move during animation**.

- Verify that the **cage is not intersecting with the head mesh**. Landmark projection expects the cage to be over the head mesh.

- **If using separate meshes for eyeballs and mouthbags**, ensure that your landmarks are applied to the head mesh, not the eyeballs or mouthbag. This can be achieved by slightly widening the area where your cages project landmarks. | | The validation tool doesn't detect [FACS animations](#animation-data-missing). | - Your head doesn't include FACS animation. Verify that your head includes FACS poses and mapping. | ### Unable to project landmarks If validation is unable to apply the projection correctly onto the surface of the head, the validation fails. This often occurs if your **cage extends beyond your head mesh**, or if your head mesh has a **hole or gap** that prevents the application of the landmark. See the following examples: _> **Error:** Cage **extends too far beyond the mesh** and the validation is unable to apply 3 landmarks to the base mesh._ _> **Error:** Base mesh has a **hollow gap** in the design, preventing validation from applying 3 landmarks to denote the eye area._ > **Success:** In both cases, adjusting the cage to allow the proper projection of landmarks to the base mesh would resolve this issue. ### Animations not displacing landmarks In the case where landmarks are correctly applied to the base mesh and the base mesh includes FACS data, it's possible that the facial animations do not displace the landmarks enough to pass validation. See the following example: _> **Warning:** Landmarks successfully established on base mesh, but are not near the expected regions._ _> **Error:** When testing animations, validation doesn't detect the expected change to the landmark regions._ > **Success:** In the example case, adjusting the cage to allow the proper projection of landmarks at the appropriate areas would resolve this issue. There may also be cases where the landmarks are correct, but the animation itself does not alter the projected landscapes enough to pass validation. In these cases, fixing the animation and/or optimizing the cage could help the validation properly identify the facial animation. ### Animation data missing Heads require [17 specific FACS poses](/docs/en-us/avatar/dynamic-heads/specifications.md#facs-animation) that are used to test the 5 basic actions. Regardless of landmarks or other FACS animations, validation immediately fails if the minimum 17 poses were not detected. For information on how to pose and map dynamic heads, see [Creating basic heads](/docs/en-us/art/characters/facial-animation/create-basic-heads.md). ## Working with non-humanoids Some character models, typically non-humanoids, have unnatural or missing eyes and mouths. The most common case of this is the classic blocky features. In this case, the cage still applies the expected landscapes to the head surface, and Roblox validates if facial animations correctly displace the facial animations. _The cage for a blocky character applies landmarks to the surface of the head._ _Ensure that the landmarks are applied in areas where animation can effectively move the landmarks._ _Validation checks the displacement of the landmarks to verify facial animation. Make sure that your [FACS animations](/docs/en-us/avatar/dynamic-heads/specifications.md#facs-animation) properly affect the region where landmarks are applied._ In cases where eyes and mouths might be missing, the validation still projects landmarks and expects facial animation in those areas. _Although this character doesn't have eyes or mouth, the cage still attempts to project landmarks for those features._ _In this example, the cage landmarks apply to the surface of this eye-less character head._ _Validation checks the displacement of the landmarks to verify facial animation. Make sure that your [FACS animations](/docs/en-us/avatar/dynamic-heads/specifications.md#facs-animation) properly affect the regions where the landmarks are applied._
--- title: "Animation export settings" url: /docs/en-us/avatar/emotes/export last_updated: 2026-06-29T19:33:57Z description: "Use the appropriate export settings in Maya and Blender to generate Studio-ready .fbx files." --- # Animation export settings You can export animation data from a third-party software, such as Blender or Maya to use in Studio. Both `.fbx` and `.gLTF` formats support animation data. Typically, creators will use a third-party modeling tool to animate a character model. Once the animation is complete, you export both the model and the animation data into Roblox Studio. After exporting, you can [import and configure](/docs/en-us/avatar/emotes/import.md) the animation data into Studio. ## Before exporting When exporting characters with animation or FACS animation, ensure that your animation timeline **Start** and **End** include the entire range of your animations. ![Zoom-in of Blender animation track indicating a Start value of 0 and End value of 308.](../../assets/art/avatar/basic-creation/Blender-Animation-Start-End.png) ## Blender Blender allows you to export in `.fbx` or `.gltf` as well as other formats. If you are using `.fbx` export, familiarize yourself with [Blender's FBX scaling](/docs/en-us/art/blender.md#adjust-scale-fbx) to ensure that you successfully import the model into Studio at the correct scale. 1. In the topbar, click **File** > **Export** > **FBX (.fbx)**. The Blender file browser window displays. 2. Set **Path Mode** to **Copy** and enable the **Embed Textures** icon. 3. In the Include section, enable **Custom Properties**. 4. Set the **Transform** > **Apply Scalings** to **FBX Unit Scale**. If you run into scaling issues on import, see [Blender FBX scaling](/docs/en-us/art/blender.md#adjust-scale-fbx) for alternative approaches. 5. Expand the Armature section and uncheck **Add Leaf Bones**. 6. Enable **Bake Animation**. 7. Expand Bake Animation and **uncheck NLA Strips**, **All Actions**, and **Force Start/End Keyframes**. 1. Ensure your project animation timeline has the correct **Start** and **End** range of all your keyframes. 8. In Bake Animation, set **Simplify** to **0.0**. 9. Click the **Export FBX** button. Save the `.fbx` to the directory of your choice. ![Screenshot of Blender export settings](../../assets/art/avatar/basic-creation/Export-Settings.png) ## Maya For additional context on exporting animations from Maya, see the [Exporting Characters from Maya](/docs/en-us/art/characters/export-avatar-animations-from-maya.md) tutorial. To export a mesh in Maya as a `.fbx` file: 1. In the topbar, click **File**. A pop-up menu displays. 2. Select **Export All**. The **Export All** window displays. 3. Near the bottom of the window, click the **Files of type** dropdown, then select **FBX export**. 4. On the right-hand side of the window, navigate to the **Options...** section. 5. In the **Geometry** section, enable **Smooth Mesh** and **Referenced Asset Content**. 6. In the **Animation** section, enable **Animation**. Avatar characters with [facial animation data](/docs/en-us/avatar/dynamic-heads.md) require animation data. 7. Enable **Bake Animation**. 8. If you need to import textures as a `.png`, in the **Embed Media** section, enable **Embed Media**. 9. In the **Advanced Options** section, - Navigate to **Units**, then enable **Automatic**. - Navigate to **Axis Conversion**, then set the **Up Axis** property to **Y**. 10. Click the **Export All** button.![Screenshot of Maya export settings for exports with animation.](../../assets/accessories/lc-requirements-maya-settings-with-animation.png) 11. After exporting, use Studio's [Importer](/docs/en-us/studio/importer.md) to import your model. See [Test characters in Studio](/docs/en-us/art/characters/testing/studio.md) for additional importing and testing information. --- title: "Import and configure animations" url: /docs/en-us/avatar/emotes/import last_updated: 2026-06-29T19:33:57Z description: "Use the Importer to add third-party models to Studio before testing, using, or uploading the character model." --- # Import and configure animations Use the following steps to import and configure the animation for publishing emotes: 1. [Import](#import-into-animation-editor) the animation into Studio. 2. [Convert](#convert-to-curveanimation) the animation to a `CurveAnimation`. 3. [Generate an asset ID](#generate-asset-id) from your animation data. 4. [Create an Animation object](#create-an-animation-object) using the asset ID 5. [Upload](#upload-and-publish-an-emote) the animation as an avatar emote. ## Import into Animation Editor If your animation data is saved to a `.fbx` file and your animation meets the emote [technical specifications](/docs/en-us/avatar/emotes/specifications.md), use the **Animation Editor** to import your animation. If your animation is already in Studio, skip this step. To import your animation in Studio: 1. If you don't already have a rigged R15 character in your workspace, add one by selecting **Avatar** ⟩ **Character**. 2. Select **Avatar** ⟩ **Clip Editor** to open the **Animation Editor**. 3. With editor open, select the rigged character in the 3D viewport. 4. In the **Animation Editor**, select **⋯** > **Import** > **From FBX Animation** and select your `.fbx` file. 5. Verify your animation populates and correctly animates on the target rig. 6. Follow steps to [convert to CurveAnimation](#convert-to-curveanimation). ## Convert to CurveAnimation To generate an asset ID for your animation, use the **Animation Editor** to convert the animation to a `CurveAnimation` and save the animation to Roblox to generate an asset ID. > **Warning:** The animation data for emotes must be a `CurveAnimation` clip. To convert an existing keyframe sequence to a `CurveAnimation`: 1. In the **Animation Editor**, select the **Curve Editor** button next to the timeline. A confirmation prompt displays. 2. Press **Confirm** to convert your keyframes into a `CurveAnimation` clip. ## Generate Asset ID To create an asset ID of the `CurveAnimation`: 1. In the **Animation Editor**, select the **⋯** > **Publish to Roblox**. 2. Add a title and description and save the animation. 3. After processing, you should see a successful upload and an asset ID to copy. ## Create an Animation object To submit an emote to the Marketplace, you must submit an `Class.Animation` object with the `AnimationId` property set to the asset ID of your `CurveAnimation`. _`Animation` object in the Explorer._ _`AnimationId` property in the Properties window._ To create an `Class.Animation` object you can publish as an emote: 1. In the **Explorer**, add a new `Animation` object. 2. In the **Properties** window, add the asset ID of the emote animation. ## Upload and publish an emote When you have an `Class.Animation` with your emote animation asset ID set, you can upload the emote to the Marketplace. This process requires an [upload fee](/docs/en-us/marketplace/marketplace-fees-and-commissions.md#upload-fees). 1. Right-click the `Animation` object and select **Save to Roblox**. 2. In the upload window, set the following fields: 1. Add a title and description. 2. Set the **Content Type** to **Avatar Item**. 3. Set the **Asset Category** to **Emotes**. 3. Press Submit to upload your asset for moderation. For more information on the upload and publishing process, see [publishing Marketplace items](/docs/en-us/marketplace/publish-to-marketplace.md#upload-an-asset). --- title: "Emotes" url: /docs/en-us/avatar/emotes last_updated: 2026-06-29T19:33:57Z description: "All Roblox users are represented by an avatar character which can be customized with body parts, accessories, and clothes from the Marketplace or within experiences." --- # Emotes **Emotes** are short animations that an avatar performs, making it easier to communicate and celebrate with others, enhancing the experience to feel more social and more lively. Creators sell their own emotes, such as gestures, reactions, dances, and more, by publishing their animation to sell on the Marketplace or within experiences. > **Info:** Developers can access and modify emotes within their own experiences. For more information, see [character emotes](/docs/en-us/characters/emotes.md). ## Components of an emote Emotes are made up of an `Class.Animation` object with the unique `Class.Animation.AnimationId` property set to the asset ID of your animation sequence. _`Animation` object in the Explorer._ _`AnimationId` property in the Properties window._ Emotes must meet [technical specifications](/docs/en-us/avatar/specifications.md) as well as [Marketplace](/docs/en-us/marketplace/marketplace-policy.md) and [Community](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards) policies. ## Creation process There are several ways to create an animation both in Studio or using a third-party tool. See the following table for common workflows and various resources to create your own. | Animation workflow | Description | | --- | --- | | Animation Capture | Use the [Animation Capture (Body)](/docs/en-us/animation/capture.md#body) to convert a video of your emote into a keyframe sequence. | | Manual animation with Animation Editor | Use the [Animation Editor](/docs/en-us/animation/editor.md) to manually animate a character rig. For an in-depth tutorial on creating a character animation, see [Create character animations](/docs/en-us/tutorials/use-case-tutorials/animation/create-an-animation.md). | | Third-party plug-ins | Use a third-party animation plugin, such as [Moon Animator](https://create.roblox.com/store/asset/4725618216/Moon-Animator-2), to generate your animation. | To convert your keyframe sequence into an animation and upload to the Marketplace, see [import and configure emotes](/docs/en-us/avatar/import.md#configure). --- title: "Emotes specifications" url: /docs/en-us/avatar/emotes/specifications last_updated: 2026-06-29T19:33:57Z description: "Character specification lists the specific technical requirements for custom characters created outside of Studio." --- # Emotes specifications Emotes are made up of an `Class.Animation` object with the `Class.Animation.AnimationId` property set to the asset ID of your animation sequence. _`Animation` object in the Explorer._ _`AnimationId` property in the Properties window._ Only the `Class.Animation` object is required to upload an emote to the Marketplace. Animations do not require meshes, textures, or other 3D art components. ## Animation requirements Emotes must also meet the following requirements or may fail validation: - Emotes must be less than 10 seconds. - The joint root of the character can not move too far from its starting position. - Movement speed can't exceed a certain threshold to prevent teleportation abuse (measured from a frame-by-frame basis). - Animation data must reference an R15 rig. - Animation must be sourced from a `CurveAnimation`. For information on converting animation data, see [import and configure](/docs/en-us/avatar/emotes/import.md#generate-asset-id). - Animations must meet [Marketplace](/docs/en-us/marketplace/marketplace-policy.md) and [Community](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards) policies. --- title: "Avatar in-experience creation" url: /docs/en-us/avatar/in-experience-creation last_updated: 2026-06-29T19:33:57Z description: "Explains how to use AvatarCreationService to prompt users to create and purchase avatars from within your experience." --- # Avatar in-experience creation > **Warning:** The following guide applies to creators who are familiar with scripting and Roblox Studio and intend to develop experiences that allow user-generated avatar items. You can publish an experience that allows players to create, customize, and purchase avatar bodies in real time. When purchased, these custom bodies save directly to the player's Roblox inventory, allowing players to equip and wear the custom avatars in other experiences. Experience owners that implement in-experience avatar creation benefit from [Marketplace commissions](/docs/en-us/marketplace/marketplace-fees-and-commissions.md#commissions) as both **creator** of the avatar item and **experience owner**. If an asset created in-experience is inspected, the item provides a link to the original experience it was created. You can test in-experience creation in Roblox's [Avatar Creator](https://www.roblox.com/games/124012682058672/Roblox-Avatar-Creator) demo. > **Warning:** Avatar assets created in-experience are not listed on the Marketplace, and cannot be resold or traded. At this time, in-experience creation only allows the creation of custom avatar bodies. ## How to implement in-experience creation Use the following instructions and code references to create your first in-experience avatar creation project. The following instructions uses a base body `Class.Model` that players can modify and customize before publishing. Before getting started, familiarize yourself with the following: - Avatar models — The following implementation requires importing a base body that meets Roblox's 15 part specifications. This `Class.Model` serves as a base for additional user customization and modification. - The base body must meet Roblox's [Avatar body guidelines](/docs/en-us/marketplace/marketplace-policy.md#avatar-body-guidelines), including the minimum number of FACS control for facial rigging. - [Avatar creation tokens](#generate-an-avatar-creation-token) — Experiences implementing avatar creation require at least one creation token. These tokens require Robux to purchase and allow you to set prices and other sale settings for purchases made in-experience. - API classes - `Class.AvatarCreationService` — Handles avatar creation prompting and validation. - `Class.EditableImage` — Handles runtime creation and manipulation of textures. - `Class.EditableMesh` — Handles runtime manipulation of mesh geometry. - `Class.WrapDeformer` — Handles runtime manipulation of invisible outer cage geometry that allow avatar characters to equip [3D clothing](/docs/en-us/avatar/layered-accessories.md). ### Import a base body The base body acts as the initial foundation which users can customize and edit. You can use your own `Class.Model`, or import a custom asset with the [Importer](/docs/en-us/studio/importer.md) and setup through [Avatar Setup](/docs/en-us/avatar-setup.md). Base bodies must adhere to Roblox's avatar specifications, and must include components such as the 15 `Class.MeshPart` instances that make up 6 body parts: head, torso, left arm, left leg, right arm, and right leg, as well as other [avatar components](/docs/en-us/avatar/character-bodies.md#components-of-an-avatar). > **Info:** If you have a single-mesh body, you can try using the [Avatar Auto Setup](/docs/en-us/avatar-setup.md#avatar-auto-setup) to automatically add all the necessary avatar components. For references and samples of properly configured avatar bodies, see [Avatar references](/docs/en-us/avatar/resources.md#references). ### Implement editing APIs To develop a system where users can edit the `Class.MeshPart` instances on an avatar in your experience for creation, use `Class.EditableImage` for texture editing, `Class.EditableMesh` for mesh editing, and `Class.WrapDeformer` for maintaining skinning and FACS data during mesh edits. 1. After importing your base body, use the following script to set up your `Class.EditableImage|EditableImages`, `Class.EditableMesh|EditableMeshes`, and `Class.WrapDeformer|WrapDeformers`.```lua local AssetService = game:GetService("AssetService") local function setupBodyPart(meshPart, wrapTarget) -- Create and attach a WrapDeformer to the MeshPart local wrapDeformer = Instance.new("WrapDeformer") wrapDeformer.Parent = meshPart -- Create an editable mesh for the wrap target's cage mesh local cageEditableMesh: EditableMesh = AssetService:CreateEditableMeshAsync(Content.fromUri(wrapTarget.CageMeshId), { FixedSize = true, }) -- Assign the cage mesh to the WrapDeformer wrapDeformer:SetCageMeshContent(Content.fromObject(cageEditableMesh)) end local function setupRigidMesh(meshPart) -- Create an editable mesh from the original MeshPart local editableMesh = AssetService:CreateEditableMeshAsync(Content.fromUri(meshPart.MeshId), { FixedSize = true, }) -- Generate a new MeshPart from the editable mesh local newMeshPart = AssetService:CreateMeshPartAsync(Content.fromObject(editableMesh)) -- Copy size, position, and texture from the original MeshPart newMeshPart.Size = meshPart.Size newMeshPart.CFrame = meshPart.CFrame newMeshPart.TextureContent = meshPart.TextureContent -- Apply the new MeshPart back to the original meshPart:ApplyMesh(newMeshPart) end local function setupMeshTexture(meshPart, textureIdToEditableImageMap) -- If EditableImage already exists for this TextureID, resuse it rather than making a new one if textureIdToEditableImageMap[meshPart.TextureID] then meshPart.TextureContent = Content.fromObject(textureIdToEditableImageMap[meshPart.TextureID]) return end -- Create a new EditableImage and apply it as the texture content local editableImage = AssetService:CreateEditableImageAsync(Content.fromUri(meshPart.TextureID)) textureIdToEditableImageMap[meshPart.TextureID] = editableImage meshPart.TextureContent = Content.fromObject(editableImage) end local function setupModel(model) -- Map for reusing EditableImage instances by texture ID local textureIdToEditableImageMap = {} for _, descendant in model:GetDescendants() do if not descendant:IsA("MeshPart") then continue end -- Configure MeshPart based on WrapTarget presence -- If WrapTarget is present, add a WrapDeformer child with an EditableMesh -- Otherwise, apply EditableMesh to the MeshPart directly local wrapTarget = descendant:FindFirstChildOfClass("WrapTarget") if wrapTarget then setupBodyPart(descendant, wrapTarget) else setupRigidMesh(descendant) end -- Configure the EditableImage for the MeshPart setupMeshTexture(descendant, textureIdToEditableImageMap) end end ``` 2. Create `Class.EditableImage` tools that allow players to recolor, draw, or add stickers to your base body. You can leverage APIs in like `Class.EditableImage:DrawImage()|DrawImage()`, `Class.EditableImage:DrawRectangle()|DrawRectangle()`, `Class.EditableImage:WritePixelsBuffer()|WritePixelsBuffer()`. - For advanced transformations, `Class.EditableImage:DrawImageTransformed()|DrawImageTransformed()` allows you to specify position, rotation, and scale when drawing one EditableImage onto another. Similarly, `Class.EditableImage:DrawImageProjected()|DrawImageProjected()` works much like `Class.EditableImage:DrawImage()|DrawImage()` but projects the drawn image properly if the `Class.EditableImage` instance is used with a `Class.MeshPart`.```lua local function recolorTexture( meshPart: MeshPart, color: Color3 ) local bodyPartTexture = AssetService:CreateEditableImageAsync(meshPart.TextureID) meshPart.TextureContent = Content.fromObject(bodyPartTexture) bodyPartTexture:DrawRectangle( Vector2.new(0, 0), bodyPartTexture.Size, color, 0, Enum.ImageCombineType.Overwrite ) end local function applySticker( meshPart: MeshPart, textureCoordinate: Vector2, stickerId: TextureId ) local bodyPartTexture = AssetService:CreateEditableImageAsync(meshPart.TextureID) meshPart.TextureContent = Content.fromObject(bodyPartTexture) local stickerTexture = AssetService:CreateEditableImageAsync(stickerId) bodyPartTexture:DrawImage(textureCoordinate, stickerTexture, Enum.ImageCombineType.BlendSourceOver) end local function applyStickerProjected( meshPart: MeshPart, targetMesh: EditableMesh, stickerId: TextureId, raycastHitPos: Vector3 ) local bodyPartTexture = AssetService:CreateEditableImageAsync(meshPart.TextureID) local relativePos = meshPart.CFrame:PointToWorldSpace(raycastHitPos) local direction = (workspace.CurrentCamera.CFrame.Position - relativePos).Unit local projectionParams: ProjectionParams = { Direction = meshPart.CFrame:VectorToObjectSpace(direction), Position = meshPart.CFrame:PointToObjectSpace(relativePos), Size = Vector3.new(1, 1, 1), Up = meshPart.CFrame:VectorToObjectSpace(Vector3.new(0, 1, 0)), } local stickerTexture = AssetService:CreateEditableImageAsync(stickerId) local localBrushConfig: BrushConfig = { Decal = stickerTexture, ColorBlendType = Enum.ImageCombineType.BlendSourceOver, AlphaBlendType = Enum.ImageAlphaType.Default, BlendIntensity = 1, FadeAngle = 90.0 } bodyPartTexture:DrawImageProjected(targetMesh, projectionParams, localBrushConfig) end ``` 3. Using `Class.WrapDeformer` and `Class.EditableMesh`, create tools for editing mesh deformations on your body. 1. `Class.WrapDeformer` handles the live deformations of the rendered `Class.MeshPart` geometry while maintaining the underlying skinning and FACS data. 2. `Class.EditableMesh` allows you to modify the cage mesh that the `Class.WrapDeformer` responds to. 1. Use `Class.WrapDeformer:SetCageMeshContent()` to apply the `Class.EditableMesh` instance that represents the relevant cage mesh to the `Class.WrapDeformer`. 2. Use `Class.EditableMesh`, such as `Class.EditableMesh:SetPosition()|SetPosition()`, to deform vertices and edit the shape of the `Class.MeshPart`.```lua local function deformBodyPart( meshPart: MeshPart, controlPointCenter: Vector3, controlPointRadius: number, controlPointDeformation: Vector3 ) local wrapTarget = meshPart:FindFirstChildWhichIsA("WrapTarget") local cageMeshId = wrapTarget.CageMeshId local wrapDeformer = Instance.new("WrapDeformer") wrapDeformer.Parent = meshPart local cageEditableMesh = AssetService:CreateEditableMeshAsync(cageMeshId) local verticesWithinSphere = cageEditableMesh:FindVerticesWithinSphere(controlPointCenter, controlPointRadius) for _, vertexId in verticesWithinSphere do local vertexPosition = cageEditableMesh:GetPosition(vertexId) cageEditableMesh:SetPosition(vertexId, vertexPosition + controlPointDeformation) end wrapDeformer:SetCageMeshContent(Content.fromObject(cageEditableMesh)) end ``` ### Create creation prompt After setting up your base body and editing APIs, create a prompt for users to create and purchase from the experience using `Class.AvatarCreationService:PromptCreateAvatarAsync()`. ```lua export type BodyPartInfo = { bodyPart: Enum.BodyPart, instance: Instance --Folder with Created MeshParts } export type BodyPartList = {BodyPartInfo} local function publishAvatar(bodyPartInstances: BodyPartList, player: Player, tokenId: string) local humanoidDescription = Instance.new("HumanoidDescription") for _, bodyPartInfo in bodyPartInstances do local bodyPartDescription = Instance.new("BodyPartDescription") bodyPartDescription.Instance = bodyPartInfo.instance bodyPartDescription.BodyPart = bodyPartInfo.bodyPart bodyPartDescription.Parent = humanoidDescription end local success, result, bundleIdOrErrorMessage, outfitId = pcall(function() return AvatarCreationService:PromptCreateAvatarAsync(tokenId, player, humanoidDescription) end) if success then if result == Enum.PromptCreateAvatarResult.Success then print("Successfully uploaded with BundleId: ", bundleIdOrErrorMessage) print("Successfully uploaded with OutfitId: ", outfitId) else print("Unsuccessfully uploaded with error message:", bundleIdOrErrorMessage) end else print("Avatar creation unsuccessful") end end ``` `Class.AvatarCreationService:PromptCreateAvatarAsync()` takes a `Class.HumanoidDescription` parameter to represent the avatar intended for purchase or creation. For avatar creation, the character's `Class.HumanoidDescription` must include new assets to be created for each of the 6 body parts (`Enum.BodyPart.Head|Head`, `Enum.BodyPart.Torso|Torso`, `Enum.BodyPart.RightLeg|RightLeg`, `Enum.BodyPart.LeftLeg|LeftLeg`, `Enum.BodyPart.RightArm|RightArm`, `Enum.BodyPart.LeftArm|LeftArm`). Optionally, it can also include a new `Enum.AccessoryType.Hair|Hair` accessory. To support this, the `Class.HumanoidDescription` should include 6 `Class.BodyPartDescription` children. Each `Class.BodyPartDescription.Instance` property references a `Class.Folder` which includes all of the `Class.MeshPart` instances that make up the body part. For example, the `LeftArm` folder contains `LeftHand`, `LeftUpperArm`, and `LeftLowerArm` `Class.MeshPart|MeshParts`. The `Class.BodyPartDescription.BodyPart` property should also be set to the relevant `Enum.BodyPart`. Each of the 15 `Class.MeshPart` body parts must include: - An `Class.EditableImage`. - A `Class.WrapDeformer` with an `Class.EditableMesh`. The provided `Class.HumanoidDescription` should not include any pre-existing asset IDs to represent body parts or accessories on the intended creation. On the other hand, the `Class.HumanoidDescription` may include the humanoid scales of `Class.HumanoidDescription.BodyTypeScale|BodyTypeScale`, `Class.HumanoidDescription.HeadScale|HeadScale`, `Class.HumanoidDescription.HeightScale|HeightScale`, `Class.HumanoidDescription.WidthScale|WidthScale`, and `Class.HumanoidDescription.ProportionScale|ProportionScale`. Be mindful of the scales that a base body is imported with so that they match the scales provided to the `Class.HumanoidDescription`. #### Include accessories If including an accessory, such as hair, the `Class.HumanoidDescription` should include a child `Class.AccessoryDescription` where: - The `Class.AccessoryDescription.Instance` property references the `Class.Accessory` instance. - The `Class.AccessoryDescription.AccessoryType` property is set to the relevant `Enum.AccessoryType`. - In the case of including `Enum.AccessoryType.Hair` in your creations, the `Class.MeshPart` should include an `Class.EditableImage`. However, it should not include a `Class.WrapDeformer` child but should include an `Class.EditableMesh` set on the `Class.MeshPart` directly. ### Generate an avatar creation token `Class.AvatarCreationService:PromptCreateAvatarAsync()` takes an **Avatar Creation Token ID** parameter. This token is the key for creations from your universe, and it is what you can use to set the price of avatar creation from your experience. For instructions and additional details on generating tokens, see [Avatar Creation Tokens](/docs/en-us/production/monetization/avatar-creation-token.md). After purchasing and generating your token, you can inspect the token in the Creator Hub to find the ID which you can then use for the `Class.AvatarCreationService:PromptCreateAvatarAsync()` API. ## Respond to players joining by attribution Avatar bundles created in-experience include an attribution link to the original experience the avatar was created. If the avatar is inspected by another player, a prompt displays providing an option to visit the experience where the avatar was created. To handle players joining your experience using this attribution link, use `Class.Player:GetJoinData()` and parse the returned table for `GameJoinContext`. `GameJoinContext` includes the following table values: - `JoinSource` — `Enum.JoinSource` - A `Class.Player` joining your experience from this attribution link will have `Enum.JoinSource.CreatedItemAttribution` to indicate entry from a created item. - `ItemType` — optional `Enum.AvatarItemType` - `AssetId` — optional `string` - `OutfitId` — optional `string` - `AssetType` — optional `Enum.AssetType` --- title: "Create avatar items" url: /docs/en-us/avatar last_updated: 2026-06-29T19:33:57Z description: "Create and upload avatar characters, clothing, and accessories to the Roblox Marketplace." --- # Create avatar items Every Roblox user is represented by an **avatar** — a fully customizable character with cosmetics and accessories that persist across experiences. Create avatar bodies, items, and clothing using our robust tools and upload them to the [Marketplace](https://www.roblox.com/catalog) where millions of users browse and shop every day. [Watch overview](https://www.youtube.com/watch?v=vIiVbFiDbBE) [Visit Marketplace](https://www.roblox.com/catalog) # Turn your creativity into virtual assets Roblox streamlines the creation process, letting you focus more on building and bringing your ideas to life with powerful, state-of-the-art tools. ### Design a 2D classic shirt Use an image editor of your choice and create your first basic 2D cosmetic. [Learn more](/docs/en-us/avatar/classic-clothing.md) ### Create a 3D accessory Use a 3D modeling tool and Roblox Studio to make a 3D cosmetic. [Guides and tutorials](/docs/en-us/art/accessories/creating-rigid.md) [Get Blender](https://www.blender.org) [Get Studio](/docs/en-us/studio/setup.md) ### Make 3D layered clothing Use your 3D modeling skills and Roblox Studio to create clothing that stretches, fits, and layers. [Guides and tutorials](/docs/en-us/avatar/layered-accessories.md) [Get Blender](https://www.blender.org) [Get Studio](/docs/en-us/studio/setup.md) # What's new? Check out our latest videos and guides designed to help you create faster — whether you're a complete newbie or a seasoned industry pro! [See all videos](https://www.youtube.com/playlist?list=PLMneGxZNs3ZYZ5cJ1IPeaO1Eyd4ejY1Lz) ### Make your own shoes Create your first shoes — an advanced type of layered clothing. ### Convert body cages and clothing cages Convert a body cage to a clothing cage and back again! Useful for advanced clothing or body creators. ### Intro to UGC Join @ducksareyellow as he asks seasoned professionals how to start creating UGC in a 3-part series [Part 2 - Modeling](https://www.youtube-nocookie.com/embed/Zb1BJow0NV4) [Part 3 - Texturing](https://www.youtube-nocookie.com/embed/MlyJD_ix9CE) # All the tools you need, for free Roblox offers the tools and infrastructure to upload your creations to a global marketplace, handling everything from moderation and localization to payment processing. ### Blender Check out in-depth guides and tutorials for creating 3D items in Blender, a free and open-sourced modeling program. [Get Blender](https://www.blender.org) [Learn more](/docs/en-us/art/blender.md) ### Roblox Studio Use native tools in Roblox Studio to quickly convert your custom models into Roblox-ready accessories. Studio is your portal to getting your 3D assets into the Roblox ecosystem. [Get Studio](/docs/en-us/studio/setup.md) [Accessory Fitting Tool](/docs/en-us/avatar/accessory-fitting-tool.md) [Avatar Setup](/docs/en-us/avatar-setup.md) ### Creator Hub Manage all your uploaded avatar items, set them on sale, review policy, and view analytics in the Creator Hub. [Go to Creator Hub](https://create.roblox.com/) [Upload and publishing guides](/docs/en-us/marketplace/publish-to-marketplace.md) ### Marketplace and beyond Create your own home store and a higher share of your commission! Or build experiences that allow others to customize and create avatar items in-game. [Marketplace policy](/docs/en-us/marketplace/marketplace-policy.md) [Create your own home store](/docs/en-us/marketplace/homestore.md) [In-experience creation](/docs/en-us/in-experience-creation.md) # Dive deeper into avatars Learn how to create advanced avatar items, explore how the Marketplace operates, and see how Roblox's community policies protect both creators and players. ### Fees, commissions, and more Understand the fees and commission structure and how to optimize your avatar item revenue. [Fees and Commissions](/docs/en-us/marketplace/marketplace-fees-and-commissions.md) [Upload and publish guides](/docs/en-us/marketplace/publish-to-marketplace.md) [Marketplace FAQs](/docs/en-us/marketplace/frequently-asked-questions.md) ### Marketplace and community policies Learn about the policies that help protect you, your creations, and the community. [Marketplace Policy](/docs/en-us/marketplace/marketplace-policy.md) [Intellectual Property](/docs/en-us/marketplace/intellectual-property.md) [Moderation](/docs/en-us/marketplace/moderation.md) ### Create more complex items Create even more advanced avatar items, like advanced clothing, bodies, heads, and more. [Clothing guides](/docs/en-us/avatar/layered-accessories.md) [Character bodies](/docs/en-us/avatar/character-bodies.md) [Animateable heads](/docs/en-us/avatar/dynamic-heads.md) ### Build your own homestore Create and design your own store and benefit from a higher commission split from sales. [Learn more](/docs/en-us/marketplace/homestore.md) ### In-experience creation Create an experience where players can build their own avatar items. Recommended for advanced developers. [Learn more](/docs/en-us/in-experience-creation.md) ### --- title: "Caging best practices" url: /docs/en-us/avatar/layered-accessories/caging-best-practices last_updated: 2026-06-29T19:33:57Z description: "In-depth description and images of various caging best practices for layered clothing." --- # Caging best practices Caging is a complex process required to define the inner and outer surface of a layered clothing item. Properly caged assets allow your clothing items to stretch and layer in combination with each other. Cages that are improperly configured can lead to cosmetic issues with the look and feel of your clothing item. _A properly configured cage typically includes a tight layering of the inner cage, clothing mesh, and the outer cage._ _The t-shirt stretches and fits naturally over a base body._ _Other layered objects, such as a jacket, should stretch and fit naturally over a target body and any other caged assets._ In extreme cases, improper cages may interfere with gameplay or social elements on Roblox. If your asset includes improper cages, Roblox may prevent you from uploading them to the Marketplace and may remove existing assets with improper cages from the catalog. > **Warning:** Roblox is enforcing a stricter set of validation rules to improve the overall quality of layered clothing accessories in the Marketplace. The following [best practices](#best-practices) and [common issues](#common-issues) is useful for 3D clothing creators of all levels and can elevate the quality of your clothing creations and save time with troubleshooting. ## Best practices Always start any caging work with one of Roblox's [provided caging templates](../../assets/modeling/meshes/reference-files/Clothing_Cage_Templates.zip). These templates include an inner cage and outer cage with properly set UVs. > **Error:** You should not edit the cage UVs since this will introduce visual artifacts. When caging your clothing items, use these important universal guidelines: - Any clothing mesh positioned between the inner cage and outer cage will be deformed by the layering system. This is the typical configuration for nearly all layered clothing assets._The t-shirt mesh fits snugly between the inner and outer cage.__The correctly configured t-shirt naturally stretches over a target body.__Other layered assets should stretch and fit naturally over a target body and any equipped caged assets._ - You must follow the [naming conventions](/docs/en-us/avatar/layered-accessories/specifications.md#cage-meshes) for the clothing geometry and the cage. - Many caging issues start from incorrect outer cage setup. See [working with outer cages](#working-with-outer-cages) for specific details. - Do not modify areas of your cage that are unrelated to the clothing item you are caging. For example, do not modify the inner or outer cages of the lower leg areas when caging a t-shirt. - Review any specific best practices for [form-fitting clothes](#caging-form-fitting-clothing), [bulky clothes](#caging-bulky-clothing-items), and [clothing with protrusions](#caging-protrusions). - If troubleshooting any issues, review [common issues](#common-issues) to help identify problematic clothing items or specific validation errors. ### Working with outer cages As long as the clothing mesh properly fits **over the inner cage** and **under the outer cage**, you can edit the outer cage in various ways depending on the final visual effect you intend to achieve. When working with the outer cage, use the following guidelines when possible: - Move vertices outwards in the normal's direction. - Keep the outer cage as close to the clothing as possible. - Work symmetrically as much as possible to save time. - Do not alter the UVs in any way. - Do not stack your vertices._Two vertices stacked directly over each other. This will introduce visual artifacts when layering other layered accessories on top._ - Keep the vertex, edge, and face count exactly the same as when you started. - Keep your cage's edge spacing as evenly as possible._ > **Success:** Outer cage with even spacing whenever possible on the faces.__ > **Error:** Outer cage with uneven spacing throughout the torso area. Uneven cages can create visual artifacts and uneven deformations._ - Keep your cage's break lines between body parts lined up to the joint's position as possible. For example, try to align the elbow to where the LowerArm joint is located._Proportional changes can help improve animation and posing quality._ ### Caging form-fitting clothing Form-fitting clothing, like t-shirts, yoga pants, shorts, sweaters and slim jackets, are the most common type of layered clothing. In this case, the outer cage needs to completely envelop the clothing accessory with minimal gaps between inner cage, clothing, and outer cage. - Move outer cage vertices outwards to completely cover the clothing item, while maintaining the silhouette of the clothing as much as possible. - Do not move outer cage vertices that don't contribute to covering the clothing item. For example, don't move outer cage vertices in the lower leg when the clothing item is a t-shirt. - The entire clothing item should be located in-between the outer and inner cages. ### Caging bulky clothing items Bulky clothing items, like puffy jackets, sweat pants, and hoodies, require additional displacement of the outer cage vertices will be much larger to envelop the bulky asset. The outer cage should still completely wrap the external surface of a bulky item. - Pay special attention to the direction neighboring outer cage vertices are moved. - Move along vertex normal directions as much as possible. - Move vertices nearby each other more or less along the same directions (smooth relative displacements). - Avoid moving vertices that are not covered by the clothing item, such as moving vertices in the arm region if you are making pants. ### Caging protrusions Some clothing items may have assets that may protrude or extend past the rest of the clothing. Examples include clothing with puffy hoods, shoulder pads, and spikes. _Like all clothing, clothing with protrusions should fit over inner cages._ _> **Success:** Setting the outer cage below the puffy hood allows the hood to be visible even when equipping other layered assets._ _> **Error:** Setting the outer cage above the puffy hood causes additional layered assets to stretch and cover the protrusion which is often undesired._ To ensure that the protruded region stays visible independent of the number of layers: - Leave the protruded region outside the outer cage. Alternatively, edit the outer cage to pass under the regions that are meant to be visible and undeformed. - Avoid moving vertices that are not covered by the clothing item ## Common issues Improper caging can cause various rendering and deformation issues. The following common examples can help diagnose and troubleshoot any caging issues you may encounter. ### Uneven caging Unexpected outer cage shapes causes issues when other layered assets are equipped. While the clothing may fit correctly on a target body, any additional layers will fail to stretch and deform appropriately. _The outer cage of the black shirt is set up with uneven vertices producing an undulated caging surface._ _Any layered clothing accessory fit on top will have visual artifacts even if the clothing asset itself looks fine._ ### Identical inner and outer cage If the inner cage and outer cage are exactly the same, the layered clothing system cannot properly determine the accessory and may not deform or deform unexpectedly. _An example of an identical inner and outer cages._ _The tshirt deforms in an unexpected manner and fails to render in its original shape or placement._ ### Clothing inside inner cage Any part of the clothing mesh positioned **inside** the **inner cage** will introduce visual artifacts when the clothing is layered on top of other clothing. You should always avoid this type of configuration. _The clothing mesh intersects and is positioned within the inner cage at the shoulders and abdomen areas._ _When equipped, the t-shirt does not fit properly, exposing the problematic areas in the shoulders and abdomen._ ### Clothing partially outside outer cage Any part of the clothing mesh positioned **outside** the **outer cage** will not be deformed by the layered system. This configuration can sometimes be intentional for effect, such as when [caging clothing assets that include protrusions](#caging-protrusions). _The outer cage of the tshirt is positioned within the clothing mesh near the abdomen._ _When equipped, layered assets fail to fit properly at the affected areas._ ### Oversized outer cage Ensure that the gap between the layered accessory and the outer cage is not too large. This can cause severe deformation issues when combined with other layered assets. ### Collapsed cage vertices When cage vertices are collapsed into one vertex or a small region, any additional layer placed on top of your clothing item will have weird artifacts near the collapsed region. _Most 3D applications can indicate if vertices are directly overlapping_ > **Warning:** This setup will be rejected during validation. ### Cages with missing limbs The cage is meant to match the shape of the template body used to model your clothing item. Cages without heads or missing limbs won't work properly with the layered clothing system. > **Warning:** This setup will be rejected during validation. ### Outer cage inside the inner cage Setting the outer cage inside the inner cage creates a clothing item that can't properly layer with other assets. > **Warning:** This setup will be rejected during validation. ### Mesh outside of outer cage Layered assets placed outside their outer cages do not deform. If the item is completely outside then it will behave similar to a rigid accessory. _Assets outside of the cage meshes are not supported._ _Even with a correct hierarchy, assets outside the cages will not deform._ > **Warning:** This setup will be rejected during validation. --- title: "Layered clothing export settings" url: /docs/en-us/avatar/layered-accessories/export last_updated: 2026-06-29T19:33:57Z description: "Use the appropriate export settings in Maya and Blender to generate Studio-ready .fbx files." --- # Layered clothing export settings Export your mesh or model as a `.fbx` or `.gltf` to take advantage of all of Studio's 3D import features. When rigging or skinning a layerable model, these file types contain all the mesh and texture data, including the rig and influence data, you need to later [import](/docs/en-us/studio/importer.md) into Studio. > **Warning:** **If creating other types of 3D models:**- For generic meshes, see [general mesh specifications](/docs/en-us/art/modeling/specifications.md) and [general export settings](/docs/en-us/art/modeling/export-requirements.md). - For rigid accessories, see [accessory specifications](/docs/en-us/avatar/rigid-accessories/specifications.md) and [accessory export settings](/docs/en-us/avatar/rigid-accessories/export.md). - For avatar characters, see [avatar specifications](/docs/en-us/avatar/character-bodies/specifications.md) and [avatar export settings](/docs/en-us/avatar/character-bodies/export.md). ## Before exporting Before exporting, ensure that you are only exporting the Roblox supported objects related to your model. If you have any modifiers to your mesh or project objects, make sure to apply or delete them before export. You can export layered clothing models with the following object structure: ![Blender clothing data model example](../../assets/modeling/skinned-meshes/Clothing-Data-Model.png) - Armature parent - Bones / joints - Primary mesh object - Cage parent object - Inner cage mesh object - Outer cage mesh object You can also export shoes together. Even though left and right shoes are separate accessories, you can either export the left and right shoe individually, or export both shoes at the same time using the following structure: ![Shoe clothing data model example](../../assets/modeling/skinned-meshes/Shoe-Data-Model.png) - Armature parent - Bones / joints - Left shoe mesh object - Right shoe mesh object - Cage parent object - Left shoe inner cage - Left shoe outer cage - Right shoe inner cage - Right shoe outer cage > **Warning:** Each shoe must include their own inner and outer cage. ## Blender Blender allows you to export in `.fbx` or `.gltf` as well as other formats. If you are using `.fbx` export, familiarize yourself with [Blender's FBX scaling](/docs/en-us/art/blender.md#adjust-scale-fbx) to ensure that you successfully import the model into Studio at the correct scale. ### Export settings To export the `.fbx` file in Blender: 1. In the topbar, click **File**. A pop-up menu displays. 2. Select **Export**, then **FBX (.fbx)**. The **Blender File View** window displays. 3. On the right-hand side, change the **Path Mode** property to **Copy**, then toggle the **Embed Textures** button. 4. Set the **Transform** > **Apply Scalings** to **FBX Unit Scale**. If you run into scaling issues on import, see [Blender FBX scaling](/docs/en-us/art/blender.md#adjust-scale-fbx) for alternative approaches. 5. Under the **Armature** section, disable **Add Leaf Bones**. 6. Click the **Export FBX** button. 7. After exporting, use Studio's [Importer](/docs/en-us/studio/importer.md) to import your model and the [Accessory Fitting Tool](/docs/en-us/avatar/accessory-fitting-tool.md) to convert the model into an accessory. ## Maya export settings To export a mesh in Maya as a `.fbx` file: 1. In the topbar, click **File**. A pop-up menu displays. 2. Select **Export All**. The **Export All** window displays. 3. Near the bottom of the window, click the **Files of type** dropdown, then select **FBX export**. 4. On the right-hand side of the window, navigate to the **Options...** section. 5. In the **Geometry** section, enable **Smooth Mesh** and **Referenced Asset Content**. 6. If you need to import textures as a `.png`, in the **Embed Media** section, enable **Embed Media**. 7. In the **Advanced Options** section, - Navigate to **Units**, then enable **Automatic**. - Navigate to **Axis Conversion**, then set the **Up Axis** property to **Y**. 8. Click the **Export All** button. 9. After exporting, use Studio's [Importer](/docs/en-us/studio/importer.md) to import your model and the [Accessory Fitting Tool](/docs/en-us/avatar/accessory-fitting-tool.md) to convert the model into an accessory. --- title: "Import clothing accessories" url: /docs/en-us/avatar/layered-accessories/import last_updated: 2026-06-29T19:33:57Z description: "Use the Importer to add third-party models to Studio before using the Accessory Fitting Tool to convert the model to an Accessory." --- # Import clothing accessories Use the following instructions to [import](#import-3d-assets) your `.fbx` or `.gltf` third-party model into Studio and [convert](#convert-layered-accessories) the asset to a clothing `Accessory` object that you can save to use in your experience, share with others, or upload to the Marketplace. > **Info:** The following asset example and instructions are part of the [Clothing accessory tutorial](/docs/en-us/art/accessories/creating.md) which covers the process of converting a model from Blender to publishing the asset to the Marketplace. ## Import 3D assets Studio's Importer provides a quick and easy way to import third-party 3D assets into your projects. The importer provides object previews and error-checking to ensure that your asset meets Roblox's [general 3D requirements](/docs/en-us/art/modeling/specifications.md). Keep in mind, your 3D layered accessory must also follow Roblox's [clothing specifications](/docs/en-us/avatar/layered-accessories/specifications.md) to eventually use this asset as a layered `Class.Accessory`, or you may experience errors later in the workflow. To import your asset: 1. From Studio's **File** menu, select **Importer**. 2. In the file browser, select the `.fbx` or `.gltf` file saved locally. The Importer loads a preview of the object. - If textures don't load for your asset, you can manually import your textures later. - See [Importer](/docs/en-us/studio/importer.md) for additional information on import settings and troubleshooting. 3. Select **Import**. The asset populates in your workspace as a `Class.Model` with the appropriate textures applied as a `Class.SurfaceAppearance` or `Class.MeshPart.TextureID`. **Manually add textures** If textures didn't load correctly, add them manually. You may need to save and publish your experience in order to access the [Asset Manager](/docs/en-us/projects/assets/manager.md). 1. In the **Asset Manager**, click the **Import** button. 2. Upload your image files. 3. After moderation clears for your image, select the `Class.MeshPart` parented within your imported `Class.Model`. 4. If you are using a single basic texture, set the `Class.MeshPart.TextureID` property to your uploaded texture image. 5. If you are using PBR textures: 1. Add a `Class.SurfaceAppearance` child to your `Class.MeshPart`. 2. In the `Class.SurfaceAppearance` properties, click each property value and assign the appropriate texture image from the asset dropdown: 1. Set the **ColorMap** to the **_ALB** texture image. 2. Set the **MetalnessMap** to the **_MTL** texture image. 3. Set the **NormalMap** to the **_NOR** texture image. 4. Set the **RoughnessMap** to the **_RGH** texture image. > **Success:** After successful import, your model object should populate in your project as a `Class.Model` with the appropriate textures applied. See [Importer](/docs/en-us/studio/importer.md) for additional information on import settings and troubleshooting. ## Convert layered accessories With the `Class.Model` in your project, the last step in the process of clothing creation requires you to use the [Accessory Fitting Tool](/docs/en-us/avatar/accessory-fitting-tool.md) (AFT) to convert this object to a standard `Class.Accessory` that avatar characters can equip. > **Warning:** For shoes, you must convert the left shoe and the right shoe as separate accessories. Use the following instructions for each shoe before grouping both shoe accessories as a `Class.Model` when [saving to Roblox](/docs/en-us/marketplace/publish-to-marketplace.md#upload-an-asset). To generate the accessory object: 1. In the toolbar's **Avatar** tab, click **Accessory** to open the AFT. 2. Select the `Class.Model` of the clothing item in the viewport. The tool's text field populates with the name of the object selected. Alternatively, you can select the object within the **Explorer** window. 3. Test out various sample characters, clothing, and animations. See [Test accessories](/docs/en-us/avatar/accessory-fitting-tool.md#test-accessories) for additional information. - If required, make minor cage adjustments using the editing features. Larger cage changes may require returning to your third-party modeling software and re-exporting the asset. 4. When ready to generate your accessory, click **Generate MeshPart Accessory**. The accessory object with your model populates in your workspace. _Clothing accessory in viewport_ _Clothing accessory in Explorer_ After successful fitting and converting, your 3D model should populate in your project as an `Class.Accessory`. With this `Class.Accessory` you can perform any of the following: - Begin the process of [uploading and publishing](/docs/en-us/marketplace/publish-to-marketplace.md#upload-an-asset) the clothing accessory to the Marketplace. - Use the accessory in your current experience by equipping it to character models with [HumanoidDescription](/docs/en-us/characters/appearance.md#manually-modify-appearance), or by dragging and dropping the accessory under the appropriate character `Class.Model` object. - Save the accessory to your [Toolbox](/docs/en-us/projects/assets/toolbox.md) or make it public on the [Creator Store](/docs/en-us/production/creator-store.md) to share or use within any of your experiences. --- title: "Layered clothing" url: /docs/en-us/avatar/layered-accessories last_updated: 2026-06-29T19:33:57Z description: "Layered clothing are cosmetic accessories that stretch, fit, and layer over any body type simulating real-life clothing." --- # Layered clothing A high-level overview of 3D clothing on Roblox. Create and sell your first basic layered clothing. Layered clothing accessories are 3D cosmetic items that users can equip and wear on their avatar body, such as pants, t-shirt, jackets, dresses, and more. Unlike [rigid accessories](/docs/en-us/rigid-accessories.md) that only attach to a specific point on a character, layered clothing stretches and fits over any body type and existing clothing. To create a custom Roblox accessory for your own experience or to sell on the Marketplace, it's important to start with the following: - A general background with 3D modeling tools such as [Blender](https://www.blender.org/) or [Maya](https://www.autodesk.com/products/maya/overview). - An understanding of the [components that make up a layered accessory](#components-of-a-layered-clothing-accessory). - An understanding of the general [clothing creation process](#creation-process). - Review Roblox's official tutorials to create your own accessories: - [Rigid accessory creation tutorial](/docs/en-us/art/accessories/creating-rigid.md) - covers each process required for converting a 3D model to a rigid accessory and publishing it to the Marketplace. - [Clothing creation tutorial](/docs/en-us/art/accessories/creating.md) - a step-by-step process of creating your own avatar-ready clothing from scratch in Blender. - [Additional tools, resources, and guides](#resources) provided by Roblox to standardize and expedite the creation process. > **Warning:** If you are a developer and are looking for information on manually equipping or configuring layered clothing accessories within an experience, including [implementing layered clothing on a non-standard character model](/docs/en-us/characters/appearance.md#layered-clothing-on-non-r15), see [Character appearance](/docs/en-us/characters/appearance.md). Roblox also supports [classic clothing](#classic-clothing), 2D images that can be applied to your character's surface. ## Components of a layered clothing accessory All accessory models are made up of the same base components of a [mesh object](#mesh-part), [textures](#textures), and [attachment](#attachments). Layered clothing requires additional components, such as a [poseable rig](#rigging-armature), and an [inner and outer cage](#inner-and-outer-cages), to allow the asset to stretch, fit, and layer over a target character and existing clothing items. When [creating accessories](#creation-process), all of the components are created first in your modeling software, then converted to their appropriate Roblox Studio instance on import. ### Mesh part _T-shirt clothing mesh object_ _Bow accessory mesh object_ > **Warning:** Simple accessories, such as the bow, are [rigid accessories](/docs/en-us/rigid-accessories.md) do not require the rigging and caging components that allow it to stretch and fit over a target. All accessories require a single mesh object that represents the geometry of the accessory object. In Studio, this mesh object is represented as a `Class.MeshPart` nested under a single `Class.Model` ### Textures _2D texture map for the t-shirt model_ _T-shirt model with texture applied_ Textures are image files that define the surface appearance of your accessory. You can create textures within a texture painting program or a 3D modeling software. In Studio, textures images are imported as image assets and are set to `Class.MeshPart` objects by a child `Class.SurfaceAppearance` object or a `Class.MeshPart.TextureID` property. ### Attachments _Attachment geometry defines where the attachment connects with the character_ _Geometry with the "_Att" suffix automatically convert to `Class.Attachment` objects in Studio_ For layered clothing, the attachment point is used to associate with the correct body part when the body ragdolls or is dismembered. In Studio, attachments are represented by `Class.Attachment` objects. Attachments for clothing items are automatically generated in Studio using the [Accessory Fitting Tool](/docs/en-us/avatar/accessory-fitting-tool.md). ### Rigging armature _To ensure natural movement of the clothing item, it must be weighted to a character rig_ _When the rig is properly set up, the layered model can move and bend with the character rig_ A rigging armature defines how a layered asset can move with a character model. Using rigging and skinning techniques, you can set the areas of your clothes to move naturally with a character model's joints, such as ensuring a shirt sleeve correctly follows the movement of the elbow and shoulder. In Studio, this rigging and skinning data is saved to the mesh geometry. ### Inner and outer cages _The inner cage defines the inner surface of the clothing item where the clothes wrap over_ _The outer cage defines the outer surface of the clothing item where any additional clothes can wrap and layer over_ Cage meshes indicate the inner and outer surfaces of a layered accessory. The inside cage of a t-shirt defines how the t-shirt stretches and fits over a character body. The outer cage of a t-shirt defines how additional layered clothing fit over the t-shirt. In Studio, these cages are represented by `Class.WrapLayer` objects. ## Creation process Custom accessories are first created in 3D modeling programs, such as [Blender](https://www.blender.org/) or [Maya](https://www.autodesk.com/products/maya/overview), before importing the `.fbx` or `.gltf` model into Studio. To get started creating your first avatar asset, see [Avatar Tutorials](/docs/en-us/avatar/tutorials.md). Depending on the type of asset you are creating, the creation process follows these high-level workflows: _ Layered Accessory Workflow_ _ Rigid Accessory Workflow_ > **Info:** Since 3D creation isn't a linear process and always requires reiteration and testing, the process of creating an accessory can differ between individuals and various creation workflows. ## Resources There are a variety of resources available for creators of all backgrounds to get started with accessory creation. If you are interested in specific avatar creation topics, use the following table to find guides and resources that best match your needs: | Topic | Resources | | --- | --- | | Tutorials | [Rigid accessory creation](/docs/en-us/art/accessories/creating-rigid.md)

[Basic clothing creation](/docs/en-us/art/accessories/creating.md) | | Reference files | [Accessory and clothing reference files](/docs/en-us/art/modeling/project-files.md) | | Technical specs | [.FBX export settings](/docs/en-us/art/modeling/export-requirements.md)

[General mesh specifications](/docs/en-us/art/modeling/specifications.md)

[Accessory specifications](/docs/en-us/avatar/rigid-accessories/specifications.md)

[Marketplace policy](/docs/en-us/marketplace/marketplace-policy.md) | | Cosmetic creation | [Accessories overview](/docs/en-us/rigid-accessories.md)

[Layered clothing overview](/docs/en-us/layered-accessories.md)

[Create face accessories](/docs/en-us/art/characters/facial-animation/create-face-accessories.md)

[Accessory Fitting Tool](/docs/en-us/accessory-fitting-tool.md)

[Accessory specifications](/docs/en-us/rigid-accessories/specifications.md)

[Marketplace requirements](/docs/en-us/marketplace/marketplace-policy.md) | | Texturing | [Texturing requirements](/docs/en-us/art/modeling/texture-specifications.md)

[PBR textures](/docs/en-us/art/modeling/surface-appearance.md) | | Rigging and skinning | [Rigging and skinning overview](/docs/en-us/art/modeling/rigging.md)

[Humanoid rigging requirements](/docs/en-us/avatar/character-bodies/specifications.md#rigging)

[Rigging facial bones](/docs/en-us/art/characters/facial-animation/create-basic-heads.md#rigging)

[Auto skin transfer](/docs/en-us/avatar/automatic-skinning-transfer.md)

[Skin facial bones](/docs/en-us/art/characters/facial-animation/create-basic-heads.md#skin-face-bones) | | Testing and validation | [Calisthenics tool](/docs/en-us/art/modeling/calisthenics-tool.md)

[Clothing validation tool](/docs/en-us/art/accessories/validation-tool.md) | | Publishing and Marketplace | [Upload to Marketplace](/docs/en-us/marketplace/publish-to-marketplace.md)

[Marketplace policy](/docs/en-us/marketplace/marketplace-policy.md)

[Fees and commissions](/docs/en-us/marketplace/marketplace-fees-and-commissions.md) | ## Classic clothing Classic clothing assets are 2D images that you can apply to the surface of an avatar body as t-shirts, shirts, or pants. You can design these assets in any image processing software, test the textures in Studio, and then upload the designs to the Marketplace to sell. See [Classic clothing](/docs/en-us/avatar/classic-clothing.md) for more information on creating, uploading, and selling these assets.
--- title: "Accessory project files and references" url: /docs/en-us/avatar/layered-accessories/project-files last_updated: 2026-06-29T19:33:57Z description: "Download various accessory-related project files and reference files." --- # Accessory project files and references > **Info:** See [Resources](/docs/en-us/avatar/resources.md) for a complete list of avatar-related downloadable content. The following `.fbx`, `.blend`, and `.ma` project files are available to use as templates or as reference: #### Clothing and mannequins | **File** | **Description** | | --- | --- | | [Tshirt-model.fbx](../../assets/accessories/reference-files/Tshirt-model.fbx) | Uncaged example clothing ready for caging in a 3D modeling software. | | [Additional-FBX-assets.zip](../../assets/accessories/reference-files/Additional-FBX-assets.zip) | Caged 3D accessory models ready for import into Studio or in a modeling tool. | | [Additional-FBX-assets.zip](../../assets/accessories/reference-files/Caging-examples.zip) | Additional caged clothing items from How to cage Roblox's [3D clothing video guide](https://www.youtube.com/watch?v=QwZaA9Gc-WQ). | | [ClassicMannequin.fbx](../../assets/art/reference-files/ClassicMannequin.fbx) | A [Classic body](/docs/en-us/avatar/character-bodies/specifications.md#classic) type blank mannequin to use in Studio or your modeling application. | | [ClassicMannequin_With-Cages](../../assets/art/reference-files/ClassicMannequin_With-Cages.fbx) | A [Classic](/docs/en-us/avatar/character-bodies/specifications.md#classic) body type blank mannequin with **body cages** to use in Studio or your modeling application for clothing design. Due to unconfigured cage objects, this file may not import correctly into Studio until modified. | | [RthroMannequin.fbx](../../assets/art/reference-files/RthroMannequin.fbx) | An [Rthro Normal](/docs/en-us/avatar/character-bodies/specifications.md#normal) body type blank mannequin to use in Studio or your modeling application. | | [RthroMannequin_With-Cages.fbx](../../assets/art/reference-files/RthroMannequin_With-Cages.fbx) | An [Rthro Normal](/docs/en-us/avatar/character-bodies/specifications.md#normal) body type blank mannequin with **body cages** to use in Studio or your modeling application to use in Studio or your modeling application for clothing design. Due to unconfigured cage objects, this file may not import correctly into Studio until modified. | | [RthroSlenderMannequin.fbx](../../assets/art/reference-files/RthroSlenderMannequin.fbx) | An [Rthro Slender](/docs/en-us/avatar/character-bodies/specifications.md#slender) body type blank mannequin to use in Studio or your modeling application. | | [RthroSlenderMannequin_With-Cages.fbx](../../assets/art/reference-files/RthroSlenderMannequin_With-Cages.fbx) | An [Rthro Slender](/docs/en-us/avatar/character-bodies/specifications.md#slender) body type blank mannequin with **body cages** to use in Studio or your modeling application to use in Studio or your modeling application for clothing design. Due to unconfigured cage objects, this file may not import correctly into Studio until modified. | #### Blender | **File** | **Description** | | --- | --- | | [Rig_and_Attachments_Template.blend](../../assets/modeling/meshes/reference-files/Rig_and_Attachments_Templates.zip) | Starting armature rig template for Blender. Contains an armature with correct R15 naming conventions and attachment points. Use this template for rigging bodies and clothing items. | | [Clothing_Cage_Template.blend](../../assets/modeling/meshes/reference-files/Clothing_Cage_Templates.zip) | Starting template for Blender that includes a full-body inner and outer cage mesh for creation of layered clothing. Use this template for caging your clothing accessories. | | [Combined-Template.blend](../../assets/modeling/meshes/reference-files/Combined_Templates.zip) | Template file containing all content from previous templates, includes rig skeleton, body cages, attachment points. Use this template to rig and cage bodies and accessories. | #### Maya | **File** | **Description** | | --- | --- | | [Rig_and_Attachments_Template.ma](../../assets/modeling/meshes/reference-files/Rig_and_Attachments_Templates.zip) | Starting armature rig template for Maya. Contains an armature with correct R15 naming conventions and attachment points. Use this template for creating bodies and clothing items. | | [Clothing_Cage_Template.ma](../../assets/modeling/meshes/reference-files/Clothing_Cage_Templates.zip) | Starting template for Maya, includes a full-body inner and outer cage mesh for creation of layered clothing. Use this template for caging your clothing accessories. | | [Combined-Template.ma](../../assets/modeling/meshes/reference-files/Combined_Templates.zip) | Template file containing all content from previous templates, includes rig skeleton, body cages, attachment points. Use this template to rig and cage bodies and accessories. | #### General > **Warning:** The following `.fbx` files were exported from Maya for use in modeling applications. If you are using Blender or Maya in your workflow, use the specific template files for those programs instead of the following files. | **File** | **Description** | | --- | --- | | [Rig_and_Attachments_Template.fbx](../../assets/modeling/meshes/reference-files/Rig_and_Attachments_Templates.zip) | Starting armature rig template for general modeling software. Contains an armature with correct R15 naming conventions and attachment points. Use this template for creating bodies and clothing items. | | [Clothing_Cage_Template.fbx](../../assets/modeling/meshes/reference-files/Clothing_Cage_Templates.zip) | Starting template for general modeling software, includes a full-body inner and outer cage mesh for creation of layered clothing. Use this template for caging your clothing accessories. | --- title: "Clothing specifications" url: /docs/en-us/avatar/layered-accessories/specifications last_updated: 2026-06-29T19:33:57Z description: "Layered clothing specifications lists the specific technical requirements for the visible mesh, cages, and proper rigging and skinning data." --- # Clothing specifications When creating clothing for Roblox, it's important to meet specific technical requirements to ensure compatibility and optimize for performance and quality. Many of these requirements must be applied when designing and modeling your asset in a third-party modeling application. Although [rigid accessories](/docs/en-us/avatar/rigid-accessories/specifications.md) and layerable accessories share many technical requirements, layerable clothing accessories must include additional components to ensure the accessories deform and stretch appropriately on different body scales. If you are intending to publish and sell these assets on the Marketplace, there are additional [Marketplace policy](/docs/en-us/marketplace/marketplace-policy.md) standards that you must follow for any accessory or clothing item. When ready to export, see [export requirements](/docs/en-us/avatar/layered-accessories/export.md) for mesh export settings for Blender and Maya. > **Warning:** **If creating other types of 3D models:**- For generic meshes, see [general mesh specifications](/docs/en-us/art/modeling/specifications.md) and [general export settings](/docs/en-us/art/modeling/export-requirements.md). - For rigid accessories, see [accessory specifications](/docs/en-us/avatar/rigid-accessories/specifications.md) and [accessory export settings](/docs/en-us/avatar/rigid-accessories/export.md). - For avatar characters, see [avatar specifications](/docs/en-us/avatar/character-bodies/specifications.md) and [avatar export settings](/docs/en-us/avatar/character-bodies/export.md). ## Geometry and Budgets - **Single Mesh** - Accessories must be a single mesh. - **Budgets** - Accessories can't exceed **4k** triangles. - **Watertight** - All geometry must be watertight without exposed holes or backfaces. - Use **quads** whenever possible. Avoid faces with 5 or more sides. - **Mesh Size** - Depending on the type of accessory asset, meshes must follow a standard size (in studs, centered on attachment point) depending on the [body scale](#size-requirements) it is designed for. ### Size Requirements Depending on the type of layerable asset, the size requirements can't exceed the following maximum width, height, and depth (in studs). | Asset Type | Width (X) | Height (Y) | Depth (Z) | | --- | --- | --- | --- | | T-Shirt, Shirt, Sweater, Jacket, Pants, Shorts, Dress & Skirt | 8 | 8 | 8 | | Eyebrow and Eyelashes | 1.5 | 0.5 | 0.5 | ### Attachment Points `Class.Attachment` objects indicate where an accessory model attaches to a point on a character body. Whether you are creating rigid or [layered](/docs/en-us/avatar/layered-accessories.md) accessories, Studio's [Accessory Fitting Tool](/docs/en-us/avatar/accessory-fitting-tool.md) (AFT) automatically adds and configures the appropriate `Class.Attachment` with the following specifications: - **One attachment** - Each accessory, including layered clothing, require at least one attachment point to its associated body part. - **Naming Convention** - The `Class.Attachment` name must follow a specific naming convention depending on the `Class.Accessory.AccessoryType`. The AFT generates an appropriate `Class.Attachment` name automatically. If setting attachment names manually in Studio, use the following `Class.Attachment` name for each accessory type: | Accessory Type | Attachment Name | | --- | --- | | Hat | `HatAttachment` | | Back | `BodyBackAttachment` | | Waist | `WaistFrontAttachment`, `WaistCenterAttachment`, `WaistBackAttachment` | | Shoulder | `RightShoulderAttachment`, `RightCollarAttachment`, `NeckAttachment`, `LeftCollarAttachment`, `LeftShoulderAttachment` | | Face, Eyelash, Eyebrow | `FaceFrontAttachment`, `FaceCenterAttachment` | | Neck | `NeckAttachment` | | Front | `BodyFrontAttachment` | | Layered tops (Shirt, TShirt, Sweater, Jacket) | `BodyFrontAttachment` | | Layered bottoms (Pants, Shorts, DressSkirt) | `WaistCenterAttachment` | > **Info:** The Importer automatically recognizes mesh objects as attachment points if the objects include the affix `\_Att`. This only applies when importing meshes with caging data, such as clothing or bodies. - **Shoulders and Collars** - Even though they are in similar locations, Shoulder and Collar attachment points interact with character rigs differently for rigid accessories. - Items using `RightShoulderAttachment` or `LeftShoulderAttachment` move with the character's arm. - Items using `RightCollarAttachment` or `LeftCollarAttachment` do not move with the character's arm. ### Face Accessories Face accessories, such as hair, eyebrows, and eyelashes are unique accessories that you can bundle with an avatar body upload. At this time, eyebrows and eyelashes can not be uploaded as standalone accessories and must be bundled with avatar bodies. For more information on bundling your face accessories with avatar models, see [Publishing bodies with eyelashes and eyebrows](/docs/en-us/art/accessories/publish-eyebrows-eyelashes.md). - **Naming Convention when bundled** - When including these assets with an avatar body upload, the accessory objects must use the following name conventions: - `EyebrowAccessory` - `EyelashAccessory` - `HairAccessory` ## Textures - Textures for Marketplace assets can't exceed 2048x2048 resolution. - Textures created for accessories must meet Roblox's [texture specifications](/docs/en-us/art/modeling/texture-specifications.md). High resolution textures are automatically converted to lower-resolution textures to optimize performance. ## Layerable Properties Clothing and accessories that deform and fit around any characters and existing clothing items require additional configuration in a 3D modeling software such as [Blender](https://www.blender.org) or [Maya](https://www.autodesk.com/products/maya/overview). To achieve the layering effect, your clothing must meet the following requirements: - Asset must be [weighted and bound](#rigging-and-skinning) to an R15 skeleton (Maya) or armature (Blender). - Asset must contain an [inner mesh cage](#inner-cage) and an [outer mesh cage](#outer-cage). - Asset must continue to follow any applicable [custom mesh requirements](/docs/en-us/avatar/character-bodies/specifications.md), such as best practices on watertightness, textures, and polycount budgets. See [Creating Layered Models](/docs/en-us/art/accessories/creating.md) for a basic guide on applying these requirements on a reference asset in Blender. Once the `.fbx` file is [exported](/docs/en-us/avatar/rigid-accessories/export.md), see [Accessory Fitting Tool](/docs/en-us/avatar/accessory-fitting-tool.md) for instructions on creating an accessory from your model. ### Rigging and Skinning Rigging and skinning a layered accessory allows the accessory to move naturally with a character body. You can perform this manually through a modeling tool, or [use automatic skinning transfer](/docs/en-us/avatar/automatic-skinning-transfer.md) to generate an accessory's skinning data at run time. If using modeling software to skin your accessories, keep in mind that **Joint Influences** (Maya) or **Bone Assignments** (Blender) per vertex should be limited to **4**. For more information on basic skinning in third-party modeling software, such as Blender's [Automatic Weights](https://docs.blender.org/manual/en/latest/animation/armatures/skinning/parenting.html#with-automatic-weights), see [Skinning a Simple Mesh](/docs/en-us/art/modeling/skin-a-simple-mesh.md) for instructions on rigging, applying weights, and skinning a basic mesh. ### Cage Meshes **Cage meshes**, or **cages**, are invisible meshes that define the inner and outer surfaces of your asset and are fundamental to the layerable properties of clothing items. The inner cage determines the inside surface of a clothing item while the outer cage determines the outside surface of a clothing item. > **Error:** Assets with invalid cage configurations may fail validation and are subject to moderation. See [Caging best practices](/docs/en-us/avatar/layered-accessories/caging-best-practices.md) for examples and instructions. For a basic overview on caging, see the [Basic Clothing Tutorial](/docs/en-us/art/accessories/creating/caging-setup.md) and the relevant section of the tutorial video at [8:32](https://www.youtube.com/watch?v=C-DwGRBHvmE&t=512s): #### Inner Cage The inner cage is the inner surface of your model and defines how the layered asset fits over another layered model. As a best practice, the shape of the inner and outer cage should match each other before editing the outer cage to completely cover your asset. The inner cage mesh object must have the same name as the accessory model appended with **_InnerCage**. #### Outer Cage The outer cage defines the external surface that another item's inner cage would layer on top of. Avatar character models must have an outer cage included with its model in order to be compatible with layered clothing. All avatar models available on the [Marketplace](https://www.roblox.com/catalog) include a properly configured outer cage and are compatible with layered assets. The outer cage of a layered clothing asset is a mesh that precisely covers the clothing item. The outer cage included in the template files is identical to the inner cage by default and must be the only cage adjusted to fit over an accessory. The outer cage mesh object must have the same name as the accessory model appended with **_OuterCage**. > **Warning:** The vertexes and UVs of the inner and outer cage meshes should not be deleted or removed, as they are used to match coordinates between other cages. ## Marketplace Requirements Your items must meet the following requirements before you upload them to the Marketplace to sell: - Ensure that your items adhere to the [Marketplace Program Guidelines](/docs/en-us/marketplace/marketplace-policy.md). - Whenever applicable, ensure that your items adhere to Roblox's [custom mesh specifications](/docs/en-us/art/modeling/specifications.md) and [rigid accessory specifications](/docs/en-us/avatar/rigid-accessories.md). - Object `Class.MeshPart.Material|Material` is set to `Plastic`. - Object `Class.MeshPart.Transparency|Transparency` is set to 0. - Object `Class.MeshPart.VertexColor|VertexColor` is the default `1, 1, 1`. - Your `Class.Accessory` instance does not contain extraneous objects, like `Class.Script` or additional `Class.Part` instances. --- title: "Import into Studio" url: /docs/en-us/avatar/makeup/import last_updated: 2026-06-29T19:33:57Z description: "Makeup is a cosmetic look made up of multiple facial textures and optional eyebrow and eyelashes." --- # Import into Studio If you are following the [common practice](/docs/en-us/avatar/makeup.md#creation-process) of authoring makeup changes in a third-party tool, use the following import instructions to get your custom makeup textures into Roblox Studio: > **Info:** These instructions apply specifically to the Makeup test place, available in the [Makeup downloadable resource](https://github.com/Roblox/avatar/tree/main/Makeup), which uses a script to programmatically apply a Decal to the face of the associated character 1. Before importing into Studio, ensure that your `.fbx` or `.gltf` template file is [correctly configured](/docs/en-us/avatar/makeup/specifications.md) with the following: - Target head mesh - Target head cage mesh - If modified, `Lips` mesh and associated textures. - If modified, `Face` mesh and associated textures. - If modified, `Eyes` mesh and associated textures. 2. In Studio, open up the Makeup test place. 3. Click **File** > **Import** and select your 3D makeup asset. Verify there are no errors with the imported components. 4. Click **Import** to add the file into Studio. A new `Model` populates in the workspace. 5. In the **Explorer** window, expand the new `Model`. Verify that `Decal` child objects have populated, each with their own `WrapTextureTransfer` child object._On import, makeup decals appear within the Model.__Each decal parents a WrapTextureTransfer object_ 6. In the test place, copy your new `Decal` objects into a character model's `Makeup` folder. 7. If you want to apply eyebrows or eyelashes, move those `MeshParts` into the character's `Accessories` folder. For this place file, you do not need to convert eyelashes and eyebrows into Accessories first. 8. Press **Play** to playtest and verify that the character has the new makeup applied. ## Test with Avatar Setup You can also use the Avatar Setup tool to preview makeup assets without playtesting. 1. Open the Avatar Setup tool by navigating to **Avatar** > **Setup**. 2. Select the model you would like to preview your assets on. 3. **For makeup decals,** such as eyes, face, and lips: 1. In the **Explorer** window, select any makeup decal assets you want to add into the Avatar Setup preview. The preview only supports valid makeup decals with a child `Class.WrapTextureTransfer`. 2. In the Avatar Setup panel, click the **+** symbol to add the assets to the Avatar Setup tool. 3. In the new prompt, specify the makeup asset type (eyes, face, lips), for each decal selected. 4. **For makeup meshes**, such as eyebrows and eyelashes, in the **Explorer** window, move the `Class.MeshPart` objects as children within the avatar model, then click the **+** button to add them to Avatar Setup. 5. In the Avatar Setup panel, navigate to the **Face** > **Makeup** tab and validate that your assets were properly imported into the tool. 6. Click on assets to equip them in your preview. 1. Change the order of the assets by dragging their icons on the right side of the preview box. 2. Click **X** to remove them from your avatar preview. 7. To see how makeup animates, navigate to the **Animations** tab, and test various sequences to see what your avatar looks like with the makeup applied. For the best results, use the avatars provided from Roblox in the test place. You may also bring in any other avatar, but the quality of the makeup fit depends on the head's underlying cage, which may result in varied appearance if not properly aligned. --- title: "Makeup" url: /docs/en-us/avatar/makeup last_updated: 2026-06-29T19:33:57Z description: "Makeup is a cosmetic look made up of multiple facial textures and optional eyebrow and eyelashes." --- # Makeup **Makeup** is a cosmetic item that you can apply to a face of an avatar or non-playable character for additional creative expression, such as traditional makeup art (e.g., eyeshadow, lipstick, and blush), face paint, battle markings, and camouflage. Makeup is made up of multiple specialized texture layers baked into a `Class.Decal` object, and each texture layer contains a unique makeup component like the lips or eyes region. Players can purchase makeup as a set on the [Marketplace](https://www.roblox.com/catalog) and apply it as a complete cohesive look, or swap out individual makeup components for others that match their own avatar's style. _Complete look_ _Eyes texture layer_ _Lip texture layer_ _Face texture layer_ This guide provides several [makeup templates](#resources) that you can use within your creation process, such as: - Template textures that you can use in 2D editing software to author makeup. - Template heads that you can use to view and test your makeup before export. - Reference experience you can use to import and test your makeup assets on characters with different skin tones and body types. You can either use these templates as a pure reference to see how everything works, or you can use all of them at each step of the suggested creation process. _Reference template render_ _Reference template data model_ ## Makeup components You can apply textures to three primary regions of the face: the eyes region, lips region, and face region. Each region has overlapping surfaces with other regions to allow for seamless, continuous looks between makeup components. _Eyes region_ _Lips region_ _Face region_ In addition, you can include eyebrows and eyelashes with your makeup creations. Eyebrows and eyelashes are `Class.Model` objects that contain `Class.MeshPart` objects with skinning and caging data. This allows them to animate properly as the character's face changes expressions. _Eyebrow and eyelash mesh objects_ _Eyebrow and eyelash data model_ When you import your makeup creation from your favorite third-party modeling software into the [reference experience](#reference-experience), Studio automatically detects mesh names, then generates the necessary `Class.Decal` objects under the imported model. You can then quickly apply your makeup `Class.Decal` objects and accessories by dragging them from the imported reference head into a character's **Makeup** or **Accessories** folder. _The imported model contains your makeup decals and face accessories_ _Your makeup displays at runtime when you drag your makeup assets into the correct folders_ > **Info:** Makeup assets that you create for the [Marketplace](https://www.roblox.com/catalog) can include up to 6 total `Class.Decal` objects, comprised of any combination of regions. ## Resources The following are downloadable resources you can use within the creation process: - See [Template textures](#template-textures) for image and project files you can use in 2D editing software like GIMP or Photoshop. - See [Template heads](#template-heads) for required 3D models you can use to view and test your makeup assets. - See [Reference experience](#reference-experience) to import and test your makeup assets into Studio > **Info:** For resources that include two templates, in almost every case **it's recommended to use the mesh template** to create makeup because: - It's easier and more intuitive to use because it mimics applying makeup in the real world. - It contains a higher-fidelity mesh and UVs with minimal distortion. > > Some creators prefer using the cage template because there is only one transfer when projecting the texture, while there are two transfers before projecting the texture for the mesh template. This can result in makeup transferring more accurately with less artifacts. > **Warning:** Be sure to review your options **before** starting to create makeup because you cannot start with one template, then transfer your makeup to the other. ### Template textures When you are finished making changes to any of the following template texture images: 1. Export your new textures.** Exporting from Photoshop** > **Info:** The video presenter uses **File** > **Export** > **Layers to Files** to export each layer as its own `.png` file.** Exporting from Substance Painter**** Exporting from Procreate** To export textures from Procreate: 1. Navigate to the **Layers** pane, then hide everything but your first makeup component layer, such as your eyes, lips, or face layer. 2. With your makeup component layer active, navigate to the **Actions** menu > **Share** tab > **Share Textures - PNG** to save all of your makeup component's textures to a location on your device. 3. Delete all of the head, eyebrows, and eyelash textures. 1. Open the location where you saved all of your makeup component's textures. 2. Delete everything except for the following PBR textures: - `TransferTarget_FullHead_Mat-Color` - `TransferTarget_FullHead_Mat-Metallic` - `TransferTarget_FullHead_Mat-Roughness` 4. Replace your makeup component's `TransferTarget_FullHead_Mat-Color` layer with a version that will export transparency in the output texture. 1. Copy your active makeup component layer. 2. Navigate to the **Gallery** > **Plus** icon > **Clipboard**. A new Procreate file displays with your makeup component layer. 3. In the **Layers** pane, disable the **Background color** layer. Your makeup component displays without a background. 4. With your makeup component layer active, navigate to the **Actions** menu > **Share** tab > **Share Image - PNG**. 5. In the file browser, navigate to the location on your device with all of your makeup component's textures, then save/override the `TransferTarget_FullHead_Mat-Color` texture. 5. Rename your makeup component's PBR textures appropriately so that you can easily reference each makeup component's PBR data when you reassign your textures in Blender or Maya. For example, if you are exporting texture maps for lips: - `Lips-Color` - `Lips-Metallic` - `Lips-Roughness` 6. Repeat this process for every makeup component you want to import for your makeup look. 2. [Reassign the preset textures](/docs/en-us/avatar/reassign-textures.md) in a [template head](#template-heads) with your new textures. 3. [Import the reference head](/docs/en-us/avatar/import.md) with your new textures into Studio. Photoshop / GIMP texture images A comprehensive `.zip` folder with realistic texture images for use in Photoshop or your preferred image editor. [Mesh Template](../../assets/makeup/resources/Photoshop-Gimp-Realistic-Mesh.zip) [Cage Template](../../assets/makeup/resources/Photoshop-Gimp-Realistic-Cage.zip) Substance Painter files A comprehensive `.zip` folder with realistic texture files for use in Substance Painter, an industry-standard PBR authoring software. [Mesh Template](../../assets/makeup/resources/SubstancePainter-Realistic-Mesh.zip) [Cage Template](../../assets/makeup/resources/SubstancePainter-Realistic-Cage.zip) Procreate texture images A comprehensive `.zip` folder with realistic texture images for use in Procreate. [Mesh Template](../../assets/makeup/resources/Procreate-Realistic-Mesh.zip) [Cage Template](../../assets/makeup/resources/Procreate-Realistic-Cage.zip) Photoshop / GIMP texture images A comprehensive `.zip` folder with blocky texture images for use in Photoshop or your preferred image editor. [Mesh Template](../../assets/makeup/resources/Photoshop-Gimp-Blocky-Mesh.zip) Substance Painter files A comprehensive `.zip` folder with blocky texture files for use in Substance Painter, an industry-standard PBR authoring software. [Mesh Template](../../assets/makeup/resources/SubstancePainter-Blocky-Mesh.zip) Procreate texture images A comprehensive `.zip` folder with blocky texture images for use in Procreate. [Mesh Template](../../assets/makeup/resources/Procreate-Blocky-Mesh.zip) ### Template heads Each template head includes: - Target head mesh object or head cage mesh object - Eye, lip, and multiple face region mesh objects, fully linked to reference PBR textures - Eyebrows and eyelash mesh objects > **Info:** The Blender and Maya templates also include a facial region reference `.png` file that you can use to reference how your makeup assets fit into the eye, lips, and face/cheek regions. Blender template reference files A comprehensive `.zip` folder of Blender assets, including multiple realistic reference heads pre-configured for testing in the Makeup test place or for authoring your own makeup. [Mesh Template](../../assets/makeup/resources/Blender-Realistic-Mesh.zip) [Cage Template](../../assets/makeup/resources/Blender-Realistic-Cage.zip) Maya template reference files A comprehensive `.zip` folder of Maya assets, including multiple realistic reference heads pre-configured for testing in the Makeup test place or for authoring your own makeup. [Mesh Template](../../assets/makeup/resources/Maya-Realistic-Mesh.zip) [Cage Template](../../assets/makeup/resources/Maya-Realistic-Cage.zip) FBX / GLTF template reference files A comprehensive `.zip` folder of `.fbx` and `.gltf` assets, including realistic heads with various example makeup looks that are ready to be implemented into Studio for testing purposes. [Mesh Template](../../assets/makeup/resources/FBX-GLTF-Realistic-Mesh.zip) [Cage Template](../../assets/makeup/resources/FBX-GLTF-Realistic-Cage.zip) Blender template reference files A comprehensive `.zip` folder of Blender assets, including multiple blocky reference heads pre-configured for testing in the Makeup test place or for authoring your own makeup. [Mesh Template](../../assets/makeup/resources/Blender-Blocky-Mesh.zip) Maya template reference files A comprehensive `.zip` folder of Maya assets, including multiple blocky reference heads pre-configured for testing in the Makeup test place or for authoring your own makeup. [Mesh Template](../../assets/makeup/resources/Maya-Blocky-Mesh.zip) FBX / GLTF template reference files A comprehensive `.zip` folder of `.fbx` and `.gltf` assets, including blocky heads with various example makeup looks that are ready to be implemented into Studio for testing purposes. [Mesh Template](../../assets/makeup/resources/FBX-GLTF-Blocky-Mesh.zip) ### Reference experience Reference place file (download) At this time, you can only test makeup on the downloadable placefile. In Github, select the download icon on the right-side to download the `MakeupBeta.zip` file. [Github link](https://github.com/Roblox/avatar/blob/main/Makeup/MakeupBeta.zip) ## Creation process > **Warning:** This process assumes that you are proficient in 3D modeling software, such as Blender or Maya, and that you understand how to update, replace, export, save, and alter materials within your modeling tool and any additional texturing software or plugins. _Download the 3D reference template head and texture templates applicable to your workflow._ _Modify the texture images in the tools of your choice._ _In Blender or Maya, replace the textures in the template with your new textures and export._ While you can easily swap `Class.Decal` objects in Studio to make a quick change to a character's makeup, the following instruction is best practice for implementing PBR-based textures into Roblox makeup at scale. This process requires: - The [Makeup test place and template files](#resources) - A 3D modeling tool such as [Blender](https://www.blender.org) or [Maya](https://www.autodesk.com/products/maya/overview) - Any PBR texturing software, such as [Substance Painter](https://www.adobe.com/products/substance3d/apps/painter.html), [Materialize](http://boundingboxsoftware.com/materialize/index.php), or plugins like [Ucupaint](https://extensions.blender.org/add-ons/ucupaint/) (Blender). As always with 3D art and creation workflows, there are many ways to achieve a specific goal, and the makeup and PBR publishing flow is no exception. It's recommended to try the workflow in this guide first, then adjust as necessary for your own creation requirements: 1. Download a [reference template head](#resources) and open it in the general modeling software of your choice. 2. Using your texturing tools or image editing software, modify the texture images associated with the different regions: `TransferTexture_Eyes`, `TransferTexture_Face`, or `TransferTexture_Lips`. 3. When you've created your new texture images, use Blender or Maya to reassign the existing file textures in the template. For more information, see [Reassign textures](/docs/en-us/avatar/reassign-textures.md). 4. Review [Face caging best practices](/docs/en-us/avatar/dynamic-heads/caging-best-practices.md) for guidance on how to adjust face cages so that your makeup displays correctly for your use case. 5. Export the entire template file as a `.fbx` or `.gltf`. All 3D modeling [export requirements](/docs/en-us/art/modeling/export-requirements.md) apply, but there are a few makeup-specific settings you must configure: - If you are using Blender, 1. In the export file browser window, navigate to the **Include** section, then enable the **Custom Properties** toggle for makeup UV data transfer purposes. 2. In the **Armature** section, enable **Only Deform Bones** to ensure your export file includes skinning data for eyebrows and eyelashes._All necessary Blender export settings for makeup_ - If you are using Maya, in the export file browser window, navigate to the **Deformed Models** section, then enable the **Skins** toggle to ensure your export file includes skinning data for eyebrows and eyelashes. 6. [Import](/docs/en-us/avatar/import.md) the template file into Studio. Studio automatically detects your mesh object names and associated textures, and generates `Decal` objects. 7. Apply any makeup decals or eyebrow and eyelash accessories to characters in the test place by dropping makeup assets in the appropriate character's `Makeup` or `Accessories` folder. 8. Click the **Play** button, then navigate to the character with new makeup assets to preview. --- title: "Reassign textures" url: /docs/en-us/avatar/makeup/reassign-textures last_updated: 2026-06-29T19:33:57Z description: "To import custom makeup, replace the existing textures on the head template with your custom textures." --- # Reassign textures To import custom makeup textures, use Blender or Maya to reassign the existing textures on the reference template head to your custom texture images. After replacing the textures, export the entire head model, then [import](/docs/en-us/avatar/makeup/import.md) it into Roblox Studio. > **Warning:** The following information is **specific for applying makeup textures to the Roblox provided head reference**, which already has PBR texture nodes assigned. For more information on assigning textures from scratch, see [Assign textures in Blender and Maya](/docs/en-us/art/modeling/assign-textures.md). ## Blender > **Info:** The video presenter opts to clear all existing image links by navigating to **File** > **External Data** > **Unpack Resources** and selecting **Remove Pack**. This is a method to unassign all images instead of individually unassigning textures like step #5 in the following instructions. To reassign textures in Blender: 1. Unzip and open the [Blender reference head file](/docs/en-us/avatar/makeup.md#template-heads). 2. Switch to the **Shading** tab. 3. In the Outliner, expand the Makeup collection and select the face region you intend to replace textures for. 4. In the node graph, select the texture image you intend to update, such as `Neutral_FaceDecal_ALB.png` 5. Click the **X** to remove the existing image assignment. 6. Click the folder icon to browse for a new image. 7. Repeat for all PBR textures that you intend to replace. 8. Delete any texture nodes you are not using. 9. Verify your final node graph looks similar to the original reference with nothing unintentionally missing. > **Info:** After replacing your PBR textures, export the entire template file as a `.fbx`or `.gltf`. All 3D modeling [export requirements](/docs/en-us/art/modeling/export-requirements.md) apply, but there are a few makeup-specific settings you must configure: 1. In the export file brower window, navigate to the **Include** section, then enable the **Custom Properties** toggle for makeup UV data transfer data purposes. 2. In the **Armature** section, enable **Only Deform Bones** to ensure your export file includes skinning data for eyebrows and eyelashes._All necessary Blender export settings for makeup_ ## Maya To reassign textures in Blender: 1. Unzip and open the Maya [reference head file](/docs/en-us/avatar/makeup.md#template-heads). 2. In the Status Line near the top of the default layout, click on the Hypershade icon. 3. Move your mouse over the material's texture you intend to replace. 4. Click and hold right mouse-click and drag the selector to **Graph Network**. 5. In the newly displayed node graph, click on the Texture Node you intend to replace, such as `Neutral_Eyes_ALB.png`. 6. In the right Property Editor, navigate to Image Name and click the folder icon. A file browser displays. 1. Select the texture image file you want to use as a replacement. 2. Repeat for all PBR textures that you intend to replace. 3. Delete any texture nodes you are not using. 4. Verify your final node graph looks similar to the original reference with nothing unintentionally missing. > **Info:** After replacing your PBR textures, export the entire template file as a `.fbx`or `.gltf`. All 3D modeling [export requirements](/docs/en-us/art/modeling/export-requirements.md) apply, but there are a few makeup-specific settings you must configure: 1. In the export file brower window, navigate to the **Deformed Models** section. 2. Enable the **Skins** toggle to ensure your export file includes skinning data for eyebrows and eyelashes. --- title: "Makeup specifications" url: /docs/en-us/avatar/makeup/specifications last_updated: 2026-06-29T19:33:57Z description: "Makeup is a cosmetic look made up of multiple facial textures and optional eyebrow and eyelashes." --- # Makeup specifications Makeup is a unique avatar asset that converts a combination of textures into a single `Decal` object that is applied over a character's face. Makeup can also include optional eyebrows and eyelashes, which are MeshPart accessories that include their own textures. > **Warning:** **If creating other types of 3D models:**- For generic meshes, see [general mesh specifications](/docs/en-us/art/modeling/specifications.md) and [general export settings](/docs/en-us/art/modeling/export-requirements.md). - For layered accessories, see [layered accessory specifications](/docs/en-us/avatar/layered-accessories/specifications.md) and [layered export settings](/docs/en-us/avatar/layered-accessories/export.md). - For avatar characters, see [avatar specifications](/docs/en-us/avatar/character-bodies/specifications.md) and [avatar export settings](/docs/en-us/avatar/character-bodies/export.md). ## General If you are following the best practice creation process, ensure that your template file submission includes the following correctly configured and named objects: - A **head mesh object** and associated **cage mesh object**. - These two objects must include the prefix "TransferTarget_" in their name. - **TransferTexture mesh objects** for each region: `Eyes`, `Face`, `Lips`. - These three objects must follow the following naming conventions. - `TransferTexture_Eyes_01` - `TransferTexture_Lips_01` - `TransferTexture_Face_01` - Any for multiple regions, increment the affix number: `TransferTexture_Face_02` - Optional [eyebrows and eyelashes](#optional-eyebrows-and-eyelashes). > **Warning:** The [provided Roblox makeup template head](https://github.com/Roblox/avatar/tree/main/Makeup) should already be properly configure with the correct objects and naming convention. To prevent issues with import, avoid renaming or deleting the data model of the template head when creating your own makeup. ## Decals and textures All textures created for accessories must meet Roblox's [texture specifications](/docs/en-us/art/modeling/texture-specifications.md), but makeup textures includes two addtional considerations: - There is a 4K texture limit for [in-experience](/docs/en-us/avatar/in-experience-creation.md) makeup creation. - There is a 1K texture limit for makeup that you upload to the [Marketplace](https://www.roblox.com/catalog). For additional information on PBR textures, see [PBR textures](/docs/en-us/art/modeling/surface-appearance.md). ## (Optional) Eyebrows and eyelashes Eyebrows and eyelashes are layered accessories, meaning that they must meet [layered accessory requirements](/docs/en-us/avatar/layered-accessories/specifications.md), notably: - **Cage data** - Eyebrows and eyelashes each require their own inner and outer cage. - Eyebrows and eyelashes can also take advantage of automatic skinning, specifically with [special skinning joints](/docs/en-us/avatar/automatic-skinning-transfer.md#special-skinning-transfer-joints). - **Geometry size** - Eye accessories can't exceed [maximum dimensions](/docs/en-us/avatar/layered-accessories/specifications.md) for their asset type. - **Budgets** - Eye accessories can't exceed 4k triangles each. --- title: "Resources" url: /docs/en-us/avatar/resources last_updated: 2026-06-29T19:33:57Z description: "Get all the resources you need to build characters and accessories." --- # Resources The following downloadable files are available for your use: - [References](#references) — Finished assets you can use as reference. - [Auto-Setup References](#auto-setup-references) — Assets that meet specific avatar auto-setup requirements. - [Project Files](#project-files) — Fundamental resources like rigs and cages for building your own assets. - [Mannequins](#mannequin-models) — Models you can use as sizing reference. - [Templates](#templates) — Prebuilt characters that require small additional customization. - [Add-ons and Tools](#add-ons-and-tools) — Third-party tools and add-ons. ## References ### Higher-fidelity rigs The following [higher-fidelity](/docs/en-us/avatar/character-bodies/specifications.md#higher-fidelity-rigs) humanoid rigs include additional bones or joints for a higher level of realism with articulated hands, shoulders, and spine movements. Mannequin A comprehensive `.zip` folder of a AA avatar-ready character body, including a dynamic head, fingers, layered clothing, and 2K PBR textures. [Download](../assets/art/reference-files/Mannequin-HigherFidelity.zip) Robuta A comprehensive `.zip` folder of an anime-style, avatar-ready character body, including a dynamic head, fingers, layered clothing and rigid accessory assets, and 2K PBR textures. [Download](../assets/art/reference-files/Robuta-HigherFidelity.zip) HipToBeSquare A comprehensive `.zip` folder of a blocky-style, avatar-ready character body, including a dynamic head, fingers, layered clothing and rigid accessory assets, and color, roughness, and metal PBR textures. [Download](../assets/art/reference-files/HipToBeSquare-HigherFidelity.zip) Roxie A comprehensive `.zip` folder of an avatar-ready character body, including a dynamic head, fingers, layered clothing and rigid accessory assets, and PBR textures. When you import this model, set **Rig Scale** to **Rthro Narrow**. [Download](../assets/art/reference-files/Roxie-HigherFidelity.zip) ### Standard rigs The following [standard](/docs/en-us/avatar/character-bodies/specifications.md#standard-rigs) humanoid rigs include the standard amount of bones or joints. Finished assets like Nature Girl and Stylish Male include clothing and accessory items that may cause validation errors unless they are removed. Nature Girl A comprehensive `.zip` folder of an avatar-ready character body, including clothing and rigid accessory assets. Each asset includes its own Blender and Maya project file, as well as their respective PBR textures. If you are opening these files in Blender, the armature bones may display in an unexpected orientation because the source model was originally exported from Maya. This different orientation doesn't affect the performance of the armature rig. [Download](../assets/art/reference-files/NatureArcherGirl.zip) Stylish Male A comprehensive `.zip` folder of an avatar-ready character body, including clothing and rigid accessory assets. Each asset includes its own Blender and Maya project file, as well as their respective PBR textures. If you are opening these files in Blender, the armature bones may display in an unexpected orientation because the source model was originally exported from Maya. This different orientation doesn't affect the performance of the armature rig. [Download](../assets/art/reference-files/StylizedMaleWithClothing.zip) Fish Person A rigged and skinned humanoid character model with a full body cage, facial animation rig, and associated PBR texture maps. If this asset were to be uploaded to the Marketplace, the bulb antenna must be a separate head accessory and not part of the character's head geometry. [Download](../assets/avatar/dynamic-heads/reference-files/Fish-Person.zip) Goblin A rigged and skinned humanoid character model with a full body cage, facial animation rig. [Download](../assets/avatar/dynamic-heads/reference-files/GoblinCharacter.zip) Blocky A Blocky character model with an animatable head and a full body cage. [Download](../assets/avatar/dynamic-heads/reference-files/BlockyCharacter.fbx) Lola A skinned R15 character created from the [Skinning a Humanoid Model](/docs/en-us/art/modeling/skin-a-humanoid-model.md) guide. Since this reference model doesn't yet have [inner and outer cage mesh data](/docs/en-us/avatar/character-bodies/specifications.md#inner-and-outer-cages), this model can't equip layered clothing or accessories. [Download](../assets/modeling/skinned-meshes/Lola.fbx) Tshirt - Uncaged Uncaged example clothing ready for caging in a 3D modeling software. [Download](../assets/accessories/reference-files/Tshirt-model.fbx) Clothing Examples Caged 3D accessory models and associated PBR textures. Ready for import into Studio or in a modeling tool. [Download](../assets/accessories/reference-files/Additional-FBX-assets.zip) Caging Examples Additional caged clothing items from How to cage Roblox's 3D [clothing video guide](https://www.youtube.com/watch?v=QwZaA9Gc-WQ). [Download](../assets/accessories/reference-files/Caging-examples.zip) ## Auto-setup references The following assets are designed for the specific [auto-setup requirements](/docs/en-us/avatar-setup.md#model-requirements). If uploaded as-is, these assets will fail normal validation. Nature Girl - Auto-Setup A comprehensive `.zip` folder of an [Avatar Auto-Setup](/docs/en-us/avatar-setup.md#avatar-auto-setup) ready character model, including clothing and rigid accessory assets and PBR texture assets. This Auto-Setup template is not compatible with the traditional avatar creation workflow. [Download](../assets/art/reference-files/NatureArcherGirl-AutoSetup.zip) Stylish Male - Auto-Setup A comprehensive `.zip` folder of an [Avatar Auto-Setup](/docs/en-us/avatar-setup.md#avatar-auto-setup) ready character model, including the base body and associated PBR textures. This Auto-Setup template is not compatible with the traditional avatar creation workflow. [Download](../assets/art/reference-files/StylizedMale-AutoSetup.zip) ## Project files The following are base rig, attachment, and cage files for avatar item creation in third-party applications. Choose the appropriate type of project file for your use-case. These assets are not suitable for direct import into Studio. R15 Rig and Attachments Standard armature rig template for various software. Use this template for rigging **bodies** and **clothing** items. [Download (.blend, .ma, .fbx)](../assets/modeling/meshes/reference-files/Rig_and_Attachments_Templates.zip) Cages for Clothing Project files for creation, includes a full-body inner and outer cage mesh for creation of **clothing** items. [Download (.blend, .ma, .fbx)](../assets/modeling/meshes/reference-files/Clothing_Cage_Templates.zip) Cages for Bodies Project files for creation, includes the 15 individual body part cages required for caging your avatar **bodies**. [Download (.blend, .ma, .fbx)](../assets/modeling/meshes/reference-files/Body_Cage_Templates.zip) Combined Project Files Template file containing all content from previous templates, includes rig skeleton, body cages, attachment points. Use this template to rig and cage bodies and clothing. [Download (.blend, .ma, .fbx)](../assets/modeling/meshes/reference-files/Combined_Templates.zip) ## Mannequin models Use the following models when designing your avatar accessory and clothing items. You can use these assets as scale, body shape, and caging reference. These assets are not meant for direct upload into Studio and may fail import or validation. Classic Mannequin A blank mannequin using Roblox's [Classic](/docs/en-us/avatar/character-bodies/specifications.md#classic) avatar proportions. Use this reference to aid your creation process for accessories, clothing, and characters in third-party applications. The caged `.fbx` contains the individual outer body cages for the body and may not import correctly into Studio without modification. [Model](../assets/art/reference-files/ClassicMannequin.fbx) [With Cage](../assets/art/reference-files/ClassicMannequin_With-Cages.fbx) Rthro Mannequin A blank mannequin using Roblox's [Rthro Normal](/docs/en-us/avatar/character-bodies/specifications.md#normal) avatar proportions. Use this reference to aid your creation process for accessories, clothing, and characters in third-party applications. The caged `.fbx` contains the individual outer body cages for the body and may not import correctly into Studio without modification. [Model](../assets/art/reference-files/RthroMannequin.fbx) [With Cage](../assets/art/reference-files/RthroMannequin_With-Cages.fbx) Rthro Slender Mannequin A blank mannequin using Roblox's [Rthro Slender](/docs/en-us/avatar/character-bodies/specifications.md#slender) proportions. Use this reference to aid your creation process for accessories, clothing, and characters in third-party applications. The caged `.fbx` contains the individual outer body cages for the body and may not import correctly into Studio without modification. [Model](../assets/art/reference-files/RthroSlenderMannequin.fbx) [With Cage](../assets/art/reference-files/RthroSlenderMannequin_With-Cages.fbx) ## Templates Use these templates if following along the [Create characters with templates](/docs/en-us/art/characters/creating.md) tutorial content. These assets require additional steps in third-party tools to pass validation in Studio. > **Warning:** If you are using Roblox's avatar template files, you must perform the [cleanup steps](/docs/en-us/art/characters/creating/combine-head-geometry.md) in order for the assets to properly validate before publishing to the Marketplace. > **Warning:** Some of the template files include additional mesh assets for eyebrows and eyelashes. These eyebrows and eyelashes versions may not be fully compatible with the template body. For the latest information on eyebrows and eyelashes, see [Makeup](/docs/en-us/avatar/makeup.md#components-of-makeup). Blender and .fbx template files with pre-baked avatar components. See [Create with templates](/docs/en-us/art/characters/creating.md) for instructions. [Male](../assets/art/reference-files/RoundMale.zip) [Female](../assets/art/reference-files/RoundFemale.zip) Blender and .fbx template files with pre-baked avatar components. See [Create with templates](/docs/en-us/art/characters/creating.md) for instructions. [Male](../assets/art/reference-files/SquareMale.zip) [Female](../assets/art/reference-files/SquareFemale.zip) Blender and .fbx template files with pre-baked avatar components. See [Create with templates](/docs/en-us/art/characters/creating.md) for instructions. [Male](../assets/art/reference-files/MuzzleMale.zip) [Female](../assets/art/reference-files/MuzzleFemale.zip) Blender and .fbx template files with pre-baked avatar components. See [Create with templates](/docs/en-us/art/characters/creating.md) for instructions. [Male](../assets/art/reference-files/SemiRealisticMale.zip) [Female](../assets/art/reference-files/SemiRealisticFemale.zip) Blender and .fbx template files with pre-baked avatar components. See [Create with templates](/docs/en-us/art/characters/creating.md) for instructions. [Male](../assets/art/reference-files/AnimeMale.zip) [Female](../assets/art/reference-files/AnimeFemale.zip) Blender and .fbx template files with pre-baked avatar components. See [Create with templates](/docs/en-us/art/characters/creating.md) for instructions. [Download](../assets/art/reference-files/Caricature.zip) Blender and .fbx template files with pre-baked avatar components. See [Create with templates](/docs/en-us/art/characters/creating.md) for instructions. [Download](../assets/art/reference-files/StylizedHuman.zip) ## Add-ons and tools Use the following add-ons and tools to help aid your creation process. Some of these tools are not actively supported and may be incompatible with your hardware version. Blender Studio Plugin Open-source Blender Studio add-on that allows you to upload assets directly from Blender to Studio. [Link to Instructions](/docs/en-us/art/modeling/roblox-blender-plugin.md) Blender Validation Tool Blender add-on for verifying avatar technical compatibility before importing into Studio. [Link to Instructions](/docs/en-us/art/accessories/validation-tool.md) Blender Calisthenics Tool Blender add-on for checking skinning data on characters and clothing. [Link to Instructions](/docs/en-us/art/modeling/calisthenics-tool.md) --- title: "Export rigid accessories" url: /docs/en-us/avatar/rigid-accessories/export last_updated: 2026-06-29T19:33:57Z description: "Use the appropriate export settings in Maya and Blender to generate Studio-ready .fbx files." --- # Export rigid accessories Export your rigid accessory model as a `.fbx` or `.gltf` file to take advantage of all of Studio's 3D import features. These file types contain mesh and texture data you need to later [import](/docs/en-us/studio/importer.md) into Studio. > **Warning:** **If creating other types of 3D models:**- For generic meshes, see [general mesh specifications](/docs/en-us/art/modeling/specifications.md) and [general export settings](/docs/en-us/art/modeling/export-requirements.md). - For layered accessories, see [layered accessory specifications](/docs/en-us/avatar/layered-accessories/specifications.md) and [layered export settings](/docs/en-us/avatar/layered-accessories/export.md). - For avatar characters, see [avatar specifications](/docs/en-us/avatar/character-bodies/specifications.md) and [avatar export settings](/docs/en-us/avatar/character-bodies/export.md). > **Warning:** If you have any modifiers to your mesh or project objects, make sure to apply or delete them before export. ## Blender Blender allows you to export in `.fbx` or `.gltf` as well as other formats. If you are using `.fbx` export, familiarize yourself with [Blender's FBX scaling](/docs/en-us/art/blender.md#adjust-scale-fbx) to ensure that you successfully import the model into Studio at the correct scale. ### Export settings To export the `.fbx` file in Blender: 1. In the topbar, click **File**. A pop-up menu displays. 2. Select **Export**, then **FBX (.fbx)**. The **Blender File View** window displays. 3. On the right-hand side, change the **Path Mode** property to **Copy**, then toggle the **Embed Textures** button. 4. Set **Transform** > **Apply Scalings** to **Unit Scale**. For more information, see [scaling and scene units](/docs/en-us/art/blender.md#adjust-scale-fbx). 5. Click the **Export FBX** button. 6. After exporting, use Studio's [Importer](/docs/en-us/studio/importer.md) to import your model and the [Accessory Fitting Tool](/docs/en-us/avatar/accessory-fitting-tool.md) to convert the model into an accessory. ## Maya export settings To export a mesh in Maya as a `.fbx` file: 1. In the topbar, click **File**. A pop-up menu displays. 2. Select **Export All**. The **Export All** window displays. 3. Near the bottom of the window, click the **Files of type** dropdown, then select **FBX export**. 4. On the right-hand side of the window, navigate to the **Options...** section. 5. In the **Geometry** section, enable **Smooth Mesh** and **Referenced Asset Content**. 6. In the **Animation** section, disable **Animation**. 7. If you need to import textures as a `.png`, in the **Embed Media** section, enable **Embed Media**. 8. In the **Advanced Options** section, - Navigate to **Units**, then enable **Automatic**. - Navigate to **Axis Conversion**, then set the **Up Axis** property to **Y**. 9. Click the **Export All** button. 10. After exporting, use Studio's [Importer](/docs/en-us/studio/importer.md) to import your model and the [Accessory Fitting Tool](/docs/en-us/avatar/accessory-fitting-tool.md) to convert the model into an accessory. --- title: "Import rigid accessories" url: /docs/en-us/avatar/rigid-accessories/import last_updated: 2026-06-29T19:33:57Z description: "Use the Importer to add third-party models to Studio before using the Accessory Fitting Tool to convert the model to an Accessory." --- # Import rigid accessories Use the following instructions to [import](#import-3d-assets) your `.fbx` or `.gltf` third-party model into Studio and [convert](#convert-rigid-accessories) the asset to an `Accessory` object that you can save to use in your experience, share with others, or upload to the Marketplace. > **Info:** The following asset example and instructions are part of the [Rigid accessory tutorial](/docs/en-us/art/accessories/creating-rigid.md) which covers the process of converting a model from Blender to publishing the asset to the Marketplace. ## Import 3D assets Studio's Importer provides a quick and easy way to import third-party 3D assets into your projects. The importer provides object previews and error-checking to ensure that your asset meets Roblox's [general 3D requirements](/docs/en-us/art/modeling/specifications.md). Keep in mind, the model that you intend to create as a rigid accessory must also follow Roblox's [accessory specifications](/docs/en-us/avatar/rigid-accessories/specifications.md) to eventually use this asset as an `Class.Accessory`, or you may experience errors later in the workflow. To import your asset: 1. From Studio's **File** menu, select **Importer**. 2. In the file browser, select the `.fbx` or `.gltf` file saved locally. The Importer loads a preview of the object. - If textures don't load for your asset, you can manually import your textures later. - See [Importer](/docs/en-us/studio/importer.md) for additional information on import settings and troubleshooting. 3. Select **Import**. The asset populates in your workspace as a `Class.Model` with the appropriate textures applied as a `Class.SurfaceAppearance` or `Class.MeshPart.TextureID`. **Manually add textures** If textures didn't load correctly, add them manually. You may need to save and publish your experience in order to access the [Asset Manager](/docs/en-us/projects/assets/manager.md). 1. In the **Asset Manager**, click the **Import** button. 2. Upload your image files. 3. After moderation clears for your image, select the `Class.MeshPart` parented within your imported `Class.Model`. 4. Add a `Class.SurfaceAppearance` child to your `Class.MeshPart`. 5. In the `Class.SurfaceAppearance` properties, click each property value and assign the appropriate texture image from the asset dropdown: 1. Set the **ColorMap** to the **_ALB** texture image. 2. Set the **MetalnessMap** to the **_MTL** texture image. 3. Set the **NormalMap** to the **_NOR** texture image. 4. Set the **RoughnessMap** to the **_RGH** texture image. > **Success:** After successful import, your model object should populate in your project as a `Class.Model` with the appropriate textures applied. See [Importer](/docs/en-us/studio/importer.md) for additional information on import settings and troubleshooting. ## Convert rigid accessories After importing your asset into Studio, you can begin **fitting** your imported object to a mannequin and **converting** the `Class.Model` object into a `Class.Accessory`. When fitting and converting your accessory it's important to use the [Accessory Fitting Tool](/docs/en-us/avatar/accessory-fitting-tool.md) (AFT) to correctly preview the placement and apply the correct configurations to your accessory. To fit and generate your accessory: 1. In the toolbar's **Avatar** tab, click **Accessory** to open the AFT. 2. In the tool's panel, select the **Part** field and, in the workspace, select the accessory `Class.MeshPart` object in the workspace and press **Next**. 3. On the Asset Type page, select the **type** of asset and the expected **body scale**. Press **Next** when complete. 1. This example uses a **Hat** asset with an **Proportions Normal** scale. 2. Body scale is typically set based on the original sculpting and sizing of the asset. See [Body Scale](/docs/en-us/art/accessories/body-scale.md) for additional information on rigid accessory scaling. 4. On the preview screen, select one of the humanoid characters as a mannequin: 1. In the Avatars section, select a humanoid base body character. 2. In the preview panel, deselect the previous selection. Only the humanoid body displays in the preview window. 5. Using both the AFT preview window and the workspace, adjust the position, scale, and rotation of the accessory. 1. Use the AFT preview window and your mannequin as an accurate preview of how your asset fits on the character. The clothing mannequin in the workspace does not accurately portray how rigid accessories attach. 2. In the workspace, use the **Move**, **Scale**, and **Rotate** tools to adjust the positioning of your rigid accessory. 3. If you accidentally select something else, click back into the AFT panel to reselect the accessory and resume your adjustments using the transformation tools. 6. After previewing and fitting your asset, select **Generate MeshPart Accessory** to create the Accessory and add it to your explorer. After successful fitting and converting, your 3D model should populate in your project as an `Class.Accessory`. With this `Class.Accessory` you can perform any of the following: - Begin the process of [uploading and publishing](/docs/en-us/marketplace/publish-to-marketplace.md#upload-an-asset) the accessory to the Marketplace. - Use the accessory in your current experience by equipping it to character models with [HumanoidDescription](/docs/en-us/characters/appearance.md#manually-modify-appearance), or by dragging and dropping the accessory under the appropriate character `Class.Model` object. - Save the accessory to your [Toolbox](/docs/en-us/projects/assets/toolbox.md) or make it public on the [Creator Store](/docs/en-us/production/creator-store.md) to share or use within any of your experiences. --- title: "Rigid accessories" url: /docs/en-us/avatar/rigid-accessories last_updated: 2026-06-29T19:33:57Z description: "Rigid accessories are basic 3D cosmetics users can equip and wear on their avatar character." --- # Rigid accessories A high-level overview of accessories on Roblox. Create and sell your first basic accessory. Rigid accessories are the most basic 3D cosmetic items that users can equip and wear on their avatar character, such props, weapons, hats, and more. Unlike [clothing accessories](/docs/en-us/layered-accessories.md) that stretch and fit over a character body, rigid accessories attach to a specific point on an avatar character and don't deform or wrap over a target. To create a custom Roblox accessory for your own experience or to sell on the Marketplace, it's important to start with the following: - A basic background with 3D modeling tools such as [Blender](https://www.blender.org/) or [Maya](https://www.autodesk.com/products/maya/overview). - An understanding of the [components that make up a rigid accessory](#components-of-a-rigid-accessory). - An understanding of the general [accessory creation process](#creation-process). - Review Roblox's official tutorials to create your own accessories: - [Rigid accessory creation tutorial](/docs/en-us/art/accessories/creating-rigid.md) - covers each process required for converting a 3D model to a rigid accessory and publishing it to the Marketplace. - [Clothing creation tutorial](/docs/en-us/art/accessories/creating.md) - a step-by-step process of creating your own avatar-ready clothing from scratch in Blender. - [Additional tools, resources, and guides](#resources) provided by Roblox to standardize and expedite the creation process. ## Components of a rigid accessory All accessory models are made up of the same base components of a [mesh object](#mesh-part), [textures](#textures), and [attachment](#attachments). When [creating accessories](#creation-process), most of these components are created first in your modeling software, then converted to their appropriate Roblox Studio instance on import. ### Mesh part _Bow rigid accessory mesh object_ _T-shirt layered clothing mesh object_ > **Warning:** Clothing, such as the t-shirt, require [additional clothing components](/docs/en-us/layered-accessories.md) to apply the layerable effect to the 3D object. All accessories require a single mesh object that represents the geometry of the accessory object. In Studio, this mesh object is represented as a `Class.MeshPart` nested under a single `Class.Model`. ### Textures _2D texture map for the t-shirt model_ _T-shirt model with texture applied_ Textures are 2D image files that define the surface appearance of your 3D object. You can create textures within a texture painting program or a 3D modeling software. In Studio, textures images are imported as image assets and are set to `Class.MeshPart` objects by a child `Class.SurfaceAppearance` object or a `Class.MeshPart.TextureID` property. ### Attachments _Attachment geometry defines where the attachment connects with the character_ _Geometry with the "_Att" suffix automatically convert to `Class.Attachment` objects in Studio_ Attachment points define where accessories attach to a character's body. In Studio, attachments are represented by `Class.Attachment` objects. ## Creation process Custom accessories are first created in 3D modeling programs, such as [Blender](https://www.blender.org/) or [Maya](https://www.autodesk.com/products/maya/overview), before importing the `.fbx` or `.gltf` model into Studio. To get started creating your first avatar asset, see the [avatar tutorials](/docs/en-us/avatar/tutorials.md). Depending on the type of asset you are creating, the creation process follows these high-level workflows: _ Rigid Accessory Workflow_ _ Layered Accessory Workflow_ > **Info:** Since 3D creation isn't a linear process and always requires reiteration and testing, the process of creating an accessory can differ between individuals and various creation workflows. ## Resources There are a variety of resources available for creators of all backgrounds to get started with accessory creation. If you are interested in specific avatar creation topics, use the following table to find guides and resources that best match your needs: | Topic | Resources | | --- | --- | | Tutorials | [Rigid accessory creation](/docs/en-us/art/accessories/creating-rigid.md)

[Basic clothing creation](/docs/en-us/art/accessories/creating.md) | | Reference files | [Accessory and clothing reference files](/docs/en-us/art/modeling/project-files.md) | | Technical specs | [.FBX export settings](/docs/en-us/art/modeling/export-requirements.md)

[General mesh specifications](/docs/en-us/art/modeling/specifications.md)

[Rigid accessory specifications](/docs/en-us/avatar/rigid-accessories/specifications.md)

[Marketplace policy](/docs/en-us/marketplace/marketplace-policy.md) | | Cosmetic creation | [Rigid accessories overview](/docs/en-us/avatar/rigid-accessories.md)

[Layered clothing overview](/docs/en-us/avatar/layered-accessories.md)


[Accessory Fitting Tool](/docs/en-us/avatar/accessory-fitting-tool.md)

[Rigid accessory specifications](/docs/en-us/avatar/rigid-accessories/specifications.md)

[Marketplace requirements](/docs/en-us/marketplace/marketplace-policy.md) | | Texturing | [Texturing requirements](/docs/en-us/art/modeling/texture-specifications.md)

[PBR textures](/docs/en-us/art/modeling/surface-appearance.md) | | Rigging and skinning | [Rigging and skinning overview](/docs/en-us/art/modeling/rigging.md)

[Humanoid rig requirements](/docs/en-us/avatar/character-bodies/specifications.md#rigging)

[Rig facial bones](/docs/en-us/art/characters/facial-animation/create-basic-heads.md#rigging)

[Automatic Skin Transfer](/docs/en-us/avatar/automatic-skinning-transfer.md)

[Skin facial bones](/docs/en-us/art/characters/facial-animation/create-basic-heads.md#skin-face-bones) | | Publishing and Marketplace | [Uploading to Marketplace](/docs/en-us/marketplace/publish-to-marketplace.md)

[Marketplace Policy](/docs/en-us/marketplace/marketplace-policy.md)

[Fees and commissions](/docs/en-us/marketplace/marketplace-fees-and-commissions.md) |
--- title: "Rigid accessory specifications" url: /docs/en-us/avatar/rigid-accessories/specifications last_updated: 2026-06-29T19:33:57Z description: "Rigid accessory specifications lists the specific technical requirements for basic avatar accessories." --- # Rigid accessory specifications When creating rigid accessories for Roblox, it's important to meet specific technical requirements to ensure compatibility and optimize for performance and quality. Many of these requirements must be applied when designing and modeling your asset in a third-party modeling application. Although rigid accessories and layerable clothing accessories share many technical requirements, layerable accessories must include [additional components](/docs/en-us/avatar/layered-accessories/specifications.md) to ensure the accessories deform and stretch appropriately on different body scales. If you are intending to publish and sell these assets on the Marketplace, there are additional [Marketplace policy](/docs/en-us/marketplace/marketplace-policy.md) standards that you must follow for any accessory or clothing item. When ready to export, see [Export requirements](/docs/en-us/avatar/rigid-accessories/export.md) for mesh export settings for Blender and Maya. > **Warning:** **If creating other types of 3D models:**- For generic meshes, see [general mesh specifications](/docs/en-us/art/modeling/specifications.md) and [general export settings](/docs/en-us/art/modeling/export-requirements.md). - For layered accessories, see [layered accessory specifications](/docs/en-us/avatar/layered-accessories/specifications.md) and [layered export settings](/docs/en-us/avatar/layered-accessories/export.md). - For avatar characters, see [avatar specifications](/docs/en-us/avatar/character-bodies/specifications.md) and [avatar export settings](/docs/en-us/avatar/character-bodies/export.md). ## Geometry and budgets - **Single Mesh** - Accessories must be a single mesh. - **Budgets** - Accessories can't exceed **4k** triangles. - **Watertight** - All geometry must be watertight without exposed holes or backfaces. - Use **quads** whenever possible. Avoid faces with 5 or more sides. - **Mesh Size** - Depending on the type of accessory asset, meshes must follow a standard size (in studs, centered on attachment point) depending on the [body scale](#body-scale) it is designed for. ### Body scale Roblox supports 3 types of body scales: `Classic`, `Normal`, and `Slender`. When designing your accessory, the size of your accessory cannot exceed the following sizes based on body scale and accessory asset type. Accessory size is measured based on attachment orientation. See [Body scale](/docs/en-us/art/accessories/body-scale.md) for more information on the different types of body proportions Roblox supports. > **Info:** You can use tools like the [Accessory Fitting Tool](/docs/en-us/avatar/accessory-fitting-tool.md) to help visualize and adjust the scale of your mesh on a mannequin within a visualized boundary before uploading and publishing the asset. #### Classic | Asset Type | Width (X) | Height (Y) | Depth (Z) | | --- | --- | --- | --- | | Hat | 3 | 4 | 3 | | Hair | 3 | 5*
(Not centered: 2 up, 3 down) | 3.5*
(Not centered: 1.5 front, 2 behind) | | Face | 3 | 2 | 2 | | Eyebrow and Eyelashes | 1.5 | 0.5 | 0.5 | | Neck | 3 | 3 | 2 | | Shoulder (attached to NeckAttachment) | 7 | 3 | 3 | | Shoulder (other) | 3 | 3 | 3 | | Front | 3 | 3 | 3 | | Back | 10 | 7 | 4.5*
(Not centered: 1.5 front, 3 behind) | | Waist | 4 | 3.5*
(Not centered: 1.5 up, 2 down) | 7 | #### Normal | Asset Type | Width (X) | Height (Y) | Depth (Z) | | --- | --- | --- | --- | | Hat | 1.87 | 2.5 | 1.87 | | Hair | 1.87 | 3.12*
(Not centered: 1.25 up, 1.875 down) | 2.18*
(Not centered: 0.9375 front, 1.25 behind) | | Face | 1.87 | 1.25 | 1.25 | | Eyebrow and Eyelashes | 1.5 | 0.5 | 0.5 | | Neck | 2.95 | 3.68 | 2.16 | | Shoulder (attached to NeckAttachment) | 6.90 | 3.68 | 3.24 | | Shoulder (attached to RightCollarAttachment, LeftCollarAttachment) | 2.95 | 3.68 | 3.24 | | Shoulder (attached to RightShoulderAttachment, LeftShoulderAttachment) | 2.67 | 4.40 | 3.09 | | Front | 2.95 | 3.68 | 3.24 | | Back | 9.86 | 8.59 | 4.87*
(Not centered: 1.623 front, 3.246 behind) | | Waist | 3.94 | 4.29*
(Not centered: 1.842 up, 2.457 down) | 7.57 | #### Slender | Asset Type | Width (X) | Height (Y) | Depth (Z) | | --- | --- | --- | --- | | Hat | 1.78 | 2.5 | 1.78 | | Hair | 1.78 | 3.12*
(Not centered: 1.25 up, 1.875 down) | 2.08*
(Not centered: 1.892 front, 1.189 behind) | | Face | 1.78 | 1.25 | 1.18 | | Eyebrow and Eyelashes | 1.5 | 0.5 | 0.5 | | Neck | 2.59 | 3.39 | 1.92 | | Shoulder (attached to NeckAttachment) | 6.05 | 3.39 | 2.88 | | Shoulder (attached to RightCollarAttachment, LeftCollarAttachment) | 2.59 | 3.39 | 2.88 | | Shoulder (attached to RightShoulderAttachment, LeftShoulderAttachment) | 2.37 | 3.96 | 2.75 | | Front | 2.59 | 3.39 | 2.88 | | Back | 8.64 | 7.91 | 4.32*
(Not centered: 1.443 front, 2.886 behind) | | Waist | 3.76 | 3.29*
(Not centered: 1.414 up, 1.885 down) | 6.73 | ## Textures - Textures for Marketplace assets cannot exceed 2048x2048 resolution. - Textures created for accessories must meet Roblox's [texture specifications](/docs/en-us/art/modeling/texture-specifications.md). ## Attachment points `Class.Attachment` objects indicate where an accessory model attaches to a point on a character body. Whether you are creating rigid or [layered](/docs/en-us/avatar/layered-accessories.md) accessories, Studio's [Accessory Fitting Tool](/docs/en-us/avatar/accessory-fitting-tool.md) (AFT) automatically adds and configures the appropriate `Class.Attachment` with the following specifications: - **One attachment** - Each accessory, including layered clothing, require at least one attachment point to its associated body part. - **Naming Convention** - The `Class.Attachment` name must follow a specific naming convention depending on the `Class.Accessory.AccessoryType`. The AFT generates an appropriate `Class.Attachment` name automatically. If setting or configuring attachments manually in Studio, use the following names for your `Class.Attachment` object depending on the accessory type: | Accessory type | Attachment name | | --- | --- | | Hat | `HatAttachment` | | Hair | `HairAttachment` | | Back | `BodyBackAttachment` | | Waist | `WaistFrontAttachment`, `WaistCenterAttachment`, `WaistBackAttachment` | | Shoulder | `RightShoulderAttachment`, `RightCollarAttachment`, `NeckAttachment`, `LeftCollarAttachment`, `LeftShoulderAttachment` | | Face, Eyelash, Eyebrow | `FaceFrontAttachment`, `FaceCenterAttachment` | | Neck | `NeckAttachment` | | Front | `BodyFrontAttachment` | > **Warning:** Importing attachment objects from a third-party tool is not supported for rigid accessories, but is supported for [character bodies](/docs/en-us/avatar/character-bodies/specifications.md#attachments). - **Shoulders and Collars** - Even though they are in similar locations, Shoulder and Collar attachment points interact with character rigs differently for rigid accessories. - Items using `RightShoulderAttachment` or `LeftShoulderAttachment` move with the character's arm. - Items using `RightCollarAttachment` or `LeftCollarAttachment` do not move with the character's arm. ## Layered properties Layered accessories which stretch and fit around any character body type must include additional configurations to achieve the layering effect. See [Clothing specifications](/docs/en-us/avatar/layered-accessories/specifications.md) for specifications required to create layerable accessories. ## Marketplace requirements Your items must meet the following requirements before you upload them to the Marketplace to sell: - Ensure that your items adhere to the [Marketplace program guidelines](/docs/en-us/marketplace/marketplace-policy.md). - Whenever applicable, ensure that your items adhere to Roblox's [custom mesh specifications](/docs/en-us/art/modeling/specifications.md). - Object `Class.MeshPart.Material|Material` is set to `Plastic`. - Object `Class.MeshPart.Transparency|Transparency` is set to 0. - Object `Class.MeshPart.VertexColor|VertexColor` is the default `1, 1, 1`. - Your `Class.Accessory` instance does not contain extraneous objects, like `Class.Script` or additional `Class.Part` instances.
--- title: "Tutorials" url: /docs/en-us/avatar/tutorials last_updated: 2026-06-29T19:33:57Z description: "Get all the tutorials you need to build characters and accessories." --- # Tutorials Learn how to create assets with structured tutorials that walks through each creation step from modeling in a third-party tool to importing in Studio. **#### Prerequisites** These tutorials use Blender, a free and open-source software. If you never used Blender or Roblox Studio before, start with the following resources before continuing. - [Download Blender](https://www.blender.org/) - Install and setup Blender. Roblox's tutorial content uses Blender 3.0+. - [Familiarize yourself with Blender](https://docs.blender.org/manual/en/latest/) - Use Blender's official docs or community-created content of your choice to get up to speed with Blender's UI and functions. - [Setting up Roblox Studio](/docs/en-us/studio/setup.md) - Install and configure Roblox Studio. ## Rigid accessories This tutorial covers the basic steps to successfully create and sell an accessory. Model and texture an object in Blender, upload the publishable accessory item to Studio, and sell the item on the Marketplace. 1. [
Model
](/docs/en-us/art/accessories/creating-rigid.md) 2. [
Texture
](/docs/en-us/art/accessories/creating-rigid/texturing.md) 3. [
Import into Studio
](/docs/en-us/art/accessories/creating-rigid/importing.md) 4. [
Fit and convert
](/docs/en-us/art/accessories/creating-rigid/converting.md) 5. [
Publish
](/docs/en-us/art/accessories/creating-rigid/publishing.md) ## Clothing Create your own clothing item from scratch using Roblox's project templates as a mannequin. Learn the processes required to convert a typical mesh object to an equipable and layerable clothing item ready for the Marketplace. 1. [
Model
](/docs/en-us/art/accessories/creating/modeling-setup.md) 2. [
Texture
](/docs/en-us/art/accessories/creating/unwrapping.md) 3. [
Rig
](/docs/en-us/art/accessories/creating/armature-setup.md) 4. [
Cage
](/docs/en-us/art/accessories/creating/caging-setup.md) 5. [
Export
](/docs/en-us/art/accessories/creating/exporting.md) 6. [
Import
](/docs/en-us/art/accessories/creating/importing.md) ## Bodies Start here for a comprehensive introduction to each step of body creation using Roblox's provided avatar templates. Each template includes pre-baked avatar components and these instructions provide everything you need to know to create a custom character ready for the Marketplace. 1. [
Pick a template
](/docs/en-us/art/characters/creating/template-files.md) 2. [
Model
](/docs/en-us/art/characters/creating/modeling-best-practices.md) 3. [
Texture
](/docs/en-us/art/characters/creating/texturing-setup.md) 4. [
Caging
](/docs/en-us/art/characters/creating/caging.md) 5. [
Cleanup
](/docs/en-us/art/characters/creating/combine-head-geometry.md) 6. [
Export
](/docs/en-us/art/characters/creating/export-textures.md)
--- title: "Avatar Setup model requirements" url: /docs/en-us/avatar-setup/auto-setup-requirements last_updated: 2026-06-29T19:33:57Z description: "The Avatar Setup tool previews animations, clothing, accessories, and body constructs on avatar rigs, directly in Studio." --- # Avatar Setup model requirements [Avatar Setup](/docs/en-us/avatar-setup.md) supports the automatic conversion of basic custom models to Roblox-ready bodies, rigid accessories, and layered clothing. By [bundling](#bundle-multiple-assets) your models together, you can automatically setup multiple custom models into avatar items for use in-experience or for upload to the Marketplace. ### Body Avatar Setup can attempt to process a 3D model into a Roblox-ready character model. This is one of the most common use-cases for Avatar Setup and can help you save time by automating the rigging, caging, and other configurations required to make a Roblox-ready avatar character. At this time, avatar assets like [rigid accessories](#accessories) and [layered clothing](#layered-clothing) may require processing with a base body. Avatar Setup is able to use this base body as a mannequin, creating more accurate attachment points and other components to ensure a better fit. If converting accessories or clothing, you must [bundle](#bundle-multiple-assets) them with a base body for fitting. #### Supported inputs Avatar Setup detects the following partial avatar body inputs: | **Reference Image** | **Avatar Setup support** | | --- | --- | | | > **Success:** A single or multiple mesh body with **no rigging**. The most common input is a single mesh object that doesn't include rigging data or other components. The input body must meet Avatar Setup's [model requirements](#body).

In this case, Avatar Setup creates the rest of the avatar components automatically. | | | > **Success:** A single or multiple mesh body with **only body rigging**. If the provided meshes include a compatible R15 body rig, Avatar Setup uses the provided rig, and creates the facial rig and all other components.

The input body rig must follow Roblox's [rigging configuration](/docs/en-us/avatar/character-bodies/specifications.md#rigging), otherwise Avatar Setup creates a brand new rig for you. | |
| > **Success:** A multiple mesh with **only facial rigging**. If you intend to use your own custom facial rigging, set the FaceRootJoint (and all child bones) as a child of the R15 head joint. For more information, see [optional rig requirements](#rig-requirements-optional). | |
| > **Success:** A **single mesh** body with **both body and facial rigging**. If the body rig, facial rig, and appropriate FACS data is included, Avatar Setup preserves that data and only generates the missing Roblox-specific components. **Bodies comprised of multiple meshes are not supported for this input.**

For more information, see [optional rig requirements](#rig-requirements-optional). | #### Mesh requirements To achieve the best results when converting a model to a Roblox-ready avatar character, it's important to configure your base model so the tool can properly generate avatar components with. Note that Roblox is actively adding improvements to this tool and these requirements may lift in the future. > **Error:** Many existing [downloadable resources](/docs/en-us/avatar/resources.md) for avatar bodies do not follow the Avatar Setup requirements below. Existing avatar references may need modification to ensure the asset follows the Avatar Setup models. See the specific [character references](/docs/en-us/avatar-setup.md#run-avatar-setup) for assets that were created for Avatar Setup requirements. The full requirements for the input body model are as follows: 1. **Single or multiple mesh** — In most cases, Avatar Setup accepts bodies comprising of 1 or more meshes. If your body includes multiple meshes, the tool automatically recombines all selected meshes as a single mesh, then decimates the combined parts to the appropriate R15 structure. 1. If providing the standard 15 multiple meshes that adhere to the R15 character naming convention, Avatar Setup attempts to place joints according to the user-partitioned body parts. 2. **5 distinct head components** — Whether you are using a single or multi-mesh character model, the following head components are required: 1. **2 eyes** — Heads must include 2 connected eyebags containing half-sphere eyes that do not share any vertices with the head component. 2. **3 mouthparts** — Heads must include a connected mouthbag that houses the **upper teeth**, **lower teeth**, and **tongue**. 1. Each of these mouthparts must be singly connected and not share any vertices with each other or with the head mesh. 3. **Head geometry must not share vertices** — Eyeballs, teeth, and tongue must be part of the model without sharing vertices with the body mesh. 4. **Within triangle budget** — The total body mesh resolution must be within 10,742 triangles. Use the following guidance to ensure each part doesn't exceed expected polycounts: | Body part grouping | Maximum triangles | Maximum quads | | --- | --- | --- | | Head | 4000 | 2000 | | Arms | 1248 per arm | 624 per arm | | Legs | 1248 per leg | 624 per arm | | Torso | 1750 | 875 | | Total: | 10,742 | 5495 | 1. The setup tool segments and adds [caps](/docs/en-us/avatar/character-bodies/specifications.md#body-parts) to the character limbs which may add to your total polycount. If your character model is close to the polycount limit, the additional geometry may cause validation failures. 5. **Humanoid shape** — The body must follow a general humanoid shape, with two arms, two legs, a torso, and a head. 6. **A-pose or T-Pose** — The body should form an upright A-pose or T-Pose. 1. Bodies with I-pose may yield lower quality results. 2. Ensure that no limbs obscure or overlap each other from the front view. 7. **Negative Z Axis** — The body front should face the negative Z axis. 8. **Symmetrical** — Asymmetrical bodies may work on a case-to-case basis. Position the center of the body with the Y-axis to improve the accuracy of the result. 1. If your asymmetrical model experiences setup issues, try using a more symmetrical version. 9. **Watertight** — Ensure the model is watertight in all regions with the exception of the eyes and mouth. Watertight means that there are no holes in the mesh and no back faces are exposed. 10. **No accessories** — Do not include accessories, including face accessories, like hair, eyebrows, beards, and eyelashes. 11. **Distinct neck area** — Keep the neck distinct and not merged with the shoulders or upper torso. 12. **Includes texture** — Models should include one or more texture maps. If the input body includes multiple textures, the tool bakes the textures to a single map. This applies to [PBR textures](/docs/en-us/art/modeling/surface-appearance.md) where the four textures are baked — one for each albedo, normal, metalness and roughness. 13. **Follows Marketplace and Community Policy** — The model must conform to Roblox's [Marketplace Policy](/docs/en-us/marketplace/marketplace-policy.md) and [Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards). #### Rig requirements (Optional) You can use your own custom body and face rig for your character model input instead of allowing Avatar Setup to generate a new one. > **Info:** If you provide the standard 15 multiple meshes that adhere to the R15 character naming convention **without a rig**, Avatar Setup attempts to generate joints according to the user-partitioned body parts. To ensure that Avatar Setup uses your **own custom body rig**: - Ensure that your bones (Blender) or joints (Maya) meet [Roblox's avatar rigging requirements](/docs/en-us/avatar/character-bodies/specifications.md#rigging). - Double-check naming conventions and hierarchy. - Body rigs should not include additional bones beyond the standard 15, and facial rigs (no bone limit) should be correctly parented to a RootFaceJoint. To ensure that Avatar Setup uses your **own custom face rig and FACs data**: - A supported R15 body rig is required when submitting custom facial rigs and facial animation data. - Review [supported body inputs](#body) to ensure you are submitting a supported body input and facial rig combination. - Facial rig must include a `RootFaceJoint` bone (usually [mapped](/docs/en-us/art/characters/facial-animation/create-basic-heads.md#map) as `DynamicHead`) whose parent is the `Head` joint of the R15 rig. - All the other facial animation joints are descendants of the `RootFaceJoint`, and not direct children of the `Head` joint. - Animations need to be provided for the [17 required poses](/docs/en-us/avatar/character-bodies/specifications.md#facial-animations), at minimum. - Neutral animation must map to frame `0`. - Since the head is part of the single mesh with the body, the facial animation mappings must be included with the single body mesh: - The mapping between animation frames and facial poses are stored in the extra attributes / custom properties of the provided single mesh. - The name of the root face joint is stored in the extra attributes / custom properties of the single mesh, mapping `RootFaceJoint` to the corresponding name (usually `DynamicHead`). #### Examples of non-supported bodies The following are common examples of bodies that may not yield expected results with Avatar Setup: ** Non-supported model examples** | _**Extreme asymmetry** — If you are having issues with an asymmetrical model, try using a more symmetrical version and make minor adjustments._ | | --- | | _**I-pose** — If you are experiencing issues with your model in an I-pose, try resubmitting with A-pose or T-pose._ | | _**Non-contiguous mesh** — setup tool expects the body mesh to be completely contiguous._ | | _**No neck** — setup tool expects a neck connecting a head to the torso._ | | _**Out of proportion limbs** — setup tool expects a more realistic humanoid-style character model._ | | _**Out of proportion limbs** — setup tool expects a more realistic humanoid-style character model._ | ### Accessories > **Warning:** At this time, you must [bundle](#bundle-multiple-assets) your accessory and clothing models with a base body to utilize Avatar Setup. For more information, see [supported inputs](#supported-inputs-1). Avatar Setup can convert models into rigid accessories, configuring the scale type and attachment components required for rigid accessories. To convert a mesh model into an accessory using Avatar Setup: 1. Fit the model onto a base body in Studio or your 3D modeling software. 2. [Bundle](#bundle-multiple-assets) the assets together in a single `Class.Folder`. 3. Use **Avatar Setup** to use Avatar Setup on your assets. During this process, you will be able to designate individual meshes or models as rigid accessories, layered accessories, or body parts. After conversion, a prompt displays allowing you to select the specific **Asset Type**. For best results, ensure your models adhere to Roblox's [rigid accessory geometry specifications](/docs/en-us/avatar/rigid-accessories/specifications.md#geometry-and-budgets). #### Supported inputs Rigid accessory meshes must be bundled with a body for Avatar Setup to configure. See the following supported inputs for rigid accessory meshes. | Input object | Auto setup generates | | --- | --- | | | `Class.Accessory` containing clothing mesh, `BodyPartScaleType`, and attachment points | ### Layered clothing Avatar Setup can convert models into clothing accessories, adding in rigging and caging data that normally requires manual configuration in a separate software. To convert a mesh model into an accessory using Avatar Setup: 1. Fit the model onto a base body in Studio or your 3D modeling software. 2. (Optional - for best results) [Bundle](#bundle-multiple-assets) the assets together in a single `Class.Folder`. 3. Use Avatar Setup to use Avatar Setup on your assets. During this process, you will be able to designate individual meshes or models as rigid accessories, layered accessories, or body parts. After conversion, a prompt displays allowing you to select the specific **Asset Type**. For best results, ensure your models adhere to Roblox's [layered clothing geometry specifications](/docs/en-us/avatar/layered-accessories/specifications.md#geometry-and-budgets). #### Supported inputs Avatar Setup can configure clothing models to a layered clothing `Class.Accessory` complete with caging and rigging data. While Avatar Setup can configure models without a bundled body, you may find better results if you include a base body for setup. | Input object | Auto setup generates | | --- | --- | | `Class.Model` containing clothing mesh object with no additional components (this is the object created when importing a custom 3D object) | > **Warning:** Without an accompanying body asset, auto setup makes a best effort for generating cage data. | | | | ## Bundle multiple assets Avatar Setup can process multiple accessory or clothing models with a single body model. To submit multiple models in the **Avatar Setup** tool, parent your models within a single folder. _A `Folder` containing the various `Model` objects ready to be processed by Avatar Setup into body, accessory, and clothing assets._ To quickly parent your objects to folder: 1. In the **Explorer**, hold `Shift` and select the `Class.Model` objects you intend to process with Avatar Setup. Ensure you are selecting a [supported base body](#body) as one of the `Class.Model` objects. 2. Right-click and select **Group as Folder**. 3. Select the `Class.Folder` and then select the **Avatar Setup** tool to begin processing. > **Warning:** At this time, you must bundle your accessory and clothing models with a base body to utilize Avatar Setup.
--- title: "Avatar Setup" url: /docs/en-us/avatar-setup last_updated: 2026-06-29T19:33:57Z description: "The Avatar Setup tool helps accelerate the avatar character, clothing, and accessory creation process by automatically processing custom models into avatar assets." --- # Avatar Setup **Avatar Setup** helps accelerate the avatar character, clothing, and accessory creation process by automatically processing custom models into avatar assets. If your characters, accessories, and clothing don't have all essential components for publishing to the [Marketplace](https://www.roblox.com/catalog), Avatar Setup can perform the following for your 3D models: - **Rigging** — Adds an R15 armature to your body model to enable movement and animation. - **Skinning** — Adds weights and influences to various surfaces of your mesh, ensuring an organic and natural flexibility during movement. - **Facial animation** — Generates the FACS poses, facial rigging, skinning, and animation data required for facial animation and avatar chat. - **Caging** — Adds the required cages to your asset, enabling it to support layered clothing. - **Partitioning** — Separates the body mesh into the appropriate R15 parts. - **Creating attachments** — Adds the appropriate attachment points enabling the character to wear rigid accessories. ![Avatar Preview button indicated in Avatar tab](../assets/studio/general/Toolbar-Avatar-Setup.png) ## Import a 3D asset The [Importer](/docs/en-us/studio/importer.md) tool allows you to import custom 3D assets that you made in third-party modeling tools like Blender or Maya directly into Studio. If your custom 3D assets don't include all essential avatar components for the Marketplace, Avatar Setup will add any missing components as it processes the assets. To import a custom 3D asset using the Importer tool: 1. Navigate to the **Home** tab, then select **Import**. A file browser displays. 2. Select the `.fbx`, `.obj`, or `.gltf` model you want to import. After a few moments, the **Import Preview** window opens with a preview of your asset. 3. In the **File General** section, disable the **Upload To Roblox** option. This setting stops the asset from immediately saving to your Toolbox, preventing unwanted copies of similar assets. 4. **OPTIONAL** To create an import preset for this workflow, 1. In the top-right of the **Import Preview** window, click the ⋯ dropdown. 2. In the contextual menu, click **Save as New**. 5. Verify the preview and any [warnings or errors](/docs/en-us/studio/importer.md#warnings-and-errors) in your model. Certain warnings and errors may require readjusting the model in the third-party software you used to create the asset. 6. Click the **Import** button to add the model into your workspace. After a moment, it displays as a `Class.Model` object in the **Explorer** window. > **Warning:** When you import a new model into Studio, the system adds the model to the moderation queue. If your model is moderated, you will receive a moderation email with a link to appeal. This appeal can take up to 10 minutes to resolve. ## Run Avatar Setup When your project has the appropriate `Class.Model` object in your workspace, you can run Avatar Setup to see if your 3D asset requires any additional avatar components before you can publish it to the Marketplace as a character body, accessory, or clothing item. Avatar Setup supports the conversion of the following 3D asset types from a base `Class.Model`: | Asset type | Input object | Output object | | --- | --- | --- | | Avatar body | `Class.Model` containing one or more `Class.MeshPart` objects that meet Avatar Setup's supported [body inputs](/docs/en-us/auto-setup-requirements.md#body). | `Class.Model` parenting associated 15 `Class.MeshPart` objects and other expected [avatar components](/docs/en-us/avatar/character-bodies.md#components-of-an-avatar). | | Accessory | `Class.Model` containing one or more `Class.MeshPart` objects that meet Avatar Setup's supported [accessory inputs](/docs/en-us/auto-setup-requirements.md#accessories).

Must [bundle](/docs/en-us/auto-setup-requirements.md#bundle-multiple-assets) with an avatar body `Class.Model` for Avatar Setup. | `Class.Accessory` that includes all expected [rigid accessory components](/docs/en-us/avatar/rigid-accessories.md#components-of-a-rigid-accessory). | | Layered clothing | `Class.Model` containing one or more `Class.MeshPart` objects that meet Avatar Setup's supported [accessory inputs](/docs/en-us/auto-setup-requirements.md#accessories-and-clothing).

Can optionally [bundle](/docs/en-us/auto-setup-requirements.md#bundle-multiple-assets) with an avatar body `Class.Model` for Avatar Setup. | `Class.Accessory` that includes all expected [layered accessory components](/docs/en-us/avatar/layered-accessories.md#components-of-a-layered-clothing-accessory). | | Multiple accessories and clothing with single body | `Folder` [bundle](/docs/en-us/auto-setup-requirements.md#bundle-multiple-assets) containing one or more `Class.Model` that meet supported inputs for accessories, clothing, or body.

Must [bundle](/docs/en-us/auto-setup-requirements.md#bundle-multiple-assets) with an avatar body `Class.Model` for Avatar Setup. | `Class.Model` character body and any `Class.Accessory` equippable items.

All generated assets include expected avatar item components. | > **Info:** For more in-depth information on supported input configurations, see the individual asset specifications on [Avatar Setup requirements](/docs/en-us/auto-setup-requirements.md). To run Avatar Setup: 1. In the **Explorer** window, select the model or specific meshes of the model. If you want to select multiple meshes at once, expand the `Class.Model` object, then hold shift and click on the individual mesh objects you want to process as avatar assets.**Reference models** For an asset that meets all of these model requirements, download one of the following Avatar Setup templates for your own reference and testing: Nature Girl A comprehensive `.zip` folder of an Avatar Setup ready character model, including clothing and rigid accessory assets and PBR texture assets. This template is not compatible with the traditional avatar creation workflow. [Download](../assets/art/reference-files/NatureArcherGirl-AutoSetup.zip) Stylish Male A comprehensive `.zip` folder of an Avatar Setup ready character model, including the base body and associated PBR textures. This template is not compatible with the traditional avatar creation workflow. [Download](../assets/art/reference-files/StylizedMale-AutoSetup.zip) 2. With the model or meshes selected, navigate to the **Avatar** tab, then select **Avatar Setup**. 3. In the **Configure Models** section, 1. Set **Preset** to the appropriate category. 1. If you want to publish your assets to the Marketplace for users to purchase and use across Roblox, select **Platform Avatar**. This option generates a more restrictive setup that's necessary to pass validation and moderation. 2. If you want to upload your assets to your inventory for non-player characters (NPCs) or starter player characters for your experiences, select **Development Avatar**. This option generates a less restrictive setup. 2. Set **Type** for each asset to the correct asset type so that it moves and animates correctly in the 3D space. 1. For character body geometry, select **Body**. 2. For layered clothing assets, select **Layered**. 3. For rigid accessory assets, select **Rigid**. 4. For eyebrow and eyelash assets, select **Eye Layered**. 4. **OPTIONAL** Near the bottom-left corner, click on the gear icon to configure specialty settings for your character model. 1. If your character has a decal for its face and you want to enable the conversion of the texture into a [dynamic head](/docs/en-us/avatar/dynamic-heads.md), enable **Handle face as 2D decal**. This process allows you to preserve your creative intent while complying with Marketplace policy that requires all avatars to have dynamic heads.**Converting a decal into a dynamic head?** The process of converting a decal into a dynamic head infers eye and mouth regions from your existing texture, generates cages and FACS data, then maps the result onto a dynamic head template. For best results, review the following guidance:**Run Avatar Setup with the character's body and head** - If you only run Avatar Setup with the character's head, this speciality setting will fail.**Verify avatar face is front-facing** - Before you run Avatar Setup, verify that your character's face is facing the front and visible in the preview. Whenever you manually position your character's face, make sure you enable **Manually align front** in the Avatar Setup window's speciality settings.**Delete the `Class.FaceControls` object** - Avatar Setup retains your current face setup whenever it detects a `Class.FaceControls` object under your character's `Enum.BodyPart|Head` model. If one exists in your character, delete it from before running Avatar Setup.**Use `Class.SurfaceAppearance` instead of `Class.MeshPart.TextureId|TextureID`** - When you have only `Class.MeshPart.TextureId|TextureID` or both `Class.MeshPart.TextureId|TextureID` and `Class.SurfaceAppearance` objects defined for your avatar, remove or replace the `Class.MeshPart.TextureId|TextureID` objects with `Class.SurfaceAppearance` objects for significant texture quality improvements when rendering. 2. If your character includes a [higher-fidelity rig](/docs/en-us/avatar/character-bodies/specifications.md#higher-fidelity-rigs) with additional joints, enable **Create R15 with optional joints**. 3. If you want to manually align the front direction of your character with the negative Z-axis in world space, enable **Manually Align Front**. While auto-setup does this automatically for you, it can sometimes fail to properly determine the front of your character. 4. If you want to add a post-processing step to significantly improve the alignment of your character's head cage with the head model and its facial features, enable **Improve facial caging**. This is particularly important if you plan on using [makeup](/docs/en-us/avatar/makeup.md) with your character. 5. If you are importing a development avatar and want to skip the auto-decimation of input meshes with resolution above the triangle budgets required for the Marketplace, disable **Reduce triangle count**. 5. Near the bottom-right corner, click the **Set Up** button. 1. If your model requires auto-setup for missing avatar components, the tool begins processing your model. This can take several minutes. Once auto-setup is complete, the tool populates your avatar asset in the Avatar Setup window. 2. If your model doesn't require auto-setup, the tool populates your avatar asset in the Avatar Setup window. ## Testing tools After you successfully load your character into the Avatar Setup window, you can access various testing tools in the window's side navigation to ensure your character moves, animates, and equips clothing and accessories properly. In each of these interfaces, you can see which items are currently equipped to your character on the right side of the Avatar Setup window. From here, you can: - **Unequip items** by right-clicking on an equipped item, then selecting **Unequip Item** from the contextual menu. - **Set the worn order** by dragging and ordering the various equipped accessories. If you discover any issues that can't be resolved with Avatar Setup testing tools, you may need to update your character model using the third-party modeling software you used to create the asset, then retry the auto-setup process. ### Check Body The **Check Body** interface contains various tabs for checking how your character model animates, emotes, wears clothing and accessories, and changes skin tone. To learn more about what you can do with this interface, click through the following tabs. #### Animations The **Animations** tab lets you test how your character animates and emotes using each [default animation](/docs/en-us/animation/using.md#default-character-animations) that Roblox provides for every avatar, such as the walk, jump, and swim animation. If you notice any issues with your character's body as it animates, you can either use Avatar Setup's editing tools or your favorite third-party 3D modeling application to adjust your character's rig until the animations play properly. #### Clothing The **Clothing** tab lets you try on various layered clothing on your character to see if your character's cage works properly for different avatar clothing types, such as shirts, jackets, pants, and shoes. If you notice any areas on your character's body that don't interact with clothing as expected, you can either use Avatar Setup's [Cage Brush](#cage-brush) or your favorite third-party 3D modeling application to adjust your character's cage until the clothing fits properly. #### Accessories The **Accessories** tab lets you try on various accessories on your character to see if your character's [attachment points](/docs/en-us/avatar/rigid-accessories.md#attachments) work properly on different areas of the character's body, such as the front, back, or shoulder of the character. If you notice any areas on your character's body that don't interact with accessories as expected, you can either use Avatar Setup's [Attachment Tool](#attachment-tool) to adjust your character's attachment points until accessories attach properly. #### Body The **Body** tab lets you change your character's skin tone and swap body parts to test how the character changes visual appearance. ### Check Face The **Check Face** interface zooms closer to the character's face and allows you to test various facial poses and cosmetics. To learn more about what you can do with this interface, click through the following tabs. _Check the range of facial poses with multiple facial animation tests._ _Unexpected facial animation behavior, such as crashing or artifacts, may require minor adjustment to the base mesh and re-running the auto-setup process._ #### Animations The **Animations** tab lets you test your character's facial animations and range of motion for different parts of the face, such as the character's eyeballs, eyebrows, and mouth. If you notice any issues with your character's facial expressions, you can either use Avatar Setup's editing tools or your favorite third-party 3D modeling application to adjust your character until the animations play properly. #### Body The **Body** tab lets you change your character's skin tone and swap heads to test how the character changes visual appearance. #### Makeup The **Makeup** tab lets you test [makeup](/docs/en-us/avatar/makeup.md) on your character's face. You can either test entire makeup looks or individual makeup components in various combinations. If you want to upload your makeup as an entire look, click on the [Save](#publish-to-the-marketplace) navigation, then select **Create Makeup Look**. #### Accessories The **Accessories** tab lets you try on accessories on your character to see if your character's [attachment points](/docs/en-us/avatar/rigid-accessories.md#attachments) work properly for hair, head, and face avatar accessories. If you notice any areas on your character's body that don't interact with accessories as expected, you can either use Avatar Setup's [Attachment Tool](#attachment-tool) to adjust your character's attachment points until accessories attach properly. ### Test in Experience The **Test in Experience** tab starts playtesting the experience with your character model. This button references your active [playtesting mode](/docs/en-us/studio/testing-modes.md) and retains the changes you make in the Avatar Setup window. ### Add test items In addition to the side navigation test tooling, you can add custom assets to the Avatar Setup's testing palette that are either active in your current project or by assetID. For more information on either method, review the tabs below. #### From Project To add a custom item that's active in your current project to the Avatar Setup testing palette: 1. In the **Explorer** window or viewport, select a valid accessory or body part asset. 2. In the **Avatar Setup** window, navigate to the **Check Body** or **Check Face** interface, then click the plus button at the bottom of the selection column along the left side of the window. The item appears in the appropriate section and subsection of the interface, such as Accessories → Hair. #### By AssetID To add a custom item by assetID to the Avatar Setup testing palette: 1. In the **Avatar Setup** window, navigate to the **Check Body** or **Check Face** interface, then click the down-facing arrow button. 2. From the contextual menu, select **AssetID**. The item appears in the appropriate section and subsection of the interface, such as Accessories → Hair. ## Editing tools In addition to the Avatar Setup window, you can use Avatar Setup's Attachment Tool and Cage Brush in the viewport to make minor to moderate adjustments to the character's attachment points and cage. ### Attachment Tool The **Attachment Tool** displays all attachment points on the character's body. When you select an attachment point, the Move tool displays, allowing you to adjust the attachment point's position within the character's body. ### Cage Brush The Cage Brush lets you modify your character's body cage mesh and make adjustments to how clothing and other layered assets fit on your character. The following controls provide fine-grain control on your cage edits: - Symmetrical and mirror edits along X-axis. - Brush with radius and falloff controls. - Brush falloff visualization over the vertices. - Ability to hide or display equipped layered clothing items. Layered clothing will update in real time in the **Avatar Setup** tool's preview window. > **Warning:** Major cage edits may require returning to your third-party modeling tool, making your cage adjustments, and then re-exporting the asset. ## Publish to the Marketplace The **Save** button opens the Asset Configuration dialog, allowing you to upload the avatar and any accessory items to your inventory or publish everything configured as platform avatar assets to the Marketplace. When publishing your assets to the Marketplace, you must pay an [upload fee](/docs/en-us/marketplace/publish-to-marketplace.md#upload-an-asset) and pass moderation in order to add your assets to the catalog. For additional resources on the publishing process and Marketplace, see the following: - [Publish to the Marketplace](/docs/en-us/marketplace/publish-to-marketplace.md) - [Marketplace fees and commissions](/docs/en-us/marketplace/marketplace-fees-and-commissions.md) - [Marketplace policy](/docs/en-us/marketplace/marketplace-policy.md)
--- title: "Adaptive Animation" url: /docs/en-us/characters/adaptive-animation last_updated: 2026-06-29T19:33:57Z description: "Set up your character rig to support universal sets of animations and emotes." --- # Adaptive Animation Roblox's **Adaptive Animation** system allows animations to play seamlessly between custom characters with unique body types, rigs, and proportions. This feature utilizes `Class.HumanoidRigDescription` and `Class.DigitsRigDescription` objects within your character model so that you can customize, modify, and map the internal joints of your custom character for universal animation support. After importing a character rig, you can access several Studio tools to help you remap your joints and set a baseline T-pose reference to better support animations. After configuring your rig, your custom character can playback any R15 animation, or any animation that has been published from a character with `Class.HumanoidRigDescription` and `Class.DigitsRigDescription` objects. _Seamlessly import custom characters into Studio as a `Custom Humanoid` rig type._ _Modify and adjust the generated `Class.HumanoidRigDescription` to ensure compatibility with universal animations._ ## Modify custom character rig When importing a rig as a `Custom Humanoid`, Studio attempts to automatically configure the model's `Class.HumanoidRigDescription` and `Class.DigitsRigDescription` objects. When necessary, you can use the joint assignment and T-pose tools to fine-tune or adjust the automated changes. > **Info:** You can follow along with your own custom character or the [reference character](../assets/art/reference-files/Snow.fbx) featured within this guide. The reference character is a modified version of Blender Studio's [Snow](https://studio.blender.org/characters/snow/v4/) character combined with Blender's [base mesh](https://www.blender.org/download/demo-files/) assets. ### Import model Use the 3D Importer to import a character model, then use the Adaptive Animation system to add `Class.HumanoidRigDescription` and `Class.DigitsRigDescription` objects: 1. In the **Home** tab, click the **Import** button to open the 3D Importer. A file browser opens. 2. Select your rigged character's `.fbx` or `.gltf` file. The 3D Importer loads a preview of the character model. 3. In the **Rig General** section, set **Rig Type** to **Custom**. 1. Select the **Import** button. The character displays in your workspace as a `Class.Model`. 2. In the **Avatar** tab, click the **Adaptive Animation** button. The **Remap Rig** panel displays. 3. In the **Remap Rig** panel, click the **Create** button. The Adaptive Animation systems generates a `Class.HumanoidRigDescription` object and a `Class.DigitsRigDescription` for each hand. ### Assign joints The Adaptive Animation system requires at least 15 of your character's joints to map to Roblox's [standardized 15 joints skeleton](/docs/en-us/avatar/character-bodies/specifications.md). While the automated import process attempts to make this assignment automatically, you can verify and reassign joints by using the joint assignment tool. When you select your model's `Class.HumanoidRigDescription` or `Class.DigitsRigDescription` objects, the joint assignment tool's **visual picker** displays in the top right of the 3D viewport with a view of the standard Roblox skeleton. To assign joints, you can either click the joints in the 3D viewport or visual picker, then click the corresponding joint in the other view. When a joint is assigned, the joint icon **turns orange** in both the 3D viewport and the visual picker. When using the joint assignment tool, the visual picker displays: - All 15 standard joint assignments that you are required to assign to your character's rig as a filled-in gray circle. - All additonal joint assignments that you are not required to assign to your character's rig as empty circle outlines. If your rig is compatible with these extra joints, you can assign them appropriately to help improve animation quality. _Joints that are not assigned appear as a grey indicator on both the visual picker and the 3D viewport mannequins._ _When assigned, joint indicators turn orange in both the visual picker and the 3D viewport._ To access and use the joint assignment tool: 1. In the **Explorer** window, expand the model you just imported and select the child `Class.HumanoidRigDescription`. The Adaptive Animation toolbar displays with the joint assignment tool active, and your character's mesh objects become semi-translucent so you can view its internal joints. 2. In the 3D viewport, click a joint within the character's body, then in the visual picker, select the corresponding joint. The joint indicator turns orange to indicate that it has been assigned. 3. If your character's rig includes optional joints in its hands, 1. Navigate back to the **Explorer** window, then select one of your model's child `Class.DigitsRigDescription` objects. The visual picker updates to display a view of your character's left or right hand. 2. Assign each optional joint by clicking a joint on the hand, then selecting the corresponding joint in the visual picker. 3. Repeat this process for the character's other child `Class.DigitsRigDescription` object. ### Create t-pose To ensure universal animation support, it's important to set up a standard T-pose configuration as a baseline reference for your character. The Adaptive Animation toolbar offers a T-pose mode with tooling to either automatically position your character to match T-pose requirements, or modify the position and rotation of your joints to ensure the best animation results. _ To move joints, select the joint while T-pose tooling is active, then use the multi-transform tool to apply a positional or rotational change._ To create a T-pose using T-pose mode: 1. In the Adaptive Animation toolbar, select the **T-pose mode** button to switch to T-pose mode. 2. Select the **Enforce T-pose** button to automatically position your character for T-pose requirements. 3. If you need to make adjustments to your character's T-pose, 1. If you want to rotate both left and right limbs simultaneously, enable the left and right symmetry icon. 2. Select one of the upper arm joints near the shoulder. The joint turns blue to indicate that it is selected. 3. Use the **Rotate** tool to rotate the joint into a T-pose. 4. If necessary, position the legs and other areas of the body until your rig is in a T-pose. 4. To test your rig's T-pose, 1. Navigate to the Adaptive Animation toolbar, then use the dropdown to select an animation. 2. Press the play icon. If an animation doesn't animate correctly, you may need to remap or re-pose your character's rig. > **Success:** Once your character's rig is configured and saved, that character can [support playback](/docs/en-us/animation.md) of any R15 animation or animations published from a custom character with a `Class.HumanoidRigDescription`. --- title: "Character appearance" url: /docs/en-us/characters/appearance last_updated: 2026-06-29T19:33:57Z description: "Customize your in-experience character appearance and properties." --- # Character appearance Most experiences let players use their own Roblox avatar, although some implement an in-experience customization system like the [UGC Homestore](/docs/en-us/resources/templates.md#ugc-homestore) template. Other experiences make limited [modifications](/docs/en-us/characters/appearance.md) to player avatars such as helmets, wings, or accessories that match the genre. To create a unique experience that alters the appearance of your users, you can customize the default character properties through [avatar settings](#global-avatar-settings) or a [manually modify appearance](#manually-modify-appearance). ## Global avatar settings Studio's **File** ⟩ **Avatar Settings** allows you to quickly set several global player character properties in your experience. These settings apply globally to all player character models joining your experience. To modify specific characters, such as non-player character models, see [manually modify appearance](#manually-modify-appearance). In this window, you can set various presets for clothing, accessories, body parts, collision behavior, animations and more. When editing these settings, a preview of the applied settings displays in the workspace. For more information, see [Avatar Settings](/docs/en-us/studio/avatar-settings.md). ## Manually modify appearance Character models contain a `Class.Humanoid` object that gives the model special characteristics, such as walking, jumping, equipping items, and interacting with the environment. You can programmatically modify a `Class.Humanoid` by updating `Class.HumanoidDescription`. This includes player character models or non-player character models in your experience. You can adjust the following character properties in your experience using `Class.HumanoidDescription`: | Character property | Description | | --- | --- | | Scale | Number values for physical traits `Class.HumanoidDescription.HeightScale\|height`, `Class.HumanoidDescription.WidthScale\|width`, `Class.HumanoidDescription.HeadScale\|head`, `Class.HumanoidDescription.BodyTypeScale\|body type` and `Class.HumanoidDescription.ProportionScale\|proportion`. This doesn't affect R6 body types. | | Accessories | The asset IDs of `Class.Accessory\|accessories` equipped by a character. | | Classic Clothing | The asset IDs of the `Class.Shirt`, `Class.Pants`, and `Class.ShirtGraphic` image textures that you can apply to the character. | | Body Part | The asset IDs of the `Class.HumanoidDescription.Face\|Face`, `Class.HumanoidDescription.Head\|Head`, `Class.HumanoidDescription.Torso\|Torso`, `Class.HumanoidDescription.RightArm\|RightArm`, `Class.HumanoidDescription.LeftArm\|LeftArm`, `Class.HumanoidDescription.RightLeg\|RightLeg` and `Class.HumanoidDescription.LeftLeg\|LeftLeg` parts of a character. | | Body Colors | The `Class.BodyColors` of the character's individual parts. | | Animations | The asset IDs of `Class.Animation\|Animations` you can use on a character. | Customize a character with `Class.HumanoidDescription` using the following steps: 1. [Create a description](#create-humanoiddescription) from the user's character, a specific Outfit ID, or from a specific User ID. 2. [Modify the description](#modify-humanoiddescription) to customize the properties that you want to apply to the `Class.Humanoid` character. 3. [Apply the description](#apply-humanoiddescription) on either a single character, all player characters, or even on all spawning characters. > **Warning:** When updating a character's properties through `Class.HumanoidDescription`, it is important to use an up-to-date `Class.HumanoidDescription` of that specific `Class.Humanoid` with `Class.Humanoid:GetAppliedDescription()|Humanoid:GetAppliedDescription()`. ### Create HumanoidDescription You can create a new `Class.HumanoidDescription` instance directly within the **Explorer** hierarchy or within a `Class.Script` with the following code: ```lua local humanoidDescription = Instance.new("HumanoidDescription") ``` In most cases, you should use an existing `Class.HumanoidDescription` instead of a default new `Class.HumanoidDescription` by referencing an existing player character, avatar outfit, or user ID. #### From the player character Use the following code sample to create a new `Class.HumanoidDescription` based on the player character's current properties: ```lua local humanoid = player.Character and player.Character:FindFirstChildWhichIsA("Humanoid") local humanoidDescription = Instance.new("HumanoidDescription") if humanoid then humanoidDescription = humanoid:GetAppliedDescription() end ``` #### From an existing outfit Use the following sample code to create a `Class.HumanoidDescription` from an outfit ID using `Class.Players:GetHumanoidDescriptionFromOutfitId()|Players.GetHumanoidDescriptionFromOutfitID`: ```lua local Players = game:GetService("Players") local outfitId = 480059254 local humanoidDescriptionFromOutfit = Players:GetHumanoidDescriptionFromOutfitId(outfitId) ``` #### From a specific user Use the following sample code to create a `Class.HumanoidDescription` from a user ID using `Class.Players:GetHumanoidDescriptionFromUserId()`: ```lua local Players = game:GetService("Players") local userId = 491243243 local humanoidDescriptionFromUser = Players:GetHumanoidDescriptionFromUserId(userId) ``` ### Modify HumanoidDescription To customize `Class.HumanoidDescription` properties, set them directly on the `Class.HumanoidDescription` or use a specified method before applying the `Class.HumanoidDescription` to a character. The following code sample provides examples of setting the different types of `Class.HumanoidDescription` properties: ```lua local humanoidDescription = Instance.new("HumanoidDescription") humanoidDescription.HatAccessory = "2551510151,2535600138" humanoidDescription.BodyTypeScale = 0.1 humanoidDescription.ClimbAnimation = 619521311 humanoidDescription.Face = 86487700 humanoidDescription.GraphicTShirt = 1711661 humanoidDescription.HeadColor = Color3.new(0, 1, 0) ``` #### Set multiple accessories For layered or bulk accessory changes, you can use `Class.HumanoidDescription:SetAccessories()` to make accessory related updates. The following code sample adds a layered sweater and jacket in that order to a `Class.HumanoidDescription`: ```lua local humanoidDescription = Instance.new("HumanoidDescription") local accessoryTable = { { Order = 1, AssetId = 6984769289, AccessoryType = Enum.AccessoryType.Sweater }, { Order = 2, AssetId = 6984767443, AccessoryType = Enum.AccessoryType.Jacket } } humanoidDescription:SetAccessories(accessoryTable, false) ``` ### Apply HumanoidDescription Apply `Class.HumanoidDescription` to specific `Class.Humanoid` characters in your experience with `Class.Humanoid:ApplyDescription()` or `Class.Player:LoadCharacterWithHumanoidDescription()|Humanoid.LoadCharacterWithHumanoidDescription`. > **Warning:** Changing the assets on a character while also changing `Class.HumanoidDescription` might lead to undefined behavior. #### On a single character `Class.Humanoid:ApplyDescription()|ApplyDescription()` can target any `Class.Humanoid`. Use the following code to add a new pair of sunglasses and a new torso to the player character: ```lua local humanoid = player.Character and player.Character:FindFirstChildWhichIsA("Humanoid") if humanoid then local descriptionClone = humanoid:GetAppliedDescription() descriptionClone.Torso = 86500008 -- Multiple face accessory assets are allowed in a comma-separated string descriptionClone.FaceAccessory = descriptionClone.FaceAccessory .. ",2535420239" -- Apply modified "descriptionClone" to humanoid humanoid:ApplyDescription(descriptionClone) end ``` #### On all player characters Use the following sample code to apply a `Class.HumanoidDescription` to all current players in the game: ```lua local Players = game:GetService("Players") for _, player in Players:GetPlayers() do local humanoid = player.Character and player.Character:FindFirstChildWhichIsA("Humanoid") if humanoid then -- Create a HumanoidDescription local humanoidDescription = Instance.new("HumanoidDescription") humanoidDescription.HatAccessory = "2551510151,2535600138" humanoidDescription.BodyTypeScale = 0.1 humanoidDescription.ClimbAnimation = 619521311 humanoidDescription.Face = 86487700 humanoidDescription.GraphicTShirt = 1711661 humanoidDescription.HeadColor = Color3.new(0, 1, 0) humanoid:ApplyDescription(humanoidDescription) end end ``` #### On all spawning characters Use the following sample code to set a specific `Class.HumanoidDescription` for all spawning player characters: ```lua local Players = game:GetService("Players") -- Stop automatic spawning so it can be done in the "PlayerAdded" callback Players.CharacterAutoLoads = false local function onPlayerAdded(player) -- Create a HumanoidDescription local humanoidDescription = Instance.new("HumanoidDescription") humanoidDescription.HatAccessory = "2551510151,2535600138" humanoidDescription.BodyTypeScale = 0.1 humanoidDescription.ClimbAnimation = 619521311 humanoidDescription.Face = 86487700 humanoidDescription.GraphicTShirt = 1711661 humanoidDescription.HeadColor = Color3.new(0, 1, 0) -- Spawn character with the HumanoidDescription player:LoadCharacterWithHumanoidDescription(humanoidDescription) end -- Connect "PlayerAdded" event to "onPlayerAdded()" function Players.PlayerAdded:Connect(onPlayerAdded) ``` If the `Class.HumanoidDescription` instance was created in the **Explorer** and parented to the workspace, use the following sample code in a `Class.Script` to access the workspace instance: ```lua local Players = game:GetService("Players") -- Stop automatic spawning so it can be done in the "PlayerAdded" callback Players.CharacterAutoLoads = false local function onPlayerAdded(player) -- Spawn character with "workspace.StudioHumanoidDescription" player:LoadCharacterWithHumanoidDescription(workspace.StudioHumanoidDescription) end -- Connect "PlayerAdded" event to "onPlayerAdded()" function Players.PlayerAdded:Connect(onPlayerAdded) ``` ## Layered clothing on non-R15 Caged accessories, like layered clothing, use `Class.WrapTarget` and `Class.WrapLayer` to stretch and wrap over a target `Class.Model`. Layered accessories can work with both standard [R15 Roblox characters](/docs/en-us/characters.md#avatar-characters) and non-R15 models. Custom implementation of layered clothing, such as a model using a unique cage UV map, cannot be uploaded and published to the Marketplace. For more information, see [Layered clothing specifications](/docs/en-us/avatar/layered-accessories/specifications.md). Whether you are implementing layered accessories on an avatar R15 rig, or using a custom rig, ensure that your accessories and bodies include the following: - The target model, typically the body, has a `Class.WrapTarget` component on the meshes that additional models are intended to wrap around. - The layering model, typically the clothing or accessory, has a `Class.WrapLayer` component on the meshes meant to wrap the target model. - The **outer cage of the target model**, and the **inner and outer cage of the layering model** have matching UV maps. - The corresponding vertices on the target cage should have the same UVs as those vertices on the layer cage. - If your target model is not R15, or doesn't include a `Class.Humanoid`, you must add a `Class.Weld` object to the layering `Class.MeshPart`. - The `Class.Weld` must have `Part0` and `Part1` set to link the layering MeshPart to the Part hierarchy of the Model. For example, `Part0` refers to the accessory and `Part1` refers to the parent Part. - If your target model is both R15 and includes a `Class.Humanoid`, this weld is created automatically. > **Info:** See the following resources for additional information on layered clothing and caging: - [Layered clothing caging overview](/docs/en-us/avatar/layered-accessories.md#inner-and-outer-cages) - [Layered clothing caging best practices](/docs/en-us/avatar/layered-accessories/caging-best-practices.md) - [Layered clothing cage mesh specifications](/docs/en-us/avatar/layered-accessories/specifications.md#cage-meshes) - [Avatar body cage specifications](/docs/en-us/avatar/character-bodies/specifications.md#outer-cages) --- title: "Abilities" url: /docs/en-us/characters/character-controller-library/abilities last_updated: 2026-06-29T19:33:58Z description: "Abilities in the Character Controller Library (CCL) evaluate what a character can do, as well as enable flexible behavior composition such as a character being able to move while also aiming and crouching." --- # Abilities **Abilities** in the [Character Controller Library](/docs/en-us/characters/character-controller-library.md) (CCL) evaluate what a character can do, such as the ability to run, jump, climb, and swim. Instead of relying on rigid engine‑defined character states like those in `Enum.HumanoidStateType`, the CCL enables flexible behavior composition such as a character being able to move while also aiming and crouching. > **Info:** At this time, custom abilities are not supported, although the CCL fully supports the traditional character abilities (run, climb, jump, swim, etc.) and you can customize their behaviors more easily. In the future, custom abilities will allow you to add new character mechanics such as crouching, aiming, wall‑jumping, and more. ## Enable CCL The CCL is **opt-in** through Studio's [Avatar Settings](/docs/en-us/studio/avatar-settings.md) window. To enable it: 1. From the **Avatar** tab, open [Avatar Settings](/docs/en-us/studio/avatar-settings.md).![Avatar Settings indicated in Studio's toolbar](../../assets/studio/general/Toolbar-Avatar-Settings.png) 2. Select the **Movement** tab on the left side of the window and, in the **Abilities** section, select **Character Controller Library**.![Character Controller Library toggle in the Avatar Settings window](../../assets/studio/general/Avatar-Settings-CCL.png) 3. All of the standard abilities like **Running**, **Jumping**, and **Climbing** are enabled by default. To disable any of them at runtime, uncheck the associated box. > **Warning:** It's not recommended to disable **Running**, as doing so will prevent characters from moving along the ground. Additionally, you should always keep **Getting Up** enabled if **Falling Down** is enabled, as a mismatch will allow characters to fall down (trip) but never get back up. ## Configuration While your experience is running (or through a script that runs from `Class.ServerScriptService`), you can experiment with the built‑in ability configurations. You can also modify specific [controllers](/docs/en-us/characters/character-controller-library/controllers.md) to adjust the physical simulation of the character and its interaction with the environment, such as the character's base movement speed. ### Runtime To experiment with ability configurations at runtime: 1. Begin a playtest. 2. In the [Explorer](/docs/en-us/studio/explorer.md), locate the character model in the `Class.Workspace` and then expand the `Abilities` tree under `AbilityManagerActor`: 3. Select one of the built-in abilities and, within its **Attributes** section of the [Properties](/docs/en-us/studio/properties.md) window, customize [attributes](/docs/en-us/studio/properties.md#instance-attributes) such as those noted below. | Ability | Attributes | | --- | --- | | `Climbing` | | | `Dead` | | | `FacingMoveDirection` | | | `FallingDown` | | | `Freefall` | | | `GettingUp` | | | `Jumping` | | | `NoLocomotion` | | | `Running` | | | `Sitting` | | | `Swimming` | | ### Scripted To set ability configurations for all characters through a script: 1. Create a new server-side `Class.Script` within `Class.ServerScriptService` and rename it to `AbilitiesScript`. 2. Copy and paste the following code into the new script. This example multiplies the base movement speed for the `Running` ability by `2`. Feel free to adjust other ability attributes such as those described in the [table above](#configuration).```lua local Players = game:GetService("Players") local function onCharacterAdded(character) local AbilityManagerActor = character:WaitForChild("AbilityManagerActor") local Abilities = AbilityManagerActor:WaitForChild("Abilities") local Running = Abilities:FindFirstChild("Running") if Running then -- Double base move speed Running:SetAttribute("SpeedMultiplier", 2) end end local function onPlayerAdded(player) if player.Character then onCharacterAdded(player.Character) end player.CharacterAdded:Connect(onCharacterAdded) end Players.PlayerAdded:Connect(onPlayerAdded) ``` --- title: "Controllers" url: /docs/en-us/characters/character-controller-library/controllers last_updated: 2026-06-29T19:33:58Z description: "Controllers in the Character Controller Library (CCL) handle the physical simulation of the character and its interaction with the environment." --- # Controllers In the [Character Controller Library](/docs/en-us/characters/character-controller-library.md) (CCL), a core `Class.ControllerManager` instance handles the physical simulation of the character and its interaction with the environment. This includes several intentional changes in movement/feel designed to resolve legacy `Class.Humanoid` inconsistencies, as well as physical improvements like ground friction, linear acceleration curves, and realistic conservation of linear and angular momentum when jumping. ## Enable CCL The CCL is **opt-in** through Studio's [Avatar Settings](/docs/en-us/studio/avatar-settings.md) window. To enable it: 1. From the **Avatar** tab, open [Avatar Settings](/docs/en-us/studio/avatar-settings.md).![Avatar Settings indicated in Studio's toolbar](../../assets/studio/general/Toolbar-Avatar-Settings.png) 2. Select the **Movement** tab on the left side of the window and, in the **Abilities** section, select **Character Controller Library**.![Character Controller Library toggle in the Avatar Settings window](../../assets/studio/general/Avatar-Settings-CCL.png) 3. All of the standard abilities like **Running**, **Jumping**, and **Climbing** are enabled by default. To disable any of them at runtime, uncheck the associated box. > **Warning:** It's not recommended to disable **Running**, as doing so will prevent characters from moving along the ground. Additionally, you should always keep **Getting Up** enabled if **Falling Down** is enabled, as a mismatch will allow characters to fall down (trip) but never get back up. ## Structure Under the core `Class.ControllerManager` instance within the character model, individual controllers such as a `Class.GroundController` and `Class.AirController` define how movement is applied to the character. Abilities then interact with the `Class.ControllerManager` and its descendants to modify controller behaviors or switch between controllers. ## Configuration While your game is running (or through a script that runs from `Class.ServerScriptService`), you can experiment with the core `Class.ControllerManager` and built‑in controllers. ### Runtime To experiment with controller configurations at runtime: 1. Begin a playtest. 2. In the [Explorer](/docs/en-us/studio/explorer.md), locate the character model in the `Class.Workspace` and then expand the `Class.ControllerManager` tree: 3. Select either the `Class.ControllerManager` or one of its controller descendants and, in the [Properties](/docs/en-us/studio/properties.md) window, customize properties such as those summarized in the tables below (note that these tables are not exhaustive; please consult the API [classes documentation](/docs/en-us/reference/engine/classes.md) for additional property options). #### ControllerManager | Property | Description | | --- | --- | | `Class.ControllerManager.BaseMoveSpeed\|BaseMoveSpeed` | The **base** linear movement speed used by all controllers. Controllers individually customize movement speed through their `MoveSpeedFactor` property. | | `Class.ControllerManager.BaseTurnSpeed\|BaseTurnSpeed` | The **base** angular turning speed used by all controllers to align the character to face the desired direction. Some controllers individually customize turn speed through their `TurnSpeedFactor` property. | | `Class.ControllerManager.UpDirection\|UpDirection` | `Datatype.Vector3` which indicates the upward-facing vector for the `Class.ControllerManager.RootPart`. | #### Individual Controllers | Controller | Properties | | --- | --- | | `Class.GroundController` | | | `Class.AirController` | | | `Class.ClimbController` | | | `Class.SwimController` | | ### Scripted To set controller configurations for all characters through a script: 1. Create a new server-side `Class.Script` within `Class.ServerScriptService` and rename it to `ControllerScript`. 2. Copy and paste the following code into the new script. This example increases ground‑based moving/turning speed as well adds a slight acceleration and deceleration time. Feel free to adjust other properties such as those described in the [tables above](#configuration) or for each class as documented (`Class.ControllerManager`; `Class.GroundController`; `Class.AirController`; `Class.ClimbController`; `Class.SwimController`).```lua local Players = game:GetService("Players") local function onCharacterAdded(character) local ControllerManager = character:FindFirstChildWhichIsA("ControllerManager") if ControllerManager then local GroundController = ControllerManager:FindFirstChild("GroundController") if GroundController then -- Double the move and turn speeds GroundController.MoveSpeedFactor *= 2 GroundController.TurnSpeedFactor *= 2 -- Add slight acceleration and deceleration GroundController.AccelerationTime = 0.2 GroundController.DecelerationTime = 0.4 end end end local function onPlayerAdded(player) if player.Character then onCharacterAdded(player.Character) end player.CharacterAdded:Connect(onCharacterAdded) end Players.PlayerAdded:Connect(onPlayerAdded) ``` --- title: "Character Controller Library" url: /docs/en-us/characters/character-controller-library last_updated: 2026-06-29T19:33:57Z description: "The Character Controller Library (CCL) is a modular framework for building character movement and behaviors through attributes and Luau scripts." --- # Character Controller Library The **Character Controller Library** (CCL) is a modular framework for building character movement and behaviors through attributes and Luau scripts. This architecture replaces rigid `Class.Humanoid` state machines with a flexible, extensible system for character mechanics built around abilities and controllers. > **Success:** The CCL is **opt-in** through the [Avatar Settings](/docs/en-us/studio/avatar-settings.md) window and experiences can continue using the legacy movement system. ## Abilities [Abilities](/docs/en-us/characters/abilities.md) evaluate what a character can do, such as the ability to run, jump, climb, and swim. Instead of relying on a fixed set of engine‑defined character states like those in `Enum.HumanoidStateType`, abilities dynamically determine what a character can do and how it should respond to player input. > **Info:** At this time, custom abilities are not supported, although the CCL fully supports the traditional character abilities (run, climb, jump, swim, etc.) and you can customize their behaviors more easily. In the future, custom abilities will allow you to add new character mechanics such as crouching, aiming, wall‑jumping, and more. ## Controllers [Controllers](/docs/en-us/characters/controllers.md) apply the physical simulation of movement, for example movement behavior on ground, in the air, in water, etc. [Abilities](/docs/en-us/characters/abilities.md) interact with controllers to change physical behaviors or to switch between controller contexts; for example, an ability might update movement acceleration, change direction, or activate an air controller instead of a ground controller. --- title: "Emotes" url: /docs/en-us/characters/emotes last_updated: 2026-06-29T19:33:57Z description: "Emotes are character animations used through chat commands, or accessing the emotes menu." --- # Emotes Emotes are expressive character [animations](/docs/en-us/animation.md) that are accessible by using chat commands ("/e cheer") or by accessing the **emotes menu** on the top right of any experience. All users have access to default emotes, such as **dance**, **point**, and **cheer**. Additional avatar emotes can be purchased and equipped from the [Marketplace](https://www.roblox.com/catalog). In your experience, you can perform the following emote customizations: - [Open and close](#open-and-close) a user's emotes menu programmatically. - [Add or remove](#add-and-remove-emotes) emotes options from a user's menu. - [Disable](#disable) access to the menu. - [Play](#play-emotes) an emote, targeting a specific user character. ## Emotes menu You can open and close a user's emote menu manually, customize the menu to display specific emotes, or disable the menu completely. ### Open and close To manually open or close a player's emote menu, call `Class.GuiService:SetEmotesMenuOpen()` with a boolean value of true or false. The following code sample will open the emotes menu for the user: ```lua -- Open the emote Menu local GuiService = game:GetService("GuiService") GuiService:SetEmotesMenuOpen(true) ``` If you need to detect whether the emotes menu is open, call `Class.GuiService:GetEmotesMenuOpen()`. This returns a boolean indicating the menu's current state. ### Add and remove emotes Customize the emote menu by setting emotes from the catalog and then equipping emotes to a `Class.Humanoid`. Set emotes with the `Class.HumanoidDescription:SetEmotes()` method and equip up to 8 emotes to the emotes menu using `Class.HumanoidDescription:SetEquippedEmotes()`. Use the following code sample in a `Class.LocalScript` within the `Class.StarterCharacterScripts` folder to set and equip emotes in your experience: ```lua local Players = game:GetService("Players") local humanoid = Players.LocalPlayer.Character.Humanoid local humanoidDescription = humanoid.HumanoidDescription -- Set custom emotes within a table local emoteTable = { ["Hello"] = {3576686446}, ["Stadium"] = {3360686498}, ["Tilt"] = {3360692915}, ["Shrug"] = {3576968026}, ["Salute"] = {3360689775}, ["Point"] = {3576823880} } humanoidDescription:SetEmotes(emoteTable) -- Equip emotes in a specific order local equippedEmotes = {"Hello", "Stadium", "Tilt", "Shrug", "Salute", "Point"} humanoidDescription:SetEquippedEmotes(equippedEmotes) ``` > **Info:** Key names, such as "Shrug" or "Salute" are customizable and will appear to the player as written. Emotes will also show up in the order they were set, starting at the top of the wheel and continuing clockwise. ### Disable Disable the emotes menu with `Class.StarterGui:SetCoreGuiEnabled()`. Disabling the emotes menu will not prevent emotes from being performed with a chat command. The following sample code will disable the emotes menu: ```lua local StarterGui = game:GetService("StarterGui") StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType.EmotesMenu, false) ``` In addition to disabling the menu, you can disable loading of user-owned emotes by setting the `Class.StarterPlayer.UserEmotesEnabled` property within **StarterPlayer** ⟩ **Character** to **false**. This specific property can only be set in Studio and cannot be set by scripts. ## Play emotes To manually play an emote that a character has in its `Class.HumanoidDescription`, call `Class.Humanoid:PlayEmote()`, passing the string name of the emote. This call will return true to indicate that the emote was played successfully, or false otherwise. Use the following code sample to play the Shrug emote: ```lua local Players = game:GetService("Players") local humanoid = Players.LocalPlayer.Character.Humanoid humanoid:PlayEmote("Shrug") ``` --- title: "Characters" url: /docs/en-us/characters last_updated: 2026-06-29T19:33:57Z description: "Characters are interactive models that interact with the world or other users." --- # Characters **Characters** typically refer to any `Class.Model` objects that interact with the world or other users. While a character can be as simple as a glowing sphere that communicates and interacts with players, characters are often human-like models with additional means of expression to encourage immersion and realism. Characters range between **basic** characters, such as simple non-player characters (NPCs), or **avatar** characters, which are user-controlled models that include advanced features for movement, animation, and cosmetics. All Roblox users are associated with an account-based avatar character. Along with this avatar character, Roblox represents users as [players](/docs/en-us/players.md) in the data model, giving creators access to additional character customization properties, social features, and relevant gameplay and account information. ## Basic characters Basic characters are often used as NPCs, and they typically perform one or two simple tasks. A common component of basic characters include a display name, health, and basic movement. You can use the following components within your `Class.Model` object to enable these basic features: - A group of parts, or [assembly](/docs/en-us/physics/assemblies.md), that includes the following: - A collection with the name `HumanoidRootPart` to indicate the root part of the assembly. - A part with the name `Head` to display the character's name over their head geometry. - Additional mesh parts that make up the individual body parts, which commonly include the 6 (R6) or 15 (R15) body parts used for human-like models. - Joints, such as `Class.Bone` or `Class.Motor6D`, that connects each body part as an assembly. - A `Class.Humanoid` instance to implement and access common character properties. _Basic Character Example (R6)_ _Data Model_ With these components, the character model displays a `Class.Humanoid.DisplayName`, has health, and can move with `Class.Humanoid.Move()`. For more information on additional configurations of the display and health elements, see [Name/health display](/docs/en-us/characters/name-health-display.md). > **Info:** For information on implementing layered clothing on a non-R15 character model, see [Character appearance](/docs/en-us/appearance.md#layered-clothing-on-non-r15). ## Avatar characters Avatar characters use a standardized set of character components that allow you to use body and facial animations, and equip clothing and accessories. By standardizing the components, all avatar character models can access these features, whether they are controlled by a player or an NPC. By default, all players join experiences as their saved Roblox avatar, which already includes all the components for an avatar character. A complete avatar character has the following components in their `Class.Model`: - An [assembly](/docs/en-us/physics/assemblies.md) of `Class.MeshPart` objects, that includes the following: - A part with the name `HumanoidRootPart` to indicate the root part of the assembly. - A part with the name `Head` to display the character's [name and health](/docs/en-us/name-health-display.md) over their head geometry. - 15 meshes that make up the individual body parts, such as the character's arm or leg. - Joints, such as `Class.Bone` or `Class.Motor6D` objects, that connect each part and follow a [standardized](/docs/en-us/avatar/character-bodies/specifications.md#rigging) joint hierarchy. - A `Class.Humanoid` instance to implement and access common character properties. - `Class.WrapLayer` objects for each of the model's body parts, enabling it to wear clothing and other layerable cosmetics. - `Class.FaceControls` to enable facial expressions and poses for the character's head. - `Class.Attachment` objects for each of the standard attachment points on the character body. _Avatar Character Example (R15)_ _Data Model_ Standard avatar character rigs require 15 poseable joints, and higher-fidelity character rigs support up to 37 additional joints for a greater level of realism with articulated hands, shoulders, and spine movements. For more information on higher-fidelity rigs, their specific bone hierarchy and naming conventions, and how to animate them with `Class.HumanoidRigDescription` and `Class.DigitsRigDescription` objects, see [Character specifications - Higher-fidelity rigs](/docs/en-us/avatar/character-bodies/specifications.md#higher-fidelity-rigs). > **Info:** If you are creating your own custom avatar character, you must configure your character model's components in a third-party modeling tool like Blender or Maya before importing the model into Studio. See [Avatars](/docs/en-us/avatar/character-bodies.md) for information regarding the creation of avatar components, creation guides, and Marketplace information. ## Character Controller Library The [Character Controller Library](/docs/en-us/character-controller-library.md) (CCL) is a modular framework for building out character movement and behaviors through attributes and Luau scripts. This architecture replaces rigid state machines with a flexible, ability‑driven system for character mechanics. --- title: "Character name/health display" url: /docs/en-us/characters/name-health-display last_updated: 2026-06-29T19:33:58Z description: "You can customize character UI, like name and health displays, using Class.Humanoid." --- # Character name/health display The `Class.Humanoid` instance is used to create character models, both for user avatars and NPCs. When a `Class.Humanoid` is present inside a `Class.Model` that contains a part named **Head**, Roblox displays a name and/or health bar above that part. ![Character display information above an in-experience avatar](../assets/avatar/name-health-display/Display-Indicated.jpg) Through various `Class.Humanoid` properties, you can modify the following: - The [distance](#display-distance-type) from which users can see the name/health of other humanoids in relation to their own character's humanoid. - The [display name](#override-display-names) which shows over a humanoid. - Whether a humanoid's [health bar](#health-display-type) always appears, never appears, or only appears when the humanoid is damaged. - Whether names and health bars are [occluded](#occlusion) (hidden) when line-of-sight between the camera and another humanoid is blocked. > **Warning:** As noted in the introduction, character name/health display requires that the `Class.Humanoid` instance is inside a `Class.Model` and that the model contains a `Class.BasePart` named **Head**. Both objects must also be at the same level in the model's hierarchy. ## Display properties ### Display distance type The `Class.Humanoid.DisplayDistanceType` property sets how users see the name/health of other characters in relation to their own character. #### Viewer When a humanoid's `Class.Humanoid.DisplayDistanceType|DisplayDistanceType` is set to `Enum.HumanoidDisplayDistanceType|HumanoidDisplayDistanceType.Viewer`, it sees the name/health of other humanoids within range of its own `Class.Humanoid.NameDisplayDistance|NameDisplayDistance` and `Class.Humanoid.HealthDisplayDistance|HealthDisplayDistance`. You can consider this the lowest priority since it is not taken into account for other humanoids configured as [subject](#subject) or [none](#none). In the following scenario, the user's character (**Viewer**) has a larger `Class.Humanoid.NameDisplayDistance|NameDisplayDistance` than `Class.Humanoid.HealthDisplayDistance|HealthDisplayDistance`, as indicated by the circles. As a result, the user sees character names for both **Watchman** and **Octavia**, but only sees a health bar for **Watchman**. #### Subject When a humanoid's `Class.Humanoid.DisplayDistanceType|DisplayDistanceType` is set to `Enum.HumanoidDisplayDistanceType|HumanoidDisplayDistanceType.Subject`, it takes **full control** over its own name and health display through its `Class.Humanoid.NameDisplayDistance|NameDisplayDistance` and `Class.Humanoid.HealthDisplayDistance|HealthDisplayDistance` values. Effectively, other humanoids will only see the subject's name/health within those distances from the **subject** humanoid. In the following scenario, both **Watchman** and **Octavia** are set to **Subject** and their `Class.Humanoid.NameDisplayDistance|NameDisplayDistance` ranges are indicated by the circles. Only the name **Octavia** is seen by the user whose character is standing inside her range but outside the **Watchman** humanoid's range. #### None When a humanoid's `Class.Humanoid.DisplayDistanceType|DisplayDistanceType` is set to `Enum.HumanoidDisplayDistanceType|HumanoidDisplayDistanceType.None`, its name and health bar do not appear under any circumstances. In the following scenario, both **Watchman** and **Octavia** are set to **None**, so the other character does not see their name or health even when in range. ### Health display type The `Class.Humanoid.HealthDisplayType` property provides further control over the character's health bar visibility. The bar reflects the humanoid's `Class.Humanoid.Health|Health` as a factor of its `Class.Humanoid.MaxHealth|MaxHealth` and it changes color from green to yellow to red as the humanoid's health decreases. #### AlwaysOn When a humanoid's `Class.Humanoid.HealthDisplayType|HealthDisplayType` is set to `Enum.HumanoidHealthDisplayType|HumanoidHealthDisplayType.AlwaysOn`, its health bar always appears. #### DisplayWhenDamaged A humanoid with `Class.Humanoid.HealthDisplayType|HealthDisplayType` set to `Enum.HumanoidHealthDisplayType|HumanoidHealthDisplayType.DisplayWhenDamaged` only shows a health bar when its `Class.Humanoid.Health|Health` is less than its `Class.Humanoid.MaxHealth|MaxHealth`. In the following scenario, **Watchman** has full health and does not display a health bar, but **Octavia** is damaged by 50% and displays a yellow health bar. #### AlwaysOff When a humanoid's `Class.Humanoid.HealthDisplayType|HealthDisplayType` is set to `Enum.HumanoidHealthDisplayType|HumanoidHealthDisplayType.AlwaysOff`, its health bar never appears under any circumstances. ### Occlusion Occlusion (hiding) of humanoid names behind walls or other objects is controlled by the character's `Class.Humanoid.NameOcclusion` property. > **Warning:** Occlusion does not occur if the occluding object's `Class.BasePart.Transparency|Transparency` is higher than 0.99. Occlusion also does not occur when a humanoid is hidden behind a `Class.Model` containing a `Class.Humanoid`, such as another user's avatar character. #### NoOcclusion When a humanoid is hidden behind a visible object and its `Class.Humanoid.NameOcclusion|NameOcclusion` is set to `Enum.NameOcclusion|NameOcclusion.NoOcclusion`, its name and health are never occluded from viewing humanoids. In the following scenario, both **Watchman** and **Octavia** are set to **NoOcclusion**. Although both are sufficiently hidden behind stone columns, the viewing humanoid still sees their name/health displays. #### OccludeAll When a humanoid is hidden behind a visible object and its `Class.Humanoid.NameOcclusion|NameOcclusion` is set to `Enum.NameOcclusion|NameOcclusion.OccludeAll`, its name and health are always occluded from viewing humanoids. In the following scenario, both **Watchman** and **Octavia** are sufficiently hidden behind stone columns. **Watchman** is set to **OccludeAll**, so its name and health are hidden from the viewing humanoid. **Octavia**, however, is set to [NoOcclusion](#noocclusion) and her name/health remains visible to the viewing humanoid. #### EnemyOcclusion When a humanoid is hidden behind a visible object and its `Class.Humanoid.NameOcclusion|NameOcclusion` is set to `Enum.NameOcclusion|NameOcclusion.EnemyOcclusion`, its name and health are only occluded from enemy humanoids (players on a different `Class.Team`). In the following scenario, both **Watchman** and **Octavia** are sufficiently hidden behind stone columns, and both are set to **EnemyOcclusion**. The viewing humanoid and **Watchman** are on the same `Class.Team`, so name/health occlusion does not occur. However, the name and health of **Octavia**, on the opposing team, are occluded. ## Modify character displays ### User avatars To modify the name or health display for every incoming avatar in an experience, connect the `Class.Players.PlayerAdded` and `Class.Player.CharacterAdded` events in a `Class.Script` and set [display properties](#display-properties) on the character's `Class.Humanoid`. ```lua local Players = game:GetService("Players") local function onPlayerAdded(player) player.CharacterAdded:Connect(function(character) local humanoid = character:FindFirstChildWhichIsA("Humanoid") if humanoid then -- Give each humanoid full control over its name/health display distance humanoid.DisplayDistanceType = Enum.HumanoidDisplayDistanceType.Subject -- Set name display distance to 20 studs humanoid.NameDisplayDistance = 20 -- Set health bar display distance to 15 studs humanoid.HealthDisplayDistance = 15 -- Only show health bar when humanoid is damaged humanoid.HealthDisplayType = Enum.HumanoidHealthDisplayType.DisplayWhenDamaged end end) end Players.PlayerAdded:Connect(onPlayerAdded) ``` You can also customize properties based on a player's `Class.Team`, such as setting all "guard" players to a generic name, and hiding the names of all "ninja" players. ```lua local Players = game:GetService("Players") local function onPlayerAdded(player) player.CharacterAdded:Connect(function(character) local humanoid = character:FindFirstChildWhichIsA("Humanoid") if humanoid then -- Set the name of all guards to generic "Guard" if player.Team.Name == "Guards" then humanoid.DisplayName = "Guard" -- Hide the name for all ninjas elseif player.Team.Name == "Ninjas" then humanoid.DisplayDistanceType = Enum.HumanoidDisplayDistanceType.None end end end) end Players.PlayerAdded:Connect(onPlayerAdded) ``` > **Info:** When a player is assigned to a `Class.Team`, their character's name appears in the same color as the team's `Class.Team.TeamColor|TeamColor`. This helps identify teammates and opposing players in team-based experiences. ### NPC characters For NPC characters already placed in the 3D world, you can edit name/health directly on the `Class.Humanoid` object in the [Properties](/docs/en-us/studio/properties.md) window. ## Override display names By default, a humanoid's display name matches the user's Roblox account **Display Name** which is unique and separate from their account **Username**. To show a fully custom name that's unrelated to the user's account, you can override the `Class.Humanoid.DisplayName` property. ### Set directly You can set the `Class.Humanoid.DisplayName|DisplayName` property of any `Class.Humanoid` instance which you gain reference to through a `Class.Script`, such as the [team customization](#user-avatars) example, or directly on an [NPC](#npc-characters) character's **Humanoid** object. ### Set through user input In some genres like roleplaying or fighting, you may want to provide a method for users to input their own character name, pet character name, etc. that's specific to the experience and isn't tied to their account display name. You can gather this input on the client side through a `Class.TextBox` name entry. Once the input is submitted, you can pass it to the server through a [remote event](/docs/en-us/scripting/events/remote.md) and then, on the server side, listen for the remote event and assign the [filtered](/docs/en-us/ui/text-filtering.md) name to the user character's `Class.Humanoid`. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local changeNameEvent = ReplicatedStorage:WaitForChild("ChangeNameEvent") changeNameEvent:FireServer("Amory") ``` ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local TextService = game:GetService("TextService") -- Create remote event to receive text from client for filtering local changeNameEvent = Instance.new("RemoteEvent") changeNameEvent.Name = "ChangeNameEvent" changeNameEvent.Parent = ReplicatedStorage local function onRequestNameChange(player, newName) local character = player.Character local humanoid = character:FindFirstChildWhichIsA("Humanoid") local filterResult local success, errorMessage = pcall(function() filterResult = TextService:FilterStringAsync(newName, player.UserId) end) if success then local filteredName local success, errorMessage = pcall(function() filteredName = filterResult:GetNonChatStringForBroadcastAsync() end) if success and humanoid then humanoid.DisplayName = filteredName end end end changeNameEvent.OnServerEvent:Connect(onRequestNameChange) ``` --- title: "Pathfinding" url: /docs/en-us/characters/pathfinding last_updated: 2026-06-29T19:33:58Z description: "Pathfinding is the process of moving a character or object (agent) along a logical path to reach a destination." --- # Pathfinding **Pathfinding** is the process of moving a character or object (agent) along a logical path around obstacles to reach a destination, optionally avoiding hazardous materials or defined regions. ## Navigation visualization To assist with pathfinding layout and debugging, Studio can render a navigation mesh and [modifier](#pathfinding-modifiers) labels. To enable them, toggle on **Navigation mesh** and **Pathfinding modifiers** from the [Visualization Options](/docs/en-us/studio/ui-overview.md#visualization-options) widget in the upper‑right corner of the 3D viewport. ![A close up view of the 3D viewport with the Visualization Options button indicated in the upper-right corner.](../assets/studio/general/Visualization-Options.png) #### Navigation Mesh With **Navigation mesh** enabled, colored areas show where a character might walk or swim. Small arrows indicate areas that a character will attempt to reach by jumping. ![Navigation mesh showing in Studio](../assets/avatar/pathfinding/Navigation-Mesh.jpg) #### Pathfinding Modifiers With **Pathfinding modifiers** enabled, text labels indicate specific materials and regions that are taken into consideration when using [pathfinding modifiers](#pathfinding-modifiers). ![Navigation labels showing on navigation mesh](../assets/avatar/pathfinding/Navigation-Labels.jpg) ## Implementation Although pathfinding can be implemented in various ways through `Class.PathfindingService` and its associated methods such as `Class.PathfindingService:CreatePath()|CreatePath()`, this section uses the following pathfinding script for the player's character. To test while reading: 1. **IMPORTANT** In the [Explorer](/docs/en-us/studio/explorer.md), select the `Class.StarterPlayer` container. Then, in the [Properties](/docs/en-us/studio/properties.md) window, set both `Class.StarterPlayer.DevComputerMovementMode|DevComputerMovementMode` and `Class.StarterPlayer.DevTouchMovementMode|DevTouchMovementMode` to `Scriptable`. 2. Copy the following code into a `Class.LocalScript` within `Class.StarterCharacterScripts`, or [get this package](https://create.roblox.com/store/asset/95888831676289/Player-Path-Following-Script) and drop it into `Class.StarterCharacterScripts`.```lua local PathfindingService = game:GetService("PathfindingService") local Players = game:GetService("Players") local RunService = game:GetService("RunService") local DESTINATION = Vector3.new(20, 0.5, 20) local GROUND_WAIT = 0.01 local VELOCITY_MULTIPLIER = 0.0625 local path = PathfindingService:CreatePath({ AgentCanClimb = true, Costs = { Water = 20 } }) local character = script.Parent local humanoid = character:WaitForChild("Humanoid") local waypoints local nextWaypointIndex local blockedConnection local currentWaypointReachedConnection local currentWaypointPlaneNormal = Vector3.zero local currentWaypointPlaneDistance = 0 local pathfinderWorking = false local function disconnectCurrentWaypointReachedConnection() if not currentWaypointReachedConnection then return end currentWaypointReachedConnection:Disconnect() currentWaypointReachedConnection = nil end local function isCurrentWaypointReached() if humanoid.FloorMaterial == Enum.Material.Air then return false end local reached = false if currentWaypointPlaneNormal ~= Vector3.zero then -- Compute the distance from humanoid to destination plane local dist = currentWaypointPlaneNormal:Dot(humanoid.RootPart.Position) - currentWaypointPlaneDistance -- Compute the component of the humanoid velocity that is towards the plane local velocity = -currentWaypointPlaneNormal:Dot(humanoid.RootPart.Velocity) -- Compute the threshold from the destination plane based on humanoid velocity local threshold = math.max(1.0, VELOCITY_MULTIPLIER * velocity) -- Consider waypoint reached if less then threshold in front of the plane reached = dist < threshold else reached = true end if reached then currentWaypointPlaneNormal = Vector3.zero currentWaypointPlaneDistance = 0 moveToNextWaypoint() end end local function calculateNextWaypointApproach() nextWaypointIndex += 1 if nextWaypointIndex > #waypoints then return false end local currentWaypoint = waypoints[nextWaypointIndex - 1] local nextWaypoint = waypoints[nextWaypointIndex] -- Build destination plane from next waypoint towards current one currentWaypointPlaneNormal = currentWaypoint.Position - nextWaypoint.Position -- Set normal perpendicular to Y plane when not climbing up if nextWaypoint.Label ~= "Climb" then currentWaypointPlaneNormal = Vector3.new(currentWaypointPlaneNormal.X, 0, currentWaypointPlaneNormal.Z) end if currentWaypointPlaneNormal.Magnitude > 0.000001 then currentWaypointPlaneNormal = currentWaypointPlaneNormal.Unit currentWaypointPlaneDistance = currentWaypointPlaneNormal:Dot(nextWaypoint.Position) end return true end local function resetWaypointData() humanoid:Move(Vector3.zero) currentWaypointPlaneNormal = Vector3.zero currentWaypointPlaneDistance = 0 disconnectCurrentWaypointReachedConnection() pathfinderWorking = false end local function waitForGround() while humanoid.FloorMaterial == Enum.Material.Air do task.wait(GROUND_WAIT) end end function moveToNextWaypoint() if calculateNextWaypointApproach() then disconnectCurrentWaypointReachedConnection() currentWaypointReachedConnection = RunService.Heartbeat:Connect(isCurrentWaypointReached) local nextWaypointPosition = waypoints[nextWaypointIndex].Position local nextWaypointAction = waypoints[nextWaypointIndex].Action humanoid:Move(nextWaypointPosition - humanoid.RootPart.Position) if waypoints[nextWaypointIndex + 1] and waypoints[nextWaypointIndex + 1].Label == "UseBoat" then nextWaypointIndex += 1 -- Call your own customized function to make agent use the boat elseif nextWaypointAction == Enum.PathWaypointAction.Jump then humanoid:ChangeState(Enum.HumanoidStateType.Jumping) while humanoid.FloorMaterial ~= Enum.Material.Air do task.wait(GROUND_WAIT) end humanoid:Move(nextWaypointPosition - humanoid.RootPart.Position) end else resetWaypointData() end end local function findStartingPoint(waypoints) nextWaypointIndex = 1 while nextWaypointIndex + 1 <= #waypoints do local dist = waypoints[nextWaypointIndex + 1].Position - humanoid.RootPart.Position dist = Vector3.new(dist.X, 0, dist.Z) if dist.magnitude >= 2 then return end nextWaypointIndex += 1 end end local function followPath() -- Compute the path pathfinderWorking = true waitForGround() local success, errorMessage = pcall(function() path:ComputeAsync(character.PrimaryPart.Position, DESTINATION) end) if not success or path.Status ~= Enum.PathStatus.Success then warn("Path not computed!", errorMessage) return end -- Get the path waypoints waypoints = path:GetWaypoints() -- Detect if path becomes blocked blockedConnection = path.Blocked:Connect(function(blockedWaypointIndex) -- Check if the obstacle is further down the path if blockedWaypointIndex >= nextWaypointIndex then -- Stop detecting path blockage until path is re-computed blockedConnection:Disconnect() resetWaypointData() -- Call function to re-compute new path followPath() end end) findStartingPoint(waypoints) moveToNextWaypoint() end followPath() ``` 3. Edit the `DESTINATION` variable (**LINE 5**) to a `Datatype.Vector3` destination within the 3D world that the player character can reach. 4. Proceed through the following sections to learn about path computation and character movement. ### Path creation Pathfinding is initiated through `Class.PathfindingService` and its `Class.PathfindingService:CreatePath()|CreatePath()` method (**LINES 9–14**). This method accepts an optional table of parameters which fine tune how the character (agent) moves along the path. | Key | Description | Type | Default | | --- | --- | --- | --- | | `AgentRadius` | Agent radius, in studs. Useful for determining the minimum separation from obstacles. | integer | `2` | | `AgentHeight` | Agent height, in studs. Empty space smaller than this value, like the space under stairs, will be marked as non-traversable. | integer | `5` | | `AgentCanJump` | Determines whether jumping during pathfinding is allowed. | boolean | `true` | | `AgentCanClimb` | Determines whether climbing `Class.TrussPart\|TrussParts` during pathfinding is allowed. A climbable path has a `Datatype.PathWaypoint.Label\|Label` named `Climb` and the [cost](#material-costs) for a climbable path is `1` by default. | boolean | `false` | | `WaypointSpacing` | Spacing between intermediate waypoints in path. If set to `Library.math.huge`, there will be no intermediate waypoints. | number | `4` | | `Costs` | Table of materials or defined `Class.PathfindingModifier\|PathfindingModifiers` and their cost for traversal. Useful for making the agent prefer certain materials/regions over others. See [modifiers](#pathfinding-modifiers) for details. | table | `nil` | ### Path computation After you've created a valid path with `Class.PathfindingService:CreatePath()|CreatePath()`, it must be **computed** by calling `Class.Path:ComputeAsync()` with a `Datatype.Vector3` for both the starting point and destination (**LINES 133–139**). ![Path start/end marked across two bridges](../assets/avatar/pathfinding/Path-Start-End.jpg) Once the `Class.Path` is computed, it will contain a series of **waypoints** that trace the path from start to end. These points can be gathered with the `Class.Path:GetWaypoints()` method (**LINE 142**). The returned array is arranged in order of waypoints from path start to path end. ![Waypoints indicated across computed path](../assets/avatar/pathfinding/Waypoints.jpg)_Waypoints indicated across computed path_ > **Warning:** The pathfinding engine includes specific limitations, and pathfinding computations can fail for various reasons. If your implementation does not behave as expected, see [limitations and failure factors](#limitations-and-failure-factors) for possible causes. ### Path movement Each `Datatype.PathWaypoint` consists of both a `Datatype.PathWaypoint.Position|Position` (`Datatype.Vector3`) and `Datatype.PathWaypoint.Action|Action` (`Enum.PathWaypointAction|PathWaypointAction`). To move a character containing a `Class.Humanoid`, like a typical Roblox character, the best way is to call `Class.Humanoid:Move()` from waypoint to waypoint and use the script's `isCurrentWaypointReached()` callback (**LINES 32–56**) to detect when the character reaches each waypoint. > **Warning:** Note that the script waits for the humanoid to be touching the ground before calling the pathfinder. If the path is computed while the character is falling through the air, the pathfinder will try to determine an appropriate start position for the path. ### Blocked paths Many Roblox worlds are dynamic; parts might move or fall and floors may collapse. This can block a computed path and prevent the character from reaching its destination. To handle this, you can connect the `Class.Path.Blocked` event and re-compute the path around whatever blocked it (**LINES 145–154**). > **Warning:** Paths may also become blocked somewhere **behind** the agent, such as a pile of rubble falling on a path as the agent runs away, but that doesn't mean the agent should stop moving. The`if blockedWaypointIndex >= nextWaypointIndex` check makes sure that the path is re-computed only if the blocked waypoint is **ahead** of the current waypoint. ## Pathfinding modifiers By default, `Class.Path:ComputeAsync()` returns the **shortest** path between the starting point and destination, with the exception that it attempts to avoid jumps. This looks unnatural in some situations; for instance, a path may go through swamp water rather than around it simply because the path through the water is geometrically shorter. ![Two paths indicated with the shorter path not necessarily more logical](../assets/avatar/pathfinding/Paths-Shortest-Best.jpg) To optimize pathfinding even further, you can implement **pathfinding modifiers** to compute smarter paths across various [materials](#material-costs), around defined [regions](#configure-regions), or to [ignore obstacles](#ignore-obstacles). ### Material costs When working with `Class.Terrain` and `Class.BasePart` materials, you can include a `Costs` table within `Class.PathfindingService:CreatePath()|CreatePath()` to make certain materials more traversable than others. All materials have a default cost of `1` and any material can be defined as non‑traversable by setting its value to `Library.math.huge`. Keys in the `Costs` table should be **string** names representing `Enum.Material` names, for example `Water` for `Enum.Material.Water` or `CrackedLava` for `Enum.Material.CrackedLava`. ```lua local PathfindingService = game:GetService("PathfindingService") local Players = game:GetService("Players") local RunService = game:GetService("RunService") local DESTINATION = Vector3.new(20, 0.5, 20) local GROUND_WAIT = 0.01 local VELOCITY_MULTIPLIER = 0.0625 local path = PathfindingService:CreatePath({ AgentCanClimb = true, Costs = { Water = 20, CrackedLava = 100, Slate = 20 } }) ``` > **Info:** To set a material such as `CrackedLava` as a "last option," assign it a high but finite material cost like `100` or `1000`. Since there's no engine‑enforced upper cap below `Library.math.huge`, the pathfinder will only route through the material if every alternative is more expensive. ### Configure regions In some cases, [material preference](#material-costs) is not enough. For example, you might want characters to avoid a defined **region**, regardless of the materials underfoot. This can be achieved by adding a `Class.PathfindingModifier` object to a part. 1. Create an `Class.BasePart.Anchored|Anchored` part around the region and set its `Class.BasePart.CanCollide|CanCollide` property to `false`.![Anchored part defining a region to apply a pathfinding modifier to.](../assets/avatar/pathfinding/GeyserBlocker-Block.jpg) 2. Insert a `Class.PathfindingModifier` instance onto the part, locate its `Class.PathfindingModifier.Label|Label` property, and assign a meaningful name like `DangerZone`.![PathfindingModifier instance with Label property set to DangerZone.](../assets/studio/properties/PathfindingModifier-Label.png) 3. Include a matching `DangerZone` key and associated numeric value within the `Costs` table of `Class.PathfindingService:CreatePath()|CreatePath()`. A modifier can be defined as non‑traversable by setting its value to `Library.math.huge`.```lua local PathfindingService = game:GetService("PathfindingService") local Players = game:GetService("Players") local RunService = game:GetService("RunService") local DESTINATION = Vector3.new(20, 0.5, 20) local GROUND_WAIT = 0.01 local VELOCITY_MULTIPLIER = 0.0625 local path = PathfindingService:CreatePath({ AgentCanClimb = true, Costs = { DangerZone = math.huge, Water = 20, CrackedLava = 20, Slate = 20 } }) ``` ### Ignore obstacles In some cases, it's useful to pathfind through solid obstacles as if they didn't exist. This lets you compute a path through specific physical blockers, versus the computation failing outright. 1. Create an `Class.BasePart.Anchored|Anchored` part around the object and set its `Class.BasePart.CanCollide|CanCollide` property to `false`.![Anchored part defining a region to apply a pathfinding modifier to.](../assets/avatar/pathfinding/DoorPassThrough-Block.jpg) 2. Insert a `Class.PathfindingModifier` instance onto the part and enable its `Class.PathfindingModifier.PassThrough|PassThrough` property.![PathfindingModifier instance with PassThrough property enabled.](../assets/studio/properties/PathfindingModifier-PassThrough.png) Now, when a path is computed from the zombie NPC to the player character, the path extends beyond the door and you can prompt the zombie to traverse it. Even if the zombie is unable to open the door, it reacts as if it "hears" the character behind the door.![Zombie NPC path passing through the previously blocking door.](../assets/avatar/pathfinding/Zombie-Full-Path.jpg) ## Pathfinding links Sometimes it's necessary to find a path across a space that cannot be normally traversed, such as across a chasm, and perform a custom action to reach the next waypoint. This can be achieved through the `Class.PathfindingLink` object. Using the example from above, you can make the agent use a boat. ![PathfindingLink showing how an agent can use a boat.](../assets/avatar/pathfinding/PathfindingLink-Path.jpg) To create a `Class.PathfindingLink` using this example: 1. **OPTIONAL** Toggle on **Pathfinding links** from the [Visualization Options](/docs/en-us/studio/ui-overview.md#visualization-options) widget in the upper‑right corner of the 3D viewport. This helps with visualization and debugging when implementing pathfinding links. 2. Create two `Class.Attachment|Attachments`, one on the boat's seat and one near the boat's landing point.![Attachments created for pathfinding link's start and end.](../assets/avatar/pathfinding/PathfindingLink-Attachments.jpg) 3. Create a `Class.PathfindingLink` object in the workspace, then assign its `Class.PathfindingLink.Attachment0|Attachment0` and `Class.PathfindingLink.Attachment1|Attachment1` properties to the starting and ending attachments respectively.![Attachment0/Attachment1 properties of a PathfindingLink.](../assets/studio/properties/PathfindingLink-Attachments.png)![PathfindingLink visualized in the 3D world.](../assets/avatar/pathfinding/PathfindingLink-In-World.jpg) 4. Assign a meaningful name like `UseBoat` to its `Class.PathfindingLink.Label|Label` property. This name is used as a flag in the pathfinding script to trigger a custom action when the agent reaches the starting link point.![Label property specified for PathfindingLink.](../assets/studio/properties/PathfindingLink-Label.png) 5. Include a `Costs` table within `Class.PathfindingService:CreatePath()|CreatePath()` containing both a `Water` key and a custom key matching the `Class.PathfindingLink.Label|Label` property name. Assign the custom key a value **lower** than that of `Water`.```lua local PathfindingService = game:GetService("PathfindingService") local Players = game:GetService("Players") local RunService = game:GetService("RunService") local DESTINATION = Vector3.new(20, 0.5, 20) local GROUND_WAIT = 0.01 local VELOCITY_MULTIPLIER = 0.0625 local path = PathfindingService:CreatePath({ AgentCanClimb = true, Costs = { UseBoat = 2, Water = 20 } }) ``` 6. In the `moveToNextWaypoint()` function (**LINES 93–114**), a custom check for the `Class.PathfindingLink.Label|Label` modifier name can be used to take a different action than `Class.Humanoid:Move()`; in this case, you might call a function to seat the agent in the boat, move the boat across the water, unseat the agent at the boat's landing point, and then continue the agent's path to its final destination. ## Streaming compatibility In-experience [instance streaming](/docs/en-us/workspace/streaming.md) is a powerful feature that dynamically loads and unloads 3D content as a player's character moves around the world. As they explore the 3D space, new subsets of the space stream to their device and some of the existing subsets might stream out. Consider the following best practices for using `Class.PathfindingService` in streaming-enabled experiences: - Streaming can block or unblock a given path as a character moves along it. For example, while a character runs through a forest, a tree might stream in somewhere ahead of them and obstruct the path. To make pathfinding work seamlessly with streaming, it's highly recommended that you use the [handling blocked paths](#blocked-paths) technique and re-compute the path when necessary. - A common approach in pathfinding is to use the coordinates of existing objects for [computation](#path-computation), such as setting a path destination to the position of an existing `TreasureChest` model in the world. This approach is fully compatible with server-side `Class.Script|Scripts` since the server has full view of the world at all times, but `Class.LocalScript|LocalScripts` and `Class.ModuleScript|ModuleScripts` that run on the client may fail if they attempt to compute a path to an object that's not streamed in. To address this issue, consider setting the destination to the position of a `Class.BasePart` within a [persistent](/docs/en-us/workspace/streaming.md#model-streaming-controls) model. Persistent models load soon after the player joins and they never stream out, so a client-side script can connect to the `Class.Workspace.PersistentLoaded|PersistentLoaded` event and safely access the model for creating waypoints after the event fires. ## Limitations and failure factors The pathfinding engine includes specific limitations to ensure efficient processing and optimal performance. Additionally, pathfinding [computations](#path-computation) can fail for various reasons as outlined below. > **Warning:****Path request too long** — The direct line‑of‑sight distance for pathfinding from the start to the finish point must not exceed 3,000 studs. > **Warning:****Node budget exhausted** — A pathfinding computation may exceed 20,000 nodes well before reaching the distance cap of 3,000 studs, especially when pathfinding in a vast open world or through complex mazes. > **Warning:****Incompatible agent parameters** — A pathfinding computation will fail if the [creation parameters](#path-creation) cannot resolve. For example, if the destination can **only** be reached by the agent jumping but `AgentCanJump` is `false`, or `AgentHeight` is greater than the height of any traversable path. > **Warning:****Vertical waypoint limits** — Pathfinding calculations only consider paths within a set vertical boundary. Potential waypoints with a bottom global **Y** coordinate less than `-65,536` studs or greater than `65,536` studs are ignored. --- title: "R6 to R15 Adapter" url: /docs/en-us/characters/r6-to-r15-adapter last_updated: 2026-06-29T19:33:58Z description: "The R6 to R15 adapter enables R15 avatar characters to join your R6 experience." --- # R6 to R15 Adapter > **Warning:** This feature is only applicable to developers who manage an active experience with **R6** avatar characters enabled. The **R6 to R15 Adapter** allows R15 avatars to join your R6 experience. All avatars in the experience will still use the R6-like scale and movement systems. The adapter allows your experience to take advantage of modern R15 components, such as layered clothing and animatable heads, with minimal performance or gameplay impact to your experience. It's important to understand how the adapter uses [adapter parts](#adapter-parts) and review the feature's [known limitations](#known-limitations) before [enabling](#enable-the-r6-to-r15-adapter) and testing the adapter for your experience. ## Adapter parts The R6 to R15 Adapter implements a Luau script injection when an avatar spawns that creates adapter parts. These are invisible `Class.MeshPart|MeshParts` that have the exact same name as R6 body parts and are welded to their corresponding R15 body parts. The scripts allow adapter parts to accept R6-based script interactions and forward them to the appropriate R15 parts. The adapter parts perform the following: - Emulates R6 physics using extra invisible collideable parts with the positions and dimensions of R6 hitboxes. - Sets the visible R15 body parts as non-collideable. - Scales and positions the visible R15 parts to match the R6 size and joint positions. - Acts as a shim between R6 and R15 body parts. Property changes applied to the invisible R6 parts are passed along to their corresponding visible R15 parts. - For example, a color change in R6 `LeftArm` is passed to the R15 `LeftUpperArm`, `LeftLowerArm` and `LeftHand` parts. ## Enable the R6 to R15 Adapter You can enable the R6 to R15 Adapter by setting the `Class.Workspace.AvatarUnificationMode|AvatarUnificationMode` property in `Class.Workspace`. You can only access this property if **Avatar Type** is set to `R6` in Studio's **File** ⟩ **Experience Settings** window. At this time, the **Default** setting disables unification mode. To enable the R6 to R15 Adapter: 1. In the **Explorer**, navigate to **Workspace**. 2. In the **Properties** window, set **AvatarUnificationMode** to **Enabled**. ## Known limitations In a majority of cases, the R6 to R15 Adapter works out-of-the-box with the systems of a R6 experience. In rare cases, there may be conflicts with custom systems that handle security, or character-related behavior. See the following for a list of potential limitations or conflicts that you may encounter when using the R6 to R15 Adapter. ### Security Some R6 experiences with active cheat detection can interpret the Luau script injection as an attempt to circumvent the security. Since the default behavior of R6 avatars is to spawn with all their parts already in place, many experiences tend to flag changes in body parts as potential exploits. In experiences with the adapter enabled, the R15 avatars spawn with their default bodies before changing body parts based on their saved avatar body parts and accessories. ### Custom avatar editors Experiences with custom avatar editors that allow players to swap body parts might break the connection with the adapter parts. ### Pre-existing R15 support Experiences that check the avatar rig type and include solutions specific for each R15 and R6 case might not work properly with the adapter. `AvatarUnificationMode` uses code paths corresponding to R15 which may require testing in your experience. ### Body part rescaling Experiences that resize the R6 body parts won't see the scale change propagated to the proxied R15 parts. This is also the case if joint attachments are moved. ### GetChildren API calls `Class.Instance:GetChildren()|GetChildren()` calls return both the R6 proxy parts and their corresponding R15 parts. You may need to account for this extra information. ### FindFirstChild API calls Don't immediately use `Class.Instance:FindFirstChild()|FindFirstChild()`, or "dot indexing," in your scripts to find character parts. Instead, use `Class.Instance:WaitForChild()|WaitForChild()` before you call `Class.Instance:FindFirstChild()|FindFirstChild()`. Replication in `Class.Workspace.AvatarUnificationMode|AvatarUnificationMode` is different, and the experience may fail to find a child that doesn't exist yet. This has always been a best practice for Roblox scripts, even if some cases work without following this practice. ### Head.ClassName conditionals `Class.Workspace.AvatarUnificationMode|AvatarUnificationMode` sets the Head to a `Class.MeshPart`. Any calls that read or write to the `Class.SpecialMesh.MeshId` property will fail. ### Head collisions R15 characters joining an R6 experience don't support collisions with their Head part. If your experience detects or depends on collisions with a character's Head, you must also update your scripts to check for `CollisionHead` as well. --- title: "Customize bubble chat" url: /docs/en-us/chat/bubble-chat last_updated: 2026-06-29T19:33:58Z description: "The text chat system allows users to communicate and socialize with each other." --- # Customize bubble chat With [TextChatService](/docs/en-us/chat/in-experience-text-chat.md), you can use bubble chat to display customizable speech chat bubbles above user avatars and NPCs. Bubble chat can make your experience more visually immersive and help users easily identify messages and their speakers in a contextually relevant manner. This feature is especially useful for experiences where users need to focus on the content in the meantime communicating with others in a less obtrusive way. ## Enable bubble chat To enable bubble chat in your experience: 1. In the **Explorer** window, select `Class.BubbleChatConfiguration` under `Class.TextChatService`. 2. In the **Properties** window, check the `Class.BubbleChatConfiguration.Enabled` checkbox. ## Bubble customization After enabling bubble chat, you can customize the appearance and behavior of your chat bubbles to match your experience theme. Use the **Properties** window of `Class.BubbleChatConfiguration` for [basic](#basic-customization) changes like text color and spacing, or implement [advanced](#advanced-customization) customization for bubble background images and other visual adjustments. Alternatively, add a `Class.LocalScript` in `Class.StarterPlayerScripts` with all your customization settings. This allows the engine to apply your customizations during runtime, overriding the settings in Studio. It's useful for adding special effects to chat bubbles when users trigger certain events or conditions. ### Basic customization The following table shows common bubble chat customization properties. For a full list of customization properties, see `Class.BubbleChatConfiguration`. #### Appearance | Property | Description | Default | | --- | --- | --- | | `Class.BubbleChatConfiguration.BackgroundColor3\|BackgroundColor3` | Background color of bubbles in `Datatype.Color3`. | `[250, 250, 250]` | | `Class.BubbleChatConfiguration.FontFace\|FontFace` | `Datatype.Font` of the bubble text. | `Enum.Font.BuilderSansMedium\|BuilderSansMedium` | | `Class.BubbleChatConfiguration.TextColor3\|TextColor3` | Color of bubble text in `Datatype.Color3`. | `[57, 59, 61]` | | `Class.BubbleChatConfiguration.TextSize\|TextSize` | Size of bubble text. | `16` | #### Behavior | Property | Description | Default | | --- | --- | --- | | `Class.BubbleChatConfiguration.Enabled\|Enabled` | Indicating whether bubble chat is enabled in the experience. | `true` (checked) | | `Class.BubbleChatConfiguration.AdorneeName\|AdorneeName` | String name of the body part or `Class.Attachment` that bubbles attach to; if multiple instances of the same name exist, the system attaches to the first instance found. | `HumanoidRootPart` | | `Class.BubbleChatConfiguration.BubbleDuration\|BubbleDuration` | Time before a bubble fades out, in seconds. | `30` | | `Class.BubbleChatConfiguration.BubblesSpacing\|BubblesSpacing` | Vertical space between stacked bubbles, in pixels. | `6` | | `Class.BubbleChatConfiguration.LocalPlayerStudsOffset\|LocalPlayerStudsOffset` | If adorned to the local player, the offset of bubbles in studs from their adornee, relative to the camera orientation (`Datatype.Vector3`). | `(0, 0, 0)` | | `Class.BubbleChatConfiguration.MaxDistance\|MaxDistance` | Maximum distance from the camera that bubbles are shown. | `100` | | `Class.BubbleChatConfiguration.MinimizeDistance\|MinimizeDistance` | Distance from the camera when bubbles turn into a single bubble with an ellipsis (**⋯**) to indicate chatter. | `40` | | `Class.BubbleChatConfiguration.VerticalStudsOffset\|VerticalStudsOffset` | Extra space between bubbles and their adornee, in studs. | `0` | | `Class.BubbleChatConfiguration.MaxBubbles\|MaxBubbles` | Maximum number of bubbles displayed before older bubbles disappear. | `3` | ### Advanced customization For advanced customization of your bubble, add UI objects representing certain aspects of the bubble appearance as children under `Class.BubbleChatConfiguration`, including: - `Class.ImageLabel` for background image settings. - `Class.UIGradient` for background gradient settings. - `Class.UICorner` for the corner shape of bubbles. - `Class.UIPadding` for the padding space between the text and bubble edges, relative to the parent's normal size. After adding these objects, you can modify properties of these objects applicable to chat bubbles for advanced bubble customization. The following example `Class.LocalScript` adds a background image and sharp corners to bubbles: ```lua local TextChatService = game:GetService("TextChatService") local bubbleChatConfiguration = TextChatService.BubbleChatConfiguration bubbleChatConfiguration.TailVisible = false bubbleChatConfiguration.TextColor3 = Color3.fromRGB(220, 50, 50) bubbleChatConfiguration.FontFace = Font.fromEnum(Enum.Font.LuckiestGuy) local bubbleUICorner = bubbleChatConfiguration:FindFirstChildOfClass("UICorner") if not bubbleUICorner then bubbleUICorner = Instance.new("UICorner") bubbleUICorner.Parent = bubbleChatConfiguration end bubbleUICorner.CornerRadius = UDim.new(0, 0) local bubbleUIPadding = bubbleChatConfiguration:FindFirstChildOfClass("UIPadding") if not bubbleUIPadding then bubbleUIPadding = Instance.new("UIPadding") bubbleUIPadding.Parent = bubbleChatConfiguration end bubbleUIPadding.PaddingTop = UDim.new(0, 20) bubbleUIPadding.PaddingRight = UDim.new(0, 10) bubbleUIPadding.PaddingBottom = UDim.new(0, 15) bubbleUIPadding.PaddingLeft = UDim.new(0, 10) local bubbleImageLabel = bubbleChatConfiguration:FindFirstChildOfClass("ImageLabel") if not bubbleImageLabel then bubbleImageLabel = Instance.new("ImageLabel") bubbleImageLabel.Parent = bubbleChatConfiguration end bubbleImageLabel.Image = "rbxassetid://109157529833093" bubbleImageLabel.ScaleType = Enum.ScaleType.Slice bubbleImageLabel.SliceCenter = Rect.new(40, 40, 320, 120) bubbleImageLabel.SliceScale = 0.5 ``` The following tables outline the available `Class.GuiObject` and [appearance modifier](/docs/en-us/ui/appearance-modifiers.md) children along with their valid customization properties: #### ImageLabel | Property | Description | Default | | --- | --- | --- | | `Class.ImageLabel.Image\|Image` | Asset ID of the bubble background image. | | | `Class.ImageLabel.ImageColor3\|ImageColor3` | Color tint of the bubble background image in `Datatype.Color3`. | `[255, 255, 255]` | | `Class.ImageLabel.ImageRectOffset\|ImageRectOffset` | Offset of the image area to be displayed from the top-left in pixels. | `(0, 0)` | | `Class.ImageLabel.ImageRectSize\|ImageRectSize` | Size of the image area to be displayed in pixels. To display the entire image, set either dimension to `0`. | `(0, 0)` | | `Class.ImageLabel.ScaleType\|ScaleType` | The scale type for rendering the image when its size is different from the absolute size of the bubble. | `Enum.ScaleType\|Stretch` | | `Class.ImageLabel.SliceCenter\|SliceCenter` | Slice boundaries of the image if the image is a 9-sliced image. Only applicable when you set `Class.ImageLabel.ScaleType\|ScaleType` as `Enum.ScaleType\|Slice`. | `(0, 0, 0, 0)` | | `Class.ImageLabel.SliceScale\|SliceScale` | Scale ratio of slice edges if the image is a 9-sliced image. Only applicable when you set `Class.ImageLabel.ScaleType\|ScaleType` as `Enum.ScaleType\|Slice`. | `1` | | `Class.ImageLabel.TileSize\|TileSize` | Tiling size of the image. Only applicable when you set `Class.ImageLabel.ScaleType\|ScaleType` as `Enum.ScaleType\|Tile`. | `(1, 0, 1, 0)` | #### UIGradient | Property | Description | Default | | --- | --- | --- | | `Class.UIGradient.Enabled\|Enabled` | Indicating whether the bubble background gradient is enabled. | `false` (unchecked) | | `Class.UIGradient.Color\|Color` | Color of the background gradient. | `[250, 250, 250]` | | `Class.UIGradient.Offset\|Offset` | Scalar translation of the gradient from the center of the bubble. | `(0, 0)` | | `Class.UIGradient.Rotation\|Rotation` | Clockwise rotation, in degrees, of the gradient starts from left to right. | `0` | | `Class.UIGradient.Transparency\|Transparency` | Transparency of the background gradient. | `(1, 0)` | #### UICorner | Property | Description | Default | | --- | --- | --- | | `Class.UICorner.CornerRadius\|CornerRadius` | Radius of the bubble corner shape in pixels. | `(0, 12)` | #### UIPadding | Property | Description | Default | | --- | --- | --- | | `Class.UIPadding.PaddingBottom\|PaddingBottom` | Padding on the bottom. | `Datatype.UDim.new(0,8)` | | `Class.UIPadding.PaddingLeft\|PaddingLeft` | Padding on the left. | `Datatype.UDim.new(0,8)` | | `Class.UIPadding.PaddingRight\|PaddingRight` | Padding on the right. | `Datatype.UDim.new(0,8)` | | `Class.UIPadding.PaddingTop\|PaddingTop` | Padding on the top. | `Datatype.UDim.new(0,8)` | ## Per-bubble customization You can individually style and modify chat bubble behaviors based on specific conditions in order to override your general settings. For example, you can use chat bubbles to differentiate NPCs and users, highlight critical health status, and apply special effects to messages with pre-defined keywords. To set per-bubble customization, add a client-side `Class.LocalScript` using `Class.BubbleChatMessageProperties`, which overrides matching properties of `Class.BubbleChatConfiguration`, and the `Class.TextChatService.OnBubbleAdded` callback to specify how to customize each bubble. The callback supplies you with the `Class.TextChatMessage` property as well as the adornee, so you can apply the customization based on attributes associated with users, the chat text content, user character properties, and any special conditions you want to define. The following basic customization properties are available for per-bubble customization: | Property | Description | Default | | --- | --- | --- | | `Class.BubbleChatConfiguration.BackgroundColor3\|BackgroundColor3` | Background color of bubbles in `Datatype.Color3`. | `(250, 250, 250)` | | `Class.BubbleChatConfiguration.BackgroundTransparency\|BackgroundTransparency` | Background transparency of bubbles. | `0.1` | | `Class.BubbleChatConfiguration.FontFace\|FontFace` | `Datatype.Font` of the bubble text. | `Enum.Font.BuilderSansMedium\|BuilderSansMedium` | | `Class.BubbleChatConfiguration.TextColor3\|TextColor3` | Color of bubble text in `Datatype.Color3`. | `[57, 59, 61]` | | `Class.BubbleChatConfiguration.TextSize\|TextSize` | Size of bubble text. | `16` | The following example adds special appearance to VIP users' chat bubbles by checking if a chat message sender has the `IsVIP` attribute: ```lua local TextChatService = game:GetService("TextChatService") local Players = game:GetService("Players") -- Event handler for when a new chat bubble is added to the experience TextChatService.OnBubbleAdded = function(message: TextChatMessage, adornee: Instance) -- Check if the chat message has a TextSource (sender) associated with it if message.TextSource then -- Create a new BubbleChatMessageProperties instance to customize the chat bubble local bubbleProperties = Instance.new("BubbleChatMessageProperties") -- Get the user who sent the chat message based on their UserId local player = Players:GetPlayerByUserId(message.TextSource.UserId) if player:GetAttribute("IsVIP") then -- If the player is a VIP, customize the chat bubble properties bubbleProperties.TextColor3 = Color3.fromHex("#F5CD30") bubbleProperties.BackgroundColor3 = Color3.fromRGB(25, 27, 29) bubbleProperties.FontFace = Font.fromEnum(Enum.Font.PermanentMarker) end return bubbleProperties end end ``` All advanced customization options are available for per-bubble customization. Similar to advanced customization for general bubbles, add instances that you want to customize as children of `Class.BubbleChatMessageProperties`. The following example adds a special gradient effect along with other properties to chat bubbles of users with low health status by checking the `Class.Humanoid.Health` property of chat message senders' characters: ```lua local TextChatService = game:GetService("TextChatService") local Players = game:GetService("Players") -- Event handler for when a new chat bubble is added to the experience TextChatService.OnBubbleAdded = function(message: TextChatMessage, adornee: Instance) -- Check if the chat message has a TextSource (sender) associated with it if message.TextSource then -- Get the user who sent the chat message by using their UserId local player = Players:GetPlayerByUserId(message.TextSource.UserId) -- Find the humanoid in the user's character local humanoid = player.Character:FindFirstChildWhichIsA("Humanoid") if humanoid and humanoid.Health < 25 then -- Create a new BubbleChatMessageProperties instance to customize the chat bubble local bubbleProperties :BubbleChatMessageProperties = Instance.new("BubbleChatMessageProperties") -- Customize the chat bubble properties for low health condition bubbleProperties.BackgroundColor3 = Color3.fromRGB(245, 245, 245) bubbleProperties.TextColor3 = Color3.fromRGB(234, 51, 96) bubbleProperties.TextSize = 20 bubbleProperties.FontFace = Font.fromEnum(Enum.Font.DenkOne) -- Add a UIGradient as a child to customize the gradient local uiGradient : UIGradient = Instance.new("UIGradient") uiGradient.Color = ColorSequence.new(Color3.fromRGB(110, 4, 0), Color3.fromRGB(0, 0, 0)) uiGradient.Rotation = 90 uiGradient.Parent = bubbleProperties return bubbleProperties end end end ``` ## Manually display bubbles You might want to display a chat bubble when players haven't sent a message, such as with NPCs. Use the `Class.TextChatService:DisplayBubble` method to manually display a chat bubble. Customization of these bubbles is the same as the customization of the bubbles that are automatically displayed when Players send messages through TextChannels using the [`Class.TextChatService.OnBubbleAdded` callback](#per-bubble-customization). > **Success:** To generate speech or other text using an LLM, see the `Class.TextGenerator` class. ### NPC bubbles Display chat bubbles for non-player characters (NPCs) by calling `Class.TextChatService:DisplayBubble(character, message)`, with the NPC character and the message as parameters. These bubbles are customizable using the `Class.TextChatService.OnBubbleAdded` callback just like any other chat bubble. `Class.TextChatService:DisplayBubble()` only works on client-side scripts, so be sure to use a `Class.Script` with `Class.BaseScript.RunContext|RunContext` set to `Enum.RunContext.Client`, or a `Class.LocalScript` in an [appropriate container](/docs/en-us/projects/data-model.md#client), such as `Class.StarterPlayerScripts`. If you attach a `Class.ProximityPrompt` to an NPC, a script for displaying a chat bubble might look like this: ```lua local TextChatService = game:GetService("TextChatService") local Workspace = game:GetService("Workspace") local prompt = Workspace.SomeNPC.ProximityPrompt local head = prompt.Parent:WaitForChild("Head") prompt.Triggered:Connect(function() TextChatService:DisplayBubble(head, "Hello world!") end) ``` --- title: "Customize the chat window" url: /docs/en-us/chat/chat-window last_updated: 2026-06-29T19:33:58Z description: "Customize the chat window and message UI of your in-experience text chat." --- # Customize the chat window The [in-experience text chat](/docs/en-us/chat/in-experience-text-chat.md) system, powered by `Class.TextChatService`, allows players to easily communicate and socialize with each other in live experiences. In addition to supporting the default text chat, you can [customize](#chat-window-configuration) the front‑end user interface. ## Chat window configuration The overall chat window consists of: - Chat window - Input bar - Channel tabs (optional) ![Core components of the text chat window.](../assets/players/in-experience-text-chat/Chat-Window-Components.jpg) The channel tabs are disabled by default and each component can be toggled on and off in Studio or through scripting: #### Studio In the [Explorer](/docs/en-us/studio/explorer.md) window, expand the `Class.TextChatService` branch and select `Class.ChatWindowConfiguration`, `Class.ChatInputBarConfiguration`, or `Class.ChannelTabsConfiguration`. Then enable or disable the component in the [Properties](/docs/en-us/studio/properties.md) window. #### Scripting From a client script within `Class.StarterPlayerScripts`, enable each component as desired: ```lua local TextChatService = game:GetService("TextChatService") local ChatWindowConfiguration = TextChatService:FindFirstChildOfClass("ChatWindowConfiguration") local ChatInputBarConfiguration = TextChatService:FindFirstChildOfClass("ChatInputBarConfiguration") local ChannelTabsConfiguration = TextChatService:FindFirstChildOfClass("ChannelTabsConfiguration") -- Enable chat window if ChatWindowConfiguration then ChatWindowConfiguration.Enabled = true end -- Enable input bar if ChatInputBarConfiguration then ChatInputBarConfiguration.Enabled = true end -- Enable channel tabs if ChannelTabsConfiguration then ChannelTabsConfiguration.Enabled = true end ``` When `Class.ChannelTabsConfiguration` is enabled, each default `Class.TextChannel` appears in a tab as outlined in the following table. In addition, each custom `Class.TextChannel` creates a tab corresponding to the channel's `Class.Instance.Name|Name` property. | Default channel | Tab name | | --- | --- | | `RBXGeneral` | **General** | | `RBXSystem` | **General** (combined into a single tab with `RBXGeneral`) | | `RBXTeam` | **Team** | | `RBXWhisper` | User name of the other player | ### Window appearance Appearance of the overall chat window is customizable through `Class.ChatWindowConfiguration`. ![ChatWindowConfiguration instance in Explorer hierarchy.](../assets/studio/explorer/TextChatService-ChatWindowConfiguration.png) | Property | Description | Default | | --- | --- | --- | | `Class.ChatWindowConfiguration.BackgroundColor3\|BackgroundColor3` | `Datatype.Color3` background color of the chat window. | `[25, 27, 29]` | | `Class.ChatWindowConfiguration.BackgroundTransparency\|BackgroundTransparency` | Transparency of the chat window's background. | `0.3` | | `Class.ChatWindowConfiguration.Enabled\|Enabled` | Whether to show the default chat window. Set to `false` to hide. | `true` | | `Class.ChatWindowConfiguration.FontFace\|FontFace` | `Datatype.Font` of chat window text. | `Enum.Font.BuilderSansMedium\|BuilderSansMedium` | | `Class.ChatWindowConfiguration.TextColor3\|TextColor3` | `Datatype.Color3` of chat window text. | `[255, 255, 255]` | | `Class.ChatWindowConfiguration.TextSize\|TextSize` | Size of chat window text. | `14` | | `Class.ChatWindowConfiguration.TextStrokeColor3\|TextStrokeColor3` | `Datatype.Color3` of the stroke for chat window text. | `[0, 0, 0]` | | `Class.ChatWindowConfiguration.TextStrokeTransparency\|TextStrokeTransparency` | Transparency of the stroke for chat window text. | `0.5` | | `Class.ChatWindowConfiguration.HorizontalAlignment\|HorizontalAlignment` | Horizontal alignment of the chat window. | `Enum.HorizontalAlignment.Left\|Left` | | `Class.ChatWindowConfiguration.VerticalAlignment\|VerticalAlignment` | Vertical alignment of the chat window. | `Enum.VerticalAlignment.Top\|Top` | | `Class.ChatWindowConfiguration.HeightScale\|HeightScale` | Height scale of the chat window relative to the screen size. | `1` | | `Class.ChatWindowConfiguration.WidthScale\|WidthScale` | Width scale of the chat window relative to the screen size. | `1` | ### Input bar appearance Appearance of the chat input bar is customizable through `Class.ChatInputBarConfiguration`. ![ChatInputBarConfiguration instance in Explorer hierarchy.](../assets/studio/explorer/TextChatService-ChatInputBarConfiguration.png) | Property | Description | Default | | --- | --- | --- | | `Class.ChatInputBarConfiguration.BackgroundColor3\|BackgroundColor3` | `Datatype.Color3` background color of the chat input bar. | `[25, 27, 29]` | | `Class.ChatInputBarConfiguration.BackgroundTransparency\|BackgroundTransparency` | Transparency of the chat input bar's background. | `0.2` | | `Class.ChatInputBarConfiguration.FontFace\|FontFace` | `Datatype.Font` of chat input text. | `Enum.Font.BuilderSansMedium\|BuilderSansMedium` | | `Class.ChatInputBarConfiguration.PlaceholderColor3\|PlaceholderColor3` | `Datatype.Color3` of placeholder chat input text. | `[178, 178, 178]` | | `Class.ChatInputBarConfiguration.TextColor3\|TextColor3` | `Datatype.Color3` of player-entered chat input text. | `[255, 255, 255]` | | `Class.ChatInputBarConfiguration.TextSize\|TextSize` | Size of chat input text. | `14` | | `Class.ChatInputBarConfiguration.TextStrokeColor3\|TextStrokeColor3` | `Datatype.Color3` stroke color of chat input text. | `[0, 0, 0]` | | `Class.ChatInputBarConfiguration.TextStrokeTransparency\|TextStrokeTransparency` | Transparency of the stroke for chat input text. | `0.5` | | `Class.ChatInputBarConfiguration.AutocompleteEnabled\|AutocompleteEnabled` | Whether the text chat system shows autocomplete options for emojis and [commands](/docs/en-us/chat/in-experience-text-chat.md). Emojis are autocompleted by typing `:` followed by non-whitespace characters, while commands are autocompleted by typing `/`. | `true` | | `Class.ChatInputBarConfiguration.KeyboardKeyCode\|KeyboardKeyCode` | Additional key players can press to trigger focusing on the default chat input bar. | `Enum.KeyCode.Slash\|Slash` | ### Channel tabs appearance Appearance of the **channel tabs** is customizable through `Class.ChannelTabsConfiguration`. ![ChannelTabsConfiguration instance in Explorer hierarchy.](../assets/studio/explorer/TextChatService-ChannelTabsConfiguration.png) | Property | Description | Default | | --- | --- | --- | | `Class.ChannelTabsConfiguration.BackgroundColor3\|BackgroundColor3` | `Datatype.Color3` background color of the channel tabs. | `[25, 27, 29]` | | `Class.ChannelTabsConfiguration.BackgroundTransparency\|BackgroundTransparency` | Transparency of the channel tabs' background. | `0` | | `Class.ChannelTabsConfiguration.HoverBackgroundColor3\|HoverBackgroundColor3` | `Datatype.Color3` background color of tabs when hovering over them. | `[125, 125, 125]` | | `Class.ChannelTabsConfiguration.FontFace\|FontFace` | `Datatype.Font` for the text in channel tabs. | `Enum.Font.BuilderSansBold\|BuilderSansBold` | | `Class.ChannelTabsConfiguration.TextColor3\|TextColor3` | `Datatype.Color3` of text in an unselected tab. | `[175, 175, 175]` | | `Class.ChannelTabsConfiguration.SelectedTabTextColor3\|SelectedTabTextColor3` | `Datatype.Color3` of text in a selected tab. | `[255, 255, 255]` | | `Class.ChannelTabsConfiguration.TextSize\|TextSize` | Size of the text in channel tabs. | `18` | | `Class.ChannelTabsConfiguration.TextStrokeColor3\|TextStrokeColor3` | `Datatype.Color3` stroke color of the text in channel tabs. | `[0, 0, 0]` | | `Class.ChannelTabsConfiguration.TextStrokeTransparency\|TextStrokeTransparency` | Transparency of the stroke for text in channel tabs. | `1` | ## Customize messages You can customize the appearance of chat message bodies and prefixes using `Class.ChatWindowMessageProperties` and `Class.TextChatService.OnChatWindowAdded` callbacks without overriding the existing UI. The customization options let you modify the appearance of chat messages to match your experience's theme, and you can also sort or highlight messages from different user groups by coloring prefixes or adding [chat tags](/docs/en-us/chat/examples/group-chat-tags.md). > **Info:**`Class.ChatWindowMessageProperties` and `Class.TextChatService.OnChatWindowAdded|OnChatWindowAdded` only affect the appearance of messages in the chat window. To customize chat bubbles, see [Bubble Chat](/docs/en-us/chat/bubble-chat.md). ### Color user names When a user sends a chat message, their `Class.Player.DisplayName|DisplayName` displays as the prefix portion of the message. By default, each user's name is colored according to their `Class.Player.TeamColor` but you can change the colors of chat names using `Class.ChatWindowMessageProperties|ChatWindowMessageProperties` and `Class.TextChatService.OnChatWindowAdded|OnChatWindowAdded`. The following `Class.LocalScript` in `Class.StarterPlayerScripts` assigns a predetermined color to each user, picking randomly from a table of RGB colors. ![Colored user name in the chat window.](../assets/players/in-experience-text-chat/Chat-User-Name-Colored.png) ```lua local TextChatService = game:GetService("TextChatService") local chatWindowConfiguration = TextChatService.ChatWindowConfiguration local nameColors = { Color3.fromRGB(255, 0, 0), Color3.fromRGB(0, 255, 0), Color3.fromRGB(0, 0, 255), Color3.fromRGB(255, 255, 0), } TextChatService.OnChatWindowAdded = function(message: TextChatMessage) local properties = chatWindowConfiguration:DeriveNewMessageProperties() local textSource = message.TextSource if textSource then local index: number = (textSource.UserId % #nameColors) + 1 local randomColor: Color3 = nameColors[index] properties.PrefixTextProperties = chatWindowConfiguration:DeriveNewMessageProperties() properties.PrefixTextProperties.TextColor3 = randomColor end return properties end ``` You can also apply color and transparency gradients to color message prefixes using `Class.UIGradient`. ![Gradient user name in the chat window.](../assets/players/in-experience-text-chat/Chat-User-Name-Gradient.png) ```lua local TextChatService = game:GetService("TextChatService") local chatWindowConfiguration = TextChatService.ChatWindowConfiguration local gradient = Instance.new("UIGradient") gradient.Color = ColorSequence.new{ ColorSequenceKeypoint.new(0, Color3.fromRGB(255, 0, 0)), ColorSequenceKeypoint.new(0.5, Color3.fromRGB(255, 255, 0)), ColorSequenceKeypoint.new(1, Color3.fromRGB(255, 0, 255)) } TextChatService.OnChatWindowAdded = function(message: TextChatMessage) local properties = chatWindowConfiguration:DeriveNewMessageProperties() local textSource = message.TextSource if textSource then properties.PrefixTextProperties = chatWindowConfiguration:DeriveNewMessageProperties() gradient:Clone().Parent = properties.PrefixTextProperties end return properties end ``` ### Rich text customization Rich text [font color tags](/docs/en-us/ui/rich-text.md#supported-tags) can be used to format chat messages, helpful if you want to apply formatting to very specific parts of the message. Note that rich text does not support gradients, but the following code sample shows how you can move the user name (stored in `Class.TextChatMessage.PrefixText`) into the message body and then apply rich text tagging to only the name portion. ![Rich text customization of user name in the chat window.](../assets/players/in-experience-text-chat/Chat-Rich-Text.png) ```lua local TextChatService = game:GetService("TextChatService") local Players = game:GetService("Players") local chatWindowConfiguration = TextChatService.ChatWindowConfiguration local gradient = Instance.new("UIGradient") gradient.Color = ColorSequence.new{ ColorSequenceKeypoint.new(0, Color3.fromRGB(255, 0, 0)), ColorSequenceKeypoint.new(0.5, Color3.fromRGB(255, 255, 0)), ColorSequenceKeypoint.new(1, Color3.fromRGB(255, 0, 255)) } TextChatService.OnChatWindowAdded = function(message: TextChatMessage) local properties = chatWindowConfiguration:DeriveNewMessageProperties() if message.TextSource then properties.PrefixText = "[VIP]" properties.Text = string.format("%s", message.PrefixText) .. " " .. message.Text properties.PrefixTextProperties = chatWindowConfiguration:DeriveNewMessageProperties() gradient:Clone().Parent = properties.PrefixTextProperties end return properties end ``` ## Messages from non‑player sources When `Class.TextChatService.CreateDefaultTextChannels` is `true`, one of the default text channels is the `RBXSystem` channel. The default chat scripts automatically display system messages in this channel. You can customize the appearance of these messages using the `Class.TextChannel.OnIncomingMessage` callback. You might want to customize or alter the system messages that are automatically emitted by the chat system. Since the default system messages are localized for users, you should reference them by `Class.TextChatMessage.Metadata` in your [text chat callbacks](/docs/en-us/chat/in-experience-text-chat.md#text-chat-hooks-and-callbacks) if you wish to customize their appearance. For example, you could use `Class.TextChatMessage.Metadata|Metadata` to identify system messages, error messages, or messages from specific systems in your experience. ### System To deliver a system message to the local player, such as "speech" from a public address system, call `Class.TextChannel:DisplaySystemMessage()|DisplaySystemMessage()` from the default `RBXGeneral` channel with a prefix before the player's display name. ```lua local Players = game:GetService("Players") local TextChatService = game:GetService("TextChatService") local player = Players.LocalPlayer local generalChannel: TextChannel = TextChatService:WaitForChild("TextChannels").RBXGeneral local PREFIX = "[Guide] Welcome " -- Send "system message" to player with their display name appended generalChannel:DisplaySystemMessage(PREFIX .. player.DisplayName) ``` ![Image showing a basic system message in the chat window.](../assets/players/in-experience-text-chat/Chat-System.jpg) ### NPC/object You can also stylize non-player dialogue and add [chat bubbles](/docs/en-us/chat/bubble-chat.md) to make it appear like messages are coming from an NPC or object within the 3D world. ```lua local TextChatService = game:GetService("TextChatService") local Workspace = game:GetService("Workspace") local generalChannel: TextChannel = TextChatService:WaitForChild("TextChannels").RBXGeneral TextChatService.OnIncomingMessage = function(textChatMessage: TextChatMessage) local properties = Instance.new("TextChatMessageProperties") -- Check for system messages that contain metadata if not textChatMessage.TextSource and textChatMessage.Metadata ~= "" then -- Add prefix to make message look like it was sent by a player properties.PrefixText = string.format("%s: ", "#50C999", textChatMessage.Metadata) -- Add bubble chat TextChatService:DisplayBubble(Workspace.Statue, textChatMessage.Text) end return properties end local message = "Welcome! I will be your guide." local speakerName = "Ancient Knight" generalChannel:DisplaySystemMessage(message, speakerName) ``` ![Image showing a knight statue NPC broadcasting a chat message to the chat window, along with a chat bubble above its head.](../assets/players/in-experience-text-chat/Chat-NPC.jpg) --- title: "Cross-server chat" url: /docs/en-us/chat/cross-server-chat last_updated: 2026-06-29T19:33:58Z description: "Cross-server chat allows players to interact with others across different servers." --- # Cross-server chat **Cross-server chat** is a native extension of `Class.TextChatService` that allows players to safely communicate with others across different servers in the same universe. This increases social engagement, especially in experiences with low server occupancy or during live events with a global community. Roblox automatically groups servers based on age, language, and occupancy to ensure meaningful and safe interactions. Like default Roblox chat, cross-server chat includes full support for abuse reporting, text filtering, chat rules, muting, and blocking. > **Info:** Each global chat channel connects about 200 users to ensure chat remains readable and performant. If users send messages too quickly, a throttle applies to the senders and receivers, and messages drop to protect the chat system. ## Enable cross-server chat When you enable cross-server chat, the [chat window](/docs/en-us/chat/chat-window.md) includes two tabs for either single or multi-server communication: - **Here** - Players can chat with others in the same server. - **Global** - Players can chat with others across different servers. ![Core components of the text chat window with cross-server chat enabled.](../assets/players/cross-server-chat/overview-example.png) Cross-server chat is **enabled by default** for all experiences, but you have full control to enable or disable it according to your experience's design requirements. To access the cross-server chat setting: 1. Navigate to the [Creator Dashboard](https://create.roblox.com/dashboard/creations). 2. Select an experience, then in the left-hand navigation, navigate to **Audience** > **Communication Settings**. 3. Under the **Cross-server chat** section, toggle **Allow cross-server chat** on or off to enable or disable cross-server chat. > **Warning:** Even if cross-server chat is enabled, the **Global** tab is disabled in [private servers](/docs/en-us/production/monetization/private-servers.md) to respect the privacy of users who only want to access your experience with specific friends. ## Global chat commands The **Global** tab supports a subset of standard chat commands for global chat: - `/mute ` - Mutes a specific user in global chat. - `/unmute ` - Unmutes a user. - `/help` - Lists commands available within global chat. - `/clear` - Clears the local chat window history. --- title: "Custom text chat commands" url: /docs/en-us/chat/examples/custom-text-chat-commands last_updated: 2026-06-29T19:33:58Z description: "Learn how to create custom chat commands." --- # Custom text chat commands `Class.TextChatService` has built-in chat commands for common purposes, such as muting other players and using avatar emotes. You can enable them by setting `Class.TextChatService.CreateDefaultCommands|CreateDefaultCommands` to `true` in Studio's **Properties** window. You can also add custom commands using `Class.TextChatCommand`. Users sending a defined command in the chat input bar trigger a callback defined by `Class.TextChatCommand.Triggered` to perform your customized actions. The following example shows how to create a chat command that allows players to increase or decrease their character's size when they input `/super` or `/mini`. 1. Add a `Class.TextChatCommand` instance inside `Class.TextChatService`. 2. Rename it to **SizeCommand**. 3. Set its `Class.TextChatCommand.PrimaryAlias|PrimaryAlias` property to `/super` and its `Class.TextChatCommand.SecondaryAlias|SecondaryAlias` to `/mini`. 4. Insert the following `Class.Script` inside `Class.ServerScriptService` to define a callback for the chat command that scales the character's size:```lua local TextChatService = game:GetService("TextChatService") local Players = game:GetService("Players") local sizeCommand: TextChatCommand = TextChatService:WaitForChild("SizeCommand") sizeCommand.Triggered:Connect(function(textSource, message) local scaleMult = 1 local messageWords = string.split(message, " ") if messageWords[1] == "/super" then scaleMult = 2 elseif messageWords[1] == "/mini" then scaleMult = 0.5 end local player = Players:GetPlayerByUserId(textSource.UserId) if player then local character = player.Character if character then local humanoid = character:FindFirstChildWhichIsA("Humanoid") if humanoid then for _, child in humanoid:GetChildren() do if child:IsA("NumberValue") then child.Value *= scaleMult end end end end end end) ``` --- title: "Assign chat tags" url: /docs/en-us/chat/examples/group-chat-tags last_updated: 2026-06-29T19:33:58Z description: "Example of how to assign chat tags to players based on their membership in a group." --- # Assign chat tags This example demonstrates how to assign **chat tags** to players based on their membership in a [group](/docs/en-us/projects/groups.md). Chat tags are a way to visually identify a player in the chat window and useful for indicating a player's role or status. ![VIP chat tag appended to user name in the chat window.](../../assets/players/in-experience-text-chat/Chat-Tag-VIP.png) Because [text chat callbacks](/docs/en-us/chat/in-experience-text-chat.md#text-chat-hooks-and-callbacks) expect a non-yielding callback, attempting to query the group membership status of a player in the `Class.TextChatService.OnIncomingMessage` callback is not recommended, as it may cause the chat system to hang or become unresponsive. Instead, set a player [attribute](/docs/en-us/studio/properties.md#instance-attributes) when they join the server. Setting an attribute lets you reuse the player's status in other parts of your experience such as allowing access to particular areas or providing bonus experience. 1. Create a `Class.Script` in `Class.ServerScriptService` and add the following code to it:```lua local Players = game:GetService("Players") -- Replace 123456 with the group ID you want to check for local groupID = 123456 Players.PlayerAdded:Connect(function(player) local success, isInGroup = pcall(function() return player:IsInGroupAsync(groupID) end) if success and isInGroup then player:SetAttribute("IsVIP", true) else player:SetAttribute("IsVIP", false) end end) ``` 2. Create a `Class.LocalScript` in `Class.StarterPlayer` ⟩ `Class.StarterCharacterScripts` and add the following code to display a **[VIP]** tag in the chat window:```lua local Players = game:GetService("Players") local TextChatService = game:GetService("TextChatService") TextChatService.OnIncomingMessage = function(message: TextChatMessage) local textSource = message.TextSource if textSource then local player = Players:GetPlayerByUserId(textSource.UserId) if player then if player:GetAttribute("IsVIP") == true then local overrideProperties = Instance.new("TextChatMessageProperties") overrideProperties.PrefixText = "[VIP] " .. message.PrefixText return overrideProperties end end end return nil end ``` --- title: "Proximity-based chat" url: /docs/en-us/chat/examples/proximity-chat last_updated: 2026-06-29T19:33:58Z description: "Learn how to implement exclusive chat for users who are close to each other in locations using the TextChatService." --- # Proximity-based chat This example shows how to implement an exclusive chat for users who are near each other in the 3D world. It extends the callback with a function using `Class.TextSource` to identify the locations of a user who might be a potential message receiver. If this function returns `false`, it means that the user locates further than the preset valid range from the message sender, so the system doesn't deliver the message to that user. Add the following to a `Class.Script` in `Class.ServerScriptService`: ```lua local TextChatService = game:GetService("TextChatService") local Players = game:GetService("Players") -- Get the chat channel for proximity-based chat -- You can replace this general channel with a dedicated channel local generalChannel: TextChannel = TextChatService:WaitForChild("TextChannels").RBXGeneral -- Function to get the position of a user's character local function getPositionFromUserId(userId: number) -- Get the player associated with the given user ID local targetPlayer = Players:GetPlayerByUserId(userId) -- If the player exists, get their character's position if targetPlayer then local targetCharacter = targetPlayer.Character if targetCharacter then return targetCharacter:GetPivot().Position end end -- Return a default position if the player or character cannot be found return Vector3.zero end -- Set the callback for the general channel to control message delivery generalChannel.ShouldDeliverCallback = function(textChatMessage: TextChatMessage, targetTextSource: TextSource) -- Get the positions of the message sender and target local sourcePos = getPositionFromUserId(textChatMessage.TextSource.UserId) local targetPos = getPositionFromUserId(targetTextSource.UserId) -- Deliver message if distance between sender and target is less than 50 units return (targetPos - sourcePos).Magnitude < 50 end ``` --- title: "Rate-limited text inputs" url: /docs/en-us/chat/examples/rate-limit-public-text-inputs last_updated: 2026-06-29T19:33:58Z description: "Learn how to implement a rate limit for any text field that might be used by players to conduct a conversation." --- # Rate-limited text inputs This example shows how to implement a rate limit of 1 minute on a text input field that allows players to post notes to a community bulletin board. Add the following to a `Class.Script` in `Class.ServerScriptService`: ```lua local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") -- Create RemoteEvent for communication local postNoteEvent = Instance.new("RemoteEvent") postNoteEvent.Name = "PostNoteEvent" postNoteEvent.Parent = ReplicatedStorage -- Rate limit of one note per minute per player local COOLDOWN_TIME = 60 -- Time in seconds local lastPostTime = {} local function canPlayerPost(player) local userId = player.UserId local currentTime = tick() if lastPostTime[userId] then local timeSinceLastPost = currentTime - lastPostTime[userId] if timeSinceLastPost < COOLDOWN_TIME then return false, COOLDOWN_TIME - timeSinceLastPost end end return true, 0 end -- Handle post requests with rate limit and text filtering postNoteEvent.OnServerEvent:Connect(function(player, noteData) local canPost, cooldown = canPlayerPost(player) if not canPost then postNoteEvent:FireClient(player, "cooldown", math.ceil(cooldown)) return end -- All text shared publicly must go through a text filter local filterResult = getFilterResult(noteData.text, player.UserId) if filterResult then local success, filteredText = pcall(function() return filterResult:GetNonChatStringForBroadcastAsync() end) if success then -- Create the note local note = { text = filteredText } -- Update last post time lastPostTime[player.UserId] = tick() -- Send success response postNoteEvent:FireClient(player, "success", note) -- Broadcast new note to all players postNoteEvent:FireAllClients("newNote", note) else warn("Error filtering text!") end end) -- Clean up when player leaves Players.PlayerRemoving:Connect(function(player) lastPostTime[player.UserId] = nil end) ``` --- title: "Custom text chat UI" url: /docs/en-us/chat/examples/simple-custom-frontend-ui last_updated: 2026-06-29T19:33:58Z description: "Example of how to create a simple frontend UI powered with TextChatService." --- # Custom text chat UI This example shows how to use the `Class.TextChatService` class to design your own frontend. It reuses the default text channels from `Class.TextChatService.CreateDefaultTextChannels|CreateDefaultTextChannels` and is very simple compared to the default UI. 1. Disable the default UI that comes with the `Class.TextChatService` by setting the `Class.ChatWindowConfiguration.Enabled` and `Class.ChatInputBarConfiguration.Enabled` properties to `false` in Studio's **Properties** window. 2. Create a replacement for the chat input bar. This is the text box that emits messages when the user presses `Enter`. 1. Create a `Class.ScreenGui` and parent it to `Class.StarterGui`. 2. Create a `Class.TextBox` and parent it to the new `Class.ScreenGui`, then [reposition](/docs/en-us/ui/position-and-size.md#position) and [resize](/docs/en-us/ui/position-and-size.md#size) it as desired. 3. Create a `Class.LocalScript` and parent it to the new `Class.TextBox`. 4. Add the following code to the `Class.LocalScript`:```lua local TextChatService = game:GetService("TextChatService") -- RBXGeneral is the default public channel local RBXGeneral = TextChatService:FindFirstChild("TextChannels"):WaitForChild("RBXGeneral") local textBox = script.Parent textBox.FocusLost:Connect(function(enterPressed) local text = textBox.Text if enterPressed and #text > 0 then local success, response = pcall(function() return RBXGeneral:SendAsync(textBox.Text) end) if not success then RBXGeneral:DisplaySystemMessage("Failed to send message") end -- Users expect the input box to be cleared after sending a message textBox.Text = "" end end) ``` 3. Create a replacement for the chat window. This is the `Class.ScrollingFrame` that displays messages as they are received from `Class.TextChatService.MessageReceived`. This step also creates a `Class.UIListLayout` to automatically layout the messages. 1. Create another new `Class.ScreenGui` and parent it to `Class.StarterGui`. 2. Create a `Class.ScrollingFrame` and parent it to the `ScreenGui`, then [reposition](/docs/en-us/ui/position-and-size.md#position) and [resize](/docs/en-us/ui/position-and-size.md#size) it as desired. 3. Create a `Class.UIListLayout` and parent it to the `Class.ScrollingFrame`. 4. Create a `Class.LocalScript` and parent it to the `Class.ScrollingFrame`. 5. Add the following code to the `Class.LocalScript`:```lua local TextChatService = game:GetService("TextChatService") -- Function to create a new text label for each message received local function addMessageGui(textChatMessage: TextChatMessage) local isOutgoingMessage = textChatMessage.Status == Enum.TextChatMessageStatus.Sending local parent = script.Parent local originalLabel = parent:FindFirstChild(textChatMessage.MessageId) if originalLabel then originalLabel.Text = textChatMessage.Text originalLabel.BackgroundTransparency = if isOutgoingMessage then 0.5 else 0 else local textLabel = Instance.new("TextLabel") textLabel.BorderSizePixel = 0 textLabel.Font = Enum.Font.BuilderSans textLabel.TextSize = 18 textLabel.TextXAlignment = Enum.TextXAlignment.Left textLabel.BackgroundTransparency = if isOutgoingMessage then 0.5 else 0 textLabel.BackgroundColor3 = Color3.fromRGB(0, 0, 0) textLabel.TextColor3 = Color3.fromRGB(255, 255, 255) textLabel.Name = textChatMessage.MessageId textLabel.AutomaticSize = Enum.AutomaticSize.XY textLabel.Text = textChatMessage.Text textLabel.Parent = parent end end -- Start listening for incoming messages TextChatService.MessageReceived:Connect(addMessageGui) ``` > **Warning:** When using the `Class.TextChatService.MessageReceived` event, it's important to check if the message already exists in the chat window. This event is called once when sending and once when the message is received from the server. 4. Test your experience by sending messages in the chat input bar. You should see the messages appear in the chat window. You can then expand on this example by adding features such as focusing the `Class.TextBox` when a key is pressed or adding [chat tags](/docs/en-us/chat/examples/group-chat-tags.md). --- title: "Chat system guidelines" url: /docs/en-us/chat/guidelines last_updated: 2026-06-29T19:33:58Z description: "Important guidelines for using the in-experience text chat feature." --- # Chat system guidelines In-experience text chat is any message created within your experience that originated from one user and is delivered to one or more other users, including: - Chat bubbles over an avatar's head - Direct messages between users - Chat window communication between users - Team-specific messages For sending and delivery, these types of communications must each go through a `Class.TextChannel` instance. This ensures messages respect privacy settings, are visible to moderators, and are properly text-filtered. Certain text is **not** considered chat: - Text on menus created by developers (for example, "Press any button to continue.") - Status updates from the experience (for example, "Two minutes remaining!") - Announcements from admin commands - A user renaming their pet dog - Moderation audit logs or messages - Any user-generated text unrelated to a conversation - Comments on posts in an experience > **Info:** If the comments support a reply feature, these replies would be considered a conversation and thus subject to this policy. For communication shared by users that can be seen by other users, even if it doesn't need to go through `Class.TextChatService`, you must ensure it goes through a [text filter](/docs/en-us/ui/text-filtering.md). ## Requirements - All experiences that offer in-experience text chat for users must integrate `Class.TextChatService`, per the [Misusing Roblox Systems Community Standard](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards). For information on migrating from the legacy chat system, see the [Roblox Developer Forum](https://devforum.roblox.com/t/migrate-to-textchatservice-removing-support-for-legacy-chat-and-custom-chat-systems/3237100) and [Migrate from legacy chat](/docs/en-us/chat/in-experience-text-chat.md#migrate-from-legacy-chat). - All experiences that offer editable text that can be used and seen between users must add rate limits of at least 1 minute, such as communication through **signs or bulletin boards**. Other intentionally built communications or chat systems must otherwise adhere and call to `Class.TextChatService`. See [rate‑limited text inputs](/docs/en-us/chat/examples/rate-limit-public-text-inputs.md) for an example on how to set up rate limiting for text input fields. - All incoming text that originates from another user must first use [Text filtering](/docs/en-us/ui/text-filtering.md) before your experience displays it. If users repeatedly send messages that violate community standards, Roblox warns and then temporarily prevents them from sending messages. To learn more, see [Text Chat Nudge FAQ](https://en.help.roblox.com/hc/en-us/articles/37541811348884-Text-Chat-Nudge-FAQ-s). - Communication between users must respect user privacy settings. - `Class.TextChannel:SendAsync()` handles basic privacy and parental settings automatically. - `Class.TextChannel:SetDirectChatRequester()` must be used to mark `Class.TextChannel|TextChannels` created for direct chat. - In-experience communication should be [reportable for abuse](https://en.help.roblox.com/hc/en-us/articles/203312410-How-to-Report-Rule-Violations). `Class.TextChannel|TextChannels` handle this automatically. ## Respect privacy settings Users can have different levels of access to communication features based on their [privacy and content maturity settings](https://www.roblox.com/my/account#!/privacy/Communication/ExperienceChat) or parental controls. Some users might have additional restrictions or constraints depending on which app store they used to install Roblox or their local laws. The following methods respect these requirements. Use them within your experience to determine how to handle messaging requests: - `Class.TextChannel:SetDirectChatRequester()` - `Class.TextChatService:CanUserChatAsync()` - `Class.TextChatService:CanUsersChatAsync()` - `Class.TextChatService:CanUsersDirectChatAsync()` --- title: "Text chat overview" url: /docs/en-us/chat/in-experience-text-chat last_updated: 2026-06-29T19:33:58Z description: "TextChatService lets players communicate with each other using text-based messages in live sessions." --- # Text chat overview Roblox offers text-based messaging between players in live sessions through `Class.TextChatService`, a singleton class responsible for managing the overall chat system, including chat message filtering, moderation, and user permissions. This service has its standard functionality and also provides a set of methods and events for extending and customizing chat, such as delivering messages based on [customized requirements](#conditionally-deliver-messages), adding special permissions or moderation to specific players, and creating [custom commands](/docs/en-us/chat/examples/custom-text-chat-commands.md) to execute specific actions. ## UI configuration `Class.TextChatService` provides a default UI that can be customized to fit your experience's needs. Disable any of these configurations to hide its associated UI element. If desired, you can also replace these UI elements with custom interfaces: - `Class.ChatWindowConfiguration` - `Class.ChatInputBarConfiguration` - `Class.ChannelTabsConfiguration` - `Class.BubbleChatConfiguration` For more information, see [Chat window](/docs/en-us/chat/chat-window.md) and [Bubble chat](/docs/en-us/chat/bubble-chat.md). ## Channels, messages, and commands - `Class.TextChannel` — Text channels pass user-sent messages from the client to the server, which then displays them to other users based on permissions. Text channels must be parented to `Class.TextChatService` in order to function. > **Info:** If `Class.TextChatService.CreateDefaultTextChannels` is set to `true`, the service automatically creates two text channels, `RBXGeneral` and `RBXSystem`. You can manually create additional `Class.TextChannel` instances and parent them to `Class.TextChatService`, as well. - `Class.TextSource` — A user in a `Class.TextChannel`. Text sources are directly parented to the `Class.TextChannel` when `Class.TextChannel:AddUserAsync()|AddUserAsync()` is called. Text sources contains detailed permissions of a user in the channel, such as their ability to send messages. If a single user is in multiple text channels, they are associated with multiple text sources. - `Class.TextChatMessage` — A message in a text channel. Chat messages contain basic information such as the sender of the message, the original message, the filtered message, and the creation timestamp. - `Class.TextChatCommand` — Lets users invoke specific actions or behaviors by sending messages that match the `Class.TextChatCommand.PrimaryAlias|PrimaryAlias` or `Class.TextChatCommand.SecondaryAlias|SecondaryAlias` properties. Chat commands must be parented to `Class.TextChatService` in order to function. > **Info:** If `Class.TextChatService.CreateDefaultCommands` is set to `true`, default chat commands will be created automatically. You can manually create additional `Class.TextChatCommand` instances and parent them to `Class.TextChatService`, as well. ## Chat flowchart Text chat uses the [client‑server](/docs/en-us/projects/client-server.md) model, with a **sending client**, the **server**, and **receiving clients**. ![A flowchart for in-experience text chat.](../assets/players/in-experience-text-chat/Chat-Workflow.png) 1. A player sends a message from their local device, triggering the `Class.TextChannel:SendAsync()` method. This method processes the message and determines whether it's a chat command or a regular chat message. - If the message is a chat command, it fires the `Class.TextChatCommand.Triggered` event to perform the defined action. No further steps are required. - If the message is a regular chat message, it fires the `Class.TextChatService.SendingMessage` event to display the message to the sender on the sending client. At the same time, `Class.TextChannel:SendAsync()` passes the message to the server. 2. The server fires `Class.TextChannel.ShouldDeliverCallback` to determine whether to deliver the message to other players based on permissions and Roblox community filtering requirements. 3. If `Class.TextChannel.ShouldDeliverCallback` determines that message is eligible to deliver to other players, the server applies any filters and fires `Class.TextChannel.OnIncomingMessage` twice: 1. The first time is on the sending client and signals that the server is processing the message through the `Class.TextChatService.MessageReceived` event. This event replaces the local message on the sending client with the processed message from the server. The message is identical if the original didn't require filtering. 2. The second time is on the receiving clients, which triggers the `Class.TextChatService.MessageReceived` event to display the message to other players. ## Text chat hooks and callbacks The `Class.TextChatService` API encourages a clear separation on the appearance and delivery of chat messages. Multiple instances of the text chat system provide hooks and callbacks to format in centralized, clear locations. > **Warning:** All callbacks are expected to be non-yielding functions. Yielding or waiting for a response in a callback blocks the chat system and can cause unexpected behavior. ![A flowchart of the TextChatService callbacks order](../assets/players/in-experience-text-chat/TextChatService-Callbacks.png) | Callback | Return Value | | --- | --- | | `Class.TextChannel.ShouldDeliverCallback` | boolean | | `Class.TextChatService.OnIncomingMessage` | `Class.TextChatMessageProperties` | | `Class.TextChannel.OnIncomingMessage` | `Class.TextChatMessageProperties` | | `Class.TextChatService.OnBubbleAdded` | `Class.BubbleChatMessageProperties` | | `Class.TextChatService.OnChatWindowAdded` | `Class.ChatWindowMessageProperties` | ### Conditionally deliver messages The `Class.TextChannel.ShouldDeliverCallback` callback should be defined on the server only. The callback is fired for each `Class.TextSource` child of the text channel when a message is sent to determine whether the message should be delivered. This callback can be used to implement custom message delivery logic that may depend on additional gameplay context, such as: - [Proximity-based chat](/docs/en-us/chat/examples/proximity-chat.md) where users can only send messages to those close to them. - Preventing users with certain attributes from sending messages to others. ### Customize message display The default `Class.TextChatService` UI relies on [rich text](/docs/en-us/ui/rich-text.md) to format and customize how messages are displayed. You can use the following callbacks to format messages before they are displayed to users, for example to add colors or [chat tags](/docs/en-us/chat/examples/group-chat-tags.md) to user names or format message content. The following callbacks are called on every `Class.TextChatMessage` that is about to be displayed, which lets you customize chat window appearance based on the `Class.TextChannel`, `Class.TextSource`, or `Class.TextChatMessage` content. When a client sends a message, these callbacks are called once when the message is sent to the server and the `Class.TextChatMessage.Status` value will be `Enum.TextChatMessageStatus.Sending`. Once the message is received by the server and is being delivered to other users, the sender client receives the message again with an updated `Enum.TextChatMessageStatus` value. - `Class.TextChatService.OnIncomingMessage` — This callback should be defined on the client only. The callback is fired when a message is received, either from the server or if the local client has just sent a message. The callback is called on every `Class.TextChatMessage` received from all `Class.TextChannel` instances and is the first to process the message before it is displayed to the user. - `Class.TextChannel.OnIncomingMessage` — This callback should be defined on the client only. The callback is fired when a message is received from the server. The callback is called on every `Class.TextChatMessage` received from the `Class.TextChannel`. Default `Class.TextChannel` instances created from `Class.TextChatService.CreateDefaultTextChannels` have this callback defined and can be overwritten. - `Class.TextChatService.OnBubbleAdded` — This callback should be defined on the client only. Use it to customize the appearance of chat bubbles independent of the appearance of the message in the chat window UI. - `Class.TextChatService.OnChatWindowAdded` — This callback should be defined on the client only. Use it to customize the appearance of chat messages in the chat window UI independent of the appearance of the message in chat bubbles. ## Migrate from legacy chat This section assists you in migrating from the legacy chat system by providing alternative methods for implementing common chat functionalities and behaviors using `Class.TextChatService`. 1. In the [Explorer](/docs/en-us/studio/explorer.md) window, select `Class.TextChatService`. 2. In the [Properties](/docs/en-us/studio/properties.md) window, find the `Class.TextChatService.ChatVersion|ChatVersion` dropdown and select `Enum.ChatVersion|TextChatService`. ### Basic functionalities Though both systems share the same basic chat functionalities, `Class.TextChatService` implementations are in general more sustainable and easier to iterate on. | Functionality | Legacy chat | TextChatService | Differences | | --- | --- | --- | --- | | Send a chat message | `Class.Players:Chat()` | `Class.TextChannel:SendAsync()` | The `Class.TextChannel:SendAsync()\|SendAsync()` method supports more advanced chat features, such as rich text formatting and message priority. It also includes built-in filtering to help prevent inappropriate messages from being sent. | | Implement messaging callbacks | `Class.Chat:InvokeChatCallback()`
`Class.Chat:RegisterChatCallback()` | `Class.TextChatService.SendingMessage`
`Class.TextChatService.OnIncomingMessage` | The legacy chat system binds a function to chat system events for delivering messages. The two methods of `TextChatService` offer better flexibility and customization. | | Add custom chat commands | `ChatService/ChatCommand` module | `Class.TextChatCommand` | `TextChatService` has a dedicated class for text commands rather than using a legacy chat module. | | Display a system message | `Class.StarterGui:SetCore()` using `ChatMakeSystemMessage` | `Class.TextChannel:DisplaySystemMessage()` | The `Class.TextChannel.OnIncomingMessage` callback can return a `Class.TextChatMessageProperties` instance to customize the message appearance. | | Disable chat | [Experience Settings](/docs/en-us/studio/experience-settings.md) in Studio and `ChatWindow/ChatSettings` module for hiding the chat window | `Class.ChatWindowConfiguration.Enabled` | | ### Message filtering `Class.TextChatService` automatically filters chat messages based on each player's account information, so you don't need to manually implement text filtering for all kinds of chat messages. | Functionality | Legacy chat | TextChatService | | --- | --- | --- | | Filter chat message for individual player | `Class.Chat:FilterStringAsync()` | Automatic | | Filter broadcasting messages | `Class.Chat:FilterStringForBroadcast()` | Automatic | ### Window and bubble chat Both the [chat window](/docs/en-us/chat/chat-window.md) and [bubble chat](/docs/en-us/chat/bubble-chat.md) behavior and customization options of `Class.TextChatService` are identical to those of the legacy chat system. As the legacy chat system only allows customization using chat modules or the `Class.Players` container, the service provides dedicated classes (`Class.ChatWindowConfiguration` and `Class.BubbleChatConfiguration`) to manage all chat window and bubble chat properties. Additionally, you can easily adjust and preview your bubble chat appearance and behavior properties using Studio settings instead of having to script them all. | Functionality | Legacy chat | TextChatService | | --- | --- | --- | | Enable Chat Window | `Class.Chat.LoadDefaultChat`
`Class.Players.ClassicChat` | `Class.ChatWindowConfiguration.Enabled` | | Enable Bubble Chat | `Class.Chat.BubbleChatEnabled`
`Class.Players.BubbleChat` | `Class.BubbleChatConfiguration.Enabled` | | Set Chat Window Properties | `Class.Players:SetChatStyle()` | `Class.ChatWindowConfiguration` | | Set Bubble Chat Properties | `Class.Chat:SetBubbleChatSettings()`
`Class.Chat.BubbleChatSettingsChanged()`
`Class.Players.BubbleChat`
`Class.Players:SetChatStyle()` | `Class.BubbleChatConfiguration` | | Enable NPC Bubbles | `Class.Chat:Chat()` | `Class.TextChatService:DisplayBubble()` | ### Migrate speaker "extra data" The legacy Lua chat system allowed developers to use `SetExtraData` on the `Speaker` class. This data was used to format the name color, chat color, or to apply name tags for a given speaker. ```lua -- An example of setting extra data on a speaker in the legacy chat system ChatService.SpeakerAdded:Connect(function(playerName) local speaker = ChatService:GetSpeaker(playerName) speaker:SetExtraData("NameColor", Color3.fromRGB(255, 255, 55)) speaker:SetExtraData("ChatColor", Color3.fromRGB(212, 175, 55)) speaker:SetExtraData("Tags", {{TagText = "YourTagName", TagColor = Color3.fromRGB(0, 255, 0)}, {TagText = "OtherTagName", TagColor = Color3.fromRGB(255, 0, 0)}}) end) ``` `Class.TextChatService` does not have a direct equivalent to `SetExtraData`. Instead, use [callbacks](#text-chat-hooks-and-callbacks) such as `Class.TextChatService.OnWindowAdded|OnWindowAdded` to customize the appearance of messages using rich text based on the `Class.TextSource` of the message. The following is an example of emulating legacy Lua chat's "extra data" by accessing attributes on `Class.Player` objects: ```lua local Players = game:GetService("Players") Players.PlayerAdded:Connect(function(player) player:SetAttribute("NameColor", Color3.fromRGB(255, 255, 55)) player:SetAttribute("ChatColor", Color3.fromRGB(212, 175, 55)) player:SetAttribute("isYourTag", true) player:SetAttribute("isOtherTag", true) end) ``` Then you can use the `Class.TextChatService.OnChatWindowAdded|OnChatWindowAdded` callback to customize the appearance of the chat window based on the attributes set on the player: ```lua local TextChatService = game:GetService("TextChatService") local Players = game:GetService("Players") TextChatService.OnChatWindowAdded = function(textChatMessage) local textSource = textChatMessage.TextSource if textSource then local player = Players:GetPlayerByUserId(textSource.UserId) if player then local overrideProperties = TextChatService.ChatWindowConfiguration:DeriveNewMessageProperties() overrideProperties.PrefixText = textChatMessage.PrefixText overrideProperties.Text = textChatMessage.Text local nameColor = player:GetAttribute("NameColor") if nameColor and typeof(nameColor) == "Color3" then overrideProperties.PrefixTextProperties.TextColor3 = nameColor end local chatColor = player:GetAttribute("ChatColor") if chatColor and typeof(chatColor) == "Color3" then overrideProperties.TextColor3 = chatColor end local isYourTag = player:GetAttribute("isYourTag") if isYourTag == true then overrideProperties.PrefixText = \`[YourTag] {overrideProperties.PrefixText}\` end local isOtherTag = player:GetAttribute("isOtherTag") if isOtherTag == true then overrideProperties.PrefixText = \`[OtherTag] {overrideProperties.PrefixText}\` end return overrideProperties end end return nil end ```
--- title: "Preset system guidelines" url: /docs/en-us/chat/preset-system-guidelines last_updated: 2026-06-29T19:33:58Z description: "Guidelines for implementing preset systems that comply with Roblox Community Standards and Terms of Use." --- # Preset system guidelines Preset systems let users safely send predefined text to each other to coordinate gameplay. They can be implemented in a variety of ways, such as wheels that let users select and send presets publicly to one another in-game. These systems **_must not_** be used to enable unrestricted, real-time, two-way communication between players of different ages or to bypass platform safety systems. They are meant to: - Support quick in-game coordination - Reduce friction in common gameplay moments - Preserve Roblox's existing safety and age-based communication protections ## Guidelines If you want to implement this type of system, you must adhere to these guidelines, which are meant to help you comply with our [Community Standards](https://about.roblox.com/community-standards) and [Terms of Use](https://en.help.roblox.com/hc/en-us/articles/115004647846-Roblox-Terms-of-Use). ### Safety first Preset systems must reinforce existing Roblox safety features – not work around them. If the preset system enables dynamic conversation, then it undermines platform safety and doesn't comply with our policies. **Ask yourself**: Does this feature break, bypass, or circumvent any existing Roblox safety features by enabling free-form, two-way, directed, and dynamic conversation? If it does, the system must respect the requirements that users be age checked (through [Facial Age Estimation](https://about.roblox.com/age-estimation) or [ID-verification](/docs/en-us/production/publishing/account-verification.md#verify-through-government-id)) and of a [similar age to chat](https://en.help.roblox.com/hc/en-us/articles/43611824582292-How-to-Chat-on-Roblox). If the system you want to implement is purely to help players communicate in-game actions, confirmations, or basic reactions, you can use a preset system that lets users send presets to each other regardless of age or age check status. ### Intent, not conversation Preset systems are for expressing immediate gameplay intent, not dialog. Each preset must stand alone and be complete without requiring a response unrelated to gameplay. General back and forth calls-to-action and replies such as "Help" followed by an "OK" are acceptable. **Presets must:** - Be relevant to the current game mode or scenario - Avoid open-ended or socially exploratory language - Ideally expire quickly or lose relevance outside the moment **Presets must not:** - Include slang that could carry hidden or evolving meanings - Reference to real-world contact, identity, or relationships - Involve emotional manipulation, social pressure, or content that would violate Roblox's Community Standards **Examples** - ✅ "Need help" - ✅ "Defending this area" - ✅ "Ready" - ❌ "Why didn't you help me?" - ❌ "Do you like me?" ### Limited, finite Scope Preset systems must not gain meaning when combined, repeated, or sequenced. They should be tied to the current game context and lose relevance outside the moment. If players can use the wheel to "say anything, just slower," the design is not permitted. **Examples** - ✅ "Pass the ball" - ✅ "Reloading" - ✅ "Enemy nearby" - ❌ "Where are you from?" - ❌ "What are your socials?" ## Enforcement and review All content and related features may be reviewed by Roblox to ensure compliance with our guidelines and to protect users on the platform. This may include: - Reviewing preset system content during experience review - Requiring changes or removal if a system enables circumvention or otherwise violates Roblox's Community Standards - Disabling preset system features that undermine platform safety goals - Taking additional enforcement actions taken on your experience, account, or group Creators are responsible for ensuring that all content and future updates continue to comply with these guidelines. ### Requirements All preset systems and their features must adhere to the following requirements: - All UI of presets must be properly labeled as **system preset** when displaying within chat - All preset systems should be visually distinct from Roblox's native freeform chat - Presets should not incorporate any end marks or terminal punctuation, such as exclamation marks, question marks, or periods, to prevent them from being confused with general chat messages - All presets must go through `Class.TextService:FilterStringAsync()` - Add a rate-limit (10 seconds per send) - Limit the number of presets displayed to 12 or less for your Universe ### What is allowed Preset systems may include: - Tactical gameplay commands (e.g., "Attack", "Defend", "Hold Position") - Cooperative commands (e.g., "Follow me", "Ready", "Wait") - Sports/FPS-style callouts (e.g., "Reloading", "Cover me", "Nice shot") - Neutral encouragement or game statements (e.g., "Well played", "Thanks") These should always be finite, curated, and creator-controlled. ### What is not allowed Preset systems may not be used to: - Simulate free-form chat or conversation - Compliment or flatter others with statements that don't relate to gameplay - Enable back-and-forth dialogue across age groups - Encode custom messages - Circumvent chat, DM, or voice restrictions - Replace or mirror unrestricted social features - Questions or social questions Examples of disallowed patterns: - Alphabet wheels or number-based encoding - Question/answer structures ("Why?", "Because") - "Hi!" "Hello!" "How are you?" "What's up?" --- title: "Voice Chat" url: /docs/en-us/chat/voice-chat last_updated: 2026-06-29T19:33:58Z description: "Explains how to use the Voice Chat feature." --- # Voice Chat > **Success:** Voice Chat is currently available to all 13+ phone number verified users in a specific set of countries. Review the [Voice Chat FAQ](https://en.help.roblox.com/hc/en-us/articles/4405807645972-Voice-Chat-FAQs) to learn more. Users **not** in these countries should use [ID verification](https://en.help.roblox.com/hc/en-us/articles/4407282410644) to enable chat with voice. Once verified, eligible 13+ phone verified users can opt‑in to use this feature by visiting their account **Settings** page or from within a voice enabled experience, allowing them to chat with voice in any Roblox experience that supports it. Experiences with voice often see an uplift in engagement, DAU, and spending. **Voice Chat** is a feature enabling real-time, spoken communication between yourself and other players. It is only available for places that support a maximum of 100 players. ![Two users chatting with voice inside an experience](../assets/players/voice-chat/In-Experience-Example.jpg) ## Enable Voice Chat Voice Chat is enabled by default for verified 13+ users on all new experiences with a maximum of 100 players. To activate voice for existing experiences or disable it for new ones: 1. Open your experience in Studio. 2. Open **File** ⟩ **Experience Settings**. 3. Navigate to the **Communication** tab on the left side of the window. 4. Toggle **Enable Voice Chat** to your desired setting. 5. **OPTIONAL** For greater communication among players within your experience, toggle on **Enable Camera** to allow eligible players to animate their avatar with their movement. 6. [Publish](/docs/en-us/production/publishing/publish-games-and-places.md) the place to save the changes. By enabling this setting, eligible 13+ users can opt-in to Voice Chat within your experience. Enabling Voice Chat is required to use the speech-to-text API. ### Set maximum players If you previously set the maximum number of players in a place to more than 100, you'll need to reduce it to support Voice Chat. 1. In the left-hand navigation of the **Experience Settings** window, select **Places**. Every place within your experience displays. 2. Click the **⋯** button next to the place with more than 100 players, then select **Configure Place**. 3. In the **Max Players** field, enter any number less than or equal to 100. 4. Click the **Save** button and then [publish](/docs/en-us/production/publishing/publish-games-and-places.md) to save the changes. When you update the maximum number of players in a place to fewer than 100, there may be servers already configured to a different, higher number. Since those servers won't support Voice Chat, it's recommended to [restart servers](/docs/en-us/projects/update-games.md#restart-servers). ### Customize voice behavior Voice Chat is **proximity-based** by default, adjusting the volume of participants based on how close they are to each other. However, you can set `Class.VoiceChatService.UseAudioApi|UseAudioApi` to `Enum.AudioApiRollout|Enabled` to take control over how voices are set up and used in your experience. If `Class.VoiceChatService` does not appear already: 1. Right‑click in the **Explorer** window and select **Show Services…** from the context menu. 2. Select `Class.VoiceChatService` in the popup window and click **Insert**. The service appears in the **Explorer** hierarchy.![VoiceChatService in Explorer hierarchy](../assets/studio/explorer/VoiceChatService.png) 3. With `Class.VoiceChatService` selected, ensure that `Class.VoiceChatService.UseAudioApi|UseAudioApi` to `Enum.AudioApiRollout|Enabled` in the **Properties** window. This may already be done for you. #### Team-based To implement `Class.Team`-based chat where only teammates can hear one another, you can use the following `Class.Script` within `Class.ServerScriptService`: ```lua local Teams = game:GetService("Teams") local function findAudioInput(forPlayer : Player) : AudioDeviceInput? -- Assumes there is only one AudioDeviceInput per player, parented to the Player object -- This is provided for you if VoiceChatService.EnableDefaultVoice is true -- May need to be reworked for generality if your place puts AudioDeviceInput objects elsewhere return forPlayer:FindFirstChildWhichIsA("AudioDeviceInput") end local function onTeamChanged(player : Player) local team = player.Team if not team then return end local device = findAudioInput(player) if not device then return end -- Only permit teammates to hear each other device.AccessType = Enum.AccessModifierType.Allow local allowed = {} for _, player : Player in team:GetPlayers() do table.insert(allowed, player.UserId) end device:SetUserIdAccessList(allowed) end local function onTeamAdded(team : Team) team.PlayerAdded:Connect(onTeamChanged) team.PlayerRemoved:Connect(onTeamChanged) end for _, team : Team in Teams:GetTeams() do onTeamAdded(team) end Teams.ChildAdded:Connect(function(child : Instance) if child:IsA("Team") then onTeamAdded(child) end end) ``` #### Non-spatial Depending on your experience's needs, it might make more sense to hear others with equal volume, regardless of their geographic location. If you [disable default Voice Chat](#disable-per-place), you can then implement non‑spatial Voice Chat through the following `Class.Script` within `Class.ServerScriptService`: ```lua local Players = game:GetService("Players") local Workspace = game:GetService("Workspace") local function wireUp(source : Instance, target : Instance) local wire = Instance.new("Wire") wire.SourceInstance = source wire.TargetInstance = target wire.Parent = source end -- Set up a global volume slider for everybody's voice local volumeSlider = Instance.new("AudioFader", Workspace) local output = Instance.new("AudioDeviceOutput", Workspace) wireUp(volumeSlider, output) local function onPlayerAdded(player : Player) local device = Instance.new("AudioDeviceInput", player) device.Player = player -- Route each new player's microphone input to the global volume slider wireUp(device, volumeSlider) end for _, player in Players:GetPlayers() do onPlayerAdded(player) end Players.PlayerAdded:Connect(onPlayerAdded) ``` > **Warning:** The above script runs on the server, creating an `Class.AudioDeviceInput` per‑player and connecting them all to a shared `Class.AudioFader`. Taking this behavior as‑is allows players to hear all other players **and** themselves. Since hearing yourself can be quite distracting, you can place the following `Class.LocalScript` in `Class.StarterPlayerScripts` to remove the `Class.Wire|Wires` that would otherwise ferry your own voice back to your own speakers. ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local function onDescendantAdded(descendant : Instance) if descendant:IsA("Wire") then descendant:Destroy() end end for _, descendant in player:GetDescendants() do onDescendantAdded(descendant) end player.DescendantAdded:Connect(onDescendantAdded) ``` ### Disable per place If you don't want to enable Voice Chat for every place within your experience, you can disable it within specific places that would otherwise be voice‑eligible. > **Info:** If you're currently setting a place's **Max Players** to a value over 100 in order to disable Voice Chat, it's recommended to use this workflow instead. To disable Voice Chat for a specific place within an experience: 1. Right‑click in the **Explorer** window and select **Show Services…** from the context menu. 2. Select `Class.VoiceChatService` in the popup window and click **Insert**. The service appears in the **Explorer** hierarchy.![VoiceChatService in Explorer hierarchy](../assets/studio/explorer/VoiceChatService.png) 3. With `Class.VoiceChatService` selected, disable `Class.VoiceChatService.EnableDefaultVoice|EnableDefaultVoice` in the **Properties** window. 4. Publish the place to save the changes and [restart servers](/docs/en-us/projects/update-games.md#restart-servers) to ensure the change takes effect for all servers currently running your experience. ## Check status You can check if a player has enabled Voice Chat by calling `Class.VoiceChatService:IsVoiceEnabledForUserIdAsync()|IsVoiceEnabledForUserIdAsync()` in a `Class.LocalScript`, or in a `Class.Script` with `Class.BaseScript.RunContext|RunContext` set to `Enum.RunContext.Client`. ```lua local Players = game:GetService("Players") local VoiceChatService = game:GetService("VoiceChatService") local localPlayer = Players.LocalPlayer local success, enabled = pcall(function() return VoiceChatService:IsVoiceEnabledForUserIdAsync(localPlayer.UserId) end) if success and enabled then print("Voice Chat enabled!") end ``` --- title: "Manage API keys" url: /docs/en-us/cloud/auth/api-keys last_updated: 2026-06-29T19:33:58Z description: "Explains how to create API keys and add permissions to use Open Cloud web APIs for your experience." --- # Manage API keys Open Cloud authenticates and authorizes API access with the use of API keys, which allow you to add granular permissions and security control for accessing and utilizing certain resources in your experience, such as data stores and places. All Open Cloud APIs require you to create an API key with valid permissions and include an `x-api-key` header in your request, which allows the application to authenticate to Open Cloud on your behalf. ## Create API keys > **Warning:** All references to API keys in the text below refer to user-owned keys. You can create and configure API keys to access your resources. An API key's access is determined by the permissions of the user who owns it. This means it can generally access any resource the user has permissions for, including their individual experiences and any [group-owned](/docs/en-us/projects/groups.md) experiences where they have the appropriate role. Some scopes can be restricted to specific experiences, but not all. For details on how to create API keys for managing group resources, see the [Create API keys for managing group-owned resources](#create-api-keys-for-managing-group-owned-resources) section below. To create an API key: 1. In the [Creator Dashboard](https://create.roblox.com/dashboard/creations), go to the [API Keys](https://create.roblox.com/dashboard/credentials?activeTab=ApiKeysTab) page. 2. Click the **Create API Key** button. 3. Enter a unique name for your API key. Use a name that can help you recall the purpose later, such as `PLACE_PUBLISHING_KEY` for publishing places to your experience. 4. In the **Access Permissions** section, select an API from the **Select API System** menu. Repeat this step if you need to add multiple APIs to the key. 5. If applicable, select the experience that you want to access with the API key. You can optionally disable **Restrict by Experience**. When disabled, your API key has access to all of your user-owned experiences and any group-owned experiences where you have the appropriate permissions, including any experiences you create in the future. 6. From the **Select Operations** dropdown, select the operations that you want to enable for the API key. Most operations in the [API reference](/docs/en-us/cloud.md) include the required permission scopes. For example, the [flush memory store](/docs/en-us/cloud/reference/MemoryStore.md#Cloud_FlushMemoryStore) operation requires the `universe.memory-store:flush` permission. For a list of all scopes and the APIs they support, see [Scopes](/docs/en-us/cloud/reference/scopes.md). 7. **(Optional)** In the **Security** section, explicitly restrict IP access to the key using [CIDR notation](#cidr-format). You can find the IP address of your local machine and add it to the **Accepted IP Addresses** section along with additional IP addresses for those that need access. If you don't have a fixed IP, or you are using the API key only in a local environment, you can leave the `Restrict IP addresses` toggle unchecked to allow any IP to use your API key. 8. **(Optional)**: To add additional protection for your resources, set an expiration date for your key. 9. Click the **Save & Generate key** button. 10. Copy and save the API key string to a secure location, **not** a public repository for your code. 11. Verify the status of your API key on the [API Extensions](https://create.roblox.com/dashboard/credentials) page of **Creator Dashboard**. > **Warning:** The API key string is equivalent to a password for your application. Never share it with untrusted parties, such as anyone outside of your development team. ## Create API keys for managing group-owned resources An API key grants access to all resources the user account has permissions for, including personal experiences outside of the group. If you use your personal account's API key for group automation and that key is compromised, other resources you have access to are also at risk. To prevent this, we **strongly recommend** creating a separate API key on a dedicated alternate account with access strictly limited to the target group. This new account dedicated for automation purposes should only be given access to the target group and granted the minimal permissions required for its task. 1. Create a new, dedicated Roblox account for your automation. 2. Invite the new account to your group. 3. Assign it a group role with the minimum permissions required for its task (e.g., only "Create and edit group experiences"). 4. Log in to the new account and follow the steps in the section above to [create an API key](#create-api-keys). 5. Use the generated API key for group resource automation. ## Best Practices For Managing API Keys API keys are sensitive credentials that should be kept secure to prevent unauthorized access to your data. Here are some best practices for managing API keys. - **Create separate keys for each application**: Create separate API keys for each application or use case to isolate access and reduce the impact if a key is compromised. - **Select the minimum permissions needed**: When configuring scopes, select the minimum permissions necessary for the key's intended use. For those scopes that allow you to restrict scope access by experience, limit access to only the specific experiences that are needed. - **Use IP address restrictions**: Restrict API key access to specific IP addresses or CIDR ranges to prevent unauthorized usage from unknown locations. Do not use IP address restrictions when using your API key in Roblox places to ensure your key can be used with Roblox servers. - **Set expiration dates**: For short-term use cases, configure expiration dates to automatically disable keys after a set period, reducing the risk if a key is compromised. Setting expiration dates is not recommended for longer-term use cases unless you have a key rotation process in place, as your automation can unexpectedly fail when the key expires. - **Use dedicated alternate accounts for group resource management**: Use a dedicated account with minimal permissions for group resource management, as detailed in the [Create API keys for managing group-owned resources](#create-api-keys-for-managing-group-owned-resources) section. - **Store API keys securely**: Never store API keys directly in your source code, version control systems, or scripts where they could be exposed. Use a secrets management system for storing and controlling access to your keys. In Roblox places, use a [Secrets Store](/docs/en-us/cloud/auth/cloud-services/secrets.md). - **Do not share API keys through public channels**: Never share API keys through public communication channels, forums, or social media. Only share keys through secure, private channels with trusted team members. Limit access to who you share your keys with to minimize the blast radius if a key is compromised. ## CIDR format To further protect your resources, when [creating an API key](#create-api-keys), specify IP addresses that can access the API key with either normal IP addresses or using the [CIDR notation](https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing#CIDR_notation). A CIDR IP address looks like a normal IP address except it ends with a slash and a decimal that represents how many bits of the IP address are significant for network routing: - **Normal**: 192.168.0.0 - **CIDR**: 192.168.0.0/24 The former part is the IP address and the latter part is the **netmask**, counting the bits of 1s in binary format. In the previous example, **24** means **255.255.255.0** (24 1s) that allows all IPs between **192.168.0.0** and **192.168.0.255**. Understanding CIDR format is particularly useful if you plan to run your applications on a server. ## API key status API keys initially have an active status, but they can become inactive over their lifetime. To learn why an API key has changed status and how to return the API key back to an active status, see the following table. > **Warning:** If you or your group don't use or update an API key for 60 days, it automatically expires, even if it doesn't have a set expiration date. This reduces the risk of bad actors using old API keys. If you're no longer using an API key, you should manually disable or delete it. | Status | Reason | Resolution | | --- | --- | --- | | Active | No issues. The user can use the key to authenticate API calls. | N/A | | Disabled | The user disabled the key by disabling the **Enable Key** toggle. | Enable the **Enable Key** toggle. | | Expired | The key's expiration date has passed. | Either remove or set a new expiration date. | | Auto-Expired | The user hasn't used or updated the key in the past 60 days. | You can either disable then enable the **Enable Key** toggle, or you can update any of the key's properties, such as the name, description, or expiration date. | | Revoked | For group keys only. The account that generated the key no longer has the sufficient access permission to manage the group's keys. | Click the **Regenerate Key** to get a new secret. | | Moderated | A Roblox admin changed the key's secret for security reasons. | Click the **Regenerate Key** to get a new secret. | | User Moderated | The account that generated the key is under moderation by Roblox. | Resolve the moderation issue on the account. | ## Introspect API keys ### `POST` api-keys/v1/introspect Retrieve information about an API key. Verifies whether the key can be used from the requester's IP address and whether the key or the last generated user is moderated. #### Request `(application/json)` | Key | Value | | --- | --- | | apiKey | `` | ```bash curl --location --request POST 'https://apis.roblox.com/api-keys/v1/introspect' \ --header 'Content-Type: application/json' \ --data '{ "apiKey": "your-api-key" }' ``` #### Response There are four possible resource identifiers that can be present in each scope object: - userId - groupId - universeId - universeDatastore The userId and groupId identifiers are only relevant to scopes with the creator target. The universeDatastore identifier is only relevant to scopes with the universe-datastore target. The resource identifier will be omitted for scopes that do not support resource selection. An asterisk (`*`) in the resource identifier list indicates that the scope has permission on all resources of that type. ```json { "name": "test key", "authorizedUserId": 234, "scopes": [ { "name": "universe-datastores.objects", "operations": [ "create" ], "universeDatastores": [ { "universeId": "123", "datastoreName": "playerData" } ] }, { "name": "asset", "operations": [ "write" ], "groupIds": [ "*" ], "userIds": [ "*" ] } ], "enabled": true, "expired": false, "expirationTimeUtc": "2026-01-01T12:00:00.000Z" } ``` --- title: "OAuth 2.0 app implementation" url: /docs/en-us/cloud/auth/oauth2-develop last_updated: 2026-06-29T19:33:58Z description: "Introduces OAuth 2.0 authentication implementation." --- # OAuth 2.0 app implementation Open Cloud supports the OAuth 2.0 authorization flow with and without [PKCE](/docs/en-us/cloud/auth/oauth2-overview.md#authorization-code-flow-with-pkce), depending on whether your clients are confidential or public. - _Confidential clients_ are apps that are capable of keeping credentials confidential, such as a website that can securely store and retrieve secrets on its backend. - _Public clients_ are apps that can't keep secrets, such as mobile and web browser apps. We recommend the OAuth 2.0 authorization code flow with PKCE for _all_ clients and require it for public clients. > **Warning:** Store the client secret in a secure place and never expose it to the public. Bad actors can use it to impersonate your application. To implement the authorization code flow, your app performs the following steps: 1. Generate a code verifier and code challenge (PKCE only). This lets you include a challenge in your requests rather than the client secret, which improves security. 2. Send a `GET` request to the authorization endpoint with the appropriate parameters. 3. Handle the authorization callback. After obtaining authorization, use the authorization code from Roblox in a redirect request to the URL you specified during app creation. Parse the authorization code for later use. 4. Send a `POST` request to the token endpoint with the authorization code. If successful, you receive access and refresh tokens with which to make API calls. 5. Call any Open Cloud API that supports OAuth 2.0 authentication based on the reference documentation for the resources you want to access. The following sections describe each step in greater depth. ## Generate a code challenge (for PKCE only) Before initiating the authorization process, you need to generate a code challenge from a code verifier. You can use libraries or built-in functions in the programming language of your choice to create the code verifier, calculate the hash, and perform Base64 encoding to generate the code challenge. When creating the code verifier, only use unreserved characters, including uppercase and lowercase letters (A-Z, a-z), decimal digits (0-9), hyphen (-), period (.), underscore (_), and tilde (~), with a minimum length of 43 characters and a maximum length of 128 characters. The following example shows how to use Javascript to create a code verifier and generate the code challenge using the SHA-256 hashing algorithm: ```javascript const crypto = require('crypto'); // base64URL encode the verifier and challenge function base64URLEncode(str) { return str.toString('base64') .replace(/\+/g, '-') .replace(/\//g, '_') .replace(/=/g, ''); } // create sha256 hash from code verifier function sha256(buffer) { return crypto.createHash('sha256').update(buffer).digest(\`base64\`); } // create a random code verifier var code_verifier = base64URLEncode(crypto.randomBytes(32)); // generate a challenge from the code verifier var code_challenge = base64URLEncode(sha256(code_verifier)); ``` For PKCE, you need both the code verifier and challenge values in later steps. ## Construct the authorization URL > **Info:** See the [authorization endpoint reference documentation](/docs/en-us/cloud/auth/oauth2-reference.md#get-v1authorize) for complete information on how to construct the authorization URL. To initiate the user authorization process, construct an authorization URL with the required parameters: - `client_id`: Your app's client ID obtained after [registering your app](/docs/en-us/cloud/auth/oauth2-registration.md). - `redirect_uri`: Your app's redirect URI, the re-entry point of your app. - `scope`: The requested scopes that define the access permissions your app needs in a space-separated list. - `response_type`: Set it to `code` to indicate the authorization code flow. **Required for PKCE only** - `code_challenge`: The code challenge generated from a code verifier. - `code_challenge_method`: Set this parameter value to `S256` to indicate that the code challenge was transformed using the SHA-256 algorithm, the most widely supported and secure code challenge method. - `state`: An opaque, secure random value to maintain state between the request and callback. **Required for non-PKCE only** - `client_secret`: Your app's client secret obtained after [registering your app](/docs/en-us/cloud/auth/oauth2-registration.md). If you are using authentication with PKCE, omit this parameter. Your authorization request URL must be well formatted, like in the following example: ```bash https://apis.roblox.com/oauth/v1/authorize?client_id=7290610391231237934964 &code_challenge=PLEKKVCjdD1V_07wOKlAm7P02NC-LZ_1hQfdu5XSXEI &code_challenge_method=S256 &redirect_uri=https://example.com/redirect &scope=openid%20profile &response_type=code &state=abc123 ``` When users visit the URL, they are taken through the authorization flow. If successful, Roblox redirects the user to the specified `redirect_uri`. ## Handle authorization callbacks When an authorization flow is successful, your app receives a `GET` request from the Roblox authorization server at the `redirect_uri` that you specified. In the request, you receive `code` (authorization code) and `state`(if you provided a value previously) parameters. - The `code` parameter contains an authorization code that the app can exchange for an access token from the authorization server. Most backend server languages have standard ways to access query parameters as decoded objects. You'll need to obtain the `code` parameter and use it to exchange for access tokens. - The `state` parameter is an opaque value that you initially provided in the authorization request. You can use this parameter to maintain the state or context of the authorization process. For example, you might receive a `GET` request with the following URL if you specified your redirect URI to be `https://example.com/redirect`. ```bash https://example.com/redirect?code=10c45PNuquKnFJ6pUcy5-fHkghEM6lSegm-7hj9mVEprub1dSDuStuKK_EAUXY7AHTD63xcnmvxSLthp-C8g3jzIGZVzuXSd20Y2dEYI9hx0LZmPg95ME4z2K03UheiZbroyXUjYyB3ReoMqobzDVPzyx6IS8kj2Uu-11Xq_0JiTYxtDatuqXRNIAmJT8gMJmbSyOLOP_vnDvbeMUiBsqCRrkTGVbWSwYSc8sTVVE-535kYdqQOgNjH1ffCoZLGl8YYxLnpb2CXJlRQPrcjkA&state=6789 ``` If authorization fails, your app receives a `GET` request to the specified redirect URL with the `error`, `error_description`, and `state` (if applicable) parameters. - The `error` parameter indicates the specific OAuth 2.0 error that occurred during the authorization process. - The `error_description` parameter provides additional details of the error. - The `state` parameter helps your app maintain state in the case of a failure. ## Exchange an authorization code for access tokens When you have parsed the authorization `code`, exchange it for tokens to access desired Roblox resources: 1. Request an access token and refresh token by sending a `POST` request to the [token exchange endpoint](/docs/en-us/cloud/auth/oauth2-reference.md#post-v1token). The request must include the authorization code, client ID, and the code verifier value (PKCE) or client secret (non-PKCE) in `x-www-form-urlencoded` format. 2. Parse the applicable tokens from the response received. The access token is valid for 15 minutes. Store the refresh token securely, typically on the server side, so you can use it to obtain new tokens in the future.```json { "access_token": "eyJhbGciOiJFUzI1NiIsImtpZCI6IlBOeHhpb2JFNE8zbGhQUUlUZG9QQ3FCTE81amh3aXZFS1pHOWhfTGJNOWMiLCJ0eXAiOiJKV11234.eyJzdWIiOiIyMDY3MjQzOTU5IiwiYWlkIjoiM2Q2MWU3NDctM2ExNS00NTE4LWJiNDEtMWU3M2VhNDUyZWIwIiwic2NvcGUiOiJvcGVuaWQ6cmVhZCBwcm9maWxlOnJlYWQiLCJqdGkiOiJBVC5QbmFWVHpJU3k2YkI5TG5QYnZpTCIsIm5iZiI6MTY5MTYzOTY5OCwiZXhwIjoxNjkxNjQwNTk4LCJpYXQiOjE2OTE2Mzk2OTgsImlzcyI6Imh0dHBzOi8vYXBpcy5yb2Jsb3guY29tL29hdXRoLyIsImF1ZCI6IjcyOTA2MTAzOTc5ODc5MzQ5Nj1234.BjwMkC8Q5a_iP1Q5Th8FrS7ntioAollv_zW9mprF1ats9CD2axCvupZydVzYphzQ8TawunnYXp0Xe8k0t8ithg", "refresh_token": "eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwia2lkIjoidGpHd1BHaURDWkprZEZkREg1dFZ5emVzRWQyQ0o1NDgtUi1Ya1J1TTBBRSIsInR5cCI6IkpXVCJ9..nKYZvjvXH6msDG8Udluuuw.PwP-_HJIjrgYdY-gMR0Q3cabNwIbmItcMEQHx5r7qStVVa5l4CbrKwJvjY-w9xZ9VFb6P70WmXndNifnio5BPZmivW5QkJgv5_sxLoCwsqB1bmEkz2nFF4ANLzQLCQMvQwgXHPMfCK-lclpVEwnHk4kemrCFOvfuH4qJ1V0Q0j0WjsSU026M67zMaFrrhSKwQh-SzhmXejhKJOjhNfY9hAmeS-LsLLdszAq_JyN7fIvZl1fWDnER_CeDAbQDj5K5ECNOHAQ3RemQ2dADVlc07VEt2KpSqUlHlq3rcaIcNRHCue4GfbCc1lZwQsALbM1aSIzF68klXs1Cj_ZmXxOSOyHxwmbQCHwY7aa16f3VEJzCYa6m0m5U_oHy84iQzsC-_JvBaeFCachrLWmFY818S-nH5fCIORdYgc4s7Fj5HdULnnVwiKeQLKSaYsfneHtqwOc_ux2QYv6Cv6Xn04tkB2TEsuZ7dFwPI-Hw2O30vCzLTcZ-Fl08ER0J0hhq4ep7B641IOnPpMZ1m0gpJJRPbHX_ooqHol9zHZ0gcLKMdYy1wUgsmn_nK_THK3m0RmENXNtepyLw_tSd5vqqIWZ5NFglKSqVnbomEkxneEJRgoFhBGMZiR-3FXMaVryUjq-N.Q_t4NGxTUSMsLVEppkTu0Q6rwt2rKJfFGuvy3s12345", "token_type": "Bearer", "expires_in": 899, "id_token": "eyJhbGciOiJFUzI1NiIsImtpZCI6IkNWWDU1Mi1zeWh4Y1VGdW5vNktScmtReFB1eW15YTRQVllodWdsd3hnNzgiLCJ0eXAiOiJKV11234.eyJzdWIiOiIyMDY3MjQzOTU5IiwibmFtZSI6ImxpbmtzZ29hdCIsIm5pY2tuYW1lIjoibGlua3Nnb2F0IiwicHJlZmVycmVkX3VzZXJuYW1lIjoibGlua3Nnb2F0IiwiY3JlYXRlZF9hdCI6MTYwNzM1NDIzMiwicHJvZmlsZSI6Imh0dHBzOi8vd3d3LnJvYmxveC5jb20vdXNlcnMvMjA2NzI0Mzk1OS9wcm9maWxlIiwibm9uY2UiOiIxMjM0NSIsImp0aSI6IklELnltd3ZjTUdpOVg4azkyNm9qd1I5IiwibmJmIjoxNjkxNjM5Njk4LCJleHAiOjE2OTE2NzU2OTgsImlhdCI6MTY5MTYzOTY5OCwiaXNzIjoiaHR0cHM6Ly9hcGlzLnJvYmxveC5jb20vb2F1dGgvIiwiYXVkIjoiNzI5MDYxMDM5Nzk4NzkzNDk2NCJ9.kZgCMJQGsariwCi8HqsUadUBMM8ZOmf_IPDoWyQY9gVX4Kx3PubDz-Q6MvZ9eU5spNFz0-PEH-G2WSvq2ljDyg", "scope": "openid profile" } ``` ## Make a call to a resource method Now that you have the required access token, you can use it to make authenticated calls to resource methods. Include the access token in the header of all API requests to authorize them. For example, you can test if your app functions correctly by going through the entire authorization flow and then making a `GET` request to the [user information endpoint](/docs/en-us/cloud/auth/oauth2-reference.md#get-v1userinfo) with an access token. Ensure you have the `openid` or both the `openid` and `profile` scopes before calling this endpoint. If successful, the response from that endpoint looks like this: ```json { "sub": "12345678", "name": "Jane Doe", "nickname": "robloxjanedoe", "preferred_username": "robloxjanedoe", "created_at": 1607354232, "profile": "https://www.roblox.com/users/12345678/profile" } ``` --- title: "OAuth 2.0 overview" url: /docs/en-us/cloud/auth/oauth2-overview last_updated: 2026-06-29T19:33:58Z description: "Introduces the 3rd-party app creation support with OAuth 2.0 authorization framework." --- # OAuth 2.0 overview You can build or authorize apps that use Open Cloud APIs to access Roblox resources. Open Cloud provides authentication for these apps using OAuth 2.0.
**As an experience creator or group owner**
You can securely use tools created by others to improve your creation productivity. The OAuth 2.0 authorization layer allows you to grant permissions to third-party apps to access your or your group's experiences without giving them your credentials and personal information. You select the access permissions of your specific Roblox resources, and Roblox handles the authorization process for you with the OAuth 2.0 framework. > **Info:** You must have a 13+ account to authorize OAuth 2.0 apps.
**As an app developer**
You can create apps for yourself and others in the Roblox community. OAuth 2.0 defines the roles involved in the authorization process, the protocol of how roles interact with each other, and the authorization flows that you need to follow to develop secure and compatible apps. > **Info:** You must be [ID verified](/docs/en-us/production/publishing/account-verification.md#verify-through-government-id) to register and publish OAuth 2.0 apps.
## Roles The Open Cloud OAuth 2.0 protocol has the following roles. It's useful to understand the specific roles before learning about how they interact with one another in authorization flows. - **Resource owner**: An entity capable of granting access to a protected resource. For example, a creator who allows a third-party app to access their Roblox resources through Open Cloud Web APIs. - **Resource server**: A Roblox service that hosts protected resources and responds to requests from a resource owner. - **Client**: An app that accesses protected resources on behalf of the resource owner (with the owner's authorization). - **Authorization server**: The Roblox server that authenticates the identity of the resource owner and issues access tokens to the client. ## Grant types Authorization flows, or grant types, are the steps of actions that roles perform during the authorization process. Roblox supports the OAuth 2.0 authorization code flow and its Proof Key for Code Exchange (PKCE) extension, with different implementation requirements for apps that are capable or incapable of storing client secrets. ### Authorization code flow Through the authorization code flow, a client exchanges an authorization code for an access token and a refresh token to complete the authorization process in the following steps: 1. The client sends an authorization request to the Roblox authorization server. 2. The authorization server verifies the identity of the resource owner. 3. The authorization server receives permissions for accessing specific Roblox resources from the resource owner. 4. The authorization server redirects the resource owner back to the client with an authorization code. 5. The client requests an access token using the authorization code at the token endpoint. 6. The client receives a response from the token endpoint containing an access token, an ID token, and a refresh token. 7. The client retrieves the permitted resources after getting the access token. The following figure describes the interactions between roles in the authorization code flow that you'll read in the following sections: ### Authorization code flow with PKCE The [PKCE extension](https://www.rfc-editor.org/rfc/rfc7636) of the authorization code flow helps reduce risk of leaking the authorization code and prevent cross-site request forgery (CSRF), an attack that tricks users into submitting unintended web requests. This flow completes the authorization process with the following steps: 1. The client generates a unique and cryptographically random key called a _code verifier_ for every authorization request. 2. The client runs a SHA-256 hash algorithm on the code verifier to generate a _code challenge_. 3. If the client: - Is a [public client](/docs/en-us/cloud/auth/oauth2-develop.md#public-clients), instead of using the client secret, it passes the client ID and the code challenge in the authorization request. - Is a [confidential client](/docs/en-us/cloud/auth/oauth2-develop.md#confidential-clients), it adds the code challenge along with the client ID and secret in the request. 4. The client sends an authorization request to the Roblox authorization server. 5. The authorization server verifies the identity of the resource owner. 6. The authorization server receives permissions for accessing specific Roblox resources from the resource owner. 7. The authorization server redirects the resource owner back to the client with an authorization code. 8. The client includes the authorization code and the original code verifier in the token request to the [token endpoint](/docs/en-us/cloud/auth/oauth2-reference.md#token-exchange). 9. The authorization server verifies the authorization code and the associated code verifier. 10. The client receives a response from the token endpoint containing an access token, an ID token, and a refresh token. 11. The client retrieves the permitted resources after getting the access token. ## OpenID Connect support Roblox uses [OpenID Connect (OIDC)](https://openid.net/connect/) as an identity layer on top of the OAuth 2.0 protocol for authentication to protect sensitive account information. OIDC allows applications to verify the identity of users and obtain their basic public profile information, such as user ID, usernames, display names, and profile links. > **Info:** When [adding permission scopes](/docs/en-us/cloud/auth/oauth2-registration.md#register-an-app) to your app on **Creator Dashboard**, make sure to select the `openid` identity scope for receiving an ID token in the token response as part of the authentication process. ## Registration and implementation To implement a web or mobile app that uses authorization code flow, you need to: 1. [Register](/docs/en-us/cloud/auth/oauth2-registration.md) your app with Roblox. This lets you obtain a client ID and secret to register your app with Roblox and make calls to your endpoints. 2. [Implement](/docs/en-us/cloud/auth/oauth2-develop.md) the authorization code flow. For a complete reference of the OAuth 2.0 endpoints that you need to call, see the [Authentication](/docs/en-us/cloud/auth/oauth2-reference.md) reference. 3. Go through the [review process](/docs/en-us/cloud/auth/oauth2-registration.md#submit-for-review) to get more user quota.
--- title: "OAuth 2.0 authentication" url: /docs/en-us/cloud/auth/oauth2-reference last_updated: 2026-06-29T19:33:58Z description: "Describes how to make authenticated calls to Open Cloud with API keys and OAuth 2.0" --- # OAuth 2.0 authentication > **Warning:** OAuth 2.0 authentication is a beta feature that might be subject to change for future releases. All Open Cloud endpoints also support [API key authentication](/docs/en-us/cloud/auth/api-keys.md). > **Info:** For complete implementation guides and information on OAuth 2.0, including a sample app, see the [OAuth 2.0 overview](/docs/en-us/cloud/auth/oauth2-overview.md). This document describes endpoints that you call to implement the OAuth 2.0 authorization code flow, as well as other endpoints useful for implementing authentication in your apps. ```http https://apis.roblox.com/oauth ``` ```http GET v1/authorize POST v1/token POST v1/token/introspect POST v1/token/resources POST v1/token/revoke GET v1/userinfo GET .well-known/openid-configuration ``` ## Authorization ### `GET` v1/authorize Obtains authorization from the user to authenticate with their Roblox account. Expects a valid authorization URL constructed with the specified parameters. This endpoint supports PKCE authorization. #### Query parameters | Name | Description | Required | Example | | --- | --- | --- | --- | | client_id | The app's client ID. | yes | `816547628409595165403873012` | | redirect_uri | The URL that users are redirected back to after the completing authorization flow. | yes | `https://www.roblox.com/example-redirect` | | scope | The requested scopes, space delimited. Use the `openid` scope to receive an ID token. Use the `openid` and `profile` scope to obtain more user information. | yes | `openid profile` | | response_type | The credentials the app wants returned. The default is the authorization code grant type. | yes | `none`, `code` | | prompt | Specifies what authentication and consent pages are shown to users. Certain screens are required for third-party apps and can't be skipped. | no | `none`, `login`, `consent`, `select_account` | | nonce | The cryptographic number that binds the token with the client. | no | `` | | state | An opaque value that prevents cross-site request forgery, a type of malicious attack. Passed back to the app after authorization. | no | `` | | code_challenge | The resulting string of applying the `code_challenge_method` to the `code_verifier`. | no | Base64-URL-encoded string | | code_challenge_method | The function that is applied to the `code_verifier`. | no | S256 | #### Request ```plain https://apis.roblox.com/oauth/v1/authorize?client_id=816547628409595165403873012&redirect_uri=https://my-app.com/redirect&scope=openid&response_type=code&nonce=12345&state=6789 ``` #### Response After calling this endpoint, the user is redirected to the specified redirect URL with the authorization code in a `code` query parameter. The authorization code: - Has a lifetime of one minute. - Can only be redeemed once. - Is invalid after one use. ## Token exchange To obtain tokens to access APIs, exchange an **authorization code** for a set of confidential tokens. All token endpoints support two types of client authentication: 1. HTTP Basic Authentication Scheme with an authorization header: `Authorization: Basic Base64 encoded(:)`. 2. Client ID and secret in the request body as parameters. The following list describes the various tokens you receive from this endpoint. - **Access token** - Represents the authorization from a creator or user for a third-party app to access their protected Roblox resources. It's a string denoting a specific scope, lifetime, and other access attributes. Once the Roblox authorization server issues an access token to an app, the token: - Is valid for 15 minutes. - Can be used multiple times before it expires. - Can be invalidated before it expires if an app user revokes the authorization. - **Refresh token** - Refreshes an authorization session. An app can use the refresh token to obtain a new set of tokens, which includes an access token, a refresh token, and an ID token. A refresh token: - Is valid for 90 days. - Can only be used once before it expires to refresh tokens. - Can be invalidated before it expires if an app user revokes the authorization. - **ID token** - Provides proof that a user's identity has been authenticated. Its content depends on the scopes requested and can contain basic user information, including a user's Roblox display name and username. The ID token is only for identity authentication purposes and doesn't provide access to any Roblox resources. ### `POST` v1/token Obtain a set of tokens with an authorization code. #### Request `(x-www-form-urlencoded)` | Key | Value | | --- | --- | | code | `` | | code_verifier | `` | | grant_type | authorization_code | | client_id | `` | | client_secret | `` | ```bash curl --location --request POST 'https://apis.roblox.com/oauth/v1/token' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'client_id=840974200211308101' \ --data-urlencode 'client_secret=RBX-CR9...St12L' \ --data-urlencode 'grant_type=authorization_code' \ --data-urlencode 'code=yCnq4ofX1...XmGpdx' ``` #### Response ```json { "access_token": "...", "refresh_token": "...", "token_type": "Bearer", "expires_in": 899, "scope": "universe-messaging-service:publish" } ``` ### `POST` v1/token Obtain a set of tokens with a refresh token. #### Request `(x-www-form-urlencoded)` | Key | Value | | --- | --- | | grant_type | refresh_token | | refresh_token | `` | | client_id | `` | | client_secret | `` | ```bash curl --location --request POST 'https://apis.roblox.com/oauth/v1/token' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'grant_type=refresh_token' \ --data-urlencode 'refresh_token=Ujfstayclfdlbm...BGydlsnU' \ --data-urlencode 'client_id=840974200211308101' \ --data-urlencode 'client_secret=RBX-CR9...St12L' ``` #### Response ```json { "access_token": "...", "refresh_token": "...", "token_type": "Bearer", "expires_in": 899, "scope": "universe-messaging-service:publish" } ``` ### `POST` v1/token/introspect Receive information about a token. Verifies whether the token is presently valid and hasn't expired yet. Useful for stateless validation. Use only if the API you're accessing doesn't require a resource, such as Assets API, or if you just want to see specific claims of the token. > **Info:** Even if the user has revoked the authorization linked to the access token, the endpoint still shows the access token as `valid: true` because it checks whether the token is active based on its lifetime (typically 15 minutes for access tokens). #### Request `(x-www-form-urlencoded)` | Key | Value | | --- | --- | | token | ``, `` or `` | | client_id | `` | | client_secret | `` | ```bash curl --location --request POST 'https://apis.roblox.com/oauth/v1/token/introspect' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'token=eyjlflabtfl...4gxqYBG' \ --data-urlencode 'client_id=840974200211308101' \ --data-urlencode 'client_secret=RBX-CR9...St12L' ``` #### Response ```json { "active": true, "jti": "RT.2GcjvTduKzk6QY9tjTfm", "iss": "https://apis.roblox.com/oauth/", "token_type": "Bearer", "client_id": "840974200211308101", "aud": "4239311013248676173", "sub": "1516563360", "scope": "universe-messaging-service:publish", "exp": 1676394509, "iat": 1660842510 } ``` ### `POST` v1/token/resources Check whether a token can access a specific resource by obtaining the list of user resources that the user gave permission for. This is useful for stateful validation. #### Request `(x-www-form-urlencoded)` | Key | Value | | --- | --- | | token | `` | | client_id | `` | | client_secret | `` | ```bash curl --location --request POST https://apis.roblox.com/oauth/v1/token/resources' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'token=eyjlflabtfl...4gxqYBG' \ --data-urlencode 'client_id=840974200211308101' \ --data-urlencode 'client_secret=RBX-CR9...St12L' ``` #### Response The value `U` in `ids` indicates that a scope has granted access to a resource owned by the authorizing `owner`. ```json { "resource_infos": [ { "owner": { "id": "1516563360", "type": "User" }, "resources": { "universe": { "ids": ["3828411582"] }, "creator": { "ids": ["U"] } } } ] } ``` ### `POST` v1/token/revoke Revoke an authorization session using the provided refresh token. #### Request `(x-www-form-urlencoded)` | Key | Value | | --- | --- | | token | `` | | client_id | `` | | client_secret | `` | ```bash curl --location --request POST https://apis.roblox.com/oauth/v1/token/revoke' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'token=Ujfstayclfdlbm...BGydlsnU' \ --data-urlencode 'client_id=840974200211308101' \ --data-urlencode 'client_secret=RBX-CR9...St12L' ``` #### Response `200 OK` with an empty response ## User Information ### `GET` /v1/userinfo Gets the Roblox user ID and other user metadata. #### Request **Authorization header**: `Authorization: Bearer ` ```bash curl --location --request GET 'https://apis.roblox.com/oauth/v1/userinfo' \ --header 'Authorization: Bearer eyjlflabtfl...4gxqYBG' ``` #### Response You can use the `sub` value to uniquely identify the user. Users can change their Roblox username and display name, so don't use them as unique identifiers to refer to users on your app. | Claim | Description | | --- | --- | | sub | Roblox user ID. | | name | Roblox display name. | | nickname | Roblox display name. | | preferred_username | Roblox username. | | created_at | Creation time of the Roblox account as a Unix timestamp. | | profile | Roblox account profile URL. | | picture | Roblox avatar headshot image. Can be null if the avatar headshot image hasn't yet been generated or has been moderated. | ```json { "sub": "1516563360", "name": "exampleuser", "nickname": "exampleuser", "preferred_username": "exampleuser", "created_at": 1584682495, "profile": "https://www.roblox.com/users/1516563360/profile", "picture": "https://tr.rbxcdn.com/03dc2a9abe7b1aacaaf93ea46d5c0646/150/150/AvatarHeadshot/Png" } ``` ```json { "sub": "1516563360" } ``` ## Discovery The OpenID Connect (OIDC) Discovery Document is a JSON document that contains metadata about the Open Cloud configuration details, including a list of identity-related scopes and claims that are supported. You can use it to dynamically discover information about Open Cloud OAuth 2.0 endpoints and configuration, such as the authorization endpoint, token endpoint, and public key set. After retrieving and fetching the Discovery Document from the Discovery document URI, you can either manually inspect fields in the response to verify the information, or you can create your own custom library mapping to fields in the response schema to automate your workflow. ### `GET` .well-known/openid-configuration #### Response All Discovery Document responses follow the same schema as the following example response. > **Warning:** Not all of the following scopes and claims are available to third-party app creators, as some can only be used by official Roblox apps. ```json { "issuer": "https://apis.roblox.com/oauth/", "authorization_endpoint": "https://apis.roblox.com/oauth/v1/authorize", "token_endpoint": "https://apis.roblox.com/oauth/v1/token", "introspection_endpoint": "https://apis.roblox.com/oauth/v1/token/introspect", "revocation_endpoint": "https://apis.roblox.com/oauth/v1/token/revoke", "resources_endpoint": "https://apis.roblox.com/oauth/v1/token/resources", "userinfo_endpoint": "https://apis.roblox.com/oauth/v1/userinfo", "jwks_uri": "https://apis.roblox.com/oauth/v1/certs", "registration_endpoint": "https://create.roblox.com/dashboard/credentials", "service_documentation": "https://create.roblox.com/docs/reference/cloud", "scopes_supported": [ "openid", "profile", "email", "verification", "credentials", "age", "premium", "roles" ], "response_types_supported": ["none", "code"], "subject_types_supported": ["public"], "id_token_signing_alg_values_supported": ["ES256"], "claims_supported": [ "sub", "type", "iss", "aud", "exp", "iat", "nonce", "name", "nickname", "preferred_username", "created_at", "profile", "email", "email_verified", "verified", "age_bracket", "premium", "roles", "internal_user" ], "token_endpoint_auth_methods_supported": [ "client_secret_post", "client_secret_basic" ] } ``` --- title: "OAuth 2.0 app registration" url: /docs/en-us/cloud/auth/oauth2-registration last_updated: 2026-06-29T19:33:58Z description: "Explains how to register an app and how the review workflow works." --- # OAuth 2.0 app registration Registering your OAuth 2.0 app allows it to access Roblox resources. After registration, Roblox assigns the app a unique client ID and secret that you can use to obtain authorization from users to access their Roblox information and resources. > **Info:** You can only register apps for individual accounts or groups that you own. ## Register your app To register an app: 1. In the [Creator Dashboard](https://create.roblox.com/dashboard/creations), go to the [OAuth 2.0 Apps](https://create.roblox.com/dashboard/credentials?activeTab=OAuthTab) page. 2. Click the **Create App** button. 3. Enter a valid and globally unique **Application Name**. 4. Read and agree to the **Roblox Terms of Service** and click **Create**. 5. Copy and store the **Client ID** and **Secret** in a secure place. After closing the page, you no longer have access to the secret and can only generate a new one. After you finish registering an app, you can still access and edit the app's other properties. 6. Click **Continue to Edit** to configure your app, which is described in the following sections. ## Configure general information Once you've [registered](#register-your-app) your app, you should configure its general information, including: - A description, which is visible to everyone in the Roblox community. - A thumbnail image for your app. The recommended file size is at least 150x150 pixels. - An **Entry Link** as the entry point of your app, such as the app's website homepage. - A **Privacy Terms URL** and a **Terms of Service URL** for end-users to read before authorizing your app. Each URL must be HTTPS and no more than 256 characters. > **Info:** You can skip this section during app development and testing, but you must complete it during the review process to publish your app to users. ## Add permissions To ensure proper access control and security within your app, you must add **Permission Scopes**. There are two types of permission scopes available: - **Identity scopes** for authentication, such as `openid` for user IDs and `profile` for the full profiles. If you select the `profile` scope, you must also select `openid`. - **API-specific permission scopes**, such as `asset:read` for the read permission of assets. Select only the minimum number of scopes that you need for your app. > **Info:** Each app is expected to request the minimal number of scopes needed for its function. The set of scopes for an app must fall under a single app category listed in the [Creator Third Party App Policy](https://en.help.roblox.com/hc/en-us/articles/37924211313044-Creator-Third-Party-App-Policy). ## Add redirect URLs **Redirect URLs** are the reentry points of your app that users are redirected to when they finish authorizing your app. Ensure your redirect URLs meet the following requirements: - All redirect URLs must be in one of the following forms: - Plain HTTPS, for example: `https://www.example.com`. - Paths on localhost with HTTP or HTTPS (self-signed certificate) for local debugging purposes, for example: - `http://localhost:80` - `https://localhost:80` - Custom schemes for specific applications or purposes, for example: - `my-app-scheme:/` - `my-app-scheme://foo/bar/...` - `my-app://action?param1=value1¶m2=value2` - The maximum length of a redirect URL is 256 characters. - You can add up to 10 redirect URLs. With multiple redirect URLs, you can have test environments and allow users to migrate between two sites without downtime. > **Info:** You can skip this step during app registration and add the redirect URLs later when you have a testable version of your app. ## Submit for review To minimize the chances of malicious apps harming the community, registering an app doesn't make it broadly accessible. Instead, it remains in **private mode** with a limit of 10 unique users. This mode is helpful for testing and debugging within your team. If you want to extend the user base for your app, you need to publish it in public mode by submitting it for review: 1. Go to the [OAuth 2.0 apps](https://create.roblox.com/dashboard/credentials?activeTab=OAuthTab) page. 2. Click the **Edit and Publish** button for the app that you want to publish. 3. Fill in all the required information and click the **Review and Publish** button. A message with limits on your app permissions during the pending review period appears. 4. Click **Submit for Review**. While your app review is pending, you can't edit or submit another request until the current review request is approved or rejected. Your app remains in private mode during the review. When your app is approved, it transitions to public mode, and you can't revert it back to private mode. Regardless of whether your app is approved or rejected, you receive an email notification of the result when the review is completed. ## Edit and delete your app You can edit or delete your app with the following steps if it's not under review: 1. Go to the [OAuth 2.0 apps](https://create.roblox.com/dashboard/credentials?activeTab=OAuthTab) page. 2. Click **Edit** or **Delete** for the desired app. - If you chose to edit an app, follow the same steps and requirements of [registration](#register-your-app). - If you chose to delete an app, click **Yes, Delete** to confirm deletion. After you delete an app, you can't recover it. Modifying your app's general information or redirect URLs does not require users to reauthorize the app. However, if you add or modify permission scopes, you must obtain authorization for the new permissions from your users and acquire a new set of tokens. If you don't obtain authorization for the new scopes, your app can continue using the existing tokens, but is limited to the previous scopes. For apps in private mode, the system automatically updates your edits with your users. For apps in public mode, you must [submit the app for review](#submit-for-review) again to reflect these changes. Otherwise, the system saves the unpublished changes for you without releasing it to the community. > **Info:** You can clone a copy of your public app and keep it in private mode for iterative testing and debugging. This allows for a controlled environment where you can safely make changes and resolve any issues before submitting the app for review. --- title: "OAuth 2.0 sample app" url: /docs/en-us/cloud/auth/oauth2-sample last_updated: 2026-06-29T19:33:58Z description: "Provides a working OAuth 2.0 sample application." --- # OAuth 2.0 sample app Roblox provides a Node.js sample app that shows how to use OAuth 2.0 to let users log in to their Roblox accounts and broadcast messages across their experiences. This app uses the [authorization code flow _without_ PKCE](/docs/en-us/cloud/auth/oauth2-overview.md#grant-types) and is thus only suitable for confidential clients, such as private servers. After downloading the `.zip` file, extract it to its own folder. ## Register the app The first step to setting up the app is to [register it on the Roblox website](/docs/en-us/cloud/auth/oauth2-registration.md) and copy the client ID and secret somewhere safe. Then follow the standard registration steps with these settings: 1. Under **Permissions**, add the `openid`, `profile`, and `universe-messaging-service:publish` scopes. 2. Under **Redirect URLs**, add the `http://localhost:3000/oauth/callback` redirect. If you want to use a non-default port for your app, specify it here. ## Set environment variables Rather than storing them in code (not recommended), the app uses environment variables for your client ID and secret. The process for adding environment variables differs by operating system. On Windows, run the following PowerShell commands: ```powershell $env:ROBLOX_CLIENT_ID='your_client_id_here' $env:ROBLOX_CLIENT_SECRET='your_client_secret_here' $env:ROBLOX_PORT=3000 # Optional. Default is 3000. ``` On Mac and most Linux distributions, run these commands at the terminal: ```bash export ROBLOX_CLIENT_ID=your_client_id_here export ROBLOX_CLIENT_SECRET=your_client_secret_here export ROBLOX_PORT=3000 # Optional. Default is 3000. ``` If you specified a non-default port in the redirect URL when registering your app, be sure to add the `ROBLOX_PORT` variable. > **Info:** These commands only set environment variables for the duration of your terminal session. If you want to load these variables every time you open a new terminal window, add the commands to your shell configuration file, such as `.zshrc` or `.bashrc`. Many hosting services have features to help you add environment variables to your servers. ## Install dependencies The app has a handful of dependencies, visible in `package.json`. To install them, run: ```bash npm ci ``` ## Run the app To start the app, run: ```bash npm start ``` Then navigate to `http://localhost:3000` (or your non-default port) in a web browser. The app immediately redirects you to the Roblox login screen, at which point you can log in, verify the permissions that the app is requesting, and click **Confirm and Give Access**. Roblox then redirects you back to `localhost`, where you can see that the app now displays some minimal user information, a link back to your profile on Roblox, and fields that you can use to broadcast messages across your experiences. ### About the app In broad strokes, `index.js` performs the following operations: 1. Starts a new web server using `express`. 2. Retrieves the Roblox OpenID Connect (OIDC) configuration, which includes endpoints for authorization, user information, etc. For more information about these endpoints, see [OAuth 2.0 authentication](/docs/en-us/cloud/auth/oauth2-reference.md). 3. Creates a new Open ID client using `openid-client` and your stored credentials. This client dramatically simplifies the process of properly forming and sending HTTP requests to the OAuth 2.0 endpoints. 4. Defines the routes for the app, including redirects for the login and logout flows and the OAuth 2.0 callback. 5. After a successful login, stores the various tokens as cookies, along with some minimal user information that it displays as HTML with help from `getHomeHtml.js`. --- title: "Experience configs" url: /docs/en-us/cloud/guides/configs last_updated: 2026-06-29T19:33:58Z description: "Use Open Cloud to read and update experience configs programmatically. This guide walks through creating configs, publishing, verifying changes, and rolling back." --- # Experience configs The configs API lets you manage [experience configs](/docs/en-us/production/configs.md) programmatically rather than using Creator Hub or Studio. This guide covers a typical flow: - Create or update a draft config. - Publish it. - Get the latest config values. - Roll back to a prior value using version history. Values you publish are available in your experience right away. In server scripts, use `Class.ConfigService` to read configs and react to real-time updates. `Class.ConfigService:GetConfigAsync()` and `Class.ConfigSnapshot:GetValue()` let you read values, and `Class.ConfigSnapshot.UpdateAvailable` with `Class.ConfigSnapshot:Refresh()` or `Class.ConfigSnapshot:GetValueChangedSignal()` lets you respond when values change. For Luau code samples and further details, see [Add configs to your code](/docs/en-us/production/configs.md#add-configs-to-your-code) and [Refresh snapshots](/docs/en-us/production/configs.md#refresh-snapshots) in the experience configs guide. Before you start, [generate an API key](/docs/en-us/cloud/auth/api-keys.md) or [configure an OAuth 2.0 app](/docs/en-us/cloud/auth/oauth2-overview.md). Read operations require the `universe:read` scope. Write operations (drafts, publish, restore) need `universe:write`. All endpoints use your universe ID, which you can find on the [Creator Dashboard](https://create.roblox.com/dashboard/creations). Click the experience tile overflow menu and **Copy Universe ID**. For the full endpoint reference, request and response schemas, and error codes, see the [Cloud API reference](/docs/en-us/cloud/reference/features/configs.md). ## Repositories Requests to the configs endpoints use a **repository** in the path and an **entries** object in the request body. For example, this request adds a draft config: ```json PATCH /creator-configs-public-api/v1/configs/universes//repositories//draft { "entries": { "enableNewTutorial": true } } ``` - **Repositories** differ by product and separate configs by system. - **Entries** are the config key-value pairs. You send and receive a single flat object of keys and values. This guide covers in-experience configs, so all requests use `InExperienceConfig` for repository. Repositories will eventually expand to cover additional products and use cases. | Use case | Repository | | --- | --- | | In-experience configs | `InExperienceConfig` | ## Create or update configs Before going live, config changes are staged as drafts. You can either set the entire config in one operation or apply partial updates until you're happy with it: - **Starting from scratch or replacing everything** — Use the overwrite endpoint so that the payload is the full desired state. Any key you omit is treated as removed. - **Changing only some keys** — Use the partial update endpoint so only the keys you send are updated; everything else stays as-is. This sample code sets `bossHealth` to `100` and uses the overwrite endpoint: #### Python ```python import requests API_KEY = "" UNIVERSE_ID = "" BASE = f"https://apis.roblox.com/creator-configs-public-api/v1/configs/universes/{UNIVERSE_ID}/repositories/InExperienceConfig" headers = {"x-api-key": API_KEY, "Content-Type": "application/json"} # Optional: send previousDraftHash if you have an existing draft and want optimistic concurrency payload = { "entries": { "bossHealth": 100 } } r = requests.put(f"{BASE}/draft:overwrite", headers=headers, json=payload) r.raise_for_status() draft = r.json() draft_hash = draft["draftHash"] # Keep this for publishing print("Draft staged. draftHash:", draft_hash) ``` #### cURL ```bash curl --request PUT \ "https://apis.roblox.com/creator-configs-public-api/v1/configs/universes//repositories/InExperienceConfig/draft:overwrite" \ --header "x-api-key: " \ --header "Content-Type: application/json" \ --data '{"entries":{"bossHealth":100}}' ``` The response includes the `draftHash` value. You need this hash in order to publish. If you prefer to only tweak a few keys in your draft, use the PATCH method on the `/draft` endpoint and only send the entries (keys) you want to change. ### About draft hashes A **draft hash** is an opaque identifier for the current state of your draft. The API returns it after any read or write operation to a draft (create, update, overwrite, reset, restore). When updating or publishing drafts, you can send `previousDraftHash` in the request body. If the hash doesn't match the server's current draft (e.g. someone else published or edited in the meantime), the request fails. You can then re-read the updated draft, get the new hash, and retry or merge changes. Always save the `draftHash` from the last draft response before calling publish or before making another draft change if you use `previousDraftHash`. ## Publish your changes When you're happy with the draft, publish it so the new values go live. Pass `draftHash` from the draft response, `message` for version history purposes, and `deploymentStrategy` (either `GradualRollout` for a 15-minute rollout or `Immediate`). The following sample code deploys the config immediately: #### Python ```python publish_payload = { "draftHash": draft_hash, "message": "Set boss health to 100", "deploymentStrategy": "Immediate" } r = requests.post(f"{BASE}/publish", headers=headers, json=publish_payload) r.raise_for_status() result = r.json() print("Published. configVersion:", result["configVersion"]) ``` #### cURL ```bash # Use the draftHash from the previous step curl --request POST \ "https://apis.roblox.com/creator-configs-public-api/v1/configs/universes//repositories/InExperienceConfig/publish" \ --header "x-api-key: " \ --header "Content-Type: application/json" \ --data '{ "draftHash": "", "message": "Set boss health to 100", "deploymentStrategy": "Immediate" }' ``` After a successful publish, the draft is cleared, and your experience receives the new config according to the deployment strategy. The response includes the new config version: ```json { "configVersion": 6 } ``` ## Get your published config To confirm the change went through, fetch the latest published config. Use the values-only endpoint when you only need keys and values; use the full endpoint when you also need metadata (descriptions, last-accessed time, etc.). This sample code uses the values-only endpoint, with the full endpoint commented out: #### Python ```python # Values only (same shape RCC and your game see) r = requests.get(BASE, headers=headers) r.raise_for_status() config = r.json() boss_health = config["entries"].get("bossHealth") print("Published bossHealth:", boss_health) # Should be 100 # Or with full metadata: # r = requests.get(f"{BASE}/full", headers=headers) ``` #### cURL ```bash curl --location \ "https://apis.roblox.com/creator-configs-public-api/v1/configs/universes//repositories/InExperienceConfig" \ --header "x-api-key: " ``` Verify that `entries.bossHealth` (or your key) matches what you published. ## View history and roll back Every publish creates a revision, just like a version control system. You can list revisions to see who changed what and when, then restore to a previous revision if you need to roll back. This sample code calls the revisions endpoint and prints the results. Each revision includes `revisionId`, `version`, `time`, `publishedBy`, `message`, and `changes` (the key, the "before" value, and the "after" value). #### Python ```python r = requests.get(f"{BASE}/revisions", headers=headers, params={"MaxPageSize": 10}) r.raise_for_status() data = r.json() for rev in data["revisions"]: print(rev["version"], rev["time"], rev["message"], "— revisionId:", rev["revisionId"]) ``` #### cURL ```bash curl --location \ "https://apis.roblox.com/creator-configs-public-api/v1/configs/universes//repositories/InExperienceConfig/revisions?MaxPageSize=10" \ --header "x-api-key: " ``` To roll back, call the restore endpoint with the **revisionId** you want to revert to. That clears the current draft and stages a revert commit; it does **not** publish. To publish, you must call `publish` with the returned `draftHash`, just like publishing a new change. #### Python ```python REVISION_ID = "1234567890" # From the list above r = requests.post(f"{BASE}/revisions/{REVISION_ID}/restore", headers=headers) r.raise_for_status() restore_draft = r.json() restore_hash = restore_draft["draftHash"] # Publish the revert publish_payload = { "draftHash": restore_hash, "message": "Rollback to previous config", "deploymentStrategy": "Immediate" } r = requests.post(f"{BASE}/publish", headers=headers, json=publish_payload) r.raise_for_status() print("Rollback published. configVersion:", r.json()["configVersion"]) ``` #### cURL ```bash # 1) Restore (returns a new draftHash) curl --request POST \ "https://apis.roblox.com/creator-configs-public-api/v1/configs/universes//repositories/InExperienceConfig/revisions//restore" \ --header "x-api-key: " # 2) Publish the reverted draft using the draftHash from the response curl --request POST \ "https://apis.roblox.com/creator-configs-public-api/v1/configs/universes//repositories/InExperienceConfig/publish" \ --header "x-api-key: " \ --header "Content-Type: application/json" \ --data '{"draftHash":"","message":"Rollback","deploymentStrategy":"Immediate"}' ``` ## Limitations | Limit | Maximum | | --- | --- | | **Keys per repository** | 100 | | **Key length** | 256 characters | Requests that exceed these limits will fail. For type-specific value limits (e.g. string vs JSON) in your experience, see [Limits](/docs/en-us/production/configs.md#limits) in the experience configs guide. For full details on all the endpoints in this guide, see the [Cloud API reference](/docs/en-us/cloud/reference.md). --- title: "Open Cloud data stores" url: /docs/en-us/cloud/guides/data-stores last_updated: 2026-06-29T19:33:58Z description: "Explains how to use Open Cloud APIs to access and modify data stores." --- # Open Cloud data stores In addition to accessing data stores from the Engine API in Studio or live experiences (`Class.DataStoreService`), you can use the Open Cloud APIs to access [standard](/docs/en-us/cloud/reference/DataStoreEntry.md) and [ordered data stores](/docs/en-us/cloud/reference/OrderedDataStoreEntry.md) from external scripts and other tools. Open Cloud access to your data stores unlocks many potential use cases, including: - A customer support portal that lets your team directly handle support requests, such as modifying user inventories or issuing refunds - Global leaderboards that you can display on an external website - Schema updates with scripts that read entries from the current data store, map it to the new schema, and write entries back to a new data store The examples on this page demonstrate how to build a [user inventory support portal](#user-inventory-support-portal) and an [external leaderboard](#external-persistent-leaderboard)) with Node.js and Python, but use whichever language you prefer; the Open Cloud APIs support any programming language that can send an HTTP request. ## Differences from the Engine API Although the Open Cloud APIs access the same underlying data stores and are similar to working with `Class.DataStoreService`, there are a few key differences: - **Universe ID**: Unlike the Engine API, Open Cloud APIs are stateless and can come from anywhere, so you must always provide the **universe ID**, the unique identifier of your experience. - **Separate permissions for creating and updating**: The Engine API creates new entries if they don't exist when you call `Class.DataStore:SetAsync()`, but Open Cloud methods for creating and updating entries are separate. Separate permissions can be safer and more flexible in certain situations. For example, you might want your customer support tool to only be able to edit an existing user's profile, not create a new one. - **Data serialization**: All Open Cloud endpoints require you to serialize data before sending it. Serialization means converting an object into a string. Deserialization is the opposite, converting a string into an object. The Engine API serializes and deserializes entry content automatically, but for Open Cloud, you must generate or parse your entry to and from JSON on your own. ## Permissions Data stores often store sensitive information, such as user profiles and virtual currency. To maintain security, each Open Cloud method has corresponding permissions, called **scopes**, that you must add to your API key, such as the `universe-datastores.control:list` scope for the [List Data Stores](/docs/en-us/cloud/reference/DataStore.md#Cloud_ListDataStores) method. If you don't add the required permissions, your API call returns an error. See the reference documentation for the required scopes for each endpoint. For more information on managing permissions, see [Manage API keys](/docs/en-us/auth/api-keys.md). ## User inventory support portal This example uses a data store named `Inventory` and a schema for each entry of `"userId": {"currency": number, "weapon": string, "level": number}`. The key is `userId`. ### Required scopes When [creating an API Key](/docs/en-us/auth/api-keys.md) for this example, add the following scopes to your key: - `universe-datastores.objects:list` - `universe-datastores.objects:read` - `universe-datastores.objects:update` Optionally, add the permissions for only the `Inventory` data store, set an IP address restriction, and set an expiration date. ### Add scripts for the user inventory support portal After creating the API key with permissions required for the example app, you can create a script to make requests to the endpoints. These scripts get the first 10 entries in the data store, increment the `currency` value for each by 10, and then update each entry. For a larger data store, you would need to deal with [pagination](/docs/en-us/reference/patterns.md#pagination) using the `maxPageSize` and `pageToken` query parameters. #### Node.js ```javascript const apiKey = process.env.API_KEY; if (!apiKey) { throw new Error('The API_KEY environment variable is not set.'); } const apiHeaderKey = 'x-api-key'; const universeId = ''; const dataStoreId = 'Inventory'; const baseUrl = 'https://apis.roblox.com/cloud/v2/'; async function listEntries(universe, dataStore) { const listPath = \`universes/${universe}/data-stores/${dataStore}/entries\`; const url = baseUrl + listPath; const response = await fetch(url, { headers: { [apiHeaderKey]: apiKey } }); return response.json(); } async function getEntry(path) { const url = baseUrl + path; const response = await fetch(url, { headers: { [apiHeaderKey]: apiKey } }); return response.json(); } async function updateEntry(path, payload) { const url = baseUrl + path; const response = await fetch(url, { method: 'PATCH', headers: { [apiHeaderKey]: apiKey, 'Content-Type': 'application/json' }, body: JSON.stringify(payload) // The body must be a string }); return response; } (async () => { try { const entries = await listEntries(universeId, dataStoreId); for (const entry of entries.dataStoreEntries) { const path = entry.path; console.log(\`\nProcessing entry: ${path}\`); const currentData = await getEntry(path); currentData.value.currency += 10; const payload = { value: currentData.value }; const updateResponse = await updateEntry(path, payload); console.log(\`Status: ${updateResponse.status}\`); console.log(\`Response: ${await updateResponse.text()}\`); } } catch (error) { console.error('An error occurred during execution:', error); } })(); ``` To test, set the `API_KEY` environment variable, install dependencies, and run the script: ```bash export API_KEY= node incrementCurrency.js ``` #### Python ```python import json import os import requests apiKey = str(os.environ['API_KEY']) apiHeaderKey = 'x-api-key' universe_id = '' data_store_id = 'Inventory' base_url = 'https://apis.roblox.com/cloud/v2/' def list_entries(universe, data_store): list_path = f'universes/{universe}/data-stores/{data_store}/entries' url = base_url + list_path return requests.get(url, params={}, headers={apiHeaderKey: apiKey}) def get_entry(path): url = base_url + path return requests.get(url, params={}, headers={apiHeaderKey: apiKey}) def update_entry(path, payload): url = base_url + path return requests.patch(url, params={}, headers={apiHeaderKey: apiKey, 'Content-Type': 'application/json'}, data=payload) entries = list_entries(universe_id, data_store_id).json() for entry in entries['dataStoreEntries']: path = entry['path'] get_response = get_entry(path).json() get_response['value']['currency'] += 10 json_payload = json.dumps({'value': get_response['value']}) update_response = update_entry(path, json_payload) print(update_response.status_code) print(update_response.text) print() ``` To test, set the `API_KEY` environment variable, install dependencies, and run the script: ```bash export API_KEY= python3 increment_currency.py ``` ## External persistent leaderboard This example creates a predefined list of users for demo purposes, but for it to be useful in a real experience, you would need an actual data store of users. ### Required scopes When [creating an API Key](/docs/en-us/auth/api-keys.md) for this example, add the following scopes to your key: - `universe.ordered-data-store.scope.entry:read` - `universe.ordered-data-store.scope.entry:write` ### Add scripts for the leaderboard After creating the API key with permissions required for the example app, you can create a script to make requests to the endpoints. These scripts add some sample entries to the ordered data store with random numbers and then retrieve them from highest to lowest value. For a larger data store, you would need to deal with [pagination](/docs/en-us/reference/patterns.md#pagination) using the `maxPageSize` and `pageToken` query parameters. #### Node.js ```javascript const apiKey = process.env.API_KEY; const apiHeaderKey = 'x-api-key'; const universeId = ''; const orderedDataStoreId = 'PlayerScores'; const scopeId = 'global'; const baseUrl = 'https://apis.roblox.com/cloud/v2/'; async function createOrderedEntry(universe, orderedDataStore, entryId, payload) { const createPath = \`universes/${universe}/ordered-data-stores/${orderedDataStore}/scopes/${scopeId}/entries\`; const url = new URL(baseUrl + createPath); url.searchParams.append('id', entryId); const response = await fetch(url, { method: 'POST', headers: { [apiHeaderKey]: apiKey, 'Content-Type': 'application/json', }, body: JSON.stringify(payload), }); if (!response.ok) { const errorText = await response.text(); throw new Error(\`API Error (${response.status}): ${errorText}\`); } return response.text(); } async function listOrderedEntries(universe, orderedDataStore) { const listPath = \`universes/${universe}/ordered-data-stores/${orderedDataStore}/scopes/${scopeId}/entries\`; const url = new URL(baseUrl + listPath); url.searchParams.append('orderBy', 'value desc'); return fetch(url, { headers: { [apiHeaderKey]: apiKey, }, }); } async function main() { if (!apiKey) { console.error('Error: The API_KEY environment variable is not set.'); process.exit(1); } const entryNames = ['Ragdoll', 'Balinese', 'Tabby', 'Siamese']; console.log('Creating sample data...'); for (const name of entryNames) { try { const randomValue = Math.floor(Math.random() * 50) + 1; const payload = { value: randomValue }; const responseText = await createOrderedEntry(universeId, orderedDataStoreId, name, payload); console.log(responseText); } catch (error) { console.error(\`Failed to create entry for "${name}": ${error.message}\`); } } console.log('\nGetting sorted list of entries...'); try { const playerScoresResponse = await listOrderedEntries(universeId, orderedDataStoreId); console.log(playerScoresResponse.status); const responseText = await playerScoresResponse.text(); console.log(responseText); } catch (error) { console.error(\`Failed to list entries: ${error.message}\`); } } main(); ``` To test, set the `API_KEY` environment variable, install dependencies, and run the script: ```bash export API_KEY= node leaderboard.js ``` #### Python ```python import json import os import random import requests apiKey = str(os.environ['API_KEY']) apiHeaderKey = 'x-api-key' universe_id = '' ordered_data_store_id = 'PlayerScores' scope_id = 'global' base_url = 'https://apis.roblox.com/cloud/v2/' def create_ordered_entry(universe, ordered_data_store, entry_id, payload): create_path = f'universes/{universe}/ordered-data-stores/{ordered_data_store}/scopes/{scope_id}/entries' url = base_url + create_path return requests.post(url, params={'id': entry_id}, headers={apiHeaderKey: apiKey, 'Content-Type': 'application/json'}, data=payload) def list_ordered_entries(universe, ordered_data_store): list_path = f'universes/{universe}/ordered-data-stores/{ordered_data_store}/scopes/{scope_id}/entries' url = base_url + list_path return requests.get(url, params={'orderBy': 'value desc'}, headers={apiHeaderKey: apiKey}) # Create sample data entryNames = ["Ragdoll", "Balinese", "Tabby", "Siamese"] for name in entryNames: random_value = random.randint(1, 50) json_payload = json.dumps({'value': random_value}) create_response = create_ordered_entry(universe_id, ordered_data_store_id, name, json_payload) print(create_response.text) # Get list player_scores = list_ordered_entries(universe_id, ordered_data_store_id) print(player_scores.status_code) print(player_scores.text) ``` To test, set the `API_KEY` environment variable, install dependencies, and run the script: ```bash export API_KEY= python3 leaderboard.py ``` --- title: "Handle Open Cloud data store requests" url: /docs/en-us/cloud/guides/data-stores/request-handling last_updated: 2026-06-29T19:33:58Z description: "Explains how to handle Open Cloud API requests for data stores." --- # Handle Open Cloud data store requests There are a number of considerations around sending requests to and handling responses from the Open Cloud data store APIs. ## HTTP headers Like all Open Cloud endpoints, data store requests that use API key authentication must include the `x-api-key` header. For more information, see [Manage API keys](/docs/en-us/cloud/auth/api-keys.md). - An incorrect or missing API key results in a 401 error. - A valid API key that lacks the correct permissions results in a 403 error. If a request includes a body, such as a create or update request, include the `Content-Type: application/json` and `Content-Length` headers. Most clients include these headers automatically. ## Pagination Data stores often contain many entries, which means your code, particularly when calling [List Data Store Entries](/docs/en-us/cloud/reference/DataStoreEntry.md#Cloud_ListDataStoreEntries__Using_Universes), must deal with a maximum number of results and pages. To learn more, see [Pagination](/docs/en-us/cloud/reference/patterns.md#pagination). ## Filters If you use prefixes in the names of your data store entries, you likely want to filter your GET calls, like so: ```python def list_entries(universe, data_store, filter): list_path = f'universes/{universe}/data-stores/{data_store}/entries' url = base_url + list_path return requests.get(url, params={'filter': f'id.startsWith("global/{filter}")'}, headers={apiHeaderKey: apiKey}) ``` Filter syntax is extremely limited. See the reference documentation for available filters for [standard](/docs/en-us/cloud/reference/DataStoreEntry.md#Cloud_ListDataStoreEntries__Using_Universes) and [ordered](/docs/en-us/cloud/reference/OrderedDataStoreEntry.md#Cloud_ListOrderedDataStoreEntries) data stores. ## Allow missing flags The PATCH (update) methods for standard and ordered data stores have an `allow_missing` parameter. If set to true, this parameter lets you create entries that don't already exist rather than throwing an error. Leave this parameter at its default value of false unless you have a good reason not to; updating an entry that was never created often represents an error in programming logic. ## Scopes Many requests require you to specify a scope, even if it's the default value of `global`. These scopes are distinct from permission scopes and let you subdivide and organize the entries in a data store. Rather than creating non-default scopes, however, we strongly recommend using prefixes. To learn more, see [Listing and prefixes](/docs/en-us/cloud-services/data-stores/versioning-listing-and-caching.md#listing-and-prefixes). ## Content-MD5 > **Info:** This section only applies to the Open Cloud v1 API. The Open Cloud v1 API supports an optional base64-encoded MD5 checksum in some requests, which can catch data integrity issues. Many languages have built-in functions to calculate the value of the `content-md5` header. The following example uses Python: ```python $ echo "750" | python -c "import base64, hashlib; print(str(base64.b64encode(hashlib.md5(bytes(input(), encoding='utf8')).digest()), encoding='utf8'))" sTf90fedVsft8zZf6nUg8g== ``` If you run into issues generating a valid `content-md5` value, you might need to encode your request body in UTF-8 before computing the checksum. --- title: "Rate limits and throttling" url: /docs/en-us/cloud/guides/data-stores/throttling last_updated: 2026-06-29T19:33:58Z description: "Covers rate limits for the Open Cloud v2 Data Stores API." --- # Rate limits and throttling The [Data Stores API](/docs/en-us/cloud/reference/DataStore.md) enforces two types of throttling: **requests per minute** and **throughput**. Each experience allows a certain number of requests per minute and a certain amount of data per minute. These limits are enforced for the universe regardless of the number of API keys used. Unlike the Engine API, these limits do **not** scale based on user count. Exceeding either one causes the endpoint to return `429 Too Many Requests`. > **Info:** Data store rate limits for the [Open Cloud v1](/docs/en-us/reference/cloud/datastores-api/v1.md) and [Open Cloud v2](/docs/en-us/cloud/reference/DataStore.md) APIs are identical. ## Standard data stores throttling limits | Request type | Method | Throttle limits | | --- | --- | --- | | Write | | | | Read | | | | Delete | | | ## Ordered data stores throttling limits | Request type | Method | Throttle limits | | --- | --- | --- | | Write | | | | Read | | | --- title: "User notifications" url: /docs/en-us/cloud/guides/experience-notifications last_updated: 2026-06-29T19:33:58Z description: "Use Open Cloud to send experience notifications to users. Help players keep up with their favorite experiences through timely, personalized notifications." --- # User notifications **Experience notifications** are a way for [opted-in](https://en.help.roblox.com/hc/en-us/articles/24769602332692-Out-of-Experience-Notifications) users age 13+ to keep up with their favorite experiences through timely, personalized notifications. As the developer, you can determine what kinds of in‑experience activities are most important to notify your users about, as well as define the notification content. #### Async Activity ![Example notification](../../assets/open-cloud/experience-notifications/Example-Notification-A.png) ![Example notification](../../assets/open-cloud/experience-notifications/Example-Notification-D.png) #### Progress / Achievement ![Example notification](../../assets/open-cloud/experience-notifications/Example-Notification-C.png) ![Example notification](../../assets/open-cloud/experience-notifications/Example-Notification-E.png) #### User Mentions ![Example notification](../../assets/open-cloud/experience-notifications/Example-Notification-B.png) ![Example notification](../../assets/open-cloud/experience-notifications/Example-Notification-F.png) After they receive a notification, users can join the experience directly via the **Join** button and spawn according to your [launch data](#include-launch-and-analytics-data). For more information on features, eligibility requirements, usage guidelines, and the corresponding Engine API, see the [Experiences guide](/docs/en-us/production/promotion/experience-notifications.md). ## Implementation The [UserNotification](/docs/en-us/cloud/reference/UserNotification.md) resource lets you send experience notifications to users. Before using it, you must [generate an API key](/docs/en-us/cloud/auth/api-keys.md) or [configure OAuth 2.0](/docs/en-us/cloud/auth/oauth2-overview.md) for your app. The examples on this page use API keys. To send an experience notification to a user: 1. [Create a notification string](/docs/en-us/production/promotion/experience-notifications.md#create-a-notification-string) in the [Creator Dashboard](https://create.roblox.com/dashboard/creations) (this step must be done in the Creator Dashboard; there's no Open Cloud API for it). 2. Form the request: 1. Copy the API key to the `x-api-key` request header. 2. Copy the notification string asset ID as the value of the `payload.message_id` property. 3. Set `payload.type` to `"MOMENT"`. 4. Set `source.universe` to be the universe resource URL `"universes/${UniverseID}"`. ```bash curl --location 'https://apis.roblox.com/cloud/v2/users/${UserId}/notifications' \ --header 'x-api-key: ${ApiKey}' \ --header 'Content-Type: application/json' \ --data '{ "source": { "universe": "universes/${UniverseID}" }, "payload": { "message_id": "${AssetID}", "type": "MOMENT" } }' ``` Example response which returns the notification ID in the `id` field: ```json { "path": "users/505306092/notifications/6ca4d981-36fa-4255-82a1-14d95c116889", "id": "6ca4d981-36fa-4255-82a1-14d95c116889" } ``` ### Customize notifications using parameters To customize the notification for each recipient, include **parameters** in the [notification string](#implementation). Then customize the parameters when calling the API. For example, you can define the notification string as: **{userId-friend} beat your high score by {points} points! Time to level up?** Add the `userId-friend` and `points` parameters in the script: ```bash curl --location 'https://apis.roblox.com/cloud/v2/users/${UserId}/notifications' \ --header 'x-api-key: ${ApiKey}' \ --header 'Content-Type: application/json' \ --data '{ "source": { "universe": "universes/${UniverseID}" }, "payload": { "message_id": "${AssetID}", "type": "MOMENT", "parameters": { "userId-friend": {"int64_value": 3702832553}, "points": {"string_value": "5"} } } }' ``` ### Include launch and analytics data To further improve user experience, you can include **launch data** in the notification, useful for scenarios such as routing users to a coordinate location or personalizing the joining experience. Additionally, you can include [analytics](/docs/en-us/production/promotion/experience-notifications.md#analytics) data to segment the performance of different categories of notifications. ```bash curl --location 'https://apis.roblox.com/cloud/v2/users/${UserId}/notifications' \ --header 'x-api-key: ${ApiKey}' \ --header 'Content-Type: application/json' \ --data '{ "source": { "universe": "universes/${UniverseID}" }, "payload": { "message_id": "${AssetID}", "type": "MOMENT" }, "join_experience": { "launch_data": "Test_Launch_Data" }, "analytics_data": { "category": "Test_Analytics_Category" } }' ``` ## Rate limits and delivery Each user can receive **one** notification per day from a given experience, and you receive transparent feedback when a user's throttle limit is reached. There are many other reasons that a notification might not be delivered. For more information, see [Delivery system](/docs/en-us/production/promotion/experience-notifications.md#delivery-system) in the Engine guide. --- title: "Open Cloud guides" url: /docs/en-us/cloud/guides last_updated: 2026-06-29T19:33:58Z description: "In addition to using Engine and Studio tools to create experiences on Roblox, you can automate your internal workflows, improve your efficiency creating content, and support your experience operation needs from the web." --- # Open Cloud guides The Open Cloud guides provide detailed walkthroughs for popular Open Cloud use cases, such as [retrieving user inventories](/docs/en-us/cloud/inventory.md), [sending announcements](/docs/en-us/cloud/usage-messaging.md), or [using webhooks](/docs/en-us/webhooks/webhook-notifications.md) to notify your apps of certain events. Guides often include sample code in Python or Node.js and are a great way to get started, understand a resource, and spark ideas for your own usage. For a comprehensive summary of available resources, see the Open Cloud API reference. --- title: "Engine instances" url: /docs/en-us/cloud/guides/instance last_updated: 2026-06-29T19:33:58Z description: "Explains how to use Open Cloud APIs to access the Roblox Engine Instances." --- # Engine instances The Engine Open Cloud APIs let you manage `Class.Instance` objects in your Roblox experiences from the web. ## Beta restrictions These APIs are currently in beta and have the following restrictions: - You can only read and update `Class.Script`, `Class.LocalScript`, and `Class.ModuleScript` objects. - You can't update scripts that are currently open in Roblox Studio. - You can't update scripts that are part of a [package](/docs/en-us/projects/assets/packages.md). - You can only use API key authentication. Create an [API key](/docs/en-us/cloud/auth/api-keys.md) with **universe-place-instances** added as an API system. You must specify the experiences you want the key to have access to as well as the desired read and write permission scopes.![The Creator Hub API key screen](../../assets/open-cloud/instance-api.png) - You must have a [collaborative](/docs/en-us/projects/collaboration.md) session enabled for the experience that you want to access. - Request bodies, such as to [Update Instance](/docs/en-us/cloud/reference/Instance.md#Cloud_UpdateInstance), are limited to 200 KB. ## List children List all children of a specific instance by specifying an instance ID and calling the [List Instance Children](/docs/en-us/cloud/reference/Instance.md#Cloud_ListInstanceChildren) method. > **Info:** The first time you use these methods, specify `root` for the instance ID. After you receive the list of top-level children and their IDs, you can make additional calls to find instances deeper within your place's instance hierarchy. #### Python ```python import requests # Generate at https://create.roblox.com/dashboard/credentials apiKey = "" # Find at https://create.roblox.com/dashboard/creations in the overflow menu of an experience tile universeId = "" # Find Start Place ID at https://create.roblox.com/dashboard/creations in the overflow menu of an experience tile placeId = "" # The default ID for the root of any place's data model instanceId = "root" # Request header apiKeyHeaderKey = "x-api-key" # Endpoint URL for List Children method listChildrenUrl = "https://apis.roblox.com/cloud/v2/universes/%s/places/%s/instances/%s:listChildren" def ListChildren(): url = listChildrenUrl % (universeId, placeId, instanceId) headerData = {apiKeyHeaderKey: apiKey} results = requests.get(url, headers = headerData) return results response = ListChildren() print("Operation Results:", response.status_code, response.text) # Parse the Operation object's path to later obtain the Instance resource. See the Polling for Results section for more information. operationPath = response.json()['path'] ``` #### cURL ```bash curl --include --location --request GET "https://apis.roblox.com/cloud/v2/universes//places//instances/:listChildren" --header "x-api-key: " ``` Rather than just listing the children, the response includes an `Operation` object with a different endpoint. [Poll this endpoint](#poll-for-results) to asynchronously retrieve the actual list of children. A more complete code sample looks like this: ```python import requests import time apiKey = "" universeId = "" placeId = "" instanceId = "root" apiKeyHeaderKey = "x-api-key" listChildrenUrl = "https://apis.roblox.com/cloud/v2/universes/%s/places/%s/instances/%s:listChildren" getOperationUrl = "https://apis.roblox.com/cloud/v2/%s" numberOfRetries = 10 retryPollingCadence = 5 doneJSONKey = "done" def ListChildren(): url = listChildrenUrl % (universeId, placeId, instanceId) headerData = {apiKeyHeaderKey: apiKey} results = requests.get(url, headers = headerData) return results def GetOperation(operationPath): url = getOperationUrl % (operationPath) headerData = {apiKeyHeaderKey: apiKey} results = requests.get(url, headers = headerData) return results def PollForResults(operationPath): currentRetries = 0 while (currentRetries < numberOfRetries): time.sleep(retryPollingCadence) results = GetOperation(operationPath) currentRetries += 1 if (results.status_code != 200 or results.json()[doneJSONKey]): return results response = ListChildren() print("Operation Results:", response.status_code, response.text) # Parse the Operation object's path to use in polling for the instance resource. operationPath = response.json()['path'] response = PollForResults(operationPath) print("Response:", response.status_code, response.text) ``` The final response might look something like this: ```json { "path": "universes/1234567890/places/98765432109/instances/root/operations/a1a2a3a4-a1a2-a1a2-a1a2-a1a2a3a4a5a6", "done": true, "response": { "@type": "type.googleapis.com/roblox.open_cloud.cloud.v2.ListInstanceChildrenResponse", "instances": [ { "path": "universes/1234567890/places/98765432109/instances/b1b2b3b4-b1b2-b1b2-b1b2-b1b2b3b4b5b6", "hasChildren": true, "engineInstance": { "Id": "b1b2b3b4-b1b2-b1b2-b1b2-b1b2b3b4b5b6", "Parent": "a1a2a3a4-a1a2-a1a2-a1a2-a1a2a3a4a5a6", "Name": "Workspace", "Details": {} } }, ... ] } } ``` You can then iterate over the JSON to find a particular instance ID: ```python instances = response.json()['response']['instances'] replicatedStorageId = "" for i in instances: if i['engineInstance']['Name'] == "ReplicatedStorage": replicatedStorageId = i['engineInstance']['Id'] if replicatedStorageId: # You now have an instance ID and can get or update it. else: # The name wasn't found. ``` Scripts contain some additional information in the `Details` object, such as the script type, source, and whether they're enabled. ## Get an instance This method returns a single [Instance](/docs/en-us/cloud/reference/Instance.md). #### Python ```python import requests # Generate at https://create.roblox.com/dashboard/credentials apiKey = "" # Find at https://create.roblox.com/dashboard/creations in the overflow menu of an experience tile universeId = "" # Find Start Place ID at https://create.roblox.com/dashboard/creations in the overflow menu of an experience tile placeId = "" # The default ID for the root of any place's data model instanceId = "" # Request header apiKeyHeaderKey = "x-api-key" # Endpoint URL for Get Instance method getInstanceUrl = "https://apis.roblox.com/cloud/v2/universes/%s/places/%s/instances/%s" def GetInstance(): url = getInstanceUrl % (universeId, placeId, instanceId) headerData = {apiKeyHeaderKey: apiKey} return requests.get(url, headers = headerData) response = GetInstance() print("Response:", response.status_code, response.text) # Parse the Operation object's path from response. See the Polling for Results section for more information. operationPath = response.json()['path'] ``` #### cURL ```bash curl --include --location --request GET "https://apis.roblox.com/cloud/v2/universes//places//instances/" --header "x-api-key: " ``` Just like the List Instance Children method, the response includes an `Operation` object that you poll to retrieve the actual instance. See [Poll for results](#poll-for-results) for more information. ## Update instances After you obtain the appropriate instance ID, you can update it. [Poll for results](#poll-for-results) after making the initial update request. #### Python ```python import json import requests # Generate at https://create.roblox.com/dashboard/credentials apiKey = "" # Find at https://create.roblox.com/dashboard/creations in the overflow menu of an experience tile universeId = "" # Find Start Place ID at https://create.roblox.com/dashboard/creations in the overflow menu of an experience tile placeId = "" instanceId = "" instanceType = "" propertyName = "" propertyValue = "" # Request header apiKeyHeaderKey = "x-api-key" contentTypeHeaderKey = "Content-type" contentTypeHeaderValue = "application/json" # Endpoint URL for Update Instance method updateInstanceUrl = "https://apis.roblox.com/cloud/v2/universes/%s/places/%s/instances/%s" # JSON keys detailsJSONKey = "Details" engineInstanceJSONKey = "engineInstance" def GeneratePostData(): propertiesDict = {propertyName: propertyValue} detailsDict = {instanceType: propertiesDict} instanceDict = {detailsJSONKey: detailsDict} outerDict = {engineInstanceJSONKey: instanceDict} return json.dumps(outerDict) def UpdateInstance(postData): url = updateInstanceUrl % (universeId, placeId, instanceId) headerData = {apiKeyHeaderKey: apiKey, contentTypeHeaderKey: contentTypeHeaderValue} return requests.patch(url, headers = headerData, data = postData) postData = GeneratePostData() response = UpdateInstance(postData) print("Response:", response.status_code, response.text) # Parse the Operation object's path from response. Poll for results to perform # the update. operationPath = response.json()['path'] ``` #### cURL ```bash curl --include --location --request PATCH "https://apis.roblox.com/cloud/v2/universes//places//instances/" --header "x-api-key: " --header "Content-Type: application/json" --data '{"engineInstance": {"details": {"": {"": ""}}}}' ``` > **Info:** If you receive a `TypeError` when attempting to update a script, verify that the script isn't open in Roblox Studio. If it is, close its Script Editor tab. You can also check the Studio output for additional errors, such as `Engine_OC_API: Processing Error - Live scripting session is active`. ## Poll for results All current [Instance](/docs/en-us/cloud/reference/Instance.md) methods return an `Operation` object instead of the resource you requested. This object lets you asynchronously perform the original operation. Use the `Operation` object's path (included in the initial response) to poll for when the resource is ready. You can use a variety of polling strategies, such as using exponential backoff or a fixed delay in between requests. The following example polls every five seconds, up to 10 times. #### Python ```python import requests import time # Generate at https://create.roblox.com/dashboard/credentials apiKey = "" # Use the Operation path from your initial request # Takes the form of "universes//places//instances//operations/" operationPath = "" # Polling constants numberOfRetries = 10 retryPollingCadence = 5 # Request header apiKeyHeaderKey = "x-api-key" # Endpoint URL for long-running operation polling getOperationUrl = "https://apis.roblox.com/cloud/v2/%s" # JSON keys doneJSONKey = "done" def GetOperation(operationPath): url = getOperationUrl % (operationPath) headerData = {apiKeyHeaderKey: apiKey} results = requests.get(url, headers = headerData) return results def PollForResults(operationPath): currentRetries = 0 while (currentRetries < numberOfRetries): time.sleep(retryPollingCadence) results = GetOperation(operationPath) currentRetries += 1 if (results.status_code != 200 or results.json()[doneJSONKey]): return results response = PollForResults(operationPath) print("Response:", response.status_code, response.text) ``` #### cURL ```bash curl --include --location --request GET "https://apis.roblox.com/cloud/v2/universes//places//instances//operations/" --header "x-api-key:" ``` ## Potion Shop demo The Potion Shop Google Sheets demo shows you to update an experience's script from the web. The demo consists of the following: - An uncopylocked place with a mock UI that displays various potions and their prices. - A `.ods` file that you import into Google Sheets. This spreadsheet lets you specify and update values for properties within the experience. - Code for a Google Apps Script that reads data from the Google Sheet and updates the **ReplicatedStorage > ItemList** script in the Potion Shop experience. ### Set up the demo 1. Go to the [Potion Shop Demo](https://www.roblox.com/games/14215142052/Potion-Shop-Demo) Discover page. Click on the overflow menu, and then **Edit in Studio**. Studio opens with a copy of the place. 2. Click **File** → **Save to Roblox** and fill out the necessary information to save the Potion Shop Demo as the default place for your experience. Test the experience. You should see the UI for the Potion Shop Demo. Make a note of the name, cost, and colors of the potions. You'll change them later using Open Cloud! ### Set up the sheet 1. [Download](../../assets/open-cloud/open-cloud-potion-shop-demo.ods) the Potion Shop spreadsheet file. 2. Go to [Google Sheets](https://sheets.google.com/) and click **Blank Spreadsheet**. 3. In the sheet that appears, click **File > Import**, and click the **Upload** tab. 4. Drag the Potion Shop spreadsheet file into the upload window. 5. Choose **Replace spreadsheet** and then **Import data**. 6. In the AppScript tab of the sheet, copy the code in cell A1. 7. Click the **Extensions > Apps Script** menu and paste the code into the `Code.gs` file. Then save the project. 8. Back in the spreadsheet, select the **Update Potion Shop** tab. Click the **Update Script** button, click the overflow menu, and select **Assign script**. 9. In the **Assign script** window, enter `UpdateScript` and click **OK**. ### Create an API key 1. Go to the [Creator Hub Open Cloud API Keys](https://create.roblox.com/dashboard/credentials?activeTab=ApiKeysTab) page and click **Create API Key**. 2. Fill out the form with the following information. - **Name**: PotionShop - **API System**: Add the **universe-place-instances** API system. Add your Potion Shop experience to the system. For **Experience Operations**, add read and write access. - **Accepted IP Addresses**: Add 0.0.0.0/0 as an IP Address - **Expiration**: No Expiration - Click **Save & Generate Key** and then **Copy Key to Clipboard**. 3. Paste the API key to the API Key cell (D2) on the Intro tab of your Google Sheet. ### Obtain the universe and place ID 1. Go to the [Creator Hub Creations](https://create.roblox.com/dashboard/creations) page, hover over the Potion Shop's experience tile, and click the overflow menu. 2. Select **Copy Universe ID** and paste it into the **Universe ID** cell (E2) on the Intro tab of your Google Sheet. 3. Select **Copy Start Place ID** and paste it into the **Place ID** cell (F2) on the Intro tab of your Google Sheet. ### Update the script values 1. In the **Update Potion Shop** tab of the sheet, modify any values you'd like and click the **Update Script** button. 2. If Google Sheets prompts you to for authorization, click **OK** and allow your account to run the script. 3. In Studio, playtest the Potion Shop to see any reflected changes. From the **Explorer** window, you can also open the **ReplicatedStorage > ItemList** script to inspect the changes. --- title: "User inventories" url: /docs/en-us/cloud/guides/inventory last_updated: 2026-06-29T19:33:58Z description: "Covers core use cases for the Inventory API, such as verifying that a user owns a particular item and filtering return values for categories of items." --- # User inventories The [Inventory API](/docs/en-us/cloud/reference/InventoryItem.md) lets you access most of the same information as the Roblox **My Inventory** page. You can check whether a user owns individual items and retrieve the full list of items that a player owns. Responses from the [Inventory API](/docs/en-us/cloud/reference/InventoryItem.md) include items from the following categories: - Clothing (accessories, bottoms, classic clothing, shoes, tops) - Purchases and rewards (badges, passes, purchased places, private servers) - Avatar items (avatar animations, classic heads, emotes, faces, hair, heads) Before using the [Inventory API](/docs/en-us/cloud/reference/InventoryItem.md), you must [generate an API key](/docs/en-us/cloud/auth/api-keys.md) or [configure OAuth 2](/docs/en-us/cloud/auth/oauth2-overview.md) for your app. The examples on this page use API keys. ## Check item ownership If you want to check whether a user owns a particular item (for example, a limited, badge, pass, or private server), use the `filter` parameter to check for a comma-separated list of one or more IDs. This code sample checks for three asset IDs: #### Node.js ```js const https = require('node:https'); const userId = 11111111111; const hostname = 'apis.roblox.com'; const path = \`/cloud/v2/users/${userId}/inventory-items\`; const params = '?filter=assetIds=62724852,1028595,4773588762'; const url = 'https://' + hostname + path + params; const apiKey = '123456789012345678901234567890123456789012345678'; const options = { headers: { 'x-api-key': \`${apiKey}\`, }, }; https .get(url, options, (response) => { console.log('statusCode:', response.statusCode); let data = ''; response.on('data', (d) => { data += d; }); response.on('end', () => { if (response.statusCode === 200) { const jsonData = JSON.parse(data); console.log('Response Data:', JSON.stringify(jsonData, null, 2)); } else { console.error('Error:', response.statusCode, response.statusMessage); } }); }) .on('error', (e) => { console.error(e); }); ``` #### Python ```python import requests import json userId = 11111111111 apiKey = '123456789012345678901234567890123456789012345678' hostname = 'https://apis.roblox.com' path = f'/cloud/v2/users/{userId}/inventory-items' url = hostname + path parameters = { 'filter': 'assetIds=62724852,1028595,4773588762' } headers = {'x-api-key': f'{apiKey}'} response = requests.get(url, params=parameters, headers=headers) print(json.dumps(json.loads(response.text), indent=2)) ``` The following response indicates that the user owns one of the three items: ```json { "inventoryItems": [ { "path": "users/11111111111/inventory-items/VVNFUl9BU1NFVF9JRD0yMDAxMDUxMTkzODg", "assetDetails": { "assetId": "1028595", "inventoryItemAssetType": "CLASSIC_TSHIRT", "instanceId": "200105119388" } } ], "nextPageToken": "" } ``` ## Filter items If you want to display, for example, only the collectibles that a user owns, use the same code as above, just with a different `filter` parameter. #### Node.js ```js const params = '?filter=onlyCollectibles=true;inventoryItemAssetTypes=*'; ``` #### Python ```python parameters = { 'filter': 'onlyCollectibles=true;inventoryItemAssetTypes=*' } ``` Mix and match filters using a semicolon-separated list. Here are a few examples: ```text filter=onlyCollectibles=true;inventoryItemAssetTypes=HAT,CLASSIC_PANTS filter=badgeIds=111111,222222;gamePassIds=777777;privateServerIds=999999 filter=gamePasses=true;badges=true ``` Most calls to the API do not require any specific permissions, but several filters require Inventory read permissions. For more information, see [Filtering](/docs/en-us/cloud/reference/patterns.md#list-inventory-items). ## Paginate results If a response includes a value for `nextPageToken`, use that value in the `pageToken` parameter of the subsequent request to retrieve the next page. For more information, see [Pagination](/docs/en-us/cloud/reference/patterns.md#pagination). If the number of results is divisible by `maxPageSize` (for example, you have 50 results and a `maxPageSize` of 25), you can encounter a situation in which your response includes a value for `nextPageToken`, but a request using that token returns no results: ```json GET /cloud/v2/users/{userId}/inventory-items?maxPageSize=25&pageToken=cccDDD { "inventoryItems": [], "nextPageToken": "" } ``` When implementing pagination in your app, best practice is to check not just for a non-empty `nextPageToken`, but also for a non-empty `inventoryItems` array. --- title: "Secrets stores" url: /docs/en-us/cloud/guides/secrets-store last_updated: 2026-06-29T19:33:58Z description: "Covers usage for the Secrets store API." --- # Secrets stores In addition to [managing your secrets within experiences](/docs/en-us/cloud-services/secrets.md), you can manage secrets using the Open Cloud secrets store API. Before using the API, you must [generate an API key](/docs/en-us/cloud/auth/api-keys.md) with the `secret-store` API system or [configure your OAuth 2.0 app](/docs/en-us/cloud/auth/oauth2-overview.md) with the `universe.secret` scope type. The examples on this page use API keys. ## Secret encryption When creating or updating secrets on Roblox, you must encrypt secrets with a [LibSodium sealed box](https://libsodium.gitbook.io/doc/public-key_cryptography/sealed_boxes) and your experience's public key, and then base64-encode the result. First, get your experience's public key: ```bash curl --location 'https://apis.roblox.com/cloud/v2/universes/{universeId}/secrets/public-key' \ --request GET \ --header 'x-api-key: ' \ ``` Next, create a sealed box and base64-encode it. The example below uses Python and [PyNaCl](https://pynacl.readthedocs.io/en/latest/public/#nacl-public-sealedbox). (Run `pip install pynacl` to install it locally). ```python from base64 import b64encode from nacl import encoding, public public_key = "Zgj4+V7vSaEZ06rXazKJUIcUnVa95tUNiwXAif/vdHo=" secret_content = "my_api_key_content" public_key = public.PublicKey(public_key.encode("utf-8"), encoding.Base64Encoder()) sealed_box = public.SealedBox(public_key) encrypted = sealed_box.encrypt(secret_content.encode("utf-8")) print(b64encode(encrypted).decode("utf-8")) ``` You can then create or update a secret using the output. This example creates a new secret: ```bash curl --location 'https://apis.roblox.com/cloud/v2/universes/6930499524/secrets' \ --request POST \ --header 'Content-Type: application/json' \ --header 'x-api-key: ' \ --data '{ "id": "mySecret", "secret": "fP9scJkcDk492F4c1VHZ5QS8v2qsAg7uI+NVVEw6zC0GBnj7xpi7UrNr++lCfr4wyq3ia9Uuu+Ao8HtIXz2gRxBX", "key_id": "1200590785272263122" }' ``` After you create one, see [Use secrets](/docs/en-us/cloud-services/secrets.md#use-secrets) to use your secret in experience. ## Update secrets To update the above secret: ```bash curl --location 'https://apis.roblox.com/cloud/v2/universes/6930499524/secrets/mySecret' \ --request PATCH \ --header 'Content-Type: application/json' \ --header 'x-api-key: ' \ --data '{ "secret": "2Fczw/PL7woOzHnGHQ65sT0MbzJjEOlfibyKxy374CqzFyEb2QTS8grtNBgG/0sfIvSHEo9JWN+pUr0NTPs0V6lj", "key_id": "1200590785272263122" }' ``` If you need to clean up a secret, a deletion request looks like this: ```bash curl --location 'https://apis.roblox.com/cloud/v2/universes/6930499524/secrets/mySecret' \ --request DELETE \ --header 'Content-Type: application/json' \ --header 'x-api-key: ' ``` --- title: "Usage guide for assets" url: /docs/en-us/cloud/guides/usage-assets last_updated: 2026-06-29T19:33:58Z description: "Explains how to use Open Cloud Web APIs for assets to support usage such as uploading and updating." --- # Usage guide for assets The [Assets API](/docs/en-us/reference/cloud/assets/v1.md) of Open Cloud allows you to upload and update assets with a single HTTP request rather than manually importing them to Studio. This API supports: - Uploading new assets. - Updating existing assets with version control. - Updating asset metadata, including descriptions, display names, icons, and previews. - Managing asset versions, such as rolling back to a specified previous version. - Checking existing information of an asset, including metadata, versions, and any in-process updating operations. > **Info:** This API contains beta endpoints that might be subject to changes for future releases. ## Supported asset types and limits For endpoints that don't create a new asset or update the content of existing assets, there are no restrictions and limits. However, the asset content uploading functionality powered by the **Create Asset** and **Update Asset** endpoints only supports limited types of assets with restrictions. For each call, you can only create or update one asset with the file size up to 20 MB with the following limits: > **Info:** Updating asset metadata using the **Update Asset** endpoint is not subject to the following limits. | Asset type | Format | Content type | Restrictions | | --- | --- | --- | --- | | [Animation](/docs/en-us/animation.md) | | | | | [Audio](/docs/en-us/audio/assets.md) | | | | | [Decal, Image](/docs/en-us/parts/textures-decals.md) | | | | | [Mesh](/docs/en-us/parts/meshes.md) | | | | | [Model](/docs/en-us/parts/models.md) | | | | | [Video](/docs/en-us/ui/video-frames.md) | | | | ## Security permissions The API supports both first-party use with [API key authorization](/docs/en-us/cloud/auth/api-keys.md) and third-party use in [OAuth 2 applications](/docs/en-us/cloud/auth/oauth2-overview.md). Each way requires different security permission settings. ### API keys To use the API in your own scripts or tools, you need to [create an API key](/docs/en-us/cloud/auth/api-keys.md#create-api-keys) for authorization and security. > **Warning:** To create an API key for managing group assets, you must have the corresponding permissions. For more information on granting group permissions, see [Group roles and permissions](/docs/en-us/projects/groups.md#roles-and-permissions). When creating an API key, make sure to add the following permissions: 1. Add **assets** to **Access Permissions**. 2. Add **Read** and **Write** operation permissions to your selected experience, depending on the required scopes of the endpoints you plan to call. Once you have the API key, copy it to the `x-api-key` request header. All endpoints require the `x-api-key` request header. ```bash --header 'x-api-key: ${ApiKey}' \ ``` ### OAuth 2.0 apps To use the API for a third-party OAuth 2.0 application, add the `asset:read` and `asset:write` permission scopes when [registering your app](/docs/en-us/cloud/auth/oauth2-registration.md#add-permissions). Choose these scopes based on the requirements of the endpoints you plan to use. ## Create a new asset To upload a new asset by an HTTP request: 1. Copy the API key to the `x-api-key` request header of the [Create Asset](/docs/en-us/reference/cloud/assets/v1.md#POST-v1-assets) endpoint. 2. In your request: 1. Specify the target [asset type](#supported-asset-types-and-limits). 2. Add your asset name and description. 3. Add the creator information. - If you want to create the asset **on your own behalf**, add your user ID. You can find your user ID on the URL of your Roblox profile. For example, for `https://www.roblox.com/users/1234567/profile`, your user ID is `1234567`. - If you want to create the asset **as a group asset**, add the group ID of your group. You can find the group ID on the URL of your group's page. For example, for `https://www.roblox.com/groups/7654321/example-group#!/`, the group ID is `7654321`. 4. Add the file path and content type of your asset.```bash curl --location 'https://apis.roblox.com/assets/v1/assets' \ --header 'x-api-key: ${ApiKey}' \ --form 'request="{ \"assetType\": \"Model\", \"displayName\": \"Name\", \"description\": \"This is a description\", \"creationContext\": { \"creator\": { \"userId\": \"${userId}\" # Use groupId for creating a group asset } } }"' \ --form 'fileContent=@"/filepath/model.fbx";type=model/fbx' ``` ## Update an existing asset > **Info:** Asset updating is powered by beta endpoints that might be subject to changes for future releases. To update an existing asset by an HTTP request: 1. Copy the API key to the `x-api-key` request header of the [Update Asset](/docs/en-us/reference/cloud/assets/v1.md#PATCH-v1-assets-_asset_) endpoint. 2. Add the asset type and asset ID in your request. To copy your asset ID: 1. Navigate to the [Creation](https://create.roblox.com/dashboard/creations) page of the **Creator Dashboard**. 2. Select the **Development Items** category. 3. Select the category of your asset and find the target asset. 4. Hover over the thumbnail of the target asset and click the **⋯** button to display a list of options, then select **Copy Asset ID** from the list. #### Asset Content > **Warning:** Currently, you can only update the asset content for `.fbx` files. The update creates a new version. ```bash curl --location --request PATCH 'https://apis.roblox.com/assets/v1/assets/{assetId}' \ --header 'x-api-key: {apiKey}' \ --form 'request={ \"assetType\": \"{assetType}\", \"assetId\": \"{assetId}\", \"creationContext\": { \"creator\": { \"userId\": {userId} }, \"expectedPrice\":{expectedPrice} }, }' \ --form 'fileContent=@"{file-path}"' ``` #### Metadata ```bash curl --location --request PATCH 'https://apis.roblox.com/assets/v1/assets/{assetId}?updateMask=description%2CdisplayName' \ --header 'x-api-key;' \ --form 'request={ \"assetType\": \"{assetType}\", \"assetId\": \"{assetId}\", \"displayName\": \"{new display name}\", \"description\": \"{new description}\" }' ``` #### Content and Metadata ```bash curl --location --request PATCH 'https://apis.roblox.com/assets/v1/assets/{assetId}?updateMask=description%2CdisplayName' \ --header 'x-api-key: {apiKey}' \ --form 'request={ \"assetType\": \"{assetType}\", \"assetId\": \"{assetId}\", \"displayName\": \"{new display name}\", \"description\": \"{new description}\", \"creationContext\": { \"creator\": { \"userId\": {userId} }, \"expectedPrice\":{expectedPrice} }, }' \ --form 'fileContent=@\"{file-path}\"' ``` ## Retrieve asset operation status If your request for creating a new asset or updating an existing asset succeeds, it returns an **Operation ID** in the format of `{ "path": "operations/${operationId}" }`. You can use it to check the status and result of your upload with the following steps: 1. Copy the API key to the `x-api-key` request header of the [Get Operation](/docs/en-us/reference/cloud/assets/v1.md#GET-v1-operations-_operationId_) method and send the request, like the following code sample:```bash curl --location 'https://apis.roblox.com/assets/v1/operations/{operationId}' \ --header 'x-api-key: {$ApiKey}' ``` 2. If your request succeeds, it returns an `Operation` object, either including a `response` representing the uploaded asset information or a `status` explaining why the asset upload fails as the following code sample shows:```json { "path": "operations/{operationId}", "done": true, "response": { "@type": "type.googleapis.com/roblox.open_cloud.assets.v1.Asset", "path": "assets/2205400862", "revisionId": "1", "revisionCreateTime": "2023-03-02T22:27:04.062164400Z", "assetId": "2205400862", "displayName": "Name", "description": "This is a description", "assetType": "ASSET_TYPE_DECAL", "creationContext": { "creator": { "userId": "11112938575" } }, "moderationResult": { "moderationState": "MODERATION_STATE_APPROVED" } } } ``` 3. (Optional) Check the created asset on your Roblox account. 1. Navigate to the **Inventory** page of your [Roblox account](https://www.roblox.com/home). 2. Select the **Category** of the asset that you want to check. 3. Find the target asset and click its thumbnail to view the asset. ## Add assets API to OAuth 2.0 apps You can create [OAuth 2.0 applications](/docs/en-us/cloud/auth/oauth2-overview.md) supporting Assets API to allow your users to upload and update assets to Roblox. > **Warning:** Third-Party app support through OAuth 2.0 is a Beta feature that might be subject to changes for future releases. To use Assets API for your application and request permissions from your users, perform the following settings: 1. When [registering your application](/docs/en-us/cloud/auth/oauth2-registration.md#register-your-app), under **Permissions**, select **asset:read** and **asset:write** scopes. 2. When [implementing the authorization flow](/docs/en-us/cloud/auth/oauth2-overview.md#implement-authorization-flows), include `asset:read` and `asset:write` as the scope parameters of the authorization URL that redirects users back to your application, like the following example:```plain https://apis.roblox.com/oauth/v1/authorize?client_id=819547628404595165403873012&redirect_uri=https://my-app.com/redirect&scope=asset:read+asset:write&response_type=Code&prompts=login+consent&nonce=12345&state=6789 ``` 3. When sending the request, include the access token in the authorization header and the form data of the asset content to create or update in the request URI in. The following example shows a sample request for uploading a new asset:```bash curl --location --request POST 'https://apis.roblox.com/assets/v1/assets' \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --form 'request="{ \"assetType\": \"Decal\", \"displayName\": \"DecalDemo123\", \"description\": \"This is a description\", \"creationContext\": { \"creator\": { \"userId\": \"\" } } }"' \ --form 'fileContent=@"/filepath/p1.png"' ``` --- title: "Messaging usage guide" url: /docs/en-us/cloud/guides/usage-messaging last_updated: 2026-06-29T19:33:58Z description: "Explains how to use Open Cloud Messaging Service API to support cross-server messaging." --- # Messaging usage guide The [Messaging Service API](/docs/en-us/cloud/reference/Universe.md#Cloud_PublishUniverseMessage) is the Open Cloud equivalent of the Engine `Class.MessagingService`, which lets you communicate across your experience's servers and clients. The Engine API only allows you to write and update scripts manually in Studio for publishing messages, but the Open Cloud API lets you send messages to live servers from external tools to automate and improve your operations workflows. ## Usage There are several helpful tools that you can build by supporting cross-server communication with the Messaging Service API, including: - **Announcement Portals**: A web portal can be helpful to support sending announcements to all users across servers in your experience, such as announcing an upcoming event, an update, and the winner for a competition. On the portal, you can edit a message and click a button that calls the API to send the message out for all users or selected users. - **Moderation System**: A moderation system can help keep your experience safe and secure. When detecting a user with inappropriate behavior, you can publish a message to trigger the experience server to warn or ban the specific user. You can also use [data stores](/docs/en-us/cloud/reference/DataStore.md) in the moderation system to add user accounts to a blocklist that prevents them from rejoining. - **LiveOps Dashboard**: LiveOps dashboards are useful tools for managing live events, such as a Halloween party. On the dashboard, you can pre-code an event, update event messages, trigger the event when it's ready, and reward selected users with special items like a virtual crown without updating any of the experience's code. > **Info:** Currently, the API can only target live experience servers through HTTP. ## Limits The Messaging Service API follows the same limits as the Engine `Class.MessagingService`. Rate limits are shared across both APIs, and usage from both APIs is counted against the same limits. | Limit | Description | | --- | --- | | **Messages sent per game server** | `600 + 240 * (number of players in this game server)` per minute | | **Messages received per topic** | `(40 + 80 * number of servers)` per minute | | **Messages received for entire game** | `(400 + 200 * number of servers)` per minute | | **Subscriptions allowed per game server** | `20 + 8 * (number of players in this game server)` | | **Subscribe requests per game server** | 240 requests per minute | | **Topic size** | 80 characters | | **Message size** | 1,024 characters (1 KiB) | ## Set up a topic for messaging Before you can publish a message to your experience's live servers, you must set up a **topic**, which is a customized message channel that is accessible from multiple servers. After defining a topic, you subscribe users to the topic in order to receive your incoming messages. Currently, you can only define a topic in Studio and use the Luau API `Class.MessagingService:SubscribeAsync()` to subscribe users to it. The following code sample subscribes any user to a topic when they join the experience: ```lua local MessagingService = game:GetService("MessagingService") local Players = game:GetService("Players") local function onPlayerAdded(player) -- Define and subscribe to the topic local topic = "YourTopic" local connection = MessagingService:SubscribeAsync(topic, function(message) print(message.Data) end) player.AncestryChanged:Connect(function() -- Unsubscribe from the topic upon player ancestry change connection:Disconnect() end) end Players.PlayerAdded:Connect(onPlayerAdded) ``` ## Publish messages to live servers After [setting up](#set-up-a-topic-for-messaging) a topic, publish a message to your experience's live servers: 1. [Create an API key](/docs/en-us/cloud/auth/api-keys.md#create-api-keys) on [Creator Dashboard](https://create.roblox.com/dashboard/credentials?activeTab=ApiKeysTab) and copy it somewhere safe. Make sure you perform the following settings: 1. Add **messaging-service** to **Access Permissions**. 2. Select an experience, and add the **universe-messaging-service:publish** operation. 2. Get the **Universe ID** for your experience: 1. Navigate to the [Creator Dashboard](https://create.roblox.com/dashboard/creations). 2. Find the experience that you want to publish your messages to. 3. Hover over an experience's thumbnail, click the **⋯** button, and select **Copy Universe ID**. 3. Add the API key and universe to a `POST` request, as in this example:```bash curl -L -X POST 'https://apis.roblox.com/cloud/v2/universes/{universe}:publishMessage' \ -H 'x-api-key: {api-key}' \ -H 'Content-Type: application/json' \ --data '{ "topic": "your-topic", "message": "Hello, everyone!" }' ``` 4. Send the HTTP request to publish the message. > **Info:** If your request succeeds, it returns HTTP 200 with an empty response body. ## Add the Messaging Service API to OAuth 2.0 apps You can create [OAuth 2.0 applications](/docs/en-us/cloud/auth/oauth2-overview.md) that allow your users to publish messages to their own live servers. > **Warning:** Third-party app support through OAuth 2.0 is a beta feature that might be subject to changes for future releases. To use Messaging Service API for your application and request permissions from your users, perform the following settings: 1. When [registering your application](/docs/en-us/cloud/auth/oauth2-registration.md#register-your-app), under **Permissions**, select the **universe-messaging-service:publish** scope. 2. When [implementing the authorization flow](/docs/en-us/cloud/auth/oauth2-overview.md), include `universe-messaging-service:publish` in the `scope` parameter of the authorization URL that redirects users back to your application, like the following example:```plain https://apis.roblox.com/oauth/v1/authorize?client_id=816547628409595165403873012&redirect_uri=https://my-app.com/redirect&scope=openid+universe-messaging-service:publish&response_type=Code&prompts=login+consent&nonce=12345&state=6789 ``` 3. Request access to the `universeId` of the experience that the user wants to publish their messages to. Your application can send a `POST` request to the [token resources endpoint](/docs/en-us/cloud/auth/oauth2-reference.md#token-exchange) with the access token, client ID and secret or the `code challenge`, depending on your [implementation of your authorization flow](/docs/en-us/cloud/auth/oauth2-overview.md#implementing-authorization-flows), as request parameters to get a list of `universeIds` of experiences that the user granted permission to:```bash curl --location --request POST 'https://apis.roblox.com/oauth/v1/token/resources' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'token=' \ --data-urlencode 'client_id=' \ --data-urlencode 'client_secret=' ``` This endpoint returns a list of `universeIds` like the following example response:```json { "resource_infos": [ { "owner": { "id": "1516563360", "type": "User" }, "resources": { "universe": { "ids": ["3828411582"] } } } ] } ``` 4. Your application can now send messages to any experience that a user has granted permission. When sending the request, include the access token in the authorization header and the `universeId` and topic in the request URI in the following format:```bash curl --location --request POST 'https://apis.roblox.com/cloud/v2/universes/{universe}:publishMessage' \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data-raw '{"topic": "some-topic","message":"message to publish"}' ``` ## Use with HTTPService This API is one of the [Open Cloud endpoints supported](/docs/en-us/cloud-services/http-service.md#supported-open-cloud-endpoints) by `Class.HttpService`. You can use it to publish messages across different experiences from within an experience. --- title: "Usage guide for place publishing" url: /docs/en-us/cloud/guides/usage-place-publishing last_updated: 2026-06-29T19:33:58Z description: "Explains how to use Open Cloud Place Publishing API to publish places programmatically with version control." --- # Usage guide for place publishing The [place publishing API](/docs/en-us/reference/cloud/universes-api/v1.md) offers similar functionality to [publishing to Roblox](/docs/en-us/production/publishing/publish-games-and-places.md) in Studio, with extra permission control and automation in continuous release workflow. You can use this API to update existing places of an experience to a new version, making it useful for automating your publishing workflow. For example, you can call this API from a GitHub action and have it automatically push a binary place file to Roblox after successful integration testing. ## Limitations - This API only supports HTTPS requests. - The place publishing API doesn't update certain instance types. If your experience contains `Class.EditableImage`, `Class.EditableMesh`, `Class.PartOperation`, `Class.SurfaceAppearance`, or `Class.BaseWrap` instances, publish from Studio after modifying them. ## Places on Roblox Experiences on Roblox can have multiple places, with one **starting place** as the user entry point of your experience and optional other places that you can [teleport users between](/docs/en-us/projects/teleport.md). You can use the API to publish either the starting place or other places of your experience with version control. Before you can update an existing place of an experience, you need the place's **Place ID** and the experience's **Universe ID**. The **Place ID** identifies the place and the **Universe ID** identifies the experience. Each is unique even if your experience has only one place. ## Publish a place To publish an existing place of an experience with [Place Publishing API](/docs/en-us/reference/cloud/universes-api/v1.md): 1. [Create an API key](/docs/en-us/cloud/auth/api-keys.md#create-api-keys) on the **Creator Dashboard**. Make sure you perform the following settings: 1. Add **universe-places** to **Access Permissions**. 2. Add **Write** operation to your selected experience. 2. Get the **Universe ID** of the experience in which you want to publish the place. 1. Navigate to the [Creator Dashboard](https://create.roblox.com/dashboard/creations). 2. Find the experience with the place that you want to update. 3. Hover over the experience's thumbnail, click the **⋯** button, and select **Copy Universe ID**. 3. Get the **Place ID** of the place that you want to update. 1. Stay on the [Creations](https://create.roblox.com/dashboard/creations) page on **Creator Dashboard** and click the thumbnail of the target experience. 2. On the left navigation menu, click the **Places** tab. 3. Find the place that you want to update and click its thumbnail. 4. The **Place ID** is in the redirected URL. For example, in the URL `https://create.roblox.com/dashboard/creations/experiences/0000000/places/111111/configure`, the **Place ID** is `111111`. 4. Add the API Key in the **x-api-key** header of a `POST` request to the API. The following two example requests reference a Roblox XML place file (`.rbxlx`) and a binary place file (`.rbxl`).```bash $ curl --verbose --location POST 'https://apis.roblox.com/universes/v1/{universeId}/places/{placeId}/versions?versionType=Published' \ --header 'x-api-key: ' \ --header 'Content-Type: application/xml' \ --data-binary @/home/placefiles/place1.rbxlx ``````bash $ curl --verbose --location POST 'https://apis.roblox.com/universes/v1/{universeId}/places/{placeId}/versions?versionType=Published' \ --header 'x-api-key: ' \ --header 'Content-Type: application/octet-stream' \ --data-binary @/home/placefiles/place1.rbxl ``` To run the commands, replace `{universeId}` and `{placeId}` with the actual **Universe ID** and **Place ID** of the experience and place that you want to publish. 5. If you send your request correctly, you receive a success response body with the place version number in the following format:```json { "versionNumber": 7 } ``` 6. (Optional) Verify the upload in Studio or on [Creator Dashboard](https://create.roblox.com/dashboard/creations). --- title: "Cloud API reference" url: /docs/en-us/cloud last_updated: 2026-06-29T19:33:58Z description: "Get comprehensive API reference documentation for Open Cloud." --- # Cloud API reference With Open Cloud, you can access Roblox resources through standard [REST](https://en.wikipedia.org/wiki/REST) APIs, which lets you build everything from command line automation tools to complex web apps. You can update experiences, restart servers, work with your data stores and memory stores, manage user restrictions, list inventory items, and much, much more. This reference is broken into two sections: - A [section that separates endpoints by feature](/docs/en-us/cloud/reference/features/accounts.md) (Avatars, Game Passes, Users, etc.) - A [section that separates endpoints by domain](/docs/en-us/cloud/reference/domains/apis.md) (base URL) **Both sections** contain the full list of available API endpoints. We recommend the [features section](/docs/en-us/cloud/reference/features/accounts.md) since it helps consolidate endpoints by use case, but experienced Open Cloud developers might prefer to browse by domain. - Whenever possible, use endpoints that support [API keys](/docs/en-us/auth/api-keys.md) or [OAuth 2.0](/docs/en-us/auth/oauth2-overview.md) for authentication. They have strong stability guarantees and receive regular updates. - Legacy APIs use cookie-based authentication, can incorporate breaking changes without notice, and have minimal stability guarantees. We don't recommend them for production applications. > **Info:** Roblox also offers [webhooks](/docs/en-us/webhooks/webhook-notifications.md), which can notify your applications when certain events occur, such as refunds or changes to subscriptions. ## Get started with Open Cloud 1. Set up authentication for your application. See the documentation for how to use [API keys](/docs/en-us/auth/api-keys.md) or [OAuth 2.0](/docs/en-us/auth/oauth2-overview.md). API keys are the easiest way to get started. 2. Test API calls using tools like [Postman](https://www.postman.com) with [OpenAPI descriptions](/docs/en-us/reference/openapi.md) or the [OAuth 2.0 sample app](/docs/en-us/auth/oauth2-sample.md). 3. Review the [resource guides](/docs/en-us/guides.md) for end-to-end walkthroughs of using certain APIs. 4. Explore the left navigation for the full list of features, [common API patterns](/docs/en-us/reference/patterns.md), [types](/docs/en-us/reference/types.md), and [error codes](/docs/en-us/reference/errors.md). ## Make requests within experiences `Class.HttpService` lets you make HTTP requests to a subset of the Open Cloud endpoints. For more information, see [In-experience HTTP requests](/docs/en-us/cloud-services/http-service.md). --- title: "Errors" url: /docs/en-us/cloud/reference/errors last_updated: 2026-06-29T19:33:58Z description: "Defines the errors that are returned by Open Cloud APIs" --- # Errors The following sections describe the error handling for Open Cloud APIs. Due to implementation differences across endpoints and validation layers, error responses can vary significantly in format. ## Gateway errors For authentication or routing issues, both Open Cloud v1 and v2 APIs may return errors in this format: ```json { "errors": [ { "code": 0, "message": "Invalid API Key" } ] } ``` ## Open Cloud v2 Open Cloud v2 APIs generally follow a consistent error format when the error occurs within the API endpoint itself. ### Standard v2 error format Open Cloud v2 APIs return errors in this format: - `code` - A string representing the error type (e.g. `INVALID_ARGUMENT`, `NOT_FOUND`). - `message` - A human-readable message explaining the error. - `details` - An optional array containing additional error-specific information. ```json { "code": "INVALID_ARGUMENT", "message": "Invalid User ID in the request." } ``` ```json { "code": "INVALID_ARGUMENT", "message": "The provided filter is invalid.", "details": [ { ... } ] } ``` ### v2 error codes The following table describes possible values for `code` in v2 API responses. | Code | HTTP Status | Description | | --- | --- | --- | | `INVALID_ARGUMENT` | 400 | You passed an invalid argument, such as an invalid `universeId`. You might also have missing or invalid headers, such as `Content-Length` and `Content-Type`. | | `PERMISSION_DENIED` | 403 | Your request doesn't have sufficient permissions or scopes to perform the operation. | | `NOT_FOUND` | 404 | The system can't find your specified resources, such as a data store entry. | | `ABORTED` | 409 | The operation was aborted. | | `RESOURCE_EXHAUSTED` | 429 | You don't have enough quota to perform the operation, typically due to sending too many requests. | | `CANCELLED` | 499 | The system terminates the request, typically due to a client side timeout. | | `INTERNAL` | 500 | Internal server error, typically due to a server bug. | | `NOT_IMPLEMENTED` | 501 | The server doesn't implement the API method. | | `UNAVAILABLE` | 503 | Service is unavailable, typically returned when the server is down. | ## Open Cloud v1 Open Cloud v1 APIs have inconsistent error response formats. The format depends on the specific endpoint, the type of error, and where the error occurs in the request processing pipeline. Most v1 endpoints return errors in one of these three formats: ```json { "error": "INVALID_ARGUMENT", "message": "Invalid cursor.", "errorDetails": [ { "errorDetailType": "DatastoreErrorInfo", "datastoreErrorCode": "InvalidCursor" } ] } ``` ```json { "code": "INVALID_ARGUMENT", "message": "Invalid cursor." } ``` ```json { "errors": { "assetId": ["The value 'a' is not valid."] }, "type": "https://tools.ietf.org/html/rfc9110#section-15.5.1", "title": "One or more validation errors occurred.", "status": 400, "extensions": { "traceId": "00-427917f0fc3b8375ee33e4603a7f0693-f3f6ad560ff1a122-00" } } ``` ### v1 error codes The following table describes possible values for `error` or `code` in v1 API responses: | HTTP Status Code | Error | Descriptions | | --- | --- | --- | | 400 | INVALID_ARGUMENT | You passed an invalid argument, such as an invalid `universeId`. You might also have missing or invalid headers, such as `Content-Length` and `Content-Type`. | | 403 | INSUFFICIENT_SCOPE | The request requires higher privileges than provided by the access token. | | 403 | PERMISSION_DENIED | Your request doesn't have sufficient scope to perform the operation. | | 404 | NOT_FOUND | The system can't find your specified resources, such as a data store. | | 409 | ABORTED | The operation was aborted due to a conflict, such as publishing a place that is not part of the universe. | | 429 | RESOURCE_EXHAUSTED | You don't have enough quota to perform the operation, typically due to sending too many requests. | | 499 | CANCELLED | The system terminates the request, typically due to a client side timeout. | | 500 | INTERNAL | Internal server error. Typically a server bug. | | 501 | NOT_IMPLEMENTED | The server doesn't implement the API method. | | 503 | UNAVAILABLE | Service unavailable. Typically the server is down. | --- title: "OpenAPI document" url: /docs/en-us/cloud/reference/openapi last_updated: 2026-06-29T19:33:58Z description: "Provides a link to a OpenAPI document for the Roblox Cloud APIs that is compliant to the OpenAPI specification." --- # OpenAPI document Roblox publishes an [OpenAPI 3.0.4 document](https://spec.openapis.org/oas/v3.0.4) (formerly known as a Swagger file) that contains **all** of the Roblox Cloud APIs. You can find this file, `openapi.json`, in the open source [creator-docs](https://github.com/Roblox/creator-docs/tree/main/content/en-us/reference/cloud) repository. You can use OpenAPI documents with tools like the [Swagger Editor](https://editor.swagger.io/) and [Postman](https://www.postman.com/) to test calls and help streamline development. Tools like [OpenAPI Generator](https://github.com/OpenAPITools/openapi-generator) can also generate client libraries for your preferred programming languages. > **Warning:** This document is still under active development and may contain issues. Report a bug if you notice inaccurate information. ### Vendor extensions Roblox uses [specification extensions](https://spec.openapis.org/oas/v3.0.4#specification-extensions) to extend the OpenAPI specification. These extensions represent information that the specification doesn't capture by default. Extensions may appear throughout the file where allowed by the OpenAPI specification. The majority of these specification extensions are experimental. The extensions used are subject to change without notice in favor of fields found within the official OpenAPI specification. | Extension | Location | Description | | --- | --- | --- | | `x-roblox-extensions-version` | OpenAPI object | A version string, using semantic versioning, that represents the versions of the extensions specified here and other common structures | | `x-roblox-stability` | Operation object | A string that represents an operation's stability level. | | `x-roblox-deprecated` | Operation object | An object that provides additional information related to an endpoint's deprecation status. | | `x-roblox-alternatives` | Operation object | An object that provides additional information related to an endpoint's alternatives. | | `x-roblox-rate-limits` | Operation object | An object that provides rate limiting information for an endpoint. | | `x-roblox-scopes` | Operation object | An object that provides the list of required, conditional, and optional scopes that are associated with an endpoint. | | `x-roblox-engine-usability` | Operation object | An object indicating the endpoint's usability with the Roblox engine. | --- title: "Patterns" url: /docs/en-us/cloud/reference/patterns last_updated: 2026-06-29T19:33:58Z description: "Explains considerations and guidelines for making proper requests to Open Cloud endpoints and interpreting responses." --- # Patterns This page covers common patterns with the Open Cloud APIs, particularly around making requests and handling responses. ## Paths To make a request to the Open Cloud APIs, you must first form a URL. This URL is a combination of the base URL (e.g. `https://apis.roblox.com`), the Open Cloud API path (for example, `/cloud/v2/universes/{universe_id}/places/{place_id}/user-restrictions`), and any query parameters (for example, `?maxPageSize=25`). A full request URL might look like this: ```json https://apis.roblox.com/cloud/v2/users/4687549151/inventory-items?maxPageSize=100 ``` Many paths, including the example above, have **path parameters**, designated by curly brackets in the API reference. Path parameters are just variables that you insert before making the request and are almost always IDs: user IDs, group IDs, place IDs, etc. IDs are often numeric, but not necessarily; for example, data store and memory store IDs support a wider character set. ## Content length and type Many API calls, particularly those that create or update, require a JSON request body. If your request has a body, be sure to include the `Content-Length` and `Content-Type` headers. Most HTTP clients add these headers automatically. ## Pagination If you specify `maxPageSize` in your request, some methods return paginated results—essentially partial responses: ```json GET /cloud/v2/users/{user_id}/inventory-items?maxPageSize=25 { "inventoryItems": [ ... ], "nextPageToken": "aaaBBB" } ``` ```json GET /cloud/v2/universes/{universe_id}/data-stores?maxPageSize=25 { "dataStores": [ ... ], "nextPageToken": "datastore1" } ``` If a response includes a value for `nextPageToken`, use that value in the `pageToken` parameter of the subsequent request to retrieve the next page. When `nextPageToken` is empty or omitted entirely, you've reached the end of your results: ```json GET /cloud/v2/users/{user_id}/inventory-items?maxPageSize=25&pageToken=aaaBBB { "inventoryItems": [ ... ], "nextPageToken": "" } ``` ```json GET /cloud/v2/universes/{universe_id}/data-stores?maxPageSize=25&pageToken=datastore1 { "dataStores": [ ... ] } ``` Aside from the `pageToken`, you must use the same query for pagination to work properly. Altering any filter parameter results in a 400 error. ## Open Cloud v2 ### Multiple paths Some resources have multiple path patterns, visible under the **Resource Paths** header in the API reference. For example, the URL for [List User Restrictions](/docs/en-us/cloud/reference/UserRestriction.md#Cloud_ListUserRestrictions__Using_Universes) can be either of the following: - `https://apis.roblox.com/cloud/cloud/v2/universes/{universe_id}/user-restrictions` - `https://apis.roblox.com/cloud/cloud/v2/universes/{universe_id}/places/{place_id}/user-restrictions` You can probably infer the difference between the two: some user restrictions apply to an entire universe (experience), whereas others apply to specific places within a universe. Aside from the small addition to the path and extra path parameter, the calls are identical. Many endpoints return a path as part of their response, which you can use to make further requests. If an API needs more than a few seconds to fulfill a request, it often returns an [operation](#long-running-operations) rather than the resource or response itself. ### Long running operations Some methods return an `Operation` object that represents a long-running request that returns an asynchronous response later. The object contains the following fields: - **path** - The endpoint path to call to poll for the request's completion. Append the path to the original base URL of the resource method. - **done** - A boolean value that represents whether or not the operation has completed. - **response** - The response object. This field is empty until the `done` field has a value of `true`. - **metadata** - Custom metadata specific to the request being made. ```json { "path": "v1/assets/12345/operation/xyz", "done": true, "response": { "value1": "myValue", "value2": 1234 }, "metadata": { "metadata1": "string", "metadata2": 5678 } } ``` Use the `Operation` object's path to poll for when the resource is ready. A good strategy is to use exponential backoff. For example, you might poll immediately, then after one second, two seconds, four seconds, etc. ```python def PollForResults(operationPath): currentRetries = 0 maxRetries = 10 retryPollingDelay = 1 retryPollingMultiplier = 2 while (currentRetries < maxRetries): # No delay on the first check if (currentRetries == 0): results = GetOperation(operationPath) # Retry logic for subsequent checks else: time.sleep(retryPollingDelay) results = GetOperation(operationPath) # Exponential backoff retryPollingDelay *= retryPollingMultiplier # Check for results and return if they exist if (results.status_code != 200 or results.json()[doneJSONKey]): return results # Otherwise, increment the retry count else: currentRetries += 1 ``` For a more complete code sample that uses a fixed retry interval rather than exponential backoff, see [Poll for results](/docs/en-us/cloud/guides/instance.md#poll-for-results). ### Filtering Some methods let you filter the response by including a `filter` parameter in the request. The following sections describe filter syntax and guidelines for the specified endpoints. #### List group memberships The wildcard character `-` can be used in place of group ID in order to list memberships across all groups: `groups/-/memberships`. Filtering conforms to Common Expression Language (CEL). Only two operators are supported: - `==` - `in [...]` If you specify a group ID in the resource path, you can filter memberships by either user or role in the following formats: - **User filter**: `filter=user == 'users/9876543210'` - **Role filter**: `filter=role == 'groups/123/roles/7920705'` If you specify the wildcard character for the group ID, you must filter memberships by user (up to 50) in the following format: - **User filter**: `filter=user in ['users/1', 'users/156', 'users/9876543210', ...]` #### List inventory items You can filter by collectible status, inventory item type, and inventory item ID. If you don't provide a filter, the user's entire inventory is returned. The filter format is a semicolon-separated list: `filter={field}={value};{field}={value},{value},...;{field}=...` - `{field}` can be any of the predefined type fields or ID fields in the following tables. - `{value}` can be a string, boolean, or enum. Depending on the field type, this can be a single value (boolean fields) or multiple values (list fields). > **Info:** You can't combine type and ID fields in the same filter. **Type fields** | Filter | Type | Description | | --- | --- | --- | | `badges` | boolean | Include badges in the response. Default is false. | | `gamePasses` | boolean | Include game passes in the response. Default is false. | | `inventoryItemAssetTypes` | See [assetDetails](/docs/en-us/cloud/reference/InventoryItem.md) for the full list of asset types. | Comma-separated list of asset types to include. Default is none. Specify `*` for all asset types. Must have Inventory read scope to filter by purchased places. | | `onlyCollectibles` | boolean | Include **only** collectibles in the response. Default is false. This field must be used with the `inventoryItemAssetTypes` field in order to return items and only returns non-UGC limited items. | | `privateServers` | boolean | Include private servers in the response. Default is false. Must have Inventory read scope to filter by this field. | **ID Fields** | Filter | Type | Description | | --- | --- | --- | | `assetIds` | string | Comma-separated list of numeric asset IDs to include. | | `badgeIds` | string | Comma-separated list of numeric badge IDs to include. | | `gamePassIds` | string | Comma-separated list of numeric game pass IDs to include. | | `privateServerIds` | string | Comma-separated list of numeric private server IDs to include. Must have Inventory read scope to filter by this field. | **Examples** - Returns all collectible items that the user owns:`filter=onlyCollectibles=true;inventoryItemAssetTypes=*` - Returns all items of the specified types:`filter=inventoryItemAssetTypes=HAT,CLASSIC_PANTS,TSHIRT_ACCESSORY` - Returns all items of the listed types or any game passes. Excludes badges from the results (the same behavior as if `badges` wasn't included in the filter):`filter=inventoryItemAssetTypes=HAT,CLASSIC_PANTS,TSHIRT_ACCESSORY;gamePasses=true;badges=false` - Returns assets that match the specified IDs:`filter=assetIds=1,2,3,4` - Returns assets, badges, game passes, and private servers that match the specified IDs:`filter=assetIds=1,2,3,4;badgeIds=1,2,3,4;gamePassIds=1,2,3,4;privateServerIds=1,2,3,4` --- title: "Rate limits" url: /docs/en-us/cloud/reference/rate-limits last_updated: 2026-06-29T19:33:58Z description: "Explains rate limiting on Open Cloud endpoints." --- # Rate limits Rate limits control the number of requests you can make to endpoints within a specific time window. These limits help ensure service stability and protect Roblox resources from abuse. ## Discovery There are two ways to determine rate limits: the reference documentation and response headers. ### Documentation pages Public rate limits can be found under the **Limits** section for each endpoint on their documentation pages. View these sections to understand the rate limits that apply before making requests. > **Info:** We strive to be as transparent as possible about rate limits, but additional, undocumented limits may apply, including for DDoS protection and service stability. Always ensure your application handles HTTP 429 rate limit responses. See [Handling rate limits](#handling-rate-limits) for guidance. > > If you hit an undocumented rate limit that blocks your use case (or you would like higher limits for an endpoint), please leave feedback explaining your needs in the [Developer Forum](https://devforum.roblox.com/). ### Response headers Many API responses include headers that provide information about your current rate limit status. | Header | Description | | --- | --- | | `x-ratelimit-limit` | The total number of requests allowed within the rate limit window. This may include multiple limit values for different time windows. | | `x-ratelimit-remaining` | The number of requests you can still make with your current authentication. When this reaches 0, you receive HTTP 429 responses. | | `x-ratelimit-reset` | The number of seconds remaining until your rate limit quota resets to the full limit. | Here's an example of rate limit headers in an API response: ```text x-ratelimit-limit: 1000, 1000;w=60, 1000;w=60 x-ratelimit-remaining: 998 x-ratelimit-reset: 20 ``` In this example: - You have 998 requests remaining out of your total limit. - Your quota will reset in 20 seconds. ## Behavior Rate limit behavior differs by authentication method and request origin, including whether you make requests from within a Roblox experience. ### API key Rate limits for API keys are applied across all API keys per owner, which can be either a user or a group. These limits are enforced consistently regardless of where the requests originate—whether from `Class.HttpService`, web applications, or other sources. #### In-experience with HttpService When calling endpoints in-experience using `Class.HttpService`, requests also contribute to the fixed limit of 500 HTTP requests per minute per Roblox game server. See [In-experience HTTP requests](/docs/en-us/cloud-services/http-service.md) for more details. ### OAuth 2.0 OAuth 2.0 rate limits are applied per access token. Since each access token represents a unique combination of user and application, every user using your app receives their own independent rate limit quota. ## Handling rate limits Write your code to expect and handle HTTP 429 (Too Many Requests) responses due to documented limits, abuse prevention, or high-load scenarios. When rate limited, check the `retry-after` response header as a guideline for when to retry. If there is no `retry-after` response header present, implement an exponential backoff retry strategy. --- title: "Risk levels" url: /docs/en-us/cloud/reference/risk-levels last_updated: 2026-06-29T19:33:58Z description: "Explains the risk level classifications for Open Cloud API endpoints and their behavior in the \"try it out\" feature." --- # Risk levels Risk levels classify Open Cloud endpoints based on the potential impact of their operations. These classifications apply to the OAuth 2.0 authorization flow and the "try it out" feature here in the API documentation. Risk levels help protect your data and account when testing API endpoints in the documentation. Always review an endpoint's functionality and potential impact before testing, particularly for medium and high risk operations that can modify your data. ## Try it out risk levels The "try it out" feature uses four risk levels, which control the warnings and restrictions that you encounter when testing endpoints directly from the documentation. | Risk level | "Try it out" allowed | Warning dialog | Warning banner | Cookie attachment | | --- | --- | --- | --- | --- | | Low | ✅ | No | Yes | Yes | | Medium | ✅ | Yes (per endpoint, per session) | Yes | Yes | | High | ✅ | Yes (per endpoint, per session) | Yes | Yes (after accepting second warning dialog) | | Critical | ❌ | N/A | N/A | N/A | - **Low risk** endpoints primarily retrieve publicly available information and have minimal impact on your data or experiences. These endpoints show a simple informational banner when using "try it out." - **Medium risk** endpoints can create, update, or access private information. Most changes you make from these endpoints can be reversed using other available endpoints. These endpoints have the following "try it out" safety measures: - They display a confirmation dialog before you send a request. This dialog appears once per endpoint per browser session. - They show a warning banner to remind you of the risk level. - **High risk** endpoints can modify or delete private information in ways that may be difficult or impossible to reverse. This category includes most DELETE operations and other potentially destructive actions. In addition to the confirmation dialog and warning banner from medium risk endpoints, a second confirmation dialog is required to send the first request (enforced only per session, not per endpoint). - **Critical risk** endpoints access or modify highly sensitive data that could compromise account security or privacy. "Try it out" is disabled for these endpoints. --- title: "Scopes" url: /docs/en-us/cloud/reference/scopes last_updated: 2026-06-29T19:33:58Z description: "List of Roblox Cloud scopes and their corresponding endpoints." --- # Scopes This page lists all Open Cloud scopes and the API endpoints that they support. It also lists the target types that are supported for each scope. --- title: "Types" url: /docs/en-us/cloud/reference/types last_updated: 2026-06-29T19:33:58Z description: "Explains the different data types of Open Cloud web APIs." --- # Types Open Cloud represents request and response payloads as [standard JSON](https://www.ecma-international.org/publications-and-standards/standards/ecma-404/). The standards JSON types are text, values, objects, arrays, numbers, and strings. Some types have special considerations for representing specific kinds of data, which are described in the following sections. ## Timestamp Uses RFC 3339, where generated output will always be Z-normalized and uses 0, 3, 6 or 9 fractional digits. Offsets other than "Z" are also accepted. ```json { "timestamp": "1972-01-01T10:00:20.021Z" } ``` ## Duration Generated output always contains 0, 3, 6, or 9 fractional digits, depending on required precision, followed by the suffix "s". Accepted are any fractional digits (also none) as long as they fit into nano-seconds precision and the suffix "s" is required. The range must be from -315,576,000,000 to 315,576,000,000 seconds, inclusive. ```json { "duration-9": "1.000340012s", "duration-0": "1s" } ``` ## Bytes Byte data are encoded as a string using standard base64 encoding with paddings. Either standard or URL-safe base64 encoding with or without paddings are supported. ```json { "bytes": "YWJjMTIzIT8kKiYoKSctRbLx+" } ``` ## FieldMask A FieldMask is a string that describes the fields to act on when making a request. To construct a field mask, you specify comma delimited JSON field names in a string. For example, given the following: ```json { "foo": { "a": "c", "b": "d" }, "bar": "x", "baz": "y" } ``` If you wanted to specify a field mask to update the values of only `foo.b` and `bar`, the field mask would look like: `foo.b, bar` In Open Cloud, update methods that support a field mask have a parameter named `updateMask`, where you can specify a field mask as a value. ## Money Generally used to define a price, the `Money` type has a three-letter currency code (as defined in ISO 4217) and a quantity, which uses the `Decimal` type. For example, you might represent a $17.99 price like this: ```json "myPrice": { "currencyCode": "USD", "quantity": { "significand": 1799, "exponent": -2 } } ``` Creator Store products require an `exponent` of -9, so the same price looks like this: ```json "myPrice": { "currencyCode": "USD", "quantity": { "significand": 17990000000, "exponent": -9 } } ``` ## Decimal Represents a decimal number in a form similar to scientific notation, with significant digits and an exponent. Examples: - 17`{"significand": 17, "exponent": 0}` or just `{"significand": 17}` - -0.005`{"significand": -5, "exponent": -3}` - 33.5 million (33,500,000)`{"significand": 335, "exponent": 5}` - 11/8 (1.375)`{"significand": 1375, "exponent": -3}` When `exponent` is greater than 0, it represents the number of trailing zeroes after the significant digits. When `exponent` is less than 0, it represents how many of the significant digits come after the decimal point. When `exponent` is 0, the value of the `Decimal` is the value of the `significand`. --- title: "Automate right to erasure requests" url: /docs/en-us/cloud/webhooks/automate-right-to-erasure last_updated: 2026-06-29T19:33:58Z description: "Explains how to automate Right to Erasure requests with webhooks and Open Cloud APIs for data stores." --- # Automate right to erasure requests The **General Data Protection Regulation (GDPR)** is a European regulation on data protection and privacy. It grants individuals the right to request the deletion of their personal data, known as the [right to erasure](https://gdpr-info.eu/art-17-gdpr/). If you store any **Personally Identifiable Information (PII)** of your users, such as their User IDs, you must comply with GDPR requirements by deleting this information upon receiving a user's request. Instead of handling requests manually, you can [set up a webhook](/docs/en-us/cloud/webhooks/webhook-notifications.md) and use a bot within a third-party messaging application to automate the process. As [data stores](/docs/en-us/cloud-services/data-stores.md) being the most common way for storing PII data, this tutorial provides an example on how to create a bot within Discord that uses the [Open Cloud API for data stores](/docs/en-us/cloud/guides/data-stores.md) to delete PII data as an automation solution. ## Workflow Upon completing this tutorial, you should be able to create a locally-running custom program that automates the handling of right to erasure requests from users. The workflow for this process is as follows: 1. Roblox Support receives a right to erasure request from a user. 2. Roblox webhook is triggered, containing the User ID and a list of Start Place IDs for the experiences they have joined in the payload. 3. Your bot listens for these webhook notifications, verifies their authenticity, and utilizes the [Open Cloud API for data stores](/docs/en-us/cloud/guides/data-stores.md) to delete the PII data stored in data stores. > **Warning:** To use this solution, make sure your data store keys are identifiable by User IDs, such as containing User IDs as substrings, or you need to modify the scripts to match your own data schema. 4. The bot responds to the webhook message in Discord with the deletion status. ## Configure a webhook with third-party integration Before creating a bot, set up a server with webhook integration on the third-party messaging application. Then use the server to configure a webhook on Creator Dashboard. ### Set up a server The following steps show how to set up the server using Discord. 1. Create a new Discord server. If you are unfamiliar with the process, see [Discord Support](https://support.discord.com/hc/en-us/articles/204849977-How-do-I-create-a-server-). > **Info:** It's recommended to set your server as a private server to protect user security. See [Discord Support](https://support.discord.com/hc/en-us/articles/206143407-How-do-I-set-up-a-private-server-) if you are unfamiliar with the process. 2. The server automatically creates a **#general** channel as your default channel. Click the **Edit Channel** icon of the **#general** channel. 3. Under **Permissions**, set the channel to private. 4. Create a webhook integration with the new server, name it to `GDPR Hook`. If you are unfamiliar with the process, see [Discord Support](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks). 5. Copy the webhook URL and store it in a secure place. Only allow trusted team members to access it, as leaking the URL can enable bad actors to send fake messages and potentially delete your user data. ### Configure a webhook on Roblox After obtaining the third-party server URL, use it to [configure a webhook](/docs/en-us/cloud/webhooks/webhook-notifications.md#configure-webhooks-on-creator-dashboard) on Creator Dashboard. make sure you perform the following settings: > **Info:** Currently, only group owners can receive Right to Erasure requests for group-owned experiences. To implement the automation solution for a group-owned experience, make sure that the group owner configures the webhook. - Add the Discord server URL as the **Webhook URL**. - Include a custom **Secret**. Though a secret is optional for completing the configuration, you should include one to prevent bad actors from impersonating Roblox and deleting your data. For more information on the usage of a secret, see [Verify webhook security](/docs/en-us/cloud/webhooks/webhook-notifications.md#verifying-webhook-security). - Select **Right to Erasure Request** under **Triggers**. You can test the webhook using the **Test Response** button to see if you receive a notification in your server's **#general** channel from Roblox. If you don't receive the notification, try again or check your server settings to troubleshoot the error. ## Configure a bot After you add the webhook, use it to configure the bot with the following steps. For more information, see the [Discord documentation](https://discord.com/developers/docs/topics/oauth2#bot-vs-user-accounts). 1. Navigate to the [Applications page](https://discord.com/developers/applications). 2. Create a new application and name it to `GDPR Bot`. 3. The system redirects you to the **General Information** settings of the bot. Copy and save its application ID in a secure place. 4. Under the settings menu, select **OAuth2**. 5. Navigate to the **OAuth2** settings 1. Enable the **bot** scope, a list of additional Bot Permissions displays. 2. Add the **Administrator** permission. Save your changes. 3. Save the generated URL. 6. Copy the generated URL. Leave the application settings page unclosed. 7. Navigate to the generated URL. Select the target server. Click the **Continue** button and then the **Authorize** button. 8. Navigate back to application settings page and navigate to the Bot settings. 9. Under the **Privileged Gateway Intents** section, enable **Message Content Intent**. 10. In the Bot settings > **Build-A-Bot** section, save the bot token in a secure place for later steps. If you don't see the token, click the **Reset Token** button to generate a new one. ## Create an Open Cloud API key To allow your third-party bot to access your data stores for storing PII data of users, [create an Open Cloud API key](/docs/en-us/cloud/auth/api-keys.md) that can access your experiences and add the **Delete Entry** permission of data stores for data deletion. If you use ordered data stores for storing PII, you also need to add the **Write** permission of ordered data stores. After completion, copy and save the API key in a secure location to use it in later steps. ## Obtain identifiers of experiences and places For the bot to locate the PII data requested by users for deletion, obtain the following identifiers of all experiences that you intend to use the bot for: - The **Universe ID**, the unique identifier of your experience. - The **Start Place ID**, the unique identifier of the start place of an experience. To obtain these identifiers: 1. Navigate to the [Creator Dashboard](https://create.roblox.com/dashboard/creations). 2. Hover over an experience's thumbnail, click the **⋯** button, and select **Copy Universe ID** and **Copy Start Place ID**, respectively. ## Add scripts After you finish setting up the webhook, bot, and API key for data stores, add them to the scripts that implement the bot's automation logic. The following example uses Python 3. 1. Install Python libraries using the following commands:```bash pip3 install discord pip3 install requests pip3 install urllib3==1.26.6 ``` 2. Copy and save the following scripts corresponding to different parts of the bot logic in the same directory:```python BOT_TOKEN = "" OPEN_CLOUD_API_KEY = "" ROBLOX_WEBHOOK_SECRET = "" # Dictionary of the Start place ID to # (universe ID, list of (data stores name, scope, and entry key)) for # Standard Data Stores # User data stored under these entries will be deleted STANDARD_DATA_STORE_ENTRIES = { # Start Place ID 111111111: ( # Universe ID 222222222, [ ("StandardDataStore1", "Scope1", "Key1_{user_id}"), ("StandardDataStore1", "Scope1", "Key2_{user_id}"), ("StandardDataStore2", "Scope1", "Key3_{user_id}") ] ), 33333333: ( 444444444, [ ("StandardDataStore3", "Scope1", "Key1_{user_id}") ] ) } # Dictionary of the Start place ID to # (universe ID, list of (data stores name, scope, and entry key)) for # Ordered Data Stores # User data stored under these entries will be deleted ORDERED_DATA_STORE_ENTRIES = { 111111111: ( 222222222, [ ("OrderedDataStore1", "Scope2", "Key4_{user_id}") ] ) } ``````python import requests import bot_config from collections import defaultdict """ Calls Data Stores Open Cloud API to delete all entries for a user_id configured in STANDARD_DATA_STORE_ENTRIES. Returns a list of successful deletions and failures to delete. """ def delete_standard_data_stores(user_id, start_place_ids): successes = defaultdict(list) failures = defaultdict(list) for owned_start_place_id in bot_config.STANDARD_DATA_STORE_ENTRIES: if owned_start_place_id not in start_place_ids: continue universe_id, universe_entries = bot_config.STANDARD_DATA_STORE_ENTRIES[owned_start_place_id] for (data_store_name, scope, entry_key) in universe_entries: entry_key = entry_key.replace("{user_id}", user_id) response = requests.delete( f"https://apis.roblox.com/datastores/v1/universes/{universe_id}/standard-datastores/datastore/entries/entry", headers={"x-api-key": bot_config.OPEN_CLOUD_API_KEY}, params={ "datastoreName": data_store_name, "scope": scope, "entryKey": entry_key } ) if response.status_code in [200, 204]: successes[owned_start_place_id].append((data_store_name, scope, entry_key)) else: failures[owned_start_place_id].append((data_store_name, scope, entry_key)) return successes, failures """ Calls Ordered Data Stores Open Cloud API to delete all entries for a user_id configured in ORDERED_DATA_STORE_ENTRIES. Returns a list of successful deletions and failures to delete. """ def delete_ordered_data_stores(user_id, start_place_ids): successes = defaultdict(list) failures = defaultdict(list) for owned_start_place_id in bot_config.ORDERED_DATA_STORE_ENTRIES: if owned_start_place_id not in start_place_ids: continue universe_id, universe_entries = bot_config.ORDERED_DATA_STORE_ENTRIES[owned_start_place_id] for (data_store_name, scope, entry_key) in universe_entries: entry_key = entry_key.replace("{user_id}", user_id) response = requests.delete( f"https://apis.roblox.com/ordered-data-stores/v1/universes/{universe_id}/orderedDatastores/{data_store_name}/scopes/{scope}/entries/{entry_key}", headers={"x-api-key": bot_config.OPEN_CLOUD_API_KEY} ) if response.status_code in [200, 204, 404]: successes[owned_start_place_id].append((data_store_name, scope, entry_key)) else: failures[owned_start_place_id].append((data_store_name, scope, entry_key)) return successes, failures ``````python import time import hmac import hashlib import re import base64 import bot_config """ Parses received message for Roblox signature and timestamp, the footer is only set if you configured webhook secret """ def parse_footer(message): if not message.embeds[0].footer or \ not message.embeds[0].footer.text: return "", 0 footer_match = re.match( r"Roblox-Signature: (.*), Timestamp: (.*)", message.embeds[0].footer.text ) if not footer_match: return "", 0 else: signature = footer_match.group(1) timestamp = int(footer_match.group(2)) return signature, timestamp """ Verifies Roblox signature with configured secret to check for validity """ def validate_signature(message, signature, timestamp): if not message or not signature or not timestamp: return False # Prevents replay attack within 300 seconds window request_timestamp_ms = timestamp * 1000 window_time_ms = 300 * 1000 oldest_timestamp_allowed = round(time.time() * 1000) - window_time_ms if request_timestamp_ms < oldest_timestamp_allowed: return False # Validates signature timestamp_message = "{}.{}".format(timestamp, message.embeds[0].description) digest = hmac.new( bot_config.ROBLOX_WEBHOOK_SECRET.encode(), msg=timestamp_message.encode(), digestmod=hashlib.sha256 ).digest() validated_signature = base64.b64encode(digest).decode() if signature != validated_signature: return False # Valid signature return True """ Parses a received webhook messaged on Discord. Extracts user ID, prevents replay attack based on timestamp received, and verifies Roblox signature with configured secret to check for validity. """ def parse_message(message): # Parses received message for user ID and game ID if len(message.embeds) != 1 or \ not message.embeds[0].description: return "", [] description_match = re.match( r"You have received a new notification for Right to Erasure for the User Id: (.*) in " + r"the game\(s\) with Ids: (.*)", message.embeds[0].description ) if not description_match: return "", [] user_id = description_match.group(1) start_place_ids = set(int(item.strip()) for item in description_match.group(2).split(",")) signature, timestamp = parse_footer(message) if validate_signature(message, signature, timestamp): return user_id, start_place_ids else: return "", [] ``````python import discord import bot_config import data_stores_api import message_parser def run(): intents = discord.Intents.default() intents.message_content = True client = discord.Client(intents=intents) @client.event async def on_ready(): print(f"{client.user} is listening to Right to Erasure messages") """ Handler for webhook messages from Roblox """ @client.event async def on_message(message): # Parses and validates message user_id, start_place_ids = message_parser.parse_message(message) if not user_id or not start_place_ids: return # Deletes standard data stores user data [successes, failures] = data_stores_api.delete_standard_data_stores(user_id, start_place_ids) if successes: await message.reply(f"Deleted standard data stores data for " + f"user ID: {user_id}, data: {dict(successes)}") if failures: await message.reply(f"Failed to delete standard data stores data for " + f"user ID: {user_id}, data: {dict(failures)}") # Deletes ordered data stores user data [successes, failures] = data_stores_api.delete_ordered_data_stores(user_id, start_place_ids) if successes: await message.reply(f"Deleted ordered data stores data for " + f"user ID: {user_id}, data: {dict(successes)}") if failures: await message.reply(f"Failed to delete ordered data stores data for " + f"user ID: {user_id}, data: {dict(failures)}") client.run(bot_config.BOT_TOKEN) if __name__ == "__main__": run() ``` 3. On the `bot_config.py` file for main configuration of the bot: 1. Set `BOT_TOKEN` to the token generated by your bot. 2. Set `OPEN_CLOUD_API_KEY` as the API key you created. 3. Set `ROBLOX_WEBHOOK_SECRET` as the secret you set when configuring the webhook on Creator Dashboard. 4. In `STANDARD_DATA_STORE_ENTRIES` and `ORDERED_DATA_STORE_ENTRIES` dictionaries for locating the data store of each record to delete: 1. Add your copied Start Place IDs as keys. 2. Add Universe IDs as the first element of the tuple value. 3. Replace the second element of the tuple with the name, scope, entry key name, and associated User ID of your data stores. If you use a different data schema, modify to match your own data schema accordingly. 4. Execute the following command to run the bot:```bash python3 discord_bot.py ``` 5. The bot then starts to listen and verify Roblox webhooks for right to erasure Requests and calls the Open Cloud endpoint for deleting the corresponding data store. > **Warning:** To ensure constant and secure execution of the scripts, save and run them locally only. Keep your local device or virtual machine running the scripts turned on at all times. In the event that your device goes offline, you need to manually manage any missed messages during the offline period and handle delivery failures according to the [retry policy](/docs/en-us/cloud/webhooks/webhook-notifications.md#delivery-failure-retry-policy). ## Test You can create and run a test message to verify that your custom program can properly handle right to erasure requests and delete PII data: 1. Send an HTTP `POST` request to your Discord webhook server with the following request body:```bash curl -X POST {serverUrl} -H 'Content-Type: application/json' -d '{ "embeds":[{ "title":"RightToErasureRequest", "description":"You have received a new notification for Right to Erasure for the User Id: {userIds} in the game(s) with Ids: {gameIds}", "footer":{ "icon_url":"https://create.roblox.com/dashboard/assets/webhooks/roblox_logo_metal.png", "text":"Roblox-Signature: {robloxSignature}, Timestamp: {timestamp}" } }] }' ``` 2. If you have a webhook secret: 1. Generate a `Roblox-Signature` by applying HMAC-SHA256 encoding to your webhook secret key. 2. Set the current time using UTC timestamp in seconds as `Timestamp`. 3. Put together the `description` in the following format:```plain {Timestamp}. You have received a new notification for Right to Erasure for the User Id: {userId} in the game(s) with Ids: {gameIds}\`. ``` For example:```plain 1683927229. You have received a new notification for Right to Erasure for the User Id: 2425654247 in the game(s) with Ids: 10539205763, 13260950955 ``` Your program should be able to identify that your message is from the official Roblox source since you encoded the message with your secret. It should then delete the PII data associated with your request. ```json { "embeds": [ { "title": "RightToErasureRequest", "description": "You have received a new notification for Right to Erasure for the User Id: 2425654247 in the game(s) with Ids: 10539205763, 13260950955", "footer": { "icon_url": "https://create.roblox.com/dashboard/assets/webhooks/roblox_logo_metal.png", "text": "Roblox-Signature: UIe6GJ78MHCmU/zUKBYP3LV0lAqwWRFR6UEfPt1xBFw=, Timestamp: 1683927229" } } ] } ``` --- title: "Webhook notifications" url: /docs/en-us/cloud/webhooks/webhook-notifications last_updated: 2026-06-29T19:33:58Z description: "Explains how to set up webhooks to automate your notification management workflow." --- # Webhook notifications Instead of manually monitoring all events in your experience and requests from users, you can set up webhooks to receive real-time notifications on a third-party messaging tool or your custom endpoint that can receive HTTP requests. This helps you automate your notification management workflow to reduce manual effort handling notifications. > **Info:** Currently, Roblox fully supports webhook notifications for Discord and Slack. Using webhooks with other third-party tools carries the risk of not receiving all notifications. ## Webhook workflow Webhooks send real-time notifications or data between two different applications or services, such as Roblox and a third-party messaging tool. Unlike traditional APIs, which require you to set up a client application to send requests to a server to receive data, webhooks send data to your client endpoint as soon as an event occurs. They're useful for automating workflows between Roblox and third-party applications that you use for collaborating with your team, as they allow for real-time data sharing and processing. Once you set up a webhook, whenever a target event occurs, Roblox sends a request to the webhook URL you provide. The webhook URL then redirects the request to your receiving application or custom endpoint, which can take action based on the data included in the webhook payload. This could include erasing data for GDPR, sending a confirmation to the user, or triggering another event. ## Supported triggers Roblox currently supports the following event triggers. ### Subscription - **Subscription Resubscribed** - When a user resubscribes to a subscription, a message is sent containing the subscription and subscriber. - **Subscription Renewed** - When a user renews a subscription, a message is sent containing the subscription and subscriber. - **Subscription Refunded** - When a user receives a refund for their subscription, a message is sent containing the subscription and subscriber. - **Subscription Purchased** - When a user purchases a subscription, a message is sent containing the subscription and subscriber. - **Subscription Cancelled** - When a user cancels a [subscription](/docs/en-us/production/monetization/subscriptions.md), a message is sent containing the subscription and subscriber, as well as the reason given for the cancellation. For more information on subscription events and their fields, see the [Subscription](/docs/en-us/cloud/reference/Subscription.md) reference. ### Compliance - **Right to Erasure Request** - When a user submits a data deletion request under the [General Data Protection Regulation](https://gdpr.eu/right-to-be-forgotten/) (GDPR). ### Commerce - **Commerce Product Order Refunded** - When a user has received a refund for their commerce product order, or the order was cancelled. - **Commerce Product Order Paid** - When a user has paid for their commerce product order. Please note that duplicate webhook events are possible, so you should deduplicate events using the unique commerce order ID. ## Configure webhooks on Creator Dashboard To receive notifications through webhooks, you need to configure a webhook that subscribes to certain events for triggering notifications. For group-owned experiences, only group owners can configure and receive webhook notifications. > **Info:** If you're setting up webhooks and handling personal data, ensure they comply with the [General Data Protection Regulation (GDPR)](https://gdpr-info.eu/). To set up a webhook: 1. Select your experience on [Creator Hub](https://create.roblox.com/). 2. Under **Configure**, select **Webhooks** and click **Add Webhook**. The webhook URL comes from your provider. For example, a Slack URL probably looks like this:```text https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX ``` 3. Enter your webhook URL and a name. 4. (Optional) Include a secret, which helps ensure that the notifications you receive are coming from Roblox. For more information, see [Verify webhook security](#verify-webhook-security). 5. Choose one or more options from the list of [supported triggers](#supported-triggers) of events for which you want to receive notifications. 6. (Optional) Use the **Test Response** button to check if your service can receive a sample request. 7. Click **Save Changes**. > **Info:** Currently, you can configure up to 5 webhooks in total. ## Set up webhook URLs You can set up a custom HTTP service endpoint as your webhook URL, provided it fulfills the following requirements: - It must be publicly accessible for handling requests. - It can handle POST requests. - It can respond to the request with a 2XX response within 5 seconds. - It can handle HTTPS requests. When your endpoint receives a POST request, it must be able to: - Extract the details required about the notification from the body of the POST message. - Read the body of the POST message with the generic details on the notification and specific details related to the event type on the notification. For more information of the schema of POST requests to handle, see the [Payload Schema](#payload-schema). ### Delivery failure retry policy When a webhook notification fails to reach your specified URL due to errors such as endpoint unavailability, Roblox retries sending the message to the configured URL 5 times using a fixed window size. If the notification still fails to be delivered after 5 attempts, Roblox stops trying to send the notification and assumes that the URL is no longer valid. In this situation, you need to update your webhook configuration with a new URL that is reachable and able to receive notifications. To troubleshoot and confirm that your webhook URL can successfully receive notifications, see [Test webhooks](#test-webhooks). ### Third-party requirements Third-party tools usually have their own requirements for webhooks that you need to follow when setting up your webhook URL. You can find these requirements by searching for the keyword "webhook" on the support or documentation site of the target tool. For the supported third-party tools, see the following: - [Discord Help Center](https://support.discord.com/hc/en-us) - [Slack API Reference](https://api.slack.com/) ## Test webhooks You can test whether the webhook you've configured can successfully receive notifications on the [Creator Dashboard](https://create.roblox.com/dashboard/creations): 1. Navigate to the [Webhooks](https://create.roblox.com/settings/webhooks) configuration page. 2. Select the webhook you want to test from the list of configured webhooks. 3. Click the pencil icon next to the target webhook. 4. Click the **Test Response** button. The system then sends a `SampleNotification` event, which includes the **User ID** of the user who triggered the notification, as shown here: ```json { "NotificationId": "string", "EventType": "SampleNotification", "EventTime": "2023-12-30T16:24:24.2118874Z", "EventPayload": { "UserId": 1 } } ``` If you are integrating your webhook with a third-party service, you can test it using the third-party URL to confirm that the service can successfully receive notifications from your webhook. If you provide a secret when configuring the webhook, it also generates a `roblox-signature` that you can use to test the `roblox-signature` logic. ## Verify webhook security After you configure your server to receive payloads, it starts to listen for any payload sent to the endpoint. If you set a secret when configuring your webhook, Roblox sends a `roblox-signature` in each webhook notification to ensure that the request actually came from Roblox. The signature is in the payload header for custom endpoints and in the footer for third-party servers. ```csv t=,v1= ``` If you did not set a secret for your webhook, the signature only contains the timestamp of when the notification was sent: ```csv t= ``` To verify a signature: #### Custom endpoints 1. Extract the timestamp and signature values. All signatures for webhooks with secrets share the same format as a CSV string with these two values following by the prefixes: - `t`: The timestamp of when the notification was sent. - `v1`: The signature value generated using the secret provided by the Creator Dashboard configuration. 2. Re-create the base string of `roblox-signature` by concatenating: 1. The timestamp as a string. 2. The period character `.`. 3. The JSON string of the request body. 3. Compute a hash-based message authentication code (HMAC) with the SHA256 hash function using the secret you defined during the configuration as the key and the base string you generated through step 2 as the message. Convert the result to Base64 format to get the expected signature. 4. Compare the extracted signature value to the expected signature. If you generated the signature correctly, the value should be the same. 5. (Optional) To prevent replay attacks, a type of cyber attack where attackers intercept and resend data to gain unauthorized access or perform malicious actions, it's helpful to compare the extracted timestamp value with the current timestamp and ensure it falls within a reasonable time limit. For example, a 10-minute window is usually a good reasonable time limit. #### Third-party servers 1. Extract the timestamp value from the `Timestamp` footer of the notification. 2. Extract the message body from the notification. An example message body might look like this:```text You have received a new notification for Right to Erasure for the User Id: XXXXXXXX in the game(s) with Ids: YYYYYYY, ZZZZZZZZZ ``` 3. Concatenate the timestamp value and the message with a period character in the format as:```text 168487229.You have received a new notification for Right to Erasure for the User Id: XXXXXXXX in the game(s) with Ids: YYYYYYY, ZZZZZZZZZ ``` 4. Compute a Hash-based message authentication code (HMAC) with the SHA256 hash function using the secret you defined during the configuration as the key and the base string you generated through step 2 as the message. Convert the result to Base64 format to get the expected signature. 5. Compare the extracted signature value to the expected signature. If you generated the signature correctly, the value should be the same. 6. **(Optional)** To prevent replay attacks, a type of cyber attack where attackers intercept and resend data to gain unauthorized access or perform malicious actions, it's helpful to compare the extracted timestamp value with the current timestamp and ensure it falls within a reasonable time limit. For example, a 10-minute window is usually a good reasonable time limit. ## Payload schema When the target event of your webhook is triggered, it sends a request to your webhook URL, including information about the event in the payload. All payloads of requests share the same schema that consists of fixed and variable fields. This ensures that the data transmitted in the payload is structured and consistent, making it easier for the receiving application to process and use the data. The **fixed payload schema fields** can help maintain consistency across all webhook requests, with the following fields available: 1. `NotificationId` (string): A unique identifier for each notification sent. If the same `NotificationId` is received twice, it is considered a duplicate. 2. `EventType` (string): Indicates the type of event for which the notification was triggered. 3. `EventTime` (string): The timestamp of when the event was triggered. The **variable payload schema fields** provides flexibility for webhooks to accommodate various types of events, which include: 1. `EventPayload` (object): Contains information specific to the `EventType` that triggered the webhook. The structure of the `EventPayload` schema varies based on the type of event. The following example shows the payload schema of the **Right To Erasure request** event: ```json { "NotificationId": "string", "EventType": "RightToErasureRequest", "EventTime": "2023-12-30T16:24:24.2118874Z", "EventPayload": { "UserId": 1, "GameIds": [ 1234, 2345 ] } } ``` ## Handle notifications If you store any **Personally Identifiable Information (PII)** of your users, such as their User IDs, you must delete this information when a user submits such a request to comply with the GDPR [right to erasure](https://gdpr-info.eu/art-17-gdpr/) compliance requirements. You can create a bot to handle webhook notifications and help automate data deletion, provided you're storing PII in a data store. See [Automating Right to Erasure Requests Deletion](/docs/en-us/cloud/webhooks/automate-right-to-erasure.md) for an example on how to create a bot within Discord that uses the [Open Cloud API for data stores](/docs/en-us/cloud/guides/data-stores.md) to delete PII data as an automation solution. This example can be adapted for handling other notifications, such as subscription events. If you use a custom endpoint as your webhook server instead of a third-party tool, you can extract the data subject to deletion from the webhook payload and build your own automation solution. The following code sample is an example of a server that has prevention against replay attacks by verifying the timestamp and that the request is coming from Roblox: ```javascript const crypto = require('crypto'); const express = require('express'); const secret = '' // This can be set as an environment variable let app = express(); app.use(express.json()); app.all('/*', function (req, res) { console.log('New request received'); // Extract the timestamp and signature from header const signatureHeader = req.headers['roblox-signature'].split(','); const timestamp = signatureHeader.find(e => e.startsWith('t=')).substring(2); const signature = signatureHeader.find(e => e.startsWith('v1=')).substring(3); // Ensure the request came within a 300 second window to prevent replay attacks const requestTimestampMs = timestamp * 1000; const windowTimeMs = 300 * 1000; const oldestTimestampAllowed = Date.now() - windowTimeMs; if (requestTimestampMs < oldestTimestampAllowed) { return res.status(403).send('Expired Request'); } // Validate signature const message = \`${timestamp}.${JSON.stringify(req.body)}\`; const hmac = crypto.createHmac('sha256', secret); const calculatedSignature = hmac.update(message).digest('base64'); if (signature !== calculatedSignature) { return res.status(401).send('Unauthorized Request'); } // Your logic to handle payload const payloadBody = req.body; const eventType = payloadBody['EventType']; if (eventType === 'RightToErasureRequest'){ const userId = payloadBody['EventPayload']['UserId']; const gameIds = payloadBody['EventPayload']['GameIds']; console.log(\`Payload data: UserId=${userId} and GameIds=${gameIds}\`); // If you store PII in data stores, use the UserId and GameIds to delete the information from data stores. } return res.json({ message: 'Processed the message successfully' }); }); app.listen(8080, function () { console.log('Server started'); }); ``` --- title: "Cross-server messaging" url: /docs/en-us/cloud-services/cross-server-messaging last_updated: 2026-06-29T19:33:58Z description: "Cross-server messaging enables you to communicate with other servers or client instances of your experience." --- # Cross-server messaging Normally, the code within an experience can only affect the server or clients that it's running on, but there may be situations where you want different servers to communicate with each other, including: - **Global Announcements** — Send announcements such as "A user found a special item!" to all the experience's servers. - **Real-Time Server Browser** — Compile a list of all the experience's servers and who is in them (updated every minute) and display the list on a maximum of 20 servers. You can support cross-server messaging in your experience using `Class.MessagingService`. You can also use the [Teleportation Playground](https://www.roblox.com/games/3112653247/Teleportation-Playground) sample experience to see how cross‑server messaging works before you implement it. Lastly, see [here](/docs/en-us/cloud/guides/usage-messaging.md) to explore cross‑server communication using external tools. ## Cross-server messaging setup To enable cross-server messaging, you must set up a **topic** which is a customized message channel that's accessible from multiple servers. After you create a topic, you can [subscribe users](#subscribe-users-to-receive-messages) to the topic to receive messages and enable [publishing messages](#publish-messages) to the topic. ### Subscribe users to receive messages Use `Class.MessagingService:SubscribeAsync()` to subscribe users to a topic and specify a callback function that detects messages publishing to that topic. For example, the following code sample subscribes all users to a **FriendServerEvent** topic that receives messages when any user is teleported to a different server. ```lua local MessagingService = game:GetService("MessagingService") local Players = game:GetService("Players") local MESSAGING_TOPIC = "FriendServerEvent" Players.PlayerAdded:Connect(function(player) -- Subscribe to the topic local subscribeSuccess, subscribeConnection = pcall(function() return MessagingService:SubscribeAsync(MESSAGING_TOPIC, function(message) print(message.Data) end) end) if subscribeSuccess then -- Unsubscribe from topic upon player ancestry change player.AncestryChanged:Connect(function() subscribeConnection:Disconnect() end) end end) ``` ### Publish messages Use `Class.MessagingService:PublishAsync()` to match a specific topic and publish a message to it. For example, the following code sample uses `Class.MessagingService:PublishAsync()|PublishAsync()` to notify all users when a user joins a new server, including the `Class.Player.Name` representing the user's display name and the `Class.DataModel.JobId|JobId`, a unique identifier for the running experience server instance. ```lua local MessagingService = game:GetService("MessagingService") local Players = game:GetService("Players") local MESSAGING_TOPIC = "FriendServerEvent" Players.PlayerAdded:Connect(function(player) -- Publish to topic local publishSuccess, publishResult = pcall(function() local message = player.Name .. " joined server with 'JobId' of " .. game.JobId MessagingService:PublishAsync(MESSAGING_TOPIC, message) end) if not publishSuccess then print(publishResult) end end) ``` --- title: "Best practices for data stores" url: /docs/en-us/cloud-services/data-stores/best-practices last_updated: 2026-06-29T19:33:58Z description: "Guidelines that help you manage your data and data stores (DataStores) more efficiently." --- # Best practices for data stores Best practices are guidelines that help you manage your data more efficiently. ## General best practices ### Create fewer data stores Data stores have similar behavior to tables in databases. When you minimize the number of data stores in an experience and put related data in the same data store, you're able to configure each data store individually and improve the service's efficiency to operate the data. ### Use a single object for related data To use the maximum [4 MB object size limit](/docs/en-us/cloud-services/data-stores/error-codes-and-limits.md#throughput-limits) more efficiently, fetch all relevant data in one call. `Class.GlobalDataStore:SetAsync()|SetAsync()` updates all data so that all data for the same user is always in sync. The versioning system versions individual objects instead of the entire data store. This means self-contained objects are consistent when you restore data stores to older versions. ### Use key prefixes to organize your data Filter keys with a specific [prefix](/docs/en-us/cloud-services/data-stores/versioning-listing-and-caching.md#listing-and-prefixes) when calling `Class.DataStore:ListKeysAsync()|ListKeysAsync()`. For example, you can save keys with a prefix like `/User_1234/profiles/warrior` and `/User_1234/profiles/mage` in experiences that support users with multiple character profiles. You can then use a prefix search with `/User_1234/profiles` to get a list of all profiles belonging to that user. ## Optimization best practices Data stores have storage limits that apply per experience. When your experience approaches or exceeds its quota, you should take action to reduce usage and avoid potential additional costs. ### Monitor storage usage Use these tools for data store observability: - **Data Stores Dashboard**, which provides you with a visual overview of your storage usage over time. Use this dashboard to monitor how your storage scales and to identify periods of unexpected growth in usage. - **Data Stores Manager**, which gives you a direct view into your experience's stored data. The Manager includes key metrics like your current storage usage and the total keys you have stored across all of your data stores. If your experience exceeds its quota, the Manager displays an alert and your new estimated monthly cost. If your experience has more than 100 data stores, the Manager might not display their detailed metrics. In this case, consider using programmatic analysis through Open Cloud or batch operations. - **Notifications**, which notify you when your experience approaches or exceeds storage limits. Notifications include guidance and links to dashboards where you can then review and reduce your usage. ### Resolve over-quota usage If your experience exceeds your storage limits, you can: - **Delete unused data stores.** If you're testing or have temporary stores, deleting them regularly helps maintain healthy storage usage. When you delete a data store, it can take Roblox up to 30 days to process your request. - **Delete individual keys.** This is useful when you need to remove temporary items or a certain player's data. - **Use the Batch Processor.** In cases when deleting data manually isn't efficient, you can use the [Batch Processor](https://github.com/Roblox/data-stores-batch-processor-cli) or the Open Cloud Data Stores API to delete data in bulk. ### Use data store versions instead of new keys When saving updates or new versions of player or configuration data, you can use the data stores versioning system instead of creating a new key for each version. Versioning allows you to roll back or inspect previous saves without increasing your total key count or storage footprint. ### Use memory stores for temporary data Use [memory stores](/docs/en-us/cloud-services/memory-stores.md) instead of data stores for caches or ephemeral data. The data in memory stores automatically expires after 45 days, which reduces your long-term storage growth. ### Clean up your data after testing After you're done testing, delete test data stores through the Data Stores Manager. Avoid creating new scopes or entire new data stores just for test data. ### Store player data by key Instead of storing player data by data store, store it by key. Deleting a key is faster than deleting a data store. ### Remove data after events After seasonal events or temporary features, delete unused data through the Batch Processor or Open Cloud APIs. ### Review usage regularly Make sure to review your usage regularly and detect anomalies early with the Data Stores Dashboard. --- title: "Data Stores Manager" url: /docs/en-us/cloud-services/data-stores/data-stores-manager last_updated: 2026-06-29T19:33:58Z description: "The Data Stores Manager helps you monitor your data stores (DataStores) on Creator Hub." --- # Data Stores Manager With the Data Stores Manager, you can browse and monitor your data stores, their key entries, and their storage usage directly on the Creator Hub. ## Access the Data Stores Manager > **Info:** If you're the owner of an experience or the group owner of a group-owned experience, you already have full access to the Data Stores Manager by default. > > To give members of your group access to the Data Stores Manager, you can [grant them one of the following permissions](/docs/en-us/projects/groups.md#roles-and-permissions): - **View Data Stores** lets them view the Data Stores Manager page in the Creator Hub. - **Edit Data Stores** lets them delete keys. - **Delete Data Stores** lets them delete data stores. - **Edit all group experiences** gives them full access to all of the functionality of the Data Stores Manager. To access the Data Stores Manager: 1. Go to Creations and select an experience. 2. Go to **Configure** ⟩ **Data Stores Manager**. ![Overview of Data Stores Manager page displaying the Summary section, the Data Stores list, and the Keys list.](./../../assets/data/data-store/Data-Stores-Manager-Page.png) The **Summary** section of the Data Stores Manager page includes the **Total Size** and the **Storage Limit** of your data stores. Total Size is calculated by adding the number of bytes consumed by all existing keys in your experience, and Storage Limit is calculated based on your experience's lifetime user count. For more information about storage limits, see [Limits](/docs/en-us/cloud-services/data-stores/error-codes-and-limits.md#limits). If your Total Size exceeds your Storage Limit, your **Est. Monthly Costs** will populate in your Summary and show an estimation of the impact of the usage that has gone over your quota. For tips on how to reduce your storage consumption, see [Best practices](/docs/en-us/cloud-services/data-stores/best-practices.md). ## View data stores and keys > **Warning:** If your experience has more than 100 data stores, the data store size and number of keys won't be available in the Data Stores list. The **Data Stores** list displays the name, size, and number of keys for all data stores in your experience. You can filter this list by entering a prefix in the search bar. To further drill down into each specific data store, you can: - Select a data store from the data store list to display a list of all keys in that data store. - Enter a prefix in the search bar to filter the list of keys. The prefix must start with the scope name. - Select a key entry to display that key's value, metadata, version history, status, and when it was last updated. - Select both a key entry and a version, and click **Compare versions** to compare the current key entry version with the version you selected. - Select a previous version of a key and click **Revert** to go back to that version. You must have Edit permissions to do this. Reverting to a previous version conditionally updates the list and writes a new version with the value of the version you have previously selected. ![Comparison of two key entries in a data store.](./../../assets/data/data-store/Data-Stores-Manager-Compare-Versions.png) ## Delete data stores and keys > **Info:** When you schedule a data store for permanent deletion, that data store immediately becomes inaccessible. Any attempts to read or write to its entries will fail. To schedule a data store or key for permanent deletion: 1. Go to Creations and select an experience. 2. Go to **Configure** ⟩ **Data Stores Manager**. 3. Click the **Action** menu next to the item you want to delete. 4. Click **Mark for Deletion**. There is a cooldown period between when you delete a data store or key and when that item is actually deleted. During this cooldown period, you can undo the deletion and restore the item to its normal state. To restore a data store, click the **Action** menu next to the data store you want to restore and then click **Restore**, or call the [`UndeleteDataStore`](/docs/en-us/cloud/reference/DataStore.md#Cloud_UndeleteDataStore) method from the Open Cloud API. To restore a key, call the `Class.GlobalDataStore.UpdateAsync|UpdateAsync` method from the Engine API or the [`UpdateDataStoreEntry`](/docs/en-us/cloud/reference/DataStoreEntry.md#Cloud_UpdateDataStoreEntry__Using_Universes_DataStores) method from the Open Cloud API. --- title: "Data store error codes and limits" url: /docs/en-us/cloud-services/data-stores/error-codes-and-limits last_updated: 2026-06-29T19:33:58Z description: "Error codes you might encounter and limits you might hit when using data stores (DataStores) to store data." --- # Data store error codes and limits Requests you make to data stores can fail due to poor connectivity or other issues. To handle errors and return messages with an error code, wrap data store functions in `Global.LuaGlobals.pcall()`. A failed write call, such as `Class.GlobalDataStore:UpdateAsync()|UpdateAsync()`, means the game server did not receive a successful response. It does not always guarantee that the backend write did not occur. In some failure scenarios, the final write state may be unknown to the caller until it is verified with a follow-up read without cache. ## Error code reference | Error code | Error name | Error message | Notes | | --- | --- | --- | --- | | `101` | `KeyNameEmpty` | Key name can't be empty. | Check if the key input into the data store function is an empty string. | | `102` | `KeyNameLimit` | Key name exceeds the 50 character limit. | Check if the key input into the data store function exceeds a length of 50. | | `103` | `ValueNotAllowed` | Can't allow **X** in `DataStore`. | A bad update function returned a value of type **X**. | | `104` | `CantStoreValue` | Can't store **X** in `DataStore`. | The update function returned a value of type **X** that didn't serialize. | | `105` | `ValueTooLarge` | Serialized value exceeds **X** limit. | If you're setting a value with `Class.GlobalDataStore:SetAsync()\|SetAsync()` or `Class.GlobalDataStore:UpdateAsync()\|UpdateAsync()`, the serialized length of the value can't exceed the size **X**. To check the serialized length of the data, use `Class.HttpService:JSONEncode()\|JSONEncode()`. | | `106` | `MaxValueInvalid` | `MaxValue` must be an integer. | If you're passing a maximum value to `Class.OrderedDataStore:GetSortedAsync()\|GetSortedAsync()` for an `Class.OrderedDataStore`, it must be an integer. | | `106` | `MinValueInvalid` | `MinValue` must be an integer. | If you're passing a minimum value to `Class.OrderedDataStore:GetSortedAsync()\|GetSortedAsync()` for an `Class.OrderedDataStore`, it must be an integer. | | `106` | `PageSizeGreater` | `PageSize` must be within a predefined range. | The minimum page size for an `Class.OrderedDataStore` is 1. | | `106` | `PageSizeLesser` | `PageSize` must be within a predefined range. | The maximum page size for an `Class.OrderedDataStore` is 100. | | `107` | `MinMaxOrderInvalid` | `MaxValue` must be greater than or equal to `MinValue`. | The maximum value must be greater than or equal to the minimum value for `Class.OrderedDataStore:GetSortedAsync()\|GetSortedAsync()`. | | `301` | `GetAsyncThrottle` | `GetAsync` request dropped. Request was throttled but queue was full. | `Class.GlobalDataStore:GetAsync()\|GetAsync()` request has exceeded the maximum queue size and Roblox is unable to process the requests at the current throughput. | | `302` | `SetAsyncThrottle` | `SetAsync` request dropped. Request was throttled but queue was full. | `Class.GlobalDataStore:SetAsync()\|SetAsync()` request has exceeded the maximum queue size and Roblox is unable to process the requests at the current throughput. | | `303` | `IncreAsyncThrottle` | `IncrementAsync` request dropped. Request was throttled but queue was full. | `Class.GlobalDataStore:IncrementAsync()\|IncrementAsync()` request has exceeded the maximum queue size and Roblox is unable to process the requests at the current throughput. | | `304` | `UpdateAsyncThrottle` | `UpdateAsync` request dropped. Request was throttled but queue was full. | `Class.GlobalDataStore:UpdateAsync()\|UpdateAsync()` request has exceeded the maximum queue size and Roblox is unable to process the requests at the current throughput. | | `304` | `TransformThrottle` | `UpdateAsync` request dropped. Request was throttled but queue was full. | `Class.GlobalDataStore:UpdateAsync()\|UpdateAsync()` request has exceeded the maximum queue size and Roblox is unable to process the requests at the current throughput. | | `305` | `GetSortedThrottle` | `GetSorted` request dropped. Request was throttled but queue was full. | `Class.OrderedDataStore:GetSortedAsync()\|GetSortedAsync()` request has exceeded the maximum queue size and Roblox is unable to process the requests at the current throughput. | | `306` | `RemoveAsyncThrottle` | `RemoveAsync` request dropped. Request was throttled but queue was full. | `Class.GlobalDataStore:RemoveAsync()\|RemoveAsync()` request has exceeded the maximum queue size and Roblox is unable to process the requests at the current throughput. | | `401` | `DataModelNoAccess` | Request failed. `DataModel` is inaccessible while the experience is shutting down. | `Class.DataModel` is uninitialized because the experience is shutting down. | | `402` | `LuaWebSrvsNoAccess` | Request failed. `LuaWebService` is inaccessible while the experience is shutting down. | `Class.LuaWebService` is uninitialized because the experience is shutting down. | | `403` | `StudioAccessToApisNotAllowed` | Can't write to `DataStore` from Studio because API access is not enabled. | API access must be active in order to use data stores in Studio. | | `404` | `InternalError` | `OrderedDataStore` doesn't exist. | The `Class.OrderedDataStore` associated with this request wasn't found. This might be a sign of data corruption. Try again later. | | `501` | `InternalError` | Can't parse response because data might be corrupted. | The server was unable to parse the response to your request. This might be a sign of data corruption. Try again later. | | `502` | `RequestRejected` | API Services rejected the request with error **X**. | Error **X** occurred when processing on Roblox servers. Try again later. | | `503` | `InternalError` | Data store request was successful but key wasn't found. | The key requested wasn't found in the data store. This might be a sign of data corruption. Try again later. | | `504` | `InternalError` | Data store request was successful but the response wasn't formatted correctly. | The server was unable to parse the response to your request. This might be a sign of data corruption. Try again later. | | `505` | `InternalError` | `OrderedDataStore` request was successful but the response wasn't formatted correctly. | The server was unable to parse the response to your `Class.OrderedDataStore` request. This might be a sign of data corruption. Try again later. | | `509` | `OperationNotAllowed` | Data store operations are blocked while running on a Personal RCC to prevent possible data corruption. | Data store writes are blocked on private RCC channels. | | `511` | `AttributeSizeTooLarge` | Metadata attribute size exceeds **X** limit. | The serialized metadata size exceeds the limit of **X**. The value **X** is dynamic. If the size changes, the value also changes. | | `512` | `UserIdLimitExceeded` | `UserID` size exceeds **X** limit. | The length of the user IDs array provided by the user exceeds the limit of **X**. | | `513` | `AttributeFormatError` | Attribute `userId` format is invalid. | The user ID provided isn't a number. | | `513` | `AttributeFormatError` | Attribute metadata format is invalid. | The metadata isn't a table. | | | `GetVersionAsyncThrottle` | `GetVersionAsync` request dropped. Request was throttled. | `Class.DataStore:GetVersionAsync()\|GetVersionAsync()` request has exceeded the maximum queue size and Roblox is unable to process the requests at the current throughput. | | | `GetVersionAtTimeAsyncThrottle` | `GetVersionAtTimeAsync` request dropped. Request was throttled. | `Class.DataStore:GetVersionAtTimeAsync()\|GetVersionAtTimeAsync()` request has exceeded the maximum queue size and Roblox is unable to process the requests at the current throughput. | | | `ListDataStoresAsyncThrottle` | `ListDataStoresAsync` request dropped. Request was throttled. | `Class.DataStoreService:ListDataStoresAsync()\|ListDataStoresAsync()` request has exceeded the maximum queue size and Roblox is unable to process the requests at the current throughput. | | | `ListKeysAsyncThrottle` | `ListKeysAsync` request dropped. Request was throttled. | `Class.DataStore:ListKeysAsync()\|ListKeysAsync()` request has exceeded the maximum queue size and Roblox is unable to process the requests at the current throughput. | | | `ListVersionsAsyncThrottle` | `ListVersionsAsync` request dropped. Request was throttled. | `Class.DataStore:ListVersionsAsync()\|ListVersionsAsync()` request has exceeded the maximum queue size and Roblox is unable to process the requests at the current throughput. | | | `RemoveVersionAsyncThrottle` | `RemoveVersionAsync` request dropped. Request was throttled. | `Class.DataStore:RemoveVersionAsync()\|RemoveVersionAsync()` request has exceeded the maximum queue size and Roblox is unable to process the requests at the current throughput. | | | | `InvalidTimestamp` | Timestamp must be positive and not more than ten minutes in the future. | The timestamp provided to `Class.DataStore:GetVersionAtTimeAsync()\|GetVersionAtTimeAsync()` was not valid. | | | | `StandardReadExperienceThrottled` | `StandardRead` request was throttled by experience limits. | A request to `Class.DataStore:GetAsync()\|GetAsync()`, `Class.DataStore:GetVersionAsync()\|GetVersionAsync()`, `Class.DataStore:GetVersionAtTimeAsync()\|GetVersionAtTimeAsync()`, or the read of `Class.DataStore:UpdateAsync()\|UpdateAsync()` on a standard data store exceeded the `StandardRead` experience-level rate limit. | | | `StandardWriteExperienceThrottled` | `StandardWrite` request was throttled by experience limits. | A request to `Class.DataStore:SetAsync()\|SetAsync()`, `Class.DataStore:IncrementAsync()\|IncrementAsync()`, or the write of `Class.DataStore:UpdateAsync()\|UpdateAsync()` on a standard data store exceeded the `StandardWrite` experience-level rate limit. | | | `StandardListExperienceThrottled` | `StandardList` request was throttled by experience limits. | A request to `Class.DataStore:ListKeysAsync()\|ListKeysAsync()`, `Class.DataStore:ListVersionsAsync()\|ListVersionsAsync()`, or `Class.DataStoreService:ListDataStoresAsync()\|ListDataStoresAsync()` on standard data stores exceeded the `StandardList` experience-level rate limit. | | | `StandardRemoveExperienceThrottled` | `StandardRemove` request was throttled by experience limits. | A request to `Class.DataStore:RemoveAsync()\|RemoveAsync()` on a standard data store exceeded the `StandardRemove` experience-level rate limit. | | | `OrderedReadExperienceThrottled` | `OrderedRead` request was throttled by experience limits. | A request to `Class.OrderedDataStore:GetAsync()\|GetAsync()` or the read of `Class.OrderedDataStore:UpdateAsync()\|UpdateAsync()` on an ordered data store exceeded the `OrderedRead` experience-level rate limit. | | | `OrderedWriteExperienceThrottled` | `OrderedWrite` request was throttled by experience limits. | A request to `Class.OrderedDataStore:SetAsync()\|SetAsync()`, `Class.OrderedDataStore:IncrementAsync()\|IncrementAsync()`, or the write of `Class.OrderedDataStore:UpdateAsync()\|UpdateAsync()` on an ordered data store exceeded the `OrderedWrite` experience-level rate limit. | | | `OrderedListExperienceThrottled` | `OrderedList` request was throttled by experience limits. | A request to `Class.OrderedDataStore:GetSortedAsync()\|GetSortedAsync()` on an ordered data store exceeded the `OrderedList` experience-level rate limit. | | | `OrderedRemoveExperienceThrottled` | `OrderedRemove` request was throttled by experience limits. | A request to `Class.OrderedDataStore:RemoveAsync()\|RemoveAsync()` on an ordered data store exceeded the `OrderedRemove` experience-level rate limit. | | | `StandardReadGameServerThrottled` | `StandardRead` request was throttled by game server limits or the request queue was full. | A request to `Class.DataStore:GetAsync()\|GetAsync()`, `Class.DataStore:GetVersionAsync()\|GetVersionAsync()`, `Class.DataStore:GetVersionAtTimeAsync()\|GetVersionAtTimeAsync()`, or the read of `Class.DataStore:UpdateAsync()\|UpdateAsync()` on a standard data store exceeded the `StandardRead` game server-level rate limit. | | | `StandardWriteGameServerThrottled` | `StandardWrite` request was throttled by game server limits or the request queue was full. | A request to `Class.DataStore:SetAsync()\|SetAsync()`, `Class.DataStore:IncrementAsync()\|IncrementAsync()`, or the write of `Class.DataStore:UpdateAsync()\|UpdateAsync()` on a standard data store exceeded the `StandardWrite` game server-level rate limit. | | | `StandardListGameServerThrottled` | `StandardList` request was throttled by game server limits or the request queue was full. | A request to `Class.DataStore:ListKeysAsync()\|ListKeysAsync()`, `Class.DataStore:ListVersionsAsync()\|ListVersionsAsync()`, or `Class.DataStoreService:ListDataStoresAsync()\|ListDataStoresAsync()` on standard data stores exceeded the `StandardList` game server-level rate limit. | | | `StandardRemoveGameServerThrottled` | `StandardRemove` request was throttled by game server limits or the request queue was full. | A request to `Class.DataStore:RemoveAsync()\|RemoveAsync()` on a standard data store exceeded the `StandardRemove` game server-level rate limit. | | | `OrderedReadGameServerThrottled` | `OrderedRead` request was throttled by game server limits or the request queue was full. | A request to `Class.OrderedDataStore:GetAsync()\|GetAsync()` or the read of `Class.OrderedDataStore:UpdateAsync()\|UpdateAsync()` on an ordered data store exceeded the `OrderedRead` game server-level rate limit. | | | `OrderedWriteGameServerThrottled` | `OrderedWrite` request was throttled by game server limits or the request queue was full. | A request to `Class.OrderedDataStore:SetAsync()\|SetAsync()`, `Class.OrderedDataStore:IncrementAsync()\|IncrementAsync()`, or the write of `Class.OrderedDataStore:UpdateAsync()\|UpdateAsync()` on an ordered data store exceeded the `OrderedWrite` game server-level rate limit. | | | `OrderedListGameServerThrottled` | `OrderedList` request was throttled by game server limits or the request queue was full. | A request to `Class.OrderedDataStore:GetSortedAsync()\|GetSortedAsync()` on an ordered data store exceeded the `OrderedList` game server-level rate limit. | | | `OrderedRemoveGameServerThrottled` | `OrderedRemove` request was throttled by game server limits or the request queue was full. | A request to `Class.OrderedDataStore:RemoveAsync()\|RemoveAsync()` on an ordered data store exceeded the `OrderedRemove` game server-level rate limit. | ### Server error codes | Error name | Error message | Notes | | --- | --- | --- | | `DatastoreDeleted` | The data store is deleted. | An operation on the data store could not occur because the data store was previously deleted. | | `DatastoreThrottled` | The request rate exceeds the allowed maximum for the `datastore`. | Too many requests were sent to a single data store. | | `InternalServerError` | An internal server error occurred. | Occasional error on Roblox servers. Try again, ideally with exponential backoff. | | `InvalidExclusiveStartKey` | The provided exclusive start key is not valid. | The exclusive start key (cursor) provided to a list operation such as `Class.DataStore:ListKeysAsync()\|ListKeysAsync()` is not valid. | | `InvalidPlace` | The provided place is invalid. | No matching Universe ID for the place. Try again later. | | `InvalidTarget` | The provided target is invalid. | Ordered data store key name exceeds the 50 character limit. | | `InvalidUniverse` | The provided universe is invalid. | No matching Place ID for the universe. Try again later. | | `InvalidUserIds` | The provided user IDs have an invalid format. | Failed to parse user IDs. | | `KeyThrottled` | The request rate exceeds the allowed maximum for the key. | The request rate exceeds the maximum allowed request rate for a single key. | | `KeyNotFound` | The requested key doesn't exist. | The key doesn't exist. | | `N/A` | No pages to advance to. | This error occurs when you call `Class.Pages:AdvanceToNextPageAsync()` on the last page. | | `StandardReadExperienceThrottled` | The standard read request rate exceeds the allowed maximum for the experience. | A request to `Class.DataStore:GetAsync()\|GetAsync()`, `Class.DataStore:GetVersionAsync()\|GetVersionAsync()`, `Class.DataStore:GetVersionAtTimeAsync()\|GetVersionAtTimeAsync()`, or the read of `Class.DataStore:UpdateAsync()\|UpdateAsync()` on a standard data store exceeded the `StandardRead` experience-level rate limit. | | `StandardWriteExperienceThrottled` | The standard write request rate exceeds the allowed maximum for the experience. | A request to `Class.DataStore:SetAsync()\|SetAsync()`, `Class.DataStore:IncrementAsync()\|IncrementAsync()`, or the write of `Class.DataStore:UpdateAsync()\|UpdateAsync()` on a standard data store exceeded the `StandardWrite` experience-level rate limit. | | `StandardListExperienceThrottled` | The standard list request rate exceeds the allowed maximum for the experience. | A request to `Class.DataStore:ListKeysAsync()\|ListKeysAsync()`, `Class.DataStore:ListVersionsAsync()\|ListVersionsAsync()`, or `Class.DataStoreService:ListDataStoresAsync()\|ListDataStoresAsync()` on standard data stores exceeded the `StandardList` experience-level rate limit. | | `StandardRemoveExperienceThrottled` | The standard remove request rate exceeds the allowed maximum for the experience. | A request to `Class.DataStore:RemoveAsync()\|RemoveAsync()` on a standard data store exceeded the `StandardRemove` experience-level rate limit. | | `OrderedReadExperienceThrottled` | The ordered read request rate exceeds the allowed maximum for the experience. | A request to `Class.OrderedDataStore:GetAsync()\|GetAsync()` or the read of `Class.OrderedDataStore:UpdateAsync()\|UpdateAsync()` on an ordered data store exceeded the `OrderedRead` experience-level rate limit. | | `OrderedWriteExperienceThrottled` | The ordered write request rate exceeds the allowed maximum for the experience. | A request to `Class.OrderedDataStore:SetAsync()\|SetAsync()`, `Class.OrderedDataStore:IncrementAsync()\|IncrementAsync()`, or the write of `Class.OrderedDataStore:UpdateAsync()\|UpdateAsync()` on an ordered data store exceeded the `OrderedWrite` experience-level rate limit. | | `OrderedListExperienceThrottled` | The ordered list request rate exceeds the allowed maximum for the experience. | A request to `Class.OrderedDataStore:GetSortedAsync()\|GetSortedAsync()` on an ordered data store exceeded the `OrderedList` experience-level rate limit. | | `OrderedRemoveExperienceThrottled` | The ordered remove request rate exceeds the allowed maximum for the experience. | A request to `Class.OrderedDataStore:RemoveAsync()\|RemoveAsync()` on an ordered data store exceeded the `OrderedRemove` experience-level rate limit. | ## Limits Data models have **limits**. If an experience exceeds these limits, the service automatically throttles the experience's data store usage and causes future requests to be placed in one of the following queues: - Set - Ordered set - Get - Ordered get Requests in a queue are handled in the order they are received. The called function continues to yield as long as its request is still queued. If the data store key itself is throttled, the request is placed in a queue but is temporarily skipped. Each queue has a limit of 30 requests. When the limit of a queue is reached, requests fail with an error code in the 301-306 range, indicating that the requests have been dropped entirely. ### Access limits Data stores are subject to both **experience and server-level limits**. Experience-level limits scale with total concurrent users across the experience, while server-level limits are configurable and meant to be used as a tool by the creator. #### Experience Limits Each experience is allowed a certain number of data store requests based on the data store type, request type, and number of concurrent users. For each data store type and request type, the limit is shared among all listed functions. Note that `Class.GlobalDataStore:UpdateAsync()|UpdateAsync()` consumes from both the read and write request budgets. A single call will decrement both limits. ##### Standard data stores | Request type | Functions | Requests per minute | | --- | --- | --- | | **Read** | `Class.DataStore:GetAsync()\|GetAsync()`
`Class.DataStore:GetVersionAsync()\|GetVersionAsync()`
`Class.DataStore:GetVersionAtTimeAsync()\|GetVersionAtTimeAsync()`
`Class.DataStore:UpdateAsync()\|UpdateAsync()` | 250 + concurrentUsers × 40 | | **Write** | `Class.DataStore:SetAsync()\|SetAsync()`
`Class.DataStore:IncrementAsync()\|IncrementAsync()`
`Class.DataStore:UpdateAsync()\|UpdateAsync()` | 250 + concurrentUsers × 20 | | **List** | `Class.DataStoreService:ListDataStoresAsync()\|ListDataStoresAsync()`
`Class.DataStore:ListKeysAsync()\|ListKeysAsync()`
`Class.DataStore:ListVersionsAsync()\|ListVersionsAsync()` | 10 + concurrentUsers × 2 | | **Remove** | `Class.DataStore:RemoveAsync()\|RemoveAsync()` | 100 + concurrentUsers × 40 | ##### Ordered data stores | Request type | Functions | Requests per minute | | --- | --- | --- | | **Read** | `Class.OrderedDataStore:GetAsync()\|GetAsync()`
`Class.OrderedDataStore:UpdateAsync()\|UpdateAsync()` | 250 + concurrentUsers × 40 | | **Write** | `Class.OrderedDataStore:SetAsync()\|SetAsync()`
`Class.OrderedDataStore:IncrementAsync()\|IncrementAsync()`
`Class.OrderedDataStore:UpdateAsync()\|UpdateAsync()` | 250 + concurrentUsers × 20 | | **List** | `Class.OrderedDataStore:GetSortedAsync()\|GetSortedAsync()` | 100 + concurrentUsers × 2 | | **Remove** | `Class.OrderedDataStore:RemoveAsync()\|RemoveAsync()` | 100 + concurrentUsers × 40 | #### Server limits Each server has a configurable rate limit for each request type, based on the number of players in that server. Servers receive a one-time startup burst of additional request budget when they are first created. Use `Class.DataStoreService:GetRequestBudgetForRequestType()|GetRequestBudgetForRequestType()` to confirm the number of data store requests that the current server can make at any given time. These limits are **configurable by the creator** using the `Class.DataStoreService:SetRateLimitForRequestType()|SetRateLimitForRequestType()` API. Using this API, a creator can configure their own data stores rate limits for each request type. The following **default rate limits** apply if the API is not called: ##### Standard data stores | Request type | `DataStoreRequestType` Enum | Functions | Requests per minute | | --- | --- | --- | --- | | **Read** | `StandardRead` | `Class.DataStore:GetAsync()\|GetAsync()`
`Class.DataStore:GetVersionAsync()\|GetVersionAsync()`
`Class.DataStore:GetVersionAtTimeAsync()\|GetVersionAtTimeAsync()`
`Class.DataStore:UpdateAsync()\|UpdateAsync()` | 60 + numPlayers × 40 | | **Write** | `StandardWrite` | `Class.DataStore:SetAsync()\|SetAsync()`
`Class.DataStore:IncrementAsync()\|IncrementAsync()`
`Class.DataStore:UpdateAsync()\|UpdateAsync()` | 60 + numPlayers × 40 | | **List** | `StandardList` | `Class.DataStoreService:ListDataStoresAsync()\|ListDataStoresAsync()`
`Class.DataStore:ListKeysAsync()\|ListKeysAsync()`
`Class.DataStore:ListVersionsAsync()\|ListVersionsAsync()` | 5 + numPlayers × 2 | | **Remove** | `StandardRemove` | `Class.DataStore:RemoveAsync()\|RemoveAsync()` | 60 + numPlayers × 40 | | **RemoveVersion (Deprecated)** | `RemoveVersionAsync` | `Class.DataStore:RemoveVersionAsync()\|RemoveVersionAsync()` | 5 + numPlayers × 2 | ##### Ordered data stores | Request type | `DataStoreRequestType` Enum | Functions | Requests per minute | | --- | --- | --- | --- | | **Read** | `OrderedRead` | `Class.OrderedDataStore:GetAsync()\|GetAsync()`
`Class.OrderedDataStore:UpdateAsync()\|UpdateAsync()` | 60 + numPlayers × 40 | | **Write** | `OrderedWrite` | `Class.OrderedDataStore:SetAsync()\|SetAsync()`
`Class.OrderedDataStore:IncrementAsync()\|IncrementAsync()`
`Class.OrderedDataStore:UpdateAsync()\|UpdateAsync()` | 30 + numPlayers × 5 | | **List** | `OrderedList` | `Class.OrderedDataStore:GetSortedAsync()\|GetSortedAsync()` | 5 + numPlayers × 2 | | **Remove** | `OrderedRemove` | `Class.OrderedDataStore:RemoveAsync()\|RemoveAsync()` | 30 + numPlayers × 5 | > **Info:** Data store requests made in Studio Run mode are subject to a separate set of static limits, which may be lower than the limits configured with `Class.DataStoreService:SetRateLimitForRequestType()|SetRateLimitForRequestType()`. When testing rate limits, we recommend using Studio Team Create instead. ### Data limits Data stores limit how much data can be used per entry. The data store name, key name, and [scope](/docs/en-us/cloud-services/data-stores/versioning-listing-and-caching.md#scopes) must all be under a certain character length. Use `Library.string.len()` to check their length. The data (key value) is also stored as a string, regardless of its initial type. You can check the size of the data with the `Class.HttpService:JSONEncode()|JSONEncode()` function, which converts Luau data into a serialized JSON table. | Component | Maximum number of characters | | --- | --- | | Data store name | 50 | | Key name | 50 | | Scope | 50 | | Data (key value) | 4,194,304 per key | ### Metadata limits Limits to the number of characters in user-defined metadata. | Component | Maximum number of characters | | --- | --- | | Key name | 50 | | Value | 250 | | Key-value pairs | 300 | > **Info:** There's no limit to the total number of key-value pairs, but the total size can't exceed 300 characters. ### Throughput limits Per-key throughput limits ensure that performance is optimal on Roblox servers. Each limit applies to every single key across all servers in an experience and refreshes over time. Roblox examines the usage of quota associated with the key over the last 60 seconds. If the usage, including the current request, is within the throughput limit, the request is approved. If the usage exceeds the limit, the request is denied. | Request type | Limit | | --- | --- | | Read | 25 MB per minute | | Write | 4 MB per minute | In addition to the above throughput limits, Roblox organizes data into partitions based on an internal schema. As a result, when the backend server receives a high volume of requests to the same data store, it can result in further throttling. Regardless of the cause, throttling manifests as either `DatastoreThrottled` or `KeyThrottled` errors, depending on whether the throughput limit was exceeded for a single data store or a key. These error messages apply to both ordered and standard data stores. > **Info:** For every request, Roblox rounds throughput up to the next kilobyte. For example, if you write 800 bytes and 1.2 KB in two requests, Roblox counts that as 3 KB total throughput (1 KB and 2 KB, respectively). ### Storage limits In the future, to provide a scalable and stable storage experience, data stores will implement an experience-level storage limit on your storage usage. This limit will be the sum of a base limit for each of your experiences and a per-user limit based on the number of lifetime users in your experience. A lifetime user is any user who has joined your experience at least once. The storage limit will be calculated using the formula `Total latest version storage limit = 100 MB + 1 MB * lifetime user count`. Any keys that you delete or replace, even if still accessible through version APIs, do not count towards your experience's storage usage. However, entire data stores deleted via the Open Cloud [`DeleteDataStore`](/docs/en-us/cloud/reference/DataStore.md#Cloud_DeleteDataStore) method continue to count towards your storage usage for the duration of their 30-day processing period, until they are permanently deleted.
--- title: "Data stores" url: /docs/en-us/cloud-services/data-stores last_updated: 2026-06-29T19:33:58Z description: "How to implement data stores (DataStores) to store persistent data." --- # Data stores The `Class.DataStoreService` lets you store data that needs to persist between sessions, like items in a player's inventory or skill points. Data stores are consistent per experience, so any place in an experience can access and change the same data, including places on different servers. If you want to add granular permission control to your data stores and access them outside of Studio or Roblox servers, you can use [Open Cloud APIs for data stores](/docs/en-us/cloud/reference/DataStore.md). To view and monitor all the data stores in an experience through the Creator Hub, use the [Data Stores Manager](/docs/en-us/cloud-services/data-stores-manager.md). For temporary data that you need to update or access frequently, use [memory stores](/docs/en-us/cloud-services/../memory-stores.md). ## Enable Studio access By default, experiences tested in Studio can't access data stores, so you must first enable them. Accessing data stores in Studio can be dangerous for live experiences because Studio accesses the same data stores as the client application. To avoid overwriting production data, do not enable this setting for live experiences. Instead, enable it for a separate test version of the experience. To enable Studio access in a [published](/docs/en-us/production/publishing/publish-games-and-places.md) experience: 1. Open Studio's **File** ⟩ **Experience Settings** window. 2. Navigate to **Security**. 3. Enable the **Enable Studio Access to API Services** toggle. 4. Click **Save**. ## Access data stores To access a data store inside an experience: 1. Add `Class.DataStoreService` to a server-side `Class.Script|Script`. 2. Use the `Class.DataStoreService:GetDataStore()|GetDataStore()` function and specify the name of the data store you want to use. If the data store doesn't exist, Studio creates one when you save your experience data for the first time. ```lua local DataStoreService = game:GetService("DataStoreService") local experienceStore = DataStoreService:GetDataStore("PlayerExperience") ``` > **Warning:** The server can only access data stores through `Class.Script|Scripts`. Attempting client-side access in a `Class.LocalScript` causes an error. ## Create data A data store is essentially a dictionary, similar to a Luau table. A unique **key** indexes each value in the data store, like a user's unique `Class.Player.UserId` or a named string for an experience promo. | **User data key** | **Value** | | --- | --- | | `31250608` | 50 | | `351675979` | 20 | | `505306092` | 78000 | | **Promo data key** | **Value** | | `ActiveSpecialEvent` | SummerParty2 | | `ActivePromoCode` | BONUS123 | | `CanAccessPartyPlace` | true | To create a new entry, call `Class.GlobalDataStore:SetAsync()|SetAsync()` with the key name and a value. ```lua local DataStoreService = game:GetService("DataStoreService") local experienceStore = DataStoreService:GetDataStore("PlayerExperience") local success, errorMessage = pcall(function() experienceStore:SetAsync("User_1234", 50) end) if not success then print(errorMessage) end ``` > **Warning:** Functions like `Class.GlobalDataStore:SetAsync()|SetAsync()` that access a data store's contents are network calls that might occasionally fail. To catch and handle errors, make sure to wrap these calls in `Global.LuaGlobals.pcall()`. ## Update data To change any stored value in a data store, call `Class.GlobalDataStore:UpdateAsync()|UpdateAsync()` with the entry's key name and a callback function that defines how you want to update the entry. This callback takes the current value and returns a new value based on the logic you define. If the callback returns `nil`, the write operation is cancelled and the value isn't updated. > **Warning:** The callback function you pass into `Class.GlobalDataStore:UpdateAsync()|UpdateAsync()` does **not** have permission to yield. It can't contain any yielding functions like `Library.task.wait()`. ```lua local DataStoreService = game:GetService("DataStoreService") local nicknameStore = DataStoreService:GetDataStore("Nicknames") local function makeNameUpper(currentName) local nameUpper = string.upper(currentName) return nameUpper end local success, updatedName = pcall(function() return nicknameStore:UpdateAsync("User_1234", makeNameUpper) end) if success then print("Uppercase Name:", updatedName) end ``` ### Set vs update Use set to quickly update a specific key. The `Class.GlobalDataStore:SetAsync()|SetAsync()` function: - Can cause data inconsistency if two servers try to set the same key at the same time - Only counts against the write limit Use update to handle multi-server attempts. The `Class.GlobalDataStore:UpdateAsync()|UpdateAsync()` function: - Reads the current key value from the server that last updated it before making any changes - Is slower because it reads before it writes - Counts against both the read and write limits ## Read data To read the value of a data store entry, call `Class.GlobalDataStore:GetAsync()|GetAsync()` with the entry's key name. ```lua local DataStoreService = game:GetService("DataStoreService") local experienceStore = DataStoreService:GetDataStore("PlayerExperience") local success, currentExperience = pcall(function() return experienceStore:GetAsync("User_1234") end) if success then print(currentExperience) end ``` > **Warning:** The values you retrieve using `Class.GlobalDataStore:GetAsync()|GetAsync()` sometimes can be out of sync with the backend due to the [caching](/docs/en-us/cloud-services/versioning-listing-and-caching.md#caching) behavior. For more information, see [Disabling caching](/docs/en-us/cloud-services/versioning-listing-and-caching.md#disable-caching). ## Increment data To increment an integer in a data store, call `Class.GlobalDataStore:IncrementAsync()|IncrementAsync()` with the entry's key name and a number for how much to change the value. `Class.GlobalDataStore:IncrementAsync()|IncrementAsync()` is a convenience function that lets you avoid calling `Class.GlobalDataStore:UpdateAsync()|UpdateAsync()` and manually incrementing the integer. ```lua local DataStoreService = game:GetService("DataStoreService") local experienceStore = DataStoreService:GetDataStore("PlayerExperience") local success, newExperience = pcall(function() return experienceStore:IncrementAsync("Player_1234", 1) end) if success then print(newExperience) end ``` ## Remove data To remove an entry and return the value associated with the key, call `Class.GlobalDataStore:RemoveAsync()|RemoveAsync()`. ```lua local DataStoreService = game:GetService("DataStoreService") local nicknameStore = DataStoreService:GetDataStore("Nicknames") local success, removedValue = pcall(function() return nicknameStore:RemoveAsync("User_1234") end) if success then print(removedValue) end ``` ## Metadata > **Info:** Ordered data stores don't support [versioning](/docs/en-us/cloud-services/versioning-listing-and-caching.md#versioning) and metadata, so `Class.DataStoreKeyInfo|DataStoreKeyInfo` is always `nil` for keys in an `Class.OrderedDataStore|OrderedDataStore`. If you need to support versioning and metadata, use `Class.DataStore|DataStore`. There are two types of metadata associated with keys: - **Service-defined**: Default read-only metadata, like the most recent update time and creation time. Every object has service-defined metadata. - **User-defined**: Custom metadata for tagging and categorization. Defined using the `Class.DataStoreSetOptions` object and the `Class.DataStoreSetOptions:SetMetadata()|SetMetadata()` function. To manage metadata, expand the `Class.GlobalDataStore:SetAsync()|SetAsync()`, `Class.GlobalDataStore:UpdateAsync()|UpdateAsync()`, `Class.GlobalDataStore:GetAsync()|GetAsync()`, `Class.GlobalDataStore:IncrementAsync()|IncrementAsync()`, and `Class.GlobalDataStore:RemoveAsync()|RemoveAsync()` functions. - `Class.GlobalDataStore:SetAsync()|SetAsync()` accepts the optional third and fourth arguments: - A table of `Class.Player.UserId|UserIds`. This can help with content copyright and intellectual property tracking and removal. - A `Class.DataStoreSetOptions` object, where you can define custom metadata using the `Class.DataStoreSetOptions:SetMetadata()|SetMetadata()` function.```lua local DataStoreService = game:GetService("DataStoreService") local experienceStore = DataStoreService:GetDataStore("PlayerExperience") local setOptions = Instance.new("DataStoreSetOptions") setOptions:SetMetadata({["ExperienceElement"] = "Fire"}) local success, errorMessage = pcall(function() experienceStore:SetAsync("User_1234", 50, {1234}, setOptions) end) if not success then print(errorMessage) end ``` - `Class.GlobalDataStore:GetAsync()|GetAsync()`, `Class.GlobalDataStore:IncrementAsync()|IncrementAsync()`, and `Class.GlobalDataStore:RemoveAsync()|RemoveAsync()` return a second value in the `Class.DataStoreKeyInfo` object. This second value contains both service-defined properties and functions to fetch user-defined metadata. - The `Class.DataStoreKeyInfo:GetUserIds()|GetUserIds()` function fetches the table of `Class.Player.UserId|UserIds` that you passed to `Class.GlobalDataStore:SetAsync()|SetAsync()`. - The `Class.DataStoreKeyInfo:GetMetadata()|GetMetadata()` function fetches user-defined metadata that you passed to `Class.GlobalDataStore:SetAsync()|SetAsync()` through `Class.DataStoreSetOptions:SetMetadata()|SetMetadata()`. - The `Class.DataStoreKeyInfo.Version|Version` property fetches the version of the key. - The `Class.DataStoreKeyInfo.CreatedTime|CreatedTime` property fetches the time the key was created, formatted as the number of milliseconds since epoch. - The `Class.DataStoreKeyInfo.UpdatedTime|UpdatedTime` property fetches the last time the key was updated, formatted as the number of milliseconds since epoch.```lua local DataStoreService = game:GetService("DataStoreService") local experienceStore = DataStoreService:GetDataStore("PlayerExperience") local success, currentExperience, keyInfo = pcall(function() return experienceStore:GetAsync("User_1234") end) if success then print(currentExperience) print(keyInfo.Version) print(keyInfo.CreatedTime) print(keyInfo.UpdatedTime) print(keyInfo:GetUserIds()) print(keyInfo:GetMetadata()) end ``` - The callback function of `Class.GlobalDataStore:UpdateAsync()|UpdateAsync()` takes an additional parameter in the `Class.DataStoreKeyInfo` object that describes the current key state. It returns the modified value, the keys associated with `Class.Player.UserId|UserIds`, and the key's metadata.```lua local DataStoreService = game:GetService("DataStoreService") local nicknameStore = DataStoreService:GetDataStore("Nicknames") local function makeNameUpper(currentName, keyInfo) local nameUpper = string.upper(currentName) local userIDs = keyInfo:GetUserIds() local metadata = keyInfo:GetMetadata() return nameUpper, userIDs, metadata end local success, updatedName, keyInfo = pcall(function() return nicknameStore:UpdateAsync("User_1234", makeNameUpper) end) if success then print(updatedName) print(keyInfo.Version) print(keyInfo.CreatedTime) print(keyInfo.UpdatedTime) print(keyInfo:GetUserIds()) print(keyInfo:GetMetadata()) end ``` > **Error:** When calling `Class.GlobalDataStore:SetAsync()|SetAsync()`, `Class.GlobalDataStore:IncrementAsync()|IncrementAsync()`, and `Class.GlobalDataStore:UpdateAsync()|UpdateAsync()`, you must always update metadata definitions with a value, even when there are no changes to the current value. If you don't, you lose the current value. For limits when defining metadata, see the [metadata limits](/docs/en-us/cloud-services/data-stores/error-codes-and-limits.md#metadata-limits). ## Ordered data stores By default, data stores don't sort their content. If you need to get data in an ordered way, like in persistent leaderboard stats, call `Class.DataStoreService:GetOrderedDataStore()|GetOrderedDataStore()` instead of `Class.DataStoreService:GetDataStore()|GetDataStore()`. ```lua local DataStoreService = game:GetService("DataStoreService") local characterAgeStore = DataStoreService:GetOrderedDataStore("CharacterAges") ``` Ordered data stores support the same basic functions as default data stores, plus the unique `Class.OrderedDataStore:GetSortedAsync()|GetSortedAsync()` function. This retrieves **multiple sorted keys** based on a specific sorting order, page size, and minimum/maximum values. The following example sorts character data into pages with three entries, each in descending order, then loops through the pages and outputs each character's name and age. ```lua local DataStoreService = game:GetService("DataStoreService") local characterAgeStore = DataStoreService:GetOrderedDataStore("CharacterAges") -- Populates ordered data store local characters = { Mars = 19, Janus = 20, Diana = 18, Venus = 25, Neptune = 62 } for char, age in characters do local success, errorMessage = pcall(function() characterAgeStore:SetAsync(char, age) end) if not success then print(errorMessage) end end -- Sorts data by descending order into pages of three entries each local success, pages = pcall(function() return characterAgeStore:GetSortedAsync(false, 3) end) if success then while true do -- Gets the current (first) page local entries = pages:GetCurrentPage() -- Iterates through all key-value pairs on page for _, entry in entries do print(entry.key .. " : " .. tostring(entry.value)) end -- Checks if last page has been reached if pages.IsFinished then break else print("----------") -- Advances to next page pages:AdvanceToNextPageAsync() end end end ``` > **Info:** When you iterate through `Class.DataStoreService:GetOrderedDataStore()|GetOrderedDataStore()` using `Class.Pages:AdvanceToNextPageAsync()|AdvanceToNextPageAsync()`, the limit for requests is the same as the maximum page size you set for an ordered data store. `Class.Pages:AdvanceToNextPageAsync()|AdvanceToNextPageAsync()` always has the same limit as the class that originally requires it. --- title: "Data store observability" url: /docs/en-us/cloud-services/data-stores/observability last_updated: 2026-06-29T19:33:58Z description: "Explains how to use the observability dashboard for data stores (DataStores)." --- # Data store observability The data stores observability dashboard provides real-time charts on your request counts and on your usage against future data store limits, and allows you to filter the request data by standard or ordered data stores. ![An image showing the request count by API dashboard in the Creator Hub.](../../assets/data/data-store/Data-Store-API.png) ## Access the dashboard The data stores dashboard is available for any experience that uses `Class.DataStoreService`, but you must either be the experience owner or have [analytics group permissions](/docs/en-us/production/analytics/analytics-dashboard.md#grant-group-permission) to access the dashboard. 1. Navigate to the [Creations](https://create.roblox.com/dashboard/creations) page on the **Creator Hub**. 2. Under the **Creator Hub** dropdown, select your account or the group owning the target experience. 3. Select the experience. 4. In the **Monitoring** dropdown, select **Data Stores**. ## Available charts - **Storage Usage Bytes** on amount of data store storage and limit currently available for the selected universe. - **Request Count by API** on API request count per minute by API method, such as `Class.DataStore:SetAsync()` or `Class.OrderedDataStore:GetSortedAsync()`. - **Request Count by Status** on API request count by [response status](#response-status-codes). - **Request by API x Status** on response statuses returned by all or a specific API method. - **Read Request Type Quota Usage** on number of read API method calls against future read category limits. - **Write Request Type Quota Usage** on number of write API method calls against future write category limits. - **List Request Type Quota Usage** on number of list API method calls against future list category limits. - **Remove Request Type Quota Usage** on number of remove API method calls against future remove category limits. Use the selector at the top of the page to filter by standard or ordered data stores. The default view includes standard data store only. Use the **Explore** button next to each chart to deep dive into that metric, compare the current period against past periods, or break down that metric by its available categories. Each chart contains data for the past 30 days, and you can select to view a custom time range with the selector at the top of the page. If you select a time range earlier than 30 days, the system returns a **Request Failed** error. Data from the most recent three minutes might be incomplete, so it's normal to see a drop at the end of the charts. Storage usage and limit are displayed in bytes, where 1 kilobyte is 1024 bytes. ## Response status codes The dashboard's **Request Count by Status** and **Requests by API x Status** charts include status codes of API responses that you can use to understand usage and troubleshoot errors. For a table that lists and describes all of these status codes (aside from `200 OK`), see [Error codes](/docs/en-us/cloud-services/data-stores/error-codes-and-limits.md#error-code-reference). ## Service limits For information on current limits, usage quotas, and future limits, see [Limits](/docs/en-us/cloud-services/data-stores/error-codes-and-limits.md#limits). --- title: "Implement player data and purchasing systems" url: /docs/en-us/cloud-services/data-stores/player-data-purchasing last_updated: 2026-06-29T19:33:58Z description: "Guide for implementing player data and purchase handling systems." --- # Implement player data and purchasing systems ## Background Roblox provides a set of APIs to interface with data stores via `Class.DataStoreService`. The most common use case for these APIs is for saving, loading, and replicating _player data_. That is, data associated with the player's progress, purchases, and other session characteristics that persists between individual play sessions. Most experiences on Roblox use these APIs to implement some form of a player data system. These implementations differ in their approach, but generally seek to solve the same set of issues. ## Common problems Below are some of the most common problems player data systems attempt to solve: - **In Memory Access:** `Class.DataStoreService` requests make web requests that operate asynchronously and are subject to rate limits. This is appropriate for an initial load at the start of the session, but not for high frequency read and write operations during the normal course of gameplay. Most developers' player data systems store this data in-memory on the Roblox server, limiting `Class.DataStoreService` requests to the following scenarios: - Initial read at the start of a session - Final write at the end of the session - Periodic writes at an interval to mitigate the scenario where the final write fails - Writes to ensure data is saved while processing a purchase - **Efficient Storage:** Storing all of a player's session data in a single table lets you update multiple values atomically and handle the same amount of data in fewer requests. It also removes the risk of inter-value desynchronization and makes rollbacks easier to reason around. Some developers also implement custom serialization to compress large data structures (typically to save in-game user-generated content). - **Replication:** The client needs regular access to a player's data (for example, to update the UI). A generic approach to replicating player data to the client lets you transmit this information without having to create bespoke replication systems for each component of data. Developers often want the option to be selective about what is and isn't replicated to the client. - **Error Handling:** When DataStores cannot be accessed, most solutions will implement a retrying mechanism and a fallback to 'default' data. Special care is needed to ensure fallback data doesn't later overwrite 'real' data, and that this is communicated to the player appropriately. - **Retries:** When data stores are inaccessible, most solutions implement a retry mechanism and a fallback to default data. Take special care to ensure that fallback data doesn't later overwrite "real" data, and communicate the situation to the player appropriately. - **Session Locking:** If a single player's data is loaded and in-memory on multiple servers, issues can occur in which one server saves out-of-date information. This can lead to data loss and common item duplication loopholes. - **Atomic Purchase Handling:** Verify, award, and record purchases atomically to prevent items from being lost or awarded multiple times. ## Sample code > **Warning:** This code is provided for reference purposes only and has not proven itself over a long period of time in a popular experience. It exists so that you can examine its approach to solving various problems and apply them to your own player data systems. Don't use this code in your experience as-is without extensive testing. Roblox has reference code to assist you with designing and building player data systems. The remainder of this page examines background, implementation details, and general caveats. After you import the model into Studio, you should see the following folder structure: ![Explorer window showing the purchasing system model.](../../assets/data/player-data-purchasing/Sample-Hierarchy.png) ## Architecture This high-level diagram illustrates the key systems in the sample and how they interface with code in the rest of the experience. ![An architecture diagram for the code sample.](../../assets/data/player-data-purchasing/sample-architecture.png) ## Retries **Class:** [`DataStoreWrapper`](#sample-code) ### Background As `Class.DataStoreService` makes web requests under the hood, its requests are not guaranteed to succeed. When this happens, the `Class.GlobalDataStore|DataStore` methods throw errors, allowing you to handle them. A common "gotcha" can occur if you attempt to handle data store failures like this: ```lua local function retrySetAsync(dataStore, key, value) for _ = 1, MAX_ATTEMPTS do local success, result = pcall(dataStore.SetAsync, dataStore, key, value) if success then break end task.wait(TIME_BETWEEN_ATTEMPTS) end end ``` While this is a perfectly valid retry mechanism for a generic function, it is not suitable for `Class.DataStoreService` requests because it does not guarantee the order in which requests are made. Preserving the order of requests is important for `Class.DataStoreService` requests because they interact with state. Consider the following scenario: 1. Request A is made to set the value of key `K` to 1. 2. The request fails, so a retry is scheduled to run in 2 seconds. 3. Before the retry occurs, request B sets the value of `K` to 2, but the retry of request A immediately overwrites this value and sets `K` to 1. Even though `Class.GlobalDataStore:UpdateAsync()|UpdateAsync` operates on the latest version of the key's value, `Class.GlobalDataStore:UpdateAsync()|UpdateAsync` requests must still be processed in order to avoid invalid transient states (for example, a purchase subtracts coins before a coin addition gets processed, resulting in negative coins). Our player data system uses a new class, `DataStoreWrapper`, which provides yielding retries that are guaranteed to be processed in order per key. ### Approach ![An process diagram illustrating the retry system](../../assets/data/player-data-purchasing/retry-diagram.png) `DataStoreWrapper` provides methods corresponding to the `Class.GlobalDataStore|DataStore` methods: `Class.GlobalDataStore:GetAsync()|DataStore:GetAsync()`, `Class.GlobalDataStore:SetAsync()|DataStore:SetAsync()`, `Class.GlobalDataStore:UpdateAsync()|DataStore:UpdateAsync()` and `Class.GlobalDataStore:RemoveAsync()|DataStore:RemoveAsync()`. These methods, when called: 1. Add the request to a queue. Each key has its own queue, where requests are processed in order and in series. The requesting thread yields until the request has completed. This functionality is based on the `ThreadQueue` class, which is a coroutine-based task scheduler and rate limiter. Rather than returning a promise, `ThreadQueue` yields the current thread until the operation is complete and throws an error if it fails. This is more consistent with idiomatic asynchronous Luau patterns. 2. If a request fails, it retries with a configurable exponential backoff. These retries form part of the callback submitted to the `ThreadQueue`, so they are guaranteed to complete before the next request in the queue for this key begins. 3. When a request is completed, the request method returns with the `success, result` pattern `DataStoreWrapper` also exposes methods to get the queue length for a given key and clear out stale requests. The latter option is particularly useful in scenarios when the server is shutting down and there is no time to process any but the most recent requests. ### Caveats `DataStoreWrapper` follows the principle that, outside of extreme scenarios, every data store request should be allowed to complete (successfully or otherwise), even if a more recent request makes it redundant. When a new request occurs, stale requests aren't removed from the queue, but are instead allowed to complete before the new request is started. The rationale for this is rooted in this module's applicability as a generic data store utility rather than a specific tool for player data, and is as follows: 1. It's hard to decide on an intuitive set of rules for when a request is safe to remove from the queue. Consider the following queue:`Value=0, SetAsync(1), GetAsync(), SetAsync(2)` The expected behavior is that `Class.GlobalDataStore:GetAsync()|GetAsync()` would return `1`, but if we remove the `Class.GlobalDataStore:SetAsync()|SetAsync()` request from the queue due to it being made redundant by the most recent one, it would return `0`. The logical progression is that when a new write request is added, only prune stale requests as far back as the most recent read request. `Class.GlobalDataStore:UpdateAsync()|UpdateAsync()`, by far the most common operation (and the only one used by this system), can both read and write, so it would be difficult to reconcile within this design this without adding extra complexity.`DataStoreWrapper` could require you to specify whether an `Class.GlobalDataStore:UpdateAsync()|UpdateAsync()` request was permitted to read and/or write, but it would have no applicability to our player data system, where this cannot be determined ahead of time due to the session locking mechanism (covered in more detail later). 2. Once removed from the queue, it's hard to decide on an intuitive rule for _how_ this should be handled. When a `DataStoreWrapper` request is made, the current thread is yielded until it is completed. If we removed stale requests from the queue, we would have to decide whether to return `false, "Removed from queue"` or to never return and discard the active thread. Both approaches come with their own drawbacks and offload additional complexity onto the consumer. Ultimately, our view is that the simple approach (processing every request) is preferable here and creates a clearer environment to navigate in when approaching complex issues like session locking. The only exception to this is during `Class.DataModel:BindToClose()`, where clearing the queue becomes necessary to save all users' data in time and the value individual function calls return is no longer an ongoing concern. To account for this, we expose a `skipAllQueuesToLastEnqueued` method. For more context, see [Player Data](#player-data). ## Session locking **Class:** [`SessionLockedDataStoreWrapper`](#sample-code) ### Background Player data is stored in memory on the server and is only read from and written to the underlying data stores when necessary. You can read and update in-memory player data instantly without needing web requests and avoid exceeding `Class.DataStoreService` limits. For this model to work as intended, it is imperative that no more than one server is able to load a player's data into memory from the `Class.GlobalDataStore|DataStore` at the same time. For example, if server A loads a player's data, server B can't load that data until server A releases its lock on it during a final save. Without a locking mechanism, server B could load out-of-date player data from the data store before server A has a chance to save the more recent version that it has in memory. Then if server A saves its newer data after server B loads the out-of-date data, server B would overwrite that newer data during its next save. Even though Roblox only allows a client to be connected to one server at a time, you can't assume that data from one session is always saved before the next session starts. Consider the following scenarios that can occur when a player leaves server A: 1. Server A makes a `Class.GlobalDataStore|DataStore` request to save their data, but the request fails and requires several retries to successfully complete. During the retry period, the player joins server B. 2. Server A makes too many `Class.GlobalDataStore:UpdateAsync()|UpdateAsync()` calls to the same key and gets throttled. The final save request is placed in a queue. While the request is in the queue, the player joins server B. 3. On server A, some code connected to the `Class.Players.PlayerRemoving|PlayerRemoving` event yields before the player's data is saved. Before this operation completes, the player joins server B. 4. The performance of server A has degraded to the point that the final save is delayed until after the player joins server B. These scenarios should be rare, but they do occur, particularly in situations where a player disconnects from one server and connects to another in rapid succession (for example, while teleporting). Some malicious users might even attempt to abuse this behavior to complete actions without them persisting. This can be particularly impactful in experiences that allow players to trade and is a common source of item duplication exploits. Session locking addresses this vulnerability by ensuring that when a player's `Class.GlobalDataStore|DataStore` key is first read by the server, the server atomically writes a lock to the key's metadata inside the same `Class.GlobalDataStore:UpdateAsync()|UpdateAsync()` call. If this lock value is present when any other server attempts to read or write the key, the server does not proceed. ### Approach ![An process diagram illustrating the session locking system](../../assets/data/player-data-purchasing/session-lock-diagram.png) `SessionLockedDataStoreWrapper` is a meta-wrapper around the `DataStoreWrapper` class. `DataStoreWrapper` provides queueing and retrying functionality, which `SessionLockedDataStoreWrapper` supplements with session locking. `SessionLockedDataStoreWrapper` passes every `Class.GlobalDataStore|DataStore` request—regardless of whether it is `Class.GlobalDataStore:GetAsync()|GetAsync`, `Class.GlobalDataStore:SetAsync()|SetAsync` or `Class.GlobalDataStore:UpdateAsync()|UpdateAsync`—through `Class.GlobalDataStore:UpdateAsync()|UpdateAsync`. This is because `Class.GlobalDataStore:UpdateAsync()|UpdateAsync` allows a key to be both read and written to atomically. It is also possible to abandon the write based on the value read by returning `nil` in the transformation callback. The transformation function passed into `Class.GlobalDataStore:UpdateAsync()|UpdateAsync` for each request performs the following operations: 1. Verifies the key is safe to access, abandoning the operation if it is not. "Safe to access" means: - The key's metadata object does not include an unrecognized `LockId` value that was last updated less than the lock expiry time ago. This accounts for respecting a lock placed by another server and for ignoring that lock if it expired. - If this server has placed its own `LockId` value in the key's metadata previously, then this value is still in the key's metadata. This accounts for the situation where another server has taken over this server's lock (by expiry or by force) and later released it. Alternatively phrased, even if `LockId` is `nil`, another server could still have replaced and removed a lock in the time since you locked the key. 2. `Class.GlobalDataStore:UpdateAsync()|UpdateAsync` performs the `Class.GlobalDataStore|DataStore` operation the consumer of `SessionLockedDataStoreWrapper` requested. For example, `Class.GlobalDataStore:GetAsync()|GetAsync()` translates to `function(value) return value end`. 3. Depending on the parameters passed into the request, `Class.GlobalDataStore:UpdateAsync()|UpdateAsync` either locks or unlocks the key: 1. If the key is to be locked, `Class.GlobalDataStore:UpdateAsync()|UpdateAsync` sets the `LockId` in the key's metadata to a GUID. This GUID is stored in-memory on the server so it can be verified the next time it accesses the key. If the server already has a lock on this key, it makes no changes. It also schedules a task to warn you if you don't access the key again to maintain the lock within the lock's expiration time. 2. If the key is to be unlocked, `Class.GlobalDataStore:UpdateAsync()|UpdateAsync` removes the `LockId` in the key's metadata. A custom retry handler is passed into the underlying `DataStoreWrapper` so that the operation is retried if it was aborted at step 1 due to the session being locked. A custom error message is also returned to the consumer, allowing the player data system to report an alternative error in the case of session locking to the client. ### Caveats The session locking regime relies on a server always releasing its lock on a key when it is done with it. This should always happen through an instruction to unlock the key as part of the final write in `Class.Players.PlayerRemoving|PlayerRemoving` or `Class.DataModel:BindToClose()|BindToClose()`. However, the unlock can fail in certain situations. For example: - The server crashed or `Class.DataStoreService` was inoperable for all attempts to access the key. - Due to an error in logic or similar bug, the instruction to unlock the key was not made. To maintain the lock on a key, you must regularly access it for as long as it is loaded in memory. This would normally be done as part of the auto-save loop running in the background in most player data systems, but this system also exposes a `refreshLockAsync` method if you need to do it manually. If the lock expiry time has been exceeded without the lock being updated, then any server is free to take over the lock. If a different server takes the lock, attempts by the current server to read or write the key fail unless it establishes a new lock. ## Developer Product processing **Singleton:** [`ReceiptHandler`](#sample-code) ### Background The `ProcessReceipt` callback performs the critical job of determining when to finalize a purchase. `ProcessReceipt` is called in very specific scenarios. For its set of guarantees, see `Class.MarketplaceService.ProcessReceipt`. Although the definition of "handling" a purchase can differ between experiences, we use the following criteria 1. The purchase has not previously been handled. 2. The purchase is reflected in the current session. 3. The purchase has been saved to a `Class.GlobalDataStore|DataStore`. Every purchase, even one-time consumables, should be reflected in the `Class.GlobalDataStore|DataStore` so users' purchase history is included with their session data. This requires conducting the following operations before returning `PurchaseGranted`: 1. Verify the `PurchaseId` has not already been recorded as handled. 2. Award the purchase in the player's in-memory player data. 3. Record the `PurchaseId` as handled in the player's in-memory player data. 4. Write the player's in-memory player data to the `Class.GlobalDataStore|DataStore`. Session locking simplifies this flow, as you no longer need to worry about the following scenarios: - The in-memory player data in the current server potentially being out-of-date, requiring you to fetch the latest value from the `Class.GlobalDataStore|DataStore` before verifying the `PurchaseId` history - The callback for the same purchase running in another server, requiring you to both read and write the `PurchaseId` history and save the updated player data with the purchase reflected atomically to prevent race conditions Session locking guarantees that, if an attempt to write to the player's `Class.GlobalDataStore|DataStore` is successful, no other server has successfully read or written to the player's `Class.GlobalDataStore|DataStore` between the data being loaded and saved in this server. In short, the in-memory player data in this server is the most up-to-date version available. There are some caveats, but they do not impact this behavior. ### Approach The comments in `ReceiptProcessor` outline the approach: 1. Verify that the player's data is currently loaded on this server and that it loaded without any errors. Because this system uses session locking, this check also verifies that the in-memory data is the most up-to-date version. If the player's data hasn't loaded yet (which is expected when a player joins a game), wait for the player's data to load. The system also listens for the player leaving the experience before their data loads, as it should not yield indefinitely and block this callback from being invoked again on this server for this purchase if the player rejoins. 2. Verify the `PurchaseId` is not already recorded as processed in the player data. Due to session locking, the array of `PurchaseIds` the system has in memory is the most up-to-date version. If the `PurchaseId` is recorded as processed and reflected in a value that has been loaded to or saved to the `Class.GlobalDataStore|DataStore`, return `PurchaseGranted`. If it is recorded as processed, but _not_ reflected in the `Class.GlobalDataStore|DataStore`, return `NotProcessedYet`. 3. Update the Player Data locally in this server to "award" the purchase.`ReceiptProcessor` takes a generic callback approach and assigns a different callback for each `DeveloperProductId`. > **Warning:** As soon as this step completes, the player receives access to the purchase in the current session. This means that if the player's data fails to save in step 5, the player receives the purchase for free for the duration of the session. 4. Update the player data locally in this server to store the `PurchaseId`. 5. Submit a request to save the in-memory data to the `Class.GlobalDataStore|DataStore`, returning `PurchaseGranted` if the request is successful. If not, return `NotProcessedYet`. If this save request is not successful, a later request to save the player's in-memory session data could still succeed. During the next `ProcessReceipt` call, step 2 handles this situation and returns `PurchaseGranted`. ## Player data **Singletons:** [`PlayerData.Server`](#sample-code), [`PlayerData.Client`](#sample-code) ### Background Modules that provide an interface for code to synchronously read and write player session data are common in Roblox experiences. This section covers `PlayerData.Server` and `PlayerData.Client`. ### Approach `PlayerData.Server` and `PlayerData.Client` handle the following: 1. Loading the player's data into memory, including handling cases in which it fails to load 2. Providing an interface for server code to query and change the player data 3. Replicating changes in the player's data to the client so that client code can access it 4. Replicating loading and/or saving errors to the client so that it can show error dialogs 5. Saving the player's data periodically, when the player leaves, and when the server shuts down #### Load player data ![An process diagram illustrating the loading system](../../assets/data/player-data-purchasing/data-load-diagram.png) 1. `SessionLockedDataStoreWrapper` makes a `getAsync` request to the data store. If this request fails, the default data is used and the profile is marked as "errored" to ensure it is not written to the data store later. An alternative option is to kick the player, but we recommend letting the player play with default data and clear messaging as to what occurred rather than removing them from the experience. 2. An initial payload is sent to `PlayerDataClient` containing the loaded data and the error status (if any). 3. Any threads yielded using `waitForDataLoadAsync` for the player are resumed. #### Provide an interface for server code - `PlayerDataServer` is a singleton that can be required and accessed by any server code running in the same environment. - Player data is organized into a dictionary of keys and values. You can manipulate these values on the server using the `setValue`, `getValue`, `updateValue` and `removeValue` methods. These methods all operate synchronously without yielding. - The `hasLoaded` and `waitForDataLoadAsync` methods are available to ensure the data has loaded before you access it. We recommend doing this once during a loading screen before other systems are started to avoid having to check for load errors before every interaction with data on the client. - A `hasErrored` method can query if the player's initial load failed, causing them to use default data. Check this method before allowing the player to make any purchases, as purchases cannot be saved to data without a successful load. - A `playerDataUpdated` signal fires with the `player`, `key`, and `value` whenever a player's data is changed. Individual systems can subscribe to this. #### Replicate changes to the client - Any change to player data in `PlayerDataServer` is replicated to `PlayerDataClient`, unless that key was marked as private using setValueAsPrivate - `setValueAsPrivate` is used to denote keys that should not be sent to the client - `PlayerDataClient` includes a method to get the value of a key (get) and a signal that fires when it is updated (updated). A `hasLoaded` method and a `loaded` signal are also included, so the client can wait for data to load & replicate before starting its systems - `PlayerDataClient` is a singleton that can be required and accessed by any client code running in the same environment #### Replicate errors to the client - Error statuses encountered when saving or loading player data are replicated to `PlayerDataClient`. - Access this information with the `getLoadError` and `getSaveError` methods, along with the `loaded` and `saved` signals. - There are two types of errors: `DataStoreError` (the `Class.DataStoreService` request failed) and `SessionLocked` (see [Session Locking](#session-locking)). - Use these events to disable client purchase prompts and implement warning dialogs. This image shows an example dialog: ![A screenshot of an example warning that could be shown when player data fails to load](../../assets/data/player-data-purchasing/data-warning.png) #### Save player data ![A process diagram illustrating the saving system](../../assets/data/player-data-purchasing/data-save-diagram.png) 1. When the player leaves the experience, the system takes the following steps: 1. Check if it is safe to write the player's data to the data store. Scenarios where it would be unsafe include the player's data failing to load or still undergoing loading. 2. Make a request through the `SessionLockedDataStoreWrapper` to write the current in-memory data value to the data store and remove the session lock once complete. 3. Clears the player's data (and other variables such as metadata and error statuses) from server memory. 2. On a periodic loop, the server writes each player's data to the data store (provided it is safe to save). This welcome redundancy mitigates loss in case of a server crash and is also necessary to maintain the session lock. 3. When a request to shutdown the server is received, the following occurs in a `Class.DataModel:BindToClose()|BindToClose` callback: 1. A request is made to save each player's data in the server, following the process normally gone through when a player leaves the server. These requests are made in parallel, as `Class.DataModel:BindToClose()|BindToClose` callbacks only have 30 seconds to complete. 2. To expedite the saves, all other requests in each key's queue are cleared from the underlying `DataStoreWrapper` (see [Retries](#retries)). 3. The callback doesn't return until all requests have completed. --- title: "Data store right to be forgotten (RTBF)" url: /docs/en-us/cloud-services/data-stores/right-to-be-forgotten last_updated: 2026-06-29T19:33:58Z description: "Configure automated deletion of data store keys and stores when you receive a right-to-be-forgotten request, using the Data Stores Manager or the Open Cloud Configs API with config templates." --- # Data store right to be forgotten (RTBF) **Right to be forgotten (RTBF)** for data stores lets you declare which keys and data stores hold a user's data. When Roblox processes an RTBF request for your experience, the system uses those templates to remove the matching data automatically. You can configure templates visually through the [Data Stores Manager](/docs/en-us/cloud-services/data-stores/data-stores-manager.md) in the Creator Hub, or programmatically through the Open Cloud [Configs API](/docs/en-us/cloud/reference/features/configs.md). ## Automated RTBF To use automated RTBF processing, you must define deletion templates. These templates tell Roblox which data stores or keys belong to a specific user. You can have up to 100 templates to identify your user data. ### Deletion templates Automated RTBF works by matching patterns. You define a template for your user data using a `{UserId}` token. When Roblox receives a right-to-be-forgotten request, the system replaces that token with the requester's actual ID and scans your data stores for matches to delete. ### Eligibility requirements For an entry to be eligible for automated deletion, it must meet the following criteria: - **Storage type** — User data should be stored in a standard data store or an ordered data store. Deletion of an entire data store is supported for standard data stores only. - **Identifier** — The user ID must be part of the `Name` or the `Scope` of the data store or key. - **Format** — The ID must be identifiable via a static string pattern (for example, `User_{UserId}`). ## Configure automated RTBF via Data Stores Manager The Creator Hub provides a visual interface to manage your RTBF templates without using the command line. > **Info:** For group experiences, you must add the **View Analytics** permission to view configs, and the **Edit and publish experience** permission to view, edit, and publish configs. Additionally, the user themself must be [eligible to publish experiences](https://en.help.roblox.com/hc/en-us/articles/203313890-How-to-Publish-Public-Experiences-on-Roblox). 1. Navigate to the [Creator Hub](https://create.roblox.com/dashboard/creations). 2. Select the experience you want to configure. 3. In the left-hand navigation menu, go to **Configure** ⟩ **Data Stores Manager**. 4. Select the **RTBF Deletion** tab. 5. Click **Create Template**. 6. Select the **Type** (**Standard Data Store**, **Standard Key**, or **Ordered Key**). 7. Enter the corresponding fields with your template using the `{UserId}` token (for example, `PlayerInventory_{UserId}`). This token is case-sensitive. 8. Double-check the sample output and click **Create**.![Create template dialog with Type, Data Store, Key Pattern, and Scope fields, and a preview of the resulting data store path.](./../../assets/data/data-store/Data-Stores-RTBF-Template.png) ## Configure automated RTBF via Configs API You can also manage RTBF templates programmatically using the Open Cloud Configs API. This is useful for automation or managing templates at scale. 1. In the Creator Dashboard, [create or edit an API key](https://create.roblox.com/dashboard/credentials) to include the **`universe:read`** and **`universe:write`** permissions for every universe where you want to automate RTBF deletion. For more detail, see [Manage API keys](/docs/en-us/cloud/auth/api-keys.md). 2. Templates are stored as a JSON configuration named `user_data_templates`. There are two main types: - **Key template** — Identifies specific keys. Requires `data_store_type` (`STANDARD` or `ORDERED`), `data_store_name`, and `key_pattern`. `scope_pattern` is optional but recommended. - **Data store template** — Identifies an entire data store. Requires `data_store_type` (currently only supports `STANDARD`) and `data_store_pattern`. For both template kinds, the `{UserId}` token is replaced with the user's ID when an RTBF request is processed.```json { "user_data_templates": [ { "key_template": { "data_store_type": "STANDARD", "data_store_name": "PlayerInventory", "key_pattern": "User_{UserId}", "scope_pattern": "Scope_{UserId}" } }, { "key_template": { "data_store_type": "ORDERED", "data_store_name": "PlayerLeaderboard", "key_pattern": "User_{UserId}", "scope_pattern": "global" } }, { "data_store_template": { "data_store_type": "STANDARD", "data_store_pattern": "Player_{UserId}_Save" } } ] } ``` > **Warning:** The `{UserId}` token is **case-sensitive**. If `scope_pattern` is omitted or blank, it defaults to `global`. 3. Next, submit via the Open Cloud [Configs API](/docs/en-us/cloud/reference/features/configs.md). In the following examples, replace `` and `` in each request, and send all requests to the `DataStoresConfig` repository. > **Info:** For additional endpoints and behavior, see the [Cloud API reference](/docs/en-us/cloud/reference/features/configs.md) and the [experience configs](/docs/en-us/cloud/guides/configs.md) guide. 1. Create a draft via a `PUT` request that defines your configuration. If a draft already exists, this request overwrites it.```bash curl --location --request PUT 'https://apis.roblox.com/creator-configs-public-api/v1/configs/universes//repositories/DataStoresConfig/draft:overwrite' \ --header 'x-api-key: ' \ --header 'Content-Type: application/json' \ --data-raw '{ "entries": { "user_data_templates": [ { "key_template": { "data_store_type": "STANDARD", "data_store_name": "PlayerInventory", "key_pattern": "User_{UserId}", "scope_pattern": "Scope_{UserId}" } }, { "key_template": { "data_store_type": "ORDERED", "data_store_name": "PlayerLeaderboard", "key_pattern": "User_{UserId}", "scope_pattern": "global" } }, { "data_store_template": { "data_store_type": "STANDARD", "data_store_pattern": "Player_{UserId}_Save" } } ] } }' ``` 2. Verify your draft via a `GET` request to retrieve and review the draft before it goes live.```bash curl --location --request GET 'https://apis.roblox.com/creator-configs-public-api/v1/configs/universes//repositories/DataStoresConfig/draft' \ --header 'x-api-key: ' ``` 3. After verification, send this `POST` request to publish. The only way to undo this action is to restore a previous version. This action also requires that the account associated with the API key is [eligible to publish experiences](https://en.help.roblox.com/hc/en-us/articles/203313890-How-to-Publish-Public-Experiences-on-Roblox).```bash curl --location --request POST 'https://apis.roblox.com/creator-configs-public-api/v1/configs/universes//repositories/DataStoresConfig/publish' \ --header 'x-api-key: ' \ --header 'Content-Type: application/json' \ --data-raw '{ "deploymentStrategy": "Immediate" }' ``` 4. After publishing, confirm the configuration is live with this `GET` request.```bash curl --location --request GET 'https://apis.roblox.com/creator-configs-public-api/v1/configs/universes//repositories/DataStoresConfig/full' \ --header 'x-api-key: ' ``` ## Best practices - **Case sensitivity** — Use the exact `{UserId}` token in your patterns. Variants such as `{userId}` are not accepted. - **Manual verification** — Compare your patterns to your live Luau usage using [Data Stores Manager](/docs/en-us/cloud-services/data-stores/data-stores-manager.md) in the Creator Hub before you publish configuration. - **Default scopes** — If a data store uses the default scope, set `scope_pattern` to `"global"` in the key template. - **Test end-to-end flow on a test experience** — For full validation of your templates, consider creating a test experience and dummy account, populating it with dummy data for that account's User ID, requesting RTBF on the dummy account, and ensuring the data is deleted once the account is processed. - **Confirm deletions** — After onboarding on your live experience, when an RTBF request appears in your Roblox messages, verify that the corresponding data is removed within 30 days. --- title: "Data store versioning, listing, and caching" url: /docs/en-us/cloud-services/data-stores/versioning-listing-and-caching last_updated: 2026-06-29T19:33:59Z description: "How to manage data stores (DataStores) through versioning, listing, and caching." --- # Data store versioning, listing, and caching Manage your data using versioning, listing, and caching. ## Versioning Versioning happens when you [set](/docs/en-us/cloud-services/data-stores.md#create-data), [update](/docs/en-us/cloud-services/data-stores.md#update-data), and [increment](/docs/en-us/cloud-services/data-stores.md#increment-data) data. The functions `Class.GlobalDataStore:SetAsync()|SetAsync()`, `Class.GlobalDataStore:UpdateAsync()|UpdateAsync()`, and `Class.GlobalDataStore:IncrementAsync()|IncrementAsync()` create versioned backups of your data using the first write to each key in each UTC hour. Successive writes to a key in the same UTC hour permanently overwrite the previous data. Versioned backups expire 30 days after a new write overwrites them. The latest version never expires. The following functions perform versioning operations: | Function | Description | | --- | --- | | `Class.DataStore:ListVersionsAsync()\|ListVersionsAsync()` | Lists all versions for a key by returning a `Class.DataStoreVersionPages` instance that you can use to enumerate all version numbers. You can filter versions using a time range. | | `Class.DataStore:GetVersionAsync()\|GetVersionAsync()` | Retrieves a specific version of a key using the key's version number. | | `Class.DataStore:RemoveVersionAsync()\|RemoveVersionAsync()` | Deletes a specific version of a key. This function also creates a tombstone version while retaining the previous version. For example, if you call `RemoveAsync("User_1234")` and then try to call `GetAsync("User_1234")`, you get `nil` back. However, you can still use `Class.DataStore:ListVersionsAsync()\|ListVersionsAsync()` and `Class.DataStore:GetVersionAsync()\|GetVersionAsync()` to retrieve older versions of the data. | You can use versioning to handle user requests. If a user reports that a problem occurred at 2020-10-09T01:42, you can revert data to a previous version using the following example: ```lua local DataStoreService = game:GetService("DataStoreService") local experienceStore = DataStoreService:GetDataStore("PlayerExperience") local DATA_STORE_KEY = "User_1234" local maxDate = DateTime.fromUniversalTime(2020, 10, 09, 01, 42) -- Gets the version closest to the given time local listSuccess, pages = pcall(function() return experienceStore:ListVersionsAsync(DATA_STORE_KEY, Enum.SortDirection.Descending, nil, maxDate.UnixTimestampMillis) end) if listSuccess then local items = pages:GetCurrentPage() if #items > 0 then -- Reads the closest version local closestEntry = items[1] local success, value, info = pcall(function() return experienceStore:GetVersionAsync(DATA_STORE_KEY, closestEntry.Version) end) -- Restores current value by overwriting it with the closest version if success then local setOptions = Instance.new("DataStoreSetOptions") setOptions:SetMetadata(info:GetMetadata()) experienceStore:SetAsync(DATA_STORE_KEY, value, nil, setOptions) end else -- No entries found end end ``` ### Snapshots The [Snapshot Data Stores Open Cloud API](/docs/en-us/cloud/reference/DataStore.md#Cloud_SnapshotDataStores) lets you take a snapshot of all data stores in an experience once a day. Before you publish any experience update that changes your data storage logic, make sure to take a snapshot. Taking a snapshot guarantees that you have the most recent data available from the previous version of the experience. For example, without a snapshot, if you publish an update at 3:30 UTC that causes data corruption, the corrupted data overwrites any data written between 3:00-3:30 UTC. If you take a snapshot at 3:29 UTC, though, the corrupted data doesn't overwrite anything written before 3:29 UTC, and the latest data for all keys written between 3:00-3:29 UTC is preserved. ## Listing and prefixes Data stores let you list by prefix. For example, listing by the first **n** characters of a name, like "d" , "do", or "dog" for any key or data store with a prefix of "dog". You can specify a prefix when listing all data stores or keys, and get back only objects that match that prefix. Both `Class.DataStoreService:ListDataStoresAsync()|ListDataStoresAsync()` and `Class.DataStore:ListKeysAsync()|ListKeysAsync()` functions return a `Class.DataStoreListingPages` object that you can use to enumerate the list. | Function | Description | | --- | --- | | `Class.DataStoreService:ListDataStoresAsync()\|ListDataStoresAsync()` | Lists all data stores. | | `Class.DataStore:ListKeysAsync()\|ListKeysAsync()` | Lists all keys in a data store. | ### Scopes > **Warning:** For new experiences, use [listing and prefixes](#listing-and-prefixes) to organize keys in your data store instead of the legacy scopes feature. For existing experiences that use scopes, continue using them. You can organize keys in a data store further by setting a unique string as a scope for the second parameter of `Class.DataStoreService:GetDataStore()|GetDataStore()`. The default scope (if no scope is given) is `global`. The scope is automatically prepended to the beginning of all keys in all operations done on the data store. | Key | Scope | | --- | --- | | `houses/User_1234` | houses | | `pets/User_1234` | pets | | `inventory/User_1234` | inventory | The combination of data store name, scope, and key uniquely identifies a key. All three values are required to identify a key with a scope. For example, you can read a `global`-scoped key named `User_1234` as: ```lua local DataStoreService = game:GetService("DataStoreService") local inventoryStore = DataStoreService:GetDataStore("PlayerInventory") local success, currentGold = pcall(function() return inventoryStore:GetAsync("User_1234") end) ``` If the key `User_1234` has a scope of `gold`, though, you can only read it as: ```lua local DataStoreService = game:GetService("DataStoreService") local inventoryStore = DataStoreService:GetDataStore("PlayerInventory", "gold") local success, currentGold = pcall(function() return inventoryStore:GetAsync("User_1234") end) ``` ### AllScopes property `Class.DataStoreOptions` contains an `Class.DataStoreOptions.AllScopes|AllScopes` property that lets you return keys from all [scopes](#scopes) in a list. You can then use the `Class.DataStoreKey.KeyName|KeyName` property of a list item for common data store operations like [reading data](/docs/en-us/cloud-services/data-stores.md#reading-data) with `Class.GlobalDataStore:GetAsync()|GetAsync()` and [removing data](/docs/en-us/cloud-services/data-stores.md#removing-data) with `Class.GlobalDataStore:RemoveAsync()|RemoveAsync()`. When you use the `AllScopes` property, the second parameter of `Class.DataStoreService:GetDataStore()|GetDataStore()` must be an empty string (`""`). ```lua local DataStoreService = game:GetService("DataStoreService") local options = Instance.new("DataStoreOptions") options.AllScopes = true local ds = DataStoreService:GetDataStore("DS1", "", options) ``` > **Info:** When you use the `Class.DataStoreOptions.AllScopes|AllScopes` property, `Class.DataStore:ListKeysAsync()|ListKeysAsync()` returns every key with their scope as the prefix argument, such as `global/player_data_1234` or `houses/house3`. Remember that the default scope is `global`. If you enable the `Class.DataStoreOptions.AllScopes|AllScopes` property and create a new key in the data store, you must always specify a scope for that key in the format of scope/keyname. If you don't, the APIs throw an error. For example, `gold/player_34545` is acceptable with gold as the scope, but `player_34545` leads to an error. | `global/K1` | `house/K1` | | --- | --- | | `global/L2` | `house/L2` | | `global/M3` | `house/M3` | ## Caching Use caching to temporarily store data from data stores to improve performance and reduce the number of requests made to the server. For example, an experience can cache a copy of its data so that it can access that data quickly without having to make another call to the data store. Caching applies to modifications you make to data store keys using: - `Class.GlobalDataStore:GetAsync()|GetAsync()` to [read data](/docs/en-us/cloud-services/data-stores.md#reading-data). - `Class.GlobalDataStore:SetAsync()|SetAsync()` to [create data](/docs/en-us/cloud-services/data-stores.md#creating-data). - `Class.GlobalDataStore:UpdateAsync()|UpdateAsync()` to [update data](/docs/en-us/cloud-services/data-stores.md#updating-data). - `Class.GlobalDataStore:IncrementAsync()|IncrementAsync()` to [increment data](/docs/en-us/cloud-services/data-stores.md#incrementing-data). - `Class.GlobalDataStore:RemoveAsync()|RemoveAsync()` to [remove data](/docs/en-us/cloud-services/data-stores.md#removing-data). `Class.DataStore:GetVersionAsync()|GetVersionAsync()`, `Class.DataStore:ListVersionsAsync()|ListVersionsAsync()`, `Class.DataStore:ListKeysAsync()|ListKeysAsync()`, and `Class.DataStoreService:ListDataStoresAsync()|ListDataStoresAsync()` don't implement caching and always fetch the latest data from the service backend. By default, the engine uses `Class.GlobalDataStore:GetAsync()|GetAsync()` to store values you retrieve from the backend in a local cache for four seconds. Also by default, `Class.GlobalDataStore:GetAsync()|GetAsync()` requests for cached keys return the cached value instead of continuing to the backend. Your `Class.GlobalDataStore:GetAsync()|GetAsync()` requests that return a cached value don't count towards your [server limits](/docs/en-us/cloud-services/data-stores/error-codes-and-limits.md#server-limits) and [throughput limits](/docs/en-us/cloud-services/data-stores/error-codes-and-limits.md#throughput-limits). All `Class.GlobalDataStore:GetAsync()|GetAsync()` calls that retrieve a value not being cached from the backend update the cache immediately and restart the four second timer. > **Warning:** Caching is local to a particular data store instance, so different data stores can have their caches in different states. For example, if you access a key twice through two different data store instances, like getting one data store with a specified scope and another through having the `Class.DataStoreOptions.AllScopes|AllScopes` property enabled, each data store instance has its own cache. If you change the value of that key in one data store instance and not the other, you end up with inconsistent data. ### Disable caching To disable caching and opt out of using the cache to retrieve the most up-to-date value from the servers, add the `Class.DataStoreGetOptions` parameter to your `Class.GlobalDataStore:GetAsync()|GetAsync()` call and set the `Class.DataStoreGetOptions.UseCache|UseCache` property to `false` to make your request ignore any keys in the cache. Disabling caching is useful if you have multiple servers writing to a key with high frequency and need to get the latest value from servers. However, it can cause you to consume more of your [data stores limits and quotas](/docs/en-us/cloud-services/data-stores/error-codes-and-limits.md#limits), since `Class.GlobalDataStore:GetAsync()|GetAsync()` requests bypassing caching always count towards your throughput and server limits. For immediate verification reads after a write, use `Class.GlobalDataStore:GetAsync()|GetAsync()` with `DataStoreGetOptions.UseCache = false`. By default, `Class.GlobalDataStore:GetAsync()|GetAsync()` uses a local four-second cache, so a normal verification read can return stale data. This is especially important after write operations like `Class.GlobalDataStore:UpdateAsync()|UpdateAsync()`, `Class.GlobalDataStore:SetAsync()|SetAsync()`, `Class.GlobalDataStore:IncrementAsync()|IncrementAsync()`, or `Class.GlobalDataStore:RemoveAsync()|RemoveAsync()` return an error. In these cases, your code needs to determine whether to retry, refund, or take other corrective action. The following example shows how to bypass the cache when verifying the result of a write: ```lua local ok = pcall(function() store:UpdateAsync(key, transform) end) if not ok then local options = Instance.new("DataStoreGetOptions") options.UseCache = false local success, value = pcall(function() return store:GetAsync(key, options) end) if success then -- decide based on backend state, not cached state end end ``` ## Serialization The `Class.DataStoreService` stores data in JSON format. When you save Luau data in Studio, Roblox uses a process called serialization to convert that data into JSON to save it in data stores. Roblox then converts your data back to Luau and returns it to you in another process called deserialization. Serialization and deserialization support the following Luau data types: - [Nil](/docs/en-us/luau/nil.md) - [Booleans](/docs/en-us/luau/booleans.md) - [Numbers](/docs/en-us/luau/numbers.md) - You should not store the special numeric values `inf`, `-inf`, and `nan`, because these values don't conform to JSON standards. You can't access keys that contain these values with Open Cloud. - [Strings](/docs/en-us/luau/strings.md) - [Tables](/docs/en-us/luau/tables.md) - Tables must only contain other supported data types - Numeric keys are translated into strings if the length of the table is 0 - [Buffers](/docs/en-us/reference/engine/libraries/buffer.md) If you try to store a data type that serialization doesn't support, you either: - Fail in storing that data type and get an error message. - Succeed in storing that data type as `nil`. To debug why your data type is being stored as `nil`, you can use the `Class.HttpService.JSONEncode|JSONEncode` function. When you pass your Luau data type into this function, you receive it back in the format Roblox would have stored it with data stores, which lets you preview and investigate the returned data. > **Info:** Serialization doesn't happen when you use the [DataStore Open Cloud API](/docs/en-us/cloud/reference/DataStore.md) because that data is already sent to Roblox in JSON format and doesn't need to be converted. --- title: "Choose between cloud services" url: /docs/en-us/cloud-services/data-stores-vs-memory-stores last_updated: 2026-06-29T19:33:58Z description: "When to use standard data stores, ordered data stores, memory stores, secrets stores, and configs." --- # Choose between cloud services Roblox offers several options for data storage. Each storage option has pros and cons and is appropriate for different use cases. This table summarizes the available options and provides the closest points of comparison from standard web services. | Service | Persistence | Scope | In-game access | Cloud comparison | | --- | --- | --- | --- | --- | | **Data stores** | Permanent | Cross-server | Read-write | NoSQL database | | **Memory stores** | 45 day maximum | Cross-server | Read-write | In-memory cache | | **Configs** | Permanent | Cross-server | Read-only | App configuration and feature flags | | **Secrets stores** | Permanent | Cross-server | Read-only | Secrets management | | **Luau in-memory** | Session duration | Single server | Read-write | Server RAM | > **Success:** Within Roblox games, cloud services are only accessible from server scripts. ## When to use data stores `Class.DataStoreService` stores long-term data that needs to last between sessions, such as user progress or inventory items. Data stores are consistent per game, so every server for every place within a game can access and change the same data. There are two types of data stores: standard and ordered. - **Standard data stores** can store data like numbers, strings, and tables that don't need to be ranked or sorted. They store data as key-value pairs; each entry is stored under a key that is unique within its data store and that you can retrieve, update, or delete. For more information, see [Data stores](/docs/en-us/cloud-services/data-stores.md). - **Ordered data stores** can only store numbers. Each entry is stored under a key that is unique within its data store and that you can retrieve, update, or delete. You can rank and sort this data numerically and retrieve it in ascending or descending order based on stored numerical values. For more information, see [Ordered data stores](/docs/en-us/cloud-services/data-stores.md#ordered-data-stores). | **Standard data stores** | **Ordered data stores** | **Data type** | Numbers, strings, booleans, and tables. | Only numbers. | | --- | --- | --- | | **Common use cases** | User progress, inventory items, and game settings. | All-time, persistent ranking systems and leaderboards. Unlike leaderboards in memory stores, this leaderboard data is permanent. | | **Past version backup** | Automatically manages previous versions of your data for 30 days. | Does not manage previous versions of your data. | > **Info:** If you want to add granular permission control to your data stores and access them outside of Studio or Roblox servers, use the [Open Cloud APIs for data stores](/docs/en-us/cloud/reference/features/storage.md). ## When to use memory stores `Class.MemoryStoreService` is a high throughput and low latency service that stores temporary data that needs to be updated or accessed frequently, such as global leaderboards or matchmaking queues. With memory stores, every server for every place within a game can access and change the same data quickly and frequently. Data in a memory store expires after a certain period of time, up to 45 days. If you need to quickly read (not write to) a small number of values across all servers, [configs](#when-to-use-configs) are the better choice. Memory stores are not as performant as configs when frequently accessing a single key or partition. Although memory stores store temporary data, they also support permanent features like a global marketplace. The marketplace is permanent, but the items for sale inside it have an expiration date. | **Memory stores** | **Data type** | Numbers, strings, booleans, and tables that don't need to persist for over 45 days. | | --- | --- | | **Common use cases** | Skill-based matchmaking, match states for multiplayer games. | | Daily and monthly leaderboards. | ## When to use configs [Configs](/docs/en-us/production/configs.md) let you update in-game variables in real time without restarting servers. They are ideal for feature flags and any in-game values you hope to tune over time or [experiment](/docs/en-us/production/experiments.md) with. - Configs are read-only from within a game—you modify them on Creator Hub or in Roblox Studio—so they don't serve the same use cases as data stores and memory stores. - Changes to configs deploy over the course of five minutes; changes to data and memory stores are more instantaneous. | **Configs** | **Data type** | Numbers, strings, booleans, and JSON objects. | | --- | --- | | **Common use cases** | Enabling a new dungeon, disabling a limited-time game mode, starting a holiday event. | | Enemy health, weapon damage, item prices, game multipliers, welcome messages. | ## When to use secrets stores Use the [secrets store](/docs/en-us/cloud-services/secrets.md) for your game to store any API keys, passwords, and access tokens that you use to authenticate with external services. Storing these values in a data store presents unnecessary security risks. | **Secrets stores** | **Data type** | Strings. | | --- | --- | | **Common use cases** | API keys, passwords, and access tokens for analytics or music services. | ## When to use in-memory storage in Luau Use in-memory storage in Luau to store temporary data that needs to be accessed with minimal latency and without the cost of making external service calls. This storage is built into Luau and requires no setup. | **In-memory storage in Luau** | **Data type** | Numbers, strings, booleans, and tables. | | --- | --- | | **Common use cases** | Data that is only relevant to a single server session and doesn't need to be persisted, such as status effects or temporary points that reset when the user leaves the game. | | Values that change frequently, like counters, timers, state flags, or a health bar. | | Data that drives game logic, which needs to be accessible instantly, like temporary variables or an enemy's current health. | --- title: "Extended Services" url: /docs/en-us/cloud-services/extended-services last_updated: 2026-06-29T19:33:58Z description: "Extended Services is a platform that lets you manage your free and paid services." --- # Extended Services Extended Services is a solution that lets you manage service usage and payment beyond Roblox's default limits. ## Eligibility requirements To use Extended Services, your Roblox account must: - Be [ID-verified](/docs/en-us/production/publishing/account-verification.md#verify-through-government-id) and 18+ years old. - Be located in a [supported country](#supported-countries). - Have a valid email address associated with it. - Not currently be banned or moderated. - Accept and comply with the [terms of service](https://en.help.roblox.com/hc/articles/37967848292500). > **Info:** For group accounts, only the group owner needs to meet the eligibility requirements. ## Unlock Extended Services If you meet all of the [eligibility requirements](#eligibility-requirements), you can finish setting up Extended Services by entering your account details and payment information. For group-owned games, only the group owner can unlock Extended Services and onboard the game onto it. To unlock and use Extended Services: 1. In the [Creator Dashboard](https://create.roblox.com/dashboard/creations), go to **Creations** and select a game. 2. Go to **Extended Services**. 3. If you haven't entered your account details and payment information yet, click **Add missing information** on the top banner. 1. Enter your account information. The **Account type**, **Tax ID type**, and **Tax ID** fields are optional. - You can also go to **Finances** ⟩ **Account Information** under the **Extended Services** section to manage your account information. You don't need to update anything else on this page. 2. Click **Next**. 3. Enter a payment method. You can enter a new payment method or select from the available payment options already associated with your Roblox account. - You can also go to **Finances** ⟩ **Payments** to add new payment methods, or go to **Finances** ⟩ **Billing** to set a new default payment method. 4. Click **Save**. ##### Account status Your Extended Services account can be in one of the following statuses: **Status** | **Description** | **Required action** | **Normal** | You can use all Extended Services features. | No action is required. | | --- | --- | --- | | **Overdue** | Your account is overdue because your automatic payment method failed. Roblox offers a grace period of **seven days** before suspending your Extended Services account. | Change your payment method and retry a manual payment, or wait until Roblox attempts to charge your payment method again after the grace period has ended. | | **Suspended** | You can't use any paid Extended Services. You can, however, still use services with default limits. | Fix your eligibility requirements, add a valid payment method, or manually pay the overdue balance on your account. | ## Enable a service To enable a service: 1. Go to **Extended Services**. 2. Toggle on the service you want to enable. 3. Enter a monthly budget for each applicable resource under the service. The minimum monthly budget you can set is $0.50 USD. Your payment method is charged either at the end of the month when a bill is generated, or when your usage reaches $10 USD, whichever comes first. You can increase or decrease a service's monthly budget any time. Your service is automatically throttled if you decrease your budget to an amount below your current month-to-date usage, or if you exceed the budget you have set. Roblox notifies you by email when your usage reaches 80% of your budget, and then again when it reaches 100%. ## Manage billing To access the Extended Services billing dashboard: 1. Go to **Finances** ⟩ **Billing**. 2. Select the **Cloud services** tab to display the following: - Your pending balance, including month-to-date cost - Your billing history, including all Extended Services billing activity across your games For a detailed breakdown of your current pending balance costs, click **View Details** next to your payment method. For a detailed invoice of a previous bill, click **View Bill** under **Billing history**. ## Service pricing See the following table for Extended Services pricing: | **Service** | | **Default** | **Extended** | | --- | --- | --- | --- | | [Memory stores](https://create.roblox.com/docs/cloud-services/memory-stores) | Requests | 1000 + (CCU x 120) requests per minute | $0.003 / 1M requests | | | Storage | 64KB + (CCU x 1.2KB) | $0.10 / GB per hour¹ | | [Text-to-speech](https://create.roblox.com/docs/audio/objects#text-to-speech) | Requests | 1 + (CCU x 6) requests per minute | $5.00 / 1M requests | | [Speech-to-text](https://create.roblox.com/docs/audio/objects#speech-to-text) | Requests | 1 + (CCU x 5) requests per minute | $1 / 1M requests | | [Standard data stores](https://create.roblox.com/docs/cloud-services/data-stores) | Get | 250 + (CCU * 40) requests per minute | $0.08 / 1M requests | | | Set | 250 + (CCU * 20) requests per minute | $0.80 / 1M requests | | | List | 10 + (CCU * 2) requests per minute | $0.60 / 1M requests | | | Remove | 100 + (CCU * 40) requests per minute | $0.80 / 1M requests | | [Ordered data stores](https://create.roblox.com/docs/cloud-services/data-stores) | Get | 250 + (CCU * 40) requests per minute | $0.08 / 1M requests | | | Set | 250 + (CCU * 20) requests per minute | $0.25 / 1M requests | | | GetSorted | 100 + (CCU * 2) requests per minute | $0.32 / 1M requests | | | Remove | 100 + (CCU * 40) requests per minute | $0.25 / 1M requests | | [Data store storage](https://create.roblox.com/docs/cloud-services/data-stores) | Storage | 100 MB + (1MB * Lifetime Players) GB per month | $0.12 / GB per month | | [Compute](https://create.roblox.com/docs/production/analytics/performance) | CPU cores | First 100K playtime hours per month at no charge (when enrolled in Extended Services for Compute) | $0.001 / playtime hour² | _¹ GB per hour = the storage consumption multiplied by the amount of time. For example, 100GB hours might equate to "100GB x 1 hour" or "200GB x 0.5 hours".__² Extended Services for Compute is billed based on how long the feature remains enabled, measured in minutes of playtime. Because enabling the feature reserves additional compute capacity for your game, charges apply whenever the feature is active, even if the additional compute is not fully consumed. Playtime hours are calculated as the sum of all player session durations. For example, if 10,000 users each play for 1 hour, that equals 10,000 playtime hours, resulting in $10 in charges beyond the included monthly allowance of 100,000 playtime hours. These limits and pricing only apply to games enrolled in Extended Services for Compute. Games that do not enroll continue to receive unlimited playtime hours under the current default limits at no additional charge._## Supported countries Extended Services is available in the following countries: | Country | | --- | | Australia | Germany | Poland | | Austria | Hong Kong | Portugal | | Belgium | Hungary | Romania | | Brazil | Ireland | Singapore | | Bulgaria | Italy | Slovakia | | Croatia | Latvia | Slovenia | | Cyprus | Lithuania | South Africa | | Czechia | Malta | Spain | | Denmark | Mexico | Sweden | | Estonia | Netherlands | Switzerland | | Finland | New Zealand | Thailand | | France | Norway | United States | --- title: "In-game HTTP requests" url: /docs/en-us/cloud-services/http-service last_updated: 2026-06-29T19:33:59Z description: "Use HttpService to send HTTP requests to third-party web services and Open Cloud." --- # In-game HTTP requests You can use `Class.HttpService` to send generic HTTP requests to third-party web services for use cases like analytics, data storage, or error logging. `Class.HttpService` also supports certain Open Cloud endpoints. ## Enable HTTP requests The `Class.HttpService:GetAsync()`, `Class.HttpService:PostAsync()`, and `Class.HttpService:RequestAsync()` methods aren't enabled by default. To send requests, you must **Allow HTTP Requests** under [Experience Settings](/docs/en-us/studio/experience-settings.md) for your game. ## Use in plugins You can use `Class.HttpService` in Studio plugins to check for updates, download content, or other business logic. The first time a plugin attempts to use the service, the user might be prompted to give the plugin permission to communicate with the particular web address. Users can accept, deny, and revoke these permissions at any time through the **Plugin Management** window. Plugins can also communicate with other software running on the same computer through the `localhost` and `127.0.0.1` hosts. By running programs compatible with such plugins, you can extend the functionality of your plugin beyond the normal capabilities of Studio, such as interacting with your computer's file system. Beware that such software must be distributed separately from the plugin itself and can pose security risks. ## Use with Open Cloud `Class.HttpService` can currently call a subset of the Open Cloud endpoints. You can call these endpoints the same way that you'd call any other endpoint via `HttpService`. The only difference is that you must include an Open Cloud API key in the request: 1. [Create an Open Cloud API key](/docs/en-us/cloud/auth/api-keys.md#create-api-keys). 2. [Save the API key to your secrets store](/docs/en-us/cloud-services/secrets.md#add-secrets). 3. Make the request. The following code sample demonstrates how to update a user's group membership from within a game: ```lua local HttpService = game:GetService("HttpService") local groupId = "your_group_id" local membershipId = "your_membership_id" local roleId = "your_role_id" local function request() local response = HttpService:RequestAsync({ Url = \`https://apis.roblox.com/cloud/v2/groups/{groupId}/memberships/{membershipId}\`, Method = "PATCH", Headers = { ["Content-Type"] = "application/json", -- When sending JSON, set this! ["x-api-key"] = HttpService:GetSecret("APIKey"), -- Set in Creator Hub }, Body = HttpService:JSONEncode({ role = \`groups/{groupId}/roles/{roleId}\` }), }) if response.Success then print("The response was successful:", response.StatusCode, response.StatusMessage) else print("The response returned an error:", response.StatusCode, response.StatusMessage) end print("Response body:\n", response.Body) print("Response headers:\n", HttpService:JSONEncode(response.Headers)) end -- Wrap the function in pcall() for safety local success, errorMessage = pcall(request) if not success then print("The HTTP request failed to send:", errorMessage) end ``` ### Supported Open Cloud endpoints The following endpoints are supported. Due to current limitations on `Class.HttpService`, the `..` string is not allowed in URL path parameters to Roblox domains. This means, for example, that data stores and entries containing this string are currently inaccessible from `Class.HttpService`. #### Assets - [GetAsset](/docs/en-us/cloud/features/assets.md#/default/Assets_GetAsset) - [ListAssetVersions](/docs/en-us/cloud/features/assets.md#/default/listAssetVersions) - [GetAssetVersion](/docs/en-us/cloud/features/assets.md#/default/Assets_GetAssetVersion) #### Bans and blocks - [ListUserRestrictions (Place)](/docs/en-us/cloud/features/bans-and-blocks.md#/UserRestriction/Cloud_ListUserRestrictions__Using_Universes) - [GetUserRestriction (Place)](/docs/en-us/cloud/features/bans-and-blocks.md#/UserRestriction/Cloud_GetUserRestriction__Using_Universes_Places) - [UpdateUserRestriction (Place)](/docs/en-us/cloud/features/bans-and-blocks.md#/UserRestriction/Cloud_UpdateUserRestriction__Using_Universes_Places) - [ListUserRestrictions (Universe)](/docs/en-us/cloud/features/bans-and-blocks.md#/UserRestriction/Cloud_ListUserRestrictions) - [GetUserRestriction (Universe)](/docs/cloud/features/bans-and-blocks#/UserRestriction/Cloud_GetUserRestriction__Using_Universes) - [UpdateUserRestriction (Universe)](/docs/en-us/cloud/features/bans-and-blocks.md#/UserRestriction/Cloud_UpdateUserRestriction__Using_Universes) - [ListUserRestrictionLogs (Universe)](/docs/en-us/cloud/features/bans-and-blocks.md#/UserRestriction/Cloud_ListUserRestrictionLogs) #### Configs - [GetConfigRepositoryValues](/docs/en-us/cloud/reference/features/configs.md#CreatorConfigsPublicApi_GetConfigRepositoryValues) - [GetConfigRepositoryDraft](/docs/en-us/cloud/reference/features/configs.md#CreatorConfigsPublicApi_GetConfigRepositoryDraft) - [UpdateDraft](/docs/en-us/cloud/reference/features/configs.md#CreatorConfigsPublicApi_UpdateDraft) - [DeleteDraft](/docs/en-us/cloud/reference/features/configs.md#CreatorConfigsPublicApi_DeleteDraft) - [OverwriteDraft](/docs/en-us/cloud/reference/features/configs.md#CreatorConfigsPublicApi_OverwriteDraft) - [GetConfigRepositoryFull](/docs/en-us/cloud/reference/features/configs.md#CreatorConfigsPublicApi_GetConfigRepositoryFull) - [PublishDraft](/docs/en-us/cloud/reference/features/configs.md#CreatorConfigsPublicApi_PublishDraft) - [ListRevisions](/docs/en-us/cloud/reference/features/configs.md#CreatorConfigsPublicApi_ListRevisions) - [RestoreRevision](/docs/en-us/cloud/reference/features/configs.md#CreatorConfigsPublicApi_RestoreRevision) #### Creator Store - [CreateCreatorStoreProduct](/docs/en-us/cloud/features/creator-store.md#/CreatorStoreProduct/Cloud_CreateCreatorStoreProduct) - [GetCreatorStoreProduct](/docs/en-us/cloud/features/creator-store.md#/CreatorStoreProduct/Cloud_GetCreatorStoreProduct) - [UpdateCreatorStoreProduct](/docs/en-us/cloud/features/creator-store.md#/CreatorStoreProduct/Cloud_UpdateCreatorStoreProduct) - [CreatorStoreAssetsSearch](/docs/en-us/cloud/features/creator-store.md#toolbox-service) #### Developer products - [CreateDeveloperProduct](/docs/en-us/cloud/reference/features/developer-products.md#DeveloperProducts_CreateDeveloperProductV2) - [UpdateDeveloperProduct](/docs/en-us/cloud/reference/features/developer-products.md#DeveloperProducts_UpdateDeveloperProductV2) - [GetDeveloperProductConfig](/docs/en-us/cloud/reference/features/developer-products.md#DeveloperProducts_GetDeveloperProductConfigV2) - [ListDeveloperProductConfigsByUniverse](/docs/en-us/cloud/reference/features/developer-products.md#DeveloperProducts_ListDeveloperProductConfigsByUniverseV2) #### Game passes - [CreateGamePass](/docs/en-us/cloud/reference/features/game-passes.md#GamePasses_CreateGamePass) - [UpdateGamePass](/docs/en-us/cloud/reference/features/game-passes.md#GamePasses_UpdateGamePass) - [GetGamePassConfig](/docs/en-us/cloud/reference/features/game-passes.md#GamePasses_GetGamePassConfig) - [ListGamePassConfigsByUniverse](/docs/en-us/cloud/reference/features/game-passes.md#GamePasses_ListGamePassConfigsByUniverse) #### Data and memory stores Data stores: - [ListDataStores](/docs/en-us/cloud/reference/DataStore.md#Cloud_ListDataStores) - [SnapshotDataStores](/docs/en-us/cloud/features/storage.md#/DataStore/Cloud_SnapshotDataStores) - [ListDataStoreEntries](/docs/en-us/cloud/reference/DataStoreEntry.md#Cloud_ListDataStoreEntries__Using_Universes) - [CreateDataStoreEntry](/docs/en-us/cloud/reference/DataStoreEntry.md#Cloud_CreateDataStoreEntry__Using_Universes) - [GetDataStoreEntry](/docs/en-us/cloud/reference/DataStoreEntry.md#Cloud_GetDataStoreEntry__Using_Universes_DataStores) - [DeleteDataStoreEntry](/docs/en-us/cloud/reference/DataStoreEntry.md#Cloud_DeleteDataStoreEntry__Using_Universes_DataStores) - [UpdateDataStoreEntry](/docs/en-us/cloud/reference/DataStoreEntry.md#Cloud_UpdateDataStoreEntry__Using_Universes_DataStores) - [IncrementDataStoreEntry](/docs/en-us/cloud/reference/DataStoreEntry.md#Cloud_IncrementDataStoreEntry__Using_Universes_DataStores) - [ListDataStoreEntryRevisions](/docs/en-us/cloud/features/storage.md#/DataStoreEntry/Cloud_ListDataStoreEntryRevisions__Using_Universes_DataStores) - [ListDataStoreEntries (With Scope)](/docs/en-us/cloud/features/storage.md#/DataStoreEntry/Cloud_ListDataStoreEntries__Using_Universes_DataStores) - [CreateDataStoreEntry (With Scope)](/docs/en-us/cloud/features/storage.md#/DataStoreEntry/Cloud_CreateDataStoreEntry__Using_Universes_DataStores) - [GetDataStoreEntry (With Scope)](/docs/en-us/cloud/features/storage.md#/DataStoreEntry/Cloud_GetDataStoreEntry__Using_Universes_DataStores_Scopes) - [DeleteDataStoreEntry (With Scope)](/docs/en-us/cloud/features/storage.md#/DataStoreEntry/Cloud_DeleteDataStoreEntry__Using_Universes_DataStores_Scopes) - [UpdateDataStoreEntry (With Scope)](/docs/en-us/cloud/features/storage.md#/DataStoreEntry/Cloud_UpdateDataStoreEntry__Using_Universes_DataStores_Scopes) - [IncrementDataStoreEntry (With Scope)](/docs/en-us/cloud/features/storage.md#/DataStoreEntry/Cloud_IncrementDataStoreEntry__Using_Universes_DataStores_Scopes) - [ListDataStoreEntryRevisions (With Scope)](/docs/en-us/cloud/features/storage.md#/DataStoreEntry/Cloud_ListDataStoreEntryRevisions__Using_Universes_DataStores_Scopes) Memory stores: - [CreateMemoryStoreQueueItem](/docs/en-us/cloud/features/storage.md#/MemoryStoreQueueItem/Cloud_CreateMemoryStoreQueueItem) - [DiscardMemoryStoreQueueItems](/docs/en-us/cloud/features/storage.md#/MemoryStoreQueueItem/Cloud_DiscardMemoryStoreQueueItems) - [ReadMemoryStoreQueueItems](/docs/en-us/cloud/features/storage.md#/MemoryStoreQueueItem/Cloud_ReadMemoryStoreQueueItems) - [ListMemoryStoreSortedMapItems](/docs/en-us/cloud/features/storage.md#/MemoryStoreSortedMapItem/Cloud_ListMemoryStoreSortedMapItems) - [CreateMemoryStoreSortedMapItem](/docs/en-us/cloud/features/storage.md#/MemoryStoreSortedMapItem/Cloud_CreateMemoryStoreSortedMapItem) - [GetMemoryStoreSortedMapItem](/docs/en-us/cloud/features/storage.md#/MemoryStoreSortedMapItem/Cloud_GetMemoryStoreSortedMapItem) - [DeleteMemoryStoreSortedMapItem](/docs/en-us/cloud/features/storage.md#/MemoryStoreSortedMapItem/Cloud_DeleteMemoryStoreSortedMapItem) - [UpdateMemoryStoreSortedMapItem](/docs/en-us/cloud/features/storage.md#/MemoryStoreSortedMapItem/Cloud_UpdateMemoryStoreSortedMapItem) - [FlushMemoryStore](/docs/en-us/cloud/features/storage.md#/MemoryStore/Cloud_FlushMemoryStore) Ordered data stores: - [ListOrderedDataStoreEntries](/docs/en-us/cloud/features/storage.md#/OrderedDataStoreEntry/Cloud_ListOrderedDataStoreEntries) - [CreateOrderedDataStoreEntry](/docs/en-us/cloud/features/storage.md#/OrderedDataStoreEntry/Cloud_CreateOrderedDataStoreEntry) - [GetOrderedDataStoreEntry](/docs/en-us/cloud/features/storage.md#/OrderedDataStoreEntry/Cloud_GetOrderedDataStoreEntry) - [DeleteOrderedDataStoreEntry](/docs/en-us/cloud/features/storage.md#/OrderedDataStoreEntry/Cloud_DeleteOrderedDataStoreEntry) - [UpdateOrderedDataStoreEntry](/docs/en-us/cloud/features/storage.md#/OrderedDataStoreEntry/Cloud_UpdateOrderedDataStoreEntry) - [IncrementOrderedDataStoreEntry](/docs/en-us/cloud/features/storage.md#/OrderedDataStoreEntry/Cloud_IncrementOrderedDataStoreEntry) #### Groups - [GetGroup](/docs/en-us/cloud/reference/Group.md#Cloud_GetGroup) - [ListGroupJoinRequests](/docs/en-us/cloud/reference/GroupJoinRequest.md#Cloud_ListGroupJoinRequests) - [AcceptGroupJoinRequest](/docs/en-us/cloud/reference/GroupJoinRequest.md#Cloud_AcceptGroupJoinRequest) - [DeclineGroupJoinRequest](/docs/en-us/cloud/reference/GroupJoinRequest.md#Cloud_DeclineGroupJoinRequest) - [ListGroupMemberships](/docs/en-us/cloud/reference/GroupMembership.md#Cloud_ListGroupMemberships) - [UpdateGroupMembership](/docs/en-us/cloud/reference/GroupMembership.md#Cloud_UpdateGroupMembership) - [ListGroupRoles](/docs/en-us/cloud/reference/GroupRole.md#Cloud_ListGroupRoles) - [GetGroupRole](/docs/en-us/cloud/reference/GroupRole.md#Cloud_GetGroupRole) - [GetGroupShout](/docs/en-us/cloud/reference/GroupShout.md#Cloud_GetGroupShout) #### Inventories - [ListInventoryItems](/docs/en-us/cloud/reference/InventoryItem.md#Cloud_ListInventoryItems) #### Luau execution - [CreateLuauExecutionSessionTaskBinaryInput](/docs/en-us/cloud/features/luau-execution.md#/LuauExecutionSessionTaskBinaryInput/Cloud_CreateLuauExecutionSessionTaskBinaryInput) - [CreateLuauExecutionSessionTask](/docs/en-us/cloud/features/luau-execution.md#/LuauExecutionSessionTask/Cloud_CreateLuauExecutionSessionTask__Using_Universes) - [CreateLuauExecutionSessionTask (with version)](/docs/en-us/cloud/features/luau-execution.md#/LuauExecutionSessionTask/Cloud_CreateLuauExecutionSessionTask__Using_Universes_Places) - [GetLuauExecutionSessionTask](/docs/en-us/cloud/features/luau-execution.md#/LuauExecutionSessionTask/Cloud_GetLuauExecutionSessionTask) - [ListLuauExecutionSessionTaskLogs](/docs/en-us/cloud/features/luau-execution.md#/LuauExecutionSessionTaskLog/Cloud_ListLuauExecutionSessionTaskLogs) #### Notifications - [CreateUserNotification](/docs/en-us/cloud/features/notifications.md#/UserNotification/Cloud_CreateUserNotification) #### Places - [GetPlace](/docs/en-us/cloud/features/places.md#/Place/Cloud_GetPlace) - [UpdatePlace](/docs/en-us/cloud/features/places.md#/Place/Cloud_UpdatePlace) - [GetInstance](/docs/en-us/cloud/features/places.md#/Instance/Cloud_GetInstance) - [UpdateInstance](/docs/en-us/cloud/features/places.md#/Instance/Cloud_UpdateInstance) #### Universes - [UpdateUniverse](/docs/en-us/cloud/features/universes.md#/Universe/Cloud_UpdateUniverse) - [GetUniverse](/docs/en-us/cloud/features/universes.md#/Universe/Cloud_GetUniverse) - [PublishUniverseMessage](/docs/en-us/cloud/features/universes.md#/Universe/Cloud_PublishUniverseMessage) - [RestartUniverseServers](/docs/en-us/cloud/features/universes.md#/Universe/Cloud_RestartUniverseServers) #### Users - [GetUser](/docs/en-us/cloud/features/users.md#/User/Cloud_GetUser) - [GenerateUserThumbnail](/docs/en-us/cloud/features/users.md#/User/Cloud_GenerateUserThumbnail) ## Limitations - Only the `x-api-key` and `content-type` headers are allowed. - The `x-api-key` header must be a `Datatype.Secret`. See [Secrets stores](/docs/en-us/cloud-services/secrets.md). - The `".."` string is not allowed in URL path parameters. - Only the HTTPS protocol is supported. - You cannot use port `1194` or any port below `1024`, except `80` and `443`. If you try to use a blocked port, you receive either a `403 Forbidden` or `ERR_ACCESS_DENIED` error. ## Rate limits For each Roblox game server, there is a limit of 2500 Open Cloud requests per minute. Exceeding this can cause request-sending methods to stall for around 30 seconds. Your `Global.LuaGlobals.pcall()` may also fail with a message of `Number of Open Cloud requests exceeded limit`. - Open Cloud requests **do not** consume the same overall limit of 500 HTTP requests per minute enforced on all other requests. - Each endpoint has its own limit per API key owner (can be a user or a group) that is enforced no matter where the calls come from (`Class.HttpService`, the web, etc.). For detailed information about Open Cloud rate limits, authentication-based rate limiting, and best practices, see [Rate Limits](/docs/en-us/cloud/reference/rate-limits.md). ## Best practices To optimize your `Class.HttpService` usage and avoid exceeding the limits, apply the following best practices: - **Handle errors gracefully**. Web requests can fail for many reasons. Use `Global.LuaGlobals.pcall()` and have a plan for when requests fail. Furthermore, strictly validate and sanitize all received data from external APIs, ensuring correct data where you can. - Use [exponential backoff](https://en.wikipedia.org/wiki/Exponential_backoff) to stay below limits. If a request returns a recoverable error, instead of immediately retrying, wait for two seconds, then four, eights, etc. between attempts. This helps limit congestion and improves the chance of a successful request by giving the endpoint time to "cool off." - Aggregate and send data in bulk. When possible, it's recommended to let your server collect all necessary data in order to send one HTTP request, rather than multiple small requests. For example, if you are sending an HTTP request for every player in your server, check if the API has a bulk/batch endpoint and, if so, collect the information from all players and send it all in one request. In some cases you may have to use `Class.HttpService:RequestAsync()` to include data in the body of the request. - **Use HTTP/2 endpoints**. HTTP/2 provides significant performance benefits through features such as header compression and request/response multiplexing over a single connection. `Class.HttpService` automatically uses HTTP/2 when available. Note that the HTTP/2 specification requires all header names to be sent in lowercase. ## Observability The **Observability Dashboard** provides insights and analytics for monitoring and troubleshooting your `Class.HttpService` usage. The dashboard features two primary charts: **Request Count** which tracks the volume of `Class.HttpService` requests from your game, and **Response Time** which measures the latency for endpoints to respond. The available dimensions for filtering and breakdown are defined as follows: ### Request Type - `GET` - `POST` - `PUT` - `PATCH` - `DELETE` - `Other` (for non-specified request types) ### Status - Success (HTTP 1xx and 2xx status codes) - Redirect (HTTP 3xx status codes) - 400 (Bad Request) - 401 (Unauthorized) - 403 (Forbidden) - 404 (Not Found) - 429 (Too Many Requests) - 500 (Internal Server Error) - 503 (Service Unavailable) - ExternalError (any other non-specified error codes returned from the external service) - InternalError (an issue returned from `Class.HttpService` within Roblox) The **Response Time** chart is not correlated with status data. If you select "Status" as a breakdown or filter, this chart will not display data. ## Additional considerations - Requests should provide a secure form of authentication, such as a pre-shared secret key, so that bad actors cannot pose as one of your Roblox servers. - Be aware of the general capacity and rate-limiting policies of the web servers to which requests are being sent. --- title: "Best practices when designing MemoryStore data structures" url: /docs/en-us/cloud-services/memory-stores/best-practices last_updated: 2026-06-29T19:33:59Z description: "Explains how to best design data structures to reduce the chance of experiencing throttling." --- # Best practices when designing MemoryStore data structures Depending on the data structure type, MemoryStoreService enforces [limits](/docs/en-us/cloud-services/memory-stores.md#data-structure-size-limits) on the memory and number of items in a data structure. All data structures are also constrained by a global per-partition request limit. Each Roblox experience has the [Memory Store Observability Dashboard](/docs/en-us/cloud-services/memory-stores/observability.md), which includes a set of charts that you can use to monitor memory store usage. ## Sorted maps and queues Sorted maps and queues both have limits on the maximum number of items and maximum total memory. Additionally, the items in one of these data structures always reside on a single partition. Every request to one of those data structures is a request to the same partition. When a sorted map or queues reaches its item or memory limit, the best course of action is to remove unnecessary items manually or by adding an expiration policy for items. Alternatively, if only the memory limit is causing throttling, you can try to reduce the size of your items by stripping unnecessary information out of your keys and values. If you need all of your items or are experiencing throttling due to request throughput, the only solution is sharding. ### Sharding Sharding is the process of storing a set of related data across multiple data structures. In other words, it means taking an existing, high-throughput data structure and replacing it with multiple, smaller ones that together contain the same set of data as the original. The key challenge to sharding is finding a way to spread the data across multiple data structures in a way that maintains the same functionality as the original. For hash maps, although the data structure is already partitioned, sharding is done by spreading the requests among several keys. ### Sharding a sorted map To shard a sorted map, consider splitting your data into alphabetic subsections with character ranges. For example, assume that you only have keys with the first letter from A-Z, and you believe four sorted maps is sufficient for your current use case and future growth: - The first map can cover A-G, the second H-N, the third O-T, and the fourth U-Z. - To insert or retrieve an item, use the appropriate map based on the item's starting character. > **Info:** Use a helper function to get the correct sorted map from an item key. This way, you don't have to repeat the same block of code for every function call. ```lua -- Initialize the MemoryStore Service local MemoryStoreService = game:GetService("MemoryStoreService") -- Create your Sorted Map buckets local sm_AtoG = MemoryStoreService:GetSortedMap("AtoG") local sm_HtoM = MemoryStoreService:GetSortedMap("HtoM") local sm_NtoT = MemoryStoreService:GetSortedMap("NtoT") local sm_UtoZ = MemoryStoreService:GetSortedMap("UtoZ") -- Helper function to retrieve the correct bucket from the Item Key local function getSortedMapBucket(itemKey) if (itemKey >= "a" and itemKey < "h") then return sm_AtoG elseif (itemKey < "n") then return sm_HtoM elseif (itemKey < "u") then return sm_NtoT else return sm_UtoZ end end -- Initialize player names with default value of 0 for _, player in game:GetService("Players"):GetPlayers() do local bucket = getSortedMapBucket(player) bucket:SetAsync(player, 0, 600) end -- Retrieve a player's value local player = "myPlayer" local bucket = getSortedMapBucket(player) local playerScore = bucket:GetAsync(player) print(playerScore) ``` ### Sharding a queue Sharding a queue is tricker than sharding a sorted map. Although you want to spread the request throughput across multiple queues, adds, reads, and removes only ever occur at the front or back of the queue. One solution is to use a revolving queue, which means creating multiple queues and rotating between them when you add or read an item: 1. Create several queues and add them to an array. 2. Create two local pointers. One represents the queue you want to read and remove items from. The other represents the queue you want to add items to: - For read operations, calculate the number of items you need from each queue, as well as where to move the read pointer to. - For remove operations, pass the IDs from the read to each queue. - For add operations, add to the queue at the add pointer and increment the pointer. ```lua -- Initialize the MemoryStore Service local MemoryStoreService = game:GetService("MemoryStoreService") -- Create your Queues local q1 = MemoryStoreService:GetQueue("q1") local q2 = MemoryStoreService:GetQueue("q2") local q3 = MemoryStoreService:GetQueue("q3") local q4 = MemoryStoreService:GetQueue("q4") -- Put the Queues in an Array local queueArr = { q1, q2, q3, q4 } -- Create two pointers representing the indices of the read and add queues local readIndex = 1 local addIndex = 1 -- Create a local function that updates the indices appropriately local function rotateIndex(index, n) return (index + n - 1) % 4 + 1 end -- Create a local function that reads n items from the queue local function readFromQueue(count, allOrNothing, waitTimeout) local endIndex = count % 4 local countPerQueue = count // 4 local items = {} local ids = {} -- loop through each queue for i = 1, 4, 1 do -- determine if this queue will read an extra item local diff = i - readIndex if diff < 0 then diff += 4 end local queue = queueArr[i] -- read items from each queue -- +1 items if matches extra read criteria if diff < endIndex then items[i], ids[i] = queue:ReadAsync(countPerQueue + 1, allOrNothing,waitTimeout) else items[i], ids[i] = queue:ReadAsync(countPerQueue, allOrNothing,waitTimeout) end end readIndex = rotateIndex(readIndex, count) return items, ids end -- Create a local function that removes n items from the queue local function removeFromQueue(ids) for i = 1, 4, 1 do local queue = queueArr[readIndex] queue:RemoveAsync(ids[i]) end end -- Create a local function that adds an item to the queue local function addToQueue(itemKey, expiration, priority) local queue = queueArr[readIndex] queue:AddAsync(itemKey, expiration, priority) addIndex = rotateIndex(addIndex, 1) end -- Write some code! for _, player in game:GetService("Players"):GetPlayers() do addToQueue(player, 600, 0) end local players, ids = readFromQueue(20, true, -1) removeFromQueue(ids) ``` ## Hash maps Hash maps do not have individual memory or item count limits and are automatically sharded, but you can still encounter throttling if you use them poorly. For example, consider an experience with a hash map of data, stored as the value of a single key named `metadata`. If this metadata contains a nested object with information such as place ID, player count, and more, every time the metadata is needed, you have no choice but to call `GetAsync("metadata")` and retrieve the entire object. In this case, all requests go to a single key and therefore a single partition. Rather than storing all metadata as a single, nested object, the better approach is to store each field as its own key so that the hash map can take advantage of automatic sharding. If you need separation between metadata and the rest of the hash map, add a naming prefix (e.g. `metadata_user_count` rather than just `user_count`). Additionally, if one or few keys is being accessed frequently, it's important to shard these calls among a lot of keys. For example, if all experience servers must retrieve a value from one hash map key you may run into partition throttling. To prevent this you can spread those calls among several keys with the same value until partition throttling no longer occurs. --- title: "Memory store hash map" url: /docs/en-us/cloud-services/memory-stores/hash-map last_updated: 2026-06-29T19:33:59Z description: "Explains how to implement the hash map data structure for memory stores." --- # Memory store hash map **Hash maps**, similar to sorted maps, let you store in-memory data as key-value pairs. Unlike sorted maps, they maintain no ordering guarantees. This data structure is useful for cases that require simple data caching and rapid access, such as shared inventories, physical auction houses, and more. Hash maps automatically handle partitioning your data and are very useful if you have more than 1,000 keys. For smaller key spaces, we recommend [sorted maps](/docs/en-us/cloud-services/memory-stores/sorted-map.md). ## Limits Hash maps have a key size limit of 128 characters and a value size limit of 32 KB. Otherwise, hash maps use the same [API request](/docs/en-us/cloud-services/memory-stores.md#api-requests-limits) and [memory quota](/docs/en-us/cloud-services/memory-stores.md#memory-size-quota) limits as the other memory store data structures. ## Get a hash map To get a hash map, call `Class.MemoryStoreService:GetHashMap()` with a name for the hash map. The name is global within the experience, so you can access the same hash map on any script using this name. ```lua local MemoryStoreService = game:GetService("MemoryStoreService") local hashMap = MemoryStoreService:GetHashMap("HashMap1") ``` After you get a hash map, call any of the following functions to read or write data in it: | Function | Action | | --- | --- | | `Class.MemoryStoreHashMap:SetAsync()` | [Add](#add-or-overwrite-data) a new key or overwrite the value if the key already exists. | | `Class.MemoryStoreHashMap:GetAsync()` | [Read](#get-data) a particular key. | | `Class.MemoryStoreHashMap:ListItemsAsync()` | [List](#get-data) items in a hash map. | | `Class.MemoryStoreHashMap:UpdateAsync()` | [Update](#update-data) the value of a key after retrieving it from a hash map. | | `Class.MemoryStoreHashMap:RemoveAsync()` | [Remove](#remove-data) a key from the hash map. | For in-depth documentation about each function, see `Class.MemoryStoreHashMap`. > **Warning:** All functions accessing data structures in memory stores are asynchronous network calls that might occasionally fail. You should wrap these calls in `Global.LuaGlobals.pcall()` to catch and handle errors, as shown in the code samples. ## Add or overwrite data To add a new key or overwrite the value of a key in the hash map, call `Class.MemoryStoreHashMap:SetAsync()` with the key **name**, its **value**, and an **expiration time** in seconds. The memory automatically cleans up once the key expires. The maximum expiration time is 3,888,000 seconds (45 days). > **Warning:** Under the EU General Data Protection Regulation (GDPR), if your memory stores have user data subject to the [right to be forgotten](https://gdpr.eu/right-to-be-forgotten/) you must remove the data within 30 days, even if you set your memory store key's expiration up to 45 days. ```lua local MemoryStoreService = game:GetService("MemoryStoreService") local hashMap = MemoryStoreService:GetHashMap("HashMap1") local setSuccess, _ = pcall(function() return hashMap:SetAsync("User_1234", 1000, 30) end) if setSuccess then print("Set succeeded.") end ``` ## Get data You can either get a value associated with a specific key or get multiple key-value pairs in the hash map. ### Get data with one key To get a value associated with one key from the hash map, call `Class.MemoryStoreHashMap:GetAsync()` with the key **name**. ```lua local MemoryStoreService = game:GetService("MemoryStoreService") local hashMap = MemoryStoreService:GetHashMap("HashMap1") local setSuccess, _ = pcall(function() return hashMap:SetAsync("User_1234", 1000, 30) end) if setSuccess then print("Set succeeded.") end local item local getSuccess, getError = pcall(function() item = hashMap:GetAsync("User_1234") end) if getSuccess then print(item) else warn(getError) end ``` ### Get data with multiple key-value pairs To get all the key-value pairs from the hash map as a single operation, call `Class.MemoryStoreHashMap:ListItemsAsync()` with the desired page size. This function lists all existing keys in a paginated manner. For example, the following code sample retrieves up to 32 items from the hash map. ```lua local MemoryStoreService = game:GetService("MemoryStoreService") local hashMap = MemoryStoreService:GetHashMap("HashMap1") -- Get list of items, 32 items at a time local success, pages = pcall(function() return hashMap:ListItemsAsync(32) end) if success then while true do -- Get the current page local entries = pages:GetCurrentPage() -- Iterate through all key-value pairs on page for _, entry in ipairs(entries) do print(entry.key .. " : " .. tostring(entry.value)) end -- Check if last page has been reached if pages.IsFinished then break else print("----------") -- Advance to next page pages:AdvanceToNextPageAsync() end end end ``` ## Update data To retrieve the value of a key from a hash map and update it, call `Class.MemoryStoreHashMap:UpdateAsync()` with the key **name**, a **callback function** to update the key, and an **expiration time** in seconds. For most experiences, multiple servers can update the same key concurrently and change the value. As `Class.MemoryStoreHashMap:UpdateAsync()|UpdateAsync()` always modifies the latest value before updating, you should use it to read the latest value as the input for your callback function. For example, the following code sample updates the resource count of a resource in a shared inventory. `Class.MemoryStoreHashMap:UpdateAsync()|UpdateAsync()` ensures that all player contributions will make their way into this shared inventory, even if these contributions are made simultaneously. In this function, it also enforces a max resource count of 500. ```lua local MemoryStoreService = game:GetService("MemoryStoreService") local hashMap = MemoryStoreService:GetHashMap("ResourceInventory") local function contributeResources(itemResource, addedCount) local success, newResourceCount = pcall(function() return hashMap:UpdateAsync(itemResource, function(resource) resource = resource or {count = 0} resource.count = resource.count + addedCount -- ensure we don't exceed the maximum resource count if resource.count > 500 then resource.count = 500 end return resource end, 1200) end) if success then print(newResourceCount) end end ``` The latency for `Class.MemoryStoreHashMap:UpdateAsync()|UpdateAsync()` is similar to `Class.MemoryStoreHashMap:GetAsync()|GetAsync()` and `Class.MemoryStoreHashMap:SetAsync()|SetAsync()` unless there is contention. When contention occurs, the system automatically retries the operation until one of these three happens: the operation succeeds, the callback function returns `nil`, or the maximum number of retries is reached. If the system reaches the maximum number of retries, it returns a conflict. ## Remove data You can use `Class.MemoryStoreHashMap:RemoveAsync()` for both removing one key from the hash map and deleting all data in a memory store hash map. ### Remove a key To remove a key from the hash map, call `Class.MemoryStoreHashMap:RemoveAsync()` with a key **name**. ```lua local MemoryStoreService = game:GetService("MemoryStoreService") local hashMap = MemoryStoreService:GetHashMap("HashMap1") local setSuccess, _ = pcall(function() return hashMap:SetAsync("User_1234", 1000, 30) end) if setSuccess then print("Set succeeded.") end local removeSuccess, removeError = pcall(function() hashMap:RemoveAsync("User_1234") end) if not removeSuccess then warn(removeError) end ``` ### Delete all data To delete all data in a hash map, list all your items with `Class.MemoryStoreHashMap:ListItemsAsync()`, then remove them with `Class.MemoryStoreHashMap:RemoveAsync()`. ```lua local MemoryStoreService = game:GetService("MemoryStoreService") local hashMap = MemoryStoreService:GetHashMap("HashMap1") -- Get list of items, 32 items at a time local success, pages = pcall(function() return hashMap:ListItemsAsync(32) end) if success then while true do -- Get the current page local entries = pages:GetCurrentPage() local removeSuccess = true local removeError = nil -- Iterate through all key-value pairs on page for _, entry in ipairs(entries) do print(entry.key .. " : " .. tostring(entry.value)) removeSuccess, removeError = pcall(function() hashMap:RemoveAsync(entry.key) end) if not removeSuccess then warn(removeError) end end -- Check if last page has been reached if pages.IsFinished then print("Finished deleting all data.") break else print("----------") -- Advance to next page pages:AdvanceToNextPageAsync() end end end ``` --- title: "Memory stores" url: /docs/en-us/cloud-services/memory-stores last_updated: 2026-06-29T19:33:59Z description: "Explains how to implement memory store data structures to store frequent in-memory data." --- # Memory stores `Class.MemoryStoreService` is a high throughput and low latency data service that provides fast in-memory data storage accessible from all servers in a live session. **Memory Stores** are suitable for frequent and ephemeral data that change rapidly and don't need to be durable, because they are faster to access and vanish when reaching the maximum lifetime. For data that needs to persist across sessions, use [data stores](/docs/en-us/cloud-services/data-stores.md). ## Data structures Instead of directly accessing raw data, memory stores have three primitive data structures shared across servers for quick processing: [sorted map](/docs/en-us/cloud-services/memory-stores/sorted-map.md), [queue](/docs/en-us/cloud-services/memory-stores/queue.md), and [hash map](/docs/en-us/cloud-services/memory-stores/hash-map.md). Each data structure is a good fit for certain use cases: - **Skill-based matchmaking** - Save user information, such as skill level, in a shared **queue** among servers, and use lobby servers to run matchmaking periodically. - **Cross-server trading and auctioning** - Enable universal trading between different servers, where users can bid on items with real-time changing prices, with a **sorted map** of key-value pairs. - **Global leaderboards** - Store and update user rankings on a shared leaderboard inside a **sorted map**. - **Shared inventories** - Save inventory items and statistics in a shared **hash map**, where users can utilize inventory items concurrently with one another. - **Cache for Persistent Data** - Sync and copy your persistent data in a data store to a memory store **hash map** that can act as a cache and improve your experience's performance. ```mermaid graph TD A[Does your data need to be sorted in a specific order?] B[Use a sorted map.] C[Do you need the ability to scan all of your items at once?] D[Use a hash map.] E[Do you expect to have fewer than 1,000 keys?] A -- YES --> B A -- NO --> C C -- YES --> E C -- NO --> D E -- YES --> B E -- NO --> D ``` In general, if you need to access data based on a specific key, use a hash map. If you need that data to be ordered, use a sorted map. If you need to process your data in a specific order, use a queue. ## Limits and quotas To maintain the scalability and system performance, memory stores have data usage quotas for the memory size, API requests, and the data structure size. Memory stores have an eviction policy based on expiration time, also known as time to live (TTL). Items are evicted after they expire, and memory quota is freed up for new entries. When you hit the memory limit, all subsequent write requests fail until items expire or you manually delete them. ### Memory size quota The memory quota limits the total amount of memory that an experience can consume. It's not a fixed value; instead, it changes over time depending on the number of users in the experience according to the formula **64KB + 1.2KB * [number of users]**. The quota applies on the experience level instead of the server level. When users join the experience, the additional memory quota is available immediately. When users leave the experience, the quota doesn't reduce immediately. There's a traceback period of eight days before the quota reevaluates to a lower value. After your experience hits the memory size quota, any API requests that increase the memory size always fail. Requests that decrease or don't change the memory size still succeed. With the [observability](/docs/en-us/cloud-services/memory-stores/observability.md) dashboard, you can view the memory size quota of your experience in real time using the **Memory Usage** chart. ### API request limits A **request unit** quota applies to all `Class.MemoryStoreService` API calls. This quota is **1000 + 120 * [number of concurrent users]** request units per minute. Most API calls only consume one request unit, with a few exceptions: - `Class.MemoryStoreSortedMap:GetRangeAsync()` Consumes units based on the number of returned items. For example, if this method returns 10 items, the call counts as 10 request units. If it returns an empty response, it counts as one request unit. - `Class.MemoryStoreQueue:ReadAsync()` Consumes units based on the number of returned items, just like `MemoryStoreSortedMap:GetRangeAsync()`, but consumes an additional unit every two seconds while reading. Specify the maximum read time with the `waitTimeout` parameter. - `Class.MemoryStoreHashMap:UpdateAsync()` Consumes a minimum of two units. - `Class.MemoryStoreHashMap:ListItemsAsync()` Consumes **[number of partitions scanned] + [items returned]** units. The requests quota is also applied on the experience level instead of the server level. This provides flexibility to allocate the requests among servers as long as the total request rate does not exceed the quota. If you exceed the quota, you receive an error response when the service throttles your requests. With the [observability](/docs/en-us/cloud-services/memory-stores/observability.md) feature available, you can view the request unit quota of your experience in real time. ### Data structure size limits For a single sorted map or queue, the following size and item count limits apply: - Maximum number of items: 1,000,000 - Maximum total size (including keys for sorted map): 100 MB ### Per-partition limits See [per-partition limits](/docs/en-us/cloud-services/per-partition-limits.md). ## Best practices To keep your memory usage pattern optimal and avoid hitting the [limits](#limits-and-quotas), follow these best practices: - **Remove processed items.** Consistently cleaning up read items using `Class.MemoryStoreQueue:RemoveAsync()` method for queues and `Class.MemoryStoreSortedMap:RemoveAsync()` for sorted maps can free up memory and keep the data structure up-to-date. - **Set the expiration time to the smallest time frame possible when adding data.** Though the default expiration time is 45 days for both `Class.MemoryStoreQueue:AddAsync()` and `Class.MemoryStoreSortedMap:SetAsync()`, setting the shortest possible time can automatically clean up old data to prevent them from filling up your memory usage quota. - Don't store a large amount of data with a long expiration, as it risks exceeding your memory quota and potentially causing issues that can break your entire experience. - Always either explicitly delete unneeded items or set a short item expiration. - Generally, you should use explicit deletion for releasing memory and item expiration as a safety mechanism to prevent unused items from occupying memory for an extended period of time. - Only keep necessary values in memory. For example, for an auction house experience, you only need to maintain the highest bid. You can use `Class.MemoryStoreSortedMap:UpdateAsync()` on one key to keep the highest bid rather than keeping all bids in your data structure. - Use [exponential backoff](https://en.wikipedia.org/wiki/Exponential_backoff) to help stay below API request limits. For example, if you receive a `DataUpdateConflict`, you might retry after two seconds, then four, eight, etc. rather than constantly sending requests to `Class.MemoryStoreService` to get the correct response. - Split giant data structures into multiple smaller ones by [sharding](https://en.wikipedia.org/wiki/Shard_(database_architecture)). It's often easier to manage data in smaller structures rather than storing everything in one large data structure. This approach can also help avoid usage and rate limits. For example, if you have a sorted map that uses prefixes for its keys, consider separating each prefix into its own sorted map. For an especially popular experience, you might even separate users into multiple maps based on the last digits of their user IDs. - [Shard](/docs/en-us/cloud-services/memory-stores/best-practices.md) frequently accessed keys in hash maps with multiple copies of the key to distribute load. - Compress stored values. For example, consider using the [LZW](https://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Welch) algorithm to reduce the stored value size. - Enroll in Extended Services. You can increase your Storage and Request Limit quotas by onboarding onto [Extended Services](https://create.roblox.com/docs/cloud-services/extended-services) ## Observability The [Observability Dashboard](/docs/en-us/cloud-services/memory-stores/observability.md) provides insights and analytics for monitoring and troubleshooting your memory store usage. With real-time updating charts on different aspects of your memory usage and API requests, you can track the memory usage pattern of your experience, view the current allocated quotas, monitor the API status, and identify potential issues for performance optimization. The following table lists and describes all status codes of API responses available on the Observability Dashboard's **Request Count by Status** and **Requests by API x Status** charts. For more information on how to resolve these errors, see [Troubleshooting](#troubleshooting). For the specific quota or limit that an error relates to, see [Limits and Quotas](#limits-and-quotas). | Status code | Description | | --- | --- | | Success | Success. | | DataStructureMemoryOverLimit | Exceeds data structure level memory size limit (100MB). | | DataUpdateConflict | Conflict due to concurrent update. | | AccessDenied | Unauthorized to access experience data. This request doesn't consume request units or use quota. | | InternalError | Internal error. | | InvalidRequest | The request doesn't have required information or has malformed information. | | DataStructureItemsOverLimit | Exceeds data structure level item count limit (1M). | | NoItemFound | No item found in `Class.MemoryStoreQueue:ReadAsync()` or `Class.MemoryStoreSortedMap:UpdateAsync()`. `ReadAsync()` polls every 2 seconds and returns this status code until it finds items in the queue. | | DataStructureRequestsOverLimit | Exceeds data structure level request unit limit (100,000 request units per minute). | | PartitionRequestsOverLimit | Exceeds partition request unit limit. | | TotalRequestsOverLimit | Exceeds universe-level request unit limit. | | TotalMemoryOverLimit | Exceeds universe-level memory quota. | | ItemValueSizeTooLarge | Value size exceeds limit (32KB). | The following table lists states codes from client side, which are currently not available on the Observability Dashboard. | Status code | Description | | --- | --- | | InternalError | Internal Error. | | UnpublishedPlace | You must publish this place to use MemoryStoreService. | | InvalidClientAccess | MemoryStoreService must be called from server. | | InvalidExpirationTime | The field 'expiration' time must be between 0 and 3,888,000. | | InvalidRequest | Unable to convert value to json. | | InvalidRequest | Unable to convert sortKey to a valid number or string. | | TransformCallbackFailed | Failed to invoke transformation callback function. | | RequestThrottled | Recent MemoryStores requests hit one or more limits. | | UpdateConflict | Exceeded max number of retries. | ### Troubleshooting The following table lists and describes the recommended solution for each response status code: | Error | Troubleshooting options | | --- | --- | | DataStructureRequestsOverLimit / PartitionRequestsOverLimit | | | TotalRequestsOverLimit | | DataStructureItemsOverLimit | | | DataStructureMemoryOverLimit | | TotalMemoryOverLimit | | DataUpdateConflict | | | Internal Error | | | InvalidRequest | | | ItemValueSizeTooLarge | | ## Test and debug in Studio The data in `Class.MemoryStoreService` is isolated between Studio and production, so changing the data in Studio doesn't affect production behavior. This means that your API calls from Studio don't access production data, allowing you to safely test memory stores and new features before going to production. Studio testing has the same [limits and quotas](#limits-and-quotas) as production. For quotas calculated based on the number of users, the resulting quota can be very small since you are the only user for Studio testing. When testing from Studio, you might also notice slightly higher latency and elevated error rates compared to usage in production due to some additional checks that are performed to verify access and permissions. For information on how to debug a memory store on live experiences or when testing in studio, use [Developer Console](/docs/en-us/studio/developer-console.md). --- title: "Memory store observability" url: /docs/en-us/cloud-services/memory-stores/observability last_updated: 2026-06-29T19:33:59Z description: "Explains how to use the observability dashboard for memory stores." --- # Memory store observability The memory stores observability dashboard provides real-time charts on your memory usage and API requests. It also has a built-in alert system that notifies you by email when an issue arises to help you troubleshoot in sync. For further information about specific errors, you can view your [Error Report](/docs/en-us/production/analytics/error-report.md) to find the error logs. ## Access the dashboard The memory stores observability dashboard is available for any experience using `Class.MemoryStoreService`, but you must either be the experience owner or have [analytics group permissions](/docs/en-us/production/analytics/analytics-dashboard.md#grant-group-permission) to access the dashboard. To access the dashboard: 1. Navigate to the [Creations](https://create.roblox.com/dashboard/creations) page on the **Creator Dashboard**. 2. Expand the account switcher in the upper-left and select your account or the group owning the target experience. 3. Select the experience. 4. In the **Monitoring** dropdown, select **Memory Stores**. ## Available charts The dashboard includes two categories of line graphs: - **Quota Usage** charts for tracking your usage compared to the [dynamically allocated quotas](/docs/en-us/cloud-services/memory-stores.md#limits-and-quotas), which are calculated based on the number of users in your experience. - **Memory Usage** on your memory usage per minute in bytes compared to how much your allocated quota left. - **API Request Unit** on your total request units per minute compared to how much your allocated quota left. This chart can be broken down by each API method. - **API Usage and Performance** charts for monitoring the API usage pattern and performance based on API method and response status. - **Request Count by API** on API request count per minute by API method, such as `Class.MemoryStoreQueue:ReadAsync()` or `Class.MemoryStoreSortedMap:UpdateAsync()`. - **Request Count by Status** on API request count by [response status](#response-status-codes). - **Request by API x Status** on response statuses returned by all or a specific API method. The chart contains data for the past 30 days, and you can select to view a custom time range with the selector at the top of the page. If you select a time range earlier than 30 days, the system returns a **Request Failed** error. ## Response status codes The Observability Dashboard's **Request Count by Status** and **Requests by API x Status** charts include status codes of API responses that you can use to understand and troubleshoot errors. For a table that lists and describes all of these status codes, see [Observability](/docs/en-us/cloud-services/memory-stores.md#observability). In addition, for information on how to resolve these errors, or the specific quota or limit that an error relates to, see [Troubleshooting](/docs/en-us/cloud-services/memory-stores.md#troubleshooting) and [Limits and Quotas](/docs/en-us/cloud-services/memory-stores.md#limits-and-quotas), respectively. ## Notification alerts The email alert system automatically detects and sends you alerts of memory store usage issues that can harm the performance of your experience. There are two types of alerts: - **Warnings** are alerts with lower priority about issues that can potentially impact your experience. You should resolve them before they become critical issues. The dashboard flags these alerts by highlighting the chart in yellow with a warning message. - **Criticals** are alerts with higher priority on issues directly impacting your experience. You should resolve these issues as soon as possible to prevent degraded performance. The dashboard flags these alerts by highlighting the chart in red with an error message. When your experience triggers two types of alerts at the same time, the dashboard displays the alert header message and highlights the chart region for your attention as **Critical**. Currently, the system has four built-in alerts for memory usage and API requests. If your experience exceeds an alerting threshold, the system notifies you at most once a day for each alert. When receiving an email alert, you should view the dashboard and take actions to prevent degraded performance. ### Memory usage alerts The system sends the following memory usage alerts: - A **Warning** alert when your experience's memory usage has exceeded 70% of the total quota within the past hour. - A **Critical** alert when your experience exceeded the memory size quota for the past hour. These issues usually occur when you overpopulate a memory store data structure with too much data, which can consume the quota quickly even if you only send requests on a few keys. For example, if you set a long expiration time on keys or don't clean up processed data, extra data can fill up your memory quota. To prevent and resolve these issues, apply [best practices](/docs/en-us/cloud-services/memory-stores.md#best-practices) to keep track of data structures you're actively using and set up a mechanism for cleaning up processed data. ### API request alerts The system sends the following API request alerts: - A **Critical** alert when the number of your memory store request failures has exceeded 20% within the past hour. - A **Critical** alert when more than 10% of your memory store requests are being throttled for the past hour. - This alert is based on the number of **DataStructureRequestsOverLimit** + **TotalRequestsOverLimit**, which are error responses indicating that your experience is throttled because it sends too many requests. - This alert isn't related to the request size, only quantity. All API requests have corresponding [response status codes](#response-status-codes), so you can view the ratio of **Success** responses to all error responses using the **Request Count by Status** chart for [troubleshooting](/docs/en-us/cloud-services/memory-stores.md#troubleshooting). --- title: "Partitions and data distribution" url: /docs/en-us/cloud-services/memory-stores/per-partition-limits last_updated: 2026-06-29T19:33:59Z description: "Explains the concept of a partition and per-partition limits." --- # Partitions and data distribution With the release of the `Class.MemoryStoreHashMap` data structure, Roblox removed all existing limits for individual data structures and replaced them with a single, global "per-partition" throttling limit. The exact limit fluctuates based on internal values and how the automatic partitioning process distributes your data, but generally allows for much higher usage before throttling, particularly for hash maps. This new limit enables flexible usage of memory stores across all data structures. ## Partitions The MemoryStores API stores data on _partitions_, which are just subdivisions of storage. Whenever you write an item to a memory store, that item is stored on exactly one partition. Partitions are fully managed by the MemoryStores API; you do not need to manage them yourself. ## Partition assignment Partition storage is different according to the data structure an item is being stored on. For sorted maps and queues, each data structure is assigned a single partition. For example, consider a carnival game with a sorted map called `PlayerScores` and a queue called `PlayerLine` of players waiting to play the game: Unlike sorted maps and queues, hash maps are allotted multiple partitions, and data is automatically distributed across these partitions. If you were to add a hash map called `Prizes`, the partitions might look like this: Note how the hash map exists on all partitions, and each partition has some subset of items. ## Limits Having a per-partition limit allows for higher throughput to all data structures. It also favors hash maps, because they're distributed across all partitions. For example, consider an example per-partition limit of 50,000 requests per minute (RPM): - In the very best case, a sorted map and a queue are limited to 50,000 RPM, because each one resides on a single partition. - Requests to hash maps are spread across the item keys, which are themselves spread across partitions, so hash maps can have a much higher effective limit before throttling, many times that of the other data structures assuming that requests are spread across many item keys. - Although hash maps can achieve higher overall throughput by spreading requests across partitions, individual item keys are still rate limited to maintain system stability. If most of your traffic targets a single item key, that key may still be throttled. - To scale effectively and avoid these per-key limits, implement key sharding. Spread reads and writes evenly across multiple item keys to reduce bottlenecks and maintain smooth performance. For this reason, if you don't need sorting or "first in, first out" functionality, hash maps are usually the best choice for a memory store data structure. For more information, see the [best practices](/docs/en-us/cloud-services/memory-stores/best-practices.md). --- title: "Memory store queue" url: /docs/en-us/cloud-services/memory-stores/queue last_updated: 2026-06-29T19:33:59Z description: "Explains how to implement the queue data structure for memory stores." --- # Memory store queue A **queue** is a linear data structure with a collection of items that either follows the first-in-first-out (FIFO) principle or prioritizes elements based on predefined criteria. [Memory stores](/docs/en-us/cloud-services/memory-stores.md) support two types of queue, the FIFO [regular queues](/docs/en-us/luau/queues.md#regular-queues) and [priority queues](/docs/en-us/luau/queues.md#priority-queues). Both types share the same set of functions for initializing an empty queue, adding data to the queue, reading data from the queue, and removing data from the queue. Memory store queues are useful for order-based processing and storing user information, such as skill levels, to facilitate matchmaking based on your desired criteria. For example, you can add a lobby place as the start place of your experience, use memory store queues as a centralized user information storage system accessible by multiple servers, manage the placement order of users using the queues, and teleport user who have completed the matchmaking to the main place of your experience. ## Get a queue To get a queue, call `Class.MemoryStoreService:GetQueue()` with a **name**, which is global within the experience for any script to access, and an optional **invisibility timeout** in seconds, which prevents duplicated processing of the same queue item. Invisibility timeout is 30 seconds by default if you leave it empty like the following code sample. ```lua local MemoryStoreService = game:GetService("MemoryStoreService") local queue = MemoryStoreService:GetQueue("Queue1") ``` When a queue is processing an item in it, the invisibility timeout applies to the item, turning it invisible from being processed by other servers, as multiple servers can update the queue concurrently. Though it's expected to complete both read and removal operations for an item during the invisibility timeout duration, if an error occurs that causes the item to remain in the queue after the timeout elapses, the items become visible for processing again. In doing this, the invisibility timeout guarantees that all items in a queue can still be processed even if unexpected issues occur. After you get a queue, call any of the following functions: | Function | Action | | --- | --- | | `Class.MemoryStoreQueue:AddAsync()` | [Add](#add-data) a new item to the queue. | | `Class.MemoryStoreQueue:ReadAsync()` | [Read](#read-and-remove-data) one or more items from the queue as a single operation. | | `Class.MemoryStoreQueue:RemoveAsync()` | [Remove](#read-and-remove-data) one or more items previously read from the queue. | | `Class.MemoryStoreQueue:GetSizeAsync()` | [Get](#get-size) the number of items in the queue. | > **Warning:** All functions accessing data structures in memory stores are asynchronous network calls that might occasionally fail. You should wrap these calls in `Global.LuaGlobals.pcall()` to catch and handle errors, like the code sample in each section shows. ## Add data To add a new item to the queue, call `Class.MemoryStoreQueue:AddAsync()` with the item value, an expiration time in seconds, and an optional priority of the item. If you want to keep your queue in the FIFO sequence, you can leave the priority empty or pass `0`. ```lua local MemoryStoreService = game:GetService("MemoryStoreService") local queue = MemoryStoreService:GetQueue("Queue1") local addSuccess, addError = pcall(function() queue:AddAsync("User_1234", 30, 1) end) if not addSuccess then warn(addError) end ``` ## Read and remove data To read one or more items from the queue at once, call `Class.MemoryStoreQueue:ReadAsync()`, which returns an `id` representing the read item. When you finish processing items, immediately call `Class.MemoryStoreQueue:RemoveAsync()` to delete them from the queue with its `id`. This ensures that you never process an item more than once. To capture and respond to all items that are continuously being added to a queue, include a [loop](/docs/en-us/luau/control-structures.md) like the following code sample: ```lua local MemoryStoreService = game:GetService("MemoryStoreService") local queue = MemoryStoreService:GetQueue("Queue1") local addSuccess, addError = pcall(function() queue:AddAsync("User_1234", 30, 1) end) if not addSuccess then warn(addError) end -- Queue processing loop while true do local readSuccess, items, id = pcall(function() return queue:ReadAsync(1, false, 30) end) if not readSuccess then task.wait(1) elseif #items > 0 then print(items, id) local removeSuccess, removeError = pcall(function() queue:RemoveAsync(id) end) if not removeSuccess then warn(removeError) end end end ``` ## Get size To get the number of items in the queue, call `Class.MemoryStoreQueue:GetSizeAsync()`. This method takes an optional boolean `excludeInvisible`. When it is `true`, invisible items are excluded from the returned count. This boolean defaults to `false`. ```lua local MemoryStoreService = game:GetService("MemoryStoreService") local queue = MemoryStoreService:GetQueue("Queue1") local addSuccess, addError = pcall(function() return queue:AddAsync("User_1234", 30, 1) end) if not addSuccess then warn(addError) end local size local success, sizError = pcall(function() size = queue:GetSizeAsync(true) end) if success then print(size) else warn(sizeError) end ``` --- title: "Memory store sorted map" url: /docs/en-us/cloud-services/memory-stores/sorted-map last_updated: 2026-06-29T19:33:59Z description: "Explains how to implement the sorted map data structure for memory stores." --- # Memory store sorted map The **sorted map** data structure of [memory stores](/docs/en-us/cloud-services/memory-stores.md) allows you to store frequent in-memory data as key-value pairs with an optional sort key and maintain a specific order based on the sort keys and keys. Unlike queues, the order of keys entering a map doesn't determine the order of processing, making sorted maps useful for sorting based data organization for implementing in-experience entities for engagement such as leaderboards and cross-server auctioning. ## Limits In addition to the [data structure size limits](/docs/en-us/cloud-services/memory-stores.md#data-structure-size-limits), sorted maps have a key size limit of 128 characters, a value size limit of 32 KB, and a sort key size limit of 128 characters. If you need to store data that surpasses this limit for your experience, you can adopt the sharding technique to split and distribute them through **key prefix** into multiple data structures. Sharding memory stores can also help improve the scalability of your system. ## Get a sorted map To get a sorted map, call `Class.MemoryStoreService:GetSortedMap()` with a **name** you want to define for the map. The name is global within the experience, so you can access the same sorted map on any script using the name. ```lua local MemoryStoreService = game:GetService("MemoryStoreService") local sortedMap = MemoryStoreService:GetSortedMap("SortedMap1") ``` After you get a sorted map, call any of the following functions: | Function | Action | | --- | --- | | `Class.MemoryStoreSortedMap:SetAsync()` | [Add](#add-or-overwrite-data) a new key or overwrite the value and/or sort key if the key already exists. | | `Class.MemoryStoreSortedMap:GetAsync()` | [Read](#get-data) a particular key. | | `Class.MemoryStoreSortedMap:GetRangeAsync()` | [Read](#get-data) all existing keys or a specific range of them. | | `Class.MemoryStoreSortedMap:UpdateAsync()` | [Update](#update-data) the value of a key and/or sort key after retrieving it from a sorted map. | | `Class.MemoryStoreSortedMap:RemoveAsync()` | [Remove](#remove-data) a key from the sorted map. | | `Class.MemoryStoreSortedMap:GetSizeAsync()` | [Get](#get-size) the number of items in the sorted map. | > **Warning:** All functions accessing data structures in memory stores are asynchronous network calls that might occasionally fail. You should wrap these calls in `Global.LuaGlobals.pcall()` to catch and handle errors, like the code sample in each section does. ## Add or overwrite data To add a new key or overwrite the value or sort key of a key in the sorted map, call `Class.MemoryStoreSortedMap:SetAsync()` with the key **name**, its **value**, an **expiration time** in seconds and an **optional sort key**. The memory automatically cleans up once the key expires. The maximum expiration time is 3,888,000 seconds (45 days). The sort key, if provided, must be a valid number (integer or floating point) or a string. In the sorting order of your keys, a sort key takes precedence over a key. For example, when sorting in ascending order, numeric sort keys sort first, followed by string sort keys, followed by items with no sort key. All items with numeric sort keys are sorted by sort key, if the sort key for two items is equal, they are sorted by key. Similarly, all items with string sort keys are sorted by sort key, if the sort key for two items is equal, they are sorted by key. All items with no sort key are sorted just by the key. Example of some data sorted in ascending order - ```text {key: "player1", value: someValue1, sortKey: -1} {key: "player2", value: someValue2, sortKey: 0} {key: "player4", value: someValue3, sortKey: 1} {key: "player5", value: someValue4, sortKey: 1} {key: "player3", value: someValue5, sortKey: 3.14} {key: "player6", value: someValue6, sortKey: "someString"} {key: "player0", value: someValue7} {key: "player7", value: someValue8} ``` Note how `player0` sorts after all keys with a sort key. `player6` sorts after all keys with a numeric sort key. `player4` and `player5` have the same sort key, so they are sorted in ascending order by key. > **Warning:** Under the EU General Data Protection Regulation (GDPR), if your memory stores have user data subject to [Right to be Forgotten](https://gdpr.eu/right-to-be-forgotten/) you **must** remove the data in 30 days, even if you set your memory store key's expiration up to 45 days. ```lua local MemoryStoreService = game:GetService("MemoryStoreService") local sortedMap = MemoryStoreService:GetSortedMap("SortedMap1") local setSuccess, _ = pcall(function() return sortedMap:SetAsync("User_1234", 1000, 30, 3.14152) end) if setSuccess then print("Set succeeded.") end ``` ## Get data You can either get a data value and sort key associated with a specific key or get multiple values and sort keys for keys within a range. ### Get data with one key To get a value and sort key associated with one key from the sorted map, call `Class.MemoryStoreSortedMap:GetAsync()` with the key **name**. ```lua local MemoryStoreService = game:GetService("MemoryStoreService") local sortedMap = MemoryStoreService:GetSortedMap("SortedMap1") local setSuccess, _ = pcall(function() return sortedMap:SetAsync("User_1234", 1000, 30, 3.14152) end) if setSuccess then print("Set succeeded.") end local item local getSuccess, getError = pcall(function() item = sortedMap:GetAsync("User_1234") end) if getSuccess then print(item) else warn(getError) end ``` ### Get data with multiple keys To get data for multiple keys from the sorted map as a single operation, call `Class.MemoryStoreSortedMap:GetRangeAsync()`. This function lists all existing keys by default, but you can set the upper and lower bounds for the key range. For example, the following code sample retrieves up to 20 items starting from the beginning of the sorted map, with keys greater than or equal to `10`, sort keys greater than or equal to `100` and keys less than or equal to `50`, sort keys less than or equal to `500`. ```lua local MemoryStoreService = game:GetService("MemoryStoreService") local sortedMap = MemoryStoreService:GetSortedMap("SortedMap1") local lowerBound = {} lowerBound["key"] = "10" lowerBound["sortKey"] = 100 local upperBound = {} upperBound["key"] = "50" upperBound["sortKey"] = 500 -- Get up to 20 items starting from the beginning local getSuccess, items = pcall(function() return sortedMap:GetRangeAsync( Enum.SortDirection.Ascending, 20, lowerBound, upperBound) end) if getSuccess then for _, item in items do print(item.key) print(item.sortKey) end end ``` ## Update data To retrieve the value and sort key of a key from a sorted map and update it, call `Class.MemoryStoreSortedMap:UpdateAsync()` with the key **name**, a **callback function** to update the value and sort key for this key, and an **expiration time** in seconds. The maximum expiration time is 3,888,000 seconds (45 days). For most experiences, multiple servers can update the same key concurrently and change the value. As `Class.MemoryStoreSortedMap:UpdateAsync()|UpdateAsync()` always modifies the latest value before updating, you should use it to read the latest value as the input for your callback function. For example, the following code sample updates the score in a leaderboard for a player. The score is calculated as kills / deaths. `Class.MemoryStoreSortedMap:UpdateAsync()|UpdateAsync()` ensures that the kills and deaths are updated for the most recent values even if multiple experience servers update the same item simultaneously. A player's kills and deaths are monotonically increasing values and can hence only increase in value in a session. ```lua local MemoryStoreService = game:GetService("MemoryStoreService") local sortedMap = MemoryStoreService:GetSortedMap("Leaderboard") local function updateLeaderboard(itemKey, killsToAdd, deathsToAdd) local success, newStats, newScore = pcall(function() return sortedMap:UpdateAsync(itemKey, function(playerStats, playerScore) playerStats = playerStats or { kills = 0, deaths = 0 } playerStats.kills += killsToAdd playerStats.deaths += deathsToAdd if playerStats then -- \`playerScore\` is the sortKey being used to sort items in the map playerScore = playerStats.kills / math.max(playerStats.deaths, 1) return playerStats, playerScore end return nil end, 30) end) if success then print(newStats) print(newScore) end end ``` The latency for `Class.MemoryStoreSortedMap:UpdateAsync()|UpdateAsync()` is similar to `Class.MemoryStoreSortedMap:GetAsync()|GetAsync()` and `Class.MemoryStoreSortedMap:SetAsync()|SetAsync()` unless there is contention. When contention occurs, the system automatically retries the operation until one of these three happens: the operation succeeds, the callback function returns `nil`, or the maximum number of retries is reached. If the system reaches the maximum number of retries, it returns a conflict. ## Remove data You can use `Class.MemoryStoreSortedMap:RemoveAsync()` for both removing one key from the sorted map and deleting all data in a memory store sorted map. ### Remove a key To remove a key from the sorted map, call `Class.MemoryStoreSortedMap:RemoveAsync()` with a key **name**. ```lua local MemoryStoreService = game:GetService("MemoryStoreService") local sortedMap = MemoryStoreService:GetSortedMap("SortedMap1") local setSuccess, _ = pcall(function() return sortedMap:SetAsync("User_1234", 1000, 30, "someStringSortKey") end) if setSuccess then print("Set succeeded.") end local removeSuccess, removeError = pcall(function() sortedMap:RemoveAsync("User_1234") end) if not removeSuccess then warn(removeError) end ``` ### Delete all data To delete memory in sorted maps, list all your keys with `Class.MemoryStoreSortedMap:GetRangeAsync()`, then remove them with `Class.MemoryStoreSortedMap:RemoveAsync()`. ```lua local MemoryStoreService = game:GetService("MemoryStoreService") local sortedMap = MemoryStoreService:GetSortedMap("SortedMap1") -- Initial lower bound of nil starts flush from first item local exclusiveLowerBound = nil while true do -- Get up to a hundred items starting from current lower bound local getRangeSuccess, items = pcall(function() return sortedMap:GetRangeAsync(Enum.SortDirection.Ascending, 100, exclusiveLowerBound) end) if getRangeSuccess then local removeSuccess = true local removeError = nil for _, item in items do removeSuccess, removeError = pcall(function() sortedMap:RemoveAsync(item.key) end) end -- If there was an error removing items, try again with the same exclusive lower bound if not removeSuccess then warn(removeError) -- If range is less than a hundred items, end of map is reached elseif #items < 100 then break else -- The last retrieved key is the exclusive lower bound for the next iteration exclusiveLowerBound = {} exclusiveLowerBound["key"] = items[#items].key exclusiveLowerBound["sortKey"] = items[#items].sortKey end end end ``` ## Get size To get the number of items in the sorted map, call `Class.MemoryStoreSortedMap:GetSizeAsync()`. ```lua local MemoryStoreService = game:GetService("MemoryStoreService") local sortedMap = MemoryStoreService:GetSortedMap("SortedMap1") local setSuccess, _ = pcall(function() return sortedMap:SetAsync("User_1234", 1000, 30, 3.14152) end) if setSuccess then print("Set succeeded.") end local size local success, sizError = pcall(function() size = sortedMap:GetSizeAsync() end) if success then print(size) else warn(sizeError) end ``` --- title: "Secrets stores" url: /docs/en-us/cloud-services/secrets last_updated: 2026-06-29T19:33:59Z description: "Securely manage API keys, passwords, and assess tokens for third-party services with your game's secrets store." --- # Secrets stores Roblox offers a **secrets** store for each game. Secrets are sensitive information like API keys, passwords, and access tokens that you use to authenticate with external services. For example, if you want to connect to a third-party analytics or music service, you likely need to use an API key to authenticate with it. You could copy and paste the API key into a script or add it to a data store, but those approaches carry unnecessary security risks. The better solution is to use the secrets store and access the key using a small set of secure methods. ## Add secrets To view, create, or edit secrets, you must be the game owner or group owner. You can have up to 500 secrets per game. 1. Navigate to the [Creator Dashboard](https://create.roblox.com/dashboard/creations). 2. Select your game, and then select **Secrets** ⟩ **Create Secret**. 3. Enter a name, the secret, and the applicable domain. - The name acts as a unique identifier for the secret, so we recommend something descriptive. - Secrets can be up to 1,024 characters in length. API keys and access tokens should come from the service provider, but if the secret is a password, you probably created it yourself. - You can use a limited wildcard syntax for the domain, such as `*` for any domain (not recommended) or `*.example.com` for any subdomain at `example.com`. Specific domains are even better, such as `my.example.com`. > **Success:** You can also use Open Cloud to manage your secrets. See the [reference documentation](/docs/en-us/cloud/reference/features/universes.md#get_cloud_v2_universes__universeId__secrets) and [guide](/docs/en-us/cloud/guides/secrets-store.md). ## Local secrets For security reasons, the secrets store for each game is only available to live servers or [collaborative testing](/docs/en-us/studio/testing-modes.md#collaborative-testing) environments. If you try to access a secret during [local playtesting](/docs/en-us/studio/testing-modes.md#playtesting), you receive a `Can't find secret with given key` error. You receive an identical error if you try to access secrets from a client script. To specify secrets for local testing, go to Studio and then go to **File** ⟩ **Experience Settings** ⟩ **Security**. You can create new secrets and edit or delete existing secrets in the **Secrets** section. ![View, create, edit secrets with the Secrets UI.](../assets/data/secrets-store/LocalSecrets.png)![Add a new secret with name secretName1, value 123456, and restricted to domain *.example.com.](../assets/data/secrets-store/LocalSecrets_Edit.png) ## Use secrets Before using secrets within your game, you must enable **Allow HTTP Requests** in the **Security** section of Studio's **File** ⟩ **Experience Settings** window. Then call `Class.HttpService:GetSecret()` within a server script: ```lua local HttpService = game:GetService("HttpService") local testSecret = HttpService:GetSecret("test_secret") ``` Part of the appeal of using secret stores is that you can't accidentally print a secret. Instead of the secret itself, the following code outputs the name you provided when creating the secret: ```lua print(testSecret) --> Secret(test_secret) ``` You can't manipulate the string directly. Instead, the `Datatype.Secret` data type lets you add a prefix and suffix to the secret to help form a URL or insert content like `Bearer`: ```lua local HttpService = game:GetService("HttpService") local testSecret = HttpService:GetSecret("test_secret") local prefix = "https://my.example.com/endpoint?apiKey=" local suffix = "&user=username" local url = testSecret:AddPrefix(prefix) url = url:AddSuffix(suffix) print(url) --> https://my.example.com/endpoint?apiKey=Secret(test_secret)&user=username ``` After you have a URL with the secret inserted, you can make standard HTTP requests using methods like `Class.HttpService:RequestAsync()`. Of course, you can ignore these methods and insert the secret directly into a header, too. > **Info:** You can't include secrets in the HTTP request body, only the URL and headers. --- title: "Creation overview" url: /docs/en-us/creation last_updated: 2026-06-29T19:33:59Z description: "Learn everything you need to know about creating on Roblox." --- # Creation overview **Learn everything you need to know about creating on Roblox.** If you're new to developing Roblox experiences, read these guides for a tour of Roblox creation before diving into the product documentation and tutorials. ## The Roblox platform Roblox is free-to-use for both users and creators and spans a broad demographic with different user behaviors than other social platforms. Learn what makes Roblox unique and what to think about before creating. #### Overview Learn about our unique platform and values. #### The Roblox user base Learn what characterizes the Roblox audience, and what social and cultural factors you should consider when developing on the platform. #### Designing for Roblox Discover some common practices from successful experiences and find out how to tailor your designs to the unique characteristics of Roblox. ## Experiences Experiences are the 3D worlds that you create and publish in Roblox. Learn how they technically work, so you understand how to build the best experiences possible for your users. #### Roblox projects Understand how to build projects in Roblox Studio and publish them as experiences on Roblox. #### Work with assets Understand how to upload assets to the Roblox cloud and how to use them in your projects. #### The data model Understand the data model, the underlying definition of how places in your 3D experiences look and function. #### Client-server runtime Understand how your experiences run in the Roblox Cloud. #### 3D workspace The 3D workspace is where you define all objects that are rendered by Roblox. #### Scripting Learn how to script logic for your Roblox experiences. #### Characters Understand how character models work for NPCs and Players in Roblox. #### Custom models and meshes Understand how to use Blender and other tools to create custom 3D objects. ## Roblox Studio Studio is an all-in-one IDE that lets you create on Roblox. You build projects that you can later publish as experiences to Roblox and also use Studio to create characters and accessories. #### Studio overview Learn about Studio, the all-in-one IDE for Roblox. #### UI overview Learn about the Studio UI and all the features and tools that are available. ## Discovery and monetization With tens of millions daily active users, there's many ways to monetize and grow your creations. #### Promoting on Roblox Discover how experiences are commonly promoted on Roblox and how you can spend your budget most appropriately. #### Earning on Roblox Learn about monetization options and strategies on the platform along with associated considerations for developers. ## Avatars Learn how to build character models and accessories in 3rd party tools like Blender and Maya and sell them on the Marketplace. See [Avatars](/docs/en-us/avatar.md) for creation guides, technical specifications, and additional resources. --- title: "Creator Hub" url: /docs/en-us/creator-hub last_updated: 2026-06-29T19:33:59Z description: "The place to manage your creations on the web" --- # Creator Hub You'll often use Creator Hub and Studio together when you're creating, like managing your creations, tracking analytics, requesting monetization payouts, and a host of other creator actions, all on the web. [Go to dashboard](https://create.roblox.com/dashboard/creations) ## Manage experiences, avatar items, and assets The Creator Dashboard lets you manage and access all of your creations in one place. [Creations dashboard](https://create.roblox.com/dashboard/creations) ![Manage experiences, avatar items, and assets](/assets/landing/hub-home.jpg) ## Analyze and scale your creations Access a variety of analytics features to help you chart your creations' growth, track user behavior and retention, and find opportunities for optimization. You can use analytics to understand what actions you can take to grow your experience. [Guides](/docs/en-us/production/analytics.md) [Analytics dashboard](https://create.roblox.com/dashboard/analytics) ![Analyze and scale your creations](./assets/analytics/overview/step3.png) ## Monetize your creations and track your finances You can request a developer exchange payment, track Creator Store transactions, create ad campaigns, sponsor ads, and more. [Developer exchange](https://create.roblox.com/dashboard/devex) [Creator transactions](https://create.roblox.com/dashboard/devex) [Ads Manager](https://advertise.roblox.com/landing) [Sponsored Ads](https://www.roblox.com/sponsorships/list) ![Monetize your creations and track your finances](./assets/promotion/search-ads/Ad-Set-Search.png) ## Access other properties like the Creator Store, Forum, and the Creator Roadmap The Creator Hub gives you quick access to other creator-related properties such as the Creator Store where you can publish, share, and discover assets. In the Forum, you can interact with the rest of the community. Finally, the Creator Roadmap outlines new features that we plan on shipping. [Creator Store](https://create.roblox.com/store) [Forum](https://devforum.roblox.com) [Roadmap](https://create.roblox.com/roadmap) ![Access other properties like the Creator Store, Forum, and the Creator Roadmap](./assets/landing/store.jpg) --- title: "Brand Developer Directory and Roblox Partner Program" url: /docs/en-us/creator-programs/brand-developer-directory last_updated: 2026-06-29T19:33:59Z description: "The Brand Developer Directory promotes developers with a history of creating engaging experiences on the platform and partnering with brands." --- # Brand Developer Directory and Roblox Partner Program The Brand Developer Directory promotes developers with a history of creating engaging experiences on the platform and partnering with brands. This directory will offer more visibility for you and your team to various brands, rights holder, and music artists from around the world. See the current [Notable Brand Developers](https://create.roblox.com/talent/search/notable-brand-developers). These developers will also be official Brand Creator Partners and will receive a badge, as part of the Roblox Partner Program. ![Partner Program logo.](../assets/creator-programs/partner-program.png) **Status: Open** ## Eligibility - Published at least 1 experience with 4M+ MAU and a 65% like score - Have built a custom experience for a brand, IP, or artist on Roblox. (We encourage you to express interest even if you haven't built a custom experience for a brand for a chance to be featured in the future.) - An updated, detailed, and complete [Talent Hub](https://create.roblox.com/talent/) profile - Meet our [Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards) and have account moderation history in good standing - The developer submitting the survey must be over the age of 18. If you work in a group/studio, please have the owner submit on behalf of the team. A member of our team will reach out to you with the next steps via the contact information you provided. For more information, check out [Roblox for Brands](https://brands.roblox.com/). [Apply](https://survey.roblox.com/jfe/form/SV_802xyrv9VHb7VHM) ## Contact info [PartnerProgram@roblox.com](/docs/en-us/creator-programs/mailto:PartnerProgram@roblox.com.md) --- title: "Creator Affiliate Pilot Program" url: /docs/en-us/creator-programs/creator-affiliate last_updated: 2026-06-29T19:33:59Z description: "The Creator Affiliate Pilot Program rewards creators for bringing new users to Roblox." --- # Creator Affiliate Pilot Program > **Error:** Effective on July 24, 2025, the Creator Affiliate program will be deprecated and replaced by the [Creator Rewards](https://devforum.roblox.com/t/introducing-creator-rewards-earn-more-by-growing-the-community/3777628) program. The [Creator Affiliate Pilot Program](https://create.roblox.com/affiliate) rewards creators for bringing new users to Roblox. If accepted into our program, you can earn up to 50% of spend when new users sign up via your affiliate share links and purchase Robux. Earnings are capped at $100 USD per new user and based on their first six months of purchasing Robux. You receive your revenue in Robux, which you can exchange for real-world money through the [DevEx program](https://create.roblox.com/dashboard/devex). This program is currently in pilot, and we intend to expand to all eligible creators by mid-2025. **Status: Closed** ## Eligibility We are looking for creators, influencers, and studios with one or more of the following: - **A track record of successfully developed experiences and products**, either on or off Roblox. You can demonstrate this through player count, sales estimates, and overall popularity. - **A strong presence on social, video, and gaming platforms.** You can demonstrate this through follower count and post engagement. - **The ability to reach [new audiences](https://corp.roblox.com/newsroom/2024/07/roblox-genre-insights-what-will-you-create-next) and meet their needs in growing genres and markets.** Growing genres include open world action, sports, driving, and social cooperation, and growing markets include Japan, India, Germany, Brazil, and South Korea. - **Marketing expertise.** You can demonstrate this through previous creative and successful marketing campaigns, as well as through the quality of your Creator Affiliate Pilot Program application. ## How it works You can add your affiliate link to: - YouTube videos, video descriptions, and your YouTube about page - Social media posts on LinkedIn, Twitter, Instagram, and TikTok - Content on your website, email newsletters, and other written mediums like blog posts and articles You should use hyperlinks and text links to reference anchor text (for example, "Join Roblox for free"). If you want to share the direct affiliate link without an anchor text, make sure to use a custom URL shortener instead of pasting the full link. You must also provide an FTC disclosure that mentions you might earn a commission from your affiliate link. ## Requirements - Be at least 13 years old (if under 18 years old, you must have parental or guardian consent) - Meet our [Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards) - Be based in countries supporting [DevEx](https://en.help.roblox.com/hc/en-us/articles/203314100-Developer-Exchange-DevEx-Overview-How-to-Submit-Requirements) --- title: "Creator Events" url: /docs/en-us/creator-programs/creator-events last_updated: 2026-06-29T19:33:59Z description: "Explains how to be an event organizer through the Roblox Creator Events program to host virtual events." --- # Creator Events As a Roblox event organizer, you can host virtual events through webinars, live streams, or in-experience through the Roblox Creator Events program. If you're interested in participating, [apply](https://survey.roblox.com/jfe/form/SV_7W1I2AOPuTd8FhA) to become a voluntary event organizer for the program. To preview events hosted by our current organizers, visit the [past events](https://events.roblox.com/home/content) page, which contains a video library of our past events. **Status: Open** Participants in the program receive: - Membership in the [Creator Events program Roblox group](https://www.roblox.com/communities/9420522/Creator-Events#!/about) and [DevForum group](https://devforum.roblox.com/g/Event_Organizers). - Access to 1:1 connections, office hours and monthly program syncs. - Physical Roblox swag as gifts for participation. - Access to a versatile events dashboard, with access to event performance and key metrics. - Potential for events highlight opportunities across the site and Roblox social media platforms. - Access to the program handbook. ## Requirements - You must be 16 years or older. - You are comfortable with public speaking. - You have an updated, detailed, and complete [Talent Hub](https://create.roblox.com/talent/) profile. - You follow our [Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards) and have an account moderation history in good standing. ### Nice to have (not required) - Previous experience in hosting events, either in-person or virtual. - Good organizational and communication skills. - Experience with creating experiences or assets on Roblox. - Reputation with the Roblox community gained from activities you've engaged with on Roblox, such as experiences and groups. - Experience with presentation tools, such as PowerPoint and Google Slides. ## Application process Fill out the application carefully to start the process. After we receive your application, you should hear from us within 1–2 weeks with an update on your application. Large volumes of applications may cause delays in response time. [Apply](https://survey.roblox.com/jfe/form/SV_7W1I2AOPuTd8FhA) ## Contact If you have any questions or concerns about this program, please reach out to any [program staff](https://devforum.roblox.com/g/Events_Staff) on the DevForum. Alternatively, you can email us at [dev-events@roblox.com](/docs/en-us/creator-programs/mailto:dev-events@roblox.com.md). --- title: "Developer Awards" url: /docs/en-us/creator-programs/developer-awards last_updated: 2026-06-29T19:33:59Z description: "Developer Awards is a data-driven program designed to reward developers for reaching significant milestones." --- # Developer Awards Developer Awards is a data-driven program designed to reward developers for reaching significant milestones. The program aims to recognize and celebrate developers' achievements and contributions to the platform. By meeting specific criteria, developers can earn awards that reflect their dedication and success. **Status: Open** ## Award categories - Virtual Items - Metric: Monthly Active Users (MAU) - Developers receive crown(s) for their avatar each milestone their game reaches: - 100+ MAU - 1K+ MAU - 10K+ MAU - 100K+ MAU - 1M+ MAU - 10M+ MAU - 100M+ MAU - 1B+ MAU - Swag Bags - Metric: 40M+ lifetime Robux earned (rolling 12-month) - Developers receive swag bags for themselves and some teammates. Items include: - Backpack - Socks - Hat - Sweatshirt - Trophies - Metric: Lifetime Play Hours - Developers receive a trophy for each milestone reached: - 10M+ Hours - 20M+ Hours - 50M+ Hours - 100M+ Hours - 250M+ Hours - 1B+ Hours - 5B+ Hours - 10B+ Hours ## Eligibility - Must be at least 13 years old - Must be the owner of your experience ## Receiving an award There is no application. Roblox will reach out regarding swag bags and trophies. ## Contact info [devawards@roblox.com](/docs/en-us/creator-programs/mailto:devawards@roblox.com.md) --- title: "Community Feedback Program" url: /docs/en-us/creator-programs/feedback last_updated: 2026-06-29T19:33:59Z description: "The Community Feedback Program is designed to gather a diverse group of active and trusted creators to collaborate and work closely with Roblox staff." --- # Community Feedback Program The Community Feedback Program is designed to gather a diverse group of active and trusted creators to collaborate and work closely with Roblox staff. **Status: Open** As part of this program, you are: - A member of the Community Feedback Group - One of the first to receive sign-ups for beta products, focus groups, 1:1 connections, and office hours - Given access to the Community Feedback Program private Developer Forum category - Gifted physical Roblox swag as a reward for participation ## Eligibility Eligibility can include, but is not limited to: - Has contributed to the development of an experience that has shown some success on the platform - Manages a large group with an active community - Created a community resource that has helped many - Designed avatar assets that have grown into a popular Roblox clothing line ## How to apply Fill out the application below. Roblox staff reviews applications every three months and will reach out if you have been selected. [Apply](https://devforum.roblox.com/t/community-feedback-program-application/1034529) --- title: "Roblox Incubator" url: /docs/en-us/creator-programs/incubator last_updated: 2026-06-29T19:33:59Z description: "Roblox Incubator is a six-month, milestone-driven program that helps experienced teams refine promising concepts into polished, scalable, commercially successful games." --- # Roblox Incubator **Roblox Incubator** is a six-month, milestone-driven program designed to help experienced teams refine promising concepts into polished, scalable, commercially successful games. Each Incubator cohort includes up to 40 teams that work toward milestones that unlock resources and tailored support. **Status: Closed** ## Eligibility Small, experienced teams who have a strong prototype or plan to build one, and are ready to dedicate a significant effort to development during the program. ## How to apply For more information on the selection criteria, FAQs, and the application process, click the following button. [Apply](https://create.roblox.com/build) --- title: "Creator programs" url: /docs/en-us/creator-programs last_updated: 2026-06-29T19:33:59Z description: "Roblox offers various programs for creators on the platform to provide additional support and exposure. This page includes programs that are available publicly for creators. Check them out and see what you qualify for." --- # Creator programs Roblox offers various programs for creators on the platform to provide additional support and exposure. Below, we share programs that are available publicly for creators. Check them out and see what you qualify for. #### Brand Developer Directory and Roblox Partner Program **Status: Open** Promotes developers with a strong background in partnering with brands [Learn More](/docs/en-us/creator-programs/brand-developer-directory.md) #### Community Feedback Program **Status: Open** Gathers a diverse group of active and trusted creators to work closely with Roblox staff [Learn More](/docs/en-us/creator-programs/feedback.md) #### Community Safety Council **Status: Closed** A group of creators committed to raising awareness of safety concerns on the platform [Learn More](/docs/en-us/creator-programs/safety-council.md) #### Creator Events **Status: Open** Host virtual events to teach others in the Roblox creator community [Learn More](/docs/en-us/creator-programs/creator-events.md) #### Creator Rewards **Status: Open** Rewards creators for building engaging experiences on Roblox [Learn More](/docs/en-us/creator-rewards.md) #### Creator Spotlights **Status: Open** DevForum articles that celebrate the awesome things our creators accomplish [Learn More](/docs/en-us/creator-programs/spotlights.md) #### Developer Awards **Status: Open** A data-driven program designed to reward developers for reaching significant milestones in their experiences [Learn More](/docs/en-us/creator-programs/developer-awards.md) #### Inspire **Status: Open** Inspire is an annual global online event where creators come together to exchange ideas and learn from each other [Learn More](/docs/en-us/creator-programs/inspire.md) #### Learn & Explore Sort **Status: Open** Highlights experiences that let users learn educational topics in an engaging way [Learn More](/docs/en-us/creator-programs/learn-explore-sort.md) #### Roblox Brand Link **Status: Open** A curated directory connecting brands with top Roblox creators experienced in brand integrations [Learn More](/docs/en-us/creator-programs/managed-brand-integrations.md) #### Roblox Developers Conference **Status: Closed** RDC is an invite-only, hybrid event that brings together Roblox developers, brands, and creators worldwide [Learn More](/docs/en-us/creator-programs/rdc.md) #### Roblox Incubator **Status: Closed** A six-month, milestone-driven program designed to help experienced teams refine promising concepts into polished, scalable, commercially successful games [Learn More](/docs/en-us/creator-programs/incubator.md) #### Roblox Innovation Awards **Status: Closed** Roblox Innovation Awards is an annual event celebrating the most innovative creators, video stars, and experiences [Learn More](/docs/en-us/creator-programs/innovation-awards.md) #### Roblox Jumpstart **Status: Open** A continuous program designed to help developers who are new to Roblox to learn the platform, and experienced developers who want to explore novel games [Learn More](/docs/en-us/creator-programs/jumpstart.md) #### Roblox Research Panel **Status: Paused** An exclusive program for people to share their opinions to help improve Roblox [Learn More](/docs/en-us/creator-programs/research-panel.md) #### Standout Games **Status: Open** A handpicked sort dedicated to highlighting novel games on Roblox [Learn More](/docs/en-us/creator-programs/standout-games.md) #### Today's Picks on Marketplace **Status: Open** Daily curation of Marketplace items to help users bring their unique selves to Roblox [Learn More](/docs/en-us/creator-programs/todays-picks-marketplace.md) --- title: "Roblox Innovation Awards" url: /docs/en-us/creator-programs/innovation-awards last_updated: 2026-06-29T19:33:59Z description: "Roblox Innovation Awards is an annual event celebrating the most innovative creators, video stars, and experiences." --- # Roblox Innovation Awards Roblox Innovation Awards is an annual event celebrating the most innovative creators, video stars, and experiences, nominated by Roblox staff and the community. **Status: Closed** You can watch the 2025 Roblox Innovation Awards [here](https://www.youtube.com/watch?v=7DXTQcA9G_A). ## Nominations All nominees across every category are selected annually by Roblox staff. Nominations for the Roblox Innovation Awards are currently closed. --- title: "Roblox Inspire" url: /docs/en-us/creator-programs/inspire last_updated: 2026-06-29T19:33:59Z description: "Inspire is Roblox's annual creator program that brings creators together through local events, live workshops, and a global challenge." --- # Roblox Inspire Inspire is a global creator program designed to help creators learn, connect, and build on Roblox. In 2026, the program includes Inspire Cafe meetups, virtual workshops led by experienced creators, and the Inspire Challenge, a 72-hour game jam where teams build a playable experience from scratch. **Status: Open** ## Program format ### Inspire Cafe Inspire Cafe is a casual in-person gathering where creators can connect with their local community. Attendance is limited by location and requires an application. For 2026, announced locations include Los Angeles, Houston, New York, Seoul, and São Paulo. ### Inspire Workshops Inspire Workshops are live virtual sessions focused on foundational game development skills. The 2026 workshop lineup runs from July 22–25, 2026, and covers topics such as Roblox Studio, scripting, 3D building, UI, AI-assisted workflows, multiplayer systems, and game design. Participants who join at least one live workshop challenge can earn an exclusive virtual accessory reward. ### Inspire Challenge The Inspire Challenge is a 72-hour game jam running from July 31, 2026 at 10:00 AM PT to August 3, 2026 at 10:00 AM PT. Teams of 1–5 creators can submit a new playable Roblox experience for judging across categories including Best Overall Experience, Best Technical Quality, Most Creative Game Concept, Most Immersive Experience, Best Video Trailer, and Global Citizenship Award. ## Challenge requirements - Teams must have 1–5 members. - All team members must be registered on DevForum. - Participants must remain in good standing with Roblox moderation. - All team members and submissions must follow the Roblox Terms of Use, Community Standards, and Terms of Participation. - The game must be created and submitted during the official challenge window. - Participants must be 18 years or older to be eligible for the RDC prize. ## Awards - Best Overall Experience: fully paid lodging and round-trip flight tickets to RDC 2026. - Remaining categories: top 3 teams in each category can receive per-member gift card prizes, subject to availability and location. - Workshop reward: join at least one live challenge during any workshop to earn an exclusive virtual accessory. ## How to participate Apply for an Inspire Cafe if one is available in your region, register for the live workshops you want to attend, and submit your game during the Inspire Challenge window. For the latest schedules, speaker lineup, FAQ, and registration details, see the official event page at [Roblox Inspire](https://create.roblox.com/inspire). --- title: "Roblox Jumpstart" url: /docs/en-us/creator-programs/jumpstart last_updated: 2026-06-29T19:33:59Z description: "Roblox Jumpstart is a program that helps developers who are new to Roblox to learn the platform, and experienced developers who want to explore novel games." --- # Roblox Jumpstart **Roblox Jumpstart** is a continuous program designed to help developers who are new to Roblox to learn the platform, and experienced developers who want to explore novel games. **Status: Open** ## Why apply? In this program, developers who are selected for Roblox Jumpstart: - Receive mentorship from Roblox subject matter experts. - Gain access to valuable ad credits and platform promotions to help their games successfully launch and scale. - Acquire curated placement on [Home](https://www.roblox.com/home) to boost exposure through the [Standout Games](/docs/en-us/creator-programs/standout-games.md) sort. In addition, an off-platform marketing fund helps drive new players directly to participants' games. ## Eligibility Small teams who are exploring what's possible on Roblox and working toward releasing their first novel game on the platform. ## How to apply Applications for the Jumpstart program are open on a rolling basis, ensuring that we can support great ideas whenever they strike. For more information on the selection criteria, FAQs, and the application process, click the following button. [Apply](https://create.roblox.com/build) --- title: "Learn & Explore Sort" url: /docs/en-us/creator-programs/learn-explore-sort last_updated: 2026-06-29T19:33:59Z description: "The Learn and Explore sort highlights experiences that let users learn in an engaging way." --- # Learn & Explore Sort The [Learn & Explore](https://www.roblox.com/charts#/sortName/Learn_And_Explore) sort highlights experiences that let users learn in an engaging way. These experiences should bring subjects like history or science to life in ways that immerse students and motivate exploration, play, and deep thinking. Through this sort, you can receive recognition as a credible educational developer and increased discovery opportunities as an educational experience. **Status: Open** ## Experience eligibility - Explores a subject matter, such as science, history, or math, or have educational value that is clear to an educator - Doesn't allow users to encounter aggressive monetization as a part of their first time user experience. Monetization should not detract from learning or gameplay (i.e. users must pay to access large portions of content or gain an unfair advantage in gameplay due to monetization.) - Remains appropriate for a classroom for students of all ages, even if it contains advanced subject matter - Like ratio above 50% and maintains at least 15 concurrent users ## How to apply Submit through the form below. Due to the high volume of applicants, only those considered for the Learn & Explore sort will be contacted. Applications are reviewed by the Education team at Roblox every 1-2 months. [Apply](https://roblox.qualtrics.com/jfe/form/SV_8313iVKrwkQUiGy) --- title: "Roblox Brand Link" url: /docs/en-us/creator-programs/managed-brand-integrations last_updated: 2026-06-29T19:33:59Z description: "The Roblox Brand Link program is for creators interested in brand integrations." --- # Roblox Brand Link The **Roblox Brand Link** program is for creators interested in brand integrations. This program targets brands that invest in Roblox and want to integrate into popular experiences. Roblox manages the entire process for brands, including coordinating with creators to bring these integrations to life. This is an excellent opportunity for creators to work with Roblox and prominent brands on innovative integrations. Creators will have access to paid opportunities to integrate brands into their experiences and the chance to collaborate with high-profile brands. **Status: Open** ## Eligibility This is currently an invite-only program. However, to increase the likelihood of being invited, creators should ideally: - Own an experience with over 500k Monthly Active Users (MAUs) - Communicate in a timely manner - Have good organizational skills - Be willing to closely partner with brands Once selected: - Creators will receive an invite on DevForum to join the program server. - Creators will be expected to work with program staff to create a custom deck that can be shared with brands on integration opportunities. - Creators will be expected to consider opportunities, engage with brands on exploration conversations, and co-produce integration projects. --- title: "Notable Translators Program" url: /docs/en-us/creator-programs/notable-translators last_updated: 2026-06-29T19:33:59Z description: "The Notable Translators Program aims to give more visibility to dedicated and skilled community translators who want to offer their services to other Roblox creators." --- # Notable Translators Program The Notable Translators Program (Pilot) aims to give more visibility to dedicated and skilled community translators who want to offer their services to other Roblox creators. We also want to make it easier for creators to find and hire qualified community translators to help make their experiences appealing to users all over the world. **Status: Deprecated** ## Eligibility - Be at least 18 years old - Have an [Age ID Verified](https://en.help.roblox.com/hc/en-us/articles/4407282410644-Age-ID-Verification) Roblox account - Have a Roblox account that is at least 2 months old - Have native or near-native language proficiency in the language you're testing for ## How to participate Please use one of the links below to take the translation test. Only one submission per candidate. - [English > German Translation Test](https://docs.google.com/forms/d/e/1FAIpQLSfwtTgKdNcwHfpvhGW1OFYz1lMzbdBf0AUbUP6JOS4Sn4WFXQ/viewform) - [English > Japanese Translation Test](https://docs.google.com/forms/d/e/1FAIpQLSdjdN0JcmKfxsPCHJgwCO7MCkbN7WPWczy6g_38VwVBzRTm7Q/viewform) - [English > Thai Translation Test](https://docs.google.com/forms/d/e/1FAIpQLSdihHNOQdTc_Ug91CpeFAjVqFDsvSuTb8_z_FNnWfNxFysAKg/viewform) You can expect to receive your test results via email within a few weeks after the submission. We're planning on expanding this program to further language pairs and adding more translators to the list over the next few months. See the current list of [Notable Translators](https://devforum.roblox.com/t/notable-translators-program/3136858). ## Disclaimer Roblox does not post jobs, perform work, or employ individuals or teams to perform work for translators posted on the Creator Translator Program, and nothing shall create an employment, agency, or joint venture relationship between Roblox and any translator. Roblox is not a party to any agreement a translator may enter into, does not supervise, direct, or control the performance, compensation or procurement of services in the Community Translator Program, and shall not be responsible or liable for any disputes or damages related thereto, including, without limitation, if a translator is not paid for their work or if a Job Creator pays for work that a translator does not complete. Roblox also makes no representation or warranty as to the quality of work that a translator may undertake in connection with any work or services performed for a Job Creator. Roblox does not recommend or endorse any particular translator or Job Creator, or her/his/its respective offered services or job posts. --- title: "Roblox Developers Conference (RDC)" url: /docs/en-us/creator-programs/rdc last_updated: 2026-06-29T19:33:59Z description: "RDC is an invite-only, hybrid (in-person and virtual) event that brings together Roblox developers, brands, and creators worldwide." --- # Roblox Developers Conference (RDC) RDC is an invite-only event that brings together Roblox developers, brands, and creators worldwide. It features networking opportunities, in-depth breakout sessions, product demonstrations, and office hours—all designed to empower creators and highlight the future of Roblox. **Status: Closed** For some examples of Roblox Developers Conference keynotes, talks, and Q&A sessions, see the [RDC 2025 YouTube playlist](https://youtube.com/playlist?list=PLuEQ5BB-Z1PJ8Du1rJ_JaMpW1W_G2T_lm). ## Invitations Eligible creators annually receive invites to the Roblox Developers Conference. These invites can be for in-person attendance or for virtual viewing. Keep an eye on the developer forum for updates regarding the conference and the "Save the Date" announcement for when registration opens. --- title: "Roblox Research Panel" url: /docs/en-us/creator-programs/research-panel last_updated: 2026-06-29T19:33:59Z description: "The Roblox Research Panel is an exclusive program where people can share their opinions and insights to help improve Roblox." --- # Roblox Research Panel The [Roblox Research Panel](https://survey.roblox.com/app/audience-management#/GRES_em4gkWtqVNNKQ9f/login) is an exclusive program where people can share their opinions and insights to help improve Roblox. By participating in surveys, polls, and discussions, you directly contribute to enhancing the Roblox experience for everyone. Roblox partners with Qualtrics, a market research company, to host the panel. **Status: Paused** ## Why participate? As a valued member of the Roblox community, your feedback is essential. By joining the Roblox Research Panel, you can: - Influence Roblox: Your insights help shape new features and improvements. - Be Heard: Engage in meaningful discussions with fellow members and provide feedback that matters. ## Types of research - Surveys: Share your thoughts through detailed surveys on various topics. - Polls: Give your quick opinions on specific subjects via short polls. - Discussions: Join in-depth discussions and provide valuable feedback on new ideas and features. - Testing: Try new Roblox features. ## How to join Joining the Roblox Research Panel is by invitation only. We invite Roblox users to join the panel in various ways including emails, a survey intercept on the Roblox website or app, or Roblox DevForum posts. These communications will contain all the necessary information and a link to complete your registration. --- title: "Community Safety Council" url: /docs/en-us/creator-programs/safety-council last_updated: 2026-06-29T19:33:59Z description: "The Community Safety Council is a group of creators that increase awareness of community concerns on the platform directly to the teams responsible for addressing them." --- # Community Safety Council The Community Safety Council is designed for a diverse group of engaged and trusted creators who are passionate about safety of Roblox to raise increased awareness of community concerns on the platform directly to the teams responsible for addressing them. As part of this group, you are: - A member of the Community Safety Council for a 6 month period after application is accepted. - Invited to participate in monthly connections with the Safety Group to discuss concerns. - Willing to provide honest and detailed feedback about safety concerns on the platform and respond to follow-up from Roblox staff, as required. - Abide by our Community Standards. As a reminder, our policy on Harmful Off-Platform Speech or Behavior requires all users to treat everyone with respect, both on and off platform. **Status: Closed** ## Eligibility Requirements Eligible creators must meet the following requirements: - Must be 18 years of age or older - Must have been on the platform for at least 2 years - Commit to 1 hour monthly connections - Commit to 6 months of participation - A trusted creator in good standing ## FAQs ### When can I expect to hear back after applying? Due to the volume of applications, we will not be able to respond to every application. If you have not heard back about your application in 1 month, you can assume you have not been accepted into the current cohort. We encourage you to reapply in 6 months. Check our program status page for application status. ### If I'm not accepted into the Community Safety Council, can I re-apply? Yes, applications will be open twice a year. We will update the status of the program when we are accepting applications. Previous applicants are eligible to reapply. ### How many people will be on the Council? Our plan is to start with 20 - but this is a new program, and depending on the volume and quality of applications, we may choose to expand. ### Can I be on the Council more than once? We want to ensure that we include a diverse group of creators and influencers which is why we are accepting applicants for two separate cohorts each year. That said, we remain open to the possibility of extended participation on a limited basis. We are fine-tuning the program and will provide any major updates to eligibility or otherwise on this page. ### Will you announce who is part of the Council? Out of respect for the privacy of Council members, we do not intend to announce who is on the Council. If members of the Council are willing to share their participation, they are able to do so but it is not a requirement for the program. ## How to Apply > **Info:** Applications are currently closed. The Community Safety Council operates in 6 month periods. Check back here again in the future. Fill out the application below. Roblox staff reviews applications and will reach out if you have been selected. [Apply](https://survey.roblox.com/jfe/form/SV_b8Ub8XGMoDnLvFA) --- title: "Creator Spotlights" url: /docs/en-us/creator-programs/spotlights last_updated: 2026-06-29T19:33:59Z description: "Creator Spotlights are in-depth DevForum articles that celebrate the awesome things our creators accomplish." --- # Creator Spotlights Creator Spotlights are in-depth DevForum articles that celebrate the awesome things our creators accomplish. Our goal for this program is to uplift the diverse range of creators who are thriving on our platform. Through our spotlights, you can share your story, provide tips and advice, and share anything exciting coming up to fellow Roblox developers and creators. **Status: Open** ## Eligibility - Be at least 13 years old (if under 18 years old, must have parental/guardian consent) - Have developed on Roblox for at least 1 year and have contributed to engaging content - Meet our [Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards) ## How to participate 1. Fill out the nomination form below (nominate yourself or another creator). Nominations are reviewed weekly. 2. Receive follow up from DevRel team. Due to the high volume of applicants, only those considered for a spotlight will be contacted. If you haven't heard back within a month, we encourage you to reapply in 6-12 months. Be sure to focus your application on your domain expertise and what other creators will learn from you. 3. Join a Zoom call with a DevRel team member to share your background, expertise, and advice. 4. Provide visual assets and examples of work. [Apply](https://survey.roblox.com/jfe/form/SV_71CLpOMprlzQ8JM) ## Past examples - [Behind The Games: Midnight Racing: Tokyo, Dress to Impress, and Michael's Zombies](https://devforum.roblox.com/t/behind-the-games-midnight-racing-tokyo-dress-to-impress-and-michael%E2%80%99s-zombies/3131289) - [Creator Spotlight: BelowNatural's Journey Building Paradoxum Games](https://devforum.roblox.com/t/creator-spotlight-belownatural%E2%80%99s-journey-building-paradoxum-games/3027035) - [Creator Spotlight: how unroot designs ui for marbles, mermaids, and more](https://devforum.roblox.com/t/creator-spotlight-how-unroot-designs-ui-for-marbles-mermaids-and-more/3077233) - [Creator Spotlight: Meet Ahlvie, the Environmental Artist Behind Fantastical Worlds](https://devforum.roblox.com/t/creator-spotlight-meet-ahlvie-the-environmental-artist-behind-fantastical-worlds/2964648) --- title: "Standout Games" url: /docs/en-us/creator-programs/standout-games last_updated: 2026-06-29T19:33:59Z description: "Standout Games is a daily curation of the best novel games on the platform." --- # Standout Games **Standout Games** is a curated sort on [Home](https://www.roblox.com/home) that's dedicated to highlighting novel games on Roblox, handpicked by our curation team. Games that are novel often include unique and in-depth gameplay mechanics, distinctive visual styles, or are in an underrepresented genre on the platform. **Status: Open** ## Why apply? Your standout creations are what makes Roblox special, and we are thrilled to feature more of them on Home, providing you with additional exposure on our platform. ## What qualifies as a standout game? Genre, gameplay, and visual style are common factors in a player's decision to try a new experience. Entrance into Roblox Accelerator and Jumpstart hinge on innovation in these areas. - **Genres**: RPG, strategy, and shooter games are heavily underrepresented despite strong demand from older age groups. We're seeking bold games in these core genres, plus unexpected genre mash-ups and projects that blend traditional mechanics with Roblox's avatars, social features, and cross-platform support. - **Gameplay**: Deep gameplay mechanics, metagame systems, and skillful challenges keep players coming back to play again. We're looking for creators who seamlessly blend depth with Roblox's intuitive nature, massive multiplayer scale, and emergent social dynamics, specifically creators who can craft highly replayable, memorable experiences and moments players can't find anywhere else, even Roblox! Games that seem like a copy or reskin of a popular Roblox game are a hard sell. - **Visual style**: We're looking for games that push aesthetic boundaries and make players think "Wait, that's Roblox?" We're looking for teams innovating with hyper-realistic 3D assets, stylized 2.5D sprites, high-fidelity avatars, or any other technique that brings their vision to life in ways that are entirely new to Roblox. > **Info:** While not necessary for consideration, it's recommended that your game: - Includes optimizations for both console and mobile devices. - Supports R15 and layered clothing. - Updates regularly and includes a roapmap. - Uses [experience events](/docs/en-us/production/promotion/experience-events.md) for future events and updates. ## How to apply We highly encourage you to prepare the following **before** applying for the Standout Games sort: - Record video footage that highlights your core gameplay mechanics and other novel aspects to upload in your nomination survey. - [Localize](/docs/en-us/production/localization.md) your game to reach a global audience and improve accessibility for international players. - Upload a gameplay [trailer](/docs/en-us/production/publishing/thumbnails.md#videos) to give users a clear visual preview of the experience before they join. - Review your existing thumbnails to make sure they comply with our [best practices](/docs/en-us/production/publishing/thumbnails.md#best-practices). If you wish to nominate your novel game for Roblox to promote in the Standout Games sort, please complete the survey below. Our curation team reviews nominations on an ongoing basis. Once your nomination is reviewed, you will receive an email to inform you of whether or not your game has been selected. [Apply](https://survey.roblox.com/jfe/form/SV_b1QDMtBNrR0EUGG) --- title: "Today's Picks on Home" url: /docs/en-us/creator-programs/todays-picks-home last_updated: 2026-06-29T19:33:59Z description: "Today's Picks on Home is a curation of the best experiences on a daily basis." --- # Today's Picks on Home **Today's Picks on Home** is a curation of the best experiences on a daily basis. We highlight exciting updates, new and notable experiences, events, seasonal and/or cultural moments to introduce fresh and vibrant content to Roblox users. **Status: Open** ## Why apply? Your amazing creations are what makes Roblox special, and we are thrilled to feature more of them on Home, providing you with additional exposure on our platform. ## How to apply If you wish to nominate your experience to be featured for Today's Picks, please complete the survey below. Our editorial team reviews nominations on an ongoing basis. Due to the high volume of applicants, only those selected for Today's Picks on Home will be notified. If you haven't heard back, we encourage you to fill out the form each time your experience has a notable update. [Apply](https://survey.roblox.com/jfe/form/SV_7VTnD59gg3nFiTA) --- title: "Today's Picks on Marketplace" url: /docs/en-us/creator-programs/todays-picks-marketplace last_updated: 2026-06-29T19:33:59Z description: "Today's Picks on Marketplace is a curated sort with items selected each day by a team of in-house curators." --- # Today's Picks on Marketplace **Today's Picks on Marketplace** is a curated sort with items selected each day by a team of in-house curators. We strive to connect every user with the avatar items that help them bring their unique self to the platform. **Status: Open** ## Why apply? Using the combined efforts of our team of Roblox curators and our Marketplace recommendation system, Today's Picks on Marketplace will give us more ways to feature the work and creativity you bring to Roblox. ## Eligibility - Be 17+ and ID-verified - Meet our [Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards) and [Marketplace Policy](/docs/en-us/marketplace/marketplace-policy.md) ## How to apply Fill out the survey below to nominate your item. We review new submissions every week. Due to the high volume of applicants, only those selected for Today's Picks on Marketplace will be contacted. If you don't hear back, we encourage you to reapply when you have new items to submit. [Apply](https://survey.roblox.com/jfe/form/SV_8cZb94oj9OvQm9g) --- title: "Creator Rewards" url: /docs/en-us/creator-rewards last_updated: 2026-06-29T19:33:59Z description: "Learn how to earn Robux for creating engaging experiences and growing the platform." --- # Creator Rewards Creator Rewards let creators earn Robux by driving Daily Engagement and Audience Expansion. This document describes how Payout Awards are calculated under the Creator Rewards Program. Roblox may modify this framework from time to time, so visit often to learn about any changes. ## Payout Awards Framework - Terms and Definitions Together with the Roblox [Terms of Use](https://en.help.roblox.com/hc/en-us/articles/115004647846-Roblox-Terms-of-Use), the following Payout Awards Framework terms and definitions apply to the Creator Rewards program: - "User" means any person or entity who uses or accesses the Services (per the [Roblox Dictionary](https://en.help.roblox.com/hc/en-us/articles/4415545981332-Roblox-Dictionary)). - "Experience" means Interactive content Published on the Services by Developers for the engagement and enjoyment of Users (per the [Roblox Dictionary](https://en.help.roblox.com/hc/en-us/articles/4415545981332-Roblox-Dictionary)). - "Creator" means any User who creates, uploads, publishes, generates, or otherwise makes available UGC on the Services. Creators include but are not limited to Developers (per the [Roblox Dictionary](https://en.help.roblox.com/hc/en-us/articles/4415545981332-Roblox-Dictionary)). - "Services" means any service or application offered by Roblox, such as those allowing Users to develop Virtual Content, connect with others, and use Virtual Content created by you and other Users. It includes websites on the roblox.com domain, our Roblox mobile, virtual reality and Xbox, and PlayStation apps, the Roblox Player, the Roblox Studio, and our other websites, products, software, applications, content, data feeds, and other services (per the [Roblox Dictionary](https://en.help.roblox.com/hc/en-us/articles/4415545981332-Roblox-Dictionary)). - "Content" means digital media content. - "Influencer" means an individual or entity with an established online presence and audience who creates and distributes Content relating to Experiences or other UGC on the Services not created or owned by the Influencer. - "New User" means a User who: (a) is completely new to Roblox; and (b) is not a Duplicate of another User; and (c) did not join as a result of a Roblox promotional or marketing campaign. - "Lapsed User" means a User who has not logged on to Roblox for 60 consecutive days. - "Reactivated User" means a Lapsed User who: (a) begins using Roblox again; and (b) did not rejoin as a result of a Roblox promotional or marketing campaign. - "Duplicate" means two or more Users who are controlled by the same person or entity. Determining a Duplicate is at the sole discretion of Roblox. - "Active Spender" means any User who on a particular day: (a) has made Qualifying Purchases totaling at least $9.99 USD anywhere on Roblox within the past 60 days; and (b) was not a New User or Reactivated User during the past 60 days. - "Share Link(s)" means unique and trackable links that generate metrics that allow you to track and grow your off-platform user acquisition. - "Qualifying Purchases" means the purchase of Robux, Roblox Premium, or UGC subscriptions. ## Payout Awards for Creators The following sections describe the different rewards available. > **Info:** Influencers who are members of our [Video Stars](https://influencers.roblox.com/) program can also earn Daily Engagement and Audience Expansion rewards. ### Daily Engagement Rewards Creators earn 5 Robux each day if their experience is one of the first three an Active Spender plays for 10+ minutes. | **Qualifying Action** | **Timeframe** | **Payout** | | --- | --- | --- | | Active Spender engages with Creator's experience for 10+ minutes in a day, provided it's one of the first three experiences they launch that day | Awarded on a daily basis (24 hour period) | 5 Robux | Daily Engagement Rewards pays Creators 5 Robux for each user who spends at least 10 minutes in their experience over the course of the day. To qualify, the experience must be one of the first three experiences that the user visits that day, and the user must spend at least 10 minutes in it. Users must be Active Spenders (meaning they must have spent at least $9.99 in the past 60 days) for their sessions to be eligible. ###### Example 1 An Active Spender visits 5 experiences during the day in the following order: 1. Experience A for 2 minutes 2. Experience B for 5 minutes 3. Experience C for 10 minutes 4. Experience D for 5 minutes 5. Experience A for 10 minutes 6. Experience E for 15 minutes Experience A (where the user spent a total of 12 minutes), Experience C (where the user spent 10 minutes), and Experience E (where the user spent 15 minutes) each earn 5 Robux that day. Experience B and Experience D do not earn 5 Robux because the user didn't spend 10 or more minutes in them that day. ###### Example 2 An Active Spender visits 5 experiences during the day in the following order: 1. Experience A for 2 minutes 2. Experience B for 10 minutes 3. Experience C for 15 minutes 4. Experience D for 5 minutes 5. Experience E for 15 minutes 6. Experience A for 15 minutes Experience A (where the user spent a total of 17 minutes), Experience B (where the user spent 10 minutes), and Experience C (where the user spent 15 minutes) each earn 5 Robux because they were the first three experiences the user visited and spent at least 10 minutes in that day. Experience D does not earn 5 Robux because the user only spent 5 minutes in it. Experience E also does not earn 5 Robux because it was the fourth experience the user visited that day, even though they spent over 10 minutes in it. ### Audience Expansion Rewards The audience expansion rewards system compensates Creators for bringing New Users to Roblox or for turning a Lapsed User into a Reactivated User. When one of these qualifying users joins or returns and is attributed to a Creator, the Creator earns a 35% revenue share on their first $100 of Qualifying Purchases anywhere on the platform during their first 60 days﹡. | **Qualifying Action** | **Timeframe** | **Payout** | | --- | --- | --- | | New User or Reactivated User clicks the Creator's experience's Share Link, which results in the user joining or rejoining Roblox, and plays that experience for 10+ minutes within the same day; AND the experience maintains an average of 100+ DAU for 60 days after the joining or rejoining date. | 60 days | 35% revenue share on their first $100 of Qualifying Purchases anywhere on the platform﹡ | | New User or Reactivated User clicks the Creator's experience's direct link, which leads to entering that experience as the user's first session on the day of joining or rejoining Roblox, and plays the experience for 10+ minutes that day; excluding users who join or rejoined via a Share Link; AND the experience maintains an average of 100+ DAU for 60 days after the joining or rejoining date. | 60 days | 35% revenue share on their first $100 of Qualifying Purchases anywhere on the platform﹡ | | New or Reactivated User searches the Creator's specific experience name, which leads to entering that experience as the user's first session on the day of joining or rejoining Roblox, and plays the experience for 10+ minutes that day; excluding users who join or rejoined via a Share Link; AND the experience maintains an average of 100+ DAU for 60 days after the joining or rejoining date. | 60 days | 35% revenue share on their first $100 of Qualifying Purchases anywhere on the platform﹡ | _﹡Less Qualifying Purchases they have made in the 60 days after each time (if any) they have previously reactivated)_ ## Access payout data Payout Awards, if any, are made in "Earned Robux" with a 60 day holding period. If you want to exchange Payout Awards for fiat currency (i.e. real money) or if you would like to earn Audience Expansion rewards, the [DevEx Terms](https://help.roblox.com/hc/en-us/articles/115005718246-Developer-Exchange-Terms-of-Use) also apply to you. Read the [DevEx Terms](https://help.roblox.com/hc/en-us/articles/115005718246-Developer-Exchange-Terms-of-Use) for eligibility requirements and restrictions, which include, but are not limited to, information regarding taxes and withholding requirements. ### For experiences To access payout data for one of your experiences: 1. On Creator Dashboard, go to your [Creations](https://create.roblox.com/dashboard/creations) page and select the desired experience. 2. Click **Monetization** ⟩ **Creator Rewards** in the left menu. The dashboard displays the following metrics: - **Creator rewards earned** - Estimated Robux earnings over a specified period with a breakdown for Total, Daily Engagement (from active spenders), New Audience Expansion, and Reactivated Audience Expansion. - **Daily rewarded active spenders** - Number of daily [rewarded active spenders](#daily-engagement-rewards) who engage with your experience over a specified period. These users contribute to your Daily Engagement rewards. - **Daily rewarded active spender %** - displays the percentage change, along with a 50th-90th percentile benchmark to similar experiences. - **Rewarded signups** - Shows how many [new users](#audience-expansion-rewards) you've brought to Roblox over the specified time period. These users contribute to your New Audience Expansion rewards. - The **Rewarded signups %** graph displays the percentage change, along with a 90th-95th percentile benchmark. - **Rewarded reactivations** - Shows how many [reactivated users](#audience-expansion-rewards) you've brought to Roblox over the specified time period. These users contribute to your Reactivated Audience Expansion rewards. - **Rewarded reactivations %** graph displays the percentage change, along with a 90th-95th percentile benchmark. ### For share links To access payout data for your Share Links: 1. On Creator Dashboard, go to the [Analytics](https://create.roblox.com/dashboard/analytics) page. 2. Click the [Share Links](https://create.roblox.com/dashboard/analytics?tab=ShareLinks) tab. The dashboard displays the following metrics: - **Clicks** - Total clicks on your Share Link. - **Active spender visits (for Video Stars program members only)**: Number of visits from active spenders (on Roblox) starting their day by clicking on your home page or profile Share Link and playing in experiences for 10+ minutes. Excludes rewarded new or reactivated users in their first two months. - **Signups** - New Roblox users who signed up during the selected time period and are attributed to your experience, excluding possible duplicates. - **Reactivations** - Lapsed Roblox users who reactivated during the selected time period and are attributed to your experience, excluding possible duplicates. **(Audience expansion) spenders** - Rewarded signups and reactivations who made a Qualifying Purchase in their first two months. - **Qualifying spend per spender** - Average Robux value of qualifying purchases per rewarded (audience expansion) spender, in their first two months. - **Estimated payout** - Estimate of Creator Reward earnings in Robux before adjustments. ## Frequently Asked Questions ### Who is eligible to earn Creator Rewards? - All creators begin earning Daily Engagement Rewards starting July 24, 2025 — no additional enrollment is required. - To qualify for Audience Expansion Rewards, creators must have an ID-verified account in good standing and a valid DevEx account. You can check your eligibility in the [Creator Dashboard](https://create.roblox.com/settings/eligibility/creator-rewards). - If the experience is owned by a community, then a community member with the permission to configure group revenue must have an ID-verified account in good standing and a valid DevEx account. - Members of our Video Stars program can earn both Daily Engagement and Audience Expansion rewards. ### What terms apply to Creator Rewards? - A fair and equitable Creator Rewards program is in everyone's best interest – creators, players, and the platform alike. It's vital that rewards go to creators driving genuine user growth and engagement. Participation in Creator Rewards requires adherence to Roblox Terms of Use including the Roblox Community Standards. - To uphold the integrity of the program for all participants, all Creator Rewards are contingent on genuine engagement from users who participate economically on the platform. We actively monitor for and discount inauthentic user activity. Misuse or attempts to exploit the system can result in penalties, including forfeit of illegitimate rewards, removal from the Creator Rewards program, and/or account termination. - Specifically, we do not allow: - **Artificial or Automated Activity**: Using bots, automated tools, browser extensions, plugins, or manipulating teleports to artificially generate visits, referrals, or engagement time. - **Encouraging Alt Accounts for Rewards**: Audience Expansion Rewards are limited to one per user, and we use various inputs like unique device ID, IP, and payment data to detect alts. Encouraging existing users to create or use alternate accounts is strictly prohibited. - **Impersonation or Misrepresentation**: Impersonating Roblox, other individuals, or misrepresenting affiliations to gain rewards (e.g., in referral attempts). - **Fraudulent Transactions**: Rewards may be withheld for accounts that generate a disproportionately high percentage of chargeback, as defined at Roblox's discretion. - **Violation of Policies**: Any violation of the Roblox Terms of Use, Roblox Community Standards, or general Program Policies. - Audience Expansion Rewards are limited to one per user, and Roblox uses a variety of inputs (e.g. unique device identifier) to identify and remove credit for "alt" accounts. ### Will Paid Access experiences be eligible for Creator Rewards? Yes. Paid access experiences are eligible to earn Robux from Creator Rewards. ### Who counts as an Active Spender? Active Spenders are users who have been active on the platform for at least 60 days and have spent at least $9.99 in the last 60 days. ### What is happening with Engagement Based Payouts and Creator Affiliate programs? - Engagement Based Payouts and Creator Affiliate programs have been discontinued and replaced with Creator Rewards, which is the next step in our goal to support creators who bring engaging content to our platform. - With Engagement Based Payouts, we found that creators lacked transparency into which actions drove higher earnings and predictability in how those earnings would evolve over time. We want to put creators in control, with more transparency into the actions they can take to earn higher rewards. With Creator Rewards, creators who publish engaging content can earn, directly from Roblox, for the engagement their content drives on the platform. --- title: "Discovery" url: /docs/en-us/discovery last_updated: 2026-06-29T19:33:59Z description: "Explains how players discover games on the Roblox platform." --- # Discovery Roblox's [mission](https://devforum.roblox.com/t/discovery-on-roblox-past-present-and-future-vision/2859111) for discovery is to connect every player with the best games and communities that meet their interests. This guide outlines how discovery works and how you can help your games get discovered. ## How recommendation works Roblox has millions of games and needs a way to figure out which ones to show to a particular user that are personalized to them. On [Home](https://www.roblox.com/home), the **Recommended for You** sort personalizes content and connects users with games that foster deep engagement, social interaction, and repeat play. The Recommended for You algorithm decides what games to show each user in two stages: ![Diagram of how all games are sorted, ranked, and finally displayed on home page.](./assets/analytics/discovery/Retrieval-And-Ranking-Diagram.png) | **Stage 1: Retrieval** | **Stage 2: Ranking** | | --- | --- | | In the Retrieval stage, the algorithm selects a subset of games that each user might enjoy playing based on key signals like engagement, retention, and monetization. | In the Ranking stage, the algorithm takes the input from the Retrieval stage and selects the most relevant games to be ranked in a personalized way and shown to each user. | | Signals from sponsored ads, curation, search, charts, friends, teleport, notifications, curated games, and other social media sharing can accelerate your consideration for organic discovery from Recommended for You. Games that have even a small number of people playing can signal to the system that the game is worth considering for distribution to more users who might find the game enjoyable through Recommended for You. | How far your game goes and how much organic distribution it gets from Recommended For You depends entirely on the engagement and retention of users who come to your game through Recommended for You. Roblox doesn't count the engagement, monetization, or retention of users first acquired from ads, curation, friends, search, social media, or any other source in the ranking stage of Recommended for You. | There are 4 factors that impact how many home recommendation impressions each game gets: 1. What you do, such as updates to your game or changes to gameplay, influences signals of home recommendations. 2. What Roblox does or how the Discovery algorithm changes. 3. Total users or people visiting the Home page. There are many forms of seasonality, such as: - Weekly seasonality which usually peaks on a Saturday and lowers during weekdays - Summer/back to school seasonality - Holiday seasonality 4. If other games are significantly more successful at engaging players, your game's distribution may decrease, even if your own engagement signals remain steady. ### Key signals Roblox ranks games in the Recommended for You sort using many signals that work together, each carrying a different amount of influence on where your game ranks. Every signal reflects user behavior after they discover your game. As the recommendation system evolves, Roblox will continue to add and remove signals while also adjusting their influence. These changes work toward a consistent goal: a recommendation system that promotes games with strong long term retention. | **Priority** | **Signals** | **Time segments** | | --- | --- | --- | | **Most important** | **Play through rate**
The rate at which users play your game after seeing it in the Recommended for You sort. | N/A | | **First play bounce rate**
The rate at which users leave your game after a short play session. **This is a negative signal**, high bounce rates suggest players aren't finding enough to stay engaged early in their session. | | | **Play days per user**
The average number of unique days users engage with your game. | | | **Playtime per user**
The average amount of time users spend in your game. There is a maximum of 60 minutes per user, per game, per day. | | **Important** | **Intentional co-play days per user**
The average number of unique days that users come back to play your game with friends, including co-play days through join, invites, or private servers. | | **Qualified play sessions per user**
The average number of qualified play sessions per user who clicked and played your game through recommendations on the [Home](https://www.roblox.com/home) page. A "qualified play" refers to a user's meaningful play session with your game, and it filters out accidental clicks or quick bounces. | | **Spend days per user**
The average number of unique days users spend Robux in your game. | | **Robux spent per user**
The average amount of Robux users spend in your game. | _Each signal is based on users who **_organically_** joined from the Recommended for You sort on Home. Here is a 2 min [overview video](https://www.youtube.com/watch?v=K7jsk3bzmvE) of the overall algorithm._ Improving your [retention](/docs/en-us/production/analytics/retention.md), [engagement](/docs/en-us/production/analytics/engagement.md), and [monetization](/docs/en-us/production/analytics/monetization.md) directly enhances Roblox's recommendation signals, resulting in better visibility on the Home page. Roblox's recommendation system uses **explore and expand** phases to understand the key signals. For example, you might see a spike in new users from recommendations (explore) after a content update. If that new user cohort has good engagement and monetization, Roblox is more likely to continue to recommend your game to more such user cohorts (expand). ## Understand your metrics in Creator Analytics When distribution increases, you settle into a new baseline. As a result of recommendations on the Home page surfacing your game to a wider set of users, some of those users might like your game a lot, but not all of them will be super fans from day one. You can expect an increase in impressions and plays in the **Home Recommendation** tab of the Creator Analytics dashboard. As the Recommended for You algorithm explores which users might be best suited for your game, you might see some fluctuations in the signals of users acquired from Recommended for You before you settle into a new baseline. This is a result of engagement from users who discover you in the Recommended for You sort only. The behavior of users you get from ads, recommendations on the Home page, and other sources can vary. It's natural for your play through rate, playtime, and retention to differ by source, and for these to change as you and other games on Roblox grow. For example: - Game A is established, acquiring an average of 10 thousand daily players on 25 thousand impressions from recommendations on the Home page. - By running ads, Game A acquires an additional 5 thousand daily players from Sponsored sort, from 20 thousand impressions. - Game A is now acquiring an average of 15 thousand daily players to their game. This extends the reach of the game, while also increasing earning potential. - As long as the behavior of the 10 thousand daily players from Home Recommendations does not change, the game will continue to get around the same traffic from recommendations on the Home page, excluding external factors like seasonality or newly popular games. ## Use Home Recommendations analytics to grow your game The **Home Recommendations** dashboard helps you monitor these recommendation signals. To access it, go to **Analytics** ⟩ **Acquisition** on the Creator Hub and select the **Home Recommendations** tab. You can use the Home Recommendation dashboard to: **1. Analyze your Home Recommendation impressions and plays trends.** For example, if you notice a dip in Home Recommendation impressions starting on February 5th, you can analyze the corresponding recommendation signal trends. **2. Analyze recommendation signal trends for optimization.** Start with the most important signals and understand how the signals changed. Typically, impression drop follows drop in play through rate, play days per user, and playtime per user, and increase in first play bounce rate. These are the most important signals. Consider different time periods of D8-D28, D2-D7 and D1 for play days per user and playtime per user, and these signals are shown in different charts on the Creator Analytics dashboard. If your most important signals are stable, then focus on the next set of signals to identify potential changes, such as intentional co-play days, qualified play sessions, spend days, and robux spend in the D8-D28, D2-D7 and D1 time periods. In the example above, there is a drop across D2-7, and D8-28 metrics, while D1 continues to be stable. This shows potential issues for impression change. **3. Understand signals compared to other experiences your users also play.** It's also important to identify recommendation signals that are below benchmark as areas of opportunity. Benchmark metrics are based on a set of similar experiences shown at the bottom of your analytics page. If you believe these experiences might not be the right benchmark for your experience, use the information only as a rough guideline and focus on improving your game's Home recommendation signals. Keep in mind that benchmarks and benchmark experiences do not impact the Recommended for You algorithm in any way. They are meant to only provide you with a point of comparison and help you identify signals that are low as areas of opportunity on the Home Recommendations dashboard. ![List of similar games on the Home Recommendations page.](./assets/analytics/discovery/Similar-Experiences.png) **Do I have to optimize for every recommendation signal in the Recommended for You algorithm?** While optimizing for all recommendation signals is beneficial, it's most effective to focus on creating a high-quality, engaging game. Prioritize core gameplay, user retention, and accurate metadata to improve overall user experience. It's important to note that you may notice a temporary drop in play through rate when your impressions increase, particularly during initial discovery phases. This is a normal occurrence as new users discover your game, and your play through rate should stabilize as traffic patterns normalize. **Do these recommendation signals favor larger experiences with more players?** No. These recommendation signals are calculated as averages per user, not total values. This ensures that smaller experiences with highly engaged users are not disadvantaged. Roblox is focused on per user engagement, not total engagement. The Recommended for You algorithm is designed to match users with experiences they are most likely to enjoy. By prioritizing user satisfaction, you'll create experiences that resonate with your audience and foster long-term engagement. **Why did my daily active users increase or decline?** If your daily active users increase or decline substantially, consider investigating your [acquisition analytics](/docs/en-us/production/analytics/acquisition.md) to understand where new and returning users are coming from by source, such as recommendations, search, sponsored ads, or others. If plays from recommendations contributed the most to your user change, look at your Home recommendation signal charts to identify major movements. **How do benchmark games impact discovery algorithms and home recommendations?** Benchmarks and benchmark games do not impact the Recommended for You algorithm in any way. They are meant to provide you with a point of comparison and help you identify signals that are low as areas of opportunity on the Home Recommendations dashboard. ## Best practices for discovery Your content must always adhere to Roblox's [Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards). To increase your reach and help your game get discovered, make sure to also follow the best practices for discovery: - **Be accurate** - Avoid using irrelevant keywords in your metadata and follow the [metadata best practices](/docs/en-us/production/publishing/publish-games-and-places.md#publish-games). - **Build trust** - You should not rely on promotional monetary rewards to drive engagement. Instead, your metadata should reflect what your game is about. - **Use unique metadata** - Focus on using original imagery and naming that you or your teammates created to help your game stand out. Avoid publishing content with repetitive titles and images that have been previously published. When using [thumbnail personalization](/docs/en-us/production/publishing/thumbnails.md#thumbnail-personalization-for-the-home-page), make sure all thumbnails accurately reflect your game. - **Add your own spin to existing trends in the title, images, description, and in-game content** - When you follow trends, unique updates add value and differentiate your game from other games following the same trend. ### Issues that limit exposure The level of exposure your content receives on the homepage and in search is directly influenced by its quality. Certain issues can reduce visibility or prevent your content from being recommended, including: - **Leading with giveaways** - Metadata that implies any type of monetary reward is not prioritized for recommendations. Example: A game titled "Robux! Play now!" receives less exposure because the title leads with monetary implications instead of in-game content. - **Mismatched metadata and content** - Metadata and content that is highly mismatched is not recommended to users and is less visible in search results. Example: A game titled "The Great Dinosaur Quest" that has a thumbnail showing dinosaurs but where the actual gameplay is a generic obstacle course with no dinosaurs or adventure elements. - **Non-unique games** - Games with metadata and place files that closely resemble existing games on Roblox are no longer prioritized for recommendations and might rank lower in search results. Example: A game with the same title and visuals as a previously published game. ### Track and improve content quality Roblox continually reclassifies content quality with every update, giving all games the opportunity to improve their reach. To be reassessed and improve your reach, make sure to align your game with [best practices](#best-practices-for-discovery) for discovery. To check if your game is affected by quality issues, go to the **Creator Dashboard**. Games with reduced exposure display a banner that updates daily and provides the latest status about quality and visibility. If you come across any issues, go to [Support](https://www.roblox.com/support), select **Bug Report**, and provide the Universe ID of your game along with a description of your issue. ## Discovery for other surfaces Home's **Recommended For You** is not the only discovery surface that Roblox offers. Below is a primer on our other surfaces: ## Other Home sorts Home is a user's personalized view of Roblox. Outside of the Recommended for You sort, Home also includes **Continue Playing**, **Friends List**, **Sponsored**, **Curated Sorts**, and more. For a deeper dive on some of these sections: - **Standout Games** has novel games that are hand curated by Roblox that often include unique and in-depth gameplay mechanics, distinctive visual styles, or are in an underrepresented genre on the platform. For more information, see [Standout Games](/docs/en-us/creator-programs/standout-games.md). - **Live Events** has games that are part of a limited time event that you can complete quests for to unlock rewards. You can see past events from Roblox [here](https://www.roblox.com/groups/4111519/Roblox-Presents#!/about). - **Sponsored** lets you invest directly in getting your games discovered by a specific audience segment. For more information, see [Ads Manager](/docs/en-us/production/promotion/ads-manager.md#sponsored-experiences). ### Game details page The Game Details Page aims to offer users comprehensive insights about the game, enhancing their understanding and aiding in decision-making. This, in turn, drives high-intent users to your games. You can leverage the Game Details Page to improve user onboarding and attract returning users by: - **Maintaining up-to-date events**: Events are crucial for community engagement. Use [Experience Events](#experience-events) to inform users about upcoming events and drive traffic to your game. - **Maintaining Roblox Groups**: Roblox Groups offer the best way for creators to connect with and inform their communities. - **Increasing Monetization**: Boost revenue by adding [passes](/docs/en-us/production/monetization/passes.md) and [subscriptions](/docs/en-us/production/monetization/subscriptions.md) for your game. The Game Details Page also provides additional recommendation opportunities by highlighting similar games, helping users discover more relevant content. #### Experience events **Experience events** are key to keeping a community engaged. These moments are where all your users can come together and engage for unique events and scenarios. [Experience events](/docs/en-us/production/promotion/experience-events.md) are a way for you to tell your users about upcoming events within your game, and for them to opt in and be notified when that event starts. Roblox is continuing to build on that foundation by offering deeper event details customization. You can add up to 5 thumbnails to promote your event with users and include a primary event type. ### Search **Search** aims to be a companion (easy to find and use), concierge (understands user intent, safe and trustworthy to use), and a rescuer (helps when recommendations aren't quite what the user is looking for). Historically, search has primarily focused on relevance based on exact search queries and limited metadata such as titles. Roblox is constantly improving search to better understand user intent. For example, you can now use semantic search for all of our officially supported languages to find games through natural language queries, such as "food games" or "avatar editors". ### Discover (top charts and trending sorts) The [Discover](https://www.roblox.com/discover#/) page is designed to reflect the many constantly changing high-quality games available on the platform and showcasing the best-performing content on Roblox. Roblox is [committed](https://devforum.roblox.com/t/discovery-on-roblox-past-present-and-future-vision/2859111) to enhance the Discover page to better serve Roblox's community and provide more opportunities to highlight a diverse set of top recommendations for every user. For example, top and trending sorts have been [updated](https://devforum.roblox.com/t/testing-an-enhanced-discover-page-top-charts-and-new-sorts/2954676) to transform the Discover page into a more impactful and dynamic space that delights users. ### Notifications **Notifications** elevate timely and actionable information to users. Historically, Roblox has focused on building and scaling social notifications, such as friend requests and invitations. This system allows for creators to engage with users directly while they are away. Milestones, high scores, [friend activity](https://devforum.roblox.com/t/user-mentions-in-experience-notifications/2980675), and other key moments can be delivered to users as personalized notifications to the notification stream. For additional information and implementation instructions, see [experience notifications](/docs/en-us/cloud/guides/experience-notifications.md). You can also use [in-experience notification permission prompts](https://devforum.roblox.com/t/introducing-in-experience-notification-permission-prompts/2909125) to upsell notification opt-in within games. Notifications can help resurrect lapsed users or remind users when they need to take an action.
--- title: "Early Access Programs" url: /docs/en-us/early-access-programs last_updated: 2026-06-29T19:33:59Z description: "Roblox Early Access Programs (EAPs) let creators test new features, tools, and updates before they are released to everyone else." --- # Early Access Programs **Early Access Programs (EAPs)** let creators test new features, tools, and updates before they are released to everyone else. Joining an EAP helps Roblox improve the platform and gives you early access, but you need to follow special steps to avoid accidentally breaking your games. ## Protect your game files > **Error:** Do NOT open or change your live, published games while using an Early Access Program. When you are in an EAP, Roblox changes how it saves files to a new format that regular players cannot use. If you publish a game using an EAP feature, nobody will be able to play your game. If you accidentally save over a live game file, you will need to go to the Creator Hub and revert to an older version to fix it. - **Always make a copy** — Before you open any file in an EAP, make a backup copy of your file (`.rbxl`) or start a brand new, empty place. - **Use testing places** — Only test EAP features in separate test games that do not affect your main project or active players. ## How to sign up To join an Early Access Program: 1. **Fill out the survey** — Complete the sign-up survey for the program you want to join. This form is usually in the DevForum announcement. Since you can easily toggle features on and off, you can sign up with either your main account or an alternate account. 2. **Wait for the next wave** — Roblox accepts new creators in groups to make sure everything stays stable. Check the [Developer Forum](https://devforum.roblox.com/) for announcements when a new wave of users is added. 3. **Check your settings** — To see if you are accepted, go to [Roblox](https://www.roblox.com) > **Settings** > **Account Settings**. Look for the **Early Access Programs** section. If you see the dropdown option, you have been accepted into the program. ## Private release channels When you enable an EAP, your Roblox account moves from the standard public version of Roblox to a private testing channel. This keeps unfinished features away from standard players, but it comes with some restrictions: | Action | Standard Roblox channel | Early Access Program channel | | --- | --- | --- | | Playing public games | > **Success:** You can join and play any public game on Roblox. | > **Error:** Blocked. You cannot join standard public game servers. | | Team Create (Studio) | > **Success:** You can work with any other creator in Studio. | > **Warning:** Restricted. You can only collaborate with friends who are in the exact same EAP. | | Testing with others | > **Success:** Anyone can join you to test your games. | > **Warning:** You can only playtest with other creators who have the same EAP turned on. | ## How to update Studio and the Roblox Player Once you enable the program, you need to update your apps to get the new tools: 1. **Enable the program** — Choose the program you want from the **Early Access Programs** dropdown menu in your Account Settings. 2. **Update Roblox Studio** — After a few minutes, you will see an **Update** button on the top right of your Studio screen. Click it to update and restart Studio. 3. **Update the Roblox Player** — The next time you open the Roblox Player to play a game, a message will pop up asking you to install an update. Let it install and restart. 4. **Ready to go** — After restarting, the new early access features will be active and ready to test. ## How files and saving work When you make or edit things in an EAP, Roblox handles your files differently: - **Existing files** — If you open an old game file, the EAP features will automatically upgrade your project to the new format. When you save it, it will be stored in the new format. - **New files** — Any brand new game files you create will use the new format right away when you save them to Roblox. - **Testing in the cloud** — You can publish your test games to Roblox and play them, as long as everyone playing has the same EAP update installed. ## How to leave a program You can leave an Early Access Program and go back to standard Roblox at any time: 1. Go to [Roblox](https://www.roblox.com) > **Settings** > **Account Settings**. 2. Find the **Early Access Programs** dropdown menu and change it to **No Program Selected**. 3. This moves your account back to the standard public Roblox version. 4. Open Roblox Studio or the Roblox Player. They will update and restart one more time to take you back to the standard public version. --- title: "Buy upgrades" url: /docs/en-us/education/adventure-game-series/buying-upgrades last_updated: 2026-06-29T19:33:59Z description: "Part of the Adventure Game Series in Roblox. Code a script to sell items in a Roblox experience." --- # Buy upgrades This brings us to the last stage of the game loop - buying upgrades. By letting players buy upgrades that increase the size of their item bag, they can harvest more items per trip and earn even more gold. ## Create a shop Each shop will have a button that players click to purchase a larger item bag. The shop itself will be a part with a SurfaceGUI, an item that allows for text to be written on parts. ### Create the sign 1. In the Workspace, create a new model named Shop. 2. In Shop, create a new block part named BuyButton. 3. In BuyButton, add a new Surface GUI by clicking the + and scrolling to GUI. 4. In Surface GUI, add a new TextLabel named BuyText. A small label will appear somewhere on the part. 5. Depending on how the part was made, the label can be somewhere else. If you don't see the text on the side you want, go into the SurfaceGUI and find the Face property. Change that property until you see the text label. > **Warning:** Depending on the part, the Face property might be different than shown above. ### Change the sign text Right now, the TextLabel is really small and hard for players to see. It needs to be scaled up. 1. In the BuyText properties, click on the arrow next to **Size**. Change the **Offset** for X (left and right) and Y (up and down) to 0. 2. Change the **scale** of X and Y to 0.5 to make a square. 3. Scroll up in the TextLabel properties and click the arrow left of **AnchorPoint**. Type in 0.5 for X and Y. This moves part of your label out of view but you'll position it correctly next. 4. Scroll-down in the properties and open **Position**. Change the scale of X and Y to 0.5 so the box is centered in the middle. 5. In the BuyText properties, scroll-down and change **Text** to something descriptive. For instance: `"Buy Larger Bag: 100 gold"`. 6. Check **TextScaled** to be **on**. This automatically scales your text so it fits the box. ### Add a click detector Players will buy items by clicking on the shop rather than just touching it. The script will use a click detector to tell if a player has clicked the shop sign. **ClickDetectors** are objects that allow users to interact with something in the environment, such as opening a door. 1. In the BuyButton, add a ClickDetector. 2. In BuyButton, add a new script named BuyScript and give it a descriptive comment. 3. In BuyScript, create variables to store the button part and click detector.```lua -- Lets players click a button to buy an upgrade that increases Spaces local buyButton = script.Parent local clickDetector = buyButton.ClickDetector ``` 4. Create a new function named `giveUpgrade()` that gets a parameter named `player`. The function will upgrade a player's spaces whenever they click the button.```lua local buyButton = script.Parent local clickDetector = buyButton.ClickDetector local function giveUpgrade(player) end ``` 5. After the function, type connect the click detector's `MouseClick` event to the `giveUpgrade()` function.```lua local function giveUpgrade(player) end clickDetector.MouseClick:Connect(giveUpgrade) ``` 6. Add a print statement in `giveUpgrade()` to test the function.```lua local function giveUpgrade(player) print("Someone clicked the button.") end ``` 7. **Play** your project. Click the button and check that you see the text in the Output window. > **Info:** Click Detectors have a distance from which players can interact with it. To change this distance, go to the object and modify its MaxActivationDistance. ### Troubleshooting tips **Issue:** You're unable to click the button or a mouse cursor doesn't appear on the button. - Make sure that the ClickDetector object is a child of the part you're trying to click on. - Check that your character is close enough to the button. Or, make sure that the tool is not equipped. ## Buy upgrades With a working button, it's time to add code into giveUpgrade to remove a player's gold in exchange for an upgraded bag. ### Add upgrade variables Each upgrade will have two variables: the cost of the upgrade and how many spaces it has. 1. In BuyScript, under `local clickDetector`, create two variables: - `newSpace`: How many spaces an upgrade adds when purchased. - `upgradeCost`: The cost of a single upgrade```lua -- Variables for the upgrade local newSpaces = 10 local upgradeCost = 100 ``` ### Grant upgrades Before selling the player the upgrade, you need to check if they have enough money. If they do, you'll add to their maximum amount of spaces. 1. In `giveUpgrade()`, type the following below to get the player's leaderstats so you can access their gold and spaces variables.```lua local function giveUpgrade(player) print("Someone clicked the button") -- Get's the player's leaderboard to get other IntValues local playerStats = player:FindFirstChild("leaderstats") if playerStats then -- Gets the player's money and spaces to make changes local playerGold = playerStats:FindFirstChild("Gold") local playerSpaces = playerStats:FindFirstChild("Spaces") end end ``` > **Warning:** Make sure the variable names are the same as the ones in the PlayerSetup script. 2. After writing the variables for spaces, create an if statement to if the value of `playerGold` is more or equal to the upgrade cost.```lua local function giveUpgrade(player) local playerStats = player:FindFirstChild("leaderstats") if playerStats then local playerGold = playerStats:FindFirstChild("Gold") local playerSpaces = playerStats:FindFirstChild("Spaces") -- Checks if player has enough money to afford the upgrade if playerGold and playerSpaces and playerGold.Value >= upgradeCost then end end end ``` 3. In the if statement, subtract the upgrade's cost from the player's gold.```lua if playerGold and playerSpaces and playerGold.Value >= upgradeCost then -- Subtract the item's cost from the player's money playerGold.Value -= upgradeCost end ``` 4. On the next line, add the number of the player's current spaces along with the new spaces granted per upgrade.```lua if playerGold and playerSpaces and playerGold.Value >= upgradeCost then playerGold.Value -= upgradeCost playerSpaces.Value += newSpaces end ``` 5. Play your project and check the leaderboard to check if the spaces upgrade worked. ### Troubleshooting tips At this point, the upgrades don't work as intended, try one of the following below. - Make sure anything in the `()` of `FindFirstChild()` has quotations on **both** sides, like `"leaderstats"`. - Check that each string in FindFirstChild is the exact same as that IntValue's name in the PlayerSetup script. For example, if your code uses Rubies as money, you should have `FindFirstChild("Rubies")`. - Make sure that `giveUpgrade()` is above `clickDetector.MouseClick`. ## Complete BuyScript script A finished version of the script can be referenced below. ```lua -- Lets players click a button to buy an upgrade that increases MaxSpaces local buyButton = script.Parent local clickDetector = buyButton.ClickDetector -- Variables for the upgrade local newSpaces = 10 local upgradeCost = 100 local function giveUpgrade(player) print("Someone clicked the button") -- Get's the player's leaderboard to get other IntValues local playerStats = player:FindFirstChild("leaderstats") if playerStats then -- Gets the player's money and spaces to make changes local playerGold = playerStats:FindFirstChild("Gold") local playerSpaces = playerStats:FindFirstChild("Spaces") -- Checks if player has enough money to afford the upgrade if playerGold and playerSpaces and playerGold.Value >= upgradeCost then print("Player can buy item") -- Subtract the item's cost from the player's money playerGold.Value -= upgradeCost playerSpaces.Value += newSpaces end end end clickDetector.MouseClick:Connect(giveUpgrade) ``` --- title: "Code the leaderboard" url: /docs/en-us/education/adventure-game-series/code-the-leaderboard last_updated: 2026-06-29T19:33:59Z description: "Part of the Adventure Game Series in Roblox. Create a leaderboard in Roblox Studio to track player items." --- # Code the leaderboard In-game, player will have important stats they need to see like the items they've collected. These numbers will be displayed using a leaderboard. **Leaderboards** are built-in features of Roblox that need a script to be activated and customized. > **Info:** Note that the leaderboard in this experience doesn't save player information in-between sessions. For information on saving player data, you'll need to use an advanced coding concept called [data stores](/docs/en-us/cloud-services/data-stores.md). ## Set up the leaderboard Whenever a player is added to the experience, they'll need to be added to the leaderboard along with code for tracking the individual stats. 1. In the Explorer, under **ServerScriptService**, create a new script named PlayerSetup. In that script, delete the hello world line and write a descriptive comment. 2. After the comment, create a custom function named onPlayerJoin with a parameter named player.```lua -- Creates a leaderboard that shows player variables local function onPlayerJoin(player) end ``` 3. In `onPlayerJoin`, create a variable named `leaderstats`, and have it create a new **Folder** Instance.```lua local function onPlayerJoin(player) local leaderstats = Instance.new("Folder") end ``` 4. Name the new **Folder** instance `leaderstats`, and parent it to the player. Naming the folder `leaderstats` lets Roblox Studio know to create a leaderboard.```lua local function onPlayerJoin(player) local leaderstats = Instance.new("Folder") leaderstats.Name = "leaderstats" leaderstats.Parent = player end ``` > **Warning:** Because leaderboards are a built-in feature, they must be named exactly as seen. For example, a folder named "leaderboard" wouldn't work. 5. After the end of the function, connect `OnPlayerJoin` to the `PlayerAdded` event. Whenever a player joins the experience, each player will be provided the leaderboard.```lua local Players = game:GetService("Players") local function onPlayerJoin(player) local leaderstats = Instance.new("Folder") leaderstats.Name = "leaderstats" leaderstats.Parent = player end Players.PlayerAdded:Connect(onPlayerJoin) ``` > **Warning:** Don't test yet since you won't see a leaderboard. Because there are no stats to display, the leaderboard won't appear. ## Track player stats Now that a leaderboard is created, it needs to show the player these numbers: - **Gold** - How much money the player has. - **Items** - How many items the player has collected from the world. - **Spaces** - The most items a player can hold at one time. Each of these numbers will be an IntValue, a placeholder object for a number. ### Code player gold Start with coding a stat for gold. 1. In `OnPlayerJoin`, under `leaderstats.Parent = player`, type `local gold = Instance.new("IntValue")`. This creates a new IntValue and stores it in the variable gold.```lua local function onPlayerJoin(player) local leaderstats = Instance.new("Folder") leaderstats.Name = "leaderstats" leaderstats.Parent = player local gold = Instance.new("IntValue") end ``` 2. Next, type `gold.Name = "Gold"`. This gives the IntValue a name so you can use it in other scripts. The name will also be shown to players on the leaderboard.```lua local function onPlayerJoin(player) local gold = Instance.new("IntValue") gold.Name = "Gold" end ``` > **Warning:** If you decide to use your own stat names, keep track of their exact name and spelling. They'll be referenced later in the series for other scripts. 3. On a new line, type `gold.Value = 0`. This sets the starting value for players.```lua local function onPlayerJoin(player) local gold = Instance.new("IntValue") gold.Name = "Gold" gold.Value = 0 end ``` > **Info:** While variables are normally changed using `=` as in `myNumber = 10`, an IntValue is changed using `Value`, like `myIntValue.Value = 10`. 4. Type `gold.Parent = leaderstats`. This parents the IntValue for gold to leaderstats. If the IntValue is not parented to leaderstats, players won't see it.```lua local function onPlayerJoin(player) local gold = Instance.new("IntValue") gold.Name = "Gold" gold.Value = 0 gold.Parent = leaderstats end ``` 5. Play your project and notice that a leaderboard appears in the top right. ### Troubleshooting tips If you don't see the leaderboard, try the following: - Make sure that `.Value` is capitalized. - Make sure that the variable for the IntValue is parented to the leaderboard like `gold.Parent = leaderstats`. ### Code items and spaces Remember that the stat names can be anything based off the game design document. In other words, `"Items"` can be `"Crystals"` instead. 1. Add a blank line to separate the next stat, then create the item stat by setting up a new IntValue the same way you did for gold.```lua local function onPlayerJoin(player) gold.Parent = leaderstats -- Create the Items stat local items = Instance.new("IntValue") items.Name = "Items" items.Value = 0 items.Parent = leaderstats end ``` 2. Create a new stat for the player's bag spaces. Set `spaces.Value` to `2` so players start the experience only being able to hold two items at once, encouraging them buy a new bag as soon as they can.```lua local function onPlayerJoin(player) items.Parent = leaderstats -- Create the Spaces stat local spaces = Instance.new("IntValue") spaces.Name = "Spaces" spaces.Value = 2 spaces.Parent = leaderstats end ``` 3. Test the project. Players should have a leaderboard showing Gold, Items, and Spaces. If the leaderboard doesn't appear, try checking the following below. - If you can't see the number on the leaderboard, check that each IntValue is parented to leaderstats. - Make sure each IntValue is spelled exactly as shown - Check that the PlayerAdded event is at the bottom of the script ## Complete PlayerSetup script A finished version of the script can be referenced below. ```lua local Players = game:GetService("Players") -- Creates a leaderboard that shows player variables local function onPlayerJoin(player) local leaderstats = Instance.new("Folder") leaderstats.Name = "leaderstats" leaderstats.Parent = player local gold = Instance.new("IntValue") gold.Name = "Gold" gold.Value = 0 gold.Parent = leaderstats local items = Instance.new("IntValue") items.Name = "Items" items.Value = 0 items.Parent = leaderstats local spaces = Instance.new("IntValue") spaces.Name = "Spaces" spaces.Value = 2 spaces.Parent = leaderstats end -- Run onPlayerJoin when the PlayerAdded event fires Players.PlayerAdded:Connect(onPlayerJoin) ``` --- title: "Collect items" url: /docs/en-us/education/adventure-game-series/collect-items last_updated: 2026-06-29T19:33:59Z description: "Part of the Adventure Game Series in Roblox. Code a tool to collect items." --- # Collect items With the leaderboard created, players need something to collect. For that, you need to create a 3D item for players to find in the world. Below is a video of the process of players harvesting items. ## Build an item Items in the experience are 3D models that players use a tool to harvest. Each item, once harvested, disappears and then reappears after a limited amount of time. For the item, refer back to your game design document. This series uses crystals as the example. 1. Create an object, either using parts or objects found by trusted users in the Marketplace. If desired, download the crystal part with this [link](../../assets/education/adventure-game-series/crystalPart.rbxm). To add it, right-click on the Workspace and select **Insert from File**. 2. If using your own part, group all the parts into a **Model**. One way of doing this is to select all items, right-click on a part, and select **Group**. This creates a model that organizes your parts. 3. Make sure that the parts are all **anchored**. 4. So items cannot be harvested while they disappear, create a **BoolValue** named CanHarvest to track its status. 5. In Properties for CanHarvest, check the **Value** box. Checking the value box makes the boolean true, meaning players can harvest the item. ## Create a tool Players need something like an ax or a shovel to gather items with. In Roblox, items that players can equip and use are called **tools**. This lesson uses a starter tool with all the parts and an animation already made that you can customize later. ### Add the tool For players to use the starter tool, download and place it in the StarterPack. 1. Download the starter tool below. 2. In Explorer, under Workspace, right-click on StarterPack. Then, select **Insert from File**. 3. Select the downloaded file, `starterTool.rbxm`. 4. Playtest your project. Players should be equipped with the tool as soon as they start. In-game, press `1` to equip or put away the tool. Left-click to swing it. > **Info:** If you want to rename the starter tool, just rename the object in StarterPack. That name displays to players. ## Code the tool If the tool hits a harvestable object and the player has enough space in their bag, the player's item count goes up by 1 on the leaderboard. Harvesting an item makes it disappear for a few seconds and become unharvestable for a few seconds before reappearing. This encourages players to explore to find more items, instead of just clicking the same item. ### Set up the script At this point, add a script to the tool. This script handles what happens when the tool touches a harvestable object. 1. In StarterPack, under StarterTool, add a new script named ToolScript. 2. In the script, write a descriptive comment at top, and then create variables to store the tool part and the tool itself.```lua -- Gives players item when they touch a harvestable part local tool = script.Parent local toolPart = tool.Handle ``` ### Check for items Whenever the tool touches an object, it checks if that object has CanHarvest inside and if the boolean is set to True. 1. Create a new function named `onTouch()` with a parameter named `partTouched`.```lua local tool = script.Parent local toolPart = tool.Handle local function onTouch(partTouched) end ``` 2. In that function, create a local variable named `canHarvest`. Then, use the `FindFirstChild()` function to see if there is CanHarvest boolean in the parent of that part.```lua local function onTouch(partTouched) local canHarvest = partTouched:FindFirstChild("CanHarvest") end ``` 3. Now the script needs to check if there was actually anything found and if so, run the code. To do this, create an if statement where the condition is `canHarvest`. If anything exists in `canHarvest`, this statement evaluates to true.```lua local function onTouch(partTouched) local canHarvest = partTouched:FindFirstChild("CanHarvest") if canHarvest then end end ``` 4. In the if statement, add a print statement to see if the script is working. You can code the logic for harvesting items after you're sure it works.```lua if canHarvest then -- Used for testing if code works print("Found an item") end ``` 5. Under the function's `end` statement, add `toolPart.Touched:Connect(onTouch)`. This lets the script check if anything is touching the tool (or in this case, its handle) and if so, calls `onTouch()`.```lua local function onTouch(partTouched) local canHarvest = partTouched:FindFirstChild("CanHarvest") if canHarvest then print("Found an item") end end toolPart.Touched:Connect(onTouch) ``` 6. Play the project and use the tool on a harvestable item (left-click to swing). Make sure you see the message "Found an item" in the Output window. ### Troubleshooting tips If you don't see the message, try the following tips. - If you're using custom parts and meshes, it's possible to get an error. The script only works if the CanHarvest object is a child of the part the tool is touching. - Make sure that the tool is in the StarterPack, not in the Workspace. - Check that the part is anchored. ### Get player stats Before increasing the player's items, the tool must find the location of how many items a player has in that player's leaderboard. Once the tool has access to the leaderboard, it can change that player's item count. 1. First, get the player using the tool. In the ToolScript, under `local item = toolitem`, and above the custom function, type:```lua local item = toolitem local backpack = tool.Parent local player = backpack.Parent local function onTouch(partTouched) ``` 2. On the next line, find the player's leaderstats using the `FindFirstChild()` function.```lua local backpack = tool.Parent local player = backpack.Parent local playerStats = player:FindFirstChild("leaderstats") local function onTouch(partTouched) ``` 3. Under `local playerStats`, create variables to store the items and spaces stats.```lua local playerStats = player:FindFirstChild("leaderstats") local playerItems = playerStats:FindFirstChild("Items") local playerSpaces = playerStats:FindFirstChild("Spaces") ``` > **Warning:** The strings inside the `()` for `FindFirstChild()` need to be the **same** as the IntValue names in the PlayerSetup script. If you had a different name for the items, make sure it's the same as in the PlayerSetup script. ### Check for a harvestable object Now that the tool script has the playerItems and playerSpaces variables created, you can start giving players an item. Use the function created to check if the object touching the tool can be harvested, and if the player has enough space in their bag to increase the items shown on the leaderboard by one. 1. The script will need an if statement with two conditions to be met. Start by creating an if statement, then add in the following conditions, connected with the `and` keyword. . - `canHarvest.Value == true` - `playerItems.Value < playerSpaces.Value````lua local function onTouch(partTouched) local canHarvest = partTouched.Parent:FindFirstChild("CanHarvest") if canHarvest then if canHarvest.Value == true and playerItems.Value < playerSpaces.Value then end end end ``` > **Info:** To access the contents of any value object, type .Value at the end of it. If you just do the object itself, you'll just get its name. 2. In the if statement itself, add to the player's items by typing `playerItems.Value += 1`.```lua if canHarvest then if canHarvest.Value == true and playerItems.Value < playerSpaces.Value then playerItems.Value += 1 end end ``` 3. Play your project; use your tool to harvest an item and check that the item count went up. > **Warning:** Right now, the player's Items stat will **instantly** fill up after hitting one item. In the next section, you'll make the item reset so players only get one item from each item. ### Reset the item When the item is harvested, it will reset in two ways: - The item will disappear and not be interactable - CanHarvest set to false The item will then go back to normal after a short time. This way, players only get one item for each harvest, and need to look around for more while the original resets. 1. Under where items are added, set `canHarvest` to false. By making the value of `canHarvest` false as soon as players harvest the item, the script won't give more than one item per tool hit.```lua if canHarvest then if canHarvest.Value == true and playerItems.Value < playerSpaces.Value then playerItems.Value += 1 canHarvest.Value = false end end ``` 2. After setting the value to false, set the part's Transparency to 1 (invisible), and CanCollide to false, meaning players can't touch it.```lua if canHarvest.Value == true and playerItems.Value < playerSpaces.Value then playerItems.Value += 1 canHarvest.Value = false partTouched.Transparency = 1 partTouched.CanCollide = false end ``` 3. Type `task.wait(5)` to give time for the item to reset. 5 is a suggested number, and maybe differ for how long you'd like for your experience.```lua if canHarvest.Value == true and playerItems.Value < playerSpaces.Value then playerItems.Value += 1 canHarvest.Value = false partTouched.Transparency = 1 partTouched.CanCollide = false task.wait(5) end ``` 4. After the wait, do the opposite of previous code, by setting the CanHarvest to true, and resetting the Transparency and CanCollide to their original values.```lua task.wait(5) canHarvest.Value = true partTouched.Transparency = 0 partTouched.CanCollide = true end ``` 5. Play the project and check: - The player only gets 1 item for harvesting an item. - The item disappears and then reappears after five seconds. ### Troubleshooting tips At this point, if one of the checks didn't pass try one of the following below. - Check that Transparency and CanCollide are spelled and capitalized exactly. - Make sure to use canHarvest.Value and not canHarvest = true. ## Complete ToolScript A finished version of the script can be referenced below. ```lua local toolPart = script.Parent local tool = toolPart.Parent local backpack = tool.Parent local player = backpack.Parent local playerStats = player:FindFirstChild("leaderstats") local playerItems = playerStats:FindFirstChild("Items") local playerSpaces = playerStats:FindFirstChild("Spaces") local function onTouch(partTouched) local canHarvest = partTouched:FindFirstChild("CanHarvest") if canHarvest then if canHarvest.Value == true and playerItems.Value < playerSpaces.Value then playerItems.Value += 1 canHarvest.Value = false -- Reset partTouched, the harvested item partTouched.Transparency = 1 partTouched.CanCollide = false task.wait(5) -- Make the harvested item reappear and usable again canHarvest.Value = true partTouched.Transparency = 0 partTouched.CanCollide = true end end end toolPart.Touched:Connect(onTouch) ``` --- title: "Create the map" url: /docs/en-us/education/adventure-game-series/create-the-map last_updated: 2026-06-29T19:33:59Z description: "Part of the Adventure Game Series in Roblox. Learn how to use terrain tools in Roblox Studio." --- # Create the map Adventure games can come in different forms but often focus on getting a player to explore a world. This experience will be all about exploring, harvesting items, selling them, upgrading bags, and then doing it all over again. This cycle of actions is called a **game loop**. Each part of the game loop is a different game mechanic, or action, players can do. This game loop has four mechanics: - **Explore** the experience to find items. - **Harvest** items. Players can only hold so many at a time. - **Sell** items for gold. - **Buy** bag upgrades to holds more items at a time. ## Plan the project Before professional developers start a new project, they plan out what things will look like in a process called **pre-production**. They'll often create a game design document that includes how the experience works. While **game design documents** (GDDs) can often be long, for this series, you'll start with a vision of what players will do and sketches of the world they'll explore. Having a clear vision helps speed up development and also make it easier to communicate your ideas to get feedback from others. ### Create a game design document To start, get a piece of paper or open up a word editor to keep track of the document. ### Game loop prompts The following are used to help you figure out the game loop, or what players will be doing. | Component | Examples | | --- | --- | | **Setting**: A description of the setting, such as a theme or what players will see when exploring. | | | **Items**: What players gather or harvest. | | | **Tools**: The object players use to collect that item. | | | **Upgrades**: What players will buy to hold more items. | | ### Map layouts The last part of the game design document is to create the map layout. This gives you an idea of what to create later. An example layout is below. In the layout, annotate these key places: - Where players start the experience. - Placement of the harvestable items. - Where players will sell items. - Where players will buy upgrades. - Environment details, such as grasslands, hills, and more. ## Create the environment The environment will be created using the **Terrain Editor**. This tool in Roblox Studio is used to create landscapes like mountains, rivers, or deserts. While using the tools, reference the map layout previously created. You'll use the tools in the Terrain Editor to create the world you drew in your game vision document. 1. Open Studio and create a new **Flat Terrain** project. 2. In the **Home** tab, click the **Terrain** button. This opens the **Terrain Editor** on the left of the screen. 3. In the Terrain Editor, go to the **Edit** tab. Then use the **Paint** tool to change the material of the landscape. Depending on your vision, paint in roads, water, or even lava.![Paint tool indicated in Edit tab of Terrain Editor](../../assets/studio/terrain-editor/Edit-Tab-Paint.png) > **Info:** While using the terrain editor, rotate the camera as you build. Terrain might look differently from different angles. 4. Use the **Draw** tool to draw the setting you created in your vision document. Use the **Brush Mode** toggle to switch between adding and subtracting terrain.![Draw tool indicated in Edit tab of Terrain Editor](../../assets/studio/terrain-editor/Edit-Tab-Draw.png) - Add terrain to create mountains, hills, and break up grasslands. - Subtract terrain to sculpt mountains, create sharper features like cliffs, or carve rivers. ## Finished map sample Once finished, your map might look like the following. --- title: "Finish the project" url: /docs/en-us/education/adventure-game-series/finishing-the-project last_updated: 2026-06-29T19:33:59Z description: "Part of the Adventure Game Series in Roblox. Finish the project by adjusting scripts." --- # Finish the project Your experience is almost ready for others to play! Before sharing, you'll make some small changes to scripts, build out your world a bit more, and then play-test your experience. ## Start player variables While making the experience, many variables were set to small values, like the player's starting spaces was 2. While this made it easy to test, this might not be the right number for players in their final experience. Before sharing your experience with others, it's important that the variables feel **challenging**, but **fair**. Getting the right numbers for an experience will give players a more fun experience. 1. In ServerScriptService > PlayerSetup change the value of `Spaces`. Remember this is how many spaces a player has to hold items before they must sell them. A number between 6-10 is a good starting point, but play-testing for the right amount is best.```lua local maxSpaces = Instance.new("IntValue") maxSpaces.Name = "Spaces" -- 6 as a possible value for a player's bag maxSpaces.Value = 6 ``` 2. In SellPart > SellScript, change the value of how much players get for each item. This is the number multiplied by `playerItems.Value`. Try a number between 50 and 200.```lua local totalSell = playerItems.Value * 200 ``` 3. In Shop > BuyButton > BuyScript, change the starting values in the variables for `newMaxItems` (recommended 10-15) and `upgradeCost` (recommended 500-1000).```lua -- Possible values for a game local newMaxItems = 15 local upgradeCost = 500 ``` ## Add more items Where you place your items will affect how much fun a player has getting them. Different placements and obstacles to getting items are two ways of making item collection more fun. ## Playtesting and feedback Each number that a game designer uses for something in their experience is an educated guess based on what might be fun. Each part of your experience should be challenging, but fair. Giving a player 10 spaces to start with might sound fun, but you don't really know until you test out the project. Always test to see if it feels fun, and ask friends to play-test it as well. As you play-test, think about these questions. - Does the player have to walk a lot to find an item or are they too easy to find? - Does the cost of upgrading feel too easy or too hard? - Are there any bugs that you didn't notice? ## Expand the experience Although you've finished the course, there are many ways you can continue adding to your experience using the skills you've learned: - Make more than one item for players to collect - Create different types of upgrades --- title: "Create an adventure game" url: /docs/en-us/education/adventure-game-series/landing last_updated: 2026-06-29T19:33:59Z description: "How to make a game in Roblox Studio. Learn to design and code an adventure experience for Roblox as a beginner." --- # Create an adventure game ### Series description Create an adventure game where players explore a world to find items to harvest and sell. Perfect for those who have already have some familiarity with Roblox Studio and basic coding concepts up to if/then statements. ### Objectives and prerequisites | **Learning Objectives** | Apply knowledge of common coding concepts, such as variables, if statements, and functions to create scripts. Practice the design process by pre-planning, designing, and then testing to iterate and improve an experience. | | --- | --- | | **Prerequisites** | Have an understanding of the following Luau concepts: variables, conditional (if/else) statements, and typing functions. Feel comfortable working with the world creation tools in Roblox, such as making parts or creating terrain. | ### Series contents | Article | Description | | --- | --- | | [Create the Map](/docs/en-us/education/adventure-game-series/create-the-map.md) | Go into pre-planning by creating a game design document, then using it to design a map with Roblox terrain tools. | | [Code the Leaderboard](/docs/en-us/education/adventure-game-series/code-the-leaderboard.md) | Create a script that manages items players collect in the experience. | | [Making Collecting Items](/docs/en-us/education/adventure-game-series/collect-items.md) | Design an item for players to gather, then create and code a tool so they can harvest it | | [Selling Items](/docs/en-us/education/adventure-game-series/selling-items.md) | Use Touch events in scripts to create a part that allows players to seel their items for currency. | | [Buying Upgrades](/docs/en-us/education/adventure-game-series/buying-upgrades.md) | Learn how to use click detectors to create a button where players can buy upgrades to give them more space for items. | | [Finishing the Project](/docs/en-us/education/adventure-game-series/finishing-the-project.md) | Some final steps to wrap up the project, as well as tips on how to expand and improve. | ### Example project | | [Adventure Game Example](https://www.roblox.com/games/9544298367/) Play a version of the final project to see what you can develop with this series. | | --- | --- | --- title: "Sell items" url: /docs/en-us/education/adventure-game-series/selling-items last_updated: 2026-06-29T19:33:59Z description: "Part of the Adventure Game Series in Roblox. Code how to sell items in a Roblox experience." --- # Sell items For the next stage of the game loop, players need to sell their items for gold to allow them to purchase more space in their backpacks. ## Create a sell platform Players will sell their items by stepping onto a platform that gives them gold for each item in their bag. ### Set up the platform The platform can be any part and will include a script that handles selling. 1. Create a new part named SellPlatform. Customize it to fit the theme of your experience. 2. In SellPlatform, create a new script named SellScript and add a comment. 3. In SellScript, type `local sellPart = script.Parent` to get the SellPlatform part.```lua -- Sells all a player's items and gives them gold local sellPart = script.Parent ``` ### Handle touch events To use the platform, the script needs a function to check if any players touch it. 1. Create a function named `onTouch()` that checks if a player is touching the platform.```lua local function onTouch(partTouched) local character = partTouched.Parent end ``` 2. To change any of the stats on the leaderboard, the script needs to know what player is controlling the character. In the if statement, use the `GetPlayerFromCharacter()` function to find a player.```lua local Players = game:GetService("Players") local player = Players:GetPlayerFromCharacter(character) ``` 3. On the next line, get that player's leaderstats container.```lua local Players = game:GetService("Players") local player = Players:GetPlayerFromCharacter(character) if player then -- Gets the player's leaderboard. Needed to get items and money local playerStats = player:FindFirstChild("leaderstats") end ``` 4. On the next line, create variables to get the player's money and items.```lua local Players = game:GetService("Players") local player = Players:GetPlayerFromCharacter(character) if player then -- Gets the player's leaderboard. Needed to get items and money local playerStats = player:FindFirstChild("leaderstats") if playerStats then -- Gets the player's items and money local playerItems = playerStats:FindFirstChild("Items") local playerGold = playerStats:FindFirstChild("Gold") end end ``` > **Warning:** Make sure the names of everything in `FindFirstChild()` is exactly like the names written in the PlayerSetup script. For example, if your money is "Rubies" in PlayerSetup, playerGold should look for "Rubies" instead of "Gold". 5. To check your work, add a print statement that will run if a player touched sellPart.```lua local playerItems = playerStats:FindFirstChild("Items") local playerGold = playerStats:FindFirstChild("Gold") print("A player touched sellPart") ``` 6. At the bottom of the script, connect the `onTouch()` to sellPart's Touched event.```lua local Players = game:GetService("Players") local function onTouch(partTouched) local character = partTouched.Parent local player = Players:GetPlayerFromCharacter(character) if player then -- Gets the player's leaderboard. Needed to get items and money local playerStats = player:FindFirstChild("leaderstats") if playerStats then -- Gets the player's items and money local playerItems = playerStats:FindFirstChild("Items") local playerGold = playerStats:FindFirstChild("Gold") print("A player touched sellPart") end end end sellPart.Touched:Connect(onTouch) ``` 7. Play your project and step on sellPart; you should see the message `"A Player touched sellPart"` in the Output window. ## Sell items In this experience, a player will get 100 Gold for each item. After getting money, their items will be set back to 0, letting players explore the world for more items. ### Code a new sell function 1. Under the variables, create a function named `sellItems()` that gets two parameters named `playerItems` and `playerGold`.```lua -- Sells all a player's items and gives them gold local sellPart = script.Parent local function sellItems(playerItems, playerGold) end local function onTouch(partTouched) ``` 2. To give players the right amount of gold, take the value of the `playerItems` and multiply it by the amount of gold they should receive per item. This example gives one hundred gold pieces per item. In the `sellItems()` function, type `local totalSell = playerItems.Value * 100````lua local function sellItems(playerItems, playerGold) -- Gets how many items the player has and multiplies that by item worth. local totalSell = playerItems.Value * 100 end ``` 3. Type `playerGold.Value += totalSell` to add the gold for the items to their current gold.```lua local function sellItems(playerItems, playerGold) local totalSell = playerItems.Value * 100 -- Add how much the player earns to their money playerGold.Value += totalSell end ``` 4. Type `playerItems.Value = 0`. This sets a player's items back to 0. If a player's items aren't set back to 0, the script will keep giving players gold without stopping.```lua local function sellItems(playerItems, playerGold) local totalSell = playerItems.Value * 100 playerGold.Value += totalSell playerItems.Value = 0 end ``` 5. In the `onTouch()` function, **under the second if statement**, call the `sellItems()` function. Pass in the parameters, `playerItems` and `playerGold` so they can be changed.```lua local Players = game:GetService("Players") local player = Players:GetPlayerFromCharacter(character) if player then -- Gets the player's leaderboard. Needed to get items and money local playerStats = player:FindFirstChild("leaderstats") if playerStats then -- Gets the player's items and money local playerItems = playerStats:FindFirstChild("Items") local playerGold = playerStats:FindFirstChild("Gold") if playerItems and playerGold then sellItems(playerItems, playerGold) end end end ``` 6. Play your project; check that whenever a player steps on the platform, their gold increases and items are set to 0. ### Troubleshooting Tips At this point, selling items doesn't work as intended, try one of the following below. - `sellItems()` is called in the **second** if statement that checks the player's items. - Any IntValue, like playerItems, uses .Value at the end if you're making a change to it. Value is always capitalized. - `sellPart.Touched:Connect(onTouch)` is typed at the **bottom** of the script. - `sellItems(playerItems, playerGold)` is typed before the end of the if humanoid then statement. --- title: "Cleanup and reset" url: /docs/en-us/education/battle-royale-series/cleanup-and-reset last_updated: 2026-06-29T19:33:59Z description: "Create a battle royale experience in Roblox Studio. Finish coding the scripts." --- # Cleanup and reset Time to code the last phase of the game: cleanup and reset. Code in this phase ensures that the game loops to intermission and that future matches start the same for each player. ## Update the GUI Before doing cleanup and reset, inform players how the game ended using the DisplayManager to show the appropriate status. ### Get the winner's name Start by getting the winning player's name if there was one. Previously, the code checked if the size of the active players table was down to 1. To get the remaining player's name, return the name at the first index of that table. 1. In PlayerManager, start a new module function named `getWinnerName()`.```lua function PlayerManager.getWinnerName() end -- Events Players.PlayerAdded:Connect(onPlayerJoin) ``` 2. Add an if statement that runs if something exists in `activePlayers[1]`. Although the table count was checked before, the player might have disconnected or left the game.```lua function PlayerManager.getWinnerName() local winningPlayer = activePlayers[1] if winningPlayer then end end ``` 3. In the if statement: - Return the player's name. - For the else, return an error string.```lua function PlayerManager.getWinnerName() local winningPlayer = activePlayers[1] if winningPlayer then return winningPlayer.Name else return "Error: No winning player found" end end ``` ### Get the end status Use a module function to take information from the correct end state, whether the timer ends or one player is left. Then, send that state variable to DisplayManager to update the status GUI with the appropriate message. 1. In **MatchManager**, code a new module function named `getEndStatus()` with a parameter named `endState`. To store the message that will be sent, add an empty variable named `statusToReturn`.```lua function MatchManager.prepareGame() playerManager.sendPlayersToMatch() matchStart:Fire() end function MatchManager.getEndStatus(endState) local statusToReturn end matchStart.Event:Connect(startTimer) matchEnd.Event:Connect(stopTimer) return MatchManager ``` 2. Set the value of `statusToReturn` using if and elseif statements. Check for the end state variables: `FoundWinner` and `TimerUp`. For error checking, include an else at the end.```lua function MatchManager.getEndStatus(endState) local statusToReturn if endState == gameSettings.endStates.FoundWinner then elseif endState == gameSettings.endStates.TimerUp then else end end ``` 3. Add the following for each condition:`FoundWinner` - A variable for the winner using `playerManager.getWinnerName()`. - Update `statusToReturn` with a string announcing the winner.`TimerUp` - Update `statusToReturn` with a string announcing time ran out.`Else` - Update `statusToReturn` with an error message in case there are issues with getting the end game message.```lua function MatchManager.getEndStatus(endState) local statusToReturn if endState == gameSettings.endStates.FoundWinner then local winnerName = playerManager.getWinnerName() statusToReturn = "Winner is : " .. winnerName elseif endState == gameSettings.endStates.TimerUp then statusToReturn = "Time ran out!" else statusToReturn = "Error found" end end ``` 4. Send back the message by typing `return statusToReturn`.```lua function MatchManager.getEndStatus(endState) local statusToReturn if endState == gameSettings.endStates.FoundWinner then local winnerName = playerManager.getWinnerName() statusToReturn = "Winner is : " .. winnerName elseif endState == gameSettings.endStates.TimerUp then statusToReturn = "Time ran out!" else statusToReturn = "Error found" end return statusToReturn end ``` ### Display and test Get the updated announcement in GameManager and display it to the players using the DisplayManager. 1. Open **GameManager**. In the while true do loop, **delete** the last print statement. Then, create a variable named endStatus. Set it equal to calling `matchManager.getEndStatus(endState)`.```lua while true do displayManager.updateStatus("Waiting for Players") repeat task.wait(gameSettings.intermissionDuration) until #Players:GetPlayers() >= gameSettings.minimumPlayers displayManager.updateStatus("Get ready!") task.wait(gameSettings.transitionTime) matchManager.prepareGame() local endState = matchEnd.Event:Wait() local endStatus = matchManager.getEndStatus(endState) end ``` 2. To display the returned message in the GUI label, call `displayManager.updateStatus()` and pass in `endStatus`.```lua local endStatus = matchManager.getEndStatus(endState) displayManager.updateStatus(endStatus) ``` 3. So the game pauses to let players see the message, add a wait using `transitionTime`.```lua local endStatus = matchManager.getEndStatus(endState) displayManager.updateStatus(endStatus) task.wait(gameSettings.transitionTime) ``` 4. Start a **test server** and check that players see the following messages for the time up and winning player conditions._Time is up condition__Winning player condition_ ### Troubleshooting tips At this point, you're unable to see the messages, try one of the following below. - If your end status message is "Error Found", none of the conditions were successful. Check the code in `MatchManager.getEndStatus()` against the code samples. - If the end status doesn't display, check that `task.wait(gameSettings.transitionTime)` is after sending the message to displayManager. ## Start new matches Before starting a new match, there will be a brief transition. This gives players time to see the end status and makes being teleported to the lobby feel less sudden. At the end of the transition, remaining players will be removed from the arena, and all of the code will reset. This ensures that players will start the next match with a clean version of the game. ### Handle transitions When players move into the transition state, remove their weapons. 1. In PlayerManager, find the local functions. Copy and paste the highlighted code for `removePlayerWeapon()` below. The code will remove an individual player's weapon if it's actively equipped or in the player's backpack.```lua local function removePlayerWeapon(whichPlayer) -- Check to see if a player exist in case they disconnected or left. if whichPlayer then local character = whichPlayer.Character -- If the player has it currently on their character local weapon = character:FindFirstChild("Weapon") if weapon then weapon:Destroy() end -- If the player has the weapon in their backpack local backpackWeapon = whichPlayer.Backpack:FindFirstChild("Weapon") if backpackWeapon then backpackWeapon:Destroy() end else print("No player to remove weapon") end end ``` 2. Start a new module function named `removeAllWeapons()`.```lua function PlayerManager.removeAllWeapons() end -- Events Players.PlayerAdded:Connect(onPlayerJoin) return PlayerManager ``` 3. In that function, use a for loop to go through the active players table. In the loop, call `removePlayerWeapon()` and pass in the player found.```lua function PlayerManager.removeAllWeapons() for playerKey, whichPlayer in activePlayers do removePlayerWeapon(whichPlayer) end end ``` ### Clean between matches Cleanup will be its own function in MatchManager. For now, cleanup will just use that previously created function to remove player weapons. As you expand the game, more can be added, such as functions to reset a map that changed during a match. 1. Open MatchManager. Add a new module function named `cleanupMatch()`. In that function, call `playerManager.removeAllWeapons()`.```lua function MatchManager.cleanupMatch() playerManager.removeAllWeapons() end matchStart.Event:Connect(startTimer) matchEnd.Event:Connect(stopTimer) return MatchManager ``` 2. Next, call the cleanup function. Open **GameManager** and find the while true do loop. So players have weapons removed during the ending intermission, call `matchManager.cleanupMatch()` before the last `Library.task.wait()`.```lua while true do displayManager.updateStatus("Waiting for Players") repeat task.wait(gameSettings.intermissionDuration) until #Players:GetPlayers() >= gameSettings.minimumPlayers displayManager.updateStatus("Get ready!") task.wait(gameSettings.transitionTime) matchManager.prepareGame() local endState = matchEnd.Event:Wait() local endStatus = matchManager.getEndStatus(endState) displayManager.updateStatus(endStatus) matchManager.cleanupMatch() task.wait(gameSettings.transitionTime) end ``` 3. Start a test server and run a match. Wait for the timer to run out and confirm that the player's weapon is removed during the end game intermission._During a match__After the match_ ### Reset matches You may have noticed a few other things in the game, like players still being in the arena after a match ends. With the match cleaned up, next reset the game. This includes sending players in the arena back into the lobby and clearing the active players table. With a reset in place, a game loop can run indefinitely. First, start a function to send players back to the lobby. 1. In PlayerManager: - Create a module function named `resetPlayers()`. - Add a for loop to iterate through activePlayers. - In the loop, call `respawnPlayerInLobby()` and pass in the player as the parameter.```lua function PlayerManager.resetPlayers() for playerKey, whichPlayer in activePlayers do respawnPlayerInLobby(whichPlayer) end end -- Events Players.PlayerAdded:Connect(onPlayerJoin) return PlayerManager ``` 2. Make sure the `activePlayers` table is empty for the next match by setting it equal to `{}`, which is a quick way of resetting to an empty table.```lua function PlayerManager.resetPlayers() for playerKey, whichPlayer in activePlayers do respawnPlayerInLobby(whichPlayer) end activePlayers = {} end ``` 3. Open MatchManager. Code a new module function named `resetMatch()` and call `playerManager.resetPlayers()`.```lua function MatchManager.resetMatch() playerManager.resetPlayers() end matchStart.Event:Connect(startTimer) matchEnd.Event:Connect(stopTimer) return MatchManager ``` 4. Go back to **GameManager**. At the end of the while true do loop, call `matchManager.resetMatch()`.```lua while true do displayManager.updateStatus("Waiting for Players") repeat task.wait(gameSettings.intermissionDuration) until #Players:GetPlayers() >= gameSettings.minimumPlayers displayManager.updateStatus("Get ready!") task.wait(gameSettings.transitionTime) matchManager.prepareGame() local endState = matchEnd.Event:Wait() local endStatus = matchManager.getEndStatus(endState) displayManager.updateStatus(endStatus) matchManager.cleanupMatch() task.wait(gameSettings.transitionTime) matchManager.resetMatch() end ``` 5. Start a test server and run a match. Confirm that you can at least go through two game loops without any errors. ## Completed scripts Below are completed scripts to double check your work. ### GameManager script ```lua -- Services local ServerStorage = game:GetService("ServerStorage") local Players = game:GetService("Players") -- Module Scripts local moduleScripts = ServerStorage:WaitForChild("ModuleScripts") local matchManager = require(moduleScripts:WaitForChild("MatchManager")) local gameSettings = require(moduleScripts:WaitForChild("GameSettings")) local displayManager = require(moduleScripts:WaitForChild("DisplayManager")) -- Events local events = ServerStorage:WaitForChild("Events") local matchEnd = events:WaitForChild("MatchEnd") while true do displayManager.updateStatus("Waiting for Players") repeat task.wait(gameSettings.intermissionDuration) until #Players:GetPlayers() >= gameSettings.minimumPlayers displayManager.updateStatus("Get ready!") task.wait(gameSettings.transitionTime) matchManager.prepareGame() local endState = matchEnd.Event:Wait() local endStatus = matchManager.getEndStatus(endState) displayManager.updateStatus(endStatus) matchManager.cleanupMatch() task.wait(gameSettings.transitionTime) matchManager.resetMatch() end ``` ### MatchManager script ```lua local MatchManager = {} -- Services local ServerStorage = game:GetService("ServerStorage") local ReplicatedStorage = game:GetService("ReplicatedStorage") -- Module Scripts local moduleScripts = ServerStorage:WaitForChild("ModuleScripts") local playerManager = require(moduleScripts:WaitForChild("PlayerManager")) local gameSettings = require(moduleScripts:WaitForChild("GameSettings")) local displayManager = require(moduleScripts:WaitForChild("DisplayManager")) local timer = require(moduleScripts:WaitForChild("Timer")) -- Events local events = ServerStorage:WaitForChild("Events") local matchStart = events:WaitForChild("MatchStart") local matchEnd = events:WaitForChild("MatchEnd") -- Values local displayValues = ReplicatedStorage:WaitForChild("DisplayValues") local timeLeft = displayValues:WaitForChild("TimeLeft") -- Creates a new timer object to be used to keep track of match time. local myTimer = timer.new() -- Local Functions local function stopTimer() myTimer:stop() end local function timeUp() matchEnd:Fire(gameSettings.endStates.TimerUp) end local function startTimer() myTimer:start(gameSettings.matchDuration) myTimer.finished:Connect(timeUp) while myTimer:isRunning() do -- Adding +1 makes sure the timer display ends at 1 instead of 0. timeLeft.Value = (myTimer:getTimeLeft() + 1) // 1 -- By not setting the time for wait, it offers more accurate looping task.wait() end end -- Module Functions function MatchManager.prepareGame() playerManager.sendPlayersToMatch() matchStart:Fire() end function MatchManager.getEndStatus(endState) local messageToReturn if endState == gameSettings.endStates.FoundWinner then local winnerName = playerManager.getWinnerName() messageToReturn = "Winner is : " .. winnerName elseif endState == gameSettings.endStates.TimerUp then messageToReturn = "Time ran out!" else messageToReturn = "Error found" end return messageToReturn end function MatchManager.cleanupMatch() playerManager.removeAllWeapons() end function MatchManager.resetMatch() playerManager.resetPlayers() end matchStart.Event:Connect(startTimer) matchEnd.Event:Connect(stopTimer) return MatchManager ``` ### PlayerManager script ```lua local PlayerManager = {} -- Services local Players = game:GetService("Players") local ServerStorage = game:GetService("ServerStorage") local ReplicatedStorage = game:GetService("ReplicatedStorage") -- Modules local moduleScripts = ServerStorage:WaitForChild("ModuleScripts") local gameSettings = require(moduleScripts:WaitForChild("GameSettings")) -- Events local events = ServerStorage:WaitForChild("Events") local matchEnd = events:WaitForChild("MatchEnd") -- Map Variables local lobbySpawn = workspace.Lobby.StartSpawn local arenaMap = workspace.Arena local spawnLocations = arenaMap.SpawnLocations -- Values local displayValues = ReplicatedStorage:WaitForChild("DisplayValues") local playersLeft = displayValues:WaitForChild("PlayersLeft") -- Player Variables local activePlayers = {} local playerWeapon = ServerStorage.Weapon local function checkPlayerCount() if #activePlayers == 1 then matchEnd:Fire(gameSettings.endStates.FoundWinner) print("Found winner") end end local function removeActivePlayer(player) print("removing player") for playerKey, whichPlayer in activePlayers do if whichPlayer == player then table.remove(activePlayers, playerKey) playersLeft.Value = #activePlayers checkPlayerCount() end end end local function respawnPlayerInLobby(player) player.RespawnLocation = lobbySpawn player:LoadCharacter() end local function preparePlayer(player, whichSpawn) player.RespawnLocation = whichSpawn player:LoadCharacter() local character = player.Character or player.CharacterAdded:Wait() -- Give the player a tool local sword = playerWeapon:Clone() sword.Parent = character local humanoid = character:WaitForChild("Humanoid") humanoid.Died:Connect(function() respawnPlayerInLobby(player) removeActivePlayer(player) end) end local function onPlayerJoin(player) player.RespawnLocation = lobbySpawn end local function removePlayerWeapon(whichPlayer) -- Check to see if a player exist in case they disconnected or left. if whichPlayer then local character = whichPlayer.Character -- If the player has it currently on their character local weapon = character:FindFirstChild("Weapon") if weapon then weapon:Destroy() end -- If the player has the weapon in their backpack local backpackWeapon = whichPlayer.Backpack:FindFirstChild("Weapon") if backpackWeapon then backpackWeapon:Destroy() end else print("No player to remove weapon") end end function PlayerManager.sendPlayersToMatch() local availableSpawnPoints = spawnLocations:GetChildren() for playerKey, whichPlayer in Players:GetPlayers() do table.insert(activePlayers,whichPlayer) -- Gets a spawn location and then removes it from the table so the next player gets the next spawn local spawnLocation = table.remove(availableSpawnPoints, 1) preparePlayer(whichPlayer, spawnLocation) end playersLeft.Value = #activePlayers end function PlayerManager.getWinnerName() local winningPlayer = activePlayers[1] if winningPlayer then return winningPlayer.Name else return "Error: No player found" end end function PlayerManager.removeAllWeapons() for playerKey, whichPlayer in activePlayers do removePlayerWeapon(whichPlayer) end end function PlayerManager.resetPlayers() for playerKey, whichPlayer in activePlayers do respawnPlayerInLobby(whichPlayer) end activePlayers = {} end -- Events Players.PlayerAdded:Connect(onPlayerJoin) return PlayerManager ``` --- title: "Code the game loop" url: /docs/en-us/education/battle-royale-series/coding-the-game-loop last_updated: 2026-06-29T19:33:59Z description: "Create a battle royale experience in Roblox Studio. Code a round manager for a game." --- # Code the game loop With the map created, it's time to start building out the scripts. The remainder of this course will heavily focus on scripting all different elements of the game loop. ## Set up the scripts The battle royale will use a combination of module scripts and normal scripts. Below are the scripts and their functions. | **GameManager** | Script. Runs functions from the Match Manager using variables from the Game Settings | | --- | --- | | **MatchManager** | Module Script. Runs functions like sending players into an arena or keeping track of time in a match. | | **GameSettings** | Module Script. Stores commonly used variables used by other scripts. | ### GameSettings script Create a module script named GameSettings to store variables used by other scripts, like match and intermission duration. These variables will be used by the GameManager script later. 1. In **ServerStorage**, create a folder named **ModuleScripts**. In that folder, create a new module script named **GameSettings**. 2. Open GameSettings and rename the module table to match the name of the script.```lua local GameSettings = {} return GameSettings ``` 3. In the module table, add variables for the following uses. Take your best guess for each value, you can always change it later as you test. - **Intermission Duration** - Seconds players wait before a match. - **Match Duration** - Length of a match in seconds. - **Minimum Players** - Smallest number of players needed to start. - **Transition Time** - Time before and after a match in seconds. Makes transitioning between parts of the game loop less sudden.```lua local GameSettings = {} -- Game Variables GameSettings.intermissionDuration = 5 GameSettings.matchDuration = 10 GameSettings.minimumPlayers = 2 GameSettings.transitionTime = 5 return GameSettings ``` ### MatchManager script The second script connected to the GameManager is the MatchManager. This script manages tasks like starting the timer or resetting players once the match ends. Within MatchManager is a function named `prepareGame()` starts the game by transitioning players into the match. 1. In ServerStorage > ModuleScripts > add a module script named MatchManager. Rename the module table.```lua local MatchManager = {} return MatchManager ``` 2. Add a new module function to MatchManager named `prepareGame()`. Include a print statement to test the script later.```lua local MatchManager = {} function MatchManager.prepareGame() print("Game starting!") end return MatchManager ``` ## Code the game loop The main game loop will be coded in the GameManager script using the variables just created. Remember, there are three phases in the game loop: intermission, competition, and cleanup & reset. ### GameManager script This script is a normal server script, so put it in ServerScriptService, rather than the module scripts folder. The actual game loop will be in a while true do loop. 1. In **ServerScriptService**, create a new script named **GameManager**. 2. Add a variable for the service "ServerStorage", which is where the Modulescripts are. Then add a variable for the service "Players", which will be needed to check player count during intermissions.```lua -- Services local ServerStorage = game:GetService("ServerStorage") local Players = game:GetService("Players") ``` 3. To use the previously created modules: - Set a variable named `moduleScripts` to the location of the ModuleScripts folder. - Add variables named `matchManager` and `gameSettings`. Set each variable to require their respective script.```lua -- Services local ServerStorage = game:GetService("ServerStorage") local Players = game:GetService("Players") -- Module Scripts local moduleScripts = ServerStorage:WaitForChild("ModuleScripts") local matchManager = require(moduleScripts:WaitForChild("MatchManager")) local gameSettings = require(moduleScripts:WaitForChild("GameSettings")) ``` 4. After the variables, add a `while true do` loop. All phases of the game loop will go inside to repeat indefinitely.```lua -- Module Scripts local moduleScripts = ServerStorage:WaitForChild("ModuleScripts") local matchManager = require(moduleScripts:WaitForChild("MatchManager")) local gameSettings = require(moduleScripts:WaitForChild("GameSettings")) -- Main game loop while true do end ``` ### Code the intermission While the game loop runs indefinitely, the intermission should pause the loop and only continue when there are enough players for a match. To code this pause, include a nested repeat loop for the intermission in the while loop. That nested loop will repeat until there are enough players, pausing the main loop. Once there are enough players, it'll exit and transition players into a match. With a **repeat loop**, the code in the loop will run at least once. Unlike a while loop, it doesn't check it's condition until the loop ends. This ensures players always go to the lobby before a match. 1. In the `while true do` loop, type `repeat` and press `Enter` to autocomplete with the keyword `until`.```lua while true do repeat until end ``` 2. Check if the current number of players `(#Players:GetPlayers())` is greater or equal to the `minimumPlayers` variable created earlier in the GameSettings module.```lua while true do repeat until #Players:GetPlayers() >= gameSettings.minimumPlayers end ``` 3. In the repeat loop, add a print statement saying the intermission is starting. Use `Library.task.wait()` to pause for the intermission using `intermissionDuration` from GameSettings.```lua while true do repeat print("Starting intermission") task.wait(gameSettings.intermissionDuration) until #Players:GetPlayers() >= gameSettings.minimumPlayers end ``` 4. Playtest and check that the print statement `"Starting intermission"` is shown at least twice. Seeing the message twice proves the repeat loop didn't find enough players and ran again. You'll have to wait the length of intermission before seeing the message a second time. ### Troubleshooting tips At this point, if you're not spawning as intended, try one of the following below. - `Library.task.wait()` should be inside the repeat loop. Without the wait, the script will run too many times in a second, overloading Roblox Studio and causing an error. - In the Game Settings module, the variable `intermissionDuration` should be greater than 1. If lower, the script can repeat too often, causing slow down issues. ### End the intermission Once there are enough players, have them wait a short transition time. Then, send them into the match by calling the `prepareGame()` function in MatchManager. Remember, that function just prints a line, but you'll add more code later. 1. At the end of the repeat loop, add a print statement saying the intermission is over to test your code. Then, follow it with a `Library.task.wait()` using GameSetting's `transitionTime` variable.```lua while true do repeat print("Starting intermission") task.wait(gameSettings.intermissionDuration) until #Players:GetPlayers() >= gameSettings.minimumPlayers print("Intermission over") task.wait(gameSettings.transitionTime) end ``` > **Warning:** Keep the code within the scope of the while loop (between `do` and `end`). If code is outside, parts of your game loop might not repeat and players will just be stuck in the intermission phase. 2. After the wait, call the `prepareGame()` from the MatchManager module. When the code runs, this will just print text to the Output window. Wait until the next section to test this code.```lua while true do repeat print("Starting intermission") task.wait(gameSettings.intermissionDuration) until #Players:GetPlayers() >= gameSettings.minimumPlayers print("Intermission over") task.wait(gameSettings.transitionTime) matchManager.prepareGame() end ``` > **Warning:** If you add onto the script after this project, keep in mind code below the while true do loop **won't** run. Keep relevant code inside the while loop or call module functions from the main loop. ## Test multiplayer games Right now, to have the code run `prepareGame()`, it needs to exit the repeat loop. But, to do that, there needs to be more than one player. This means if you use the playtest button, the function will never run because you're the only player in the game (unless your minimum players is one). To test this out, you'll need to simulate a multiplayer game. ### Start a local server To test code requiring more than one player, create a local server. While published games are normally on Roblox servers, a **local server** simulates a multiplayer game on your computer with simulated players. 1. To start a local server, navigate to the **Test** menu's **Start Test Session** ⟩ **Server and Clients**. This lesson uses 2 players. 2. Wait a few seconds for the server to set up. Multiple windows will open in addition to your original Studio window. You may need to allow access to Roblox Studio from firewalls or other online security software. ### Troubleshooting tips At this point, you're unable to see the test servers, try one of the following below. - If you have any issues with the server starting, double check the article [Firewall and Router Issues](https://en.help.roblox.com/hc/en-us/articles/203312840-Firewall-and-Router-Issues). - Set the number of players to a small amount, like 2 or 3. - If the issue doesn't resolve, try restarting Studio or restarting your computer. ### Test in the local server You'll see multiple windows when the server starts. Each one represents a different part of the server/client relationship. - **Server** (green border) runs the game. - **Client** (blue borders) simulates a player's experience. _Server with green border_ _Client with blue border_ With the server up, you can check if the code worked. 1. Find the **Server** window with the green border. Check for the print statement called from the MatchManager script. Because there is a repeat loop, you'll see the same print statements repeating. 2. Once you're done testing, click the **End Session** button in any window to close all server and client windows and return to your normal Studio window. ### Troubleshooting tips At this point, if the intended print statements didn't appear, try one of the following below. - Check that functions like `prepareGame()` are in scope of the while true do loop. - If the print from MatchManager didn't work, check some common troubleshooting with module scripts, like making sure that the MatchManager script is required in GameManager or that `prepareGame()` is added to that module's table. ## Completed scripts Below are completed scripts to double check your work. ### GameManager script ```lua -- Services local ServerStorage = game:GetService("ServerStorage") local Players = game:GetService("Players") -- Module Scripts local moduleScripts = ServerStorage:WaitForChild("ModuleScripts") local matchManager = require(moduleScripts:WaitForChild("MatchManager")) local gameSettings = require(moduleScripts:WaitForChild("GameSettings")) -- Main game loop while true do repeat print("Starting intermission") task.wait(gameSettings.intermissionDuration) until #Players:GetPlayers() >= gameSettings.minimumPlayers print("Intermission over") task.wait(gameSettings.transitionTime) matchManager.prepareGame() end ``` ### MatchManager script ```lua local MatchManager = {} function MatchManager.prepareGame() print("Game starting!") end return MatchManager ``` ### GameSettings script ```lua local GameSettings = {} -- Game Variables GameSettings.intermissionDuration = 5 GameSettings.roundDuration = 10 GameSettings.minimumPlayers = 2 GameSettings.transitionTime = 5 return GameSettings ``` --- title: "Create a GUI" url: /docs/en-us/education/battle-royale-series/creating-a-gui last_updated: 2026-06-29T19:33:59Z description: "Create a battle royale experience in Roblox Studio. Create and code a GUI that displays a game status during a match." --- # Create a GUI Right now, much of the game information is currently in the Output window, invisible to players. So players can be informed of what's happening in the game, you'll create a graphical user interface (GUI) and code it. ## Display information with a GUI For this game a text label will display the current game status as well as the remaining player count and time. _During Intermission_ _During a Match_ ### Set up the GUI First, create a **Screen GUI** object to hold the different text elements. When the player moves the camera, the screen GUI stays in the same place on their screen. To ensure all players see the same display, place the GUI in the **StarterGUI** folder. At game startup, this folder is copied to all players. 1. In the StarterGUI folder, create a new ScreenGUI. Then in ScreenGUI, add a new TextLabel named StatusText. 2. To move the label, in the Explorer, select StatusText. Then, in the game view, drag the label where you would like it. Your numbers may differ from the video. The label can also be resized using the anchor points on the corners. ## Script the GUI To reflect changes in the game, scripts will need to update the GUI elements. For instance, the game status, whether it's an intermission or active round, will be stored in a StringValue and updated using local scripts. > **Info:** Compared to scripts and module scripts which run Roblox servers, local scripts run on a player's device. They're often used for coding GUI elements like the timer or player input, like mouse or keyboard actions. ### Set up the script The StatusDisplay script will be used to update the player's GUI whenever the game state changes. 1. In **ReplicatedStorage**, create a folder named DisplayValues. In that folder, add a StringValue named Status. To test the value later, give it a temporary value, like "Welcome to the Battle!". > **Info:** Because local scripts only run on a player's device, they can't be stored in Server folders like ServerStorage. ReplicatedStorage is a folder that's available both to the client (device) and server. 2. In StarterGUI > ScreenGUI > Status, add a new local script named StatusDisplay. Scripts that affect the GUI are often parented to that GUI element. 3. Open StatusDisplay and define the following variables for the follow: - ReplicatedStorage service - DisplayValues folder - Status StringValue - TextLabel - use `script.Parent`.```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local displayValues = ReplicatedStorage:WaitForChild("DisplayValues") local status = displayValues:WaitForChild("Status") local textLabel = script.Parent ``` ### Change the text label To change the text in the label, use a Changed event so whenever the Status string is changed by another script, the text label will be updated. 1. Code a new function named `updateText()`. In that function, set the Text property of `textLabel` to `status.Value`.```lua local textLabel = script.Parent local function updateText() textLabel.Text = status.Value end ``` 2. Connect the function to the Changed event.```lua local function updateText() textLabel.Text = status.Value end status.Changed:Connect(updateText) ``` 3. So players see the most up to date status when starting the game, run `updateText()` at the end of the script.```lua local function updateText() textLabel.Text = status.Value end status.Changed:Connect(updateText) updateText() ``` 4. Run the game and confirm that you see the temporary value in the display. ## Create the display manager During a game, the text label will need to get information from GameManager, MatchManager, and possibly other scripts. So these different scripts can update the text label when needed, create a module script named DisplayManager. ### Set up the script Because DisplayManager needs to communicate with other scripts, it'll be a module script. 1. In **ServerStorage** > **ModuleScripts**, create a new module script named DisplayManager. Rename the module table to match the script name. 2. Add local variables for the following: Replicated Storage, DisplayValues folder, Status.```lua local DisplayManager = {} -- Services local ReplicatedStorage = game:GetService("ReplicatedStorage") -- Display Values used to update Player GUI local displayValues = ReplicatedStorage:WaitForChild("DisplayValues") local status = displayValues:WaitForChild("Status") -- Local Functions -- Module Functions return DisplayManager ``` 3. Create a new module function named `updateStatus()` that updates the string in the Status value. Other scripts will be able to call this function.```lua -- Local Functions -- Module Functions function DisplayManager.updateStatus(newStatus) status.Value = newStatus end ``` ### Update the text status With the Display Manager set up, it can be used in other scripts to update the GUI text label. As the first message in the GUI, show the start and end of the intermission through the GameManager script. 1. In **ServerScriptService** > GameManager, create a variable named `displayManager` and require the DisplayManager module in ServerStorage.```lua -- Services local ReplicatedStorage = game:GetService("ReplicatedStorage") local ServerStorage = game:GetService("ServerStorage") local Players = game:GetService("Players") -- Module Scripts local moduleScripts = ServerStorage:WaitForChild("ModuleScripts") local roundManager = require(moduleScripts:WaitForChild("RoundManager")) local gameSettings = require(moduleScripts:WaitForChild("GameSettings")) local displayManager = require(moduleScripts:WaitForChild("DisplayManager")) ``` 2. As the **first** line after the while true do statement, call displayManager > `updateStatus()` and pass in a message about waiting for players.```lua -- Events local events = ServerStorage:WaitForChild("Events") local matchEnd = events:WaitForChild("MatchEnd") while true do displayManager.updateStatus("Waiting for Players") repeat print("Starting intermission") task.wait(gameSettings.intermissionDuration) until #Players:GetPlayers() >= gameSettings.minimumPlayers task.wait(gameSettings.transitionTime) matchManager.prepareGame() matchEnd.Event:Wait() end ``` 3. After the end of the repeat loop for the intermission, call `updateStatus()` and pass in a string announcing the match is starting. Since you'll be testing with the GUI, delete the two print statements for noting the start and end of the intermission.```lua while true do displayManager.updateStatus("Waiting for Players") repeat task.wait(gameSettings.intermissionDuration) until #Players:GetPlayers() >= gameSettings.minimumPlayers displayManager.updateStatus("Get ready!") task.wait(gameSettings.transitionTime) matchManager.prepareGame() matchEnd.Event:Wait() end ``` 4. Test the game **with** and **without** your minimum players. The message should read the following: - Without the minimum players: `"Waiting for Players"`. - With the minimum players: `"Get ready"`. ### Troubleshooting tips At this point, if the text label doesn't display the first message, or still displays "Label", try one of the following below. - Make sure in the StatusDisplay local script that `updateText()` is called at the bottom of the script. This ensures that the player gets the most up to date message. - Check that the Status StringValue is in ReplicatedStorage. Due to the unique nature of client-server relations, if it's in ServerStorage, a local script won't be able to find it. ## Display match status During a match, the GUI will display two numbers: remaining player count and time. As these numbers change, the text label will change as well. ## Set up values and functions IntValues will be used to store the player count and time left. 1. In ReplicatedStorage > DisplayValues, create two IntValues named PlayersLeft and TimeLeft. 2. In DisplayManager, add variables to store the players left and time left values.```lua local DisplayManager = {} -- Services local ReplicatedStorage = game:GetService("ReplicatedStorage") -- Display Values used to update Player GUI local displayValues = ReplicatedStorage:WaitForChild("DisplayValues") local status = displayValues:WaitForChild("Status") local playersLeft = displayValues:WaitForChild("PlayersLeft") local timeLeft = displayValues:WaitForChild("TimeLeft") ``` 3. Create a local function named `updateMatchStatus()`. Then, set the value of status to display the number of players left and the remaining time.```lua local displayValues = ReplicatedStorage:WaitForChild("DisplayValues") local status = displayValues:WaitForChild("Status") local playersLeft = displayValues:WaitForChild("PlayersLeft") local timeLeft = displayValues:WaitForChild("TimeLeft") -- Local Functions local function updateRoundStatus() status.Value = "Players Left: " .. playersLeft.Value .. " / Time Left: " .. timeLeft.Value end ``` 4. For **both** IntValue variables, connect `updateRoundStatus()` to the Changed event.```lua -- Module Functions function DisplayManager.updateStatus(newStatus) status.Value = newStatus end playersLeft.Changed:Connect(updateRoundStatus) timeLeft.Changed:Connect(updateRoundStatus) return DisplayManager ``` > **Warning:** Don't test yet. Nothing has updated the PlayersLeft or TimeLeft values, so the status won't be changed once a round starts. ### Display players Next, add the code for displaying the number of players at the start of a game. Later lessons will update the PlayersLeft value as players are eliminated from the game. 1. In PlayerManager, add local variables for ReplicatedStorage service, DisplayValues folder and PlayersLeft IntValue.```lua local PlayerManager = {} -- Services local Players = game:GetService("Players") local ServerStorage = game:GetService("ServerStorage") local ReplicatedStorage = game:GetService("ReplicatedStorage") -- Map Variables local lobbySpawn = workspace.Lobby.StartSpawn local arenaMap = workspace.Arena local spawnLocations = arenaMap.SpawnLocations -- Values local displayValues = ReplicatedStorage:WaitForChild("DisplayValues") local playersLeft = displayValues:WaitForChild("PlayersLeft") ``` 2. Show the starting player count by setting the value of `playersLeft` to the size of the active players array. Then, in `sendPlayersToMatch()`, under the for loop, type: `playersLeft.Value = #activePlayers````lua function PlayerManager.sendPlayersToMatch() local availableSpawnPoints = spawnLocations:GetChildren() for playerKey, whichPlayer in Players:GetPlayers() do table.insert(activePlayers, whichPlayer) local spawnLocation = table.remove(availableSpawnPoints, 1) preparePlayer(whichPlayer, spawnLocation) end playersLeft.Value = #activePlayers end ``` ### Display the timer Remember that module scripts are used to centralize similar code. Since the timer is tracked in MatchManager, update the TimeLeft value using functions from the Timer script. The display manager will listen for changes to the TimeLeft, and update to match the new value. 1. In MatchManager, create variables to store the **ReplicatedStorage** service, DisplayValues folder and TimeLeft value.```lua local MatchManager = {} -- Services local ServerStorage = game:GetService("ServerStorage") local ReplicatedStorage = game:GetService("ReplicatedStorage") -- Module Scripts local moduleScripts = ServerStorage:WaitForChild("ModuleScripts") local playerManager = require(moduleScripts:WaitForChild("PlayerManager")) local gameSettings = require(moduleScripts:WaitForChild("GameSettings")) local timer = require(moduleScripts:WaitForChild("Timer")) -- Events local events = ServerStorage:WaitForChild("Events") local matchStart = events:WaitForChild("MatchStart") -- Values local displayValues = ReplicatedStorage:WaitForChild("DisplayValues") local timeLeft = displayValues:WaitForChild("TimeLeft") local myTimer = timer.new() ``` 2. Find the `startTimer()` function. **After** the timer's `Finished` event, copy and paste the whole, highlighted while loop below. The code runs a loop to update the `timeLeft` value as long as the timer is still active.```lua while myTimer:isRunning() do -- Adding +1 makes sure the timer display ends at 1 instead of 0. timeLeft.Value = (myTimer:getTimeLeft() + 1) // 1 -- By not setting the time for wait, it offers more accurate looping task.wait() end ``` When added in, the code should look like the sample below.```lua local function startTimer() print("Timer started") myTimer:start(gameSettings.matchDuration) myTimer.finished:Connect(timeUp) while myTimer:isRunning() do -- Adding +1 makes sure the timer display ends at 1 instead of 0. timeLeft.Value = (myTimer:getTimeLeft() + 1) // 1 -- By not setting the time for wait, it offers more accurate looping task.wait() end end ``` 3. Run the game with the minimum players. Check that the status text displays: - The correct amount of starting players. Remember, this number won't change until additional code is added in a future lesson. - Time decreases each second until it stops at 1. ## Completed scripts Below are completed scripts to double check your work. ### GameManager script ```lua -- Services local ServerStorage = game:GetService("ServerStorage") local Players = game:GetService("Players") -- Module Scripts local moduleScripts = ServerStorage:WaitForChild("ModuleScripts") local matchManager = require(moduleScripts:WaitForChild("MatchManager")) local gameSettings = require(moduleScripts:WaitForChild("GameSettings")) local displayManager = require(moduleScripts:WaitForChild("DisplayManager")) -- Events local events = ServerStorage:WaitForChild("Events") local matchEnd = events:WaitForChild("MatchEnd") while true do displayManager.updateStatus("Waiting for Players") repeat task.wait(gameSettings.intermissionDuration) until #Players:GetPlayers() >= gameSettings.minimumPlayers displayManager.updateStatus("Get ready!") task.wait(gameSettings.transitionTime) matchManager.prepareGame() matchEnd.Event:Wait() end ``` ### DisplayManager script ```lua local DisplayManager = {} -- Services local ReplicatedStorage = game:GetService("ReplicatedStorage") -- Display Values used to update Player GUI local displayValues = ReplicatedStorage:WaitForChild("DisplayValues") local status = displayValues:WaitForChild("Status") local playersLeft = displayValues:WaitForChild("PlayersLeft") local timeLeft = displayValues:WaitForChild("TimeLeft") -- Local Functions local function updateRoundStatus() status.Value = "Players Left: " .. playersLeft.Value .. " / Time Left: " .. timeLeft.Value end -- Module Functions function DisplayManager.updateStatus(newStatus) status.Value = newStatus end playersLeft.Changed:Connect(updateRoundStatus) timeLeft.Changed:Connect(updateRoundStatus) return DisplayManager ``` ### MatchManager script ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") -- Module Scripts local moduleScripts = ServerStorage:WaitForChild("ModuleScripts") local playerManager = require(moduleScripts:WaitForChild("PlayerManager")) local gameSettings = require(moduleScripts:WaitForChild("GameSettings")) local timer = require(moduleScripts:WaitForChild("Timer")) -- Events local events = ServerStorage:WaitForChild("Events") local matchStart = events:WaitForChild("MatchStart") local matchEnd = events:WaitForChild("MatchEnd") -- Values local displayValues = ReplicatedStorage:WaitForChild("DisplayValues") local timeLeft = displayValues:WaitForChild("TimeLeft") local myTimer = timer.new() -- Local Functions local function timeUp() print("Time is up!") end local function startTimer() print("Timer started") myTimer:start(gameSettings.matchDuration) myTimer.finished:Connect(timeUp) while myTimer:isRunning() do -- Adding +1 makes sure the timer display ends at 1 instead of 0. timeLeft.Value = (myTimer:getTimeLeft() + 1) // 1 -- By not setting the time for wait, it offers more accurate looping task.wait() end end -- Module Functions function MatchManager.prepareGame() playerManager.sendPlayersToMatch() matchStart:Fire() end matchStart.Event:Connect(startTimer) return MatchManager ``` ### StatusDisplay script ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local displayValues = ReplicatedStorage:WaitForChild("DisplayValues") local status = displayValues:WaitForChild("Status") local textLabel = script.Parent local function updateText() textLabel.Text = status.Value end status.Changed:Connect(updateText) updateText() ``` --- title: "End matches" url: /docs/en-us/education/battle-royale-series/ending-matches last_updated: 2026-06-29T19:33:59Z description: "Create a battle royale experience in Roblox Studio. Write scripts that handle victory conditions or time running out." --- # End matches Matches can end in a few conditions including timers running out or a single player being left. ## Manage defeated players Right now, defeated players respawn in the arena. Instead, send them back to the lobby to wait for the next match. 1. In PlayerManager, create a local function named `respawnPlayerInLobby()`. In that function, do the following: - Set the player's RespawnLocation property to the lobbySpawn. - Reload the character with `Class.Player:LoadCharacter()`. Remember, `Class.Player:LoadCharacter()|LoadCharacter()` recreates the player at their spawn, removing any tools they had. ## Use anonymous functions Anonymous functions are commonly used in advanced scripts under specific situations. Before defining them, it's important to understand a situation your script may encounter. Consider this: To teleport defeated players to the lobby, the respawn function will have to be connected to the player's Died event. There is a problem though. If you connect the event as we have in the past, there's no way to get the name of the player from the Died event. The script needs that name to remove a defeated player from the table tracking active players. To get the name of the player that triggered the Died event, use an anonymous function. **Anonymous functions** don't have names, and can be created directly within `Connect()` rather than separately. A sample anonymous function is below that shows the syntax. ```lua myPlayer.Died:Connect(function() print(player) end) ``` ### Code the died event For this function, whenever a player's character dies, they'll trigger the function that respawns them. 1. To get access to the player's Died event, in PlayerManager > `preparePlayer()`, add a variable for the player's humanoid.```lua local function preparePlayer(player, whichSpawn) player.RespawnLocation = whichSpawn player:LoadCharacter() local character = player.Character or player.CharacterAdded:Wait() local sword = playerWeapon:Clone() sword.Parent = character local humanoid = character:WaitForChild("Humanoid") end ``` 2. Create an anonymous function that connects to the Died event. Start by typing `humanoid.Died:Connect()`. Then, **inside** `Connect()`, type `function()`, press `Enter` to autocomplete `end`, and delete the extra parenthesis.```lua local humanoid = character:WaitForChild("Humanoid") humanoid.Died:Connect(function() end) ``` 3. In the anonymous function, call `respawnPlayerInLobby()`. Pass in `player`, a variable storing the player getting ready to enter a match.```lua local humanoid = character:WaitForChild("Humanoid") humanoid.Died:Connect(function() respawnPlayerInLobby(player) end) ``` 4. Start a **server** and play a match. Test that when a player dies, they respawn in the lobby. To defeat players, walk up to them and use your weapon until you can confirm they've respawned in the lobby._Attacking Player2__Player2 in Lobby after respawning_ Note, there are also some different ways of testing if desired. - To kill a player, in the Server > Output window > Command Bar, copy and paste: `workspace.Player1.Humanoid.Health = 0`. Press `Enter` to run the command. To remove other players, use Player2, Player3, etc. - Setting GameSettings > `matchDuration` to a longer time can give you more time to find and take out all players. - To have quicker tests, change GameSettings > `minimumPlayers` to smaller number, like 2. ## End the game Now that defeated players respawn, start working on ending the game. Remember creating the MatchEnd event? It'll be fired when either the timer runs out or a winner is found. To tell other scripts which condition ended the game, create a table with variables for `TimerUp` and `FoundWinner`. When the match end event fires, it'll pass in a variable so other scripts can respond. 1. In GameSettings, create an empty module table named `endStates`.```lua local GameSettings = {} -- Game Variables GameSettings.intermissionDuration = 5 GameSettings.matchDuration = 10 GameSettings.minimumPlayers = 2 GameSettings.transitionTime = 5 -- Possible ways that the game can end. GameSettings.endStates = { } return GameSettings ``` 2. Create two variables named `TimerUp` and `FoundWinner`. Set each to a string matching their name. These strings will be used to test the code. ```lua GameSettings.endStates = { TimerUp = "TimerUp", FoundWinner = "FoundWinner" } ``` ### End with a timer When the timer ends, fire the Match End event and send the matching end state variable. That way, other scripts listening for that event can respond accordingly. Remember that while events fire signals, that can also send data that's received by listening scripts, like `TimerUp` or `FoundWinner`. 1. In **GameManager**, in the while true do loop, find the line `matchEnd.Event:Wait()`. At the start of the line, add `local endState =`.```lua while true do displayManager.updateStatus("Waiting for Players") repeat task.wait(gameSettings.intermissionDuration) until #Players:GetPlayers() >= gameSettings.minimumPlayers displayManager.updateStatus("Get ready!") task.wait(gameSettings.transitionTime) matchManager.prepareGame() local endState = matchEnd.Event:Wait() end ``` 2. To confirm that the correct state was received, add a print statement including `endState`.```lua while true do displayManager.updateStatus("Waiting for Players") repeat task.wait(gameSettings.intermissionDuration) until #Players:GetPlayers() >= gameSettings.minimumPlayers displayManager.updateStatus("Get ready!") task.wait(gameSettings.transitionTime) matchManager.prepareGame() local endState = matchEnd.Event:Wait() print("Game ended with: " .. endState) end ``` 3. In MatchManager, find `timeUp()` and **remove** the print statement. Then, to fire the match end event, type `matchEnd:Fire()` and pass in `gameSettings.endStates.TimerUp`.```lua -- Values local displayValues = ReplicatedStorage:WaitForChild("DisplayValues") local timeLeft = displayValues:WaitForChild("TimeLeft") -- Creates a new timer object to be used to keep track of match time. local myTimer = timer.new() -- Local Functions local function timeUp() matchEnd:Fire(gameSettings.endStates.TimerUp) end ``` 4. Test a match. Once the timer runs out, check that the print statement includes the string stored in the `TimerUp` variable. ### Troubleshooting tips At this point, the message wasn't displayed, try one of the following below. - Check that wherever the end state variables are called, that they're written exactly, like here: `gameSettings.endStates.TimerUp`. - Make sure to use : (colon operator) with the `Class.Fire|Fire()` **instead** of the dot operator, like in `matchEnd:Fire()`. ## Code a winning match Next, the script needs to identify winning players, remove losing players, and run cleanup such as stopping the timer. Matches will also end if one player is left. To see if the `FoundWinner` condition is met, you'll need a function that checks the number left in the table tracking players in a match. ### Check player count Matches will end if there's only one player left. To check this, the script needs to track the players in a round. Once one player is left, a winner can be assigned. 1. In PlayerManager, define the following variables: - ModuleScripts folder - GameSettings module - Used to access end state variables - Events folder and the MatchEnd event - Fires event```lua local PlayerManager = {} -- Services local Players = game:GetService("Players") local ServerStorage = game:GetService("ServerStorage") local ReplicatedStorage = game:GetService("ReplicatedStorage") -- Modules local moduleScripts = ServerStorage:WaitForChild("ModuleScripts") local gameSettings = require(moduleScripts:WaitForChild("GameSettings")) -- Events local events = ServerStorage:WaitForChild("Events") local matchEnd = events:WaitForChild("MatchEnd") ``` 2. Above `respawnPlayerInLobby()`, add a new local function named `checkPlayerCount()`.```lua -- Player Variables local activePlayers = {} local playerWeapon = ServerStorage.Weapon local function checkPlayerCount() end local function respawnPlayerInLobby(player) ``` > **Warning:** Remember, to be in scope, this function needs to come **before** the functions that will use it later in the script. 3. Within that function, use an if then statement to check for a winner. In that statement: - Check if the size of the `activePlayers` table is 1. - If so, fire `matchEnd` and pass in `gameSettings.endStates.FoundWinner`.```lua local function checkPlayerCount() if #activePlayers == 1 then matchEnd:Fire(gameSettings.endStates.FoundWinner) end end ``` ### Remove a player An accurate count can't be kept unless players are removed. When a player is defeated, keep an accurate player count by removing them from the player table. Then, check the size of the active player table to see if there's a winner. 1. Under `checkPlayerCount()`, create a new local function named `removeActivePlayer()` with a parameter named `player`.```lua local function checkPlayerCount() if #activePlayers == 1 then matchEnd:Fire(gameSettings.endStates.FoundWinner) end end local function removeActivePlayer(player) end ``` 2. To find the player in the `activePlayers` table, iterate through it using a `for` loop. Then, add an `if` statement that runs if a player matching the name passed into the function is found.```lua local function removeActivePlayer(player) for playerKey, whichPlayer in activePlayers do if whichPlayer == player then end end end ``` > **Warning:** Keep similar variable names **different**. As you code, may sure you don't have overlapping variable names, like the above function which includes two versions of player: `player` and `whichPlayer`. > > For instance, adding which to a variable name in an iterating table is a good convention to avoid confusion, such as `whichPlayer` or `whichPart`. 3. To remove the player, in the if statement: - Call `Library.table.remove()`. Inside the parentheses, pass in `activePlayers`, the table to look in, and `playerKey` - the player to remove from the table. - Set the value of the `playersLeft` object to `#activePlayers`. - Check for a winning player by running `checkPlayerCount()`.```lua local function removeActivePlayer(player) for playerKey, whichPlayer in activePlayers do if whichPlayer == player then table.remove(activePlayers, playerKey) playersLeft.Value = #activePlayers checkPlayerCount() end end end ``` ### Connect events and test To use the function just created, call it from within the anonymous function connected to the player's `Died` event. 1. Find `preparePlayer()`. In the anonymous function with the Died event connection, call `removeActivePlayer()`. Then, pass in the player as the parameter.```lua humanoid.Died:Connect(function() respawnPlayerInLobby(player) removeActivePlayer(player) end) ``` 2. To see if a winning player is found, start a **test server**. When there is only one player left you should see FoundWinner in the Output window. 3. Continue testing and let the match end. Notice as a new match starts, an error appears in the Output window: This error is because the timer wasn't stopped, which will be fixed in the next section. ### Stop the timer Whenever a match ends with a winning player, the timer should stop as well. To halt the timer before time is up, have the timer stop whenever the match end event fires. This is one of the benefits of creating events. They can be reused in multiple situations to script cause and effect relationships. 1. In MatchManager, create a new local function named `stopTimer()`. Inside, type `myTimer:stop()` to halt the timer.```lua -- Creates a new timer object to be used to keep track of match time. local myTimer = timer.new() -- Local Functions local function stopTimer() myTimer:stop() end local function timeUp() matchEnd:Fire(gameSettings.endStates.TimerUp) end ``` 2. To stop the timer when the match ends, connect the matchEnd event to `stopTimer()`.```lua -- Module Functions function MatchManager.prepareGame() playerManager.sendPlayersToMatch() matchStart:Fire() end matchStart.Event:Connect(startTimer) matchEnd.Event:Connect(stopTimer) return MatchManager ``` 3. **Test** that the previous error no longer appears by starting a **server**. Eliminate all but one player and then wait a few seconds once the match ends. ## Next steps While the two win conditions are finished, there are still some tasks left to finish the game loop. For instance, the winning player isn't ever teleported to the lobby. In the next lesson, you'll display how the match ended to players and reset the game, finally completing the whole loop. ## Completed scripts Below are completed scripts to double check your work. ### PlayerManager script ```lua local PlayerManager = {} -- Services local Players = game:GetService("Players") local ServerStorage = game:GetService("ServerStorage") local ReplicatedStorage = game:GetService("ReplicatedStorage") -- Modules local moduleScripts = ServerStorage:WaitForChild("ModuleScripts") local gameSettings = require(moduleScripts:WaitForChild("GameSettings")) -- Events local events = ServerStorage:WaitForChild("Events") local matchEnd = events:WaitForChild("MatchEnd") -- Map Variables local lobbySpawn = workspace.Lobby.StartSpawn local arenaMap = workspace.Arena local spawnLocations = arenaMap.SpawnLocations -- Values local displayValues = ReplicatedStorage:WaitForChild("DisplayValues") local playersLeft = displayValues:WaitForChild("PlayersLeft") -- Player Variables local activePlayers = {} local playerWeapon = ServerStorage.Weapon -- Local Functions local function checkPlayerCount() if #activePlayers == 1 then matchEnd:Fire(gameSettings.endStates.FoundWinner) end end local function removeActivePlayer(player) for playerKey, whichPlayer in activePlayers do if whichPlayer == player then table.remove(activePlayers, playerKey) playersLeft.Value = #activePlayers checkPlayerCount() end end end local function respawnPlayerInLobby(player) player.RespawnLocation = lobbySpawn player:LoadCharacter() end local function onPlayerJoin(player) player.RespawnLocation = lobbySpawn end local function preparePlayer(player, whichSpawn) player.RespawnLocation = whichSpawn player:LoadCharacter() local character = player.Character or player.CharacterAdded:Wait() local sword = playerWeapon:Clone() sword.Parent = character local humanoid = character:WaitForChild("Humanoid") humanoid.Died:Connect(function() respawnPlayerInLobby(player) removeActivePlayer(player) end) end -- Module Functions function PlayerManager.sendPlayersToMatch() local arenaSpawns = spawnLocations:GetChildren() for playerKey, whichPlayer in Players:GetPlayers() do table.insert(activePlayers, whichPlayer) local spawnLocation = table.remove(arenaSpawns, 1) preparePlayer(whichPlayer, spawnLocation) end playersLeft.Value = #activePlayers end -- Events Players.PlayerAdded:Connect(onPlayerJoin) return PlayerManager ``` ### GameSettings script ```lua local GameSettings = {} -- Game Variables GameSettings.intermissionDuration = 5 GameSettings.matchDuration = 10 GameSettings.minimumPlayers = 2 GameSettings.transitionTime = 5 -- Possible ways that the game can end. GameSettings.endStates = { TimerUp = "TimerUp", FoundWinner = "FoundWinner" } return GameSettings ``` ### GameManager script ```lua -- Services local ServerStorage = game:GetService("ServerStorage") local Players = game:GetService("Players") -- Module Scripts local moduleScripts = ServerStorage:WaitForChild("ModuleScripts") local matchManager = require(moduleScripts:WaitForChild("MatchManager")) local gameSettings = require(moduleScripts:WaitForChild("GameSettings")) local displayManager = require(moduleScripts:WaitForChild("DisplayManager")) -- Events local events = ServerStorage:WaitForChild("Events") local matchEnd = events:WaitForChild("MatchEnd") while true do displayManager.updateStatus("Waiting for Players") repeat task.wait(gameSettings.intermissionDuration) until #Players:GetPlayers() >= gameSettings.minimumPlayers displayManager.updateStatus("Get ready!") task.wait(gameSettings.transitionTime) matchManager.prepareGame() local endState = matchEnd.Event:Wait() print("Game ended with: " .. endState) end ``` ### MatchManager script ```lua local MatchManager = {} -- Services local ServerStorage = game:GetService("ServerStorage") local ReplicatedStorage = game:GetService("ReplicatedStorage") -- Module Scripts local moduleScripts = ServerStorage:WaitForChild("ModuleScripts") local playerManager = require(moduleScripts:WaitForChild("PlayerManager")) local gameSettings = require(moduleScripts:WaitForChild("GameSettings")) local timer = require(moduleScripts:WaitForChild("Timer")) -- Events local events = ServerStorage:WaitForChild("Events") local matchStart = events:WaitForChild("MatchStart") local matchEnd = events:WaitForChild("MatchEnd") -- Values local displayValues = ReplicatedStorage:WaitForChild("DisplayValues") local timeLeft = displayValues:WaitForChild("TimeLeft") -- Creates a new timer object to be used to keep track of match time. local myTimer = timer.new() -- Local Functions local function stopTimer() myTimer:stop() end local function timeUp() matchEnd:Fire(gameSettings.endStates.TimerUp) end local function startTimer() print("Timer started") myTimer:start(gameSettings.matchDuration) myTimer.finished:Connect(timeUp) while myTimer:isRunning() do -- Adding +1 makes sure the timer display ends at 1 instead of 0. timeLeft.Value = (myTimer:getTimeLeft() + 1) // 1 -- By not setting the time for wait, it offers more accurate looping task.wait() end end -- Module Functions function MatchManager.prepareGame() playerManager.sendPlayersToMatch() matchStart:Fire() end matchStart.Event:Connect(startTimer) matchEnd.Event:Connect(stopTimer) return MatchManager ``` --- title: "Finish the project" url: /docs/en-us/education/battle-royale-series/finishing-the-project last_updated: 2026-06-29T19:33:59Z description: "Create a battle royale experience in Roblox Studio. Wrap up the project with map improvements." --- # Finish the project Congrats! You've just created a multiplayer battle royale! Over this series, you have: - Created modular scripts that handled different game functions like teleporting players. - Learned how to code custom events for the start and end of matches - Used arrays to manage players as they join, win, or leave games. But, your game is almost ready for others to play. Attract players to your game by making your arena unique and creating an eye-catching thumbnail. ## Optional improvements Below are a few ways of improving your experience. ### Improve map visuals Having a visually interesting map sets a strong first impression for your game, encouraging people to start playing. Take some time to turn your graybox level into a real map. Remember writing a description of the setting for your game at the start of this series? As you build, ensure your map has a clear setting. For inspiration, below are some example maps built by Roblox developers. _Map by Luxeyes_ _Map by Janedel_ You can either build in Studio, or use premade assets. Below are some suggested assets uploaded by Roblox that can be used to build an environment. Each pack includes high quality, fully textured models. - [Synty Nature Pack](https://www.roblox.com/library/6933438443/Synty-Nature-Pack) - [Synty City Pack](https://www.roblox.com/library/6933556508/Synty-City-Pack) - [Synty Dungeon Pack: Cave & Castle Interiors](https://www.roblox.com/library/6934021345/) ### Changing the Forcefield During a game, you may have noticed a force field when players respawn. Change how long the force field lasts in the SpawnLocation properties. 1. Click on the relevant Spawn Location. 2. In Properties > Forcefield, change the Duration value. ### Playtest and confirm variables Successful games on Roblox are playtested frequently to ensure the gameplay is fun and fair. Playtest your game with peers and check for the following: - Does the duration of a match feel right? Do matches end too quickly without a winning player, or take too long? - Does the size of the map feel right? Are there any areas that feel too empty? Will it take a long time to run into another player? Test, evaluate, and modify variables to improve gameplay. Some examples: - Change `GameSettings.matchDuration` to make matches longer in a larger map. - Make the intermission duration longer if players feel it's too sudden. ### Make the lobby social Popular games in Roblox often add mini-games to encourage players to have fun and be social while they wait for an intermission. This can include putting in sphere parts with physics so players can play, or even mini-obstacle courses. ## Optional challenges Many Roblox experiences continue to get updates even after they release. Below are some optional challenges that can add new features for your project. ### Traps Make maps more challenging by adding traps or obstacles that damage players. Learn more in [Creating Traps](/docs/en-us/tutorials/fundamentals/coding-3/traps-with-if-statements.md). ### Tracking score Create a leaderboard that tracks how many times someone wins a round. Code one using this article on [Leaderboards](https://developer.roblox.com/articles/Leaderboards). ### Powerups Create scripted parts that make changes like modify a player's speed or tool's attack power. Remember, after the end of a match, use the `resetMatch()` to recreate the set of powerups. For reference, learn more in the [Powerups](/docs/en-us/tutorials/fundamentals/coding-3/powerups-with-if-statements.md) tutorial. ### Add more arenas Build out more arenas with different settings and code a randomized map selection. Whenever players start a match, a module script named MapManager will pick a random map, and then assign players to those spawn locations as needed. Check the code box below if you need hints or to see one implementation. ```lua --[[ Setup Notes: 1. In Workspace, create a folder named Maps. Store all parts of a map in individual folders. 2. For each individual map, include a folder named SpawnLocations 3. When starting a match, use pickNewMap() to get a random map. When assigning player spawn points, use GetSpawnLocations() to get a table with all locations. ]] local MapManager = {} local mapsFolder = workspace.Maps -- Stores all maps that can be rotated between local availableMaps = mapsFolder:GetChildren() -- Stores the current map in play local activeMap -- Used to get random maps. local randomGenerator = Random.new() -- Gets a random map from the available maps table function MapManager.pickNewMap() local whichMapKey = randomGenerator:NextInteger(1, #availableMaps) activeMap = availableMaps[whichMapKey] print("New map: " .. activeMap.Name) end -- returns a table with the maps current spawn points function MapManager.getSpawnLocations() local spawnPoints = activeMap:FindFirstChild("SpawnLocations") local availableSpawnPoints = spawnPoints:GetChildren() return availableSpawnPoints end return MapManager ``` --- title: "Create a Battle Royale" url: /docs/en-us/education/battle-royale-series/landing last_updated: 2026-06-29T19:33:59Z description: "How to make a game in Roblox Studio. Learn to design and code a battle royale experience for Roblox as a beginner." --- # Create a Battle Royale ### Series description Create a round-based multiplayer experience where users compete to be the last player left! A great next lesson after the adventure game, this series expands on core concepts in game design and computer science. Once finished, the experience can be easily customized with unique gameplay elements and monetized. ### Objectives and prerequisites | **Learning Objectives** | Practice **modular programming** by creating scripts that separately handle game functions, like teleporting players or starting a match timer. Implement **events** whenever game matches start and end to create cause and effect relationships between separate scripts. Implement **arrays** to manage players and manipulate them as needed as players start, win, or leave games. Understand the **coding architecture** of round based games, including how to cleanup and reset to making looping gameplay. | | --- | --- | | **Prerequisites** | Understand how to use if statements, arrays and for loops. Have a general understanding of module scripts. | ### Series contents | Article | Description | | --- | --- | | [Project Setup](/docs/en-us/education/battle-royale-series/project-setup.md) | Plan out a vision for the experience and build out a map to test gameplay and movement with. | | [Coding the Game Loop](/docs/en-us/education/battle-royale-series/coding-the-game-loop.md) | Use module scripts to code a game loop that will run in the background of the experience. | | [Managing Players](/docs/en-us/education/battle-royale-series/managing-players.md) | Continue to use module scripts to manage an array of players and perform functions such as teleporting them to a match. | | [Timers and Events](/docs/en-us/education/battle-royale-series/timers-and-events.md) | Use events to track different states of the game and signal whenever a state changes, such as the end of a timer. | | [Creating a GUI](/docs/en-us/education/battle-royale-series/creating-a-gui.md) | Display the current game state and other information to players using a graphical user interface. | | [Ending Matches](/docs/en-us/education/battle-royale-series/ending-matches.md) | Track the current player count in matches and use that information to send events that trigger the game's end. | | [Clean Up and Reset](/docs/en-us/education/battle-royale-series/cleanup-and-reset.md) | Learn how code is cleaned up to ensure that each player experiences a continuous gameplay loop after a match. | | [Finishing the Project](/docs/en-us/education/battle-royale-series/finishing-the-project.md) | Find assets to decorate the map and see optional challenges to take the experience further. | ### Example project | | [Battle Royale Example](https://www.roblox.com/games/3623999509/Battle-Royale-Template) Play a version of the final project to see what you can develop with this series. | | --- | --- | --- title: "Manage players" url: /docs/en-us/education/battle-royale-series/managing-players last_updated: 2026-06-29T19:33:59Z description: "Create a battle royale experience in Roblox Studio. Write scripts that handle player spawning and equipment." --- # Manage players With the game loop coded, it's time to start adding features into it. During a match, players can come and go. Because of this, code is needed for tasks like sending players into a match and keeping track of active players. To manage these tasks, create a module script named **PlayerManager**. This script will start a function to send players into the arena with a weapon and be expanded later in the series. ### Set up the script Because the player manager includes functions used by other scripts, it'll be a module script. 1. In ServerStorage > ModuleScripts, add a new module script named PlayerManager. Then, rename the module table to match the script name and add comments for local and module functions.```lua local PlayerManager = {} -- Local Functions -- Module Functions return PlayerManager ``` 2. Add local variables for the following:**Services:** - Players - Know what players have joined or left the game. - ServerStorage - Storage for player weapons.**Map and Player Variables:** - Lobby Spawn, Arena Folder, and Arena Spawn Folder - Used to teleport players to different areas. - An Array of Active Players - Keeps track of players currently in a game.```lua local PlayerManager = {} -- Services local Players = game:GetService("Players") local ServerStorage = game:GetService("ServerStorage") -- Map Variables local lobbySpawn = workspace.Lobby.StartSpawn local arenaMap = workspace.Arena local spawnLocations = arenaMap.SpawnLocations -- Player Variables local activePlayers = {} -- Local Functions -- Module Functions return PlayerManager ``` 3. Create a module function named `sendPlayersToMatch()` with a test print inside.```lua -- Local Functions -- Module Functions function PlayerManager.sendPlayersToMatch() print("Sending players to match") end return PlayerManager ``` ### Spawn players in the lobby Right now, there's multiple spawn locations, meaning that players spawn at a random one when joining the game. To ensure players spawn in the lobby, change the player's `RespawnLocation` property. 1. Create a new local function named `onPlayerJoin()` with a parameter of `player`. In that function, set the player's respawn location to the lobby spawn variable made earlier.```lua -- Local Functions local function onPlayerJoin(player) player.RespawnLocation = lobbySpawn end ``` 2. Add an events section beneath your module function. Then, connect `onPlayerJoin()` to Player Service's `PlayerAdded` event.```lua -- Module Functions function PlayerManager.sendPlayersToMatch() print("Sending players to match") end -- Events Players.PlayerAdded:Connect(onPlayerJoin) ``` ### Connect and test Now the modules can be connected and tested. With the PlayerManager created, require it so that the code in that module script can then run and send players to the lobby. 1. Go back to **MatchManager** and create variables for the following: - **ServerStorage** service. - **ModuleScripts** folder, child of ServerStorage. - **PlayerManager** module script, child of moduleScripts.```lua local MatchManager = {} -- Services local ServerStorage = game:GetService("ServerStorage") -- Module Scripts local moduleScripts = ServerStorage:WaitForChild("ModuleScripts") local playerManager = require(moduleScripts:WaitForChild("PlayerManager")) function MatchManager.prepareGame() playerManager.sendPlayersToMatch() end return MatchManager ``` 2. Use a **local server** with at least the minimum players to test. Confirm that you can see the following: - All players spawn in the Lobby. - The print statement from PlayerManager appears in the Output window. 3. Once finished, click Cleanup to shut down the server. ### Troubleshooting tips At this point, parts of the script aren't working as intended, try one of the following below. - Check the name of parts such as the Arena, or the location of Lobby > StartSpawn, especially if you named them differently than instructed in the lesson. - Make sure that modules are required in each script using the `require()` function and correctly spelled. ## Send players to the arena Now that players spawn in the lobby, teleport them into a match once the intermission is over. Change the player's `RespawnLocation` to a spawn location in the arena using a function in the Player object called `ReloadCharacter()`. 1. Go to the **PlayerManager** script, below `onPlayerJoin()`, add a new local function named `preparePlayer()`. Include two parameters: `player` and `whichSpawn`, the spawn location to send them to.```lua local activePlayers = {} -- Local Functions local function onPlayerJoin(player) player.RespawnLocation = lobbySpawn end local function preparePlayer(player, whichSpawn) end -- Module Functions function PlayerManager.sendPlayersToMatch() print("Sending players to match") end ``` 2. Set the player's respawn location to `whichSpawn`.```lua local function preparePlayer(player, whichSpawn) player.RespawnLocation = whichSpawn end ``` 3. Force the character to reload, using `LoadCharacter()`, and the player will respawn using their newly assigned location.```lua local function preparePlayer(player, whichSpawn) player.RespawnLocation = whichSpawn player:LoadCharacter() end ``` > **Info:** Reloading a character ensures that players only start with tools provided at the start of a round. For instance, if you further developed this game and added tools with ammo and pickup weapons, it may be possible for a character to bring those tools in, giving them an unfair advantage. ### Send players to spawn Make sure each player gets teleported to a different spawn location in the arena by using a `for` loop to iterate through the active players array. Using a `for` loop allows you to go through every value in the players array, allowing the script to adapt to a variety of player numbers. 1. In the `sendPlayersToMatch()` function, use a variable to create an array of all the arena spawn locations by getting the children of the Arena > SpawnLocations folder.```lua --Module Functions function PlayerManager.sendPlayersToMatch() local arenaSpawns = spawnLocations:GetChildren() end ``` 2. Add the `for` loop below to get an array of all players and then iterate through each of them. To get players, type: `Players:GetPlayers()`.```lua function PlayerManager.sendPlayersToMatch() local arenaSpawns = spawnLocations:GetChildren() for playerKey, whichPlayer in Players:GetPlayers() do end end ``` ### Track and spawn When the game runs, it needs to identify which users are playing so they can be spawned in the arena. At the start of a round, every player will be tracked in an array of active players. That array will be used for different functions, such as teleporting or assigning weapons, ensuring that players still in the lobby during a round aren't affected. 1. In the for loop, use `Library.table.insert()`, using the two parameters for the `activePlayers` array and the player to add.```lua function PlayerManager.sendPlayersToMatch() local arenaSpawns = spawnLocations:GetChildren() for playerKey, whichPlayer in Players:GetPlayers() do table.insert(activePlayers, whichPlayer) end end ``` 2. To get a spawn location from the arena, create a variable named `spawnLocation` and set it to the **first** index in the `arenaSpawns` table.```lua for playerKey, whichPlayer in Players:GetPlayers() do table.insert(activePlayers, whichPlayer) local spawnLocation = arenaSpawns[1] end ``` 3. Call `preparePlayer()` and pass in `whichPlayer` and `spawnLocation`. Then, since that spawn location was used, **remove** it from the table so the next player will get a different spawn.```lua for playerKey, whichPlayer in Players:GetPlayers() do table.insert(activePlayers, whichPlayer) local spawnLocation = table.remove(arenaSpawns, 1) preparePlayer(whichPlayer, spawnLocation) end ``` 4. Test on a **local** server that players are sent to the arena. The players will continue to respawn at the same location because the code to send them back to the lobby isn't yet in place. ### Troubleshooting tips At this point, you didn't see the intended results, try one of the following below. - In `GetPlayers()`, make sure there are **two** closing parentheses, such as `Class.Players.GetPlayers(|Players:GetPlayers())` in the statement. - Check the series of function calls in the module scripts. For example, `matchManager.prepareGame()` should call `playerManager.sendPlayersToMatch()`. ## Giving Players Weapons When a round starts, each player in the arena will be provided a weapon to use. ### Add a tool Player weapons will be a tool. While any tool in Roblox can be used, we've provided a sample sword to start. 1. Import the weapon from the Toolbox, or create your own (see `Class.Tool|Tools`). 2. Place the weapon into ServerStorage. If you're creating your own tool, make sure the tool is named Weapon, since that'll be used in later scripts. ### Give tools to players Now that the tool is in storage, work on a script to go through the active player array and provide each user that tool. 1. In PlayerManager, add a variable named `playerWeapon` for the Weapon in ServerStorage.```lua -- Map Variables local lobbySpawn = workspace.Lobby.StartSpawn local arenaMap = workspace.Arena local spawnLocations = arenaMap.SpawnLocations -- Player Variables local activePlayers = {} local playerWeapon = ServerStorage.Weapon ``` 2. In `preparePlayer()`, paste the following code to get the player's character.```lua local function preparePlayer(player, whichSpawn) player.RespawnLocation = whichSpawn player:LoadCharacter() local character = player.Character or player.CharacterAdded:Wait() end ``` > **Info:** It's possible that a player's character isn't loaded when the game starts. Adding the extra wait ensures that if a character isn't immediately available, the script will wait and not cause an error. 3. Create a new variable named sword and use the `Clone()` function to create a copy of the weapon in ServerStorage. Then, parent the sword to the player's character.```lua local function preparePlayer(player, whichSpawn) player.RespawnLocation = whichSpawn player:LoadCharacter() local character = player.Character or player.CharacterAdded:Wait() local sword = playerWeapon:Clone() sword.Parent = character end ``` 4. Test on a **local server** to confirm that every player gets a tool when sent to the arena. Keep in mind, if you continue testing, the intermission will keep restarting and so players will respawn every few seconds. This will be resolved in the next lesson. ## Completed scripts Below are completed scripts to double check your work. ### GameManager script ```lua -- Services local ServerStorage = game:GetService("ServerStorage") local Players = game:GetService("Players") -- Module Scripts local moduleScripts = ServerStorage:WaitForChild("ModuleScripts") local matchManager = require(moduleScripts:WaitForChild("MatchManager")) local gameSettings = require(moduleScripts:WaitForChild("GameSettings")) while true do repeat task.wait(gameSettings.intermissionDuration) print("Restarting intermission") until #Players:GetPlayers() >= gameSettings.minimumPlayers print("Intermission over") task.wait(gameSettings.transitionTime) matchManager.prepareGame() end ``` ### MatchManager script ```lua local MatchManager = {} -- Services local ServerStorage = game:GetService("ServerStorage") -- Module Scripts local moduleScripts = ServerStorage:WaitForChild("ModuleScripts") local playerManager = require(moduleScripts:WaitForChild("PlayerManager")) function MatchManager.prepareGame() playerManager.sendPlayersToMatch() end return MatchManager ``` ### PlayerManager module script ```lua local PlayerManager = {} -- Services local Players = game:GetService("Players") local ServerStorage = game:GetService("ServerStorage") -- Map Variables local lobbySpawn = workspace.Lobby.StartSpawn local arenaMap = workspace.Arena local spawnLocations = arenaMap.SpawnLocations -- Player Variables local activePlayers = {} local playerWeapon = ServerStorage.Weapon -- Local Functions local function onPlayerJoin(player) player.RespawnLocation = lobbySpawn end local function preparePlayer(player, whichSpawn) player.RespawnLocation = whichSpawn player:LoadCharacter() local character = player.Character or player.CharacterAdded:Wait() local sword = playerWeapon:Clone() sword.Parent = character end -- Module Functions function PlayerManager.sendPlayersToMatch() print("Sending players to match") local arenaSpawns = spawnLocations:GetChildren() for playerKey, whichPlayer in Players:GetPlayers() do table.insert(activePlayers, whichPlayer) local spawnLocation = table.remove(arenaSpawns, 1) preparePlayer(whichPlayer, spawnLocation) end end --events Players.PlayerAdded:Connect(onPlayerJoin) return PlayerManager ``` --- title: "Battle Royale" url: /docs/en-us/education/battle-royale-series/project-setup last_updated: 2026-06-29T19:33:59Z description: "Create a Battle Royale experience in Roblox Studio. Learn coding in Roblox and how to make a game." --- # Battle Royale **Battle Royales** are a multiplayer game genre where opponents compete until only one player is left. While every battle royale is different, they all include a way to eliminate players such as freezing someone or knocking them off the map. When one player survives, or a timer ends, the match is over and a new round starts. The genre is popular because rounds are quick, easy to pick-up, and challenging to master. Battle royales can be customized with different game mechanics to appeal to a wide audience, like unique weapons, platforming obstacles, or visual themes. Some popular battle royale experiences in Roblox include Island Royale and Strucid. _Island Royale by LordJurrd_ _Strucid by Frosted Studios_ Battle royales generally follow a round-based **game loop**, or series of phases. In the project you'll make, players go through the game loop below: _Intermission_ _Competition_ _Cleanup and Reset_ During each phase, a different set of tasks happened that you'll code during this series. - **Intermission** - Players socialize or watch games in the lobby until a new round starts. - **Match** - Timer starts and players are teleported to an arena where they compete. If a player loses, they're teleported back to the lobby. - **Cleanup and Reset** - Happens when one player is left or the timer finishes. Players are then teleported back to the lobby where the loop restarts. ## Develop the experience Battle royales are made of many elements, like code and art assets. To manage larger projects, developers plan a **workflow**, or a series of steps, to get to completion. During this series, you'll go through the following workflow: - **Preproduction** - Create a sketch of the game map. - **Design a Test Map** - Develop a map using placeholder assets to test out the design, without worrying about visual look and feel. - **Code and Test** - Begin the process of coding the game loop. - **Polish and Improve** - Replace placeholder assets with finalized models, and improve code and design through frequent playtesting. Instead of working on different parts of a project simultaneously, developers break large projects into manageable chunks. Each phase should have its own specific goal before going to the next phase. This makes it easier to catch potential errors and save time down the road. For instance, designing art to polish a map that hasn't been tested may result in wasted time if the map needs to be redesigned to be fun. ### Plan the project The first phase is to plan your vision in a process called **preproduction**. Taking the time to make a plan helps you focus on important design choices, such as where to place obstacles and player spawns. To plan, you'll create a layout map with paper or a drawing software. A layout map is the floor plan for the arena drawn in basic shapes, focusing on how players move through the world rather than visual details. Once the layout map is finished, you'll recreate it in Studio. _Drawn Layout_ _Layout in Studio_ ### Design a map layout For the sketch, the objective here is to create a design you can then replicate in Studio. The map arena should have enough variety to be fun, but also include balance as to not give users an unfair advantage. 1. Write a brief description of the **setting**, such as a jungle, an abandoned moon base, or a medieval castle. During the polishing phase, you'll add map details using this setting. 2. Identify the shape of the map and then draw it out using a combination of 1-3 **basic shapes** (square, rectangle, octagon). Even if you envision a more complex map, such as an island, try and break it down into basic shapes. > **Info:** We recommend making a symmetrical map to easily balance the experience. As you gain more experience, you can start working with non-symmetrical map shapes, which can be more complicated to balance fairly for everyone. 3. Add player spawns. For now, use eight spawns, but you can always add more later. The example here uses a square for the map shape. 4. To make players less predictable and add interest, place obstacles that force them choose different directions. Draw 2-4 shapes (orange) that create choices in moving around the arena. Add secondary obstacles (yellow) that prevent players from fighting right at the start. ### Tips for layouts Keep designs simple, but engaging. Since players make split second decisions in moving through a level, give them enough choices to make a level feel different each time, but not too much that they can't remember how to move through the map or feel overwhelmed. _Too simple_ _Balanced_ _Potentially complex_ ## Create the map Recreate the layout map quickly in Roblox Studio using basic parts in a process called grayboxing. Stay focused on designing a fun, playable map. Don't spend time adding textures or small details, like decorative props. After you've created a working map with code, then spend time designing art to fit the map's setting. ### Build a lobby Before creating the arena, build the lobby, where players enter the experience and socialize between matches. 1. Create a new Baseplate project and delete the baseplate. 2. Construct a walled room with a spawn location. > **Info:** Enable Snap to Grid to make moving and lining up objects easier. Rotating parts at 90 or 45 degree angles will also help result in cleaner designs. 3. Place all lobby parts into a folder named Lobby. ### Arena and spawns The arena is where players will compete. When building the arena, you'll **graybox** the environment with simple parts and colors. A graybox environment is an approximation of the final design, so a large cylinder may be an equally sized tree in the final version. This process, common in level design, gives designers a working prototype to test and iterate with. Once the map design feels good in playtesting, graybox assets are replaced with 3D assets and terrain. 1. Create a folder named Arena. Inside, add the floor for the arena. If you're using terrain, leave the folder empty for now. Below are sample arenas. 2. Create eight spawn locations on the map. Create a new folder in Arena named SpawnLocations and move the eight spawns there. > **Info:** As you place spawns, keep in mind that all players will randomly spawn when joining. ### Graybox the arena Remember that grayboxing is an approximation of the final design using simple parts. A grayboxed level should give designers an understanding of how players move through the arena. To create the arena, use an optional building kit or basic parts. 1. Import the Grayboxing Kit from the Toolbox. This includes a variety of common building features such as stairs, ramps, and walls. > **Info:** Note that Roblox_Educators is the official, trusted account for assets from Roblox Education. 2. Using a combination of parts and assets from the grayboxing kit, create obstacles and barriers. As you build, some tips for map design are below. - **Vary Height** - Flat maps can get repetitive for players. Use hills, stairs, and ramps of different heights to add variety to the map. - **Build Half of the Map and then Duplicate** - This technique allows you to quickly build a symmetrical map. -**Test and Check for Scale** - While building, think about the map in relation to a player. For example, how spacious does an area feel or can a player fit easily through a door. Keep in mind the average avatar is 6.5 studs tall. ### Playtest the graybox With the arena completed, it's important to see if it's fun and interesting to move around. 1. Click **Play Here** in the arena and test the map. While testing, self-evaluate your work with the prompts below and make changes as needed to improve. - Can players move around without getting confused or stuck? - Does the size of the map feel right? Are there any areas that feel too empty? Will it take a long time to run into another player? - Does anything look strange, such as floating or misaligned parts? --- title: "Timers and events" url: /docs/en-us/education/battle-royale-series/timers-and-events last_updated: 2026-06-29T19:33:59Z description: "Create a battle royale experience in Roblox Studio. Code a timer and use bindable events for a match based game." --- # Timers and events During the course of a round, the scripts will need to track time and send signals between different scripts. Time will be managed using a time script, while events, a concept in Roblox coding, will signal changes such as the end of a match. ## Send signals with events With players now in the arena, events can be used to signal the start of the match and the code for the timer can begin. Later, an event can also be used to signal the end of the match, and that it's time to transition players back to the lobby. These events aren't prebuilt, so custom event objects called **bindable events** will need to be created. Bindable events are often used for player-fired actions and are similar to events like `Touched` or `Changed`. Multiple scripts can listen for the same bindable events. This keeps your code organized and makes it easier to add additional code for the start or end of the match later if needed. ### Create bindable events Start by creating bindable event objects for the start and end of the match. Since bindable events don't interact with the client, they can be stored in Server Storage. 1. In ServerStorage, create a new folder named Events. In that folder, create two **BindableEvents** named MatchStart and MatchEnd. ### Use events Right now, when players enter the arena, the intermission keeps restarting rather than beginning the timer. The main game loop needs to be told to stop and wait until the MatchEnd event fires before moving on to the next part of the code. Events have two built-in functions: `Connect()` and `Wait()`. Instead of using `Connect()` like previously, call `Wait()` on MatchEnd to pause the game manager script until MatchEnd is fired. In this case, the wait function pauses the code until the game manager receives a signal that the match ended. 1. In **GameManager**, create variables for the `Events` folder and `MatchEnd` event.```lua -- Module Scripts local moduleScripts = ServerStorage:WaitForChild("ModuleScripts") local matchManager = require(moduleScripts:WaitForChild("MatchManager")) local gameSettings = require(moduleScripts:WaitForChild("GameSettings")) -- Events local events = ServerStorage:WaitForChild("Events") local matchEnd = events:WaitForChild("MatchEnd") ``` 2. Have the script wait for the match end event to fire before moving on. In the **loop**, at the **end**, type: `matchEnd.Event:Wait()````lua while true do repeat task.wait(gameSettings.intermissionDuration) print("Restarting intermission") until #Players:GetPlayers() >= gameSettings.minimumPlayers print("Intermission over") task.wait(gameSettings.transitionTime) matchManager.prepareGame() -- Placeholder wait for the length of the game. matchEnd.Event:Wait() end ``` 3. **Test** the game. Confirm that once players are in the arena, the intermission loop **does not** continue. The script is now waiting for the `matchEnd` signal to fire. ### Troubleshooting tips At this point, the code doesn't work as expected, try one of the following below. - Double check the usage of the dot or colon operators in `matchEnd.Event:Wait()`. - Make sure that MatchEnd is a BindableEvent, and not another type, such as a RemoteEvent. ## Use a timer One of the conditions that will cause the end of the match is a timer running out, which will be handled through the script. ### Set up the timer To add a timer into the game, use the premade module script in the steps below. It includes functions to start and end a timer, as well as return the amount of time left. 1. In ServerStorage > ModuleScripts, create a new module script named Timer. Replace the code with the code below.```lua local Timer = {} Timer.__index = Timer function Timer.new() local self = setmetatable({}, Timer) self._finishedEvent = Instance.new("BindableEvent") self.finished = self._finishedEvent.Event self._running = false self._startTime = nil self._duration = nil return self end function Timer:start(duration) if not self._running then task.spawn(function() self._running = true self._duration = duration self._startTime = tick() while self._running and tick() - self._startTime < duration do task.wait() end local completed = self._running self._running = false self._startTime = nil self._duration = nil self._finishedEvent:Fire(completed) end) else warn("Warning: timer could not start again as it is already running.") end end function Timer:getTimeLeft() if self._running then local now = tick() local timeLeft = self._startTime + self._duration - now if timeLeft < 0 then timeLeft = 0 end return timeLeft else warn("Warning: could not get remaining time, timer is not running.") end end function Timer:isRunning() return self._running end function Timer:stop() self._running = false end return Timer ``` 2. In MatchManager, require the GameSettings and Timer modules.```lua local MatchManager = {} -- Services local ServerStorage = game:GetService("ServerStorage") -- Module Scripts local moduleScripts = ServerStorage:WaitForChild("ModuleScripts") local playerManager = require(moduleScripts:WaitForChild("PlayerManager")) local gameSettings = require(moduleScripts:WaitForChild("GameSettings")) local timer = require(moduleScripts:WaitForChild("Timer")) ``` 3. Below the variables, create a new timer object by setting a variable named `myTimer` equal to `timer.new()`. This object will be used to call functions that start and stop the timer.```lua local gameSettings = require(moduleScripts:WaitForChild("GameSettings")) local timer = require(moduleScripts:WaitForChild("Timer")) -- Creates a new timer object to be used to keep track of match time. local myTimer = timer.new() ``` > **Info:** The timer module script can be called by other scripts to create more timers as needed, like if you wanted to have traps appear after a specific time. To use the timer in more than one situation, create a new Timer object, like `myTrapTimer = timer.new()`. Timer objects should only be used for a single purpose, rather than reused. ### Start and stop Now that a timer is created, use the included functions `start()` and `stop()` during a match. Below is a description of each function and the parameter it accepts. - `start(time)` - Starts the timer, with time in seconds as the parameter. - `finished:Connect(functionName)` - When the timer finishes, runs the function passed as a parameter. 1. In **MatchManager**, create a new function named `timeUp()` to run whenever the timer has finished. Include a test print statement.```lua local myTimer = timer.new() -- Local Functions local function timeUp() print("Time is up!") end -- Module Functions function MatchManager.prepareGame() playerManager.sendPlayersToMatch() end return MatchManager ``` 2. Below `timeUp()`, add a function named `startTimer()` with a print statement. You'll display the timer in-game later.```lua -- Local Functions local function timeUp() print("Time is up!") end local function startTimer() print("Timer started") end ``` 3. To start and stop the timer, in `startTimer()`: - Call `myTimer.start()`. Pass in `gameSettings.matchDuration`. - Call `myTimer.finished:Connect()`. Pass in `timeUp()`.```lua -- Local Functions local function startTimer() print("Timer started") myTimer:start(gameSettings.matchDuration) myTimer.finished:Connect(timeUp) end ``` ### Start the timer The timer can be triggered at the start of a match using the Match Start event. 1. In MatchManager, under the module variables, create variables to store the Events folder, MatchStart, and MatchEnd (which is used in a future lesson).```lua -- Module Scripts local moduleScripts = ServerStorage:WaitForChild("ModuleScripts") local playerManager = require(moduleScripts:WaitForChild("PlayerManager")) local gameSettings = require(moduleScripts:WaitForChild("GameSettings")) local timer = require(moduleScripts:WaitForChild("Timer")) -- Events local events = ServerStorage:WaitForChild("Events") local matchStart = events:WaitForChild("MatchStart") local matchEnd = events:WaitForChild("MatchEnd") --Creates timer local myTimer = timer.new() ``` 2. Above `return MatchManager`, connect the match start event to `startTimer()`.```lua -- Module Functions function MatchManager.prepareGame() playerManager.sendPlayersToMatch() end matchStart.Event:Connect(startTimer) return MatchManager ``` 3. To fire the match start event, in `prepareGame()`, type `matchStart:Fire()`.```lua -- Module Functions function MatchManager.prepareGame() playerManager.sendPlayersToMatch() matchStart:Fire() end ``` 4. Test the game. In the Output window, confirm that you can see the print statements for the timer's start and stop functions. ## Completed scripts Below are completed scripts to double check your work. ### MatchManager script ```lua local MatchManager = {} -- Services local ServerStorage = game:GetService("ServerStorage") -- Module Scripts local moduleScripts = ServerStorage:WaitForChild("ModuleScripts") local playerManager = require(moduleScripts:WaitForChild("PlayerManager")) local gameSettings = require(moduleScripts:WaitForChild("GameSettings")) local timer = require(moduleScripts:WaitForChild("Timer")) -- Events local events = ServerStorage:WaitForChild("Events") local matchStart = events:WaitForChild("MatchStart") local matchEnd = events:WaitForChild("MatchEnd") -- Creates a new timer object to be used to keep track of match time. local myTimer = timer.new() -- Local Functions local function timeUp() print("Time is up!") end local function startTimer() print("Timer started") myTimer:start(gameSettings.matchDuration) myTimer.finished:Connect(timeUp) end -- Module Functions function MatchManager.prepareGame() playerManager.sendPlayersToMatch() matchStart:Fire() end matchStart.Event:Connect(startTimer) return MatchManager ``` ### GameManager script ```lua -- Services local ServerStorage = game:GetService("ServerStorage") local Players = game:GetService("Players") -- Module Scripts local moduleScripts = ServerStorage:WaitForChild("ModuleScripts") local matchManager = require(moduleScripts:WaitForChild("MatchManager")) local gameSettings = require(moduleScripts:WaitForChild("GameSettings")) -- Events local events = ServerStorage:WaitForChild("Events") local matchEnd = events:WaitForChild("MatchEnd") while true do repeat task.wait(gameSettings.intermissionDuration) print("Restarting intermission") until #Players:GetPlayers() >= gameSettings.minimumPlayers print("Intermission over") task.wait(gameSettings.transitionTime) matchManager.prepareGame() -- Placeholder wait for the length of the game. matchEnd.Event:Wait() end ``` --- title: "Build one half" url: /docs/en-us/education/build-it-play-it-create-and-destroy/build-one-half last_updated: 2026-06-29T19:33:59Z description: "Part of the Create and Destroy series. Work on finishing a map for the multiplayer experience." --- # Build one half With the SpawnLocations done, it's time to design the city. Designing will follow these steps: - **Add buildings**, which come in large and medium sizes, each giving different points when destroyed. - **Place props** to make the city more visually interesting. - **Paint terrain** like mountains, rivers, and hills. _Buildings_ _Props_ _Terrain_ ## Start building To build the city, you'll duplicate buildings from the of objects on the right side of the game world. ## Duplicate the first building 1. Select a large building from the palette and **duplicate** it (`Ctrl``D` or `⌘``D`). Nothing obvious will happen. With collisions off, the new building will overlap the original. > **Warning:** To avoid errors in your experience, **always duplicate** buildings and props, not copy and paste. Duplicate puts the new object into the same folder, copy and paste moves the new object outside of the folder. 2. Select the **Move** tool and use the **arrows** to drag the **duplicate** to the half of the map you're working on. Use the white grid lines to help you position the buildings halfway between the two SpawnLocations and to make sure you're keeping to one half of the map. ## Check all the angles As you work, make sure that you're not accidentally moving the buildings upwards off the ground. Take a moment to check the building you just placed by rotating the camera to get a better look from different angles. Start by checking the building from the side. 1. Select the building, and press `F` to focus the camera on it. 2. Use the small arrows on the **View Selector** (top right) to change to the side view. If you can't find the View Selector, find it's button in the View tab. 3. Use the camera controls to get a good view of the building. Below are camera controls. | Action | Control | | --- | --- | | **Move** | `W` `A` `S` `D` or arrow keys | | **Rotate** | Hold the right mouse button and look around. | | **Pan** | Hold the middle mouse button to drag your camera around. | 4. Get back to the top view by using the arrows, or by clicking areas of the View Selector. ## Place additional large buildings Duplicate and place **3 - 5 additional** large buildings on the same half of the map. Remember that you will be duplicating this half to make the rest of the map. Your map doesn't have to look like the example, but each player should be able to reach the same amount of buildings. Here, every player has an equal chance of reaching three of the four buildings. ### Troubleshooting tips While designing the map, if you notice buildings don't snap in place, try the following below. - In the Model tab, check that your Snap to Grid settings for Move is set to 4 studs. If not, your buildings may snap incorrectly. - To get the most accurate snapping, it's recommended to work from the top view and to always move objects using the arrows, not by dragging. - If an object won't snap correctly, restart by deleting the building and then duplicating a new one from the palette. --- title: "Build the roads" url: /docs/en-us/education/build-it-play-it-create-and-destroy/build-the-roads last_updated: 2026-06-29T19:33:59Z description: "Part of the Create and Destroy series. Place roads to continue building a city in Roblox Studio." --- # Build the roads Next, duplicate the road tiles in your palette to create roads between the buildings. As you place them, you'll need to rotate them into the correct position. 1. Select a road in the palette and **duplicate** it. Then, **move** it over into your map. 2. Rotate the selected road tile to get the desired orientation. To rotate the tiles, select the **Rotate** tool and drag the green handle until the tile rotates. > **Info:** You can save time building roads by duplicating a road tile in your arena, rather than always duplicating from the palette. ## Map example Leave plenty of room for the other buildings and decorations. --- title: "Buildings and props" url: /docs/en-us/education/build-it-play-it-create-and-destroy/buildings-and-props last_updated: 2026-06-29T19:33:59Z description: "Part of the Create and Destroy series. Add building and props to decorate a city." --- # Buildings and props Once you're happy with the roads in your city, start placing the medium buildings. Players get 10 points for smashing these, so try to place the same amount of them near each SpawnLocation. 1. Duplicate and move the **medium buildings** onto the map. 2. Place 8 - 10 medium buildings, adding more roads as needed. ## Finish with props Next, start placing provided props, such as trees or cars, around the map. Players don't get any points for smashing these; they're there to add realism to your world. An example of props placed is below. --- title: "Take the challenge!" url: /docs/en-us/education/build-it-play-it-create-and-destroy/challenge-1 last_updated: 2026-06-29T19:33:59Z description: "Complete the first Create and Destroy challenge to get a free Roblox avatar item." --- # Take the challenge! Test your knowledge and earn the Rodan's Head avatar item and Drafting the Blueprints Badge! 1. Click **Play Quiz Experience** below to open a Roblox experience. Answer questions about what you just learned to complete the challenge. 2. **Come back** and continue building your city. --- title: "Take the challenge!" url: /docs/en-us/education/build-it-play-it-create-and-destroy/challenge-2 last_updated: 2026-06-29T19:33:59Z description: "Complete the second Create and Destroy challenge to get a free Roblox avatar item." --- # Take the challenge! Test your knowledge and earn the Godzilla Backpack avatar item and World Builder Badge by clicking the button below to open a Roblox quiz experience. When you're done, keep moving forward through this series. --- title: "Take the challenge!" url: /docs/en-us/education/build-it-play-it-create-and-destroy/challenge-3 last_updated: 2026-06-29T19:33:59Z description: "Complete the third Create and Destroy challenge to get a free Roblox avatar item." --- # Take the challenge! Test your knowledge and earn the Ghidorah's Wings avatar item and Architect of Destruction Badge by clicking the button below to open a Roblox quiz experience. When you're done, keep moving forward through this series. --- title: "Change the script" url: /docs/en-us/education/build-it-play-it-create-and-destroy/change-the-script last_updated: 2026-06-29T19:33:59Z description: "Part of the Create and Destroy series. Learn how to open and change a script in Roblox Studio." --- # Change the script With the map complete, you'll move onto polishing the experience. These next articles will cover these finishing touches: - Modify a script to award different points for buildings. - Upload a custom game image. - Share the experience with friends. ## Open the script Besides just the map, other aspects of Create and Destroy can be customized through a **script**, a container for code used to run an experience. In this case, you'll change the points awarded by destroying buildings. 1. At the top of the **Explorer**, a list of everything included in a project, on the right-hand side, type **GameSettings**. 2. Double-click **GameSettings** to open the script editor. ## GameSettings script contents In the script, you'll see a section with the three different point values given to players for large buildings (HighPoints), medium buildings (MediumPoints), and for props (LowPoints). ```lua -- Game Variables GameSettings.intermissionDuration = 10 GameSettings.roundDuration = 30 GameSettings.minimumPlayers = 1 GameSettings.transitionStart = 3 GameSettings.transitionEnd = 3 GameSettings.pointValues = { -- Value types must match folder names to award points correctly LowPoints = 0, MediumPoints = 10, HighPoints = 15, } ``` ## Change the points value Giving players more points can make smashing buildings feel even more rewarding. 1. Look for Line 11 in the script. Make the large buildings worth 150 points by changing `HighPoints = 15`, to `HighPoints = 150`. Be sure to keep the comma after the number.```lua GameSettings.pointValues = { -- Value types must match folder names to award points correctly LowPoints = 0, MediumPoints = 10, HighPoints = 150, } ``` 2. **Playtest** and see how the change feels. Ask yourself if you'd like players to get more points for smashing the medium sized buildings as well. You can change that too. 3. If desired, take some time to experiment in making changes to the script. For example, changing the number in `Class.GameSettings.roundDuration` can make rounds go faster or shorter, depending on how many seconds you type. > **Warning:** If the project doesn't work as intended once a change has been made in the script, go back to the script editor and **undo** your last change. ## Share with friends When you first publish an experience, it's automatically set to private. Making it public in the **Experience Settings** menu will allow friends to be able to join with a link. 1. In the **File** → **Experience Settings** window, select **Permissions**. 2. Choose **Public** and click **Save**. --- title: "Complete the city" url: /docs/en-us/education/build-it-play-it-create-and-destroy/complete-the-city last_updated: 2026-06-29T19:33:59Z description: "Part of the Create and Destroy series. Finish creating a city for the multiplayer experience." --- # Complete the city When the first half of the city is ready, the next step is to use duplicate to turn it into a complete 4-player map. Even after you've duplicated the first half, you can still keep making changes to the city. 1. In the Model tab, **collisions** should be **off** (not highlighted). Collisions will make the map difficult to move and rotate. 2. Click and drag your mouse to **select all** the buildings, roads, and props. 3. **Duplicate** the selection just like you have before. 4. Use the **Move** and **Rotate** tools to reposition your city. If it doesn't look right, just undo (`Ctrl + Z` or `⌘ + Z`) and try again. After duplicating and rotating the city, you can add or adjust tiles to make the halves fit together better. --- title: "Create and destroy" url: /docs/en-us/education/build-it-play-it-create-and-destroy/designing-a-map last_updated: 2026-06-29T19:33:59Z description: "Part of the Create and Destroy series. Create a map in Roblox Studio." --- # Create and destroy Most popular experiences are made by teams of people with different skills working together. One role on teams is the **level designer**, a common career in game development. Level designers shape the game environment, painting terrain, placing props, and adding buildings. In competitive experiences, they make sure maps are balanced and fair for all players. For this challenge, you'll become a level designer and turn a blank starter map into a city ripe for destruction. ## Symmetrical map design One technique used by level designers to ensure multiplayer maps are fair is by building half the map, and then duplicating and rotating it to create the second half. This is called symmetrical design since it divides the map into two equal halves. Notice in the video below how one half of the city is copied to create full map. ## Open the template Time to open Roblox Studio! If you need to, [download Studio](https://www.roblox.com/create) and come back to this page when you're ready. This experience will be using the Create and Destroy template, which can be downloaded below. 1. Open Roblox Studio and log in. 2. Download the template. 3. In Roblox Studio, in the top left, click **File** → **Open from File** and select the downloaded file. ## About the template In this template, a prop artist and a coder have already created a starter map, you just need to design the city. On the left is an island, and on the right is a palette of objects to use. This sort of setup is very common in game development. --- title: "Finish the challenge" url: /docs/en-us/education/build-it-play-it-create-and-destroy/finish-the-challenge last_updated: 2026-06-29T19:33:59Z description: "Part of the Create and Destroy series." --- # Finish the challenge **Congrats!** You've completed the Creator Challenge. This is just the beginning. If you want to keep learning, below is a quick tutorial on improving your map with new buildings. ## Custom models While the map comes with premade buildings, different visuals can be used, be it a fantasy castle or even giant objects of food. Any model can be destroyed for points, but it must be placed into a specific folder. You can even copy and paste objects from other Studio files into the template. ### Find models A building can be anything you construct in Studio. You can either learn to [construct models](/docs/en-us/parts.md) or sample trusted objects from the [Marketplace](/docs/en-us/tutorials/first-experience.md#inserting-assets). ### Add models Models must be placed in specific folders to be interactive in-game. 1. Delete any search filters you have at the top of the Explorer. 2. Expand **Workspace** > **Arena** > **Map**. These folders, like **HighPoints**, are where the code looks for what parts can be destroyed and can count for points. 3. Place buildings and props in the HighPoints, MediumPoints, or LowPoints folder according to how that object is worth when destroyed. --- title: "Island terrain" url: /docs/en-us/education/build-it-play-it-create-and-destroy/island-terrain last_updated: 2026-06-29T19:33:59Z description: "Part of the Create and Destroy series. Learn how to open the Terrain Editor." --- # Island terrain A square map isn't very natural or interesting to players. To finish the map, you'll use the terrain tools to customize the edges of the map to make it look more like an actual island. _Before_ _After_ ## Open the Terrain Editor All terrain, whether it's mountains or painting grass, is created using the Terrain Editor. 1. Before starting, **publish** your experience so far. That way if you don't like how your changes turn out, you can easily go back to the saved version. 2. Go back to the **top-down** view (click on Top in the corner widget). This will help you edit terrain better in the next page. 3. From the **Home** tab, click **Terrain** to open the **Terrain Editor**. --- title: "Create and destroy" url: /docs/en-us/education/build-it-play-it-create-and-destroy/landing last_updated: 2026-06-29T19:33:59Z description: "Get started creating games and experiences in Roblox. Create a multiplayer game where you destroy buildings for points." --- # Create and destroy Learn how to design a multiplayer map for an experience in which players destroy cities with laser beams for points. Creating this map is great practice if you want to learn how to build battle royales or other combat games down the road. > **Info:** A finished version of the experience is available at the [Create and Destroy Test Map](https://www.roblox.com/games/7290548674/Create-and-Destroy-Test-Map?). If you don't have Roblox installed, you can do so by playing the test map. ## Download Roblox Studio You'll need Roblox Studio, which is used to create every experience on the Roblox platform and is available free for **PC** and **Mac**. We strongly recommend using a 3-button mouse with a scroll-wheel for navigating in 3D space. 1. Go to the Create page and click the Start Creating button to download Studio. 2. After installing, double-click the desktop icon (Windows) or click the dock icon (Mac). 3. On the login screen, enter your Roblox username and password, then click Log In. > **Info:** If you're new to Roblox and are [signing up](https://www.roblox.com/home) for the first time, remember these safety tips: - **Never share your password**, even with a real life friend. - **Make your password hard to guess** — If your username is "bloxdev," your password should not be "bloxdev123." - **Roblox employees will never ask for your password** — Report anyone who asks using the Report Abuse feature. - **There's no such thing as free Robux** — Never trust players or sites who say they have a secret way to get free Robux! > > For more tips, please see [Keeping Your Account Safe](https://en.help.roblox.com/hc/en-us/articles/203313380-Account-Security-Theft-Keeping-your-Account-Safe-). ### Three lessons, three prizes _1: Getting Started_ _2: Build and Test_ _3: Polish and Publish_ After each lesson, you'll get the chance to earn virtual prizes by playing a quiz on Roblox. --- title: "Playtest the map" url: /docs/en-us/education/build-it-play-it-create-and-destroy/playtest-the-map last_updated: 2026-06-29T19:33:59Z description: "Part of the Create and Destroy series. Playtest the map to see how it looks in-game." --- # Playtest the map To test your map as it will look in-game, playtest the map in Studio. **Playtesting** is the process of playing through a game and making sure that it's both fun and bug-free. 1. Click **Play**. You'll start out in a lobby area, and then get transported to the city. 2. Wait until the 5 second intermission finishes to be teleported to the map you built. Run around and make sure that you didn't miss any floating buildings that need to be fixed. Test the experience by holding `E` to destroy buildings. Below are controls for while in-game. | Action | Control | | --- | --- | | **Move** | `W` `A` `S` `D` or arrow keys | | **Jump** | `Spacebar` | | **Rotate View** | Hold and drag mouse to look around | 3. Click the **Stop** button to exit the playtest. If you don't stop the playtest, any changes made will be lost. --- title: "Save and publish" url: /docs/en-us/education/build-it-play-it-create-and-destroy/save-and-publish last_updated: 2026-06-29T19:33:59Z description: "Part of the Create and Destroy series. Learn how to save and publish a Roblox experience." --- # Save and publish It's important to save the whole project by **publishing** it to Roblox. It's a good idea to publish every ten minutes while you're working or after making a big change. 1. Select **File** → **Publish** to Roblox to open the publishing window. 2. Enter a place name and an optional description. 3. When ready, click the **Create** button. --- title: "Snap to grid" url: /docs/en-us/education/build-it-play-it-create-and-destroy/snap-to-grid last_updated: 2026-06-29T19:33:59Z description: "Part of the Create and Destroy series. Learn how to use the snap to grid settings in Roblox Studio." --- # Snap to grid The tile pieces on the palette are lined up on a grid so that they will snap together nicely. To have an easier time keeping them lined up on the grid, adjust the **Snap to Grid** settings. Snap to Grid controls how much you can move or rotate an object at a time. By default, the move tool only allows objects to move one stud at a time. The small tiles are 4x4 and the large are 8x8, so changing the **Move** setting to 4 studs will make it easier to line the tiles up with each other. 1. Select the **Home** or **Model** tab. 2. Toward the left side of the toolbar where it says **1 studs**, click and type **4**. ## Place the starting points When designing maps, start with the most important elements such as the starting locations and largest map features. In Roblox Studio, the player starting locations are set using SpawnLocation objects. There's two of them sitting on the palette for you to use. ## Place the first SpawnLocation 1. From the Model tab, select the **Move** tool. 2. Select one of the SpawnLocations in the palette. 3. Use the red and blue arrows to drag it onto the island where you want the first SpawnLocation to go. Be careful to drag by the arrows as just grabbing the piece can make it move oddly. > **Info:** If the SpawnLocation moves in a way you don't want, just `Ctrl + Z` or Command `⌘ + Z` to undo and set it back to where it was. ## Place the second SpawnLocation Pick which half of the island you want to build and where you want the second SpawnLocation to go. The white gridlines are there to help you know where the middle of the island is. 1. Select the **second** SpawnLocation in the palette on the **right** side of the map. 2. Move the second SpawnLocation. To keep the experience balanced, try and keep both SpawnLocations an equal distance from the center. > **Info:** If you find yourself needing to move the camera, you can move using the `WASD` or arrow keys to move in different directions. --- title: "Terrain tools" url: /docs/en-us/education/build-it-play-it-create-and-destroy/terrain-tools last_updated: 2026-06-29T19:33:59Z description: "Part of the Create and Destroy series. Learn how to use Terrain Tools in Roblox Studio to make an island." --- # Terrain tools The **Terrain Editor** is used to sculpt natural features. To start, you'll shape the shape of the island, then get into details such as mountains and painting the landscape. ## Create the shape 1. In the Terrain Editor window, select the **Edit** tab. Then, click the **Add** tool. 2. **Add** creates land wherever you click or drag the blue cursor. Change the **Base Size** or **Shape** to give you more control over the tool. 3. Click and drag the blue cursor to create a natural island shape. Cover the hard edges of the square building area, and try to get rid of any straight lines. If you don't like the results, press `Ctrl + Z` or `⌘ + Z`. ## Add details Right now, the island may look a bit jagged. Next, you'll smooth out the terrain and fine-tune it with the Subtract and Paint tools. 1. Select the **Smooth** tool. This will average out the highs and lows of terrain. Drag it around to make areas look more natural. > **Warning:** To better see what your terrain looks like, remember to rotate your camera and see terrain from multiple angles, not just always from the top. 2. Remove unwanted terrain with the **Subtract** tool. This can be useful to quickly delete terrain that's overtaken the city. 3. **Paint** sand and mud on the edges to create beaches. Paint changes the texture without changing the shape. An example of a map with terrain is below. --- title: "Add new models" url: /docs/en-us/education/build-it-play-it-galactic-speedway/add-new-models last_updated: 2026-06-29T19:33:59Z description: "Learn how to import more parts into Roblox Studio to customize the spaceship you built in the Build It Play It Galactic Speedway challenge." --- # Add new models Change your driftspeeder's look or create a new one with this kit of extra parts. Once you finish designing an even better speeder, get a new avatar item by testing your knowledge in a game. ## Add to your inventory Get extra parts to work with by importing an asset. Assets are parts of a Roblox game, like a 3D model or sound, that are stored online. 1. Open the link to the kit below in your web browser. 2. On the page for the asset, click the **Get** button to add it into your inventory. ## Import into the game All assets can be found in your **Inventory**. This stores any models you've added and lets you add them into a game. > **Info:****Position Your Camera Near the Work Area** The kit will be imported wherever your camera is positioned. To make sure your kit is imported near the repair station, move your camera there. 1. In Studio, find the **Toolbox**. If it's not visible, open it from the **Window** menu. 2. Select the **Inventory** tab. 3. Make sure the left dropdown is to **My Models**. Then, find and click on **Driftspeeder Parts**. 4. To make it easy to work, move the kit somewhere near your work area. 5. **Duplicate** (`Ctrl``D` or `⌘``D`) parts to improve your flyer or create a new one. Below are some example designs. ## Take the challenge Test your knowledge and earn the **Saturn Ring Hat** avatar item and **Junkyard Master** Badge by clicking the button below to open a Roblox game. Once you're done, be sure to **follow** the game to get updates on when **new prizes** and lessons come out! --- title: "Add to the garage" url: /docs/en-us/education/build-it-play-it-galactic-speedway/add-to-the-garage last_updated: 2026-06-29T19:33:59Z description: "Learn how to add your custom spaceship to the garage using the Roblox Studio Explorer as part of the Roblox Build It Play It Galactic Speedway challenge." --- # Add to the garage Remember how you flew a test speeder earlier? To fly your own speeder, it needs to be placed in the garage with the other vehicles. 1. Right-click on the driftspeeder. In the menu, select **Cut**. The speeder will disappear for now, but will reappear once you add it into the correct folder. 2. In the **Explorer** window, scroll to find a folder named **Garage**. > **Info:** If you don't see it the **Explorer** window, enable it from Studio's **Window** menu. 3. Instead of using paste, right-click on **Garage** and select **Paste Into**. Your speeder will then reappear. > **Warning:****Driftspeeder not appearing?** To get your speeder back, use undo (`Ctrl``Z` or `⌘``Z`) and repeat the process of **Cut** and **Paste Into**. 4. Name your driftspeeder something descriptive like **The Lightning**. 1. In the **Explorer** window, right-click on **Model**. A contextual menu displays. 2. In the menu, select **Rename**. 3. Type the new name and then finish by pressing `Enter`. > **Warning:****Making changes after grouping** To make changes to your speeder, just ungroup it and then make your changes. Group it all back together again when you're done. > > If you've added new parts, you'll have to move the model back to the garage: 1. Ungroup and make changes. 1. In the 3D viewport, right-click the driftspeeder, then select **Ungroup**. 2. Make your changes. 2. Group the parts back together. 1. Select all parts of the speeder. 2. Right-click anywhere in the 3D viewport, then select **Group** from the contextual menu (`Ctrl``G` or `⌘``G`). 3. Place back into the Garage folder if necessary. 1. In the **Explorer** window, make sure that your speeder is in the **Garage** folder. If not, move it back with **Cut** and **Paste Into**. 2. Name the driftspeeder by right-clicking on **Model** and selecting **Rename**. --- title: "Add wings" url: /docs/en-us/education/build-it-play-it-galactic-speedway/adding-wings last_updated: 2026-06-29T19:33:59Z description: "Learn how to use the Roblox Studio Move and Rotate tools to add wings to your spaceship in the Build It Play It Galactic Speedway challenge." --- # Add wings To work correctly in a race, the driftspeeder can only have one body. But, it can have as many wings and other parts as you'd like. ## Change snapping Before adding wings, set **rotate snapping** to make it easier to position your wings. Snapping rotates parts a set amount a time. 1. In the toolbar, make sure **Rotate** snapping is enabled. 2. Set it to `45°`. ## Place the first wing 1. In the 3D viewport, click a wing from the second section of the scrapyard, then use the **Move** tool to drag it into the work area. > **Warning:****Trouble moving the wing?** In the toolbar, make sure **Move** snapping is turned off. 2. In the toolbar, select the **Rotate** tool, then drag the handles in the 3D viewport to rotate the wings. Each handle rotates in a different direction. > **Info:****Rotate quickly with a hotkey** To rotate an object 90 degrees, select that part and press `Ctrl``R` or `⌘``R`. 3. In the toolbar, select the **Scale** tool and drag the handles to scale the wings to a desired size. 4. If you want, use the **matching wing** in the scrapyard for the other side of the driftspeeder. > **Warning:****Keep the driftspeeder in the work area** Make sure the speeder can fit through the canyon while racing by keeping all of the parts within the blue lines. --- title: "Colors and textures" url: /docs/en-us/education/build-it-play-it-galactic-speedway/colors-and-textures last_updated: 2026-06-29T19:33:59Z description: "Learn how to add custom textures to your spaceship parts in Roblox Studio as part of the Build It Play It Galactic Speedway challenge." --- # Colors and textures Give your driftspeeders a new paint job with updated texture files. Your speeders can now be stylishly jade, rusty orange, or even a classic blue. ## Download a texture To change the texture of your driftspeeder, start by downloading one or more of the textures below. | | | | | --- | --- | --- | > **Info:****Creating your own textures** Make your own designs by bringing any one of these textures into a tool like [Photoshop](https://www.adobe.com/products/photoshop) (paid) or [Krita](https://krita.org/) (open-source). Try variations like changing the color or adding new designs. ## Import a texture To use textures, they must be imported into your game. Make sure your game has been published before moving on. 1. To find the texture import option, select a **part** in your driftspeeder model. Do this by selecting the speeder and expanding it in the Explorer. 2. In the **Properties**, click the box next to **TextureID**. 3. To upload a new texture, click **Add Image**. > **Warning:** If you can't see the Add Image button, make sure that your game has been published. 4. Click **Choose File** and pick a downloaded texture. 5. Finish by clicking **Create**. You should see the updated texture on the ship. Continue to customize your speeder by adding more textures. _Original texture_ _With new texture_ > **Info:****Changing multiple textures at a time** Select multiple parts in the Explorer and follow the same process of changing a single part's texture. - **Select Individual Parts**: `Ctrl`‑click or `⌘`‑click - **Selecting Groups**: Click on one part. Then, `Shift`‑click on a part below to select all parts between them. ## Take the challenge Test your knowledge to earn the **Scrappers Backpack** avatar item and **As Many Colors as Stars** badge by clicking the button below to open a Roblox quiz game. Once you're done, be sure to **follow** the game to get updates on when **new prizes** and lessons come out! --- title: "Customize and share" url: /docs/en-us/education/build-it-play-it-galactic-speedway/customize-and-share last_updated: 2026-06-29T19:33:59Z description: "Learn how to modify a Luau script in Roblox Studio to customize how your spaceship handles and flies in the Build It Play It Galactic Speedway challenge." --- # Customize and share This last lesson is all about putting the final touches on the driftspeeder, such as making it faster or quicker to turn. Once finished, take flight and share your work with friends. ## Modify the Engine To change a driftspeeder's speed or how quickly it can turn, you'll open the **_Settings_** script. In Roblox, code is typed inside of scripts using the coding language [Luau](https://luau.org). Games often have separate scripts for each thing the game needs to do. 1. Click on your speeder. 2. In the **Explorer** window, look for the **Garage** folder and your driftspeeder. 3. Next to the driftspeeder's name, click the ▸ arrow to expand it. Then, find **Body** and click the ▸ to see the attached script. 4. Double-click the **Settings** script to open it. ## The Settings script In the script, you'll see words like `DefaultSpeed` or `BoostSpeed`. These words are **variables**. Variables are placeholders for information that can be changed as needed. ```lua -- ================================================================================ -- Settings -- ================================================================================ local Settings = {} Settings.DefaultSpeed = 100 -- Speed when not boosted [Studs/second, Range 50-300] Settings.BoostSpeed = 200 -- Speed when boosted [Studs/second, Maximum: 400] Settings.BoostAmount = 10 -- Duration of boost in seconds Settings.Steering = 5 -- How quickly the speeder turns [Range: 1-10] -- ================================================================================ return Settings ``` Make your speeder the fastest on the track by increasing the default speed setting. 1. Change the number in the line `Settings.DefaultSpeed = 100` to any number between `50` (slower) and `300` (faster).```lua Settings.DefaultSpeed = 100 -- Speed when not boosted [Studs/second, Range 50-300] Settings.BoostSpeed = 200 -- Speed when boosted [Studs/second, Maximum: 400] Settings.BoostAmount = 10 -- Duration of boost in seconds Settings.Steering = 5 -- How quickly the speeder turns [Range: 1-10] ``` 2. Playtest and see how the change feels. Ask yourself if the speed fits the driftspeeder's design and if you'd be excited using it in a race._`Settings.DefaultSpeed = 50`__`Settings.DefaultSpeed = 300`_ 3. Stop the playtest when finished. Remember, because you can't use your mouse while flying, press `E` to exit the speeder so you can stop the playtest. > **Info:****Making multiple speeders** If you make multiple speeders, make each one feel unique by giving them different strengths and weaknesses. One speeder might be slower normally, but have a faster boost. Another speeder might be very fast, but slow to turn. --- title: "Design the world" url: /docs/en-us/education/build-it-play-it-galactic-speedway/design-the-world last_updated: 2026-06-29T19:33:59Z description: "Learn how to use the Roblox Studio terrain tools to customize the racetrack world in the Build It Play It Galactic Speedway challenge." --- # Design the world Change the season of Junker's Canyon or make it look even more alien by painting new textures and colors onto the landscape. ## Paint textures You can use the **Terrain Paint** tool to add snow, water, or even lava into your world. 1. From the **Home** tab, click **Terrain** to open the **Terrain Editor**. 2. The editor will open on the left. To paint: a. Click on the **Edit** Tab. b. Then, click on the **Paint** tool. 3. Select a material, such as Snow. To paint, left click and drag on any terrain. Paint changes the look without changing the shape. 4. Paint terrain until your game looks completely different from the original. Experiment with different materials, like grass or water, and brush sizes. ## Change texture colors Being on an alien planet, not all grass has to be green. Try changing the color of the terrain you paint, turning snow purple or even black. 1. In the Explorer, under **Workspace**, click on **Terrain**. 2. In the **Properties**, click the > next to expand `MaterialColors`. 3. Find a material you painted with, like **Snow**, and click the **color** box next to it. 4. Use the color picker to change the terrain color. ## Take the challenge Test your knowledge and earn the **Jetpack Backpack** avatar item and **Terraformer Technician** badge by clicking the button below to open a Roblox quiz game. ## Get your final prize Not all junkyard racers have gotten this far, but **you** have! As a prize for developing your skills, follow this link to get your own **Junkbot** avatar package! --- title: "Design your speeder" url: /docs/en-us/education/build-it-play-it-galactic-speedway/designing-your-speeder last_updated: 2026-06-29T19:33:59Z description: "Learn the high level process of designing a spaceship in Roblox Studio, as well as how to move the Studio camera so you can see the parts you have to build with. All part of the Roblox Build It Play It Galactic Speedway challenge." --- # Design your speeder Now that you've piloted a driftspeeder, it's time to design your own. The speeder will be built in three steps: _1: Body_ _2: Wings_ _3: Decorations_ ## Salvage parts The scrapyard is divided into three sections, one for each step. By combining, rotating, and resizing these parts, you can create thousands of unique designs. _Body Parts_ _Wing Parts_ _Decorative Parts_ ## Get ready to build To move parts where you want them, first turn off **collisions** and **move snapping**. Turning off collisions allow objects to pass through one another. Having snapping off lets you freely move objects. 1. In the toolbar, click the **Geometric** button and make sure **Collisions** is turned off. 2. Make sure that **Move** snapping is also turned **off**. ## Change the camera view To work on the driftspeeder, use the camera controls below. | Action | Control | | --- | --- | | **Move** | `W` `A` `S` `D` | | **Rotate** | Hold the right mouse button and look around | | **Zoom** | Use the scroll wheel | | **Focus** | Press `F` to focus the camera on a specific part | Move the camera so that you can better see the driftspeeder bodies and the work area at the same time. > **Warning:****Can't move the camera?** If the camera doesn't move when you press `W` `A` `S` `D`, click somewhere inside the game world. --- title: "Duplicate and design" url: /docs/en-us/education/build-it-play-it-galactic-speedway/duplicate-and-design last_updated: 2026-06-29T19:33:59Z description: "Learn how to duplicate parts and add decorations to spaceship in Roblox Studio as part of the Build It Play It Galactic Speedway challenge." --- # Duplicate and design As you build, parts can be used more than once by creating duplicates. This is useful for reusing a part from the scrapyard, or copying something already added to the speeder. 1. Click the wing you just added and duplicate it (`Ctrl``D` or `⌘``D`). Nothing obvious will happen in the 3D viewport because with collisions off, the wing overlaps the original. 2. Use the **Move**, **Rotate**, and **Scale** tools to position it differently than the first. > **Info:****Get Precise Rotation** Instead of rotating by a set amount, you can rotate freely by turning off rotation snapping. In the toolbar, next to **Rotate**, uncheck the box. Just remember to turn it back on when you're done. ## Continue building Now that you know how to work with parts, add as many wings as you'd like to create a cool design. Use the following designs as inspiration: ## Add decorations Further personalize your speeder with decorative parts like thrusters or spoilers from the third section of the scrapyard. Below are two different speeders with decorations. > **Info:****Create Bigger Ships with Extensions** Build further away from the body by using parts to extend where you can place objects. --- title: "Get off the ground" url: /docs/en-us/education/build-it-play-it-galactic-speedway/get-off-the-ground last_updated: 2026-06-29T19:33:59Z description: "Learn how to download and set up your project using a Roblox Studio template for the Build It Play It Galactic Speedway challenge." --- # Get off the ground All of the parts for building your driftspeeder and the code for the annual Junker's Canyon Race are included in the **Galactic Speedway** template. ## Open the template Time to open Roblox Studio! If you need to, [download Studio](https://www.roblox.com/create), then click the following button to open the **Galactic Speedway** template. ## Junker's Canyon With the template open, you can see Junker's Canyon, home to crafty builders and daring racers. To build your driftspeeder, **salvage** parts from the scrapyard and combine them in the work area. Once you're done, race your driftspeeder over the **racetrack waters** to be the champion of Junker's Canyon. ## Close extra windows The first time you launch Roblox Studio, extra windows might open up that aren't needed right now. Closing the extra windows will give you more space to work. You can always open them up again later. 1. Close each window on the **left** of the 3D view by clicking the **×** in the window's upper-right corner. If you don't see anything to close on the left, go to the next step. 2. Leave the **Explorer** and **Properties** windows open on the right side of Studio. If you have done each step correctly, Studio looks like the following image. > **Warning:** If you don't see the **Explorer** and **Properties** windows on the right, toggle them on from Studio's **Window** menu. --- title: "Go beyond the challenge" url: /docs/en-us/education/build-it-play-it-galactic-speedway/go-beyond-the-challenge last_updated: 2026-06-29T19:33:59Z description: "Pick what you would like to learn next in the Build It Play It Galactic Speedway challenge to further customize your game in Roblox Studio." --- # Go beyond the challenge There are still more rewards to earn and more ways to personalize your driftspeeder! So far, you've learned how to build a custom **3D model** and modify its engine speed with **code**. Use your new skills to build levels, decorative props, or even your own alien landscape. Below are a few ways to continue customizing your game and earn more virtual prizes. ## Other cool tricks | | | | --- | --- | | **Share Driftspeeders** Send your speeder to friends or use their own. | **Add Particles** Have thrusters shoot fire, make your ship emit sparks, and more. | | | | ## Unlock more virtual prizes Add a little extra style to your driftspeeder and earn more rewards on Roblox. | | | | | --- | --- | --- | | **Add New Models** Import new models to add variety to your speeders. | **Colors and Textures** Give your driftspeeder a new style by customizing its colors. | **Design the World** Customize the environment by painting snow or water. | | | | | --- title: "Going for a race" url: /docs/en-us/education/build-it-play-it-galactic-speedway/going-for-a-race last_updated: 2026-06-29T19:33:59Z description: "Learn how to race your spaceship in the Roblox Build It Play It and customize your published game so that your friends can play with you." --- # Going for a race With your driftspeeder fully customized, it's time to test it out in an actual race! 1. Click **Play**. 2. Walk up to the robot, start a new race, and pick your driftspeeder. See if you can go through each ring. ## Invite friends to play When you first publish a game, it's automatically set to private. Set it to **public** so others can get a chance to race in your speeder. 1. **Publish** your game so your friends will get your latest changes. 2. Open **File** ⟩ **Experience Settings**. 3. On the left bar, select **Permissions**, then choose **Public**. 4. Click the **Save** button. --- title: "Group the parts" url: /docs/en-us/education/build-it-play-it-galactic-speedway/grouping-the-parts last_updated: 2026-06-29T19:33:59Z description: "Learn how to group parts together to create models in Roblox Studio as part of the Build It Play It Galactic Speedway challenge." --- # Group the parts The driftspeeder is almost complete! Everything just needs to be **grouped** into a single model. Once grouped, the code attached to the body will work for the entire speeder. 1. Before grouping, get a good view of your entire design by positioning your camera above it, then click and drag to select **only** the driftspeeder parts. 2. Right-click anywhere and select **Group** (`Ctrl``G` or `⌘``G`) to combine everything into one model. 3. Before moving on, double-check that everything is grouped by moving the driftspeeder. If any pieces were left behind, or you grouped objects not part of the speeder, undo the grouping (`Ctrl``Z` or `⌘``Z`) and try again. --- title: "Galactic Speedway" url: /docs/en-us/education/build-it-play-it-galactic-speedway/main-page last_updated: 2026-06-29T19:33:59Z description: "Learn how to build your own custom spaceship models in Roblox Studio as part of the Build It Play It Galactic Speedway challenge." --- # Galactic Speedway Enter the space race of the millennia! No ship, no problem. All racers will be asked to design custom driftspeeders using what can be found on the abandoned galactic spaceyard of Junker's Canyon. Using Roblox Studio, learn how to combine parts to make spaceships that can survive racing in this beautiful alien ~~deathtrap~~, _ahem_ landscape. ## Download Roblox Studio To start, you'll need Roblox Studio. This program is used to create every game on Roblox and is available free for **PC** and **Mac**. If you've played Roblox games on your computer, you already have Studio installed. While creating, we strongly recommend using a **3-button mouse with a scroll wheel**. > **Info:** If you're completely new to Roblox, follow these instructions to get started: 1. [Sign up for an account.](https://www.roblox.com/account/signupredir) 2. Download [Roblox Studio.](https://www.roblox.com/create) 3. After installing, double-click the Roblox Studio desktop icon (Windows) or click the dock icon (Mac). 4. On the login screen, enter your Roblox username and password, then click **Log In**. > > With your new account, remember these safety tips: - **Never share your password**, even with a real life friend. - **Make your password hard to guess** -- If your username is "bloxdev," your password should not be "bloxdev123." - **Roblox employees will never ask for your password** -- Report anyone who asks using the [Report Abuse](https://en.help.roblox.com/hc/articles/203312410)feature. - **There's no such thing as free Robux** -- Never trust players or sites who say they have a secret way to get free Robux! > > For more tips, please see [Keeping Your Account Safe](https://en.help.roblox.com/hc/en-us/articles/203313380-Account-Security-Theft-Keeping-your-Account-Safe-). ## Three lessons, three prizes To build the driftspeeder and add it to the race, follow along with each of the three step-by-step tutorials. _Get Off the Ground_ _Adding Wings_ _Customize and Share_ After each lesson, you'll get the chance to earn avatar items and badges by playing a quiz game on Roblox. --- title: "Move the body" url: /docs/en-us/education/build-it-play-it-galactic-speedway/moving-the-body last_updated: 2026-06-29T19:33:59Z description: "Learn how to move parts in Roblox Studio to build your own spaceship as part of the Build It Play It Galactic Speedway challenge." --- # Move the body There are six different bodies to choose from in the scrapyard. Each body can be combined with other parts for a completely different look. _Original Body_ _Possible Design_ _Possible Design_ 1. In the toolbar, select the **Move** tool. 2. Select the body you want. Then, drag the **directional arrows** to move it into the center of the work area. --- title: "Save and publish" url: /docs/en-us/education/build-it-play-it-galactic-speedway/save-and-publish last_updated: 2026-06-29T19:33:59Z description: "Learn how to save all the progress you have made building a spaceship in the Roblox Build It Play It Galactic Speedway challenge." --- # Save and publish It's a good idea to publish your game every ten minutes while you're working or whenever you make a big change. Once published, you can edit it from any computer. For now, your game will be private, but once the driftspeeder is ready, you'll make it public for everyone to play. 1. Select **File** → **Publish to Roblox** to open the publishing window. 2. Enter a place name and an optional description. 3. When ready, click the **Create** button. > **Info:****Saving after publishing** Next time you want to save your work, just go to **File** ⟩ **Publish to Roblox** or use the hotkey combo `Alt``P` or `⌘``P`. --- title: "Share your speeders" url: /docs/en-us/education/build-it-play-it-galactic-speedway/sharing-your-speeders last_updated: 2026-06-29T19:33:59Z description: "Learn how to share the spaceship models you build in Roblox Studio as part of the Build It Play It Galactic Speedway challenge with your friends." --- # Share your speeders Swap driftspeeders with your friends by sharing it as an asset. Assets are parts of a Roblox game, like a 3D model or sound, that are stored online. To share your speeder with friends: 1. Select your driftspeeder. In the **Explorer** window, right-click the highlighted speeder model and select **Save to Roblox** from the contextual menu. The **Asset Configuration** window displays. 2. Give the model a **title** and **description**. This information is used so other players can search for new models and understand what they're downloading. 3. Click the **Submit** button. The **Asset Configuration** window displays two links to see your asset on the Creator Dashboard. 4. Click the link to the **Configure** page. Your default web browser opens. 5. Under the **Distribution** section, enable the **Distribute on Creator Store** toggle. This allows your friends to copy your speeder into their inventory. ## View and use models If you've added models from friends, or just want to reuse your own, find them in the **Inventory**. This stores any models you've added and lets you add them into a game. 1. Ask a friend to share the link to their 3D model asset. Each link should follow a formula like this: `www.roblox.com/library/0123456789/My-Speeder` 2. Open the link in your web browser. 3. On the page for the asset, click the **Get Model** button to add it into your inventory. 4. In Studio, find the **Toolbox**. If it's not visible, open it from the **Window** menu. 5. In the **Toolbox** window, click the icon for **Inventory**. 6. Make sure the left dropdown is set to **My Models**, then find and click on your friend's speeder. This will add the speeder into your experience. > **Warning:****Thumbnail Blank?** If you just uploaded a model, you might see a temporary placeholder instead of a thumbnail. It may take some time until the new asset has been reviewed by Roblox moderators. --- title: "Take flight" url: /docs/en-us/education/build-it-play-it-galactic-speedway/take-flight last_updated: 2026-06-29T19:33:59Z description: "Learn the character movement and flight controls for the Roblox Build It Play It Galactic Speedway template." --- # Take flight Before designing your own driftspeeder, try flying a test speeder that's already in the garage to get an idea of the environment. 1. Click **Play** to test. 2. In the experience, walk up to the robot character under the spaceship sign and press `E` to open up a menu. On that screen, click **Race**. In a race, you'll fly through rings to make the fastest time. Below are controls for in-game. | Action | Control | | --- | --- | | **Move** | `W` `A` `S` `D` or arrow keys | | **Rotate** | Hold the right mouse button and look around. | | **Pan** | Hold the middle mouse button to drag your camera around. | 3. For now, there's only one driftspeeder in the garage to choose from. Select it and then click **Start**. 4. Wait for the countdown to finish. While inside, take a minute to get used to flying with the controls below. Since you can't use the mouse while flying, remember to **exit** the speeder by pressing `E`. Below are controls for the speeder. | Action | Control | | --- | --- | | **Move the Speeder** | `W` `A` `S` `D` or arrow keys | | **Boost Speed** | `Shift` | | **Exit the Speeder** | `E` | 5. Stop playing by pressing the red square or press `Shift``F5`. --- title: "Take the challenge" url: /docs/en-us/education/build-it-play-it-galactic-speedway/take-the-challenge-2 last_updated: 2026-06-29T19:33:59Z description: "Take the second challenge of Roblox Build It Play It Galactic Speedway and earn the Scrap Metal Hard Hat avatar item and Galactic Mechanic badge." --- # Take the challenge Test your knowledge and earn the **Scrap Metal Hard Hat** avatar item and **Galactic Mechanic** badge! 1. Click **Play Quiz Game** below to open the **Roblox Creator Challenge** game. 2. In the game, start the challenge by interacting with the robot. Answer questions about what you just learned to complete the challenge. --- title: "Take the challenge" url: /docs/en-us/education/build-it-play-it-galactic-speedway/take-the-challenge-3 last_updated: 2026-06-29T19:33:59Z description: "Take the third challenge of Roblox Build It Play It Galactic Speedway and earn the Alien Friend avatar item and Taking the Victory Lap badge." --- # Take the challenge Test your knowledge and earn the **Alien Friend** avatar item and **Taking the Victory Lap** badge! 1. Click **Play Quiz Game** below to open the **Roblox Creator Challenge** game. 2. In the game, start the challenge by interacting with the robot. Answer questions about what you just learned to complete the challenge. --- title: "Take the challenge" url: /docs/en-us/education/build-it-play-it-galactic-speedway/take-the-challenge last_updated: 2026-06-29T19:33:59Z description: "Take the first challenge of Roblox Build It Play It Galactic Speedway and earn the Supernova Pauldrons avatar item and Getting off the Ground badge." --- # Take the challenge Test your knowledge and earn the **Supernova Pauldrons** avatar item and **Getting off the Ground** badge! 1. Click **Play Quiz Game** below to open the **Roblox Creator Challenge** game. 2. In the game, start the challenge by interacting with the robot. Answer questions about what you just learned to complete the challenge. --- title: "Test the speeder" url: /docs/en-us/education/build-it-play-it-galactic-speedway/test-the-speeder last_updated: 2026-06-29T19:33:59Z description: "Learn how to take your custom built spaceship on a test flight in Roblox Studio as part of the Build It Play It Galactic Speedway challenge." --- # Test the speeder Before moving on, take time to check that your speeder works correctly. 1. Click **Play** to test. 2. In the experience, walk up to the robot character under the spaceship sign and press `E` to open up a menu. On that screen, select **Race** and pick your speeder in the selection menu. 3. While flying, make sure that your driftspeeder looks as you intended. Then **stop** the playtest and, if needed, fix any issues. Remember, you can stop playtesting with `Shift``F5`. ## Troubleshoot the speeder If your driftspeeder does not show up in the selection screen, check the following steps: **Make sure the speeder is in the Garage.** 1. Click on your speeder to help locate it in the **Explorer** window. 2. In the **Explorer** window, check that it's inside the **Garage** folder. 3. If not in **Garage**, use **Cut** and **Paste Into** to move it there. **Check that the speeder has a Body.** The code in game only loads speeders that have a Body part grouped within its model. _> **Success:** No extra models_ _> **Error:** Extra model that causes issues_ --- title: "Add animations" url: /docs/en-us/education/build-it-play-it-island-of-move/adding-animations last_updated: 2026-06-29T19:33:59Z description: "Learn how to make animations in Roblox Studio with step by step tutorials in this one hour challenge. Use scripts to add animations into an experience." --- # Add animations To implement animations in-game, use scripts. For this tutorial, you'll implement the previously made victory animation using a pre-made script. Once finished, this animation can be used to celebrate a player's accomplishment, like reaching the end of an obby or finding a secret. ## Script animations Animations are triggered using **scripts**. One approach is to use events to play animations in a variety of situations, like a player finishing a level, defeating an enemy, or even making an in-game purchase. ### Set up the project For this project, you'll create parts that when touched, trigger an animation for that player. 1. To organize all parts that will play the animation, add a folder in Workspace (hover over Workspace and click +) named TouchPartFolder. 2. In TouchPartFolder, add a part. Make sure the part is placed where playing the animation would make sense, such as the end of a level or near an object players collect._Example Victory Part__Example Interaction Part_ 3. In StarterPlayer > StarterCharacterScripts, create a LocalScript named TouchPartRegister. Then copy and paste the code below.```lua -- Used with "PlayerAnimationFeedback" script to play animations on part touches -- Services local ReplicatedStorage = game:GetService("ReplicatedStorage") local Players = game:GetService("Players") local player = Players.LocalPlayer local character = player.Character or player.CharacterAdded:Wait() local humanoid = character:WaitForChild("Humanoid") local canTouch = false -- Include feedback animation module local PlayerAnimationFeedback = require(ReplicatedStorage:WaitForChild("PlayerAnimationFeedback")) -- Function called when a part is touched local function onPartTouch(otherPart) if humanoid and canTouch == false then canTouch = true PlayerAnimationFeedback:PlayAnimation() canTouch = false end end -- On startup, call animation module load function PlayerAnimationFeedback:LoadAnimation(humanoid) -- Also bind a folder of parts to the "Touched" event to run "onPartTouch()" local touchPartFolder = workspace:WaitForChild("TouchPartFolder") local touchParts = touchPartFolder:GetChildren() for _, touchPart in touchParts do touchPart.Touched:Connect(onPartTouch) end ``` This script finds all parts in the TouchPartFolder and gives them `Touched()` events. When fired, the event runs a function that plays an animation for a player. > **Info:** While this script uses `Touched()`, different events like `MouseClick()` or `Changed()` can also be used. Additionally, your own game events, like awarding a player points can be fired from the TouchPartRegister script. To see an example that can be added, check out the article on Health Pickups for code. 4. The next script triggers animations for a player. In ReplicatedStorage, create a new ModuleScript named PlayerAnimationFeedback. Then, copy and paste the code below.```lua -- Used with "TouchPartRegister" script to play animations for a player local PlayerAnimationFeedback = {} local feedbackAnimationTrack local ANIMATION_FADE = 0.3 local ANIMATION_ID = "rbxassetid://YOUR_ANIMATION" -- Function to load animation onto player's character function PlayerAnimationFeedback:LoadAnimation(humanoid) local feedbackAnimation = Instance.new("Animation") feedbackAnimation.AnimationId = ANIMATION_ID feedbackAnimationTrack = humanoid.Animator:LoadAnimation(feedbackAnimation) feedbackAnimationTrack.Priority = Enum.AnimationPriority.Action feedbackAnimationTrack.Looped = false end -- Function to play the animation function PlayerAnimationFeedback:PlayAnimation() feedbackAnimationTrack:Play(ANIMATION_FADE) task.wait(feedbackAnimationTrack.Length) end return PlayerAnimationFeedback ``` ## Play animations Animations must be identified in a script, loaded, and played. ### Set the animation The script needs to know which animation to play. To use an exported animation, find its **asset ID** through a web browser. That ID will then allow that animation to be loaded in the script. 1. Open the [Animations](https://www.roblox.com/develop?View=24) section of the Create page. 2. Locate and click an exported animation. 3. Copy its ID from the URL in your browser. 4. In the script, PlayerAnimationFeedback, replace the placeholder, `YOUR_ANIMATION` (Line 8), with the **ID** you copied. 5. Run the project and test that once a player hits the part, you see the animation. ## Next steps Below are a few ways to continue learning ### Learn about animation So far, you've learned how to create animations and add them into experiences. To continue learning, we recommend visiting the [Animation](/docs/en-us/animation.md) overview. On that page, you'll find useful links to improving animations, such as using the curve editor for smooth movement, or tips in refining animations. ### Animate parts Additionally, start the optional lesson [Animating Parts](/docs/en-us/education/build-it-play-it-island-of-move/animating-parts.md) to learn how to code tweens, a feature that lets you scale, rotate, and move parts. A sample of the final project is below. --- title: "Add new models" url: /docs/en-us/education/build-it-play-it-island-of-move/adding-new-models last_updated: 2026-06-29T19:33:59Z description: "Part of the Roblox Build It, Play It Challenge - Island of Move. Decorate the template." --- # Add new models Create new buildings or expand the current ruins with this kit of extra parts. Once you finish designing a more visually impressive world, get a new avatar item. ## Add to your inventory Get extra parts to work with by importing an asset. **Assets** are parts of a Roblox game, like a 3D model or sound, that are stored online. 1. Open the link to the kit below in your web browser. 2. On the page for the asset, click the **Get** button to add it into your inventory. ## Import into the game All assets can be found in your **Inventory**. This stores any models you've added and lets you add them into a game. 1. In Studio, find the **Toolbox**. If it's not visible, open it from the **Window** menu. 2. Select the **Inventory** tab. 3. Make sure the left dropdown is set to **My Models**. Then, find and click on **Island of Move Extra Props**. This will add the parts into your game. ## Challenge checkpoint You've customized your game, now claim your free prize! Celebrate your new skills by getting the **Crystalline Companion** and **Bringing the World Alive** badge. Copy the code below. `WorldAlive` Click the button to open a Roblox game, find the in-game character, and click **REDEEM CODE** to get your prize. --- title: "Add the second pose" url: /docs/en-us/education/build-it-play-it-island-of-move/adding-the-second-pose last_updated: 2026-06-29T19:33:59Z description: "Part of the Roblox Build It, Play It Challenge - Island of Move. Create the second pose." --- # Add the second pose Next move to the middle of the timeline and start working on the second pose. After designing the pose, Roblox Studio will connect everything to create a looping animation. 1. To place the second pose in the middle, set the current time to `0:15`. 2. Use **Rotate** to create the second pose. Remember to move the camera around while working. > **Info:****Posing Faster** Speed up posing by rotating body parts connected to the torso first, like the upper arm and thigh. Then, work outwards to pose the hands and feet. > **Warning:****Start a Pose from Scratch** Don't like a pose and want to just start from the beginning? You can reset any pose to the starting default. 1. Right click on the top white diamond of the pose you want to reset. 2. Select **Reset Selected**. ## Play the animation Time to see the animation in action! You'll want to turn on looping so that the animation doesn't stop right away and you can get a better idea of how it will look in-game. 1. To turn on looping, click the **Loop** icon. 2. To play the animation, press `Space`. 3. To stop testing, press `Space` again. --- title: "Animate parts" url: /docs/en-us/education/build-it-play-it-island-of-move/animating-parts last_updated: 2026-06-29T19:33:59Z description: "Learn how to move parts in Roblox Studio by coding tweens. Rotate, scale, and change the colors of objects." --- # Animate parts Players can get feedback to whether or not their actions are doing anything with animations. This can be a tree shaking when hit, or a pet cheerfully bouncing when fed. In this case, we'll show you how to make a button that moves when the player clicks it. ## Tween with animations One way of animating objects is using tweens. A **tween**, short for in-between, is the process of changing a starting value to an ending value over a certain amount of time. Tweens can be used to change properties such as position, color, or rotation. ### Project setup For this project, a tween will move an object from a starting position to a goal position. 1. Determine what part you'll be animating. In the example below, `1` is the button and `2` is the wall. 2. In the part that will move, add a ClickDetector and a script named TweenMove. Do not rename the ClickDetector. > **Warning:** If you're using a model with many parts, pick one part to move (such as the button in this example), and put the MoveTween script as its child. 3. Open the TweenMove script. Then, copy and paste the script below.```lua -- Tweens a part back and forth based on a chosen axis -- The button has an on/off state which moves the part forwards or backwards local TweenService = game:GetService("TweenService") local button = script.Parent local clickDetector = button:FindFirstChildWhichIsA("ClickDetector") local buttonState = -1 -- Determines button direction; -1 means it will press in, 1 means it will pop out local inTween = false -- Customizable variables local TWEEN_TIME = 0.15 local TWEEN_MOVE_DISTANCE = 1.5 -- Tween variables local buttonTweenInfo = TweenInfo.new( TWEEN_TIME, -- Time Enum.EasingStyle.Quad, -- EasingStyle Enum.EasingDirection.Out -- EasingDirection ) local function buttonPress() -- If the button is tweening, prevent it from being used again if inTween == true then return end -- Calculate new CFrame for button position local offsetCFrame = CFrame.new(0, TWEEN_MOVE_DISTANCE * buttonState, 0) local newCFrame = button.CFrame:ToWorldSpace(offsetCFrame) -- Create a tween and play it local tweenButton = TweenService:Create(button, buttonTweenInfo, {CFrame = newCFrame}) tweenButton:Play() inTween = true -- On tween completion, make button usable again tweenButton.Completed:Connect(function() inTween = false -- Invert the button state buttonState *= -1 end) end clickDetector.MouseClick:Connect(buttonPress) ``` 4. Playtest and click the object. While testing, you may notice the object moves the wrong direction or distance. That can be customized in the next section. _Wrong direction; needs adjustment_ _Moves correctly; no adjustment needed_ ## Adjust the move tween With the script added, you can customize it to suit the needs of your game. The button can be tweened to move in directions like forward and back, or up and down. ### Change position Remember, tweens transition from a starting value to a goal value. This script uses [CFrames](/docs/en-us/workspace/cframes.md) to move the button in a direction relative to its current position. That direction is controlled in **Line 28** of the script. On that line, a new CFrame is created using X, Y, and Z coordinates. In this example, the button moves relative to its Y axis. ```lua -- Calculate new CFrame for button position local offsetCFrame = CFrame.new(0, TWEEN_MOVE_DISTANCE * buttonState, 0) local newCFrame = button.CFrame:ToWorldSpace(offsetCFrame) ``` To have the button move in a **different** direction, simply replace one of the desired coordinates with the line: `TWEEN_MOVE_DISTANCE * buttonState`. Set all other coordinates to 0. For example, this line will move it relative to its X axis instead. ```lua -- Calculate new CFrame for button position local offsetCFrame = CFrame.new( TWEEN_MOVE_DISTANCE * buttonState, 0, 0) local newCFrame = button.CFrame:ToWorldSpace(offsetCFrame) ``` ### Tween time and distance The script includes the following variables that affect the tween's movement. Capitalized names like `TWEEN_TIME` are written specifically for this script to be modified. Try changing the value of one variable below: - Line 11 - `TWEEN_TIME`: The time in seconds it takes for a tween to happen. - Line 12 - `TWEEN_MOVE_DISTANCE`: The distance the button moves in studs. ## Tween other properties Most properties with a numerical data type can be tweened. This section includes some common properties, like rotation, that can be used to bring more player feedback into your game. Keep in mind, you can also tween multiple properties at once. For a full list of properties, see the `Class.TweenService` API page. ### Rotate For rotation, combine tweening and CFrames instead of just using Vector3. Rotation tweens can be used to make creatures wiggle when pet, or coins twirl as they're picked up. In the next script example, the creature's position and rotation are both tweened when clicked to show how happy it is when being pet. The code below will rotate an object based off the number of degrees in `TWEEN_ROT_ANGLES`. ```lua -- Uses tween to make object rotate up and bounce local TweenService = game:GetService("TweenService") local partToTween = script.Parent local clickDetector = partToTween:FindFirstChildWhichIsA("ClickDetector") local inTween = false -- Customizable variables local TWEEN_TIME = 0.25 local TWEEN_ROT_ANGLES = -45 local TWEEN_MOVE_DISTANCE = 3 -- Tween variables local tweenInfo = TweenInfo.new( TWEEN_TIME, -- Time Enum.EasingStyle.Quad, -- EasingStyle Enum.EasingDirection.Out, -- EasingDirection 1, -- RepeatCount (when less than zero the tween will loop indefinitely) true -- Reverses (tween will reverse once reaching its goal) ) local function activateAction() -- If the object is tweening, prevent it from being tweened again if inTween == true then return end -- Calculate new CFrame for object position and rotation local offsetCFrame = CFrame.new(0, TWEEN_MOVE_DISTANCE, 0) local rotatedCFrame = CFrame.Angles(math.rad(TWEEN_ROT_ANGLES), 0, 0) offsetCFrame = offsetCFrame:ToWorldSpace(rotatedCFrame) local newCFrame = partToTween.CFrame:ToWorldSpace(offsetCFrame) -- Create a tween and play it local tweenObject = TweenService:Create(partToTween, tweenInfo, {CFrame = newCFrame}) tweenObject:Play() inTween = true -- On tween completion, make object clickable again tweenObject.Completed:Connect(function() inTween = false end) end clickDetector.MouseClick:Connect(activateAction) ``` ### Scale Changing the size of in-game objects can also show that a player interacted with them. For instance, pickups like health packs or gemstones can shrink when touched. To scale parts, change the tween goal to a new Vector3 of the desired size. ```lua -- Tween the scale of a part when it's touched, then destroy it local TweenService = game:GetService("TweenService") local partToScale = script.Parent local inTween = false -- Customizable variables local TWEEN_TIME = 1 local TWEEN_SCALE = Vector3.zero -- Tween variables local tweenInfo = TweenInfo.new( TWEEN_TIME, -- Time Enum.EasingStyle.Exponential, -- EasingStyle Enum.EasingDirection.Out -- EasingDirection ) local function onPartTouch(otherPart) -- If the object is tweening, prevent it from being tweened again if inTween == true then return end local partParent = otherPart.Parent local humanoid = partParent:FindFirstChildWhichIsA("Humanoid") if humanoid then -- Prevent further collisions on object since it has been picked up partToScale.CanCollide = false -- Create a tween and play it local tweenObject = TweenService:Create(partToScale, tweenInfo, {Size = TWEEN_SCALE}) tweenObject:Play() inTween = true -- On tween completion, destroy object tweenObject.Completed:Connect(function() partToScale:Destroy() end) end end partToScale.Touched:Connect(onPartTouch) ``` ### Color change Parts can transition colors by tweening with Color3 values. For instance, enemies or destructible objects can flash colors to give feedback that they've been hit or clicked. ```lua -- Tween an object's color upon click and destroy it after 3 clicks local TweenService = game:GetService("TweenService") local partToTween = script.Parent local clickDetector = partToTween:FindFirstChildWhichIsA("ClickDetector") local inTween = false -- Customizeable variables local TWEEN_TIME = 0.2 local COLOR_ON_HIT = Color3.fromRGB(255, 0, 50) local hitCount = 0 -- Tween variables local tweenInfo = TweenInfo.new( TWEEN_TIME, -- Time Enum.EasingStyle.Exponential, -- EasingStyle Enum.EasingDirection.InOut, -- EasingDirection 0, -- RepeatCount (when less than zero the tween will loop indefinitely) true -- Reverses (tween will reverse once reaching its goal) ) local function activateAction() -- If the object is tweening, prevent it from being tweened again if inTween == true then return end -- Create a tween and play it local tweenObject = TweenService:Create(partToTween, tweenInfo, {Color = COLOR_ON_HIT}) tweenObject:Play() inTween = true -- On tween completion, make object clickable again tweenObject.Completed:Connect(function() inTween = false -- Increment hit count hitCount += 1 -- After 3 hits, destroy the object if hitCount == 3 then partToTween:Destroy() end end) end clickDetector.MouseClick:Connect(activateAction) ``` While this script uses a normal Part to change color, it's also possible to use this on MeshParts with an applied texture. MeshParts are often imported 3D models, rather than parts build directly in Roblox Studio. If you are working with a fully textured MeshPart, we advise the following: 1. Use a SpecialMesh instead of a MeshPart. This is because a MeshPart's texture overrides the color being tinted by the script. 2. Change the script to modify the VertexColor of the SpecialMesh instead of the Color property. ## Project sample View all the script examples in this non-copylocked place. Get all the scripts to modify as well. | | Includes scripts for rotation, scaling, and color change tweens. | | --- | --- | --- title: "Animations and feedback" url: /docs/en-us/education/build-it-play-it-island-of-move/animations-and-feedback last_updated: 2026-06-29T19:33:59Z description: "Learn how to make animations in Roblox Studio with step by step tutorials in this one hour challenge. Start by creating an animation." --- # Animations and feedback Established developer? Just getting started? Create and code a victory animation to celebrate player achievements in this Build It, Play It Challenge! When finished, add this animation into any experience, making your work more engaging and attractive to players. These types of animations are perfect for giving feedback when a player wins a match, reaches a checkpoint, or levels up. Below is an example of the animation you can expect to produce by the end of this series. ## Animations and feedback Before learning to create an animation, take a moment to think about how animations and feedback are connected. Imagine an adventure game where you fight monsters. After hitting a monster, nothing happens - it doesn't react and there's no sound effect. You might think that the game is broken, but after a few more hits, the monster just disappears. This scenario is frustrating since the monster fails to give the user any **feedback**. Like real life, whenever players take **action** in-game, they expect a **reaction**. Designing informative and visually appealing feedback is critical for designing successful experiences that engage players. One way of giving feedback is with animations. Whenever a player performs an action, like hitting an enemy, animations can show a reaction, like the opponent being struck. The reaction tells the player their action did something, making them feel a part of the world and reducing the chance of confusion. ## Project setup The first step in making a victory animation is to create an animation using the Animation Editor. Later, you'll hook it up with code to celebrate a player's in-game accomplishment. That code will work in any experience you've created. ## Set up for animation Roblox Studio features a powerful, built-in **Animation Editor** that allows you to design and publish custom animations. 1. Start or open a project in Roblox Studio. 2. To animate, you'll need to freely manipulate parts. From the toolbar, make sure **Move** and **Rotate** snapping are turned off. 3. To open the Animation Editor, select **Animation** from the **Avatar** tab. 4. To create player animations, a character rig is needed. In the **Avatar** tab, click on **Character**. In the popup, select any rig. ### Create a new animation Individual animations, such as walking or jumping, need to be created and saved. 1. In the Animation Editor, type in a new animation name and click Create. 2. To animate, parts of the rig need to be added to the Animation Editor. Click the **(+)** icon and pick **Add All**. > **Info:** By default, the length of all new animations is set to one second (shown as 1:00). To get a different length, change the second box in the position indicator. --- title: "Island of Move" url: /docs/en-us/education/build-it-play-it-island-of-move/beginner-landing-page last_updated: 2026-06-29T19:33:59Z description: "Learn how to use Roblox Studio to make games and experiences. This lesson shows you how to animate to make your own simulator." --- # Island of Move Welcome to the **Island of Move**, an ancient, far-away land where Roblox players are always in motion. Using Roblox Studio, learn how to create your own simulator game where people move to gain points. The way players move will be up to you, whether it's swimming through the air or dancing along the track. After completing this **Build It, Play It** challenge, you'll have a finished game to share with friends. Players can even join on a mobile device and gain points by walking and moving in real-life. Now, time to get moving! ## Download Roblox Studio To start, you'll need **Roblox Studio**. This program is used to create every game on Roblox and is available free for **PC** and **Mac**. If you've played Roblox games on your computer, you already have Studio installed. While creating, we strongly recommend using a **2-button mouse** with a **scroll wheel**. > **Info:****Create a New Account and Get Studio** If you're completely new to Roblox, follow these instructions to get started: 1. [Sign up for an account](https://www.roblox.com/account/signupredir). 2. [Download Roblox Studio](https://www.roblox.com/create). 3. After installing, double-click the Roblox Studio desktop icon (Windows) or click the dock icon (Mac). 4. On the login screen, enter your Roblox username and password, then click **Log In**. > > With your new account, remember these safety tips: - **Never share your password**, even with a real life friend. - **Make your password hard to guess** — If your username is "bloxcool," your password should not be "bloxcool123." - **Roblox employees will never ask for your password** — Report anyone who asks using the [Report Abuse](https://en.help.roblox.com/hc/articles/203312410) feature. - **There's no such thing as free Robux** — Never trust players or sites who say they have a way to get free Robux! ## Three lessons, three prizes To design an animation for your own simulator game, follow along with each of the three step-by-step tutorials. _1: Getting Started_ _2: Creating Animations_ _3: Export and Play_ After each lesson, you'll get a code that can be redeemed for exclusive Roblox avatar prizes. By the end, you'll have a cool animation and some swag to go along with it! --- title: "Challenge checkpoint" url: /docs/en-us/education/build-it-play-it-island-of-move/challenge-checkpoint-2 last_updated: 2026-06-29T19:33:59Z description: "Complete the second Island of Move challenge to earn free Roblox avatar items." --- # Challenge checkpoint You got your animation ready! Celebrate your new skills by getting the **Hustle Hat** and the **Strike a Pose** badge. 1. **Copy** the following code: `StrikeAPose` 2. Click the **Enter World** button below to open the **Island of Move** experience. This game includes a finished example of what you're working on. 3. When you are playing game, interact with the golem character, then click **REDEEM CODE**. Use the code from step 1 to get your item. --- title: "Challenge checkpoint" url: /docs/en-us/education/build-it-play-it-island-of-move/challenge-checkpoint-3 last_updated: 2026-06-29T19:33:59Z description: "Complete the third Island of Move challenge to earn free Roblox avatar items." --- # Challenge checkpoint You got your animation ready! Celebrate your new skills by getting the **Speedy Shades** and **Get Moving** badge. 1. **Copy** the following code: `GetMoving` 2. Click the **Enter World** button below to open the **Island of Move** experience. This game includes a finished example of what you're working on. 3. When you are playing game, interact with the golem character, then click **REDEEM CODE**. Use the code from step 1 to get your item. --- title: "Challenge checkpoint" url: /docs/en-us/education/build-it-play-it-island-of-move/challenge-checkpoint last_updated: 2026-06-29T19:33:59Z description: "Complete the first Island of Move challenge to earn free Roblox avatar items." --- # Challenge checkpoint You got your animation ready! Celebrate your new skills by getting the **Build It Backpack** and **Setting the Stage** badge. 1. **Copy** the following code: `SettingTheStage` 2. Click the **Enter World** button below to open the **Island of Move** experience. This game includes a finished example of what you're working on. 3. When you are playing game, interact with the golem character, then click **REDEEM CODE**. Use the code from step 1 to get your item. --- title: "Change the animation" url: /docs/en-us/education/build-it-play-it-island-of-move/change-the-animation last_updated: 2026-06-29T19:33:59Z description: "Part of the Roblox Build It, Play It Challenge - Island of Move. Swap the default walk animation with your own." --- # Change the animation The template has a pre-built setting for what animation to use when players run. To change the animation, you'll need to replace the default animation with yours. ## Change the animation ID Now that you have the animation ID, you can enter it in your game. Then, Studio will know what animation to play. 1. Make sure the **Explorer** and **Properties** windows are open. > **Warning:** If you don't see the windows on the right, open them from Studio's **Window** menu. 2. To find the walk setting, 1. Go to the **Explorer** window, then in the **search bar**, type "walk". 2. Click on **WalkAnimationID**. 3. In the **Properties** window, click the **Value** field and paste the animation ID by pressing `Ctrl``V` or `⌘``V`. > **Warning:****Need to Get the Animation ID?** If nothing was pasted, or another line of text was used, you'll need to get the animation ID again. 1. Go to the [Creator Dashboard](https://create.roblox.com/dashboard/creations). 2. In the horizontal navigation, select **Development Items**, then click the **Animations** button. All animations you have ever published display here. 3. Select your animation. The animation's **Configure** page displays. 4. In the left-hand navigation, click the vertical three dots icon, then select **Copy Asset ID** from the contextual menu. ## Test and check After swapping the animation, play the game to see your creation. 1. Press the **Play** Button. 2. Use the `WASD` keys to walk around and see the animation play. > **Warning:** If you make updates to the animation, remember to **Save** it in the **Animation Editor**. ## Troubleshooting tips **Issue:** Animation is spotty, skips, or looks glitchy. - Make sure that the first and last poses are the same. Right-click on the diamond for the first pose and copy it. Then, paste that pose at the end of the timeline. - Check that the figure stays inside the white grid when selected. If not, it may go through walls or the ground. **Issue:** Animation isn't the one that you were working on. - Check that you've published the animation through the Animation Editor. - Find in the Explorer an object named WalkAnimationID. Make sure its Value is only a number. --- title: "Create the first pose" url: /docs/en-us/education/build-it-play-it-island-of-move/create-the-first-pose last_updated: 2026-06-29T19:34:00Z description: "Part of the Roblox Build It, Play It Challenge - Island of Move. Start the first pose of the animation." --- # Create the first pose Start by creating the first pose of your animation. This tutorial shows a swimming animation, but yours can be whatever you envision. Depending on the animation you want to create, your first pose may be very different. _Swimming_ _Crouching_ _Dancing_ ## Pose the figure Posing figures is done by rotating individual parts and joints, like the head or elbow. 1. To rotate, click on a body part. A set of handles will appear. > **Warning:****Troubleshooting Rotation** - **Issue:** Can't click on Rotation Handles Another part may be blocking your click. Try rotating around your camera until you see a blue box whenever you hover over the part you want to click. - **Issue:** Can't See Rotation Handles If you see a set of arrows instead of the circles, you'll need to turn the **Rotate** tool on by pressing `R`. 2. Rotate by **dragging** the handles. Each handle rotates in a different direction. **Undo** any change with (`Ctrl``Z` or `⌘``Z`). 3. Continue to rotate different parts until the first pose is complete. While working, change the camera to see the pose from multiple angles. --- title: "Create animations" url: /docs/en-us/education/build-it-play-it-island-of-move/creating-animations last_updated: 2026-06-29T19:33:59Z description: "Part of the Roblox Build It, Play It Challenge - Island of Move. Learn how to start creating animations." --- # Create animations Creating animations is like posing an action figure. For this tutorial, you'll create two poses. When the game runs, Roblox Studio will connect the poses together to create an animation. ## Set up the Animation Editor The poses and the final animation will be created using the Animation Editor. 1. To find the Animation Editor, click **Animation** from the **Avatar** tab. The Animation Editor will open at the bottom of your screen. 2. Next, you need a character to animate. On the pedestal, one has already been set up for you. Select the **figure** on top. The part names that make up the figure will appear in the animation editor. ## Prepare to pose Before creating the poses, make it easier to pose figures by changing a program setting and then position the camera. 1. In the toolbar, find the snapping tools and make sure **Rotate** and **Move** are not checked. 2. Using the camera controls, move the camera to get a good view of the part you want to animate. When moving, hold `Shift` to slow down camera movements. > **Info:****Camera Controls**| **Action** | **Control** | | --- | --- | | **Move** | `W` `A` `S` `D` | | **Rotate** | Hold the right mouse button to look around | | **Zoom** | Use the scroll wheel | | **Focus** | Press `F` to focus the camera on a specific part | > **Warning:****Can't move the camera?** If the camera doesn't move when you press `W` `A` `S` `D`, click somewhere inside the game world. --- title: "Design poses" url: /docs/en-us/education/build-it-play-it-island-of-move/designing-poses last_updated: 2026-06-29T19:34:00Z description: "Learn how to make animations in Roblox Studio with step by step tutorials in this one hour challenge. Design a pose for an animation." --- # Design poses Now that the animation is set up, it's time to start creating poses. A key pose is an important point of motion, such as the moment a player jumps or begins to swing a tool. Animations can have one or more key poses. When thinking of your own animation, imagine breaking it down into a set of key poses. For this example, we'll guide you how to create a celebration jump with these poses. Your own animation may have more or less. _Neutral_ _Crouch_ _Leap_ The process of animating involves moving and rotating avatar parts to create key poses. When the animation runs, Studio connects the poses into a smooth motion. ## Set a neutral pose Used at the start and end of this animation, the **neutral** pose shows the player at rest. So that the animation begins with the player at rest, add a keyframe at the start. In the process of animating, keyframes store information on how parts are positioned. 1. In the Animation Editor timeline, right click on the first track and select Add Keyframe Here. This creates keyframes for each part of the player. ## Create the second pose The second pose shows anticipation as the player crouches before leaping. Depending on your animation, the second pose may be different. 1. Make sure the rig is selected. Then, click on the top bar to set the animation time to a third of the length (e.g. 0:09). 2. Use the **Rotate** tool to start posing the rig. One way animators pose is to start with parts connected to the torso, like the shoulder. Then, move out to parts like the hand. > **Info:** Instead of clicking parts on the model, you may find it helpful to select them in the Animation Editor hierarchy. This is especially true for smaller parts like hands. 3. For this animation, the player will crouch before jumping. If you're doing a different pose, your rig may need to be moved differently. To move the rig: - Switch to the Move tool by pressing `R`. - Click the LowerTorso part (either in the rig or animation hierarchy) and position the body slightly down. Moving this part moves the entire rig. > **Warning:****Only** move the lower torso. Moving any part other than the LowerTorso will cause body parts to become disconnected from the rig. 4. Continue to pose the rig, switching between Move and Rotate by pressing `R`. ## Create the third pose The next set of keyframes will be near the middle, showing the most extreme motion of the animation. In this animation, the avatar will be at the highest point of their jump. 1. Set the animation time to the middle by clicking on the top bar. > **Info:** Exact times can be typed in the first box in the position indicator. 2. Animate the pose using Rotate and Move like below. As you work, exaggerate movement (spread arms out wider, twist the head, etc) to make the animation more clear and exciting._Front View__Side Angle View_ ## Add the last pose For the last pose, copy the first pose and paste it at the end. While you could customize the last neutral pose, this is a quick way of returning the animation to its starting point so it loops smoothly. 1. Select the top keyframe in the timeline for the first pose. Be sure to select all keyframes at that point, then copy them (`Ctrl``C` or `⌘``C`). 2. Move to the end of the animation (1:00 in the example) by clicking on the timeline or entering that value in the first box of the position indicator. 3. Paste the keyframes (`Ctrl``V` or `⌘``V`). ## Test the animation Seeing an animation play more than once can make it easier to spot changes you want to make. 1. Toggle **Looping** on to ensure the animation playback loops. 2. Press **Play**. ## Improve and export With the keyframes created, take time to make the animation more satisfying by adding or fine-tuning frames. Below are some optional ways to improve animations. ### Add more poses Extra poses can add subtle details that make an animation go from "just okay" to "looking great." Incorporate extra poses to exaggerate a motion, show anticipation, or communicate a character's personality. For example, carefully watch the video below a few times. Notice how the animation on the right has more poses, which leads to a greater range of expression. ### Move keyframes Smooth out actions or make them feel more realistic by adjusting when keyframes happen. 1. Click a keyframe. You can either select an entire pose or individual parts._Selecting individual parts__Selecting an entire pose_ 2. Drag the keyframe(s) left or right into a new position. In the video below, notice how moving the animation allows it to feel more "snappy". ## Export the animation Once finished, export the animation so it can be added in-game. 1. Click the `...` button in the upper-left section of the animation editor window. 2. Then, select **Export**. Follow the dialog boxes. > **Info:** If you make any further changes to your animation, remember to Export it again to see the most up to date version. You've made your first animation! Time to get that in-game. --- title: "Export animations" url: /docs/en-us/education/build-it-play-it-island-of-move/exporting-animations last_updated: 2026-06-29T19:34:00Z description: "Part of the Roblox Build It, Play It Challenge - Island of Move. Export the animation to use in Roblox." --- # Export animations You have an animation but now it needs to go into your game. To use the animation, it will need to be exported from the animation editor to the Roblox platform. 1. In the **Animation Editor**, click on the **...** and select **Publish to Roblox**. The **Asset Configuration** window displays. > **Warning:****Animation Editor Blank?** If you closed Studio, the Animation Editor may be blank or closed. - To open the **Animation Editor**, go back to the **Avatar** tab, then select the **Animation** button. - If the Animation Editor is blank, reselect the Figure on the pedestal. 2. In the window, give the animation a **title** and **description**. 3. Click the **Submit** button. ## Get the animation ID An animation ID lets the game know what animation to play. You'll use the ID of your exported animation for players in your game. 1. Once the animation is exported, **copy** the ID by clicking the icon next to the ID. You should see a green "**ID Copied!_**" text. 2. Press the **Close** button. --- title: "Go beyond the challenge" url: /docs/en-us/education/build-it-play-it-island-of-move/go-beyond-the-challenge last_updated: 2026-06-29T19:34:00Z description: "Part of the Roblox Build It, Play It Challenge - Island of Move. Finish the challenge." --- # Go beyond the challenge So far, you've learned how to animate a character and bring that motion into your own simulator game. Below are a few ways to continue customizing your game. ## Other cool tricks | **Share Animations** Send your animation to friends, or use their own. | **Learn Coding in Roblox** Get started coding by creating traps, powerups, and more. | | --- | --- | | | | ## Improve your game Add a little extra style to your simulator game. | **Design Your World** | **Adding New Models** | | --- | --- | | Customize your game and create new environments. | Get a change of scenery by adding in new assets. | | | | --- title: "Invite friends to play" url: /docs/en-us/education/build-it-play-it-island-of-move/invite-friends-to-play last_updated: 2026-06-29T19:34:00Z description: "Part of the Roblox Build It, Play It Challenge - Island of Move. Share the final experience with others." --- # Invite friends to play Moving is always more fun with friends, so let's set up your game for others can play. When a game is first published, it's automatically set to private. Set it instead to public so others can join. 1. **Publish** your game so your friends will get your latest changes. 2. Open **File** ⟩ **Experience Settings**. 3. On the left bar, select **Permissions**, then choose **Public**. 4. Click the **Save** button. --- title: "Island of move" url: /docs/en-us/education/build-it-play-it-island-of-move/landing last_updated: 2026-06-29T19:34:00Z description: "Learn how to make animations in Roblox Studio with step by step tutorials in this one hour challenge." --- # Island of move Using Roblox Studio, we'll show you how to make an experience where you players level up by moving in real-life with their mobile device. The way players move is up to you! Whether they're swimming through the air, or dance through the tracks, the only limit is your imagination. Whether you're new to building in Roblox, or have created before, we have a tutorial for either experience level interested in creating animations. ## What's your skill level? | **BEGINNER** Never used Studio? Get started by creating animations for a simulator experience. | **EXPERIENCED** Used Studio before? Designs animations to add feedback into your existing experiences. | | --- | --- | --- title: "Loop animations" url: /docs/en-us/education/build-it-play-it-island-of-move/looping-animations last_updated: 2026-06-29T19:34:00Z description: "Part of the Roblox Build It, Play It Challenge - Island of Move. Learn how to loop the animation." --- # Loop animations To make sure the animation repeats smoothly, copy the first pose to the end of the animation before creating the second pose. To move to the end of the animation, you'll need to change your position on the **timeline**. This is the numbered line representing the animation in seconds. ## Copy the first pose The first pose is at `0:00` on the timeline. 1. In the **Animation Editor**, click on the **top white diamond** in the timeline. The diamond represents the entire start pose. Make sure the white diamond has a blue border (meaning it's selected) 2. Copy the pose by pressing `Ctrl``C` or `⌘``C`. 3. To move to the animation end, find the box for current time, click on it, and **clear** the text inside. In that box, type `1:00` and press `Enter`. Notice the blue line will move to the animation's end. 4. To paste the first pose at the end, press `Ctrl``V` or `⌘``V`. A new set of diamonds will appear at the end. --- title: "Open the template" url: /docs/en-us/education/build-it-play-it-island-of-move/opening-the-template last_updated: 2026-06-29T19:34:00Z description: "Part of the Roblox Build It, Play It Challenge - Island of Move. Open the template for the movement simulator." --- # Open the template In your movement simulator, players will move around a track to earn stars. After players earn three stars, they'll level up and be able to move faster. The more you move, the faster you'll go. ## Template overview A template has been created that includes all the parts to make a simulator-style game. This includes code and a starter world that you can customize later. ## Open the template Time to open Roblox Studio! If you need to, download [Studio](https://www.roblox.com/create), then click the following button to open the **Move It Simulator** template. ## Close extra windows The first time you launch Roblox Studio, extra windows might open up that aren't needed right now. Closing these windows will give you more space to work. You can always open them up again later. 1. Close each window on the **left** of the 3D view by clicking the **×** in the window's upper-right corner. If you don't see anything to close on the left, go to the next step. 2. Leave the **Explorer** and **Properties** windows open on the right side of Studio. If you have done each step correctly, Studio looks like the following image. > **Warning:** If you don't see the windows on the right, navigate to the **Window** menu and toggle on **Explorer** and **Properties**. --- title: "Personalize the game" url: /docs/en-us/education/build-it-play-it-island-of-move/personalize-the-game last_updated: 2026-06-29T19:34:00Z description: "Part of the Roblox Build It, Play It Challenge - Island of Move. Customize the template script." --- # Personalize the game Make the Move It Simulator into your own unique vision by changing the rewards a player earns, the name of the game, and settings like player speed. Making these changes can change the mood of your game. In a few minutes, go from just a template to a unique game based off your vision. ## Change the game settings The words you see in-game like "Move It Simulator", and "Roblox Developer" are all changeable. You can even make players move faster. These changes can be done by opening a script called _GameSettings_. That script includes different **variables**, or placeholders, for names and numbers used. 1. To open the script, find **Explorer** on the right side of Studio. If you cannot see Explorer, go into the View tab and click the Explorer button. 2. In Explorer **search bar**, type _GameSettings_. 3. Double-click the _GameSettings_ script. ### Change words Inside _GameSettings_, you can change words displayed to players. This can even include the game title or messages shown when players level up. To start, give the game its own unique name. 1. In the GameSetting script, on the left side are **line numbers**. Find **Line 8**.```lua local GameSettings = {} -- ================================================================================ -- Game Information -- These settings affect text that you see in game -- ================================================================================ GameSettings.gameName = "Move It Simulator" GameSettings.developerName = "RobloxUser" GameSettings.levelUpMessage = "Level Up!" ``` 2. Between the quotation marks, type a new name for your game.`GameSettings.gameName = "Wizardry School Simulator"` 3. Run your game. Notice how the billboard at the game start changed._Before__After_ ### Change numbers By modifying different numbers in _GameSettings_, you can create a unique experience for your players. For instance, players can start out moving extra fast or gain levels slowly. 1. In the _GameSetting_ script, find line **28**. This line has the number for the player's starting move speed. Replace the number `7` with a higher number, like `30`.```lua -- Multiplier determines how much players need to move before a level; 1 = small amount of movement, 3 much more movement local growthModifier = 1.2 -- How fast a player starts with 0 levels local startMoveSpeed = 30 -- The amount of WalkSpeed added per level local speedBoostPerLevel = 3 ``` Run the game and notice that your player begins the game faster than before. ## Challenge checkpoint You've customized your game, now claim your free prize! Celebrate your new skills by getting the **Kinetic Staff** and **Do-It-Yourself** badge. Copy the code below. `DIY` Click the button to open a Roblox game, find the in-game character, and click **REDEEM CODE** to get your prize. ## Continue customizing You've taken the first steps in making this game your own, now keep going! Below are a few options to change the look and feel of your game. | **Creating Terrain** Sculpt mountains, rivers, or even cover the world in snow. | **Particle Effects** Add fire, sparks, and other effects to objects in game. | **Atmosphere** Change a game's mood by using environment effects. | | --- | --- | --- | --- title: "Save and publish" url: /docs/en-us/education/build-it-play-it-island-of-move/save-and-publish last_updated: 2026-06-29T19:34:00Z description: "Part of the Roblox Build It, Play It Challenge - Island of Move. Save the project." --- # Save and publish ## Save the animation With the first pose is finished, save the animation. 1. In the **Animation Editor**, find and click on the three dots icon. 2. From the menu, select **Save**. ## Save the place The animation is saved, but it's important to save the whole project by publishing it to Roblox. It's a good idea to **publish** every ten minutes while you're working or after making a big change. 1. Select **File** → **Publish to Roblox** to open the publishing window. 2. Enter a place name and an optional description. 3. When ready, click the **Create** button. Once published, games can be edited from any computer since they're connected to your account. > **Info:****This game runs on mobile** Players can use mobile devices with accelerometers as a way to move characters. Anyone playing on desktop devices will still click a button to move. > **Info:****Saving after publishing** Next time you want to save your work, just go to **File** ⟩ **Publish to Roblox** or use the hotkey combo (`Alt``P`/`⌥``P`). --- title: "Export and Import Animations" url: /docs/en-us/education/build-it-play-it-island-of-move/sharing-animations last_updated: 2026-06-29T19:34:00Z description: "Part of the Roblox Build It, Play It Challenge - Island of Move. Export animations to share with other developers." --- # Export and Import Animations Share animations with friends and fellow developers by exporting and importing them through Studio. > **Info:****Animation Permission Changes** You can now share animations with friends, groups, and experiences without re-uploading them. For more information, see [Asset privacy](/docs/en-us/projects/assets/privacy.md). ## Export animations Make sure to save your animation before exporting it. 1. In **Explorer**, expand the rig that you were animating with. It's likely that rig is named **_Dummy_**. 2. Expand **_AnimSaves_**. Then, find the name of the animation to share. 3. Right-click the animation name and select **Save to File**. This saves the animation as an `.RBXM` file on your computer, which can be shared. You can export more than one animation at a time. ## Import animations Animations saved in an `.RBXM` file can be imported into different rigs by yourself or by friends in any Roblox file. 1. Select the rig you want to use. In the **Explorer**, select **_AnimSaves_**. > **Warning:****Check The Rig Has Previous Animations** If the rig doesn't have any previous animations, it won't have an AnimSaves folder. To make that folder, in the rig, create and save a new animation. Saving an animation automatically creates the needed folder. 2. Right-click on **_AnimSaves_** and select **Insert From File**. Then, pick the `.RBXM` file with the exported animation. 3. To load the animation, in the Animation Editor (see the Plugins tab), click on **⋯**. Next, in the drop-down menu, go to **Load** and select the name of the `.RBXM` file just imported. ### Troubleshooting tips **Issue**: Animation Not Loading In the original `.RBXM` file, make sure there is a saved `Class.KeyframeSequence` object. If only a `Class.Keyframe` object was imported, try re-exporting the original animation and importing again. Make sure to export the named `Class.KeyframeSequence` and not just a keyframe object. --- title: "Test the game" url: /docs/en-us/education/build-it-play-it-island-of-move/test-the-game last_updated: 2026-06-29T19:34:00Z description: "Part of the Roblox Build It, Play It Challenge - Island of Move. Test the game experience you'll create." --- # Test the game Before animating, test out the template to see how the game works. In-game, players make progress around the track by clicking the move button, or using a mobile device and moving around in real-life. Players start off slow and then get faster and faster. 1. Click **Play** to test. 2. In the experience, walk up to the golem character on the stage and press `E` to open up a menu. On that screen, click **Play Game**. 3. Once in-game, go forward by repeatedly clicking the **Move** button. Your character will automatically follow the track as you click. 4. **Stop** playing by clicking the red square. --- title: "Add scripts" url: /docs/en-us/education/build-it-play-it-mansion-of-wonder/adding-scripts last_updated: 2026-06-29T19:34:00Z description: "Learn how to use Luau code to manipulate beams and emitters to add tutorial mechanics to your game in the Roblox Build It Play It Mansion of Wonder." --- # Add scripts Time to bring all this work together! Now that you've created the beam and particle components, you'll add in three premade scripts. These scripts manage the tutorial by telling components when to do what. For example, the scripts will create beams for new players and emit particles whenever they interact with goals. > **Info:** While you can follow all these steps even if you haven't coded before, having that knowledge can help you adapt these scripts for your own experience. To learn more, see the [Coding Fundamentals](/docs/en-us/tutorials/fundamentals/coding-1/coding-fundamentals.md) tutorial. ## Store beam and particles Before adding the scripts, the beam and particles need to be moved to where the scripts will be able to make copies of them as needed. > **Warning:** If you're using your own project, you may need to replace the values within some scripted variables to match your objects. 1. In **ReplicatedStorage**, create a new folder named **PlayerTutorial**. Move TutorialBeam out of TestPlayer, and into the new folder. 2. In **ServerStorage**, create a folder named **TutorialParticles**. Move the **Burst** particle out of TestPlayer into that folder. 3. Once the beam and particle emitter are moved, you no longer need **TestPlayer** since the script will work with real players when finished. Delete **TestPlayer**. ## Create events Each time players interact with a goal, the tutorial script will need to know so it can update that player's progress and emit the particle effect. To inform scripts, signals can be sent using **events**. 1. In **ReplicatedStorage** > **PlayerTutorial**, create two **RemoteEvent** objects. Name them **NextGoal** and **TutorialEnd**. ## Add the scripts The three scripts below will look for the particle emitter and beam objects created earlier and manage the tutorial system. 1. In **ReplicatedStorage** > **PlayerTutorial** > create a new **ModuleScript** named **TutorialManager**. Replace the default code by copying and pasting the entire code below.```lua local TutorialManager = {} local ReplicatedStorage = game:GetService("ReplicatedStorage") local tutorialFolder = ReplicatedStorage:WaitForChild("PlayerTutorial") local TutorialEndEvent = tutorialFolder:WaitForChild("TutorialEnd") local NextGoalEvent = tutorialFolder:WaitForChild("NextGoal") -- Note Goal parts must be ordered in the table, or else Goal order may be different in-game local goalParts = { workspace.TutorialGoals.GoalPart1, workspace.TutorialGoals.GoalPart2 } local function checkTutorialEnd(player, goalParts) local currentIndex = player:WaitForChild("GoalProgress") return currentIndex.Value >= #goalParts end local function finishTutorial(player) local playerBeam = player.Character.HumanoidRootPart:FindFirstChildOfClass("Beam") playerBeam:Destroy() print(player.Name .. " finished the tutorial") -- Placeholder for further code. E.g. if you wanted to send messages to the server to do other tasks end function TutorialManager.interactGoal(player) NextGoalEvent:FireServer() end function TutorialManager.getTutorialGoals() return goalParts end function TutorialManager.nextGoal(player, goalParts) if checkTutorialEnd(player, goalParts) then finishTutorial(player) else -- Increment the player's Goal tracker local currentGoalIndex = player:WaitForChild("GoalProgress") currentGoalIndex.Value += 1 end end -- Creates an int value to locally track player's progress through the tutorial Goals function TutorialManager.setupPlayerProgress(player) local currentGoalProgress = Instance.new("IntValue") currentGoalProgress.Name = "GoalProgress" currentGoalProgress.Value = 1 currentGoalProgress.Parent = player end return TutorialManager ``` This script runs code for managing a player's progress in the tutorial. This includes tasks like running code for interacting with goals, or what happens when the tutorial is over. 2. In **ServerScriptService**, create a new **Script** named **TutorialParticles**. Paste the code below.```lua local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") local ServerStorage = game:GetService("ServerStorage") local tutorialFolder = ReplicatedStorage:WaitForChild("PlayerTutorial") local NextGoalEvent = tutorialFolder:WaitForChild("NextGoal") local EMIT_RATE = 50 local function playParticleBurst(player) local character = player.Character or player.CharacterAdded:Wait() local humanoidRootPart = character:WaitForChild("HumanoidRootPart") local particleAttachment = humanoidRootPart:WaitForChild("ParticleAttachment") -- Go through particles on the attachment and play them according to the type of particle for _, particle in particleAttachment:GetChildren() do if particle:IsA("ParticleEmitter") then particle:Emit(EMIT_RATE) end end end local function setupPlayerParticles(player) player.CharacterAdded:Connect(function(character) local humanoidRootPart = character:WaitForChild("HumanoidRootPart") local playerParticleAttachment = Instance.new("Attachment") playerParticleAttachment.Name = "ParticleAttachment" playerParticleAttachment.Parent = humanoidRootPart -- Clone particles in the folder, even if there are more than one and attach to player for _, emitter in ServerStorage.TutorialParticles:GetChildren() do emitter:Clone().Parent = playerParticleAttachment end end) end Players.PlayerAdded:Connect(setupPlayerParticles) NextGoalEvent.OnServerEvent:Connect(playParticleBurst) ``` This script plays the burst particle whenever players interact with goals. There's also a variable named `EMIT_RATE` that determines how many particles spawn during an interaction. 3. In **StarterPlayer** > **StarterPlayerScripts**, create a new **LocalScript** named **TutorialScript**. Then, paste the script below. This script creates and manages the beam used to guide players.```lua local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") local tutorialFolder = ReplicatedStorage:WaitForChild("PlayerTutorial") local TutorialManager = require(tutorialFolder:WaitForChild("TutorialManager")) local TutorialEndEvent = tutorialFolder:WaitForChild("TutorialEnd") local player = Players.LocalPlayer local goalParts = TutorialManager.getTutorialGoals() local playerBeam = nil local goalIndex = nil local function getTargetAttachment() local currentTarget = goalParts[goalIndex.Value] local interactionPart = currentTarget:FindFirstChild("InteractionPart") local attachment = interactionPart and interactionPart:FindFirstChildOfClass("Attachment") if not attachment then attachment = Instance.new("Attachment") attachment.Name = "BeamAttachment" attachment.Parent = currentTarget end return attachment end local function updateBeamTarget() playerBeam = player.Character.HumanoidRootPart:FindFirstChildOfClass("Beam") local targetBeamAttachment = getTargetAttachment() if targetBeamAttachment then playerBeam.Attachment1 = targetBeamAttachment else warn("Attachment not found in a goal. Check that goals have attachments or they're included under the InteractionPart") end end local function setupGoals() for _, part in goalParts do local interactionPart = part:FindFirstChild("InteractionPart") local proximityPrompt = interactionPart and interactionPart:FindFirstChild("ProximityPrompt") if proximityPrompt then proximityPrompt.Triggered:Connect(function(player) proximityPrompt.Enabled = false TutorialManager.nextGoal(player, goalParts) TutorialManager.interactGoal(player) end) else warn("Proximity prompt not included in goal. Add one to each goal part under the InteractionPart") end end end local function createBeamForCharacter(character) local humanoidRootPart = character:WaitForChild("HumanoidRootPart") local playerBeamAttachment = Instance.new("Attachment") local beamTemplate = tutorialFolder:WaitForChild("TutorialBeam") if not beamTemplate then warn("Tutorial Beam not found in ReplicatedStorage") end playerBeamAttachment.Name = "BeamAttachment" playerBeamAttachment.Parent = humanoidRootPart local targetBeamAttachment = getTargetAttachment() playerBeam = beamTemplate:Clone() playerBeam.Attachment0 = playerBeamAttachment playerBeam.Attachment1 = targetBeamAttachment playerBeam.Enabled = true playerBeam.Parent = humanoidRootPart end local function setupPlayer() setupGoals() TutorialManager.setupPlayerProgress(player) goalIndex = player:WaitForChild("GoalProgress") player.CharacterAdded:Connect(createBeamForCharacter) if player.Character then createBeamForCharacter(player.Character) end end setupPlayer() goalIndex.Changed:Connect(updateBeamTarget) ``` 4. Play the project to test the scripts. Move from booth to booth, using the interact feature to see if code works. ### Troubleshooting tips **Issue**: Particles play when game starts. - Go into ServerStorage > Tutorial Particles > Burst. Check Enabled to be off. **Issue**: Warnings in the compiler such as an "infinite yield". - Because the script is looking for specific objects in certain locations, it's possible that a part is named incorrectly. Double check that the name and location of each part in game matches the tutorial. ### Script benefits and limitations If you're using this tutorial system in your experience, keep in mind the following: **Benefits** - Events such as TutorialEnd can be used to trigger other scripts. For instance, you can award players a special item when this event fires. - The TutorialParticles script can play multiple particles at once. You can add more particles in ServerStorage/TutorialParticles for more complex effects. **Limitations** - Player progress in the tutorial is not persistent, meaning you'll have to code some way of saving that progress. For guidance, see the article: Saving Data. --- title: "Become an artist" url: /docs/en-us/education/build-it-play-it-mansion-of-wonder/become-an-artist last_updated: 2026-06-29T19:34:00Z description: "Start learning to become a visual effects artist using Roblox Studio through the Build It Play It Mansion of Wonders challenge." --- # Become an artist In the **Mansion of Wonder** carnival ride, players hop on a ride and defend their cart from enemies. To help in the fight, everyone wields a magic blaster. You'll become an **FX artist**, designing the special effects used by the blaster, like magical blasts and explosions. ## Template overview To start your development journey, a template has been created that includes the starter world and code. You'll be able to customize your experience as much as you'd like once you've finished these basic lessons. ## Open the template Time to open Roblox Studio! If you need, download [Studio](https://www.roblox.com/create), then click the following button to open the **Mansion of Wonder** template. ## Close extra windows The first time Roblox Studio is launched, extra windows might open up that aren't needed right now. Closing these windows will give you more space to work. You can always open them up again later. 1. Close each window on the **left** of the 3D view by clicking the **×** in the window's upper-right corner. If you don't see anything to close on the left, go to the next step. 2. Leave the **Explorer** and **Properties** windows open on the right side of Studio. If you have done each step correctly, Studio looks like the following image. > **Warning:** If you don't see the **Explorer** and **Properties** windows on the right, enable them from Studio's **Window** menu. --- title: "Welcome to Build It, Play It!" url: /docs/en-us/education/build-it-play-it-mansion-of-wonder/beginner-landing-page last_updated: 2026-06-29T19:34:00Z description: "Start learning how to add particle effects to your Roblox experiences by taking the Build It Play It Mansion of Wonder challenge." --- # Welcome to Build It, Play It! Create your own Roblox experience by customizing elements of the **Mansion of Wonder** template in Roblox Studio! Design and personalize the magic blasts that you and your friends will use to defeat sinister slimes and ghastly ghosts as you ride through a spooky carnival experience. Will you conquer these creatures with bolts of lightning or piles of angry cats? Only you get to decide: you build it, then play it! ## Download Roblox Studio Before starting, you'll need **Roblox Studio**. This program is used to create every experience on Roblox and is available for free on a **PC** or **Mac**. If you've played Roblox on your computer, you already have Studio installed. When using Studio, we recommend using a **mouse** if you have one. If you already have a Roblox account, skip to the next [section](/docs/en-us/education/build-it-play-it-mansion-of-wonder/beginner-landing-page.md#series-overview). 1. [Sign up for an account](https://www.roblox.com/account/signupredir). 2. [Download Roblox Studio](https://www.roblox.com/create). 3. After installing, double-click the Roblox Studio desktop icon (Windows) or click the dock icon (Mac). 4. On the login screen, enter your Roblox username and password, then click **Log In**. With your new account, remember these safety tips: - **Never share your password**, even with a real life friend. - **Make your password hard to guess** — If your username is "bloxcool," your password should not be "bloxcool123." - **Roblox employees will never ask for your password** — Report anyone who asks using the [Report Abuse](https://en.help.roblox.com/hc/articles/203312410) feature. - **There's no such thing as free Robux** — Never trust players or sites who say they have a way to get free Robux! ## Series overview To design your personalized **magic blast**, follow along with these three step-by-step tutorials. _1: Design the blast_ _2: Make it unique_ _3: Make final touches_ After each lesson, there's a special code that can be redeemed for a **free** virtual Roblox **avatar item**. Once you've completed all the lessons, you'll have a Roblox experience to play with friends and some cool swag to show off. If you're ready, click the **next arrow** to start. --- title: "Challenge checkpoint" url: /docs/en-us/education/build-it-play-it-mansion-of-wonder/challenge-checkpoint-2 last_updated: 2026-06-29T19:34:00Z description: "Earn the second set of rewards in Roblox's Build It Play It Mansion of Wonders -- the Ghastly Aura avatar item and Alchemical Antics badge." --- # Challenge checkpoint You got your animation ready! Celebrate your new skills by getting the **Ghastly Aura** and **Alchemical Antics** badge. 1. **Copy** the following code: `ThingsGoBoom` 2. Click the **Enter World** button below to open the **Mansion of Wonder** experience. 3. When you are playing game, interact with Freddie the fox character, then click **REDEEM CODE**. Use the code from step 1 to get your item. --- title: "Challenge checkpoint" url: /docs/en-us/education/build-it-play-it-mansion-of-wonder/challenge-checkpoint-3 last_updated: 2026-06-29T19:34:00Z description: "Earn the third set of rewards in Roblox's Build It Play It Mansion of Wonders -- the Tomes of the Magus avatar item and Master of the Mansion badge." --- # Challenge checkpoint You got your animation ready! Celebrate your new skills by getting the **Tomes of the Magus** and **Master of the Mansion** badge. 1. **Copy** the following code: `ParticleWizard` 2. Click the **Enter World** button below to open the **Mansion of Wonder** experience. 3. When you are playing game, interact with Freddie the fox character, then click **REDEEM CODE**. Use the code from step 1 to get your item. --- title: "Challenge checkpoint" url: /docs/en-us/education/build-it-play-it-mansion-of-wonder/challenge-checkpoint last_updated: 2026-06-29T19:34:00Z description: "Earn the first rewards in Roblox's Build It Play It Mansion of Wonders -- the Artist Backpack avatar item and Conjurer of Color badge." --- # Challenge checkpoint You got your animation ready! Celebrate your new skills by getting the **Artist Backpack** and **Conjurer of Color** badge. 1. **Copy** the following code: `FXArtist` 2. Click the **Enter World** button below to open the **Mansion of Wonder** experience. 3. When you are playing game, interact with Freddie the fox character, then click **REDEEM CODE**. Use the code from step 1 to get your item. --- title: "Connect the beam" url: /docs/en-us/education/build-it-play-it-mansion-of-wonder/connect-the-beam last_updated: 2026-06-29T19:34:00Z description: "Learn how to connect a beam effect in Roblox Studio, and to configure its properties to customize it to your game's needs. Part of the Build It Play It Mansion of Wonders series." --- # Connect the beam Now that you've made the attachments, it's time to connect them to the beam so that a texture can be shown. 1. Under **TestPlayer**, select the **TutorialBeam**. In the **Properties** window, find **Attachment0**. Click the empty box to the right of the property. Then, in the **Explorer** window, find **TestPlayer** and click on **PlayerAttachment**. 2. In the **TutorialBeam** properties, set **Attachment1** to the attachment you created in **GoalPart1**, the **GoalAttachment**. ## Adjust the beam By default, a beam doesn't always face the camera. This may lead to situations where players are unable to see a beam from different angles. This can be fixed by turning on a property called **FaceCamera**. _FaceCamera Off_ _FaceCamera On_ 1. Make the beam visible at any position by going into the beam's properties and enabling **FaceCamera**. Once finished, you should see a white beam between the player and goal, regardless of camera angle. ## Make the tutorial arrows The image displayed along the beam can be customized by changing its 2D texture. In addition, beams have a variety of visual options, such as texture, color, motion that can be modified. 1. To stream arrows along the beam like in the example, copy `5886559421` and paste it into the beam's **Texture** property. > **Info:** Custom textures can be any image that you have. Learn how to upload them in the [Asset Manager](/docs/en-us/projects/assets/manager.md) article. 2. Right now, the arrow's texture is stretched. Set **TextureMode** to **Static** to make the arrow repeat at its original size. > **Warning:** If the arrow doesn't point the correct direction, attachments may be swapped. Make sure that Attachment0 connects to PlayerAttachment and Attachment1 to GoalAttachment. Alternatively, if you're using a custom texture, you may need to mirror the image in a photo-editing program. 3. Modify beam properties like **Color**, **LightEmission**, and **TextureSpeed** to design something that feels eye-catching and appealing. Below is just one example. ### Tips for color choices **Color** is one opportunity for improving a first time user experience. For example, contrasting or saturated colors get more attention, making it easier to inform players where to look on their screen. Additionally, as you design, consider [accessibility](/docs/en-us/production/publishing/accessibility.md) for your players. Some players may struggle to see certain color combinations, making it important that colors appeal to the broadest audience. --- title: "Create a sparkler" url: /docs/en-us/education/build-it-play-it-mansion-of-wonder/creating-a-sparkler last_updated: 2026-06-29T19:34:00Z description: "Learn how to attach a particle emitter to a player-carried tool in Roblox Studio. Once you are finished, Earn the fourth set of rewards in Roblox's Build It Play It Mansion of Wonders -- the Head Slime avatar item and Glimmer and Gumption badge." --- # Create a sparkler You've learned the basics of particles, now apply those skills to design a sparkler that can be carried around by people. Just like blasts, the particles can be customized for different effects, like magical stars or raging flames. In addition to the properties covered in the Build It, Play It challenge, this tutorial will use color gradients which allows particles to change colors over time, like from blue to yellow as seen in the video. The sparklers can be used with the Mansion of Wonder template or any other experience you've created. ## Find a tool For this tutorial, sparklers are **tools** with attached particle effects. In Roblox, tools are items players equip; if you've played an experience with a sword or gun, it's likely you've used tools before. To start, add the pre-made sparkler set into your existing experience. You'll then choose one and customize its particle emitter. 1. Start by pressing the **Add to Inventory** button in the following component. This adds the set into your Inventory, which is accessed in the Toolbox. #### Sparkler Set Click the button to get a set of sparklers for the tutorial. 2. In Studio, find the **Toolbox**. If it's not visible, open it from the **Window** menu. 3. Select the **Inventory** tab. 4. Make sure the left dropdown is set to **My Models**, then find and click on **Sparkler Set**. This will add the parts into your experience. ## Create the particle Each sparkler comes with a customizable particle emitter. Follow the steps to find the particle, change its texture, and then create the color gradient. 1. Select one of the pre-made tools. In the **Explorer** window, find the highlighted part. Then, click the **>** arrows to expand until you find **Attachment** and select **SparklerParticle**. 2. Change the **Texture** property. Some texture ID's are included below._5860841663__5857851812__5857851618__6711256324__5833235272__6772783963__5833323391__5857892330__5857892405__5857931724__5857851618__5860841737_ When finished, a tool may look like the example below. 3. Find the **Color** property and click anywhere in the row next to its name. Then, press the **...** to open the Color Sequence editor. You'll learn how to edit the color in the next section. ## Create a color gradient Each **arrow** on the color sequence represents a different color. Colors can be changed, added, removed, or moved around. Use a combination of techniques to create a color gradient, like the example below. ### Change a color Under the color sequence, select an **arrow**. Then, click the **Color** button to open the color picker and set a new color. Note that arrows are yellow when selected. ### Add or remove colors To add a color, click on the color sequence. This adds a new arrow that can be clicked. To remove a color, click on an arrow and click the **Delete** button on the screen. ### Move colors To move a color, click on any arrow other than the start or end arrows and drag it to a new area. ### Improve the particle After creating a color gradient, use your knowledge from the previous lessons to change properties like **Size** and **Speed** to get the particular effect you want. ## Finish the sparkler So players can have the sparkler when the experience starts, you'll need to move it to the StarterPack. This folder holds all items a player receives when they join. 1. In the **Explorer** window, find the **tool** of your choice, such as the **Staff Sparkler** or **Torch Sparkler**. Right-click on that tool and select **Cut**. This copies the staff to your clipboard, but also deletes it from the folder. 2. Scroll down in the **Explorer** window until you find the **StarterPack**. Right-click on it and select **Paste Into** to add the tool. 3. Playtest project, and select the sparkler from your backpack by pressing `1` on your keyboard. Note that you can repeat this process to make more sparklers. > **Info:** Each sparkler has a name that's shown to players in the experience. To rename a sparkler, in the **Explorer** window, right click on the tool and select **Rename**. ## Challenge checkpoint You got your animation ready! Celebrate your new skills by getting the **Head Slime** avatar item and **Glimmer and Gumption** badge. 1. **Copy** the following code: `Glimmer` 2. Click the **Enter World** button below to open the **Mansion of Wonder** experience. 3. When you are playing game, interact with Freddie the fox character, then click **REDEEM CODE**. Use the code from step 1 to get your item. --- title: "Create engaging experiences" url: /docs/en-us/education/build-it-play-it-mansion-of-wonder/creating-engaging-experiences last_updated: 2026-06-29T19:34:00Z description: "Start learning how to add a tutorial using beam effects in Roblox Studio as part of the Build It Play It Mansion of Wonder. You can add these to your own game, or use a provided template." --- # Create engaging experiences Before starting, think about your past experiences opening a new game or smartphone app. In bad experiences, like trying to use a confusing app, you might have given up and closed it. Or maybe a game had too much information up front that was hard to remember. In order to build good experiences, creators must engage and inform new users at the same time. There's an entire career devoted to studying this. **User experience (UX)** designers are people who take time to make sure that users can figure out where to go and how to accomplish goals without feeling frustrated. For instance, these two pictures compare the impact user experience can have to someone's first impression of a game. _Accessible UX_ _Overwhelming UX_ ## Project setup This course will walk through designing a guided tutorial using a starter template. Alternatively, you can use your own project since the finished tutorial system can be applied to any experience. The starter place for this series includes parts of an amusement park themed game where players collect tickets for a prize. New players will be directed to booths to collect their tickets. ### Open the template Time to open Roblox Studio! Click the following button to open the **Boardwalk** template. A new instance of Studio will open with the map. The first thing you'll see is a TestPlayer object that will simulate a player. ### Use your own project If using your own project, use the main actions your players will do during the experience. For instance, in a farming game, have players collect wheat to sell at a shop booth. Additionally, we recommend the following: 1. **Import Files** - Import the [Boardwalk Starter Objects](https://www.roblox.com/library/6777983114/Boardwalk-Starter-Objects) into the project's Workspace using the Inventory. This includes ticket booths with ProximityPrompts and a TestPlayer. 2. **Follow the Course and Adapt** - When finished, replace the premade models with your own. Keep in mind, you may need to rename some variable references in scripts. ## The template The place includes two interactive booths with `Class.ProximityPrompt|ProximityPrompts`, which are objects that run scripts when players interact with something in the template. During the tutorial, players will interact with each booth to collect tickets and advance the tutorial. Note that all parts but the booth and test player are locked. ## Beams and attachments To guide players to booths for tickets, you'll create a path using beams like in the video below. **Beams** are objects also used for effects like lasers or force fields. This object draws a texture between two points with customizable properties like speed, width, and curve size. To display the beam, **attachments** are needed to set the beam's start and end points. You'll insert one attachment into the player and the other attachment into the goal object. 1. In the **Explorer** window, find **TestPlayer** and add an **attachment** named **PlayerAttachment**. 2. Under **TestPlayer**, add a **Beam** named **TutorialBeam**. Keep in mind, the beam won't be visible until its start and end points are set later on. 3. Find the **TutorialGoals** folder and **expand** it. Under both **InteractionPart** objects, add an attachment named **GoalAttachment**. --- title: "A player's first experience" url: /docs/en-us/education/build-it-play-it-mansion-of-wonder/experienced-landing-page last_updated: 2026-06-29T19:34:00Z description: "Learn why the first time user experience of a game is so important, and how you can use effects build to help a player learn how to play. This is the beginning of the advanced series in Roblox's Build It Play It Mansion of Wonder." --- # A player's first experience Are you an established game developer? Or just getting started? Either way, the first impression users have of your experiences matter. If they don't like what they see or don't know what to do, they're likely to leave and never come back. One way to create a strong **first time user experience** is to teach new users how to play by designing an interactive tutorial. While doing the tutorial, players will learn about key aspects of the experience, whether it's collecting items or knowing where to find the shop. In your tutorial, you'll guide first time players to a goal, let them know when that task has been completed, and then lead them to their next goal The concepts and skills in this series can be applied to any experience, whether you're working on your own or want to use the supplied example map. When finished, you'll be able to: - Use beams to design a trail that guides players to each goal. - Create a particle effect that plays when a player completes their task. - Add scripts to track the player's current goals and progress. --- title: "Finish the blast" url: /docs/en-us/education/build-it-play-it-mansion-of-wonder/finish-the-blast last_updated: 2026-06-29T19:34:00Z description: "Learn how to change the size and movement style of particles from particle emitters in Roblox Studio as part of the Build It Play It Mansion of Wonders." --- # Finish the blast Make final changes to the blast by editing the size and how it moves. ## Change the size 1. Find **Size** and click on its row. Type a number from 1 to 10 and then press **Enter**. You may have a texture that looks like the example below. 2. Press **Play** to test the project. If needed, make changes to color and size to better fit your vision. When finished, finish testing by clicking the **Stop** button or pressing `Shift``F5`. ## Change the move style A particle can either be a **trail** or a **single blast** by toggling the **LockedToPart** property. The video below shows what a trailing blast vs a single blast looks like. Choose which one you like better, and then continue. ### Make trailing blasts 1. [Skip](/docs/en-us/education/build-it-play-it-mansion-of-wonder/making-an-explosion.md) to the next section. To have a trailing blast, don't do anything. ### Make single blasts 1. In the particle properties, scroll down to find **LockToPart**. Click the checkbox to turn it on. 2. **Play** the project to check that it's moving as a single part. --- title: "Invite friends" url: /docs/en-us/education/build-it-play-it-mansion-of-wonder/invite-friends last_updated: 2026-06-29T19:34:00Z description: "Learn how to invite your friends to play the game you build in Roblox Studio by configuring the game's permissions. Part of the Build It Play It Mansion of Wonder series." --- # Invite friends Adventuring is always more fun with friends, so let's set up your experience for others can play. When an experience is first published, it's automatically set to private. Set it instead to public so others can join. 1. **Publish** your experience so your friends will get your latest changes. Remember you can publish by going to the File menu in the top left. 2. Select **File** (top left of Studio) and click **Experience Settings**. 3. On the left bar, select **Permissions**, then choose **Public**. 4. Click the **Save** button. --- title: "Mansion of wonder" url: /docs/en-us/education/build-it-play-it-mansion-of-wonder/landing-page last_updated: 2026-06-29T19:34:00Z description: "Learn how to use special effects in Roblox Studio to give players feedback or help them learn how to play your game in the Build It Play It Mansion of Wonder." --- # Mansion of wonder Using Roblox Studio, you'll learn to create special effects, whether it's a magical blast in an arcade experience, or a beam of arrows that guides new players around a world. These two tutorials take you step-by-step as you learn and earn exclusive prizes. ## What's your skill level? Depending on whether or not you've used Roblox Studio, there are different tutorials for your skill level. Pick the one below that suits your needs. | **BEGINNER** Never used Studio? Get started by designing special effects for an arcade shooter! Earn these prizes! | **EXPERIENCED** Used Studio before? Super-charge your skills by learning particle effects and scripting! Earn this prize! | | --- | --- | --- title: "Make final touches" url: /docs/en-us/education/build-it-play-it-mansion-of-wonder/make-final-touches last_updated: 2026-06-29T19:34:00Z description: "Learn how to fine tune your particle emitters in Roblox Studio by adjusting their size, speed, light emission, rotational speed, and lifetime. Part of the Build It Play It Mansion of Wonder series." --- # Make final touches Make your explosion feel more unique by customizing more properties. ## Size and speed Start by changing these two properties to achieve your vision. 1. In the explosion properties, change **Size** to a number between `1` and `10` for best results. Different particle textures may work better with different sizes. Some example sizes can be seen in the video below. 2. Particles can burst out quickly, or creep out slowly. To experiment with this speed, scroll down to the **Emission** section and find **Speed**. We recommend trying a number between `10` (slow) and `100` (fast). 3. When finished, run the project to test the particle. Make changes to the design until it looks right to you. ## Additional properties You've experimented with basic properties of the magic blast and the explosion from your blaster, but there are a few more properties to personalize your work. Try changing the properties below, or experiment with one that isn't listed. ### LightEmission Makes particles glow and can be a range from `0` (opaque) to `1` (glowing). _0_ _0.5_ _1.0_ ### RotSpeed Makes particles rotate over time. Notice how the rotation of the texture below (straight lines) changes as the RotSpeed changes. ### Lifetime This property is how long particles exist in seconds. Depending on your explosion, you may want a short lifetime (e.g. 0.5 seconds) for a quick, snappy blast, or a longer number (e.g. 2 seconds) for something that lingers. ### Randomized numbers Randomizing properties can make particles feel less repetitive. You can add randomization to some particles properties, such as **Lifetime** and **Rotation**, by inputting a minimum and maximum value. 1. To use a randomized property, click the small arrow next to the property name. Once the property expands, you can enter a min and max value. > **Info:** Using your new knowledge with explosions, take some time to also **improve the blast particle**. Experiment with properties like Lifetime. Remember, the blast emitter can be found by searching for `BlastParticles` in the **Explorer** window. --- title: "Make an explosion" url: /docs/en-us/education/build-it-play-it-mansion-of-wonder/making-an-explosion last_updated: 2026-06-29T19:34:00Z description: "Learn how to make and explosion effect from scratch with a custom texture using particle emitters in Roblox Studio as part of the Build It Play It Mansion of Wonder." --- # Make an explosion Now for the explosions! The same skills you learned in making the magic blast also apply to making an explosion, such as changing the color and size. ## Change the look Start by changing basic properties of the explosion, such as the color and texture. 1. In the **Explorer** window's search bar, clear the previous search, then type **ExplosionParticles** and click the found object. 2. In the **Properties** window, change the **color** of the explosion. Think of a color that will go well with the blast, such as a similar or contrasting color. > **Warning:** Keep in mind, you can only see what your explosion looks like by testing your experience. 3. Find a texture for your explosion from the options below and then **copy** the **number** below it. The explosion texture can be the same or different than the magic blast._5860841663__5857851812__5857851618__6711256324__5833235272__6772783963__5833323391__5857892330__5857892405__5857931724__5857851618__5860841737_ 4. Find the **Texture** property, then paste the copied number ID in the previous step. The example in the following picture uses the star texture. 5. To see the new colored texture, press **Play**. Then, walk up to the Play It section and start a new game. Right now, the explosions might look a bit small, or you may want to make some other tweaks to them. Keep going to fine-tune the look of explosions. --- title: "Next steps" url: /docs/en-us/education/build-it-play-it-mansion-of-wonder/next-steps-advanced last_updated: 2026-06-29T19:34:00Z description: "For advanced developers, choose what to learn next in the Roblox Build It Play It Mansion of Wonder." --- # Next steps So far, you've learned how to create beams and particle effects. Now, show off your work or take your skills further! ## Learn more about user experience User experience designers are a common career choice for many in video games and software in general. Learn more about what this profession does in the [design tab](/docs/en-us/production/game-design/ui-ux-design.md). --- title: "Next steps" url: /docs/en-us/education/build-it-play-it-mansion-of-wonder/next-steps last_updated: 2026-06-29T19:34:00Z description: "For newer developers, choose what to learn next in the Roblox Build It Play It Mansion of Wonder." --- # Next steps **Congratulations** on taking the first steps to creating experiences. So far, you've learned how to create special effects in your very own Roblox action experience. Now, **keep going!** ## Supercharge your experience Learn techniques to customize your particles even further or take a chance to show off your work. | **Create a Sparkler** Add particles onto staves or torches your players wield! | | --- | --- title: "Add particle emitters" url: /docs/en-us/education/build-it-play-it-mansion-of-wonder/particle-emitters last_updated: 2026-06-29T19:34:00Z description: "Learn Roblox Studio camera controls and the basics of editing particle emitters in the Build It Play It Mansion of Wonders." --- # Add particle emitters While testing, you may have noticed that the magic blasts are a bit bland. The white sparks you see right now are just a starting point; you'll make them much cooler by customizing a `Class.ParticleEmitter` object. Particle emitters make special effects in Roblox Studio. If you've seen effects like smoke, fire, or even magic portals in other Roblox experiences, you've definitely seen `Class.ParticleEmitter|ParticleEmitters`. ## Find the blast The template has two customizable particle emitters: one for the **magic blast** that flies through the air and another for the **explosion** when it hits an enemy. Start first with the blast, and in a later tutorial, you'll work on the explosion. 1. Using the camera controls (see below for instructions), move the camera to get a good view of the magic blast, a ball with white sparkles. Below are **controls** on using the camera in Studio. | Action | Control | | --- | --- | | **Move** | `W` `A` `S` `D` or arrow keys | | **Rotate** | **Computer Mouse:** Hold the right mouse button and move the mouse to look around.
**Laptop Trackpad:** Hold down two fingers and click with another. | | **Zoom** | **Computer Mouse:** Use the mouse scroll wheel.
**Laptop Trackpad:** Slide two fingers up or down. | 2. In the top-right box in Roblox Studio, find the **Explorer** window. This has all of the pieces of the project, such as code, decorations, and the blast you'll personalize. > **Info:** If you don't see it the **Explorer** window, open it from Studio's **Window** menu. 3. You'll need to find the magic blast's particles in the **Explorer** window to work with it. In the **Explorer** window's **search bar**, type **BlastParticles**, then select the object found. ## Change the color Start personalizing the magical blast by giving it a new color. 1. You can make changes to particle emitters in the **Properties** window. Find it on the bottom right of Studio, under the **Explorer** window. 2. In the **Properties** window, find the **Color** property, then double-click the light grey box to its right. A pop-up **Colors** window displays. 3. In the **Colors** window, select a color and then click the **OK** button. For example, if you set the **Color** property to `202, 210, 20`, you will then have a particle that looks like the example below.
--- title: "Save by publishing" url: /docs/en-us/education/build-it-play-it-mansion-of-wonder/save-by-publishing last_updated: 2026-06-29T19:34:00Z description: "Learn how to save the projects you make in Roblox Studio for the Build it Play It Mansion of Wonders challenge." --- # Save by publishing Before moving to the next section, take a moment to save the project. It's important to save the whole project by publishing it to Roblox. **Publishing** just saves your project to your Roblox account, it doesn't make it public yet. It's a good idea to publish every ten minutes while you're working or after making a big change. 1. Select **File** (top left of Studio) and click **Publish to Roblox** to open the publishing window. 2. Enter a **name** for your experience and an optional **description**. 3. When you're happy with the name and description of your project (you can always go back and change it), click the **Create** button. Once published, a project can be edited from any computer since all published projects connected to your account. > **Info:** Next time you want to save your work, just go to **File** ⟩ **Publish to Roblox** or use the hotkey combo (`Alt``P` or `⌥``P`). --- title: "Test and play" url: /docs/en-us/education/build-it-play-it-mansion-of-wonder/test-and-play last_updated: 2026-06-29T19:34:00Z description: "Learn how to use Roblox Studio to play the Build It Play It Mansion of Wonder template game." --- # Test and play To see how the carnival ride works, test it out. When playing, you'll ride on a cart and blast away approaching enemies. Just make sure no enemies touch the ride - too many hits from an enemy and the cart will explode! 1. Click **Play** to test. 2. In the experience, walk up to the character under the **Play It** sign and press `E` to open up a menu. On that screen, click **Join Game**. > **Warning:** During the ride, you can't use your mouse to click parts of Studio. To use the mouse, press `1` on the keyboard. 3. When the ride starts, left click to shoot the blaster at enemies. Watch the cart's **health** at the top of the screen and try and make it to the treasure room at the end. 4. Either finish a round, or press `1` to use the mouse. To stop testing, press the **Stop** button or press `Shift``F5` on the keyboard. --- title: "Use particles for actions" url: /docs/en-us/education/build-it-play-it-mansion-of-wonder/using-particles-for-actions last_updated: 2026-06-29T19:34:00Z description: "Learn how to use particle effects to give players feedback to their actions, and learn several techniques to further customize your particles in Roblox Studio. Part of the Build It Play It Mansion of Wonder series." --- # Use particles for actions With arrows finished, first-time players can follow the path to their goal, making their initial introduction clearer and less likely that they get lost and quit. To further enhance the user experience, let players know they've successfully completed their task with an appealing particle effect. In the example below, a celebratory burst of ticket-shaped particles emits whenever players interact with the booths. > **Info:** Giving **multiple forms** of feedback is also another way of improving a player's user experience. A player with the sound down or one who is hearing impaired may rely more on visual cues. Other players may be low vision and paying more attention to sound. ## Create particles When creating a particle, it needs to be inserted into a part or attachment. In this example, you'll place particles in the TestPlayer object used earlier. 1. In **TestPlayer**, create a **ParticleEmitter** named **Burst**. Default particles will start emitting from the TestPlayer object. You'll customize the emitter to create a burst effect. An example is shown below. > **Warning:** Particles must be named exactly since a script will be looking for the name later. 2. With **Burst** selected, go to the **Properties** window and find the **Texture** property. Copy and paste one of the texture IDs supplied below, or use your own, into the texture field and press `Enter`._6772766862__6772766551__5857851618__6803084085__6772783963__6703369286__6749057157__6772766413_ If using the ticket texture, it will look like below. > **Info:** Custom textures can be any image that you have. Learn to upload them in the [Asset Manager](/docs/en-us/projects/assets/manager.md) article. 3. In the **Explorer** window, select **TestPlayer**, then in the **Properties** window, find and change the **Color** and **Size** properties to something appropriate for your experience. For example, the following image includes particles with the following settings: - **Color** = `255, 178, 44` - **Size** = `0.6` ## Create a burst effect Different properties can be changed so that the particles look more like a quick burst rather than a gentle stream. After designing the particle, you'll disable the emitter so that it only plays when activated by a script. ## Make particles spread The ParticleEmitter sends out particles along two planes controlled by the property, **SpreadAngle**. 1. To make the particles fly out in all directions like the example, set the **SpreadAngle X** and **Y** to **360**. ## Different burst properties After adding the recommended values under the video, the particles will look like they are quickly bursting from the player like a firework. 1. To get a burst motion, set the following properties to these values to make the particles explode and then quickly fade out. | Property | Value | Rationale | | --- | --- | --- | | **LightEmission** | 0.4 | Adds a faint glow. Note the maximum is 1. | | **Drag** | 8 | More drag causes the particles to quickly lose speed. | | **Lifetime** | 0.6, 1 | Makes particles exist for between .6 and 1 seconds. | | **Rate** | 50 | How many particles are emitted per second. | | **Speed** | 40 | How fast the particles are going when first emitted. | > **Info:** Try adding some randomized numbers to make particles feel less repetitive. Some properties, such as **Lifetime** and **Rotation** add randomization by allowing for a minimum and a maximum value. 2. Lastly, the particle should only play when the script (which you'll see in the next lesson) tells it to. Find the **Enabled** property and toggle it **off**. > **Info:** This page only covered a few properties. For more, see the [Particle Emitters](/docs/en-us/effects/particle-emitters.md) reference article. --- title: "Use textures" url: /docs/en-us/education/build-it-play-it-mansion-of-wonder/using-textures last_updated: 2026-06-29T19:34:00Z description: "Learn how to customize and change the texture of particle emitters in Roblox Studio as part of the Build It Play It Mansion of Wonder." --- # Use textures Every particle created by a particle emitter, such as white sparks or fire, is actually a picture. In Roblox Studio, these pictures are called **textures**, which can be any 2D visual, like a symbol or even something you've drawn. For example, notice in the video below how a single texture is changed and repeated to create an effect. ## Change the blast texture 1. A starter pack of textures has been provided. Pick one you want to use for your blast and **copy** the **number** beneath its image below. > **Info:** These textures are used for both your blast and explosion. Before moving on, take a moment to think of you want different textures or the same._5860841663__5857851812__5857851618__6711256324__5833235272__6772783963__5833323391__5857892330__5857892405__5857931724__5857851618__5860841737_ 2. Make sure the emitter is selected, then in the **Properties** window, find the **Texture** property. > **Warning:** If you can't see the properties shown in the picture above, you may need to reselect the emitter. To do so, in the Explorer search bar, type BlastParticles and then select the BlastParticles emitter. 3. Click in the **Texture** row to highlight the text. Press `Ctrl``V` (`⌘``V`) to paste in the previously copied ID and then press Enter. Your particle emitter now emits particles that look like your texture. For example, if you use the `5833323391` texture, your particles will look like the example below. --- title: "Code the story" url: /docs/en-us/education/build-it-play-it-story-games/code-the-story last_updated: 2026-06-29T19:34:00Z description: "Part of the Story Games series that teaches you to code in Roblox. Use string variables and concatenation to code the story script." --- # Code the story After the player answers all of the questions, they'll get to see their answers combined with the story. The story will also be stored in a variable using strings and then be combined with the strings holding the player's answers. ## Code the first string Remember the first sentence you wrote for the story? Time to add that into the code. 1. Make sure the playtest is stopped. 2. Go back to the script by clicking on the StoryManager script tab above the game editor. If you don't see the script, look at the Explorer, find StoryManager, and double click it. 3. Under where you typed the question, create a new variable named `story`. Make sure the variable name is **lowercase**.```lua -- Code story between the dashes -- ============================================= local name1 = storyMaker:GetInput("What is your favorite name?") local story -- ============================================= end ``` 4. To find the first string, go back to the original story. Circle or highlight everything **before** the first placeholder. If your variable happens to be in the middle of a sentence, the rest can be added later.**Original Placeholder**: _In a tree on a hill, lives the great wizard_ name1. 5. Have the story variable store the string like below. Make sure to add a space after the last word but before the quotation mark.```lua -- Code story between the dashes -- ============================================= local name1 = storyMaker:GetInput("What is your favorite name?") local story = "In a tree on a hill lives the great wizard " -- ============================================= ``` ## Add the name Next, the first string of the story needs to be combined with the player's answer. Combining things together is called **concatenation**. To combine the two strings together, use `..` 1. On the same line as the story variable, type ..```lua -- Code story between the dashes -- ============================================= local name1 = storyMaker:GetInput("What is your favorite name?") local story = "In a tree on a hill lives the great wizard " .. -- ============================================= ``` 2. Still on the same line, type the name of the variable holding the player's answer.```lua -- Code story between the dashes -- ============================================= local name1 = storyMaker:GetInput("What is your favorite name?") local story = "In a tree on a hill lives the great wizard " .. name1 -- ============================================= ``` ## Show the story Now that the story is typed, it needs to be shown to players. T 1. Under the second dashed line, find `storyMaker:Write()`. Between the `()`, type the variable `story`. This tells the program to write the story in the game.```lua -- Code story between the dashes -- ============================================= local name1 = storyMaker:GetInput("What is your favorite name?") local story = "In a tree on a hill lives the great wizard " .. name1 -- ============================================= -- Add the story variable between the parenthesis below storyMaker:Write(story) ``` 2. Playtest the game. You should see the two strings (show by different colors in the picture below) combined together. ### Troubleshooting tips If the sentence didn't show up, try one of the following. **If the question is not being asked**: - Check that the question is inside of quotation marks. **If the story is combined**: - The first part of the story is inside of quotation marks. - The name of the variable holding the player answers matches exactly. Capitalization counts! - The name of the variable holding the player's answer is not inside of quotation marks. - The two strings are separated by `..` **If the story doesn't appear**: - Look at `storyMaker:Write()`. Check that in between the `()` is the `story` variable. --- title: "Code a question" url: /docs/en-us/education/build-it-play-it-story-games/coding-a-question last_updated: 2026-06-29T19:34:00Z description: "Part of the Story Games series that teaches you to code in Roblox. Code the first question asked to players." --- # Code a question Remember how you wrote a sentence for your story, then swapped a word out for a placeholder? It's time to give players a chance to add something to your experience. In the script, the placeholder you made will be a **variable**. In coding, variables are placeholders for information, in this case a word. You'll start by asking players a question. Then, they'll type in an answer that gets **stored** in the variable. ## Create a variable Variables have names that tell programmers what they store. In this case, you'll create a variable called `name1` for the placeholder. 1. Click below the dashed lines and type `local name1`.```lua -- GLOBAL VARIABLES local storyMaker = require(script:WaitForChild("StoryMaker")) -- Code controlling the game local playing = true while playing do storyMaker:Reset() -- Code story between the dashes -- ============================================= local name1 -- ============================================= -- Add the story variable between the parenthesis below storyMaker:Write() -- Play again? playing = storyMaker:PlayAgain() end ``` ## Set a variable Now players need to have a chance to put something inside the placeholder. To change a variable, it needs to be **set** to something using the **=** symbol. 1. After `name1`, make sure to add a space and then type `=`.```lua while playing do storyMaker:Reset() -- Code story between the dashes -- ============================================= local name1 = -- ============================================= -- Add the story variable between the parenthesis below storyMaker:Write() end ``` 2. After the equal sign, type `storyMaker:GetInput()`. The code must be typed exactly as is, and capital letters must match.```lua while playing do storyMaker:Reset() -- Code story between the dashes -- ============================================= local name1 = storyMaker:GetInput() -- ============================================= -- Add the story variable between the parenthesis below storyMaker:Write() end ``` ## Type a question Variables can store different types of data including small numbers, true or false values, and strings. **String** type variables are special because they can store whole sentences. It's easy to spot string type variables because they're always in quotation marks "like this". The question to ask players will be a string variable. 1. In `GetInput()`, click **between** the parentheses. Inside type a question enclosed by quotation marks.```lua -- Code story between the dashes -- ============================================= local name1 = storyMaker:GetInput("What is your favorite name?") -- ============================================= end ``` --- title: "Complete the challenge" url: /docs/en-us/education/build-it-play-it-story-games/complete-the-challenge last_updated: 2026-06-29T19:34:00Z description: "Part of the Story Games series that teaches you to code in Roblox. Learn how to share an experience in Roblox." --- # Complete the challenge **Congratulations**, you just finished the latest Roblox Creator Challenge! Now that you've completed the experience, here are a few ideas to take your experience further and keep learning. ## Share your experience One of the most exciting features of Roblox is that it's really easy to share your experience with friends. Currently your experience is probably private, meaning only you can play it. For your friends to see the experience, you need to make it public. 1. Select **File** (top left of Studio) and click **Experience Settings**. 2. On the left bar, select **Permissions**, then choose **Public**. 3. Click the **Save** button. 4. In the top right of Studio, click on the **Share** button. A pop-up menu displays. 5. Click the **Copy Link** button, then share the link with your friends. ## Add more to the story Although you completed the challenge, there's always more that you can add into the story. Here are a few ideas: **Add More Characters** – Think of one or two more characters in your story. Remember to create a new variable for each character, like name2 or name3. **Add More Lines** – Write down a new sentence for your story and pick an object you can replace. --- title: "Finish and add more" url: /docs/en-us/education/build-it-play-it-story-games/finish-and-add last_updated: 2026-06-29T19:34:00Z description: "Part of the Story Games series that teaches you to code in Roblox. Continue adding more to the story script." --- # Finish and add more You're **almost** done with the project! What's left is to complete the first sentence, then add in another question to give players some more choice. ## Finish the sentence To add more words or punctuation to the sentence, add another string using concatenation. 1. On the same line as the story variable, type `..` 2. Add another string containing the rest of the sentence, or just punctuation. Don't forget to add an extra space at the end of the sentence.```lua -- Code story between the dashes -- ============================================= local name1 = storyMaker:GetInput("What is your favorite name?") local story = "In a tree on a hill lives the great wizard " .. name1 .. ". " -- ============================================= ``` ## Add a second question To ask a second question, create a new question and keep adding to the same variable holding the story. 1. Decide what word to delete from the second sentence in your story.**Original Placeholder:** _Every morning, the wizard loves eating a giant bowl of honey roasted_ **food1**. 2. Beneath the first variable, create a new variable to act as a placeholder.```lua local name1 = storyMaker:GetInput("What is your favorite name?") local food1 local story = "In a tree on a hill lives the great wizard " .. name1 .. ". " ``` 3. Use `storyMaker:GetInput("")` to ask the player a question and store their answer.```lua local name1 = storyMaker:GetInput("What is your favorite name?") local food1 local story = "In a tree on a hill lives the great wizard " .. name1 .. ". " ``` 4. In the story variable, concatenate the next story string using `..` Be sure to include a space after the end of the sentence.```lua local name1 = storyMaker:GetInput("What is your favorite name?") local food1 = storyMaker:GetInput("What is your favorite food?") local story = "In a tree on a hill lives the great wizard " .. name1 .. ". " .. "Every morning, the wizard loves eating a giant bowl of honey roasted " ``` > **Info:** You may need to scroll in the code editor window to the right for longer sentences. 5. After the new story string, concatenate the answer for the second question and finish with punctuation.```lua local name1 = storyMaker:GetInput("What is your favorite name?") local food1 = storyMaker:GetInput("What is your favorite food?") local story = "In a tree on a hill lives the great wizard " .. name1 .. ". " .. "Every morning, the wizard loves eating a giant bowl of honey roasted " .. food1 .. ". " ``` ## Optional additions If you're interested in developing the story more, we've included some ideas. For example, some ways of improving the story include: - Add more lines to your story - Playtest the game every time you add a new set of variables and strings - Ask a peer or friend on what other words they would like to customize. Also, below are some **tips and tricks** to make stories for fun for players. ### Use variables more than once Variables can be used more than once — just use concatenation between strings where you want to include the word(s). **Example Code**: `"I am " .. name1 .. " and you are in the palace of " .. name1 .. "!"` **Result**: I am Sameth and you are in the palace of Sameth! ### Add line breaks Line breaks can be added by typing `\n` in a string. Also, more than one line break can be combined like `\n\n`. **Example Code**: `"One \n Two \n\n Three"` **Result**: One Two Three --- title: "First challenge" url: /docs/en-us/education/build-it-play-it-story-games/first-challenge last_updated: 2026-06-29T19:34:00Z description: "The first part of the Story Games series that teaches you to code in Roblox. Complete a quiz to earn a free Roblox avatar prize." --- # First challenge Test your knowledge and earn the first of three prizes by clicking the button below to open a Roblox quiz game. After earning your prize, come back here to continue the lesson. --- title: "Story Games project" url: /docs/en-us/education/build-it-play-it-story-games/landing last_updated: 2026-06-29T19:34:00Z description: "Learn to code in Roblox with this word game. Script your first experience and learn about variables and computer science." --- # Story Games project In Roblox Story Games, words are going missing and it's up to players to fill in the blanks! Participate in the **Hour of Code**™ by coding your own story game using Roblox's free coding and design tools. Prove your coding knowledge to earn exclusive badges and avatar items. Play the video below to see the library you'll be working in and the game in action. ### Your coding journey You'll create your story game by going through three different lessons. Each lesson will have instructions teaching you how to code your game from start to finish. After finishing a lesson, you'll get the chance to earn prizes by playing a quiz game on Roblox. After earning the prize, come back and continue the next lesson. _1: Creating Variables_ _2: Getting Player Answers_ _3: Telling the Story_ After finishing a lesson, you'll get the chance to earn prizes by playing a quiz game on Roblox. Once you earn a prize, come back and continue the next lesson. ## Get Roblox Studio ready **Roblox Studio** is used to make every game you see on Roblox. If you already have Roblox Studio installed, login and click the Next button at the bottom of this page. 1. Download and install Roblox Studio. 2. After installing, double-click the desktop icon (Windows) or click the dock icon (Mac). 3. On the login screen, enter your Roblox username and password, then click **Log In**. > **Info:** If you're new to Roblox and are [signing up](https://www.roblox.com/home) for the first time, remember these safety tips: - **Never share your password**, even with a real life friend. - **Make your password hard to guess** — If your username is "bloxdev," your password should not be "bloxdev123." - **Roblox employees will never ask for your password** — Report anyone who asks using the Report Abuse feature. - **There's no such thing as free Robux** — Never trust players or sites who say they have a secret way to get free Robux! > > For more tips, please see [Keeping Your Account Safe](https://en.help.roblox.com/hc/en-us/articles/203313380-Account-Security-Theft-Keeping-your-Account-Safe-). --- title: "Open the template" url: /docs/en-us/education/build-it-play-it-story-games/opening-the-template last_updated: 2026-06-29T19:34:00Z description: "Part of the Story Games series that teaches you to code in Roblox. Open the template used to code the story game experience." --- # Open the template With the story created, it's time to turn that vision into code using Roblox. ## Start Roblox Studio The experience will be created using **Roblox Studio**. It's free to use and can instantly publish games to iPhone, Android, Xbox Live, PC, Mac, and VR. 1. Open **Roblox Studio** by double-clicking the it's icon on the desktop (Windows) or clicking the dock icon (Mac). 2. On the login screen, enter your Roblox username and password, then click Log In. ## Open the template A template has been created that has everything needed for the experience to work, except the code for the actual story. Templates are pre-made worlds that you can use as a base for your own experiences. 1. Download the template. 2. In Roblox Studio, in the top left, click **File** → **Open from File** and select the downloaded file. ## Close extra windows The first time you launch Roblox Studio, extra windows might open up that you don't need right now. Closing the extra windows will give you more space to see what you're doing. 1. Close each window on the **left** of the 3D view by clicking the **×** in the window's upper-right corner. If you don't see anything to close on the left, go to the next step. 2. Leave the **Explorer** and **Properties** windows open on the right side of Studio. --- title: "Second challenge" url: /docs/en-us/education/build-it-play-it-story-games/second-challenge last_updated: 2026-06-29T19:34:00Z description: "The second part of the Story Games series that teaches you to code in Roblox. Complete a quiz to earn a free Roblox avatar prize." --- # Second challenge Test your knowledge and earn the first of three prizes by clicking the button below to open a Roblox quiz game. After earning your prize, come back here to continue the lesson. --- title: "Start coding" url: /docs/en-us/education/build-it-play-it-story-games/start-coding last_updated: 2026-06-29T19:34:00Z description: "Part of the Story Games series that teaches you to code in Roblox. Open the script used to manage the experience." --- # Start coding In Roblox, code is typed inside of **scripts** using the coding language [Luau](https://luau.org). Games often have separate scripts for each thing the game needs to do. The library template already has a script named **StoryManager** which you'll add more code to for your word game. ## Find the StoryManager 1. In the **Explorer** window, click the arrow next to **StarterGUI** to see everything beneath it. 2. Click the arrow next to **GameGUI** to expand that section. 3. Double-click the **StoryManager** script to open it. ## Script contents The script already contains some of the code that's necessary for showing the completed story to the player. All of the code you create will be typed below the dashed lines. For now, notice how a large portion of the script starts with the `--` symbol. Lines of code starting with `--` are called **comments**. These are used to leave notes for coders and don't change the way a program runs. ```lua -- GLOBAL VARIABLES local storyMaker = require(script:WaitForChild("StoryMaker")) -- Code controlling the game local playing = true while playing do storyMaker:Reset() -- Code story between the dashes -- ============================================= -- ============================================= -- Add the story variable between the parenthesis below storyMaker:Write() -- Play again? playing = storyMaker:PlayAgain() end ``` --- title: "Test and save" url: /docs/en-us/education/build-it-play-it-story-games/test-and-save last_updated: 2026-06-29T19:34:00Z description: "Part of the Story Games series that teaches you to code in Roblox. Save and publish the experience in Roblox Studio." --- # Test and save This is a good point at which to check your code and make sure it runs. Testing your code often makes it easier to figure out where you might have made a mistake. 1. Test your by code by clicking the **Play** button. 2. Walk up to the pedestal using `W` `A` `S` `D` on your keyboard and press `E`. | Control | Action | | --- | --- | | `W` | Moves the camera forward. | | `S` | Moves the camera back. | | `A` | Moves the camera left. | | `D` | Moves the camera right. | | `Spacebar` | Your playable character jumps. | | Hold and drag the `Right Mouse Button` | Moves the camera view around. | 3. Check to see if your question is displayed. **Don't** try to answer the question or click the "Submit" button. The code for that isn't made yet. 4. **Stop** the playtest. ## Save your code It's important to save the whole project by **publishing** it to Roblox. It's a good idea to publish every ten minutes while you're working or after making a big change. 1. Select **File** → **Publish to Roblox** to open the publishing window. 2. Enter a place name and an optional description. 3. When ready, click the **Create** button. Once published, games can be edited from any computer since they're connected to your account. --- title: "Third challenge" url: /docs/en-us/education/build-it-play-it-story-games/third-challenge last_updated: 2026-06-29T19:34:00Z description: "The third part of the Story Games series that teaches you to code in Roblox. Complete a quiz to earn a free Roblox avatar prize." --- # Third challenge Test your knowledge and earn the first of three prizes by clicking the button below to open a Roblox quiz game. After earning your prize, come back here to continue the lesson. --- title: "Write the story" url: /docs/en-us/education/build-it-play-it-story-games/writing-the-story last_updated: 2026-06-29T19:34:00Z description: "Part of the Story Games series that teaches you to code in Roblox. This section is brainstorming ideas for a story." --- # Write the story ## Brainstorm ideas Before coding, write the story you'll use as the base of your game. The story can be about anything you wish. To start, on a piece of paper or using a text editor, **write 2-3 sentences** for the opening paragraph. Here's a few story ideas to get you started: - Your dream vacation - What a wizard eats for breakfast - A superhero's shopping trip An example story is below: _In a tree on a hill lives the great wizard Nia. Every morning, the wizard loves eating a giant bowl of honey roasted lizard leaf._ ## Create placeholders With an idea for a story, you can start the process of turning it into a game. Remember, in your experience, players will type in words that **replace** key parts of your story. For this step, you'll find parts of the story that can be **placeholders**, where a player can add in their own input. 1. In the first sentence, pick a single word to be replaced by the player such as a name, action, or a noun. In this example, we'll select the wizard's name._In a tree on a hill lives the great wizard **Nia**._ 2. Replace the word with a placeholder for what type of word you'll ask the player for. Since you might end up asking the player for more than one noun or adjective, number the playerholder._In a tree on a hill lives the great wizard **name1**._ With the sentence created, it's time to turn it into code! --- title: "Collaborating in Roblox" url: /docs/en-us/education/collaboration/collaborating-in-roblox-landing-page last_updated: 2026-06-29T19:34:00Z description: "Educators can use Roblox for collaborative learning in the classroom. Learn strategies and tips for building social-emotional skills." --- # Collaborating in Roblox As educators, we understand the value collaboration offers. Whether it's building social-emotional learning skills or preparing students for future jobs, it's just more rewarding to work together. That's why Roblox offers a variety of tools and resources to suit your needs. ## Tools and features | | [

Live collaboration

](/docs/en-us/projects/collaboration.md) Collaboratively build worlds and edit scripts in real time using Roblox Studio. Multiple students can work together to design a virtual city or solve a scripting exercise. | | --- | --- | | | [

Groups

](/docs/en-us/projects/groups.md) Create and manage a centralized webpage to connect with students online. Find student accounts, post announcements and share projects. | | | [

Private servers

](/docs/en-us/education/support/private-servers-for-classroom-use.md) Play existing games together with your students. Host class lessons, virtual field trips, or even test student games. | | | [

Best practices for collaboration

](/docs/en-us/education/collaboration/collaboration-best-practices.md) Practical tips and strategies on fostering meaningful collaboration with Roblox. | ## Handouts These handouts can be printed out and distributed to your class. #### Collaboration guidelines Provide students a resource so they can help others and independently resolve conflict. #### Trust and safety brochures Communicate with parents about Roblox safety, privacy, and educational benefits.
--- title: "Best practices for collaboration" url: /docs/en-us/education/collaboration/collaboration-best-practices last_updated: 2026-06-29T19:34:00Z description: "Learn how Roblox is used in the classroom to build collaboration and communication skills among students. These tips are from educators and teachers using Roblox to teach and inspire." --- # Best practices for collaboration Educators in both online and in-person classrooms find that using Roblox to collaborate in classrooms can be meaningful as well as educational. Based on the experiences of Roblox educators, here are some ways of fostering a collaborative atmosphere by setting clear expectations and using supportive strategies. ## Set classroom expectations Before starting a Roblox classroom, create guidelines like the ones below for students up-front. Encourage students to add on with their own suggestions. ### Classroom guidelines - **Build your own, don't copy.** If you do want to use someone's work, ask permission. - **Respect the work of others.** Never vandalize or put down the work of others. - **Solve problems together.** Assume the best of each other. Try and solve a conflict without teacher intervention. > **Info:****Download handout** This handout includes the aforementioned guidelines. It can also be edited to suit your classroom. ### Conflict resolution Help students independently resolve conflict by modeling and teaching them the tips below. - Encourage **"I" messages** _("I feel …")_ rather than **"you" messages** _("You did …")_. This helps both sides listen. - Have students take turns speaking without interruption. If a student wants to interrupt, prompt them to take a deep breath or do a small private hand gesture (e.g. tap themselves on the thumb) to signal they should focus. - When trying to find a resolution to a conflict, talk about interests and goals _("I feel this would look better blue")_, rather than positions _("You're wrong, blue is the best color …")_. Sharing interests and goals gives students something concrete to try resolving conflict. ## Collaboration strategies One way of creating a positive learning environment is to use strategies that foster collaboration. Below are strategies to meet specific goals you may have. ### Students helping one another Strategies that encourage students to work together build independence and make collaboration feel more natural. #### Help students help themselves - If students have a question, have them ask three peers before an instructor in a process often called **"Ask Three Before Me"**. During a class, remind students of this rule whenever they're asking you for help. #### Reward sharing - Allow students to contribute by letting them lead **skill-shares**. If a student discovers something fun, or has a skill they want to share, give them 5-10 minutes during an independent work time to lead a tutorial. This encourages an atmosphere of sharing and rewards students for wanting to help others. ### Facilitate groups One way of having collaboration is often through group work. To ensure group work is productive and meaningful, we recommend the following strategies. #### Assign roles - Depending on the project, have students take on **roles** (either chosen or assigned). For instance, a team building a game may include a world builder and a coder. If you assign roles, be sure to rotate them so students learn new skills. They also might discover something they didn't expect to enjoy. #### Have groups manage a "to-do" list - To ensure all group members are busy, have a group collectively manage a to-do list. If someone has an idea or task, they can add it onto the list for someone who needs something to work on. ## Further reading - [Colleen Lewis: Tips for Pair Programming](https://www.youtube.com/watch?list=PLHqz-wcqDQIEMMuXadwy90YxN2Qb4SrXT&time_continue=5&v=TWj78n4ZuMY&feature=emb_logo) --- title: "Beta test experiences" url: /docs/en-us/education/developer/beta-testing-experiences last_updated: 2026-06-29T19:34:00Z description: "Learn common methods of beta testing on Roblox, recommended for success metrics, and options for community feedback." --- # Beta test experiences Developers on the Roblox platform have a strong culture of conducting beta tests in order to get feedback from the community and to begin collecting analytics about their user base. There are several ways to collect user feedback, starting with closed betas and then moving to open betas after the initial feedback has been addressed. ## Testing options There are different methods for beta testing an experience depending on the project's current playability and testing goals. While the table below provides a quick summary, each of the following methods will be discussed in-depth. | Method | Ideal for | | --- | --- | | **Closed access** | Ideal for studios interested in testing specific features or unpolished builds to get initial player reactions. Can often follow up with, or prepare for an open access beta. | | **Open paid access** | Similar to open access beta but testers are users paying to play. This often results in a smaller volume of users, but more motivated in providing feedback. | | **Limited time test** | Provides benefits of an open access beta but requires less of the commitment and investment needed to run an open access beta. Experiences can be playable but have minimal, or focused content. | | **Open access beta** | For experiences that are relatively polished and playable, but may lack content. Best for studios interested in testing at scale to gain the largest amount of user data and impressions. | In general, a developer is recommended to launch a beta program whenever the experience meets the following criteria: - A playable experience where the user can go through the entire game loop, even if there's only one area, or content is limited. - The experience is performant on all target platforms, such as mobile or desktop. - Resources are dedicated to supporting a community or accepting feedback. > **Info:** If your studio is unclear on which option is best, Roblox Education recommends closed access testing. This method tends to be the safest, but also provides enough data to address bugs and gameplay issues. ### Closed access beta Closed access programs are best for studios interested in testing experiences that may be unpolished or not fully ready for a large audience. In this program, users from a trusted group are granted access to the Roblox group where the experience is published from. Trusted members can often be from pre-existing communities or friends and family. To launch the closed access beta, users are given invites to, or request to join, a Roblox group. Once in, they are granted a role that permits them to access an experience published by that group. Note that the group and whitelisting must be managed and set up by the developer. | Benefits | Considerations | | --- | --- | | | | ### Open paid access Similar to open access betas, these experiences should be playable, mostly bug free, but can be missing content. Here, users pay an upfront Robux fee to access the experience (see [Paid access in Robux](/docs/en-us/production/monetization/paid-access-robux.md)). This limits the amount of users, often providing a more motivated testing audience before opening it to a wider audience. | Benefits | Considerations | | --- | --- | | | | ### Limited time test Running an open beta takes time and energy away from development as developers must respond to feedback and interact with their community. A lower-impact alternative is the limited time test. In this method, developers plan for an open access beta test during a specific time frame, such as a key weekend or week. An experience is opened and closed based off the time. Similar to the open access beta, the experience should be playable, generally bug-free, but can lack content. To launch the limited time test, we advise developers to advertise the event. Developers can use experience [notifications](/docs/en-us/production/promotion/experience-notifications.md), group, and [social media](/docs/en-us/production/promotion.md#social-media) to communicate when the experience will be temporarily opened. | Benefits | Considerations | | --- | --- | | | | ### Open access beta If developers are confident their experience is playable by the general public, but may be lacking content, polish, and quality of life features, an open access beta is feasible. To launch an open beta, set the experience to public, which allows any Roblox user to play. If you enable **Beta mode**, the experience doesn't show up under **Recommended For You**. Some recommendations for running an open access beta are below. - Encourage users to join a Roblox group connected to the experience, or social community such as Discord. This provides users an avenue to leave feedback. - Inform users that the experience is in a beta state. This can be done by adding the tag _Beta_ to the experience title and using in-experience pop-ups to set expectations as to the occasional bug or unfinished feature. ![Picture of Roblox game with beta tag](../../assets/education/beta-testing-edu-experiences/BetaTestingGame.png)_Example game page showing a beta_ | Benefits | Considerations | | --- | --- | | | | ## Get community feedback Regardless of how a beta is implemented, some means for recording user feedback should always be implemented. Two methods for obtaining direct user feedback are in-experience feedback forms and Discord. ### In-experience feedback Feedback systems can be built directly into the experience with UI pop-ups at key moments. Two examples can be seen in the experiences [Stranger Things](https://www.roblox.com/games/5853107391/Stranger-Things-Starcourt-Mall) and [Alo Sanctuary](https://www.roblox.com/games/8523408215/Alo-Sanctuary). > **Info:** Additionally, some community-developed tools have been created to make feedback forms easier (see [Form Embedder](https://devforum.roblox.com/t/form-embedder-an-impossibly-simple-way-to-add-a-google-form-to-your-experience/2183701)). Note though that community developed tools are not officially supported by Roblox. | Benefits | Considerations | | --- | --- | | | | ### Discord communities Many developers choose to create Discord communities for their experiences. To setup communities, we recommend that you investigate large experiences on Roblox, such as Adopt Me. Seeing how other communities are structured can inform your own decisions, such as what channels users will expect to see, and how users may engage with your community. | Benefits | Considerations | | --- | --- | | | | ### Think-aloud feedback A think-aloud is a method of studying the mental processes people use in a task. Participants communicate their thoughts during gameplay, often in stream of conscious, as a way of understanding different goals, such as the clarify of an educational objective. For developers interested in doing a think-aloud, it's recommended they find participants in their target age demographic, either through family and friends, or a social community. | Benefits | Considerations | | --- | --- | | | | ## Analytics Analytics gathered during a beta program are often used to gauge if a launch experience is on track to meeting its goals. For instance, many common Roblox experiences have specific goals for retention, monetization, and session time. ### Track analytics Roblox provides a variety of built-in analytics using the [Analytics Dashboard](/docs/en-us/production/analytics/analytics-dashboard.md). | **Roblox Provided Analytics** | | | --- | --- | | **Common custom analytics** | | ### Measure success Measuring success is often determined by the analytics important during a beta. To help determine what analytics are important during a beta, consider these questions. - How much content is in the experience and what level of retention and engagement should be aimed for? For instance, a one-time guided classroom experience will focus more on average session length than retention. - What is the intended target age for users? What was the age range of users during the beta, and was there any discrepancy? For all experiences, consult its [analytics](/docs/en-us/production/analytics.md) to determine: - Onboarding and tutorial completion. - Metrics related to educational learning objectives. These differ between experiences. - Player drop off points. For instance, if users are not exploring specific areas, or using game features. > **Info:** One universal metric for success is an experience's likes, which often influences a user's decision to play. Developers should strive for an at least 80% like ratio after the close of a beta program. ## Frequently asked questions **How long should an experience spend in beta before launching?** Every experience is different, but a general time frame would be 7 to 30 days. This is specifically based off getting enough data to determine D1 and D30 retention metrics. **Do developers engage live in-game with users during a beta?** This is optional, but many developers enjoy entering their experience to converse with users. There is also a culture in Roblox to reward this. For instance, some experiences reward badges for meeting a developer. **When should we start building a community?** It's recommend to build a community **before** beta testing. This community can be started on networks such as Discord or Twitter. As long as you have content, such as screenshots, videos, or even livestreams, you can start a community without a playable experience. Starting a community can help find sourcing testers for the beta much easier. --- title: "Design educational experiences" url: /docs/en-us/education/developer/designing-educational-experiences last_updated: 2026-06-29T19:34:00Z description: "Learn how to get started creating educational experiences and games using Roblox." --- # Design educational experiences Educational game design presents unique challenges and opportunities. For instance, developers need to consider the constraints of schools, such as limited technology and time. To support developers, this presentation (in PDF and video formats), serves as an introduction to creating educational experiences. The topics covered include: - What are educational games and experiences? What are attributes of good experiences? - What is the recommended process of designing an educational game, and how does it contrast with traditional game development? - What are recommendations for working with audiences such as schools or at-home learners? How do audiences differ in needs? Additional topics covered include: - How to design a hypothetical educational experience. - How to identify learning objectives. - How to plan for development constraints. - How to design a monetization plan. ## Slide deck Download the presentation as a `.PDF` below. ## Video The below presentation was taken at Connect 2022, a virtual conference focused on game development in Roblox. --- title: "Plan for educational settings" url: /docs/en-us/education/developer/planning-for-educational-settings last_updated: 2026-06-29T19:34:00Z description: "Covers how to design educational games and experiences for classes and teachers. Includes information on user needs, common devices found in schools, and unique considerations like safety." --- # Plan for educational settings Designing a Roblox experience for a classroom is similar to designing for traditional audiences - with a few additional considerations. When creating a game design document for education, there are differing user needs and classroom logistics to account for. This article covers both the processes and considerations for designing educational experiences on the Roblox platform for use within K-12 American classrooms. > **Info:** Note that this document will focus on design considerations for usage within a traditional school classroom, and may not apply to experiences meant for supplemental education, edutainment, or adult education markets. ## Plan an education experience During the planning stages of the experience, designers need to identify what learning standards will be utilized within the experience, and then how the usage of those learning standards will be communicated to teachers. These steps, though not in the traditional game design process, ensure that learning is built into a core game loop and that educators understand how to utilize an experience in the classroom. ### Start with learning standards Many game designers start with a mechanic or idea. Educational game designers, instead, start with learning standards and objectives, then create gameplay that supports the learning. Learning standards are concise descriptions of what a student is expected to know or do, such as: _"Apply Newton's Third Law to design a solution to a problem involving the motion of two colliding objects"_. In practice, standards are adopted by states and determine what concepts a teacher must cover in class. For teachers to adopt learning tools into their classroom, that tool will often need to adhere to specific standards sought by that educator. An example of a science learning standard is below. | Disciplinary core idea | Learning standard | Standard description | | --- | --- | --- | | Motion and Stability: Forces and Interactions | [MS-PS2-1](https://www.nextgenscience.org/pe/ms-ps2-1-motion-and-stability-forces-and-interactions) | Apply Newton's Third Law to design a solution to a problem involving the motion of two colliding objects | > **Info:** Learning standards vary state-by-state, and even by country. For example, different states may teach Newtonian physics in different ways, but will share universal content similarities. For more details on locating relevant learning standards, a list of institutions offering standards is listed in [Additional Resources](#additional_resources). ### Define learning standards Within the design process for an educational experience, standards will serve as inspiration for game mechanics, and be crucial parts of a core game loop. For a designer, to define the standards which will be used, we recommend the following: 1. Determine the **topic** to cover, such as gravity, geometry, or climate change and the **target age range** of your experience, such as middle school or 10th grade. This will inform where to research learning standards. Example: _High school physics_ 2. Identify learning standards(s) for the experience. For more on this topic, see [Further Reading](#further-reading)._Example: [HS-PS2-1](https://www.nextgenscience.org/pe/hs-ps2-1-motion-and-stability-forces-and-interactions): Analyze data to support the claim that Newton's second law of motion describes the mathematical relationship among the net force on a macroscopic object, its mass, and its acceleration._ > **Info:** The learning standard above is from the institution Next Generation Science Standards. Viewing the standard provides more detail on how to implement it in classrooms, which can serve as inspiration for gameplay. ### Connect learning with gameplay Once you identify a learning standard, you may proceed by establishing learning objectives. A **learning objective** defines what users should know or be able to do after finishing that experience. Objectives are **specific** and **measurable**. When written, the objective breaks down a broad topic, like genetics in biology, into discrete components such as Punnett squares and recessive genes. Additionally, each objective should show how a student demonstrates that knowledge, often by how they are able to describe specific concepts or analyze situations. To continue the design process, we recommend the following: 1. Write **learning objective(s)** for the experience that align with the learning standard._Example: After completing the experience, users will be able to determine the probability of offspring with specific and desired traits._ 2. Create **situations and core game loops** that require players to **demonstrate their understanding** in order to progress. Use inspiration from the standards and objectives to elicit joy, such as playful situations and narrative._Example: In-game, students strategize to breed pets for competitions by deciding which dominant and recessive traits will be prominent in offspring._ 3. Continue to complete a design document following a traditional game design process. ### Release to educators After a design document is completed, there are additional considerations for releasing the experience in classrooms. Educators must understand how an experience helps their students learn, and how best to launch it in their classroom. 1. Determine how the experience's learning standards and objectives will be to communicated to educators. This can be accomplished by listing the standards and objectives on an external website, on a game page, or any educator marketing materials. 2. Plan to provide **supplemental resources** to educators. For example, partner with an educator to write a lesson plan or video to help educators understand how to implement the experience in a classroom. Teachers need to understand how much time an activity will take. It can also be helpful to provide example discussion questions to frame the experience for students. Guides can be found in [Further Reading](#further-reading). ## Identify user needs It is important to balance the requirements of educators with the students' desire for fun and engaging experiences. Use the needs for educators and students listed below as best practices to guide the development of your design document. Note that the needs are universal across all ages, unless stated otherwise. ### Educator needs Educators favor games and experiences that in addition to aligning to educational standards and grade levels, also present few barriers to entry. Teachers need to be able to jump into an experience quickly, with little tutorials.. #### Make design easy to pick up Educators have precious little time to plan, so new classroom tools must be easy to adopt. As such, we recommend the following practices within the experience itself: - **Use Familiar User Interface (UI) Patterns** - If possible, research similar learning games or educational products used by educators. - **Reduce Text, Add Visuals** - Keep explanations simple and visual. When possible, use icons and commonly understood symbols to replace text. - **Avoid Jargon** - Use simple, jargon-free terminology, especially in terms of vocabulary common in gaming. Very few educators understand common "gamer terms" like W-A-S-D but everyone knows arrow keys on a keyboard. #### Design for collaboration and accessibility Educators appreciate activities that engage their students and meet their students' diverse needs. - **Foster Collaboration** - Educators often desire opportunities for students to interact and collaborate. Find ways to make solitary gameplay collaborative, such as encouraging students to discuss and solve a shared problem. - **Design for Accessibility** - Many schools require accessibility features, such as color contrasts for visually impaired students. To learn more, read our [Accessibility](/docs/en-us/production/publishing/accessibility.md) article. ### Student needs Besides meeting educator needs, an experience should be engaging and rewarding to students. #### Design for exploration, not memorization Students learn best when engaged in open-ended exploration that provides meaningful choices. When designing the core loop, ensure students have voice and choice in their decisions. If students can solve problems through rote memorization, they may become bored. For example, imagine a game where students are playing a golf-inspired game on different planets to understand physics. | **Lacks student agency** | Students input different numbers for a ball's acceleration and then press a button to see if the ball enters a goal. Learning is focused on trial and error. | | --- | --- | | **Provides student agency** | Students design a ball by modifying different attributes, such as its mass, material density, and add-ons like wings. They then position their avatar to hit the ball with a desired amount of force. Learning is focused on observation, reflection, and application of a theory, such as "Does more mass help?" | #### Align to learning differences A user's age will impact game design and user experience choices. When designing your experience, identify your target age range and research best practices for that group. Some concepts to research include cognitive differences, such as the complexity of subject matter, and motor skills. To help conceptualize these differences, we provide some examples below. Please note that these differences are not exhaustive. | Component | 8 - 12 year old | 12 - 15 year old | | --- | --- | --- | | **User interface** | Large and minimal GUI. | GUI can follow standard commercial game conventions. | | **Interactions** | Interactions should only take one hand. Keyboard usage should be minimal. | Gestures can use both hands, such as using WASD and clicking with a mouse. | | **Gameplay** | Gameplay should focus on one task at a time, like a simple quest. | Gameplay can include having multiple, simultaneous tasks. | For further research, we recommend the following resources. - [Designing for Kids: Cognitive Considerations (NN/g, 2018)](https://www.nngroup.com/articles/kids-cognition/) - [Design for Kids Based on Their Stage of Physical Development (NN/g, 2018)](https://www.nngroup.com/articles/children-ux-physical-development/) > **Info:** In general, the younger the student, the more impactful the age differences. Teaching a fourteen year old is similar to teaching an adult, while teaching a ten year old may be much different than teaching a nine year old. #### Additional needs When designing for students, traditional design principles still hold true as well. Some additional considerations are as follows: - Students often avoid reading in-game text. Tutorials should be visual and quick. **Don't** rely on text screens to communicate important information. - Provide positive reinforcement when students achieve goals. Reinforcement can be as simple as a burst of particle effects, or getting to playfully interact with an object. If students struggle in-game, provide encouragement and specific feedback on how they can improve. ## Understand the classroom Although each educator's classroom is different, there are common themes that can inform your experience design. ### Educational contexts Before designing an experience, identify the **educational context** where an experience might be used. Some common examples are below. | Setting | Common features | | --- | --- | | **Whole group instruction** | | | **Group activities** | | | **Independent projects or homework** | | ### Common devices Student and educator devices vary, but in general it's best to test experiences on low-end devices for playability. A typical classroom may have Chromebooks, PC laptops, or iPads. For testing purposes, we recommend a device with the following minimum specifications: - **Processor**: Intel® Celeron Processor - Celeron N4000 or better. Intel® i5, or i7 Processor - **Memory**: 4GB or more - **Display**: 11-12 inch HD displays ### Safety and well-being Safety is built into every aspect of Roblox developers must prioritize safety in their experiences. Below are recommendations that may influence a project's design. - **Be Cautious with Violence** - Some educators will not accept any form of violence. If needed, aim for cartoonish violence and avoid direct confrontations. For instance, instead of using a sword, have magical wands that cast spells. - **Disable Voice Chat and Limit Text Chat** - Voice chat can be difficult for educators to supervise, or could be a source of distraction. The same follows for text chat. - **Create a Closed Garden** - Students cannot, under any circumstance, be allowed to interact with the general public while online. Encourage the use of private servers. ## Demonstrate educational value Educational experiences need some way of measuring a student's ability to meet learning objectives and displaying that progress to educators. Measuring performance can happen automatically through tracked metrics, or through designed assessments. ### Capture data in experience Developers can use experiences to collect data that offers insight on how users are meeting learning objectives. Below is a high level overview of the types of information that can be captured within an experience. Please note this is not exhaustive. More detail can be found in the book [Stealth Assessment](https://mitpress.mit.edu/9780262518819/stealth-assessment/) by Valerie Shute and Matthew Ventura. | Assessment type | Implementation | | --- | --- | | Completion | Whenever a user completes or fails a specific event tied to a learning objective, document that and report it to an educator. This can utilize the Event system in Roblox. | | Time stamps | Provide time indications when a user completes a specific task or how much time they've been in experience on task. | | Performance | Any in-game metrics related to a student's performance in-game. This varies by experience but may be in the form of points or medals that signify student progress. | | Trajectory data | For non-linear learning tasks, these data indicate the order a user takes among a set of specific tasks. For instance, this can be the order of quests completed in a non-linear role playing experience. | | GUI and keystroke interactions | This includes interactions with the graphical user interface or keystrokes. For instance, how often a user opens the "Help" dialog box, or interacts with an in-game character. | > **Warning:** For any developers capturing data, ensure that your tools comply with Roblox [privacy and data regulations](https://en.help.roblox.com/hc/en-us/articles/115004630823-Roblox-Privacy-and-Cookie-Policy-). ### Assess student progress Assessments can be constructed to measure a user's progress towards learning objectives. These can be built into the experience, or be external to the experience, such as a physical handout completed by the student. Note that this list is not exhaustive and more detail along with pros and cons can be found on this external resource on [Exam Questions: Types, Characteristics, and Suggestions](https://uwaterloo.ca/centre-for-teaching-excellence/teaching-resources/teaching-tips/developing-assignments/exams/questions-types-characteristics-suggestions) by University of Waterloo. | Type | Implementation | | --- | --- | | **Multiple choice** | Users complete a quiz or test designed to capture mastery of knowledge learned in the experience. Guidance on writing strong questions can be found in [Writing Good Multiple Choice Test Questions](https://cft.vanderbilt.edu/guides-sub-pages/writing-good-multiple-choice-test-questions/) by Vanderbilt. Note: Multiple choice quizzes are cautioned against for demonstrating understanding of content due to their simplicity. | | **Short response** | Have students write in response to a prompt. That written work is then graded by an educator. For instance, a user builds a robot in an experience. A prompt then has the student writing about their process in designing the robot to demonstrate their engineering process. | | **Fill in the blank** | Users are asked about what they learned and are prompted to fill in blanks with key words or concepts. | ## Additional resources Below are some resources providing more insight into the topics which were covered throughout this article. ### Design checklist While working on the game design document, reference this checklist. This serves as a summary of key points in this article. | Topic | Self-reflection questions | | --- | --- | | Determine learning and gameplay foundation | | | Understand the needs of educators and students using the experience | | | Determine the context where the experience will be played. | | | Identify how the experience is measuring learning. | | ### Further reading Below are resources for learning more about learning standards, creating lesson plans, and organizing classroom activities. Note that all below resources are external, non-roblox sites, and provided as a courtesy only. #### Find learning standards Designers can reference these educational institutions to find standards that align to their experience's target age and topic. - [Next Generation Science Standards](https://www.nextgenscience.org/) - Respected standards for science education that are commonly adopted within United States classrooms. - [Common Core](https://learning.ccsso.org/common-core-state-standards-initiative) - English and Math standards, often adjusted on a state-by-state (United States) basis. - [College Board](https://apstudents.collegeboard.org/course-index-page) - Series of exams with standards for high school to college coursework. > **Info:** Designers can also search using key terms such as "high school gravity learning standards". If you do find a standard, ensure that it's verified by an institution such as Common Core. #### Design supplemental resources The following articles are in-depth resources on how to design supplements for educators, such as lesson plans. - [Writing Learning Objectives](https://cteresources.bc.edu/documentation/learning-objectives/) - Outlines how to write effective learning objectives. - [Writing Lesson Plans](https://cte.smu.edu.sg/approach-teaching/integrated-design/lesson-planning) - For designers interested in writing supplemental lesson plans for educators. - [Lesson Plan Template](https://uei.uchicago.edu/learn/LessonPlan_Rubric_CGW.pdf) - An example of how to present lesson plans. Use this as inspiration to design your own, with your organization's branding - [Using Discussion Questions](https://citl.indiana.edu/teaching-resources/teaching-strategies/discussions/index.html) - Outlines how to use utilize discussion questions in a lesson plan. - [Designing Discussion Questions](https://teaching.pitt.edu/wp-content/uploads/2020/07/FLEX-Designing-Discussion-Questions-Using-Blooms-Taxonomy-Examples.pdf) - Covers how to write effective discussion questions. --- title: "Classroom collection" url: /docs/en-us/education/edu-templates/classroom-collection last_updated: 2026-06-29T19:34:00Z description: "Classroom collection" --- # Classroom collection [Simulation

Mansion of Wonder

CONTENT-AREAAGE-GRADE](#placeholder-link) [Simulation

Mansion of Wonder

CONTENT-AREAAGE-GRADE](#placeholder-link) [Simulation

Mansion of Wonder

CONTENT-AREAAGE-GRADE](#placeholder-link) [Simulation

Mansion of Wonder

CONTENT-AREAAGE-GRADE](#placeholder-link) ## Content badges STEMSocial studiesLanguage artsArtsVocational ## Age badges Early childhoodPrimarySecondary
--- title: "Lesson title" url: /docs/en-us/education/edu-templates/edu-lesson-plan-template last_updated: 2026-06-29T19:34:00Z description: "Lesson plan template" --- # Lesson title Lesson Description: Get students creating and coding their first game in Roblox. Learn how to build an obstacle course and code color changing blocks. | #### Lesson objectives | #### Skills and concepts | | --- | --- | | | | ## Get ready | #### Materials | #### Prep | | --- | --- | | | | ## Lesson plan Common lesson plan elements include: - Introduction and Wrap-up (for each lesson) - Guided Tutorial, Guided Work, Independent Work, Activity | Duration | Activity | Description | | --- | --- | --- | | 5 min | Introduction | Introduce the project and lesson structure. | | 35 min | Guided Tutorial: Activity Name | Teach the basics of using Roblox Studio while building a simple obstacle course. | | 5 min | Wrap-up | Recap the lesson and concepts learned. | ### Introduction ### Lesson element ### Lesson element ### Wrap-up ## Appendix Note: For the appendix, pick and choose anything that's needed. Not every component needs to be included. ### Troubleshooting tips - Example troubleshooting tip ### Classroom management ### Customize the lesson ### Miscellaneous resources --- title: "Edu styles" url: /docs/en-us/education/edu-templates/edu-styles last_updated: 2026-06-29T19:34:00Z description: "Edu styles" --- # Edu styles ## Alerts - `error` - Critical warning, use very sparingly. - `info` - Teacher related comments - `success` - Additional information not crucial to a lesson - `warning` - Warning. Use this as a heads up. ## Learning objective table To be used at the start of an individual module. | #### Learning objectives | Students will be able to: | | --- | --- | --- title: "Series title" url: /docs/en-us/education/edu-templates/series-template last_updated: 2026-06-29T19:34:00Z description: "Series title" --- # Series title | #### Learning objectives | Students will be able to: | | --- | --- | | #### Prerequisites | Students should: | Description text. Briefly summarize the project, learning, and more. **ISTE Standards**: Innovative Designer 4a, 4c, 4d, Creative Communicator 6b, 6d. ## Series sample ## Series contents - Link to Series Module - Link to Series Module - Link to Series Module --- title: "Intro to Roblox" url: /docs/en-us/education/educator-onboarding/1-intro-to-roblox last_updated: 2026-06-29T19:34:00Z description: "Written for educators, learn what Roblox is as a platform, how Roblox is educational, and safety features for students." --- # Intro to Roblox This module serves as an introduction to how Roblox supports education. You'll learn what Roblox is as a platform, and how it can be used to promote creativity and 21st century skills. By the end of this article, you will: - Understand how Roblox is a platform that hosts games and virtual learning experiences. - Know what it means to run Roblox, such as costs and hardware. - Be able to articulate the unique advantages compared to other digital learning tools. ## What is Roblox? You're probably here because you've heard about Roblox, and you have some questions. Such as, what is Roblox and how does it fit into my classroom? ### Overview To start, Roblox is not a single game. It's a platform that hosts millions of user-generated **experiences**. Some examples include historical roleplaying games, or virtual labs to simulate physics experiments. Experiences are designed by Roblox users, who may be individual students or professional studios. Three examples of experiences are included below. _[Plane Crazy](https://www.roblox.com/games/166986752/)_ _[Ocean Conservation Experience](https://www.roblox.com/games/6708164649/)_ _[The Normal Model](https://www.roblox.com/games/77019455/)_ In addition to playing experiences, students can create their own using **Roblox Studio**. Using project-based learning, students can collaborate with peers to design a historically accurate city, or learn computer science principles as they create their own 3D world. Whether a class is playing or designing experiences, we have free lesson plans to support you. Our lesson plans are flexible, with many options for different ages, subjects, or time periods, such as a 45 minute workshop or a semester. Additionally, teaching with Roblox is accessible to all educators. You don't need prior experience in coding or game development to design or play experiences on Roblox. ### Costs On Roblox, it's **free** to play, create, and publish experiences. You don't need to pay licensing fees to use Roblox or to publish a student's project. There are a few optional features which are available for small fees paid in **Robux**, the currency used in all Roblox experiences. The most common expense for educators is a private server for playing online, which often averages about $1 (USD) per month. More information on private servers is covered later in the training. > **Info:****Purchasing Robux** Robux can be purchased on Roblox.com or using physical gift cards found in grocery stores. Do not trust any other sources. ### Software Roblox is actually two software packages with different hardware requirements. | Software | Use case | Hardware requirements | | --- | --- | --- | | **Roblox Client** | Visit experiences as a classroom | PC, Mac, Chromebook, mobile, and tablet devices. | | **Roblox Studio** | Create or code your own experiences | PC or Mac, with recommended 2-button mouse with a scroll-wheel | #### Common questions about software **Roblox client plays all experiences** Installing the Roblox client allows you to play any experience on Roblox. Experiences do not have their own installations. **More detail on hardware specifications** For more details such as supported graphics cards or unique mobile device questions, see the [Hardware Requirements](https://en.help.roblox.com/hc/en-us/articles/203312800-Computer-Hardware-Operating-System-Requirements) page. **Software Updates** Both the Roblox client and Studio require consistent internet access. Regular updates occur on a weekly basis. When the software does update, changes are often minor and do not disrupt curriculum. ### Recommended ages Roblox offers educational opportunities and challenges **for all ages**. - For **students under 10**, there are virtual learning experiences, such as games to improve math skills or roleplay as dinosaurs. - **Older students** can find learning experiences for content such as physics or history. Additionally, they can work in Roblox Studio for project-based learning. ## Use Roblox to learn Students can primarily use Roblox to play or create. One method is using **virtual learning experiences** where students play on Roblox. They can go on virtual field trips, explore natural settings, and interact with simulations of real-world phenomena. Additionally, Roblox can be used for **code and creation** with Roblox Studio. In Studio, students can design virtual environments or learn computer science with Luau, a text-based coding language similar to Python and Lua. ### Advantages of Roblox **Roblox is free, accessible, and trusted by educators** All the software and content for Roblox is free, whether you're a small coding camp or a large school district. **Engage students and practice skills** Widely popular among K-12 students, Roblox has unique opportunities for boosting classroom engagement. - Engage students in subject-matter content such as history, or experience real-world concepts such as physics and the natural sciences. - Support creativity by allowing students to design their own projects, or further skill building as they improve critical thinking abilities. - Promote teamwork and communication as students interact in a multiplayer game world, or collaborate simultaneously on projects in Roblox Studio. **We prioritize safety and privacy** Roblox works with parents, educators, and digital safety experts to promote a welcoming environment for everyone. Our software includes features such as chat filters, private servers, and extensive content moderation. To learn more, and see information on compliance such as COPPA, see our [Safety](https://corp.roblox.com/safety-civility-resources/) page. --- Now that you've learned a bit about what Roblox is, in the next section, you'll visit an online Roblox experience. It'll be an interactive opportunity to learn more about Roblox, and become comfortable navigating a virtual world. --- title: "Play on Roblox" url: /docs/en-us/education/educator-onboarding/2-playing-on-roblox last_updated: 2026-06-29T19:34:00Z description: "For educators, learn how to play Roblox. This covers creating an account, learning controls, and common terms found on Roblox." --- # Play on Roblox Included in this training is creating an account and learning how to navigate within Roblox experiences. For the second part, there is the option of joining a training game specially created to familiarize you with Roblox mechanics. By the end of this article, you will: - Create and login into a Roblox account. - Understand the layout of the Roblox platform and how to launch experiences. - Learn basic skills in the Roblox client, such as avatar controls and camera manipulation. ## Create your account To play and create on Roblox, you need a free account. 1. Create an account by signing up on the [Roblox Home Page](https://www.roblox.com). Follow the prompts to create your account. 2. To create an account, you may be asked to complete a **CAPTCHA**. These are timed challenges used to discourage people creating fake accounts. Challenges vary, but often ask you to find a specific object in an image. > **Info:****CAPTCHAs and students** Note that your students may have to accomplish CAPTCHA challenges when logging into accounts. Because of this, we encourage setting aside time for account log-ins at the start of class. ## The Roblox home page Once you've logged in, you'll see the Roblox home page. This includes recommended games and navigation options. Additionally, you'll see your username and a headshot of your **avatar**, your virtual representation in Roblox. > **Info:****Customize avatars** Many users enjoy customizing their avatar, changing it to be a realistic representation of themselves, or even becoming a fantasy character. Avatars are customized using avatar items in the [Marketplace](https://en.help.roblox.com/hc/en-us/articles/203313300-The-Avatar-Shop). Before moving onto the training experience, it's recommended you add an email to your account. This adds extra security to your account and ensures you don't get locked out if a password is forgotten. 1. On the Roblox home page, in the top right, click the gear icon and select **Settings**. 2. On your account screen, click **Add Email**. Type in your school or organization email. Follow prompts as needed to verify the email. > **Info:** While Roblox does its best to ensure accounts are safe, you're one of the best defenses against bad actors. Learn additional tips in [Account Security](https://en.help.roblox.com/hc/en-us/articles/203313380-Account-Security-Theft-Keeping-your-Account-Safe-). ## Learn Roblox client With an account created, you'll now launch your first Roblox experience. The **What is Roblox? - Training Experience** is designed by the Roblox team to introduce educators how to play Roblox and feel comfortable on the platform. Alternatively, you can view the [Roblox Platform Quick Start Guide](../../assets/education/legacy/Handout_-_Roblox_Client_Quick_Start_Guide.pdf), a handout summarizing the interface and controls instead of joining the experience. ### Experience Pages All Roblox experiences have their own page. This page includes a description and button used to open that specific experience in the Roblox client. A sample page is below. ### Launch the experience 1. To start, go to the [Learn to Play Roblox](https://www.roblox.com/games/16279707695/Learn-to-Play-Roblox-Tutorial) experience. 2. On that page, click the **Play** button. If you already have Roblox installed, it'll open the experience. If not, permit Roblox to be installed onto your device. 3. With the Roblox client open, go through the experience as instructed. This should take about 5–10 minutes. Come back to your web browser when you see a sign reading "Find new games!"_Example in-experience__Finish screen_ ### Desktop controls While all controls are introduced in the experience, here's a quick reference. | **Move Forward** | `W` or `↑` | | --- | --- | | **Move Backward** | `S` or `↓` | | **Move Left** | `A` | | **Move Right** | `D` | | **Jump** | `Space` | ### Mobile controls Roblox works easily on any mobile device. Whenever playing Roblox, we recommend holding the device with thumbs on the screen like below. | **Move** | Tap and drag in the lower-left area of the screen | | --- | --- | | **Jump** | Press the jump button in the lower-right area of the screen | | **Rotate Camera** | Tap and drag in the middle of the screen | --- title: "Set up a classroom" url: /docs/en-us/education/educator-onboarding/3-setting-up-a-roblox-classroom last_updated: 2026-06-29T19:34:00Z description: "For teachers and educators, learn how to set up Roblox in the classroom. This article covers getting student accounts and finding lessons." --- # Set up a classroom Setting up a Roblox classroom is a quick process that includes tasks like getting accounts and software ready. Note, while this article overviews these topics, an in-depth, guided checklist is at the end of the course. By the end of this article, you will: - Know the steps needed to create accounts and install Roblox on desired devices. - Understand how to find and share content relevant to learning objectives. ## Accounts and software All Roblox accounts and software are **free**. ### Get accounts Account registration is through a form that asks for basic information like a birthday. Names are not needed for sign-up. While emails aren't requested at signup, it's recommended to add one since forgotten passwords can only be recovered through email. Each account grants that user full access to: - Play experiences with the **Roblox client**. - Code and create experiences with **Roblox Studio**. > **Info:****Accounts for students under 13** If students list their age under 13, that account will have some restrictions. For instance, that student can only see curated games deemed appropriate by Roblox. For more information, see [Account Restrictions](https://en.help.roblox.com/hc/en-us/articles/360000375686). ### Accounts in the class Every student and educator needs **their own** account. Compared to other educational tools, accounts are individual and not managed by an institution or person. Since Roblox is used for both home and school, we believe this system respects the privacy of our students. When starting a Roblox class, keep in mind that accounts cannot be created in a batch. It's recommended students create accounts outside class. Also, plan to manage passwords, such as by using password manager software. > **Info:****Managing passwords for younger students** We understand that password management can be a challenge for younger students. To help, an educator can use software like [LastPass](https://www.lastpass.com/plans-and-pricing) (offered free) to keep a central database of student account logins and passwords. Students can provide account information to you, where you store them to use if a student forgets. ## Install software Roblox is two software packages (client and Studio), each installed separately. Software can be installed in two ways: - **Individual installations per computer:** Install Roblox by downloading the software from a web browser on each device. Ideal for small classrooms, workshops, or small summer camps. - **Batch Installers:** Create a packaged installer that can be deployed on multiple computers using a USB drive or image. Ideal for schools or larger educational institutions. For more information, see [How to Install and Play Roblox](https://en.help.roblox.com/hc/en-us/articles/204473560-How-to-Install-and-Play-Roblox-Using-Browser). These instructions will also be summarized at the end of the course. > **Info:** **File storage** > > Storing files is only relevant for students using Roblox Studio to develop experiences. In Roblox Studio, files can be saved locally (using the .RBXL format), or stored online on an account. Projects saved to a student's account can be accessed from any computer. There are no limitations in the number of projects or file size that can be stored online. ## Find educational content With accounts and software ready, the next step is to find relevant content. Whether it's playing experiences or designing projects, there are many options that align to educator needs. ### Virtual learning experiences The Roblox platform hosts millions of pre-made experiences created by the community. In the Learn and Explore sort, curated by educators at Roblox, students can find historical roleplays, physics simulations, or creative building games. ### Code and creation For educators interested in creation, students can build and code using Roblox Studio. Our content is developed for a variety of ages and subjects, such as digital citizenship or teaching computer science. ## Share content with students ### Virtual learning experiences Once you have an experience, students can access it using a unique **hyperlink**. Links are sharable through email or a learning management system (e.g. Google Classroom). Additionally, when playing an experience, we recommend using a **private server**. Normally, playing Roblox means interacting with players across the world. Private servers are spaces that only include those you've permitted to join, like students. Each private server has its own shareable hyperlink that can be provided to students. > **Info:** Some private servers may have a **monthly** fee paid in Robux. That price is set by the developer of that experience and not Roblox. That fee often averages about 100 Robux ($1 USD) per month. Most educational experiences, though, include one free private server per user. ### Code and creation Students using Roblox Studio for projects are instructed by educators. Most of the assets, such as templates and 3D models, are already built into Studio. For teachers with specific needs, such as if you have a premade template to share, files can be shared locally to students. ## Communicate about Roblox Before beginning a Roblox class, you may have questions from different people such as parents or administrators. Resources and printable brochures are included below. ### Parents Resources and handouts are available about the safety and educational benefits of Roblox. ##### For parents Common answers for parents, like what is Roblox and safety features. [View](https://corp.roblox.com/parents/) ##### Trust and safety Print-ready PDF that explains Roblox Education. [View](../../assets/education/legacy/Safety_education_brochure.pdf) ### IT departments There may be considerations unique to your school, such as port-forwarding or domain-filtering. For a comprehensive guide, we have our [networking guide](https://en.help.roblox.com/hc/en-us/articles/115005744663). --- title: "Run a classroom" url: /docs/en-us/education/educator-onboarding/4-running-classrooms last_updated: 2026-06-29T19:34:00Z description: "Covers running a Roblox class with topics such as class management, collaboration options, and student evaluations." --- # Run a classroom This module covers different aspects of running a class, like assessments and class management. Note that some parts may be more applicable based on your teaching situation. By the end of this article, you will: - Understand ways to assess or evaluate student progress. - Recognize optional learning tools, such as Live Collaboration or Private Servers. - Know where to find resources on digital civility and classroom management. ## Assessments and evaluation As an educator, there are different ways to gauge student progress and understanding. Options are included for those playing experiences and designing them. ### Assess virtual learning experiences Roblox has built-in tools that allow anyone to screenshots or record video. These media artifacts can then be integrated into existing assignments. For example, a student may make recordings of a historical shrine as evidence in an Asian history assignment. To learn about these options, see [Taking a Screenshot](https://en.help.roblox.com/hc/en-us/articles/203314160-How-Do-I-Take-a-Screenshot-) or [Recording Videos](https://en.help.roblox.com/hc/en-us/articles/203314190). Additionally, some virtual learning experiences may have built-in assessments, such as quizzes. Some lesson plans on Roblox Education also have reflection questions, handouts, or quizzes. _Quiz in [Lua Learning](https://www.roblox.com/games/1334669864/)_ ### Assess code and creation Projects created in Roblox Studio can be shared as local files or uploaded to the cloud. Uploading to the cloud allows projects to be shared through a hyperlink. An educator can then take that local file, or link, to evaluate a project. To view an example of how a Roblox Studio project can be evaluated, see the [Project-Based Rubric](../../assets/education/legacy/Handout_-_Project_Rubric.pdf) handout. ### Quizzes and rubrics Examples of traditional quizzes and rubrics are included from sample lessons below. Both are available to download and customize at the end of this course. | #### Traditional knowledge | **Check for knowledge.**_This example covers our Coding 1-2 lesson plans and evaluates basic computer science knowledge._ | | --- | --- | | #### Project-based rubric | **Evaluate skills or a project.**_ This example covers the [Story Games](/docs/en-us/education/build-it-play-it-story-games/landing.md) coding project and evaluates a student's coding project and classroom ethic._ | ## Classroom tools To make learning and teaching easier, there are optional features in Roblox. For example, students can build collaboratively in Roblox Studio. A quick summary of each tool and use cases are below. | **[Roblox chat](#roblox-chat)** | Roblox offers multiple means of communicating on the platform, like experience-messaging and private messages. | | --- | --- | | **[Live collaboration](#live-collaboration)** | A tool in Roblox Studio that allows people to invite friends to collaborate on a project simultaneously with them. | | **[Private servers](#private-servers)** | A closed server for experiences that allows you to control who can join. | | **[Groups](#groups)** | Social networks with member lists and post boards. Also allows for collaboration opportunities. | ### Roblox chat Most Roblox experiences offer basic chat features. These include: - Being able to chat among all players in an experience server. - Send private messages to a specific player. - Mute/unmute specific players. - Create groups for chat between specific players. ### Live collaboration **Live Collaboration** allows the owner of a project to invite friends to join them in Roblox Studio to **collaboratively** build worlds or edit scripts together in real-time. Multiple students can work together to design an environment or solve a scripting exercise. This tool allows for shared projects or group-work in Studio. ### Private servers Normally, whenever a player enters a Roblox experience, they're in a public server. If desired, educators can create private servers which allow for control over who may join. Invites to play can be sent with hyperlinks which can be shared through email or a learning management system (like Google Classroom). ### Groups Groups are mini-communities that can be formed on the Roblox platform. Each group includes features like a member list with ranks and a "wall" to post announcements. For educators, groups can be used in classroom management posting links to experiences. Additionally, students can form their own groups as a way of collaboratively working on projects in Roblox Studio. ## Foster a positive culture Over the years, we've developed teacher-tested resources to help foster supportive learning environments. Practices such as digital civility better prepare students for the online world. ### Teach digital civility Digital civility is useful for all Roblox students. Our materials teach skills such as how to identify false or suspicious information, and how to treat others with kindness and respect. #### Intro to digital safety Learn real-life skills playing a Roblox experience. #### Intro to digital civility Become a digital citizen while learning Roblox Studio skills. ### Classroom management The resources below have been built with educators on how to create supportive learning environments. #### Classroom best practices Generalized tips for any Roblox classroom. #### Collaboration best practices Guidelines for collaborative study and play. --- title: "Next steps" url: /docs/en-us/education/educator-onboarding/5-next-steps last_updated: 2026-06-29T19:34:00Z description: "A page of resources to get started with Roblox in the classroom for teachers. Includes Roblox handouts, presentations, and where to find educational experiences." --- # Next steps ![image of classroom in Roblox](../../assets/education/educator-onboarding/hero-banner-classroom.jpg) **Congratulations**! You've completed the Roblox Educator training and are now ready to inspire a new generation through play. We've compiled a list of resources to help you on your journey. ## Classroom kit To help get your classroom started, the Classroom Kit that includes resources like cheat sheets and customizable templates. The kit can be downloaded as a .ZIP file, or the files can be downloaded individually below. [Download Classroom Kit](../../assets/education/legacy/Roblox_Classroom_Kit.zip) ## Educator resources The following resources include different handouts, templates, and guides to better facilitate a Roblox classroom. | [Classroom setup guide](../../assets/education/legacy/Handout_-_Getting_Ready.pdf) | A step-by-step guide to setting up a classroom such as where to get software and accounts. | | --- | --- | | [Roblox glossary](../../assets/education/legacy/Handout_-_Roblox_Glossary.pdf) | Lists common words unique to Roblox, such as obby or Robux. | | [Trust and safety brochure](../../assets/education/legacy/Safety_education_brochure.pdf) | Outlines the educational benefits of Roblox and safety features, such as chat protections. | | [Assessment handouts (.zip)](../../assets/education/legacy/Roblox_Assessment_Handouts.zip) | Assessment with one knowledge-focused quiz and one project-based rubric. | | [Roblox client cheatsheet](../../assets/education/legacy/Handout_-_Roblox_Client_Quick_Start_Guide.pdf) | In-depth reference for the Roblox Client controls, interface, and common concepts in Roblox games. | ## Student resources The following resources are handouts specifically intended to be distributed to students. | [Account creation guide](/docs/en-us/assets/education/legacy/Roblox_Account_Creation_(Student_Handout.md).pdf) | Intended for students, lists the steps needed to create accounts and includes information on safety. | | --- | --- | | [Roblox Studio cheatsheet](../../assets/education/legacy/RobloxStudio-Cheatsheet.pdf) | Student friendly reference for using Roblox Studio, the software used to make Roblox experiences. | ## Explore experiences We've compiled some of our favorite experiences to showcase the best that Roblox has to offer. To see more, browse the [Learn and Explore](https://www.roblox.com/discover#/sortName/Curated_67) Sort. #### Ocean Conservation Experience A museum built in Roblox that educators people about ocean conservations. #### Lua Learning Learn how to code in Luau, the language used in all Roblox projects. The experience is named Lua for historical reasons, as Roblox used Lua before developing it into Luau. ## Explore lesson plans Depending on your needs, Roblox Studio has a wide variety of premade lesson plans, great for different ages and subjects. ### Single session lessons Try out Roblox with these stand alone lessons. Learn something new in less than an hour. #### Coding lesson Use strings and variables to create your own story. #### Animation lesson Create your own animation using a poseable figure. ### Ages 10 and up Lessons below focus on project-based learning with Constructionist principles. Students are able to explore tools in a structured way and produce unique projects they can share with others. #### Create an obstacle course Practice design thinking by making an obstacle course. #### Design a city Learn building to make a city for a multiplayer experience. ### Ages 13 and up For those with more computer experience, explore game design and computer science with these lessons. #### Roblox Developer 101 Build a game all while learning computer science and design. #### Coding fundamentals Explore basics such as variables and if/then statements.. ## Stay connected We have different options for staying connected. #### Education mailing list Updates on new programs, lessons, and news. #### Contact us Still have questions? Looking for guidance? Connect with our team. --- title: "Educator onboarding" url: /docs/en-us/education/educator-onboarding/landing last_updated: 2026-06-29T19:34:00Z description: "Learn how to get started teaching with Roblox in the classroom. Get your students to create accounts, learn lessons to use, and understand how to play." --- # Educator onboarding This course introduces you to Roblox, preparing you to feel confident in using Roblox as an educational tool. Training, which includes setting up a Roblox account and software on your device, will take 30 to 45 minutes. ### Learning objectives - Understand how to implement Roblox software and accounts in a classroom. - Identify where to find resources, such as standards-aligned learning materials and handouts. - Practice playing a Roblox experience to learn basic controls and common Roblox concepts. > **Info:** In a hurry? This document covers all the steps needed to start a classroom, such as account and software setup. It also includes a collection of useful resources like links to technical support. [Download Quick Start Guide](../../assets/education/educator-onboarding/Handout_-_Getting_Ready.pdf) ### Series modules | Article | Description | | --- | --- | | **[Get started](/docs/en-us/education/educator-onboarding/1-intro-to-roblox.md)** | Learn about the Roblox platform. | | **[Play on Roblox](/docs/en-us/education/educator-onboarding/2-playing-on-roblox.md)** | Create your account and play an experience. | | **[Set up classrooms](/docs/en-us/education/educator-onboarding/3-setting-up-a-roblox-classroom.md)** | Covers account and software setup. | | **[Run classrooms](/docs/en-us/education/educator-onboarding/4-running-classrooms.md)** | Tips and resources on running a successful classroom. | | **[Next steps](/docs/en-us/education/educator-onboarding/5-next-steps.md)** | A collection of useful resources, handouts, and guides. | --- title: "Build It, Play It challenges" url: /docs/en-us/education/landing-pages/build-it-play-it last_updated: 2026-06-29T19:34:00Z description: "Get started learning Roblox Studio with these quick lessons. Build games and experiences." --- # Build It, Play It challenges These challenges are a series of workshops that serve as **first introductions** to beginners interested in Roblox Studio. Some challenges may focus on coding, while others include animation or building. Going through one challenge will often take less than an hour, includes full step-by-step tutorials and a high quality game experience to fully customize. Each series can be completed self-directed, while lesson plans are provided for classrooms for students ages 10 and up. ## Mansion of Wonder Using Roblox Studio, you'll learn to create **special effects**, whether it's a magical blast in an arcade experience, or a beam of arrows that guides new players around a world. ## Island of Move Using Roblox Studio, learn how **animation** and create your own simulator game where people move to gain points. The way players move will be up to you, whether it's swimming through the air or dancing along the track. ## Galactic Speedway Learn **3D modeling** while building a spaceship to race in a multiplayer experience with friends. ## Create and Destroy Build an entire city and then with your friends, destroy it as rampaging monsters. Learn how to become a **level designer** in Roblox. ## Story Games **Code** a game where players can create their own story. Learn how scripts work in Roblox and get a first introduction to computer science. --- title: "Animate in Roblox" url: /docs/en-us/education/lesson-plans/animate-in-roblox-lesson last_updated: 2026-06-29T19:34:00Z description: "Learn about animation and study how the human body moves." --- # Animate in Roblox **Lesson Description**: Learn about animation and study how the human body moves. Pose a Roblox character to create an animation, such as swimming or skipping, for use in a multiplayer experience. **ISTE Standards**: Innovative Designer 4a, 4c, 4d, Creative Communicator 6b, 6d | **Lesson objectives** | | | --- | --- | | **Skills and concepts** | | | **Prep** | | | **Materials** | | ## Overview | Duration | Activity | Description | | --- | --- | --- | | 5 min | Introduction | Introduce the project and lesson structure. | | 5 min | Guided Tutorial: Plan an Animation | Brainstorm and plan an animation idea. | | 20 min | Guided Tutorial: Studio and Animation Basics | Learn how to use Roblox Studio to create a simple animation. | | 10 min | Independent Work: Improve Animations | Customize the animation or catch up. | | 10 min | Guided Tutorial: Add Animations | Export animations and test projects. | | 5 min | Wrap-up | Reflect on project and recap concepts learned. | ## Lesson plan ### Introduction 1. Grab attention by showing Presentation: Slide 2. - Going to pose figures to create an animation of their choice. - Once finished, that animation will be added into a Roblox experience where it'll be the default way users walk around. 2. Introduce and define animations in Presentation: Slide 3. ### Guided tutorial - Plan an animation 1. Lead students through Presentation: Slide 4 - 5. - As a class, have students brainstorm actions (ex: real life actions/sports like swimming or dancing, or fantastical like flying. - Give students 2-4 minutes to decide on an animation and collect reference material (either images or have them act out motions in real life). 2. Recap by letting 2-3 students share their planned animation. - Optional - Let students physically perform their planned animation. ### Guided tutorial - Studio and animation basics 1. Set expectations that you'll guide students through a tutorial to make their animation. Even if they're not finished, they'll have more time in the independent work time. 2. Lead students through [Get started](/docs/en-us/tutorials/curriculums/animator/get-started.md) through [Work with the Animation Editor](/docs/en-us/tutorials/curriculums/animator/work-with-the-animation-editor.md). - Slides are provided to help you throughout the tutorial. ### Independent work - Improve animations 1. Have students catch up on their projects. If finished, challenge them to improve their animations using tips in Presentation: Slide 10. ### Guided tutorial - Add animations 1. Lead students through [Play your animation](/docs/en-us/tutorials/curriculums/animator/play-your-animation.md) through [Test and save](/docs/en-us/tutorials/curriculums/animator/test-and-save.md). - At **Playtest your animation**, give students at most 2 minutes to play-test. 2. Have students playtest their experience to ensure they see their animation. ### Wrap-up 1. Recap what students have created and vocabulary: **animation** and **keypose**. 2. Optional - To showcase work, students can pair up and watch another's animation. ## Appendix ### Troubleshooting tips **General troubleshooting** - Introduce students to "Ask 3 Before Me": a student needs to ask three peers before asking the instructor for help. **General animating tips** - Encourage students to frequently use their body or reference pictures when animating. Having a visual reference point can help them understand which body part to rotate. Students can use image search engines (with Safe Mode on) for reference pictures. - Remind students to frequently view their animation in Roblox Studio from different angles. An animation may look correct from one angle but not another. ### Classroom management **Using motion in the classroom** This lesson uses motion in two ways: students can play their experiences using mobile devices with accelerometers. Additionally, poses can be physically done in real life as reference. If you plan to include either feature in the classroom, set strict guidelines with students. For instance, ensure they are in a safe place where they can make motion where they're not at risk to themselves or others. **Timing notes** To ensure students finish their project, give them time limits for each pose, such as 2-4 minutes per pose. They can always improve poses. ### Customize the lesson **Simplify the lesson** Have students follow along to build their own version of a specific animation (such as swimming in the tutorial). Students can follow your steps, then personalize in the independent work time. **Provide feedback for projects** Students can provide each other feedback with the optional [Animation Feedback Handout](../../assets/education/lesson-plans/animateInRoblox-evaluation.pdf). If using this handout, before students start working, have them create a goal for their animation. After completing their work, pair up students to see each other's animation and respond to the prompts in the handout. **Integratr with science curriculum** - This lesson can be customized to go along with a science lesson on the muscle or skeletal systems. Encourage students to perform their animations in real-life and explain what specific bones or muscles are being used. - Optionally, you can include an educational video, such as [SciShow Kids - How Do Our Bodies Move](https://www.youtube.com/watch?v=j918PoWWaB0), in the Introduction. **Integrate with fine arts curriculum** - Animation can be used as an opportunity for students to study motion and drawing. For instance, in the planning phase, students can draw stick figures to represent each pose of their animation. - Alternatively, you can expand this lesson by having students create a flipbook animation first of their pose, before making it in Roblox Studio. ### Miscellaneous resources **Course description:** Create your very own Roblox experience and learn how to animate! This challenge is all about movement, both in-game and in real life. We'll show you how to make a game where users race their avatars around a track and gain levels by moving in real life with their mobile device. How do their avatars move? That's up to you: you'll get to build the animations from scratch. Will they fly? Run backward? Turn cartwheels? The possibilities are as limitless as your imagination. In this course, your student will: - Create their own animation by posing a Roblox figure. - Test, evaluate, and redesign to create a fun, polished game experience. - Take home a complete experience that can be played and shared online --- title: "Create Your Own Game on Roblox - 1 Hour Activity Plan" url: /docs/en-us/education/lesson-plans/build-an-obby-lesson last_updated: 2026-06-29T19:34:00Z description: "A classroom activity plan demonstrating how to create an obby using Roblox Studio and AI assistance." --- # Create Your Own Game on Roblox - 1 Hour Activity Plan **Lesson Description:** 1 hour classroom activity plan demonstrating how to create an obby game on Roblox. A perfect starting point for anyone. No experience with coding, game development, or AI needed. Get students familiar with Roblox and Roblox Studio by having them create a classic platformer style game, also called an "obby" on the Roblox platform. Students will practice foundational prompting skills in conjunction with improving their own computational logic. **ISTE standards:** Innovative Designer 4a, 4c, 4d, Creative Communicator 6b, 6d | Lesson objectives | Skills and concepts | | --- | --- | | **Skills and concepts** | | | **Prep** | | | **Materials** | | ## Lesson plan | Duration | Activity | Description | | --- | --- | --- | | 3 minutes | Introduction | Introduce the project and lesson structure. | | 5 - 10 minutes | Logging In | Help students log into their accounts. | | 35 minutes | Guided Tutorial | Introduce the basics of Roblox Studio, computational thinking, and AI prompting by building the first few pieces of an obstacle course. | | 5 minutes | Wrap-up | Reflect on project and recap concepts learned. | ### Introduction (3 - 5 minutes) 1. Ask students: "Who can tell me what the Roblox Platform is?" Possible answers: - Roblox is the ultimate virtual universe that lets you create, share experiences with friends, and be anything you can imagine. - Roblox is an online gaming platform and creation system where users can create, share, and play games and virtual experiences created by other users. 2. Ask students: "Who can describe an obby to me?" Possible answer: - "Obby" is short for "obstacle course." Obbies are jumping puzzles where you must figure out how to jump from Bridge to Bridge without falling. They're one of the most popular types of games on Roblox 3. Ask students: "Has anyone heard of Roblox Studio? What is it?" Possible Answer: - Roblox Studio is the creation tool used by all experiences on the Bridge. It is available for free so everyone can create and share new Roblox experiences. 4. Introduce Lesson: - Explain students will be coding and designing their own obby (obstacle course) that others can play using Roblox Studio. - This lesson will show you an example of an obby. For students who have never used Roblox before, this is also a chance to learn how the player controls work. ### Logging in and opening Roblox Studio (5 - 10 minutes) Logging In and Opening Roblox Studio (5 - 10 minutes) To use Roblox Studio, you will need a free Roblox account. See [Learn About Studio](/docs/en-us/tutorials/curriculums/studio.md). While students are logging in, here are some potential discussion topics: - **Strong passwords:** Emphasize creating unique, complex passwords for Roblox that are different from passwords used on other sites. Discuss why reusing passwords is risky. - **Recognizing scams:** Educate students about common scams, such as offers for free Robux, requests for login information, or links to unfamiliar websites. Stress that Roblox employees will never ask for passwords or personal information. Teach them to identify legitimate Roblox sites and communication channels. - Roblox employees will never ask for your password — Report anyone who asks using the Report Abuse feature. - There is no such thing as free Robux — Never trust players or sites who say they have a way to get free Robux! Check out this Roblox game to have your students learn more about online safety and civility: Google's Be Internet Awesome World [Google Be Internet Awesome](https://www.roblox.com/games/17756790122/Google-Be-Internet-Awesome-World). ### Guided tutorial - 35 Minutes Lead students through the following tutorial: [Building lesson](/docs/en-us/tutorials/curriculums/building.md). #### Tips - **Chapter 1:** Get Started 5 minutes - Let students play 2 - 3 minutes. This will give your students an idea of what they are working towards without having to go online. Let students know from the start how much time they have and give a one minute notice before time's up. - **Chapter 2:** Work with Parts 5 minutes - Remind students to rotate their camera view to get an accurate view of their project - Some students will be faster than others. Challenge faster students to create more parts for their obby using the greatest variety in size, shape, and rotation they can. - Emphasize that students are creating an experience that others can play, not just for themselves. - For those needing a greater challenge, encourage the students to experiment with the Properties window. What can they change about the parts? - **Chapter 3:** Coding with AI 10 Minutes - These Assistant requests are written in English to produce a specific and reliable result. Slight changes in grammar and word choice may cause different outcomes. - Encourage students to examine the Explorer and property windows to help them formulate prompts for Assistant. Properties is a key concept for those who continue coding. - **Chapter 4:** Test and Save 10 - 15 minutes - After students have saved and tested their games, allow students to switch seats and provide peer feedback - If time allows students five minutes to improve their obby based on the feedback provided. Have students switch places and play-test each other's experience. - Start by having students offer feedback in the form of two Stars (What they liked) and a Wish (What they would want to see). - After receiving feedback, each student should implement a change based on feedback they received. #### General tips Introduce students to "Ask Three Before Me": A student needs to ask three peers before asking the instructor for help. Keep in mind students have different prior experience with computers. Emphasize exact steps when teaching, such as when to double-click or right-click. Set strict time expectations for accomplishing a task, such as one minute to pick a color for a part, or two minutes to play test. ### Wrap-up 5 minutes #### Discussion questions - Why is it still important to learn how code works if AI is so useful? - Possible Answers: AI does not always generate the code for actions you intended. Learning to code can help you talk to AI and generate better results. - Did AI always provide the results you expected? Ask for examples where a student got unexpected results and what the student did in response. - What types of jobs do you think there are at gaming companies? - Possible Answers: Engineers, artists, musicians, organizers (project managers), accountants, moderators. People who train AI. #### Lesson recap Roblox, Roblox Studio, how to test games, and why you need to learn more about coding even with helpful AI like Assistant. --- title: "Create and Destroy" url: /docs/en-us/education/lesson-plans/create-and-destroy-lesson last_updated: 2026-06-29T19:34:00Z description: "Learn design thinking and world building by creating a multiplayer map where players stomp cities for points." --- # Create and Destroy **Lesson Description**: Learn design thinking and world building by creating a multiplayer map where players stomp cities for points. **ISTE Standards**: Innovative Designer 4a, 4c, 4d, Creative Communicator 6b, 6d | **Lesson objectives** | | | --- | --- | | **Skills and concepts** | | | **Prep** | | | **Materials** | | ## Lesson plan ### Overview | Duration | Activity | Description | | --- | --- | --- | | 5 min | Introduction | Introduce the project and lesson structure. | | 5 min | Guided Tutorial: Designing a Balanced Map | Introduce level design and setup a new project. | | 30 min | Guided Tutorial: Building the City | Build one half of the city and then duplicate to create a symmetrical design. | | 15 min | Guided Tutorial: Terrain Tools | Use terrain tools to sculpt an environment. | | 15 min | Independent Work: Polishing and Publishing | Add final touches, modify a script to change score, and publish the game online. | | 5 min | Wrap-up | Reflect on project and recap concepts learned. | ### Introduction 1. Grab attention by showing the video in [Presentation: Create and Destroy](../../assets/education/lesson-plans/createAndDestroy-presentation.pptx) - Slide 2. 2. Explain students will use Roblox Studio to design the look of their own city using premade buildings and props. That city will be used in a game where players destroy buildings for the most points. ### Guided tutorial - Design a balanced map 1. Lead students through [Designing a Map](/docs/en-us/education/build-it-play-it-create-and-destroy/designing-a-map.md). - When publishing the game, save time by having students use placeholder titles and descriptions (My game, my description). Can always change later when finished. ### Guided tutorial - Build the city 1. To minimize troubleshooting, have students check the following before building: - Collisions are off. - Snap to Grid is on and set to 4 studs. - Using the View Selector, camera is set to the Top View. 2. Lead students through [Build One Half](/docs/en-us/education/build-it-play-it-create-and-destroy/build-one-half.md). Stop at Complete the City. - To ensure students finish, set strict limits on how many objects to place per section. For example: 3-5 large buildings, 8 medium buildings, 4 props. - Remind students to build on half of the map; will duplicate later to create a full map. ### Guided tutorial - Terrain tools 1. Lead students through [Terrain Tools](/docs/en-us/education/build-it-play-it-create-and-destroy/terrain-tools.md). ### Independent work - Polish and publish 1. Lead students through [Changing the Script](/docs/en-us/education/build-it-play-it-create-and-destroy/change-the-script.md). 2. Have students catch up on their projects. If finished, can always add more buildings, roads, and props to their city. ### Wrap up - Recap what students have created and vocabulary: level designer and script. - Optional - To showcase work, have students trade seats with a partner and playtest their game. ## Appendix ### Troubleshooting tips **Facilitation tips** - If students are playtesting games together once publishing, set guidelines to ensure good sportsmanship. For example: - If you lose, don't make excuses. If you win, don't rub it in. - End with a handshake (or alternative action) and respect others. **Timing notes** - To ensure students finish their projects, set and enforce strict expectations on how many buildings or props they can place in a section. This helps students focus since they might get involved in building. ### Customize the lesson **Simplify the lesson** - Reduce the amount of recommended buildings, roads, and props. For example, instead of placing five buildings, add at most three. - Adding Terrain can be an extension if students finish early. **Expand the lesson** - Have students create their own 3D models using parts. In the page, [Finish the challenge](/docs/en-us/education/build-it-play-it-create-and-destroy/finish-the-challenge.md), go to the section saying Custom Models and follow those instructions. **Theme the lesson** - This lesson can be easily customized for different social studies units. For example: - Create custom buildings inspired by the architecture of famous city. - Research the biome and climate of a city and replicate it in game. ### Miscellaneous resources **Course description:** Discover how to build the ultimate competitive multiplayer game using Roblox's free coding and design tools. In this unique course, students will be able to explore the basics of game design and 3D modeling, then share their completed game to play with friends online! Created specifically for students new to game design, this curriculum is a great way for students to get started creating before moving onto more advanced projects and programming. In this course, your student will: - Design the city and environment for a multiplayer game - Test, evaluate, and redesign to create a fun, polished game experience. - Take home a complete game that can be played and shared online. --- title: "Animate characters" url: /docs/en-us/education/lesson-plans/digital-citizenship/animating-characters last_updated: 2026-06-29T19:34:00Z description: "Learn digital citizenship with Roblox. A full course created for middle to high school students. This session has students learning animation." --- # Animate characters **Lesson Description**: Students will further personalize their project by posing a 3D figure into a unique walk animation, whether it's swimming through the air or hopping. | **Lesson Objectives** | | | --- | --- | | **Skills and Concepts** | | | **Prep** | | ## Overview | Duration | Activity | Description | | --- | --- | --- | | 5 min | Introduction | Introduce the project and lesson structure. | | 5 min | Guided Activity: Plan an Animation | Brainstorm and plan an animation idea. | | 25 min | Guided Tutorial: Designing an Animation | Show how special effects can dramatically change a project's look. | | 10 min | Guided Activity: Add Animations | Use a script to change the player's walk in the obstacle course to the new animation. | | 10 min | Guided Activity: Feedback and Redesign | Get feedback from a peer to improve the animation. | | 5 min | Wrap Up | Recap the lesson and concepts learned. | ### Introduction 1. Grab student attention by showing Presentation: Animations in Roblox - Slide 2. - Students will create their own animation that will be included in their obstacle course. - Ask students to verbally offer some animation ideas for their games (ex: flying, skipping, etc). 2. After creating an animation, will get feedback on their animation from a peer to help them improve it. Remind that this is another opportunity to practice civility both online and in-person. ### Plan an animation 1. Lead students through Presentation: Animations in Roblox - Slide 3-4. - As a class, have students brainstorm actions (ex: real life actions/sports like swimming or dancing, or fantastical like flying. - Have students create a goal for their animation (make a silly walk, create a cool superhero flying animation, etc). Goals will be used for the feedback activity. - Give students 2-4 minutes to decide on an animation and collect reference material (either images or have them act out motions in real life). 2. Recap by letting 2-3 students share their planned animation. ### Design an animation 1. Lead students through the following tutorial: [Animation lesson](/docs/en-us/tutorials/curriculums/animator.md). - Note that this lesson uses a template which is not needed. Students will build this in their obstacle course experience. - Skip any pages referring to Challenge Checkpoints. - Slides are provided to help you throughout the tutorial. ### Add animations 1. Explain that students will use their exported animation as their players' walk 2. Use instructions in Presentation: Animations in Roblox - Slide 10 - 11. - Have students add the Animation Swap Script into their project, replace the animation ID in the script, and test to ensure their animation was replaced. 3. Ensure students have time to test out their animations. - If student animations aren't working, have them repeat the process. - For any students that are finished, have them help others. ### Feedback and redesign 1. Provide students the Handout: Animation Feedback. - Remind students about practicing civility in earlier sessions. Recap some guidelines if needed, or best practices in providing and receiving feedback. 2. Have students show an animation to another peer (either by switching seats or screen-sharing if remote). Peers will fill out the hand out to offer feedback and a recommendation. 3. When students have finished offering feedback, take a moment to discuss the value of feedback. Note they may not agree with feedback, but it's a valuable opportunity for them to grow. 4. Give students time to implement an idea they had after receiving feedback. ### Wrap up 1. Recap what students have created and vocabulary: animation and keypose. 2. Have students end the class by doing the pose they created in-game (if possible). --- title: "Being a digital citizen" url: /docs/en-us/education/lesson-plans/digital-citizenship/being-a-digital-citizen last_updated: 2026-06-29T19:34:00Z description: "Learn digital citizenship with Roblox. A full course created for middle to high school students. This session introduces upstanding and safety online." --- # Being a digital citizen **Lesson Description**: Learn how to positively contribute to online communities and confront cyberbullying. Then, work on the obstacle course and get feedback from peers. | **Lesson Objectives** | | | --- | --- | | **Skills and Concepts** | | | **Prep** | | | **Materials** | | ## Overview | Duration | Activity | Description | | --- | --- | --- | | 5 min | Introduction | Introduce the project and lesson structure. | | 15 min | Lesson: Digital Citizenship | Discuss ways on being a positive member of an online community and addressing cyberbullying. | | 30 min | Independent Work Time: Projects | Spend time adjusting the look and feel or change the layout of the obstacle course. | | 10 min | Guided Activity: Project Feedback | Play a peer's experience and offer feedback. Then set a goal to make a change based off feedback. | | 5 min | Wrap Up | Recap the lesson and concepts learned. | ### Introduction 1. Outline the session. - Will first learn about digital citizenship and how to intervene in cyberbullying. - Rest of the time will be devoted to improving their projects, ending with a feedback opportunity where they'll get to player each other's experiences. ### Digital citizenship 1. Go through the lesson [Intro to Digital Civility](/docs/en-us/education/resources/intro-to-digital-civility.md). 2. Guide students in an activity to practice upstanding. - Depending on your class, either do everything as a class or form small student groups (2-3 students). - Explain that students will hear different situations that they might see in an experience online. They'll listen and will practice a response. - Go through prompts in the Resource: Upstander Roleplay Situations and have students verbally roleplay their response. ### Work on projects 1. If needed, continue teaching any other lessons from the previous day. 2. Inform students they'll have this time to improve their projects. - While students work, encourage them to play-test (for at most one minute). Experiences should be challenging, but fun. 3. In the last five minutes, make sure students can play each other's projects. - If in person, they can just trade seats. - If remote, have students make their games public so others can play by sharing links. ### Project feedback 1. Open Presentation: Project Feedback and pass out Handout: Project Feedback to each student. This - Explain the process of playtesting and getting feedback in slides 2-3. - Connect to digital citizenship - ask students what are some ways they can practice being a good citizen while sharing and getting feedback (ex: Be open-minded with different experiences, don't pressure others to change their projects, etc). 2. Have students play each other's project for about five minutes, then write down feedback for their peer. 3. Reconvene as a class and have 2-3 students share a piece of feedback they received. 4. Have students reflect on making one change in their experience and write it down on the handout. - Will make that change tomorrow. - Have students pair up and share the change they'll make. ### Wrap up 1. Recap what students have created and the vocabulary term: **upstander**. 2. Have students connect giving feedback outside the classroom (online games, sports, talking with family). Remind that good online behavior is just like real life behavior. Ask these questions to start. - What are good or bad examples of giving someone feedback? Why? - What are good or bad examples of accepting feedback? How should you react or respond well when receiving feedback? --- title: "Create your first civil and safe game on Roblox" url: /docs/en-us/education/lesson-plans/digital-citizenship/civil-and-safe-game last_updated: 2026-06-29T19:34:00Z description: "Learn about civility and safety with Roblox. A full course created for middle to high school students. This session introduces how to be safe, civil, and responsible digital citizens." --- # Create your first civil and safe game on Roblox This ten-module course, with more than 400 minutes of content and hands-on activities, concentrates on transferable game design and engineering skills using Roblox Studio and the coding language "Lua". - The first six modules introduce students to the basics of Roblox Studio and introductory coding principles as well safety and civility principles necessary for creating games and experiences that respect the Roblox community. - The last four modules are designed as group projects that mimic real-world working environments. Throughout the course, there will be tie-ins to various careers and how the demonstrated skills are used in real life. - Two additional stand-alone extension lessons are included for students who progress through content faster than others. > **Info:** International Society of Technology in Education (ISTE) standards: [Innovative Designer 4a, 4c, 4d, Creative Communicator 6b, 6d](https://iste.org/standards/students). Click the following button to download a `.pdf` version of this lesson plan. --- title: "Coding fundamentals" url: /docs/en-us/education/lesson-plans/digital-citizenship/coding-fundamentals last_updated: 2026-06-29T19:34:00Z description: "Learn digital citizenship with Roblox. A full course created for middle to high school students. This session has students learning coding." --- # Coding fundamentals **Lesson Description**: Explore the coding concepts of functions, loops, and if/then statements with mini-projects that can be added to Roblox experiences like color changing platforms and traps. | **Lesson Objectives** | | | --- | --- | | **Skills and Concepts** | | ### Overview | Duration | Activity | Description | | --- | --- | --- | | 5 min | Introduction | Introduce the session. | | 20 min | Guided Tutorial: Working with Properties | Modify the color properties of parts using scripts. | | 35 min | Guided Tutorial: Coding a Trap | Code a trap using functions and if/then statements. | | 5 min | Wrap-up | Reflect on project and recap concepts learned. | ### Lesson Plan #### Introduction 1. Explain today's session will be different mini-projects to develop skills in scripting. #### Guided tutorial - Work with properties 1. Lead students through the following tutorials: - [Object Properties](/docs/en-us/tutorials/fundamentals/coding-1/object-properties.md) - [Parents and Children](/docs/en-us/tutorials/fundamentals/coding-1/parents-and-children.md) 2. Encourage students to troubleshoot independently if they have an error. To help, have them try: - Redoing their last few steps. - Asking a peer for help. 3. As students write code, check that students write comments that accurately describe the code. - Not Specific: Runs code - Improved: Changes color of PracticePart #### Guided tutorial - Code a trap 1. Lead students through the following tutorials: - [Code a function](/docs/en-us/tutorials/fundamentals/coding-2/code-a-function.md) - [Intro to if statements](/docs/en-us/tutorials/fundamentals/coding-3/intro-to-if-statements.md) - [Traps with if statements](/docs/en-us/tutorials/fundamentals/coding-3/traps-with-if-statements.md) 2. When working with if statements, check that students indent code to look like code samples. This helps make the code more readable, which becomes important as scripts grow larger. #### Wrap-up 1. Recap what students have created and vocabulary: **property**, **loop**, and **function**. ### Appendix ### Troubleshooting and classroom tips - While coding, remind students to double-check their capitalization or ask a peer to check their work. Even one incorrect letter can cause an error in longer words like `FindFirstChildWhichIsA`. - Try the following below to help students develop troubleshooting skills. - Purposely make a mistake in your code and ask students to identify the error. This can be easily turned into a game with students as you challenge them to find the error in a limited amount of time. - Write a partial line of code and ask students to complete it. - Utilize 'Ask 3 Before Me' where students ask three peers before asking the teacher for help. It's important for students to become comfortable independently troubleshooting as they advance to more complex projects. --- title: "Finish projects" url: /docs/en-us/education/lesson-plans/digital-citizenship/finishing-projects last_updated: 2026-06-29T19:34:00Z description: "Learn digital citizenship with Roblox. A full course created for middle to high school students. For the last session, students finish projects ." --- # Finish projects **Lesson Description**: Take time to catch up on previous lessons and prepare the Roblox experience for others to play. Students should evaluate if their project meets the goals of being fun, challenging, and bug-free; redesigning or improving their work as needed. | **Lesson Objectives** | | | --- | --- | | **Skills and Concepts** | | ## Overview | Duration | Activity | Description | | --- | --- | --- | | 5 min | Introduction | Introduce the project and lesson structure. | | 40 min | Guided Activity: Wrapping up the Project | Make final changes to their game. | | 15 min | Wrap Up and Showcase | Recap the week and showcase student projects. | ### Introduction 1. Explain that today is an opportunity to catch up or further develop their project. 2. Regardless of what students work, will want to evaluate and redesign their game to meet specific goals: - **Fun** - players enjoy playing, want to keep playing. - **Challenging** - players are challenge but not punished or overly frustrated. - **Error-free** - no script errors, unusual looking parts/visuals, etc. ### Wrap up the project 1. Explain that students have this time to make improvements before getting feedback. - Emphasize that improvements should be small (e.g. tweaking a current part of the obstacle course rather than adding new parts). - If unclear what to work on, ask students to self-evaluate against the three goals (fun, challenging, errors) and look for something to improve. ### Wrap up and showcase 1. Wrap up by leading a class discussion. Ask one of more of the below prompts. - One thing that was challenging but how they overcame it. - Something they're proud of in their experience and why. - An example of how another student helped them improve their project. 2. Recap learning about digital citizenship and civility this week. - Ask a few ways students can practice being a digital citizen or stay safe. Some ideas: standing up if someone is being bullied, helping others, spotting unreliable information. 3. Showcase student work. Some options are below. - Students can make games public and then share links to peers to try out. - Select 2-3 student projects and play them as a class. Either have one student show their game at their computer, or you get a link to play a project at your computer. --- title: "Intro to Roblox" url: /docs/en-us/education/lesson-plans/digital-citizenship/intro-to-roblox last_updated: 2026-06-29T19:34:00Z description: "Learn digital citizenship with Roblox. A full course created for middle to high school students. This session introduces students to Roblox." --- # Intro to Roblox **Lesson description**: Start the course by learning digital safety tips while being online. Then, start creating an obstacle course using Roblox Studio, the software used to make all Roblox games. | **Lesson Objectives** | | | --- | --- | | **Skills and Concepts** | | | **Prep** | | | **Materials** | | ## Overview | Duration | Activity | Description | | --- | --- | --- | | 5 min | Introduction | Introduce the project and lesson structure. | | 10 min | Lesson: Intro to Digital Safety | Introduce topics like protecting private information and finding reliable information online. | | 15 min | Guided Activity: Digital Safety Scavenger Hunt | Go through a Roblox game to identify unreliable information online. | | 25 min | Guided Tutorial: Intro to Roblox Studio | Teach the basics of using Roblox Studio while building a simple obstacle course. | | 5 min | Wrap Up | Recap the lesson and concepts learned. | ### Introduction 1. Introduce Roblox Studio by playing the Obby template. Explain to students that they will be: - Making their own obstacle course that others can play. - Learning ways they can have fun while being safe online. 2. Outline the sessions in the course. - Start of first two days will include lessons on digital safety and citizenship. - Rest of the sessions will include developing a game, practicing positive communication skills by giving and receiving feedback, and finding ways to create an uplifting community by helping others in their class. 3. Explain today's session will be learning the basics of Roblox Studio by building an obstacle course. ### Intro to digital safety 1. Lead students through Intro to Digital Safety, using [Presentation: Digital Safety](../../../assets/education/handouts/digital-safety-presentation.pptx). ### Digital safety scavenger hunt 1. Have students open the [Beat the Scammers](https://www.roblox.com/games/5450795073/Digital-Safety-Scavenger-Hunt) Roblox experience. Make sure to send students to a private server created for that game. - Provide students Handout: Digital Safety Scavenger Hunt. - Give students about five minutes to explore and collect information on their handouts. - (Optional) To help students, you can leave the Presentation: Digital Safety - Slide 5 up so students can see the four clues. 2. Give students a one minute warning, then transition them off the computers so they can discuss the experience. 3. As a class, discuss unreliable information found in that game. - Make connections to other relevant content students may see online (news websites, social media apps, other games, etc). - Encourage students to use the four clues noted the handout and explain why information is unreliable. ### Intro to Roblox Studio 1. Inform student they'll be learning how to create their own experiences using Roblox Studio. 2. Lead students through the following tutorial: [Building lesson](/docs/en-us/tutorials/curriculums/building.md). 3. As you teach, keep in mind the following: - Remind students to rotate their camera view to get an accurate view of their project. - Encourage students to use the Explorer, a foundational skill in Roblox Studio. - If parts fall or disappear when playtesting, remind students to turn Anchoring on for that part. ### Wrap up 1. Recap what students have learned in Digital Safety, such as private/personal information and ways to spot unreliable information. 2. If there's time, allow students to showcase work by trading seats with a partner and playing their obstacle course. --- title: "Digital citizenship with Roblox" url: /docs/en-us/education/lesson-plans/digital-citizenship/landing last_updated: 2026-06-29T19:34:00Z description: "Learn digital citizenship with Roblox. A full course created for middle to high school students." --- # Digital citizenship with Roblox Teach your students digital citizenship while building their skills in computer science and design thinking. Throughout this course, students will learn how to stay safe online and create positive relationships. As they do so, they'll use Roblox Studio to design an obstacle course, getting a sampling of professional fields like 3D modeling, animation and coding. While iterating on their designs, they'll focus on their collaborative communication skills , providing peer feedback and reflecting on their design process. **Learning objectives and outcomes** - Participate online in a way that fosters positive relationships and builds community. - Practice design thinking by planning, designing, and playtesting a game. - Create scripts for gameplay elements using coding concepts like if/then statements and variables. **ISTE standards**: Empowered Learner 1d, Innovative Designer 4a, 4c, Creative Communicator 6b, 6c > **Info:** This course is comprised of multiple lesson plans intended to be used over time. We estimate each lesson plan will be a full class day (45 minutes) for high school students. ### Sessions | Session | Description | | --- | --- | | [Intro to Roblox](/docs/en-us/education/lesson-plans/digital-citizenship/intro-to-roblox.md) | Start the course by learning digital safety tips while being online. Then, start creating an obstacle course using Roblox Studio, the software used to make all Roblox games. | | [Being a Digital Citizen](/docs/en-us/education/lesson-plans/digital-citizenship/being-a-digital-citizen.md) | Learn how to positively contribute to online communities and confront cyberbullying. Then, work on the obstacle course and get feedback from peers. | | [Lights and Polish](/docs/en-us/education/lesson-plans/digital-citizenship/lights-and-polish.md) | Personalize the obstacle course with special effects and work on implementing feedback and improvements. | | [Coding Fundamentals](/docs/en-us/education/lesson-plans/digital-citizenship/coding-fundamentals.md) | Explore coding concepts like if/then statements. Using Luau, the programming language in Roblox, students will add traps into their game for a new challenge. | | [Animating Characters](/docs/en-us/education/lesson-plans/digital-citizenship/animating-characters.md) | Students will further personalize their game by posing a 3D figure into a unique walk animation, whether it's "swimming" through the air or hopping. | | [Finishing Projects](/docs/en-us/education/lesson-plans/digital-citizenship/finishing-projects.md) | Take time to catch up on previous lessons and prepare the adventure game for others to play. Students should evaluate if their game meets the goals of being fun, challenging, and bug-free; redesigning or improving their game as needed. | --- title: "Lights and polish" url: /docs/en-us/education/lesson-plans/digital-citizenship/lights-and-polish last_updated: 2026-06-29T19:34:00Z description: "Learn digital citizenship with Roblox. A full course created for middle to high school students. This session has students improve their projects." --- # Lights and polish **Lesson Description**: Personalize the obstacle course with special effects and work on implementing feedback and improvements. | **Lesson Objectives** | | | --- | --- | | **Skills and Concepts** | | ## Overview | Duration | Activity | Description | | --- | --- | --- | | 5 min | Introduction | Introduce the project and lesson structure. | | 15 min | Guided Activity: Implement Feedback | Implement a change in their project from feedback they received previously. | | 5 min | Lesson: Post Processing Effects | Show how special effects can dramatically change an experience's look. | | 30 min | Independent Work Time: Projects | Spend time adjusting the look and feel or change the layout of the obstacle course. | | 5 min | Wrap Up | Recap the lesson and concepts learned. | ### Introduction 1. Grab student attention by showing them some of the before/after pictures and videos on the [Post Processing Effects](/docs/en-us/environment/post-processing-effects.md) page. 2. Outline the session. - Students will start by implementing feedback from the last session. - Next will be a brief lesson on how to add post-processing effects, a type of special effect. Rest of class time will be used on projects. ### Implement feedback 1. Remind students about giving each other feedback using the handout. They will now take time to make a change in their project based off the goal they wrote down in the previous session. 2. Help students 1 on 1 if they're reluctant to make changes on feedback. Remind them making changes can help their experience. ### Post processing effects 1. Before giving more work time, going to show students how to improve their projects's look and feel. 2. Point out students don't need to use their computers during this lesson, they just need to follow along with you. 3. Show students the different post processing effects available in [Post Processing Effects](/docs/en-us/environment/post-processing-effects.md). - In this case, just show students where they can add an effect and explain each one. Students can independently explore each one in the next activity. Students don't need to directly follow along. ### Work on projects 1. Introduce Ask 3 Before Me - students ask 3 peers for help before asking the teacher. Call out this as an opportunity for digital citizenship and upstanding - if someone sees another struggling, ask if you can help. 2. Explain that students can use this time to either continue working on their obstacle course or to add special effects. ### Wrap up 1. Recap what students have created and vocabulary: **Post Processing Effect**. 2. To showcase work, have students trade seats with a partner and let them explore their world. --- title: "Galactic Speedway" url: /docs/en-us/education/lesson-plans/galactic-speedway-lesson last_updated: 2026-06-29T19:34:00Z description: "Create a spaceship and race it on an alien planet." --- # Galactic Speedway **Lesson description**: Create your very own spaceship and race it on an alien planet. Practice skills like 3D modeling and coding as you publish an experience on Roblox. **ISTE standards**: Innovative Designer 4a, 4c, 4d, Creative Communicator 6b, 6d | **Lesson objectives** | | | --- | --- | | **Skills and concepts** | | | **Prep** | | | **Materials** | | ## Overview | Duration | Activity | Description | | --- | --- | --- | | 5 min | Introduction | Introduce the project and lesson structure. | | 5 min | Guided Tutorial: Take a Test Drive | Teach the basics of using Roblox Studio while building a simple obstacle course. | | 35 min | Guided Work: Design, Evaluate, and Redesign | Set a goal and build a driftspeeder using premade parts. | | 10 min | Independent Work: Polish and Publish | Add final touches, modify a script to change speed, and publish the experience online. | | 10 min | Wrap-up | Reflect on project and recap concepts learned. | ## Lesson plan ### Introduction 1. Grab attention by showing the video in [Presentation: Build It Play It](../../assets/education/lesson-plans/galacticSpeedway-presentation.pptx) - Slide 2. - Inform students that they're going to build a spaceship and use it to race with friends. ### Guided tutorial - Take a test drive 1. Establish context by leading students through [Get Off The Ground](/docs/en-us/education/build-it-play-it-galactic-speedway/get-off-the-ground.md) through [Moving the Body](/docs/en-us/education/build-it-play-it-galactic-speedway/moving-the-body.md). - Before students test fly a spaceship, set a time limit (such as 2 minutes) to ensure students stay focused. 2. Lead students through [Save and Publish](/docs/en-us/education/build-it-play-it-galactic-speedway/save-and-publish.md). - When publishing the game, save time by having students use placeholder titles and descriptions (My experience, my description). Can always change later when finished. ### Guided work - Design, evaluate, and redesign 1. Pass out and Introduce the [Handout: Galactic Speedway Self Evaluation](../../assets/education/lesson-plans/galacticSpeedway-evaluationHandout.pdf). - At the end of building, will self evaluate themselves based on goals. - Read out loud the different possible goals. - Give students 1-2 minutes to silently identify a goal in building. - _Optional: Have students pair-share or share as a class._ 2. Lead students through [Adding Wings](/docs/en-us/education/build-it-play-it-galactic-speedway/adding-wings.md) to [Test the Speeder](/docs/en-us/education/build-it-play-it-galactic-speedway/test-the-speeder.md). - To ensure students finish, set time limits on each step (e.g. five minutes for wings). - Save at least five minutes for grouping speeders and testing their work. ### Independent work - Polish and publish - Lead students through [Customize and Share](/docs/en-us/education/build-it-play-it-galactic-speedway/customize-and-share.md). - Have students catch up on their projects. If finished, can always improve their driftspeeder or build another one. ### Wrap up - Close computers and have students follow instructions on Self Evaluation Handout. - Recap what students have created and vocabulary: 3D Modeling and variable. - To showcase work, have students trade seats with a partner and fly their driftspeeder. ## Appendix ### Troubleshooting tips **General troubleshooting** - Introduce students to "Ask 3 Before Me": a student needs to ask three peers before asking the instructor for help. - If a driftspeeder isn't flying, try the following: - Make sure it's included in the Garage. Note that the Garage is a specific folder in the Explorer part of the Studio workspace. - Check that the finished model includes at least one Body part. This part includes a script allowing the model to fly. **General building tips** - Encourage students to check their work from multiple angles using the camera tools. It's possible that buildings may be floating or misaligned. - Remind students to experiment with different sizes and rotations for parts. - While students build, call out unique uses of parts (Ex: _Oh, I didn't think about using a wing as a body! Huh, I didn't think you could use those parts to make a car!_) ### Classroom management **Facilitation tips** - One way of protecting students online is setting up VIP servers. This feature allows you to create servers where only specific users can enter. For example, when students publish their experience, they can allow for private servers where only members of the class can join. See [Private Servers](/docs/en-us/education/support/private-servers-for-classroom-use.md) for more information. - While students playtest, set guidelines for good sportsmanship. For example: - Respect and encourage others. - If you lose, don't make excuses. If you win, don't rub it in. **Timing notes** - To ensure students finish projects, ste and enforce expectations on how much time per section (body, wings, decorations), such as five minutes for each. Because students might get focused on one section, this helps them complete projects fully. ### Customize the lesson **Expand the lesson** Pair Building Activity - Group up two students. Have one publish a place and set up [Live Collaboration](/docs/en-us/projects/collaboration.md) . - Have students take turns adding one piece at a time, or making one change at a time until they create a finished speeder. **Simplify the lesson** - The Self-Evaluation checklist can be optional. - Have students add at most two wings and two decorations. ### Miscellaneous resources **Check for understanding** Students can go online to take a quiz or alternatively, you can quiz students. - Online: Students can take a quiz through this [online experience](https://www.roblox.com/games/4201429814/) and earn virtual Roblox prizes. - Quiz: Use this [handout](../../assets/education/lesson-plans/galacticSpeedway-quizQuestions.pdf) to quiz students verbally. **Course description:** Enter the space race of the millenia! No ship, no problem. Discover how your own racing spaceship using Roblox's free coding and design tools. In this course, students will explore the basics of 3D modeling and programming, then share their completed experience to play with friends online. Created specifically for students new to game design, this curriculum is a great way for students to get started creating before moving onto more advanced projects and programming. In this course, your student will: - Design a spaceship for a multiplayer racing experience. - Test, evaluate, and redesign to create a fun, polished game. - Take home a complete experience that can be played and shared online. --- title: "Intro to coding and game design" url: /docs/en-us/education/lesson-plans/intro-to-game-and-coding last_updated: 2026-06-29T19:34:00Z description: "Learn how to build an obstacle course and code color changing blocks." --- # Intro to coding and game design **Lesson description**: Get students creating and coding their first experience in Roblox. Learn how to build an obstacle course and code color changing blocks. **ISTE standards**: Innovative Designer 4a, 4c, 4d, Creative Communicator 6b, 6d | **Lesson objectives** | | | --- | --- | | **Skills and concepts** | | | **Prep** | | | **Materials** | | ## Overview | Duration | Activity | Description | | --- | --- | --- | | 5 min | Introduction | Introduce the project and lesson structure. | | 30 min | Guided Tutorial: Intro to Studio | Introduce the basics of Roblox Studio by building the first few pieces of an obstacle course. | | 60 min | Guided Tutorial: Intro to Coding | Create scripts that change the colors of a part. | | 20 min | Independent Work: Finish Student Projects | Let students catch up or continue building their project. | | 5 min | Wrap-up | Reflect on project and recap concepts learned. | ## Lesson Plan ### Introduction 1. Introduce the course: - Students will be making their own obby (obstacle course) that others can play. - Will also learn coding to create color changing parts to decorate their obby with. 2. Make sure all students are actively logged into Roblox Studio. Note that if this is their first time, it's recommended to allot 5 minutes for this process in case any student has an issue logging in. ### Guided tutorial - Intro to Studio 1. Lead students through the following tutorial: [Building lesson](/docs/en-us/tutorials/curriculums/building.md). 2. As you teach, keep in mind the following: - Remind students to rotate their camera view to get an accurate view of their project. - Encourage students to use the Explorer, a foundational skill in Roblox Studio. ### Guided tutorial - Intro to coding 1. Inform students that they'll now start coding to add to their experience. - During this time, students should focus on coding and not continuing to work on their obby. They'll have time later to continue adding more parts or playtesting. 2. Lead students through the following tutorials: - [Create a script](/docs/en-us/tutorials/fundamentals/coding-1/create-a-script.md) - [Object properties](/docs/en-us/tutorials/fundamentals/coding-1/object-properties.md) - [Parents and children](/docs/en-us/tutorials/fundamentals/coding-1/parents-and-children.md) ### Independent work - Finish student projects 1. As students go into independent work, point out they have the following goals to achieve for their obstacle course. - A user can reach the end (with a reasonable level of difficulty). - Have a balance of easy and difficult jumps. - Be free of any unintended visual or code issues. 2. Have students catch up with their coding projects. If finished, give them time to work on adding more to or improving their obstacle course. 3. If there's time, two students can play-test each other's experiences and offer feedback. ### Wrap up 1. Recap what students have created and vocabulary: **variable**, **string**, **loop**. 2. Ask two or three students what they would add to their experience to improve it. For example, a more visually interesting endpoint or more challenging jumps ## Appendix ### Troubleshooting tips **General troubleshooting tips** - Introduce students to "Ask 3 Before Me": a student needs to ask three peers before asking the instructor for help. - Keep in mind students have different prior experience with computers. Emphasize exact steps when teaching, such as when to double-click or right-click. - Set strict time expectations for accomplishing a task, such as 1 minute to pick a color for a part, or two minutes to play test. **Intro to Studio** - Emphasize that students are creating an experience that others can play, not just for themselves. - If you're running low on time, the lesson Colors and Materials can be optional. **Intro to coding** - The most common errors for students at this age are improper capitalization and typos. - Ask students leading questions rather than solving the issue for them as much as possible. This allows students to build valuable troubleshooting skills. For example: _What symbols need to be around a string? - A quotation marks_. ### Customize the lesson **Expand the Lesson** Have students switch places and play-test each other's experience. - Start by having students offer feedback in the form of 2 Stars (What they liked) and a Wish (What they would want to see). - After receiving feedback, each student should implement a change based on feedback they received. ### Extra resources If you're using this lesson as part of a workshop, we've included a description for public use. **Example course description:** With over 60 millions users a month, Roblox is the world's largest social technology platform. But did you know that all Roblox experiences created by the users? Learn to create and code your own games using the free tools made available by Roblox. This is a perfect course for first time, aspiring game developers. In this course, your student will: - Learn programming fundamentals with Luau. - Explore design thinking and 3D modeling by creating an obstacle course. - Take home a complete experience that can be played and shared online. --- title: "Roblox developer 101" url: /docs/en-us/education/lesson-plans/roblox-developer/landing last_updated: 2026-06-29T19:34:00Z description: "A full course of lesson plans to teach students to develop and code in Roblox. Great for beginners interested in games or computer science." --- # Roblox developer 101 Teach how to code and create games for the Roblox platform - perfect for educators looking for an introductory course with multiple sessions. In the first half of the course, students develop skills in 3D modeling, coding, and design by creating an obstacle course and using code to add gameplay elements like traps. In the second half, they use those skills to create an adventure game where players explore, gather resources, and purchase items. **Learning objectives and outcomes** - Manipulate 3D parts and sculpt environments to create a virtual world. - Experience the game design process by planning, designing, and playtesting a game. - Create scripts for gameplay elements using concepts like if/then statements and variables. **ISTE standards**: Empowered Learner 1d, Innovative Designer 4a, 4c, Creative Communicator 6b, 6c > **Info:** This course is comprised of multiple lesson plans intended to be used over time. We estimate each lesson plan will be a full class day (45 minutes) for high school students. ### Sessions | Session | Description | | --- | --- | | [Roblox Studio Basics](/docs/en-us/education/lesson-plans/roblox-developer/roblox-developer-1.md) | Introduce students to the course and projects they'll be working on. Then, develop foundational skills such as manipulating parts and creating scripts by creating a simple obstacle course. Once these skills are mastered, students can go on to code an adventure game in later sessions. | | [Coding Fundamentals](/docs/en-us/education/lesson-plans/roblox-developer/roblox-developer-2.md) | Explore the coding concepts of functions, loops, and if/then statements with mini-projects that can be added to games like color changing platforms and traps. | | [Adventure Game Pt. 1](/docs/en-us/education/lesson-plans/roblox-developer/roblox-developer-3.md) | After learning the basics of using Roblox Studio and scripting, students take their skills further by starting a more complex project, the adventure game. They'll plan out elements of their game, create a virtual world, and setup basic gameplay components like keeping track of player items. | | [Adventure Game Pt. 2](/docs/en-us/education/lesson-plans/roblox-developer/roblox-developer-4.md) | Students continue developing the adventure game. They'll create scripts for using tools, selling items, and upgrading their spaces. | | [Adventure Game Pt. 3](/docs/en-us/education/lesson-plans/roblox-developer/roblox-developer-5.md) | Take time to catch up on previous lessons and prepare the adventure game for others to play. Students should evaluate if their game meets the goals of being fun, challenging, and bug-free; redesigning or improving their game as needed. | --- title: "Roblox Studio basics" url: /docs/en-us/education/lesson-plans/roblox-developer/roblox-developer-1 last_updated: 2026-06-29T19:34:00Z description: "Part of the Roblox Developer education series. Teach students to use Roblox to build an obstacle course game." --- # Roblox Studio basics **Lesson description**: Introduce students to the course and projects they'll be working on. Then, develop foundational skills such as manipulating parts and creating scripts by creating a simple obstacle course. Once these skills are mastered, students can go on to code an adventure game in later sessions. | **Lesson objectives** | | | --- | --- | | **Skills and concepts** | | | **Prep** | | | **Materials** | | ### Overview | Duration | Activity | Description | | --- | --- | --- | | 5 min | Introduction | Introduce the project and lesson structure. | | 5 min | Guided Tutorial: Roblox Studio Basics | Teach the basics of using Roblox Studio while building a simple obstacle course. | | 15 min | Guided Tutorial: Guided Tutorial | Introduce coding by creating a script that displays text. | | 5 min | Wrap-up | Reflect on project and recap concepts learned. | ### Lesson plan #### Introduction 1. Grab student attention by playing an example of the finished [adventure game](https://www.roblox.com/games/9544298367/). This is an example of the type of project they'll have by the end of the course. - Explain the game: Players explore a world to collect items and upgrade their backpack. - Call out that students will create and customize the game to their own vision (e.g. collecting cupcakes in a fantasy world or harvesting iron on the moon). 2. Outline the sessions in the course - First two sessions focus on developing skills in game design and coding by making practice projects. This will include building an obstacle course. - Will spend rest of sessions developing an adventure game 3. Point out for this session or day, students will be learning the basics of Roblox Studio by building an obstacle course. - Note the obstacle course is practice, but the can include parts of it in their adventure game if desired. #### Guided tutorial - Roblox Studio basics 1. Lead students through the following tutorial: [Introduction to Roblox Studio](/docs/en-us/tutorials/first-experience.md). 2. As you teach, keep in mind the following: - Remind students to rotate their camera view to get an accurate view of their project. - Encourage students to use the Explorer, a foundational skill in Roblox Studio. #### Guided tutorial - Create scripts 1. Lead students through the [Create a script](/docs/en-us/tutorials/fundamentals/coding-1/create-a-script.md) tutorial. #### Wrap up 1. Recap what students have created and vocabulary learned: variable and string. ### Appendix #### Troubleshooting tips **General troubleshooting** - If parts fall or disappear when playtesting, remind students to turn Anchoring on for that part. - The most common errors for students at this age are improper capitalization and typos. Encourage students to independently check their work, rather than rely on teachers. #### Classroom management - Introduce students to "Ask 3 Before Me": a student needs to ask three peers before asking the instructor for help. - Keep in mind students have different prior experience with computers. Emphasize exact steps when teaching, such as when to double-click or right-click. - Keep your lessons on time by setting strict limits for accomplishing tasks, such as allowing only a minute to add a single part or to pick a color. To build troubleshooting skills, ask students leading questions rather than solving the issue for them as much as possible. For example: _What symbols need to be around a string? - A quotation marks._ --- title: "Coding fundamentals" url: /docs/en-us/education/lesson-plans/roblox-developer/roblox-developer-2 last_updated: 2026-06-29T19:34:00Z description: "Part of the Roblox Developer education series. Teach students to code in Roblox with lessons for beginners." --- # Coding fundamentals **Lesson description**: Explore the coding concepts of functions, loops, and if/then statements with mini-projects that can be added to games like color changing platforms and traps. | **Lesson objectives** | | | --- | --- | | **Skills and concepts** | | ### Overview | Duration | Activity | Description | | --- | --- | --- | | 5 min | Introduction | Introduce the session. | | 20 min | Guided Tutorial: Working with Properties | Modify the color properties of parts using scripts. | | 35 min | Guided Tutorial: Coding a Trap | Code a trap using functions and if/then statements. | | 5 min | Wrap-up | Reflect on project and recap concepts learned. | ### Lesson plan #### Introduction 1. Explain today's session will be different mini-projects to develop skills in scripting - Important to understand these concepts since adventure game will have more scripts. - Point out these projects, like a trap, can be included in the adventure game project they'll start in Session 3. #### Guided tutorial - Work with properties 1. Lead students through the following tutorials: - [Object Properties](/docs/en-us/tutorials/fundamentals/coding-1/object-properties.md) - [Parents and Children](/docs/en-us/tutorials/fundamentals/coding-1/parents-and-children.md) 2. Encourage students to troubleshoot independently if they have an error. To help, have them try: - Redoing their last few steps. - Asking a peer for help. 3. As students write code, check that students write comments that accurately describe the code. - Not Specific: Runs code - Improved: Changes color of PracticePart #### Guided tutorial - Code a trap 1. Lead students through the following tutorials: - [Code a function](/docs/en-us/tutorials/fundamentals/coding-2/code-a-function.md) - [Intro to if statements](/docs/en-us/tutorials/fundamentals/coding-3/intro-to-if-statements.md) - [Traps with if statements](/docs/en-us/tutorials/fundamentals/coding-3/traps-with-if-statements.md) 2. When working with if statements, check that students indent code to look like code samples. This helps make the code more readable, which becomes important as scripts grow larger. #### Wrap-up 1. Recap what students have created and vocabulary: **property**, **loop**, and **function**. 2. Point out in the next session, students will start a new project where they make an adventure game. ### Appendix ### Troubleshooting and classroom tips - While coding, remind students to double-check their capitalization or ask a peer to check their work. Even one incorrect letter can cause an error in longer words like `FindFirstChildWhichIsA`. - Try the following below to help students develop troubleshooting skills. - Purposely make a mistake in your code and ask students to identify the error. This can be easily turned into a game. - Write a partial line of code and ask students to complete it. - Utilize 'Ask 3 Before Me' where students ask three peers before asking the teacher for help. It's important for students to become comfortable independently troubleshooting as they advance to more complex projects. --- title: "Adventure game part 1" url: /docs/en-us/education/lesson-plans/roblox-developer/roblox-developer-3 last_updated: 2026-06-29T19:34:00Z description: "The first part of a Roblox Developer education series. Teach students to create an adventure game in Roblox." --- # Adventure game part 1 **Lesson description**: After learning the basics of using Roblox Studio and scripting, students take their skills further by starting a more complex project, the adventure game. They'll plan out elements of their game, create a virtual world, and setup basic gameplay components like keeping track of player items. | **Lesson objectives** | | | --- | --- | | **Skills and concepts** | | | **Materials** | | ### Overview | Duration | Activity | Description | | --- | --- | --- | | 5 min | Introduction | Introduce the session. | | 10 min | Guided Work: Planning a Game | Create a game design document to plan out elements of the project. | | 10 min | Guided Work: Creating the World | Design an environment using terrain tools based off the previous vision document. | | 30 min | Guided Tutorial: Setting up The Game | Code scripts to keep track of player items and build the first item players will collect. | | 5 min | Wrap-up | Reflect on project and recap concepts learned. | ### Lesson plan #### Introduction 1. Explain that students will be using skills learned in previous sessions to build a game over the next three sessions. They will: - Plan out their unique world using a game design document. - Create a world in Roblox referencing their plans in the game design document. - Create and code items for players to gather in-game. #### Guided work - Plan a game 1. Note that students don't need access to computers at the start of this section. 2. Lead students through the lesson: [Create the Map](/docs/en-us/education/adventure-game-series/create-the-map.md), stopping before the section: Creating the Environment. 3. As students draw their starting area, keep in mind the following. - Areas drawn should be achievable in scale of what a student can do in a week - such as a house with a front lawn or a simple forest vs a detailed city block. This helps focus students and they can always add more when finished. - Drawings don't have to be complex - simple symbols like circles and squares are enough. #### Guided work - Create the world 1. Students should be at their computers for this section. 2. Lead students through the lesson: [Create the Map](/docs/en-us/education/adventure-game-series/create-the-map.md), starting at Creating the Environment - Briefly show students each terrain tool at once and give at most six minutes to create their starting areas. They can always add more later. #### Guided tutorial - Set up the game 1. Lead students through the following tutorials: [Coding the Leaderboard](/docs/en-us/education/adventure-game-series/code-the-leaderboard.md). #### Wrap-up 1. Recap what students have created and vocabulary: game mechanic and pre-production. 2. Optional: Have students to reflect on the mid-point of their sessions by asking one or more of the following questions: - One thing that was challenging but how they overcame it. - One skill you're looking to improve over the next two sessions (e.g. better at troubleshooting, making more interesting worlds, etc). - What was the most exciting thing you accomplished today. How did you do that and why was it exciting? ### Appendix #### Troubleshooting tips - The name of the `leaderstats` variable must be `"leaderstats"`. Without this, the script won't know to create a new leaderboard. - [Handout: Adventure Game Reference](../../../assets/education/lesson-plans/adventureGameReferenceHandout.pdf) helps students keep track of variable names. This is especially useful if they replaced default variable names, like `"Gold"` with something of their own, like `"Rubies"`. - Remind students to build everything using parts, not by using the Toolbox. Using the Toolbox may introduce unexpected issues into their games. #### Classroom management - As students work on their project, help them keep in mind a reasonable scale of what they can accomplish by the end of the sessions. - If students have ambitious goals (_I want to build three different worlds in my game)_, remind them to focus on their goals for today's current session. Have them write down their additional ideas on the game design document. - Set strict expectations as to how much time students can spend working on their starting area. They can always continue in later sessions. #### Customize the lesson - Students can spend more time building out their starting area using the terrain tools or adding decorative parts. - If students are unclear what to add, ask questions about what objects they'd expect to find in that themed world (E.g. _If you were on a moonbase, what would you see? How can you build that using parts?_) - Students can add more than one type of item to harvest. Just remember that each item follows the same organization in the Explorer and has a BoolValue named `CanHarvest` set to true. --- title: "Adventure game part 2" url: /docs/en-us/education/lesson-plans/roblox-developer/roblox-developer-4 last_updated: 2026-06-29T19:34:00Z description: "The second part of a Roblox Developer education series. Teach students to create an adventure game in Roblox." --- # Adventure game part 2 **Lesson description**: Students continue developing the adventure game. They'll create scripts for using tools, selling items, and upgrading their spaces. | **Lesson objectives** | | | --- | --- | | **Prep** | | ### Overview | Duration | Activity | Description | | --- | --- | --- | | 5 min | Introduction | Introduce the session. | | 50 min | Guided Tutorial: Scripting Game Mechanics | Have students create scripts for tools, selling items, and upgrading spaces. | | 5 min | Wrap-up | Reflect on project and recap concepts learned. | ### Lesson plan #### Introduction 1. Explain that students will be adding the core gameplay mechanics of the adventure game today: using a tool to collect items, selling items, and upgrading their spaces to collect more items. 2. Point out that this session will be heavy on guided tutorials. The next session will be more free-form, so students can work on their own or catch up. #### Guided tutorial - Script game mechanics 1. Make sure all students have access to the [starter tool](../../../assets/education/adventure-game-series/starterTool.rbxm). 2. Lead students through the following tutorials: - [Collecting Items](/docs/en-us/education/adventure-game-series/collect-items.md) - [Selling Items](/docs/en-us/education/adventure-game-series/selling-items.md) - [Buying Upgrades](/docs/en-us/education/adventure-game-series/buying-upgrades.md) #### Wrap-up 1. Recap what students have created. 2. Optional - Ask students what feature they're excited to work on or add tomorrow as they finish their games. ### Appendix #### Troubleshooting and classroom tips **General troubleshooting** - While students code, leave up example scripts for students to reference. The Tool script is especially helpful as it has nested if statements. - Check that students are indenting their code to look like the code samples. This makes code more readable and reduces the possibility of errors. **Scripting tips** - Remind students to always add a comma between multiple parameters in a function, like in `sellItems(playerItems, playerGold)`. - In the Upgrade script, the order of functions matters. Make sure that the function `giveUpgrade()` is above `clickDetector.MouseClick`. **Facilitation tips** - To keep students on track, set a strict time limit for playtesting, like one minute. - Encourage students to remember a specific goal whenever playtesting, such as check if a tool harvests an item as intended. This helps them stay focused. #### Customize the lesson **Expand the lesson** - More than one item can be added to harvest, just remember that each item needs a BoolValue with `CanHarvest` set to true. - Surface GUI's, like the Upgrade Sign, can be customized. Encourage students to explore properties of TextLabels, like color and font in the properties of that TextLabel and SurfaceGui. --- title: "Adventure game part 3" url: /docs/en-us/education/lesson-plans/roblox-developer/roblox-developer-5 last_updated: 2026-06-29T19:34:00Z description: "The third part of a Roblox Developer education series. Teach students to create an adventure game in Roblox." --- # Adventure game part 3 **Lesson description**: Take time to catch up on previous lessons and prepare the adventure game for others to play. Students should evaluate if their game meets the goals of being fun, challenging, and bug-free; redesigning or improving their game as needed. | **Lesson objectives** | | | --- | --- | | **Skills and concepts** | | ### Overview | Duration | Activity | Description | | --- | --- | --- | | 5 min | Introduction | Introduce the goals for student games and ability to catch up or take projects forward. | | 15 min | Guided Tutorial: Getting the Game Ready | Make minor changes to their game and take time to playtest with a peer. | | 30 min | Independent Work | Catch up on previous lessons, expand their game with improved environments, or more items to collect. | | 10 min | Wrap-up | Recap the course and share games. | ### Lesson plan #### Introduction 1. Explain that today is an opportunity to catch up or further develop their game. 2. Regardless of what students work, will want to evaluate and redesign their game to meet specific goals: - **Fun** - players enjoy playing, want to keep playing - **Challenging** - players have a challenge but aren't punished or overly frustrated - **Error and glitch free** - no script errors, unusual looking parts/visuals, etc. #### Guided tutorial - Get the game ready 1. Lead students through [Finishing the Project](/docs/en-us/education/adventure-game-series/finishing-the-project.md). - Playtesting is optional. Depending on your class, that time might be utilized for students to catch up on their projects. #### Independent work 1. Have students either catch up on lessons or further develop their final projects. - As students work, their actions should help them meet the goals for today (fun, challenging, error-free). 2. If students feel finished, you can encourage them to: - Add additional items or upgrades. - Use the terrain tools to expand and improve their environment. - Add decorative parts that fit their environments theme. - Have a friend play their game and get feedback. #### Wrap-up 1. Wrap up by leading a class discussion. Ask one of more of the below prompts. - One thing that was challenging but how they overcame it. - Something they're proud of in their game and why. - An example of how another student helped thi improve their game. 2. To showcase work, have students trade seats with a partner and let thi explore their world. --- title: "Roblox developer 101" url: /docs/en-us/education/lesson-plans/roblox-developer-lesson last_updated: 2026-06-29T19:34:00Z description: "Learn the basics of creating an environment and scripting gameplay elements." --- # Roblox developer 101 Teach how to code and create games for the Roblox platform - perfect for educators looking for an introductory course with multiple sessions. In the first half of the course, students develop skills in 3D modeling, coding, and design by creating an obstacle course and using code to add gameplay elements like traps. In the second half, they use those skills to create an adventure game where players explore, gather resources, and purchase items. **Learning objectives and outcomes** - Manipulate 3D parts and sculpt environments to create a virtual world. - Experience the game design process by planning, designing, and playtesting a game. - Create scripts for gameplay elements using concepts like if/then statements and variables. **ISTE standards**: Empowered Learner 1d, Innovative Designer 4a, 4c, Creative Communicator 6b, 6c **Sessions** | Session | Description | | --- | --- | | [Roblox Studio Basics](#1--roblox-studio-basics) | Introduce students to the course and projects they'll be working on. Then, develop foundational skills such as manipulating parts and creating scripts by creating a simple obstacle course. Once these skills are mastered, students can go on to code an adventure game in later sessions. | | [Coding Fundamentals](#2--coding-fundamentals) | Explore the coding concepts of functions, loops, and if/then statements with mini-projects that can be added to games like color changing platforms and traps. | | [Adventure Game Pt. 1](#3-adventure-game-pt-1) | After learning the basics of using Roblox Studio and scripting, students take their skills further by starting a more complex project, the adventure game. They'll plan out elements of their game, create a virtual world, and setup basic gameplay components like keeping track of player items. | | [Adventure Game Pt. 2](#4---adventure-game-pt-2) | Students continue developing the adventure game. They'll create scripts for using tools, selling items, and upgrading their spaces. | | [Adventure Game Pt. 3](#5---adventure-game-pt-3) | Take time to catch up on previous lessons and prepare the adventure game for others to play. Students should evaluate if their game meets the goals of being fun, challenging, and bug-free; redesigning or improving their game as needed. | ## 1- Roblox Studio basics **Lesson Description**: Introduce students to the course and projects they'll be working on. Then, develop foundational skills such as manipulating parts and creating scripts by creating a simple obstacle course. Once these skills are mastered, students can go on to code an adventure game in later sessions. | **Lesson objectives** | | | --- | --- | | **Skills and concepts** | | | **Prep** | | | **Materials** | | ### Overview | Duration | Activity | Description | | --- | --- | --- | | 5 min | Introduction | Introduce the project and lesson structure. | | 5 min | Guided Tutorial: Roblox Studio Basics | Teach the basics of using Roblox Studio while building a simple obstacle course. | | 15 min | Guided Tutorial: Guided Tutorial | Introduce coding by creating a script that displays text. | | 5 min | Wrap-up | Reflect on project and recap concepts learned. | ### Lesson plan #### Introduction 1. Grab student attention by playing an example of the finished [adventure game](https://www.roblox.com/games/9544298367/). This is an example of the type of project they'll have by the end of the course. - Explain the game: Players explore a world to collect items and upgrade their backpack. - Call out that students will create and customize the game to their own vision (e.g. collecting cupcakes in a fantasy world or harvesting iron on the moon). 2. Outline the sessions in the course - First two sessions focus on developing skills in game design and coding by making practice projects. This will include building an obstacle course. - Will spend rest of sessions developing an adventure game 3. Point out for this session or day, students will be learning the basics of Roblox Studio by building an obstacle course. - Note the obstacle course is practice, but the can include parts of it in their adventure game if desired. #### Guided tutorial - Roblox Studio basics 1. Lead students through the following tutorial: [Introduction to Roblox Studio](/docs/en-us/tutorials/first-experience.md) 2. As you teach, keep in mind the following: - Remind students to rotate their camera view to get an accurate view of their project. - Encourage students to use the Explorer, a foundational skill in Roblox Studio. #### Guided tutorial - Create scripts 1. Lead students through the [Create a script](/docs/en-us/tutorials/fundamentals/coding-1/create-a-script.md) tutorial. #### Wrap up 1. Recap what students have created and vocabulary learned: variable and string. ### Appendix #### Troubleshooting tips **General troubleshooting** - If parts fall or disappear when playtesting, remind students to turn Anchoring on for that part. - The most common errors for students at this age are improper capitalization and typos. Encourage students to independently check their work, rather than rely on teachers. #### Classroom management - Introduce students to "Ask 3 Before Me": a student needs to ask three peers before asking the instructor for help. - Keep in mind students have different prior experience with computers. Emphasize exact steps when teaching, such as when to double-click or right-click. - Keep your lessons on time by setting strict limits for accomplishing tasks, such as allowing only a minute to add a single part or to pick a color. To build troubleshooting skills, ask students leading questions rather than solving the issue for them as much as possible. For example: _What symbols need to be around a string? - A quotation marks._ ## 2- Coding fundamentals **Lesson Description**: Explore the coding concepts of functions, loops, and if/then statements with mini-projects that can be added to games like color changing platforms and traps. | **Lesson objectives** | | | --- | --- | | **Skills and concepts** | | ### Overview | Duration | Activity | Description | | --- | --- | --- | | 5 min | Introduction | Introduce the session. | | 20 min | Guided Tutorial: Working with Properties | Modify the color properties of parts using scripts. | | 35 min | Guided Tutorial: Coding a Trap | Code a trap using functions and if/then statements. | | 5 min | Wrap-up | Reflect on project and recap concepts learned. | ### Lesson plan #### Introduction 1. Explain today's session will be different mini-projects to develop skills in scripting - Important to understand these concepts since adventure game will have more scripts. - Point out these projects, like a trap, can be included in the adventure game project they'll start in Session 3. #### Guided tutorial - Work with properties 1. Lead students through the following tutorials: - [Object properties](/docs/en-us/tutorials/fundamentals/coding-1/object-properties.md) - [Parents and children](/docs/en-us/tutorials/fundamentals/coding-1/parents-and-children.md) 2. Encourage students to troubleshoot independently if they have an error. To help, have them try: - Redoing their last few steps. - Asking a peer for help. 3. As students write code, check that students write comments that accurately describe the code. - Not Specific: Runs code - Improved: Changes color of PracticePart #### Guided tutorial - Code a trap 1. Lead students through the following tutorials: - [Code a function](/docs/en-us/tutorials/fundamentals/coding-2/code-a-function.md) - [Intro to if statements](/docs/en-us/tutorials/fundamentals/coding-3/intro-to-if-statements.md) - [Traps with if statements](/docs/en-us/tutorials/fundamentals/coding-3/traps-with-if-statements.md) 2. When working with if statements, check that students indent code to look like code samples. This helps make the code more readable, which becomes important as scripts grow larger. #### Wrap-up 1. Recap what students have created and vocabulary: **property**, **loop**, and **function**. 2. Point out in the next session, students will start a new project where they make an adventure game. ### Appendix ### Troubleshooting and classroom tips - While coding, remind students to double-check their capitalization or ask a peer to check their work. Even one incorrect letter can cause an error in longer words like `FindFirstChildWhichIsA`. - Try the following below to help students develop troubleshooting skills. - Purposely make a mistake in your code and ask students to identify the error. This can be easily turned into a game. - Write a partial line of code and ask students to complete it. - Utilize 'Ask 3 Before Me' where students ask three peers before asking the teacher for help. It's important for students to become comfortable independently troubleshooting as they advance to more complex projects. ## 3. Adventure game part 1 **Lesson description**: After learning the basics of using Roblox Studio and scripting, students take their skills further by starting a more complex project, the adventure game. They'll plan out elements of their game, create a virtual world, and setup basic gameplay components like keeping track of player items. | **Lesson objectives** | | | --- | --- | | **Skills and concepts** | | | **Materials** | | ### Overview | Duration | Activity | Description | | --- | --- | --- | | 5 min | Introduction | Introduce the session. | | 10 min | Guided Work: Planning a Game | Create a game design document to plan out elements of the project. | | 10 min | Guided Work: Creating the World | Design an environment using terrain tools based off the previous vision document. | | 30 min | Guided Tutorial: Setting up The Game | Code scripts to keep track of player items and build the first item players will collect. | | 5 min | Wrap-up | Reflect on project and recap concepts learned. | ### Lesson plan #### Introduction 1. Explain that students will be using skills learned in previous sessions to build a game over the next three sessions. They will: - Plan out their unique world using a game design document. - Create a world in Roblox referencing their plans in the game design document. - Create and code items for players to gather in-game. #### Guided work - Plan a game 1. Note that students don't need access to computers at the start of this section. 2. Lead students through the lesson: [Create the Map](/docs/en-us/education/adventure-game-series/create-the-map.md), stopping before the section: Creating the Environment. 3. As students draw their starting area, keep in mind the following. - Areas drawn should be achievable in scale of what a student can do in a week - such as a house with a front lawn or a simple forest vs a detailed city block. This helps focus students and they can always add more when finished. - Drawings don't have to be complex - simple symbols like circles and squares are enough. #### Guided work - Create the world 1. Students should be at their computers for this section. 2. Lead students through the lesson: [Create the Map](/docs/en-us/education/adventure-game-series/create-the-map.md), starting at Creating the Environment - Briefly show students each terrain tool at once and give at most six minutes to create their starting areas. They can always add more later. #### Guided tutorial - Set up the game 1. Lead students through the following tutorials: [Coding the Leaderboard](/docs/en-us/education/adventure-game-series/code-the-leaderboard.md). #### Wrap-up 1. Recap what students have created and vocabulary: game mechanic and pre-production. 2. Optional: Have students to reflect on the mid-point of their sessions by asking one or more of the following questions: - One thing that was challenging but how they overcame it. - One skill you're looking to improve over the next two sessions (e.g. better at troubleshooting, making more interesting worlds, etc). - What was the most exciting thing you accomplished today. How did you do that and why was it exciting? ### Appendix #### Troubleshooting tips - The name of the `leaderstats` variable must be `"leaderstats"`. Without this, the script won't know to create a new leaderboard. - [Handout: Adventure Game Reference](../../assets/education/lesson-plans/adventureGameReferenceHandout.pdf) helps students keep track of variable names. This is especially useful if they replaced default variable names, like `"Gold"` with something of their own, like `"Rubies"`. - Remind students to build everything using parts, not by using the Toolbox. Using the Toolbox may introduce unexpected issues into their games. #### Classroom management - As students work on their project, help them keep in mind a reasonable scale of what they can accomplish by the end of the sessions. - If students have ambitious goals (_I want to build three different worlds in my game)_, remind them to focus on their goals for today's current session. Have them write down their additional ideas on the game design document. - Set strict expectations as to how much time students can spend working on their starting area. They can always continue in later sessions. #### Customize the lesson - Students can spend more time building out their starting area using the terrain tools or adding decorative parts. - If students are unclear what to add, ask questions about what objects they'd expect to find in that themed world (E.g. _If you were on a moonbase, what would you see? How can you build that using parts?_) - Students can add more than one type of item to harvest. Just remember that each item follows the same organization in the Explorer and has a BoolValue named `CanHarvest` set to true. ## 4 - Adventure game part 2 **Lesson description**: Students continue developing the adventure game. They'll create scripts for using tools, selling items, and upgrading their spaces. | **Lesson objectives** | | | --- | --- | | **Prep** | | ### Overview | Duration | Activity | Description | | --- | --- | --- | | 5 min | Introduction | Introduce the session. | | 50 min | Guided Tutorial: Scripting Game Mechanics | Have students create scripts for tools, selling items, and upgrading spaces. | | 5 min | Wrap-up | Reflect on project and recap concepts learned. | ### Lesson plan #### Introduction 1. Explain that students will be adding the core gameplay mechanics of the adventure game today: using a tool to collect items, selling items, and upgrading their spaces to collect more items. 2. Point out that this session will be heavy on guided tutorials. The next session will be more free-form, so students can work on their own or catch up. #### Guided tutorial - Script game mechanics 1. Make sure all students have access to the [starter tool](../../assets/education/adventure-game-series/starterTool.rbxm). 2. Lead students through the following tutorials: - [Collecting Items](/docs/en-us/education/adventure-game-series/collect-items.md) - [Selling Items](/docs/en-us/education/adventure-game-series/selling-items.md) - [Buying Upgrades](/docs/en-us/education/adventure-game-series/buying-upgrades.md) #### Wrap-up 1. Recap what students have created. 2. Optional - Ask students what feature they're excited to work on or add tomorrow as they finish their games. ### Appendix #### Troubleshooting and classroom tips **General troubleshooting** - While students code, leave up example scripts for students to reference. The Tool script is especially helpful as it has nested if statements. - Check that students are indenting their code to look like the code samples. This makes code more readable and reduces the possibility of errors. **Scripting tips** - Remind students to always add a comma between multiple parameters in a function, like in `sellItems(playerItems, playerGold)`. - In the Upgrade script, the order of functions matters. Make sure that the function `giveUpgrade()` is above `clickDetector.MouseClick`. **Facilitation tips** - To keep students on track, set a strict time limit for playtesting, like one minute. - Encourage students to remember a specific goal whenever playtesting, such as check if a tool harvests an item as intended. This helps them stay focused. #### Customize the lesson **Expand the lesson** - More than one item can be added to harvest, just remember that each item needs a BoolValue with `CanHarvest` set to true. - Surface GUI's, like the Upgrade Sign, can be customized. Encourage students to explore properties of TextLabels, like color and font in the properties of that TextLabel and SurfaceGui. ## 5 - Adventure game part 3 **Lesson description**: Take time to catch up on previous lessons and prepare the adventure game for others to play. Students should evaluate if their game meets the goals of being fun, challenging, and bug-free; redesigning or improving their game as needed. | **Lesson objectives** | | | --- | --- | | **Skills and concepts** | | ### Overview | Duration | Activity | Description | | --- | --- | --- | | 5 min | Introduction | Introduce the goals for student games and ability to catch up or take projects forward. | | 15 min | Guided Tutorial: Getting the Game Ready | Make minor changes to their game and take time to playtest with a peer. | | 30 min | Independent Work | Catch up on previous lessons, expand their game with improved environments, or more items to collect. | | 10 min | Wrap-up | Recap the course and share games. | ### Lesson plan #### Introduction 1. Explain that today is an opportunity to catch up or further develop their game. 2. Regardless of what students work, will want to evaluate and redesign their game to meet specific goals: - **Fun** - players enjoy playing, want to keep playing - **Challenging** - players have a challenge but aren't punished or overly frustrated - **Error and glitch free** - no script errors, unusual looking parts/visuals, etc. #### Guided tutorial - Get the game ready 1. Lead students through [Finishing the Project](/docs/en-us/education/adventure-game-series/finishing-the-project.md). - Playtesting is optional. Depending on your class, that time might be utilized for students to catch up on their projects. #### Independent work 1. Have students either catch up on lessons or further develop their final projects. - As students work, their actions should help them meet the goals for today (fun, challenging, error-free). 2. If students feel finished, you can encourage them to: - Add additional items or upgrades. - Use the terrain tools to expand and improve their environment. - Add decorative parts that fit their environments theme. - Have a friend play their game and get feedback. #### Wrap-up 1. Wrap up by leading a class discussion. Ask one of more of the below prompts. - One thing that was challenging but how they overcame it. - Something they're proud of in their game and why. - An example of how another student helped thi improve their game. 2. To showcase work, have students trade seats with a partner and let thi explore their world. --- title: "Code a story game" url: /docs/en-us/education/lesson-plans/story-games-lesson last_updated: 2026-06-29T19:34:00Z description: "Learn to code a story game as part of the Hour of Code™ initiative." --- # Code a story game **Lesson description**: Start your coding journey with Roblox while joining millions participating in the world-wide Hour of Code™ initiative. **ISTE standards**: Innovative Designer 4a, 4c, 4d, Creative Communicator 6b, 6d | **Lesson objectives** | | | --- | --- | | **Skills and concepts** | | | **Prep** | | | **Materials** | | ## Overview | Duration | Activity | Description | | --- | --- | --- | | 5 min | Introduction | Introduce the project and lesson structure. | | 5 min | Guided Work: Create Stories | Have students write their stories and create placeholders. | | 25 min | Guided Tutorial: Story Games | Lead students through a tutorial to build a story game. | | 10 min | Independent Work: Finish Student Projects | Let students catch up or continue building stories. | | 5 min | Wrap-up | Recap the lesson and have student shares stories | ## Lesson plan ### Introduction 1. Play this [video](https://www.youtube.com/watch?v=KsOIlDT145A) about Hour of Code™, a nationwide initiative that introduces millions of students to one hour of computer programming. 2. Tell students they will be building a game using Roblox Studio, the tool used to create all Roblox experiences. Students will write a story and then use user's answers to replace words and names. For example: One day [name] woke up and ate a giant [user's favorite food]. 3. Play the project overview video to show students the experience they will create and build excitement. ### Guided work - Create stories 1. Provide students paper and pencil to write their stories. 2. Lead students through [Brainstorm your story](/docs/en-us/tutorials/curriculums/coding/get-started.md#brainstorm-your-story). - Have students do this activity away from computers to reduce distractions. - Brainstorming Tip: Have students generate ideas by having one suggest a character (wizard, chef, etc), and another suggesting what that character does. ### Guided tutorial - Story games 1. Lead students through the tutorials starting at [Open template](/docs/en-us/tutorials/curriculums/coding/get-started.md#open-template) and ending with [Customize strings](/docs/en-us/tutorials/curriculums/coding/customize-strings.md). ### Independent work - Finish student projects 1. Show students the tips and tricks from [Customize strings](/docs/en-us/tutorials/curriculums/coding/customize-strings.md). 2. Have students catch up on their projects. If finished, add the following challenges, have them add two or more characters (each using a different variable: name2, name3). ### Wrap up 1. Recap what students have created and vocabulary: variable, string, concatenation. 2. Encourage 2 or 3 students to verbally share the story they wrote as a class. For example, one student reads out the story and then calls on another student to provide an answer. ## Appendix ### Troubleshooting tips **General troubleshooting tips** - Introduce students to "Ask 3 Before Me": a student needs to ask three peers before asking the instructor for help. - Have neighbors test each others work. As they test, encourage students to look out for common errors like missing spaces in concatenation. **Lesson 1: Create variables** - To help students better find the Story Manger script, draw the order on a display board: `StarterGUI` > `GameGUI` > `StoryManager`. - As students type in their first variable, make sure it's between the two dashed green lines. This will help avoid future errors. **Lesson 2: Get player answers** - Stress strict capitalization and spelling as students type words like `storyMaker` and `GetInput`. Having one letter misspelled or incorrectly capitalized will cause an error. - Remind students to press the Stop button whenever they're done playtesting. **Lesson 3: Tell the story** - As students type out sentences, they may notice extra spaces or some words combined together. String variables show literally everything between quotations. - If the story writes words that are combined like, "wizardNia", add an extra space before that variable. ### Customize the lesson **Simplify the lesson** - Have students focus on just writing one question, one sentence, and one variable. **Expand the lesson** - Have students include at least one technique from [Tips and Tricks](/docs/en-us/education/build-it-play-it-story-games/finish-and-add.md). - Allow students to trade places to play each other experiences when finished. ### Miscellaneous resources Below are questions to check for understanding in the content. | Lesson 1 questions | Answer | | --- | --- | | A variable is ... | A placeholder for information | | What does scripts hold? | Code to run programs | | Name an example of a good variable name: | `adjective03`, `name3`, `myVerb1` | | Where in the script does the code need to be typed? | Between the two green dashed lines | | Lesson 2 questions | Answer | | --- | --- | | Variables can hold more than one type of information. True or false? | True | | String type variables are sandwiched between what? | Quotation marks | | What can and cannot have spaces between them in your code? | String contents can have spaces (like "`My name`"), variables cannot (like `name1`) | | Lesson 3 questions | Answer | | --- | --- | | What does concatenation do and how? | Combines two or more strings/variables together using `..` | **Course description** Discover how to build the ultimate interactive word game using Roblox's free coding and design tools. In this unique one-hour course, students will be able to explore the basics of computer programming and game design, then share their completed experience online with friends to craft their own fun stories! Created specifically for students new to programming, this curriculum was developed in partnership between Roblox, the largest entertainment platform for user-generated 3D experiences, and Hour of Code™, a global movement that aims to broaden participation in computer science. In this course, your student will: - Learn and develop programming fundamentals with Luau. - Use programming to create a story game in a 3D world. - Take home a complete experience that can be played and shared online. --- title: "Best practices for the classroom" url: /docs/en-us/education/resources/classroom-best-practices last_updated: 2026-06-29T19:34:00Z description: "For teachers, best practices for running Roblox in a classroom. Includes tips to make teaching with Roblox fun and easy." --- # Best practices for the classroom While every classroom is unique, we've included the following practices designed by teachers that can be easily integrated into an existing class culture. ## Prepare for class **Go Through Projects and Learning Experiences** Before classes, we always recommend playing a learning experience or going through the steps of a project. **Develop Classroom Guidelines with Students** Before starting a Roblox classroom, create guidelines like the ones below for students up-front. Encourage students to add on with their own suggestions. Examples of student expectations: - **Solve problems together** - Assume the best of each other. Share information. Try to solve a conflict without teacher intervention. - **Use Roblox Wisely** - Remind students that while in class, Roblox is an educational tool. They can always play Roblox outside of class. Additionally, identify consequences for students who break any guidelines. ## Manage a classroom **Helping with Transitions** For instance, we recommend the following system. Depending on the classroom, you can signal this with visuals, like colored flags, or verbally call out the "type" of time. For instance, we recommend the following system: | **I Do** | Students **observe**your instruction and do not interact with devices. If using laptops, students close them or keep a mobile device face down. | | --- | --- | | **We Do** | Students **follow**along with instruction. If finished with the task, they can indicate that by closing their laptop halfway or something similar.. | | **You Do** | Students work independently on their projects to complete prompts or implement a new concept. | **Help Students Help Themselves** During class, students will have questions whether it's technical or related to a project. Other more experienced or motivated students can be a great source of assistance. For instance, the practices below have been successful for teachers: - **Ask Three Before Me** - At the start of classes, have students ask three peers before an adult. - **Nominate Student Helpers** - At the start of class, pick 3-5 students to assist others. Depending on a class, you can use this as a reward for motivated students. Remember to rotate the students serving as helpers to ensure others have a chance to show leadership and that students continue to work on their own projects. **Handling Questions about Roblox** It's likely that students will ask questions unrelated to learning objectives, such as your "favorite Roblox game," or "Can you give me free Robux?". To preempt these questions, point out at the start of class that you'll be addressing questions related to the project or experience at hand. ## Code and creation tips **Celebrate Small Wins to Motivate Students** Using Roblox Studio can be a rewarding, although occasionally challenging process. While leading a project, try and include occasional "warm-up" or more relaxed challenges. For instance, if students are spending a whole class session coding, give them a few minutes towards the end of class to use Terrain Tools, a fairly relaxed and creative activity. --- title: "Coding concept - Abstraction" url: /docs/en-us/education/resources/coding-concept-abstraction last_updated: 2026-06-29T19:34:00Z description: "Learn high level coding concepts, such as abstractions and functions." --- # Coding concept - Abstraction | **Learning Objectives** | Students will be able to: | | --- | --- | | **Prerequisites** | Students should: | **Abstractions** in computer science provide a simplified representation of something larger. They pull out only the most necessary information and hide everything else. **Functions** are reusable abstractions. When called, users get the benefits of the function without having to rewrite or even look at the code for the entire function. A common example in coding languages is `print()`. Most of its code is hidden, so the coder can focus on what needs to be printed and not the rest of the code. ## Why create abstractions Abstractions keeps programs organized, reduces complexity, and makes code easier to update. ### Shop example Say that you have an in-game shop that sells just two different backpacks. The code for the second backpack was copied with slight changes, such as a different name and selling price. _Yellow Backpack - 10 Robux_ _Violet Backpack - 25 Robux_ Here, the code is **not** abstracted. Each backpack has a script of its own. What would happen if you tried to add the following? - 20 more backpacks. - The ability for some bags to hold more items than other bags. - A holiday sale, 25% off all backpacks. ## Design abstractions Having separate backpack scripts makes adding and updating backpacks time consuming. Instead, create an abstraction so you don't have to make updates in so many different places. To design an abstraction decide: - Which parts of the code will be reused. - What elements will be different each time. The abstraction should pull out the information that changes, and hide the rest. In the backpack example, the differences are the backpack's name, price, and number of items it can carry. So an example of an abstraction, you can design is a function that takes in the backpack's name and returns it's price and capacity. | | | | --- | --- | | **No Abstraction** | **Abstraction** | | Four different backpacks, four different places to update. | Use a function to search a table for unique information. Only one place to update. | > **Info:****Check for Understanding - Using Abstractions** What are other game mechanics you can create abstractions for? > > Possible Examples - Trap parts - Allowing for different harm values - Creating a function for awarding random power-ups - Creating character classes ## Conclusion **Abstractions** provide a simplified representation of something larger by leaving out details. When deciding whether to create an abstraction, look for code that is often reused but with small changes each time. For example, a generic item like a backpack can be abstracted to a reusable function that looks up price and capacity. Taking time to plan and structure code with abstractions helps coders focus on what's important. This investment in time keeps programs better organized and makes updating them easier. > **Info:****Examples of Abstractions in Roblox Studio Templates** Galactic Speedway - The Settings script has only the variables for unique features of the driftspeeder. The rest of the code is hidden. This allows students new to coding to stay focused without getting lost. > > Story Game Creator Challenge - Students call `getInput()` from the StoryMaker module script to create questions for players to answer. Students can practice typing strings and ask as many questions as they like, but don't need to see the code in the StoryMaker module. --- title: "Coding concept - Algorithms" url: /docs/en-us/education/resources/coding-concept-algorithms last_updated: 2026-06-29T19:34:00Z description: "Learn about algorithms and creating your own practical applications." --- # Coding concept - Algorithms ## Algorithms in real life and in code An **algorithm** uses a series of steps to determine an outcome. Steps can be a set of directions, comparisons, or even a mathematical formula. Your daily life is full of algorithms that you use without thinking about it. They help you make decisions, create things, and solve problems. **Real life examples:** - Checking if you have enough money to buy a snack. - Getting dressed in the morning. - Drawing stick figures. **Coding examples:** - Checking if a skill level is high enough to use an item. - Sorting a list of items into alphabetical order. - A player standing on lava loses 5 health every second. If they reach 0, they respawn. > **Success:****Offline Activity: Stick Figure Algorithms** Stick figures are something that everyone knows how to draw, but rarely think about. This is because we all have an internal stick figure algorithm. - Have everyone draw a stick figure. - Have everyone write down the steps they took. - Pair up students and have one student direct the second student using the directions they wrote down previously. ## Create algorithms in code In real life, we don't usually think about the algorithms we use everyday. Computers however, need algorithms to be coded step-by-step and use at least one of three methods to solve a problem or produce an outcome. | Methodology | Example | | --- | --- | | **Selection** - Uses conditional statements such as if/then to determine an output. | ```lua if time == 0 then stopLightColor = red end ``` | | **Sequencing** - A set of precise steps. | ```lua local function createBridge() create new block size block set color rotate set location parent to workspace end ``` | | **Iteration** - Repeats parts of the code as necessary, such as in for loops or multiplication. | ```lua for countDown = 10, 1, -1 do time -= 1 task.wait(1) end ``` | ## Combine algorithms Much like larger problems can be broken into smaller problems, some algorithms can be broken down into a series of smaller algorithms. Think of your real-life morning time algorithm to get dressed. If that's your main algorithm, it might use one algorithm for picking out your clothes, and a second algorithm for putting clothes on. ```lua -- First algorithm for picking clothes local function pickClothes() pick top clothing pick bottom clothing pick Socks pick Shoes end -- Second algorithm for putting clothes on local function putOnClothes() put on top clothing put on bottom clothing put on socks put on shoes end -- Main algorithm, calls pickClothes()and putOnClothes() local function getDressed() pickClothes() putOnClothes() end ``` > **Success:****Challenge - Divide pickClothes() Into Additional Algorithms** What are additional algorithms pickClothes() might use? - Check the weather. - Check if clothes are clean. - Check if clothes match. > **Info:****Discussion - Algorithms for Game Mechanics** What additional algorithms might be used by the main algorithm of the following game mechanics? - A timed bridge. - A shop that sells items. - An item that gives a random powerup. ## Summary **Algorithms** are predefined steps that provide an outcome. In daily life, algorithms solve problems like getting dressed, going to work, or making a cake. In code, algorithms solve problems like managing websites, handling traffic congestion, or running game mechanics. To complete their goal, algorithms will often call on other algorithms. Algorithms use three different ways to come to a conclusion; **selection**, **iteration**, and **sequencing**. Selection uses conditionals such as if/then statements. Iteration repeats parts of the code as necessary. Sequencing uses a series of steps to produce a result. --- title: "Best practices for collaboration" url: /docs/en-us/education/resources/collaboration-best-practices last_updated: 2026-06-29T19:34:00Z description: "Learn best practices for creating classrooms that foster collaboration and education." --- # Best practices for collaboration Educators in both online and in-person classrooms find that using Roblox to collaborate in classrooms can be meaningful as well as educational. Based on the experiences of Roblox educators, here are some ways of fostering a collaborative atmosphere by setting clear expectations and using supportive strategies. ## Set classroom expectations ### Example guidelines Each class is different, but to help you get started, we've included these guidelines commonly used by our educators. - **Build your own, don't copy**. If you do want to use someone's work, ask permission. - **Respect the work of others**. Never vandalize or put down the work of others. - **Solve problems together**. Assume the best of each other. Try and solve a conflict without teacher intervention. To help out your classroom, we have a downloadable .DOCX handout with these above guidelines. It can also be edited to suit your classroom. ### Resolve conflict It's recommended to train students to be able to identify and resolve conflicts as they emerge. Help students independently resolve conflict, by modeling and teaching them the tips below. - Encourage "I" messages ("I feel …") rather than "you" messages ("You did …"). This helps both sides listen. - Have students take turns speaking without interruption. If a student wants to interrupt, prompt them to take a deep breath or do a small private hand gesture (e.g. tap themselves on the thumb) to help them personally re-focus. - When trying to find a resolution to a conflict, talk about interests and goals ("I feel this would look better blue"), rather than positions ("You're wrong, blue is the best color …"). Sharing interests and goals gives students something concrete to try resolving conflict. ## Collaboration strategies One way of creating a positive learning environment is to use strategies that foster collaboration. Below are strategies to meet specific goals you may have. ### Students helping one another Strategies that encourage students to work together build independence and make collaboration feel more natural. **Help Students Help Themselves** - If students have a question, have them ask three peers before an instructor in a process often called "Ask Three Before Me". During a class, remind students of this rule whenever they're asking you for help. **Reward Positive Contributions** - Allow and motivate students to contribute by letting them lead **skill-shares**. If a student discovers something fun, or has a skill they want to share, give them 5-10 minutes during an independent work time to lead a tutorial. Allow other students to optionally "attend" that student's skill-share. This encourages an atmosphere of sharing and rewards students for wanting to help others. ### Facilitate groups - One way of having collaboration is often through group work. To ensure group work is productive and meaningful, we recommend the following strategies. **Assigning Roles** - Depending on the project, have students take on roles (either chosen or assigned). For instance, a team building a game may include a world builder and a coder. If you assign roles, be sure to rotate them so students learn new skills. They also might discover something they didn't expect to enjoy. **Having Groups Manage a "To-Do" List** - To ensure all group members are busy, have a group collectively manage a to-do list. If someone has an idea or task, they can add it onto the list for someone who needs something to work on. ## Further reading We've compiled a list of external resources on the topic of collaboration in the classroom. - [Colleen Lewis: Tips for Pair Programming](https://www.youtube.com/watch?list=PLHqz-wcqDQIEMMuXadwy90YxN2Qb4SrXT&time_continue=5&v=TWj78n4ZuMY&feature=emb_logo) --- title: "Frequently asked questions" url: /docs/en-us/education/resources/frequently-asked-questions-education last_updated: 2026-06-29T19:34:00Z description: "Common questions for educators, such as hardware requirements, costs associated, and suggested age ranges." --- # Frequently asked questions ## General Roblox information Below is information relevant to any educational work in Roblox. ### What is Roblox? To start, Roblox is not a single game. It's a **platform** that hosts millions of user-generated experiences, such as historical roleplaying games or virtual labs to simulate physics experiments. Because of the diversity of content you'll find on Roblox, we use the term **experience** to refer to what you play on Roblox. Experiences are designed by Roblox users, who may be individual students or professional studios. ### Recommended ages and hardware Roblox is an **all ages** platform where everyone can join, socialize, learn, and play safely. Various experiences may be targeted at a specific age range or grade level. For example, one math related experience might target fifth grade arithmetic while another might target high school robotics. Roblox can also be used for creation. Roblox Studio is used for building and coding all Roblox experiences. Our team typically recommends it for ages 10 and up, although some students may certainly start younger. Younger students typically focus on the creation and design of environments, such as recreating historical landmarks, while older students who are comfortable reading and typing can begin learning to code with the language Luau and built-in IDE. #### Hardware requirements for play and game-based learning Below are requirements for just visiting a Roblox experience. - A **PC** or **Mac** computer. Alternatively, Roblox is also playable on a Chromebook, tablet, or mobile device. - **Consistent Internet Access**. All experiences on Roblox are stored online, requiring an active connection. - For the best performance, we recommend either a computer less than five years old with a dedicated video card, or a laptop less than three years old with an integrated video card. #### Hardware requirements for creation Below are requirements if students are interested in using Roblox Studio to develop their own experiences. - A **PC** or **Mac** computer. Roblox Studio cannot run on Chromebooks or mobile devices such as smartphones. - **Consistent Internet access.** This allows us to keep the software up to date and gives you the opportunity to save your projects to your Roblox account. - A **free Roblox account**. Roblox Studio is free to use and does not require a license. - A **mouse** with a scroll wheel. While Roblox Studio is usable with a trackpad, having a mouse makes it easier. ### Costs and educational pricing Using and developing experiences on Roblox are all **free**! | **Curriculum** | Our [curriculum](/docs/en-us/education/support/education-content.md)is written and shared under a Creative Commons license to be used and adapted for the classroom or larger educational institutions. | | --- | --- | | **Creating Experiences** | Developing experiences in Roblox is free and doesn't require any paid licenses. | | **Visiting Experiences** | Most Roblox experiences are free to visit, but some may include microtransactions to purchase optional benefits, like private servers. These prices are set by individual developers, not by Roblox. When present, these costs are often minor, such as $1 USD per month for a private server. | ### Robux and digital currencies **Robux** is the currency used among all Roblox experiences. This currency can be bought, such as through gift cards, or earned by making experiences. Robux can be used to customize a student's avatar or be spent in Roblox experiences, such as buying special items. Additionally, remember there is no such thing as free Robux. Students may occasionally find bad actors and sites promising this. To help students better identify inaccurate information, we've developed a lesson on [Digital Civility](/docs/en-us/education/resources/intro-to-digital-civility.md). ### Roblox and Minecraft While similar at first glance, Roblox is very different. With Minecraft, students are only making changes to an existing game. Roblox is designed from the ground up to empower students to code, create, and share their own unique creations. ## Create in Roblox Using Roblox Studio, students can create their own experiences, practice computer science, and develop a variety of career skills such as project-planning. ### Roblox Studio vs Roblox Client Whenever Roblox is installed on any computer, two pieces of software are included. **Roblox Studio** is used to design and code experiences, while **Roblox Client** is for visiting experiences. ### Code in Roblox Studio Roblox Studio uses a typed-out scripting language called **Luau**. It's best described as similar to Python, but even easier to teach since Luau doesn't require strict adherence to indentations and other syntax that create hurdles for new learners. Roblox Studio does not currently offer drag-and-drop coding, like the type found in Scratch. ### Installation and classroom networks **Roblox** can be installed once and is updated automatically - no need to worry about versions. When installing Roblox, you install both Roblox Client (used to play experiences), and Roblox Studio (used for developing). For **Roblox Studio** to run, it needs access to the internet. This ensures that the software is up to date and you get access to a variety of features, such as storing your files online. Depending on your network, you may have connection issues. For support, check out our Technical Support pages on [General Connection Problems](https://en.help.roblox.com/hc/en-us/articles/203312880). We offer tools to create installers that can be used with software deployment and imaging tools. See [Creating Bundled Installers](/docs/en-us/education/support/creating-bundled-installers.md). ## Play and learn in Roblox Below are common questions we've received from educators using Roblox in the classroom. ### Safety and privacy Roblox as a company prides itself on the variety of ways we keep our community safe. Our [Parent's Guide](https://corp.roblox.com/parents/) is constantly being updated with new information and resources to help educate parents about the Roblox platform. ### Block chat in Roblox While in **Roblox Studio**, students cannot chat with others online unless building in a [collaborative](/docs/en-us/projects/collaboration.md) session. For **visiting Roblox** experiences, teachers can block Chat by following the steps on [Safety Features and Chat](https://en.help.roblox.com/hc/en-us/articles/203313120-Safety-Features-Chat-Privacy-Filtering). Additionally, some experiences offer the ability to rent private [ servers](/docs/en-us/education/support/private-servers-for-classroom-use.md) for free or a small fee. ### Disable Roblox Client Roblox Studio and Client are two separate pieces of software. While Studio is used to create and develop, Client is used to visit Roblox experiences. To use Roblox Studio, Client does not actually have to be installed. ## Market with Roblox Roblox is committed to the success of our educators. Below we provide some resources that may be helpful to your own program. ### Roblox Featured Educator Program This program picks some of our top educators and highlights them. We often send out a notice to members of our mailing list. You can sign up at this [survey](https://roblox.qualtrics.com/jfe/form/SV_3wmxTflT51MQDPM) ### Logos and marketing guidelines No special permissions are needed to use the Roblox logo on your materials so long as the [Roblox Terms of Use](https://en.help.roblox.com/hc/en-us/articles/115004647846-Roblox-Terms-of-Use) are followed. Educators are free to use the Roblox logo to advertise courses featuring our software so long as it isn't implied to be an official Roblox offering nor directly affiliated. . Please make sure to use our current logos and to type Roblox, **not** ROBLOX in all external communications. You can find our current logos on our [Marketing Materials](/docs/en-us/education/resources/marketing-materials.md) page. When downloaded, you'll also find our brand standards. **Do not alter the logo** in any way, including its color, transparency, drop-shadow, etc. --- title: "Get started for developers" url: /docs/en-us/education/resources/getting-started-for-developers-education last_updated: 2026-06-29T19:34:00Z description: "Learn how to develop educational experiences and games on Roblox." --- # Get started for developers Roblox is a global platform where millions of people gather together every day to imagine, create, and share experiences with each other in immersive, user-generated, 3D worlds. The Roblox platform and its community offer a compelling, unique destination for engaging students through learning and play. This article outlines resources catered specifically for developers interested in education. Learn about Roblox as a development platform, find what works, and view resources to jumpstart development. > **Info:** If you are a school, educator, or partner interested in working with Roblox Education, we advise starting at the [Education website](https://education.roblox.com/) ## Education on Roblox Roblox experiences transport students through space and time into virtual worlds designed for exploration, investigation, and experimentation. Students can witness and dissect scientific phenomena, see what it was like to live in ancient Rome, or even build virtual robots. This section helps developers understand what they can design on Roblox. See the [Education Partners](https://education.roblox.com/partners/) page to learn more about leveraging the Roblox platform for educational purposes. ### Inspiration for developers Designers looking for ideas for educational experience can find a variety of topics and example mechanics in live experiences to explore on Roblox within the Inspiration Guide. #### Example educational experiences Below are some examples of educational experiences highlighting the wide range of educational content developed on and available on Roblox. ![Person with Mars Rover](../../assets/education/developer/thumbnail-missionMars.png) [

Mission: Mars

](https://www.roblox.com/games/10840095864)Engineer a rover to withstand challenges on the harsh martian landscape. Designed by the renowned Boston Museum of Science, this experience fosters STEM and design thinking. ![Logo of Lua Learning](../../assets/education/developer/thumbnail-luaLearning.png) [

Lua Learning

](https://www.roblox.com/games/1334669864/)Learn computational thinking, and Luau coding within Roblox. Users can complete interactive courses and even find user-created lessons on advanced technical concepts such as network security. ![alt](../../assets/education/developer/thumbnail-untamedAnimals.png) [

Untamed Animals

](https://www.roblox.com/games/5716123942)Become a park ranger, or roleplay as an animal as you explore nature and engage in an active ecosystem. This experience has users building empathy for wildlife through gameplay. ## Develop on Roblox All Roblox experiences are developed in Roblox Studio, a professional development engine similar to Unity or Unreal. | [Learn About Roblox Studio](/docs/en-us/platform.md) | Learn how Roblox offers an all-in-one tool to develop global, multiplayer experiences for free. | | --- | --- | | [Start Using Roblox Studio](/docs/en-us/tutorials/first-experience.md) | Begin with the basics of building a world in Roblox Studio. | | [Developing on Roblox](/docs/en-us/creation.md) | Discover more in-depth topics on development, such as best practices for setting up teams,visuals and optimization, and monetization on Roblox. | ## Design for education To assist developers interested in learning, Roblox provides resources specific to educational game design. These articles cover topics such as integrating gameplay with learning, meeting student user needs, and more. | [Designing Educational Experiences](/docs/en-us/education/developer/designing-educational-experiences.md) | Introduces designing for educational games. Focuses on define educational experiences and integrate gameplay with learning. | | --- | --- | | [Planning for Educational Settings](/docs/en-us/education/developer/planning-for-educational-settings.md) | Provides a primer for anyone interested in building an experience to be used in the classroom. This article includes explanations of classroom structures, user needs, and resources for assessing student progress. | | [Designing Studio Templates](/docs/en-us/education/support/designing-studio-templates.md) | For partners interested in teaching Roblox Studio, this article covers how to build custom templates for lessons such as computer science or building. | ## Resources and opportunities In addition to documentation, Roblox Education has different programs that kickstart the development of educational projects. | Roblox Community Fund | The fund provides investments, grants, and advances for education partners who develop curriculum and educational experiences that leverage the platform in immersive and compelling ways. For more information, [Contact Us](https://corp.roblox.com/partners/). | | --- | --- | | Education Open Office Hours | Meet one-on-one with a member of the Roblox Education team for free assistance on a specific topic. The team can assist with developing an educational idea, assessing educational value, and discuss other issues related to development. [Apply](https://roblox.qualtrics.com/jfe/form/SV_9QPfsGGqLHRYGMe?jfefe=new) to participate. | | Learn and Explore Sort | Many Roblox users interested in education find new experiences in the curated **Learn and Explore** sort. Experiences here can expect more significant traffic and visibility. [Apply](https://roblox.qualtrics.com/jfe/form/SV_8313iVKrwkQUiGy) for consideration. | ## Frequently asked questions **Can I contract Roblox to develop a project?** Roblox doesn't actually develop experiences. Every experience on the platform is created by the Roblox community. If development assistance is needed, explore the [Talent Hub](https://talent.roblox.com/) as a means of finding potential developers, coders, and creators. **How do educational experiences monetize?** Developers have a variety of options for monetizing an educational experience. Traditional monetization still applies for developers interested in pursuing informal learning, such as "edutainment" experiences. For developers interested in creating for schools, note that monetization is prohibited during school hours. Such developers can pursue alternatives to traditional monetization, such as offering teacher training and lesson resources outside of the Roblox platform.
--- title: "Intro to digital civility" url: /docs/en-us/education/resources/intro-to-digital-civility last_updated: 2026-06-29T19:34:00Z description: "Digital Civility is a set of skills and behaviors that create positive online experiences." --- # Intro to digital civility | **Learning Objectives** | Comprehend how their online behavior both positively and negatively affects themselves and others. Demonstrate ways they can contribute to fostering a positive in-game community. Recognize cyberbullying or negative behavior and take actions to address it so others feel comfortable. | | --- | --- | Digital civility (or also known as digital citizenship) is a set of skills and behaviors that helps create positive online experiences. This field includes many topics, such as data, privacy and media literacy To start, this overview discusses online identities and communities. Practical tips are included to help educators foster positive online experiences. ## Digital identities Many of us create unique online identities. We might go online to pretend to be someone else, or enter a social network to talk about favorite games and movies. Regardless of who you are online, you're still accountable for your behaviors. Part of being a digital citizen is being **responsible** for your actions and helping others be **accountable** for their own. Your actions online have an impact on others and what people think of you. Being helpful and kind can spark friendship and foster a supportive community. Being rude might hurt someone, or encourage others to also act out. - **Define Yourself** - Don't feel pressured to conform to what's popular or what friends will like. Dress up as your own avatar, or share music you love on social media. You should likewise be supportive of the choices others are making. - **The "Real Me" Test** - If you have second thoughts about something, ask yourself if you would do or say this in person? How would you feel if someone said the same thing to you? Is it okay to bully someone just because it's a game, or to share something you know is fake on social media? Would it be alright to do so? **Discussion: Talking about Bullying** If you're using this lesson in the classroom, take time for this discussion on bullying. 1. Before starting a conversation about bullying, set a few ground rules to foster a safe discussion space (no interruptions, don't spread rumors, etc). 2. Have students reflect on real life bullying with these discussion questions. - Was there a time you were bullied online or in real life? How did it make you feel? Did it make you stressed, keep you from doing something you liked? - Outside Roblox, where are places online you've seen bullying, such as in a social network or game. How can you respond to that bullying? ## Online communities Roblox, as well as other social networks, are all great places to meet new people, hang out with friends, and play together. Part of what makes being online fun is friendly interactions, like helping someone. By being **positive** and **proactive**, everyone can contribute to an online community. ### Build positive culture Treat being online as you would real life. By being kind to others, they'll be kind in return. If everyone follows this standard, it creates a culture where kindness is the norm. - **Learn Online Etiquette** - Before joining a new online space, try and see if there are any rules you should follow. If there aren't any rules, see if you and some friends can make your own so everyone has a good time together. These may be rules like "no put-downs", or "stay relaxed - it's only a game". - **Find Ways to Participate** - Ask others for help on a quest, share gameplay tips, or strike up a conversation. - **Be Calm and Open-Minded** - Just like real life, disagreements can happen. Always treat others as you would in real life. Take a step away from your device if you feel stressed or wanting to say something rude. **Discussion: Opportunities to Practice** If you're using this lesson in the classroom, take time for this discussion on getting actionable steps to practice the ideas in the above section. For 1. Give students some time to think of an online community they are part of or would like to be (such as a game or social network). Some examples and ideas below: - In a Roblox game, ask if anyone is new to the game and needs some advice on getting started. Or, even start a conversation in chat like "What's your favorite part of this game?". - On social media, if someone is having a rough day, reach out to them. Alternatively, take time to celebrate someone's success. 2. Ask students what are some specific ways they can personally build a positive culture. ## Counter cyberbullying While cyberbullying does exist, you and others can personally take steps to address it. Instead of being a bystander, who observes an incident, be an **upstander** who takes action to reduce conflict. _Being an Upstander_ _Being a Bystander_ Some ways that you can practice being an upstander are below. - **Don't Engage** - Most people engaging in negative behavior online do so for fun, so don't give them the reaction they want. - **Support Those Affected** - If you noticed someone bullied, offer them support. Make some small talk, like talking about their favorite game. Don't talk about the person harassing them. It's better to redirect the conversation to something positive and move on, than focusing on a hurtful incident. - **Take Action and Report** - Take time to learn how to report bad behavior on the platform you're on. Reporting is quick, simple, and anonymous. For instance, you can learn how to report or block users in Roblox. Additionally, one unique type of cyberbullying you might encounter is griefing. **Griefing** is where one player actively tries to disrupt another's game or creative experiences. For instance, if you are collaboratively building in Roblox Studio, griefing can be when someone destroys another's work. It may also happen in a Roblox experience, where one player purposes stands in front of another to annoy or harass them. To counter this, set strict expectations upfront to students and continue to reinforce good citizenship. ## Activity - Upstander roleplay Help students be proactive in being upstanders by roleplaying how they'd react to cyberbullying. This handout includes different situations students may encounter and ideas for responses. --- title: "Intro to digital safety" url: /docs/en-us/education/resources/intro-to-digital-safety last_updated: 2026-06-29T19:34:01Z description: "Learn how to protect personal and private information." --- # Intro to digital safety Playing and learning with Roblox can be a great way of connecting with others and participating in a global community. By taking proactive measures to protect your privacy and understand how to spot unreliable information, you can prevent others from ruining the experience for you. ## Personal and private information Part of the joy of being online is sharing with others. Whether it's talking about favorite movies, or making memories in a virtual field trip. As you share, keep yourself safe from scams and other risks by being mindful of what you communicate to others. **General information**, like tips for how to play a game, is okay to share. Some examples below. - How you earned an avatar item. - Tips for getting better at a game. - What you like about a game. **Private information** that would let people online find you, like your home address, is best kept to yourself. Some examples below. - Name, age, gender identity, where you live. - Contact or social media information. - Game account information like password, email, or date of birth. To protect yourself, always learn about a platform's privacy practices and settings. On Roblox, don't share personal information, even if it's with a friend. Even simple information, like an email, can lead to someone finding your real life identity or give them hints as to how to get into your account. This can lead to events like having your identity and Robux stolen, someone impersonating you and saying rude things to friends, or selling off your items. ## Report bad actors When a negative situation happens, like seeing an abusive player or being the victim of a scam, you can take action with a report. Creating a report lets a platform, like Roblox, know that something needs to be investigated. By providing specific and clear information, you can solve personal account problems and help others in the community. To file a Roblox report within an experience: 1. In the top-left corner, click the Roblox button. A contextual menu displays. 2. Go to the **Report** tab, then follow the prompts to create the report. When finished, click the **Submit** button. > **Info:** Different social media websites, games, and other platforms have their own ways of making reports. For a trusted source to see how to make reports on a variety of websites, see [ReportHarmfulContent.com](https://reportharmfulcontent.com) . Note, while this website is focused on the United Kingdom, the information is applicable internationally. ### Write helpful reports As you create a report, make the description specific and clear. This helps Roblox staff respond more effectively to an issue. Notice the difference between the following reports. _"A player is being mean. Help!"_ - **Not Specific** - doesn't provide specific information people can use to take action. _"Snarly805 is messaging people about giving out free robux if they go to this website."_ - **Specific and Clear** - Mentions a specific user by account name and what they were doing. ## Reliable information online In addition to being mindful of your personal information, be watchful with what information you trust. While some sources, like the official Roblox website can be trusted, you may find games or websites looking to take advantage of you with scams. > **Warning:** Be cautious with private messages. It's okay to talk to people online but be careful with people you don't know, especially for people asking for suspicious favors. Scammers often will try and get you to click on links or chat on platforms outside Roblox, such as Instagram or Discord. ### Spot unreliable information Below are clues used to spot unreliable websites and information. **Clue 1:** Transactions deal with real life money or gifts like free Robux. Red flags should go up whenever you hear someone offering free Robux. A free gift or the promise of real life money is often used to entice players into scams. For instance, a player may promise to send you real life money for an in-game item, often seen in [Player Trading Scams](https://en.help.roblox.com/hc/en-us/articles/203312390-Player-Trading-Scams). And of course, there is no such thing as free Robux. For example, below are screenshots of real scam videos found on Youtube. Notice how they all reference free robux in the title and attempt to bait a viewer with images that prompt urgency, such as a "shocked" emoji. **Discussion: Scams in Real Life** If you're using this lesson in the classroom, take time for this discussion on spotting scams in real-life experiences. 1. Help your students make connections to real life by sparking a discussion with the following questions: - What types of scams have you seen online or in Roblox? What did it say and how did you feel it was a scam? - What kind of scams have you seen in emails or social media? What was your reaction? - You're friend shows you something that you think is a scam and may share it with others. How can you talk to them so they understand it's a scam? **Clue 2:** Prompts you for account or personal information. Roblox employees will never ask for your password or personal identifying information like your home address. Particularly don't trust any websites that ask you for your Roblox account information. You don't want people to be able to find you in real life, or steal your account. **Clue 3**: Makes claims without a source Use the Rule of 3: if you're not sure about something, see if you can find three other trusted sources, be it on a trusted website or adult. For instance, you may have heard Roblox is shutting down (it's not). But how would you check? **Clue 4:** Links to non-official websites. Links to other websites can often be bait for scams or other harmful activities. Sometimes, the scammers will use similar sounding names or common misspellings to catch people who aren't paying attention. Sometimes this might even be a link from a friend who's not sure it's a scam. These links could look like `rublux.com` or `r0blox.com`. If the link isn't from a trusted site, don't follow it no matter what it says. Even clicking or typing in links can expose you to risks. For example, the text below is a common scam. _i'm making a game and i wanna put your roblox character in it. could you upload your roblox char texture as a decal and send me the link? here's a tutorial on youtube if you don't know how to do it._ Users would go to Youtube and follow instructions where they'd type in a browser link which had malicious code. Scammers would then gain access to accounts. ## Activity - Investigate scams You can use real life scams to get your students to critically think about the information they see and apply the information they learn here. To challenge students, go through one of the external resources below. - [Adopt Me! Scams](https://adoptme.fandom.com/wiki/Scams) - [Scams in League of Legends (Youtube)](https://www.youtube.com/watch?v=iHJ49zbGOW8&) - [Fortnite Scams and More (Tulane University)](https://sopa.tulane.edu/blog/guide-to-protecting-young-online-gamers) Explain a situation and ask the following questions: - What would be your immediate response to this? - How would you know this is a scam? What clues are there? - If your friend was accidentally sending you a scam, how would you inform them about it so they can learn? ## Examples of information Below are examples of information you might find in a Roblox game. What makes one reliable and the other unreliable? **Example 1** _"If you give me your account info, I'll give you some free pets I don't need anymore."_ - **Unreliable** - Tells you to go to a website outside Roblox which may have exploits and hacks. Free Robux is being used to bait people. **Example 2** _If you buy a game pass in this game's shop for 200 Robux, we'll give you a free in-game pet each week._ - **Reliable** - While it is a transaction, it stays within Roblox. It also lets you know what's being traded and you know what you're getting in the end. ## Additional resources ### Protect accounts One way of protecting your account is to use 2 Factor Authentication (or sometimes called 2FA). This feature makes sure nobody else can log into your account, even if they know your password. Whenever you log in, you'll receive a unique code from Roblox in your email, giving you an extra layer of protection. Learn more in Add [2-Step Verification](https://en.help.roblox.com/hc/en-us/articles/212459863-Add-2-Step-Verification-to-Your-Account) to Your Account. ### Scavenger hunt game To help you identify reliable information, go through a scavenger hunt in a Roblox experience. As you go through the experience, use the four clues in Spotting Unreliable Information to identify potential risks and explain why. ### Supplemental materials - [Scavenger Hunt Handout](../../assets/education/intro-to-digital-safety/Digital_Safety_Scavenger_Hunt_Handout.docx) - A Powerpoint presentation covering the information on this page. - [Digital Safety Presentation](../../assets/education/intro-to-digital-safety/Intro_to_Digital_Safety_Lesson_Slides.pptx) - A Powerpoint presentation covering the information on this page. --- title: "Marketing materials" url: /docs/en-us/education/resources/marketing-materials last_updated: 2026-06-29T19:34:01Z description: "Marketing materials for educators using Roblox in the classroom. Includes high quality renders and logos." --- # Marketing materials For organizations looking to use Roblox in their branding materials, we've included high quality renders and logos. Note the download size is 500 MB. ## Branding guidelines In order to use the Roblox logo, you must follow these guidelines. --- title: "Classroom resources" url: /docs/en-us/education/resources/resources-landing last_updated: 2026-06-29T19:34:01Z description: "Resources to help you get started teaching with Roblox in the classroom." --- # Classroom resources Find resources to help teach with Roblox, whether it's handouts for the classroom to technical support in setting up school networks. > **Info:** We've curated our most popular resources, but the rest of the content can be found in the **left** navigation bar under **Educator Training**. ## Education resources Resources for running Roblox in classrooms and other settings. #### Classroom Best Practices Generalized tips for running any Roblox classroom. #### Frequently Asked Questions Hardware requirements, recommended ages, and more. #### Roblox Classroom Kit A .ZIP file with handouts and guides for setting up. ## Community resources Resources related to helping you communicate more about your Roblox educational offerings. #### Marketing Kit Official Roblox art, logos, and branding guidelines. #### Roblox Educators A list of educators and organizations using Roblox to teach. #### Private Servers Make private communities on Roblox for students. ## Technical resources | Resource | Description | | --- | --- | | [General Connection Problems](https://en.help.roblox.com/hc/en-us/articles/203312880) | Troubleshoot common issues like firewalls, connection loss, or issues in loading or connecting to experiences. | | [Troubleshooting Education Networks](https://en.help.roblox.com/hc/en-us/articles/115005744663) | For educators interested in using Roblox in schools. This guide includes domains, ports, and common issues we've found. | | [Firewall and Router Issues](https://en.help.roblox.com/hc/en-us/articles/203312840-Firewall-and-Router-Issues) | Issues with firewalls, routers, and antivirus programs. | | [General Roblox Studio Issues](https://en.help.roblox.com/hc/en-us/articles/203552894-General-Roblox-Studio-Issues) | Provides guidance on how to address common issues such as logging in, crashes, and visual glitches. | ## Contact us If you need assistance that isn't covered by one of the resources here, you can reach out to our Education team. --- title: "Roblox educators" url: /docs/en-us/education/resources/roblox-educators last_updated: 2026-06-29T19:34:01Z description: "A list of schools, summer camps, and online educators offering Roblox in education for students and parents." --- # Roblox educators Around the world, educators are using Roblox to teach coding, design thinking, entrepreneurship and inter-discipline learning. These organizations offer a variety of services, from one on one tutorship to online learning. Please see their websites for more information. > **Info:** All educators and external links on this page are provided as a service to our community by Roblox and don't intend to be an endorsement of their work. ## Featured educators Our Featured Educators represent the best that the Roblox Education community has to provide. They're selected on a yearly basis by our team for their exemplary work in using Roblox to educate and inspire others. | | iD Tech is an international educator offering students STEM education through camps, online tutoring, or online classes. | | --- | --- | | | Code Ninjas allows students between the ages of 7-14 to learn real programming skills in a fun, safe, and inspiring learning environment. | | | EDMO provides in-person and online camps, classes, and tutoring. Their mission is to make equitable, high quality STEAM and SEL programs accessible to all communities in order to cultivate curious, courageous, and kind humans everywhere. | | | Natalie Clabo is a 3D artist and modeler that produces educational videos on Youtube. She covers videos on 3D modeling in Roblox and Blender, taking a user-friendly approach to ensure her students learn easily. | | | Based in Colombia, Genius Academy is an extracurricular educational center that offers courses using Roblox Studio for Spanish-language learners. | | | Code Wiz is an outreach educational company that offers online and in-person coding classes for students ages 7-17. Their work is aligned with K–12 Computer Science Framework. | | | WGFactory, located in South Korea, provides Roblox workshops to students in elementary to university level. Their students produce online art galleries and historical recreations of Korean society in Roblox. | ## All educators We have many educators across the world teaching with Roblox. If you're interested in being added to this list, [contact us](https://roblox.qualtrics.com/jfe/form/SV_8oiiVEvjteux7hk). ### Online educators [Academia de Super Inventores](https://superinventores.com) [Algorithmics Online](https://algorithmics-online.com/product/roblox/) [AlvinBlox](https://www.alvinblox.com/) [ARTX Campus](https://www.artxcampus.co.kr) [Atolye Vizyon](https://www.atolyevizyon.com/) [BYJU's FutureSchool](https://www.byjusfutureschool.com/) [Brendon Ross](https://www.mrbrendonross.com/) [Caprikon Education](https://www.caprikon.education/) [CODDY](https://coddyschool.com/courses/sozdanie_igr_v_roblox_studio/) [Coder Sports](https://codersports.com) [CodaKid](https://codakid.com/roblox-coding/) [Code 4 Kidz Escola de Programação](https://code4kidz.com.br/) [Code Kingdoms](https://codekingdoms.com/roblox/) [Code.Up](https://www.codeup.pt) [Coding with Kids](https://www.codingwithkids.com/#!/camp?desc=RB&s=2019_SUMMER) [Computer Wise Kids](https://computerwisekids.com/) [ConMasFuturo](https://www.conmasfuturo.com/) [Connected Camps](https://connectedcamps.com/summer-camp) [Create & Learn](https://www.create-learn.us/) [Cria Jogo](https://criajogo.com/) [Curiosity and Learn](https://curiosityandlearn.com/) [DIGITANE](https://digitane.jp/) [D.LAB](https://www.daddyslab.com) [Deviac Education](https://www.udemy.com/user/deviac-education/) [Digital Media Academy](https://www.digitalmediaacademy.org) [EDMO](https://edmo.org/) [Empire Code](https://empirecode.co/) [Exclusive-It](https://excklusiveit.com/) [Fire Tech](https://www.firetechcamp.com/) [Game-U](https://www.game-u.com/) [Geekedu Coding for Kids](https://www.geekedu.org/) [Genius Academia](https://soygenius.com/) [Giganci Programowania](https://www.giganciprogramowania.edu.pl) [ICanCode Asia](https://www.icancodeasia.com) [impact Academies and Camps](https://impactacademies.com/) [Kiddy Coders](https://kiddycodersclub.com/) [Kids Innovative](https://www.kidsinnovative.com) [KidzToPros](https://www.kidztopros.com/) [Kodeclik](https://www.kodeclik.com/) [Kodecoon Academy](https://www.kodecoon.com/) [Koding Next](https://kodingnext.com/) [KodingAkademi](https://kodingakademi.id/) [KraoEsp](https://www.kraorobloxacademy.com/) [Krubots](https://krubots.com/) [Learn With Cat](https://learnwithcat.catblountstories.com/) [Lua Learning](https://twitter.com/LuaLearning) [MAKERSPLACE](https://makersplacegh.com/online-sessions/coding-games-in-roblox/) [Makers' Muse](https://www.makersmuse.in) [NaalAcademy](https://www.gnstartup.kr/naalacademy/board/f265a2df-2833-44ae-bbe1-b1320dc830b9/12a233ca-153a-4437-86fd-7249dbe1c0f1) [Outschool.com](https://outschool.com/online-classes?age&startAfter=AllUpcoming&q=roblox) [Ozon Academy](https://ozonacademy.ru/) [Popfizz Computer Science](https://popfizz.io/) [Program School](https://program-school.org/) [ROBOROBO](https://www.roborobo.co.kr/main) [Roblox Worlds in LUA](https://simplycoding.org/roblox-coding-for-kids/) [Skysmart](https://skysmart.ru/programmirovanie-dlya-detej?direction=basics) [Small Academy](https://small.academy/cursuri/programare-roblox/) [Smart Kiddo Education](https://www.smart-kiddo.com/) [STEM Work](https://www.stemwork.com.hk/) [Smart Kids Educational Club](https://www.smartkidsclubtt.com/) [TechKidz Africa's](https://techkidzafrica.co.ke/) [The MADE](https://themade.org/) [Thinklum](https://thinklum.com) [Timedoor Academy](https://timedooracademy.com/) [Udemy](https://www.udemy.com/topic/roblox-game-development/) [WGfactory](https://wgfactory.xyz/program/gamedev/) [WOW School](https://www.wowschool.pl/) [WhiteHat Jr.](https://www.whitehatjr.com/) [Zenva](https://academy.zenva.com/product/roblox-game-development-mini-degree/?zva_src=robloxedu) [digitalsake](https://digitalsake.com/) [funCoderZ Coding Classes](https://funcoderz.com) [iD Tech](https://www.idtech.com/roblox-summer-camps?utm_source=roblox&utm_medium=website&utm_campaign=2018&utm_content=roblox-site) [iKids Online](https://online.ikids.su/) [kidOYO](https://kidoyo.com/) ### Educators on Youtube [Brisimb17](https://www.youtube.com/watch?v=BSlGrDBLka4&list=PL14SLYIw8Yncstv8IrGNsiNBaN1Qj60Wr) [Disobeyedcrab](https://www.youtube.com/c/Disobeyedcrab) [Natalie Clabo](https://www.youtube.com/c/NatalieClabo) [SpooksHD](https://www.youtube.com/c/SpooksHD) [Virtual Escritory](https://www.youtube.com/channel/UCLkSx-YvsK7QwEsrPIj0trg) ### All countries #### United States [Academy of Technology Art and Music](https://www.atampalisades.com) [AccelerateKID](https://www.acceleratekid.com/camps/) [Allison Tutoring](https://www.allisontutoring.com/) [Anne Arundel Community College](https://webapps.aacc.edu/search/course/crs_desc.cfm?courseId=62002) [BYJU's FutureSchool](https://www.byjusfutureschool.com/) [Badger Bots](https://www.badgerbots.org/) [Black Rocket](https://blackrocket.com/courses/) [Blue Valley Recreation](https://www.bluevalleyrec.org) [Bucks County Community College](https://www.bucks.edu/) [C0deEX](https://www.c0deex.com/) [CODDY USA](https://ny.coddyschool.com/en/) [Cabrillo College](https://www.cabrillo.edu/) [Clinton Community College](https://www.eicc.edu/summercamps#ccc) [Club SciKidz Maryland](https://www.clubscikidzmd.com/summer-camps/beginner-coding-roblox/) [Club SciKidz Richmond](https://richmond.clubscikidz.com/summer-camps/coding-with-roblox/) [CodaKid](https://codakid.com/roblox-coding/) [Code Central](https://mycodecentral.com/portfolio-items/roblox-foundations/) [Code Ninjas](https://www.codeninjas.com/) [Code Rev](https://www.coderevkids.com/tech-camps/courses.php?gclid=EAIaIQobChMIqrLyrKGh2wIVSFSGCh1_SAu9EAAYASAAEgL9f_D_BwE#game_design_track) [Code Wiz](https://thecodewiz.com/roblox/) [Code-A-Robot](https://code-a-robot.com/) [Coder Kids Club](https://pongoslearninglab.com/) [Coder Kids Texas](https://www.coderkidstx.com/) [Coder Sports](https://codersports.com) [Coding Butterfly](https://cobu.teachable.com/p/game-design-roblox-studio-for-beginners) [Coding Crew](https://www.codingcrew.org/) [Coding Minds Academy](https://codingmindsacademy.com/roblox2.html) [Coding with Kids](https://www.codingwithkids.com/#!/camp?desc=RB&s=2019_SUMMER) [Community College Workforce Alliance](https://ccwa.augusoft.net.cfm?method=ClassInfo.ClassInformation&int_class_id=12334&int_category_id=0&int_sub_category_id=0&int_catalog_id=0) [Computer Wise Kids](https://computerwisekids.com/) [Connected Camps](https://connectedcamps.com/summer-camp) [Crafting Education](https://www.craftingeducation.org/roblox) [Daemen College](https://docs.google.com/forms/d/e/1FAIpQLSe216qstfdXv2GsgYxVMiTtabBGsxzWDSG-L0EUMyW5Tt0Pvw/viewform) [Delaware Valley University](https://www.delval.edu/) [Digital Adventures](https://www.digitaladventures.com/programs/classes) [Digital Dragon](https://digitaldragon.co/camps/thanksgiving-week-camp-roblox/) [Digital Media Academy](https://www.digitalmediaacademy.org) [Drobots Company](https://drobotscompany.com/technology-and-coding-stem-camps-kids-summer-camps-for-ages-8-to-13/) [Duke School](https://www.dukeschool.org) [EDMO](https://edmo.org/) [ESF Camps](https://www.esfcamps.com/georgetownprep/programs/tech-camps/) [Engineering for Kids](https://www.engineeringforkids.com/about/roblox-educational-partnership/) [Fab Lab Iowa City](https://steamiowacity.org) [Fire Tech](https://www.firetechcamp.com/) [Fleming Tech Camp](https://flemingcamps.com) [Full S.T.E.A.M. Ahead](https://fullsteamaheadfl.com/2019/01/15/roblox-studio-2/) [Game-U](https://www.game-u.com/) [Gaming4Good](https://www.loyaltyfoundation.org/) [Garrison Forest School Inc.](https://www.gfs.org) [Geekedu Coding for Kids](https://www.geekedu.org/) [Germantown Academy](https://www.germantownacademy.net/summer/summer-programs/coding-minecraft) [Harford Community College](https://www.harford.edu) [Hofstra Summer Camps](https://www.hofstra.edu/academics/ce/summer-camp/specialty_minecraft_club.html) [IDEAS Summer Camps](https://ideassummercamps.com/roblox-minecraft/) [Ithaca Media Arts](https://www.ithacamedia.org/services-view/full-half-day-camp-programs/) [Ivy Seed](https://www.ivy-seed.com/gamedesignsummercamp) [KIBERone](https://kiber1.com/main/) [Kellogg Community College](https://reg136.imperisoft.com/kellogg/ProgramDetail/3235313532/Registration.aspx) [Kiddy Coders](https://kiddycodersclub.com/) [Kids 'R' Kids](https://kidsrkids.com/south-riding/) [Kids That Code](https://www.kidsthatcode.org/) [Kids4Coding](https://www.kids4coding.com/) [Kirkwood Center for Lifelong Learning](https://kirkwood.augusoft.net.cfm?method=ClassInfo.ClassInformation&int_class_id=104322) [LFCC Workforce Solutions](https://lordfairfax.augusoft.net.cfm?method=ClassInfo.ClassInformation&int_class_id=52716&int_category_id=4&int_sub_category_id=14) [Lavner Summer Camp](https://www.lavnercampsandprograms.com/roblox-camp-philadelphia-pa-main-line-pa-nyc-manhattan-ny-nj-liny-dc-md-st-louis-nashville-tn-los-angeles-ca/) [Learn With Cat](https://learnwithcat.catblountstories.com/) [Maker-State](https://maker-state.com/product/creative-coding-with-minecraft-roblox/) [Mason Game & Technology Camp](https://vsgi.gmu.edu/) [McDonogh School](https://www.mcdonogh.org/community/parents/summer-camps/game-dev-coding-camps) [Mighty Coders](https://mightycoders.io/#programs) [Mind Crafters Robotics](https://mindcraftersrobotics.com/) [Mindframe Education](https://mindframeeducation.com/stem-summer-camps/) [Planet Bravo](https://www.planetbravo.com/camps/courses2018.php) [Project Scientist](https://projectscientist.org/) [R3Space](https://www.r3space.org/) [Randolph College](https://www.randolphcollege.edu/about/visit/summer-camps/tech-cats-day-coding-camp/) [S.T.E.A.M. Academy](https://steameducate.com/) [STEM Learning/Interactive Math Academy](https://www.imathacademy.com/game/) [San Ramon Valley Unified School District](https://www.srvusd.net/) [Scott Community College](https://www.eicc.edu/summercamps#ccc) [Skilstak Coding Arts](https://skilstak.io/) [StreetCode Academy](https://streetcode.org/) [T.A.G. Labs](https://www.taglabs.org/) [Tech Brainiacs](https://techbrainiacs.com/) [Techakids](https://www.techakids.com/) [Temple University](https://noncredit.temple.edu/publicViewHome.do?method=load) [Terrific Scientific](https://terrificscientificnc.org) [The MADE](https://themade.org/) [The Roeper School](https://www.roeper.org/) [Tidewater Community College](https://www.tcc.edu/) [TriCounty Technical College](https://cce.tctc.edu/wconnect/CourseStatus.awp?&course=191XDKC71202) [UNC Charlotte](https://continuinged.uncc.edu/) [University of Wisconsin Parkside](https://www.uwp.edu/learn/continuingeducation/youthprograms.cfm) [Waubonsee Community College](https://www.waubonsee.edu/) [Waxhaw Kid Coders](https://waxhawkidcoders.com.html) [Wiz Kid Learning](https://www.wizkidlearning.com/) [Wearable Tech Ventures](https://www.wearabletechventures.org) [Youth Camps at Edmonds Community College](https://www.campusce.net/edmondsarts/course/course.aspx?C=1152&pc=64&mc=65&sc=0) [Youth Tech, Inc.](https://www.youthtechinc.com/) [digitalsake](https://digitalsake.com/) [funCoderZ Coding Classes](https://funcoderz.com) [iCanCode](https://www.icancodeclub.com/camps) [iD Tech](https://www.idtech.com/roblox-summer-camps?utm_source=roblox&utm_medium=website&utm_campaign=2018&utm_content=roblox-site) [kidOYO](https://kidoyo.com/) #### Afghanistan [Black Rocket](https://blackrocket.com/courses/) #### Armenia [CODDY](https://coddyschool.com/courses/sozdanie_igr_v_roblox_studio/) [KIBERone Armenia](https://arm.kiber-one.com/modules.html) #### Australia [BYJU's FutureSchool](https://www.byjusfutureschool.com/) [Building Block Studio](https://www.buildingblockstudio.com/) [Camp Sonshine](https://campsonshineinternational.org/) [Fire Tech](https://www.firetechcamp.com/) [ICanCode Asia](https://www.icancodeasia.com) [Thinklum](https://thinklum.com) [Zenva](https://academy.zenva.com/product/roblox-game-development-mini-degree/?zva_src=robloxedu) #### Azerbaijan [CODDY](https://coddyschool.com/courses/sozdanie_igr_v_roblox_studio/) [impact Academies and Camps](https://impactacademiesaz.com/) #### Belarus [CODDY](https://coddyschool.com/courses/sozdanie_igr_v_roblox_studio/) [Exclusive-It](https://excklusiveit.com/) #### Belgium [impact Academies and Camps](https://impactacademies.be/en/liege) [Logiscool](https://www.logiscool.com/) #### Brazil [BYJU's FutureSchool](https://www.byjusfutureschool.com/) [Code 4 Kidz Escola de Programação](https://code4kidz.com.br/) [Supergeeks](https://supergeeks.com.br/cursos/curso-extra-roblox/) [Wearable Tech Ventures ](https://www.wearabletechventures.org) #### Bulgaria [Logiscool](https://www.logiscool.com/) #### Burundi [TechKidz Africa's](https://techkidzafrica.co.ke/) #### Canada [Algorithmics Canada](https://waterloo.alg.academy/) [BYJU's FutureSchool](https://www.byjusfutureschool.com/) [Black Rocket](https://blackrocket.com/courses/) [Code Ninjas](https://www.codeninjas.com/) [Coder Sports](https://codersports.com) [Creative Kids](https://www.creativekids.info) [DataWiz Academy](https://datawiz.ca/) [Digital Media Academy](https://www.digitalmediaacademy.org) [Geekedu Coding for Kids](https://www.geekedu.org/) [impact Academies and Camps](https://impactacademies.ca/) [Kids Innovative](https://www.kidsinnovative.com) [Logiscool](https://www.logiscool.com/) #### Colombia [Academia de Super Inventores](https://superinventores.com) [Genius Academia](https://soygenius.com/) [Logiscool](https://www.logiscool.com/) [digitalsake](https://digitalsake.com/) #### Costa Rica [digitalsake](https://digitalsake.com/) #### Cyprus [impact Academies and Camps](https://impactacademiescyprus.com/) #### Czech Republic [impact Academies and Camps](https://impactacademies.cz/) [KIBERone Czech Republic](https://kiber-one.cz/moduli/roblox-studio-sdelat-vse-chto-vy-mozhete-sebe-predstavit/) [Logiscool](https://www.logiscool.com/) #### Denmark [impact Academies and Camps](https://impactacademies.dk/copenhagen) #### Dominican Republic [Camp Sonshine](https://campsonshineinternational.org/) #### Estonia [CODDY](https://coddyschool.com/courses/sozdanie_igr_v_roblox_studio/) [DigisCool](https://www.digiscool.ee/roblox-est) #### Georgia [CODDY](https://coddyschool.com/courses/sozdanie_igr_v_roblox_studio/) #### Germany [CODDY](https://coddyschool.com/courses/sozdanie_igr_v_roblox_studio/) [impact Academies and Camps](https://impactacademies.de/) [KIBERone Germany](https://kiber-one.de/moduli/roblox-studio-sdelat-vse-chto-vy-mozhete-sebe-predstavit/) [Logiscool](https://www.logiscool.com/) #### Ghana [MAKERSPLACE](https://makersplacegh.com/online-sessions/coding-games-in-roblox/) #### Greece [Algorithmics Greece](https://greece.alg.academy/) #### Guatemala [Camp Sonshine](https://campsonshineinternational.org/) #### Hong Kong [931SMD SmartersMind](https://www.931smd.com/) [BTL](https://btl3d.com/roblox) [Cobo Academy](https://www.coboacademy.com/) [Caprikon Education](https://www.caprikon.education/) [Koding Kingdom](https://www.kodingkingdom.com/home/?page_id=25212) [RoboCode Academy](https://www.robocodeacademy.com/holiday-camps/) [Smart Kiddo Education](https://www.smart-kiddo.com/) [STEM Work](https://www.stemwork.com.hk/) [iD Tech](https://www.idtech.com/roblox-summer-camps?utm_source=roblox&utm_medium=website&utm_campaign=2018&utm_content=roblox-site) #### Hungary [Logiscool](https://www.logiscool.com/) #### India [Logiscool](https://www.logiscool.com/) [Makers' Muse ](https://www.makersmuse.in) [Wearable Tech Ventures ](https://www.wearabletechventures.org) [WhiteHat Jr.](https://www.whitehatjr.com/) #### Indonesia [BYJU's FutureSchool](https://www.byjusfutureschool.com/) [D-SCHOOL ONLINE](https://d-school.co/online/online/) [Timedoor Academy](https://timedooracademy.com/) [Koding Next](https://kodingnext.com/) [KodingAkademi](https://kodingakademi.id/) #### Ireland [impact Academies and Camps](https://impactacademiesie.com/) #### Israel [CODDY](https://coddyschool.com/courses/sozdanie_igr_v_roblox_studio/) [topEduGames](https://www.topedu.games/) #### Japan [Coder Sports](https://codersports.com) [DIGITANE](https://digitane.jp/) [Tento](https://www.tento-net.com) #### Kazakhstan [CODDY](https://coddyschool.com/courses/sozdanie_igr_v_roblox_studio/) [KIBERone Kazakhstan](https://kiber-one.kz/moduli/roblox-studio-sdelat-vse-chto-vy-mozhete-sebe-predstavit/) #### Kenya [TechKidz Africa's](https://techkidzafrica.co.ke/) #### Kyrgyzstan [CODDY](https://coddyschool.com/courses/sozdanie_igr_v_roblox_studio/) #### Latvia [CODDY](https://coddyschool.com/courses/sozdanie_igr_v_roblox_studio/) #### Malaysia [ICanCode Asia](https://www.icancodeasia.com) [Koding Next](https://kodingnext.com/) #### Mexico [BYJU's FutureSchool](https://www.byjusfutureschool.com/) [Camp Sonshine](https://campsonshineinternational.org/) [Kodotto](https://www.kodotto.com/) [Logiscool](https://www.logiscool.com/) #### Moldova [CODDY](https://coddyschool.com/courses/sozdanie_igr_v_roblox_studio/) [Small Academy](https://small.academy/cursuri/programare-roblox/) [impact Academies and Camps](https://impactacademies.com/) #### Montenegro [impact Academies and Camps](https://impactacademies.me/) #### Mozambique [Code.Up](https://www.codeup.pt) #### New Zealand [Bricks 4 Kidz New Zealand](https://www.bricks4kidz.co.nz/) [Grand Training](https://grandtraining.co.nz) [Scratchpad](https://scratchpad.co.nz/courses/roblox-learn-it-school-holiday-programme/) #### Nigeria [Logiscool](https://www.logiscool.com/) #### Oman [Fire Tech](https://www.firetechcamp.com/) #### Philippines [Camp Sonshine](https://campsonshineinternational.org/) #### Poland [Giganci Programowania](https://www.giganciprogramowania.edu.pl) [impact Academies and Camps](https://impactacademies.pl/) [Kids Coder Lab](https://www.kidscoderlab.pl) [WOW School](https://www.wowschool.pl/) #### Portugal [Code.Up](https://www.codeup.pt) [Cria Jogo](https://criajogo.com/) #### Puerto Rico [digitalsake](https://digitalsake.com/) #### Romania [Impact Academies and Camps](https://impactacademiesro.com/) [Logiscool](https://www.logiscool.com/) [Small Academy](https://small.academy/cursuri/programare-roblox/) #### Russian Federation [Algorithmics](https://algoritmika.org/) [CODDY](https://coddyschool.com/courses/sozdanie_igr_v_roblox_studio/) [Codabra](https://start.codabra.org/) [Era IT](https://era-it-center.ru/roblox-studio/) [Exclusive-It](https://excklusiveit.com/) [impact Academies and Camps](https://impactacademies.ru/) [KIBERone Russia](https://kiber-one.com/moduli/roblox-studio-sdelat-vse-chto-vy-mozhete-sebe-predstavit/) [Ozon Academy](https://ozonacademy.ru/) [Program School](https://program-school.org/) [Skysmart](https://skysmart.ru/programmirovanie-dlya-detej?direction=basics) [iKids](https://ikids.su/) #### Rwanda [TechKidz Africa's](https://techkidzafrica.co.ke/) #### Sao Tome and Principe [Code.Up](https://www.codeup.pt) #### Saudi Arabia [Logiscool](https://www.logiscool.com/) #### Serbia [Bee Tesla](https://www.beetesla.rs/) [Logiscool](https://www.logiscool.com/) #### Singapore [Black Rocket](https://blackrocket.com/courses/) [D-SCHOOL ONLINE](https://d-school.co/online/) [Empire Code](https://empirecode.co/) [ICanCode Asia](https://www.icancodeasia.com) [Kodecoon Academy](https://www.kodecoon.com/) [Logiscool](https://www.logiscool.com/) [Paideia Learning Academy](https://paideia.com.sg/computing.php) [iD Tech](https://www.idtech.com/roblox-summer-camps?utm_source=roblox&utm_medium=website&utm_campaign=2018&utm_content=roblox-site) #### Slovakia [Logiscool](https://www.logiscool.com/) #### South Africa [Camp Sonshine](https://campsonshineinternational.org/) [Logiscool](https://www.logiscool.com/) [Wearable Tech Ventures ](https://www.wearabletechventures.org) #### South Korea [ARTX Campus](https://www.artxcampus.co.kr) [D.LAB](https://www.daddyslab.com) [LIKELION](https://www.likelion.net) [NaalAcademy](https://www.gnstartup.kr/naalacademy/board/f265a2df-2833-44ae-bbe1-b1320dc830b9/12a233ca-153a-4437-86fd-7249dbe1c0f1) [ROBOROBO](https://www.roborobo.co.kr/main) [WGfactory](https://wgfactory.xyz/program/gamedev/) [iD Tech](https://www.idtech.com/roblox-summer-camps?utm_source=roblox&utm_medium=website&utm_campaign=2018&utm_content=roblox-site) #### Spain [Camp Tecnológico](https://camptecnologico.com/campamentos-navidad-bilbao-manana/#roblox) [ConMasFuturo](https://www.conmasfuturo.com/) [Curiosity and Learn](https://curiosityandlearn.com/) [Giganci Programowania](https://www.giganciprogramowania.edu.pl) [impact Academies and Camps](https://impactacademies.es/) [KIBERone Spain](https://kiber-one.es/moduli/roblox-studio-hacer-todo-lo-que-uno-puede-imaginarse/) [Logiscool](https://www.logiscool.com/) #### Switzerland [impact Academies and Camps](https://impactacademies.ch/) [Logiscool](https://www.logiscool.com/) [iD Tech](https://www.idtech.com/roblox-summer-camps?utm_source=roblox&utm_medium=website&utm_campaign=2018&utm_content=roblox-site) #### Taiwan [Logiscool](https://www.logiscool.com/) [iD Tech](https://www.idtech.com/roblox-summer-camps?utm_source=roblox&utm_medium=website&utm_campaign=2018&utm_content=roblox-site) #### Tajikistan [Algorithmics Tajikistan](https://dushanbe.alg.academy/) #### Tanzania [TechKidz Africa's](https://techkidzafrica.co.ke/) #### Thailand [Brighton College Bangkok](https://brightoncollege.ac.th/) #### Trinidad and Tobago [Smart Kids Educational Club](https://www.smartkidsclubtt.com/) #### Turkey [Atolye Vizyon](https://www.atolyevizyon.com/) [Logiscool](https://www.logiscool.com/) #### Uganda [TechKidz Africa's](https://techkidzafrica.co.ke/) #### Ukraine [Algorithmics Ukraine](https://logikaschool.com/#programs) [CODDY](https://coddyschool.com/courses/sozdanie_igr_v_roblox_studio/) [Exclusive-It](https://excklusiveit.com/) #### United Arab Emirates [Fire Tech](https://www.firetechcamp.com/) [The-Code](https://www.the-code.org/copy-of-programming-2) #### United Kingdom [Aspire 2Be](https://aspire2be.co.uk/) [BYJU's FutureSchool](https://www.byjusfutureschool.com/) [Black Rocket](https://blackrocket.com/courses/) [Code Kingdoms](https://codekingdoms.com/roblox/) [Code Ninjas](https://www.codeninjas.com/) [Coder Sports](https://codersports.com) [Fire Tech](https://www.firetechcamp.com/) [FunTech](https://funtechsummercamps.com/course-descriptions/roblox-game-maker) [iD Tech](https://www.idtech.com/roblox-summer-camps?utm_source=roblox&utm_medium=website&utm_campaign=2018&utm_content=roblox-site) [impact Academies and Camps](https://impactacademies.co.uk/) #### Uruguay [digitalsake](https://digitalsake.com/) #### Uzbekistan [CODDY](https://coddyschool.com/courses/sozdanie_igr_v_roblox_studio/) [impact Academies and Camps](https://impactacademiesuz.com/) #### Venezuela [digitalsake](https://digitalsake.com/) #### Vietnam [D-SCHOOL ONLINE](https://d-school.co/online/) --- title: "Roblox for the AP CSP Create Performance Task" url: /docs/en-us/education/resources/roblox-for-the-ap-csp-create-performance-task last_updated: 2026-06-29T19:34:01Z description: "Recommendations on how to incorporate Roblox Studio and Luau into a classroom for use in the AP CSP Create Performance Task" --- # Roblox for the AP CSP Create Performance Task Roblox Studio's collaborative nature and ease of prototyping makes it a great option to use with the [AP CSP Create Performance Task.](https://apcentral.collegeboard.org/courses/ap-computer-science-principles/exam?course=ap-computer-science-principles) Some of the benefits include: - Free, easy to download software. - Built-in features allowing real-time collaboration. - Ability to innovate and express oneself using the scripting language, Luau. ## Roblox for computer science ### Code with Luau Luau is excellent as a first coding language, or for those ready for a new challenge after visual languages such as MIT Scratch. Luau is a typed syntax language. It's similar to Python, but without white space concerns. This means your students' code won't break if they forget to indent a block of code or add a semicolon. With Luau, students can focus more on computer science concepts than excessive troubleshooting. ### Recommendations Before starting a classroom using Roblox, we do have some recommendations. As students have only limited time to create their program, it's recommended that students have used Roblox Studio throughout the semester, or have previous experience with Luau or Lua. For Luau coding principles that you can adapt to the classroom, check our [Coding Fundamentals](/docs/en-us/tutorials/fundamentals/coding-1/landing.md) series. Students can then practice coding by going through our guided projects such as the [Adventure Game](/docs/en-us/education/adventure-game-series/landing.md). ### Collaboration Roblox Studio is built with [collaboration](/docs/en-us/projects/collaboration.md) in mind, making it an ideal choice for students studying for the AP test independently or online. Additionally, students can work simultaneously in the [same script](https://www.youtube.com/watch?v=6wuZJTiwCtM&feature=emb_logo), or dedicate scripts to specific tasks, making it easy to track a student's individual contributions. ### Types of student projects Some examples of abstractions and algorithms students can incorporate within a 6 - 8 hour timeframe are: - Setting up a shop to buy and sell items. - Creating and updating custom leaderboards. - Making changes to player properties such as health, speed, and size. - Incorporating timers to get past obstacles or to create round-based games. - Creating branching stories. ## The AP CSP Create Performance Task Students are required to submit the following for the Create Performance Task: - A one minute video of the program running. - Individual written responses about the program and their development process. - The program's code. For more information, refer to the [AP CSP Exam Sheet](https://apcentral.collegeboard.org/pdf/ap-csp-student-task-directions.pdf?course=ap-computer-science-principles) for precise details. ### Example Roblox idea For the written submission, students will need to identify an algorithm in their project that incorporates two smaller algorithms. They will also need to call out an abstraction. Students are allowed to reuse an algorithm for an abstraction so long as they describe how it's an abstraction, rather than repeating their previous answer. ### Project example **Program Purpose:** Players collect coins in a game and then use the coins to buy items. **Main algorithm:** `buyItem(itemToBuy)` - Allows players to buy items. When players select an item to purchase, it first checks if the player has enough coins. If so, it subtracts the cost of the desired items from their total coins and updates the player's coins on the leaderboard. **Sub algorithm 1:** `verifyPurchase()` Check the player's coins. If greater than or equal to the cost of the desired item, display the purchase confirmation screen. If the value of the player's coins is less than the cost of the item, display a screen with the text: "Sorry, you don't have enough coins". **Sub algorithm 2:** `purchaseItem()` Get the player's current amount of coins. Subtract the cost of the item from the player's coins. Then, update the player's current coins displayed on the leaderboard. **Potential Abstraction:** Rather than creating an individual function for the sale of each type of item, `buyItem()` has a parameter for the item being bought, `itemToBuy`. The item cost is found using `getCost()`, which returns a value from a dictionary of items and how much they cost. That cost is then used in `verifyPurchase()` and `purchaseItem()`. ### Recommended task milestones Plan on spending **at least two hours** preparing students for the PT Create Task, and then a minimum of 12 hours of class time to complete and submit a program along with written and video responses. This recommended schedule gives students approximately 6 or 7 hours for completing the code, with additional time to prepare and upload responses. | ApproximateTimeline | Objective | | --- | --- | | PT Prep 1 | Introduce Create PT. | | PT Prep 2 | Brainstorm ideas for core and sub algorithms. | | Hour 1 | Begin class with a defined core algorithm. Break down the problem and begin coding solutions. Document any issues or problem points that arise, as well as their solutions. | | Hour 2 | Have a working prototype. Be able to articulate algorithms and abstractions used. Make changes to goals as necessary if a task is proving too difficult. | | Hours 3 - 4 | Identify what specific abstraction will be featured in the submission video. Continue keeping development notes, particularly of any iterations that the project goes through. | | Hours 5 - 7 | Complete main project. | | Hour 8 | Record video response to question 2A. | | Hours 9 - 10 | Complete written responses. | | Hours 11 - 12 | Submit program code and written responses. | ### Video requirements The video should be of running code and demonstrate the purpose of the overall program as well as at least one significant feature. All videos must be under one minute and not exceed 30 MB. For an example of an exemplary video response, with scoring notes see [AP Central: Sample Response A.](https://apcentral.collegeboard.org/courses/ap-computer-science-principles/classroom-resources/create-applications-ideas-sample-response-a) For complete requirements see the [Digital Portfolio Student User Guide.](https://apstudents.collegeboard.org/ap/2019-07/digital-portfolio-student-user-guide-apcsp.pdf) ### Prep for the PT Plan on spending one class session introducing the project and showing examples of graded projects. Use a second session to plan the project. Below are resources to help you structure your prep for the PT Create. - **Code.org** - [Lesson 1: Create PT - Review the Task](https://curriculum.code.org/csp-19/csp-create/1/) - [Lesson 2: Create PT - Make a Plan](https://curriculum.code.org/csp-19/csp-create/2/) - [CS Principles Curriculum Guide](https://docs.google.com/document/d/1ZVzF_-cON8pjDVUOZjVk32y4flCMXugcrA6gFeWDHzE/preview#heading=h.rgibzjtvyu24) - [Student Survival Guide](https://studio.code.org/s/csp-create-2018/stage/2/puzzle/1?viewAs=Teacher) You'll need a code.org account to download this. - **AP Central - AP CSP** - [Assessment Overview and Performance Task Directions for Students](https://apcentral.collegeboard.org/pdf/ap-csp-student-task-directions.pdf?course=ap-computer-science-principles) - [Digital Portfolio Student User Guide](https://apstudents.collegeboard.org/ap/2019-07/digital-portfolio-student-user-guide-apcsp.pdf) - **Khan Academy** - [AP CSP Exam Overview](https://www.khanacademy.org/computing/ap-computer-science-principles/ap-csp-exam-preparation/prepare-for-the-2019-ap-cs-p-exam/a/ap-cs-p-exam-format) ## Roblox specific tips ### Keep visuals and 3D worlds simple When creating games, students can get invested in decorating or customizing their world. Help them keep in mind that they only have a limited time, and encourage them to use basic block shapes and colors to plan out their game. ### Plan out needed scripts As part of the planning session, have students write down what scripts they will need, where that script will be located, and that script's function. Make sure that students can identify at least one function that simplifies their code. Usually these functions will have parameters that allow them to take in different values, or will be called from multiple places. Student code should be logical, not created through trial and error or hacked together. Below are some questions to help students reflect: - What function is responsible for the main algorithm in the game? - What are two smaller algorithms needed to run the main algorithm? - What assets, such as art or sounds, are needed to make the program function as intended? What tasks and behaviors need to be coded? - If you become low on time, what features can be cut? ### Avoid using toolbox assets The Toolbox is a part of Roblox Studio that includes prebuilt assets, like 3D models. While these can often save students time in building, they often already include scripts that may have unwanted functionality or conflict with the student's code. If students are not diligent about removing all scripts included with prepackaged assets, it can result in time wasted while debugging later. --- title: "Teach remotely with Roblox Studio" url: /docs/en-us/education/resources/teaching-remotely last_updated: 2026-06-29T19:34:01Z description: "Collaboration and creation tools to bring students for distance learning." --- # Teach remotely with Roblox Studio As educators, we understand the importance of having meaningful and collaborative learning experiences. With schools and teachers responding to COVID-19, Roblox is proud to offer resources that allow students and educators to collaborate and learn in real time. ## Tools for teaching remotely The tools below make remote learning easy and engaging. | | #### Get trained Learn about Roblox, such as installing software and creating accounts | | --- | --- | | | #### Interacting online Foster a positive culture when learning remotely. | | | #### Collaborate together Build, code, and chat online with Collaboration. | | | #### Private servers Setup servers for classes or students. | ## Explore different lessons Roblox Education has a variety of lesson plans for different ages and subjects. ## Learn and explore with experiences Many Roblox experiences offer educational value, such as allowing students to roleplay jobs like being a veterinarian, explore historical sites, or experiment with rocket physics. Check out these hand-picked experiences to play with students, or use them to kick-start a lesson of your own. ## Tips to teaching remotely Teaching with Roblox introduces unique opportunities. When running a remote classroom, keep in mind the following tips: - **Set Expectations Early** - Whether it's how students interact together, or taking turns building a collaborative project, set expectations before starting a lesson. Encourage students to create expectations with you for greater buy-in. For example, expectations like "Don't change someone's work without permission", can make collaborative building fun and easier. - **Prep Log-in Practices** - Depending on your classroom, students may need up to 3-5 minutes to login. Additionally, come up with a system so students track their own Roblox usernames and passwords. To help you run a remote classroom, we recommend the following curated resources: - [ISTE.org](https://www.iste.org/explore/10-strategies-online-learning-during-coronavirus-outbreak) - 10 strategies for online learning during a coronavirus outbreak. - [Edsurge](https://www.edsurge.com/news/2020-03-11-coronavirus-has-led-to-a-rush-of-online-teaching-here-s-some-advice-for-newly-remote-instructors)- Coronavirus Has Led to a Rush of Online Teaching. Here’s Some Advice for Newly Remote Instructors - [Roblox Education FAQ](/docs/en-us/education/resources/frequently-asked-questions-education.md)- Answers to questions about accounts. ## Questions and support Need advice or help getting started? [Contact](https://roblox.qualtrics.com/jfe/form/SV_8oiiVEvjteux7hk)the Roblox Education team. --- title: "Create bundled installers" url: /docs/en-us/education/support/creating-bundled-installers last_updated: 2026-06-29T19:34:01Z description: "Install Roblox Studio on multiple computers for schools or summer camps." --- # Create bundled installers For organizations looking to install Roblox Client or Studio on multiple computers, we offer bundled installers. Normally, an installer is used on individual computers, but this can be difficult if you're looking to mass install Roblox, or Roblox Studio on multiple computers. To help in this process, you can create your own bundled installer, giving you a traditional executable that can be installed on multiple computers. This can then be used in software deployment and imaging tools. ## Create the bundler The following instructions are applicable to **Windows** computers. 1. Install [Roblox Studio](https://www.roblox.com/create) on your computer. 2. Open the **Command Prompt**. You can do this by clicking the Windows start icon. Then type `CMD` and click on the Command Prompt application. 3. Find the installation of Roblox on your computer. You need the executable of what you'll be creating a bundle for. This will typically be in:`C:\Users\userName\AppData\Local\Roblox\Versions` > **Info:** If you have multiple versions, you'll have both Roblox Studio and Client. To find which one includes the executable, open the folders and check for either RobloxStudio.exe or RobloxPlayerLauncher.exe. 4. In the Command Prompt, go to the location of the Roblox executable to use by typing `cd`, then the file path. and then `Enter`. For example:`cd C:\Users\userName\AppData\Local\Roblox\Versions\version-6d02431b656044a6` 5. Depending on the installer you want to create, type the file name, followed by -bundle, like below, and press `Enter`.```bash RobloxStudioLauncherBeta.exe -bundle RobloxPlayerLauncher.exe -bundle ``` 6. Let the bundler work until it closes.This will create a bundled installer in the same folder as the Roblox executable with a file name such as: `RobloxStudioLauncherBeta_version-6d02431b656044a6.exe`. --- title: "Design Studio templates" url: /docs/en-us/education/support/designing-studio-templates last_updated: 2026-06-29T19:34:01Z description: "Educators can create templates to use in a classroom or for a lesson. Templates make it easy for students to make their own projects." --- # Design Studio templates Educators can create templates to kickstart a student's project in Roblox Studio. These templates can be fully realized experiences, or pre-existing Roblox templates customized with your organization's branding. This guide outlines the process of creating a template and making it accessible to students. ## Benefits of templates One benefit of templates is they can provide students a **curated** set of assets to work with, such as 3D models or scripts. This can be a great alternative to having students create their own assets, which can be time consuming, or using models from the Toolbox. Premade assets can also be a part of lesson plans. Such as challenging students to create apply urban planning to design a city, or perhaps debug a set of scripts. _A kit of buildings_ _Student project using building kit_ Templates already installed in Roblox Studio can also be customized to include **branding**, such as your organization's logo. _The Mansion of Wonder template customized by [Byju's Future School](https://www.byjusfutureschool.com/)_ ## Customize or develop a template Templates can be any Roblox project designed in Roblox Studio. One way of creating a custom template is to **modify an existing** template in Roblox Studio. Studio includes multiple pre-made games that can be customized. _Original Mansion of Wonder Template_ _Modified by Byju's Future School_ Another way of creating a custom template is to **design a new one**. Though, depending on the scale of your template, this could even be providing a blank workspace with premade scripts. _Templates designed by online educator Code Kingdoms_ ## Publish a template To distribute a template, you'll **publish** it to Roblox. Once on the Roblox cloud, students can access it online from any machine, without having to worry about local storage and file transfers. Templates can be published either as individual Roblox users or [groups](https://en.help.roblox.com/hc/en-us/articles/203313730-How-to-Make-and-Join-Groups). Each has its own benefits. | **User Publishing** | Ideal for individual educators and developers. | | --- | --- | | **Group Publishing** | Best for larger organizations where more than one person is expected to work on the template. Note that groups require a one-time payment of 100 Robux (about $1.25 USD). | > **Warning:** If your organization chooses to run a group, only allow your developers inside that group. Do not allow students to join. If in a group, a student may accidentally make unintended modifications to a template. ### Publish the template With a template ready, it can be published to Roblox, so students can access it online. Once a template is published, educators are responsible for sharing it through a learning management system such as Google Classroom or a messaging system like email. 1. In Roblox Studio, in the top-left, click on **File** ⟩ **Publish to Roblox As**. > **Info:** When students open a template, the first thing they see will be the view of the template where it was last saved. To do this, before publishing use the camera controls to position the in-game view to where you expect students to start working. 2. At the bottom left of the pop-up, click **Create new game**. 3. Complete the basic info, such as **name** and **description**. This will be shown to all students on the template's Roblox webpage. 4. In the **Creator** field, click on the dropdown. Individual developers should use **Me**, while those in development groups should use the name of that group. 5. Finish by clicking the **Create** button. ### Set up the template to copy Even though the template is now published on Roblox, students need to be able to copy it in their own version of Studio. 1. In the same template, select **File** ⟩ **Experience Settings**. > **Info:****Adding Screenshots and Icons** On your experience's webpage, [screenshots](/docs/en-us/production/publishing/thumbnails.md) and an [icon](/docs/en-us/production/publishing/experience-icons.md) will be shown to students. While optional, adding these can boost the look of your template and serve as an additional opportunity for branding. 2. Click on the **Permissions** tab, set **Playability** to **Public**. 3. In the **Places** tab, click on the **⋯** icon and select **Configure Place**. 4. Scroll down to find **Allow Copying** and toggle it **On**. Once this property is on, it's possible for any Roblox user to access your template. For alternatives, see the card under the image. > **Info:****Sharing Only in Your Organization** There are situations in which you'd want to share a template, but only with a certain set of students. In this situation, we recommend sharing local files. 5. Once your template is ready, in Studio, go to **File** ⟩ **Download a Copy**. This creates a shareable `.rbxl` file. 6. Distribute the `.rbxl` file as desired. ## Share templates with students Templates can now be shared by having students go to the template's web page. There, students can click a link that opens a copy of that template in Roblox Studio. 1. To share the template, send the **URL** to the template's game page through a learning management system (such as Google Classroom) or student email. When sending the URL, we recommend removing the place name. It's possible a place name may change, but an ID number won't. > **Info:** **Sharing Only in Your Organization** > > When sending the URL, we recommend removing the place name. It's possible a place name may change, but an ID number won't. > > Recommended: `https://www.roblox.com/games/6763393969/` > > Not Recommended: `https://www.roblox.com/games/6763393969/Sample-Template` 2. Once the student opens the link, instruct them to click on the **...** icon and select **Edit**. If Roblox Studio is installed, it will automatically open the template in that software. --- title: "Contact us" url: /docs/en-us/education/support/education-contact-form last_updated: 2026-06-29T19:34:01Z description: "Contact Roblox Education for questions regarding how our resources can be used to teach coding and more in the classroom." --- # Contact us To contact the Roblox Education team, fill out this [survey](https://roblox.qualtrics.com/jfe/form/SV_8oiiVEvjteux7hk). --- title: "Education content" url: /docs/en-us/education/support/education-content last_updated: 2026-06-29T19:34:01Z description: "Featured lesson plans and tutorial series." --- # Education content Our lessons are designed for educational programs of varying lengths and skill levels. Find everything from two hour workshops to semester long programs. > **Info:** We've curated some samples of our most popular work, but the rest of the content can be found in the **left** navigation bar. ## Featured lesson plans Each lesson plan includes a set of tutorials complete with teaching notes and supplemental material. ![alt](../../assets/education/edu-landingPages/introToGameDesign_thumbnail.jpg) #### Code and Design Have students create their first experience. Great for beginners. ![alt](../../assets/education/edu-landingPages/bipi_lessonPlan_thumbnail_312x200.png) #### Animate in Roblox A one hour workshop for beginners focused on animation. ![alt](../../assets/education/edu-landingPages/ccw2019_thumbnail_312x200px.png) #### Design a Ship Students build and race a spaceship while learning 3D modeling. ## Featured tutorial series Each tutorial series includes a set of lessons covering specific topics in Roblox Studio or computer science. Educators can present a tutorial to students, or individuals can learn self-directed. ![alt](../../assets/education/edu-landingPages/thumbnail_traps_312x200.jpg) #### Coding Fundamentals A comprehensive course covering a variety of topics in Luau, a descendant of Lua developed by Roblox. ![alt](../../assets/education/edu-landingPages/introToStudio_thumbnail.jpg) #### Introduction to Studio Students learn the basics while designing an obstacle course. ![alt](../../assets/education/edu-landingPages/thumbnail_adventureRthroAlt_312x200.jpg) #### Design An Adventure Game From start to finish, students plan, design, and code a complete experience. --- title: "Get started with Roblox Education" url: /docs/en-us/education/support/education-getting-started last_updated: 2026-06-29T19:34:01Z description: "Learn how to get started using Roblox in the classroom. Find teacher training, get lessons, and discover experiences to play." --- # Get started with Roblox Education ![Get Started with Roblox Education](../../assets/landing/education/banner.jpg) **Discover resources, best practices, and lesson plans to incorporate Roblox experiences into your classroom.** ## Get Started with Roblox Education Roblox is more than an online entertainment platform, it's an educational tool where the opportunities for creativity and imagination are limitless. We offer free software and curriculum to teach students of all ages computer science, digital citizenship, entrepreneurship, and more. #### Educator Onboarding Implement Roblox in the classroom with our training and guides. #### Education Content Find free lessons using Roblox whether it's for creation or learning. #### Developer Onboarding For studios and developers interested in making educational projects on Roblox. #### Learning Experiences A list of experiences that can facilitate learning through play. #### Find Roblox Educators A resource for our community that lists educators using Roblox to educate. --- title: "Private servers for classroom use" url: /docs/en-us/education/support/private-servers-for-classroom-use last_updated: 2026-06-29T19:34:01Z description: "Private servers in Roblox are used so students can only interact with those chosen by teachers." --- # Private servers for classroom use In addition to building and creating, playing is a proven way to learn and improve social skills. Many experiences on Roblox offer private servers available for a small monthly fee that help to cover the price of the server or even for free. ## Create private servers To see if an experience offers private servers, look for the Private Servers section beneath the game's description. If private servers are offered, there will be a button saying "Create Private Server" A server's costs, and the number of players it can host is set by the developer of the experience. To learn, go to the [Private Servers](https://en.help.roblox.com/hc/en-us/articles/205345050-How-do-I-Purchase-and-Configure-VIP-Server) page on Roblox Support. ## Invite students Once you have your server set up, it can be found on the servers tab. To invite students, click options button in the top right and select **Configure**. From there, you can either provide your class the link to the server. Alternatively, add their Roblox usernames to the server. There, you can change the name of the server to help organize groups of students. Example: _JordanHS-Afternoon, HillsdaleMS-2pm_ A link can be generated to make sharing with students on PC or Mac easier. Players on mobile devices must be invited directly to the server rather than using the sharing link provided. ## Examples in the classroom Below are some ways our educators have used private servers in the classroom. - Create a private server for students to explore a historical site. - Allow students to connect virtually when they may not have the opportunity to do otherwise. - Graduation and birthday parties. - If students have created an experience, allow them to create a private server so only members of the class can play. ## Find experience The [Learn & Explore](https://www.roblox.com/discover#/sortName/Curated_67) sort, features experience that offer exploration of new places and subjects like history, safety, physics, and natural sciences. Many were created by educators and students. The [Play Together](https://www.roblox.com/discover#/sortName/Curated_71) sort is a curated list of experiences that are designed to be played with friends and are currently offering free private servers. This is a great place to start looking for experiences to bring your students together. ### Popular games offering servers Here's just a few of the millions of experiences that can be found on Roblox. - [Dinosaur Simulator](https://www.roblox.com/games/228181322/Dinosaur-Simulator#!/game-instances) - [Plane Crazy](https://www.roblox.com/games/166986752/Plane-Crazy) - [Ocean Conservation Experience](https://www.roblox.com/games/6708164649/Ocean-Conservation-Experience#!/game-instances) ## Private servers for student projects If you or your students have created an experience, you can enable private servers to make it easier for yourself and other educators to use in class. To learn how to turn on private servers go to the [Private Servers](/docs/en-us/production/monetization/private-servers.md) page on the Roblox Developer Hub. If you do have students interact online, keep in mind that they will come across "imperfect" conduct online. Being proactive by teaching students how to react to bullies and angry words is a valuable skill. Before starting online play, teach students to use our [reporting tools](https://en.help.roblox.com/hc/en-us/articles/203312410-How-to-Report-Rule-Violations) should they encounter inappropriate behavior. At Roblox, safety is our number one priority. To learn more about how we strive to foster a positive community and use cutting edge moderation to detect inappropriate behaviors, please visit our [Parents](https://corp.roblox.com/parents) page. --- title: "Roblox account creation" url: /docs/en-us/education/support/roblox-account-creation last_updated: 2026-06-29T19:34:01Z description: "Create Roblox accounts and log into Roblox Studio." --- # Roblox account creation This document shows students how to create accounts online and log into Roblox Studio. Students should have Roblox Studio installed and tested on their computers before continuing. ## Create new accounts 1. If you're completely new to Roblox, [sign up for a free account](https://www.roblox.com/home). 2. Have students document their account information somewhere safe. ## Account safety Once students have created accounts, be sure to cover tips to keep their accounts safe. - **Never share your password**, even with a real life friend. - **Make your password hard to guess** - If your username is _bloxcool_, your password should **not** be _bloxcool123_. - **Roblox employees will never ask for your password** - Report anyone who asks using the [Report Abuse](https://en.help.roblox.com/hc/en-us/articles/203312410) feature. - **There's no such thing as free Robux** - Never trust players or sites who say they have a secret way to get free Robux! For more tips, see [Keeping Your Account Safe](https://en.help.roblox.com/hc/en-us/articles/203313380-Account-Security-Theft-Keeping-your-Account-Safe). ## Open Roblox Studio Roblox Studio is the tool used to make all Roblox games. If Roblox Studio is not already on your computer, get a secure download [here](https://www.roblox.com/create). 1. After installing Roblox Studio, double-click the desktop icon (Windows) or click the dock icon (Mac). 2. On the login screen, enter your Roblox username and password, then click **Log In**. --- title: "Beams" url: /docs/en-us/effects/beams last_updated: 2026-06-29T19:34:01Z description: "Beams create a visual, animatable effect between two attachments." --- # Beams A `Class.Beam` is an object that renders a `Class.Texture` between two `Class.Attachment` objects. By setting beam properties, you can: - Add a [texture](#texture) and [color gradient](#color) to create interesting visuals like waterfalls and force fields. - Modify a beam's [transparency](#transparency) so that it fades over time. - Warp its shape by changing the [width](#width) or [curve](#curve) of each attachment point. - Specify the texture's [length and mode](#texture-lengthmode) to affect how it repeats across the beam's length. ## Create beams Before you begin to create a beam, it's useful to toggle on visibility of attachments so you can see where the beam starts and ends. You can do so by enabling **Show Constraint Details** from Studio's **View** menu. To create a beam: 1. Create two parts and position them a short distance from each other. One will act as the start of the beam and the other as the end of the beam. Group the parts into a `Class.Model`. 2. In the **Explorer** window, add an `Class.Attachment` object to each part: 1. Hover over one part and click the ⊕ button. A contextual menu displays. 2. From the contextual menu, insert an **Attachment**. 3. Repeat for the other part. 3. In the **Explorer** window, add a `Class.Beam` object to the model: 1. Hover over the model and click the ⊕ button. A contextual menu displays. 2. From the contextual menu, insert a **Beam**. 4. Select the new **Beam** object and assign its attachments. Ensure you assign each attachment property to a different `Class.Attachment` object. 1. In the **Properties** window, select the **Attachment0** property. Your cursor changes. 2. In the **Explorer** window, select the attachment of the part you want to be the start of your beam. 3. Back in the **Properties** window, select the **Attachment1** property. Your cursor changes. 4. In the **Explorer** window, select the attachment of the part you want to be the end of your beam. _Default beam between two attachments_ > **Warning:** Beams require attachments to function properly. If you remove either attachment object from step 4, the beam stops rendering its texture. ## Customize beams By experimenting with the following properties, you can customize a beam's visual appearance to make unique gameplay elements like force fields, waterfalls, and pathway obstacles. > **Info:** The visual quality of your beams can change depending on the graphics settings on the player's device. To review your beam across quality levels, it's recommended to open [Studio Settings](/docs/en-us/studio/setup.md#customization), search for **Editor Quality Level**, and set it to the both the lowest and highest level after you finish customizing your beam. ### Texture The `Class.Beam.Texture|Texture` property renders that texture across the length of the beam. You can set a beam's `Class.Beam.Texture|Texture` property to any asset ID. For more information, including how to add or upload your own textures, see [here](/docs/en-us/projects/assets/manager.md#asset-import). ![A texture of dark gray circles against a grey background emits between two attachments.](../assets/lighting-and-effects/beam/Texture-Applied.png) A beam renders its texture using two triangles drawn between `Class.Beam.Segments|Segments`, and the segments are laid out between the two attachment points' orientation. When you rotate attachment points in different directions, segments also rotate. ![The same texture from the previous image is now curved because the attachments are in different orientations.](../assets/lighting-and-effects/beam/Attachments-Rotated.png) ### Color The `Class.Beam.Color|Color` property tints the beam's texture to either a specific hue, or to a gradient `Datatype.ColorSequence` across its entire span. #### Constant Color 1. In the **Explorer** window, select the beam. 2. In the **Properties** window, select the **Color** property. You can either: 1. Click on the color square to open the **Colors** pop-up window and select a color. 2. Input three numbers into the RGB color value field. #### Color Gradient 1. In the **Explorer** window, select the beam. 2. In the **Properties** window, click inside the **Color** property field, then click the **⋯** button.![Button to open color sequence popup](../assets/studio/properties/Color-Open-Sequence.png) In the color sequence popup, each triangle on the bottom axis is a **keypoint** that determines the color value of the beam at that point along its span.![Keypoints labeled in color sequence popup](../assets/studio/general/ColorSequence-White-Keypoints-Labeled.png) 3. Click the keypoint at the start of the color sequence, click the small square next to **Color**, and select a color from the popup window.![Color sequence popup from red to white](../assets/studio/general/ColorSequence-Red-White.png) 4. If you want the beam to change color near the end of its span, click the keypoint at the end of the color sequence, click the small square next to **Color**, and select a color from the popup window.![Color sequence popup from red to purple](../assets/studio/general/ColorSequence-Red-Purple.png) 5. For more customization, you can: - Add another keypoint by clicking anywhere on the graph. - Make a color change sooner or later within the gradient by dragging an intermediary keypoint to a new position. - Delete an intermediary keypoint by selecting it and clicking the **Delete** button. - Reset the entire sequence by clicking the **Reset** button.![Color sequence popup from red to cyan to purple](../assets/studio/general/ColorSequence-Red-Cyan-Purple.png) ### Transparency The `Class.Beam.Transparency|Transparency` property sets the transparency of the beam as a consistent value or as a `Datatype.NumberSequence` with ranges from **0** (totally opaque) to **1** (fully clear). #### Constant Opacity 1. In the **Explorer** window, select the beam. 2. In the **Properties** window, select the **Transparency** property. 3. Input the desired opacity for the beam. #### Number Sequence 1. In the **Explorer** window, select the beam. 2. In the **Properties** window, click inside the **Transparency** property field, then click the **⋯** button.![Button to open number sequence popup](../assets/studio/properties/Transparency-Open-Sequence.png) In the number sequence popup, each square at the start and end of the graph is a **keypoint** that determines the opacity value of the beam at that point along its span. By default, the graph defaults to a straight line and the beam is of consistent opacity.![Keypoints labeled in number sequence popup](../assets/studio/general/NumberSequence-0-0-Keypoints-Labeled.png) 3. Perform any of the following actions: - To change the opacity at a point, click on a keypoint and either drag it up or down, or enter a value in the **Value** field. - To insert a new keypoint, click on any point in the graph. - To delete a keypoint, select it and click the **Delete** button. - Reset the entire sequence by clicking the **Reset** button.![Number sequence popup from 0 to 1 to 0](../assets/studio/general/NumberSequence-0-1-0.png) ### Width You can set the beam's width in studs at each endpoint by configuring the `Class.Beam.Width0|Width0` and `Class.Beam.Width1|Width1` properties. A higher value equates to a larger width and a lower value equates to a smaller width. If you set either value to smaller than 0, Studio sets it back to **0**. The beam below has a `Class.Beam.Width0|Width0` value of **0.5** and a `Class.Beam.Width1|Width1` value of **3**. ![A texture emits between two attachments. The texture starts with a small width and ends with a large width.](../assets/lighting-and-effects/beam/Width-Adjusted.png) ### Texture length/mode A beam's `Class.Beam.TextureLength|TextureLength` and `Class.Beam.TextureMode|TextureMode` determine how its [texture](#texture) repeats across its length. When `Class.Beam.TextureMode|TextureMode` is set to `Enum.TextureMode.Wrap` or `Enum.TextureMode.Static`, the texture repetitions will equal the beam's overall length (in studs) divided by its `Class.Beam.TextureLength|TextureLength`. ![TextureMode diagram with Wrap or Static mode](../assets/engine-api/enums/TextureMode/Wrap-Static.png) When `Class.Beam.TextureMode|TextureMode` is set to `Enum.TextureMode.Stretch`, the texture will repeat `Class.Beam.TextureLength|TextureLength` times across the beam's overall length. ![TextureMode diagram with Stretch mode](../assets/engine-api/enums/TextureMode/Stretch.png) ### Facing A beam is a 2D projection existing in 3D space, meaning that it may not be visible from every angle. The `Class.Beam.FaceCamera|FaceCamera` property, when set to `true`, ensures that the beam always faces the `Class.Workspace.CurrentCamera|CurrentCamera`, regardless of its orientation. _Default vs. FaceCamera_ ### Curve Beams are configured to use a cubic Bézier curve formed by four control points. This means they are not constrained to straight lines and the curve of the beam can be modified by changing `Class.Beam.CurveSize0|CurveSize0`, `Class.Beam.CurveSize1|CurveSize1`, and the orientation of the beam's `Class.Attachment|Attachments`. - **P0** — The start of the beam; position of `Class.Beam.Attachment0|Attachment0`. - **P1** — `Class.Beam.CurveSize0|CurveSize0` studs away from `Class.Beam.Attachment0|Attachment0`, in the positive **X** direction of `Class.Beam.Attachment0|Attachment0`. - **P2** — `Class.Beam.CurveSize1|CurveSize1` studs away from `Class.Beam.Attachment1|Attachment1`, in the negative **X** direction of `Class.Beam.Attachment1|Attachment1`. - **P3** — The end of the beam; position of `Class.Beam.Attachment1|Attachment1`. ![Beam curvature diagram](../assets/engine-api/classes/Beam/Curvature-Diagram.png) --- title: "Highlighting objects" url: /docs/en-us/effects/highlighting last_updated: 2026-06-29T19:34:01Z description: "Highlighting objects lets you call attention to specific objects within your experience." --- # Highlighting objects The `Class.Highlight` instance is a visual effect which you can use to call attention to a specific object within an experience. Every highlight effect has a silhouette **outline** that surrounds the object and a solid overlay **interior** that displays over the object. You can customize both of these components independently to modify the highlight's visual appearance. ![A tree mesh against a grey background.](../assets/ui/highlighting-objects/OutlineTransparency-1.jpg)_Base object_ ![The same tree mesh with a yellow outline and a black highlight interior.](../assets/ui/highlighting-objects/Overview-Yellow-Outline.jpg)_Yellow outline and black interior_ ![The same tree mesh with a black outline and a yellow highlight interior.](../assets/ui/highlighting-objects/Overview-Yellow-Interior.jpg)_Black outline and yellow interior_ Useful applications of the highlight effect include: - Providing visual feedback that an object is important and/or interactable. - Making distant objects visible through objects that are closer to the user. - Indicating the current position and status of other characters. ## Add highlights As a performance limit, Studio only displays 255 simultaneous `Class.Highlight` instances on the client-side at a time. If you add more than this limit, the additional `Class.Highlight` instances are silently ignored. Note also that highlights on low-end devices may be more pixelated but will otherwise look the same as on other devices with any combination of settings. ### Parent to objects To add a highlight effect to an object, you can parent a new `Class.Highlight` directly to the object. 1. In the **Explorer** window, hover over either a `Class.Model` or a `Class.BasePart`, then click the ⊕ button. A contextual menu displays. 2. From the menu, insert a **Highlight**. The highlight displays on the object with its default property values that create a white outline and a red tint overlay. ### Set the adornee Alternatively, you can place the `Class.Highlight` instance outside of a child/parent relationship either within the workspace, `Class.StarterPlayer`, `Class.StarterGui`, `Class.StarterPack`, or `Class.ReplicatedStorage`, then set its `Class.Highlight.Adornee|Adornee` property to the `Class.Model` or `Class.BasePart` that you want to highlight. ## Customize highlights You can change the properties of a `Class.Highlight` instance to create interesting visual effects that properly highlight objects in theme with your experience. ### OutlineColor The `Class.Highlight.OutlineColor|OutlineColor` property sets the `Datatype.Color3` value of the highlight's outline. ![A tree mesh with red outline.](../assets/ui/highlighting-objects/OutlineColor-Red.jpg)_OutlineColor = [255, 100, 50]_ ![A tree mesh with green outline.](../assets/ui/highlighting-objects/OutlineColor-Green.jpg)_OutlineColor = [0, 255, 125]_ ![A tree mesh with blue outline.](../assets/ui/highlighting-objects/OutlineColor-Blue.jpg)_OutlineColor = [75, 150, 255]_ ### OutlineTransparency The `Class.Highlight.OutlineTransparency|OutlineTransparency` property sets the visibility of the highlight's outline to any value between the default value of **0** (opaque) and **1** (invisible). ![A tree mesh with black opaque outline.](../assets/ui/highlighting-objects/OutlineTransparency-0.jpg)_OutlineTransparency = 0_ ![A tree mesh with transparent outline.](../assets/ui/highlighting-objects/OutlineTransparency-1.jpg)_OutlineTransparency = 1_ ### FillColor The `Class.Highlight.FillColor|FillColor` property sets the `Datatype.Color3` value of the highlight's interior. ![A tree mesh with a red fill.](../assets/ui/highlighting-objects/FillColor-Red.jpg)_FillColor = [255, 100, 50]_ ![A tree mesh with a red fill.](../assets/ui/highlighting-objects/FillColor-Green.jpg)_FillColor = [0, 255, 125]_ ![A tree mesh with a blue fill.](../assets/ui/highlighting-objects/FillColor-Blue.jpg)_FillColor = [75, 150, 255]_ ### FillTransparency The `Class.Highlight.FillTransparency|FillTransparency` property sets the visibility of the highlight's interior to any value between the default value of **0** (opaque) and **1** (invisible). You can use this property to determine how much of the object's existing color you want viewers to see. ![A tree mesh with an opaque black fill.](../assets/ui/highlighting-objects/FillTransparency-0.jpg)_FillTransparency = 0_ ![A tree mesh with a semi-transparent black fill.](../assets/ui/highlighting-objects/FillTransparency-Half.jpg)_FillTransparency = 0.5_ ![A tree mesh with a transparent black fill.](../assets/ui/highlighting-objects/FillTransparency-1.jpg)_FillTransparency = 1_ ### DepthMode The `Class.Highlight.DepthMode|DepthMode` property controls how the effect displays with respect to other objects in the world. - `Enum.HighlightDepthMode.AlwaysOnTop|AlwaysOnTop` — Allows the highlight to display regardless if there are objects between the camera and the highlighted object. This means the viewer is always able to see the highlight regardless of what is between the highlighted object and the camera. - `Enum.HighlightDepthMode.Occluded|Occluded` — Hides the highlight if there are objects between the camera and the highlighted object. This means the viewer is only able to see the object if there are no obstructing objects between the highlighted object and the camera's view. #### AlwaysOnTop ![Two objects are behind pillars, but because their AlwayOnTop property is set to true, you can still see the objects and their highlight.](../assets/ui/highlighting-objects/DepthMode-AlwaysOnTop.jpg) #### Occluded ![Two objects are behind pillars, but because their Occuluded property is set to true, you can only see the objects and their highlight in areas where they are unobstructed.](../assets/ui/highlighting-objects/DepthMode-Occluded.jpg) ### Enabled The `Class.Highlight.Enabled|Enabled` property allows you to quickly enable or disable the highlight **without any impact on performance**. > **Warning:** While a disabled `Class.Highlight` doesn't display, it still takes one of the 255 available `Class.Highlight` slots. If you plan to permanently disable a `Class.Highlight` instance, it's best to delete the highlight rather than disable it. ## Performance tips While you have a lot of options to customize `Class.Highlight` instances, the following tips are recommended to increase your experience's performance on all devices: - Adding or removing a `Class.Highlight` can cause a geometry rebuilding step that might lead to performance spikes and extra draw calls. If you want to change a `Class.Highlight` instance's appearance or temporarily hide/display it on an object, it's best to customize the properties of the `Class.Highlight` instance directly, as changing any property of the `Class.Highlight` instance is lightweight and doesn't impact performance. - Roblox draws objects in a back to front order, which can cause problems if you embed objects with a child `Class.Highlight` inside other objects that also have children `Class.Highlight` instances. For this reason, it's best to keep objects with `Class.Highlight` instances outside of a parent/child relationship with other objects with `Class.Highlight` instances. - The first `Class.Highlight` rendered on the screen incurs most of the performance cost (up to 1 millisecond of GPU time on mobile devices). For additional highlights beyond the first, you should not see a significant performance impact on any platform. - On mobile devices, highlights are more costly on performance when they cover more of the screen. On other platforms, highlights have the same performance cost regardless of their screen coverage. - On all platforms, highlights that are not visible on the screen (whether disabled or fully transparent) incur no performance cost. --- title: "Effects" url: /docs/en-us/effects last_updated: 2026-06-29T19:34:01Z description: "Learn how to add visual effects to your experiences." --- # Effects You can create special effects by parenting special effect objects to other objects or attachments. ## Light sources Light sources let you attach lighting effects to objects or attachments. There are three types of light sources: - A `Class.PointLight` emits light spherically from a single point. This object is ideal for non-directional lights like bulbs, torches, and fireballs. - A `Class.SpotLight` emits light in the shape of a cone with a spherical base. This object is ideal for directional lights like street lamps, flashlights, and headlights. - A `Class.SurfaceLight` emits light from the face of a BasePart. This object is ideal for lighting from TV or computer screens, billboards, and fluorescent panels. _Point lights_ _Spotlights_ _Surface lights_ ## Particle emitters A [particle emitter](/docs/en-us/effects/particle-emitters.md) is an object that emits customizable 2D images (particles) into the world, useful for simulating special effects like fire, smoke, and sparks. ## Beams A [beam](/docs/en-us/effects/beams.md) is an object that renders a texture between two `Class.Attachment` objects `Class.Beam.Attachment0` and `Class.Beam.Attachment1`. By setting beam properties, you can: - Add a [texture](/docs/en-us/effects/beams.md#texture) and [color gradient](/docs/en-us/effects/beams.md#color) to create interesting visuals like waterfalls and force fields. - Modify a beam's [transparency](/docs/en-us/effects/beams.md#transparency) so that it fades over time. - Warp its shape by changing the [width](/docs/en-us/effects/beams.md#width) or [curve](/docs/en-us/effects/beams.md#curve) of each attachment point. ## Trails A [trail](/docs/en-us/effects/trails.md) is an object that creates a trail between and behind two `Class.Attachment` objects as they move through space. Trails can help players visualize movement, such as a sword slashing through the air, projectiles flying to their target, or footprints walking away. By setting trail properties, you can: - Add a [texture](/docs/en-us/effects/trails.md#texture) to create interesting visuals. - Set a constant or gradient [color](/docs/en-us/effects/trails.md#color). - Modify a trail's [lifetime](/docs/en-us/effects/trails.md#lifetime). ## Highlighting `Class.Highlight` is a visual effect which you can use to call attention to a specific object within an experience, such as to provide visual feedback that the object is important and/or interactable. See [Highlighting Objects](/docs/en-us/effects/highlighting.md) for details. ![A tree mesh against a grey background.](../assets/ui/highlighting-objects/OutlineTransparency-1.jpg)_Base object_ ![The same tree mesh with a white outline and a red highlight interior.](../assets/ui/highlighting-objects/Adding-Highlight.jpg)_White outline, 50% red interior_ ![The same tree mesh with a yellow outline and a black highlight interior.](../assets/ui/highlighting-objects/Overview-Yellow-Outline.jpg)_Yellow outline, black interior_ --- title: "Light sources" url: /docs/en-us/effects/light-sources last_updated: 2026-06-29T19:34:01Z description: "Light sources simulate realistic lighting from objects such as torches, spotlights, and screens." --- # Light sources Local light sources simulate realistic lighting from objects such as lamps, torches, spotlights, and TV screens. By using the different types of light sources instead of just general global lighting through the `Class.Lighting` service, you can create immersive environments such as cyberpunk cities, traditional light festivals, and moody detective scenes. ![A far out view of a diner bulding in the middle of a nighttime city. The diner is utilizing several local light sources.](../assets/lighting-and-effects/light-sources/Showcase.jpg) > **Info:** The `Class.Lighting.LightingStyle|LightingStyle` property, modifiable only in the [Properties](/docs/en-us/studio/properties.md) window for the global `Class.Lighting` object, sets your experience's lighting style. For more information, see [Appearance](/docs/en-us/environment/lighting.md#appearance). > **Success:** To view **light guides** that indicate the color and field of effect from light sources, such as the angle of light emission from the cone's apex of a `Class.SpotLight`, open [Studio Settings](/docs/en-us/studio/setup.md#customization) and toggle on **Show Light Guides**. ## Light types Local light sources include [PointLight](#pointlight), [SpotLight](#spotlight), and [SurfaceLight](#surfacelight). Each [shares various properties](#shared-properties) from the `Class.Light` class, including `Class.Light.Color|Color`, `Class.Light.Brightness|Brightness`, and `Class.Light.Shadows|Shadows`. ### PointLight A `Class.PointLight` emits light spherically from a single point. This object is ideal for **non-directional** lights like bulbs, torches, and fireballs. To create a point light in Studio, insert a `Class.PointLight` into an `Class.Attachment` or a `Class.BasePart` (`Class.Attachment` is recommended for point‑specific light emission). Then adjust the object's `Class.PointLight.Range|Range` as well as [shared properties](#shared-properties) like `Class.PointLight.Brightness|Brightness` and `Class.PointLight.Color|Color`. #### Range A point light's `Class.PointLight.Range|Range` property defines the radial distance of illumination from the light's position, measured in studs. #### 8 ![A street lamp point light with a small range.](../assets/lighting-and-effects/light-sources/PointLight-Range-8.jpg) #### 12 ![The same street lamp point light with a larger range.](../assets/lighting-and-effects/light-sources/PointLight-Range-12.jpg) ### SpotLight A `Class.SpotLight` emits light in the shape of a cone with a spherical base. This object is ideal for directional lights like street lamps, flashlights, and headlights. To create a spotlight in Studio, insert a `Class.SpotLight` into an `Class.Attachment` or a `Class.BasePart` and set its `Class.SpotLight.Face|Face` property to specify which direction light emits from. Then adjust the `Class.SpotLight.Angle|Angle` and `Class.SpotLight.Range|Range`, as well as [shared properties](#shared-properties) like `Class.SpotLight.Brightness|Brightness` and `Class.SpotLight.Color|Color`. #### Face A spotlight's `Class.SpotLight.Face|Face` property determines which face/axis light emits from, as shown from the following streetlamp's glowing light part: #### Bottom ![A street lamp spotlight that emits light from its bottom face.](../assets/lighting-and-effects/light-sources/SpotLight-Face-Bottom.jpg) #### Left ![A street lamp spotlight that emits light from its left face.](../assets/lighting-and-effects/light-sources/SpotLight-Face-Left.jpg) #### Angle A spotlight's `Class.SpotLight.Angle|Angle` property defines the angle of light emission from the cone's apex. The maximum value is `180` which illuminates a full half sphere from the apex. #### 30 ![A street lamp spotlight with a 30 degree angle of emission.](../assets/lighting-and-effects/light-sources/SpotLight-Angle-30.jpg) #### 75 ![The same street lamp spotlight with a 75 degree angle of emission.](../assets/lighting-and-effects/light-sources/SpotLight-Angle-75.jpg) ### SurfaceLight A `Class.SurfaceLight` emits light from the face of a `Class.BasePart`. This object is ideal for lighting from computer screens, billboards, signs, and fluorescent panels. To create a surface light in Studio, insert a `Class.SurfaceLight` into a `Class.BasePart` and set its `Class.SurfaceLight.Face|Face` property to specify which surface light emits from. Then adjust the `Class.SurfaceLight.Angle|Angle` and `Class.SurfaceLight.Range|Range`, as well as [shared properties](#shared-properties) like `Class.SurfaceLight.Brightness|Brightness` and `Class.SurfaceLight.Color|Color`. #### Face A surface light's `Class.SurfaceLight.Face|Face` property determines the face of the `Class.BasePart` from which light emanates. Notice that light emits from the entire surface, not just a point on the surface. #### Bottom ![A sign surface light that emits light from its bottom face.](../assets/lighting-and-effects/light-sources/SurfaceLight-Face-Bottom.jpg) #### Right ![A sign surface light that emits light from its right face.](../assets/lighting-and-effects/light-sources/SurfaceLight-Face-Right.jpg) #### Angle A surface light's `Class.SurfaceLight.Angle|Angle` property defines the angle of light emission from the part's surface. An angle of `0` means that light travels directly outward from the surface while an angle of `180` means light travels outward perpendicular to the surface. #### 0 ![A sign surface light with a 0 degree angle of emission.](../assets/lighting-and-effects/light-sources/SurfaceLight-Angle-0.jpg) #### 60 ![A sign surface light with a 60 degree angle of emission.](../assets/lighting-and-effects/light-sources/SurfaceLight-Face-Right.jpg) ## Shared properties All light sources share various properties from the `Class.Light` class, including [color](#color), [brightness](#brightness), and [shadows](#shadows). ### Color The `Class.Light.Color|Color` property sets the `Datatype.Color3` value of the emitted light. #### [255, 100, 50] ![A torch that emits red light.](../assets/lighting-and-effects/light-sources/Light-Color-255-100-50.jpg) #### [0, 255, 125] ![A torch that emits green light.](../assets/lighting-and-effects/light-sources/Light-Color-0-255-125.jpg) #### [75, 150, 255] ![A torch that emits blue light.](../assets/lighting-and-effects/light-sources/Light-Color-75-150-255.jpg) ### Brightness The `Class.Light.Brightness|Brightness` property sets the light's brightness with maximum effect at the center of the light. Note that `Class.Light.Brightness|Brightness` is still limited to the light's defined range, so a higher `Class.Light.Brightness|Brightness` value doesn't light up a larger region around the light. #### 2 ![A torch that emits low light.](../assets/lighting-and-effects/light-sources/Light-Brightness-2.jpg) #### 10 ![A torch that emits medium light.](../assets/lighting-and-effects/light-sources/Light-Brightness-10.jpg) #### 50 ![A torch that emits bright light.](../assets/lighting-and-effects/light-sources/Light-Brightness-50.jpg) ### Shadows The `Class.Light.Shadows|Shadows` property projects shadows where light is blocked by an obstacle. #### Enabled ![A corner view of the diner with shadows enabled.](../assets/lighting-and-effects/light-sources/Light-Shadows-True.jpg) #### Disabled ![A corner view of the diner with shadows disabled.](../assets/lighting-and-effects/light-sources/Light-Shadows-False.jpg) --- title: "Particle emitters" url: /docs/en-us/effects/particle-emitters last_updated: 2026-06-29T19:34:01Z description: "Particle emitters emit 2D images to simulate special effects like fire, smoke, and sparks." --- # Particle emitters A `Class.ParticleEmitter` is an object that emits customizable 2D images (particles) into the world, useful for simulating special effects like fire, smoke, and sparks. ## Create particle emitters You can parent a `Class.ParticleEmitter` to an `Class.Attachment` or an object of the `Class.BasePart` class. When parented to a `Class.BasePart`, particles spawn randomly within the part's bounding box or [shape](#shape); when parented to an `Class.Attachment`, particles spawn from the attachment's position. Additionally, the `Class.ParticleEmitter.EmissionDirection|EmissionDirection` property determines the face (`Enum.NormalId`) of emission when the emitter is parented to a `Class.BasePart`. When you change this property, you consequently change the particle emission direction as well. > **Info:** When you rotate an emitter's parent `Class.BasePart`, the orientation of its face also changes, altering the direction of particle emission. Alternatively, if you add an emitter to an `Class.Attachment`, you can rotate the attachment itself instead of using `Class.ParticleEmitter.EmissionDirection|EmissionDirection`. To create a particle emitter on a part or attachment: 1. In the **Explorer** window, hover over the part or attachment and click the ⊕ button. A contextual menu displays. 2. From the menu, insert a **ParticleEmitter**. The particle emitter immediately emits particles within the part's area or from the attachment's position.![ParticleEmitter object as child of MeshPart in Explorer hierarchy](../assets/studio/explorer/MeshPart-ParticleEmitter.png) ## Customize particles By experimenting with the following properties, you can customize a particle's visual appearance to make unique gameplay elements like bursting volcanos, magical dust, and dust motes. > **Info:** The visual quality of your particles can change depending on the graphic settings on the player's device. To review your particles across quality levels, it's recommended to open [Studio Settings](/docs/en-us/studio/setup.md#customization), search for **Editor Quality Level**, and set it to the both the lowest and highest level after you finish customizing your particles. ### Texture The `Class.ParticleEmitter.Texture|Texture` property renders the image that each particle displays. By default, particle emitters have particles with a white sparkle texture, but you can change it to any texture to achieve interesting effects. _Three similar particle emitters with different `Class.ParticleEmitter.Texture|Texture` assets_ If you're creating an image to use as a particle texture, it's best to use `.png` format with a transparent background. If your texture is grayscale with no alpha channel, try setting the particle emitter's `Class.ParticleEmitter.LightEmission|LightEmission` property to **1** to hide the darker regions. > **Info:** For steps on how to import an image for use as a particle texture, see [here](/docs/en-us/projects/assets/manager.md#asset-import). To insert an image into a particle emitter: 1. From Studio's **Window** menu or **Home** tab toolbar, open the [Toolbox](/docs/en-us/projects/assets/toolbox.md). 2. If you want to insert an image that you have previously imported, click the **Inventory** tab. If you want to insert an image from another creator, click the **Creator Store** tab.![Inventory tab indicated in Studio's Toolbox](../assets/studio/toolbox/Inventory-Tab.png)![Creator Store tab indicated in Studio's Toolbox](../assets/studio/toolbox/Creator-Store-Tab.png) 3. Right-click on the image you want to insert into a particle emitter and select **Copy Asset ID**. 4. In the **Explorer** window, select a `Class.ParticleEmitter`. 5. In the **Properties** window, paste the asset ID into the **Texture** property. ### Color The `Class.ParticleEmitter.Color|Color` property tints each particle's texture to either a specific hue, or to a gradient `Datatype.ColorSequence` across its [lifetime](#lifetime). #### Constant Color 1. In the **Explorer** window, select the `Class.ParticleEmitter`. 2. In the **Properties** window, select the **Color** property. You can either: 1. Click on the color square to open the **Colors** pop-up window and select a color. 2. Input three numbers into the RGB color value field.![Similar particle emitters with different colored particles](../assets/lighting-and-effects/particle-emitter/Color-Examples.jpg)_Similar particle emitters with different colored particles_ #### Color Gradient 1. In the **Explorer** window, select the `Class.ParticleEmitter`. 2. In the **Properties** window, click inside the **Color** property field, then click the **⋯** button.![Button to open color sequence popup](../assets/studio/properties/Color-Open-Sequence.png) In the color sequence popup, each triangle on the bottom axis is a **keypoint** that determines the color value of the property at that point in the particle's lifetime.![Keypoints labeled in color sequence popup](../assets/studio/general/ColorSequence-White-Keypoints-Labeled.png) 3. Click the keypoint at the start of the color sequence, click the small square next to **Color**, and select a color from the popup window. ![Color sequence popup from red to white](../assets/studio/general/ColorSequence-Red-White.png) 1. If you want particles to change their color near the end of their lifetime, click the keypoint at the end of the color sequence, click the small square next to **Color**, and select a color from the popup window.![Color sequence popup from red to purple](../assets/studio/general/ColorSequence-Red-Purple.png) 2. For more customization, you can: - Add another keypoint by clicking anywhere on the graph. - Make a color change sooner or later within the gradient by dragging an intermediary keypoint to a new position. - Delete an intermediary keypoint by selecting it and clicking the **Delete** button. - Reset the entire sequence by clicking the **Reset** button. ### Size The `Class.ParticleEmitter.Size|Size` property sets the size of each particle to a consistent size, or to a `Datatype.NumberSequence` across its [lifetime](#lifetime). #### Constant Size 1. In the **Explorer** window, select the `Class.ParticleEmitter`. 2. In the **Properties** window, select the **Size** property. 3. Input the size that you want each particle to be. #### Number Sequence 1. In the **Explorer** window, select the `Class.ParticleEmitter`. 2. In the **Properties** window, click inside the **Size** property field, then click the **⋯** button.![Button to open number sequence popup](../assets/studio/properties/Size-Open-Sequence.png) In the number sequence popup, each square at the start and end of the graph is a **keypoint** that determines the size value of the property at that point in the particle's lifetime. By default, the graph defaults to a straight line and a particle remains the same size throughout its lifetime.![Keypoints labeled in number sequence popup](../assets/studio/general/NumberSequence-0-0-Keypoints-Labeled.png) 3. Perform any of the following actions: - To change the size at a point, click on a keypoint and either drag it up or down, or enter a value in the **Value** field. - To insert a new keypoint, click on any point in the graph. - To delete a keypoint, select it and click the **Delete** button. - Reset the entire sequence by clicking the **Reset** button. - To add a random range for size, click on any keypoint and drag the **envelope lines** up or down. At that time, particles generate at a random size between the pink envelope.![Envelope lines indicated in number sequence popup](../assets/studio/general/NumberSequence-Envelope.png)_Emitter with large size envelope toward end of its lifetime_ > **Warning:** Particle size can impact performance due to fill-rate. The more pixels particles occupy on a player's screen, the more costly it is on the GPU. ### Transparency The `Class.ParticleEmitter.Transparency|Transparency` property sets the opacity of each particle as a consistent value, or as a `Datatype.NumberSequence` opacity across its [lifetime](#lifetime). Opacity can range from **0** (totally opaque) to **1** (fully clear). For details on how to set particles to either a specific opacity or to a number sequence, follow the process in customizing [Size](#size) but use the emitter's **Transparency** property field instead. > **Success:** The `Class.ParticleEmitter.Transparency|Transparency` property is one of the most vital properties of a particle emitter. Fading particles near the start and/or end of their lifetime avoids a popping in/out effect and provides a more realistic effect. _Static transparency vs. fading in/out_ ### Lifetime The `Class.ParticleEmitter.Lifetime|Lifetime` property sets the lifetime of a particle in seconds. You can set this property either as a consistent value, or provide a **Min** and **Max** range from which a random lifetime will be chosen for each particle. > **Info:** Particles have a maximum lifetime of 20 seconds. `Class.ParticleEmitter.Lifetime|Lifetime` values higher than this will be internally capped at 20. ### Speed The `Class.ParticleEmitter.Speed|Speed` property determines a random range of velocities (minimum to maximum) at which new particles will emit, measured in studs per second. Each particle's velocity is chosen upon emission and it applies in the `Class.ParticleEmitter.EmissionDirection|EmissionDirection`. Negative values cause particles to travel in reverse. Note that changing `Class.ParticleEmitter.Speed|Speed` does not affect active particles and they retain whatever speed they already have. However, `Class.ParticleEmitter.Acceleration|Acceleration`, `Class.ParticleEmitter.Drag|Drag`, and `Class.ParticleEmitter.VelocityInheritance|VelocityInheritance` can be used to affect the speed of active particles over their lifetime. ### Rate The `Class.ParticleEmitter.Rate|Rate` property sets the number of particles that emit per second. A single particle emitter can create up to 400 particles per second (100 per second on mobile). For best performance, keep the particle rate as low as possible and experiment with [size](#size) and [other properties](#other-properties) to achieve the desired visual effect. > **Warning:** Particle count can impact performance due to overdraw, especially when particles are overlapping. The more layers of transparent effects on screen, the more costly it is on the GPU. ### Orientation The `Class.ParticleEmitter.Orientation|Orientation` property determines which orientation mode to use for an emitter's particle geometry. | Orientation | Particle behavior | | --- | --- | | **FacingCamera** | Standard camera-facing billboard quad; default behavior. | | **FacingCameraWorldUp** | Facing the camera, but rotating only on the vertical upward world **Y** axis. | | **VelocityParallel** | Aligned parallel to their direction of movement. | | **VelocityPerpendicular** | Aligned perpendicular to their direction movement. | _Expected outcome of particle orientation_ ### LightEmission The `Class.ParticleEmitter.LightEmission|LightEmission` property determines the blending of particle `Class.ParticleEmitter.Texture|Texture` colors with the colors behind them. A value of 0 uses normal blending mode while a value of 1 uses additive blending. Higher values can make particles glow even in environments with low lighting. ### SpreadAngle The `Class.ParticleEmitter.SpreadAngle|SpreadAngle` property has an **X** and a **Y** value which determine the range of angles from which a particle can emit. The range is calculated from both sides around the axes; for example, a value of `(45, 0)` emits particles in a range of 0° to 45° away from the `Class.ParticleEmitter.EmissionDirection|EmissionDirection` across the **X** axis. ### WindAffectsDrag If you've enabled [global wind](/docs/en-us/environment/global-wind.md) in an experience, particles will follow the global wind vector as long as the emitter's `Class.ParticleEmitter.WindAffectsDrag|WindAffectsDrag` property is enabled and its `Class.ParticleEmitter.Drag|Drag` property is greater than 0. ![Drag and WindAffectsDrag properties shown in Properties window of Studio](../assets/studio/properties/ParticleEmitter-WindAffectsDrag.png) ### Shape The `Class.ParticleEmitter.Shape|Shape` property sets the shape of the particle emitter to either a **Box**, **Sphere**, **Cylinder**, or **Disc**. ![Emitter of Box shape](../assets/lighting-and-effects/particle-emitter/Shape-Box.jpg)_Box_ ![Emitter of Sphere shape](../assets/lighting-and-effects/particle-emitter/Shape-Sphere.jpg)_Sphere_ ![Emitter of Cylinder shape](../assets/lighting-and-effects/particle-emitter/Shape-Cylinder.jpg)_Cylinder_ ![Emitter of Disc shape](../assets/lighting-and-effects/particle-emitter/Shape-Disc.jpg)_Disc_ After you select a shape for your particle emitter, you can experiment with the [ShapeStyle](#shapestyle), [ShapeInOut](#shapeinout), and [ShapePartial](#shapepartial) properties to further customize particle emission. > **Warning:** If you parent a particle emitter to an `Class.Attachment`, the **Sphere** and **Cylinder** shapes will not display correctly. These shapes should only be used if the emitter is parented to a `Class.BasePart`. #### ShapeStyle The `Class.ParticleEmitter.ShapeStyle|ShapeStyle` property sets the emission type to either: - **Volume** — Particles emit anywhere within the shape. - **Surface** — Particles only emit from the outside of the shape. ![Emitter of Cylinder shape with Volume shape style](../assets/lighting-and-effects/particle-emitter/Cylinder-Volume.jpg)_Cylinder + Volume_ ![Emitter of Cylinder shape with Surface shape style](../assets/lighting-and-effects/particle-emitter/Cylinder-Surface.jpg)_Cylinder + Surface_ #### ShapeInOut The `Class.ParticleEmitter.ShapeInOut|ShapeInOut` property sets the emission as follows: - **Inward** — Particles emit toward the shape. - **Outward** — Particles emit away from the shape. - **InAndOut** — Particles randomly behave as both **Inward** and **Outward**. #### ShapePartial The `Class.ParticleEmitter.ShapePartial|ShapePartial` property is a factor you can use to further modify **Cylinder**, **Disc**, and **Sphere** shapes. #### Cylinder For cylinders, `Class.ParticleEmitter.ShapePartial|ShapePartial` multiplies the radius of the cylinder on the side of its **EmissionDirection**. ![Emitter of Cylinder shape with ShapePartial value of 0.5](../assets/lighting-and-effects/particle-emitter/Cylinder-ShapePartial-0.5.jpg)_ShapePartial = 0.5_ ![Emitter of Cylinder shape with ShapePartial value of 0](../assets/lighting-and-effects/particle-emitter/Cylinder-ShapePartial-0.jpg)_ShapePartial = 0_ #### Disc For discs, `Class.ParticleEmitter.ShapePartial|ShapePartial` inversely multiplies the **inner radius** of the disc. ![Emitter of Disc shape with ShapePartial value of 0.5](../assets/lighting-and-effects/particle-emitter/Disc-ShapePartial-0.5.jpg)_ShapePartial = 0.5_ ![Emitter of Disc shape with ShapePartial value of 0.1](../assets/lighting-and-effects/particle-emitter/Disc-ShapePartial-0.1.jpg)_ShapePartial = 0.1_ #### Sphere For spheres, `Class.ParticleEmitter.ShapePartial|ShapePartial` multiplies the **hemispherical angle**. ![Emitter of Sphere shape with ShapePartial value of 1](../assets/lighting-and-effects/particle-emitter/Shape-Sphere.jpg)_ShapePartial = 1_ ![Emitter of Sphere shape with ShapePartial value of 0.5](../assets/lighting-and-effects/particle-emitter/Sphere-ShapePartial-0.5.jpg)_ShapePartial = 0.5_ ### Squash The `Class.ParticleEmitter.Squash|Squash` property allows for non-uniform scaling of particles, curve-controlled over their lifetime. Values greater than 0 cause particles to both shrink horizontally and grow vertically, while values less than 0 cause particles to both grow horizontally and shrink vertically. For details on how to set the squash amount to either a constant or to a `Datatype.NumberSequence`, follow the process in customizing [Size](#size) but use the emitter's **Squash** property field instead. ### Flipbooks Particle flipbook textures let you animate a particle's texture over its lifetime. The flipbook texture can have a frame layout of 2×2, 4×4, 8×8, or custom. For example, the following 1024×1024 image has an 8×8 layout, so it's suitable for a 64‑frame animation. ![Sample texture for particle flipbooks](../assets/lighting-and-effects/particle-emitter/8x8-Explosion.png) > **Error:** When you create a flipbook texture, include spacing between each of the particle frames. In some cases, mip filtering might require even more spacing. The previous image has transparent spacing around each frame for the flipbook texture. > **Warning:** Features with custom textures such as flipbooks cost memory to render. If you use many flipbooks with other features that use high memory, then clients automatically deactivate flipbooks when they are low on memory, which is likely for older mobile phones. To reduce the memory usage of flipbooks, use fewer unique animated particle effects or choose a texture of smaller resolution when possible. Reusing textures costs less memory than using unique textures. Once you've specified a valid flipbook [texture](#texture) for the emitter's `Class.ParticleEmitter.Texture|Texture` property, the `Class.ParticleEmitter.FlipbookLayout|FlipbookLayout` property determines the layout of the texture. It can be any value of the `Enum.ParticleFlipbookLayout` enum: - `Enum.ParticleFlipbookLayout|None` — Disable flipbook features and use the texture as a single static texture over the particle's lifetime. - `Enum.ParticleFlipbookLayout|Grid2x2` — 2×2 frames for a 4-frame animation. - `Enum.ParticleFlipbookLayout|Grid4x4` — 4×4 frames for a 16-frame animation. - `Enum.ParticleFlipbookLayout|Grid8x8` — 8×8 frames for a 64-frame animation. - `Enum.ParticleFlipbookLayout|Custom` — A custom grid size defined by `Class.ParticleEmitter.FlipbookSizeX|FlipbookSizeX` and `Class.ParticleEmitter.FlipbookSizeY|FlipbookSizeY`. To further customize the flipbook behavior, you can adjust the following properties: #### FlipbookFramerate The `Class.ParticleEmitter.FlipbookFramerate|FlipbookFramerate` property determines how fast the flipbook texture animates in frames per second. Like `Class.ParticleEmitter.Lifetime|Lifetime`, you can set a minimum and maximum range to randomize the framerate of the flip book, with a maximum of 30 frames per second. #### FlipbookMode The `Class.ParticleEmitter.FlipbookMode|FlipbookMode` property determines the type of the flipbook animation. The property can be any value of the `Enum.ParticleFlipbookMode` enum: - **Loop** — Continuously play through all frames, starting back at the first frame after playing the last. - **OneShot** — Play through the animation only once across the particle's lifetime. With this setting, the `Class.ParticleEmitter.FlipbookFramerate|FlipbookFramerate` property doesn't apply; instead, the framerate is determined by the particle's `Class.ParticleEmitter.Lifetime|Lifetime` divided evenly by the number of frames in the animation. **OneShot** animations are useful for clear non-repeating animations, such as an explosion that creates a puff of smoke and then fades out. - **PingPong** — Play from the first to the last frame, then in reverse from the last to the first, repeating throughout the `Class.ParticleEmitter.Lifetime|Lifetime` of the particle. - **Random** — Play the frames in a random order, blending/crossfading from one frame to the next. This can be useful for organic particle textures at low framerates, such as stars slowly twinkling between subtly different shapes. #### FlipbookStartRandom The `Class.ParticleEmitter.FlipbookStartRandom|FlipbookStartRandom` property determines whether each particle begins at a random frame of the animation instead of always starting at the first frame. One use case is to enable this property and also set `Class.ParticleEmitter.FlipbookFramerate|FlipbookFramerate` to zero, causing each emitted particle to be a static frame chosen randomly from the flipbook texture. ### Other properties To further customize particles, consider the following emitter properties, and click through to the `Class.ParticleEmitter` reference page for more details. #### Appearance The following appearance properties are in addition to [color](#color), [light emission](#lightemission), [orientation](#orientation), [size](#size), [texture](#texture), [transparency](#transparency), [squash](#squash), and [flipbooks](#flipbooks). | Property | Description | | --- | --- | | `Class.ParticleEmitter.LightInfluence\|LightInfluence` | Determines how much environmental light affects the color of individual particles. | | `Class.ParticleEmitter.Brightness\|Brightness` | Scales the light emitted from the emitter when `Class.ParticleEmitter.LightInfluence\|LightInfluence` is 0. | | `Class.ParticleEmitter.ZOffset\|ZOffset` | Determines the forward-backward render position of particles, in studs, without changing their size on the screen. This allows for multiple emitters to be layered, or to render particles in front of or behind the parent object. | #### Emission The following emission properties are in addition to [emission direction](#create-particle-emitters), [lifetime](#lifetime), [speed](#speed), [rate](#rate), [spread angle](#spreadangle), and [shape](#shape). | Property | Description | | --- | --- | | `Class.ParticleEmitter.Enabled\|Enabled` | Determines if particles emit from the emitter. Setting this to `false` stops further particles from spawning, but any existing particles remain active until they expire or the `Class.ParticleEmitter:Clear()\|Clear()` method is called. | | `Class.ParticleEmitter.Rotation\|Rotation` | Determines the angle of rotation for newly-emitted particles. Single numbers create particles at that angle, while two numbers (minimum, maximum) set a random rotation for each particle. | | `Class.ParticleEmitter.RotSpeed\|RotSpeed` | Determines the angular speed in degrees per second for particles. This can be a single number or a number range for a randomized speed. Negative values cause particles to rotate counter-clockwise. | #### Motion The following motion properties are in addition to [wind influence](#windaffectsdrag). | Property | Description | | --- | --- | | `Class.ParticleEmitter.Acceleration\|Acceleration` | Determines by how much the velocity of particles changes per second. You can use this property to create effects such as particles being affected by gravity. | | `Class.ParticleEmitter.Drag\|Drag` | Determines the rate in seconds that particles lose half their speed. | | `Class.ParticleEmitter.LockedToPart\|LockedToPart` | Determines if particles will "stick" to the emission source to which the emitter is parented. | | `Class.ParticleEmitter.TimeScale\|TimeScale` | A value between 0 and 1 that controls the speed of the particle effect. | | `Class.ParticleEmitter.VelocityInheritance\|VelocityInheritance` | Determines how much of the parent object's velocity is inherited by particles when they are emitted. | --- title: "Trails" url: /docs/en-us/effects/trails last_updated: 2026-06-29T19:34:01Z description: "Trails create visual motion effects as an object moves through space." --- # Trails A `Class.Trail` is an object that creates a trail between and behind two `Class.Attachment` objects as they move through space. Trails are useful to help players visualize movement, such as a sword slashing through the air, projectiles flying to their target, or footprints walking away. After you [create a trail](#create-trails), you can: - Add a [texture](#texture) to create interesting visuals. - Set a constant or gradient [color](#color) and/or [transparency](#transparency). - Modify a trail's [lifetime](#lifetime). - Specify the texture's [length and mode](#texture-lengthmode) to affect how it scales, repeats, and moves along with the trail's attachments. ## Create trails Before you begin to create a trail, it's useful to toggle on visibility of attachments so you can more easily discern how their spacing affects the trail's width. You can do so by enabling **Show Constraint Details** from Studio's **View** menu. The following video demonstrates how attachments that are closer to each other create a trail with a smaller width: To create a trail on a part: 1. In the **Explorer** window, insert two `Class.Attachment|Attachments` and a `Class.Trail` object into the part. 1. Hover over the part and click the **⊕** button. A contextual menu displays. 2. From the menu, insert two **Attachments** and one **Trail**. 2. Select the new **Trail** object and assign its attachments. Ensure you assign each attachment property to a different `Class.Attachment` object. 1. In the **Properties** window, select the **Attachment0** property. Your cursor changes. 2. In the **Explorer** window, select the first attachment you created. 3. Back in the **Properties** window, select the **Attachment1** property. Your cursor changes. 4. In the **Explorer** window, select the second attachment you created. 3. Using the [Move](/docs/en-us/parts.md#move) tool, position both attachments within the part according to how wide you want the trail to be. 4. To see the trail, move the part in any direction and the trail follows. > **Warning:** Trails require attachments to function properly. If you remove either `Class.Attachment` object from step 2, the trail stops rendering its texture. ## Customize trails By experimenting with the following properties, you can customize a trail's visual appearance to make unique gameplay elements like wind gusts, sword slashes, and tire marks from fast cars. > **Info:** The visual quality of your trails can change depending on the graphics settings on the player's device. To review your trail across quality levels, it's recommended to open [Studio Settings](/docs/en-us/studio/setup.md#customization), search for **Editor Quality Level**, and set it to the both the lowest and highest level after you finish customizing your trail. ### Texture The `Class.Trail.Texture|Texture` property renders a texture across the length of the trail. You can set a trail's `Class.Trail.Texture|Texture` property to any asset ID. For more information, including how to add or import your own textures, see [here](/docs/en-us/projects/assets/manager.md#asset-import). ### Color The `Class.Trail.Color|Color` property tints the trail's texture to either a specific hue or a `Datatype.ColorSequence`. The video below shows how a constant color compares with a gradient color across the trail's [lifetime](#lifetime). #### Constant Color 1. In the **Explorer** window, select the trail. 2. In the **Properties** window, select the **Color** property. You can either: 1. Click on the color square to open the **Colors** pop-up window and select a color. 2. Input three numbers into the RGB color value field. #### Color Gradient 1. In the **Explorer** window, select the trail. 2. In the **Properties** window, click inside the **Color** property field, then click the **⋯** button.![Button to open color sequence popup](../assets/studio/properties/Color-Open-Sequence.png) In the color sequence popup, each triangle on the bottom axis is a **keypoint** that determines the color value of the trail at that point in its lifetime.![Keypoints labeled in color sequence popup](../assets/studio/general/ColorSequence-White-Keypoints-Labeled.png) 3. Click the keypoint at the start of the color sequence, click the small square next to **Color**, and select a color from the popup window.![Color sequence popup from red to white](../assets/studio/general/ColorSequence-Red-White.png) 4. If you want the trail to change color near the end of its lifetime, click the keypoint at the end of the color sequence, click the small square next to **Color**, and select a color from the popup window.![Color sequence popup from red to purple](../assets/studio/general/ColorSequence-Red-Purple.png) 5. For more customization, you can: - Add another keypoint by clicking anywhere on the graph. - Make a color change sooner or later within the gradient by dragging an intermediary keypoint to a new position. - Delete an intermediary keypoint by selecting it and clicking the **Delete** button. - Reset the entire sequence by clicking the **Reset** button. ### Transparency The `Class.Trail.Transparency|Transparency` property sets the transparency of the trail's segments over its `Class.Trail.Lifetime|Lifetime`. It can be a consistent value or a `Datatype.NumberSequence` with ranges from **0** (totally opaque) to **1** (fully clear). #### Constant Opacity 1. In the **Explorer** window, select the trail. 2. In the **Properties** window, select the **Transparency** property. 3. Input the desired opacity for the trail. #### Number Sequence 1. In the **Explorer** window, select the trail. 2. In the **Properties** window, click inside the **Transparency** property field, then click the **⋯** button.![Button to open number sequence popup](../assets/studio/properties/Transparency-Open-Sequence.png) In the number sequence popup, each square at the start and end of the graph is a **keypoint** that determines the opacity value of the trail at that point along its span. By default, the graph defaults to a straight line and the trail is of consistent opacity.![Keypoints labeled in number sequence popup](../assets/studio/general/NumberSequence-0-0-Keypoints-Labeled.png) 3. Perform any of the following actions: - To change the opacity at a point, click on a keypoint and either drag it up or down, or enter a value in the **Value** field. - To insert a new keypoint, click on any point in the graph. - To delete a keypoint, select it and click the **Delete** button. - Reset the entire sequence by clicking the **Reset** button.![Number sequence popup from 0 to 1 to 0](../assets/studio/general/NumberSequence-0-1-0.png) ### Lifetime The `Class.Trail.Lifetime|Lifetime` property allows you to set the duration of the trail in seconds. When you decrease the value of this property, the trail moves through the space more quickly and consequently cuts down the length of the trail. This can provide the object with a faster, more animated visual appearance. _Trail with lifetime of 0.5 (left) vs. trail with lifetime of 3.0 (right)_ ### Texture length/mode A trail's `Class.Trail.TextureLength|TextureLength` and `Class.Trail.TextureMode|TextureMode` determine how its [texture](#texture) scales, repeats, and moves along with the trail's attachments. #### Scaling and Repetition When `Class.Trail.TextureMode|TextureMode` is set to `Enum.TextureMode.Wrap` or `Enum.TextureMode.Static`, the `Class.Trail.TextureLength|TextureLength` property sets the length of the texture as it repeats across the trail's length. ![TextureMode diagram with Wrap mode](../assets/engine-api/enums/TextureMode/Wrap-Static.png) When `Class.Trail.TextureMode|TextureMode` is set to `Enum.TextureMode.Stretch`, the texture will repeat `Class.Trail.TextureLength|TextureLength` times across the trail's overall length. ![TextureMode diagram with Stretch mode](../assets/engine-api/enums/TextureMode/Stretch.png) #### Movement The `Class.Trail.TextureMode|TextureMode` property also affects the **movement** of the trail's texture as follows: - If set to `Enum.TextureMode.Stretch`, the texture will stretch out based on the lifetime of the trail, and shrink inwards if the trail's attachments stop moving. - If set to `Enum.TextureMode.Wrap`, the texture will be tiled as the length of the trail changes, but the textures will remain stationary relative to their attachments. - If set to `Enum.TextureMode.Static`, the texture will be rolled out as the attachments move, and they will remain in place until their lifetime is met. This setting is ideal for trail textures that should appear "stamped" where rendered, such as paw prints or tire tracks. ### Facing A trail is a 2D projection existing in 3D space, meaning that it may not be visible from every angle. The `Class.Trail.FaceCamera|FaceCamera` property, when set to `true`, ensures that the trail always faces the `Class.Workspace.CurrentCamera|CurrentCamera`, regardless of its orientation. --- title: "Atmospheric effects" url: /docs/en-us/environment/atmosphere last_updated: 2026-06-29T19:34:01Z description: "Atmospheric effects simulate realistic environments by scattering sunlight in unique ways." --- # Atmospheric effects **Atmospheric effects** simulate realistic environments by scattering sunlight in unique ways based on properties that control air particles. Using the `Class.Atmosphere` object, you can: - Control particle density. - Create a silhouette or blend distant objects with an offset. - Simulate a haze or glare. - Set an atmosphere's color or decay. ![Atmospheric effects used to render a brilliant sunset scene](../assets/lighting-and-effects/atmosphere/Sahara-Sunset.jpg) ## Atmosphere properties Every property of the `Class.Atmosphere` object inside the `Class.Lighting` service works together to support the overall vision, themes, and mood of your experience. ![Atmosphere object shown in Explorer window of Studio](../assets/studio/explorer/Lighting-Atmosphere.png) ### Density The `Class.Atmosphere.Density|Density` property controls how many particles exist in the air of your experience; the higher the density, the more particles, which obstruct a player's view of objects and terrain. #### 0 ![Atmosphere with Density value of 0](../assets/lighting-and-effects/atmosphere/Density-A.jpg) #### 0.35 ![Atmosphere with Density value of 0.35](../assets/lighting-and-effects/atmosphere/Density-B.jpg) > **Info:** Density only **directly** affects objects and terrain; however, since you view a [skybox](/docs/en-us/environment/skybox.md) through those objects and terrain, the visibility of a skybox is also affected. ### Offset The `Class.Atmosphere.Offset|Offset` property controls how light transmits between the camera and the sky background. When you increase this value, it creates a horizon silhouette; when you decrease this value, it blends distant objects into the sky for a seemingly endless and seamless open world. #### 0 ![Atmosphere with Offset value of 0](../assets/lighting-and-effects/atmosphere/Offset-A.jpg) #### 1 ![Atmosphere with Offset value of 1](../assets/lighting-and-effects/atmosphere/Offset-B.jpg) > **Warning:** Carefully balance the `Class.Atmosphere.Offset|Offset` property with the `Class.Atmosphere.Density|Density` property. A low offset value may cause the [skybox](/docs/en-us/environment/skybox.md) to be seen through objects and terrain, and a high offset value may cause distant objects and terrain to have too much detail for your desired lighting effect. ### Haze The `Class.Atmosphere.Haze|Haze` property controls the haziness of the atmosphere to create a visible effect both above the horizon and far into the distance from the camera. To create environmental moods, like a smoky tint for a polluted alien planet or a foggy blue tint for a somber experience, combine this property with the [Color](#color) property. #### 1 ![Atmosphere with Haze value of 1](../assets/lighting-and-effects/atmosphere/Haze-A.jpg) #### 2.8 ![Atmosphere with Haze value of 2.8](../assets/lighting-and-effects/atmosphere/Haze-B.jpg) ### Color The `Class.Atmosphere.Color|Color` property sets the hue of the atmosphere for subtle environmental moods and themes. To expand this property's visible effect, combine it with a high [Haze](#haze) property value. #### [255, 255, 255] ![Atmosphere with Color value of [255, 255, 255]](../assets/lighting-and-effects/atmosphere/Color-A.jpg) #### [255, 200, 255] ![Atmosphere with Color value of [255, 200, 255]](../assets/lighting-and-effects/atmosphere/Color-B.jpg) ### Glare The `Class.Atmosphere.Glare|Glare` property sets an atmospheric glare around the sun. A high value results in an increased effect of sunlight cast onto the sky and experience. To see this property's visible effect, you **must** combine a glare with a [Haze](#haze) value higher than 0. #### 0 ![Atmosphere with Glare value of 0](../assets/lighting-and-effects/atmosphere/Glare-A.jpg) #### 1 ![Atmosphere with Glare value of 1](../assets/lighting-and-effects/atmosphere/Glare-B.jpg) ### Decay The `Class.Atmosphere.Decay|Decay` property sets the hue of the atmosphere away from the sun, gradually falling off from [Color](#color) toward this value. To see this property's visible effect, you **must** combine a glare with [Haze](#haze) and [Glare](#glare) values higher than 0. #### [255, 255, 255] ![Atmosphere with Decay value of [255, 255, 255]](../assets/lighting-and-effects/atmosphere/Decay-A.jpg) #### [255, 90, 80] ![Atmosphere with Decay value of [255, 90, 80]](../assets/lighting-and-effects/atmosphere/Decay-B.jpg) --- title: "Dynamic clouds" url: /docs/en-us/environment/clouds last_updated: 2026-06-29T19:34:01Z description: "Dynamic Clouds render realistic, customizable clouds that drift slowly across the sky." --- # Dynamic clouds Roblox's **dynamic clouds** are realistic clouds that drift slowly across the sky. You can adjust their appearance through the `Class.Clouds` object to create unique atmospheres such as stormy skies, stunning sunsets, and alien worlds. You can also customize their direction and speed through [global wind](/docs/en-us/environment/global-wind.md). ## Enable clouds You can manage dynamic clouds in an experience through the `Class.Clouds` object. While you can place this object anywhere for organization or replication purposes, clouds only render if you parent the object under the `Class.Terrain` class. To enable dynamic clouds: 1. In the **Explorer** window, hover over **Terrain** and click the ⊕ button. A contextual menu displays. 2. From the menu, insert **Clouds**.![Clouds object shown in Explorer window of Studio](../assets/studio/explorer/Terrain-Clouds.png) 3. Adjust the appearance of clouds through the new object's properties. 4. Set the clouds in motion through [global wind](/docs/en-us/environment/global-wind.md). ## Cloud properties From the `Class.Clouds` object under `Class.Terrain`, you can adjust the appearance of clouds through the **Properties** window. ### Cover The `Class.Clouds.Cover|Cover` property controls how much the clouds span across the overall skyscape layer from a value of 0 (sparse cloud cover) to 1 (full cloud cover). #### 0.65 ![Clouds with Cover value of 0.65](../assets/lighting-and-effects/clouds/Cover-A.jpg) #### 0.8 ![Clouds with Cover value of 0.8](../assets/lighting-and-effects/clouds/Cover-B.jpg) ### Density The `Class.Clouds.Density|Density` property controls the intensity of the particles that make up each cloud, mainly affecting their transparency. For example, lower values produce light, semi-translucent clouds, and higher values produce heavy, dark clouds with a stormy appearance. #### 0.1 ![Clouds with Density value of 0.05](../assets/lighting-and-effects/clouds/Density-A.jpg) #### 0.3 ![Clouds with Density value of 0.4](../assets/lighting-and-effects/clouds/Density-B.jpg) ### Color The `Class.Clouds.Color|Color` property controls the material color of cloud particles. It's important to note that several `Class.Lighting` and `Class.Atmosphere` properties also influence cloud color, so if you want to simulate specific atmospheres, experiment with multiple properties until you have the effect you want. #### [255, 255, 255] ![Clouds with Color value of [255, 255, 255]](../assets/lighting-and-effects/clouds/Color-A.jpg) #### [75, 50, 255] ![Clouds with Color value of [75, 50, 255]](../assets/lighting-and-effects/clouds/Color-B.jpg) --- title: "Global wind" url: /docs/en-us/environment/global-wind last_updated: 2026-06-29T19:34:01Z description: "The global wind vector sets the direction and strength that wind blows through an experience, affecting terrain grass, dynamic clouds, and particles." --- # Global wind The `Class.Workspace.GlobalWind|GlobalWind` vector sets the direction and strength that wind blows through an experience, affecting [terrain grass](/docs/en-us/parts/terrain.md#grass-animation) and [dynamic clouds](/docs/en-us/environment/clouds.md). You can set it as a [constant vector](#global-wind-vector), or adjust it through [scripting](#scripted-effects) to create cyclical gusts of wind. Additionally, you can influence [particles](#particle-influence) to follow the global wind vector. ## Global wind vector Global wind is controlled through The `Class.Workspace.GlobalWind|GlobalWind` vector is a property of `Class.Workspace` and you can edit it directly in Studio, or set it through [scripting](#scripted-effects). To set the global wind vector in Studio: 1. In the **Explorer** window, select the top-level `Class.Workspace` service.![Workspace object shown in Explorer window of Studio](../assets/studio/explorer/Workspace.png) 2. In the **Properties** window, locate the `Class.Workspace.GlobalWind|GlobalWind` property and set an **X**, **Y**, and **Z** value for its direction and strength.![GlobalWind property shown in Properties window of Studio](../assets/studio/properties/Workspace-GlobalWind.png) > **Info:** Note that the speed of [animated grass](/docs/en-us/parts/terrain.md#grass-animation) — but not its vector direction — will be reduced if the player has toggled on the **Reduce Motion** [accessibility](/docs/en-us/production/publishing/accessibility.md#reduced-motion) setting from the Roblox or in‑experience **Settings** menu. ## Particle Influence Particles emitted by a `Class.ParticleEmitter` will follow the global wind vector as long as the emitter's `Class.ParticleEmitter.WindAffectsDrag|WindAffectsDrag` property is enabled and its `Class.ParticleEmitter.Drag|Drag` property is greater than `0`. `Class.Fire` and `Class.Smoke` instances follow the wind vector by default. ![Drag and WindAffectsDrag properties shown in Properties window of Studio](../assets/studio/properties/ParticleEmitter-WindAffectsDrag.png) ## Wind direction widget To make it easier to tune global wind, you can use the **Wind Direction** widget, accessible from Studio's **View** menu. The widget lets you visualize how wind is blowing using a wind sock model, and you can dynamically set the wind's **Speed**, **Yaw**, and **Pitch** by clicking the desired aspect name and sliding the slider along the bottom, or you can adjust yaw or pitch by manipulating the green ring and blue arrow on the animated portion. You can also click and drag the widget to reposition it anywhere in the 3D viewport. ![Wind Direction widget showing in 3D viewport of Studio](../assets/lighting-and-effects/aero-fluid-dynamics/Wind-Direction-Widget.jpg) ## Scripted effects Scripting of the `Class.Workspace.GlobalWind|GlobalWind` property opens up a whole range of possibilities. For example, you can use the following code sample to cause cyclical gusts of wind that ease in and out using the `Library.math.sin()` function. ```lua local gustCycleDelay = 5 -- Max duration between gust cycles in seconds local gustCycleDuration = 3.5 -- Duration of each gust cycle in seconds -- During each gust cycle, a portion of "gust" will be added to "baseWind" in a ramped fashion local baseWind = Vector3.new(5, 0, 2) -- Base wind speed and direction local gust = Vector3.new(25, 0, 10) -- Gust speed and direction local gustIntervals = 100 -- Number of iterations used to calculate each gust interval local dg = gustCycleDuration / gustIntervals local dgf = dg / gustCycleDuration -- Set global wind to base wind initially workspace.GlobalWind = baseWind -- Wait delay amount before starting gusts task.wait(gustCycleDelay) while true do for i = 1, gustIntervals do local f = math.sin(math.pi * dgf * i) -- Use sin() function to ramp gust workspace.GlobalWind = baseWind + f * gust -- Set global wind to base wind + gust task.wait(dg) end workspace.GlobalWind = baseWind -- Reset global wind to base wind at end of gust cycle task.wait(math.random() * gustCycleDelay) -- Wait a random fraction of delay before next gust cycle end ``` --- title: "Lighting and effects" url: /docs/en-us/environment last_updated: 2026-06-29T19:34:01Z description: "Create more immersive environments through lighting, atmospheres, and special effects." --- # Lighting and effects The `Class.Lighting` container services let you control and customize an experience's environment such as [lighting](#global-lighting), [atmosphere](#atmospheric-effects), and [clouds](#clouds-and-skies). You can also apply [post-processing effects](#post-processing-effects) to adjust how the experience appears on the screen. ## Global lighting The `Class.Lighting` service contains properties that you can adjust to update the global lighting in an experience, such as the `Class.Lighting.ClockTime|ClockTime` and `Class.Lighting.Brightness|Brightness`. #### ClockTime = 0 ![Lighting with ClockTime of 0 (TimeOfDay of 00:00:00)](../assets/lighting-and-effects/lighting-properties/TimeOfDay-0.jpg) #### ClockTime = 6.3 ![Lighting with ClockTime of 6.3 (TimeOfDay of 06:18:00)](../assets/lighting-and-effects/lighting-properties/TimeOfDay-6.3.jpg) #### Brightness = 0.5 ![Lighting with Brightness property of 0.5](../assets/lighting-and-effects/lighting-properties/Brightness-0.5.jpg) #### Brightness = 3.75 ![Lighting with Brightness property of 3.75](../assets/lighting-and-effects/lighting-properties/Brightness-3.75.jpg) > **Info:** In addition to global lighting, you can create and attach [light sources](/docs/en-us/effects/light-sources.md) to parts or attachments to simulate objects like lamps, torches, spotlights, or TV screens. ## Atmospheric effects [Atmospheric effects](/docs/en-us/environment/atmosphere.md) simulate realistic environments by scattering sunlight in unique ways. Using the `Class.Atmosphere` object in the `Class.Lighting` service, you can control air particle [density](/docs/en-us/environment/atmosphere.md#density), simulate [haze](/docs/en-us/environment/atmosphere.md#haze) or [glare](/docs/en-us/environment/atmosphere.md#glare), set an atmosphere's [color](/docs/en-us/environment/atmosphere.md#color), and more. ![Atmospheric effects used to render a brilliant sunset scene](../assets/lighting-and-effects/atmosphere/Showcase.jpg)_Atmospheric effects used to render a brilliant sunset scene_ ## Clouds and skies By default, the `Class.Sky` object forms a [skybox](/docs/en-us/environment/skybox.md) with celestial bodies such as a sun, moon, and stars. In addition, you can adjust the cloud cover, density, and color properties of the `Class.Clouds` object to render realistic, [dynamic clouds](/docs/en-us/environment/clouds.md) that drift slowly across the sky through [global wind](/docs/en-us/environment/global-wind.md). _Wind blowing dynamic clouds across the sky_ ## Post-processing effects **Post-processing effects** are customizable filters that allow you to quickly enrich the visuals of your experience. Using the post-processing effect objects in the `Class.Lighting` service or `Class.Camera`, you can: - Simulate a camera viewing a bright light and exaggerate its glow. - Apply a Gaussian blur to the entirety of your experience or add a blur to parts of your experience that aren't in focus. - Enhance an environment's appearance to create a specific mood through hue. - Render a halo of light that moves with the sun. ![Landscape with depth-of-field effect applied, simulating distance blur](../assets/lighting-and-effects/post-processing/DepthOfFieldEffect-With.jpg)_Landscape with depth-of-field effect applied, simulating distance blur_ --- title: "Global lighting" url: /docs/en-us/environment/lighting last_updated: 2026-06-29T19:34:01Z description: "Explains how to customize the global lighting with the Lighting service." --- # Global lighting The `Class.Lighting` service contains properties that you can adjust to update and customize the global lighting in an experience. There are five categories of lighting properties: - [Color](#color) — Configures hue within the experience. - [Intensity](#intensity) — Configures the intensity or amount of light hitting the camera. - [Shadows](#shadows) — Configures how a user experiences shadows within the experience. - [Appearance](#appearance) — Properties that determine the lighting style and lighting/shading quality or view distance prioritization. - [Environment](#environment) — Configures the conditions of the experience's world, such as the time of day and geographic latitude. ## Color ### Ambient The `Class.Lighting.Ambient|Ambient` property sets a hue for the **entirety** of an experience. This property affects the lighting for both outdoor and indoor environments. #### [0, 0, 0] ![Lighting.Ambient property of [0, 0, 0]](../assets/lighting-and-effects/lighting-properties/Ambient-0-0-0.jpg) #### [160, 80, 0] ![Lighting.Ambient property of [160, 80, 0]](../assets/lighting-and-effects/lighting-properties/Ambient-160-80-0.jpg) #### [25, 0, 125] ![Lighting.Ambient property of [25, 0, 125]](../assets/lighting-and-effects/lighting-properties/Ambient-25-0-125.jpg) ### OutdoorAmbient The `Class.Lighting.OutdoorAmbient|OutdoorAmbient` property sets a hue for **outdoor areas** of an experience. This can help simulate how the ambient color of real-life lighting changes throughout the day. For example, sunlight in the early morning or late afternoon is usually warmer and more pink and orange in tone, while late evening is usually cooler and more blue and purple in tone. In the following images, note that the lighting inside the garage and cafe doesn't change like it would if you were changing the [Ambient](#ambient) property. #### [255, 150, 50] ![Lighting.OutdoorAmbient property of [255, 150, 50]](../assets/lighting-and-effects/lighting-properties/OutdoorAmbient-255-150-50.jpg) #### [200, 150, 240] ![Lighting.OutdoorAmbient property of [200, 150, 240]](../assets/lighting-and-effects/lighting-properties/OutdoorAmbient-200-150-240.jpg) #### [0, 175, 255] ![Lighting.OutdoorAmbient property of [0, 175, 255]](../assets/lighting-and-effects/lighting-properties/OutdoorAmbient-0-175-255.jpg) ### ColorShift_Top The `Class.Lighting.ColorShift_Top|ColorShift_Top` property sets a hue that reflects from surfaces facing the sun or moon. #### [0, 100, 255] ![Lighting.ColorShift_Top property of [0, 100, 255]](../assets/lighting-and-effects/lighting-properties/ColorShift-Top-0-100-255.jpg) #### [255, 60, 0] ![Lighting.ColorShift_Top property of [255, 60, 0]](../assets/lighting-and-effects/lighting-properties/ColorShift-Top-255-60-0.jpg) ### ColorShift_Bottom The `Class.Lighting.ColorShift_Bottom|ColorShift_Bottom` property sets a hue that reflects from surfaces that are facing away from the sun or moon. In the following images, note the hue change on the sandstone wall facing away from the sun. #### [255, 0, 220] ![Lighting.ColorShift_Bottom property of [255, 0, 220]](../assets/lighting-and-effects/lighting-properties/ColorShift-Bottom-255-0-220.jpg) #### [0, 255, 190] ![Lighting.ColorShift_Bottom property of [0, 255, 190]](../assets/lighting-and-effects/lighting-properties/ColorShift-Bottom-0-255-190.jpg) ## Intensity ### Brightness The `Class.Lighting.Brightness|Brightness` property sets the intensity of illumination. This can help increase the contrast between brightly illuminated areas and shadows, simulating bright sunshine and warmer weather. #### 0.5 ![Lighting.Brightness property of 0.5](../assets/lighting-and-effects/lighting-properties/Brightness-0.5.jpg) #### 1.5 ![Lighting.Brightness property of 1.5](../assets/lighting-and-effects/lighting-properties/Brightness-1.5.jpg) #### 3.75 ![Lighting.Brightness property of 3.75](../assets/lighting-and-effects/lighting-properties/Brightness-3.75.jpg) ### ExposureCompensation The `Class.Lighting.ExposureCompensation|ExposureCompensation` property applies exposure to an experience. Exposure is the amount of light that reaches the camera. A lower value is similar to under-exposure in photography, while a higher value is similar to over-exposure. #### 0 ![Lighting.ExposureCompensation property of 0](../assets/lighting-and-effects/lighting-properties/ExposureCompensation-0.jpg) #### -1 ![Lighting.ExposureCompensation property of -1](../assets/lighting-and-effects/lighting-properties/ExposureCompensation--1.jpg) #### 1.25 ![Lighting.ExposureCompensation property of 1.25](../assets/lighting-and-effects/lighting-properties/ExposureCompensation-1.25.jpg) ## Shadows ### GlobalShadows When enabled, the `Class.Lighting.GlobalShadows|GlobalShadows` property renders shadows. #### Enabled ![Lighting.GlobalShadows property enabled](../assets/lighting-and-effects/lighting-properties/GlobalShadows-True.jpg) #### Disabled ![Lighting.GlobalShadows property disabled](../assets/lighting-and-effects/lighting-properties/GlobalShadows-False.jpg) ### ShadowSoftness The `Class.Lighting.ShadowSoftness|ShadowSoftness` property adjusts how blurry shadows are from a value of `0` (hard edges) to `1` (soft edges). This property is only valid when `Class.Lighting.LightingStyle|LightingStyle` is set to `Enum.LightingStyle|Realistic`. #### 0 ![Lighting.ShadowSoftness property of 0](../assets/lighting-and-effects/lighting-properties/ShadowSoftness-0.jpg) #### 1 ![Lighting.ShadowSoftness property of 1](../assets/lighting-and-effects/lighting-properties/ShadowSoftness-1.jpg) ## Appearance ### LightingStyle `Class.Lighting.LightingStyle|LightingStyle` indicates the artistic intent behind lighting in the experience, as an `Enum.LightingStyle` option. `Enum.LightingStyle|Realistic` provides the most advanced and realistic lighting and shadows Roblox can deliver, while `Enum.LightingStyle|Soft` produces a flat, retro‑Roblox look with softer lights and shadows. #### Realistic ![Lighting.LightingStyle setting of Realistic](../assets/lighting-and-effects/lighting-properties/LightingStyle-Realistic.jpg) #### Soft ![Lighting.LightingStyle setting of Soft](../assets/lighting-and-effects/lighting-properties/LightingStyle-Soft.jpg) ### PrioritizeLightingQuality The `Class.Lighting.PrioritizeLightingQuality|PrioritizeLightingQuality` property indicates whether you prefer lighting/shading quality or view distance to scale down first. As the rendering quality level reduces, a setting of `true` prioritizes features such as advanced shadows and high‑quality shaders at closer distances, while a setting of `false` prioritizes view distance. If lighting and shadows are very important to the artistic feel of your experience, set this to `true`. ## Environment ### ClockTime and TimeOfDay The `Class.Lighting.ClockTime|ClockTime` and `Class.Lighting.TimeOfDay|TimeOfDay` property both represent the current time of day in hours, and they are directly related; if you change one property, the other also changes. The only difference between these properties is their numeric value; `Class.Lighting.ClockTime|ClockTime` represents time from hour `0` through `24` while `Class.Lighting.TimeOfDay|TimeOfDay` represents time through a 24 hour string. #### 0 = 00:00:00 ![Lighting.ClockTime of 0 (TimeOfDay of 00:00:00)](../assets/lighting-and-effects/lighting-properties/TimeOfDay-0.jpg) #### 6.3 = 06:18:00 ![Lighting.ClockTime of 6.3 (TimeOfDay of 06:18:00)](../assets/lighting-and-effects/lighting-properties/TimeOfDay-6.3.jpg) #### 17 = 17:00:00 ![Lighting.ClockTime of 17 (TimeOfDay of 17:00:00)](../assets/lighting-and-effects/lighting-properties/TimeOfDay-17.jpg) > **Info:** Note that each property doesn't follow the actual time of day, and they will not change during an experience unless changed through a script. ### GeographicLatitude The `Class.Lighting.GeographicLatitude|GeographicLatitude` property represents the geographic latitude in degrees. Note that while this property changes the position of the sun and moon, it does not change the `Class.Lighting.ClockTime|ClockTime` and `Class.Lighting.TimeOfDay|TimeOfDay` properties. ### EnvironmentDiffuseScale The `Class.Lighting.EnvironmentDiffuseScale|EnvironmentDiffuseScale` property determines how much ambient light is derived from the environment. In the following images, note how the ambient light changes, particularly inside the kitchen of the ramen shop. #### 0 ![Lighting.EnvironmentDiffuseScale property of 0](../assets/lighting-and-effects/lighting-properties/EnvironmentDiffuseScale-0.jpg) #### 1 ![Lighting.EnvironmentDiffuseScale property of 1](../assets/lighting-and-effects/lighting-properties/EnvironmentDiffuseScale-1.jpg) ### EnvironmentSpecularScale The `Class.Lighting.EnvironmentSpecularScale|EnvironmentSpecularScale` property determines how much specular light is derived from the environment. When set near a value of 1, smooth objects better reflect the environment and metal appears more realistic. #### 0 ![Lighting.EnvironmentSpecularScale property of 0](../assets/lighting-and-effects/lighting-properties/EnvironmentSpecularScale-0.jpg) #### 1 ![Lighting.EnvironmentSpecularScale property of 1](../assets/lighting-and-effects/lighting-properties/EnvironmentSpecularScale-1.jpg) --- title: "Post-processing effects" url: /docs/en-us/environment/post-processing-effects last_updated: 2026-06-29T19:34:01Z description: "Post-processing effects are customizable filters that quickly enrich the visuals of an experience." --- # Post-processing effects **Post-processing effects** are customizable filters that allow you to quickly enrich the visuals of your experience. Using the post-processing effect objects in the `Class.Lighting` service or `Class.Camera`, you can: - Simulate a camera viewing a bright light and exaggerate its glow ([bloom](#bloom)). - Apply a Gaussian [blur](#blur) to the entirety of your experience, or add a blur to parts of your experience that aren't in focus ([depth‑of‑field](#depth-of-field)). - Enhance an environment's appearance to create a specific mood through hue ([color correction](#color-correction)). - Render a halo of light that moves with the sun ([sun rays](#sun-rays)). ![Landscape with depth-of-field effect applied, simulating distance blur](../assets/lighting-and-effects/post-processing/DepthOfFieldEffect-With.jpg)_Landscape with depth-of-field effect applied, simulating distance blur_ ## Add post-processing effects When you add post-processing effects to the `Class.Lighting` service, they display to **all players** who enter the experience. This is useful for effects that affect the global environment, such as [sun rays](#sun-rays). When you add post-processing effects to the `Class.Camera` object, they only display to a **specific player**. This is useful for effects that should only pertain to that player based on their actions, such as blurring the 3D world view when they have a UI menu open. To add post-processing effects to either the `Class.Lighting` service or `Class.Camera`: 1. In the **Explorer** window, hover over the `Class.Lighting` service or `Class.Camera`, then click the ⊕ button. A contextual menu displays. 2. From the menu, insert one or more of the following post-processing effects: - **BloomEffect** - **BlurEffect** - **ColorCorrectionEffect** - **DepthOfFieldEffect** - **SunRaysEffect** - **ColorGradingEffect** 3. Depending on your Studio settings, some effects may not appear. To increase your rendering quality level: 1. Open [Studio Settings](/docs/en-us/studio/setup.md#customization). 2. In the left-side navigation, select **Rendering**, then the **Editor Quality Level** dropdown menu. 3. Set it to the highest level. ## Bloom The `Class.BloomEffect` effect exaggerates lights within your experience by simulating a camera viewing a bright light. When this effect has a high value, parts with light colors glow. #### Default ![Experience without any effects applied](../assets/lighting-and-effects/post-processing/BloomEffect-Without.jpg) #### Bloom Effect ![Experience with BloomEffect applied](../assets/lighting-and-effects/post-processing/BloomEffect-With.jpg) ## Blur The `Class.BlurEffect` effect applies a Gaussian blur to the entirety of the 3D world view. You can use this effect to blur the background when a player has a menu open, allowing them to focus on the most important details. #### Default ![Experience without any effects applied](../assets/lighting-and-effects/post-processing/BlurEffect-Without.jpg) #### Blur Effect ![Experience with BlurEffect applied](../assets/lighting-and-effects/post-processing/BlurEffect-With.jpg) ## Color correction The `Class.ColorCorrectionEffect` effect adjusts color properties to enhance an environment's appearance. You can also use this effect to provide player feedback, such as tinting the screen red when their character's health is low. The following list describes the primary `Class.ColorCorrectionEffect` properties: - `Class.ColorCorrectionEffect.Brightness|Brightness` — Sets the brightness of pixels. - `Class.ColorCorrectionEffect.Contrast|Contrast` — Sets the change in separation between dark and light colors. - `Class.ColorCorrectionEffect.Saturation|Saturation` — Sets the intensity of colors. - `Class.ColorCorrectionEffect.TintColor|TintColor` — Determines how much the RGB channels of pixels scale. #### Default ![Experience without any effects applied](../assets/lighting-and-effects/post-processing/ColorCorrectionEffect-Without.jpg) #### Color Correction Effect ![Experience with ColorCorrectionEffect applied](../assets/lighting-and-effects/post-processing/ColorCorrectionEffect-With.jpg) ## Depth-of-field The `Class.DepthOfFieldEffect` effect blurs parts of your experience that aren't in focus. You can use this effect to blur distant objects or to focus a player on specific parts of your experience, such as an item in a shop. #### Default ![Experience without any effects applied](../assets/lighting-and-effects/post-processing/DepthOfFieldEffect-Without.jpg) #### Depth-of-Field Effect ![Experience with DepthOfFieldEffect applied](../assets/lighting-and-effects/post-processing/DepthOfFieldEffect-With.jpg) ## Sun rays The `Class.SunRaysEffect` effect creates a halo of light with rays around the sun that move based on the `Class.Lighting.ClockTime|ClockTime` or `Class.Lighting.TimeOfDay|TimeOfDay` property. Objects between the player's camera and the sun shape this effect, allowing for realistic visuals of light and shadow. ## Color grading The `Class.ColorGradingEffect` effect modifies how color values calculated by the renderer should be converted to the screen's color range, impacting the mood and appearance of your place. This effect's primary property is `Class.ColorGradingEffect.TonemapperPreset|TonemapperPreset`. The following values are available: - `Enum.TonemapperPreset.Default` — Sets the tone mapper to use the post‑2019 Roblox appearance which provides vivid colors and high contrasts. - `Enum.TonemapperPreset.Retro` — Sets the tone mapper to imitate the pre‑2019 Roblox appearance. Colors look less saturated and there's less contrast between them. If you wish to recreate a full pre‑2019 Roblox look for your experience, experiment with `Enum.TonemapperPreset.Retro` and set the brightness of all lights to a maximum of `1.0`. #### Default ![Experience without any effects applied](../assets/lighting-and-effects/post-processing/ColorGrading-Default.jpg) #### Retro ![Experience with retro ColorGrading applied](../assets/lighting-and-effects/post-processing/ColorGrading-Retro.jpg) --- title: "Skyboxes" url: /docs/en-us/environment/skybox last_updated: 2026-06-29T19:34:01Z description: "Skyboxes are cubes made up of six individual images that create an immersive background." --- # Skyboxes A **skybox** is a cube made up of six individual images that create an immersive sky background in an experience. When the images are designed to be perfectly aligned with each other, the skybox appears to be panoramic without the impression of being inside a cube. This makes experiences feel larger than they really are, and it adds depth to your atmosphere, such as simulating deep space or underwater environments. Additionally, the `Class.Sky` object includes celestial bodies such as a sun, moon, and stars which dynamically appear, rise, and set based on the `Class.Lighting.TimeOfDay|TimeOfDay` or `Class.Lighting.ClockTime|ClockTime`. Finally, the `Class.Sky` object can be used as a cubemap for reflections in `Class.ViewportFrame|ViewportFrames`. For details, see [viewport frames](/docs/en-us/ui/viewport-frames.md). ## Skybox construction If you've created your own skybox images, you must first [import](/docs/en-us/projects/assets/manager.md#asset-import) them to Roblox before you can use them in a skybox. Each image must be seamless along **all edges** of neighboring images when "folded" into a cube. #### Image Faces #### Skybox "Folded" To create a skybox: 1. In the **Explorer** window, insert a `Class.Sky` object into the `Class.Lighting` service. 2. Select the new `Class.Sky` object, then in the **Properties** window, assign a texture to each of the following sky properties: - **SkyboxBk** — The **back** square of the skybox. - **SkyboxDn** — The **down** square of the skybox. - **SkyboxFt** — The **front** square of the skybox. - **SkyboxLf** — The **left** square of the skybox. - **SkyboxRt** — The **right** square of the skybox. - **SkyboxUp** — The **up** square of the skybox. ## Celestial bodies By default, the `Class.Sky` object includes celestial bodies such as a sun, moon, and stars. These bodies dynamically appear, rise, and set based on the `Class.Lighting.TimeOfDay|TimeOfDay` or `Class.Lighting.ClockTime|ClockTime` property values. You can customize celestial bodies through the following properties: - `Class.Sky.SunTextureId|SunTextureId` — Sets the texture of the sun. - `Class.Sky.SunAngularSize|SunAngularSize` — Sets the relative size of the sun in degrees. - `Class.Sky.MoonTextureId|MoonTextureId` — Sets the texture of the moon. - `Class.Sky.MoonAngularSize|MoonAngularSize` — Sets the relative size of the moon in degrees. - `Class.Sky.StarCount|StarCount` — Sets the amount of stars in the skybox. > **Info:** To disable all celestial bodies, you can toggle off the `Class.Sky.CelestialBodiesShown|CelestialBodiesShown` property. Alternatively, you can disable only the sun and/or moon (while keeping the stars) by setting `Class.Sky.SunAngularSize|SunAngularSize` or `Class.Sky.MoonAngularSize|MoonAngularSize` to `0`. ## Orientation The `Class.Sky.SkyboxOrientation|SkyboxOrientation` property changes the orientation of the skybox surfaces. The property takes a `Datatype.Vector3` of degree values in the typical **XYZ** order, but rotation is **applied** first around the **Y** axis, then **X**, and then **Z** to allow for predictable control over complex movements. An easy way to script an orientation animation is to spin around the **Y** axis (keeping the horizon level), then tilt this axis by setting **X** and **Z** to a fixed value. The following script, for example, animates the **Y** axis for spinning while keeping a consistent 30° tilt on the **X** axis. ```lua local Lighting = game:GetService("Lighting") local RunService = game:GetService("RunService") local sky = Lighting:FindFirstChild("Sky") local ROTATION_SPEED = 5 -- In degrees per second RunService.Heartbeat:Connect(function(deltaTime) sky.SkyboxOrientation = Vector3.new( 30, (sky.SkyboxOrientation.Y + ROTATION_SPEED * deltaTime) % 360, 0 ) end) ``` > **Info:** The modulo operation `%` is optional but recommended. You can use values above `360` or below `0`, but using smaller numbers avoids issues when the animation runs for a very long time (due to limited precision). Note that skybox orientation is a low-cost feature which works seamlessly across all platforms and visual quality levels. As a result, some intentional exceptions include: - If the sky is visible in indoor reflections such as a mirror surface through an open window, that specific reflected view will not be rotated. Achieving this would require expensive re‑rendering and convolving of cubemaps which would significantly impact performance and broad availability. - Only the skybox surfaces rotate; [celestial bodies](#celestial-bodies) are not affected by this property. - If you utilize a `Class.Sky` within a `Class.ViewportFrame`, it will reflect the global `Class.Sky.SkyboxOrientation|SkyboxOrientation` values. You cannot adjust the angle per `Class.ViewportFrame`. - The [dynamic clouds](/docs/en-us/environment/clouds.md) feature under `Class.Terrain` is not impacted. --- title: "Get started with experiences on Roblox" url: /docs/en-us/experiences last_updated: 2026-06-29T19:34:01Z description: "Learn how to create Roblox experiences with guides, tutorials, and code samples." --- # Get started with experiences on Roblox Roblox is a 3D creation platform that provides an all-in-one IDE that includes the Roblox Engine, a client emulator, and all the tools and APIs you need to start creating Roblox experiences. #### Get Studio and take the tour Get acclimated with Studio by downloading and launching the onboarding tour. #### Create your first experience Create a simple 3D platformer while learning important Roblox building and scripting concepts. #### Learn about the platform Roblox is designed for mega-scale, 3D worlds. Learn about how it works! [Overview](/docs/en-us/creation.md#experiences) [For Unity Developers](/docs/en-us/unity.md) [For Unreal Developers](/docs/en-us/unreal.md) ## Play and create with templates Roblox Studio has a variety of templates that you can edit and play without prior knowledge. Give them a try and add on to them when you're ready! #### Platformer Build your own **3D platformer** with customizable platforms, coin pickups, and double-jump, dashing, rolling, and long jump character mechanics! #### Laser tag Build your own **first-person shooter** with customizable blasters, round systems, and modular building assets to reconfigure a high-quality arena! #### Racing Build your own **kart racer** with a customizable driving mechanics, checkpoints, and modular winding track pieces that you can restructure for countless courses! ## Learn with curriculum paths To dive deeper, follow along with guided curriculum that teaches you the skills you need to create and monetize your experiences. #### Environmental art Environmental Art teaches you how to create a high-quality environment for a laser tag experience. #### Gameplay scripting Gameplay Scripting teaches you how to organize and implement the scripting logic for large, complex project. #### UI Design UI Design teaches you how to plan and implement UI components for unique user flows. ## Connect Learn, share, and interact with the Roblox community in the following places. #### On forums [Staff announcements](https://devforum.roblox.com/c/updates/announcements/36) [Staff articles](https://devforum.roblox.com/c/resources/roblox-staff/278) [Community tutorials](https://devforum.roblox.com/c/resources/community-tutorials/46) [Community help and feedback](https://devforum.roblox.com/c/help-and-feedback/54) #### On social video channels [YouTube channel](https://www.youtube.com/@RobloxLearn) [Content and events platform](https://events.roblox.com) --- title: "Experiences with Generative AI" url: /docs/en-us/generative-AI last_updated: 2026-06-29T19:34:01Z --- # Experiences with Generative AI Roblox provides a variety of AI-powered tools and services to enhance creation and in-experience creativity. When incorporating generative AI into your experiences, whether using Roblox-provided models or third-party solutions, you must ensure these integrations remain safe, follow Roblox's [Community Standards](https://about.roblox.com/community-standards), and provide transparency to your users. This page outlines the guidelines, rules, and best practices for using generative AI in Roblox experiences. ## Moderation To maintain a safe environment, all content, including content created by or using generative AI, must adhere to Roblox's Community Standards. **When using Roblox APIs, such as `Class.GenerationService` and `Class.TextGenerator`**: All content inputs and outputs go through [moderation](https://about.roblox.com/newsroom/2025/07/roblox-guard-advancing-safety-for-llms-with-robust-guardrails), including text filtering, image review, and asset review. Content identified as violative is not served to users. - Users will not be issued consequences for violative outputs generated in response to non-violative inputs. **When using third-party tools**: You are responsible for the content delivered to users, even if it is AI-generated. To make sure your experience aligns with Roblox's safety standards, you should ensure that: - Outputs adhere to Roblox's Community Standards. - Inputs and outputs do not circumvent Roblox-mandated safety measures. For example, text inputs and outputs must go through `Class.TextChatService`. ## Content maturity If your experience allows players to interact with a generative AI model in any way that triggers a response from the model, such as interactions that include text chat, voice chat, images, 3D generations, and avatar movement, you must disclose it within the [Content Maturity](/docs/en-us/production/promotion/content-maturity.md) questionnaire. While merely including an AI component in your experience does not impact the content maturity of your experience, experiences with extended AI interactions must include the appropriate **Restricted** content maturity label so that they are only available to the appropriate audience. ### Limited interactions For experiences without the **Restricted** content maturity label, model outputs must adhere to the underlying Content Maturity classification of the experience. **When using Roblox-served AI tools, such as `Class.GenerationService` and `Class.TextGenerator`**: - For all experiences, we instruct our model to align with an All Ages content maturity rating and avoid outputs that would require a descriptor, such as Horror elements. - For experiences with a Content Maturity label of mild violence or higher, models can generate otherwise compliant weapons. **When using third-party tools**: - You must ensure outputs align with the Content Maturity rating of your experience. ### Extended interactions Experiences with **extended interactions** are those that are intended to, or can be used to, engage in continuous conversation with chatbot-like generative AI features. Your experience is considered to have extended AI interactions if it meets one of the following criteria: - The experience's main theme or purpose is for players to continuously interact with a generative AI bot or character without a time limit. - For example, an experience titled "Your AI friend" whose sole purpose is for users to engage in a continuous conversation with an AI-driven non-player character (NPC). - The AI component includes cross-session memory so that the experience saves the context of a user's prior interactions with AI and loads it on subsequent sessions. - For example, an experience primarily focused on gameplay that has one AI-driven NPC which users can only interact with for several minutes. However, when reloading the game and interacting with the NPC again, the model can refer back to previous conversations with the user. If your experience includes extended interactions, you must answer "Yes" to the Extended AI question of the Content Maturity questionnaire. Because these experiences may not be suitable for younger users, they need a Restricted content maturity label so that they are inaccessible to users under the age of 18. > **Info:** For more information on Content Maturity rules and guidelines, see the [Content Maturity](/docs/en-us/production/promotion/content-maturity.md) guide. ## Disclosures When including generative AI components in your experience, you must ensure visible disclosures are presented to users, including: - At the start of any interaction, and throughout the session, users must see a notice that their conversation is with AI. For example: "_This is an AI-powered conversation, not human. It may make mistakes._" - Users must be directed to authoritative resources if seeking any medical or professional advice. Specifically, if a user expresses thoughts of self-harm or signs of a mental health crisis, AI models must encourage them to seek support. For example: "_If you or a loved one are experiencing a hard time, please reach out to qualified mental health professionals or support services who can provide the help you need. Information and links to resources and crisis hotlines can be found on Roblox's Community Standards._" --- title: "Get started creating on Roblox" url: /docs/en-us/get-started/creating last_updated: 2026-06-29T19:34:01Z description: "Learn how to create experiences and avatar items quickly on Roblox" --- # Get started creating on Roblox Getting started on Roblox is easy, and learning about the ecosystem only takes a few minutes. Better yet, you can start creating and sharing your creations in less than an hour with these resources. ### 1. Learn about what you can do This video goes over some of the things you can build on Roblox and the tools you need to do it. [Watch overview](https://www.youtube.com/watch?v=vIiVbFiDbBE) ### 2. Get a Roblox account and the app The same Roblox account works for playing and creating. Sign up at roblox.com and download the app for your preferred device. [Go to roblox.com](https://www.roblox.com) ### 3. Customize your avatar and play something The best way to understand Roblox is to use it! Customize your avatar with free or paid items and play a few of the millions of experiences. [Browse avatar items](https://www.roblox.com/catalog) [Browse experiences](https://www.roblox.com/charts) ### 4. Get Studio and take the tour Roblox Studio is your main creation tool and is supported on Windows and Mac. The onboarding tour gets you acclimated to basic workflows. [Roblox Studio setup](/docs/en-us/studio/setup.md) # Experiences Making an experience can seem intimidating, but we promise it's not too bad! Walk through the tutorial or watch the following videos to understand a bit more about how experiences work. [See all docs](/docs/en-us/experiences.md) ### Create your first experience Create a simple 3D platformer while learning important Roblox building and scripting concepts. [Start creating](/docs/en-us/tutorials/curriculums/core.md) ### Intro to world building Sit back and watch one of our 3D artists show you how to construct 3D worlds in Studio. [Watch livestream](https://youtu.be/SgPU84AqpkY) ### Intro to physics The Roblox engine simulates real-life materials, physics, collisions and more. Join one of our product managers for a overview of how the physics simulation works. [Watch livestream](https://youtu.be/9mXvhYSv7fc) # Avatar items Making avatar items can sometimes be easier than making an experience. You can make a simple 2D t-shirt with an image editor without having to open Studio at all. If you're interested in 3D modeling, use Blender and Studio together to make a basic 3D accessory. [See all docs](/docs/en-us/avatar.md) ### Avatar items overview Learn about all the different things you can create and allow users to equip for their avatars. [Watch overview](https://youtu.be/EUDSIUmLjxA) ### Create a t-shirt A simple t-shirt is the easiest example of how to create an avatar item. [Watch how to do it](https://youtu.be/r_unfGZT5Ps) [Guides](/docs/en-us/avatar/classic-clothing.md) ### Create a backpack accessory Learn how to create a backpack, which is an example of a 3D accessory. These accessories are simple, non-animated items that attach to a single point on an avatar's body. [Watch how to do it](https://youtu.be/Eed29gV0hLA) [Guides](/docs/en-us/art/accessories.md) --- title: "How is Roblox different from other platforms?" url: /docs/en-us/get-started/how-is-roblox-different last_updated: 2026-06-29T19:34:01Z description: "Frictionless play, community collaboration, and reach beyond Roblox." --- # How is Roblox different from other platforms? ## Frictionless play #### No complicated downloads Roblox is built on the cloud, allowing users to join new experiences in seconds, without having to download massive files. #### Automatic matchmaking Users are automatically placed in servers with friends and other users from their region, making it easier than ever to connect with friends and make new ones. #### Cross-platform support Experiences can be published across all major devices, allowing players to play how they want, where they want, with whomever they want. ## Collaborate with your players #### Communities are part of the process Due to the user-generated content (UGC) nature of Roblox, the community is much more involved, as many devs are also often community members. Cultivating a healthy community and involving users in development is key to success. #### Benefit from direct feedback Since Roblox enables creators to develop and publish updates quickly, they can efficiently gather feedback and iterate -- resulting in a deeper understanding of their audience and a more focused experience for users. ## Reach beyond Roblox #### Massive creator ecosystem Roblox fuels a thriving community of content creators on platforms like YouTube and TikTok, generating views and driving engagement. #### Engaged cross-platform audience Roblox content resonates beyond the platform, with creators building loyal followings and extending reach to new players. --- title: "Get Started" url: /docs/en-us/get-started last_updated: 2026-06-29T19:34:01Z description: "A guide for new Roblox developers. Explains why to develop on Roblox the platform, and how to get started." --- # Get Started Welcome to Roblox, we're glad youʼre here! Itʼs an exciting time to develop on Roblox. This guide is a crash course in all things Roblox, covering the most commonly asked questions by developers: - Why should I develop on Roblox? - How is Roblox different from other platforms? - What tools does Roblox provide? - How do I make money? - Are there any recommended strategies? - What other resources are there to learn more? ## Choose a section to learn more about Roblox [Why should I develop on Roblox?](/docs/en-us/why-build-on-roblox.md) Reasons to build on the Roblox platform—audience, tools, and opportunity. [How is Roblox different from other platforms?](/docs/en-us/how-is-roblox-different.md) Frictionless play, community collaboration, and reach beyond Roblox. [What tools does Roblox provide?](/docs/en-us/tools.md) Roblox Studio, Creator Hub, AI Assistant, and more. [How do I make money?](/docs/en-us/monetization.md) Creator Rewards, in-experience purchases, subscriptions, UGC, and more. [Recommended strategies](/docs/en-us/strategies.md) Set your game up for success, build and test your MVP, and iterate. [Start creating](/docs/en-us/creating.md) Get started creating experiences and avatar items. [Resources](/docs/en-us/resources.md) Find more useful resources for your creation journey. --- title: "How do I make money?" url: /docs/en-us/get-started/monetization last_updated: 2026-06-29T19:34:01Z description: "Creator Rewards, in-experience purchases, subscriptions, UGC, immersive ads, and more." --- # How do I make money? ## Earn on Roblox #### Diverse earning opportunities We're excited to provide creators with a wide variety of ways to earn from their experiences, including subscriptions, in-app purchases, engagement rewards, and more. We're always evolving and adding new features to help you earn and succeed. #### Developer Exchange Program (DevEx) The [DevEx program](https://en.help.roblox.com/hc/en-us/articles/203314100-Developer-Exchange-DevEx-FAQs) allows eligible creators to convert earned Robux into real-world currency. Once you've met the requirements, including having a minimum of 30,000 earned Robux, you can submit a request to cash out. ## In-app purchases #### Repeat purchases [Dev Products](/docs/en-us/production/monetization/developer-products.md) are a type of sale item that can be purchased multiple times, only with Robux. This is used for things like in-game purchases, boosts, etc. Dev Products generally monetize better, as they can be repeatedly purchased. #### One-time purchases [Game passes](/docs/en-us/production/monetization/passes.md) are a type of sale item that can only be purchased once, only with Robux. This is useful for things like VIP passes, content bundles, or battle passes. #### You receive 70% of Robux spent The remaining 30% is Robloxʼs Marketplace Fee. To convert these Robux to a real currency, youʼll still need to withdraw it via DevEx. ## Subscriptions #### Subscriptions [Subscriptions](/docs/en-us/production/monetization/subscriptions.md) provide players with benefits for a recurring monthly fee. Common benefits include in-game currency, multipliers, and access to exclusive vehicles, weapons and clothing. Subscriptions auto-renew and are priced in local currency. #### Subscriber analytics The Creator dashboard allows you to track metrics including subscriber breakdown and cancellations, estimated revenue, subscriptions by platform, and platform earnings. ## Creator rewards #### Creator Rewards Weʼre increasing our investment in the developer community by rewarding creators for producing engaging content and bringing new users to the platform. #### Daily engagement Earn a fixed payout of 5 Robux when a user who has spent at least $9.99 on the platform in the last 60 days (an "Active Spender") plays your game for 10 minutes or more. This reward applies to the first three experiences they play per day. #### Audience expansion Earn a 35% revenue share on the first $100 in Robux purchases made by a new or returning user who finds your experience through a direct link or search, and plays for at least 10 minutes.11_ Less qualifying purchases they have made in the 60 days after each time (if any) they have previously reactivated. Additional restrictions apply._ ## Creator Store purchases #### Sell models and plugins Create and monetize [3D assets and Studio extensions](/docs/en-us/production/creator-store.md#distribute-and-sell-assets) to other creators for use in their own development. #### Real-world pricing model The Creator Store's real-world pricing model returns 100% of net proceeds to creators, bypassing platform fees and DevEx rates. ## UGC avatar items #### UGC for everyone Previously restricted to approved creators, selling user-generated avatar clothing is now available to all creators who are age-verified and subscribe to Roblox Plus or Premium. #### Creators receive 30%, sellers 40% For items sold in the Avatar Marketplace, Roblox collects the 40% seller portion + a 30% market fee. If your item is sold from within another experience, the owner of that experience will collect the 40% seller portion instead. #### Sell in your own experience If you sell your UGC item within an experience you own, you will earn 30% for being the creator and 40% for being the seller, bringing your return to 70% of Robux spent. ## Immersive ads #### Advertise Programmatically-served ad content from brands appear directly within your experiences. These native ads blend into the 3D environment, offering a seamless way to monetize your experience. #### Ad formats Integrate a [variety of formats](/docs/en-us/production/promotion/advertise.md), including video ads that can autoplay or be click-to-play, image ads that are static billboards, and portal ads that can teleport users to an advertiser's experience. #### How to earn Creators are paid through a revenue share based on engagement. You can earn from video views, ad impressions, and successful teleports that are generated by the ads in your experience. ## Brand integrations #### Limited-time activations Limited-time activations bring brands into an experience for a brief time – usually 2–4 weeks – through content like quests and avatar items. For a more authentic integration that players will respond to, be sure to choose brand partners that fit your experienceʼs vibe, aesthetics, and goals. #### Make it meaningful For a seamless and authentic brand integration, weave the brand into your game's [core loop](https://youtu.be/gkFKF9A-snY). This means the brand content should feel like a natural extension of the gameplay, enhancing the player's progression and overall experience. ## Digital + physical merchandising #### Sell physical goods The new [Roblox Commerce APIs](https://corp.roblox.com/newsroom/2025/05/roblox-launches-shopify-integration-amp-licensing-program) enable creators and brands to sell physical products directly within their Roblox experiences, creating a virtual mall-like experience. Shopify is the first integrated partner. #### Bridge digital & physical The Approved Merchandiser Program (AMP) links physical product purchases with digital items on Roblox. Physical products with the AMP badge will include a code unlocking a unique digital item in the experience. #### Empowering creators By bridging the gap between virtual and real-world shopping, Roblox is opening up new avenues for revenue generation and brand building. ## Price optimization #### Automated price testing Our [Price Optimization](/docs/en-us/production/monetization/price-optimization.md) system intelligently experiments with different price points, showing them to subsets of your users to identify the sweet spot that maximizes your revenue. #### Dynamic pricing Keep your prices competitive with dynamically-scripted pricing. Our tools make flexible pricing strategies simple, allowing you to respond to market trends and player behavior in real time. #### Data-driven decisions Monitor both short-term and long-term impacts, and use our insights to help ensure that your pricing strategy drives sustainable growth and maximizes your overall earnings. ## Regional pricing #### Unlock international reach Roblox's [Regional Pricing](https://corp.roblox.com/newsroom/2025/04/roblox-launches-regional-pricing-for-in-experience-items) automatically determines and sets optimal local prices for your in-experience items, making your content more accessible and appealing to players around the world. #### Help boost worldwide spending Early testing demonstrates substantial increases in user spending in regions like Mexico, Brazil, and the Philippines, demonstrating the power of localized pricing strategies. --- title: "Resources" url: /docs/en-us/get-started/resources last_updated: 2026-06-29T19:34:01Z description: "Get started with these resources, community feedback, growth strategies, and RDC." --- # Resources ## Get started with these resources [Roblox Creator Roadmap](https://create.roblox.com/roadmap) See what's coming to the platform and plan ahead. [Tips for finding the right developer partners](https://brands.roblox.com/resources/activating-on-roblox-partnering-with-developers) Guidance for brands on partnering with the right developers. [More on building engaging experiences](https://brands.roblox.com/resources/how-to-build-engaging-brand-experiences-on-roblox) Best practices for creating compelling brand experiences on Roblox. [Learn techniques for monetizing your experience](/docs/en-us/production/monetization.md) Learn how to monetize your experience in the creator economy. [Get pre-built Feature Packages](/docs/en-us/resources/feature-packages.md) Ready-to-use systems for common game features. [Learn about Roblox's License Manager and Catalog](https://corp.roblox.com/newsroom/2025/07/roblox-launches-new-licensing-platform-for-experiences) Licensing and catalog tools for experiences and content. ## Learn more at the Roblox Developers Conference #### Presentations RDC offers creators a firsthand look into the future of Roblox with keynote presentations from our CEO David Baszucki and other Roblox leaders. #### Breakout sessions There are in-depth breakout sessions showcasing the latest technology that will empower creator success and lots of great networking opportunities. #### Roblox Innovation Awards This is where we also hold the [Roblox Innovation Awards](https://www.youtube-nocookie.com/watch?v=7DXTQcA9G_A), an annual award show recognizing the yearʼs most innovative experiences and creators. [Read more about RDC 2025](https://corp.roblox.com/newsroom/2025/09/roblox-rdc-2025) --- title: "Recommended strategies" url: /docs/en-us/get-started/strategies last_updated: 2026-06-29T19:34:01Z description: "Set your game up for success, build and test your MVP, and iterate." --- # Recommended strategies ## Set your game up for success #### Assemble your Roblox dream team Look for developers with expertise in game design, 3D modeling, scripting, and optimization from within the Roblox community. A seasoned developer can help navigate the technical complexities of Roblox and ensure your vision is executed effectively, leading to a higher quality and more engaging experience. #### Be aware of popular games & genres Understanding the preferences of the Roblox user base is essential when selecting a genre. Popular genres on Roblox include role-playing games, simulators, obstacle courses (obbies), and survival games. Choosing a proven genre will increase the likelihood of attracting and retaining players. ## Understand your target audience #### Play similar experiences Itʼs important to have a good knowledge of popular Roblox experiences, especially those that are similar to yours. Why are users drawn to this experience? What keeps them interested? #### Talk to players Watch the chat or join an experienceʼs Discord server to see what players talk about. Feel free to join in and ask questions yourself. #### Watch YouTube videos If you donʼt have the time to get deep into an experience, watch videos on YouTube. These are often made by the community and can give you a good idea of what theyʼre interested in. ## Define your KPIs #### Identify goals To measure the success of your Roblox experience, define your Key Performance Indicators (KPIs) before development begins. These metrics (such as engagement, retention, and monetization) should guide your design from the start to ensure your game supports its target goals. #### Organic social growth KPIs D1 Retention: Users who return to your game the day after their first session. Average Playtime Per User: The average amount of time that users spend in your game (Average Sessions/Day x Session Length). #### Sustained growth KPIs D7 & D30 Retention: Users who return 7 and 30 days after their first session. Conversion: The percentage of users who spend Robux in your experience. ARPPU: Average Revenue Per Paying User. ## Design for multi-platform play #### Playable anywhere Roblox is played on a variety of devices, including PCs, mobile phones, and consoles. It's essential to design a user interface (UI) and user experience (UX) that works seamlessly across all platforms. The UI should be clean, intuitive, and easy to navigate, regardless of the device being used. #### Massive mobile audience Given Roblox's large mobile audience, ensure that your game is easily playable on a touchscreen with a well-designed mobile UI. Even for games with a higher percentage of PC players, such as shooters and RPGs, supporting mobile is still a best practice. #### Optimize for performance Many Roblox users play on low-end devices, so performance is essential. Non-performant games may struggle to grow and maintain a user base. [Learn more about Performance Optimization](/docs/en-us/performance-optimization.md) ## Build a strong foundation #### Design a killer core loop The [core loop](/docs/en-us/production/game-design/core-loops.md) is the fundamental gameplay cycle that keeps players engaged and returning to your experience. It should consist of a series of actions and rewards that are satisfying, repeatable, and progressively challenging in order to motivate players to continue interacting with your experience. #### Create a world they'll love Create a visually appealing and engaging environment that immerses players in your experience. The environment should be easy to navigate, fun to explore, and encourage positive interactions. Provide opportunities for players to connect with each other, fostering a sense of community. #### Think ahead Plan long-term by designing with updates in mind. Implement reusable systems, such as quest, collection, or event frameworks that can be easily expanded. This approach will reduce development time and costs for future updates and ensure that your experience remains fresh and engaging. ## Create a social, multiplayer experience #### Friends play together Users often play together as a group. Try to design your gameplay to support group play wherever possible and make it easy for friends to connect. #### Make users into content Designing for social interaction in games is not only good for growing your game, but it also serves as a form of content, taking some of the burden off of your team. #### Design spaces for interaction When youʼre building out your level, think about how players can find and interact with each other. If the map is huge, players may be spread too thin. ## Get to the fun quickly #### Get to the fun quickly What makes your experience fun? What makes it different? Is it worth playing again? Users should be able to answer these questions within the first few minutes of playing your experience. #### Avoid long tutorials Roblox players like to get to the action immediately and figure it out on their own (or with friends). If you must have a tutorial, keep it short, contextual, and interactive. #### Donʼt restrict socialization Allow new players to socialize and learn from each other. Donʼt create barriers like individual server instances that prevent users from playing with friends. ## Keep players invested #### Give players goals Keep players motivated by providing a mix of short-term, mid-term, and long-term goals that cater to different playstyles and encourage exploration. This variety will improve player retention and prevent them from losing interest. #### Add progression to make content last Progression systems give players a clear path toward their goals and a sense of accomplishment while slowing down their content consumption, giving you time to produce more. #### Donʼt gate the fun If you do add progression, make sure there is still enough content available upfront to ensure new users can have fun in their first play session. They should also have a clear understanding of what their next goal is and how to achieve it. [Season Pass Feature Package](https://create.roblox.com/docs/resources/feature-packages/season-passes) ## Use F2P monetization practices #### Give spenders options Help maximize your monetization potential by providing a variety of virtual items at different price points. This strategy appeals to both casual spenders and dedicated fans. Consider adding a starter pack containing items that appeal to new players. #### Design for repeat spend Why would a player want to return and continue to spend, over time? As opposed to one-time purchases, players will spend much more on repeatable purchases (like consumable items) as thereʼs no limit to how much they can spend. #### Optimize your storefront Make your shop as transparent and frictionless as possible, so players understand what to buy, why, and how. Clearly communicate the value and availability of all items. ## Build & test your MVP #### Define your MVP What are your core features? If youʼre making a game, what is your core loop? Whatʼs the minimal version of your experience that can demonstrate these features? #### Get feedback early While it may be tempting to build your fully realized experience, starting with MVP will allow you to get feedback and iterate early, resulting in a better final product. #### Test your MVP As soon as itʼs ready, get your MVP in playersʼ hands. Their early feedback is the key to identifying issues, finding the fun, and setting your game up for success with real Roblox players. Donʼt wait until itʼs too late to pivot. ## Playtest early and often #### Small group playtests Give access to specific users only, which allows you to run small playtests. This option is good for testing whether players understand how to play and find the experience fun. #### Limited-time public test Release a test experience to the public, acquire a small amount of users, then remove the game after a set amount of time (a weekend, week, etc.) This option is great for getting organic growth metrics like D1 Retention and session length. #### Closed group beta The experience is playable by any user that joins your Group. This option is great for gathering community feedback, long-term retention, and monetization. However, it requires some existing community, and may be more friendly than an open test. #### Open beta The experience is open to everyone, but has a "Beta" tag in the title. This is very common on Roblox; however, there is no practical difference between open beta and a full release beyond setting player expectations. ## Keep testing and iterating #### Identify soft metrics Comparing your analytics to your target [KPIs](https://www.youtube.com/watch?v=RfpMZZFy8Zw) will help you figure out what areas need improvement, whether itʼs retention, engagement, or monetization. #### Gather community insight Once you know what areas needs work, use your community to find out what is causing those problems. #### Implement & evaluate Once you identify a potential cause, make the necessary changes and test again. Go through this cycle until your KPIs are in line with your target. ## Get community feedback #### Start a community chat server Be sure to set up your own social chat server, link it to your experience, and have moderation measures in place. This is where your dedicated community members will congregate. #### Talk to your users Ask your users what they think. What do they like? What donʼt they like? What could make it better? Users will be happy to answer, plus it will make them feel like you care. #### Watch how users interact Join your experience to observe what users do and what they talk about in the chat. Are they behaving how you would expect? Do they get confused at certain points? Are they interacting with other players? #### Helpful Channels to Have in Your Community Chat Server ##### #Rules or #FAQ Explains how the server works and the expected behavior ##### #Announcements Post any updates or announcements here ##### #General Area for general conversation ##### #Bugs For players to report bugs ##### #Suggestions For players to submit ideas and feedback ##### #Creative For players to submit content & fan art ## Be creator-friendly #### Consider streamer needs Design your game to be streamer-friendly. Ensure players can easily record videos without UI elements getting in the way, and create an experience that's engaging for viewers to watch on platforms like YouTube and Twitch. #### Fuel excitement & speculation Many experiences have built-in hidden secrets, backstories, and "surprise and delight" moments that encourage players to share and discuss the content, leading to more social media engagement. #### Create shareable moments In your experience, is there anything that users would want to share on TikTok or YouTube? Have they created something of their own? Are there achievements or "wow moments" worthy of posting on social media? ## Build awareness through social media #### Connect with creators Many Roblox users learn about new experiences from their favorite YouTubers. Getting one or a few of these influencers to play your experience can help get some initial traction. #### Find the right influencer Influencers are more likely to cover your experience if it fits their audience – so do your research. If engaging with an influencer, be sure to follow the FTC's [Endorsement and Testimonial Guides](https://www.ftc.gov/sites/default/files/attachments/press-releases/ftc-publishes-final-guides-governing-endorsements-testimonials/091005revisedendorsementguides.pdf) and consult with your own legal counsel for specific advice. #### Try targeted TikTok ads TikTok has also proven to be a good way to raise awareness off platform. You can use their targeting tools to surface ads to users that are interested in similar content. Check out TikTokʼs [best practices for Roblox developers](https://activity-va.tiktok.com/magic/eco/runtime/release/689e44394a786f02edabc8a7). ## Unlock organic growth #### Most users come from Home Beyond external sources like social media and word of mouth, Home is where a majority of discovery takes place and consists of recommendations that are algorithmically generated to optimize for engagement, long-term retention, and monetization on a per-user basis. #### Data-defined placement We take many factors and signals into account when generating recommendations for someone. Beyond user-focused personalization (age, device, location, etc.) we also look at how well an experience retains players, how much Robux and time players spend, and how often users return to play again. #### Spread the word The best way to maximize how much youʼre surfaced in recommendations is to create a strong experience with solid KPIs. Packaging (icon, thumbnail, media, description) can also help improve D1 retention and play through rate. ## Use paid UA to build momentum #### Use sponsored ads Sponsored Ads appear in the "Sponsored" sort on the home page and within search results. Theyʼre great at getting users into experiences but arenʼt as good at targeting specific users. [Learn more about Roblox Ads Manager](/docs/en-us/production/promotion/ads-manager.md) #### Use earned Robux If you do run an ad campaign, try to use Robux youʼve earned rather than purchasing Robux – the exchange rate is higher for purchased Robux. ## Keep the content coming #### Update to stay relevant New content updates keep experiences from becoming stale and give players another reason to come back. They also give content creators new material to cover. #### A consistent cadence forms habits Establishing a consistent content cadence can encourage players to check in regularly. If itʼs sustainable for your team, aim for minor content updates every 1–2 weeks and major system updates every 2–3 months. #### Plan for LiveOps from the start Itʼs important to plan for liveops ahead of time. What would those updates include? How do you plan on delivering these updates regularly? Scope updates to fit your teamʼs capacity. ## Generate buzz with limited-time content #### Limited-time events add variety Running seasonal events keeps your experience feeling fresh. Events can include thematic map updates, battle/season passes, holiday content and limited-time rewards. #### Limited rewards create social value Time-limited reward items create rarity. The rarer an item is, the more social value it has amongst players, driving more of them to seek out and earn those items. #### Let players flex Players will notice when other players have a special item or ability and ask "Howʼd you get that?!" This makes the owner feel special and encourages information sharing, which further increases the value of rare or limited items. ## Plan & promote your updates #### Plan for the long haul Develop a roadmap outlining your content plans for the next 6 to 12 months, outlining regular content updates and new systems or major features that keep gameplay fresh. A well-defined roadmap will help you stay organized, manage resources, and communicate your vision to both your development team and your players. #### Spread the word Promote your updates across your social media channels and within the experience itself. Build anticipation by teasing new features, showcasing behind-the-scenes development, and engaging with your community. #### Use update announcements The integrated events system lets you create time-based events for your experience. Players can discover your events on the experience's detail page and through an event details page, and they can opt into notifications that they'll receive when your event begins. --- title: "What tools does Roblox provide?" url: /docs/en-us/get-started/tools last_updated: 2026-06-29T19:34:01Z description: "Roblox Studio, Creator Hub, AI Assistant, matchmaking, discovery, and more." --- # What tools does Roblox provide? ## Dive into Roblox Studio #### Your one-stop shop Create 3D environments, add animations, build physics simulations, and program game logic with Lua. #### Collaborate with Team Create Multiple users can access and edit the same project simultaneously, making it easier to collaborate and communicate with teammates. #### Test and publish directly from Studio Test your creations and quickly iterate, then when youʼre ready, publish to Roblox with the click of a button. ## Learn and grow with Creator Hub #### Creator Dashboard Access your experienceʼs engagement, retention, and monetization KPIs, free of charge. #### Documentation Hit the ground running with articles, tutorials and videos on topics ranging from design and implementation to analytics and LiveOps. #### Developer Forum Get advice from the community, file a bug report, and learn about the latest Roblox updates. ## Creator Hub: Marketplace and Talent Hub #### Marketplace Find all the [assets](https://create.roblox.com/store/models) you need, from royalty-free music to community-made models and helpful plugins. #### Talent Hub Whether youʼre searching for artists, programmers or designers, you can find them and review their work on [Talent Hub](https://create.roblox.com/talent). ## Meet your AI assistant #### Supercharge your workflow Our [AI Assistant](/docs/en-us/assistant/guide.md) revolutionizes your development process by providing instant, context-aware support. Get immediate answers to your Roblox questions, generate and modify code snippets, and insert assets directly into your game, all within Studio. #### Automate the mundane AI Assistant empowers you to automate common actions such as bulk property modifications. By handling these time-consuming chores, Assistant frees you to concentrate on the higher-level design and innovative gameplay elements that make your experiences truly unique. ## Imagine it, generate it #### Generate 3D worlds [Roblox Cube](https://about.roblox.com/newsroom/2025/03/introducing-roblox-cube) is a groundbreaking generative AI system that lets you create 3D models and environments directly from text prompts, reducing the time and effort required for 3D asset creation. #### Unlock player creativity Cube empowers your players to personalize their gameplay in exciting new ways. By enabling the generation of unique 3D content within experiences, Cube opens the door for deeper player engagement and self-expression. ## Master matchmaking #### Fine-tune with signal control Adjust the weight of [matchmaking](/docs/en-us/matchmaking/customize-matchmaking.md) signals such as friends, occupancy, latency, and play history. Define up to two custom signals based on player attributes like skill level or game mode preference. #### Configure and preview The intuitive interface in Creator Hub lets you configure your settings with ease, and the built-in preview and test feature allows you to see the impact of your changes before they go live. #### Optimize with integrated analytics The dedicated analytics dashboard provides in-depth insights into signal metrics over time, allowing you to monitor the impact of your configurations on the player experience. ## Unlock the power of discovery #### Targeted discovery Roblox's "Recommended for You" algorithm intelligently connects each user with their ideal experiences, fostering deep engagement, social interaction, and repeat plays. #### Objective ranking Experiences are ranked by key performance indicators like playtime and Robux spent. The Home Recommendations dashboard displays the relevant analytics for informed decision-making. #### Quality content Roblox is committed to continuously improving search functionality to better understand user intent and showcase the highest-quality content, ensuring that exceptional experiences get the attention they deserve. ## Supercharge your growth #### Targeted user acquisition Roblox [Sponsored Ads](/docs/en-us/production/promotion/ads-manager.md) empower you to reach the players most likely to engage with your experience. With comprehensive audience selection options, you can tailor your campaigns to specific demographics and interests, delivering maximum impact and driving high-quality traffic to your game. #### Managed promotions Take control of your advertising strategy with flexible campaign scheduling and daily budget options. Roblox provides the tools you need to manage your ad spend effectively and track performance. [Go to Creator Hub](https://create.roblox.com) [Studio documentation](/docs/en-us/studio.md) [First experience tutorial](/docs/en-us/tutorials/first-experience.md) --- title: "Why should I develop on Roblox?" url: /docs/en-us/get-started/why-build-on-roblox last_updated: 2026-06-29T19:34:01Z description: "Reasons to build on the Roblox platform—audience, tools, and opportunity." --- # Why should I develop on Roblox? ## Robust tools with no upfront costs #### Hit the ground running We provide services, developer support, and free tools such as [Roblox Studio](/docs/en-us/studio.md) to help you get started immediately. #### Keep it simple We cover all hosting, storage, customer support, localization, payment processing, and platform fees, allowing you to focus on developing your experience. ## Multiplayer made easy #### Out of the box multiplayer All experiences support multiplayer out of the gate. No need to write complicated backend code, no need to set up or pay for your own servers. #### Servers support up to 100 players You donʼt need a big team to create a big game. Creating large multiplayer experiences has never been easier. #### Scale as you grow Weʼve significantly increased the limits on free data storage and now offer creators the option to [purchase additional service usage](https://create.roblox.com/docs/en-us/cloud-services/extended-services.md) to support even larger experiences. ## Publish, analyze, and iterate with ease #### No publishing approvals Assets are automatically moderated, so you can modify and publish your experience immediately across console, desktop, and mobile devices. #### Get deep insights Built-in Roblox analytics give you access to key retention, engagement, and monetization KPIs for free with no setup required. #### Track your traffic Acquire users with our paid UA system and get insights into how users are finding your game to optimize your UA strategy. --- title: "Gamepad input" url: /docs/en-us/input/gamepad last_updated: 2026-06-29T19:34:01Z description: "Explains how to accept input from USB gamepads, such as Xbox and PlayStation controllers." --- # Gamepad input Roblox accepts input from gamepads such as Xbox and PlayStation controllers. To simplify [cross‑platform](/docs/en-us/projects/cross-platform.md) inputs, including gamepads, Roblox provides the [Input Action System](/docs/en-us/input/input-action-system.md) to define **actions** such as "jump," "sprint," or "shoot" and set up **bindings** for multiple hardware inputs to drive those actions. When binding gamepad inputs, see [common control schemas](#common-control-schemas) to create a consistent gamepad experience for players. After inputs are set, you can enhance the player's experience by including [haptic feedback](#haptic-feedback) on supported controllers. As you build out support for gamepads, remember to test frequently using a connected gamepad or the [Controller Emulator](#controller-emulation) in Studio. ## Input type detection In cross‑platform development, it's important that you determine and respond to the `Class.UserInputService.PreferredInput|PreferredInput` type a player is using, normally to ensure that [UI elements](/docs/en-us/ui.md#ui-objects) like on-screen buttons and menus work elegantly and support interaction across devices. For example, a console assumes that gamepads are the default input, but a player on PC or laptop may also choose to connect a bluetooth gamepad. In this case, mouse/keyboard remains a valid input for that player, but you can assume they want to switch to the connected gamepad as the **primary** input type. See [input type detection](/docs/en-us/input.md#input-type-detection) for more information. > **Info:** For alternative gamepad detection methods, see the `Class.UserInputService.GamepadEnabled` property and the `Class.UserInputService.GamepadConnected|GamepadConnected`/`Class.UserInputService.GamepadDisconnected|GamepadDisconnected` events. ## Common control schemas When considering specific control bindings for the [Input Action System](/docs/en-us/input/input-action-system.md), it's best to establish consistency across different experiences. The following input bindings will help players immediately feel familiar and comfortable with gamepad controls. | Input | Common use cases | | --- | --- | | `Enum.KeyCode\|ButtonA` | Accepts player prompts or GUI selections. Alternatively used for primary actions such as jumping. | | `Enum.KeyCode\|ButtonB` | Cancels player prompts or GUI selections. Alternatively used for secondary actions such as a dodge, roll, or sprint. | | `Enum.KeyCode\|Thumbstick1` | Generally associated with character movement. | | `Enum.KeyCode\|Thumbstick2` | Generally associated with camera movement. | | `Enum.KeyCode\|ButtonL2`, `Enum.KeyCode\|ButtonR2` | Generally used for primary actions, such as shooting. | | `Enum.KeyCode\|ButtonL1`, `Enum.KeyCode\|ButtonR1`, `Enum.KeyCode\|ButtonX`, `Enum.KeyCode\|ButtonY` | Secondary actions such as reloading, targeting, or accessing an inventory or minimap. | #### Xbox #### PlayStation ## Haptic feedback Many gamepad controllers have motors built in to provide haptic feedback. Adding rumbles and vibrations can greatly enhance a player's experience and provide subtle feedback beyond visuals or audio. Roblox supports haptics for PlayStation gamepads, Xbox gamepads, and the Quest Touch controller. Haptic feedback is managed through `Class.HapticEffect` instances which can be set to a specific `Class.HapticEffect.Type|Type` such as `Enum.HapticEffectType|GameplayCollision` or `Enum.HapticEffectType|UIClick`. Once a `Class.HapticEffect` is in place, you can initiate it through the `Class.HapticEffect:Play()|Play()` method, for instance: ```lua local Workspace = game:GetService("Workspace") local effect = Instance.new("HapticEffect") effect.Type = Enum.HapticEffectType.GameplayExplosion effect.Parent = Workspace -- Play the haptic effect effect:Play() ``` ## Controller emulation The **Controller Emulator**, accessible from Studio's **Test** menu, lets you accurately emulate gamepad input directly in Studio. The default controller is a generic gamepad, but you can select alternatives from the upper‑left picker menu. ![View of the generic controller in the Controller Emulator.](../../assets/studio/general/Controller-Emulator.png) While playtesting, you can control the experience with the virtual controller using your mouse. You can also click **Edit mappings** in the upper‑right corner to view and edit key mappings for the virtual controller, for example `E` to `Enum.KeyCode.ButtonL2|ButtonL2` or `9` to `Enum.KeyCode.ButtonA|ButtonA`. These mappings are saved like other Studio settings (per controller, per user, per computer) and are translated to gamepad events in both the emulator window and the 3D viewport. --- title: "Input" url: /docs/en-us/input last_updated: 2026-06-29T19:34:02Z description: "Summary of cross-platform input on Roblox and implementation guidelines." --- # Input Every experience needs to receive user input for players to interact and view their environment. Roblox supports nearly all forms of input, including [mouse/keyboard](/docs/en-us/input/mouse-and-keyboard.md), [touch](/docs/en-us/input/mobile.md), [gamepads](/docs/en-us/input/gamepad.md), and VR. ## Cross-platform input Roblox is inherently [cross‑platform](/docs/en-us/projects/cross-platform.md), as players can discover and join experiences on their phone or tablet, then later continue where they left off on their PC or console. Input is especially important as part of your cross‑platform development plan. To simplify this process, Roblox provides the [Input Action System](/docs/en-us/input/input-action-system.md) to define **actions** such as "jump," "sprint," or "shoot" and set up **bindings** for multiple hardware inputs to drive those actions. This frees you from thinking of all the technical aspects of hardware inputs and allows you to simply define which inputs perform which actions. ## Input type detection In cross‑platform development, it's important that you determine and respond to the **primary** input type a player is using, normally to ensure that [UI elements](/docs/en-us/ui.md#ui-objects) like on‑screen buttons and menus work elegantly across devices. For example, a touch‑enabled device assumes touch is the default input and that touch buttons may appear for actions, but if a player connects an additional bluetooth keyboard/mouse or gamepad, you can assume they want to switch to that as the **primary** input type and possibly use touch as a backup input for on‑screen UI. The read‑only `Class.UserInputService.PreferredInput` property is a convenient way to test for and adapt to multiple input types across multiple device types, based on anticipated player behavior. #### Behavior The value of `Class.UserInputService.PreferredInput|PreferredInput` changes based on built‑in device inputs and the player's most recent interaction with a connected gamepad or keyboard/mouse. Examples include: | Real-World Scenario | `PreferredInput` | | --- | --- | | Player is using a phone with no other connected input devices; no possibility of an input type change. | `Enum.PreferredInput\|Touch` | | Player is using a mobile device with a bluetooth keyboard & mouse connected, but no gamepad is connected. | `Enum.PreferredInput\|KeyboardAndMouse` | | Player is using a tablet with a bluetooth gamepad connected, but no keyboard or mouse is connected. | `Enum.PreferredInput\|Gamepad` | | Player is using an Xbox or PlayStation with a bluetooth keyboard & mouse connected and has most recently interacted with the keyboard or mouse. | `Enum.PreferredInput\|KeyboardAndMouse` | | Player is on a Windows or Mac PC with a gamepad connected and has most recently interacted with the gamepad. | `Enum.PreferredInput\|Gamepad` | #### Benefits Importantly, the `Class.UserInputService.PreferredInput|PreferredInput` property overcomes the following issues related to `Class.UserInputService.TouchEnabled` and `Class.UserInputService:GetLastInputType()`, both traditionally used to detect input types. - `Class.UserInputService.TouchEnabled|TouchEnabled` is `true` on laptops with touch screens, so it is **not** a proper "is mobile" check. - UI layouts for gamepad or keyboard/mouse may be more appropriate even if a device has touch capability, for example a mobile device with a bluetooth gamepad connected. - Multiple `Class.UserInputService:GetLastInputType()|GetLastInputType()` values may represent the same input device, requiring compound `or` logic in scripts. For example, `Enum.UserInputType|MouseWheel`, `Enum.UserInputType|MouseMovement`, and `Enum.UserInputType|MouseButton1` all represent a mouse. - `Class.UserInputService:GetLastInputType()|GetLastInputType()` frequently thrashes between `Enum.UserInputType|MouseMovement` and `Enum.UserInputType|Keyboard` which may cause code to run more often than expected. Similarly, thrashes between `Enum.UserInputType|Touch` and `Enum.UserInputType|Gamepad[]` may cause on‑screen UI to flicker between layouts. - `Class.UserInputService:GetLastInputType()|GetLastInputType()` may return confusing values such as `Enum.UserInputType|TextInput`, `Enum.UserInputType|Focus`, and `Enum.UserInputType|InputMethod`. > **Info:** For advanced workflows or control schemes that rely on detecting and responding to the player's **specific** most recent UserInputType, you can continue using UserInputService:GetLastInputType() and UserInputService.LastInputTypeChanged. The following `Class.LocalScript` is a template for initially detecting the preferred input and responding to changes during gameplay. ```lua local UserInputService = game:GetService("UserInputService") local function preferredInputChanged() local preferredInput = UserInputService.PreferredInput if preferredInput == Enum.PreferredInput.Touch then -- Player is on touch-enabled device with no other input types available/connected print("Touch") elseif preferredInput == Enum.PreferredInput.Gamepad then -- Player has connected or most recently interacted with a gamepad print("Gamepad") elseif preferredInput == Enum.PreferredInput.KeyboardAndMouse then -- Player has connected or most recently interacted with a keyboard or mouse print("KeyboardAndMouse") end end preferredInputChanged() UserInputService:GetPropertyChangedSignal("PreferredInput"):Connect(function() preferredInputChanged() end) ``` ## Default bindings Roblox provides default input bindings for movement, camera control, and basic environment interaction — Roblox players are familiar with these controls, so you should only override them in specific cases. Also note that the **reserved** inputs cannot be overridden and will always operate with their intended purpose. #### Roblox Reserved | Action | Mouse/Keyboard | Gamepad | Touch | | --- | --- | --- | --- | | Open Roblox menu | `Esc` | `Start` button (`Enum.KeyCode\|ButtonStart`) | N/A | | [Developer Console](/docs/en-us/studio/developer-console.md) | `F9` | N/A | N/A | | Fullscreen mode (Windows)
Show desktop (Mac) | `F11` | N/A | N/A | | Record video (Windows) | `F12` | N/A | N/A | | Take screenshot | `PrintScreen` | N/A | N/A | #### Character | Action | Mouse/Keyboard | Gamepad | Touch | | --- | --- | --- | --- | | Move forward | `W` `↑` | Press up on primary thumbstick | Press up on virtual thumbstick | | Move backward | `S` `↓` | Press down on primary thumbstick | Press down on virtual thumbstick | | Move left | `A` | Press left on primary thumbstick | Press left on virtual thumbstick | | Move right | `D` | Press right on primary thumbstick | Press right on virtual thumbstick | | Jump | `Space` |   | Tap virtual jump button | #### Interface / Inventory > **Info:** The following action inputs are reserved unless you [disable the respective feature](/docs/en-us/players/disable-ui.md). | Action | Mouse/Keyboard | Gamepad | Touch | | --- | --- | --- | --- | | Open text chat | `/` | N/A | N/A | | Show/hide players list | `Tab` | N/A | N/A | | Toggle backpack | ``` | N/A | N/A | | Equip/unequip tools | `0`-`9` | Swap between tools using front‑left and front‑right triggers (`Enum.KeyCode.ButtonL1\|ButtonL1` and `Enum.KeyCode.ButtonR1\|ButtonR1`) | Tap on-screen tool icons | | Use equipped tool | Click left mouse button | Back‑right trigger (`Enum.KeyCode.ButtonR2\|ButtonR2`) | Tap on screen | | Drop equipped tool | `Backspace` | N/A | N/A | | Toggle **UI selection** mode¹ | `\` | `Select` button (`Enum.KeyCode.ButtonSelect\|ButtonSelect`) | N/A | | Navigate among interactive UI elements while in **UI selection** mode | `↑` `↓` `←` `→`
`W` `S` `A` `D` | Primary thumbstick (`Enum.KeyCode.Thumbstick1\|Thumbstick1`) or D‑pad (`Enum.KeyCode.DPadUp\|DPadUp`; `Enum.KeyCode.DPadDown\|DPadDown`; `Enum.KeyCode.DPadLeft\|DPadLeft`; `Enum.KeyCode.DPadRight\|DPadRight`) | | | Activate an interactive UI element while in **UI selection** mode | `Enter` | Back‑right trigger (`Enum.KeyCode.ButtonR2\|ButtonR2`) | | | Scroll up/down/left/right in selected `Class.ScrollingFrame` while in **UI selection** mode | `PageUp`
`PageDown`
`Home`
`End` | Secondary thumbstick (`Enum.KeyCode.Thumbstick2\|Thumbstick2`) | | _¹ If `Class.GuiService.GuiNavigationEnabled|GuiNavigationEnabled` is enabled (default)_ #### Camera | Action | Mouse/Keyboard | Gamepad | Touch | | --- | --- | --- | --- | | Move camera view around | While holding right mouse button | Move secondary thumbstick | Touch-drag around screen | | Zoom camera in/out | Mouse scroll wheel
`I`/`O` | Press secondary thumbstick | Pinch in/out on screen | | Rotate camera left or right | `←` `→` | N/A | N/A | | Toggle mouse lock² | `Shift` | N/A | N/A | _² If `Class.StarterPlayer.EnableMouseLockOption|EnableMouseLockOption` is enabled (default)_
--- title: "Input Action System" url: /docs/en-us/input/input-action-system last_updated: 2026-06-29T19:34:02Z description: "The cross-platform Input Action System lets you connect actions and arrange bindings across various hardware inputs at edit time." --- # Input Action System The cross-platform **Input Action System** lets you connect [actions](#input-actions) and arrange [bindings](#input-bindings) across various hardware inputs at edit time. Combined with [contexts](#input-contexts), you can easily configure and edit a modular input system that works on any device in any phase of play. Use cases include: - A first-person shooter system with actions dynamically swapping in and out depending on if the player is in battle mode or spectator mode. - A comprehensive driving system equipped with acceleration/deceleration, car boosters, and refuel stations. - Hotkeys for an abilities system in a fighting game to swap out moves seamlessly without players missing a punch. ## Input contexts An `Class.InputContext` is a collection of actions which holds related [input actions](#input-actions), for example `PlayContext` for in‑experience character controls and `NavContext` for controls to navigate around UI menus. You can [enable/disable contexts](#context-changes) (and their corresponding actions) through their `Class.InputContext.Enabled|Enabled` property, such as to enable the `NavContext` when an inventory menu is open and then change to the `PlayContext` when the player closes the menu and returns to primary gameplay. Even if an experience may not use multiple input contexts initially, it's recommended to create a primary context at the top level of any input system, for example the `PlayContext` instance for input that occurs during gameplay. 1. **RECOMMENDED** Create a `Class.Folder` named `Inputs` inside `Class.ReplicatedStorage` to hold various input contexts.![New Folder inside ReplicatedStorage, renamed to Inputs](../assets/studio/explorer/ReplicatedStorage-Folder-Inputs.png) 2. Insert a new `Class.InputContext` into the folder and rename it to `PlayContext`.![New InputContext instance inside ReplicatedStorage, renamed to PlayContext](../assets/studio/explorer/ReplicatedStorage-InputContext.png) 3. In the [Properties](/docs/en-us/studio/properties.md) window, set `Class.InputContext.Priority|Priority` to `2000` and enable `Class.InputContext.Sink|Sink`. A context with `Class.InputContext.Sink|Sink` enabled consumes input events for its bound `Enum.KeyCode|KeyCodes` at its priority level, blocking those inputs from reaching lower-priority contexts. This is practical for use cases like an inventory screen that should suppress specific gameplay inputs while open. In this example, `PlayContext` is given a high enough `Class.InputContext.Priority|Priority` to sink its bound inputs before the default `Class.PlayerScripts` contexts process them. ## Input actions An `Class.InputAction` defines a gameplay action mechanic such as "Jump," "Sprint," or "Shoot." These actions are then mapped to hardware inputs using [input bindings](#input-bindings). An `Class.InputAction` can be of several variations depending on its `Class.InputAction.Type|Type` property (`Enum.InputActionType`). The default is `Enum.InputActionType|Bool`, designed to receive `true`/`false` values from press/release of inputs such as `Enum.KeyCode.ButtonA|ButtonA`, `Enum.KeyCode.E|E`, or `Enum.KeyCode.MouseLeftButton|MouseLeftButton`. | Input Action Type | Example Usage | | --- | --- | | `Enum.InputActionType\|Bool` | Triggered actions such as jump, shoot, sprint, etc. with support for pressed/released thresholds on analog inputs. | | `Enum.InputActionType\|Direction1D` | Variable zero-to-full actions such as a car's accelerator pedal or a view scope's zoom level. | | `Enum.InputActionType\|Direction2D` | 2D directional movement such as camera rotation, or the standard Roblox character movement. | | `Enum.InputActionType\|Direction3D` | 3D directional movement like an airborne vehicle that can levitate up/down, accelerate/decelerate, and drift left/right. | | `Enum.InputActionType\|ViewportPosition` | 2D viewport coordinates like mouse input, such as for custom cursors or raycasting to select world objects. | #### Character Sprint To test an `Class.InputAction` for simple character sprinting: 1. Create a new `Class.InputAction` inside the `PlayContext` context within `Class.ReplicatedStorage`. Rename it to `CharacterSprint` to indicate its dedicated action.![New InputAction instance inside an InputContext, renamed to CharacterSprint](../assets/studio/explorer/ReplicatedStorage-InputContext-SprintAction.png) 2. In the [Properties](/docs/en-us/studio/properties.md) window, notice that the action's `Class.InputAction.Type|Type` is `Enum.InputActionType|Bool` (default). This is a logical type for simple character sprinting as a boolean `true`/`false` action (character is either sprinting or not sprinting).![Type property of an InputAction set to Bool](../assets/studio/properties/SprintAction-Type-Bool.png) #### Camera Rotation To test an `Class.InputAction` for camera rotation: 1. Create a new `Class.InputAction` inside the `PlayContext` context within `Class.ReplicatedStorage`. Rename it to `CameraRotate` to indicate its dedicated action.![New InputAction instance inside an InputContext, renamed to CameraRotate](../assets/studio/explorer/ReplicatedStorage-InputContext-CameraAction.png) 2. In the [Properties](/docs/en-us/studio/properties.md) window, set the action's `Class.InputAction.Type|Type` to `Enum.InputActionType|Direction2D`. This reflects that camera rotation is a continuous 2D analog input rather than a discrete press/release.![Type property of an InputAction set to Direction2D](../assets/studio/properties/CameraAction-Type-Direction2D.png) ## Input bindings An `Class.InputBinding` defines which hardware binding should trigger the parent `Class.InputAction`, for example a key press, gamepad button, or tap on a touch‑enabled device. For [cross‑platform](/docs/en-us/projects/cross-platform.md) compatibility, each `Class.InputAction` should have an `Class.InputBinding` for **gamepad**, **keyboard/mouse**, and **touch** as illustrated here. The `Class.InputAction.Type|Type` assigned to the parent `Class.InputAction` directly affects which general input types (key/button/tap, analog trigger, thumbstick, etc.) are valid for child `Class.InputBinding` instances. In turn, values sent to the parent action's connected events depend on a binding's chosen input type. See [input events](#input-events) for details on the correlation between action types, bindings, and return values. #### Character Sprint To hook up bindings for simple character sprinting: 1. Insert a new `Class.InputBinding` into the `CharacterSprint` action and rename it to `KeyboardBinding`. Then set the binding's `Class.InputBinding.KeyCode|KeyCode` property to `Enum.KeyCode.LeftShift|LeftShift`. 2. To ensure mouse `Shift`-lock does not interfere with the key binding, select `Class.StarterPlayer` in the [Explorer](/docs/en-us/studio/explorer.md) and disable its `Class.StarterPlayer.EnableMouseLockOption|EnableMouseLockOption` in the [Properties](/docs/en-us/studio/properties.md) window. 3. Insert a second `Class.InputBinding` into the `CharacterSprint` action and rename it to `GamepadBinding`. Then set the binding's `Class.InputBinding.KeyCode|KeyCode` property to `Enum.KeyCode.ButtonY|ButtonY`. 4. Inside a `Class.ScreenGui` container inside `Class.StarterGui`, create an [on-screen button](/docs/en-us/ui/buttons.md), rename it to `SprintButton`, and [position/resize](/docs/en-us/ui/position-and-size.md) it as desired. 5. Insert a third `Class.InputBinding` into the `CharacterSprint` action and rename it to `TouchBinding`. Then, in the [Properties](/docs/en-us/studio/properties.md) window, link the binding's `Class.InputBinding.UIButton|UIButton` property to the `SprintButton` button you created previously inside `Class.StarterGui`. #### Camera Rotation To hook up bindings for camera rotation: 1. Insert a new `Class.InputBinding` into the `CameraRotate` action and rename it to `KeyboardBinding`. Leave `Class.InputBinding.KeyCode|KeyCode` empty and instead set the four composite directional properties — `Class.InputBinding.Left|Left`, `Class.InputBinding.Right|Right`, `Class.InputBinding.Up|Up`, and `Class.InputBinding.Down|Down` — to `Enum.KeyCode.Left|Left`, `Enum.KeyCode.Right|Right`, `Enum.KeyCode.Up|Up`, and `Enum.KeyCode.Down|Down` respectively. 2. Insert a second `Class.InputBinding` and rename it to `GamepadBinding`. Then set the binding's `Class.InputBinding.KeyCode|KeyCode` property to `Enum.KeyCode.Thumbstick2|Thumbstick2` (the right thumbstick). 3. Insert a third `Class.InputBinding` and rename it to `MouseBinding`. Set the binding's `Class.InputBinding.KeyCode|KeyCode` property to `Enum.KeyCode.MouseDelta|MouseDelta` and its `Class.InputBinding.Scale|Scale` to `0.01`. `Enum.KeyCode.MouseDelta|MouseDelta` reports values in pixels, so the scale converts them to a reasonable rotation range. 4. Insert a fourth `Class.InputBinding` and rename it to `TouchBinding`. Set the binding's `Class.InputBinding.KeyCode|KeyCode` property to `Enum.KeyCode.TouchDelta|TouchDelta` and its `Class.InputBinding.Scale|Scale` to `0.01`. Like `Enum.KeyCode.MouseDelta|MouseDelta`, `Enum.KeyCode.TouchDelta|TouchDelta` reports values in pixels. ## Input events The `Class.InputAction` instance has three built-in **events** to handle player input coming from `Class.InputBinding|InputBindings`. - `Class.InputAction.Pressed|Pressed` — This event fires only when the input action's `Class.InputAction.Type|Type` is set to `Enum.InputActionType|Bool`, and only when the state transitions from `false` to `true`. - `Class.InputAction.Released|Released` — This event fires only when the input action's `Class.InputAction.Type|Type` is set to `Enum.InputActionType|Bool`, and only when the state transitions from `true` to `false`. - `Class.InputAction.StateChanged|StateChanged` — This event fires for all input action types whenever the state changes, except if the state attempts to transition to the same state. Depending on the input action's `Class.InputAction.Type|Type` (`Enum.InputActionType|Bool`, `Enum.InputActionType|Direction1D`, `Enum.InputActionType|Direction2D`, `Enum.InputActionType|Direction3D`, or `Enum.InputActionType|ViewportPosition`) and the general input type coming from a child `Class.InputBinding` (key/button/tap, analog trigger, thumbstick, etc.), different values are returned to the `Class.InputAction.Pressed|Pressed`, `Class.InputAction.Released|Released`, and `Class.InputAction.StateChanged|StateChanged` event handlers. Examine the following tables to better understand the correlation. #### Bool The `Enum.InputActionType|Bool` type is best for triggered actions such as jump, shoot, sprint, etc. with support for pressed/released thresholds on analog inputs. | Valid Input Types on `Class.InputBinding\|InputBindings` | Returned to the `Class.InputAction` Event(s) | | --- | --- | | Boolean inputs from keyboard keys or basic mouse/gamepad buttons through the binding's `Class.InputBinding.KeyCode\|KeyCode` property, or a `Class.GuiButton` press/release through the binding's `Class.InputBinding.UIButton\|UIButton` property. | | | Variable input amounts from analog inputs like gamepad triggers (`Enum.KeyCode.ButtonL2\|ButtonL2`/`Enum.KeyCode.ButtonR2\|ButtonR2`) through the binding's `Class.InputBinding.KeyCode\|KeyCode` property. | | #### Direction1D The `Enum.InputActionType|Direction1D` type is best for variable zero‑to‑full actions such as a car's accelerator pedal or a view scope's zoom level. | Valid Input Types on `Class.InputBinding\|InputBindings` | Returned to the `Class.InputAction` Event(s) | | --- | --- | | Variable input amounts from analog inputs like gamepad triggers (`Enum.KeyCode.ButtonL2\|ButtonL2`/`Enum.KeyCode.ButtonR2\|ButtonR2`) through the binding's `Class.InputBinding.KeyCode\|KeyCode`, `Class.InputBinding.Up\|Up`, and `Class.InputBinding.Down\|Down` properties. | | | Boolean inputs from keyboard keys or basic mouse/gamepad buttons through the binding's `Class.InputBinding.KeyCode\|KeyCode`, `Class.InputBinding.Up\|Up`, and `Class.InputBinding.Down\|Down` properties. | | #### Direction2D The `Enum.InputActionType|Direction2D` type is best for 2D directional movement such as camera rotation, or the standard Roblox character movement. | Valid Input Types on `Class.InputBinding\|InputBindings` | Returned to the `Class.InputAction` Event(s) | | --- | --- | | Variable input amounts from 2D analog inputs like gamepad thumbsticks (`Enum.KeyCode.Thumbstick1\|Thumbstick1`/`Enum.KeyCode.Thumbstick2\|Thumbstick2`) through the binding's `Class.InputBinding.KeyCode\|KeyCode` property. | | | Variable input amounts from analog inputs like gamepad triggers (`Enum.KeyCode.ButtonL2\|ButtonL2`/`Enum.KeyCode.ButtonR2\|ButtonR2`) through the binding's `Class.InputBinding.Up\|Up`, `Class.InputBinding.Down\|Down`, `Class.InputBinding.Left\|Left`, and `Class.InputBinding.Right\|Right` properties. | | | Boolean inputs from keyboard keys or basic mouse/gamepad buttons through the binding's `Class.InputBinding.Up\|Up`, `Class.InputBinding.Down\|Down`, `Class.InputBinding.Left\|Left`, and `Class.InputBinding.Right\|Right` properties. | | #### Direction3D The `Enum.InputActionType|Direction3D` type is best for 3D directional movement like an airborne vehicle that can levitate up/down, accelerate/decelerate, and drift left/right. | Valid Input Types on `Class.InputBinding\|InputBindings` | Returned to the `Class.InputAction` Event(s) | | --- | --- | | Variable input amounts from analog inputs like gamepad triggers (`Enum.KeyCode.ButtonL2\|ButtonL2`/`Enum.KeyCode.ButtonR2\|ButtonR2`) through the binding's `Class.InputBinding.Up\|Up`, `Class.InputBinding.Down\|Down`, `Class.InputBinding.Left\|Left`, `Class.InputBinding.Right\|Right`, `Class.InputBinding.Forward\|Forward`, and `Class.InputBinding.Backward\|Backward` properties. | | | Boolean inputs from keyboard keys or basic mouse/gamepad buttons through the binding's `Class.InputBinding.Up\|Up`, `Class.InputBinding.Down\|Down`, `Class.InputBinding.Left\|Left`, `Class.InputBinding.Right\|Right`, `Class.InputBinding.Forward\|Forward`, and `Class.InputBinding.Backward\|Backward` properties. | | #### ViewportPosition The `Enum.InputActionType|ViewportPosition` type is best for absolute 2D viewport coordinates of an input such as a mouse pointer or touch, which can be used for features like custom cursors or selecting objects. | Valid Input Types on `Class.InputBinding\|InputBindings` | Returned to the `Class.InputAction` Event(s) | | --- | --- | | Variable input amounts from positional inputs such as a mouse pointer (`Enum.KeyCode.MousePosition\|MousePosition`) or touch (`Enum.KeyCode.Touch\|Touch`). | | #### Character Sprint To connect events for simple character sprinting: 1. Insert a new `Class.Script` into the `CharacterSprint` tree, alongside the various input bindings. Then set its `Class.BaseScript.RunContext|RunContext` to `Enum.RunContext.Client|Client` and rename it to `OnActivate`. 2. Paste the following code into the `OnActivate` script. Note the `Class.InputAction.Pressed|Pressed` event connection on lines `13`‑`15` which doubles the character's walk speed when a sprint input binding is pressed, and the corresponding `Class.InputAction.Released|Released` event connection on lines `16`‑`18` which resets the walk speed to default when a sprint input binding is released.```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local character = player.Character if not character or character.Parent == nil then character = player.CharacterAdded:Wait() end local humanoid = character:WaitForChild("Humanoid") local defaultWalkSpeed = humanoid.WalkSpeed local inputAction = script.Parent inputAction.Pressed:Connect(function() humanoid.WalkSpeed = defaultWalkSpeed * 2 end) inputAction.Released:Connect(function() humanoid.WalkSpeed = defaultWalkSpeed end) ``` 3. Playtest your experience and test the character sprint action with the [bindings](#input-bindings) you chose previously: `Enum.KeyCode.LeftShift|LeftShift` for keyboard, `Enum.KeyCode.ButtonY|ButtonY` for gamepad, and the on‑screen `SprintButton` for touch‑enabled devices. Remember that you can use the [Controller Emulator](/docs/en-us/studio/testing-modes.md#controller-emulation) to test gamepad inputs directly in Roblox Studio. #### Camera Rotation For **continuous analog inputs** like camera rotation, `Class.InputAction.StateChanged|StateChanged` fires **once** when the state changes and does not signal until the next change, so a thumbstick held at a fixed angle only fires the event once. In these cases, poll `Class.InputAction:GetState()|GetState()` each frame and multiply by delta time for frame-rate-independent behavior. 1. Insert a new `Class.Script` into the `CameraRotate` tree, alongside the various input bindings. Then set its `Class.BaseScript.RunContext|RunContext` to `Enum.RunContext.Client|Client` and rename it to `OnInput`. 2. Paste the following code into the `OnInput` script. Note that it uses `Class.RunService:BindToRenderStep()|BindToRenderStep()` with `Enum.RenderPriority|Enum.RenderPriority.Camera.Value` rather than `Class.RunService.RenderStepped|RenderStepped`; this guarantees the camera update runs after the engine's input processing step and stays synchronized with the default `Class.PlayerScripts` camera pipeline.```lua local RunService = game:GetService("RunService") local CAMERA_SENSITIVITY = 1.5 -- Radians per second at action state 1 local inputAction = script.Parent local camera = workspace.CurrentCamera RunService:BindToRenderStep("CameraRotation", Enum.RenderPriority.Camera.Value, function(dt) local state = inputAction:GetState() if state.Magnitude > 0 then camera.CFrame = camera.CFrame * CFrame.Angles(0, -state.X * CAMERA_SENSITIVITY * dt, 0) * CFrame.Angles(-state.Y * CAMERA_SENSITIVITY * dt, 0, 0) end end) ``` ## Context changes Once you have an [input context](#input-contexts) such as `PlayContext`, you can enable/disable it during gameplay through scripting, change its `Class.InputContext.Priority|Priority` to determine which actions take precedence over others, and `Class.InputContext.Sink|Sink` inputs from being processed within contexts of lower priority. 1. To make it easier to switch contexts from other scripts, insert a new `Class.BindableEvent` into your inputs folder within `Class.ReplicatedStorage` and rename it to `ContextEvent`. 2. Create a new `Class.Script` at the same level, set its `Class.BaseScript.RunContext|RunContext` to `Enum.RunContext.Client|Client`, and rename it to `UpdateContext`. 3. Inside the `UpdateContext` script, paste the following code:```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local inputsFolder = ReplicatedStorage:WaitForChild("Inputs") local contextEvent = inputsFolder:WaitForChild("ContextEvent") -- Connect bindable event contextEvent.Event:Connect(function(targetContext, enabled) local context = inputsFolder:FindFirstChild(targetContext) if context then context.Enabled = enabled print(context.Name .. ": " .. tostring(context.Enabled)) else warn("InputContext not found!") end end) ``` 4. With the `UpdateContext` script in place, you can now update a named `Class.InputContext` by firing the bindable event, for example from a `Class.LocalScript` that powers a `Class.GuiButton` inside a `Class.ScreenGui` container.```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local inputsFolder = ReplicatedStorage:WaitForChild("Inputs") local contextEvent = inputsFolder:WaitForChild("ContextEvent") local button = script.Parent button.Activated:Connect(function() -- Fire bindable event with target input context and enabled state contextEvent:Fire("PlayContext", true) end) ``` ## Default bindings Roblox provides default input bindings for movement, camera control, and basic environment interaction — Roblox players are familiar with these controls, so you should only override them in specific cases. Also note that the **reserved** inputs cannot be overridden and will always operate with their intended purpose. #### Roblox Reserved | Action | Mouse/Keyboard | Gamepad | Touch | | --- | --- | --- | --- | | Open Roblox menu | `Esc` | `Start` button (`Enum.KeyCode\|ButtonStart`) | N/A | | [Developer Console](/docs/en-us/studio/developer-console.md) | `F9` | N/A | N/A | | Fullscreen mode (Windows)
Show desktop (Mac) | `F11` | N/A | N/A | | Record video (Windows) | `F12` | N/A | N/A | | Take screenshot | `PrintScreen` | N/A | N/A | #### Character | Action | Mouse/Keyboard | Gamepad | Touch | | --- | --- | --- | --- | | Move forward | `W` `↑` | Press up on primary thumbstick | Press up on virtual thumbstick | | Move backward | `S` `↓` | Press down on primary thumbstick | Press down on virtual thumbstick | | Move left | `A` | Press left on primary thumbstick | Press left on virtual thumbstick | | Move right | `D` | Press right on primary thumbstick | Press right on virtual thumbstick | | Jump | `Space` |   | Tap virtual jump button | #### Interface / Inventory > **Info:** The following action inputs are reserved unless you [disable the respective feature](/docs/en-us/players/disable-ui.md). | Action | Mouse/Keyboard | Gamepad | Touch | | --- | --- | --- | --- | | Open text chat | `/` | N/A | N/A | | Show/hide players list | `Tab` | N/A | N/A | | Toggle backpack | ``` | N/A | N/A | | Equip/unequip tools | `0`-`9` | Swap between tools using front‑left and front‑right triggers (`Enum.KeyCode.ButtonL1\|ButtonL1` and `Enum.KeyCode.ButtonR1\|ButtonR1`) | Tap on-screen tool icons | | Use equipped tool | Click left mouse button | Back‑right trigger (`Enum.KeyCode.ButtonR2\|ButtonR2`) | Tap on screen | | Drop equipped tool | `Backspace` | N/A | N/A | | Toggle **UI selection** mode¹ | `\` | `Select` button (`Enum.KeyCode.ButtonSelect\|ButtonSelect`) | N/A | | Navigate among interactive UI elements while in **UI selection** mode | `↑` `↓` `←` `→`
`W` `S` `A` `D` | Primary thumbstick (`Enum.KeyCode.Thumbstick1\|Thumbstick1`) or D‑pad (`Enum.KeyCode.DPadUp\|DPadUp`; `Enum.KeyCode.DPadDown\|DPadDown`; `Enum.KeyCode.DPadLeft\|DPadLeft`; `Enum.KeyCode.DPadRight\|DPadRight`) | | | Activate an interactive UI element while in **UI selection** mode | `Enter` | Back‑right trigger (`Enum.KeyCode.ButtonR2\|ButtonR2`) | | | Scroll up/down/left/right in selected `Class.ScrollingFrame` while in **UI selection** mode | `PageUp`
`PageDown`
`Home`
`End` | Secondary thumbstick (`Enum.KeyCode.Thumbstick2\|Thumbstick2`) | | _¹ If `Class.GuiService.GuiNavigationEnabled|GuiNavigationEnabled` is enabled (default)_ #### Camera | Action | Mouse/Keyboard | Gamepad | Touch | | --- | --- | --- | --- | | Move camera view around | While holding right mouse button | Move secondary thumbstick | Touch-drag around screen | | Zoom camera in/out | Mouse scroll wheel
`I`/`O` | Press secondary thumbstick | Pinch in/out on screen | | Rotate camera left or right | `←` `→` | N/A | N/A | | Toggle mouse lock² | `Shift` | N/A | N/A | _² If `Class.StarterPlayer.EnableMouseLockOption|EnableMouseLockOption` is enabled (default)_
--- title: "Mobile input" url: /docs/en-us/input/mobile last_updated: 2026-06-29T19:34:01Z description: "Explains Roblox support for mobile devices." --- # Mobile input The majority of Roblox sessions are played on mobile devices, so it's important to consider the mobile [device orientation](#device-orientation) and other [cross‑platform](/docs/en-us/projects/cross-platform.md) factors when designing an experience for a wide audience. To simplify cross‑platform inputs, including player interaction with virtual buttons, Roblox provides the [Input Action System](/docs/en-us/input/input-action-system.md) to define **actions** such as "jump," "sprint," or "shoot" and set up **bindings** for multiple hardware inputs to drive those actions. Roblox also supports native mobile features like [haptic feedback](#haptic-feedback), touch gestures such as `Class.UserInputService.TouchSwipe|TouchSwipe` and `Class.UserInputService.TouchPinch|TouchPinch`, or accelerometer and gyroscope functionality on `Class.UserInputService.AccelerometerEnabled|AccelerometerEnabled` and `Class.UserInputService.GyroscopeEnabled|GyroscopeEnabled` devices. ## Input type detection In cross‑platform development, it's important that you determine and respond to the `Class.UserInputService.PreferredInput|PreferredInput` type a player is using, normally to ensure that [UI elements](/docs/en-us/ui.md#ui-objects) like on-screen buttons and menus work elegantly and support interaction across devices. For example, a touch-enabled device assumes touch is the default input and that touch-buttons may appear for actions, but a player may choose to connect a bluetooth gamepad. In this case, touch remains a valid input, but you can assume the player wants to switch to the connected gamepad as the **primary** input type and possibly use touch as a backup input for on-screen UI. See [input type detection](/docs/en-us/input.md#input-type-detection) for more information. ## Device orientation By default, Roblox experiences run in **landscape** mode, allowing the experience to switch between landscape "left" and landscape "right" as the player's device rotates. However, experiences can be locked to a particular orientation if desired. There are five different orientation modes, including two sensor-based modes and three locked modes.¹ Each of these can be set as the **default** orientation through `Class.StarterGui.ScreenOrientation`, accessible through Studio's [Properties](/docs/en-us/studio/properties.md) window. | Sensor Modes | | --- | | `Enum.ScreenOrientation\|LandscapeSensor` | The default Roblox setting in which the experience always appears in landscape mode (no portrait mode) and the device detects its physical orientation to ensure the experience view is always oriented upward. | | `Enum.ScreenOrientation\|Sensor` | The device detects its physical orientation to ensure the experience view is always oriented upward, switching between landscape and portrait mode as needed. | | Locked Modes | | `Enum.ScreenOrientation\|LandscapeLeft` | On devices with a virtual home/nav bar, its touch region is at the bottom of the display. On devices with a physical home button, the home button is to the left of the display. | | `Enum.ScreenOrientation\|LandscapeRight` | On devices with a virtual home/nav bar, its touch region is at the bottom of the display. On devices with a physical home button, the home button is to the right of the display. | | `Enum.ScreenOrientation\|Portrait` | On devices with a virtual home/nav bar, its touch region is at the bottom of the display. On devices with a physical home button, the home button is below the display. | _¹ Roblox does not include a "portrait upside-down" mode since many devices do not natively support that orientation_ Beyond the default `Class.StarterGui.ScreenOrientation` property, you can change orientation during gameplay through `Class.PlayerGui.ScreenOrientation`. This can be useful when an experience needs to enforce an orientation, such as locking the view to `Enum.ScreenOrientation|Portrait` for a minigame. The following code in a `Class.LocalScript` sets the screen orientation to portrait after a short delay: ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local playerGui = player.PlayerGui task.wait(5) playerGui.ScreenOrientation = Enum.ScreenOrientation.Portrait ``` Finally, you can detect changes to screen orientation during gameplay by listening to value changes on the `Class.PlayerGui.CurrentScreenOrientation` property. ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local playerGui = player.PlayerGui local function onOrientationChange() print("Orientation changed to", playerGui.ScreenOrientation) end playerGui:GetPropertyChangedSignal("ScreenOrientation"):Connect(onOrientationChange) ``` ## Haptic feedback Most mobile devices have motors built in to provide haptic feedback. Adding rumbles and vibrations can greatly enhance a player's experience and provide subtle feedback beyond visuals or audio. Roblox supports haptics for haptic-capable devices including most iPhone, Pixel, and Samsung Galaxy devices. Haptic feedback is managed through `Class.HapticEffect` instances which can be set to a specific `Class.HapticEffect.Type|Type` such as `Enum.HapticEffectType|GameplayCollision` or `Enum.HapticEffectType|UIClick`. Once a `Class.HapticEffect` is in place, you can initiate it through the `Class.HapticEffect:Play()|Play()` method, for instance: ```lua local Workspace = game:GetService("Workspace") local effect = Instance.new("HapticEffect") effect.Type = Enum.HapticEffectType.GameplayExplosion effect.Parent = Workspace -- Play the haptic effect effect:Play() ``` --- title: "Mouse and keyboard input" url: /docs/en-us/input/mouse-and-keyboard last_updated: 2026-06-29T19:34:01Z description: "Explains Roblox support for computer mice and keyboards." --- # Mouse and keyboard input A large percentage of Roblox sessions are played on devices with a mouse and keyboard, so it's important to support these inputs when designing an experience for a wide audience. To simplify [cross‑platform](/docs/en-us/projects/cross-platform.md) inputs, including mouse/keyboard, Roblox provides the [Input Action System](/docs/en-us/input/input-action-system.md) to define **actions** such as "jump," "sprint," or "shoot" and set up **bindings** for multiple hardware inputs to drive those actions. Roblox also supports general mouse/keyboard input through `Class.UserInputService` events like `Class.UserInputService.InputBegan|InputBegan` and `Class.UserInputService.InputEnded|InputEnded` and methods like `Class.UserInputService:IsKeyDown()|IsKeyDown()` to check if a particular key is pressed on a keyboard. ## Input type detection In cross‑platform development, it's important that you determine and respond to the `Class.UserInputService.PreferredInput|PreferredInput` type a player is using, normally to ensure that [UI elements](/docs/en-us/ui.md#ui-objects) like on-screen buttons and menus work elegantly and support interaction across devices. For example, a PC or laptop assumes mouse/keyboard is the default input, but a player may choose to connect a bluetooth gamepad. In this case, mouse/keyboard remains a valid input, but you can assume the player wants to switch to the connected gamepad as the **primary** input type. See [input type detection](/docs/en-us/input.md#input-type-detection) for more information. ## Generic mouse/key input Beyond the [Input Action System](/docs/en-us/input/input-action-system.md), you can capture mouse and keyboard inputs using `Class.UserInputService`. The following `Class.LocalScript`, when placed in `Class.StarterPlayerScripts`, captures the began and ended phases of key and mouse clicks and prints the result to the [Output](/docs/en-us/studio/output.md) window. ```lua local UserInputService = game:GetService("UserInputService") local function onInputBegan(inputObject, processedEvent) -- Return if another script has already processed the input if processedEvent then return end print(inputObject.KeyCode.Name, "began!", inputObject.Position) end local function onInputEnded(inputObject, processedEvent) -- Return if another script has already processed the input if processedEvent then return end print(inputObject.KeyCode.Name, "ended!", inputObject.Position) end UserInputService.InputBegan:Connect(onInputBegan) UserInputService.InputEnded:Connect(onInputEnded) ``` ## Mouse icon You can customize the appearance and behavior of a player's mouse icon within your experience to create a cohesive style across all of your UI elements. This includes temporarily changing the player's mouse icon in specific circumstances, such as hovering over a button. #### Change Icon You can change the player's mouse icon in a `Class.LocalScript` by setting the `Class.UserInputService.MouseIcon|MouseIcon` property in `Class.UserInputService` to a custom Roblox asset ID. For example, the following `Class.LocalScript` changes the player's default mouse icon to a circle with a blue dot in the middle. ```lua local UserInputService = game:GetService("UserInputService") UserInputService.MouseIcon = "rbxassetid://3400146391" ``` #### Hide Icon You can hide the player's mouse icon by setting the `Class.UserInputService.MouseIconEnabled` to `false` in a `Class.LocalScript`. For example, the following code switches the mouse icon from visible to invisible and back every two seconds. ```lua local UserInputService = game:GetService("UserInputService") while true do task.wait(2) UserInputService.MouseIconEnabled = false task.wait(2) UserInputService.MouseIconEnabled = true end ``` #### Lock Icon You can lock the mouse icon's position to the screen using `Class.UserInputService.MouseBehavior` with a `Enum.MouseBehavior` value of `Enum.MouseBehavior|LockCurrentPosition` or `Enum.MouseBehavior|LockCenter`, then unlock it again with a value of `Enum.MouseBehavior|Default`. If a player's mouse icon is locked in a position, `Class.UserInputService.InputChanged` still fires when the player moves the mouse, passing in the distance the mouse has moved. For example, the following code sample locks the player's mouse icon after one second, then Studio prints the mouse delta whenever the player moves their mouse. ```lua local UserInputService = game:GetService("UserInputService") task.wait(5) UserInputService.MouseBehavior = Enum.MouseBehavior.LockCurrentPosition UserInputService.InputChanged:Connect(function(inputObject) if inputObject.UserInputType == Enum.UserInputType.MouseMovement then print("Mouse delta is (" .. tostring(inputObject.Delta.X) .. ", " .. tostring(inputObject.Delta.Y) .. ")") end end) ``` --- title: "IP licensing for creators" url: /docs/en-us/ip-licensing/creators last_updated: 2026-06-29T19:34:01Z description: "How to use IP licensing as a creator." --- # IP licensing for creators Creating experiences using approved popular IP can make your content more recognizable to users and help you reach new audiences that already know and love certain characters, settings, and storylines. As a creator, you can browse the [Licenses catalog](https://create.roblox.com/explore/licenses) and request to use an available IP in your experience. A license allows you to create a full Roblox experience based on an IP while following the conditions and revenue share outlined by the rights holder. > **Warning:** Roblox takes intellectual property infringement very seriously. Publishing content on Roblox that uses someone else's IP rights without permission violates the [Roblox Terms of Use](https://en.help.roblox.com/hc/en-us/articles/115004647846-Roblox-Terms-of-Use) and might result in the removal of the content and your Roblox account. ## Explore licenses To explore available licenses in the Licenses catalog: 1. In the **Explore** dropdown in the Creator Hub, click **Licenses**. 2. Click an available IP license to see the details page for that specific license. ![Screenshot of Licenses page.](../assets/ip-licensing/LicensesCatalog.png) 3. Click **View details** to see more details about the license, like eligibility requirements and content standards. ## Request to use a license > **Info:** Before requesting to use an IP license, make sure your experience is public. To request to use an IP license in your experience: 1. In the **Explore** dropdown in the Creator Hub, click **Licenses**. 2. Select an IP from the available licenses. 3. Click **Request license**. 4. Select the experience you want to use for this license request and click **Next**. 5. Under **Intent**, enter more details about your experience in the form of a pitch. This can help the rights holder make a decision about your license request. For more information about writing a pitch, see [Pitch details](#pitch-details). 6. (For perpetual licenses) Under **Experience readiness**, select one of the following: - **Yes, my experience is ready to use this intellectual property** if you're done incorporating the IP into your experience. Revenue share will start as soon as the license agreement becomes active. - **No, I need time to incorporate this intellectual property into my experience** if you're not done incorporating the IP into your experience. Revenue share won't start immediately, but the rights holder can still turn on monetization at any time. 7. (For time-limited licenses) Under **Proposed date range**, use the calendar to set the proposed dates that your experience will incorporate the IP. Start and end times for time-limited licenses are based on midnight UTC. 8. Acknowledge licensing terms, review the guidelines and restrictions, and click **Next**. 9. Click **Submit** to submit your license request. The rights holder then reviews and either approves or rejects your license request. > **Info:** You can currently only request or be offered a license for a full experience, where the IP is central to and present throughout the experience. > **Warning:** Early use of IP in your experience may lead to early activation of a license request or accepted license agreement. > **Info:** You can cancel a pending license request under certain conditions. See [Cancel a license](#cancel-a-license) for details. ### Pitch details Including a well-written pitch with your request can make the rights holder more likely to approve your license request. Your pitch should convey a compelling idea and show that you are confident in your ability to execute this creative vision. We recommend that you include the following information in your pitch: - **Overall vision**: - What makes your adaption of the IP unique and exciting for players. - Your team's relevant experience, including examples of previous experiences you have developed, along with any previous work involving IP. - How you believe the IP integration will engage players, expand the IP's reach, and create new opportunities for monetization and community growth. - **Creative elements**: - How your experience's visual and audio design will reflect the IP, including character models, environments, UI, and soundscapes. For example, if you're requesting to integrate an IP like the Blair Witch, you can highlight the use of atmospheric visuals and sound effects that evoke suspense and dread. - How the story, lore, or signature moments from the IP will be integrated into your experience. This could involve recreating iconic scenes, referencing key plot points, or introducing original storylines that fit within the universe of the IP. - How you will maintain the integrity of the IP, making sure all creative choices align with the IP's established tone, themes, and content standards. - **Gameplay**: - How the main gameplay loops will leverage the IP's unique features. For example, a survival horror IP could include mechanics like stealth, resource management, or cooperative puzzle-solving. - How players will interact with the IP. You can describe progression systems, challenges, and rewards that are consistent with the theme of the IP. - How your experience genre aligns with the IP. For example, an IP like The Strangers might be more aligned with PvE escape games or social deduction horror, while an IP like Fall might be more aligned with high-stakes obbys or survival challenges. - **Timeline**: - What the current state of your experience is and when you expect the IP to be fully integrated into your experience. For more information about guidelines you must follow when writing your pitch, see [Communication guidelines](/docs/en-us/ip-licensing/faq.md#communication-guidelines). ## View license agreements To view all of your offers and license agreements, go to **Intellectual Property** ⟩ **Licenses**. You can filter all license agreements by: | **Filter** | **Description** | | --- | --- | | **Offers** | All license offers that rights holders have sent you. | | **Requests** | All license requests that you have sent to rights holders. | | **Active** | All active license agreements between you and rights holders. | | **Archived** | All archived offers or license agreements between you and rights holders. | ## Review a license offer > **Error:** Any license offer you receive from a rights holder automatically becomes active after 14 days. **You must dispute the offer to reject it.** For licenses with a perpetual duration, rights holders can reach out and make a license offer directly to your experience. This can happen if Roblox detects that your experience has a high likelihood of making prominent use of a registered IP. After you receive an offer, you can review the offer details and either: - Do nothing and wait for the offer to be automatically accepted after 14 days. - [Dispute the offer](#dispute-the-offer). ### Dispute the offer To dispute a license offer: 1. Go to **Intellectual Property** ⟩ **Licenses** ⟩ **Offers**. 2. Select the license offer you want to dispute. 3. Under **Actions you can take**, click **dispute this license offer**. 4. In the dispute modal, select one of the following reasons for the dispute: - **My creation does not use this IP** if you believe that the rights holder made the license offer by mistake. - **My use of this IP is "fair" or otherwise legally allowed** if you believe that the IP is [fair use](https://copyright.gov/fair-use/). - **I modified my creation to remove the IP** if you have fully removed the IP from your experience. 5. Click **Next**. 6. Review and accept the legal agreements. 7. Click **Dispute**. The rights holder can then accept or reject your offer dispute. If the rights holder accepts your dispute, you will not enter into an agreement with them. However, if the rights holder rejects your dispute, you will receive their license offer again. After you receive their offer a second time, you can either wait 14 days for the offer to automatically become active or dispute the offer one final time. If you submit a second and final dispute, you will not enter into an agreement with the rights holder. Instead, the offer will be archived, and the rights holder might choose to pursue a [DMCA claim](/docs/en-us/production/publishing/dmca-guidelines.md) separately. ## Cancel a license You can cancel a pending license request if the following conditions are met: - The rights holder hasn't responded to your license request yet. - Early IP usage has not been detected in your experience. - (For time-limited licenses only) The cancellation is made at least three days before the proposed start date. If you cancel but still intend to use the IP in your experience, you must submit a new license request. To cancel a license request: 1. Navigate to **Intellectual Property** ⟩ **Licenses**. 2. Select the license request you want to cancel. 3. In the top-right, select **Cancel** and complete the prompt. ## Transactions When you enter an agreement with a rights holder, you agree to the revenue share rate they have set for their IP license. For example, if the revenue share rate of the IP license is set to **10%** and one of the following items sells for **100 Robux** inside your experience: | **Game passes** | | | --- | --- | | **60%** (in this case, 60 Robux) from the sale revenue goes to the item owner. | From the 60 Robux:

| | **10%** (in this case, 10 Robux) from the sale revenue goes to affiliate fees. For example, if the pass is owned by User A but it's on sale inside User B's experience, each under a different license. | From the 10 Robux:

| | **30%** (in this case, 30 Robux) from the sale revenue goes to Roblox fees. | | | **Subscriptions and developer products** | | | **70%** (in this case, 70 Robux) from the sale revenue goes to the item owner. | From the 70 Robux:

| | **30%** (in this case, 30 Robux) from the sale revenue goes to Roblox fees. | | | **Avatar items, avatar bundles, and items for resale** | | | **40%** (in this case, 40 Robux) from the sale revenue goes to the experience owner in the form of commission. | From the 40 Robux:

| | **30%** (in this case, 30 Robux) from the sale revenue goes to the owner of the item. | | | **30%** (in this case, 30 Robux) from the sale revenue goes to Roblox fees. | | > **Info:** Items purchased outside of the experience on the Marketplace are not subject to an IP license's revenue share rate.
--- title: "FAQ and guidelines" url: /docs/en-us/ip-licensing/faq last_updated: 2026-06-29T19:34:01Z description: "Frequently asked questions and guidelines about IP licensing tools." --- # FAQ and guidelines ## Frequently asked questions
**What are copyrights and trademarks?**
Copyrights and trademarks are types of IP. - A copyright protects original works of authorship like stories, music, art, movies, TV shows, etc. - A trademark protects something that functions as a way to identify a company's products or services, like product names ("Stranger Things", "Polly Pocket"), logos, slogans, and even certain sounds.
**What information is shared with rights holders if I request for a license?**
Only publicly available information about the experience you're requesting with is visible to the rights holder before they accept your request. If you enter an agreement with a rights holder, they receive metrics on the licensed experience including the number of visits, average of active users over a 7-day period (7DAU), and an anonymized list of transactions if a revenue share rate is set in the license's terms.
**What do I do if I'm currently using a popular IP that isn't available in the Licenses catalog?**
If you want to use an IP in your experience, you must have explicit permission from the rights holder. We recommend removing any content that might use IP and isn't covered by a license. There might be some creators and experiences that have reached agreements with rights holders on their own; if you already have a licensing agreement with the rights holder, then that is still valid.
**Can I request a license with an experience that doesn't currently meet the eligibility requirements?**
Yes. When you request a license, the experience you pick is meant to showcase to rights holders your abilities as a creator. This experience can be partially or fully finished, or it can be a fork of an existing experience. After you request to use a license, the rights holder can review your license request to build your experience around their IP and then choose to grant you a license.
**How much creative freedom do I have with the IP?**
Each license comes with a set of content standards for how an IP can and can't be used, and each license's standards are different depending on the needs of the rights holder. As long as you operate within the bounds of the Roblox Terms of Use, and the license and its standards, you have creative control on how to use IP in your experience. A rights holder can request changes through Roblox if you're deviating from those standards, and can request to terminate a license if you consistently do not follow those standards.
**Do I have to use a template or approved assets in order to use an IP?**
No. As long as you're following the terms and content standards of the license, you can make or use your own assets.
**Can I create one level or a portion of my experience based on an IP and request a license?**
No. You can only request or be offered a license for a full experience, where the IP is central to and present throughout the experience.
**As a creator, can I also be verified and offer a license for an IP that I own?**
Yes. Any creator who has evidence that they own an IP can register as a rights holder and offer IP licenses.
**Are the terms of each license the same, or can they be negotiated?**
All rights holders start with a standard template and are able to customize it in a few ways, such as selecting what is allowed or not allowed for content standards, defining the scope of a license, choosing a revenue share rate, and setting eligibility requirements for creators.
**How do I communicate with rights holders I have an agreement with?**
Rights holders can request changes to a licensed experience if they believe it is deviating from the content standards or scope defined for the license. You'll receive a notification from Roblox to ensure that you can make any necessary changes to your experience.
**How will I be notified about updates or changes to my license request or license agreements?**
You'll be notified via email and on Creator Hub. You can also always check your active license agreements in your License Manager.
**How are disputes resolved?**
Rights holders can attempt to license an experience twice, and creators can dispute an offer for a license twice. If an agreement can't be reached and the rights holder still believes that the experience contains their IP, they can pursue other options, such as filing a DMCA claim.
**How does IP licensing interact with the DMCA process?**
IP licensing provides rights holders and creators with an alternative to DMCA takedowns. The current DMCA process will continue as required by law, but we want to make it easier for rights holders and creators to form partnerships and create more opportunities.
**What protections do I have as a creator from a rights holder who is trying to inaccurately claim IP in my experience?**
If you believe a rights holder has inaccurately offered a license for your experience, you have 14 days to dispute the offer. When you dispute an offer, you can select from a set of reasons that tell the rights holder why the license isn't needed.
**Can an active license agreement ever be terminated?**
A rights holder can choose to cancel an active license agreement for a small set of reasons, like if a creator consistently doesn't follow the license's content standards or if the rights holder somehow loses the rights to the IP. In this case, you'll receive at least 30 days advance notice before the termination of the license agreement. During that time, you should work to remove the IP from your experience. Following the notice period, the terms and any revenue share will immediately be removed from the experience.
## Communication guidelines Whether you're a rights holder communicating with a creator or a creator communicating with a rights holder, you must follow these communication guidelines to prevent your message from being moderated: - Do not attempt to negotiate or discuss licensing terms - Do not request financial or personal information - Do not use foul or harmful language - Do not use legal or threatening language that could be considered harassing for a 13+ audience - Do not make requests to connect with the creator or rights holder outside of Roblox
--- title: "Get started as a rights holder" url: /docs/en-us/ip-licensing/get-started last_updated: 2026-06-29T19:34:01Z description: "Get started with IP tools as a rights holder." --- # Get started as a rights holder **License Manager** and **Rights Manager** are tools that let you manage your intellectual property (IP) on Roblox. With [License Manager](/docs/en-us/ip-licensing/license-manager.md), you can collaborate with creators to bring your IP to new audiences. By creating a license, you set the terms and conditions that determine how a creator can use your IP to build an experience. You can specify content standards, revenue share requirements, and set eligibility criteria for the experience, like maturity level and minimum number of daily active users. Work with creators by finding existing experiences that already use your IP and offering those experiences a license, or by publishing your license to the Licenses catalog, where creators can directly request to use your IP. [Rights Manager](/docs/en-us/production/publishing/rights-manager.md) helps you protect your IP by allowing you to report unauthorized use of your IP on Roblox and request the removal of experiences, avatar items, and assets hosted on the platform. To start using these tools, [register as a rights holder](#register-as-a-rights-holder) and [create an IP family](#create-an-ip-family). > **Info:** For more information about IP and the Digital Millennium Copyright Act (DMCA), see [intellectual property](/docs/en-us/marketplace/intellectual-property.md) and [DMCA guidelines](/docs/en-us/production/publishing/dmca-guidelines.md). > **Info:** For more information about IP terms, see the [glossary](/docs/en-us/ip-licensing/glossary.md). ## Eligibility To use IP tools, make sure you meet the eligibility requirements for each tool. | **Requirement** | **Rights Manager** | **License Manager** | | --- | --- | --- | | Valid ID | ✓ | ✓ | | Verified email address | ✓ | ✓ | | Exclusive copyright or an equally exclusive license to the IP | | ✓ | | Worldwide rights to the IP | | ✓ | | A commercialized IP portfolio of 5 or more works (not all need to participate on Roblox) **and/or** an existing license with an established Roblox creator | | ✓ | ##### Ineligible IP types - Shared or non-exclusive licenses - Per-territory licenses - Public-domain claimants - Platform-exclusive licensees (for example, "Roblox-only" or "PC-only") - AI-only works without recognized human authorship ## Register as a rights holder > **Error:** If you plan on licensing your IP with the License Manager, you must register as a rights holder using a Roblox account that does **not** have published experiences that earn money. > > Earnings from licenses are taxed as royalties, while earnings from experiences are taxed as services. If you use the same Roblox account to earn money from both licenses and experiences, all of your earnings will be taxed as royalties. Before managing your intellectual property on Roblox, you must first register either as a rights holder or as their authorized representative. This allows Roblox to confirm your legal identity with the creations that you own or represent. You only need to register once. To register your account as a rights holder or as their authorized representative: 1. In the Creator Hub, go to **Intellectual Property** ⟩ **Registration**. 2. Click **Start Registration**. 3. Verify your ID and email address. 4. Click **Next**. 5. Fill out the registration form. 1. Under **Rights holder**: 1. In the dropdown, select: - **I am the rights holder** if you're registering on behalf of yourself. - **I am reporting on behalf of my organization or client** if you're an authorized representative registering on behalf of someone else. 2. Enter your full name. 3. If you're an authorized representative, enter your organization or client name. 2. Under **Address**, enter your address. 3. If you're an authorized representative, under **Supporting documentation**, upload documents that prove you're an authorized representative of the rights holder. See [Supporting documentation for account registration](#supporting-documentation-for-account-registration) for details. 4. Under **Legal agreements**, confirm that your information is accurate, and agree to the [Terms of Use](https://www.roblox.com/info/terms) and [Privacy Policy](https://www.roblox.com/info/privacy). 5. Under **Signature**, enter your full legal name as your electronic signature. 6. Click **Submit for review**. Roblox will now review your information and register your rights holder account. ## Create an IP family An **IP family** is a folder inside your IP library that contains keywords and images that represent a particular IP. The keywords and images you add to your IP family are used by Roblox's scanning and search features to detect experiences across the platform that might match your IP. To create an IP family, you must first verify ownership of your IP. After your ownership is verified, you can add official IP to that family. ### Verify IP ownership To verify ownership of your IP: 1. Go to **Intellectual Property** ⟩ **IP Library**. 2. Click **Create IP family**. 3. Enter the name of your IP. 4. Under **Are you interested in licensing your IP?**, select: - **Yes** if you plan to both use IP enforcement features **and** enter license agreements with creators. This option gives you access to both License Manager and Rights Manager tools. 1. Under **Type of license holder**, select one or both: - **I have a substantial IP portfolio** if you have a commercialized IP asset portfolio of 5 or more works. - **I have an existing license with established Roblox creator(s)** if you already have an agreement with a creator on the Roblox platform. 2. Under **Scope of rights**, confirm that you hold **Exclusive and worldwide rights** to license this IP. You must not share these rights with any other party. - **No** if you only plan to use IP enforcement features. This option gives you access to Rights Manager tools only. 1. Under **Scope of Rights**, confirm that you have either a **Registered copyright** for the IP, a **Registered trademark** for the IP, or both. 5. Under **IP ownership**: 1. Upload documents to show proof of ownership of this IP. See [Supporting documentation for IP ownership](#supporting-documentation-for-ip-ownership) for details. 2. (Optional) Enter links to official websites associated with the IP. 3. (Optional) Enter any additional details you want to share about your IP family. 6. Under **Legal agreements**, agree to the [Terms of Use](https://en.help.roblox.com/hc/en-us/articles/42542704086548-License-Manager-Terms). This step is only required the first time you set up an IP family. 7. Click **Submit for review**. After you submit your verification of ownership, Roblox will review your submission and either approve or reject it. Your submission might be rejected if you don't meet the eligibility criteria or if Roblox is unable to verify your IP ownership. ### Add IP to your IP family > **Warning:** Keywords and images must be verifiable using citations to the supporting documentation you provide. > **Info:** To receive experience matches, you must have at least 10 approved images in your IP family. If you have fewer than 10 approved images, you can still receive license requests from creators, but Roblox will not generate experience matches for your IP. To add IP to your IP family: 1. Go to **Intellectual Property** ⟩ **IP Library**. 2. Select the IP family you want to add to. 3. Click **Add IP**. 4. Under **Supporting documentation**: 1. Upload documentation that proves you are the rightful owner of any keywords or media you want to add to your IP family. See [Supporting documentation for IP ownership](#supporting-documentation-for-ip-ownership) for details. 2. (Optional) Enter links to official websites associated with the IP. 5. Under **Primary keyword**: 1. Enter the word or phrase that is the official registered mark of your IP. This is usually the primary name of your copyrighted work and is used to find potential experience matches across Roblox. You can only have one primary keyword per language. 2. (Optional) Click **Add translation** to include translations of your IP name by country. This is important if your IP is registered under different names in different countries. 3. Enter a citation for your primary keyword. Include the name of the document and the specific location where the keyword appears in your supporting documentation. See [Citation guidelines](#citation-guidelines) for details. 6. (Optional) Under **Secondary keywords**: 1. Enter additional words or phrases that represent characters, branded elements, or fictitious places associated with your IP. These keywords are combined with your primary keyword to find potential matches. 2. (Optional) Click **Add translation** to include translations of your secondary keywords by country. 3. Enter a citation for each secondary keyword. Include the name of each document and the specific location where the keyword appears in your supporting documentation. See [Citation guidelines](#citation-guidelines) for details. 7. (Optional) Under **Media**: 1. Upload images that are distinct to your IP. See [Image requirements](#image-requirements) for details. 2. Enter a citation for each image. Include the name of each document and the specific location where the image appears in your supporting documentation. See [Citation guidelines](#citation-guidelines) for details. 8. Click **Submit**. After you submit an IP family for approval, Roblox will review your submission and either approve or reject it. If your submission is rejected, you can go to **IP Library** and click **View rejection reason** under **Status** for more details. Usually, Roblox will ask for additional legal documentation, such as copyright or trademark registrations, to validate the ownership of elements in your IP library. You can add more keywords or media to your IP family at any time by going to **IP Library** ⟩ **[IP family name]** and clicking **Add IP media**. ## Additional information ### Supporting documentation for account registration If you're registering as the authorized representative of a rights holder, you must upload supporting documentation in the form of a **letter of authorization**. This letter must: - Be on official company letterhead. - Identify you as the individual authorized representative. - Identify the company you work for. - State whether you are authorized to act on the rights holder's entire IP portfolio or only on specific IP titles. - State whether you can enforce the IP rights (for example, issue removal requests), enter into license agreements pertaining to the IP rights, or both. - Be signed and dated. Digital signatures are allowed. You can upload a maximum of 3 files. ### Supporting documentation for IP ownership Supporting documentation Roblox accepts as proof of IP ownership includes: - A copyright registration certificate for the primary IP name. - A trademark registration number or certificate for the primary IP name. For example, an international registration number for WIPO search. - Rights-assignment agreements transfer documents that assign you full ownership of the IP rights. - An exclusive license, sublicense, or product/distributor agreement granting sole rights (reproduction, distribution, adaptation, performance) to the entire work worldwide. - Notarized statements of authorship or creation with date confirmation. - A sworn affidavit or notarized declaration attesting to your exclusive ownership and detailing any territorial or media-type limits. You can upload a maximum of 3 files. ### Citation guidelines Citations verify that your keywords and images match the supporting documentation you have provided. For example, "Work X can be found in Supporting Doc Y, Page 1, Section 2." Citations should follow these guidelines: - If citing a PDF, include: - The name of the PDF document you uploaded as a supporting document - The page number where the character or place first appears - If citing a video, include: - The URL to the official video (like a movie, TV episode, or game trailer) - The timestamp showing the first appearance of the character or place - If citing a press kit or a website, include: - The URL to the official press kit or marketing page - The specific section or text where the character or place appears ### Image requirements > **Warning:** All images must abide by Roblox's [content moderation standards](https://en.help.roblox.com/hc/en-us/articles/21416271342868-Content-Moderation-on-Roblox). The images that represent your IP should meet these requirements: - File specifications: - File size: under 5 MB - File type: JPEG or PNG - Aspect ratio: 1:1 (preferred), and up to 16:9 or 9:16 - Minimum resolution: 224 x 224 pixels - Content guidelines: - Images must be limited to the subjects described by the secondary keywords in your IP family. - Each IP family can include up to 50 approved or pending images. Archived images don't count toward the image limit. - Avoid uploading images that are not distinct to your IP, like simple geometric shapes or common objects and locations. - For best results, use color images with characters or logos shown individually on a plain background. - Don't include composite images that contain smaller sub-images. - Avoid overlays like text or speech bubbles, unless they're highly distinct to your IP. --- title: "Glossary" url: /docs/en-us/ip-licensing/glossary last_updated: 2026-06-29T19:34:02Z description: "Glossary of IP licensing terms." --- # Glossary | **Term** | **Description** | | --- | --- | | **Intellectual property (IP)** | An IP represents a franchise or body of creative works that can be made available for licensing. | | **Rights holder** | The entity that is the licensor for the creative work, brand, or franchise being registered. | | **IP library** | A list of all of your IP families; a library of text and images that Roblox uses to identify the IP. | | **IP family** | The entity of the IP being registered. For example, "Polly Pocket" or "Blue Lock".

We recommend you only create one IP family per IP. | | **License catalog** | The full catalog of IPs on the Creator Hub that creators can request to use in their experiences. | | **IP listing** | A listing that explains the IP and all available licenses to creators who want to request to use that IP. | | **IP license** | A set of terms and conditions that defines how a particular IP can be used by creators to build experiences on Roblox. Rights holders can create several licenses per IP family (for example, "Polly Pocket Full License", "20% Revenue Share License", or "Minimal Maturity License"). | | **Primary keyword** | The official name of your IP. For example, "Polly Pocket". | | **Secondary keywords** | A list of words or phrases that represent characters, objects, places, or elements related to your IP. For example, "Polly", "Shani", and "Pollyville". | | **Private license** | A private license is only available to creators that the rights holder reaches out to directly. | | **Public license** | A public license is visible to all creators in the Licenses page. | | **Revenue share rate** | The revenue share that rights holders receive from experiences using their IP. This is a percentage of the gross Robux the creator of the experience earns.

Revenue share only applies to:

| | **Revenue share timing** | One of:

|
--- title: "Use Iconic IP on Roblox" url: /docs/en-us/ip-licensing last_updated: 2026-06-29T19:34:02Z description: "Learn how to scale on Roblox." --- # Use Iconic IP on Roblox ## One of the most significant ways for creators to expand their reach is to tap into the established fanbases of beloved global IP. We've seen major success when creators and rights holders partner, and we are building an ecosystem that supports licensing for rights holders at scale across Roblox. Our vision is to empower all creators to easily partner with the franchises they love and bring the franchises' licensed IP into their experiences. ### Request to partner with a rights holder today Requesting for licensable IP is simple – find your desired IP in the Licenses catalog, click Request for License, select an experience for your license request, review terms and content standards, include your pitch, and submit. You'll be notified on Creator Hub once the rights holder reviews your license request. [Request to partner](/docs/en-us/ip-licensing/creators.md) ### Register your IP For eligible rights holders and brands, the License Manager provides powerful self-serve tools to efficiently license your IP to Roblox's massive and engaged audience. You will need to provide the necessary proof of ownership, and a letter of authorization to offer licensed use of your IP. [Register IP](/docs/en-us/ip-licensing/get-started.md) # Meet our founding partners ## For creators: unleash your creations Creating experiences using popular IP can make your content more recognizable and exciting for users! It's a fantastic opportunity to reach new audiences who already know and love certain characters, settings, and storylines. [Explore catalog](https://create.roblox.com/explore/licenses) ## For rights holders: new IP management tools License Manager offers rights holders complete IP management, including registration, adding licenses to the central Licenses catalog, proactive IP usage scanning, and oversight of licensed IP. [License Manager](/docs/en-us/ip-licensing/license-manager.md) ![For creators: unleash your creations](/assets/ip-licensing/BlueLock.png) # Latest announcements ### New Lionsgate IPs added to the Licenses catalog Lionsgate has expanded its offerings with three newly licensable properties as previously announced at RDC this year: The Strangers: Chapters 1 & 2, The Blair Witch franchise, and Fall (2022). [Read](https://devforum.roblox.com/t/new-lionsgate-ips-arrive-in-the-licenses-catalog-%E2%80%93-plus-the-new-pitch-feature/4008864) ### Kodansha IPs now available During RDC 2025, Roblox CEO Dave Baszucki and Kodansha CEO Yoshinobu Noma announced major additions to our Licenses catalog including Kodansha's Blue Lock and That Time I Got Reincarnated as a Slime. [Read](https://corp.roblox.com/newsroom/2025/09/roblox-rdc-2025) ### Roblox Launches New Licensing Platform With our new licensing platform, including the Roblox License Manager and Licenses catalog, rights holders of creative works can partner with creators to reach a large and engaged audience. With this launch, creators can build experiences with popular IP. [Read](https://corp.roblox.com/newsroom/2025/07/roblox-launches-new-licensing-platform-for-experiences) ### The Evolution of IP on Roblox Learn how intellectual property works on Roblox, our vision for easier IP-based creations, and the innovative technologies that make it all possible. [Read](https://corp.roblox.com/newsroom/2025/09/roblox-rdc-2025) --- title: "License Manager" url: /docs/en-us/ip-licensing/license-manager last_updated: 2026-06-29T19:34:02Z description: "How to use IP licensing as a rights holder." --- # License Manager **License Manager** is an intellectual property (IP) management tool that lets you grant permission to creators to use your established IP in their experiences. With the License Manager, you can define licenses, specify content standards and revenue share requirements, and set eligibility criteria that experiences must meet in order to use your IP. Before using the License Manager, you must [register as a rights holder](/docs/en-us/ip-licensing/get-started.md#register-as-a-rights-holder) and [create an IP family](/docs/en-us/ip-licensing/get-started.md#create-an-ip-family). > **Warning:** To use the License Manager, you must have exclusive, worldwide rights to an IP that includes either copyright or trademark registrations. You will be required to provide verifiable documentation to prove your intellectual property rights. Any false claims of ownership can result in the termination of your License Manager access or your Roblox account. ## Create a license listing After you [create and set up your IP family](/docs/en-us/ip-licensing/get-started.md#create-an-ip-family), you can create a **license listing** to help promote your IP to creators and to encourage them to request to use it in their experiences. License listings are made up of: - A listing that represents your IP family to creators and allows them to request to use your IP in their experiences through the [Licenses catalog](/docs/en-us/ip-licensing/creators.md#explore-licenses). A listing can have one or more licenses under it. - One or more licenses that represent the set of terms and conditions that define how creators can use your IP to build those experiences. To create your license listing: 1. Go to **Intellectual Property** ⟩ **License Manager** ⟩ **Licenses**. 2. Select the **My Licenses** tab. 3. Click **Create license listing**. 4. Under **IP family**, select an IP family that you have previously created. 5. Under **Listing details**, enter a title and a description for the listing. 6. Under **Thumbnails**, enter up to 5 images that relate to the listing. The first image you upload is shown to creators on the listing tile of the Licenses page. Make sure these images have an aspect ratio of 16:9, and are in PNG or JPG formats. 7. Click **Create**. 8. Listings must undergo moderation review for their name, description, and thumbnails. Click **Submit for review**. Once your listing has been created, you will be redirected to the listing details page. To add a license to your listing: 1. Click **Add license** in the top-right corner. 2. Under **License details**, enter a title and a description. If you make your license public, the title and description will become public-facing and be visible to creators. 3. Under **Duration**: 1. Set a **Duration type**, either **Time-limited** or **Perpetual**: - **Time-limited** licenses grant the use of your IP within defined date boundaries and will auto-terminate an active agreement on the end date. - **Perpetual** licenses grant the use of your IP from the date an agreement is active until termination of that agreement is actively sought. > **Warning:** Rights holders can only [offer licenses to matching experiences](#review-matches-and-send-a-license-offer) if the license's duration is set to **perpetual**. If the duration is set to **time-limited**, [creators must request for your license](/docs/en-us/ip-licensing/creators.md#request-to-use-a-license) directly. Creators can request licenses for either time-limited or perpetual licenses. > **Info:** Once a license has been created, the duration type cannot be changed. A perpetual license cannot be updated to a time-limited license, and vice versa. 1. Set a **Duration range** (Time-limited only), a defined date boundary you expect your IP usage to be active for. The creator will specify a start and end date within this boundary. Start and end times for time-limited licenses are based on midnight UTC. 4. Under **Monetization**: 1. Set a **revenue share rate** between 0% and 95%. This is the revenue percentage you want to receive from experiences using your IP, and is shown to creators when they request to use your IP through a license listing. 2. Select one of the following for the **default revenue share timing**: - **Monetize on activation**, which means the revenue share is applied beginning at the moment the agreement between the rights holder and the creator of the experience becomes active. - **Monetize later**, which allows you to turn on revenue share at a later date of your choosing. 3. Set a **Default revenue share timing**. This option only displays if **Revenue share rate** is not 0%. > **Info:** Time-limited licenses are set to monetize on activation by default, and this preference cannot be changed. Time-limited licenses will begin monetizing on the day the agreement is active, which will be the start date designated by the creator. 5. Under **Experience eligibility**, select the criteria you want experiences to meet in order to request to use the license. Creators whose experiences don't meet these requirements aren't eligible to request to use your IP. To allow all creators to request, leave the default values. - For **Minimum average last 7 daily active users (DAU)**, choose between no requirement, greater than 1,000 DAU, or greater than 25,000 DAU. - For **Maximum maturity rating**, choose between minimal, mild, moderate, and restricted. See [Maturity labels](/docs/en-us/production/promotion/content-maturity.md#questionnaire-categories) for more information about different maturity ratings. 6. Under **Guidelines and restrictions**: 1. Enter the scope of your IP license. Include general background about the IP and clarify if any parts of the IP can or cannot be referenced by creators in their experience. 2. Under **Content standards**, set the rules that creators must follow when using your IP. 3. (Optional) Under **Brand guidelines**, upload a file to provide creators with creative direction. 7. Under **Privacy**, select one of the following: - **Public** to make the license listing public and visible to all creators. This option also allows all eligible creators to request to use the license. - **Private** to make the license only available to creators that you reach out to directly and that have experiences that match your IP. > **Info:** Because time-limited licenses cannot be used in license offers, they should be marked as **Public** once ready so that creators can request the license with proposed dates. 8. Click **Create**. 9. Licenses must undergo moderation review for their name, description, scope of license, and brand guidelines document (if provided). Click **Submit for review**. After you have created a license listing, you can go to **My licenses** ⟩ **[IP license listing]** to make changes to both the listing and its associated licenses: - To edit details of the listing, like its IP family and thumbnails, click **Edit listing**. - To edit details of a specific license, click the edit icon next to that license. You can edit any field except for the revenue share rate. - To add new licenses to the listing, click **Add license**. - To copy an existing license, click the copy icon next to that license. The copied license will undergo moderation review. > **Info:** Both listings and licenses undergo moderation review when they are first created. Subsequent edits only go through moderation review if sensitive fields are updated, specifically name, description, images, or documents. Rejected licenses can no longer be edited; however, they can be copied and the copy will undergo moderation review again. > **Warning:** Once your license has been used in any agreement, you **cannot** change the revenue share rate or experience eligibility requirements. > **Info:** Items purchased outside of the experience on the Marketplace are not subject to the revenue share rate. ## Review matches and send a license offer > **Warning:** To receive matches: - **You must have a minimum of 10 approved images** when you create an IP family. If you have fewer than 10 approved images, Roblox won't generate experience matches for your IP. - **Your license duration must be set to Perpetual.** Licenses set to time-limited durations require a [creator license request](#review-license-requests-from-creators). > **Info:** Matches are refreshed daily. If you have just created a new IP family, it might take some time before matches appear for that IP. Matches are experiences that use a significant amount of content related to your IP library. Roblox uses the primary and secondary keywords and the media added to your IP family to find experiences that include content that matches your IP. Matches are limited to public experiences that are actively played and only apply to licenses with a perpetual duration. You can review the experiences matched to your IP and send them a license offer in order to enter an agreement with an experience's creator, earn a share of their revenue, and set guidelines for the creator to follow. To review your matches and send a creator a license offer: 1. Go to **Intellectual Property** ⟩ **License Manager** ⟩ **Matches**. 2. Select the experience you want to send an offer to. 3. Click **Offer license**. 4. In the **New license offer** panel: 1. Select an IP family and a license. 2. Select the revenue share timing. You can choose to **Monetize on activation** and apply the revenue share rate of the license you selected the moment the creator accepts your offer, or **Monetize later** and activate the revenue share rate later. 3. Click **Send offer**. If you believe an experience is using a significant amount of content related to your IP but you don't see that experience on your Matches page, you can send that experience a [match request](#request-a-match). > **Warning:** You can't send an offer to an experience that is already associated with a pending license offer, accepted license agreement, or active license agreement. > **Warning:** After you send a license offer, the creator gets access to the license agreement details. The activation date for the license agreement is **14 days from when the creator has received the offer**. > > If the creator doesn't dispute the offer by the activation date, **the agreement automatically becomes active**. ## Request a match If an experience using your IP doesn't appear on your **Matches** page, you can manually request to add it. You can submit up to 3 match requests per day. To request a match: 1. Go to **Intellectual Property** ⟩ **License Manager** ⟩ **Matches**. 2. Click **Request match**. 3. Under **Request an experience as a match**: 1. Enter the URL for the experience using your IP. 2. Select an IP family and a license. 3. Select the revenue share timing. You can choose to **Monetize on activation** and apply the revenue share rate of the license you selected the moment the creator accepts your offer, or **Monetize later** and activate the revenue share rate later. 4. Click **Confirm**. > **Warning:** Roblox reviews the submitted experience to verify that it matches your IP. **If approved, a license offer is automatically sent to the creator of the experience.** ## Review license requests from creators Creators can browse the **Licenses** catalog and directly request to use your IP in their experiences without first being matched to your IP. After receiving a license request, you can choose to accept or reject it. Unlike [license matches](#review-matches-and-send-a-license-offer), creators can request licenses with either perpetual or time-limited durations. > **Warning:** If early IP usage is detected in a creator's experience, the license request or accepted license agreement may be activated early. To review a license request from a creator: 1. Go to **Intellectual Property** ⟩ **License Manager** ⟩ **Licenses**. 2. Select the **License agreements** tab. 3. Filter by **Requests**. 4. Select the license request you want to review. 5. Accept or reject the license request. - To accept the license request and enter a license agreement with the creator, click **Accept**. - To reject the license request, click **Reject**. You can also provide them with a reason for the rejection. This feedback can help the creator make any necessary changes to their experience and successfully request again in the future. > **Info:** We recommend that you play the creator's experience before accepting or rejecting their license request. ## View license agreements To view all of your offers and license agreements: 1. Go to **Intellectual Property** ⟩ **License Manager** ⟩ **Licenses**. 2. Select the **License agreements** tab. You can filter all license agreements by: | **Filter** | **Description** | | --- | --- | | **Requests** | All license requests from creators to use one of your licenses. | | **Offers** | All license offers that you have sent to creators. | | **Active** | All active license agreements between you and creators. | | **Archived** | All archived offers or license agreements between you and creators. | ## Review an offer dispute from a creator After you send a license offer to a creator, they can choose to dispute that offer. If a creator disputes your offer, you can either accept the dispute and archive the offer you sent, or reject the dispute and send the offer to the creator again. To review an offer dispute from a creator: 1. Go to **Intellectual Property** ⟩ **License Manager** ⟩ **Licenses**. 2. Select the license agreement that the creator has disputed. The reason for the creator's dispute displays at the top of the page. 3. Accept or reject the dispute. - To accept the dispute and archive the offer, click **Accept dispute**. - To reject the dispute and send the license offer to the creator again, click **Reject dispute**. If the creator disputes the offer a second time, the offer is automatically archived and the agreement does not take place. ## Make changes to active agreements If you have an active agreement with a creator and you believe their implementation of your IP has deviated from the defined scope of the license and its content standards, you can request that they make changes to their experience by clicking the **Change request** button and supplying details about which content standard this experience is violating and the specific change you want to request. ## Analytics Use analytics to analyze the impact of your individual licenses. To view a license's metrics: 1. Go to **Intellectual Property** ⟩ **License Manager** ⟩ **Licenses**. 2. Select the **My licenses** tab. 3. Select a license. 4. Select the **Analytics** tab to see the following: | **Metric** | **Description** | | --- | --- | | **DAU** | The number of daily active users (DAU) over 7 days. | | **Visits** | The number of lifetime visits to the experience. | | **Transactions** | The Robux you have earned from the experience.

Click **View transactions** and select **License payments** to see the revenue earned from agreements with creators. |
--- title: "Booleans" url: /docs/en-us/luau/booleans last_updated: 2026-06-29T19:34:02Z description: "Data type having a value of either true or false." --- # Booleans The **boolean** data type, or `bool`, has a value of either `true` or `false`. ## Conditionals If a value isn't `false` or `nil`, Luau evaluates it as `true` in [conditional statements](/docs/en-us/luau/control-structures.md#if-statements). Unlike many other languages, Luau considers both zero and the empty string as `true`. ## Operators You can formulate complex conditions with [relational](/docs/en-us/luau/operators.md#relational) and [logical](/docs/en-us/luau/operators.md#logical) operators. --- title: "Luau comments" url: /docs/en-us/luau/comments last_updated: 2026-06-29T19:34:02Z description: "Comments are text that the Luau parser ignores at runtime." --- # Luau comments A **comment** is text that the Luau parser ignores at runtime. > **Info:** Roblox Studio has a similarly-named [comments](/docs/en-us/projects/collaboration.md#comments) feature that is unrelated to code comments in Luau. ## Single-line comments You can define single-line comments with a double hyphen (`--`) anywhere outside a string. Single-line comments extend to the end of the line. Use single line comments for in-line notes. If the comment spans multiple lines, use multiple single-line comments. You can add and remove single-line comments in the Script Editor with the keyboard shortcut `Ctrl``/` (`⌘``/`). ```lua -- This condition is really important because the world would blow up if it -- were missing. if not foo then stopWorldFromBlowingUp() end ``` ## Block comments You can define multiline block comments with double hyphens and double brackets (`--[[]]`). Use block comments for documenting items: - Use a block comment at the top of files to describe their purpose. - Use a block comment before functions or objects to describe their intent. ```lua --[[ Shuts off the cosmic moon ray immediately. Should only be called within 15 minutes of midnight Mountain Standard Time, or the cosmic moon ray may be damaged. ]] local function stopCosmicMoonRay() end ``` If necessary, you can nest multiple brackets inside a block comment using the same number of equal signs in both the beginning and ending bracket: ```lua --[=[ In-depth detail about cosmic moon ray: [[[TOP SECRET]]] ]=] ``` ## Comment directives Luau uses comments that start with `!` to control features like [type checking](/docs/en-us/luau/type-checking.md), [native code generation](/docs/en-us/luau/native-code-gen.md), and [linting](https://luau.org/lint). ```lua --!strict --!nonstrict --!nocheck --!native --!nolint --!optimize 0|1|2 ``` For linting, Roblox Studio enables the following subset of warning codes from the [Luau linter](https://luau.org/lint): 1, 2, 3, 6, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28. The `--!optimize` directive controls the optimization level of the Luau compiler for the script: - 0 disables optimizations. - 1 enables basic optimizations (default in Studio testing). - 2 enables further optimizations (default in live experiences). Exact optimizations aren't published and are subject to change. We recommend using the default values unless you have a specific reason not to. ## To-do comments Roblox Studio supports special `TODO` comments. Studio bolds any text following `TODO` (until broken by a space): ```lua -- TODO: Finish the function below so that it actually does what its name implies. local function stopWorldFromBlowingUp() end ``` Use `TODO` comments to keep track of and communicate issues within your code. --- title: "Control structures" url: /docs/en-us/luau/control-structures last_updated: 2026-06-29T19:34:02Z description: "Control structures are statements that manage the flow of Luau code execution." --- # Control structures **Control structures** are statements that manage the flow of Luau code execution. There are four main types of control structures: - An [if then else](#if-statements) statement executes code only if a specified condition is `true`. The code execution doesn't repeat. - A [while loop](#while-loops) executes code only if a specified condition is `true`, and repeats execution while the condition remains `true`. - A [repeat loop](#repeat-loops) executes code, and repeats execution if the condition is `true`. - A [for loop](#for-loops) executes code a set number of times depending on specified inputs. The condition for `if` statements, `while` loops, and `repeat` loops can be any Luau expression or value. If a value isn't `false` or `nil`, then Luau evaluates it as `true` in conditional statements. Unlike other scripting languages, Luau considers both zero and the empty string as `true`. ## If statements The basic `if` statement tests its condition. If the condition is true, then Luau executes the code between `then` and `end`. You can use an `elseif` statement to test for additional conditions if the `if` condition is false. You can use an `else` statement to execute code if all `if` and `elseif` conditions fail. The `elseif` and `else` parts are both optional, but you can't use either without an initial `if` statement. In a chain of `if`, `elseif`, and `else` conditions, Luau tests conditions from top to bottom, stops at the first `true` condition, and executes the code that follows it. ```lua if 2 + 2 == 5 then print("Two plus two is five") -- Doesn't print because the condition is false elseif 2 + 3 == 5 then print("Two plus three is five") -- Two plus three is five else print("All conditions failed") -- Doesn't print because the previous condition is true end ``` ## While loops A `while`—`do` loop evaluates if a specified condition is true or false. If the condition is `false` or `nil`, then the loop ends, and Luau skips the code in the loop. If the condition is `true`, then Luau executes the code in the loop and repeats the process. ```lua local timeRemaining = 10 while timeRemaining > 0 do print("Seconds remaining: " .. timeRemaining) task.wait(1) timeRemaining -= 1 end print("Timer reached zero!") --[[ Resulting output: Seconds remaining: 10 Seconds remaining: 9 Seconds remaining: 8 Seconds remaining: 7 Seconds remaining: 6 Seconds remaining: 5 Seconds remaining: 4 Seconds remaining: 3 Seconds remaining: 2 Seconds remaining: 1 Timer reached zero! ]] ``` ### Infinite loops You can use a `while`—`do` loop to write infinite game loops by setting `true` as the condition. ```lua while true do print("Looping...") task.wait(0.5) end --[[ Resulting output: Looping... Looping... Looping... Looping... ... ]] ``` > **Error:** Always include a delay such as `Library.task.wait()` in an infinite loop. Omitting it freezes the experience and crashes Studio. ## Repeat loops The `repeat`—`until` loop repeats until a condition is true. The conditional test evaluates **after** the code block runs, so the code block always runs at least once. Unlike other languages, the Luau scope of a local variable declared inside a `repeat`—`until` loop includes the condition. ```lua local currentGoblinCount = 18 -- Spawn goblins up to a maximum of 25 in the game repeat spawnGoblin() currentGoblinCount += 1 print("Current goblin count: " .. currentGoblinCount) until currentGoblinCount == 25 print("Goblins repopulated!") --[[ Resulting output: Current goblin count: 19 Current goblin count: 20 Current goblin count: 21 Current goblin count: 22 Current goblin count: 23 Current goblin count: 24 Current goblin count: 25 Goblins repopulated! ]] ``` ## For loops A for loop executes code a set number of times, either based on a [numerical counter](#numeric-for-loops) or the number of [items in a collection](#generic-for-loops). ### Numeric for loops A `for`—`do` loop determines the number of times to execute the loop using a counter. The loop is declared with a start value, end value, and optional increment. Luau sets the counter equal to the start value, executes the code block in the `for` loop, then adds the increment to the counter. If the increment is positive, then the process repeats until the counter is equal to or greater than the end value. If the increment is negative, then the process repeats until the counter is equal to or less than the end value. The optional increment defaults to `1`. It doesn't need to be a whole number. ```lua for counter = 1, 3 do print(counter) end --[[ Resulting output: 1 2 3 ]] for counter = 1, 6, 2 do print(counter) end --[[ Resulting output: 1 3 5 ]] for counter = 2, 0, -0.5 do print(counter) end --[[ Resulting output: 2 1.5 1 0.5 0 ]] ``` ### Generic for loops The generic `for` loop iterates over items in a collection rather than a sequence of numbers. With generic `for` loops, you can execute code for each item in the collection, and can easily use each item in the code. For loops need a function, or iterator, to iterate over different types of collections. The global `ipairs()` returns an iterator for arrays, and the global `pairs()` returns an iterator for dictionaries. The `Library.string` library provides `Library.string.gmatch()` to iterate over strings. ### Generalized iteration In Luau, you can iterate over a table using the `in` keyword directly on the table, instead of using an iterator function such as `ipairs()`: ```lua for i, v in {1, 2, 3, 4, 5} do print(i, v) end ``` Generalized iteration also lets you use the `__iter` metamethod to create a custom iterator function. This contrived example iterates over an array in reverse order, from its last element to its first: ```lua local myTable = {1, 2, 3, 4, 5} myMetatable = { __iter = function(t) local i = #t + 1 return function() i -= 1 if i > 0 then return i, t[i] end end end, } setmetatable(myTable, myMetatable) for i, v in myTable do print(i, v) end --[[ Resulting output: 5 5 4 4 3 3 2 2 1 1 ]] ``` #### Arrays The `ipairs()` function returns an iterator that iterates through numerical indices in a table and returns an `index` and `value` for each element. This makes it appropriate for arrays, where all indices are numeric. ```lua local array = {"a", "b", "c", "d", "e"} for index, value in ipairs(array) do print(index, value) end --[[ Resulting output: 1 a 2 b 3 c 4 d 5 e ]] ``` #### Dictionaries The `pairs()` function returns an iterator that iterates through all indices (including numerical indices) in a table and returns a `key` and `value` for each entry in the dictionary. The order of traversing elements in a dictionary table is arbitrary. This makes it appropriate for iterating over dictionaries, where items are stored out of order with non-numeric indices. ```lua local dictionary = { [1] = "a", ["Hello"] = "b", [5] = "c", [true] = "d", ["World"] = "f", [false] = "e" } for key, value in pairs(dictionary) do print(key, value) end --[[ Resulting output: Hello b true d false e World f 5 c 1 a ]] ``` ## Break loops To force a loop to end, use the `break` keyword. The following code sample shows how to break an infinite `while`—`do` loop. ```lua local secondsElapsed = 0 local timeout = 5 while true do task.wait(1) secondsElapsed += 1 print("Seconds elapsed:", secondsElapsed) if secondsElapsed == timeout then break end end print("Five seconds elapsed. Time to move on!") --[[ Resulting output: 1 2 3 4 5 Five seconds elapsed. Time to move on! ]] ``` ## Continue loops To force a loop to iterate and start again, use the `continue` keyword. A `for` loop will iterate the counter; `while` and `repeat`—`until` will check the loop condition before continuing. The following code sample gets all children of an `Class.Instance` of a specific `Class.Instance.ClassName|ClassName`. ```lua local function GetChildrenOfClass(parent: Instance, className: string): {Instance} local children = {} for _, child in parent:GetChildren() do if child.ClassName ~= className then continue end -- Iterates the loop table.insert(children, child) end return children end ``` --- title: "Enums" url: /docs/en-us/luau/enums last_updated: 2026-06-29T19:34:02Z description: "A fixed list of items (enumeration)." --- # Enums > **Info:** Enums are not a [built-in Luau type](https://luau.org/typecheck#builtin-types) and they exist only in Roblox, but they're conceptually similar to other Luau data types and are something you'll work with frequently in Roblox development. The **enumeration** data type, or `Datatype.Enum`, is a fixed list of items. You can access enums through the global object called `Datatype.Enum`. For a full list and their respective items, see [Enums](/docs/en-us/reference/engine/enums.md). ## Enum items To get all items of an enum, call the `Datatype.Enum:GetEnumItems()|GetEnumItems()` method on it. The following code sample demonstrates how to call `Datatype.Enum:GetEnumItems()|GetEnumItems()` on the `Enum.PartType` enum. ```lua local partTypes = Enum.PartType:GetEnumItems() for index, enumItem in partTypes do print(enumItem) end --[[ Enum.PartType.Ball Enum.PartType.Block Enum.PartType.Cylinder Enum.PartType.Wedge Enum.PartType.CornerWedge ]] ``` ## Data type The `Datatype.EnumItem` is the data type for items in enums. An `Datatype.EnumItem` has three properties: - `Name` — The name of the `Datatype.EnumItem`. - `Value` — The numerical index of the `Datatype.EnumItem`. - `EnumType` — The parent `Datatype.Enum` of the `Datatype.EnumItem`. Some properties of objects can only be items of certain enums. For example, the `Class.Part.Shape|Shape` property of a `Class.Part` object is an item of the `Enum.PartType` enum. The following code sample demonstrates how to print the properties of the `Enum.PartType.Cylinder` enum item. ```lua print(Enum.PartType.Cylinder.Name) --> "Cylinder" print(Enum.PartType.Cylinder.Value) --> 2 print(Enum.PartType.Cylinder.EnumType) --> PartType ``` To assign an `Datatype.EnumItem` as the value of a property, use the full `Datatype.Enum` declaration. You can also use the item's `Datatype.EnumItem.Name|Name` property as a string. ```lua local Workspace = game:GetService("Workspace") local part = Instance.new("Part") -- Create a new part part.Shape = Enum.PartType.Cylinder -- By full enum item declaration (best practice) part.Shape = "Cylinder" -- By enum item's name as a string part.Parent = Workspace ``` --- title: "Functions" url: /docs/en-us/luau/functions last_updated: 2026-06-29T19:34:02Z description: "Functions are blocks of code that can execute multiple times on command." --- # Functions **Functions** are [blocks of code](/docs/en-us/luau/scope.md) that you can execute multiple times on command. You can also connect them to [events](#event-handlers) or assign them as [callbacks](#callbacks). ## Basic functions A function definition includes: - The [scope](/docs/en-us/luau/scope.md) of the function (global or `local`). - The `function` keyword. - The name of the function in `camelCase`. - The parameters of the function in parentheses (`()`). - The block of code, or "body", of the function. - The `end` keyword. The body of the function executes when you call the function. To call a function, type its name followed by parentheses. You can define a variable to accept the return value or use the return value in place of a variable. ```lua -- This function has no parameters and returns nil local function addOneAndTwo() local result = 1 + 2 print(result) end -- Calling a function without a return addOneAndTwo() -- 3 ``` ### Parameters Parameters are variables that you make available to the function and are only used in the function's [scope](/docs/en-us/luau/scope.md). Functions have no parameters by default. If you call a function with more parameters than it expects, Luau ignores the extra parameters. If you call a function with fewer parameters than it expects, Luau passes `nil` for all missing parameters. ```lua -- This function has two parameters: num1 and num2 local function addNumbers(num1, num2) print(num1 + num2) end addNumbers(2, 3) -- 5 addNumbers(5, 6, 7) -- 11 addNumbers(9) -- attempt to perform arithmetic (add) on number and nil ``` ### Return In the body of the function, the `return` keyword returns a result from a computation. You can return multiple values from one function. `return` ends function execution, and Luau expects the `end` keyword to follow the `return` statements, so writing code between the `return` command and the `end` command throws an error. ```lua -- This function returns one return value local function addNumbers(num1, num2) local result = num1 + num2 return result end print(addNumbers(1, 2)) -- 3 local seven = addNumbers(3, 4) print(seven) -- 7 -- This function returns multiple values: sum and difference local function addAndSubtract(num1, num2) local sum = num1 + num2 local difference = num1 - num2 return sum, difference end -- Calling a function and expecting multiple return values local sum, difference = addAndSubtract(2, 3) print(sum) -- 5 print(difference) -- -1 ``` ## Methods Methods are functions that are members of an object, such as a [class](/docs/en-us/reference/engine/classes.md) or [table](/docs/en-us/luau/tables.md). They expect the object itself (`self`) as the first argument. When you call a method, use the colon notation (`:`) instead of dot notation (`.`) to pass `self` as the first argument automatically. All objects in Roblox descend from `Class.Instance` and have commonly used methods including `Class.Instance:Destroy()`, `Class.Instance:Clone()`, and `Class.Instance:FindFirstChild()`. ```lua local Workspace = game:GetService("Workspace") -- Destroying a part with dot notation (function) local firstPart = Instance.new("Part") firstPart.Parent = Workspace print(firstPart.Parent) -- Workspace firstPart.Destroy(firstPart) print(firstPart.Parent) -- nil -- Destroying a part with colon notation (method) local secondPart = Instance.new("Part") secondPart.Parent = Workspace print(secondPart.Parent) -- Workspace secondPart:Destroy() print(secondPart.Parent) -- nil ``` > **Info:** The statement `table:Method()` is functionally identical to `table.Method(table)`, and when defining a method, `function table:Method()` is identical to `function table.Method(self)`. This behavior extends to `Class.Instance` methods, since they are also derived from tables. ### Define methods To create a method in a table, use the name of the method as the key and the method function as the value. In the definition of the method, the `self` parameter refers to the method's parent table. When you call a method using colon notation, you pass the table itself as the first argument. You can define parameters for a method, but you need to list them after the `self` parameter. In the following example, the `testButton` table has a method as the value of the `changeEnabled` key. You can verify that `self` refers to the method's parent table by printing the value of `self.enabled`. ```lua local testButton = { enabled = true, changeEnabled = function(self, isEnabled) self.enabled = isEnabled print(self.enabled) end } print(testButton.enabled) -- true -- Call the method testButton:changeEnabled(false) -- false ``` ## Callbacks Callbacks are functions that execute in response to another function or process. ### Basic callbacks Functions can be passed into other functions, for example, an [anonymous](#anonymous-functions) function can be used to implement a callback that `Library.table.sort()` then uses to sort a list of `Class.Player|Players` from `Class.Players.GetPlayers()`. ```lua local Players = game:GetService("Players") local sortedPlayers = Players:GetPlayers() table.sort(sortedPlayers, function(a, b) -- Use an anonymous callback to sort players by name return a.Name < b.Name end) ``` In the Roblox API, callbacks refer to a write-only function member, callbacks yield until they return. Widely used callbacks include: - `Class.MarketplaceService.ProcessReceipt`, which handles developer products purchases. - `Class.BindableFunction.OnInvoke`, which calls the function when a script calls `BindableFunction:Invoke(...)`. - `Class.RemoteFunction.OnClientInvoke`, which calls the function when the server calls `RemoteFunction:FireClient(player, ...)` or `RemoteFunction:FireAllClients(...)`. - `Class.RemoteFunction.OnServerInvoke`, which calls the function when a client calls `RemoteFunction:InvokeServer(...)`. To set a callback, assign a function to it. For example, `Class.BindableFunction.OnInvoke` is a callback of `Class.BindableFunction`. You can set a named or [anonymous](#anonymous-functions) function to it, and you can call (**invoke**) that function by calling the `:Invoke()` method on the callback. The arguments you pass to `:Invoke()` forward to the callback, and the return value from the callback function returns to the caller of `:Invoke()`. ```lua local bindableFunction = Instance.new("BindableFunction") bindableFunction.OnInvoke = function(number) return 2 * number end print(bindableFunction:Invoke(42)) -- 84 ``` ## Function techniques ### Event handlers You can assign a function, known as an **event handler**, to execute when an event fires. For example, you can create a function called `onPlayerAdded()` to the `Class.Players.PlayerAdded` event to print the name of whatever player joins. For more information, see [Events](/docs/en-us/scripting/events.md). ```lua local Players = game:GetService("Players") local function onPlayerAdded(player) print(player.Name .. " joined the game!") end Players.PlayerAdded:Connect(onPlayerAdded) ``` ### Anonymous functions You can create functions without names, known as **anonymous functions**, to use as [callbacks](#callbacks) and [event handlers](#event-handlers). Like named functions, anonymous functions need to start and end with the `function` and `end` keywords, but you don't need the `local` keyword to indicate local scope because they always have local scope. In the following example, the callback for the `Library.task.delay()` function and the event handler for the `Class.Players.PlayerAdded` event are both anonymous functions. ```lua -- Anonymous function in a callback to task.delay() task.delay(2, function(exactTimeElapsed) print(exactTimeElapsed) -- 2.0064592329945 end) -- Anonymous function in an event handler local Players = game:GetService("Players") Players.PlayerAdded:Connect(function(player) print(player.Name .. " joined the game!") end) ``` ### Functions in module scripts You can reuse functions across multiple scripts by storing them in `Class.ModuleScript|ModuleScripts`. Functions are a Luau data type, so you can store them in tables with other data. ### Variadic functions A variadic function accepts any number of arguments. For example, `Globals.LuaGlobals.print()` is a variadic function. ```lua print(2, "+", 2, "=", 2 + 2) --2 + 2 = 4 print(string.format("The %s is a %s!", "cake", "lie")) -- The cake is a lie! print(string.char(115, 101, 99, 114, 101, 116)) -- secret ``` #### Define variadic functions To define a variadic function, you use the `...` token as the last or only parameter (not to be confused with `..`, the concatenation [operator](/docs/en-us/luau/operators.md)). You can put the `...` values in a table for ease of use. > **Warning:** The variadic token `...` can only be used within functions defined as variadic; functions or callbacks defined within a variadic function **cannot** use the same `...`, even if they are variadic themselves. > > `...` does not behave like a regular variable, and can only be passed into other functions, returned, or put into a table. ```lua local function variadic(named, ...) local arguments = {...} -- pack the extra arguments into a table print("Named argument =", named) for i, value in arguments do print("Input No.", i, "=", value) end end variadic(10, "Hi", 20, "Variadic Function") --[[ Resulting output: Named argument = 10 Input No. 1 = Hi Input No. 2 = 20 Input No. 3 = Variadic Function ]] ``` #### Forward arguments You can define variadic functions as wrappers around other functions to pass, or forward, arguments from the wrapper to the other functions. ```lua local function printAround(functionToPrintAround, ...) print("Before") functionToPrintAround(...) print("After") end local function addNumbers(x, y, z) print("x =", x) print("y + z =", y + z) end printAround(addNumbers, 1, 2, 3) --[[ Resulting output: Before x = 1 y + z = 5 After ]] ``` #### Call a variadic function with arrays If you want to pass a table array of values to a global variadic function, such as `print()`, you can use the global `unpack()` function to pass the values of the table instead of the table itself. ```lua local squares = {1, 4, 9, 16, 25} print("The first 5 square numbers are:", unpack(squares)) -- The first 5 square numbers are 1 4 9 16 25 ``` --- title: "Luau" url: /docs/en-us/luau last_updated: 2026-06-29T19:34:02Z description: "Luau is the scripting language creators use in Roblox Studio." --- # Luau [Luau](https://luau.org) is the scripting language creators use in Roblox Studio. It is a fast, small, safe, gradually typed embeddable scripting language derived from [Lua 5.1](https://www.lua.org/manual/5.1/). > **Success:** Contributing your Luau scripts for AI training can help enhance Luau-focused AI tools in Studio. For more information, see [Empower Luau creation](https://create.roblox.com/data-collection). ## Support in Studio The **Script Editor** in Studio supports Luau with autocompletion, syntax highlighting, static linting, type checking, and script analysis. It also shows documentation and function signatures for members of the [Roblox Engine API](/docs/en-us/reference/engine.md). ## Types Luau includes the following data types: - [Nil](/docs/en-us/nil.md) represents non-existence or nothingness. It's different from any other value or data type. - [Booleans](/docs/en-us/booleans.md), or `bool`, have a value of either `false` or `true`. - [Numbers](/docs/en-us/numbers.md), or `double`, represent double-precision (64-bit) floating-point numbers. - [Strings](/docs/en-us/strings.md) are sequences of characters, such as letters, numbers, and symbols. - [Tables](/docs/en-us/tables.md) are [arrays](/docs/en-us/tables.md#arrays) or [dictionaries](/docs/en-us/tables.md#dictionaries) of any value except `nil`. - [Enums](/docs/en-us/enums.md) are fixed lists of items. Luau is dynamically typed by default. Variables, function parameters, and return values can be any data type. This helps you write code faster because you don't need to provide types for each piece of data. You can still declare explicit types for variables in Luau and enable [strict type checking](/docs/en-us/type-checking.md) to make type issues obvious and easy to locate. ## Data structures You can also implement the following data structures using primitive data types: - [Stacks](/docs/en-us/stacks.md) are Last-In-First-Out collections of items that you can implement using tables. - [Queues](/docs/en-us/queues.md) are First-In-First-Out collections of items that you can implement using tables. - [Metatables](/docs/en-us/metatables.md) are tables with advanced configurations that can achieve functionalities such as storing pairs of keys and values and calculating arithmetic operations. ## Features In Luau, [variables](/docs/en-us/variables.md) and [functions](/docs/en-us/functions.md) can have global and local [scope](/docs/en-us/scope.md) within a script. Luau has logical, relational, and compound assignment [operators](/docs/en-us/operators.md). You can use [control structures](/docs/en-us/control-structures.md) and [functions](/docs/en-us/functions.md) to control when Luau executes code. Many operators and variable assignments perform [type coercion](/docs/en-us/type-coercion.md) to change values to the types that Luau expects. --- title: "Luau and C# comparison" url: /docs/en-us/luau/luau-csharp-comparison last_updated: 2026-06-29T19:34:02Z description: "Explains the similarities and differences between the C# and Luau programming languages." --- # Luau and C# comparison Roblox uses the Luau programming language. The following code samples and tables indicate some of the differences between syntaxes for C# and Luau. ## Line endings You don't need semicolons in Luau, but they don't break the syntax. ## Reserved keywords The following table has Luau's reserved keywords mapped to their C# equivalent. Note it doesn't show all C# keywords. | Luau | C# | | --- | --- | | `and` | | | `break` | `break` | | `do` | `do` | | `if` | `if` | | `else` | `else` | | `elseif` | `else if` | | `then` | | | `end` | | | `true` | `true` | | `false` | `false` | | `for` | `for` or `foreach` | | `function` | | | `in` | `in` | | `local` | | | `nil` | `null` | | `not` | | | `or` | | | `repeat` | | | `return` | `return` | | `until` | | | `while` | `while` | ## Comments ```lua -- Single line comment --[[ Resulting output: Block comment --]] ``` ```cs // Single line comment /* Block comment */ ``` ## Strings ```lua -- Multi-line string local multiLineString = [[This is a string that, when printed, appears on multiple lines]] -- Concatenation local s1 = "This is a string " local s2 = "made with two parts." local endString = s1 .. s2 ``` ```cs // Multi-line string string multiLineString1 = "This is a string that,\nwhen printed, appears\n on multiple lines."; string multiLineString2 = @"This is a string that, when printed, appears on multiple lines"; // Concatenation string s1 = "This is a string "; string s2 = "made with two parts."; string endString = s1 + s2; ``` ## Tables To learn more about tables in Luau, see [Tables](/docs/en-us/luau/tables.md). ### Dictionary tables You can use tables in Luau as dictionaries just like in C#. ```lua local dictionary = { val1 = "this", val2 = "is" } print(dictionary.val1) -- Outputs 'this' print(dictionary["val1"]) -- Outputs 'this' dictionary.val1 = nil -- Removes 'val1' from table dictionary["val3"] = "a dictionary" -- Overwrites 'val3' or sets new key-value pair ``` ```cs Dictionary dictionary = new Dictionary() { { "val1", "this" }, { "val2", "is" } }; Console.WriteLine(dictionary["val1"]); // Outputs 'this' dictionary.Remove("val1"); // Removes 'val1' from dictionary dictionary["val3"] = "a dictionary"; // Overwrites 'val3' or sets new key-value pair dictionary.Add("val3", "a dictionary"); // Creates a new key-value pair ``` ### Numerically-indexed tables You can use tables in Luau as arrays just like in C#. Indices start at `1` in Luau and `0` in C#. ```lua local npcAttributes = {"strong", "intelligent"} print(npcAttributes[1]) -- Outputs 'strong' print(#npcAttributes) -- Outputs the size of the list -- Append to the list table.insert(npcAttributes, "humble") -- Another way... npcAttributes[#npcAttributes+1] = "humble" -- Insert at the beginning of the list table.insert(npcAttributes, 1, "brave") -- Remove item at a given index table.remove(npcAttributes, 3) ``` ```cs List npcAttributes = new List{"strong", "intelligent"}; Console.WriteLine(npcAttributes[0]); // Outputs 'strong' Console.WriteLine(npcAttributes.Count); // Outputs the size of the list // Append to the list npcAttributes.Add("humble"); // Another way... npcAttributes.Insert(npcAttributes.Count, "humble"); // Insert at the beginning of the list npcAttributes.Insert(0, "brave"); // Remove item at a given index npcAttributes.Remove(2); ``` ## Operators ### Conditional operators | Operator | Luau | C# | | --- | --- | --- | | Equal To | `==` | `==` | | Greater Than | `>` | `>` | | Less Than | `<` | `<` | | Greater Than or Equal To | `>=` | `>=` | | Less Than or Equal To | `<=` | `<=` | | Not Equal To | `~=` | `!=` | | And | `and` | `&&` | | Or | `or` | `\|\|` | ### Arithmetic operators | | Luau | C# | | --- | --- | --- | | Addition | `+` | `+` | | Subtraction | `-` | `-` | | Multiplication | `*` | `*` | | Division | `/` | `/` | | Modulus | `%` | `%` | | Exponentiation | `^` | `**` | ## Variables In Luau, variables don't specify their type when you declare them. Luau variables don't have access modifiers, although you may prefix "private" variables with an underscore for readability. ```lua local stringVariable = "value" -- "Public" declaration local variableName -- "Private" declaration - parsed the same way local _variableName ``` ```cs string stringVariable = "value"; // Public declaration public string variableName // Private declaration string variableName; ``` ## Scope In Luau, you can write variables and logic in a tighter scope than their function or class by nesting the logic within `do` and `end` keywords, similar to curly brackets `{}` in C#. For more details, see [Scope](/docs/en-us/luau/scope.md). ```lua local outerVar = 'Outer scope text' do -- Modify 'outerVar' outerVar = 'Inner scope modified text' -- Introduce a local variable local innerVar = 'Inner scope text' print('1: ' .. outerVar) -- prints "1: Inner scope modified text" print('2: ' .. innerVar) -- prints "2: Inner scope text" end print('3: ' .. outerVar) -- prints "3: "Inner scope modified text" -- Attempting to print 'innerVar' here would fail ``` ```cs var outerVar = "Outer scope text"; { // Modify 'outerVar' outerVar = "Inner scope modified text"; // Introduce a local variable var innerVar = "Inner scope text"; Console.WriteLine("1: " + outerVar); // prints "1: Inner scope modified text" Console.WriteLine("2: " + innerVar); // prints "2: Inner scope text" } Console.WriteLine("3: " + outerVar); // prints "3: "Inner scope modified text" // Attempting to print 'innerVar' here would fail ``` ## Conditional statements ```lua -- One condition if boolExpression then doSomething() end -- Multiple conditions if not boolExpression then doSomething() elseif otherBoolExpression then doSomething() else doSomething() end ``` ```cs // One condition if (boolExpression) { doSomething(); } // Multiple conditions if (!boolExpression) { doSomething(); } else if (otherBoolExpression) { doSomething(); } else { doSomething(); } ``` ### Conditional operator ```lua local max = if x > y then x else y ``` ```cs int max = (x > y) ? x : y; ``` ## Loops To learn more about loops in Luau, see [Control Structures](/docs/en-us/luau/control-structures.md). ### While and repeat loops ```lua while boolExpression do doSomething() end repeat doSomething() until not boolExpression ``` ```cs while (boolExpression) { doSomething(); } do { doSomething(); } while (boolExpression) ``` ### For loops ```lua -- Forward loop for i = 1, 10 do doSomething() end -- Reverse loop for i = 10, 1, -1 do doSomething() end ``` ```cs // Forward loop for (int i = 1; i <= 10; i++) { doSomething(); } // Reverse loop for (int i = 10; i >= 1; i--) { doSomething(); } ``` ```lua local abcList = {"a", "b", "c"} for i, v in ipairs(abcList) do print(v) end local abcDictionary = { a=1, b=2, c=3 } for k, v in pairs(abcDictionary) do print(k, v) end ``` ```cs List abcList = new List{"a", "b", "c"}; foreach (string v in abcList) { Console.WriteLine(v); } Dictionary abcDictionary = new Dictionary { {"a", 1}, {"b", 2}, {"c", 3} }; foreach (KeyValuePair entry in abcDictionary) { Console.WriteLine(entry.Key + " " + entry.Value); } ``` Luau also supports [generalized iteration](/docs/en-us/luau/control-structures.md#generalized-iteration), which further simplifies working with tables. ## Functions To learn more about functions in Luau, see [Functions](/docs/en-us/luau/functions.md). ### Generic functions ```lua -- Generic function local function increment(number) return number + 1 end ``` ```cs // Generic function int increment(int number) { return number + 1; } ``` ### Variable argument number ```lua -- Variable argument number local function variableArguments(...) print(...) end ``` ```cs // Variable argument number void variableArguments(params string[] inventoryItems) { for (item in inventoryItems) { Console.WriteLine(item); } } ``` ### Named arguments ```lua -- Named arguments local function namedArguments(args) return args.name .. "'s birthday: " .. args.dob end namedArguments{name="Bob", dob="4/1/2000"} ``` ```cs // Named arguments string namedArguments(string name, string dob) { return name + "'s birthday: " + dob; } namedArguments(name: "Bob", dob: "4/1/2000"); ``` ## Try-catch structures ```lua local function fireWeapon() if not weaponEquipped then error("No weapon equipped!") end -- Proceed... end local success, errorMessage = pcall(fireWeapon) if not success then print(errorMessage) end ``` ```cs void fireWeapon() { if (!weaponEquipped) { // Use a user-defined exception throw new InvalidWeaponException("No weapon equipped!"); } // Proceed... } try { fireWeapon(); } catch (InvalidWeaponException ex) { // An error was raised } ``` --- title: "Metatables" url: /docs/en-us/luau/metatables last_updated: 2026-06-29T19:34:02Z description: "Metatables attach powerful metamethods to tables, allowing for manipulation like indexing, addition, and concatenation." --- # Metatables Metatables allow tables to become more powerful than before. They are attached to data and contain values called metamethods. Metamethods are fired when a certain action is used with the datum that it is attached to. ## Manipulate metatables The two primary functions for adding and finding a table's metatable are `Global.LuaGlobals.setmetatable()` and `Global.LuaGlobals.getmetatable()`. ```lua local x = {} local metaTable = {} -- Metatables are tables, too! setmetatable(x, metaTable) -- Give x a metatable called metaTable! print(getmetatable(x)) --> table: [hexadecimal memory address] ``` The `Global.LuaGlobals.setmetatable()` function also returns the table that you're setting the metatable of, so these two scripts do the same thing: ```lua local x = {} setmetatable(x, {}) ``` ```lua local x = setmetatable({}, {}) ``` ### Metamethods Metamethods are the functions that are stored inside a metatable. They can go from calling a table, to adding a table, to even dividing tables as well. Here's the list of available metamethods: | Method | Description | | --- | --- | | `__index(table, index)` | Fires when `table[index]` is indexed, if `table[index]` is `nil`. Can also be set to a table, in which case that table will be indexed. | | `__newindex(table, index, value)` | Fires when `table[index]` tries to be set `(table[index] = value)`, if `table[index]` is `nil`. Can also be set to a table, in which case that table will be indexed. | | `__call(table, ...)` | Fires when the table is called like a function, `...` is the arguments that were passed. | | `__concat(table, value)` | Fires when the `..` concatenation operator is used on the table. | | `__unm(table)` | Fires when the unary `–` operator is used on the table. | | `__add(table, value)` | The `+` addition operator. | | `__sub(table, value)` | The `–` subtraction operator. | | `__mul(table, value)` | The `*` multiplication operator. | | `__div(table, value)` | The `/` division operator. | | `__idiv(table, value)` | The `//` floor division operator. | | `__mod(table, value)` | The `%` modulus operator. | | `__pow(table, value)` | The `^` exponentiation operator. | | `__tostring(table)` | Fired when tostring is called on the table. | | `__metatable` | If present, locks the metatable so `Global.LuaGlobals.getmetatable()` will return this instead of the metatable and `Global.LuaGlobals.setmetatable()` will error. Non-function value. | | `__eq(table, value)` | The `==` equal to operator¹ | | `__lt(table, value)` | The `<` less than operator¹ | | `__le(table, value)` | The `<=` operator¹ | | `__mode` | Used in weak tables, declaring whether the keys and/or values of a table are weak. Note that references to Roblox instances are never weak. Tables that hold such references will never be garbage collected. | | `__len(table)` | Fired when the `#` length operator is used on the object. | | `__iter(table)` | Used to denote a custom iterator when using generalized iteration. | > **Info:** ¹ Requires two values with the **same** metamethod function and basic type (table/userdata/etc.); does not work with a table and another random table, or with a userdata and a table. > > If you want to override relational comparison for your type, you must implement `__lt`. By default, all four operators (`<`, `<=`, `>`, `>=`) will call it and interpret the result according to relational identities (`aa`, `a<=b` == `not(b > In some cases, such as when preserving unordered semantics exactly, you can have different behavior for `<=`.. For example, for floating-point numbers, `NaN < NaN` is false, and `NaN <= NaN` is also false. For this case, you can override `__le` as well, in which case `<=` and `>=` will use it instead of inverting the result of `__lt`. It should be noted that when writing functions for either arithmetic or relational metamethods the two function parameters are interchangeable between the table that fired the metamethod and the other value. For example, when doing vector operations with scalars division is not commutative. Therefore if you were writing metamethods for your own `vector2` class, you'd want to be careful to account for either scenario. ```lua local vector2 = {__type = "vector2"} local mt = {__index = vector2} function mt.__div(a, b) if type(a) == "number" then -- a is a scalar, b is a vector local scalar, vector = a, b return vector2.new(scalar / vector.x, scalar / vector.y) elseif type(b) == "number" then -- a is a vector, b is a scalar local vector, scalar = a, b return vector2.new(vector.x / scalar, vector.y / scalar) elseif (a.__type and a.__type == "vector2" and b.__type and b.__type == "vector2") then -- both a and b are vectors return vector2.new(a.x / b.x, a.y / b.y) end end function mt.__tostring(t) return t.x .. ", " .. t.y end function vector2.new(x, y) local self = setmetatable({}, mt) self.x = x or 0 self.y = y or 0 return self end local a = vector2.new(10, 5) local b = vector2.new(-3, 4) print(a / b) -- -3.3333333333333, 1.25 print(b / a) -- -0.3, 0.8 print(2 / a) -- 0.2, 0.4 print(a / 2) -- 5, 2.5 ``` ### Usage There are many ways to use metatables, for example the `__unm` metamethod to make a table negative: ```lua local metatable = { __unm = function(t) -- __unm is for the unary - operator local negated = {} for key, value in t do negated[key] = -value -- negate all of the values in this table end return negated -- return the table end } local table1 = setmetatable({10, 11, 12}, metatable) print(table.concat(-table1, "; ")) --> -10; -11; -12 ``` Here's an interesting way to declare things using `__index`: ```lua local metatable = { __index = {x = 1} } local t = setmetatable({}, metatable) print(t.x) --> 1 ``` `__index` was fired when `x` was indexed in the table and not found. Luau then searched through the `__index` table for an index called `x`, and, finding one, returned that. Now you can easily do that with a simple function, but there's a lot more where that came from. Take this for example: ```lua local t = {10, 20, 30} print(t(5)) ``` Typically you can't call a table, but with metatables you can: ```lua local metatable = { __call = function(t, param) local sum = {} for i, value in ipairs(t) do sum[i] = value + param -- Add the argument (5) to the value, then place it in the new table (t). end return unpack(sum) -- Return the individual table values end } local t = setmetatable({10, 20, 30}, metatable) print(t(5)) --> 15 25 35 ``` You can do a lot more as well, such as adding tables: ```lua local table1 = {10, 11, 12} local table2 = {13, 14, 15} for k, v in table1 + table2 do print(k, v) end ``` This will error saying that you're attempting to perform arithmetic on a table, but it works when attempted with a metatable: ```lua local metatable = { __add = function(t1, t2) local sum = {} for key, value in t1 do sum[key] = value end for key, value in t2 do if sum[key] then sum[key] += value else sum[key] = value end end return sum end } local table1 = setmetatable({10, 11, 12}, metatable) local table2 = setmetatable({13, 14, 15}, metatable) for k, v in table1 + table2 do print(k, v) end ``` When playing with metatables, you may run into some problems. If you need to use the `__index` metamethod to create new values in a table, but that table's metatable also has a `__newindex` metamethod, you'll want to use the Luau built-in function `Global.LuaGlobals.rawset()` to set the value without invoking any metamethods. Take the following code as an example of what happens if you don't use these functions. ```lua local t = setmetatable({}, { __index = function(self, i) self[i] = i * 10 return self[i] end, __newindex = function(self, i, v) -- Don't set values to the table the normal way end }) print(t[1]) -- Causes a stack overflow ``` Stack overflows happen when you try to call a function from itself too many times. In the `__index` function above, `self[i]` is set to a value, so when it gets to the next line, `self[i]` should exist and presumably won't call the `__index` metamethod. The problem is that `__newindex` doesn't let you set the value. Its presence stops values from being added to the table with the standard `t[i] = v` method. In order to get past this, use the `Global.LuaGlobals.rawset()` function: ```lua local t = setmetatable({}, { __index = function(self, i) rawset(self, i, i * 10) return self[i] end, __newindex = function(self, i, v) -- Don't set values to the table the normal way end }) print(t[1]) --> 10 ``` ## Use the set datatype A **set** is a collection of items with no order and no duplicate elements. An item either **is** or **is not** contained within a set. Using metatables, you can construct and manipulate sets within Luau scripts. ### Basic methods The following code includes basic set functionality, letting you construct new sets, add and remove an item, check if a set contains an item, and output the contents of a set. ```lua local Set = {} Set.__index = Set -- Function to construct a set from an optional list of items function Set.new(items) local newSet = {} for key, value in items or {} do newSet[value] = true end return setmetatable(newSet, Set) end -- Function to add an item to a set function Set:add(item) self[item] = true end -- Function to remove an item from a set function Set:remove(item) self[item] = nil end -- Function to check if a set contains an item function Set:contains(item) return self[item] == true end -- Function to output set as a comma-delimited list for debugging function Set:output() local elems = {} for key, value in self do table.insert(elems, tostring(key)) end print(table.concat(elems, ", ")) end ``` #### Create set A new set can be constructed by calling `Set.new()` with an optional array of items to add. ```lua local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"}) ``` Note that by definition, a set has no concept of ordering. #### Add item Adding an item to an existing set can be done via the `Set:add()` method. ```lua local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"}) fruits:add("Mango") ``` #### Remove item To remove an item from a set, call `Set:remove()` with the item name. ```lua local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"}) fruits:remove("Orange") ``` #### Check for item To check if a set contains a specific item, use `Set:contains()`. ```lua local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"}) local result1 = fruits:contains("Cherry") print(result1) -- true local result2 = fruits:contains("Watermelon") print(result2) -- false ``` ### Additional methods Other useful operations can be implemented for sets, letting you compare items between sets, combine sets, or subtract one set from another. #### Intersection When considering sets as Venn diagrams, you can get the **intersection** of two sets as follows, meaning the items that appear in **both** sets. ```lua local function getIntersection(set1, set2) local result = Set.new() for key, value in set1 do if set2:contains(key) then result:add(key) end end return result end local freshFruits = Set.new({"Mango", "Lemon", "Orange", "Cherry", "Lime", "Peach"}) local frozenFruits = Set.new({"Mango", "Peach", "Pineapple"}) local commonFruits = getIntersection(freshFruits, frozenFruits) commonFruits:output() -- Mango, Peach ``` #### Union You can get the **union** of two sets with the following function, meaning a collection of the items in both sets with no duplicates. Note that this function uses the metatable `__add` method to provide an addition shortcut of `set1 + set2`. ```lua function Set:__add(otherSet) local result = Set.new() for entry in self do result[entry] = true end for entry in otherSet do result[entry] = true end return result end local sweetFruits = Set.new({"Apple", "Mango", "Cherry", "Peach"}) local sourFruits = Set.new({"Lemon", "Lime"}) local allFruits = sweetFruits + sourFruits allFruits:output() -- Peach, Lime, Apple, Cherry, Lemon, Mango ``` #### Subtraction You can remove all items in one set from the items in another set via the following function. Similar to the function above, this uses the metatable `__sub` method to provide a subtraction shortcut of `set1 - set2`. ```lua function Set:__sub(otherSet) local result = Set.new() for entry in self do result[entry] = true end for entry in otherSet do result[entry] = nil end return result end local allFruits = Set.new({"Apple", "Lemon", "Mango", "Cherry", "Lime", "Peach"}) local sourFruits = Set.new({"Lemon", "Lime"}) local sweetFruits = allFruits - sourFruits sweetFruits:output() -- Mango, Apple, Cherry, Peach ``` --- title: "Native code generation" url: /docs/en-us/luau/native-code-gen last_updated: 2026-06-29T19:34:02Z description: "Luau Native Code Generation allows Luau code to be translated directly to CPU machine code." --- # Native code generation With Luau support for native code generation, server-side scripts in your experience can be compiled directly into the machine code instructions that CPUs execute, rather than regular bytecode that the Luau VM operates on. This feature can be used to improve execution speed for some scripts on the server, in particular those that have a lot of numerical computation without using too many heavy Luau library or Roblox API calls. ## Enable native code generation To enable native code generation for a `Class.Script`, add the `--!native` comment at the top:¹ ```lua --!native print("Hello from native code!") ``` This enables native code generation for all functions in the script, and the top-level scope, if deemed profitable. No additional changes are required; behavior of the natively executing scripts is exactly the same as before and only the performance is different. All features of the Luau language and all Roblox APIs remain supported. Alternatively, you can enable native code generation for an individual function by adding the `@native` attribute: ```lua @native local function f(x) return (x + 1) end ``` _1 In the future, some scripts might automatically start running natively if it is determined to be profitable, but manually placed `--!native` comments are currently required._## Best practices The following tips will help you benefit most from native code generation: - It's best to enable this feature inside scripts that perform a lot of computation directly inside Luau. If you have a lot of mathematical operations on tables and especially `Library.buffer` types, the script may be a good candidate. - Only the script's [functions](/docs/en-us/luau/functions.md) are compiled natively. The code in the top outer [scope](/docs/en-us/luau/scope.md) is often executed only once and doesn't benefit as much as functions that are called many times, especially those that are called every frame. - It's recommended that you measure the time a script or a function takes with and without native compilation to judge when it's best to use it. The [Script Profiler](#script-profiler) tool can measure the performance of functions in order to make informed decisions. - It may be tempting to place the `--!native` comment in **every** script just in case some of them will execute faster, but native code generation has some drawbacks: - Code compilation time is required which can increase the startup time of servers. - Extra memory is occupied to store natively compiled code. - There's a limit on the total allowed amount of natively compiled code in an experience. These problems can be addressed by a judicious use of the `@native` attribute. ## Code to avoid While all features will behave the same with or without native code generation enabled, some of them will not run natively and might cause de‑optimization or a fallback to interpreted execution. These include: - Use of deprecated `Global.LuaGlobals.getfenv()`/`Global.LuaGlobals.setfenv()` calls. - Use of various Luau built‑in functions like `Library.math.asin()` with non‑numeric arguments. - Passing improperly typed parameters to typed functions, for example calling `foo(true)` when `foo` is declared as `function foo(arg: string)`. Remember to always use correct [type annotations](#use-type-annotations). When using the [Script Profiler](#script-profiler), you can compare time taken by a regular version of the function versus the one compiled natively. If a function inside a `--!native` script or marked with `@native` doesn't appear to be natively executing, one or more factors from the list above may be triggering de‑optimization. ## Use type annotations Native code generation attempts to infer the most likely type for a given variable in order to optimize code paths. For example, it's assumed that `a + b` is performed on numbers, or that a table is accessed in `t.X`. Given operator overloading, however, `a` and `b` may be tables or `Datatype.Vector3` types, or `t` may be a Roblox datatype. While native code generation will support any type, mispredictions may trigger unnecessary checks, resulting in slower code execution. To solve some common issues, Luau type annotations on function arguments are checked, but it's especially recommended to annotate `Datatype.Vector3` arguments: ```lua --!native -- "v" is assumed to be a table; function performs slower due to table checks local function sumComponentsSlow(v) return v.X + v.Y + v.Z end -- "v" is declared to be a Vector3; code specialized for vectors is generated local function sumComponentsFast(v: Vector3) return v.X + v.Y + v.Z end ``` ## Studio tooling The following Studio tooling is supported for `--!native` scripts and `@native` functions. ### Debugging General [debugging](/docs/en-us/studio/debugging.md) of scripts is supported, but the views for locals/upvalues may be incomplete and missing variables from [call stack](/docs/en-us/studio/debugging.md#call-stack-window) frames that are executing natively. Also note that when debugging code selected for native compilation, placing [breakpoints](/docs/en-us/studio/debugging.md#insert-breakpoints) will disable native execution for those functions. ### Script Profiler In the [Script Profiler](/docs/en-us/studio/optimization/scriptprofiler.md), functions executing natively display `` next to them: ![Example of native functions flagged in the Script Profiler](../assets/studio/console/ScriptProfiler-Native-Annotation.png) If a function marked `@native` or inside a `--!native` script doesn't show the `` annotation, that function may not be executing natively due to [breakpoint](/docs/en-us/studio/debugging.md#insert-breakpoints) placement, use of [discouraged code](#code-to-avoid), or mismatched [type annotations](#use-type-annotations). ### Luau heap In the [Luau heap](/docs/en-us/studio/optimization/memory-usage.md#luau-heap) profiler, memory taken by native functions displays as `[native]` elements in the graph. ![Example of native memory usage flagged in the Luau Heap profiler](../assets/studio/console/LuauHeap-Native-Annotation.png) ### Size analysis Every natively-compiled script consumes memory. When the size of compiled code reaches a predefined limit, native compilation stops and the remaining code is run non‑natively. This makes it essential to choose scripts carefully for native compilation. To monitor the native code size of individual functions and scripts: 1. Make sure you're in **Server** view through the [client/server toggle](/docs/en-us/studio/testing-modes.md#toggle-clientserver) button. 2. Invoke `debug.dumpcodesize()` from the [Command Bar](/docs/en-us/studio/ui-overview.md#command-bar). In the [Output](/docs/en-us/studio/output.md) window, you'll see the total number of scripts and functions that have been natively compiled up to the point of invocation, the memory consumed by their native code, and the native code size limit. Following the summary, you'll see a table for every natively‑compiled script in descending order of code size. ![Example of native code size displayed in the Output window.](../assets/studio/debugging/Native-Code-Size-Analysis.png) For each script, the output displays the number of functions compiled and the native code memory consumption. Each function is then listed in descending order of native code size, with anonymous functions shown as `[anonymous]` and entire scripts shown as `[top level]`. In the final column, the percentage is computed with respect to the native code size limit. Note that native code size of functions is reported precisely but the memory consumption for scripts is rounded up to the nearest page size. ## Limits and troubleshooting Compiling code into instructions for a particular CPU requires additional storage memory. Additionally, optimizations for complex functions may take too much time to perform. Hitting an internal limit will report an error in Studio's [Output](/docs/en-us/studio/output.md) window, including: > _Function 'f' at line 20 exceeded single code block instruction limit_ > > This error means that a single block of code inside a function used more than 64K instructions. This can be avoided by simplifying the function or by splitting it into individual smaller functions. > _Function 'f' at line 20 exceeded function code block limit_ > > This error means that a single function contains more than 32K internal blocks of code. Internal blocks of code do not exactly map to the control‑flow blocks in your script, but this error can be avoided by simplifying the control‑flow in the function or by splitting it into individual smaller functions. > _Function 'f' at line 200 exceeded total module instruction limit_ > > This error means that, in total, the function has reached a limit of 1 million instructions for the entire script. In some cases, the reported function itself may have a lot of instructions, or the limit may have been reached by functions earlier in the script. To avoid this issue, it's recommended to either move particularly large functions into a separate non‑native script or use `@native` on the other functions. You can also try marking that separate script with`--!native`, but 1 million instructions takes up a lot of memory and you may exceed the memory limit. > _Function 'f' at line 20 encountered an internal lowering failure_ **(or)** _Internal error: Native code generation failed (assembly lowering)_ > > Sometimes a function contains complex bits of code that the native code compiler cannot currently handle. To avoid this error, inspect complex expressions in the code and split them up or simplify them, but also consider opening a bug report with an example of the code that failed for this reason. > _Memory allocation limit reached for native code generation_ > > This error means that the overall memory limit for native code data has been reached. To avoid this, try removing`--!native` from the more memory‑intensive scripts, allowing more smaller scripts to fit under the limit. Alternatively, move large or infrequently called functions to a separate non‑native module. --- title: "Nil" url: /docs/en-us/luau/nil last_updated: 2026-06-29T19:34:02Z description: "Data type representing non-existence or nothingness." --- # Nil In Luau, `nil` represents non-existence or nothingness. It's different from any other value or data type. You can use it to destroy a variable or remove a value in a table. It's the only value other than `false` which doesn't evaluate to [`true`](/docs/en-us/luau/booleans.md). Luau has a **garbage collector** that removes data that is no longer accessible by any script. For best performance, redefine large variables as `nil` in long-running scripts when you don't need them anymore so the garbage collector removes them. ```lua local variableToDelete = 5 print(variableToDelete) -- 5 variableToDelete = nil print(variableToDelete) -- nil local dictionaryTable = { Monday = 1, Tuesday = 2, Wednesday = 3 } -- Output value of 'Tuesday' key print(dictionaryTable.Tuesday) -- 2 -- Clear 'Tuesday' key dictionaryTable.Tuesday = nil -- Output value of key again print(dictionaryTable.Tuesday) -- nil ``` You can use `nil` to clear some properties of objects. For example, you can set the `Parent` of an object to `nil` to effectively remove the object from the experience. To return the object to the experience after you remove it, reassign the `Parent`. The following example demonstrates how to use `nil` to remove a `Class.Part`: ```lua local Workspace = game:GetService("Workspace") -- Create a new brick local part = Instance.new("Part") -- Parent new part to the workspace, making it viewable part.Parent = Workspace task.wait(1) -- Remove the part from view but not from memory part.Parent = nil task.wait(1) -- Part still exists because it's referenced by the variable "part", so it can be returned to view part.Parent = Workspace task.wait(1) -- Remove the part from view again part.Parent = nil -- Clear part reference so it gets picked up by the garbage collector part = nil ``` --- title: "Numbers" url: /docs/en-us/luau/numbers last_updated: 2026-06-29T19:34:02Z description: "A double-precision floating-point number." --- # Numbers The **number** data type, or `double`, represents a [double-precision (64-bit) floating-point](https://wikipedia.org/wiki/Double-precision_floating-point_format) number. Numbers can range from -1.7 * 10308 to 1.7 * 10308 (around 15 digits of precision, positive or negative). ## Signed and unsigned The sign of the number indicates whether it's positive or negative. For example, `1` is positive and `-1` is negative. In Luau, the number `-0` is equivalent to `0`. ```lua print(0 == -0) --> true print(-0 > 1) --> false print(-0 < 1) --> true print(-0 > -1) --> true print(-0 < -1) --> false ``` ## Number classifications Luau doesn't distinguish between integers and numbers, but the API reference sometimes distinguishes between them to be more specific about how to use each API. ### float The `float` number type refers to a real number with a decimal place. In computer science terms, they are [single-precision (32-bit) floating-point number](https://wikipedia.org/wiki/Single-precision_floating-point_format), which isn't as precise as double-precision floating-point numbers, but is sufficiently precise for most use cases and requires less memory and storage. ### int The `integer` number type, or `int`, refers to a 32-bit whole number, which ranges from -231 to 231 - 1. Properties and functions that expect integers may automatically round or raise errors when you assign or pass non-integers to them. ### int64 The `int64` number type refers to a signed 64-bit integer, which ranges from -263 to 263 - 1. This type of integer is common for methods that use ID numbers from the Roblox website. For example, `Class.Player.UserId` is an `int64`, and `Class.MarketplaceService:PromptPurchase()` and `Class.TeleportService:Teleport()` each expect `int64` for the ID arguments. ## Notation Numbers are notated with the most significant digits first (big-endian). There are multiple ways to notate number literals in Luau: - [Decimal (base-10)](https://wikipedia.org/wiki/Decimal) — Write the digits of the number normally using digits 0–9 with a single optional decimal point, for example `7`, `1.25`, or `-22.5`. - [Scientific notation](https://wikipedia.org/wiki/Scientific_notation) — Write a decimal number followed by `e` or `e+`, then an integer to raise the decimal number to a power of 10. For instance, `12e3` is 12 × 10^3 (12,000). - [Hexadecimal (base-16)](https://wikipedia.org/wiki/Hexadecimal) — Begin the number with `0x` followed by digits 0–9 or A–F (capitalization ignored). For example, `0xF` is 15 and `0x3FC` is 1020. - [Binary (base-2)](https://wikipedia.org/wiki/Binary_number) — Begin the number with `0b` followed by 0s or 1s, for instance `0b1100` (12 in decimal format). > **Info:** To aid in the readability of long numbers, you can include underscores anywhere within a number literal without changing the value, **except** at the beginning where this would make it an identifier. For example, `1_234_567` is the same as `1234567`, both of which are equal to 1,234,567. ## Operations You can use logical and relational [operators](/docs/en-us/luau/operators.md) to manipulate and compare numbers. You can also use mathematical functions such as `Library.math.sqrt()` and `Library.math.exp()` in the `Library.math` library and bitwise operations in the `Library.bit32` library. ## Type introspection You can determine if a value `x` is a number by using `type(x)` or `typeof(x)`. Both return the string `number` if `x` is a number. ```lua local testInt = 5 local testDecimal = 9.12761656 local testString = "Hello" print(type(testInt)) --> number print(type(testDecimal)) --> number print(type(testString)) --> string print(typeof(testInt)) --> number print(typeof(testDecimal)) --> number print(typeof(testString)) --> string ``` ## Round functions You can round numbers using `Library.math.floor()`, `Library.math.ceil()`, or `Library.math.modf()`. These functions return an integer result if Luau can represent it as an integer. If the number is too large, Luau returns it as a float. - To determine if a number `x` is an integer, use `math.floor(x) == x`. - To round a number down, use `Library.math.floor()`. - To round a number up, use `Library.math.ceil()`. - To round a number towards zero, use `Library.math.modf()`. It also returns the fractional difference of the rounded number as a second result. ```lua print(math.floor(3.3)) --> 3 print(math.floor(-3.3)) --> -4 print(math.ceil(3.3)) --> 4 print(math.ceil(-3.3)) --> -3 print(math.modf(3.3)) --> 3 0.2999999999999998 print(math.modf(-3.3)) --> -3 -0.2999999999999998 ``` --- title: "Operators" url: /docs/en-us/luau/operators last_updated: 2026-06-29T19:34:02Z description: "Operators are symbols for performing an operation or conditional evaluation." --- # Operators An **operator** is a symbol for performing an operation or conditional evaluation. ## Logical Logical operators return values depending on the boolean values of the given arguments. If an argument isn't `false` or `nil`, then the operator evaluates it as `true`. Unlike many other languages, Luau considers both zero and the empty string as `true`. The following table summarizes how logical operators behave in [conditionals](/docs/en-us/luau/control-structures.md#if-statements). | Operator | Descriptions | | --- | --- | | `and` | Evaluates as `true` only if both conditions are true | | `or` | Evaluates as `true` if either condition is true | | `not` | Evaluates as the opposite of the condition | ### and The binary operator `and` returns one of the two arguments. If the first argument evaluates to `true`, then it returns the second argument. Otherwise, it returns the first argument. ```lua print(4 and 5) -- 5 print(nil and 12) -- nil print(false and 12) -- false print(false and true) -- false print(false and false) -- false print(true and false) -- false print(true and true) -- true ``` You can use `and` to test multiple conditions in [control structures](/docs/en-us/luau/control-structures.md) such as [`if` statements](/docs/en-us/luau/control-structures.md#if-statements) and [`while` loops](/docs/en-us/luau/control-structures.md#while-loops). For example, the following `if`‑`then` statement tests that two conditions are both true: ```lua local pasta = true local tomatoSauce = true if pasta == true and tomatoSauce == true then print("We have spaghetti dinner") else print("Something is missing...") end -- Output: We have spaghetti dinner ``` ### or The binary operator `or` returns one of the two arguments. If the first argument evaluates to `true`, then it returns the first argument. Otherwise, it returns the second argument. ```lua local y = x or 1 print(y) -- 1 because x doesn't exist and is therefore nil local d = false local e = d or 1 print(e) -- 1 because d is false print(4 or 5) -- 4 print(nil or 12) -- 12 print(false or 12) -- 12 print(false or true) -- true print(false or false) -- false print(true or false) -- true print(true or true) -- true ``` You can use `or` to perform complex logical tests in [control structures](/docs/en-us/luau/control-structures.md). For example, the following `if`‑`then` statement tests whether two conditions are true **or** a third condition is true: ```lua local pasta = false local tomatoSauce = true local garlicBread = true if (pasta == true and tomatoSauce == true) or garlicBread == true then print("We have either spaghetti dinner OR garlic bread") else print("Something is missing...") end -- Output: We have either spaghetti dinner OR garlic bread ``` ### not The unary operator `not` returns the opposite boolean value of the argument. If the argument is `false` or `nil`, then it returns `true`. Otherwise, it returns `false`. ```lua print(not true) -- false print(not false) -- true print(not nil) -- true print(not "text") -- false print(not 0) -- false ``` You can use the `not` operator to trigger a conditional or loop if a variable is `false` or `nil`. ```lua local nilVariable -- Variable is declared but has no value, so it's nil local falseVariable = false -- Variable is declared with value of false if not nilVariable then print(nilVariable) -- Outputs "nil" because nil isn't true end if not falseVariable then print(falseVariable) -- Outputs "false" because false isn't true end ``` You can also use the `not` operator to test for the opposite of an entire multi-condition statement. In the following code sample, the `if`‑`then` conditional tests that it's neither true that three is greater than four nor is it true that five is greater than four. ```lua local three = 3 local four = 4 local five = 5 if not (three > four or five < four) then print("Three is less than 4 and five is greater than 4.") end -- Output: Three is less than 4 and five is greater than 4. ``` ## Relational Relational operators compare two parameters and return a [`boolean`](/docs/en-us/luau/booleans.md): `true` or `false`. | Operator | Description | Example | Associated metamethod | | --- | --- | --- | --- | | `==` | Equal to | `3 == 5` → `false` | `__eq` | | `~=` | Not equal to | `3 ~= 5` → `true` | | | `>` | Greater than | `3 > 5` → `false` | | | `<` | Less than | `3 < 5` → `true` | `__lt` | | `>=` | Greater than or equal to | `3 >= 5` → `false` | | | `<=` | Less than or equal to | `3 <= 5` → `true` | `__le` | ## Arithmetic Luau supports the usual binary operators along with exponentiation, modulus, and unary negation. | Operator | Description | Example | Associated metamethod | | --- | --- | --- | --- | | `+` | Addition | `1 + 1 = 2` | `__add` | | `-` | Subtraction | `1 - 1 = 0` | `__sub` | | `*` | Multiplication | `5 * 5 = 25` | `__mul` | | `/` | Division | `10 / 5 = 2` | `__div` | | `//` | Floor Division | `10 // 4 = 2`
`-10 // 4 = -3` | `__idiv` | | `^` | Exponentiation | `2 ^ 4 = 16` | `__pow` | | `%` | Modulus | `13 % 7 = 6` | `__mod` | | `-` | Unary negation | `-2 = 0 - 2` | `__unm` | ## Compound assignment You can use compound assignment operators to set a variable equal to the result of an operation where the first parameter is the variable's current value. The operation in a compound assignment occurs once. For example, if an expression generates a random index in a table, Luau uses the same index for both the operation and the assignment. In the following examples, suppose `local x = 3`. | Operator | Operation | Example | New Value of `x` | | --- | --- | --- | --- | | `+=` | Addition | `x += 2` | `5` | | `-=` | Subtraction | `x -= 2` | `1` | | `*=` | Multiplication | `x *= 2` | `6` | | `/=` | Division | `x /= 2` | `1.5` | | `//=` | Floor Division | `x //= 2` | `1` | | `%=` | Modulus | `x %= 2` | `1` | | `^=` | Exponentiation | `x ^= 2` | `9` | | `..=` | Concatenation | `x ..= " World!"` | `"3 World!"` | ## Miscellaneous Miscellaneous operators include **concatenation** and **length**. | Operator | Description | Example | Associated metamethod | | --- | --- | --- | --- | | `..` | Concatenates two strings | `print("Hello " .. "World!")` | `__concat` | | `#` | Length of table | If `tableVar = {1, 2, 3}`, then `#tableVar == 3`. | `__len` |
--- title: "Queues" url: /docs/en-us/luau/queues last_updated: 2026-06-29T19:34:02Z description: "Queues are data structures that follow the First In First Out (FIFO) principle." --- # Queues A queue is a linear data structure with a collection of items. There are two types of queues on Roblox: [regular queues](#regular-queues), which follow the first-in-first-out (FIFO) principle, and [priority queues](#priority-queues), which have priorities for items in the queue that determine the data accessing order. Items in both types of queues can be any Luau [data type](/docs/en-us/luau.md#types). Queue is a built-in data structure of the [non-persistent data storage](/docs/en-us/cloud-services/memory-stores/queue.md) service named `Class.MemoryStoreService`, for which you can directly call the built-in functions to get a queue and add, read, or remove data from the queue. For any other usage, such as scheduling tasks and handling requests in your experience, you can use tables to implement queues on your own. ## Regular queues Regular queues are maintained in the FIFO sequence, in which all items are added to the back of the queue and read or removed in the same order as they are added, from the front to the end. _The order of how a regular queue adds, reads, and removes items_ ## Priority queues Priority queues are not following the FIFO rule, in which each item can be added with a priority number that indicates its order being read or removed. The item at the back of a priority queue has the default priority of 0, and the item at the front of the queue has the highest set priority, which is 5 in the following example. _An item's set priority changes the order in which a queue reads items_ For this example, an item with a set priority of 3 is being added to a queue. The queue places the new item behind all existing items with the set priority of 3 or more. To place an item at the front of the queue, you need to set the priority higher than the current highest set priority. In this example, you need to set the priority to 6 or higher. Priority queues are useful for situations where you want to read or access data based on the order of importance instead of the order of being added. You can set a priority as an integer when adding an item, and the queue processes items with higher priority before items with lower priorities. For example, you can use priority queues for matchmaking by assigning higher priorities to users who have been waiting for a long time. ## Implement queues You can use built-in queues of `Class.MemoryStoreService` or use [tables](/docs/en-us/luau/tables.md) to implement queues for all other usage. The following code sample shows the implementation a **regular queue**. To use this implementation for your experience, you should save it as a `Class.ModuleScript` and store it in `Class.ReplicatedStorage`, so your queue is accessible for both client and server. ```lua --!strict local Queue = {} Queue.__index = Queue export type Queue = typeof(setmetatable( {} :: { _first: number, _last: number, _queue: { T }, }, Queue )) function Queue.new(): Queue local self = setmetatable({ _first = 0, _last = -1, _queue = {}, }, Queue) return self end -- Check if the queue is empty function Queue.isEmpty(self: Queue) return self._first > self._last end -- Add a value to the queue function Queue.enqueue(self: Queue, value: T) local last = self._last + 1 self._last = last self._queue[last] = value end -- Remove a value from the queue function Queue.dequeue(self: Queue): T if self:isEmpty() then error("Cannot dequeue from empty queue") end local first = self._first local value = self._queue[first] self._queue[first] = nil self._first = first + 1 return value end return Queue ``` The following code sample is a usage example as a `Class.Script` under `Class.Workspace`. You can modify the code, type, and storage location to fit your own usage, as long as you have the previous implementation code sample properly stored. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local Queue = require(ReplicatedStorage:WaitForChild("Queue")) local myQueue = Queue.new() -- Add some values to the queue myQueue:enqueue(5) myQueue:enqueue(10) myQueue:enqueue(15) -- myQueue = { 5, 10, 15 } -- Remove one value from the queue local first = myQueue:dequeue() print("The first value added to the queue was", first) -- myQueue = { 10, 15 } -- Add more values to the queue myQueue:enqueue(20) myQueue:enqueue(25) myQueue:enqueue(30) -- myQueue = { 10, 15, 20, 25, 30 } -- Remove another value from the queue local second = myQueue:dequeue() print("The second value added to the queue was", second) -- myQueue = { 15, 20, 25, 30 } ``` --- title: "Scope" url: /docs/en-us/luau/scope last_updated: 2026-06-29T19:34:02Z description: "The scope of a variable or function is the block of code that can access it." --- # Scope In scripting, a block of code is the body of a [control structure](/docs/en-us/luau/control-structures.md) or [function](/docs/en-us/luau/functions.md). The **scope** of a variable or function is the block of code that can access it, and it can be **global** or **local**. All blocks can access global variables and functions. A block can access local variables and functions in its parent block, but not in any of its child blocks. Variables and functions have global scope by default, but it's almost always better to declare them with local scope because Luau accesses local variables and functions faster than global ones. To give a variable or function local scope, put the keyword `local` before its name when you declare it. Scripts cannot access global and local variables or functions in other scripts. If you want to share values and functions between scripts, use `Class.ModuleScript|ModuleScripts`. ```lua local helloWorld = 'Hello World!' local function printHelloWorld() print(helloWorld) end printHelloWorld() -- Hello World! ``` - Block B **can** access the local variable in block A. - Block C **can** access the local variables and functions in blocks A and B. - Block A **cannot** access the local variables and functions in blocks B or C. - Block B **cannot** access the local variable in block C. ## Global scope After you declare a global variable or function, any block of code in the same [script](/docs/en-us/scripting.md) can access it. Variables and functions have global scope unless you declare them with the `local` keyword. In the following code, `testVar` has global scope within the local `testFunc()` function. When Luau calls the `testFunc()`, it assigns `testVar` the value `64`. The `testVar` has global scope, so the `print()` function outside `testFunc()` can access it and print `64`. ```lua local function testFunc() -- local scope testVar = 64 -- global scope end testFunc() print(testVar) -- 64 ``` In the following code, the global variable `x` starts at `0`, increments by `1` with each iteration of the [`for` loop](/docs/en-us/luau/control-structures.md#for-loops), and prints again afterward with a final value of 4. ```lua x = 0 -- Global variable "x" for i = 1, 4 do x += 1 print("Global 'x' = " .. x) end print("Global 'x' = " .. x) --[[ Resulting output: Global 'x' = 1 Global 'x' = 2 Global 'x' = 3 Global 'x' = 4 Global 'x' = 4 ]] ``` It's easier to declare global variables and functions because you don't need to type as much, but global variables and functions have the following disadvantages compared to local ones: - Luau accesses global variables and functions with a hash lookup, so it's expensive to use in terms of performance. Using a global variable in a time-critical loop can make it perform more than 10% slower than using a local variable in the same loop. - Luau disposes of local variables after their scope ends, reducing memory usage. - You can access global variables and functions within the same script, but not between multiple scripts. Therefore, a global variable or function doesn't provide any benefit over an in-scope local equivalent, an [upvalue](#capture), or a [shadow](#shadow). ## Local scope Luau can only access a local variable or function in the block of code where you declare it. Creating a variable with local scope gives you tighter control over when and where its value changes. In the following code, the `testFunc()` function and `testVar` variable have local scope. Only the code within `testFunc()` can access the `testVar` variable. The `testVar` variable doesn't have a value outside `testFunc()`, so calling `print(testVar)` within `testFunc()` prints the value of `testVar`, but calling `print(testVar)` outside `testFunc()` prints `nil`. ```lua local function testFunc() -- local scope local testVar = 64 -- local scope print(testVar) -- 64 end testFunc() print(testVar) -- nil ``` In the following code, the local variable `x` has value `0` at line 1. As Luau iterates through the [`for` loop](/docs/en-us/luau/control-structures.md#for-loops), a different local variable `x` has value `1`. Then, Luau prints the initial variable `x` with an unchanged value `0`. ```lua local x = 0 -- Local variable "x" for i = 1, 4 do local x = 1 -- Different variable "x", local to this "for" loop print("Loop 'x' = " .. x) end print("Initial 'x' = " .. x) --[[ Resulting output: Loop 'x' = 1 Loop 'x' = 1 Loop 'x' = 1 Loop 'x' = 1 Initial 'x' = 0 ]] ``` ### Capture After you declare and assign a local variable, you can read it in its scope level and functions whose scopes is enclosed by the same scope containing the local variable. This technique is known as **capturing**. In the following code, the function `f` captures the local variable `x`. The variable `x` in `f()` is an **upvalue**. ```lua local x = 5 local function f() print(x) end f() -- 5 print(x) -- 5 ``` ### Shadow After you declare and assign a local variable, you can read it in its scope level and descendant scope levels. If you redeclare and reassign the variable in a descendant scope level, then you create a new local variable with the same name but different value from the most previous assignment. The new local variable doesn't affect the local variable from the previous assignment. This technique, known as **shadowing**, helps you reuse the name of a variable without reusing its value. In the following code, Luau shadows the variable `x`. The variable `x` in `f()` is a **shadow** variable. ```lua local x = 5 local function f() local x = 7 print(x) end f() -- 7 print(x) -- 5 ``` You can also redeclare a local variable without assigning a value to it so you can reassign the variable in both its scope level and descendant scope levels. Redeclaring a local variable without assigning a value to it sets its value to `nil`. In the following code, Luau shadows the local variable `fruitName` in the function `getFruitByColor()`. The function redeclares `fruitName` as a new shadow variable and sets its value to `nil`. The function has a descendant scope level, a [`for` loop](/docs/en-us/luau/control-structures.md#for-loops), that assigns a value to `fruitName`. After the for loop, the function returns the `fruitName` shadow variable. Throughout the entire function, `fruitName` is the same local variable that doesn't overwrite the initial `fruitName` local variable. ```lua local fruitName = "Chocolate" local fruitTable = { Lemon = "Yellow", Apple = "Red", Orange = "Orange" } local function getFruitByColor(color) local fruitName for key, value in fruitTable do if value == color then fruitName = key end end return fruitName end local fruit = getFruitByColor("Yellow") print(fruit) -- Lemon print(fruit .. ", not " .. fruitName) -- Lemon, not Chocolate ``` --- title: "Stacks" url: /docs/en-us/luau/stacks last_updated: 2026-06-29T19:34:02Z description: "Stacks are data structures that follow the Last in First Out (LIFO) principle." --- # Stacks A stack is a linear data structure with a collection of items that follows the Last-In-First-Out (LIFO) principle. The top of the stack is the item most recently added to the stack, and the bottom of the stack is the item that was least recently added. You can think of the stack data structure as a stack of dinner plates: you start with one, and then you put another above it. When you take plates from the stack, the **first** one you remove from the stack is the **last** one you put on the top. Stacks have two main operations: **push** for adding an element to the top of the stack and **pop** for removing the element from the top of the stack. A Stack can either have a fixed size or be dynamically resized. Stacks are helpful for design usage such as backtracking algorithms. ## Implement stacks Though Luau doesn't have stacks as a built-in data structure, you can use [tables](/docs/en-us/luau/tables.md) to implement stacks. The following code sample shows how to create a stack, `push` an object to a stack, and `pop` an object from the stack. To use this implementation for your experience, you should save it as a `Class.ModuleScript` and store it in `Class.ReplicatedStorage`, so your stack is accessible for both client and server. ```lua local Stack = {} Stack.__index = Stack function Stack.new() local self = setmetatable({}, Stack) self._stack = {} return self end -- Check if the stack is empty function Stack:isEmpty() return #self._stack == 0 end -- Put a new value onto the stack function Stack:push(value) table.insert(self._stack, value) end -- Take a value off the stack function Stack:pop() if self:isEmpty() then return nil end return table.remove(self._stack, #self._stack) end return Stack ``` The following code sample is a usage example as a `Class.Script` under `Class.Workspace`. You can modify the code, type, and storage location to fit your own usage, as long as you have the previous implementation code sample properly stored. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local Stack = require(ReplicatedStorage:WaitForChild("Stack")) local s = Stack.new() -- Change the stack Resulting stack Output s:push(1) -- {1} s:push(5) -- {1, 5} s:push(10) -- {1, 5, 10} print(s:pop()) -- {1, 5} 10 print(s:pop()) -- {1} 5 s:push(20) -- {1, 20} print(s:pop()) -- {1} 20 print(s:pop()) -- {} 1 ``` > **Warning:** When running `pop()` on a stack, make sure the stack is not empty. If you run the previous code sample without changing anything, the expected output is: ```lua 10 5 20 1 ``` --- title: "Strings" url: /docs/en-us/luau/strings last_updated: 2026-06-29T19:34:02Z description: "A sequence of characters, such as letters, numbers, and symbols." --- # Strings The **string** data type is a sequence of characters, such as letters, numbers, and symbols. It's the data type for storing most text-based information. ## Declare strings To declare a string variable, put quotes around the characters. It's more common to use double quotes (`"`), but single quotes (`'`) also work. If you want to include a single or double quote in your string, enclose your string in the other type of quote, or use an [escaped quote](#escape-strings). ```lua local string1 = "Hello world!" print(string1) --> Hello world! local string2 = 'Hello "world"!' print(string2) --> Hello "world"! ``` To include both single and double quotes in a string, or to create multi-line strings, declare them using double brackets: ```lua local string1 = [[Hello world! Hello "world"! Hello 'world'!]] print(string1) --> Hello --> world! --> Hello "world"! --> Hello 'world'! ``` If necessary, you can nest multiple brackets inside a string using the same number of equal signs in both the beginning and ending bracket: ```lua local string1 = [=[Hello [[world!]] ]=] print(string1) --> Hello --> [[world!]] ``` ## Combine strings To combine strings, **concatenate** them with two dots (`..`). Concatenating strings doesn't insert a space between them, so you'll need to include space(s) at the end/beginning of a preceding/subsequent string, or concatenate a space between the two strings. ```lua local hello = "Hello" local helloWithSpace = "Hello " local world = "world!" local string1 = hello .. world local string2 = helloWithSpace .. world local string3 = hello .. " " .. world print(string1) --> Helloworld! print(string2) --> Hello world! print(string3) --> Hello world! ``` Note that the `print()` command takes multiple arguments and combines them **with** spaces, so you can use `,` instead of `..` to yield spaces in `print()` outputs. ```lua local hello = "Hello" local world = "world" local exclamationMark = "!" print(hello .. world .. exclamationMark) --> Helloworld! print(hello, world .. exclamationMark) --> Hello world! print(hello, world, exclamationMark) --> Hello world ! ``` ## Convert strings To convert a string to a number, use the `Global.LuaGlobals.tonumber()` function. If the string doesn't have a number representation, `Global.LuaGlobals.tonumber()` returns `nil`. ```lua local numericString = "123" print(tonumber(numericString)) --> 123 local alphanumericString = "Hello123" print(tonumber(alphanumericString)) --> nil ``` ## Escape strings To escape a double- or single-quote string declaration and embed almost any character, put a backslash (`\`) before the character. For example: - To embed a single quote in a single-quote string, use `\'`. - To embed a double quote in a double-quote string, use `\"`. ```lua local string1 = 'Hello \'world\'!' print(string1) --> Hello 'world'! local string2 = "Hello \"world\"!" print(string2) --> Hello "world"! ``` Certain characters following backslashes produce special characters rather than escaped characters: - To embed a new line, use `\n`. - To embed a horizontal tab, use `\t`. ```lua local string1 = "Hello\nworld!" print(string1) --> Hello --> world! local string2 = "Hello\tworld!" print(string2) --> Hello world! ``` ## String interpolation Luau supports **string interpolation**, a feature that lets you insert expressions into strings. Use backticks (`\``) to declare an interpolated string, then add expressions inside of curly brackets: ```lua local world = "world" local string1 = \`Hello {world}!\` print(string1) --> Hello world! ``` Although variables are the most common usage, you can use any expression, including math: ```lua local world = "world" local number = 1 local letters = {"w", "o", "r", "l", "d"} local string1 = \`Hello {world}, {number} time!\` local string2 = \`Hello {world}, {number + 1} times!\` local string3 = \`Hello {table.concat(letters)} a third time!\` print(string1) --> Hello world, 1 time! print(string2) --> Hello world, 2 times! print(string3) --> Hello world a third time! ``` Standard escape rules apply for backticks, curly brackets, and backslashes: ```lua local string1 = \`Hello \\`\{world\}\\`!\` print(string1) --> Hello \`{world}\`! ``` ## Math conversion If you perform math operations on a string, Luau automatically converts the string to a number. If the string doesn't have a number representation, it throws an error. ```lua print("55" + 10) --> 65 print("55" - 10) --> 45 print("55" * 10) --> 550 print("55" / 10) --> 5.5 print("55" % 10) --> 5 print("Hello" + 10) --> print("Hello" + 10):1: attempt to perform arithmetic (add) on string and number ``` ## Comparisons Strings can be compared using the `<`, `<=`, `>` and `>=` operators which compare using lexicographical order based on the ASCII codes of each character in a string. This will result in numbers in strings not being compared correctly, for example, `"100"` will be less than `"20"`, since the bytes `"0"` and `"1"` have lower ASCII codes than byte `"2"`. ```lua print("Apple" < "apple") --> true print("Banana" < "apple") --> true (B is before a in ASCII) print("number100" < "number20") --> true ``` ## String pattern reference A **string pattern** is a combination of characters that you can use with `Library.string.match()`, `Library.string.gmatch()`, and other functions to find a piece, or substring, of a longer string. ### Direct matches You can use direct matches in a Luau function like `Library.string.match()`, except for [magic characters](#magic-characters). For example, these commands look for the word **Roblox** within a string: ```lua local match1 = string.match("Welcome to Roblox!", "Roblox") local match2 = string.match("Welcome to my awesome game!", "Roblox") print(match1) --> Roblox print(match2) --> nil ``` ### Character classes Character classes are essential for more advanced string searches. You can use them to search for something that isn't necessarily character-specific but fits within a known category (class), including **letters**, **digits**, **spaces**, **punctuation**, and more. The following table shows the official character classes for Luau string patterns: Class | Represents | Example Match | `.` | Any character | `32kasGJ1%fTlk?@94` | | --- | --- | --- | | `%a` | An uppercase or lowercase letter | `aBcDeFgHiJkLmNoPqRsTuVwXyZ` | | `%l` | A lowercase letter | `abcdefghijklmnopqrstuvwxyz` | | `%u` | An uppercase letter | `ABCDEFGHIJKLMNOPQRSTUVWXYZ` | | `%d` | Any digit (number) | `0123456789` | | `%p` | Any punctuation character | `!@#;,.` | | `%w` | An alphanumeric character (either a letter **or** a number) | `aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789` | | `%s` | A space or whitespace character | ` `, `\n`, and `\r` | | `%c` | A special [control character](https://wikipedia.org/wiki/Control_character) | | | `%x` | A hexadecimal character | `0123456789ABCDEF` | | `%z` | The NULL character (`\0`) | | For single-letter character classes such as `%a` and `%s`, the corresponding uppercase letter represents the "opposite" of the class. For instance, `%p` represents a punctuation character while `%P` represents all characters except punctuation. ### Magic characters There are 12 "magic characters" which are reserved for special purposes in patterns: | `$` | `%` | `^` | `*` | `(` | `)` | | --- | --- | --- | --- | --- | --- | | `.` | `[` | `]` | `+` | `-` | `?` | You can escape and search for magic characters using the `%` symbol. For example, to search for **roblox.com**, escape the `.` (period) symbol by preceding it with a `%` as in `%.`. ```lua -- "roblox.com" matches "roblox#com" because the period is interpreted as "any character" local match1 = string.match("What is roblox#com?", "roblox.com") print(match1) --> roblox#com -- Escape the period with % so it is interpreted as a literal period character local match2 = string.match("I love roblox.com!", "roblox%.com") print(match2) --> roblox.com ``` ### Anchors You can search for a pattern at the beginning or end of a string by using the `^` and `$` symbols. ```lua local start1 = string.match("first second third", "^first") -- Matches because "first" is at the beginning print(start1) --> first local start2 = string.match("third second first", "^first") -- Doesn't match because "first" isn't at the beginning print(start2) --> nil local end1 = string.match("first second third", "third$") -- Matches because "third" is at the end print(end1) --> third local end2 = string.match("third second first", "third$") -- Doesn't match because "third" isn't at the end print(end2) --> nil ``` You can also use both `^` and `$` together to ensure a pattern matches only the full string and not just some portion of it. ```lua -- Using both ^ and $ to match across a full string local match1 = string.match("Roblox", "^Roblox$") -- Matches because "Roblox" is the entire string (equality) print(match1) --> Roblox local match2 = string.match("I play Roblox", "^Roblox$") -- Doesn't match because "Roblox" isn't at the beginning AND end print(match2) --> nil local match3 = string.match("I play Roblox", "Roblox") -- Matches because "Roblox" is contained within "I play Roblox" print(match3) --> Roblox ``` ### Class modifiers By itself, a character class only matches **one** character in a string. For instance, the following pattern (`"%d"`) starts reading the string from left to right, finds the **first** digit (`2`), and stops. ```lua local match = string.match("The Cloud Kingdom has 25 power gems", "%d") print(match) --> 2 ``` You can use **modifiers** with any character class to control the result: Quantifier | Meaning | `+` | Match 1 or more of the preceding character class | | --- | --- | | `-` | Match as few of the preceding character class as possible | | `*` | Match 0 or more of the preceding character class | | `?` | Match 1 or less of the preceding character class | | `%n` | For `n` between **1** and **9**, matches a substring equal to the `n`th captured string. | | `%bxy` | The balanced capture matching `x`, `y`, and everything between (for example, `%b()` matches a pair of parentheses and everything between them) | Adding a modifier to the same pattern (`"%d+"` instead of `"%d"`), outputs `25` instead of `2`: ```lua local match1 = string.match("The Cloud Kingdom has 25 power gems", "%d") print(match1) --> 2 local match2 = string.match("The Cloud Kingdom has 25 power gems", "%d+") print(match2) --> 25 ``` ### Class sets **Sets** should be used when a single character class can't do the whole job. For instance, you might want to match both lowercase letters (`%l`) **and** punctuation characters (`%p`) using a single pattern. Sets are defined by brackets `[]` around them. In the following example, notice the difference between using a set (`"[%l%p]+"`) and **not** using a set (`"%l%p+"`). ```lua local match1 = string.match("Hello!!! I am another string.", "[%l%p]+") -- Set print(match1) --> ello!!! local match2 = string.match("Hello!!! I am another string.", "%l%p+") -- Non-set print(match2) --> o!!! ``` The first command (set) tells Luau to find both lowercase characters and punctuation. With the `+` quantifier added after the entire set, it finds **all** of those characters (`ello!!!`), stopping when it reaches the space. In the second command (non-set), the `+` quantifier only applies to the `%p` class before it, so Luau grabs only the first lowercase character (`o`) before the series of punctuation (`!!!`). Like character classes, sets can be "opposites" of themselves. This is done by adding a `^` character at the beginning of the set, directly after the opening `[`. For instance, `"[%p%s]+"` represents both punctuation and spaces, while `"[^%p%s]+"` represents all characters **except** punctuation and spaces. Sets also support **ranges** which let you find an entire range of matches between a starting and ending character. This is an advanced feature which is outlined in more detail on the [Lua 5.1 Manual](https://www.lua.org/manual/5.1/manual.html#5.4.1). ### String captures String **captures** are sub-patterns within a pattern. These are enclosed in parentheses `()` and are used to get (capture) matching substrings and save them to variables. For example, the following pattern contains two captures, `(%a+)` and `(%d+)`, which return two substrings upon a successful match. ```lua local pattern = "(%a+)%s?=%s?(%d+)" local key1, val1 = string.match("TwentyOne = 21", pattern) print(key1, val1) --> TwentyOne 21 local key2, val2 = string.match("TwoThousand= 2000", pattern) print(key2, val2) --> TwoThousand 2000 local key3, val3 = string.match("OneMillion=1000000", pattern) print(key3, val3) --> OneMillion 1000000 ``` The `?` quantifier that follows both of the `%s` classes is a safe addition because it makes the space on either side of the `=` sign optional. That means the match succeeds if one (or both) spaces are missing around the equal sign. In the `Library.string.gsub()` function, you can include capture groups in the replacement string using `%1`, `%2`, `%3`, etc., up to `%9`. Even though Luau supports up to 32 capture groups before throwing an error, you can only reference the first nine with this syntax: ```lua local str = "love2play Roblox" local pattern = "(%w+)(%d+)(%w+)%s+(%w+)" local replacement = "I %1 %2 %3 %4!" local result = string.gsub(str, pattern, replacement) print(result) --> I love 2 play Roblox! ``` You can also nest string captures: ```lua local places = "The Cloud Kingdom is heavenly, The Forest Kingdom is peaceful" local pattern = "(The%s(%a+%sKingdom)[%w%s]+)" for description, kingdom in string.gmatch(places, pattern) do print(description) print(kingdom) end --> The Cloud Kingdom is heavenly --> Cloud Kingdom --> The Forest Kingdom is peaceful --> Forest Kingdom ``` This pattern search works as follows: The `Library.string.gmatch()` iterator looks for a match on the entire "description" pattern defined by the outer pair of parentheses. This stops at the first comma and captures the following: | # | Pattern | Capture | | --- | --- | --- | | **1** | `(The%s(%a+%sKingdom)[%w%s]+)` | The Cloud Kingdom is heavenly | Using its successful first capture, the iterator then looks for a match on the "kingdom" pattern defined by the inner pair of parentheses. This nested pattern simply captures the following: | # | Pattern | Capture | | --- | --- | --- | | **2** | `(%a+%sKingdom)` | Cloud Kingdom | The iterator then backs out and continues searching the full string, capturing the following: | # | Pattern | Capture | | --- | --- | --- | | **3** | `(The%s(%a+%sKingdom)[%w%s]+)` | The Forest Kingdom is peaceful | | **4** | `(%a+%sKingdom)` | Forest Kingdom | In addition to all of the above, there is a special case with an **empty capture** (`()`). If a capture is empty, then the position in the string will be captured: ```lua local match1 = "Where does the capture happen? Who knows!" local match2 = "This string is longer than the first one. Where does the capture happen? Who knows?!" local pattern = "()Where does the capture happen%? Who knows!()" local start1, finish1 = string.match(match1, pattern) print(start1, finish1) --> 1 42 local start2, finish2 = string.match(match2, pattern) print(start2, finish2) --> 43 84 ``` These special captures may be nested like normal ones: ```lua local places = "The Cloud Kingdom is heavenly, The Forest Kingdom is peaceful." local pattern = "The (%a+()) Kingdom is %a+" for kingdom, position in string.gmatch(places, pattern) do print(kingdom, position) end --> Cloud 10 --> Forest 42 ``` The returned values are unusual in that they are **numbers** rather than strings: ```lua local match = "This is an example" local pattern = "This is an ()example" local position = string.match(match, pattern) print(typeof(position)) --> number ``` --- title: "Tables" url: /docs/en-us/luau/tables last_updated: 2026-06-29T19:34:02Z description: "Data type which can store multiple values of any non-nil type, including booleans, numbers, strings, functions, and other tables." --- # Tables The **table** data type can store multiple values of any type that isn't `nil`, including [booleans](/docs/en-us/luau/booleans.md), [numbers](/docs/en-us/luau/numbers.md), [strings](/docs/en-us/luau/strings.md), [functions](/docs/en-us/luau/functions.md), and other tables. Construct tables with curly braces (`{}`): ```lua -- Construct an empty table assigned to variable "t" local t = {} print(t) -- {} ``` You can use a table as an [array](#arrays) or [dictionary](#dictionaries). Arrays use an ordered list of numbers as indices, but dictionaries can have numbers, strings, and objects as indices. For more information on built-in functions for working with tables, see the `Library.table` library. ## Arrays An **array** is an ordered list of values. Arrays are useful for storing collections of data, such as a group of players with special permissions. ### Create arrays To create an array using a Luau table, declare the values in sequential order, separated by commas. ```lua -- Construct an array with three items local testArray = {"A string", 3.14159, true} print(testArray) ``` ### Read from arrays To read from an array, add a pair of square brackets after its reference and specify the index number of the element inside (`[pos]`): ```lua -- Construct an array with three items local testArray = {"A string", 3.14159, true} print(testArray[1]) -- A string print(testArray[2]) -- 3.14159 print(testArray[3]) -- true ``` > **Warning:** Unlike some languages, Luau uses 1-based indexing for arrays, so the first item in the array is `[1]`, not `[0]`. ### Write to arrays To define or rewrite the value of an array at an index, declare the index number in square brackets (`[index]`) followed by `=` and the value: ```lua local testArray = {"A string", 3.14159, true} testArray[2] = 12345 testArray[4] = "New string" print(testArray[2]) --12345 print(testArray[4]) -- New string ``` ### Iterate over arrays To iterate over an array, you can use a `for` loop. Because the arrays have numerical indices, you can also use a numeric `for` loop from `1` to the length of the array (`#array`). ```lua local testArray = {"A string", 3.14159, true, "New string"} -- Loop using general iteration for index, value in testArray do print(index, value) end -- Iterate using the array length operator (#) for index = 1, #testArray do print(index, testArray[index]) end ``` ### Insert items There are two built-in ways to insert an item to the **end** of an array: - Pass a reference to the array and the item value to Luau's `Library.table.insert()` function. - Add the new item to the array using the `array[#array+1]` syntax. ```lua local testArray = {"A string", 3.14159} table.insert(testArray, "New string") testArray[#testArray+1] = "Another new string" print(testArray[3]) -- New string print(testArray[4]) -- Another new string ``` To insert an item between the start and end of an array, include a position value as the second argument of `Library.table.insert()`. This inserts the new item and pushes the following items up one index position. ```lua local testArray = {"First item", "Next item"} table.insert(testArray, 2, "NEW ITEM #2") print(testArray[1]) -- First item print(testArray[2]) -- NEW ITEM #2 print(testArray[3]) -- Next item ``` ### Remove items To remove an item from an array, use `Library.table.remove()`. This removes the item at the specified position and moves any following items back one index position. ```lua local testArray = {"First item", "Next item", "Last item"} table.remove(testArray, 2) print(testArray[1]) -- First item print(testArray[2]) -- Last item ``` ## Dictionaries Dictionaries are an extension of arrays. Dictionaries store a set of key-value pairs, where the keys can be any number, string, or object. ### Create dictionaries To create a dictionary table, define each **key** followed by `=` and the **value**. Separate each key-value pair with a comma: ```lua local testDictionary = { fruitName = "Lemon", fruitColor = "Yellow", sour = true } ``` The keys for dictionaries can be numbers, strings, and objects. For example, a key may also be an `Class.Instance`. To use objects as keys, declare the key in square brackets (`[key]`): ```lua local part = Instance.new("Part") local testDictionary = { partType = "Block", [part] = true } ``` ### Read from dictionaries To read from a dictionary, add a pair of brackets after its reference and specify the key name. Directly reference a string key using either (`["key"]`) or (`.key`), or instead use a variable value (`[key]`). ```lua local part = Instance.new("Part") local testDictionary = { partType = "Block", [part] = true } -- Include quotes for string keys print(testDictionary["partType"]) -- Block -- Or use . to index string keys without spaces print(testDictionary.partType) -- Block -- Omit quotes for non-string keys print(testDictionary[part]) -- true ``` ### Write to dictionaries To define or rewrite the value of a new or existing dictionary key, declare the key name in brackets (`[key]`) or, if the key is a string, use (`.key`) followed by `=` and the value: ```lua local testDictionary = { fruitName = "Lemon", sour = true } -- Change value of existing keys testDictionary["fruitName"] = "Cherry" testDictionary.sour = false -- Insert new key-value pair testDictionary.fruitCount = 10 print(testDictionary.fruitName) -- Cherry print(testDictionary.sour) -- false print(testDictionary.fruitCount) -- 10 ``` ### Iterate over dictionaries To iterate over a dictionary, use a `for` loop: ```lua local testDictionary = { fruitName = "Lemon", fruitColor = "Yellow", sour = true } for key, value in testDictionary do print(key, value) end --[[ Resulting output: fruitName Lemon sour true fruitColor Yellow ]] ``` ### Remove key-value pairs To remove or erase a key-value pair from a dictionary, set its value for a key to `nil`. ```lua local testDictionary = { fruitName = "Lemon", fruitColor = "Yellow", sour = true } testDictionary.sour = nil for key, value in testDictionary do print(key, value) end --[[ Resulting output: fruitName Lemon fruitColor Yellow ]] ``` ## Tables as references If you store a table in a new variable, Luau doesn't create a copy of that table. Instead, the variable becomes a **reference**, or pointer, to the original table. Any reference to a table reflects any changes to the original table: ```lua local originalArray = {10, 20} local arrayReference = originalArray print("Original:", originalArray[1], originalArray[2]) print("Reference:", arrayReference[1], arrayReference[2]) -- Change values in original array originalArray[1] = 1000 originalArray[2] = 2000 print("Reference:", arrayReference[1], arrayReference[2]) --[[ Resulting output: Original: 10 20 Reference: 10 20 Reference: 1000 2000 ]] ``` ## Clone tables ### Shallow clones To copy a table without any nested tables, Luau offers the `Library.table.clone()` method. ```lua local original = { key = "value", engine = "Roblox", playerID = 505306092 } local clone = table.clone(original) ``` ### Deep clones To copy a more complex table with nested tables inside it, you'll need to use a recursive function similar to the following: ```lua -- The function used for deep cloning a table local function deepClone(original) -- Define the new table for the copy local clone = table.clone(original) -- Loop through the original table to check for table values -- If a table is found as a value, deep clone it to the key (index) for key, value in original do if type(value) == "table" then clone[key] = deepClone(value) end end -- Return the finalized copy of the deep cloned table return clone end ``` With the function in place, you can make a deep copy as follows: ```lua local original = { key = "value", playerInfo = { playerID = 505306092, playerName = "PlayerName" }, otherInfo = { { {1, 3, 5, 7, 9} } } } local clone = deepClone(original) ``` ## Freeze tables Freezing a table makes it read-only, which is useful for creating constant values that you don't want to change. Freezing is permanent; there's no "unfreeze" or "thaw" method. To check if a table is frozen, use `Library.table.isfrozen()`. ### Shallow freezes To freeze a table without any nested tables, Luau offers the `Library.table.freeze()` method. ```lua local target = { key = "value", engine = "Roblox", playerID = 505306092 } table.freeze(target) target.playerID = 1 --> attempt to modify a readonly table ``` ### Deep freezes To freeze a more complex table with nested tables inside it, use a recursive function similar to the following: ```lua local function deepFreeze(target) -- Shallow freeze the table table.freeze(target) -- Check each key of the table and freeze it if it's a table for _, value in target do -- Make sure the value isn't frozen; if it already is, an error will occur if type(value) == "table" and table.isfrozen(value) == false then deepFreeze(value) end end end ``` With the function in place, you can deep freeze a table as follows: ```lua local target = { key = "value", playerInfo = { playerID = 505306092, playerName = "PlayerName" }, otherInfo = { { {1, 3, 5, 7, 9} } } } deepFreeze(target) target.playerInfo.playerID = 1 --> attempt to modify a readonly table ``` --- title: "Tuples" url: /docs/en-us/luau/tuples last_updated: 2026-06-29T19:34:02Z description: "Tuples are lists of values." --- # Tuples A **tuple** is a list of values. Many [methods](/docs/en-us/luau/functions.md#methods) and [callbacks](/docs/en-us/luau/functions.md#callbacks) in the [Roblox Engine API](/docs/en-us/reference/engine.md) accept and return multiple values, but the API Reference says "Tuple" instead of those values. ## Parameters If a [method](/docs/en-us/luau/functions.md#methods) or [callback](/docs/en-us/luau/functions.md#callbacks) accepts a tuple as a parameter, then it accepts multiple values. For example, the API Reference shows that the `Class.BindableFunction:Invoke()` method accepts a "Tuple" as a parameter, so it accepts multiple arguments. ```lua BindableFunction:Invoke(1, true, "string", Vector3.new(0, 0, 0)) ``` ## Returns If a [method](/docs/en-us/luau/functions.md#methods) or [callback](/docs/en-us/luau/functions.md#callbacks) returns a tuple, then it returns multiple values. For example, the API Reference shows that the `Class.Players:GetUserThumbnailAsync()` method returns a "Tuple", so it returns multiple values. The first return value is a Content URL, and the second is a [boolean](/docs/en-us/luau/booleans.md). ```lua local Players = game:GetService("Players") local userId = 156 -- builderman local thumbType = Enum.ThumbnailType.HeadShot local thumbSize = Enum.ThumbnailSize.Size420x420 local content, isReady = Players:GetUserThumbnailAsync(userId, thumbType, thumbSize) print(content, isReady) -- rbxthumb://type=AvatarHeadShot&id=156&w=420&h=420 true ``` --- title: "Type checking" url: /docs/en-us/luau/type-checking last_updated: 2026-06-29T19:34:02Z description: "Luau uses gradual typing through the use of type annotations and inference." --- # Type checking > **Info:** For the latest and most complete type checking documentation, see [here](https://luau.org/typecheck). Luau supports a gradual type system through the use of type annotations and type inference. These types are used to provide better warnings, errors, and suggestions in the [Script Editor](/docs/en-us/studio/script-editor.md). ## Define a type Use the `type` keyword to define your own types: ```lua type Vector2 = {x: number, y: number} ``` ## Inference modes There are three Luau type inference modes that can be set on the first line of a `Class.Script`: - `--!nocheck` — Don't check types. - `--!nonstrict` — Only asserts variable types if they are explicitly annotated. - `--!strict` — Asserts all types based off the inferred or explicitly annotated type. The `--!nonstrict` and `--!strict` modes control how strict the type checker is with inferring and checking types for variables and functions. Any type mismatches in scripts are highlighted in the [Script Editor](/docs/en-us/studio/script-editor.md) and surfaced as warnings in the [Script Analysis](/docs/en-us/studio/script-editor.md#script-analysis) window. To set a default mode for all scripts that you can override as needed, see `Class.Workspace.LuauTypeCheckMode`. ## Types A type annotation can be defined using the `:` operator after a local variable, followed by a type definition. By default, in `nonstrict` mode, all variables are assigned the type `any`. ```lua local foo: string = "bar" local x: number = 5 ``` There are four primitive types that can be used in an annotation: - `nil` - no value - `boolean` - `true` or `false` - `number` - a numeric value - `string` - text Within Roblox, all classes, data types, and enums have their own types that you can check against: ```lua local somePart: Part = Instance.new("Part") local brickColor: BrickColor = somePart.BrickColor local material: Enum.Material = somePart.Material ``` To make a type optional, use a `?` at the end of the annotation: ```lua local foo: string? = nil ``` This will allow the variable to be either the specified type (in this case `string`) or `nil`. ### Literal types You can also cast strings and booleans to literal values instead of using `string` and `boolean`: ```lua local alwaysHelloWorld: "Hello world!" = "Hello world!" alwaysHelloWorld = "Just hello!" -- Type error: Type '"Just hello!"' could not be converted into '"Hello world!"' local alwaysTrue: true = false -- Type error: Type 'false' could not be converted into 'true' ``` ### Type casts Sometimes, you might need to assist the typechecker by explicitly casting a value to a different type with the `::` operator: ```lua local myNumber = 1 local myString: string myString = myNumber -- Not OK; type conversion error myString = myNumber :: any -- OK; all expressions can be cast to 'any' local myFlag = myNumber :: boolean -- Not OK; types are unrelated ``` ## Function typing Consider the following function: ```lua local function add(x, y) return x + y end ``` This function adds `x` to `y`, but errors if one or both of them is a string. Luau doesn't know that this function can only use numbers. To prevent this category of problem, add types to the parameters: ```lua local function add(x: number, y: number) return x + y end ``` Luau now knows that the function takes two numbers and throws a warning if you try to pass anything that isn't a number into the function: ```lua add(5, 10) add(5, "foo") -- Type error: string could not be converted into number ``` To define a return type, put a `:` operator at the end of the function definition: ```lua local function add(x: number, y: number): number ``` To return multiple types, place the types in parentheses: ```lua local function FindSource(script: BaseScript, pattern: string): (string, number) return 42, true -- Type errors end ``` ### Define a functional type A functional type can be defined by using the syntax `(in) -> out`. Using the functions from the previous examples, the types of the functions are: ```lua type add = (x: number, y: number) -> number type FindSource = (script: BaseScript, pattern: string) -> (string, number) ``` ## Table types Luau does not have a `table` type; instead, table types are defined using `{}` syntax. One way of defining tables is using the `{type}` syntax, which defines a list type. ```lua local numbers: {number} = {1, 2, 3, 4, 5} local characterParts: {Instance} = LocalPlayer.Character:GetChildren() ``` Define index types using `{[indexType]: valueType}`: ```lua local numberList: {[string]: number} = { Foo = 1, Baz = 10 } numberList["bar"] = true -- Type error: boolean can't convert to number ``` Tables can also have explicit string indices defined in a type. ```lua type Car = { Speed: number, Drive: (Car) -> () } local function drive(car) -- Always go the speed limit end local taxi: Car = {Speed = 30, Drive = drive} ``` ## Variadics Here's a function that calculates the sum of an arbitrary amount of numbers: ```lua local function addLotsOfNumbers(...) local sum = 0 for _, v in {...} do sum += v end return sum end ``` As expected, this function can take any value, and the typechecker won't raise a warning if you provide an invalid type, such as a `string`. ```lua print(addLotsOfNumbers(1, 2, 3, 4, 5)) -- 15 print(addLotsOfNumbers(1, 2, "car", 4, 5)) -- Attempt to add string to number ``` Instead, assign a type to the `...`, just like how you assign any other type: ```lua local function addLotsOfNumbers(...: number) ``` And now, the second line raises a type error. ```lua print(addLotsOfNumbers(1, 2, 3, 4, 5)) print(addLotsOfNumbers(1, 2, "car", 4, 5)) -- Type error: string could not be converted into number ``` However, this does not work when writing a functional type definition: ```lua type addLotsOfNumbers = (...: number) -> number -- Expected type, got ':' ``` Instead, use the syntax `...type` to define a variadic type. ```lua type addLotsOfNumbers = (...number) -> number ``` ## Unions and intersections You can even define a type as two or more types using a union or intersection: ```lua type numberOrString = number | string type type1 = {foo: string} type type2 = {bar: number} type type1and2 = type1 & type2 -- {foo: string} & {bar: number} local numString1: numberOrString = true -- Type error local numString2: type1and2 = {foo = "hello", bar = 1} ``` ## Define an inferred type You can use the `typeof` function in a type definition for inferred types: ```lua type Car = typeof({ Speed = 0, Wheels = 4 }) --> Car: {Speed: number, Wheels: number} ``` One way to use `typeof` is to define a metatable type using `setmetatable` inside the `typeof` function: ```lua type Vector = typeof(setmetatable({}::{ x: number, y: number }, {}::{ __add: (Vector, Vector|number) -> Vector })) -- Vector + Vector would return a Vector type ``` ## Generics Generics are at a basic level parameters for types. Consider the following `State` object: ```lua local State = { Key = "TimesClicked", Value = 0 } ``` Without generics, the type for this object would be as follows: ```lua type State = { Key: string, Value: number } ``` However, you might want the type for `Value` to be based on the incoming value, which is where generics come in: ```lua type GenericType = T ``` The `` denotes a type that can be set to anything. The best way to visualize this is as a substitution type. ```lua type List = {T} local Names: List = {"Bob", "Dan", "Mary"} -- Type becomes {string} local Fibonacci: List = {1, 1, 2, 3, 5, 8, 13} -- Type becomes {number} ``` Generics can also have multiple substitutions inside the brackets. ```lua type Map = {[K]: V} ``` To rework the `State` object from earlier to use a generic type: ```lua type State = { Key: string, Value: T } ``` ### Function generics Functions can also use generics. The `State` example infers the value of `T` from the function's incoming arguments. To define a generic function, add a `<>` to the function name: ```lua local function State(key: string, value: T): State return { Key = key, Value = value } end local Activated = State("Activated", false) -- State local TimesClicked = State("TimesClicked", 0) -- State ``` ## Type exports To make it so a type can be used outside of a `Class.ModuleScript`, use the `export` keyword: ```lua export type Cat = { Name: string, Meow: (Cat) -> () } ``` ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local Types = require(ReplicatedStorage.Types) local newCat: Types.Cat = { Name = "metatablecat", Meow = function(self) print(\`{self.Name} said meow\`) end } newCat:Meow() ``` --- title: "Type coercion" url: /docs/en-us/luau/type-coercion last_updated: 2026-06-29T19:34:02Z description: "Type coercion in Luau allows for arithmetic, concatenation, and assignment of differently typed values." --- # Type coercion If Luau tries to use a value or [variable](/docs/en-us/luau/variables.md) in an operation, such as [arithmetic](#arithmetic), [concatenation](#concatenation), or [assignment](#assignment), but the value isn't the type that the operation expects, then Luau converts (**coerces**) the value to change its data type. Coercion happens at run time for that operation and doesn't change the value of a variable. ## Arithmetic Luau coerces strings to numbers in [arithmetic operations](/docs/en-us/luau/operators.md#arithmetic). This behavior is built into Luau. If the types are incompatible for arithmetic, Luau throws an error and doesn't run the rest of the script. For example, you can't add a string to a number if the string does not represent a number. ```lua print(100 + "7") -- 107 print(100 - "7") -- 93 print("1000" + 234) -- 1234 print("1000" - 234) -- 766 print("hello" + 234) -- error: attempt to perform arithmetic (add) string and number ``` ## Concatenation In concatenation, Luau coerces numbers to strings. To convert a number to a string without using coercion, use the `Library.string.format()` function. ```lua print("Pi is " .. math.pi) --> Pi is 3.1415926535898 print("Pi is " .. 3.1415927) --> Pi is 3.1415927 -- Rounds to three decimal places print("Pi is " .. string.format("%.3f", 3.1415927)) -- Pi is 3.142 ``` ## Assignment Some properties expect certain data types, such as an [Enum](#enums) or string, but you can assign a value of a different type to it and Luau converts the value to the type the property expects. ### Enums Luau coerces numbers and strings of enum values into the full enum name. For example, you can name the value of the `Class.Part.Material` property using a number, string, or full enum name, and the `print()` function always prints the full enum name. It's best practice to be explicit and use the full enum name. For more information on Enums, see [Enums](/docs/en-us/luau/enums.md). ```lua local Workspace = game:GetService("Workspace") local part1 = Instance.new("Part") part1.Material = 816 part1.Parent = Workspace print(part1.Material) -- Enum.Material.Concrete local part2 = Instance.new("Part") part2.Material = "Concrete" part2.Parent = Workspace print(part2.Material) -- Enum.Material.Concrete -- This is best practice because it's the most explicit local part3 = Instance.new("Part") part3.Material = Enum.Material.Concrete part3.Parent = Workspace print(part3.Material) -- Enum.Material.Concrete ``` ### Time of day The `Class.Lighting.TimeOfDay` property, which defines whether it is night, day, or any other time, is a string representation of the `Datatype.DateTime` data type. If you assign a number to `Class.Lighting.TimeOfDay`, Luau converts it to the string representation of `Datatype.DateTime`. ```lua local Lighting = game:GetService("Lighting") Lighting.TimeOfDay = "05:00:00" print(Lighting.TimeOfDay) -- 05:00:00 Lighting.TimeOfDay = 5 print(Lighting.TimeOfDay) -- 05:00:00 ``` --- title: "Userdata" url: /docs/en-us/luau/userdata last_updated: 2026-06-29T19:34:02Z description: "Arbitrary C/C++ data that exists in Luau." --- # Userdata Userdata is one of the basic types in Luau. Userdata represents arbitrary C/C++ data that exists in Luau. See [Userdata](https://www.lua.org/pil/28.1.html) for more information. --- title: "Variables" url: /docs/en-us/luau/variables last_updated: 2026-06-29T19:34:02Z description: "A name that holds a value." --- # Variables A **variable** is a name that holds a value. Variable values can be [numbers](/docs/en-us/luau/numbers.md), [strings](/docs/en-us/luau/strings.md), [booleans](/docs/en-us/luau/booleans.md), [data types](/docs/en-us/reference/engine/datatypes.md), and more. ## Name variables Variable names can be any non-reserved string of letters, digits, and underscores that don't start with a digit. ```lua LETTERS -- valid a1 -- valid var_name -- valid _test -- valid if -- NOT valid 25th -- NOT valid ``` Variable names are **case-sensitive**, so `TestVar` and `TESTVAR` are different names. Avoid naming variables with an underscore and all uppercase letters, such as `_VERSION`, because Luau may reserve them for internal global variables. ### Best practices It's best practice to spell out words fully. Abbreviations generally make code easier to write, but harder to read. Following common naming practices when naming your variables can help you and others understand their meaning or purpose: - Use `PascalCase` names for class and enum-like objects. - Use `PascalCase` names for all Roblox APIs. `camelCase` APIs are mostly deprecated. - Use `camelCase` names for local variables, member values, and [functions](/docs/en-us/luau/functions.md). - Use `LOUD_SNAKE_CASE` names for local constants (variables with values that you don't expect to [change](#change-values)). - Don't capitalize entire acronyms within names. For example, write `aJsonVariable` or `MakeHttpCall`. ### Reserved names Luau reserves the following keywords, so you can't use them to name variables or [functions](/docs/en-us/luau/functions.md): - `and` - `for` - `or` - `break` - `function` - `repeat` - `do` - `if` - `return` - `else` - `in` - `then` - `elseif` - `local` - `true` - `end` - `nil` - `until` - `false` - `not` - `while` ## Assign values To create a variable and assign a value to it, use the `=` operator. Put the variable on the left of the `=` and the value on the right. If you don't put a value, the value is `nil`. Variables can have **global** or **local** [scopes](/docs/en-us/luau/scope.md). They have global scope by default, but it's almost always better to create them with local scope because Luau accesses local variables faster than global ones. To give a variable local scope, put the keyword `local` before a variable's name when you assign a value to it. For more information on Scope in Luau, see [Scope](/docs/en-us/luau/scope.md). ```lua local nilVar local x = 10 local word = "Hello" local boolean = true print(nilVar) -- nil print(x) -- 10 print(word) -- Hello print(boolean) -- true ``` ### Assign values to multiple variables You can assign values to multiple variables in one line by separating each variable-value pair with a comma. If you have more variables than values, then Luau assigns `nil` to the extra variables. If you have more values than variables, Luau doesn't assign the extra values to any variables. ```lua local a, b, c = 1, 2, 3 local d, e, f = 4, 5 -- extra variable local g, h = 7, 8, 9 -- extra value print(a, b, c) -- 1, 2, 3 print(d, e, f) -- 4, 5, nil print(g, h) -- 7, 8 ``` ## Change values To change a value of a variable, assign another value to it. ```lua local x, y = 10, 20 print(x) -- 10 print(y) -- 20 local x = 1000 local y = 2000 print(x) -- 1000 print(y) -- 2000 ``` --- title: "Marketplace asset categories" url: /docs/en-us/marketplace/categories last_updated: 2026-06-29T19:34:02Z description: "Reference of all supported Marketplace asset categories and a brief description of each." --- # Marketplace asset categories > **Warning:** Roblox is currently experimenting with automated asset categorization, which may impact how your existing and future uploads are categorized. If the system misidentifies your Marketplace asset, [report a bug](https://devforum.roblox.com/c/bug-reports/10). To ensure a consistent and high-quality experience for all users, creators must categorize their uploads according to the definitions provided below. When uploading avatar assets, use the following guidelines to select your avatar asset category to the best of your ability. It's important to note that intentional [miscategorization](/docs/en-us/marketplace/marketplace-policy.md#miscategorization) is against Marketplace policy. ## Rigid accessories See [Rigid accessories](/docs/en-us/avatar/rigid-accessories.md). | Asset | Description | | --- | --- | | Hair | An accessory representing a hairstyle. | | Hat | Accessories primarily worn on, around, or floating near the top region of the avatar's head. These items sit above the eyes and ears, typically associated with the crown, forehead, or top surface of the head. | | Face | Accessories designed specifically for facial features (eyes, nose, mouth, ears). Includes items that enhance, conceal, or decorate facial areas. | | Neck | Accessories worn around the avatar's neck region, such as scarves or necklaces. | | Shoulder | Items that rest on or attach specifically to one or both shoulders. | | Front | Decorative or protective items worn specifically on the chest area, not covering the entire torso. | | Back | Accessories attached to or protruding from the avatar's back, such as wings, capes, backpacks, tails. | | Waist | Accessories worn around the avatar's waist or hips, such as belts, holsters, bags. | | Body suits
_(auto-detected)_ | A full-body asset that covers both the upper and lower body in a single piece. | | Full masks
_(auto-detected)_ | An accessory or clothing designed to fully enclose or replace the character's head and face. | ## Layered accessories See [Layered accessories](/docs/en-us/avatar/layered-accessories.md). | Asset | Description | | --- | --- | | T-Shirt | Short-sleeved top characterized by its classic "T" silhouette and collarless neckline. It fits closely to the avatar's torso without the buttons, zippers, or formal collars. | | Shirt | Upper-body garments, such as dress shirts, tanktops, camisoles. | | Sweater | Knitted or heavy-fabric garments worn directly on the torso, typically long-sleeved, and providing moderate to significant warmth. Usually softer and more casual than jackets or coats, worn as standalone or layered garments without significant outer structure. | | Pants | Garments explicitly covering the waist, hips, and legs, generally extending from the waistline down to the ankles or near-ankle region, providing complete or near-complete coverage of the lower body. | | Dress/Skirt | Garments specifically designed to cover waist, hips, and legs without separate leg compartments. Skirts typically drape downward from the waist and can vary significantly in length, from short (above knee) to full-length (ankle or floor-length). | | Shorts | Lower-body garments covering the waist, hips, and upper legs, typically ending above the knee. Provide less leg coverage compared to pants, usually informal or casual in style. | | Shoes | Footwear items that fully enclose the toes and front of the foot, typically designed with structured soles and upper coverage. | | Body suits
_(auto-detected)_ | A full-body layered asset that covers both the upper and lower body in a single piece. | | Full masks
_(auto-detected)_ | An accessory or clothing designed to fully enclose or replace the character's head and face. | ## Bodies and heads See [Bodies](/docs/en-us/avatar/character-bodies.md). | Asset | Description | | --- | --- | | Body | A collection of 3D parts (Torso, Arms, and Legs, Head) that define the character's physical structure and proportions. | | Head | A 3D component that replaces the character's default head, including support for [facial animations](/docs/en-us/avatar/dynamic-heads.md). | ## Animations See [Emotes](/docs/en-us/avatar/emotes.md). | Asset | Description | | --- | --- | | Emotes | A triggered animation sequence that performs a specific social action or gesture, such as waving or dancing. | ## Classic clothing See [Classic clothing](/docs/en-us/avatar/classic-clothing.md). | Asset | Description | | --- | --- | | Classic T-shirts | 2D clothing consisting of a single overlay image applied to the front of the character's torso. | | Classic Shirts | 2D clothing consisting of a single overlay image applied to the character's entire upper body and arms. | | Classic Pants | 2D clothing consisting of a single overlay image applied to the character's lower body and legs. |
--- title: "Custom thumbnails" url: /docs/en-us/marketplace/custom-thumbnails last_updated: 2026-06-29T19:34:02Z description: "Explains how to create your own custom thumbnail for marketplace items." --- # Custom thumbnails Thumbnail images provide a preview of the 3D asset in the [Marketplace](https://www.roblox.com/catalog), user inventories, and avatar editors. Before [uploading an asset to the Marketplace](/docs/en-us/marketplace/publish-to-marketplace.md), you can create your own thumbnails to customize the exact look and feel of your item preview thumbnails. This step is optional, but can help ensure that the thumbnail accurately portrays your item. _> **Error:** The default thumbnail may not correctly represent your item._ _> **Success:** A custom thumbnail can create a more accurate preview._ ### Create thumbnails You must create thumbnails before [publishing your asset on the Marketplace](/docs/en-us/marketplace/publish-to-marketplace.md). You can quickly create these thumbnail configuration instances with the [Custom Thumbnail Tool](https://www.roblox.com/library/11628621048/UGC-Custom-Thumbnail-Tool). Roblox stores custom thumbnail information in a child `ThumbnailConfiguration` object that parents a `CameraTarget` and `CameraValue`. To use the Custom Thumbnail Tool: 1. Install the [Custom Thumbnail Tool plugin](https://www.roblox.com/library/11628621048/UGC-Custom-Thumbnail-Tool). This may require restarting Studio. 2. In the **Explorer** window, select the `Class.Accessory` you intend to create a thumbnail for. 3. With the `Class.Accessory` highlighted in the **Explorer** window, navigate to the **Plugins** tab and click the **UGC Thumbnail Tool**. The viewport display updates and centers on the selected accessory in the workspace. 4. Adjust the viewport to center on your accessory. Use the following controls to help navigate the camera: - `W` `A` `S` `D` to pan the camera. - Hold **right-click** to rotate the camera. - Hold **middle-click** to pan the camera. - Hold `Shift` for fine adjustment. 5. When complete, click **Accept** to generate the thumbnail configuration objects. 6. You can preview the thumbnail by right-clicking the accessory in the **Explorer** window and selecting **Save To Roblox…**. The **Asset Configuration** window appears and displays the current thumbnail. If your thumbnail does not look correct, **repeat steps 2-5** to overwrite the existing thumbnail data. > **Warning:** Once you upload an asset, you can't modify or change an asset's thumbnail. ### Troubleshooting thumbnails If your thumbnails don't look the way you expect, you might need to manually resolve these issues or attempt to use the Custom Thumbnail tool again. See the following for common issues and fixes: | Issue | Description | | --- | --- | | Incorrect Thumbnail Zoom | The scale of the thumbnail depends on the `Handle` size of the `Class.Accessory`. For the best thumbnail, make sure your `Handle` covers the entire item.

When changing the size of the `Handle` object, adjust the `Size` property instead of using the resize tool. The resize tool can cause other unintended changes to the item.

You can resolve this issue by using the [Custom Thumbnail Tool](https://www.roblox.com/library/11628621048/UGC-Custom-Thumbnail-Tool). | | Backward Thumbnails | When importing meshes, Studio may change the orientation of the custom model by 180°. To resolve this, you can rotate your mesh 180° in your third party modeling tool, such as Blender or Maya, and then re-import the 3D model into Studio.

Regardless of import orientation, Studio equips the `Class.Accessory` on a character with the same fit and attachment. |
--- title: "Frequently asked questions" url: /docs/en-us/marketplace/frequently-asked-questions last_updated: 2026-06-29T19:34:02Z description: "Marketplace FAQ providing answers and resources for common questions." --- # Frequently asked questions
**What do I need to do to be eligible to sell in Marketplace?**
Creators who are [ID-verified](https://en.help.roblox.com/hc/en-us/articles/4407282410644-Age-ID-Verification), have [Roblox Plus](/docs/en-us/production/monetization/roblox-plus.md) or [Roblox Premium 1000 or 2200](https://www.roblox.com/premium/membership), and consistently follow our [Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards) and [Marketplace Policies](/docs/en-us/marketplace/marketplace-policy.md) can upload, publish, and sell in Marketplace. These eligibility requirements allow us to more effectively enforce our Marketplace policies and prevent violators from circumventing moderation. You can learn more about the requirements to upload and publish as an individual or a group [here](/docs/en-us/marketplace/marketplace-policy.md#creator-requirements). Roblox also requires a [publishing advance](/docs/en-us/marketplace/marketplace-fees-and-commissions.md#publishing-advance) for all items published on Marketplace, with the exception of free Limited items. A publishing advance is a rebatable upfront fee that you pay at the time of publishing an item.
**Why is Roblox providing more transparent eligibility requirements to create and sell?**
By providing more transparent eligibility requirements, we are making it possible for a larger and more diverse community of creators to publish items and grow their businesses on Roblox.
**Can I publish as part of a group and as an individual?**
Yes! Creators can publish as part of a group and as an individual. You can read more about the requirements to publish as a group and as an individual [here](/docs/en-us/marketplace/marketplace-policy.md#creator-requirements).
**What types of items can I create?**
Creators can create 3D avatar items which include clothing, accessories, bodies, and heads. Creators can choose to make items that are non-Limited (unlimited quantity) or [Limited](/docs/en-us/marketplace/marketplace-fees-and-commissions.md#limiteds) (3D items with a fixed quantity).
**Why does Roblox have a publishing advance?**
When the number of items in a single category goes up faster than community interest in that category, it can make it harder for people to discover unique items and for creators to earn. A publishing advance allows us to raise the bar on selling in Marketplace with a goal of incentivizing more original and unique creations. You can learn more about the Publishing Advance [here](/docs/en-us/marketplace/marketplace-fees-and-commissions.md#publishing-advance).
**Will the publishing advance ever change?**
Yes. As our market evolves, we will reevaluate our publishing advance from time to time. We expect this advance to change 2 - 3 times per quarter at most.
**Is there a cap on the number of items we can upload and publish?**
There is no cap on the number of 3D accessories, clothing, bodies, or heads that you can upload from Studio. Once your items are uploaded, you can publish any number of non-Limited items and only 1 Limited item per day.
**Why is Roblox Plus or Premium a requirement?**
We care about creating a marketplace with items that people love and where creativity is protected. We also care about ensuring Marketplace remains a safe and civil space for all. Requiring that creators have Roblox Plus or Premium and be ID verified brings more accountability to the ecosystem and motivates creators who want to sell in Marketplace to publish items they believe will sell.
**Will the price floors ever change?**
Yes. Our system can help support smarter pricing to better reflect market conditions by automatically setting the lowest price in an item category based on demand. We also give creators controls that let them set rules for how much to charge for their items relative to these dynamic prices. We're confident this will help make sure creators can earn while giving consumers more access to items at fair market prices.
**Can creators still make free items? How much does it cost to publish free items?**
Yes, creators can make free Limited items only and these will be charged for each unit created ([per-unit fee](/docs/en-us/marketplace/marketplace-fees-and-commissions.md#per-unit-fee)). As of today, the per-unit fee for free Limited items is 100 Robux. These per-unit fees will continue to be dynamic and can be changed in the future.
**Can I increase the quantity of a free Limited item after publishing?**
Yes. You can [restock](/docs/en-us/marketplace/marketplace-fees-and-commissions.md#restocking) a free Limited item to increase its available quantity by editing the quantity on the item's configure page. Restocking charges the same [per-unit fee](/docs/en-us/marketplace/marketplace-fees-and-commissions.md#per-unit-fee) as publishing for each additional unit. Restocking is only available for free Limited items that have never been made resellable.
**How much can I earn from selling my items in Marketplace?**
Creators will earn at least 30% of the gross of any item sales. The remainder is shared between the platform and, if your item is sold in an experience, the developer who created that experience (who could be you). You can learn about earnings from creating, selling, and reselling avatar items [here](/docs/en-us/marketplace/marketplace-fees-and-commissions.md).
**Does this mean you are sunsetting the [UGC Program](https://devforum.roblox.com/t/updates-and-changes-to-the-ugc-catalog-application/1974990)?**
Yes, we will be sunsetting the legacy application process to join the UGC Program. Creators who were part of the UGC Program who consistently meet our eligibility requirements mentioned above can continue to create, publish, and sell items in Marketplace.
**How does Roblox ensure creators are following the Community Standards and Marketplace policies?**
We recently revamped our enforcement and now may revoke access to creation temporarily or permanently for creators who violate our policies. Our changes included requiring ID verification to sell in Marketplace, which helps us prevent people from easily making an alternative account to rejoin the program and circumvent moderation.
**How can I protect my [intellectual property (IP)](/docs/en-us/marketplace/intellectual-property.md)?**
IP owners are the ones best positioned to tell us what they believe infringes on their rights. Use the [Rights Manager](/docs/en-us/production/publishing/rights-manager.md) for managing IP infringement removal requests. You can use Rights Manager to report experiences, avatar items, and assets such as models, audio, and images that infringe on your IP. Roblox investigates each report and takes appropriate action, which may include removing or disabling the infringing content, and, in appropriate circumstances, permanently terminating the accounts of users who have repeatedly infringed on the IP of others.
**One of my items was taken down. How do I appeal?**
All moderation decisions can be appealed via our [appeals page](https://www.roblox.com/report-appeals#/).
**Can I take down my infringing material myself?**
You can submit a request to archive your uploaded items. See [Archiving Assets](/docs/en-us/marketplace/moderation.md#archive-assets) for more details.
**What's the difference between a DSA request and a DMCA request? What happens after I file that request?**
We provide several distinct channels to report potentially violating content found in our Marketplace. These channels have different use cases and we ask the person making the report to choose the one that best suits the situation. After you submit your report or request to the correct channel, it will be reviewed and investigated, and appropriate action will be applied should the content prove violative of the laws or guidelines that the channel monitors for.
**DSA:**
If you are located in the European Union (EU) who would like to report content that they believe violates the law of EU member states, you can use our [Illegal Content Reporting Form](https://www.roblox.com/illegal-content-reporting). This process is available as part of Roblox's recent updates in response to the European Union's Digital Services Act (DSA) and should only be used by EU residents to report content that is believed to be in violation of EU and Member States law.
**DMCA:**
If you believe that your own intellectual property rights have been infringed or violated by user-created accessories, clothing, bodies, or heads, you may submit a DMCA request via the [Rights Manager](/docs/en-us/production/publishing/rights-manager.md). Rights Manager handles infringement of any copyrights you either own or are authorized to report on behalf of. If you are not the rights holder or authorized representative, your request will not be processed. For 3D accessories, clothing, bodies, or heads that may violate Roblox's Community Standards, please use the Report Abuse feature available on an item's catalog page or submit a report via [support ticket](https://www.roblox.com/support).
--- title: "UGC Homestore" url: /docs/en-us/marketplace/homestore last_updated: 2026-06-29T19:34:02Z description: "Showcase and sell your published avatar items in a easily customizable shop template." --- # UGC Homestore The **UGC Homestore** is a user-friendly template that avatar item creators can use to showcase and sell their marketplace items. This template is ideal for creators with an existing UGC catalog who want a dedicated space to promote their assets. Creators who sell their own avatar items from their own experience also benefit from a [larger commission split](/docs/en-us/marketplace/marketplace-fees-and-commissions.md#commissions). The homestore template provides the following features that you can quickly customize with minimal scripting: - **Mannequins** — 3D displays that can showcase avatar items. Users can interact with mannequins and purchase the showcased items. - **Integrated shop** — Conveniently located shop button at the bottom of the screen opens a catalog where users can view and purchase items. - **Modular Building Parts** — Customizable building components for creating a unique and personalized shopfront. You can find the UGC Homestore Template in Roblox Studio's start screen. To access Studio's provided templates at any time, select **File** ⟩ **Open from Roblox** and select **Templates**. ## Customize Homestore You can modify the features of the homestore to showcase and sell your assets as well as add your own distinct theme and design. If the place is unpublished, the homestore showcases Roblox-created assets on the mannequins and shop as reference. ### Mannequins Each mannequin includes custom attributes that allow you to add avatar items, display different poses, use different skin tones, and more. ![A store mannequin wearing a custom hair, sweater, skirt, and shoes](../assets/publishing/marketplace/Mannequin-Example.png)_When playtesting, the mannequin loads all the accessories and settings defined in its Attributes properties._ ![The Properties window with the Attributes tab expanded - various components of the mannequin are added as property fields](../assets/publishing/marketplace/Mannequin-Explorer.png)_Custom Attributes properties allow you to edit accessories, body types, and other configurations._ To customize a mannequin: 1. Select an existing mannequin object. 2. In **Properties Panel** ⟩ **Attributes**, modify the following fields: 1. `accessoryIds` — Comma separated list of accessory IDs that display on mannequin. 2. `bundleIds` — Comma separated list of bundle IDs that display on the mannequin. 1. To add shoes, you must set them as a bundle to include the right and left shoe. 3. `poseAnimation` — Animation ID for the mannequin, can be still or looped. 4. `skinColor` — Sets color tone for the surface skin of the mannequin. > **Info:** If scripting mannequin behavior, each mannequin includes the `Mannequin` tag for easy referencing. ### Shop The template includes a shop button at the bottom of the screen visible for all users. When selected, a catalog interface opens displaying all available catalog items from the creator. ![Catalog interface with the default Roblox marketplace items](../assets/publishing/marketplace/Catalog-Example.png) By default, the catalog applies the following behavior: - If the place is unpublished, such as during initial playtesting, the catalog displays Roblox's marketplace items as reference. - If the place is published, the catalog automatically displays the current experience owner's available marketplace items. - If the current experience owner doesn't have any available marketplace items, the catalog doesn't display any items. - You can [set a different creator's catalog](#specify-another-creators-catalog) as the default by modifying `ReplicatedStorage.Settings`. #### Specify another creator's catalog To set the catalog to use another creator's marketplace items: 1. In the **Security** section of [Experience Settings](/docs/en-us/studio/experience-settings.md), enable **Allow Third Party Sales**.![Section of the Explorer window highlighting the Settings file in Replicated Storage.](../assets/publishing/marketplace/Enable-Third-Party-Sales.png) 2. Ensure that the marketplace items have their sale location set to `Marketplace and All Experiences` or have specified this specific experience as a valid [sale location](/docs/en-us/marketplace/publish-to-marketplace.md#sale-location). 1. If the experience is added as a unique sale location, the experience owner [must enable the specific asset for sale using the Creator Dashboard](/docs/en-us/production/monetization/avatar-items.md#adding-items-to-experience). > **Warning:** Failure to properly link and enable a third-party asset means that users can only see the added items, but can't purchase them. 3. In **ReplicatedStorage**, open the **Settings** ModuleScript object.![Section of the Explorer window highlighting the Settings file in Replicated Storage.](../assets/publishing/marketplace/ReplicatedStorage-Settings.png) 4. Modify **DEFAULT_CREATOR_NAME** to the name of the creator. 1. If empty, the shop displays the entire Marketplace catalog from all creators. 5. Set **FETCH_CREATOR_NAME** to `false`. This sets the catalog to always reference the **DEFAULT_CREATOR_NAME** instead of the experience owner. ### Building Each building component is designed to be modular and extendible. You can quickly duplicate pieces, rotate, and rearrange them to create larger and more intricate structures. For more information on working with modular environments, see [Assembling Modular Environments](/docs/en-us/tutorials/use-case-tutorials/modeling/assemble-modular-environments.md). ### Advanced customizations For an advanced technical breakdown of the template project, including descriptions of various scripts, components, and behaviors, navigate to `ServerScriptService` and open the `README` ModuleScript. This content is intended for creators who intend to modify the underlying scripts and behaviors of the template project. ![Section of the Explorer window highlighting the README file in ServerScriptService.](../assets/publishing/marketplace/ServerScriptStorage-Readme.png) --- title: "Marketplace overview" url: /docs/en-us/marketplace last_updated: 2026-06-29T19:34:02Z description: "Understand how to create and sell items on the Marketplace, including policies, fees and commissions, and managing IP." --- # Marketplace overview The **Marketplace** is where all Roblox users can find avatar items to purchase and equip to their global avatar character. In the Marketplace, you can find character bodies and heads, clothing, accessories, animations, and other cosmetic items. Users can access the Marketplace [through the web](https://www.roblox.com/catalog) or within the Roblox App. Developers can also set up access to the Marketplace within their experiences by using the [Avatar Editor Service](/docs/en-us/players/avatar-editor.md). Developers receive a commission based on sales of Marketplace items within their experience. > **Warning:** The Marketplace is not the same as the [Creator Store](/docs/en-us/production/creator-store.md). The Creator Store provides assets for creators to use for the development of their experiences, such as models, images, and plugins. ## Sell on the Marketplace Creators can sell 3D accessories, clothing, and character bodies on the Marketplace. To ensure that assets you create meet Roblox's technical and community standards, make sure to review the following important policies and guidelines: - [Marketplace policy](/docs/en-us/marketplace/marketplace-policy.md) — covers the rules and policies for Marketplace creators and their assets. - [Intellectual property](/docs/en-us/marketplace/intellectual-property.md) — provides an overview of how intellectual property works on the Marketplace. - [Moderation](/docs/en-us/marketplace/moderation.md) — describes the process in which assets are moderated, including instructions on filing appeals and submitting DMCA requests. If you have an asset ready to sell, and both you and your asset meet Roblox's policies and guidelines, you can begin [uploading and publishing](/docs/en-us/marketplace/publish-to-marketplace.md) your assets to the Marketplace. Depending on the type of item sold, various fees and commissions can apply. For more information, see [Fees and Commissions](/docs/en-us/marketplace/marketplace-fees-and-commissions.md). --- title: "Intellectual property for avatar items" url: /docs/en-us/marketplace/intellectual-property last_updated: 2026-06-29T19:34:02Z description: "Explains protected intellectual property, both for individual and Roblox creations." --- # Intellectual property for avatar items > **Warning:** The information below is provided for general informational purposes only and is not legal advice. If you have any questions about this information or your specific situation or rights, please contact an attorney. **Intellectual property (IP)** refers to creations of the mind, such as inventions; literary and artistic works; designs; and symbols, names and images used in commerce. Copyright is a legal protection for original works of authorship. Copyright protects many different types of creative works including music, movies, pictures, books and virtual goods among others. Typically when you create an original work, the things that are unique about it – your intellectual property – automatically get legal copyright protection as soon as you create it. Only the unique, inventive aspects can be protected by copyright so **the more original and creative your design is, the more its design elements are protected by copyright**. **Publishing a design on Roblox that you found elsewhere does not necessarily give you IP ownership for that design specifically on Roblox**, which means that you may not be able to prevent other Roblox users from also making that design. For example, users that create virtual baseball caps on Roblox will not be able to request the removal of other completely different baseball caps, as shown below: Other examples where you may not have IP ownership: - If you see an interesting hat in the real world or on another platform and want to create a virtual replica of that hat on Roblox, you may still need the permission of the original creator of the hat. - If you give credit to the copyright owner, you don't automatically have the rights to use their copyrighted work. - If you want to create a hat on Roblox inspired by a well-known style of hat, such as a top hat, you may not be able to stop other users from uploading similar hats. ## Protect your own IP In order to protect your IP, it's important that your creations are unique. For example, the creation below takes a well-established concept (a Trojan helmet) and adds intricate details that make the design an original expression of the idea, such as the shape and orientation of the gold adornments and the shape of the facial opening. _Trojan helmet with design detail elements that may be copyright-protected due to their unique nature._ When the creator sees others using those specific original design elements in their designs, such as the examples below, they can request the removal of those items. _Potentially infringing Trojan helmets using unique design elements of the original._ But just because the original creator can request the removal of those three Trojan helmets does not mean that they can remove _all_ Trojan helmets from the platform. For example, the helmet below is likely not infringing because it does not include any of the creator's unique elements: _Substantially differentiated Trojan helmet sharing none of the unique design elements of the original, reducing the likelihood that it would be removed for IP infringement._ Another way to strengthen your IP protection is incorporating your own unique trademark into your items. This can be a helpful way to build your own brand natively on Roblox and visually differentiate your creations from others. To protect the creativity of others, the Roblox [Terms of Use](https://www.roblox.com/catalog/10546552/Trojan-Infantry-Helmet) states that **any content you create must respect the intellectual property rights of others – both on and off our platform**. - This means you should not upload content you do not own or did not get the owner's permission to use. - Infringing on someone's intellectual property is not only a violation of Roblox's Terms of Use, but it's also a violation of the law. ## Roblox protecting your IP Roblox complies with the [Digital Millennium Copyright Act (DMCA)](https://www.copyright.gov/dmca/) and has a designated process for receiving and handling copyright takedown reports from the owner of the original work. - Roblox must adopt and reasonably implement a policy providing for the termination in appropriate circumstances of users who are repeat copyright infringers. - Roblox investigates each report and takes appropriate action, which may include removing or disabling the infringing content, and permanently terminating the accounts of users who have repeatedly infringed on the IP of others. - If the filing is compliant with legal requirements and the infringing item is taken down, the infringing creator will lose any [per-unit fee(s)](/docs/en-us/marketplace/marketplace-fees-and-commissions.md#per-unit-fee) from publishing Limiteds, as well as Robux earned from sales of the item. Only the IP rights holder knows what rights they have and who they have given permission to use their creations. Because of that, **we rely on rights holders to let us know what they believe infringes their rights by filing a takedown report**. If you find content on Roblox that you believe infringes your IP rights, please see our [DMCA Guidelines](/docs/en-us/production/publishing/dmca-guidelines.md#what-is-the-dmca) for additional information on how to submit an intellectual property takedown notice to us. - Submitting an IP takedown notice to us is a legal process. - Only the rights holder or their authorized representative can report content that infringes on their intellectual property rights. - If Roblox receives a takedown notice about content from someone other than the IP owner or the owner's representative, Roblox cannot legally take action against the infringer unless contacted by the actual owner. ## Roblox-created IP What we shared above equally applies to items that we created as well. We've found that the majority of complaints we see about copies of Roblox-created items do not in fact _infringe on our IP_. We do enforce our own rights when items infringe on our IP, but IP protections do not necessarily extend to every idea presented in a work, but rather the original and unique elements of a work. For example, Roblox may publish a virtual item that's a replica of an ordinary, common item in the real world where countless versions (both physical and digital) already exist from many different makers. This might be an unbranded baseball cap or an [orange traffic cone](https://www.roblox.com/catalog/1082932/Traffic-Cone). With so few distinguishing elements of the Roblox-created item, even items that appear similar to our creations may not infringe on our IP rights. _Roblox-created traffic cone. Just because other creators make traffic cones, Roblox will not request the removal of other completely different traffic cones made by other creators._ --- title: "Marketplace fees and commissions" url: /docs/en-us/marketplace/marketplace-fees-and-commissions last_updated: 2026-06-29T19:34:02Z description: "Explains upload fees and commission from selling accessories and clothes on the Marketplace." --- # Marketplace fees and commissions You can create and sell bodies, heads, accessories, and clothes on the [Marketplace](https://www.roblox.com/catalog). After you pay any applicable upload fees, you can upload your new asset for marketplace approval. Once the moderation team reviews and approves your asset, you can set your item for sell on the Marketplace. You receive a commission every time users purchase your item. If users purchase your item within an experience using the [Avatar Inspect Menu](/docs/en-us/players/avatar-inspect-menu.md), [Avatar Editor Service](/docs/en-us/players/avatar-editor.md), or `Class.MarketplaceService`, the experience owner also receives a commission. For information on viewing your sales data, see [Sales Data analytics](/docs/en-us/production/analytics/analytics-dashboard.md#sales-data). ## Upload fees Uploading a 2D avatar item, such as t-shirts, shirts, and pants, requires an upload fee of **10 Robux** per submission. Uploading a 3D avatar item, such as accessories, bodies, and animations, requires an upload fee of **300 Robux** per submission. In general, fees are not refunded if an item is rejected through moderation. If your asset clears the uploading process, your asset is ready to publish to the Marketplace. ## Publishing advance A publishing advance is a refundable upfront fee that you pay at the time of publishing an item. The publishing advance does not apply for **free Limited items** which require a [per-unit fee](#per-unit-fee). Items taken off-sale and put back on-sale do not require another publishing advance. This publishing advance is dependent on the type of Marketplace item being sold: | 2D avatar asset type | Publishing advance | | --- | --- | | T-shirt | 10 | | Shirt | 10 | | Pants | 10 | | 3D avatar asset type | Non-Limited publishing advance | Paid Limited publishing advance | | --- | --- | --- | | Hat | 1500 | 13000 | | Face | 1500 | 13000 | | Hair | 1000 | 6000 | | Neck | 1000 | 15000 | | Shoulder | 1000 | 15000 | | Front | 1000 | 15000 | | Back | 1000 | 15000 | | Waist | 1000 | 15000 | | Jacket | 600 | 5000 | | T-Shirt | 600 | 5000 | | Shirt | 600 | 5000 | | Sweater | 600 | 5000 | | Pants | 600 | 5000 | | Dress/Skirt | 600 | 5000 | | Shorts | 600 | 5000 | | Body | 2500 | 20000 | | Head | 1500 | 10000 | | Shoes | 600 | 5000 | | Emotes | 1500 | 10000 | **Publishing advance example scenario** For an example of how the publishing advance is recouped, the following table represents a hypothetical situation where the **publishing advance is 1000 Robux**, and the **item is sold in an experience at 400 Robux**: | | **Creator Receives** | **Roblox Receives** | **Affiliate Receives** | | --- | --- | --- | --- | | | 30% Creator Commission | Publishing Advance Rebate (from Platform Commission) | 30% Platform Commission | 40% Affiliate (experience owner) Commission | | Unit 1 sold | 120 Robux | 120 Robux | ← Given to creator | 160 Robux | | Unit 2 sold | 120 Robux | 120 Robux | ← Given to creator | 160 Robux | | Unit 3 sold | 120 Robux | 120 Robux | ← Given to creator | 160 Robux | | Unit 4 sold | 120 Robux | 120 Robux | ← Given to creator | 160 Robux | | Unit 5 sold | 120 Robux | 120 Robux | ← Given to creator | 160 Robux | | Unit 6 sold | 120 Robux | 120 Robux | ← Given to creator | 160 Robux | | Unit 7 sold | 120 Robux | 120 Robux | ← Given to creator | 160 Robux | | Unit 8 sold

_Creator has received 960 Robux in total publishing advance rebates_ | 120 Robux | 120 Robux | ← Given to creator | 160 Robux | | Unit 9 sold

_Creator has received 1000 Robux in total publishing advance rebates_ | 120 Robux | 40 Robux | 80 Robux | 160 Robux | | Unit 10 sold

_No more publishing advance rebate_ | 120 Robux | 0 Robux | 120 Robux | 160 Robux | | Continues with each unit sold | … | … | … | … | See the following guidelines: - Publishing advance rebates are subject to revenue share. - Once per day, Roblox processes and reimburses rebates from your sales and adds them to your transaction report. - If you see the rebate on your transaction report, the credit has been applied to your account. - The 30 day escrow does not apply for rebates. - You can access this data on the Creator Dashboard's [My Transactions](https://create.roblox.com/dashboard/transactions) report, by filtering **Type of Transaction** to **Publishing Advance Rebates**.![Transaction report filtered by Publishing Advance Rebates.](../assets/publishing/marketplace/Transaction-Report.png) - If a sale occurs after Roblox processes rebates, you may need to wait until the next day's processing to receive the rebate credit and see the rebate in the transaction report. ## Commissions When community items are sold on Roblox, a portion of the sales is split between the item creator and Roblox. If the item is sold within an experience, the revenue share will additionally be split with the owner of the experience. > **Warning:** Your account must follow [Marketplace Creator Requirements](/docs/en-us/marketplace/marketplace-policy.md#creator-requirements) to keep items listed on the Marketplace and eligible for commissions. There is a 30 day escrow hold for each purchase. Roblox holds the commission from the sale of your items for 30 days starting from the date of sale and your Robux share of the sale. | Item Type | Marketplace Purchase | In-Experience Purchase | | --- | --- | --- | | 3D assets (bodies, heads, clothing, accessories) | Creator receives 30% | Creator receives 30%
Experience owner receives 40% | | Classic clothing | Creator receives 70% | Creator receives 60%
Experience owner receives 10% | > **Info:** Limited items that are sold for free follow a different per-unit payout structure. See [Per-Unit Fee](#per-unit-fee) for more information. ### Progressive revenue share Avatar items sold in the Marketplace use a progressive revenue share system: the more an item is priced above its asset type's [price floor](#price-ranges), the greater the share of revenue the creator earns. Additional guidelines: - Progressive revenue share **only applies to purchases in the Marketplace**. In-experience purchases receive the base 30% commission. - The revenue share % is progressive. For example, an item priced at 1.15x the price floor will receive ~33% of the creator revenue share. - [Regional pricing](/docs/en-us/production/monetization/regional-pricing.md) adjustments do not affect revenue share percentage. Revenue share is based solely on the listed price relative to current floor. | Price floor multiple | Total creator revenue share | | --- | --- | | 1 | 30% | | 1.3 | 37% | | 1.5 | 41% | | 2 | 50% | | 2.5 | 57% | | 3 | 62% | | 3.5 | 65% | | 4 | 67% | | 5 | 69% | | 6 | 70% | | 8 | 70% | | 10 | 70% | **Progressive commission example scenario** The following is an example of a hypothetical breakdown of various item prices and expected creator share returns. The price floor in this scenario is set to 100. | **Item price**
_(Price floor at 100)_ | **Revenue share %** | **Creator share** | | --- | --- | --- | | 100 | 30% | 30 | | 130 | 37% | 48 | | 150 | 41% | 62 | | 200 | 50% | 100 | | 250 | 57% | 142 | | 300 | 62% | 186 | | 350 | 65% | 228 | | 400 | 67% | 268 | | 500 | 69% | 345 | | 600 | 70% | 420 | | 800 | 70% | 560 | | 1000 | 70% | 700 | ## Limiteds When setting assets on sale, you can [configure your creation](/docs/en-us/marketplace/publish-to-marketplace.md#marketplace-settings) as a **Limited** item to set an available quantity for that asset. At this time, only Roblox-created Limiteds are tradeable. ### Per-unit fee When publishing free Limiteds, you must provide a per-unit fee depending on the quantity being published and other factors like the type of asset being uploaded. This payment depends on a range of market-based factors and may change over time. As a hypothetical case where Limited hat items are 100 Robux per-unit, if 200 Limited hats are listed at a **0** Robux price, the creator pays **20,000** Robux to list this free Limited item. For free Limiteds, **Roblox keeps the entire per-unit fee**. ### Restocking After publishing a free Limited, you can **restock** the item to increase its available quantity. To restock, open the item's configure page and edit the quantity to a higher value. Before you confirm, Roblox shows the total fee for the additional quantity, then charges you when you complete the restock. Restocking uses the same [per-unit fee](#per-unit-fee) as the original publish. As a hypothetical case where Limited hat items are 100 Robux per-unit, restocking a free Limited hat by an additional 50 units costs **5,000** Robux. As with publishing, **Roblox keeps the entire per-unit fee**. Restocking is only available for free Limiteds that have never been made resellable. Once you enable reselling on an item, you can no longer restock it because the [resellable status](/docs/en-us/marketplace/publish-to-marketplace.md#item-attributes) can't be disabled after it's enabled. ### Reselling If reselling is enabled by the original creator, [Roblox Plus](/docs/en-us/production/monetization/roblox-plus.md) or [Premium](https://www.roblox.com/premium/membership) members can resell purchased Limited items. Every time a creator's item is resold, the creator benefits from a 10% original creator commission. After purchasing a Limited, there is up to a 30 day holding period when the item cannot be resold. > **Info:** Reselling community-created Limiteds is not available for accounts based in Japan. **Selling Limiteds**: As a reminder, when selling anything on Roblox, the item creator, the seller, and Roblox receive a split of every transaction as follows: - 30% to creator - 40% to seller/affiliate - 30% to Roblox **Reselling community-created Limiteds**: The following is the breakdown of the earning percentages for community-created Limited resales: - 50% to reseller - 10% to creator - 10% to seller/affiliate - 30% to Roblox Similar to other commission and payment breakdowns, a single party can receive one or more commissions for a single transaction, such as the case where the original creator is also the reseller. > **Warning:** Robux acquired from trading or selling items that you didn't create are not considered earned and are ineligible for the Developer Exchange program. For more information on earning Robux, see [Earned Robux & DevEx Rates](https://en.help.roblox.com/hc/en-us/articles/203314100-Developer-Exchange-DevEx-FAQs#h_01FTN4PJV8AS4JHD2YHS363TDE). ### Rate limits To protect against automated purchases, Roblox sets purchase limits for Limiteds sold in-experience or in the Marketplace. For Limiteds, the following applies: - Users are subject to the following purchase rate limits: - 9 successful purchases per minute per user. - 1 purchase request every 2 seconds per item per user. - A maximum of 3 successful purchases per user per item. - For in-experience purchases, users must be in the experience for 60 seconds to successfully complete a purchase. ## Timed options > **Info:** At this time, timed options are available for 3D shirts, pants, and sweater categories. Timed options allows users to purchase your items for a period of time of either 3, 7, or 14 days at a cheaper cost. You can enable or disable timed options for your Marketplace assets [in bulk](#in-bulk) or [per item](#per-item). ![Example of timed options displaying for end user viewing marketplace item](../assets/publishing/marketplace/Timed-Options-Mobile-Marketplace.png)_ With timed options enabled, users gain access to multiple purchase options for each available time range._ ### In bulk To toggle timed options for multiple assets: 1. Navigate to your [Creations](https://create.roblox.com/dashboard/creations) page > Avatar items. 2. Filter by avatar asset type, such as **Clothing**. 3. On the top right, select the **Timed option** button. ![Timed options button on top right of Creations page](../assets/publishing/marketplace/Timed-Options-Bulk-A.png) 1. Follow the prompts to confirm the bulk selection and timed option settings. ![Confirmation prompt for setting timed options for all assets](../assets/publishing/marketplace/Timed-Options-Bulk-B.png) ### Per item To toggle timed options for individual assets: 1. In your [Creations](https://create.roblox.com/dashboard/creations) tab, navigate to the individual asset's **Manage Item** page. 2. Toggle the timed option for your asset. ![Individual timed option toggle on the specific asset sale setting page](../assets/publishing/marketplace/Timed-Options-Per-Item.png) 1. When complete, click the **Save Changes** button. ## Price ranges Depending on the 3D asset type, you must set the price of the item within the marketplace price range. If you are selling a [Limited](#limiteds) item, you can set your item price as **0**, or any value after the **Limiteds Price Floor**. [View Current Marketplace Ranges](https://create.roblox.com/dashboard/creations/pricing) ## Classic clothing You can upload and sell [classic clothing](/docs/en-us/avatar/classic-clothing.md) through the Roblox website. It costs **10 Robux** to upload these assets. If the seller takes the item off sale, it does not require an additional fee when they place it on sale again.
--- title: "Marketplace policy" url: /docs/en-us/marketplace/marketplace-policy last_updated: 2026-06-29T19:34:02Z description: "Marketplace Requirements lists the specific requirements for Marketplace Assets." --- # Marketplace policy Avatar assets you sell on the [Marketplace](https://www.roblox.com/catalog), such as clothing accessories, must follow specific requirements. Some requirements may require additional configuration on a third-party modeling software, such as Blender or Maya. If you are ready to sell an item and the item meets the requirements below, see [Publish Marketplace items](/docs/en-us/marketplace/publish-to-marketplace.md) for details on publishing the asset. ## Creator requirements To upload, publish, and maintain existing content on-sale, Roblox requires users and groups to have [ID verification](https://en.help.roblox.com/hc/en-us/articles/4407282410644), and/or a [Roblox Plus](/docs/en-us/production/monetization/roblox-plus.md) or [Premium membership](https://www.roblox.com/premium/membership), depending on if the asset is a 2D or 3D item. If you're a Premium user, you must have an active Premium 1000/2200 membership to keep your 3D Marketplace items on sale after uploading and publishing. See the following breakdown of the user and group requirements: | For 2D items: | Creator is able to | Requirements | | --- | --- | --- | | Individually | Upload content from Creator Dashboard | 10 Robux upload fee | | Publish content to the Marketplace | | | Keep existing content on-sale | Roblox Plus or Premium 1000/2200 | | As a Group | Upload content from Creator Dashboard | 10 Robux upload fee | | Publish content to the Marketplace | Publishing user needs:



Group owner needs Roblox Plus or Premium 1000/2200 | | Keep existing content on-sale | Group owner needs Roblox Plus or Premium 1000/2200 | | For 3D items: | Creator is able to | Requirements | | --- | --- | --- | | Individually | Upload content from Studio | | | Publish content to the Marketplace | | | Keep existing content on-sale | | | As a Group | Upload content from Studio | Uploading user needs:

| | Publish content to the Marketplace | Publishing user needs:



Group owner needs Roblox Plus or Premium 1000/2200 | | Keep existing content on-sale | Group owner needs Roblox Plus or Premium 1000/2200 | ## General creation guidelines When designing and creating your assets, follow these guidelines to ensure that your item passes moderation and allows Roblox users to express themselves with safety and civility. Failure to follow these guidelines may result in [moderation](/docs/en-us/marketplace/moderation.md) and item removal. Whether or not you are the original creator of the asset, you must ensure that you have the right to upload the asset and that your item adheres to the following program guidelines: - Follow the [Roblox Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards). For instance, do not create items that are political, religious, gory, violent, self-harm-related, drug-related, sexual/wedding/dating-related, or items that are likely to be used for these types of roleplay. These are some examples of items that violate the Community Standards. - Do not use any Roblox-created assets or official [Roblox branding or iconography](https://en.help.roblox.com/hc/en-us/articles/115001708126) as part of your items. - Do not create items that are overly similar to existing items, such as valuable and limited items. This includes scenarios such as: - Creators who have already published an item and re-publish the same item as a Limited item. - Creators who publish a Limited item and then re-publish the same Limited item. - Creators who copy another creator's item or Roblox's item. - Only sell items that you have permission to sell. If you are not the original creator or owner of the IP, you must have permission from the IP owner to sell their content or inspired content. - Be especially careful when submitting items in or near the mouth or waist of the avatar. Make sure to frequently test your assets on multiple types of characters to avoid design issues. - Do not create items that intentionally disrupt a user's experience, such as: - Obscuring the majority of the user's avatar. - Obscuring the user's in-experience UI. - Obscuring other users' avatars or in-experience view. - Making a user's avatar disappear (completely or partially). - Do not create items that depend on issues or glitches on the Roblox platform. - Do not include excessive text on items. - Do not miscategorize your items, such as listing a pants asset in the Shirt category. See [Miscategorization](#miscategorization). ## Accessory and clothing guidelines Submissions of accessory and clothing items to the Marketplace must follow additional guidelines, along with the [general guidelines](#general-creation-guidelines), [Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards), and respective technical specifications for [rigid accessories](/docs/en-us/avatar/rigid-accessories/specifications.md) or [layered accessories](/docs/en-us/avatar/layered-accessories/specifications.md) items. ### Miscategorization Do not miscategorize items when you upload them. For a list of categories and descriptions, see [Marketplace categories](/docs/en-us/marketplace/categories.md). Additional categorization information: - Complete hairstyles (hair with full head coverage) must be in the Hair category. - Partial hairstyles (e.g. Bangs and Braids) can be in Hat, Face, or Hair categories. - Facial hair must be in the Face category. - Non-hair accessories that are primarily visible above the neck must be in either Hat or Face categories. Accessories not primarily visible above the neck cannot be in these categories. - Items that sit on only the avatar shoulders can be in Shoulder. - Clothing categories must be in their proper grouping of either Tops or Bottoms. - Designs that are distinctly Tops (T-Shirt, Shirt, Jacket) must be in one of those Tops categories. - Designs that are distinctly Bottoms (Shorts, Pants, Skirt) must be in one of those Bottoms categories. - Hat designs (e.g. baseball caps, beanies, cowboy hats) must be in the Hat category. - Designs that combine items with hair designs (e.g. beanie with hair sticking out, mohawk with headphones) can be in the Hat or Hair category. - Designs that are components of facial anatomy (e.g. noses, mustache) or accessories that augment facial anatomy (e.g. eyeshadow, blush, wrinkles) must be in the Face category. - Roblox automatically detects some individual asset categories, like bodysuits. This automatic categorization occurs after upload. ## Avatar body guidelines Submissions of avatar bodies to the Marketplace must follow additional guidelines, along with the [general guidelines](#general-creation-guidelines), [Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards), and [Avatar technical specifications](/docs/en-us/avatar/character-bodies/specifications.md). ### Body part requirements Each body can only include the following parts, and cannot have additional appendages: - Body Parts - 1 right arm, comprising of an upper arm, a lower arm, and a hand. - 1 left arm, comprising of an upper arm, a lower arm, and a hand. - 1 torso, comprising of an upper torso and a lower torso. - 1 right leg, comprising of an upper leg, a lower leg, and a foot. - 1 left leg, comprising of an upper leg, a lower leg, and a foot. - Heads - Head model - All heads must be [caged](/docs/en-us/avatar/dynamic-heads/specifications.md#head-cage) - The cage mesh must envelope the head model - All heads must have one mouth region - The mouth region on the head is determined by the projection of the cage mouth region onto the head model - The cage mouth region is defined by the cage vertices forming the edge of the mouth - The mouth region does not need to have lips, teeth or tongue; it must deform in response to mouth open and close animations - All heads must have one or two eye regions - The left eye region on the head is determined by the projection of the cage left eye region onto the head model. Same applies for the right eye region - A one-eyed head has both the left and right eye regions on the cage projected onto the same region on the head model - The cage eye region is defined by the cage vertices forming the outline of the cage eyes - The head eye region does not need to have an eyeball or eyelid; it must deform in response to eye blink animations - All heads must include the [17 minimum FACS controls](/docs/en-us/avatar/dynamic-heads/specifications.md#facs-animation) - Specifically we require the head to deform in response to happiness and sadness expressions - It's recommended to include more poses to increase facial animation quality and fidelity - Eyebrows (optional) - Eyelashes (optional) Avatars must adhere to the [appropriate size requirements](/docs/en-us/avatar/character-bodies/specifications.md#body-scale) and cannot have any invisible or non-rendering body parts. See [Avatar character specifications](/docs/en-us/avatar/character-bodies/specifications.md) for more information. Roblox moderates avatars that do not follow the body part requirements under the [Misusing Roblox Systems Community Standard](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Rules). ### Modesty layers A _modesty layer_ is a layer of clothing that covers an avatar's upper torso and lower torso. In general, modesty layers are required if your character resembles a human. See the following for specific policies for when modesty layers are required: Modesty layers are **required** if your avatar character: - Has smooth and flat skin-like surface texture in the groin and chest area. Modesty layers are **optional** if your avatar character: - Resembles an **animal**. - **Animal**: Living creature that exists in the real world, doesn't resemble humans, and doesn't normally wear clothes. Examples include: dogs, sharks, insects, bears. - Resembles an **inanimate object**. - **Inanimate object**: Object in the real world that is not alive and doesn't resembles a human or animal. Examples include: food items, rocks, books, houses. | Requires modesty layer | Does not require modesty layer | | --- | --- | | __ | __ | ** Additional modesty layer examples** | Requires modesty layer | Does not require modesty layer | | --- | --- | | _[GigaChad](https://www.roblox.com/bundles/2836/GigaChad)_ | _[Cybernetic Superhero](https://www.roblox.com/bundles/808/Cybernetic-Superhero)_ | | _[Scarlett](https://www.roblox.com/bundles/3968/Scarlett)_ | _[Greaser Chicken](https://www.roblox.com/bundles/1178/Greaser-Chicken)_ | | _[Claire](https://www.roblox.com/bundles/128/Claire)_ | _[The Great Bear War Grizzly Warrior](https://www.roblox.com/bundles/370/The-Great-Bear-War-Grizzly-Warrior)_ | | _[Octavia The Ivory Spider Girl](https://www.roblox.com/bundles/473/Octavia-The-Ivory-Spider-Girl)_ | _[Scarecrow](https://www.roblox.com/bundles/6506/Scarecrow)_ | | | _[Custom Dragon Colour](https://www.roblox.com/bundles/7735/Custom-Dragon-Colour)_ | | | _[Living Pumpkin](https://www.roblox.com/bundles/8734/Living-Pumpkin)_ | | | _[Telamons Business Casual](https://www.roblox.com/bundles/210/Telamons-Business-Casual)_ | | | _[Polar Bear](https://www.roblox.com/bundles/195/Polar-Bear)_ | | | _[Teddy Bear](https://www.roblox.com/bundles/2094/Teddy-Bear)_ | | | _[Bungling](https://www.roblox.com/bundles/115561/Bungling)_ | | | _[Gang O' Fries](https://www.roblox.com/bundles/671/Gang-O-Fries)_ | | | _[Mr. Toilet](https://www.roblox.com/bundles/591/Mr-Toilet)_ | | | _[Skeleton](https://www.roblox.com/bundles/295/Skeleton)_ | | | _[Clawed Companion](https://www.roblox.com/bundles/593/Clawed-Companion)_ | For all modesty layers, the following applies: - All modesty layers must be fully opaque. While they can include artistic highlights, shadows, or textures, modesty layers that are sheer or partially transparent are not permitted. - The modesty layer must be a different color from the avatar's skin tone. - Modesty layers designed to be sexually suggestive, such as lingerie, are not permitted. - Both upper and lower torso modesty layers are required if your avatar character resembles a minor. _ Examples of appropriate modesty layers_ Roblox moderates avatars that do not follow the modesty layers requirements under the [Romantic and Sexual Content Community Standard](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Rules). #### Lower torso The **lower torso** modesty layer must provide full coverage from the avatar's hips to the bottom of the groin and buttocks. Lower torso modesty layers can include clothing styles such as briefs, high-waisted underwear, boxers, or boy shorts that fully cover the buttocks. #### Upper torso You must add an **upper torso** modesty layer if your avatar uses a skin-like texture in the chest where the breasts protrude off the chest and have a rounded or oblong shape. Upper torso modesty layers are not required for characters with flat, muscle-shaped pectorals. For upper torso modesty layers, the following applies: - For characters that resemble minors, the upper torso modesty layer must provide full coverage of the entire breast and stomach area, such as a tank top. - For characters that don't resemble minors, the upper torso modesty layer must provide full coverage of the entire breast area. - Cleavage can be depicted using outlines but must be completely covered by the modesty layer. - Full coverage of the back is not required, but it must be clear that the avatar character is not naked if seen from the back. _ Upper torso layers must be clearly visible from the back_ Upper torso modesty layers can include strapless bras, racerback bras, balconette bras, tube tops, crop tops, or bandeau tops. ### Age appropriate Avatar bodies must not be sexually suggestive, or depict or suggest nudity in genital areas. This includes: - Any depiction of exposed pubic hair, genitalia, or nipples - Any depiction of genitalia, buttock or chest cleavage, or nipples through or around the [modesty layers](#modesty-layers) - Excessive highlighting or shadowing of breasts, pelvis, or buttocks - Avatars where, due to exaggerated size, the focal point of the body is on the breasts, pelvis, or buttocks Roblox moderates avatars that do not follow the modesty layers requirements under the [Romantic and Sexual Content Community Standard](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Rules). ### Accessories and clothing Avatar bodies cannot include any accessories or clothing. Accessories are items that you can find separately in the Marketplace that are not part of the body part requirements, such as equipable tails, wings, extra limbs, glasses, clothing, [tattoos](#tattoos), and [makeup](#makeup). You must upload and sell these items separately. Heads can include hair, eyelashes, and eyebrows, but these must also be separate items. _Antennas are an example of an accessory you can find in the Marketplace and must be sold separately._ _Robot tentacles are an example of an accessory you can find in the Marketplace and must be sold separately._ ** Additional accessory examples** | Wings | | --- | | Horns | | Antlers | | Tails | | Tail Feathers | | [Hipster Glasses](https://www.roblox.com/catalog/125369932/Hipster-Glasses) | | [Sunglasses](https://www.roblox.com/catalog/42847660/Epic-Sunglasses) | | [Whiskers](https://www.roblox.com/catalog/13209067084/Whiskers) | The following is an example of rejected and accepted version of the same asset: _Body rejected for accessories (horns)._ _[Demon3](https://www.roblox.com/bundles/2485/Demon3) approved after removal of accessories on body upload._ Roblox moderates avatars with accessories or clothing under the [Misusing Roblox Systems Community Standard](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Rules). #### Tattoos Tattoos are drawings, patterns, or shapes that are distinct from the body's texture and differ in color from the rest of the body, You must upload and sell tattoos separately. Avatars can have textures, but not tattoos. Textures are a repeating pattern that looks like a tangible/physical object and covers at least 50% of the avatar. Examples of textures include: scales, feathers, rocks, and fur. _Tattoo markings on the upper arm_ _Texture that makes up natural lizard skin_ The following is an example of rejected and accepted version of the same asset: _Body rejected for tattoo (face on neck)._ _[Samy the Cobra](https://www.roblox.com/bundles/6699/Samy-The-Cobra) approved after removal of tattoo on body. Note that this avatar doesn't require a modesty layer because it does not have skin-like texture in the groin area._ #### Makeup Avatar heads can use shading and shadowing to show dimension and definition. This includes shading of the eyelids in a tone similar to skin tone, single color lips, eyeliner, eyelashes, and eyebrows, or cheeks shaded to look flushed, rosy, healthy, or warm. Your avatar head cannot include any additional color, shading, or outlining not solely used to show dimension and definition of the face. You must upload and sell these features separately. This includes facial painting, multicolor eyelashes, eyeliner, and lips, and eyeshadow that goes beyond shading and is not related to the skin tone of the rest of the body. | Facial shadowing and detail are permitted | Face painting and multi-color features are not permitted | | --- | --- | | | | | | | | | | ### Customizable skin tones Roblox recommends including customizable skin tones, or skin tone mutability, for avatars that resemble humans. Skin tone mutability is optional. Similarly, for avatars created through formal brand partnerships, skin tone mutability is optional. For more information on creating customizable skin tone textures, see [Custom Skin Tone](/docs/en-us/avatar/character-bodies/specifications.md#custom-skin-tone).
--- title: "Moderation" url: /docs/en-us/marketplace/moderation last_updated: 2026-06-29T19:34:02Z description: "Provides information on the moderation policy and process for Marketplace items." --- # Moderation When uploading an asset to the Marketplace, the Moderation team reviews the item before you can sell the item. This process can take up to 24 hours. After approval, Roblox sets your item as "Ready To Sell" in the Creator Dashboard or within Studio. To ensure that your asset has the best chance of successfully clearing moderation, make sure your asset adheres to the [Marketplace Policy](/docs/en-us/marketplace/marketplace-policy.md) and that the metadata, such as the title and description, follow [general best practices](/docs/en-us/production/publishing/publish-games-and-places.md#publish-games). Moderations can happen after the item passes initial moderation. If Roblox removes an item with sales, Roblox refunds anyone who purchased the item and cancels any pending payments for that item. If you believe your asset has been incorrectly moderated, you can [file an appeal](https://en.help.roblox.com/hc/en-us/articles/360000245263-Appeal-Your-Content-or-Account-Moderation) with our moderation staff. > **Info:** If you believe an asset is infringing on your IP, or that one of your assets has been incorrectly removed due to IP, submit a [DMCA request](/docs/en-us/production/publishing/dmca-guidelines.md). ## Archive assets Archiving an asset allows you to remove an asset from the Marketplace. This process can be used to archive both 2D and 3D avatar items. Avatar items may only be archived if they were uploaded at least 180 days prior. At this time, archiving Limited items is not supported. If you archive an avatar item: - Your item will no longer be discoverable in Marketplace and additional users will not be able to acquire it. - Existing owners of your item will continue owning the item in the inventory and can equip it on their avatar. - The underlying development assets for the avatar item will continue to exist, and you can still insert the archived avatar item into Studio if the asset ID is known. - An archived item may still be the subject of an IP takedown report, but your decision to archive the item will be considered when applying any account penalties related to the IP report. To archive items: 1. Navigate to **Creator Dashboard** ⟩ **Creations**. 2. On the asset you intend to archive, click the **⋯** icon on the thumbnail. 3. Select **Archive in Marketplace**. > **Warning:** This process is not a substitute for being vigilant about not infringing on the IP rights of others when creating and uploading content. --- title: "Publish to Marketplace" url: /docs/en-us/marketplace/publish-to-marketplace last_updated: 2026-06-29T19:34:02Z description: "Explains how to publish user-generated content to the Marketplace." --- # Publish to Marketplace In order to publish and sell assets on the Marketplace, your account or group must first meet [Roblox's Creator and Group requirements](/docs/en-us/marketplace/marketplace-policy.md#creator-and-group-requirements). Users who satisfy the requirements and have not hit their [publishing limits](#upload-and-publish-limits), can upload and sell their assets in the following general steps: 1. [Upload the asset](#upload-an-asset) through Studio for validation and moderation. 2. [Publish the asset](#publish-an-asset) to the Marketplace after configuring metadata and Marketplace settings. > **Info:** The following publishing instructions apply to 3D accessories and clothing assets. See [Classic clothing](/docs/en-us/avatar/classic-clothing.md) for information on uploading and selling classic 2D clothing. ## Upload and publish limits There are limits on the number of assets you can upload to Roblox and enable for sale on the Marketplace: | | Limiteds | Bodies or Heads | | --- | --- | --- | | Uploading and moderation | No limit | No limit | | Publishing - Enabling on sale | No limit for paid Limiteds. 50 per day for free Limiteds. | No limit | __For assets that can be taken off-sale, only the first time the asset is enabled for sale counts against this limit.__ ## Upload an asset When uploading your asset, Studio checks your asset for technical issues, such as an incorrect hierarchy or accessory structure. You should also add a [custom thumbnail](/docs/en-us/marketplace/custom-thumbnails.md) to your asset before uploading to customize how the item displays on the Marketplace. - After uploading your asset, the item is placed in the moderation queue before you can enable it on sale. See [Moderation](/docs/en-us/marketplace/moderation.md) for more information on this process. - For details on upfront payments and fees when publishing assets to the marketplace, see [Fees and commissions](/docs/en-us/marketplace/marketplace-fees-and-commissions.md). > **Warning:** You can't update or edit assets and thumbnails after uploading. You must test all of your assets thoroughly before uploading them to ensure they adhere to Roblox's [Marketplace policy](/docs/en-us/marketplace/marketplace-policy.md). To upload your asset: 1. In Studio, add the Marketplace item to your workspace. - For accessories and clothing, the item must be an `Class.Accessory` object. For information on the required specifications, see [Rigid accessory specifications](/docs/en-us/avatar/rigid-accessories/specifications.md) and [Layered clothing specifications](/docs/en-us/avatar/layered-accessories/specifications.md). - Upload clothing and accessories as an `Class.Accessory`. For information on converting your `Class.Model` into an `Class.Accessory`, see [Accessory Fitting Tool](/docs/en-us/avatar/accessory-fitting-tool.md). - If uploading shoes, each shoe must be their own accessory object: - Name the respective shoe accessories `LeftShoeAccessory` and `RightShoeAccessory`. - Select both left and right shoe accessories and right-click ⟩ **Group as a Model** before step 2. - For bodies and heads, the item must be a `Class.Model`. For information on the required specifications, see [Avatar character specifications](/docs/en-us/avatar/character-bodies/specifications.md). - You can add a default skin color to your body by adding a `Class.BodyColors` object to your model. 2. In the **Explorer** window, right-click the object and select **Save to Roblox**. 3. In **Submit As**, select **Avatar Asset**. 4. In the **Asset type** dropdown menu, select the appropriate marketplace asset type. Validation begins upon selection. > **Info:** If you don't see this dropdown, your account might not have access to upload assets to the Marketplace. For more information, see [Creator requirements](/docs/en-us/marketplace/marketplace-policy.md#creator-requirements)._Uploading a clothing accessory asset__Uploading a body asset bundle_ 5. Once validation completes successfully, an item description dialog displays. Fill out the following fields and follow [metadata best practices](/docs/en-us/production/publishing/publish-games-and-places.md#publish-games) when possible. - **Title**: The name of your asset in the Marketplace. - **Creator**: You can set your user or any associated groups as the creator of this asset. The creator can access the Marketplace asset to set prices, benefits, and access sales information. 6. Click **Submit** to pay any applicable upload fee and submit your asset for moderation approval. After uploading the asset, you can find it in the **Creations** tab of the [Creator Hub](https://create.roblox.com/dashboard/creations?activeTab=HairAccessory&filterIndex=0) in a pre-published state where you can view the current moderation status or edit the title and description. ![List of Hat assets on the Creator Hub with current status listed below each entry.](../assets/publishing/marketplace/Creation-Page.png) ### Troubleshooting During the upload process, you might encounter issues that prevent an item from uploading. These might occur due to technical issues with the asset itself, or problems with the metadata of the asset, such as invalid ownership, or inappropriate text strings. For technical issues during the validation step, hover over the error to see the specific failure output. Most issues may require adjusting your model in your third-party modeling application and re-importing into Studio. See the modeling specifications for [rigid accessories](/docs/en-us/avatar/rigid-accessories/specifications.md), [layered accessories](/docs/en-us/avatar/layered-accessories/specifications.md), and [character bodies](/docs/en-us/avatar/character-bodies/specifications.md) for technical requirements. ![An example error indicating a missing FaceControls object from an Avatar Head.](../assets/publishing/marketplace/Error-Example.png)_An error that displays when a FaceControls object is not detected for head animation_ For other validation issues, keep in mind the following common uploading conflicts: - Studio's text filter identifies inappropriate strings in the name or description. - Your item uses an `AssetID` or `TextureID` that belongs to an existing asset in the Marketplace, or doesn't belong to you or an appropriate group. - Your item uses an `AssetID` or `TextureID` that is still pending moderation. ## Publish an asset You can access uploaded assets in your [Creator Dashboard](https://create.roblox.com/dashboard/creations). Each item has a **Manage Item** page where you can modify metadata, configure sale-related settings, and publish your asset to the Marketplace. > **Warning:** You can't change or modify the asset or thumbnail after upload. If you discover that your item is broken or doesn't work as expected, you might need to remove the item and re-upload a corrected asset. > > If a Roblox update breaks an existing asset, [submit a help ticket](https://www.roblox.com/support) with a link to the broken item and an `.rbxm` of the correct version. Before publishing your asset, it's important to consider if you want to sell your asset as a Limited or Non-Limited item. ### Metadata You can adjust the metadata for any of your creations at any time. Keep in mind that the metadata, such as the title and description, should follow [general best practices](/docs/en-us/production/publishing/publish-games-and-places.md#publish-games) to avoid moderation and improve discoverability. ![Manage Item page with example asset.](../assets/publishing/marketplace/Manage-Item-Page.png) On the Manage Item page, you can update the following fields: - **Title**: The name of your asset in the Marketplace. You can modify this after publishing. - **Description**: The description of your asset in the Marketplace. ### Item attributes In **Item Attributes** you can set the **Availability** of your asset as a **Non-Limited** or a **Limited** item. Each availability type changes the available item attribute fields you can modify. - **Non-Limited** items allow for unlimited copies to be purchased. - **Limited** items allow only a fixed quantity of items to be purchased, allowing you to sell a set number of exclusive or event-specific digital items. | Item availability | Sale properties | | --- | --- | | **Non-Limited**
| — Unlimited copies available to purchase
— Can't limit number of copies per user
— Can't sell free items
— Can't resell | | **Limited**
| — Limits total number of copies sold
— Can limit number of original copies per user
— Can sell free items, per-unit fee applies
— Resellable | > **Warning:** Before selecting either option, familiarize yourself with the various [fee and commission structures](/docs/en-us/marketplace/marketplace-fees-and-commissions.md) involved with different types of items and any applicable country policy limitations. ![Item Attribute settings.](../assets/publishing/marketplace/Item-Attributes.png) The following item attributes only apply if you set your asset as a Limited item: - **Quantity**: The number of assets that are available on the Marketplace. The maximum quantity for paid Limiteds is 3000, while free Limiteds do not have a quantity maximum. After publishing a free Limited, you can increase its quantity by [restocking](/docs/en-us/marketplace/marketplace-fees-and-commissions.md#restocking) the item. - **Limit copies per user**: (Optional) Sets the number of times a user can purchase this asset. - Copy limits are based on ownership. If a user owns the maximum limit of copies in their inventory, they can no longer purchase the asset from the original listing. If the user no longer owns the maximum number of copies, they can purchase the copies from the original listing. - After publishing, you can only increase, but not decrease, this value. - **Free Item**: Sets the selling price of this item to zero. Free Limiteds utilize a [per-unit fee structure](/docs/en-us/marketplace/marketplace-fees-and-commissions.md#per-unit-fee). - **Resellable**: If enabled, users can resell your Limited item. If resold, you are eligible for [resell commissions](/docs/en-us/marketplace/marketplace-fees-and-commissions.md#reselling). You can change the **Resellable status** on a published item. If enabled on an item, it can not be disabled in the future. - The resellable attribute is not available for accounts based in Japan. - Enabling reselling permanently disables [restocking](/docs/en-us/marketplace/marketplace-fees-and-commissions.md#restocking) for the item. ##### Limit copies per user If you are selling a Limited item, you can enable **Limit Copies Per User** to set the number of times a user can purchase this asset. This only affects the purchases from the original listing and users can still purchase additional copies of the same Limited asset from resellers. You can't change this setting after publishing. Copy limits are based on ownership. If a user owns the maximum limit of copies in their inventory, they can no longer purchase the asset from the original listing. If the user no longer owns the maximum number of copies, they can purchase the copies from the original listing. ### Pricing In the pricing section, you can set various price settings. When listing your Marketplace item, you must set your price within the appropriate [dynamic price range](/docs/en-us/marketplace/marketplace-fees-and-commissions.md#price-ranges) for that asset type. ![Price settings fields.](../assets/publishing/marketplace/Pricing-Settings.png) You can set two types of price controls based on the price floor: - **Amount Above Price Floor (required)** is the amount of Robux that the item will be above the current price floor. For example, if the floor is 50 Robux and you input 5 Robux here, the price will be 55 Robux. You can also choose to set this value to zero if you want your item to always be priced at the floor price. - **Do Not Price Below (optional)** is the lowest price a creator's item can go. It is optional and will always be respected. This input is helpful if you care about what the lowest price your item can be sold for. For example, if you input 200 Robux here, your item price will never drop below 200 Robux regardless of what the price floor is. > **Info:** If you had a Marketplace listing before the implementation of the dynamic price range, the following price controls now apply until changed: - **Amount Above Price Floor** defaults to 0 Robux. - **Do Not Price Below** defaults to the existing price of the item. When setting a price, you can see the percentage breakdown of commissions in the dialog box. A similar breakdown on fees, when applicable, is provided at the bottom of the prompt. See [Marketplace fees and commissions](/docs/en-us/marketplace/marketplace-fees-and-commissions.md) for more information on the fees and commissions for Marketplace items. ##### Regional Pricing for Avatar items Regional Pricing allows you to offer your item to users at region-specific prices. After you determine the item's global price, Roblox uses a variety of signals like a region's purchasing power, currency exchange rates, and local spending behavior to set the most appropriate price for the item region by region. Regional Pricing is opted-in by default. You can opt-out any time. For more information, see [Regional Pricing](/docs/en-us/production/monetization/regional-pricing.md). ### Sale location The **Sale Location** option sets where you can sell your asset beyond the Marketplace. In some cases, you may want to limit the availability of your asset to certain experiences, or only the Marketplace. ![Sale location fields.](../assets/publishing/marketplace/Sale-Location.png) Experiences that sell Marketplace items receive an additional [commission](/docs/en-us/marketplace/marketplace-fees-and-commissions.md) including transactions made through the [Inspect Menu](/docs/en-us/players/avatar-inspect-menu.md) or [through the API](/docs/en-us/players/avatar-editor.md) within an experience. The following location options are available for all items: | Location | Description | | --- | --- | | Marketplace and All Experiences | Users can purchase this item in the Marketplace or any experiences that offer Marketplace purchases. This is the default setting. | | Experience By Place ID (API Only) | Users can only purchase the original stock of this item in the experiences associated with the provided Place ID. Only one Place ID per experience is required and creators can update and change Place IDs at any time.

After saving a new Place ID, the experience owner must manually [enable the avatar item](/docs/en-us/production/monetization/avatar-items.md) for their experience. | ### Publish item When first publishing your asset, select the **Publish Item** button at the end of the form to pay the [publishing fee](/docs/en-us/marketplace/marketplace-fees-and-commissions.md#publishing-advance) and list your item on sale. ### On sale toggle ![On-sale toggle at top-right of page.](../assets/publishing/marketplace/Sale-Toggle.png) You can disable the sale of a published asset by disabling the **On Sale** at the top of the **Manage Item** page. After enabling an item for sale, you can take the item off-sale by disabling the toggle. A publishing fee is not required to re-enable. > **Warning:** To permanently archive your asset, see [Archive assets](/docs/en-us/marketplace/moderation.md#archive-assets). ### Schedule item sale To schedule the sale of your asset in the **Manage Item** page, click **Schedule Sale** under the **On Sale** toggle and enter a **Sale Start** date and an optional **Sale End** date. If you don't enter an end date, the item is on sale indefinitely. You can use scheduling to sell both Limited and Non-Limited assets. You can schedule sales up to 30 days in advance.
--- title: "Sponsored items" url: /docs/en-us/marketplace/sponsor-items last_updated: 2026-06-29T19:34:02Z description: "Sponsoring items allows you to increase the discoverability of 3D user-generated content within the Marketplace." --- # Sponsored items Sponsor items to increase the discoverability of your 3D user-generated content to users within the [Marketplace](https://www.roblox.com/catalog). You can choose your audience, schedule the duration of your ad, and specify your budget per day. Sponsored item ads cycles can run anywhere from 24 hours to 28 days. ## Bidding system Advertisement space works on a bidding system where the higher you set your **Daily Budget** amount in relation to other creators' bids, the more likely your sponsored item will display within the **Sponsored** category on any item's details page on the Roblox website or in the Roblox app. The location of each sponsored item within the category is random. For example, if there are three sponsored items in the bidding system, advertising space is split according to how much each user has bid: | User | Bid amount | Result | | --- | --- | --- | | A | 50 Robux | — | | B | 100 Robux | User B's sponsored item will display **twice** as often as User A's. | | C | 300 Robux | User C's sponsored item will display **six times** as often as User A's, and **three times** as often as User B's. | ## Create sponsored items Once you have at least **10 Robux** to bid on advertising space per day, you can create a new sponsored item for a specific audience, including gender, age, and platform. To create a new sponsored item: 1. Navigate to the [Creator Dashboard](https://create.roblox.com/dashboard/creations). 2. In left-hand navigation, under **Ads**, select **Sponsored Items**. 3. In the horizontal navigation, select **Items**. All filters update. 4. In the top-right corner, click the **Create Sponsored Ad** button. The **Sponsor This Item** page displays. 5. Click the **Select Item** dropdown menu, then select the item you want to sponsor. The **Icon** section displays the current version of your item's icon. 6. In the **Target Audience**, **Schedule**, and **Daily Budget** sections, choose who can see your sponsored item, how long you want to sponsor your item, and the amount of Robux you want to spend per day. 7. In the **Ad Name** section, enter a descriptive name for your sponsored item. This is only visible to you. 8. Click the **Preview Your Ad** button. A new page with a preview of your sponsored item and a sponsorship summary display. 9. Click the **Run** button. Roblox holds the full amount for your ad cycle as soon as you click the Run button. If you cancel the ad cycle, you will receive a refund for any amount per full day left of the ad cycle. ## View sponsored item statistics After you have created a sponsored item, you can view statistics related to the ad's performance. To view a sponsored item's statistics: 1. Navigate to the [Creator Dashboard](https://create.roblox.com/dashboard/creations). 2. In left-hand navigation, under **Ads**, select **Sponsored Items**. 3. In the horizontal navigation, select the **Items** tab. All filters update. 4. Click the **Select Item** dropdown menu, then select the sponsored item you want to view the statistics for. A high-level view of the statistics for this sponsored item displays. | Metric | Description | | --- | --- | | Attributed Purchases | The amount of people who purchase your item after they click on your ad. This is only recorded once per user. | | Clicks | Every time a person clicks on your ad. | | Conversion Rate | The amount of attributed purchases divided by impressions. | | CPC (Cost Per Click) | The total Robux spent divided by the number of clicks. | | CPP (Cost Per Purchase) | The total Robux spent divided by the number of attributed purchases. | | CTR (Click Through Rate) | Clicks divided by impressions. | | Impressions | Estimate for the number of times your ad was shown to a user when using the Roblox app or website, or when your ad was requested from servers. | | Status | A report if your ad is running or not. | | Spent | The amount of Robux you have spent on the ad. | ## Cancel sponsored items You can cancel a sponsored item at any time and receive a refund for any amount per full day left of the ad cycle. For example, if you set up a three day ad cycle and cancel on the first day, you will receive a refund for the remaining two days. To cancel a sponsored item: 1. Navigate to the [Creator Dashboard](https://create.roblox.com/dashboard/creations). 2. In left-hand navigation, under **Ads**, select **Sponsored Items**. 3. In the horizontal navigation, select the **Items** tab. All filters update. 4. Click the **Select Item** dropdown menu, then select the sponsored item you want to cancel. A high-level view of the statistics for this sponsored item displays. 5. Select the **⋯** button, then **Stop**. A warning pop-up dialog displays to confirm that you want to stop your ad. 6. Click the **Stop** button. --- title: "UGC validation system" url: /docs/en-us/marketplace/validation-system last_updated: 2026-06-29T19:34:02Z description: "Explains Roblox's technical mechanism that checks if avatar assets meet platform specifications before you can upload and publish them to the Marketplace." --- # UGC validation system The **UGC validation system** is Roblox's technical mechanism that checks if avatar assets meet platform specifications before you can upload and publish them to the Marketplace. This process ensures that avatar bodies, cosmetics, clothing, and accessories behave consistently across the platform. The UGC validation system runs each time you: - Try to upload an avatar asset to the Marketplace through Studio. - Call `Class.AvatarCreationService` APIs for in-experience avatar asset creation. During this process, the UGC validation system looks at your asset to see if it meets the requirements in validation categories that are specific to its asset type. For a comprehensive list of these requirements, see [Body specifications](/docs/en-us/avatar/character-bodies/specifications.md), [Dynamic head specifications](/docs/en-us/avatar/dynamic-heads/specifications.md), [Makeup specifications](/docs/en-us/avatar/makeup/specifications.md), [Rigid accessory specifications](/docs/en-us/avatar/rigid-accessories/specifications.md), and [Layered accessory specifications](/docs/en-us/avatar/layered-accessories/specifications.md). When an avatar asset passes validation, you can proceed with the Studio upload process by paying the upload fee, submitting the asset for moderation, and selling it on the Marketplace, or the player can proceed to purchase the avatar asset they created within an experience. ![Chart of validation system success workflows.](../assets/avatar/validation-system/Validation-Success.png) When an avatar asset fails validation, you or the player receive error messages with information on why the avatar asset didn't pass validation. For troubleshooting assistance, the UGC validation has visualization tools with checks that provide asset-specific visual guides on how to fix or improve your asset so that it can pass validation. > **Info:** Assets that you don't intend to use for the Marketplace, such as those for in-game use only, do **not** need to pass the validation process. Regardless, the validation process can still be useful for troubleshooting and debugging custom bodies and accessories. ## Validation categories The UGC validation system organizes validation into several high-level categories. For information on these categories and some areas that they validate, review the following table. | Category | What it validates | | --- | --- | | **Schema** | | | **Mesh geometry** | | | **Texture / materials** | | | **Rigging / skinning** | | | **Inner and outer cages** | | | **Attachments** | | | **Dynamic head** | | | **Security / moderation** | | ## Visualization checks The UGC validation system includes optional visualization tools with checks to help you identify, understand, diagnose, and fix avatar assets that aren't yet ready to be on the Marketplace. When active, the visualization checks provide information on validation errors for non-compliant assets, and quality assessments for compliant assets that could use improvement. _Red icons provide information on what areas are failing validation and yellow warnings indicate areas of improvement._ _When you select an error or warning, visual elements display in the viewport to help you understand the error or warning._ To access the UGC validation system's visualization tools, you must first enable the beta feature. To enable and use the optional visualization checks: 1. In Studio, navigate to **File** > **Beta Features**. 2. Enable **Visualizing UGC Validation**. 3. Restart Studio, then in the mezzanine, click the plus icon to add a custom tab. A new tab displays.![Button to add custom tab indicated in Studio's mezzanine.](../assets/studio/general/Toolbar-Add-Custom-Tab.png) 4. In your custom tab, click the **Add tools** button. The **Add Tools** pop-up window displays.![Button to add new tool to a custom tab in Studio's toolbar.](../assets/studio/general/Toolbar-Custom-Tab-Add-Tool.png) 5. Enable **UGC Validation** to add it to your toolbar, then close the window. 6. Navigate back to your custom tab and select the **UGC Validation** button to run the plugin. A **UGC Validation** window displays. 7. In the **Explorer** window, select a character body, dynamic head, rigid accessory, or layered accessory, then navigate back to the **UGC Validation** window and click the **Run validation** button. A pop-up window displays. 8. In the pop-up window: 1. Confirm the asset type of the avatar asset that you selected. For example, if you selected a `Enum.AccessoryType.Pants` layered accessory, use the drop-down menu to select **PantsAccessory**. 2. Click the **Run validation** button. The validation service will run in the background and report back results in the **UGC Validation** window as a combination of errors and/or warnings depending on the state of your asset. For information on each visual validation check, review the following subsections. ### Measure_Cage_Distance_Head `Measure_Cage_Distance_Head` is a validation check that scores how closely the avatar head's outer cage hugs the head mesh, checking that the cage neither floats too far from the mesh nor sinks inside it. | Error code | Severity | | --- | --- | | Quality score cannot be properly computed due to the input data being incorrect or incomplete. Please check your model or file a bug report. | Blocks validation | | Head cage intersects wit the head mesh. `{negativeSDFPercent}%` of head cage vertices are inside the head mesh. | Blocks validation | | Head cage is too far from the head mesh. Maximum distance between head cage and mesh is `{max_sdf}`, which is above allowed threshold `{max_sdf_threshold}` | Blocks validation | | Asset quality warning: Head cage distance score is `{score}`. Max distance between head cage and mesh is `{max_distance}`, `{negative_sdf_percent}%` of head cage vertices are inside the head mesh. | Warning | ### Measure_Cage_Mesh_Distance `Measure_Cage_Mesh_Distance` is a validation check that scores how closely the layered clothing's outer cage tracks the rendered mesh, flagging when a cage drifts too far away or pushes inside the mesh. | Error code | Severity | | --- | --- | | Quality score cannot be properly computed due to the input data being incorrect or incomplete. Please check your model or file a bug report. | Blocks validation | | Outer cage intersects with the render mesh. `{negativeSDFPercent}%` of outer cage vertices are inside render mesh. | Blocks validation | | Outer cage is too far from the render mesh. Maximum distance between outer cage and mesh is `{max_sdf}`, which is above allowed threshold `{max_sdf_threshold}`. | Blocks validation | | Asset quality warning: Cage-to-mesh distance score is `{score}`. Max distance between outer cage and mesh is `{max_distance}`, `{negative_sdf_percent}%` of outer cage vertices are inside the render mesh. | Warning | ### Measure_Cage_Mesh_Distance_Avatar `Measure_Cage_Mesh_Distance_Avatar` is a validation check that scores how closely the full body outer cage follows the body mesh, flagging when a cage floats too far off the surface or intersects it. | Error code | Severity | | --- | --- | | Quality score cannot be properly computed due to the input data being incorrect or incomplete. Please check your model or file a bug report. | Blocks validation | | Body cage intersects with the body mesh. `{negativeSDFPercent}%` of body cage vertices are inside the body mesh. | Blocks validation | | Body cage is too far from the body mesh. Maximum distance between body cage and mesh is `{max_sdf}`, which is above allowed threshold `{max_sdf_threshold}`. | Blocks validation | | Asset quality warning: Body cage distance score is `{score}`. Max distance between body cage and mesh is `{max_distance}`, `{negative_sdf_percent}%` of body cage vertices are inside the body mesh. | Warning | ### Measure_Cage_Relevancy `Measure_Cage_Relevancy` is a validation check that scores how much of the outer cage you moved actually sits over the accessory, so cage edits stay where the garment needs them. | Error code | Severity | | --- | --- | | Quality score cannot be properly computed due to the input data being incorrect or incomplete. Please check your model or file a bug report. | Blocks validation | | `{outer_cage_face_irrelevant_percent}%` of the modified outer cage vertices do not cover the accessory. Make sure you are moving the outer cage only where needed. | Blocks validation | | Asset quality warning: Cage relevancy score is `{score}`. `{irrelevant_percent}%` of the modified outer cage vertices do not cover the accessory. | Warning | ### Measure_Cage_UV `Measure_Cage_UV` is a validation check that checks if the inner and outer cage UVs match the layered clothing template; UVs that don't match the layered clothing template break how the asset wraps over the body. | Error code | Severity | | --- | --- | | Quality score cannot be properly computed due to the input data being incorrect or incomplete. Please check your model or file a bug report. | Blocks validation | | There are `{incorrect_uv_count}` UV values in cage `{cage_name}` that are incorrect or do not belong to the template. Please correct the cage UV. | Blocks validation | | Asset quality warning: Cage UV score is `{score}` for `{cage_name}`. Incorrect UV count: `{incorrect_uv_count}`. | Warning | ### Measure_Cage_UV_Avatar `Measure_Cage_UV_Avatar` is a validation check that checks that each [body part](/docs/en-us/avatar/character-bodies/specifications.md#body-parts) has a cage that keeps its UVs on the standard template; wrong cage UVs distort how layered clothing wraps over the body. | Error code | Severity | | --- | --- | | Quality score cannot be properly computed due to the input data being incorrect or incomplete. Please check your model or file a bug report. | Blocks validation | | There are `{incorrect_uv_count}` UV values in cage `{cage_name}` that are incorrect or do not belong to the template. Please correct the cage UV. | Blocks validation | | Asset quality warning: Cage UV score is `{score}` for `{cage_name}`. Incorrect UV count: `{incorrect_uv_count}`. | Warning | ### Measure_Degen_Triangles `Measure_Degen_Triangles` is a validation check that scores each mesh part for degenerate triangles (faces with zero or near-zero area); degenerate triangles add no surface and can break rendering. | Error code | Severity | | --- | --- | | Asset quality: `{partName}` has `{degenerate_triangle_percent}%` degenerate triangles `(score: {score})`. | Warning | | Asset quality score for `{measureName}` could not be computed because the input data was incomplete or invalid. This does not block your upload. | Warning | ### Measure_Dynamic_Head `Measure_Dynamic_Head` is a validation check that verifies if a [dynamic head](/docs/en-us/avatar/dynamic-heads.md) is animatable. Dynamic heads that are animatable have a head cage, FACS data, aligned landmarks, and can close their eyes, open their mouth, and express emotions. | Error code | Severity | | --- | --- | | Quality score cannot be properly computed due to the input data being incorrect or incomplete. Please check your model or file a bug report. | Blocks validation | | Dynamic heads on the Marketplace must be properly caged so that the eyes and mouth vertices on the cage line up with the mesh vertices. Please improve your caging and its alignment with the mesh. | Blocks validation | | Cannot detect smile expression for the Dynamic head. Please adjust cage landmarks for the mouth to match where the mouth is on the head and make sure it can show a smile expression. | Blocks validation | | Cannot detect left eye close expression for the dynamic head. Please adjust cage landmarks for the left eye to match where the left eye is on the head and make sure it can be closed. | Blocks validation | | Dynamic heads on the Marketplace have no head cages. Please create a head cage for the dynamic head. | Blocks validation | | Cannot detect mouth open expression for the dynamic head. Please adjust cage landmarks for mouth to match where the mouth is on the head and make sure it can open. | Blocks validation | | Dynamic heads on the Marketplace must be animated, but this head has no FACS data. | Blocks validation | | Cannot detect right eye close expression for the dynamic head. Please adjust cage landmarks for the right eye to match where the right eye is on the head and make sure it can be closed. | Blocks validation | | Cannot detect frown expression for the dynamic head. Please adjust cage landmarks for the mouth to match where the mouth is on the head and make sure it can show a frown expression. | Blocks validation | | Asset quality warning: dynamic head expression score is `{score}`. | Warning | ### Measure_Joint_Number `Measure_Joint_Number` is a validation check that scores the body rig's [joint setup](/docs/en-us/avatar/character-bodies/specifications.md#rigging) against the expected configuration. The body rig's joint setup includes the joint counts in the left and right hands for [higher-fidelity](/docs/en-us/avatar/character-bodies/specifications.md#higher-fidelity-rigs) rigs. | Error code | Severity | | --- | --- | | Asset quality: joint configuration scored `{score} (joints: {joint_number}, left hand: {left_hand_joint_number}, right hand: {right_hand_joint_number})`. | Warning | | Asset quality score for `{measureName}` could not be computed because the input data was incomplete or invalid. This does not block your upload. | Warning | ### Measure_Mesh_Manifold `Measure_Mesh_Manifold` is a validation check that scores each mesh part for manifold integrity, catching holes, non-manifold edges, and similar topology problems in the geometry. | Error code | Severity | | --- | --- | | Asset quality: `{partName}` has mesh manifold issues `(score: {score})`. | Warning | | Asset quality score for `{measureName}` could not be computed because the input data was incomplete or invalid. This does not block your upload. | Warning | ### Measure_Mesh_Outside_OuterCage `Measure_Mesh_Outside_OuterCage` is a validation check that scores how much of an accessory's render mesh stays between its cages; a mesh that pokes outside the outer cage deforms badly when worn. | Error code | Severity | | --- | --- | | Quality score cannot be properly computed due to the input data being incorrect or incomplete. Please check your model or file a bug report. | Blocks validation | | `{mesh_outside_outer_cage_percent}%` of mesh vertices for the accessory are outside its outer cage. Make adjustments to have more of the accessory placed between the cages. | Blocks validation | | Asset quality warning: Mesh containment score is `{score}`. `{mesh_outside_outer_cage_percent}%` of mesh vertices are outside the outer cage. | Warning | ### Measure_Texture_Complexity `Measure_Texture_Complexity` is a validation check that scores each texture for visual complexity, flagging flat or near-empty textures that add little detail to the asset. | Error code | Severity | | --- | --- | | Asset quality score for `{measureName}` could not be computed because the input data was incomplete or invalid. This does not block your upload. | Warning | | Asset quality: texture `{textureName}` has low complexity `(score: {score})`. | Warning | ### Measure_Texture_Resolution `Measure_Texture_Resolution` is a validation check that scores each texture by its pixel dimensions, rewarding resolution high enough to keep the asset sharp on the body. | Error code | Severity | | --- | --- | | Asset quality score for `{measureName}` could not be computed because the input data was incomplete or invalid. This does not block your upload. | Warning | | Asset quality: texture `{textureName}` has resolution `{texture_width}x{texture_height} (score: {score})`. | Warning | ### Measure_Triangle_Intersection `Measure_Triangle_Intersection` is a validation check that scores each mesh part for self-intersecting triangles in which faces pass through one another and create messy, overlapping geometry. | Error code | Severity | | --- | --- | | Asset quality score for `{measureName}` could not be computed because the input data was incomplete or invalid. This does not block your upload. | Warning | | Asset quality: `{partName}` has `{intersecting_tri_face_percent}%` intersecting triangle faces `(score: {score})`. | Warning | ### Measure_UV_Bound `Measure_UV_Bound` is a validation check that measures how many of a mesh's UV vertices fall outside the 0-1 texture space; it reports a score but never blocks upload. | Error code | Severity | | --- | --- | | Asset quality score for `{measureName}` could not be computed because the input data was incomplete or invalid. This does not block your upload. | Warning | | Asset quality: `{partName}` has `{uv_outside_bound_vert_percent}%` UV vertices outside bounds `(score: {score})`. | Warning | ### Measure_Vertex_Similarity `Measure_Vertex_Similarity` is a validation check that measures the share of near-duplicate vertices in a mesh; it reports a score but never blocks upload. | Error code | Severity | | --- | --- | | Asset quality score for `{measureName}` could not be computed because the input data was incomplete or invalid. This does not block your upload. | Warning | | Asset quality: `{partName}` has `{similar_vertex_percent}%` similar vertices `(score: {score})`. | Warning | --- title: "Matchmaking analytics" url: /docs/en-us/matchmaking/analytics last_updated: 2026-06-29T19:34:02Z description: "Use matchmaking analytics to analyze your matchmaking configurations." --- # Matchmaking analytics Use the matchmaking analytics dashboard metrics to analyze the impact of individual matchmaking configurations and monitor signal performance over different periods of time. ## Access the dashboard To access the matchmaking analytics dashboard: 1. In the **Creator Dashboard**, navigate to **Creations**, then select an experience. 2. In the lefthand navigation, navigate to the **Configure** section, then select **Custom Matchmaking**. 3. In the horizontal navigation, select the **Analytics** tab. ## See metrics for a specific place The analytics dashboard includes charts for Roblox signals and custom signals. If a matchmaking configuration has one numerical custom signal and one categorical custom signal, data is populated in both the numerical and categorical signal charts. If a configuration has two of the same type of custom signal (as in, two numerical or two categorical), only one of the charts is populated with two lines representing data for both. Categorical charts represent a similarity ratio, while numerical charts represent the difference. To see matchmaking metrics for a specific place in your experience: 1. In the **Analytics** tab, click the **Date Range** dropdown and select a date range between the previous day and the last 90 days. 2. Click the **Place** dropdown and select the place you want to see metrics for. The **Current Configuration** updates with the name of the matchmaking configuration applied to the place you selected. ![Matchmaking analytics charts for several different ratios.](../assets/matchmaking/MatchmakingAnalytics.png) If the selected place doesn't have a custom matchmaking configuration applied to it, the dashboard shows metrics for the default Roblox matchmaking configuration. ## Filter by percentile values In the **Analytics** tab, select the **Filter By** menu to swap signal charts between averages and the following percentile values: - **P90** to filter by the number of values that fall within the 90th percentile. - **P10** to filter by the number of values that fall within the 10th percentile. - **P50** to filter by the median, with half of all values falling above it and half below it. The filter applies to all signal charts. > **Info:** Percentile values are calculated smallest to largest, so for metrics where lower numbers are better, P10 represents the "best case" and P90 the "worst case" rather than the other way around. ## Signal charts The **Analytics** tab includes the following signal charts: | **Chart** | **Description** | | --- | --- | | **Occupancy Ratio** | The average/P10/P50/P90 occupancy ratio of the chosen server. | | **Average for Preferred Player Match Ratio** | The average percentage of matchmaking requests that match the joining player to a server with preferred players. | | **Age Difference** | The average/P10/P50/P90 value of the age difference between the joining player and the mean age of all players in the chosen server. | | **Common Language Ratio** | The average/P10/P50/P90 common language ratio of the chosen server. The common language is the language shared between existing players in the server and the joining player. | | **Estimate Ping** | The average/P10/P50/P90 estimate ping value of the chosen server. | | **Voice Chat Ratio** | The average/P10/P50/P90 occupancy ratio of players with voice chat enabled in the chosen server. | | **Common Device Ratio** | The average/P10/P50/P90 common device ratio of the chosen server. The common device is the device type shared between existing players in the server and the joining player. | | **Play History Difference** | The average/P10/P50/P90 value of the play history difference between the joining player and the mean play history of all players in the chosen server. | | **Numerical Custom Signal Difference** | The average/P10/P50/P90 value of the numerical signal difference between the joining player and the server average. | | **Categorical Custom Signal Similarity Ratio** | The average/P10/P50/P90 categorical signal similarity ratio between the joining player and the server average. | --- title: "Attributes and signals" url: /docs/en-us/matchmaking/attributes-and-signals last_updated: 2026-06-29T19:34:02Z description: "Attributes and signals define the player and server data used in matchmaking, including both Roblox-provided signals and custom signals you can create." --- # Attributes and signals ## Existing attributes The default Roblox matchmaking configuration uses signals based on existing attributes like player location, age group, and latency. To use data for matchmaking that Roblox doesn't automatically include in its default configuration, you need to create [custom attributes](#custom-attributes) and [custom signals](#custom-signals). | **Attribute** | **Type** | **Description** | | --- | --- | --- | | **Estimated Player Latency** | Numerical | The estimated latency of a player in a server. | | **Has Friends** | Categorical | Whether a server has a friend or another player of the same IP address as the joining player. | | **Is Voice Chat Enabled** | Categorical | Whether a player has voice chat enabled. | | **Player Age** | Numerical | The player’s age. | | **Player Device Type** | Categorical | The player’s device type. Can be a mobile device, a computer, a tablet, a console, or a VR device. | | **Player Language** | Categorical | The player’s language. | | **Player Play History** | Numerical | The log-10 number of minutes a player has played in a universe in the past 28 days. | | **Server Occupancy** | Numerical | The number of players in a server. | | **Player Text Chat Group** | Categorical | The player's text chat group where they can text chat together. | ## Existing signals The following are Roblox-defined signals derived based on Roblox attributes: | **Signal** | **Description** | | --- | --- | | [**Age**](#age) | The difference between the average age of players in the server and the joining player’s age, with a maximum relevant difference of 25. | | [**Device Type**](#device-type) | The ratio of players in the server with the same device type as the joining player. | | [**Friends**](#friends) | The number of people in the server who are friends with the joining player or who share an IP address with the joining player. 1 if there is a preferred player, 0 otherwise. | | [**Latency**](#latency) | The estimated player latency for a server, with a max relevant value of 250. | | [**Language**](#language) | The ratio of players in the server with the same language as the joining player | | [**Occupancy**](#occupancy) | The ratio of players in the server compared to the capacity of the server. | | [**Play History**](#play-history) | The difference between the average play history in the server and the joining player’s play history, with a maximum relevant difference of 4.6. | | [**Voice Chat**](#voice-chat) | The ratio of players in the server with voice chat enabled. | | [**Text Chat**](#text-chat) | The ratio of players in the server who can text chat with the joining player together. | ### Age A numerical signal that compares the average ages of players on a server to the joining player's age. This signal has a max relevant difference of 25. The signal score is inversely related to the age difference, meaning lower age differences have higher scores. ageDifferenceSignalScore = 1 − min ⁡ ( 25, ageDifference) / 25 \text{ageDifferenceSignalScore} = 1 - \min(25, \text{ageDifference}) / 25ageDifferenceSignalScore=1−min(25,ageDifference)/25 where ageDifference = ∣ avgServerAge − joiningPlayerAge ∣ \text{ageDifference} = |\text{avgServerAge} - \text{joiningPlayerAge}|ageDifference=∣avgServerAge−joiningPlayerAge∣ ### Device Type A categorical signal that measures the ratio of players on the server with the same device type as the joining player. Device types include: Computer, mobile device, tablet, console, and VR device. deviceTypeSignalScore = # players with same device as joining player / # players on the server \text{deviceTypeSignalScore} = \text{\# players with same device as joining player} / \text{\# players on the server}deviceTypeSignalScore=# players with same device as joining player/# players on the server ### Friends A preferred player is a player who is either friends with the joining player or who shares the same IP address as the joining player. The Friends signal is a categorical signal with a score of 1 when there is a preferred player in the server and a score of 0 when there are no preferred players on the server. friendsSignalScore = hasFriends ?   1: 0 \text{friendsSignalScore} = \text{hasFriends} \ {?} \ 1 : 0friendsSignalScore=hasFriends ? 1:0 The Friends signal can also be considered a numerical signal with a maximum relevant value of 1. friendsSignalScore = min ⁡ ( # preferred players in server, 1) / 1 \text{friendsSignalScore} = \min(\text{\# preferred players in server}, 1) / 1friendsSignalScore=min(# preferred players in server,1)/1 ### Language A categorical signal that measures the ratio of players on the server who share the same language setting as the joining player. languageSignalScore = # players with same language setting as joining player / # players on the server \text{languageSignalScore} = \text{\# players with same language setting as joining player} / \text{\# players on the server}languageSignalScore=# players with same language setting as joining player/# players on the server ### Latency A numerical signal that measures the estimated ping time in milliseconds of the joining player if they were to play on a server. This signal has a max relevant value of 250 milliseconds. The signal score is inversely related to the ping, meaning lower ping values have higher scores. latencySignalScore = 1 − min ⁡ ( 250, estimatedPingMs) / 250 \text{latencySignalScore} = 1 - \min(250, \text{estimatedPingMs}) / 250latencySignalScore=1−min(250,estimatedPingMs)/250 ### Occupancy A numerical signal that measures the ratio of players on the server to the capacity of the server. occupancySignalScore = # players in server / serverCapacity \text{occupancySignalScore} = \text{\# players in server} / \text{serverCapacity}occupancySignalScore=# players in server/serverCapacity ### Play History The Play History attribute value is the log-10 number of minutes a player has played in a universe in the past 28 days. This numerical signal compares the average log-10 Play History value of players in the server to the joining player's Play History value. This signal has a max relevant difference of 4.6. The signal score is inversely related to the play history difference, meaning lower play history differences have higher scores. playHistorySignalScore = 1 − min ⁡ ( 4.6, playHistoryDifference / 4.6) \text{playHistorySignalScore} = 1 - \min(4.6, \text{playHistoryDifference} / 4.6)playHistorySignalScore=1−min(4.6,playHistoryDifference/4.6), where playHistoryDifference = ∣ avgServerPlayHistory − joiningPlayerPlayHistory ∣ \text{playHistoryDifference} = |\text{avgServerPlayHistory} - \text{joiningPlayerPlayHistory}|playHistoryDifference=∣avgServerPlayHistory−joiningPlayerPlayHistory∣ ### Voice Chat A player can have voice chat enabled or disabled. The Voice Chat signal is a categorical signal that measures the ratio of players with the same voice chat setting as the joining player to the number of players in the server. If a place has voice chat disabled, the Voice Chat signal's weight is 0. voiceChatSignalScore = # players with same voice chat setting as joining player / # players on the server \text{voiceChatSignalScore} = \text{\# players with same voice chat setting as joining player} / \text{\# players on the server}voiceChatSignalScore=# players with same voice chat setting as joining player/# players on the server ### Text Chat A categorical signal that measures the ratio of players on the server who can text chat with the joining player together in common. textChatSignalScore = # players can text with the joining player in common  / # players on the server \text{textChatSignalScore} = \text{\# players can text with the joining player in common } / \text{\# players on the server}textChatSignalScore=# players can text with the joining player in common /# players on the server ## Custom attributes Custom attributes give custom signals access to player and server data. For more information about creating your own custom attributes, see [Create a custom attribute](/docs/en-us/matchmaking/customize-matchmaking.md#create-a-custom-attribute). For more information about existing Roblox attributes, see [Existing attributes](#existing-attributes). | **Attribute** | **Characteristics** | | --- | --- | | **Player** | Persistent

Custom player data that persists in data stores, like a player's level or score inside the experience

Managed using the `Class.DataStore\|DataStore` APIs | | **Server** | Not persistent and only last as long as the server is active

Server-specific data like the server level or game mode

Managed using the `Class.MatchmakingService\|MatchmakingService` API | ```lua local DataStoreService = game:GetService("DataStoreService") local eloStore = DataStoreService:GetDataStore("PlayerElo") function onMatchEnded(players: {Player}, winners: {Player}, losers: {Player}) for _, player in players do local updatedElo = CalculateUpdatedElo(player, winners, losers) local success, errorMessage = pcall(function() eloStore:SetAsync(player.UserId, updatedElo) end) end end ``` ```lua local MatchmakingService = game:GetService("MatchmakingService") local RunService = game:GetService("RunService") if RunService:IsStudio() then -- Sets up initial attributes and schema for testing MatchmakingService:InitializeServerAttributesForStudio({ Level = "Advanced", Elo = 123.456, TrainingMode = true }) end -- Retrieves the Level attribute local currentLevel, errorMessage = MatchmakingService:GetServerAttribute("Level") if errorMessage then warn(errorMessage) else print("Current level: " .. currentLevel) end -- Updates the Level attribute value to Advanced local success, errorMessage = MatchmakingService:SetServerAttribute("Level", "Advanced") if not success then warn("Failed to update server attribute [Level] to [Advanced] due to error: " .. errorMessage) else print("Successfully set [Level] to [Advanced]") end ``` ## Custom signals Custom signals are created and defined by you and can be numerical or categorical: - Numerical signals are numbers. They compare the difference between the joining player's attribute and the server's aggregated value, with larger differences lowering or increasing the score. For example, the closer the skill level of a player is to the average skill level of the server, the higher the numerical signal's score is. This score is then multiplied by the signal's weight. - Categorical signals are strings or booleans. They're based on how common the joining player's attribute is when compared to the other players in the server. For example, if a high percentage of the players inside a server have the same preferred language as the joining player, the score increases. This score is then also multiplied by the signal's weight. For more information about creating your own custom signals, see [Create a custom signal](/docs/en-us/matchmaking/customize-matchmaking.md#create-a-custom-signal). For more information about existing Roblox signals, see [Existing signals](#existing-signals). ### Player numerical ##### Joining player Minimizes the difference between the server's average player attribute and the joining player's attribute. Differences beyond 1000 return a signal score of 0. ```lua local diff = math.abs(server_{aggregation_function}_{attribute_name} - joining_player_{attribute_name}) local score = 1 - math.min(diff / max_relevant_difference, 1) return score ``` ```lua local server_average_Elo = 2000 local joining_player_Elo = 1000 local max_relevant_difference = 1500 local diff = math.abs(server_average_Elo - joining_player_Elo) local score = 1 - math.min(diff / max_relevant_difference, 1) return score ``` ##### Constant value Minimizes the difference between the server's average player attribute and a constant value of 500. Differences beyond 1000 return a signal score of 0. ```lua local diff = math.abs(server_sum_{attribute_name} + joining_player_{attribute_name} - constant_value) local score = 1 - math.min(diff / max_relevant_difference, 1) return score ``` ```lua local server_sum_PowerLevel = 4500 local joining_player_PowerLevel = 9901 local max_relevant_difference = 1500 local constant_value = 5000 local diff = math.abs(server_sum_PowerLevel + joining_player_PowerLevel - constant_value) local score = 1 - math.min(diff / max_relevant_difference, 1) ``` ### Player categorical ##### Clustering Maximizes the ratio of players in the server who have the same attribute as the joining player's attribute. ```lua local score = num_players_same_{attribute_name} / occupancy return score ``` ```lua local num_players_same_Guild = 15 local occupancy = 19 local score = num_players_same_Guild / occupancy return score ``` ##### Diversifying Maximizes the ratio of players in the server who have a different attribute than the joining player's attribute. ```lua local score = num_players_same_{attribute_name} / occupancy return 1 - score ``` ```lua local num_players_same_RpgClass = 15 local occupancy = 19 local score = num_players_same_RpgClass / occupancy return score ``` ### Server numerical ##### Joining player Minimizes the difference between the server's attribute and the joining player's attribute. Differences beyond 1000 return a signal score of 0. ```lua local diff = math.abs(server_{attribute_name} - joining_player_{attribute_name}) local score = 1 - math.min(diff / max_relevant_difference, 1) return score ``` ```lua local server_Level = 4500 local joining_player_Level = 9000 local max_relevant_difference = 1500 local diff = math.abs(server_Level - joining_player_Level) local score = 1 - math.min(diff / max_relevant_difference, 1) ``` ##### Constant value Minimizes the difference between the server's attribute value and a constant value. Differences beyond this constant value return a signal score of 0. ```lua local diff = math.abs(server_{attribute_name} - constant_value) local score = math.min(diff / max_relevant_difference, 1) return score ``` ```lua local server_GameTime = 500 local max_relevant_difference = 1000 local constant_value = 1000 local diff = math.abs(server_GameTime - constant_value) local score = math.min(diff / max_relevant_difference, 1) ``` ### Server categorical ##### Compare to joining player The score is 1 when the server's attribute value (for example, Game Mode) is equal to the player's attribute value (for example, Preferred Game Mode). Otherwise, the signal score is 0. ```lua if server_{attribute_name} == joining_player_{attribute_name} then return 1 else return 0 end ``` ```lua local server_GameMode = "Survival" local joining_player_GameMode = "Survival" if server_GameMode == joining_player_GameMode then return 1 else return 0 end ``` ##### Compare to constant value The score is 1 when the server's attribute value is equal to a constant value of true. Otherwise, the signal score is 0. ```lua if server_{attribute_name} == constant_value then return 1 else return 0 end ``` ```lua local server_GameNotStarted = true if server_GameNotStarted == true then return 1 else return 0 end ```
--- title: "Customize your matchmaking configuration" url: /docs/en-us/matchmaking/customize-matchmaking last_updated: 2026-06-29T19:34:03Z description: "Use custom matchmaking to match players based on attributes like their skill level, their age and language, and their gameplay preferences." --- # Customize your matchmaking configuration With a custom matchmaking configuration, you can build competitive, cooperative, and unique experiences by matching players to servers based on characteristics like skill level, latency, and gameplay preferences. By default, Roblox provides you with a matchmaking configuration that uses predefined Roblox signals. To customize matchmaking, you can create new matchmaking configurations by [adjusting the weights of existing Roblox signals](#custom-configuration-with-roblox-signals), [creating new custom signals](#custom-configuration-with-custom-signals), or both. You can then preview your configuration by [previewing server scores with mock servers](#preview-and-test-server-scores). ## Custom configuration with Roblox signals You can adjust the weights of existing Roblox signals to customize your experience's matchmaking. For more information about Roblox signals and how their formulas are calculated, see [Existing signals](/docs/en-us/matchmaking/attributes-and-signals.md#existing-signals). To create a custom matchmaking configuration using only Roblox signals: 1. In the **Creator Dashboard**, navigate to **Creations**, then select an experience. 2. In the lefthand navigation, navigate to the **Configure** section, then select **Custom Matchmaking**. 3. Select the **Configuration** tab and click **Create Configuration**. 4. In the **Create a Custom Matchmaking Configuration** page, enter a name for your configuration. 5. Change the weights of the Roblox signals you want to update. 6. Click **Save Configuration**. 7. Select the places you want to apply this configuration to. 8. Click **Save Configuration** again. The new matchmaking configuration shows up in the **Configurations** list of the **Configuration** tab. ![The Configurations list of the Configuration tab.](../assets/matchmaking/CustomConfiguration_List.png) ## Custom configuration with custom signals To create a custom matchmaking configuration using custom signals: 1. In the **Creator Dashboard**, navigate to **Creations**, then select an experience. 2. In the lefthand navigation, navigate to the **Configure** section, then select **Custom Matchmaking**. 3. Select the **Configuration** tab and click **Create Configuration**. 4. In the **Create a Custom Matchmaking Configuration** page, enter a name for your configuration. 5. Click **Add Custom Signal** and [create a custom signal](#create-a-custom-signal). 6. Change the weights of the signals you want to update. 7. Click **Save Configuration**. 8. Select the places you want to apply this configuration to. 9. Click **Save Configuration** again. The new matchmaking configuration shows up in the **Configurations** list of the **Configuration** tab. ### Create a custom signal > **Info:** You can create a total of 2 custom signals per experience. > **Info:** Custom signals use custom attributes to access player and server data. In order to create a custom signal, you must first create a [custom attribute](#create-a-custom-attribute). Custom signals can be player numerical, server numerical, player categorical, or server categorical. For more information about custom signals and how their formulas are calculated, see [Custom signals](/docs/en-us/matchmaking/attributes-and-signals.md#custom-signals). To create a custom signal: 1. In the **Create a Custom Matchmaking Configuration** page, click **Add Custom Signal**. 2. Under **Signal Details**, enter a signal name and description. 3. Under **Signal Configuration**: - For a **player numerical attribute**: 1. Choose the [attribute](#create-a-custom-attribute) you want to reference. 2. Select how you want to aggregate the attribute in each server. - **Average**: The average of all players' attribute values in the server. Minimizes the difference between the server's average player attribute and the joining player's attribute. - **Median**: The median of all players' attribute values in the server. Minimizes the difference between the server's median player attribute and the joining player's attribute. - **Sum**: The sum of all players' attribute values in the server, including the joining player's. Minimizes the difference between the total attribute value of the players in the server, including the joining player, and a constant value. 3. Adjust the values under the aggregation. - For a **server numerical attribute**: 1. Choose the [attribute](#create-a-custom-attribute) you want to reference. 2. Adjust the values under the attribute. - For a **player categorical attribute**: 1. Choose the [attribute](#create-a-custom-attribute) you want to reference. 2. Select how you want to distribute the attribute value across servers: - **Cluster**: Groups players with the same attribute value. Maximizes the ratio of players in the server who have the same attribute value as the joining player. - **Diversify**: Groups players with different attribute values. Maximizes the ratio of players in the server who have a different attribute value from the joining player. - For a **server categorical attribute**: 1. Choose the [attribute](#create-a-custom-attribute) you want to reference. 2. Adjust the values under the attribute. 4. **(Optional)** Expand **Preview and Test Signal** to preview the signal score. You can adjust the **Test Signal** values to optimize the preview result.![A preview of the signal score in Preview and Test Signal.](../assets/matchmaking/Preview_Custom_Signal.png) 5. Click **Create Signal**. The new custom signal shows up in the **Configuration** list of the **Create a Custom Matchmaking Configuration** page. ### Create a custom attribute > **Info:** You can create a total of 5 server attributes and 5 player attributes per experience. Attributes are properties associated with players and servers, like skill level and game mode preferences. If you want to create and use custom signals, you must first create custom attributes. You can create player or server attributes. For more information about the difference between player and server attributes, see [Custom attributes](/docs/en-us/matchmaking/attributes-and-signals.md#custom-attributes). ##### Player attributes To create a player attribute: 1. In the **Custom Matchmaking** page, select the **Attributes** tab and click **Create Attribute**. 2. In the **Create Attribute** page, select **Player Attribute** and enter a unique name for it. 3. Choose a **Data Type** and a **Default Value** for the attribute. 4. Under **Data Store Settings**, select the data store that the attribute data lives in. 5. Enter a **Data Store Key Template** to store the player attribute values. At runtime, the `{UserId}` is replaced by the player's actual user ID. 6. Enter a **Data Store Value Path** so that matchmaking can locate the player attribute value inside a JSON object in the data store you selected. 7. Enter a **Data Store Scope** to specify the scope. If you don't enter a scope, it defaults to global. 8. Click **Save Changes** to save your player attribute. ![The attribute characteristics and data store settings for the creation of a player attribute.](../assets/matchmaking/Player_Attribute.png) The new attribute shows up in the **Player Attributes** list of the **Attributes** tab. ##### Server attributes To create a server attribute: 1. In the **Custom Matchmaking** page, select the **Attributes** tab and click **Create Attribute**. 2. In the **Create Attribute** page, select **Server Attribute** and enter a unique name for it. 3. Choose a **Data Type** and a **Default Value** for the attribute. 4. Click **Save Changes** to save your server attribute. ![The attribute characteristics for the creation of a server attribute.](../assets/matchmaking/Server_Attribute.png) The new attribute shows up in the **Server Attributes** list of the **Attributes** tab. ## Preview and test server scores > **Warning:** Preview server scores are just examples and don't reflect actual experience servers. Preview and test server scoring to see how your configured signal weights impact server selection. You can experiment with different weight combinations to optimize your matchmaking configuration. When you create a preview, the preview mocks server scores and identifies which mock server has the highest total score and is the best match for the player. To preview and test, click **Preview and Test** in the **Create a Custom Matchmaking Configuration** page. The preview shows you three mock servers with different scores and highlights the winning server. To see details about the formulas used for scoring, hover over each signal score result for each mock server. For more information about scoring and weights, see [Scoring](/docs/en-us/matchmaking/scoring.md). ## Apply a configuration to places You can apply a matchmaking configuration to as many places as you want inside your experience. To apply a configuration to places: 1. In the **Custom Matchmaking** page, click **Add to Place**. 2. Select a configuration and the place or places you want to apply it to. 3. Click **Save Configuration**. The places you selected show up in the **Applied Places** list of the **Configuration** tab. You can also add a configuration to places during the creation of that matchmaking configuration. --- title: "Matchmaking glossary" url: /docs/en-us/matchmaking/glossary last_updated: 2026-06-29T19:34:03Z description: "Glossary of matchmaking terms." --- # Matchmaking glossary | **General terms** | | | --- | --- | | **Joining player** | The player being matched to a server in your experience. | | **Common device** | The device type shared between existing players in the server and the joining player. Can be a mobile device, a desktop, a laptop, a tablet, or a console. | | **Common language** | The language shared between existing players in the server and the joining player. | | **Scoring** | | | **Server** | An RCC instance hosting a place. | | **Signal weight** | A non-negative number that describes the importance of a signal relative to other signals. | | **Weighted sum** | The sum of all weighted signal scores for a server. The basis for picking the best server for a player. | | **Server score** | The final matchmaking score for a server, based on the weighted sum of its signals. Players are matched to the server with the highest score. | | **Weighted signal score** | The value after multiplying the signal score by its weight. | | **Signals** | | | **Signal** | The processed version of an attribute. A signal is created when an attribute is transformed to fit the matchmaking scoring system. | | **Signal score** | The value of a signal after transformations, ranging between 0 and 1. | | **Weight** | Describes the importance of a signal in the scoring process. Signals with higher weights have a bigger impact in matchmaking. | | **Weighted signal score** | The result of multiplying a signal's score by its weight. Reflects both the value and importance of the signal. | | **Numerical custom signal** | The difference between the joining player's attribute and the server's average. | | **Categorical custom signal** | How common the joining player's attribute is when compared to the other players in the server. | | **Attributes** | | | **Attribute** | A property used in matchmaking scoring. Represents “raw signal values” before they're processed.

Can be a number or a string. | | **Custom attribute** | A universe-level property defined by creators. Can be a **player attribute** or a **server attribute**. | | **Player attribute** | A characteristic of a player, like their preferred game mode or skill level. | | **Server attribute** | A characteristic of a server, like the server's response time or active game mode. |
--- title: "Matchmaking" url: /docs/en-us/matchmaking last_updated: 2026-06-29T19:34:03Z description: "Match players to servers inside your experience." --- # Matchmaking Matchmaking is the process of matching players to servers based on player characteristics like age, language, skill level, and other gameplay preferences. ## How matchmaking works When a player tries to join your experience, the matchmaking service finds all eligible servers that the player can join, scores these servers using signals, and matches the player to the server with the highest score. The matchmaking flow works like this: 1. A player requests to join a place. 2. Matchmaking finds eligible servers that the player can join, filtering out servers that are full, private, reserved, or about to be shut down. 3. Matchmaking scores all eligible servers to determine the most compatible server for the player. 4. Matchmaking matches the player to the server with the highest score. 5. The player joins the winning server. _Roblox default matchmaking_![The default Roblox matchmaking flow.](../assets/matchmaking/Default_Matchmaking.png)_Custom matchmaking_![The custom matchmaking flow.](../assets/matchmaking/Custom_Matchmaking.png) ## Scoring The matchmaking scoring algorithm uses the weighted sum (WS) of signal values to assign scores to servers. For more information on scoring, see [Server scoring](/docs/en-us/scoring.md). ## Attributes Attributes are properties used in matchmaking scoring. An attribute can be a number, like a player's age and skill level rating, or a string, like a player's language. Attributes can be numerical or categorical: - **Numerical attributes** compare the difference between the joining player's attribute and the server's aggregated value, with larger differences lowering or increasing the score. For example, the closer the skill level of a player is to the average skill level of the server, the higher the numerical signal's score is. This score is then multiplied by the signal's weight. - **Categorical attributes** are based on how common the joining player's attribute is when compared to the other players in the server. For example, if a high percentage of the players inside a server have the same preferred language as the joining player, the score increases. This score is then also multiplied by the signal's weight. For a list of all existing attributes, see [Existing attributes](/docs/en-us/attributes-and-signals.md#existing-attributes). For more information about custom attributes, see [Custom attributes](/docs/en-us/attributes-and-signals.md#custom-attributes). ## Signals Signals used in server scoring can either be the default ones already provided by Roblox, or custom ones you create yourself. Roblox signals use existing attributes that Roblox already has access to, like player location, age group, and latency. Custom signals, however, need to use custom attributes to access data from your data stores. In order to create a custom signal, you must first create a custom attribute. For a list of all existing Roblox signals, see [Existing signals](/docs/en-us/attributes-and-signals.md#existing-signals). For more information about custom signals, see [Custom signals](/docs/en-us/attributes-and-signals.md#custom-signals). ## Custom matchmaking When a player joins your experience, matchmaking assigns a score to each running server based on the weighted sum of the server's signal values. You can customize this process by adjusting the weight of each of your signals to change how much each signal matters in the scoring of servers. Increasing the weight of the latency signal, for example, means the matchmaking algorithm prioritizes servers with lower ping for smoother gameplay. After adjusting signal weights, you can preview the impact of these new weights on matchmaking server selection by evaluating them on mock servers. For more information about how to customize matchmaking and simulate the results, see [Customize your matchmaking configuration](/docs/en-us/customize-matchmaking.md). ## Analytics Matchmaking analytics helps you track the success of your matchmaking configurations and find opportunities to optimize them. For more information about matchmaking analytics, see [Analytics](/docs/en-us/analytics.md). --- title: "Server scoring" url: /docs/en-us/matchmaking/scoring last_updated: 2026-06-29T19:34:03Z description: "How matchmaking uses scoring to match players to servers." --- # Server scoring ## The scoring formula Servers are scored using a weighted sum formula. There are four parts to this formula: - [Attributes](#attributes), which are data that describe players and servers, such as player age or server occupancy. - [Signals](#signals), which transform attributes into numbers between 0 and 1. - [Weights](#weights), which describe the relative importance of signals. - The [server score](#server-score), which measures the server's compatibility with the joining player. The joining player is matched to the server with the highest server score. ### Attributes Attributes are data that describe players and servers. An attribute's value can be a number or a string. When an attribute's value is a string, it's called a categorical attribute. When an attribute's value is a number, it's called a numerical attribute. For a full list of all available Roblox attributes, see [Existing attributes](/docs/en-us/matchmaking/attributes-and-signals.md#existing-attributes). For more information about custom attributes, see [Custom attributes](/docs/en-us/matchmaking/attributes-and-signals.md#custom-attributes). ##### Example: Categorical and numerical attributes | **Categorical attributes** | | --- | | **Has Friends** is a categorical attribute because its value is the string "true" when there is a preferred player on this server, and "false" when there are no preferred players on this server. | | **Language** is a categorical attribute because its value is the player's language setting, such as "Japanese". | | **Numerical attributes** | | **Age** is a numerical attribute because its value is the player's age. | | **Occupancy** is a numerical attribute because its value is the number of players currently in this server. | ### Signals Signals transform attribute values into numbers between 0 and 1, called signal scores. A signal score of 1 predicts high player compatibility, while a signal score of 0 predicts low player compatibility. Depending on the attribute, transformations can include different aggregations, comparisons, and normalizations. For a full list of existing Roblox signals, see [Existing signals](/docs/en-us/matchmaking/attributes-and-signals.md#existing-signals). For more information about custom signals, see [Custom signals](/docs/en-us/matchmaking/attributes-and-signals.md#custom-signals). ##### Example: Two servers with different signal scores The following table calculates Occupancy signal scores for two different server with capacity for 8 players: | | **Players in server** | **Occupancy score** | | --- | --- | --- | | **Server A** | 2 | 2/8 = 0.25 | | **Server B** | 6 | 6/8 = 0.75 | The Occupancy signal scores Server B higher and considers it a better fit for the joining player. ### Weights A signal's weight describes that signal's importance relative to other signals. A higher weight increases the signal's contribution to the server score. The value after multiplying a signal score by its weight is called a **weighted signal**. Signals can't have negative weights. If a signal's weight is 0, that signal is not considered for scoring. ##### Example: Weighting the Occupancy score The following table calculates weighted Occupancy signal scores for a place with capacity for 8 players and an Occupancy signal weight of 2: | | **Players in server** | **Occupancy score** | **Weighted occupancy score** | | --- | --- | --- | --- | | **Server A** | 2 | 2/8 = 0.25 | 0.25 * 2 = 0.5 | | **Server B** | 6 | 6/8 = 0.75 | 0.75 * 2 = 1.5 | ##### Example: Two configurations with different weights Different weights can also make matchmaking choose different servers. The following table calculates the server scores of two servers with different matchmaking configurations. Configuration 2 has a higher weight for its Occupancy signal, and Server A has a friend of the joining player in it. | | | **Friends** | **Occupancy** | | | --- | --- | --- | --- | --- | | | | **Weight** | **Score** | **Weight** | **Score** | **Total score** | | **Config 1** | **Server A** | 1 | 1 | 5 | 0.25 | 1 * 1 + 5 * 0.25 = 2.25 | | **Server B** | 1 | 0 | 5 | 0.75 | 1 * 0 + 5 * 0.75 = 3.75 | | **Config 2** | **Server A** | 3 | 1 | 5 | 0.25 | 3 * 1 + 5 * 0.25 = 4.25 | | **Server B** | 3 | 0 | 5 | 0.75 | 3 * 0 + 5 * 0.75 = 3.75 | ### Server score The server score is calculated by the following weighted sum formula, which sums a server's weighted signal scores: ServerScore = WeightedSignalScore 1 + WeightedSignalScore 2 + … + WeightedSignalScore n \text{ServerScore} = \text{WeightedSignalScore}_1 + \text{WeightedSignalScore}_2 + \ldots + \text{WeightedSignalScore}_nServerScore=WeightedSignalScore1​+WeightedSignalScore2​+…+WeightedSignalScoren​ ServerScore = Weight 1 × SignalScore 1 + Weight 2 × SignalScore 2 + … + Weight n × SignalScore n \phantom{\text{ServerScore}} = \text{Weight}_1 \times \text{SignalScore}_1 + \text{Weight}_2 \times \text{SignalScore}_2 + \ldots + \text{Weight}_n \times \text{SignalScore}_nServerScore=Weight1​×SignalScore1​+Weight2​×SignalScore2​+…+Weightn​×SignalScoren​ ### Matchmaking configurations A matchmaking configuration is the set of signals and weights used to score servers of a place. By default, all servers of all places are scored by the Roblox default configuration, meaning you don't need to customize or enable any settings in order to use it. The Roblox default configuration includes the following signals and weights: | **Signal** | **Weight** | | --- | --- | | Friends | 15 | | Latency | 3 | | Text Chat | 3 | | Occupancy | 2 | | Play History | 2 | | Language | 2 | | Age | 1 | | Voice Chat | 1 | | Device Type | 0 | In the default Roblox configuration, the Friends signal's weight is greater than the sum of the weights of every other signal. This means that, if available, the joining player is always matched to servers with friends (or players on the same IP address) in them. The Device Type signal exists in the default configuration but has a weight of 0, so it doesn't affect matchmaking decisions. You can adjust this weight when you [customize matchmaking](/docs/en-us/matchmaking/customize-matchmaking.md). For more information about each Roblox signal, see [Existing signals](/docs/en-us/matchmaking/attributes-and-signals.md#existing-signals). ## Advanced concepts The following are more advanced concepts about the matchmaking process. ### Normalization Signals normalize attribute values to be numbers between 0 and 1. Numerical signals can be normalized by any positive number, called the **normalizing factor**. If the normalized value is greater than 1, it is clamped down to 1. ##### Example: Designing the Age signal The Age signal measures the difference between the average age of players in the server and the joining player's age. ageDifference = ∣ avgServerAge − joiningPlayerAge ∣ \text{ageDifference} = |\text{avgServerAge} - \text{joiningPlayerAge}|ageDifference=∣avgServerAge−joiningPlayerAge∣ Servers with age differences beyond 25 are all considered equally incompatible with the player. For example, an age difference of 25 is no worse for a player than an age difference of 26, so both values should take the signal score to 0. In this case, 25 is considered the normalizing factor. normAgeDifference = min ⁡ ( 1, ∣ avgServerAge − joiningPlayerAge ∣ / 25) \text{normAgeDifference} = \min(1, |\text{avgServerAge} - \text{joiningPlayerAge}| / 25)normAgeDifference=min(1,∣avgServerAge−joiningPlayerAge∣/25) The signal score is inversely related to the age difference, meaning that the signal score is higher when the age difference is smaller. ageDifferenceSignalScore = 1 − normAgeDifference \text{ageDifferenceSignalScore} = 1 - \text{normAgeDifference}ageDifferenceSignalScore=1−normAgeDifference The following table shows Age signal scores with two different normalizing factors: | **Age** | **Normalizing factor: 100** | **Normalizing factor: 25** | | --- | --- | --- | | 50 | 0.5 | 0 | | 25 | 0.75 | 0 | | 12 | 0.88 | 0.52 | | 5 | 0.95 | 0.8 | | 0 | 1 | 1 | The signal with a normalizing factor of 25 considers the 5-year and 12-year age differences to be farther apart. Smaller normalizing factors have the effect of amplifying marginal increases or decreases in age difference. ### Weight magnitudes A weight indicates the importance of a signal relative to other signals in a configuration. The relative magnitudes of weights determine signal importance, not their absolute magnitudes. ##### Example: Relative vs absolute magnitude The table below calculates the server scores of two servers with two different matchmaking configurations. Configuration 5 and Configuration 6 have Occupancy weights higher than their respective Friends weights. Server A has a friend of the joining player in it. | | | **Friends** | **Occupancy** | | | --- | --- | --- | --- | --- | | | | **Weight** | **Score** | **Weight** | **Score** | **Total score** | | **Config 5** | **Server A** | 10,000 | 1 | 15,000 | 0.25 | 10,000 * 1 + 15,000 * 0.25 = 13,750 | | **Server B** | 10,000 | 0 | 15,000 | 0.75 | 10,000 * 0 + 15,000 * 0.75 = 11,250 | | **Config 6** | **Server A** | 0.01 | 1 | 0.05 | 0.25 | 0.01 * 1 + 0.05 * 0.25 = 0.0225 | | **Server B** | 0.01 | 0 | 0.05 | 0.75 | 0.01 * 0 + 0.05 * 0.75 = 0.0375 | Server A wins with Configuration 5 while Server B wins with Configuration 6. This is because Configuration 5's Occupancy weight is only 1.5x its Friends weight, while Configuration 6's Occupancy weight is 5x its Friends weight. Despite differences in absolute magnitudes, Configuration 6 prioritizes Occupancy over Friends more than Configuration 5 does. --- title: "Parts" url: /docs/en-us/parts last_updated: 2026-06-29T19:34:03Z description: "Parts are Roblox's primary building blocks." --- # Parts `Class.Part|Parts` are descendants of the `Class.BasePart` class and are Roblox's primitive building blocks with properties like position, size, orientation, and color. You can use basic parts as is, or you can apply [solid modeling](/docs/en-us/parts/solid-modeling.md) operations to combine parts into more complex shapes. For advanced and intricate 3D models, you can also import third-party model files as `Class.MeshPart|MeshParts` as outlined in [Meshes](/docs/en-us/parts/meshes.md). ![A single gray sphere part](../assets/modeling/parts/Basic-Part-Sphere.png)_Basic sphere part_ ![A bright blue hollow bowl that was made with solid modeling operations.](../assets/modeling/parts/Part-Example-CSG.jpg)_Bowl created with solid modeling_ ![A high-quality treasure chest mesh with a texture.](../assets/modeling/parts/Mesh-Example.jpg)_Mesh with texture_ By default, parts are rigid bodies that follow real-world physical mechanics, including gravity, collision, and momentum. You can connect related parts together as a single [assembly](/docs/en-us/physics/assemblies.md) using a `Class.WeldConstraint`, or a joint like `Class.Motor6D` or `Class.Bone`. As an assembly, the connected parts act as a single rigid entity, referencing a common position, orientation, and scale. You can use a `Class.Model` container to group related parts together and access the group as a single assembly in the [Explorer](/docs/en-us/studio/explorer.md). See [Models](/docs/en-us/parts/models.md) for more information. ## Basic part types `Class.Part` objects can take the shape of blocks, spheres, cylinders, wedges, or corner wedges. In addition, `Class.TrussPart` acts as a truss beam that characters can climb like a ladder. ![A single Block part.](../assets/modeling/parts/Basic-Part-Block.png)_ Block_ ![A single Sphere part.](../assets/modeling/parts/Basic-Part-Sphere.png)_ Sphere_ ![A single Cylinder part.](../assets/modeling/parts/Basic-Part-Cylinder.png)_ Cylinder_ ![A single Wedge part.](../assets/modeling/parts/Basic-Part-Wedge.png)_ Wedge_ ![A single Corner Wedge part.](../assets/modeling/parts/Basic-Part-Corner-Wedge.png)_ Corner Wedge_ ## Part properties Each part has a variety of properties that you can customize through the [Properties](/docs/en-us/studio/properties.md) window. ![A close up view of the Properties window with the Appearance and Transform properties highlighted.](../assets/studio/properties/Sections-Example.png) The following are commonly utilized properties: - `Class.BasePart|Anchored` controls if physics affects the position of the part. When this property is set to true, the part never changes position due to gravity or any other force. You should anchor most parts in your experience or gravity and physics will affect your parts as soon as the experience runs. > **Success:** To view lighted outlines around the base of anchored parts in order to visualize which parts cannot move under gravity or physics, toggle on **Anchored parts** from the [Visualization Options](/docs/en-us/studio/ui-overview.md#visualization-options) widget in the upper‑right corner of the 3D viewport. - `Class.BasePart|CanCollide` controls whether or not a part can collide with other parts. When this property is set to true, the part is impenetrable and the physics engine accounts for it within your experience. Conversely, when this property is set to false, the part can pass through anything, and the physics engine does **not** account for it. - `Class.BasePart|Transparency` sets a part's visibility to any value between the default value of 0 (fully visible) and 1 (fully transparent). If you have many partially transparent parts, they can slow down performance. To alleviate this, merge them using [solid modeling](/docs/en-us/parts/solid-modeling.md). ## Insert parts The **Part** insertion button, accessible from the **Home** and **Model** tab toolbars, inserts a new part into the workspace. Click‑holding over the small corner arrow on the button lets you select either **Block**, **Sphere**, **Wedge**, **Corner Wedge**, or **Cylinder**. ![Part tool and its part type picker menu.](../assets/studio/general/Toolbar-Insert-Part-Widget.png) > **Info:** To insert a `Class.TrussPart` (not available on the insertion menu), use the [Explorer](/docs/en-us/studio/explorer.md) as outlined [here](/docs/en-us/studio/explorer.md#insert-and-parent). ## Select parts As you hover over parts in the 3D viewport, they are outlined to indicate their potential selection. You can select an outlined part by clicking it, or you can select multiple parts by holding `Shift`, `Ctrl`, or `⌘` as you hover over and click them. See [here](/docs/en-us/studio/ui-overview.md#object-selection) for advanced methods of selecting parts in the viewport. ![Multiple models selected in 3D viewport](../assets/studio/general/Editor-Window-Object-Selection.jpg) ## Transform parts You can move, scale, and rotate selected parts either through modeling tools or by setting a new position, size, or orientation in the [Properties](#part-properties) window. ![Transform tools indicated in Studio's toolbar.](../assets/studio/general/Toolbar-Transform-Tools.png) ![Move draggers shown on part in 3D viewport.](../assets/modeling/parts/Transform-Move-SM.png)_Move_ ![Scale draggers shown on part in 3D viewport.](../assets/modeling/parts/Transform-Scale-SM.png)_Scale_ ![Rotate draggers shown on part in 3D viewport.](../assets/modeling/parts/Transform-Rotate-SM.png)_Rotate_ When using the tools, you can move, scale, or rotate parts in either **world** orientation or **local** orientation by pressing `Ctrl``L` on Windows or `⌘``L` on Mac. When you enable local orientation, the arrow axis indicators change to a part's local orientation, and an **L** indicator displays. #### World ![An angled block part with draggers in World orientation mode.](../assets/modeling/parts/Move-World-Orientation.png) #### Local ![An angled block part with draggers in Local orientation mode.](../assets/modeling/parts/Move-Local-Orientation.png) Tool transform **snapping** increments are based on **studs** for moving/scaling or **degrees** for rotating, each adjustable in the toolbar. While transforming, you can temporarily toggle snapping by holding the `Shift` key. ![Transform snapping tools indicated in Studio's toolbar.](../assets/studio/general/Toolbar-Transform-Snapping.png) > **Info:** To quickly jump to the **move/scale** increment input, press `Shift``2`. To quickly jump to the **rotate** increment input, press `Alt``R` (Windows) or `⌥``R` (Mac). ### Move You can move a selected part to a new position using the **Move** tool (default shortcut `2`) or by **cursor dragging**. While moving a part, you can temporarily toggle snapping by holding `Shift`. #### Move Tool To move a part by an axis dragger along the **X**, **Y**, or **Z** axis, click/drag an arrow pointing along the desired axis of movement. ![A block part with the Move tool's visual aids.](../assets/modeling/parts/Transform-Move.png) After releasing the drag, the numerical **distance indicator** remains visible. If desired, fine‑tune the distance that was moved by clicking inside the indicator and entering any number. ![A moved part showing the distance moved in an indicator/input field](../assets/modeling/parts/Transform-Move-Indicator.png) If you drag a part by its [pivot](/docs/en-us/studio/pivot-tools.md) point while the **Move** tool is selected, the pivot will "soft snap" to surfaces and edges of nearby parts. ![A block part with an offset pivot soft-snapping to the surface of a nearby part.](../assets/modeling/parts/Transform-Move-Pivot-Snap.png) #### Cursor Drag To move a part by cursor dragging, click anywhere on the part to grab it. If snapping is **enabled**, a ruler will appear as you hover, showing what point you'll grab the part by. As you drag the part around, another ruler will appear on the surface/edge of nearby parts, indicating alignment of the grab point. ![A block part showing the grab point and snapping ruler on a neighboring part.](../assets/modeling/parts/Transform-Move-Ruler-Snap.png) If snapping is **disabled**, the part will "soft snap" to surfaces and edges of nearby parts. ![A block part soft-snapping to the corner edges of a neighboring part.](../assets/modeling/parts/Transform-Move-Cursor-Drag-Snap.png) While cursor dragging, `T` and `R` can be used to quickly rotate the part in 90° increments around the point you picked it up by. `T` tilts the part 90° towards the camera, while `R` rotates the part 90° around the normal of the hovered surface. ![Diagram showing how parts can be tilted and rotated with the T and R keys respectively.](../assets/modeling/parts/Transform-Move-Cursor-Drag-T-R.png) ### Scale To scale (resize) a selected part along the **X**, **Y**, or **Z** axis, use the **Scale** tool (default shortcut `3`) and click/drag a handle. While dragging, you can temporarily toggle snapping by holding `Shift`. ![A block part with the Scale tool's visual aids.](../assets/modeling/parts/Transform-Scale.png) ### Rotate To rotate a selected part around the **X**, **Y**, or **Z** axis, use the **Rotate** tool (default shortcut `4`) and click/drag a rotation ring. While dragging, you can temporarily toggle snapping by holding `Shift`. ![A block part with the Rotate tool's visual aids.](../assets/modeling/parts/Transform-Rotate.png) > **Info:** In addition to using the **Rotate** tool, you can quickly rotate selected objects 90° about their **X** axis by pressing `Ctrl``T` (`⌘``T`) or 90° about their **Y** axis by pressing `Ctrl``R` (`⌘``R`). ## Other actions ### Group You can group objects into a [model](/docs/en-us/parts/models.md) by selecting them and clicking the **Group** button. This action has a default shortcut of `Ctrl``G` (Windows) or `⌘``G` (Mac). Alternatively, you can group objects into a [folder](/docs/en-us/studio/explorer.md#organizing-by-folders) by click‑holding over the small corner arrow on the button and selecting **Group as Folder** (default shortcut of `Alt``Ctrl``G` or `⌥``⌘``G`). ![Group tool indicated in Studio's toolbar](../assets/studio/general/Toolbar-Group.png) To **ungroup** an existing model or folder, select **Ungroup** from the **Edit** menu (default shortcut of `Ctrl``U` or `⌘``U`). ### Anchor The **Anchor** toggle controls whether the part will be **immovable** by physics. When `Class.BasePart.Anchored|Anchored`, a part will never change position due to gravity, other parts collisions, overlapping other parts, or any other physics-related causes. This action has a default shortcut of `Alt``A` (Windows) or `⌥``A` (Mac). ![Anchor toggle indicated in Studio's toolbar](../assets/studio/general/Toolbar-Anchor.png) > **Info:** If you prefer a click-to-toggle workflow in the 3D viewport, click‑hold over the small corner arrow on the **Anchor** button and enable **Click object to anchor**. Once enabled, the tool operates as a "key" for both states — clicking on an unanchored object in the viewport anchors it, while clicking on an anchored object unanchors it. ### Color While a part is gray by default, you can change it to any color through the following methods. #### Color Button Clicking the **Color** button applies the selected color to all selected parts. You can choose a color by click‑holding over the small corner arrow on the **Color** button to reveal a hexagonal picker. ![Color widget's hexagonal picker.](../assets/studio/general/Toolbar-Color-Picker.png) > **Info:** If you prefer a fill/paint workflow in the 3D viewport, enable **Click object to apply color** at the bottom of the hexagonal picker popup and then click parts in the viewport to apply the chosen color. #### Color Popup The **Color** popup allows you to set a color through your operating system's color picker widget. To access it, navigate to the [Properties](/docs/en-us/studio/properties.md) window and click the small box to the left of the `Class.BasePart.Color|Color` property. ![A close up view of the Properties window with the Color property's color box highlighted.](../assets/studio/properties/Color-Picker.png) #### RGB Value To define a specific RGB color value for a part, enter an RGB value into the `Class.BasePart.Color|Color` property field. ![A close up view of the Properties window with the Color property's RGB color value highlighted.](../assets/studio/properties/Color-RGB-Entry.png) ### Material Similar to [color](#color), you can customize a part's [material](/docs/en-us/parts/materials.md) to simulate real‑world materials such as wood, glass, or fabric. Click‑holding over the small corner arrow on the **Material** button reveals the [material](/docs/en-us/parts/materials.md) picker. ![Material widget's picker highlighted.](../assets/studio/general/Toolbar-Material-Picker.png) When selecting a material, consider the following: - **Material affects the physical traits of a part, not just its appearance**. For example, the `Enum.Material.Concrete|Concrete` material is heavier than the `Enum.Material.Plastic|Plastic` material, so a concrete brick will have higher density than a plastic brick and sink in water faster. - Some materials have special physical effects. For example, parts will appear to glow if they are set to the `Enum.Material.Neon|Neon` material.![An angled red block part with a smooth plastic material.](../assets/modeling/parts/Material-SmoothPlastic.png)_SmoothPlastic_![An angled red block part with a glowing neon material.](../assets/modeling/parts/Material-Neon.png)_Neon_ See [Materials](/docs/en-us/parts/materials.md) for more information on how to apply both default and custom materials to parts. ### Lock The **Lock** toggle controls whether the part can be selected from the 3D viewport. This action has a default shortcut of `Alt``L` (Windows) or `⌥``L` (Mac). ![Lock tool indicated in Studio's toolbar](../assets/studio/general/Toolbar-Lock.png) > **Info:** If you prefer a click-to-toggle workflow in the 3D viewport, click‑hold over the small corner arrow on the **Lock** button and enable **Click object to lock**. Once enabled, the tool operates as a "key" for both states — clicking on an unlocked object in the viewport locks it, while clicking on a locked object unlocks it. To unlock all objects, select **Unlock All** from the **Edit** menu. --- title: "Materials" url: /docs/en-us/parts/materials last_updated: 2026-06-29T19:34:03Z description: "Materials determine objects' visual appearance and physical properties." --- # Materials Roblox's materials are unlike materials on other platforms, in that their visual appearance **and** their [physical properties](#physical-properties) reflect those of materials in the real world. For example, concrete is heavier than plastic and sinks faster in water. When you set the material of a part or terrain, Roblox simulates its physical material properties to make this behavior just work. The Roblox Engine offers a range of [base materials](#base-materials) suitable to build many experiences, including various categories of metal, rock, and organic materials. You can also create your own [custom materials](#custom-materials) and apply them to parts or [terrain](/docs/en-us/parts/terrain.md). Custom materials have an additional [adaptive materials](#adaptive-materials) behavior that lets you adapt any model to use your art style and custom materials, even if someone else created the model. ## Apply materials You can quickly apply materials to [parts](/docs/en-us/parts.md) through the [Material](#material-widget) widget. The [Material Manager](#material-manager) offers the same functionality and an additional "paint tool" application mode. ### Material widget The **Material** widget is accessible from the **Home** or **Model** tab toolbars. Click‑holding over the small corner arrow on the button reveals a material picker. See [Parts](/docs/en-us/parts.md#material) for more information. ![Material widget's picker highlighted.](../assets/studio/general/Toolbar-Material-Picker.png) ### Material Manager The **Material Manager** is accessible through **Window** ⟩ **3D** ⟩ **Material** or from the **Material** widget's picker popup. From the manager window, you can apply materials to [parts](/docs/en-us/parts.md) through the following workflows. #### Apply to Selected The default `Enum.Material` property for new `Class.Part` instances is **Plastic**. To apply a different material to parts: 1. In the 3D viewport or [Explorer](/docs/en-us/studio/explorer.md), select one or more parts. 2. In the **Material Manager** palette, hover your mouse over the desired material (you don't need to select it) and click the **Apply to Selected Parts** button.![Apply to Selected Parts button indicated in Material Manager](../assets/studio/material-manager/Apply-To-Selected-Parts.png) #### Paint Tool You can also use a material as a painting tool that applies to parts: 1. In the **Material Manager**, select the material you want to apply. 2. In the top-left corner, click the **Paint Parts With Selected Material** button to enable the material as a painting tool.![Paint Parts With Selected Material button indicated in Material Manager](../assets/studio/material-manager/Paint-Parts-With-Selected-Material.png) 3. In the 3D viewport, hover over and click the parts that you want to apply the material to. 4. When you're done painting, click the button again to disable the tool. ## Custom materials The [Material Manager](#material-manager) provides a user interface to interact with various aspects of `Class.MaterialService`, including creating new custom materials and applying them to parts and [terrain](/docs/en-us/parts/terrain.md). Custom materials are represented by `Class.MaterialVariant` instances within `Class.MaterialService`. ![Explorer window showing two MaterialVariant instances within MaterialService](../assets/modeling/materials/Explorer-MaterialVariant-Instances.png) You can apply custom materials per-part or globally to both parts and terrain, and you can fine-tune how custom materials apply to faces of terrain with `Class.TerrainDetail` instances. > **Info:**`Class.MaterialVariant` and `Class.SurfaceAppearance` instances both use [PBR](/docs/en-us/art/modeling/surface-appearance.md) textures to customize the appearance of objects. The difference is that `Class.MaterialVariant` is for customizing the appearance of reusable tileable material, whereas `Class.SurfaceAppearance` is for customizing the visual appearance of a specific mesh with UV mapping. `Class.MaterialVariant` instances also have [physical properties](#physical-properties) that `Class.SurfaceAppearance` instances don't. ### Create custom materials You can edit all properties of a custom material in the [Material Manager](#material-manager) or through the properties of a `Class.MaterialVariant` instance. You can also generate custom materials through the prompt‑based [Material Generator](/docs/en-us/studio/material-generator.md). To create a custom material in the [Material Manager](#material-manager): 1. Click the **base material** from which your custom material will inherit [physical properties](#physical-properties). If you skip this step, the base material will be **Plastic**, but you can change it later.![A Base material of Asphalt that's selected in the Material Manager.](../assets/studio/material-manager/Base-Material-Selected.png) 2. In the top-left corner, click **Create Material Variant**.![Create Material Variant button indicated in Material Manager.](../assets/studio/material-manager/Create-Material-Variant.png) A new variant appears in the palette with an icon in the bottom-right corner, indicating it's a custom material.![New MaterialVariant tile in Material Manager with icon to indicate a custom material.](../assets/studio/material-manager/Custom-Material-Icon.png) 3. In the inspector, rename your custom material to describe its purpose. You can change the name later, but if you do so **after** applying the material to parts, you'll need to re-apply it to those parts. 4. For each **texture map** option such as **Color** or **Normal**, paste an asset ID or import a new texture from your computer. Square textures work best. If you don't specify an asset for a texture map, that texture remains blank. 5. If desired, adjust the **Studs Per Tile** and **Pattern** values to change how the material looks. > **Info:** If necessary, you can delete a custom material from the [Material Manager](#material-manager) by selecting it and clicking the **Delete** button below its preview globe. Alternatively, you can delete its associated `Class.MaterialVariant` instance within **MaterialService** of the [Explorer](/docs/en-us/studio/explorer.md). ### Apply custom materials For [parts](/docs/en-us/parts.md), you can use a custom material just like any other material, applying it to selected parts through the [Material](#apply-materials) widget or the [Material Manager](#material-manager).¹ You can also apply the new material to a part by setting its **MaterialVariant** property in the [Properties](/docs/en-us/studio/properties.md) window. In this case, Studio automatically sets its **Material** property to the base material you chose when creating the material. ![Material and MaterialVariant properties set in the Properties window.](../assets/modeling/materials/Properties-Material-And-MaterialVariant.png) > **Warning:** Note that if you rename a custom material **after** applying it to parts, those parts will not automatically use the custom material with the new name. This behavior allows for [Adaptive Materials](#adaptive-materials). If you want parts to continue using a custom material after you rename it, you'll need to re-apply the custom material. _¹ Unlike on parts, you cannot **directly** apply custom materials to [terrain](/docs/en-us/parts/terrain.md), although you can set a custom material as a [material override](#material-overrides) to an existing base material for all terrain using that material._### Material overrides You can set a custom material as a **material override** to make its base material serve as a reference to the custom material. When you do so, Studio will use the custom material for both the textures and [physical properties](#physical-properties) of any part or [terrain](/docs/en-us/parts/terrain.md) that uses the custom material. > **Info:** Material overrides are the only way to apply custom materials to terrain. Note also that the materials for terrain are global per place, so you can't apply multiple variants of the same base material to the terrain in a single place. #### Set overrides To set a custom material as a material override in the [Material Manager](#material-manager): 1. Click the custom material that you want to set as an override. 2. In the inspector, scroll down to **Overrides** and enable **Set as Override**.![.](../assets/studio/material-manager/Set-Override-Enabled.png) The new override appears as a property of **MaterialService** in the [Properties](/docs/en-us/studio/properties.md) window. #### Terrain details By default, applying a custom material to parts or as an override applies that custom material as tiles across each face. For terrain, you can optionally configure `Class.TerrainDetail` instances to customize the **top**, **side**, and **bottom** of terrain voxels using that custom material. To customize the faces of terrain using a custom material: 1. In the palette of the [Material Manager](#material-manager), click the custom material. 2. In the inspector, confirm that its **Set as Override** toggle is enabled. 3. In the **Terrain Details** section, click **Create** for each face you want to customize. 4. For each face you enable, expand the arrow to access and edit details such as its name, texture maps, studs per tile, and pattern. #### Disable overrides You can disable an entire material override and all base materials that it's currently overriding, or you can disable the override for a specific base material. #### Disable Entire Override 1. In the palette of the [Material Manager](#material-manager), click a **custom material** that's being used as an override. 2. In the inspector, scroll down to **Overrides** and disable **Set as Override**. #### Disable Override for Specific Material 1. In the palette of the [Material Manager](#material-manager), click a **base material** which is being overridden by a custom material. 2. In the inspector, scroll down to **Material Override** and select **None** from the menu. ## Physical properties All materials have built-in **physical properties** such as density, elasticity, and friction. Through the application of [custom materials](#custom-materials) with unique physical properties, you can affect global material behavior for all parts and [terrain](/docs/en-us/parts/terrain.md) which use the custom material, such as creating an extremely slippery variant of the **Ice** material. When factoring physical properties, the engine prioritizes more granular per-part settings over material behaviors to determine the effective physical properties of a surface: Custom physical properties of the specific part. Custom physical properties of the part's custom material. Custom physical properties of the [material override](#set-overrides) of the part's material. The [default physical properties](#default-physical-properties) of the part's material. #### Applying to Custom Materials To set unique physical properties for any [custom material](#custom-materials) and automatically apply them to all parts and [terrain](/docs/en-us/parts/terrain.md) which use the material: 1. In the palette of the [Material Manager](#material-manager), click the custom material. 2. In the inspector, scroll down to the **Physics** section and set custom physical properties as detailed in the `Datatype.PhysicalProperties` reference.![Custom physical properties in Material Manager inspector pane](../assets/studio/material-manager/Physical-Properties.png) For any part that uses the custom material and does **not** have [part-specific](#physical-properties) overrides, the **CurrentPhysicalProperties** branch in the [Properties](/docs/en-us/studio/properties.md) window reveals that its default physical properties are overridden by the custom material's properties.![Properties window showing physical properties overridden by those of custom material](../assets/modeling/materials/Properties-Physical-Overridden.png) #### Per-Part Overrides If you need to override a part's custom material properties and set physical properties for that specific part, you can use its **CustomPhysicalProperties** toggle. 1. With the part selected, enable **CustomPhysicalProperties** in the [Properties](/docs/en-us/studio/properties.md) window.![CustomPhysicalProperties enabled in a part's properties](../assets/modeling/materials/Properties-Part-CustomPhysicalProperties.png) 2. Set custom physical properties as detailed in the `Datatype.PhysicalProperties` reference. ## Adaptive materials When you apply a custom material to a part, the part's `Class.Part.MaterialVariant` property becomes the name of its `Class.MaterialVariant` rather than its specific instance. This means that when you reuse the part in the same or a different place, as in a model or package, it's easier for you to adapt different custom materials to adjust the part's look. The adaptive behavior of custom materials has the following effects: - If you create collections of custom materials with the same name but different textures, then you can quickly change the style of a place by changing which collection is a child of `Class.MaterialService`. - If you insert a model with parts that use a custom material, then you can modify its look by creating an instance of `Class.MaterialVariant` in `Class.MaterialService` and renaming it to the same name as the previous custom material, rather than applying the new material to the parts in the model. When you reuse custom materials in models and packages, each `MaterialVariant` instance must be in `Class.MaterialService` for it to work. - If you distribute a model to the Creator Store with a custom material, include the `Class.MaterialVariant` instance in the model. For more information about distributing models to the Creator Store, see [Distributing Assets](/docs/en-us/production/creator-store.md). - If you insert a model from the Creator Store, look for any `Class.MaterialVariant` instances and copy them to `Class.MaterialService`. For more information about importing models from the Creator Store, see [Creator Store](/docs/en-us/production/creator-store.md). - If you want to use custom materials with packages, put the package in `Class.MaterialService`. For more information on packages, see [Packages](/docs/en-us/projects/assets/packages.md). The [Creator Store](/docs/en-us/production/creator-store.md) has a category called Materials for "material packs", models that contain only `Class.MaterialVariant`, `Class.TerrainDetail`, `Class.Folder`, and `Class.Model` instances. The Materials category is a way to promote and discover custom materials by other creators. > **Success:** To make the most of adaptive materials, use a consistent naming convention for your `Class.MaterialVariant` instances. For example, you can use `PascalCase` with the base material of the custom material as the first word, as in `GrassWet`, `GrassDry`, and `GrassBurned`. ## Asset and property reference ### Base materials Shaders generate the look and feel of materials. The base material shaders work differently than the shader which `Class.MaterialVariant` instances use, so you can't create custom materials that look exactly like base materials, but you can still create custom materials that use their textures. The following tables list the asset IDs for material details such as `Class.SurfaceAppearance.ColorMap|ColorMap` and `Class.SurfaceAppearance.RoughnessMap|RoughnessMap`. #### Current Base | Material | ColorMap | Normal | Metalness | Roughness | | --- | --- | --- | --- | --- | | `Enum.Material.Asphalt\|Asphalt` | `9930003046` | `9429449876` | | `9429450346` | | `Enum.Material.Basalt\|Basalt` | `9920482056` | `9438412214` | | `9438412457` | | `Enum.Material.Brick\|Brick` | `9920482813` | `9438453152` | | `9438453413` | | `Enum.Material.Cardboard\|Cardboard` | `14108651729` | `14108654002` | | `14108654299` | | `Enum.Material.Carpet\|Carpet` | `14108662587` | `14108663154` | | `14108663726` | | `Enum.Material.CeramicTiles\|CeramicTiles` | `17429425079` | `17429425915` | `17429426100` | `17429426861` | | `Enum.Material.ClayRoofTiles\|ClayRoofTiles` | `18147681935` | `18147683410` | | `18147684855` | | `Enum.Material.Cobblestone\|Cobblestone` | `9919718991` | `9438457162` | | `9438457470` | | `Enum.Material.Concrete\|Concrete` | `9920484153` | `9466554006` | | `9466554186` | | `Enum.Material.CorrodedMetal\|CorrodedMetal` | `9920589327` | `9439548484` | `9439548749` | `9439556441` | | `Enum.Material.CrackedLava\|CrackedLava` | `9920484943` | `9438508790` | | `9438509046` | | `Enum.Material.DiamondPlate\|DiamondPlate` | `10237720195` | `9438583222` | `9438583347` | `9438583558` | | `Enum.Material.Fabric\|Fabric` | `9920517696` | `9873280412` | | `9873282563` | | `Enum.Material.Foil\|Foil` | `9466552117` | `9424786192` | `9424786272` | `9424786620` | | `Enum.Material.ForceField\|ForceField`² | | | | | | `Enum.Material.Glacier\|Glacier` | `9920518732` | `9438812958` | | `9438851286` | | `Enum.Material.Glass\|Glass`³ | `9438868521` | `7547304785` | | `7547304892` | | `Enum.Material.Granite\|Granite` | `9920550238` | `9438882935` | | `9438883109` | | `Enum.Material.Grass\|Grass` | `9920551868` | `9438955773` | | `9438955997` | | `Enum.Material.Ground\|Ground` | `9920554482` | `9439043558` | | `9439043765` | | `Enum.Material.Ice\|Ice` | `9920555943` | `9467301039` | | `9467301203` | | `Enum.Material.LeafyGrass\|LeafyGrass` | `9920557906` | `9439080781` | | `9439080950` | | `Enum.Material.Leather\|Leather` | `14108670073` | `14108670486` | | `14108670748` | | `Enum.Material.Limestone\|Limestone` | `9920561437` | `9439415191` | | `9439415495` | | `Enum.Material.Marble\|Marble` | `9439430596` | `9439431240` | | `9439431383` | | `Enum.Material.Metal\|Metal` | `9920574687` | `9873295432` | `9873318201` | `9873318890` | | `Enum.Material.Mud\|Mud` | `9920578473` | `9439509827` | | `9439510012` | | `Enum.Material.Neon\|Neon`² | | | | | | `Enum.Material.Pavement\|Pavement` | `9920579943` | `9439519281` | | `9439519532` | | `Enum.Material.Pebble\|Pebble` | `9920581082` | `9439528644` | | `9439537267` | | `Enum.Material.Plaster\|Plaster` | `14108671255` | `14108671870` | | `14108672378` | | `Enum.Material.Plastic\|Plastic`² | | | | | | `Enum.Material.Rock\|Rock` | `9920587470` | `9439538417` | | `9439545859` | | `Enum.Material.RoofShingles\|RoofShingles` | `119722544879522` | `77534750680073` | | `129397260312247` | | `Enum.Material.Rubber\|Rubber` | `14108673018` | `14108674698` | `14108674894` | `14108675142` | | `Enum.Material.Salt\|Salt` | `9920590225` | `9439565809` | | 9439566688 | | `Enum.Material.Sand\|Sand` | `9920591683` | `9439577084` | | `9439577327` | | `Enum.Material.Sandstone\|Sandstone` | `9920596120` | `9439596530` | | `9439596711` | | `Enum.Material.Slate\|Slate` | `9920599782` | `9439612514` | | `9439612733` | | `Enum.Material.SmoothPlastic\|SmoothPlastic`² | | | | | | `Enum.Material.Snow\|Snow` | `9920620284` | `9439632006` | | `9439632145` | | `Enum.Material.Wood\|Wood` | `9920625290` | `9439641376` | | `9439648605` | | `Enum.Material.WoodPlanks\|WoodPlanks` | `9920626778` | `9439650689` | | `9439658127` | _² Material is unique and/or its texture assets are bundled with Studio instead of being accessible as a typical asset ID.__³ Refraction of light through the glass material is not supported on mobile devices due to computational limitations._ #### Pre-2022 Base | Material | Color | Normal | Metalness | Roughness | | --- | --- | --- | --- | --- | | `Enum.Material.Brick\|Brick` | `7546648254` | `7546649654` | | `7546650017` | | `Enum.Material.Cobblestone\|Cobblestone` | `7546651802` | `7546652689` | | `7546652892` | | `Enum.Material.Concrete\|Concrete` | `7546653328` | `7546653707` | | `7546653868` | | `Enum.Material.CorrodedMetal\|CorrodedMetal` | `7547183598` | `7547181182` | `7547184321` | `7547184588` | | `Enum.Material.DiamondPlate\|DiamondPlate` | `7546654401` | `7546654536` | `7547162002` | `7547162137` | | `Enum.Material.Fabric\|Fabric` | `7547100606` | `7547100915` | | `7547101072` | | `Enum.Material.Foil\|Foil` | `7546644642` | `7546644903` | `7546644642` | `7546644963` | | `Enum.Material.Glass\|Glass` | `7547304577` | `7547304785` | | `7547304892` | | `Enum.Material.Granite\|Granite` | `7547164400` | `7546654648` | | `7547164660` | | `Enum.Material.Grass\|Grass` | `7547167347` | `7547168653` | | `7547169207` | | `Enum.Material.Ice\|Ice` | `7546644642` | `7547171198` | | `7547171276` | | `Enum.Material.Marble\|Marble` | `7547174345` | `7547176060` | | `7547177213` | | `Enum.Material.Metal\|Metal` | `7547178395` | `7547287997` | `7547288112` | `7547179082` | | `Enum.Material.Pebble\|Pebble` | `7547291174` | `7546645052` | | `7547291306` | | `Enum.Material.Sand\|Sand` | `7547294684` | `7547294810` | | `7547295087` | | `Enum.Material.Slate\|Slate` | `7547297050` | `7547297808` | | `7547298051` | | `Enum.Material.Wood\|Wood` | `7547190453` | `7547190548` | `7547190619` | `7547303147` | | `Enum.Material.WoodPlanks\|WoodPlanks` | `7547301709` | `7547188159` | `7547188891` | `7547332869` | #### Current Terrain | Material | Terrain Face | Color | Normal | Roughness | | --- | --- | --- | --- | --- | | `Enum.Material.Asphalt\|Asphalt` | Top | `9930003046` | `9429449876` | `9429450346` | | Side | `9930005689` | `9429465046` | `9429466226` | | `Enum.Material.Basalt\|Basalt` | (all) | `9920482056` | `9438412214` | `9438412457` | | `Enum.Material.Brick\|Brick` | (all) | `9920482813` | `9438453152` | `9438453413` | | `Enum.Material.Cobblestone\|Cobblestone` | (all) | `9919718991` | `9438457162` | `9438457470` | | `Enum.Material.Concrete\|Concrete` | (all) | `9920484153` | `9466554006` | `9466554186` | | `Enum.Material.CrackedLava\|CrackedLava` | (all) | `9920484943` | `9438508790` | `9438509046` | | `Enum.Material.Glacier\|Glacier` | Top | `9920518732` | `9438812958` | `9438851286` | | Side | `9920520269` | `9438853412` | `9438853585` | | Bottom | `9920521000` | `9438867267` | `9438867465` | | `Enum.Material.Grass\|Grass` | Top | `9920551868` | `9438955773` | `9438955997` | | Side | `9932815307` | `9438958337` | `9439010060` | | Bottom | `9932815307` | `9439011893` | `9439012136` | | `Enum.Material.Ground\|Ground` | (all) | `9920554482` | `9439043558` | `9439043765` | | `Enum.Material.Ice\|Ice` | Top | `9920555943` | `9467301039` | `9467301203` | | Side | `9920556722` | `9439071195` | `9439071371` | | `Enum.Material.LeafyGrass\|LeafyGrass` | Top | `9920557906` | `9439080781` | `9439080950` | | Side | `9932815307` | `9439151713` | `9439151929` | | Bottom | `9932815307` | `9439151713` | `9439151929` | | `Enum.Material.Limestone\|Limestone` | Top | `9920561437` | `9439415191` | `9439415495` | | Side | `9920562675` | `9439417923` | `9439418334` | | `Enum.Material.Mud\|Mud` | (all) | `9920578473` | `9439509827` | `9439510012` | | `Enum.Material.Pavement\|Pavement` | Top | `9920579943` | `9439519281` | `9439519532` | | Side | `9920580293` | `9439527387` | `9439527572` | | `Enum.Material.Rock\|Rock` | (all) | `9920587470` | `9439538417` | `9439545859` | | `Enum.Material.Salt\|Salt` | Top | `9920590225` | `9439565809` | `9439566688` | | Side | `9920590689` | `9439568299` | `9439575868` | | `Enum.Material.Sand\|Sand` | Top | `9920591683` | `9439577084` | `9439577327` | | Side | `9920592270` | `9439586304` | `9439593887` | | `Enum.Material.Sandstone\|Sandstone` | Top | `9920596120` | `9439596530` | `9439596711` | | Side | `9920628274` | `9439605275` | `9439605506` | | `Enum.Material.Slate\|Slate` | Top | `9920599782` | `9439612514` | `9439612733` | | Side | `9920614158` | `9439615040` | `9439621979` | | `Enum.Material.Snow\|Snow` | Top | `9920620284` | `9439632006` | `9439632145` | | Side | `9920620898` | `9439639648` | `9439640077` | | `Enum.Material.WoodPlanks\|WoodPlanks` | (all) | `9920626778` | `9439650689` | `9439658127` | #### Pre-2022 Terrain | Material | Terrain Face | Color | Normal | Roughness | | --- | --- | --- | --- | --- | | `Enum.Material.Asphalt\|Asphalt` | Top | `7547349715` | `7547350415` | `7551984908` | | Side | `7551989667` | `7547322693` | `7547350597` | | `Enum.Material.Basalt\|Basalt` | (all) | `7551975939` | `7547348152` | `7551977581` | | `Enum.Material.Brick\|Brick` | (all) | `7547631333` | `7547633037` | `7546657679` | | `Enum.Material.Cobblestone\|Cobblestone` | Top | `7551992689` | `7547351364` | `7547351553` | | Side | `7547648825` | `7547352096` | `7547649793` | | `Enum.Material.Concrete\|Concrete` | Top | `7547196561` | `7547337919` | `7547197958` | | Side | `7547628584` | `7547199386` | `7547310473` | | `Enum.Material.CrackedLava\|CrackedLava` | (all) | `7551980711` | `7547320674` | `7551982079` | | `Enum.Material.Glacier\|Glacier` | Top | `7547646888` | `7551930815` | `7551932698` | | Side | `7551937696` | `7551940030` | `7551941712` | | Bottom | `7551946144` | `7551948150` | `7551932698` | | `Enum.Material.Grass\|Grass` | Top | `7547307376` | `7547336606` | `7547308019` | | Side | `7547621556` | `7547191705` | `7547622691` | | Bottom | `7551927733` | `7547193357` | `7547625590` | | `Enum.Material.Ground\|Ground` | (all) | `7547348623` | `7547348887` | `7547349016` | | `Enum.Material.Ice\|Ice` | Top | `7547352634` | `7547651999` | `7547652934` | | Side | `7547656350` | `7547657495` | `7546663210` | | `Enum.Material.LeafyGrass\|LeafyGrass` | Top | `7546663659` | `7546664288` | `7546664614` | | Side | `7546665232` | `7546666003` | `7546666120` | | `Enum.Material.Limestone\|Limestone` | Top | `7547206319` | `7547670319` | `7547671387` | | Side | `7547206579` | `7547674533` | `7547206799` | | `Enum.Material.Mud\|Mud` | (all) | `7551972606` | `7552022188` | `7552023936` | | `Enum.Material.Pavement\|Pavement` | Top | `7547678151` | `7547207799` | `7547207869` | | Side | `7547680765` | `7547208846` | `7547682025` | | `Enum.Material.Rock\|Rock` | (all) | `7546659890` | `7546660701` | `7547643804` | | `Enum.Material.Salt\|Salt` | Top | `7546666647` | `7547660879` | `7547661939` | | Side | `7547665222` | `7547667170` | `7547668252` | | `Enum.Material.Sand\|Sand` | Top | `7547635114` | `7547311493` | `7546658153` | | Side | `7546658461` | `7546659003` | `7546659187` | | `Enum.Material.Sandstone\|Sandstone` | Top | `7547202858` | `7547204511` | `7551954003` | | Side | `7551958805` | `7551960790` | `7551962377` | | Bottom | `7551967204` | `7551968869` | `7551970693` | | `Enum.Material.Slate\|Slate` | (all) | `7547309616` | `7546656859` | `7547626537` | | `Enum.Material.Snow\|Snow` | (all) | `7547315875` | `7547201338` | `7547316776` | | `Enum.Material.WoodPlanks\|WoodPlanks` | (all) | `7547637836` | `7547639230` | `7547640511` | ### Default colors The following table lists the default RGB values for each base material. For information on how to color parts and terrain, see [Parts](/docs/en-us/parts.md#color) and [Environmental Terrain](/docs/en-us/parts/terrain.md#custom-terrain-colors). | Material | RGB Value | Color | | --- | --- | --- | | `Enum.Material.Asphalt\|Asphalt` | `[80, 84, 84]` | `rgb(80,84,84)` | | `Enum.Material.Basalt\|Basalt` | `[75, 74, 74]` | `rgb(75,74,74)` | | `Enum.Material.Brick\|Brick` | `[138, 97, 73]` | `rgb(138,97,73)` | | `Enum.Material.Cardboard\|Cardboard` | `[255, 206, 152]` | `rgb(255,206,152)` | | `Enum.Material.Carpet\|Carpet` | `[163, 162, 165]` | `rgb(163,162,165)` | | `Enum.Material.CeramicTiles\|CeramicTiles` | `[181, 173, 156]` | `rgb(181,173,156)` | | `Enum.Material.ClayRoofTiles\|ClayRoofTiles` | `[255, 142, 87]` | `rgb(255,142,87)` | | `Enum.Material.Cobblestone\|Cobblestone` | `[134, 134, 118]` | `rgb(134,134,118)` | | `Enum.Material.Concrete\|Concrete` | `[152, 152, 152]` | `rgb(152,152,152)` | | `Enum.Material.CorrodedMetal\|CorrodedMetal` | `[104, 140, 173]` | `rgb(104,140,173)` | | `Enum.Material.CrackedLava\|CrackedLava` | `[255, 24, 67]` | `rgb(255,24,67)` | | `Enum.Material.DiamondPlate\|DiamondPlate` | `[168, 175, 176]` | `rgb(168,175,176)` | | `Enum.Material.Fabric\|Fabric` | `[194, 193, 168]` | `rgb(194,193,168)` | | `Enum.Material.Foil\|Foil` | `[168, 175, 176]` | `rgb(168,175,176)` | | `Enum.Material.ForceField\|ForceField` | `[163, 162, 165]` | `rgb(163,162,165)` | | `Enum.Material.Glacier\|Glacier` | `[221, 228, 229]` | `rgb(221,228,229)` | | `Enum.Material.Glass\|Glass` | `[138, 167, 168]` | `rgb(138,167,168)` | | `Enum.Material.Granite\|Granite` | `[149, 146, 139]` | `rgb(149,146,139)` | | `Enum.Material.Grass\|Grass` | `[111, 126, 62]` | `rgb(111,126,62)` | | `Enum.Material.Ground\|Ground` | `[140, 130, 104]` | `rgb(140,130,104)` | | `Enum.Material.Ice\|Ice` | `[204, 210, 223]` | `rgb(204,210,223)` | | `Enum.Material.LeafyGrass\|LeafyGrass` | `[106, 134, 64]` | `rgb(106,134,64)` | | `Enum.Material.Leather\|Leather` | `[110, 73, 53]` | `rgb(110,73,53)` | | `Enum.Material.Limestone\|Limestone` | `[255, 243, 192]` | `rgb(255,243,192)` | | `Enum.Material.Marble\|Marble` | `[122, 122, 122]` | `rgb(122,122,122)` | | `Enum.Material.Metal\|Metal` | `[168, 175, 176]` | `rgb(168,175,176)` | | `Enum.Material.Mud\|Mud` | `[121, 112, 98]` | `rgb(121,112,98)` | | `Enum.Material.Neon\|Neon` | `[163, 162, 165]` | `rgb(163,162,165)` | | `Enum.Material.Pavement\|Pavement` | `[143, 144, 135]` | `rgb(143, 144, 135)` | | `Enum.Material.Pebble\|Pebble` | `[122, 122, 118]` | `rgb(122,122,118)` | | `Enum.Material.Plaster\|Plaster` | `[204, 142, 105]` | `rgb(204,142,105)` | | `Enum.Material.Plastic\|Plastic` | `[163, 162, 165]` | `rgb(163,162,165)` | | `Enum.Material.Rock\|Rock` | `[99, 100, 102]` | `rgb(99,100,102)` | | `Enum.Material.RoofShingles\|RoofShingles` | `[66, 66, 66]` | `rgb(66,66,66)` | | `Enum.Material.Rubber\|Rubber` | `[32, 32, 32]` | `rgb(32,32,32)` | | `Enum.Material.Salt\|Salt` | `[255, 255, 254]` | `rgb(255,255,254)` | | `Enum.Material.Sand\|Sand` | `[207, 203, 167]` | `rgb(207,203,167)` | | `Enum.Material.Sandstone\|Sandstone` | `[148, 124, 95]` | `rgb(148,124,95)` | | `Enum.Material.Slate\|Slate` | `[88, 89, 86]` | `rgb(88,89,86)` | | `Enum.Material.SmoothPlastic\|SmoothPlastic` | `[163, 162, 165]` | `rgb(163,162,165)` | | `Enum.Material.Snow\|Snow` | `[235, 253, 255]` | `rgb(235,253,255)` | | `Enum.Material.Wood\|Wood` | `[172, 148, 108]` | `rgb(172,148,108)` | | `Enum.Material.WoodPlanks\|WoodPlanks` | `[172, 148, 108]` | `rgb(172,148,108)` | ### Default physical properties The following table lists each material's default physical properties as detailed in the `Datatype.PhysicalProperties` reference. For information on customizing physical properties, see [physical properties](#physical-properties). | Material | Density | Elasticity | ElasticityWeight | Friction | FrictionWeight | | --- | --- | --- | --- | --- | --- | | `Enum.Material.Asphalt\|Asphalt` | `2.36` | `0.2` | `1` | `0.8` | `0.3` | | `Enum.Material.Basalt\|Basalt` | `2.691` | `0.15` | `1` | `0.7` | `0.3` | | `Enum.Material.Brick\|Brick` | `1.922` | `0.15` | `1` | `0.8` | `0.3` | | `Enum.Material.Cardboard\|Cardboard` | `0.7` | `0.05` | `2` | `0.5` | `1` | | `Enum.Material.Carpet\|Carpet` | `1.1` | `0.25` | `2` | `0.4` | `1` | | `Enum.Material.CeramicTiles\|CeramicTiles` | `2.4` | `0.2` | `1` | `0.51` | `1` | | `Enum.Material.ClayRoofTiles\|ClayRoofTiles` | `2` | `0.2` | `1` | `0.51` | `1` | | `Enum.Material.Cobblestone\|Cobblestone` | `2.691` | `0.17` | `1` | `0.5` | `1` | | `Enum.Material.Concrete\|Concrete` | `2.403` | `0.2` | `1` | `0.7` | `0.3` | | `Enum.Material.CorrodedMetal\|CorrodedMetal` | `7.85` | `0.2` | `1` | `0.7` | `1` | | `Enum.Material.CrackedLava\|CrackedLava` | `2.691` | `0.15` | `1` | `0.65` | `1` | | `Enum.Material.DiamondPlate\|DiamondPlate` | `7.85` | `0.25` | `1` | `0.35` | `1` | | `Enum.Material.Fabric\|Fabric` | `0.7` | `0.05` | `1` | `0.35` | `1` | | `Enum.Material.Foil\|Foil` | `2.7` | `0.25` | `1` | `0.4` | `1` | | `Enum.Material.ForceField\|ForceField` | `2.403` | `0.2` | `1` | `0.25` | `1` | | `Enum.Material.Glacier\|Glacier` | `0.919` | `0.15` | `1` | `0.05` | `2` | | `Enum.Material.Glass\|Glass` | `2.403` | `0.2` | `1` | `0.25` | `1` | | `Enum.Material.Granite\|Granite` | `2.691` | `0.2` | `1` | `0.4` | `1` | | `Enum.Material.Grass\|Grass` | `0.9` | `0.1` | `1.5` | `0.4` | `1` | | `Enum.Material.Ground\|Ground` | `0.9` | `0.1` | `1` | `0.45` | `1` | | `Enum.Material.Ice\|Ice` | `0.919` | `0.15` | `1` | `0.02` | `3` | | `Enum.Material.LeafyGrass\|LeafyGrass` | `0.9` | `0.1` | `2` | `0.4` | `2` | | `Enum.Material.Leather\|Leather` | `0.86` | `0.25` | `1` | `0.35` | `1` | | `Enum.Material.Limestone\|Limestone` | `2.691` | `0.15` | `1` | `0.5` | `1` | | `Enum.Material.Marble\|Marble` | `2.563` | `0.17` | `1` | `0.2` | `1` | | `Enum.Material.Metal\|Metal` | `7.85` | `0.25` | `1` | `0.4` | `1` | | `Enum.Material.Mud\|Mud` | `0.9` | `0.07` | `4` | `0.3` | `3` | | `Enum.Material.Neon\|Neon` | `0.7` | `0.2` | `1` | `0.3` | `1` | | `Enum.Material.Pavement\|Pavement` | `2.691` | `0.17` | `1` | `0.5` | `0.3` | | `Enum.Material.Pebble\|Pebble` | `2.403` | `0.17` | `1.5` | `0.4` | `1` | | `Enum.Material.Plaster\|Plaster` | `0.75` | `0.2` | `1` | `0.6` | `0.3` | | `Enum.Material.Plastic\|Plastic` | `0.7` | `0.5` | `1` | `0.3` | `1` | | `Enum.Material.Rock\|Rock` | `2.691` | `0.17` | `1` | `0.5` | `1` | | `Enum.Material.RoofShingles\|RoofShingles` | `2.36` | `0.2` | `1` | `0.8` | `0.3` | | `Enum.Material.Rubber\|Rubber` | `1.3` | `0.95` | `2` | `1.5` | `3` | | `Enum.Material.Salt\|Salt` | `2.165` | `0.05` | `1` | `0.5` | `1` | | `Enum.Material.Sand\|Sand` | `1.602` | `0.05` | `2.5` | `0.5` | `5` | | `Enum.Material.Sandstone\|Sandstone` | `2.691` | `0.15` | `1` | `0.5` | `5` | | `Enum.Material.SmoothPlastic\|SmoothPlastic` | `0.7` | `0.5` | `1` | `0.2` | `1` | | `Enum.Material.Slate\|Slate` | `2.691` | `0.2` | `1` | `0.4` | `1` | | `Enum.Material.Snow\|Snow` | `0.9` | `0.03` | `4` | `0.3` | `3` | | `Enum.Material.Wood\|Wood` | `0.35` | `0.2` | `1` | `0.48` | `1` | | `Enum.Material.WoodPlanks\|WoodPlanks` | `0.35` | `0.2` | `1` | `0.48` | `1` | --- title: "Meshes" url: /docs/en-us/parts/meshes last_updated: 2026-06-29T19:34:03Z description: "Meshes are a collection of vertices, edges, and faces that make up 3D objects." --- # Meshes `Class.MeshPart` objects are descendants of the `Class.BasePart` class. They represent **meshes**, which are collections of vertices, edges, and faces that make up a 3D object. Unlike parts, which you can directly create in Studio, you need to use a third-party modeling application like Blender or Maya to create meshes, then import them into Studio as `Class.MeshPart` objects. After importing a mesh into Studio, you can customize its rendering properties, such as textures, level of detail, and collision fidelity. In addition to importing your own meshes, you can also browse and select from user-uploaded meshes using the [Creator Store](/docs/en-us/production/creator-store.md). Roblox supports many types of meshes, as long as they adhere to the [general mesh specifications](/docs/en-us/art/modeling/specifications.md). A basic mesh consists of at least one mesh object and one texture: ![A tree mesh object without a texture.](../assets/art/Basic-Mesh-Example.png)_A mesh object sets the shape and geometry of the 3D object_ ![The texture image map for the leaves of the tree in the previous image.](../assets/art/Basic-Texture-Example.png)_A texture image map applies a surface appearance and color_ ![A tree mesh object with the leaves texture applied.](../assets/art/Basic-Mesh-Combined-Example.png)_The mesh and texture combine to make a unique custom 3D object_ Studio also supports meshes with components for creating avatar character models or accessories, such as [rigging and skinning](#rigging-and-skinning) data. > **Info:** You can sell `Class.MeshPart` assets as avatar bodies, accessories, and clothing on the Marketplace. See [Avatar](/docs/en-us/avatar.md) for more information. ## Import meshes You can import meshes into Studio using the [Importer](/docs/en-us/studio/importer.md). With this tool, you can preview and examine meshes before importing them into your workspace or **Toolbox**, such as verifying texture, rigging, skinning, and animation data. It also flags issues and rejects meshes with error. If the mesh file you are importing contains objects using specific naming conventions or contains facial animation data, the Importer automatically detects and converts them into the following objects instead of `Class.MeshPart`: - `Class.Attachment` — Objects with `_Att` at the end of their names. - `Class.WrapTarget` — Objects with `_OuterCage` at the end of their names. - `Class.WrapLayer` — Objects with both `_InnerCage` `_OuterCage` at the end of their names. - `Class.FaceControls` — Objects containing avatar character heads and the appropriate facial animation data. If you want to bulk-import meshes along with non-3D assets, such as images and audio, you can use the [Asset Manager](/docs/en-us/projects/assets/manager.md); note, however, that it doesn't support importing meshes with rigging, skinning, and animation data, accessories, or characters with facial animations. ## Customize meshes Unlike basic parts, meshes have more customization options that you can adjust for advanced rendering fidelity. ### Texture **Textures** contribute to the visual appearance of meshes by adding 2D details such as color and patterns. Studio allows you to either apply one texture using the `Class.MeshPart.TextureID` property, or apply up to four physically-based rendering (PBR) textures within a `Class.SurfaceAppearance` child object of the mesh or made with a `Class.MaterialVariant` and set in the mesh's `Class.MeshPart.MaterialVariant` property. PBR textures allow you to represent realistic shading and lighting by using multiple types of texture images, or maps, on a single object. #### PBR Textures Studio supports four PBR texture maps, each corresponding to a visual characteristic of an object's surface appearance. Combining multiple texture maps can more accurately simulate color, roughness, and reflectivity in any lighting environment, and enhance the visual elements of your assets and environment. For more information on PBR textures and the texture maps, see [PBR Textures](/docs/en-us/art/modeling/surface-appearance.md). ![Example of SurfaceAppearance applied to a Bush Mesh](../assets/modeling/surface-appearance/SurfaceAppearance-Example-1.jpg)![Example of SurfaceApperance applied to a Rock Mesh](../assets/modeling/surface-appearance/SurfaceAppearance-Example-3.jpg) You can apply PBR textures using one of the following objects: - `Class.SurfaceAppearance` — Applies PBR textures to the mesh surface and doesn't affect its geometry. - `Class.MaterialVariant` — Represents a custom material that not only applies PBR textures to the mesh surface but also adds physical properties. To add PBR textures to a mesh: #### SurfaceAppearance 1. In the [Explorer](/docs/en-us/studio/explorer.md) window, hover over the `Class.MeshPart` object. Click the **⊕** button and select `Class.SurfaceAppearance`. 2. In the [Properties](/docs/en-us/studio/properties.md) window, edit the properties corresponding to the PBR texture maps. #### MaterialVariant 1. In the [Explorer](/docs/en-us/studio/explorer.md) window, hover over the `Class.MaterialService`. Click the **⊕** button and select `Class.MaterialVariant`. 2. In the [Properties](/docs/en-us/studio/properties.md) window, edit the properties corresponding to the PBR texture maps. 3. Name the new `Class.MaterialVariant` to a name of your choice. 4. Select the `Class.MeshPart` and, in the [Properties](/docs/en-us/studio/properties.md) window, set the `Class.MaterialVariant` to the one you just created. > **Info:** If you both add `Class.SurfaceAppearance` as a child object and set the `Class.MaterialVariant` in the mesh's properties, Studio only applies the texture map settings of the `Class.SurfaceAppearance` object to the mesh. The mesh still has all other settings of the `Class.MaterialVariant` object, such as custom physical properties. #### Single Texture If the mesh you import to Studio doesn't come with texture data, or you want to change the existing texture, use the following steps to add a single texture: 1. [Import the texture file](/docs/en-us/projects/assets/manager.md#asset-import). The file must follow the [texture specifications](/docs/en-us/art/modeling/texture-specifications.md). Upon completion, Studio automatically assigns and prompts an asset ID. 2. Copy the asset ID. 3. In the **Explorer** window, select the **MeshPart** object. 4. In the **Properties** window, select the **TextureID** field and paste the asset ID of the texture. > **Warning:** If your mesh has existing PBR textures, setting a Texture ID can't override the PBR textures. ### Level of detail You can dynamically control a mesh's level of detail using its `Enum.RenderFidelity` property. The default value is `Enum.RenderFidelity.Automatic|Automatic`, meaning the mesh's detail is based on its distance from the camera as outlined in the following table. | Distance from camera | Render fidelity | Example | | --- | --- | --- | | Less than 250 studs | Highest | ![A potion mesh object that is rendered in the highest level of detail.](../assets/modeling/meshes/Render-Fidelity-High.jpg) | | 250-500 studs | Medium | ![A potion mesh object that is rendered in a medium level of detail. Its edges are more jagged than the previous image.](../assets/modeling/meshes/Render-Fidelity-Medium.jpg) | | 500 or more studs | Lowest | ![A potion mesh object that is rendered in the lowest level of detail. Its edges are more jagged than the previous image, and the liquid is no longer shiny.](../assets/modeling/meshes/Render-Fidelity-Low.jpg) | ### Collision fidelity **Collision fidelity** determines how closely the visual representation of a mesh matches its physical bounds. The `Class.MeshPart.CollisionFidelity` property has the following options, in order of fidelity and performance impact from lowest to highest: - **Box** — Creates a bounding collision box, ideal for small or non‑interactive objects. - **Hull** — Generates a convex hull, suitable for objects with less pronounced indentations or cavities. - **Default** — Produces an approximate collision shape that supports concavity, suitable for complex objects with semi-detailed interaction needs. - **PreciseConvexDecomposition** — Offers the most precise fidelity but still not a 1:1 representation of the visual. This option has the most expensive performance cost and takes longer for the engine to compute. #### Original Mesh ![Original mesh of castle tower](../assets/physics/collisions/Collision-Fidelity-MeshPart.jpg) #### Default ![Collision fidelity of Default shown for mesh](../assets/physics/collisions/Collision-Fidelity-Default.jpg) #### Box ![Collision fidelity of Box shown for mesh](../assets/physics/collisions/Collision-Fidelity-Box.jpg) #### Hull ![Collision fidelity of Hull shown for mesh](../assets/physics/collisions/Collision-Fidelity-Hull.jpg) #### Precise ![Collision fidelity of PreciseConvexDecomposition shown for mesh](../assets/physics/collisions/Collision-Fidelity-Precise.jpg) > **Success:** To view collision fidelity in Studio, toggle on **Collision fidelity** from the [Visualization Options](/docs/en-us/studio/ui-overview.md#visualization-options) widget in the upper‑right corner of the 3D viewport. For more information on the performance impact of collision fidelity options and how to mitigate them, see [Improve performance](/docs/en-us/performance-optimization/improve.md#physics-computation). For an in-depth walkthrough on how to choose a collision fidelity option that balances your precision and performance requirements, see [Set physics and rendering parameters](/docs/en-us/tutorials/curriculums/environmental-art/optimize-your-experience.md#review-physics-and-rendering-parameters). ## Rigging and skinning **Rigging** is the process of connecting a mesh with an internal poseable skeleton rig. Rigged meshes allow mesh surfaces to rotate and move using internal bones within a model, such as a character's knee or elbow. **Skinning** a rigged mesh allows the rigged mesh object to deform, stretch, and bend in a more realistic manner. _Without skinning, the entire head mesh rotates on a single axis_ _With skinning, the head mesh bends naturally at the neck, and the bottom of the neck stays connected to the torso_ For more information on rigging and skinning, see [Rigging and skinning](/docs/en-us/art/modeling/rigging.md). After rigging a mesh, you can add animation and poses to it using the **Animation Editor**. See [Create an animation](/docs/en-us/animation/editor.md#create-an-animation) for more information. Marketplace 3D assets, such as avatar clothing and bodies, also require rigging and skinning. See [Avatar](/docs/en-us/avatar.md) for more information on requirements for Marketplace assets. --- title: "Model generation" url: /docs/en-us/parts/model-generation last_updated: 2026-06-29T19:34:03Z description: "How text prompts and preset schemas can generate both static 3D assets and fully functional models such as vehicles that drive, planes that fly, and weapons that shoot." --- # Model generation Through text prompts and preset schemas, you (and players in‑experience) can generate both static 3D assets and **fully functional** models such as vehicles that drive, planes that fly, and weapons that shoot. This guide demonstrates how to use `Class.GenerationService:GenerateModelAsync()|GenerateModelAsync()` as part of a dynamic, player‑driven creative experience. ## Overview `Class.GenerationService:GenerateModelAsync()|GenerateModelAsync()` enables generation of multi‑mesh geometries with the following advantages: - The provided [schema](#schema-based-generation) defines how the generated geometry will be broken into parts. - Any object generated in-experience will be replicated and visible to all players, allowing them to build together in real‑time. - Generated meshes are higher fidelity with better overall forms, silhouettes, more detail, and fewer artifacts. Generated textures are more coherent and detailed, with less graininess and less pronounced baked‑in lighting. - Generation times have lower latency, enabling quicker and more responsive creation in experiences. - You and even players during gametime can specify bounding boxes to suggest size and proportions of the generated model. - An optional max triangle count keeps generated objects performant for your experience. ## Schema-based generation To create articulated, functional objects, the generated geometry must be separated into multiple `Class.MeshPart|MeshParts`. For example, to create a drivable car with wheels that spin and steer, at least five `Class.MeshPart|MeshParts` are needed: one for the car body and four for each of the car's wheels. `Class.GenerationService:GenerateModelAsync()|GenerateModelAsync()` achieves this through a **schema** that lists the parts to generate, as shown in the following code snippet: ```lua local GenerationService = game:GetService("GenerationService") local Workspace = game:GetService("Workspace") -- Set up inputs for the generated geometry local inputs = { TextPrompt = "green dragon car with 4 wheels" } -- Set schema to the predefined five-model car chassis local schema = { PredefinedSchema = "Car5" } -- Make the call to generate the model local success, result, metadata = pcall(function() return GenerationService:GenerateModelAsync(inputs, schema) end) if success then -- Scale model to target size and position near world center local targetSize = 16 local modelSize = result:GetExtentsSize() local scaleFactor = targetSize / math.max(modelSize.X, modelSize.Y, modelSize.Z) result:ScaleTo(scaleFactor) result:PivotTo(CFrame.new(0, result:GetExtentsSize().Y / 2, -40)) -- Anchor all parts so that the meshes don't fall apart for _, descendant in result:GetDescendants() do if descendant:IsA("BasePart") then descendant.Anchored = true end end -- Name the model and parent it to workspace result.Name = "BasicDragonCarGeneration" result.Parent = Workspace else warn(result) end ``` > **Info:**`Class.GenerationService:GenerateModelAsync()|GenerateModelAsync()` currently supports two fixed schemas: `Car5` which produces a car with a body and four wheels, and `Body1` which yields a single `Class.MeshPart` object. The following image shows the expected output and its construction as it appears in the [Explorer](/docs/en-us/studio/explorer.md) hierarchy: ## Retargetable scripts In Roblox, **behaviors** typically encompass the scripts, attachments, [constraints](/docs/en-us/physics.md#constraints), and other instance types attached to static textured geometries to make them functional. Since `Class.GenerationService:GenerateModelAsync()|GenerateModelAsync()` generates a wide range of geometries on‑the‑fly, **retargetable scripts** must be attached to automatically adapt behaviors to expected functionality. For example, consider an experience where players get to make their own drivable cars. `Class.GenerationService:GenerateModelAsync()|GenerateModelAsync()` takes the player prompt and generates five distinct meshes (body and four wheels) via the `Car5` [schema](#schema-based-generation). Then, a preset retargetable script adapts its driving behavior to the size and shape of the model to ensure the car is drivable and its wheels spin realistically. ```mermaid block-beta columns 1 block:Main columns 5 Prompt(["   prompt   
   (creator)   "]) Generate{" Geometry 
 Generator 
(Roblox)"} Geometry(("GEOMETRY
   single / multi-mesh   ")) space:2 Schema(["   schema   "]) space Retarget{"    Retarget    
behavior
(creator)"} space FunctionalModel((("    FUNCTIONAL    
MODEL"))) Prompt --> Generate Generate --> Geometry Geometry --> Retarget Retarget --> FunctionalModel Schema --> Generate end style Main fill-opacity: 0, stroke-opacity: 0; style Prompt color:white, fill:RoyalBlue; style Schema color:white, fill:RoyalBlue; style Generate color:white, fill:#9933FF, fill-opacity:0.7; style Retarget color:white, fill:RoyalBlue; style Geometry color:white, fill:SeaGreen style FunctionalModel color:white, fill:SeaGreen ``` To test retargetable scripts: 1. Download the [`Behaviors.rbxm`](../assets/modeling/model-generation/Behaviors.rbxm) file. 2. In Studio's [Explorer](/docs/en-us/studio/explorer.md) hierarchy, right‑click `Class.ReplicatedStorage`, select **Insert** ⟩ **Import Roblox Model**, and choose the downloaded file. The model unpacks into several folders. 3. Copy and paste one of the following code snippets into a server `Class.Script` within `Class.ServerScriptService`, depending on the object type you'd like to generate. #### Car This example generates a functional car with a brake, nitro boost, audio and visual effects, and an on‑screen UI speedometer.```lua local GenerationService = game:GetService("GenerationService") local ReplicatedStorage = game:GetService("ReplicatedStorage") local Workspace = game:GetService("Workspace") -- Set up inputs for the generated geometry local inputs = { TextPrompt = "green dragon car with 4 wheels" } -- Set schema to the predefined five-model car chassis local schema = { PredefinedSchema = "Car5" } -- Make the call to generate the model local success, result, metadata = pcall(function() return GenerationService:GenerateModelAsync(inputs, schema) end) if success then -- Scale model to target size local targetSize = 16 local modelSize = result:GetExtentsSize() local scaleFactor = targetSize / math.max(modelSize.X, modelSize.Y, modelSize.Z) result:ScaleTo(scaleFactor) -- Load "makeFunctional" module for "CarBehavior" local makeFunctional = require(ReplicatedStorage.Behaviors.CarBehavior.makeFunctional) -- Reference car parts by names guaranteed through the "Car5" schema local body = result:FindFirstChild("body") local frontLeftWheel = result:FindFirstChild("front left wheel") local frontRightWheel = result:FindFirstChild("front right wheel") local rearLeftWheel = result:FindFirstChild("rear left wheel") local rearRightWheel = result:FindFirstChild("rear right wheel") -- Set behavioral configuration parameters local config = { seatPosition = "inside", invisibleWhenSeated = false } -- Attach behavior via the "makeFunctional" script local functionalModel = makeFunctional(result, frontLeftWheel, frontRightWheel, rearLeftWheel, rearRightWheel, body, config) -- Position the model near world center functionalModel:PivotTo(CFrame.new(0, functionalModel:GetExtentsSize().Y / 2, -40)) functionalModel.Name = "BasicDragonCarGeneration" functionalModel.Parent = Workspace else warn(result) end ``` > **Success:** To test other generated models with retargetable scripts in the `Car5` schema, change the `TextPrompt` string value on line 7 and playtest again. Ideas include: - `"yellow sports car with a spoiler"` - `"purple bat car with 4 wheels"` - `"giant monster truck with spiked roof"`#### Airplane This example generates a basic plane using the `Body1` schema that flies through player input.```lua local GenerationService = game:GetService("GenerationService") local ReplicatedStorage = game:GetService("ReplicatedStorage") local Workspace = game:GetService("Workspace") -- Set up inputs for the generated geometry local inputs = { TextPrompt = "basic paper airplane with two wings" } -- Set schema to the predefined static body local schema = { PredefinedSchema = "Body1" } -- Make the call to generate the model local success, result, metadata = pcall(function() return GenerationService:GenerateModelAsync(inputs, schema) end) if success then -- Scale model to target size local targetSize = 30 local modelSize = result:GetExtentsSize() local scaleFactor = targetSize / math.max(modelSize.X, modelSize.Y, modelSize.Z) result:ScaleTo(scaleFactor) -- Load "makeFunctional" module for "AirplaneBehavior" local makeFunctional = require(ReplicatedStorage.Behaviors.AirplaneBehavior.makeFunctional) -- Set behavioral configuration parameters local config = { seatPosition = "ontop", forwardSpeed = 80, turnSpeed = 8 } -- Attach behavior via the "makeFunctional" script local functionalModel = makeFunctional(result, config) -- Place model slightly above ground to let it fall into place local boundingBox = functionalModel:GetBoundingBox() local location = Vector3.new(0, boundingBox.Y / 2 + 3, -40) functionalModel:PivotTo(CFrame.new(location)) functionalModel.Name = "BasicPlaneGeneration" functionalModel.Parent = Workspace else warn(result) end ```
--- title: "Models" url: /docs/en-us/parts/models last_updated: 2026-06-29T19:34:03Z description: "Models are container objects for geometric groupings like parts, Motor6D objects, and other models." --- # Models **Models** are containers for physical objects that you can use to organize your workspace and group your assets, such as parts, welds, or joints. While models often contain connected parts known as an [assembly](/docs/en-us/physics/assemblies.md), they can also contain any number of individual parts or objects, such as scripts, attachments, or other models. To demonstrate, avatar characters are a single `Class.Model` containing the appropriate humanoid parts, joints, and scripts necessary for runtime behavior: ![An example character model of a humanoid monster girl in a dark purple dress.](../assets/modeling/model-objects/Model-Example-3D.jpg)_A model named Octavia_ ![The groupings which comprise the Octavia model in Studio's Explorer window.](../assets/modeling/model-objects/Model-Example-Hierarchy.png)_The groupings that comprise the model_ ## Create models > **Warning:** You can create models using any open use or restricted asset, but any restricted assets that you don't have [explicit permission](/docs/en-us/projects/assets/privacy.md#view-permissions) to use are **not** visible or audible at runtime unless the experience itself has permission to use those assets. There are two ways to create models: - Group objects together to automatically form a `Class.Model` object. - Insert an empty `Class.Model` object into the Workspace, then manually add child objects to the model. To create a model using the grouping method: 1. In the **Explorer** window or 3D viewport, select every object that you want to group into a model. 2. Right-click on one of the objects and select **Group**, or press `Ctrl``G` on Windows or `⌘``G` on Mac. A new `Class.Model` object displays with all of the objects that make up the model nested underneath.![A close up view of the Explorer window. A Model object is highlighted with three nested children.](../assets/modeling/model-objects/Model-Group-Simple.png) > **Info:** To completely ungroup a model back to its original objects, right-click it and select **Ungroup**, or press `Ctrl``U` on Windows or `⌘``U` on Mac. ### Set a primary part If you have a model with parts that are joined together through physical joints like `Class.WeldConstraint|WeldConstraints` or `Class.Motor6D|Motor6Ds`, you should specify a `Class.BasePart` within the model to become a `Class.Model.PrimaryPart|PrimaryPart`. A model's `Class.Model.PrimaryPart|PrimaryPart` is the physical reference that specifies which `Class.BasePart` the pivot point and bounding box should move with when the model changes position or orientation. To set a primary part: 1. In the **Explorer** window, select a model. 2. In the **Properties** window, select the **PrimaryPart** property. Your cursor changes. 3. Back in the **Explorer** window, select the part that you want to become your primary part. ## Select models As you hover over models in the viewport, they are outlined to indicate their potential selection. You can select an outlined model by clicking it, or you can select multiple models by holding `Shift`, `Ctrl`, or `⌘` as you hover over and click them. ![A warehouse view in the 3D viewport. Multiple models are selected and highlighted with a light blue outline.](../assets/studio/general/Editor-Window-Object-Selection.jpg) As models typically contain multiple child [parts](/docs/en-us/parts.md) or [meshes](/docs/en-us/parts/meshes.md), some children may be hidden from view. To select a specific child without moving the camera around or locating the child in the [Explorer](/docs/en-us/studio/explorer.md) hierarchy, click while holding `Alt` on Windows or `⌥` on Mac to perform [selection cycling](/docs/en-us/studio/ui-overview.md#selection-cycling). _Selection cycling_ ## Transform models You can move, scale, or rotate a model using the Studio [transform](/docs/en-us/parts.md#transform-parts) tools. Unless you've set a [primary part](#set-a-primary-part), a model transforms based on the center of its bounding box. Additionally, within a `Class.Script` or `Class.LocalScript`, you can move or rotate a model through the following methods: | Method | Description | | --- | --- | | `Class.Model:MoveTo()\|MoveTo()` | Moves the model's `Class.Model.PrimaryPart\|PrimaryPart` to the given position. If a primary part has not been specified, the root part of the model will be used. | | `Class.PVInstance:PivotTo()\|PivotTo()` | Transforms the model along with all of its descendant `Class.PVInstance\|PVInstances` such that the pivot is located at the specified `Datatype.CFrame`. | | `Class.Model:TranslateBy()\|TranslateBy()` | Shifts a model by the given `Datatype.Vector3` offset, preserving the model's orientation. | ## Model behaviors While models act similarly to `Class.Folder` objects for most purposes in your experience, they also exhibit some unique behaviors. ### Character models When a `Class.Humanoid` is present inside a model that contains a `Class.Part` named **Head**, Roblox displays a name and/or health bar above that part. For more information, see [Character Name/Health Display](/docs/en-us/characters/name-health-display.md). ![Character display information above an in-experience avatar.](../assets/avatar/name-health-display/Display-Indicated.jpg) ### Destroy height To prevent parts that have fallen off of an experience's map from continuing to fall forever, Studio automatically destroys parts that fall below the `Class.Workspace.FallenPartsDestroyHeight` value. If a part destroyed due to this behavior is the last part in a model, then that model will also be destroyed. ## Model streaming Instance [streaming](/docs/en-us/workspace/streaming.md) dynamically loads and unloads `Class.Model|Models` on a player's device as their character explores the 3D world. With streaming enabled, you can specify the way each model should be treated under streaming behavior. For example, a model set to [Persistent](/docs/en-us/workspace/streaming.md#persistent) will never stream out, or a model set to [Atomic](/docs/en-us/workspace/streaming.md#atomic) will stream in and out as a single unit with all of its descendants. For more information, see [per-model streaming controls](/docs/en-us/workspace/streaming.md#per-model-streaming-controls). Because the 3D content that exists on the client changes dynamically in a streaming-enabled experience, models might suddenly disappear, which can be visually jarring for the player. To avoid this situation, you can set specific models to render as optimized "SLIM" meshes or as lower resolution "imposter" meshes when streamed out. See [model level of detail](/docs/en-us/workspace/streaming/techniques.md#model-level-of-detail). ## Upload models When you upload or import a `.gltf`, `.fbx` and `.obj` model file to the cloud using the [Importer](/docs/en-us/art/accessories/creating-rigid/importing.md), Roblox represents it as a cloud-based asset with a unique corresponding ID. This cloud-based asset system allows you to store models through Roblox and reuse them across the platform in various contexts without maintaining local copies as part of each saved Studio experience. To upload a model you made in Studio: 1. In the **Explorer** window, right-click your model. A contextual menu displays. 2. Select **Save to Roblox**. The **Asset Configuration** window displays. 3. In the **Asset Configuration** window, 1. Set **Content Type** to **Development Item**. 2. Set **Asset Category** to **Model**. 3. Complete the following fields: - **Title** - The name of your model. - **Description** - A short description of your model. - **Creator** - Use the dropdown to select if you'd like to publish this asset as an individual or as part of an associated group. - **Genre** - The genre of your model. 4. Click the **Save** button. After a moment, the **Asset Configuration** window displays your model's asset ID that you can use in your projects or share with other creators, groups, or experiences. For more information on granting permission to collaborators and experiences so that they can use your models, see [Asset privacy](/docs/en-us/projects/assets/privacy.md) ## Distribute and sell models You can publicly distribute and sell your own models on the Creator Store for United States Dollars (USD), as long as the models have less than 15,000 dependencies. Each of these dependencies must be an: - Open use asset or a restricted asset that you created, excluding audio and video. - Audio or video asset that you acquired from the Creator Store. This monetization method lets you earn 100% of net proceeds on transactions, bypassing platform fees and DevEx rates. For more information on asset and seller requirements, including how to set up a seller account to set prices and receive payouts, see [Creator Store - Requirements](/docs/en-us/production/creator-store.md#requirements). > **Info:** If you want to distribute a model with a child audio asset that you created, you must distribute the audio asset on the Creator Store **_before_** making it a child of the model in order for the audio to be audible at runtime. When creating models for the Creator Store, ensure: - The model and its dependencies adhere to the [Community Rules](https://en.help.roblox.com/hc/articles/203313410), [Terms of Use](https://en.help.roblox.com/hc/articles/115004647846), [DMCA Guidelines](/docs/en-us/production/publishing/dmca-guidelines.md), and Creator Store requirements. - The model is set to a reasonable scale and orientation so that it's usable out of the box when inserted from the Creator Store. - All of the model's dependencies import into Studio without any warnings. - Each mesh dependency contains no more than 20,000 triangles. - Each texture dependency meets Roblox's [texture specifications](/docs/en-us/art/modeling/texture-specifications.md). --- title: "Procedural models" url: /docs/en-us/parts/procedural-models last_updated: 2026-06-29T19:34:03Z description: "Procedural models let you build parameter-driven 3D objects using code or AI." --- # Procedural models `Class.ProceduralModel|ProceduralModels` are a type of parameter-driven `Class.Model` that let you build and generate 3D objects using code. Instead of manually editing a model's content, you define its structure through a **generator module** and a set of attributes. When those attributes change, or when the model is resized, the generator automatically runs to update the model. Procedural models make it easier to: - Adjust models non-destructively by changing parameters instead of rebuilding them. For example, changing the `StepSize` value on a spiral staircase automatically regenerates the staircase with the updated number of steps. - Automatically integrate with existing Studio systems like undo/redo, Team Create, and dragger tools by implementing a single `OnGenerate` function. - Keep performance high, with models behaving like standard objects until their parameters change. - Generate content both at edit time and at runtime, using the same system in Studio and in-game. - Share parametized models on the Creator Store, making assets more flexible and customizable. _Example: Procedural model that dynamically adds more chairs as the table's width or length increases._## Insert procedural models You can add procedural models to your game in the following ways: - Manually insert a `ProceduralModel` object into the Workspace. This option includes a template generator `ModuleScript` to help you get started.![A close up view of the Explorer window. A Model object is highlighted with three nested children.](../assets/modeling/procedural-models/Procedural-Model-Explorer.png) - Generate a procedural model using [Assistant](/docs/en-us/assistant/guide.md#generate-procedural-models) or the [Roblox Studio MCP server](/docs/en-us/studio/mcp.md). - Insert a procedural model from the Creator Store to use or customize an existing model. > **Warning:** Procedural models can make permanent changes to your game at edit time. To protect against unwanted changes, any procedural model inserted from the Creator Store will have `Sandboxed = true` automatically enabled on its generator module. To customize how procedural models are generated, you can define your own generator modules. ## OnGenerate function The `OnGenerate` function is at the core of every `ProceduralModel` in the form of `OnGenerate(params, targetContainer)`. This developer-defined function is responsible for generating the model's contents based on the provided parameters and writing the results into the given container. `OnGenerate` has two primary responsibilities: - Process the input parameters to determine the model's structure - Write the generated results according to that structure into the provided `targetContainer` The following sections describe: - When and how the engine invokes `OnGenerate` - How the engine processes the contents written to the `targetContainer` ### Generator modules The `OnGenerate` function is defined inside a `ModuleScript` known as a **generator module**. The `ProceduralModel` identifies which generator module to use through its `Generator` property. Generator modules are standard Luau modules, but are required directly by the engine as part of the generation process. They follow a defined structure and return a table with standardized fields. ```lua local MyGenerator = {} MyGenerator.Attributes = { DoSomething = true, AmountOfSomething = 137, } type Params = { Size: Vector3, Attributes: typeof(MyGenerator.Attributes), Pause: (self: any) -> (), } function MyGenerator.OnGenerate(params: Params, targetContainer: GeneratedFolder) end return MyGenerator ``` The standardized fields include: - `Attributes`: A map that defines parameter names and their default values. - `OnGenerate`: The function that receives a `params` table and a `targetContainer`. The `params` table inside `OnGenerate` includes: - `Attributes`: A table that contains either the module-defined attributes or all model attributes if none are specified. - `Size`: A parameter that reflects the model's dimensions at a scale of one. - `Pause`: A method that allows generation to yield efficiently during complex generation tasks. The `OnGenerate` function should generate content around the origin in the `targetContainer`. It should never directly modify the main `DataModel` hierarchy. ### Parameters **Parameters** are the inputs that control generation. These include the built-in `Size` property of `ProceduralModel`, as well as custom attributes specified in the generator module. These **explicit parameters** are passed directly to `OnGenerate`. **Implicit parameters**, such as the module's source code or sandboxing status, also trigger regeneration when changed but are not passed to `OnGenerate`. The full set of parameters includes: - Explicit: - `Size`: Defines the region around the origin where content is generated. - `Attributes`: A map containing current input attribute values. - Implicit: - Generator: The module that supplies the generation logic. - Source code: Changes to the generator module's Luau code. - Sandboxing: Changes to the generator module's sandboxing status. ## Regeneration behavior To maintain performance and simplify API interactions, `OnGenerate` is **not** called immediately when parameters change. Instead, the `ProceduralModel` is marked as **dirty**, indicating that regeneration is pending. This pending regeneration typically occurs later in the same frame. If regeneration fails, the dirty state is cleared until another parameter change occurs. If a place is saved while the model is dirty, the state persists and the model is added to the generation queue the next time the game loads. ### Execution process When processing time is available, the engine regenerates dirty models by executing the following steps: 1. Requires the generator module, yielding if necessary. 2. Starts a new thread for the generation process. 3. Builds the parameter table, normalizing the `Size` by the model's current scale. 4. Applies default attribute values to any missing attributes. 5. Initializes a temporary `Class.GeneratedFolder` as the `targetContainer`. 6. Invokes `OnGenerate`. 7. After `OnGenerate` returns: - If successful, migrates the results in the `targetContainer` to the `ProceduralModel`. - If generation fails, uses a failure placeholder instead. If parameters change again while a generation thread is yielded, the outstanding generation is cancelled and restarted with the updated parameters. ### Generation results Placing generated results into the model involves the following steps: 1. Terminates any child threads leaked during generation. 2. Positions the results using `PivotTo`. 3. If the model's `Scale` is not 1, uses `ScaleTo` to undo the size normalization. 4. Parents the results to the model's `GeneratedFolder`. 5. If the generator module is sandboxed, ensures the `ModuleScripts` in the result don't exceed the generator module capabilities. 6. Populates any missing attributes on the model with their default values. 7. Clears the model's dirty state. ## Generation details In addition to the core generation flow, the following details describe behaviors and considerations that affect how `ProceduralModel` generation works. ### Sizing Because of the scale normalization applied during generation, `ProceduralModel` supports multiple sizing workflows: - Changing the `Size` property regenerates the model with new content. - Changing the `Scale` uniformly scales the existing results using `ScaleTo`. ### Timeouts As a failsafe, a generator module that performs too much work without yielding is terminated. If generation hits a "Script Timeout" error, add additional calls to `parameters:Pause()` to allow the engine to defer remaining work to a later frame. Calling `parameters:Pause()` is very inexpensive and typically returns immediately during normal generator execution, so it can be called liberally at the start of loops without impacting performance. ### Undo history Generated results are grouped with the parameter change that triggered them using undo waypoint overrides. If multiple parameter changes occur in a single frame, the results are associated with the most recent waypoint. ### Sandboxing `ProceduralModel` supports sandboxing. All third-party procedural models you insert from the Creator Store are sandboxed by default to help prevent unintended changes to your game. Generation code runs in the context of the generator module and uses that module's sandboxing status. These settings aren't visible by default. To view or modify them, set `Workspace.SandboxedInstanceMode = Experimental`. This exposes the `Sandboxed` and `Capabilities` properties on the `ModuleScript`. A sandboxed generator module typically requires at least the following capabilities: - `RunServerScript` (to execute the module) - `CreateInstances` - `Basic` Sandboxing for the ProceduralModel and its generator module are independent of each other. When applied to the generator module, sandboxing does not affect normal runtime scripts within the `ProceduralModel`. This lets you keep edit-time generation tightly restricted without impacting in-game behavior. ### Nesting `ProceduralModels` can generate other `ProceduralModels`. When this happens, the engine processes the hierarchy top-down within the same generation step when possible, with more deeply-nested models generated last. ## Limitations - Generation is not supported within `Actor` instances. - Models can't be converted into packages. - Joints created when using draggers with **Joint Surfaces** enabled are not preserved across `ProceduralModel` regenerations. --- title: "Solid modeling" url: /docs/en-us/parts/solid-modeling last_updated: 2026-06-29T19:34:03Z description: "Solid modeling is the process of joining parts together to form complex shapes." --- # Solid modeling **Solid modeling** is the process of joining [parts](/docs/en-us/parts.md) together in unique ways to form more complex shapes. This includes the boolean operations **union**, **subtraction**, and **intersection**, which are commonly known as Constructive Solid Geometry (CSG). You can perform solid modeling everywhere: in Studio, plugins, even [in-experience](#in-experience-solid-modeling) on both the server and client. In addition to boolean CSG, solid modeling also supports meshes, as long as they are [watertight](#watertight-geometry), and operations like **sweep** and **fragment** that let you and your players slice, cut, and shatter geometry for unique gameplay interactions. _A mesh object created with `Class.GeometryService:SweepPartAsync()|SweepPartAsync()`_ _A mesh object broken with `Class.GeometryService:FragmentAsync()|FragmentAsync()`_ > **Success:** Performing CSG on meshes and using the sweep and fragment operations are only available by enabling the beta feature through **File** ⟩ **Beta Features** ⟩ **Solid Modeling On Meshes**. > **Info:** In this guide, the term "part" refers to an instance of `Class.Part`, `Class.PartOperation`, or `Class.MeshPart`, which are all children of `Class.BasePart`. ## Watertight geometry ** Basic elements of meshes** There are three basic elements of meshes: - **Vertex** - A single point on the mesh. - **Edge** - A line that connects two vertices. - **Face** - A surface area between three or more vertices. ![A single active vertex on a cube mesh.](../assets/art/3p-software/blender/Vertex.png)_Vertex_ ![A single active edge on a cube mesh.](../assets/art/3p-software/blender/Edge.png)_Edge_ ![A single active face on a cube mesh.](../assets/art/3p-software/blender/Face.png)_Face_ Solid modeling operations can only work with **watertight** geometry; in fact, "solid" and "watertight" are synonymous. In technical terms, a mesh being watertight means that it's closed, manifold, and non-self-intersecting. These terms have strict definitions, but here are some simple rules: - Each face must have an 'inside' side and an 'outside' side. These are determined by the face's winding order, which is the order of its three vertices. - Each edge must be shared by exactly two triangles. This means there cannot be any holes in the mesh because the edges around the rim of a hole would only have one triangle. - Faces cannot pass through other faces. - Adjacent triangles must agree on which side is the 'outside' side. - Each vertex must have exactly one fan of adjacent triangles. ![Examples of non-watertight goemetry](../assets/modeling/solid-modeling/Not-Watertight-Examples.png) ** Repairing non-watertight meshes** The solid modeling system is able to automatically repair specific small issues with a mesh, but in general, API calls will fail if the mesh is not watertight. There's no one-size-fits-all way to repair an existing non-watertight mesh, but there are several Blender plugins which can help, such as [3D Print Toolbox](https://extensions.blender.org/add-ons/print3d-toolbox) and [Mesh Repair Tools](https://extensions.blender.org/add-ons/mesh-repair-tools). As another option, [Meshlab](https://www.meshlab.net) also has very useful tools built-in to try and make the mesh manifold, which is the main requirement for a mesh being watertight. One way to see that a mesh will be extremely difficult to make watertight is to look at it in Studio from all angles, then try enabling and disabling the mesh's `Class.MeshPart.DoubleSided` property. If you can see any difference, then the mesh is just a shell and the tools mentioned above won't work because they cannot guess what space is inside versus outside the mesh. However, if all you want is a thin mesh and it isn't important to keep the mesh's dimensions exactly the same, you can use Blender's [Solidify modifier](https://docs.blender.org/manual/en/latest/modeling/modifiers/generate/solidify.html) to sightly thicken the shell into a watertight mesh. ![Example of a mesh which looks different when DoubleSided is enabled.](../assets/modeling/solid-modeling/DoubleSided-Diff.png)_This is a shell mesh and automatic repair methods won't work for it._ ## Solid modeling in Studio You can perform three basic boolean operations using four tools within the **Model** tab toolbar. ![Solid modeling tools highlighted in Studio's toolbar.](../assets/studio/general/Toolbar-Solid-Modeling.png) | Tool | Shortcut | Description | | --- | --- | --- | | [Union](#union-parts) | `Shift``Ctrl``G` (Windows)
`Shift``⌘``G` (Mac) | Join two or more parts together to form a single solid union. | | [Intersect](#intersect-parts) | `Shift``Ctrl``I` (Windows)
`Shift``⌘``I` (Mac) | Intersect overlapping parts into a single solid intersection. | | [Negate](#negate-parts) | `Shift``Ctrl``N` (Windows)
`Shift``⌘``N` (Mac) | Negate parts, useful for making holes and indentations. | | [Separate](#separate-unions-or-intersections) | `Shift``Ctrl``U` (Windows)
`Shift``⌘``U` (Mac) | Separate the union or intersection back into its individual parts. | ### Union parts The **Union** tool joins two or more parts together to form a single solid `Class.UnionOperation`. ![A block and a cylinder.](../assets/modeling/solid-modeling/Union-Before.png)_Individual Parts_ ![A block and a cylinder combined into one object.](../assets/modeling/solid-modeling/Union-After.png)_Union result_ To combine parts together into a union: 1. Select all parts that you want to join together. 2. Click the **Union** button. All of the parts combine into one solid `Class.UnionOperation` with the name **Union**. ### Intersect parts The **Intersect** tool intersects overlapping parts into a single solid `Class.IntersectOperation`. ![A block and a cylinder.](../assets/modeling/solid-modeling/Intersect-Before.png)_Individual Parts_ ![A block and a cylinder combined into one object.](../assets/modeling/solid-modeling/Intersect-After.png)_Intersect result_ To intersect overlapping parts together: 1. Select all parts that you want to intersect. 2. Click the **Intersect** button. All of the parts combine into one solid `Class.IntersectOperation` with the name **Intersection**. ### Negate parts The **Negate** tool negates a part so that when it's [unioned with another part](#union-parts), the shape of the negated part is **subtracted** from the other part. ![A block and a cylinder.](../assets/modeling/solid-modeling/Subtract-Before.png)_Block and negated cylinder_ ![A block and a cylinder combined into one object.](../assets/modeling/solid-modeling/Subtract-After.png)_Subtract result_ To subtract a part from other overlapping parts: 1. Select the part you want to negate from other parts. 2. Click **Negate**. The part becomes tagged as a negated part and a negated symbol appears in the Explorer. The part becomes translucent with a reddish tint to indicate its state. 3. Select both the negated part and the parts you want to subtract it from. 4. Click **Union**. The negated part is cut out from the included overlapping parts. The tag is exposed for scripting, so you can also negate parts by adding the tag `rbxNegate` from a script or plugin. `Class.NegateOperation` is no longer used. ### Separate unions or intersections The **Separate** tool separates a `Class.UnionOperation` back into its individual parts, essentially serving as an "undo" tool for unions and intersections. To separate a union or intersection back into individual parts: 1. Select the union or intersection. 2. Click **Separate**. The parts separate back into their original form. ## In-experience solid modeling You can also perform solid modeling operations while an experience is running by using `Class.GeometryService` functions. ### UnionAsync(), IntersectAsync(), and SubtractAsync() Similar to Studio's built-in basic boolean operation tools, you can use `Class.GeometryService` functions like `Class.GeometryService:UnionAsync()|UnionAsync()`, `Class.GeometryService:IntersectAsync()|IntersectAsync()`, and `Class.GeometryService:SubtractAsync()|SubtractAsync()`to perform basic boolean operations while an experience is running. For example, the following script uses `Class.GeometryService:SubtractAsync()|SubtractAsync()`to subtract the volume of one part from another. ```lua local GeometryService = game:GetService("GeometryService") local mainPart = Instance.new("Part") local otherPart = Instance.new("Part") otherPart.Position = Vector3.new(1, 0.5, 1) local success, newParts = pcall(function() return GeometryService:SubtractAsync(mainPart, {otherPart}) end) if success and newParts then for _, newPart in pairs(newParts) do newPart.Parent = workspace end end ``` ![A block subtracted from another block.](../assets/modeling/solid-modeling/Subtract-Simple-Block.png) > **Warning:** In-experience solid modeling operations are asynchronous, meaning they can impact performance. For best results, you should not perform a large series of `Async` calls such as `Class.GeometryService:UnionAsync()|UnionAsync()` in quick succession. To further demonstrate, the next code sample combines the geometry of `mainPart` and the parts in the `otherParts` array, then it destroys the original parts involved in the operation. You can replace the call to `Class.GeometryService:UnionAsync()|UnionAsync()` with `Class.GeometryService:IntersectAsync()|IntersectAsync()` or `Class.GeometryService:SubtractAsync()|SubtractAsync()` to perform the other boolean operations. ```lua local GeometryService = game:GetService("GeometryService") local mainPart = workspace.BlueBlock local otherParts = { workspace.PurpleCylinder } local options = { CollisionFidelity = Enum.CollisionFidelity.Default, RenderFidelity = Enum.RenderFidelity.Automatic, SplitApart = false, } -- Perform union operation in pcall() since it's asynchronous local success, newParts = pcall(function() return GeometryService:UnionAsync(mainPart, otherParts, options) end) if success and newParts then -- Loop through resulting parts to reparent/reposition for _, newPart in pairs(newParts) do newPart.Parent = mainPart.Parent newPart.CFrame = mainPart.CFrame newPart.Anchored = mainPart.Anchored end -- Destroy original parts mainPart.Parent = nil mainPart:Destroy() for _, otherPart in pairs(otherParts) do otherPart.Parent = nil otherPart:Destroy() end end ``` As long as all the inputs are composed of primitive parts rather than `Class.MeshPart|MeshParts`, the union, interact, and subtract boolean operations will produce a `Class.PartOperation` with two pieces of stored data: a tree of CSG operations known as a CSGTree, and a mesh for rendering. ** Migrating from BasePart CSG APIs** Compared to `Class.BasePart:UnionAsync()`/`Class.BasePart:IntersectAsync()`/`Class.BasePart:SubtractAsync()`, the `Class.GeometryService` boolean functions differ as follows: - The output is an array of instances rather than a single instance. - The input parts do not need to be parented to the scene, allowing for background operations. - When the `SplitApart` option is set to `true` (default), each distinct body will be returned in its own `Class.PartOperation`/`Class.MeshPart`. - All the returned parts are in the coordinate space of the main part, so their `Class.PVInstance.Origin` positions are the same as the main part's. This keeps the vertices of the mesh in the same position relative to the object as before the operation, but it does also mean the `(0, 0, 0)` of a returned part is not necessarily at the center of its body. ### SweepPartAsync() > **Success:** This API is only available by enabling the beta feature through **File** ⟩ **Beta Features** ⟩ **Solid Modeling On Meshes**. The `Class.GeometryService:SweepPartAsync()` function creates a `Class.MeshPart` which has the shape of the input part dragged through a given set of `CFrame` positions. This function can be very useful for performing slicing and cutting interactions. The input can be a `Class.Part`, `Class.PartOperation`, or `Class.MeshPart`. The result's shape is defined as the union of the convex hulls of each adjacent pair of `CFrames`; if only a single `CFrame` is provided, the result will be a convex hull of the input part. To demonstrate how this function works, the following code sample sweeps a ball through a set of `CFrame` positions to create a spiral: ```lua local GeometryService = game:GetService("GeometryService") local inputPart = Instance.new("Part") inputPart.Shape = Enum.PartType.Ball local cframeList = {} for i = 1, 50 do local rotation = CFrame.Angles(0, i * 0.5, 0) local position = Vector3.new(0, i * 0.1, -1) table.insert(cframeList, rotation * CFrame.new(position)) end local success, sweptPart = pcall( function() return GeometryService:SweepPartAsync(inputPart, cframeList) end) if success and sweptPart then sweptPart.Parent = workspace end ``` ![A spiral shape created by sweeping a sphere.](../assets/modeling/solid-modeling/SweepPart-Spring.png) **Example** ** Slicing gameplay using sweep** ![A translucent curved shape subtracted from a block.](../assets/modeling/solid-modeling/SweepPart-Slice.png) This examples uses `Class.GeometryService:SweepPartAsync()` to achieve a sword or laser gun slice gameplay feature, where the movement of the sword is based on the player's mouse position. The user's mouse movement is recorded as a list of `CFrames`, `Class.GeometryService:SweepPartAsync()|SweepPartAsync()` builds a slice mesh from this data, then the slice mesh is subtracted from the part which was hit. To get this example running in Studio: 1. Create the following `Script` in `ServerScriptService` to perform all of the solid modeling operations.```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local GeometryService = game:GetService("GeometryService") local DrawCurveEvent = ReplicatedStorage:WaitForChild("DrawCurveEvent") DrawCurveEvent.OnServerEvent:Connect(function(player, cframeList, hitInstance) local blade = Instance.new("Part") blade.Size = Vector3.new(0.2, 0.2, 15.0) local success, sweptPart = pcall( function() return GeometryService:SweepPartAsync(blade, cframeList) end) if success and sweptPart then -- Visualize the sweep sweptPart.Parent = workspace sweptPart.Transparency = 0.5 sweptPart.Anchored = true sweptPart.CanQuery = false -- Subtract the sweep from the hit instance local subtractSuccess, newParts = pcall( function() return GeometryService:SubtractAsync(hitInstance, {sweptPart}) end) if subtractSuccess and newParts then for _, newPart in pairs(newParts) do newPart.Parent = hitInstance.Parent newPart.Anchored = true end hitInstance:Destroy() end end end) ``` 2. Create the following `LocalScript` in `StarterPlayerScripts` to handle user input.```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local GeometryService = game:GetService("GeometryService") local DrawCurveEvent = ReplicatedStorage:WaitForChild("DrawCurveEvent") DrawCurveEvent.OnServerEvent:Connect(function(player, cframeList, hitInstance) local blade = Instance.new("Part") blade.Size = Vector3.new(0.2, 0.2, 15.0) local success, sweptPart = pcall( function() return GeometryService:SweepPartAsync(blade, cframeList) end) if success and sweptPart then -- Visualize the sweep sweptPart.Parent = workspace sweptPart.Transparency = 0.5 sweptPart.Anchored = true sweptPart.CanQuery = false -- Subtract the sweep from the hit instance local subtractSuccess, newParts = pcall( function() r eturn GeometryService:SubtractAsync(hitInstance, {sweptPart}) end) if subtractSuccess and newParts then for _, newPart in pairs(newParts) do newPart.Parent = hitInstance.Parent newPart.Anchored = true end hitInstance:Destroy() end end end) ``` 3. Create a `RemoteEvent` in `ReplicatedStorage` named **DrawCurveEvent**. ### FragmentAsync() > **Success:** This API is only available by enabling the beta feature through **File** ⟩ **Beta Features** ⟩ **Solid Modeling On Meshes**. The `Class.GeometryService:FragmentAsync()` and `Class.GeometryService:GenerateFragmentSites()` functions let you shatter a part into pieces with natural-looking shapes. `Class.GeometryService:FragmentAsync()` uses [voronoi](https://en.wikipedia.org/wiki/Voronoi_diagram) decomposition to divide a single part into multiple `Class.MeshPart` instances according to the pattern of points passed in, while `Class.GeometryService:GenerateFragmentSites()` is a helper function which generates points known as voronoi sites to pass into `Class.GeometryService:FragmentAsync()|FragmentAsync()`. To demonstrate how these functions work together, the following code sample generates voronoi sites to fragment a block part: ```lua local GeometryService = game:GetService("GeometryService") local inputPart = Instance.new("Part") inputPart.Position = Vector3.new(0, 0.7, 20) local sites = GeometryService:GenerateFragmentSites(inputPart) local success, fragments = pcall( function() return GeometryService:FragmentAsync(inputPart, sites) end) if success and fragments then for _, item in fragments do local instance = item.Instance instance.Parent = workspace end end ``` ![A block smashed into pieces.](../assets/modeling/solid-modeling/Fragment-Simple-Block.png) > **Warning:** Keep in mind that the number of small pieces created can be massive. If you give users the freedom to fragment objects, you need a system in place for cleaning up the pieces, such as removing them after some amount of time using the `Class.Debris` service. > > In addition, you can create your own lists of sites manually rather than using `Class.GeometryService:GenerateFragmentSites()|GenerateFragmentSites()`, but it can take 50+ lines of Lua to generate well-behaved points. **Examples** ** Localized fragment at a given position** ![A block with the corner fragmented in pieces.](../assets/modeling/solid-modeling/Fragment-Localized.png) The following script fragments an area of a part, given by a position and radius. The position might commonly come from a physical collision or a raycast from a player. The first element of the site array which `Class.GeometryService:GenerateFragmentSites()|GenerateFragmentSites()` generates will be an inner array of all the sites which are outside the requested `radius`. If you want to do something specific to the remaining 'unshattered portion' of the part, you can find that portion by checking `fragments[i].Index == 1` when looping over the results of `Class.GeometryService:FragmentAsync()|FragmentAsync()`. ```lua local GeometryService = game:GetService("GeometryService") function fragmentAtPosition(player, part, contactPoint, radius) local allSites = GeometryService:GenerateFragmentSites(part, {Origin = contactPoint, Radius = radius}) local success, fragments = pcall( function() return GeometryService:FragmentAsync(part, allSites) end) if not success then warn("Failed to Fracture:"..tostring(fragments)) return end local decals = {} for _,child in pairs(part:GetChildren()) do if child:IsA("Decal") or child:IsA("SurfaceAppearance") then table.insert(decals,child) end end for i = 1, #fragments do local fragment = fragments[i].Instance if fragment == nil then continue end for _,d in pairs(decals) do local d2 = d:Clone() d2.Parent = fragment end fragment.Anchored = false fragment.Parent = part.Parent fragment:SetNetworkOwner(player) end if #fragments ~= 0 then part:Destroy() end return fragments end ``` ** Fragment within an area defined by another part** The following script breaks fragments off the first part within a shape defined by using a second part as a stencil. Only voronoi sites within the second part will result in separate pieces. All other sites will have their cells combined into a single part. ![Stencil in the shape of the Roblox Studio logo](../assets/modeling/solid-modeling/Fragment-Stencil-Before.png)_Part (dark grey) and stencil part_ ![Results of a fragment in the shape of a stencil](../assets/modeling/solid-modeling/Fragment-Stencil-After.png)_Fragment script result_ ```lua local GeometryService = game:GetService("GeometryService") function fragmentWithinStencil(player, part) local overlapParams = OverlapParams.new() overlapParams.FilterType = Enum.RaycastFilterType.Include overlapParams.FilterDescendantsInstances = {workspace.Stencil} overlapParams.RespectCanCollide = false local sensor = Instance.new("Part") sensor.Size = Vector3.new(0.01, 0.01, 0.01) local allSites = GeometryService:GenerateFragmentSites(part, {SiteSpacing = 0.9}) local fragmentSites = {} local mainPartSites = {} for _, site in ipairs(allSites) do sensor.CFrame = CFrame.new(site) local partsFound = workspace:GetPartsInPart(sensor, overlapParams) if #partsFound > 0 then table.insert(fragmentSites, site) else table.insert(mainPartSites, site) end end local sortedSites = fragmentSites table.insert(sortedSites, mainPartSites) workspace.Stencil:Destroy() local success, fragments = pcall( function() return GeometryService:FragmentAsync(part, sortedSites, {SplitApart = false}) end) if not success then warn("Failed to Fracture:"..tostring(fragments)) return end local decals = {} for _,child in pairs(part:GetChildren()) do if child:IsA("Decal") or child:IsA("SurfaceAppearance") then table.insert(decals,child) end end for i = 1, #fragments do local fragment = fragments[i].Instance if fragment == nil then continue end for _,d in pairs(decals) do local d2 = d:Clone() d2.Parent = fragment end fragment.Anchored = false fragment.Parent = part.Parent fragment:SetNetworkOwner(player) end if #fragments ~= 0 then part:Destroy() end return fragments end ``` ** Fragment multiple parts and hide the original part boundaries** The following script is a much more niche use case, but it demonstrates the power of the index data which is returned from `Class.GeometryService:FragmentAsync()`. For example, many places contain buildings formed from multiple non-unioned block parts. If a grenade, cannonball, or sledgehammer were to damage it, you would want all of the wall parts to be fragmented. This script fragments all of the nearby parts, then unions the fragments of different parts together to completely hide the seams. This involves multiple `Async` operations, so it may not be suitable for use in-experience as an instant response to user input, such as a sledgehammer tool. ![A row of blocks](../assets/modeling/solid-modeling/Fragment-Multiple-Before.png)_A row of blocks_ ![A row of blocks fragmented](../assets/modeling/solid-modeling/Fragment-Multiple-After.png)_Each piece may originate from multiple input parts_ ```lua local GeometryService = game:GetService("GeometryService") function fragmentCrossPart(player, part, contactPoint, radius) local allSites = GeometryService:GenerateFragmentSites(part, {Origin = contactPoint, Radius = radius}) local fragmentsSorted = {} for i = 1, #allSites do fragmentsSorted[i] = {} end local partsFound = workspace:GetPartBoundsInRadius(contactPoint, radius) for i, part in ipairs(partsFound) do local success, fragments = pcall( function() return GeometryService:FragmentAsync(part, allSites) end) if not success then warn("Failed to Fracture:"..tostring(fragments)) return end for i = 1, #fragments do local fragment = fragments[i].Instance local siteIndex = fragments[i].Index if fragment == nil or siteIndex == nil then continue end table.insert(fragmentsSorted[siteIndex], fragment) end end for i = 1, #fragmentsSorted do local fragmentList = fragmentsSorted[i] if #fragmentList == 0 then continue end if #fragmentList == 1 then local fragment = fragmentList[1] fragment.Anchored = false fragment.Parent = part.Parent fragment:SetNetworkOwner(player) continue end if i == #allSites then for j = 1, #fragmentList do local fragment = fragmentList[j] fragment.Parent = part.Parent fragment.Anchored = true end continue end local mainPart = fragmentList[1] local otherParts = {} for j = 2, #fragmentList do table.insert(otherParts, fragmentList[j]) end local success, results = pcall( function() return GeometryService:UnionAsync(mainPart, otherParts) end) if not success then warn("Failed to Union:"..tostring(results)) return end for j = 1, #results do results[j].Parent = part.Parent results[j].Anchored = false results[j]:SetNetworkOwner(player) end end for i, part in ipairs(partsFound) do part:Destroy() end end ``` ** Customizable Luau GenerateFragmentSites()** The following script is a nearly identical Luau replacement for `Class.GeometryService:GenerateFragmentSites()`. If you want similar behavior to `Class.GeometryService:GenerateFragmentSites()` but want to make slight changes, you can use this as a starting point. It uses a jittered grid of points, and guarantees that the fragmented area is well behaved, unlike fully random points. ```lua local function generateFragmentSites(part: BasePart, siteSpacing: number?, origin: Vector3?, radius: number?): {Vector3} local RANDOMNESS_MULTIPLIER = 1.0 -- Use this to adjust the amount of jitter if (origin and not radius) or (radius and not origin) then warn("Either both origin and radius should be provided, or neither.") return {} end local isLocalized = (radius ~= nil) -- isLocalized means don't fracture the whole part, just a section. local partCFrame = part.ExtentsCFrame local gridDimensions: Vector3 local localGridCenter: Vector3 local spacing if siteSpacing then spacing = siteSpacing elseif isLocalized then spacing = radius * 0.5 else local partSize = part.Size local volume = partSize.X * partSize.Y * partSize.Z spacing = (volume / 5) ^ (1/3) end if isLocalized then local localOrigin = partCFrame:PointToObjectSpace(origin) local gridSize = math.ceil(radius * 2 / spacing) + 3 gridDimensions = Vector3.new(gridSize, gridSize, gridSize) localGridCenter = localOrigin else local partSize: Vector3 = part.Size local xCount = math.ceil(partSize.X / spacing) local yCount = math.ceil(partSize.Y / spacing) local zCount = math.ceil(partSize.Z / spacing) gridDimensions = Vector3.new(xCount, yCount, zCount) localGridCenter = Vector3.zero end local totalGridSize = gridDimensions * spacing local halfCell = Vector3.new(spacing, spacing, spacing) * 0.5 local localStartOffset = localGridCenter - (totalGridSize * 0.5) + halfCell local innerJitter = spacing * 0.5 * RANDOMNESS_MULTIPLIER local outerJitter = math.min(spacing * 0.5 * 0.866, innerJitter) local sitesFlatList = {} for x = 0, gridDimensions.X - 1 do for y = 0, gridDimensions.Y - 1 do for z = 0, gridDimensions.Z - 1 do local isOuterShell = x == 0 or x == gridDimensions.X - 1 or y == 0 or y == gridDimensions.Y - 1 or z == 0 or z == gridDimensions.Z - 1 local jitterAmount = if (isOuterShell and isLocalized) then outerJitter else innerJitter local jitterOffset = Vector3.new( (math.random() - 0.5) * 2 * jitterAmount, (math.random() - 0.5) * 2 * jitterAmount, (math.random() - 0.5) * 2 * jitterAmount ) local offsetInGrid = Vector3.new(x, y, z) * spacing table.insert(sitesFlatList, localStartOffset + offsetInGrid + jitterOffset) end end end local sitesListFinal = {} if isLocalized then local mainPartSites = {} for _, localSite in ipairs(sitesFlatList) do local worldSite = partCFrame * localSite local distance = (worldSite - origin).Magnitude if distance < radius then table.insert(sitesListFinal, worldSite) else table.insert(mainPartSites, worldSite) end end table.insert(sitesListFinal, 1, mainPartSites) else for _, localSite in ipairs(sitesFlatList) do local worldSite = partCFrame * localSite table.insert(sitesListFinal, worldSite) end end return sitesListFinal end ``` ### Preserve constraints If an input part has constraints or attachments that you want to preserve, you can transfer them over to the resulting parts. It can be tedious to figure out which output part a constraint should be attatched to, so it's recommended to use `Class.GeometryService:CalculateConstraintsToPreserve()` to generate a table of recommendations which you can loop over and apply. To demonstrate, the following code sample performs a subtract operation, loops through the resulting parts to reparent and reposition the resulting parts, then calculates a table of constraints and attachments to either preserve or drop before destroying all original parts. ```lua local GeometryService = game:GetService("GeometryService") local mainPart = workspace.PurpleBlock local otherParts = { workspace.BlueBlock } local options = { CollisionFidelity = Enum.CollisionFidelity.Default, RenderFidelity = Enum.RenderFidelity.Automatic, SplitApart = true, } local constraintOptions = { tolerance = 0.1, weldConstraintPreserve = Enum.WeldConstraintPreserve.All, dropAttachmentsWithoutConstraints = false, } -- Perform subtract operation in pcall() since it's asynchronous local success, newParts = pcall(function() return GeometryService:SubtractAsync(mainPart, otherParts, options) end) if success and newParts then -- Loop through resulting parts to reparent/reposition for _, newPart in pairs(newParts) do newPart.Parent = mainPart.Parent newPart.CFrame = mainPart.CFrame newPart.Anchored = mainPart.Anchored end -- Calculate constraints/attachments to either preserve or drop local recommendedTable = GeometryService:CalculateConstraintsToPreserve(mainPart, newParts, constraintOptions) -- Preserve constraints/attachments based on recommended table for _, item in pairs(recommendedTable) do if item.Attachment then item.Attachment.Parent = item.AttachmentParent if item.Constraint then item.Constraint.Parent = item.ConstraintParent end elseif item.NoCollisionConstraint then local newNoCollision = Instance.new("NoCollisionConstraint") newNoCollision.Part0 = item.NoCollisionPart0 newNoCollision.Part1 = item.NoCollisionPart1 newNoCollision.Parent = item.NoCollisionParent elseif item.WeldConstraint then local newWeldConstraint = Instance.new("WeldConstraint") newWeldConstraint.Part0 = item.WeldConstraintPart0 newWeldConstraint.Part1 = item.WeldConstraintPart1 newWeldConstraint.Parent = item.WeldConstraintParent end end -- Destroy original parts mainPart.Parent = nil mainPart:Destroy() for _, otherPart in pairs(otherParts) do otherPart.Parent = nil otherPart:Destroy() end end ``` ### Behavior details > **Info:** In this section, the term "main part" refers to either the first part you select while solid modeling in Studio, or the first argument while solid modeling in-experience. - The original parts remain intact following a successful operation, and the returned parts have no set `Class.Instance.Parent|Parent`. In most cases, it's recommended to parent the returned part(s) to the same place as the main part, then `Class.Instance.Destroy|Destroy()` the original parts. - If the main part is moving during the calculation of the operation, you can set the resulting parts to the updated `Datatype.CFrame` of the main part, since the returned parts are in the same coordinate space as the main part. - There are functions to swap out the mesh data of an instance, making it easier to utilize the geometry of the operation while maintaining properties, attributes, tags, and children of the main part, such as `Class.Attachment|Attachments`, `Class.Constraint|Constraints`, `Class.ParticleEmitter|ParticleEmitters`, light objects, and decals. This approach also circumvents the potential "flicker" of completely replacing the original `Class.PartOperation` with another. - If using this method with a `Class.PartOperation` as the main part and none of the other parts are `Class.MeshPart|MeshParts`, you can substitute in the geometry of another `Class.PartOperation` via `Class.PartOperation:SubstituteGeometry()|SubstituteGeometry()`. - If the main part is a `Class.MeshPart`, you can use `Class.MeshPart:ApplyMesh()`. - It's possible to call these functions on the client, but with some limitations. First, it must be done with objects **created** on the client. Secondly, there is no replication available from client to the server. - The following properties from the main part are applied to the resulting `Class.PartOperation|PartOperations` or `Class.MeshPart|MeshParts`: - Appearance: `Class.BasePart.Color|Color`, `Class.BasePart.Material`, `Class.BasePart.MaterialVariant`, `Class.BasePart.Reflectance`, `Class.BasePart.Transparency` - Collision: `Class.BasePart.AudioCanCollide`, `Class.BasePart.CanCollide` - Part: `Class.BasePart.Anchored`, `Class.BasePart.CurrentPhysicalProperties`/`Class.BasePart.CustomPhysicalProperties` ## Solid modeling results considerations ### Colors and UVs The colors of the resulting part(s) after solid modeling come from two places: the face colors and the part's `Class.BasePart.Color|Color`. - If the result is a `Class.PartOperation`, it will have the `Class.BasePart.Color|Color` of the first part you selected in Studio, but Studio uses the face colors by default to keep each face the same color as it was before the operation. You can enable its `Class.PartOperation.UsePartColor|UsePartColor` property in Studio to override this behavior and make the entire result a single color. - If the result is a `Class.MeshPart`, its `Class.BasePart.Color|Color` will be white, and the face colors will always show through. You can adjust the tint of the resulting part(s) by changing their `Class.BasePart.Color|Color`, but it will be mixed (multiplied) with the face colors. This tints the result rather than overriding the face colors completely. If you want complete control over the color of the output, it's best to make the inputs white first. UVs are also handled differently depending of the type of result: - `Class.PartOperation|PartOperations` always have boxmapped UVs, which means each face will have the material/texture/decal from one direction (one of -x, +x, -y, +y, -z, +z) applied to it. This can stretch textures. - `Class.MeshPart|MeshParts` are not boxmapped. The UVs of the main part's mesh are used. Since Roblox does not currently have multi-material support, the UVs of faces originating from the other parts are given UVs of (0, 0). For best results, ensure pixel (0, 0) of your texture has a reasonable color. ### Smoothing angle A solid modeled part's `Class.PartOperation.SmoothingAngle|SmoothingAngle` property smooths angles between adjacent surfaces of the same color. A higher value produces a smoother appearance while a lower value produces a rougher appearance with more sharp edges. While a value between 30 and 70 degrees usually produces a good result, values between 90 and 180 are not recommended as they may cause a "shadowing" effect on unions and intersections with sharp edges. ![Solid modeled part with SmoothingAngle of 0](../assets/modeling/solid-modeling/SmoothingAngle-0.png)_`Class.PartOperation.SmoothingAngle|SmoothingAngle` = 0_ ![Solid modeled part with SmoothingAngle of 45](../assets/modeling/solid-modeling/SmoothingAngle-45.png)_`Class.PartOperation.SmoothingAngle|SmoothingAngle` = 45_ > **Warning:** You can only adjust a solid modeled part's `Class.PartOperation.SmoothingAngle|SmoothingAngle` property in Studio, and it isn't currently possible to adjust the smoothing angle of a `Class.MeshPart`. ### Part simplification If a solid modeling operation would result in any parts with more than 20,000 triangles, they will be simplified to 20,000. If that cannot be done, usually in a case with thousands of non-overlapping components, the operation results in an error. ![A MeshPart in good condition](../assets/modeling/solid-modeling/Simplification-Before.png)_Before simplication_ ![A MeshPart with obvious reduction in mesh quality](../assets/modeling/solid-modeling/Simplification-After.png)_After simplication_
--- title: "Environmental terrain" url: /docs/en-us/parts/terrain last_updated: 2026-06-29T19:34:03Z description: "Explore how to generate and sculpt realistic terrain environments such as mountains, bodies of water, grass-covered hills, or a flat desert." --- # Environmental terrain Studio's [Terrain Editor](/docs/en-us/studio/terrain-editor.md) lets you generate and sculpt detailed and realistic terrain environments such as mountains, bodies of water, grass-covered hills, or a flat desert. Terrain is made up of grids of **voxels** which are 4×4×4 stud regions in the 3D world with a set material. ![Terrain Editor indicated in Studio's toolbar](../assets/studio/general/Toolbar-Terrain-Editor.png)![Desert terrain with mountains in the distance](../assets/modeling/terrain/Showcase.jpg) Using the editor tools, you can easily [generate](#generate-terrain) and edit terrain either at a [voxel](#detailed-editing) or [region](#large-scale-editing) level with the option of importing a [heightmap](#heightmaps-and-colormaps) and [colormap](#heightmaps-and-colormaps). For more precise, dynamic, or procedural terrain editing, you can also [script](#scripting) terrain creation. ## Terrain materials The following default materials are available for terrain, and you can also apply [custom materials](/docs/en-us/parts/materials.md#custom-materials). Materials affect both the shape and appearance of terrain in the world; for example, [animated grass](#grass-animation) renders only on the `Enum.Material.Grass|Grass` material and the `Enum.Material.Water|Water` material [ripples and shimmers](#water-appearance) with a subtle motion. ![Appearance of Asphalt material](../assets/modeling/terrain/Material-Asphalt.jpg)_Asphalt_ ![Appearance of Basalt material](../assets/modeling/terrain/Material-Basalt.jpg)_Basalt_ ![Appearance of Brick material](../assets/modeling/terrain/Material-Brick.jpg)_Brick_ ![Appearance of Cobblestone material](../assets/modeling/terrain/Material-Cobblestone.jpg)_Cobblestone_ ![Appearance of Concrete material](../assets/modeling/terrain/Material-Concrete.jpg)_Concrete_ ![Appearance of Cracked Lava material](../assets/modeling/terrain/Material-Cracked-Lava.jpg)_Cracked Lava_ ![Appearance of Glacier material](../assets/modeling/terrain/Material-Glacier.jpg)_Glacier_ ![Appearance of Grass material](../assets/modeling/terrain/Material-Grass.jpg)_Grass_ ![Appearance of Ground material](../assets/modeling/terrain/Material-Ground.jpg)_Ground_ ![Appearance of Ice material](../assets/modeling/terrain/Material-Ice.jpg)_Ice_ ![Appearance of Leafy Grass material](../assets/modeling/terrain/Material-Leafy-Grass.jpg)_Leafy Grass_ ![Appearance of Limestone material](../assets/modeling/terrain/Material-Limestone.jpg)_Limestone_ ![Appearance of Mud material](../assets/modeling/terrain/Material-Mud.jpg)_Mud_ ![Appearance of Pavement material](../assets/modeling/terrain/Material-Pavement.jpg)_Pavement_ ![Appearance of Rock material](../assets/modeling/terrain/Material-Rock.jpg)_Rock_ ![Appearance of Salt material](../assets/modeling/terrain/Material-Salt.jpg)_Salt_ ![Appearance of Sand material](../assets/modeling/terrain/Material-Sand.jpg)_Sand_ ![Appearance of Sandstone material](../assets/modeling/terrain/Material-Sandstone.jpg)_Sandstone_ ![Appearance of Slate material](../assets/modeling/terrain/Material-Slate.jpg)_Slate_ ![Appearance of Snow material](../assets/modeling/terrain/Material-Snow.jpg)_Snow_ ![Appearance of Water material](../assets/modeling/terrain/Material-Water.jpg)_Water_ ![Appearance of Wood Planks material](../assets/modeling/terrain/Material-Wood-Planks.jpg)_Wood Planks_ ![Air material icon (no visual appearance)](../assets/modeling/terrain/Material-Air.png)_Air_ ### Water appearance By default, terrain water ripples, oscillates, and shimmers with a subtle motion. To customize the color and motion of water: 1. In the **Explorer** window, navigate to the `Class.Workspace`, then select the `Class.Terrain` object.![Terrain object shown in Explorer window of Studio](../assets/studio/explorer/Workspace-Terrain.png) 2. In the **Properties** window, customize the appearance of water through the following properties: | Property | Description | | --- | --- | | `Class.Terrain.WaterColor\|WaterColor` | Adjusts the overall hue of all terrain water in the experience. | | `Class.Terrain.WaterReflectance\|WaterReflectance` | Adjusts how much water surfaces reflect the sky and surrounding objects from a value of 1 (high) to 0 (none). | | `Class.Terrain.WaterTransparency\|WaterTransparency` | Adjusts how transparent water is from a value of 1 (clear) to 0 (opaque). | | `Class.Terrain.WaterWaveSize\|WaterWaveSize` | Adjusts the size of waves from a value of 1 (large) to 0 (none). | | `Class.Terrain.WaterWaveSpeed\|WaterWaveSpeed` | Adjusts the speed of waves from a value of 100 (turbulent) to 0 (still). | > **Info:** Some water properties are only visible while playtesting. To preview all properties while editing, open [Studio Settings](/docs/en-us/studio/setup.md#customization), search for **Editor Quality Level**, and set it to the highest level. ### Grass animation While most materials are static, you can add animated blades of grass to the `Enum.Material.Grass|Grass` terrain material. By default, grass sways gently in a simulated wind, and you can adjust the direction/strength of its animation through [global wind](/docs/en-us/environment/global-wind.md). > **Info:** Note that the speed of animated grass — but not its vector direction — will be reduced if the player has toggled on the **Reduce Motion** [accessibility](/docs/en-us/production/publishing/accessibility.md#reduced-motion) setting from the Roblox or in‑experience **Settings** menu. To add animated grass to the `Enum.Material.Grass|Grass` material: 1. In the **Explorer** window, navigate to the `Class.Workspace`, then select the `Class.Terrain` object.![Terrain object shown in Explorer window of Studio](../assets/studio/explorer/Workspace-Terrain.png) 2. In the **Properties** window, toggle on the `Class.Terrain.Decoration|Decoration` property.![Decoration property of Terrain object in Properties window of Studio](../assets/studio/properties/Terrain-Decoration.png) 3. Adjust the grass length by entering a value between `0.1` and `1` for the `Class.Terrain.GrassLength|GrassLength` property.![GrassLength property of Terrain object in Properties window of Studio](../assets/studio/properties/Terrain-GrassLength.png)![GrassLength comparison depicted on rolling grassland hills.](../assets/modeling/terrain/Terrain-GrassLength.jpg) 4. Adjust the direction and strength of its animation through [global wind](/docs/en-us/environment/global-wind.md). ### Custom terrain colors Each terrain material is assigned a default color, but you can customize any material's color to better fit your experience. #### Default ![Default terrain colors used in desert landscape](../assets/modeling/terrain/Showcase.jpg) #### Fantasy ![Custom terrain colors applied for fantasy landscape](../assets/modeling/terrain/Custom-Colors-Fantasy.jpg) #### Tundra ![Custom terrain colors applied for tundra landscape](../assets/modeling/terrain/Custom-Colors-Tundra.jpg) To customize any material color other than water: 1. In the **Explorer** window, navigate to the `Class.Workspace`, then select the `Class.Terrain` object.![Terrain object shown in Explorer window of Studio](../assets/studio/explorer/Workspace-Terrain.png) 2. In the **Properties** window, expand `Class.Terrain.MaterialColors|MaterialColors`. All materials display with their RGB code.![MaterialColors property shown in Properties window of Studio](../assets/studio/properties/Terrain-MaterialColors-Expand.png) 3. For any material, either input a new RGB code or click the color box to open the [colors popup](/docs/en-us/parts.md#color). ## Generate terrain Using the following tools and methods, you can generate large areas of terrain procedurally through tooling or scripting, or automatically based on a heightmap and colormap. ### Generate tool The **Generate** tool allows you to procedurally generate terrain in seconds. This is useful if you want to create a large map and fine-tune [terrain details](#detailed-editing). 1. In the editor window, navigate to the **Create** tab and select the **Generate** tool.![Generate tool indicated in Create tab of Terrain Editor](../assets/studio/terrain-editor/Create-Tab-Generate.png) 2. In the 3D viewport, move/resize the **selection region** in which to generate terrain. Alternatively, expand the tool's **Selection Settings** and enter values into the **X**/**Y**/**Z** inputs to set a specific size and position. 3. In the tool's **Biome Settings** section, choose the following biomes to include in the new terrain: - Arctic - Dunes - Canyons - Lavascape - Water - Mountains - Hills - Plains - Marsh 4. Click the **Generate** button. ### Heightmaps and colormaps A **heightmap** is a 2D representation of a 3D terrain map, as viewed directly from above. Brighter areas of a heightmap result in higher terrain, like mountains, while darker areas result in lower regions, like valleys. An optional **colormap**, along with a heightmap, converts colors to terrain materials using a **color key**. ![Example heightmap image](../assets/modeling/terrain/Terrain-Heightmap.png)_Heightmap_ ![Example colormap image](../assets/modeling/terrain/Terrain-Colormap.png)_Colormap_ ![Terrain generated from the example heightmap and colormap](../assets/modeling/terrain/Terrain-Colormap-Result.jpg)_Generated terrain_ 1 pixel in a heightmap represents 4 studs in Studio, and Studio supports a maximum of 4096×4096 pixels in either `.jpg` or `.png` format. To import a heightmap and optional colormap: 1. In the editor window, navigate to the **Create** tab and select the **Import** tool.![Import tool indicated in Create tab of Terrain Editor](../assets/studio/terrain-editor/Create-Tab-Import.png) 2. In the tool's **Map Settings** section, click the import button and choose the image you want to import as a heightmap. 3. In the tool's **Material Settings** section, select a terrain material or, alternatively, upload a colormap. #### Material To apply one consistent material across all of the generated terrain, select the **Material** tab and pick a terrain material. #### Colormap To apply a colormap, click the **Colormap** tab, click its import button, and choose the file to import. Colors on the image should match the following RGB/hex values and use hard edges, since anti‑aliasing or edge smoothing may create pixel colors outside the expected value ranges. The following table describes color mapping to a corresponding material. If your colormap contains a color that's not in the table, Studio chooses the closest matching material; for this reason, it is best to download the [`RobloxColorMapIndex`](../assets/modeling/terrain/RobloxColorMapIndex.png) file and directly sample its colors, or type the exact RGB/hex values into the color picker of your image editing application. | Material | RGB Value | Hex Value | Color | | --- | --- | --- | --- | | `Enum.Material.Air\|Air` | `[255, 255, 255]` | `FFFFFF` | `rgb(255,255,255)` | | `Enum.Material.Asphalt\|Asphalt` | `[115, 123, 107]` | `737B6B` | `rgb(115,123,107)` | | `Enum.Material.Basalt\|Basalt` | `[30, 30, 37]` | `1E1E25` | `rgb(30,30,37)` | | `Enum.Material.Brick\|Brick` | `[138, 86, 62]` | `8A563E` | `rgb(138,86,62)` | | `Enum.Material.Cobblestone\|Cobblestone` | `[132, 123, 90]` | `847B5A` | `rgb(132,123,90)` | | `Enum.Material.Concrete\|Concrete` | `[127, 102, 63]` | `7F663F` | `rgb(127,102,63)` | | `Enum.Material.CrackedLava\|CrackedLava` | `[232, 156, 74]` | `E89C4A` | `rgb(232,156,74)` | | `Enum.Material.Glacier\|Glacier` | `[101, 176, 234]` | `65B0EA` | `rgb(101,176,234)` | | `Enum.Material.Grass\|Grass` | `[106, 127, 63]` | `6A7F3F` | `rgb(106,127,63)` | | `Enum.Material.Ground\|Ground` | `[102, 92, 59]` | `665C3B` | `rgb(102,92,59)` | | `Enum.Material.Ice\|Ice` | `[129, 194, 224]` | `81C2E0` | `rgb(129,194,224)` | | `Enum.Material.LeafyGrass\|LeafyGrass` | `[115, 132, 74]` | `73844A` | `rgb(115,132,74)` | | `Enum.Material.Limestone\|Limestone` | `[206, 173, 148]` | `CEAD94` | `rgb(206,173,148)` | | `Enum.Material.Mud\|Mud` | `[58, 46, 36]` | `3A2E24` | `rgb(58,46,36)` | | `Enum.Material.Pavement\|Pavement` | `[148, 148, 140]` | `94948C` | `rgb(148,148,140)` | | `Enum.Material.Rock\|Rock` | `[102, 108, 111]` | `666C6F` | `rgb(102,108,111)` | | `Enum.Material.Salt\|Salt` | `[198, 189, 181]` | `C6BDB5` | `rgb(198,189,181)` | | `Enum.Material.Sand\|Sand` | `[143, 126, 95]` | `8F7E5F` | `rgb(143,126,95)` | | `Enum.Material.Sandstone\|Sandstone` | `[137, 90, 71]` | `895A47` | `rgb(137,90,71)` | | `Enum.Material.Slate\|Slate` | `[63, 127, 107]` | `3F7F6B` | `rgb(63,127,107)` | | `Enum.Material.Snow\|Snow` | `[195, 199, 218]` | `C3C7DA` | `rgb(195,199,218)` | | `Enum.Material.WoodPlanks\|WoodPlanks` | `[139, 109, 79]` | `8B6D4F` | `rgb(139,109,79)` | | `Enum.Material.Water\|Water` | `[12, 84, 92]` | `0C545C` | `rgb(12,84,92)` | 4. In the 3D viewport, move/resize the **selection region** in which to generate terrain. Alternatively, enter values into the **Select** tool fields to set a more specific position and size. > **Info:** Minimum and maximum terrain heights depend on the darkest and lightest areas of the heightmap image in relation to the **Y** size (height) of the selection region. For instance, if you choose a height of 128, pure black areas are 64 studs below the center position and pure white areas are 64 studs above the center position. 5. Click the **Generate** button. ### Scripting You can script terrain generation using the `Class.Terrain` class. For example, to create terrain with grass material that fills a volume, you can use methods such as `Class.Terrain:FillBall()|FillBall()`, `Class.Terrain:FillBlock()|FillBlock()`, `Class.Terrain:FillCylinder()|FillCylinder()`, `Class.Terrain:FillRegion()|FillRegion()`, or `Class.Terrain:FillWedge()|FillWedge()`. ```lua local Workspace = game:GetService("Workspace") Workspace.Terrain:FillBlock(CFrame.new(0, 0, 0), Vector3.new(4, 4, 4), Enum.Material.Grass) ``` ## Large-scale editing The editor's **Edit** tab contains tools for large-scale editing. ### Select regions The **Select** tool is the universal tool for selecting rectangular regions of terrain. ![Select tool indicated in Edit tab of Terrain Editor](../assets/studio/terrain-editor/Edit-Tab-Select.png) Select a region by clicking and dragging in the 3D viewport, reposition it with the **move** draggers, and edit its size with the **scale** handles. Alternatively, enter values into the tool's **X**/**Y**/**Z** inputs to set a specific position and size. ![Move draggers and scale handles on a selected region](../assets/studio/terrain-editor/Select-Region-Labeled.jpg)_Move draggers and scale handles on a selected region_ Studio also supports the following keyboard and mouse shortcuts, assuming the Select tool is active and nothing is selected in the **Explorer** window. | Windows | Mac | Action | | --- | --- | --- | | `Ctrl``C` | `⌘``C` | Copy terrain within the selected region to the clipboard. | | `Ctrl``V` | `⌘``V` | Paste terrain that has been copied to the clipboard and swap to the Transform tool so that the new terrain can be transformed. | | `Ctrl``X` | `⌘``X` | Cut terrain within the selected region to the clipboard. | | `Ctrl``D` | `⌘``D` | Duplicate terrain within the selected region and swap to the Transform tool so that the new terrain can be transformed. | | `Delete` | `Delete` | Delete terrain within the selected region. | | `Shift` | `Shift` | When held down while dragging any **scale** handle, scales the region proportionally across all other axes. | | `Ctrl` | `⌘` | When held down while dragging any **scale** handle, scales the region equally in both the positive and negative direction along that axis. | ### Transform regions The **Transform** tool lets you manipulate entire selected regions to a new position, size, or orientation. To transform a region: 1. Select a region and then activate the **Transform** tool. Note that the tool will be automatically activated if you paste or duplicate terrain.![Transform tool indicated in Edit tab of Terrain Editor](../assets/studio/terrain-editor/Edit-Tab-Transform.png) 2. In the 3D viewport, transform the region with the **move** draggers, **rotate** rings, and **scale** handles. Alternatively, enter values into the tool's **X**/**Y**/**Z** inputs to set a specific position, size, and rotation.![Move draggers, scale handles, and rotate rings on the Y axis of a selected region](../assets/studio/terrain-editor/Transform-Region-Labeled.jpg) > **Info:** Holding `Shift` while dragging any **scale** handle scales the region proportionally across all other axes, and holding `Ctrl` or `⌘` while dragging scales the region equally in both the positive and negative direction along that axis. > > Holding `Shift` while dragging any **rotate** ring toggles between rotation snapping or free‑form rotation. > **Success:** By default, this tool uses **Live Edit** mode to constantly update terrain as you transform it. To view only a wireframe preview of the terrain as you transform it, disable live edit mode and then, while transforming, press `Enter`/`Return` or click the **Apply** button to apply the changes. ### Fill and replace regions The **Fill** tool lets you fill an entire selected region with a specific material, or replace all material within the region with another material. 1. Select a region, then activate the **Fill** tool.![Fill tool indicated in Edit tab of Terrain Editor](../assets/studio/terrain-editor/Edit-Tab-Fill.png) 2. In the tool's **Material Settings** section: - To fill the region with a specific material, select **Fill** and choose the desired material. - To replace all terrain of one material with another material, select **Replace**, then choose a **source** material and **target** material. 3. Click the **Apply** button or press `Enter`/`Return`.![Region filled with Salt material](../assets/studio/terrain-editor/Fill-Region.jpg)_Selected region filled with Salt material_ ### Set sea level The **Sea Level** tool lets you create a consistent water level or remove all water within a region. 1. Activate the **Sea Level** tool.![Sea Level tool indicated in Edit tab of Terrain Editor](../assets/studio/terrain-editor/Edit-Tab-Sea-Level.png) 2. Select the intended region by clicking and dragging the **move** draggers and **scale** handles in the 3D viewport. Alternatively, enter values into the tool's **X**/**Y**/**Z** inputs to set a specific position and size. 3. Click the **Evaporate** button to remove water inside the selected region, or click the **Create** button to fill the selected region with water. ## Detailed editing The **Edit** tab also contains tools for precision editing using a "brush" tool to draw, sculpt, smooth, flatten, or paint terrain. ![Detailed editing tools indicated in Edit tab of Terrain Editor](../assets/studio/terrain-editor/Edit-Tab-Detail-Tools.png) Each tool lets you choose from a **sphere**, **box**, or **cylinder** brush shape and a base size between 1–64 studs. ![Brush shape and size controls in the Terrain Editor](../assets/studio/terrain-editor/Brush-Shape-Size.png) For tools which use the brush, Studio supports the following keyboard and mouse shortcuts. | Windows | Mac | Action | | --- | --- | --- | | `Ctrl` | `⌘` | When held down while using the [Draw](/docs/en-us/studio/terrain-editor.md#draw) and [Sculpt](/docs/en-us/studio/terrain-editor.md#sculpt) tools, toggles on the alternate brush mode. For example, toggles on "subtract" mode instead of the default "add" mode. | | `Shift` | `Shift` | When held down while using the [Draw](/docs/en-us/studio/terrain-editor.md#draw) and [Sculpt](/docs/en-us/studio/terrain-editor.md#sculpt) tools, temporarily activates the [Smooth](/docs/en-us/studio/terrain-editor.md#smooth) tool. | | `B` | `B` | When held down while dragging the mouse or using the scroll wheel, adjusts the brush's **base size**. | | `Ctrl``B` | `⌘``B` | When held down while dragging the mouse or using the scroll wheel, adjusts the brush's **height**. Only applies if the brush's shape is set to **box** or **cylinder**. | | `Shift``B` | `Shift``B` | When held down while dragging the mouse or using the scroll wheel, adjusts the brush's **strength**. Only applies when using the [Sculpt](/docs/en-us/studio/terrain-editor.md#sculpt), [Smooth](/docs/en-us/studio/terrain-editor.md#smooth), or [Flatten](/docs/en-us/studio/terrain-editor.md#flatten) tool. | | `Alt` | `⌥` | When held down on mouse click, shows the material picker. | ### Draw The **Draw** tool **adds** or **subtracts** terrain using the brush. This tool functions in a dual mode where holding down `Ctrl` or `⌘` toggles on "subtract" mode instead of the default "add" mode. Additionally, holding down `Shift` temporarily activates the [Smooth](/docs/en-us/studio/terrain-editor.md#smooth) tool. ### Sculpt The **Sculpt** tool **adds** or **subtracts** terrain using the brush. Unlike the Draw tool, this tool includes a **strength** slider to allow for more gentle manipulation of terrain. Similar to the Draw tool, the Sculpt tool functions in a dual mode where holding down `Ctrl` or `⌘` toggles on "subtract" mode instead of the default "add" mode. Additionally, holding down `Shift` temporarily activates the Smooth tool. ### Smooth The **Smooth** tool smoothes out abrupt edges in terrain using the brush. This tool can be used in standalone mode, or you can toggle it on by holding `Shift` while using the Draw or Sculpt tools. ### Flatten The **Flatten** tool flattens terrain to a consistent level across a visualized plane. By default, the tool lowers terrain above the plane **and** raises terrain below to the plane, but you can opt to selectively lower **or** raise through the tool's **Flatten Mode** option. ### Paint The **Paint** tool, using the brush, **paints** a terrain material over an existing material or **replaces** one material with another material. --- title: "Textures and decals" url: /docs/en-us/parts/textures-decals last_updated: 2026-06-29T19:34:03Z description: "Textures and decals are images you can place on object surfaces." --- # Textures and decals A `Class.Texture` is an image you can place on any face of a [part](/docs/en-us/parts.md) or [union](/docs/en-us/parts/solid-modeling.md) that **repeats** both horizontally and vertically on the size of the surface. In contrast, a `Class.Decal` is an image that **stretches** to fit the area of a part or union's surface. After you add a `Class.Texture` or `Class.Decal` object to a part or union, you can: - Change the texture or decal `Class.Decal.Color3|Color3` property to set a color tint using RGB color codes. - Change the texture or decal `Class.Decal.Transparency|Transparency` property to a value between the default of **0** (fully visible) and **1** (invisible). - For a texture, set its [scale](#scale-textures) and [offset](#offset-textures). ![An example texture image of a light blue hexagon on top of a dark blue background.](../assets/modeling/textures-decals/Decal-Texture-Sample-2.png)_Texture image_ ![The same texture repeated eight times on a block part.](../assets/modeling/textures-decals/Texture-On-Surface.jpg)_Texture applied to a part (repeating)_ ![An example decal image of a light purple hexagon on top of a dark purple background.](../assets/modeling/textures-decals/Decal-Texture-Sample-1.png)_Decal image_ ![The same decal stretched on a block part.](../assets/modeling/textures-decals/Decal-On-Surface.jpg)_Decal applied to a part (stretched)_ ## Create textures or decals To create a texture or decal, you must add either a `Class.Texture` or `Class.Decal` object to a part or union. You can [import](/docs/en-us/projects/assets/manager.md#asset-import) images for textures and decals to Studio for use between experiences, and [distribute](/docs/en-us/production/creator-store.md) them to the [Creator Store](/docs/en-us/production/creator-store.md). Once you import the image, Studio assigns it a unique asset ID. > **Info:** Every texture or decal image that you create and import to Roblox must adhere to the [Community Rules](https://en.help.roblox.com/hc/articles/203313410) and [Terms of Use](https://en.help.roblox.com/hc/articles/115004647846). To add a texture or decal to a [part](/docs/en-us/parts.md) or [union](/docs/en-us/parts/solid-modeling.md): 1. In the [Explorer](/docs/en-us/studio/explorer.md) window, add a `Class.Texture` or `Class.Decal` to the part or union. An empty texture or decal object displays on the part or union with orange outlining. 2. In the [Properties](/docs/en-us/studio/properties.md) window, navigate to the `Class.FaceInstance.Face|Face` property and choose a face (`Enum.NormalId|Top`, `Enum.NormalId|Bottom`, `Enum.NormalId|Front`, `Enum.NormalId|Back`, `Enum.NormalId|Left`, or `Enum.NormalId|Right`). - **OPTIONAL** To assist in choosing the correct face, right-click the part/union and select **Show Orientation Indicator**. This displays a circle **🅕**, a blue line attached to the object's front face, and a green arrow that points in the direction of the object's top face. 3. Select the `Class.Decal.ColorMapContent|ColorMapContent` property and apply an image through any of the following methods: - Select any texture or decal you've uploaded previously. - Enter an [asset ID](/docs/en-us/projects/assets.md) into the field. 4. **OPTIONAL** Set a color tint by clicking the small box to the left of the `Class.Decal.Color3|Color3` property or by entering a RGB color code.![A close of view of the Color3 property with the small color box highlighted.](../assets/studio/properties/Appearance-Color3-Picker.png)![A close of view of the Color3 property with the RGB code highlighted.](../assets/studio/properties/Appearance-Color3-RGB-Entry.png)![A block part with a repeating blue hexagon texture on its top face.](../assets/modeling/textures-decals/Texture-On-Surface.jpg)_`Class.Decal.Color3|Color3`: [255, 255, 255]_![The same block part with a repeating hexagon texture on its top face, but the hexagons are pink against on dark purple background.](../assets/modeling/textures-decals/Texture-Color3-255-0-100.jpg)_`Class.Decal.Color3|Color3`: [255, 0, 100]_ 5. **OPTIONAL** Set the `Class.Decal.Transparency|Transparency` property to any value between the default of `0` (fully visible) and `1` (invisible).![A block part with a repeating blue hexagon texture on its top face.](../assets/modeling/textures-decals/Texture-On-Surface.jpg)_`Class.Decal.Transparency|Transparency`: 0_![The same block part with a repeating hexagon texture on its top face, but the hexagons are semi-transparent.](../assets/modeling/textures-decals/Texture-Transparency-0.4.jpg)_`Class.Decal.Transparency|Transparency`: 0.6_ ## Customize textures Unlike decals, **textures** provide further functionality to scale, offset, and animate the source image. ### Scale textures The size of the part doesn't affect the texture. Instead, scaling a part only increases or decreases the number of times the texture repeats. The `Class.Texture.StudsPerTileU|StudsPerTileU` and `Class.Texture.StudsPerTileV|StudsPerTileV` properties determine the size of each "tile" in studs. `Class.Texture.StudsPerTileU|StudsPerTileU` determines the texture's horizontal size while `Class.Texture.StudsPerTileV|StudsPerTileV` determines the texture's vertical size. #### StudsPerTileU & StudsPerTileV = 2 ![Texture on a surface of 8x6 studs with size of each tile in 2x2.](../assets/modeling/textures-decals/Texture-Sample-Scale-1.png) #### StudsPerTileU & StudsPerTileV = 4 ![Texture on a surface of 8x6 studs with size of each tile in 4x4.](../assets/modeling/textures-decals/Texture-Sample-Scale-2.png) ### Offset textures If you want more control over a texture's position, offset the texture by adjusting the `Class.Texture.OffsetStudsU|OffsetStudsU` (horizontal) and `Class.Texture.OffsetStudsV|OffsetStudsV` (vertical) properties. This is also helpful for [animation](#animate-textures). #### OffsetStudsU & OffsetStudsV = 0 ![2x2 texture on a surface of 8x6 studs with no offset.](../assets/modeling/textures-decals/Texture-Sample-Offset-1.png) #### OffsetStudsU & OffsetStudsV = 0.5 ![2x2 texture on a surface of 8x6 studs with offset of 0.5x0.5 studs.](../assets/modeling/textures-decals/Texture-Sample-Offset-2.png) ### Animate textures Using `Class.TweenService`, you can tween texture properties like `Class.Texture.OffsetStudsU|OffsetStudsU` and `Class.Texture.StudsPerTileV|StudsPerTileV` to achieve animated surfaces. For example, if you apply two fog textures to one container and animate them with the following script, you can achieve the appearance of a layered moving fog. _Two animated textures near the floor to simulate a moving fog effect_ ```lua local TweenService = game:GetService("TweenService") local texture1 = script.Parent.Texture1 local texture2 = script.Parent.Texture2 local tweenInfo1 = TweenInfo.new(8, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut, -1) local tween1 = TweenService:Create(texture1, tweenInfo1, {OffsetStudsV=50}) local tweenInfo2 = TweenInfo.new(7, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut, -1, true) local tween2 = TweenService:Create(texture2, tweenInfo2, {OffsetStudsU=50, StudsPerTileU=55, StudsPerTileV=45}) tween1:Play() tween2:Play() ``` ## Texture streaming **Texture streaming** dynamically loads textures based on distance and camera view, which lets you use textures more freely without worrying about scaling across devices. The engine loads baseline-quality textures first, in order of importance, and then progressively raises the quality of each texture up to the memory available on the device. Texture streaming is enabled automatically, so you don't have to take any action to benefit from it. ![A visualization showing an experience with and without texture streaming.](../assets/modeling/textures-decals/texture-streaming.webp)_The same scene with and without texture streaming_ ### Benefits - **Faster perceived load times and smaller downloads**: Textures appear almost immediately at a baseline quality, and the engine downloads only the detail it needs, compressing it both in transit and on the local disk cache. - **Higher quality where it matters**: The engine continuously estimates how important each object is, such as by how much screen space it occupies, and uses the memory budget on those textures so that the most prominent objects get the highest detail. - **Fewer out-of-memory crashes**: Because the engine stays within a memory budget and can lower texture quality when memory runs low, players are much less likely to crash from texture memory spikes. Texture streaming works with `Class.MeshPart` (skinned and unskinned), `Class.SurfaceAppearance`, `Class.Texture`, `Class.Decal`, `Class.MaterialVariant`, `Class.Beam`, `Class.ParticleEmitter`, and the base materials applied to `Class.PartOperation` and `Class.BasePart` instances. ### Best practices - Favor unique, individual textures over texture atlases unless the atlas is applied to batchable parts; requesting a high resolution for one texture in an atlas streams in the entire atlas at that resolution. - Use near-uniform texel density in your UV-mapped meshes so that the engine can select the best resolution for each texture. - Continue to manage texture memory carefully for instance types that texture streaming doesn't support yet. --- title: "Design for performance" url: /docs/en-us/performance-optimization/design last_updated: 2026-06-29T19:34:03Z description: "Outlines performance best practices to follow as you build a new game." --- # Design for performance Designing for performance means following a handful of best practices **as you build** your game. Compared to finding and fixing performance issues later in the development process, designing for performance early can save you a lot of time and effort. ## Low-end devices Lower-end devices, particularly mobile devices, have severe memory limitations and are susceptible to crashes due to out of memory (OOM) errors: - If you want to support lower-end devices, choose at least one "baseline" device, [test your game on it](/docs/en-us/performance-optimization/test-on-hardware.md) throughout the development process, and pay close attention to frame rate and memory usage. As you find problem areas in your game, use those areas to identify the limits of your device. For example, you might test a game with the **Render** (`Shift``F2`) and **Summary** (`Shift``F2`) debug stats enabled. If the frame rate starts to drop in a particularly cluttered area, you could examine the **Draw (scene)** numbers and determine that you need to stay below 1,000 draw calls and 1,000,000 triangles for the game to run well on your baseline device. Or you could examine the **Developer Console** (`F9`) and note that memory usage is a bit high unless you enable [streaming](/docs/en-us/workspace/streaming.md). Having a clear understanding of device limits can help you stay under them as you continue to build your game.![A Roblox game with three overlays active.](../assets/optimization/perf-hud.png) - The device emulator in Roblox Studio is useful for checking aspect ratio and controls, but isn't accurate for memory usage; when you test a game in Studio, it runs the server and the client, so memory usage is significantly higher. > **Info:** Roblox does not have access to all of a device's memory. Some amount is required for the operating system and other applications. More generally, [testing on a variety of devices](/docs/en-us/performance-optimization/test-on-hardware.md) can help you check that the game matches your visual and performance expectations at different graphics quality levels. For a much more detailed example of how you might think about optimizing your game for low-end mobile devices, see [Real World Building and Scripting Optimization](https://devforum.roblox.com/t/real-world-building-and-scripting-optimization-for-roblox/3127146). ## Streaming and teleportation - [Instance streaming](/docs/en-us/workspace/streaming.md) lets Roblox dynamically load and unload 3D content and is a great option for most places, especially larger ones. Streaming improves join times, reduces memory footprint, and increases frame rate. For example, when you enable `Class.Workspace.EnableSLIMAvatars` and set your world models' `Class.Model.LevelOfDetail|LevelOfDetail` property to `Enum.ModelLevelOfDetail.SLIM|SLIM`, you can create worlds with social spaces and crowded events that remain visually populated while scaling across a wide range of device capabilities. For more information, see [Improve performance](/docs/en-us/performance-optimization/improve.md#instance-streaming). - Consider breaking large places into more manageable ones and using [teleportation](/docs/en-us/projects/teleport.md) to move players between them. This approach can reduce **initial** join times, but imposes additional join times as players teleport from place to place. Benefits to memory usage vary depending on the size of the place and whether you've enabled streaming. Even ignoring performance considerations, you might find that having multiple places simplifies the development process, especially if you regularly add new content to your game or are part of a larger team. ## Materials and duplication - Built-in materials use far less memory than custom textures, but might not match your artistic vision. Try to use materials whenever possible in order to conserve memory budget for the textures that are central to your game. - As you create assets, convert them into [packages](/docs/en-us/projects/assets/packages.md). Making packages part of your workflow helps avoid the common issue of duplicate assets with different IDs, which can hurt performance. - When you add meshes and textures, use and reuse them rather than importing duplicate copies. By resizing, rotating, and overlapping, you can create rich, varied environments that require very few [draw calls](/docs/en-us/performance-optimization/improve.md#draw-calls). For more information, see [Remove duplicate textures](/docs/en-us/tutorials/curriculums/environmental-art/optimize-your-experience.md#remove-duplicate-textures). ## Transparency - Avoid transparency values other than 0 (visible) and 1 (invisible). When you use partial transparency, be especially careful to avoid [high transparency overdraw](/docs/en-us/tutorials/curriculums/environmental-art/optimize-your-experience.md#delete-layered-transparencies). ## Scripting - Whenever possible, write event-driven code rather than per-frame calculations. At 60 FPS, the total budget for each frame is 16.67 milliseconds (ms). Even seemingly minor per-frame calculations can use a significant portion of that budget. - Find ways to break long-running code into manageable chunks. If a piece of code takes 100 ms to execute and you run it every frame, your game can only run at 10 FPS. If you decide to only run the code once per second in a game that otherwise runs at 60 FPS, 59 of your frames arrive after 16.67 ms... and then one after 100 ms, which causes a jarring stutter. Instead, investigate how you can break up the code. Maybe you can perform 5 ms of work per frame, use `Library.task.wait()`, and have the completed calculation every 20 frames while still maintaining 60 FPS. [Multithreading](/docs/en-us/scripting/multithreading.md), sometimes called Parallel Luau, can also help. - Use the `Datatype.RBXScriptConnection:Disconnect()` method to stop functions from being called unnecessarily the next time an event fires. - Don't call the same method every time you need a value. Call the method once, store the value, and then overwrite it later as necessary. - Don't store everything in `Class.ReplicatedStorage`. The client loads everything that is in this container. Instead, use `Class.ServerStorage` for anything that the client does not need access to. --- title: "Identify performance issues" url: /docs/en-us/performance-optimization/identify last_updated: 2026-06-29T19:34:03Z description: "Explains how to use Roblox tools to identify performance issues." --- # Identify performance issues Identifying performance issues generally means drilling down on one of three categories: frame rate (compute), memory, or load time. Roblox has a number of tools for identifying performance issues, some of which are available in the Roblox client (and therefore Studio) and some of which are only available in Studio. Whereas you can observe client behavior directly, you must use tools to diagnose server issues. ## Diagnostic tools | Tool | Description | Location | Keyboard shortcut | Environment | | --- | --- | --- | --- | --- | | [Developer Console](/docs/en-us/studio/developer-console.md) | A console with real-time log messages, errors, and detailed information on memory, networking, and script performance. It's useful for viewing memory consumption, key server health stats, script performance stats, and to launch the client or server MicroProfiler. | **Studio**
**In-Game** | `F9` | **Studio Testing**
**Live Sessions** | | [MicroProfiler](/docs/en-us/performance-optimization/microprofiler.md) | A debugging tool that breaks down how much computational time is spent on tasks each frame. It can generate dumps for analysis showing a precise breakdown of individual frames, making it helpful for identifying the specific task causing performance issues. You can run the profiler on either the client or the server, depending on which side has an issue. | **Studio**
**In-Game** | ``Ctrl``Alt``F6` (`⌘``⌥``F6`)` | **Studio Testing**
**Live Sessions** | | [Scene Analysis](/docs/en-us/performance-optimization/scene-analysis.md) | Provides at-a-glance comparative information about the resources your client and server scenes are using and which instances are responsible for that resource usage. | Studio | N/A | **Studio Testing** | | Performance Stats | A toolbar with basic performance statistics, including memory consumption, CPU, GPU, network data sent and received, and ping time. | **In-Game** | `Ctrl``Alt``F7` (`⌘``⌥``F7`) | **Studio Testing**
**Live Sessions** | | Debug Stats | Overlays with detailed information around graphics, physics, network traffic, and FPS. | **Studio**
**In-Game** | `Shift``Ctrl``F1` (`Shift``⌘``F1`), `Shift``Ctrl``F2` (`Shift``⌘``F2`), `Shift``Ctrl``F3` (`Shift``⌘``F3`), `Shift``Ctrl``F4` (`Shift``⌘``F4`), `Shift``Ctrl``F5` (`Shift``⌘``F5`) | **Studio Testing**
**Live Sessions** | | [Network Simulation](/docs/en-us/studio/testing-modes.md#network-simulation) | Properties that simulate real-world network conditions like latency, jitter, and packet loss to playtest connections. | **Studio** | `Alt``S`(`⌥``S`) | **Studio Testing** | | [Performance Dashboard](/docs/en-us/production/analytics/performance.md) | A dashboard with aggregate charts for client and server memory usage, client frame rate, server heartbeat, and crash rates in real time. It's helpful for analyzing performance patterns over time. See [monitoring performance](/docs/en-us/performance-optimization/monitor.md). | **Creator Dashboard** | N/A | **Live Sessions** | ## Server compute Server heartbeat is capped at 60 FPS for all games, so lower values might indicate a performance issue. To check server heartbeat: - With the [Developer Console](/docs/en-us/studio/developer-console.md) - In the **Server Jobs** tab, expand the **Heartbeat** row and check the **Steps Per Sec** value, which represents the heartbeat of your game. - With the [MicroProfiler](/docs/en-us/performance-optimization/microprofiler.md), you can check the graph to see if the frame takes longer than 16.67 ms. > **Info:** The MicroProfiler is particularly useful for identifying "spikes" in performance, where some frames take significantly longer than others to process. Another symptom of degraded server heartbeat is increased latency (commonly known as ping). The longer the server takes to finish computing its tasks each frame, the longer it takes to process network data sent and received from clients. To check average ping for all players connected to a server, go to the **Server Stats** tab in the [Developer Console](/docs/en-us/studio/developer-console.md). ## Client compute The default client frame rate cap is 60 FPS. However, users can raise their frame rate cap up to 240 FPS on Windows. Frame rate differs wildly between devices. For example, a high-end PC might be able to "brute force" a computational issue and only experience an imperceptible frame rate dip. If you [test on lower-end devices](/docs/en-us/performance-optimization/test-on-hardware.md), problems tend to be more severe and thus easier to notice. To check the frame rate of your game: - In the client, press `Shift``F5` to show the debug stats summary. - From Studio's **Window** ⟩ **Performance** menu, toggle on **Stats** to enable debug stats. > **Warning:** Performance stats in Studio are skewed by the Studio application, so you should view the frame rate on the client for accurate numbers. - With the [MicroProfiler](/docs/en-us/performance-optimization/microprofiler.md), you can check the graph to see if the frame takes longer than 16.67 ms. > **Info:** When evaluating frame rate, it can help to set the graphics quality to its maximum value to remove the effect of the frame rate manager. In the client, open the menu, click **Settings**, change **Graphics Mode** to manual, and raise graphics quality. ## Server memory Try to keep server memory usage below 50%. Total server memory uses the following formula: `6.25 GiB + (100 MiB * largest_number_of_connected_players)` > **Info:** These numbers use powers of 2, so 1 GiB refers to 2^30 bytes and 1 MiB to 2^20 bytes. For example, a server with 30 connected players has approximately 9.18 GiB of total memory. Servers gain memory when players connect, but **don't** lose it when players disconnect. If 10 players leave, the server still has 9.18 GiB of memory rather than shrinking to 8.2 GiB. When servers shut down (for example, when they are empty or as part of the [update process](/docs/en-us/projects/update-games.md)), their replacements start with the base amount of memory and begin scaling up as players connect. ## Client memory There are several ways to check memory usage for a game: 1. Open the [Developer Console](/docs/en-us/studio/developer-console.md) and switch to the **Memory** tab. This tab gives a breakdown of how memory is being allocated. Use the client rather than Studio to get the most accurate readouts. 2. Enable the **Performance Stats** view from the settings menu in the client to see an overlay with total client memory usage. High memory usage is not necessarily indicative of a problem, but some indications that you may need to investigate more are: - A significant percentage of client crashes showing in the **Performance Dashboard**, particularly a sudden uptick that coincides with an update. Some number of crashes are expected, but you should investigate if your crash rates increase above 2-3%. - A crash occurs while testing on a device that you want your game to support. A significant portion of a game's memory consumption on the client are from assets, such as images, meshes, and audio files, loaded into memory. In the Developer Console, check the following labels under **PlaceMemory**: - **GraphicsMeshParts** - Graphics memory consumed by meshes. - **GraphicsTexture** - Graphics memory consumed by textures. - **Sounds** - Memory consumed by audio files. ## Load times There are no built-in tools for checking load times, but because they don't require millisecond-level precision, a stopwatch is usually all you need to understand your current baseline and check whether you made a substantive improvement. You can use a client script in `Class.ReplicatedFirst` to get some sense of how your changes impact load times, but this script doesn't provide a complete, end-to-end measurement: ```lua local startTime = os.clock() game.Loaded:Connect(function() local loadTime = os.clock() - startTime local roundedLoadTime = math.round(loadTime * 10000) / 10000 -- four decimal places print("Game loaded in " .. roundedLoadTime .. " seconds.") print("Number of instances loaded: " .. #workspace:GetDescendants()) end) ``` For additional insight, enable **Print Join Size Breakdown** from the **Network** tab of [Studio Settings](/docs/en-us/studio/setup.md#customization) to print the top 20 instances by size and a percentage breakdown by instance type when you start the game in Studio. ## Ping The two primary measurements of ping (latency) are **network ping** and **data ping**. Network ping is the round-trip time measured from when the client sends a network data packet to when it receives the server's echo reply. It is similar to what would be reported as `time` by the general purpose ping network utility. Network ping is visible in the **Performance Stats** bar. It is mostly affected by the length of the network path between the client and server, and is outside your control. However, you can simulate ping and other network conditions using [network simulation](/docs/en-us/studio/testing-modes.md#network-simulation) when playtesting in Studio. Data ping is the round-trip time measured from when the client sends data reliably through the replication system to when it receives an echo reply from the server's replication system. It includes time spent in both the client's and server's replication queues, along with TCP-like retransmissions of lost or corrupted packets. Data ping is similar to sending a reliable remote event from the client, receiving it on the server, and then sending a reliable remote event from the server to the client in response. Because data ping also includes time spent on the network between the client and server, it is greater than or equal to network ping. Average data ping for all players connected to a server is visible in the **Server Stats** tab of the Developer Console. Data ping for an individual client is visible using `Shift``F3` to display network debug stats. Data ping is affected by both the network path and by replication queueing delays. When the server generates replication data faster than the network can send it (or faster than the client can process it), data must wait in replication queues. Some difference between data ping and network ping is normal due to both network retransmissions and the way network data is processed by the Roblox app. However, if data ping is significantly higher than network ping, use the [MicroProfiler](/docs/en-us/performance-optimization/microprofiler/network.md) to view network traffic. For optimization suggestions, see [networking and replication](/docs/en-us/performance-optimization/improve.md#networking-and-replication).
--- title: "Improve performance" url: /docs/en-us/performance-optimization/improve last_updated: 2026-06-29T19:34:03Z description: "Lists common performance problems and steps to mitigate them." --- # Improve performance This page describes common performance problems and best practices for mitigating them. ## Script computation Expensive operations in Luau code take longer to process and can thus impact frame rate. Unless it is being executed in parallel, Luau code runs synchronously and blocks the main thread until it encounters a function that yields the thread. ### Common problems - **Intensive operations on table structures** - Complex operations such as serialization, deserialization, and deep cloning incur a high performance cost, especially on large table structures. This is particularly true if these operations are recursive or involve iterating over very large data structures. - **High frequency events** - Tying expensive operations to frame-based events of `Class.RunService` without limiting the frequency means these operations are repeated every frame, which often results in an unnecessary increase in computation time. These events include: - `Class.RunService.PreAnimation` - `Class.RunService.PreRender` - `Class.RunService.PreSimulation` - `Class.RunService.PostSimulation` - `Class.RunService.Heartbeat` ### Mitigation - Invoke code on `Class.RunService` events sparingly, limiting usage to cases where high frequency invocation is essential (for example, updating the camera). You can execute most other code in other events or less frequently in a loop. - Break up large or expensive tasks using `Library.task.wait()` to spread the work across multiple frames. - Identify and optimize unnecessarily expensive operations and use [multithreading](/docs/en-us/scripting/multithreading.md) for computationally expensive tasks that don't need to access the data model. - Certain server-side scripts can benefit from [native code generation](/docs/en-us/luau/native-code-gen.md), a simple flag that compiles a script to machine code rather than bytecode. ### MicroProfiler scopes | Scope | Associated computation | | --- | --- | | RunService.PreRender | Code executing on the PreRender event | | RunService.PreSimulation | Code executing on the Stepped event | | RunService.PostSimulation | Code executing on Heartbeat event | | RunService.Heartbeat | Code executing on Heartbeat event | For more information on debugging scripts using the MicroProfiler, see the `Library.debug` library, which includes functions for tagging specific code and further increasing specificity, such as `Library.debug.profilebegin` and `Library.debug.profileend`. Many Roblox API methods called by scripts also have their own associated MicroProfiler tags that can provide useful signal. ## Script memory usage Memory leaks can occur when you write scripts that consume memory that the garbage collector can't properly release when its no longer in use. Leaks are specifically pervasive on the server, because they can continuously be online for many days, whereas a client session is much shorter. The following memory values in the **Developer Console** can indicate a problem that needs further investigation: - **LuaHeap** - High or growing consumption suggests a memory leak. - **InstanceCount** - Consistently growing numbers of instances suggest references to some instances in your code are not being garbage collected. - **PlaceScriptMemory** - Provides a script by script breakdown of memory usage. ### Common problems - **Leaving connections connected** - The engine never garbage collects events connected to an instance and any values referenced inside the connected callback. Therefore, active connections of events and code inside the connected instances, connected functions, and referenced values, are out of scope for the memory garbage collector, even after the events are fired. Although events are disconnected when the instance they belong to is destroyed, a common mistake is to assume this applies to `Class.Player` objects. After a user leaves a game, the engine doesn't automatically destroy their representative `Class.Player` object and character model, so connections to the `Class.Player` object and instances under the character model, such as `Class.Player.CharacterAdded|CharacterAdded`, still consume memory if you don't disconnect them in your scripts. This can result in very significant memory leaks over time on the server as hundreds of users join and leave the game. - **Tables** - Inserting objects into tables but not removing them when they are no longer needed causes unnecessary memory consumption, especially for tables that track user data when they join. For example, the following code sample creates a table adding user information each time a user joins:```lua local playerInfo = {} Players.PlayerAdded:Connect(function(player) playerInfo[player] = {} -- some info end) ``` If you don't remove these entries when they are no longer needed, the table continues to grow in size and consumes more memory as more users join the session. Any code that iterates over this table also becomes more computationally expensive as the table grows in size. ### Mitigation To clean up all used values for preventing memory leaks: - **Disconnect all connections** - Go through your codebase and make sure each connection is cleaned up via one of the following paths: - Disconnecting manually using the `Datatype.RBXScriptConnection:Disconnect()|Disconnect()` function. - Destroying the instance the event belongs to with the `Class.Instance:Destroy()|Destroy()` function. - Destroying the script object that the connection traces back to. - **Remove player objects and characters after leaving** - Enable `Class.Workspace.PlayerCharacterDestroyBehavior` to automatically destroy player objects and character models after a user leaves. If you prefer, you can instead clean them up manually:```lua local Players = game:GetService("Players") Players.PlayerAdded:Connect(function(player) player.CharacterRemoving:Connect(function(character) task.defer(character.Destroy, character) end) end) Players.PlayerRemoving:Connect(function(player) task.defer(player.Destroy, player) end) ``` ## Physics computation Excessive physics simulation can be a key cause of increased computation time per frame on both the server and the client. ### Common problems - **Excessive physics time step frequency** - By default, stepping behavior is in [adaptive mode](/docs/en-us/physics/adaptive-timestepping.md), where physics steps at either 60 Hz, 120 Hz, or 240 Hz, depending on the complexity of the physics mechanism. A fixed mode with improved accuracy of physics is also available, which forces all physics assemblies to step at 240 Hz (four times per frame). This results in significantly more computation each frame. - **Excessive number of complexity of simulated objects** - The more 3D assemblies that are simulated, the longer physics computations take each frame. Often, games will have objects being simulated that do not need to be or will have mechanisms that have more constraints and joints than they need. - **Overly precise collision detection** - Mesh parts have a `Class.MeshPart.CollisionFidelity|CollisionFidelity` property for detecting collision which offers a variety of modes with different levels of performance impact. Precise collision detection mode for mesh parts has the most expensive performance cost and takes the engine longer to compute. ### Mitigation - **Anchor parts that don't require simulation** - Anchor all parts that don't need to be driven by physics, such as for static NPCs. - **Use adaptive physics stepping** - Adaptive stepping dynamically adjusts the rate of physics calculations for physics mechanisms, allowing physics updates to be made less frequently in some cases. - **Reduce mechanism complexity** - Where possible, minimize the number of physics constraints or joints in an assembly. - Reduce the amount of self-collision within a mechanism, such as by applying limits or no-collision constraints to ragdoll limbs to prevent them from colliding with each other. - **Reduce the usage of precise collision fidelity for meshes** - For small or non-interactable objects where users would rarely notice the difference, use box fidelity. - For small-medium size objects, use box or hull fidelities, depending on the shape. - For large and very complex objects, build out custom collisions using invisible parts when possible. - For objects that don't require collisions, disable collisions and use box or hull fidelity, since the collision geometry is still stored in memory. - You can render collision geometry for debug purposes in Studio by toggling on **Collision fidelity** from the [Visualization Options](/docs/en-us/studio/ui-overview.md#visualization-options) widget in the upper‑right corner of the 3D viewport. Alternatively, you can apply the `CollisionFidelity=PreciseConvexDecomposition` filter to the [Explorer](/docs/en-us/studio/explorer.md#property-filters) which shows a count of all mesh parts with the precise fidelity and allows you to easily select them. - For an in-depth walkthrough on how to choose a collision fidelity option that balances your precision and performance requirements, see [Set physics and rendering parameters](/docs/en-us/tutorials/curriculums/environmental-art/assemble-an-asset-library.md#collisionfidelity). ### MicroProfiler scopes | Scope | Associated computation | | --- | --- | | physicsStepped | Overall physics computation | | worldStep | Discrete physics steps taken each frame | ## Physics memory usage Physics movement and collision detection consumes memory. Mesh parts have a `Class.MeshPart.CollisionFidelity|CollisionFidelity` property that determines the approach that's used to evaluate the collision bounds of the mesh. ### Common problem The default and precise collision detection modes consumes significantly more memory than the two other modes with lower fidelity collision shapes. If you see high levels of memory consumption under **PhysicsParts**, you might need to explore reducing the [collision fidelity](/docs/en-us/workspace/collisions.md#mesh-and-solid-model-collisions) of objects in your game. ### How to mitigate To reduce memory used for collision fidelity: - For parts that do not need collisions, disable their collisions by setting `Class.BasePart.CanCollide`, `Class.BasePart.CanTouch` and `Class.BasePart.CanQuery` to `false`. - Reduce fidelity of collisions using the `Class.MeshPart.CollisionFidelity|CollisionFidelity` setting. `Enum.CollisionFidelity.Box|Box` has the lowest memory overhead, and `Enum.CollisionFidelity.Default|Default` and `Enum.CollisionFidelity.Precise|Precise` are generally more expensive. - It's generally safe to set any small anchored part's collision fidelity to `Enum.CollisionFidelity.Box|Box`. - For very complex large meshes, you may want to build your own collision mesh out of smaller objects with box collision fidelity. ## Humanoids `Class.Humanoid` is a class that provides a wide range of functionalities to player and non player characters (NPCs). Although powerful, a `Class.Humanoid` comes with a significant computation cost. ### Common problems - **Leaving all HumanoidStateTypes enabled on NPCs** - There is a performance cost to leaving certain `Enum.HumanoidStateType|HumanoidStateTypes` enabled. Disable any that are not needed for your NPCs. For example, unless your NPC is going to climb ladders, it's safe to disable the `Enum.HumanoidStateType.Climbing|Climbing` state. - **Instantiating, modifying, and respawning models with `Class.Humanoid|Humanoids` or [skinned](/docs/en-us/art/modeling/rigging.md) `Class.MeshPart|MeshParts` frequently** - This can be intensive for the engine to process, particularly if these models use **layered clothing**. This also can be particularly problematic in games where avatars respawn often. - In the MicroProfiler, lengthy **updateInvalidatedFastClusters** tags (over 4 ms) are often a signal that avatar instantiation/modification is triggering excessive invalidations. - **Using Humanoids in cases where they are not required** - Static NPCs that do not move generally have no need for the `Class.Humanoid` class. - **Playing animations on a large number of NPCs from the server** - NPC animations that run on the server need to be simulated on the server and replicated to the client. This can be unnecessary overhead. - **Performing unnecessary size and scale changes** - Size/scale changes cause FastCluster to be rebuilt. Try to reduce this during gameplay if you are seeing FastCluster-related performance issues. Similarly, other property changes might also cause FastCluster to be rebuilt, so in general reduce these changes as much as possible. ### Mitigation - **Play NPC animations on the client** - In games with a large number of NPCs, consider creating the `Class.Animator` on the client and running the animations locally. This reduces the load on the server and the need for unnecessary replication. It also makes additional optimizations possible (such as only playing animations for NPCs who are near to the character). - **Use performance-friendly alternatives to Humanoids** - NPC models don't necessarily need to contain a humanoid object. - For static NPCs, use a simple `Class.AnimationController`, because they don't need to move around but just need to play animations. - For moving NPCs, consider implementing your own movement controller and using an `Class.AnimationController` for animations, depending on the complexity of your NPCs. - **Disable unused humanoid states** - Use `Class.Humanoid:SetStateEnabled()` to only enable necessary states for each humanoid. - **Pool NPC models with frequent respawning** - Instead of destroying an NPC completely, send the NPC to a pool of inactive NPCs. This way, when a new NPC is required to respawn, you can simply reactivate one of the NPCs from the pool. This process is called pooling, which minimizes the amount of times characters need to be instantiated. - **Only spawn NPCs when users are nearby** - Don't spawn NPCs when users aren't in range, and cull them when users leave their range. - **Avoid making changes to the avatar hierarchy after it is instantiated** - Certain modifications to an avatar hierarchy have significant performance implications. Some optimizations are available: - For custom procedural animations, don't update the `Class.JointInstance.C0` and `Class.JointInstance.C1` properties. Instead, update the `Class.Motor6D.Transform` property. - If you need to attach any `Class.BasePart` objects to the avatar, do so outside the hierarchy of the avatar `Class.Model`. ### MicroProfiler scopes | Scope | Associated computation | | --- | --- | | stepHumanoid | Humanoid control and physics | | stepAnimation | Humanoid and animator animation | | updateInvalidatedFastClusters | Associated with instantiating or modifying an avatar | ## Rendering A significant portion of the time the client spends each frame is on rendering the scene in the current frame. The server doesn't do any rendering, so this section is exclusive to the client. ### Draw calls A draw call is a set of instructions from the engine to the GPU to render something. Draw calls have significant overhead. Generally, the fewer draw calls per frame, the less computational time is spent rendering a frame. You can see how many draw calls are currently occurring with the **Render Stats** ⟩ **Timing** item in Studio. You can view **Render Stats** in the client by pressing `Shift``F2`. The more objects that need to be drawn in your scene in a given frame, the more draw calls are made to the GPU. However, the Roblox Engine utilizes a process called _instancing_ to collapse identical meshes with the same texture characteristics into a single draw call. Specifically, multiple meshes with the same `Class.MeshPart.MeshContent|MeshContent` are handled in a single draw call when: - `Class.SurfaceAppearance|SurfaceAppearances` are identical if present, otherwise when `Class.MeshPart.TextureContent|TextureContents` are identical. - Materials are identical when both `Class.SurfaceAppearance` and `Class.MeshPart.TextureID` don't exist. ### Other common problems - **Excessive object density** - If a large number of objects are concentrated with a high density, then rendering this area of the scene requires more draw calls. If you are finding your frame rate drops when looking at a certain part of the map, this can be a good signal that object density in this area is too high. Objects like decals, textures, and particles don't batch well and introduce additional draw calls. Pay extra attention to these object types in a scene. In particular, property changes to `Class.ParticleEmitter|ParticleEmitters` can have a dramatic impact on performance. - **Missed instancing opportunities** - Often, a scene will include the same mesh duplicated a number of times, but each copy of the mesh has different mesh or texture asset IDs. This prevents instancing and can lead to unnecessary draw calls. A common cause of this problem is when an entire scene is imported at once, rather than individual assets being imported into Roblox and then duplicated post-import to assemble the scene. Even a simple script like this one can help you identify mesh parts with the same name that use different mesh IDs:```lua for _,descendant in workspace:GetDescendants() do if descendant:IsA("MeshPart") then print(descendant.Name .. ", " .. descendant.MeshId) end end ``` The output (with **Stack Lines** enabled) might look something like this. Repeated lines indicate reuse of the same mesh, which is good. Unique lines aren't necessarily bad, but depending on your naming scheme, could indicate duplicate meshes in your game:```text LargeRock, rbxassetid://106420009602747 (x144) -- good LargeRock, rbxassetid://120109824668127 LargeRock, rbxassetid://134460273008628 LargeRock, rbxassetid://139288987285823 LargeRock, rbxassetid://71302144984955 LargeRock, rbxassetid://90621205713698 LargeRock, rbxassetid://113160939160788 LargeRock, rbxassetid://135944592365226 -- all possible duplicates ``` - **Excessive object complexity** - Although not as important as the number of draw calls, the number of triangles in a scene does influence how long a frame takes to render. Scenes with a very large number of very complex meshes are a common problem, as are scenes with the `Class.MeshPart.RenderFidelity` property set to `Enum.RenderFidelity.Precise|Precise` on too many meshes. - **Excessive shadow casting** - Handling shadows is an expensive process, and maps that contain a high number and density of light objects that cast shadows (or a high number and density of small parts influenced by shadows) can have performance issues. - **High transparency overdraw** - Placing objects with partial transparency near each other forces the engine to render the overlapping pixels multiple times, which can hurt performance. For more information on identifying and fixing this issue, see [Delete layered transparencies](/docs/en-us/tutorials/curriculums/environmental-art/optimize-your-experience.md#delete-layered-transparencies). - **Unnecessary skinned MeshPart movement** - Skinned MeshParts that are part of a Model without a Humanoid are grouped using spatially-organized FastClusters. When these MeshParts move, they must be continually added to and removed from these spatial clusters, forcing the clusters to be rebuilt and impacting performance. - A highly effective workaround is to embed a Humanoid within the Model. The presence of a Humanoid overrides the default spatial clustering behavior, mandating the use of a single, unified FastCluster for the entire Model. Consequently, position updates no longer necessitate cluster rebuilds, thus mitigating the performance bottleneck. This technique should be reserved exclusively for MeshParts with expected movement, as it may introduce memory overhead and negate the benefits of spatial optimization. We recommend always profile your game after making these types of changes. See [Humanoid performance tips](#humanoids) for additional information. - **Too many parts in a `Class.Model`** - Too many parts in a Model could cause rebuilds more often due to the potential for a part's property to change leading to requiring a full rebuild. Find the right balance of parts in a Model when it is using FastCluster. ### Mitigation - **Instancing identical meshes and reducing the amount of unique meshes** - If you ensure all identical meshes to have the same underlying asset IDs, the engine can recognize and render them in a single draw call. Make sure to only upload each mesh in a map once and then duplicate them in Studio for reuse rather than importing large maps as a whole, which might cause identical meshes to have separate content IDs and be recognized as unique assets by the engine. [Packages](/docs/en-us/projects/assets/packages.md) are a helpful mechanism for object reuse. - **Culling** - Culling describes the process of eliminating draw calls for objects that don't factor into the final rendered frame. By default, the engine skips draw calls for objects outside the camera's field of view (frustum culling) and parts, meshes, and terrain occluded from view by other objects (occlusion culling). In certain scenarios, such as indoor environments, you might be able to implement a room or portal system and manually cull objects to further reduce draw calls or overall computational load. - **Reducing level of detail for models** - Enable [instance streaming](/docs/en-us/workspace/streaming.md) and set your world models' `Class.Model.LevelOfDetail|LevelOfDetail` property to `Enum.ModelLevelOfDetail.SLIM|SLIM` to render [optimized lightweight SLIM meshes](/docs/en-us/workspace/streaming/techniques.md#model-level-of-detail) for models as distance from the camera increases. - **Reducing level of detail for avatars** - Enable instance streaming and set the `Class.Workspace.EnableSLIMAvatars` property to render platform avatar models with any number of accessories or layers of clothing as [optimized lightweight avatar representations](/docs/en-us/workspace/streaming/techniques.md#slim-avatars) with full support for animations as distance from the camera increases. - **Reducing render fidelity** - Set `Class.MeshPart.RenderFidelity` to `Enum.RenderFidelity.Automatic|Automatic` or `Enum.RenderFidelity.Performance|Performance`. This allows meshes to fall back to less complex alternatives, which can reduce the number of polygons that need to be drawn. - **Disabling shadow casting on appropriate parts and light objects** - The Roblox engine automatically degrades shadow quality as client graphics quality level decreases, eventually disabling shadows altogether at quality levels below 4. However, you can selectively disable shadow casting properties on light objects and parts to improve performance while shadows are enabled and increase the likelihood that shadows remain enabled. Some examples of optimizations you can make either at edit time or dynamically at runtime: - Use the `Class.BasePart.CastShadow` property to disable shadow casting on small parts where shadows are unlikely to be visible. This strategy is particularly effective when applied to parts that are far away from the user's camera. > **Warning:** This might result in visual artifacts on shadows. - Disable shadows on moving objects when possible. - Disable `Class.Light.Shadows` on light instances where the object does not need to cast shadows. - Limit the range and angle of light instances. - Use fewer light instances. - Consider disabling lights that are outside of a specific range or on a room-by-room basis for indoor environments. ### MicroProfiler scopes | Scope | Associated computation | | --- | --- | | Prepare and Perform | Overall rendering | | Perform/Scene/computeLightingPerform | Light grid and shadow updates | | LightGridCPU | Voxel light grid updates | | ShadowMapSystem | Shadow mapping | | Perform/Scene/UpdateView | Preparation for rendering and particle updates | | Perform/Scene/RenderView | Rendering and post processing | ## Networking and replication Networking and replication describes the process by which data is sent between the server and connected clients. Information is sent between the client and server every frame, but larger amounts of information require more compute time. ### Common problems - **Excessive remote traffic** - Sending a large amount of data through `Class.RemoteEvent` or `Class.RemoteFunction` objects or invoking them very frequently can lead to a large amount of CPU time being spent processing incoming packets each frame. Common mistakes include: - Replicating data every frame that does not need to be replicated. - Replicating data on user input without any mechanism to throttle it. - Dispatching more data than is required. For example, sending the player's entire inventory when they purchase an item rather than just details of the item purchased. - **Creation or removal of complex instance trees** - When a change is made to the data model on the server, it is replicated to connected clients. This means creating and destroying large instance hierarchies like maps at runtime can be very network intensive. A common culprit here is the complex animation data saved by **Animation Editor** plugins in rigs. If these aren't removed before the game is published and the animated model is cloned regularly, a large amount of data will be replicated unnecessary. - **Server-side TweenService** - If `Class.TweenService` is used to tween an object server side, the tweened property is replicated to each client every frame. Not only does this result in the tween being jittery as clients' latency fluctuates, but it causes a lot of unnecessary network traffic. ### Mitigation You can employ the following tactics to reduce unnecessary replication: - **Avoid sending large amounts of data at once through remote events**. Instead, only send necessary data at a lower frequency. For example, for a character's state, replicate it when it changes rather than every frame. - **Chunk up complex instance trees** like maps and load them in pieces to distribute the work replicating these across multiple frames. - **Clean up animation metadata**, especially the animation directory of rigs, after importing. - **Limit unnecessary instance replication**, especially in cases where the server doesn't need to have knowledge of the instances being created. This includes: - Visual effects such as an explosion or a magic spell blast. The server only needs to know the location to determine the outcome, while the clients can create visuals locally. - First-person item view models. - Tween objects on the client rather than the server. ### MicroProfiler scopes | Scope | Associated computation | | --- | --- | | ProcessPackets | Processing for incoming network packets, such as event invocations and property changes | | Allocate Bandwidth and Run Senders | Outgoing events relevant on servers | ## Asset memory usage The highest impact mechanism available to creators to improve client memory usage is to enable [instance streaming](/docs/en-us/workspace/streaming.md). ### Instance streaming Instance streaming selectively loads out parts of the data model that are not required, which can lead to considerably reduced load times and increase the client's ability to prevent crashes when it comes under memory pressure. If you are encountering memory issues and have instance streaming disabled, consider updating your game to support it, particularly if your 3D world is large. Instance streaming is based on distance in 3D space, so larger worlds naturally benefit more from it. If instance streaming is enabled, you can increase the aggressiveness of it. For example, consider: - Reducing use of `Enum.ModelStreamingMode.Persistent` where possible. You may need to update your scripts if you're using it as a compatibility measure. - Reducing the `Workspace.StreamingMinRadius` and `Workspace.StreamingTargetRadius`. For more information on streaming options and their benefits, see [streaming properties](/docs/en-us/workspace/streaming.md#streaming-properties). ### Other common problems - **Asset duplication** - A common mistake is to upload the same asset multiple times resulting in different asset IDs. This can lead to the same content being loaded into memory multiple times. - **Excessive asset volume** - Even when assets are not identical, there are cases when opportunities to reuse the same asset and save memory are missed. - **Audio files** - Audio files can be a surprising contributor to memory usage, particularly if you load all of them into the client at once rather than only loading what you need for a portion of the game. For strategies, see [Load times](#load-times). - **High resolution textures** - Graphics memory consumption for a texture is unrelated to the size of the texture on the disk; the number of pixels in the texture determines memory usage. For example, a 1024x1024 pixel texture consumes four times the graphics memory of a 512x512 texture. Images uploaded to Roblox are transcoded to a fixed format, so there is no memory benefit to uploading images in a color model associated with fewer bytes per pixel. Similarly, compressing images prior to upload or removing the alpha channel from images that don't need it can decrease image size on disk, but doesn't improve memory usage. As a game loads, the engine automatically starts with lower quality textures and then ramps up quality based on available device memory, distance from the camera, amount of screen-space that the texture takes up, and other factors. Even still, strategically sizing your textures can improve memory usage in your game. ### Mitigation - **Only upload assets once** - Reuse the same asset ID across objects and ensure the same assets, especially meshes and images, aren't uploaded separately multiple times. - **Find and fix duplicate assets** - Look for identical mesh parts and textures that are uploaded multiple times with different IDs. - Though there is no API to detect similarity of assets automatically, you can collect all the image asset IDs in your place (either manually or with a script), download them, and compare them using external comparison tools. - For mesh parts, the best strategy is to take unique mesh IDs and organize them by size to manually identify duplicates. - Instead of using separate textures for different colors, upload a single texture and use the `Class.SurfaceAppearance.Color` property to apply various tints to it. - **Import assets in map separately** - Instead of importing an entire map at once, import and reconstruct assets in the map individually and reconstruct them. The Importer doesn't do any de-duplication of meshes, so if you were to import a large map with a lot of separate floor tiles, each of those tiles would be imported as a separate asset (even if they are duplicates). This can lead to performance and memory issues down the line, as each mesh is treated as individually and takes up memory and draw calls. - **Limit the pixels of images** to no more than the necessary amount. Unless an image is occupying a large amount of physical space on the screen, it usually needs at most 512x512 pixels. Most minor images should be smaller than 256x256 pixels. - **Use trim sheets** to ensure maximum texture reuse in 3D maps. For steps and examples on how to create trim sheets, see [Create trim sheets](/docs/en-us/resources/beyond-the-dark/building-architecture.md#create-trim-sheets). You might also consider using sprite sheets to load many smaller UI images as a single image. You can then use `Class.ImageLabel.ImageRectOffset` and `Class.ImageLabel.ImageRectSize` to display portions of the sheet. ## Load times Many games implement custom loading screens and use the `Class.ContentProvider:PreloadAsync()` method to request assets so that images, sounds, and meshes are downloaded in the background. The advantage of this approach is that it lets you ensure important parts of your game are fully loaded without pop-in. However, a common mistake is overutilizing this method to preload more assets than are actually required. An example of a bad practice is loading the entire `Class.Workspace`. While this might prevent texture pop-in, it significantly increases load times. Another similar practice is utilising `Class.ContentProvider.RequestQueueSize` to ensure that all requested assets have finished loading. However, this presents the same issue of significantly increased load times, while also being an unreliable method due to its fluctuating nature. Instead, only use `Class.ContentProvider:PreloadAsync()` in necessary situations, which include: - Images in the loading screen. - Important images in your game menu, such as button backgrounds and icons. - Important assets in the starting or spawning area. If you must load a large number of assets, we recommend you provide a **Skip Loading** button. --- title: "Performance optimization" url: /docs/en-us/performance-optimization last_updated: 2026-06-29T19:34:03Z description: "Introduces the performance optimization process for games on Roblox." --- # Performance optimization Performance optimization is the process of building and iterating on a game to improve frame rate, memory usage, and load times. Players expect games to perform well, so taking the time to understand and improve your game's performance can be critical to its success. The first step in performance optimization is to [design for performance](/docs/en-us/design.md), which means following a set of best practices **as you build** a new game. After you publish a game, the optimization process follows a common cycle: 1. [Identify performance Issues](/docs/en-us/identify.md) - Use Roblox's built-in tools to find performance problems and identify root causes. 2. [Improve performance](/docs/en-us/improve.md) - After you find an issue, see if it's a common one and how you might mitigate it. You might also want to take proactive action. 3. [Monitor performance](/docs/en-us/monitor.md) - Use analytics tools to monitor the impact of your changes and watch for indicators of any performance issues. ![A diagram showing the flow from designing, identifying, improving, and monitoring performance.](../assets/optimization/Perf-Flow.png) ## Why performance matters To put it simply, performant games are more fun. When a game has a consistent, smooth frame rate, even basic actions like running and jumping feel more precise and enjoyable. Aiming can go from feeling impossible to feeling completely natural. A game with a small memory footprint and fast load times might always have plenty of other people to play with. Performance optimization also lets you expand your ambitions. If your game runs well, you might be able to make it more expansive or beautiful or add new systems to it. From a business perspective, performant games often have higher user engagement, better user retention, and make more money. ## Measure performance - **Frame rate** is the number of unique frames that the client presents to the user, measured in frames per second (FPS). The cause of low frame rates is typically expensive computational operations—in other words, overtaxing the CPU, GPU, or both. Because the server doesn't render frames, it doesn't _technically_ have a frame rate, but server heartbeat is essentially the same thing: the number of times per second that the server updates its simulation of the world. A low server heartbeat reduces the frame rate for all clients and increases latency. On the client, low or inconsistent frame rates reduce the smoothness and responsiveness of the game. By default, Roblox games target 60 FPS, which is one frame every 16.67 milliseconds with proper frame pacing. - **Memory usage** is the amount of RAM or swap that your game uses. Even if a game has low starting memory usage, memory leaks can cause that amount to increase over time. On the server, excessive memory usage can cause crashes, which disconnect all players from the game. Excessive memory usage causes client crashes, too, but it also prevents users on lower-end devices from playing your game in the first place. Reducing memory usage can greatly expand your addressable audience, especially on mobile. - **Load time** is the amount of time it takes for a player to start enjoying your game. Long load times are annoying and hurt user acquisition and retention. Players might decide a game simply isn't worth the wait. Conversely, quick load times can give your game a "jump in" feel that makes players more likely to engage with it when they have a spare moment. --- title: "MicroProfiler" url: /docs/en-us/performance-optimization/microprofiler last_updated: 2026-06-29T19:34:03Z description: "The MicroProfiler is a Studio and client tool for optimizing your games on Roblox." --- # MicroProfiler The **MicroProfiler** is a performance optimization and troubleshooting tool in Roblox Studio and the Roblox client. It provides detailed, visual timing information for all the [engine tasks](/docs/en-us/performance-optimization/task-scheduler.md) that run during a frame, like animating characters, updating physics, running scripts, and rendering geometry. You can identify which tasks ran during which frame, how long these tasks took to run, and whether any caused you to miss your frame time goals. ## Frame times To use the MicroProfiler, you must understand the relationship between **frame time** and **frame rate**. - Frame time is how long a frame took to render on screen, generally measured in milliseconds. - Frame rate is the number of frames rendered on screen, generally measured in frames per second (FPS). Lower frame times mean higher frame rate. Divide 1,000 milliseconds (1 second) by frame time to get frame rate. | Average frame time | Frames per second | | --- | --- | | 33.33 ms | 30 FPS | | 16.67 ms | 60 FPS | | 8.33 ms | 120 FPS | | 4.17 ms | 240 FPS | For smooth gameplay, however, it's not enough to have a high frame rate. You need **consistent frame times**. For example, if 59 frames arrive in 10 milliseconds and one frame in 410 milliseconds, players perceive a huge, jarring stutter, even though the game is running at 60 FPS. If all frames take roughly the same amount of time to render, your game will feel perceptibly smoother due to that consistency, especially at lower frame rates. The MicroProfiler focuses **entirely on frame time**. Its purpose is to help you identify frame time spikes and what caused them. ## Open the MicroProfiler Opening the MicroProfiler varies by platform: - On the mobile client, open the **Settings** menu and change the MicroProfiler to **On**. Then from a development machine **on the same network**, use your web browser to navigate to the provided IP address and port.![The MicroProfiler UI on mobile.](../../assets/optimization/microprofiler/micro-mobile.png) This screenshot shows an IP address of `172.18.56.105` and a port of `1338`, so you navigate to `172.18.56.105:1338`. Depending on your device's network configuration, you might see multiple IP addresses. If one doesn't work, try the next. > **Info:** By default, the MicroProfiler web UI shows the 30 most recent frames from your mobile device. For a larger set of frames, add a slash and a number to the URL, such as `172.18.56.105:1338/90`. To get a fresh set of frames, use the **Re-capture** button. - On the desktop client, press `Ctrl``F6` (`⌘``F6`). - In Studio, press `Ctrl``F6` (`⌘``F6`). Generally, the mobile client is the best place to profile your game. Most players on Roblox use phones and tablets, and these devices have severe thermal and power constraints that limit their performance. If your game runs well on a midrange Android tablet, it almost certainly performs much, much better on a gaming desktop. > **Success:** Powerful devices like gaming desktops can actually obscure performance problems, especially if you have a frame rate cap in place. For example, if you cap FPS to 60 on the client, you might not notice the difference between a frame time of 4 milliseconds and 16 milliseconds, even though the latter is four times as long, because they all arrive in under 16.67 milliseconds. On a mobile device that is struggling to stay at 30 FPS, a frame that takes four times as long (133 ms) is hard to miss. ## MicroProfiler basics The MicroProfiler has two key components: the **frame time bar graph** and the **detailed task timeline**. - The frame time graph runs along the top of the interface and shows how long each frame took to render. Taller bars mean longer frame times. - The timeline shows all of the individual tasks that ran during the frame. Wider bars represent tasks that took longer to run. A typical workflow in the MicroProfiler is to use the frame time graph to identify a spike (a taller bar) and then use the timeline to identify which tasks caused the spike. Then you can check the [Tag reference](/docs/en-us/performance-optimization/tag-table.md) for information on the task and how to improve its performance. ![Example of a frame time spike.](../../assets/optimization/microprofiler/micro-spike.png) ### Frame time graph The height of each bar indicates the number of milliseconds that it took to complete the frame. More recent frames are on the right. Hover over a frame for some basic information around CPU and GPU usage. ![The Microprofiler frame graph, showing blue frames and detailed frame information.](../../assets/optimization/microprofiler/micro-frames.png) - Orange bars are frames where the **Jobs Wall Time** exceeds the **Render Wall Time**. In these frames, at least one of the [worker threads](#threads), which do things like run scripts, calculate physics, and play animations, took longer to run than the [main render thread](#threads). If the game is not reaching your frame time goals and has a large number of orange frames, common causes are scripts, physics, and animations. See [Improve performance](/docs/en-us/performance-optimization/improve.md#script-computation). - Blue bars are frames where the **Render Wall Time** exceeds the **Jobs Wall Time**. In these frames, the main render thread took more time than any of the worker threads. If the game is not reaching your frame time goals and has a large number of blue frames, that indicates a rendering bottleneck. Common causes are excessive object density, object movement, and lighting. See [Improve performance](/docs/en-us/performance-optimization/improve.md#rendering). - Red bars are frames where two conditions are true: - **Render Wall Time** exceeds **Jobs Wall Time** - **GPU Wait Time** is greater than 2.5 milliseconds Red bars are less common than orange and blue and often the result of excessive object complexity, texture size, and visual effects. Optimization is similar to blue bars. See [Improve performance](/docs/en-us/performance-optimization/improve.md#rendering). Tiny tasks at the end of a frame can sometimes throw off the **Jobs Wall Time** and **Render Wall Time**, which is a good reason to focus more on frame time than frame color. There's no "good" color to strive for. A mixture of orange, blue, and red isn't problematic as long as you're reaching your frame time goals. If you **aren't** reaching your frame time goals, the colors can indicate where to optimize. ### Timeline - Left-click and drag to pan the timeline. On a machine with many CPU cores, you might need to pan up or down quite a bit to find the [thread](#threads) performing a particular task.![The lefthand side of the Microprofiler detailed view, with rows for threads.](../../assets/optimization/microprofiler/micro-panning.png) - Scrolling zooms the timeline in and out. Combined with the millisecond labels at the top of the timeline, you can get a sense of how long a task took in an absolute sense, but also how long it took relative to other tasks.![The Microprofiler detailed view with numerous horizontal labels.](../../assets/optimization/microprofiler/micro-timeline.png) - The green overlay on the bar graph indicates the number of frames currently visible on the timeline—your zoom level. Left-click on a frame bar to jump to that position on the timeline. You might have to adjust your zoom level, but hovering over a bar highlights the frame on the timeline. Light grey lines delineate frames.![The MicroProfiler bar graph with green overlay to show zoom level.](../../assets/optimization/microprofiler/micro-detailed.png) - Labels that appear directly below another label indicate tasks that are performed as part of the higher-level task. Rather than the parent task, you typically want to troubleshoot the worst-performing child tasks; a parent task can't be shorter than the sum of its child tasks. - Right-click a label to zoom the timeline to exactly the duration of that task. - If the amount of information is overwhelming, use the **Groups** or **Threads** menu to filter the timeline. For example, you might only want to see **Render** tasks in the main thread.![The on-hover view for a label, with Group highlighted.](../../assets/optimization/microprofiler/micro-group.png) - Use `Ctrl``F` (`⌘``F`) to jump to the occurrence of a task that takes up the most time in the dump. For example, `computeLightingPerform` runs in every client frame. If you search for it in a dump with 128 frames, you can jump to the frame where it took the longest to complete. ## Threads Like many programs, Roblox uses multiple threads to perform sequences of tasks at the same time. The MicroProfiler displays the thread name on the lefthand side of the timeline. ![The lefthand side of the Microprofiler detailed view, with rows for threads.](../../assets/optimization/microprofiler/micro-panning.png) There are three important thread types: - **Main** ("RBX Main"): Handles CPU-based rendering tasks. Processes input, `Class.Humanoid|Humanoids`, animations/tweening, physics ownership, sound, and waiting script resumes. Also updates Studio interfaces and coordinates the other threads. - **Worker** ("RBX Worker"): Helps the main thread with networking, physics, and pathfinding. Due to the number of cores in modern CPUs, you likely have many worker threads, most of which are in a sleep state at any given time. - **Render** ("GPU"): Follows a "prepare, perform, present" logic. Communicates with the graphics processing unit (GPU) of the device. - Prepare: Information from the main thread is used to update rendering models. - Perform: Issue rendering commands, including 2D interfaces. - Present: Synchronizes with the GPU. ## Custom script profiles If your scripts are running complicated tasks, you can profile critical portions of the code to ensure that they're not taking too long. Wrap code with `Library.debug.profilebegin()` and `Library.debug.profileend()` to time everything done between those function calls and create a label on the MicroProfiler timeline. ```lua local RunService = game:GetService("RunService") local function onPreSimulation() debug.profilebegin("Hard Work") -- Example hard work: swap two variables 200,000 times local a, b = 0, 1 for _ = 1, 200000 do a, b = b, a end debug.profileend() end RunService.PreSimulation:Connect(onPreSimulation) ``` ![Many labels on the MicroProfiler, with a custom label taking up the majority of the processing time.](../../assets/optimization/microprofiler/custom-profile.png) From its duration on the timeline, you can tell that the function is using a lot of processing time compared to other tasks. ## Save frame data If you want to save a set of frame data for later review (or to share with someone else), use the **Save to file** button. The MicroProfiler saves frame data to a standalone HTML file named `microprofile-- --- title: "MicroProfiler modes" url: /docs/en-us/performance-optimization/microprofiler/modes last_updated: 2026-06-29T19:34:03Z description: "The MicroProfiler includes several different modes, all of which offer different ways to understand your game's performance profile." --- # MicroProfiler modes The MicroProfiler includes several modes, which can help you visualize your game's performance characteristics in different ways. After opening the MicroProfiler, use the **Modes** menu to switch between them. **Frame mode** and **detailed mode** are covered under [MicroProfiler basics](/docs/en-us/performance-optimization/microprofiler.md#microprofiler-basics). ## Timers mode **Timers mode** is an alternative way of visualizing the data in the detailed view: as a list of labels with processing times and call counts. Horizontal bar graphs in some columns help you spot the busiest tasks. ![Timers mode.](../../assets/optimization/microprofiler/micro-timers-mode.png) Controls are similar to detailed view: - Left-click and hold to pan up and down. - Left-click a label to add it to the line graph in the lower-right. - Use the **Groups** menu to filter the list. - Use the **Timers** menu to customize the columns. > **Info:** All of the information in this view is visible when you hover over a label in the detailed view. ## Counters mode Counters mode is a lengthy list of categories and statistics, including instance count and memory usage (in bytes) for the various tasks. ![Counters mode with a single graph.](../../assets/optimization/microprofiler/micro-counters-mode.png) - Just like the detailed view, left-click and hold to pan up and down. - Left-click in the **Graph** column to add a small usage graph with minimum and maximum values. Left-click the graph again to expand it. - Right-click a graph to close it. - You can't filter this view, but you can left-click on a category (for example, `memory`) to collapse it. While counters mode can be useful, the [Developer Console](/docs/en-us/studio/optimization/memory-usage.md) is the best way to [identify memory issues](/docs/en-us/performance-optimization/identify.md#client-memory). You might also find the [X-ray view](/docs/en-us/performance-optimization/microprofiler.md#web-only-features) helpful for identifying when problematic memory allocation occurs. ## Groups mode Only available in the web UI. Similar to timers mode, groups mode provides average, max, and total task times for each group, broken down by thread, to help you identify how broad categories of tasks are impacting your frame times. ![MicroProfiler web UI showing groups mode.](../../assets/optimization/microprofiler/micro-groups-mode.png) ## Threads mode Only available in the web UI. Similar to timers mode, threads mode provides average and total task times for each thread, broken down by group, to help you identify how your game is distributing tasks across threads. ![MicroProfiler web UI showing threads mode.](../../assets/optimization/microprofiler/micro-threads-mode.png) ## Hidden mode Only available in the desktop UI. Hidden mode keeps the MicroProfiler menu open, but hides the bar graph. It's useful for reducing visual clutter, [saving frame data](/docs/en-us/performance-optimization/microprofiler.md#save-frame-data), and pausing and unpausing while you observe the line graph. --- title: "Network usage" url: /docs/en-us/performance-optimization/microprofiler/network last_updated: 2026-06-29T19:34:03Z description: "The MicroProfiler provides detailed information on network usage within your game." --- # Network usage The MicroProfiler also lets you capture and view network traffic, which can help you identify causes of high latency and loading problems in your games. Network data is available **only in frame dumps from the desktop client or Studio**, not in the real-time overlay. If you encounter lag or other issues that you suspect are network-related, that's a great time to [save MicroProfiler data](/docs/en-us/performance-optimization/microprofiler.md#save-frame-data). ## Capture network data 1. Open the MicroProfiler with `Ctrl``Alt``F6` or `⌘``⌥``F6`. 2. In the **Network** menu, choose **High**, **Low**, or **Off**. - **High** includes additional information on the items within a batch data packet, the task that deserialized the packet, and the asset ID for any assets in the request or response. - **Low** captures less data and is not as computationally intensive. Depending on your computer, selecting it can help your dumps contain a larger number of frames. This is the default level and the one the MicroProfiler uses when you [profile a mobile device](/docs/en-us/performance-optimization/microprofiler.md#profile-mobile-devices). - **Off** means that network data is not included in frame dumps. 3. [Save MicroProfiler frame data](/docs/en-us/performance-optimization/microprofiler.md#save-frame-data) and open the generated HTML file. ## Basic navigation After you open the MicroProfiler dump in a web browser, select **X-Ray** / **Network**. Two more rows appear below the frame time graph: - The top row is received traffic (downloads). - The bottom row is sent traffic (uploads). ![Overview of the network graphs.](../../assets/optimization/microprofiler/micro-network.png) Because network traffic can vary dramatically between frames, you might see a couple spikes and seemingly very little else. In this case, select **Options** / **Log Scale Graph** to change the scaling of the bars such that they scale logarithmically rather than linearly, which makes small bars more prominent. The stacked bars are colored not by severity, but by type of traffic: physics in blue, data in green, and assets in red. Hover over any bar to get the amount of received or sent traffic for that frame. Right-click to show the **Network events** window, which shows much more detailed information. ## Network events window The **Network events** window shows individual events within the scope of the frame. Click on any event for more detailed information. ![Overview of the network graphs.](../../assets/optimization/microprofiler/micro-network-events.png) Available information depends on the type of packet and verbosity level: - **Engine network batch data** includes traffic due to streaming, instance property updates, and events. If you chose high verbosity, the batch data expands to show individual items. At low verbosity, you only get the summary. - At high verbosity, click an Engine network packet to zoom in on the task that deserialized the packet. - Regardless of verbosity level, click any other Engine network event to show information such as size, direction, and type. - Compared to Engine network events, **HTTP asset delivery** shows some additional information, including batch size (if the request was batched) and the number of queued HTTP requests (if applicable). If you chose high verbosity, it also includes asset IDs for any assets in the request. - Click the asset ID to zoom in on the task where the next step in fetching that asset took place. - Click the **+** to view the asset on the Creator Store. ![Overview of the network graphs.](../../assets/optimization/microprofiler/micro-network-asset.png) ## Additional graphs Depending on the data included in the frame dump, you can view additional graphs below the standard download and upload graphs: - Asset fetching - Data - Large replicator data (async and chunked data) - Physics - Terrain Click any category in the legend to add these graphs, which provide dedicated views into each packet type. Each graph defaults to showing received traffic (Rx), but you can use the label in the upper-left of any of the three graphs to swap to sent traffic (Tx). ![Overview of the network graphs.](../../assets/optimization/microprofiler/micro-network-extra.png) --- title: "Tag reference" url: /docs/en-us/performance-optimization/microprofiler/tag-table last_updated: 2026-06-29T19:34:03Z description: "A list of tags for the MicroProfiler." --- # Tag reference The following is a list of common tags in the [MicroProfiler](/docs/en-us/performance-optimization/microprofiler.md), grouped by category. Tags are also interchangeably called tasks, timers, scopes, processes, and labels. Whatever the name, they represent a unit of work. Understanding these tags can help you identify problematic code in your game. The tables contain tag label, descriptions, and advice for improving performance and optimizing your game. ## Sleep When threads aren't actively performing tasks, they enter a sleep state, with tags to indicate how long the thread was sleeping. At any given time, it's extremely common for most worker threads to be in a sleep state. ## Navigation | Label | Description | Performance advice | | --- | --- | --- | | rasterizeTile | Updates navigation tiles needed for a pathfinding request, usually followed by **computePath** which requires those tiles be up-to-date. Follows **NavigationJob/preprocess** on the main thread. | Reduce the number of pathfinding tile invalidations, as this causes those paths to need recomputing. This is caused by non-navigable parts moving. | | computePath | Calculates paths, typically after **rasterizeTile**. | Reduce the number and world extents of `Class.Path:ComputeAsync()` calls. Try reusing paths for multiple agents if they are expected to start/end from approximately similar locations. | | preprocess/getPrimitivesOverlapping | Collects the world geometry for each pathfinding tile and schedules the rasterization tasks to be executed by the pathfinding thread. | Reduce the part count. | ## Animation and humanoids | Label | Description | Performance advice | | --- | --- | --- | | Simulation/gameStepped | Processing of game-specific objects such as `Class.Humanoid\|Humanoids`, `Class.Animation\|Animations` and heartbeat callbacks. | See **gameStepped** labels below. | | Simulation/gameStepped/stepHumanoid | `Class.Humanoid` state changes and movement. | Reduce the amount of `Class.Humanoid\|Humanoids` or disable `Class.Humanoid` states on NPCs that don't need them, such as `Enum.HumanoidStateType\|Climbing` or `Enum.HumanoidStateType\|Swimming`. Reduce callbacks to `Class.Humanoid.StateChanged` or state changes such as `Class.Humanoid.Running\|Running` or `Class.Humanoid.Died\|Died`. | | Simulation/gameStepped/stepAnimation | `Class.Animator\|Animators` will step forward in currently playing animations. | Reduce the amount of `Class.Animator\|Animators` or animated joints to lower the workload of this step. Reduce number of callbacks to animation events such as `Class.AnimationTrack.KeyframeReached` or `Class.AnimationTrack.Ended` | | Simulation/gameStepped/RunService.Stepped | Runs functions connected to the `Class.RunService.Stepped` event. | Reduce the amount or workload of functions connected to this event. Consider delaying or replacing expensive calculations or spreading computation across multiple frames. | ## Audio | Label | Description | Performance advice | | --- | --- | --- | | Sound | Processes acoustic simulation and updates sounds in active playback. | See **Sound** labels below. | | Sound/stepInstances | Updates the volume of active sounds in the workspace. | Reduce the amount of sounds in active playback. | ## Networking | Label | Description | Performance advice | | --- | --- | --- | | Net PacketReceive | Receives network packets. If many objects or events are being replicated, this step takes longer. | Replicate fewer objects or events. | | Replicator/ProcessPackets | Processes contents of network packets, such as motion, event invocations and property changes. | Reduce the number or size of objects being replicated, or do this in incremental steps. May increase if map size increases, as larger maps tend to have more overall activity. | | Allocate Bandwidth and Run Senders/Dispatch Physics Senders and TouchSenders | Sends data about activity in the game. | Reduce the amount of moving objects and/or touches. See following sections. | | Allocate Bandwidth and Run Senders/Dispatch StreamJob | Sends corresponding specific regions to specific clients in the streaming feature. | Reduce the minimum and target streaming radii. | | Allocate Bandwidth and Run Senders/Dispatch Data Senders | Sends property changes, remote event invocations, `Class.Humanoid` state changes, animation state changes, and replication of new instances. | Reduce the number of replicated changes to the data model. | | Replicator SendCluster | Sends [Terrain](/docs/en-us/parts/terrain.md) data to clients. | Reduce the amount or size of terrain changes. | | ModelCompleteness Mega Job | Server only: completeness is an internal concept. When models are completely sent, model completeness events are sent. | Add or remove fewer instances. | | deserializePacket | Low-level network packet processing. Prepares for **Replicator/ProcessPackets**. | Send fewer or smaller updates. | ## Rendering | Label | Description | Performance advice | | --- | --- | --- | | Prepare | Information is gathered from the main thread and updates various data structures used for rendering. | See **Prepare** labels below. | | Prepare/Pass3dAdorn | Handles rendering of various object adorns, such as text labels above objects. For `Class.Humanoid` labels with occlusion, this step includes raycasting to determine if such objects are visible. This includes non-transparent parts to facilitate debug visualizations. | Reduce the number of visible adorned objects, such as `Class.BillboardGui\|BillboardGuis`, `Class.Humanoid` name/health labels, etc. Reduce the number of visible parts. | | Prepare/Pass2d | Readies 2D UI rendering (both player and Roblox UI). | Reduce the amount or complexity of UI elements. | | Prepare/UpdatePrepare/updateInvalidParts | Updates parts that had some property changed or added. | Reduce the amount of properties changes on the world. If a script updates a large set of object properties, break it down across frames. | | Prepare/UpdatePrepare/updateInvalidatedFastClusters | Prepares "FastCluster" geometries used to render `Class.Humanoid\|Humanoids` and [skinned](/docs/en-us/art/modeling/rigging.md) `Class.MeshPart\|MeshParts`. Labels specify the number of parts, vertices, and size of vertices. | Reduce visual changes to models with `Class.Humanoid\|Humanoids` or skinned `Class.MeshPart\|MeshParts`. | | Prepare/UpdatePrepare/updateDynamicParts | Prepares `Class.Beam\|Beams`, `Class.ParticleEmitter\|ParticleEmitters`, and `Class.Humanoid\|Humanoids` for rendering. | Reduce the number of visible `Class.Beam\|Beams`, `Class.ParticleEmitter\|ParticleEmitters`, and `Class.Humanoid\|Humanoids`. | | Prepare/UpdatePrepare/updateInstancedClusters | Updates geometry that uses instanced rendering such as parts. Labels **Clusters** and **Instances** indicate the number updated. | Reduce work that implicitly updates the bounding box of the part, such as `Class.BasePart.CFrame`, `Class.BasePart.Size`, or `Class.Motor6D.Transform`. Creative property updates such as `Class.Bone.Transform` can help. | | Perform | When actual rendering commands are created and issued. | See **Perform** labels below. | | Perform/fillGuiVertices | Fills buffers with UI vertices to prepare for rendering. **gui count** label indicates the amount of `Class.LayerCollector\|LayerCollectors` visible in the frame. | If the cost is high, reduce the amount, density, and space taken by UI elements. If there are too many **Process GuiEffect** labels, consider reducing the use of `Class.UIGradient` and `Class.UICorner` on text labels. | | Perform/Scene/queryFrustumOrdered | Applies frustum culling so that objects not visible are not rendered. | If there is a high cost that means there are a lot of elements. Perhaps use some larger meshes where a single mesh has more details as opposed to many small individual pieces. | | Perform/Scene/computeLightingPerform | Computation of lighting near the camera. | Manipulate the number of light sources or move the camera less to reduce the time it takes to recalculate lighting. | | Perform/Scene/computeLightingPerform/LightGridCPU | Updates the voxel lighting, which is used at lower quality levels. | If updating chunk occupancy takes too long, consider using lower resolution geometry, reducing the number of parts, or anchoring parts. If the other labels take too long, consider reducing the number of lights and using non-shadow casting geometry for objects that move and invalidate the occupancy. | | Perform/Scene/computeLightingPerform/ShadowMapSystem | Updates shadow maps. Not performed at quality levels below 4. | Reduce the number of lights. You can also use `Class.Light.Shadows` and `Class.BasePart.CastShadow` to disable shadow casting on less important instances. See [Improve performance](/docs/en-us/performance-optimization/improve.md#mitigation-4). | | | | Perform/Scene/Glow, ColorCorrection, MSAA, SSAO, and SSAOApply | Post-processing rendering. | Reduce the number of post-processing effects. Usually this is not significant. | | Perform/Scene/UI | UI rendering. In **Id_Screen**, there is a label with the number of batches, materials and triangles used. | Reduce the number of visible UI elements. Using `Class.CanvasGroup\|CanvasGroups` can help at the expense of increased memory use. | | Perform/Scene/UpdateView/updateParticles, updateParticleBoundings | Update particle position and bounds. | Reduce the number of `Class.ParticleEmitter\|ParticleEmitters`, emission rates, lifetimes, etc. Limit the movement of emitters. | | Scene/Id_Opaque, RenderView/Id_Opaque | Parts in the scene with an overall transparency of `0.01` or lower. | Reduce the use and density of parts. | | Scene/Id_Transparent, RenderView/Id_Transparent | Parts in the scene with an overall transparency between `0.01` and `1`. | Reduce the use of partial transparency. | | Scene/Id_Decals, RenderView/Id_Decals | Decals in the scene. | Reduce the use of decals on complex meshes. | | Scene/Shadows | Shadow recalculation in the scene, usually performed on dynamic scenes and typical gameplay. Parts regardless of transparency cast shadows under the assumption that they contain decals. Not performed at quality levels below 4. | If this step takes too long, consider disabling `Class.BasePart.CastShadow` for complex meshes, parts with high partial transparency, and less important instances. Fully transparent parts that don't have decals or need decal shadows should have `Class.BasePart.CastShadow` disabled. See [Improve performance](/docs/en-us/performance-optimization/improve.md#mitigation-4). | | Perform/Present | Forwards to the GPU thread to perform rendering commands. | Reduce scene complexity in general. If this step is taking a long time, you might be limited by the GPU. | | Perform/Present/waitUntilCompleted | Waits for the GPU to finish rendering the previous frame. | If this is generally happening a lot then the amount of things rendered is too high. The Frame Rate Manager helps with balancing this, but if it remains high, try following performance advice from the individual **Scene** tags. | | LoadImage | Processes images into a format the engine can use. | Reduce the use of large images. | ## Scripts | Label | Description | Performance advice | | --- | --- | --- | | Render/PreRender/fireBindToRenderSteppedCallbacks | Running functions bound to render step via `Class.RunService:BindToRenderStep()`. | Ensure functions connected to this event do as little work as possible. | | Render/PreRender/RunService.RenderStepped | Runs functions connected to the `Class.RunService.RenderStepped` event. | Ensure functions connected to this event do as little work as possible. | | WaitingHybridScriptJob | Resumes scripts waiting using `Class.Instance:WaitForChild()` or `Globals.Roblox.wait()`. Usually performed 30 times per second. This step has an execution time budget to run waiting scripts. | If you have too many waiting scripts or scripts with a long runtime before yielding, this step is throttled and waits longer before it can run again. Reduce the amount of bound functions or long computations in this step. | | GC | Luau's garbage collection cycle. | Pool tables and other collectable objects or try to reduce creating temporary tables. | | Heartbeat/RunService.Heartbeat | Runs functions connected to the `Class.RunService.Heartbeat` event. | Reduce the amount or workload of functions connected to this event. Consider delaying or replacing expensive calculations or spreading computation across multiple frames. | ## Simulation | Label | Description | Performance advice | | --- | --- | --- | | Distributed Physics Ownership | Determines whether the server or a client has authority over certain instances such as parts. | Reduce the amount of parts that frequently switch network ownership, especially those with common interaction. | | Simulation/assemble | Updates a tree of connected objects (assemblies) used by the physics engine. | Reduce the amount of joints being created or destroyed. | | Simulation/physicsSteppedTotal/physicsStepped | Runs the physics simulation. | Reduce the amount and complexity of physically simulated bodies. | | Simulation/physicsSteppedTotal/physicsStepped/SpatialFilter/filterStep | Updates simulation islands, arranging parts according to network ownership, local simulation. Islands are non-interacting groups of parts which can be simulated independently. | Avoid setting network ownership frequently. Keep groups of parts far enough away from each other so they can be simulated separately. | | Simulation/physicsSteppedTotal/physicsStepped/worldStep/stepContacts | Updates contacts between objects. | Reduce the amount of bodies colliding at once, or use simpler collision boxes. Cubes are better than complex meshes. | | Simulation/physicsSteppedTotal/physicsStepped/worldStep/stepWorld **OR** stepWorldThrottled | Solves physics equations relating to connectors, buoyancy and `Class.Humanoid\|Humanoids`. When the engine is overloaded and unable to simulate everything in real time, some steps may be throttled (**stepWorldThrottled**) and only "real-time assemblies" such as `Class.Humanoid\|Humanoids` are simulated. | Depends on where the time is going based on the following three phases: **stepContacts**: narrow phase collision detection geometry tests. **Solver step**: integrate time and resolve collisions and other constraints **updateBroadphase**: update positions of assemblies in collision detection system and find possibly colliding narrow phase pairs. | | notifyMovingAssemblies | Helps track how long primitives have been sleeping. | | | Simulation/physicsSteppedTotal/physicsStepped/interpolateNetworkedAssemblies | Interpolates assemblies not controlled by the current player. | Set the network owner of parts to the current player to reduce this; although this will usually cause more physics work to be done elsewhere. | | Simulation/handleFallenParts | Removes parts that have fallen below `Class.Workspace.FallenPartsDestroyHeight`. | Lower the destroy height or reduce the amount of parts that fall to the destroy height. | | Heartbeat/heartbeatInternal/workspaceOnHeartbeat/updateVisuallySleeping | Second part of **notifyMovingAssemblies**. | | | Heartbeat/RunService.Heartbeat | Runs functions connected to the `Class.RunService.Heartbeat` event to simulation and scripts contacts. Current description is generic enough to be not wrong. | Reduce the amount or workload of functions connected to `Class.RunService.Heartbeat`. | | worldStep/stepContacts | Helps physics simulation step many contacts at once. | Reduce the number of colliding objects. | | SolveBatch | Helps the physics simulation solve batches of objects' motion. | | ## UI | Label | Description | Performance advice | | --- | --- | --- | | Render/PreRender/UpdateInput | Updates and fires all user input related events if the user has performed input since the last frame. | Try and not do too much work directly as you get the input. Consider doing a minimal amount of processing for the input, and larger computations should be pushed off to another thread that happens later. | | Render/PreRender/TweenService | Updates objects being tweened using `Class.TweenService` and calls completion callbacks, such as those used provided to `Class.GuiObject:TweenSize()` or `Class.GuiObject:TweenPosition()`. | Reduce the number of objects being tweened using `Class.TweenService` and ensure callbacks do as little work as possible. | | Heartbeat/TweenService | In the server, `Class.TweenService` runs in `Class.RunService.Heartbeat` instead of `Class.RunService.PreRender`. | Reduce the number of objects being tweened using `Class.TweenService` and ensure callbacks do as little work as possible. | | Render/PreRender/UpdateUILayouts | Updates position and size for UI elements on all enabled `Class.LayerCollector\|LayerCollectors`. | See **UpdateUILayouts** labels below. | | Render/PreRender/UpdateUILayouts/Rebuild Z-order list | Sorts the Z-order of UI elements (internal term not to be confused with `Class.GuiObject.ZIndex`) to prevent tearing of UI elements. | Reduce the amount of UI elements with the same `Class.GuiObject.ZIndex`. | | Render/PreRender/UpdateUILayouts/Layout | Updates position and size for UI elements in an individual `Class.LayerCollector`. Can contain a label with information about the relevant UI, along with the amount of Relayouts, Updates and Resizes. | Reduce the amount of UI elements being resized or repositioned, such as those managed by `Class.UILayout` and those tweened with `Class.TweenService`, `Class.GuiObject:TweenSize()`, or `Class.GuiObject:TweenPosition()`. Consider using fixed sizes for `Class.BillboardGui\|BillboardGuis`. | --- title: "Task scheduler" url: /docs/en-us/performance-optimization/microprofiler/task-scheduler last_updated: 2026-06-29T19:34:03Z description: "Task Scheduler coordinates tasks done in each frame as the game runs." --- # Task scheduler The **task scheduler** coordinates tasks done each frame as the game runs, even when it is paused. These tasks include detecting player input, animating characters, updating the physics simulation, and resuming scripts in a `Library.task.wait()` state. While there may be multiple tasks running, the task scheduler can potentially be overloaded, especially in the following situations: - Using a custom character rig or input scheme. - Animating parts yourself (instead of using an `Class.Animator`). - Depending heavily on precise physics. - Replicating objects regularly. ## RunService The most direct way to add frame-by-frame tasks is through the following members of `Class.RunService`: - `Class.RunService.PreAnimation` - `Class.RunService.PreSimulation` - `Class.RunService.PostSimulation` - `Class.RunService.Heartbeat` - `Class.RunService:BindToRenderStep()` - `Class.RunService.PreRender` ## Scheduler priority The task scheduler categorizes and completes tasks in the following order. Some tasks may not perform work in a frame, while others may run multiple times. * Script execution during `Class.RunService.Heartbeat` differs depending on your game's `Class.Workspace.SignalBehavior` setting. See [Deferred events](/docs/en-us/scripting/events/deferred.md). ## Best practices To build performant games with efficiency in mind, note the following: - **Don't connect/bind functions to the render step unless absolutely necessary.** Only tasks that must be done after input but before rendering should be done in such a way, like camera movement. For strict control over order, use `Class.RunService:BindToRenderStep()|BindToRenderStep()` instead of `Class.RunService.PreRender|PreRender`. - **Manage physical states carefully.** `Class.RunService.PreSimulation|PreSimulation` happens **before** physics, while `Class.RunService.PostSimulation|PostSimulation` happens **after** physics. Therefore, gameplay logic that affects the physics state should be done in `Class.RunService.PreSimulation|PreSimulation`, such as setting the `Class.BasePart.Velocity|Velocity` of parts. In contrast, gameplay logic that relies on or reacts to the physics state should be handled in `Class.RunService.PostSimulation|PostSimulation`, such as reading the `Class.BasePart.Position|Position` of parts to detect when they enter defined zones. - **Motor6D transform changes should be done on the PreSimulation event.** If you don't, `Class.Animator|Animators` will overwrite changes on the next frame. Even without an `Class.Animator`, `Class.RunService.PreSimulation|PreSimulation` is the last Luau event fired before `Class.Motor6D.Transform` is applied to part positions. --- title: "MicroProfiler walkthrough" url: /docs/en-us/performance-optimization/microprofiler/use-microprofiler last_updated: 2026-06-29T19:34:03Z description: "Explains how using the MicroProfiler can help you optimize portions of your game." --- # MicroProfiler walkthrough This walkthrough shows how to use the MicroProfiler to find a problematic aspect of a game and identify the root cause. Download the game, **Open from File** in Studio, and follow along. ## Identify the issue 1. After you open the game in Studio, start testing it with `F5` or the **Play** button. 2. The frame rate feels decent, but not as smooth as it should be for a game of this size and scope. Press `Ctrl``Shift``F5` (`⌘``Shift``F5`) to show the **Performance Summary** overlay.![Performance summary showing 45 FPS.](../../assets/optimization/microprofiler/micro-tut-framerate.png) Note that the frame rate is below 60 frames per second (FPS). 3. Open the MicroProfiler by pressing `Ctrl``Alt``F6` (`⌘``⌥``F6`).![The MicroProfiler after opening it.](../../assets/optimization/microprofiler/micro-tut-bar-graph.png) Note that the frame times are **consistent**—the bars are roughly the same height—so whatever is causing the low frame rate is running every single frame as opposed to running occasionally and causing frame time spikes. 4. Pause the MicroProfiler by pressing `Ctrl``P` (`⌘``P`). Pausing with the keyboard shortcut opens [detailed mode](/docs/en-us/performance-optimization/microprofiler/modes.md#detailed-mode). 5. Click and drag to pan the graph. Note how one particular task in the worker threads seems to be using a lot of processing time compared to the main thread. Hover over `Class.RunService.Stepped` and note how long it's taking to render.![Detailed mode with long labels for processes.](../../assets/optimization/microprofiler/micro-tut-stepped.png) 6. Stacked bars in the timeline indicate a hierarchy of code, so keep hovering on lower and lower layers to try and identify the issue.![On-hover details for various processes, with LocalScript highlighted.](../../assets/optimization/microprofiler/micro-tut-localscript.png)![On-hover details for various processes, with Raycast highlighted.](../../assets/optimization/microprofiler/micro-tut-raycast-label.png) Note the `LocalScript` label, which indicates the name of the script, and the `Raycast` label, which indicates that the problem might be related to raycasting. ## Create troubleshooting labels Now that the MicroProfiler has provided a starting point, you can troubleshoot the problematic code. 1. Stop the play test and filter the Explorer window for `localscript` to find the file.![A view of filtering in the Explorer window.](../../assets/optimization/microprofiler/micro-tut-explorer-filter.png) A search for `raycast` indicates that this portion of code is probably the culprit for the game's poor performance:```lua local RAYS_PER_SECOND = 1500 local function onStepped() for _ = 1, RAYS_PER_SECOND do local startPosition = getRandomPosition() local endPosition = getRandomPosition() local direction = endPosition - startPosition Workspace:Raycast( startPosition, endPosition ) end end RunService.Stepped:Connect(onStepped) ``` Specifically, you can see that the code is casting 1,500 rays per second in random directions. 2. To verify that this portion of code is causing the rendering delay, wrap the contents of the function with `Library.debug.profilebegin()` and `Library.debug.profileend()`:```lua local function onStepped() debug.profilebegin("Raycast Spam") for _ = 1, RAYS_PER_SECOND do local startPosition = getRandomPosition() local endPosition = getRandomPosition() local direction = endPosition - startPosition Workspace:Raycast( startPosition, endPosition ) end debug.profileend() end ``` ## Confirm and fix the issue 1. Start testing the game, and open the MicroProfiler again. 2. Note how the MicroProfiler now shows the custom label, indicating that this function was indeed the root cause.![MicroProfiler detailed view with 'Raycast Spam' visible.](../../assets/optimization/microprofiler/micro-tut-spam.png) 3. Stop the play test. 4. Delete or comment out the `onStepped()` function and the `RunService.Stepped:Connect(onStepped)` connection. 5. Start testing the game again, and check the **Performance Summary** again.![Performance summary showing 60 FPS.](../../assets/optimization/microprofiler/micro-tut-framerate2.png) Note the huge improvement to frame rate and corresponding drop in frame times on the MicroProfiler graph. --- title: "Monitor performance" url: /docs/en-us/performance-optimization/monitor last_updated: 2026-06-29T19:34:03Z description: "Explains how to use the Performance Dashboard to monitor your games." --- # Monitor performance The [Performance Dashboard](/docs/en-us/production/analytics/performance.md) is the best way to monitor the performance of your published games. The two tabs include real-time performance information for both clients and servers, and you can use date ranges to track the impact of your changes over time. For example, imagine you've just added a new place to your game: 1. You publish the updated game, and hours later, you notice a distinct uptick in client crash rate on the Performance Dashboard that seems to correlate with client memory usage. 2. You test the new place in Studio and don't see any issues. Memory usage seems similar to other places in your game. 3. You note that average player session time has increased dramatically since your update. 4. You test again, but this time, you spend more time in the game and keep a closer eye on memory usage over time. Sure enough, usage is growing over time and could cause crashes on lower-end devices. 5. You check the server tab and notice that server memory usage is growing over time, too. It's not a problem yet, but could become a problem soon. 6. Under the **Memory** tab of the Developer Console, in the **PlaceScriptMemory** breakdown, you notice that the memory usage for one of your new scripts grows each time you interact with some key objects in the world. The script runs on both the client and the server. 7. You check the script, which adds some user data to a large table, but never cleans the data up—it's growing infinitely. 8. You fix the problem, publish the updated game, and client crash rate returns to its previous value. Comparing the Performance Dashboard to other dashboards can help you correlate performance improvements (or regressions) to business metrics for a more complete idea of how a performant game impacts your bottom line. For more information, see [Analytics](/docs/en-us/production/analytics.md). --- title: "Scene Analysis" url: /docs/en-us/performance-optimization/scene-analysis last_updated: 2026-06-29T19:34:03Z description: "Use the Scene Analysis tool to identify and resolve performance issues in your experience. Check script memory, unparented instances, audio memory, animation memory, the instance composition view, and the extended scene triangle breakdown." --- # Scene Analysis The Scene Analysis tool provides a suite of views in Roblox Studio that give you at-a-glance comparative information about how your client and server scenes are put together, what resources they're using, and which instances are responsible for that resource usage. Scene Analysis is designed for use during play and test sessions, and can inspect both the client and server data models. ![Scene Analysis tool overview](../assets/optimization/scene-analysis/overview.png) ## Enable Scene Analysis 1. Enable **Scene Analysis** in **File** > **Beta Features**. 2. In the menu bar, select **Window** > **Performance Summary** > **Scene Analysis**. ## Use Scene Analysis Scene Analysis is designed to be used during a play or test session. Open the Scene Analysis window and browse the available modes. When a mode is active, you see a display of boxes—the squarified treemap view—showing the relative sizes of the resources being viewed and the instances that are contributing to them. - Below the squarified treemap view is a list of all the items in the current view. - Right-clicking a selected item in Scene Analysis usually lets you jump directly to that item in the Explorer. - You can use the search bar to filter by name in the current display. ## Modes Scene Analysis has many different modes, each of which caters to a different use case. ### Script memory This view shows how much memory each `Class.Script`, `Class.LocalScript`, and `Class.ModuleScript` is using in the Luau VM. This is the memory used by tables and other script allocations, not the memory used by assets such as animations, textures, and sounds. The view is further broken down by the common locations from which scripts are executed and by `Class.Script` versus `Class.ModuleScript`. ### Unparented instances The unparented instances view shows which scripts and modules are holding references to instances, as a count per type. Scripts holding references to instances is normal and is not, on its own, an indication of a bug or memory leak. However, if you're investigating a memory leak, this view makes it easy to spot whether a script is doing something you didn't expect. If you're still having trouble finding the root cause of a memory leak, this tool is complementary to the [Luau heap](/docs/en-us/studio/optimization/memory-usage.md#luau-heap) tool, which can show you more information about individual allocations. Right-click any script in this view to select it directly in the Explorer. ### Instance composition The instance composition view shows which instances are currently present on the client or server. This is invaluable when working on large and complex builds, as each instance has both replication and memory costs. The categories, such as Characters, Geometry, and Textures, are designed to help you understand how your scene is balanced. For example, perhaps you didn't mean to have 25,000 particle emitters, but this view lets you know. This view also makes it easy to select all instances of a given type. ### Audio memory The audio memory view shows how much memory each audio asset is currently consuming. The actual memory used by a given audio track is quite complex, but in practical terms, deleting all instances that refer to an audio track allows that memory to unload. Right-click any audio asset in this view to select all the instances that are using it. ### Animation memory Animation assets are one of the most common sources of memory leaks in production. Roblox is aggressive about unloading unused animation assets, but it's common for scripts to keep animations alive after their parent model has been destroyed. As with audio assets, deleting all instances that refer to an animation asset allows it to unload. This view, along with the unparented instances view, makes it easy to track the lifecycle of animations and how much memory they're actually using. Right-click any animation in this view to select all the instances that are using it. ### Triangle composition The triangle composition view gives you a more detailed breakdown of the triangles and draw calls currently being rendered in the main viewport. These breakdowns include counts for Shadows, Opaque, Transparent, Terrain, Grass, Particles, Sky, and UI. This compositional information helps you better understand how your scene geometry is composed and identify cases where the scene contains unexpected geometry. > **Warning:** The statistics in this view don't necessarily match what players see on their devices. Scaling can cause dramatic differences between a powerful development PC and, for example, a mid-range phone. ## Engine API You can also access Scene Analysis data programmatically through `Class.SceneAnalysisService`, which is exposed through the [MCP server](/docs/en-us/studio/mcp.md). Each method returns tables containing nested children with the same data the visual display uses. | Method | Description | | --- | --- | | `GetInstanceCompositionAsync` | Returns instance counts by category and class from the `Class.DataModel` tree. | | `GetScriptMemoryAsync` | Returns per-script Luau VM heap memory usage in bytes. | | `GetUnparentedInstancesAsync` | Returns instances held by Luau but no longer in the `Class.DataModel`, grouped by host script. | | `GetTriangleCompositionAsync` | Returns triangle and draw call counts by render pass. | | `GetAnimationMemoryAsync` | Returns loaded animation clip memory, deduplicated by clip. | | `GetAudioMemoryAsync` | Returns loaded audio asset memory, deduplicated by asset ID. | --- title: "Test on hardware" url: /docs/en-us/performance-optimization/test-on-hardware last_updated: 2026-06-29T19:34:03Z description: "Explains why and how to test your experience on real devices to catch performance and usability issues before your players do." --- # Test on hardware Roblox Studio provides useful emulation tools for [testing different devices](/docs/en-us/studio/testing-modes.md), but emulation can't fully replicate what happens on real hardware. Frame rates, memory pressure, thermal throttling, and input latency all behave differently on physical devices. Testing on hardware helps you catch issues that only surface under real-world constraints. ## Why test on devices Studio runs the client and server together on your development machine, which is typically far more powerful than the devices your players use. This creates blind spots with the devices your player base typically uses: - **Memory** — The [device emulator](/docs/en-us/studio/testing-modes.md) is useful for checking aspect ratios and controls, but isn't accurate for memory usage. On a real device, the operating system, background apps, and the Roblox engine itself all compete for limited RAM. - **Frame rate** — A high-end PC can process inefficient code or other issues that cause severe frame drops on mobile or low-end hardware. Problems are easier to notice (and reproduce) on constrained devices. - **Thermal throttling** — Phones, tablets, and even thin laptops reduce CPU and GPU clock speeds if they overheat during sustained gameplay. - **Input and latency** — Touch responsiveness, gamepad polling rates, and network behavior over cellular connections can only be validated on real hardware. ## Popular devices in the player base The majority of Roblox players are on lower-spec mobile devices, with some exceptions for specific genres or competitive games. Understanding the device landscape helps you prioritize what to test, and you can use the [Performance Dashboard](/docs/en-us/production/analytics/performance.md) to get more accurate metrics of your player base. Roblox is inherently cross-play, which means that it supports a vast range of devices, and device demographics differ between countries, age groups, genres, and more. To reach the widest audience, your experience needs to perform well across the full range of devices. The following are general device statistics across the user base: - **Android** represents roughly 65% of a typical experience's player base. - ~60% of those players have **2–4 GB** of RAM. - ~35% have **4–8 GB** of RAM. - ~5% have more than 8 GB of RAM. - Over 50% of the Roblox player base plays on devices scoring between **10,000–20,000** on [Passmark](https://www.passmark.com/) benchmarks. If you're only testing on your development machine, you're testing for the minority of your audience. Prioritize [cross-platform compatibility](/docs/en-us/projects/cross-platform.md) and pick at least one low-end baseline device that represents your target player demographic. > **Success:** For a broader set of Android test devices that covers more of the player base, choose devices at a variety of performance levels from several manufacturers. For example, you might test on an Infinix Smart 9, a Motorola Moto G05, an Oppo A18, an Amazon Fire HD 10 (2023), and a Samsung Galaxy S22 Ultra. ## Testing strategies Testing strategies depend on your experience and your development workflow — there's no single approach that fits every project. The following provide a solid baseline for integrating hardware testing into your process. Multi-input devices can increase your testing coverage without requiring a full device lab. For example, a handheld gamepad with a touchscreen lets you validate both touch and gamepad inputs on a single piece of hardware. Phones with USB-C gamepad adapters or touchscreen-equipped laptops can serve the same purpose. ### Performance Performance testing on hardware focuses on three areas: frame rate (compute), memory, and load time. For a full breakdown of diagnostic tools, see [Identify performance issues](/docs/en-us/performance-optimization/identify.md). For guidance on picking a baseline and staying within its limits, see [Design for performance](/docs/en-us/performance-optimization/design.md#low-end-devices). #### Profile on device The [MicroProfiler](/docs/en-us/performance-optimization/microprofiler.md) and [Developer Console](/docs/en-us/studio/developer-console.md) both run on client devices, not just in Studio. Use them directly on hardware to get accurate frame time breakdowns and memory readings. - Open the Developer Console with `F9` (or the in-experience menu) to check memory consumption under the **Memory** tab. - Use the MicroProfiler (`Ctrl``Alt``F6` / `⌘``⌥``F6`) to capture frame dumps on device, then analyze them on your development machine. - Enable **Performance Stats** from the settings menu to see an overlay of FPS, memory, and ping. #### Test for thermal throttling Run your experience on a mobile device for an extended session (10–15 minutes of active gameplay). Watch for frame rate degradation over time — a steady decline often indicates the device is thermal throttling. If you see this, focus on reducing sustained GPU or CPU load in hot code paths identified by the MicroProfiler. #### Validate network conditions Players on mobile networks experience higher latency, jitter, and packet loss than wired connections. Use [network simulation](/docs/en-us/studio/testing-modes.md#network-simulation) in Studio for quick iteration, but confirm behavior on a real device over cellular or constrained Wi-Fi. #### Monitor after publish Once your experience is live, use the [Performance Dashboard](/docs/en-us/performance-optimization/monitor.md) to track client frame rate, memory usage, and crash rates across device categories over time. A sudden uptick in crashes after an update often points to a memory regression on low-end devices. ### User experience Performance numbers don't tell the full story. An experience can hit 60 FPS on a device and still feel broken if touch targets are too small or UI is unreadable. For comprehensive cross-platform UI guidance, see [Cross-platform development](/docs/en-us/projects/cross-platform.md). > **Info:** Windows handhelds like the ROG Xbox Ally offer a lot of utility in user experience testing. In addition to built-in controllers, these devices generally have touchscreens and support input from a mouse and keyboard (most useful when connected to an external display). Windows handhelds tend to have dramatically faster hardware than a midrange phone, though, so they're less valuable for performance testing. #### Touch targets and gestures Test on actual phone screens, not just emulated resolutions. Buttons that seem comfortably sized on a desktop monitor can be frustratingly small on a 5-inch display. Confirm that: - Interactive elements are large enough to tap reliably on varying screen sizes. - Gestures like pinch-to-zoom and swipe don't conflict with your gameplay controls. - On-screen thumbsticks and action buttons are positioned within comfortable reach zones. #### Screen readability UI elements that look crisp at 1440p can become illegible on a low-resolution mobile screen. Test text size, icon clarity, and contrast on your lowest-supported device at arm's-length viewing distance. For guidance on adaptive layouts, see [Adaptive design](/docs/en-us/production/publishing/adaptive-design.md). #### Input switching Players frequently switch input methods mid-session — a tablet player might connect a Bluetooth controller, or a laptop player might switch from trackpad to mouse. Confirm that your experience responds correctly to [input type changes](/docs/en-us/projects/cross-platform.md#input-type-detection) and updates any [assistive hints](/docs/en-us/projects/cross-platform.md#assistive-hints) accordingly. #### Loading and streaming First impressions matter. Time the full join-to-gameplay flow on your baseline device over a typical connection. If load times exceed a few seconds, consider enabling [instance streaming](/docs/en-us/workspace/streaming.md) to reduce initial join times and memory footprint. Test that streaming doesn't cause visible pop-in that breaks immersion during normal gameplay movement. #### Audio Test with both the device speaker and headphones. Mobile speakers have limited frequency range and volume — sound effects or voice lines that are clear on desktop headphones can be muddy or inaudible on a phone speaker. --- title: "Adaptive timestepping" url: /docs/en-us/physics/adaptive-timestepping last_updated: 2026-06-29T19:34:03Z description: "Adaptive timestepping automatically assigns physical parts to varying simulation timesteps." --- # Adaptive timestepping The Roblox physics engine simulates all parts inside the 3D workspace through Newton's second law of motion. This law of motion is solved over time via **timesteps** and a single timestep is done within a **worldstep** in Roblox. By default, Roblox simulates physics at 240 Hz. Given cycles of approximately 60 frames per second, around 4 worldsteps are advanced per frame. With **adaptive timestepping**, the physics engine automatically assigns parts to three "solver islands" by varying their simulation timestep, with an emphasis on 60 Hz for best performance. However, parts that are "harder" to solve will use a faster timestep like 240 Hz to ensure physical stability. Assignment criterions are subject to change, but parts assigned to the 240 Hz island include [assemblies](/docs/en-us/physics/assemblies.md) with high velocity values, high acceleration values, and complex mechanisms that are hard to solve. ## Enable adaptive mode To enable adaptive timestepping in Studio: 1. In the **Explorer** window, select the **Workspace** object. 2. In the **Properties** window, locate **PhysicsSteppingMethod** and select **Adaptive**. To observe the timestepping process in action, you can open the Studio **Microprofiler** (`Ctrl``F6`; `⌘``F6`). Once the experience is running, press `Ctrl``P` (`⌘``P`) to pause at the current frame. Under the scope named **physicsStepped**, observe that the scope name of **worldStep** now reads **worldStep - Adaptive**. Hovering your cursor above **LDLPGSSolver::solve** will reveal the status of how many islands belong in each frequency bucket; **1dt Islands** (240 Hz), **2dt Islands** (120 Hz) and **4dt Islands** (60 Hz). ## Debug visualization During testing, it may be useful to visualize frequencies for simulated parts. To enable this option: 1. Open [Studio Settings](/docs/en-us/studio/setup.md#customization). 2. From the **Physics** tab, enable **Are Timesteps Shown**. Once enabled, simulated parts will be outlined by their current simulation rate. If a part stops being simulated, either via the [sleep system](/docs/en-us/physics/sleep-system.md) or a network ownership change, the part will no longer be outlined. _Simulated parts outlined by the color representing their current simulation rate_ ## Fixed-rate scenarios Adaptive timestepping can improve physics performance by up to 2.5 times and it is recommended in most cases. However, some experiences should use **Fixed** mode (240 Hz), including: Experiences that require highly accurate simulations and stability, such as racing games, "destruction" simulations, or experiences featuring complex mechanisms like tanks. Simulations where most parts default to the 240 Hz solver island (red outlines during [debugging](#debug-visualization)). When 240 Hz islands interact with islands of different frequencies (60–120 Hz), those islands are converted to 240 Hz with an overhead that may negate any performance gains resulting from adaptive timestepping. --- title: "Assemblies" url: /docs/en-us/physics/assemblies last_updated: 2026-06-29T19:34:03Z description: "Explains physical assemblies and how they behave in Roblox's rigid body physics engine." --- # Assemblies An **assembly** is one or more [parts](/docs/en-us/parts.md) welded by a rigid `Class.WeldConstraint|WeldConstraint` or connected through movable joints, like `Class.Motor6D|Motor6Ds`. You can group an assembly of parts in a [model](/docs/en-us/parts/models.md) container to quickly organize the parts and related objects as a single asset. ![A light blue cube against a dark blue background that represents an assembly of 1 part.](../assets/physics/assemblies/Assembly-Example-Block.png)_1 assembly; 1 part_ ![A humanoid character model against a dark blue background that represents an assembly of 18 parts.](../assets/physics/assemblies/Assembly-Example-Avatar.png)_1 assembly; 18 parts_ ![A pirate that represents an assemble of 179 parts.](../assets/physics/assemblies/Assembly-Example-Ship.png)_1 assembly; 179 parts_ From a physics perspective, an assembly is considered a single **rigid body**, meaning no force can push or pull the connected parts from each other, and they will move as a single unit. All forces applied to a specific `Class.BasePart` are applied to its assembly — for instance, `Class.BasePart:ApplyImpulse()` applies impulse to the assembly at `Class.BasePart.AssemblyCenterOfMass`. > **Info:** The joints that combine multiple parts into assemblies are only active in the `Class.Workspace` or another `Class.WorldModel` instance. If the parts are stored elsewhere, all of the welds/connections that combine parts into assemblies will be non-functional. > **Success:** To view colored outlines around parts in order to visualize single rigid body assemblies, toggle on **Assemblies** from the [Visualization Options](/docs/en-us/studio/ui-overview.md#visualization-options) widget in the upper‑right corner of the 3D viewport. ## Assembly properties The following `Class.BasePart` properties show data regarding its assembly. Their values will be the same for any part in the same assembly, so it doesn't matter which part you use. | Property | Description | | --- | --- | | `Class.BasePart.AssemblyLinearVelocity` | The linear velocity vector of the part's assembly. Setting the velocity directly may lead to unrealistic motion, so usage of a `Class.VectorForce` or `Class.LinearVelocity` constraint is preferred, or `Class.BasePart:ApplyImpulse()` for an instantaneous change in linear velocity. | | `Class.BasePart.AssemblyAngularVelocity` | The angular velocity vector of the part's assembly. Setting the velocity directly may lead to unrealistic motion, so usage of a `Class.Torque` or `Class.AngularVelocity` constraint is preferred, or `Class.BasePart:ApplyAngularImpulse()` for an instantaneous change in angular velocity. | | `Class.BasePart.AssemblyCenterOfMass` | A read-only position calculated via the mass and position of all the parts in the assembly. A force applied to the center of mass will not cause angular acceleration, only linear. | | `Class.BasePart.AssemblyMass` | The sum of the `Class.BasePart.Mass` of all parts in the assembly. If the assembly has an anchored part, the assembly's mass is considered infinite. | | `Class.BasePart.AssemblyRootPart` | The part automatically chosen to represent the assembly's [root part](#assembly-root-part). | ## Assembly root part Every assembly has a **root part** indicated by its `Class.BasePart.AssemblyRootPart|AssemblyRootPart` property. This is the part that doesn't move when `Class.Motor6D` transforms are updated, as well as the part used to keep consistent physics replication and network ownership. You cannot explicitly set the root part, but the following factors affect probability from highest to lowest: An `Class.BasePart.Anchored|Anchored` part will always be assigned as the root part. Parts with `Class.BasePart.Massless|Massless` set to **false** (default) take precedence. Higher `Class.BasePart.RootPriority|RootPriority` values take precedence. Precedence based on the part's size, with multipliers for parts with specific names. ## Anchoring behavior When one of an assembly's parts is anchored, that part becomes the root part and all of the other parts become implicitly anchored with it. The following sequence illustrates this behavior. 1. Below, four parts are welded together with `Class.WeldConstraint|WeldConstraints` (green bars) to form a single assembly, as indicated by the matching colored outlines._Four parts welded to become a single assembly_ 2. If just **one** part in the assembly is anchored, the assembly will not change, other than the root part potentially changing (the anchored part always has the highest [priority](#assembly-root-part) for becoming the root part)._Anchored part (as indicated with an anchor icon) becomes the new root part_ 3. If more than one part is anchored, the assembly will **split**. Below, both the left and top parts are anchored, so the original assembly splits into two assemblies as shown by the colored outlines. Also, the `Class.WeldConstraint` between the two assemblies deactivates, since you cannot have an active weld between two anchored assemblies._Two assemblies with anchored parts as their respective root parts_ > **Warning:** If you want to anchor all parts in an assembly, you only need to anchor the assembly's root part. Anchoring all parts is actually less performant, as it creates more assemblies. --- title: "AlignOrientation" url: /docs/en-us/physics/constraints/align-orientation last_updated: 2026-06-29T19:34:03Z description: "The AlignOrientation constraint applies torque to align two attachments, or to align one attachment with a goal orientation." --- # AlignOrientation > **Info:** For an overview on creating, visualizing, and simulating mover constraints, including `Class.AlignOrientation`, see [mover constraints](/docs/en-us/physics/mover-constraints.md). Also see [Roblox units](/docs/en-us/physics/units.md) to understand how Roblox units compare to metric units. The `Class.AlignOrientation` constraint applies torque to align two attachments, or to align one attachment with a goal orientation. As indicated by the name, it only affects the **orientation** of the attachments, not their position (to align attachments **positionally**, see [AlignPosition](/docs/en-us/physics/constraints/align-position.md)). > **Warning:** By default, the constraint only applies torque to `Class.Constraint.Attachment0|Attachment0`, although this behavior can be controlled through the constraint's [reactionary torque](#reactionary-torque). ## Affected axes The axes affected by torque are controlled through the constraint's `Class.AlignOrientation.AlignType|AlignType` property. When set to `Enum.AlignType.AllAxes|AllAxes` (default), the constraint will apply torque about all 3 axes to achieve alignment. When set to `Enum.AlignType.PrimaryAxisParallel|PrimaryAxisParallel`, `Enum.AlignType.PrimaryAxisPerpendicular|PrimaryAxisPerpendicular`, or `Enum.AlignType.PrimaryAxisLookAt|PrimaryAxisLookAt`, torque will only occur when the **primary** axes become misaligned. ## Torque magnitude You can configure the `Class.AlignOrientation` constraint to apply the maximum torque that constraints allow through the `Class.AlignOrientation.RigidityEnabled|RigidityEnabled` property. When `true`, the physics solver reacts as quickly as possible to complete the alignment. When `false`, the torque is determined by `Class.AlignOrientation.MaxTorque|MaxTorque`, `Class.AlignOrientation.MaxAngularVelocity|MaxAngularVelocity`, and `Class.AlignOrientation.Responsiveness|Responsiveness`. ## Reactionary torque By default, the constraint only applies torque to `Class.Constraint.Attachment0|Attachment0` while `Class.Constraint.Attachment1|Attachment1` remains unaffected. If desired, torque can be applied to **both** attachments in **equal and opposite directions** by enabling `Class.AlignOrientation.ReactionTorqueEnabled|ReactionTorqueEnabled`. ## Attachment mode The `Class.AlignOrientation.Mode|Mode` property specifies whether the constraint uses **one** or **two** attachments in calculating its goal. By default, this is `Enum.OrientationAlignmentMode.TwoAttachment|TwoAttachment`, meaning that the constraint attempts to match the orientation of `Class.Constraint.Attachment0|Attachment0` with the orientation of `Class.Constraint.Attachment1|Attachment1`, disregarding `Class.AlignOrientation.CFrame|CFrame`, `Class.AlignOrientation.PrimaryAxis|PrimaryAxis`, and `Class.AlignOrientation.SecondaryAxis|SecondaryAxis`. If set to `Enum.OrientationAlignmentMode.OneAttachment|OneAttachment`, the constraint disregards `Class.Constraint.Attachment1|Attachment1` and attempts to match the orientation of `Class.Constraint.Attachment0|Attachment0` with the orientation of `Class.AlignOrientation.CFrame|CFrame`, or match the attachment's `Class.Attachment.Axis|Axis` and `Class.Attachment.SecondaryAxis|SecondaryAxis` with the constraint's `Class.AlignOrientation.PrimaryAxis|PrimaryAxis` and `Class.AlignOrientation.SecondaryAxis|SecondaryAxis` properties respectively. --- title: "AlignPosition" url: /docs/en-us/physics/constraints/align-position last_updated: 2026-06-29T19:34:03Z description: "The AlignPosition constraint applies force to move two attachments together, or to move one attachment to a goal position." --- # AlignPosition > **Info:** For an overview on creating, visualizing, and simulating mover constraints, including `Class.AlignPosition`, see [mover constraints](/docs/en-us/physics/mover-constraints.md). Also see [Roblox units](/docs/en-us/physics/units.md) to understand how Roblox units compare to metric units. An `Class.AlignPosition` constraint applies force to move two attachments together, or to move one attachment to a goal position. As indicated by the name, it only affects the **position** of the attachments, not their orientation (to align attachments by **orientation**, see [AlignOrientation](/docs/en-us/physics/constraints/align-orientation.md)). > **Warning:** By default, the constraint only applies force to `Class.Constraint.Attachment0|Attachment0`, although this behavior can be controlled through the constraint's [reactionary force](#reactionary-force). ## Force location By default, force is applied to the parent of `Class.Constraint.Attachment0|Attachment0` at that attachment's location, meaning that if the parent's center of mass is not aligned with the direction of the force, torque will be applied as well as force. Alternatively, force can be applied to the parents' center of mass by toggling on `Class.AlignPosition.ApplyAtCenterOfMass|ApplyAtCenterOfMass`. ## Force magnitude You can configure the `Class.AlignPosition` constraint to apply the maximum force that constraints allow through the `Class.AlignPosition.RigidityEnabled|RigidityEnabled` property. When `true`, the physics solver reacts as quickly as possible to complete the alignment. When `false`, the force is determined by `Class.AlignPosition.MaxForce|MaxForce`, `Class.AlignPosition.MaxVelocity|MaxVelocity`, and `Class.AlignPosition.Responsiveness|Responsiveness`. ## Reactionary force By default, the constraint only applies force to `Class.Constraint.Attachment0|Attachment0` while `Class.Constraint.Attachment1|Attachment1` remains unaffected. If desired, force can be applied to **both** attachments in **equal and opposite directions** by enabling `Class.AlignPosition.ReactionForceEnabled|ReactionForceEnabled`. ## Attachment mode The `Class.AlignPosition.Mode|Mode` property specifies whether the constraint uses **one** or **two** attachments in calculating its goal. By default, this is `Enum.PositionAlignmentMode.TwoAttachment|TwoAttachment`, meaning that the constraint disregards `Class.AlignPosition.Position|Position` and attempts to move `Class.Constraint.Attachment0|Attachment0` to the position of `Class.Constraint.Attachment1|Attachment1`. If set to `Enum.PositionAlignmentMode.OneAttachment|OneAttachment`, the constraint disregards `Class.Constraint.Attachment1|Attachment1` and attempts to move `Class.Constraint.Attachment0|Attachment0` to `Class.AlignPosition.Position|Position`. --- title: "AngularVelocity" url: /docs/en-us/physics/constraints/angular-velocity last_updated: 2026-06-29T19:34:03Z description: "The AngularVelocity constraint applies torque on an assembly to maintain a constant angular velocity." --- # AngularVelocity > **Info:** For an overview on creating, visualizing, and simulating mover constraints, including `Class.AngularVelocity`, see [mover constraints](/docs/en-us/physics/mover-constraints.md). Also see [Roblox units](/docs/en-us/physics/units.md) to understand how Roblox units compare to metric units. An `Class.AngularVelocity` constraint applies torque on an assembly to maintain a constant angular velocity. > **Warning:** The `Class.AngularVelocity` constraint applies torque that attempts to maintain a **constant** angular velocity. If you want to control the amount of torque applied, use a [Torque](/docs/en-us/physics/constraints/torque.md) constraint. Alternatively, if you only need **initial** angular velocity, set the `Class.BasePart.AssemblyAngularVelocity|AssemblyAngularVelocity` method directly on the assembly. ## Relativity Application of velocity can be controlled through the constraint's `Class.AngularVelocity.RelativeTo|RelativeTo` property. If set to `Enum.ActuatorRelativeTo|World`, the angular velocity vector is used as is. If set to `Enum.ActuatorRelativeTo|Attachment1` and the constraint's `Class.Constraint.Attachment1|Attachment1` property is set to another attachment, the angular velocity will be affected by that of the other attachment as seen by how the upper-left red part's velocity affects the attached blue part's velocity. _RelativeTo = **World**_ _RelativeTo = **Attachment1**_ > **Info:** Setting `Class.AngularVelocity.RelativeTo|RelativeTo` to `Enum.ActuatorRelativeTo|Attachment1` also exposes the `Class.AngularVelocity.ReactionTorqueEnabled|ReactionTorqueEnabled` property. --- title: "AnimationConstraint" url: /docs/en-us/physics/constraints/animation last_updated: 2026-06-29T19:34:03Z description: "AnimationConstraint aligns two base parts with an animate-able kinematic or force-based joint." --- # AnimationConstraint > **Info:** For an overview on creating, visualizing, and simulating mover constraints, including `Class.AnimationConstraint`, see [mover constraints](/docs/en-us/physics/mover-constraints.md). Also see [Roblox units](/docs/en-us/physics/units.md) to understand how Roblox units compare to metric units. An `Class.AnimationConstraint` constrains its `Class.Attachment|Attachments` by an offset [transform](#transform). It can be either [kinematic](#kinematic) for snap‑to behavior or it can follow the target trajectory using [force and torque](#force-and-torque). limited by `Class.AnimationConstraint.MaxForce|MaxForce` and `Class.AnimationConstraint.MaxTorque|MaxTorque`. ## Transform The constraint's `Class.AnimationConstraint.Transform|Transform` is the internal `Datatype.CFrame` that's manipulated when the constraint is being animated. How this transform is reached depends on the constraint's [kinematic](#kinematic) setting. ## Kinematic When the constraint's `Class.AnimationConstraint.IsKinematic|IsKinematic` property is `true`, the connected parts snap to the [transform](#transform) instantly and precisely without participating in physics simulation. When `false`, the connected parts follow the trajectory using [force and torque](#force-and-torque). ## Force and torque If the constraint is **not** set to [kinematic](#kinematic), the connected parts follow a trajectory toward the [transform](#transform) using a maximum force/torque limited by `Class.AnimationConstraint.MaxForce|MaxForce` and `Class.AnimationConstraint.MaxTorque|MaxTorque` respectively. --- title: "BallSocket" url: /docs/en-us/physics/constraints/ball-socket last_updated: 2026-06-29T19:34:03Z description: "BallSocketConstraint forces its two attachments into the same position and allows them to freely rotate about all three axes, with optional limits to restrict both tilt and twist." --- # BallSocket > **Info:** For an overview on creating, visualizing, and simulating mechanical constraints, including `Class.BallSocketConstraint`, see [Mechanical constraints](/docs/en-us/physics/mechanical-constraints.md). Also see [Roblox units](/docs/en-us/physics/units.md) to understand how Roblox units compare to metric units. A `Class.BallSocketConstraint` forces its two attachments into the same position and allows them to freely rotate about all three axes. However, you can enable limits to restrict both tilt and twist. ## Limits You can set limits to restrict both **tilt** and **twist** of a ball socket, similar to how a human's head can tilt and turn within a limited axial range. Enabling the `Class.BallSocketConstraint.LimitsEnabled|LimitsEnabled` property exposes the `Class.BallSocketConstraint.UpperAngle|UpperAngle` value to restrict **tilt** within a cone; it also exposes the `Class.BallSocketConstraint.TwistLimitsEnabled|TwistLimitsEnabled` property which, when enabled, lets you restrict **twist** rotation through the `Class.BallSocketConstraint.TwistLowerAngle|TwistLowerAngle` and `Class.BallSocketConstraint.TwistUpperAngle|TwistUpperAngle` limits. _Constraint with `Class.BallSocketConstraint.LimitsEnabled|LimitsEnabled` set to `true` and `Class.BallSocketConstraint.UpperAngle|UpperAngle` set to `15`_ _Constraint with `Class.BallSocketConstraint.LimitsEnabled|LimitsEnabled` and `Class.BallSocketConstraint.TwistLimitsEnabled|TwistLimitsEnabled` set to `true`, `Class.BallSocketConstraint.TwistLowerAngle|TwistLowerAngle` set to `-60`, and `Class.BallSocketConstraint.TwistUpperAngle|TwistUpperAngle` set to `0`_ > **Info:** Enabling `Class.BallSocketConstraint.LimitsEnabled|LimitsEnabled` also exposes the `Class.BallSocketConstraint.Restitution|Restitution` value which defines the elasticity of the attachments at their tilt and/or twist limits. > **Warning:** When `Class.BallSocketConstraint.LimitsEnabled|LimitsEnabled` is enabled, orientation of a ball socket's attachments affects how it rotates. To ensure rotation occurs around the desired axis, each attachment's `Class.Attachment.Axis|Axis`, visualized by the yellow arrow, should point in the same direction. --- title: "Cylindrical" url: /docs/en-us/physics/constraints/cylindrical last_updated: 2026-06-29T19:34:03Z description: "CylindricalConstraint allows its attachments to slide along one axis and rotate about another axis, with optional assigned angular and/or linear power." --- # Cylindrical > **Info:** For an overview on creating, visualizing, and simulating mechanical constraints, including `Class.CylindricalConstraint`, see [Mechanical constraints](/docs/en-us/physics/mechanical-constraints.md). Also see [Roblox units](/docs/en-us/physics/units.md) to understand how Roblox units compare to metric units. A `Class.CylindricalConstraint` allows its attachments to slide along one axis and rotate about another axis. This constraint, along with a [SpringConstraint](/docs/en-us/physics/constraints/spring.md), is ideal for building vehicle suspensions where the wheel shock can slide up and down while the wheel itself spins. This constraint can also be powered in both an [angular](#angular-power) and [linear](#linear-power) manner. > **Info:** Orientation of the attachments affects how the cylindrical aspect will rotate. To ensure rotation occurs around the desired axis, each attachment's `Class.Attachment.Axis|Axis` and `Class.Attachment.SecondaryAxis|SecondaryAxis`, visualized by the yellow and orange arrows, should point in the same direction. ## Angular power If a cylindrical's `Class.CylindricalConstraint.AngularActuatorType|AngularActuatorType` is set to `Enum.ActuatorType|Motor`, it attempts to rotate the attachments with the goal of reaching its `Class.CylindricalConstraint.AngularVelocity|AngularVelocity`. You can further control this rotation through both `Class.CylindricalConstraint.MotorMaxAngularAcceleration|MotorMaxAngularAcceleration` and `Class.CylindricalConstraint.MotorMaxTorque|MotorMaxTorque`. If a cylindrical's `Class.CylindricalConstraint.AngularActuatorType|AngularActuatorType` is set to `Enum.ActuatorType|Servo`, it attempts to rotate to an angle specified by `Class.CylindricalConstraint.TargetAngle|TargetAngle`. This rotation is controlled by `Class.CylindricalConstraint.AngularSpeed|AngularSpeed`, `Class.CylindricalConstraint.AngularResponsiveness|AngularResponsiveness`, and `Class.CylindricalConstraint.ServoMaxTorque|ServoMaxTorque`. ## Linear power If a cylindrical's `Class.CylindricalConstraint|ActuatorType` is set to `Enum.ActuatorType|Motor`, it attempts to translate the attachments with the goal of reaching `Class.CylindricalConstraint|Velocity`. You can further control this translation through both `Class.CylindricalConstraint|MotorMaxAcceleration` and `Class.CylindricalConstraint|MotorMaxForce`. If a cylindrical's `Class.CylindricalConstraint|ActuatorType` is set to `Enum.ActuatorType|Servo`, it attempts to translate the attachments to a set separation specified by `Class.CylindricalConstraint|TargetPosition`. This translation is controlled by `Class.CylindricalConstraint|Speed`, `Class.CylindricalConstraint|LinearResponsiveness`, and `Class.CylindricalConstraint|ServoMaxForce`. ## Limits You can set limits to restrict both the **sliding range** and **rotation** of a cylindrical constraint. Enabling the `Class.CylindricalConstraint|LimitsEnabled` property exposes the `Class.CylindricalConstraint|LowerLimit` and `Class.CylindricalConstraint|UpperLimit` values, as well as `Class.CylindricalConstraint|Restitution` which defines the elasticity of the attachments when they reach either limit. Enabling the `Class.CylindricalConstraint.AngularLimitsEnabled|AngularLimitsEnabled` property exposes the `Class.CylindricalConstraint.LowerAngle|LowerAngle` and `Class.CylindricalConstraint.UpperAngle|UpperAngle` limits, as well as `Class.CylindricalConstraint.AngularRestitution|AngularRestitution` which defines the elasticity of the attachments when they reach either limit. _LimitsEnabled = **true**_ _AngularLimitsEnabled = **true**_ ## Inclination angle `Class.CylindricalConstraint.InclinationAngle|InclinationAngle` defines the direction of the rotation axis as an angle from the **X** axis in the **X**/**Y** plane of `Class.Constraint.Attachment0|Attachment0`, from -180 to 180. This lets you tilt the rotating element without changing the sliding axis. _InclinationAngle = **90**_ _InclinationAngle = **30**_ --- title: "Hinge" url: /docs/en-us/physics/constraints/hinge last_updated: 2026-06-29T19:34:03Z description: "HingeConstraint allows its two attachments to rotate about one axis, with optional assigned power for motor or servo behavior." --- # Hinge > **Info:** For an overview on creating, visualizing, and simulating mechanical constraints, including `Class.HingeConstraint`, see [Mechanical constraints](/docs/en-us/physics/mechanical-constraints.md). Also see [Roblox units](/docs/en-us/physics/units.md) to understand how Roblox units compare to metric units. A `Class.HingeConstraint` allows its two attachments to rotate about one axis, forcing them into the same position and **X** axis alignment. This constraint can also be powered to behave like a [motor](#angular-power) or [servo](#angular-power), and you can set limits to restrict the hinge's rotational range. > **Info:** Orientation of a hinge's attachments affects how it rotates. To ensure rotation occurs around the desired axis, each attachment's `Class.Attachment.Axis|Axis`, visualized by the yellow arrow, should point in the same direction. ### Angular power If a hinge's `Class.HingeConstraint.ActuatorType|ActuatorType` is set to `Enum.ActuatorType|Motor`, it attempts to rotate the attachments with the goal of reaching its `Class.HingeConstraint.AngularVelocity|AngularVelocity`. You can further control this rotation through both `Class.HingeConstraint.MotorMaxAcceleration|MotorMaxAcceleration` and `Class.HingeConstraint.MotorMaxTorque|MotorMaxTorque`. If a hinge's `Class.HingeConstraint.ActuatorType|ActuatorType` is set to `Enum.ActuatorType|Servo`, it attempts to rotate to an angle specified by `Class.HingeConstraint.TargetAngle|TargetAngle`. This rotation is controlled by both `Class.HingeConstraint.AngularSpeed|AngularSpeed` and `Class.HingeConstraint.ServoMaxTorque|ServoMaxTorque`. _ActuatorType = **Motor**_ _ActuatorType = **Servo**_ ### Limits You can set limits to restrict the rotation of a hinge, useful for mechanisms like doors which should only swing open or closed within a set range. Enabling the `Class.HingeConstraint.LimitsEnabled|LimitsEnabled` property exposes the `Class.HingeConstraint.LowerAngle|LowerAngle` and `Class.HingeConstraint.UpperAngle|UpperAngle` limits, as well as `Class.HingeConstraint.Restitution|Restitution` which defines the elasticity of the attachments when they reach either limit. --- title: "LineForce" url: /docs/en-us/physics/constraints/line-force last_updated: 2026-06-29T19:34:03Z description: "The LineForce constraint applies force along the theoretical line connecting its two attachments." --- # LineForce > **Info:** For an overview on creating, visualizing, and simulating mover constraints, including `Class.LineForce`, see [mover constraints](/docs/en-us/physics/mover-constraints.md). Also see [Roblox units](/docs/en-us/physics/units.md) to understand how Roblox units compare to metric units. The `Class.LineForce` constraint applies force along the theoretical line connecting its two `Class.Attachment|Attachments`. As the end points (attachments) move, the direction of force will change accordingly. ## Force location By default, force is applied to either parent at its attachment location. If desired, force can be focused at each parent's center of mass by toggling on `Class.LineForce.ApplyAtCenterOfMass|ApplyAtCenterOfMass`. _ApplyAtCenterOfMass = **false**_ _ApplyAtCenterOfMass = **true**_ ## Inverse square law When `Class.LineForce.InverseSquareLaw|InverseSquareLaw` is true, the force magnitude is multiplied by the inverse square of the distance, meaning the force will increase exponentially as the two attachments get closer together, like magnets. When using this setting, it's recommended that you set a `Class.LineForce.MaxForce|MaxForce` threshold to prevent infinite force if the attachments align precisely. _InverseSquareLaw = **false**_ _InverseSquareLaw = **true**_ ## Reactionary force By default, the constraint only applies force to `Class.Constraint.Attachment0|Attachment0`, while `Class.Constraint.Attachment1|Attachment1` remains unaffected. However, force can be applied to both attachments in **equal and opposite directions** by enabling `Class.LineForce.ReactionForceEnabled|ReactionForceEnabled`. _ReactionForceEnabled = **false**_ _ReactionForceEnabled = **true**_ --- title: "LinearVelocity" url: /docs/en-us/physics/constraints/linear-velocity last_updated: 2026-06-29T19:34:03Z description: "The LinearVelocity constraint applies force on an assembly to maintain a constant velocity along a 3D vector, line, or 2D plane." --- # LinearVelocity > **Info:** For an overview on creating, visualizing, and simulating mover constraints, including `Class.LinearVelocity`, see [mover constraints](/docs/en-us/physics/mover-constraints.md). Also see [Roblox units](/docs/en-us/physics/units.md) to understand how Roblox units compare to metric units. A `Class.LinearVelocity` constraint applies force on an assembly to maintain a constant velocity. It can be set to apply force along a `Datatype.Vector3`, line, or 2D plane. > **Warning:** The `Class.LinearVelocity` constraint applies a force that attempts to maintain a **constant** linear velocity. If you want to control the amount of force applied, use a [VectorForce](/docs/en-us/physics/constraints/vector-force.md) constraint. Alternatively, if you only need **initial** linear velocity, set the `Class.BasePart.AssemblyLinearVelocity|AssemblyLinearVelocity` property directly on the assembly. ## Relativity Application of velocity can be controlled through the constraint's `Class.LinearVelocity.RelativeTo|RelativeTo` property. If set to `Enum.ActuatorRelativeTo|World`, force will be applied in world coordinates, independent of the parent or attachment orientations. If set to `Enum.ActuatorRelativeTo|Attachment0` or `Enum.ActuatorRelativeTo|Attachment1`, force will be applied relative to `Class.Constraint.Attachment0|Attachment0` or `Class.Constraint.Attachment1|Attachment1` respectively. _RelativeTo = **World**_ _RelativeTo = **Attachment0**_ --- title: "NoCollision" url: /docs/en-us/physics/constraints/no-collision last_updated: 2026-06-29T19:34:03Z description: "NoCollisionConstraint prevents collisions between two specific parts, but those parts may still register collisions with the rest of the world." --- # NoCollision The `Class.NoCollisionConstraint` prevents collisions between two specific parts, but those parts may still register collisions with the rest of the world. Compared to [collision groups](/docs/en-us/workspace/collisions.md#collision-groups), it provides a direct way to disable specific collisions, such as the wheel of a car scraping against the car's body. --- title: "Plane" url: /docs/en-us/physics/constraints/plane last_updated: 2026-06-29T19:34:03Z description: "PlaneConstraint moves two attachments into a position/orientation along a plane, and both attachments remain free to translate and rotate unless otherwise constrained." --- # Plane > **Info:** For an overview on creating, visualizing, and simulating mechanical constraints, including `Class.PlaneConstraint`, see [Mechanical constraints](/docs/en-us/physics/mechanical-constraints.md). Also see [Roblox units](/docs/en-us/physics/units.md) to understand how Roblox units compare to metric units. A `Class.PlaneConstraint` moves its `Class.Constraint.Attachment0|Attachment0` and `Class.Constraint.Attachment1|Attachment1` into a position/orientation along a plane whose normal vector is the primary axis of `Class.Constraint.Attachment0|Attachment0`. Both parent assemblies remain free to translate and rotate unless otherwise constrained. Compare the following examples for how the orientation of `Class.Constraint.Attachment0|Attachment0` defines the plane, while the orientation of `Class.Constraint.Attachment1|Attachment1` has no bearing. #### Orientation = (0, 0, 90) ![Attachment0 oriented to (0, 0, 90)](../../assets/physics/constraints/Plane-Attachment0-1.jpg) #### Orientation = (-45, 0, 90) ![Attachment0 oriented to (-45, 0, 90)](../../assets/physics/constraints/Plane-Attachment0-2.jpg) --- title: "Prismatic" url: /docs/en-us/physics/constraints/prismatic last_updated: 2026-06-29T19:34:03Z description: "PrismaticConstraint allows two attachments to slide along one axis but not rotate, with optional assigned power for mechanisms like sliding doors and elevator platforms." --- # Prismatic > **Info:** For an overview on creating, visualizing, and simulating mechanical constraints, including `Class.PrismaticConstraint`, see [Mechanical constraints](/docs/en-us/physics/mechanical-constraints.md). Also see [Roblox units](/docs/en-us/physics/units.md) to understand how Roblox units compare to metric units. A `Class.PrismaticConstraint` creates a rigid joint between two attachments, allowing them to slide along one axis but not rotate. The constraint can also be [powered](#linear-power) for mechanisms like sliding doors and elevator platforms. > **Info:** Orientation of a prismatic's attachments affects how it will move. To ensure movement occurs along the desired axis, each attachment's `Class.Attachment.Axis|Axis`, visualized by the yellow arrow, should point in the same direction. ## Linear power If a prismatic's `Class.PrismaticConstraint|ActuatorType` is set to `Enum.ActuatorType|Motor`, it attempts to translate the attachments with the goal of reaching `Class.PrismaticConstraint|Velocity`. You can further control this translation through both `Class.PrismaticConstraint|MotorMaxAcceleration` and `Class.PrismaticConstraint|MotorMaxForce`. If a prismatic's `Class.PrismaticConstraint|ActuatorType` is set to `Enum.ActuatorType|Servo`, it attempts to translate the attachments to a set separation specified by `Class.PrismaticConstraint|TargetPosition`. This translation is controlled by `Class.PrismaticConstraint|Speed`, `Class.PrismaticConstraint|LinearResponsiveness`, and `Class.PrismaticConstraint|ServoMaxForce`. _ActuatorType = **Motor**_ _ActuatorType = **Servo**_ ## Limits You can set limits to restrict the sliding range a prismatic. Enabling the `Class.PrismaticConstraint|LimitsEnabled` property exposes the `Class.PrismaticConstraint|LowerLimit` and `Class.PrismaticConstraint|UpperLimit` values, as well as `Class.PrismaticConstraint|Restitution` which defines the elasticity of the attachments when they reach either limit. --- title: "Rigid" url: /docs/en-us/physics/constraints/rigid last_updated: 2026-06-29T19:34:03Z description: "RigidConstraint connects two attachments with zero offset." --- # Rigid A `Class.RigidConstraint` connects two `Class.Attachment|Attachments` or `Class.Bone|Bones` and ensures they stay in the same relative position/orientation to each other. This flexibility gives it additional functionality beyond `Class.WeldConstraint`, such as attaching accessories to `Class.Attachment|Attachments` on a character rig. > **Info:** To attach two `Class.BasePart|BaseParts` together, versus two `Class.Attachment|Attachments` or `Class.Bone|Bones`, see [WeldConstraint](/docs/en-us/physics/constraints/weld.md). When creating a `Class.RigidConstraint` using Studio's [Constraint](/docs/en-us/physics/mechanical-constraints.md#create-constraints) button, the tool behaves differently depending on whether you click on existing `Class.BasePart|BaseParts`, `Class.Attachment|Attachments`, or `Class.Bone|Bones` after the tool is activated: - Clicking on an existing `Class.BasePart` creates a new `Class.Attachment` upon it as the intended `Class.RigidConstraint.Attachment0` or `Class.RigidConstraint.Attachment1`. - Clicking on an existing `Class.Attachment` or `Class.Bone` uses that instance as the intended `Class.RigidConstraint.Attachment0` or `Class.RigidConstraint.Attachment1`. Following the second valid click, a new `Class.RigidConstraint` is created to connect the two new attachments. If either the first or second click is **not** made on a `Class.BasePart`, `Class.Attachment`, or `Class.Bone`, the workflow is canceled and no constraint is created. --- title: "Rod" url: /docs/en-us/physics/constraints/rod last_updated: 2026-06-29T19:34:03Z description: "RodConstraint keeps two attachments separated by a defined length, with optional limits on rotational tilt." --- # Rod > **Info:** For an overview on creating, visualizing, and simulating mechanical constraints, including `Class.RodConstraint`, see [Mechanical constraints](/docs/en-us/physics/mechanical-constraints.md). Also see [Roblox units](/docs/en-us/physics/units.md) to understand how Roblox units compare to metric units. A `Class.RodConstraint` keeps two attachments separated by its defined `Class.RodConstraint.Length|Length`. By default, both attachments can rotate freely, although you can enable limits to restrict rotational tilt. ## Limits You can limit rotation of the attachments within a cone, independently of each other, by enabling the `Class.RodConstraint.LimitsEnabled|LimitsEnabled` property and setting `Class.RodConstraint.LimitAngle0|LimitAngle0` and `Class.RodConstraint.LimitAngle1|LimitAngle1` respectively. --- title: "Rope" url: /docs/en-us/physics/constraints/rope last_updated: 2026-06-29T19:34:03Z description: "RopeConstraint prevents two attachments from separating further than a defined length, with optional behavior as an extending or contracting winch." --- # Rope > **Info:** For an overview on creating, visualizing, and simulating mechanical constraints, including `Class.RopeConstraint`, see [Mechanical constraints](/docs/en-us/physics/mechanical-constraints.md). Also see [Roblox units](/docs/en-us/physics/units.md) to understand how Roblox units compare to metric units. A `Class.RopeConstraint` prevents two attachments from separating further than a defined `Class.RopeConstraint.Length|Length`. The attachments can move closer together than this length and both can freely rotate. `Class.RopeConstraint.Restitution|Restitution` defines the elasticity of the attachments when they reach the separation limit specified by `Class.RopeConstraint.Length|Length`. This constraint can also be powered to behave as a [motorized winch](#winch). ## Winch If a rope's `Class.RopeConstraint.WinchEnabled|WinchEnabled` property is enabled, it attempts to translate the attachments to a set separation specified by `Class.RopeConstraint.WinchTarget|WinchTarget`, effectively the target length of the rope in studs. This translation is controlled by `Class.RopeConstraint.WinchSpeed|WinchSpeed`, `Class.RopeConstraint.WinchResponsiveness|WinchResponsiveness`, and `Class.RopeConstraint.WinchForce|WinchForce`. > **Info:**`Class.RopeConstraint.WinchSpeed|WinchSpeed` must be a **positive** value, used to either contract or extend the rope length to `Class.RopeConstraint.WinchTarget|WinchTarget`. Setting a negative speed will revert to 0, not reverse the winch servo direction. --- title: "Spring" url: /docs/en-us/physics/constraints/spring last_updated: 2026-06-29T19:34:03Z description: "SpringConstraint applies a force on its attachments based on spring and damper behavior, with an optional minimum/maximum length." --- # Spring > **Info:** For an overview on creating, visualizing, and simulating mechanical constraints, including `Class.SpringConstraint`, see [Mechanical constraints](/docs/en-us/physics/mechanical-constraints.md). Also see [Roblox units](/docs/en-us/physics/units.md) to understand how Roblox units compare to metric units. A `Class.SpringConstraint` applies a force on its attachments based on spring and damper behavior. You can customize a spring's [damping](#damping) and [stiffness](#stiffness), as well as set minimum and maximum [limits](#limits) on the spring's length. ## Free length `Class.SpringConstraint.FreeLength|FreeLength` defines the natural resting length of the spring. If the attachments are further apart than the free length, they are forced together; if the attachments are closer together than the free length, they are forced apart. ## Damping The `Class.SpringConstraint.Damping|Damping` value controls how fast the spring's oscillation dies down. A value of 0 allows the spring to oscillate endlessly, while higher values bring the spring to a rest more quickly. ## Stiffness `Class.SpringConstraint.Stiffness|Stiffness` sets the strength of the spring. Higher values create a spring that responds with more force when its attachments are closer together or further apart than `Class.SpringConstraint.FreeLength|FreeLength`. _Stiffness = **25**_ _Stiffness = **500**_ ## Limits Enabling the `Class.SpringConstraint.LimitsEnabled|LimitsEnabled` property exposes the `Class.SpringConstraint.MinLength|MinLength` and `Class.SpringConstraint.MaxLength|MaxLength` values for setting the minimum and maximum length of the spring. If the spring's attachments reach these limits, they stop moving apart from one another without restitution. --- title: "Torque" url: /docs/en-us/physics/constraints/torque last_updated: 2026-06-29T19:34:03Z description: "The Torque constraint applies constant torque on an assembly from its center of mass." --- # Torque > **Info:** For an overview on creating, visualizing, and simulating mover constraints, including `Class.Torque`, see [mover constraints](/docs/en-us/physics/mover-constraints.md). Also see [Roblox units](/docs/en-us/physics/units.md) to understand how Roblox units compare to metric units. A `Class.Torque` constraint applies constant torque on an assembly from its center of mass. > **Warning:** Because the `Class.Torque` constraint applies **constant** torque and angular acceleration, very high speeds may result if no other forces are involved. If you want to maintain a more steady velocity over time, use an [AngularVelocity](/docs/en-us/physics/constraints/angular-velocity.md) constraint. Alternatively, if you only need **initial** velocity, set the `Class.BasePart.AssemblyAngularVelocity|AssemblyAngularVelocity` property directly on the assembly. ## Relativity By default, torque is applied relative to `Class.Constraint.Attachment0|Attachment0`. If the parent assembly rotates, the torque will change direction to match the adjusted orientation of the attachment. If `Class.Torque.RelativeTo|RelativeTo` is set to **World**, torque will be applied in world coordinates, independent of the parent or attachment orientations. If `Class.Torque.RelativeTo|RelativeTo` is set to **Attachment1**, torque will be applied relative to `Class.Constraint.Attachment1|Attachment1` and, if the attachment rotates, change to match its orientation. _RelativeTo = **Attachment0**_ _RelativeTo = **World**_ --- title: "TorsionSpring" url: /docs/en-us/physics/constraints/torsion-spring last_updated: 2026-06-29T19:34:03Z description: "TorsionSpringConstraint applies torque based on a relative angle and relative angular velocity, in an attempt to bring two axes from two parts together." --- # TorsionSpring > **Info:** For an overview on creating, visualizing, and simulating mechanical constraints, including `Class.TorsionSpringConstraint`, see [Mechanical constraints](/docs/en-us/physics/mechanical-constraints.md). Also see [Roblox units](/docs/en-us/physics/units.md) to understand how Roblox units compare to metric units. A `Class.TorsionSpringConstraint` applies torque based on a relative angle and relative angular velocity. It attempts to bring two axes from two parts together and is useful for [hinged](/docs/en-us/physics/constraints/hinge.md) swinging doors with a spring-back effect. > **Info:** Correct orientation of a torsion spring's attachments is important. The constraint will attempt to bring the `Class.Attachment.SecondaryAxis|SecondaryAxis` of each attachment, visualized by the orange arrows, into alignment. When building mechanisms like swinging doors, ensure that the secondary axes are **perpendicular** to the intended axis of rotation. ## Damping The `Class.TorsionSpringConstraint.Damping|Damping` value controls how fast the spring's oscillation dies down. A value of 0 allows the spring to oscillate endlessly, while higher values bring the spring to a rest more quickly. _Damping = **0**_ _Damping = **50**_ ## Stiffness `Class.TorsionSpringConstraint.Stiffness|Stiffness` sets the torsional strength of the spring. Higher values create a spring that responds with more force. ## Limits Enabling the `Class.TorsionSpringConstraint.LimitsEnabled|LimitsEnabled` property exposes the `Class.TorsionSpringConstraint.MaxAngle|MaxAngle` value to restrict the spring's range within a cone; it also exposes the `Class.TorsionSpringConstraint.Restitution|Restitution` value which defines the elasticity of the attachments when they reach their limit. --- title: "Universal" url: /docs/en-us/physics/constraints/universal last_updated: 2026-06-29T19:34:03Z description: "UniversalConstraint ensures two axes on two assemblies remain perpendicular, useful for applications such as vehicle power transmission to rear drive shafts, robotics, and more." --- # Universal > **Info:** For an overview on creating, visualizing, and simulating mechanical constraints, including `Class.UniversalConstraint`, see [Mechanical constraints](/docs/en-us/physics/mechanical-constraints.md). Also see [Roblox units](/docs/en-us/physics/units.md) to understand how Roblox units compare to metric units. A `Class.UniversalConstraint` ensures two axes on two assemblies remain perpendicular. Example applications include vehicle power transmission to the rear drive shafts, robotics, and more. ## Orientations Orientation of a universal's two attachments affects how it will move. To ensure it behaves correctly, the `Class.Attachment.SecondaryAxis|SecondaryAxis` of the attachments, visualized by the orange arrows, should be **perpendicular** to each other. Note that a green L-shaped indicator appears when the axes are properly oriented. #### Axes Perpendicular ![Secondary axes perpendicular for correct behavior](../../assets/physics/constraints/Universal-Attachments-Correct.jpg) #### Axes Misoriented ![Secondary axes misoriented](../../assets/physics/constraints/Universal-Attachments-Incorrect.jpg) ## Limits Enabling the `Class.UniversalConstraint.LimitsEnabled|LimitsEnabled` property exposes the `Class.UniversalConstraint.MaxAngle|MaxAngle` limit to restrict tilt within a cone, as well as `Class.UniversalConstraint.Restitution|Restitution` which defines the elasticity of the attachments when they reach the limit. --- title: "VectorForce" url: /docs/en-us/physics/constraints/vector-force last_updated: 2026-06-29T19:34:03Z description: "The VectorForce constraint applies constant linear force on an assembly." --- # VectorForce > **Info:** For an overview on creating, visualizing, and simulating mover constraints, including `Class.VectorForce`, see [mover constraints](/docs/en-us/physics/mover-constraints.md). Also see [Roblox units](/docs/en-us/physics/units.md) to understand how Roblox units compare to metric units. The `Class.VectorForce` constraint applies constant linear force on an assembly. The direction and strength of the force is determined by a `Datatype.Vector3` and can be relative to an attachment on the part, another attachment, or the world coordinate system. > **Warning:** Because the `Class.VectorForce` constraint applies **constant** force and acceleration, very high speeds may result if no other forces are involved. If you want to maintain a more steady velocity over time, use a [LinearVelocity](/docs/en-us/physics/constraints/linear-velocity.md) constraint. Alternatively, if you only need **initial** velocity, set the `Class.BasePart.AssemblyLinearVelocity|AssemblyLinearVelocity` property directly on the assembly. ## Force location By default, force is applied to the assembly at the location of `Class.Constraint.Attachment0|Attachment0`. Thus, if its center of mass is not aligned with the direction/point of force, torque will be applied as well. If desired, force can be focused at the center of mass by toggling on `Class.VectorForce.ApplyAtCenterOfMass|ApplyAtCenterOfMass`. _ApplyAtCenterOfMass = **false**_ _ApplyAtCenterOfMass = **true**_ ## Relativity By default, force is applied relative to `Class.Constraint.Attachment0|Attachment0`. If the parent assembly rotates, the force will change direction to match the adjusted orientation of the attachment; visualize this behavior in how the thruster of a rocket pushes it forward, regardless of the rocket's rotation. If `Class.VectorForce.RelativeTo|RelativeTo` is set to **World**, force will be applied in world coordinates, independent of the parent or attachment orientations; visualize this behavior as a directional force like the wind blowing against an object. If `Class.VectorForce.RelativeTo|RelativeTo` is set to **Attachment1**, force will be applied relative to `Class.Constraint.Attachment1|Attachment1` and, if the attachment rotates, the force will change to match its orientation. _RelativeTo = **Attachment0**_ _RelativeTo = **World**_ --- title: "Weld" url: /docs/en-us/physics/constraints/weld last_updated: 2026-06-29T19:34:03Z description: "WeldConstraint attaches two BaseParts together and ensures they stay in the same relative position and orientation to each other." --- # Weld A `Class.WeldConstraint` connects two `Class.BasePart|BaseParts` and ensures they stay in the same relative position and orientation to each other. Even if the two parts are not touching, you can weld them together. > **Info:** To attach two `Class.Attachment|Attachments` or `Class.Bone|Bones`, versus two `Class.BasePart|BaseParts`, see [RigidConstraint](/docs/en-us/physics/constraints/rigid.md). Unique to `Class.WeldConstraint` are slight behavioral differences in how Studio handles creation and repositioning of welded parts. #### Weld Creation When creating a `Class.WeldConstraint` using Studio's [Constraint](/docs/en-us/physics/mechanical-constraints.md#create-constraints) button, the tool behaves differently depending on how many `Class.BasePart|BaseParts` are selected when the tool is activated: - If no `Class.BasePart|BaseParts` are selected, the next two `Class.BasePart|BaseParts` clicked will be connected by a new `Class.WeldConstraint`. If the same `Class.BasePart` is clicked twice, no constraint will be created. - If one `Class.BasePart` is already selected, the next `Class.BasePart` clicked will be connected to the selected one with a new `Class.WeldConstraint`. - If multiple `Class.BasePart|BaseParts` are selected, those which are touching or overlapping will be automatically welded together by new `Class.WeldConstraint|WeldConstraints`. #### Repositioning Welded Parts Roblox handles moving a welded part differently depending on whether the part was moved through its `Class.BasePart.Position|Position` or through its `Datatype.CFrame`. - If a welded part's `Class.BasePart.Position|Position` is updated, that part will move but none of the connected parts will move with it. The weld will recalculate the offset from the other parts based on the moved part's new position. - If a welded part's `Datatype.CFrame` is updated, that part will move **and** all of the connected parts will also move, ensuring they maintain the same offset as when the weld was created. --- title: "Physics" url: /docs/en-us/physics last_updated: 2026-06-29T19:34:03Z description: "Overview of Roblox's rigid body physics engine, assemblies, and constraints." --- # Physics Roblox uses a rigid body physics engine. `Class.BasePart|BaseParts` are subject to physical forces as long as they are not `Class.BasePart.Anchored|Anchored`. You can create physical [assemblies](#assemblies) using attachments and constraints, and you can detect and control [collisions](#collisions) between objects using events and collision filtering. ## Assemblies An [assembly](/docs/en-us/physics/assemblies.md) is one or more `Class.BasePart|BaseParts` connected by rigid constraints or motors (animated rigid joints). Assemblies can be set to an initial linear or angular velocity, or their behavior can be affected through [constraints](#constraints). ![A light blue cube against a dark blue background that represents an assembly of 1 part.](../assets/physics/assemblies/Assembly-Example-Block.png)_1 assembly; 1 part_ ![A humanoid character model against a dark blue background that represents an assembly of 18 parts.](../assets/physics/assemblies/Assembly-Example-Avatar.png)_1 assembly; 18 parts_ ![A pirate that represents an assemble of 179 parts.](../assets/physics/assemblies/Assembly-Example-Ship.png)_1 assembly; 179 parts_ ## Constraints Non-anchored assemblies react to force from gravity and collisions, but physical force can also be applied and simulated through [mechanical constraints](/docs/en-us/physics/mechanical-constraints.md) or [mover constraints](/docs/en-us/physics/mover-constraints.md). #### Mechanical Mechanical constraints include familiar objects like [hinges](/docs/en-us/physics/constraints/hinge.md), [springs](/docs/en-us/physics/constraints/spring.md), and [ropes](/docs/en-us/physics/constraints/rope.md) which can be used to build mechanisms. Each is summarized in the [mechanical constraints](/docs/en-us/physics/mechanical-constraints.md) guide. #### Mover Mover constraints apply velocity, force, or torque to assemblies, align assemblies by position or orientation, and more. Each is summarized in the [mover constraints](/docs/en-us/physics/mover-constraints.md) guide. ## Collisions Collision events automatically occur when two `Class.BasePart|BaseParts` touch or stop touching in the 3D world. You can [detect](/docs/en-us/workspace/collisions.md) these collisions through the `Class.BasePart.Touched|Touched` and `Class.BasePart.TouchEnded|TouchEnded` events which occur regardless of either part's `Class.BasePart.CanCollide|CanCollide` property value. Through [collision filtering](/docs/en-us/workspace/collisions.md#collision-filtering) techniques such as collision groups or part‑to‑part filtering, you can control which physical assemblies collide with others. See the [collisions](/docs/en-us/workspace/collisions.md) guide for more details on detecting and filtering collisions. ## Network ownership In order to support complex physical mechanisms while also aiming for a smooth and responsive experience for players, the Roblox physics engine utilizes a **distributed physics** system in which computations are distributed between the server and all connected clients. Within this system, the engine assigns **network ownership** of physically simulated `Class.BasePart|BaseParts` to either a client or server to divide the work of calculating physics. See the [network ownership](/docs/en-us/physics/network-ownership.md) guide for further details. ## Adaptive timestepping The engine emphasizes best performance by automatically assigning assemblies to one of three simulation rates. For scenarios featuring complex mechanisms like tanks, you can improve stability by setting a fixed timestep. See the [adaptive timestepping](/docs/en-us/physics/adaptive-timestepping.md) guide for more information. ## Sleep system When an assembly is not moving or accelerating, the Roblox physics engine improves performance by automatically stops simulating the assembly. See the [sleep system](/docs/en-us/physics/sleep-system.md) guide for details. --- title: "Mechanical constraints" url: /docs/en-us/physics/mechanical-constraints last_updated: 2026-06-29T19:34:04Z description: "Mechanical constraints behave as conceptual mechanical connections such as hinges, springs, and motors." --- # Mechanical constraints The physics engine includes the following `Class.Constraint|Constraints` that behave as conceptual mechanical connections, including hinges, springs, ropes, and more. In addition, various [mover constraints](/docs/en-us/physics/mover-constraints.md) are available to exert directional or rotational force upon [assemblies](/docs/en-us/physics/assemblies.md). [Ball Socket](/docs/en-us/physics/constraints/ball-socket.md) _`Class.BallSocketConstraint` forces its two attachments into the same position and allows them to freely rotate about all three axes, with optional limits to restrict both tilt and twist_ [Hinge](/docs/en-us/physics/constraints/hinge.md) _`Class.HingeConstraint` allows its two attachments to rotate about one axis, with optional assigned power for motor or servo behavior_ [Prismatic](/docs/en-us/physics/constraints/prismatic.md) _`Class.PrismaticConstraint` allows two attachments to slide along one axis but not rotate, with optional assigned power for mechanisms like sliding doors and elevator platforms_ [Cylindrical](/docs/en-us/physics/constraints/cylindrical.md) _`Class.CylindricalConstraint` allows its attachments to slide along one axis and rotate about another axis, with optional assigned angular and/or linear power_ [Spring](/docs/en-us/physics/constraints/spring.md) _`Class.SpringConstraint` applies a force on its attachments based on spring and damper behavior, with an optional minimum/maximum length_ [Torsion Spring](/docs/en-us/physics/constraints/torsion-spring.md) _`Class.TorsionSpringConstraint` applies torque based on a relative angle and relative angular velocity, in an attempt to bring two axes from two parts together_ [Universal](/docs/en-us/physics/constraints/universal.md) _`Class.UniversalConstraint` ensures two axes on two assemblies remain perpendicular, useful for applications such as vehicle power transmission to rear drive shafts, robotics, and more_ [Rope](/docs/en-us/physics/constraints/rope.md) _`Class.RopeConstraint` prevents two attachments from separating further than a defined length, with optional behavior as an extending or contracting winch_ [Rod](/docs/en-us/physics/constraints/rod.md) _`Class.RodConstraint` keeps two attachments separated by a defined length, with optional limits on rotational tilt_ [Plane](/docs/en-us/physics/constraints/plane.md) _`Class.PlaneConstraint` moves two attachments into a position/orientation along a plane, and both attachments remain free to translate and rotate unless otherwise constrained_ [Weld](/docs/en-us/physics/constraints/weld.md) _`Class.WeldConstraint` connects two `Class.BasePart|BaseParts` and ensures they stay in the same relative position and orientation to each other, even if they are not touching_ [Rigid](/docs/en-us/physics/constraints/rigid.md) _`Class.RigidConstraint` connects two `Class.Attachment|Attachments` or `Class.Bone|Bones` and ensures they stay in the same relative position/orientation to each other, even if they are not touching_ [No Collision](/docs/en-us/physics/constraints/no-collision.md) _`Class.NoCollisionConstraint` prevents collisions between two specific parts, but those parts may still register collisions with the rest of the world_ ## Constraint visualization To accurately visualize constraints in Studio, you can toggle the following from the **View** menu or through the respective shortcut: - **Show Welds** (`Alt``W` or `⌥``W`) — Show `Class.WeldConstraint|WeldConstraints` separately from the visualization of other constraints. - **Show Constraint Details** (`Alt``D` or `⌥``D`) — Show complete visual details of non-weld constraints. > **Success:** In addition to the above visualization, you can view colored outlines around mechanisms (groups of parts that share simulation step and [network ownership](/docs/en-us/physics/network-ownership.md)) by toggling on **Mechanisms** from the [Visualization Options](/docs/en-us/studio/ui-overview.md#visualization-options) widget in the upper‑right corner of the 3D viewport. ## Create constraints All mechanical constraints must connect one or two `Class.Attachment|Attachments` or `Class.Bone|Bones`, except for [WeldConstraint](/docs/en-us/physics/constraints/weld.md) and [NoCollisionConstraint](/docs/en-us/physics/constraints/no-collision.md). When connected to `Class.Bone|Bones`, the constraint will use their animated position and orientation. To create a mechanical constraint, you can either insert one from the **Constraint** picker/button or through the [Explorer](/docs/en-us/studio/explorer.md) window. #### Constraint Picker 1. In Studio's **Model** tab toolbar, click‑hold over the small corner arrow on a constraint button to open its picker menu, then select the desired constraint.![Constraint pickers indicated in Studio's toolbar](../assets/studio/general/Toolbar-Constraint-Pickers.png) 2. In the 3D viewport, hover over any `Class.Part` or `Class.MeshPart` and click to add a new `Class.Attachment` to the part at the visualized point. Alternatively, hover over and click an existing `Class.Attachment` or `Class.Bone` to use it for the constraint. 3. Most mechanical constraints require a **secondary** attachment in their functionality, so the tool will typically prompt you to repeat the previous step on another `Class.Part`, `Class.MeshPart`, `Class.Attachment`, or `Class.Bone`.![SpringConstraint connecting two attachments on separate blocks. One block is green and the other is blue.](../assets/physics/constraints/Creation-Diagram.jpg)_Completed [SpringConstraint](/docs/en-us/physics/constraints/spring.md) connecting two attachments_ #### Explorer > **Info:** Note that [WeldConstraint](/docs/en-us/physics/constraints/weld.md) and [NoCollisionConstraint](/docs/en-us/physics/constraints/no-collision.md) do not utilize `Class.Attachment|Attachments` or `Class.Bone|Bones`, so their linking properties in the following steps are **Part0**/**Part1**, not **Attachment0**/**Attachment1**. Similarly, you'll need to select a `Class.Part` or `Class.MeshPart` to complete the link, not an `Class.Attachment` or `Class.Bone`. 1. In the [Explorer](/docs/en-us/studio/explorer.md) hierarchy, hover over the intended parent, click the **⊕** button, and insert the desired constraint from the dropdown menu, such as a [SpringConstraint](/docs/en-us/physics/constraints/spring.md).![New SpringConstraint in Explorer hierarchy.](../assets/studio/explorer/SpringConstraint.png) 2. With the new constraint selected, locate its currently empty **Attachment0** property in the [Properties](/docs/en-us/studio/properties.md) window.![Constraint's Attachment0 property highlighted in the Properties window.](../assets/studio/properties/SpringConstraint-Attachment0-Unassigned.png) 3. Link the **Attachment0** property to an `Class.Attachment` or `Class.Bone` in two consecutive steps: 1. In the [Properties](/docs/en-us/studio/properties.md) window, click in the **Attachment0** row to reveal the selection cursor. 2. In the [Explorer](/docs/en-us/studio/explorer.md) hierarchy, click on the target `Class.Attachment` or `Class.Bone`.![Mouse pointer hovering over Attachment0 property in the Properties window.](../assets/physics/constraints/SpringConstraint-Link-Attachment0-1.png)![Target attachment indicated in Explorer hierarchy.](../assets/physics/constraints/SpringConstraint-Link-Attachment0-2.png) 4. Most mechanical constraints require a **secondary** `Class.Attachment` or `Class.Bone` in their functionality. If necessary, repeat the previous step on the **Attachment1** property in the [Properties](/docs/en-us/studio/properties.md) window. ## Physical simulation To simulate physics while [moving](/docs/en-us/parts.md#move) or [rotating](/docs/en-us/parts.md#rotate) parts, you can switch from **Geometric** mode to **Physical** mode in Studio's toolbar, effectively forcing parts to obey physical limitations. For example, if two parts are attached by a `Class.RopeConstraint` and you drag one part around the scene, the other part will follow as the rope becomes taut. --- title: "Mover constraints" url: /docs/en-us/physics/mover-constraints last_updated: 2026-06-29T19:34:04Z description: "Mover constraints apply force or torque to move one or more assemblies." --- # Mover constraints The physics engine includes the following `Class.Constraint|Constraints` that apply force or torque to move one or more assemblies. In addition, various [mechanical constraints](/docs/en-us/physics/mechanical-constraints.md) are available which behave as conceptual mechanical connections, including hinges, springs, ropes, and more. [Linear Velocity](/docs/en-us/physics/constraints/linear-velocity.md) _`Class.LinearVelocity` applies force on an assembly to maintain a constant velocity along a 3D vector, line, or 2D plane_ [Angular Velocity](/docs/en-us/physics/constraints/angular-velocity.md) _`Class.AngularVelocity` applies torque on an assembly to maintain a constant angular velocity_ [Align Position](/docs/en-us/physics/constraints/align-position.md) _`Class.AlignPosition` applies force to move two attachments together, or to move one attachment to a goal position_ [Align Orientation](/docs/en-us/physics/constraints/align-orientation.md) _`Class.AlignOrientation` applies torque to align two attachments, or to align one attachment with a goal orientation_ [Vector Force](/docs/en-us/physics/constraints/vector-force.md) _`Class.VectorForce` applies constant linear force on an assembly_ [Torque](/docs/en-us/physics/constraints/torque.md) _`Class.Torque` applies constant torque on an assembly from its center of mass_ [Line Force](/docs/en-us/physics/constraints/line-force.md) _`Class.LineForce` applies force along the theoretical line connecting its two attachments_ [Animation Constraint](/docs/en-us/physics/constraints/animation.md) _`Class.AnimationConstraint` constrains its `Class.Attachment|Attachments` by an offset transform `Datatype.CFrame`._ ## Constraint visualization To accurately visualize constraints in Studio, you can toggle the following from the **View** menu or through the respective shortcut: - **Show Welds** (`Alt``W` or `⌥``W`) — Show `Class.WeldConstraint|WeldConstraints` separately from the visualization of other constraints. - **Show Constraint Details** (`Alt``D` or `⌥``D`) — Show complete visual details of non-weld constraints. > **Success:** In addition to the above visualization, you can view colored outlines around mechanisms (groups of parts that share simulation step and [network ownership](/docs/en-us/physics/network-ownership.md)) by toggling on **Mechanisms** from the [Visualization Options](/docs/en-us/studio/ui-overview.md#visualization-options) widget in the upper‑right corner of the 3D viewport. ## Create constraints Mover constraints typically connect one or two `Class.Attachment|Attachments` or `Class.Bone|Bones`. When connected to `Class.Bone|Bones`, the constraint will use their animated position and orientation. To create a mechanical constraint, you can either insert one from the **Constraint** picker/button or through the [Explorer](/docs/en-us/studio/explorer.md) window. #### Constraint Picker 1. In Studio's **Model** tab toolbar, click‑hold over the small corner arrow on a constraint button to open its picker menu, then select the desired constraint.![Constraint pickers indicated in Studio's toolbar](../assets/studio/general/Toolbar-Constraint-Pickers.png) 2. In the 3D viewport, hover over any `Class.Part` or `Class.MeshPart` and click to add a new `Class.Attachment` to the part at the visualized point. Alternatively, hover over and click an existing `Class.Attachment` or `Class.Bone` to use it for the constraint. 3. Some mover constraints utilize or support a **secondary** attachment in their functionality, so the tool might prompt you to repeat the previous step on another `Class.Part`, `Class.MeshPart`, or `Class.Attachment`, or `Class.Bone`.![AngularVelocity using one attachment](../assets/physics/constraints/Constraint-AngularVelocity-Labeled.jpg)_[AngularVelocity](/docs/en-us/physics/constraints/angular-velocity.md) using one attachment_![AlignPosition using two attachments](../assets/physics/constraints/Constraint-AlignPosition-Labeled.jpg)_[AlignPosition](/docs/en-us/physics/constraints/align-position.md) using two attachments_ #### Explorer 1. In the [Explorer](/docs/en-us/studio/explorer.md) hierarchy, hover over the intended parent, click the **⊕** button, and insert the desired constraint from the dropdown menu, such as a [LinearVelocity](/docs/en-us/physics/constraints/linear-velocity.md) constraint.![New LinearVelocity in Explorer window.](../assets/studio/explorer/LinearVelocity.png) 2. With the new constraint selected, locate its currently empty **Attachment0** property in the [Properties](/docs/en-us/studio/properties.md) window.![Constraint's Attachment0 property highlighted in Properties window.](../assets/studio/properties/LinearVelocity-Attachment0-Unassigned.png) 3. Link the **Attachment0** property to an `Class.Attachment` or `Class.Bone` in two consecutive steps: 1. In the [Properties](/docs/en-us/studio/properties.md) window, click in the **Attachment0** row to reveal the selection cursor. 2. In the [Explorer](/docs/en-us/studio/explorer.md) hierarchy, click on the target `Class.Attachment` or `Class.Bone`.![Mouse pointer hovering over Attachment0 property in Properties window.](../assets/physics/constraints/LinearVelocity-Link-Attachment0-1.png)![Target attachment indicated in Explorer window.](../assets/physics/constraints/LinearVelocity-Link-Attachment0-2.png) 4. Some mover constraints use or support a **secondary** `Class.Attachment` or `Class.Bone` in their functionality. If necessary, repeat the previous step on the **Attachment1** property in the **Properties** window. For instance: - By default, [AlignPosition](/docs/en-us/physics/constraints/align-position.md) and [AlignOrientation](/docs/en-us/physics/constraints/align-orientation.md) align their primary attachment (**Attachment0**) with a secondary attachment (**Attachment1**). - [LineForce](/docs/en-us/physics/constraints/line-force.md) **requires** two attachments to apply force along the theoretical line connecting them. ## Physical simulation To simulate physics while [moving](/docs/en-us/parts.md#move) or [rotating](/docs/en-us/parts.md#rotate) parts, you can switch from **Geometric** mode to **Physical** mode in Studio's toolbar, effectively forcing parts to obey physical limitations. For example, if two parts are attached by a `Class.RopeConstraint` and you drag one part around the scene, the other part will follow as the rope becomes taut. ## Legacy mover conversion If your experience relies on legacy `Class.BodyMover`‑based constraints, review the following notes when converting to modern mover constraints. **`BodyPosition`  ⟩  `AlignPosition`** `Class.AlignPosition` satisfies the majority of use cases covered by the deprecated `Class.BodyPosition` mover. To sync with how the legacy mover treated each component independently and allowed a different force along each dimension, the `Class.AlignPosition.ForceLimitMode|ForceLimitMode` property of `Class.AlignPosition` allows the constraint to operate in `Enum.ForceLimitMode|Magnitude` mode or `Enum.ForceLimitMode|PerAxis` mode: - In `Enum.ForceLimitMode|Magnitude` mode, the existing behavior is preserved and `Class.AlignPosition.MaxForce|MaxForce` is interpreted as a magnitude. - In `Enum.ForceLimitMode|PerAxis` mode, the force along each axis can be specified independently. Because the maximum force is specified as a vector, the reference frame of the force can also be specified through the `Class.AlignPosition.ForceRelativeTo|ForceRelativeTo` property with `Enum.ActuatorRelativeTo|World`, `Enum.ActuatorRelativeTo|Attachment0`, and `Enum.ActuatorRelativeTo|Attachment1` options. Additionally, the formulation for the internal controller is modified to match that of `Class.BodyPosition`. **`BodyGyro`  ⟩  `AlignOrientation`** `Class.AlignOrientation` satisfies the majority of use cases covered by the deprecated `Class.BodyGyro` mover. The `Class.AlignOrientation.AlignType|AlignType` modes of `Class.AlignOrientation` provide sufficient freedom for most applications and the combination of multiple constraints can replicate the vector torque limit. Additionally, the `Enum.AlignType|PrimaryAxisLookAt` mode forces the primary axis of the constraint's first attachment (`Class.AlignOrientation.Attachment0|Attachment0`) to always point towards the second attachment (`Class.AlignOrientation.Attachment1|Attachment1`), making it a lot easier to add things such as motion tracking security cameras or guided missiles. **`BodyVelocity`  ⟩  `LinearVelocity`** `Class.LinearVelocity` satisfies the majority of use cases covered by the deprecated `Class.BodyVelocity` mover. Although the legacy mover allows for a `Class.BodyVelocity.MaxForce|MaxForce` vector, the typical application of that vector force was to zero a particular component, allowing the constraint to be disabled along that dimension. `Class.LinearVelocity` achieves a similar effect by operating in distinct `Class.LinearVelocity.VelocityConstraintMode|VelocityConstraintMode` modes that corresponded to one (`Enum.VelocityConstraintMode|Line`), two (`Enum.VelocityConstraintMode|Plane`), and three (`Enum.VelocityConstraintMode|Vector`) dimensions. Additionally, the `Class.LinearVelocity.ForceLimitMode|ForceLimitMode` property with the option of `Enum.ForceLimitMode|PerAxis` accommodates any applications of the vector force with all non‑zero components, such as an increase in the force along a single axis to counteract gravity. **`BodyAngularVelocity`  ⟩  `AngularVelocity`** Although `Class.AngularVelocity` has some discrepancies with the deprecated `Class.BodyAngularVelocity` mover, specific cases related to those discrepancies have not been highlighted by the community, nor internally. As a separate improvement, `Class.AngularVelocity` works with `Class.Attachment|Attachments` and the `Class.AngularVelocity.RelativeTo|RelativeTo` property lets you specify the `Datatype.CFrame` in which the force is specified, for example `Enum.ActuatorRelativeTo|World` or `Enum.ActuatorRelativeTo|Attachment1`. **`BodyForce`/`BodyThrust`  ⟩  `VectorForce`** `Class.VectorForce` satisfies all use cases offered by the deprecated `Class.BodyForce` and `Class.BodyThrust` movers. The modern constraint works with `Class.Attachment|Attachments` and its `Class.VectorForce.RelativeTo|RelativeTo` property lets you apply force to a relative offset from center, similar to how `Class.BodyThrust.Location` worked. **`RocketPropulsion`  ⟩  `LineForce`/`AlignOrientation`** A combination of `Class.LineForce` and `Class.AlignOrientation` satisfies the majority of use cases covered by the deprecated `Class.RocketPropulsion` mover. In the example of a guided missile, `Class.LineForce` can be used to control the "follow target" behavior of `Class.RocketPropulsion` while `Class.AlignOrientation` and its `Class.AlignOrientation.LookAtPosition|LookAtPosition` property can be used to control the "face target" behavior. --- title: "Network ownership" url: /docs/en-us/physics/network-ownership last_updated: 2026-06-29T19:34:03Z description: "Learn how the Roblox Engine utilizes network ownership to improve physical responsiveness for players." --- # Network ownership In order to support complex physical mechanisms while also aiming for a smooth and responsive experience for players, the Roblox [physics](/docs/en-us/physics.md) engine utilizes a **distributed physics** system in which computations are distributed between the server and all connected clients. Within this system, the engine assigns **network ownership** of physically simulated `Class.BasePart|BaseParts` to either a client or server to divide the work of calculating physics. Clients experience **more responsive** physics interactions with parts that they own, since there's no latency from communication with the server. Network ownership also improves server performance because physics computations can be split up among individual clients, allowing the server to prioritize other tasks. ## BasePart ownership By default, the server retains ownership of any `Class.BasePart`. Additionally, the server **always** owns anchored `Class.BasePart|BaseParts` and you cannot manually change their ownership. Based on a client's hardware capacity and the player's `Class.Player.Character` proximity to an unanchored `Class.BasePart`, the engine automatically assigns ownership of that part to the client. Thus, parts close to a player's character are more likely to become player-owned. ## Assembly ownership If a physics-based mechanism has no anchored parts, [setting ownership](#setting-ownership) on an [assembly](/docs/en-us/physics/assemblies.md) within that mechanism sets the same ownership for **every assembly** in the mechanism. If you anchor a lone assembly that is **not** part of a broader mechanism, its ownership goes to the server, since the server always owns anchored `Class.BasePart|BaseParts`. Upon unanchoring the same assembly, its previous ownership state is lost and it reverts to automatic handling by the engine. If you anchor one assembly within a broader mechanism of assemblies, its ownership goes to the server, but ownership of the other assemblies remains unchanged. Unanchoring the same assembly reverts its previously set ownership. ## Setting ownership In experiences with complex physics interactions or in cases where you need to assign direct control, you can set ownership through a server-side call to `Class.BasePart:SetNetworkOwner()`. > **Info:** > > While you can manually call `Class.BasePart:SetNetworkOwner()|SetNetworkOwner(nil)` for the server for gameplay-critical objects that the client should not be able to manipulate, you should do so conservatively since it may result in jittery physics interactions for clients. Also note that the server **always** owns anchored `Class.BasePart|BaseParts` and you cannot manually change their ownership. Consider a vehicle that has a `Class.VehicleSeat` object for the driver and a `Class.Seat` object for a passenger, with both included in the vehicle assembly. With the default ownership rules, if a player character sits in the `Class.Seat` (passenger) and then another player jumps into the `Class.VehicleSeat` (driver), the **passenger** gains physical ownership of the entire vehicle because they entered first. The driver will have to wait several network cycles before their input is recognized and the vehicle will feel less responsive. The following `Class.Script` fixes this by manually assigning network ownership to the driver. In it, the `Class.VehicleSeat` sets its `Class.VehicleSeat.Occupant|Occupant` to the `Class.Humanoid` sitting on it, so the script listens for the seat's `Class.Instance.Changed|Changed` event to catch when a player sits in the seat. When the driver leaves the seat, the vehicle's network ownership is reverted to automatic with `Class.BasePart:SetNetworkOwnershipAuto()`. ```lua local Players = game:GetService("Players") local vehicleSeat = script.Parent vehicleSeat.Changed:Connect(function(prop) if prop == "Occupant" then local humanoid = vehicleSeat.Occupant if humanoid then -- Get the player from the character local player = Players:GetPlayerFromCharacter(humanoid.Parent) if player then vehicleSeat:SetNetworkOwner(player) end else -- Reset ownership when seat is unoccupied vehicleSeat:SetNetworkOwnershipAuto() end end end) ``` > **Warning:** For smooth performance and responsive behavior, ensure you also assign ownership of any loose `Class.BasePart|BaseParts` on top of a vehicle to the same client that controls the vehicle. ## Visualizing ownership To assist with network ownership debugging, Studio can render colored outlines around objects when playtesting. | Outline color | Description | | --- | --- | | `rgb(30,100,50)` | (green) | Your client owns the part and is simulating it. | | `rgb(160,0,0)` | (red) | The part is within a "buffer zone" where your client is simulating it but it's still owned by something else. Your client might get ownership after this, or it may reject. | | `rgb(220,220,220)`
`rgb(140,140,140)` | (white/grey) | Server or another client owns the part through either automatic network ownership or from explicit assignment through `Class.BasePart:SetNetworkOwner()\|part:SetNetworkOwner()`. | | `rgb(0,0,0)` | (black) | The part is owned by neither the server nor the client. Physics will not apply to this part. | > **Info:** Note that in a [multi-client simulation](/docs/en-us/studio/testing-modes.md#multi-client-simulation), each client is assigned a unique color to indicate their ownership; this is mirrored in the [Server](/docs/en-us/studio/testing-modes.md#toggle-clientserver) view, helping you determine which client owns which part(s) at any given time. To enable network ownership visualization: 1. Click on the **visualization options** button in the upper-right corner of the 3D viewport.![A close up view of the 3D viewport with the Visualization Options button indicated in the upper-right corner.](../assets/studio/general/Visualization-Options.png) 2. In the dropdown menu, toggle on **Network owners**. ## Security concerns Roblox cannot verify physics calculations when a client has ownership over a `Class.BasePart`. Clients can exploit this and send bad data to the server, such as teleporting the `Class.BasePart`, making it go through walls or fly around. Additionally, `Class.BasePart.Touched` events are tied to network ownership, meaning that a client can fire `Class.BasePart.Touched|Touched` events on a `Class.BasePart` it owns and send it to the server, even if the server doesn't see it touch anything. For example, a client can make a sword deal damage to another player across the map by firing the event through script injections, so it's important to check the validity of such events fired by clients. See [Security Tactics and Cheat Mitigation](/docs/en-us/scripting/security/security-tactics.md) for detailed security tactics and cheat mitigation tactics for Roblox experiences.
--- title: "Sleep system" url: /docs/en-us/physics/sleep-system last_updated: 2026-06-29T19:34:03Z description: "Explore how the sleep system automatically prevents unnecessary simulation of non-moving parts." --- # Sleep system Each [assembly](/docs/en-us/physics/assemblies.md) in the Roblox Engine corresponds to a single **rigid body**. The position and velocity of each rigid body describe where it's located and how fast it's moving, and one of the primary engine tasks is to update the positions and velocities of each assembly. Assemblies can be connected together with [mechanical constraints](/docs/en-us/physics/mechanical-constraints.md) and [mover constraints](/docs/en-us/physics/mover-constraints.md) to form mechanisms such as cars or airplanes. As the number of assemblies and constraints in a mechanism increases, the time required to simulate the mechanism also increases. Fortunately, this increase is offset when the **sleep system** determines that the engine can skip simulation of non‑moving assemblies. ## Sleep states Each assembly can be in one of three states: [awake](#awake), [sleeping](#sleeping), or [sleep‑checking](#sleep-checking). ### Awake An **awake** assembly is moving or accelerating, and is therefore simulated. Assemblies enter this state from situations outlined in [sleeping](#sleeping) and [sleep‑checking](#sleep-checking), as well as [additional wake situations](#additional-wake-situations). ### Sleeping A **sleeping** assembly is neither moving nor accelerating, and is therefore not simulated. An assembly is determined to be non-moving by checking its **linear velocity** and **rotational velocity**. If its linear velocity is less than the [Linear Velocity](#threshold-reference) threshold and its rotational velocity is less than the [Rotational Velocity](#threshold-reference) threshold, the assembly is considered to be non-moving. In some cases, simply checking for non-movement would cause an assembly to incorrectly enter the sleeping state. For example, if a ball is thrown straight up, its velocity approaches zero as it reaches its maximum height, making it a candidate to sleep and never fall back down. To handle such cases, the engine considers an assembly to be accelerating if its **linear acceleration** or **rotational acceleration** are greater than the [Linear Acceleration](#threshold-reference) and [Rotational Acceleration](#threshold-reference) thresholds, respectively, and will prevent the assembly from falling asleep. > **Info:** If an assembly falls asleep when you expect it to remain awake, it's commonly because the assembly is moving too slowly. In addition to automatic checks outlined in [sleep‑checking](#sleep-checking) and [sleeping](#sleeping), you can forcibly wake up an assembly through the conditions outlined in [additional wake situations](#additional-wake-situations). ### Sleep-checking A non-moving assembly that shares a constraint with at least one [awake](#awake) neighboring assembly is put into the **sleep-checking** state and is not simulated. On each worldstep, a sleep-checking assembly checks whether: - Its **linear acceleration** is greater than the [Wake Linear Acceleration](#threshold-reference) threshold. - Its **rotational acceleration** is greater than the [Wake Rotational Acceleration](#threshold-reference) threshold. - A neighboring assembly's **linear velocity** is greater than the [Neighbor Linear Velocity](#threshold-reference) threshold. - A neighboring assembly's **rotational velocity** is greater than the [Neighbor Rotational Velocity](#threshold-reference) threshold. If any of these conditions is true, or under any of the [additional wake situations](#additional-wake-situations), the sleep-checking assembly enters the [awake](#awake) state. ## Threshold reference The following table provides the various velocity and acceleration thresholds used to determine if an assembly is moving or accelerating. | Threshold | Value | State change | | --- | --- | --- | | Linear Velocity | 0.33 studs/s | [awake](#awake) ⟩ [sleeping](#sleeping) | | Rotational Velocity | 0.42 studs/s | [awake](#awake) ⟩ [sleeping](#sleeping) | | Linear Acceleration | 0.24 studs/s² | [awake](#awake) ⟩ [sleeping](#sleeping) | | Rotational Acceleration | 0.24 studs/s² | [awake](#awake) ⟩ [sleeping](#sleeping) | | Neighbor Linear Velocity | 0.48 studs/s | [sleep-checking](#sleep-checking) ⟩ [awake](#awake) | | Neighbor Rotational Velocity | 0.59 studs/s | [sleep-checking](#sleep-checking) ⟩ [awake](#awake) | | Wake Linear Acceleration | 16.9 studs/s² | [awake](#awake) ⟩ [sleeping](#sleeping) | | Wake Rotational Acceleration | 16.9 studs/s² | [awake](#awake) ⟩ [sleeping](#sleeping) | Rotational velocity and acceleration thresholds reflect the velocity and acceleration of a point located on the assembly bounding sphere (a sphere that contains all of the parts in an assembly) that moves rigidly with the assembly. > **Info:** For a given angular velocity, the rotational velocity of an assembly is proportional to the assembly bounding sphere radius. This means that larger assemblies can rotate at a lower angular velocity without falling asleep. ## Actuated joints Sometimes developers want to move parts very slowly using an **actuated joint**, a constraint that applies force or motion to drive connected parts, such as a `Class.HingeConstraint` using a `Enum.ActuatorType.Motor|Motor`. To support these scenarios, assemblies connected to actuated joints use a stricter sleep velocity threshold. This delays when those assemblies fall asleep, allowing slow, controlled motion without the physics system prematurely putting them to sleep. The following are considered actuated joints: | Constraint | Configuration | Required nonzero properties | | --- | --- | --- | | `Class.AlignOrientation\|AlignOrientation` | `Class.AlignOrientation.RigidityEnabled\|RigidityEnabled` = `false` | | | `Class.AlignPosition\|AlignPosition` | `Class.AlignPosition.RigidityEnabled\|RigidityEnabled` = `false` | | | `Class.AngularVelocity\|AngularVelocity` | — | | | `Class.AnimationConstraint\|AnimationConstraint` | `Class.AnimationConstraint.IsKinematic\|IsKinematic` = `false` | | | `Class.CylindricalConstraint\|CylindricalConstraint` | `Class.CylindricalConstraint.ActuatorType\|ActuatorType` = `Enum.ActuatorType.Motor\|Motor` | | | `Class.CylindricalConstraint.ActuatorType\|ActuatorType` = `Enum.ActuatorType.Servo\|Servo` | | | `Class.CylindricalConstraint\|CylindricalConstraint` | `Class.CylindricalConstraint.AngularActuatorType\|AngularActuatorType` = `Enum.ActuatorType.Motor\|Motor` | | | `Class.CylindricalConstraint.AngularActuatorType\|AngularActuatorType` = `Enum.ActuatorType.Servo\|Servo` | | | `Class.HingeConstraint\|HingeConstraint` | `Class.HingeConstraint.ActuatorType\|ActuatorType` = `Enum.ActuatorType.Motor\|Motor` | | | `Class.HingeConstraint.ActuatorType\|ActuatorType` = `Enum.ActuatorType.Servo\|Servo` | | | `Class.LinearVelocity\|LinearVelocity` | `Class.LinearVelocity.VelocityConstraintMode\|VelocityConstraintMode` = `Enum.VelocityConstraintMode.Line\|Line` | | | `Class.LinearVelocity.VelocityConstraintMode\|VelocityConstraintMode` = `Enum.VelocityConstraintMode.Plane\|Plane` | | | `Class.LinearVelocity.VelocityConstraintMode\|VelocityConstraintMode` = `Enum.VelocityConstraintMode.Vector\|Vector` | | | `Class.PrismaticConstraint\|PrismaticConstraint` | `Class.PrismaticConstraint.ActuatorType\|ActuatorType` = `Enum.ActuatorType.Motor\|Motor` | | | `Class.PrismaticConstraint.ActuatorType\|ActuatorType` = `Enum.ActuatorType.Servo\|Servo` | | | `Class.RopeConstraint\|RopeConstraint` | `Class.RopeConstraint.WinchEnabled\|WinchEnabled` = `true` | | ## Additional wake situations In addition to situations outlined in [sleep‑checking](#sleep-checking) and [sleeping](#sleeping), an assembly enters the [awake](#awake) state when: - It collides with another assembly moving faster than 1 studs/s. - Any physics-related property of any `Class.BasePart` within the assembly changes, including: - `Class.BasePart.Anchored|Anchored` - `Class.BasePart.AssemblyLinearVelocity|AssemblyLinearVelocity`/`Class.BasePart.AssemblyAngularVelocity|AssemblyAngularVelocity` - `Class.BasePart.CanCollide|CanCollide`/`Class.BasePart.CanTouch|CanTouch` - `Class.BasePart.CustomPhysicalProperties|CustomPhysicalProperties` - `Class.BasePart.EnableFluidForces|EnableFluidForces` - `Class.BasePart.Massless|Massless` - `Class.BasePart.RootPriority|RootPriority` - A non-zero impulse is applied to any `Class.BasePart` within the assembly via `Class.BasePart:ApplyImpulse()|ApplyImpulse()`, `Class.BasePart:ApplyImpulseAtPosition()|ApplyImpulseAtPosition()`, or `Class.BasePart:ApplyAngularImpulse()|ApplyAngularImpulse()`. - Any physics-related property changes on the `Class.Workspace` that would affect the assembly, including: - `Class.Workspace.Gravity|Gravity` - `Class.Workspace.FluidForces|FluidForces` - `Class.Workspace.GlobalWind|GlobalWind` - `Class.Workspace.AirDensity|AirDensity` - A new `Class.Constraint` is created with an `Class.Attachment` that is parented to a `Class.BasePart` within the assembly. - Any property changes for a `Class.Constraint` with an `Class.Attachment` that is parented to a `Class.BasePart` within the assembly. - The `Class.Motor.CurrentAngle|CurrentAngle` property changes for a `Class.Motor` that is connected to a `Class.BasePart` within the assembly. - The assembly contains a `Class.VehicleSeat` with a seated player character. - The assembly is within the `Class.Explosion.BlastRadius|BlastRadius` of an `Class.Explosion`. ## Debug visualization During playtesting, you can visualize assembly sleep states by toggling on **Awake parts** from the [Visualization Options](/docs/en-us/studio/ui-overview.md#visualization-options) widget in the upper‑right corner of the 3D viewport. ![A close up view of the 3D viewport with the Visualization Options button indicated in the upper-right corner.](../assets/studio/general/Visualization-Options.png) Once enabled, simulated parts will be outlined by their current sleep state, with [awake](#awake) parts outlined in red, [sleep‑checking](#sleep-checking) parts outlined in orange, and [sleeping](#sleeping) parts un‑outlined. _Simulated parts outlined by the color representing their current sleep state_ --- title: "Roblox units" url: /docs/en-us/physics/units last_updated: 2026-06-29T19:34:03Z description: "Explore the physical units used in Roblox and how they convert to metric units." --- # Roblox units This article outlines Roblox physical units and how they convert to metric units. Understanding units is useful whenever you work with physics, as in the following examples: - Customizing your experience's gravity, jump height/power, and walk speed in the **World** tab of Studio's **File** ⟩ **Experience Settings** window. - Tuning linear/angular velocities, forces, torques, stiffness, and damping of [mechanical constraints](/docs/en-us/physics/mechanical-constraints.md) and [mover constraints](/docs/en-us/physics/mover-constraints.md). - Adjusting the density of [custom materials](/docs/en-us/parts/materials.md#custom-materials). ## Unit conversions #### Primary Units In general, you can use the conversions in the following table to relate Roblox's primary units for time, length, and mass to their metric counterparts. | Unit | Roblox | Metric | | --- | --- | --- | | **Time** | 1 second | 1 second | | **Length** | 1 stud | 28 cm | | **Mass** | 1 RMU* | 21.952 kg | _* RMU = Roblox Mass Unit_ #### Derived units The primary units are used to generate conversions for **derived** units such as water density and air pressure at standard conditions. The following physical properties are expressed in metric units and Roblox units, with primary unit equivalents provided in brackets. All conversions have been rounded to three significant figures. | Unit | Metric | Roblox | | --- | --- | --- | | **Water Density** | 1 g/cm³ | 1 RMU/stud³ | | **Air Density** (sea level) | 0.00129 g/cm³ | 0.00129 RMU/stud³ | | **1 atmosphere** | 101,325 Pa _[kg/(m s²)]_ | 1290 RMU/(stud s²) | | **Spring Stiffness** | 1 N/m _[kg/s²]_ | 0.0456 RMU/s² | | **Spring Damping** | 1 N s/m _[kg/s]_ | 0.0456 RMU/s | | **Velocity** | 1 m/s | 3.57 studs/s | | **Force** | 1 N _[kg m/s²]_ | 0.163 Rowtons _[RMU stud/s²]_ | | **Torque** | 1 N-m _[kg m²/s²]_ | 0.581 Rowton-studs _[RMU stud²/s²]_ | #### Gravity The following table illustrates gravitational acceleration in Roblox units and metric units for presets in the **World** tab of Studio's **File** ⟩ **Experience Settings** window. | Preset | Roblox | Metric | | --- | --- | --- | | **Classic** (default) | 196.2 studs/s² | 54.936 m/s² | | **Realistic** (real-world) | 35 studs/s² | 9.8 m/s² | | **Action** | 75 studs/s² | 21 m/s² | #### Physical limits Roblox places limits on certain physical properties, as outlined in the following table. Details on these properties is located in the `Datatype.PhysicalProperties` documentation. | Property | Minimum | Maximum | | --- | --- | --- | | **Density** (RMU/stud³) | 0.0001 | 100 | | **Friction** | 0.0 | 2.0 | | **Friction Weight** | 0.0 | 100 | | **Elasticity** | 0.0 | 1.0 | | **Elasticity Weight** | 0.0 | 100 | ## Importance of unit consistency Internally, the Roblox physics engine does not use unit conversions. You're free to define your own unit interpretations for studs (length) and RMUs (mass), but these should be used in a consistent manner throughout the experience. For example, if you decide that one stud equals one foot (30.483 cm), the unit density of water implies an RMU is equal to 62.4 lbs (28.3 kg): > 1 (g/cm³) × (30.48³ cm³/ft³) = 28,317 (g/ft³) × (0.00220462 lbs/g) = 62.4 (lbs/ft³) = 1 (RMU/stud³) Overall, it's recommended that you use standard Roblox units because it makes an experience work as expected in all scenarios, such as compatibility with VR controls. --- title: "The social 3D creation platform" url: /docs/en-us/platform last_updated: 2026-06-29T19:34:04Z description: "Explains the things you can create on Roblox." --- # The social 3D creation platform Roblox lets you create anything you can imagine with a comprehensive set of powerful 3D collaborative creation tools. Our platform provides you with unparalleled opportunities to create content from simple avatar items to immersive, multiplayer experiences. [Watch overview](https://www.youtube.com/watch?v=vIiVbFiDbBE) # All the tools you need, for free Roblox simplifies creation by providing free tools, hosting, and infrastructure that power automatic, synchronous real-time gameplay as well as covering storage, localization, and payment processing. ### Roblox Studio The all-in-one IDE with generative AI technology for building, scripting, testing, and publishing Roblox experiences. [Get Studio](/docs/en-us/studio/setup.md) [Learn more](/docs/en-us/studio.md) ### Roblox Engine An advanced 3D engine with access to robust data storage, physics simulation, and standard primitives to build anything you can imagine. [API reference](/docs/en-us/reference/engine.md) [Guides](/docs/en-us/creation.md) ### Open Cloud A set of web-based APIs that let you build tools and apps to access your creations. [API reference](/docs/en-us/cloud.md) [Guides](/docs/en-us/cloud/guides.md) ### Creator Hub Manage your creations, view analytics, track earnings, and connect with the community, all from your web browser. [Go to Creator Hub](https://create.roblox.com/) # Large, multiplayer experiences on any device Experiences are the 3D worlds you create, including games, communication or learning environments, visual showcases, concerts, real life simulations, and more. [Get started](/docs/en-us/experiences.md) [Engine API](/docs/en-us/reference/engine.md) ### Simulated by default Construct 3D worlds that closely simulate real life with out-of-the-box objects and services. The Roblox engine provides default behavior and logic that do the heavy lifting. ### Infinitely customizable Almost everything in Roblox is customizable and dynamic, letting you override default properties in the engine at build time or with scripts at runtime. ### Intelligent client streaming The Roblox engine adjusts how it delivers your experiences to clients based on available resources, so anything you create runs on all devices automatically. # Expressive avatars for everyone Avatars persist across all Roblox experiences by default, allowing users to create unique characters and outfits to take with them wherever they go. Create anything from clothing, accessories, animations, and even complete characters. [Get started](/docs/en-us/avatar.md) [3D art resources](/docs/en-us/art.md) ### Fully customizable characters Create full characters or individual body parts that users can equip. [Avatar characters](/docs/en-us/avatar/character-bodies.md) ### Expressive body and face animations Animations let avatars move with their body and face. [Facial animation](/docs/en-us/avatar/dynamic-heads.md) [Character animation](/docs/en-us/tutorials/use-case-tutorials/animation/create-an-animation.md) ### Layered clothing Clothing can layer on top of each other on a multitude of different body shapes and types. [Guides](/docs/en-us/avatar/layered-accessories.md) [Tutorial](/docs/en-us/art/accessories/creating.md) # Cloud-based asset management From 3D models to audio to plugins, create and share assets with the rest of the creator community to accelerate their productivity. [Learn more](/docs/en-us/assets.md) [Go to Creator Store](https://create.roblox.com/store) ### Publish and share From 3D models to audio to plugins, create and share assets with the rest of the creator community to accelerate their productivity. [Guides](/docs/en-us/projects/assets.md) ### Sell plugins The Creator Store lets you publish and sell plugins that increase Studio's functionality and feature set. [Create and sell plugins](/docs/en-us/studio/plugins.md) ### Import assets Import assets from other digital creation apps for 3D art, images, audio, and video. [Third-party tools support](/docs/en-us/art/overview-dcc.md) # Extensive scale and monetization Reach a massive, global audience of hundreds of millions of users across 180 countries with efficient discovery and user acquisition. [Learn how to scale](/docs/en-us/scale.md) [Learn how to monetize](/docs/en-us/monetize.md) ### Reach hundreds of millions instantly Deploy globally in seconds across major platforms — from mobile to console to desktop to VR — and in multiple languages. ### Analyze and grow your creations Measure and gain insights on your experience's performance with a robust set of analytics tools. Adjust content strategies and rapidly iterate to get your desired outcome. ### Monetize with a variety of strategies Earn money in many ways, including in-experience purchases, ads, as well as selling avatar items or creator plugins. --- title: "Avatar Context Menu" url: /docs/en-us/players/avatar-context-menu last_updated: 2026-06-29T19:34:04Z description: "The Avatar Context Menu (ACM) allows users to interact and customize actions with others in their experience." --- # Avatar Context Menu The **Avatar Context Menu** (ACM) makes it easier for users to interact with each other. When the ACM is enabled in your experience, a user can walk up to another user's character and click on them to open a small menu with several default options. The player can send a friend request, begin a private chat, view the user's profile, or wave. After [enabling](#enable-the-avatar-context-menu) the ACM in your experience, you can customize the ACM in the following ways: - Programmatically [open and close](#open-and-close-the-acm) the ACM for specific users. - [Add](#add-menu-options) custom options and [remove](#remove-menu-options) existing options to the ACM. - [Customize the ACM appearance](#customize-menu-appearance) to create a unique user interface. ## Enable the Avatar Context Menu The Avatar Context Menu must be enabled using the `Class.StarterGui:SetCore()` option "AvatarContextMenuEnabled" in a `Class.LocalScript`. The ACM is best used in experiences where there is no predefined behavior for clicking on other users. Use the following code sample to enable the ACM in a `Class.LocalScript`: ```lua -- Run in a LocalScript, ideally within "StarterPlayerScripts" local StarterGui = game:GetService("StarterGui") StarterGui:SetCore("AvatarContextMenuEnabled", true) ``` If you need to detect whether the ACM is enabled, you can use the following code to return a boolean on the current status of the ACM: ```lua ---Returns a boolean indicating if the ACM is currently enabled. StarterGui:GetCore(AvatarContextMenuEnabled) ``` ## Open and close the ACM Once enabled, you can open and close the ACM programmatically with `Class.StarterGui`. To programmatically open the ACM, use the following code: ```lua -- Use StarterGui:SetCore where targetPlayer is a valid Player object StarterGui:SetCore("AvatarContextMenuTarget", targetPlayer) ``` To programmatically close the ACM, use the following code: ```lua StarterGui:SetCore("AvatarContextMenuTarget", nil) ``` ## Menu options You can [add](#add-menu-options) and [remove](#remove-menu-options) actions through scripting. By default, the menu has the following options: | Menu option | Description | | --- | --- | | Friend | Sends a friend request to the selected user. | | Chat | Opens a private conversation with the selected user in the in-experience chat. | | View | Opens a window to inspect the selected user's appearance. | | Wave | Initiates a wave animation to the selected user. | Once the ACM is open, the user can scroll and select other users on the character selection carousel. Characters are sorted based on distance to the selected character. The menu only updates when opened and the list of available users will repeat when scrolling. ### Add menu options Once enabled, experience-specific actions can be added to the ACM. For example, an experience might allow for trade requests, add-to-party options, or other special interactions. The following example shows how to add a custom action to the Avatar Context Menu: ```lua local Players = game:GetService("Players") local StarterGui = game:GetService("StarterGui") local player = Players.LocalPlayer -- Connect a function to a "BindableEvent" local bindableEvent = Instance.new("BindableEvent") local function onCustomACMAction(targetPlayer) -- At this point, you could call InvokeServer() on a RemoteFunction to alert the server of the selection print("ACM event selected by " .. player.Name .. " on " .. targetPlayer.Name) end bindableEvent.Event:Connect(onCustomACMAction) -- Add the ACM option using SetCore() with "AddAvatarContextMenuOption" local options = {"Custom ACM Action", bindableEvent} StarterGui:SetCore("AddAvatarContextMenuOption", options) ``` ### Remove menu options You can remove custom and the default Add Friend, Chat, View, and Wave options from the ACM by referencing the custom Action name or the default `Enum.AvatarContextMenuOption` enum. Use the following code to remove a default and custom menu option: ```lua -- Remove the "Custom ACM Action" option StarterGui:SetCore("RemoveAvatarContextMenuOption", "Custom ACM Action") -- Remove the default "Friend" option by referencing the AvatarContextMenuOption.Friend Enum StarterGui:SetCore("RemoveAvatarContextMenuOption", Enum.AvatarContextMenuOption.Friend) ``` > **Warning:** You can not make edits to the ACM while it is open. Your code should verify that an action is still relevant in case the action was removed from the menu. For example, if a marketplace area adds an option to send trade requests, you should verify both users are still in the marketplace. ## Customize menu appearance To change the appearance of the Avatar Context Menu, call `Class.StarterGui:SetCore()` with the option "AvatarContextMenuTheme", providing a table of parameters and values to adjust the menu appearance. The ACM user interface includes the following sections: A. Name Tag: The user name of the character being interacted with. B. Button Frame: Contains all of the ACM buttons. C. Buttons: Individual buttons for the default or custom ACM actions. ### Appearance parameters These are the customization parameters with the ACM: #### Background | BackgroundColor | A `Datatype.Color3` for the overall background of the ACM (most useful when not using a background image). | | --- | --- | | BackgroundTransparency | Transparency value (0–1) for the overall background of the ACM (most useful when not using a background image). | | BackgroundImage | A valid asset ID of an image for the ACM background. | | BackgroundImageTransparency | Transparency value (0–1) for the background image. | | BackgroundImageScaleType | A `Enum.ScaleType` enum for background image scaling. | | BackgroundImageSliceCenter | A `Datatype.Rect` specifying the center of a nine-slice image when BackgroundImageScaleType is set to `Enum.ScaleType.Slice`. | #### Name tag | NameTagColor | A `Datatype.Color3` for the bar showing which player is being interacted with. | | --- | --- | | NameUnderlineColor | A `Datatype.Color3` for the thin line between the name tag and action buttons. | #### Button frame | ButtonFrameColor | A `Datatype.Color3` for the section (frame) containing the action buttons. | | --- | --- | | ButtonFrameTransparency | Transparency value (0–1) for the button frame section. | #### Button | ButtonColor | A `Datatype.Color3` for the background of the ACM action buttons. | | --- | --- | | ButtonTransparency | Transparency value (0–1) for the background color of the action buttons. | | ButtonHoverColor | A `Datatype.Color3` for the "hover" state of the action buttons. | | ButtonHoverTransparency | Transparency value (0–1) for the "hover" color of the action buttons. | | ButtonUnderlineColor | A `Datatype.Color3` for the thin line which separates each action button. | | ButtonImage | A valid asset ID of an image for the background of buttons. | | ButtonImageScaleType | A `Enum.ScaleType` enum for button image scaling. | | ButtonImageSliceCenter | A `Datatype.Rect` specifying the center of a nine-slice image when ButtonImageScaleType is set to `Enum.ScaleType.Slice`. | #### Text | Font | A `Enum.Font` enum value for the name tag and button text. | | --- | --- | | TextColor | A `Datatype.Color3` for all text within the ACM. | | TextScale | A float value to scale the default text sizes of each element. | #### Various image | LeaveMenuImage | An asset ID of an image for the ACM close button. | | --- | --- | | ScrollLeftImage | An asset ID of an image for the carousel "scroll left" button. | | ScrollRightImage | A valid asset ID of an image for the carousel "scroll right" button. | #### Selected character | SelectedCharacterIndicator | The `Class.MeshPart` which floats above a character's head to indicate they are selected. | | --- | --- | #### Size and position | Size | A `Datatype.UDim2` for the overall size of the ACM. | | --- | --- | | MinSize | A `Datatype.Vector2` specifying the minimum size of the ACM. | | MaxSize | A `Datatype.Vector2` specifying the maximum size of the ACM. | | AspectRatio | A float value specifying the relative width and height of the ACM. | | AnchorPoint | The `Class.GuiObject.AnchorPoint\|AnchorPoint` of the ACM. | | OnScreenPosition | A `Datatype.UDim2` specifying the on-screen position of the ACM (the position where it tweens to when opened). | | OffScreenPosition | A `Datatype.UDim2` specifying the off-screen position of the ACM (the position where it tweens from/to when opened/closed). | ### Example customization The following code sample customizes the ACM theme using some basic parameters: ```lua local StarterGui = game:GetService("StarterGui") StarterGui:SetCore("AvatarContextMenuTheme", { BackgroundImage = "", BackgroundTransparency = 0.5, BackgroundColor = Color3.fromRGB(111, 145, 242), NameTagColor = Color3.fromRGB(0, 0, 200), NameUnderlineColor = Color3.fromRGB(213, 233, 255), ButtonFrameColor = Color3.fromRGB(15, 24, 65), ButtonFrameTransparency = 0.2, ButtonUnderlineColor = Color3.fromRGB(213, 233, 255), Font = Enum.Font.SciFi }) ``` --- title: "Avatar Editor Service" url: /docs/en-us/players/avatar-editor last_updated: 2026-06-29T19:34:04Z description: "The Avatar Editor Service allows access to a user's avatar and inventory and Marketplace." --- # Avatar Editor Service The Avatar Editor Service lets you access and make changes to a user's avatar within an experience. The Avatar Editor Service can also access a user's inventory and the [Marketplace](https://www.roblox.com/catalog) to save outfits and purchase avatar items to the user's account. We recommend implementing the Avatar Editor Service with an in-game avatar editor for a complete character customization experience. See the [Simple Avatar Editor Demo](https://www.roblox.com/games/9376329300/Simple-Avatar-Editor-Demo) reference place for an example of this feature. To begin using the Avatar Editor Service, you must first [request access](#request-access) to the user's inventory. After access is successfully granted, you can perform the following actions: - [Read user's inventory](#read-user-inventory) to get a list of items owned by the user. - [Search the Marketplace](#search-the-marketplace), using a variety of properties to filter and sort. - [Equip avatar items and save outfits](#save-avatars-and-outfits) to the user's avatar. - [Prompt the user to purchase](#purchase-items) an Marketplace item. ## Request access To begin accessing a user's inventory, you need to prompt the user to allow access through `Class.AvatarEditorService:PromptAllowInventoryReadAccess()|PromptAllowInventoryReadAccess()`. You need to perform this request once per session. Use the following code sample to initiate the access prompt and listen for the user response: ```lua local AvatarEditorService = game:GetService("AvatarEditorService") AvatarEditorService:PromptAllowInventoryReadAccess() local result = AvatarEditorService.PromptAllowInventoryReadAccessCompleted:Wait() if result == Enum.AvatarPromptResult.Success then -- Access granted! end ``` The user receives the following prompt: Once the user accepts the prompt, the `Class.AvatarEditorService` can begin accessing the user's inventory. ## Read user inventory Once access is granted by the user, you can read their inventory with the `Class.AvatarEditorService:GetInventory()|GetInventory()` function, supplying an array of `Enum.AvatarAssetType|AvatarAssetTypes` to filter by. This function returns an `Class.InventoryPages` object containing the user owned items. Use the following code sample to print a list of specific accessories in a user's inventory: ```lua local AvatarEditorService = game:GetService("AvatarEditorService") AvatarEditorService:PromptAllowInventoryReadAccess() local result = AvatarEditorService.PromptAllowInventoryReadAccessCompleted:Wait() if result == Enum.AvatarPromptResult.Success then -- Access granted! local assetTypes = { Enum.AvatarAssetType.BackAccessory, Enum.AvatarAssetType.ShoulderAccessory, Enum.AvatarAssetType.WaistAccessory } local pagesObject = AvatarEditorService:GetInventory(assetTypes) local currentPage = pagesObject:GetCurrentPage() for _, item in currentPage do print(item) end end ``` ## Search the Marketplace `Class.AvatarEditorService` includes functions and events which let you search the Roblox catalog. To search, supply your query with a `Datatype.CatalogSearchParams` object that includes one or more of the following properties: | Property | Description | | --- | --- | | AssetTypes | An array of `Enum.AvatarAssetType` such as Enum.AvatarAssetType.BackAccessory. | | BundleTypes | An array of `Enum.BundleType` such as Enum.BundleType.BodyParts. | | CategoryFilter | A `Enum.CatalogCategoryFilter` describing the various catalog categories like "Featured" or "Community Creations". By default this is set to `Enum.CatalogCategoryFilter.None` | | MaxPrice | An integer describing the maximum price to filter. | | MinPrice | An integer describing the minimum price to filter. By default, MinPrice is **0**. | | SearchKeyword | A string to query against item descriptions in the catalog. | | SortType | A `Enum.CatalogSortType` that describes how the results are ordered. By default this is set to `Enum.CatalogSortType.Relevance`. | | IncludeOffSale | A boolean describing whether the results of the search include off sale items. By default this is set to false. | | CreatorId | An integer to specify a given creator. You can use either a UserId or a GroupId. | | CreatorName | A string used to search by items created by a given creator. You can use either a User Name or a Group Name. | The following code sample constructs a `Datatype.CatalogSearchParams` object for **Back** and **Shoulder** asset types, and passes that through a `Class.AvatarEditorService:SearchCatalog()|SearchCatalog()` call: ```lua local AvatarEditorService = game:GetService("AvatarEditorService") local catalogSearchParams = CatalogSearchParams.new() local assetTypes = { Enum.AvatarAssetType.BackAccessory, Enum.AvatarAssetType.ShoulderAccessory } catalogSearchParams.AssetTypes = assetTypes local pagesObject = --This function returns a CatalogPages object containing the results. AvatarEditorService:SearchCatalog(catalogSearchParams) local currentPage = pagesObject:GetCurrentPage() for _, item in currentPage do print(item) end ``` ## Save avatars and outfits When used alongside an in-game avatar editor, `Class.AvatarEditorService` can save and update avatar items and outfits to the Roblox platform. Users don't receive catalog items they don't own when saving an avatar or outfit. Any `Class.HumanoidDescription` can be saved to the user's current avatar with `Class.AvatarEditorService:PromptSaveAvatar()|PromptSaveAvatar()`. This may include: - Pre-defined avatar configurations that you've built using existing catalog items. - Any configuration that the user has chosen through an in-game avatar editor. Since `Class.AvatarEditorService:PromptSaveAvatar()` does not yield, you can get the result by listening to the `Class.AvatarEditorService.PromptSaveAvatarCompleted` event. The following code will save a current `Class.HumanoidDescription` using `Class.AvatarEditorService:PromptSaveAvatar()|PromptSaveAvatar()` and checks for a successful `Class.AvatarEditorService.PromptSaveAvatarCompleted` event: ```lua local AvatarEditorService = game:GetService("AvatarEditorService") local Players = game:GetService("Players") local player = Players.LocalPlayer local character = player.Character or player.CharacterAdded:Wait() local humanoid = character:WaitForChild("Humanoid") local currentDescription = humanoid:GetAppliedDescription() AvatarEditorService:PromptSaveAvatar(currentDescription, humanoid.RigType) local result = AvatarEditorService.PromptSaveAvatarCompleted:Wait() if result == Enum.AvatarPromptResult.Success then -- Avatar saved! end ``` To save any `Class.HumanoidDescription` as an outfit (without overwriting the user's current avatar), use `Class.AvatarEditorService:PromptCreateOutfit()`. Once called, you can get the result of `Class.AvatarEditorService:PromptCreateOutfit()` by listening to the `Class.AvatarEditorService.PromptCreateOutfitCompleted` event. The following code sample creates an outfit with `Class.AvatarEditorService:PromptCreateOutfit()` and listens for a successful `Class.AvatarEditorService.PromptCreateOutfitCompleted` event: ```lua local AvatarEditorService = game:GetService("AvatarEditorService") local Players = game:GetService("Players") local player = Players.LocalPlayer local character = player.Character or player.CharacterAdded:Wait() local humanoid = character:WaitForChild("Humanoid") local currentDescription = humanoid:GetAppliedDescription() AvatarEditorService:PromptCreateOutfit(currentDescription, humanoid.RigType) local result = AvatarEditorService.PromptCreateOutfitCompleted:Wait() if result == Enum.AvatarPromptResult.Success then -- Outfit saved! end ``` ## Purchase items When saving either an avatar or an outfit that uses catalog items, the user doesn't receive any items that they do not own. Before saving an avatar or outfit, check if the user owns the asset with `Class.MarketplaceService:PlayerOwnsAsset()` and provide them with an option to purchase the item with `Class.MarketplaceService:PromptPurchase()`. If you don't wish to implement item purchases, you can instead allow users to favorite non-owned items with `Class.AvatarEditorService:PromptSetFavorite()`. > **Info:** Be aware that you can't earn Robux [commissions](/docs/en-us/marketplace/marketplace-fees-and-commissions.md#commissions) if the user decides to purchase those items outside of your experience. --- title: "Avatar Inspect Menu" url: /docs/en-us/players/avatar-inspect-menu last_updated: 2026-06-29T19:34:04Z description: "The Avatar Inspect Menu allows users to view other users' avatars, try on items, make purchases." --- # Avatar Inspect Menu The **Avatar Inspect Menu** allows users to view another user's Roblox avatar character, try on items, and even make purchases within an experience. Roblox enables this menu by default and users in your experience can access this menu in three ways: - Opening the experience's main menu and clicking the **View** button next to a user in the **Players** tab. - Clicking on a user's name in the player list. - Selecting the Inspect option in the [Avatar Context Menu](/docs/en-us/players/avatar-context-menu.md), an opt-in feature which provides additional user-to-user social interaction. You can customize a user's **Avatar Inspect Menu** in the following ways: - Change the inspect target from the user's Roblox avatar to their [current in-experience appearance](#inspect-currently-equipped-items). - Change the inspect target to a [specific User ID](#inspect-specific-users), even if they are not in the experience. For more specific catalog access, you can also use the [Avatar Editor Service](/docs/en-us/players/avatar-editor.md) to access and make changes to a user's platform-wide avatar within an experience. ## Inspect currently equipped items By default, the Inspect Menu shows the same information as the user's Roblox Avatar profile page. The avatar outfit may not match the user's current appearance, since you may have opted to equip different accessories or avatar items at that specific moment. In the cases where the default Inspect Menu may not be accurate to the current character's outfit, you can inspect a character's current outfit with the following steps: 1. Disable the default profile-based Inspect Menu by setting `Class.GuiService:SetInspectMenuEnabled()` with a value of **false**. 2. Get a current `Class.HumanoidDescription` from the target player character. 3. Call `Class.GuiService:InspectPlayerFromHumanoidDescription()` to load the Inspect menu based off of the `Class.HumanoidDescription`. Use the following code sample to inspect the currently equipped items of a specific player: ```lua local GuiService = game:GetService("GuiService") local Players = game:GetService("Players") local player = Players.LocalPlayer -- Make profile-based Inspect Menu inaccessible GuiService:SetInspectMenuEnabled(false) local humanoid = player.Character and player.Character:FindFirstChildWhichIsA("Humanoid") if humanoid then -- Get current HumanoidDescription from a player character local humanoidDescription = humanoid:GetAppliedDescription() -- Load the inspect menu from a humanoid description GuiService:InspectPlayerFromHumanoidDescription(humanoidDescription, player.Name) end ``` ## Inspect specific users The Avatar Inspect Menu can inspect players who aren't in the current experience. You can use `Class.GuiService:InspectPlayerFromUserId()` to inspect a player from any `Class.Player.UserId`. Use the following code sample to open the Avatar Inspect Menu based on a `Class.Player.UserId|UserId`: ```lua local GuiService = game:GetService("GuiService") local Players = game:GetService("Players") -- Get user ID by username local success, userId = pcall(function() return Players:GetUserIdFromNameAsync("RobloxUser") end) if success then GuiService:InspectPlayerFromUserId(userId) end ``` --- title: "Disable default UI" url: /docs/en-us/players/disable-ui last_updated: 2026-06-29T19:34:04Z description: "Explains the process of disabling Roblox's default user interface elements." --- # Disable default UI All Roblox experiences include several UI elements that are enabled by default. If you don't need any of these elements or if you want to replace them with your own creations, you can use the `Class.StarterGui:SetCoreGuiEnabled()|SetCoreGuiEnabled()` method in a client‑side script with the associated `Enum.CoreGuiType` option. | Default UI | Associated enum | | --- | --- | | Dynamically updated `Class.Players` list, commonly used as a [leaderboard](/docs/en-us/players/leaderboards.md). | `Enum.CoreGuiType.PlayerList` | | The character's `Class.Humanoid.Health\|Health` bar. Does not appear if the character's `Class.Humanoid` is at full health. | `Enum.CoreGuiType.Health` | | The character's `Class.Backpack` which contains [in‑experience tools](/docs/en-us/players/tools.md). Does not appear if there are no `Class.Tool\|Tools` in the backpack. | `Enum.CoreGuiType.Backpack` | | The [text chat](/docs/en-us/chat/in-experience-text-chat.md) window. | `Enum.CoreGuiType.Chat` | | Popup menu of character [emotes](/docs/en-us/characters/emotes.md). | `Enum.CoreGuiType.EmotesMenu` | | A window displaying a player's perspective or view of their own character. Does not appear unless the player has enabled **Self View** from the Roblox menu. | `Enum.CoreGuiType.SelfView` | | A **capture screenshot** button along the right side of the screen. Does not appear unless the player has enabled **Captures** from the Roblox menu. | `Enum.CoreGuiType.Captures` | | The **Avatar Switcher** allows users to change their platform avatar. | `Enum.CoreGuiType.AvatarSwitcher` | ![Core UI elements in every Roblox experience.](../../assets/ui/misc/CoreGui-Elements.jpg) ```lua local StarterGui = game:GetService("StarterGui") -- Disable default health bar and backpack StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType.Health, false) StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType.Backpack, false) ``` Additionally, devices with touch capabilities include a virtual thumbstick and a jump button by default. If desired, you can hide these elements by setting `Class.GuiService.TouchControlsEnabled` to `false` in a client‑side script. ![UI elements for touch-capable devices in every Roblox experience](../../assets/ui/misc/TouchGui-Elements.png) ```lua local GuiService = game:GetService("GuiService") GuiService.TouchControlsEnabled = false ``` --- title: "Users and players" url: /docs/en-us/players last_updated: 2026-06-29T19:34:04Z description: "How users, players, and characters work together in a Roblox game." --- # Users and players When a Roblox user joins a game, they are represented as a `Class.Player` in the `Class.DataModel`. This `Class.Player` object contains information about the user that's universal across games, such as their username, friend list, and saved [avatar character](/docs/en-us/characters.md#avatar-characters), as well as properties, methods, and events that affect the user's [lifecycle](#lifecycle) between joining and leaving the game. Each `Class.Player` object also parents four important [containers](#containers) that you can use to customize a user's experience: `Class.Backpack`, `Class.StarterGear`, `Class.PlayerGui`, and `Class.PlayerScripts`. Each `Class.Player` also exposes a `Class.Player.User` property, which is a `Datatype.User` value representing the player's domain-scoped identity within the current experience. For more information on how user identification works, including domain-scoped user IDs and the `Datatype.User` type, see [Users and domain-scoped user IDs](/docs/en-us/users.md). ## Lifecycle The `Class.Players` service contains all the `Class.Player` instances in a game. Client and server-side scripts can both connect to the `Class.Players.PlayerAdded` and `Class.Players.PlayerRemoved` events to perform actions in response to the lifecycle of a `Class.Player` object. Scripts can also connect to the `Class.Player.CharacterAdded` and `Class.Player.CharacterRemoving` events to perform gameplay-related actions for when the character spawns or despawns. ### User join When a user client connects to a game, the `Class.Players.PlayerAdded` event fires and passes the `Class.Player` object of the user who joins; you can use this object for numerous purposes such as loading [user data](/docs/en-us/cloud-services/data-stores-vs-memory-stores.md) from a data store or assigning the player to a [team](/docs/en-us/teams.md). For example, to load a user's data when they join a game, use the `Class.Players.PlayerAdded|PlayerAdded` event in a `Class.Script` to retrieve the user's data stored in a data store under their user ID: ```lua local DataStoreService = game:GetService("DataStoreService") local Players = game:GetService("Players") local playerDataStore = DataStoreService:GetDataStore("PlayerData") Players.PlayerAdded:Connect(function(player) local userId = player.UserId -- Read data store key local getSuccess, currentData = pcall(function() return playerDataStore:GetAsync(userId) end) if getSuccess then print(currentData) end -- Do further actions with currentData end) ``` ### Character spawn By default, a user's `Class.Player.Character` model represents their platform avatar and `Class.Players.CharacterAutoLoads` is `true`, meaning the character model automatically spawns when the user joins a game. When a user's `Class.Player.Character` spawns, `Class.Script|Scripts` and `Class.LocalScript|LocalScripts` in `Class.StarterCharacterScripts` clone into the character model and the `Class.Player.CharacterAdded` event fires. This event passes the new character model to its listeners which you can use to find the character's `Class.Humanoid` object and modify its behavior. For example, you can use `Class.Humanoid:ApplyDescription()` to change the outfit of the avatar. ### Character despawn When the player's `Class.Humanoid` dies, the server automatically removes the character model after the amount of time specified by `Class.Players.RespawnTime`. You can then use the `Class.Player.CharacterRemoving` event to reset other objects or update data associated with the character. ### User leave When a user client disconnects from a game, the server destroys its associated `Class.Player` object inside the `Class.Players` service. At this point, the `Class.Players.PlayerRemoving` event fires and passes the `Class.Player` object of the user who disconnected. You can use this for numerous purposes such as saving user data, removing player stats from a scoreboard, or destroying player‑created models in the game. The following example `Class.Script` listens to the `Class.Players.PlayerRemoving|PlayerRemoving` event and attempts to save the user's data in a data store under their user ID: ```lua local DataStoreService = game:GetService("DataStoreService") local Players = game:GetService("Players") local playerDataStore = DataStoreService:GetDataStore("PlayerData") Players.PlayerRemoving:Connect(function(player) local userId = player.UserId -- Get the player's data state in the game local currentData = getCurrentData(player) -- Save to data store local setSuccess, errorMessage = pcall(function() playerDataStore:SetAsync(userId, currentData) end) if not setSuccess then warn(errorMessage) end end) ``` ## Containers Each `Class.Player` object representing a user's client stores several important containers: `Class.Backpack`, `Class.StarterGear`, `Class.PlayerGui`, and `Class.PlayerScripts`. At game runtime, several containers in the "edit" data model copy their contents over to these `Class.Player` containers, including `Class.StarterGui` to `Class.PlayerGui`, `Class.StarterPack` to `Class.Backpack`, and `Class.StarterPlayerScripts` to `Class.PlayerScripts`. ![A diagram that maps objects between 'edit' and 'runtime' data models.](../assets/scripting/client-server/Data-Model-Mapping.png) #### Backpack `Class.Tool` objects in the `Class.Backpack` make up the player's inventory and display as icon buttons at the bottom of the screen. See [in‑game tools](/docs/en-us/tools.md) for more information. As illustrated above, the contents of `Class.StarterPack` and the player's `Class.StarterGear` clone to `Class.Backpack` when a player's `Class.Player.Character|Character` spawns. When the character dies, the client destroys the `Class.Backpack` and replaces it with a new one. To disable the default Roblox inventory GUI and replace it with your own, call `Class.StarterGui:SetCoreGuiEnabled()` in a `Class.LocalScript` as outlined in [disable default UI](/docs/en-us/disable-ui.md). #### StarterGear When a player's character spawns, the contents of that player's `Class.StarterGear` are copied into the `Class.Backpack`. Additionally, when a player connects to a game that permits gear, all of the appropriate gear `Class.Tool|Tools` that the player owns are inserted into that player's `Class.StarterGear`. Unlike `Class.StarterPack`, `Class.StarterGear` isn't a service but rather a child of each `Class.Player` object, so its contents are player‑specific. To use `Class.StarterGear`, navigate to your start place's **Permissions** page and then enable gear by its genre or choose specific types to allow. > **Warning:** Always test experiences after adding gear to them to check that users can't easily abuse them there. Gear may include `Class.Script` objects which allow the player to perform actions that you might not consider; for example, navigational gear might allow the player to access a part of the map that you don't want them to, and weapon gear may allow the holder to damage other players, possibly without retribution or retaliation. #### PlayerGui The `Class.PlayerGui` container stores objects that create the player's GUI. If a `Class.ScreenGui` is a descendant of `Class.PlayerGui`, any `Class.GuiObject` inside that `Class.ScreenGui` displays on the player's screen. As illustrated above, the contents of `Class.StarterGui` automatically copy into the player's `Class.PlayerGui` when their player `Class.Player.Character|Character` spawns. If `Class.StarterGui.ResetPlayerGuiOnSpawn` is set to `true`, all the contents of a player's `Class.PlayerGui` are cleared and replaced with the contents of `Class.StarterGui` every time the player's character respawns. Note that if `Class.Players.CharacterAutoLoads` is set to `false`, the character won't spawn and `Class.StarterGui` contents won't copy over until `Class.Player:LoadCharacterAsync()` is called. #### PlayerScripts The `Class.PlayerScripts` container is created automatically when a player joins the game and its main purpose is to contain client scripts copied from the `Class.StarterPlayerScripts` container within the `Class.StarterPlayer` service. It is especially useful for scripts that aren't tied to a user's character life cycle, such as the general chat system or player input controls. Unlike the `Class.Backpack` and `Class.PlayerGui` containers, the `Class.PlayerScripts` container is not accessible to the server and server‑side `Class.Script` objects will not run when parented to `Class.PlayerScripts`. ## Ban users To ensure civility and fair play in your experiences, you can ban users who violate rules or community guidelines. You can modify ban durations, ban messages, and even extend bans to potential alternate accounts. When using this feature, you must follow banning [guidelines](#ban-guidelines) and [messaging](#message-guidelines). You have several options for working with bans: - Each experience page on the [Creator Hub](https://create.roblox.com/) has a [Bans](/docs/en-us/production/bans.md) dashboard. - For programmatic usage with the engine API, see `Class.Players:BanAsync()`. - For Open Cloud, see [bans and blocks](/docs/en-us/cloud/features/bans-and-blocks.md). ### Ban guidelines When implementing bans in your experience, adhere to the following guidelines: - Experience rules must not contradict Roblox's [Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards) and [Terms of Use](https://en.help.roblox.com/hc/en-us/articles/115004647846-Roblox-Terms-of-Use). For example, you cannot create an experience rule that excludes someone because of their gender, as this violates [Roblox's Discrimination, Slurs, and Hate Speech policy](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards#discrimination-slurs-and-hate-speech). - Creators must clearly state their experience rules somewhere accessible to all users. - Creators must apply their experience rules fairly and not arbitrarily target certain users. - Users can appeal to creators directly if they believe their ban was incorrect. Roblox will not mediate these appeals, unless the user believes the creator's experience rules or enforcement of their rules violate the [Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards). - Roblox can moderate an experience if there is reason to believe that a creator's experience rules or enforcement of their rules violate the [Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards). ### Message guidelines When a user is banned, they receive an error modal displaying information such as the ban length and reason. In the text-filtered message, you can include additional information such as appeal or contact information as long as you meet Roblox's [Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards). For example, in your ban messages, you are allowed to reference brand names and platforms: - "Visit the Discord in my group/experience page" - "Message me on Twitter or X" Mentions of personal information or direct links are not allowed in this message field. This includes posting a specific username or handle, or providing a direct link to a Discord server or X account. --- title: "In-experience leaderboards" url: /docs/en-us/players/leaderboards last_updated: 2026-06-29T19:34:04Z description: "In-experience Leaderboards let you display user information to all users in the experience, such as top scores." --- # In-experience leaderboards Roblox has a built-in **leaderboard system** that lets you display user information like scores, currency, or the fastest time in a race. ![Leaderboard Screen](../assets/players/leaderboard/Leaderboard-On-Screen.jpg) ## Set up the leaderboard To set up the leaderboard and add players when they enter the experience: 1. Create a new `Class.Script` within `Class.ServerScriptService` and name it `Leaderboard`.![Leaderboard Insert Script](../assets/players/leaderboard/Leaderboards-Insert-Script.png) 2. In the script, connect a function to the `Class.Players.PlayerAdded|PlayerAdded` event.```lua local Players = game:GetService("Players") local function leaderboardSetup(player) end Players.PlayerAdded:Connect(leaderboardSetup) ``` 3. Inside the connected function, create a new `Class.Folder` instance, name it `leaderstats`, and parent it to the player.```lua local Players = game:GetService("Players") local function leaderboardSetup(player) local leaderstats = Instance.new("Folder") leaderstats.Name = "leaderstats" leaderstats.Parent = player end Players.PlayerAdded:Connect(leaderboardSetup) ``` > **Warning:** It's essential that the folder is named `leaderstats` with all lowercase letters. Roblox doesn't add the player to the leaderboard if you name it any other way. ## Add stats Leaderboards use **value type objects** to store and display player stats. This script will show a player's gold using an `Class.IntValue`, a placeholder for an integer. In the `leaderboardSetup()` function, add lines 8 through 11: ```lua local Players = game:GetService("Players") local function leaderboardSetup(player) local leaderstats = Instance.new("Folder") leaderstats.Name = "leaderstats" leaderstats.Parent = player local gold = Instance.new("IntValue") gold.Name = "Gold" gold.Value = 0 gold.Parent = leaderstats end Players.PlayerAdded:Connect(leaderboardSetup) ``` These lines accomplish the following: 1. An `Class.IntValue` instance is created. 2. The instance's `Class.Instance.Name|Name` is set to `"Gold"`. This is exactly how the stat will appear on the leaderboard.![Stat name 'Gold' shown on leaderboard](../assets/players/leaderboard/Leaderboard-Stat-Name.png) 3. The stat's initial `Class.IntValue.Value|Value` is set to `0`. This can be set to any value you wish, including a value stored in a [data store](/docs/en-us/cloud-services/data-stores.md) if you're implementing persistent leaderboards. > **Info:** Note that the value of objects such as `Class.IntValue`, `Class.NumberValue`, and `Class.StringValue` must be set through their `Value` property, as in `gold.Value` on line 10. 4. The instance is parented to the `leaderstats` folder which adds it to the leaderboard. When a player enters the experience, their name appears on the board.![Multiple players shown on leaderboard](../assets/players/leaderboard/Leaderboards-Multiple-Players.png) ## Update stats To update a player's leaderboard stat, change the `Value` property of that stat within their `leaderstats` folder. For example, you can attach the following `Class.Script` to any pickup object to increase the `Gold` stat of the player collects it. ```lua local Players = game:GetService("Players") local goldChunk = script.Parent local function onPartTouch(otherPart) local partParent = otherPart.Parent local player = Players:GetPlayerFromCharacter(partParent) local leaderstats = player and player:FindFirstChild("leaderstats") local goldStat = leaderstats and leaderstats:FindFirstChild("Gold") if goldStat then -- Destroy the pickup goldChunk:Destroy() -- Update the player's leaderboard stat goldStat.Value += 10 end end goldChunk.Touched:Connect(onPartTouch) ``` ## Order stats There are three ways to control the order of stats in a leaderboard: - Add the stats in the order that you want them to appear. - Add a child `Class.BoolValue` named `IsPrimary` to the stat and set its value to `true` to place the stat first in the leaderboard. - Add a child `Class.NumberValue` named `Priority` to the stat and set its value to an integer. Higher priority values appear earlier in the leaderboard. Stats without a priority have a default priority of `0`. This code sample shows how to add an `IsPrimary` value to a stat: ```lua local Players = game:GetService("Players") local function leaderboardSetup(player) local leaderstats = Instance.new("Folder") leaderstats.Name = "leaderstats" leaderstats.Parent = player local gold = Instance.new("IntValue") gold.Name = "Gold" gold.Value = 0 gold.Parent = leaderstats local isPrimary = Instance.new("BoolValue") isPrimary.Name = "IsPrimary" isPrimary.Value = true isPrimary.Parent = gold end Players.PlayerAdded:Connect(leaderboardSetup) ``` > **Info:**`IsPrimary` takes precedence over any `Priority` values. If multiple stats have `IsPrimary` values set to `true`, their `Priority` values determine the leaderboard order. ## Hide the leaderboard To hide the leaderboard, such as on a menu screen or during a cutscene, place a `Class.LocalScript` within `Class.StarterGui` or `Class.StarterPlayerScripts` containing a call to `Class.StarterGui:SetCoreGuiEnabled()|StarterGui`. ```lua local StarterGui = game:GetService("StarterGui") StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType.PlayerList, false) ``` --- title: "Loading screens" url: /docs/en-us/players/loading-screens last_updated: 2026-06-29T19:34:04Z description: "Explains the process of customizing the loading screen when users are connecting to your experience." --- # Loading screens Roblox displays a default loading screen when users are connecting to an experience, but you can personalize your experience with a custom loading screen that contains static or animated content. > **Info:** This article covers the loading screens that display when a user initially joins an experience. To customize loading screens that display when a user teleports between places, see [Teleport between places](/docs/en-us/projects/teleport.md#create-custom-teleport-screens). ## Implementation To display a custom loading screen, you can either design a `Class.ScreenGui` instance directly within a `Class.LocalScript`, or you can reference a `Class.ScreenGui` object in your place hierarchy. Both options utilize `Class.ReplicatedFirst`, as this service that replicates instances to clients before anything else is replicated. This ensures that your loading screen is the first thing users see when they enter your experience. ### From script To design and display a custom loading screen entirely from a `Class.LocalScript`: 1. In `Class.ReplicatedFirst`, create a `Class.LocalScript`. 2. Paste in the following code sample to create and customize a `Class.ScreenGui` object. You can modify the following code with your own values to create your design. ```lua local Players = game:GetService("Players") local ReplicatedFirst = game:GetService("ReplicatedFirst") local player = Players.LocalPlayer local playerGui = player:WaitForChild("PlayerGui") local loadingScreen = Instance.new("ScreenGui") loadingScreen.IgnoreGuiInset = true loadingScreen.Parent = playerGui -- Replace ScreenGui values with your own local textLabel = Instance.new("TextLabel") textLabel.Size = UDim2.new(1, 0, 1, 0) textLabel.BackgroundColor3 = Color3.fromRGB(0, 20, 40) textLabel.Font = Enum.Font.DenkOne textLabel.TextColor3 = Color3.new(0.8, 0.8, 0.8) textLabel.Text = "Loading" textLabel.TextSize = 32 textLabel.Parent = loadingScreen -- Remove the default loading screen ReplicatedFirst:RemoveDefaultLoadingScreen() task.wait(5) -- Force custom loading screen to appear for a minimum time if not game:IsLoaded() then game.Loaded:Wait() end loadingScreen:Destroy() ``` ### From existing GUI Instead of creating a loading screen `Class.ScreenGui` entirely through a `Class.LocalScript`, you can also reference an existing `Class.ScreenGui` in your place hierarchy. Ensure that your experience includes the referenced `Class.ScreenGui` within `Class.ReplicatedFirst` and that the `Class.ScreenGui` includes UI elements like `Class.TextLabel|TextLabels` and `Class.ImageLabel|ImageLabels`. To demonstrate this process, the following `Class.LocalScript` references a `Class.ScreenGui` named **LoadingScreen** within `Class.ReplicatedFirst`, then it removes the default loading screen so the only loading screen a player can see is your own custom loading screen. > **Success:** Remember that you can design your loading screen `Class.ScreenGui` within the `Class.StarterGui` container to preview it, then move it to `Class.ReplicatedFirst` once you're satisfied with the result. ```lua local Players = game:GetService("Players") local ReplicatedFirst = game:GetService("ReplicatedFirst") local player = Players.LocalPlayer local playerGui = player:WaitForChild("PlayerGui") local loadingScreen = ReplicatedFirst:FindFirstChild("LoadingScreen") if loadingScreen then loadingScreen.IgnoreGuiInset = true loadingScreen.Parent = playerGui -- Remove the default loading screen ReplicatedFirst:RemoveDefaultLoadingScreen() task.wait(5) -- Force screen to appear for a minimum number of seconds if not game:IsLoaded() then game.Loaded:Wait() end loadingScreen:Destroy() end ``` ## Add animations In addition to static custom loading screens, you can add animations to enhance the loading screen and indicate loading progress. The easiest way to do this is to create a UI element, such as a `Class.TextLabel` or `Class.ImageLabel`, then to animate it using `Class.TweenService`. For example, the following code sample creates a new `Class.ScreenGui` with a child `Class.ImageLabel`, removes the default loading screen, then `Class.TweenService` fades in and rotates the central elements until the experience loads. ```lua local Players = game:GetService("Players") local ReplicatedFirst = game:GetService("ReplicatedFirst") local TweenService = game:GetService("TweenService") local player = Players.LocalPlayer local playerGui = player:WaitForChild("PlayerGui") local loadingScreen = Instance.new("ScreenGui") loadingScreen.IgnoreGuiInset = true loadingScreen.Parent = playerGui local textLabel = Instance.new("TextLabel") textLabel.Size = UDim2.new(1, 0, 1, 0) textLabel.BackgroundColor3 = Color3.fromRGB(0, 20, 40) textLabel.Font = Enum.Font.DenkOne textLabel.TextColor3 = Color3.new(0.8, 0.8, 0.8) textLabel.Text = "Loading" textLabel.TextSize = 32 textLabel.TextTransparency = 1 textLabel.Parent = loadingScreen local loadingRing = Instance.new("ImageLabel") loadingRing.Size = UDim2.new(0, 256, 0, 256) loadingRing.BackgroundTransparency = 1 loadingRing.Image = "rbxassetid://4965945816" loadingRing.AnchorPoint = Vector2.new(0.5, 0.5) loadingRing.Position = UDim2.new(0.5, 0, 0.5, 0) loadingRing.ImageTransparency = 1 loadingRing.Parent = loadingScreen -- Remove the default loading screen ReplicatedFirst:RemoveDefaultLoadingScreen() -- Initiate and start fade-in tweens local fadeTweenInfo = TweenInfo.new(1, Enum.EasingStyle.Quad, Enum.EasingDirection.Out, 0, false, 2) local fadeTween1 = TweenService:Create(textLabel, fadeTweenInfo, {TextTransparency = 0}) local fadeTween2 = TweenService:Create(loadingRing, fadeTweenInfo, {ImageTransparency = 0}) fadeTween1:Play() fadeTween2:Play() -- Initiate and start rotation tween local tweenInfo = TweenInfo.new(4, Enum.EasingStyle.Linear, Enum.EasingDirection.In, -1) local tween = TweenService:Create(loadingRing, tweenInfo, {Rotation = 360}) tween:Play() task.wait(5) -- Force screen to appear for a minimum number of seconds if not game:IsLoaded() then game.Loaded:Wait() end loadingScreen:Destroy() ``` --- title: "Implement teams" url: /docs/en-us/players/teams last_updated: 2026-06-29T19:34:04Z description: "The Teams service allows you assign players to different teams within your experience." --- # Implement teams You can divide players in your experience into multiple teams using the `Class.Teams` service. By [configuring](#add-teams) the `Class.Teams` service and [assigning teams](#assign-players-to-teams), players are automatically differentiated by their [display name](/docs/en-us/characters/name-health-display.md) color, as well as their name in the default player list. ## Add teams By default, no teams are configured. To add teams: 1. In the [Explorer](/docs/en-us/studio/explorer.md) hierarchy, hover over the `Class.Teams` object and click the **⊕** icon to show a list of objects. 2. Insert a new `Class.Team` object under the `Class.Teams` service. 3. Select the new `Class.Team` object and, in the [Properties](/docs/en-us/studio/properties.md) window, change the team `Class.Team.Name|Name` and assign a unique matching `Class.Team.TeamColor|TeamColor`. A team's name and color both appear in the experience's player list. 4. Repeat these steps to add more teams. ## Assign players to teams By default, Roblox **auto-assigns** new players joining the experience to the team with the fewest members, and you can still use the following steps to assign players to a specific team. 1. Select each `Class.Team` object in the `Class.Teams` service to display its properties. 2. Uncheck the `Class.Team.AutoAssignable|AutoAssignable` checkbox. 3. Assign a player to a specific team by changing their `Class.Player.Team` property to the team name in the format of `Teams[TEAM_NAME]`, such as`Teams["Blue Team"]`. ## Team spawn locations You can use `Class.SpawnLocation` objects to spawn players of different teams at specific locations when they join or respawn. By default, `Class.SpawnLocation|SpawnLocations` are **neutral** and any player can spawn upon them, so you need to lock each one to the team that can occupy it using the following steps: 1. Using the [Explorer](/docs/en-us/studio/explorer.md), insert a new `Class.SpawnLocation` into the workspace. 2. With the new `Class.SpawnLocation` selected, uncheck its `Class.SpawnLocation.Neutral|Neutral` checkbox in the [Properties](/docs/en-us/studio/properties.md) window. 3. Set the spawn's `Class.SpawnLocation.TeamColor|TeamColor` property to the `Class.Team.TeamColor|TeamColor` of an existing [team](#add-teams). For example, if the intended team's `Class.Team.TeamColor` is `Lapis`, set the spawn's `Class.SpawnLocation.TeamColor` to `Lapis` as well._`Class.Team.TeamColor`__`Class.SpawnLocation.TeamColor`_ > **Warning:** Note that the `Class.SpawnLocation.TeamColor` property is different from `Class.SpawnLocation.BrickColor` and `Class.SpawnLocation.Color` which represent the **visual** color of the spawn object and are not related to team functionality. --- title: "In-game tools" url: /docs/en-us/players/tools last_updated: 2026-06-29T19:34:04Z description: "Create in-game tools for your players." --- # In-game tools In-game `Class.Tool|Tools` are interactive tools that players can equip in sessions, such as weapons, magic wands, flashlights, keys, and more. You can design tools and include them in a player's inventory or place them around the world as pickups. ## Create an in-game tool The `Class.Tool` object is the basis of any tool in Roblox, so you'll need to create one. It's easier to preview how a tool looks by designing it in the `Class.Workspace`, and later you can make it an [inventory](#player-inventory) item or an [earned/purchased](#earnedpurchased-tool) item. 1. In the [Explorer](/docs/en-us/studio/explorer.md) hierarchy, hover over `Class.Workspace`, click the **⊕** button, and insert a `Class.Tool`.![Tool object created in the workspace](../assets/studio/explorer/Workspace-Tool.png) 2. With the new `Class.Tool` selected, access the [Properties](/docs/en-us/studio/properties.md) window and customize how the tool will appear in the player's inventory: - `Class.Tool.TextureId|TextureId` — Specifies the image asset ID for the tool in a player's inventory. - `Class.Tool.ToolTip|ToolTip` — The on-hover tooltip name for the tool in a player's inventory. 3. Decide whether the tool will be a [physical tool in the 3D environment](#physical-tool) (most common) or a [non‑physical](#non-physical-tool) tool that simply occupies a player's inventory and responds to their input. ### Physical tool After creating the `Class.Tool` instance for a physical tool, you'll need to [add parts/meshes](#add-partsmeshes) to compose its appearance in the 3D world. You'll also need to include a [handle](#set-the-handle) which player characters [grip](#adjust-the-grip-positionorientation). #### Add parts/meshes In-game tools can consist of one or more `Class.Part|Parts` and/or `Class.MeshPart|MeshParts`. If you construct the tool of multiple pieces, remember to connect them all together using `Class.WeldConstraint|WeldConstraints` or other [mechanical constraints](/docs/en-us/physics/mechanical-constraints.md) so that the tool stays intact as a unit while the player carries it around. ![Constraint pickers indicated in Studio's toolbar](../assets/studio/general/Toolbar-Constraint-Pickers.png) > **Warning:** When constructing tools, make sure that no parts are `Class.BasePart.Anchored|Anchored`. If you anchor the tool in 3D space, player characters will get stuck in place when they pick up or equip the tool. > > If you want to place collectible tools as pickups around the 3D world and anchor them in specific places, such as floating slightly above the ground, you can anchor them but you'll need to add a `Class.Script` to each tool which unanchors its parts upon collection. See [collectible tools](#collectible-tool) for details. #### Set the handle To enable players to carry tools around, one part of the tool needs to be named `Handle`. This is where the tool will attach to the character model's `RightHand` part (R15) or `Right Arm` part (R6). If the tool is composed of just one `Class.MeshPart`, simply name that part `Handle`. Otherwise, you can include a separate part named `Handle` as a **direct child** of the parent `Class.Tool` and then attach it to another part with a `Class.WeldConstraint` or [mechanical constraint](/docs/en-us/physics/mechanical-constraints.md). The following example shows a tool with three instances: the `Handle` mesh, a `Class.RopeConstraint`, and a lantern mesh which hangs from the rope. > **Warning:** Make sure to include only **one** part named `Handle`. If you include multiple parts named `Handle`, one will be randomly chosen as the attachment point for the character's hand, potentially causing issues such as the character holding a sword's blade instead of its hilt. #### Adjust the grip position/orientation The tool's `Class.Tool.Grip|Grip` property is a `Datatype.CFrame` which specifies how the tool is positioned and oriented relative to the character holding it. Since the ideal grip position/orientation is different for every tool, you'll need to experiment with the values until your tool's grip looks correct. The following example shows possible incorrect and correct `Class.Tool.Grip|Grip` settings for the hanging lantern: _Lantern dragging on ground with `Class.Tool.Grip|Grip.Position` of`(0, -2, 0)` and `Class.Tool.Grip|Grip.Orientation` of`(-90, 0, 0)`_ _Lantern in front of character with `Class.Tool.Grip|Grip.Position` of`(0, -1.5, 0)` and `Class.Tool.Grip|Grip.Orientation` of`(25, 0, 0)`_ ### Non-physical tool A non-physical `Class.Tool` simply occupies a player's [inventory](#player-inventory) and responds to their input. For example, a roleplaying game might feature a series of magical spells that players can "cast" through their inventory. Non-physical tools don't require any 3D composition and do not utilize a `Handle` part, so you should **disable** the tool's `Class.Tool.RequiresHandle|RequiresHandle` property: ## Add tools to a game Once you finish [creating](#create-an-in-game-tool) your in-game tool, you'll need to place it in the correct area of the [Explorer](/docs/en-us/studio/explorer.md) hierarchy. Where you place the tool in the hierarchy depends on its intended usage. ### Player inventory If you want all players to start out with a tool in their inventory, put it inside the `Class.StarterPack` container. When any player spawns, the system copies the tool to their `Class.Backpack`. From there, the player can equip the tool by clicking its inventory icon or by pressing the associated hotkey such as `1` or `2`. Note that tools are copied to the player's inventory (`Class.Backpack`) in the order they were added to `Class.StarterPack`. For a more controlled ordering, you can add a `Class.LocalScript` to `Class.StarterPack` which temporarily moves the tools to `Class.ReplicatedStorage` then re‑parents them to `Class.Backpack` in the order specified in the `order` table. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local order = {"WaterSpell", "FireSpell", "AirSpell", "EarthSpell"} local backpack = script.Parent for _, item in backpack:GetChildren() do if item:IsA("Tool") then item.Parent = ReplicatedStorage end end -- Re-parent tools to backpack in the specified order for i = 1, #order do local tool = ReplicatedStorage:FindFirstChild(order[i]) if tool then tool.Parent = backpack end end ``` > **Info:** As noted above, tools placed in `Class.StarterPack` clone to **all** players. To more specifically control which tools clone to which players, see `Class.StarterGear`. ### Collectible tool If you want to allow players to collect physical tools as they explore the 3D world, place the tools in the `Class.Workspace` just like normal 3D objects. Collected tools will be automatically equipped and added to the player's inventory (you do not need to add extra collision detection for characters to grab the tool). As noted in the [add parts/meshes](#add-partsmeshes) section, tool parts should not typically be `Class.BasePart.Anchored|Anchored`, since player characters will get stuck in place when they pick up or equip an anchored tool. However, if you want to anchor a collectible tool to a specific place in the 3D world, such as floating slightly above the ground, you can add the following `Class.Script` to the tool to unanchor its parts upon collection. ```lua local tool = script.Parent tool.Equipped:Connect(function() for _, part in tool:GetDescendants() do if part:IsA("BasePart") and part.Anchored then part.Anchored = false end end end) ``` ### Earned/purchased tool If you want to set tools as awards for when players accomplish tasks, or offer tools for sale in an in‑game store, put those tools inside `Class.ReplicatedStorage`. From there, you can `Class.Instance:Clone()|Clone()` a tool to the player's `Class.Backpack` at the proper time. ```lua local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") local player = Players.LocalPlayer local backpack = player:WaitForChild("Backpack") -- Create a clone of the tool local tool = ReplicatedStorage:FindFirstChild("WaterSpell") if tool then local clone = tool:Clone() clone.Parent = backpack -- Adds clone to the player's backpack end ``` ## Implement tool effects After [adding tools to your game](#add-tools-to-a-game), you can implement scripts to enable players to use their tools and perform the desired actions. The following tool‑specific events indicate the state of the tool and the player's input with it. - `Class.Tool.Equipped|Equipped()` — Fires when the player selects the tool from their backpack. - `Class.Tool.Unequipped|Unequipped()` — Fires when the player switches tools or drops the tool. - `Class.Tool.Activated|Activated()` — Fires when the player starts activating the tool (clicks, taps, or presses the back‑right trigger on a gamepad). - `Class.Tool.Deactivated|Deactivated()` — Fires when the player stops the activation input (releases the button or touch). ```lua local tool = script.Parent local function onEquip() print(tool.Name, "tool equipped") end local function onUnequip() print(tool.Name, "tool unequipped") end local function onActivate() print(tool.Name, "tool activated") end local function onDeactivate() print(tool.Name, "tool deactivated") end tool.Equipped:Connect(onEquip) tool.Unequipped:Connect(onUnequip) tool.Activated:Connect(onActivate) tool.Deactivated:Connect(onDeactivate) ``` Tool events only fire in client‑side `Class.LocalScript|LocalScripts` because only the player's device knows when [input](/docs/en-us/input.md) happens. As a result, most tools require both a `Class.LocalScript` and a server‑side `Class.Script` where each handles certain aspects of the tool's behavior and a `Class.RemoteEvent` passes information from the client to the server (see [remote events and callbacks](/docs/en-us/scripting/events/remote.md) for more information). As follows are example tools and their behaviors managed by either a `Class.LocalScript` or a `Class.Script`: | Tool | `Class.LocalScript` | `Class.Script` | | --- | --- | --- | | **Creator's Wand** | Detects where the player activates the tool (`Class.Tool.Activated\|Activated()`). | Creates a new part at the location within the 3D world where the player touched or clicked. | | **Invisibility Cloak** | Detects when the player equips the tool (`Class.Tool.Equipped\|Equipped()`) or unequips the tool (`Class.Tool.Unequipped\|Unequipped()`). | Makes the player invisible to all other players while the cloak is equipped and restores the player to full visibility when the cloak is unequipped. | | **Mega‑Bow** | Detects how long the player activates the tool (time between `Class.Tool.Activated\|Activated()` and `Class.Tool.Deactivated\|Deactivated()`). | Shoots a magical arrow with greater or lesser power, depending on the detected activation time. | --- title: "Users and domain-scoped user IDs" url: /docs/en-us/players/users last_updated: 2026-06-29T19:34:04Z description: "Learn about domain-scoped user IDs and the User type for identifying players in your experiences." --- # Users and domain-scoped user IDs > **Warning:** Refer to the [DevForum announcement](https://devforum.roblox.com/t/update-on-safety-privacy-introducing-scoped-user-identifiers/4677155/1) for latest information on domain-scoped user ID rollout and timelines. Roblox assigns each user a unique **domain-scoped user ID** per experience or app they interact with. This page explains how domain-scoped identification works and how to use the `Datatype.User` type in your experience code. The following concepts are central to user identification: | Concept | Description | | --- | --- | | **User** | A persistent account on Roblox. | | **Player** | The representation of a user within a running experience's data model (see [Players](/docs/en-us/players.md)). | | `Datatype.User` data type | A data type that represents a user's identity within a specific domain. This is the standard identifier for referencing users in experience code. | | **Domain-scoped user ID** | A numeric identifier that uniquely identifies a user within a specific [domain](#domain-scoped-user-ids). This identifier is different in every experience and app. | ## Domain-scoped user IDs A **domain-scoped user ID** is a unique identifier assigned to a user within a specific **experience** or **app**. Unlike a global user ID, a domain-scoped user ID is only meaningful within the domain that issued it, so the same user has a different domain-scoped user ID in each experience or app they interact with. Players who joined your experience before the scoped ID rollout keep their original global user ID in that experience (see [Returning players](#returning-players)). Domain types are represented by `Enum.DomainType`: | Domain type | Scoped to | Example | | --- | --- | --- | | `Enum.DomainType.EXPERIENCE` | A specific experience (universe) | A player's ID in your experience | | `Enum.DomainType.OAUTH` | A specific OAuth application | A user's ID in your external tool | ### How domain-scoped IDs work When a player joins your experience, they receive a domain-scoped user ID that is unique to that experience. The same player joining a different experience receives a different domain-scoped user ID. This means: - Domain-scoped user IDs are integers, similar to existing global user IDs. - A domain-scoped user ID does not collide with any existing global user ID. - The same numeric value may appear in different domains for different users. The unique key is the combination of domain type, domain ID, and domain-scoped user ID. ### Example scenario The following example demonstrates how different experiences reference the same user: | Scenario | User context | User ID seen by experience | | --- | --- | --- | | User connects to **Experience A** | Returning player (visited before scoped IDs) | `123` (global ID, unchanged) | | User connects to **Experience B** | First visit after scoped ID rollout | `500789` (domain-scoped user ID for Experience B) | | User connects to **Experience C** | First visit after scoped ID rollout | `302441` (domain-scoped user ID for Experience C) | ### Returning players If a player already joined your experience before the scoped user ID rollout, they keep their original global user ID in that experience. This ensures existing data store keys, leaderboards, and inventories continue to work without any changes. ## Working with the User Type `Datatype.User` is the standard way to identify users in experience code. A `Datatype.User` value carries the domain-scoped user ID alongside the domain type and domain ID, so the identity context always travels with the identifier. For full API details, see the `Datatype.User` reference. ### Get a User from a Player The primary way to obtain a `Datatype.User` is from a connected `Class.Player` via `Class.Player.User`: ```lua local Players = game:GetService("Players") Players.PlayerAdded:Connect(function(player) local user = player.User print("Domain user ID:", user.Id) print("Domain type:", user.DomainType) print("Domain ID:", user.DomainId) end) ``` ### Construct a User from an ID To create a `Datatype.User` from a known user ID within the current experience, use `Datatype.User.fromId()`: ```lua local user = User.fromId(knownUserId) ``` ## Using User with engine APIs Engine APIs that accept a user ID parameter also accept `Datatype.User` values directly. Using `Datatype.User` is recommended: ```lua -- Both forms work: BadgeService:AwardBadge(player.User, badgeId) -- Recommended BadgeService:AwardBadge(player.UserId, badgeId) ``` ```lua -- Both forms work: local target = Players:GetPlayerByUserId(player.User) -- Recommended local target = Players:GetPlayerByUserId(someUserId) ``` When a numeric user ID is passed to an engine API that accepts user IDs, it is automatically wrapped into a `Datatype.User` internally. ### Engine APIs that return user IDs Engine APIs that return numeric user IDs continue to use the same field names and types. Each returned ID is either a global ID or a domain-scoped user ID depending on whether the user joined the experience before or after the scoped ID rollout: - **Returning players** (first joined before scoped ID launch) — the API returns the user's global user ID. - **New players** (first join after scoped ID launch) — the API returns the user's domain-scoped user ID. Both global and domain-scoped user IDs are standard integers that do not collide, so existing code that reads these numeric fields continues to work. ## Storing user identity For data store keys within a single experience, use `user.Id` directly: ```lua local key = ("player_%d"):format(player.User.Id) PlayerDataStore:SetAsync(key, data) ``` For values that may be read by other systems or need full domain context, serialize the `Datatype.User` with `Datatype.User:ToString()|ToString()`: ```lua local encoded = player.User:ToString() SomeDataStore:SetAsync("support_ticket_user", encoded) -- Later, restore from the string local user = User.fromString(encoded) ``` For more details on serialization guarantees, see the `Datatype.User` reference. ## Best practices - Use `Class.Player.User` as the standard identifier in new code. - Use `Datatype.User:ToString()|ToString()` and `Datatype.User.fromString()|fromString()` when persisting or transmitting a `Datatype.User` value. - Do not use a numeric user ID from one domain to identify a user in another domain. ### When to use User or Player - Use `Class.Player` for in-server actions: character movement, GUI, teleport, chat, inventory. - Use `Datatype.User` for identity that outlives a session: data stores, Open Cloud tools, cross-experience references. --- title: "Acquisition" url: /docs/en-us/production/analytics/acquisition last_updated: 2026-06-29T19:34:04Z description: "Explains how to interpret and improve acquisition metrics for your experience." --- # Acquisition **Acquisition** measures where new and returning users are coming from and how well they convert. When acquiring users to your experience, such as announcing an in-game event or creating ad campaigns, make sure your experience has good [retention](/docs/en-us/production/analytics/retention.md) and [engagement](/docs/en-us/production/analytics/engagement.md). ## View acquisition metrics To view your experience's acquisition analytics: 1. Navigate to your [Creations](https://create.roblox.com/dashboard/creations) page on **Creator Dashboard** and select your experience. 2. In the **Analytics** menu on the left, select **Acquisition**. You can view analytics for individual or group owned experience. To view the latter, you need to have [group permissions for analytics](/docs/en-us/production/analytics/analytics-dashboard.md). ## Acquisition sources On the acquisition dashboard, you'll see the following charts and tables: - New users with play per source - New users with impressions per source - Returning users with plays per source - Returning users with impressions per source - Play through rate - Cumulative new users funnel Here's a list of all the available acquisition sources: | Source | Description | | --- | --- | | Home recommendations | **Recommended Experiences** section on **Home** | | Continue play | **Continue Play** on **Home** | | Today's Picks | **Today's Picks** on **Home** | | Home other | **Favorites** and all other traffic on **Home** | | Friends | **Friend Activity** section on **Home** | | Search | Organic traffic from **Search** results and **Recommended Experiences** on **Experience Details** page | | Charts | **All sorts** on **Charts** | | Sponsored ads | Acquisition from [sponsored experiences](/docs/en-us/production/promotion/ads-manager.md) and **Sponsored Takeovers** on **Home** | | Search ads | Acquisition from [search ads](/docs/en-us/production/promotion/ads-manager.md) | | Portal ads | Acquisition from [portal ads](/docs/en-us/production/monetization/immersive-ads.md#portal-ads) | | Teleport | Traffic coming from another experience through [teleporting](/docs/en-us/projects/teleport.md) | | Other | All other traffic with unknown sources or can't be attributed, including the traffic from external sources | ## Acquisition attribution You can view the acquisition dashboard by [acquisition source](#acquisition-sources) or by [share links](/docs/en-us/production/promotion/share-links.md). The dashboard categorizes results for both views as follows: | Attribution | Description | | --- | --- | | Users with plays | Unique new users with plays on the experience. | | Conversion rate | Percent of new users who played your experience after viewing an impression. | | D7 retention | Percentage of new players acquired in the date range who played again on the 8th day (D8) after their first session ended. | | 7D playtime per user (cumulative) | Cumulative playtime for new players acquired in the date range in their first 7 days divided by the number of new players acquired in the date range. | | 30D payer conversion (cumulative) | Number of paying users from new players acquired in the date range in their first 30 days divided by the number of new players acquired in the date range. | | 30D revenue per user (cumulative) | Cumulative revenue, excluding subscriptions, engagement payouts, and immersive ads, for new players acquired in the date range in their first 30 days divided by the number of new players acquired in the date range. | ![Chart indicating new users funnel by acquisition source.](../../assets/analytics/acquisition/New-Users-Funnel.png) ## Improve acquisition To improve acquisition, you can run an acquisition funnel to identify sources that might drive more traffic or convert better, such as: - The sources driving **the most number of new users** to your experience. - The sources with the best **end-to-end conversion rate**. ### Improve acquisition from Roblox sources Among all Roblox sources, **Home** is usually where the vast majority of users find experiences. To improve your experience's discovery on Home: 1. **Improve engagement and retention.** Get your [session time](/docs/en-us/production/analytics/engagement.md#improve-average-session-time) and [day 1 retention](/docs/en-us/production/analytics/retention.md#improve-day-1-retention) to be comparable or above your similar experience benchmarks. The algorithm cares about these metrics because they signal how engaging and satisfying your experience is for users. 2. **Optimize monetization.** Get your [payer conversion](/docs/en-us/production/analytics/monetization.md#improve-payer-conversion-rate) rate and [ARPPU](/docs/en-us/production/analytics/monetization.md#improve-average-revenue-per-paying-user-arppu) (average revenue per paying user) to be comparable or above your similar experience benchmarks. The algorithm cares about these metrics because they measure how invested users are in your experience. ![An example shows the statistics by unique users with impressions and unique users with plays by source.](../../assets/analytics/acquisition/Unique-Users-by-Source.png) You can adopt the following strategies to improve the number of users visiting your experience from Roblox sources, including **Home**, **Discover**, and **Search**: 1. **Improve your retention**: Day 1 retention signals how engaging and satisfying your experience is for users. Experiences with high Day 1 retention are more likely to be featured in **Recommended Experiences**. For more information on improving Day 1 retention, see [Day 1 retention](/docs/en-us/production/analytics/retention.md#improve-day-1-retention). 2. **Grow your engagement**. Average session time measures how much time users spend in your experience and signals how much users enjoy your content. Experiences with high average session time are more likely to be featured in **Recommended Experiences**. For more information on Roblox sources and best practices on improving the discovery of your experience, see [Discovery](/docs/en-us/discovery.md). ### Improve play through rate from Roblox sources You can get more users to convert by making your experience metadata engaging and accurate: 1. **Use a title, description, and icon** that's true and unique to your experience. 2. **Add images and videos** to your experience detail page to engage users. ## Improve acquisition from external sources Here are some tips to get users to visit your experience from external sources: 1. **Create social accounts and post regularly**: Add social accounts such as Twitter, YouTube, and Instagram to your Roblox Experience Detail Page. Share regular updates, teasers, and screenshots to build buzz for your experience. Reply to other large social accounts to grow your following. 2. **Create a user community**: Users in your community can share feedback, suggestions, bug reports, fan art, and more. Engage with your community regularly by hosting events and replying to comments and suggestions. 3. **Partner with other creators and influencers**: You can partner with other creators who make similar experiences to grow together. You can also reach out to influencers who have large followings on platforms like YouTube or Twitch to see if they're willing to use or review your experience. 4. **Create [share links](/docs/en-us/production/promotion/share-links.md)**: Track your off-platform user acquisition and use the **New users funnel** table to understand: - **Users with qualified plays**: How many new users are joining from each link? - **7-day playtime per user**: Which links are bringing in users who engage the most? - **D7 retention**: Which platforms drive users that keep coming back? - **30-day revenue per user**: Which channels are attracting users who spend in-experience? ![Chart indicating new users funnel by share links.](../../assets/analytics/acquisition/Share-Link-Acquisition-Dashboard.png) --- title: "Alerts" url: /docs/en-us/production/analytics/alerts last_updated: 2026-06-29T19:34:04Z description: "Create alerts to monitor performance metrics for your experience and receive notifications when they cross defined thresholds." --- # Alerts > **Info:** Alerts are currently in beta. **Alerts** let you proactively monitor critical performance metrics for your experience and receive real-time notifications when they cross your defined thresholds. Instead of manually checking dashboards, you can set up alerts that notify you automatically when metrics like crash rates spike, memory usage becomes excessive, or frame rates drop. For example, the following workflow is a typical way of using alerts to catch performance regressions: 1. **Create an alert** - Set up an alert for "Client crash rate is above 5%" with a 5-minute duration and critical severity. 2. **Add a webhook** - Connect to your team's PagerDuty or Discord webhook. 3. **Push your update** - Deploy your new version. 4. **Receive the alert** - If crash rate spikes above 5% for 5 consecutive minutes, your team is immediately notified with the metric value and a link to the dashboard. 5. **Investigate and fix** - Click the dashboard link in the webhook payload to view the performance charts and diagnose the issue. You can create up to 20 alerts per experience. Creating alerts requires the **Create and configure alerts** permission. ## View the alerts page To access the alerts page, you must either be the experience owner or have **View all analytics** [permissions](#permissions). 1. Select your experience on [Creator Hub](https://create.roblox.com/). 2. Under **Configure**, select **Alerts**. The **Configuration** tab lets you create and manage alert rules. Use the **Analytics** page to review alert history, active alerts, and events. ## Permissions Alerts require specific permissions for different actions: | Action | Required permission | | --- | --- | | Create, edit, delete, or toggle alerts | Create and configure alerts | | View the alert analytics page | View all analytics | | Create experience-level webhooks | List, create, update, and delete webhooks | ## Create alerts To create an alert, click **Create** on the alerts configuration page. ![Existing alerts dashboard with create button.](../../assets/analytics/alerts/create.png) ### Alert fields Each alert has the following configuration options. | Field | Description | | --- | --- | | Name | A unique name for the alert that is displayed on notifications, annotations, and dashboards. | | Description | An optional description to provide additional context. | | Metric | The target metric to monitor. | | Time granularity | How often the metric is evaluated (minute, half hour, hourly, or daily). | | Trigger condition | The condition that fires the alert (e.g. value > 500) | | Filters | Optional filters to narrow the metric, such as place version, operating system, or platform. All filters available on the dashboard are supported. | | Breakdown | Optional breakdown to check if the condition is met for each segment separately. | | Duration | How many times the condition must be consecutively met before the alert fires (1-10). | | Severity | The severity level (critical, medium, or low). | | Delivery channel | Where to send alert notifications. See [Webhook delivery](#webhook-delivery). | ### Supported metrics Alerts support all performance metrics available on the [Performance dashboard](/docs/en-us/production/analytics/performance.md), including: - Client frame rate - Client crash rate - Client memory usage (GB) - Client memory usage percentage - Unexpected out-of-memory exits - Server CPU time - Server frame rate - Server memory usage (GB) - Concurrent users - Session time ### Trigger conditions You can set one of two trigger condition types: - **Metric value** - Fires when the metric crosses a static numeric threshold. The value uses the same unit as the metric (percentage, milliseconds, MB, etc.). - **Period over period change** - Fires when the metric's rate of change exceeds a percentage threshold. - **Week over week** - Checks the percent change between the same time last week (e.g. 5:00 PM this Friday vs. 5:00 PM last Friday). - **Day over day** - Checks the percent change between the same time yesterday (e.g. 9:00 AM today vs. 9:00 AM yesterday). - **Hour over hour** - Checks the percent change between most recent hour and the previous hour (e.g. 9:05 AM vs. 8:05 AM). For example, you can set a metric value condition like "client crash rate is above 5%" or a period over period condition like "hourly change is above 50%." ### Duration The duration setting controls how long the trigger condition must be continuously met before the alert fires. This helps reduce noise from momentary fluctuations. If you choose 1, the alert fires as soon as one data point meets your condition. If you choose 5, five consecutive data points must meet the condition. For minute-granularity alerts, the minimum duration is 5 minutes to reduce noise. ### Severity levels Each alert has a severity level that is included in the alert message and displayed with a distinct color. | Severity | Use case | | --- | --- | | Critical | Urgent issues requiring immediate action, such as a crash rate spike. | | Medium | Notable regressions that should be investigated soon. | | Low | Minor changes worth tracking but not immediately actionable. | ## Webhook delivery Alerts arrive via experience-level webhooks. When an alert fires or recovers, an HTTP POST request is sent to your configured webhook URL with a JSON payload. The message itself is also JSON, wrapped into the `AlertMessage` field as a single string, like so: ```json { "NotificationId": "11111abc-111a-1a2a-ab1a-abc1a1a111a1", "EventType": "AnalyticsAlert", "EventTime": "2026-06-15T22:05:10.8590762Z", "EventPayload": { "TargetType": "Universe", "TargetId": "1234567890", "AlertMessage": "{\n \"summary\": \"[♟️] Game has recovered\",\n \"metric\": \"PeakConcurrentPlayers\",\n \"universe_id\": \"1234567890\",\n \"evaluation_time_utc\": \"2026-06-15T22:04:47Z\",\n \"alert_history\": \"https://create.roblox.com/dashboard/creations/experiences/1234567890/alerts?tab=AlertConfiguration-Analytics&utm_medium=webhook&utm_campaign=alert_resolved\"\n}" } } ``` The `AlertMessage.summary` field includes either the word "fired" when the alert triggers or the word "recovered" when the metric returns to a normal state. You can use webhooks to integrate with external tools like PagerDuty or Slack for team-wide visibility. ### Set up a webhook 1. Select your experience on [Creator Hub](https://create.roblox.com/). 2. Under **Configure**, select **Webhooks** and click **Add Webhook**. The webhook URL comes from your provider. For example, a Slack URL probably looks like this:```text https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX ``` 3. Enter your webhook URL and a name. 4. (Optional) Include a secret, which can help ensure that the requests you receive came from Roblox. 5. (Optional) Use the **Test Response** button to check if your service can receive a sample request. 6. Click **Save Changes**. 7. When creating or editing an alert, select your webhook under the **Delivery channel** field. Creating experience-level webhooks requires the **List, create, update, and delete webhooks** [permission](#permissions). ## Monitor alerts The **Analytics** tab on the alerts page provides a centralized view of your alert history and current status: - **Triggered events** - A graph showing the number of alerts firing over time. You can adjust the time range, search by alert name, and filter by severity. - **Active alerts** - A table showing currently active (firing) alerts. - **History** - A chronological log of all alert events within the selected time range. ![Screenshot of the analytics tab showing a history of triggered events.](../../assets/analytics/alerts/monitor.png) ### Chart annotations When an alert is actively firing, a ranged annotation appears on the corresponding metric chart on the Performance dashboard. The annotation displays the alert name and spans the duration of the firing period. Annotations only appear when the chart has the same filters applied as the alert. If your alert has breakdown filters, annotations display for the total line only. ![Screenshot showing chart annotations.](../../assets/analytics/alerts/annotations.png) ## Experience overview Active alerts (alerts that have fired and haven't recovered yet) appear in the alert tray on your experience **Overview** page. The tray shows the five most recent active alerts with their timestamp, name, and current metric value. Click **View all** to navigate to the full alerts analytics page. ![Experience overview page with active alerts](../../assets/analytics/alerts/experience-overview.png) ## Best practices for alerts These tips can help you get the most out of alerts and prevent your webhook endpoints from being flooded with notifications: - **Prevent false alarms with duration** - Avoid setting your duration to 1 for highly volatile metrics like session time. A sudden influx of players joining or loading assets simultaneously can cause temporary spikes. Setting a duration of 5 ensures an alert only fires if the issue is sustained. - **Use segment breakdowns efficiently** - Instead of creating multiple individual alerts for Android, iOS, and PC, you can create a single alert and use the **Breakdown** feature. The alert will evaluate each platform separately and specify where the issue is occurring in the webhook payload. - **Establish a baseline first** - Before setting your thresholds, review your existing analytics charts over the last 7 to 14 days. If your normal crash rate hovers around 1.2%, consider setting your alert threshold to 2.5% or 3%. Giving your metrics room for natural fluctuations prevents false alarms. - **Use descriptive, structured names** - Because alert names are injected directly into your webhook payloads and annotations, using a clear formula like `metric - context` (e.g., High client crash rate - mobile) makes it easy for your team to triage issues at a glance. --- title: "Analytics dashboard" url: /docs/en-us/production/analytics/analytics-dashboard last_updated: 2026-06-29T19:34:04Z description: "The analytics dashboard helps you measure and gain insight into your experience's performance." --- # Analytics dashboard The **analytics dashboard** helps you measure and gain insight into your experience's performance, which can give you the information you need to adjust marketing, content, and monetization strategies. The dashboard includes key performance indicators (KPIs) for your experience, including: - [Retention](/docs/en-us/production/analytics/retention.md) KPIs that measure how many users return to your experience again after their first visit. - [Engagement](/docs/en-us/production/analytics/engagement.md) KPIs that reflect how users actively use your experience and represent your core user base. - [Acquisition](/docs/en-us/production/analytics/acquisition.md) statistics that show you the sources of users finding your experience and how users from different sources convert. - [Demographics](/docs/en-us/production/analytics/demographics.md) shows you the age, gender, country, and language of monthly active users who play your experience. - [Feedback](/docs/en-us/production/analytics/feedback.md) helps you monitor player/community comments and ratings. - [Monetization](/docs/en-us/production/analytics/monetization.md) KPIs that help you understand the business performance of your experience, as well as [sales data](#sales-data) that you can download and analyze in custom ways. Any experience with more than 10 daily active users (DAU) and 10 play hours for 7 consecutive days is eligible for accessing all KPIs on the dashboard. ## Set up analytics dashboard If you are an experience owner or a group owner, and your experience meets the enrollment requirements, you can enroll in the analytics dashboard at any point with the following steps: 1. Make sure that you have a verified email address and a two-step verification method on your Roblox account. 2. Navigate to the experience overview page on the [Creator Dashboard](https://create.roblox.com/dashboard/creations). 3. Read Roblox **Terms of Use** and agree to them. Now you have access to the main dashboard on the **Overview** and dashboards of all KPI categories, including **Engagement**, **Retention**, and **Monetization**. > **Info:** If you don't want to activate the dashboard immediately, or you accidentally decline the **Terms of Use**, you can still use the **Activate Analytics** button on the experience overview page to read the **Terms of Use** and agree to them any time. Refresh the page if you don't see the button. > **Warning:** For group experiences, only the group owner and members with sufficient permissions can view the analytics dashboard, as some metrics contain sensitive information. See [roles and permissions](/docs/en-us/projects/groups.md#roles-and-permissions) for details on how to create a group role with access to the dashboard. ## Dashboard functionality After activating the analytics dashboard, you can view the default charts on the overview and each KPI category. Hover over the **Information Icon** to understand what each chart represents and the **View Icon** to understand how to interpret the summary statistics on the top of each chart. There are also functionalities on each dashboard to help you further analyze your experience's performance, such as [filtering by date](#filter-by-date), [exporting charts](#export-a-chart), [benchmarking](#benchmarking), and [viewing KPI Breakdowns](#view-kpi-breakdowns). ### Filter by date You can apply a date filter to view the chart for a date range by separately selecting any specific time frame between the first date that the data was available and the present day as the start and the end date. You can also click and drag on a chart to "zoom in" to a shorter time range across the page. ### Filter by metrics You can apply various filters to better understand your cohorts by clicking the **Filter By** button. Filters apply to all charts across all analytics pages until they are turned off. Some filters might not be available for all metrics. If a filter is not available, you will see that it is grayed out: ![Example of some filters displaying at partial opacity when unavailable.](../../assets/analytics/analytics-dashboard/Metrics-Unavailable.png) You can filter by the following metrics: | **Filter type** | **Example subcategories** | **Notes** | | --- | --- | --- | | Age group | <9, 9-12, 13-15, 18+, etc. | | | Gender | Female, male, unknown | | | Country | All countries | | | Language | All languages | This filter can help you see the impact of adding translations for different languages. | | Source | Home recommendation, search, teleports, sponsored ads, etc. | | | Active payer status | Top 15%, intermediate 35%, casual 50%, lapsed, never | Percentile-based within your experience, not a fixed value. **Top 15%** includes users who fall into the 85-100th percentile of spenders for your experience, **Intermediate 35%** 50-84th, and **Casual 50%** 0-49th. **Lapsed** indicates users who haven't spent in the past 30 days, and **Never** is users who've never spent. Recalculated daily. See **Platform spender status** for status outside of your experience. | | When user first played | 0-30 days ago, 31-90 days ago, 91-180 days ago, 181-365 days ago, 365+ days ago, never | It's uncommon, but users can purchase a game pass and never actually play the experience. You might see these users on the revenue chart. | | Platform spender status | Active, other | Fixed value, not percentile-based. **Active** includes users who've spent $9.99 or more anywhere on Roblox in the last 60 days. **Other** includes all other users.

This definition is consistent with the [Creator Rewards program](/docs/en-us/creator-rewards.md), so you can better understand the behavior of players who earn you Daily Engagement Rewards.[^1] | | Platform | Computer, phone, tablet, console, VR | Users might be counted more than once if they use multiple devices. | | OS (operating system) | Android, iOS, Windows, macOS, PlayStation, etc. | Users might be counted more than once if they use multiple devices. | | Memory group (only applicable to some performance metrics) | <2GB, 2GB-4GB, 4GB-8GB, 8GB+ | Can help you identify devices that aren't performing well in your experience. | ### Explore The **Explore** page lets you deep-dive into your data with custom visualizations. Use it to build charts from custom events or standard metrics, define formulas to calculate new metrics, overlay benchmarks and prior periods, and compare segments in custom tables. You can reach the Explore page from: - The **Explore** tab in the **Analytics** left navigation. - The **Explore** button on the top-right corner of any chart on the dashboard. - A benchmark scorecard on the **Overview** page. ![The Explore page showing the Configure chart panel on the left and a line chart on the right.](../../assets/analytics/analytics-dashboard/Explore-Page.png)_ The Explore page provides a configuration panel and a single chart for focused analysis._ #### Configure a chart The **Configure chart** panel controls what the chart plots. Choose a **Source** (for example, Engagement or a custom event) and a **Metric**, then refine with: - **Time interval** — Days, weeks, or cumulative. - **Breakdown by** — Split the chart by a segment such as platform, age group, or [custom field](/docs/en-us/production/analytics/custom-fields.md). - **Filter by** — Narrow the data to specific segment values. If you are using [custom fields](/docs/en-us/production/analytics/custom-fields.md), you can filter and break down custom metrics by dimensions unique to your experience. #### Chart types Pick the chart type that best fits the question you're asking: | Type | Best for | | --- | --- | | Line | Trends over time — how a metric fluctuates day to day. | | Horizontal bar | Comparing performance across breakdown values, such as cumulative playtime by platform. | | Stacked column | Composition — how parts make up a whole, like total kills by weapon type. | | Pie | Distribution — a quick snapshot of how a total splits across segments. | | Table | Snapshots of multiple metrics side-by-side across breakdowns. | > **Info:** Stacked column and pie charts aren't available for averages or ratios. #### Calculated metrics Toggle **Use operations** in the **Configure chart** panel to build metrics from multiple inputs using math operators. 1. **Define your variables** — Add the metrics or custom events you want to combine (`Metric A`, `Metric B`, and so on). 2. **Define your formula** — Combine them with `+`, `-`, `*`, `/`, and constants, for example `(A / B) * 100`. Common use cases: - **Win rate** — `RoundWins / RoundsPlayed` to see how difficulty changes over time. - **Economy health** — `CurrencySources - CurrencySinks` to monitor inflation in real time. - **Engagement ratios** — `EquippedWeapon / TotalPlayers` to understand feature adoption. ![The Configure chart panel with Use operations enabled, showing two metrics and a formula of (A / B) * 100 labeled Win rate.](../../assets/analytics/analytics-dashboard/Explore-Calculated-Metrics.png)_ Build calculated metrics by combining custom events and standard metrics with math operators._ #### Custom tables Choose the **Table** chart type to view multiple metrics together. Tables support: - **Row breakdowns** — Group rows by date (daily, weekly, cumulative) or by a segment such as platform, age, or spender status. - **Additional metric columns** — Add more metrics to see a snapshot of performance across different breakdowns. Each metric should support the breakdown applied to the table. ![The Explore page in table mode showing daily active users, average playtime, and revenue broken down by platform.](../../assets/analytics/analytics-dashboard/Explore-Custom-Table.png)_ Custom tables let you compare several metrics across a breakdown in a single view._ #### Overlays and smoothing Use **Overlays** and **Smoothing** to understand how your metrics are trending: - **Benchmarks** — Overlay the benchmark for your experience alongside your own data. See [Benchmarking](#benchmarking) for how benchmarks are selected. - **Period-over-period** — Plot a prior time window on the same chart to compare against recent performance. Choose the offset (for example, a 28-day offset). - **7-day moving average** — Apply smoothing to remove weekly seasonality and surface the underlying trend. ![The Overlays and Smoothing controls in the Configure chart panel, with Period-over-period selected at a 28-day offset and L7 moving average enabled.](../../assets/analytics/analytics-dashboard/Explore-Overlays-Smoothing.png)_ Combine overlays and smoothing to compare periods and see the underlying trend._ #### Share a chart Once you've configured a chart, the URL reflects your selections. Bookmark it for later or share it with teammates to open the same view. ### Export a chart To export a chart to use with other data analytics tools, click the **Export Button** and save the metrics in a `.csv` file. The file includes timestamps and values of each datapoint per KPI. > **Info:** You can't export the chart data for the given KPI chart if you don't have any data for your experience. This is due to either you enroll in the dashboard for less than 48 hours or you don't have any users for the given timeframe. ### Benchmarking Benchmarking is the process of measuring your experience's KPIs and comparing them to other experiences on Roblox. With benchmarks displayed on your dashboard, you can monitor the performance of your experience's analytics KPIs among experiences on Roblox to adjust your content strategy and set appropriate goals for improvement. For KPIs relevant to specific aspects of your user behavior, the analytics dashboard provides **benchmarks for experiences with similar players**. Depending on the availability, you might see: - **Experiences with similar players** if the model finds enough experiences enjoyed by players that are similar to yours, such as those on the recommended experiences section on your Experience Details Page. - **Genre** benchmarks if the model cannot find enough similar experiences but your experience has an internal genre. - **All experiences** benchmarks if your experience hasn't been labeled with a genre yet. Your experience may transition from one benchmark set to another as we gather more data to help you compare your experience with the most relevant experiences with at least 100 daily active users. Benchmarks for similar experiences update daily to reflect the most relevant experiences for your user base. When your benchmark sets update, such as **Genre** updating to **Experiences with similar players**, the dashboard adds annotations in the charts to identify when your experience transitions from one benchmark set to another. ![An example graph showing icon indicating transition from Genre benchmarks to Similar Experiences benchmarks.](../../assets/analytics/analytics-dashboard/benchmark-transition.png) Available KPIs for similar experience benchmarks include: - **Retention** - All KPIs - **Engagement**- Average Session Time - **Monetization** - Average Revenue per Paying Users (ARPPU), Average Revenue per DAU (ARPDAU), Conversion Rate (CVR) - **Acquisition** - Play Through Rate Each of these KPIs shows its similar experience benchmark in the 50th - 90th percentile range. For example, if you see your Day 1 Retention benchmark's 50th - 90th percentile is 12.11% - 18.73%, it means that: - 50% of experiences with similar players have a Day 1 Retention of 12.11% or lower. - 10% of experiences with similar players have a Day 1 Retention of 18.73% or higher. ![An example shows the D7 retention chart with similar experience benchmarking.](../../assets/analytics/analytics-dashboard/benchmark-similar-exp.png) For KPIs on your experience's overall success, the analytics dashboard uses the top 1000 experiences with the highest total playtime over a rolling 30 days as the benchmarking pool, excluding experiences that are less than 30 days old. Each of these KPIs has **Top 200**, **Top 500**, and **Top 1000** as benchmarking tiers. The dashboard displays the appropriate tier for your experience based on your experience's engagement metrics and updates them on a daily basis. ### View KPI breakdowns You can apply a breakdown to analyze each chart in a more specific category by clicking the **Breakdown by** and toggling between the breakdown types, including **Age Group**, **Platform**, **OS**, **Country**, and **Language**. The following table explains the subcategories and usage of each breakdown type. You might also see an "Unknown" subcategory that refers to users without relevant data. | Breakdown by | Subcategory examples | Notes | | --- | --- | --- | | Age group | <9, 9-12, 13-15, 18+, etc. | | | Platform | Computer, phone, tablet, console, VR | Users might be counted more than once if they use multiple devices. | | OS (operating system) | Android, iOS, Windows, macOS, PlayStation, etc. | Users might be counted more than once if they use multiple devices. | | Gender | Female, male, unknown | | | Source | Home recommendation, search, teleports, sponsored ads, etc. | | | Top countries | The top five countries where your experience has the most daily active users | Also includes an "other" category and total DAU. | | Top languages | The top five languages where your experience has the most daily active users | Also includes an "other" category and total DAU. | | When user first played | 0-30 days ago, 31-90 days ago, 91-180 days ago, 181-365 days ago, 365+ days ago, never | It's uncommon, but users can purchase a game pass and never actually play the experience. You might see these users on the revenue chart. | | Active payer status | Top 15%, intermediate 35%, casual 50%, lapsed, never | Percentile-based within your experience, not a fixed value. **Top 15%** includes users who fall into the 85-100th percentile of spenders for your experience, **Intermediate 35%** 50-84th, and **Casual 50%** 0-49th. **Lapsed** indicates users who haven't spent in the past 30 days, and **Never** is users who've never spent. Recalculated daily. See **Platform spender status** for status outside of your experience. | | Platform spender status | Active, other | Fixed value, not percentile-based. **Active** includes users who've spent $9.99 or more anywhere on Roblox in the last 60 days. **Other** includes all other users.

This definition is consistent with the [Creator Rewards program](/docs/en-us/creator-rewards.md), so you can better understand the behavior of players who earn you Daily Engagement Rewards.[^1] | When viewing a breakdown on a chart, you can click on the value in the legend to toggle the line on and off, or double click on one value in the chart's legend to view only that line. ## Sales data Sales data download is one of the Roblox analytics offerings that can help you view and analyze your asset and developer product sales in your desired way. ### Access sales data Roblox automatically generates a `.csv` (comma-separated values) file for your sales data and updates it every 48 hours. You can download and customize it to fit your analytics tactics. To access the file: 1. In your [Account Info Settings](https://www.roblox.com/my/account#!/info), add and verify your email address for receiving the data download link. 2. In your [Security Settings](https://www.roblox.com/my/account#!/security), set up an **Authenticator App** as the 2-Step Verification method for your account. 3. Navigate to your or your group's transactions page. 1. For your personal sales data, navigate to the [My Transactions](https://www.roblox.com/transactions) page. 2. For your group's sales data: 1. Navigate to the [Groups](https://www.roblox.com/groups) page and select the target group. 2. Click the **⋯** button on the group banner and select **Configure Group**. 3. In the navigation menu, hover over **Revenue** to show the dropdown and then select **Sales**. 4. From the **Type of Transaction** dropdown, select **Sales of Goods**. Then click the **Download Data** button. 5. On the prompted calendar, select a calendar month of sales data to download. You can choose any month from the current date to up to two years ago. If you choose the current month, the file will include all available data of the partial month. 6. In the inbox of your verified email address, you receive an email with a link to download a `.zip` file that contains your sales data in `.csv` format. The link will expire after 48 hours. 7. You can use the `.csv` sales data sheet to analyze the data in your desired way to meet your specific business needs. For example, you can filter to display only sale records with pending Robux to release to you, so you can calculate the timeline and amount of your sales payout to make decisions on when to [exchange earned Robux for real-world currency (DevEx)](https://en.help.roblox.com/hc/en-us/articles/203314100-Developer-Exchange-DevEx-Overview-How-to-Submit-Requirements) or pay your collaborators. ### Sales data attributes On the `.csv` sales data sheet, each sold item has the following attributes and metrics: | Attribute | Description | Example | | --- | --- | --- | | Buyer User ID | User ID of the user who purchased the item. | `123456789` | | Sale Date and Time | Date and time of the transaction in UTC. | `2022-07-15T19:04:30.397Z` | | Sale Location | The location in which the user bought the item, such as an experience and the Marketplace. | `WebSite`, `Marketplace`, `Game` | | Universe ID | The unique identifier of the experience that the purchased item belongs to. Only applicable for items that belong to an experience, like a pass. | `987654321` | | Universe | The name of the experience that the purchased item belongs to. Only applicable for items that belong to an experience, like a pass. | `My Awesome Experience` | | Asset ID | The unique identifier of the item. | `234565432` | | Asset Name | The name of the item. | `My Avatar Shirt`, `My Server` | | Asset Type | The type of the item, which can be a [Roblox asset type](/docs/en-us/projects/assets.md#asset-types) or a [developer product](/docs/en-us/production/monetization/developer-products.md) type. | `Game Pass`, `Private Server`, `Mesh` | | Hold Status | The status of whether Robux from this sale have been released to you or are still in a hold. | `Released`, `Held`, `Cancelled` | | Revenue | The amount of Robux you receive for the purchase, which is the item listing price excluding any [Marketplace fees](/docs/en-us/marketplace/marketplace-fees-and-commissions.md), Affiliate Fees, and Recurring Group Payouts. | `50` | | Price | The item listing price, which is the amount of Robux that the buyer paid for the purchase. | `10` |
--- title: "Server crashes chart" url: /docs/en-us/production/analytics/crashes last_updated: 2026-06-29T19:34:04Z description: "Explains how to identify the root cause of server crashes using the Crashes chart and server memory snapshots." --- # Server crashes chart The **server crashes chart** and **server out-of-memory snapshots** can help you identify the root cause of server crashes and improve your experience's stability. When a server detects it is running out of memory, the engine automatically captures a compact `JSON` summary of the data model before the server shuts down. If you see a spike in your server out-of-memory crashes, you can use the chart and memory snapshots to diagnose the issue: - **Select a snapshot**: Use the server out-of-memory snapshot list to find a crash, then examine memory usage by the latest timestamp, place ID, place version, server version, or server uptime to either find long-term memory leaks or line up the crash with reports from players or your own testing window. - **Visualize the memory usage**: Click on a snapshot to open the treemap viewer that shows nodes weighted by memory usage. You can drill into nodes to see which services, folders, or assets dominated memory at crash time. - **Investigate in Studio**: Use the breadcrumbs at the top of the viewer to highlight parent containers that hold potentially bloated assets. With this context, you can jump into Studio to investigate and fix the specific script or asset causing the issue. ![A view of the server crashes chart.](../../assets/analytics/crashes/crashes-chart.png) ## Access the chart To access the server crashes chart, you must either be the experience owner or have [analytics group permissions](/docs/en-us/production/analytics/analytics-dashboard.md#grant-group-permission). 1. Navigate to the [Creations](https://create.roblox.com/dashboard/creations) page on the **Creator Hub**. 2. Under the **Creator Hub** dropdown, select your account or the group that owns the experience. 3. Select the experience. 4. In the **Monitoring** dropdown, select **Crashes**. ## Use the chart The server crashes chart provides a deep-dive visibility into potential causes for server crashes. Crashes are broken down by crash reason: - **Out-of-memory crashes** - Servers are crashing due to high memory usage that you can take direct action to fix. - **Platform crashes** - Servers are crashing due to engine-level errors or corrupted memory when they enter an unrecoverable state. Roblox monitors and takes direct action to fix these crashes. > **Info:** If you see a significant uptick in platform crashes, [file a bug report](https://devforum.roblox.com/t/how-to-post-a-bug-report/24388). You can use the chart's menus to adjust date range, time interval, and annotations, and the **Place** menu to examine individual places rather than the entire experience. ![A close-up view of the server crashes chart's filters.](../../assets/analytics/crashes/crashes-chart-filters.png) Directly below the chart is a list of server out-of-memory snapshots that you can examine either by timestamp, server version, server uptime, place ID, or place version. When you select any of these snapshots, the treemap view displays with nodes weighted by memory usage. ![A close-up view of an example snapshot list and open memory snapshot.](../../assets/analytics/crashes/memory-snapshot.png) To demonstrate how you can use the chart and server out-of-memory snapshots to dianose a memory leak: 1. **Identify the spike**. When you notice out-of-memory crashes spiking on your server crashes chart, filter by place version so that you can confirm the location of your memory leak.![An example server crashes chart view with a spike highlighted.](../../assets/analytics/crashes/Investigate-Spike-Example.png) 2. **Select a snapshot**. Scroll down to the memory snapshots and examine the list by the latest timestamp or server uptime. If you select a snapshot from a server that crashed within minutes of starting up, this suggests a massive, acute memory spike during initialization.![An example server crashes chart view with a spike highlighted.](../../assets/analytics/crashes/Select-Snapshot-Example.png) 3. **Visualize the memory**. Open the snapshot in the treemap view, then click into each node to see which services, folders, or assets dominated memory at crash time. For example, the treemap view in the following video highlights Clouds as the largest node, then shows that ParticleEmitter makes up 50% of the Clouds node, indicating that heavy assets are being cloned into memory at startup but not properly parented or destroyed. 4. **Investigate and fix**. You can use the viewer's breadcrumbs to trace the context of the bloated node. Since certain assets are dynamically generated at runtime, you use this path as a starting point in Studio's Explorer window, add cleanup logic, then monitor your next update to verify the fix.![A memory snapshot with the breadcrumb highlighted.](../../assets/analytics/crashes/Investigate-Fix-Example.png) > **Info:** You can also download the raw CSV summary of the memory snapshots for your own analysis, including feeding the data into your favorite LLM or writing your own parsing script. --- title: "Custom events" url: /docs/en-us/production/analytics/custom-events last_updated: 2026-06-29T19:34:04Z description: "Custom Events let you track and aggregate unique metrics to your experience." --- # Custom events **Custom events** let you track metrics specific to your experience that other events do not fully capture. This includes: - **Adoption** — How many users click on a specific UI component? - **User behavior** — What is the most frequently used ability on each map? - **Core loop** — How do kill/death ratios compare across different weapons? Once your experience begins tracking custom events, you'll unlock the **Explore** page of the Analytics dashboard on the Creator Hub. You can add up to 100 custom events to your experience. ## Track custom events To unlock the **Explore** page, you must first track custom events in your experience. Start by identifying which metrics are the most important for monitoring and improving your experience. Events are aggregated daily so it may take up to 24 hours for charts to populate on the page. > **Warning:** Events can only be sent from the server and in published experiences. Events can't be sent from the client or Studio. ### Counters Counters are one-time events captured without a value. You can use counters for tracking the number of times a specific event has occurred. Use counters for single actions such as clicking a button, starting a quest, or using a tool. Events will automatically track both event count and unique user count. For value aggregations, counter events are treated as events with a default value of `1`. This means that aggregations like max/min/average will always equal `1`, and the sum will equal the total number of events. ```lua local AnalyticsService = game:GetService("AnalyticsService") AnalyticsService:LogCustomEvent( player, "MissionStarted" -- Event name ) ``` ### Events with values You can also track events with values for metrics that require more quantitative data into your experience, such as kill/death ratios or the amount of time a user spends on a mission. It can also be used as a way to send events in batches in order to stay under the rate limits, i.e. sending 10 zombies killed instead of 1 zombie killed ten times. ```lua local AnalyticsService = game:GetService("AnalyticsService") AnalyticsService:LogCustomEvent( player, "MissionCompletedDuration", -- Event name 120 -- Event value ) ``` ## Event aggregations All events metrics include the following aggregations and include slice-and-dicing support: - Count - Count unique user - Average value - Sum value - Min value - Max value - Average value per user View your event aggregations on the **Explore** page by selecting the aggregation type. ![A list of all the different aggregation options to filter your custom event data.](../../assets/analytics/event-types/Custom-Event-Aggregations.png) ## Use custom fields Custom events also allow breaking down on custom fields to support easier comparison between segments. For example, you can provide quest names to each event to see which ones users prefer the most, or attach player class to see if a class has a significantly higher kill/death ratio. You can breakdown by custom fields by using the breakdown selector. ![A dropdown indicating the three custom fields you can compare across, along with standard breakdowns like age, gender, operating system and more.](../../assets/analytics/event-types/Custom-Event-Breakdown.png) You should use custom fields whenever possible instead of event names, since there is a much tighter cardinality limit on event names than custom fields. Using custom fields also allows you to see visualizations of events across field values. For example, instead of `PlantCabbage`, `PlantTurnip`, `PlantPepper` as three separate events, you could have a single event with the name `PlantSeed` and custom field values `Plant - Cabbage`, `Plant - Turnip`, and `Plant - Pepper`. This way you can visualize both the total number of seeds planted as well as compare each plant in the same visualization. This also reduces your event name cardinality. For more information, see [Custom fields](/docs/en-us/production/analytics/custom-fields.md). ## Use custom events to grow your experience Custom events enable you to track metrics that matter most, providing insights into how players interact with specific features and content. Use these events to uncover patterns in player behavior and optimize your core game loop. In the reference game [Plant](/docs/en-us/resources/plant-reference-project.md), the core loop with a `HarvestPlant` event fires whenever a player harvests a ripe plant. Using custom events, you can track the average number of plants harvested per player each day broken down by the plant type as a custom field: ![Custom Event graph for the reference Plant experience.](../../assets/analytics/event-types/Custom-Event-Graph.png) - Try to improve the diversity of content within your experience and encourage players to explore other options as part of the [core loop](/docs/en-us/production/game-design/core-loops.md) to prevent repetitiveness. - Explore why users significantly prefer turnips over other plants, and if there are any imbalances that turnips are causing (such as with [economy events](/docs/en-us/production/analytics/economy-events.md)). - Add more event tracking within your loop, such as planting seeds, watering plants, and going to the shop, to better track player behavior and other areas of improvement. --- title: "Custom fields" url: /docs/en-us/production/analytics/custom-fields last_updated: 2026-06-29T19:34:04Z description: "Use custom fields as an additional analytics tool to track unique milestones in your experience." --- # Custom fields You can use up to three **custom fields** to filter your [economy](/docs/en-us/production/analytics/economy-events.md), [funnel](/docs/en-us/production/analytics/funnel-events.md), and [custom](/docs/en-us/production/analytics/custom-events.md) events by unique dimensions specific to your experience. Some examples include: - Levels — 1, 2, 3, . . . - Player class — Warrior, Mage, Archer - Weapon type — SMG, Pistol, Rocket Launcher The `customFields` parameter is a dictionary that lets you send up to three custom values using the provided `Enum.AnalyticsCustomFieldKeys` as keys by accessing them as `Enum.AnalyticsCustomFieldKeys.CustomField{01, 02, 03}.Name`. Anything other than `CustomField01.Name`, `CustomField02.Name`, and `CustomField03.Name` is ignored. You can have up to 8,000 unique combinations of values across the three custom fields. > **Info:** The **values** assigned to custom fields must be **strings**. If you need to log another data type like a boolean or number, pass it in string format. Using a fantasy-related experience as an example, you can track an economy event regarding equipment type, player class, and level with the following: ```lua local AnalyticsService = game:GetService("AnalyticsService") AnalyticsService:LogEconomyEvent( player, Enum.AnalyticsEconomyFlowType.Sink, "Coins", -- Currency name 80, -- Cost 20, -- Balance after transaction Enum.AnalyticsEconomyTransactionType.Shop.Name, "Obsidian Sword", -- Item SKU { [Enum.AnalyticsCustomFieldKeys.CustomField01.Name] = "Category - Weapon", [Enum.AnalyticsCustomFieldKeys.CustomField02.Name] = "Class - Warrior", [Enum.AnalyticsCustomFieldKeys.CustomField03.Name] = "Level - 10", } -- Custom field dictionary ) ``` Including the dimension name in the value is not required, but can help you remember the context later when reviewing your charts. Other keys in the `customFields` table are ignored. --- title: "Demographics" url: /docs/en-us/production/analytics/demographics last_updated: 2026-06-29T19:34:04Z description: "Explains how to interpret and improve demographics metrics for your experience." --- # Demographics **Demographics** shows you the age, gender, country, and language of monthly active users who play your experience. You can use this data to prioritize future content updates and secure brand sponsorships. To view your experience's demographic analytics: 1. Navigate to your [Creations](https://create.roblox.com/dashboard/creations) page on **Creator Dashboard** and select your experience. 2. In the **Analytics** menu on the left, select **Demographics**. You can view analytics for individual or group owned experiences. To view the latter, you need to have [group permissions for analytics](/docs/en-us/production/analytics/analytics-dashboard.md#grant-group-permission). ## View audience demographics On the demographics page you can see a breakdown of your monthly active users into the following categories: | Source | Description | | --- | --- | | Country | Percent of monthly active users by country. Hover over any country or bar to see the MAUs for that country and click the download button to get data for all countries. | | Gender | Percent of monthly active users by gender. | | Server frame rate | Frames per second processed by the server. | | Age | Percent of monthly active users by age. | | Top Languages | The top 10 languages and their percentages spoken by monthly active users. | > **Info:** For privacy reasons, if there are less than 3 users in any age or gender groups, the groups are hidden from the age and gender charts. __ ## Identify audience opportunities Consider the following strategies for using demographics data to improve your experience: - Prioritize content that your audience might be interested in. - [Localize](/docs/en-us/production/localization.md) your experience metadata such as titles with your audience in mind. - Identify opportunities to attract new audiences. - Share this data with potential brand sponsors. --- title: "Economy events" url: /docs/en-us/production/analytics/economy-events last_updated: 2026-06-29T19:34:04Z description: "Use economy events to visualize your experience's economy and track user sources, sinks and wallets." --- # Economy events **Economy events** let you track your in-experience economy, such as: - **Top sinks** — What do users spend in-experience resources on? - **Top sources** — Where do users earn resources? - **Average wallet balance** — How much resources are users holding? Once your experience begins tracking Economy events, you'll unlock the Economy page of the Analytics dashboard on the Creator Hub. ## Track economy events To unlock the Economy dashboard, you need to track some economy events in your experience. Start by identifying where users **source** (i.e. gain) and **sink** (i.e. spend) resources in your experience. These are represented in code by `Enum.AnalyticsEconomyFlowType`, which can be either `Source` or `Sink`. > **Warning:** Events can only be sent from the server and in published experiences. Events can't be sent from the client or Studio. ### Transaction types Each source and sink event requires a transaction type, encoded with `Enum.AnalyticsEconomyTransactionType`. By default, the options are: - `IAP` (source) - In-app purchases exchanging Robux for resources, e.g. starter pack. - `TimedReward` (source) - Earn resources on a schedule, e.g. daily bonus. - `Onboarding` (source) - Get resources when getting started, e.g. welcome bonus. - `Shop` (source or sink) - Trade resources in the shop, e.g. item purchase. - `Gameplay` (source or sink) - Earn or spend resources from gameplay, e.g. quest completion. - `ContextualPurchase` (sink) - Spend resources on a context-specific impulse, e.g. extra lives. These types appear on the dashboard. It's a good idea to start with the default categories, but if you need to you can also provide your own transaction type names when logging an event. ### Track sources The following sample uses `Class.AnalyticsService.LogEconomyEvent` to log two different economy events when users complete the first and second levels in the experience and earn some coins. ```lua local AnalyticsService = game:GetService("AnalyticsService") -- After level 1 completion AnalyticsService:LogEconomyEvent( player, Enum.AnalyticsEconomyFlowType.Source, "Coins", -- Currency name 50, -- Amount earned 50, -- Current balance Enum.AnalyticsEconomyTransactionType.Gameplay.Name -- Transaction type ) -- After level 2 completion AnalyticsService:LogEconomyEvent( player, Enum.AnalyticsEconomyFlowType.Source, "Coins", -- Currency name 50, -- Amount earned 100, -- Balance after transaction Enum.AnalyticsEconomyTransactionType.Gameplay.Name -- Transaction type ) ``` The following sample tracks a Robux purchase of a 1000-coin bundle, using the `IAP` (in-app purchase) transaction type, this time with an `itemSKU`, your unique identifier for the item. `itemSKU` is optional. If you don't specify it, the Economy dashboards display `N/A` in the sources and sinks table. ```lua local AnalyticsService = game:GetService("AnalyticsService") AnalyticsService:LogEconomyEvent( player, Enum.AnalyticsEconomyFlowType.Source, "Coins", 1000, -- How many coins are in the bundle 1020, -- Balance after transaction Enum.AnalyticsEconomyTransactionType.IAP.Name, "1000CoinBundle" -- Unique item SKU identifier of the coin bundle ) ``` In this example, if you have many coin bundles, you might also want to add a [custom field](/docs/en-us/production/analytics/custom-fields.md) to track the coin bundle category against other categories. ### Track sinks The following sample logs an event when users spend coins to buy a `DoubleJumpUpgrade`. Note the `Sink` flow type and the `Shop` transaction type when compared to the source tracking samples. Keep in mind that the amount (cost) should always be a positive number regardless of whether the event is a source or a sink. The economy dashboard charts will automatically show sinks as negative numbers. ```lua local AnalyticsService = game:GetService("AnalyticsService") -- After level 2 completion AnalyticsService:LogEconomyEvent( player, Enum.AnalyticsEconomyFlowType.Sink, "Coins", -- Currency name 80, -- Cost 20, -- Balance after transaction Enum.AnalyticsEconomyTransactionType.Shop.Name, "DoubleJumpUpgrade" -- Item SKU ) ``` For information on `Class.AnalyticsService` limitations, see [Event tracking limitations](/docs/en-us/production/analytics/event-types.md#event-tracking-limitations). ## Use custom fields Economy events also allow breaking down on custom fields to support easier comparison between segments. For example, you can provide quest names to each event to see which ones users are making the most money from, or attach store locations to see if users prefer one location over another. You can breakdown by custom fields by using the breakdown selector. ![A dropdown indicating the three custom fields you can compare across, along with standard breakdowns like age, gender, operating system and more.](../../assets/analytics/event-types/Custom-Event-Breakdown.png) For more information, see [Custom fields](/docs/en-us/production/analytics/custom-fields.md). ## Use economy to grow your experience The Economy dashboard includes five charts to help you take action to grow your revenue. You can add up to five currencies of resources, and all charts can be filtered by various user categories (gender, age, platform, etc.) and up to three custom fields specific to your experience. - **Total sources and sinks by category** - Use this chart to balance your in-experience economy. Total sources subtract total sinks should be close to zero. You can also see your top sources and sinks by category. If your net total is growing, consider adding more sinks.![Total Sources and Sinks by Category chart populated with data.](../../assets/analytics/event-types/Total-Sources-Sinks.png) - **Average wallet balance** - Use this chart to see how many resources users, payers, and non-payers hold on average. If average balance is growing, especially for payers, consider adding new sinks.![Average Wallet Balance chart populated with data.](../../assets/analytics/event-types/Average-Wallet.png) - **Top sources and sinks** - Use this to identify where your users gain and spend their resources. If users are not sourcing resources from in-app purchases, consider reducing prices or offering new options. If users are not spending enough of a given resource, consider adding new sinks for that resource.![Top Sources and Sinks chart populated with data, open to the Sources tab.](../../assets/analytics/event-types/Top-Sources-Sinks.png) - **All sources and sinks** - Use this to get a combined view by category for the selected date range.![All Sources and Sinks chart populated with data.](../../assets/analytics/event-types/All-Sources-Sinks.png) --- title: "Engagement" url: /docs/en-us/production/analytics/engagement last_updated: 2026-06-29T19:34:04Z description: "Explains how to improve engagement metrics for your experience." --- # Engagement **Engagement** measures how engaging your experience is for users. ## View engagement metrics To view your experience's engagement analytics: 1. Navigate to your [Creations](https://create.roblox.com/dashboard/creations) page on **Creator Dashboard** and select your experience. 2. In the **Analytics** menu on the left, select **Engagement**. You can view analytics for individual or group owned experience. To view the latter, you need to have [group permissions for analytics](/docs/en-us/production/analytics/analytics-dashboard.md). ## Improve average session time Average session time is the total time users spend in your experience divided by the number of sessions. It's a key metric that's closely tied to retention. To ensure that users are having meaningful sessions, you need to focus on the efficiency and effectiveness of engaging your users. ### Get users into the fun immediately Usually, the friction of joining and leaving an experience is very low on Roblox, so first impressions are extremely important, and you want to get users into the fun part of your experience as fast as possible with the following strategies: 1. **Ensure users have fun within the first five minutes of joining your experience**: Think about what makes your experience fun and what makes it stand out from other experiences. You should be able to communicate these to your users within the first five minutes of joining your experience. 2. **Get users into the action as soon as possible and avoid long tutorials**: If you lock all the fun of your experience behind a lengthy tutorial, users might get bored and move on to something else. Make learning contextual and action based, such as short, unobtrusive pop-up instructions when a user encounters a new feature and tooltips about the purpose of items in the shop. Also consider giving users some starter items and currency to help them enjoy the core loop as soon as possible. 3. **Remove unnecessary barriers to social interaction**: Many successful experiences on Roblox highlight socializing with friends and other users a core part of their design. Try to design your experience to support group play and social interaction where possible. ### Give users a reason to keep coming back Regardless of what type of experiences you are designing, you want to design for repeat visits. You can adopt the following strategies to motivate your users to continually use your experience: 1. **Give users clear goals to work towards**: Think about what aspirational goals that your users might work for, such as completing a pet collection, getting the highest rank, and getting a rare item. Make sure users have clear short, mid, and long-term goals to work towards. 2. **Add a progression system to make content last**: Progression systems give users a clear path toward their goals and can make content last longer, so you have time to make more content before users unlock them. 3. **Balance the progression system to not gate off the fun**: If you add progression, make sure there is still enough content available upfront to ensure new users can still have fun in their first session. ### Monitor your experience's performance Performance is how well your experience runs on different devices and platforms. To improve your experience's performance: 1. **Test on various devices and platforms** to identify and fix performance issues. 2. **Limit excessive textures, meshes, and other variables** that might slow down your experience. 3. **Set up a user community** that can help identify bugs and crashes. You can grow your experience faster if it's more engaging to both new and existing users. For more information on retaining your users for engagement, see [Retention](/docs/en-us/production/analytics/retention.md). ## Improve new user first session retention The New User First Session Retention chart shows how many new users are still playing X minutes after joining your experience for the first time. It compares the current period with the previous period. You can use it to generate insights and catch issues with your experience's onboarding. For instance, if your chart shows that the percentage of new users still playing after 5 minutes decreased significantly week-on-week, you could explore the following potential causes: 1. **Look into new user onboarding friction** from any recent updates. Did you add a new step, or change any introductory messaging? 2. **Check [error](/docs/en-us/production/analytics/error-report.md) and [performance](/docs/en-us/production/analytics/performance.md) reports** to see if there has been any change in performance or stability after a recent update. --- title: "Error Report" url: /docs/en-us/production/analytics/error-report last_updated: 2026-06-29T19:34:04Z description: "Explains how error reports can help you improve your experience." --- # Error Report **Error Report** lets you view up-to-the-minute Luau system errors and warnings for both server and client. Monitor your error report before and after updating your experience to identify potential issues early. ## View your error report To view your experience's error report: - Navigate to your [Creations](https://create.roblox.com/dashboard/creations) page in the **Creator Dashboard** and select your experience. - Under **Monitoring**, select **Error Report**. You can view analytics for individual or group-owned experiences. To view the latter, you need to have [group permissions for analytics](/docs/en-us/production/analytics/analytics-dashboard.md). ## Monitor errors and warnings You can apply the following filters and toggles to your error report: - **Date range** — Filter to last 1 hour, last 1 day, last 7 days, or last 30 days. - **Place** — Filter by place. - **Show version** — Adds place version annotations to the errors and warnings chart. - **Severity** — Filter to show errors, warnings, or both. - **Platform** — Filter to show client errors from specific devices. - **OS** — Filter to show client errors from specific operating systems. - **New errors since** — Filter to show only new errors and warnings since a certain version. Below the filters and toggles, a chart displays the numbers of errors and warnings for server, client, and total. Use this chart to look for increases in errors and warnings over time. If you see a large spike in errors and warnings, use the error report table to troubleshoot. _Errors and warnings shown over time by category_ ## Troubleshoot errors and warnings The **Errors and warnings** table has the following columns: | Column | Description | | --- | --- | | Count | Number of errors or warnings. | | Severity | Error or warning label. | | Type | Server or client label. | | Message | Error or warning message. | | First seen at | First seen timestamp of the message. | | First seen version | Only available when a place is picked. Shows the first version where the message was recorded. | _The error report table_ ### View the stack trace for an error or warning If you see a dropdown on an error or warning, click to expand it and see its corresponding **stack trace**. A stack trace is a list of the calls that your experience was performing when the exception was thrown. This can help you figure out what's going wrong. ## Custom rules Custom rules let you define regex or exact string patterns to automatically ignore or group errors and warnings. This helps you manage the signal-to-noise ratio of your error reports. You can create up to 100 rules per experience. Rules are evaluated top-to-bottom in priority order. Once an error matches a rule, further processing stops. ### Rule actions Each rule has one of two actions: | Action | Behavior | | --- | --- | | Ignore | Hides matching errors from the chart and table. Use for permanent noise like unavoidable engine sound errors or legacy print statements. | | Group | Consolidates all matching errors under a custom group name (e.g., "Network Issues", "Audio Spam"). The group name appears as a single row in the table. | ### Create a rule There are two ways to create a rule: **From the error table (inline):** 1. Click the context menu (three dots) on any error row. 2. Select **Ignore**. _Ignoring an error from the error table_ **From the rules tab:** 1. Navigate to the **Rules** tab on the Error Reports page. 2. Click **Create Rule**. 3. Enter a pattern (exact string or regex). 4. Select an action: **Ignore** or **Group** → [Group Name]. 5. Save the rule. _The Rules tab showing example rules_ ### Rule creation fields | Field | Description | | --- | --- | | Pattern | The exact string or regular expression to match against error messages. | | Action | Choose **Ignore** (hide the error) or **Group** → enter a custom group name. | _The Create rule dialog_ Rules are evaluated from top to bottom based on the priority order in the Rules tab. Once an error matches a rule, that rule's action is executed and no further rules are evaluated for that error. This prevents conflicts such as an error being grouped by one rule and then ignored by a subsequent rule. Rules only impacts errors logged after they are saved. An annotation appears on your chart when rules are modified. ### Regular expression basics A regular expression (regex) is a pattern that matches text: | Syntax | Meaning | Example | | --- | --- | --- | | `.` | Any single character | `H.t` matches "Hat", "Hit", "Hot" | | `*` | Zero or more of the preceding character | `go*d` matches "gd", "god", "good" | | `+` | One or more of the preceding character | `go+d` matches "god", "good", but not "gd" | | `.*` | Any sequence of characters (wildcard) | `Failed.*load` matches "Failed to load" and "Failed xyz load" | | `\d` | Any digit (0-9) | `v\d+` matches "v1", "v123", "v4488" | | `\w` | Any word character (letters, digits, underscore) | `\w+` matches "Player123", "my_var" | | `^` | Start of the string | `^HTTP` matches errors starting with "HTTP" | | `\/` | An escaped forward slash | `rbxassetid:\/\/` matches "rbxassetid://" | | `\[` and `\]` | Escaped square brackets (literal match) | `\[Knit\]` matches "[Knit]" | | `(a\|b)` | Match "a" or "b" | `(Error\|Warning):.*` matches both "Error: ..." and "Warning: ..." | Quick tips: - Start with an exact match (the full error string) if you only want to catch one specific error. - Use a regex when you want to catch a family of similar errors (e.g., all sound loading failures regardless of asset ID). - Test your pattern against a few real error strings before saving to make sure it catches what you expect. The system enforces two rules on your regex patterns: - You cannot put a repeater (`+` or `*`) inside a repeating group. For example, `(a+)*` or `(\w+)+` are rejected. - Must start with anchored text: Patterns cannot start with a wildcard like `.*` or `.+`. Your pattern must begin with a concrete character or anchor (like `^` or a literal string). ### Rule examples to organize your error reports Instead of seeing dozens of separate error rows, consolidate related errors under one group name: | Use case | Example errors | Pattern | Action | | --- | --- | --- | --- | | All network errors | `HttpError: ConnectFail`, `HttpError: Timedout` | `HttpError:.*` | Group → "Network Issues" | | DataStore warnings | `DataStore request was added to queue`, `DataStoreService: ...` | `DataStore` | Group → "DataStore Warnings" | | Player GUI errors | `Players.Player1.PlayerGui.Shop:12: attempt to index nil` | `Players\.\w+\.PlayerGui` | Group → "PlayerGui Errors" | | Custom UI framework | `[UIManager] Button render failed`, `[UIManager] Layout overflow` | `\[UIManager\].*` | Group → "UI System" | ## Error tracking limits The system tracks up to 500 unique errors and 500 unique warnings by count and the top 100 new errors per version. These counts reset every 6 hours. To maximize useful error tracking within these limits: - Remove unique identifiers from error messages, such as user IDs, coordinates, or timestamps. Errors like `Player 12345 failed to load` and `Player 67890 failed to load` count as two separate entries. If you log them as `Player failed to load`, they consolidate into one entry with a higher count. - Use custom rules to group related errors that differ only by dynamic values. ## Resolve errors and warnings When investigating errors from the Error Reports page, use the following approaches: 1. **Review stack traces** — Expand error rows to see the call path and identify the exact script and line number where the error occurred. 2. **Check for recent updates** — Monitor new errors since your most recent update. 3. **Use built-in tools**: - [Developer Console](/docs/en-us/studio/developer-console.md) (`F9` in-experience): View live errors in real time and test fixes. - [Script Profiler](/docs/en-us/studio/optimization/scriptprofiler.md): Identify performance-related errors caused by expensive operations. - **Output Window in Studio** — Catch errors during development before publishing. 4. **Use breakdowns** — Check if the error is specific to a platform (Desktop, Mobile, Console) or OS (Windows, macOS, iOS, Android). 5. **Gather community feedback** — If you have a community Discord or DevForum post, check whether players are reporting the issue and which devices or regions are affected. --- title: "Event types" url: /docs/en-us/production/analytics/event-types last_updated: 2026-06-29T19:34:04Z description: "Overview of using event types to track user sources, sinks and milestones in your experience." --- # Event types Event types help you identify opportunities to grow and monetize your experience. They allow you to integrate event tracking into your experience and visualize your in-experience economy and player usage patterns on the Analytics dashboard After you begin tracking these events from your experience using `Class.AnalyticsService`, you unlock new [economy](/docs/en-us/production/analytics/economy-events.md), [funnel](/docs/en-us/production/analytics/funnel-events.md), and [custom](/docs/en-us/production/analytics/custom-events.md) dashboards under Analytics on the Creator Dashboard. You can use these dashboards to identify growth opportunities and segment users by age, gender, platform, OS and custom fields. All of these features are **free to use**. ![Economy and Funnels dashboard pages populated with data.](../../assets/analytics/event-types/Overview-Economy-Funnels.png) ## Event types Roblox provides three sets of analytic dashboards you can use to track different aspects of your experience: 1. **Economy events** let you track your in-experience economy, such as: - **Top sinks** — What do users spend in-experience resources on? - **Top sources** — Where do users earn resources? - **Average wallet balance** — How many resources are users holding? 2. **Funnel events** let you track your user's progress through key stages of your experience, such as: - **Onboarding** — Where do users drop off when getting started with your experience? - **Progression** — Where do users stop advancing through your experience? - **Shop** — Where do users abandon purchases? 3. **Custom events** let you track metrics specific to your experience, such as: - **Adoption** — How many users click on a specific UI component? - **User behavior** — What is the most frequently used ability on each map? - **Core loop** — How do kill/death ratios compare across different weapons? For more information on setting up these dashboards, see [Economy events](/docs/en-us/production/analytics/economy-events.md), [Funnel events](/docs/en-us/production/analytics/funnel-events.md) and [Custom events](/docs/en-us/production/analytics/custom-events.md). ## Validate your event tracking Once you add [economy](/docs/en-us/production/analytics/economy-events.md), [funnel](/docs/en-us/production/analytics/funnel-events.md), or [custom](/docs/en-us/production/analytics/custom-events.md) events to your experience, charts on the respective pages typically take 24 hours to appear. In the meantime, you can check if events are set up correctly using the **View Events** tool: 1. Navigate to the **Economy**, **Funnel**, or **Custom** pages of your Analytics dashboard for your experience. 2. Click the **View Events** button at the top of each page. A near real-time list of the most recent events displays. 3. Refresh the page to update the list. ![View events report displaying multiple event entries sortable by event type, user ID and keyword.](../../assets/analytics/event-types/Event-Tracking.png) If you see more events than you were expecting, verify that your calls to the `Class.AnalyticsService` methods occur **after** a successful operation. For example, you should call `Class.AnalyticsService:LogEconomyEvent()|LogEconomyEvent()` after a successful purchase, not when the user attempts a purchase (which could fail due to lack of funds). You can also visit your experience's [error report](/docs/en-us/production/analytics/error-report.md) to see if there are any errors with your event tracking. ## Event tracking limitations The following limitations apply when tracking your events with `Class.AnalyticsService`. Limitations reset daily. You will be able to send new events the next day once you stop sending previous events. Each event remains visible on the Creator Dashboard and automatically rolls off after 90 days from the last data received. > **Warning:** You can leverage [custom fields](/docs/en-us/production/analytics/custom-fields.md) to additionally filter your analytic events. > > For example, instead of tracking `WarriorXP`, `MageXP`, `PaladinXP` as separate economy currencies, set `XP` as one currency and set `Warrior`, `Mage`, `Paladin`, `...`, as Custom Field 1. This allows you to break down these values on your dashboards and not use up your currency cardinality. | | **Limitation** | **Maximum value** | **Examples** | | --- | --- | --- | --- | | Global Rate Limit | Total `Class.AnalyticsService` requests per minute | 120 + (20 * CCU) | | | | | | | | Economy, Funnel, and Custom events | Number of [custom fields](/docs/en-us/production/analytics/custom-fields.md) | 3 | Class, Level, Weapon | | Unique values per [custom field](/docs/en-us/production/analytics/custom-fields.md) | Unlimited — After 8,000 combined values across all custom fields, values will be grouped as "Other" | Warrior, Mage, Archer | | Economy only | Resource types | 10 | Coins, Gold, Credits | | transactionTypes | Unlimited — After 20, values will be grouped as "Other" | IAP, Gameplay | | itemSkus | Unlimited — After 100, values will be grouped as "Other" | | | Funnel only | Number of funnels | 10 | Onboarding, Shop | | Steps per funnel | 100 | Step 1, Step 2, Step 3 | | Custom only | eventNames | 100 | MonsterDefeated, KillDeathRatio, PetAdoption | --- title: "Feedback" url: /docs/en-us/production/analytics/feedback last_updated: 2026-06-29T19:34:04Z description: "How to monitor and disseminate player feedback for your experience." --- # Feedback Listening to feedback from players and the community is a great opportunity to improve your experience — what they love, what they wish could be improved, etc. The **Feedback** dashboard is one tool that can help you disseminate player feedback. > **Warning:** This dashboard is new and only includes data from a limited time period. Experience upvotes/downvotes are factored from February 1, 2025 and onward, while comments are logged from March 17, 2025 and onward. To view your experience's feedback analytics: 1. Navigate to your [Creations](https://create.roblox.com/dashboard/creations) page and select your experience. 2. In the **Audience** menu on the left, select **Feedback**. > **Info:** You can view feedback for both individual and [group](/docs/en-us/projects/groups.md) owned experiences. To view the latter, you need to have the **View all analytics for group experiences** permission within the group. To dive deeper into player feedback, see the [feedback insight reports](/docs/en-us/production/analytics/insights.md#player-feedback-reports). ## Chart view The **chart view** shows a detailed breakdown of ratings on your experience, showing trends in sentiment over time. This view: - Shows trends in player sentiment over time. - Helps you identify issues like a new update causing a sudden spike in downvotes. - Helps you make improvements and monitor how those changes influence player sentiment. In the chart, you can adjust the range of total feedback using the **Date Range** selector, allowing you to view feedback within 7/30/60/90 days, or select a specific custom date range. ![Image showing the date range selector set to 30 days and the reflected 30-day data values in the chart below.](../../assets/analytics/feedback/Date-Range-Selector.png) Above the chart is a summary of the feedback across the selected date range: ![Image showing total feedback and percent upvotes over selected period.](../../assets/analytics/feedback/Feedback-Breakdown.png) --- **Total feedback** — Total votes (upvotes and downvotes) for the date range. **Percent upvotes over selected period** — Percentage of upvotes vs. downvotes for the range. > **Info:** Note that players can change their rating at any time. Also note that ratings update at the end of each day and may not reflect the current core rating on your experience. To the right of each core value is a **relative** percentage of improvement or regression based on the previous range. For example, a relative percentage for the last 30 days shows improvement/regression for those 30 days as compared to the 30 days before that. Hovering over a relative percentage reveals the exact date range used for calculation: ![Image showing relative percentage of improvement based on the previous range.](../../assets/analytics/feedback/Relative-Percentage.png) ## Detailed view The detailed feedback table shows specific feedback submitted by players who have played and rated it from the experience's detail screen. ![Image showing feedback widget on an experience's detail screen.](../../assets/analytics/feedback/Feedback-Widget.png) Each row in the feedback table includes: - **Rating** — Upvote (positive) or downvote (negative). This reflects the player's rating at the time they provided the feedback, although they may change their rating later. - **Device Type** — Which device type the feedback was provided from: computer, phone, tablet. - **Platform** — Which OS or platform related to the device, for example iOS or Windows. - **Comment** — Specific player feedback related to the vote. For safety reasons, all comments are sent through a text filter and will not appear if they get filtered. ![Image showing example detailed feedback including the rating, device type, platform, and reviewer's feedback.](../../assets/analytics/feedback/Detailed-View.png) > **Success:** Feedback comes in many shapes and sizes. Some players may leave a downvote but then provide useful suggestions in their comments. Make sure you read all the feedback! Hovering over any row and clicking the options button (`⁝`) reveals the option to report feedback and hide it from your dashboard. Once reported, you will not be able to see the feedback again. ![Image showing options button for feedback.](../../assets/analytics/feedback/Feedback-Options-Button.png) ## Data export To the upper-right of both the [chart](#chart-view) and [detailed](#detailed-view) views, the **export** button allows you to export feedback data in `.csv` format, useful for parsing or processing data in external applications. ![Image showing export button for chart data.](../../assets/analytics/feedback/Export-Chart-Data.png)![Image showing export button for detailed data.](../../assets/analytics/feedback/Export-Detailed-Data.png) --- title: "Funnel events" url: /docs/en-us/production/analytics/funnel-events last_updated: 2026-06-29T19:34:04Z description: "Use funnel events to visualize user progression through key milestones in your experience." --- # Funnel events **Funnel events** let you track your user's progress through key stages of your experience. This includes: - **Onboarding** — Where do users drop off when getting started with your experience? - **Progression** — Where do users stop advancing through your experience? - **Shop** — Where do users abandon purchases? Once your experience begins tracking funnel events, you unlock the Funnel page of the Analytics dashboard on the Creator Hub. You can add tabs to the dashboard for up to ten funnels. ## Track funnel events To track funnel events, first identify the most important funnels in your experience and segment them into steps. Because the goal is to understand player behavior, these steps usually correspond to some action or achievement. In an RPG, you might have a step for joining the experience, one for choosing a class, another for exiting the starting room, and so on. You typically call `Class.AnalyticsService:LogFunnelStepEvent()|LogFunnelStepEvent()` or `Class.AnalyticsService:LogOnboardingFunnelStepEvent()|LogOnboardingFunnelStepEvent()` **after** a player completes a step, but the most important thing is to be consistent about when you call these methods and use easy-to-interpret names. Your onboarding flow is a great place to start tracking funnel events, as this is likely where you lose the majority of your users. > **Warning:** Events can only be sent from the server and in published experiences. Events can't be sent from the client or Studio. ### Track one-time funnels A one-time funnel monitors conversion events that only occur once per user. A common example is an **onboarding funnel**, which is critical to understand how to improve your experience's new user retention and session time. The following example is an onboarding funnel for [Plant](https://create.roblox.com/docs/resources/plant-reference-project), a tycoon experience where new users enter a farm, plant seed, water plant, and more in sequential order: ```lua local AnalyticsService = game:GetService("AnalyticsService") -- Log the first step of the FTUE AnalyticsService:LogOnboardingFunnelStepEvent( player, 1, -- Step number "In Farm" -- Step name ) -- Log the second step AnalyticsService:LogOnboardingFunnelStepEvent( player, 2, -- Step number "Plant Seed" -- Step name ) ``` ### Track recurring funnels A recurring funnel monitors conversion events that occur multiple times per user. A common example is a **shop funnel**, which can help you understand how to improve your experience's payer conversion, ARPPU, and revenue. The following example is a shop funnel where users open store, view item, add item to cart, and more. Use `funnelSessionId` to distinguish between different sessions of the same user in a recurring funnel, such as opening the shop multiple times in a single session. ```lua local AnalyticsService = game:GetService("AnalyticsService") local HttpService = game:GetService("HttpService") local funnelSessionId = HttpService:GenerateGUID() -- Log when the user opens the store AnalyticsService:LogFunnelStepEvent( player, "ArmoryCheckout", -- Funnel name used to group steps together funnelSessionId, -- Funnel session ID for this unique checkout session 1, -- Step number "Opened Store" -- Step name ) -- Log when the user views an item AnalyticsService:LogFunnelStepEvent( player, "ArmoryCheckout", -- Funnel name used to group steps together funnelSessionId, -- Funnel session ID for this unique checkout session 2, -- Step number "Viewed Item" -- Step name ) -- Log when the user adds to cart AnalyticsService:LogFunnelStepEvent( player, "ArmoryCheckout", -- Funnel name used to group steps together funnelSessionId, -- Funnel session ID for this unique checkout session 3, -- Step number "Added to Cart" -- Step name ) ``` ### Implement funnelSessionId When implementing funnels, a `funnelSessionId` can help you track your events but may not be required in every instance. Use the following guidelines: - **One-time funnels** - You don't need to use `funnelSessionId` for one-time funnels because they only occur once per user. - **Store funnels** - Use `funnelSessionId` to distinguish between different sessions of the same user in a recurring funnel, such as opening the shop multiple times in a single session in the [earlier example](#track-recurring-funnels). In cases like this, where the player may open the shop multiple times in a single session, it is recommended to use a GUID as the `funnelSessionId`. - **Item upgrades** - Use `funnelSessionId` to distinguish between different item upgrade paths, generally over a longer time period than a single play session. Rather than use a GUID as in the store funnel case, you can often build a unique key based on the item being upgraded, for example: `-`. ## Initial step Funnels start when the first step is logged. If you want to start a funnel immediately on player join, log the first step on the `PlayerAdded` event. ```lua local AnalyticsService = game:GetService("AnalyticsService") local Players = game:GetService("Players") Players.PlayerAdded:Connect(function(player) AnalyticsService:LogOnboardingFunnelStepEvent( player, 1, -- Step number "Player Joined" -- Step name ) end) ``` ## Repeated steps If a user repeats a step in a funnel, the funnel only considers the first instance of the step. For example, if a user logs step 2 of a funnel twice, the funnel only counts the first instance of step 2. You don't have to take any special action to handle one-time funnels for players who disconnect and reconnect, and `funnelSessionId` handles recurring funnels. Repeated steps are ignored by the funnel, but they're still logged and contribute towards the [global rate limit](/docs/en-us/production/analytics/event-types.md#event-tracking-limitations). ## Skipped steps If for some reason you skip a step in a funnel, the earlier steps automatically complete. For example, if you have a funnel with steps 1, 2, and 3 and you log step 3 without logging steps 1 or 2, the funnel considers steps 1 and 2 as completed. ## Use funnel filters Roblox provides filters to help you analyze your funnel data. These include player data and device data, and you can send custom data as well. In some cases, a player's status may change during the funnel, such as when the player switches devices from mobile to desktop. To avoid double-counting funnels, filters **only apply to the first step** of the funnel. This means that if a player switches devices during the funnel, the funnel will only be attributed to their device at the time they enter the funnel. Similarly, funnels display by cohort, meaning that if a player enters the funnel on 6/19, the funnel will be attributed to the 6/19 cohort even if they complete the funnel on 6/20. ## Modify funnels After you make an update to your funnel steps, it's important to set the correct date range to see the latest funnel. If the current date is 6/21 and you updated step 2 of your onboarding funnel on 6/14, you should set the date range to 6/14 – 6/21 to view the latest funnel. If you select a date range that includes a funnel step update, a warning displays on the relevant step: ![A warning displays on the funnel dashboard indicating a name change within the selected date range.](../../assets/analytics/event-types/Plant-Game-Warning.png) ## Protect your funnels from exploiters In order to keep your data clean, it is important to add some level of data validation in your server code to prevent exploiters from sending invalid data to your analytics service. For example, if you have an onboarding funnel with three steps, you can use a `RemoteEvent` for the client to notify the server when the player has completed each step and add a server check to ensure that the step number is valid before logging the event: ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local onboardingEvent = ReplicatedStorage:WaitForChild("OnboardingEvent") local function fireOnboardingEvent(step: number) onboardingEvent:FireServer({ step = step }) end fireOnboardingEvent(1) fireOnboardingEvent(2) fireOnboardingEvent(10) -- invalid step ``` ```lua local AnalyticsService = game:GetService("AnalyticsService") local ReplicatedStorage = game:GetService("ReplicatedStorage") local onboardingEvent = ReplicatedStorage:WaitForChild("OnboardingEvent") local maxStep = 3 local function onPlayerEventFired(player: Player, args: { step: number }) local step = args.step if(step > maxStep) then warn(\`Invalid tutorial step {step} received from client.\`) return end print(\`{player.Name} completed step: {step}\`) AnalyticsService:LogOnboardingFunnelStepEvent(player, step) end onboardingEvent.OnServerEvent:Connect(onPlayerEventFired) ``` ## Use custom fields Funnel events also allow breaking down on custom fields to support easier comparison between segments. For example, you can track which starter car gives players the best progression, or attach different maps to see if a certain map has a better game loop than others. ![A dropdown indicating the three custom fields you can compare across, along with standard breakdowns like age, gender, operating system and more.](../../assets/analytics/event-types/Custom-Event-Breakdown.png) For more information, see [custom fields](/docs/en-us/production/analytics/custom-fields.md). ## Use funnels to grow your experience One of the most important funnels to track is onboarding because many experiences struggle with new user retention and engagement. In the onboarding funnel for [Plant](https://create.roblox.com/docs/resources/plant-reference-project) below, the largest drop-off is step 2 ("Plant Seed"). ![Funnels chart for Plant experience showing a 70% drop-off between In Farm, step 1, and Plant Seed, step 2.](../../assets/analytics/event-types/Plant-Game-Funnel.png) Based on this data, you could: - Add contextual indicators to better direct users to plant seeds when they're getting started. - Design a new user experience that requires users to plant seed and grow a successful plant before exploring the rest of the experience. You can improve this event by creating [positive feedback elements](/docs/en-us/production/game-design/onboarding-techniques.md#facilitate-feedback) or using other game design techniques. ![In-experience view of Plant experience showing prompts to plant seeds above the flowerpots.](../../assets/analytics/event-types/Plant-Game-Prompts.png) --- title: "Get started with analytics" url: /docs/en-us/production/analytics/get-started last_updated: 2026-06-29T19:34:04Z description: "Explains how to interpret and improve acquisition metrics for your experience." --- # Get started with analytics Analytics help you understand how players interact with your experience so you can make better design decisions. Instead of relying on guesswork or anecdotal feedback, you can use metrics to identify patterns, evaluate changes, and continuously improve your experience. This guide explains how to think about analytics: how metrics can answer questions about your players, how they help you ask better questions, and how to use them when deciding what to build next. ## Use metrics to answer questions about your players Metrics provide signals about what players are doing in your experience. By tracking key behaviors, you can answer questions such as:
**Are players enjoying a specific part of the experience?**
- Look at early-session retention or completion rates for onboarding content. - If you notice that many players leave shortly after joining, that metric may indicate an issue with onboarding, difficulty spikes, or unclear goals. - Measure how often players interact with recently added systems or content. - For more information, see [engagement metrics](#engagement-metrics).
**Where do players stop progressing?**
- Funnel or progression metrics can show where players drop off. - For more information, see [retention metrics](#retention-metrics).
**Are monetization systems working as intended?**
- Track purchase rates, engagement with stores, or reward redemption. - For more information, see [monetization metrics](#monetization-metrics).
Metrics alone don't always provide the full explanation, but they help reveal where to look more closely. Once you understand how to leverage metrics to answer questions about your users, dive into specific user cohorts using [segmentation and filtering](#segmentation-and-filtering) filters and test any changes [incrementally](#make-incremental-changes-and-measure-results). ### Engagement metrics Engagement metrics reveal how often and for how long players are interacting with your experience. Three of the most important engagement metrics are: - **Daily active users (DAU):** The daily active users metric tracks the number of players that are in your experience for a given day. This is a great way to understand the overall health of your experience over time. If you have 1,000,000 players one day, and a few days later you see a downtrend to 500,000, you can identify a problem and begin troubleshooting. - **Monthly active users (MAU):** The monthly active users metric tracks the number of players that are in your experience for a given month. This metric shows a month-over-month trend and can be used to compare user activity on larger time frames. For example, some months have increased player traffic due to seasonal events and holidays. If your monthly active user number is lower last October compared to this October, it could provide insight that your Halloween event didn't satisfy your community. - **Average session length:** The average session length is how long a player plays your experience on average from the start of their session to the time they log out. This metric helps you design content to fit within that window of time to ensure your experience can be enjoyed by the majority of your players. The average session length also serves as a health metric. If the average session length fluctuates after a significant update or seasonal event, it can inform you how your latest content was received. > **Info:** For out-of-the-box functionality to create engagement rewards, see the [Engagement rewards](/docs/en-us/resources/feature-packages/engagement-rewards.md) feature package. ### Retention metrics Retention metrics determine if players are returning to your experience. Three of the most important retention metrics are: - **Day 1 (D1) retention:** Day 1 retention measures how many players return to your experience on your second day (D1) from the first day they play your experience (D0). A low D1 is indicative of low retention, often as a result of a poor user onboarding experience. This usually means the player is confused or frustrated, or they're not getting to the fun fast enough. To learn more about how to design an effective onboarding experience, see [Onboarding](/docs/en-us/production/game-design/onboarding.md). - **Day 7 (D7) retention:** D7 retention tracks the number of players who return to your experience after 7 days. If this number is low, it's often indicative of a poor progression system, with players not having a tangible reason to see themselves playing your experience a week from when they started. - **Day 30 (D30) retention:** D30 retention tracks the amount of players who return to your experience after 30 days. If this number is low, it's often indicative of a lack of end-game content or lack of an end-game goal. Consider if you have enough content at the end of your experience for seasoned players, be it quests, social aspects, or narrative elements. ### Monetization metrics Monetization metrics indicate much how revenue your experience is generating. Three of the most important monetization metrics are: - **Average revenue per daily active user (ARPDAU):** ARPDAU is the average amount that players spend in your experience. This is a good snapshot of overall health of the experience. Consider if there are successful systems in your experience that encourage your players to spend. - **Conversion rate:** The conversion rate is considered one of the most important metrics. The conversion rate is the percentage of all users who have spent any amount of money at all in your experience. The reason why this is one of the most important metrics is that if a player makes an initial purchase, they are much more likely to make a purchase again in the future. Consider if your monetization practices encourage your players to spend. - **Average revenue per paying user (ARPPU):** The ARPPU metric tracks the total amount spent for all players in your experience, known as **spend depth**. This tells you if players in your experience are motivated to spend a lot, a little, or somewhere in between. If this number is low, it's often indicative of purchase item value not being correctly optimized. ### Segmentation and filtering When viewing your experience's analytics, you can filter your user data by **segments** that allow you to understand the behavior of a subset of users. This lets you filter your metrics by various characteristics, such as:
**Active payer status**
Segmenting metrics by **Active payer status** helps you understand how revenue is distributed among paying players. In many experiences, a small group of players generates a large share of revenue. On average, the top 15% of active payers account for roughly 50% of revenue. To analyze this in your own experience dashboard: 1. Go to **Monetization** > **Overview**. 2. Select **Breakdown by** > **Active Payer Status** on the chart. Cross check this across various **Time intervals**. ![Dropdown menu to access Active payer status filter](../../assets/analytics/overview/Active-Payer-Status.png) Based on this information, you can identify opportunities to improve your experience for various groups of spenders: - If the top 15% contributes significantly less than expected, you may have opportunities to better retain or monetize top spenders. - If the top 15% contributes significantly more, you may need to strengthen the value proposition for mid- and lower-tier spenders. > **Info:** You can also review **Average Revenue Per Paying User (ARPPU)** on the same page. Top payers typically spend several times more than other segments. If ARPPU is low or declining, your experience may lack compelling ways for top players to continue spending. > > To evaluate engagement, go to the Engagement page and select **Breakdown** by **Payer Status**. Review metrics such as **DAU**, **WAU**, **MAU**, and **Average Playtime** to see whether this payer segment remain stable over time.
**When user first played**
Segmenting players by **When user first played** helps you evaluate the health of different player cohorts who started playing your experience at different times. In newly launched experiences, most DAU comes from players who joined recently, typically within the first 30 days. Strong acquisition channels and early retention are especially important during this stage. As an experience matures, long-term growth depends increasingly on retaining and re-engaging existing players. Over time, older cohorts should represent a larger share of active users. To analyze this in your own experience dashboard: 1. Navigate to **Analytics** > **Engagement**. 2. Select **Breakdown by** > **When user first played**. Check your data across various **Time intervals**. ![Dropdown menu to access When User First Played filter](../../assets/analytics/overview/When-User-First-Played.png) Based on this information, you can identify opportunities to improve your experience for various groups of users: - Stable or growing cohorts indicate healthy long-term engagement. - Declining cohorts highlight areas that may need attention. For example, if the 0–30 day cohort remains stable but the 181–365 day cohort declines, acquisition may be healthy while long-term players are disengaging. In this case, consider introducing new content, events, or gameplay updates to encourage players to return.
**Platform spender status**
Segmenting metrics by **Platform spender status** helps you understand how players who spend Robux across Roblox interact with your experience. This segment is also relevant for [Daily Engagement Rewards](/docs/en-us/creator-rewards.md#daily-engagement-rewards), which provide a bonus each day an **Active Spender** plays your experience. To analyze this in your own experience dashboard: 1. Navigate to **Analytics** > **Acquisitions**. 2. Under **Platform spender status**, enable the **Active spender** filter and click **Apply**. ![Filter options highlighting Platform spender status status](../../assets/analytics/overview/Platform-Spender-Status.png) 1. Review **Unique users with plays by source**. ![Graph displaying the source of unique users, with no filter](../../assets/analytics/overview/Unique-Users-A.png)_The **Unique users with plays by source** graph, unfiltered._ ![Graph displaying the source of unique users, filtered by platform spender status.](../../assets/analytics/overview/Unique-Users-B.png)_The **Unique users with plays by source** graph, filtered by Active platform spenders._ Active spenders typically retain better than average players. Declines in this segment may indicate opportunities to improve engagement for highly invested players. Since Daily Engagement Rewards require active spenders to spend at least 10 minutes in your experience, you should also verify your **Engagement** > **Average Playtime** statistics. If average playtime is close to or below this threshold, many players may not meet the requirement. ![Graph displaying the average playtime in the experience, filtered by platform spender status.](../../assets/analytics/overview/Average-Playtime.png)_This graph indicates the average active payer is not staying long enough to qualify for Daily Engagement Rewards_ You may also want to monitor the share of Active Spenders within DAU, WAU, or MAU over time. Declines in either total numbers or percentage share may indicate shifts in your audience or opportunities to strengthen engagement.
> **Info:** For a full list of metric filters, see [Dashboard filter by metrics](/docs/en-us/production/analytics/analytics-dashboard.md#filter-by-metrics). ## Make incremental changes and measure results Analytics can help identify opportunities to improve your experience, but changes are most effective when introduced gradually and measured over time. Instead of making large updates all at once, consider testing smaller adjustments and observing how player behavior changes. This approach makes it easier to understand which changes have a meaningful impact. Use the following tools to support this workflow: - [Experience configs](/docs/en-us/production/configs.md) - Allows you to adjust values in your experience without publishing a new version. You can use configs to tune gameplay systems, progression rates, rewards, or other parameters while your experience is live. - [Experiments](/docs/en-us/production/experiments.md) - Allows you to test different versions of a feature with separate groups of players. Using analytics, configs, and experiments together creates a continuous improvement loop: 1. **Analyze** metrics to identify opportunities. 2. Make a targeted change using **configurations** or **experiments**. 3. **Monitor** the results across relevant metrics. 4. **Repeat** to continue refining the experience. This process helps you make informed decisions and improve your experience based on real player behavior.
--- title: "Analytics" url: /docs/en-us/production/analytics last_updated: 2026-06-29T19:34:04Z description: "An overview of analytics features to track an experience's growth, retention, and other metrics." --- # Analytics Roblox offers a variety of analytics features to help you chart your experience's growth, technical performance, track user behavior and retention, and find opportunities for optimization. You can use analytics to understand what actions you can take to grow your experience. If this is your first time engaging with analytics on Roblox, see [Get started with analytics](/docs/en-us/production/get-started.md). ## Grow your experience with analytics Analytics is primarily used to understand your users engagement with your experience. Consider following this 3-step plan to use analytics to grow your experience: #### Step 1: Optimize retention, engagement, and monetization Before driving too many new users to your experience, grow the following metrics to a level that's comparable to or above the [benchmarks for similar experiences](/docs/en-us/production/analytics/analytics-dashboard.md#benchmarking): 1. **D1 (day 1) retention** and **average session time** are key metrics to focus on first because they measure if new users are enjoying and coming back to your experience. For more information on improving this metric, see [Retention](/docs/en-us/production/analytics/retention.md) and [Engagement](/docs/en-us/production/analytics/engagement.md). 2. **D7 (day 7)** and **D30 (day 30) retention** measure if users are making progress in your experience and returning long-term. For more information on improving this metric, see [Retention](/docs/en-us/production/analytics/retention.md). 3. **Payer conversion rate** and **ARPPU (average revenue per paying user)** measure the effectiveness of your user monetization. It's important to be thoughtful about monetization from the start. For more information on improving this metric, see [Monetization](/docs/en-us/production/analytics/monetization.md). _Comparing D1 retention to similar experiences._ #### Step 2: Drive new user acquisition After you have optimized your retention, engagement, and monetization, use your [Acquisition](/docs/en-us/production/analytics/acquisition.md) page to understand: 1. Where new users are coming from by source. 2. How well home recommendations are converting. You can use the following chart to track the percent of users who played your experience after viewing it in recommendation. Test different experience icons, titles, and descriptions to see how they impact your play through rate. Roblox experiences are inherently social, and users love trying them out with their friends. Consider using features like [experience events](/docs/en-us/production/promotion/experience-events.md) and [player invite prompts](/docs/en-us/production/promotion/invite-prompts.md) to invite users to bring their friends to your experience. #### Step 3: Monitor metrics after each experience update After you update your experience, visit analytics to understand how the update impacted the following metrics: 1. [Retention](/docs/en-us/production/analytics/retention.md): D1 and D7 retention. 2. [Engagement](/docs/en-us/production/analytics/engagement.md): Average session time. 3. [Monetization](/docs/en-us/production/analytics/monetization.md): Payer conversion, ARPPU, and revenue. 4. [Acquisition](/docs/en-us/production/analytics/acquisition.md): New users and play through rate. You can monitor major weekly changes and benchmark movement on your experience overview page. [Insights](/docs/en-us/production/insights.md) highlights large movements in key metrics such as DAU, new users, and revenue. If you have 100+ DAU, you can compare how your experience is performing against similar experiences using the benchmark scorecards. Your benchmark scorecards help you focus on the right metrics to improve compared to similar experiences For example, in the scorecard below, engagement (average session time and D1 retention) looks to be the problem. As a result, you may consider using [funnel analytics](/docs/en-us/analytics/funnel-events.md) to improve your onboarding funnel. Your similar experience benchmarks are updated daily. Roblox does not use these benchmarks as a direct signal in the discovery algorithm. Instead, these benchmarks give you a point of comparison as you work on improving your engagement, monetization, and acquisition. For more information on how users find new experiences, see [Discovery](/docs/en-us/discovery.md). ## Analytics Home [Analytics Home](https://create.roblox.com/dashboard/analytics) is the highest-level entry point for creator analytics and is located in the left navigation of [Creator Hub](https://create.roblox.com/). Analytics Home enables you to: - Monitor the analytics of multiple experiences at once - Track avatar item sales and revenue - Track user acquisition through off-platform share links ### Monitor experiences You can monitor the analytics of up to nine Roblox experiences by putting them into a **watchlist**. If you have analytics permissions for the experience, your watchlist provides the following analytics at a glance: - **Daily Active Users (DAU)**: Number of unique players who joined the experience at least once in the given day. - **New Users**: Number of DAUs who are first-time users. - **Session Time**: Total play time divided by number of individual sessions each day. - **Day 1 Retention**: Percentage of new users who played again after their first session ended. - **Daily Revenue**: Total amount of Robux earned by the experience in a day. If you add an experience to your watchlist that you don't have analytics permissions for, the card only populates publicly viewable stats. These include: - Like Ratio - CCU - Updated Date - Title - Owner Watchlists are applied on the account level, are private, and persist when toggling between groups. To add an experience to your watchlist, paste the **Experience URL** or add it from your **Experience Table**, which is a list view of all the experiences owned by your account or selected group. _Experience watchlist on Analytics Home._ Clicking on an experience you're monitoring takes you to the experience overview page. If applicable, [analytics insights](/docs/en-us/production/insights.md) offer direction on next steps and redirect you to applicable documentation and relevant analytics. ### Track avatar sales and revenue Avatar items you've sold automatically appear within the **Avatar Items** tab. Here, you can: - **View top performing items**: See your top selling and grossing avatar items over a selected time period. - **Analyze overall sales and revenue**: Showcase up to eight top items on a time-series graph. - **Monitor your catalog**: Examine a table with up to 200 items, filterable by item category and sales types, and sortable by sales and total revenue. _Avatar Items on Analytics Home._ ### Track user acquisition The **Share Links** tab provides information about the **Link Visits** metric, which tracks how many users have arrived on your experience details page by clicking on the share link you created. ## Access analytics in-experience The `Class.AnalyticsService.GetPlayerSegmentsAsync|AnalyticsService:GetPlayerSegmentsAsync()` method lets you query player analytics data directly in your experience at runtime. This enables you to tailor gameplay, onboarding, and monetization opportunities for each individual player based on characteristics, or **segments**, such as their spending behavior or overall length of time spent in your experience. By retrieving a player's segment information in-experience, you can dynamically adjust UI, offers, and gameplay systems to better match the player's profile and stage of engagement. For example, you can: - **Adjust store offerings by payer status** - Present store items that better match a player's purchase history and stage of progression. For example, players who haven't spent Robux yet might see lower-cost items that support early gameplay, while players who have previously purchased may see bundle options or broader upgrades. - **Adapt onboarding and progression using account age** - Newer players, such as those within their first 30 days of playing your experience, can receive additional tutorials, tooltips, and simplified content. You can also direct more experienced players toward advanced systems such as events, PvP areas, or end-game content. For implementation details, code samples, and the full list of available segment dimensions, see `Class.AnalyticsService:GetPlayerSegmentsAsync`. --- title: "Analytics insights" url: /docs/en-us/production/analytics/insights last_updated: 2026-06-29T19:34:04Z description: "How to use insights to dive deeper into player feedback and your experience's performance." --- # Analytics insights Insights exist on each of your experience's overview page to highlight major changes in key metrics and areas of improvement, and help you dive deeper into player feedback and your experience's weekly and monthly performance. The insight below, for example, shows that your revenue declined 21%, but the revenue of similar experiences increased by 15%. This is a sign that you might want to take action and look deeper into your monetization to diagnose potential issues. You can click **Explore this metric** to slice and dice your revenue by platform, country, and more. These insights are updated daily and are available for experiences with 100+ DAU. ## Achievement panel The achievement panel highlights key metric milestones that you've reached over the past 6 months. This insight is meant to help you celebrate progress when a metric has reached a six month high. Note that if a metric is at a six month high but below benchmark comparisons, you should still continue to improve it. For more information on how analytics works, see the [Analytics dashboard](/docs/en-us/production/analytics/analytics-dashboard.md). For more information on how to use analytics to optimize your experience's design, see [Analytics essentials](/docs/en-us/production/game-design/analytics-essentials.md). ## Analytics reports You can use AI-generated reports to dive deeper into your experience's weekly and monthly analytics insights. > **Warning:** To get analytics reports, your experience must have 1000+ DAU. Monthly reports are available on the first week of every month, and weekly reports are available at the start of each of the remaining weeks. > **Info:** Analytics reports are built with [Gemini 2.5](https://ai.google.dev/gemini-api/terms). To access the insights reports: 1. Go to Creations and select an experience. 2. In the experience's **Overview** page, click **See full report**. _Click _See full report_ to see your weekly and monthly reports_ The report focuses on the following key metrics: - **Top drivers for revenue and DAU changes**: Identify what's driving your revenue and DAU changes, whether it's a specific monetization product or acquisition channel. Learn what's working for your experience, and where you can get the most value out of what you build. - **Automatic outlier detection**: Automatically identify outlier segments instead of manually filtering by age, country, and platform. For example, if retention is your lowest benchmark and has declined over the past week, the report can tell you that retention has dropped significantly more on iOS or among 18+ users. - **Player feedback summaries**: Understand top requests and takeaways from your actual users over the past week or month, and prioritize what to work on next. You can also review personalized charts and recommendations in the generated report. _Click each section to see relevant charts_ ### Player feedback reports > **Warning:** Player feedback reports are only available for experiences that have 20+ player feedback comments. > **Info:** Player feedback reports are built with [Meta Llama 3](https://www.llama.com/llama3/license/). To get more specific insights about player feedback, including what users like about your experience, suggestions for improvements, and feature requests, click **See feedback report** in the analytics report. You can also access the player feedback report by clicking **See full report** on the AI summary insight in the [Feedback dashboard](/docs/en-us/production/analytics/feedback.md). _Click _See full report_ to see your player feedback report_ > **Warning:** Analytics and player feedback reports use AI and can make mistakes. Make sure to check responses and weigh player feedback against your judgment. --- title: "Monetization" url: /docs/en-us/production/analytics/monetization last_updated: 2026-06-29T19:34:04Z description: "Explains how to improve monetization metrics for your experience." --- # Monetization **Monetization** measures your experience's ability to generate revenue. ## View monetization metrics To view your experience's monetization analytics: 1. Navigate to your [Creations](https://create.roblox.com/dashboard/creations) page on **Creator Dashboard** and select your experience. 2. In the **Monetization** menu, select **Overview**. You can view analytics for individual or group owned experience. To view the latter, you need to have [group permissions for analytics](/docs/en-us/production/analytics/analytics-dashboard.md#grant-group-permission). On the monetization page, you can see the following metrics: | Source | Description | | --- | --- | | Revenue | Hourly and daily revenue split by developer products, passes, and commissions. | | Conversion rate | Percent of daily active users who are also paying users. | | Paying users | Number of daily active users who purchased an experience related item. | | ARPPU | Average revenue per paying user. | | ARPDAU | Average revenue per daily active user. | From there, you can explore data for each monetization product: - [Developer Products](/docs/en-us/production/monetization/developer-products.md#developer-product-analytics) - [Passes](/docs/en-us/production/monetization/passes.md#passes-analytics) - [Avatar items](/docs/en-us/production/monetization/avatar-items.md#avatar-items-analytics) - [Subscriptions](/docs/en-us/production/monetization/subscriptions.md#subscription-analytics) - [Immersive ads](/docs/en-us/production/monetization/immersive-ads.md#view-immersive-ad-metrics) - [Creator rewards](/docs/en-us/creator-rewards.md#access-payout-data) ## Improve revenue Here are some high level tips for growing your revenue: 1. **Be thoughtful about monetization from the start**: Consider what users would spend on, why they would spend, and whether and how to expand monetization options in the future. It's more challenging to develop a monetization strategy after launching your experience. 2. **Design for short-term as well as long-term users**: Try to provide a mix of consumable and durable items for purchase to give options for users with different levels of engagement. 3. **Optimize your storefront**: Make it easy for users to find and understand the value of purchasable items. This ensures a transparent and easy-to-use experience. 4. **Balance your in-experience content**: Discounts, limited item offers, free items, and more can help you improve your in-experience economy. Devote as much creativity and attention to your storefront as you do to the rest of the experience. Users would appreciate an easy-to-find shop that helps them learn more about the experience. ## Improve payer conversion rate Payer conversion measures the percent of your users who engage with your paid features or content. To improve this metric: 1. **Identify why your conversion rate is low**: Reasons might include expensive items, poor item or shop visibility, and unappealing content. 2. **Consider welcome offers and discounts for first-time purchasers**: This can lower the barrier for new users to try out your paid content to see how fun it is. 3. **Analyze the purchase funnel and identify any issues**: Possible issues include unclear or confusing steps in the purchase flow or distractions that make it difficult for users to complete the process. ## Improve average revenue per paying user (ARPPU) ARPPU measures the spending behavior of your repeat purchasers. To improve this metric: 1. **Add purchasing options for different levels of engagement**. Include a mix of items that are one-time purchases as well as repeatable purchases, such as consumables, refills, and in-game currencies for users who may wish to purchase them regularly. 2. **Provide a variety of purchasable items**. If you only have a limited number of sales items available, users might not have anything new to purchase even if they're interested in buying something new. You can find your top-selling items and expand those offerings to provide more options for users. 3. **Add seasonal items**. Seasonal items can create a sense of fun and excitement around particular holidays or real-world events. ## Improve average revenue per daily active users (ARPDAU) ARPDAU is a combination of your payer conversion rate and your ARPPU. To improve ARPDAU: 1. **Provide items at a variety of price points**. Some users are only willing to spend a small amount of Robux for experience items. Selling items at a wide range of price points can accommodate users with different spending habits. 2. **Improve retention and engagement**. Keeping users engaged with your experience over time can lead to more opportunities for monetization, as well as increased loyalty and interest in making purchases. A high ARPPU and low ARPDAU suggest revenue comes from a limited user subset. Balance your monetization by targeting a wider audience and improve both ARPPU and ARPDAU for long-term success. --- title: "Performance" url: /docs/en-us/production/analytics/performance last_updated: 2026-06-29T19:34:04Z description: "Explains how to improve performance analytics metrics for your experience." --- # Performance The **Performance** dashboard provides up-to-the-minute client and server metrics for your experience. Monitor this page before and after updating your experience to identify performance issues early. ![An overview of the performance dashboard.](../../assets/analytics/performance/perf-dash-overview.png) ## View performance metrics To access the performance dashboard, you must either be the experience owner or have [analytics group permissions](/docs/en-us/production/analytics/analytics-dashboard.md#grant-group-permission). 1. Navigate to the [Creations](https://create.roblox.com/dashboard/creations) page on the **Creator Hub**. 2. In the upper-left corner, select your account or the group that owns the experience. 3. Select the experience. 4. In the **Monitoring** dropdown, select **Performance**. The dashboard begins with the current number of users, the number of servers they are spread across, and a device breakdown. For the dashboard to show client and server **charts** like the ones below, your experience must have at least 100 daily active users (DAU). The dashboard contains two tabs, **Client** and **Server**, along with menus to adjust date range and time interval. You can also use the **Place** menu to examine individual places rather than the entire experience. ![A marked up view of the performance dashboard UI.](../../assets/analytics/performance/perf-dash-ui.png) The **Filter By** menu lets you swap between averages or percentile values. ![A view of the filters menu.](../../assets/analytics/performance/perf-dash-filter.png) When reviewing charts or filtering: - **P90** refers to the number of values that fall within the 90th percentile. If P90 server heartbeat is 59, then 90% of servers are running at or below 59 FPS. - Similarly, **P10** refers to the 10th percentile. If P10 server heartbeat is 40, then 10% of servers are running at or below 40 FPS. - **P50** is the median. Half of all values fall above it, half below. To help identify the source of a regression, you can break down all client performance metrics by **Place Version** and compare your latest update against previous versions. To filter by place version, select **Place Version** in the **Breakdown by** drop-down menu. The charts will automatically update to show your newest version alongside all earlier versions. You can also use the main filter menu to isolate the newest place version across the entire dashboard. With this breakdown, you can quickly see whether your latest release has changes such as lower frame rates or an increase in out-of-memory exits compared to earlier versions. ![A view of the Break Down by Version Filter menu.](../../assets/analytics/performance/perf-place-version-filter.png) ## Client charts The **Client** tab includes the following charts, all of which are broken down by platform or operating system: | Chart | Description | | --- | --- | | Concurrent users | Line graph showing the number of users in your experience. | | Session time | Duration that users spend in your experience. | | Client frame rate | Line graph showing client frames per second. Persistent low frame rates or large drops may signal issues. For information on investigating client frame rate, see [Client Compute](/docs/en-us/performance-optimization/identify.md#client-compute). | | Client crash rate | Line graph showing client crashes, divided by ended sessions by device type. Spikes in crash rate, particularly following an update to the experience, often indicate an issue. | | Unexpected out-of-memory exits | Line graph showing how often users crash due to out-of-memory errors, or exit via low memory warning. While this chart might not cover all out-of-memory crashes due to telemetry limitations, it is directionally accurate. A spike after an update indicates a regression in memory usage that causes your users to crash. | | Client crash count | Line graph showing the crash rate count by device type. If you have noisy crash rate data due to low sample size, we recommend monitoring the crash rate count as well. | | Client memory usage | Line graph showing client memory usage by device type. Continual increases can indicate a memory leak. See [Memory](/docs/en-us/performance-optimization/identify.md#client-memory). | | Client memory usage percentage | Line graph showing percentage of available device memory the client is using. For example, if the Android line approaches 100%, the majority of Android devices running your experience are at risk of running out of memory and crashing. | ### Crash rate Client crash rate measures the number of crashes divided by the number of ended sessions. When the number of ended sessions is low, crash rate breakdowns can become unstable. In these cases, the chart will show a dashed line instead of a solid line, indicating that the data point is noisy and that the crash rate might vary. If you see a dashed line on your chart, we recommend checking the total crash count instead of the client crash rate. ![A view of the client crashes dashboard.](../../assets/analytics/performance/perf-client-crashes.png) If you have memory issues, check out the [performance optimization](/docs/en-us/performance-optimization.md) guide. ## Server charts The **Server** tab includes the following charts: | Chart | Description | | --- | --- | | Server CPU time | Stacked area chart showing milliseconds (ms) of processing time per frame. Values over 16.67 ms are highly undesirable; they can cause server frame rates to drop below 60. See [Server Compute](/docs/en-us/performance-optimization/identify.md#server-compute). | | Server frame rate | Line graph showing server frame rate in FPS. Ideally, this value is always 60. Server frame rate is capped at 60, and drops can reduce client frame rate and increase latency. See [Script Computation](/docs/en-us/performance-optimization/improve.md#script-computation). | | Server memory usage | Stacked area chart showing server memory usage in MB. Try to keep this value below 50% of the [total server memory](/docs/en-us/performance-optimization/identify.md#server-memory). However, the chart doesn't show the total server memory, so another good guideline is to stay below 3 GB. If this number stays high or continually increases over time, investigate ways to [improve memory usage](/docs/en-us/performance-optimization/improve.md#script-memory-usage). | | Server memory by age | Stacked area chart showing server memory usage by server age. If memory usage increases significantly over the lifespan of the server, investigate possible memory leaks in the categories with the most growth, particularly [scripts](/docs/en-us/performance-optimization/improve.md#script-memory-usage), [physics](/docs/en-us/performance-optimization/improve.md#physics-memory-usage), and [rendering](/docs/en-us/performance-optimization/improve.md#rendering). | | Cores used per server | Line graph showing server CPU core count usage. Low core count usage combined with high server CPU time is an indicator that you should investigate [multithreading](/docs/en-us/scripting/multithreading.md). | | Compute efficiency | Line graph showing the efficiency of your experience compared to the Roblox average. Values above 100% indicate higher efficiency than the baseline. Contributing factors include maximum number of players per server (higher is more efficient) and server CPU time per frame (lower is more efficient). Higher compute efficiency means lower environmental impact, but this metric is not indicative of a good or bad player experience. | > **Info:** For a deep dive view on server crashes and out-of-memory snapshots, see [Server crashes chart](/docs/en-us/production/analytics/crashes.md). The server CPU and memory charts default to the **Overall** view, but have dropdown menus for individual process groups, such as physics, rendering, and scripts. The sum of these categories is **not** comprehensive; the server performs a small and typically insignificant amount of additional work per frame that isn't included in any category. As such, the overall millisecond and MB values are slightly lower than reality, but close enough to give you a clear understanding of server performance. If a category is higher than usual or appears to be taking up a disproportionate amount of time, use the dropdown menu to drill down on the group and identify processes that could be causing issues. ![The server CPU time chart showing the physics dropdown.](../../assets/analytics/performance/perf-dash-drilldown.png) > **Success:** To exclude a group or process from a chart, click its label in the legend. - CPU categories and tasks map to the ones in the [MicroProfiler](/docs/en-us/performance-optimization/microprofiler.md), so you can take [server dumps](/docs/en-us/performance-optimization/microprofiler.md#profile-the-server), select **Timers** mode, and then use the **Groups** menu to filter by the problematic process group. For example, if you spot a potential problem with the **assemble** process under the **Physics** group, you can find it under the same name in the MicroProfiler:![The MicroProfiler showing the assemble process within the Physics group.](../../assets/analytics/performance/perf-dash-micro.png) - Memory tags map to the ones in `Enum.DeveloperMemoryTag`, with the notable exception of **CoreMemory:Total**, which is equivalent to `Enum.DeveloperMemoryTag.Internal`. You can use the [Developer Console](/docs/en-us/studio/optimization/memory-usage.md#memory) to check tags for excessive usage. This image shows how you might use the Developer Console to investigate an issue with **PlaceMemory:GraphicsTexture** that you see in the dashboard:![The Developer Console showing the server memory view.](../../assets/analytics/performance/perf-dash-dev.png) ## Roblox updates Each Roblox update contains many changes including features and bug fixes. **Roblox update** annotations allow you to cross‑reference your experience's performance with recent Roblox platform updates. ![A view of the Roblox updates annotations on the Performance dashboard.](../../assets/analytics/performance/perf-dash-roblox-updates.png) Some platforms release to 100% of users immediately after the version is made available. Other platforms roll out gradually and require additional time to ramp up adoption; these receive a single annotation once a version is made available, giving you the earliest available signal without adding visual clutter. **Immediate Rollout** - Windows - macOS - Xbox **Gradual Rollout** - iOS - Google - Amazon - Samsung - PlayStation - Meta Quest ## Troubleshooting If you spot a potential problem, use the following steps to remediate and fix it: 1. Check your recent updates and consider rolling them back if the impact to metrics is severe. 2. See [Identify performance issues](/docs/en-us/performance-optimization/identify.md) and [Improve performance](/docs/en-us/performance-optimization/improve.md) for information on troubleshooting problems with server compute, client compute, and memory usage. 3. Use built-in testing tools to further drill down on problems: - [Developer Console](/docs/en-us/studio/developer-console.md) for viewing error and log messages and detailed information on memory and networking. - [Script Profiler](/docs/en-us/studio/optimization/scriptprofiler.md) for identifying scripts that take up the most resources. - The [MicroProfiler](/docs/en-us/performance-optimization/microprofiler.md) for viewing unoptimized portions of your experience visually. 4. Gather user feedback from your community. ## Client API calls after join > **Info:** This metric is only available for a small number of top experiences. The **Client API Calls in First 60s After Join** metric measures the total number of requests (API calls) your experience makes to Roblox services during the critical first minute after a user successfully joins. These calls happen automatically for every user who joins, so monitoring this metric can help you understand load-time efficiency and backend performance. ### Access the metric 1. Navigate to your experience overview on the Creator Hub. 2. On the **Snapshot** graph, click **Explore**. 3. In the **Select a metric** dropdown, choose **Client API Calls in First 60s After Join**. ![Graph of the client API calls metric](../../assets/analytics/performance/client-api-calls.png) ### Monitor the metric The primary goal during the first 60 seconds of a player joining is to stream and load your experience's assets (meshes, images, sounds, etc.). Ideally, the asset delivery service is the most-called API during this key time span. The metric is updated hourly so that you can quickly check it after publishing a new version of your experience. | Condition | Implication | Action | | --- | --- | --- | | Asset delivery is the most-hit API. | **Efficient.** Your experience is correctly prioritizing the loading of necessary assets. | Continue monitoring. | | A different service (e.g. inventory, friends, or users) is the most-hit API. | **Potential inefficiency or bug.** Your experience is executing heavy, non-critical API calls too early in the join sequence, potentially delaying the load process. | Investigate and refactor the scripts that execute during the initial join to prioritize asset loading. | --- title: "Retention" url: /docs/en-us/production/analytics/retention last_updated: 2026-06-29T19:34:04Z description: "Explains how to improve retention metrics for your experience." --- # Retention **Retention** measures how many users return to your experience after their first visit in 1 day, 7 days, and 30 days. ## View retention metrics To view your experience's retention analytics: 1. Go to [Creations](https://create.roblox.com/dashboard/creations) and select an experience. 2. In the **Analytics** menu on the left, select **Retention**. To view analytics for group-owned experiences, you need to have [group permissions for analytics](/docs/en-us/production/analytics/analytics-dashboard.md). The x-axis of your [retention cohorts](#understand-your-new-user-cohorts) is based on a new user's first play date. This provides a more accurate view of how the changes you make on a specific day affect that new user cohort's retention over time. For example, the date 06/20 on all three charts shows you the same cohort: - **D1 retention**: Users who first played the experience on 06/20 and returned to the experience the next day. - **D7 retention**: Users who first played the experience on 06/20 and returned to the experience after 1 week. - **D30 retention**: Users who first played the experience on 06/20 and returned to the experience after 1 month. > **Info:** The data for the most recent dates on your D7 and D30 charts might be empty. For a user cohort that first played the experience on 06/20, their D7 retention data appears on 06/27 (after 7 days have passed), while their D30 retention data appears on 07/20 (after 30 days have passed). In the following example, there is a dip around 05/13 on all retention charts. This indicates that users acquired on 05/13 had lower D1, D7, and D30 retention. This is a typical and temporary outcome following a large influx of new users. _In this example, there is a dip around 05/13 on all retention charts. This indicates that users acquired on 05/13 had lower D1, D7, and D30 retention. This is a typical and temporary outcome following a large influx of new users._ ## Understand your new user cohorts You can find the new user retention cohort table at the bottom of your retention dashboard. You can view both daily and weekly cohorts: - **Daily cohorts** let you see how many new users were retained over their first 10 days. - **Weekly cohorts** let you track retention over a 10-week period by grouping users by the week they joined (Monday - Sunday). Cohorts are useful for tracking the impact of major updates, events, or any marketing campaigns you run. For example, you can measure if new users who started playing your experience during a big event are sticking around longer than users who started playing before the event. For each cohort, you can also analyze key down-funnel metrics, including: - 7D playtime per user (cumulative) - 7D player conversion rate (cumulative) - 7D revenue per user (cumulative) - 30D revenue per user (cumulative) These metrics allow you to see if the new users who started playing your experience during a big event are monetizing better than other user cohorts. ## Improve day 1 retention Day 1 retention is the percentage of new users who come back to your experience after visiting it for the first time. To improve this metric, focus on your experience's **core loop**, **first-time user experience**, and **performance**. ### Core loop Core loop is the actions that users repeat in your experience to make progress in a single session. - For a pet adoption experience, a core loop might include adopting, training, and leveling up pets. - For a tycoon experience, a core loop might include managing a business, balancing resources, and competing with other users. The following example is one path to improve your core loop: 1. **Identify the core steps** that users perform in the loop and make them clear and fun. 2. **Balance the challenge** of your core loop to avoid user frustration and boredom. 3. **Reward users** for completing your core loop with feedback, currency, items, and achievements. To learn more on how to design core loops, see [Core loops](/docs/en-us/production/game-design/core-loops.md). ### First-time user experience **First-time user experience (FTUE)** is how new users experience your core loop for the first time. To improve your FTUE: 1. **Use a brief tutorial** or contextual info like tooltips to guide users through your core loop. Avoid long tutorials to help users get to the fun as quickly as possible. 2. **Deliver a joyful moment** after users complete your core loop for the first time. 3. **Preview the progress** that users can make if they complete your core loop multiple times. You can also use analytics to improve your FTUE: 1. **List the steps of your core loop**. For example, for a pet adoption experience, the steps can be: - Adopt a pet. - Train a pet. - Level up a pet. 2. **Track the completion rate of each step**. Use special in-experience items to mark the completion of each step. Track both positive and negative experiences. For example, if the FTUE includes a battle with an NPC, track both whether users win or lose more frequently. 3. **Identify and fix big drop-offs**. For example, if visiting the pet store has the highest drop-off rate, you can reduce friction in this step by making the pet store easier to find. Avoid a complex or time consuming FTUE, as you want to get users to the fun as quickly as possible, ideally in 5 minutes or less after entering your experience. To learn more on how to design your FTUE, see [Onboarding](/docs/en-us/production/game-design/onboarding.md). ### Performance **Performance** is how well your experience runs on different devices and platforms. To improve your performance: 1. **Test on different devices and platforms** to fix any bugs that might affect performance. 2. **Monitor crash rates, frame rates, and errors** each time you update your experience. 3. **Set up a user community** that can help identify bugs and crashes. ## Improve day 7 retention Day 7 retention is the percentage of new users who come back to your experience after visiting it for the first time on the 8th day (Day 7). To improve this metric, focus on your experience's **progression system**. ### Progression system Progression system is how users accomplish goals and gain rewards in your experience, such as leveling up and unlocking new content. To build a healthy progression system: 1. **Set up clear user goals**: Short-term goals can provide instant gratification and feedback, and long-term goals can give users directions to work towards over an extended period of time. 2. **Add content variety**: A variety of content, such as new modes, inventory items, and social features, can prevent users from getting bored. 3. **Balance the difficulty**: If users progress too fast, they might run out of content to explore. If they progress too slow, they might get bored and frustrated and lose the motivation to stay. You can use analytics to improve your progression system: 1. **Identify your progression system**. For example: - Earning in-experience currency for a tycoon experience. - Unlocking new items to collect for an item collection experience. - Mastering casts or skills for a magic world experience. 2. **Measure user distribution** by tracking the number of users for each progression level to see how long it takes a user to reach the next level by granting each user a special in-experience item once they advance to the next level. 3. **Fix the progression system**. If users level up too quickly or too slowly, evaluate the difficulties of your progression system's tasks and adjust them accordingly to make sure users spend the proper amount of time on each level. ## Improve day 30 retention Day 30 retention is the percentage of new users who come back to your experience after visiting it for the first time on the 31st day (Day 30). To improve this metric, focus on your experience's ending system. ### Ending system Ending system is what's available to users after they've completed the main objectives of your experience or reached higher levels. To improve it: 1. **Add more content**: Regular updates can keep users coming back. A common frequency is to release smaller updates on the existing mechanics every 2-4 weeks, and bigger updates of new features every 2-3 months. 2. **Add social mechanics**: Social mechanics can motivate users to stay engaged with your experience and interact with other users while waiting for new content. Common social systems and elements include trading, parties, guilds, and competitive systems such as PvP (player versus player), tournaments and leaderboards. --- title: "Bans" url: /docs/en-us/production/bans last_updated: 2026-06-29T19:34:04Z description: "The Creator Hub Bans dashboard lets you ban users and manage bans." --- # Bans The **Bans** dashboard lets you view current bans, ban users, and unban users. You can view both public and private reasons for each ban, expiration dates for non-permanent bans, and ban history for the user. ![Overview of the Bans dashboard.](../assets/analytics/bans/bans-overview.png) ## Access the dashboard To access the Bans page, you must either be the experience owner or have [Configure bans for all group experiences permissions](/docs/en-us/projects/groups.md#roles-and-permissions). 1. Navigate to your [Creations](https://create.roblox.com/dashboard/creations) page on **Creator Dashboard** and select your experience. 2. In the **Moderation** menu on the left, select **Bans**. ## Ban users To ban a user, select the **Add users** button, fill out the details, and click **Apply Ban**. - You must have the numeric user ID for each user you want to ban. To ban multiple users, include a comma-separated list of IDs. - **Apply ban to all known alt accounts** lets you also include alt (alternate) accounts. - Include a public and private reason for the ban. These fields can be identical. Roblox displays the public reason for the ban to the user if they attempt to join your experience. - For guidelines on banning users, see [Users and players](/docs/en-us/players.md#ban-guidelines). ## View bans The default dashboard view shows current bans. You can sort by date of the ban and search for a particular user ID. Under the **More** column, choose **See ban history** to view history for the user. When choosing whether to unban a user, this information can help you decide if the user was a one-time problem or persistently breaks the rules. ## Unban users To unban users, choose one or more and click **Unban users**. Unbanned users can immediately access your experience again. Manually unbanning a user does **not** remove their ban history. --- title: "Experience configs" url: /docs/en-us/production/configs last_updated: 2026-06-29T19:34:04Z description: "Configs let you update in-game values without restarting your servers." --- # Experience configs **Experience configs** let you update in-game values in real time without restarting servers: - Turn features on and off, such as enabling or disabling a new onboarding dungeon. - Tune in-game values like boss health, experience gain, or item prices. - Launch timed content, such as a Halloween event that starts at midnight. Configs take the form of keys and values. Rather than using hard-coded constants in your code, you can use the key to get a value (string, number, boolean, or JSON object) and then update that value whenever you'd like without publishing a new version of your experience. The required code changes are minimal: ```lua local ConfigService = game:GetService("ConfigService") local configSnapshot = ConfigService:GetConfigAsync() local myValue = configSnapshot:GetValue("my_key_name") ``` You can have up to 1,000 active configs at any given time and manage them on Creator Hub or in Roblox Studio. ![Overview of the Configs page on Creator Hub](../assets/analytics/configs/configs-overview.png) ## Create and edit configs 1. On the [Creator Hub](https://create.roblox.com/dashboard/creations) **Configs** page for your experience, click **Create config**. 2. Specify a key, a type, a value, and optionally, a description to help you or your team later identify the purpose of the config. Supported types are string, number, boolean, and JSON object. 3. Copy the generated code snippet into a server script in your experience, likely in `Class.ServerScriptService`. The code should look something like this:```lua local ConfigService = game:GetService("ConfigService") local configSnapshot = ConfigService:GetConfigAsync() local MY_KEY = "my_key" -- optional, store the config key as a constant local myValue = configSnapshot:GetValue(MY_KEY) ``` 4. Use the value like you would any other variable. Configs **do nothing** unless you use them within your code. For more information about working with configs in your scripts, see [Add configs to your code](#add-configs-to-your-code). Editing a config is no different from creating one. Click the **Edit** button and update the value and description as-desired. ### Limits Config values have the following limits by type. | Type | Maximum size | | --- | --- | | String | 100,000 characters | | Number | ±1.7976931348623157e+308, ±2^53 for exact integer representations | | Boolean | N/A | | JSON | 100,000 characters | ## Publish configs After you create a config, it moves to a **staged** state so that you can test it before it becomes publicly available. Staged changes are available to you and your team in Studio play sessions, not to players in live experiences. The **Configs** page on Creator Hub shows all active and staged changes. ![The Configs page showing unpublished changes](../assets/analytics/configs/configs-publish.png) 1. After you test your staged changes, click **Publish now** to publish to all players within five minutes. You can also choose **Publish over 15 min** if you prefer a longer rollout period. 2. (Recommended) Add a descriptive publish message that indicates what you updated. This message appears on the **History** page and can help you and your team later identify the purpose of the change. ## Create and edit configs in Studio If you prefer, you can create, edit, stage, and publish configs in Roblox Studio. Click **File** > **Open Configs**. The Studio interface is particularly convenient for staging and testing new values. ![Studio window for working with configs](../assets/analytics/configs/configs-studio.png) ## View history and restore configs On the **Configs** page, click **History** to see past updates. Each update has the time and date of the change, who made the change, and the publish message. ![The history page with diff expanded for a config value](../assets/analytics/configs/configs-history.png) - Expand each row to see the key, the value before the change, and the value after it. - Use the **Search Key** box to search for keys—not descriptions or values, just keys. The **History** page also lets you restore configs to a previous state: 1. Click **Restore** next to the change to stage the "before" value. Note that restoring a config discards any existing staged changes. 2. Return to the **Configs** page and [publish the config](#publish-configs). ## Add configs to your code The main class for working with configs is `Class.ConfigService`, which fetches the latest keys and values for your experience. `ConfigService` is only available to server scripts. Attempting to call its methods from a client script results in an error. The first step to working with configs is to retrieve a `Class.ConfigSnapshot`, the latest values for all configs at the current point in time. There are two methods for getting a snapshot: - `Class.ConfigService:GetConfigAsync()` is for global configs that you want to apply to all players in the experience:```lua local ConfigService = game:GetService("ConfigService") local configSnapshot = ConfigService:GetConfigAsync() local bossHealth = configSnapshot:GetValue("bossHealth") ``` - `Class.ConfigService:GetConfigForPlayerAsync()` is for [experiments](/docs/en-us/production/experiments.md). It fetches player-specific configs so that different players can get different values.```lua local ConfigService = game:GetService("ConfigService") local Players = game:GetService("Players") local function onPlayerAdded(player) local playerConfigSnapshot = ConfigService:GetConfigForPlayerAsync(player) local bossHealth = playerConfigSnapshot:GetValue("bossHealth") end Players.PlayerAdded:Connect(onPlayerAdded) ``` In either case, if the key doesn't exist, `Class.ConfigSnapshot:GetValue()` returns nil. ### Refresh snapshots `Class.ConfigSnapshot|ConfigSnapshots` represent a point in time, the state of the config when you requested the snapshot. To avoid disrupting gameplay, updates to a config do **not** automatically propagate to snapshots. In a competitive shooter, for example, you might only want new config values between rounds so that weapon attributes don't fluctuate mid-match. To get the latest values manually, use the `Class.ConfigSnapshot:Refresh()` method. Optionally, connect a function to the `Class.ConfigSnapshot.UpdateAvailable` event to automatically refresh: ```lua configSnapshot.UpdateAvailable:Connect(function() configSnapshot:Refresh() end) ``` After you refresh a snapshot, you can use `Class.ConfigSnapshot:GetValueChangedSignal()` to listen for changes to a specific key so that you can update your experience's code: ```lua configSnapshot:GetValueChangedSignal("bossHealth"):Connect(function(newHealthValue) spawnNewBoss(newHealthValue) end) ``` ### Error handling In rare cases when the config fails to load and has never been loaded before, `Class.ConfigService:GetConfigAsync()` throws an error. If `Class.ConfigService` loses connection to the Roblox servers after it was previously loaded, `Class.ConfigService:GetConfigAsync()` returns a snapshot with the latest available values. These snapshots attempt to reconnect and fire the `UpdateAvailable` event when they successfully load new updates. How you handle these uncommon situations is up to you. You can wrap the call in a `Global.LuaGlobals.pcall()` and have fallback values in your code, show the player an error and wait for the snapshot to reconnect, or some other solution. ## Test configs `Class.ConfigService` has built-in methods for temporarily testing config changes both in Studio and on live servers without impacting the entire experience. To set a local test value, call `Class.ConfigService:SetTestingValue()` from a server script or through the [Developer Console](/docs/en-us/studio/developer-console.md). Changes to test values trigger a config update along with the `Class.ConfigSnapshot.UpdateAvailable` signal. Just like a production update, you must refresh existing snapshots to get the test value: ```lua local ConfigService = game:GetService("ConfigService") ConfigService:SetTestingValue("bossHealth", 200) local configSnapshot = ConfigService:GetConfigAsync() local bossHealth = configSnapshot:GetValue("bossHealth") -- Now 200! ``` Test changes apply for the lifetime of the current server and do not propagate to other servers. In Studio, they apply to the current play session and do not affect live servers. You can manually clear test changes with `Class.ConfigService:ClearTestingValue()`. --- title: "Creator Store" url: /docs/en-us/production/creator-store last_updated: 2026-06-29T19:34:04Z description: "The Creator Store is where you can find all assets for public use in your experiences." --- # Creator Store The **Creator Store** is a marketplace that features millions of assets made by Roblox and independent creators for others to use within their game development. It includes many different types of assets, such as 3D model asset packs, materials, Studio plugins, gameplay scripts, UI elements, and sound effects. You can find the Creator Store both on the [Creator Hub](https://create.roblox.com/store) and directly inside Studio's [Toolbox](/docs/en-us/projects/assets/toolbox.md). Both access points allow you to find and save assets to your inventory so that you can reuse the assets in any of your projects. #### Creator Hub View #### Studio View In addition to using or distributing your own free assets on the Creator Store, you can become a seller and monetize any models or plugins that you create for United States Dollars (USD). This monetization method lets you earn 100% of net proceeds on transactions, bypassing platform fees and DevEx rates. ## Asset categories The Creator Store offers many asset categories for creators to use as they search for assets for their projects. When you narrow down your search to a specific category, additional sub-categories display to help you further filter your results for distinct gameplay objects. For more information on any high-level asset category, click through the following tabs. #### 3D Assets The **3D Assets** category features three-dimensional assets that help bring your game's environment and characters to life. Models in this category contain 3D geometry, such as [parts](/docs/en-us/parts.md) and [meshes](/docs/en-us/parts/meshes.md), as well as collections of other visual or auditory objects that immerse players within the game, such as [physics](/docs/en-us/physics.md) and [audio](/docs/en-us/audio/objects.md) objects. ![Example of SurfaceAppearance applied to a Bush Mesh](../assets/modeling/surface-appearance/SurfaceAppearance-Example-1.jpg) ![Example of SurfaceApperance applied to a Rock Mesh](../assets/modeling/surface-appearance/SurfaceAppearance-Example-3.jpg) _ Example 3D assets_ #### Visual Effects The **Visual Effects** category features custom materials, particle emitters, skybox, and lighting objects that elevate your game's aesthetics. Models in this category often contain [physically-based rendering](/docs/en-us/art/modeling/surface-appearance.md) (PRR) textures that allow you to represent realistic shading and lighting in the 3D space, as well as custom [particle emitters](/docs/en-us/effects/particle-emitters.md), [trails](/docs/en-us/effects/trails.md), [light sources](/docs/en-us/effects/light-sources.md), and [skyboxes](/docs/en-us/environment/skybox.md). ![A sphere with a basket-like surface.](../assets/modeling/surface-appearance/Opacity-0.png)![A clay colored sphere with an extremely rocky surface](../assets/modeling/surface-appearance/Normal-1.png)![A sphere with regular triangular patterns on its surface completely invisible.](../assets/modeling/surface-appearance/Opacity-1.png) _ Example visual effect assets_ #### 2D Assets The **2D Assets** category features two-dimensional assets that personalize your game's environment and UI elements. This category primarily contains [decals](/docs/en-us/parts/textures-decals.md), [videos](/docs/en-us/ui/video-frames.md), and unique fonts that aren't available in Studio's default settings. ![Example of a music UI element.](../assets/creator-store/CS-UI-1.png)![Example of a particle UI element.](../assets/creator-store/CS-UI-2.png)![Example of a tool UI element.](../assets/creator-store/CS-UI-3.png) _ Example 2D assets_ #### Gameplay The **Gameplay** category features core systems and scripts that help drive gameplay logic and player interaction. Models in this category often contain [scripts](/docs/en-us/scripting/locations.md), [packages](/docs/en-us/projects/assets/packages.md), and custom tooling that streamline feature development. ![Example of a portal gameplay element.](../assets/creator-store/CS-Gameplay-1.png) ![Example of a map maker gameplay element.](../assets/creator-store/CS-Gameplay-2.png) _ Example gameplay assets_ #### Plugins The **Plugins** category features Studio extensions that enhance workflows and speed up the creative process. [Plugins](/docs/en-us/studio/plugins.md) in this category add additional features and functionality to Studio for unique design and gameplay requirements. _ Example plugin assets_ #### Audio The **Audio** category features a sound library of music and sound effects that fit any game's mood or atmosphere. This category primarily contains [audio](/docs/en-us/audio/objects.md) objects, sometimes with [additional effects](/docs/en-us/audio/effects.md) that modify or enhance audio streams. _ Example audio assets_ ## Find assets In addition to asset categories, the Creator Store offers an **Advanced Filter** button with specialty filters to further narrow your search results so that you can find the exact assets that you're looking for. When you select the Advanced Filter button, you can: - Sort by relevance or rating. - Filter creators by verification status and username. - Set price parameters. - Specify that you only want to see results that contain specific asset types. #### Advanced Filter button #### Advanced Filter Options When you find an asset of interest, hover over the asset's thumbnail and click the magnifying glass icon to open the asset's details page. Every asset's details page includes: - The name of the account that's distributing the asset. - A button to either acquire or purchase the asset. - Video preview and up to five images that highlight the asset's appearance or functionality. - The asset type, when it was created, and when it was last updated. - A description of the asset. - Technical details, such as the asset's triangle, vertice, and script count. - A ratings score and reviews from other creators. #### Inspect Asset button #### Asset Details Page If you are looking at the asset's details page on the Creator Hub, you can click the **Try in Roblox** button to test the asset in a custom demo experience, if available, or click the main blue button at the top-right corner to acquire or purchase the asset and add it to your inventory. ![Example of a Get Model button.](../assets/creator-store/Try-In-Roblox-Button.png) ![Example of a Buy Asset button.](../assets/creator-store/Buy-Model-Button.png) If you are looking at the asset's details page while in Studio, you can also insert it directly to your experience: - If the asset is a model, mesh, decal, video, or audio asset, click it or drag‑and‑drop it into the 3D viewport. The asset displays in the Explorer window and any applicable 3D content displays in the 3D viewport. - If the asset is a plugin, click it and use the **Install** button to add it to the mezzanine's **Plugins** tab. - If the asset is a font, click it and use the **Install** button to add it to your font library. The font becomes available for user interface elements such as text labels or buttons. > **Info:** Some assets include scripts that perform specific actions, such as animating at runtime or triggering a sound. If you want to use an asset without allowing any of its scripts to run, right‑click the object in the **Explorer** window, then select **Disable Scripts** from the contextual menu. ## Distribute and sell assets When you are ready, you can publicly distribute and sell your own assets on the Creator Store for others to use within their own game development. Specifically, as long as you and your assets meet Creator Store requirements, you can: - Distribute and make freely available any mesh or image that you have imported through the 3D Importer, or any model, plugin, or audio asset that you have uploaded to your inventory. - Sell any model or plugin and earn 100% of net proceeds on transactions. Roblox offers a market-leading revenue share for these sales, as only taxes and payment processing fees are deducted. > **Info:** In order to set any USD prices and sell your assets, **you must have a seller account**. Roblox administers seller accounts through Stripe, a third-party payments provider. Before you import an asset to distribute and sell on the Creator Store, it's highly recommended to opt-into the [Asset Privacy](/docs/en-us/projects/assets/privacy.md) beta so that any image, mesh, or decal is created as Restricted by default. When an asset is Restricted, Roblox always checks its permissions when it loads into an experience or when a creator takes an action on the asset, such as inserting it into Studio, sharing it with another creator, or listing it on the Creator Store. > **Info:** If a creator or experience doesn't have your explicit permission to use an asset, it cannot load in Studio or at runtime. For more information on how to distribute assets on the Creator Hub or inside Studio, click through the following tabs. #### On the Creator Hub To distribute an asset through the Creator Hub: 1. In the horizontal navigation, select **Development Items**. All assets you have previously imported display within their respective category. 2. Select the asset that you want to distribute. The asset's **Configure** page displays. 3. Configure settings for your asset's details page. 1. Fill out the **Name** and **Description** fields. 2. **OPTIONAL** If you are ID or phone verified, add up to one video and 5 thumbnail images that showcase your asset's appearance or functionality. 3. **OPTIONAL** In the **Try in Roblox** section, choose the default Roblox experience or provide a custom experience for customers to test your asset. 4. In the **Distribution** section, toggle on **Distribute on Creator Store**. 5. If you have a seller account and want to sell a plugin or model, set a price in USD for the asset in the **USD Pricing** field. If you keep the default value of **Free**, the asset displays on the Creator Store as free to all creators. 6. Click the **Save Changes** button. After a few moments, the asset becomes public and visible on the Creator Store. > **Info:** If you are distributing an audio asset for the first time, you must also enable all legal agreements, then click the **Submit** button. #### Inside Studio tab To distribute an asset inside Studio: 1. If the asset is already in your inventory, 1. Navigate to the **Toolbox**, then select the **Creations** tab. All assets you have previously imported display within the chosen category. 2. Right-click the asset you'd like to distribute and select **Edit Asset**. The **Asset Configuration** window opens. 3. Confirm asset details, such as its name and description. 4. Click the **Save** button. Once the asset finishes uploading to the cloud, click the dashboard link formatted as `https://create.roblox.com/dashboard/creations/store/.../configure`. 5. In the browser window that opens, follow the Creator Hub instructions. 2. If the asset is in Studio but not in your inventory, 1. In the **Explorer** window, right-click the asset you'd like to distribute, then in the contextual menu, select **Save / Export** ⟩ **Save to Roblox...**. The **Asset Configuration** window opens. 2. Provide a name and description. 3. Set **Content Type** to **Development Item**. 4. Set **Asset Category** to **Model**. 5. Click the **Save** button. Once the asset finishes uploading to the cloud, click the dashboard link formatted as `https://create.roblox.com/dashboard/creations/store/.../configure`. 6. In the browser window that opens, follow the Creator Hub instructions. ### Requirements In order to be eligible to participate in the Creator Store, - **Assets** must meet requirements to be listed. - **Sellers** must meet requirements to sell models and plugins. - **Customers** must meet requirements to purchase assets. For a full list of these requirements, click through the following tabs. #### Asset Requirements Every asset that you distribute and sell on the Creator Store must adhere to the [Community Rules](https://en.help.roblox.com/hc/articles/203313410), [Terms of Use](https://en.help.roblox.com/hc/articles/115004647846), and [Digital Millennium Copyright Act](https://create.roblox.com/docs/production/publishing/dmca-guidelines) (DMCA) regarding copyright. If any asset breaks these rules, the asset and your account may be subject to moderation. There are limits on the number of assets you can distribute per 30 days, depending on whether you've [verified your account](/docs/en-us/production/publishing/account-verification.md): | | Mesh assets | Image assets | Model assets | Audio assets | Plugins | | --- | --- | --- | --- | --- | --- | | Verified account | 200 | 200 | 200 | 100 | 10 | | Unverified account | 10 | 10 | 10 | 10 | 2 | In addition, the Creator Store restricts use of the following practices to ensure asset safety: - **Obscuring engine features within scripts**, including LuaVMs, `Global.LuaGlobals.getfenv()`, and `Global.LuaGlobals.setfenv()`. - **Requiring remote assets,** including `Global.RobloxGlobals.require(assetId)`, `Global.LuaGlobals.loadstring()`, `Class.InsertService:LoadAsset()`, `Class.AssetService:LoadAssetAsync()`, and `Class.ModuleScript.LinkedSource`. Assets that may look useful on the surface could load another "virus" asset at runtime. - **Including obfuscated code**. For publicly-shared assets, it's important for creators to understand what they are putting into their experiences. If code is obfuscated, creators cannot trust that the script is only doing what it should be doing. - **Extremely large scripts**. Assets with unnecessarily large scripts, including multiple repeat lines or large strings that are unused, especially if they cause rendering issues in editors. > **Info:** If you discover or purchase an asset that you believe violates Roblox's policies, report it using the **Report Item** option on the asset's detail page. #### Seller Requirements In order to set any USD prices and sell your models and plugins, you must have a seller account. In order to create a seller account, you must meet the following Roblox eligibility requirements: - Your Roblox account must be verified with a [government ID](/docs/en-us/production/publishing/account-verification.md#verify-through-government-id). You **cannot** verify with a phone number. - Your Roblox account must not have been recently banned for any reason. - You must be either 18 years of age or older, or 13-17 years of age with parental consent. A parent/guardian (18+) must review and agree to the Creator Store terms, and parent/guardian information must be entered on the Stripe form. - You must reside in [one of the countries supported](https://stripe.com/docs/connect/cross-border-payouts) by our third-party payments provider. > **Info:** Due to restrictions, selling on the Creator Store is only available within certain regions. Seller onboarding is not yet supported in countries such as Brazil, China, India, or Russia. If you violate any of these requirements after you create your seller account, your account will be suspended, your assets will be taken off sale, and payouts will be frozen. If you believe your account was incorrectly suspended, you can [appeal](https://www.roblox.com/report-appeals#/) the applicable violation. > **Warning:** You can only sell assets that you own from an **individual** user account. Group-owned assets are ineligible. If you want to sell a group-owned asset, consider re‑uploading them under individual ownership. #### Customer Requirements Only **individual** user accounts may purchase assets for USD on the Creator Store. Groups are ineligible to purchase models or plugins at this time. Transactions between any individual customer and seller are capped per month for safety purposes. If you're buying a lot of assets from one seller in a single month, any purchase over the limit will be prohibited until the following month. This includes any purchases between alternate accounts for either the customer or seller. ### Seller accounts All prospective sellers on the Creator Store must complete an application form through Stripe to create their seller account, set USD prices, and receive payouts. This process applies even if you have previously sold plugins for Robux on the Creator Store. **You are limited to one Stripe account per Roblox account**. If your Roblox account meets all eligibility criteria on the [Seller Onboarding](https://create.roblox.com/settings/eligibility/priced-assets) page, Roblox provides a link to an application form through Stripe so that you can apply for a seller account. This application form collects your personal information in order to process your case. To avoid any delays in the application process, ensure you have the following details ready before you start filling out the form: - Banking information, such as account number and sort code. - Name and home address. - Contact information, including your phone number and email address. - Tax identification numbers (TIN), if applicable. This may differ depending on your country of residence. - A [W8](https://support.stripe.com/express/questions/what-is-a-w-8-form) / [W9](https://support.stripe.com/express/questions/what-is-a-w-9-form) form. > **Info:** You do not need to fill in the 'statement descriptor' and 'website' fields. If you do provide a custom statement descriptor, Roblox automatically overrides this. After you fill out the application form, you'll receive an email acknowledging your submission, and Roblox will send you a notification when there are updates on your application. It can take up to 7 days to review your information and create your seller account. > **Warning:** Once Stripe accepts your application, you cannot change the country associated with your seller account due to tax limitation. Ensure your information is correct when you fill out the form. You can check the status of your application at any time by revisiting the [Seller Onboarding](https://create.roblox.com/settings/eligibility/priced-assets) page. Your approval status can be in any of the following states: - **Pending** - Your information is being reviewed. - **Failed** - Your seller account has not been created, either because of an error or because your information was incorrect. - **Information Needed** - Stripe requires more information from you before they can confirm your seller account. - **Success** - Your seller account has been successfully created. You can set prices and sell assets. - **Rejected** - You cannot create a seller account due to fraudulent or malicious activity. > **Warning:** If you edit any information after you have been approved, you return to the **Pending** state until Stripe reviews and confirms your changes. This temporarily freezes payouts. If you need to update your banking information or address, it's best to wait until just after your next payout. ### Earnings Once Stripe confirms your information and creates your seller account, you can start setting USD prices for your model and plugin assets on the [Creator Dashboard](https://create.roblox.com/dashboard/creations): - Plugins have a minimum price of $4.99 and maximum price of $249.99. - Models have a minimum price of $2.99 and maximum price of $49.99. When a customer buys one of your assets, the money from the sale enters a 30 day escrow period. At the end of that period, the money will get added to your Stripe account balance and will get deposited in your linked bank account in the next payout cycle. > **Info:** If your seller account is denominated in a non-USD currency, the currency conversion will happen as each sale's proceeds are transferred into your balance. For more information, see Stripe's [Supported currencies](https://docs.stripe.com/currencies) documentation. You can find a record of all your Creator Store transactions on the [Transactions](https://create.roblox.com/dashboard/transactions) page of the Creator Dashboard, including - Incoming Payments (sales) - Outgoing Payments (purchases) - Available Balance that you will be paid in the next payout - Pending Revenue that's currently being held in a 30-day escrow Your earnings automatically pay out once a month to the bank associated with your seller account. This occurs on the first of every month. You cannot access your earnings outside of the payout schedule. For some countries and currencies, there is a non-zero minimum payout amount. These minimums are documented in the [Stripe minimum payouts](https://docs.stripe.com/payouts#minimum-payout-amounts-table) table and [Stripe cross-border minimum payout amounts](https://docs.stripe.com/payouts#cbp-minimum-payout-amounts) table—the effective minimum is the greater number. ISK, HUF, TWD and UGX currencies have [special conditions](https://docs.stripe.com/currencies#special-cases). > **Warning:** If your seller eligibility is suspended, either due to a change of seller information or moderation consequences, payouts are frozen until you can restore your eligibility. To see the status of your account, see the [Seller Onboarding](https://create.roblox.com/settings/eligibility/priced-assets) page. ### Reimbursements Reimbursements for customers are deducted from the seller's account. This may occur if: - A customer requests a refund or chargeback for an asset. - An asset is moderated after customers have already purchased it, and the moderation decision is not successfully appealed within 5 days. Uploading a new version of the moderated asset will not prevent reimbursements. When a refund occurs, Roblox removes the asset from the customer's inventory. If a very high proportion of sales for an asset come from accounts with an exceptional history of chargebacks, the asset is taken off sale and your payouts are frozen. If you have grounds to appeal this action, you can file a request with [Roblox support](https://en.help.roblox.com/hc/en-us/articles/360000245263-Appeal-Your-Content-or-Account-Moderation). ## FAQ #### Seller FAQ **Can I sell models for Robux?** No, the real-world pricing model does not support Robux. You can only price assets on the Creator Store in USD. **My country currently is not supported, how can I sell my models?** Due to regional restrictions, the Creator Store has limited support for some buyers and sellers. In order to become a seller, you must reside in a country that Roblox's third-party payments provider supports. For more information, see Stripe's [Cross-border payouts](https://stripe.com/docs/connect/cross-border-payouts) documentation. **Can I sell my existing models?** If your existing models contain child image or mesh assets that you created before opting into the Asset Privacy beta, they were set to Open Use by default, meaning that they are freely available for anyone to use on Roblox if they have their assetIDs. To protect access to your content, it's recommended to: 1. Opt-in to the Asset Privacy beta. 2. Only sell newly-uploaded or versioned models with all child assets set to Restricted. If you don't opt-in to Asset Privacy beta and set your newly uploaded image and mesh assets to Restricted, anyone will be able to easily access and use them in their projects. > **Warning:** You can only sell models and plugins that **you** own from an individual account. You cannot sell group-owned assets or assets owned by someone else. **Can I directly share my own assets with others, even if I'm selling them on the Creator Store?** Yes. You can freely share assets that were created by you with other creators, groups, or games through the Creator Hub's asset permissions page. **Can I sell something I edited, but originally bought from someone else on the Creator Store?** No, you cannot sell or distribute composite models that contain Restricted assets that you didn't create, with the exception of Audio and Video that is also available on the Creator Store. You can share these models directly with your collaborators, but Roblox will not allow you to list assets you did not create on the Creator Store. **Is asset privacy a form of Digital Rights Management (DRM)? Does it prevent others from sharing my paid models with others?** No, the asset privacy feature controls how creators can purchase and load your assets on Roblox, it is not a form of DRM. If you suspect a rights violation involving your asset, you can submit the content for removal using the [Rights Manager](/docs/en-us/production/publishing/rights-manager.md). **Will my paid models be used to train Roblox's AI?** You can configure your data-sharing preferences through the Creator Hub. Paid models are only used for AI-enhancement purposes if you choose to opt-in to data sharing. If you opt-in an asset, default for free assets on the Store, and then later opt-out: - Roblox will remove it from the training dataset within 30 days. - Any AI Models that were trained using your data will be updated within 365 days. For more information, see [AI data sharing](/docs/en-us/ai-data-sharing.md). #### Customer FAQ **Can I buy models with Robux?** No, the real-world pricing model does not support Robux. You can only purchase assets on the Creator Store in USD. **Why do I have free models in my experience that aren't also in my Inventory?** Free assets you inserted into your experience but did not acquire won't appear in your inventory unless you acquire it. If you are browsing the Creator Store in Studio, you can insert most assets into your games without needing to acquire the assets. This can be helpful for creators who want to test out models, but we strongly recommend acquiring assets you wish to use in all of your projects. Cross-publishing works with models you previously inserted, but copy-paste workflows do not work. **Can I put a model I purchased into a group or collaborative experience?** Yes, but there are a few restrictions and workflows which may not work as expected. **Copy and paste workflows** If you insert a Model you purchased into a shared experience, your collaborators may be prevented from copying and pasting it into another experience unless they or the destination experience already have access to it. Note that inserting a model that you own will automatically grant that experience permission to use the model. Cross-publishing between experiences is supported with individual and group-owned experiences. However, because of current limitations with asset ownership, copy and paste flows will be broken for collaborators across group-owned experiences who do not own the model. **Local place workflows** You need to ensure that experiences have permissions to the model in Creator Hub. Attempting to load a paid model's Restricted child image and mesh assets without permission will result in errors in the output window. **Can I put a model I purchased into an un-copylocked experience?** Yes, but for people forking the experience, none of the model's Restricted child image and mesh assets will load unless that person has also purchased the model. **Can I dynamically load a paid model in an experience?** This will only work if the experience is owned by someone who bought the asset or if the experience itself has permission, which you can [grant](/docs/en-us/projects/assets/privacy.md#grant-permissions) on the asset's permissions page. It will not work for: - Group experiences, unless the model was inserted into the experience before publish time. - Collaborative experiences where the experience owner does not also own it. **How do I report an asset?** If you discover or purchase an asset that you believe violates Roblox policies, report it using the **Report Item** option on the asset's detail page. --- title: "Experiments" url: /docs/en-us/production/experiments last_updated: 2026-06-29T19:34:04Z description: "Experiments let you run A/B tests within your experiences." --- # Experiments **Experiments** let you run in-game and matchmaking A/B tests to measure the causal impact of changes to your experience. For example, you can show different onboarding experiences to different players and measure the difference in playtime, retention, and other key performance indicators. Experiments are excellent for measuring the following: - Engagement - Onboarding flows, progression systems, control schemes, custom matchmaking - Monetization - Shop visibility and user experience, starter pack types, pricing ![Overview of the Experiments page on Creator Hub](../assets/analytics/configs/experiment-overview.png) ## Create experiments Experiments come in two types: - **In-experience** experiments let you measure the impact of different [config values](/docs/en-us/production/configs.md). - **Matchmaking experiments** let you measure the impact of different [custom matchmaking configurations](/docs/en-us/matchmaking/customize-matchmaking.md). Unlike in-experience experiments, you can only run one matchmaking experiment at a time. #### In-experience 1. If you don't already have a [config](/docs/en-us/production/configs.md), create one for your experience. 2. On the [Creator Hub](https://create.roblox.com/dashboard/creations) **Experiments** page for your experience, click **Create experiment**. 3. For **Type**, choose **In-experience**. 4. Specify a name, goal metric, and planned duration for the experiment. Experiments run for between 14-60 days. Regardless of what you choose as your goal metric, experiments track all [metrics](#metrics) in the list. 5. Choose a percent rollout. This number is the percentage of players that you want to include in the experiment. In general, the more people you include in an experiment, the better the data, but use your judgment on what's best for your experience. > **Success:** Pay attention to the [minimum detectable effect](#best-practices-for-experiments), which is the smallest change in the selected metric that the experiment can reliably detect. 6. Specify variants and percentages. Variants are alternative values for your config. For a numeric config key `bossHealth` with a control value of 500, you might specify a variant of 300. You can have up to **two variants** and one control in an experiment. Percentages dictate how to assign variants **within the experiment rollout**. Consider the following example: - You choose an overall rollout of 40%. - You specify two variants and a 50/50 split between them and the control. In this example, 60% of your users are excluded from the experiment; these users receive the control and have no impact on experiment results. Approximately 20% of your users receive the control as part of the experiment. Another 20% receive the variant. Depending on your player count, this distribution might not be large enough to yield actionable results. > **Info:** When in doubt, we recommend simple 50/50 experiments of one variant and one control on large rollouts. The experiments are easier to configure, and the results are easier to interpret.![Variant page](../assets/analytics/configs/experiment-variant.png) 7. The final step is scheduling. You can start experiments immediately or schedule them for a later date and time. After you schedule an experiment, you can't change its configuration (duration, rollout percentage, variants, etc.), but you can reschedule it. #### Matchmaking 1. If you don't already have a [custom matchmaking configuration](/docs/en-us/matchmaking/customize-matchmaking.md), create one for your experience. 2. On the [Creator Hub](https://create.roblox.com/dashboard/creations) **Experiments** page for your experience, click **Create experiment**. 3. For **Type**, choose **Matchmaking**. 4. Specify a name, goal metric, and planned duration for the experiment. Experiments run for between 14-60 days. Regardless of what you choose as your goal metric, experiments track all [metrics](#metrics) in the list. 5. Choose a percent rollout. This number is the percentage of players that you want to include in the experiment. For matchmaking experiments, we highly recommend 100% rollout. It minimizes the risk of isolating players and leads to faster, more reliable results. > **Success:** Pay attention to the [minimum detectable effect](#best-practices-for-experiments), which is the smallest change in the selected metric that the experiment can reliably detect. 6. Choose the places you want to include in the experiment, and specify variants. Variants are alternative matchmaking configurations. You can include up to three variants in matchmaking experiments. Regardless of how many variants you include, Roblox splits players equally between all variants. > **Info:** When in doubt, we recommend simple 50/50 experiments of one variant and one control on large rollouts. The experiments are easier to configure, and the results are easier to interpret.![Matchmaking experiment variant page](../assets/analytics/configs/experiment-variant-place.png) 7. The final step is scheduling. You can start experiments immediately or schedule them for a later date and time. After you schedule an experiment, you can't change its configuration (duration, rollout percentage, variants, etc.), but you can reschedule it. ### Metrics Experiments track all of the following metrics over the experiment duration. | Metric | Description | | --- | --- | | D1 retention | Percentage of players who returned to your experience after one day. | | D7 retention | Percentage of players who returned to your experience after one week. | | Playtime | Average amount of time players spent within your experience. Cumulative for the duration of the experiment. | | ARPU | Average revenue per user. Revenue divided by the number of players. Cumulative for the duration of the experiment. | | ARPPU | Average revenue per paying user. Revenue divided by the number of players who purchased an experience-related item. Cumulative for the duration of the experiment. | | Payer conversion rate | Percentage of players who purchased an experience-related item. | | Session time | Playtime divided by number of sessions. Cumulative for the duration of the experiment. | ### Experiment status The **Experiments** page shows the following statuses for experiments. | Status | Description | | --- | --- | | Completed | The experiment is over, which happens when you stop it manually, when you reach a decision, or automatically shortly after the decision date (14 days after for in-experience, immediately for matchmaking). You can still review the details and results. | | Decision needed | The experiment has reached its decision date. Now is a good time to review the results. | | Running | The experiment is running but has yet to reach its decision date. | | Scheduled | The experiment is schedule to start at a future date. | | Draft | The experiment hasn't been started or scheduled. You can finish setting it up. | ## Add experiments to your code > **Info:** This section only applies to in-experience experiments. Matchmaking experiments do not require code changes. Applying in-experience experiments is similar to [applying configs](/docs/en-us/production/configs.md#add-configs-to-your-code). The main difference is the use of `Class.ConfigService:GetConfigForPlayerAsync()` rather than `Class.ConfigService:GetConfigAsync()`. `Class.ConfigService:GetConfigForPlayerAsync()|GetConfigForPlayerAsync()` retrieves a player-specific snapshot. When you call `Class.ConfigSnapshot:GetValue()|GetValue()`, the snapshot checks for an active experiment and enrolls (or doesn't enroll) the user based on the rollout percentage. ```lua local ConfigService = game:GetService("ConfigService") local Players = game:GetService("Players") local function onPlayerAdded(player) local playerConfig = ConfigService:GetConfigForPlayerAsync(player) local leaderboardColor = playerConfig:GetValue("leaderboardColor") end Players.PlayerAdded:Connect(onPlayerAdded) ``` - You must call `Class.ConfigService:GetConfigForPlayerAsync()|GetConfigForPlayerAsync()` separately for each player; `Class.ConfigService:GetConfigAsync()|GetConfigAsync()` does not apply experiments. - After you call `Class.ConfigSnapshot:GetValue()|GetValue()` on a player-specific snapshot, the player associated with the snapshot is enrolled in the experiment for that key and that key only. All subsequent calls to the method return the same control or variant for the duration of the experiment. Only the first call is random. > **Success:** Wait to call `Class.ConfigSnapshot:GetValue()|GetValue()` until you need it. Calling `Class.ConfigSnapshot:GetValue()|GetValue()` too early can cause you to enroll players who never interact with the part of the experience you're experimenting on. - Enrollment in experiments isn't limited to new users. Even if a user previously received a value from `Class.ConfigService:GetConfigAsync()|GetConfigAsync()`, you can still enroll them in an experiement using a player-specific snapshot from `Class.ConfigService:GetConfigForPlayerAsync()|GetConfigForPlayerAsync()`. - If a key in a player-specific snapshot doesn't have an active experiment, `Class.ConfigSnapshot:GetValue()|GetValue()` returns the standard config value (or nil if it has no value). ### Targeted enrollment If you want to target some portion of your players that meet specific criteria, you have to write additional code to check for those criteria and only then call `Class.ConfigSnapshot:GetValue()|GetValue()` to enroll them in the experiment. Consider the following example: - You want to test a new control scheme in your experience. - You don't want to include existing players (who are presumably used to the existing scheme), only new players. Your code might look something like this: ```lua local function getControlScheme(player, racesCompleted) if racesCompleted > 0 then return "standardScheme" else -- Player is new, enroll in experiment local playerConfigSnapshot = ConfigService:GetConfigForPlayerAsync(player) if playerConfigSnapshot:GetValue("useNewControlScheme") then return "newScheme" else return "standardScheme" end end end ``` If you want the control scheme to persist on subsequent sessions, you likely need to add a value to the player's entry in a data store. ## View and interpret results After an experiment has run for at least 24 hours, click **View** to see details and results. ![The details page for an experiment](../assets/analytics/configs/experiment-details.png) You can see the total number of players enrolled, as well as the number of players that received the control value and each variant. Viewing this page early in the experiment is useful strictly for making sure the experiment is running properly, **not** for taking action. Before taking action, see [Best practices](#best-practices-for-experiments). After the experiment is complete, check the **Results** tab. Look for statistically significant changes in goal metrics, which the dashboard highlights in green or red. These changes are more likely to show the impact of your variant and less likely to be false positives or negatives. ![The details page for an experiment](../assets/analytics/configs/experiment-results.png) Hover over any metric to see the **View confidence** button, which shows the confidence interval. A metric is statistically significant when the confidence interval for its percent change does not overlap with 0%. In the following example, D1 retention is up 17.4%, with lower and upper bounds 8.02% and 22.03%, which makes the change statistically significant. ![Confidence interval for a metric](../assets/analytics/configs/experiment-confidence.png) For convenience, the results page lets you replace the default config value with one of the variants from the experiment. Click **Make decision** to choose a variant or **Change winner** if you change your mind. If you then return to the **Configs** page, you should see the new value. ## Best practices for experiments - **Use the minimum detectable effect (MDE)** to decide if your experiment is worth running. Roblox calculates MDE using your goal metric and number of players per variant, which is based on daily active users, rollout percentage, experiment duration, and variant splits. If the MDE is too high for your goal metric (for example, more than 100%), it's unlikely you can reach statistical significance. Experiences with fewer than 1,000 daily active users might struggle to get useful data from experiments.![Insufficient MDE screen during creation.](../assets/analytics/configs/experiment-mde.png) - **Start with a hypothesis.** Rather than just changing a variable and checking the results, write a cause-and-effect statement about what you changed, what you expect to happen, and why. As you experiment more and more, having a set of written hypotheses to accompany your results can help clarify your thinking and spark new ideas for experiments. - **Let experiments run for their full durations.** The novelty effect (temporary interest in a change not because it's better, but because it's new) can heavily skew early results, sometimes causing them to swing in and out of statistical significance. Ending experiments early increases the odds of you taking premature action based on anomalous spikes that more data would have smoothed out or even contradicted. - **Don't act without statistical significance.** Even seemingly large changes in player behavior might not be statistically significant, generally due to small sample size. If a change isn't statistically significant, ignore it. - **Avoid changes during experiments**. Major bugs of course need fixes, but changes to experience content can impact player behavior and invalidate your results, even if the changes **seem** unrelated to your experiment. Similarly, only run experiments simultaneously if you're confident they won't interact with each other. - **Use confidence intervals for deep dives** into metrics and to check for borderline cases of statistical significance. If the confidence interval is too wide, the metric might never reach statistical significance. - If one metric is significantly up and another significantly down, you have to **decide whether the trade-off is worth it**, possibly in conjunction with other statistically significant movements. - Experiments provide strong signal, but **statistical significance deals in probabilities, not certainties**—hence the confidence interval. Data variability, sample size, and magnitude of the change all impact the probability of detecting whether a variant affected player behavior. Any action you take based on the results of an experiment should be balanced against qualitative data like player feedback and your overall vision for the experience. - **Document your findings and decisions.** Even if you don't use them to run additional experiments, having a body of knowledge and evidence can inform how you design your experiences. --- title: "Analyze and experiment" url: /docs/en-us/production/game-design/analytics-essentials last_updated: 2026-06-29T19:34:04Z description: "Analytics essentials" --- # Analyze and experiment Roblox offers a variety of analytics features to help you chart your experience's growth, track user behavior and retention, and find opportunities for optimization. You can use analytics to understand what actions you can take to grow your experience. Analytics provide a high level overview of your experience's health and surface optimization opportunities. The highest level of analytics you can use to uncover these opportunities are called **key performance indicators** (KPIs). Three of the most prominent KPIs are: - Engagement metrics - Retention metrics - Monetization metrics Once you identify an area for improvement through your KPIs, you can use **Experiments** and **Configs** to iterate with precision. **Experiments** allow you to run A/B tests to measure the causal impact of specific changes on your metrics, while **Configs** let you adjust in-game values in real time to optimize the experience instantly without restarting your servers. For an introduction to what Roblox's analytics features can do for you and your experience, see [Get started with analytics](/docs/en-us/production/analytics/get-started.md). --- title: "Content updates" url: /docs/en-us/production/game-design/content-updates last_updated: 2026-06-29T19:34:04Z description: "Content updates" --- # Content updates A **content cadence** is the regular release of new content updates that can include items, quests, playable maps, and other content in an experience. It's a part of **live operations** (LiveOps), the ongoing support of a live experience, which also includes experience expansions, bug fixes, and quality of life updates. Content releases keep players engaged between larger updates and can be an essential element of a live experience's monetization strategy. Maintaining a content cadence requires developers to: - Choose correct content - Manage scope - Establish a routine - Prioritize sustainability ## Choose correct content A content cadence draws on existing gameplay systems to produce small, frequent releases of content. By being simple and relatively easy to produce, these rapid releases can be maintained without becoming an undue burden on the developers. Common examples of a content cadence include: - Avatar items - Furniture - Pets - Vehicles - Weapons - Levels/maps - Quests Many of these examples are predominantly art-based, requiring little to no programming or design work to produce. Simple variants on existing assets, like assets with only slight changes to color, are ideal because they can be produced quickly on a recurring schedule by the fewest possible team members. A good example of a content cadence are Pet Variants in [_Adopt Me!_](https://www.roblox.com/games/920587237/) _Spotted pet variant in _Adopt Me!__ _Colored pet variant in _Adopt Me!__ These examples only work in experiences with systems in place to support simple variations. Adding new systems to an experience to support new deliverables turns the content into an expansion, which would be unsustainable for the creation of a content cadence. Cadenced content should consist of items and experiences that players regularly want. Tracking player behavior through analytics and gathering player feedback can provide valuable insight into the highest-value content to target. #### Themed Content Ideally, there are multiple systems in an experience from which cadenced content can be produced. Building releases around a theme can help to unlock creativity and generate ideas for assets that fit together conceptually and aesthetically, like with _Adopt Me!_ themed updates. _Themed update in _Adopt Me!__ Seasons and holidays are also rich sources of ideas with potential for limited-time cadence content. However, it is important to keep in mind that holidays are not universal and may not appeal to all players. Developers must determine which seasonal themes will be most popular with their audience. _Seasonal content in [_Robloxian High School_](https://www.roblox.com/games/447452406/)_ __ The **core loop** is a good place to start in identifying systems to support with content releases. Offering a variety of content allows developers to keep their cadence releases interesting, reduce the possibility of players becoming bored or overwhelmed by any one content type. For more about core loops, see [Core Loops](/docs/en-us/production/game-design/core-loops.md). ## Manage scope Scope is the amount of content contained in an update and the hours of work required to produce it. Spending fewer than three weeks' effort on content cadence is recommended in order to maintain a rapid update schedule and leave room for other LiveOps releases. The amount and type of content that can be produced in that time is dependent on each developer's capabilities and priorities. ## Establish a routine Establishing a routine release cadence encourages players to check back often so they don't miss out on new releases. They may begin to anticipate the next release, and even speculate about it on social media, increasing awareness and interest among other players. A regular release cadence also creates a beneficial routine for developers. Releasing on a schedule requires planning, coordination and communication between team members. With practice, developers can become more efficient at designing and releasing updates while maintaining a healthy work-life balance. Determining the appropriate frequency of releases for an experience must take into account factors such as the capabilities and availability of the developers, the scope of the planned releases, and player sentiment. In order to maintain player engagement between expansions, many experiences release content cadence updates every two weeks to one month. ## Prioritize sustainability To keep players engaged between updates, cadence content should not be immediately consumed by the majority of players,otherwise its purpose is defeated and developers are forced to release more frequently to keep up with demand. Requiring players to earn the content over time extends the life of each release and gives developers space to work on other update types. Because content requires time and effort to produce, it's imperative to produce content sustainably with: - Progression - Limited-time content - Season passes ### Progression Experiences with progression, such as player levels or gated map zones, can use their content cadence to add more permanent content at the end of the progression, where seasoned players are starting to run out of objectives. Those high-level players will appreciate the fresh content that they can play immediately, while newer players will have more content to look forward to reaching in the future. ### Limited time content Limited-time content is content which is available to all players but only for the duration of an event. Players may be required to complete quests, achieve gameplay milestones,or earn event currency that they can exchange for the limited-time items. The limited-time nature of the event drives player engagement, and the objectives are balanced so that most players will require weeks to exhaust the entirety of the content. ### Season passes A **season pass** system, also known as a battle pass, is a combination of limited-time event and progression system in which players must complete a series of quest objectives to earn item rewards. Season passes can be adapted to most experiences, offering developers a straightforward framework for designing cadence releases. _Season Pass in [_Jailbreak_](https://www.roblox.com/games/606849621/)_ However they choose to deliver their content updates, if developers structure their releases sustainably and keep up with the cadence, players will never be without new content for long. --- title: "Core loops" url: /docs/en-us/production/game-design/core-loops last_updated: 2026-06-29T19:34:04Z description: "Core Loops" --- # Core loops An experience's **core loop** is the central gameplay through which an entire experience is built. An experience's core loop is divided into three sections: - Minute to minute player interaction - Most repeated set of actions - Progression engine ## Minute to minute player interaction The first step in a core loop is the **minute to minute player interaction**. These constant actions form the baseline for the experience and vary depending on genre. In an action RPG, exploration represents the minute to minute player interaction. The player constantly explores the world, either discovering new areas or revisiting previously explored ones. ![A diagram of three light blue circles in a pyramid configuration with white arrows between them to signify an ordered cyclical relationship. The top blue circle that signifies the first step says Explore.](../../assets/game-design/core-loops/core-loops1.png)_First step in a core loop in an Action RPG_ ## Most repeated set of actions The second step in a core loop is the **most repeated set of actions**. The minute to minute interaction facilitates these actions, and these are often the defining mechanic of the experience. While experiences might involve multiple action sets, the most frequently repeated actions become part of the experience's core loop. In an action RPG, fighting is the most repeated set of actions. As they explore the world, players continually engage in combat. Though "fighting" comprises various individual actions, such as dodging, striking, and guarding, they fall under the single core mechanic. ![A diagram of three light blue circles in a pyramid configuration with white arrows between them to signify an ordered cyclical relationship. The top blue circle that signifies the first step says Explore, and the second blue circle on the bottom right-hand side that signifies the second step says Fight.](../../assets/game-design/core-loops/core-loops2.png)_Second step in a core loop in an Action RPG_ ## Progression engine The third step in a core loop is the **progression engine**, which propels players through the experience. Without a progression system, an experience becomes repetitive, boring, and shallow. In an action RPG, the upgrade tree serves as the progression system. Once players finish exploring and fighting, they require a system to manifest these changes. This process includes leveling up and acquiring new skills, which then boost the player's future exploration and combat capabilities. ![A diagram of three light blue circles in a pyramid configuration with white arrows between them to signify an ordered cyclical relationship. The top blue circle that signifies the first step says Explore, the second blue circle on the bottom right-hand side that signifies the second step says Fight, and the third blue circle on the bottom left-hand side that signifies the third step says Upgrade.](../../assets/game-design/core-loops/core-loops3.png)_Third step in a core loop in an Action RPG_ When you design a core loop effectively, you create engaging and dynamic experiences that captivate players. A well-designed core loop fosters a seamless, repeatable, enjoyable gameplay cycle. --- title: "Design for Roblox" url: /docs/en-us/production/game-design/design-for-roblox last_updated: 2026-06-29T19:34:04Z description: "Explains overall concepts for designing experiences on Roblox." --- # Design for Roblox Roblox is unique, and the expectations and behavior of its user base can be confusing at first. Understanding what informs this is critical to achieving success on the platform. Your designs should reflect the unique strengths of Roblox to thrive within it, and is done by performing the following: - Designing for behavior - Designing for the audience - Designing for the engine ## Design for behavior Each experience on Roblox exists within the **wider context** of the platform and its catalog, with users often hopping from one experience to another with friends. Always consider the way that user behavior on Roblox reflects this when designing an experience. #### First time user experience (FTUE) Because experiences are so easy to join and leave, FTUEs that get users into the fun quickly tend to do better on Roblox. Lengthy, detailed tutorials are liable to bore users and cause them to bounce off your experience in search of something more immediately engaging. Ensure that any tutorials you do include are as visual as possible to make it easier for less experienced readers. This visual emphasis also simplifies translation. Be conscious of the norms of your genre. The closer your design patterns and user experience are to the most popular experiences in that genre, the less you have to explain upfront, and the more intuitive your experience becomes to seasoned Roblox users. Bear in mind that users often help each other out and fill in the gaps of what you don't explain explicitly, either through socializing off-platform or through the in-experience chat. #### Social interaction Many users treat Roblox as a place to hang out with their friends. Creating an experience conducive to social interaction encourages this kind of engagement and helps to grow your audience as users invite their friends to play with them. You can even make invitations a feature of your gameplay through mechanics such as parties and trading. Encouraging social gameplay can also help with **retention**, as users can act as evergreen sources of content for each other. Many popular role-playing experiences such as [Brookhaven](https://www.roblox.com/games/4924922222/) focus on providing users with the tools they need to interact with each other within the experience, and these interactions become the spontaneous content that keeps users coming back. For these reasons, single-player experiences often find it harder to build and retain an audience on Roblox. Users spend a lot of time and money on their avatars and typically want them to be seen by their friends and other users. A common identity across the platform is part of what makes Roblox special, so if you're going to change that for your experience, make sure you have a good reason. To learn how to implement custom avatars in your experience, see [Customize avatar appearances](/docs/en-us/characters/appearance.md). Ensure you don't get in the way of social interaction with your design or monetization strategies. For instance, avoid making private servers prohibitively expensive, as this may discourage groups of friends from playing together. To learn how to implement private servers into your experience, see [Private servers](/docs/en-us/production/monetization/private-servers.md). #### Tourists and locals In terms of their behavior, it can be helpful when designing to think of Roblox users in two categories: - **Tourists:** Tourists typically hop from one experience to another, prioritizing variety over depth. - **Locals:** Locals are more likely to focus on a particular experience or smaller set of experiences. They engage more deeply and typically form almost all an experience's engaged user base. Bear in mind that many locals start out as tourists and convert, so don't neglect tourists in your designs and monetization strategies. For more about monetizing your experience, see [Earn on Roblox](/docs/en-us/monetize-experiences.md). ## Design for the audience Understanding the sometimes surprising expectations of the Roblox audience is essential to designing effectively for them. While Roblox has traditionally had a younger audience, there is a growing opportunity to create experiences that appeal to older Roblox users. For more about the Roblox audience, see [The Roblox user base](/docs/en-us/production/roblox-user-base.md). #### Younger audiences Younger users tend to be more interested in exploration, experimentation, and socializing than they are in competition or specific goals. Younger audiences consume content differently, informed by the instant content of social media and YouTube and driven by online interaction with their friends, and Roblox reflects this. Watch kids playing Roblox if you want to understand what makes it work. Many top experiences on the platform may seem simplistic to older audiences, but they typically tap into common childhood experiences. For instance: - [Brookhaven](https://www.roblox.com/games/4924922222/) is a roleplaying game that resembles playing "house." - [Islands](https://www.roblox.com/games/4872321990/) gives users a building set, similar to Legos. - [Jailbreak](https://www.roblox.com/games/606849621/) is a deeper version of "cops and robbers." Weirdness and creativity are a core part of Roblox culture, shaped by the openness to experimentation of its younger users. While iterating on proven designs and themes is still a relevant strategy on Roblox, strange and innovative experiences can find more success on Roblox than they would elsewhere. You may have encountered a number of interesting genres unique to Roblox, and many of the expectations which characterize the content on other platforms are not shared by younger users. While stunning visuals can help your experience make a good first impression, the most important thing is to make sure it is fun to play. Younger users are often less sensitive to visual fidelity and more likely to stick around if they're having a good time, regardless of how your experience looks. #### Older audiences While Roblox's user base was built on its younger audience, that audience is growing older every day. Many experiences are finding increasing levels of success in appealing to older Roblox users, which often involves a greater overlap with the genres and norms common to other platforms. Older users are more likely to invest more in your experience in terms of both engagement and monetization. Older users are the foundation of the communities that form around experiences, and are more likely to convert from tourist to local. Some top experiences which appeal to older users include: - [Phantom Forces](https://www.roblox.com/games/292439477/), a realistic multiplayer first-person shooter. - [Arsenal](https://www.roblox.com/games/286090429/), a fast-paced arcade-style shooter with rotating weapons. - [Doors](https://www.roblox.com/games/6516141723/), a procedural survival horror game. Though older users' interests overlap more with content on other platforms, don't just port an experience from another platform without catering to the unique characteristics of Roblox. An experience which has succeeded elsewhere is not necessarily guaranteed to succeed on Roblox without some contextual adjustment, for instance by slimming down upfront tutorials and emphasizing social gameplay. #### Influencers Many Roblox users count Roblox and YouTube as their top two content platforms. Users often follow their favorite YouTubers into new experiences, especially if it looks like a lot of fun. Consider how your experience might work in a streaming context when designing: could a streamer make great, fun content with it? Can they easily involve their friends, or other streamers? Can you make their job easier? For instance, [Brookhaven](https://www.roblox.com/games/4924922222/) has a "Creator Cam" where content creators can hide the UI when they're recording a video. For more about promoting your experience, see [Promoting on Roblox](/docs/en-us/production/promotion.md). ## Design for the engine While Roblox Studio offers a multitude of features for creating ambitious experiences with high-end visuals, it's wise not to lose focus on performance on lower-end devices. Most users play Roblox on mobile devices, and the audience is sensitive to friction and load times, so be conscious of performance in your designs. Roblox Studio makes it easy to prototype your experiences through solid modeling and the **Toolbox**. These tools are invaluable for testing and iteration. For more information, see the following resources: - [Solid modeling](/docs/en-us/parts/solid-modeling.md) - [Toolbox](/docs/en-us/projects/assets/toolbox.md) - [Prototyping](/docs/en-us/production/game-design/prototyping.md) #### UI and UX The majority of Roblox users play on a mobile device, so it's wise to design your user interfaces (**UI**) and user experience (**UX**) around mobile devices first. If you want to cater to Xbox/PlayStation or Windows/Mac users, bind commonly used actions to shortcuts on a gamepad and specific keys on keyboards. For more information, see [Input and camera](/docs/en-us/input.md) All Roblox experiences share a few **core UI** elements, such as the chat and player list. Test your own UI regularly to make sure you avoid conflicting with where they sit on the screen, or disable anything you don't want to show. Many experiences share similar UI patterns, such as having inventory slots along the bottom of the screen. Replicating these patterns will ensure experienced Roblox users will intuitively understand how to use your interface. Use consistent icons as much as possible, and keep everything as visual as you can. Many younger users struggle to read text-heavy interfaces, and regardless of ability many younger users are more reading-averse. Visual UI is also easier to translate for international audiences. For a detailed discussion of UI and UX principles on Roblox, see [UI and UX](/docs/en-us/production/game-design/ui-ux-design.md). #### Genre examples The following table includes descriptions and examples of common genres found in gaming experiences on Roblox. These categories are not exclusive and names are not fixed. | Name | Description | Example | | --- | --- | --- | | Action | Centered around combat between players. Single-player action games are rare. Generally less popular on Roblox than other platforms. | [Arsenal](https://www.roblox.com/games/286090429/) | | Customization | Common central mechanic where gameplay is focused on looking for customization options. Often found in the context of 'dressing up' and shopping for clothes. | [Fashion Famous](https://www.roblox.com/games/568350650/) | | Hide and Seek | Players hide from an enemy, either controlled by another player or non-playable character. Often combined with puzzle elements or obstacles. | [Piggy](https://www.roblox.com/games/4623386862/) | | Obby | Shorthand for "obstacle course" and known on other platforms as a "platformer". Requires players to climb, jump and navigate obstacles to reach the end of the course. | [Tower of Hell](https://www.roblox.com/games/1962086868/) | | Roleplaying | Often focused on building and exploring, with some including content from other genres. Typically more expressly oriented around imaginative social roleplaying than story-based 'RPGs' on other platforms. | [Brookhaven](https://www.roblox.com/games/4924922222/) | | Simulator | A genre where players perform simple, repetitive actions in order to make progress. The goal often involves clicking an item in the world until it reaches its maximum level, then exchanging that item for an upgraded version with higher maximum potential. Comparable to "clicker" games, which are also popular. | [Bee Swarm Simulator](https://www.roblox.com/games/1537690962/) | | Survival | Players attempt to survive or escape external obstacles, such as environmental hazards. | [Natural Disaster Survival](https://www.roblox.com/games/189707/) | | Tycoon | Players collect from "droppers" that produce currency over time, in order to pay to construct something. As each part is constructed, new parts and droppers unlock for purchase. This cycle of collecting, building, and waiting to collect continues until there are no more additions left to build. | [Lumber Tycoon 2](https://www.roblox.com/games/13822889/) | --- title: "Design experiences on Roblox" url: /docs/en-us/production/game-design last_updated: 2026-06-29T19:34:04Z description: "Design experiences on Roblox" --- # Design experiences on Roblox A designer's role is to create an engaging experience for players, retain those players over time, and devise a sustainable monetization model to fund further development. Whether you are a novice designer or a veteran, Roblox offers a variety of resources to help you through the design process. ## What's new _Sketch Series: Onboarding Techniques_ _Sketch Series: Retention Metrics_ ## Design your experience - [Design for Roblox:](/docs/en-us/production/game-design/design-for-roblox.md) Learn how Roblox is unique as a platform and how to design experiences with these factors in mind. - [Core loops:](/docs/en-us/production/game-design/core-loops.md) Learn how to design central gameplay systems to build your experience from the ground up. - [Prototyping:](/docs/en-us/production/game-design/prototyping.md) Learn different prototyping methods for exploring your experience's potential and how they can help bring your vision to life. ## Build your experience - [Onboarding:](/docs/en-us/production/game-design/onboarding.md) Learn how to design an effective onboarding experience to retain as many new players as possible, increase potential concurrent and daily active users, and improve your experience's monetization potential. - [Onboarding techniques:](/docs/en-us/production/game-design/onboarding-techniques.md) Learn strategies to help direct players through the onboarding funnel as quickly and seamlessly as possible. - [Quest design:](/docs/en-us/production/game-design/introduction-to-quest-design.md) Learn how to effectively design quests, achievements, and dailies to increase engagement and retention in your experiences. - [UI and UX design:](/docs/en-us/production/game-design/ui-ux-design.md) Learn how to design user interfaces and how to design an intuitive and engaging experience. ## Support your experience - [LiveOps essentials:](/docs/en-us/production/game-design/liveops-essentials.md) Learn how to design content that keeps players coming back and enjoying your experience after launch. - [LiveOps planning:](/docs/en-us/production/game-design/liveops-essentials.md) Learn how to maintain a dynamic and captivating experience through interweaving the release of two types of content. - [Analytics essentials:](/docs/en-us/production/game-design/analytics-essentials.md) Learn how to leverage key performance indicators (KPIs) to identify the health of your experience and potential growth opportunities. - [Content updates:](/docs/en-us/production/game-design/content-updates.md) Learn how to design content cadences to keep players engaged between larger experience updates. --- title: "Introduction to quest design" url: /docs/en-us/production/game-design/introduction-to-quest-design last_updated: 2026-06-29T19:34:04Z description: "Introduction to Quest Design, achievements, and dailies." --- # Introduction to quest design _Introduction to quests, achievements, and dailies_ Quests are an experience mechanic in which players complete objectives in order to earn rewards like items, currency, or badges. Quests provide players with fresh goals and incentives, which can motivate them to play longer and more often, increasing their [engagement](/docs/en-us/production/analytics/engagement.md). They can also reduce boredom and provide a sense of progression and accomplishment that lead to better [retention](/docs/en-us/production/analytics/retention.md). > **Info:** For out-of-the-box functionality to create quests, see the [Missions](/docs/en-us/resources/feature-packages/missions.md) feature package. ## Quest structure Structurally, quests consist of three components: - **Objective:** The objective is a task that the player must complete in order to earn the reward. Collecting items, slaying monsters, and traveling to points of interest on a map are some common examples of quest objectives. Quests can consist of single objectives, or multiple. - **Quantity:** The quantity is a modifier on the objective: collect 100 apples, slay 5 monsters, visit 10 players' homes. Quantity is one way to modify the difficulty of a quest, with larger quantities requiring more effort on the players' part in order to complete the objective. - **Reward:** The reward is the incentive for players to complete the quest. Common rewards are experience points (XP), currencies like gold coins, consumables like health potions, and other items of value. Generally, the more challenging the quest, the more valuable the rewards, so that players feel that the effort to complete the quest is worthwhile. The structure of a quest can vary from a single task to multiple tasks, all of which a player must complete to earn the reward. They might also unlock additional quests upon completion, called a called a **quest chain**. There are two common types of quests: - **Achievements:** Also known as badges on Roblox, are single-task quests that often require significant time and effort to complete. Earning a particularly challenging achievement is a source of pride, and badges allow players to show off their accomplishments to others. Achievements can also act as an indicator of player progression, representing milestones that they have achieved throughout their journey. To learn how to implement achievements in your experience, see [Badges](/docs/en-us/production/publishing/badges.md).![A close up view of an achievement badge for the Jailbreak experience that awards players who achieve the highest bounty in a busy server. The badge on the left-hand side displays a lightskinned blocky male avatar turned to his right to look at text that says Smooth Criminal.](../../assets/game-design/introduction-to-quest-design/quest-design-0.png)_Achievement badge in [_Jailbreak_](https://www.roblox.com/games/606849621/)_ - **Dailies**: Also known as daily quests, these are short tasks with modest rewards that are available for and must be completed within 24-hours. Some daily quest systems provide the same quests every day, while others rotate quests to provide players with new objectives to avoid repetition. Dailies have simple objectives with low requirements for completion to encourage players to complete the quests in a single play session.![A pop-up UI in the World // Zero experience that displays various daily quests for players along with the awards they can earn.](../../assets/game-design/introduction-to-quest-design/quest-design-1.png)_Daily quests in [_World // Zero_](https://www.roblox.com/games/2727067538/)_ For these easy quests, small rewards are appropriate, due to the rate at which they're completed. Because they are available daily, Dailies can provide a reliable source of soft currency and incentivize players to log in every day. ## Use cases Though they use similar mechanics to task and reward players, quests, achievements, and dailies play complimenting roles in an experience. They actively: - Provide goals - Surface features - Teach through tutorials - Add session variety - Create resource drip - Increase engagement - Facilitate progression - Encourage new gameplay - Deliver narrative ![A chart tracking how quests, achievements, and dailies overlap in meeting various gameplay requirements, such as short-term goals, tutorials, progression, and narrative.](../../assets/game-design/introduction-to-quest-design/quest-design-2.png)_Overlap of roles between quests, achievements, and dailies_ ### Provide goals One way to keep players engaged is to provide them with a mixture of short, mid and long-term goals that allow them to feel frequent progress and give them something to aspire toward. **Short-term goals** can be completed in a single play session and provide small, frequent moments of accomplishment. Because players must complete them within a single day, dailies should exclusively consist of short-term goals. **Mid-term goals** require days to weeks of play to complete. Most quests, especially those with multiple objectives, fall somewhere between short-term and mid-term goals. **Long-term goals** take months of play to complete. Many achievements represent long-term goals because they require significant time, effort, or skill to earn. ### Surface features By creating quests that require players to explore different areas or complete certain tasks, you can draw attention to the full breadth of the experience's features and content. This can be especially helpful for experiences with large worlds or complex systems, as players might not realize all the experience has to offer. In this example from [_Squishmallows_](https://www.roblox.com/games/7941853407/), one of the experience's many locations, the campsite, is being surfaced. This quest might be the first time that a player learns that there is a campsite in the world, and the reward incentivizes them to explore the map to find it. ![A pop-up UI in the Squishmallows experience that informs players what they need to do in a quest to obtain a reward.](../../assets/game-design/introduction-to-quest-design/quest-design-8.png)_Quests in _Squishmallows__ ### Teach through tutorials Quests can also teach players how to use those features by acting as tutorials. Quest-based tutorials have a number of advantages: - Quests promote active learning, where players learn by playing, not just reading. Active learning results in better retention of the instructions, so players are more likely to remember what to do when they are on their own. - Complex tasks can be broken into simple, discrete steps, each one a quest objective. - Each feature can have its own quest or quest chain tutorial, allowing tutorials to be spread out over time. - Quests track player progress through the tutorial, which provides valuable analytics data about where players might be struggling. - Players feel good about learning to play, because they are rewarded for it. ![A pop-up UI in the RoCitizens experience that informs players what they need to do in a tutorial quest to obtain a reward.](../../assets/game-design/introduction-to-quest-design/quest-design-9.png)_Tutorial quest in [_RoCitizens_](https://www.roblox.com/games/137877687/)_ ### Add session variety The longer a player plays an experience, the more familiar they become with its systems and content. When players are incentivized to try new things, they are less likely to fall into gameplay routines that become boring over time. Quests can add variety to a player's session by encouraging them to engage with content or systems that they might not have otherwise, or to revisit ones that they have not engaged with recently. This can be especially helpful for experiences with repetitive gameplay, or those that have less frequent updates, but any experience can benefit by keeping its players engaged and ensuring that all of its areas remain active. ![Quest UI in The Wild West experience that displays tiles in various colors to inform players what they need to do for each tile's quest to obtain a reward.](../../assets/game-design/introduction-to-quest-design/quest-design-3.png)_Quests in [_The Wild West_](https://www.roblox.com/games/2317712696/)_ ### Create resource drip A **resource drip** is a source that provides small amounts of virtual resources like currencies, XP, or crafting ingredients that add up over time, allowing players to plan their play sessions and resource spending around them. Earning even small amounts regularly encourages players to spend because they know that there is always more to be found. When sources are scarce or too effort-intensive, it can discourage participation in the overall economy and lead to negative impressions about the fairness of the experience's design. An experience's economy is tied to its core loop. When a player engages with the core mechanics of the experience, they naturally earn and spend resources. Players who do not have resources to spend cannot make the purchases that enable or enhance their activity in the rest of the experience, such as upgraded tools and abilities. Facilitating investment in an experience's economy and a healthy interaction with the core loop can also lead to improved retention. For these reasons, it can be beneficial to drip-feed resources to players over time, and one way to do that is through quest rewards. Dailies are an excellent delivery mechanism for currencies because the rewards are reliable, small, and valuable enough to encourage players to return every day. ![A pop-up UI in the Pet Simulator X! experience that displays tiles in a top-down list to inform players what they need to do for each tile's quest to obtain a reward.](../../assets/game-design/introduction-to-quest-design/quest-design-7.png)_Daily quests in [_Pet Simulator X!_](https://www.roblox.com/games/6284583030/)_ ### Increase engagement Limited-time content is any type of consumable or collectible content like quests, pets, or cars that are only available to players temporarily. If players do not earn or complete the content before time runs out, they might never have another opportunity. For that reason, limited-time content spurs engagement as players increase their efforts in a rush to beat the clock. ![A close up view of the Discover page with various experience tiles that are themed for Halloween to inspire player engagement for the season.](../../assets/game-design/introduction-to-quest-design/quest-design-10.png)_Limited-time seasonal events_ Because it is so effective at driving player engagement and monetization, limited-time content is an essential element of an experience's live operations (LiveOps). Like other forms of limited-time content, quests that are only available for a brief time create a sense of urgency and excitement, encouraging players to log in regularly throughout the event's duration in order to earn the limited-time rewards. These temporary quests are common during seasonal events, such as Halloween or anniversary celebrations. To learn more about live operations and maintaining and updating a live experience, see [LiveOps essentials](/docs/en-us/production/game-design/liveops-essentials.md). ### Facilitate progression A **progression system** can help players achieve them by promoting engagement with the experience's [core loop](/docs/en-us/production/game-design/core-loops.md). As players engage with the core systems of the experience, they progress through the loop and make progress on their own goals. Quests can act as a progression system by providing a structured way to advance through the experience's content. With each quest completed, players earn rewards like weapons or skill points that help to prepare them for the next challenge. Season passes are progression systems that incorporate quests. In [_Dragon Adventures_](https://www.roblox.com/games/3475397644/), players complete quests called "missions" in order to earn points toward reward tiers. The quests are all activities that are tied to the core loop mechanics of hatching and raising dragons. As players progress through the tiers, they unlock more rewards, culminating in unlocking an exclusive dragon as the final season reward. Through this UI, players can track their progress through the tiers as the deadline for the end of the season approaches. ![Missions UI in the Dragon Adventures experience that displays two rows of tiles to inform players what they need to do in for different reward tiers to obtain rewards for either the base gameplay or with a season pass.](../../assets/game-design/introduction-to-quest-design/quest-design-11.png)_Season pass progression in _Dragon Adventures__ ### Encourage new gameplay Quests can encourage players to play in more interesting and challenging ways that they might not have tried before. In this example from Jailbreak, players are required to not only shoot down an enemy helicopter, but to do so while riding in a vehicle – a challenge that they might never attempt otherwise. As evidenced by the "impossible" rating, achieving this badge likely requires many attempts, during which the player is highly engaged. ![A close up view of an achievement badge for the Jailbreak experience that awards players who shoot down a helicopter while riding in a vehicle. The badge on the left-hand side displays a lightskinned blocky male avatar turned to his left to look at text that says Top Gun.](../../assets/game-design/introduction-to-quest-design/quest-design-4.png)_Achievement badge in _Jailbreak__ These alternate gameplay goals can be especially engaging for experienced players who might have tried everything the experience has to offer. Without new challenges, even the most dedicated players eventually grow bored and move on to other experiences. Achievements are one way to keep them engaged between content updates. ### Deliver narrative In addition to their objectives, quantities, and rewards, quests can optionally include **flavor text** that provides bits of narrative, and backstory about the experience's world. While not strictly necessary, especially in experiences without narrative elements, story-based quests can enrich the player's experience and make them feel more immersed in the 3D world. In [_Spellbound_](https://www.roblox.com/games/6468323505/), the player accepts a quest from an NPC named Moritor, who needs help retrieving a potion to cure his baldness. The text tells the player not just what to do, but why, and it does so in a way that contributes to the experience's humorous tone and experience. Even the quest's reward, a hat, is thematically tied to the story. ![Quest UI in the form of a red scroll that displays both narrative from the blocky male character to the right of the scroll, as well as an objective prompt and rewards players will earn if they complete the quest.](../../assets/game-design/introduction-to-quest-design/quest-design-5.png)_Quest narrative in _Spellbound__ ## Resources For a more in-depth look at quests, watch these workshop videos. They include tips for designing, writing, and producing quests, as well as developing a spreadsheet to make quest development more efficient. _Designing quests, achievements, and dailies_ _Writing quests, achievements, and dailies_ --- title: "LiveOps essentials" url: /docs/en-us/production/game-design/liveops-essentials last_updated: 2026-06-29T19:34:04Z description: "LiveOps Essentials is an overview of what live ops are and how to think about designing them effectively." --- # LiveOps essentials **Live operations** (LiveOps) is the post-launch support of an experience to maintain player engagement. LiveOps consist of: - A content cadence - Major updates - Quality of life improvements - Bug fixes Without LiveOps updates, even dedicated players lose interest. After completing all challenges and consuming all content, they grow bored. A blend of LiveOps updates following [best practices](#best-practices) keeps the experience fresh and engaging. ## Content cadence **Content cadence**, the regular release of new content in a live experience, varies based on developers' capabilities, ranging from weekly to monthly releases. These frequent introductions of fresh content aim to keep players engaged between major updates. _Content cadence in _Jailbreak__ Content Cadence releases exclusively build on existing systems, which: - Sustain these systems and encourage ongoing player interaction. - Allow programming and design resources to concentrate on creating new systems for the upcoming major update. - Reduce production and debugging time for each update, enabling a maintainable release cadence. When choosing content for cadence releases, look at art assets that support current systems. Creating pets, vehicles, weapons, maps, and quests tends to be straightforward and timely for experiences with these systems. Additional cadence content types include: - Limited-time events: Limited-time events are content that's available for a restricted period that players can earn or purchase. This temporary availability incentivizes players to engage with the content and imparts prestige to those who acquire it. - Seasonal content: Season content is a limited-time event themed around seasons and holidays. Given the numerous seasonal events occurring yearly, they serve as popular sources for cadenced content. - User-generated content (UGC): UGC content is content that the experience's player community creates themselves, often through contests and events. Each experience has a unique content cadence, based on its specific systems, player preferences, and developer capabilities. Striking a balance between player desires and what developers can reliably deliver is crucial. For a more in-depth discussion of the design and production of content releases, see [Content updates](/docs/en-us/production/game-design/content-updates.md). ## Major updates Unlike content cadences, which involve artists creating content to support existing systems, major updates engage artists, designers, and programmers to provide new or expanded systems that alters the gameplay experience. _Major update in _Jailbreak__ Major updates aim to introduce new, permanent systems that evolve gameplay and both retain existing players while attracting new ones. Given their scope, major updates demand significant investment and may necessitate many months of development between releases. Types of major updates include: - Social: Systems like guilds, groups, trading, and parties promote player interaction and collaboration. By participating in these systems, players provide content for each other, introduce variety into gameplay sessions, and form friends, all of which encourage repeat visits. - Competitive: Systems such as player versus player (PVP), leaderboards, and tournaments give players ways to measure themselves against other players. - Collections and achievements: Systems introducing new short, mid, and long-term completionist goals, like collectible pet or season pass systems. - Live Events: Large, time-limited events aimed at attracting new players and re-engaging lapsed ones through a shakeup, such as map overhauls or new player roles. ## Quality of life improvements While content cadence and major updates introduce new content and systems into the experience, quality of life (QoL) updates involve polishing the existing experience, making it more usable and enjoyable for players. Although typically smaller than other update types, QoL updates can have an outsized impact on the gameplay experience, and contribute significantly to player goodwill. _Quality of life update in _Doors__ Some examples include: - Improved user interface (UI) layouts - Optimized user experience (UX) flows and interactions - Aesthetic refreshes - Accessibility features - Performance improvements Quality of life updates may be released as needed, or as developers find time between other releases. To identify high-value opportunities for quality of life updates, gather player feedback about common frustrations and time sinks. Developers may also have their own wishlists of improvements to consider. ## Bug fixes Bug fix updates address implementation issues and ensure that the experience is functioning as intended. Bugs can range from minor issues that do not impact gameplay, like typos in non-critical text, to major problems that completely shut down the experience. _Bug fixes in _Doors__ When prioritizing bugs to work on, consider their severity (how drastically they impact the gameplay experience), how much time and effort they require to fix, and the number of players impacted. ## Best practices Consider doing the following when deciding the type, content, and frequency of your LiveOps updates: - Listening to players - Improving gameplay - Prioritizing mental health #### Listening to players Although the developers are the ultimate vision-holders, players are a good source of insight for growing and improving an experience. When determining the next update, consider: - What do players want added to the game? - How can their experience be improved? Game groups and social media like Discord allow developers to connect with players and gather their feedback. Even more important than players' specific suggestions is the reasoning behind them. Often, the gap that they are trying to fill can be addressed in other creative ways that achieve both the players' and developers' goals. #### Improving gameplay LiveOps updates are also the developers' opportunity to augment and improve their experience. They can identify high-value updates by asking: - What new systems and content would support the [core loop](/docs/en-us/production/game-design/core-loops.md)? - What issues and opportunities can be gleaned from data through [analytics](/docs/en-us/production/analytics/analytics-dashboard.md)? The core loop is an experience's minute-to-minute gameplay. Any features added during LiveOps should be connected to the core loop, so all of the experience's systems remain integrated and supportive of one another. Analytics provide valuable insights into player behavior and may indicate areas of improvement that can be targeted by an update. The creator dashboard provides analytics data and suggestions based on an experience's performance. Additionally, developers should consider their own LiveOps preferences and priorities: - What is their vision for the game? - What new features and content inspire them? - What are their capabilities? #### Prioritize mental health Supporting a live experience may at times feel like a grind. It is important for developers to take the time to work on features and content that inspire them, so they remain fulfilled by their work and excited about the experience's future. It is also important to be realistic about a team's capabilities: their strengths and weaknesses, their schedules, and the frequency with which they can release updates while maintaining a healthy work-life balance. --- title: "LiveOps planning" url: /docs/en-us/production/game-design/liveops-planning last_updated: 2026-06-29T19:34:04Z description: "LiveOps Essentials is an overview of what live ops are and how to think about designing them effectively." --- # LiveOps planning [LiveOps](/docs/en-us/production/game-design/liveops-essentials.md) maintain a dynamic and captivating experience through releasing two interweaving content types: - **Events:** Temporary activities like contests and promotions that complement the [core loop](/docs/en-us/production/game-design/core-loops.md). Events offer rewards like exclusive items, status, or currency to maintain player interest between larger content releases. - **Content updates:** Significant permanent experience updates that expand or deepen the core loop. Larger content releases provide robust new content for players to enjoy. To learn more about content updates, see [Content updates](/docs/en-us/production/game-design/content-updates.md). To initiate and execute a continuous LiveOps plan, consider how you will: - **Plan:** Define your objectives and methods for achieving them. - **Communicate:** Determine how to inform players about upcoming events. - **Monitor and analyze:** Evaluate player involvement and the event's success in meeting objectives and assess the event's effectiveness post-completion. ## Plan When designing LiveOps for your experience, consider your **key performance indicators** (KPIs). Determine which metric you want to impact, like daily active users. It's crucial to acknowledge that events typically influence several KPIs simultaneously. When planning for KPI objectives, consider the following: - **Player actions:** Clarify the intended player actions during the event and the KPIs these actions will likely influence. - **Player impact:** Consider how increased player interaction with certain events might alter the usual patterns of earning or buying in-experience currencies and items. An event like a fishing tournament exposes players to a key currency-earning part of the experience and likely yields significant engagement, but the type of reward players receive has to be intentionally designed to not negatively impact the experience's economy. To learn more about KPIs, see [Analytics essentials](/docs/en-us/production/game-design/analytics-essentials.md). _Fishing tournament in _Overlook Bay 2__ ## Communicate When designing LiveOps for your experience, consider how to inform players about your event: - **External communication:** Consider how to alert players to your experience's event outside of the experience itself. Think about what social media platforms or community tools you can leverage to increase awareness. - **Internal communication:** Decide on the method of notifying players in your experience about the event. Compare different options, from utilizing pop-up messages when they log in, to integrating an alert into the UI or displaying it prominently in the experience lobby. - **Communication timing:** Think about when you'll communicate the event to players. - Communicating the event in advance encourages players to schedule their return at the event time and can maximize participation. - Waiting until just before the event begins can show spontaneity and can be effective in certain situations, but runs the risk of being overlooked if your players are busy with other options. _Event Communication in _World//Zero__ After deciding how to communicate your event to players, be intentional with designing what happens in the experience after the event starts. Is the event limited to a certain space within the experience, or is the event experience-wide? > Take the time to design an impactful and memorable start to an event to inspire players and ensure sure they're excited about the event, will want to engage with it, and keep coming back after the event is over. ## Monitor and analyze When designing LiveOps for your experience, monitor your experience effectively by accessing comprehensive data, such as the sources of and spending of your experience's currency and other relevant resources. Integrate tracking into your experience's design to understand how players interact with your features. When tracking data, make sure it's: - **Timely:** Having quick access to data is essential for fine-tuning and ensuring your event is successful. Seeing hourly, same-day data to confirm that an event is not granting too many prizes and that players are able to accomplish what you're asking them to accomplish is crucial. - **Comparable:** Since events take place during a limited period of time, it's important to actively compare data collected during the event with data from non-event periods to determine whether its impact is greater or less than your intended goals. Comparing your event's data to the week before the event and the week after helps you understand whether the event had a lasting impact on typical in-experience activity or KPIs. KPIs that can give you insight into an event's success are: - An increase in short and longer term retention during and after the event. - A boost in item and currency sales during and after the event. If the event had a lasting impact, determine whether it was positive or negative, and if your findings impact how you'll design future events to achieve your desired outcome. For additional information and best practices on how to design optimal events, see [LiveOps essentials](/docs/en-us/production/game-design/liveops-essentials.md). --- title: "Onboarding techniques" url: /docs/en-us/production/game-design/onboarding-techniques last_updated: 2026-06-29T19:34:04Z description: "Onboarding" --- # Onboarding techniques Onboarding techniques are strategies used to help direct players through the onboarding funnel as quickly and seamlessly as possible. Three popular onboarding techniques include: - Visual elements - Contextual tutorials - Timed hints Each of these techniques can be optimized using **Configs** and **Experiments**. For example, you can use **Experiments** to A/B test whether a 5-second or 10-second delay for a timed hint leads to better funnel completion. Once you've identified the winning timing, leverage **Configs** to update that value globally in real-time, allowing you to optimize your onboarding flow instantly without restarting your servers. ## Visual elements **Visual elements** are assets or effects that appear in an experience to help players understand how to play, where to go, what to pay attention to, or what actions to take. Because of these characteristics, visual elements thrive when used in conjunction with an experience's onboarding funnel. Visual elements can be static or animated, appear within the environment, or overlay the UI. These elements are usually temporary and persist only as long as they communicate their intent to the player. When utilized correctly, visual elements integrated with an onboarding funnel helps: - Improve clarity - Increase accessibility - Enhance immersion - Facilitate feedback Examples of visual elements in onboarding include: - A bouncing arrow pointing at a button in the UI - Particle effects over an item of interest in the world - A glowing trail in the world that directs players to a destination - An in-world sign displaying an experience's core loop. _Directional arrows and icons in _Creatures of Sonaria.__ ### Improve clarity Players who don't understand what to do in the first few minutes are likely to quit, but so are players who are bored by lengthy and prescriptive tutorials. Utilizing visual elements in onboarding funnels enables you to teach players what they need to know, while also allowing them to get to the fun quickly. When information must be communicated efficiently, visual elements are used to improve clarity in three ways: - Prominence - Highlights - Hints #### Prominence Prominent visuals can be harder to overlook, ignore, or misinterpret than text alone. Some experiences present pop-up messages with text as players make progress. Depending on factors like location, color and contrast, and how focused the player is on what they're doing in the experience, those text prompts can be easily overlooked. If players don't notice them, they are of no benefit. Placing visual elements in the world, where players encounter them as they are playing, is one solution to this problem. In _Color or Die_, a sign explaining the experience's core survival mechanic is placed directly in players' line of sight as they collect their first paint bucket to begin playing. It's impossible to miss, but doesn't prevent the player from starting the experience. _Prominent visual element in _Color or Die.__ This technique is an efficient way of explaining the core mechanic of an experience quickly and clearly. Compare the visual element to what this same information could look like as text: > When the monster approaches, stand against a wall that's the same color as your avatar. The monster will be confused and unable to see you. If you stand against a wall of a different color, it will notice you and you will be in danger. Players generally don't like to read instructions and skip through text prompts to get to gameplay. Presenting instructions visually with as few clicks or taps as possible prepares those impatient players better than any amount of text that they would refuse to read. #### Highlights Highlighting something that players should look at is more effective than describing it in text. The more text a player is asked to read, the more likely they are to misinterpret or forget the information. Well-designed visuals can be harder to misunderstand and easier to retain due to requiring less interpretation. Consider the following sentence: > Open your inventory by clicking on the **backpack** button. This requires players to interpret a **backpack** button and then search for it in the UI. Instead, consider the impact of having an arrow point at the button telling them exactly where they need to click. _Highlights in _Hello Kitty Cafe.__ In this example from _Hello Kitty Cafe_, the desired button is spotlighted while the rest of the screen is dimmed. A pink outline highlights the button and an arrow points at it. There is no mistaking what the player is expected to click next. #### Hints Visual hints can keep players on track. New players don't always know what's important to pay attention to and are excited to explore. Persistent visual elements that stay on-screen until the player has completed a step of the tutorial helps keep players from being distracted by other gameplay mechanics, the environment, or UI. Even if they choose to explore, the hints remain to guide them when they are ready to return. Visual elements are not limited to telling players where to go or what to click. They can also imply functionality, such as alerting players that items in the world can be interacted with. In Jailbreak, players are not told explicitly what to do in order to escape the prison, but visual hints in the world grab their attention and imply interactivity. Without these hints, new players might struggle to locate escape routes. Visual hints provide just enough information to allow players to solve the problem themselves, and feel clever for figuring it out. _Footprints hint at a possible escape route in _Jailbreak.__ When considering implementing hints into your onboarding funnel, be intentional in determining who will see them and when they appear. For more on utilizing hints, see [Timed hints](#timed-hints). ### Increase accessibility Visual elements are more broadly accessible than text. Visuals like arrows and particle effects communicate without words and don't require translation to be understood by a global audience. This helps players who rely on localization into other languages, as well as UI designers who don't have to worry about a text string getting longer after translation. For experiences that appeal to younger players, teaching without words can help those that are still learning to read or who don't have the vocabulary to understand descriptions of more complex systems. Using visual elements also ensures accessibility on various platforms, such as mobile devices, where text can be too small to read comfortably. Visual elements are also easier to interpret, especially for players who are visually impaired. For more information about designing accessible visual elements, see this video on [color contrast](https://www.youtube.com/watch?v=syQcaeRqqc4). _Directional arrows in _Winds of Fortune.__ ### Enhance immersion Visual elements can help players stay immersed in gameplay. Directional trails, for example, provide direction while allowing players the freedom to explore and make other choices. They strongly suggest an action, but don't require it. While most players will follow the directions, the availability of choice makes the tutorial feel less forced than if they had no other options, and allows players to stay immersed in the experience and explore at their own pace. _Directional trail in _Hello Kitty Cafe.__ ### Facilitate feedback Visual elements can be used as feedback outside of tutorials and onboarding funnels. Feedback occurs when a player takes an action and the experience responds in a way that communicates the results of the action. Visual elements can be used to communicate feedback and provide players with actionable information to help them make their next choice. As an example, when players are in combat in _Winds of Fortune_: - VFX flashes indicate that the player's sword swings have hit their target - Red numbers fly off an enemy when they are hit, indicating damage points - A red screen effect throbs, alerting the player that their health is getting low - Health meters update for the player and enemy - When an enemy is defeated, coins fly out to let the player know that they have received rewards - If defeating the enemy is a quest objective, the counter updates when the enemy dies _Visual Feedback in _Winds of Fortune.__ All of this feedback tells players when they have successfully hit the enemy, when they have taken damage themselves, what rewards they receive from victory, and whether they've made progress on their quest. The combination of these visual elements ensures most players understand what's happening without a needing more explicit combat tutorial. Anytime a player takes an action in an experience, it's imperative to provide clear feedback. Otherwise, players may not know that the action they took did anything, and wonder if they did something wrong, or wonder if the experience is buggy. For more information about feedback and other User Experience (UX) best practices, see [UI and UX](/docs/en-us/production/game-design/ui-ux-design.md) ## Contextual tutorials Also known as **just in time** tutorials, **contextual tutorials** are tutorials triggered by normal play, such as entering a new map zone or picking up a new weapon. By waiting for players to engage naturally, contextual tutorials personalize instruction, which maximizes their relevance and utility within the broader onboarding funnel. Contextual tutorials facilitate: - **Increased learning retention:** Players learn best by understanding the context of a situation and by interacting with tutorials that are triggered by their first interaction with an experience feature. When players practice actions while learning them, they retain knowledge better. - **Faster onboarding:** It's important to lead players to the enjoyable parts of an experience quickly to keep them engaged.Contextual tutorials allow designers to avoid lengthy onboarding systems and delay unneeded tutorials, enabling players to begin having fun more quickly. - **Reduced cognitive load:** In the first few minutes of an experience, new players often must learn many things like interactions, mechanics, and goals. The more they have to learn, the harder it becomes to retain it all, increasing the likelihood of feeling overwhelmed and quitting. Delaying non-essential information to future sessions reduces what they must learn and remember in the first session, allowing them to focus on the essentials. Traditional tutorial flows guide players in a fixed sequence through an experience's features, ensuring that they learn essential skills and knowledge. While this method can be effective for high level concepts, it can lead to boredom or frustration due to restricting player choice. Contextual tutorials enhance the experience by holding off on less crucial instructions and quickly handing control to the player, by only providing specific instructions to the relevant players. ### Use cases In [_Squishmallows_](https://www.roblox.com/games/7941853407/), contextual tutorials are used to effectively teach players how and when to use the combination station. When a player has two identical Squishmallows in their inventory, a contextual tutorial triggers, and shows players how to combine three _Squishmallows_ at the combination station in order to create a larger one. _Contextual combination station tutorial in _Squishmallows_._ _Contextual combination station tutorial in _Squishmallows_._ If the developers included this tutorial in the onboarding experience, it would make it too long and irrelevant, and players may forget the information before they can use it. Presenting the tutorial when players have the required _Squishmallows_ encourages immediate action at the combination station, and helps players remember the process for future use. Contextual tutorials also teach non-core concepts, letting players learn about these features by themselves. In _Squishmallows_, these tutorials explain the marketplace where players sell Squishmallows to each other for in-game currency. Since the marketplace mainly serves mid and late-game players trading rare or high-level Squishmallows, the developers introduce new players to the marketplace through contextual tutorials when they find it on their own. _Contextual market tutorial in _Squishmallows_._ _Contextual market tutorial in _Squishmallows_._ ## Timed Hints **Timed hints** are tutorial elements that appear to struggling players after a predetermined period of time to help them progress through the onboarding funnel as quickly and seamlessly as possible. Players who need additional help can often be identified by timing how long it takes them to follow instructions or complete tasks. In addition to helping players through the onboarding tutorial, showing timely hints to players can also: - **Increase retention:** Timed hints can increase the number of new players retained by an experience by ensuring that struggling players get the extra help they need to succeed and have fun. New players needing additional help at first can become dedicated players in the future if they receive appropriate and well timed support. - **Improve player agency:** Timed hints allow designers to create less structured tutorial sequences and add additional layers of assistance specifically for those who need it. This avoids annoying experienced players while giving newer players the opportunity to figure things out for themselves. Players who figure things out on their own tend to feel more successful and empowered, which helps them complete tutorials quickly and get to the fun faster. - **Surface overlooked features:** Timed hints can also be used to draw attention to features in an experience that weren't core enough to be included in onboarding, but are still worth surfacing. For example, if a player hasn't engaged with an experience's trade feature in their first two sessions, highlighting the trade button in the UI would encourage them to click on it and experience the feature for the first time. When deciding how and when to use timed hints, observing playtesters can provide valuable information about where such hints are needed and how long to wait before displaying them. Try to find a balance between displaying them so quickly that players feel that they haven't gotten the chance to figure it out on their own, and waiting so long that they get frustrated. ### Use cases In [Plant](https://create.roblox.com/docs/resources/plant-reference-project), players follow a simple loop of planting seeds, selling produce, and investing currency into larger pots and more valuable plants. A great candidate for timed hint utilization is in the UI when planting seeds. In order to plant a seed, players have to select the **PLANT CABBAGE SEED** button. While most players will have little trouble figuring out what to do, there's a chance that some players might get stuck. In this hypothetical situation, playtesting results determine that most players find and press the button within 10 seconds. Adding a highlight to the button at 11 seconds would give those remaining players the hint that they may need to continue, without annoying the majority of players who don't need it. _Timed hint opportunity in _Plant_._ _Timed hint application in _Plant_._ Another potential place for a timed hint is during the player's first selling opportunity. After harvesting lettuce from their first few plants, players can take their wagon to one of three marketplace stands in order to sell their yield and gain the currency needed for upgrades. Players may be confused about which marketplace to visit to complete the objective. To alleviate this, an arrow could be placed over the correct stall after players have had some time to explore. _Timed hint opportunity in _Plant_._ _Timed hint application in _Plant_._ In both hypothetical examples, the timed hints only appear after the majority of players have completed the objective, and only appear the first time the task is presented. Timed hints should be sufficient to help struggling players after only being shown once, and only be shown to those who need it. --- title: "Onboarding" url: /docs/en-us/production/game-design/onboarding last_updated: 2026-06-29T19:34:04Z description: "Learn how to design and optimize the First-Time User Experience (FTUE) using data-driven iteration." --- # Onboarding **Onboarding**, also known as the First-Time User Experience (FTUE), comprises the first few minutes of gameplay that new players experience. The FTUE introduces the experience, interface, and mechanics, and may include tutorials that teach players how to play. The success of a FTUE is determined by how well it achieves two metrics: - Day 1 retention - Onboarding goals ## Day 1 retention **Day 1 Retention** is gauged by the amount of players who make it through the FTUE, known as the **Player Funnel**. The funnel is widest at the top and narrowest at the bottom as fewer players complete each step. All experiences lose some amount of players along this path. ![A diagram of an upside down pyramid that's broken into four sections that decrease in size from top-to-bottom to signify the tunnel of fewer and fewer players completing each step.](../../assets/game-design/onboarding/onboarding_1.png) The onboarding experience aims to limit drop-off between steps and retain as many new players as possible. To identify and "plug" leaks in your funnel, you can use [Experiments](/docs/en-us/production/experiments.md). By running A/B tests on specific tutorial steps, you can measure the causal impact of different designs, such as a shorter dialogue sequence versus a guided arrow, to see which version leads to higher completion rates. Experiences with high D1 retention metrics often owe that success to effectively accomplishing their onboarding goals through constant iteration and data-driven testing. ## Onboarding goals Many of the most successful onboarding experiences follow these high-level practices: - Teach the essentials - Get to the fun quickly - Leave players wanting more ### Teach the essentials For successful onboarding, players need to grasp controls for navigation and interaction. Displaying complex, unfamiliar controls on-screen or incorporating them in a tutorial can be beneficial. ![A close up view of UI in the Super Striker League experience that displays which buttons to press on your mouse or keyboard to complete various actions.](../../assets/game-design/onboarding/onboarding_2.png)_[_Super Striker League_](https://www.roblox.com/games/3360853050/) controls._ Besides controls, onboarding should also impart knowledge of the experience's **Core Loop**, the repetitive actions for progress that define the experience. It is critical that players understand both _what_ they are expected to do and _why_ they should do it. ### Get to the fun quickly New players typically decide their interest in an experience within minutes. To retain them, you must quickly demonstrate value through: - Game progression - Social motivators - Starter items and currency #### Progression **Progression** is felt when a player gets better, gains access to new systems, and achieves goals. A great way to facilitate this is through _Player XP-Based leveling systems_. ![A chart of the experience a player needs to level up in an experience. The curve increase exponentially from left-to-right.](../../assets/game-design/onboarding/onboarding_3.png)_Player XP Level Up curve._ Keeping thresholds low for a player's early levels allows them to level up quickly and feel the fun of progression immediately. You can use **Configs** to tune these XP thresholds in real-time. This allows you to smooth out the progression curve or adjust rewards based on live player behavior without needing to restart servers or push a full update. #### Social motivators Social players often prefer onboarding experiences that provide opportunities for collaboration and competition. If your experience relies heavily on social interaction, consider using **Experiments** to test different matchmaking parameters during the FTUE to see which social groupings lead to better long-term engagement. #### Starter items and currency Providing free items like equipment or soft currency allows players to engage with your systems early. However, finding the right balance is vital. The goal is to give players the opportunity to enjoy the utility or self-expression afforded by those items to encourage future playtime. By using **Experiments**, you can gift different starting amounts of currency to different groups of new players to see which leads to higher retention. Once you find the ideal balance, **Configs** allow you to update that starting value globally and instantly for all future players. For more on core loops, see [Core loops](/docs/en-us/production/game-design/core-loops.md). ### Leave players wanting more By the end of onboarding, players should be aware of the full range of content available. Designing these two characteristics effectively keeps players coming back: - Goals - Moments of joy #### Goals Providing short, mid, and long-term goals help players conceptualize future play sessions and envision their future success. These goals can take a number of different forms: skill trees, season passes, quests, collections, and more. Surfacing these goals in highly visible places, either in the game-world or the UI, provides a frequent reminder to players of what they're working towards. ![A pop-up UI in the Jailbreak experience that displays various items a player can earn if they play through a season. The season pass exclusive items are marked with a bright color to attract attention.](../../assets/game-design/onboarding/onboarding_4.png)_Season Pass in _Jailbreak_._ #### Moments of joy Joy can be elicited through rewards, delightful animations, and celebratory visual effects when a player achieves a milestone. Ending the onboarding experience with an intentionally designed moment of joy leaves players feeling accomplished and excited to return for their next session. --- title: "Prototyping" url: /docs/en-us/production/game-design/prototyping last_updated: 2026-06-29T19:34:04Z description: "Prototyping" --- # Prototyping **Prototyping** is an experimental design process where teams iterate and explore preliminary ideas for experiences before finalizing and working on them as features. The two common prototyping techniques when it comes to Roblox development are [paper](#paper-prototyping) and [studio](#studio-prototyping) prototyping. The primary benefits of prototyping are: - **Time efficiency:** The value of prototyping is the speed in which you can iterate and explore solutions to potential problems. Prototyping ensures that you detect design flaws, oversights, and unexpected technical requirements in the early stages of pre-production, instead of months into development. The time and energy required to prototype in the early stages of creating an experience is less than the time and energy required to fix costly oversights in the later stages. - **Finding the fun:** Prototyping ensures that the experience being designed is actually fun to play. Ideas for experiences can be abstract, but prototyping forces you to confirm that your vision can be brought to life in the way you imagine. ## Paper prototyping **Paper prototyping** is the act of designing an experience using physical objects to simulate gameplay and player interaction. Examples include: - Paper - Board game pieces - Dice - Legos - Office supplies ![A stack of cardboard cards, a number 2 pencil, and a handful of semi-translucent cyan blue dice sit on top of a dark brown table to signify common household objects you can use to prototype your gameplay.](../../assets/game-design/prototyping/prototyping-1.png)_Examples of paper prototyping tools._ The benefits of paper prototyping are: - **Iteration speed:** It takes minutes to begin paper prototyping, opposed to days or weeks to create a virtual environment. As a result iteration speed is greatly increased, allowing you to rapidly explore potential solutions and ideas by simply drawing, erasing, or printing more paper. - **Broader system context:** The abstraction of paper prototyping enables you to interact with more systems in your experience without getting weighed down by practical implementation. This freedom to explore system interaction ideas allows you to think of solutions from a design perspective, that you can then test once the prototyping stage is over. Even if it proves to be out of scope or difficult to implement, the initial idea gleamed from the broader system context provided by paper prototyping enables you to discover new creative solutions you might not otherwise have considered. - **UI/UX Design:** The ease of drawing on paper makes paper prototyping one of the best mediums for exploring optimal UI and UX designs for your experience.![A lightskinned hand holds a small paper cutout of a piece of pepperoni over a piece of paper with a UI prototype of a pizza building system. The piece of paper is on a dark brown table.](../../assets/game-design/prototyping/prototyping-2.png)_Using paper prototyping to explore UI/UX design._ Despite its benefits, prototyping on paper has drawbacks due to the difference in medium. The detriments of paper prototyping are: - **Not reusable:** None of the prototyping done with paper prototyping can be used literally in the end result. Everything must be recreated virtually. - **Simulation limitations:** Simulating unique mechanics and interactions are limited or impossible to create on paper or using physical assets. - **False positives:** Some activities might prove more fun and engaging using paper and physical assets than doing them on a phone, computer, or console. ## Studio prototyping _Studio prototyping_ is the act of using Roblox Studio to design and iterate the early stages of an experience. The benefits of starting directly in Studio for prototyping includes: - **Rapid Playtesting:** Creating directly in Studio enables you to have a quick playable version of the system in your experience that you can easily share with others for feedback. - **Reusable work:** Creating in directly in Studio allows you to build off your work. Unlike paper prototyping, you don't have to start over when the prototyping period concludes, and can continue working from where you left off. - **Early technical issues detection:** The abstraction of paper prototyping can result in unrealistic ideas that are impossible to technically implement. Prototyping in Studio forces you to determine if your idea is something you can practically create with the resources available to you.![An example greybox obby course with various gray, purple, and red parts above an open space without a baseplate.](../../assets/game-design/prototyping/prototyping-3.png)_An early obby prototype made in Roblox Studio._ Despite its benefits, the limitations of Studio prototyping include: - **Smaller testing scope:** Due to the time required to script, debug, and replace assets involved in a prototype, the scope to test potential gameplay interactions and systems is limited. - **Increased iteration time** Due to the increased cost of creating different virtual environments, more time is required to iterate multiple systems in an experience.![An example greybox obby course with parts of various textures, yellow Roblox coins, and a particle emitter emitting light green particles.](../../assets/game-design/prototyping/prototyping-4.png)_A more complex prototype made in Roblox Studio._ ## Best practices The difference between working on a feature and prototyping is scope. Prototyping should be fast and quick, hitting certain aspects of the feature being tested but not the entire feature itself. When prototyping, have several playtest sessions with your team to confirm your findings. Share your prototype with friends, family, and social media to garner feedback and fresh perspectives. Iterate on your experience until you're happy with it. When prototyping, take extra care to explore the following parts of your experience: - **Core loop:** The core loop is the central gameplay through which an entire experience is built. To learn more about core loop design, see [Core loops](/docs/en-us/production/game-design/core-loops.md). - **UI/UX:** Ensure that your menu interaction input controls are intuitive and well designed. To learn more about UI/UX design, see [UI and UX](/docs/en-us/production/game-design/ui-ux-design.md). - **Game rules:** Get into the details of how your experience will function. As an example, if you know that your experience will feature a respawn mechanic, explore how long the respawn timer could be, where the player will physically respawn in experience, and if there are any factors that could change the rate at which a player can rejoin the fun. - **Edge cases:** Explore how a player might push the limits of what your experience can do. Design solutions and stop gaps to either prevent or encourage this behavior. --- title: "UI and UX design" url: /docs/en-us/production/game-design/ui-ux-design last_updated: 2026-06-29T19:34:04Z description: "UI and UX design in Roblox" --- # UI and UX design The _user interface_ (UI) combines artistic visual design with communication-centric information design to produce menus, shop layouts, heads-up displays (HUDs) and other interfaces that players interact with. The _user experience_ (UX) relates to how players interact with the experience, how they make their choices known, and how they move from one choice to the next. While UI and UX often work collaboratively, UX carries a more significant focus on interactivity and the player's experience rather than the visual and informational design of the UI. > **Warning:** Optimize your interface by using [Experiments](/docs/en-us/production/experiments.md) to test how different button placements or HUD layouts impact player engagement. You can also use [Configs](/docs/en-us/production/configs.md) to toggle platform-specific UI elements or seasonal themes instantly across all active servers to keep your experience feeling fresh. ## UI The UI shares vital information like hit points, quest objectives, and prices that allow players to make decisions and navigate the experience. Because it is often the primary way in which games communicate with the player, UI is critical to players' experience. Poorly designed UI can leave players confused and frustrated and lead to poor retention, while expertly designed UI can contribute to engagement and monetization. Effective designed UI employs the following principles: - Prioritization - Attention - Visual Language - Conventions - Consistency ### Prioritization Sharing the most significant information first is known as the _Hierarchy of Information_. This is particularly crucial in games where players frequently make instantaneous decisions based on a restricted set of information. When prioritizing the UI, identify what is essential or most relevant to the player's experience and ensure it's both accessible and easy to locate. Consider these questions: - What is the player doing at this moment? - What is the most important thing for them to be aware of, or have access to? - What choices do they have to make? - What information do they need in order to make those choices? - How frequently will they engage in those choices? Important information and features change depending on factors like the role the player assumes, their location in the 3D world, and the items they hold. The UI can be designed to present only what matters contextually – swapping out the buttons and information depending on what's useful in each context. This ensures that players always have what they need, without the clutter of everything that they don't. #### Examples Irrelevant elements can cause confusion and distraction. [_Spellbound RPG_](https://www.roblox.com/games/6468323505/) has a clean, minimalist UI that is particularly crucial for mobile interfaces, as small screens can easily get overwhelmed with excessive buttons, screens, and text. ![A close up view of an unselected circular Items button with a brown book icon. The environment in the background is a brown dirt with a patch of snow.](../../assets/game-design/ui-ux-design/ui-ux-design-3.png)_Items prior to selection in [Spellbound RPG](https://www.roblox.com/games/6468323505/)_ ![A close up view of a selected circular Items button with a brown book icon, along with three additional buttons for skills, equipment, and the players deck. The additional buttons only display when the button is selected.](../../assets/game-design/ui-ux-design/ui-ux-design-2.png)_Items after selection in [Spellbound RPG](https://www.roblox.com/games/6468323505/)_ In [_Super Striker League_](https://www.roblox.com/games/3360853050/), player interactions vary based on whether they control the ball. Without the ball, players can Sprint and Tackle to regain control. Once in possession, the buttons switch to Deke and Pass, enabling players to dodge opponents and pass the ball. By alternating these buttons, the UI stays contextually useful, avoiding unnecessary distractions. _Contextual buttons in [Super Striker League](https://www.roblox.com/games/3360853050/)_ _Contextual buttons in [Super Striker League](https://www.roblox.com/games/3360853050/)_ In [_The Survival Game,_](https://www.roblox.com/games/11156779721/) players need to be aware of their health and hunger status, and have quick access to their map, inventory, and tools. This information allows them to make decisions and prioritize actions for survival. If this information or tools are hard to find or missing, survival becomes more challenging and needlessly frustrating. _HUD features in [The Survival Game](https://www.roblox.com/games/11156779721/)_ ### Attention Despite a minimalist, prioritized, and contextualized UI, players might still need assistance locating what they need. UI designers can use several visual tools to capture and guide players' attention, such as: - **Color:** Bright colors grab attention more than dull ones. Use vibrant hues for crucial text, buttons, and other elements, and muted shades for less significant ones. This contrast enhances visibility and reduces distraction. - **Size:** Larger elements are more conspicuous and perceived as important compared to smaller ones. - **Space:** Elements encircled by ample negative space or padding are eye-catching. - **Proximity:** Grouped elements imply a connection. By placing related features and items near each other, players can understand their association and locate them more easily. - **Movement:** Animated elements draw attention. Even slight motion can make an element stand out amidst static counterparts. Particle effects, wiggling buttons, and animated arrows are often used in tutorials for guidance. Moderation is key with these tools. Excessive use of bright, moving elements might overwhelm and confuse players rather than aid them. #### Examples In [_Jailbreak_](https://www.roblox.com/games/606849621/)'s season pass UI, a stripe of golden yellow lies beneath the season pass-exclusive icons, drawing attention to the premium rewards and making them visually distinct from the free ones. _Season pass UI in [Jailbreak](https://www.roblox.com/games/606849621/)_ In [_Dragon Adventures,_](https://www.roblox.com/games/3475397644/) visual priority is given to the highest-value coin bundle by increasing its size and separating it with more padding, to distinguish it from smaller bundles. _Coin bundles in [Dragon Adventures](https://www.roblox.com/games/3475397644/)_ In [_Tower Defense Simulator_](https://www.roblox.com/games/3260590327/), both size and proximity are used to visually group and distinguish Daily Tower Skins from Daily Crates. _The shop in [Tower Defense Simulator](https://www.roblox.com/games/3260590327/)_ ### Visual language In addition to directing players' attention to the most important elements on the screen, UI designers develop a visual language to help players understand what they're seeing. A visual language is a set of deliberate and consistent design choices that provide information at a glance about an element's purpose, status, associations and relevance. The language is built from tools like shape, color, and style. Some of the uses for visual language include: #### Icons Icons can employ a visual identity that conveys meaning and associations. For instance, icons related to specific stats may share a color palette and similar shapes, enabling players to recognize and understand their function. _Player Stats in [_Winds of Fortune_](https://www.roblox.com/games/7714627632/)_ #### Buttons As a prevalent form of player input, buttons must be easy to identify. Housing buttons in a container, such as a color surrounding the text or icon, distinguishes them from the background, making them appear interactive. Adding highlights or shadows can enhance their tactile appeal by suggesting 3D depth. _Buttons in [_BotClash Simulator_](https://www.roblox.com/games/9300407930/)_ #### Text Headers and titles containing significant high-level information should be larger and bolder than body text. Colors that are highly legible and contrast well against the background color they'll be displayed over should be considered. Stylistic choices like color and bolding can highlight important information like in-game items, stats, or abilities. Always prioritize legibility when choosing a font. _Clothing Shop in [Winds of Fortune](https://www.roblox.com/games/7714627632/)_ Once the visual language has been designed, it can be documented in a _Style Guide_, a set of rules and examples that help everyone on the team understand how to apply the language consistently across the experience. ### Conventions Conventions are commonly-applied design choices that are seen across many different experiences. Some examples include: - An X symbol on a button that closes the dialog when pressed - The color gray applied to buttons that are currently unusable - A lock icon overlaying a button or feature to indicate that the player has not yet earned or unlocked it Given their widespread use, players are typically familiar with such conventions. Leveraging this familiarity can make an interface more intuitive and lessen the need for game-specific instructions. Playing games within your target genre can help identify conventions players may recognize. A UI designer may opt for a unique approach to better fit their experience, but knowing these conventions informs their decision-making process. _X Close Buttons in [Winds of Fortune](https://www.roblox.com/games/7714627632/), [Dragon Adventures](https://www.roblox.com/games/3475397644/), [BotClash Simulator](https://www.roblox.com/games/9300407930/), and [DOORS](https://www.roblox.com/games/6516141723/)_ _Green "Health" stat consistency in [Arcane Odyssey](https://www.roblox.com/games/3272915504/)_ ### Consistency Whatever decisions a UI designer makes, they should consistently apply them throughout the experience. Consistency helps players learn to navigate the experience more efficiently and prevents confusion and frustration. Here are some examples of UI consistency in a game: - The "Health" stat appears in green in all text, related icons, and the health bar. - Anytime NPC dialogue mentions an in-game item, it's highlighted in bold. - Close buttons are always square, red, and contain a white X, appearing only in the top right corner of a dialog. - Whenever a player can't afford an item in the shop, the price displays in red. These choices are deliberate, aiming to facilitate player comprehension and navigation. By applying them consistently, they enable players to associate related elements, identify important items, develop muscle memory, make swift informed decisions, and spend more time immersed in the experience. ## UX UX design relates to how players interact with the experience, how they make their choices known, and how they move from one choice to the next. Although UX often works hand-in-hand with user interface (UI), it is more focused on interactivity and player experience than the visual and informational design of UI. ### Understanding players A primary goal of the UX designer is to create interactions and flows that are intuitive, unobtrusive and convenient for players to use. That process starts with an understanding of the players themselves, the intended audience for an experience. When identifying players of an experience, consider the following: - **Demographics:** Demographics help to inform design choices by revealing generalized information about a player group. For example, younger players are generally more likely to play on mobile or tablet devices than personal computers, so games intended for that audience often prioritize designing and polishing the mobile user experience. - **Experience level:** Developers may choose to design for players with little gaming experience, a significant amount, or somewhere in between. Interactions that are familiar to experienced players, such as tapping a number key to equip an item in a toolbar, or pressing c to crouch, may not be obvious to inexperienced players, and require additional messaging or training. - **Genre familiarity:** Similar to general experience level, genre familiarity reflects players' experience level with a specific type of experience. Games that fall into popular genres on Roblox, like roleplay, survival horror, and first-person shooter, are likely to have many potential players who have played a similar genre before. Games in other genres may have fewer experienced players, but that experience gap can be overcome through tutorials and usability. - **Gameplay style:** Players often favor specific types of play, ranging from competition and achievement-collecting to exploration and cooperation. These preferences are not only reflected in the experience mechanics players prefer but also influence UX decisions. Designers consider which interactions to prioritize, which to polish, and which to present to the players. They also take into account the mindset players will have when they engage with these interactions and the emotions they aim to evoke from them. After considering these factors and selecting a target player group (or groups), get to know them by meeting them, playing with them, and learning about their goals and preferences to better inform design choices. ### Design interactions _Interactions_ are features that allow players to experience and communicate their choices in an experience. UX designers want those interactions to be intuitive, and require as little explanation as possible. To do this they utilize: - Conventions - Metaphors - Feedback #### Conventions _Conventions_ are UX designs and principles that players may already be familiar with from other experiences. _"E" proximity prompt in [_Mermaid Life_](https://www.roblox.com/games/4377254026/)_ Examples of conventions include: - The "E" proximity prompt that Roblox developers use to signal to players that an item is interactable if they stand close enough for it to appear. - The "C" key allowing the player's avatar to crouch so that they can duck under obstructions or fit into tight spaces. - Number keys equipping items from a toolbar into the avatar's hands so that they can be used. - Walking into circles on the ground in order to queue up for a match. Conventions become established when a particular implementation or design choice grows in popularity due to its ease of use. "C" to crouch, for example, is easy to remember due to it being the first letter in the word crouch, and is conveniently located next to WASD, the keys that players use to navigate. It is an easy interaction to perform quickly and under pressure, which is ideal since attempting to hide or flee from an approaching threat is a common use case for crouching. #### Metaphors _Metaphors_ relate an abstract concept to a more familiar one, creating a shortcut to comprehension. In this example from _Spellbound Wizard RPG_, the act of casting magic spells is performed by drawing cards from a deck. The deck-based UI/UX grounds the abstract act of casting spells, which has no real-life expression, into something that players can relate to more easily and quickly understand. _Spellcasting as cards in [_Spellbound RPG_](https://www.roblox.com/games/6468323505/)_ #### Feedback Communication from the experience to the player is known as _feedback_. Feedback helps players understand that an action that they have taken. It can contribute to learning how the experience functions and add an extra layer of polish that makes a feature more satisfying to use. Some examples of feedback include: - An enemy playing its "hit" reaction animation to indicate that it has taken damage from the player's weapon - A button that changes color to indicate hover, depressed, and released (selected) state - A cash register "cha-ching!" sound effect playing when the player completes a purchase - A progress bar filling up as the player completes quest objectives When feedback is missing or insufficient, players might not know what they've accomplished, understand whether the effects were good or bad, or even know whether the feature they're using is functioning correctly or experiencing bugs. ### Design flows Multiple interactions are often required for a player to complete a goal. UX designers are concerned with how players navigate from one action or UI screen or choice to the next and make sure that these paths are logical and convenient. For example, to equip an item on an avatar in [Berry Avenue RP](https://www.roblox.com/games/8481844229/), a player must: - Find and tap the Avatar button - Select an item category (face, head, neck, etc) - Scroll to find an item of interest - Tap on the item to equip it on their avatar - Find and tap the Done button to exit avatar customization _Avatar customization flow in [Berry Avenue RP](https://www.roblox.com/games/8481844229/)_ These steps taken together represent the avatar customization flow. This flow is simple and straightforward, with as few steps as possible needed to equip an item. Because players do not have to perform many actions or think too much about what they need to do, this flow is low in _friction_, a measurement of the effort required to reach a goal. Designing efficient, low-friction flows requires careful consideration of use cases. Those use cases can be expressed as player goals such as: - Wanting to be able to quickly modify an avatar - Finding items easily while modifying - Previewing items prior to selection - Easy item removal after selection These goals then guide the UI and UX design of the feature: - Players access the avatar customization feature through a prominent button on the HUD, and can immediately begin equipping items to their avatars - Tabs sort the items into convenient categories, and the search field allows players to search for items directly - Items are previewed on the avatar instantaneously - Players can remove the item by tapping on it again to deselect, or tapping on it in the equipped items list Designers can then begin to lay out the UI and plan the steps that players will take to complete the goal of modifying their avatars. This process can be aided by the use of a _flow chart_, which visually depicts the players' actions as they navigate the UI. These diagrams can help to identify overlooked steps, pain points where the steps are unclear or inconvenient, and excessive friction. _Example of a flow chart._ --- title: "Production" url: /docs/en-us/production last_updated: 2026-06-29T19:34:04Z description: "An overview of topics to consider once you finish creating your experience." --- # Production Once you have created your experience, you may want to find an audience for it and keep them engaged over time. You need to consider: - **Publishing** to make it public for everyone to enjoy. - **Promotion** to bring in new users. - **Monetization** strategies for selling any products within it. - **Localization** to help users around the world understand it. - **Analytics** to monitor how it's performing. - **Game design** to create and maintain an engaging player experience. ## Publishing When an experience is ready to play, you can [publish](/docs/en-us/production/publishing/publish-games-and-places.md) your experience on any devices you choose. An experience can be published **privately** (default) in order to test and save to the cloud, or **publicly** so that it can be discovered and played by other users. ## Monetization Users can spend Robux in experiences for items such as new vehicles or power-ups, and creators can also earn Robux from [engagement-based payouts](/docs/en-us/production/monetization/engagement-based-payouts.md), [private servers](/docs/en-us/production/monetization/private-servers.md), [paid access in Robux](/docs/en-us/production/monetization/paid-access-robux.md), and [paid access in local currency](/docs/en-us/production/monetization/paid-access-local-currency.md). There are two main methods of [monetizing](/docs/en-us/production/monetization.md) within an experience: - [Developer Products](/docs/en-us/production/monetization/developer-products.md) - users can purchase multiple times. Great for in-experience currency purchases. - [Passes](/docs/en-us/production/monetization/passes.md) - users can only purchase once. Great for VIP passes or permanent power-ups. Both are sold for Robux at a price you choose. You can then trade Robux for real world currency using the **Developer Exchange**. ## Localization Roblox players come from around the world and speak a variety of languages. It's important to [localize](/docs/en-us/production/localization.md) the experience so everyone can understand and enjoy it. Roblox automatically captures text from an experience and stores it in a table for translation in the experience's **Localization** page, where all localization settings can be configured. ## Promotion You can promote your experience within the Roblox Ecosystem in multiple ways: - Use the [Ads Manager](/docs/en-us/production/promotion/ads-manager.md#sponsored-experiences) to create ad campaigns using image, video, or portal ads within existing experiences. - Roblox [experience events](/docs/en-us/production/promotion/experience-events.md) are time-based events that users can join and discover through an events detail page. - You can use integrated features such as [update announcements](/docs/en-us/production/promotion/experience-events.md#announce-updates) and [experience notifications](/docs/en-us/production/promotion/experience-notifications.md) to promote your content to a wider audience and keep in touch with your existing user base. ## Analytics Roblox offers a variety of [analytics](/docs/en-us/production/analytics.md) features to help you chart your experience's growth, track user behavior and retention, and find opportunities for optimization. --- title: "Add source content" url: /docs/en-us/production/localization/add-source-content last_updated: 2026-06-29T19:34:04Z description: "Learn how to manually add, edit, and remove entries to your experience's cloud localization table." --- # Add source content You must add source text to translate to the experience's localization table before you or any collaborators add translations. You can use [Automatic Text Capture](#automatic-text-capture) to collect text strings automatically as your experience is played by users or [manually add text sources](#manually-add-source-content). See [Localizing images and sounds](/docs/en-us/production/localization/localize-with-scripting.md#localize-images-and-sounds) for instructions on localizing non-text content. ## Automatic Text Capture The **Automatic Text Capture (ATC)** tool collects text strings that users encounter in your experience through GUI objects, such as `Class.TextLabel|TextLabels` or `Class.BillboardGui` and is usually the fastest way to collect translatable text from your experience. In many cases, the Automatic Text Capture tool is already enabled for [automatic translations](/docs/en-us/production/localization/automatic-translations.md#enable-automatic-text-capture). ## Manually add source content You can also add content manually. This is recommended if your experience contains text that doesn't appear during typical gameplay, like a complex conversation tree, which may take longer to collect via ATC. You can add entries to the table [in the localization page](#add-sources-with-localization-settings) or [with a `.csv` file upload](#add-sources-with-file-upload). It is recommended to use the `.csv` upload if you want to specify an entry's Key, Context, or Example fields. > **Warning:** Entries in the localization table are case-sensitive. > > For example, when using "hello" as a source value on the localization table, Roblox does not apply translations for "Hello" or "HELLO". You can manually add image and sound asset IDs to the localization table with a `.csv` upload. For more information see [Localize images and sounds](/docs/en-us/production/localization/localize-with-scripting.md#localize-images-and-sounds). ### Add sources with localization settings You can add source content in the [localization page](/docs/en-us/production/localization.md#localization-settings). This is ideal for making quick additions to your localization table. To add source content through the localization page: 1. In your experience's localization page, click the **Translate** button on the top right. 2. Select the **Strings** tab. 3. Click **Add Entry**. 4. In the **Text to translate** field, input the source text and click **Save**. 5. If you are using key-based scripting to translate a string, input the key name in the **Key** field. 6. If the string has multiple meanings, such as a character's back and a back button, input the full path of the specific text object in the **Context** field. 7. Click **Save**. ### Add sources with file upload You can add source content to your localization table with a `.csv` upload. You can use this option when making large numbers of updates to your localization table. To add source content through the `.csv` upload: 1. In Studio, open **Localization Tools** from the **Window** ⟩ **Localization** 2. In the **Cloud Localization Table** section, select **Download** to save the `.csv`. 3. Open the `.csv` in your preferred spreadsheet editor. 4. Under the **Source** column, add source text and save. You can leave the other columns blank. | Key | Context | Example | Source | | --- | --- | --- | --- | | | | | Options | | | | | Start | 5. In Studio, in the **Cloud Localization Table** section, select **Update** and upload the new `.csv` file. 6. Click **Confirm** on the **Confirm Upload** prompt to save the new entry to the cloud localization table. --- title: "Add translations" url: /docs/en-us/production/localization/add-translations last_updated: 2026-06-29T19:34:04Z description: "Learn how to manually add, edit, and remove entries to your experience's cloud localization table." --- # Add translations You can add translations to source entries on your experience's localization table using the [translator portal](#in-translator-portal) or [with a `.csv` file upload](#with-file-upload). In some cases, strings may require real-time translation during gameplay, such as a timer, using units of measurement, or when displaying player names. You can add translation parameters to the localization table as placeholders for this type of content. See [Translate dynamic content](/docs/en-us/production/localization/translate-dynamic-content.md) for implementation details. ### In Translator Portal Your experience's [Translator Portal](/docs/en-us/production/localization.md#localization-settings) provides a direct way to provide translations for the collected strings. Users added as translators for your experience can access the Translator Portal. #### Add string translations To translate text through the localization page: 1. In the localization settings page, click the **Translate** button on the top right. 2. In the **Target Languages** menu on the left, select the target language you want to add a translation to. 3. Select the **Strings** tab at the top of the page. 4. In the Strings column, select the string you intend to translate. 5. In the **Text to translate** field, input the desired translation and click **Save**. > **Info:** You cannot make edits to an existing source's **Key**, **Context** or **Example** fields through the localization settings page. To add or edit a source's **Key**, **Context,** or **Example** fields, you can remove and re-add the entry on the Localization page with these fields included, or make the edits directly with [file upload](#with-file-upload). #### Add locale-specific translations Some languages may have regional differences in terms or spelling, such as "soccer" and "football". These are considered locales within languages. Currently, Roblox supports adding translations for the following locales: | Locale Name | Locale ID | | --- | --- | | English (United States) | en-us | | English (United Kingdom) | en-gb | | French (France) | fr-fr | | French (Canada) | fr-ca | | Portuguese (Brazil) | pt-br | | Portuguese (Portugal) | pt-pt | | Spanish (Spain) | es-es | | Spanish (Mexico) | es-mx | For languages that have Roblox-supported locales, you'll see subheadings under the parent language for global and locale-specific translations. Add translations that are universal for that language (not specific to a locale) to the "Global" section, and add locale-specific translations to the corresponding sections. If a locale-specific translation is present, it overrides what is in the "Global" section for users in that locale. You can also add locale-specific translations for your experience source language if they are supported. > **Warning:** If the source language of your experience is English, French, Portuguese, or Spanish, locale-specific translation overrides for experiences names and descriptions are not currently available for these locales: - English (United States) - French (France) - Portuguese (Brazil) - Spanish (Spain) #### Add experience information translations You can modify experience information, such as the name, description, icon, and thumbnails, by updating the localization table: 1. In the localization settings page, click the **Translate** button on the top right. 2. In the **Target Languages** menu on the left, select the target language you want to add a translation to. 3. Select the **Information** tab at the top of the page. 1. For Name and Description fields, input the desired translation and click **Save**. Note that if you save an empty string here, it will be saved as a manual edit signaling that the text should not be translated. In this case, users in the target language will see the source string. 2. For Icon and Thumbnails, use the Upload or Remove buttons to modify your localized images. You can upload up to 10 thumbnails per experience. When complete, click **Save**. #### Add experience product translations You can modify experience product details, such as developer [products](/docs/en-us/production/monetization/developer-products.md), [passes](/docs/en-us/production/monetization/passes.md), and [badges](/docs/en-us/production/publishing/badges.md): 1. In the localization settings page, click the **Translate** button on the top right. 2. In the **Target Languages** menu on the left, select the target language you want to add a translation to. 3. Select the **Products** tab at the top of the page. 4. Input the desired translations for your experience product and click **Save**. ### With file upload Using Studio or Creator Hub, you can download, modify, and re-upload your localization table as a `.csv` spreadsheet. This is helpful when editing several translations at a time or when collaborating with translators outside of Roblox. Using the file upload process, you can only modify in-experience string entries and translations. Unlike editing translations through your [Translator Portal](#in-translator-portal), modifying the `.csv` allows you to edit the additional **Key**, **Context**, and **Example** fields. When modifying or adding translations with file upload, the following behavior applies to brand new or existing entries: | **Does the entry already exist in the Cloud Localization Table?** | **Changes made to CSV** | **Expected behavior on upload** | | --- | --- | --- | | No | Source string row added with one or more translations | Entry is added, any translations present are added as a manual translation entry. Any translations missing for supported languages are eligible for [automatic translation](/docs/en-us/production/localization/automatic-translations.md). | | No | Source string row added without translations | Entry is added and is eligible for [automatic translation](/docs/en-us/production/localization/automatic-translations.md). | | Yes | Translation added or modified. | If the translation is different than the one in the existing Cloud Localization Table, translation updates to the one in the `.csv` file.

If the original translation was automatically generated, the new translation is added and locked as a manual translation. You can regenerate automatic translation for this entry by [unlocking the entry in Translator Portal.](/docs/en-us/production/localization/automatic-translations.md#automatic-translation-updates) | | Yes | Translation entry for a source string removed. | Translation is deleted and locked as a manual translation. You can regenerate automatic translation for this entry by [unlocking the entry in Translator Portal](/docs/en-us/production/localization/automatic-translations.md#automatic-translation-updates). | | Yes | Source string removed | No change - entry will not be deleted.

To delete an entry through `.csv`, either:
— In Creator Hub, use **Delete Table** and then **Upload CSV**.
— In Studio, use **Advanced** ⟩ **Replace** in the Cloud Localization Table section. | > **Info:** To begin adding a new supported language, you need to manually add the country code as a new column in the spreadsheet. See [Language Codes](/docs/en-us/production/localization/language-codes.md) for a list of supported languages and country codes. > **Warning:** **Ensure your spreadsheet meets the following requirements to prevent uploading or processing issues:**- Max total entries: **10,000**- Max `.csv` file size: **2MB**- Max number of modified or added entries per `.csv` upload: **1,000** #### With Creator Hub To translate text using the `.csv` in Creator Hub: 1. In the Creator Hub, navigate to your experience settings page. 2. Navigate to **Localization** and switch to the **Table Management** tab. 3. Select **Download CSV** to save the `.csv` file locally. 4. Open the `.csv` in your preferred spreadsheet editor. 5. Add translations on the same row as the source text, under the column with the specific country code you are localizing to. Save when finished. 6. Select **Upload CSV** and upload the updated `.csv`. 7. Click **Confirm** on the prompt to save the new entries to the cloud localization table. #### With Studio To translate text using the `.csv` through Studio: 1. In Studio, open **Localization Tools** from the **Window** ⟩ **Localization** menu. 2. In the **Cloud Localization Table** section, select **Download** to save the `.csv`. 3. Open the `.csv` in your preferred spreadsheet editor. 4. Add translations on the same row as the source text, under the column with the specific country code you are localizing to. **Save** when finished. 5. In the **Cloud Localization Table** section, select **Update** and upload the updated `.csv`. 6. Click **Confirm** on the prompt to save the new entries to the cloud localization table.
--- title: "Automatically translate dynamic content" url: /docs/en-us/production/localization/auto-translate-dynamic-content last_updated: 2026-06-29T19:34:04Z description: "Explain how to generate translations in real time for dynamic content." --- # Automatically translate dynamic content > **Warning:** The following feature is currently in early development and is subject to change. For the latest updates and changes, see the [announcement](https://devforum.roblox.com/t/introducing-the-real-time-translation-api/3550206). The standard translation workflow detects strings in your experience based on how frequently they're viewed by players and adds them to your localization table for translation. It might miss uncommon strings and/or strings generated during gameplay, such as dynamically generated text or text created by players. You can use the translate text API to generate translations for these strings in real time, ensuring that your experience is fully localized. ## Translate text into a player's language To translate text into a player's language, pass their `Class.Player.LocaleId` as the target language code. Below is an example of how you can get the player's locale ID in a client script and then pass it to a `Class.Script` in `Class.ServerScriptService` to make the translation request. - The translation API is an [Open Cloud API](/docs/en-us/cloud.md), meaning you need a [path](/docs/en-us/cloud/reference/patterns.md) to make a request. In this case, you need the universe ID, which can be found in the overflow menu of the experience tile on the [Creator Hub](https://create.roblox.com/dashboard/creations). - You must also include the [Open Cloud client package](/docs/en-us/production/promotion/experience-notifications.md#include-the-package) in your experience; the server script requires it. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local httpRequestFunction = ReplicatedStorage:WaitForChild("TranslateTextFunction") -- Text to translate local textToTranslate = "This is the example text to translate" -- Get the player's locale local Players = game:GetService("Players") local player = Players.LocalPlayer -- get the locale ID for the local player's locale or set to any supported locale string local locale = player.LocaleId local translatedText = httpRequestFunction:InvokeServer(textToTranslate, locale) print("Translated text: ", translatedText) ``` ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local ServerScriptService = game:GetService("ServerScriptService") local oc = require(ServerScriptService.OpenCloud.V2) -- Find at https://create.roblox.com/dashboard/creations in the overflow menu of an experience tile local universeID = -- Create RemoteFunction local remoteFunction = Instance.new("RemoteFunction") remoteFunction.Name = "TranslateTextFunction" remoteFunction.Parent = ReplicatedStorage remoteFunction.OnServerInvoke = function(player, text, locale, uni) print(player.Name .. " requested translation for text: " .. text .. " to locale: " .. locale) -- Prepare the translation request local request : oc.TranslateTextRequest = { path = oc:UniversePath(universeID), text = text, -- target language codes supports a list of multiple locales to translate to. -- Here we are passing just one language: --The player locale retrieved in the local script target_language_codes = {locale} } local result = oc:TranslateText(request) if result.Error == nil then return result.Response.translations[locale] -- Assuming translations[locale] contains the translated text else return "Error: " .. result.Error.message end end ``` ## Testing The real-time translation API currently only supports RCC authentication. As a result, you must deploy your code to a test instance in order to test the API from Studio. Use [collaborative testing](/docs/en-us/studio/testing-modes.md#collaborative-testing) to deploy the script to a test instance and test your changes. ## Translation API reference ### API request parameters | Parameter Name | Type | Description | | --- | --- | --- | | path | string | The path of the universe. Required. | | text | string | The text to be translated. Required. | | source_language_code | string | The IETF BCP-47 language code representing the language of the input text. If not provided, the system will automatically detect the source language. | | target_language_codes | `Array` | A list of target language codes in IETF BCP-47 format for translation. | ### API response fields | Field Name | Type | Description | | --- | --- | --- | | source_language_code | string | The IETF BCP-47 language code representing the detected or user-specified language of the source text. | | translations | `Dictionary` | A map containing the requested translations. The key is the IETF BCP-47 language code, and the value is the translated text for that language. The map will contain all requested translations. If the source text was filtered, this map will be empty. | ## Limits Roblox uses the following formula to throttle requests for this API based on the number of players in your experience: `max requests per minute per experience = 600 + (1.5 * number_of_concurrent_users)` There is also a combined limit of 150 requests per minute, per experience server for all Open Cloud APIs. ## Supported languages The real-time translation API currently supports the following languages, which differ slightly from [supported languages for automatic translation](/docs/en-us/production/localization/automatic-translations.md#supported-languages). | Language | Language Code | | --- | --- | | Chinese (Simplified) | zh-cn | | Chinese (Traditional) | zh-tw | | English | en-us | | French | fr-fr | | German | de-de | | Indonesian | id-id | | Italian | it-it | | Japanese | ja-jp | | Korean | ko-kr | | Polish | pl-pl | | Portuguese | pt-br | | Russian | ru-ru | | Spanish | es-es | | Thai | th-th | | Turkish | tr-tr | | Vietnamese | vi-vn | --- title: "Automatic translation" url: /docs/en-us/production/localization/automatic-translations last_updated: 2026-06-29T19:34:04Z description: "Roblox's easy to use automatic translation features allow you to quickly begin supporting various language translations on any experience." --- # Automatic translation **Automatic translation** is a tool that automatically collects and translates strings in your experience. This tool is available on any experience and allows you to immediately begin displaying translated strings in your experience and broadening your potential audience. ## How automatic translation works Automatic translation works by first [collecting strings](#enable-automatic-text-capture) encountered within the experience, either through testing or playing. Once these strings are captured, Roblox automatically translates the strings on your experience's localization table. After collecting these strings, you can [enable translations](#enable-translations) for your experience to automatically display the captured strings for users who have their default language settings set to the translated language. Automatic translation only provides automated translations for blank entries on the localization table and doesn't override any existing translations, including any [manual translations](/docs/en-us/production/localization/manual-translations.md) made by a developer or translator. For example, if you manually clear a translation for an entry, the tool assumes that you don't want to translate that entry. ## Enable automatic text capture Automatic Text Capture (ATC) captures text strings from UI elements in your experience and adds them to your translation table. When enabled, the ATC tool adds text from UI elements encountered by users within your experience or during Studio testing. ATC evaluates over time which strings should be added to your translation table, so it may take up to a few days for new strings to appear in Translator Portal after users encounter them. If you require strings to be captured immediately, you can use the [Studio text capture process](#text-capture-in-studio). ATC only captures text objects that have `Class.GuiBase2d.AutoLocalize|AutoLocalize` enabled. If you do not want ATC to collect a certain text string, such as a name or unique text entry, disable this property. Note that for `Class.TextBox` objects, only `Class.TextBox.PlaceholderText|PlaceholderText` is captured; the assumption is that strings in `Class.TextBox.Text|Text` were provided by the player. To enable Automatic Text Capture: 1. In localization settings, navigate to **Settings**. 2. Enable **Capture text from experience UI while users play**.![The Localization Settings section highlighting the toggle for Capture text from Experience UI while users play.](../../assets/localization/Portal-Enable-ATC.png) > **Info:** ATC can not capture some experience objects. These objects may require special handling with [localization scripts](/docs/en-us/production/localization/localize-with-scripting.md). The current exemptions are: - The default Roblox leaderboards and chat - Items or tools owned by a player - Images with embedded text - Badge names and descriptions pulled from the platform - Pass names and descriptions pulled from the platform ### Text capture in Studio There may be situations in which you need to immediately capture strings to your translation table. In these cases, you can use the text capture tool in Studio to capture strings while playtesting. These strings will be added to the localization table within 1–2 minutes of encountering them. To enable text capture in Studio: 1. In Studio, open **Localization Tools** from the **Window** ⟩ **Localization** menu. 2. Enable **Automatic Text Capture**.![The in-Studio localization tool showing ATC toggle enabled.](../../assets/localization/ATC-Studio-Enabled.png) Studio text capture stays on if you start and stop playtesting within the same Studio session, but must be enabled again when you start a new Studio session. ### Automatic cleanup ATC also checks your experience for strings that no longer exist, such as usernames or strings related to a past event. By default, the tool will remove these stale entries when it identifies them. Only auto-scraped strings with automatic translations will be removed; strings that were manually added or that have manual translations will not be impacted. If you do not want ATC to clean up your localization table, navigate to your localization settings and disable the second ATC toggle: ![The Localization Settings section highlighting the toggle for enabling Roblox to remove stale entries.](../../assets/localization/Portal-Enable-ATC-Auto.png) ## Enable translations Once strings are captured to your localization table, follow these steps to enable translated content: 1. In your experience's localization page, navigate to **Settings**. 2. Enable **Use Translated Content**.![The Localization Settings section highlighting the toggle for enabling Roblox to use translated content in the experience.](../../assets/localization/Portal-Use-Translated-Content.png) 3. Navigate to the **Languages** tab. 4. Enable any supported languages for automatic translations. - **Experience Information** refers to the name and description of your experience. - **Experience Strings & Products** refers to the text that appears within your experience.![The Localization Languages section highlighting the per-language toggles for automatic translation.](../../assets/localization/Portal-Automatic-Translation.png) ### Automatic translation quotas Roblox has initial and monthly quotas for automatic translation. The initial quota determines how many string entries you can translate when you localize your experience for the first time. After you use up the initial quota, any subsequent translations come from your monthly quota, which resets every month. You can track your automatic translation quota usage on your experience's localization page: ![The Localization Languages section depicting the Automatic Translation Quotas at the top of the page, including the date of the monthly quota renewal.](../../assets/localization/Automatic-Translation-Quotas.png) Quotas are calculated on a **per-character** and **per-language basis**. For example, translating the source string "hello" into all 15 automatic translation-supported languages will count as 5×15 (75) characters towards your quota. ### Automatic translation updates As the automatic translation tool improves, more accurate translations may become available for existing strings. When these updates become available, Roblox refreshes any automatic translations. Automatic translation updates will appear in your translation history. ![A translation history log displays at the bottom of each translation entry.](../../assets/localization/Update-Log.png) If you have a specific translation that you want to remain unchanged, you can lock the entry to ensure it will not be impacted by any updates. ![A 'Lock translation from automatic updates' toggle is accessible below the translation entry field in each translation entry.](../../assets/localization/Lock-Translations.png) > **Warning:** By locking an entry, you are approving the translation and turning it into a manual entry. This means it will not be impacted by any automatic translation updates, including safety-related updates. If, in the future, it is discovered that the translation violates policy, your experience will still be subject to moderation. By default, manually added strings and strings with manual translations are locked. If you unlock an entry, it will be impacted by both automatic translation updates and ATC if it is enabled. If you want to generate a new automatic translation for a previously changed or cleared translation, unlock the entry. ## Supported languages Roblox supports automatic translation between the languages listed below. Currently, Roblox Translate will always assume that source strings are in the [experience source language](/docs/en-us/production/localization.md#setting-source-language). | Currently supported languages for automatic translation | | --- | | Arabic | | Chinese (Simplified) | | Chinese (Traditional) | | English | | French | | German | | Indonesian | | Italian | | Japanese | | Korean | | Polish | | Portuguese | | Russian | | Spanish | | Thai | | Turkish | | Vietnamese | --- title: "Localization" url: /docs/en-us/production/localization last_updated: 2026-06-29T19:34:05Z description: "An overview of localization tools you can use to make sure all users can understand and enjoy content within experiences." --- # Localization Roblox users come from a variety of cultures around the world and speak different languages. Using **localization** tools effectively makes sure all users can understand and enjoy your experience as intended. Any experience can immediately take advantage of Roblox's automatic localization tools to instantly expand your potential audience. As your experience grows, use Roblox's various localization API tools, manual translator portals, and analytics, to ensure that users from around the world can fully participate and socialize in your experience. ## User-facing setting Every user has an **Automatic Translations** setting in their [Account Settings](https://www.roblox.com/my/account#!/info) page that allows them to turn on automatic translation for experience content. If a player has this setting turned on, all experience names, experience descriptions, and in-experience text will be translated for them, even if you have not explicitly enabled translation for your experience. If you add any [manual translations](#manual-translations) to your experience, **they will always be shown to players**. Roblox only automatically generates translations if they are missing. To preview and manage translations, follow the steps below to enable localization settings for your experience. There may be text in your experience that should never be translated (for example, NPC names or street signs). To ensure translations are not generated for them, you can disable the `Class.GuiBase2d.AutoLocalize|AutoLocalize` property for those objects in Studio. ## Localization settings Your experience's localization settings is the starting place for enabling translations and accessing your localization tools. You'll need to set your [source](#set-source-language) and [supported](#set-supported-languages) languages before starting any localization to a new language. The localization page also includes tools to start translating your experiences, add translation collaborators and download translation analytics. You can access your experience's localization settings through the Creator Dashboard main page: 1. Navigate to the [Creator Dashboard](https://create.roblox.com). 2. Hover over your experience and click **Manage Experience**. 3. In the left navigation, navigate to **Audience** → **Localization**. You can also access your experience's localization settings in the on your [Creations](https://create.roblox.com/dashboard/creations) page: 1. Navigate to your [Creator Dashboard](https://create.roblox.com/dashboard/creations). 2. In the **Experiences** tab, find your experience and select the **⋯** button. 3. Select **Configure Localization**. A link to your experience's localization settings is also available in Studio. To find the link: 1. Open Studio's **File** ⟩ **Experience Settings** window. 2. In the **Localization** section, click the link indicated below to launch the localization settings in your default browser. ### Set source language Before you can start any localization, Roblox needs to know which language you are translating from and which languages you are translating to. The **source language** is the primary language that the experience has been written in. Roblox currently only supports one source language at a time. The default is set to your [Roblox account language](https://www.roblox.com/my/account#!/info). To set the **source** language: 1. In the localization settings, navigate to **Languages** and select the **Source Language** dropdown. 2. Select a language from the list. 3. Click **Confirm** to add the language. ### Set supported languages The **supported languages** are the languages you intend to provide translations for. You can have multiple supported languages for your experience. See [language codes](/docs/en-us/production/language-codes.md) for a list of all languages Roblox supports. To set the **supported** languages: 1. In the localization settings, navigate to **Languages** and select the **Source Language**. 2. In the **Supported Languages** section, click **Add Language** to add the languages you intend to localize to. 3. Click the input box and type in the language you would like to add, or select from the list of languages. 4. Click **Confirm** to add the language. ## Automatic translations Roblox's automatic translation tool is available on any experience and allows you to immediately begin localizing the strings in your experience and broadening your potential audience. In many cases, you can start making significant progress on your localization journey using automatic translations before adding [manual translations](#manual-translations) or working with [translators](#work-with-translators). For more information on enabling automatic translations in your experience, see [Automatic translations](/docs/en-us/production/localization/automatic-translations.md). ## Manual translations Apply various customizations to your translations to improve and expand your experience's localization. You, or any assigned translator, can manually add and modify translations to your experience's translation table. You can also access this table programmatically, allowing you to create contextual translations, switch out images or assets based on language, or even manually display translations from a different language. For more information on custom translation use-cases, see [Translate in-experience content](/docs/en-us/production/localization/manual-translations.md). ## Work with translators Add Roblox users as translators for your experience to help you review and polish automatic translations or provide and edit manual translations. You can download monthly analytics to track your translation coverage and the contribution of your translators. For more information on working with localization contributors, see [Work with translators](/docs/en-us/production/localization/work-with-translators.md). --- title: "Language codes" url: /docs/en-us/production/localization/language-codes last_updated: 2026-06-29T19:34:04Z description: "Language codes are languages you can specify by locale ID to support multi-lingual support." --- # Language codes You can specify languages by their language code or locale ID. Refer to the following table for a list of language codes and locale IDs that you can currently support in your localization tables on Roblox: | Language | Language code | Locale ID | | --- | --- | --- | | Albanian (Shqipe) | sq | sq-al | | Arabic (العربية) | ar | ar-001 | | Bengali (বাংলা) | bn | bn-bd | | Bokmal (Bokmål) | nb | nb-no | | Bosnian (босански) | bs | bs-ba | | Bulgarian (български) | bg | bg-bg | | Burmese (ဗမာစာ) | my | my-mm | | Chinese (Simplified) (中文(简体)) | zh-hans | zh-cn | | Chinese (Traditional) (中文(繁體)) | zh-hant | zh-tw | | Croatian (Hrvatski) | hr | hr-hr | | Czech (Čeština) | cs | cs-cz | | Danish (Dansk) | da | da-dk | | Dutch (Nederlands) | nl | nl-nl | | English (English - United States) | en | en-us | | English (English - United Kingdom) | en | en-gb | | Estonian (Eesti) | et | et-ee | | Filipino (Filipino) | fil | fil-ph | | Finnish (Suomi) | fi | fi-fi | | French (Français - France) | fr | fr-fr | | French (Français - Canada) | fr | fr-ca | | Georgian (ქართული) | ka | ka-ge | | German (Deutsch) | de | de-de | | Greek (ελληνικά) | el | el-gr | | Hindi (हिन्दी) | hi | hi-in | | Hungarian (Magyar) | hu | hu-hu | | Indonesian (Bahasa Indonesia) | id | id-id | | Italian (Italiano) | it | it-it | | Japanese (日本語) | ja | ja-jp | | Kazakh (қазақ тілі) | kk | kk-kz | | Khmer (ភាសាខ្មែរ) | km | km-kh | | Korean (한국어) | ko | ko-kr | | Latvian (Latviešu) | lv | lv-lv | | Lithuanian (Lietuvių) | lt | lt-lt | | Malay (Bahasa Melayu) | ms | ms-my | | Polish (Polski) | pl | pl-pl | | Portuguese (Português - Brazil) | pt | pt-br | | Portuguese (Português - Portugal) | pt | pt-pt | | Romanian (Română) | ro | ro-ro | | Russian (русский) | ru | ru-ru | | Serbian (српски) | sr | sr-rs | | Sinhala (සිංහල) | si | si-lk | | Slovak (Slovenčina) | sk | sk-sk | | Slovenian (Slovenski) | sl | sl-sl | | Spanish (Español - Spain) | es | es-es | | Spanish (Español - Mexico) | es | es-mx | | Swedish (Svenska) | sv | sv-se | | Thai (ภาษาไทย) | th | th-th | | Turkish (Türkçe) | tr | tr-tr | | Ukrainian (україньска) | uk | uk-ua | | Vietnamese (Tiểng Việt) | vi | vi-vn | --- title: "Localize with scripting" url: /docs/en-us/production/localization/localize-with-scripting last_updated: 2026-06-29T19:34:05Z description: "Explains how to use localization APIs for specialized translation tasks." --- # Localize with scripting You can use localization APIs for specialized translation tasks that are not automatically handled by [adding translations](/docs/en-us/production/localization/manual-translations.md) to the localization table. Roblox provides a `Class.LocalizationService` to handle all localization scripting needs. Use the `Class.LocalizationService` for the following tasks: - [Localizing images and sounds](#localize-images-and-sounds) - [Translating part of individual strings](#translate-individual-strings) - [Applying translations from multiple languages](#switch-languages) If you use any localization APIs when translating your experience, listen for any changes to the user's LocaleID to [react to users switching their language](#react-to-users-switching-languages) while in an experience. When reusing translation code, you should use a [TranslationHelper ModuleScript](#create-a-translationhelper-module) to handle errors and missing translations. ## Localize images and sounds Add localization beyond text in your experience by providing unique images and sounds based off of a user's locale. To localize assets, first add the **source** and **target** asset IDs to your experience's [localization table](/docs/en-us/production/localization/manual-translations.md#cloud-localization-table) then use the localization API to fetch the different assets. _English (Source) - rbxassetid://2957093606_ _Spanish (es) - rbxassetid://2957093671_ _Portuguese (pt) - rbxassetid://2957093727_ To start localizing images and sounds, add your [source and target](/docs/en-us/production/localization/manual-translations.md) asset IDs to your localization table. Asset ID entries on the localization table must include a **Key** as an identifier to be called by the API. The following is an example entry on a localization table using asset IDs: | Key | Source | es | pt | | --- | --- | --- | --- | | Key_JewelsImage | 2957093606 | 2957093671 | 2957093727 | The following code will replace the asset ID of an `Class.ImageLabel` with the Spanish asset ID provided in the localization table: ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local LocalizationService = game:GetService("LocalizationService") -- Local variables local localizedImageID local localizedImage = Instance.new("ImageLabel") -- Load Translator for "es". Wrap the function within a pcall() to protect against failures. local res, translator = pcall(function() return LocalizationService:GetTranslatorForLocaleAsync("es") end) if res then -- Get asset ID from localization table by referencing the Key localizedImageID = translator:FormatByKey("Key_JewelsImage") -- Set the image localizedImage.Image = "rbxassetid://" .. localizedImageID else print('GetTranslatorForPlayerAsync failed: ' .. translator) end ``` > **Warning:**`Class.LocalizationService:GetTranslatorForPlayerAsync()|GetTranslatorForPlayerAsync()` and `Class.LocalizationService:GetTranslatorForLocaleAsync()|GetTranslatorForLocaleAsync()` are asynchronous and may occasionally fail due to poor connectivity or other issues. It is recommended to create a [TranslationHelper ModuleScript](#create-a-translationhelper-module) to catch errors and provide fallback translation instructions. ## Translate individual strings In some circumstances, you may want to target individual strings for translation. `Class.Translator:Translate()` can fetch individual entries on the localization table based on the source string. In the next example, the following localization entry is used: | Source | es | es | pt | | --- | --- | --- | --- | | Screen | Pantalla | 295093671 | 2957093727 | The following script will print the Spanish translation of "Screen" to the **Output** window: ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local LocalizationService = game:GetService("LocalizationService") -- Load Translator for "es". Wrap the function within a pcall() to protect against failures. local res, translator = pcall(function() return LocalizationService:GetTranslatorForLocaleAsync("es") end) if res then -- Use Translate function, providing object context and string local sourceTranslation = translator:Translate(game, "Screen") print(sourceTranslation) -- Expected Output: "Pantalla" else print('GetTranslatorForPlayerAsync failed: ' .. translator) end ``` ### Use context overrides There are some cases where the same string may have multiple meanings. For example, the word "Screen" can indicate both a computer screen and a window screen, but the Spanish translations are completely different. The **Context** column of the localization table is for specifying translations through context overrides. Specify the in-game object on your localization table as in the following example: | Context | Source | es | | --- | --- | --- | | workspace.WindowScreen.SurfaceGui.TextLabel | Screen | Mosquitero | | | Screen | Pantalla | The following script uses a context override to prioritize a specific translation: ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local LocalizationService = game:GetService("LocalizationService") local Workspace = game:GetService("Workspace") -- Load Translator for "es". Wrap the function within a pcall() to protect against failures. local res, translator = pcall(function() return LocalizationService:GetTranslatorForLocaleAsync("es") end) if res then -- use Translate function, providing object context and string local sourceTranslation = translator:Translate(Workspace.WindowScreen.SurfaceGui.TextLabel, "Screen") print(sourceTranslation) -- Expected Output: Mosquitero else print('GetTranslatorForPlayerAsync failed: ' .. translator) end ``` #### Multiple contexts In the case of multiple contexts, the localization service compares object relationships in the Context field from **right to left**, using the closest match. For example, a localization table in your experience may have the following shared Source strings entries: | Context | Source | es | | --- | --- | --- | | workspace.WindowScreen.SurfaceGui.TextLabel | Screen | Mosquitero | | playerGui.ScreenGui.TextButton | Screen | Pantalla | If the string "Screen" is added to a `playerGui.ScreenGui.TextLabel` object in your experience, the localization service displays "Mosquitero" as the Spanish translation as the closest context match. ### Substitute parameters When using parameters to translate [dynamic content](/docs/en-us/production/localization/translate-dynamic-content.md), set the values to a [table](/docs/en-us/luau/tables.md) and pass the table as an argument through the API. In this example, the experience has a localization table with the following entries: | Key | Source | es | | --- | --- | --- | | Key_Prize_1 | {1:int} jewels | {1:int} joyas | | Key_Prize_2 | ${AmountCash:fixed} cash and {NumJewels:int} jewels | ${AmountCash:fixed} dinero y {NumJewels:int} joyas | Use the following code sample to translate these strings with parameter values: ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local LocalizationService = game:GetService("LocalizationService") -- Load Translator for "es". Wrap the function within a pcall() to protect against failures. local res, translator = pcall(function() return LocalizationService:GetTranslatorForLocaleAsync("es") end) if res then -- Set the parameter value in "Key_Prize_1" to 100 local keyTranslation1 = translator:FormatByKey("Key_Prize_1", {100}) print(keyTranslation1) -- Expected Output: 100 joyas -- Set multiple parameters to 500 and 100 by name local keyTranslation2 = translator:FormatByKey("Key_Prize_2", {AmountCash=500, NumJewels=100}) print(keyTranslation2) -- Expected Output: $500.00 dinero y 100 joyas else print('GetTranslatorForPlayerAsync failed: ' .. translator) end ``` ## Switch languages In some cases, you may want to display translations of other languages in your experience. You can set a new translator with a different country code using `Class.LocalizationService:GetTranslatorForLocaleAsync()`. The following code sample sets one translator with a manual country code and an additional translator based off of the user's global locale settings: ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local LocalizationService = game:GetService("LocalizationService") local Players = game:GetService("Players") -- Local variables local player = Players.LocalPlayer -- Load Translator for "pt". Wrap translator functions within a pcall() to protect against failures. local res1, translator = pcall(function() return LocalizationService:GetTranslatorForLocaleAsync("pt") end) -- Load second Translator with Player's locale, in this example "es" local res2, fallbackTranslator = pcall(function() return LocalizationService:GetTranslatorForPlayerAsync(player) end) -- Use Translate function with first Translator if res1 then local translate1 = translator:Translate(game, "jewels") print(translate1) -- Expected Output in pt: joyas else print('GetTranslatorForPlayerAsync failed: ' .. translator) end -- Use Translate function with second Translator if res2 then local translate2 = fallbackTranslator:Translate(game, "jewels") print(translate2) -- Expected Output in if user is set to 'es': jóias else print('GetTranslatorForPlayerAsync failed: ' .. fallbackTranslator) end ``` ## React to users switching languages Users can change their language settings at any time using their in-experience Settings menu. This user setting change automatically updates non-scripting localization assets, such as strings handled by [automatic translation](/docs/en-us/production/localization/automatic-translations.md), but may not update scripted localization changes that have already rendered, such as GUI images or sounds. _In-Experience Language Setting_ _Users can choose available languages set in the experience_ To ensure that your scripted localized assets update correctly, listen to the `Class.Instance.GetPropertyChangedSignal|GetPropertyChangedSignal` event for changes in the LocaleID property of the `Class.Translator` instance returned by `Class.LocalizationService.GetTranslatorForPlayerAsync`. When using `Class.LocalizationService.GetTranslatorForPlayerAsync`, wrap the function within a `Global.LuaGlobals.pcall()` in case of errors. The following code sample prints the Locale ID of the user and the Locale ID of the Translator instance for the user when the user switches languages: ```lua local LocalizationService = game:GetService("LocalizationService") local Players = game:GetService("Players") local player = Players.LocalPlayer -- If GetTranslatorForPlayerAsync succeeds, it will return a Translator for player's current locale local res, translator = pcall(function() return LocalizationService:GetTranslatorForPlayerAsync(player) end) -- Function that gets called when change in player's locale ID is detected local function OnLocaleIdChanged() print("Translator has changed to: " .. translator.LocaleId) -- You should re-translate any assets translated with Localization APIs to the player's new language here end -- Check if GetTranslatorForPlayerAsync succeeded if res then -- If succeeded, translate assets here using translator -- Listen for a change in player's locale ID translator:GetPropertyChangedSignal("LocaleId"):Connect(OnLocaleIdChanged) else print('GetTranslatorForPlayerAsync failed: ' .. translator) end ``` ## Create a TranslationHelper module When you load translators based on the player's default locale, you might reuse code. To reuse code, set up a helper `Class.ModuleScript` that safely loads translators based on the player's default locale and includes functions for providing specific translations and switching languages. The following code sample implements a TranslationHelper which you can copy into your own project as a `Class.ModuleScript` in `Class.ReplicatedStorage`: ```lua local TranslationHelper = {} local LocalizationService = game:GetService("LocalizationService") local Players = game:GetService("Players") -- Local variables local player = Players.LocalPlayer local sourceLanguageCode = "en" -- Get translators local playerTranslator, fallbackTranslator local foundPlayerTranslator = pcall(function() playerTranslator = LocalizationService:GetTranslatorForPlayerAsync(player) end) local foundFallbackTranslator = pcall(function() fallbackTranslator = LocalizationService:GetTranslatorForLocaleAsync(sourceLanguageCode) end) -- Create a method TranslationHelper.setLanguage to load a new translation for the TranslationHelper function TranslationHelper.setLanguage(newLanguageCode) if sourceLanguageCode ~= newLanguageCode then local success, newPlayerTranslator = pcall(function() return LocalizationService:GetTranslatorForLocaleAsync(newLanguageCode) end) --Only override current playerTranslator if the new one is valid (fallbackTranslator remains as experience's source language) if success and newPlayerTranslator then playerTranslator = newPlayerTranslator return true end end return false end -- Create a Translate function that uses a fallback translator if the first fails to load or return successfully. You can also set the referenced object to default to the generic object function TranslationHelper.translate(text, object) if not object then object = game end if foundPlayerTranslator then return playerTranslator:Translate(object, text) end if foundFallbackTranslator then return fallbackTranslator:Translate(object, text) end return false end -- Create a FormatByKey() function that uses a fallback translator if the first fails to load or return successfully function TranslationHelper.translateByKey(key, arguments) local translation = "" local foundTranslation = false -- First tries to translate for the player's language (if a translator was found) if foundPlayerTranslator then foundTranslation = pcall(function() translation = playerTranslator:FormatByKey(key, arguments) end) end if foundFallbackTranslator and not foundTranslation then foundTranslation = pcall(function() translation = fallbackTranslator:FormatByKey(key, arguments) end) end if foundTranslation then return translation else return false end end return TranslationHelper ``` > **Info:** If your experience's source language is not English, change the value of the `sourceLanguageCode` variable to the country code of the experience's source language. Once the module is in `Class.ReplicatedStorage`, require it from a `Class.LocalScript` to call the module's functions. The following code uses this module's helper function to translate an individual string: ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") -- Require translation module local TranslationHelper = require(ReplicatedStorage:WaitForChild("TranslationHelper")) -- Use the functions provided in TranslationHelper TranslationHelper.setLanguage("es") local sourceTranslation = TranslationHelper.translate("Screen") print(sourceTranslation) -- Expected Output in 'es': "Pantalla" ``` --- title: "Manual translations" url: /docs/en-us/production/localization/manual-translations last_updated: 2026-06-29T19:34:05Z description: "Learn how to manually add, edit, and remove entries to your experience's cloud localization table." --- # Manual translations When localizing your content, you might need to manually modify your localization table to improve translation quality, add context for when a translation should be used, or even remove unused translation entries. Whether you have translation entries automatically captured or manually added, all the translation entries are saved to the [cloud localization table](#cloud-localization-table). To modify the localization table, you can perform the following actions: - [Add source content](/docs/en-us/production/localization/add-source-content.md) to the table, either manually or automatically. - [Remove content](/docs/en-us/production/localization/remove-content.md) from the table, such as usernames or other strings that shouldn't be translated. - [Add translations](/docs/en-us/production/localization/add-translations.md) to entries in the table for any supported languages set in your experience. Before making any changes to your table, it's important to understand each field in the [cloud localization table](#cloud-localization-table) to prevent issues or conflicts. > **Info:** Users with Automatic Translations enabled in their Roblox account settings will still see all manually added translations in your experience. See [User-facing setting](/docs/en-us/production/localization.md#user-facing-setting) for more information. ## Cloud localization table The localization table is the reference for all translations within your experience. It's important to understand each field of the localization table to effectively add or modify entries. When populated, the table contains all user-facing content within your experience that requires translation along with any available translations for specific languages. You can [add source content](/docs/en-us/production/localization/add-source-content.md) and [add translations](/docs/en-us/production/localization/add-translations.md) to the Cloud Localization Table or [download and upload a `.csv` version of your table](/docs/en-us/production/localization/add-translations.md#with-file-upload). Only users who have edit permission for an experience can access an experience's localization table. The localization table has the following columns: | Column | Description | | --- | --- | | Source | A required field containing the in-experience source text strings gathered by automatic text capture or manual entry. This is the source text that you intend to translate to other languages. Source text is required before you can add a translation and multiple entries can share the same Source, as long as they have unique **Context** fields.

If you are translating dynamic content, such as partial strings, the Source field must follow one of the supported parameter formats described in [Translate dynamic content](/docs/en-us/production/localization/translate-dynamic-content.md). | | Context | An optional field containing a reference to the in-experience object containing the source text. You can use the Context field to specify which translation Roblox displays on an object. See [Using Context Overrides](/docs/en-us/production/localization/localize-with-scripting.md#use-context-overrides) for more details.

The Automatic Text Capture system does not auto-fill the Context field and instead populates the **Location** field with similar information. | | Example | An optional field containing any additional information about an entry you may want to include as notes for translators. You can use this field to show manual translators where and when your experience uses a specific translation. | | Key | An optional field for implementing `Class.Translator:FormatByKey()\|key-based lookup` when using localization APIs. You can leave this field empty if you are not using [localization APIs](/docs/en-us/production/localization/localize-with-scripting.md) for specific tasks, such as localizing images and sounds. | | Location | A field that is auto-filled by the [Automatic Text Capture](#automatic-text-capture) system. It contains a reference to the in-experience object containing the source text which can help you or a translator identify where the string is being encountered in your experience. This field is either blank or populated by the Automatic Text Capture System. To manually add your own object reference, use the **Context** field. | > **Warning:** Some fields in the localization table must be unique to avoid unexpected behavior. Keep in mind the following: - For all entries, the **Source** field must not be empty. - No two entries can share the same **Source and Context** values. - No two entries can share the same **Key**.
--- title: "Remove content" url: /docs/en-us/production/localization/remove-content last_updated: 2026-06-29T19:34:05Z description: "Learn how to manually add, edit, and remove entries to your experience's cloud localization table." --- # Remove content If you find source content that is wrong or no longer needed in your localization, you can remove this content by either clearing all entries collected using Automatic Text Capture (ATC) or manually removing source content. When you remove source content, you also remove any associated translations. ### Clear unmodified auto-captured entries You might want to remove some auto-captured entries if the ATC tool collects strings that shouldn't be translated, such as usernames, user-generated names, or chat strings. This option only clears auto-captured entries that you haven't manually edited. To clear unmodified auto-captured entries: 1. In your experience's localization page, click **Settings**. 2. Select which time period you would like to clear strings from in the dropdown list. This clears all entries that were auto-captured from the specified time period. 3. Click the **Clear** button. ### Manually remove source content You can also remove entries manually. This is recommended if you have specific entries you want to remove or if the entries you want to remove were manually modified in any way, such as content that is added manually or ATC entries that have been manually edited. To remove source content through the localization table on Creator Dashboard: 1. In your experience's localization page, click the **Translate** button on the top right. 2. Select **Game Strings**. 3. Click the entry you would like to remove in the **Strings** column. 4. Click the trash can icon to the right of the 'Text to translate' section. 5. Click the **Delete** button in the confirmation dialog. To remove source content through a `.csv` upload: 1. In Studio, open **Localization Tools** from the **Window** ⟩ **Localization** menu. 2. In the **Cloud Localization Table** section, select **Download** to save the `.csv`. 3. Open the `.csv` in your preferred spreadsheet editor. 4. Delete the entire row for any entries you would like to remove, and save. 5. In Studio, in the **Cloud Localization Table** section, select **Update** and upload the new `.csv` file. 6. Click **Confirm** on the **Confirm Upload** prompt to save the new entry to the cloud localization table. --- title: "Translate dynamic content" url: /docs/en-us/production/localization/translate-dynamic-content last_updated: 2026-06-29T19:34:05Z description: "Explains how to use Parameters in localization tables to translate specific parts of content." --- # Translate dynamic content You can use **parameters** in your [localization table](/docs/en-us/production/localization/manual-translations.md#cloud-localization-table) when only part of the displayed string requires translation, such as localizing a unit of measurement after a number value, referencing a username, or displaying time and date. _Displaying an amount of in-game items._ _Showing a player's Roblox username in a message._ _Displaying a high score using localized separators._ Parameters consist of a **parameter value** and an optional **format specifier** enclosed in braces. In the following example, an experience has the following entries in its localization table: | Source | es | | --- | --- | | Hello {Player_Name}! | Hola {Player_Name}! | | My name is {NPC_Name} | Me llamo {NPC_Name} | If a user has their locale set to **es**, the translation output would be as follows: | Original in-game text | Spanish translation | | --- | --- | | Hello new_storm! | Hola new_storm! | | My name is Diva Dragonslayer | Me llamo Diva Dragonslayer | In some cases, you may want to use format specifiers to control how the parameter value is formatted in the localized string. The available format specifiers are as follows: | Specifier | Type | Description | Example output | | --- | --- | --- | --- | | int | number | Integer with optional negative sign; no thousand separators. | 1234 | | fixed | number | Two decimals with decimal indicator, optional negative sign, and no thousand separators. | 1234.50
1234,50 | | num | number | Two decimals with decimal indicator, optional negative sign, and thousand separators. | 1,234.50
1234,50 | | HEX | number | Integer converted to hex; negative is converted to 64-bit two's complement. | 3FF | | hex | number | Same as HEX, but lowercase. | 3ff | | datetime | number | UTC timestamp as a number to universal user-readable format. | 2017-10-10 13:38:10 | | iso8601 | number | UTC timestamp as a number to ISO-8601 format UTC time. | 2017-10-12T22:02:38Z | | shorttime | number | UTC timestamp to local "hour:minute" format. | 1:45 PM
13:45 | | shortdatetime | number | UTC timestamp to general date+time pattern with short time. | 10/10/2017 1:45 PM | | shortdate | number | UTC timestamp to short date pattern. | 10/10/2017
2017-10-10 | | translate | string | Looks for a literal **Source** string match in the localization table and uses available locale translation. | | ## Translate substrings Use the **translate** specifier when requiring a direct translation from your localization table. The localization will search for an exact match of the parameter in the Source column of your localization table. In the following example, an experience has the following rows in its localization table: | Source | es | | --- | --- | | I am from {Place_Name:translate}. | Soy de {Place_Name:translate}. | | Brazil | Brasil | | London | Londres | | Germany | Alemania | If a user has their locale set to 'es', the translation output displays as follows: | Original in-game text | Spanish translation | | --- | --- | | I am from Brazil. | Soy de Brasil. | | I am from London. | Soy de Londres. | | I am from Germany. | Soy de Alemania. | ## Translate with numbers You can use a specifier to format your numerical values to match the context within your experience. In the following example, an experience has the following number related entries in their localization table: | Source | es | | --- | --- | | {race_time:fixed} seconds | {race_time:fixed} segundos | | ${1:num} cash and {2:int} jewels | ${1:num} dinero y {2:int} joyas | If a user has their locale set to **es**, the translation output displays as follows: | Original in-game text | Spanish translation | | --- | --- | | 75.202844 seconds | 75,20 segundos | | $2500.5 cash and 99.8 jewels | $2.500,50 dinero y 100 joyas | > **Warning:** When using multiple parameters on a single entry, you must use either all strings or all numbers for parameter specifiers.
--- title: "Work with translators" url: /docs/en-us/production/localization/work-with-translators last_updated: 2026-06-29T19:34:05Z description: "Explains the benefit of working with a native speaker to assist with localization efforts." --- # Work with translators When translating content in your experience to a language that you are not familiar with, it's beneficial to work with a native speaker of that language to add or review any translations. Use the following tools to manage translators for your experience: - [Add other Roblox users or groups](#add-translators) to help you translate your experience, - [Access localization reports](#translation-analytics) to track the contributions and translation coverage of your Roblox translators. - Share the `.csv` file of your localization table with a [Non-Roblox translator](#work-with-non-roblox-translators). > **Warning:** Before working with any translators, make sure you set your [source](/docs/en-us/production/localization.md#set-source-language) and [supported languages](/docs/en-us/production/localization.md#set-supported-languages) in the experience's localization settings. ## Add translators You can add Roblox users or groups as translators in your experience's [localization page](/docs/en-us/production/localization.md#localization-settings). Adding Roblox users as translators allows you to leverage all the benefits of Roblox's translation tools such as managing group-level permissions and viewing translation analytics. Experiences owners and translators can manage, edit, and review translations and translators. Translators can see your string entries and edit translations for any of your supported languages. Translators can not configure any localization settings, add or delete any localization table entries, or grant translator permissions to any users or groups. To add a Roblox user or group as a translator: 1. Navigate to your experience's **Localization Page**. 2. Navigate to the **Translators** tab. 3. Click the **Invite Translators** button. 4. If adding an individual, select either by **Username** or by **User ID** under the options and select the correct user from the dropdown. 5. If adding a group, input the **group ID** and select the right group from the dropdown and the selected roles. 6. Click **Confirm** once you have selected the users or groups you want to add. ## Access the Translator Portal All translators that you add to your experience can access the **Translator Portal**, a translator specific page that provides access to your experience's localization table. In the Translator Portal, translators can edit translations for all supported languages, and see translation history, including the time the translation was added and the translation contributor. To share a direct link to your experience's Translator Portal: 1. Navigate to the **Translators** tab. 2. Copy the link by clicking the **Share Link** button. 3. Send the link to any Roblox user with translator permissions so they can access the Translator Portal directly. To access an experience's Translator Portal from the Creator Dashboard: 1. Navigate to the [Creator Dashboard](https://create.roblox.com). 2. Navigate to the Translator Portal via the left navigation. 3. Select the experience you want to translate, or search for it via the search bar. ## Translation analytics As translators add translations, Roblox compiles translation reports every two weeks that give you visibility into what your translation coverage looks like for each supported language and which translators have been contributing. To download a translation report: 1. In your experience's localization page, click **Reports**. 2. Select a date range for the report in the dropdown. 3. Click **Download**. ## Work with non-Roblox translators You can work with translators who do not have Roblox accounts by downloading your experience's localization table as a `.csv` and having your translators add directly to the spreadsheet file. Once your translators have added their contributions, you can re-upload the `.csv` file through Studio. When adding translations through this workflow, translation analytics does not capture translator contributions and detailed history. See [Add translations with file upload](/docs/en-us/production/localization/manual-translations.md#add-translations-with-file-upload) for more information on downloading your experience's localization spreadsheet. --- title: "U.S. 18+ exchange rate" url: /docs/en-us/production/monetization/18-plus-devex-rate last_updated: 2026-06-29T19:34:05Z --- # U.S. 18+ exchange rate The U.S. 18+ Roblox Developer Exchange Program (DevEx) Rate offers a higher exchange rate of `0.0054` for certain Earned Robux in eligible games from eligible purchases from U.S. players who have verified their age as at least 18 years old through [facial age estimation](https://about.roblox.com/age-estimation) or [government ID](/docs/en-us/production/publishing/account-verification.md#verify-through-government-id). The program's eligibility criteria prioritizes the expectations of an older American demographic, specifically high-quality character articulation and strong creative direction. These requirements aim to incentivize novel content that includes unique and in-depth gameplay mechanics or distinctive visual styles. ## Key definitions To understand the U.S. 18+ DevEx rate's eligibility criteria, it's important to familiarize yourself with the following terms and definitions. | **Term** | **Definition** | | --- | --- | | **Earned Robux** | Robux that you can earn from creating value on the platform through certain monetization methods. | | **Character** | An in-game person, creature, or entity that interacts with others or the 3D world.

Characters include player-driven avatars, computer-controlled agents, and static content. | | **Human form** | A bipedal character based on the human form, such as people, humanoid robots, and goblins.

Human form characters generally include 2 arms, 2 legs, and a head, but appendage count can vary as long as human characteristics are still recognizable, such as:

| | **Non-human form** | Any character that's not humanoid, such as:

| | **R6** | A basic humanoid rig with 6 body parts and 6 poseable joints. | | **R15** | A [standard humanoid rig](/docs/en-us/avatar/character-bodies/specifications.md#standard-rigs) with 15 body parts and 15 poseable joints. | ## Eligibility requirements The following sections detail the U.S. 18+ DevEx rate's eligibility criteria. ### Earned Robux The U.S. 18+ DevEx rate specifically focuses on Earned Robux from the following purchases from U.S. players who have verified their age as at least 18 years old: - [Developer products](/docs/en-us/production/monetization/developer-products.md) - [Passes](/docs/en-us/production/monetization/passes.md) - [Subscriptions](/docs/en-us/production/monetization/subscriptions.md) - [Private servers](/docs/en-us/production/monetization/private-servers.md) All Earned Robux must be in complete compliance with Roblox's [Terms of Use](https://en.help.roblox.com/hc/articles/115004647846) and [Community Standards](https://en.help.roblox.com/hc/articles/203313410), and Roblox maintains the exclusive right to decide if any Robux qualifies as Earned Robux at its own discretion. ### Player characters Games that qualify for the U.S. 18+ DevEx rate must include player characters that spend **100% of active playtime** as one of the following: - Platform avatars with a [standard](/docs/en-us/avatar/character-bodies/specifications.md#standard-rigs) or [higher-fidelity](/docs/en-us/avatar/character-bodies/specifications.md#higher-fidelity-rigs) rig - Custom human-form characters with the following characteristics: - Approximately 1 head, 2 legs, and 2 arms - 12 limb parts or 12 limb joints that are equally distributed among limbs - For example, if the character's left leg has `LeftUpperLeg`, `LeftLowerLeg`, and `LeftFoot` joints, the right leg must have `RightUpperLeg`, `RightLowerLeg`, and `RightFoot` joints too - A torso with at least 2 parts or 2 joints - Full bipedal range of motion - Each joint or part articulates separately during animation - Custom nonhuman-form characters In addition, animation packs on player characters must be R15 and not R6. ### Avatar systems #### Platform avatar systems If you are using Roblox's built-in avatar system to load players into your game, you must set your project-level [avatar settings](/docs/en-us/studio/avatar-settings.md) so that players can only join and play with R15 avatars. If a player can spawn into or swap to a R6 avatar at any point during active playtime, your game is **not eligible** for the U.S. 18+ DevEx rate. To set your project-level avatar settings so that players can only join with R15 avatars: 1. In Studio, navigate to the **Avatar** tab, then select the **Settings** button. The **Avatar Settings** window displays.![Avatar Settings highlighted in Studio's toolbar](../../assets/studio/general/Toolbar-Avatar-Settings.png) 2. In the **Avatar Settings** window, click the ellipsis button, then select **R15 Only**.![R15 Only button highlighted in the Avatar Settings window.](../../assets/avatar/avatar-settings/AvatarSettings-R15Only.png) #### Custom avatar systems If you're using a custom avatar system, make sure your setup meets the requirements above and the intent behind them. If it does, your game automatically qualifies for the U.S. 18+ DevEx rate. Roblox reviews games on an ongoing basis and removes eligibility for games that don't qualify. ## Compliance checks Roblox runs a background **compliance check** on eligible games to verify that their player characters are compliant with the eligibility requirements. If the check detects any eligibility issues, your game becomes ineligible for the U.S. 18+ DevEx rate until you resolve the issue. ## Group payouts If your game is owned by a group and the game acquires Earned Robux from eligible purchases, one-time payouts and recurring revenue splits respect the U.S. 18+ DevEx rate. Both your one-time payouts and DevEx requests prioritize the U.S. 18+ DevEx rate. When you cash or pay out the Earned Robux, you can view how much of it is from the U.S. 18+ DevEx rate and the standard exchange rate. ## Frequently asked questions **When did the U.S. 18+ DevEx rate take effect?** June 8, 2026. **Does a game with a mix of R6, R15, and custom player characters qualify?** No, if a player can spawn into or switch to an R6 rig at any point during active playtime, your game is not eligible. You must remove the R6 option for your game to qualify for the U.S. 18+ DevEx rate. **My custom human-form player character has fewer than 15 parts, but more than 15 joints. Do I qualify?** Yes. Either threshold is sufficient: 15+ parts **or** 15+ joints. **What about games with no player character, like a top-down strategy game?** Games without a visible player character meet the [custom nonhuman-form criteria](#player-characters). **Does the eligibility policy apply to NPCs?** No, the eligibility criteria only applies to player characters. Roblox does not evaluate non-player characters (NPCs) for the U.S. 18+ DevEx rate. **I use a custom nonhuman-form avatar system. Do I need to do anything?** No. If you're using a custom nonhuman-form avatar system, make sure your setup meets the requirements above and the intent behind them. If it does, your game automatically qualifies for the U.S. 18+ DevEx rate. Roblox reviews games on an ongoing basis and removes eligibility for games that don't qualify.
--- title: "Avatar creation token" url: /docs/en-us/production/monetization/avatar-creation-token last_updated: 2026-06-29T19:34:05Z description: "Enable users to create and purchase avatar items in experience." --- # Avatar creation token An **avatar creation token** enables developers to allow users to create, purchase, and save avatar items directly from an experience to their platform-level inventory using [in-experience creation](/docs/en-us/avatar/in-experience-creation.md). Experiences that implement in-experience creation APIs, such as `Class.AvatarCreationService.PromptCreateAvatarAsync|PromptCreateAvatarAsync` and `Class.AvatarCreationService.PromptCreateAvatarAssetAsync|PromptCreateAvatarAssetAsync`, must include one or more avatar creation tokens. You can purchase and manage tokens in your experience's [Creator Dashboard](https://create.roblox.com/dashboard/creations). > **Info:** Owners of experiences that incorporate in-experience avatar item creation also benefit from [Marketplace commissions](/docs/en-us/marketplace/marketplace-fees-and-commissions.md#commissions) as both **creator** and **experience owner**. To create avatar creation tokens, you or your group must meet the [Marketplace creator requirements](/docs/en-us/marketplace/marketplace-policy.md#creator-requirements). For the token to remain valid, the token owner must maintain these creator requirements. ## Create a token To allow users to create, pay for, and save avatar items to their Roblox inventory in your experience, you'll need to set up an Avatar Creation Token in your experience's dashboard. > **Warning:** Each token requires a **Creation Advance** and a **Creation Fee**. The advance is fully recoupable and based on the [current publishing advance prices](/docs/en-us/marketplace/marketplace-fees-and-commissions.md#publishing-advance) for that item type. Before purchase, you can preview a summary of your total combined advance and fee when creating your token. To create a token: 1. Navigate to the [Creator Dashboard](https://create.roblox.com/dashboard/creations). 2. Expand the account switcher in the upper‑left and ensure that the appropriate user or group is selected. 3. In **Creations** ⟩ **Experiences**, select your experience. 4. On the left panel, navigate to **Monetization** ⟩ **Avatar Creation Tokens**. 5. Click **Create Token**. 1. Ensure that your account or group account meets the [Marketplace Creator Requirements](/docs/en-us/marketplace/marketplace-policy.md#creator-requirements). If the token owner account is not ID-verified with a Roblox Plus or Premium subscription, the experience cannot enable in-experience creation APIs. You must maintain these requirements for the token to remain valid. 6. Complete the following fields: 1. **Name** - The name of your token. This is not user-facing and can be changed after creation. 2. **Description** - The description of your token. This is not user-facing and can be changed after creation. 3. **Item Type** - The **Body** item type can be used in experience with `Class.AvatarCreationService.PromptCreateAvatarAsync|PromptCreateAvatarAsync` whereas the rest of the item types are meant to be used with `Class.AvatarCreationService.PromptCreateAvatarAssetAsync|PromptCreateAvatarAssetAsync`. You cannot change this setting after token creation. 4. **Amount Above Price Floor** - Set the price above the Marketplace Price Floor for created items. If you intend to have different price points for items of the same type in your experience, the best way to achieve this is by purchasing multiple tokens. You can modify this field after creation. 5. **Do not price below (Optional)** - Set the minimum price for users to purchase your item. You can modify this field after creation. ![Price related fields for avatar token creation, including a calculated item price.](../../assets/monetization/avatar-creation-tokens/Avatar-Creation-Token-Prices.png) 7. When complete, verify the **Creation Advance** and **Creation Fee** calculations and select **Create Token** to submit your purchase. 8. You can now access the created token in the experience's **avatar creation tokens** settings. To implement `Class.AvatarCreationService.PromptCreateAvatarAsync|PromptCreateAvatarAsync` or `Class.AvatarCreationService.PromptCreateAvatarAssetAsync|PromptCreateAvatarAssetAsync`, you'll need the token ID, which you can access by selecting the three dots on the thumbnail and selecting **Copy Token ID**.![Dropdown for created tokens revealing a Copy Token ID option](../../assets/monetization/avatar-creation-tokens/Avatar-Creation-Token-Copy-ID.png) --- title: "Avatar items" url: /docs/en-us/production/monetization/avatar-items last_updated: 2026-06-29T19:34:05Z description: "Sell community created Avatar items exclusively through your experience." --- # Avatar items **Avatar items** are 3D assets, such as accessories and clothing, that Roblox avatars can equip and wear. While many avatar items are sold on the Roblox's Marketplace, you can work with item creators to sell avatar items exclusively in your experience. By selling unique avatar items in your experience, you can create experience-specific cosmetics while still taking advantage of Roblox's [commission payouts](/docs/en-us/marketplace/marketplace-fees-and-commissions.md#commissions) for avatar assets sold within an experience. > **Info:** For information on how to create and sell your own avatar items, which requires third-party software such as Blender or Maya, see Roblox's [Avatar](/docs/en-us/avatar.md) documentation. ## Enable in-experience sales To set an avatar item on sale, the item creator and the experience owner must perform the following: 1. The **item creator** must [add the experience's Place ID](#add-place-id) to their item's sale location data. 2. The **experience owner** must [add the avatar item's Asset ID](#add-items-to-experience) and enable the item. 1. If the experience owner and the item creator are the same person, Roblox automatically adds and enables the asset to the experience after setting the sale location. ### Add place ID Before enabling an avatar item sale in your experience, the creator of the item must add the experience's starting place ID to the avatar item's sales location. 1. Open the [Creations](https://create.roblox.com/dashboard/creations) page on **Creator Dashboard**. 2. Click the **⋯** in the corner of the experience's thumbnail and select **Copy Start Place ID**. 3. As the item creator, [update your item's marketplace settings](/docs/en-us/marketplace/publish-to-marketplace.md#marketplace-settings) and add the Place ID to the item's Sale Location field. ### Add items to experience After the item creator adds your experience to the asset's sale location, you can now enable the item for the experience. To search and enable an asset for sale: 1. Navigate to your [Creations](https://create.roblox.com/dashboard/creations) page on **Creator Dashboard** and select your experience. 2. In the **Monetization** menu, select **Avatar Items**. All avatar items for that experience display. 3. Using the search bar, use the item's Asset ID to find the avatar item. 1. If you do not see the item populate, verify that your Asset ID is correct and that the item creator has added the correct Place ID to the item's sales locations. 4. Using the dropdown on the thumbnail, click **Enable Sale**. ## Avatar items analytics Avatar Items analytics help you gauge the success of individual Avatar Items, identify trends, and forecast potential future earnings. To access Avatar Items analytics: 1. Navigate to your [Creations](https://create.roblox.com/dashboard/creations) page on **Creator Dashboard** and select your experience. 2. Navigate to **Monetization** ⟩ **Avatar Items** and select the **Analytics** tab. The analytics tab enables you to: - **View top performing items:** See your top selling and top grossing Avatar Items over a selected time period. - **Analyze overall sales and net revenue:** Showcase up to eight top items on a time-series graph. - **Monitor your catalog:** Examine a table with up to 400 items, sortable by sales and net revenue. - **View buyer demographics:** See the demographic information of buyers, including age, gender, location, and platform. --- title: "Commerce products" url: /docs/en-us/production/monetization/commerce-products last_updated: 2026-06-29T19:34:05Z description: "Commerce products let you enable real-world purchases through your experience." --- # Commerce products You can catalog and sell real-world products, or **commerce products**, in your Roblox experience. Users can purchase these products in your experience and, if eligible, you can provide digital incentives as a bundled benefit. To create and manage catalog items and commerce products, you must meet [commerce eligibility](#eligibility). To add commerce products to your experience: 1. [Import a real-world catalog item](#import-catalog-items) from Shopify. 2. [Create a commerce product](#manage-commerce-products) in Creator Hub. 3. [Implement a purchase prompt](#implement-purchases) within your experience. ## Eligibility The following eligibility requirements apply for **creators** interested in implementing commerce products, **experiences** that host commerce features, and **users** who can purchase commerce products. **Bundled digital items** also require additional eligibility requirements. These requirements are subject to change. ### Creator Eligible creators can add commerce products to their experience. To be eligible, creators must meet and maintain the following: - Be 18+ years of age. - Have an ID-verified and email-verified account. - Have 2-step verification enabled. - Be the registered account holder on Shopify for the linked store. - Alternatively, you must have written permission to link the Shopify store from its registered account holder. - Have submitted **Business Information**. You can access this submission form in the following ways: - **Commerce Eligibility page** — When visiting the commerce page for the first time, an eligibility form appears. - **Creator Dashboard** — In [Account Information](https://create.roblox.com/dashboard/account-information), you must fill out the **Core Account** and **Legal** tabs. You can skip the Extended Services section under Core Account if it appears. - To submit business information on behalf of a group, creator must have **Configure and spend group revenue** enabled. - If accessing commerce settings on behalf of a group, your role must have **Create and edit community experiences** enabled. - Be in good standing with respect to Roblox's [Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards) on an on-going basis. ### Experience Creators must ensure that experiences that host commerce features meet the following requirements: - Experience must be public. - Experience has received [content maturity labels](/docs/en-us/production/promotion/content-maturity.md) after completing the Experience Guidelines questionnaire. - Experience complies with Roblox's [Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards), [Advertising Standards](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards#advertising), and [Commerce Standards](https://en.help.roblox.com/hc/articles/36495190721172) on an on-going basis. ### User At this time, only US-based users can purchase commerce products. Participating users must also meet the following requirements: - Must be 13+ years of age, except Texas. - Must be 18+ years of age in Texas. You can verify user eligibility by using `Class.PolicyService` to return policy information about each user. ### Digital benefits Certain creators may qualify to be able to bundle a digital benefit, such as an avatar item or developer item, with their commerce product if they meet **at least one** of the following two requirements: - The experience that hosts the shop meets the following: - Maintains more than 100,000 daily active users on average for the last 90 days. - Maintains more than 1,000,000 Robux average monthly earnings in the last 90 days. - Sell at least one bundle within three months of receiving approval to bundle. - Or enter into a written agreement with Roblox for an ad spend amount of at least $50,000 USD in one year. #### Bundled digital benefits If you qualify to bundle a digital benefit with your commerce product, each digital benefit must: - Comply with Roblox's [Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards) and [Advertising Standards](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards#advertising), as an individual item and as the combination of the product and the bundled digital benefit in the context of the bundle. - Not be an item that was previously available for purchase before listing the bundle or separately made available for purchase with Robux. - Not be Robux or in-game currency. - Disclose with sufficient detail in the product listing on Roblox and must not be uncertain to the buyer. - Not be a random item and must not require a chance-based mechanic to receive the underlying item. - Not require any additional steps to acquire after the payment for the bundle is complete. ## Import catalog items In the [Creator Dashboard](https://create.roblox.com/), you can connect your Roblox account to your Shopify store and link catalog items to your experience. You must add individual products you intend to import into Roblox. ### Import catalog items from Shopify > **Warning:** At this time, due to the link between the Shopify store and a Roblox account, Roblox group ownership transfer is not allowed for a group that owns any commerce items. 1. Navigate to your experience in the [Creator Dashboard](https://create.roblox.com/) and select **Monetization Products** ⟩ **Commerce**. 2. Click **Go to Imported Catalog** ⟩ **Start Importing**. 3. Click **Connect**. 4. Install the Roblox sales channel onto your store. 5. Connect your Roblox account. Click **Next**. 1. After connecting your Roblox account, you can optionally connect a Roblox group to grant all group members access to your imported catalog items. This can be updated at any time under **Account** in the Roblox sales channel. 6. Click **Add Products** and select the items you will import to Roblox. 7. Click **Finish** and wait for the items to sync to Roblox. You can view your imported catalog items in the Imported Catalog tab of the Commerce page. ### Remove catalog items In the Commerce page, you can remove any catalog item you have imported in the Catalog tab by clicking the **Delete** or **Remove** button next to the item. When you delete an item, all commerce products that contain the item will automatically be deleted. To sell the commerce product again, you must re-import the item and recreate the product. ### Modify catalog items When you change the price of a catalog item on Shopify, all associated commerce product bundles will be taken off sale and resubmitted for bundling fee processing. When you update the content of your Shopify item, such as the name, description, or display image, all associated products will not be taken off sale, but they will be resubmitted for moderation. The product listing on Roblox for each product will continue to show the previously approved content until the new content is approved by moderation. See [Product detail page](#product-detail-page) for more information on how these Shopify attributes are displayed and managed. ## Manage commerce products After importing a catalog item to your experience's Commerce page, you can begin creating commerce products. ### Create Once you have imported catalog items, you can create commerce products. A commerce product can be either a catalog item on its own, or a catalog item bundled with a digital benefit. You can create a maximum of 500 products per experience. > **Warning:** You cannot create an avatar item type digital benefit while creating the commerce product. Create the avatar item first before adding it to a commerce product bundle. To create a commerce product: 1. Navigate to your experience in the Creator Dashboard and select **Monetization Products** ⟩ **Commerce**. 2. Click **Start Creating** or **Create Products**. 3. Select which imported catalog items you want to include in your commerce products and click **Next**. 1. If you do not see a **Next** button, your experience is not eligible to create bundles with digital benefits. Skip to step 4. 2. At this point, your commerce products are saved as drafts. If you leave this page and want to return to them, you can find draft commerce products under **Drafts** in the **Creations** tab. **(Optional) Bundling digital benefits** If you are [eligible to bundle a commerce product with digital benefits](#digital-benefits), you can bundle a digital benefit to your commerce product. Bundled products require a bundling fee after moderation. To bundle a digital benefit: 1. Click **Add Benefit**. 2. Choose a **Virtual Benefit Type**: 3. **Avatar Item**: To be part of a bundle, the avatar item must not have been on sale before. You must own the item or belong to the group that owns the item. 4. Select an avatar item by choosing an **Avatar Item Type** and using the dropdowns. Click **Save**. 5. **Developer Product**: To be part of a bundle, the developer product must be owned by the experience, cannot have been on sale before, and must not have a price. 6. Select an existing **Developer Product** or create a new one to use in a bundle by clicking **Create New**, filling out the developer product information, and clicking **Create**. Click **Save**. 1. Developer product names within the same experience must be unique. 2. Developer products are created when you submit the product for review. In order to reuse a developer product in multiple bundles, submit one product (which creates the developer product), then select that benefit on subsequent bundles. 1. Enter the **Inventory Type** of the catalog item. 1. **Fixed Quantity**: There is a fixed quantity of the commerce item. Enter the quantity under **Qty** 2. **Merch on Demand**: The commerce item is created on demand. 3. **Pre Order**: The commerce item is for pre order. 1. Double-check the details of your products and click **Preview** to preview the product listing for each product. For details on what this page contains and how it's populated, see [Customizing the Product Detail Page](#product-detail-page). Click **Submit for review**. 2. The status of your newly created commerce products is **Pending** as the product awaits moderation and potential bundling fee processing. You can hover over the information icon to see which tasks are pending. 1. **Moderation** 1. If your commerce product is found to violate Roblox community standards, you will see the **Status** of the commerce product change to **Moderated**. 2. Hover over the information icon and click **View Violation** to view the violation and submit an appeal, or create a new commerce product that abides by the Roblox community standards. You must be the user whose account is connected to the Shopify store, or the group owner for group-owned stores to view the violation. 2. **Bundling Fee** (if applicable) 1. Once a bundling fee is available for a bundled commerce product, navigate to the commerce product to view it under **Bundling Fee**. 2. To accept, select the product and click **Accept Bundling Fee**, or click the three dots by the bundling fee and click **Accept**. 3. Once these tasks are approved for your commerce product, the commerce product **Status** becomes **Approved** and it is ready to be integrated into your experience using `Class.CommerceService`. ### Get IDs You will need the product ID of a commerce product to sell it in your experience. To get the Product ID: 1. Hover over the **Product ID**. 2. Click **Copy ID** to copy it to your clipboard. ### Put on sale Commerce products must be available for sale to be purchased in-experience. Two identical catalog items cannot be on sale at once. You must take a bundle containing a catalog item off sale to put another one with that same item on sale. A maximum of 100 products can be for sale at a time per experience. To change a product's sale state: 1. In the commerce page, navigate to the product you intend to sell. 2. Click **Put on Sale** or **Take Off Sale** for the product. ### Delete To delete a commerce product: 1. Hover over the product. 2. Click **Delete** to delete the product. ## Implement purchases In your experience, implement commerce product purchases using the following `Class.CommerceService` functions: - `Class.CommerceService:PromptCommerceProductPurchase()`: Prompts a specific user to purchase a commerce product using the provided commerceProductId. It will open a webview that guides the user through the purchasing flow. - `Class.CommerceService:PromptCommerceProductPurchaseFinished()`: Use this signal to detect when a user has completed the purchasing flow and the webview has closed to resume gameplay within the experience. - This signal does not indicate a successful purchase and only signifies when the prompt is closed. - While optional, it is recommended to use this signal to reorient your users on Android, as the commerce purchasing flow will have forced them into portrait mode. ### Grant digital benefits > **Info:**[Eligible creators](#eligibility) can add a digital benefit to their commerce products. When [creating a commerce product](#create), you have the option to bundle the catalog item with a digital benefit. #### Avatar items If an avatar item is bundled with a commerce item, Roblox automatically grants the linked avatar item after a user successfully purchases a commerce product. If Roblox detects a cancellation or refund of the physical item, Roblox automatically revokes the avatar item from the account. If the avatar item is moderated, the status of any commerce product linked to the item will become moderated and it will be taken off sale. After 7 days, Roblox will automatically revoke the avatar item from all accounts that purchased these products. Upon a successful appeal, the avatar item will be regranted. #### Developer products After a user successfully purchases a commerce product, Roblox generates a developer product receipt and sends it to the server where the user is present. Follow the existing `Class.MarketplaceService.ProcessReceipt` flow for handling developer product granting. In the event of a refund or cancellation of the physical item, you may want to revoke associated in-game items or benefits. Roblox does not automatically revoke developer products that were granted to users as a digital benefit. Roblox provides webhook integration for commerce events. When an order is paid, canceled, or refunded, Roblox will send a webhook message with the latest order status and order details. Developers can use this information to revoke in-game items or benefits as needed. For more information on webhook notifications, see the [documentation](/docs/en-us/cloud/webhooks/webhook-notifications.md#commerce). Use `CommerceProductOrderPaid` and `CommerceProductOrderRefunded` events to track the status of the product order. The EventPayload will contain the relevant `CommerceProductOrder` which has the following schema: ```lua { "type": "object", "properties": { "path": { "type": "string", "description": "The resource path of the commerce product order.\n\n Format:\n \`universes/{universe_id}/commerce-products/{commerce_product_id}/orders/{commerce_product_order_id}\`", "x-example-values": { "exampleValues": [ "universes/123/commerce-products/123/orders/123e4567-e89b-12d3-a456-426655440000" ] } }, "createTime": { "readOnly": true, "type": "string", "description": "The timestamp when the commerce order was created.", "format": "date-time", "x-example-values": { "exampleValues": [ "2023-07-05T12:34:56Z" ] } }, "updateTime": { "readOnly": true, "type": "string", "description": "The timestamp when the commerce order was last updated.", "format": "date-time", "x-example-values": { "exampleValues": [ "2023-07-05T12:34:56Z" ] } }, "state": { "readOnly": true, "enum": [ "STATE_UNSPECIFIED", "STATE_PAID", "STATE_REFUNDED" ], "type": "string", "description": "The current state of this order, i.e. PAID, REFUNDED, CANCELED\n\nPossible values:\n\n | Value | Description |\n | --- | --- |\n | STATE_UNSPECIFIED | Unspecified state. |\n | STATE_PAID | The order has been paid for. |\n | STATE_REFUNDED | The order has been refunded. |", "format": "enum", "x-example-values": { "exampleValues": [ "STATE_UNSPECIFIED" ] } }, "merchantOrderId": { "immutable": true, "type": "string", "description": "The merchant-specific order ID that can be used to look up the order on the\n merchant platform." }, "merchant": { "immutable": true, "enum": [ "MERCHANT_PLATFORM_UNSPECIFIED", "SHOPIFY", ], "type": "string", "description": "The merchant that is used to fulfill orders.\n\nPossible values:\n\n | Value | Description |\n | --- | --- |\n | MERCHANT_PLATFORM_UNSPECIFIED | Unspecified merchant type. |\n | SHOPIFY | The item is sold through Shopify. |", "format": "enum", "x-example-values": { "exampleValues": [ "MERCHANT_PLATFORM_UNSPECIFIED" ] } }, "user": { "immutable": true, "type": "string", "description": "The user who placed this order.", "x-example-values": { "exampleValues": [ "users/123" ] } } }, "description": "A commerce product order represents a purchase of a CommerceProduct by a\n Roblox user. It includes the user who made the purchase and merchant order\n details.", } ``` ## Product detail page The **Product detail page (PDP)** showcases your commerce product to users within the Roblox experience, for example, when prompted via [`Class.CommerceService:PromptCommerceProductPurchase()`](#implement-purchases). You can customize several elements of this page to provide a unique and informative shopping experience. > **Info:** When viewing the product detail page for a product that is not yet on sale, a "Preview Mode" banner is displayed at the top (as seen in the example PDP image). Only creators with sufficient permissions in the experience can see this preview. [Product Images](#product-images) showcases product images provided in your Shopify store. [Product title, price, and seller information](#product-title-price-and-seller-information) contains product and seller details. [Variants and quantity](#variants-and-quantity) shows different available options and quantities. [Product details](#product-details) displays the Shopify product description. [Additional information](#additional-information) includes supplementary details, such as "Materials", "Care instructions", or "Size chart". [Virtual benefit with purchase](#virtual-benefit-with-purchase) details the virtual item users will receive with their purchase. [More from this experience](#more-from-this-experience) suggests other commerce products offered within the same experience. ### Product images The product images section displays all product images from your Shopify store. The order of the images directly mirrors the order set in Shopify. The first image, known as the **featured** image in Shopify, serves as the primary thumbnail for your product. This thumbnail is used in various places, such as within the shopping cart and in the [More from](#more-from-this-experience) section. When you update product images in Shopify, these changes are subject to the [moderation process for catalog items](#modify-catalog-items). The product listing on Roblox will continue to show the previously approved images until the new images are approved. ### Product title, price, and seller information The product title is taken directly from the product title set in the Shopify store. The displayed price dynamically updates if the user selects differently priced product [variants and quantity](#variants-and-quantity). The seller information displayed is your Shopify store name. Similar to other product attributes, if you update the product title in Shopify, these changes are subject to the [moderation process for catalog items](#modify-catalog-items). The product listing on Roblox will continue to show the previously approved title until the new title is approved. ### Variants and quantity Product variants and their options, such as different colors or sizes, are synchronized from your Shopify store. The order in which these variants and options appear matches the order in Shopify, displayed from top to bottom. When a user selects a specific variant, the main product image updates to display the image associated with that particular variant in Shopify. At this time, the quantity selector on the PDP does not offer customization options. If the selected product variant is not available, the "Buy now" and "Add to cart" buttons are disabled. Ensure your variants are correctly configured in Shopify to allow purchases. Any updates made to product variants or their images in Shopify are subject to the [moderation process for catalog items](#modify-catalog-items). The product detail page will continue to display the previously approved variant information and images until the new versions are approved. ### Product details The **Product details** section displays the content directly from your Shopify product description. This section supports the use of HTML for styling and formatting your product information. For security and consistency, Roblox automatically removes certain HTML elements, including links, images, scripts, and other similar tags and attributes. You can otherwise use HTML to style and structure the text content, such as using headings, bold text, lists, and paragraphs. Product descriptions must include accurate descriptions of the merchandise, covering details such as functionality, specifications, features, return or exchange policies, and origin. Please ensure that all your commerce products have complete descriptions in Shopify in order to be approved for sale. See [Commerce Standards](https://en.help.roblox.com/hc/articles/36495190721172) for more information. As with other product information synced from Shopify, any updates to the product description are subject to the [moderation process for catalog items](#modify-catalog-items). The product details will continue to show the previously approved description until the new content is approved by moderation. ### Additional information The **Additional information** section displays supplementary details about your product using collapsible accordions. You can add **up to five** accordion sections for different topics, such as "Materials," "Care Instructions," or "Sizing". This is an optional feature and is populated using Shopify product metafields. To create a metafield accordion in Shopify: - Define a product metafield in your Shopify store with a specific namespace: `Roblox.pdp_info_`. - You can append a unique identifier to this namespace, such as `Roblox.pdp_info_materials` or `Roblox.pdp_info_care_instructions`. - Set the following fields: - **Name** — The value in this field displays as the header for that accordion. - **Type** — Only "Multi-line text" is supported. - Then set the **Value** of the metafield for each desired product. This value displays as the content for that accordion. Like other product content synced from Shopify, any new or updated information in these metafields is subject to the [moderation process for catalog items](#modify-catalog-items). This page will display previously approved content until the new information is approved. ### Virtual benefit with purchase If you have bundled a [digital benefit](#digital-benefits) to your product, this section showcases the virtual item that users receive with their purchase. This section provides a clear visual and informational cue to users about the added digital value they receive, enhancing the appeal of your commerce product. The content in this section depends on the type of virtual item offered: - Avatar item - This section displays the thumbnail and name of the avatar item. - Users can click on this section to view a 3D preview of the avatar item. - The description of the avatar item displays when the user clicks into this section. - Developer product - This section displays the image and name of the developer product. - The description of the developer product displays when the user clicks into this section. The specific avatar item or developer product, including its name, description, and image, is configured when you [create the commerce product bundle](#create). As these benefits are part of the overall product offering, they are subject to Roblox's content policies and moderation. ### More from this experience This section displays other commerce products you offer that are currently on-sale within the same Roblox experience. This feed encourages further exploration of your product catalog and can help increase visibility for your experience's full range of product offerings. In this section, users can view the product's feature image, name, and price. --- title: "Roblox Developer Exchange Program" url: /docs/en-us/production/monetization/developer-exchange last_updated: 2026-06-29T19:34:05Z description: "The Roblox Developer Exchange Program allows eligible creators to exchange their Earned Robux for real-world local currency." --- # Roblox Developer Exchange Program The **Roblox Developer Exchange Program** (**DevEx**) is a program that allows eligible creators to exchange their Earned Robux for real-world local currency. ## What is Earned Robux? **Earned Robux** is Robux that you can earn from creating value on the platform, including: - Earnings from selling in-experience items to players, such as [developer products](/docs/en-us/production/monetization/developer-products.md), [passes](/docs/en-us/production/monetization/passes.md), [subscriptions](/docs/en-us/production/monetization/subscriptions.md) within experiences, fees from in-experience purchases of Marketplace items, and fees from [Robux Transfers](/docs/en-us/production/monetization/robux-transfers.md) through the Transfer API - Earnings from selling things related to your experience, such as paid [private servers](/docs/en-us/production/monetization/private-servers.md), [paid access](/docs/en-us/production/monetization/paid-access-robux.md) purchased in Robux, [Roblox Plus](/docs/en-us/production/monetization/roblox-plus.md) incentives, and [publishing ads](/docs/en-us/production/promotion/advertise.md) within an experience - Earnings from selling to other creators, such as paid models and plugins on the [Creator Store](/docs/en-us/production/creator-store.md) - Earnings from selling avatar items in the [Marketplace](/docs/en-us/marketplace/publish-to-marketplace.md) - Earnings from driving daily engagement and audience expansion ([Creator Rewards](/docs/en-us/creator-rewards.md)) All Earned Robux must be in complete compliance with Roblox's [Terms of Use](https://en.help.roblox.com/hc/articles/115004647846) and [Community Standards](https://en.help.roblox.com/hc/articles/203313410). For example, Earned Robux does **not** include: - Direct Robux purchase - Monthly Robux grants as part of a Roblox subscription - Trading or resale of in-experience items or avatar items - Robux received from Robux transfers - Robux gift card redemption - Passes sold for template experiences with no legitimate user visits - Violative content that was moderated Roblox maintains the exclusive right to decide if any Robux qualifies as Earned Robux at its own discretion. ## What is the exchange rate? The standard exchange rate for the DevEx program is `0.0038` per 1 Earned Robux, which comes out to $114 USD for 30,000 Earned Robux. Roblox offers a higher exchange rate of `0.0054` for certain Earned Robux in eligible games from purchases of developer products, passes, subscriptions, and private servers from U.S. players who have verified their age as at least 18 years old through [facial age estimation](https://about.roblox.com/age-estimation) or [government ID](/docs/en-us/production/publishing/account-verification.md#verify-through-government-id). For more information, see the [U.S. 18+ exchange rate](/docs/en-us/production/monetization/18-plus-devex-rate.md). **Have Earned Robux balances from before September 5, 2025?** Before September 5, 2025 at 10am PT, the standard exchange rate was `0.0035`, which comes out to $105 USD for 30,000 Earned Robux. If you have balances of Earned Robux before September 5, 2025 at 10am PT, those balances will cash out at the `0.0035` exchange rate in effect at that time. You can only cash out your Earned Robux at the old exchange rate before you can cash out any Earned Robux at the current exchange rate. This includes one-time payouts from [groups](/docs/en-us/projects/groups.md). Spending Robux on the platform does not reduce any applicable Earned Robux at the old exchange rate. Any Robux you spend deducts from your total balance, but it doesn't count towards clearing your previous exchange rate Earned Robux balance first. ## Eligibility requirements To participate in the DevEx program, you must agree to the program's Terms of Use and meet its requirements, including but not limited to: - Be at least 13 years old - Have a minimum of 30,000 Earned Robux in your account - Have a Roblox-verified email address - Have a valid DevEx portal account - Have either an IRS form W-9 (for U.S. taxpayers) or W-8 (for non-U.S. taxpayers) on file with Roblox - Be in complete compliance at all times with Roblox's [Terms of Use](https://en.help.roblox.com/hc/articles/115004647846) and [Community Standards](https://en.help.roblox.com/hc/articles/203313410) For the complete list of DevEx eligibility requirements, see [Developer Exchange Terms of Use](https://en.help.roblox.com/hc/en-us/articles/115005718246-Developer-Exchange-Terms-of-Use). > **Error:** Noncompliance with the Roblox Terms of Use or Community Standards may result in suspension from the DevEx program. ## Submit a DevEx request If you meet all of the DevEx program's eligibility requirements, you can submit a DevEx request to exchange your Earned Robux for local currency at the current exchange rate. This process is called "cashing out", and it is only available when you have a minimum of 30,000 Earned Robux in your account. To submit a DevEx request to cash out: 1. Navigate to the [Creator Hub](https://create.roblox.com/). 2. In the left-hand navigation, select **Finances**. 3. In the top-right corner, select the **Cash Out** button. The **Developer Exchange (DevEx)** form displays. 4. Complete the form with your information, specify how much Robux you want to exchange, and agree to the terms and conditions. > **Info:** You must only use your legal name in this form. Nicknames, family members, friends, or joke names, along with any other inaccurate or incomplete information, may result in your account’s ineligibility to participate in the DevEx program, or significantly delay your review. 5. At the bottom of the page, click the **Cash Out** button. Roblox removes the Earned Robux from your account and begins the review process. If Roblox rejects your DevEx request, your Earned Robux will be immediately refunded to your account. If this is your first time cashing out and Roblox approves your request, you will receive an email from `no-reply@roblox.com` inviting you to create an account on Roblox's [DevEx Portal](https://suppliers.tipalti.com/Roblox/Account/Login). You must provide your payment method and tax form within one week of receiving the registration link. > **Warning:** If you do not complete your registration within one week of receiving the registration link, Roblox automatically declines your DevEx request and refunds your Earned Robux. It's important that you enter your payment method, tax form, and all of your personal information accurately as you create your account on the DevEx Portal, and that you keep it up to date at all times. This information ensures all payments comply with laws and regulations, and any inaccuracies in your information impacts your payments. > **Info:** For information on the DevEx Portal and common tax questions, see [Tax and DevEx Portal (Tipalti) Information](https://en.help.roblox.com/hc/en-us/articles/27985018895124-Tax-and-DevEx-Portal-Tipalti-Information). Once your registration is complete, the DevEx team begins processing your payment. The following timelines for this process are **estimates** on how long it can take for the DevEx team to review and process your request: - For first time DevEx participants, allow around 10 business days. - For returning DevEx participants, allow around 5 business days. Keep in mind that the DevEx team does not process requests on weekends and holidays, and that there are additional processing times for the payment to land in your bank account once the DevEx team finishes processing your payment. ## Frequently asked questions **How do I know how much Earned Robux I have?** The [DevEx Dashboard](https://create.roblox.com/dashboard/devex) provides an estimation of your Earned Robux amount, but it is not a guarantee of payout or that all of your Earned Robux balance is eligible for the DevEx program. The DevEx team still reviews your eligible Earned Robux by reviewing sources accordingly. Note that there is up to a 24 hour delay between Earned Robux transfers/sales and the Earned Robux balance reflecting the new values. If you want to submit a DevEx request at a higher value than what your Earned Robux balance currently displays, you may do so. **Can you let me know if I am eligible for DevEx before I submit a request?** Roblox is unable to guarantee eligibility as all DevEx requests are reviewed upon submission. **I submitted a request and my Robux disappeared. Where did my Robux go?** When you submit a DevEx request, the Earned Robux amount you specified in the request is removed from your account and held during the DevEx review process. If Roblox rejects your DevEx request, your Earned Robux will be immediately refunded to your account. **Why did Roblox reject my DevEx request?** Roblox can reject DevEx requests for a variety of reasons, including but not limited to: - Failure to complete DevEx Portal registration within a week of receiving the DevEx Portal invitation to create an account - Failure to submit tax form and/or payment method information in the DevEx Portal - Insufficient Earned Robux - Roblox's [Terms of Use](https://en.help.roblox.com/hc/en-us/articles/115004647846-Roblox-Terms-of-Use) or [Community Standards](https://about.roblox.com/community-standards) violations, including but not limited to: - Scamming - Account theft - Attempts to exchange Robux for real currency other than through the DevEx program - Suspicious activity to exploit other Roblox users or features - Any illegal, unethical, or otherwise prohibited activities **My previous DevEx requests were approved, but my most recent DevEx request was rejected. Why?** Previous DevEx request approvals are not guarantees that Roblox will approve your future DevEx requests. The DevEx team reviews each DevEx request for eligibility upon submission. If activity that violates the [DevEx Terms of Use](https://en.help.roblox.com/hc/en-us/articles/115005718246-Developer-Exchange-Terms-of-Use) or the spirit of the program is identified at any time, Roblox reserves the right to reject DevEx requests. **My request was rejected for a name discrepancy. What does this mean and how can I fix it?** If Roblox rejects your DevEx request because of a name discrepancy, do not submit another request. The rejection means that the DevEx team noticed that the name of your DevEx request differs from previous requests and/or your account name in the DevEx Portal. Before you submit another DevEx request, contact Roblox using the [Support form](https://www.roblox.com/support) as you are unable to correct your name on your own. Any party involved will likely need to follow the ID verification process. For more information on this process, see [ID verification](https://en.help.roblox.com/hc/en-us/articles/27984775142292-ID-Verification). **My DevEx request was approved, but I received a different amount than what is listed on the current exchange rate. Why?** The exchange rate for cashing out may vary according to when you earned the Earned Robux, and the exchange rate is subject to change. For example, if you have any Earned Robux balances from before September 5, 2025 at 10am PT, your DevEx requests will pull DevEx funds from your account at the previous exchange rate (`0.0035` per 1 Earned Robux) until all earnings from that period of time are cashed out. **I received a moderation action on my account 3 years ago, does this mean I can't submit a DevEx request?** Not all moderation actions result in suspension from the DevEx program. Roblox takes into consideration the type of violation and its timing during the review process. In order to ensure there are no issues with your eligibility in the program, ensure that all of your activity on Roblox remains compliant with Roblox's [Terms of Use](https://en.help.roblox.com/hc/en-us/articles/115004647846-Roblox-Terms-of-Use) and [Community Standards](https://about.roblox.com/community-standards). **Can I cancel my DevEx request?** After you submit a DevEx request, Roblox cannot cancel it. **How often can I cash out?** You can have a completed DevEx request once per calendar month. For example, if you have completed a DevEx request where you have received payment in January, the next time you can submit a DevEx request is in February. **Do group funds count as Earned Robux?** Group payouts count as Earned Robux if the group funds are composed of bona fide earnings. Roblox checks all earnings and group funds for validity. If you receive group funds from a group that earned the Robux against Roblox's [Terms of Use](https://en.help.roblox.com/hc/en-us/articles/115004647846-Roblox-Terms-of-Use) or [Community Standards](https://about.roblox.com/community-standards), then those group funds do not count towards your Earned Robux balance. --- title: "Developer Products" url: /docs/en-us/production/monetization/developer-products last_updated: 2026-06-29T19:34:05Z description: "Developer Products let you charge users a Robux fee for items or abilities that they can access and use inside your game." --- # Developer Products A **developer product** is an item or ability that a user can purchase more than once, such as in-game currency, ammo, or potions. > **Info:** For items or abilities that a user should only purchase **once**, such as a special weapon or a permanent power-up, see [Passes](/docs/en-us/production/monetization/passes.md). ## Create a developer product > **Warning:** Before creating a developer product, make sure your game has been [published](/docs/en-us/production/publishing/publish-games-and-places.md) and is accessible on Roblox. To create a developer product: 1. Go to [Creations](https://create.roblox.com/dashboard/creations) and select a game. 2. Go to **Monetization** ⟩ **Developer Products**. 3. Click **Create a Developer Product**. 4. Upload an image to display as the product icon. Make sure the image doesn't exceed 512x512 pixels, doesn't include important details outside of its circular boundaries, and is in `.jpg`, `.png`, or `.bmp` format. 5. Enter a name and a description for the product. 6. Set the product price in Robux. The minimum price is 1 Robux, and the maximum price is 1 billion Robux. 7. Click **Create Developer Product**. > **Info:** If you want to use the developer product as a randomized reward, review the [randomized virtual item policy](/docs/en-us/production/monetization/paid-random-items.md). ## Get the developer product ID To use scripting, you need a developer product ID. To get the product ID: 1. Go to **Monetization** ⟩ **Developer Products**. 2. Hover over a product's thumbnail, click the **⋯** button, and select **Copy Asset ID** from the context menu. ## Sell a developer product > **Warning:** Starting May 30, 2026, cross-experience developer product sales will be disabled. If your game relies on cross-experience sales mechanics, we recommend integrating [Robux transfers](/docs/en-us/production/monetization/robux-transfers.md) before this date to avoid service interruptions. > **Info:** If you're using [price optimization](/docs/en-us/production/monetization/price-optimization.md), make sure to place the script inside a `Class.LocalScript` so that users see personalized product prices. Before selling developer products, make sure you are properly processing sales receipts and granting users their purchased products. To do so, you must: - Use the `Class.MarketplaceService.ProcessReceipt|ProcessReceipt` API to check purchase receipts. `ProcessReceipt` automatically reads and acknowledges that a user has purchased a product outside of the game. - Validate each receipt for `User ID`, `Developer Product ID`, and the receipt status. - If the receipt has a status of **Open**, grant the user the developer items they have purchased. - Respond to the `ProcessReceipt` API with a message acknowledging the receipt and validating that the purchased items were granted. You can sell developer products in two ways: - [Inside your game](#inside-your-experience) - [Outside your game](#outside-your-experience) ### Inside your experience To implement and sell a developer product inside a game, call `Class.MarketplaceService|MarketplaceService` functions. Use `Class.MarketplaceService:GetProductInfo()|GetProductInfo` to retrieve information about a developer product, like name and price, and then to display that product to users. You can sell the product inside your game's marketplace, for example. For developer products, the second parameter must be `Enum.InfoType.Product`. ```lua local MarketplaceService = game:GetService("MarketplaceService") -- Replace the placeholder ID with your developer product ID local productId = 000000 local success, productInfo = pcall(function() return MarketplaceService:GetProductInfo(productId, Enum.InfoType.Product) end) if success and productInfo then -- Display product information -- Replace the print statements with UI code to display the product print("Developer Product Name: " .. productInfo.Name) print("Price in Robux: " .. productInfo.PriceInRobux) print("Description: " .. productInfo.Description) end ``` Use `Class.MarketplaceService:GetDeveloperProductsAsync()|GetDeveloperProductsAsync` to retrieve all developer products associated with your game. This function returns a `Class.Pages|Pages` object that you can inspect and filter to build things like an in-game store or product list GUI. ```lua local MarketplaceService = game:GetService("MarketplaceService") local success, developerProducts = pcall(function() return MarketplaceService:GetDeveloperProductsAsync() end) if success and developerProducts then local firstPage = developerProducts:GetCurrentPage() for _, developerProduct in firstPage do -- Replace the print statement with UI code to display the product print(field .. ": " .. value) end end ``` Use `Class.MarketplaceService:PromptProductPurchase()|PromptProductPurchase` to prompt product purchases inside your game. You can call this function when a user performs actions like pressing a button or talking to a vendor NPC. ```lua local MarketplaceService = game:GetService("MarketplaceService") local Players = game:GetService("Players") local player = Players.LocalPlayer -- Replace the placeholder ID with your developer product ID local productId = 000000 local function promptProductPurchase() local success, errorMessage = pcall(function() MarketplaceService:PromptProductPurchase(player, productId) end) if success then print("Purchase prompt shown successfully") end end ``` You can also combine functions inside a `Class.LocalScript|LocalScript`. For example, you can create a UI element like a button or a vendor NPC, then use `Class.MarketplaceService:GetProductInfo()|GetProductInfo()` to connect an existing developer product to that element, check if the product is for sale, and use `Class.MarketplaceService:PromptProductPurchase()|PromptProductPurchase()` to prompt a purchase whenever the user clicks on it. ```lua local MarketplaceService = game:GetService("MarketplaceService") local Players = game:GetService("Players") local player = Players.LocalPlayer local button = script.Parent -- Replace the placeholder ID with your developer product ID local productId = 000000 -- Gets product info when user clicks the UI button button.MouseButton1Click:Connect(function() local success, productInfo = pcall(function() return MarketplaceService:GetProductInfo(productId, Enum.InfoType.Product) end) if success and productInfo then -- Checks if product is for sale if productInfo.IsForSale then print("This is for sale") -- Prompts product purchase MarketplaceService:PromptProductPurchase(player, productId) else -- Notifies product isn't for sale print("This product is not currently for sale.") end else print("Error retrieving product info: " .. tostring(productInfo)) end end) ``` ### Outside your experience To enable developer product purchases outside your game, you must work with the `Class.MarketplaceService.ProcessReceipt|ProcessReceipt` API. After a user makes a purchase in the **Store** tab of your game details page, you must use `ProcessReceipt` to confirm their purchase and grant them their items once they enter your game. > **Warning:** Do **not** use the `Class.MarketplaceService:PromptProductPurchaseFinished|PromptProductPurchaseFinished` event to process purchases. You must use the `ProcessReceipt` callback instead. > > The firing of `PromptProductPurchaseFinished` does not mean that a user has successfully purchased an item. #### Test mode > **Warning:** Items for sale in test mode cost actual Robux. We recommend testing low-cost developer products. The **test mode** feature helps you validate your purchase flow by simulating a developer product purchase outside your game. You should use test mode to make sure that you have implemented `ProcessReceipt` correctly before enabling external developer product sales. The developer products you put up for sale in test mode can only be seen by you and by members of your group. They are not visible to users. To test your implementation: 1. In the **Creator Hub**, go to **Monetization** ⟩ **Developer Products**. 2. Click the **⋮** menu and select **External Purchase Settings**. 3. In the **External Purchase Settings** page, click **Enable test mode**. 4. Once test mode is active, return to the **Developer Products** page and select a product to test. 5. In the **Basic Settings** page, select the **Allow external purchases** checkbox and save your changes. 6. Go to the **Store** tab of the game details page and purchase the product you made available for sale. 7. Enter the game and confirm that you have received the product you purchased. The receipt status of the `ProcessReceipt` API should update to **Closed**. After you test your implemention, Roblox verifies that the test has been successfully completed and allows you to fully activate the feature to sell developer products outside your games. For more information about the `ProcessReceipt` API and its implementation, see the `Class.MarketplaceService.ProcessReceipt|ProcessReceipt` page. #### Enable external sales > **Info:** You can only enable external sales after you have used test mode to validate your purchase flow. To enable external sales: 1. Go to the **External Purchase Settings** page. 2. Turn on **External Purchases**. 3. Return to the **Developer Products** page and select the products you want to sell outside of your game. 4. In the **Basic Settings** page, select the **Allow external purchases** checkbox and save your changes. 5. Confirm that the products are now available for purchase in the **Store** tab of the game details page. To disable the external sale of a developer product, select the product on the **Developer Products** page and clear the **Allow external purchases** checkbox. #### Limitations - Items for sale in test mode cost actual Robux. We recommend testing low-cost developer products. - Items for sale in test mode can only be seen by you or by members of your group. - To be sold externally, your developer products **must** have a thumbnail. - You should not sell the following outside your game: - Paid random items - Items that are limited to specific quantities, time, place, or user settings and roles ## Handle a developer product purchase After a user purchases a developer product, you must handle and record the transaction. To do this, use a `Class.Script` within `Class.ServerScriptService` using the `ProcessReceipt` function. For more information about the `ProcessReceipt` API and its implementation, see the `Class.MarketplaceService.ProcessReceipt|ProcessReceipt` page. ```lua local MarketplaceService = game:GetService("MarketplaceService") local Players = game:GetService("Players") local productFunctions = {} -- Example: product ID 123123 brings the user back to full health productFunctions[123123] = function(receipt, player) local character = player.Character local humanoid = character and character:FindFirstChildWhichIsA("Humanoid") if humanoid then humanoid.Health = humanoid.MaxHealth -- Indicates a successful purchase return true end end -- Example: product ID 456456 awards 100 gold coins to the user productFunctions[456456] = function(receipt, player) local leaderstats = player:FindFirstChild("leaderstats") local gold = leaderstats and leaderstats:FindFirstChild("Gold") if gold then gold.Value += 100 return true end end local function processReceipt(receiptInfo) local userId = receiptInfo.PlayerId local productId = receiptInfo.ProductId local player = Players:GetPlayerByUserId(userId) if player then -- Gets the handler function associated with the developer product ID and attempts to run it local handler = productFunctions[productId] local success, result = pcall(handler, receiptInfo, player) if success then -- The user has received their items -- Returns "PurchaseGranted" to confirm the transaction return Enum.ProductPurchaseDecision.PurchaseGranted else warn("Failed to process receipt:", receiptInfo, result) end end -- The user's items couldn't be awarded -- Returns "NotProcessedYet" and tries again next time the user joins the game return Enum.ProductPurchaseDecision.NotProcessedYet end -- Sets the callback -- This can only be done once by one server-side script MarketplaceService.ProcessReceipt = processReceipt ``` > **Info:** The `receiptInfo` table passed to the `Class.MarketplaceService.ProcessReceipt()|ProcessReceipt()` callback function contains detailed information about the purchase, like the ID of the purchased product, the user who made the purchase, and the currency they used. > > `receiptInfo` also contains the exact Robux amount the user paid for the product, even if you're running a [price optimization test](/docs/en-us/production/monetization/price-optimization.md) and showing different users different prices. > **Warning:** The functions for handling each product ID must return `true` for the transaction to be successful. If they don't, the product will not be awarded to the user who purchased it. > **Warning:** Although Roblox itself does **not** record the purchase history of developer products by specific users, you can request to [download sales data](/docs/en-us/production/analytics/analytics-dashboard.md#sales-data). If you want to track user-specific purchase history, it's your responsibility to [store the data](/docs/en-us/cloud-services/data-stores.md). ## Personalize your in-experience store You can use product intelligence APIs to sort and recommend developer products to users. Personalizing your in-game store helps surface the most relevant items to each user, boosting engagement and revenue. By tailoring developer products to user preferences, you can improve their discovery, increase conversion rates, and unlock new monetization opportunities. ### Rank developer products for sale `Class.MarketplaceService.RankProductsAsync|RankProductsAsync` takes in a list of product IDs and returns a personalized ordered list of those products. You can use this method to provide your users with personalized item recommendations in your in-game store. > **Warning:** Because `RankProductsAsync` has a strict rate limit, you should load recommendations once at game join instead of calling it repeatedly. _Example: Three "Powers" items ranked for the user_![Three items ranked for the user.](../../assets/monetization/developer-products/RankedItems.png) ```lua local MarketplaceService = game:GetService("MarketplaceService") -- Create an array of products you want to rank local productIdentifiers = { {InfoType = Enum.InfoType.GamePass, Id = 123}, {InfoType = Enum.InfoType.Product, Id = 456}, {InfoType = Enum.InfoType.Product, Id = 789} } -- Make a protected call to handle errors local success, rankedProducts = pcall(function() return MarketplaceService:RankProductsAsync(productIdentifiers) end) if not success then error("Failed to rank products") end -- Load the returned items into the store for i, rankedItem in ipairs(rankedProducts) do local productIdentifier = rankedItem.ProductIdentifier local productInfo = rankedItem.ProductInfo -- ... -- Logic to add products into store end ``` ### Display your top developer products > **Warning:** If your game has had no item sales in the past 28 days, `RecommendTopProductsAsync` returns an empty list. At least one purchase within the last 28 days is required for this API to generate recommendations. `Class.MarketplaceService.RecommendTopProductsAsync|RecommendTopProductsAsync` takes an array of `Enum.InfoType|InfoType` values and returns up to 50 items a user is most likely to engage with and purchase. You can use this method to create a "Top Picks" section in your in-game store. If no recommendations can be determined, `RecommendTopProductsAsync` returns 0 items. In rare cases, calls to the ranking model can be slow. To help prevent added lately for users, we recommend using `task.spawn` to make the call to `RecommendTopProducts` non-blocking. _Example: A "Top Picks" tab in an in-game store_![Top Picks tab of an in-game store.](../../assets/monetization/developer-products/StoreTopPicks.png) ```lua local MarketplaceService = game:GetService("MarketplaceService") -- Create an array of product types you want to include in the recommendations -- This example includes both passes and developer products local productTypes = {Enum.InfoType.GamePass, Enum.InfoType.Product} -- Make a protected call to handle errors local success, topRankedItems = pcall(function() return MarketplaceService:RecommendTopProductsAsync(productTypes) end) if not success then error("Failed to rank products") end -- Load the returned items into the store -- Make sure to filter out any ineligible items from topRankedItems such as developer products the user can no longer purchase for i, rankedItem in ipairs(topRankedItems) do local productIdentifier = rankedItem.ProductIdentifier local productInfo = rankedItem.ProductInfo -- ... -- Logic to add products into store end ``` ## Developer product analytics Use developer product analytics to analyze the success of individual products, identify trends, and forecast potential future earnings. With analytics, you can: - View your top developer products over a selected time period. - Showcase up to eight top-selling items on a time-series graph to analyze overall sales and net revenue. - Monitor your catalog and sort items by sales and net revenue. To access developer product analytics: 1. Go to [Creations](https://create.roblox.com/dashboard/creations) and select a game. 2. Go to **Monetization** ⟩ **Developer Products**. 3. Select the **Analytics** tab. --- title: "Engagement-based payouts" url: /docs/en-us/production/monetization/engagement-based-payouts last_updated: 2026-06-29T19:34:05Z description: "Engagement-based payouts allow you to earn Robux based on the share of time Roblox Premium members engage in your experience." --- # Engagement-based payouts > **Info:** Effective July 24, 2025, the Engagement-Based Payouts program is deprecated and has been replaced by the [Creator Rewards](/docs/en-us/creator-rewards.md) program. Automatic **engagement-based payouts** let you earn Robux based on the share of time that [Premium](https://www.roblox.com/premium/membership) members engage in an experience, regardless of the experience's size. These payouts are in addition to earnings from other [monetization](/docs/en-us/production/monetization.md) methods such as [Passes](/docs/en-us/production/monetization/passes.md). To encourage Premium subscriber growth and increase your potential to earn engagement-based payouts, you can add the [Premium Purchase Modal](#premium-purchase-modal) directly into an experience. Keep in mind, users from some countries may not have access to Premium subscriptions. ## Access payout data Payout data can provide vital feedback to help you understand what factors drive Premium subscribers to your experiences. To access payout data: 1. Navigate to your [Creations](https://create.roblox.com/dashboard/creations) page on **Creator Dashboard** and select your experience. 2. Navigate to the **Monetization** tab and select **Engagement Payouts**. ### Engagement payouts The **Engagement-Based Payouts** charts track payout data based on the following metrics: - **Premium Playtime Robux Earned:** The amount you can expect to earn for Premium subscriber engagement. This is not based on the daily time Premium subscribers spend engaging with the experience; instead, this metric aggregates each user's behavior over the past 28 days. As such, even though they have similar trends, this metric has no direct mathematical relationship with the Premium Playtime Score. - **Premium Playtime Score:** The amount of time Premium subscribers engage with the experience per day. This metric can provide immediate feedback on the impact of new features you release. - **Premium Visits:** How many visits are from Premium members. > **Info:** Note that the dotted "projected earnings" line becomes solid after the payout amount is final, at which point Roblox adds the payout to your **Pending Robux** amount [here](https://www.roblox.com/transactions). ## Premium purchase modal One strategy to increase engagement-based payouts is to encourage [Premium](https://www.roblox.com/premium/membership) upgrade through the **purchase modal**. Players can complete the purchase entirely within the experience and immediately receive both Premium status and their initial stipend of Robux. ![Premium purchase modal within an experience](../../assets/monetization/engagement-based-payouts/Modal-Example.jpg)_Premium purchase modal within an experience_ Remember that Premium membership should not be a "requirement" to enjoy an experience. When implementing incentives for Premium members, it's highly recommended that you follow these best practices: - Honestly and accurately describe the benefits of upgrading within the experience's [description](/docs/en-us/production/publishing/publish-games-and-places.md#publish-games). - Do not promise Robux or other out-of-experience rewards that you don't control. - Do not show the modal as a "paywall" when non-Premium members enter the experience. - Consider offering exclusive merch to Premium members, but do not give them a tactical gameplay advantage over others, such as an array of ultra-powerful weapons that non-Premium members can't compete against. ### Check membership Before scripting any logic related to Premium membership or [triggering the modal](#trigger-the-modal), check a user's `Class.Player.MembershipType|MembershipType` property to determine if they're already subscribed. ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer if player.MembershipType == Enum.MembershipType.Premium then -- Take some action specifically for Premium members end ``` ### Trigger the modal You can trigger the purchase modal with the `Class.MarketplaceService:PromptPremiumPurchase()|PromptPremiumPurchase()` method. For example, the following code prompts users to purchase Premium when their character touches the part that its containing `Class.Script` is attached to, such as a teleporter that allows access to an exclusive area. ```lua local MarketplaceService = game:GetService("MarketplaceService") local Players = game:GetService("Players") local teleporter = script.Parent local showModal = true local TELEPORT_POSITION = Vector3.new(1200, 200, 60) -- Teleport character to exclusive area local function teleportPlayer(player) -- Request streaming around target location player:RequestStreamAroundAsync(TELEPORT_POSITION) -- Teleport character local character = player.Character if character and character.Parent then local currentPivot = character:GetPivot() character:PivotTo(currentPivot * CFrame.new(TELEPORT_POSITION)) end end -- Detect character parts touching teleporter teleporter.Touched:Connect(function(otherPart) local player = Players:GetPlayerFromCharacter(otherPart.Parent) if not player then return end if not player:GetAttribute("CharacterPartsTouching") then player:SetAttribute("CharacterPartsTouching", 0) end player:SetAttribute("CharacterPartsTouching", player:GetAttribute("CharacterPartsTouching") + 1) if player.MembershipType == Enum.MembershipType.Premium then -- User has Premium; teleport character to exclusive area within experience teleportPlayer(player) else -- Show purchase modal, using debounce to show once every few seconds at most if not showModal then return end showModal = false task.delay(5, function() showModal = true end) MarketplaceService:PromptPremiumPurchase(player) end end) -- Detect character parts exiting teleporter teleporter.TouchEnded:Connect(function(otherPart) local player = Players:GetPlayerFromCharacter(otherPart.Parent) if player and player:GetAttribute("CharacterPartsTouching") then player:SetAttribute("CharacterPartsTouching", player:GetAttribute("CharacterPartsTouching") - 1) end end) -- Handle membership changed event Players.PlayerMembershipChanged:Connect(function(player) warn("User membership changed; new membership is " .. tostring(player.MembershipType)) -- Teleport character if membership type is Premium and character is on teleporter if player.MembershipType == Enum.MembershipType.Premium and player:GetAttribute("CharacterPartsTouching") > 0 then teleportPlayer(player) end end) ``` --- title: "Immersive ads" url: /docs/en-us/production/monetization/immersive-ads last_updated: 2026-06-29T19:34:05Z description: "Immersive ads allow you insert ad units into your experience that programmatically serve ad content." --- # Immersive ads With the **immersive ads** system, you can insert ad units into your experience that allow Roblox to programmatically serve ad content from advertisers to your active users. The following ad formats can be served in your experience: - **Billboards**, which include: - **Video ads**: A video up to 30 seconds long that users can watch in-experience, which can either be click-to-play or autoplaying. - **Image ads**: A static, non-clickable image within the 3D space. - **Portal ads**: A static, non-clickable image with a door that teleports users into an advertiser's experience. > **Info:** With portal ads, the advertiser's experience must include a button that allows users to return to your experience. _Video ad format (autoplaying)_ _Video ad format (click-to-play)_ _Image ad format_ _Portal ad format_ Ad content is specific to the user, meaning two users might simultaneously see different display ads or teleport to different advertiser experiences from the exact same ad unit. If a user is ineligible to see ads, ad units display either a customizable fallback image or the Roblox logo. To illustrate this concept, see the following three images of the same ad unit showing different content depending on the user. User A sees an ad promoting [The Mystery of Duvall Drive](/docs/en-us/resources/the-mystery-of-duvall-drive.md) experience while User B sees an ad promoting [Beyond the Dark](/docs/en-us/resources/beyond-the-dark.md). User C doesn't see an ad at all from this ad unit because they are ineligible to see ads. _User A_ _User B_ _User C_ ## Publisher earnings from immersive ads If your active users are eligible to see ads and meet the specific criteria from the advertiser, such as their country or device, you might be eligible to earn Robux according to how they interact with the ad content. How publishers earn: - For **video ads that are click-to-play**, users must click on the ad to initiate the video to start playing. Advertisers bid on a "15 second view", so publishers earn whenever a user watches a video for at least 15 seconds. You can implement a reward mechanism with [rewarded video ads](/docs/en-us/production/promotion/rewarded-video-ads.md) to incentivize a user to watch the video for at least 15 seconds to drive earnings. - For **video ads that are autoplay**, the video starts playing when a user looks at the ad and pauses when the user looks away. Advertisers bid on a video impression, so publishers earn for each video impression. A video impression is when a user looks at the ad for at least 0.5 seconds, the ad occupies 1.5% of the viewport, the ad is viewed at an angle of up to 55 degrees, and at least 50% of the video ad pixels are visible. - **Image ads** are static, non-clickable images within the 3D space. Advertisers bid on an image impression, so publishers earn for each image impression. An image impression is when a user looks at the ad for at least 1 second, the ad occupies 1.5% of the viewport, the ad is viewed at an angle of up to 55 degrees, and at least 50% of the image ad pixels are visible. - **Portal ads** are static, non-clickable images with a door that teleports users into an advertiser's experience. Advertisers bid on a teleport, so publishers earn for each successful teleport. A teleport is when a user enters the portal and arrives at the advertiser's experience. To learn more about how advertisers are billed, see [Billing](/docs/en-us/production/promotion/ads-manager.md#billing). Roblox pays out earnings on the 25th of the following month from when you inserted ad units into your experience. For example, if you insert ad units during the month of March, your payout date for the viewable impressions and successful teleports from those ad units is April 25. You can track your collective earnings from ads either through the **My Transactions** or the **Group Transactions** page. You can also analyze their overall performance through metrics graphs on the Creator Dashboard. > **Warning:** Roblox has ad fraud systems in place to create a positive advertiser, publisher, and user experience. Per Roblox's [Advertising Standards](https://en.help.roblox.com/hc/en-us/articles/13722260778260-Advertising-Standards), if you engage in malicious practices to inflate impressions or teleports, Roblox might deduct Robux from your expected payout or reclaim the Robux you earned in a fraudulent way. Roblox might also suspend your experience or your account. ## Publisher eligibility While anyone can insert ad units into their experiences, Roblox only serves ads into ad units if the experience is eligible to serve ads. Eligibility does not guarantee Roblox will serve ads in your experience, and nothing obligates Roblox to serve ads into your experience. For your experience to be eligible to serve ads, your account and experience must meet the following criteria: - Your experience must be **public**. - You must be **13+ years of age**. - For group-owned experiences, this requirement applies to the owner of the group. - Your account must be [ID-verified](/docs/en-us/production/publishing/account-verification.md) and have **2-factor authentication (2FA)** enabled. - For group-owned experiences, this requirement applies to the owner of the group. - This requirement is persistent. If you disable identity verification or 2FA, your experience loses eligibility. - You must complete the **Maturity and Compliance Questionnaire** for your experience. - The questionnaire must be reviewed and approved by Roblox before the experience is eligible to serve ads. If the review status is **Pending**, you can request a re-review from the experience's advertising eligibility page under **Monetization** ⟩ **Ads** ⟩ **Eligiblity**. You'll receive a response within 24 hours. - Your experience must maintain **2,000 unique visitors per month**. - This is calculated and updated monthly, based on visitor data. Bots are not included in visitor counts. - You must comply with the [Roblox terms of use](https://en.help.roblox.com/hc/en-us/articles/115004647846-Roblox-Terms-of-Use), [community standards](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards), and [publisher integrity requirements](https://en.help.roblox.com/hc/en-us/articles/13722260778260-Advertising-Standards#publisher-integrity). > **Info:** Resubmitting your **Maturity and Compliance Questionnaire** will temporarily remove your ads eligibility while the questionnaire is under review. To avoid repeated interruptions, only resubmit the questionnaire when you publish an experience update that changes your previous responses. > **Warning:** If you don't comply with these requirements, you might lose your publisher eligibility temporarily or permanently. In some cases, you might lose some or all ad payouts and might have your content or account suspended. ## User eligibility Even if your experience is eligible to serve ads, not all users are eligible to see ads, such as those outside of a campaign's audience selection. If a user is ineligible to see ads, ad units display a fallback image of the Roblox logo to those ineligible users. To remove these ad units, you can [remove ad units for ineligible users](#remove-ad-units-for-ineligible-users). ## Insert ad units When you insert an ad unit into an eligible experience, make sure to check each image ad surface to confirm whether or not it's valid and able to serve ads. For example, you can scale ad units to different sizes to match your experience's aesthetics, but you can't make them too small or large, otherwise they become invalid and can't serve ads. _This ad unit is valid and can serve ads_ _This ad unit is invalid because it's too small; it cannot serve ads until you scale it to a valid size_ ### Billboards Billboards include video and image ads. ##### Video ads Eligible users can see two types of video ads within Roblox experiences, depending on how the advertiser bids: - A **click-to-play** video ad is served when an advertiser bids on users viewing at least 15 seconds of the ad. Users initiate a video ad by clicking on the ad unit, which prompts the video to play in full-screen with sound on. Publishers are paid each time a video ad is watched for at least 15 seconds. - An **autoplaying** video ad is served when an advertiser bids on users seeing their ad, and starts playing with the sound off when a user looks at it. If they look away, the video pauses. The video ad includes controls to unmute and expand to full screen. Publishers are paid based on the number of impressions the ad receives. To insert a video ad: 1. From the **Home** or **Model** tab toolbar, insert a **Block** part into your experience. 2. Scale the part to at least 8 studs wide and 4.5 studs tall, but no more than 32 studs wide and 18 studs tall. 3. In the **Explorer** window, add an **AdGui** object to the part. To do so, hover over the part and click the **Add** button, then insert an AdGui. 4. In the **Properties** window, with the new AdGui selected, go to the **Face** property and choose a face. You can also keep the default face. 5. Make sure to enable the **EnableVideoAds** checkbox to show video ads. If you leave this checkbox disabled, the part will only show image ads. You can also implement a reward mechanism inside your experience to incentivize users to watch click-to-play video ads with [rewarded video ads](/docs/en-us/production/promotion/rewarded-video-ads.md). ##### Image ads An image ad is a non-clickable static image that serves ads through an `Class.AdGui` instance that you can place on block `Class.Part` instances anywhere within the 3D space of an experience as long as nothing obstructs the view of the ad from users. The aspect ratio of the image scales with the face of the block you choose to display the ad content. In order for the ad unit to be valid and serve ads once you publish the experience, you must ensure it meets the following criteria: - The block is within the `Class.Workspace`. - The block is no smaller than 8 by 4.5 studs, and no larger than 32 by 18 studs. - The block doesn't include another `Class.AdGui` or `Class.SurfaceGui` object on the same face of the ad. To insert an image ad: 1. From the **Home** or **Model** tab toolbar, insert a **Block** part into your experience. 2. Scale the part to at least 8 studs wide and 4.5 studs tall, but no more than 32 studs wide and 18 studs tall. 3. In the **Explorer** window, add an **AdGui** object to the part. To do so, hover over the part and click the **Add** button, then insert an AdGui. 4. In the **Properties** window, go to the **Face** property and choose a face. You can also keep the default face. > **Info:** You can also insert an image ad package from the Creator Store, then customize it to fit your experience as long as the ad unit continues to meet the validation criteria. For more information about how to insert ad units from the Creator Store, see [Portal ads](#portal-ads). After you publish the experience, users can see the ad unit in one of the following states: | State | Description | Example | | --- | --- | --- | | **Active** | The ad unit is able to serve ads, and is currently serving an ad to a user. | | | **Inactive** | The ad unit is able to serve ads, but it isn't currently serving an ad to a user because of one of the following reasons:

Roblox serves a default fallback image instead. | | | **Invalid** | The ad unit isn't able to serve ads because it didn't meet the validation criteria. | | ### Portal ads A portal ad is made up of two core components: - A static, non-clickable image. - A door that teleports users to an advertiser's experience. The Creator Store includes portal ads that represent these core components through a **BasePortal** package. The only changes you can make to this package are to modify its scale, position, and rotation. Any other change you make invalidates the package and makes it unable to serve ads. Portal ads also include a **Decorative** folder of both static and dynamic visual elements. You can customize these elements as long as the core components remain intact. For example, the following two portal ad packages have the exact same core components, but they include different visual elements you can customize to change how these ad units look and feel within your experiences. To insert a portal ad: 1. Open the Creator Store. 1. From Studio's **Window** menu or **Home** tab toolbar, open the [Toolbox](/docs/en-us/projects/assets/toolbox.md). 2. Select the **Creator Store** tab. 2. In the **Categories** section, click the **See All** button. All categories display. 3. Click the **Ads** tile. 4. Click any of the **Portal** template packages. 5. (Optional) Customize the visual elements that surround the ad unit by modifying elements within the package's **Decorative** folder. Once you publish the experience, users can see the ad unit in one of the following states: | State | Description | Example | | --- | --- | --- | | **Active** | The ad unit is able to serve ads, and is currently serving an ad to a user. | | | **Inactive** | The ad unit is able to serve ads, but it isn't currently serving an ad to a user because of one of the following reasons:

Roblox serves a default fallback image instead. | | | **Invalid** | The ad unit isn't able to serve ads because it didn't meet the validation criteria. The block surface that serves ads turns black, and the door becomes a concrete wall. | | ## Remove ad units for ineligible users Per Roblox's [Advertising Standards](https://en.help.roblox.com/hc/en-us/articles/13722260778260-Advertising-Standards), you must either hide, replace, or block ad content from users who are ineligible to see ads. By default, immersive ad units handle this by replacing ads with a fallback image of the Roblox logo for users ineligible to see ads. To hide or remove the ad units entirely, use `Class.PolicyService:GetPolicyInfoForPlayerAsync()|GetPolicyInfoForPlayerAsync()` to return an `AreAdsAllowed` boolean that determines the eligibility of each user who accesses your experience to see ads, then include logic to modify ad visibility to ineligible users. For example, the following code sample uses `Class.PolicyService:GetPolicyInfoForPlayerAsync()|GetPolicyInfoForPlayerAsync()` to check the eligibility of each user to see ads as they enter the experience. If `AreAdsAllowed` is true for a user, portal ads remain visible, but if it's false, the script destroys all of them. While this is a great strategy to remove ad content from users who are ineligible to see ads, it's important to note that destroying ad units might change your experience's gameplay if some users can see ad units while others can't. ```lua local Players = game:GetService("Players") local PolicyService = game:GetService("PolicyService") local Workspace = game:GetService("Workspace") local player = Players.LocalPlayer -- Sample assumes a "Main Portal Template" model exists under Workspace local mainPortal = Workspace:WaitForChild("Main Portal Template") -- Get the policy info for the user local success, result = pcall(PolicyService.GetPolicyInfoForPlayerAsync, PolicyService, player) if success and result then if not result.AreAdsAllowed then -- Destroy the "Main Portal Template" instance on the user's client if ads are not allowed mainPortal:Destroy() end else print("Failed to get policy for player", player.Name, "| Exception:", result) end ``` ## View immersive ad metrics After you insert ad units into your experience, the [Creator Dashboard](https://create.roblox.com/dashboard/creations) generates different types of metrics graphs to help you analyze the overall performance of your ads. After 48 hours of having ads running in your experience, you can see how many video views, impressions, and teleports you're generating through your user base, how each ad unit format is performing, and how many Robux you're earning from individual ad units. By tracking these trends over time, you can make strategic decisions on the number of ad units you include per place, which format of ad unit you want to prioritize, and where you can place individual ad units to generate video views, impressions, and teleports. To view immersive ad metrics: 1. Go to Creations and select an experience. 2. Go to **Monetization** ⟩ **Ads**. > **Info:** Metrics graphs that report impressions and teleports per ad unit use each ad unit's name in Studio. > **Warning:** Roblox might make minor "over-delivery" adjustments to impressions or teleports for advertisers to account for potential invalid video views, impressions, or teleports. This can result in occasional changes in your **Earnings per Mille** or **Earnings per Teleport** metrics.
--- title: "Monetization" url: /docs/en-us/production/monetization last_updated: 2026-06-29T19:34:05Z description: "Monetization is a part of Roblox's overall structure to provide value to creators." --- # Monetization Monetization is a part of Roblox's overall structure to provide value to creators. There are several different approaches to monetize your content, including utilizing [subscriptions](#subscriptions), charging an access fee in [Robux](#paid-access-in-robux) or [local currency](#paid-access-in-local-currency), providing [items or abilities that users can purchase](#developer-products), offering [private servers](#private-servers) for users to play just with friends, and [selling models and plugins](/docs/en-us/creator-store.md#distribute-and-sell-assets) to other creators on the Creator Store. If you choose to offer purchasable content, Roblox doesn't automatically record product or purchase information. To prevent data loss, you must carefully store this data using `Class.DataStoreService` or another data storage service hosted outside of Roblox. ## Monetization strategies Be mindful of the monetization methods you introduce into your experience as our users are likely to **downvote** your experience if they do not approve of your strategies. Also, all developers need to be transparent with any [chance-based monetization](/docs/en-us/production/paid-random-items.md) and use these mechanics responsibly. Some popular monetization strategies for free-to-play experiences on other platforms are not recommended for Roblox. For instance, appointment mechanics and timers which can be removed or brought forward may work well on other platforms, but these mechanics are unpopular with Roblox users who take issue with their fun coming to a premature end. The following best practice suggestions are popular among top developers for tailoring monetization strategies to the Roblox platform and audience. > **Warning:** Please note that by applying these techniques and considerations, you are not guaranteed to be able to effectively monetize your experience. Remember that you don't need to have any financial goals to have fun creating a Roblox experience. ### Tourists and locals It can be helpful to think of Roblox users in two categories: - **Tourists** typically hop from one experience to another, prioritizing variety over depth. They prefer items with immediate effects, either through making gameplay more fun or through making them stand out, inviting other users to question how and where they got the item. - **Locals** are more likely to focus on a particular experience or smaller set of experiences. They engage more deeply, and typically form almost all an experience's engaged user base. They are more interested in items with long term benefits, such as a battle pass. To learn more about how to design your experience with Roblox users in mind, see [Design for Roblox](/docs/en-us/production/game-design/design-for-roblox.md). ### Social features Roblox users tend to enjoy experiences which emphasize social interaction. For instance, having items or events that are only available for a **limited time** can create excitement around that content. A **trading** system encourages users to interact. While both systems can be successful on their own, they are particularly effective when combined. ### Live ops Post-launch support is in many ways easier on Roblox than on other platforms as you can quickly ship updates to improve your experience. **Frequent** content updates can be helpful if you want to maintain engagement with your experience over time. Roblox users are used to being engaged by regular updates, and it can be much harder to regain a user's interest than it can be to maintain active ones. A **weekly cadence** for updates is ideal. At the bare minimum, aim for monthly updates. For a discussion on content cadence, see the Level Up [content cadence workshop](/docs/en-us/production/game-design/content-updates.md). When designing updates, keep them focused on a particular **theme**. This makes it easier for users to understand the new update and tell what content is new when they join. It also helps you coordinate your content and promotional assets. For example, if you're adding a new Zoo to your roleplaying experience, make sure all new content has some connection to the Zoo theme. Don't be afraid to make users earn their **access** to new content. New users will flock to play new updates, but if they don't feel like they've **achieved** something in the experience, they may check out soon after. You can create a sense of earning an update either through monetizing the update content or creating pre-requisite conditions for playing the new content. To learn more about how to design effective post-launch support for your experience, see [Live ops essentials](/docs/en-us/production/game-design/liveops-essentials.md). ## Products ### Immersive ads The [immersive ads](/docs/en-us/production/monetization/immersive-ads.md) system allows you to insert ad units into your experience that permit Roblox to programmatically serve ad content from advertisers to your active users. _Image ad format_ _Portal ad format_ ### Roblox Plus [Roblox Plus](/docs/en-us/production/roblox-plus.md) lets you earn through increased in-experience purchases driven by Roblox-subsidized user discounts, sign-up bonuses for each new subscriber you bring in, and engagement in paid private servers. ### Subscriptions [Subscriptions](/docs/en-us/production/monetization/subscriptions.md) within experiences let you offer users recurring benefits for a monthly fee. _Examples of experience subscriptions_ ### Passes A [pass](/docs/en-us/production/monetization/passes.md) allows you to charge a **one-time Robux fee** in order for users to access special privileges within an experience, such as entry to a restricted area, an in-experience avatar item, or a permanent power-up. ### Developer Products A [developer product](/docs/en-us/production/monetization/developer-products.md) is an item or ability that a user can purchase more than once, such as in-experience currency, ammo, or potions. You can prompt purchases, record them, and get product information through scripting. ### Managed pricing [Managed pricing](/docs/en-us/production/monetization/managed-pricing.md) is a unified system that brings [regional pricing](/docs/en-us/production/monetization/regional-pricing.md) and [price optimization](/docs/en-us/production/monetization/price-optimization.md) together in one place. You opt in once, choose which items to include, and Managed pricing keeps your prices optimized across products and regions over time. ### Price optimization [Price optimization](/docs/en-us/production/monetization/price-optimization.md) lets you find the best price points for your passes and developer products, which can help you earn more money over time while keeping your prices competitive. ### Paid access in Robux [Paid access in Robux](/docs/en-us/production/monetization/paid-access-robux.md) allows you to charge users a **one-time Robux fee** to access your experience. Some developers temporarily use this feature to create a closed beta where their most engaged users can have early access to an experience and help with testing and feedback. ### Paid access in local currency [Paid access in local currency](/docs/en-us/production/monetization/paid-access-local-currency.md) allows you to charge users a **one-time fee in their local currency** to access your experience. If their local currency isn't available, they're charged in USD. Some developers temporarily use this feature to create a closed beta where their most engaged users can have early access to an experience and help with testing and feedback. ### Private servers A [private server](/docs/en-us/production/monetization/private-servers.md) is a subscription-based feature that allows a user to decide who can play an experience with them. While private servers can be free, you can also use private servers as a method of monetization by charging users who want to access private servers a **monthly Robux fee**. Private servers are often purchased for: - Playing experiences just with friends. - Holding gatherings such as classes, meetings, or parties. - Recording and/or streaming without other users. - Gathering in-experience resources. ### Catalog fees and commissions You can create and sell accessories and clothes on the Marketplace. After you pay the upload fee and submit a new asset for approval, the moderation team reviews your asset and, if approved, adds your asset to the catalog. You'll receive a [commission](/docs/en-us/marketplace/marketplace-fees-and-commissions.md) every time users purchase your catalog item. If users purchase your catalog item within an experience using the [avatar inspect menu](/docs/en-us/players/avatar-inspect-menu.md) or the [avatar editor service](/docs/en-us/players/avatar-editor.md), the experience owner also receives a commission. ### Plugins and Models A [plugin](/docs/en-us/studio/plugins.md) is an extension that adds additional functionality to Studio, and a [model](/docs/en-us/parts/models.md) is a reusable asset type. You can offer both to other creators on the [Creator Store](/docs/en-us/production/creator-store.md) for free, or you can sell them for **United States Dollars** (the minimum price is $4.99 for plugins and $2.99 for models). Roblox offers a market-leading revenue share for these sales, as only taxes and payment processing fees are deducted. For more information on selling plugins and models, see [Creator Store - Distribute and sell assets](/docs/en-us/creator-store.md#distribute-and-sell-assets). > **Warning:** There is a 30 day escrow hold for each purchase. Roblox holds your share of the sale for 30 days, starting from the date of sale. ## Guidelines As you design and implement monetization products into your experiences, consider the following guidelines to ensure your monetization methods are suitable for all audiences on the platform. ### Presenting products To maintain safety and fairness, you must present monetization products in a way that's transparent, honest, and user-friendly. For example, if you offer discounts, they must be **genuine** and **fair**: - A discount is not genuine if an item is always "on sale" for the same amount of Robux. - A discount is not fair if it's only offered for a very short time, pressuring users to make a purchase as quickly as possible. Users must never be misled, confused, or pressured into purchases in ways they did not intend, and they should always have the freedom to make a clear, deliberate choice to obtain a subscription or spend Robux on a specific item. As general guidance, there are some practices to avoid at all times when presenting monetization products to your audience: - Don't claim that a subscription or item is almost out of stock or only available for a short time if it isn't true. - Don't use a countdown timer that isn't accurate or automatically restarts for the same item. For example, if your game includes a "deal of the day" that says it's only available for 24 hours, but then you show the same message to users every time they login and it never stops being available, you have created a false sense of urgency that isn't user-friendly. ### Promoting to global audiences Roblox is a global community for users of all ages, so promotions within your experiences should be suitable for all audiences by default. If certain monetization products are restricted in certain locations, such as paid random items, Roblox provides `Class.PolicyService` API to help you ensure that those products are only visible to eligible users. `Class.PolicyService` returns whether a user is eligible for specific items or features according to their location, age, and platform. You must integrate this API into your game to meet your compliance obligations, including determining whether each user is: - Eligible to purchase subscriptions or commerce products. - Allowed paid random items, paid item trading, or to see immersive ads. > **Warning:** If a user can use Robux or something else that was acquired with Robux to get a random reward, you are required to use `Class.PolicyService` to block this feature for users who are not permitted to use random items in exchange for Robux. As general guidance, avoid using pushy or urgent messages when interacting with minors, as high-pressure language can be overwhelming: | **Language to avoid** | **Recommended alternatives** | | --- | --- | | "GET IT NOW" | "View Item" | | "LAST CHANCE, ACT NOW" | "See Price" | | "BUY BEFORE IT’S GONE!" | "Open Shop" | --- title: "Managed pricing" url: /docs/en-us/production/monetization/managed-pricing last_updated: 2026-06-29T19:34:05Z description: "Managed pricing brings regional pricing and price optimization into one unified system in Creator Hub." --- # Managed pricing **Managed pricing** is a unified system in Creator Hub that combines [price optimization](/docs/en-us/production/monetization/price-optimization.md) and [regional pricing](/docs/en-us/production/monetization/regional-pricing.md) under a single opt-in. Price optimization helps you find the right price points for your passes and developer products. Regional pricing helps you offer accessible prices to users across different economic regions. From the **Managed Pricing** tab, you can: - [Opt in](#opt-in-and-enroll-items) through a one-time flow for existing experiences. - Review eligible items and choose what to include for Managed Pricing. - Opt specific items in or out of automation. - Use bulk actions to enable or disable multiple items at once. - View past tests, upcoming tests, and revenue impact in one view. You can also reschedule tests. ## Opt in and enroll items > **Info:** After initial opt-in, **Managed Pricing** replaces the **Price Optimization** tab in your Creator Dashboard. Managed pricing lives in the **Managed Pricing** tab under **Monetization** in Creator Hub. To enroll an experience: 1. Go to [Creations](https://create.roblox.com/dashboard/creations) and select an experience. 2. Go to **Monetization** ⟩ **Managed Pricing**. 3. Complete the one-time opt-in flow. If managed pricing detects hard-coded prices in your experience, you're prompted to fix them first. 4. Review eligible items and choose what to include. Use bulk actions to enable or disable multiple items at once, and opt specific items in or out of automation at any time. After opt-in, the **Managed Pricing** tab shows past tests, upcoming tests, and reports for completed tests. ### Item eligibility and defaults > **Warning:** If you are already using regional pricing, your existing regional pricing items transition to managed pricing automatically. - **New experiences and newly created items** are enrolled in Managed pricing by default. Item-level opt-out is available at any time. - **Subscriptions, Private Servers, and Developer Servers** are regionalized automatically and don't require opt-in. Subscriptions are not included in price optimization tests. ## Price optimization Managed pricing uses [price optimization](/docs/en-us/production/monetization/price-optimization.md) to identify better prices for passes and developer products based on signals from your own experience, including demand, conversion, spend, and your experience's spender penetration in specific countries. Items below the experiment-eligibility threshold for price optimization tests are unaffected. For details on [how tests work](/docs/en-us/production/monetization/price-optimization.md#how-tests-work), the dynamic price check tool, the price review period, and the metric glossary, see [price optimization](/docs/en-us/production/monetization/price-optimization.md). ## Regional pricing Managed pricing uses [regional pricing](/docs/en-us/production/monetization/regional-pricing.md) to adjust prices based on a user's economic location, using signals like purchasing power, currency exchange rates, and local spending behavior. Regional prices are bound between 30% and 100% of the default price. For details on how economic location is determined, displaying regional prices in your experience, and protecting in-experience trades and gifts with `Class.MarketplaceService.GetUsersPriceLevelsAsync|GetUsersPriceLevelsAsync`, see [regional pricing](/docs/en-us/production/monetization/regional-pricing.md). --- title: "Paid access in local currency" url: /docs/en-us/production/monetization/paid-access-local-currency last_updated: 2026-06-29T19:34:05Z description: "Paid access lets you charge users a one-time fee in their local currency to access your experience." --- # Paid access in local currency **Paid access** lets you charge users a one-time fee in their local currency to access an experience. If their local currency isn't available, they're charged in USD. Users who purchase access continue to have access to the experience even if you increase the price later or disable paid access. > **Info:** Paid access can limit the number of users who join your experience, as some users might not want to pay a fee for access. > **Info:** Some developers temporarily enable paid access to create a closed beta so that their most engaged users can have early access to an experience to help with testing and feedback. If you choose to do this, make sure to let users know that they're purchasing a beta version of your experience. > **Warning:** Users can only purchase paid access in local currency on desktop and on web, but they can play their purchased experiences on any of the devices you [enable in the Creator Hub](#enable-paid-access-in-the-creator-hub). ## Eligibility To charge users for paid access in local currency, you must: - Have a verified email address. - Be 13 years of age or older with a [verified ID](/docs/en-us/production/monetization/../publishing/account-verification.md#verify-through-government-id). - Successfully [set up an account with Roblox's third-party payment provider Tipalti](#set-up-your-account-to-receive-payouts). - Reside in a country supported by Tipalti. See [Payment methods coverage](https://support.tipalti.com/Content/Topics/UserGuide/PaymentInformation/PaymentMethodsCoverage/IntroUSD.htm) for a list of eligible countries. > **Warning:** Users residing in Argentina, China, Colombia, India, Indonesia, Russia, Taiwan, Turkey, UAE, Ukraine, or Vietnam are not able to purchase paid access experiences in local currency. > **Info:** For paid access experiences that are owned by groups, these eligibility requirements only apply to the group owner. ## Pricing When you charge for paid access in local currency, you earn a percentage of the price you set, called a **developer revenue share**. For example, if you set the price to $49.99 USD, you receive 70% of the proceeds, minus certain fees like taxes and VAT. The revenue share follows three price points: | Computer & web price1 | Developer share | | --- | --- | | $9.99 | 50% | | $29.99 | 60% | | $49.99 | 70% | 1. Purchased on desktop in local currency. You can change your revenue share price point, switch to paid access in Robux, or make your experience free to play any time using the Creator Hub. ## Refunds Users have 48 hours to request a refund after purchasing a paid access experience in their local currency. After they receive their refund, they no longer have access to the paid experience. Refunds aren't available after 48 hours. ## Set up your account to receive payouts You must set up a Tipalti account to receive payouts for purchases of paid access in local currency. To set up a Tipalti account: 1. In the Creator Hub, go to **Creations** and select an experience. 2. Go to **Audience** ⟩ **Access Settings**. 3. Under **Payment**, select **Send Request**. 4. Enter your legal first and last name. Within 5 to 8 business days of submitting your payment information, Tipalti sends you an email with instructions on how to create an account and submit your tax and banking information. Tipalti then verifies your documents and confirms your account has been successfully set up. Local currency earnings from paid access purchases are held in escrow for a minimum of 60 days, and paid out and deposited into your account once a month. You can't access your earnings outside of the payout schedule. > **Info:** For experiences owned by groups, the group owner must be the one to set up a Tipalti account. Only group owners can receive payouts. ## Enable paid access in the Creator Hub To enable paid access in the Creator Hub: 1. Go to **Creations** and select an experience. 2. Go to **Audience** ⟩ **Access Settings**. 3. Under **Payment**, select **Requires Local Currency** and then select one of the available revenue share tiers. 4. Under **Devices**, select which devices you want to enable paid access for. Desktop devices are automatically enabled. 5. Click **Save Changes**. > **Info:** Before becoming available to users, your paid access experience must undergo a policy moderation review. You'll receive an email and a notification in the Creator Hub with a decision after the moderation team has reviewed your experience. ## Enable paid access in Roblox Studio To enable paid access in Roblox Studio: 1. Open Studio's **File** ⟩ **Experience Settings** window. 2. Under **Monetization**, click **Configure on Creator Hub**. 3. Follow the steps to [enable paid access in the Creator Hub](#enable-paid-access-in-the-creator-hub). ## View transaction history To view your transaction history for local currency paid access purchases: 1. In the Creator Hub, go to **Finances** ⟩ **Transactions**. 2. Select **Paid access in local currency**. The transaction table displays the date of the transaction, the user who made the purchase, the experience name, how much the user paid, and the user's refund status. ## Policies Paid access experiences in local currency must: - Follow the [Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410) and [Terms of Use](https://en.help.roblox.com/hc/articles/115004647846). - Be public and available to all users. - Provide buyers with access to the entire experience. - Not give buyers access to content or items from other experiences. - Not have [private servers](/docs/en-us/production/monetization/private-servers.md). - Not be spam, contain scams, repeat existing experiences, or be an empty experience. - Not require users to pay Robux to access additional parts of the experience. Users must be able to access the entire experience they have purchased. Microtransactions for items inside the experience, like passes or developer products, are allowed. - Not allow users to download copies of any places within the experience. Copying an experience is automatically disabled when you enable paid access. - Run properly and be playable. If a large number of users reports that an experience is broken, the experience might be quarantined and earnings still in escrow refunded to users. Violating these policies can result in loss of access to the paid access feature, removal of content, or suspension of your Roblox account. --- title: "Paid access in Robux" url: /docs/en-us/production/monetization/paid-access-robux last_updated: 2026-06-29T19:34:05Z description: "Paid access lets you charge users a one-time fee in Robux to access your experience." --- # Paid access in Robux **Paid access** lets you charge users a one-time fee in Robux to access an experience on desktop, mobile, VR, and most gaming consoles. Users who purchase access continue to have access to the experience even if you increase the price or disable paid access. > **Info:** Paid access can limit the number of users who join your experience, as some users might not want to pay a fee for access. > **Info:** Some developers temporarily enable paid access to create a closed beta so that their most engaged users can have early access to an experience to help with testing and feedback. If you choose to do this, make sure to let users know that they're purchasing a beta version of your experience. > **Warning:** Experiences with paid access are not available on Xbox. ## Eligibility To charge users for paid access in Robux, you must: - Have a verified email address. - Have an account that is at least 30 days old. ## Pricing You can charge users anywhere from 25 to 1000 Robux for paid access to your experience. ## Payouts Payouts for purchases of paid access in Robux are held in escrow for up to 7 days before they're paid out and deposited into the bank linked to your Roblox account. ## Refunds Refunds are not available for users who purchase paid access in Robux. ## Enable paid access in the Creator Hub To enable paid access in the Creator Hub: 1. Go to **Creations** and select an experience. 2. Go to **Audience** ⟩ **Access Settings**. 3. Under **Payment**, select **Requires Robux** and enter the amount of Robux you want users to pay for access. The price you enter affects the amount of Robux you earn per sale. 4. Under **Devices**, select which devices you want to enable paid access for. 5. Click **Save Changes**. ## Enable paid access in Roblox Studio To enable paid access in Roblox Studio: 1. Open Studio's **File** ⟩ **Experience Settings** window. 2. Under **Monetization**, click **Configure on Creator Hub**. 3. Follow the steps to [enable paid access in the Creator Hub](#enable-paid-access-in-the-creator-hub). ## Policies Robux paid access experiences must: - Follow the [Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410) and [Terms of Use](https://en.help.roblox.com/hc/articles/115004647846). - Be public and available to all users. - Not have [private servers](/docs/en-us/production/monetization/private-servers.md). - Not allow users to download copies of any places within the experience. Copying an experience is automatically disabled when you enable paid access. - Run properly and be playable. If a large number of users reports that an experience is broken, the experience might be quarantined and earnings still in escrow refunded to users. --- title: "Paid random items policy guidelines" url: /docs/en-us/production/monetization/paid-random-items last_updated: 2026-06-29T19:34:05Z description: "Summarizes Roblox's policy for offering paid random items in a game." --- # Paid random items policy guidelines Due to regulations in various countries, certain requirements may apply to random items purchased directly or indirectly with Robux. This includes random items purchased with paid in-experience currency. For example, both of the following are paid random items: - Paying Robux to spin a prize wheel - Paying Robux to buy gems or spin tickets that are spent at a prize wheel There are a variety of item types that are paid random items, including but not limited to: | **Item type** | **Examples** | | --- | --- | | **Capsule items** | | | **Enhancement items** | | | **Combination items** | | | **Probability modifier items** | | ## Item odds disclosures Under Roblox's [Terms of Use](https://en.help.roblox.com/hc/en-us/articles/115004647846-Roblox-Terms-of-Use) and [Community Standards](https://about.roblox.com/community-standards#roblox-economy-paid-random-items), if you offer paid random items that users can purchase with Robux or other in-experience currency purchasable with Robux, you must indicate all possible outcomes and the actual numerical odds of what they may receive. ![An example prize wheel with numerical odds of what users may receive.](../../assets/policy/paid-random-items/Prize-wheel.png) In addition: - If there are too many individual outcomes to fit in the user's dynamic 3D view, such as rarity categories that have many individual items in each category, the itemized list of final outcomes and their numerical possibilities can be shown in a clickable pop-up with an "Info" or "Details" icon visible and accessible prior to purchase. Using a standalone symbol like the (i) icon without a descriptive word is **not** sufficient. - Odds must be displayed as a probability percentage, and the probability percentages of all final outcomes must sum to exactly 100%. For probabilities with long decimals, you can round to four or more decimal places lower than the decimal place with the first non-zero number, and include a disclaimer, such as: "Probabilities are rounded; total of displayed individual probabilities may not equal 100%." - If many items within a list have the same individual odds, it is acceptable to disclose at the beginning of the list of items "Odds for each item listed below: X%" - If users can only obtain one instance of an outcome, the odds for the remaining obtainable outcomes must be updated to reflect up-to-date remaining odds for that user. _Example of odds by rarity tier with clickable "Details" button_ _Example of individual item odds disclosure_ > **Warning:** This policy also applies to indirect purchases. For example, if a user uses their Robux to purchase keys to open boxes, re-roll tokens, or spin tickets, you must disclose the odds of their potential award before they spend the in-game currency on the random outcome. Paid items that augment the odds of other random outcomes must also be explained before purchase. For example, if a user can purchase a "lucky potion" or "magic fishing rod" that improves the odds of more unlikely outcomes, these impacts must be numerically explained. The new odds of the enhanced random items must also be dynamically updated when these items are active to show the user's true odds. > **Info:** If your experience offers randomized virtual rewards in exchange for completing an action that does not involve the payment of Robux or other in-experience currency, you aren't required to disclose the odds. For example, if a user locates a key that opens a treasure chest, you don't need to state the odds of what items they might receive from the treasure chest. ## Per-user regulations To help you determine how to handle virtual items in your experience per user, use the `Class.PolicyService.GetPolicyInfoForPlayerAsync|PolicyService:GetPolicyInfoForPlayerAsync()` method. Specifically, the structure of the returned dictionary includes `ArePaidRandomItemsRestricted` and `IsPaidItemTradingAllowed`. ### ArePaidRandomItemsRestricted When `ArePaidRandomItemsRestricted` is **true**, the user cannot interact with paid random item generators, either through Robux directly or in‑experience currency bought with Robux. For ineligible users defined by the API, creators must apply one of these treatments: | **Treatment** | **Examples** | | --- | --- | | **Offering an unpaid, earnable path to acquiring the random item** | Mystery eggs are granted for free at milestones of gameplay | | **Offering the item's outcomes in a pre-determined, non-random order disclosed to the user before purchase** | First spin is always 5 gems, second is always 10 gems, third is always 20 gems, etc. | | **Offering the specific outcomes for direct, guaranteed purchase (priced based on the expected value of the outcome)** | If a lootbox for a rare sword is 10 Robux with 5% odds, offer the sword to ineligible users for direct purchase priced at 200 Robux | | **Removing or hiding the paid random item from the experience** | Replace a giant spin prize wheel with a shop offering direct purchases | | **Blocking the purchase of the paid random item with an error message** | Replacing the purchase button with "Sorry, this item is unavailable for users in your region" | | **Removing the user from the parts of the experience with paid random items** | Teleporting them away | ### IsPaidItemTradingAllowed When `IsPaidItemTradingAllowed` is **true**, the user can trade virtual items that they purchased with in‑experience currency or Robux. For ineligible users defined by the API, creators must not allow them to access the ability to trade the resulting outcome of a paid random item or other paid items. --- title: "Passes" url: /docs/en-us/production/monetization/passes last_updated: 2026-06-29T19:34:05Z description: "Passes let you charge users a one-time Robux fee to access privileges inside your game." --- # Passes **Passes** let you charge users a one-time Robux fee to access special privileges inside your game, such as entry to a restricted area, an in-game avatar item, or a permanent power-up. > **Info:** For items that a player might purchase multiple times, such as potions, temporary power-ups, or in-game currency, see [Developer products](/docs/en-us/production/monetization/developer-products.md). ## Create a pass > **Warning:** Before creating a pass, make sure your game has been [published](/docs/en-us/production/publishing/publish-games-and-places.md) and is accessible on Roblox. To create a pass: 1. Go to [Creations](https://create.roblox.com/dashboard/creations) and select a game. 2. Go to **Monetization** ⟩ **Passes**. 3. Click **Create a Pass**. 4. Upload an image to display as the pass icon. Make sure the image doesn't exceed 512x512 pixels, doesn't include important details outside of its circular boundaries, and is in `.jpg`, `.png`, or `.bmp` format. 5. Enter a name and a description for the pass. 6. Click **Create Pass**. ![Good circular trimming](../../assets/monetization/passes/Circular-Trimming-Good.png)_> **Success:** Includes relevant content_ ![Bad circular trimming](../../assets/monetization/passes/Circular-Trimming-Bad.png)_> **Error:** Crops important content_ > **Info:** If you want to use the pass as a randomized reward, review the [Randomized Virtual Item Policy](/docs/en-us/production/monetization/paid-random-items.md). ## Get the pass ID To use scripting, you need a pass ID. To get the pass ID: 1. Go to **Monetization** ⟩ **Passes**. 2. Hover over a pass' thumbnail, click the **⋯** button, and select **Copy Asset ID** from the context menu. ## Sell a pass > **Warning:** Starting May 30, 2026, cross-experience game pass sales will be disabled. If your game relies on cross-experience sales mechanics, we recommend integrating [Robux transfers](/docs/en-us/production/monetization/robux-transfers.md) before this date to avoid service interruptions. > **Info:** If you're using [price optimization](/docs/en-us/production/monetization/price-optimization.md), make sure to place the script inside a `Class.LocalScript` so that users see personalized pass prices. You can sell passes in two ways: - [Inside your game](#inside-your-experience) - [Outside your game](#outside-your-experience) ### Inside your experience To implement and sell a pass inside an experience, call `Class.MarketplaceService|MarketplaceService` functions. Use `Class.MarketplaceService:GetProductInfo()|GetProductInfo()` to retrieve information about a pass, like name and price, and then to display that pass to users. You can sell the pass inside your game's marketplace, for example. For passes, the second parameter must be `Enum.InfoType.GamePass`. ```lua local MarketplaceService = game:GetService("MarketplaceService") -- Replace the placeholder ID with your pass ID local productId = 000000 local success, productInfo = pcall(function() return MarketplaceService:GetProductInfo(productId, Enum.InfoType.GamePass) end) if success and productInfo then -- Check if product is for sale if productInfo.IsForSale then -- Display product information -- Replace the print statements with UI code to display the pass print("Pass Name: " .. productInfo.Name) print("Price in Robux: " .. productInfo.PriceInRobux) print("Description: " .. productInfo.Description) else print("This product isn't for sale") end end ``` Use `Class.MarketplaceService:PromptGamePassPurchase()|PromptGamePassPurchase()` to prompt a pass purchase if the user doesn't already have the pass in their inventory. You can call this function when a user performs actions like pressing a button or talking to a vendor NPC. ```lua local MarketplaceService = game:GetService("MarketplaceService") local Players = game:GetService("Players") -- Replace the placeholder ID with your pass ID local passID = 0000000 -- Prompt pass purchase local function promptPurchase() local player = Players.LocalPlayer local hasPass = false local success, message = pcall(function() hasPass = MarketplaceService:UserOwnsGamePassAsync(player.UserId, passID) end) if not success then warn("Error while checking if player has pass: " .. tostring(message)) return end if hasPass then -- Show a message telling user they already own the pass else -- Prompt pass purchase MarketplaceService:PromptGamePassPurchase(player, passID) end end ``` Use `Class.MarketplaceService.PromptGamePassPurchaseFinished|PromptGamePassPurchaseFinished` to handle a completed pass prompt and purchase and assign the user the privileges associated with the pass. Place the script inside the `Class.ServerScriptService` so that the server handles the user's pass privileges. ```lua local MarketplaceService = game:GetService("MarketplaceService") -- Replace the placeholder ID with your pass ID local passID = 0000000 -- Change this to your Pass ID -- Handle a completed prompt and purchase local function onPromptPurchaseFinished(player, purchasedPassID, purchaseSuccess) if purchaseSuccess and purchasedPassID == passID then print(player.Name .. " purchased the Pass with ID " .. passID) -- Assign the user the ability or bonus related to the pass end end -- Connect PromptGamePassPurchaseFinished events to the function MarketplaceService.PromptGamePassPurchaseFinished:Connect(onPromptPurchaseFinished) ``` > **Warning:** Although Roblox itself does **not** record the purchase history of passes by specific users, you can request to [download sales data](/docs/en-us/production/analytics/analytics-dashboard.md#sales-data). If you want to track user-specific purchase history, it's your responsibility to [store the data](/docs/en-us/cloud-services/data-stores.md). ### Outside your experience To sell a pass on the **Store** tab of the game details page: 1. Go to **Monetization** ⟩ **Passes**. 2. Hover over the pass and click the **⋯** menu. 3. Select the pass you want to sell. 4. Select **Sales**. 5. Enable to **Item for Sale** toggle. 6. In the **Price in Robux** field, enter the amount of Robux you want to charge users for the pass. The price you enter affects how much Robux you earn per sale. The minimum price is 1 Robux, and the maximum price is 1 billion Robux. 7. Click **Save Changes**. The pass populates in the **Store** tab of the game details page. ## Assign pass privileges You must manually assign pass privileges to users that purchase your passes. To do this, use `Class.Players.PlayerAdded|PlayerAdded` when a user joins your game to check if they already own the pass and to assign them the pass privileges. Place the script inside the `Class.ServerScriptService` so that the server handles the user's pass privileges. ```lua local MarketplaceService = game:GetService("MarketplaceService") local Players = game:GetService("Players") -- Replace the placeholder ID with your pass ID local passID = 0000000 local function onPlayerAdded(player) local hasPass = false -- Check if user already owns the pass local success, message = pcall(function() hasPass = MarketplaceService:UserOwnsGamePassAsync(player.UserId, passID) end) if not success then -- Issue a warning and exit the function warn("Error while checking if player has pass: " .. tostring(message)) return end if hasPass then -- Assign user the ability or bonus related to the pass print(player.Name .. " owns the Pass with ID " .. passID) end end -- Connect PlayerAdded events to the function Players.PlayerAdded:Connect(onPlayerAdded) ``` ## Personalize your in-experience store You can use product intelligence APIs to sort and recommend passes to users. Personalizing your in-game store helps surface the most relevant items to each user, boosting engagement and revenue. By tailoring passes to user preferences, you can improve their discovery, increase conversion rates, and unlock new monetization opportunities. ### Rank passes for sale `Class.MarketplaceService.RankProductsAsync|RankProductsAsync` takes in a list of product IDs and returns a personalized ordered list of those products. You can use this method to provide your users with personalized item recommendations in your in-game store. > **Warning:** Because `RankProductsAsync` has a strict rate limit, you should load recommendations once at game join instead of calling it repeatedly. _Example: Three "Power" items ranked for the user_![Three items ranked for the user.](../../assets/monetization/developer-products/RankedItems.png) ```lua local MarketplaceService = game:GetService("MarketplaceService") -- Create the array of products you want to rank local productIdentifiers = { {InfoType = Enum.InfoType.GamePass, Id = 123}, {InfoType = Enum.InfoType.Product, Id = 456}, {InfoType = Enum.InfoType.Product, Id = 789} } -- Optional: Wrap the call in an async task so that if the request is slow, -- it doesn’t freeze the UI / game task.spawn(function() -- Use a pcall to handle errors gracefully local success, result = pcall(function() return MarketplaceService:RankProductsAsync(productIdentifiers) end) if not success then -- If failed, 'result' is the error message. warn("Failed to rank products:", result) end -- Load the returned items into the store. for i, rankedItem in ipairs(rankedProducts) do local productIdentifier = rankedItem.ProductIdentifier local productInfo = rankedItem.ProductInfo -- ... -- Logic to add productInfo into store end end) ``` ### Display your top passes > **Warning:** If your game has had no item sales in the past 28 days, `RecommendTopProductsAsync` returns an empty list. At least one purchase within the last 28 days is required for this API to generate recommendations. `Class.MarketplaceService.RecommendTopProductsAsync|RecommendTopProductsAsync` takes an array of `Enum.InfoType|InfoType` values and returns up to 50 items a user is most likely to engage with and purchase. You can use this method to create a "Top Picks" section in your in-game store. If no recommendations can be determined, `RecommendTopProductsAsync` returns 0 items. Any passes that the user already owns are also not returned. In rare cases, calls to the ranking model can be slow. To help prevent added lately for users, we recommend using `task.spawn` to make the call to `RecommendTopProducts` non-blocking. _Example: A "Top Picks" tab in an in-game store_![Top Picks tab of an in-game store.](../../assets/monetization/developer-products/StoreTopPicks.png) ```lua local MarketplaceService = game:GetService("MarketplaceService") -- Create an array of product types to include. In this case both game passes and developer products local productTypes = {Enum.InfoType.GamePass, Enum.InfoType.Product} -- Optional: Wrap the call in an async task so that if the request is slow, -- it doesn’t freeze the UI / game task.spawn(function() -- Use a pcall to handle errors gracefully local success, result = pcall(function() return MarketplaceService:RecommendTopProductsAsync(productTypes) end) if not success then -- If failed, 'result' is the error message. warn("Failed to rank products:", result) end -- Load the returned items into the store. Make sure to filter out any -- ineligible items from topRankedItems such as developer products the -- user can no longer purchase for i, rankedItem in ipairs(rankedProducts) do local productIdentifier = rankedItem.ProductIdentifier local productInfo = rankedItem.ProductInfo -- ... -- Logic to add productInfo into store end end) ``` ## Promote a pass > **Info:** Promoted passes are given to users for free. To help more users discover your game, you can opt-in to have your passes featured on Roblox's [Buy Robux](https://www.roblox.com/upgrades/robux) page. Opting-in to promote your passes can help increase the time and Robux users spend in your game. There is no limit to the number of passes you can promote. Users are shown passes that are relevant to their play history and engagement on the platform. They are not shown passes that they already own. When users purchase an eligible Robux package, they receive the promoted pass and are prompted to claim their pass by joining your game. > **Info:** For best results, we recommend that you create a unique pass specifically for the **Buy Robux** page. To opt-in to promote one of your passes: 1. Go to **Monetization** ⟩ **Passes**. 2. Select the pass you want to promote. 3. Select **Promotions**. 4. Enable **Include this item in the bonus pool**. 5. Click **Save Changes**. The pass is shown to relevant users in the **Buy Robux** page. The promoted pass: - Can be off-sale or on-sale. If they're on-sale, their Robux value must be greater than 49 Robux and less than 801 Robux. - Must include a thumbnail. - Cannot grant [paid random items](/docs/en-us/production/monetization/paid-random-items.md). - Must comply with the [Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards). ## Pass analytics Use pass analytics to analyze the success of individual passes, identify trends, and forecast potential future earnings. With analytics, you can: - View your top passes over a selected time period. - Showcase up to eight top-selling items on a time-series graph to analyze overall sales and net revenue. - Monitor your catalog and sort items by sales and net revenue. - View how many passes were acquired by users through promotion on the **Buy Robux** page. - View how many users joined your game after acquiring the pass through the **Buy Robux** page. To access pass analytics: 1. Go to [Creations](https://create.roblox.com/dashboard/creations) and select a game. 2. Go to **Monetization** ⟩ **Passes**. 3. Select the **Analytics** tab. --- title: "Price optimization" url: /docs/en-us/production/monetization/price-optimization last_updated: 2026-06-29T19:34:05Z description: "Price optimization finds the best price points for your passes and developer products, helping you earn more money over time." --- # Price optimization > **Info:** Price optimization is part of [Managed pricing](/docs/en-us/production/monetization/managed-pricing.md), the unified system that keeps your prices optimized across products and regions. > **Warning:** To run a successful price optimization test, your experience has to have enough transactions to produce significant data. In most cases, this means your experience should have had at least 60,000 transactions over the previous 30 days. > > To find how many transactions your experience has had, add the total number of product sales over a 30-day period. For more information, see [Pass analytics](/docs/en-us/production/monetization/passes.md#pass-analytics) and [Developer Product analytics](/docs/en-us/production/monetization/developer-products.md#developer-product-analytics). **Price optimization**, included in Managed Pricing, lets you find the best price points for your passes and developer products, which can help you earn more money over time while keeping your prices competitive. When you run a price test, subsets of your users see different prices for the same product. At the end of the test, you receive recommendations for the prices that performed best with your audience. Before using price optimization, you should use the [dynamic price check tool](#check-for-dynamic-pricing) to make sure product prices are dynamically scripted inside your experience. After using price optimization, you can run a price review period to track the long-term revenue impact of your price changes. ## How tests work - Your users are automatically split into several test groups. The system is optimized to choose the smallest cohort of users to run the test. - Each test group sees a different price while Roblox measures the conversion rate across groups. - After the test, optimized prices are applied only if it results in positive incremental total revenue. - You can review the results and impact in reporting. - Tests run automatically at least every 90 days, or sooner when prices may no longer be optimized. ## Check for dynamic pricing Price optimization can't collect data from and make changes to prices you have hard-coded into your experience. To run a price optimization test on products with hard-coded prices, you must first update them to be dynamically scripted. Dynamically scripted prices update through `Class.MarketplaceService|MarketplaceService` and use functions like `Class.MarketplaceService:GetProductInfo()|GetProductInfo()` and `Class.MarketplaceService:GetDeveloperProductsAsync()|GetDeveloperProductsAsync()` to retrieve and display product prices you have set through the Creator Hub. For information on how to dynamically script product prices, see [Sell a pass](/docs/en-us/production/monetization/passes.md#sell-a-pass) and [Sell a developer product](/docs/en-us/production/monetization/developer-products.md#sell-a-developer-product). The dynamic price check tool updates all products for sale with a fake Robux price or a fake economic location to identify which of your product prices are hard-coded and which are scripted with `Class.MarketplaceService|MarketplaceService` inside your experience. If a product price updates to a new price, the price is scripted. If it remains the same, the price is hard-coded. > **Info:** The `InfoType` parameter in `GetProductInfo()` fetches an asset by default. To fetch a developer product, use `Enum.InfoType.Product`. To fetch a pass, use `Enum.InfoType.GamePasses`. ```lua local productInfo = MarketplaceService:GetProductInfo(PRODUCT_ID, Enum.InfoType.Product) local price = productInfo.PriceInRobux ``` ```lua local priceInRobux = 500 ``` To use the dynamic price check tool: 1. Go to [Creations](https://create.roblox.com/dashboard/creations) and select an experience. 2. Go to **Monetization** ⟩ **Managed Pricing**. 3. Click **Dynamic Price Check**. 4. Under **Add test accounts**, enter up to five Roblox users to check for hard-coded prices. 5. Select a testing type. - **Price pinned** updates all dynamically-scripted prices with a set fake Robux amount. - **Location pinned** updates all dynamically-scripted prices with a region-specific price for a fake economic location. 6. Click **Enable**. After a few minutes, you can enter your experience to identify the hard-coded prices. To disable the dynamic price check tool, go to the **Dynamic Price Check** page and click **Disable**. ## Use price optimization To use the Managed Pricing price optimization feature: 1. Go to [Creations](https://create.roblox.com/dashboard/creations) and select an experience. 2. Go to **Monetization** ⟩ **Managed Pricing**. 3. Select the developer products and passes you want to include in the price test. For best results, include all products. 4. Click **Start Test**. Users will see the test prices when they re-join your experience. After approximately 3 weeks, you will receive an email notification that the test is complete. The **Managed Pricing** page will update with the optimized product prices, the recommended price percentage change, and the approximate long-term revenue impact of applying the new product prices. 5. Click **Review & Apply prices** to apply the results of the price optimization test. You can stop the price optimization test any time by clicking **Stop test** in the **Managed Pricing** page. If you stop the test, your product prices revert to their original prices. ## Limitations - You can only optimize the prices of developer products and passes. Price optimization isn't available for subscriptions. - You can't make changes to the prices of products involved in price tests while the tests are in progress. ## Glossary | **Term** | **Definition** | | --- | --- | | **Test period** | The price optimization test period or the price review period. | | **Price groups** | The test groups involved in the Managed Pricing tests. | | **Paying users** | The number of users who purchased at least one tested product during the test period. | | **Sales** | The number of products sold during the test period. | | **Total playtime** | The total time users spent inside the experience during the test period. | | **Daily active users** | The number of active users during the test period. | | **Revenue** | The amount of Robux spent on tested products during the four-week price review period. | | **Approximate revenue impact** | The projected long-term revenue increase if the optimized prices are applied to the tested products. | | **Tested products revenue increase** | The revenue of the tested products shown to 98% of users during the price review period. | | **Overall revenue increase** | The combined revenue of the tested products during the price review period, including the 2% of users shown the original prices. | | **ARPPU** | The average revenue per paying user during the price review period. | | **Recommended price increase** | The number of products for which we recommend increasing the price. | | **Recommended price decrease** | The number of products for which we recommend lowering the price. | | **Recommended no change** | The number of products for which we recommend keeping the current price. | --- title: "Private servers" url: /docs/en-us/production/monetization/private-servers last_updated: 2026-06-29T19:34:05Z description: "Private servers are a subscription-based feature that allows users to decide who can play an experience with them." --- # Private servers A **private server** is a subscription-based feature that allows a user to decide who can play an experience with them. While private servers can be free, you can also use private servers as a method of monetization by charging users who want to access private servers a **monthly Robux fee**. Private servers offer users privacy in circumstances like: - Playing experiences with their friends. - Holding gatherings such as classes, meetings, or parties. - Recording and/or streaming without other users. - Gathering in-experience resources. > **Warning:** You cannot enable [paid access in Robux](/docs/en-us/production/monetization/paid-access-robux.md) or [paid access in local currency](/docs/en-us/production/monetization/paid-access-local-currency.md) and private servers at the same time. > **Warning:** Players under the age of 13 may not be able to join private servers depending on their privacy and parental control settings. ## Enable private servers Before you can enable private servers, your experience must be **public** to all users. To create a private server: 1. Open Studio's **File** ⟩ **Experience Settings** window. 2. In the left-hand navigation, click **Monetization**. 3. Enable the **Private Servers** toggle. Additional settings appear for setting how to monetize private servers: - **Free** — Click the **Free** radio button to make private servers free to all. - **Paid** — Click the **Paid** radio button, then enter the amount of Robux you want users to pay to access the private server. The price you enter affects the amount of Robux you earn per sale. 4. Click the **Save** button. > **Info:** You can only change the price of a paid private server **once every 60 days**. > **Warning:** Note that changing the price of private servers **cancels all active subscriptions**. Users who had active subscriptions automatically receive a private message in their inbox about this change. --- title: "Regional pricing" url: /docs/en-us/production/monetization/regional-pricing last_updated: 2026-06-29T19:34:05Z description: "Regional pricing adjusts the price of your items based on a user's region." --- # Regional pricing With Managed Pricing, you can offer **region-specific prices** for your items and build a more inclusive and accessible global economy. After you determine a default global price for an item, Roblox uses a variety of signals like the region's purchasing power, currency exchange rates, and local spending behavior to set the most appropriate price for that item region by region. You can choose which of your items you want to price regionally. When you enable Managed Pricing for an item in your experience, Roblox automatically adjusts the price of that item for users based on their economic location. Economic location is not always the same as account location. To determine a user's economic location, Roblox looks at signals, including VPN usage, billing history, and account history. If their economic location can't be determined, users see the default global price for the item instead. To prevent price arbitrage when users gift or trade items with different managed prices, you can [manage in-experience transfers with the `GetUsersPriceLevelsAsync` API](#protect-your-trades-and-gifts). > **Warning:** Regional prices will never be discounted by more than 70% of your default price, and they will never exceed the default price you set for your item. > > For example, if your item has a default price of 100 Robux, the lowest the item can be priced at is 30 Robux and the highest is 100 Robux, regardless of a user's region. ## Enable regional pricing > **Info:** Regional prices update periodically to reflect changes in global economy trends. ### For passes Regional pricing is enabled by default for all new and existing passes through [Managed pricing](/docs/en-us/production/monetization/managed-pricing.md). To disable managed pricing for a pass: 1. Go to [Creations](https://create.roblox.com/dashboard/creations) and select an experience. 2. Go to **Monetization** ⟩ **Passes**. 3. Select one or more existing passes. 4. Click **Disable Managed Pricing**. 5. **OPTIONAL** To view managed prices by country or region, select a pass and go to its **Sales** page. The **Top Countries/Regions** list updates to show the adjusted prices based on the default price of the pass. To view prices for all countries and regions, click **View all countries**. ### For developer products To enable managed pricing for a developer product: 1. Check that your developer products have [dynamically-scripted prices](#check-for-dynamic-pricing). 2. [Implement the `GetUsersPriceLevelsAsync` method](#protect-your-trades-and-gifts) to regulate item transfers based on users' price levels. 3. Go to [Creations](https://create.roblox.com/dashboard/creations) and select an experience. 4. Go to **Monetization** ⟩ **Developer Products**. 5. Select one or more existing products, or create a new product. 6. Click **Enable Managed Pricing**. 7. **OPTIONAL** To view Managed Pricing by country or region, select a product and go to its **Basic Settings** page. The **Top Countries/Regions** list updates to show the adjusted prices based on the default price of the product. To view Managed Pricing for all countries and regions, click **View all countries**. ### For subscriptions Managed pricing is enabled by default for subscriptions priced in Robux and cannot be turned off. For more information, see [Create subscriptions](/docs/en-us/production/monetization/subscriptions.md#create-subscriptions). Managed pricing isn't available for subscriptions priced in local currency. ### For Avatar items To enable Managed Pricing for an Avatar item, see [Pricing](/docs/en-us/marketplace/publish-to-marketplace.md#pricing) in the Publish to Marketplace page. ## Check for dynamic pricing > **Info:** If you're only applying Managed Pricing to passes for sale on your experience details page, you don't need to check for hard-coded prices. When you enable Managed Pricing, the price of the item adjusts for users in different regions whether it's for sale inside or outside of your experience. However, if you have hard-coded the price into your experience's UI, that number does not update as it's not dynamic or accessible by Roblox. As a result, users are charged the correct region-specific price but the UI still shows them the hard-coded value. The **dynamic price check tool** updates all items for sale inside your experience with a fake Robux price or a fake economic location to help you identify which prices are hard-coded in your experience's UI and which are dynamically-scripted with `Class.MarketplaceService|MarketplaceService` and called from a client script. After you have identified the hard-coded passes, you can update them to use `MarketplaceService` functions. To use the dynamic price check tool: 1. Go to **Monetization**. - For passes, go to **Passes**. - For developer products, go to **Developer Products**. 2. Click **…** and select **Dynamic Price Check**. 3. Under **Add test accounts**, enter up to five Roblox users to check for hard-coded prices. 4. Select a testing type. - **Price pinned** updates all dynamically-scripted prices with a set fake Robux amount. - **Location pinned** updates all dynamically-scripted prices with a region-specific price for a fake economic location. 5. Click **Enable**. After a few minutes, enter your experience to identify the hard-coded prices. To disable the dynamic price check tool, go to the **Dynamic Price Check** page and click **Disable**. For more information about hard-coded versus dynamically-scripted product prices, see [Check for dynamic pricing](/docs/en-us/production/monetization/price-optimization.md#check-for-dynamic-pricing) in the Price optimization page. For more information about selling passes and developer products with `MarketplaceService` functions, see [Sell a pass inside your experience](/docs/en-us/production/monetization/passes.md#inside-your-experience) and [Sell a developer product inside your experience](/docs/en-us/production/monetization/developer-products.md#inside-your-experience). ## Display regional prices in your experience With Managed Pricing, the price a user sees can vary based on their location. If you display prices in a custom in-experience UI, like a `Class.TextLabel`, you can retrieve product information at runtime to make sure the displayed price matches the price shown in the purchase prompt. To display the regional price for a single developer product or pass, use `Class.MarketplaceService.GetProductInfoAsync|GetProductInfoAsync`. To display the regional price for all developer products, use `Class.MarketplaceService.GetDeveloperProductsAsync|GetDeveloperProductsAsync`. ## Protect your trades and gifts Managed pricing can affect in-experience transfers like trading and gifting. Because of price differences across regions, price arbitrage (exploiting price discrepancies between the same products to generate a profit) can take place. To manage the potential for price arbitrage, you can use the `Class.MarketplaceService.GetUsersPriceLevelsAsync|GetUsersPriceLevelsAsync` API, which lets you determine the relative price levels of users so that you can implement logic to regulate item transfers based on these levels. `GetUsersPriceLevelsAsync` provides a numerical value between 1 and 1000 that indicates a user's pricing level. This value is designed to represent the percentage of the full price a user is expected to pay. The price level 1000 represents the full global price, while lower price levels indicate that the user typically pays a lower price because of their economic location. A lower price level difference means that there's a smaller price difference between the full global price and the user's managed price. A higher price level difference means that there's a larger price difference between the two. For example: - User A has the price level 1000, which represents the full global price. This user pays 100% of the base price for an item. - User B has the price level 500, which represents 50% of the full global price. This user pays 50% of the base price for an item. - Once you know each user's price level, you can choose to approve or block gifting or trading between User A and User B based on their price level difference of 500. ### Implement GetUsersPriceLevelsAsync > **Warning:** We recommend that you **do not** cache the results of your `GetUsersPriceLevelsAsync` API calls after a user session. User price levels can change. Relying on cached data can lead to inaccurate transfer logic and potential abuse or exploitation of the system. > > To make sure that you have the most up-to-date price information for each user, always make calls to `GetUsersPriceLevelsAsync` at player join. To get the price levels of users involved in an item transfer, call the `GetUsersPriceLevelsAsync` function in the `MarketplaceService`. This function takes an array of user IDs as its argument, and returns an array of `PriceLevelInfo` objects with the following fields: ```lua type PriceLevelInfo = { UserId: number, PriceLevel: number } ``` After implementing the function, use the returned price levels to choose whether you want to allow an item transfer to take place. For example, you can let users with a higher price level gift items to users with a lower price level, but only allow trading between users that have the same price level as each other. See the [available examples](#examples) for more information. For more information about the `GetUsersPriceLevelsAsync` API, see `Class.MarketplaceService.GetUsersPriceLevelsAsync|MarketplaceService`. ### Examples Examples of how you can use `GetUsersPriceLevelsAsync` with regional pricing. ##### Example 1: Check the price levels for a list of users This example shows you how to retrieve price levels for a list of users, which can help you choose whether you want to allow an item transfer to take place. ```lua -- Get the MarketplaceService local MarketplaceService = game:GetService("MarketplaceService") -- Define a function to retrieve price levels for a list of users and creates a lookup table of UserId to PriceLevel local function getPriceLevelsLookup(userIds) local success, result = pcall(function() return MarketplaceService:GetUsersPriceLevelsAsync(userIds) end) if success then -- Map each PriceLevelInfo to a UserId -> PriceLevel lookup table local lookup = {} for _, userData in ipairs(result) do lookup[userData.UserId] = userData.PriceLevel end return lookup else warn("Error getting price levels:", result) return nil end end -- Example using placeholder IDs local user1Id = 123456789 local user2Id = 987654321 -- Call the function and store the result local priceLevels = getPriceLevelsLookup({user1Id, user2Id}) -- If successful, print each user's level if priceLevels then print("Price level for User 1:", priceLevels[user1Id]) print("Price level for User 2:", priceLevels[user2Id]) else print("Failed to retrieve price levels.") end ``` ##### Example 2: Compare a sender's price level to a recipient's price level This example shows you a **sample implementation** of how to check a sender's price level against the recipient's price level. This can help you manage gifting from users in higher-priced regions to users in lower-priced regions. ```lua -- Get the MarketplaceService local MarketplaceService = game:GetService("MarketplaceService") -- Define a function to retrieve price levels for a list of users and creates a lookup table of UserId to PriceLevel local function getPriceLevelsLookup(userIds) local success, result = pcall(function() return MarketplaceService:GetUsersPriceLevelsAsync(userIds) end) if success then -- Map each PriceLevelInfo to a UserId -> PriceLevel lookup table local lookup = {} for _, userData in ipairs(result) do lookup[userData.UserId] = userData.PriceLevel end return lookup else warn("Error getting price levels:", result) return nil end end -- Define a function that checks if the sender has a higher or equal price level to the recipient -- The parameters are the Player sending the item and the Player receiving the item -- The function returns true if the sender's price level is greater than or equal to the recipient's price level function isSenderPriceLevelHigherOrEqualforGifting(sender, recipient) local priceLevelsLookup = getPriceLevelsLookup({ sender.UserId, recipient.UserId }) if not priceLevelsLookup then error("MarketplaceService:GetUsersPriceLevelsAsync failed. Unable to retrieve user price levels.") end local senderPriceLevel = priceLevelsLookup[sender.UserId] local recipientPriceLevel = priceLevelsLookup[recipient.UserId] return senderPriceLevel >= recipientPriceLevel end ``` ##### Example 3: Check if two users have the same price level This example shows you a **sample implementation** of how to check if two users have the same price level. This can help you manage trading and ensure a fair exchange between users from different regions. ```lua -- Get the MarketplaceService local MarketplaceService = game:GetService("MarketplaceService") -- Define a function to retrieve price levels for a list of users and creates a lookup table of UserId to PriceLevel local function getPriceLevelsLookup(userIds) local success, result = pcall(function() return MarketplaceService:GetUsersPriceLevelsAsync(userIds) end) if success then -- Map each PriceLevelInfo to a UserId -> PriceLevel lookup table local lookup = {} for _, userData in ipairs(result) do lookup[userData.UserId] = userData.PriceLevel end return lookup else warn("Error getting price levels:", result) return nil end end -- Define a function that checks if two users have the same price level -- The parameters are the two users whose price levels you want to compare -- The function returns true if both users have the same price level function haveSamePriceLevelForTrading(userA, userB) local priceLevelsLookup = getPriceLevelsLookup({ userA.UserId, userB.UserId }) if not priceLevelsLookup then error("MarketplaceService:GetUsersPriceLevelsAsync failed. Unable to retrieve user price levels.") end local userAPriceLevel = priceLevelsLookup[userA.UserId] local userBPriceLevel = priceLevelsLookup[userB.UserId] return userAPriceLevel == userBPriceLevel end ``` --- title: "Roblox Plus" url: /docs/en-us/production/monetization/roblox-plus last_updated: 2026-06-29T19:34:05Z description: "Roblox Plus gives you additional ways to earn from your games by rewarding engagement and purchases from Plus subscribers." --- # Roblox Plus **Roblox Plus** gives you additional ways to earn from your games by rewarding engagement and purchases from Plus subscribers. Users who subscribe to Plus get a range of benefits to enhance their purchasing power, access, and overall game on the platform: - **Discounted purchases**: Subscribers get 10% off eligible purchases, including in-game items, avatar items, and more. The discount increases to 20% starting in the third month. Roblox covers this discount for you. - **Free paid private servers**: Subscribers get unlimited access to paid private servers at no cost. You're still compensated when they spend time in your paid private servers. - **Free Robux transfers**: Subscribers can transfer Robux at no cost to sender or recipient. Transfers are subject to age restrictions and parental consent. Robux received through transfers are not eligible for the DevEx program. - [**Marketplace access**](/docs/en-us/marketplace/marketplace-policy.md#creator-requirements): Subscribers can trade and resell limited items, and publish and sell avatar items. Premium subscribers have the same Marketplace access. For more information about the benefits of Roblox Plus to end-users, see the [help center](https://en.help.roblox.com/hc/en-us/articles/47967913158164-Roblox-Plus). As a creator, you can earn from Roblox Plus subscribers through: - [**In-game Robux purchases**](#earn-from-in-experience-purchases): Subscribers receive a 10–20% discount on purchases, which is covered by Roblox so that your earnings per purchase are not reduced. Lower prices can encourage more frequent purchases, helping increase your overall revenue. - [**Driving Plus sign-ups**](#earn-from-in-experience-plus-subscriptions): Encourage engaged users to subscribe to Roblox Plus directly from your game using `Class.MarketplaceService.PromptRobloxSubscriptionPurchase|PromptRobloxSubscriptionPurchase`. You can earn up to 750 Robux for each new subscriber you bring in, with earnings starting once the subscription becomes paid (free trial periods are not included). - [**Time spent in paid private servers**](#earn-from-paid-private-server-time): Earn up to 100 Robux per subscriber when they spend at least 60 minutes in your paid private servers each month. - [**In-game Robux transfers**](#earn-from-robux-transfers): Earn 10% every time a Plus subscriber sends Robux to another user within your game using `Class.MarketplaceService.PromptRobuxTransferAsync|PromptRobuxTransferAsync`. This amount is eligible for the DevEx program. ## Earn from in-experience purchases With Roblox Plus, users receive a 10–20% discount on eligible Robux purchases. Because Roblox subsidizes these discounts, you earn the same amount per purchase as you would from non-subscribers. At the same time, lower prices for Plus subscribers can encourage more frequent purchases and increase your overall revenue. Subscribers receive a 10% discount for their first two months, which increases to 20% starting in the third month. If a user cancels their Plus subscription and later resubscribes, their discount resets to 10% and increases again after two months. Eligible Robux purchases include: - Developer products - Game passes - Developer subscriptions - Paid access games - Avatar items Discounts do not apply to: - Items priced above 1,000,000 Robux - Marketplace publishing fees - Ad credits - Group fees The following table shows how Roblox Plus discounts are subsidized by Roblox. | **User type** | **Item price in Robux** | **User spend** | **Roblox subsidy** | **Creator earnings** | **Effective revenue share** | | --- | --- | --- | --- | --- | --- | | Non-subscriber | 100 | 100 | — | 70 | 70% | | Plus subscriber (10% discount) | 100 | 90 | 10 | 70 | 78% | | Plus subscriber (20% discount) | 100 | 80 | 20 | 70 | 88% | > **Warning:****If you hard-code prices, your in-game UI might display incorrect pricing to Plus subscribers.** While subscribers are still charged the discounted price, your game might show them the full price instead. This mismatch can cause confusion. > > To display accurate, real-time pricing (including Plus discounts), you should use `Class.MarketplaceService` functions like `Class.MarketplaceService.GetProductInfoAsync|GetProductInfoAsync` and `Class.MarketplaceService.GetDeveloperProductsAsync|GetDeveloperProductsAsync`. > > For implementation details, see [Sell a pass](/docs/en-us/production/monetization/passes.md#sell-a-pass) and [Sell a developer product](/docs/en-us/production/monetization/developer-products.md#sell-a-developer-product). ## Earn from in-experience Plus subscriptions You can encourage users to subscribe to Roblox Plus directly from your game with `Class.MarketplaceService.PromptRobloxSubscriptionPurchase|PromptRobloxSubscriptionPurchase`. For each user who becomes a subscriber through your game, you earn **250 Robux per month for their first three consecutive months**, with up to **750 Robux for every newly acquired subscriber**. Any earnings are subject to a 60-day holding period. > **Info:** Roblox Plus subscription prompts might appear in other places like paid private server pages or purchase modals, but you only earn a Robux payout when a user subscribes through your game using `PromptRobloxSubscriptionPurchase`. To offer Plus subscriptions within your game and grant rewards to users: 1. Detect when the user reaches the point in your game where you want to offer the Plus subscription. 2. Check whether the user already has an active Plus subscription with `Class.Player.HasRobloxSubscription|HasRobloxSubscription`. 3. If the user is already a subscriber: 1. Grant them the reward immediately. 4. If the user isn't a subscriber: 1. Prompt them to purchase Roblox Plus with `Class.MarketplaceService.PromptRobloxSubscriptionPurchase|PromptRobloxSubscriptionPurchase`. 2. Listen to `Class.MarketplaceService.PromptRobloxSubscriptionPurchaseFinished|PromptRobloxSubscriptionPurchaseFinished` to detect when the purchase prompt UI has closed. 3. Confirm the Plus subscription on the server with `Class.Player.HasRobloxSubscription|HasRobloxSubscription` through the `Class.Instance.GetPropertyChangedSignal|GetPropertyChangedSignal` before granting any rewards. This event verifies that the user's Plus subscription status has actually changed. ### Examples The following examples show how to work with Roblox Plus in your game, including retrieving discount information, checking subscription status, prompting users to subscribe, and handling the purchase flow. ##### Example 1 Print Roblox Plus discount information for a game pass. ```lua local MarketplaceService = game:GetService("MarketplaceService") local PASS_ID = 12345678 local DiscountTypeDisplay = { RobloxPlusSubscription = "Roblox Plus Discount", } local productInfo = MarketplaceService:GetProductInfoAsync(PASS_ID, Enum.InfoType.GamePass) print(string.format("Original Price: %d", productInfo.UserBasePriceInRobux)) for _, discount in ipairs(productInfo.PriceDiscountDetails) do local displayName = DiscountTypeDisplay[discount.Type] or "Other Discount" print(string.format("%s (%d%%): -%d", displayName, discount.Percent, discount.AmountInRobux)) end print(string.format("You Pay: %d", productInfo.PriceInRobux)) ``` ##### Example 2 Check whether a user has an active Roblox Plus subscription and perform actions based on their subscription status. ```lua local MarketplaceService = game:GetService("MarketplaceService") local player = game.Players.LocalPlayer local success, details = pcall(function() return MarketplaceService:GetRobloxSubscriptionDetailsAsync(player) end) if success and details.IsSubscribed then -- Check the length of the user's Plus subscription local threeMonths = 90 * 24 * 60 * 60 if details.StartTime and (os.time() - details.StartTime.UnixTimestamp) > threeMonths then print("Awarding the '3-Month Subscription Veteran' skin!") end -- Check if user subscribed through the game if details.IsOriginExperience then print("Attribution confirmed: User subscribed via this game.") else print("User subscribed elsewhere: Website or another game.") end end ``` ##### Example 3 Prompt users to subscribe to Plus and handle the purchase flow in your game. ```lua local MarketplaceService = game:GetService("MarketplaceService") local Players = game:GetService("Players") local teleporter = script.Parent local showModal = true local EXCLUSIVE_AREA_POSITION = Vector3.new(1200, 200, 60) -- Grant the reward and teleport the subscribing user to the exclusive area local function grantRewardAndTeleport(player) player:RequestStreamAroundAsync(EXCLUSIVE_AREA_POSITION) local character = player.Character if character and character.Parent then local currentPivot = character:GetPivot() character:PivotTo(currentPivot * CFrame.new(EXCLUSIVE_AREA_POSITION)) end end -- Detect when the character touches the teleporter teleporter.Touched:Connect(function(otherPart) local player = Players:GetPlayerFromCharacter(otherPart.Parent) if not player then return end if not player:GetAttribute("CharacterPartsTouching") then player:SetAttribute("CharacterPartsTouching", 0) end player:SetAttribute("CharacterPartsTouching", player:GetAttribute("CharacterPartsTouching") + 1) if player.HasRobloxSubscription then -- User already has Roblox Plus; grant the reward immediately grantRewardAndTeleport(player) else -- Prompt Roblox Plus subscription if not showModal then return end showModal = false task.delay(5, function() showModal = true end) MarketplaceService:PromptRobloxSubscriptionPurchase(player) end end) -- Detect when the character exits the teleporter teleporter.TouchEnded:Connect(function(otherPart) local player = Players:GetPlayerFromCharacter(otherPart.Parent) if player and player:GetAttribute("CharacterPartsTouching") then player:SetAttribute("CharacterPartsTouching", player:GetAttribute("CharacterPartsTouching") - 1) end end) -- Grant the reward when the server confirms a membership change -- Connect HasRobloxSubscription change for each player Players.PlayerAdded:Connect(function(player) player:GetPropertyChangedSignal("HasRobloxSubscription"):Connect(function() if player.HasRobloxSubscription and player:GetAttribute("CharacterPartsTouching") and player:GetAttribute("CharacterPartsTouching") > 0 then grantRewardAndTeleport(player) end end) end) ``` ##### Example 4 Detect when the user closes the purchase prompt UI. ```lua local MarketplaceService = game:GetService("MarketplaceService") local function onPromptRobloxSubscriptionPurchaseFinished(player, didTryPurchasing) if didTryPurchasing then -- User attempted to subscribe; wait for HasRobloxSubscription change to confirm print(player.Name, "attempted to subscribe to Roblox Plus") else print(player.Name, "closed the Roblox Plus subscription prompt without purchasing") end end MarketplaceService.PromptRobloxSubscriptionPurchaseFinished:Connect(onPromptRobloxSubscriptionPurchaseFinished) ``` ## Earn from paid private server time Roblox Plus subscribers can create paid [private servers](/docs/en-us/production/monetization/private-servers.md) for free. To make sure that you can still earn from these servers, Roblox compensates you based on the time Plus subscribers spend in the paid private servers they create within your game. **Payouts are based on the time a Plus subscriber spends in paid private servers they create themselves.** This means that if a Plus subscriber invites another Plus subscriber to a paid private server, you do not earn from the invited player. On the Plus subscription renewal day, Roblox evaluates paid private server usage over the previous 30 days for that subscriber. This evaluation occurs even if the subscriber cancels Plus instead of renewing. Roblox then determines the **top five paid private servers the subscriber created within your game**, based on the amount of time they spent in each server during that period, with a minimum of 60 cumulative minutes. If a Plus subscriber (including a subscriber on a Free Plus Trial) spends **at least 60 cumulative minutes over the last 30 days** in a paid private server they created within your game, you can earn up to **100 Robux per user per server** based on the server price. In the following example, Servers A, B, and C qualify because the Plus subscriber spent at least 60 minutes in each server during the last 30 days. Servers D and E don't qualify because they don't meet the 60-minute threshold. If all five servers (A–E) belong to the same game and represent the top five servers the subscriber spent time in, the creator earns a total of 205 Robux across the qualifying servers. | **Game's paid private server** | **Server price** | **Cumulative time (30 days prior to renewal)** | **Creator earnings** | | --- | --- | --- | --- | | Server A | 100 Robux | 120 minutes | 70 Robux (70% revenue share on a 100 Robux server price) | | Server B | 200 Robux | 80 minutes | 100 Robux (70% revenue share on a 200 Robux server price, capped at 100 Robux) | | Server C | 50 Robux | 70 minutes | 35 Robux (70% revenue share on a 50 Robux server price) | | Server D | 60 Robux | 15 minutes | Not eligible (less than 60 minutes) | | Server E | 70 Robux | 10 minutes | Not eligible (less than 60 minutes) | ## Earn from Robux transfers Robux transfers allow Plus subscribers to send Robux directly to other users. You can implement the Transfers API to prompt these transfers within your game and earn a share of each transfer. When a Plus subscriber initiates a transfer prompted by `Class.MarketplaceService.PromptRobuxTransferAsync|PromptRobuxTransferAsync` inside your game, you receive **10% of the transfer amount**. For example, if a subscriber transfers 100 Robux to another user, the recipient receives 90 Robux (90%) and your game receives 10 Robux (10%). Robux your game earns from transfers is **eligible for the DevEx program**. Roblox does not take a fee from these transfers. > **Info:** When setting up transfers in your experience, the amount must be between 10 and 500 Robux per transaction. For more information about implementing Robux transfers within your game, see [Robux transfers](/docs/en-us/production/monetization/robux-transfers.md). ## Track your Plus earnings You can track your earnings from users who subscribe to Roblox Plus through your in-game prompts, Robux transfers using the Transfers API, and time spent by Plus subscribers in your paid private servers, whether they join from within your game or outside of it. To see a detailed breakdown of your Plus subscription incentives: 1. In Creator Hub, go to **Creations** and select a game. 2. Go to **Monetization** ⟩ **Roblox Plus**. --- title: "Robux transfers" url: /docs/en-us/production/monetization/robux-transfers last_updated: 2026-06-29T19:34:05Z description: "Explains how to implement Robux transfers inside your game." --- # Robux transfers **Robux transfers** let [Roblox Plus](/docs/en-us/production/monetization/roblox-plus.md#earn-from-robux-transfers) subscribers send Robux directly to other users. To support transfers in your game, call `Class.MarketplaceService.PromptRobuxTransferAsync|PromptRobuxTransferAsync` to open the transfer prompt and use `Class.MarketplaceService.BindReceiptHandler|BindReceiptHandler` to process transfer receipts. When a subscriber completes a transfer, your game earns **10%** of the Robux sent and the recipient receives the remaining **90%**. For example, a 100 Robux transfer pays 90 Robux to the recipient and 10 Robux to your game. Robux your game earns from transfers is **eligible for the DevEx program**. Roblox does not take a fee from these transfers. > **Info:** When setting up transfers in your experience, the amount must be between 10 and 500 Robux per transaction. As a creator, you do not have access to transfer details or control over execution. All Robux transfers are processed and managed by the Roblox platform, and all sender and recipient actions go through the same safety and eligibility checks. Inside your game, transfers are completed through a Roblox-controlled UI to ensure they are safe and policy-compliant. ## Prompt Robux transfers To initiate a Robux transfer within your game: 1. Call `Class.MarketplaceService.PromptRobuxTransferAsync|PromptRobuxTransferAsync` from a server-side script. 2. Pass the required parameters: - `sender`: The player initiating the transfer. - `receiverUserId`: The user ID of the recipient. - `amount`: The amount of Robux to transfer. 3. Store the returned `transferRequestId` that identifies the transfer request and can be used to log or correlate the transfer with receipts later. The following example initiates a Robux transfer between two users: ```lua local MarketplaceService = game:GetService("MarketplaceService") local Players = game:GetService("Players") -- Transfers Robux when a user triggers a RemoteEvent local transferEvent = game.ReplicatedStorage:WaitForChild("RequestTransfer") transferEvent.OnServerEvent:Connect(function(sender, receiverUserId, amount) -- Validates inputs if not sender or not sender:IsA("Player") then return end if typeof(receiverUserId) ~= "number" or receiverUserId <= 0 then warn("Invalid receiverUserId") return end if typeof(amount) ~= "number" or amount <= 0 then warn("Invalid amount") return end local success, result = pcall(function() return MarketplaceService:PromptRobuxTransferAsync(sender, receiverUserId, amount) end) if success then print(\`Transfer initiated with TransferRequestId: {result}\`) else warn(\`Transfer failed: {result}\`) end end) ``` ## Handle transfer receipts After a successful transfer, Roblox generates receipts for both the sender and the recipient. You can process these receipts using `Class.MarketplaceService.BindReceiptHandler|BindReceiptHandler` to confirm that the transfer completed successfully. To encourage Plus subscribers to transfer Robux within your game, you can offer items or other perks for each successful transfer. If you do, process the receipt before granting the reward to ensure it's only given for completed transfers. To process Robux transfer receipts: 1. Use `Class.MarketplaceService.BindReceiptHandler|BindReceiptHandler` to register a handler for `Enum.ReceiptType.RobuxTransferSender|RobuxTransferSender`, which handles receipts for the user who initiated the transfer. 2. Register a second handler for `Enum.ReceiptType.RobuxTransferReceiver|RobuxTransferReceiver`, which handles receipts for the user who received the Robux. 3. In each handler, inspect the receipt info, including `PlayerId` and `TransferRequestId`. Both receipt types include a `TransferRequestId` that matches the `transferRequestId` returned by `PromptRobuxTransferAsync`. 4. Return a `Enum.ReceiptDecision` value from your handler: - Return `Enum.ReceiptDecision.Processed|Processed` after you finish handling the receipt. This marks the receipt as complete and confirms the Robux transfer was successful. - Return `Enum.ReceiptDecision.NotProcessedYet|NotProcessedYet` if the receipt can't be processed yet (for example, if the user is no longer in the server). This keeps the receipt unresolved so it can be delivered later (for example, when the user rejoins the server). The following example registers handlers for both sender and recipient receipts: ```lua local MarketplaceService = game:GetService("MarketplaceService") local Players = game:GetService("Players") -- Handles sender receipts (the user who sent the Robux) local senderConnection = MarketplaceService:BindReceiptHandler( Enum.ReceiptType.RobuxTransferSender, function(receiptInfo) local player = Players:GetPlayerByUserId(receiptInfo.PlayerId) if not player then -- User left the server - receipt processing is deferred until user rejoins the server return Enum.ReceiptDecision.NotProcessedYet end print(\`{player.Name} sent Robux (TransferRequestId: {receiptInfo.TransferRequestId})\`) -- You can grant any sender-side acknowledgement or update the UI here return Enum.ReceiptDecision.Processed end ) -- Handles recipient receipts (the user who received the Robux) local receiverConnection = MarketplaceService:BindReceiptHandler( Enum.ReceiptType.RobuxTransferReceiver, function(receiptInfo) local player = Players:GetPlayerByUserId(receiptInfo.PlayerId) if not player then return Enum.ReceiptDecision.NotProcessedYet end print(\`{player.Name} received Robux (TransferRequestId: {receiptInfo.TransferRequestId})\`) -- You can grant any receiver-side benefits or update the UI here return Enum.ReceiptDecision.Processed end ) ``` --- title: "Shop" url: /docs/en-us/production/monetization/shop last_updated: 2026-06-29T19:34:05Z description: "Sell passes and developer products inside your game's Shop." --- # Shop > **Info:****Shop will become available to players on July 7, 2026.** Until then, you can set up and preview your Shop, but players won't see it in their in-game menus. > **Warning:** On July 7, 2026, Roblox will automatically publish your Shop to your in-game menu using suggested categories and listing settings. Review your Shop before then to customize those suggestions before players see them. > > If you don't want Shop to appear in your in-game menu, [hide it](#in-game-menu) before July 7, 2026. **Shop** provides players with a personalized shopping experience for your game. Roblox combines platform-wide signals with signals from your game to surface the most relevant items for each player, and automatically handles ranking and optimization. Shop displays all passes and eligible developer products available in your game, making it easier for players to discover items they want to purchase. Players can also browse **Top Picks**, personalized categories, and search results. In addition to individual items, Shop can surface Robux packages that include one or more of your items. When a player purchases one of these packages, they receive additional Robux to spend across Roblox, including in your game. ## Manage your Shop You can manage your Shop from Creator Hub, where you can review item categories and visibility, preview your Shop in Roblox Studio, and save changes. To access the Shop page: 1. Go to [Creator Hub](https://create.roblox.com/dashboard/creations) and select a game. 2. Go to **Monetization** > **Shop**. The Shop page has two tabs: - **Overview**: A summary of your Shop, including key metrics and recent activity. - **Item catalog**: The full list of passes and developer products in your Shop, where you manage [visibility](#list-or-unlist-items) and [categories](#update-categories). Changes to your Shop are not visible to players until you save them. To publish your changes, click **Save** in the upper-right corner of the **Item catalog** tab. ### List or unlist items > **Info:** Passes are always listed and can't be unlisted. Listing controls whether a developer product appears in the in-game Shop or other eligible Roblox surfaces. - **Listed**: The developer product appears in Shop and might appear on other Roblox surfaces, such as the game details page. - **Unlisted**: The developer product doesn't appear in Shop or on other Roblox surfaces, but can still be purchased through your own in-game purchase flows. To change the listing status of a developer product: 1. In the **Item catalog** tab, select the item. 2. Do one of the following: - To list the item, click **List items** or click **⋮** > **Show in Shop**. - To unlist the item, click **Unlist items** or click **⋮** > **Hide from Shop**. Listing developer products also lets Roblox surface your items outside of your game, which can create more opportunities for purchases. To be eligible for external surfacing, make sure that `ProcessReceipt` is properly implemented for your items. We strongly recommend implementing `ProcessReceipt` so players receive their purchases immediately, including purchases made through Shop and on other Roblox surfaces. For more information, see [Handle a developer product purchase](/docs/en-us/production/monetization/developer-products.md#handle-a-developer-product-purchase). ### Update categories Each item belongs to a category that determines where it appears in Shop. Roblox automatically suggests categories for your items, but you can customize them to match your game. You can create new categories, rename existing ones, and move items between them. To create a new category: 1. Click the **Category** dropdown. 2. Click **Add category**. 3. Enter a category name and click **Save**. To rename an existing category: 1. Click the **Category** dropdown. 2. Click the edit icon next to the category you want to rename. 3. Enter the new name and click **Save**. To move items between categories, do one of the following: - To move a single item, click the **Category** dropdown next to the item and choose a new category. - To move multiple items, select the items, click **Edit category**, choose a new category, and then click **Save**. ## Shop access Players can access Shop through the in-game menu or through the global Shop button in your game. ### In-game menu By default, Shop appears in the in-game menu. You can hide it by adding the following code snippet to a client script: ```lua local StarterGui = game:GetService("StarterGui") StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType.ExperienceShop, false) ``` ### Global Shop button The global Shop button provides a persistent way for players to open Shop in your game. It appears in the upper-left corner of the screen, next to the top bar. The button is disabled by default. We recommend enabling it so players always have a visible way to access Shop. To enable the global Shop button: 1. Go to **Monetization** > **Shop**. 2. In the **Overview** tab, toggle the **Shop button**. ## Purchases and earnings Purchases made through Shop use your existing purchase handling, and standard earnings apply. Shop is a discovery surface and doesn't change the economics of any pass or developer product. --- title: "Subscriptions" url: /docs/en-us/production/monetization/subscriptions last_updated: 2026-06-29T19:34:05Z description: "Subscriptions within experiences let you offer users recurring benefits for a monthly fee." --- # Subscriptions **Subscriptions** within experiences let you offer users recurring benefits for a monthly fee. Subscriptions are similar to [passes](/docs/en-us/production/monetization/passes.md), but while the benefits of a pass are granted indefinitely, the benefits of a subscription are contingent on the user paying a monthly fee. Subscriptions on Roblox have the following characteristics: - **Auto-renewal:** Subscriptions are auto-renewing, not one-time purchases, and can be priced in local currency or Robux. - **Robux payout:** Whether a subscription is purchased in local currency or Robux, you receive the subscription revenue in Robux. For more details, see [Earn with subscriptions](#earn-with-subscriptions). - **Single-tiered:** All subscriptions within an experience can be owned by users simultaneously. Mutually exclusive subscriptions are not supported. - **Regional Pricing:** [Regional Pricing](/docs/en-us/production/monetization/regional-pricing.md) is enabled by default for subscriptions sold in Robux and cannot be turned off. Regional Pricing is not available for subscriptions sold in local currency. ## Subscription guidelines Before creating your subscriptions, make sure they align with [Roblox's Terms of Use](https://en.help.roblox.com/hc/articles/19694609252884/) and comply with local laws. Any experience that engages in scams, attempts to mislead users with false offerings, or otherwise violate our [Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards) will be taken down. You must also make sure your subscriptions follow these guidelines: - **Provide clear, distinguishable subscription options:** Use short, succinct, and self-explanatory names that differentiate subscription options from one another. Specify the price and duration for each option when merchandising in-experience. - **Offer the same benefits across platforms and devices:** Regardless of where the subscription was purchased, ensure the user receives the same benefits for any given subscription plan. - **Design subscriptions to fit with each other and overall experience offerings:** Tiering of the same suite of benefits, like offering "Bronze," "Silver," and "Gold" tiers that are mutually exclusive, is not currently available. Ensure subscriptions represent distinct sets of benefits. - **Offer the benefits for the full term of the subscription:** Once a subscription offering is live, honor the benefits described and do not revoke benefits behind the scenes. - **Do not direct users to purchase on another platform (like mobile, web, etc.) in-experience:** While you are free to communicate with users off-platform, using the Roblox app to direct users to purchase elsewhere is prohibited. - **Do not gate subscription benefits by additional requirements once a user has paid:** Requiring a user to perform additional tasks, such as posting to social media, to get access to benefits they have paid for is prohibited. This guideline does not impact battle passes, which you are allowed to both create and market as a subscription purchase. ## Robux vs local currency Subscriptions can be priced in Robux or local currency. The following table compares the two payment options. | | **Robux** | **Local currency** | | --- | --- | --- | | **Eligibility** | All creators | Requires an account [verified by ID or phone number](/docs/en-us/production/publishing/account-verification.md) | | **Platforms** | Available on all platforms | Available on Web, App Store, and Google Play | | **Countries** | Available in all countries that support Roblox | Unavailable in Argentina, China, Colombia, India, Indonesia,
Japan, Russia, Taiwan, Türkiye (Turkey), UAE, Ukraine, and Vietnam | | **Price** | Any amount equal to or greater than 49 Robux | $2.99, $4.99, $7.99, $9.99, or $14.99 | | **Regional Pricing** | Enabled by default | Unavailable | | **Payouts** | 70% of subscription earnings each month | 70% in the first month, then 100% in subsequent months | ## Create subscriptions To create a subscription: 1. Go to your experience in the [Creator Dashboard](https://create.roblox.com/) and select **Monetization** ⟩ **Subscriptions**. 2. Click **Create Subscription**. 3. Upload a cover image. 4. Enter a name that is unique. You cannot have two subscriptions with the same name in the same experience. 5. Enter a description that clearly describes the benefits you're offering. 6. Select one of the following payment options: - **Subscribers pay Robux:** Charge users the recurring subscription fee in Robux. If you select this option, you must enter a custom price greater than or equal to 49 Robux. [Regional Pricing](/docs/en-us/production/monetization/regional-pricing.md) is enabled by default for the subscription and cannot be turned off. - **Subscribers pay local currency:** Charge users the recurring subscription fee in their local currency. If you select this option, you must choose a subscription price from the 5 available price tiers. 7. Select one of the following product types: - **Durable:** Permanent items that persist after use or acquisition, such as physical items like weapons. If a subscription includes a bundle of different types of goods, with one or more of them durable, such as a value pack with a sword and a potion, choose **Durable** as the product type. - **Consumable:** Temporary re-purchasable items that expire after use or acquisition, such as consumable potions that grant temporary boosts that expire over time. - **Currency:** Any medium of exchange that users can use to purchase items within your experience. 8. Click **Create Subscription**. > **Info:** After creating a subscription, you can switch the payment type between Robux and local currency. This change does not affect users with existing subscriptions. New subscriptions will use the selected payment method at the time of purchase. ## Activate subscriptions You can have up to **50 subscriptions** per experience between active and inactive subscriptions. To put a subscription up for sale, click **⋮** next to the subscription you want to activate and select **Activate**. Active subscriptions are automatically available for sale in the **Store** tab of the experience details page, and can be added to your experience through `Class.MarketplaceService` functions. Before activating your subscription for the first time, you must confirm a shortened version of your experience name. This shortened experience name is displayed to the user when they subscribe, and appears alongside the subscription name you created in [Create subscriptions](#create-subscriptions). > **Warning:** Shortened experience names are permanent and cannot be changed when set. They do not change the name of your experience on Roblox. ### Subscription states Subscriptions have two possible states: - **Active:** Active subscriptions are available for sale, with subscribers able to renew their subscription at the start of the next period. - **Inactive:** Inactive subscriptions are unavailable for sale. To change a subscription's state, click **⋯** at the top right corner of the subscription tile and select **Activate** (if the subscription is currently inactive) or **Take Off Sale** (if the subscription is currently active). If you select **Take Off Sale**, you'll be given the option to either let existing subscribers renew their subscription, or to cancel future renewals for current subscribers. If you're not planning to permanently remove the associated subscription benefits from your experience, we recommend that you let existing subscribers renew their subscription. ## Edit subscriptions The fields you can edit depend on the payment method. | Editable field | Subscriptions sold in Robux | Subscriptions sold in local currency | | --- | --- | --- | | Cover image | Yes | Yes | | Name | No | No | | Description | Yes | Yes | | Payment method | Yes (can switch to local currency) | Yes (can switch to Robux) | | Price | Yes (once every 60 days) | No | | Product type | No | No | | Active state | Yes | Yes | Changing the payment method only affects new subscriptions. Existing subscribers remain on their original payment method and must cancel and resubscribe to switch. > **Info:** You can only change the price of a subscription sold in Robux **once every 60 days**. For price increases, the new price only takes effect after users receive at least 30 days' notice from Roblox. > > You **cannot** change the price of subscriptions sold in local currency. To use a different price, you must delete and recreate the subscription. ## Delete subscriptions > **Warning:** If you delete a subscription in local currency, you must refund all currently subscribed users. Refunds are not available for subscriptions in Robux. To delete a subscription, click **⋮** next to the subscription you want to delete and select **Delete**. Deleting an active subscription results in full refunds for active subscribers and zero Robux for you. In most situations, if you want to delete a subscription, we recommend that you take it off sale first, select the option to cancel all renewals, and wait for the current period to conclude. Deleting a subscription requires the last four digits of the subscription ID for confirmation. ## Integrate subscriptions into an experience ### Check a user's subscription status The following code sample implements subscription detection in your experience: ```lua -- Run this code on the server local MarketplaceService = game:GetService("MarketplaceService") local Players = game:GetService("Players") local SUBSCRIPTION_ID = "EXP-11111111" -- Replace with your subscription ID local function grantAward(player: Player) -- You should grant the award associated with the subscription here end local function revokeAwardIfGranted(player: Player) -- This method is called for every player who does _not_ have the subscription -- If your code saves subscriptions to Data Stores or provides some benefit that needs to be 'revoked' -- you should use this method to handle the revocation end local function checkSubStatus(player) local success, response = pcall(function() return MarketplaceService:GetUserSubscriptionStatusAsync(player, SUBSCRIPTION_ID) end) if not success then warn(\`Error while checking if player has subscription: {response}\`) return end if response.IsSubscribed then grantAward(player) else revokeAwardIfGranted(player) end end local function onUserSubscriptionStatusChanged(player: Player, subscriptionId: string) if subscriptionId == SUBSCRIPTION_ID then checkSubStatus(player) end end Players.PlayerAdded:Connect(checkSubStatus) Players.UserSubscriptionStatusChanged:Connect(onUserSubscriptionStatusChanged) ``` ### Replace a pass with a subscription One option for rolling out subscriptions in your experience is to replace an existing pass with a subscription. This is a great option if you want to quickly implement subscriptions without adding new features or awards. When replacing a pass with a subscription, keep the following in mind: - Any existing holders of the pass should continue to receive the benefit they paid for. - The pass should be taken off sale so that new users can purchase the subscription instead. - Subscriptions can be revoked, which means if your pass previously persisted its benefits to a data store, you need to "undo" these benefits. This consideration does not apply to all pass implementations, but might apply to more complex ones. The following code sample replaces a pass with a subscription: ```lua -- Run this code on the server local MarketplaceService = game:GetService("MarketplaceService") local Players = game:GetService("Players") local LEGACY_GAME_PASS_ID = 1234 -- Replace with the ID of the pass being replaced by a subscription local SUBSCRIPTION_ID = "EXP-11111111" -- Replace with your subscription ID local function awardBenefit(player: Player) -- You should award the subscription here end local function revokeBenefitIfGranted(player: Player) -- This method is called for every player who does _not_ have the subscription -- If your code saves subscriptions to Data Stores or provides some benefit that needs to be 'revoked' -- you should use this method to handle the revocation end local function checkSubscriptionStatus(player: Player) local success, result = pcall(function() return MarketplaceService:GetUserSubscriptionStatusAsync(player, SUBSCRIPTION_ID) end) if not success then print(\`Error fetching subscription status: {result}\`) return end if result.IsSubscribed then awardBenefit(player) else revokeBenefitIfGranted(player) end end local function onPlayerAdded(player: Player) local success, result = pcall(function() return MarketplaceService:UserOwnsGamePassAsync(player.UserId, LEGACY_GAME_PASS_ID) end) if not success then print(\`Error fetching pass status: {result}\`) return end if result then -- If the player has purchased the legacy pass, we do not need to look up their subscription status -- as they have the benefit granted for life awardBenefit(player) return end checkSubscriptionStatus(player) end local function onUserSubscriptionStatusChanged(player: Player, subscriptionId: string) if subscriptionId == SUBSCRIPTION_ID then checkSubscriptionStatus(player) end end local function onPromptGamePassPurchaseFinished(player: Player, purchasedPassID: number, purchaseSuccess: boolean) if purchaseSuccess and purchasedPassID == LEGACY_GAME_PASS_ID then awardBenefit(player) end end Players.PlayerAdded:Connect(onPlayerAdded) Players.UserSubscriptionStatusChanged:Connect(onUserSubscriptionStatusChanged) -- We will continue to listen for pass purchases in case the pass is still on sale MarketplaceService.PromptGamePassPurchaseFinished:Connect(onPromptGamePassPurchaseFinished) ``` ### Prompt subscription purchases Users can purchase subscriptions directly from the **Store** tab of your experience details page, but you should also add a way for them to purchase subscriptions in-game. When you prompt a subscription purchase, `Class.Players.UserSubscriptionStatusChanged|UserSubscriptionStatusChanged` fires if the player already owns the subscription, which helps catch scenarios where a player purchases a subscription from the **Store** tab of your experience details page while they are already in-game. Although you can prompt a subscription purchase from the client, checking if a user already has a subscription with `Class.MarketplaceService.GetUserSubscriptionStatusAsync|GetUserSubscriptionStatusAsync` must be done from the server. > **Warning:** When adding subscriptions to your experience, make sure to only offer them in supported regions and platforms. If not, users in unsupported regions and platforms will be able to see the offering but will be unable to complete the purchase. The following code sample creates a `RemoteFunction` that the client can use to request the status of a subscription: ```lua --This code should run on the server local ReplicatedStorage = game:GetService("ReplicatedStorage") -- Create a RemoteFunction that the client can use to request the subscription status local getSubscriptionStatusRemote = Instance.new("RemoteFunction") getSubscriptionStatusRemote.Name = "GetSubscriptionStatus" getSubscriptionStatusRemote.Parent = ReplicatedStorage getSubscriptionStatusRemote.OnServerInvoke = function(player: Player, subscriptionId: string) assert(typeof(subscriptionId) == "string") return MarketplaceService:GetUserSubscriptionStatusAsync(player, subscriptionId) end ``` ```lua -- This code should run on the client local MarketplaceService = game:GetService("MarketplaceService") local Players = game:GetService("Players") local SUBSCRIPTION_ID = "EXP-11111111" -- Replace with your subscription ID local getSubscriptionStatusRemote = ReplicatedStorage:WaitForChild("GetSubscriptionStatus") local purchaseSubscriptionButton = script.Parent.PromptPurchaseSubscription -- Replace with your button local function playerHasSubscription() -- Note if your subscription is replacing a pass, you will need to check if the pass is owned here too local success, result = pcall(function() return getSubscriptionStatusRemote:InvokeServer(SUBSCRIPTION_ID) end) if not success then print(\`Error fetching subscription status: {result}\`) return end return result.IsSubscribed end -- Hides the button if the player already has the subscription local function hideButtonIfPlayerHasSubscription() if playerHasSubscription() then purchaseSubscriptionButton.Visible = false end end local function onPromptSubscriptionPurchaseFinished(player: Player, subscriptionId: string, didTryPurchasing: boolean) if didTryPurchasing then -- It can take a while for the subscription to be registered, so we will check to see if the purchase went through in 10 seconds -- and hide the button if it has task.delay(10, hideButtonIfPlayerHasSubscription) end end -- If the player already has the subscription, we don't want to show the button at all hideButtonIfPlayerHasSubscription() purchaseSubscriptionButton.Activated:Connect(function() MarketplaceService:PromptSubscriptionPurchase(Players.LocalPlayer, SUBSCRIPTION_ID) -- If the player already has the subscription, hide the button hideButtonIfPlayerHasSubscription() end) MarketplaceService.PromptSubscriptionPurchaseFinished:Connect(onPromptSubscriptionPurchaseFinished) ``` ### Other relevant functions Other relevant functions available in `Class.MarketplaceService` are: - `Class.MarketplaceService:GetSubscriptionProductInfoAsync()|GetSubscriptionProductInfoAsync`, which returns the product information of a subscription. Includes whether the subscription is priced in Robux or local currency. - `Class.MarketplaceService:GetUserSubscriptionPaymentHistoryAsync()|GetUserSubscriptionPaymentHistoryAsync`, which returns a user's subscription payment history. - `Class.MarketplaceService:GetUserSubscriptionStatusAsync()|GetUserSubscriptionStatusAsync`, which returns a user's subscription status. ## Earn with subscriptions For **subscriptions priced in local currency**, you earn Robux at a rate of US $0.01 to 1 Robux according to the base platform price you selected, after platform fees. You earn **70% of the subscription value in the first month**, and **100% from the second month onward**. This revenue split is consistent across all platforms. For example, if your monthly subscription is priced at USD $5: - **First month:** You earn 350 Robux (`500 * 0.7 = 350`) - **Second month onward:** You earn the full amount of 500 Robux (`500 * 1.0 = 500`) For **subscriptions priced in Robux**, you earn **70% of the subscription value every month**. This revenue split is consistent across all platforms. For example, if your monthly subscription is priced at 500 Robux: - **First month:** You earn 350 Robux (`500 * 0.7 = 350`) - **Second month onward:** You continue to earn 350 Robux (`500 * 0.7 = 350`) Earnings from subscriptions in local currency are subject to a 30-day hold period and will be added to your Robux balance after the full term of the subscription has been delivered. Earnings from subscriptions in Robux follow the same hold period as passes and developer products, which is approximately 5 days. > **Warning:** Subscriptions are currently ineligible to be cross-sold by other experiences and are ineligible for affiliate fees. ### Refunds > **Info:** Subscriptions purchased with Robux are not eligible for refunds. For subscriptions in local currency, if a user requests a refund through their bank or app store within the 30-day hold period, the hold will be canceled and you will not receive the payout for that transaction. Refunds received outside the hold window will result in the payout amount for the refunded transaction being deducted from your Robux balance. If your experience is owned by a Group and the Group's balance is less than the amount to be deducted, the remainder will be deducted from the Group Owner's Robux balance. When users cancel or fail to renew their active subscriptions, they do not automatically receive refunds. They must manually request refunds, which are handled on a case-by-case basis. For more information on the user subscription refund process, check out the [help center](https://en.help.roblox.com/hc/en-us/articles/20292312961556-Getting-Started-How-To-Subscribe). ### Payouts You can find your subscription payouts in **Robux Balance** ⟩ **My Transactions**. **Subscription** and **Group Subscription** payouts appear under **Incoming** Robux, while refunds are grouped under **Outgoing** Robux as a **Payout Reversal**. The subscriptions and group subscriptions subpages provide additional information for their respective categories. To view refunding information for individual users, use `Class.MarketplaceService:GetUserSubscriptionPaymentHistoryAsync()|GetUserSubscriptionPaymentHistoryAsync`. ## Subscription analytics **Subscription analytics** help you gauge the success of individual subscriptions, identify trends, and forecast potential future earnings. Subscriptions are added to the subscriptions analytics page after the first time they're activated and are not removed when they're deactivated or deleted. You can access ready-made analytics through the Creator Dashboard, or you can capture subscription updates in real time using webhooks. ### Through the Creator Dashboard To access subscription analytics on the Creator Dashboard: 1. Go to your [Creations](https://create.roblox.com/dashboard/creations) page on **Creator Dashboard** and select your experience. 2. Go to **Monetization** ⟩ **Subscriptions**. 3. Select the **Analytics** tab. Subscription analytics track the following metrics: - **Subscriptions:** The total amount of active subscriptions in your experience. - **Estimated revenue:** The net revenue earned by experiences after fees. Does not include refund information. - **Subscriber breakdown:** The different types of subscribers. - **New**: The number of subscription purchases by first-time subscribers. - **Renewed**: The number of renewing subscriptions purchased in a prior period. - **Resurrected**: The number of new subscriptions purchased by users who had previously canceled. - **Cancellations**: The number of subscriptions that were not renewed. This includes all cancellations regardless of trigger, be it user cancellation, subscription deactivation by the creator, or by other means. - Cancellations are different from refunds. Cancelled subscriptions are subscriptions that will no longer renew but are paid in full for the remainder of the billing cycle, whereas refunds return the paid subscription amount to the user. - **Subscriptions by platform**: The number of subscriptions purchased on each platform. - **Platform earnings**: The net revenue earned through subscriptions purchased on each platform. This information is visible in the form of charts based on a date range under the **Trends** tab, and as a comprehensive list of total subscriptions offered under the **History** tab. ### Through webhooks The Cloud API Webhook feature includes triggers for four subscription events: cancelled, purchased, refunded and renewed. These notifications fire immediately, so you can respond in real time or create your own custom analytics. For more information on how to set up a webhook, see [Webhook notifications](/docs/en-us/cloud/webhooks/webhook-notifications.md).
--- title: "Ad integrations" url: /docs/en-us/production/promotion/ad-integrations last_updated: 2026-06-29T19:34:05Z description: "Ad integrations are advertisements that aren't part of Roblox's built-in ad products and that promote products or services outside of an game or off Roblox." --- # Ad integrations **Ad integrations** are ads that you incorporate into your game, whether through custom content or branded assets. These can include: - **Custom ad content** that doesn't use Roblox ad formats, like immersive billboards, portals, or rewarded video. - **In-game self-promotional assets that count as advertising**, like brand partnerships or promoting your own off-platform products and services. This includes creator-owned merchandise related to your game (for example, apparel or physical items featuring your game or characters). ## Is your content advertising? Roblox classifies content as an ad if the content: - Is published in exchange for any form of compensation to promote a brand, product, service, event, or persona. - Includes calls to action or promotes demand, attributes, information, or availability for specific off-platform products, services, events, or personas. If any of your assets meet either of these criteria, you must register, label, and submit them through the ad integration registration process, even if you're promoting your own content inside your game. For more information on what counts as advertising, see the Advertising section in the [Roblox Community Standards](https://about.roblox.com/community-standards). ## Requirements To run ad integrations in your game, you must: - Make sure all ad integrations comply with the [Community Standards](https://about.roblox.com/community-standards) and [Advertising Standards](https://en.help.roblox.com/hc/en-us/articles/13722260778260-Advertising-Standards). - Submit all assets associated with your ad integration campaign to Roblox for review and approval before exposing them to users. - Clearly and prominently disclose when content is an ad using simple, easy-to-understand language. - Not include misleading or deceptive content to encourage user engagement (for example, offering free Robux). - Only show [rewarded video ads](/docs/en-us/production/promotion/rewarded-video-ads.md) to users 13 and older. > **Warning:** Any violation of these requirements may lead to moderation actions, including (but not limited to) account suspension, removal of content, or loss of eligibility to publish ads. ## Submit your ad integration Running an ad integration is simple and involves four key steps: - [Campaign registration in Ads Manager](#campaign-registration) - [Asset creation](#asset-creation) - [Asset deployment](#asset-deployment) - [Asset submission and moderation](#asset-submission-and-moderation) This process helps make sure that your users receive clear and appropriate disclosures about what content is an ad, why they're seeing it, and who is responsible for it. ### Campaign registration To register your ad integration campaign: 1. Go to the [Ads Manager](https://ads.roblox.com) portal while signed into your Roblox account. 2. Go to **Ad integrations**. 3. Click **Register**. 4. Under **Integration details**: 1. Select the game you want to add the integration to. 2. Enter the name of the advertiser. This is the name users see in ad disclosures within your game. 3. Select an advertiser category from the drop-down options. If your advertiser doesn't fit into any of the available categories, select **None of the above**. The moderation team will review your submission and may update the category if needed. 4. Select a start and end to the campaign. 5. Enter a name for the campaign. 5. Under **Assets**: 1. Click **Manage assets**. 2. Enter the IDs for all individual assets or models included in the ad campaign. If you haven't created them yet, you can register the campaign without assets and [add them later](#asset-submission-and-moderation). 3. Click **Save**. 6. Click **Register**. As you fill out the form, the **Your revenue share** box updates with an estimated revenue share cost for the campaign. For more details, see [Revenue share](#revenue-share). Before the campaign begins, you can edit the start date, end date, campaign name, and advertiser name. Once the campaign starts, only the end date can be updated. We recommend registering campaigns as early as possible and submitting them at least 5 days before the campaign start date. ### Asset creation You can create ad integration assets before or after registering a campaign. To associate assets with a campaign, [create them in Studio](/docs/en-us/projects/assets.md) and use their asset ID during the campaign registration. If you register a campaign without assets, make sure to edit the campaign and associate the assets before the campaign goes live. ### Asset deployment Before an ad integration campaign can go live, make sure all registered and moderated assets are implemented in your game. You must check user eligibility and add the required ad labeling and disclosure. #### User eligibility Check whether users are eligible to see the ad assets using `Class.AdService.GetCampaignEligibilityAsync|GetCampaignEligibilityAsync`. ```lua local success, result = pcall(function() return AdService:GetCampaignEligibilityAsync("your-campaign-id") end) if success and result.IsEligible then print("The local player is eligible to see your campaign!") else print("The local player is not eligible— your campaign may not be shown to this player.") end ``` If you update your game with ad assets before the campaign start date, `Class.AdService.GetCampaignEligibilityAsync|GetCampaignEligibilityAsync` will return a rejection until the campaign begins. #### Labeling and disclosure All ad assets have to include clear and accessible disclosure for users. To support this, Roblox provides standardized ad labels that are automatically rendered in games for assets associated with the ad integration campaign. Ad labels indicate which parts of your experience are advertisements, and allow users to identify the advertiser or report the ad. `Class.AdService.RegisterDisclosureButton|RegisterDisclosureButton` enables interactive disclosure when a user clicks or taps the ad label, providing additional information about the ad. You can find the placement ID from Ads Manager in the **Manage Assets** section. For example: ```lua AdService:RegisterDisclosureButton(yourDisclosureButton, "your-placement-id") ``` Choose the right label type based on how your ad content appears in your experience and how users interact with it. Different label types work better for different contexts. | **Label type** | **Description** | **Example** | | --- | --- | --- | | **Floating ad label** | Appears above, beside, or near an interactive 3D object in your experience.

Use it for freestanding, movable, or standalone sponsored objects, like branded vehicles, product displays, or charaacters within the environment. | | | **HUD-style ad label** | A 2D label placed on the user's screen overlay or control layer.

Use it for environments or areas where the entire location makes up the ad, like a branded store, a sponsored zone, or a virtual exhibition. | | | **Sticker-style ad label** | Directly attached or embedded into the surface texture of a 3D object.

Use it for flat or stationary surfaces featuring branded content, like sponsored billboards, branded wall murals, columns, or architectural elements. | | When placing ad labels in your experience, make sure to: - Keep the ad label distinct and readable. The label should not be covered, distorted, or blended into surrounding visuals. - Avoid letting nearby branding or decorative elements visually overpower the ad label. The disclosure should be the primary focus in its immediate area. - Any aesthetic adjustments, like size or opacity, should not reduce clarity or make the label harder to recognize, interact with, or understand. - When using subtle or highly stylized branding, make the ad label especially clear and visible on first encounter. ### Asset submission and moderation To submit additional assets or models to an existing ad integration campaign: 1. In Ads Manager, go to **Ad integrations**. 2. Click **⋮** ⟩ **Manage assets** next to the relevant campaign. 3. Enter the IDs of the individual assets or models you want to add to the campaign. 4. Click **Save**. Roblox reviews and moderates all submitted assets, and [their status](#asset-status) directly affects your [campaign status](#campaign-status). Moderation is typically completed within 48 hours. > **Info:** If you add assets to an active campaign, the entire campaign will only be served to users aged 18 and over while the new assets are under review. To avoid this, create separate campaigns with fixed start and end dates for each asset drop. #### Campaign status To check your campaign status, go to the **Ad integrations** page. | **Status** | **Description** | | --- | --- | | **Approved** | The campaign has been approved for all audiences. | | **Completed** | The campaign has reached its end date. | | **In review** | The campaign is under review by the moderation team. While it's under review, the campaign is only shown to users aged 18 and over. | | **Limited** | The campaign or an asset associated with the campaign has been approved for certain countries and age groups. | | **Paused** | The campaign has been manually stopped from serving ads. | | **Rejected** | The campaign has failed moderation review. This means that at least one registered asset violates the Roblox advertising standards. | #### Asset status Roblox also reviews and moderates all assets associated with a campaign. To check the status of your assets, go to the **Ad integrations** page and click **⋮** ⟩ **Manage assets** next to the relevant campaign. | **Status** | **Description** | | --- | --- | | **In review** | The asset is under review by the moderation team. | | **Limited** | The asset has been partially approved for certain countries and age groups. You can appeal this status. | | **Rejected** | The asset has failed moderation review. You can appeal this status. | To view the reason an asset was assigned a **Rejected** or **Limited** status, click **⋮** ⟩ **Appeal status** next to the asset. You can appeal the status and resubmit the asset for moderation by clicking **Appeal**. ## Ad integration tags Ad integration tags categorize content based on compliance with [Community](https://about.roblox.com/community-standards) and [Advertising Standards](https://en.help.roblox.com/hc/en-us/articles/13722260778260-Advertising-Standards). During the moderation process, each ad asset is assigned one or more tags that determine the eligible audience for the campaign. Tags have one of the following serving outcomes: - **Rejected**: The entire campaign is prohibited under the Advertising Standards or contains content that violates the Community Standards, and can't be served to users. - **Limited serving**: The entire campaign can only be served to specific users, and might be restricted by region or age. - **Full serving (Approved)**: The entire campaign is approved and can be served to all relevant users. **Campaign eligibility is determined by the most restrictive asset label applied to any of your registered assets.** If one asset is rejected, `Class.AdService.GetCampaignEligibilityAsync|GetCampaignEligibilityAsync` returns `false` for all users. For a full list of prohibited, regulated, and restricted advertising content, see the [Advertising Standards](https://en.help.roblox.com/hc/en-us/articles/13722260778260-Advertising-Standards#prohibited-advertising-content). ## Revenue share > **Info:** Starting January 1, 2027, ad integration campaigns will be subject to a revenue share based on qualified visits. This revenue share applies to all campaigns that begin on or after January 1, 2027. > > Revenue share costs do not apply to: - Brands labeling their own assets as ads within games they own. - Other aspects of a partnership outside the ad integration itself, such as influencer arrangements. ### Qualified visits For active ad integration campaigns, revenue share costs are calculated based on qualified visits. A **qualified visit** is a session in which a user engages with one or more branded assets from your campaign. Qualified visits match what Roblox reports to brands as ad engagements, based on Interactive Advertising Bureau (IAB) standards for time-in-view, occlusion, and size. If a user engages with multiple branded assets during the same session, that session counts as a **single qualified visit**. If the same user returns in a separate session and engages with a branded asset again, that session counts as a **new qualified visit**. > **Info:** Reporting capabilities for ad integration campaigns will be available later this year. ### Cost per 1,000 visits (CPTV) Cost per 1,000 visits (CPTV) represents the amount Roblox charges you for qualified visits. Rates vary by region and align with common advertising industry pricing models. As a general guideline, creators typically charge brands an average CPTV of $4 to $5 globally. Integrations that deliver higher engagement, longer time spent, or stronger outcomes may charge higher rates. The following table shows CPTV rates for the first 28 days of a campaign: | **Tier** | **Markets** | **CPTV (first 28 days)** | | --- | --- | --- | | **US** | United States | $1.50 | | **Tier 1** | United Kingdom, Canada, Australia, New Zealand, Nordics | $0.75 | | **Tier 2** | Western Europe, Japan, South Korea | $0.20 | | **Tier 3** | Global floor | $0.05 | After the first 28 days, all qualified visits are billed at a flat CPTV rate of $0.10 for the rest of the campaign, up to a maximum campaign duration of 12 months. For the complete list of countries in each tier, see [Markets](#markets). ### Markets The following markets are grouped into tiers for CPTV pricing. The United States has its own tier and isn't listed below. **#### Tier 1 markets** | **Region** | **Country code** | **Country** | | --- | --- | --- | | APAC | AU | Australia | | APAC | NZ | New Zealand | | EMEA | DK | Denmark | | EMEA | FI | Finland | | EMEA | IS | Iceland | | EMEA | IE | Ireland | | EMEA | IM | Isle of Man | | EMEA | MT | Malta | | EMEA | NL | Netherlands | | EMEA | NO | Norway | | EMEA | SJ | Svalbard and Jan Mayen | | EMEA | SE | Sweden | | EMEA | CH | Switzerland | | EMEA | GB | United Kingdom | | US & Canada | AS | American Samoa | | US & Canada | CA | Canada | | US & Canada | NU | Niue | **#### Tier 2 markets** | **Region** | **Country code** | **Country** | | --- | --- | --- | | EMEA | AD | Andorra | | EMEA | AT | Austria | | EMEA | BE | Belgium | | EMEA | HR | Croatia | | EMEA | CY | Cyprus | | EMEA | FO | Faroe Islands | | EMEA | FR | France | | EMEA | DE | Germany | | EMEA | GI | Gibraltar | | EMEA | GR | Greece | | EMEA | GL | Greenland | | EMEA | GG | Guernsey | | EMEA | HK | Hong Kong | | EMEA | IT | Italy | | EMEA | JP | Japan | | EMEA | JE | Jersey | | EMEA | LI | Liechtenstein | | EMEA | LU | Luxembourg | | EMEA | MC | Monaco | | EMEA | PT | Portugal | | EMEA | SM | San Marino | | EMEA | KR | South Korea | | EMEA | ES | Spain | | EMEA | TW | Taiwan | **#### Tier 3 markets** | **Region** | **Country code** | **Country** | | --- | --- | --- | | APAC | BN | Brunei | | APAC | KH | Cambodia | | APAC | CN | China | | APAC | FJ | Fiji | | APAC | ID | Indonesia | | APAC | LA | Laos | | APAC | MO | Macao | | APAC | MY | Malaysia | | APAC | MV | Maldives | | APAC | FM | Micronesia | | APAC | MM | Myanmar (Burma) | | APAC | PH | Philippines | | APAC | SG | Singapore | | APAC | TH | Thailand | | APAC | TL | Timor-Leste | | APAC | VN | Vietnam | | EMEA | AF | Afghanistan | | EMEA | AL | Albania | | EMEA | DZ | Algeria | | EMEA | AO | Angola | | EMEA | AM | Armenia | | EMEA | AZ | Azerbaijan | | EMEA | BH | Bahrain | | EMEA | BD | Bangladesh | | EMEA | BY | Belarus | | EMEA | BJ | Benin | | EMEA | BT | Bhutan | | EMEA | BA | Bosnia and Herzegovina | | EMEA | BW | Botswana | | EMEA | BG | Bulgaria | | EMEA | BF | Burkina Faso | | EMEA | BI | Burundi | | EMEA | CV | Cabo Verde | | EMEA | CM | Cameroon | | EMEA | TD | Chad | | EMEA | KM | Comoros | | EMEA | CI | Cote d'Ivoire | | EMEA | CZ | Czechia | | EMEA | CD | Democratic Republic of the Congo | | EMEA | DJ | Djibouti | | EMEA | EG | Egypt | | EMEA | GQ | Equatorial Guinea | | EMEA | EE | Estonia | | EMEA | SZ | Eswatini | | EMEA | ET | Ethiopia | | EMEA | PF | French Polynesia | | EMEA | GA | Gabon | | EMEA | GE | Georgia | | EMEA | GH | Ghana | | EMEA | GU | Guam | | EMEA | GN | Guinea | | EMEA | GW | Guinea-Bissau | | EMEA | HU | Hungary | | EMEA | IN | India | | EMEA | IQ | Iraq | | EMEA | IL | Israel | | EMEA | JO | Jordan | | EMEA | KZ | Kazakhstan | | EMEA | KE | Kenya | | EMEA | KI | Kiribati | | EMEA | XK | Kosovo | | EMEA | KW | Kuwait | | EMEA | KG | Kyrgyzstan | | EMEA | LV | Latvia | | EMEA | LB | Lebanon | | EMEA | LS | Lesotho | | EMEA | LR | Liberia | | EMEA | LY | Libya | | EMEA | LT | Lithuania | | EMEA | MG | Madagascar | | EMEA | MW | Malawi | | EMEA | ML | Mali | | EMEA | MH | Marshall Islands | | EMEA | MR | Mauritania | | EMEA | MU | Mauritius | | EMEA | YT | Mayotte | | EMEA | MD | Moldova | | EMEA | MN | Mongolia | | EMEA | ME | Montenegro | | EMEA | MA | Morocco | | EMEA | MZ | Mozambique | | EMEA | NA | Namibia | | EMEA | NR | Nauru | | EMEA | NP | Nepal | | EMEA | NC | New Caledonia | | EMEA | NE | Niger | | EMEA | NG | Nigeria | | EMEA | MK | North Macedonia | | EMEA | MP | Northern Mariana Islands | | EMEA | OM | Oman | | EMEA | PK | Pakistan | | EMEA | PW | Palau | | EMEA | PS | Palestine | | EMEA | PG | Papua New Guinea | | EMEA | PL | Poland | | EMEA | QA | Qatar | | EMEA | CG | Republic of the Congo | | EMEA | RE | Reunion | | EMEA | RO | Romania | | EMEA | RW | Rwanda | | EMEA | WS | Samoa | | EMEA | ST | Sao Tome and Principe | | EMEA | SA | Saudi Arabia | | EMEA | SN | Senegal | | EMEA | RS | Serbia | | EMEA | SC | Seychelles | | EMEA | SL | Sierra Leone | | EMEA | SK | Slovakia | | EMEA | SI | Slovenia | | EMEA | SB | Solomon Islands | | EMEA | SO | Somalia | | EMEA | ZA | South Africa | | EMEA | SS | South Sudan | | EMEA | LK | Sri Lanka | | EMEA | SD | Sudan | | EMEA | TJ | Tajikistan | | EMEA | TZ | Tanzania | | EMEA | GM | The Gambia | | EMEA | TG | Togo | | EMEA | TO | Tonga | | EMEA | TN | Tunisia | | EMEA | TR | Turkiye | | EMEA | TM | Turkmenistan | | EMEA | TV | Tuvalu | | EMEA | UG | Uganda | | EMEA | UA | Ukraine | | EMEA | AE | United Arab Emirates | | EMEA | UZ | Uzbekistan | | EMEA | VU | Vanuatu | | EMEA | WF | Wallis and Futuna | | EMEA | EH | Western Sahara | | EMEA | YE | Yemen | | EMEA | ZM | Zambia | | EMEA | ZW | Zimbabwe | | LATAM | AI | Anguilla | | LATAM | AG | Antigua and Barbuda | | LATAM | AR | Argentina | | LATAM | AW | Aruba | | LATAM | BB | Barbados | | LATAM | BZ | Belize | | LATAM | BM | Bermuda | | LATAM | BO | Bolivia | | LATAM | BR | Brazil | | LATAM | VG | British Virgin Islands | | LATAM | BQ | Caribbean Netherlands | | LATAM | KY | Cayman Islands | | LATAM | CL | Chile | | LATAM | CO | Colombia | | LATAM | CK | Cook Islands | | LATAM | CR | Costa Rica | | LATAM | CW | Curacao | | LATAM | DM | Dominica | | LATAM | DO | Dominican Republic | | LATAM | EC | Ecuador | | LATAM | SV | El Salvador | | LATAM | GF | French Guiana | | LATAM | GD | Grenada | | LATAM | GP | Guadeloupe | | LATAM | GT | Guatemala | | LATAM | GY | Guyana | | LATAM | HT | Haiti | | LATAM | HN | Honduras | | LATAM | JM | Jamaica | | LATAM | MQ | Martinique | | LATAM | MX | Mexico | | LATAM | MS | Montserrat | | LATAM | NI | Nicaragua | | LATAM | PA | Panama | | LATAM | PY | Paraguay | | LATAM | PE | Peru | | LATAM | PR | Puerto Rico | | LATAM | BL | Saint Barthelemy | | LATAM | KN | Saint Kitts and Nevis | | LATAM | LC | Saint Lucia | | LATAM | MF | Saint Martin | | LATAM | PM | Saint Pierre and Miquelon | | LATAM | VC | Saint Vincent and the Grenadines | | LATAM | SX | Sint Maarten | | LATAM | SR | Suriname | | LATAM | BS | The Bahamas | | LATAM | TT | Trinidad and Tobago | | LATAM | TC | Turks and Caicos Islands | | LATAM | VI | U.S. Virgin Islands | | LATAM | UY | Uruguay | | LATAM | VE | Venezuela | ### FAQ **Will I be billed for every visit to a branded integration?** Each qualified visit is billed once per session. If a user engages with 10 branded assets during a single session, it counts as 1 qualified visit. If the same user returns across 10 separate sessions and engages with a branded asset each time, it counts as 10 qualified visits. This approach is consistent with other advertising formats, where the same user can generate multiple engagements over time. **Will I always be charged the maximum cost?** No. You're charged the lower of: - The actual cost calculated from your branded visits. - Your locked-in maximum cost. **What are the invoicing and payment terms?** Roblox issues invoices at the end of each quarter for campaigns that ended during that quarter. Payment is due within 90 days of the invoice date, giving you time to receive payment from the brand before paying Roblox. **What happens if I change the dates of my campaign?** If you change your campaign dates, the additional cost is calculated using the same rate as your original locked-in maximum cost. For example, if you lock in a maximum cost of $5,000 for a 7-day campaign and then extend the campaign to 14 days, your maximum cost increases to $10,000. For campaign days beyond the initial 28-day pricing period, qualified visits are billed at the flat CPTV rate of $0.10. **What are some options for reducing the actual cost of my planned campaign?** If your estimated campaign cost is higher than the amount you're able to charge the brand, consider adjusting the campaign configuration to reduce costs: - **Reduce the number of campaign days.** Shortening the campaign duration is the simplest way to reduce costs. - **End the campaign after reaching a target number of impressions.** You can toggle the campaign off in Ads Manager at any time. While a campaign is off, `Class.AdService.GetCampaignEligibilityAsync|GetCampaignEligibilityAsync` returns `false` for all users. **Is there a maximum cost for deals?** Yes. All campaigns in 2027 have a hard cap of $400,000.
--- title: "Ads Manager" url: /docs/en-us/production/promotion/ads-manager last_updated: 2026-06-29T19:34:05Z description: "The Ads Manager is where you can manage ad campaigns, ads reporting, and ads billings and payments in one place." --- # Ads Manager The **Ads Manager** offers you control over your ad campaigns, empowering you to create, optimize, and measure ads effectively while reaching your campaign objectives. You can use this tool to manage your ad campaigns, ads reporting, and ads billings in one place. ## Set up an ad account To access the Ads Manager, you must set up an ad account with a verified email on a Roblox account registered for users aged 13 years or older. When setting up an account, you can choose between creating a personal or a business ads account. A personal account is for individuals who want to advertise on Roblox, while a business account manages a company's ad presence on Roblox. To set up an ad account: 1. Create an ad account. 1. Go to the [Ads Manager](https://ads.roblox.com) portal while signed into your Roblox account and click **Create ad account**. 2. Choose a personal account or a business account. 3. Check the box to confirm your information and click **Create ad account**. 2. Add a payment method to your ad account. 1. Go to **Bills & Payments** > **Payment Settings**. 2. Click **Add Payment Method**. 3. Attach a credit or debit card to your account or buy ad credits. - To add a card to your account, select the **Card** tab, enter your information, and click **Save and Authenticate**. This option is available for users 18 and above. A temporary $1.00 USD hold will be placed on the card and refunded after verification is complete within 7 business days. - To buy ad credits, select the **Robux Ad Credit** tab, enter the amount of credits you want to purchase, and click **Buy**. You must buy a minimum of 1 ad credit. Converting Robux into ad credits to fund your campaigns is available for all users 13 and up. ##### Robux to ad credit conversion > **Error:** Converting Robux to ad credit is a permanent and irreversible action. Once you convert Robux to ad credits, you can only spend your ad credits on ad campaigns within the Ads Manager. Any Roblox user aged 13 and above can convert Robux to ad credits and use it for running ads. This accessibility ensures that a wide range of creators can participate in the advertising ecosystem. When converting Robux to ad credits, consider the following: - 1 ad credit is equivalent to 263 Robux. - 1 ad credit is the minimum conversion requirement. This minimum conversion requirement streamlines the conversion process, prevents inaccuracies, and makes sure that you have enough ad credits to support meaningful advertising campaigns. ## Manage your ads Use the Ads Manager to create and manage ad campaigns for your games. With this tool, you can set budgets, select creatives, and control delivery settings while monitoring and adjusting your campaigns over time. ### Ad campaign components Ad campaigns are made up of a few core components that control how your ads are delivered, how much you spend, and what players see. ##### Goals Each ad campaign has a single goal that determines who Roblox shows the campaign to and how its performance is measured. Choose the goal that matches what you're trying to achieve with the campaign. - **Plays** The **Plays** objective reaches players who are most likely to start a session in your game. Use it when you want broad distribution, are testing new ideas with a wide audience, or want initial traffic to help recommendation systems learn what's working and qualify your game for Recommended For You. - **Earnings** (Limited) The Earnings objective reaches players who are most likely to spend Robux in your game. Use it to increase earnings by acquiring new high-value players, especially if your game has a well-developed in-game economy. - **Engagement** The Engagement objective reaches age-checked highly engaged players whose sessions count toward your highly engaged player threshold for [Roblox Kids and Select](/docs/en-us/production/publishing/kids-and-select.md). Use it if you want a more direct path toward meeting the threshold. Many games reach the threshold organically over time, so using the Engagement objective is optional. When using the Engagement objective, keep the following in mind: - **Expect a higher cost per play (CPP).** The highly engaged player audience is smaller than the Plays audience, which typically results in a higher CPP. - **Use a dedicated budget.** Consider Engagement as a separate investment if your goal is to make progress toward the Roblox Kids and Select threshold, instead of shifting spend from Plays campaigns. - **Track your progress.** Engagement campaigns do not automatically pause when you reach your Highly Engaged Player threshold. Track your progress and pause the campaign when you reach your goal. - **Allow time for results.** Most campaigns require several days to produce meaningful results. If you don't see progress after 3–5 days, consider improving your game's onboarding and first-time user experience before continuing. ##### Automated bidding With auto-bidding, you set an ad budget and duration and Roblox automatically calculates the bid that will get you the most plays at the lowest cost. ##### Budgets When you create an ad campaign, you must set budgeting parameters to control campaign spending. There are two different types of budgets: - A **daily budget** is the maximum amount you pay for your ads per day. - A **lifetime budget** is the maximum amount you pay for your ads for the entire duration of the campaign. You can pay for campaign budgets using either your debit or credit card on file, or your existing ad credit balance. If you fund a campaign with ad credits, you can't modify it after it's published. If you have unused ad credits after your campaign ends, those credits are refunded to your account. During campaign creation, you can enable **auto-reload** to automatically purchase ad credits when your account runs out. When auto-reload is triggered, Roblox reloads one day's worth of ad credits, and continues to reload daily as needed to keep your campaign running. ##### Creatives An ad creative is the thumbnail that shows up on a player's Home page when you sponsor a game. You can select up to 10 thumbnails for your ad campaign, which are then evenly distributed across players' Home pages. After you create a campaign, you can edit the campaign to turn specific thumbnails on and off. You can use existing thumbnail images you have previously uploaded to the Creator Hub, or upload new ad-specific images with the Ads Manager. Any image you use as a thumbnail is resized to a ratio of 16:9 when displayed on the Home page. ### Create an ad campaign > **Info:** Your ad campaign is automatically shown in both the Home page and search results. > **Warning:** Ad campaigns for games that are not unique can result in **no ad spend** and **no conversions**. > > Games with metadata and place files that closely resemble existing games on Roblox are not prioritized for recommendations and might rank lower in search results. Campaigns for these games might not be effective. To create an ad campaign: 1. Go to **Manage Ads** and click **Create Campaign**. 2. Under **Experience**: 1. Select the game you want players to join when they click on your ad. 2. Enter a name for the campaign. 3. **OPTIONAL** Select **Advanced options** to send players to a specific place in your game when they click your ad. For more information, see [Advanced join options](#advanced-join-options). 3. Under **Goal**, select an objective for the ad campaign: | **Plays** | Find the most players for your game. | | --- | --- | | (Limited) **Earnings** | Reach the players most likely to spend money inside your game. | | **Engagement** | Grow the number of highly engaged players in your game. | 4. Under **Audience**: 1. Select an audience for the ad campaign: | **All Players** | Show ad to all players, regardless of their past activity. | | --- | --- | | **New Players** | Show ad to players who have never played your game, or who haven’t played it in 180+ days. | | **Recent Players** | Show ad to players who have played your game in the last 30 days. Only available for games with 10,000 recent players or more. | | **Lapsed Players** | Show ad to players who played your game 30 to 180 days ago but haven’t returned. Only available for games with 20,000 lapsed players or more. | 2. **OPTIONAL** Under **Advanced Targeting**, narrow down your audience by customizing settings like location, age, gender, genre, and device type. 5. Under **Budget & Duration**: 1. Choose between a **Daily Budget** and a **Lifetime Budget**. 2. Select a **Payment Method**. If you're using ad credits, make sure the campaign budget does not exceed the available ad credits in your account. 3. Set a **Start Date** and a **Start Time** for your campaign. 4. Set a **Duration** for your campaign. 5. **OPTIONAL**If you're using ad credits, enable **auto-reload** to automatically add ad credits to the campaign when your budget runs out. 6. Under **Creatives**, choose up to 10 thumbnails to show players. 7. Click **Publish** and confirm submission. Your ad campaign will go live after it's approved by the moderation team. > **Info:** Roblox tries to complete moderation within 24 hours for each ad submitted. Ads that do not pass moderation show the status of **rejected**. ### Edit an ad campaign After a campaign is published, you can edit some fields while others are fixed: | **Changeable** | **Fixed** | | --- | --- | | Campaign name | Goal | | Budget amount | Audience | | Schedule | Budget type | | Creatives | | ### Cancel an ad campaign You can cancel a campaign up to 6 hours before the campaign's scheduled start time. Cancelling a campaign automatically cancels any associated ads, returns any ad credits you spent back to your account, and prevents your credit or debit card from being charged. ### Advanced join options Advanced join options let you control where users land in your game when they click your ad. You can also include launch data to identify which players joined through a specific ad campaign, allowing you to personalize a player's experience or trigger custom effects when they join. > **Info:** Launch data appears in the join URL and isn’t hidden. Users can share the URL and join your game with the same launch data, so joins using your campaign’s launch data might not exactly match your game’s sponsored tile play metrics. By identifying which players joined through a specific ad, you can: - **Tailor onboarding**: Adjust the player's introduction based on the audience targeted by the ad. For example, players from a **New Players** campaign can be shown tutorial steps, while **Lapsed Players** can receive a "Welcome back" message. - **Customize spawn locations**: Spawn players in a location related to the ad they clicked instead of the default spawn point. For example, if the ad promotes a specific map, players can spawn at the beginning of that map. - **Offer ad-only rewards**: Grant small cosmetic items or limited-time currency boosts to players who join through an ad. Exclusive rewards are a good way to increase campaign conversion. To customize your campaign's join options: 1. During campaign creation, click **Advanced options** to open the **Advanced join options** panel. 2. Select the start place you want to send players to when they click your ad. If you don't select a start place, players are sent to your game's default spawn location. 3. Set launch data parameters. 4. Use the launch URL to test the join implementation before launching the campaign. > **Info:** Both selecting a start place and setting launch data parameters are optional and independent of each other. You can customize a player's start place without using launch data, or use launch data to run custom logic while sending players to the default start place. If you set launch data parameters, you must open Roblox Studio and update `Class.Player.GetJoinData|GetJoinData` in your game's `onPlayerAdded` function to retrieve the launch data. You can then check whether a player joined through the specific ad campaign associated with those parameters and run custom logic, such as granting rewards or triggering other in-game effects. The following example updates the `onPlayerAdded` function to retrieve and check the launch data parameters you defined when creating the campaign. ```lua local Players = game:GetService("Players") local function onPlayerAdded(player) -- Retrieve the join data local launchData = player:GetJoinData().LaunchData -- Check for the launch data parameters you defined when creating the campaign in Ads Manager if launchData then if launchData == "AdLaunchData" then -- Custom logic to tailor onboarding, offer rewards, trigger effects, etc end end end Players.PlayerAdded:Connect(onPlayerAdded) ``` ## Reporting Reports provide insights into the overall effectiveness of your ad campaigns. You can use reporting data to compare performance across campaigns and identify opportunities to optimize. To view your reports, go to **Manage Ads** and select the date range and game you want to analyze. Impressions and clicks are reported in real time. Reporting on plays and earnings, however, relies on **attribution** and can include events that occur up to 30 days after an ad is served. Data can also take up to 48 hours to appear in reports. This delay is most noticeable for post-click metrics such as plays and earnings, which require additional processing and verification before credit can be assigned to the correct events. The goal of attribution is to assign credits to Ads Manager based on the role advertising plays in acquiring users and driving conversions. This helps you understand how your ad spend contributes to your growth and earnings. ### Reporting views **Reporting views** break down attribution across different types of users, helping you understand the impact of your campaigns according to their objectives. The reporting views include: | **Reporting filter** | **Who these users are** | **How attribution works** | | --- | --- | --- | | All Users | All users reached by your ads. | A combined view of all other filters, based on the user group they were in when they saw the ad. | | New Users | Users who had never visited your game before being reached by your ads. | All plays and earnings are attributed to the ad for up to 30 days after the user first joins through it.

For example, if a user joins through an ad and returns the next day through another source (such as search or continue play), Ads Manager still attributes those plays, playtime, and revenue to the ad. | | Recent Users | Users reached by your ads less than 7 days after they last played your game. | Only plays and earnings from sessions that start with an ad click are attributed to the ad. No additional attributed activity is included. | | Resurrected Users (7D) | Users reached by your ads between 7 and 29 days after they last played your game. | All plays and earnings are attributed to the ad for 7 days after the user returns through it.

For example, if a user returns through an ad and plays again the next day, Ads Manager counts both plays and includes all associated playtime in its reporting. | | Resurrected Users (30D) | Users reached by your ads 30 days or more after they last played your game. | All plays and earnings are attributed to the ad for 30 days after the user returns through it. | The following examples show you how attribution works across different user types and reporting views: - If a user last played less than 7 days ago from another source, the ad is only credited for that play and any Robux generated during that session. - If a user joins your game for the first time through an ad, their plays and Robux spend are credited to that ad for the next 30 days. - If a user comes back through an ad after not playing for at least 7 days, they're considered resurrected by the ad. Their plays and Robux spend are then credited to that ad for the next 7 days. > **Warning:** In some cases, campaign performance in the **All Users** view might appear low for objectives like retention, plays, or Robux revenue. Switch to **Recent Users** or **Resurrected Users** to better understand impact on those specific user groups. ### Metric cards The reporting metric cards include: | **Metric card** | **Description** | | --- | --- | | **Amount Spent** | The total amount of ad credits you have spent on all ad campaigns for the selected game. | | **Impressions** | The total number of times all ads associated with the selected game were shown to players. | | **Plays** | The total number of times players entered and started playing the selected game as a result of ads. | | **Playtime** | The total time players spent in the selected game over the selected date range as a result of ads. | ### Reporting table The reporting table includes: | **Field** | **Description** | | --- | --- | | **Campaign** | The name of the ad campaign. Campaigns with auto-reload enabled display an auto-reload icon next to the name. | | **Off/On** | A toggle indicating if the ad campaign is active or paused. | | **Status** | The status of the ad campaign. See the [Status](#status) table for more details. | | **Spent** | The total amount of USD or ad credits you have spent on the campaign. | | **Impressions** | The number of times your ad was shown to players. | | **Clicks** | The number of times players clicked on your ad on the Home and Search pages. | | **Plays** | The number of times players entered and started playing your game as a result of your campaign. | | **CPP (Cost-per-play)** | The average cost per play, calculated by dividing your total campaign spend by the number of plays. | | **Playtime** | How much time players spent in your game as a result of your campaign. | | **Robux Earnings** | Cumulative Robux earnings as a result of your campaign. Excludes subscriptions, engagement payouts, and immesive ads. | #### Status | **Status** | **Description** | | --- | --- | | **In Review** | Campaign has been created and will be reviewed within 24 hours. | | **Moderated** | Campaign has been moderated. This status includes a reason for the moderation. | | **Active** | Campaign is active and running as expected. | | **Learning** | The first 24 hours of an active campaign. During this time, the system continuously learns from engagement data to refine budget allocation. | | **Scheduled** | Campaign is scheduled to run at a future date. | | **Inactive** | Campaign is inactive. | | **Completed** | Campaign has reached its goal or end date. | | **Auto-Completed** | Campaign was stopped by the system after a period of time. | | **Canceled** | Campaign was manually stopped and removed from serving ads. | | **Error** | Campaign status can't be shown due to system error. | | **Rejected** | Campaign failed moderation review. | ## Billing The ads billing tool charges you for ad placements on Roblox using the payment method you have on file. To view billing information, go to **Billing & Payments** > **Payment Activity**. ##### Credit and debit cards > **Warning:** For first-time credit card users, $5 USD will be charged upon campaign submission and used toward your first bill. Any unused balance will be refunded. You can get charged at two different points when you use a credit or debit card: - When you reach your payment threshold - On the first of the month if the payment threshold isn't reached Your account has a payment threshold that increases as you spend more on ads. When your ad spend reaches this threshold, your payment method on file is charged automatically. For example, if your account has a threshold of $100 USD, your payment method is charged when your accrued ad spend reaches $100. If you don't reach the threshold by the end of the month, any remaining balance is charged at the close of the monthly billing cycle. This means that if your campaigns reach the $100 threshold on May 7, your payment method is charged $100 on May 7. If your spend stays below $100, you're charged the outstanding amount at the end of the month instead. ##### Ad credits Ad credits are deducted in real time as your campaigns spend. Once per day, the total ad credit spend across all of your campaigns is combined into a **single daily charge**, which appears in your ad credit transaction history. Until this daily charge is finalized, any ad credit spend appears as **Pending** in your transaction history. Ad credits can have the following states: - **Purchased Ad Credit:** Transactions related to converting Robux into ad credit. This transparent categorization makes sure that you can easily track your ad credit acquisition. - **Funded:** Ad credit allocated towards a campaign. Tracking this allocation allows you to monitor your campaign budget and spending. - **Unused:** Ad credit allocated to a specific campaign but not used, which is returned on the campaign end date. This feature makes sure that your ad credits are used efficiently and returned when appropriate. - **Ad Fraud Refund:** Any refunds applied back to your account. Refunds credited for a specific campaign remain locked to that campaign, not credited at the account level. ##### Ad fraud protection Roblox uses advanced detection methods for bots and other fraudulent activities to make sure that you don't pay for fraudulent traffic, and regularly adjusts billing and reporting to safeguard their interests. After 14 days from the campaign end date, campaigns are analyzed for fraudulent traffic, and transaction amounts are refunded accordingly. If invalid traffic is detected, refunds are applied 16 days after the campaign end date. This proactive approach helps maintain the integrity of your campaigns and ensures fair spending. Accounts responsible for ad fraud have their accounts suspended to prevent ongoing abuse. In addition, Roblox might suspend ad campaigns or advertiser accounts for non-payment of services properly rendered. Roblox expects you to act with integrity and follow the [advertising standards](https://en.help.roblox.com/hc/en-us/articles/13722260778260-Advertising-Standards).
--- title: "Advertise on Roblox" url: /docs/en-us/production/promotion/advertise-on-roblox last_updated: 2026-06-29T19:34:05Z description: "An overview of advertising opportunities on the platform." --- # Advertise on Roblox **Advertising on Roblox** is an opportunity to engage a global audience and drive traffic to your content. Roblox offers several different types of official advertising products you can use, and you can also independently advertise in your experiences. In either case, all advertisements on the platform must adhere to Roblox's [Advertising Standards](https://en.help.roblox.com/hc/articles/13722260778260), [Community Standards](https://en.help.roblox.com/hc/articles/203313410), and [Terms of Use](https://en.help.roblox.com/hc/articles/115004647846). ## Roblox-served ads Roblox-served ads are advertisements you can purchase and serve through Roblox's official advertising products. Almost every Roblox advertising product allows you to choose your audience, schedule the duration of your ad, and specify your budget per day. Which advertising product is right for your content depends on the type of content you're advertising and where you want to reach your audience. The following table details what type(s) of content you can advertise through each advertising product, and where the ad displays on Roblox: | Advertising product | What you're advertising | Ad location | | --- | --- | --- | | [Immersive ads](/docs/en-us/production/monetization/immersive-ads.md) | Experiences, Videos, Images | Within the **[immersive ad units](/docs/en-us/production/monetization/immersive-ads.md)** publisher place within their experiences | | [Search ads](/docs/en-us/production/promotion/ads-manager.md) | Sponsored experience tiles based off of keywords and relevance | Search results for experiences | | [Sponsored experiences](/docs/en-us/production/promotion/ads-manager.md) | All ages, 9+, 13+ experiences | Within the **Sponsored** category on the [Home](https://www.roblox.com/home) page | | [Sponsored items](/docs/en-us/marketplace/sponsor-items.md) | 3D user-generated content | Within the **Sponsored** category on any item's main details page on the Roblox website, and the **Recommended Items for You** page of the Marketplace on the Roblox app | ## Independent advertising In general, independent advertisements are advertisements that fall outside of Roblox's advertising products that promote the sale of a product or service that's either out-of-experience or off-Roblox. This type of advertisement can take many forms. If you're a brand or a developer working with outside advertisers and are unsure if your content is advertising, **it's best to consult applicable regulatory guidance and seek legal counsel as appropriate**. While not prescriptive, you may want to ask yourself the following questions as a starting point when determining if your content is advertising: - Do you consider or intend for the content to be advertising? - Were you paid to publish the content in order to promote a third-party's product or service? - Does the content promote demand for a product or service available outside the experience where the content is placed? - Does the content promote attributes of a product or service, either expressly or implicitly, such as quality, price, features, performance, attributes, or benefits? - Does the content promote where or how to purchase something available outside the experience? - Does the content contain a call to action encouraging the purchase of something available outside the experience? If your content is advertising, you are fully responsible for ensuring this content complies with all Roblox policies and any applicable local laws or regulations. For example, independent advertisements must adhere to Roblox's [Independent Advertising Policy](https://en.help.roblox.com/hc/articles/203313410#independent-advertisement-publishing), [Community Standards](https://en.help.roblox.com/hc/articles/203313410), and [Terms of Use](https://en.help.roblox.com/hc/articles/115004647846), which require that you hide advertisements from users who are ineligible to receive ads and disclose that content is advertising in a way that Roblox users who encounter it will understand. For more information, see the [advertising standards](/docs/en-us/production/promotion/comply-with-advertising-standards.md). > **Warning:** If you violate any of Roblox's advertising standards, Roblox may suspend your experience and/or account. --- title: "Advertising for creators" url: /docs/en-us/production/promotion/advertise last_updated: 2026-06-29T19:34:05Z description: "Grow your player base, increase your earnings, and invest in your growth." --- # Advertising for creators Advertising on Roblox gives you powerful ways to grow your audience, boost engagement, and increase your earning potential. Built with creator growth and earnings in mind, Roblox's ad system helps you reach players at the right moments, turn discovery into real gameplay, and support long-term monetization as your experience grows. Whether you're launching something new or expanding an established experience, ads are designed to help you connect with the players who are more likely to engage, return, and invest in what you build. # Grow with ads Ads Manager is Roblox's self-service advertising platform, designed to support creators of all sizes as they grow on the platform. By advertising on the Home page and in Search results, you can reach and retain players who are interested in playing your experience. # 150% ### lift in impressions for experiences that run ads # 24% ### increase in plays # 16% ### increase in playtime ### Get your game discovered Whether you've just published an experience or you're ready to take an existing one to the next level, Ads Manager helps you drive awareness, highlight new content, and find new audiences. ### Improved your retention efforts Ads Manager audiences make it easier to reach specific groups of players, including new, recent, and lapsed players. Re-engage players who have already shown interest in your experience and give them a reason to come back and play again. ### Control your growth Unlike organic discovery, you're in control of the ads you run. With Ads Manager, you can ramp traffic up or down to test new features, support live updates, or scale quickly when you're ready to grow. You can also run always-on campaigns to continuously optimize your user funnel. This flexibility helps you experiment, iterate, and respond to player behavior in real time. ### [Ads Manager](/docs/en-us/production/promotion/production/promotion/ads-manager.md) # Success story Creators across the platform are using Ads Manager to grow, sustain, and scale their audiences. Here's how one top-100 experience turned advertising into a core discovery channel. ![Hypershot creative featuring a sniper character](../../assets/promotion/ads-manager/Hypershot1.webp)![Hypershot pink Kawaii-style weapon skin creative](../../assets/promotion/ads-manager/Kawaii.webp)![Hypershot creative showing a first-person sniper exchange](../../assets/promotion/ads-manager/ShotAt.webp)![Hypershot creative featuring a flaming dragon weapon skin](../../assets/promotion/ads-manager/DragonNew.webp) ### Precision control: How Hypershot sustains top-tier rankings Creating a great game will naturally help drive organic plays, but organic reach is just one piece of the puzzle. Hypershot, a top 100 game on Roblox, is a clear example of how creators can use Ads Manager to help drive consistent growth and visibility, even at the highest levels of success. Beyond mere volume, ads provide a way to quickly test and scale winning creatives to capture more plays. By scaling from 10 to 25 unique creatives, the Hypershot team identified and validated high-performing visual concepts that optimized their clickthrough rate for maximized impressions, while simultaneously pausing weaker-performing assets. This proactive strategy helped ensure a steady flow of players into the game, leveraging their strong monetization funnel. Today, Sponsored Ads are a key discovery channel for Hypershot, **accounting for up to 10% of plays**. By strategically deploying "Plays" campaigns targeting All Players and Recent Players, Hypershot demonstrates that Ads Manager is a vital tool for sustaining and protecting a game's position at the top of the charts. I love that the Ads Manager's algorithm automatically targets relevant players, which drives meaningful engagement. On top of that, the ability to customize a specific audience gives me the precision I need to reach exactly who I want, making every ad dollar go further. — PhoenixSigns, CEO, Frosted Studio # Start earning with ads ### Deploy in-game ads Placing ads inside your experience can help increase earnings and diversify monetization by letting you earn from both paying and non-paying players. Simply deploy ads in your experience and let Roblox handle delivery and earnings. ### Choose between ad formats like Rewarded Videos, which let you offer in-experience rewards to incentivize players to watch click-to-play video ads, Billboards, which display image or video ads, and Portals, which combine an image and an interactive door that teleports players to an advertised experience. ### [Rewarded Video](/docs/en-us/production/promotion/production/promotion/rewarded-video-ads.md) [Billboards](/docs/en-us/production/promotion/production/monetization/immersive-ads.md#billboards) [Portals](/docs/en-us/production/promotion/production/monetization/immersive-ads.md#portal-ads) --- title: "Advertising standards" url: /docs/en-us/production/promotion/comply-with-advertising-standards last_updated: 2026-06-29T19:34:05Z description: "Explains ad content, disclosure, data privacy, user safety, and ad system integrity requirements." --- # Advertising standards Roblox's **advertising standards** aim to foster a more transparent, safe, and civil experience for users, advertisers, and publishers of advertising content. Together, each standard works to create a positive experience on the platform, whether that applies to what type of ad content users can interact with, how they can identify it as an ad, or if they're even able to see ads at all. Roblox's advertising standards apply to every ad on Roblox, regardless if it's a [Roblox-served ad or independent ad](/docs/en-us/production/promotion/advertise-on-roblox.md), but when you choose to independently publish advertising content outside of Roblox's advertising products, it's your responsibility as the ad publisher to ensure that your ad content adheres to these policies, including Roblox's [Terms of Use](https://en.help.roblox.com/hc/articles/115004647846), [Community Standards](https://en.help.roblox.com/hc/articles/203313410), [Independent Advertising Policy](https://en.help.roblox.com/hc/articles/203313410#independent-advertisement-publishing), and [Advertising Standards](https://en.help.roblox.com/hc/articles/13722260778260), as well as any applicable laws or regulations across jurisdictions. This guide outlines the content, disclosure, data privacy, user safety, and overall ad system integrity requirements you must meet in order for your ads to serve on Roblox, such as: - Ensuring your advertising only includes policy-compliant content. - Clearly disclosing that your content is advertising. - Hiding, replacing, or blocking ads from users who are ineligible to receive ads. - Engaging the ad system with integrity, both as a publisher and advertiser. - Not using third-party advertising services. > **Warning:** If you fail to comply with any of these advertising standards, Roblox may suspend your experience and/or account. ## Content requirements For the safety and well-being of all Roblox users, you must ensure that your ads only contain content that doesn't expose them to dangerous, illegal, fraudulent, or otherwise harmful products, services, or messaging. For example, ad content must not contain any hint of child endangerment, violence, or personally identifiable information, otherwise you are in direct violation of Roblox's advertising standards. For a full list of what types of content ads cannot include, see Advertising Standards' [Prohibited Advertising Practices and Content](https://en.help.roblox.com/hc/articles/203313410#prohibited-advertising-practices-and-content) section. _> **Success:** This ad includes content that complies with Roblox policies, and it clearly discloses that it's an ad._ _> **Error:** This ad includes content that doesn't comply with Roblox policies because it leads users off platform with a phone number, and the phone number could potentially be personally identifiable information._ ## Disclosure requirements If you believe your content is advertising, you must clearly disclose it as advertising in a way that Roblox users will understand in their language. In addition, you must ensure that your disclosures don't replicate ad disclosures from official Roblox advertising products because it might confuse users on which ads are from independent advertising campaigns rather than [official Roblox-served ads](/docs/en-us/production/promotion/advertise-on-roblox.md#roblox-served-ads). For example, the following disclosure is sufficiently different from Roblox's disclosures because it includes a different font and color. _> **Success:** This ad's disclosure is clear, legible, and doesn't replicate Roblox's official ad disclosure._ _> **Error:** This ad's disclosure doesn't state that it's an ad, it's difficult to read, and it replicates Roblox's official ad disclosure._ > **Info:** Roblox's [localization tools](/docs/en-us/production/localization.md) are a great starting point to making sure users can read your ad disclosures in many languages. ## Data privacy requirements To protect user privacy and their experience on the platform, it's of the utmost importance that you retain complete control of independent ad content within your experiences. This means that you cannot serve ad content that you didn't directly upload within your own Roblox experiences, and you cannot insert any code within your experiences that makes programmatic calls to third-party ad servers, even for analytics. ## User safety requirements By maintaining complete control over your independent ad content, you can also take the mandatory precautions to protect users from ad content that they shouldn't have access to. To determine when these precautions are necessary, you must use `Class.PolicyService.GetPolicyInfoForPlayerAsync`, which returns a dictionary of values that determine whether certain aspects of the experience need to change for that unique user. One entry within the policy information is `AreAdsAllowed`, a boolean that represents whether a user is eligible to see ads. Ads can remain visible if `AreAdsAllowed` returns true, but if `AreAdsAllowed` returns false, you must include additional logic to hide, replace, or block the ads so those users cannot interact with the ad content. To demonstrate what this can look like, the following images take place in an experience where the non-playable character's dialog text is an ad. The first image displays the ad text that displays to users who are eligible to see ads, and the second displays replacement text that displays to users who aren't eligible to see ads. By using the `AreAdsAllowed` boolean, the creator of the experience can programmatically verify user eligibility to see ads, then change the ad content context accordingly to align with each user's requirements. _The character's ad dialog text displays to users who are eligible to see ads._ _The character's ad dialog text switches to generic content for users who aren't eligible to see ads._ If you believe all the content in your experience collectively constitutes an ad, you must prevent users ineligible to receive ads from viewing the content. For example, the following `Class.Script` checks every user's eligibility to see ads as they enter the experience using `Class.PolicyService.GetPolicyInfoForPlayerAsync`. If `AreAdsAllowed` returns false, the experience disconnects the user from the experience and provides a message informing the user that they are ineligible to access the experience. ```lua local Players = game:GetService("Players") local PolicyService = game:GetService("PolicyService") local Workspace = game:GetService("Workspace") local player = Players.LocalPlayer -- Get the policy info for the user local success, result = pcall(PolicyService.GetPolicyInfoForPlayerAsync, PolicyService, player) if success and result then if not result.AreAdsAllowed then -- Remove ineligible user from accessing the experience player:Kick("You are ineligible to access the experience.") end else print("Failed to get policy for player", player.Name, "Exception:", result) end ``` ## Ad system integrity requirements If you have advertising content in your experience, whether it's Roblox-served or independent ad content, you are an ad publisher, and as an ad publisher, you must engage the ad system with integrity. This means that you cannot abuse the ad system, such as: - Leveraging bots or large scale ad engagement campaigns in order to create fraudulent impressions for ads. - Misclassifying experience metadata in order to deceptively increase ad delivery to users. - Obscuring disclosures in a way that makes users believe they are engaging with non-advertising content. Any of the previous actions may result in Roblox suspending your experience and/or account, as well as revoking any ad payout related to fraudulent impressions or traffic to your experience. For more information, see Advertising Standards' [Publisher Integrity](https://en.help.roblox.com/hc/articles/13722260778260#publisher-integrity) section. --- title: "Content maturity and compliance" url: /docs/en-us/production/promotion/content-maturity last_updated: 2026-06-29T19:34:05Z description: "Explains how to accurately disclose content in experiences to receive appropriate maturity labels and content descriptors." --- # Content maturity and compliance **Content maturity** provides information on the experience's main details page about what kinds of content the experience contains so that players can make informed decisions about what they interact with. Roblox uses this information to recommend experiences on the [Home](https://www.roblox.com/home) and [Charts](https://www.roblox.com/charts#/) pages according to each user's age group and regional content policies. When applicable, this information is also used to generate country or locale-specific **experience labels**, which may include different age and maturity recommendations from Roblox's. > **Error:****Additional rules apply to uploaded assets (such as images and meshes)**. Realistic or excessive depictions of violence, blood, crude humor, or romantic themes may be moderated regardless of your experience’s content maturity label. See the [Restricted Content Policy](https://en.help.roblox.com/hc/en-us/articles/15869919570708-Restricted-Content-Policy) for details. Content maturity consists of two components: - **Content maturity label** - Indicates the level of maturity suitable for the experience according to child development research and industry standards. - **Content descriptors** - Indicates what type of content is within an experience, such as realistic depictions of blood or paid item trading. If an experience does not have accurate or all content maturity information, Roblox restricts the playability of the experience on the platform for all players. In addition, experiences without content maturity information cannot contain any [Restricted content](https://en.help.roblox.com/hc/en-us/articles/15869919570708-Roblox-17-Policy-Standards) without risk of moderation. For this reason, Roblox strongly recommends that you fill out the questionnaire for each of your experiences so that they're available to the largest appropriate audience as possible. Content maturity labels determine which Roblox account types can access your experience: - Games rated **Minimal or Mild** are eligible for Roblox Kids (ages 5–8) and Roblox Select (ages 9–15). - Games rated **Moderate** are eligible for Roblox Select (ages 9–15) and standard Roblox (16+). - Games rated **Restricted** are only accessible to age-verified Roblox users 18 and older. To reach Roblox Kids and Select audiences, creators must also meet additional publishing requirements. See [Roblox Kids and Select](/docs/en-us/production/publishing/kids-and-select.md). > **Warning:** Content descriptors that generate content maturity labels are separate from [genres](/docs/en-us/production/publishing/experience-genres.md) that classify experiences according to their core gameplay. In cases where there is overlap between genres and in-experience content or behavior, answer the Maturity & Compliance questionnaire as accurately as you can regardless of your genre selection or assignment from Roblox. > **Info:** Content maturity information only applies to the content you create for your experience. It does **not** apply to user-generated content that players bring with them into your experience, such as avatar clothing and accessories. ## Generate labels You can generate content maturity information by filling out the **Maturity & Compliance Questionnaire**, which contains a set of questions about the type of content players can possibly encounter within your experience, as well as how frequently it occurs. Your answers give Roblox an understanding of the content in your experience and ensure that the experience is available to the appropriate audience. > **Warning:** If your experience is going to have [Restricted content](https://en.help.roblox.com/hc/articles/15869919570708), it must first receive a Restricted maturity label so that its content is restricted to age-verified players who are at least 18 years old. You must not add any restricted content to your experience before adding content maturity information. As you are completing the questionnaire, **base your answers on the most mature or extreme content players can encounter within your experience**. You can retake the questionnaire and generate a new maturity label at any time to accurately reflect the content in your experience. For restricted experiences, you can't change the maturity, but you can update its content descriptors. To generate content maturity information: 1. **(Optional)** If you want your experience to include restricted content, confirm you are at least 18 years old by [verifying your account](/docs/en-us/production/publishing/account-verification.md) so the questionnaire can ask questions to generate the Restricted maturity label. 2. Navigate to the [Creator Dashboard](https://create.roblox.com/dashboard/creations) and click on the thumbnail of the experience you want to generate content maturity information for. The experience's **Overview** page displays. 3. In the left-hand navigation, navigate to the **Audience** section, then select **Maturity & Compliance**. 4. If you've never taken the questionnaire before, read the notes and details, then click **Start** to proceed. If you've previously completed the questionnaire, the **Questionnaire Completed** page displays, but you can click the **Restart** button to start over. 5. Answer each page of questions based on the content within your experience, then click the **Save and Continue** button. After you have answered every question, the **Questionnaire Preview** page displays all of your answers, the maturity label, applicable content descriptors, and any regions where your experience is non-compliant according to your answers. > **Error:** Please review your experience and confirm that your answers accurately reflect the content of your experience. If you intentionally misrepresent your experience, you may be subject to [moderation consequences](#content-maturity-moderation). 6. If you need to modify a previous answer, click the **Edit** button, otherwise click the **Submit** button to immediately publish the content maturity information to the experience's main page. If your experience receives a Restricted maturity label, servers running the experience restart to remove all players except those age-verified as 18+, and Studio removes all creators from any active [collaboration](/docs/en-us/projects/collaboration.md) session except those age-verified as 18+. As long as your experience doesn't have a Restricted maturity label, if you believe that your content maturity information doesn't match your intended audience, you can update the content in your experience so that your experience is appropriate for your target audience, then resubmit the questionnaire. To learn how you can dynamically adjust the content of your experience for different audiences, see the `Class.PolicyService` API reference. > **Warning:** If you publish an update that changes any of the answers from the questionnaire, you **must** update your answers and resubmit the questionnaire to remain compliant with Roblox's [Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards) and [Terms of Use](https://en.help.roblox.com/hc/articles/115004647846). ## Questionnaire categories The following sections provide guidance on answering the questions within each category of the Maturity & Compliance questionnaire. **Content maturity information only applies to content that you create for your experience**, not user-generated content that players bring with them, such as avatar clothing or accessories. After you submit the questionnaire, your experience receives a maturity label with content descriptors according to the following table, as well as a regional compliance result. | Maturity label | Description | | --- | --- | | **Minimal** | May contain occasional mild violence and/or light unrealistic blood. | | **Mild** | May contain repeated mild violence, heavy unrealistic blood, mild fear-based content, and/or mild crude humor. | | **Moderate** | May contain moderate violence, light realistic blood, moderate fear-based content, moderate crude humor, and/or unplayable gambling content. | | **Restricted** | May contain strong violence, heavy realistic blood, moderate crude humor, romantic themes, unplayable gambling content, the presence of alcohol, and/or strong language. | > **Info:** To remain compliant with local laws and regulations, experiences with a Restricted maturity label are unplayable for players who created their accounts or are located in certain countries or regions, such as Korea, Saudi Arabia, and Türkiye (Turkey). > **Warning:** While parents can use parental controls to bypass most content restrictions according to what they believe is appropriate for their child, Roblox restricts [free-form user creation](#free-form-user-creation) and [social hangouts](#social-hangout) to players over 16. ### Violence **Question 1: Does this experience depict violence and/or violent content?** Violence is the intentional use of physical or psychological force against players or non-playable characters (NPCs). If your experience includes violence, including within any [asset type](/docs/en-us/projects/assets.md#asset-types), you must disclose it in the Maturity & Compliance Questionnaire. **Question 2: How intense is the violence?** If your experience contains violence, you must specify the violence's maximum level of intensity according to the following table: | Violence intensity | Description | | --- | --- | | **Mild** | Implied or unrealistic depictions of violence, such as bodies disappearing the moment their health reaches zero. | | **Moderate** | Non-graphic, realistic-looking depictions of violence and/or death that don't violate Roblox Community Standards, such as realistic depictions of real-life injuries. | | **Restricted** | Graphic and realistic-looking depictions of violence and/or death that do not violate the [Restricted Content Policy](https://en.help.roblox.com/hc/en-us/articles/15869919570708-Roblox-17-Policy-Standards), such as non-real world beheadings/decapitation, impalement, hangings, dismemberment, mutilation, severed/severing body parts, presence of organs, maiming, disfiguration, and electrocution.

**Experiences with strong violence are only available to age-verified players that are at least 18 years old.** | | If you imply violence anywhere within your experience, such as violence that occurs off-screen that players can hear but can't see, your experience automatically meets the mild criteria. If you visually show violence, the criteria for meeting mild, moderate, or strong violence depends on the **consequence** of the violence. For example, if the consequence of violence is unrealistic, your experience meets the mild criteria, but if there is any moment that the consequence of violence is realistic, your experience meets either the moderate or strong criteria, even if the realistic violence only occurs once. > **Error:** Roblox doesn't allow content that contains extreme violence or serious physical or psychological abuse. This includes animal abuse and torture, realistic depictions of extreme gore, or the depiction, support, or glorification of war crimes or human rights violations, including torture. For more information, see Roblox's [Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410) and [Restricted Content Policy](https://en.help.roblox.com/hc/articles/15869919570708). **Question 3: How frequent is the violence?** After you specify its intensity, you must also specify how often the violence occurs within your experience according to the following table. Note that even a small part of your experience contains repeated violence, it meets the Repeated criteria. | Violence frequency | Description | | --- | --- | | **Occasional** | Violence occurs either rarely or occasionally, such as at a couple key moments of the experience. | | **Repeated** | Violence either occurs often, or it occurs rarely, but when it does occur, many violent events happen in quick succession. | ### Blood **Question 1: Does this experience depict any blood?** Blood is the red liquid that flows through human and animal bodies that's essential to life. If your experience includes blood, including within any [asset type](/docs/en-us/projects/assets.md#asset-types), you must disclose it within the Maturity & Compliance Questionnaire. **Question 2: How realistic is the blood?** If your experience contains blood, you must specify the blood's realism according to the following table: | Blood realism | Description | | --- | --- | | **Unrealistic** | Blood appears unrealistic, such as being pixelated or having a different color or shape. | | **Realistic** | Blood appears realistic, such as having the same color, shape, and splatter properties as blood in the real world. | **Question 3 for unrealistic blood: Are the depictions of blood infrequent and/or fleeting?** If you answer unrealistic for question 2, you must also specify how often the unrealistic blood occurs within your experience according to the following table: | Unrealistic blood frequency | Description | | --- | --- | | **Yes** | Unrealistic blood only occurs sometimes, such as yellow blood splattering for a few seconds. | | **No** | Unrealistic blood only occurs often, such as repeated bloodshed or with lasting imagery. | **Question 3 for realistic blood: What level of blood and/or gore is depicted?** If you answer realistic for question 2, you must also specify the blood's maximum level of intensity according to the following table. Note that if you depict realistic blood anywhere within your experience, such as blood splatter from a distance, your experience automatically meets the Light criteria. | Blood intensity | Description | | --- | --- | | **Light** | The bloodshed is minimal, such as blood spatter from a distance. | | **Heavy** | The bloodshed is significant, such as pools of blood, gushing blood, and up-close blood spatter.

**Experiences with heavy, realistic blood are only available to age-verified players that are at least 18 years old.** | | ### Fear **Question 1: Does this experience include scary elements that may trigger fear?** Fear-based content contains scary or horrifying elements that trigger fear in players. If your experience includes scary elements, including within any [asset type](/docs/en-us/projects/assets.md#asset-types), you must disclose it in the Maturity & Compliance Questionnaire. **Question 2: What level of scary elements are there?** If your experience contains scary elements, you must specify the intensity of the elements according to the following table: | Scary elements intensity | Description | | --- | --- | | **Mild** | Loud/heavy breathing, pounding heart, shrieking or screaming, creepy-looking NPCs, jump scares, ominous music, and/or gameplay that builds suspense. | | **Moderate** | Characters with disfigured mouths with realistic blood, lack of flesh with realistic-looking connective tissues, organs, and/or blood vessels visible, realistic open wounds and/or leaking/bleeding eyes with realistic blood. | ** Moderate Examples (Not Suitable for Young Readers)** The following images meet the moderate criteria for fear-based content. _Includes visible organs, blood vessels, and leaking/bleeding eyes._ _Includes realistic blood and a lack of flesh._ _Includes realistic open wounds and leaking/bleeding eyes._ **Question 3: How frequently do the scary elements occur?** After you specify its intensity, you must also specify how often the scary elements occur within your experience according to the following table. Note that even a small part of your experience contains repeated scary elements, it meets the Repeated criteria. | Scary elements frequency | Description | | --- | --- | | **Occasional** | Scary elements occur either rarely or occasionally, such as at a couple key moments of the experience. | | **Repeated** | Scary elements either occur often, or they occur rarely, but when they do occur, many violent events happen in quick succession. | ### Crude humor **Question 1: Does this experience depict, reference, or encourage crude humor?** Crude humor is a type of humor that depicts or references crude bodily functions, such as burping, farting, vomit, pee, and/or poop for comical purposes. If your experience includes crude humor, including within any [asset type](/docs/en-us/projects/assets.md#asset-types), you must disclose it in the Maturity & Compliance Questionnaire. **Question 2: What is the level of crude humor?** If your experience contains crude humor, you must specify the intensity of the elements according to the following table: | Level of crude humor | Description | | --- | --- | | **Mild** | Depicts and/or references burping, farting (e.g. fart cloud), and/or unrealistic looking vomit or poop (e.g. poop coils, poop emoji). | | **Moderate** | Depicts and/or references pee. | ### Gambling **Question 1: Does this experience contain unplayable gambling content?** Gambling is the practice of exchanging real world money, Robux, or in-experience items of value for a game of chance. While experiences cannot contain playable gambling content, including simulated gambling, you can depict unplayable gambling content, such as showing a casino or people playing cards that players cannot bet on or play. You do **not** need to report depictions of, and/or references to, items or activities that are typically associated with gambling, but are not games of chance/luck, such as horse racing, car racing, and poker chips. ### Strong language **Question 1: Do you depict and/or want to allow strong language in your experience?** Strong language is vulgar and obscene language that is not used to harass, discriminate, incite violence, or threaten others, or used in a sexual context. For example, strong language content could be depictions of a non-playable character (NPC) using obscenity like the "f-word" that is not directed towards another character or group of people (e.g. "f* off"). If your experience includes strong language, you must disclose it within the Maturity & Compliance Questionnaire. Experiences that include strong language are only available to age-verified players that are at least 18 years old. > **Info:** If you want to enable strong language in player communication aside from content for your experiences with **Restricted** content maturity labels, see [here](/docs/en-us/projects/configure-games.md#allow-strong-language). ### Romantic themes **Question 1: Does this experience depict or reference (including in the title) romantic themes and/or primarily take place in private spaces (e.g. shower stalls, hotel rooms) or settings intended for adults (e.g. clubs, bars)?** To understand this question, it's important to understand three different definitions: - Romantic themes are non-sexual expressions of love or affection, such as a quick kiss on the mouth. - Private spaces are enclosed spaces that meet the following two criteria: - They are designed for activities that are personal and secluded, such as sleeping, changing clothes, or bathing. - They are designed for one person or for a very small number of people to engage in private activities, such as a bathroom stall, bedroom, or small tent. - Settings intended for adults are settings that are designed to cater to adult clientele. These settings are often legally restricted to individuals who have reached the age of majority and are otherwise deemed inappropriate or unsuitable for minors, such as clubs or bars. If your experience includes (including in the title) romantic themes and/or primarily takes place in private spaces or settings intended for adults, you must disclose it within the Maturity & Compliance Questionnaire, and it will only be available to age-verified players that are at least 18 years old. ### Alcohol **Question 1: Does this experience depict, reference, or include use of alcohol?** Alcohol is an intoxicating adult beverage. If your experience includes, depicts, or references alcohol, or depicts adult business and locations that provide or sell alcohol, such as characters drinking alcohol at a bar, you must disclose it within the Maturity & Compliance Questionnaire. Experiences that depict alcohol are only available to age-verified players that are at least 18 years old. ### Social hangout **Question 1: Is the primary theme or activity of this experience a social hangout?** Social hangouts are experiences where the primary theme or activity is to talk to or interact with other players using voice or text chat. This includes hangouts, vibe games, socializing spaces, and supportive places like sad rooms. It does not apply to experiences where the primary theme or activity is roleplay. To be considered a roleplay experience, the roleplay components, such as adopting a new role and using in-game items to play your role with, must be central to the experience. Note that if your title or description includes content referencing social hangouts, your experience will be classified as a social hangout. If this is inaccurate, please update your title or description and re-take the questionnaire. If your experience is a social hangout, you must disclose it within the Maturity & Compliance Questionnaire. **Question 2: Does your experience include private spaces?** If your experience is a social hangout, you must specify if it includes private spaces. Private spaces are enclosed spaces that meet the following two criteria: - They are designed for activities that are personal and secluded, such as sleeping, changing clothes, or bathing. - They are designed for one person or for a very small number of people, such as a bathroom stall, bedroom, or small tent. If your experience is a social hangout that includes private spaces, you must disclose it within the Maturity & Compliance Questionnaire. Social hangouts without private spaces are only available to players that are at least 16 years old, and social hangouts with private spaces are only available to age-verified players that are at least 18 years old. ### Free-form user creation **Question 1: Does this experience include free-form user creation?** Free-form user creation refers to features that allow players to create anything within an experience, such as writing words or making illustrations on a chalkboard. While this applies to in-experience, free-form drawing or creation tools, it does not apply to in-experience creations that players assemble with 3D assets, such as building a house or creating an outfit, or anything that goes through Roblox moderation before it's published or replicated. If your experience includes free-form user creation features, you must disclose it within the Maturity & Compliance Questionnaire. Free-form user creation is only available to players that are at least 16 years old. ### Sensitive issues **Question 1: Is the primary theme of this experience a sensitive issue?** Sensitive issues are any current social, political, and religious issue that meets the following criteria: - **Polarized Viewpoints** - The issue has a limited number of strongly opposing viewpoints, with little room for compromise or middle ground. - **Strong Emotional Response** - The issue evokes strong emotional reactions and/or passionate debate. Sensitive issues are frequently in the news and inspire strong opinions and debate. For example: immigration, capital punishment, gun control, marriage equality, pay equity in sports, prayer in schools, racial profiling, affirmative action, vaccination policies, and reproductive rights. What meets the criteria can differ significantly depending on your location and cultural context, so answer from your local perspective. If your experience includes content related to a sensitive issue or people implicated by them, but it isn't primarily themed on that issue, such as a religious building in a city, guns in a first-person shooter experience, or a national holiday parade in a hangout experience, you do **not** need to disclose it within the Maturity & Compliance Questionnaire. However, if the **majority of the content or gameplay of your experience** refers to a sensitive issue, you must disclose it within the Maturity & Compliance Questionnaire. Experiences with a primary theme of a sensitive issue are only available to players who are at least 16 years old, but they aren't recommended and discoverable unless the players have age-verified themselves. Parents can [manually allow access](https://en.help.roblox.com/hc/en-us/articles/30428310121620-Parental-Controls-Overview) to these experiences, regardless of their child's age. > **Info:** Individual assets, as well as text and voice chat, do not qualify for this descriptor. ### Paid random items **Question 1: Does this experience contain paid random items?** Paid random items are virtual items players can purchase with Robux or other currency. If your experience includes paid random items, such as a coin players can purchase with Robux to later redeem for a random virtual item, you must disclose it within the Maturity & Compliance Questionnaire. You do **not** need to report virtual items that you provide in exchange for players completing an action that doesn't involve the payment of Robux or any other currency. **Question 2: Does this experience respect the ArePaidRandomItemsRestricted policy API?** If your experience contains paid random items, it's recommended to take the mandatory precautions to protect players in certain regions that don't allow paid random items so that they can still access your experience. To check when these precautions are necessary, use `Class.PolicyService.GetPolicyInfoForPlayerAsync` to return a dictionary of values that determine whether certain aspects of the experience need to change for that unique player. One entry within the policy information is `ArePaidRandomItemsRestricted`, a boolean that represents whether a player is eligible to pay for random items. Random items can remain purchasable if `ArePaidRandomItemsRestricted` returns false, but if `ArePaidRandomItemsRestricted` returns true, include additional logic to hide, replace, or block the purchase of random items for those players so that your experience is available to the largest audience as possible. ### Paid item trading **Question 1: Does this experience contain the ability for users to trade items that they paid for?** Paid item trading is the ability for players to purchase virtual items that they can then trade with other players. If your experience includes paid item trading, such as a marketplace for exchanging Limited items, you must disclose it within the Maturity & Compliance Questionnaire. **Question 2: Does this experience respect the IsPaidItemTradingAllowed policy API?** If your experience contains paid item trading, it's recommended to take the mandatory precautions to protect players in certain regions that don't allow paid item trading so that they can still access your experience. To check when these precautions are necessary, use `Class.PolicyService.GetPolicyInfoForPlayerAsync` to return a dictionary of values that determine whether certain aspects of the experience need to change for that unique player. One entry within the policy information is `IsPaidItemTradingAllowed`, a boolean that represents whether a player is eligible to trade paid items. Paid items can remain tradeable if `IsPaidItemTradingAllowed` returns true, but if `IsPaidItemTradingAllowed` returns false, include additional logic to hide, replace, or block the trading of paid items for those players so that your experience is available to the largest audience as possible. ### Media **Question 1: Does your experience allow users to share media content (videos, images, text, audio, 3D models) from their gameplay that other users can see?** This question only applies if your experience lets users share gameplay content using tools built into the experience itself. **Question 2: Does your experience contain content feeds with continuous loading or audio/video that plays automatically?** **Continuous loading** means that content loads automatically as the user scrolls, without requiring any specific user interaction (such as a manual "load more" button or pagination). **Question 3: Does your experience allow users to view content that was captured from other experiences on Roblox?** This question applies to any in-experience viewing functionality, such as a `Class.SurfaceGui` for static images, a `Class.VideoFrame`, or an asset viewer. ### AI interaction **Question 1: Does your experience allow users to interact with generative AI components?** This question includes experiences in which a user can interact with a generative AI model in any way that triggers a response from the model. These interactions can include text chat, voice chat, images, 3D generations, avatar movement, etc. Your answer to this question should **not** consider whether you used generative AI to help develop the experience, only whether users can interact with it in the experience. **Question 2: What type of interactions does your experience allow between users and a generative AI model?** **Extended interactions** meet at least one of the following criteria: - Cross-session memory is enabled. The experience saves the context of a user's prior interactions with AI and loads it on subsequent sessions. - The experience's main purpose is to interact with a generative AI bot or character. Users can interact with the AI continuously with no time limit. Experiences that do not meet at least one of these criteria are considered **limited interactions**. ## Content maturity moderation Roblox relies on the information you provide in the Maturity & Compliance Questionnaire to generate accurate content maturity information, so the Moderation team may review your content maturity to ensure its validity based on the content of your experience. Whenever Roblox discovers a discrepancy between your submission and the content in your experience, the following actions occur: - You receive a moderation notification through a private message on Roblox. - The **Maturity & Compliance Questionnaire** page on the [Creator Dashboard](https://create.roblox.com/dashboard/creations) updates to provide moderator feedback and guidance on how to generate accurate content maturity information. - Roblox provides a moderation action. - If your experience contains restricted content without a Restricted maturity label, your experience is subject to moderation consequences. - If your experience contains content that is prohibited by Roblox's [Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410), [Terms of Use](https://en.help.roblox.com/hc/articles/115004647846), or [Restricted Content Policy](https://en.help.roblox.com/hc/articles/15869919570708), your experience is subject to moderation consequences. - If your experience otherwise has inaccurate content maturity information according to its content, Roblox may remove the maturity label (or only some or all descriptors if the maturity label is Restricted) from your experience. If an experience does not have content maturity information, Roblox restricts the playability of the experience on the platform for all players. - If you repeatedly include inaccurate content maturity information, **your experience and/or account is subject to moderation consequences**. If your experience receives a moderation action and you believe Roblox made a mistake, you can appeal the decision: 1. If you are the owner of a user-owned experience or the group owner of a group-owned experience, navigate to [roblox.com/report-appeals](https://www.roblox.com/report-appeals#/). 2. Select the violation you want to appeal. 3. Click the **Request Appeal** button. 4. **(Recommended)** In the text field, describe why the moderation action was a mistake. 5. Click **Send** to submit your appeal. Even if the violation does not appear on the **Violations & Appeals** page, you can appeal the decision: 1. Click **Support** (appeal something not shown). 2. Confirm your contact information and device type, 3. Set **Type of help category** to **Appeal a Decision**. 4. Set **Help Subcategory Type** to **I was wrongly moderated for other content I created**. 5. Input your experience ID into the **Asset ID** field. 6. In the **Please describe your issue** field, describe why the moderation action was a mistake. 7. Click **Continue** to submit your appeal. If your appeal is accepted, **Appeal accepted** is shown under your violation when you click on it.
--- title: "Deep links" url: /docs/en-us/production/promotion/deeplinks last_updated: 2026-06-29T19:34:05Z description: "Deep links let you launch experiences and send users directly to a specific place with custom launch data." --- # Deep links Deep links let you send users to a specific place in an experience, which can make the process of joining more seamless, help users find their friends, and provide traffic attribution. The optional launch data in these links lets you customize the user experience when someone joins. > **Success:** This process is deprecated. To create public deep links, use [share links](/docs/en-us/production/promotion/share-links.md). ## Construct a deep link A deep link URL consists of a URL format along with parameters that you specify. The following sections describe how to construct each format. ### URL parameters Deep links support the following URL parameters. All are optional unless otherwise noted. | Parameter | Description | | --- | --- | | `placeId` | The place ID to join. Required unless `userId` is specified. | | `userId` | The user ID to join. Results in a "Followed user has left the experience" error if the user left the experience or is offline. | | `accessCode` | The private server access code. | | `linkCode` | The private server link code. | | `gameInstanceId` | The unique identifier of the experience instance to join, also called the `Class.DataModel.JobId`. | | `launchData` | Additional information that you want to include within the deep link, such as promotional codes or coordinates. Process using the `Class.Player:GetJoinData()` method. See [Include launch data](/docs/en-us/production/promotion/invite-prompts.md#include-launch-data). | ### Requirements and guidelines - You must URL encode special characters, such as spaces. These characters are automatically decoded when the user joins your experience. - The decoded launch data can't exceed 200 bytes. - You can store more complex data as a JSON string and decode it with `Class.HttpService:JSONDecode()` on the server. - Don't send confidential information in the `launchData` parameter; it's fully visible in the URL. Further, users can modify the URL, so the data might not be authentic. ### Web list to app This format sends users to the Roblox experience page on the web and then launches the Roblox app. The provided example provides a place ID and a URL encoded launch data string. | **Format** | `https://www.roblox.com/games/start?placeId=&launchData=` | | --- | --- | | **Example** | [Example Link](https://www.roblox.com/games/start?placeId=6900305353&launchData=%7B%22roomId%22%3A%202%7D) | ### Direct to app This format sends users directly to the Roblox app. The provided example provides a place ID and a URL encoded launch data string. | **Format** | `roblox://placeId=&launchData=` | | --- | --- | | **Example** | [Example Link](roblox://placeId=6900305353&launchData=%7B%22roomId%22%3A%202%7D) | ### Deferred For users who don't have Roblox installed on their mobile devices, use the AppsFlyer version of a deep link to let users download the Roblox app and then follow the deep link. To construct this type of deep link, specify the `https://ro.blox.com/Ebh5?` prefix. Provide the "direct to app" deep link with the `af_dp` parameter and the "web listing to app" deep link with the `af_web_dp` parameter, which are described in the previous sections. | **Format** | `ro.blox.com/Ebh5?af_dp=&af_web_dp=` | | --- | --- | | **Example** | `ro.blox.com/Ebh5?af_dp=roblox%3A%2F%2FplaceId%3D6900305353%26launchData%3D%257B%2522roomId%2522%253A%25202%257D&af_web_dp=https%3A%2F%2Fwww.roblox.com%2Fgames%2Fstart%3FplaceId%3D6900305353%26launchData%3D%257B%2522roomId%2522%253A%25202%257D` | ## Process a deep link In your experience, obtain the launch data with the `Class.Player:GetJoinData()` method, which returns a [dictionary](/docs/en-us/luau/tables.md). In that dictionary, the `LaunchData` key contains the string that you specified in the `launchData` parameter of your deep link. See the `Class.Player:GetJoinData()` reference documentation for code samples on how to process launch data. --- title: "Experience events and updates" url: /docs/en-us/production/promotion/experience-events last_updated: 2026-06-29T19:34:05Z description: "Explore how to create and promote time-based events for your experience, as well as announce experience updates to opted-in users." --- # Experience events and updates As part of your [promotional](/docs/en-us/production/promotion.md) initiatives, the integrated **events** system lets you create time‑based events for your experience. Players can discover your events on the experience's detail page and through an [event details page](#event-details-page), and they can opt into [notifications](#streampush-notifications) that they'll receive when your event begins. ![Example event page on the Roblox website](../../assets/promotion/experience-events/Dedicated-Event-Page-Live.jpg) In addition to events, the integrated [update announcements](#announce-experience-updates) system lets you announce experience updates for which opted‑in players receive a notification in their Roblox notifications stream, along with a link to join the experience directly from the notification. ![Example update announcement notification on the Roblox website](../../assets/promotion/experience-events/Notification-Update.png) ## Create events To create an event, you must have the [Edit all group experiences](/docs/en-us/projects/groups.md#roles-and-permissions) permission in a [group‑owned](/docs/en-us/projects/groups.md) experience, or be the sole owner of a user‑owned experience. Currently, you can publish a maximum of 10 ongoing or upcoming events. 1. Navigate to an experience's page on the [Creator Dashboard](https://create.roblox.com/dashboard/creations). 2. In the left column, expand the **Engagement** branch and click **Events & Updates**. 3. Click the **Create Event or Update** button. 4. Add a **thumbnail** to promote your event. If you don't add any thumbnails for the event, the primary [experience thumbnail](/docs/en-us/production/publishing/thumbnails.md) will be used instead. > **Success:** Using a unique event thumbnail that is different from your experience thumbnails will help to increase player engagement with the event. 5. Select an event **category** that most accurately describes your event. The category may be shown alongside your event thumbnail in certain places on Roblox. 6. Enter the event title, subtitle, and description. The description is optional but it can help provide additional information about the details of your event. If you supply a description, it should follow general [best practices](/docs/en-us/production/publishing/publish-games-and-places.md#publish-games) and accurately portray the event, including: - A summary of the event and how it relates to the overall experience. If you're using the event to promote a major experience update, summarize the key updates. - Incentives for players to join such as exclusive event‑specific merch or [badges](/docs/en-us/production/publishing/badges.md) that may be earned during the event. 7. Set the starting date/time, and an ending date/time. 8. Set the place for your event. If you select a place other than the experience's start place, anyone who joins the event through an event-specific surface (such as an event start notification or the event tile) will spawn in the event place rather than the start place. 9. Select a privacy level; **public** events will be viewable to all players while **private** events will only be visible by you and others with elevated access. > **Info:** If you're not yet ready to publicize the event, set it as **private**. Later, when you're ready to begin promoting the event, change it to **public**. Private events and their event pages cannot be seen by players without access, and they will not appear on its experience's detail page. 10. When ready, click **Create** to save your event. If you set the event to public, it will be immediately discoverable on the experience's detail page.![Example events on the Creator Dashboard](../../assets/creator-dashboard/Creations-Events-Example.png) > **Warning:** Assets which are pending review or are moderated will display a placeholder. ## Event discovery The following sections detail how you can promote events and how events are discoverable by players on the Roblox platform. ### Event details page All published events feature an event details page which you can share with players on and off platform. To access a **shareable link**, you can use either the event details page itself or the **Events** page. #### Event Details Page From the event details page, click/tap the **share** button below the event title to open your device's native sharing UI, or to copy the link to your clipboard. ![Share button indicated on event details page](../../assets/promotion/experience-events/Dedicated-Event-Page-Copy-URL.png) #### Creator Dashboard From the experience's **Events** page, hover over the event thumbnail, click the **⋯** button, and select **Copy Event Details URL**. ![Event's right-click context menu indicating the selection of Copy Event Details URL](../../assets/creator-dashboard/Event-Context-Menu-Copy-Event-Details-URL.png) > **Warning:** Event pages persist even after an event ends, but players will no longer be able to join from the page. To remove an event page entirely, see [Deleting Events](#delete-events). ### Experience detail page All published events also appear in a dedicated **Events** section on the experience's detail page. Players can join active events directly through the **Join Event** button, or click **Notify Me** for upcoming events to receive stream notifications in their Roblox inbox and the ability to opt into [push notifications](#streampush-notifications). ![Event promoted on experience's detail page](../../assets/promotion/experience-events/Experience-Page-Promotion.png) ### Roblox charts The **Trending Events in Experiences** sort in Roblox's [charts](https://www.roblox.com/charts), available globally and across all platforms, can help increase player discovery and participation in your events. This sort shows active events based on the experience's daily active users (DAU), filterable by location and platform. To be featured in **Trending Events in Experiences**, your event must meet the following criteria: - An active event that started within the last 7 days. - Minimum of 1,000 RSVPs. - Visible to all players (not private). ### Group page If you create events as an admin for a [group](/docs/en-us/projects/groups.md), the group's events will appear under the **Events** tab on the group page. ![Event listed on group's page under the Events tab.](../../assets/promotion/experience-events/Group-Page-Events-Tab.jpg) In addition, you can feature an event in the group's **About** section for extra visibility: 1. Go to the group's **Events** tab. 2. Find the event you want to feature, click on the **⋯** button, and select **Feature Event**.![Feature Event button indicated on an event's tile on the group's page.](../../assets/promotion/experience-events/Group-Page-Feature-Event.jpg) The event should now appear in the group page's **About** section:![Event featured on group's page under the About tab.](../../assets/promotion/experience-events/Group-Page-About-Tab.jpg) ### Stream/push notifications Players who click **Notify Me** for an upcoming event will receive stream notifications in their Roblox inbox when the event starts. In addition, they can opt into **push notifications** to receive a notification on their device that will take them into the experience. Stream notifications remain accessible in the player's Roblox inbox for the duration of the event, making it easy for them to hop back in at any time. ![Event notification on phone lock screen](../../assets/promotion/experience-events/Notification-Lock-Screen.jpg)_Notification on phone lock screen_ ![Event notification in Roblox app](../../assets/promotion/experience-events/Notification-Roblox-App.png)_Notification in Roblox app_ ## Delete events To delete an event and remove its [detail page](#event-details-page): 1. On an experience's **Events & Updates** page, click the tab corresponding to the event's timing (**Active**, **Scheduled**, or **Finished**). 2. Hover over the event thumbnail, click the **⋯** button, and select **Delete Event**.![Event's right-click context menu indicating the selection of Delete Event](../../assets/creator-dashboard/Event-Context-Menu-Delete-Event.png) ## Use event attribution When a player joins an experience through an event entry point, such as by clicking on an event [notification](#streampush-notifications) or through the join button on the [event details page](#event-details-page), the event ID is added to the player's `GameJoinContext`. You can use this information in your experience to identify players who have come to participate in the event and show them custom prompts or otherwise personalize their experience. ```lua local Players = game:GetService("Players") local function onPlayerAdded(player) local source = player:GetJoinData() -- Check if the provided source is valid if source then -- Read eventId from GameJoinContext local eventId = source.GameJoinContext.EventId if eventId then print(player.Name, "joined from the event:", eventId) end end end Players.PlayerAdded:Connect(onPlayerAdded) ``` ## Announce experience updates > **Info:** Events and updates are currently in the process of being combined. Announcing updates still uses the old form, but that is subject to change in the future. The integrated announcements system lets you notify [opted‑in](https://en.help.roblox.com/hc/en-us/articles/24769602332692-Out-of-Experience-Notifications) players about new and exciting updates. When you send an announcement, players receive a notification in their Roblox notifications stream, along with a link to launch the experience directly from the notification. ![Example update announcement notification on the Roblox website](../../assets/promotion/experience-events/Notification-Update.png) Updates have a limit of 60 characters and you can only announce an update once every three days. When drafting your update, consider the following best practices: - Focus on announcing new, exciting, and accessible information, such as new areas, skills, and achievements. - Be descriptive in your updates. Updates like "bug fixes" are vague and don't inform the reader of what was actually fixed. - Avoid overwhelming players with too many updates in a short time period. To announce an update: 1. Navigate to the [Creator Dashboard](https://create.roblox.com/dashboard/creations). 2. Click on the thumbnail of the experience you want to associate with the update. The experience's **Overview** page displays. 3. In the left-hand navigation, under **Engagement**, click **Events & Updates**. 4. Click **Create Event or Update**. 5. At the top of the creation form, click on the link to the old updates page. 6. In the input field, describe what has been updated in your experience, then click the **Preview** button to see how the update will display for desktop, tablet, and phone users. 7. When ready, click the **Send** button. The update is sent to everyone who enabled notifications for your experience and it immediately displays in the **History** section of your **Updates** page with the sent date, the update message, and statistics related to the update's performance. | Statistic | Description | | --- | --- | | **Views** | Number of people who have viewed the update. | | **Visit rate** | Percentage of people who have visited the experience from the notification. | | **Unfollow rate** | Percentage of people who have unfollowed the experience from the notification. | ## Off-Platform Featuring program The **Off-Platform Featuring** program extends your event's visibility beyond Roblox to global off-platform audiences. Through this program, selected events may appear on external platforms such as the App Store, Google Play Store, and Roblox marketing channels. ![Examples of how your event might look off-platform.](../../assets/promotion/experience-events/off-platform-featuring.png) ### Eligibility To be considered for the program, submitted events must meet the following criteria: - The submitting creator must be **13 years of age** or older with an email address linked to their account. - The submission must represent a **time-limited event or significant experience update** (for example, seasonal content, feature update, collaboration, anniversary, or live moment). - The event must be **submitted at least 7 days prior to the event start** and must be set to public and active while the event is live. - All event metadata, including title, description, schedule, and thumbnails, must be **complete, accurate, and aligned** with the event's content. - The event and its assets must comply with Roblox [Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards) and content guidelines. - Selections are based on performance signals (DAU growth, playtime, retention, monetization), current trends, and seasonal alignment. ### How it works 1. On the [Creator Hub](https://create.roblox.com/), navigate to your experience. Then choose **Events & Updates**. 2. Click **Create Event or Update**. 3. While creating your event, select the **Submit for Featuring** toggle.![Submit for Featuring toggle.](../../assets/promotion/experience-events/submit-for-featuring.png) 4. Roblox's marketing teams review eligible submissions. - The best events run for 7-30 days and highlight something new or time-limited for players. - High-quality visuals and clear metadata improve the odds of selection. - Avoid making changes to your event after submitting it. Changes might affect eligibility or delay the review. 5. If selected, you will be notified and asked to provide event-specific art assets. Due to submission volume, we can't provide feedback on why an event wasn't selected. 6. Selected events are featured on global surfaces and app stores at no cost to you. 7. After the event ends, you receive a performance report summarizing how many views and interactions your event generated off-platform. --- title: "Experience guidelines" url: /docs/en-us/production/promotion/experience-guidelines last_updated: 2026-06-29T19:34:05Z description: "Explains how to provide accurate descriptions of experiences to determine accurate experience ratings." --- # Experience guidelines **Experience guidelines** provide information on the experience's main page about what kind of content the experience contains so that players can make informed decisions about what they interact with. Roblox uses this information to recommend experiences on the [Home](https://www.roblox.com/home) and [Charts](https://www.roblox.com/charts#/) pages according to each user's age group and regional content policies. Experience guidelines consist of two components: - **Age recommendations** — Indicates which age group an experience is suitable for based on child development research and industry standards. For more information, see [Age recommendations](https://en.help.roblox.com/hc/en-us/articles/8862768451604). - **Content descriptors** — Indicates what type of content is within an experience, such as realistic depictions of blood or paid item trading. Roblox treats experiences without guidelines the same as experiences with an age recommendation of **Ages 13+**, meaning Roblox doesn't recommend them to younger players. In addition, experiences without guidelines cannot contain any [17+ content](https://en.help.roblox.com/hc/articles/15869919570708) without risk of moderation. For this reason, Roblox strongly recommends that you fill out the questionnaire for each of your experiences so that they're available to the largest appropriate audience possible. > **Info:** Experience guidelines only apply to the content you create for your experience. They do **not** apply to user-generated content that players bring with them into your experience, such as avatar clothing and accessories. ## Generate experience guidelines If your Roblox account is at least 30 days old, you can generate experience guidelines by filling out the **Experience Questionnaire** which contains a set of questions about the type of content players can possibly encounter within your experience, as well as how frequently it occurs. Your answers give Roblox an understanding of the content in your experience and ensure that the experience is available to the appropriate audience. > **Info:** If your experience is going to have [17+ content](https://en.help.roblox.com/hc/articles/15869919570708), it must first receive experience guidelines with an age recommendation of 17+ so that its content is restricted to verified players who are at least 17 years old. You must not add any 17+ content to your experience before generating guidelines with an age recommendation of 17+. As you are completing the questionnaire, **base your answers on the most mature or extreme content players can encounter within your experience**. You can retake the questionnaire and generate new guidelines at any time to accurately reflect the content in your experience. For 17+ experiences, you can't change the age recommendation, but you can update the content descriptors. To generate experience guidelines: 1. **(Optional)** If you want your experience to include 17+ content, confirm you are at least 17 years old by [verifying your account](/docs/en-us/production/publishing/account-verification.md) so the questionnaire can ask questions to generate the correct age recommendation of 17+. 2. Navigate to the [Creator Dashboard](https://create.roblox.com/dashboard/creations) and click on the thumbnail of the experience you want to generate experience guidelines for. The experience's **Overview** page displays. 3. In the left-hand navigation, navigate to the **Audience** section, then select **Maturity & Compliance**. 4. If you've never taken the questionnaire before, read the notes and details and click **Start** to proceed. If you've previously completed the questionnaire, the **Questionnaire Completed** page displays, but you can click the **Restart** button to start over. 5. Answer each page of questions based on the content within your experience, then click the **Save and Continue** button. After you've answered every question, the **Questionnaire Preview** page displays all of your answers, the age recommendation and content descriptors for the experience, and any regions where your experience is non-compliant based on your answers. 6. If you need to modify a previous answer, click the **Edit** button, otherwise click the **Submit** button to immediately publish the guidelines to the experience's main page. If your experience receives an age recommendation of 17+, servers running the experience restart to remove all players except those age-verified as 17+, and Studio removes all players from any active [collaboration](/docs/en-us/projects/collaboration.md) session except those age-verified as 17+. If you believe that the rating doesn't match your intended audience, and the experience doesn't have an age recommendation of 17+, you can update the experience's content so that it's appropriate for your target audience, then resubmit the questionnaire. To learn how you can dynamically adjust the content of your experience for different audiences, see the `Class.PolicyService` reference. > **Warning:** If you publish an update that changes any of the answers from the questionnaire, you **must** return and update your answers and resubmit the questionnaire to remain compliant with Roblox's [Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards) and [Terms of Use](https://en.help.roblox.com/hc/articles/115004647846). > **Error:** Please review your experience and confirm that your answers accurately reflect the content of your experience. If you intentionally misrepresent your experience, you may be subject to [moderation consequences](#experience-guidelines-moderation). ## Questionnaire categories The following sections provide guidance on answering the questions within each category of the questionnaire. **Guidelines only apply to content that you create for your experience**, not user-generated content that players bring with them, such as avatar clothing and accessories. After you submit the questionnaire, your experience receives an age recommendation with content descriptors according to the following table. | Age recommendation | Description | | --- | --- | | **All ages** | Content is generally suitable for all ages. May contain occasional mild violence and/or light unrealistic blood. | | **9+** | Content is generally suitable for ages 9 and up. May contain repeated mild violence, heavy unrealistic blood, mild crude humor, and/or mild fear-based content. | | **13+** | Content is generally suitable for ages 13 and up. May contain repeated moderate violence, light realistic blood, moderate crude humor, unplayable gambling content, moderate fear-based content, free-form user creation, and/or is a social hangout. | | **17+** | Content is only suitable for ages 17 and up. May contain strong violence, heavy realistic blood, moderate crude humor, romantic themes, unplayable gambling content, strong language, and/or the presence of alcohol. | > **Info:** To remain compliant with local laws and regulations, experiences with an age recommendation of 17+ are unplayable for players who created their accounts or are located in certain countries or regions, such as Korea, Saudi Arabia, and Türkiye (Turkey). > **Warning:** While parents can use parental controls to bypass most content restrictions according to what they believe is appropriate for their child, Roblox restricts free form user creation and social hangouts to players over 13. ### Violence Violence is the intentional use of physical or psychological force against players or non-playable characters (NPCs). If your experience includes violence, including within any [asset type](/docs/en-us/projects/assets.md#asset-types), you must disclose it in the experience questionnaire based on the violence's maximum level of intensity and how often a user might encounter it. #### Level of intensity After you confirm that your experience contains violence, you must specify whether the maximum level of intensity of violence within your experience is **mild**, **moderate**, or **strong**. If you imply violence anywhere within your experience, such as violence that occurs off-screen that players can hear but can't see, your experience automatically meets the mild criteria. If you visually show violence, the criteria for meeting mild, moderate, or strong violence depends on the **consequence** of the violence. For example, if the consequence of violence is unrealistic, such as characters disappearing the moment they die, your experience meets the mild criteria, but if there is any moment that the consequence of violence is realistic, such as characters mimicking real-life injury or death, your experience meets either the moderate or strong criteria, even if the realistic violence only occurs once. Experiences with strong violence are only available to verified players that are at least 17 years old. > **Error:** Roblox doesn't allow content that contains extreme violence or serious physical or psychological abuse. This includes animal abuse and torture, realistic depictions of extreme gore, or the depiction, support, or glorification of war crimes or human rights violations, including torture. For more information, see Roblox's [Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410) and [17+ Policy Standards](https://en.help.roblox.com/hc/articles/15869919570708). #### Frequency The next question of the violence category asks you to specify whether the frequency of violence within your experience is **occasional** or **repeated**. Occasional violence occurs either rarely or occasionally, such as at a couple key moments of the experience. Repeated violence either indicates that violence occurs often, or that it occurs rarely, but when it does occur, many violent events happen in quick succession. This means that even if a small part of your experience contains repeated violence, it meets the repeated criteria. ### Blood Blood is the red liquid that flows through human and animal bodies that's essential to life. If your experience includes blood, including within any [asset type](/docs/en-us/projects/assets.md#asset-types), you must disclose it within the experience questionnaire and specify whether the realism of blood is **unrealistic** or **realistic**. Unrealistic blood doesn't look like real blood, so if you display blood within your experience as pixels, or as a different color or shape, it meets the unrealistic criteria. Realistic blood, however, has the same color, shape, and splatter properties as blood in the real world. If you answer **unrealistic**, the questionnaire asks if the blood depictions are infrequent and/or fleeting, such as yellow blood splattering on the screen for a few seconds. The next question of the blood category asks you to specify whether the level of intensity of blood within your experience is **light** or **heavy**. If you depict blood anywhere within your experience, such as blood splatter from a distance, your experience automatically meets the light criteria. If you depict heavy bloodshed, such as pools of blood, gushing blood, organs or intestines, decapitation, dismemberment, mutilation of body parts, and/or up-close blood spatter, your experience meets the heavy criteria. Experiences that include heavy bloodshed are only available to verified players that are at least 17 years old. ### Crude humor Crude humor is a type of humor that depicts or references bodily functions, such as belching, flatulence, vomiting, urinating, and/or defecation for comical purposes. If your experience includes crude humor, including within any [asset type](/docs/en-us/projects/assets.md#asset-types), you must specify if it is **mild** or **moderate** within the experience questionnaire based on what bodily functions you use for comical purposes. For example, if you depict or reference flatulence, vomiting, and/or unrealistic looking feces, such as poop coils or the poop emoji, your experience meets the mild criteria. If you depict or reference urine, urination, realistic looking feces, or your experience is primarily themed or set in a bathroom, your experience meets the moderate criteria. ### Gambling Gambling is the practice of exchanging real world money, Robux, or in-experience items of value for a game of chance. While experiences cannot contain playable gambling content, including simulated gambling, you can depict unplayable gambling content, such as showing a casino or people playing cards. ### Social hangout Social hangouts are experiences in which the primary theme or activity is to chat or interact with other players with voice or text either as themselves or as their avatar. While this applies to vibe games, clubs, socializing spaces, and supportive places like sad rooms, it does **not** apply to roleplay and/or life simulation experiences where players adopt various roles like teacher or police officer, or are provided with items to role-play with. If your experience is a social hangout, you must disclose it within the Maturity & Compliance Questionnaire. Social hangouts are only available to players that are at least 13 years old. ### Free-form user creation Free-form user creation are features that allow players to create anything within an experience, such as writing words or making illustrations on a chalkboard. While this applies to in-experience, free-form drawing or creation tools, it does **not** apply to in-experience creations that players assemble with 3D assets, such as building a house or creating an outfit, or anything that goes through Roblox moderation before it's published or replicated. If your experience includes free-form user creation features, you must disclose it within the Maturity & Compliance Questionnaire. Free-form user creation is only available to players that are at least 13 years old. ### Alcohol Alcohol is an intoxicating adult beverage. If your experience includes, depicts, or references alcohol or adult businesses and locations that provide or sell alcohol, you must disclose it within the experience questionnaire. Experiences that depict alcohol are only available to verified players that are at least 17 years old. ### Romantic themes Romantic themes are the non-sexual expression of love or affection. If your experience includes romantic themes, such as a quick kiss on the mouth, you must disclose it within the experience questionnaire. Experiences that include romantic themes are only available to verified players that are at least 17 years old. ### Strong language Strong language is vulgar and obscene language that players cannot use to harass, discriminate, or threaten others, incite violence, or use in a sexual context. For example, strong language content can be depictions of a non-playable character (NPC) using an obscenity like the "f-word" that isn't directed toward another character or group of people, for example "F* off!". If your experience includes strong language, you must disclose it within the experience questionnaire. Experiences that include strong language are only available to verified players that are at least 17 years old. > **Info:** If you want to enable strong language in user communication aside from content for your 17+ experiences, see [here](/docs/en-us/projects/configure-games.md#allow-strong-language). ### Fear Fear-based content contains scary or horrifying elements that may trigger fear in players. If your experience includes scary elements, including within any [asset type](/docs/en-us/projects/assets.md#asset-types), you must specify if it is **mild** or **moderate** within the experience questionnaire based on the content's intensity. For example, mild scary elements can include loud or heavy breathing, sounds of a pounding heart, shrieking or screaming, creepy-looking NPCs, jump scares, ominous music, and/or gameplay that builds suspense; moderate scary elements can include NPCs with disfigured mouths with realistic blood, lack of flesh with realistic-looking connective tissues, organs, and/or blood vessels visible, realistic open wounds and/or leaking/bleeding eyes with realistic blood. ** Moderate Examples (Not Suitable for Young Readers)** The following images meet the moderate criteria for fear-based content. _Includes visible organs, blood vessels, and leaking/bleeding eyes._ _Includes realistic blood and a lack of flesh._ _Includes realistic open wounds and leaking/bleeding eyes._ After you specify the level of intensity, the questionnaire asks you to specify whether the frequency of the fear-based content within your experience is **occasional** or **repeated**. Occasional scary elements occur either rarely or occasionally, such as at a couple key moments of the experience. Repeated scary elements either indicate that fear-based content occurs often, or that it occurs rarely, but when it does occur, many scary elements happen in quick succession. > **Info:** The fear descriptor only applies to the content you create for your experiences. It does **not** apply to user-generated content that players bring with them into your experience, such as avatar clothing and accessories. ## Experience guidelines moderation Roblox relies on the information you provide in the experience questionnaire to generate accurate guidelines, so the moderation team may review your generated guidelines to ensure their validity based on the content of your experience. Whenever Roblox discovers a discrepancy between your submission and the content in your experience, the following actions occur: - You receive a moderation notification through a private message on Roblox. - The **Questionnaire** page on the [Creator Dashboard](https://create.roblox.com/dashboard/creations) updates to provide moderator feedback and guidance on how to generate accurate experience guidelines. - Roblox removes the current guidelines from your experience's main page, and your experience becomes only visible to players aged 13+ years. - Roblox provides a moderation action. - If your experience contains 17+ content without an age recommendation of 17+, your experience is subject to moderation consequences. - If your experience contains content that is noncompliant with Roblox's [Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410), [Terms of Use](https://en.help.roblox.com/hc/articles/115004647846), or [17+ Policy Standards](https://en.help.roblox.com/hc/articles/15869919570708), your experience is subject to moderation consequences. - If your experience otherwise has inaccurate guidelines according to its content, Roblox may remove the current guidelines (or some or all of the descriptors if the age recommendation is 17+). Roblox treats unrated experiences the same as those with an age recommendation of **Ages 13+**. --- title: "Experience notifications" url: /docs/en-us/production/promotion/experience-notifications last_updated: 2026-06-29T19:34:09Z description: "Send experience notifications within your experience with Luau APIs. Experience notifications are a way for users to keep up with their favorite experiences through timely, personalized notifications." --- # Experience notifications **Experience notifications** are a way for [opted-in](https://en.help.roblox.com/hc/en-us/articles/24769602332692-Out-of-Experience-Notifications) users age 13+ to keep up with their favorite experiences through timely, personalized notifications. As the developer, you can determine what kinds of in‑experience activities are most important to notify your users about, as well as define the notification content. #### Async Activity ![Example notification](../../assets/open-cloud/experience-notifications/Example-Notification-A.png) ![Example notification](../../assets/open-cloud/experience-notifications/Example-Notification-D.png) #### Progress / Achievement ![Example notification](../../assets/open-cloud/experience-notifications/Example-Notification-C.png) ![Example notification](../../assets/open-cloud/experience-notifications/Example-Notification-E.png) #### User Mentions ![Example notification](../../assets/open-cloud/experience-notifications/Example-Notification-B.png) ![Example notification](../../assets/open-cloud/experience-notifications/Example-Notification-F.png) The experience notification system features the following: - **Customizable notifications with parameters** — Full flexibility to customize the [notification message](#create-a-notification-string) with parameters, for example:**Your gold goose egg has hatched!****Allie @LaterSk8er1 just beat your record on the Tokyo Tour track!** - **Launch Data** — Include optional [launch data](#include-launch-and-analytics-data) that can be read through `Class.Player:GetJoinData()` when the notification recipient joins. This could involve routing a user to a coordinate location or personalizing their joining experience. - **Analytics Support** — Track your reachable audience and the performance of your notifications in the [Creator Dashboard](https://create.roblox.com/dashboard/creations). ## Eligibility requirements In order to use the APIs to send notifications, the experience must meet the following base criteria: - Minimum 100 visits since launch. - The experience must not be under moderation. - You as the developer must have permission to manage the experience. ## Usage guidelines ** Best practices** Notifications should be **personalized** to the receiver and should be based on in‑experience activity that's specifically relevant to the user. Inversely, notifications should not be of a generic, advertising nature. > **Success:** You're 2 races away from completing the weekly challenge! > **Error:** A new line of race cars just dropped in Race Car Craze. Check them out! Ideally, notifications should also alert users of something they can take **immediate action** on. Avoid purely informational notifications that do not prompt a direct response or action. > **Success:** Allie @LaterSk8er1 just beat your record on the Tokyo Tour track! Time for revenge? > **Error:** It's been a few days since you participated in a race with Allie @LaterSk8er1. ** Respect community standards** All notification content and behaviors are subject to Roblox's [Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards) and platform‑wide [text filtering](/docs/en-us/ui/text-filtering.md), regardless of your experience's [age guidelines](/docs/en-us/production/promotion/experience-guidelines.md). This means that if your experience is a 17+ experience, your notifications are still subject to the platform‑wide standards, **not** the [17+ Policy Standards](https://en.help.roblox.com/hc/en-us/articles/15869919570708). ** Avoid deceptive nudge tactics** Notification content is **not** permitted to incorporate dark patterns or other tactics that manipulate or deceive users into making choices they don't intend, or which may be counter to their best interests. This could include the following: - **Disguised Ads** — Notifications that are intentionally disguised as organic content, but are actually advertising. For example, assume that clicking the following notification leads to Petz World but no "important information" is displayed. > **Error:** Click here for important information about your Petz World account. - **Time Pressured Actions** — Notifications that pressure users into clicking, subscribing, consenting, or purchasing through applying false time pressure. > **Error:** Make a purchase in Petz World in the next 10 minutes to avoid missing out on important gameplay updates! - **Bait-and-Switch with Free Items or Other Rewards** — Notifications that falsely tell users that they'll receive something for free when it's not. For example, upon clicking the following notification, it becomes clear that something further is required to get the gift. > **Error:** Play Petz World now and get a free dog bed! - **Tricking Users Into Purchasing** — Notifications that trick users into making unintended purchases. For example, assume that clicking the following notification leads directly to a purchase system pre‑loaded with items that the user didn't choose to buy. > **Error:** Check out our new Petz in Petz World! ** Do not gate gameplay** Experiences should **not** require users to turn on notifications in order to participate or advance in gameplay. ## Implementation Implementing experience notifications begins with [creating a notification string](#create-a-notification-string) and including the [package](#include-the-package) in your project. Once these are set up, you can [send notifications](#send-an-experience-notification) with optional [custom parameters](#customize-notifications-using-parameters). Alternatively, you can use the [Open Cloud API](/docs/en-us/cloud/guides/experience-notifications.md) to trigger notifications through freeform API requests. ### Create a notification string As with [Player Invite Prompts](/docs/en-us/production/promotion/invite-prompts.md), you must create and edit your notification strings in the [Creator Dashboard](https://create.roblox.com/dashboard/creations). **There is no default experience notification string, so this step is required.** 1. Navigate to the [Creator Dashboard](https://create.roblox.com/dashboard/creations). 2. Similar to [badges](/docs/en-us/production/publishing/badges.md), notification strings are tied to a **specific experience**. Locate that experience's thumbnail and click on it. 3. In the left column, under **Engagement**, click **Notifications**. 4. In the center region, click the **Create a Notification String** button. 5. Fill in an identifier name (only visible to you) and the custom notification string; this is limited to 99 characters and can include unlimited [custom parameters](#customize-notifications-using-parameters). Notifications will automatically use the title of your experience as the notification title, but you can additionally use **{experienceName}** to reference your experience in the notification body text. Example notification strings:**You're {numQuests} quests away from completing the weekly challenge!****Your {eggName} hatched! Come meet your new pet.****You won {numRaces} races this week and unlocked the {racetrackName} track!****{userId-friend} just beat your record on the Tokyo Tour track! Time for revenge?** > **Warning:** Before writing notification strings, please review the [guidelines](#usage-guidelines) for using the system. 6. When ready, click the **Create Notification String** button. 7. On the notifications page, in the table of notifications, click the **⋯** button in the **Actions** column and select **Copy Asset ID**. 8. Use the copied ID for the `messageId` key value in the `payload` table as demonstrated in the [example script](#send-an-experience-notification). ### Include the package To implement experience notifications, you must obtain the Luau package from the [Creator Store](/docs/en-us/production/creator-store.md). 1. From Studio's **Window** menu or **Home** tab toolbar, open the [Toolbox](/docs/en-us/projects/assets/toolbox.md) and select the **Creator Store** tab. 2. Make sure the **Models** sorting is selected, then click the **See All** button for **Categories**. 3. Locate and click the **Packages** tile. 4. Locate the **Open Cloud** module and click it, or drag‑and‑drop it into the 3D view. 5. In the [Explorer](/docs/en-us/studio/explorer.md) window, move the entire **OpenCloud** model into **ServerScriptService**. ### Send an experience notification Once you've [created a notification string](#create-a-notification-string) and included the [package](#include-the-package) in your project, you can send notifications from server‑side scripts. Notifications will be delivered to [opted-in](https://en.help.roblox.com/hc/en-us/articles/24769602332692-Out-of-Experience-Notifications) users age 13+ through their Roblox notification stream, at which point they can join the experience directly via the **Join** button on the notification and spawn according to your [launch data](#include-launch-and-analytics-data). ![Notifications stream on the Roblox app](../../assets/open-cloud/experience-notifications/Notification-Stream.png) To send a basic notification to a specific user, include the [notification string](#create-a-notification-string) asset ID in the payload's `messageId` field, then call the [createUserNotification](#createusernotification) function with the recipient's `Class.Player.UserId` and the request data. ```lua local ServerScriptService = game:GetService("ServerScriptService") local OCUserNotification = require(ServerScriptService.OpenCloud.V2.UserNotification) local recipientPlayerID = 505306092 -- In the payload, "messageId" is the value of the notification asset ID local userNotification = { payload = { messageId = "5dd7024b-68e3-ac4d-8232-4217f86ca244", type = "MOMENT" } } local result = OCUserNotification.createUserNotification(recipientPlayerID, userNotification) if result.statusCode ~= 200 then print(result.statusCode) print(result.error.code) print(result.error.message) end ``` ### Customize notifications using parameters To customize the notification for each recipient, you can include **parameters** in the [notification string](#create-a-notification-string), then customize the parameters when calling the API. For example, you can define the notification string as: **{userId-friend} beat your high score by {points} points! Time to level up?** Then, set the `userId-friend` and `points` parameters in the script: ```lua local ServerScriptService = game:GetService("ServerScriptService") local OCUserNotification = require(ServerScriptService.OpenCloud.V2.UserNotification) local recipientPlayerID = 505306092 local userIdFriendParam = {int64Value = 3702832553} local pointsParam = {stringValue = "5"} -- In the payload, "messageId" is the value of the notification asset ID -- In this example, the notification string is "{userId-friend} beat your high score by {points} points! Time to level up?" local userNotification = { payload = { messageId = "ef0e0790-e2e8-4441-9a32-93f3a5783bf1", type = "MOMENT", parameters = { ["userId-friend"] = userIdFriendParam, ["points"] = pointsParam } } } local result = OCUserNotification.createUserNotification(recipientPlayerID, userNotification) if result.statusCode ~= 200 then print(result.statusCode) print(result.error.code) print(result.error.message) end ``` ### Prompt users to enable notifications To encourage users to enable notifications for your experience, you can display an in‑experience permission prompt to users age 13+ using the `Class.ExperienceNotificationService:PromptOptIn()` method. ![The in-experience permission prompt encourages users to enable notifications](../../assets/promotion/misc/In-Experience-Enable-Notifications.png) You can trigger the prompt in any suitable context within your experience that warrants a future notification. The prompt's text is not customizable and is standardized across all experiences. The modal will **not** appear if the user: - Is under the age of 13. - Has already enabled notifications for your experience. - Has already seen the permission prompt for your experience in the past 30 days. To prompt users to enable notifications, you should first determine whether the user is eligible. Once confirmed, you can display the permission prompt to the user. 1. Call `Class.ExperienceNotificationService:CanPromptOptInAsync()`, wrapped in a `Global.LuaGlobals.pcall|pcall()` since it's an asynchronous network call that may occasionally fail. 2. If the user can be prompted, call `Class.ExperienceNotificationService:PromptOptIn()`. ```lua local ExperienceNotificationService = game:GetService("ExperienceNotificationService") -- Function to check whether the player can be prompted to enable notifications local function canPromptOptIn() local success, canPrompt = pcall(function() return ExperienceNotificationService:CanPromptOptInAsync() end) return success and canPrompt end local canPrompt = canPromptOptIn() if canPrompt then local success, errorMessage = pcall(function() ExperienceNotificationService:PromptOptIn() end) end -- Listen to opt-in prompt closed event ExperienceNotificationService.OptInPromptClosed:Connect(function() print("Opt-in prompt closed") end) ``` ### Include launch and analytics data To further improve user experience, you can include **launch data** in the notification, useful for scenarios such as routing users to a coordinate location or personalizing the joining experience. Additionally, you can include [analytics](#analytics) data to segment the performance of different categories of notifications. Please also refer to the [Player invite prompts](/docs/en-us/production/promotion/invite-prompts.md#include-launch-data) example on how launch data can be set and used. ```lua local ServerScriptService = game:GetService("ServerScriptService") local OCUserNotification = require(ServerScriptService.OpenCloud.V2.UserNotification) local recipientPlayerID = 505306092 -- In the payload, "messageId" is the value of the notification asset ID local userNotification = { payload = { messageId = "5dd7024b-68e3-ac4d-8232-4217f86ca244", type = "MOMENT", joinExperience = { launchData = "Test_Launch_Data" }, analyticsData = { category = "Test_Analytics_Category" } } } local result = OCUserNotification.createUserNotification(recipientPlayerID, userNotification) if result.statusCode ~= 200 then print(result.statusCode) print(result.error.code) print(result.error.message) end ``` ## Delivery system A spam prevention system exists to ensure the quality of notifications for users and protect the shared notification channel for all developers. Because of this, delivery of notifications is not guaranteed. This spam prevention system is directly informed by user engagement: the more users engage with your notifications, the more reach they'll receive. You can transparently track engagement metrics in the analytics dashboard, as explained below. Experience notifications have a static throttle limit; each user can receive one notification per day from a given experience, and you receive transparent feedback when a user's throttle limit is reached. Additionally, the following list outlines some of the special cases which may result in **non‑delivery** of a notification: - Experience **eligibility requirements** are not met. - Recipient is not opted in to notifications from your experience. - Recipient's throttle limit for your experience has been reached. - Recipient's aggregate daily throttle limit has been reached. - Missing or invalid request parameters. - Notification string was moderated. - For notifications with user mentions, non-delivery occurs if either of these conditions are met: - The receiver and mentioned user are not friends. - The mentioned user has **No** selected for "Update friends about my activity?" under **Privacy** → **Other Settings** in their Roblox account settings. ## Analytics Performance of your notifications and notifiable audience are displayed in the **Analytics** tab of the **Notifications** page where you [configure notification strings](#create-a-notification-string) (simply tab from **Creations** to **Analytics**). 1. Navigate to the [Creator Dashboard](https://create.roblox.com/dashboard/creations). 2. Similar to [badges](/docs/en-us/production/publishing/badges.md), notification strings are tied to a **specific experience**. Locate that experience's thumbnail and click on it. 3. In the left column, under **Engagement**, click **Notifications**. 4. On the target page, click the **Analytics** tab to switch to the analytics dashboard. ### Notifications summary The summary section serves as a snapshot of the aggregate performance of your notifications. A minimum of 100 aggregate impressions is required to display the performance statistics. | Statistic | Description | | --- | --- | | **Opted-in Users** | The total number of users that have turned on notifications for your experience. Please note that this does include users under the age of 13 who are only able to receive notification of [experience updates](/docs/en-us/production/promotion/experience-events.md#announce-experience-updates), not personalized [experience notifications](/docs/en-us/production/promotion/experience-notifications.md). | | **Impressions** | The total number of user impressions all of your notifications have received in aggregate. | | **Clicks** | The total number of clicks all of your notifications have received in aggregate. | | **CTR** | The rate at which users are clicking on your notifications, calculated as the ratio of clicks to impressions. | | **Turn Off** | The rate at which users are turning off notifications for your experience directly from your notifications, calculated as the ratio of turn off actions to impressions. | | **Dismiss** | The rate at which users are dismissing your notifications, calculated as the ratio of dismiss actions to impressions. | ### Itemized stats The **Experience Notifications** table displays detailed performance statistics for each notification with at least 100 impressions, ordered by the date of first impression for that notification. The **Name** column is the key identifier for the notification. By default, the name matches the identifier name you specified when [creating the notification string](#create-a-notification-string), but you can override it through the `category` field in your API calls, in which case `category` overrides the name. Changing the string name in the [Creator Dashboard](https://create.roblox.com/dashboard/creations) or changing the string your message ID references in the API call will generate a new row in the table. If you'd like to A/B test the performance of different strings, it's recommended that you create an entirely new notification string with a similar name, for example: - **EggHatchA** — "Your gold egg has hatched! Come meet your new pet." - **EggHatchB** — "It's hatching time! Come meet your new pet." ## API reference ### Functions #### createUserNotification **createUserNotification (**userId : [number](/docs/en-us/luau/numbers.md), userNotification : [UserNotification](#usernotification)**)** : [UserNotificationResult](#usernotificationresult) Sends a notification from a server‑side script. Requires the recipient's `Class.Player.UserId` and a [UserNotification](#usernotification). Returns a [UserNotificationResult](#usernotificationresult). ```lua local ServerScriptService = game:GetService("ServerScriptService") local OCUserNotification = require(ServerScriptService.OpenCloud.V2.UserNotification) local recipientPlayerID = 505306092 -- In the payload, "messageId" is the value of the notification asset ID local userNotification = { payload = { messageId = "5dd7024b-68e3-ac4d-8232-4217f86ca244", type = "MOMENT" } } local result = OCUserNotification.createUserNotification(recipientPlayerID, userNotification) if result.statusCode ~= 200 then print(result.statusCode) print(result.error.code) print(result.error.message) end ``` ### Types #### UserNotification Table containing details on the notification to be sent to the user. Must contain a `payload` table with required `messageId` and `type` strings, and optional `parameters`, `joinExperience`, and `analyticsData` tables. | Key | Type | Description | | --- | --- | --- | | `messageId` | string | An ID that represents a customizable notification message template that you create in the [Creator Dashboard](https://create.roblox.com/dashboard/creations). | | `type` | string | The type of notification. Only `"MOMENT"` is currently supported. | | `parameters` | table | A table of parameters used to render a notification message template. See [Customize notifications using parameters](#customize-notifications-using-parameters) for example usage. | | `joinExperience` | table | A call-to-action that represents joining an experience. Currently supports a `launchData` key‑value pair which represents arbitrary data available to an experience when a user joins the experience from the notification; this value is limited to a maximum of 200 bytes. See [Include launch and analytics data](#include-launch-and-analytics-data) for example usage. | | `analyticsData` | table | Data for how analytics are reported. Currently supports a `category` key‑value pair which represents the notification category, used to group analytics data. See [Include launch and analytics data](#include-launch-and-analytics-data) for example usage. | #### UserNotificationResult A wrapper object that holds the response from a sent notification. Contains the following key‑value pairs: | Key | Type | Description | | --- | --- | --- | | `statusCode` | number | The HTTP status code for the request. | | `error` | table | Table containing `code` and `message` keys describing the GRPC error code and the error message, respectively. | | `response` | table | Table containing `id` and `path` keys describing a unique UUID and the resource path of the user notification, respectively. | --- title: "Promotion" url: /docs/en-us/production/promotion last_updated: 2026-06-29T19:34:05Z description: "An overview of promotional tools you can use to promote your experiences, avatar items, and social media." --- # Promotion **Promotion** on Roblox allows you to engage a global audience to drive traffic and awareness to your experiences, avatar items, and social media. All promotional content on the platform must adhere to Roblox's [Community Standards](https://en.help.roblox.com/hc/articles/203313410) and [Terms of Use](https://en.help.roblox.com/hc/en-us/articles/115004647846), and if your promotional content is advertising, it must also adhere to Roblox's [Advertising Standards](https://en.help.roblox.com/hc/articles/13722260778260). ## Experiences The following table details promotional methods for experiences on the platform. | Method | Description | | --- | --- | | **Search ads** | Promote your experiences in search results by creating search ads. This form of advertising allows developers to target their ads to users using search, increasing the likelihood of engagement based off of specific keywords.

For more information, see [Ads Manager](/docs/en-us/production/promotion/ads-manager.md). | | **Sponsored experiences** | Advertise your experiences within the **Sponsored** category on the [Home](https://www.roblox.com/home) page. Each sponsored experience ad campaign lets you choose your audience, schedule the duration of your ad campaign, and specify your budget per day.

For more information, see [Ads Manager](/docs/en-us/production/promotion/ads-manager.md). | | **Immersive portal ad campaigns** | Advertise experiences through the immersive portal ad format that transports users from other creators' experiences directly into your own.

For more information, see [Ads Manager](/docs/en-us/production/promotion/ads-manager.md). | | **Time-based events** | Invite users back into your experiences with engaging time-based events that display on your experience's main details page. For more information, see [Events and updates](/docs/en-us/production/promotion/experience-events.md). | | **Player invite prompts** | [Player invite prompts](/docs/en-us/production/promotion/invite-prompts.md), implemented directly inside your experience, encourage players to invite their friends and increase co‑experience gameplay. | | **Friend invite reward system** | The [friend reward invite system](/docs/en-us/production/promotion/referral-system.md) rewards existing users for bringing in new users to your experiences. | | **Experience notifications** | [Notifications](/docs/en-us/production/promotion/experience-notifications.md) are a way for 13+ users to keep up with their favorite experiences through timely, personalized notifications. As the developer, you can determine what kinds of in‑experience activities are most important to notify your users about, as well as define the notification content. | ## Avatar items The following table details promotional methods for avatar items on the platform. | Method | Description | | --- | --- | | **Sponsored items** | Sponsoring items allows you to increase the discoverability of your 3D user-generated content to users within the [Marketplace](https://www.roblox.com/catalog). You can choose your audience, schedule the duration of your ad, and specify your budget per day.

For more information, see [Sponsor items](/docs/en-us/marketplace/sponsor-items.md). | ## Social media [Social media links](/docs/en-us/production/social-media-links.md) are links to your social media channels. Use social media links to promote your social media presence by embedding these links on your experience details page. ## Share links [Share links](/docs/en-us/production/share-links.md) are links to your Roblox experiences. Use share links on your social media to promote your Roblox experiences and to track metrics for acquisition through off-platform channels. Unlike social media links, which direct users to your social media channels, share links promote and direct users to your Roblox experiences. ## Communities [Communities](/docs/en-us/projects/groups.md) can serve as a form of marketing for the experiences that you publish through them. Users playing an experience often engage with the creator of the experience, which in this case is the group page. Here, users can find social links, follow related communities for new content, and post on comment boards. Some recommendations for this page include: - Ensure your community is named after your studio or company. - Use thumbnails and descriptions to communicate your brand and identity. - Include social links, such as YouTube, Discord, or X (Twitter).
--- title: "Player invite prompts" url: /docs/en-us/production/promotion/invite-prompts last_updated: 2026-06-29T19:34:06Z description: "Invite prompts are prompts sent to the player of an experience to invite their friends to join them." --- # Player invite prompts In addition to common [promotion](/docs/en-us/production/promotion.md) methods for increasing your player base, you can implement **invite prompts** directly inside your experience, encouraging players to invite their friends and increase co-experience gameplay. The invite prompt system features the following: - **Dynamic Invitees** — Prompt players to invite multiple friends from a selection list, or invite one specific friend. - **Launch Data** — Include optional [launch data](#include-launch-data) that can be read through `Class.Player:GetJoinData()` when the invited friend joins. Example use cases include routing invited friends to a coordinate location or personalizing the joining experience for the invitee. - **Customizable Text** — Customize the [invite prompt](#prompt-an-invite) message and the [notification](#set-notification-options) message. For example, an invite prompt for the player may read "Ask your friends to join the adventure!" and the notification message for the invited friend(s) may read "{displayName} wants you to join their adventure in {experienceName}!". You can also track and reward inviters and invitees using the [Friend Invite Reward System](/docs/en-us/production/promotion/referral-system.md). #### In-experience prompt #### Invite notification _ Notification in Roblox app_ _ Notification on phone lock screen_ ## Set invite options By default, an invite prompt for the player shows a menu of their friends with **Invite** buttons. To customize the prompt message, target a specific friend, or include launch data in the invite, you'll need to set up an `Class.ExperienceInviteOptions` object with the desired properties. | Property | Type | Description | | --- | --- | --- | | `Class.ExperienceInviteOptions.PromptMessage\|PromptMessage` | string | Custom text shown on the invite prompt for the sending player, for example "Ask your friends to join the adventure!" for a multi-friend invite prompt, or "Invite this friend to join the adventure!" for a specific friend invite prompt. Note that if your custom invite prompt message is long enough to overflow the bounds of the UI, it will not be shown. | | `Class.ExperienceInviteOptions.InviteUser\|InviteUser` | number | Roblox `Class.Player.UserId\|UserId` of the specific friend to invite; if not provided, the player will be prompted to pick from a list of friends. | | `Class.ExperienceInviteOptions.InviteMessageId\|InviteMessageId` | string | Asset ID that maps to a **Notification** asset type. This asset is used to store/localize a custom string for the invite notification that friends receive. See [Setting Notification Options](#set-notification-options) for details. | | `Class.ExperienceInviteOptions.LaunchData\|LaunchData` | string | Used to set a parameter in `Class.Player:GetJoinData()` when a friend joins from the invite notification. Maximum of 200 characters. See [Include launch data](#include-launch-data) for a usage example. | #### Multiple friends ```lua local SocialService = game:GetService("SocialService") local Players = game:GetService("Players") local player = Players.LocalPlayer -- Construct invite options with a custom prompt message local inviteOptions = Instance.new("ExperienceInviteOptions") inviteOptions.PromptMessage = "Ask your friends to join the adventure!" ``` #### Specific friend ```lua local SocialService = game:GetService("SocialService") local Players = game:GetService("Players") local player = Players.LocalPlayer local receiverUserID = 505306092 -- Construct invite options with friend's user ID and a custom prompt message local inviteOptions = Instance.new("ExperienceInviteOptions") inviteOptions.InviteUser = receiverUserID inviteOptions.PromptMessage = "Invite this friend to join the adventure!" ``` ## Set notification options By default, the invite notification that friends receive contains the sender's `Class.Player.DisplayName|DisplayName`, username, and the experience name. To customize the message, you can create a **Notification** asset on the [Creator Dashboard](https://create.roblox.com/dashboard/creations) and include its asset ID as a parameter of `Class.ExperienceInviteOptions`. 1. Navigate to the [Creator Dashboard](https://create.roblox.com/dashboard/creations). 2. Similar to [badges](/docs/en-us/production/publishing/badges.md), notification strings are tied to a **specific experience**. Locate that experience's thumbnail and click on it. 3. In the left column, under **Engagement**, click **Notifications**. 4. In the center region, click the **Create a Notification String** button. 5. Fill in an identifier name (only visible to you) and the custom notification text. Note that you must include **{experienceName}** as a placeholder to identify the experience's name for invited friends, and you can optionally include the sender's `Class.Player.DisplayName|DisplayName` through the **{displayName}** placeholder. Example notification strings: - {displayName} wants you to join their adventure in {experienceName}! - {displayName} just cleared the sixth stage of {experienceName}. Can you? 6. When ready, click the **Create Notification String** button. 7. On the notifications page, in the table of notifications, click the **⋯** button in the **Actions** column and select **Copy Asset ID**. 8. In the `Class.ExperienceInviteOptions` object for the invite prompt, paste the asset ID as the value of the `Class.ExperienceInviteOptions.InviteMessageId|InviteMessageId` property.```lua local SocialService = game:GetService("SocialService") local Players = game:GetService("Players") local player = Players.LocalPlayer -- Construct invite options with friend's user ID local inviteOptions = Instance.new("ExperienceInviteOptions") inviteOptions.InviteMessageId = "ef0e0790-e2e8-4441-9a32-93f3a5783bf1" ``` ## Prompt an invite To prompt an invite, you should first determine whether the player **can** send an invite, as the ability may vary depending on the platform or player. Once confirmed, you can display the invitation prompt to the player. 1. Call `Class.SocialService:CanSendGameInviteAsync()`, wrapped in a `Global.LuaGlobals.pcall()` since it's an asynchronous network call that may occasionally fail. 2. If the invite ability is confirmed, call `Class.SocialService:PromptGameInvite()` with the optional [invite options object](#set-invite-options) as the second argument. Once prompted, the player will see an on-screen prompt to invite multiple friends, or the specific friend defined in the [invite options object](#set-invite-options). When the player then clicks the **Invite** button for one or more friends, those friends will receive a notification containing the sender's `Class.Player.DisplayName|DisplayName`, username, and the experience name. Notifications may be further customized as outlined in [Set notification options](#set-notification-options). #### Multiple Friends ```lua local SocialService = game:GetService("SocialService") local Players = game:GetService("Players") local player = Players.LocalPlayer -- Function to check whether the player can send an invite local function canSendGameInvite(sendingPlayer) local success, canSend = pcall(function() return SocialService:CanSendGameInviteAsync(sendingPlayer) end) return success and canSend end local canInvite = canSendGameInvite(player) if canInvite then SocialService:PromptGameInvite(player) end ``` #### Specific Friend ```lua local SocialService = game:GetService("SocialService") local Players = game:GetService("Players") local player = Players.LocalPlayer local receiverUserID = 505306092 -- Construct invite options with friend's user ID local inviteOptions = Instance.new("ExperienceInviteOptions") inviteOptions.InviteUser = receiverUserID -- Function to check whether the player can send an invite local function canSendGameInvite(sendingPlayer) local success, canSend = pcall(function() return SocialService:CanSendGameInviteAsync(sendingPlayer, receiverUserID) end) return success and canSend end local canInvite = canSendGameInvite(player) if canInvite then SocialService:PromptGameInvite(player, inviteOptions) end ``` ## Include launch data To further improve in-experience cooperation or to incentivize player invites, you can include **launch data** in an invite prompt, useful for scenarios such as routing invited friends to a coordinate location or personalizing the joining experience for the invitee. 1. When [prompting an invite](#prompt-an-invite), include an `Class.ExperienceInviteOptions` object with relevant data that will be used when the friend joins the experience, for example the sender's `Class.Player.UserId`, the ID of a [badge](/docs/en-us/production/publishing/badges.md) to award to the friend upon joining, or a coordinate location to spawn the friend at. If you need to compile multiple pieces of data, encode the data using `Class.HttpService:JSONEncode()|JSONEncode()`. #### Multiple Friends```lua local HttpService = game:GetService("HttpService") local SocialService = game:GetService("SocialService") local Players = game:GetService("Players") local player = Players.LocalPlayer local data = { senderUserID = player.UserId, spawnLocation = {12, 48, 205.5} } local launchData = HttpService:JSONEncode(data) -- Construct invite options with launch data local inviteOptions = Instance.new("ExperienceInviteOptions") inviteOptions.LaunchData = launchData -- Function to check whether the player can send an invite local function canSendGameInvite(sendingPlayer) local success, canSend = pcall(function() return SocialService:CanSendGameInviteAsync(sendingPlayer) end) return success and canSend end local canInvite = canSendGameInvite(player) if canInvite then SocialService:PromptGameInvite(player, inviteOptions) end ```#### Specific Friend```lua local HttpService = game:GetService("HttpService") local SocialService = game:GetService("SocialService") local Players = game:GetService("Players") local player = Players.LocalPlayer local receiverUserID = 505306092 local data = { senderUserID = player.UserId, spawnLocation = {12, 48, 205.5} } local launchData = HttpService:JSONEncode(data) -- Construct invite options with friend's user ID and launch data local inviteOptions = Instance.new("ExperienceInviteOptions") inviteOptions.InviteUser = receiverUserID inviteOptions.LaunchData = launchData -- Function to check whether the player can send an invite local function canSendGameInvite(sendingPlayer) local success, canSend = pcall(function() return SocialService:CanSendGameInviteAsync(sendingPlayer, receiverUserID) end) return success and canSend end local canInvite = canSendGameInvite(player) if canInvite then SocialService:PromptGameInvite(player, inviteOptions) end ``` > **Warning:** Always remember to include `inviteOptions` containing the launch data (maximum of 200 characters) as the second parameter of `Class.SocialService:PromptGameInvite()|PromptGameInvite()`. 2. For incoming friends who join via the notification, check for launch data on the server side through `Class.Player:GetJoinData()`. If you encode multiple pieces of data into JSON for the invite prompt, remember to decode it with `Class.HttpService:JSONDecode()|JSONDecode()`.```lua local HttpService = game:GetService("HttpService") local Players = game:GetService("Players") local ATTEMPT_LIMIT = 10 local RETRY_DELAY = 1 local function onPlayerAdded(player) local launchData for _ = 1, ATTEMPT_LIMIT do task.wait(RETRY_DELAY) local joinData = player:GetJoinData() if joinData.LaunchData ~= "" then launchData = joinData.LaunchData break end end if launchData then local data = HttpService:JSONDecode(launchData) print(data.senderUserID) print(data.spawnLocation) else warn("No launch data received!") end end Players.PlayerAdded:Connect(onPlayerAdded) ``` > **Info:** Launch data can take a few seconds to appear after the friend joins from the invite. As shown in the code sample above, it's recommended that you wait until the launch data arrives before attempting to use it. 3. If the launch data exists, you can use it for a wide variety of design scenarios, including: - Spawn the incoming friend at the beginning of a challenging obstacle course that the sender just completed, based on a coordinate location passed through the launch data. - Check if the **sender** is in the place, based on their `Class.Player.UserId` in the launch data, and teleport the friend's character near their character. --- title: "Friend referral system" url: /docs/en-us/production/promotion/referral-system last_updated: 2026-06-29T19:34:05Z description: "Use referral links to track and reward players that have successfully invited other players into your experience, and players that have joined your experience using a referral link invitation from another player." --- # Friend referral system > **Info:** If you previously used this feature while it was still in beta, make sure to implement the new [customized reward banners](#create-a-reward-banner) to maximize the reach of your friend referral system. | | | | --- | --- | The friend referral system encourages existing players to bring new players into your experience, increasing player retention and overall engagement. Players can access and share referral links from [player invite prompts](/docs/en-us/production/promotion/invite-prompts.md) or directly from the default in-experience invite menu. As a developer, you can create customized banners to advertise your reward system and use shareable referral links to: - Track which players have successfully invited other players into your experience. - Track which players have joined your experience using a referral link invitation from another player. - Create and distribute rewards to both inviters and invitees. _Inviters send out invites_ _Invitees join the experience_ To implement a referral system, [set up a referral event](#set-up-a-referral-event) and [create referral rewards](#grant-referral-rewards) in Studio. The `ReferredByPlayerId` property of `Class.Player:GetJoinData()|GetJoinData()` automatically populates for all types of invitations and gives you access to the user ID of the referring player. You can then access this data in the `Players.PlayerAdded` event to identify the inviter and grant rewards to both the inviter and the invitee. ```lua function onPlayerAdded(player) local referredByPlayerId = player:GetJoinData().ReferredByPlayerId local referrerEvent: RemoteEvent = ReplicatedStorage:FindFirstChild("ReferralReceivedEvent") referrerEvent:FireClient(player, referredByPlayerId) end Players.PlayerAdded:Connect(onPlayerAdded) ``` To make sure that players understand the reward system, [implement a customized reward banner](#create-a-reward-banner) at the top of your friend invite modal showing them details about the rewards they can earn. ## Set up a referral event To set up a referral event: 1. Set up a `RemoteEvent` in `ReplicatedStorage` to create a remote event to communicate with the client when a referral is received. 2. Retrieve the inviter's user ID using `ReferredByPlayerId` to track player joins and handle the referral logic in your server-side script during the `Players.PlayerAdded` event. ```lua local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") -- Create or get the RemoteEvent for handling referrals local referrerEvent: RemoteEvent = ReplicatedStorage:FindFirstChild("ReferralReceivedEvent") -- Function that triggers when a player joins function onPlayerAdded(player) local joinData = player:GetJoinData() local referredByPlayerId = joinData.ReferredByPlayerId -- Check if the player was invited through a referral if referredByPlayerId and referredByPlayerId ~= 0 then -- Fire the referral event to the client, passing the inviter's ID referrerEvent:FireClient(player, referredByPlayerId) -- Additional logic for rewarding inviter and invitee can be added here -- e.g., rewardReferrer(referredByPlayerId) -- e.g., rewardInvitee(player) end end -- Connect the function to the PlayerAdded event Players.PlayerAdded:Connect(onPlayerAdded) ``` ## Grant referral rewards To encourage participation, grant rewards to both inviters and invitees. For example, you can give inviters a badge or in-experience currency when their friend joins the experience, and give invitees a welcome reward for joining the experience through a referral link. ```lua local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") -- Create or get the RemoteEvent for handling referrals local referrerEvent: RemoteEvent = ReplicatedStorage:FindFirstChild("ReferralReceivedEvent") -- Function that triggers when a player joins function onPlayerAdded(player) local joinData = player:GetJoinData() local referredByPlayerId = joinData.ReferredByPlayerId -- Check if the player was invited through a referral if referredByPlayerId and referredByPlayerId ~= 0 then -- Fire the referral event to the client, passing the inviter's ID referrerEvent:FireClient(player, referredByPlayerId) -- Reward the inviter function rewardReferrer(referrerId) local referrerPlayer = Players:GetPlayerByUserId(referrerId) if referrerPlayer then -- Grant the inviter their reward -- Example: referrerPlayer.leaderstats.Coins.Value += 100 end end -- Reward the invitee function rewardInvitee(player) -- Grant the invitee their reward -- Example: player.leaderstats.WelcomeBonus.Value += 50 end end end -- Connect the function to the PlayerAdded event Players.PlayerAdded:Connect(onPlayerAdded) ``` ## Create a reward banner Reward banners encourage users to invite friends to join your experience by showing them details about the referral rewards they can earn. These banners are displayed at the top of your friend invite modal. To create a customized reward banner: 1. In the Creator Hub, go to **Creations** and select the experience that you have set up a referral event for. 2. Go to **Engagement** ⟩ **Referral Rewards**. 3. Click **Add Reward Details**. 4. In the **Referral Rewards** page: 1. Replace the default icon with an image that represents your experience. 2. Enter a reward name to display on the banner. 3. Select a start and end date for when you want to show the banner to players. 4. (Optional) Enter a description with details about the reward. 5. (Optional) If you want to add reward restrictions, enter a reward limit. For example, a limit of up to 3 rewards per inviter. 5. Click **Save** to save your changes. 6. Click **Publish** to make the banner visible to players. You can create many different banners, but only one banner can be published and shown at a time. Make sure that the published banner accurately describes the referral system you have implemented in Studio. ## Prevent referral system abuse You can implement safeguards to prevent players from exploiting the friend referral system. - Offer one-time rewards to track invitees and make sure they're only rewarded once. - Introduce a cooldown period before an inviter can submit another referral. - Monitor unusual activity and implement corrective measures like banning users or canceling rewards. ```lua local DataStoreService = game:GetService("DataStoreService") local Players = game:GetService("Players") -- Create or get the datastore for referrals local referralDataStore = DataStoreService:GetDataStore("ReferralDataStore") -- Function to check and mark referral local function onPlayerAdded(player) local joinData = player:GetJoinData() local referredByPlayerId = joinData.ReferredByPlayerId -- Load player's referral data local success, alreadyReferred = pcall(function() return referralDataStore:GetAsync(tostring(player.UserId)) end) if not success then warn("Failed to get referral data for player:", player.UserId) return end if referredByPlayerId and referredByPlayerId ~= 0 and not alreadyReferred then -- Reward inviter and invitee rewardReferrer(referredByPlayerId) rewardInvitee(player) -- Mark the player as referred in DataStore local saveSuccess, err = pcall(function() referralDataStore:SetAsync(tostring(player.UserId), true) end) if not saveSuccess then warn("Failed to save referral status for player:", player.UserId, err) end end end -- Connect the function to the player joining Players.PlayerAdded:Connect(onPlayerAdded) ``` ## Frequently asked questions **Which experiences are eligible to use this feature?** This feature is open to any experience that has been live for at least one day and that doesn't violate the [Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards). **Which players can take advantage of referrals?** All players are eligible to earn rewards through referrals. **Is the invite restricted to a player's friends on Roblox?** No, players can send an invite to friends they aren't connected with on the Roblox platform yet. **Does the referral link expire?** The link never expires. **How can I get the most out of this feature?** You can add an in-experience button to advertise the referral system to encourage players to take advantage of referrals. You can then give the button a descriptive title and connect the button click event to the friend invite modal where players can see the reward banner. --- title: "Regional Content Availability" url: /docs/en-us/production/promotion/regional-content-availability last_updated: 2026-06-29T19:34:06Z description: "Explains why experiences or assets may not be available in every country." --- # Regional Content Availability ## Our approach to global compliance As a global platform, we are committed to respecting the local requirements of the countries in which we operate. This commitment is a core part of our long-term strategy to maintain a presence in nearly every country, allowing creators to reach the widest possible audience. ## Why is my content unavailable in certain regions? Content availability can change based on regional requirements. At times, we may receive requests to restrict certain types of content in a specific country, as well as requests to restrict a particular experience or avatar item in a specific country. ## What does this mean for my content? When content is restricted in a specific country, the following changes occur: - **Experiences**: Impacted experiences will be neither discoverable nor playable by users in that country. - **Avatar items**: Impacted avatar items will be undiscoverable and taken off-sale for users in that country. Your content’s status in other countries remains unchanged, and your global engagement and analytics in other regions will continue as usual. Our approach to content availability is not static; it is subject to the ongoing evolution of requirements worldwide. Content that was once restricted may become available, and new requirements may be introduced. --- title: "Ads Manager reporting and billing" url: /docs/en-us/production/promotion/reporting-and-billing last_updated: 2026-06-29T19:34:06Z description: "The reporting and billing tools in the Ads Manager help you effectively manage your ad campaigns." --- # Ads Manager reporting and billing > **Info:** This feature is in beta. Use the reporting and billing tools in the Ads Manager to effectively manage your ad campaigns. ## Reporting The ads reporting tool provides the information necessary to manage ad campaigns at the campaign, the ad set, and the individual ad level. This allows you to evaluate campaign performance, optimize strategies, and reach your campaign objectives. Ads Manager provides accurate and transparent reporting through the use of click-based attribution for conversion tracking. - [Ad campaign reports](#ad-campaign-reports) offer insights into overall campaign effectiveness and enable you to compare and optimize various campaigns you have running at the same time. The campaign report level provides a holistic view of performance, guiding budget allocation and strategy adjustments. - [Ad set reports](#ad-set-reports) highlight targeting and budget effectiveness and enable you to make adjustments to improve audience engagement. The ad set report level provides information on how to optimize ads for specific audiences. - [Individual ad reports](#individual-ad-reports) assess creative impact and help guide design improvements. ### Ad campaign reports | **Reporting column** | **Definition** | | --- | --- | | Campaign | The name of the ad campaign. | | Off/on | A toggle indicating if the ad campaign is active, paused, or disabled. | | Status | The status of the ad campaign. | | Spent | The total amount of USD or ad credit you have spent on the selected campaign. | | Impressions | **Image impression**: the user looked at the ad for at least 1 second, the ad occupied 1.5% of the viewport, and was viewed at an angle of up to 55 degrees with at least 50% of the image ad pixels visible.

**Video impression**: the user looked at the ad for at least 0.5 seconds, the ad occupied 1.5% of the viewport, and was viewed at an angle of up to 55 degrees with at least 50% of the video ad pixels visible. | | Cost-per-mille (CPM) | The cost of 1,000 impressions of your ad. | | Clicks | The number of times users clicked on your ad on the Home and Search pages. | | Click-through rate (CTR) | The percentage calculated by dividing the number of clicks by the number of impressions and multiplying by 100. | | Cost-per-play (CPP) | The average cost incurred for each play, calculated by dividing the total campaign cost by the number of plays. | | Plays | The number of times users entered your experience. | | Play rate | The percentage calculated by dividing the number of plays by the number of impressions. | | Budget | The amount of money allocated for this ad campaign, on a daily or lifetime timeframe. | | Objective | The campaign objective between awareness or visits. | | Schedule | The campaign run time. | | Payment method | The method used to pay for your advertising campaign, categorized as either card or ad credit. | | 7D playtime | The total estimated playtime over 7 days from the latest conversion, for players who converted through an ad within the selected date range. | | 30D Robux earnings | The estimated total Robux earnings over 30 days from the latest conversion, for players who converted through an ad in the selected date range. Excludes subscriptions, engagement payouts, and immersive ads. | ### Ad set reports | **Reporting column** | **Definition** | | --- | --- | | Name | The name of the ad set. | | Off/on | A toggle indicating if the ad campaign is active, paused, or disabled. | | Status | The status of the ad set. | | Campaign | The campaign the ad set belongs to. | | Max bid | The maximum bid amount set for ad auction, shown in either USD or ad credit. | | Spent | The total amount of USD or ad credit you have spent. | | Impressions | **Image impression**: the user looked at the ad for at least 1 second, the ad occupied 1.5% of the viewport, and was viewed at an angle of up to 55 degrees with at least 50% of the image ad pixels visible.

**Video impression**: the user looked at the ad for at least 0.5 seconds, the ad occupied 1.5% of the viewport, and was viewed at an angle of up to 55 degrees with at least 50% of the video ad pixels visible. | | Cost-per-mille (CPM) | The cost of 1,000 impressions of your ad. | | Clicks | The number of times users clicked on your ad on the Home and Search pages. | | Cost-per-click (CPC) | The cost for each click on your ad on the Home and Search pages. | | Click-through rate (CTR) | Percentage calculated by dividing the number of clicks by the number of impressions and multiplying by 100. | | Plays | The number of times users entered your experience. | | Play rate | Percentage calculated by dividing the number of plays by the number of impressions. | | Cost-per-play (CPP) | The average cost incurred for each play, calculated by dividing the total campaign cost by the number of plays. | ### Individual ad reports | **Reporting column** | **Definition** | | --- | --- | | Name | The name of the ad. | | Off/On | A toggle indicating if the ad campaign is active, paused, or disabled. | | Status | The status of the individual ad. | | Ad set | The ad set the individual ad belongs to. | | Ad format | Whether the ad is an image or a portal ad. | | Spent | The total amount of USD or ad credit you have spent. | | Impressions | **Image impression**: the user looked at the ad for at least 1 second, the ad occupied 1.5% of the viewport, and was viewed at an angle of up to 55 degrees with at least 50% of the image ad pixels visible.

**Video impression**: the user looked at the ad for at least 0.5 seconds, the ad occupied 1.5% of the viewport, and was viewed at an angle of up to 55 degrees with at least 50% of the video ad pixels visible. | | Cost-per-mille (CPM) | The cost of 1,000 impressions of your ad. | | Clicks | The number of times users clicked on your ad on the Home and Search pages. | | Plays | The number of times users entered your experience. | | Play rate | Percentage calculated by dividing the number of plays by the number of impressions. | | Cost-per-play (CPP) | The average cost incurred for each play, calculated by dividing the total campaign cost by the number of plays. | ### Delivery columns The delivery column of the Ads Manager defines the **status** of your campaign, ad set, or individual ad. - The [ad campaign status](#ad-campaign-status) offers a valuable overview of the campaign's overall progress, helping you identify and optimize active, underperforming, or completed campaigns for better results. - The [ad set status](#ad-set-status) highlights the performance of each ad set in terms of targeting and budget allocation, enabling you to fine-tune your strategies for improved audience engagement and to reach your campaign objectives. - The [individual ad status](#individual-ad-status) provides insights into the delivery status of each individual ad, allowing you to monitor their effectiveness and note any dependencies on their parent ad sets or campaigns. #### Ad campaign status | **Delivery status** | **Definition** | | --- | --- | | Active | The campaign is running normally, or your campaign contains at least one ad running normally. | | Paused | The campaign is not delivering because it's toggled off. | | Scheduled | The campaign start date is set in the future and will turn on at the scheduled time. | | Inactive | There are no delivering ad sets within the campaign. This could mean that no ad sets have been created within the campaign, all ad sets or ads are paused, or a combination of the two. Looking at the **Ad Sets** or **Ads** tab will help determine why they are not delivering. | | Error | Technical error. Contact [support](https://www.roblox.com/support). | | Completed | The campaign is no longer running because the scheduled end date has passed. | #### Ad set status | **Delivery status** | **Definition** | | --- | --- | | Active | The ad set is running normally, or your ad set contains at least one ad running normally. | | Paused | The ad set is not delivering because it's toggled off. | | Scheduled | The ad set start date is set in the future and will turn on at the scheduled time. | | Inactive | There are no delivering ads within the ad set. This could mean that no ads have been created within the ad set, ads are paused, or have been rejected. Looking at the **Ads** tab will help determine why they are not delivering. | | Error | Technical error. Contact [support](https://www.roblox.com/support). | | Completed | The ad set is no longer running because the scheduled end date has passed. | #### Individual ad status | **Delivery status** | **Definition** | | --- | --- | | Active | The ad is running normally, or your campaign or ad set contain at least one individual ad running normally. | | Rejected | The ad can't run because it doesn't comply with the [Roblox advertising standards](/docs/en-us/production/promotion/comply-with-advertising-standards.md). | | In review | The ad is in review to make sure it complies with the advertising standards. Following this review, your ad will become active and begin running as scheduled. | | Paused | The individual ad is not delivering because it's toggled off, or the individual ad is toggled on but the parent campaign or ad set isn't active. | | Error | Technical error. Contact [support](https://www.roblox.com/support). | | Completed | The individual ad is no longer running because the scheduled end date has passed. | ## Billing The ads billing tool charges you for ad placements on Roblox using the payment method you have on file. To view billing information, go to **Billing Center** ⟩ **Payment Activity**. ### Credit and debit cards > **Warning:** For first-time credit card users, $5 USD will be charged upon campaign submission and used toward your first bill. Any unused balance will be refunded. You can get charged at two different points when you use a credit or debit card: - When you reach your payment threshold - On the first of the month if the payment threshold isn't reached The ad spend daily limit of credit and debit cards is $300 USD. The payment threshold for Roblox advertisers is $100 USD. Whenever an advertiser account accrues a balance of $100 through advertising on Roblox, the payment method on file is charged. If the payment threshold is not reached by the end of the month, the remaining outstanding balance is paid instead. For example, if your account reaches the $100 payment threshold on May 7th due to their campaign parameters, your payment method gets charged $100 on May 7th. If you accrue less than $100, your payment method gets charged the amount at the monthly billing cycle's close. ### Ad credits Campaigns using ad credit are settled at the end of the campaign at the end date. Any unused ad credits are refunded to their ad credit balance within 24-48 hours after the campaign ends. Ad credits are never refunded to Robux. There is no daily spend limit when using ad credits. For example, if you set a 100 ad credit campaign to run from 8/1 to 8/7, 100 ad credits are debited from your account. But if the campaign has only spent 75 ad credits by the end, the remaining 25 credits are refunded in ad credits. The following are ad credit-related activities: - **Purchased Ad Credit:** Transactions related to converting Robux into ad credit. This transparent categorization makes sure that you can easily track your ad credit acquisition. - **Funded:** Ad credit allocated towards a campaign. Tracking this allocation allows you to monitor your campaign budget and spending. - **Unused:** Ad credit allocated to a specific campaign but not used, which is returned on the campaign end date. This feature makes sure that your ad credits are used efficiently and returned when appropriate. - **Ad Fraud Refund:** Any refunds applied back to your account. Refunds credited for a specific campaign remain locked to that campaign, not credited at the account level. ## Ad fraud protection Roblox uses advanced detection methods for bots and other fraudulent activities to make sure that you don't pay for fraudulent traffic, and regularly adjusts billing and reporting to safeguard their interests. After 14 days from the campaign end date, campaigns are analyzed for fraudulent traffic, and transaction amounts are refunded accordingly. If invalid traffic is detected, refunds are applied 16 days after the campaign end date. This proactive approach helps maintain the integrity of your campaigns and ensures fair spending. Accounts responsible for ad fraud have their accounts suspended to prevent ongoing abuse. In addition, Roblox might suspend ad campaigns or advertiser accounts for non-payment of services properly rendered. Roblox expects you to act with integrity and follow the [advertising standards](/docs/en-us/production/promotion/comply-with-advertising-standards.md).
--- title: "Rewarded video ads" url: /docs/en-us/production/promotion/rewarded-video-ads last_updated: 2026-06-29T19:34:06Z description: "Immersive ads allow you insert ad units into your experience that programmatically serve ad content." --- # Rewarded video ads With rewarded video ads, you can implement a reward mechanism inside your experience to incentivize users to watch click-to-play video ads. A rewarded video is a full-screen ad that lasts anywhere from 6 to 30 seconds and that rewards the user with an item after they have finished watching it. Rewarded video ads let you: - Monetize a greater number of your users. - Increase your ad revenue per user and bring in demand from hundreds of brands. - Improve user playtime and deliver greater engagement from your experience. Before implementing rewarded video ads, consider where inside your experience you want to place the ads, and which items you want to reward users with. Keep the following in mind: - **Take advantage of high-traffic areas**: Your earnings from rewarded videos depend on the number of users in your experience who engage with ads. Place rewarded videos in lobbies or menus as a way to drive high traffic, as these are areas where users are most likely to engage when starting their gameplay. - **Use natural gameplay breaks**: To avoid gameplay disruption, implement rewarded videos at natural gameplay breaks, like when users completes a level, before they start a challenge, or when they run out of lives or resources. - **Boost discovery with prompts**: To make sure that users can find the rewarded video ad in your experience, use easy access reward prompts that are easily discoverable and that only require 1-2 clicks. Have a clear call-to-action (for example, "Watch this ad for 2x the coins!") to make the action obvious to users. - **Offer meaningful rewards**: We recommend rewards that are equivalent to 3 to 10 Robux. Use items like boosters, in-game currency, extra lives, and temporary privileges. - **Ensure compliance**: Clearly disclose to users that they are engaging with an ad, and explain what users have to do to receive the reward and what the reward is. Rewards for video ads **cannot** be randomized items. ## Implement rewarded video ads > **Warning:** Rewards must be **developer products**. You can't reward users with Robux. > **Warning:** Video ads shouldn't negatively impact the user's character during gameplay. To make sure their character isn't harmed, you can pause damage while an ad plays or only show ads in safe zones like the experience lobby. To implement rewarded video ads: 1. Make sure you meet the [eligibility requirements](#eligibility-requirements), including being 13+ years of age, having an ID-verified Roblox account, and have a public experience with at least 2 thousand unique visitors per month. 2. [Enable rewarded video ads in Creator Hub](#video-ad-setup). 3. Choose the item you want to reward users with for watching the ad. You can select an existing developer product or create a brand new one. 4. Create [client-side](#client-side-implementation) and [server-side](#server-side-implementation) scripts. The client-side script checks if a video ad is available to be played to the user, while the server-side script turns a developer product into a reward, shows the user the video ad, and grants the user their reward. After implementation, you can use [analytics](#analytics) to understand key metrics and optimize your rewarded video ad earnings. > **Info:** Earnings from rewarded video ads come from impressions. Your total earnings are calculated by multiplying EPM (earnings per 1000 impressions) by the total number of impressions. ### Video ad setup To set up a rewarded video ad inside your experience: 1. Go to [Creations](https://create.roblox.com/dashboard/creations) and select an experience. 2. Go to **Monetization** ⟩ **Ads** ⟩ **Settings**. 3. Under **Rewarded Video** ⟩ **Serving**, turn on the **Serving enabled** toggle. 4. Select the reward you want to grant the user. If the reward doesn't already exist, [create a new developer product in the Creator Hub](/docs/en-us/production/monetization/developer-products.md#create-a-developer-product). This developer product must have been created for the specific universe the place is in. 5. Insert a button that the user must press before the video ad starts playing. ### Client-side implementation To implement the rewarded video ad on the client-side: 1. Add a new `Class.LocalScript` to the button you just inserted into your experience. 2. Use the `Class.AdService.GetAdAvailabilityNowAsync|GetAdAvailabilityNowAsync` method to make sure the button is only visible to the user if an ad is available. ```lua -- Services local AdService = game:GetService("AdService") local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") -- StarterGui ⟩ ScreenGui ⟩ ShopFrame ⟩ RewardedAdButton local PlayerGui = Players.LocalPlayer:WaitForChild("PlayerGui") local ScreenGui = PlayerGui:WaitForChild("ScreenGui") local ShopFrame = ScreenGui:WaitForChild("ShopFrame") local RewardedAdButton = ShopFrame:WaitForChild("RewardedAdButton") -- StarterGui ⟩ ScreenGui ⟩ OpenShopButton local OpenShopButton = ScreenGui:WaitForChild("OpenShopButton") -- Event to communicate between clients & server local RewardedAdEvent = ReplicatedStorage:WaitForChild("RewardedAdEvent") local INELIGIBLE_RESULTS = { Enum.AdAvailabilityResult.PlayerIneligible, Enum.AdAvailabilityResult.DeviceIneligible, Enum.AdAvailabilityResult.PublisherIneligible, Enum.AdAvailabilityResult.ExperienceIneligible, } local function isIneligible(result: Enum.AdAvailabilityResult) for _, inEligibleResult in ipairs(INELIGIBLE_RESULTS) do if result == inEligibleResult then return true end end return false end function checkForAds() local isSuccess, result = pcall(function() return AdService:GetAdAvailabilityNowAsync(Enum.AdFormat.RewardedVideo) end) if isSuccess and result.AdAvailabilityResult == Enum.AdAvailabilityResult.IsAvailable then RewardedAdButton.Visible = true return end if isIneligible(result.AdAvailabilityResult) then return end end RewardedAdEvent.OnClientEvent:Connect(function(isSuccess : boolean, result : Enum.ShowAdResult) if result == Enum.ShowAdResult.ShowCompleted then checkForAds() end end) OpenShopButton.MouseButton1Click:Connect(function() if ShopFrame.Visible then ShopFrame.Visible = false return end ShopFrame.Visible = true checkForAds() end) RewardedAdButton.MouseButton1Click:Connect(function() RewardedAdButton.Visible = false RewardedAdEvent:FireServer() end) ``` ### Server-side implementation To implement the rewarded video ad on the server-side: 1. Create a new `Class.Script` under **ServerScriptService**. 2. Use the `Class.AdService.CreateAdRewardFromDevProductId|CreateAdRewardFromDevProductId` method to pass the developer product ID of the reward and create an `AdReward` object. 3. Use the `Class.AdService.ShowRewardedVideoAdAsync|ShowRewardedVideoAdAsync` method to play the video ad to the user. - (Optional) Add a `placementId` to track individual [video ad placements](#placements) inside your experience. 4. Use the `Class.MarketplaceService.ProcessReceipt|ProcessReceipt` method to grant the user their reward if they have watched the entire video ad. ```lua -- Services local AdService = game:GetService("AdService") local MarketplaceService = game:GetService("MarketplaceService") local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") local RewardedAdEvent = ReplicatedStorage:WaitForChild("RewardedAdEvent") -- Provide a developer product ID for the video ad reward -- This developer product must be created for the specific universe that this place is in local DEV_PRODUCT_ID = 1919753834 RewardedAdEvent.OnServerEvent:Connect(function(player) local isSuccess, result = pcall(function() local reward = AdService:CreateAdRewardFromDevProductId(DEV_PRODUCT_ID) return AdService:ShowRewardedVideoAdAsync(player, reward) end) RewardedAdEvent:FireClient(player, isSuccess, result) end) MarketplaceService.ProcessReceipt = function(receiptInfo) local player = Players:GetPlayerByUserId(receiptInfo.PlayerId) if not player then return Enum.ProductPurchaseDecision.NotProcessedYet end if receiptInfo.ProductId == DEV_PRODUCT_ID then -- Include the logic for granting rewards here return Enum.ProductPurchaseDecision.PurchaseGranted end return Enum.ProductPurchaseDecision.NotProcessedYet end ``` ## Placements Use placements to track the performance of individual rewarded video ads inside your experience. To create a rewarded video ad placement: 1. In the Creator Dashboard, go to **Creations** and select an experience. 2. Go to **Monetization** ⟩ **Ads** ⟩ **Placements**. 3. Click **Create placement**. 4. Enter a name for the placement and click **Create**. The new placement populates on the Placement table with a unique **Placement ID**. You can use this Placement ID in the `Class.AdService.ShowRewardedVideoAsync|ShowRewardedVideoAsync` method to differentiate between and track metrics for the individual video ads inside your experience. ## Ad opportunities Use `Class.AdService.RegisterAdOpportunityAsync|RegisterAdOpportunityAsync` to track how many times a user has the opportunity to watch a rewarded video ad, and the rate at which they actually watch the video ad. You can provide a Placement ID as an optional parameter to track metrics for individual ad opportunities in your experience. ```lua local AdService = game:GetService("AdService") local playerGui = game:GetService("Players").LocalPlayer:WaitForChild("PlayerGui") -- Create the ScreenGui and the Button local screenGui = Instance.new("ScreenGui", playerGui) local adButton = Instance.new("TextButton", screenGui) -- Register the button with AdService local success, err = pcall(function() -- The second parameter is an optional Placement ID (Number) parameter generated in the Creator Hub. -- If you choose not to provide a Placement ID, the ad opportunity is tracked under the default placement. AdService:RegisterAdOpportunityAsync(adButton, 1234567891234567) end) if not success then warn("Failed to register ad opportunity:", err) end ``` ## Exclude likely spenders Based on past behavior and purchasing habits, certain users are more likely to make purchases in your experience. To reduce your risk of losing revenue, you can exclude users who are likely to spend in your experience from seeing rewarded video ads. We recommend that you only enable this setting if you plan to let users choose between watching a video ad and purchasing the reward from that ad. If the reward you're offering through the video ad is unique and isn't available for purchase, we recommend that you do not enable this setting. To prevent likely spenders from seeing rewarded video ads: 1. In the Creator Dashboard, go to **Creations** and select an experience. 2. Go to **Monetization** ⟩ **Ads** ⟩ **Settings**. 3. Turn on the **Exclude your most likely purchasers from Rewarded Video Ads** toggle. ## Estimate potential earnings Use the calculator to estimate your earnings before you implement rewarded videos ad in your experience. Potential earnings are calculated by multiplying the number of ads-eligible users who have visited your experience in the last 7 days, the number of ads you might show per user, and your projected EPM (earnings per 1000 impressions). Because the number of daily ad views per user is the main variable you can control when you implement rewarded video ads in your experience, you should design a user experience that naturally encourages users to watch ads. For example, if you want most users to watch at least one ad a day, make your ad placements accessible and easy to find. You can also boost daily views by offering rewards that feel valuable and connect to your core gameplay loop, keeping users engaged and boosting your total ad impressions. To use the calculator: 1. In the Creator Dashboard, go to **Creations** and select an experience. 2. Go to **Monetization** ⟩ **Ads** ⟩ **Eligibility**. 3. Under **Ad format**, select **Rewarded video**. 4. Adjust the **daily ad views per user** slider. to update the **Weekly potential rewarded video ads earning** number and see potential earnings. ## Analytics Use analytics metrics to evaluate the effectiveness of your rewarded video ads, test different reward types to identify the ones that best resonate with your audience, and measure how often users watch entire video ads to claim rewards. To access your rewarded video ad metrics: 1. In the Creator Dashboard, go to **Creations** and select an experience. 2. Go to **Monetization** ⟩ **Ads** ⟩ **Analytics**. 3. Filter by **Rewarded Video** to see all of the available metrics. | Metric | Description | | --- | --- | | **Earnings (Robux)** | Total revenue amount in Robux earned from rewarded video ads in your experience.

Different lines show you this metric for different ad placements inside the experience. | | **Monetization Funnel at Experience level** | This metric shows you:

| | **Fill Rate** | The percentage of requests that had an ad returned as a reponse. | | **EPM (Earnings per Mille)** | Your effective Robux earnings per thousand impressions. Calculated by total earnings / the number of impressions (in thousands) in your experience.

Different lines show you this metric for different ad placements inside the experience. | | **Ad Opportunities** | The number of opportunities users had to watch a video ad. Use this metric to track how many times a user had the chance to watch a video ad and the rate at which they actually took that chance and watched the ad.

Different lines show you this metric for different ad placements inside the experience. | | **Impressions** | The number of rewarded video ads shown in your experience.

Different lines show you this metric for different ad placements inside the experience. | | **Rewarded** | The number of rewards granted for video ad views in your experience.

Different lines show you this metric for different ad placements inside the experience. | | **DUV (Daily Unique Viewers)** | The number of unique users who have viewed one or more video ads in your experience in a day. A view is defined by an impression.

This data is updated with 1 day delay. | | **AEPDUV (Average Earning Per Daily Unique Viewer)** | The earnings generated per daily unique viewer for rewarded video ads.

This data is updated with 1 day delay. | | **Eligible Daily Active User** | The number of unique daily users to your experience who were eligible to view rewarded video ads.

This data is updated with 1 day delay. | | **DUV/eDAU** | The percentage of ads-eligible users who visited your experience and viewed one or more video ads in a day. | | **Opt-In Rate** | The percentage of ads-eligible users who are engaging with ads in your game. | | **Frequency** | The number of ads a user is seeing per day in your game. | ## De-risk your implementation with experiments If you're unsure how your community will react to a new rewarded video ad placement, you can use [experiments](/docs/en-us/production/experiments.md) to roll the placement out to a percentage of your users first. This lets you measure real-time changes in engagement and retention before rolling the placement out to your entire audience. ## Eligibility requirements To prevent abuse of the rewarded ad system and to provide the best user experience possible, you must follow the eligibility requirements when you implement rewarded video ads. | **Experiences** | Experiences must:

| | --- | --- | | **Publishers** | As a publisher, you must:

| | **Rewards** | The reward for watching a video ad should:

The right reward strategy is critical. See [Best practices](#best-practices) for more guidance on choosing the right reward for your video ad. | > **Info:** Resubmitting your **Maturity and Compliance Questionnaire** will temporarily remove your ads eligibility while the questionnaire is under review. To avoid repeated interruptions, only resubmit the questionnaire when you publish an experience update that changes your previous responses. > **Warning:** Any violation of the eligilibity requirements can result in account suspension, removal of content, revocation of ad payouts, or loss of eligibility to earn from ads. ## Best practices To get the most out of your rewarded video ad, make sure to: - Call `Class.AdService.GetAdAvailabilityNowAsync|GetAdAvailabilityNowAsync` as close as possible to the moment you plan to show the ad. For example, if you have a "Watch video ad to get a reward" button in a shop menu, you should only call `GetAdAvailabilityNowAsync` when the user opens the shop menu. This approach improves performance by preventing ads from unnecessarily being held in memory, and benefits CPM (cost-per-thousand impressions) and earnings by optimizing the ad fill rate. - Call `Class.AdService.GetAdAvailabilityNowAsync|GetAdAvailabilityNowAsync` when the user finishes watching the ad to determine if another ad is available for the user to watch. - Use analytics to identify the best placement for video ads to encourage user engagement without disrupting gameplay. - Use ad opportunities to track when you offer a user a chance to watch a video ad for a reward. Keep an eye on the ratio between filled requests, ad opportunities, and ad impressions to figure out if you should make your ads easier to find or if your rewards need to be more compelling. For the best reward strategy, we recommend that your rewards: - Have prominent reward prompts to ensure all eligible users engage with the video. - Be relevant to the gameplay and align with core experience mechanics. For example, extra lives in a battle experience or new customization options in a roleplaying game. - Be scaled so that they remain valuable to users as they advance through the experience. - Motivate the user by offering in-experience progression, collection, customization, or competition. - Be offered at strategic moments, like after the user loses a life or after they complete a difficult level. - Be offered at appropriate intervals to avoid making the reward so powerful that it trivializes in-experience progression. - Be a limited-time or rare reward to increase engagement.
--- title: "Search ads" url: /docs/en-us/production/promotion/search-ads last_updated: 2026-06-29T19:34:06Z description: "Search ads promote experiences in the search results of Roblox's experience catalog." --- # Search ads **Search ads** enable creators and brands to promote their experiences to users searching for specific keywords on Roblox. This form of advertising allows developers to target their ads to users using search, increasing the likelihood of engagement. When creating a search ad, advertisers select up to 10 keywords within an Ad Set relevant to their experiences. Once a successful search ad is applied and launched, your experience displays as a search result if a user submits a search with an exact keyword match. Search results populate in the following ways: _ Search ads display at the top of search results._ _ For queries that directly match an experience name, such as "Driving Empire", the exact match appears as the primary enlarged tile, and search ads display immediately after._ _ Search ads on desktop._ > **Warning:** All Roblox advertisers of experiences must abide by our [Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards). Roblox prevents surfacing experiences identified as clickbait or spam. ## Create search ads Search ads are managed in the [Ads Manager](/docs/en-us/production/promotion/ads-manager.md) as a Visits campaign objective. Search ads also use a [modified bidding and auction](#bidding-and-auction) process to determine pricing and availability of individual keywords. > **Warning:** Unlike other ad types, you can't modify brand suitability and audience targeting for search ads. Search ads reach audiences in all regions, all genders, ages 13 and over, and all eligible devices. To create search ads: 1. Navigate to the [Ads Manager](https://advertise.roblox.com/landing). If it's your first time creating an ad campaign, you must first [create an account](/docs/en-us/production/promotion/ads-manager.md#create-an-ad-account) and [add a payment method](/docs/en-us/production/promotion/ads-manager.md#add-a-payment-method). 2. In **Manage Ads**, click **CREATE**. 3. In the Campaign page: 1. Select your **Payment Method**. 2. Set **Campaign Objective** to **Visits**. 3. Set the **budget type**, **date range**, and **campaign name**. 4. Click **NEXT**. 4. In the Ad Set page: 1. Select **Search Experience**. The audience estimation shows the approximate number of Roblox user accounts your search ad might reach over a month. This number is based on the keywords you choose and the budget and bids you set when creating your ad. 2. In **Keyword(s)**, choose up to 10 keywords for each Ad Set. Keywords populate after typing in the text field. You should group relevant keywords together within each Ad Set for clearer reporting on campaign performance. 3. Click **NEXT**. 5. In the Ad page: 1. Set your **Destination Experience**. This is the experience that users are directed to after clicking on the search ad. 2. Enter a name for this specific ad. 3. Click **NEXT**. 6. In the Review Campaign page, review the final details of your advertisement and click **SUBMIT** to launch your campaign. After launching your campaign, you can monitor the campaign's performance with [ads reporting](#reporting) and [billing](/docs/en-us/production/promotion/ads-manager.md#billing). ## Bidding and auction Ads on Roblox work in a bidding system, where advertisers bid to have their ads shown to users through the available ad units placed in an experience. Search ads differ from other ads in that they also calculate the relevance of the experience to the user's query. **This means that experiences that are more relevant to a search query have a greater chance of displaying as ads**. To maximize user experience and make the most of your ad spend, **you should bid on keywords that are most relevant to your experience**. For instance, if you have a horror experience and you bid on the keyword "obby", you could get some initial traffic from this ad, but the ad system will ultimately optimize for ads that are most relevant for your experience. The following is the [same bidding and auction information](/docs/en-us/production/promotion/ads-manager.md#bidding-and-auction) provided for other advertising formats with the specific differences for search ads bolded: > All eligible ads participate in a second price auction. The ad with the highest bid wins the ad slot, but only charges the price of the second highest price, plus one cent. In principle, this looks like: 1. Advertisers submit their bids for ad space. 2. Roblox calculates an adjusted eCPM value for each Ad, based on the player, ad, **and query information. The adjusted eCPM accounts for the relevance of the Ad to the chosen keyword.**3. The ads are then placed into an auction. 4. The ad with the **highest adjusted eCPM** value wins the auction. 5. The winning ad is then displayed to the player. 6. The advertiser is charged its bid amount of the second highest price, plus one cent ($0.01). 7. The advertiser is charged the bid amount of the **second highest ranked ad, adjusted by how much better the ad scored in the auction relative to the next highest ranked ad**, plus one cent ($0.01). > > As an example: 1. An advertiser wants to display their ad. 2. The advertiser submits a bid of $10. 3. Another advertiser submits a bid of $12. 4. The second advertiser wins the auction and is charged $10.01, the price of the second highest bid, plus one cent. ## Reporting You can review your ad campaign's performance using the Ad Manager's [ads reporting tool](/docs/en-us/production/promotion/ads-manager.md#reporting). At this time, you can't filter reports by keywords, but you can create ad sets with a single keyword to compare how an individual keyword performs for your campaign. Search ad metrics also populate in the [Analytics Dashboard](/docs/en-us/production/analytics/acquisition.md#acquisition-sources) acquisition metrics. At this time, search ad and sponsored ad results are combined as a single Sponsored Ad metric on acquisition reports. --- title: "Share links" url: /docs/en-us/production/promotion/share-links last_updated: 2026-06-29T19:34:06Z description: "How to create and use share links to promote your experiences and track acquisition metrics." --- # Share links Share links are unique and trackable links that generate metrics that allow you to track and grow your off-platform user acquisition. You can also use custom `Class.Player:GetJoinData()|LaunchData` to provide in-experience perks to users that join through your share link. The analytics available for share links include metrics like how many new users have arrived at the experience details page through a share link, and which off-platform channel has brought in the most engaged users. There is no limit to how many share links you can create per experience; creating different links for different channels can help you keep better track of acquisition metrics. For more information on the analytics for share links, see the [Acquisition](/docs/en-us/production/analytics/acquisition.md) page. ## Create a share link > **Warning:** You can only create share links for experiences you own. For group-owned experiences, you must be a member of the group with the **Create and configure share links** permission, and the share link must be created from the group account. To create a share link: 1. In the [Creator Dashboard](https://create.roblox.com/dashboard/creations), go to **Creations** and select the **Share Links** tab. 2. Click **Create link**. 3. Enter a unique campaign name and select the experience you want to create a share link for. 4. **OPTIONAL** To enable custom `Class.Player:GetJoinData()|LaunchData`, toggle on **Use custom LaunchData** and enter a `LaunchData` parameter. 5. Click **Generate link**. The new share link generates instantly and is ready to be shared. --- title: "Social media links" url: /docs/en-us/production/promotion/social-media-links last_updated: 2026-06-29T19:34:06Z description: "Explains how to engage with your audience by promoting your experience on social media." --- # Social media links Social media links connect your audience to your social media, which you can use to market updates, monitor community feedback, and promote your experiences. If you verify your age as at least 13 years old through [facial age estimation](https://about.roblox.com/age-estimation) or [government ID](/docs/en-us/production/publishing/account-verification.md#verify-through-government-id), you can add up to three links to social media sites that display on your experience's main details page. > **Info:** Community owners that verify their age as at least 13 years old can add social media links for their community-owned experiences. ## Visibility Social media links are only visible to users who have verified their age as at least 13 years old, and users can only view social media links from people in their own age group and similar age groups. | **User's age** | **Can view social media links from users** | **Cannot view social media links from users** | | --- | --- | --- | | 13 to 15 years old | 13 to 17 years old | | | 16 to 17 years old | 13 to 20 years old | | | 18 to 20 years old | 16 years old or older | | | 21 years old or older | 18 years old or older | | ## Guidelines Roblox's [Community Standards](https://about.roblox.com/community-standards) only permit you to share social media links on your experience's main details page. You **cannot** share social media links directly within your experience. However, if you want to direct your audience to your social media links on your experience's main details page, refer to the following examples of what text is and isn't allowed: - ✅ "Check out our social media links on our game's page" - ✅ "Follow us on social media for merch. Links on our game's page" - ✅ "Join us in our Community Server. Links on our game's page" - ❌ "Follow for merch on X.com/abc" - ❌ "Join our Discord discord.gg/invitecode" - ❌ "Send bug reports to [abc@xyz.com](/docs/en-us/production/promotion/mailto:abc@xyz.com.md)" ## Configure links There are seven different social media types that you can link on your experience's main details page: Facebook, Twitter, YouTube, Twitch, Discord, Guilded, and a Roblox community. You can only add each type of social media once. To configure a link to social media: 1. Navigate to the [Creator Dashboard](https://create.roblox.com/dashboard/creations). 2. Click on the thumbnail of the experience you want to associate with the user advertisement. The experience's **Overview** page displays. 3. In the left-hand navigation, under **Engagement**, click **Social Links**. 4. In the **Link Type** field, select the social media type you want to link to. 5. In the **URL** and **Title** fields, enter the URL to your social media and the descriptive text you want to display to users to tell them what you want to do. 6. **OPTIONAL** Click the **Add Link** button to add a second or third social link. 7. When ready, click the **Save** button. The social media links immediately display on your experience's main page. --- title: "About GDPR and CCPA" url: /docs/en-us/production/publishing/about-GDPR-and-CCPA last_updated: 2026-06-29T19:34:06Z description: "Explains the General Data Protection Regulation and California Consumer Privacy Act." --- # About GDPR and CCPA > **Warning:** The information below is provided for general informational purposes only and is not legal advice. If you have any questions about this information or your specific situation or rights, please contact an attorney. ## What is GDPR? GDPR stands for the **General Data Protection Regulation**. The GDPR is a law in the European Union that focuses on protecting the personal information of everyone in the European Union and European Economic Area by guaranteeing specific rights to the collection, use, and sharing of their personal information. These rights extend beyond the territorial boundaries of Europe, such that many companies or individuals that collect EU personal information are subject to GDPR. ## What is CCPA? CCPA stands for the **California Consumer Privacy Act**. This law provides rights to consumers who reside in California, USA, including knowing what information is collected about them, requesting a business to delete any personal information about a consumer from that consumer, and not to discriminate against a consumer if they exercise their privacy rights. > **Warning:** **What is personal information?** > > Most people associate the terms "personal information" or "personally identifiable information" (PII) as data like a name, email address, or home address. However, GDPR and CCPA have broader definitions for personal information which can also cover information that does not directly link to a specific individual, such as user IDs or IP addresses. > > As a general rule, developers should **not** collect more personal information than what is supplied by Roblox, for instance the user ID and username for their players. For more information, see our [community rules](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Rules). ## Impact on developers As a developer, here are some ways to honor a player's rights under GDPR and CCPA: - You may receive a message from Roblox regarding a **personal information deletion request**. Roblox takes special care to verify these requests to ensure that they're legitimate, so you should **only comply to requests from Roblox**. If a player contacts you first, please ask them to make the request at https://www.roblox.com/support. - Aside from user ID and username, do **not** store other forms of personal information such as birth dates or personal photos. - If you're **asked by Roblox** to delete personal information about an individual who has exercised their right under GDPR or CCPA, you may need to delete specific data from your experience's [data stores](/docs/en-us/cloud-services/data-stores.md). - If you have already stored other personal information beyond what Roblox provides access to, remove it and update your experience so that it doesn't store that data in the future. --- title: "Accessibility guidelines" url: /docs/en-us/production/publishing/accessibility last_updated: 2026-06-29T19:34:06Z description: "Explains the practice of designing products and services to be usable by people with disabilities." --- # Accessibility guidelines **Accessibility** is the practice of designing products and services to be usable by people with disabilities. Recent stats cite that over 26% of people have some type of disability, so making your Roblox experience accessible can help you reach a wider audience. ## Text size Players may find it difficult to read small text. Compare the following in-experience shop menu with a blur applied, simulating what it might look like to somebody with impaired vision. #### Sighted ![Example of small text on user interface](../../assets/publishing/accessibility/Text-Size-SM.jpg) #### Low Vision ![Example of small text on user interface with blur applied to simulate viewing with impaired vision](../../assets/publishing/accessibility/Text-Size-SM-Blur.jpg) If you increase the size of the smaller font labels, it will be clearer to most players. #### Sighted ![Example of larger text on user interface](../../assets/publishing/accessibility/Text-Size-LG.jpg) #### Low Vision ![Example of larger text on user interface with blur applied to simulate viewing with impaired vision](../../assets/publishing/accessibility/Text-Size-LG-Blur.jpg) ## Color contrast Players might find it difficult to read light text on a light background, or dark text on a dark background. To improve accessibility, it's recommended that you pick text and background colors with sufficient color contrast. ![Example UI elements with high contrast](../../assets/publishing/accessibility/Color-Contrast-High.png)_> **Success:** High contrast_ ![Example UI elements with low contrast](../../assets/publishing/accessibility/Color-Contrast-Low.png)_> **Error:** Low contrast_ ## Color non-reliance Over 5% of people in the world have some form of color blindness. Although it's rare for someone to see **only** in black and white, imagine viewing an experience in grayscale: #### No Color Blindness ![Example user interface shown in full color](../../assets/publishing/accessibility/Color-Normal.jpg) #### Color Blindness ![Example user interface shown in grayscale to simulate color blindness](../../assets/publishing/accessibility/Color-Grayscale.jpg) By modifying the image to use different **symbols** alongside colors, more players can tell the difference in gameplay and in other contexts: #### No Color Blindness ![Example user interface shown in full color](../../assets/publishing/accessibility/Color-Symbols-Normal.jpg) #### Color Blindness ![Example user interface shown in grayscale to simulate color blindness](../../assets/publishing/accessibility/Color-Symbols-Grayscale.jpg) ## Sound non-reliance Sound is an excellent addition for immersive experiences, but hearing-impaired players or anyone who turns their volume off will be confused by in-experience events that are **only** conveyed with sound. Consider the following scene where a ringing phone is signalled only by sound, and then signalled with both sound **and** visual aids. ## Player preferences Various visual settings are available to players from the Roblox and in‑experience **Settings** menus, including [preferred transparency](#preferred-transparency), [preferred text size](#preferred-text-size), and [reduced motion](#reduced-motion). For optimal accessibility, your [user interface](/docs/en-us/ui.md) should accommodate each. ### Preferred transparency The **Background Transparency** setting maps to the `Class.GuiService.PreferredTransparency` property. A value of `1` indicates the player prefers the default background transparency, while a value of `0` indicates the player prefers fully opaque (non‑transparent) background transparency for improved readability and contrast. Multiplying a UI element's `Class.GuiObject.BackgroundTransparency|BackgroundTransparency` with `Class.GuiService.PreferredTransparency|PreferredTransparency` is the recommended way to use this setting; backgrounds will become more opaque as `Class.GuiService.PreferredTransparency|PreferredTransparency` approaches `0`. 1. Using the [tagging](/docs/en-us/studio/properties.md#instance-tags) system, apply a tag of `TransparentBack` to all UI items with `Class.GuiObject.BackgroundTransparency|BackgroundTransparency` such as `Class.TextLabel|TextLabels` or `Class.Frame|Frames`.![Four TextLabel elements with varying BackgroundTransparency levels](../../assets/publishing/accessibility/Preferred-Transparency-Labels.png)_Four `Class.TextLabel` elements with varying `Class.GuiObject.BackgroundTransparency|BackgroundTransparency` levels_ 2. Paste the following code into a `Class.LocalScript` within `Class.StarterPlayerScripts`.```lua local GuiService = game:GetService("GuiService") local CollectionService = game:GetService("CollectionService") local TAG = "TransparentBack" local transparentBackObjects = {} local function onInstanceAdded(object) if object.BackgroundTransparency then local defaultTransparency = object.BackgroundTransparency transparentBackObjects[object] = defaultTransparency object.BackgroundTransparency = defaultTransparency * GuiService.PreferredTransparency end end local function onInstanceRemoved(object) transparentBackObjects[object] = nil end -- Store initial tagged instances for _, object in CollectionService:GetTagged(TAG) do onInstanceAdded(object) end -- Detect when tagged instance is added or removed CollectionService:GetInstanceAddedSignal(TAG):Connect(onInstanceAdded) CollectionService:GetInstanceRemovedSignal(TAG):Connect(onInstanceRemoved) -- When in-experience setting is changed, adjust tagged instances GuiService:GetPropertyChangedSignal("PreferredTransparency"):Connect(function() for object, defaultTransparency in transparentBackObjects do object.BackgroundTransparency = defaultTransparency * GuiService.PreferredTransparency end end) ``` 3. Playtest the experience, open the **Settings** menu, and adjust **Background Transparency**. The tagged UI elements should update between their default `Class.GuiObject.BackgroundTransparency|BackgroundTransparency` and fully opaque. #### Default #### 50% More Opaque #### Fully Opaque ### Preferred text size The **Text Size** setting maps to the `Class.GuiService.PreferredTextSize` property which defaults to `Enum.PreferredTextSize.Medium|Medium`. Players can choose to increase text size through the engine's font rendering pipeline to `Enum.PreferredTextSize.Large|Large`, `Enum.PreferredTextSize.Larger|Larger`, or `Enum.PreferredTextSize.Largest|Largest`. When working with UI elements, note the following behaviors: - Text that is constrained to a minimum and/or maximum size through a `Class.UITextSizeConstraint` will **not** shrink below or expand above the set `Class.UITextSizeConstraint.MinTextSize|MinTextSize`/`Class.UITextSizeConstraint.MaxTextSize|MaxTextSize`, regardless of the player's text size setting. - When `Class.TextLabel.TextScaled|TextScaled` is enabled for a `Class.TextLabel.TextScaled|TextLabel` or `Class.TextButton.TextScaled|TextButton`, the element's text will **not** be scaled by the `Class.GuiService.PreferredTextSize|PreferredTextSize` value. - UI elements with `Class.GuiObject.AutomaticSize|AutomaticSize` enabled will shrink/grow as `Class.GuiService.PreferredTextSize|PreferredTextSize` decreases/increases (element bounds will resize to fit the resized text). - When `Class.TextLabel.TextWrapped|TextWrapped` is enabled for a `Class.TextLabel.TextWrapped|TextLabel` or `Class.TextButton.TextWrapped|TextButton`, the element's text will wrap to additional lines as `Class.GuiService.PreferredTextSize|PreferredTextSize` increases, within limits of the element's absolute size. - The results returned by `Class.TextService:GetTextSize()` and `Class.TextService:GetTextBoundsAsync()` honor changes related to `Class.GuiService.PreferredTextSize|PreferredTextSize`. ### Reduced motion The **Reduce Motion** toggle maps to the `Class.GuiService.ReducedMotionEnabled` property. A value of `true` indicates the player wants motion effects through [UI animations/tweens](/docs/en-us/ui/animation.md) to be reduced or completely removed. A basic approach to removing motion from UI tweens is to set the `Datatype.TweenInfo.Time|Time` parameter of a `Datatype.TweenInfo` to `0` when `Class.GuiService.ReducedMotionEnabled|ReducedMotionEnabled` is `true`, effectively making the UI object snap to its target instantly. ```lua local GuiService = game:GetService("GuiService") local Players = game:GetService("Players") local TweenService = game:GetService("TweenService") local player = Players.LocalPlayer local playerGui = player.PlayerGui local menuUI = playerGui:WaitForChild("MenuUI") -- Use tween time of 0.75 seconds unless reduced motion is enabled local TWEEN_TIME = if GuiService.ReducedMotionEnabled then 0 else 0.75 local tweenInfo = TweenInfo.new(TWEEN_TIME, Enum.EasingStyle.Cubic, Enum.EasingDirection.Out) local tween = TweenService:Create(menuUI.SettingsFrame, tweenInfo, {Position = UDim2.fromScale(0.5, 0.5)}) tween:Play() ``` Alternatively to instant snapping, you can handle `Class.GuiService.ReducedMotionEnabled|ReducedMotionEnabled` with more distinct `true`/`false` logic. For example, the following `Class.LocalScript` uses motion tweening by default, but switches to fade‑in for players who enable reduced motion, resulting in a more elegant transition without positional movement. ```lua local GuiService = game:GetService("GuiService") local Players = game:GetService("Players") local TweenService = game:GetService("TweenService") local player = Players.LocalPlayer local playerGui = player.PlayerGui local menuUI = playerGui:WaitForChild("MenuUI") -- Create a canvas group to tween all children uniformly local canvasGroup = Instance.new("CanvasGroup") canvasGroup.Size = UDim2.fromScale(1, 1) canvasGroup.BackgroundTransparency = 1 canvasGroup.Parent = menuUI -- Add children to canvas group menuUI.SettingsFrame.Parent = canvasGroup local tweenInfo = TweenInfo.new(0.75, Enum.EasingStyle.Cubic, Enum.EasingDirection.Out) local tween -- Use fading tween for reduced motion, or a motion tween otherwise if GuiService.ReducedMotionEnabled then -- Initially set canvas group to fully transparent (invisible) canvasGroup.GroupTransparency = 1 -- Fade in canvas group tween = TweenService:Create(canvasGroup, tweenInfo, {GroupTransparency = 0}) else -- Initially set canvas group off screen canvasGroup.Position = UDim2.fromScale(0, -1) -- Move canvas group on screen tween = TweenService:Create(canvasGroup, tweenInfo, {Position = UDim2.fromScale(0, 0)}) end tween:Play() ``` ## Volume controls Different sounds playing at the same time can be overwhelming, distracting, or difficult to distinguish. Providing users with volume controls for different "groups" of audio such as sound effects, music, and speech lets them customize their experience and focus on what they need to. Consider the following example of a very noisy experience where the user is able to modify music and sound effect volumes separately. --- title: "Account verification" url: /docs/en-us/production/publishing/account-verification last_updated: 2026-06-29T19:34:06Z description: "Explains how to verify your Roblox account." --- # Account verification **Account verification** is the process of connecting your identity on Roblox to your real world identity, either through a government ID or through your phone number. When you verify your account, you can distribute more of each asset type and maximize their discoverability within the [Creator Store](/docs/en-us/production/creator-store.md), distribute audio assets under 10 seconds, monetize your [plugins](/docs/en-us/studio/plugins.md), and access age-restricted Studio features such as [voice chat](/docs/en-us/chat/voice-chat.md). > **Warning:****Phone number verification is not sufficient** to sell priced assets on the Creator Store. To sell priced assets, you need to verify with a [government ID](/docs/en-us/production/publishing/account-verification.md#verify-through-government-id) and create a seller account. For more information, see [Creator Store - Distribute and sell assets](/docs/en-us/production/creator-store.md#distribute-and-sell-assets). ## Verify through government ID In order to verify your account with this method, you must: - Be at least 13 years of age. - Have a government-issued photo ID with your picture on it, such as a driver's license, passport, or residency permit. - Have a mobile device with a camera that can take photos of your face and ID. Roblox uses advanced image processing technology to ensure your documents are legitimate. For more information on the account verification process, see [Age ID Verification FAQs](https://en.help.roblox.com/hc/en-us/articles/4407276151188). To learn how Roblox processes your personal and biometric data, refer to the [Roblox Privacy Policy](https://en.help.roblox.com/hc/en-us/articles/115004630823-Roblox-Privacy-and-Cookie-Policy) and [Roblox Facial Media Capture Notice](https://en.help.roblox.com/hc/en-us/articles/4412863575316-Roblox-Facial-Media-Capture-Privacy-Notice). To verify your account with a government-issued ID: 1. Navigate to **Settings**, either on the [roblox.com](https://www.roblox.com/home) or on the Roblox app. 1. **On web browser:** In the top-right corner, click the gear icon to display a contextual dropdown menu, then select [**Settings**](https://www.roblox.com/my/account#!/info).![Account settings menu on roblox.com](../../assets/publishing/account-verification/Account-Settings.png) 1. **On Roblox app:** In the bottom-right corner, click the **⋯ More** icon and select **Settings**. 2. Select the **Account Info** tab. 3. Underneath your birthday, click **Continue with ID**.![Option to verify age with selfie or ID](../../assets/publishing/account-verification/Verify-Birthday.png) 4. Follow the instructions to complete the ID verification flow.![Option to verify age with selfie or ID](../../assets/publishing/account-verification/Verify-With-ID.png) 1. Allow camera access. 2. Scan your ID document. Roblox detects what type of document you have. 1. If there is a barcode on the back, scan and capture an image of the back of your ID. 3. Capture a photo of yourself. This photo will be used to verify that you are the same person as the one on your photo ID. 5. When verification is complete, your verified birthday displays in **Settings** > **Account info**. Verification can take a few minutes. ## Verify through phone number In order to verify your account with this method, you must be at least 13 years of age and have a phone number that can receive text messages. You can only use your phone number with a **single** Roblox account. To verify your account with your phone number: 1. Navigate to [roblox.com](https://www.roblox.com/home). 2. In the top-right corner, click the gear icon to display a contextual dropdown menu, then select **Settings**.![Account settings menu on roblox.com](../../assets/publishing/account-verification/Account-Settings.png) 3. In the **Account Info** section, click the **Add Phone** button. The **Add Phone** dialog displays. 4. Click the **Country Code** dropdown menu, then select your applicable country code. 5. Fill in the following fields: - **Phone Number** — The phone number for your personal mobile device. - **Verify Account Password** — Your active account's password. 6. Click the **Add Phone** button. The system texts your phone a 6-digit code, and the **Verify Your Phone** dialog displays. 7. Enter the 6-digit verification code, then click the **Continue** button. After a moment, the **Phone Number** field changes to include your phone number and a **Verified** status. 8. **(Optional)** Restart Studio to validate your new age verification status. ## Check verification status by script Within a `Class.Script`, the `Class.Player:IsVerified()` method lets you check the verification status of users accessing your experiences, allowing you to limit access to specific content, ranked queues, or even the experience itself. Because users that verify their accounts connect their Roblox identity to their real-world identity, it's much less likely they will cheat, spam, or otherwise risk being blocked from your experience, making this method especially useful for competitive and ranked experiences. The following script checks the verification status of each player as they join the experience. If they have verified their account, the console prints `true`. ```lua local Players = game:GetService("Players") local function onPlayerAdded(player) print(player:IsVerified()) end for _, player in Players:GetPlayers() do onPlayerAdded(player) end Players.PlayerAdded:Connect(onPlayerAdded) ``` --- title: "Adaptive design guidelines" url: /docs/en-us/production/publishing/adaptive-design last_updated: 2026-06-29T19:34:06Z description: "Guidelines to help you deliver best-in-class experiences that intelligently adapt to any device and input type to reach a wide audience." --- # Adaptive design guidelines Adaptive design ensures your Roblox experiences look and feel great across various screen sizes, orientation, and input types. Moving beyond traditional device categories like mobile, desktop, and console, today's devices have far more dynamic capabilities, allowing for seamless transition between different inputs and displays. Imagine a tablet that supports multiple orientations (landscape and portrait), multiple input types (touch, gamepad, mouse and keyboard), and multiple display sizes (native display, and extending to a large TV). These guidelines will help you deliver best-in-class experiences that intelligently adapt to any device and input type to reach a wide audience. For more details on workflows and APIs to assist with implementing these guidelines, see the [cross‑platform development](/docs/en-us/projects/cross-platform.md) guide. > **Info:** To explore a pre-made experience that showcases the following adaptive design concepts, check out the [Interactivity Showcase](https://www.roblox.com/games/132115384567250/). ## Core principles Every experience is unique, but following a few key principles will help you deliver an intuitive and polished experience to every player, on every device. 1. [Input fluidity](#input-fluidity) — Make sure your UI works with various input types like gamepad, touch, keyboard, and VR controllers. This includes changing your UI layout and providing assistive hints depending on the input type. 2. [Responsive layout](#responsive-layout) — Design UI layouts that automatically adjust to different screen sizes and orientations. 3. [Dynamic sizing](#dynamic-sizing) — Scale UI elements as needed to fit various screen dimensions. 4. [Legibility and visual clarity](#legibility--visual-clarity) — Make all UI components, including text and buttons, easily readable and visually clear. ### Input fluidity Ensure your experience feels natural on various input devices, including gamepads, mouse/keyboard, touch, and VR. - **Design controls and shortcuts for multiple inputs** — Make sure every action in your experience can be triggered using gamepad buttons/D‑pad, keyboard shortcuts, and touch. Controls should be visually and functionally focusable for directional pad or stick navigation. Test each input mode so that core gameplay, menus, and UI are accessible and natural, regardless of device. - **Provide visual feedback by input type** — Update UI labels, prompts, and tooltips to reflect the active input method, for example showing `Press A` on a gamepad, `Pinch` on touch, or `Shift``A` on keyboard. Use icons and language familiar to each platform's players. #### Mouse/Keyboard #### Touch #### Gamepad ### Responsive layout The layout of your experience UI should adapt to different screen sizes and orientation. On a big computer monitor, you have lots of space to show buttons all at once. On a small phone screen, however, all those buttons would look cramped and messy. - **Adapt layouts to screen size and orientation** — Use responsive patterns so your UI rearranges or condenses appropriately. For example, move less‑important buttons into a collapsible menu ("hamburger"/menu button) on small screens, while showing them in full on desktops. Your experience UI should adapt gracefully to both landscape and portrait orientation if it supports them. - **Implement layout components that react to parent size** — Build UI panels and widgets that adjust size or position based on their parent container. For example, use split‑view layouts where side panels collapse on narrow screens but expand on wider ones. #### Medium / Landscape #### Small / Portrait ### Dynamic sizing This is about the **size** of your UI elements, not just their position. Your UI should scale up or down with the screen so everything stays the right size and is always easy to interact with. - **Support content-driven sizing and automatic wrapping** — Implement auto‑sizing behaviors: let text boxes grow or shrink with content, and support automatic line wrapping so labels don't overflow or become unreadable as screen size changes. - **Expose and respect accessibility / user preferences** — Allow your UI to respond to user‑specified text or UI scaling settings, tying those settings directly to UI element sizing so increased [accessibility preferences](/docs/en-us/production/publishing/accessibility.md#player-preferences) always translate to larger, more readable UI. #### Large #### Medium #### Small ### Legibility & visual clarity Can players actually read your text on a small phone screen or a larger TV that is far away from them? Are your buttons easy to see against the background of your 3D world? - **Choose font sizes and styles for small screens** — Always select font sizes that are readable on mobile devices, not just desktop. Test on actual devices, and verify text is legible at the smallest expected size. Avoid excessively decorative or thin fonts. - **Ensure high contrast and clear visual hierarchy** — Use colors for text and backgrounds that provide enough contrast to be readable in varying in‑game lighting conditions. Employ strong visual hierarchy and make key actions and labels stand out through size, color, or placement. ## Final thoughts Adaptivity is about meeting players where they are, whether in their hands, on their desk, or on the big screen. Roblox creators who build with these guidelines deliver meaningful, accessible, and fun cross‑platform experiences. Start adapting today and let your creations thrive! --- title: "Badges" url: /docs/en-us/production/publishing/badges last_updated: 2026-06-29T19:34:06Z description: "Badges are a special award you can gift users when they meet a goal within your experience." --- # Badges A **badge** is a special award you can gift players when they meet a goal within your experience, such as completing a difficult objective or playing for a certain amount of time. As soon as a player receives a badge, it displays within the **Badges** category of their inventory. ![Example badges in a player's inventory](../../assets/publishing/badges/Badges-Inventory.png)_Example badges in a player's inventory_ ## Create badges You can create up to 5 badges for free in a 24‑hour period (GMT) for each experience you own. If you want to create more within the 24‑hour period, each additional badge costs 100 Robux. To create a badge: 1. Navigate to the [Creator Dashboard](https://create.roblox.com/dashboard/creations). 2. Hover over an experience's thumbnail, click the **⋯** button, and select **Create Badge**. 3. On the create page, click the **Upload Image** button and then select/confirm the image you want to use as the badge's icon. When creating an image to use for a badge, consider the following: - Use a template of **512×512 pixels**. - The upload process trims and crops the badge image into a circular icon, so avoid putting important details outside of the circular boundaries.![Good circular trimming](../../assets/publishing/badges/Circular-Trimming-Good.png)_ > **Success:** Main content included_![Bad circular trimming](../../assets/publishing/badges/Circular-Trimming-Bad.png)_ > **Error:** Text content cropped_ 4. Complete the following fields: - **Name** — A title for the badge. - **Description** — A description of what the player can do to earn the badge. - **Badge is Enabled** — Whether or not the badge will be enabled when it is created. Disabled badges are not shown under the **Badges** section of the experience's main page and cannot be earned by players. 5. Click the **Create Badge** button. The new badge displays within the **Engagement** ⟩ **Badges** section on the [Creator Dashboard](https://create.roblox.com/dashboard/creations). If the new badge is enabled, it will also be shown under the **Badges** section of the experience's main page._Badge on experience's main page_ ## Reorder badges When a badge is first created, it is placed at the end of the badge list. You can reorder up to 50 badges at once using the drag‑and‑drop interface. To reorder badges: 1. Navigate to the associated experience. 2. In the side panel under the **Engagement** header, navigate to the **Badges** page. 3. Select **Reorder** to navigate to the reordering page.![Badge reorder button](../../assets/publishing/badges/Reorder-Button.png) 4. On the reordering page, drag and drop badges into their preferred order.![Badge drag-and-drop to reorder](../../assets/publishing/badges/Reordering.png) 5. Select **Save** to save this order. The specified order should now appear on the experience details page. ## Script badges Common badge scripting workflows include [awarding badges](#award-badges), checking if a player has previously [earned](#check-earned-badges) a badge in your experience, and [getting badge information](#get-badge-info). ### Locate badge IDs A badge's ID is its unique identifier. You'll need this ID when implementing workflows such as [awarding the badge](#award-badges) to a player. 1. On the [Creator Dashboard](https://create.roblox.com/dashboard/creations), navigate to the associated experience's **Badges** section under **Engagement**. 2. Hover over a badge's thumbnail, click the **⋯** button, and select **Copy Asset ID** from the context menu. ### Award badges You can award badges to players throughout your experience by calling the `Class.BadgeService:AwardBadgeAsync()` method in a server-side `Class.Script`. `Class.BadgeService:GetBadgeInfoAsync()` returns properties of the badge, including `IsEnabled` which confirms whether or not the badge can be awarded to a player. You can enable or disable a badge from the **Configure Badge** form on the [Creator Dashboard](https://create.roblox.com/dashboard/creations). The following is an example of a safe function for awarding badges to players. ```lua local BadgeService = game:GetService("BadgeService") local function awardBadge(player, badgeId) -- Fetch badge information local success, badgeInfo = pcall(function() return BadgeService:GetBadgeInfoAsync(badgeId) end) if success then -- Confirm that badge can be awarded if badgeInfo.IsEnabled then -- Award badge local awarded, errorMessage = pcall(BadgeService.AwardBadgeAsync, BadgeService, player.UserId, badgeId) if not awarded then warn("Error while awarding badge:", errorMessage) end end else warn("Error while fetching badge info: " .. badgeInfo) end end ``` ### Check earned badges The following script checks when any player enters the experience, then uses the `Class.BadgeService:UserHasBadgeAsync()` method to verify if that player owns the badge with the [matching ID](#locate-badge-ids) set in the variable `BADGE_ID`. You can also verify badge ownership in batches using the `Class.BadgeService:CheckUserBadgesAsync()` method. ```lua local BadgeService = game:GetService("BadgeService") local Players = game:GetService("Players") local BADGE_ID = 00000000 -- Change this to your badge ID local function onPlayerAdded(player) -- Check if the player has the badge local success, hasBadge = pcall(BadgeService.UserHasBadgeAsync, BadgeService, player.UserId, BADGE_ID) -- If there's an error, issue a warning and exit the function if not success then warn("Error while checking if player has badge") return end if hasBadge then -- Handle player's badge ownership as needed end end -- Connect "PlayerAdded" events to the "onPlayerAdded()" function Players.PlayerAdded:Connect(onPlayerAdded) ``` ### Get badge info To get information about a badge, such as its description or icon asset ID, call the `Class.BadgeService:GetBadgeInfoAsync()` method with a [badge ID](#locate-badge-ids). For example: ```lua local BadgeService = game:GetService("BadgeService") local BADGE_ID = 00000000 -- Change this to your badge ID -- Fetch badge information local success, result = pcall(BadgeService.GetBadgeInfoAsync, BadgeService, BADGE_ID) -- Output the information if success then print("Badge:", result.Name) print("Enabled:", result.IsEnabled) print("Description:", result.Description) print("Icon:", "rbxassetid://" .. result.IconImageId) else warn("Error while fetching badge info:", result) end ``` --- title: "Console development guidelines" url: /docs/en-us/production/publishing/console-guidelines last_updated: 2026-06-29T19:34:06Z description: "Explains design requirements to follow for publishing an experience to consoles." --- # Console development guidelines With 200M+ Xbox and PlayStation players, consoles present a major opportunity for you to attract more players. Compared to regular devices, designing for controllers and the 10-foot UI experience of consoles has some special guidelines that you can follow to help your experience succeed on consoles. > **Info:** Experiences designed for consoles always need to provide [content maturity information](/docs/en-us/production/promotion/content-maturity.md) to ensure smooth releases and minimize the risk of being removed from consoles. ## Build for the 10ft experience When on consoles, players are typically sitting 8–10 feet away from the screen. Implement your UI using relative sizes and relative positions to measure everything as percentages of a frame. Incorporate a scale factor to all UI sizes by: - Developing for lower resolutions first. - Using relative positions and `Class.UISizeConstraint` to scale the UI. - Creating designs that take into consideration large screen sizes, based on `Class.GuiService.ViewportDisplaySize`, where `Enum.DisplaySize.Large|Large` represents most TVs or larger. - Implementing `Class.ScrollingFrame` to reduce clutter on screen as the UI scales up. Note that some TVs will not show content fully to the edges of the screen due to historical and technical limitations, so you should put UI elements in TV‑safe areas to ensure important experience elements are always visible. ![An example illustration showing the dimensions of the TV-safe and unsafe zone.](../../assets/publishing/console/TV-Safe-Area.png)_The blue area represents the TV-unsafe zone_ ## Design for controllers Console experiences receive commands from players through input controllers, which require special designs for smooth interactions. Apply the following concepts to simplify the UI design for consoles: - **Accessible navigation control** — Baseline navigation, including the four directions, select, and back, controls how players get around and interact with your content on consoles. Make sure players can reach all UI elements using these basic navigation controls, so they can easily access every interactive part of your experience. - **Default controller navigation** — Roblox provides both directional selection and virtual cursor as out‑of‑the‑box solutions, but if you have specific UI layouts, it's recommended that you think about bespoke controller navigation. - **Minimal controller input complexity** — Unlike tappable or clickable input methods, console players navigate by pressing one button at a time, so an action that takes one move on other devices might take extra moves with a controller. Make sure that key actions in your experience only take a few moves to access. - **No chat window** — Regardless of how you customize the chat system, [disable the chat window](/docs/en-us/chat/chat-window.md#chat-window-configuration) for console experiences. > **Info:** As controllers aren't just for consoles and VR but also are available on devices such as desktop and mobile, minimizing the number of moves needed can enhance the UI for all devices and input types in addition to PlayStation and Xbox. ### Add supplemental control Unlike mobiles and desktops, navigation is always sequential on consoles, so players can't jump between far away elements as quickly as on other devices. To enhance the speed of navigation, consider adding additional buttons and shortcuts for essential in-experience actions. ### Accommodate dynamic button icons As your experience expands to more platforms, be sure to show button icons that match different devices. `Class.UserInputService` has two methods available that can achieve this: - `Class.UserInputService:GetStringForKeyCode()` can convert the US query key codes to console keyboard layouts. It's helpful for showing your custom assets as button icons to match the look and feel of your experience. - `Class.UserInputService:GetImageForKeyCode()` returns Roblox default icons for the requested `Enum.KeyCode` as easy drop-in replacements. | Key Code | Xbox Image | PlayStation Image | String | | --- | --- | --- | --- | | `Enum.KeyCode.ButtonA` | | | `ButtonA` | | `Enum.KeyCode.ButtonX` | | | `ButtonX` | | `Enum.KeyCode.One` | n/a | n/a | `1` | | `Enum.KeyCode.Five` | n/a | n/a | `5` | ## Provide haptic feedback One unique advantage of controllers is the capability of providing haptic feedback by physically vibrating the controller to engage players' sense of touch. You can use `Class.HapticEffect|HapticEffects` to enhance player immersion and provide useful feedback. Examples that can be more immersive with haptic feedback include: - Physically dramatic events, such as explosions or collisions. - Events that require player attention, such as a doorbell ring or a new customer entering the shop. - UI action notifications, such as scrolling to the end of a list or clicking on a disabled button. `Class.HapticEffect|HapticEffects` also allow you to control the position and radius of vibrations for different purposes and to maintain the consistency with which actions trigger feedback. Design haptics carefully and avoid overuse, as players might find constant vibrations unpleasant and annoying. ## Adapt progressive disclosure Progressive disclosure defers advanced or rarely used features to a secondary screen. It's one of the best ways to declutter your UI and make it easy to use. For the console UX, it's common and faster to have the player go in and out of screens rather than fitting everything onto one screen like designing for desktops. ## Provide audio feedback Unlike desktop or mobile interfaces, on which interactions are typically silent or rely on subtle haptic feedback, you can add sound effects to improve console interactions. When players navigate through the UI using a controller, consider incorporating sound effects to confirm selections or signal menu traversal, which can elevate the overall experience. --- title: "Experience descriptions" url: /docs/en-us/production/publishing/descriptions last_updated: 2026-06-29T19:34:06Z description: "Experimental guidelines for writing experience descriptions to improve discovery and search engine performance." --- # Experience descriptions > **Warning:** These guidelines are experimental and only meant for select creators. We do not recommend following these guidelines for all experiences. This document outlines guidelines for creating and managing Roblox experience descriptions. The primary goal is to improve SEO for better discovery through external search engines like Google, Bing, and DuckDuckGo. ## Context Top games have millions of impressions in any given month on Google alone. Depending on the search query, however, copycat games, wikis, videos, and game publications might rank above the official link to your game. There is a large opportunity to translate more of this external search traffic into direct visits to your games' official details pages. ### Key objectives - Enhanced User Clarity: Provide prospective players with a more clear and comprehensive understanding of what the game is, how it is played, and why they should play it before they click to join. - Improved Search Engine Performance: By including more descriptive text and relevant keywords, the external search engine crawlers will be able to more accurately index and categorize the game. This will lead to better visibility in search results and targeted recommendations. ## Content recommendations Your description should contain the following: - **[Hook]** A very short description up to 160 characters of the game with core keywords - **[Recent Updates (Optional)]** A blurb of your game's recent or upcoming updates - **[Main Content]** An expansion of this description focused on the game mechanics - **[Additional Information]** Device support, codes, and FAQ Feel free to use emojis as desired for extra flair. ### The hook The first ~160 characters of your description is the most critical section as it will be the content that will likely populate search snippets and what new users first see. _(Google bolds the keywords that they think are relevant to the query)_ - Use your title, core genre, and the most unique feature. - Include primary search terms/keywords. - Immediately communicate the main objective. - Example: - Less optimized: X is a fun, action-packed time with friends to work together to defeat enemies. - Better: - X is a fast-paced tower defense game to build up your fortress and fend off hordes of enemies. Can you build the ultimate base and survive all 500 levels? - Welcome to X, the #1 tower defense game on Roblox. Build up your defenses to face off against hordes of enemies. Will you have what it takes to endure all 500 levels? This hook will also be what will show on the mobile app until the user expands the description content, so it's very important to make this engaging. ### (Optional) Recent updates Give a brief blurb about any recent updates your game may have had. While good for your returning users, keep in mind that the more content you have in this section, the further this pushes down the main content of your description that will be more useful to new users. Aim to keep this short, only highlighting the main attractions of your updates. #### Examples 🏴‍☠️ [NEW] Quest Expansion! Explore new islands and complete exciting quests 🚨 SEASON 9 is here! Featuring the new DANGER cosmetic set and updated player stats! ### Main content Detail your game's core gameplay and key features in an easily digestible format. Feel free to use bullet points, or write full sentences in paragraph format. - Describe the main gameplay loop - Give the reader a glimpse into what it is they'll be doing in your game - Be sure to outline your game's key selling points that differentiate it from others #### Examples 🐟 Real world fishing simulation 🦈 500+ different fish to catch 🎣 Licensed fishing gear brands like X, Y, Z and more! 🐡 Explore unlimited customization options 🏅 Compete with your friends on the leaderboards 🏆 Show off your best catches at community events **Or, in paragraph format:** Dive into the ultimate fishing experience with the most realistic fishing! Discover over 500 different fish species to catch using licensed fishing gear from top brands like X, Y, and Z. Explore unlimited customization options for your avatar and equipment. Compete with friends on the global leaderboards and show off your best catches at exciting community events. ### Additional information - **Device availability:** Mention platform support (e.g. Desktop, Mobile, and Tablet) if applicable. - **(Optional) Redeem codes:** any you'd like to showcase if applicable - **(Optional) Premium benefits:** if your game has it, you can show it off here. If character count becomes a problem, consider this as one of the first things to cut. - **(Optional) Small FAQ section** below the rest of the description content. - Focus on basic questions here, like "How do I X?" or "What is X?". - **⚠️ Warning:** FAQs can take up a lot of your 1000 character count, so be concise and deliberate with your wording. Avoid too many questions, or just answer commonly asked questions in your description text. - **(Optional) Changelog / Credits:** This should be a link to devforum for your changelog / credits. ### Additional notes - Tags and hashtags are explicitly not recommended. They are unused in our ranking algorithms and serve only to eat up your character count limit. - Please do not enumerate your game's controls in the description. New players should learn the controls through a clear tutorial, and it can eat into the character limit a lot, depending on control complexity. We would instead ask you to clearly outline them in your game itself. - Keep the entire description appropriate for all ages. ## Full examples Here are a couple full examples to help inspire your own descriptions. ### First-person shooter game (FPS) **[Hook]** 🔫 Ragefire is the most intense first-person shooter game on Roblox! Join the battlefield, dominate in Team Deathmatch, and unlock over 50 unique weapons and skins. Can you reach the top of the leaderboards? **[Recent Updates]** 🚨 NEW MAP: Abandoned Warehouse is here! Plus, check out the limited-time Tactical Gear Bundle and new Season 3 Battle Pass rewards! **[Main Content]** Jump into the action with realistic combat and non-stop adrenaline! 🎯 Intense 5v5 Multiplayer: Compete across diverse maps and game modes (Team Deathmatch, Domination, Free-For-All). 🛡️ Deep Loadout Customization: Unlock and customize over 50 unique weapons, attachments, and cosmetic skins. 🏅 Ranked Play: Climb the global leaderboards and earn exclusive rewards as you prove your skill. 🛠️ Regular Content Updates: New maps, weapons, and seasonal events added every month! **[Additional Information]** 🖥️ Play on Desktop, Mobile, and Tablet! ⌨️ Redeem Code: FIRESTORM for 500 Credits! FAQ Q: How many people can play together? A: Ragefire supports up to 16 players on the same server. Invite your friends and play together! Q: How do I earn currency for new weapons? A: Just play the game! Winning and completing daily quests earns you even more currency. ### Soccer sports game **[Hook]** ⚽ Goal Rush is the most action-packed soccer simulator on Roblox! Score epic goals, master the dribble and shoot mechanics, and lead your team to victory in the World Cup Tournament. **[Recent Updates]** 🚨 NEW LEAGUE SEASON 5 is live! Featuring the new "Legendary Striker" cosmetic set and updated player stats! **[Main Content]** Experience realistic soccer gameplay with intuitive controls and deep team customization! 🔥 Fast-Paced 5v5 Matches: Jump into quick, competitive games against other players. 🎨 Customizable Players & Gear: Unlock new jerseys, cleats, and celebrations. Personalize your look! 🏅 Skill-Based Mechanics: Perfect your passing, tackling, and special shot techniques. 🏆 Career Mode: Rise through the ranks, complete challenges, and earn exclusive rewards. OR Experience realistic soccer gameplay with intuitive controls and deep team customization! Jump into quick, competitive 5v5 matches against other players. Unlock new jerseys, cleats, and celebrations to personalize your look. Perfect your passing, tackling, and special shot techniques with our skill-based mechanics. Rise through the ranks in Career Mode, complete challenges, and earn exclusive rewards. **[Additional Information]** 🖥️ Play on Desktop, Mobile, and Tablet! ⌨️ Redeem Code: GOLAZO for 500 Coins! FAQ Q: How do I control my player? A: Use the WASD keys (PC) or the on-screen joystick (Mobile/Tablet) to move. Special actions like shoot and pass are clearly mapped in-game! Q: What kind of progression is there in the game? A: Earn gold by participating in matches to use for cool new cosmetics that come out every update! Or shoot for the top of the leaderboards! --- title: "DMCA guidelines" url: /docs/en-us/production/publishing/dmca-guidelines last_updated: 2026-06-29T19:34:06Z description: "Explains copyright law and the details of DMCA claims." --- # DMCA guidelines > **Warning:** The information below is provided for general informational purposes only and is not legal advice. If you have any questions about this information or your specific situation or rights, please contact an attorney. ## Frequently asked questions **###### What is copyright?** Copyright is a legal protection granted for original works of authorship. Copyright protects many different types of creative works including music, movies, pictures, and books among others. Copyright does not protect facts or ideas but it may protect the specific expression of a fact or idea. To learn more about copyright visit the [U.S. Copyright Office](https://www.copyright.gov/help/faq/faq-general.html). It is important to note that you can still infringe a copyright even if you: - Didn't monetize your experience. - Credited the original creator. - Downloaded the content from the internet. - Thought that your use was a [fair use](https://copyright.gov/fair-use/). - Purchased a physical copy of the content (DVD, game, etc.). - Saw other people include the same content in their experiences. - Explained that you didn't intend to infringe anyone's copyright. **###### What is the difference between copyright and trademark?** Copyright is only one form of intellectual property law. Copyright incentivizes creators to create original works of authorship. Trademarks help avoid consumer confusion about the source of a product or service by protecting brand names, logos, or other symbols that distinguish the goods or services of one party from those of others. **###### What is the DMCA?** The **Digital Millennium Copyright Act** ("DMCA") allows copyright holders or their agents to notify Roblox about material they believe infringes upon their copyrights and ask Roblox to remove it. To comply with the requirements of the DMCA, Roblox must respond expeditiously to valid DMCA notices. Roblox must also adopt and reasonably implement a policy providing for the termination in appropriate circumstances of users who are repeat copyright infringers. - **How can I submit a DMCA claim?** If you are a copyright owner or an agent of a copyright owner and believe that any content on Roblox infringes upon your copyrights, you may submit a notification pursuant to the DMCA by contacting our [Copyright Agent](/docs/en-us/production/publishing/mailto:copyright_agent@roblox.com.md), or Legal, Roblox Corporation, 3150 S. Delaware St. San Mateo, CA 94403. You may also contact us by phone at (888) 858-2569. - **What information must I provide when filing a DMCA claim?** - An electronic or physical signature of the person authorized to act on behalf of the owner of the copyright or other right being infringed. - A description of the copyright-protected work or other intellectual property right that you claim has been infringed. - A description of the material that you claim is infringing and where it is located. - Your address, telephone number, and email address. - A statement by you that you have a good faith belief that the use of those materials is not authorized by the copyright owner, its agent, or the law. - A statement by you that the above information in your notice is accurate and that, under penalty of perjury, you are the copyright or intellectual property owner or authorized to act on the copyright or intellectual property owner's behalf. - **When can I expect a reply to my DMCA claim?** If your notice complies with the DMCA, we will act fast to take down the allegedly infringing content, and in some circumstances we will act to promptly contact you to remedy deficiencies in the notice. - **Will the other party receive my personal information when they receive a claim?** In some circumstances, we will share the name of the corporation or username of the individual who is submitting the claim. **###### What happens if I receive a DMCA takedown notice against something I've posted on Roblox?** If you believe that your content on Roblox has been taken down because of mistake or misidentification (for example, you have authorization to use the removed content, or fair use applies) you may contest the takedown notice by submitting a DMCA counter‑notice. - **How do I know if I received a DMCA takedown notice?** Among other things, Roblox will issue a warning on your account to notify you of the DMCA claim. We will also notify you via email (if an email address is associated with your account) and through Roblox's messaging system to provide you with more details. The message will be sent to the email associated with your Roblox account. If you don't have an email linked to your Roblox account, you will only receive a Roblox message. We highly encourage you to link an email to your Roblox account as a [security precaution](https://en.help.roblox.com/hc/en-us/articles/203313380-Account-Security-Keeping-your-Account-Safe-) and to receive such important emails from us. - **What if a DMCA claim is made against my experience?** Should a valid claim be made against your experience, your experience will be taken down or access to it will be disabled. If your experience was taken down due to a DMCA notice, we generally are not in a position to reopen the experience without the copyright owner's permission, unless you have filed a counter‑notice under the DMCA. Roblox is not in a position to obtain such permission on your behalf. **###### How do I file a DMCA counter‑notice?** When we notify you about DMCA notices, we also provide instructions about how to submit a counter‑notice, which may be sent to our DMCA agent: [copyright_agent@roblox.com](/docs/en-us/production/publishing/mailto:copyright_agent@roblox.com.md). Note that if you are under 13 years of age, a parent or other adult representative will have to submit the counter‑notice on your behalf. - **What will happen after I submit a DMCA counter‑notice?** If we determine that your counter‑notice is valid, we will forward it to the person who submitted the copyright takedown notice. Based on a valid counter‑notice, Roblox will generally restore your content within 10-14 business days, unless we are informed that a lawsuit was filed against you. Be aware that submitting a counter‑notice may have serious consequences. If the person who submitted the copyright takedown notice believes that your counter‑notice is not accurate, or your content is infringing, they may file a lawsuit against you. Also, submitting a knowingly false counter‑notice is illegal and can subject you to liability. **###### How do I locate content that I previously uploaded?** You can identify content you've previously uploaded by navigating to the [Creations](https://create.roblox.com/dashboard/creations?activeTab=Model) page and using the tabs at the top of the screen to navigate through the different asset types uploaded to your account. **###### Where can I find more information about the Roblox DMCA process?** Please view our [Terms of Use](https://en.help.roblox.com/hc/en-us/articles/115004647846-Roblox-Terms-of-Use) under section 6 of the User Terms for information regarding DMCA. **###### What if I think someone has copied original material that I uploaded to Roblox?** If you believe that someone has improperly copied original material that you previously uploaded to Roblox, you may file a complaint using Roblox's User-to-User Copying Complaint Process. A complaint may be filed by emailing [copyright_agent@roblox.com](/docs/en-us/production/publishing/mailto:copyright_agent@roblox.com.md), identifying the copied material, the original work, and any other relevant details. Please view the "Disputes Between Users and Creators or Between Creators" section of our [Terms of Use](https://en.help.roblox.com/hc/en-us/articles/115004647846-Roblox-Terms-of-Use) for more information. --- title: "Genres" url: /docs/en-us/production/publishing/experience-genres last_updated: 2026-06-29T19:34:06Z description: "Genres help users better discover and understand the content of your experience." --- # Genres Your experience's **genre** and optional **subgenre** are displayed on the experience's main page to help users better understand what kind of gameplay to expect. Roblox also uses this information when placing experiences in genre-specific top and trending sorts on the Charts page. ![Experience details showing rating, active users, total visits, developer, genre, subgenre, and server size.](../../assets/publishing/experience-metadata/Genre-Example.png) ## Update genres You can update your experience's genre and optional subgenre in the Creator Dashboard. > **Warning:** You can only change genres once every three months. When creating a new experience, you should only select a genre once you're confident what genre your experience is. To update your genre: 1. Navigate to the [Creator Dashboard](https://create.roblox.com/dashboard/creations). 2. Click on the thumbnail of the experience you want to update the genre for. The experience's overview page displays. 3. In the left-hand navigation, select **Configure** ⟩ **Settings**. 4. Select the genre of your experience. For additional guidance, see [Best Practices](#best-practices). 5. **(Optional)** Select a subgenre if one applies. When you update your genre, the genre immediately updates on your experience's main page, but it may take a few days to reflect in other Discovery systems like genre-specific sorts in Charts. ## Genre accuracy To ensure genres are accurate and relevant for users, Roblox regularly reviews experiences and may update any genre selections that appear inaccurate. If Roblox updates your experience's genre, you will be notified by email and the change will appear on the experience's **Settings** page. When Roblox changes your genre, you can appeal the decision if you think Roblox made a mistake: 1. Confirm that Roblox changed your genre by viewing the change on your experience's **Settings** page within the Creator Dashboard. 2. Navigate to [roblox.com/support](http://roblox.com/support). 3. Confirm that you are logged into your Roblox account with edit permissions for the experience. 4. Select the **Experience Genre** help category, and provide the universe ID for your experience. You can find the universe ID for your experience on Creator Hub. 5. Click **Continue** to submit your appeal. Roblox will review your appeal and notify you of the outcome, usually within a few days. If your appeal is accepted, your genre will reset back to the previous genre before Roblox changed it. If your appeal is rejected, your genre will remain the same, and you won't be able to appeal the decision again. > **Warning:** If your appeal has not yet been reviewed and you update your genre through the regular selection flow, your original appeal will not be reviewed. ## Best practices Selecting a genre helps users discover and understand what to expect from your experience. Consider the following best practices to select the most relevant genre for your experience. - **Primary purpose and core gameplay** — Sometimes there isn't a genre that perfectly describes your experience, or multiple genres can be valid. In these cases, it's recommended to pick the genre that fits best and describes the primary purpose or core gameplay loop of your experience. - **Selecting a subgenre** — While genres give users a broad sense of what kind of gameplay to expect, subgenres help describe the core mechanics of your experience in more detail. It's recommended to select a subgenre if one applies. However, if there isn't a subgenre that fits your experience, you can still select a genre without a subgenre. - **Genre and subgenre descriptions** — Refer to the [genre and subgenre descriptions](#genre-and-subgenre-descriptions) to compare genres and help you make the best choice for your experience. ## Genre and subgenre descriptions Use the following genre descriptions and additional subgenres to best represent your experience's content. Some genres do not include subgenres. ### Action Experiences that emphasize physical challenges and quick reflexes. They normally involve combat or other fast-paced gameplay. ###### Subgenres - **Battlegrounds & Fighting** — Experiences focused on combat between two or more characters. They often feature a variety of different combat mechanics. - **Music & Rhythm** — Experiences that challenge a player's sense of rhythm. They most often require players to press buttons in sequence to the beat of music. - **Open World Action** — Experiences where players can freely explore large worlds with an emphasis on action-oriented gameplay such as fighting and other combat. ### Adventure Experiences focused on elements such as exploration, solving challenges, and/or interacting with characters to progress through a story or to complete a goal. ###### Subgenres - **Exploration** — Experiences where players freely explore worlds. They often involve players uncovering hidden secrets, landmarks, or other unique details at their own pace. - **Scavenger Hunt** — Experiences where the objective is to find and collect a series of objects. - **Story** — Experiences focused on providing players a narrative experience. They often tell a story through a series of levels, puzzles, and challenges. ### Education Experiences focused on learning specific skills or subjects. ### Entertainment Experiences meant to entertain through consumption or creation of content, including audio, visual, or other forms of media. ###### Subgenres - **Music & Audio** — Experiences for listening, discovering, or creating music and audio. - **Showcase & Hub** — Experiences that act as a demo, show off an immersive environment, or highlight and portal to other experiences. - **Video** — Experiences for watching or creating video content. ### Obby & platformer Experiences where players navigate surfaces and obstacles to progress. Player actions often involve jumping, climbing, or changing directions. ###### Subgenres - **Classic Obby** — Experiences where players jump between platforms to progress. - **Runner** — Experiences where players automatically move and must avoid obstacles to continue. - **Tower Obby** — Experiences where players climb upwards through a series of platforms and obstacles. ### Party & casual Experiences focused on casual social play with other players. ###### Subgenres - **Childhood Game** — Experiences recreating classic childhood games like tag or hide-and-seek. - **Coloring & Drawing** — Experiences that focus on coloring or drawing as the primary gameplay mechanic. - **Minigame** — Experiences made up of short round-based games. - **Quiz** — Experiences centered around trivia or quizzes. ### Puzzle Experiences focused on problem-solving challenges to progress. ###### Subgenres - **Escape Room** — Experiences focused on solving puzzles to escape a room or building. - **Match & Merge** — Experiences where players combine items to create new ones. - **Word** — Experiences where players create, guess, or find words. ### RPG Experiences where players embody characters in a fictional world, making choices that affect their journey. Players progress through a system of rules, like stats and abilities. ###### Subgenres - **Action RPG** — RPG experiences focused on real-time combat. - **Open World & Survival RPG** — RPG experiences where players traverse an open world, often challenging the player to survive. - **Turn-based RPG** — RPG experiences involving turn-based combat. ### Roleplay & avatar sim Experiences where players immerse themselves in various roles, often with avatar customization. They emphasize creativity, social interaction, and personal expression. ###### Subgenres - **Animal Sim** — Experiences where players take on the role of an animal in a virtual world. - **Dress Up** — Experiences centered around dressing up avatars. - **Life** — Experiences where players create and live out scenarios that mimic everyday life. - **Morph Roleplay** — Experiences focused on unstructured roleplay where players take on predefined characters that don't resemble their avatars. - **Pet Care** — Experiences where players raise or take care of a pet. ### Shooter Experiences where players shoot ranged weapons to defeat other players or enemy units. ###### Subgenres - **Battle Royale** — Shooter experiences where many players all fight each other. The last player or team standing wins. - **Deathmatch Shooter** — Shooter experiences where the primary objective is eliminating the other players or team. - **PvE Shooter** — Shooter experiences where players primarily battle against computer-controlled enemies. ### Shopping Experiences that support online shopping for users to purchase digital or real-life goods. ###### Subgenres - **Avatar Shopping** — Experiences that help users browse and purchase avatar items. ### Simulation Experiences simulating real-world systems, processes, and activities. The focus is on performing specific activities like managing businesses or operating vehicles. ###### Subgenres - **Idle** — Experiences with little to no player input or interaction. - **Incremental Simulator** — Experiences where progression involves simple repetitive actions to increase a counter. As players progress, they often unlock new capabilities, levels, and characters. - **Physics Sim** — Experiences focused on physics and interactions within the environment to simulate reactions. - **Sandbox** — Experiences providing players with tools and resources to build and customize an environment. - **Tycoon** — Experiences simulating the management of a business or base. They often involve collecting money from "droppers" used to upgrade the base. - **Vehicle Sim** — Experiences centered around driving or operating vehicles. This often includes cars, planes, boats, or other vehicles. ### Social Experiences that primarily serve to connect people through hanging out, communication, or sharing. ### Sports & racing Experiences focused on sports or racing related competition. ###### Subgenres - **Racing** — Experiences featuring a racing competition, where the objective is to achieve the fastest time. - **Sports** — Experiences focused on the practice of real-life sports. ### Strategy Experiences that emphasize the use of skillful thinking or strategic planning. ###### Subgenres - **Board & Card Games** — Experiences inspired by traditional board and card games in real life. They typically involve a combination of luck and/or skillful thinking. - **Tower Defense** — Experiences where players strategically position defensive units along a path to prevent waves of enemies from reaching the end of the path. ### Survival Experiences where the objective is for players to survive, escape, or defeat something that is threatening them. ###### Subgenres - **1 vs All** — Experiences where players have different roles and a win condition. Typically one player is "it" and the others need to escape or defeat them. - **Escape** — Experiences challenging players to make a successful escape in order to survive a threat. ### Utility & other Experiences that don't fit into the other genres, including utility experiences that provide value to users in some way. --- title: "Icons" url: /docs/en-us/production/publishing/experience-icons last_updated: 2026-06-29T19:34:06Z description: "Icons are an important factor in helping users recognize your experience and grow its brand." --- # Icons A high-quality **icon**, in addition to a showcase [thumbnail](/docs/en-us/production/publishing/thumbnails.md), is an important factor in helping users recognize your experience and grow its brand. With prominent placement in various sections of the [Home](https://www.roblox.com/home) page, it's recommended that the icon expresses the theme, tone, and/or genre of the experience. ![Icon expressing an urban action game](../../assets/publishing/experience-metadata/Icon-Action.jpg)_Urban action game_ ![Icon expressing a camping simulation](../../assets/publishing/experience-metadata/Icon-Camping.jpg)_Camping simulation_ ![Icon expressing an ocean theme](../../assets/publishing/experience-metadata/Icon-Ocean.jpg)_Peaceful ocean theme_ When you [publish an experience](/docs/en-us/production/publishing/publish-games-and-places.md) for the first time, an icon is automatically generated from a collection of default images. While you can only display one icon per [locale](/docs/en-us/production/localization.md), you can also include supplementary [image and video thumbnails](/docs/en-us/production/publishing/thumbnails.md) that further promote the experience. For more information on maximizing the impact of your experience's icon, see [best practices](#best-practices). ## Upload icons To ensure your experience's icon always displays in high resolution and with an ideal aspect ratio, use a template of **512×512 pixels**. In some places on the Roblox site and app, icons scale down to smaller sizes like 150×150 pixels, so it's recommended that you preview an icon at smaller sizes to confirm its details remain clear. In addition, see [best practices](#best-practices) for tips on icon format and design. To upload an experience icon: 1. Navigate to the [Creator Dashboard](https://create.roblox.com/dashboard/creations). 2. Click on the experience you want to upload an icon for. The experience's **Overview** page displays. 3. In the left-hand navigation, under **Configure**, select **Places**. 4. Click the **start place** marked with a star icon (if the experience contains only one place, it will be the only option). The place's **Basic Settings** page displays.![Start place tile indicated in Places display on the Creator Dashboard](../../assets/creator-dashboard/Places-Start-Place-No-Icon.png) 5. In the place's left-hand navigation menu, select **Icon**. 6. Set the media type to **Image** and click the **Change** button. Then, from the file browser, select and confirm the image you want to use as the icon. Note that all icon images must pass [moderation](/docs/en-us/projects/assets.md#asset-moderation) before they appear to others on the Roblox platform. 7. If the preview icon appears as you expect, click **Save Changes**. ## Best practices To create the most positive impact for users looking to play your experience, consider the following best practices. ### Quality and aspect ratio When you upload an icon image, it should be **square** and at least **512×512 pixels** so that it always displays in high resolution and at an ideal aspect ratio across the Roblox site and app. ![High quality and sharp icon](../../assets/publishing/experience-metadata/Icon-High-Res.jpg)_> **Success:** High quality and sharp_ ![Low quality and blurry icon example](../../assets/publishing/experience-metadata/Icon-Low-Res.jpg)_> **Error:** Low quality and blurry_ ![Non-square icon](../../assets/publishing/experience-metadata/Icon-Not-Square.jpg)_> **Error:** Not a square image_ ### Relevant content An icon will have a higher impact if it's unique and provides relevant imagery on what users should expect when they join the experience. Icons with ambiguous graphics may lead to unnecessary confusion. ![Icon expressing a fun theme park setting](../../assets/publishing/experience-metadata/Icon-Theme-Park.jpg)_> **Success:** Icon expresses a fun theme park setting_ ![Default Roblox icon which does not match the same theme](../../assets/publishing/experience-metadata/Icon-Default.jpg)_> **Error:** Default icon does not match the same theme_ ![Icon with ambiguous graphic in top-right corner](../../assets/publishing/experience-metadata/Icon-Theme-Park-Symbol.jpg)_> **Error:** Ambiguous graphic in top‑right corner_ ### Color and contrast You can express your experience's theme through color and contrast to help users decide if it's suitable and appealing to them. ![Icon with bright, high-saturation colors for a fantasy or dreamy setting](../../assets/publishing/experience-metadata/Icon-Colorization-A.jpg)_Bright, high-saturation colors for a fantasy or dreamy setting_ ![Icon with muted low-saturation colors for a somber, moody setting](../../assets/publishing/experience-metadata/Icon-Colorization-B.jpg)_Muted low-saturation colors for a somber, moody setting_ ![Icon with heavy color balance and contrast for a horror experience](../../assets/publishing/experience-metadata/Icon-Colorization-C.jpg)_Heavy color balance and contrast for a horror experience_ --- title: "Roblox Kids and Select" url: /docs/en-us/production/publishing/kids-and-select last_updated: 2026-06-29T19:34:06Z description: "Explains the Roblox Kids and Select account types and the publishing requirements for reaching audiences under 16." --- # Roblox Kids and Select > **Info:** This feature is actively rolling out. For the latest timelines, updates, and announcements, see the [DevForum announcement](https://devforum.roblox.com/t/alternate-publishing-requirements-for-roblox-kids-and-select/4630944). Roblox is launching a unified safety and publishing framework to align content with user age. This update introduces two new age-based account types named **Roblox Kids** and **Roblox Select**, sets new verification requirements for creators who create content for all ages, and establishes a new [evaluation process](#for-all-ages-including-kids-and-select-accounts) for any game available to users under 16. Parents and guardians can link accounts and modify their child's access. To learn more, see [parental controls](https://about.roblox.com/parental-controls). ## Account types Roblox accounts are assigned to one of three tiers based on the user's age-checked age: | | **Roblox Kids**
(Age-checked 5-8) | **Roblox Select**
(Age-checked 9-15) | **Roblox**
(Age-checked 16+) | | --- | --- | --- | --- | | **Content ratings** | Minimal or Mild | Minimal, Mild, or Moderate | All rated content, except Restricted (18+) | | **Chat** | Off by default. Can be enabled through a linked parental account. | If available in your region, chat is gradually introduced with safeguards. | If available in your region, chat is on by default. | | **Parental controls** | Full suite of parental controls, including new Approved Experience controls. | Full suite of parental controls through age 12. Certain controls and visibility into kids' accounts available through age 15. | Visibility into friends, screen time, and spending available through age 18. | > **Info:** Age ranges may vary by region. Ages are based on facial age checks and a parent can correct a child's age if necessary. For information on how content maturity labels determine which account types can access your game, see [Content maturity and compliance](/docs/en-us/production/promotion/content-maturity.md). ## Publishing requirements Publishing requirements depend on the target audience. For more information on publishing games, see [Publish games and places](/docs/en-us/production/publishing/publish-games-and-places.md#publish-games). The following table summarizes what creators must satisfy to reach each audience: | **Publishing requirements** | **Roblox Kids**
(Ages 5–8) | **Roblox Select**
(Ages 9–15) | **Roblox**
(Ages 16+ and Trusted Friends) | | --- | --- | --- | --- | | **Age-checked creator account** | ✓ | ✓ | ✓ | | **ID-verified creator account** | ✓ | ✓ | - | | **2-Step Verification on creator account** | ✓ | ✓ | - | | **Fee or subscription to publish or update games** | One-time refundable per-game fee1
**or**
An active, 2+ month old Roblox Plus or Premium subscription | Free, no fee or subscription required | | **Evaluation process2** | Required | Required | Not required | | **Content maturity** | Minimal, Mild | Minimal, Mild, Moderate | All rated content, except Restricted3 | 1 As long as the game is playable and not permanently removed due to Community Standards violations. 2 Evaluation process requires 500 unique plays from highly engaged players in the last 60 days. 3 Restricted content is limited to 18+ users. ### For all ages (including Kids and Select accounts) To reach users who are under 16 years old, creators of individual-owned games or [group](/docs/en-us/projects/groups.md) owners of group-owned games must meet the following requirements for a higher bar of accountability: 1. **Account verification** - Have an account that is in good standing. 2. **ID verification** - Verify your account through [government ID](/docs/en-us/production/publishing/account-verification.md#verify-through-government-id) or linked [parental account](https://about.roblox.com/parental-controls). 3. **Two-factor authentication** - Enable [2FA](https://en.help.roblox.com/hc/en-us/articles/212459863-Add-2-Step-Verification-to-Your-Account) on your Roblox account. 4. **Publishing fee or subscription** - Meet **one** of the following requirements: - Maintain an active [Roblox Plus](/docs/en-us/production/monetization/roblox-plus.md) or [Roblox Premium](https://www.roblox.com/premium/membership) subscription for 2 consecutive months. - Pay a one-time, refundable [fee](/docs/en-us/production/publishing/publish-games-and-places.md#optional-fees). > **Warning:** If your game is permanently moderated or taken down for not meeting Roblox's [Community Standards](https://en.help.roblox.com/hc/articles/203313410), the publishing or expedited review fee **will not be refunded**. 5. **Evaluation** - Your game must pass the following evaluation process: 1. **Trial phase** - The game is first made available only to age-checked users 16 and older. 2. **Analysis of Highly Engaged users** - Engagement is analyzed through metrics like account age, play history, and spend on Roblox to verify it's from real users and not bots. Increasing engagement with age-checked 16+ users will help you progress towards becoming eligible faster. 3. **Safety review** - The game's real-time moderation reports and gameplay are reviewed for age-suitability. 4. **Threshold** - Once the game reaches a benchmark of 500 unique plays by highly engaged age-checked users within a 60-day window, it completes evaluation and becomes eligible for Kids and Select audiences. You can monitor your game's evaluation progress in real time using the **Audience Reach** dashboard in the [Creator Hub](https://create.roblox.com). > **Info:** You only need to meet the subscription requirement or pay the optional fee when you publish a new game or update an existing one for all ages audiences. If your subscription later expires, your game stays published for all ages. ### For ages 16+ and Trusted Friends To reach users who are at least 16 years old, creators of individual-owned games or [group](/docs/en-us/projects/groups.md) owners of group-owned games must meet the following requirements: 1. **Account verification** - Have an account that is in good standing and at least 2 days old. 2. **Age verification** - Verify your age through [facial age estimation](https://about.roblox.com/age-estimation), [phone number](/docs/en-us/production/publishing/account-verification.md#verify-through-phone-number), or [government ID](/docs/en-us/production/publishing/account-verification.md#verify-through-government-id). 3. **Content maturity label and descriptors** - Complete the [Maturity & Compliance Questionnaire](/docs/en-us/production/promotion/content-maturity.md). ## Expedited review If you're planning a timed launch or running an off-platform campaign and need to know exactly when your game will be ready for all ages, you can secure an expedited review for Roblox Kids and Select accounts by paying a one-time, refundable fee of 100,000 Robux per game. The expedited review fee is intended to provide launch flexibility and financial accountability for qualified creators who haven't yet met the highly engaged age-checked player requirement for Kids and Select accounts. While it accelerates safety and compliance reviews, all games must continue to meet the same standards as other Roblox Kids and Select games. For more information on the expedited review fee, such as how to pay it and when you can receive a refund, see [Publish games and places - Expedited review fee](/docs/en-us/production/publishing/publish-games-and-places.md#expedited-review-fee). ## Frequently asked questions **Why is a publishing fee or subscription required to publish to all ages?** Requiring either a one-time publishing fee or a recurring Roblox Plus or Premium subscription creates a meaningful financial cost for bad actors attempting to bypass publishing requirements with spam accounts, while remaining as affordable as possible for legitimate creators. Creators who are not yet ready to pay the fee or subscribe can still reach the 16+ audience. **What if my subscription expires?** Your game remains published and available to all ages. You only need to satisfy the publishing fee or subscription requirement when publishing a new game or updating an existing game for all ages audiences. **How do I track my game's evaluation status?** Use the **Audience Reach** dashboard in the [Creator Hub](https://create.roblox.com) to monitor your game's engagement benchmarks in real time. **What if a parent's favorite game is missing from Roblox Kids?** Parents can use the **Approved Experience** control in parental settings to manually allow access to specific games for their child. This does not apply to Restricted (18+) content. **What exactly is a "highly engaged player"?** Before a game can reach younger audiences, we need to confirm it meets our policies for Roblox Kids and Select accounts. To do this, our Realtime Multimodal Moderation system reviews player engagement, using signals like account age, play history, and platform spend to verify that it is genuine and comes from highly engaged players. Today, a highly engaged player must meet requirements on account tenure, playtime in your game, and platform spend to qualify. Users who meet the criteria for "platform spend" have made a minimum purchase anywhere on Roblox in the last 60 days and have spent time in your game within that same window. They don't need to spend anything in your game specifically. This spend creates a real cost to abuse and makes it much harder for bad actors to generate fake engagement at scale. Highly engaged players play all kinds of games, including ones that are free to play, so your game doesn't need to be monetized for players to count toward your total. We'll keep evolving these criteria over time to capture more genuine players, with safety as the priority. **Will the highly engaged player requirements change over time?** Yes. We took an intentionally conservative approach at launch to make sure our safety systems have enough data to accurately evaluate games. We're working to expand eligibility so more compliant games can qualify in the coming weeks and months. We'll share an update on requirements before the global launch in June. **Will I lose my publishing fee if my game loses eligibility?** No. The refundable publishing fee will be automatically refunded 90 days after your game becomes eligible for Kids or Select accounts. If your game never reaches eligibility, it's still refunded 90 days after your initial payment date. The only exception is if your game is permanently removed from Roblox due to severe Community Standards violations. **What if I need my game to reach Kids and Select users before reaching the "highly engaged player" requirement?** You can pay a one-time, refundable [expedited review](#expedited-review) fee. **What happens to uncopylocked games?** We know many of you use uncopylocked games to share templates and learning resources with other creators. We're looking into options for how these are shared and discovered on Creator Store. We don't have a specific timeline yet, but will have more to share in the coming months. **I paid the publishing fee but should have received a free Plus subscription. Can I get a refund?** Yes. The free Plus subscription rolled out one day later than planned, which caused a small number of eligible creators to pay the publishing fee unnecessarily. We're processing automatic refunds for everyone affected. No action needed on your end. The Robux will be back in your account within a few weeks. **How do these changes impact games available exclusively to playtesters?** "Private" games are now restricted solely to creators with Edit permissions. To share with anyone else, including those with Playtest permissions, you need to meet the new publishing eligibility requirements. Set your Audience to **Limited** > **Playtesters** to share your game with those with Playtest permissions. Playtesters who are Trusted Friends of age-checked game owners can access the game regardless of age, as long as the game's content maturity questionnaire has been completed. Games published to any of the Limited audiences will not be visible publicly in search or on the home page.
--- title: "Create and publish games and places" url: /docs/en-us/production/publishing/publish-games-and-places last_updated: 2026-06-29T19:34:06Z description: "Explains how to create and publish games." --- # Create and publish games and places When you open Studio and create a new game, your project starts with a single **place** that players load into when they first join the game. Games can have one or multiple places that each contain all components for that portion of the game, including its specific environment, 3D objects, and scripts. > **Info:** Places are comparable to scenes in Unity or maps in Unreal Engine. Many creators create additional places within a game to organize assets for different gameplay areas. For example, if you want players to join a dungeon before [teleporting](/docs/en-us/projects/teleport.md) to either a vast desert or spooky island, you can organize the assets for each area into their own place. Every place is represented by a data model that stores and organizes: - All objects that make up the 3D world of a place, such as its parts, meshes, terrain, and lighting. - All objects that can control runtime behavior, such as scripts. Understanding both the general structure of a place's data model, and how the Roblox engine uses it as the source of truth for a place's state is important for understanding where to store and configure different types of objects. For more information, see [data model](/docs/en-us/projects/data-model.md). ## Create games When you create a new game, it opens a start place that all players will load into when they join the game. The start place represents the overall game until you add additional places for different gameplay areas. To create a game: 1. Open Studio. 2. In the **Open a Template** section, select a template with the objects you want to start working with for your game, such as: - **Baseplate** — Starts with a `Class.SpawnLocation` and a baseplate. - **Platformer** — Starts with customizable platforms, coin pickups, and double-jump, dashing, rolling, and long jump character mechanics! - **Racing** — Starts with a working racecar and customizable track objects. When Studio opens up the template, you now have `Place1` that represents your overall game. ## Publish games When you publish a game, Roblox stores the data model of your start place as a `.rblx` file in the cloud. This is an important process because it connects the game to your account, allowing you to access and work on it from any computer. By default, new games are set to **private**, meaning that they are only accessible to you as the creator, to users with **Edit** or **Playtest** access, and to group members with the appropriate [roles and permissions](/docs/en-us/projects/groups.md#roles-and-permissions). When you are ready to go live to everyone, you can [release your game to the public](#make-game-public) and optionally mark it as a beta. > **Info:** When creating and collaborating on games as part of a [group](/docs/en-us/projects/groups.md), the ability for group members to playtest/edit/publish games is, by default, dependent on their role permissions. However, group owners or members with sufficient permissions can adjust access on a per‑game level. For more info, see [configure games](/docs/en-us/projects/configure-games.md). To publish a game: 1. In the top-left corner of your computer, click **File** ⟩ **Publish to Roblox**. 2. In the **Publish Experience** window, fill in the following fields: - **Name** / **Description** — The name of the game and a description that describes what a potential player should expect. See the metadata best practices dropdown at the bottom of this section for guidelines. - **Creator** — The creator or [group](/docs/en-us/projects/groups.md) (**RECOMMENDED**) you'd like to attribute as the creator of the game. - **Devices** — Each applicable device type that you want to support. The default options are practical for most new creators. 3. Click **Create**. ** Metadata best practices** Your game's name and description create an important first impression and contribute to how easily players find your game through Roblox's dynamic [discovery](/docs/en-us/discovery.md) systems. #### Game name All games should adhere to the following best practices for naming: - **Keep the name consistent** – Renaming a game too often reduces the chances that players can find it using a previous name. - **Avoid spamming** – Frequent repetition of words or phrases may result in demotion of your game. > **Success:****Obby Madness** > **Error:****Obby Madness OBBY OBBY OBBY OBBY OBBY** - **Use decorations cautiously** – Decorating the name with one or two well-placed emojis isn't harmful, but misplaced or excessive decorations can confuse players who quickly want to identify the game. > **Success:** 🌍 **Evolution Simulator** 🌍 > **Error:** 🐒🐬🐊 **Ev**🌍**luti**🌍**n Simulator** 🌷🍀🌿 #### Game description A well-written description promotes the genre and unique qualities of your game while also providing the best context for both players and Roblox's dynamic discovery systems. - **Summarize your game** – Summarize what your game is about in the first sentence, as this is your opportunity to present the most accurate impression of its genre and content. > **Success:** Compete with racers around the world in some of the craziest courses you've ever seen! > **Success:** Fight off hordes of zombies with bats, guns, swords, and a bunch of weapons hidden throughout challenging mazes. - **Provide keywords** – Include all keywords that may be relevant to your game. This makes it easier for players and Roblox to understand the themes and genres presented in your game. > **Success:** Do you enjoy being a sheriff? Join and rid our town of robbers and crooks! Arrest the bad guys, throw them in jail, and even have a guns-blazing shoot-out outside the saloon. > **Success:** Open your own restaurant, make delicious food, get lots of customers, and expand! Earn experience along the way and learn more recipes. If you love cooking and starting your own business, this is your chance to become a tycoon! - **Avoid spamming** – Avoid repeating keywords or adding irrelevant keywords. This may result in demotion of your game. > **Error:** Play our fun granny obby game. Obby obby obby obby obby obby obby adopt me bloxburg arsenal ninja legends parkour tycoon battle disaster survival super hero simulator run jump gun rpg pokemon wild west space alien piggy baldi granny murder mystery prison life jail break mario zelda dungeon quest. > **Info:** Roblox supports places up to 100 MB (104,857,600 bytes). Beyond that limit, **Save to Roblox** and **Publish to Roblox** might fail. The data that Studio uploads to Roblox is slightly smaller than the `.rbxl` file format, so you can export your place to `.rbxl` to get a sense of its size. > > Very few places, even the most complex ones, reach this limit, and when they do, it's often due to inefficiencies in the place. See [Troubleshooting](/docs/en-us/projects/place-files.md#troubleshoot-place-file-size). ### Create additional places After you have published your game, you can add additional places for different gameplay areas. To create a new place within an existing game: 1. Open an existing place file or create a new place from any Studio template. 2. Select **File** ⟩ **Publish to Roblox As…**. 3. In the **Publish Experience** window, click the tile for the game you'd like to add the place to. 4. Select **Add as a new place**, then click **Create**. Whether you choose to have a single, large place or many smaller places is mostly personal preference. For more information, see [design for performance](/docs/en-us/performance-optimization/design.md#streaming-and-teleportation). ### Change start place The start place of a game cannot be instantly swapped with another place, but you can overwrite the current start place. To change the start place: 1. **OPTIONAL** Save your **current** start place to a `.rbxl` file or to a new place within the game. 2. Go to the [Creator Dashboard](https://create.roblox.com/dashboard/creations) and select the game with the start place that you want to change. 3. Go to **Configure** ⟩ **Places**. 4. Click **Edit in Studio** for the intended **new** start place. Studio opens and loads that specific place. 5. In the top-left corner of Studio, go to **File** ⟩ **Publish to Roblox As…**. > **Error:** Do not select **Publish to Roblox** as this would publish the place over itself. 6. Click the tile for the same game, then choose the **current** start place. Click **Overwrite**. 7. **OPTIONAL** If the game is live, it's recommended that you [restart its servers](/docs/en-us/projects/update-games.md#restart-servers). ## Make game public By default, new games are set to **private**, meaning they are only accessible to you and your [creator team](/docs/en-us/projects/groups.md) who have **Edit** permissions (see [manage collaborators](/docs/en-us/projects/collaboration.md#manage-collaborators)). To make your game publicly available to anyone outside of the creator team, you have to meet the [publishing requirements](#publishing-requirements) below. > **Info:** You can make up to **5 private games that have never been public before** public per day. This limit resets daily. Making an existing public game private and then public again does **not** count toward this limit. ### Audience A game's target audience can be set through its **Audience** setting: 1. In the [Creator Dashboard](https://create.roblox.com/dashboard/creations), select your game. 2. Navigate to **Configure** ⟩ **Settings**. 3. Under **Audience**, choose from the options below: | Audience | Description | | --- | --- | | **Private** | Available only to users who have **Edit** permissions for the game. | | **Limited** | Available only to those who are part of the selected category of users. Not discoverable to the general public on Roblox. | | **Public** | Available and discoverable to the general public on Roblox. | 4. **OPTIONAL** For **Public** games, you can choose **Enable Beta Mode** to omit the game from the **Recommended For You** category on Roblox. Beta games are still public, but they have more limited reach and exposure. To widen your audience while still remaining in beta, create an ad campaign as outlined in [Ads Manager](/docs/en-us/production/promotion/ads-manager.md). If you enable sponsored ads, the game's analytics charts might show some users from **Recommended For You**. 5. Click **Save Changes**. ### Publishing requirements Publishing requirements depend on the target [audience](#audience) and apply to **all** publish actions: - Publishing a new game. - Updating an existing public game (publishing a new version of a place). - Publishing to a different game via **Publish to Roblox As**. - Setting your audience to **Limited** (any option under **Limited**) or changing it from **Public** to **Private**. #### Private games A game published as **Private** is only available to the owner or users with **Edit** permissions. Those with **playtest** permissions can't play private games. To enable their access, make sure you meet the requirements for **Public** games and then set the [audience](#audience) to **Limited** ⟩ **Playtesters**. #### Public/Limited games Publishing to a **Public** audience or **Limited** audience (playtesters, friends, or community members) has various requirements. To reach **16+** and **Trusted Friends**: - Have an account that is in good standing and at least 2 days old. - Complete an age check through [facial age estimation](https://about.roblox.com/age-estimation), [phone number](/docs/en-us/production/publishing/account-verification.md#verify-through-phone-number), or [government ID](/docs/en-us/production/publishing/account-verification.md#verify-through-government-id). - Complete the [content maturity & compliance](/docs/en-us/production/promotion/content-maturity.md) questionnaire. To reach **all ages** including [Roblox Kids and Select](/docs/en-us/production/publishing/kids-and-select.md), you must meet all of the previous requirements **plus**: - Verify your account through [government ID](/docs/en-us/production/publishing/account-verification.md#verify-through-government-id) or linked [parental account](https://about.roblox.com/parental-controls). - Enable [2FA](https://en.help.roblox.com/hc/en-us/articles/212459863-Add-2-Step-Verification-to-Your-Account) on your Roblox account. - Meet **one** of the following: - Maintain an active [Roblox Plus](/docs/en-us/production/monetization/roblox-plus.md) or [Roblox Premium](https://www.roblox.com/premium/membership) subscription for 2 consecutive months. - Pay a one-time, refundable [fee](#optional-fees). - Game must pass the [evaluation process](/docs/en-us/production/publishing/kids-and-select.md#for-ages-9-15-in-kids-and-select-accounts) for **Kids** and **Select** accounts. ### Optional fees If you do not have a Roblox Plus or Roblox Premium subscription for 2 consecutive months and you want to publish a game to users of all ages, you have the option to pay either a publishing or expedited review fee. #### Publishing fee The **publishing fee** is a one-time, refundable fee of 1,000 Robux per game that allows you to publish and expand the reach of your game to users of all ages. This fee is intended to make it expensive and logistically difficult for bad actors to create large numbers of accounts and flood the platform with spam. If your game maintains 100 highly engaged players for 60 days without moderation, you will receive a refund of the publishing fee. However, if your game is permanently moderated or taken down for not meeting Roblox's [Community Standards](https://en.help.roblox.com/hc/articles/203313410) within the 60-day period, the fee will **not** be refunded. To pay the publishing fee: 1. In the [Creator Hub](https://create.roblox.com/dashboard/creations), select the game you want to release to users of all ages. 2. In the left-hand navigation, navigate to **Audience** > **Reach**. 3. Under **Experience reach**, navigate to the **Refundable publishing fee** section, then click the blue **Pay** button. A pop-up dialog displays. 4. Click the **Pay 1,000** button to confirm your payment. #### Expedited review fee The **expedited review** fee is a one-time, refundable fee of 100,000 Robux per game that allows you to accelerate the safety and compliance reviews required to publish your game to users of all ages. This fee is intended to provide launch flexibility and financial accountability for qualified creators who haven't yet met the highly engaged age-checked player requirement for Kids and Select accounts. > **Info:** While the expedited review fee accelerates safety and compliance reviews for creators planning a timed launch or running an off-platform campaign and need to know exactly when their game will be ready for all ages, **all games must continue to meet the same standards as other Roblox Kids and Select games**. After 90 days, you can request a refund. As long as your game remains in good standing and maintains 100 highly engaged players, you can withdraw your fee and maintain Roblox Kids and Select eligibility. If your game hasn't hit those organic milestones yet, you can leave the fee in place to maintain your eligibility for Kids and Select audiences. However, if your game is permanently moderated or taken down for not meeting Roblox's [Community Standards](https://en.help.roblox.com/hc/articles/203313410), the fee will **not** be refunded. To pay the expedited review fee: 1. In the [Creator Hub](https://create.roblox.com/dashboard/creations), select the game you want to expedite for a safety review. 2. In the left-hand navigation, navigate to **Audience** > **Reach**. 3. Under **Experience reach**, navigate to the blue alert, then click the gray **Pay** button. A pop-up dialog displays. 4. Click the **Pay 100,000** button to confirm your payment. > **Warning:** If a minor violation is found during the 48-hour review, you must make the appropriate adjustments to your game, then resubmit. The 48-hour clock restarts from the point of resubmission. --- title: "Rights Manager" url: /docs/en-us/production/publishing/rights-manager last_updated: 2026-06-29T19:34:06Z description: "How to request the removal of IP infringing experiences, avatar items, and assets hosted on Roblox." --- # Rights Manager **Rights Manager** is an intellectual property (IP) management tool that lets rights holders or their authorized representatives report unauthorized use of their content on Roblox. All rights holders, including those that don't have a presence on Roblox, can use this tool to request the removal of unauthorized experiences, avatar items, and assets hosted on the platform. Before using the Rights Manager, you must [register as a rights holder](/docs/en-us/ip-licensing/get-started.md#register-as-an-ip-holder) and [verify ownership of your IP](/docs/en-us/ip-licensing/get-started.md#verify-ip-ownership). ## File an IP removal request Once Roblox accepts your registration as a rights holder, you can create IP removal requests for assets, experiences, and avatar items to protect your intellectual property. To file a removal request: 1. Go to **Intellectual Property** ⟩ **Removal Requests**. 2. In the **Removal Requests** tab, click **New Removal Request**. 3. Fill out the removal request form. 1. Under **Select creations for removal**: 1. Provide a link to your creation on the Roblox platform. 2. Provide up to 250 links to the creations you are reporting. 3. (Optional) Enter any additional details that might be relevant and that might help Roblox understand your report. For example, if you are reporting an asset in an experience, provide a screenshot and details of where to find the asset you are reporting. 4. Click **Add Creation**. 2. Under **Review creations**, review the details of your removal request and click **Next**. 3. Under **Add details**: 1. Under **General**, enter a name for the removal request. This makes it easier for you to search for this specific request later. 2. Under **Legal Agreements**, confirm that the information you provided is accurate and enter your full legal name as your signature. 3. Click **Submit for Review**. Roblox will now review your IP removal request. > **Info:** After you submit a removal request, you cannot edit any information within the request while Roblox reviews its content. ## Review IP removal requests To review removal requests, go to **Intellectual Property** ⟩ **Removal Requests**. Requests you filed are listed under the **Removal Requests** tab, while requests filed against you are listed under the **Claims Against Me** tab. Removal requests can have one of three statuses: - **Pending**: The IP removal request is still under review. - **Approved**: Roblox approved the IP removal request and removed the infringing content. - **Rejected**: Roblox rejected the IP removal request. If you believe that a rejected creation is still infringing on you, your client, or your organization's copyrights, you can file a new IP removal request with additional information. --- title: "Thumbnails" url: /docs/en-us/production/publishing/thumbnails last_updated: 2026-06-29T19:34:06Z description: "Thumbnail images and videos appear on the Roblox home page and at the top of an experience's detail page to showcase features and share information." --- # Thumbnails Thumbnails appear on Roblox's [Home](https://www.roblox.com/home) page and at the top of your experience's detail page, allowing you to showcase your experience's features and announce updates or events. You can feature up to 10 images or videos for each of your experiences' detail pages. All uploaded images and videos are moderated to make sure they adhere to the [Community Rules](https://en.help.roblox.com/hc/articles/203313410) and [Terms of Use](https://en.help.roblox.com/hc/articles/115004647846). | Type | Description | Cost / Quota | | --- | --- | --- | | **Image** | An [uploaded image](#images) which supplements the experience [icon](/docs/en-us/production/publishing/experience-icons.md) and helps players know what the experience offers. | Free | | **Uploaded Video** | An [uploaded authentic video](#videos), useful for displaying gameplay previews and other in-experience features. | Free, with monthly quota of 3 uploads | | **Auto‑Generated** | An auto‑generated image based on Studio's camera position for the most recent publish of the place. | Free | ## Manage thumbnails Uploaded thumbnails appear in the **Experience Detail Page** tab where they can be reordered, deleted, or given "alt" text for improved [accessibility](/docs/en-us/production/publishing/accessibility.md). ![Thumbnails and related actions on the Creator Dashboard](../../assets/creator-dashboard/Manage-Thumbnails.png) ### Images A thumbnail image should be **16:9 aspect ratio** and ideally **1920×1080 pixels** so that it always displays in high resolution across the Roblox site and app. Supported formats include `.jpg`, `.gif`, `.png`, `.tga`, or `.bmp`. See [best practices](#best-practices) for additional design recommendations. To upload an image thumbnail for your experience: 1. In the [Creator Dashboard](https://create.roblox.com/dashboard/creations), select the experience you want to upload an image thumbnail for. 2. Go to **Configure** ⟩ **Places** and select a place. 3. Click **Thumbnails** in the left nav, then use the **Home Page** tab and the **Experience Detail Page** tab to customize your experience's thumbnails for these two spaces. ### Videos > **Warning:** Xbox , PlayStation, and VR headsets do not currently support video thumbnails. To help users understand your experience before they join, you can upload an authentic gameplay video. Once approved, the video will appear first on your experience's detail page, and it can also be shown on the [Home](https://www.roblox.com/home) page. To upload a video thumbnail for your experience: 1. In the [Creator Dashboard](https://create.roblox.com/dashboard/creations), select the experience you want to upload a video for. 2. Go to **Configure** ⟩ **Places** and select a place. 3. Click **Videos** in the left nav. Video thumbnails should be **authentic** and [accurately portray in‑experience content](#video-accuracy) without misleading alterations. **All videos will be reviewed** to ensure they accurately reflect the experience and do not violate the [Community Rules](https://en.help.roblox.com/hc/articles/203313410) or [Terms of Use](https://en.help.roblox.com/hc/articles/115004647846). Certain modifications are acceptable, as long as they don't misrepresent the core experience: > **Success:****Camera Work** — Using varied camera angles for aesthetic or cinematic purposes is fine, as long as they showcase real gameplay. Studio's [free camera mode](#free-camera-mode) is especially helpful for capturing different vantage points. > **Success:****Minor Post-Processing** — Small adjustments like brightness, contrast, or minor visual effects are acceptable if they don't fundamentally alter the visual fidelity of the actual experience. It's also acceptable to overlay your experience's logo or branding in a video. > **Success:****Curated Sequences** — You can showcase optimized or highlight-reel playthroughs. These sequences don't need to be strictly linear but they must display actual gameplay mechanics and interactions. > **Success:****In-Experience UI Text** — Text that is part of your experience's UI (character names, menus, notifications that appear to the player, etc.) is acceptable because it's part of the authentic in‑experience view. > **Success:****Music Tracks** — Videos can contain music from Roblox's [catalog](https://create.roblox.com/store/audio) as governed by Roblox's internal licensing arrangements. To prevent misleading players, please adhere to the following policies. Videos which do not adhere to these policies will be **rejected for distribution** and count against the monthly quota of 3 video uploads. > **Error:****Misrepresented Gameplay** — Do not display gameplay mechanics, UI elements, or interactions that are not actually available in your experience. For instance, a roleplay experience should not be marketed as a racing game. Also ensure the video accurately reflects the core themes, for example a horror experience should depict horror elements and not misrepresent its theme. > **Error:****Artificial Visuals** — Graphics shown must be representative of the actual in-experience visuals. Avoid artificially enhancing graphics beyond what a player will experience. > **Error:****External Content & Overlays** — Don't include real-life footage of people, places, or any other content external to your experience. > **Error:****Spoken Audio** — Added voice-overs, narration, voice chat conversations, and music with lyrics is **not** permitted. > **Error:****Text, Claims, and Advertisements** — Overlay text sparingly and only to describe gameplay contexts such as "Collect coins to boost jumps." Do not include any text, visuals, or audio that is an advertisement, promotion, or subjective claim, for example "50% off" or "Free UGC!". ## Free camera mode Studio's **free camera mode** is a useful tool to capture in-experience screenshots and videos because it allows you to move the camera to vantage points which may not be possible through the default view camera. To enable free camera mode: 1. Go to an experience where you have server-side [Developer Console](/docs/en-us/studio/developer-console.md) access. 2. Initiate a solo [playtesting](/docs/en-us/studio/testing-modes.md#playtesting) session.![Test option in the testing modes dropdown of Studio's mezzanine.](../../assets/studio/general/Mezzanine-Testing-Mode-Test.png) 3. Press `Left Shift` and `P`. Once in free camera mode, you can use the following controls: | Keys/shortcuts | Action | | --- | --- | | `W` `A` `S` `D` | Moves the camera forward/left/back/right. | | `Q` `E` | Moves the camera down/up. | | `Z` `C` | Tilts the camera left/right (roll). | | `Z` `C` | Tilts the camera left/right (roll). | | `Shift` | In combination with any movement key, changes the camera speed. | | **Right mouse button** | When pressed, dragging the mouse moves the camera view around. | | **Mouse scroll wheel** | Zooms the camera in or out. | | `Ctrl``Shift``G` (Windows)
`⌘``Shift``G` (Mac) | Disables all `Class.CoreGui\|CoreGuis` but not developer-created GUIs. | | `Ctrl``Shift``C` (Windows)
`⌘``Shift``C` (Mac) | Disables all developer-created GUIs but not `Class.CoreGui\|CoreGuis`. | ## Home page personalization **Thumbnail personalization** helps you attract more users to your experience by showing the most relevant thumbnail to each user on their [Home](https://www.roblox.com/home) page. The goal of thumbnail personalization is to improve your qualified play through rate, which is the percentage of users that actively engage with your experience for a significant amount of time. > **Success:** During testing, experiences using thumbnail personalization had an average increase of +8.5% in their qualified play through rate. Some experiences had an increase of +50%. Thumbnail personalization only starts when you activate 2 or more thumbnails. If only one of your thumbnails is active, all users see that thumbnail for your experience on their home page. After you set up and activate thumbnail personalization, its algorithm shows each thumbnail to a random set of users to get data on the qualified play through rate for each user group. It then gives more impressions to the winning thumbnail for each group, but still gives the other thumbnails minimal traffic to continue exploration. ### Set up thumbnail personalization To set up thumbnail personalization: 1. In the **Home Page** tab, click **Edit active thumbnails**. 2. Upload new thumbnails or activate existing thumbnails. - To upload new thumbnails, click **Upload thumbnails** and select and confirm the images you want to use. Make sure to keep these images under 3MB and 1920×1080 pixels. - To activate existing thumbnails, enable the **Active** checkbox next to 2-5 thumbnails. 3. Click **Save changes**. 4. In the **Start new thumbnail personalization?** dialog, click **Start**. The banner in the **Home Page** tab updates to indicate that thumbnail personalization is active. ![Example of the Home Page tab](../../assets/publishing/experience-metadata/Thumbnail-Home-Page-Tab.png) ### Keep multiple thumbnails active > **Warning:** To get the most out of thumbnail personalization, make sure to **keep multiple thumbnails active** instead of choosing one winner, as this allows personalization to adapt to changing user trends. For example, if you had removed the thumbnail associated with the green line in the chart below too early, you would have missed future additional impressions for what ended up becoming your second-best performing thumbnail. ![Example of the Impression by Thumbnail graph](../../assets/publishing/experience-metadata/Thumbnail-Impressions-Graph.png) It's recommended to test multiple new thumbnails with any major experience or content update, and then not make changes to your thumbnails until your experience's next update. ### Analyze thumbnail personalization performance After a few hours, the **Thumbnail Performance** table in the **Home Page** tab populates with statistics related to each of your active thumbnails: | Title | Description | | --- | --- | | **Status** | Indicates if the thumbnail is active for personalization. | | **Impressions** | Number of impressions from home page recommendations. | | **Qualified Plays** | Number of qualified plays from home page recommendations. | | **Average Playtime** | The average session time in minutes per qualified play. | | **Qualified Play Through Rate** | The percentage of qualified plays divided by impressions from home page recommendations. | | **Winning Segment** | The thumbnail with the highest play through rate during the current thumbnail personalization process. | You can also use the **Impressions by thumbnail** and the **Qualified play through rate by thumbnail** graphs to further analyze the performance of your personalized thumbnails over time. ## Best practices To create the most positive impact for players looking to play your experience, consider the following best practices. > **Warning:** In addition to the practices shown below, avoid placing any essential text or elements at the **bottom** of the thumbnail, as it may potentially be covered by metadata like the player count. ### Quality and aspect ratio A thumbnail image should be **16:9** aspect ratio and ideally **1920×1080 pixels** so that it always displays in high resolution across the Roblox site and app. Displayed thumbnails will be stretched to 16:9 aspect ratio regardless of the upload's aspect ratio. ![High quality and sharp thumbnail](../../assets/publishing/experience-metadata/Thumbnail-High-Res.jpg)_> **Success:** High quality and sharp; upload in 16:9 aspect ratio_ ![Non-16:9 thumbnail](../../assets/publishing/experience-metadata/Thumbnail-Not-16-9.jpg)_> **Error:** Upload not in 16:9 aspect ratio; displayed thumbnail stretched_ ### Relevant content A thumbnail will have a higher impact if it's unique and provides relevant imagery on what players should expect when they join the experience. Thumbnails with ambiguous imagery or graphics may lead to unnecessary confusion. ![Thumbnail portraying a high-action urban racing experience](../../assets/publishing/experience-metadata/Thumbnail-Urban-Rush.jpg)_> **Success:** Thumbnail portrays a high‑action urban racing experience_ ![Thumbnail with unclear imagery and an ambiguous graphic in the top-right corner](../../assets/publishing/experience-metadata/Thumbnail-Unclear.jpg)_> **Error:** Unclear imagery and ambiguous graphic in top‑right corner_ ### Video accuracy Video thumbnails should be authentic and portray actual in‑experience content without misleading alterations. _> **Success:** Video presents actual gameplay which gives players an accurate and honest expectation of the game_ _> **Error:** Video presents an exaggerated version of the experience and gameplay which is misleading to players_ ### Theme and color You can express your experience's theme through overall theme and colors to help players decide if it's suitable and appealing to them. ![Thumbnail with a comical theme/character and bright colors](../../assets/publishing/experience-metadata/Thumbnail-Theme-Comical.jpg)_Comical theme/character with bright colors_ ![Thumbnail with a dark and stormy theme for a horror experience](../../assets/publishing/experience-metadata/Thumbnail-Theme-Horror.jpg)_Dark and stormy theme for a horror experience_
--- title: "VR guidelines" url: /docs/en-us/production/publishing/vr-guidelines last_updated: 2026-06-29T19:34:06Z description: "Guidelines on publishing an experience for VR devices." --- # VR guidelines Roblox VR uses [OpenXR](https://www.khronos.org/openxr/) as a backend for all VR devices. That means the implementation is platform agnostic, allowing experiences to work interchangeably on Quest and PC VR headsets. Testing on a PC VR headset should be valid for the Quest 2. At minimum, Oculus drivers require an NVidia GTX1060 graphics card or equivalent. ## Graphics quality mode When running Roblox on standalone VR headsets such as Quest 2 or Quest Pro, maintaining a comfortable performance level is crucial, as frame rate drops can cause discomfort for the user. To achieve this, an **Auto Quality Mode** setting is available on Quest which aims to maintain a minimum of 72 frames per second by automatically scaling the rendering detail based on performance data. ![Auto Quality Mode toggled on](../../assets/publishing/vr/Auto-Quality-Mode.png) If you test an experience on the Quest 2 and are not pleased with the visuals when **Auto Quality Mode** is turned on, it's recommended that you attempt to optimize your experience further. ## Comfort settings When users experience VR, movement of their camera/position can cause some discomfort due to the disconnect between what they see and the lack of motion detected by their body and inner ear. Roblox provides multiple settings to help users of all sensitivity levels, so everyone can enjoy an immersive and comfortable VR experience while playing. In addition to **Comfort**, **Default**, and **Expert** modes, the user may choose **Custom** to individually turn the following settings on and off: - **Vignette** — An overlay is displayed during motion which restricts the user's peripheral vision during motion and conceals camera teleports. - **Stepped Rotation** — The camera uses stepped rotation (teleporting to the next rotated position) for 1st-person and 3rd-person camera modes, versus the smooth rotation mode when rotating the camera with the controller. - **3rd Person Fixed Camera** - The camera is fixed in space and does not closely follow the player. The camera will teleport in order to keep the player in view. Additional information can be found at [VR Menus and Settings](https://en.help.roblox.com/hc/en-us/articles/15703381902740-VR-Menus-and-Settings). ## Camera modes When developing a VR-first experience, you can take advantage of the default camera scripts or implement your own custom scripts. #### Default cameras Roblox provides an array of default camera options, including a third-person comfort camera, a first person camera, and a vehicle camera. These camera modes are implemented as child scripts of `CameraModule` within `Class.PlayerScripts`. Support for the comfort menu options and future "immersion" features will be included with the default scripts. By utilizing the default scripts provided by Roblox, you benefit from ongoing updates and improvements that ensure compatibility with all new features and devices. This will likely provide an easier and more sustainable development process going forward. #### Custom camera As a developer, you have the flexibility to customize the default camera scripts by replacing them with your own VR camera implementation. This lets you achieve your own vision with an immersive VR-first view, but doing so opts your experience out of future script updates and you'll need to manually merge changes and improvements to the default scripts as they become available. If you have existing experiences that use a custom camera or other custom `Class.PlayerScripts`, it's likely that you do **not** have the most recent camera scripts that support VR. If you test your experience in VR and the camera is not working properly, it's recommended that you either merge the most current VR camera scripts or switch to the default scripts. ## Performance tips Untethered mobile VR devices like the Quest 2 target a high frame rate and resolution while rendering two separate images on a low-power chipset. To achieve stable and solid performance levels, the draw distance may be scaled down and light computation might get disabled through auto graphics quality. The following best practices may help you reach 72 frames per second with a higher graphic setting: - Enable [instance streaming](/docs/en-us/workspace/streaming.md) so that content is streamed in and out dynamically on the device. - Be careful to not overuse CPU-heavy computations like [raycasting](/docs/en-us/workspace/raycasting.md). - Avoid the usage of `Library.task.wait()` over `Class.RunService` frame updates. `Library.task.wait()` will not give you precise results compared to connecting to `Class.RunService.Heartbeat`. - Mobile VR is sensitive to a high number of draw calls. Build your environments efficiently, adding high detail where it really matters and lower detail elsewhere while being conservative with the number of objects used in the scene. - When creating custom 3D meshes, always strive to use as little geometry as possible for maximum runtime efficiency. - Minimize the number of semi-transparent objects and textures with partial transparency such as `Class.Decal|Decals` or the `Enum.Material|Glass` material. - Numerous and complex `Class.SurfaceGui|SurfaceGuis` can be costly, both on the rendering and CPU side. - Avoid writing platform-dependent code such as actions that rely on keyboard presses. Instead, use objects like `Class.InputAction` which supports input bindings from multiple sources. - Test and iterate often to make sure you're getting the anticipated performance and visual quality. If possible, invest in a Quest 2 headset. --- title: "Recommendation systems" url: /docs/en-us/production/recommendation last_updated: 2026-06-29T19:34:06Z description: "Use Roblox's built-in recommendation systems to create personalized curated content for individual users." --- # Recommendation systems Build custom recommendation systems in your experience to surface curated content such as 2D and 3D assets, minigames, other experiences, and more. The `Class.RecommendationService|Recommendation Service API` lets you log user activity, capture impressions, and deliver personalized results for virtually any type of engagement. Some common use cases for recommendation systems include presenting users with an end-of-round suggestion interface, a menu overlay presenting personalized actions, or other implementations that present a unique set of choices, assets, locations, or experiences to the user. Use the following universal steps and best practices to set up your recommendation system: 1. [Register your content](#register-your-content) 2. [Log users impressions](#log-user-impressions) 3. [Log quality actions](#log-quality-actions) 4. [Fetch recommendations](#fetch-recommendations) 5. [Monitor analytics](#monitor-analytics) > **Warning:** This guide covers the process of implementing `Class.RecommendationService` in your experience. This content doesn't cover the process of creating the [UI](/docs/en-us/ui.md), adding [assets](/docs/en-us/assets.md), or [teleporting users](/docs/en-us/projects/teleport.md). See additional documentation for those topics. ## Register your content > **Info:** There are many ways you can implement recommendations in your experience. The example settings in this guide are intended to recommend users various minigames within an experience, prioritizing recommendations most likely to **maximize quality play sessions**. > > This type of recommendation system is often incorporated in a **carousel UI menu**, allowing users to scroll, select, and find more details about the activity they are about to jump into. Considerations for this type of interface are included in the settings below. > > If creating your own system, you may need to use slightly different settings and configurations but follow the same basic steps. To populate content that your recommendation system can serve to users, use `Class.RecommendationService.RegisterItemAsync|RegisterItemAsync`. When registering an item, provide a `referenceId`, a developer-defined string that uniquely identifies the item in your own database. This referenceId is typically used to look up an item's metadata, such as the name, description, or asset ID, from your data store to display in the UI. As a best practice, register items as soon as they are created to ensure the recommendation pool is always fresh. ## Log user impressions Once the service knows about your items and starts suggesting them, tell the service when a user actually views the recommendations with `Class.RecommendationService.LogImpressionEvent|LogImpressionEvent`. ### When to log A carousel-type UI is a common interface for displaying multiple playable options to a user. Use the following best practices to ensure your logging provides clean actionable data: - **Single Log**: Log the impression **only once** per session for a specific item. - Do not log every time the item appears, as this creates noisy information that can interfere with your logging. - **Trigger**: You can choose to log when the item becomes fully visible in the menu or when the user interacts with the item, such as opening a Detail Page. > **Warning:****The ID trap** Be extremely careful about which ID you pass to `Class.RecommendationService.LogImpressionEvent`. The service needs to link the event strictly to the specific recommendation instance it generated. - **Do not** use a raw string or reference ID. - **Do** use the itemId that was returned to you by `Class.RecommendationService.RegisterItemAsync|RegisterItemAsync` or `Class.RecommendationService.GenerateItemListAsync|GenerateItemListAsync`. ### Duration Duration tracks the time of the impression. You can think of this as how long a user stared at the image or recommendation. If you are not using video, you can set the **Duration** value to `1`. This ensures that you get clean consistent data that the "user viewed this item", which is typically the only logging you need for a static card. ## Log quality actions When a user decides to interact with a recommendation, use `Class.RecommendationService.LogActionEvent|LogActionEvent`. In this example, simply clicking "Play" on the recommendation is not enough signal for a high-quality recommendation system. It's important to distinguish between an accidental click and a genuine session. ### Required action type When you call `Class.RecommendationService.LogActionEvent|LogActionEvent`, you must specify the type of action occurring. To track action events that result in play sessions, use `Enum.RecommendationActionType.Play`. It's important to use the correct enum to tell the backend that a specific type of action is activated. For play-time related recommendations, `Enum.RecommendationActionType.Play` is strictly required to match the MaximizePlays configuration template used in the next step to fetch recommendations. If you use a different enum, the model will not register the event correctly for play-related actions. ### The "quality play" strategy Depending on your experience, you may have different definitions on what constitutes "quality play". Use the following steps to help fine-tune what differentiates quality play in your situation. 1. **Track Internally**: When a user enters the mini-game, track their session time locally in your script. 2. **Filter**: Define a threshold for satisfaction, such as playing for more than 60 seconds. 3. **Log**: Only fire `Class.RecommendationService.LogActionEvent|LogActionEvent` if the user passes this threshold. > **Error:** **Strict rule: No "dummy" traffic** > > Only log actions for items that were actually served by the `Class.RecommendationService`. - Do not log plays that came from Search, Random picks, or Past Plays. - Do not use "Dummy Tracing IDs" to force these into the system. > > The model expects a realistic ratio. If you serve 1 impression but log 10 plays from external traffic, you confuse the training data and ruin the accuracy of your metrics. ## Fetch recommendations To fetch recommendations, use `Class.RecommendationService.GenerateItemListAsync|GenerateItemListAsync`. `Class.RecommendationService.GenerateItemListAsync|GenerateItemListAsync` accepts a dictionary that includes various options for the recommendation list query. While many of these settings are straightforward, it's important to understand the supported `ConfigName` parameters to ensure you provide successful recommendations. ### ConfigName You have several configurations available to you when fetching recommendations. In the current example designed to maximize recommendations based on the number of quality plays, use the `MaximizePlays` configuration. Depending on the use case, the `MaximizePlays` configuration is better than `MaximizeTimeSpent` as it additionally indexes on user satisfaction, rather than just time spent. This tells the system to return items that will most likely yield quality play. For more information on each supported config, check out `Class.RecommendationService.GenerateItemListAsync|GenerateItemListAsync`. ## Monitor analytics Once you have integrated the service, you can monitor its performance directly in the [Creator Hub](https://create.roblox.com/). To access your recommendation analytics: 1. In the [Creator Hub](https://create.roblox.com/), navigate to your experience page. 2. In the sidebar, navigate to **Engagement** > **Recommendation Service**. ![Link to recommendation service metrics within Creator Hub](../assets/analytics/recommendation-service.png) Key metrics to watch: - **Total Actions over time**: Are users actually engaging with the recommendations? - **Total Unique Users**: How many people are using the discovery feature? - **Average Items Impressed per User**: Are users scrolling through the carousel? - **Average Time Spent per User**: Is the quality of the recommendations keeping them in the experience longer? - In the example provided, **Duration** is set to `1`, so this specific metric isn't particularly valuable in that use-case. However, in other applications, such as videos, this can be a strong indicator for your impressions. Keep in mind the settings and processes used in this recommendation system intended to maximize quality plays, particularly using 1 second duration for thumbnails, logging only quality plays, and strictly managing your IDs. By keeping your logging and impression data clean, you can recommend and fine-tune the content that best fits the unique experiences of your users. While this flow covers the process for a recommendation system that maximizes playtime, you can use a similar order of operations and fundamental concepts to build out a recommendation service that fits your experience's needs. --- title: "The Roblox user base" url: /docs/en-us/production/roblox-user-base last_updated: 2026-06-29T19:34:06Z description: "Explains the distinct qualities of Roblox's user base and culture." --- # The Roblox user base Roblox's user base is distinct from other platforms in both its demographics and its expectations. Understanding the composition and culture of the Roblox community is essential to reaching a wider audience and achieving any measure of success with your experience. Roblox users value three fundamental strengths of the platform: - **Content variety** – the collective depth of the millions of unique user-generated experiences attracts and maintains a massive user base for all experiences. - **Low friction** – users can launch a new experience and start playing in seconds on almost any device. This ease of entry contributes to strong organic traffic and low UA costs. - **Social connection** – Roblox is in part a social network where users often play with their friends. A strong social component can massively accelerate growth and engagement for many experiences. Always bear in mind how your experience plays to these strengths when designing for the Roblox audience. ## Demographics Part of what makes Roblox so special is the **diversity** and **inclusivity** of its community and its content, whether that's giving a safe space for gender identities, the creation of inclusive content, or the wide variety of developers creating content on the platform. Roblox's user base now has more users that are at least 13 years old than under the age of 13. Understanding how Roblox works and its network effects will require some focus on younger users, but there's a growing opportunity to create content for their older counterparts. ![Over 13 user base as a percentage of Daily Active Users](../assets/getting-started/developing-on-roblox/Percentage-O13-Users-Chart.jpg)_Over 13 user base as a percentage of Daily Active Users_ Roblox is also expanding **geographically**. While the US, UK and Canada are still the biggest regions in terms of users, Europe, APAC, and the rest of the world are growing more year over year. As more local developers come online from around the world, this growth is only set to continue. ![Distribution of Daily Active Users in Q2 2022 worldwide](../assets/getting-started/developing-on-roblox/Global-User-Community-Chart.jpg)_Distribution of Daily Active Users in Q2 2022 worldwide_ ## User behavior on the platform A Roblox session typically means **playing a handful of different experiences**. Roblox is closer in many ways to YouTube than the App Store. It's important to realize that individual experiences fit into a larger, overarching experience. While some users do focus their attention in a session on one experience, it's very common for users to join an experience for a short time, switch to another, and repeat. User behavior in your experience may reflect this in more ways than just session length. Understanding your audience means understanding the other experiences your users engage with regularly. Since experiences are so interconnected, there tends to be a lot of **common conventions** and **mechanics** across the platform. Studying and emulating these conventions helps your users familiarize themselves with your experience quickly and may reduce the risk of them bouncing off in frustration. For more about designing for behavior on the platform, see [Design for Roblox](/docs/en-us/production/game-design.md). ### Social factors Roblox allows users to add **Friends**, join them in their experience, and invite them to yours. Chat is built directly into every experience, so news spreads fast from user to user. In addition to socializing in various user made Groups on-platform, users are often active on platforms like Discord and X (Twitter). Engaging experiences will generate a lot of **organic traffic** through word of mouth. Users find an experience they like, invite their friends, and then those friends invite more friends. YouTube can also attract a lot of attention, as users want to try out whatever their favorite YouTuber is playing. For more on generating traffic and building a community, see [Promote on Roblox](/docs/en-us/production/promotion.md). ## Roblox culture Roblox culture represents the dominant gaming, social, and consumer culture of the future. It has its own vocabulary, memes, celebrities, and even myths. Understanding the culture is essential to understanding your users. The sharing of **community-generated content** is the foundation of Roblox culture. Users see developers as celebrity peers. Iteration happens fast, leveraging real-time user feedback. Because of the prominent role of organic, word-of-mouth traffic for success on Roblox, **communicating with users is critical**. ### Glossary | Term | Definition | | --- | --- | | **Admin** | A Roblox employee. Admin status is easy to verify by checking for the Admin badge on a user's profile. | | **Badge** | Icons that show up on your Roblox profile, representing your achievements as a Roblox developer or user. | | **Builder** | A developer whose skills are a combination of environment art and level design. | | **Builderman** | The username of Roblox founder and CEO David Baszucki. | | **Developer forum** | A [community forum](https://devforum.roblox.com/) where developers can make posts, report bugs, request features, and read announcements from Roblox staff members. Typically shortened to DevForum. | | **Developer Relations** | The organization within Roblox that interfaces directly with developers, providing announcements, support, and special programs and events. Typically shortened to DevRel. | | **DevEx** | Shorthand for the Developer Exchange Program, which allows [qualified developers](https://en.help.roblox.com/hc/en-us/articles/203314100-Developer-Exchange-DevEx-FAQs) to withdraw Robux (Roblox's currency) from their accounts according to the current Robux exchange rate. | | **Experience** | A generic term for anything that can be created on Roblox, such as games, showcases, and concerts. | | **Pass** | A premium item sold in an experience that provides a permanent boost to gameplay. | | **Group** | A feature that allows developers to share assets, access, and profits. | | **Marketplace** | Allows approved developers to sell creations such as plugins and avatar items. | | **Moderation** | All content in a Roblox experience goes through a moderation process to ensure that it's appropriate according to Trust and Safety standards. Although this process occurs anytime content is uploaded prior to being published, content may also be moderated after publication. | | **Obby** | Shorthand for "obstacle course", a popular genre on Roblox that requires users to climb, jump and navigate obstacles to reach the end of the course. | | **Paid access** | While most experiences on Roblox are free, some developers choose to charge a fee for access. Paid access can be especially useful for developers who want to test their experience with a smaller audience prior to a full, free release. | | **Place** | Roblox experiences consist of one or more places, similar to how games comprise scenes in Unity or maps in the Unreal Engine. | | **Private server** | Some Roblox experiences offer users the ability to create private servers in which only they and their invited friends can play. Developers may offer private servers for free, or charge users a subscription fee to create them. | | **Showcase** | An experience that emphasizes visuals and environment design, with no gameplay. | | **Simulator** | A genre where users perform simple, repetitive actions in order to make progress. The goal often involves clicking an item in the world until it reaches its maximum level, then exchanging that item for an upgraded version with higher maximum potential. | | **Tycoon** | A genre where users collect from "droppers" that produce currency over time, in order to pay to construct something. As each part is constructed, new parts and droppers unlock for purchase. This cycle of collecting, building, and waiting to collect continues until there are no more additions left to build. | --- title: "Activity History" url: /docs/en-us/projects/activity-history last_updated: 2026-06-29T19:34:06Z description: "Activity History is a log of key configuration, publish, and edit activities within a place." --- # Activity History **Activity History** is a shared log of key changes within an experience. Each entry tells you what changed, who changed it, and when they made the change. 1. From Studio's **Window** menu, open **Activity History**. Events are displayed in chronological order, with the most recent appearing first.![A view of the Activity History window showing various changes and updates.](../assets/studio/general/Activity-History.png) 2. If you have a large number of events, you can filter them by type or collaborator. 3. If you want to manage the configuration or get more details on a change, use the **⋯** menu next to each event to get a link to the setting in either Studio or the Creator Hub. ## Event reference Most events are visible in both Studio and the experience's **Activity History** page on the [Creator Dashboard](https://create.roblox.com/dashboard/creations), although some are only visible in one location or the other. ### Place configuration | Event | Shown in Studio | Shown in Creator Hub | | --- | --- | --- | | Place published | ✓ | ✓ | | Place name changed | ✓ | ✓ | | Place description changed | ✓ | ✓ | | Place added to experience | | ✓ | | Place removed from experience | | ✓ | | **Allow place to be copied as a template using Create Place API** changed | ✓ | ✓ | | **Allow place to be updated using Save Place API** changed | ✓ | ✓ | ### Place edits | Event | Shown in Studio | Shown in Creator Hub | | --- | --- | --- | | Script draft committed | ✓ | | ### Experience configuration | Event | Shown in Studio | Shown in Creator Hub | | --- | --- | --- | | Experience changed to public or private | ✓ | ✓ | | Experience name changed | ✓ | ✓ | | Experience description changed | ✓ | ✓ | | Experience archived | ✓ | ✓ | | Age recommendation questionnaire updated | ✓ | ✓ | | **Shut down all servers** used | ✓ | ✓ | | Playable devices changed | ✓ | ✓ | | **Enable Studio Access to API Services** enabled or disabled | ✓ | ✓ | ### Monetization settings | Event | Shown in Studio | Shown in Creator Hub | | --- | --- | --- | | **Paid Access** enabled or disabled | ✓ | ✓ | | Paid access price changed | ✓ | ✓ | | **Allow Private Servers** enabled or disabled | ✓ | ✓ | | Private servers price changed | ✓ | ✓ | ### Notifications | Event | Shown in Studio | Shown in Creator Hub | | --- | --- | --- | | Notification string created | | ✓ | | Notification string deleted | | ✓ | | Notification string updated | | ✓ | For more information, see [Experience notifications](/docs/en-us/production/promotion/experience-notifications.md). ### Collaboration permissions | Event | Shown in Studio | Shown in Creator Hub | | --- | --- | --- | | Collaborator added with edit or play permissions | ✓ | ✓ | | Collaborator permissions level changed | ✓ | ✓ | | Collaborator removed | ✓ | ✓ | | Group given play permissions | ✓ | ✓ | | Group with play permissions removed | ✓ | ✓ | | Group role's permissions to this experience changed | ✓ | ✓ | For more information, see [Roles and permissions](/docs/en-us/projects/groups.md#roles-and-permissions). --- title: "Creator Store queries" url: /docs/en-us/projects/assets/api last_updated: 2026-06-29T19:34:06Z description: "Creator Store Queries let you externally query the Creator Store and Marketplace Catalog." --- # Creator Store queries You can search Roblox's assets outside Studio by accessing the Creator Store API. Use the [Creator Store API](#creator-store-api) to query Studio assets, such as meshes, models, and audio, and the [Marketplace API](#marketplace-api) to query avatar assets on the Marketplace. Each API requires a URL and custom search parameters for that specific catalog. If both URL and parameters are valid, the API returns a JSON format with the results of your search. ## Creator Store API For information on making calls to the Creator Store API for search, see Toolbox Service in the [Open Cloud reference](/docs/en-us/cloud/reference/features/creator-store.md#Toolbox_SearchCreatorStoreAssets). ## Marketplace API You can query avatar items from the Marketplace using the following URL: `https://catalog.roblox.com/v1/search/items/details?[params]` You can replace `[params]` with the appropriate [query parameters](#query-parameters) to customize your search. ### Query parameters You can specify search parameters by appending a series of parameters and values to the URL, each separated by a `&`. Use the following parameters to query the Marketplace: | Parameter | Type | Options and Values | | --- | --- | --- | | Category | byte | `0` = Featured
`1` = All
`2` = Collectibles
`3` = Clothing
`4` = BodyParts
`5` = Gear
`11` = Accessories
`12` = AvatarAnimations
`13` = CommunityCreations | | CreatorType | byte | `1` = User
`2` = Group

Queries using `CreatorType` also require the `CreatorTargetId` parameter. | | CreatorTargetId | long | The user or group ID depending on the CreatorType provided.

Queries using `CreatorTargetId` also require the `CreatorType` parameter. | | CreatorName | string | Search by creator name. If `Enum.CreatorType` is not provided, search is for users only. | | Cursor | string | Each search response contains a `nextPageCursor` if there is a next page and `previousPageCursor` if there is a previous page. Pass either of these values in the Cursor parameter of your next query to get the next or previous page of results. The cursor is valid if the other query parameters remain the same. | | Genres | byte | Specifies the genre for the search. The recommended approach to filtering on genres is to match the URL of a catalog page.

`1` = TownAndCity
`2` = Medieval
`3` = SciFi
`4` = Fighting
`5` = Horror
`6` = Naval
`7` = Adventure
`8` = Sports
`9` = Comedy
`10` = Western
`11` = Military
`13` = Building
`14` = FPS
`15` = RPG | | Keyword | string | Standard keyword search. | | Limit | int | Number of results to return. Values are currently limited to 10, 28, and 30. | | MaxPrice | int | The maximum price in Robux of items in the query. | | MinPrice | int | The minimum price in Robux of items in the query. | | SortAggregation | byte | `1` = PastDay
`3` = PastWeek
`4` = PastMonth
`5` = AllTime | | SortType | byte | `0` = Relevance (Default)
`1` = Favorited
`2` = Sales
`3` = Updated
`4` = PriceAsc
`5` = PriceDesc | | Subcategory | byte | `0` = Featured
`1` = All
`2` = Collectibles
`3` = Clothing
`4` = BodyParts
`5` = Gear
`9` = Hats
`10` = Faces
`12` = Shirts
`13` = TShirts
`14` = Pants
`15` = Heads
`19` = Accessories
`20` = HairAccessories
`21` = FaceAccessories
`22` = NeckAccessories
`23` = ShoulderAccessories
`24` = FrontAccessories
`25` = BackAccessories
`26` = WaistAccessories
`27` = AvatarAnimations
`37` = Bundles
`38` = AnimationBundles
`39` = EmoteAnimations
`40` = CommunityCreations
`41` = Melee
`42` = Ranged
`43` = Explosive
`44` = PowerUp
`45` = Navigation
`46` = Musical
`47` = Social
`48` = Building
`49` = Transport
`54` = HeadAccessories
`55` = ClassicTShirts
`56` = ClassicShirts
`57` = ClassicPants
`58` = TShirtAccessories
`59` = ShirtAccessories
`60` = PantsAccessories
`61` = JacketAccessories
`62` = SweaterAccessories
`63` = ShortsAccessories
`64` = ShoesBundles
`65` = DressSkirtAccessories
`66` = DynamicHeads | The following URL will search for the first 10 "Gear" items in "Accessories" sorted by all-time ("SortAggregation") and relevance ("SortType") created by Roblox ("CreatorTargetId" and "CreatorType"): `https://catalog.roblox.com/v1/search/items/details?Category=11&Subcategory=5&CreatorTargetId=1&CreatorType=User&SortType=0&SortAggregation=5&Limit=10` ### Response fields API responses return in a JSON format. The response provides asset details in the `data` key using the following fields: | Field | Description | | --- | --- | | assetType | One of the following asset type values (only returned if the item is an asset).

`2` = T-Shirt
`8` = Hat
`11` = Shirt
`12` = Pants
`17` = Head
`18` = Face
`19` = Gear
`25` = Arms
`26` = Legs
`27` = Torso
`28` = RightArm
`29` = LeftArm
`30` = LeftLeg
`31` = RightLeg
`41` = HairAccessory
`42` = FaceAccessory
`43` = NeckAccessory
`44` = ShoulderAccessory
`45` = FrontAccessory
`46` = BackAccessory
`47` = WaistAccessory
`48` = ClimbAnimation
`49` = DeathAnimation
`50` = FallAnimation
`51` = IdleAnimation
`52` = JumpAnimation
`53` = RunAnimation
`54` = SwimAnimation
`55` = WalkAnimation
`56` = PoseAnimation
`61` = EmoteAnimation | | bundleType | The bundle type ID (only returned if the item is a bundle). Possible values are `BodyParts` and `AvatarAnimations`. | | creatorName | The creator's name. | | creatorTargetId | The creator's ID. | | creatorType | The item's creator type. | | description | The item description. | | favoriteCount | The favorite count of an item. | | genres | List of the item's genres. Possible values include `All`, `Tutorial`, `Scary`, `TownAndCity`, `War`, `Funny`, `Fantasy`, `Adventure`, `SciFi`, `Pirate`, `FPS`, `RPG`, `Sports`, `Ninja`, and `WildWest`. | | id | The ID of the item. | | itemRestrictions | List of the item's restrictions. Possible values include `ThirteenPlus`, `LimitedUnique`, `Limited`, and `Rthro`. | | itemStatus | List of the item's status flags. Possible values include `New`, `Sale`, `XboxExclusive`, `AmazonExclusive`, `GooglePlayExclusive`, `IosExclusive`, and `SaleTimer`. | | itemType | The item type. Possible values are `Asset` or `Bundle`. | | lowestPrice | The lowest reseller price of the item (only returned if the item is resellable). | | name | The item name. | | price | The listing price of the item (current price may differ if the item is resellable). | | priceStatus | The price status of an item that is not on sale. Possible values are `Free`, `OffSale`, or `NoResellers`. | | purchaseCount | The purchase count of an item. | | unitsAvailableForConsumption | The unit available for consumption of a limited unique item. | The following is an example of expected return output for a single item: ```json { "keyword": null, "previousPageCursor": null, "nextPageCursor": "2_1_c541d05046b5c1c78a5d386b5e302243", "data": [ { "id":527373900, "itemType": "Asset", "assetType":42, "name":"Restless Souls Bandana", "description":"This bandana won't help you blend in with ghosts, but at least you'll be stylish.", "productId":41270974, "genres":[ "Scary", "Adventure" ], "itemStatus":[], "itemRestrictions":[], "creatorType":"User", "creatorTargetId":1, "creatorName":"Roblox", "price":300, "favoriteCount":15943, "offSaleDeadline":null } ] } ```
--- title: "In-Experience asset creation" url: /docs/en-us/projects/assets/in-experience-asset-creation last_updated: 2026-06-29T19:34:06Z description: "Explains how to allow users to create and save assets in your experience." --- # In-Experience asset creation With the in-experience asset creation feature, you can allow your users to save creations they made in your experience to their inventories. Your users can use these in-experience creations just like any other asset. Additionally, these creations attribute to your experience when displayed on the Roblox platform, so any user can use the attribution link to come to your experience and create their own. For example, you can enable users to create custom creatures as pets in your experience, and allow them to save their favorite pets to their inventories. You have full control to specify which objects users can modify and save from your experience. Users in turn can display their creations on their profiles with attribution to your experience, boosting visibility of your experience. ## Supported asset types and limits Just like all assets on the platform, in-experience creations are subject to [asset moderation](/docs/en-us/projects/assets.md#asset-moderation). Currently, you can only allow users to create [packages](/docs/en-us/projects/assets/packages.md) from your experience. These packages can't contain any scripts or private assets, such as audio, video, and nested packages. If the system detects scripts or private assets in a package that can be saved by users, it blocks the in-experience save action by hiding the save prompt for users. When you are running or testing your experience and add scripts or private assets as part of an in-experience creation, it fails to save and prompts error messages to the Studio [Output window](/docs/en-us/studio/output.md) or the [Developer Console](/docs/en-us/studio/developer-console.md). ## Enabling in-experience asset creation To enable in-experience asset creation for your users, use the `Class.AssetService:PromptCreateAssetAsync()` API method in a server-side script, along with other creation logic. Specify which instances in your experience you want to enable this functionality, set a custom trigger (such as a UI icon) for invoking the method, and listen for client remote events for saving assets. `Class.AssetService:PromptCreateAssetAsync()` takes the following parameters: - A `Class.Player` object representing the user who submits an asset creation. - An `Class.Instance` object representing the asset for creation. - The `Enum.AssetType`, which is currently limited to `Enum.AssetType.Model`. When the server invokes `Class.AssetService:PromptCreateAssetAsync()`, it prompts a **Submit Package** dialog on the client, so the user who triggers the save action can enter a name and description for the package. Roblox provides the dialog UI out-of-the-box, as the save workflow is a platform-level functionality. The following example server-side script prompts users to save a car that they paint in an experience: ```lua -- Define the AssetService variable local AssetService = game:GetService("AssetService") -- Set up PromptCreateAssetAsync() for prompting the submission dialog local function CreateAsset(player, instance) local complete, result, assetId = pcall(function() return AssetService:PromptCreateAssetAsync(player, instance, Enum.AssetType.Model) end) if complete then if result == Enum.PromptCreateAssetResult.Success then print("successfully uploaded, AssetId:", assetId) else print("Received result", result) end else print("error") print(result) end end -- Car painting logic omitted -- Add an event handler local function onUserPublish(player, promptObject) -- User saves the car instance with the experience's default color if promptObject.Name == "car" then CreateAsset(player, car) elseif promptObject.Name == "CarPaintYellow" or promptObject.Name == "CarPaintBlue" or promptObject.Name == "CarPaintBlack" or promptObject.Name == "CarPaintRed" then PaintCarColor(promptObject.Name) end end PublishEvent.OnServerEvent:Connect(onUserPublish) ``` [In-Experience Creation Demo Arena](https://www.roblox.com/games/12992503026/In-Experience-Creation-Demo-Arena) showcases an example on how you can use this feature. You can join the demo to walk through the in-experience creation workflow as a user, or edit the place in Studio to reference the design. ## Post-creation and attribution After users create and save an asset from your experience, they can find it in the following places: - Their [My Inventory](https://en.help.roblox.com/hc/en-us/articles/360000463726-How-to-View-or-Hide-Your-Inventory-in-a-Browser) page. - The **Creations** tab of their [Profile](https://en.help.roblox.com/hc/en-us/articles/203313660-All-About-Profiles-Blurbs-and-Profile-Customization) page. - The **Development Items** tab under their Creator Dashboard [Creations](https://create.roblox.com/dashboard/creations?activeTab=Model) page. - The **Inventory** tab of their [Toolbox](/docs/en-us/projects/assets/toolbox.md) in Studio. When users see in-experience creations on their friends' profiles or inventories, they see attribution to the original experience in which the asset was created. Users can click on the attribution link to redirect to the experience page, so they can join the experience and create their own. > **Warning:** Attribution is currently tied to a specific version of the created asset. If a user saves a package from an experience and further edits in Studio to create a new version, attribution no longer displays for the new version. --- title: "Assets" url: /docs/en-us/projects/assets last_updated: 2026-06-29T19:34:06Z description: "An overview of how assets work on the platform." --- # Assets Almost everything in Roblox is represented as a cloud-based asset with a unique corresponding ID. This ID is typically in the form of `rbxassetid://[ID]`, which gets applied to various instances as a property that's appropriate for that particular asset type. For example, `Class.Texture`, `Class.MeshPart`, and `Class.AudioPlayer` instances reference image, mesh, and audio assets through their respective `Class.Texture.ColorMapContent|ColorMapContent`, `Class.MeshPart.MeshId|MeshId`, and `Class.AudioPlayer.Asset|Asset` properties. `rbxassetid://7229442422` ![A decal asset of a young woman with a button for an eye.](../../assets/modeling/textures-decals/Texture-Example-Grafitti04.png) `rbxassetid://6768917255` ![An untextured treasure chest MeshPart asset.](../../assets/modeling/meshes/Base-Mesh-In-Marketplace.png) `rbxassetid://9125402735` This cloud-based asset system allows you to store assets through Roblox and reuse them across the platform in various contexts, such as in different objects and places, without maintaining local copies as part of each saved Studio experience. You can find millions of project assets in the [Creator Store](https://create.roblox.com/store), equip avatar assets in the [Marketplace](https://www.roblox.com/catalog), or you can create your own assets and [import](#asset-management) them directly into Studio through the asset management tools. When you import assets, they must pass a [moderation](#asset-moderation) check before users can see and interact with them in published experiences. After Roblox approves imported assets, you can choose to retain ownership of their usage on the platform or make them publicly available as detailed in [Asset permissions](#asset-permissions). ## Asset types All [asset types](/docs/en-us/reference/engine/enums/AssetType.md) available on the platform generally fall into one of three categories: - Assets that map to project-level items. You can find and configure these assets for specific experiences on the [Creator Dashboard](https://create.roblox.com/dashboard/creations). - Assets that are objects, or that change the appearance or behavior of objects within places. You can [import](#asset-management) these assets or find them in the [Creator Store](https://create.roblox.com/store). - Assets that change the body, clothing, or animation of avatars and non-playable characters. You can find these assets in the [Marketplace](https://www.roblox.com/catalog). Every asset type works differently depending on where it lives on the platform. See the following sections for information on how to use each asset type for [experiences](#for-experiences), [places](#for-places), and [avatars](#for-avatars). ### For experiences There are three asset types that map to project-level items. These asset types are unique to the experience that they map to, meaning they are non-transferable to other projects. - **Places** — Every experience has one or more [places](/docs/en-us/projects.md#places), or individual 3D worlds. Each place is represented by a [data model](/docs/en-us/projects/data-model.md) that describes the place's 3D world and logic. - **Badges** — A [badges](/docs/en-us/production/publishing/badges.md) is a special award you can gift users when they meet a goal within an experience, such as completing a difficult objective or playing for a certain amount of time. - **Passes** — A [pass](/docs/en-us/production/monetization/passes.md) is a monetization product that allows you to charge a one-time Robux fee in order for users to access special privileges within an experience, such as entry to a restricted area, an in-experience avatar item, or a permanent power-up. ### For places In general, there are two categories of asset types for places that you can either [import](#asset-management) or find in the [Creator Store](https://create.roblox.com/store): - Assets that exist as objects within the data model, such as [models](/docs/en-us/parts/models.md) and [meshes](/docs/en-us/parts/meshes.md). - Assets that you apply as an object's property, such as audio, images, fonts, and videos. For more information on both types of these assets for places, such as where and how you can use them in Studio, reference the following table. | Asset Type | Description | | --- | --- | | **Model** | A `Class.Model` is a container object for geometric groupings such as `Class.BasePart\|BaseParts`, `Class.MeshPart\|MeshParts`, and other `Class.Model` objects. Models can also contain objects such as `Class.Script\|Scripts`. Whenever you group objects together in Studio, they automatically become a `Class.Model` object. For more information, see [models](/docs/en-us/parts/models.md). | | **Mesh** | A `Class.MeshPart` is a type of part object that includes a physically-simulated custom mesh. For more information, see [meshes](/docs/en-us/parts/meshes.md). | | **Audio** | An `Class.AudioPlayer` instance loads and plays an audio file and passes the output through a `Class.Wire` to an `Class.AudioEmitter` or `Class.AudioDeviceOutput`. For more information, see [audio assets](/docs/en-us/audio/assets.md) and [audio objects](/docs/en-us/audio/objects.md). | | **Image** | Images are used in several ways within a place, including [textures/decals](/docs/en-us/parts/textures-decals.md) on parts, [image labels](/docs/en-us/ui/labels.md), [mesh textures](/docs/en-us/parts/meshes.md#adding-textures), textures for [custom materials](/docs/en-us/parts/materials.md), textures for [special effects](/docs/en-us/environment.md#special-effects), and more. | | **Font** | `Class.TextButton`, `Class.TextLabel`, and `Class.TextBox` objects display typography in a specific style when you apply a font asset ID. You cannot import fonts, but the [Creator Store](https://create.roblox.com/store) offers over 80 different fonts for your use. | | **Video** | A `Class.VideoFrame` object displays a video through its `Class.VideoFrame.Video\|Video` asset property. See [video frames](/docs/en-us/ui/video-frames.md) for more information. | ### For avatars There are three categories of asset types for avatars that you can find in the [Marketplace](https://www.roblox.com/catalog) and equip to avatars: - **Body Parts** — Assets that represent sections of the avatar character model, such as its head, torso, or legs. - **Clothing and Accessories** — Assets that represent clothing and accessories on top of body parts. - **Animations** — Assets that animate the avatar character model, such as how it runs, jumps, or swims. Every [character](/docs/en-us/characters.md) model contains a `Class.HumanoidDescription` object with asset IDs for the character's body parts, clothing, accessories, and animations. By default, a user's playable character references their personal Roblox avatar, but you can apply a custom `Class.HumanoidDescription` if desired. For more information, see [character appearance](/docs/en-us/characters/appearance.md). ## Packages Within Studio, you can convert single assets or asset hierarchies into **packages** and reuse them in multiple experiences, letting you optimize asset management across your entire team or across multiple projects. When a package is updated, you or your team members can update specific copies to the most current version, update all copies across an experience, or set specific copies to auto update. For more information, see [packages](/docs/en-us/projects/assets/packages.md). ## Asset URIs Assets and other content stored outside of the current place are identified through **Uniform Resource Identifiers** (URIs) which are formatted strings that point to a file stored online, within the Roblox application package, or saved to the client's device. The basic structure is a **scheme** followed by `://` and a **path** that varies according to the scheme. **[Scheme]://[Path]** The Roblox Engine supports several custom URI schemes for referencing content stored on the platform. ### rbxassetid `rbxassetid` points to a user-uploaded asset on Roblox. This format is a common standard for properties such as `Class.Decal.ColorMapContent`, `Class.ParticleEmitter.Texture`, and `Class.AudioPlayer.Asset`. _**rbxassetid://7229442422**_![A decal asset of a young woman with a button for an eye.](../../assets/modeling/textures-decals/Texture-Example-Grafitti04.png) _**rbxassetid://3845386987**_![Three particle emitters emit red, blue, and yellow particles over jar assets that sit on top of a stone tower. The red particles are highlighted.](../../assets/lighting-and-effects/particle-emitter/Texture-Indicated.jpg) _**rbxassetid://9125402735**_ ### rbxasset `rbxasset` points to Roblox's content folder on the user's device, for example: **rbxasset://textures/face.png** The content folder's location depends on the user's operating system: #### Windows `%LOCALAPPDATA%\Roblox\Versions\\content` (alternatively `C:\Program Files (x86)\Roblox\Versions\\content`) #### Mac `Applications/RobloxStudio.app/Contents/Resources/content` ### rbxthumb `rbxthumb` points to a thumbnail image for the provided asset. Its string format takes a thumbnail type (`type`), an asset ID (`id`), and width/height (`w`/`h`). **rbxthumb://type=Asset&id=24813339&w=150&h=150** | Type (`type`) | Expected `id` Value | Supported Sizes (`w`×`h`) | | --- | --- | --- | | `Asset` | ID for a 2D or 3D asset | 150×150, 420×420 | | `Avatar` | ID for a Roblox user (`Class.Player.UserId`) | 48×48, 60×60, 100×100, 150×150, 180×180, 352×352, 420×420, 720×720 | | `AvatarBust` | ID for a Roblox user (`Class.Player.UserId`) | 50×50, 60×60, 75×75, 100×100, 150×150, 180×180, 352×352, 420×420 | | `AvatarHeadShot` | ID for a Roblox user (`Class.Player.UserId`) | 48×48, 60×60, 100×100, 150×150, 180×180, 352×352, 420×420 | | `BadgeIcon` | ID for a [badge](/docs/en-us/production/publishing/badges.md#locating-badge-ids) | 150×150 | | `BundleThumbnail` | ID for a [Marketplace](https://www.roblox.com/catalog) bundle such as a [character](https://www.roblox.com/catalog?Category=17), [head](https://www.roblox.com/catalog?Category=4&Subcategory=66), or [shoes](https://www.roblox.com/catalog?Category=3&Subcategory=64) | 150×150, 420×420 | | `FontFamily` | ID for a font in the [Creator Store](/docs/en-us/production/creator-store.md) or [Toolbox](/docs/en-us/projects/assets/toolbox.md) inventory | 1200×80 | | `GameIcon` | ID for an experience (`Class.DataModel.GameId`); shows the experience's [icon](/docs/en-us/production/publishing/experience-icons.md) | 50×50, 150×150 | | `GamePass` | ID for a [pass](/docs/en-us/production/monetization/passes.md#locating-pass-ids) | 150×150 | | `GameThumbnail` | ID for an experience (`Class.DataModel.PlaceId`); shows the experience's primary [thumbnail](/docs/en-us/production/publishing/thumbnails.md) | 256×144, 384×216, 480×270, 576×324, 768×432 | | `GroupIcon` | ID for a [group](/docs/en-us/projects/groups.md) | 150×150, 420×420 | | `Outfit` | ID gathered from an `Class.OutfitPages` table | 150×150, 420×420 | ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local playerGui = player.PlayerGui local screenGui = Instance.new("ScreenGui") screenGui.Parent = playerGui local imageLabel = Instance.new("ImageLabel") imageLabel.AnchorPoint = Vector2.new(0, 0) imageLabel.Size = UDim2.new(0, 150, 0, 150) imageLabel.Position = UDim2.new(0, 16, 0, 16) imageLabel.BorderSizePixel = 0 imageLabel.BackgroundColor3 = Color3.new(0, 0, 0) imageLabel.BackgroundTransparency = 0.8 imageLabel.Parent = screenGui local success, content, isReady = pcall(function() return Players:GetUserThumbnailAsync(player.UserId, Enum.ThumbnailType.HeadShot, Enum.ThumbnailSize.Size352x352) end) if success then -- Apply thumbnail content to image label imageLabel.Image = content -- Crop thumbnail to circle local corner = Instance.new("UICorner") corner.CornerRadius = UDim.new(1, 0) corner.Parent = imageLabel end ``` ### rbxgameasset `rbxgameasset` points to an asset uploaded through the [Asset Manager](/docs/en-us/projects/assets/manager.md). It allows access to assets by a user‑friendly name instead of ID, so if you upload an image named `Potion.png`, you can reference it as: **rbxgameasset://Images/Potion** In the above example, `Images` is the category/folder that appears in the [Asset Manager](/docs/en-us/projects/assets/manager.md) and `Potion` is the asset name, minus its extension. > **Warning:**`rbxgameasset` only works for the current experience. If you paste a parent object that utilizes the asset into another experience, the asset will not load (or will load a different asset if you've uploaded one with the same name to that other game). If you need an asset to work across multiple experiences, use `rbxassetid` instead of `rbxgameasset`. ### rbxhttp `rbxhttp` is shorthand for `Class.ContentProvider.BaseUrl`, for example: **rbxhttp://Thumbs/Avatar.ashx?x=100&y=100&format=png**### rbxtemp `rbxtemp` allows you to refer `Class.File|Files`. Use `Class.File:GetTemporaryId()` to generate a valid `rbxtemp` URL. For example: **rbxtemp://1**### https / http `https` or `http` points to the exact location of something on the internet. It only works on Roblox-approved domains and raises an error if you use it elsewhere. **https://www.roblox.com/asset/?id=9723979220**## Asset permissions An asset's privacy status determines who has permission to use the asset within their experiences. By default, all assets are **private** when you or your [group](/docs/en-us/projects/groups.md) members import them into Studio. If you want to grant everyone access to the asset ID, you must distribute the asset to the [Creator Store](https://create.roblox.com/store). > **Warning:** Special privacy rules and workflows apply to **audio** and **video** assets. See [asset privacy](/docs/en-us/projects/assets/privacy.md) for details on sharing these asset types among friends and experiences. ## Asset moderation Roblox performs both human and automated asset moderation on a proactive and reactive basis to ensure assets adhere to the [Community Rules](https://en.help.roblox.com/hc/articles/203313410), [Terms of Use](https://en.help.roblox.com/hc/articles/115004647846), and [Digital Millennium Copyright Act](/docs/en-us/production/publishing/dmca-guidelines.md) (DMCA). If any asset violates these moderation policies, such as including discriminatory or adult content, the Moderation team flags and removes the asset to protect users from harmful or non-compliant content. This process generally happens within a few hours after you import the asset. If an asset is still in the moderation queue when you publish your experience, users cannot see or interact with the asset until Roblox approves it. If the asset doesn't pass moderation, you will receive a [notification](https://en.help.roblox.com/hc/en-us/articles/360020870412-Understanding-Moderation-Messages) explaining why the asset cannot be on the platform in its current state. For more information, see Working with Assets - Asset Moderation. > **Warning:** To maintain community safety and civility, Roblox may take down experiences and/or terminate accounts that maliciously import or publish non-compliant assets. ## Asset management Roblox offers several tools to import, store, and publish your assets: | Tool | Description | | --- | --- | | [Asset Manager](/docs/en-us/projects/assets/manager.md) | Imports and stores [images](/docs/en-us/parts/textures-decals.md), [meshes](/docs/en-us/parts/meshes.md), [audio](/docs/en-us/audio/assets.md), and more. Only displays assets that you've imported into the currently opened experience. | | [Toolbox](/docs/en-us/projects/assets/toolbox.md) | Stores every asset that you or your [groups](/docs/en-us/projects/groups.md) have ever imported into Studio, regardless of what experience you currently have open. Also includes assets from the [Creator Store](https://create.roblox.com/store), allowing you to quickly drop user-generated assets directly into your experiences. | | [Importer](/docs/en-us/studio/importer.md) | Imports 3D models from third-party modeling tools as a custom `Class.Model` instance. | ## Archive assets To further help with organization, you can archive most asset types in the **Creations** ⟩ **Development Items** section of the [Creator Dashboard](https://create.roblox.com/dashboard/creations). To archive an item, hover over an asset's thumbnail, click the **⋯** button, and select **Archive** from the context menu. ![Options button indicated for an asset.](../../assets/creator-dashboard/Options-Button-Asset.png) After you archive an asset, it disappears from the website and is no longer usable or visible in Roblox experiences, including your own. > **Warning:** For more information on how archiving works with Marketplace items, see [moderation](/docs/en-us/marketplace/moderation.md#archive-assets). --- title: "Asset Manager" url: /docs/en-us/projects/assets/manager last_updated: 2026-06-29T19:34:08Z description: "The Asset Manager lets you manage places and bulk import assets into your game." --- # Asset Manager The **Asset Manager**, accessible from Studio's **Window** menu or **Home** tab, lets you manage [places](/docs/en-us/production/publishing/publish-games-and-places.md) and bulk import assets into your game, including [images](/docs/en-us/parts/textures-decals.md), [meshes](/docs/en-us/parts/meshes.md), [packages](/docs/en-us/projects/assets/packages.md), [audio](/docs/en-us/audio/assets.md), and [models](/docs/en-us/parts/models.md). ## Current > **Info:** The following sections outline the current Asset Manager. The [V2 Beta](#v2-beta) is documented below. ### Asset folders Assets are organized within folders based on their type. You can switch between **grid view** and **list view** by clicking the view toggle button. ![The Asset Manager window with the View toggle button highlighted.](../../assets/studio/asset-manager/View-Toggle.png) ### Importing assets The legacy **bulk import** tool is ideal for importing up to 50 files in one batch. Imported assets enter the moderation queue and are only visible to you within their respective [folder](#asset-folders) and within the **Inventory** tab of the [Toolbox](/docs/en-us/projects/assets/toolbox.md). This bulk import tool is only available in the current version of the Asset Manager. The latest Asset Manager version utilizes the Studio [Importer](/docs/en-us/studio/importer.md). ![The Asset Manager window with the Bulk Import button highlighted.](../../assets/studio/asset-manager/Import-Button-V1.png) | Asset Type | Details | | --- | --- | | **Image** | You can import images in `.png`, `.jpg`, `.gif`, `.tga`, or `.bmp` format for use as [textures/decals](/docs/en-us/parts/textures-decals.md) on parts, [image labels](/docs/en-us/ui/labels.md), [mesh textures](/docs/en-us/parts/meshes.md#adding-textures), textures for [custom materials](/docs/en-us/parts/materials.md), textures for [special effects](/docs/en-us/environment.md#special-effects), and more. | | **Mesh** | You can bulk import meshes in either `.fbx` or `.obj` format, although this workflow does not support complex meshes such as those with rigging, skinning, or animation data. For complex meshes, it's recommended that you use the [Importer](/docs/en-us/studio/importer.md). | | **Audio** | You can import audio assets that you are certain you have permission to use in `.ogg`, `.mp3`, `.flac`, or `.wav` format. If you're uncertain whether you have permission to use an audio file, the [Creator Store](/docs/en-us/production/creator-store.md) has a variety of free-to-use audio, including more than 100,000 professionally-produced audio assets. See [Audio Assets](/docs/en-us/audio/assets.md) for details. | ### Insert assets You can insert assets into the [Explorer](/docs/en-us/studio/explorer.md) window hierarchy by dragging-and-dropping or by right-clicking the asset name/tile and selecting **Insert**. Behavior of dragging-and-dropping into the 3D viewport varies by asset type: | Asset Type | Drag-and-Drop Behavior | | --- | --- | | **Image** | If hovering over a valid parent object like a `Class.BasePart`, creates a new `Class.Decal` inside that parent with its `Class.Decal.ColorMapContent\|ColorMapContent` property preset to the asset ID. | | **Mesh** | Inserts the asset as a new `Class.MeshPart` in the workspace with its `Class.MeshPart.MeshId\|MeshId` property preset to the asset ID. | | **Audio** | Creates a new `Class.Sound` object in the workspace with its `Class.Sound.SoundId\|SoundId` property preset to the asset ID. | | **Package** | Inserts a copy of the package into the workspace. | ### Quick actions Quick actions are accessible by right-clicking an asset name/tile and selecting a context option. #### Places | Quick Action | Description | | --- | --- | | **Rename** | Lets you enter a new name for the place. | | **Copy ID to Clipboard** | Copies the place ID to the clipboard. | | **View History** | Opens the place version history, letting you view previous commits (publish actions) and their date/time. If desired, you can roll back to a previous version by selecting it and clicking the **Open** button. | | **Remove From Game** | Completely removes the place from the game. Not applicable to the [starting place](/docs/en-us/production/publishing/publish-games-and-places.md#change-start-place). | #### Images | Quick Action | Description | | --- | --- | | **Edit Asset** | Lets you edit general details such as the image title and description. | | **Rename Alias** | Renames the image alias in the **Asset Manager**. | | **Insert** | Inserts the image into the selected instance (or the workspace). | | **Copy ID to Clipboard** | Copies the image ID to the clipboard. | | **Remove From Game** | Removes the image from the **Asset Manager** but does not remove its instances from the game. | #### Meshes | Quick Action | Description | | --- | --- | | **Edit Asset** | Lets you edit general details such as the mesh title and description. | | **Rename Alias** | Renames the mesh alias in the **Asset Manager**. | | **Insert** | Inserts the mesh into the workspace. | | **Insert With Location** | Inserts the mesh into the workspace, retaining location data stored during the mesh import process. | | **Copy ID to Clipboard** | Copies the mesh ID to the clipboard. | | **Copy Mesh ID to Clipboard** | Copies the mesh `Class.MeshPart.TextureID` to the clipboard. | | **Remove From Game** | Removes the mesh from the **Asset Manager** but does not remove its instances from the game. | #### Packages | Quick Action | Description | | --- | --- | | **Insert** | Inserts a copy of the package into the workspace. | | **View on Website** | Opens up your browser to the package asset page. | | **Copy ID to Clipboard** | Copies the package ID to the clipboard. | | **Package Details** | Lets you manage basic package details, access permissions, and package versions. | #### Audio | Quick Action | Description | | --- | --- | | **Edit Asset** | Lets you edit general details such as the audio title and description. | | **Rename Alias** | Renames the item alias in the **Asset Manager**. | | **Insert** | Inserts the audio as a `Class.Sound` object into the selected instance (or the workspace). | | **Copy ID to Clipboard** | Copies the audio file ID to the clipboard. | | **Remove From Game** | Removes the audio from the **Asset Manager** but does not remove its instances from the game. | ### Asset inspection You can closely inspect an image, mesh, package, or audio file by hovering over its thumbnail in [grid view](#asset-folders) and clicking the "magnify" icon, or by right-clicking its name in [list view](#asset-folders) and selecting **Asset Preview**. ![A preview view of an asset in the Asset Manager. The Insect Asset icon is highlighted.](../../assets/studio/asset-manager/Asset-Inspect-Icon.png) When previewing 3D assets like meshes, you can move the virtual camera around to get a better view from all angles. For videos, you can preview the entire video in the popup. | Action | Description | | --- | --- | | Left mouse button + drag | Rotate around the object. | | Right mouse button + drag | Pan left, right, up, or down. | | Mouse scroll wheel | Zoom in or out. | In the lower-right corner of the preview frame, the **composition** button reveals the full hierarchy of the asset including `Class.Script|Scripts`, `Class.MeshPart|MeshParts`, `Class.Animation|Animations`, and more. ![A preview view of an asset in the Asset Manager, and the full hierarchy of the asset displays underneath the 3D representation. The Asset Composition icon is highlighted.](../../assets/studio/general/Preview-Panel-Hierarchy.png) ## V2 Beta > **Success:** The revamped Asset Manager is currently in beta. Enable it through **File** ⟩ **Beta Features** ⟩ **Revamped Asset Manager**. ### Inventory sorting Assets are sorted by various **inventories** as selected through the collapsible menu on the left side of the window. If you don't see the sorting list, click the expand/collapse button in the lower‑left area of the window. ![A preview of how inventories are sorted in the left navigation of the Asset Manager.](../../assets/studio/asset-manager/Inventory-Sorts.png) - **** — The open game's inventory, displayed by its name, with assets [shared with the game](/docs/en-us/projects/assets/privacy.md#to-experiences) listed under **Project**. - **Places In Experience** — The game's [places](/docs/en-us/production/publishing/publish-games-and-places.md). - **My Inventory** — Assets imported to your own user account, [shared with you](/docs/en-us/projects/assets/privacy.md#to-creators), or acquired from the [Creator Store](/docs/en-us/production/creator-store.md). - **Group Inventories** — Assets imported into [groups](/docs/en-us/projects/groups.md) you own or groups you belong to (assuming you have sufficient [permissions](/docs/en-us/projects/groups.md#roles-and-permissions) within the group), or assets [shared with the groups](/docs/en-us/projects/assets/privacy.md#to-groups). Right‑clicking over a group reveals a context menu with the following options: | **Hide Group** | Hides the group from the sidebar. | | --- | --- | | **Customize Groups** | Allows you to choose the groups which appear in the sidebar and search filter options. | | **Refresh Groups** | Refreshes the list of groups that you're eligible to show or hide. | ### Folders Folders let you organize assets and other folders into a hierarchy within an inventory. You can create folders inside **My Inventory**, inside **Group Inventories** that support folder operations, and inside an open game's inventory under **Project**. Folders have the following limits: - Up to **50,000 folders** per account or root inventory. - Up to **100 child folders** in any single folder or root inventory. - Up to **20,000 assets** per folder. - Up to **20 levels** of folder nesting. > **Info:** The first time folders are initialized for an account, a brief setup period may apply. If folder operations aren't yet available, the sidebar shows a warning icon next to the affected account; click it to refresh readiness. Setup typically completes within a few minutes. #### Create, rename, and delete Right-click in the sidebar or main view to access folder actions. | Action | Description | | --- | --- | | **Create Folder** | Creates a new empty folder in the current location. A new entry appears with an inline name field; type a name and press `Enter` to confirm or `Esc` to cancel. | | **Group Selection as Folder** | Available when one or more assets are selected. Creates a new folder and moves the selected assets into it. | | **Rename** | Replaces the folder's name with an inline editable field pre-filled with the current name. Press `Esc` to cancel without committing changes. | | **Delete Folder** | Removes the folder. Folders must be empty before they can be deleted; move or delete the contents first. When multiple empty folders are selected, the menu shows **Delete Folders**. | Folder names cannot contain `\`, `/`, `:`, `*`, `?`, `"`, `<`, `>`, or `|`, and cannot end with a period. Names beginning with a period (for example, `.hidden`) are allowed. #### Navigate You can navigate the folder hierarchy from either panel: - **Sidebar** — Use the expand arrow to reveal subfolders, or single-click a folder to open it. - **Main view** — Double-click a folder to drill into it. Click the parent entry in the sidebar, or use the back arrow, to return. #### Move items You can move both assets and folders, but only within the same inventory. Cross-inventory moves (for example, from **My Inventory** into a **group** inventory) aren't supported. | Method | Description | | --- | --- | | **Drag and drop** | Drag selected items onto a destination folder in either the main view or the sidebar tree. Valid drop targets highlight while you hover. | | **Move dialog** | Right-click a selection and choose **Move** to open a folder picker. Use the drill-in arrow or double-click to enter a folder, **Back** to go up a level, then select a destination and click **Move**. **Refresh** re-fetches the folder list from the server. | You can move multiple items at once. Moving a folder into itself or into one of its own descendants is blocked, and invalid destinations are not highlighted as drop targets or are grayed out in the **Move** dialog. #### Search and folders When you type a search query, results include a separate **Folders** section listing matching folders alongside asset results. Double-click a folder result to exit search mode and navigate into that folder. > **Info:** Drag-and-drop and the **Move** dialog are disabled while viewing search results. Navigate to a folder or inventory first, then reorganize. #### Unsupported scopes Folder operations aren't available in the following views: - **Places In Experience** — Places can't be organized into folders. - **Recent Uploads** — Read-only for organizational purposes. ### Asset display options In the main panel, assets for the selected [inventory](#inventory-sorting) are displayed. To toggle the view type, click on the **view type** button and select either **List** or **Grid**. While in grid view, asset tiles can be resized through the slider widget. ![Diagram of the view type button and options in the Asset Manager.](../../assets/studio/asset-manager/Display-Options.png) In **List** view, click on a column header to order assets by that detail and resize columns by dragging the separator bar between them. ![Diagram of how columns can be ordered and resized in List view within the Asset Manager.](../../assets/studio/asset-manager/List-View-Ordering.png) > **Info:** Columns can be toggled on or off by right‑clicking in the header bar region. While columns cannot be reordered by dragging, you can toggle any column off and then toggle it back on to add it as the furthest column to the right. In **Grid** view, ordering is controlled through the **sort** button: ![Location of the sort button for Grid view within the Asset Manager.](../../assets/studio/asset-manager/Grid-View-Ordering.png) ### Asset type filters The **filter items** button lets you control which assets are displayed. ![Location of the filter button in the Asset Manager.](../../assets/studio/asset-manager/Asset-Type-Filters.png) | Filter Type | Options | | --- | --- | | **Asset Type** | **Animation** \| **Audio** \| **Decal** \| **FontFamily** \| **Image** \| **Mesh** \| **MeshPart** \| **Model** \| **Place** \| **Plugin** \| **Video** | | **Creator** | | | **Source** | **Creator Store** \| **Shared With Me** \| **Uploaded** | | **Is Package** | | | **Only Archived** | | ### Asset import The **asset import** button lets you import one or more assets of any asset type. Imported assets enter the moderation queue and, upon approval, are added to the inventory of the user/group that owns the experience. ![Location of the import button in the Asset Manager.](../../assets/studio/asset-manager/Import-Button.png) For more information on supported file types and other features, see [Importer](/docs/en-us/studio/importer.md). ### Insert and quick actions Quick actions are accessible by right‑clicking an asset name/tile and selecting an option from the context menu. Note that places offer unique actions versus other assets. #### General | Action | Description | | --- | --- | | **Insert** | Lets you select from either **Insert at Camera** or **Insert at Asset Position** for spatial 3D assets (non‑spatial assets like audio are simply inserted into the `Class.Workspace` or the currently selected [Explorer](/docs/en-us/studio/explorer.md) instance). Does not appear for **FontFamily** or **Plugin** assets. | | **Edit Asset** | Opens the asset configuration window to edit details such as the asset title, description, and more. Does not appear for **FontFamily** or **Plugin** assets. | | **Move** | Opens the [Move dialog](#move-items) to relocate the asset into a folder within the same inventory. Disabled while viewing search results. | | **Copy Asset ID** | Copies the asset ID to the clipboard, convenient for pasting the ID into an object's property value such as `Class.AudioPlayer.AssetId` or `Class.MeshPart.MeshId`. | | **View in Browser** | Opens your browser to the asset's page. | | **Install** / **Update** / **Report** | Applicable only for **Plugin** assets. Installs, updates, or reports the plugin, respectively. | #### Places | Action | Description | | --- | --- | | **Open** | Opens the place in another Studio session if it's not the currently open place. | | **Rename** | Lets you enter a new name for the place. | | **View History** | Opens the place version history, letting you view previous commits (publish actions) and their date/time. If desired, you can roll back to a previous version by selecting it and clicking the **Open** button. | | **Remove From Experience** | Completely removes the place from the game. Not applicable to the [starting place](/docs/en-us/production/publishing/publish-games-and-places.md#change-start-place). | | **Copy Asset ID** | Copies the place's `Class.DataModel.PlaceId\|PlaceId` to the clipboard. | | **View in Browser** | Opens your browser to the place's page. | ### Search query language Use the search query language to refine how you find assets in your Asset Manager inventory. You can combine keywords, operators, and tags to filter, prioritize, or exclude results. > **Info:** When combining multiple features, search terms are processed in the following order (highest to lowest priority): Exact search → Excluded terms → Optional terms → Asset ID tag → Created before → Created after → Updated before → Updated after → Creator name → Creator ID → Group name → Group ID → Numeric asset ID → Audio type. ##### Query features The following features can be combined in a single query: | Feature | Syntax | How it works | | --- | --- | --- | | Exact search | `"..."` | Matches a phrase exactly. Exact matches take the highest priority and bypass other tag processing.

Use quotes to search for text that looks like a tag (for example, `"asset_id:123"`). You can mix quoted phrases with regular terms and include multiple quoted phrases in one query. | | Exclude terms | `-term`
`exclude:term` | Excludes results containing specific terms.

You can use multiple `-` prefixes and `exclude` tags in the same query, or mix both syntaxes. | | Optional terms | `term?`
`term!`
`optional:term` | By default, all terms are required. Optional terms boost matching results without excluding results that don't contain them.

You can use `?`, `!`, or the `optional` tag, and mix these syntaxes. | | Asset ID search | `asset_id:123456`
`assetId:123456`
`123456` | Pins matching assets to the top of the results.

A standalone number is treated as an asset ID if it matches one. Multiple IDs are supported and are merged automatically. | | Date filters | `created_before`
`created_after`
`updated_before`
`updated_after`
| Filters results by creation or update date.

Dates use the US format (MM/DD/YYYY) and must be wrapped in brackets if they include spaces. Natural language dates (for example, "yesterday") are not supported.

Each tag also supports a camelCase alternative: `createdBefore`, `createdAfter`, `updatedBefore`, `updatedAfter`. | | Creator filters | `creator_name`
`creator_id`
`group_name`
`group_id` | Filters results by the creator user or creator group based on their name or ID. When multiple creator or group filters are provided, results matching any of them are included.

Each tag also supports a camelCase alternative: `creatorName`, `creatorId`, `groupName`, `groupId`.

Names that contain spaces must be wrapped in brackets (for example, `group_name:[Group With Space]`). If a provided name or ID is invalid, only that specific filter is ignored; other valid creator or group filters in the same query are still applied. | | Audio type | `music`
`sfx`
`sound effects` | Filters audio assets by type. To restrict results to a specific audio type, append the type at the end of your query.

To search for the literal word instead of filtering, wrap the word in quotes. | ##### Tags Tags modify how search works, but they are not treated like search terms themselves. They only filter or rank your results. If a tag has a typo or an invalid date, it's treated as a regular search term instead. Tags can use the formats `tag:value` or `tag=value`, and are not case-sensitive. Some tags accept multiple values when you use brackets. For example, `exclude:[rusty, broken]` and `asset_id:[123, 456]`. ##### Date formats Date filters support the following formats: | Format | Example | | --- | --- | | ISO 8601 | `2026-03-10` | | ISO with time | `2026-03-10T14:30:00Z` | | US format | `03/10/2026` | | Long form (requires brackets) | `[March 10, 2026]` | | With time (requires brackets) | `[03/10/2026 2:30 PM]` | ##### Example queries - `"wooden crate" asset_id:99887766 created_after:2023-06-01 -damaged optional:painted`: Exactly matches "wooden crate", pins a specific asset by ID, filters by date, excludes "damaged", and ranks "painted" results higher. - `explosion sfx -loop created_before:[December 31, 2023]`: Filters sound effects named "explosion", excludes looping variants, and limits the results by date. - `125447393891114 sword glowing? -rusty`: Pins a specific asset by ID, searches for "sword", boosts "glowing", and excludes "rusty". - `tree creator_name:buildmaster group_id:9876543`: Searches for "tree", filtered to assets by creator "buildmaster" or the group with ID 9876543. - `sword creator_id:1000001,2000002 group_name:[Studio Builders]`: Searches for "sword", filtered to assets by creators with IDs 1000001 or 2000002, or the group "Studio Builders".
--- title: "Monetize your creator assets" url: /docs/en-us/projects/assets/monetize last_updated: 2026-06-29T19:34:06Z description: "An overview of creator assets and the Creator Store." --- # Monetize your creator assets ## Create and sell Studio plugins You can create and sell Studio [plugins](/docs/en-us/studio/plugins.md) to the creator community through the [Creator Store](/docs/en-us/production/creator-store.md). A plugin is an extension that adds additional features or functionality to [Roblox Studio](/docs/en-us/studio.md) and helps improve creator workflows. You can either offer plugins for free or sell them for a minimum of $4.99 USD. Roblox offers a market-leading revenue share for these sales, as only taxes and payment processing fees are deducted. For more information, see [Creator Store - Distribute and sell assets](/docs/en-us/production/creator-store.md#distribute-and-sell-assets). Creator assets promote collaboration and resource sharing, and help developers create better and more dynamic experiences. | Feature | Description | | --- | --- | | [Plugins](/docs/en-us/production/creator-store.md#distribute-and-sell-assets) | Generate real-world income by selling plugins on the Creator Store. Plugins are tools designed to improve development workflows. | --- title: "Packages" url: /docs/en-us/projects/assets/packages last_updated: 2026-06-29T19:34:06Z description: "Packages allow you to reuse single assets or asset hierarchies across experiences." --- # Packages To keep assets organized and reusable across your team or across multiple projects, you can group your instances and convert them into reusable **packages**. Packages make the overall process of building worlds faster and more ergonomic, but they also help with consistency, deduplication, collaboration, and versioning: - Easily [update all copies](#mass-updates) of a package to the latest version, or only [update specific copies](#update-outdated-copies). - Set packages to [automatically update](#automatic-updates) whenever there's a new version. - Give collaborators **Edit** or **Use** permissions to the contents of a package. - View the full version history for a package, compare versions, and restore old versions. The most efficient workflow is to [create a package](#create-packages), [share it](#share-and-access-levels) with any collaborators, and [set it to auto-update](#automatic-updates). And you don't have to wait for a model to be "done" before using it in a package. Even if the package is a placeholder on top of a simple shape, you can use it to [greybox your environment](/docs/en-us/tutorials/curriculums/environmental-art/greybox-your-environment.md) and then have the placeholder copies automatically update as you publish more detailed versions. ## Create packages > **Warning:** If you want to create a package with restricted assets that you don't have [explicit permission](/docs/en-us/projects/assets/privacy.md#view-permissions) to use, you can still create the package but those specific restricted assets will **not** be visible or audible at runtime unless the experience itself has permission to use those assets. You can create a package from any single object or any single parent and children branch of objects. If you want to create a package for a single object, add it to a `Class.Model` grouping first so that you can add, remove, or scale child objects within the package later without breaking the package. To create a package: 1. In the [Explorer](/docs/en-us/studio/explorer.md) window or 3D viewport, right-click the object(s) you want to turn into a package and, in contextual menu, select **Convert to Package**. 2. In the new window, fill in the package details. In particular, if you're working in a [group](/docs/en-us/projects/groups.md), set **Ownership** to the appropriate group in which you have permission to create/edit group experiences. > **Warning:** Ownership transfers are **not** supported by the asset system, so carefully consider the owner when creating a package. 3. Click **Submit**. 4. After the conversion completes, the object receives a "chain link" symbol in the [Explorer](/docs/en-us/studio/explorer.md) window to identify it as a package. Additionally, you can see a new `Class.PackageLink` object parented to the object.![A maple tree model in the Explorer window with the package icon next to it.](../../assets/studio/packages/Package-Structure-Explorer.png) 5. **OPTIONAL** Select the `Class.PackageLink` object and enable `Class.PackageLink.AutoUpdate|AutoUpdate` in the [Properties](/docs/en-us/studio/properties.md) window. Note how the icon changes to indicate that the package is set to automatically update. > **Error:** Do not delete or move the `Class.PackageLink` object! Doing so for any package copy converts the copy back into a normal object and loses package capabilities, such as ability to update itself when there's a new version. ## Insert packages To insert a package that doesn't already exist in the current place, you must **initially** insert it from the [Toolbox](/docs/en-us/projects/assets/toolbox.md): - From **Inventory** ⟩ **My Packages** for packages that you've published to or obtained from the [Creator Store](/docs/en-us/production/creator-store.md), as well as packages that a friend owns and has given you [permissions](#share-and-access-levels) to use. - From **Creations** ⟩ **Group Packages** for packages published by members of your [group](/docs/en-us/projects/groups.md) (including yourself). ![A close up view of the Toolbox with both the Inventory tab and the assets dropdown menu highlighted.](../../assets/studio/toolbox/Inventory-My-Packages.png)_Toolbox ⟩ Inventory ⟩ My Packages_ ![A close up view of the Toolbox with the Creations tab highlighted.](../../assets/studio/toolbox/Creations-Group-Packages.png)_Toolbox ⟩ Creations ⟩ Group Packages_ Once you've inserted a package into a published place's data model, it appears in the [Asset Manager](/docs/en-us/projects/assets/manager.md) and remains there even if you later delete all copies of it. > **Warning:** Be careful when inserting assets that you didn't create into your experiences, as they can contain malicious scripts. Save your experience first and then investigate any scripts within unfamiliar assets so that you can easily revert back to the place version. ## Modify packages You can edit packages and their children just like other objects. Modifying packages disables [auto‑update](#automatic-updates) until you [publish](#publish-package-changes) or [revert](#revert-package-changes) the changes. Most edits flag the package as modified, although the following changes are **not** considered package modifications: - Changing the **name** of the root node. - Changing the **position** or **rotation** of the root node of a package that is a `Class.BasePart`, `Class.Model`, or `Class.GuiObject`. - Changing the `Class.LayerCollector.Enabled|Enabled` property of a root node `Class.GuiObject` such as a `Class.ScreenGui`, `Class.SurfaceGui`, or `Class.BillboardGui`. - Changing a part reference of a `Class.Weld` inside the package that references an instance outside of the package. Once modified, packages with unpublished changes get a modified icon in the [Explorer](/docs/en-us/studio/explorer.md) window: ![The Explorer window with an icon that identifies unpublished package changes.](../../assets/studio/packages/Package-Modified.png) ### Add or update configurations You can include [instance attributes](/docs/en-us/studio/properties.md#instance-attributes) at the root of a package to customize its behavior. When you publish a package, the current set of attributes/values will become the package's **default configurations**. On any given **copy** of a package, those attribute values can be changed on a per‑instance basis, and those changed are noted through **bold italics**. When package copies are [updated](#update-outdated-copies), modified configuration values will be preserved, while other attributes will be updated to the latest default value. ![The Attributes section of the Properties window showing one attribute value modified and noted through bold italics.](../../assets/studio/packages/Package-Configured-Attributes.png) > **Info:** Currently the only way to handle configuration values is to add scripts in the package which read and apply behavior based on the configuration at runtime. > **Info:** If you simply want to update the default **values** for configurations, you can change those attribute values and republish the package (the package will **not** be flagged as modified in this case). If you want to publish other modifications but not change the default configuration values, you should publish on a package without any value changes, or revert the configurations to their defaults before you publish. ### Nested packages You can nest packages inside of other packages to maintain and collaborate on complex hierarchies, such as a series of vehicle mechanics which can be modified independently of the vehicle's parent package. If you modify a nested package, both the nested package **and** the parent package are considered as modified. You must [publish changes](#publish-package-changes) to any **nested** package before you're allowed to publish changes to its **parent** package, since publishing the parent would mark it as current/unmodified and conflict with the nested package's modifications. ### Package scripts Each script within an **unmodified** package is read-only and shows a notification on the top with a hyperlink to unlock the script. ![A script tab with a yellow notification that you can click to modify the script that's within an unmodified package.](../../assets/studio/packages/Script-Unlock-Link.png) Clicking the hyperlink: - Flags the package as modified regardless of whether you edit the script. - Removes the notification/hyperlink from **other** scripts within the package. Once the package is published and moved to an **unmodified** state, the scripts under it become read‑only with a hyperlink to modify. ## Publish package changes You can publish any change to a package as a new version, making your updates available to other package copies throughout the place and across all experiences. It's **not** required to publish a modified package before publishing a place because the modified version is saved along with the place for future iteration. To publish changes to a package: 1. In the [Explorer](/docs/en-us/studio/explorer.md) window or 3D viewport, right-click the modified copy and select **Publish to Package**. 2. If a package copy has [auto-update](#automatic-updates) turned on, it immediately pulls in the updated version when you open the place that contains them. Other copies get a "download" symbol that indicates an update is available. You can [individually update](#update-outdated-copies) or [mass-update](#mass-updates) all copies.![Icons next to packages that have a potential update.](../../assets/studio/packages/Package-Update-Available.png) 3. **OPTIONAL** Add a description of your changes: 1. Right-click the package and select **Package Details**. 2. In the **Asset Configuration** window, select **Versions**. 3. Under your most-recent change, select **Add**. 4. Describe your changes, then click the **Submit** button. ## Update outdated copies You can update outdated package copies to the most recent version, or you can continue to use the older version. 1. In the [Explorer](/docs/en-us/studio/explorer.md) window, locate outdated copies by the "download" symbol. You can also select the outdated copy in the 3D viewport.![Icons next to packages that have a potential update.](../../assets/studio/packages/Package-Update-Available.png) 2. Right-click an outdated copy and select **Get Latest Package**. You can also select multiple packages, right-click, and **Get Latest For Selected Packages**. ### Mass updates Extensive use of packages may result in many package copies across multiple places in an experience. In addition to [individual syncing](#update-outdated-copies) and [automatic updates](#automatic-updates), you can update all copies of a package through **mass updating**. 1. **RECOMMENDED** Close other Studio instances with any of the experience's places open; this prevents another unsaved instance of a place from potentially overwriting your updates. 2. In the [Explorer](/docs/en-us/studio/explorer.md) window or 3D viewport, right-click the desired package and select **Update All**. 3. Choose all places in the experience or some subset of places and click **Update**. > **Info:** Mass updating packages automatically **saves** the selected places but does not **publish** them. Additionally, to prevent unintended overwrites, mass updating does not affect [modified](#modify-packages) versions of a package; after the mass update completes, Studio displays a warning with the number of packages that it did **not** update, if any. ### Automatic updates To make syncing easier, you can set a package copy to update automatically whenever a newer version is published. Auto-update of the package copies will take place when a place is opened in Studio. 1. In the [Explorer](/docs/en-us/studio/explorer.md) window, expand the package's hierarchy tree and select its `Class.PackageLink` object.![A close up view of a package in the Explorer window with its PackageLink object highlighted.](../../assets/studio/packages/PackageLink-Explorer.png) 2. In the [Properties](/docs/en-us/studio/properties.md) window, enable the `Class.PackageLink.AutoUpdate|AutoUpdate` property. If you have [nested packages](#nested-packages), this property only applies to the highest-level parent package, meaning automatic updates only occur when the **parent** package is updated. > **Warning:** Automatic updating does not apply to [modified](#modify-packages) package copies. Once you modify a package instance, its **AutoUpdate** property becomes disabled and is ignored. ## Share and access levels You can grant permission to friends, experiences, groups, or specific group user roles so that they can freely use your packages in their creations. For more information on asset access, see [Asset privacy](/docs/en-us/projects/assets/privacy.md). > **Warning:** If you share a package with restricted assets that you don't have [explicit permission](/docs/en-us/projects/assets/privacy.md#view-permissions) to use, you can still share the package but those specific restricted assets will **not** be visible or audible at runtime unless the friend, experience, or group has permission to use those assets. #### To collaborators To change package permissions for a collaborator, such as a friend or group: 1. In the [Explorer](/docs/en-us/studio/explorer.md) window, [Toolbox](/docs/en-us/projects/assets/toolbox.md), or [Asset Manager](/docs/en-us/projects/assets/manager.md), right-click the desired package and select **Package Options** ⟩ **Package Details**. 2. In the **Asset Configuration** window, select **Permissions**. 3. Using the search bar, input and select a collaborator that you want to grant permission to use your package, then choose a permission level. | Permission | Description | | --- | --- | | **Use & View** | The collaborator can use and view (but not edit) the current and previous package versions. Once you provide a collaborator with this ability, you cannot revoke access to a copy they already inserted into their experience; revoking access prevents reinsertion or package updates, but package copies in their data model remain intact. | | **Edit** | The collaborator can use, view, and edit the current and previous package versions, including publishing changes to it. | #### To experiences To grant package access to an experience, the experience must be editable to either you or a group that you belong to in which you have the **Create and edit group experiences** role permission. To change package permissions for an experience: 1. Navigate to the [Creator Dashboard](https://create.roblox.com/dashboard/creations). 2. In the upper tab bar, select **Development Items**, then click **Models & Packages**. 3. Select the package you want your experience to have permission to use. The package's **Configure** page displays. 4. In the package's left-hand navigation, select **Permissions**. The package's **Permissions** page displays. 5. From the **Experiences** tab, click the **Add experiences** button. 6. Type the experience's universeID into the **Enter Universe IDs** input, then click the **Add** button. The experience displays beneath the input with its access visible. > **Info:** If you want to give multiple experiences permission to use your restricted access at the same time, you can enter multiple universeIDs as long as you separate them with a comma. 7. Click the **Done** button to finalize your package asset access permissions. ## Revert package changes Instead of undoing an entire series of package changes one by one, you can [revert unpublished changes](#revert-unpublished-changes) in one action, restore a package to a previous [version](#restore-to-published-version), or revert changes to specific [configurations](#revert-configurations). ### Revert unpublished changes To undo an entire series of **unpublished** changes: 1. In the [Explorer](/docs/en-us/studio/explorer.md) window, locate modified copies by the dot on their package icons. You can also select modified copies in the 3D viewport.![The Explorer window with an icon that identifies unpublished package changes.](../../assets/studio/packages/Package-Modified.png) 2. Right-click a single modified copy and select **Undo Changes to Package**, or select multiple copies (at least one of them modified), right-click, and select **Undo Changes to Selected Packages**. ### Restore to published version To restore a package to a **previously published** version: 1. In the [Explorer](/docs/en-us/studio/explorer.md) window, [Toolbox](/docs/en-us/projects/assets/toolbox.md), or [Asset Manager](/docs/en-us/projects/assets/manager.md), right-click the desired package and select **Package Options** ⟩ **Package Details**. 2. In the **Asset Configuration** window, select **Versions**. The window displays details for each published version, including the date and time of publication, along with any descriptions of the changes. 3. Click the checkmark next to the version you want to restore and click **Submit**. > **Warning:** Reverting changes to a package does not reset the configuration to the default. See [revert configurations](#revert-configurations). ### Revert configurations To revert any [configuration](#add-or-update-configurations) attribute to its default, select **Reset** from its options menu in the **Attributes** section of the [Properties](/docs/en-us/studio/properties.md) window. ![The Attributes section of the Properties window with the attribute's option menu expanded.](../../assets/studio/packages/Package-Configured-Attributes-Reset.png) ## Compare package versions When a package has multiple versions, you can compare changes between versions using the diff viewer. The tool has a package hierarchy menu that indicates all added, removed, or modified instances between versions using corresponding icons, including the following features: - **Visual Overview** shows the visual differences of the 3D rendering under different camera positions. It's the default view for packages with a 3D object (models, parts) as the root object, and it's currently only available for the root object. - **Properties** shows changes of properties and attributes. It's the default view for packages with a non-3D object (scripts, lights, 2D objects) as the root object, and it's available for all instances in a package. - **Script** shows line-by-line script differences. It's available for packages containing scripts, regardless of whether the script is the root object or not. To compare package versions: 1. In the [Explorer](/docs/en-us/studio/explorer.md) or 3D viewport, right‑click the target package and select **Package Options** ⟩ **Compare Package Versions**. 2. By default, the window compares changes between your local copy and the latest version. Use the two dropdown menus to compare different versions.![A close up view of an example diff viewer. The compare settings are highlighted.](../../assets/studio/packages/Compare-Versions-Selector.png) > **Warning:** Some older versions might be incompatible with the package diff tool. 3. After selecting versions: 1. To compare the visual renderings of the root model, if applicable, select the **Visual Overview** tab and adjust the camera control for your desired angle. Controls are synchronized across views: - Pan the camera using left mouse clicks. - Rotate the camera using right mouse clicks. - Zoom in and out the camera with the mouse wheel. - Recenter using the keyboard shortcut `F`. 2. To compare properties and attributes of an instance, select the instance and the **Properties** tab. 3. To compare script differences for a package that is a script or contains scripts, use the **Script** tab. > **Warning:** This tool only allows you to compare script changes between the current version and the latest published or local version, without indicating the collaborators who made the change. > **Info:** Alternatively, you can open the script diff tool directly by right‑clicking the target package in the [Explorer](/docs/en-us/studio/explorer.md) window or 3D viewport and selecting **View Script Changes**. --- title: "Asset privacy" url: /docs/en-us/projects/assets/privacy last_updated: 2026-06-29T19:34:06Z description: "Reference for the Roblox asset privacy system. Explains which asset types Asset Privacy affects (Images, Decals, Meshes only), how to enable Asset Privacy, how to grant and revoke permissions for collaborators and experiences, and which operations Asset Privacy does not affect." --- # Asset privacy The **asset privacy system** lets you control which creators and experiences can use your assets on Roblox. Assets can have one of two access privacy types: - **Restricted** — Creators or experiences can only use the asset after the asset owner grants permission. - **Open Use** — Any creator or experience can use the asset. If a creator or experience doesn't have permission to use an asset, it cannot load in Studio, and a clickable error message displays in the [Output](/docs/en-us/studio/output.md) window. If a creator has permission to use an asset but the experience they're working on does not, clicking the error message displays a pop-up window to allow the creator to grant permission to the experience for any restricted assets. When an asset is Open Use, Roblox doesn't need to check its permissions when it loads into an experience because every creator and experience can freely use it. However, when an asset is Restricted, Roblox **always** checks its permissions when it loads into an experience or when a creator takes an action on the asset, such as inserting it into Studio, sharing it with another creator, or listing it on the [Creator Store](/docs/en-us/production/creator-store.md). > **Info:** The asset privacy system controls which creators and experiences can use the asset, but others can still see its metadata, such as its name and description. ## What Asset Privacy controls Asset Privacy controls only the default privacy state assigned to **Images**, **Decals**, and **Meshes** when they are created. No other asset types are affected. **Asset Privacy does not affect:** - **Audio, Video, Models, MeshParts, Animations, and Packages** — these types have their own creation defaults and are not changed by the Asset Privacy setting. If audio or other asset types are not loading or distributing as expected, Asset Privacy is not the cause. - **Distributing assets on the Creator Store or Marketplace** — enabling or disabling Asset Privacy does not change your distribution eligibility or workflow. - **Using your own assets in your own published experiences** — your assets are always accessible to you in your own experiences regardless of their privacy setting. - **Assets created before Asset Privacy was enabled** — the setting only applies to newly created assets. - **Sharing restricted assets** — Restricted does not mean permanently inaccessible. You can grant access to specific friends, groups, and experiences at any time through [Creator Dashboard](#grant-permissions), or use the [Revamped Asset Manager](https://devforum.roblox.com/t/beta-updates-to-revamped-asset-manager/4548832/1) to bulk-grant access across many assets at once. **Key constraints:** - **Open Use is irreversible.** Once an asset is set to Open Use, it cannot be changed back to Restricted. - **Experience grants are permanent.** Once you grant an experience permission to use a restricted asset, that permission cannot be revoked. - **Revoking a collaborator's access does not affect experiences.** If a collaborator has already used a restricted asset in an experience, that experience retains access even after the collaborator is removed. - **The setting is not retroactive.** Only assets created after enabling Asset Privacy are Restricted by default. ## Restrict permissions > **Info:** You can list your or your group's restricted assets by themselves or as a dependency of a restricted asset on the Creator Store. This allows creators to use your assets as you list them under models, mesh parts, or packages, and disallows them from accessing and repackaging your restricted asset dependencies. By default, Images, Decals, and Meshes are created as **Open Use**. Enable **Asset Privacy** to make these asset types **Restricted** on creation instead. All other asset types are not affected by this setting. ### Enable Asset Privacy Individual creators and groups can enable Asset Privacy via Creator Dashboard. When enabled, newly created Images, Decals, and Meshes are set to **Restricted** by default. All other asset types are unaffected by this setting. > **Info:** Enabling Asset Privacy does not change your ability to distribute assets on the Creator Store or Marketplace, use your assets in published experiences, or share your assets with collaborators. #### As a creator To restrict your mesh, image, and decal assets on creation: 1. Navigate to the [Creator Dashboard](https://create.roblox.com/dashboard/creations). 2. In the top-right corner of the page, click on your username and select **Settings**. 3. In the left-hand navigation, select **Advanced**. 4. In the **Asset Privacy** section, enable the **Opt-in to restrict assets on creation** toggle. Every newly created Image, Decal, and Mesh is now Restricted by default. #### As a group > **Warning:** You can only complete this process if you are the group owner. To restrict your group mesh, image, and decal assets on creation: 1. Navigate to the [Creator Dashboard](https://create.roblox.com/dashboard/creations). 2. In the upper-left, expand the account switcher and select your group. 3. Expand the account switcher again, select **Settings** under the group's name, then select **Group Profile**. 4. In the **Asset Privacy** section, enable the **Allow restricted assets on creation** toggle. Every newly created Image, Decal, and Mesh your group creates is now Restricted by default. After enabling Asset Privacy, verify the asset permission of any newly uploaded asset in the asset configuration page: ![The landing page for asset permissions with the Collaborators tab selected.](../../assets/creator-dashboard/Asset-Toggle.png) Selecting **Open Use** warns that once an asset has been set to **OpenUse**, it cannot be set back to **Restricted**. ![The landing page for asset permissions with the Collaborators tab selected.](../../assets/creator-dashboard/Asset-Make-OpenUse.png) After setting to **Open Use**, the asset displays its current asset access permission. ![The landing page for asset permissions with the Collaborators tab selected.](../../assets/creator-dashboard/Asset-OpenUse.png) ### Decals All `Class.Decal|Decals` are linked to an `Image`, which means a `Decal` can only be set to OpenUse if its `Image` dependency is OpenUse. Otherwise the following warning displays: ![The landing page for asset permissions with the Collaborators tab selected.](../../assets/creator-dashboard/Asset-Decals-Warning.png) ### Avatar assets Avatar assets, such as accessories, clothing, and bodies, require all dependent textures and meshes to be `OpenUse` when uploading the Avatar asset to the Marketplace. If you have Asset Privacy enabled and use a newly imported model in an avatar asset, you will be prompted to set your restricted texture and mesh dependencies to `OpenUse` during Marketplace [upload](/docs/en-us/marketplace/publish-to-marketplace.md#upload-an-asset): ![The landing page for asset permissions with the Collaborators tab selected.](../../assets/creator-dashboard/Asset-Avatar-Restricted.png)_ If your avatar item or bundle contains any restricted primitive assets, you must convert them to Open Use._ ![The landing page for asset permissions with the Collaborators tab selected.](../../assets/creator-dashboard/Asset-Avatar-Success.png)_ If your entire avatar item has correct permissions, you can continue to upload and publish your avatar item to the Marketplace._ ## Grant permissions Restricted assets must be explicitly granted permission before they are visible or audible at runtime. Grant permissions individually through Creator Dashboard, or use the [Revamped Asset Manager](https://devforum.roblox.com/t/beta-updates-to-revamped-asset-manager/4548832/1) to bulk-grant permissions across many assets at once. ### To collaborators To grant a collaborator permission to use one of your restricted assets, the following must be true: - To grant to an individual creator: the creator must be your [friend](https://en.help.roblox.com/hc/en-us/articles/203313580-How-to-Make-Friends) on the platform. - To grant to a group: you must have [Edit all group experiences](/docs/en-us/projects/groups.md#roles-and-permissions) permission in that group. When you grant an individual creator permission: - They can use the asset in any of their individual or group-owned experiences. - They can grant permission for experiences they own or have edit access to. When you grant a group permission: - All group members with Edit access can use the asset in experiences owned by that group. Once a collaborator has permission to use one of your restricted assets, they can insert the asset from the **Inventory** and/or **Creation** tab of the [Toolbox](/docs/en-us/projects/assets/toolbox.md), or by using the restricted asset ID in the [Properties](/docs/en-us/studio/properties.md) window. ![A close up view of the Toolbox with the Inventory tab highlighted.](../../assets/studio/toolbox/Inventory-Tab.png)![A close up view of the Toolbox with the Creations tab highlighted.](../../assets/studio/toolbox/Creations-Tab.png)_Inserting a restricted asset through the Toolbox_ ![A close up view of the Properties window with the Asset property highlighted.](../../assets/studio/properties/Enter-Private-Asset-ID.png)_Inserting a restricted asset in the Properties window_ If a collaborator wants to use the restricted asset in a script, or wants to save or publish a template place that includes the restricted asset, the experience itself must also have permission to use the asset. If this step is skipped, the asset is not visible or audible during runtime, and a clickable error message displays in the **Output** window. To grant a collaborator permission to use a restricted asset: 1. Navigate to the [Creator Dashboard](https://create.roblox.com/dashboard/creations). 2. In the upper tab bar, select **Development Items**, then click one of the following tabs: - **Models & Packages** - **Audio** - **Decals** - **Images** - **Videos** - **Meshes** - **MeshParts** - **Animations** 3. Select the asset you want your collaborator to have permission to use in their experiences. The asset's **Configure** page displays. 4. In the asset's left-hand navigation, select **Permissions**. The asset's **Permissions** page displays. 5. From the **Collaborators** tab, click the **Add collaborators** button.![The landing page for asset permissions with the Collaborators tab selected.](../../assets/creator-dashboard/Permissions-Collaborators.png) 6. Using the search bar, input and select a friend or group that you want to grant permission to use your restricted asset. The creator or group displays beneath the search bar with their access type.![The Add Collaborators pop-up menu.](../../assets/creator-dashboard/Add-Collaborators.png) 7. **(Optional)** If your selected asset type is a model or package, you can choose one of the following access types: - **Use** - The collaborator receives permission to use the asset in their experiences. - **Edit** - The collaborator receives permission to edit the asset's metadata, such as its name and description. 8. Click the **Done** button to finalize your asset access permissions. > **Info:** To grant permissions across multiple assets at once, or to bulk-share assets with specific groups and users, use the latest Asset Manager beta. See [Revamped Asset Manager](https://devforum.roblox.com/t/beta-updates-to-revamped-asset-manager/4548832/1) for details. ### To experiences > **Error:** Once an experience has permission to use a restricted asset, you cannot revoke access to the asset. To grant an experience permission to use one of your restricted assets, the experience must be editable to either you or a group that you belong to in which you have the [Edit all group experiences](/docs/en-us/projects/groups.md#roles-and-permissions) permission. Once an experience has permission to use a restricted asset, anyone with **Edit** access to that experience can: - Copy and paste the asset into another place file within that experience. - Use its asset ID in the **Properties** window or in scripts within any place file within the experience. > **Info:** When you grant permission to an experience by inserting a restricted asset into a collaborator's experience, the asset is also automatically granted to the experience's owner. The following tabs walk you through the process of either granting one or more experiences access to a single restricted asset, or granting an experience access to one or more restricted assets. #### On an asset level To grant an experience permission to use a restricted asset: 1. Navigate to the [Creator Dashboard](https://create.roblox.com/dashboard/creations). 2. In the upper tab bar, select **Development Items**, then click one of the following tabs: - **Models & Packages** - **Audio** - **Decals** - **Images** - **Videos** - **Meshes** - **MeshParts** - **Animations** 3. Select the asset you want the experience to have permission to use. The asset's **Configure** page displays. 4. In the asset's left-hand navigation, select **Permissions**. The asset's **Permissions** page displays. 5. From the **Experiences** tab, click the **Add experiences** button.![The landing page for asset permissions with the Experiences tab selected.](../../assets/creator-dashboard/Permissions-Experiences.png) 6. Type the experience's universeID into the **Enter Universe IDs** input, then click the **Add** button. The experience displays beneath the input with its access visible. > **Info:** To give multiple experiences permission at the same time, enter multiple universeIDs separated by commas.![The Add Experiences pop-up menu.](../../assets/creator-dashboard/Add-Experiences.png) 7. Click the **Done** button to finalize your asset access permissions. #### On an experience level To grant an experience permission to use many restricted assets: 1. Navigate to the [Creator Dashboard](https://create.roblox.com/dashboard/creations). 2. Select one of your or your group's experiences. The experience's overview page displays. 3. In the experience's left-hand navigation, navigate to the **Configure** section, then select **Permissions**. The experience's **Permissions** page displays. 4. In the **Enter asset IDs** input, type every asset ID you want the experience to have access to separated by commas, then click the **Add** button. All restricted assets the experience has access to display beneath the input with the asset's name, asset ID, owner, and asset type. 5. At the bottom of the page, click the **Save Changes** button. ## View permissions To view every restricted asset that your or your group's experiences have permission to use, review the experience's **Permissions** page on the Creator Dashboard. 1. Navigate to the [Creator Dashboard](https://create.roblox.com/dashboard/creations). 2. Select one of your or your group's experiences. The experience's overview page displays. 3. In the experience's left-hand navigation, navigate to the **Configure** section, then select **Permissions**. The experience's **Permissions** page displays. 4. **(Optional)** Click the download arrow button to export the data for offline processing. ## Revoke permissions > **Warning:** Removing a creator as a friend does not automatically revoke their permission to use a restricted asset. Revoking a collaborator's permission prevents them from granting new experiences access to the asset. It does not affect experiences that already use the asset — those experiences retain access permanently. To revoke permission for a creator to use a restricted asset in any additional experience: 1. Navigate to the [Creator Dashboard](https://create.roblox.com/dashboard/creations). 2. In the upper tab bar, select **Development Items**, then click one of the following tabs: - **Models & Packages** - **Audio** - **Decals** - **Images** - **Videos** - **Meshes** - **MeshParts** - **Animations** 3. Select the asset you want to revoke permission for one or more creators to use in their own experiences. The asset's **Configure** page displays. 4. In the asset's left-hand navigation, select **Permissions**. The asset's **Permissions** page displays. 5. From the **Collaborators** tab, locate the creator you want to revoke permission from, then click the Access dropdown next to their username. Additional options display. 6. Click the **Remove** button. A pop-up displays to confirm that you want to revoke permission from the collaborator. 7. From the pop-up, click the **Remove** button. ## Open permissions > **Error:** Opening an asset's permissions is irreversible. Once you set an asset to Open Use, it **cannot** be reset to Restricted. > **Info:** To set multiple assets to **Open Use** in bulk, use the Revamped Asset Manager. See [Revamped Asset Manager](https://devforum.roblox.com/t/beta-updates-to-revamped-asset-manager/4548832/1) for details. With **Asset Privacy** enabled, you can still open the permissions of any Image, Decal, or Mesh so that any creator or experience can freely use it. To open permissions for your or your group's Image, Decal, or Mesh: 1. Enable Asset Privacy as described in [Restrict permissions](#restrict-permissions). 2. Back in the [Creator Dashboard](https://create.roblox.com/dashboard/creations), navigate to the upper tab bar, select **Development Items**, then click either **Decals**, **Images**, or **Meshes**. All of your Decal, Image, or Mesh assets display. > **Warning:** To set a Decal to Open Use, set its Image dependency to Open Use **first**. The Asset Privacy system blocks the change if the Image is still Restricted. 3. Select the asset you want every creator and experience to freely use. The asset's **Configure** page displays. 4. In the **Asset Access** section, enable **Open Use**. A pop-up displays asking you to confirm this permanent change. 5. In the pop-up, click the **Make Open Use** button. Anyone on Roblox can now use your asset. --- title: "Toolbox" url: /docs/en-us/projects/assets/toolbox last_updated: 2026-06-29T19:34:09Z description: "The Toolbox contains a selection of assets made by Roblox or Roblox community members." --- # Toolbox The **Toolbox**, accessible from Studio's **Window** menu or **Home** tab toolbar, contains a selection of [models](/docs/en-us/parts/models.md), [images](/docs/en-us/parts/textures-decals.md), [meshes](/docs/en-us/parts/meshes.md), [audio](/docs/en-us/audio/assets.md), [plugins](/docs/en-us/studio/plugins.md), [videos](/docs/en-us/ui/video-frames.md), and fonts made by Roblox or Roblox community members. It also includes all of the creations that you've personally published or those which were published by [groups](/docs/en-us/projects/groups.md) you belong to. ![Toolbox highlighted in Studio's toolbar.](../../assets/studio/general/Toolbar-Toolbox.png) The toolbox contains four distinct sections: #### Creator Store ![A close up view of the Toolbox with the Creator Store tab highlighted.](../../assets/studio/toolbox/Creator-Store-Tab.png) The **Creator Store** section contains models, decals, meshes, audio, plugins, videos, and fonts made by Roblox or Roblox community members. See [Creator Store](/docs/en-us/production/creator-store.md) for more information. #### Inventory ![A close up view of the Toolbox with the Inventory tab highlighted.](../../assets/studio/toolbox/Inventory-Tab.png) The **Inventory** section contains models, decals, meshes, audio, packages, videos, plugins, animations, and fonts that you've personally published, those which were published by [groups](/docs/en-us/projects/groups.md) you belong to, or those taken from the [Creator Store](/docs/en-us/production/creator-store.md). #### Recent ![A close up view of the Toolbox with the Recent tab highlighted.](../../assets/studio/toolbox/Recent-Tab.png) The **Recent** tab is similar to **Inventory** except that filters your recently used models, decals, meshes, audio, videos, and animations. #### Creations ![A close up view of the Toolbox with the Creations tab highlighted.](../../assets/studio/toolbox/Creations-Tab.png) The **Creations** tab is similar to **Inventory**, with the important distinction that it filters models, decals, meshes, audio, plugins, and animations that you've personally published or those which were published by [groups](/docs/en-us/projects/groups.md) you belong to. You can [configure](#asset-configuration) all assets in this section directly within Studio. ## Find and use assets With millions of assets available, it's helpful to narrow the search results to find exactly what you are looking for. To browse and use assets: 1. In the top-left corner, click the category selector dropdown and choose an asset category. Optionally click the **advanced filter** button to filter results by [verified creator](/docs/en-us/production/publishing/account-verification.md) status, a specific creator, the sale price for [plugins](/docs/en-us/studio/plugins.md), and file length for [audio assets](/docs/en-us/audio/assets.md).![The Toolbox window with the category selector highlighted](../../assets/studio/toolbox/Creator-Store-Category-Selector.png)![The Toolbox window with the advanced filter highlighted](../../assets/studio/toolbox/Creator-Store-Advanced-Filter.png) 2. In the search field, type what you want to find and press `Enter`. The view curates a selection of assets according to your query. 3. **OPTIONAL** Hover over an asset thumbnail and click the magnifying glass icon to closely inspect the asset's community rating, description, and more.![A preview view of an asset with the inspect icon highlighted.](../../assets/studio/toolbox/Asset-Inspect-Icon.png) 4. To add an asset to an experience: - If the asset is a [model](/docs/en-us/parts/models.md), [mesh](/docs/en-us/parts/meshes.md), [decal](/docs/en-us/parts/textures-decals.md), [video](/docs/en-us/ui/video-frames.md), or [audio](/docs/en-us/audio/assets.md) asset, click it or drag‑and‑drop it into the 3D viewport. It then inserts into the [Explorer](/docs/en-us/studio/explorer.md) hierarchy, and assets with 3D content display in the viewport. - If the asset is a [plugin](/docs/en-us/studio/plugins.md), click it and use the **Install** button to add it to the mezzanine's **Plugins** tab. - If the asset is a font, click it and use the **Install** button to add it to your font library. It then becomes available for user interface elements such as [text labels](/docs/en-us/ui/labels.md) or [buttons](/docs/en-us/ui/buttons.md). > **Info:** Some assets include scripts that perform specific actions, such as animating at runtime or triggering a sound. If you want to use an asset without allowing any of its scripts to run, right‑click the object in the [Explorer](/docs/en-us/studio/explorer.md) window and select **Disable Scripts** from the context menu. ## Asset configuration Right-clicking an asset and selecting **Edit Asset** opens the **Asset Configuration** window. In this window, you can edit the asset's details or restore it to a previous version. > **Info:** Asset configuration is only possible for assets you've personally published or those which were published by [groups](/docs/en-us/projects/groups.md) you belong to. The **Creations** tab is particularly useful for filtering assets in this way. ## Troubleshooting The toolbox runs through a web view. If you're experiencing any of the following issues, run the associated troubleshooting steps. ** General Issues** > **Warning:** Make sure there are no active instances of Roblox Studio running before running these steps. | Windows | | --- | |
  1. [Reinstall](https://developer.microsoft.com/en-us/microsoft-edge/webview2/) your Windows WebView2 installation.
  2. Delete the WebView2 data folder found at `%LOCALAPPDATA%\Roblox\RobloxStudio\WebView2`. Studio will automatically recreate this folder when it opens.
| | Mac | | Delete the WebKit data folders found at `~/Library/WebKit/com.Roblox.RobloxStudio` and `~/Library/Caches/com.Roblox.RobloxStudio/WebKit`. Studio will automatically recreate these folders when it opens. | ** Audio Not Working** | Windows | | --- | |
  1. Right click the speaker icon in the taskbar.
  2. Click **Open Volume Mixer**.
  3. Make sure that **Microsoft Edge WebView2** is not muted.
|
--- title: "Client-server runtime" url: /docs/en-us/projects/client-server last_updated: 2026-06-29T19:34:06Z description: "An overview of the client-server model in Roblox." --- # Client-server runtime ## Server Roblox experiences are multiplayer by default and run in a client-server model. The Roblox **server** is the ultimate authority for maintaining the experience's state, and is responsible for keeping all connected clients in sync with the server. ![A server grouping with connections to three client devices.](../assets/scripting/client-server/Client-Server-Model.png)_Server with connections to three client devices_ ## Client When an experience runs, Roblox copies a version of the "edit" data model that you built and published from Studio and runs it on Roblox servers as the "runtime" data model. Connected clients also receive a copy of the runtime data model and any initialization of the player occurs, such as initializing a player's backpack (inventory) or local user interface. When an experience has `Class.Workspace.StreamingEnabled` set to true, the server initially only sends a subset of content under `Class.Workspace` that is closest to the client. The client then renders the 3D world and begins running any applicable scripts. ![A diagram that maps objects between 'edit' and 'runtime' data models.](../assets/scripting/client-server/Data-Model-Mapping.png)__ ## Replication The server constantly updates connected clients, keeping everything in sync across the server and clients through a process called **replication**, which synchronizes the data model, physics simulation, and chat messages. Replication logic exists on both the client and server to ensure synchronization. #### Data Data model changes can occur in a variety of cases, such as when something in the 3D world is created or a property of the 3D world changes. This typically occurs when a script on the server or client makes a change that needs to be reflected on the other side of the client-server boundary. The following diagrams show common scenarios for data replication. **Client → Server**![A diagram of one client communicating with the server.](../assets/scripting/client-server/Remote-Flow-Client-Server.png)_Communication from any client to the server. For example, a client presses the `P` key to drink an invisibility potion, and tells the server to make that player's character invisible to all other players._ **Server → Client**![A diagram of the server communicating with one client.](../assets/scripting/client-server/Remote-Flow-Server-Client.png)_Communication from the server to one specific client. For example, a player joins the experience and the server populates that player's inventory with a set of items._ **Server → All Clients**![A diagram of the server communicating with all connected clients.](../assets/scripting/client-server/Remote-Flow-Server-All-Clients.png)_Communication between the server and all connected clients. For example, displaying a countdown timer to all participants in a race._ #### Physics Roblox uses a rigid body physics engine, which is responsible for calculating the movement and interactions of parts in the 3D world. By default, all parts in Roblox are rigid bodies and participate in simulated physics, unless otherwise specified. You can also group multiple parts together into assemblies, which the physics engine treats as a single rigid body. ![A single block part in the shape of a cube that represents a single assembly.](../assets/physics/assemblies/Assembly-Example-Block.png)_1 assembly; 1 part_ ![A player character that represents a single assembly of 18 individual parts.](../assets/physics/assemblies/Assembly-Example-Avatar.png)_1 assembly; 18 parts_ ![A pirate ship mesh that represents a single assembly of 179 individual parts.](../assets/physics/assemblies/Assembly-Example-Ship.png)_1 assembly; 179 parts_ Roblox replicates physics simulation data between the server and clients when necessary. To assist with simulation performance, Roblox can assign ownership of assemblies to a specific client or server. This means that the client or server can be responsible for simulating the physics of that assembly. Other clients receive updates about the assembly's position and movement from the owning client or server. Ownership typically happens automatically, but you can assign it directly for fine-tuned responsiveness. _Part ownership indicated through colored outlines_ #### Chat Roblox replicates chat messages between the server and client. The server is responsible for filtering chat messages and deciding which messages to replicate to other clients. For example, the server may filter out messages that contain profanity or are too long. > **Success:** Most players on Roblox experience between 100–300 milliseconds of network latency. Roblox Studio playtesting runs with a default of no latency, but you can change **Network Simulation** settings in the **Network** section of [Studio Settings](/docs/en-us/studio/setup.md#customization) (`Alt``S` on Windows; `⌥``S` on Mac). Use a minimum delay of 50–150 milliseconds in both the inbound and outbound directions to better simulate how network latency will affect your experience. --- title: "Collaboration" url: /docs/en-us/projects/collaboration last_updated: 2026-06-29T19:34:06Z description: "Studio's built-in collaboration tools let creators contribute independently, or work together with a team." --- # Collaboration Creating a high-quality experience demands many skills such as modeling, scripting, user interface design, and audio production. It's unlikely that one person has all of these skills, which makes collaboration between different roles an essential part of the development workflow. With Studio's **built-in** **collaboration tools**, creators can contribute to experiences independently on their own time, or work together with their team all at the same time. ![Three creators working together in a collaborative session, each with different color markers to demonstrate what objects they're currently editing in the environment.](../assets/studio/collaboration/Collaborative-Session.jpg) ## Age-based collaboration requirements > **Warning:** This requirement only applies if you add collaborators to your game using Studio's collaboration tools through Team Create. You can still use Team Create by yourself without completing an age check. **To use Studio's collaboration tools with other creators, you must first complete an age check** by either [verifying your ID](/docs/en-us/projects/../production/publishing/account-verification.md#verify-through-government-id) or [completing a facial age estimation](https://en.help.roblox.com/hc/en-us/articles/42295385001236-How-does-Roblox-estimate-my-age). Group or game owners must complete an age check to enable the rest of their collaborators to join their games. After completing an age check, you can only collaborate with creators in compatible age groups by default. For example, a 16-year-old creator can collaborate with a creator who is 17 and another who is 21. A creator who is under 13, however, must be a trusted friend of older collaborators or have parental permission to collaborate with creators who are older than 18. | Your age group | Can collaborate with | Cannot collaborate with | | --- | --- | --- | | Under 9 | Users under 13 | Users 13 and older | | 9–12 | Users under 16 | Users 16 and older | | 13–15 | Users aged 9–17 | Users under 9 and 18+ | | 16–17 | Users 13 and older | Users under 13 | | 18+ | Users 16 and older | Users under 16 | **To work with creators outside of your age group, you must meet at least one of the following requirements the first time you join a Team Create session:** - Become a [trusted friend](https://en.help.roblox.com/hc/en-us/articles/46158344285204-How-do-I-add-trusted-friends) with every collaborator who has already accessed your game. - You can send trusted friend requests to multiple people at once directly from Studio. Trusted friend requests from users under 13 require parental approval. In some regions, this requirement also applies to older teenagers. - To see which collaborators you need to become trusted friends with to unblock yourself and which creators are currently blocked by you, go to your game in Creator Hub and then go to **Safety** > **Collaborators**. - Receive permission from a [linked parent account](https://en.help.roblox.com/hc/en-us/articles/30428321333140-Parents-How-to-Link-Your-Child-s-Account) to collaborate with creators of all age groups through the [collaboration parental controls](https://www.roblox.com/my/account#!/parental-controls). Parents can use these controls to choose which age groups they are comfortable allowing their child to collaborate with. - Creators under 16 can request that a parent update this setting. Linked parents can update this setting directly from their parental controls. - This setting remains in effect until a parent changes it. Creators do **not** need to request permission every time they use Team Create. - Permission to collaborate with all age groups does **not** guarantee access to every collaboration session. For example, if another collaborator under 16 who accessed the game before you has their own parental permissions restricted to similar age groups or trusted friends, you won't be able to collaborate with them unless they add you as a trusted friend. After a creator meets one of these requirements and successfully joins a Team Create session, they retain access unless they lose trusted friend status with a required collaborator or a parent revokes their Studio collaboration permission. Collaborators who can't join Team Create because of age-based collaboration requirements still have **Edit** permissions. They can: - Use **Save As** to publish a copy of the game to Roblox. - Use Open Cloud APIs. - Manage the game's metadata in Creator Hub. For more information, see the [Team Create age requirements FAQ](https://devforum.roblox.com/t/age-requirements-for-team-create-in-studio/4539725#p-14194111-faqs-8). ## Manage collaborators Collaborators you add to an experience have permission settings that correspond to their level of access to the experience. As follows are the different user permission settings: | Permission | Description | | --- | --- | | **Owner** | User is the owner of the experience and has permission to configure other users' permissions. | | **Edit** | User has permission to edit the experience. This also grants the user **Play** permission. | | **Play** | User has permission to play the experience privately. | | **No Access** | User does not have either **Edit** or **Play** permissions. | There are some small differences when managing collaborators in [group‑owned experiences](#group-owned-experiences) vs. [user‑owned experiences](#user-owned-experiences). ### Group-owned experiences For [group](/docs/en-us/projects/groups.md) experiences, only the group owner or members with sufficient permissions can manage the group's roles, either across **all group experiences** or on a **per‑experience** basis. Such users can also add individual collaborators to group‑owned experiences in the same workflow as [user‑owned](#user-owned-experiences) experiences, but only for **Play** access. #### All group experiences If you're the group owner or a member with sufficient permissions, you can configure collaboration across **all** group experiences, for example grant **Edit** permission to an "Audio Artist" group role so they can fine-tune audio playback across multiple group experiences. 1. From the [Creator Dashboard](https://create.roblox.com/dashboard/creations), expand the account switcher in the upper‑left and select the group. 2. Expand the account switcher again, select **Settings** under the group's name, then select **Roles**. 3. Enable [Edit all group experiences](/docs/en-us/projects/groups.md#roles-and-permissions) for roles that should have editing permission. Remember to click **Save Changes** for each role that you change. In Studio's **Manage Collaborators** window for any group-owned experience, eligible roles display **Edit** permission but are muted to indicate that you can't change the permission level from Studio.![Studio's mezzanine bar with the Collaborate button highlighted.](../assets/studio/general/Toolbar-Manage-Collaborators.png)![Manage Collaborators window showing permission level selection for each group role.](../assets/studio/collaboration/Edit-Permission-All-Group-Experiences.png) #### Per-experience If you're the group's owner, you can grant permissions on a per-experience basis to roles that do not have those permissions across all group experiences. For example, you can temporarily grant **Edit** permission to an "FX Artist" group role so they can fine-tune visual effects before an experience's public release. 1. In Studio's **Manage Collaborators** window for any group-owned experience, select **Edit** from the permissions dropdown for the desired roles. Remember that you can only modify roles that do **not** already have edit permission across all group experiences.![Studio's mezzanine bar with the Collaborate button highlighted.](../assets/studio/general/Toolbar-Manage-Collaborators.png)![Manage Collaborators window showing permission level selection for each group role.](../assets/studio/collaboration/Edit-Permission-Per-Experience.png) 2. Click **Save** for your collaboration settings to take effect. All group members within the modified roles should now have **Edit** permission for this experience only; this action won't grant them equal permission to other group experiences. ### User-owned experiences For user-owned experiences, you can grant **Play** access to any user or [group](/docs/en-us/projects/groups.md), but you can only grant **Edit** permission to Roblox friends. To give **Edit** permission to a friend for an experience that you own: 1. With the experience open in Studio, click the **Collaborate** button on the right side of the mezzanine bar.![Studio's mezzanine bar with the Collaborate button highlighted.](../assets/studio/general/Toolbar-Collaborate.png) 2. Type into the search bar at the top to search for a collaborator to add. A dropdown appears listing matching collaborators, with friends indicated by the **Friend** label. Select the collaborator to add.![Manage Collaborators window showing search query for a Roblox friend.](../assets/studio/collaboration/Collaborator-Search.png) 3. Select **Edit** from the permissions dropdown for the friend, then click **Save** for your collaboration settings to take effect. ## Access a session Those who have permission to edit an experience can join a collaborative session as follows: 1. Navigate to the [Creator Dashboard](https://create.roblox.com/dashboard/creations). 2. Locate the experience depending on whether it's [group-owned](#group-owned-experiences) or [user-owned](#user-owned-experiences). #### Group-Owned Experience 1. Select the group from the upper‑left selector menu. 2. Make sure **Creations** is selected on the left and **My Experiences** in the main panel.**My Experiences****Shared With Me**#### User-Owned Experience 1. Select your personal account from the upper‑left selector menu. 2. Make sure **Creations** is selected on the left and **Shared With Me** in the main panel.**My Experiences****Shared With Me** 3. Hover over the experience's tile and click the **Edit in Studio** button to collaborate.![A close up view of an experience tile with the Edit in Studio button highlighted.](../assets/creator-dashboard/Edit-Button-Experience.png) ### View collaborators While working in a collaborative session, you can see the current collaborators in the upper-right corner of Studio, each with a unique assigned color that's consistent across all collaborators' devices. ![Studio's menu bar with icons of the current collaborators highlighted.](../assets/studio/general/Toolbar-Current-Collaborators.png) To view more details on the current collaborators, click on any of the icons to open the **Live Collaborators** window. In this window, you can see whether a user is active or inactive inside Studio, as well as an indication of where the user is working. Users become inactive if they do not use Studio for more than 5 minutes. ![Live Collaborators window with icons of current collaborators, as well as their activity status.](../assets/studio/collaboration/Live-Collaborators-Status.png) ### Selection visualization By default, selected code in the [Script Editor](/docs/en-us/studio/script-editor.md) and selected objects in the 3D viewport are highlighted with the unique color assigned to each collaborator. Additionally, the [Explorer](/docs/en-us/studio/explorer.md) window marks selected objects with dots in these assigned colors to indicate selection by other collaborators. ![A viewport view of a block part pyramid. The parts near the top of the pyramid are highlighted in green to signify that the collaborator with the green color has the parts selected in their Studio instance.](../assets/studio/collaboration/User-Color-Parts.jpg) ![A close up view of the three block part objects in the Explorer window. There is a green circle near the objects to signify that the collaborator with the green color has the parts selected in their Studio instance.](../assets/studio/collaboration/User-Color-Explorer.png) To make all collaborators' selections invisible to only you while still seeing their work, uncheck **Show collaborator selections** at the bottom of the [Live Collaborators](#view-collaborators) window. ### Join collaborators To quickly jump to a location in the workspace or to the exact line in a script that a collaborator is editing, hover over their name in the [Live Collaborators](#view-collaborators) window and click **Join**. ## Comments Roblox Studio has a **comments** feature that lets you pinpoint an object in the 3D viewport and start a conversation with your collaborators, leave to-do notes, and more. Comments update in real time which lets you and your team receive and respond to feedback without disrupting your workflow in Studio. You can also receive personalized [notifications](#notifications) to help you stay on top of feedback, even when you're not using Studio. ### Workflow To create a comment, click the **Comment** button on the right side of Studio's mezzanine (keyboard shortcut `C`). ![Studio's mezzanine bar with the Comments button highlighted.](../assets/studio/general/Toolbar-Comments.png) In the viewport, your cursor turns blue. Click on any object that inherits from `Class.BasePart` (most do, including `Class.Terrain`), type your comment, and click **Submit**. ![Blue comment cursor in the viewport.](../assets/studio/collaboration/Comments-Cursor.jpg) > **Info:** To **tag** a collaborator in your comment, use the `@username` syntax. You can tag any collaborator in the group for a [group‑owned](#group-owned-experiences) experience or any user with **Play** or **Edit** permissions in a [user‑owned](#user-owned-experiences) experience. Comments appear in the **Comments** window, accessible from Studio's **Window** ⟩ **Collaboration** menu, with the most recent comments at the top. From within the **Comments** window, you can: - Single-click on a comment to show the full conversation in the 3D viewport. - Double-click on a comment to zoom in on it in the viewport. - Use the **⋯** menu in the upper-right corner to show resolved comments, filter for only the comments that you've been tagged in, or hide comments in the viewport. - Resolve comments as you and your team address them. After you resolve a comment, it disappears from both the viewport and the main list, but it isn't completely gone; you can always un‑resolve it later. ### Notifications To help you stay on top of feedback even when you're not using Studio, you'll receive an **email digest** that recaps all recent activity. Each email digest provides a simple overview of comment activity per place, including: - When someone mentions (tags) you in a comment, for example `@username`. - When someone comments in a thread that you previously replied to or were mentioned in. - When someone resolves a thread that you previously replied to or were mentioned in. From within the email overview, simply click **View in Studio** to go directly to the conversation in Roblox Studio. > **Info:** You're always in control of both the type and source of comments which make up email digests. Mentions and reply notifications are on by default, while resolution notifications are off. You can also unsubscribe from a specific place or thread if you don't need updates. ## Collaborative scripting In a collaborative session, you can code together in real time through [live scripting](#live-scripting), or you can [draft](#drafts-mode) scripts in a more focused environment before committing them to a collaborator-shared repository. ### Live scripting **Live Scripting** lets collaborators code together in real time. In the [Script Editor](/docs/en-us/studio/script-editor.md), each collaborator's cursor color matches their assigned color in the [Live Collaborators](#view-collaborators) window. ![Script Editor window showing cursors for two collaborators, colored according to their assigned color in the Live Collaborators window.](../assets/studio/collaboration/Live-Scripting-Cursors.png) ![Live Collaborators window with icons of current collaborators, as well as their activity status.](../assets/studio/collaboration/Live-Collaborators-Status.png) While live scripting, edits are auto‑saved every 5 minutes just like place edits, and a collaborator can manually save a script at any time with `Ctrl``S` (`⌘``S`). Saved or auto‑saved versions are logged in the [Script History](#view-script-history) window. > **Info:** Live Scripting is **enabled** by default. If you and your team prefer to collaborate on scripts in an environment similar to source control, explore [Drafts](#drafts-mode) mode. ### Drafts mode Through **Drafts** mode, you can independently edit and test scripts without affecting the experience for others. After you finish drafting a script, you can [commit](#commit-drafts) it to the shared repository and use [collaborative testing](/docs/en-us/studio/testing-modes.md#collaborative-testing) to test the committed version with others. > **Warning:** Drafts Mode is **disabled** by default. To enable it, open Studio's **File** ⟩ **Experience Settings** window, select the **Other** tab, and turn on **Enable Drafts Mode**. > > Note that all collaborators will need to exit the session for the change to take effect. Alternatively, you can [disable collaboration](#disable-collaboration) and then reenable it to restart the session. #### Commit drafts Once you've edited a script, it appears in the **Drafts** window, a non‑default window which you must add to a [custom tab](/docs/en-us/studio/ui-overview.md#custom-tabs) in Studio's toolbar. ![The Drafts window with two drafted script instances.](../assets/studio/collaboration/Drafts-Window.png) Drafts are saved to your local file system and persist between Studio sessions on the same machine. To commit your local edits to the repository, left-click a script, or hold `Shift` and left-click to select multiple scripts. Then click **Commit** to commit all selected scripts. ![The Drafts window with the Commit button highlighted.](../assets/studio/collaboration/Drafts-Commit.png) #### Compare and merge changes If another collaborator commits changes to the same script that you're editing, an icon with a green **⊕** symbol appears in the **Drafts** window. To view their changes, right‑click the script and select **Compare With Server**. ![The Drafts window with the green plus icon highlighted to the left of a changed script, and the right-click popup window showing with the Compare With Server option highlighted.](../assets/studio/collaboration/Drafts-Compare.png) In the **(Diff)** tab that opens in the [Script Editor](/docs/en-us/studio/script-editor.md), code that other collaborators changed or deleted appears in red, while code that you updated appears in green. ![A close up view of lines that were edited by different collaborators.](../assets/studio/collaboration/Drafts-Diff-Result.png) To merge their changes into your script: 1. In the **Drafts** window, right‑click the script and select **Merge From Server**.![The Drafts window right-click popup window showing with the Merge From Server option highlighted.](../assets/studio/collaboration/Drafts-Merge.png) 2. In the merge window, you can pick which code to keep, or make manual edits. - Check **Draft** to keep your changes, or leave it unchecked to discard them. - Check **Server** to merge the committed changes into your draft, or leave it unchecked to ignore them. - Check **Other** to manually edit the script and save the changes to your draft. 3. Once you've previewed the merge resolution, click **Merge All** to update your local script. #### Restore deleted scripts If a collaborator deletes a script that you're editing, an icon with a red **⊘** symbol appears in the **Drafts** window. To restore the script, right‑click it and select **Restore Script**. Scripts are restored to the place's **Workspace** tree, so you may need to manually re‑parent them back to their original location. ![The Drafts window with the red cross icon highlighted to the left of a deleted script, and the right-click popup window showing with the Restore Script option highlighted.](../assets/studio/collaboration/Drafts-Restore.png) ### View script history All script changes, whether saved by a collaborator, auto-saved, or committed by a collaborator through [Drafts](#drafts-mode) mode, are logged in the **Version History** window. To access it: 1. Right-click the script in the [Explorer](/docs/en-us/studio/explorer.md) window and select **View Script History**. 2. In the **Version History** window that opens, you'll see all committed versions of the script, the commit date, which collaborator committed, and more. From this window, the following actions are possible: #### Compare With Previous To compare any version (except the oldest) with its previous version, select it and click **Compare With Previous Version**. In the **(Diff)** tab that opens in the [Script Editor](/docs/en-us/studio/script-editor.md), code from the newer version appears in green while code from the older version appears in red. #### Compare Selected To compare any **two** versions, hold `Ctrl` or `⌘` and select both, then click **Compare Selected Versions**. In the **(Diff)** tab that opens in the [Script Editor](/docs/en-us/studio/script-editor.md), code from the newer version appears in green while code from the older version appears in red. #### Open If the version **Comment** indicates just one script was committed, select it and click **Open Script** to open it in the [Script Editor](/docs/en-us/studio/script-editor.md). If the version **Comment** indicates more than one script was committed (commonly the result of an auto‑save on multiple unsaved scripts), you can click **Show** within the version's row to open a popup displaying the script(s) and their respective version. Then, from the version history window, click **Open All Scripts From Batch** to open them in the [Script Editor](/docs/en-us/studio/script-editor.md). ## Save and publish During a collaborative session, Studio automatically saves the project to the cloud every four minutes. ## Revert to previous versions The owner of an experience can revert changes made by other editors. See [here](/docs/en-us/projects/configure-games.md#access-version-history) for instructions. > **Error:** Be careful when reverting changes. If anyone is currently editing the experience in a collaborative session, their changes may still auto‑save and overwrite the revert action. To ensure that nobody else is editing the place when you revert, [disable collaboration](#disable-collaboration). You might also want to check [Activity History](/docs/en-us/projects/activity-history.md). This view provides a chronological event log that improves team visibility into key experience settings. ## Disable collaboration **Team Create** is the core Studio feature that enables collaboration. Workflows that involve the [Manage Collaborators](#manage-collaborators) dialog will automatically enable the feature, but you can manually disable it if necessary. 1. If the [Live Collaborators](#view-collaborators) window isn't already open, click on any of the collaborator icons to open it. 2. In the bottom-right corner of the window, click the **⋯** button and select **Disable Team Create**. 3. When prompted, confirm ending the session to reload the place in a non‑collaborative state. --- title: "Configure games and places" url: /docs/en-us/projects/configure-games last_updated: 2026-06-29T19:34:06Z description: "Explains how to configure games." --- # Configure games and places You can configure most game and place-level settings from the [Creator Dashboard](https://create.roblox.com/dashboard/creations), a hub that lets you manage and access all of your Roblox creations in one place. It's useful to explore the left-hand navigation to see every configuration possible, such as those for [localization](/docs/en-us/production/localization.md), [analytics](/docs/en-us/production/analytics.md), and [monetization](/docs/en-us/production/monetization.md), but the following sections highlight notable workflows for most games. ## Generate content descriptors The **Maturity & Compliance Questionnaire** contains a set of questions about the type of content players can possibly encounter within your game, as well as how frequently it occurs. Your answers give Roblox an understanding of the content in your game and ensure that the game is available to the appropriate audience on the [Home](https://www.roblox.com/home) and [Discover](https://www.roblox.com/discover) pages according to each player's age group and regional content policies. For step-by-step instructions on how to fill out the questionnaire, see [content maturity & compliance](/docs/en-us/production/promotion/content-maturity.md). ## Set age and geography restrictions You can set restrictions so that only players over a certain age or from specific regions can play and discover your games. This is particularly helpful when you want to age up your audience without including mature content to age gate through content maturity labels. To set age and geography restrictions: 1. Navigate to the [Creator Dashboard](https://create.roblox.com/dashboard/creations). 2. Click on the thumbnail of the game for which you want to set age and geography restrictions. The game's **Overview** page displays. 3. In the left-hand navigation, navigate to **Audience** ⟩ **Access Settings**. 4. In the **Age** section, set **Minimum age** to the youngest age a player can have to access your game. 5. In the **Region** section, enable every region that can access your game. 6. At the bottom of the page, click the **Save Changes** button. ## Copy landing page link After you publish your game, Roblox generates a landing page for the game referred to as the game's **main details page**. You can access and copy this link from either the [Creator Dashboard](https://create.roblox.com/dashboard/creations) or the Roblox app, then share it with others so they can play your game. #### Creator Dashboard 1. Hover over a game's thumbnail, click the **⋯** button, and select **Copy URL**. 2. Share the copied URL with others as a direct link to the game's landing page featuring a **play** button. #### Roblox app 1. Open the Roblox app on your mobile device. 2. Locate the game, typically under the **Continue** header on the home screen, and tap its tile to open the info screen. 3. In the lower-left corner of the screen, click the **⋯** button and select **Share** to open your device's sharing options. ## Edit collaborator permissions When creating and collaborating on games as part of a [group](/docs/en-us/projects/groups.md), abilities granted to group members are dependent on their **role permissions**. Owners or other group members with sufficient permissions can adjust play/edit/publish access and more on a per‑game level, such as allowing a limited group role the ability to edit a specific game even if that role cannot edit all games. > **Info:** For step-by-step instructions on how to edit collaborator permissions, see [roles and permissions](/docs/en-us/projects/groups.md#roles-and-permissions). ## Access version history Roblox automatically retains saved versions of each place for version control and backup purposes. You can access **Version History** in the Creator Dashboard to revert to a previously saved version of any place within a game, or in Studio to leave notes attached to your saved place files. For more information about accessing Version History, saving place files with notes, and reverting to previous place versions, see [Version History](/docs/en-us/projects/version-history.md). ## Allow copying By default, all private and public games are locked so that you or your [group](/docs/en-us/projects/groups.md) are the only creators who have permission to edit its place files. However, if you want to allow other creators to be able to access an editable copy of your game for their own creation process, you can **uncopylock** the game. This is particularly helpful when you create templates or reference files for open use. To allow the community to access an editable copy of your game: 1. Navigate to the [Creator Dashboard](https://create.roblox.com/dashboard/creations). 2. Click on the thumbnail of the game you want to allow players to copy. The game's **Overview** page displays. 3. In the left-hand navigation, navigate to **Configure** ⟩ **Places**. 4. Click the **start place** marked with a star icon. The place's **Basic Settings** page displays.![Start place tile indicated in Places display on the Creator Dashboard](../assets/creator-dashboard/Places-Start-Place-Icon.png) 5. In the place's left-hand navigation menu, select **Permissions**. 6. Enable **Allow users to download a copy of this Place**. 7. Click the **Save Changes** button. Your game is now uncopylocked and creators can access an editable copy of it from the game's main page by clicking the **⋯** button and selecting **Edit in Studio**. ## Allow strong language You can enable strong language and relax the text chat filter in your games for older player audiences. Doing so also adds a tag above your game page to warn other players that your game may include strong language: ![Label indicating the game contains strong language](../assets/publishing/experiences-places-assets/Maturity-Label-Strong-Language.jpg) Regardless of whether or not you activate the toggle, players do not face moderation consequences just for using strong language in chat or voice in games with Restricted content maturity labels, but they cannot violate [Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410#safety) and [Terms of Use](https://www.roblox.com/info/terms). For example, players can type an expletive if they are scared by a monster, but they cannot abuse another player using strong language or otherwise. > **Warning:** In-game assets and game metadata cannot contain strong language, even if the toggle is on and your game has a Restricted content maturity label. If you include strong language in your assets or game page, your game will be moderated. To allow strong language between players in text chat within your game: 1. Navigate to the [Creator Dashboard](https://create.roblox.com/dashboard/creations) and click on the thumbnail of the game in which you want to allow strong language. The game's **Overview** page displays. 2. In the left-hand navigation, navigate to **Audience** ⟩ **Communication Settings**. 3. Enable the **Allow Strong Language** toggle, then press the **Save Changes** button. Players within your game can now use strong language in text chat. --- title: "Cross-platform development" url: /docs/en-us/projects/cross-platform last_updated: 2026-06-29T19:34:09Z description: "Consolidated overview of cross-platform recommendations to help you design a Roblox experience that's accessible and enjoyable on all platforms." --- # Cross-platform development Roblox is inherently **cross‑platform**, as players can discover and join experiences on their phone or tablet, then later continue where they left off on their PC or console. To support an expanding suite of platforms, you should build user interfaces that seamlessly adapt to the input type (touch, gamepad, mouse/keyboard), screen size, and device orientation. This guide, the [adaptive design](/docs/en-us/production/publishing/adaptive-design.md) guide, and the linked resources can help you design a true cross‑platform experience that's compatible, accessible, and enjoyable on **all** platforms. > **Info:** To explore a pre-made experience that showcases the following cross‑platform design concepts, check out the [Interactivity Showcase](https://www.roblox.com/games/132115384567250/). ## Input A carefully designed experience should support all primary input types, including mouse and keyboard, touch, and gamepads. ### Cross-platform actions > **Success:** Support all primary input types (mouse/keyboard, touch, and gamepads) using the [Input Action System](/docs/en-us/input/input-action-system.md). To simplify cross‑platform inputs, Roblox provides the [Input Action System](/docs/en-us/input/input-action-system.md) to define **actions** such as "jump," "sprint," or "shoot" and set up **bindings** for multiple hardware inputs to drive those actions. You do not need to think about all the technical aspects of hardware inputs and when to listen for specific input triggers; you simply define which inputs perform which actions. ### Input type detection > **Success:** Ensure that UI elements respond and adapt to the player's **primary** input type as detailed in [input type detection](/docs/en-us/input.md#input-type-detection). Also consider [assistive hints](#assistive-hints) to help players understand specific input changes. In cross-platform development, it's important that you determine and respond to the primary input type a player is using, normally to ensure that [UI elements](/docs/en-us/ui.md#ui-objects) like on‑screen buttons and menus work elegantly and support interaction across devices. For example, a touch‑enabled device assumes touch is the **default** input and that touch buttons may appear for actions, but a player may choose to connect a bluetooth gamepad. In this case, touch remains a valid input, but you can assume the player wants to switch to the connected gamepad as the **primary** input type and possibly use touch as a backup input for on‑screen UI. ### Assistive hints Based on the [primary input type](#input-type-detection), it's recommended that you include **assistive UI hints** whenever possible. For example, when selecting tools to equip, display input‑specific hints such as `1`–`5` when the player is using a keyboard, or gamepad trigger hints when the player is using a gamepad. #### Keyboard Input #### Gamepad Input The `Class.UserInputService:GetImageForKeyCode()|GetImageForKeyCode()` and `Class.UserInputService:GetStringForKeyCode()|GetStringForKeyCode()` methods are helpful in gathering a platform‑specific image or string to use in a UI hint, such as the following examples: | Key Code | Xbox Image | PlayStation Image | String | | --- | --- | --- | --- | | `Enum.KeyCode.ButtonA` | | | `ButtonA` | | `Enum.KeyCode.ButtonX` | | | `ButtonX` | | `Enum.KeyCode.One` | n/a | n/a | `1` | | `Enum.KeyCode.Five` | n/a | n/a | `5` | ## User interface A well-designed user interface is essential for players on all platforms to interact with your experience. ### Position and size > **Success:** Ensure that all UI elements are visible on all screen sizes and orientations. > **Success:** Ensure that all buttons/controls are interactable and that they are not blocked or obscured by [device cutouts or Roblox UI elements](/docs/en-us/ui/position-and-size.md#cross-platform-factors). All UI elements should be [positioned](/docs/en-us/ui/position-and-size.md#position) and [sized](/docs/en-us/ui/position-and-size.md#size) appropriately to ensure visibility and interactivity across multiple screen sizes. Positioning UI elements by `Datatype.UDim.Scale|Scale` is a common approach since it's based on a **percentage** of the screen's **X** and **Y** bounds, not a pixel value (pixels can vary arbitrarily by both number and density across devices). You can also utilize the element's `Class.GuiObject.AnchorPoint|AnchorPoint` to set its positional origin. For example, the UI container holding the gem/coin counters in the following image is positioned at an **X** scale of `1` (right) and **Y** scale of `0` (top) with an upper‑right anchor point. `Datatype.UDim.Scale|Scale` is also recommended for **sizing** UI elements, such as making a UI container span 75% width on every screen. However, 75% would render to a huge size on 4K TVs for console players, so it's recommended that you explore [screen size adaptation](#screen-size-adaptation) as a way to adapt UI layouts across different viewports. ### Screen size adaptation > **Success:** Adapt UI to various display sizes by checking and listening for changes to the viewport display size. With a multitude of possible screen sizes accessible to the Roblox platform, attempting to predict screen size by pixels often leads to misinterpretation. To assist in determining a player's screen size to adapt the size/position of UI elements, Roblox provides the read‑only `Class.GuiService.ViewportDisplaySize|ViewportDisplaySize` property which represents the internally‑categorized rendering size of the viewport. | `Class.GuiService.ViewportDisplaySize\|ViewportDisplaySize` | Summary | | --- | --- | | `Enum.DisplaySize.Small\|Small` | Most tablet/mobile/handheld devices | | `Enum.DisplaySize.Medium\|Medium` | Most laptops and monitors | | `Enum.DisplaySize.Large\|Large` | Most TVs or larger | For more native customization, you can use the UI [Style Editor](/docs/en-us/ui/styling/editor.md) to configure [Style Queries](/docs/en-us/ui/styling/editor.md#style-queries) for both `Class.GuiService.ViewportDisplaySize|ViewportDisplaySize` and `Class.UserInputService.PreferredInput|PreferredInput` values. Using intrinsic selectors like `@ViewportDisplaySizeSmall` or `@PreferredInputTouch` allows your UI to automatically swap tokens for text sizes, container dimensions, and other measurements as the player's device environment or primary input method changes. ### Layout containers > **Success:** Use layouts to organize individual UI components and ensure that containers adjust responsively to the children elements within them. For easier UI organization, you should use **layout components** to organize children. When using layout [containers](/docs/en-us/ui/frames.md), make sure that the children are legible, [accessible](#accessibility), and non‑cluttered. A common layout component is `Class.UIListLayout`, which positions sibling `Class.GuiObject|GuiObjects` into rows or columns and adjusts accordingly whenever you add or remove a sibling object. You should also consider the distribution and relative positioning of elements within layout containers for the best user experience. Integrating [flex](/docs/en-us/ui/list-flex-layouts.md#flex-layouts) into a list layout is a powerful way to [equally fill/distribute](/docs/en-us/ui/list-flex-layouts.md#equal-fill-or-distribution) or [align/stretch](/docs/en-us/ui/list-flex-layouts.md#alignment) items across their line, or [flex specific items](/docs/en-us/ui/list-flex-layouts.md#flex-individual-items) across a variable space. ![UIListLayouts illustrating FillDirection of either horizontal or vertical.](../assets/engine-api/classes/UIListLayout/FillDirection.png) ### Accessibility > **Success:** Ensure that all text is legible on all screens. Also make sure all UI has [sufficient contrast](/docs/en-us/production/publishing/accessibility.md#color-contrast) and that [nothing relies solely on color](/docs/en-us/production/publishing/accessibility.md#color-non-reliance) to distinguish an action or outcome. Even with UI elements [properly sized](#position-and-size) across all device screen sizes, the overall experience should meet [accessibility standards](/docs/en-us/production/publishing/accessibility.md) for players with impaired vision or color blindness. Text size can factor heavily into the legibility of text‑based UI, so you should include a [text size constraint](/docs/en-us/ui/size-modifiers.md#text-size) to ensure text doesn't become illegible (too small) or visually too large on larger screens like 4K TVs. For more tips on accessibility, see [here](/docs/en-us/production/publishing/accessibility.md). ## Final tips & tricks As you work toward a full cross‑platform experience, consider the following tips and tricks: - Device emulation is essential to designing an optimal experience on Roblox. Utilize the built‑in [Device Emulator](/docs/en-us/studio/testing-modes.md#device-emulation) and [Controller Emulator](/docs/en-us/studio/testing-modes.md#controller-emulation) to emulate various devices and gamepads directly in Studio. - Roblox's [UI styling](/docs/en-us/ui/styling.md) pipeline, similar to CSS, lets you declare and globally apply overrides to UI instance properties. Along with the integrated [Style Editor](/docs/en-us/ui/styling/editor.md), you can set up a complete and easy‑to‑modify user interface. - Once your experience is effectively cross-platform, explore native features like haptic feedback on [mobile](/docs/en-us/input/mobile.md#haptic-feedback) and [gamepads](/docs/en-us/input/gamepad.md#haptic-feedback), touch gestures such as `Class.UserInputService.TouchSwipe|TouchSwipe` and `Class.UserInputService.TouchPinch|TouchPinch`, or accelerometer and gyroscope functionality on `Class.UserInputService.AccelerometerEnabled|AccelerometerEnabled` and `Class.UserInputService.GyroscopeEnabled|GyroscopeEnabled` devices. --- title: "Data model" url: /docs/en-us/projects/data-model last_updated: 2026-06-29T19:34:06Z description: "Explains the hierarchy of objects that describe everything about a place." --- # Data model Every place is represented by a data model, a hierarchy of objects that describe everything about the place. The data model contains all objects that make up the 3D world, such as parts, terrain, lighting, and other environmental elements. It also contains objects that can control runtime behavior, such as scripts that modify properties, call methods and functions, and respond to events that enable dynamic behavior and interactivity. The Roblox Engine uses the data model as a source of truth for a place's state, so it can simulate and render it on client devices. For more information on how the Roblox Engine interprets the data model, see [Client-Server Runtime](/docs/en-us/projects/client-server.md). ## Objects You place and organize objects in the data model to describe a place in Roblox. Every object in Roblox inherits from the `Class.Instance` class, which defines generic properties, methods, and events that are common to all objects. Objects also define their own characteristics depending on the functionality the object provides. There are many categories of objects with a wide variety of uses, but you'll frequently work with `Class.BasePart` and `Class.LuaSourceContainer` objects, commonly referred to as parts and scripts. For a comprehensive list of all the features of the Roblox Engine, see the [reference documentation](/docs/en-us/reference/engine.md). ### 3D building blocks `Class.BasePart` is the core class for physically-simulated 3D building blocks in the world. It defines the properties and methods common to all physical objects with properties like position, size, and orientation. | Object | Description | | --- | --- | | `Class.Part` | A primitive [part](/docs/en-us/parts.md) that can take the shape of a block, ball, cylinder, wedge, or corner wedge. | | `Class.MeshPart` | An imported [mesh](/docs/en-us/parts/meshes.md) from 3D modeling software like Maya or Blender. | | `Class.TrussPart` | A truss beam that characters can climb like a ladder. | While you can theoretically create a fully functional Roblox experience using just simple parts, you'll most likely import [meshes](/docs/en-us/parts/meshes.md) and combine primitive parts into more complex objects and structures through [solid modeling](/docs/en-us/parts/solid-modeling.md). ### Scripts You can add interactivity and behavior to your place's 3D world and define logic with scripts. You write scripts in the Luau programming language to do things like moving parts, calling other scripts, and responding to events. Because Roblox works in a client-server model, you can run scripts on the server, client, or have them communicate across the network boundary. - A `Class.Script` object represents a script that can only run on the server. - A `Class.LocalScript` object represents a script that can only run on the client. - A `Class.ModuleScript` object represents a reusable script that you can `Global.LuaGlobals.require()` from both server and client scripts. For scripts to behave properly, you must place them in the correct containers in the data model. For more information, see the [Server](#server) and [Client](#client) sections. ## Object organization While you have a lot of flexibility in how you organize your data model, the Roblox Engine expects certain objects to be in certain **container services** which are objects that have specific behaviors and can affect the behaviors of the objects they contain. The main categories of container services include: - **Workspace** — `Class.Workspace` stores all objects that render in the 3D world. - **Environment** — Containers like `Class.Lighting` and `Class.SoundService` that contain objects for environmental settings and elements. - **Replication** — Containers for content and logic that replicates between the server and client, such as `Class.ReplicatedStorage` and `Class.ReplicatedFirst`. - **Server** — Containers for server-side only content and logic, such as `Class.ServerScriptService` and `Class.ServerStorage`. - **Client** — Containers for client-side content and logic, such as `Class.StarterPlayer` and `Class.StarterGui`. - **Chat** — Containers for objects that enable chat features, such as `Class.VoiceChatService` and `Class.TextChatService`. > **Info:** The Roblox Engine has many more services that provide built-in functionality that you can call within your scripts. The Roblox Engine doesn't give these services special treatment in the data model. For more information, see [services](/docs/en-us/scripting/services.md). In addition, you can further organize your objects with the following objects: - **Folders** — A `Class.Folder` is for organizational purposes and doesn't define any behavior. For example, you can use folders to group similar objects like a set of scripts in server storage. - **Models** — A `Class.Model` is mainly intended for geometric groupings of parts, such as grouping together a desk set that includes a chair, table, and a lamp. To organize more complex sets, you can even nest models within models. ### Workspace `Class.Workspace` contains all objects that make up a place's 3D world. You can add objects to the workspace to customize your 3D world, such as base parts, mesh parts, and models. Clients render everything that appears in this container and nothing outside of it, so you can control what users see and interact with in your place. Although not actually rendered, you can also add scripts that are parented to the parts and models that they are associated with. By default, the workspace is pre-populated with a `Class.Terrain` and `Class.Camera` object. #### Camera `Class.Camera` determines how the client views the 3D world. By default, there is one camera in the workspace, but you can add multiple camera objects for creating different perspectives and views. Every client takes these settings and creates its own camera view that the server can't directly modify. For example, you can set a camera to follow user movements or stay fixed in a particular location. You can also adjust the field of view, distance, and angle to create different visual effects of how users view your 3D world. For more information, see [customize the camera](/docs/en-us/workspace/camera.md). #### Terrain `Class.Terrain` lets you create landscapes for your place. You can apply a material to the terrain to simulate desired natural environments, such as grass, water, sand, or a custom material. Though you can only have one terrain object for your 3D world and apply one material to that terrain, you can use the [Terrain Editor](/docs/en-us/studio/terrain-editor.md) to edit regions of your terrain. For more information, see [environmental terrain](/docs/en-us/parts/terrain.md). ### Environment Custom lighting can make your 3D world much more immersive and realistic. The `Class.Lighting` service contains objects that control global lighting settings of your place, such as `Class.Atmosphere` for simulating atmospheric effects or `Class.Sky` to alter the sun, moon, and stars in your environments. In addition, you can use [light sources](/docs/en-us/effects/light-sources.md) to emit light from specific objects. Adding [audio](/docs/en-us/audio.md) to your experiences is also crucial for elevating your experiences to new heights. By strategically using positional and non-positional audio, you can immerse players into your worlds, provide them useful feedback for their actions, and direct their attention to what they need to do to be successful in their objectives. ### Replication **Replication** is the process of the server synchronizing the state of your place with all connected clients. The Roblox Engine intelligently and automatically replicates data, physics, and chat messages between the server and client for many cases, but you can also specify certain objects to replicate by placing them in specific containers. #### ReplicatedFirst `Class.ReplicatedFirst` contains objects that you want to replicate to a client when it joins your place. It typically contains objects that are essential to initialize a player, such as client-side `Class.LocalScript` objects and the objects associated with the scripts. All content in this container is replicated from the server to the client only once. #### ReplicatedStorage `Class.ReplicatedStorage` contains objects that are available to both the server and connected clients. Any changes that occur on the client persist but won't be replicated to the server. The server can overwrite changes on the client to maintain consistency. This container is typically used for `Class.ModuleScript` objects that need to be shared and accessed by both `Class.Script` (server-side) and `Class.LocalScript` (client-side) objects. In addition, you can use this container to replicate any objects that don't need to exist in the 3D world until needed, such as a `Class.ParticleEmitter` for cloning and parenting to all incoming character models. For more information on how replication works, see [client-server runtime](/docs/en-us/projects/client-server.md). ### Server The data model defines dedicated containers for server-side only objects that are never replicated to the client. This allows the server to affect client behavior and state without exposing the server's objects and logic to the client. > **Info:** You can use remote events and functions to allow client-side communication with the server. #### ServerScriptService `Class.ServerScriptService` contains `Class.Script` objects, `Class.ModuleScript` objects that are required by server scripts, and other scripting-related objects that are only meant for server use. If your scripts require other, non-scripting objects, you must place them in `Class.ServerStorage`. Scripts in this container are never replicated to clients, which allows you to have secure, server-side logic. #### ServerStorage `Class.ServerStorage` contains objects that are only meant for server use. You can use this container to store objects that you want to clone and parent to the workspace or other containers at runtime. For example, you can store large objects such as maps in this container until they are needed and move them into the workspace only when required. This lets you decrease client network traffic on initial join. > **Info:** Scripts don't run when they are parented to this container, but scripts inside `Class.ServerScriptService` can access and run `Class.ModuleScript` objects in this container. ### Client The client container services are meant for objects that are replicated to every connected client. This category of containers replicate to every connected client and typically contain 3D objects and associated `Class.LocalScript` objects. All objects you store in these containers are non-persistent across sessions and reset every time a client rejoins. You can put objects in these containers such as player GUIs, client-side scripts, and other objects that are only relevant to the client. When a client connects to a server, the `Class.Players` container service listens for users joining your place and creates a `Class.Player` object for every client. The server copies the objects from the client containers in the edit data model to the corresponding location in the runtime data model inside the `Class.Players` object. The following table describes the original container it resides on in the container and the resulting container they are copied to on the client: | Edit data model | Runtime data model | Notes | | --- | --- | --- | | `Class.StarterPack` | `Player.Backpack` | Scripts that set up the player's inventory and generally contain `Class.Tool` objects but often contains local scripts as well. | | `Class.StarterGui` | `Player.PlayerGui` | Scripts that can manage the player's local GUI. When a player respawns, the contents of PlayerGui are emptied. The server copies the objects inside StarterGui into the PlayerGui. | | `Class.StarterPlayerScripts` | `Player.PlayerScripts` | General purpose scripts for the client. For example, if you want to create special effects on the client when certain conditions are met, you can place a local script in this container to do that. The server cannot access this container. | | `Class.StarterCharacterScripts` | `Player.Character` | Scripts that are copied to the client when they spawn. These scripts do not persist when the player respawns. | | `Class.ReplicatedFirst` | | The contents of this container are replicated to all clients (but not back to the server) first, before anything else. | > **Info:** For more information on edit and runtime data models, see [client-server runtime](/docs/en-us/projects/client-server.md#client). > **Warning:** You don't add `Class.Player` objects to the `Class.Players` container service explicitly. ### Chat #### TextChatService `Class.TextChatService` represents the service that handles various, in-experience text chat tasks, such as managing channels, decorating messages, filtering text, creating commands, and developing custom chats interfaces. For more information, see [text chat overview](/docs/en-us/chat/in-experience-text-chat.md). #### VoiceChatService `Class.VoiceChatService` represents the proximity-based voice chat feature that simulates realistic communication based on how close you are to other users. You can use this service to toggle the feature on and off. For more information, see [voice chat](/docs/en-us/chat/voice-chat.md). ## Folders and models There are two primary methods for grouping objects in the data model: **folders** and **models**. Both are containers for objects, but they have different purposes. - **Folders** are best for storing sections of an environment, such as the lobby or a combat arena. - **Models** are used for sets of objects, such as a desk set that includes a chair, table, and a lamp. To organize more complex sets, nest models inside models. You should always name your objects descriptively. This makes it easy to find and modify objects later on if needed. ![The Explorer window with a demonstration of a folder organization structure.](../assets/studio/explorer/Example-Organized-Folders-In-Workspace.png)_Objects organized into `Class.Folder|Folders`_ ![The Explorer window with a demonstration of a model organization structure in several services.](../assets/studio/explorer/Example-Maps-In-Workspace-ServerStorage.png)_Various "map" models to swap between `Class.Workspace` and `Class.ServerStorage`_ --- title: "Third-party tools" url: /docs/en-us/projects/external-tools last_updated: 2026-06-29T19:34:08Z description: "These popular tools for managing Roblox projects as a set of local files can enhance developer productivity." --- # Third-party tools For professional development studios, setting up third-party tools and investing in automation can dramatically improve developer productivity. Roblox's cloud-first approach has many advantages, but moving certain portions of the development workflow **outside** of the cloud can help larger teams track changes over time, review code, and use the languages and tools with which they're already familiar. - If you just want to use an external text editor or keep code in version control, [Script Sync](/docs/en-us/scripting/sync.md) is a great choice. - If you want to manage your entire project as a set of local files, this page covers some tools and strategies. > **Info:** Although this page covers several popular tools, it shouldn't be taken as a strict recommendation of them. Every team has different needs, so this page is intended to help you examine ways to improve your workflow rather than tell you to download anything in particular. > > The tools on this page are not maintained by Roblox and can change or stop working at any time. ## The syncing problem At its core, using external tools with Roblox is a _syncing_ problem: - You want your Roblox instances to exist as files on disk so that you can use your own tools to work on them. - You have to get your files back into your Roblox project after you've finished working on them. - If someone else changed those same files in the meantime, you have to handle any conflicts. For the whole solution to feel seamless and automatic, you need to a) listen for changes to files and b) incorporate these changes back into Studio. Rather than Roblox's cloud-first approach, [Rojo](https://rojo.space/) allows for a "file system-first" approach. It runs a server on your machine that communicates with a [Studio plugin](/docs/en-us/studio/plugins.md) to synchronize your files and the Studio data model. ## Install Rojo with Rokit You can manually download and run a Rojo binary, but that approach runs the risk of different developers on your team using different Rojo versions. A better solution is to use a toolchain manager like [Rokit](https://github.com/rojo-rbx/rokit), which uses a configuration file—a list of repositories and versions—to make the installation and upgrade process consistent across machines. Because it manages your baseline development environment rather than the packages within your project, Rokit is more akin to [nvm](https://github.com/nvm-sh/nvm) than [npm](https://www.npmjs.com/), but the comparison isn't perfect. A simple `rokit.toml` file looks like this: ```toml [tools] rojo = "rojo-rbx/rojo@7.6.1" wally = "upliftgames/wally@0.3.2" lune = "lune-org/lune@0.10.4" ``` Then you install these tools with `rokit install`. In addition to a global `rokit.toml` file, Rokit supports per-project files, so you can easily use different versions of Rojo, Wally, or any other tool for different projects and keep your entire team on those same versions. When a tool releases a new version, you then explicitly bump the version number in your `.toml` file, use Rokit to perform the upgrade, test the new version, and downgrade if it causes any problems. For commands and installation instructions, see [Rokit](https://github.com/rojo-rbx/rokit#-rokit). ## Run Rojo After you install Rojo with Rokit, what you've really installed is the Rojo server. The next step is to install the Rojo plugin for Roblox Studio. You can install it from the [Creator Store](https://create.roblox.com/store/asset/13916111004/Rojo) or install it locally with this command: ```bash rojo plugin install ``` Then generate the project structure for your new experience and build it: ```bash rojo init my-new-experience cd my-new-experience rojo build -o my-new-experience.rbxl ``` Alternatively, you can [port an existing experience](https://rojo.space/docs/v7/getting-started/existing-game/). Either way, after you have a project, start the Rojo server: ```bash rojo serve ``` In Roblox Studio, open the `.rbxl` file you just built, open the Rojo plugin, and connect to your now-running server, at which point you can start making changes in your preferred text editor and watch those changes automatically sync back to Studio. ![Visual Studio Code with a Rojo project open.](../assets/scripting/external-tools/external-tools-vscode.png) ![The Rojo plugin and Studio Explorer side-by-side.](../assets/scripting/external-tools/external-tools-rojo-plugin.png) Rojo projects have certain naming requirements for files, numerous configuration options, and some limitations, all of which are covered in the [Rojo documentation](https://rojo.space/docs/v7/). ## Package managers Roblox has a robust set of included APIs, but if you want to make use of community software packages in a consistent, reproducible way, you need a package manager. [Wally](https://wally.run/) is a popular option. You can install it through Rokit, just like Rojo. Within your experience's Rojo directory, run `wally init`. Then add your desired packages to `wally.toml`. The file might look like this: ```toml [package] name = "my-home-directory/my-new-experience" version = "0.1.0" registry = "https://github.com/UpliftGames/wally-index" realm = "shared" [dependencies] react = "jsdotlua/react@17.2.1" react-roblox = "jsdotlua/react-roblox@17.2.1" cryo = "phalanxia/cryo@1.0.3" ``` Then run `wally install`. Wally creates a `Packages` directory and downloads the specified packages there. The final step is to add the `Packages` directory to Rojo so that its contents sync back to Roblox. Open `default.project.json` and add the path. For simplicity, this example adds the entire directory to `Class.ReplicatedStorage` so that all packages are available to all scripts, but you might prefer to add specific packages to `Class.ServerScriptService` or `Class.StarterPlayerScripts`: ```json { "name": "my-new-experience", "tree": { "$className": "DataModel", "ReplicatedStorage": { "Shared": { "$path": "src/shared" }, "Packages": { "$path": "Packages" } }, ... } } ``` Then you can require packages within your scripts just like any other `Class.ModuleScript`: ```lua local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") local React = require(ReplicatedStorage.Packages.react) local ReactRoblox = require(ReplicatedStorage.Packages["react-roblox"]) local handle = Instance.new("ScreenGui", Players.LocalPlayer.PlayerGui) local root = ReactRoblox.createRoot(handle) local helloFrame = React.createElement("TextLabel", { Text = "Hello World!", Size = UDim2.new(0, 200, 0, 200), Position = UDim2.new(0.5, 0, 0.5, 0), AnchorPoint = Vector2.new(0.5, 0.5), BackgroundColor3 = Color3.fromRGB(248, 217, 109), Font = Enum.Font.LuckiestGuy, TextSize = 24 }) root:render(helloFrame) ``` Like most other software projects, the goal is that contributors can clone a repository, install Rokit, run a few commands, and have the same development environment as the rest of the team. > **Success:** For a detailed walkthrough of using React to create a Roblox UI, see [React + Roblox](https://devforum.roblox.com/t/how-to-react-roblox/2964543). ## Version control Having a set of plain text files on your computer unlocks a variety of capabilities, but the primary one is _version control_. You can store your files in a [Git](https://git-scm.com/) or [Mercurial](https://www.mercurial-scm.org/) repository; host remote repositories and review code changes in [GitHub](https://github.com), [GitLab](https://gitlab.com), or [Bitbucket](https://bitbucket.org); and use whichever text editor you like. [Visual Studio Code](https://code.visualstudio.com) and [Cursor](https://cursor.com) have the largest extension ecosystem, but [Sublime Text](https://www.sublimetext.com) and [Notepad++](https://notepad-plus-plus.org), and [Vim](https://www.vim.org) are all popular choices. Whichever editor you choose, matching the capabilities of the Studio script editor will require some extensions. You might also consider adding: - A linter like [selene](https://github.com/Kampfkarren/selene) to catch common issues and enforce coding standards - A code formatter like [StyLua](https://github.com/JohnnyMorganz/StyLua) - A language server like [Luau Language Server](https://github.com/JohnnyMorganz/luau-lsp) for autocompletion, type checking, and more - [Open Cloud](/docs/en-us/cloud/guides.md) scripts (not synced to Studio) to [update a published experience](/docs/en-us/cloud/reference/Universe.md#Cloud_UpdateUniverse) or [restart servers](/docs/en-us/cloud/reference/Universe.md#Cloud_RestartUniverseServers) ## Undo everything Because third-party tools sync changes back to Roblox Studio rather than replacing it, no part of this workflow involves any lock-in. At any time, you can stop using one or all of these tools and go back to editing your experience exclusively in Studio. The lack of risk makes experimenting with third-party tools particularly appealing. --- title: "Game ownership transfer" url: /docs/en-us/projects/game-ownership-transfer last_updated: 2026-06-29T19:34:09Z description: "Transfer ownership of a game to other Roblox users or groups." --- # Game ownership transfer With **game ownership transfer**, you can transfer your games to [groups](/docs/en-us/projects/groups.md) you have publish access to. This unlocks all of the features available to groups, including permissions and revenue management, and allows you to efficiently manage your content as your team scales and your priorities shift. ## Prerequisites Before making a transfer or accepting a transfer, you must first: - Verify your email address. - Upload your private `Class.ModuleScript|ModuleScripts` and `Class.InsertService|InsertService` asset usages to the group you're transferring the game to. If the game uses packages where the package owner is a user and not a group, you might have to recreate those packages or replace them with packages that are already owned by a group. > **Info:** If you use Open Cloud API keys for the game, be aware that the effective permissions of an API key are determined by the access of the user who last generated the API key. If the transfer changes that user's access to the game, the effective permissions of the API key change accordingly. > > In many cases, transfers have no impact on existing API keys. For example, if you transfer a game from your user account to a group you own, your access stays equivalent and existing API keys continue to work as before. > > If the transfer does change access, for instance, when you transfer to a group you do not own, or the original owner loses access, you should create new API keys under an account that has the appropriate access for the game. ## Transfer a game To transfer a game to a group: 1. Go to [Creations](https://create.roblox.com/dashboard/creations) and choose the game you want to transfer. 2. Go to **Configure** ⟩ **Settings**. 3. Click **Initiate ownership transfer**. 4. In the **Transfer Details** dialog, carefully read and acknowledge the implications of the transfer. Then, click **Next**. 5. Select a group to transfer the game to. You must be able to publish games to this group. 6. Verify the transfer by entering the game name. 7. Click **Initiate transfer**. The **Content Settings** page updates to include the pending group's username. > **Warning:** Once the group accepts the transfer, Roblox makes your game private and closes all servers associated with it. The transfer process typically completes in a few minutes but may take longer for complex games. > **Info:** To cancel a pending transfer, click **Cancel transfer request** in the **Content Settings** page. ## Receive a transferred game To receive a transferred game: 1. Go to the game page of the game you're receiving. You can navigate to this page by clicking the transfer request notification you received when the game's current owner initiated the transfer, or by directly getting the overview page link from the current owner. 2. In the **Transfer Details** dialog, carefully read and acknowledge the implications of the transfer. Then, click **Next**. 3. Verify the transfer by entering the game name. 4. Click **Accept transfer**. > **Warning:** Once your group accepts the transfer, Roblox makes the game private and closes all servers associated with it. The transfer process typically completes in a few minutes but may take longer for complex games. 5. After the transfer to your group is complete, set up any new permissions you need and make the game public again. ## Frequently asked questions ** What kind of information does the transferred game retain?** The transferred game retains its old game ID, place ID, and URL. ** Do transfer requests expire?** Yes, transfer requests expire after 7 days. ** How often can the same game be transferred?** After receiving a transferred game, you must wait 30 days to transfer that game again. ** Can changes be made to a game that's transferring?** You can continue making changes to the game until the new group owner accepts the transfer and the transfer process begins. Changes you make during the active transfer process specifically aren't carried over. ** What happens to credit card information when a game is transferred?** Roblox removes credit card information when you transfer a game. ** What happens to revenue from private server subscriptions when a game is transferred?** The group you transferred your game to receives the revenue from any existing private server subscriptions. This revenue does not respect any group or game revenue splitting. New private server subscriptions respect revenue splitting. ** Does transferring a game impact discovery?** Transferring a game doesn't impact search as long as the game's title remains the same, the new group owner is not a moderated account, and the game is made public again after the transfer. Note that transferring a game can impact discovery for up to 24 hours after the new group owner makes the game public again. --- title: "Group guidelines" url: /docs/en-us/projects/group-guidelines last_updated: 2026-06-29T19:34:09Z description: "Rules and owner accountability for Roblox groups." --- # Group guidelines If you are the owner of one or more [groups](/docs/en-us/projects/groups.md), carefully read through the following expectations for managing your groups. ## Roblox rules Roblox groups and **all content uploaded to them** must follow Roblox's [Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards) and [Terms of Use](https://en.help.roblox.com/hc/en-us/articles/115004647846-Roblox-Terms-of-Use). Content uploaded to groups that is subject to the Community Standards includes, but is not limited to: - Assets - Avatars - Experiences ## Owner accountability Group owners play a crucial role in maintaining a safe and positive environment within their groups. - Group owners should make a reasonable effort to address and mitigate harmful behavior within their communities and groups. - Group owners are responsible for utilizing Roblox-provided moderation tools such as [activity history](/docs/en-us/projects/groups.md#group-activity-history) to proactively ensure a healthy environment and moderate potentially violative content. ## Violations and consequences Groups that violate Roblox's [Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards) may face enforcement actions such as warnings, restrictions on features, temporary locks, and — for repeat or severe violations — permanent removal. Group owners can [appeal](https://en.help.roblox.com/hc/en-us/articles/360000245263-Appeal-Your-Content-or-Account-Moderation) any enforcement actions if they believe they were incorrect. - **Member Violations** — While group owners are not **directly** penalized for member violations, repeated failure to address violations within a group can result in future restrictions on group ownership. An owner whose group is permanently removed will not be able to own a new group for 90 days. Multiple permanent removals will result in a complete loss of group ownership privileges for the owner. - **Owner Violations** — Group owners who are found to be using their group to promote or facilitate violations of Roblox's Community Standards (through uploading or encouraging the upload of violative content) will face enforcement actions on their own accounts and possibly the permanent deletion of their group. --- title: "Groups (teams)" url: /docs/en-us/projects/groups last_updated: 2026-06-29T19:34:09Z description: "Explains Roblox groups and how to collaborate with multiple creators on the same experience." --- # Groups (teams) A Roblox **group** allows multiple creators to work on the same experience, use the same assets, share profits, and give credit to all contributors. > **Error:** Group ownership of experiences helps creators collaborate and operate as independent studios. If a conflict arises within a group, Roblox cannot help arbitrate or resolve disagreements. ## Group creation Creating a group costs 100 Robux. New groups maintain certain capabilities on the legacy [Groups](https://www.roblox.com/communities) pages, but improved workflows and options for [configuring roles/permissions](#roles-and-permissions) and inviting members are only available on the [Creator Dashboard](https://create.roblox.com/dashboard/creations). > **Warning:** Before creating a group, carefully read through the [rules and expectations](/docs/en-us/projects/group-guidelines.md) for group management. To create a new group: 1. Navigate to the [Creator Dashboard](https://create.roblox.com/dashboard/creations), expand the account switcher in the upper‑left, and click the **+** button.![Create Group button on the Creator Dashboard](../assets/creator-dashboard/Group-Create-Group-Button.png) 2. On the **Create Group** page, upload an image for the group logo and enter the group name. 3. When ready, click the **Purchase** button. Once the group is created, you'll be directed to the group profile page where you can modify the group's details, including adding up to three social links. ## Roles and permissions Group members can be assigned one or multiple **roles** within a group, and each role has **permissions** which specify the abilities of its members. > **Warning:** Role configuration is only accessible if you're the group owner or you have permission to create, configure, and assign group roles. > **Error:** Roles created using the legacy [Groups](https://www.roblox.com/communities) pages will continue to operate on the legacy system and can only be managed through the legacy workflow. If permissions were granted to a legacy role, **the member will continue to have those permissions**, so it's recommended that you revoke permissions/roles in the legacy system and then migrate members to the new system. 1. From the [Creator Dashboard](https://create.roblox.com/dashboard/creations), expand the account switcher in the upper‑left and select the group. 2. Expand the account switcher again, select **Settings** under the group's name, then select **Roles**. 3. For each role, carefully confirm the permissions under the **Permissions** tab.![Permissions tab indicated for a group role on the Creator Dashboard.](../assets/creator-dashboard/Group-Roles-Permissions-Tab.png)** Group Permissions**| **Add or remove group members** | Members with this role can [invite](#manage-members) and [remove](#member-removal) other members. | | --- | --- | | **Add or remove [role] role members** | Members with this role can give or remove the role to/from other members in the group. This permission is useful for roles that should have some management permissions but not at the super‑admin level. | | **Configure limited roles** | Members with this role can configure the roles they are assigned and assign permissions to those roles that they themselves have. | | **Administrate all roles** | Members with this role can create, delete, and configure any role in the group. This is a super‑admin permission, as it provides nearly full access. | | **Configure group profile** | Members with this role can update the group's name, description, and other metadata. Does not include transferring ownership, which only the owner can do. | | **View group activity history** | Members with this role can view [group activity history](#group-activity-history). |** Experience Permissions**| **Playtest all group experiences** | Members with this role can play all experiences owned by the group, whether they're private or public. Optionally available as a [per‑experience permission](/docs/en-us/projects/configure-games.md). | | --- | --- | | **Edit all group experiences** | Members with this role can edit all experiences and [assets](/docs/en-us/projects/assets.md) owned by the group and use all features in the [Data Stores Manager](/docs/en-us/cloud-services/data-stores/data-stores-manager.md). Does not include editing an experience outside of a [collaborative](/docs/en-us/projects/collaboration.md) session or saving a place from one experience into another experience. Optionally available as a [per‑experience permission](/docs/en-us/projects/configure-games.md). | | **Edit and publish all group experiences** | Members with this role can both edit experiences, as above, and also publish experiences to Roblox. Optionally available as a [per‑experience permission](/docs/en-us/projects/configure-games.md). | | **View all analytics for group experiences** | Members with this role can access the [analytics](/docs/en-us/production/analytics.md) of all experiences owned by the group, even if they don't have edit access. Optionally available as a [per‑experience permission](/docs/en-us/projects/configure-games.md). | | **Create and configure share links** | Members with this role can create/configure [share links](/docs/en-us/production/promotion/share-links.md). | | **Create and configure experience events** | Members with this role can create/configure [experience events](/docs/en-us/production/promotion/experience-events.md). | | **Configure bans for all group experiences** | Members with this role can ban and manage banned users for all experiences owned by the group. | | **Access read-only chat and configure bans for all group experiences** | Members with this role can view all in-experience chat messages and ban or manage banned users across all experiences owned by the group.

To view in-experience chat messages, enable **Read-only view** in the in-experience settings. | | **Configure monetization for all group experiences** | Members with this role can manage monetization products such as [passes](/docs/en-us/production/monetization/passes.md), [developer products](/docs/en-us/production/monetization/developer-products.md), [subscriptions](/docs/en-us/production/monetization/subscriptions.md), and more. Optionally available as a [per‑experience permission](/docs/en-us/projects/configure-games.md). | | **List, create, update, and delete secrets for all group experiences** | Members with this role can see the list of [secrets](/docs/en-us/cloud-services/secrets.md) and can create, update, and delete secrets for all experiences owned by the group. Optionally available as a [per‑experience permission](/docs/en-us/projects/configure-games.md). |** Group Revenue**| **View group revenue** | Members with this role can view the group Robux balance, any configured recurring split payouts, and revenue logging features. | | --- | --- | | **Configure and spend group revenue** | Members with this role can view all group revenue, as above, and also directly manage it. This includes setting up [recurring payouts](#recurring-payouts) and sending [one‑time payouts](#one-time-payouts) to collaborators. | | **Create ad campaigns for the group** | Members with this role can advertise the group. |** Avatar Item Permissions**| **Configure avatar items** | Members with this role can configure avatar items such as clothing. | | --- | --- | | **Create avatar items** | Members with this role can create avatar items such as clothing. |** Open Cloud Permissions**| **Manage own API keys** | Members with this role can configure and upload their own [Open Cloud API keys](/docs/en-us/cloud/auth/api-keys.md) to the group, but not configure everyone else's. | | --- | --- | | **Manage all API keys** | Members with this role can upload their own [Open Cloud API keys](/docs/en-us/cloud/auth/api-keys.md) to the group, as well as configure everyone else's. |** Asset Permissions**| **View development items** | Members with this role can view development items owned by the group, for example **Audio**. | | --- | --- | | **Create and configure development items** | Members with this role can upload and configure asset development items such as **Decals**. | | **Manage development item permissions** | Members with this role can configure permissions of development items, for example audio sharing. |** Data Stores**| **View data stores for all group experiences** | Members with this role can view data stores for all group experiences through the [Data Stores Manager](/docs/en-us/cloud-services/data-stores/data-stores-manager.md), but they cannot delete data. Optionally available as a [per‑experience permission](/docs/en-us/projects/configure-games.md). | | --- | --- | | **Edit data stores for all group experiences** | Members with this role can delete specific keys within data stores for all group experiences through the [Data Stores Manager](/docs/en-us/cloud-services/data-stores/data-stores-manager.md). Optionally available as a [per‑experience permission](/docs/en-us/projects/configure-games.md). | | **Delete data stores for all group experiences** | Members with this role can delete entire data stores for all group experiences through the [Data Stores Manager](/docs/en-us/cloud-services/data-stores/data-stores-manager.md). Optionally available as a [per‑experience permission](/docs/en-us/projects/configure-games.md). | 4. **IMPORTANT** Click the **Save Changes** button to apply the permission settings. 5. **OPTIONAL** Click the **Settings** tab and choose a color for the role, then click **Save Changes**.![Settings tab indicated for a group role on the Creator Dashboard.](../assets/creator-dashboard/Group-Roles-Settings-Tab.png)![Color options indicated for a group role on the Creator Dashboard.](../assets/creator-dashboard/Group-Roles-Role-Color.png) > **Info:** A role's **Settings** section is also where group owners and members with sufficient permissions can remove a role entirely through the **Delete Role** button. ## Manage members > **Warning:** Member configuration is only accessible if you're the group owner or you have permission to manage other members and their roles. Once [roles](#roles-and-permissions) have been configured for the group, members can be invited and assigned from either the **Members** tab within a specific role or from the **Collaboration** ⟩ **Members** page. ![Add members button indicated for a group role on the Creator Dashboard.](../assets/creator-dashboard/Group-Roles-Add-Members.png)_Collaboration ⟩ Roles ⟩ Members tab_ ![Invite button indicated in the group's Members section on the Creator Dashboard.](../assets/creator-dashboard/Group-Members-Invite-Button.png)_Collaboration ⟩ Members ⟩ Member tab_ From the add/invite popup, locate a creator by typing their username into the search field. Creators will be notified when invited to your group (a customizable setting within the creator notification system), or you can copy the **group link** and share it with invited creators through other means. ### Manage roles To assign roles to a member, hover over their name, click the **Add Role** button, and then manage roles from the popup. ![Assign roles to a group member on the Creator Dashboard.](../assets/creator-dashboard/Group-Members-Assign-Role.png) To quickly unassign a role from a member, hover over it and click the **×** button. ![Hover-over showing how to unassign a role from a group member on the Creator Dashboard.](../assets/creator-dashboard/Group-Members-Unassign-Role.png) > **Error:** Currently, roles configured through the legacy [Groups](https://www.roblox.com/communities) pages appear for each member. If permissions were granted to a role through the legacy system, **the member will continue to have those permissions**, so it's recommended that you revoke permissions/roles in the legacy system and then migrate members to the new system. ### Member removal To remove a member entirely, hover over their name, click the **⋮** button, and then select the removal option. A dialog will appear for you to confirm the action. ![Hover-over showing how to remove a member from a group on the Creator Dashboard.](../assets/creator-dashboard/Group-Members-Remove-Member.png) ## Manage payouts If you're the group owner or you have the correct [permission](#roles-and-permissions) within **Group Revenue**, you'll find a **Payouts** page under **Finances** in the left navigation. Here, you can send one‑time payouts, as well as define percentage splits with other members. > **Info:** Some groups may not have this page unlocked initially for various reasons, such as the age of the group or insufficient funds to payout. > **Warning:** Payouts cannot be shared across group members for experiences that charge for [paid access in local currency](/docs/en-us/production/monetization/paid-access-local-currency.md). ### One-time payouts One-time payouts can be made to members in a batch, selecting a set amount for each. Safety features include 2FA challenges, confirmation dialogues, and checks around the eligibility of members being paid. ![One-time payout button indicated in the Payouts section on the Creator Dashboard.](../assets/creator-dashboard/Group-Payouts-One-Time.png) Once you click the **Send Robux** button, you can choose payout recipients from the popup or upload a `.csv` spreadsheet file with the columns `userId` and `payoutInRobux`, for example: | userId | payoutInRobux | | --- | --- | | 012345 | 5000 | | 098765 | 7000 | ### Recurring payouts Recurring payouts can also be made across the entire group **and** per‑experience, assigning a percentage payout to each member before the remainder enters the group's overall balance. ![Split payout buttons indicated in the Payouts section on the Creator Dashboard.](../assets/creator-dashboard/Group-Payouts-Split-Options.png) Consider the following scenario where a group experience **Laser Maze** is split 40%–30%–10% among three members, with a remainder of 20%. Assuming the experience earns 1000 Robux, the three members receive 400, 300, and 100 Robux respectively (40%–30%–10%). The remaining 200 Robux (20% of the experience split) passes onward to the **group split** percentages, defined at 20%–20%–10% among the same three members; they receive an additional 40, 40, and 20 Robux respectively. The remaining 100 Robux (50% of the group split) is placed in the group's overall balance. ![Example flowchart of how experience splits are distributed to group members first, with remainder going factored into group splits for final distribution among the group.](../assets/creator-dashboard/Group-Payouts-Splits-Example.png) > **Warning:** Note that revenue from [private server](/docs/en-us/production/monetization/private-servers.md) subscriptions does not change if you adjust split percentages at a later time, meaning that if a player buys a private server subscription, the split percentages at time of purchase will apply to that particular subscription forever (until it is canceled). This policy may be changed in the future. ## Group activity history Group members with the **View group activity history** permission can view detailed activity such as role changes and publishing actions from the **Activity History** section under **Collaboration** on the Creator Dashboard. This is also a useful way for group owners to moderate activity that may violate [group guidelines](/docs/en-us/projects/group-guidelines.md). ## Intellectual property protection Group members with permission to edit all group experiences can enable the [Place Copying](/docs/en-us/projects/configure-games.md#allow-copying) setting for a creation, potentially allowing the entire Roblox community to copy it and use assets within it. To help protect intellectual property in a group, the owner or members with sufficient permissions should: - Confirm that each member is [assigned the appropriate role](#manage-roles). - Check that each group role has the correct [permissions](#roles-and-permissions). - Confirm that the [Place Copying](/docs/en-us/projects/configure-games.md#allow-copying) setting is disabled before private assets are added.
--- title: "Projects" url: /docs/en-us/projects last_updated: 2026-06-29T19:34:09Z description: "An overview of projects and games in Roblox." --- # Projects A Roblox project is a collection of [places](#places), [assets](#assets), [settings](#settings), and other resources that together represent a **game**. Roblox stores your projects in the cloud for convenient collaboration, editing, and version control. You create and manage projects with [Roblox Studio](/docs/en-us/studio.md), an all‑in‑one IDE that provides building, scripting, testing, and publishing tools. ## Places Games on Roblox are made up of individual **places**, comparable to scenes in Unity or maps in Unreal Engine. Each place contains all components for that portion of the game, including its specific environment, parts, meshes, scripts, and user interface. See the pages for [creating and publishing](/docs/en-us/production/publishing/publish-games-and-places.md), [configuring](/docs/en-us/configure-games.md), and [updating](/docs/en-us/update-games.md) games and places for details. ![A game grouping of three individual places with unique environments.](../assets/publishing/experiences-places-assets/Experience-Hierarchy.png) Every place is represented by a **data model**, a hierarchy of objects that describe everything about the place. The Roblox Engine uses the data model as a source of truth for a place's state, so it can simulate and render it on client devices. For more information on how the engine interprets the data model, see [Client-server runtime](/docs/en-us/projects/client-server.md). Proper, intentional object organization within the data model is essential for functionality and maintenance of your project. For more information on what objects are available and how to organize and use them, see [Data model](/docs/en-us/projects/data-model.md). ## Assets In Roblox, assets such as images, meshes, and audio are stored as **cloud-based assets**, so you don't need to bundle local copies into a saved Studio game. Each asset in the cloud is assigned a unique **asset ID** from which multiple games can utilize them. You can create assets directly in Studio, such as models, or import assets like images, audio, and meshes from other tools. `rbxassetid://7229442422` ![A decal asset of a young woman with a button for an eye.](../assets/modeling/textures-decals/Texture-Example-Grafitti04.png) `rbxassetid://6768917255` ![An untextured treasure chest MeshPart asset.](../assets/modeling/meshes/Base-Mesh-In-Marketplace.png) `rbxassetid://9125402735` By default, assets are private to your game and you can use an asset in any place by referencing its ID. You can also distribute them to the community in the [Creator Store](https://create.roblox.com/store/), so others can use them as well. For more information on how to import and publish assets, see [Assets](/docs/en-us/projects/assets.md). ## Packages [Packages](/docs/en-us/projects/assets/packages.md) are reusable object hierarchies that you can define and reuse in multiple places across multiple games. For any large project, packages offer the following benefits: - Packages can be used as asset kits, allowing you to duplicate a set of objects as needed. - Packages make it easier to update assets. For instance, a package can include a tree that's duplicated many times in an environment. If you need to make a change, such as swap textures for the tree, it can be updated once in the package instead of for each individual instance. - A package can start with graybox assets, and eventually be replaced with final art assets. When assets are replaced, they retain all original positions and orientations. ## Settings Game settings are managed from the [Creator Dashboard](https://create.roblox.com/dashboard/creations) or within Studio, including: - **Basic Info** — Basic information about the game, such as its name, description, and genre. Much of the information here is used in your game's listing. - **Communication** — Settings which enable eligible users to use [voice chat](/docs/en-us/chat/voice-chat.md) or animate their avatar via their camera within your game. - **Permissions** — Configures who can access your game. New games begin as **private** and can only be edited and joined by you and members of your [group](/docs/en-us/projects/groups.md) with the correct permissions. When appropriate, you can [release](/docs/en-us/production/publishing/publish-games-and-places.md#make-game-public) the game to the public. - **Monetization** — Options for earning revenue from your game, as outlined in [Monetization](/docs/en-us/production/monetization.md). - **Localization** — Configuration for different [languages and regions](/docs/en-us/production/localization.md). - **Avatar** — Settings related to avatars, such as avatar scaling and clothing overrides. ## Collaboration With Studio's **built-in collaboration tools**, team members can contribute to games independently on their own time, or alongside others. Key features include: - Group admins can manage which members have access to collaborate and which do not, effectively maintaining proper roles within a large team. - Collaborators can build alongside other team members in real time and automatically see changes made by others. - Collaborators can independently edit the same scripts that others may be editing, test locally, and commit their changes to the cloud-based project when ready. For more information, see [Collaboration](/docs/en-us/projects/collaboration.md). ## Testing Your [team](/docs/en-us/projects/groups.md) can instantly test a game on PC, mobile, VR, and other devices you wish to support through the Roblox app; no need to compile builds, deploy to app stores, or await app store approval. Studio offers a suite of options for testing a game before releasing it to the public: - **Rapid playtesting** that provides a close simulation of the game running on the Roblox application. - **Multi-client simulation** for comparing how each client "sees" other clients within the game. - **Device emulation** that provides insight on how controls operate on a mobile device or how on-screen UI displays on different screens and aspect ratios. - **Collaborative playtesting** with members of your team. For more information on each testing option, see [Studio testing modes](/docs/en-us/studio/testing-modes.md). --- title: "Place files" url: /docs/en-us/projects/place-files last_updated: 2026-06-29T19:34:09Z description: "Learn how to manage and optimize your place files in Roblox." --- # Place files Each Roblox experience consists of one or more [places](/docs/en-us/projects.md#places), and Studio uploads your place data to Roblox's servers whenever you save or publish to Roblox in the **File** menu. You might also want to export local copies of places for use with a version control system or other [external tools](/docs/en-us/projects/external-tools.md). To export your place in Studio, select **Save to File** or **Download a Copy** from the **File** menu. Studio offers two file formats: - `.rbxl` is the binary place file format. This format is compact, but not human-readable. - `.rbxlx` is the XML-based place file format, which is quite a bit larger on disk than the binary format. > **Info:** Roblox supports places up to 100 MB (104,857,600 bytes). Beyond that limit, **Save to Roblox** and **Publish to Roblox** might fail. The data that Studio uploads to Roblox is slightly smaller than the `.rbxl` file format, so you can export your place to `.rbxl` to get a sense of its size. > > Very few places, even the most complex ones, reach this limit, and when they do, it's often due to inefficiencies in the place. See [Troubleshooting](/docs/en-us/projects/place-files.md#troubleshoot-place-file-size). ## Auto-recovery files If saving to Roblox fails due to the file size limit, Studio automatically generates a backup of your place called a **recovery file**. The frequency of auto-recovery backups depends on whether your experience has [collaboration](/docs/en-us/projects/collaboration.md) enabled: - If you have collaboration enabled and saving to Roblox fails, Studio backs up the last three save attempts as recovery files on your system. - If collaboration is disabled, Studio uses **Auto-Recovery** settings from [Studio Settings](/docs/en-us/studio/setup.md#customization). On Windows, recovery files are located in `C:\Users\Admin\AppData\Local\Roblox\RobloxStudio\AutoSaves`. On Mac, you can find them in `/Users/your-username/Library/Application Support/Roblox/RobloxStudio/AutoSaves/`. ## Troubleshoot place file size If you reach the 100 MB limit on your place file, there's likely an underlying issue that you can troubleshoot. Try the following steps. ### Safety check Check the models in your place and their attached scripts for any obfuscated or unclear text. This text does not compress well and might even be a malicious backdoor into your place. Roblox moderation systems actively filter out these models, but if you happen to find one, delete the model from your place, find the model on the [Creator Store](https://create.roblox.com/store/models), and report it immediately. ### Redundant parts Unreferenced or hidden assets can accumulate over time and unnecessarily increase file size. 1. See if any models or groups of parts are taking up more space than others: 1. Export your place file and note its size on disk. 2. Open the file in Roblox Studio. 3. Copy and paste the models or groups of parts that you suspect are inordinately large. 4. Save the file again and note its increase in file size. 2. Check for redundant parts (parts of the same size and shape with the same position) or instances (redundant `Class.SurfaceAppearance|SurfaceAppearances`, textures, etc.) and delete them. The Creator Store has plugins that can help you identify these cases. ### Simplify terrain Reducing or simplifying terrain can significantly reduce place size and improve performance. Avoiding excessive terrain features like dense forests or complex hills helps make experiences run faster and more efficiently. Of course, the experience should match your artistic vision, but consider the following optimizations: 1. Horizontal layers of the same material/occupancy compress much better than sloped layers. If possible, try to limit subsurface detail to horizontal layers. 2. Turn on **Show Wireframe Rendering** from Studio's **View** menu and check for messy holes or misplaced water under the terrain surface. These mistakes can increase compressed size. 3. There are community plugins that optimize non-visible blocks of terrain for better compression, such as [this one](https://create.roblox.com/store/asset/133396894381603/Terrain-File-Size-Optimizer). ### Collision fidelity `Enum.CollisionFidelity.Box|Box` and `Enum.CollisionFidelity.Hull|Hull` collision fidelity are more memory efficient than the default. Select your meshes, open the [Properties](/docs/en-us/studio/properties.md) window, and change the **CollisionFidelity** values as appropriate for your use case. ### Teleports If other size reduction strategies fail, you can break your place into multiple, smaller places and link them through [teleports](/docs/en-us/projects/teleport.md). Serialization and upload happen at the place level, so this approach lets you grow your universe place by place over time instead of trying to maintain a single, massive place. --- title: "Server authority model" url: /docs/en-us/projects/server-authority last_updated: 2026-06-29T19:34:09Z description: "The server authority model lets you build responsive cheat-free competitive experiences such as FPS, racing, sports, and combat." --- # Server authority model In a **server authority model**, the server is the single **source of truth** for the entire experience state, and clients are only trusted to report their own inputs. This architecture is the core netcode foundation of a fair, competitive experience because it prevents entire classes of cheating like flyhacks or speedhacks by never trusting a client to report its own position or state. ## Advantages In a naive server-owned system, clients would simply send their inputs to the server and display the results of the experience sent back by the server. While technically correct, such a system would face significant **input latency** because every player action would have to travel to the server, be processed, and have the result sent back to the client before it could be displayed. For most experiences, especially those fast‑paced, that round‑trip delay would cause gameplay to feel laggy, unresponsive, and unplayable. In Roblox's **server authority** model, latency is compensated for by having clients instantly [predict](#client-prediction) the effects of their inputs in addition to sending them to the server. For example, when a player presses a key, the client doesn't wait for the server to respond; instead it **predicts** a few frames ahead of the last known server state. This allows the client to show the result of the input action instantly, effectively hiding network latency and making the experience feel responsive. Sometimes the client will get its prediction wrong ([misprediction](#client-misprediction)) and, because of network latency, the client will not know that it has made a mistake for a few frames. For example: > **Warning:** Your client predicts that you've moved forward, but on the server another player used a stun ability that stopped you from moving. The client and server now have different states. When a misprediction is detected, the client must correct its prediction based on the server's **authoritative state**. If the authoritative state is different from the client's predicted state, the client must [roll back and resimulate](#rollback-and-resimulation) its predicted frames. This system of client‑side prediction, rollback, and resimulation is known as "latency compensation" and helps make server‑authoritative multiplayer experiences feel smooth and responsive. > **Info:** Mispredictions are a **normal and expected** part of the server authority architecture because clients inherently cannot predict other players' inputs, so actions by other players will always trigger some amount of misprediction. When tuned correctly, the resulting corrections are small and should be imperceptible to players. Read further on [advanced techniques](/docs/en-us/projects/techniques.md) to smooth out and mask network artifacts. ## Setup As the server authority model requires certain other engine technologies to function correctly, you must first set the following properties on the `Class.Workspace` object in the [Explorer](/docs/en-us/studio/explorer.md): 1. `Class.Workspace.NextGenerationReplication` must be enabled 2. `Class.Workspace.PlayerScriptsUseInputActionSystem` must be enabled 3. `Class.Workspace.SignalBehavior` must be `Enum.SignalBehavior.Deferred|Deferred` 4. `Class.Workspace.UseFixedSimulation` must be enabled 5. `Class.Workspace.StreamingEnabled` must be enabled 6. `Class.Workspace.AuthorityMode` must be `Enum.AuthorityMode.Server|Server` (all of the above must be set first) ## Concepts The server authority system runs on a few core concepts as follows. ### Client prediction Through **client prediction**, the client simulates a few frames ahead of the last known server state to predict the effects of player inputs immediately. This hides input latency, but the prediction may later turn out to be incorrect ([client misprediction](#client-misprediction)) and thus require correction. The client tries to simulate just far enough ahead of the last known authoritative server state so that its inputs arrive on the server at the intended frame. The number of frames the client will predict ahead of the known server state is based on the latency behind the client and server. ### Client misprediction When the client receives the authoritative state from the server, it checks that state against a historical record of what it predicted locally for that frame. When there is a difference between what the client [predicted](#client-prediction) and what the server actually did, this is a **misprediction**. Mispredictions can occur for several reasons, including shifts in network latency, other players acting in ways the client didn't anticipate, the experience running certain logic exclusively on the server, etc. If the authoritative state is different from the client's predicted state, the client must [roll back and resimulate](#rollback-and-resimulation). ### Rollback and resimulation When a client detects a [misprediction](#client-misprediction), it must reset to the server's authoritative state and then resimulate to jump back to its predicted frame. Based on the network latency, the client tries to simulate just far enough ahead of the last known authoritative server state so that its inputs arrive on the server at the intended frame. ```mermaid block-beta columns 1 block:Client columns 7 ClientHeader("Client        ") space:6 C1["3"] C2["4"] C3["5"] C4["6"] C5["3 4 5 6 7"] C6["8"] C7["9"] end block:Middle columns 10 TimeStart("TIME") space:8 TimeEnd(" ") space:10 end block:Server columns 7 ServerHeader(["Server        "]) space:6 S1["1"] S2["2"] S3["3"] S4["4"] S5["5"] S6["6"] S7["7"] end TimeStart --> TimeEnd C1 --"  Input (Frame 3)  "--> S3 S3 --"  Server State (Frame 3)  "--> C5 style Client fill:RoyalBlue style ClientHeader stroke-opacity:0, fill-opacity:0 style Middle fill-opacity:0, stroke-opacity:0 style TimeStart fill-opacity:75%, stroke-opacity:75% style TimeEnd display:none style Server fill:SeaGreen style ServerHeader stroke-opacity:0, fill-opacity:0 ``` _In the above diagram, the client is simulating 2 frames ahead of the server. It sends its inputs for frame 3 which arrive at the intended frame (3) on the server. The server sends the authoritative state for frame 3 and the client receives it on frame 7. The client discovers it mispredicted frame 3 so it resets to the server's frame 3 and resimulates frames 4, 5 and 6 before it simulates frame 7. Players may see a noticeable network artifact such as a sudden movement._ In summary, the **client**: 1. Receives the **authoritative state** from the server and compares it against its own **predicted state**. 2. If the client's prediction was incorrect: 1. Client rolls back to the last known authoritative state received from the server. 2. Client resimulates from the authoritative state to its predicted state, re-applying any local inputs. ## Implementation ### Network ownership and prediction In the server authority model, you're able to keep the core gameplay objects server‑owned without incurring the input latency cost normally associated with server ownership. Things like cars, player characters, or other gameplay‑critical objects can remain server‑owned, even when interacting with other players. By default, Roblox will automatically predict properties with [simulation access](#simulation-access) near the local player `Class.Player.Character|Character`, but if you want more fine‑grained control, you can explicitly force an instance's prediction on or off with `Class.RunService:SetPredictionMode()`. ### Simulation sync In the server authority model, the client and the server must both run the core simulation, and the client's simulation needs to be able to [roll back and resimulate](#rollback-and-resimulation) when a [misprediction](#client-misprediction) occurs. To enable this, write your core logic inside functions bound through `Class.RunService:BindToSimulation()` in a `Class.ModuleScript` that's initialized on both the client and server. ![Server authority setup](../../assets/studio/explorer/ReplicatedStorage-Server-Authority.png) During a resimulation, Roblox will re-run the functions bound to the simulation via `Class.RunService:BindToSimulation()|BindToSimulation()`. Processing player inputs, interacting with synchronized physics objects, and updating the core experience state should live inside those bound functions. #### Simulation `Class.ModuleScript` named `Simulation` in `Class.ReplicatedStorage`: ```lua local RunService = game:GetService("RunService") local Players = game:GetService("Players") local Simulation = {} Simulation.Initialize = function() RunService:BindToSimulation(function(deltaTime) -- Read player inputs -- Update experience state end) end return Simulation ``` #### ServerLoader `Class.Script` named `ServerLoader` as child of the `Simulation` `Class.ModuleScript`: ```lua local Simulation = require(script.Parent) Simulation:Initialize() ``` #### ClientLoader `Class.LocalScript` named `ClientLoader` as child of the `Simulation` `Class.ModuleScript`: ```lua local Simulation = require(script.Parent) Simulation:Initialize() ``` ### State sync with attributes Roblox automatically synchronizes all properties with [simulation access](#simulation-access) on predicted instances. For custom data, [attributes](/docs/en-us/studio/properties.md#instance-attributes) are the primary way to synchronize instances marked as predicted; on such instances, any mismatch in attribute values between the server's source of truth and the client's prediction will trigger a full [rollback and resimulation](#rollback-and-resimulation). > **Success:** Use attributes to store experience data that affects your core simulation, including player health, ammunition, inventory, or custom rules (see [limitations](#attribute-limits)). > **Warning:** Only write to attributes on **predicted** instances from within functions bound through `Class.RunService:BindToSimulation()|BindToSimulation()` to ensure that Roblox can fully track relevant experience states and resimulate properly. #### Attribute limits In order to be replicated, an attribute must meet all of the following criteria: - It is among the first 64 attributes on its `Class.Instance`. - Its name contains at most 50 characters. - If a string type attribute, its value contains at most 50 characters. #### Simulation access Many properties and methods in the engine API reference include the **Simulation Access** label, for example `Class.BasePart.CFrame`. Properties with this label will be predicted by the server authority system. Additionally, only properties and methods with this label can be accessed inside functions bound with `Class.RunService:BindToSimulation()`. ### Input actions In a server-authoritative experience, the primary way for a client to affect the experience's state is through the [Input Action System](/docs/en-us/input/input-action-system.md). These inputs are sent to the server and are replayed during resimulation on the client. As a result, `Class.InputAction|InputActions` should be used for **all inputs that affect the core simulation** and they should be checked for sanity before they're processed. > **Warning:** To reiterate, do not use traditional events like `Class.UserInputService.InputBegan` in the core simulation of a server-authoritative experience. Note that `Class.InputContext|InputContexts` must be a descendent of a `Class.Player` so that the engine knows who has ownership over the `Class.InputContext`. One approach is to add your `Class.InputContext|InputContexts` to a folder under `Class.ReplicatedStorage` and use a `Class.Script` under `Class.ServerScriptService` to clone the `Class.InputContext|InputContexts` per player: ```lua local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") local InputsFolder = ReplicatedStorage:WaitForChild("Inputs") local function onPlayerAdded(player) local clone = InputsFolder:Clone() clone.Parent = player end Players.PlayerAdded:Connect(onPlayerAdded) for _, player in Players:GetPlayers() do onPlayerAdded(player) end ``` Using this pattern, you can read `Class.InputAction|InputActions` for all players in `Class.RunService:BindToSimulation()` on both client and server to receive the same data for a given frame and record the previous frame's input in an [attribute](/docs/en-us/studio/properties.md#instance-attributes), for example to trigger a character run when a `RunAction` input action is triggered. Note that if you need to write custom data into the [Input Action System](/docs/en-us/input/input-action-system.md), you should use a function connected to `Class.RunService.RenderStepped` or `Class.RunService:BindToRenderStep()`, not `Class.RunService:BindToSimulation()`. Firing an `Class.InputAction` in `Class.RunService:BindToSimulation()|BindToSimulation()` can cause clients to produce incorrect results during a resimulation. ```lua local Players = game:GetService("Players") local RunService = game:GetService("RunService") local Workspace = game:GetService("Workspace") RunService:BindToRenderStep("CameraInput", Enum.RenderPriority.Last.Value, function() -- Send the client camera's forward vector to the server through an InputAction local cameraForwardInputAction = Players.LocalPlayer.PlayContext.CameraForward local cameraForwardVector = Workspace.CurrentCamera.CFrame.LookVector cameraForwardInputAction:Fire(cameraForwardVector) end) ``` ### Remote events [Remote events](/docs/en-us/scripting/events/remote.md) can still be used within the server authority model to facilitate discrete communication between client and server. For example, servers can use remote events to broadcast data about players scoring points or picking up objects, and clients can use remote events as an alternative API for sending inputs to the server, such as for button presses or tapping objects in the 3D world. > **Warning:**`Class.RemoteEvent|RemoteEvents` might not be consistently ordered with property and attribute updates over the network. For example, if you change a property before firing a remote event on a client, the server might receive the property change after the remote event. To learn more, see [replication order](/docs/en-us/scripting/attributes.md#replication-order). ### Animations, sounds, and effects Client-side effects like animations and sounds must be written knowing that the client simulation is merely a prediction of the authoritative server state. `Class.RunService:BindToSimulation()|BindToSimulation()` limits what properties and methods can be called from within bound functions to help guide you in writing only to the synchronized simulation state. Displaying the results of this simulation, triggering effects and sounds, etc. should be done in a separate function connected to `Class.RunService.RenderStepped|RenderStepped` that reads the results of the simulation and triggers the desired effects. Further guidance around rendering a predicted simulation is covered in the [advanced techniques](/docs/en-us/projects/techniques.md) guide. ## Example projects In addition to this documentation, the following templates can help you get started: _Racing_ _Soccer_ _Laser Tag_ --- title: "Server authority techniques" url: /docs/en-us/projects/server-authority/techniques last_updated: 2026-06-29T19:34:09Z description: "Techniques for creating high-quality, smooth, multiplayer experiences using the server authority model." --- # Server authority techniques This guide outlines various techniques for creating high-quality, smooth, multiplayer experiences using the [server authority model](/docs/en-us/projects/server-authority.md). ## Predictive instance creation (instance stitching) Instance **stitching** lets client scripts predict `Datatype.Instance.new()` calls made inside `Class.RunService:BindToSimulation()` callbacks. The client creates the `Class.Instance` immediately without waiting for a server round‑trip; when the server's authoritative copy arrives, the client‑created instance and the server's authoritative copy are merged into one. From your script's perspective, the `Class.Instance` exists immediately and is consistent with the server. Instance stitching is useful in cases where an instance must be visible and active on the client as soon as possible. While the server will eventually replicate any instance the client needs (along with any effects they had on the world), this process incurs at least one round‑trip of latency due to server communication. Examples include firing a rocket launcher and creating physics constraints — without stitching, the client will see the rocket pop in far away from them, or some jitter when the new constraints replicate to them. ### Technical behavior Instance stitching works by generating the same deterministic GUID on both the client and the server for the same `Datatype.Instance.new()` call. The GUID is derived from four inputs: the type of the `Class.Instance` being created, the calling script's identity (two scripts with the same text are considered different), the current simulation frame, and a per‑script call counter that resets each frame. If client and server agree on all four inputs, they produce matching GUIDs and the stitch succeeds. ### Implementation To utilize instance stitching, simply call `Datatype.Instance.new()` inside a `Class.RunService:BindToSimulation()|BindToSimulation()` callback from a `Class.ModuleScript` that gets required on both the client **and** the server. Nothing else is required on your end; the system handles GUID assignment and reconciliation automatically. ```lua local RunService = game:GetService("RunService") local Players = game:GetService("Players") local Simulation = {} Simulation.Initialize = function() RunService:BindToSimulation(function(deltaTime) local part = Instance.new("Part", workspace) -- Part exists immediately on the client and will be reconciled with the server end) end return Simulation ``` ### Limitations Currently, instance stitching has the following limitations (to be resolved in the future): > **Warning:** Setting `Class.Instance.Parent|Parent` separately from `Datatype.Instance.new()` is not supported inside a simulation callback. Instead, pass the parent as the second argument to `Datatype.Instance.new()`:```lua -- Correct local part = Instance.new("Part", workspace) -- Not supported (will fail) local part = Instance.new("Part") part.Parent = workspace ``` > **Warning:** Setting other properties such as `Class.Instance.Name|Name`, `Class.BasePart.Size|Size`, or `Class.BasePart.CFrame|CFrame` must be deferred to the next frame using `Library.task.defer()`:```lua local part = Instance.new("Part", workspace) task.defer(function() part.Name = "PredictedPart" part.Size = Vector3.new(2, 2, 2) end) ``` > **Warning:** Stitching only applies to `Datatype.Instance.new()`. Instances created via `Class.Instance:Clone()` or `Datatype.Instance.fromExisting()` inside a simulation callback will not be reconciled with the server and will remain client‑local. ## Position smoothing You can visually smooth out the position of mispredicted synchronized objects by rendering a different object than what is being simulated. 1. Make the **simulated** object invisible. 2. Make a **renderer** object as a massless, non‑collidable, visual‑only clone to track the simulated object. 3. Attach a script to the renderer object that smoothly tracks the position of the invisible, simulated object. This separation between rendering and simulation lets you alter the position of the renderer object to create a visually smooth experience. In the following sample `Class.Script`, the rendered object (parent) smoothly tracks the simulated object. The rendered object is always slightly "behind" the simulated object which is typically fine but may be undesirable in certain situations. ```lua local RunService = game:GetService("RunService") local TweenService = game:GetService("TweenService") local Workspace = game:GetService("Workspace") -- Object to smoothly track local smoothTarget:BasePart = Workspace.SimulatedPart -- Visual object that will be smoothed local renderer:BasePart = script.Parent -- Time to smooth over; smaller means faster local smoothTime = 0.07 -- Store data needed to compute the smooth position local smoothVelocity = Vector3.new() -- Disable the renderer object's physics renderer.Massless = true renderer.Anchored = true renderer.CanCollide = false RunService.RenderStepped:Connect(function(deltaTime: number) -- Smoothly track the target object local smoothPosition, smoothVelocity = TweenService:SmoothDamp( renderer.Position, smoothTarget.Position, smoothVelocity, smoothTime, math.huge, deltaTime) renderer.Position = smoothPosition end) ``` The [Soccer](https://www.roblox.com/games/110687099504272/Soccer-Server-Authority-Template) example experience uses a variation of this technique to more intelligently turn on and off position smoothing for the soccer ball. Specifically, the soccer ball only smooths its position when the simulated ball has "jumped" far enough away from the rendered ball. This approach provides the best of both worlds: the soccer ball has no visual latency under normal conditions, and the experience smoothly interpolates its position only after the simulated ball has unexpectedly jumped to a new location, likely due to a network artifact or server‑side change. ## Writing animation code Under server authority, the client's simulation can be [rolled back and resimulated](/docs/en-us/projects/server-authority.md#rollback-and-resimulation) when the server corrects a misprediction. During rollback, animation state is rewound, which means `Class.AnimationTrack` handles that you cached in earlier frames may no longer be valid. ### Mirror animation logic As with any core gameplay logic, the logic for controlling animations must be in sync between server and client or there may be mispredictions and jittery behavior. See [simulation sync](/docs/en-us/projects/server-authority.md#simulation-sync) for a pattern that binds functions through `Class.RunService:BindToSimulation()` in a `Class.ModuleScript` that's initialized on both the client and server. ### Avoid track caching A common pattern in non-server-authority scripts is to cache `Class.AnimationTrack` objects at load time and reuse them indefinitely. This pattern fails in a server authoritative game when the server corrects a misprediction and the client rewinds/replays its simulation with corrected data. If your script still holds a reference to a stopped or replaced track, calls like `Class.AnimationTrack:AdjustWeight()|AdjustWeight()` or `Class.AnimationTrack:AdjustSpeed()|AdjustSpeed()` will operate on a track that's no longer visually represented. ```lua local Players = game:GetService("Players") local RunService = game:GetService("RunService") local player = Players.LocalPlayer local character = player.Character or player.CharacterAdded:Wait() local humanoid = character:WaitForChild("Humanoid") local animator = humanoid:WaitForChild("Animator") -- Cache animation tracks local tracks = {} tracks["WalkForward"] = animator:LoadAnimation(walkForwardAnim) RunService:BindToSimulation(function(dt: number) tracks["WalkForward"]:AdjustSpeed(1 + math.cos(time())) end) ``` Instead of holding onto track objects, store the **animation IDs** (or `Class.Animation` instances) and query the `Class.Animator` for the live track whenever you need to interact with it. Two APIs are available for this: - `Class.Animator:GetTrackByAnimationId()` — Returns the currently active track for a specific animation ID, or `nil` if there are no active animations with that ID. Use this when you know which specific animation you're looking for. - `Class.Animator:GetPlayingAnimationTracks()` — Returns all active tracks (playing, fading out, or paused). Use this when you need to iterate over everything that's active (for example, to stop all animations or find tracks by some criteria). #### CustomAnimate `Class.ModuleScript` named `CustomAnimate` in `Class.ReplicatedStorage`: ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local RunService = game:GetService("RunService") local CustomAnimate = {} -- Store animation references (not loaded tracks) local animations = { WalkForward = ReplicatedStorage.Animations.WalkForward, } local function getOrLoadTrack(animator: Animator, animation: Animation): AnimationTrack local track = animator:GetTrackByAnimationId(animation.AnimationId) if not track then track = animator:LoadAnimation(animation) end return track end CustomAnimate.SyncAnimations = function(character) local humanoid = character:WaitForChild("Humanoid") local animator = humanoid:WaitForChild("Animator") RunService:BindToSimulation(function(dt: number) local walkTrack = getOrLoadTrack(animator, animations.WalkForward) if not walkTrack.isPlaying then walkTrack.Looped = true walkTrack.Priority = Enum.AnimationPriority.Core walkTrack:Play() end walkTrack:AdjustSpeed(1 + math.cos(time())) end) end return CustomAnimate ``` #### ServerSync `Class.Script` named `ServerSync` in `Class.ServerScriptService`: ```lua local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") local CustomAnimate = require(ReplicatedStorage.CustomAnimate) local function onCharacterAdded(character) CustomAnimate.SyncAnimations(character) end local function onPlayerAdded(player) player.CharacterAdded:Connect(onCharacterAdded) end Players.PlayerAdded:Connect(onPlayerAdded) ``` #### ClientSync `Class.LocalScript` named `ClientSync` in `Class.StarterCharacterScripts`: ```lua local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") local CustomAnimate = require(ReplicatedStorage.CustomAnimate) local player = Players.LocalPlayer local character = player.Character if not character or character.Parent == nil then character = player.CharacterAdded:Wait() end CustomAnimate.SyncAnimations(character) ``` ## Playing sounds and visual effects In a predicted simulation, it's possible to trigger effects or sounds for events that the client predicted would happen but which never occurred on the server. The rendering system should be prepared to "undo" any mispredicted effects. For example, a client might predict that a grenade exploded and trigger a particle effect, but if another player defused the grenade, the client should hide the particle effect. A good strategy for rendering a predicted simulation is to synchronize a state machine pattern within the simulation loop and render changes to the state in a render step function. The following example simulates a grenade with a state machine pattern: ```lua local Workspace = game:GetService("Workspace") local module = {} module.GrenadeStates = { Idle = 0, Lit = 1, Exploded = 2, Defused = 3, } module.GrenadeExplodeTime = 3.0 module.Initialize = function(grenade) RunService:BindToSimulation(function(deltaTime) -- Initialize empty grenade state local grenadeState = grenade:GetAttribute("State") if grenadeState == nil then grenadeState = module.GrenadeStates.Idle grenade:SetAttribute("State", grenadeState) grenade:SetAttribute("Timer", 0.0) end -- Increment grenade timer local timer = grenade:GetAttribute("Timer") timer = timer + deltaTime grenade:SetAttribute("Timer", timer) -- Explode lit grenades if grenadeState == module.GrenadeStates.Lit then if timer >= module.GrenadeExplodeTime then grenadeState = module.GrenadeStates.Exploded grenade:SetAttribute("State", grenadeState) grenade:SetAttribute("Timer", 0.0) end end end) end return module ``` With the previous state machine in place, you can render grenade effects in a `Class.RunService.RenderStepped` connection within a separate script based on the synchronized grenade state: ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local RunService = game:GetService("RunService") local Simulation = require(ReplicatedStorage.Simulation) local grenade = script.Parent local previousGrenadeState = nil -- Highlight instance to indicate grenade state local highlight = Instance.new("Highlight") highlight.Parent = grenade highlight.FillTransparency = 1 highlight.OutlineTransparency = 1 highlight.DepthMode = Enum.HighlightDepthMode.Occluded RunService.RenderStepped:Connect(function(deltaTime: number) local grenadeState = grenade:GetAttribute("State") local grenadeTimer = grenade:GetAttribute("Timer") -- Emit the lit particles if the grenade is lit grenade.LitEmitter.Enabled = grenadeState == Simulation.GrenadeStates.Lit -- Play the explosion emitter if the grenade just exploded if previousGrenadeState ~= grenadeState then if grenadeState == Simulation.GrenadeStates.Exploded and grenadeTimer < 0.2 then grenade.ExplosionEmitter:Emit(100) grenade.ExplosionSound:Play() end previousGrenadeState = grenadeState end -- Change the grenade's highlight color based on the state and time if grenadeState == Simulation.GrenadeStates.Lit then highlight.FillColor = Color3.fromRGB(255, 0, 0) highlight.FillTransparency = 1 - (grenadeTimer / Simulation.GrenadeExplodeTime) elseif grenadeState == Simulation.GrenadeStates.Idle then highlight.FillTransparency = 1 elseif grenadeState == Simulation.GrenadeStates.Exploded then highlight.FillTransparency = 1 elseif grenadeState == Simulation.GrenadeStates.Defused then highlight.FillColor = Color3.fromRGB(0, 255, 125) highlight.FillTransparency = 0.5 end end) ``` ## Designing around network latency Certain gameplay mechanics lend themselves better to networked multiplayer than other mechanics. Players will always have some delay between when another player performs an action and when they receive that player's input. The best way to create a super smooth multiplayer experience is to design your experience with these limitations in mind. For example, an experience with slower acceleration on player movement will appear smoother than one with higher acceleration because the difference in position caused by the network latency of player input will be less than in an experience with higher acceleration. As another example, a gameplay mechanic where players can **instantly** trigger a large explosion by pressing an input will have more network artifacts than if the explosion is delayed after the input, as if by lighting a fuse. This puts the resimulation on the fuse effect instead of on the explosion effect which is a less noticeable network artifact. ## Predicting other player inputs By default, Roblox does not forward the inputs from each client to every other client. Whether this is right for your experience depends on its design: - For basic humanoid movement, the default behavior means that other player characters' movements are not extrapolated from the authoritative server state and, as a result, other player characters will not mispredict but will render slightly in the past. - In a racing game, by contrast, the default behavior means that clients will not know whether other players are applying the throttle or other inputs, so other cars may appear behind the local player even if they're actually ahead. To alleviate this, you can store player inputs in [attributes](/docs/en-us/studio/properties.md#instance-attributes) on the server and operate on those synchronized attributes client‑side using `Class.RunService:BindToSimulation()` as demonstrated in the following code sample and the [Racing](https://www.roblox.com/games/134686834388911/Racing-Server-Authority-Template) template. This approach lets you use attributes as inputs to your simulation to have fully replicated player inputs. ```lua local Players = game:GetService("Players") local RunService = game:GetService("RunService") local module = {} module.storePlayerInput = function(player:Player, humanoidRootPart:BasePart) local inputContext:InputContext = player.PlayerGui.InputContext local throttle = inputContext.DefuseAction:GetState() humanoidRootPart:SetAttribute("Throttle", throttle) -- Write any other inputs into attributes... end module.Initialize = function() RunService:BindToSimulation(function(deltaTime) if RunService:IsServer() then -- Forward inputs from server to all clients for _, player in Players:GetPlayers() do local humanoidRootPart:BasePart = player.Character.HumanoidRootPart local inputContext:InputContext = player.PlayerGui.InputContext module.storePlayerInput(player, humanoidRootPart) end else -- Write local player inputs as attributes local player = Players.LocalPlayer local humanoidRootPart:BasePart = player.Character.HumanoidRootPart local inputContext:InputContext = player.PlayerGui.InputContext module.storePlayerInput(player, humanoidRootPart) end -- Use the attributes as inputs to the game for _, player in Players:GetPlayers() do local humanoidRootPart:BasePart = player.Character.HumanoidRootPart local throttle = humanoidRootPart:GetAttribute("Throttle") if throttle then -- Apply the throttle to the player's vehicle end end end) end) return module ``` ## Debugging There are some new tools and techniques you can use to debug a server‑authoritative experience. ### Server authority visualizer Pressing `Ctrl``Shift``F6` (Windows) or `⌘``Shift``F6` (Mac) opens Studio's **server authority visualizer** which shows several key pieces of information: | Details | Description | | --- | --- | | **Instance prediction success rate** | The percentage of correctly predicted instances over the last 8 seconds. | | **Input accept rate** | The percentage of all players' inputs that arrived on time on the server. Late inputs will lower this number. | | **Client-server step delta** | The number of frames between the client and the server, including the join time of the client. The stability of this number represents the stability of your connection to the server. | | **RCC heartbeat FPS** | The frame rate of the simulation on the server. If this number drops below 59, the server cannot keep up with the simulation and the experience will degrade in quality. | | **Predicted instance count** | The number of instances your client is [predicting](/docs/en-us/projects/server-authority.md#client-prediction). | | **Input drop reason counts** | The number of times the server has dropped an input for each reason: | ### Simulation radius When relying on automatic prediction (`Enum.PredictionMode.Automatic`), you can visualize the prediction radius around your player character by enabling **Are Regions Enabled** in Studio's settings (`Alt``S` on Windows; `⌥``S` on Mac). The green cylinder indicates the range around your character in which instances are predicted, and its radius grows and shrinks based on the performance characteristics of the device. ![Simulation radius around player character with server authority running](../../assets/studio/debugging/Server-Authority-Simulation-Radius.jpg) --- title: "Teleport between places" url: /docs/en-us/projects/teleport last_updated: 2026-06-29T19:34:09Z description: "Explains how to use TeleportService to teleport users between different places in your game." --- # Teleport between places Many games are subdivided into multiple [places](/docs/en-us/production/publishing/publish-games-and-places.md#create-additional-places), such as a fantasy world with towns, castles, dungeons, and a vast forest. Use `Class.TeleportService` to teleport users between places, to different servers, or even to other games. > **Warning:**`Class.TeleportService` doesn't support playtesting in Roblox Studio. You must publish the game and test in the Roblox client. ## Teleport players To teleport players, use `Class.TeleportService:TeleportAsync()`. This method accepts three parameters: - The `Class.DataModel.PlaceId|PlaceId` for users to teleport to. - An array containing the `Class.Player` instances you want to teleport. - An optional `Class.TeleportOptions` instance that contains custom properties for the `Class.TeleportService:TeleportAsync()|TeleportAsync()` call. ```lua local Players = game:GetService("Players") local TeleportService = game:GetService("TeleportService") local TARGET_PLACE_ID = 12345678901234 -- replace with your own local playerToTeleport = Players:GetPlayers()[1] -- get the first user in the game TeleportService:TeleportAsync(TARGET_PLACE_ID, {playerToTeleport}) ``` To get the appropriate players to teleport, you might use a `Class.BasePart.Touched` or a `Class.ProximityPrompt.Triggered` event to get an individual `Class.Player`. Then you can check if the player is part of a team (`Class.Player.Team`) or party (`Class.Player.PartyId`). Finally, you can use `Class.Team:GetPlayers()` or `Class.SocialService:GetPlayersByPartyId()` if you want to teleport the entire group rather than just the individual. > **Info:** To reduce client-side exploits, you can only call `Class.TeleportService:TeleportAsync()|TeleportAsync()` from server scripts. If necessary, client scripts can call `Class.TeleportService:Teleport()|Teleport()`, but we don't recommend it. For more information, see [Configure secure teleportation](#configure-secure-teleportation). ## Configure secure teleportation Three settings handle teleport security. | Setting | Description | | --- | --- | | (Creator Dashboard) **Audience** > **Access Settings** > **Access Control for Places** | Controls whether players can join any place in your game or must first join the [start place](/docs/en-us/production/publishing/publish-games-and-places.md#create-games). | | (Creator Dashboard) **Place** > **Access** > **Direct Access Control** | Overrides your game-level **Access Control for Places** setting for a non-start place. | | (Studio) **File** > **Experience Settings** > **Security** > **Allow Third Party Teleports** | Controls teleports from your game to **games that you don't own**. You can leave this setting disabled and still teleport players between published games you own. | **Access Control for Places** controls players teleporting **into** your game and is the most critical setting for preventing teleport-based exploits. ![Access control for places on the Creator Hub](../assets/players/teleport-access-control.png) - If you choose **Fully open**, players can join any place in your game through teleports from any game, including deep links, game invites, joining a friend, and more. This is a good choice if your game has several places and you want friends to be able to easily join each other no matter which place they're in. If you don't choose this option, players who try to join their friends in non-start places will instead join your game's start place. - If you choose **Limited to same universe**, players can only join non-start places through teleports within your games. This setting allows both client- and server-initiated teleports. This is a good choice if you have a legacy game that you don't want to [migrate to secure teleports](#migrate-to-secure-teleports). - If you choose **Secure within universe only**, players can only join non-start places through server-initiated teleports within this game. This is a good choice if your game has a strict progression system before players can access certain areas. It's also a good choice if your game has a test place that players shouldn't have access to or for places that exclusively use reserved servers. > **Success:** Ultimately, your **Access Control for Places** setting depends on the type of game you want to build. Many games don't need secure teleports. ### Migrate to secure teleports If you have an existing game that uses client-side teleports and want to require server-initiated teleports, the goal is to move all teleport logic out of client scripts and into server scripts: 1. Find all client scripts that call `Class.TeleportService:Teleport()|Teleport()`. 2. Change these calls to instead fire [remote events](/docs/en-us/scripting/events/remote.md). Alternatively, you can change the calls to instead use `Class.ProximityPrompt|ProximityPrompts`, `Class.ClickDetector|ClickDetectors`, or even just the `Class.BasePart.Touched` event. 3. Reimplement the teleport in a server script using `Class.TeleportService:TeleportAsync()|TeleportAsync()`. 4. When no more `Class.TeleportService:Teleport()|Teleport()` calls exist, change **Access Control for Places** to **Secure**. ## Create custom teleport screens When a user triggers a teleport, they see the standard Roblox loading screen as they wait for the new place to load. If desired, you can add a custom teleport screen by calling `Class.TeleportService:SetTeleportGui()` on the client. The following example sets a customized `Class.ScreenGui` from `Class.ReplicatedStorage`. Any scripts within the `Class.ScreenGui` do **not** run. ```lua local TeleportService = game:GetService("TeleportService") local ReplicatedStorage = game:GetService("ReplicatedStorage") local teleportGui = ReplicatedStorage.TeleportGui TeleportService:SetTeleportGui(teleportGui) ``` ## Customize teleport options You can customize teleportations, such as [teleporting users to a specific server](#teleport-to-specific-servers) and [sending user data along with teleports](#send-user-data-along-with-teleports), by setting the `Class.TeleportOptions` instance and passing it to the `Class.TeleportService:TeleportAsync()` method. ### Teleport to specific servers To teleport users to specific servers, set the target server using `Class.TeleportOptions` and pass it to the `Class.TeleportService:TeleportAsync()|TeleportAsync()` method. If you don't specify a server, users are teleported into a public server; the information of the first user in the list is used for matchmaking. To teleport users to a specific public server, set the `Class.TeleportOptions.ServerInstanceId` property as a valid instance ID, which is a unique identifier for a public server. ```lua local teleportOptions = Instance.new("TeleportOptions") teleportOptions.ServerInstanceId = targetServerId ``` To teleport users to a specific reserved server, set a valid `Class.TeleportOptions.ReservedServerAccessCode`, which is a unique code for entering a reserved server. ```lua local teleportOptions = Instance.new("TeleportOptions") teleportOptions.ReservedServerAccessCode = reservedServerCode ``` To teleport users to a new reserved server, set `Class.TeleportOptions.ShouldReserveServer` to true. ```lua local teleportOptions = Instance.new("TeleportOptions") teleportOptions.ShouldReserveServer = true ``` ### Send user data along with teleports Teleporting a user between places discards any local data associated with that user. You can use the following approaches to handle data persistence between places: - If your game utilizes **secure** user data like in-game currency or inventory, implement [data stores](/docs/en-us/cloud-services/data-stores.md) or [memory stores](/docs/en-us/cloud-services/memory-stores.md) to maintain data from place to place. - To send basic **non-secure** data from place to place, call `Class.TeleportOptions:SetTeleportData()` before passing it to `Class.TeleportService:TeleportAsync()|TeleportAsync()`. **Don't** pass secure data using this method; the data is visible to the client and unencrypted. ```lua local teleportData = { randomNumber = RNG:NextInteger(1, 100), } local teleportOptions = Instance.new("TeleportOptions") teleportOptions:SetTeleportData(teleportData) ``` To retrieve all data when a user arrives at the new place after a teleport, use the `Class.Player:GetJoinData()` function, which returns a dictionary with a `TeleportData` key. ```lua local Players = game:GetService("Players") local function onPlayerAdded(player) local joinData = player:GetJoinData() local teleportData = joinData.TeleportData local randomNumber = teleportData.randomNumber print(player.Name .. " joined with the number " .. randomNumber) end Players.PlayerAdded:Connect(onPlayerAdded) ``` To retrieve only the teleport data on the client, you can use `Class.TeleportService:GetLocalPlayerTeleportData()`. ## Handle failed teleports Like any API call that involves network requests, teleports can fail and throw an error. Wrap them in protected calls (`Global.LuaGlobals.pcall()`). Some failures benefit from retries, particularly those involving reserved servers, so we recommend retrying some number of times on failure. Even if a call succeeds and the teleport initiates, it can still fail at the last moment without throwing an error and leave the user in the server. When this happens, it triggers the `Class.TeleportService.TeleportInitFailed` event. The following example `Class.ModuleScript` returns a single `SafeTeleport` function that teleports players in a protected call with retry logic. It also has a `handleFailedTeleport` function to deal with situations in which the call was successful, but the teleport didn't occur. ```lua local TeleportService = game:GetService("TeleportService") local ATTEMPT_LIMIT = 5 local RETRY_DELAY = 1 local FLOOD_DELAY = 15 local function SafeTeleport(placeId, players, options) local attemptIndex = 0 local success, result -- define pcall results outside of loop so results can be reported later on repeat success, result = pcall(function() return TeleportService:TeleportAsync(placeId, players, options) -- teleport the user in a protected call to prevent erroring end) attemptIndex += 1 if not success then task.wait(RETRY_DELAY) end until success or attemptIndex == ATTEMPT_LIMIT -- stop trying to teleport if call was successful, or if retry limit has been reached if not success then warn(result) -- print the failure reason to output end return success, result end local function handleFailedTeleport(player, teleportResult, errorMessage, targetPlaceId, teleportOptions) if teleportResult == Enum.TeleportResult.Flooded then task.wait(FLOOD_DELAY) elseif teleportResult == Enum.TeleportResult.Failure then task.wait(RETRY_DELAY) else -- if the teleport is invalid, report the error instead of retrying error(("Invalid teleport [%s]: %s"):format(teleportResult.Name, errorMessage)) end SafeTeleport(targetPlaceId, {player}, teleportOptions) end TeleportService.TeleportInitFailed:Connect(handleFailedTeleport) return SafeTeleport ``` The `SafeTeleport` function receives the same arguments as the `Class.TeleportService:TeleportAsync()|TeleportAsync()` function. You can use the following script with the `SafeTeleport` function to perform teleports from anywhere in your game: ```lua local Players = game:GetService("Players") local TeleportService = game:GetService("TeleportService") local ServerScriptService = game:GetService("ServerScriptService") local SafeTeleport = require(ServerScriptService.SafeTeleport) local PLACE_TO_TELEPORT_TO = 12345678 local function teleport(touchPart) local playerToTeleport = game.Players:GetPlayerFromCharacter(touchPart.Parent) if playerToTeleport then SafeTeleport(PLACE_TO_TELEPORT_TO, {playerToTeleport}) end end script.Parent.Touched:Connect(teleport) ``` --- title: "Release game updates" url: /docs/en-us/projects/update-games last_updated: 2026-06-29T19:34:09Z description: "Explains how to release updates for games." --- # Release game updates When you publish an updated version of a game to Roblox, players aren't immediately removed from old versions of the game. Instead, you can migrate players to the updated version by restarting all of your outdated servers. If you don't restart servers, players transition to the new version of the game as the servers running old versions eventually empty and shut down. This option is non‑disruptive, but your player base might be playing different versions of the game for a prolonged period of time; if your underlying player data format changes, you should probably restart servers. For time-sensitive updates, you might prefer to deploy early, hide content behind a [config](/docs/en-us/production/configs.md), and then change the config value to release your new content. ## Restart servers To restart servers for release updates: 1. Go to [Creations](https://create.roblox.com/dashboard/creations) and select the game you want to restart servers for. 2. Go to **Configure** ⟩ **Server Management**. 3. Select the places you want to restart. 4. Click **Restart Servers**. 5. In the **Confirm Server Restart** dialog: 1. Select one or both of the following restart options: - **Restart only servers with outdated versions** to avoid restarting servers that are running the latest published place version. - **Delay server restart** to delay the shutdown of servers and allow players a set time between 1 and 60 minutes to leave the game on their own instead of being temporarily disconnected. This is also known as server bleed-off. 2. Click **Restart**. > **Warning:** Unless you have a game update workflow that is independent of place version, it's recommended that you always select **Restart only servers with outdated versions** to avoid unnecessarily disconnecting players. After you restart servers, Roblox: 1. Stops all matchmaking to servers designated for shutdown. Players who join your game by clicking the **Play** button will not be matched to these servers, but they can still join them by accepting invites, teleporting, or joining another user from their profile. 2. Waits for the configured delay time, if you selected the **Delay server restart** option. 3. Automatically teleports players to the updated version of the game. All players on the same old server are sent to the same replacement server running the new version. > **Info:** If you want to completely shut down your game and not allow players to reconnect after you restart your servers, you must make the game private. ## Notifications on servers When you launch a delayed server restart, the `Class.DataModel.ServerRestartScheduled` event will fire on affected servers with the following arguments: - `restartTime` — A `Datatype.DateTime`. The earliest time the server is scheduled to shut down. - `source` — This will always be `Enum.CloseReason.DeveloperUpdate` for your published updates. - `attributes` — An optional JSON object you can use to provide custom information about the restart, such as the update reason, urgency, etc. In your server scripts, you can subscribe to this event to provide players with information about the upcoming restart. You might choose to provide a countdown timer in your UI, prompt players to save their progress, or teleport them to updated servers at the most convenient time. --- title: "Version History" url: /docs/en-us/projects/version-history last_updated: 2026-06-29T19:34:09Z description: "Manage saved and published versions of a place with Version History." --- # Version History **Version History** lets you track, manage, and restore saved and published versions of a place. You can use it to create and find development checkpoints, collaborate with other creators, and quickly roll back to a known good state when needed. With Version History, you have access to the following: - **Version notes**: Descriptive notes you can add to your saved and published places to create clear checkpoints for development milestones. - **Search**: Search across version notes to more easily find the place version you're looking for. - **Advanced filtering**: Filter by date range, save type (auto or manual), published saves, collaborator, and whether a save has version notes attached to it. - **Tracking collaborators**: Track who was in a place edit session and who saved the place. - **Dockable widget**: A Version History widget you can position anywhere in your Studio layout for easier access. You can access this widget by going to **Window** ⟩ **Version History**.![A view of the Version History window.](../assets/studio/general/Version-History.png) > **Info:** Place versions created before Version History was introduced do not include the new metadata fields and cannot be searched or filtered by them. Add notes to older versions to make them searchable. ## Create a save with version notes 1. Go to **File** ⟩ **Save to Roblox with Notes**. 2. Enter a version name and any notes you might have. 3. Click **Save**. ![Version History save dialog.](../assets/studio/general/Version-History-Notes.png) You can also use the following keyboard shortcuts to save your current place version with version notes: - Windows: `Ctrl` + `Alt` + `S` - macOS: `Cmd` + `Option` + `S` ## Publish with version notes Version notes are required when publishing so you can clearly track what changes were released to your players. To publish with version notes: 1. Go to **File** ⟩ **Publish**. 2. Select **Publish**. 3. Enter a version name and any notes you might have. 4. Click **Publish**. ## Restore to a previous version You can restore to a previous place version through Studio or the Creator Dashboard. This action creates a new version of the place. > **Info:** Restoring a place to a previous version does **not** automatically publish the changes. If the place is [public](/docs/en-us/production/publishing/publish-games-and-places.md#make-game-public) and you want the restored version to replace the live version, open the place in Studio, publish it, and [restart your servers](/docs/en-us/projects/update-games.md#restart-servers). Through Studio: 1. Go to **Window** ⟩ **Version History** to open the Version History window. 2. Click **⋮** next to the version you want to restore to. 3. Click **Open Local Copy** to open a copy of the place in a new Studio session. 4. In the new Studio session, go to **File** ⟩ **Save to Roblox As**. 5. Select the game and place you want to overwrite. 6. Confirm the action to restore to that version. Through the Creator Dashboard: 1. Go to [Creations](https://create.roblox.com/dashboard/creations) and select a game. 2. Go to **Configure** ⟩ **Places**. 3. Select the place you want to restore to a previous version. 4. Go to **Version History**. 5. Click **Restore** next to the version you want to restore to. 6. Confirm the action to restore to that version. --- name: "Roblox.AssetPermissionsApi" last_updated: 2026-06-29T19:34:06Z type: opencloud api_base_url: "https://apis.roblox.com" version: "1.0" endpoints: 1 auth: [api-key, cookie] --- # Roblox.AssetPermissionsApi **API Version:** 1.0 **Base URL(s):** - `https://apis.roblox.com` ## Authentication Each endpoint requires one of the following authentication methods: - **API Key**: Pass your key in the `x-api-key` HTTP header. Create keys at [Creator Dashboard](https://create.roblox.com/dashboard/credentials). - **Cookie** *(not recommended)*: `.ROBLOSECURITY` cookie. Do not use in production. ``` # API Key example curl -H "x-api-key: YOUR_API_KEY" https://apis.roblox.com/... # OAuth 2.0 example curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" https://apis.roblox.com/... ``` ## Endpoints ### PATCH `/asset-permissions-api/v1/assets/permissions` [BETA] Grant a subject permission to multiple assets. Authorization is required to grant permissions to the subject and asset IDs in the request. **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) **Scopes:** `asset-permissions:write` **Request Body:** `application/json-patch+json` — Type: `BatchGrantPermissionsRequest` See [BatchGrantPermissionsRequest](#batchgrantpermissionsrequest) in Models. **Request example:** ```json { "subjectType": "Invalid", "subjectId": "string", "action": "Invalid", "requests": [ { "assetId": "...", "grantToDependencies": "...", "parentVersionNumber": "..." } ], "assetIds": [ 0 ], "enableDeepAccessCheck": false } ``` > **Verify mutations:** If your API key lacks the required scope (`asset-permissions:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `BatchGrantPermissionsResponse` - `400`: Bad Request → `ErrorResponse` - `403`: Forbidden → `ErrorResponse` - `500`: Internal Server Error → `ErrorResponse` **Response fields** (`BatchGrantPermissionsResponse`) See [BatchGrantPermissionsResponse](#batchgrantpermissionsresponse) in Models. **Response example:** ```json { "successAssetIds": [ 0 ], "errors": [ { "assetId": "...", "code": "..." } ] } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/asset-permissions-api/v1/assets/permissions" \ -H "Content-Type: application/json" \ -d '{ "subjectType": "Invalid", "subjectId": "string", "action": "Invalid", "requests": [ { "assetId": "...", "grantToDependencies": "...", "parentVersionNumber": "..." } ], "assetIds": [ 0 ], "enableDeepAccessCheck": false }' ``` ## Models ### AssetGrantRequest Asset grant requests with additional options to grant to dependencies. | Property | Type | Required | Description | |----------|------|----------|-------------| | `assetId` | `integer` | No | The asset ID to grant permission to. | | `grantToDependencies` | `boolean` | No | Whether to extend the permission grant to dependencies of the asset. This will be done asynchronously after the main grant. | | `parentVersionNumber` | `integer` | No | The version number of 'assetId' to use for determining asset dependencies. | ### BatchGrantPermissionsRequest Request object to grant one permission to multiple assets. | Property | Type | Required | Description | |----------|------|----------|-------------| | `subjectType` | `SubjectType` | No | | | `subjectId` | `string` | No | The subject ID to grant to. Must be empty for SubjectType 'All'. | | `action` | `AssetAction` | No | | | `requests` | `AssetGrantRequest[]` | No | Array of asset grant requests. If populated, 'requests' will override 'assetIds'. | | `assetIds` | `integer[]` | No | [Deprecated] The list of asset IDs to grant this permission to. 'requests' will be prioritized over this list. | | `enableDeepAccessCheck` | `boolean` | No | [Do not use] An optional boolean to indicate if a deep access check should be done. This is not intended for public use. | ### BatchGrantPermissionsResponse Response object to grant one permission to multiple assets. | Property | Type | Required | Description | |----------|------|----------|-------------| | `successAssetIds` | `integer[]` | No | The list of asset IDs that granted successfully. | | `errors` | `GrantPermissionError[]` | No | The list of grants that had errors. | ### Error The error object for results. | Property | Type | Required | Description | |----------|------|----------|-------------| | `code` | `ErrorCode` | No | | | `message` | `string` | No | The human readable error message. | ### ErrorResponse The error object for responses. | Property | Type | Required | Description | |----------|------|----------|-------------| | `error` | `Error` | No | | ### GrantPermissionError Single error for BatchGrantPermissionsResponse. | Property | Type | Required | Description | |----------|------|----------|-------------| | `assetId` | `integer` | No | Failed asset ID. | | `code` | `ErrorCode` | No | | --- name: "Assets API" last_updated: 2026-06-29T19:34:06Z type: opencloud api_base_url: "https://apis.roblox.com/assets" version: "0.0.1" endpoints: 9 auth: [api-key, oauth2] description: "You can send and receive the following request and response payloads to create assets on Roblox" --- # Assets API You can send and receive the following request and response payloads to create assets on Roblox. For information on the usage of the API, see the [usage guide](/docs/en-us/cloud/guides/usage-assets.md). **API Version:** 0.0.1 **Base URL(s):** - `https://apis.roblox.com/assets` ## Authentication Each endpoint requires one of the following authentication methods: - **API Key**: Pass your key in the `x-api-key` HTTP header. Create keys at [Creator Dashboard](https://create.roblox.com/dashboard/credentials). - **OAuth 2.0**: Use Bearer token in the `Authorization` header. Authorization URL: `https://apis.roblox.com/oauth/v1/authorize`, Token URL: `https://apis.roblox.com/oauth/v1/token` ``` # API Key example curl -H "x-api-key: YOUR_API_KEY" https://apis.roblox.com/... # OAuth 2.0 example curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" https://apis.roblox.com/... ``` ## Endpoints ### POST `/v1/assets` [BETA] Creates an asset with provided content and metadata. Creates an asset with provided content and metadata. You can't add [SocialLink](#SocialLink) objects when you create an asset. Instead, use [Update Asset](#PATCH-v1-assets-_assetId_). Provide the [Asset](#Asset), binary asset file path, and [content type](/docs/en-us/cloud/guides/usage-assets.md#supported-asset-types-and-limits) in the form data. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `asset:read`, `asset:write` **Request Body:** `multipart/form-data` — Type: `object` | Property | Type | Required | Description | |----------|------|----------|-------------| | `request` | [Asset](#asset) | Yes | Asset attributes to create. | | `request.assetType` | `string` | No | The asset type. Required for [Create Asset](#POST-v1-assets). | | `request.assetId` | `integer` | No | The unique identifier of the asset. Required for [Update Asset](#PATCH-v1-assets-_asset_). | | `request.creationContext` | [CreationContext](#creationcontext) | No | | | `request.description` | `string` | No | The description of the asset. Limit to 1000 characters. Required for [Create Asset](#POST-v1-assets) | | `request.displayName` | `string` | No | Display name of the asset. Required for [Create Asset](#POST-v1-assets). | | `request.path` | `string` | No | The returned resource path of the asset. Format: `assets/{assetId}`. Example: `assets/2205400862`. | | `fileContent` | `string` | Yes | The binary asset file path and the content type. See [Asset types and limits](/docs/en-us/cloud/guides/usage-assets.md#supported-as | **Request example:** ```json { "request": { "assetType": "string", "assetId": 0, "creationContext": { "assetPrivacy": "...", "creator": "...", "expectedPrice": "..." }, "description": "string", "displayName": "string", "path": "string" }, "fileContent": "string" } ``` > **Verify mutations:** If your API key lacks the required scope (`asset:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: Returns the Operation ID for checking the creation status. → `Operation` - `400`: Invalid argument. Failed to parse the request or the file. → `Status` - `401`: The API key is not valid for this operation / You don't have the authorization. - `500`: Server internal error / Unknown error. **Response fields** (`Operation`) See [Operation](#operation) in Models. **Response example:** ```json { "path": "string", "done": false, "error": { "code": 0, "message": "string" }, "response": { "assetType": "string", "assetId": 0, "creationContext": { "assetPrivacy": "...", "creator": "...", "expectedPrice": "..." }, "description": "string", "displayName": "string", "path": "string" } } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Rate Limits:** perIp: 120/minute, perApiKeyOwner: 120/minute, perOauth2Authorization: 120/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/assets/v1/assets" \ -F "request={"assetType":"string","assetId":0,"creationContext":{"assetPrivacy":"default","creator":{"userId":"...","groupId":"..."},"expectedPrice":0},"description":"string","displayName":"string","path":"string"};type=application/json" \ -F "fileContent=@file.bin;type=application/octet-stream" ``` ### GET `/v1/assets/{assetId}` [BETA] Retrieve specific asset metadata. Include the `readMask` parameter for additional asset metadata. Retrieve specific asset metadata. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `asset:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetId` | path | `string` | Yes | The unique identifier of the asset. | | `readMask` | query | `string` | No | Asset metadata fields to retrieve, including the description, display name, icon, social links, and previews. Examples: `description%2CdisplayName`, `previews%2CtwitchSocialLink`. | **Responses:** - `200`: Asset resource retrieved successfully. → `Asset` - `400`: Malformed request, likely due to an invalid read mask. - `401`: The API key is not valid for this operation / You don't have the authorization. - `403`: Doesn't have the required permission. - `404`: Asset doesn't exist. - `500`: Server internal error / Unknown error. **Response fields** (`Asset`) See [Asset](#asset) in Models. **Response example:** ```json { "assetType": "string", "assetId": 0, "creationContext": { "assetPrivacy": "default", "creator": { "userId": "...", "groupId": "..." }, "expectedPrice": 0 }, "description": "string", "displayName": "string", "path": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perIp: 120/minute, perApiKeyOwner: 120/minute, perOauth2Authorization: 120/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/assets/v1/assets/{ASSETID}" ``` ### PATCH `/v1/assets/{assetId}` [BETA] Updates an asset with provided content and metadata. Updates an asset with provided content and metadata, including the description, display name, icon, social links, and previews. Currently can only update the content body for **Models**. Icons and Previews must be **Image** assets. Icons must have square dimensions. Provide the [Asset](#Asset), binary asset file path, and [content type](/docs/en-us/cloud/guides/usage-assets.md#supported-asset-types-and-limits) in the form data. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `asset:read`, `asset:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetId` | path | `string` | Yes | The unique identifier of the asset. | | `updateMask` | query | `string` | No | Asset metadata fields to update, including the description, display name, icon, and previews. Examples: `description%2CdisplayName`, `previews%2CtwitchSocialLink`. | **Request Body:** `multipart/form-data` — Type: `object` | Property | Type | Required | Description | |----------|------|----------|-------------| | `request` | [Asset](#asset) | Yes | Asset attributes to update. | | `request.assetType` | `string` | No | The asset type. Required for [Create Asset](#POST-v1-assets). | | `request.assetId` | `integer` | No | The unique identifier of the asset. Required for [Update Asset](#PATCH-v1-assets-_asset_). | | `request.creationContext` | [CreationContext](#creationcontext) | No | | | `request.description` | `string` | No | The description of the asset. Limit to 1000 characters. Required for [Create Asset](#POST-v1-assets) | | `request.displayName` | `string` | No | Display name of the asset. Required for [Create Asset](#POST-v1-assets). | | `request.path` | `string` | No | The returned resource path of the asset. Format: `assets/{assetId}`. Example: `assets/2205400862`. | | `fileContent` | `string` | Yes | The binary asset file path and the content type. See [Asset types and limits](/docs/en-us/cloud/guides/usage-assets.md#supported-as | **Request example:** ```json { "request": { "assetType": "string", "assetId": 0, "creationContext": { "assetPrivacy": "...", "creator": "...", "expectedPrice": "..." }, "description": "string", "displayName": "string", "path": "string" }, "fileContent": "string" } ``` > **Verify mutations:** If your API key lacks the required scope (`asset:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: Returns the Operation ID for checking the update status / Returns the updated metadata fields. → `Operation` - `400`: Invalid argument. Failed to parse the request or the file. → `Status` - `401`: The API key is not valid for this operation / You don't have the authorization. - `500`: Server internal error / Unknown error. **Response fields** (`Operation`) See [Operation](#operation) in Models. **Response example:** ```json { "path": "string", "done": false, "error": { "code": 0, "message": "string" }, "response": { "assetType": "string", "assetId": 0, "creationContext": { "assetPrivacy": "...", "creator": "...", "expectedPrice": "..." }, "description": "string", "displayName": "string", "path": "string" } } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Rate Limits:** perIp: 120/minute, perApiKeyOwner: 120/minute, perOauth2Authorization: 120/minute **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/assets/v1/assets/{ASSETID}" \ -F "request={"assetType":"string","assetId":0,"creationContext":{"assetPrivacy":"default","creator":{"userId":"...","groupId":"..."},"expectedPrice":0},"description":"string","displayName":"string","path":"string"};type=application/json" \ -F "fileContent=@file.bin;type=application/octet-stream" ``` ### POST `/v1/assets/{assetId}:archive` [BETA] Archives the asset. Archives the asset. Archived assets disappear from the website and are no longer usable or visible in Roblox experiences, but you can [restore](#POST-v1-assets-{assetId}:restore) them. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `asset:read`, `asset:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetId` | path | `string` | Yes | The unique identifier of the asset. | > **Verify mutations:** If your API key lacks the required scope (`asset:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: Asset archived succesfully successfully. → `Asset` - `400`: Bad request - invalid request. - `403`: Forbidden - API key without Write scope or user doesn't have access. - `404`: Asset not found. **Response fields** (`Asset`) See [Asset](#asset) in Models. **Response example:** ```json { "assetType": "string", "assetId": 0, "creationContext": { "assetPrivacy": "default", "creator": { "userId": "...", "groupId": "..." }, "expectedPrice": 0 }, "description": "string", "displayName": "string", "path": "string" } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perIp: 100/minute, perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/assets/v1/assets/{ASSETID}:archive" ``` ### POST `/v1/assets/{assetId}:restore` [BETA] Restores an archived asset. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `asset:read`, `asset:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetId` | path | `string` | Yes | The unique identifier of the asset. | > **Verify mutations:** If your API key lacks the required scope (`asset:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: Asset restored successfully. → `Asset` - `400`: Bad request - invalid request. - `403`: Forbidden - API key without Write scope or user doesn't have access. - `404`: Asset not found. **Response fields** (`Asset`) See [Asset](#asset) in Models. **Response example:** ```json { "assetType": "string", "assetId": 0, "creationContext": { "assetPrivacy": "default", "creator": { "userId": "...", "groupId": "..." }, "expectedPrice": 0 }, "description": "string", "displayName": "string", "path": "string" } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perIp: 100/minute, perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/assets/v1/assets/{ASSETID}:restore" ``` ### GET `/v1/assets/{assetId}/versions/{versionNumber}` [BETA] Get Asset Version Retrieve a specific asset version by the asset ID and the version number. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `asset:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetId` | path | `string` | Yes | The unique identifier of the asset. | | `versionNumber` | path | `string` | Yes | The version number. | **Responses:** - `200`: Asset version retrieved successfully. → `AssetVersion` - `403`: Forbidden - API key without Read scope or user doesn't have access. - `404`: Asset or Asset Version not found. **Response fields** (`AssetVersion`) See [AssetVersion](#assetversion) in Models. **Response example:** ```json { "creationContext": { "assetPrivacy": "default", "creator": { "userId": "...", "groupId": "..." }, "expectedPrice": 0 }, "path": "string", "moderationResult": { "moderationState": "string" }, "published": false } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perIp: 100/minute, perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/assets/v1/assets/{ASSETID}/versions/{VERSIONNUMBER}" ``` ### GET `/v1/assets/{assetId}/versions` [BETA] List Asset Versions of an Asset List all versions of a specific asset, with optional pagination. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `asset:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetId` | path | `string` | Yes | The unique identifier of the asset. | | `maxPageSize` | query | `integer` | No | Specifies the number of asset versions to include in the response. Valid values range from 1 to 50 (inclusive). Defaults to 8 when not provided. | | `pageToken` | query | `string` | No | A token for pagination. The value is obtained from a previous request and allows for retrieving the next page of asset versions. | **Responses:** - `200`: Asset versions listed successfully. → `AssetVersion[]` - `400`: Bad request - invalid parameters. - `403`: Forbidden - API key without Read scope or user doesn't have access. - `404`: Asset not found. **Response fields** (`AssetVersion[]`) See [AssetVersion](#assetversion) in Models. **Error handling:** `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perIp: 100/minute, perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/assets/v1/assets/{ASSETID}/versions" ``` ### POST `/v1/assets/{assetId}/versions:rollback` [BETA] Rollback an asset to a previous version. Rollback an asset to a specific previous version. Provide the asset version path in the form data. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `asset:read`, `asset:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetId` | path | `string` | Yes | The unique identifier of the asset. | **Request Body:** `multipart/form-data` — Type: `object` | Property | Type | Required | Description | |----------|------|----------|-------------| | `assetVersion` | `string` | Yes | The asset version path in the format of `assets/{assetId}/versions/{versionNumber}`. | **Request example:** ```json { "assetVersion": "string" } ``` > **Verify mutations:** If your API key lacks the required scope (`asset:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: Asset rolled back successfully. → `AssetVersion` - `400`: Bad request - invalid request body. - `403`: Forbidden - API key without Write scope or user doesn't have access. - `404`: Asset or Asset Version not found. **Response fields** (`AssetVersion`) See [AssetVersion](#assetversion) in Models. **Response example:** ```json { "creationContext": { "assetPrivacy": "default", "creator": { "userId": "...", "groupId": "..." }, "expectedPrice": 0 }, "path": "string", "moderationResult": { "moderationState": "string" }, "published": false } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perIp: 100/minute, perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/assets/v1/assets/{ASSETID}/versions:rollback" \ -F "assetVersion=string" ``` ### GET `/v1/operations/{operationId}` [BETA] Get the result of an asset creation or update. Get the result of an asset creation or update using the returned Operation ID. Requires **Read** for the API key permission and **asset:read** for OAuth 2.0 apps. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `asset:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `operationId` | path | `string` | Yes | The unique identifier of the operation. | **Responses:** - `200`: Operation result retrieved successfully. → `Operation` - `400`: Invalid argument. Failed to parse the request or the file. → `Status` - `401`: The API key is not valid for this operation / You don't have the authorization. - `500`: Server internal error / Unknown error. **Response fields** (`Operation`) See [Operation](#operation) in Models. **Response example:** ```json { "path": "string", "done": false, "error": { "code": 0, "message": "string" }, "response": { "assetType": "string", "assetId": 0, "creationContext": { "assetPrivacy": "...", "creator": "...", "expectedPrice": "..." }, "description": "string", "displayName": "string", "path": "string" } } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Rate Limits:** perIp: 300/minute, perApiKeyOwner: 300/minute, perOauth2Authorization: 300/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/assets/v1/operations/{OPERATIONID}" ``` ## Models ### Asset Represents an asset. | Property | Type | Required | Description | |----------|------|----------|-------------| | `assetType` | `string` | No | The asset type. Required for [Create Asset](#POST-v1-assets). | | `assetId` | `integer` | No | The unique identifier of the asset. Required for [Update Asset](#PATCH-v1-assets-_asset_). | | `creationContext` | `CreationContext` | No | | | `description` | `string` | No | The description of the asset. Limit to 1000 characters. Required for [Create Asset](#POST-v1-assets). | | `displayName` | `string` | No | Display name of the asset. Required for [Create Asset](#POST-v1-assets). | | `path` | `string` | No | The returned resource path of the asset. Format: `assets/{assetId}`. Example: `assets/2205400862`. | | `revisionId` | `string` | No | Revision ID of the asset. Equivalent to `versionNumber`. Every change of the asset automatically commits a new version. The format is an integer string. Example: `1`. | | `revisionCreateTime` | `string` | No | The creation timestamp of the current revision. | | `moderationResult` | `ModerationResult` | No | | | `icon` | `string` | No | The resource path for the icon. | | `previews` | `Preview[]` | No | A list of previews, each with an asset path and alt text. Previews must be **Image** assets. | | `state` | `State` | No | | | `socialLink` | `SocialLink` | No | | ### AssetVersion An asset version. | Property | Type | Required | Description | |----------|------|----------|-------------| | `creationContext` | `CreationContext` | No | | | `path` | `string` | No | The returned resource path of the asset version. Format: `assets/{assetId}/versions/{version}`. Example: `assets/2205400862/versions/1`. | | `moderationResult` | `ModerationResult` | No | | | `published` | `boolean` | No | Only applies to place asset types. Indicates if the place has been published or not. | ### CreationContext The context of creation that is not part of the asset content, such as metadata and creator information. Required for [Create Asset](#POST-v1-assets). | Property | Type | Required | Description | |----------|------|----------|-------------| | `assetPrivacy` | `AssetPrivacy` | No | Desired privacy setting for the asset on creation. Only applies to asset types that support privacy override. | | `creator` | `Creator` | No | | | `expectedPrice` | `integer` | No | Expected asset upload fee in Robux. When the actual price is more than expected, the operation fails with a 400 error. | ### Creator Represents a creator. | Property | Type | Required | Description | |----------|------|----------|-------------| | `userId` | `integer` | No | The User ID the creator. Required if the asset is individual-user-owned. | | `groupId` | `integer` | No | The Group ID. Required if the asset is group-owned. | ### ModerationResult The moderation result of the asset. | Property | Type | Required | Description | |----------|------|----------|-------------| | `moderationState` | `string` | No | The moderation state of the asset. Can be `Reviewing`, `Rejected`, or `Approved`. | ### Operation This resource represents a long-running operation that is the result of a network API call. | Property | Type | Required | Description | |----------|------|----------|-------------| | `path` | `string` | No | The server-assigned resource path. The default format is `operations/{operation_id}`. | | `done` | `boolean` | No | If `false`, the operation is still in progress. If `true`, the operation is completed. | | `error` | `Status` | No | | | `response` | `Asset` | No | | ### Preview An asset preview. | Property | Type | Required | Description | |----------|------|----------|-------------| | `asset` | `string` | No | The preview asset path. | | `altText` | `string` | No | Alt text for the preview asset. | ### SocialLink A social media link for the asset. Maximum of three per asset. Object name can be any of: For syntax, see the sample request under [Update Asset](#PATCH-v1-assets-_assetId_). | Property | Type | Required | Description | |----------|------|----------|-------------| | `title` | `string` | No | An optional title for the social media link. Not used on the Creator Hub. | | `uri` | `string` | No | The URI for the social media link. Must match the expected format for the type of link. For example, the title for a `twitchSocialLink` object must be of the format `https://twitch.tv/your-channel`. | ### Status The logical error model explaining the error status. | Property | Type | Required | Description | |----------|------|----------|-------------| | `code` | `integer` | No | The HTTP status code. | | `message` | `string` | No | The error message. | --- name: "Cloud API" last_updated: 2026-06-29T19:34:06Z type: opencloud api_base_url: "https://apis.roblox.com" version: "2.0.1" endpoints: 78 description: "The Roblox Open Cloud v2 API" --- # Cloud API The Roblox Open Cloud v2 API. This is a subset of endpoints that Roblox provides. See https://create.roblox.com/docs/cloud/open-cloud for more details. **API Version:** 2.0.1 **Base URL(s):** - `https://apis.roblox.com` ## Endpoints ### POST `/cloud/v2/creator-store-products` Create Creator Store Product Add a Creator Store product. Only use this method if your product has never been distributed on the Creator Store; otherwise, use the `PATCH` method to update the product. **Scopes:** `creator-store-product:write` **Request Body:** `application/json` — Type: `CreatorStoreProduct` See [CreatorStoreProduct](#creatorstoreproduct) in Models. **Request example:** ```json { "path": "string", "basePrice": { "currencyCode": "string", "quantity": { "significand": "...", "exponent": "..." } }, "purchasePrice": { "currencyCode": "string", "quantity": { "significand": "...", "exponent": "..." } }, "published": false, "restrictions": [ "RESTRICTION_UNSPECIFIED" ], "purchasable": false } ``` > **Verify mutations:** If your API key lacks the required scope (`creator-store-product:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `CreatorStoreProduct` **Response fields** (`CreatorStoreProduct`) See [CreatorStoreProduct](#creatorstoreproduct) in Models. **Response example:** ```json { "path": "string", "basePrice": { "currencyCode": "string", "quantity": { "significand": "...", "exponent": "..." } }, "purchasePrice": { "currencyCode": "string", "quantity": { "significand": "...", "exponent": "..." } }, "published": false, "restrictions": [ "RESTRICTION_UNSPECIFIED" ], "purchasable": false } ``` **Example:** ```bash curl -X POST -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/creator-store-products" \ -H "Content-Type: application/json" \ -d '{"path":"string","basePrice":{"currencyCode":"string","quantity":{"significand":"...","exponent":"..."}},"purchasePrice":{"currencyCode":"string","quantity":{"significand":"...","exponent":"..."}},"published":false,"restrictions":["RESTRICTION_UNSPECIFIED"],"purchasable":false}' ``` ### GET `/cloud/v2/creator-store-products/{creator_store_product_id}` Get Creator Store Product Get a Creator Store product. **Scopes:** `creator-store-product:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `creator_store_product_id` | path | `string` | Yes | The creator-store-product ID. | **Responses:** - `200`: OK → `CreatorStoreProduct` **Response fields** (`CreatorStoreProduct`) See [CreatorStoreProduct](#creatorstoreproduct) in Models. **Response example:** ```json { "path": "string", "basePrice": { "currencyCode": "string", "quantity": { "significand": "...", "exponent": "..." } }, "purchasePrice": { "currencyCode": "string", "quantity": { "significand": "...", "exponent": "..." } }, "published": false, "restrictions": [ "RESTRICTION_UNSPECIFIED" ], "purchasable": false } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/creator-store-products/{CREATOR_STORE_PRODUCT_ID}" ``` ### PATCH `/cloud/v2/creator-store-products/{creator_store_product_id}` Update Creator Store Product Update a Creator Store product. **Scopes:** `creator-store-product:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `creator_store_product_id` | path | `string` | Yes | The creator-store-product ID. | | `updateMask` | query | `string` | No | The list of fields to update. | | `allowMissing` | query | `boolean` | No | If set to true, and the creator store product is not found, a creator store product is created. In this situation, `update_mask` is ignored. | **Request Body:** `application/json` — Type: `CreatorStoreProduct` See [CreatorStoreProduct](#creatorstoreproduct) in Models. **Request example:** ```json { "path": "string", "basePrice": { "currencyCode": "string", "quantity": { "significand": "...", "exponent": "..." } }, "purchasePrice": { "currencyCode": "string", "quantity": { "significand": "...", "exponent": "..." } }, "published": false, "restrictions": [ "RESTRICTION_UNSPECIFIED" ], "purchasable": false } ``` > **Verify mutations:** If your API key lacks the required scope (`creator-store-product:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `CreatorStoreProduct` **Response fields** (`CreatorStoreProduct`) See [CreatorStoreProduct](#creatorstoreproduct) in Models. **Response example:** ```json { "path": "string", "basePrice": { "currencyCode": "string", "quantity": { "significand": "...", "exponent": "..." } }, "purchasePrice": { "currencyCode": "string", "quantity": { "significand": "...", "exponent": "..." } }, "published": false, "restrictions": [ "RESTRICTION_UNSPECIFIED" ], "purchasable": false } ``` **Example:** ```bash curl -X PATCH -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/creator-store-products/{CREATOR_STORE_PRODUCT_ID}" \ -H "Content-Type: application/json" \ -d '{"path":"string","basePrice":{"currencyCode":"string","quantity":{"significand":"...","exponent":"..."}},"purchasePrice":{"currencyCode":"string","quantity":{"significand":"...","exponent":"..."}},"published":false,"restrictions":["RESTRICTION_UNSPECIFIED"],"purchasable":false}' ``` ### GET `/cloud/v2/groups/{group_id}` Get Group Gets the specified group. **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `group_id` | path | `string` | Yes | The group ID. | **Responses:** - `200`: OK → `Group` **Response fields** (`Group`) See [Group](#group) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:00Z", "id": "string", "displayName": "string", "description": "string" } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/groups/{GROUP_ID}" ``` ### GET `/cloud/v2/groups/{group_id}/forum-categories` List Group Forum Categories Lists forum categories in the group. Supports filtering. **Scopes:** `group-forum:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `group_id` | path | `string` | Yes | The group ID. | | `maxPageSize` | query | `integer` | No | The maximum number of group forum categories to return. The service might return fewer than this value. If unspecified, at most 10 group forum categories are returned. The maximum value is 10 and higher values are set to 10. | | `pageToken` | query | `string` | No | A page token, received from a previous call, to retrieve a subsequent page. When paginating, all other parameters provided to the subsequent call must match the call that provided the page token. | | `filter` | query | `string` | No | This field may be set in order to filter the resources returned. Filtering conforms to Common Expression Language (CEL). Only the boolean field `archived` and `==` operator are supported. If `archived=false`, archived categories are not returned. The default value is `false`. Example: `"filter=archived==true"` | **Responses:** - `200`: OK → `ListGroupForumCategoriesResponse` **Response fields** (`ListGroupForumCategoriesResponse`) See [ListGroupForumCategoriesResponse](#listgroupforumcategoriesresponse) in Models. **Response example:** ```json { "groupForumCategories": [ { "path": "...", "createTime": "...", "updateTime": "...", "groupForumCategoryId": "...", "displayName": "...", "creator": "..." } ], "nextPageToken": "string" } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/groups/{GROUP_ID}/forum-categories" ``` ### GET `/cloud/v2/groups/{group_id}/forum-categories/{forum_category_id}/posts` List Group Forum Posts Lists forum posts in the group's forum category. Supports filtering. **Scopes:** `group-forum:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `group_id` | path | `string` | Yes | The group ID. | | `forum_category_id` | path | `string` | Yes | The forum-category ID. | | `maxPageSize` | query | `integer` | No | The maximum number of group forum posts to return. The service might return fewer than this value. If unspecified, at most 10 group forum posts are returned. The maximum value is 100 and higher values are set to 100. | | `pageToken` | query | `string` | No | A page token, received from a previous call, to retrieve a subsequent page. When paginating, all other parameters provided to the subsequent call must match the call that provided the page token. | | `filter` | query | `string` | No | This field may be set in order to filter the resources returned. Filtering conforms to Common Expression Language (CEL). Only the query `filter=pinned==true` is supported, where only pinned posts are returned. `filter=pinned==false`is not supported. | | `view` | query | `VIEW_UNSPECIFIED \| FULL \| FULL_WITH_FIRST_COMMENT` | No | The view in which to retrieve the group forum post. Supports FULL and FULL_WITH_FIRST_COMMENT. Defaults to FULL. Possible values: \| Value \| Description \| \| --- \| --- \| \| VIEW_UNSPECIFIED \| The group forum post view is not specified; the default will be used. \| \| FULL \| Includes all fields but does not dereference `first_comment`. Only the `path` field is populated. \| \| FULL_WITH_FIRST_COMMENT \| Includes all fields and also fully populates `first_comment`. The default view. \| Valid values: `VIEW_UNSPECIFIED`, `FULL`, `FULL_WITH_FIRST_COMMENT` | **Responses:** - `200`: OK → `ListGroupForumPostsResponse` **Response fields** (`ListGroupForumPostsResponse`) See [ListGroupForumPostsResponse](#listgroupforumpostsresponse) in Models. **Response example:** ```json { "groupForumPosts": [ { "path": "...", "createTime": "...", "updateTime": "...", "pinned": "...", "locked": "...", "groupForumPostId": "..." } ], "nextPageToken": "string" } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/groups/{GROUP_ID}/forum-categories/{FORUM_CATEGORY_ID}/posts" ``` ### GET `/cloud/v2/groups/{group_id}/forum-categories/{forum_category_id}/posts/{post_id}/comments` List Group Forum Comments Lists forum comments on a group's forum post. Supports filtering. **Scopes:** `group-forum:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `group_id` | path | `string` | Yes | The group ID. | | `forum_category_id` | path | `string` | Yes | The forum-category ID. | | `post_id` | path | `string` | Yes | The post ID. | | `maxPageSize` | query | `integer` | No | The maximum number of group forum comments to return. The service might return fewer than this value. If unspecified, at most 10 group forum comments are returned. The maximum value is 100 and higher values are set to 100. | | `pageToken` | query | `string` | No | A page token, received from a previous call, to retrieve a subsequent page. When paginating, all other parameters provided to the subsequent call must match the call that provided the page token. | | `filter` | query | `string` | No | This field may be set in order to filter the resources returned. Filtering conforms to Common Expression Language (CEL). Only the string field `repliesToCommentId` and the `==` operator are supported. If `repliesToCommentId` is specified, only comments that respond to the comment are returned. The default value is `''`. If `repliesToCommentId=''`, only comments responding directly to the post are returned. Example: `"filter=repliesToCommentId==01234567-89ab-cdef-0123-456789abcdef"` | **Responses:** - `200`: OK → `ListGroupForumCommentsResponse` **Response fields** (`ListGroupForumCommentsResponse`) See [ListGroupForumCommentsResponse](#listgroupforumcommentsresponse) in Models. **Response example:** ```json { "groupForumComments": [ { "path": "...", "groupForumCommentId": "...", "message": "..." } ], "nextPageToken": "string" } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/groups/{GROUP_ID}/forum-categories/{FORUM_CATEGORY_ID}/posts/{POST_ID}/comments" ``` ### GET `/cloud/v2/groups/{group_id}/join-requests` List Group Join Requests List join requests under a group. Supports filtering. **Scopes:** `group:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `group_id` | path | `string` | Yes | The group ID. | | `maxPageSize` | query | `integer` | No | The maximum number of group join requests to return. The service might return fewer than this value. If unspecified, at most 10 group join requests are returned. The maximum value is 20 and higher values are set to 20. | | `pageToken` | query | `string` | No | A page token, received from a previous call, to retrieve a subsequent page. When paginating, all other parameters provided to the subsequent call must match the call that provided the page token. | | `filter` | query | `string` | No | This field may be set in order to filter the resources returned. Filtering conforms to Common Expression Language (CEL). Only the `user` field and `==` operator are supported. Example: `"user == 'users/156'"` | **Responses:** - `200`: OK → `ListGroupJoinRequestsResponse` **Response fields** (`ListGroupJoinRequestsResponse`) See [ListGroupJoinRequestsResponse](#listgroupjoinrequestsresponse) in Models. **Response example:** ```json { "groupJoinRequests": [ { "path": "...", "createTime": "...", "user": "..." } ], "nextPageToken": "string" } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/groups/{GROUP_ID}/join-requests" ``` ### POST `/cloud/v2/groups/{group_id}/join-requests/{join_request_id}:accept` Accept Group Join Request Accepts a join request. **Scopes:** `group:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `group_id` | path | `string` | Yes | The group ID. | | `join_request_id` | path | `string` | Yes | The join-request ID. | **Request Body:** `application/json` — Type: `AcceptGroupJoinRequestRequest` See [AcceptGroupJoinRequestRequest](#acceptgroupjoinrequestrequest) in Models. **Request example:** ```json {} ``` > **Verify mutations:** If your API key lacks the required scope (`group:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK **Example:** ```bash curl -X POST -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/groups/{GROUP_ID}/join-requests/{JOIN_REQUEST_ID}:accept" \ -H "Content-Type: application/json" \ -d '{}' ``` ### POST `/cloud/v2/groups/{group_id}/join-requests/{join_request_id}:decline` Decline Group Join Request Declines a join request. **Scopes:** `group:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `group_id` | path | `string` | Yes | The group ID. | | `join_request_id` | path | `string` | Yes | The join-request ID. | **Request Body:** `application/json` — Type: `DeclineGroupJoinRequestRequest` See [DeclineGroupJoinRequestRequest](#declinegroupjoinrequestrequest) in Models. **Request example:** ```json {} ``` > **Verify mutations:** If your API key lacks the required scope (`group:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK **Example:** ```bash curl -X POST -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/groups/{GROUP_ID}/join-requests/{JOIN_REQUEST_ID}:decline" \ -H "Content-Type: application/json" \ -d '{}' ``` ### GET `/cloud/v2/groups/{group_id}/memberships` List Group Memberships List group members in a group. Supports filtering. **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `group_id` | path | `string` | Yes | The group ID. | | `maxPageSize` | query | `integer` | No | The maximum number of group memberships to return. The service might return fewer than this value. If unspecified, at most 10 group memberships are returned. The maximum value is 100 and higher values are set to 100. | | `pageToken` | query | `string` | No | A page token, received from a previous call, to retrieve a subsequent page. When paginating, all other parameters provided to the subsequent call must match the call that provided the page token. | | `filter` | query | `string` | No | This field may be set in order to filter the resources returned. See the [filtering](/docs/en-us/cloud/reference/patterns.md#list-group-memberships) documentation for more information. | **Responses:** - `200`: OK → `ListGroupMembershipsResponse` **Response fields** (`ListGroupMembershipsResponse`) See [ListGroupMembershipsResponse](#listgroupmembershipsresponse) in Models. **Response example:** ```json { "groupMemberships": [ { "path": "...", "createTime": "...", "updateTime": "...", "user": "...", "role": "...", "roles": "..." } ], "nextPageToken": "string" } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/groups/{GROUP_ID}/memberships" ``` ### PATCH `/cloud/v2/groups/{group_id}/memberships/{membership_id}` Update Group Membership **Deprecated.** Use AssignGroupRole and UnassignGroupRole instead. Updates the group membership for a particular group member. This action requires the requester to be able to manage lower ranked members. Guest or Owner ranks cannot be assigned, and a requester cannot change their own rank. **Scopes:** `group:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `group_id` | path | `string` | Yes | The group ID. | | `membership_id` | path | `string` | Yes | The membership ID. | **Request Body:** `application/json` — Type: `GroupMembership` See [GroupMembership](#groupmembership) in Models. **Request example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:00Z", "user": "string", "role": "string", "roles": [ "string" ] } ``` > **Verify mutations:** If your API key lacks the required scope (`group:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `GroupMembership` **Response fields** (`GroupMembership`) See [GroupMembership](#groupmembership) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:00Z", "user": "string", "role": "string", "roles": [ "string" ] } ``` **Example:** ```bash curl -X PATCH -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/groups/{GROUP_ID}/memberships/{MEMBERSHIP_ID}" \ -H "Content-Type: application/json" \ -d '{ "path": "string", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:00Z", "user": "string", "role": "string", "roles": [ "string" ] }' ``` ### POST `/cloud/v2/groups/{group_id}/memberships/{membership_id}:assignRole` Assign Role Group Membership Assigns a specific role to a user within a group. If the user already holds the specified role, no action is taken. **Scopes:** `group:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `group_id` | path | `string` | Yes | The group ID. | | `membership_id` | path | `string` | Yes | The membership ID. | **Request Body:** `application/json` — Type: `AssignRoleGroupMembershipRequest` See [AssignRoleGroupMembershipRequest](#assignrolegroupmembershiprequest) in Models. **Request example:** ```json { "role": "string" } ``` > **Verify mutations:** If your API key lacks the required scope (`group:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `GroupMembership` **Response fields** (`GroupMembership`) See [GroupMembership](#groupmembership) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:00Z", "user": "string", "role": "string", "roles": [ "string" ] } ``` **Example:** ```bash curl -X POST -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/groups/{GROUP_ID}/memberships/{MEMBERSHIP_ID}:assignRole" \ -H "Content-Type: application/json" \ -d '{ "role": "string" }' ``` ### POST `/cloud/v2/groups/{group_id}/memberships/{membership_id}:unassignRole` Unassign Role Group Membership Unassigns a specific role from a user within a group. If the user does not hold the specified role, no action is taken. **Scopes:** `group:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `group_id` | path | `string` | Yes | The group ID. | | `membership_id` | path | `string` | Yes | The membership ID. | **Request Body:** `application/json` — Type: `UnassignRoleGroupMembershipRequest` See [UnassignRoleGroupMembershipRequest](#unassignrolegroupmembershiprequest) in Models. **Request example:** ```json { "role": "string" } ``` > **Verify mutations:** If your API key lacks the required scope (`group:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `GroupMembership` **Response fields** (`GroupMembership`) See [GroupMembership](#groupmembership) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:00Z", "user": "string", "role": "string", "roles": [ "string" ] } ``` **Example:** ```bash curl -X POST -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/groups/{GROUP_ID}/memberships/{MEMBERSHIP_ID}:unassignRole" \ -H "Content-Type: application/json" \ -d '{ "role": "string" }' ``` ### GET `/cloud/v2/groups/{group_id}/roles` List Group Roles List roles in a group. The permissions field for roles is viewable based on the requester's access and scopes. Permissions for the guest role are always visible - a scope is not needed. If the requester is a member of the group and has the `group:read` scope, permissions in their role are visible. If the requester is the owner of the group and has the `group:read` scope, permissions in all roles are visible. **Scopes:** `group:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `group_id` | path | `string` | Yes | The group ID. | | `maxPageSize` | query | `integer` | No | The maximum number of group roles to return. The service might return fewer than this value. If unspecified, at most 10 group roles are returned. The maximum value is 20 and higher values are set to 20. | | `pageToken` | query | `string` | No | A page token, received from a previous call, to retrieve a subsequent page. When paginating, all other parameters provided to the subsequent call must match the call that provided the page token. | **Responses:** - `200`: OK → `ListGroupRolesResponse` **Response fields** (`ListGroupRolesResponse`) See [ListGroupRolesResponse](#listgrouprolesresponse) in Models. **Response example:** ```json { "groupRoles": [ { "path": "...", "createTime": "...", "updateTime": "...", "id": "...", "displayName": "...", "description": "..." } ], "nextPageToken": "string" } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/groups/{GROUP_ID}/roles" ``` ### GET `/cloud/v2/groups/{group_id}/roles/{role_id}` Get Group Role Get the group role **Scopes:** `group:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `group_id` | path | `string` | Yes | The group ID. | | `role_id` | path | `string` | Yes | The role ID. | **Responses:** - `200`: OK → `GroupRole` **Response fields** (`GroupRole`) See [GroupRole](#grouprole) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:00Z", "id": "string", "displayName": "string", "description": "string" } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/groups/{GROUP_ID}/roles/{ROLE_ID}" ``` ### GET `/cloud/v2/universes/{universe_id}` Get Universe Gets the specified universe. **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | **Responses:** - `200`: OK → `Universe` **Response fields** (`Universe`) See [Universe](#universe) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:00Z", "displayName": "string", "description": "string", "user": "string" } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}" ``` ### PATCH `/cloud/v2/universes/{universe_id}` Update Universe Updates the specified universe. This method is guaranteed to return all updated fields. This method may additionally return the full resource. **Scopes:** `universe:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `updateMask` | query | `string` | No | The list of fields to update. | **Request Body:** `application/json` — Type: `Universe` See [Universe](#universe) in Models. **Request example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:00Z", "displayName": "string", "description": "string", "user": "string" } ``` > **Verify mutations:** If your API key lacks the required scope (`universe:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Universe` **Response fields** (`Universe`) See [Universe](#universe) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:00Z", "displayName": "string", "description": "string", "user": "string" } ``` **Example:** ```bash curl -X PATCH -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}" \ -H "Content-Type: application/json" \ -d '{ "path": "string", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:00Z", "displayName": "string", "description": "string", "user": "string" }' ``` ### GET `/cloud/v2/universes/{universe_id}/data-stores` List Data Stores Returns a list of data stores. Data stores scheduled for permanent deletion are omitted from the results by default (or when `showDeleted` is set to `false`). When this is the case, the operation will check up to 512 data stores. If all checked data stores are deleted, it will return an empty list with a page token to continue iteration. **Scopes:** `universe-datastores.control:list` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `maxPageSize` | query | `integer` | No | The maximum number of data stores to return. The service might return fewer than this value. If unspecified, at most 10 data stores are returned. The maximum value is 100 and higher values are set to 100. | | `pageToken` | query | `string` | No | A page token, received from a previous call, to retrieve a subsequent page. When paginating, all other parameters provided to the subsequent call must match the call that provided the page token. | | `filter` | query | `string` | No | This field may be set in order to filter the resources returned. The `filter` field supports a very small subset of CEL: * Only the `id` field is supported. * Only the `startsWith` function is available; no other operators nor built-ins are supported. Example filter: `id.startsWith("foo")` | | `showDeleted` | query | `boolean` | No | If true, resources marked for pending deletion will be included in the results. | **Responses:** - `200`: OK → `ListDataStoresResponse` **Response fields** (`ListDataStoresResponse`) See [ListDataStoresResponse](#listdatastoresresponse) in Models. **Response example:** ```json { "dataStores": [ { "path": "...", "createTime": "...", "expireTime": "...", "state": "...", "id": "..." } ], "nextPageToken": "string" } ``` **Rate Limit Note:** Data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/data-stores" ``` ### DELETE `/cloud/v2/universes/{universe_id}/data-stores/{data_store_id}` Delete Data Store Schedules the specified data store for permanent deletion. This operation assigns the data store an expiry time 30 days in the future, at which point permanent deletion begins. To cancel, use the `UndeleteDataStore` operation before the data store's expiry time. Permanent deletion consists of deleting all of the entries in the data store and then the data store resource itself. The data store is no longer returned by the `ListDataStores` Open Cloud endpoint or `ListDataStoresAsync` Luau API, and you can reuse the data store's name. The duration of the permanent deletion process depends on the number of entries in the data store. However, you can expect a data store with 1 million or fewer entries to be permanently deleted within 3 days. Data stores scheduled for permanent deletion are returned by the `ListDataStores` Open Cloud endpoint when the query parameter `showDeleted` is set to `true`. In the return value, each data store will have a `DELETED` state and an `expireTime` field. Data stores scheduled for permanent deletion are immediately made inaccessible, meaning attempts to read or write to their entries will fail. Note: Due to caching in the backend service, attempts to read from or write to entries in these data stores can continue to succeed for a limited time after the deletion: * `GetDataStoreEntry`: can succeed for up to 24 hours. * All other endpoints: can succeed for several minutes. If the data store is already in a `DELETED` state, this operation is a no-op, and the data store is returned as-is. **Scopes:** `universe-datastores.control:delete` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `data_store_id` | path | `string` | Yes | The data-store ID. | **Responses:** - `200`: OK → `DataStore` **Response fields** (`DataStore`) See [DataStore](#datastore) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "expireTime": "2024-01-01T00:00:00Z", "state": "STATE_UNSPECIFIED", "id": "string" } ``` **Rate Limit Note:** Data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -X DELETE -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/data-stores/{DATA_STORE_ID}" ``` ### GET `/cloud/v2/universes/{universe_id}/data-stores/{data_store_id}/entries` List Data Store Entries Returns a list of entries from a data store. Only the `path` and `id` fields are populated; use `GetDataStoreEntry` to retrieve other fields. Specify the wildcard scope (`-`) to list entries from all scopes. **Scopes:** `universe-datastores.objects:list` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `data_store_id` | path | `string` | Yes | The data-store ID. | | `maxPageSize` | query | `integer` | No | The maximum number of data store entries to return. The service might return fewer than this value. If unspecified, at most 10 data store entries are returned. The maximum value is 256 and higher values are set to 256. | | `pageToken` | query | `string` | No | A page token, received from a previous call, to retrieve a subsequent page. When paginating, all other parameters provided to the subsequent call must match the call that provided the page token. | | `filter` | query | `string` | No | This field may be set in order to filter the resources returned. The `filter` field supports a very small subset of CEL: * Only the `id` field is supported. * Only the `startsWith` function is available; no other operators nor built-ins are supported. Example filter: `id.startsWith("foo")` | | `showDeleted` | query | `boolean` | No | If true, resources marked for pending deletion will be included in the results. | **Responses:** - `200`: OK → `ListDataStoreEntriesResponse` **Response fields** (`ListDataStoreEntriesResponse`) See [ListDataStoreEntriesResponse](#listdatastoreentriesresponse) in Models. **Response example:** ```json { "dataStoreEntries": [ { "path": "...", "createTime": "...", "revisionId": "...", "revisionCreateTime": "...", "state": "...", "etag": "..." } ], "nextPageToken": "string" } ``` **Rate Limit Note:** Data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/data-stores/{DATA_STORE_ID}/entries" ``` ### POST `/cloud/v2/universes/{universe_id}/data-stores/{data_store_id}/entries` Create Data Store Entry Creates an entry with the provided ID and value. Returns a 400 Bad Request if the entry exists. **Scopes:** `universe-datastores.objects:create` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `data_store_id` | path | `string` | Yes | The data-store ID. | | `id` | query | `string` | No | The ID to use for the data store entry, which will become the final component of the data store entry's resource path. This value should be a 1-50 character string. We strongly recommend using only lowercase letters, numeric digits, and hyphens. | **Request Body:** `application/json` — Type: `DataStoreEntry` See [DataStoreEntry](#datastoreentry) in Models. **Request example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "revisionId": "string", "revisionCreateTime": "2024-01-01T00:00:00Z", "state": "STATE_UNSPECIFIED", "etag": "string" } ``` **Responses:** - `200`: OK → `DataStoreEntry` **Response fields** (`DataStoreEntry`) See [DataStoreEntry](#datastoreentry) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "revisionId": "string", "revisionCreateTime": "2024-01-01T00:00:00Z", "state": "STATE_UNSPECIFIED", "etag": "string" } ``` **Rate Limit Note:** Data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -X POST -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/data-stores/{DATA_STORE_ID}/entries" \ -H "Content-Type: application/json" \ -d '{ "path": "string", "createTime": "2024-01-01T00:00:00Z", "revisionId": "string", "revisionCreateTime": "2024-01-01T00:00:00Z", "state": "STATE_UNSPECIFIED", "etag": "string" }' ``` ### GET `/cloud/v2/universes/{universe_id}/data-stores/{data_store_id}/entries/{entry_id}` Get Data Store Entry Gets the specified entry. To get the entry at a specific revision, add `@` to the end of the path. For example, to get `my-entry` at the revision ID `08DC3D3F43F9FCC1.0000000001.08DC3D3F43F9FCC1.01`, use the path `/cloud/v2/universes/1234/data-stores/5678/entries/my-entry@08DC3D3F43F9FCC1.0000000001.08DC3D3F43F9FCC1.01`. If your entry ID contains one or more `@` characters, and you want to get the latest version rather than at any specific revision, append the special revision ID `@latest` to the end of the path. Otherwise, the segment of the entry ID after the last `@` will be interpreted as a revision ID. For example, to get the latest revision of `my-entry`, use the path `/cloud/v2/universes/1234/data-stores/5678/entries/my@entry@latest`. To get the entry that was current at a specific time, add `@latest:` to the end of the path, where `` is RFC-3339 formatted. The given timestamp must be after the Unix epoch (1/1/1970) and not more than ten minutes in the future. For example, to get the revision of `my-entry` that was current on 12/2/2024 at 06:00 UTC, use the path `/cloud/v2/universes/1234/data-stores/5678/entries/my-entry@latest:2024-12-02T06:00:00Z`. **Scopes:** `universe-datastores.objects:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `data_store_id` | path | `string` | Yes | The data-store ID. | | `entry_id` | path | `string` | Yes | The entry ID. | **Responses:** - `200`: OK → `DataStoreEntry` **Response fields** (`DataStoreEntry`) See [DataStoreEntry](#datastoreentry) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "revisionId": "string", "revisionCreateTime": "2024-01-01T00:00:00Z", "state": "STATE_UNSPECIFIED", "etag": "string" } ``` **Rate Limit Note:** Data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/data-stores/{DATA_STORE_ID}/entries/{ENTRY_ID}" ``` ### PATCH `/cloud/v2/universes/{universe_id}/data-stores/{data_store_id}/entries/{entry_id}` Update Data Store Entry Updates the value, attributes, and users of an entry. Updating specific revisions of the entry is **unsupported**. If you specify a revision ID in the path and `allow_missing` is `true`, the update request will instead create a new entry with the `@` suffix as part of the key. Partial update is **unsupported**. If attributes or users are not provided when updating the value, they will be cleared. Value must always be provided when updating an entry. **Scopes:** `universe-datastores.objects:update` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `data_store_id` | path | `string` | Yes | The data-store ID. | | `entry_id` | path | `string` | Yes | The entry ID. | | `allowMissing` | query | `boolean` | No | If set to true, and the data store entry is not found, a data store entry is created. | **Request Body:** `application/json` — Type: `DataStoreEntry` See [DataStoreEntry](#datastoreentry) in Models. **Request example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "revisionId": "string", "revisionCreateTime": "2024-01-01T00:00:00Z", "state": "STATE_UNSPECIFIED", "etag": "string" } ``` **Responses:** - `200`: OK → `DataStoreEntry` **Response fields** (`DataStoreEntry`) See [DataStoreEntry](#datastoreentry) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "revisionId": "string", "revisionCreateTime": "2024-01-01T00:00:00Z", "state": "STATE_UNSPECIFIED", "etag": "string" } ``` **Rate Limit Note:** Data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -X PATCH -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/data-stores/{DATA_STORE_ID}/entries/{ENTRY_ID}" \ -H "Content-Type: application/json" \ -d '{ "path": "string", "createTime": "2024-01-01T00:00:00Z", "revisionId": "string", "revisionCreateTime": "2024-01-01T00:00:00Z", "state": "STATE_UNSPECIFIED", "etag": "string" }' ``` ### DELETE `/cloud/v2/universes/{universe_id}/data-stores/{data_store_id}/entries/{entry_id}` Delete Data Store Entry Marks the specified entry for deletion. Entries are not be deleted immediately; instead, the `state` field will be set to `DELETED`. Permanent deletion occurs after 30 days. On success, returns 200 OK. If the entry doesn't exist, returns 404 Not Found. **Scopes:** `universe-datastores.objects:delete` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `data_store_id` | path | `string` | Yes | The data-store ID. | | `entry_id` | path | `string` | Yes | The entry ID. | **Responses:** - `200`: OK **Rate Limit Note:** Data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -X DELETE -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/data-stores/{DATA_STORE_ID}/entries/{ENTRY_ID}" ``` ### POST `/cloud/v2/universes/{universe_id}/data-stores/{data_store_id}/entries/{entry_id}:increment` Increment Data Store Entry Increments the value of the specified entry. Both the existing value and the increment amount must be integers. If the entry doesn't exist, creates an entry with the specified value. Incrementing specific revisions of the entry is **unsupported**. If you specify a revision ID in the path, the increment request will create a new entry with the `@` suffix as part of the key. Known issue: the value may be incremented past the valid range of values. When this happens, the returned value will be clamped to the valid range, but the backend may persist the original value. This behavior is maintained for backwards compatibility reasons, but may change in a future version of this API. **Scopes:** `universe-datastores.objects:create`, `universe-datastores.objects:update` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `data_store_id` | path | `string` | Yes | The data-store ID. | | `entry_id` | path | `string` | Yes | The entry ID. | **Request Body:** `application/json` — Type: `IncrementDataStoreEntryRequest` See [IncrementDataStoreEntryRequest](#incrementdatastoreentryrequest) in Models. **Request example:** ```json { "amount": 0, "users": [ "string" ], "attributes": "..." } ``` **Responses:** - `200`: OK → `DataStoreEntry` **Response fields** (`DataStoreEntry`) See [DataStoreEntry](#datastoreentry) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "revisionId": "string", "revisionCreateTime": "2024-01-01T00:00:00Z", "state": "STATE_UNSPECIFIED", "etag": "string" } ``` **Rate Limit Note:** Data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -X POST -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/data-stores/{DATA_STORE_ID}/entries/{ENTRY_ID}:increment" \ -H "Content-Type: application/json" \ -d '{ "amount": 0, "users": [ "string" ], "attributes": "..." }' ``` ### GET `/cloud/v2/universes/{universe_id}/data-stores/{data_store_id}/entries/{entry_id}:listRevisions` List Data Store Entry Revisions List revisions of the data store entry. This method returns partial data store entries. In particular, only the `path`, `id`, `createTime`, `revisionCreateTime`, `revisionId`, `etag`, and `state` fields are populated. Both the `path` and `id` fields will have an `@` suffix. In order to get the full entry at a revision, you can use the provided `path` field with the `GetDataStoreEntry` method, i.e. `GET /cloud/v2/universes/1234/data-stores/5678/entries/my-entry@`. **Scopes:** `universe-datastores.versions:list` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `data_store_id` | path | `string` | Yes | The data-store ID. | | `entry_id` | path | `string` | Yes | The entry ID. | | `maxPageSize` | query | `integer` | No | The maximum number of revisions to return per page. The service might return fewer than the maximum number of revisions. If unspecified, at most 10 revisions are returned. The maximum value is 100 values and higher values are set to 100. | | `pageToken` | query | `string` | No | A page token, received from a previous call, to retrieve a subsequent page. When paginating, all other parameters provided to the subsequent call must match the call that provided the page token. | | `filter` | query | `string` | No | Supports the following subset of CEL: * Only the `&&`, `<=`, and `>=` operators are supported. * Only the `revision_create_time` field is supported. For example: `"revision_create_time >= 2000-01-01T00:00:00Z && revision_create_time <= 2001-01-01T00:00:00Z"` | **Responses:** - `200`: OK → `ListDataStoreEntryRevisionsResponse` **Response fields** (`ListDataStoreEntryRevisionsResponse`) See [ListDataStoreEntryRevisionsResponse](#listdatastoreentryrevisionsresponse) in Models. **Response example:** ```json { "dataStoreEntries": [ { "path": "...", "createTime": "...", "revisionId": "...", "revisionCreateTime": "...", "state": "...", "etag": "..." } ], "nextPageToken": "string" } ``` **Rate Limit Note:** Data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/data-stores/{DATA_STORE_ID}/entries/{ENTRY_ID}:listRevisions" ``` ### GET `/cloud/v2/universes/{universe_id}/data-stores/{data_store_id}/scopes/{scope_id}/entries` List Data Store Entries Returns a list of entries from a data store. Only the `path` and `id` fields are populated; use `GetDataStoreEntry` to retrieve other fields. Specify the wildcard scope (`-`) to list entries from all scopes. **Scopes:** `universe-datastores.objects:list` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `data_store_id` | path | `string` | Yes | The data-store ID. | | `scope_id` | path | `string` | Yes | The scope ID. | | `maxPageSize` | query | `integer` | No | The maximum number of data store entries to return. The service might return fewer than this value. If unspecified, at most 10 data store entries are returned. The maximum value is 256 and higher values are set to 256. | | `pageToken` | query | `string` | No | A page token, received from a previous call, to retrieve a subsequent page. When paginating, all other parameters provided to the subsequent call must match the call that provided the page token. | | `filter` | query | `string` | No | This field may be set in order to filter the resources returned. The `filter` field supports a very small subset of CEL: * Only the `id` field is supported. * Only the `startsWith` function is available; no other operators nor built-ins are supported. Example filter: `id.startsWith("foo")` | | `showDeleted` | query | `boolean` | No | If true, resources marked for pending deletion will be included in the results. | **Responses:** - `200`: OK → `ListDataStoreEntriesResponse` **Response fields** (`ListDataStoreEntriesResponse`) See [ListDataStoreEntriesResponse](#listdatastoreentriesresponse) in Models. **Response example:** ```json { "dataStoreEntries": [ { "path": "...", "createTime": "...", "revisionId": "...", "revisionCreateTime": "...", "state": "...", "etag": "..." } ], "nextPageToken": "string" } ``` **Rate Limit Note:** Data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/data-stores/{DATA_STORE_ID}/scopes/{SCOPE_ID}/entries" ``` ### POST `/cloud/v2/universes/{universe_id}/data-stores/{data_store_id}/scopes/{scope_id}/entries` Create Data Store Entry Creates an entry with the provided ID and value. Returns a 400 Bad Request if the entry exists. **Scopes:** `universe-datastores.objects:create` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `data_store_id` | path | `string` | Yes | The data-store ID. | | `scope_id` | path | `string` | Yes | The scope ID. | | `id` | query | `string` | No | The ID to use for the data store entry, which will become the final component of the data store entry's resource path. This value should be a 1-50 character string. We strongly recommend using only lowercase letters, numeric digits, and hyphens. | **Request Body:** `application/json` — Type: `DataStoreEntry` See [DataStoreEntry](#datastoreentry) in Models. **Request example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "revisionId": "string", "revisionCreateTime": "2024-01-01T00:00:00Z", "state": "STATE_UNSPECIFIED", "etag": "string" } ``` **Responses:** - `200`: OK → `DataStoreEntry` **Response fields** (`DataStoreEntry`) See [DataStoreEntry](#datastoreentry) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "revisionId": "string", "revisionCreateTime": "2024-01-01T00:00:00Z", "state": "STATE_UNSPECIFIED", "etag": "string" } ``` **Rate Limit Note:** Data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -X POST -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/data-stores/{DATA_STORE_ID}/scopes/{SCOPE_ID}/entries" \ -H "Content-Type: application/json" \ -d '{ "path": "string", "createTime": "2024-01-01T00:00:00Z", "revisionId": "string", "revisionCreateTime": "2024-01-01T00:00:00Z", "state": "STATE_UNSPECIFIED", "etag": "string" }' ``` ### GET `/cloud/v2/universes/{universe_id}/data-stores/{data_store_id}/scopes/{scope_id}/entries/{entry_id}` Get Data Store Entry Gets the specified entry. To get the entry at a specific revision, add `@` to the end of the path. For example, to get `my-entry` at the revision ID `08DC3D3F43F9FCC1.0000000001.08DC3D3F43F9FCC1.01`, use the path `/cloud/v2/universes/1234/data-stores/5678/entries/my-entry@08DC3D3F43F9FCC1.0000000001.08DC3D3F43F9FCC1.01`. If your entry ID contains one or more `@` characters, and you want to get the latest version rather than at any specific revision, append the special revision ID `@latest` to the end of the path. Otherwise, the segment of the entry ID after the last `@` will be interpreted as a revision ID. For example, to get the latest revision of `my-entry`, use the path `/cloud/v2/universes/1234/data-stores/5678/entries/my@entry@latest`. To get the entry that was current at a specific time, add `@latest:` to the end of the path, where `` is RFC-3339 formatted. The given timestamp must be after the Unix epoch (1/1/1970) and not more than ten minutes in the future. For example, to get the revision of `my-entry` that was current on 12/2/2024 at 06:00 UTC, use the path `/cloud/v2/universes/1234/data-stores/5678/entries/my-entry@latest:2024-12-02T06:00:00Z`. **Scopes:** `universe-datastores.objects:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `data_store_id` | path | `string` | Yes | The data-store ID. | | `scope_id` | path | `string` | Yes | The scope ID. | | `entry_id` | path | `string` | Yes | The entry ID. | **Responses:** - `200`: OK → `DataStoreEntry` **Response fields** (`DataStoreEntry`) See [DataStoreEntry](#datastoreentry) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "revisionId": "string", "revisionCreateTime": "2024-01-01T00:00:00Z", "state": "STATE_UNSPECIFIED", "etag": "string" } ``` **Rate Limit Note:** Data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/data-stores/{DATA_STORE_ID}/scopes/{SCOPE_ID}/entries/{ENTRY_ID}" ``` ### PATCH `/cloud/v2/universes/{universe_id}/data-stores/{data_store_id}/scopes/{scope_id}/entries/{entry_id}` Update Data Store Entry Updates the value, attributes, and users of an entry. Updating specific revisions of the entry is **unsupported**. If you specify a revision ID in the path and `allow_missing` is `true`, the update request will instead create a new entry with the `@` suffix as part of the key. Partial update is **unsupported**. If attributes or users are not provided when updating the value, they will be cleared. Value must always be provided when updating an entry. **Scopes:** `universe-datastores.objects:update` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `data_store_id` | path | `string` | Yes | The data-store ID. | | `scope_id` | path | `string` | Yes | The scope ID. | | `entry_id` | path | `string` | Yes | The entry ID. | | `allowMissing` | query | `boolean` | No | If set to true, and the data store entry is not found, a data store entry is created. | **Request Body:** `application/json` — Type: `DataStoreEntry` See [DataStoreEntry](#datastoreentry) in Models. **Request example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "revisionId": "string", "revisionCreateTime": "2024-01-01T00:00:00Z", "state": "STATE_UNSPECIFIED", "etag": "string" } ``` **Responses:** - `200`: OK → `DataStoreEntry` **Response fields** (`DataStoreEntry`) See [DataStoreEntry](#datastoreentry) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "revisionId": "string", "revisionCreateTime": "2024-01-01T00:00:00Z", "state": "STATE_UNSPECIFIED", "etag": "string" } ``` **Rate Limit Note:** Data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -X PATCH -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/data-stores/{DATA_STORE_ID}/scopes/{SCOPE_ID}/entries/{ENTRY_ID}" \ -H "Content-Type: application/json" \ -d '{ "path": "string", "createTime": "2024-01-01T00:00:00Z", "revisionId": "string", "revisionCreateTime": "2024-01-01T00:00:00Z", "state": "STATE_UNSPECIFIED", "etag": "string" }' ``` ### DELETE `/cloud/v2/universes/{universe_id}/data-stores/{data_store_id}/scopes/{scope_id}/entries/{entry_id}` Delete Data Store Entry Marks the specified entry for deletion. Entries are not be deleted immediately; instead, the `state` field will be set to `DELETED`. Permanent deletion occurs after 30 days. On success, returns 200 OK. If the entry doesn't exist, returns 404 Not Found. **Scopes:** `universe-datastores.objects:delete` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `data_store_id` | path | `string` | Yes | The data-store ID. | | `scope_id` | path | `string` | Yes | The scope ID. | | `entry_id` | path | `string` | Yes | The entry ID. | **Responses:** - `200`: OK **Rate Limit Note:** Data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -X DELETE -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/data-stores/{DATA_STORE_ID}/scopes/{SCOPE_ID}/entries/{ENTRY_ID}" ``` ### POST `/cloud/v2/universes/{universe_id}/data-stores/{data_store_id}/scopes/{scope_id}/entries/{entry_id}:increment` Increment Data Store Entry Increments the value of the specified entry. Both the existing value and the increment amount must be integers. If the entry doesn't exist, creates an entry with the specified value. Incrementing specific revisions of the entry is **unsupported**. If you specify a revision ID in the path, the increment request will create a new entry with the `@` suffix as part of the key. Known issue: the value may be incremented past the valid range of values. When this happens, the returned value will be clamped to the valid range, but the backend may persist the original value. This behavior is maintained for backwards compatibility reasons, but may change in a future version of this API. **Scopes:** `universe-datastores.objects:create`, `universe-datastores.objects:update` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `data_store_id` | path | `string` | Yes | The data-store ID. | | `scope_id` | path | `string` | Yes | The scope ID. | | `entry_id` | path | `string` | Yes | The entry ID. | **Request Body:** `application/json` — Type: `IncrementDataStoreEntryRequest` See [IncrementDataStoreEntryRequest](#incrementdatastoreentryrequest) in Models. **Request example:** ```json { "amount": 0, "users": [ "string" ], "attributes": "..." } ``` **Responses:** - `200`: OK → `DataStoreEntry` **Response fields** (`DataStoreEntry`) See [DataStoreEntry](#datastoreentry) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "revisionId": "string", "revisionCreateTime": "2024-01-01T00:00:00Z", "state": "STATE_UNSPECIFIED", "etag": "string" } ``` **Rate Limit Note:** Data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -X POST -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/data-stores/{DATA_STORE_ID}/scopes/{SCOPE_ID}/entries/{ENTRY_ID}:increment" \ -H "Content-Type: application/json" \ -d '{ "amount": 0, "users": [ "string" ], "attributes": "..." }' ``` ### GET `/cloud/v2/universes/{universe_id}/data-stores/{data_store_id}/scopes/{scope_id}/entries/{entry_id}:listRevisions` List Data Store Entry Revisions List revisions of the data store entry. This method returns partial data store entries. In particular, only the `path`, `id`, `createTime`, `revisionCreateTime`, `revisionId`, `etag`, and `state` fields are populated. Both the `path` and `id` fields will have an `@` suffix. In order to get the full entry at a revision, you can use the provided `path` field with the `GetDataStoreEntry` method, i.e. `GET /cloud/v2/universes/1234/data-stores/5678/entries/my-entry@`. **Scopes:** `universe-datastores.versions:list` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `data_store_id` | path | `string` | Yes | The data-store ID. | | `scope_id` | path | `string` | Yes | The scope ID. | | `entry_id` | path | `string` | Yes | The entry ID. | | `maxPageSize` | query | `integer` | No | The maximum number of revisions to return per page. The service might return fewer than the maximum number of revisions. If unspecified, at most 10 revisions are returned. The maximum value is 100 values and higher values are set to 100. | | `pageToken` | query | `string` | No | A page token, received from a previous call, to retrieve a subsequent page. When paginating, all other parameters provided to the subsequent call must match the call that provided the page token. | | `filter` | query | `string` | No | Supports the following subset of CEL: * Only the `&&`, `<=`, and `>=` operators are supported. * Only the `revision_create_time` field is supported. For example: `"revision_create_time >= 2000-01-01T00:00:00Z && revision_create_time <= 2001-01-01T00:00:00Z"` | **Responses:** - `200`: OK → `ListDataStoreEntryRevisionsResponse` **Response fields** (`ListDataStoreEntryRevisionsResponse`) See [ListDataStoreEntryRevisionsResponse](#listdatastoreentryrevisionsresponse) in Models. **Response example:** ```json { "dataStoreEntries": [ { "path": "...", "createTime": "...", "revisionId": "...", "revisionCreateTime": "...", "state": "...", "etag": "..." } ], "nextPageToken": "string" } ``` **Rate Limit Note:** Data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/data-stores/{DATA_STORE_ID}/scopes/{SCOPE_ID}/entries/{ENTRY_ID}:listRevisions" ``` ### POST `/cloud/v2/universes/{universe_id}/data-stores/{data_store_id}:undelete` Undelete Data Store Restore the data store **Scopes:** `universe-datastores.control:delete` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `data_store_id` | path | `string` | Yes | The data-store ID. | **Request Body:** `application/json` — Type: `UndeleteDataStoreRequest` See [UndeleteDataStoreRequest](#undeletedatastorerequest) in Models. **Request example:** ```json {} ``` **Responses:** - `200`: OK → `DataStore` **Response fields** (`DataStore`) See [DataStore](#datastore) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "expireTime": "2024-01-01T00:00:00Z", "state": "STATE_UNSPECIFIED", "id": "string" } ``` **Rate Limit Note:** Data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -X POST -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/data-stores/{DATA_STORE_ID}:undelete" \ -H "Content-Type: application/json" \ -d '{}' ``` ### POST `/cloud/v2/universes/{universe_id}/data-stores:snapshot` Snapshot Data Stores Takes a new snapshot of the data stores in an experience. After a snapshot, the next write to every key in the experience will create a versioned backup of the previous data, regardless of the time of the last write. In effect, all data current at the time of the snapshot is guaranteed to be available as a versioned backup for at least 30 days. Snapshots can be taken once per UTC day, per experience. If the latest snapshot was taken within the same UTC day, this operation is a no-op and the time of the latest snapshot will be returned. For more information on using snapshots, see the [Data stores](https://create.roblox.com/docs/cloud-services/data-stores#snapshots) Engine guide. **Scopes:** `universe-datastores.control:snapshot` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | **Request Body:** `application/json` — Type: `SnapshotDataStoresRequest` See [SnapshotDataStoresRequest](#snapshotdatastoresrequest) in Models. **Request example:** ```json {} ``` **Responses:** - `200`: OK → `SnapshotDataStoresResponse` **Response fields** (`SnapshotDataStoresResponse`) See [SnapshotDataStoresResponse](#snapshotdatastoresresponse) in Models. **Response example:** ```json { "newSnapshotTaken": false, "latestSnapshotTime": "2024-01-01T00:00:00Z" } ``` **Rate Limit Note:** Data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -X POST -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/data-stores:snapshot" \ -H "Content-Type: application/json" \ -d '{}' ``` ### POST `/cloud/v2/universes/{universe_id}/luau-execution-session-task-binary-inputs` Create Luau Execution Session Task Binary Input Create a new `LuauExecutionSessionTaskBinaryInput`. **Scopes:** `universe.place.luau-execution-session:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | **Request Body:** `application/json` — Type: `LuauExecutionSessionTaskBinaryInput` See [LuauExecutionSessionTaskBinaryInput](#luauexecutionsessiontaskbinaryinput) in Models. **Request example:** ```json { "path": "string", "size": 0, "uploadUri": "string" } ``` > **Verify mutations:** If your API key lacks the required scope (`universe.place.luau-execution-session:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `LuauExecutionSessionTaskBinaryInput` **Response fields** (`LuauExecutionSessionTaskBinaryInput`) See [LuauExecutionSessionTaskBinaryInput](#luauexecutionsessiontaskbinaryinput) in Models. **Response example:** ```json { "path": "string", "size": 0, "uploadUri": "string" } ``` **Example:** ```bash curl -X POST -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/luau-execution-session-task-binary-inputs" \ -H "Content-Type: application/json" \ -d '{ "path": "string", "size": 0, "uploadUri": "string" }' ``` ### POST `/cloud/v2/universes/{universe_id}/memory-store/queues/{queue_id}/items` Create Memory Store Queue Item Creates a new queue item. If `ttl` is set, the item will automatically be removed from the queue after the time interval specified. If a numerical `priority` is set, the item will be inserted into the queue based on the priority value. The higher the value, the closer to the front of the queue the item will be. If priority values are the same then the item will be inserted after existing values with the same priority. **Scopes:** `memory-store.queue:add` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `queue_id` | path | `string` | Yes | The queue ID. | **Request Body:** `application/json` — Type: `MemoryStoreQueueItem` See [MemoryStoreQueueItem](#memorystorequeueitem) in Models. **Request example:** ```json { "path": "string", "data": "...", "priority": 0, "ttl": "string", "expireTime": "2024-01-01T00:00:00Z", "id": "string" } ``` **Responses:** - `200`: OK → `MemoryStoreQueueItem` **Response fields** (`MemoryStoreQueueItem`) See [MemoryStoreQueueItem](#memorystorequeueitem) in Models. **Response example:** ```json { "path": "string", "data": "...", "priority": 0, "ttl": "string", "expireTime": "2024-01-01T00:00:00Z", "id": "string" } ``` **Example:** ```bash curl -X POST -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/memory-store/queues/{QUEUE_ID}/items" \ -H "Content-Type: application/json" \ -d '{ "path": "string", "data": "...", "priority": 0, "ttl": "string", "expireTime": "2024-01-01T00:00:00Z", "id": "string" }' ``` ### POST `/cloud/v2/universes/{universe_id}/memory-store/queues/{queue_id}/items:discard` Discard Memory Store Queue Items Discards read items from the front of the queue. Takes a `readId` from a previous `Read` operation. **Scopes:** `memory-store.queue:discard` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `queue_id` | path | `string` | Yes | The queue ID. | **Request Body:** `application/json` — Type: `DiscardMemoryStoreQueueItemsRequest` See [DiscardMemoryStoreQueueItemsRequest](#discardmemorystorequeueitemsrequest) in Models. **Request example:** ```json { "readId": "string" } ``` **Responses:** - `200`: OK **Example:** ```bash curl -X POST -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/memory-store/queues/{QUEUE_ID}/items:discard" \ -H "Content-Type: application/json" \ -d '{ "readId": "string" }' ``` ### GET `/cloud/v2/universes/{universe_id}/memory-store/queues/{queue_id}/items:read` Read Memory Store Queue Items Returns the specified number of items at the front of the queue. **Scopes:** `memory-store.queue:dequeue` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `queue_id` | path | `string` | Yes | The queue ID. | | `count` | query | `integer` | No | The number of items to read from the queue If unspecified, 1 item will be returned. The maximum value is 200; values above 200 will be coerced to 200. | | `allOrNothing` | query | `boolean` | No | If `all_or_nothing` is true and the requested number of objects is not available, will return a 404 Error. Otherwise, will return the path and read_id of the read operation and a list of the MemoryStoreQueue items. | | `invisibilityWindow` | query | `string` | No | Invisibility window for items read, in seconds. Items read are invisible in subsequent reads during the invisibility window duration. It must be written in seconds greater than 0 and end with `s`. Defaults to `30s`. | **Responses:** - `200`: OK → `ReadMemoryStoreQueueItemsResponse` **Response fields** (`ReadMemoryStoreQueueItemsResponse`) See [ReadMemoryStoreQueueItemsResponse](#readmemorystorequeueitemsresponse) in Models. **Response example:** ```json { "readId": "string", "items": [ { "path": "...", "data": "...", "priority": "...", "ttl": "...", "expireTime": "...", "id": "..." } ] } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/memory-store/queues/{QUEUE_ID}/items:read" ``` ### GET `/cloud/v2/universes/{universe_id}/memory-store/sorted-maps/{sorted_map_id}/items` List Memory Store Sorted Map Items Gets and returns items in the map with a given order and filter. **Scopes:** `memory-store.sorted-map:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `sorted_map_id` | path | `string` | Yes | The sorted-map ID. | | `maxPageSize` | query | `integer` | No | The maximum number of memory store sorted map items to return. The service might return fewer than this value. If unspecified, at most 1 memory store sorted map items are returned. The maximum value is 100 and higher values are set to 100. | | `pageToken` | query | `string` | No | A page token, received from a previous call, to retrieve a subsequent page. When paginating, all other parameters provided to the subsequent call must match the call that provided the page token. | | `orderBy` | query | `string` | No | If specified, results are ordered according to the specified fields. Values must be a comma-separated list of fields, with an optional, per-field " desc" suffix to sort by descending rather than ascending values. You can access subfields with a `.` operator. Results may be ordered by the following fields: id. Example: "id desc" | | `filter` | query | `string` | No | This field may be set in order to filter the resources returned. Filtering conforms to Common Expression Language (CEL). Only the `id` and `sortKey` fields are supported. In terms of operators, only `<`, `>` and `&&` are allowed' Example: `id > "key-001" && id < "key-100"` | **Responses:** - `200`: OK → `ListMemoryStoreSortedMapItemsResponse` **Response fields** (`ListMemoryStoreSortedMapItemsResponse`) See [ListMemoryStoreSortedMapItemsResponse](#listmemorystoresortedmapitemsresponse) in Models. **Response example:** ```json { "memoryStoreSortedMapItems": [ { "path": "...", "value": "...", "etag": "...", "ttl": "...", "expireTime": "...", "id": "..." } ], "nextPageToken": "string" } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/memory-store/sorted-maps/{SORTED_MAP_ID}/items" ``` ### POST `/cloud/v2/universes/{universe_id}/memory-store/sorted-maps/{sorted_map_id}/items` Create Memory Store Sorted Map Item Creates the specified map item if it doesn't exist. **Scopes:** `memory-store.sorted-map:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `sorted_map_id` | path | `string` | Yes | The sorted-map ID. | | `id` | query | `string` | No | The ID to use for the memory store sorted map item, which will become the final component of the memory store sorted map item's resource path. This value should be a 1-127 character string that supports alphanumeric and special characters. This id is case sensitive. The id must be url encoded if it contains any url breaking special characters. | **Request Body:** `application/json` — Type: `MemoryStoreSortedMapItem` See [MemoryStoreSortedMapItem](#memorystoresortedmapitem) in Models. **Request example:** ```json { "path": "string", "value": "...", "etag": "string", "ttl": "string", "expireTime": "2024-01-01T00:00:00Z", "id": "string" } ``` > **Verify mutations:** If your API key lacks the required scope (`memory-store.sorted-map:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `MemoryStoreSortedMapItem` **Response fields** (`MemoryStoreSortedMapItem`) See [MemoryStoreSortedMapItem](#memorystoresortedmapitem) in Models. **Response example:** ```json { "path": "string", "value": "...", "etag": "string", "ttl": "string", "expireTime": "2024-01-01T00:00:00Z", "id": "string" } ``` **Example:** ```bash curl -X POST -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/memory-store/sorted-maps/{SORTED_MAP_ID}/items" \ -H "Content-Type: application/json" \ -d '{ "path": "string", "value": "...", "etag": "string", "ttl": "string", "expireTime": "2024-01-01T00:00:00Z", "id": "string" }' ``` ### GET `/cloud/v2/universes/{universe_id}/memory-store/sorted-maps/{sorted_map_id}/items/{item_id}` Get Memory Store Sorted Map Item Gets and returns the value of the given key in the map. **Scopes:** `memory-store.sorted-map:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `sorted_map_id` | path | `string` | Yes | The sorted-map ID. | | `item_id` | path | `string` | Yes | The item ID. | **Responses:** - `200`: OK → `MemoryStoreSortedMapItem` **Response fields** (`MemoryStoreSortedMapItem`) See [MemoryStoreSortedMapItem](#memorystoresortedmapitem) in Models. **Response example:** ```json { "path": "string", "value": "...", "etag": "string", "ttl": "string", "expireTime": "2024-01-01T00:00:00Z", "id": "string" } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/memory-store/sorted-maps/{SORTED_MAP_ID}/items/{ITEM_ID}" ``` ### PATCH `/cloud/v2/universes/{universe_id}/memory-store/sorted-maps/{sorted_map_id}/items/{item_id}` Update Memory Store Sorted Map Item Updates the specified map item. **Scopes:** `memory-store.sorted-map:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `sorted_map_id` | path | `string` | Yes | The sorted-map ID. | | `item_id` | path | `string` | Yes | The item ID. | | `allowMissing` | query | `boolean` | No | If set to true, and the memory store sorted map item is not found, a memory store sorted map item is created. | **Request Body:** `application/json` — Type: `MemoryStoreSortedMapItem` See [MemoryStoreSortedMapItem](#memorystoresortedmapitem) in Models. **Request example:** ```json { "path": "string", "value": "...", "etag": "string", "ttl": "string", "expireTime": "2024-01-01T00:00:00Z", "id": "string" } ``` > **Verify mutations:** If your API key lacks the required scope (`memory-store.sorted-map:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `MemoryStoreSortedMapItem` **Response fields** (`MemoryStoreSortedMapItem`) See [MemoryStoreSortedMapItem](#memorystoresortedmapitem) in Models. **Response example:** ```json { "path": "string", "value": "...", "etag": "string", "ttl": "string", "expireTime": "2024-01-01T00:00:00Z", "id": "string" } ``` **Example:** ```bash curl -X PATCH -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/memory-store/sorted-maps/{SORTED_MAP_ID}/items/{ITEM_ID}" \ -H "Content-Type: application/json" \ -d '{ "path": "string", "value": "...", "etag": "string", "ttl": "string", "expireTime": "2024-01-01T00:00:00Z", "id": "string" }' ``` ### DELETE `/cloud/v2/universes/{universe_id}/memory-store/sorted-maps/{sorted_map_id}/items/{item_id}` Delete Memory Store Sorted Map Item Deletes the specified item from the map. **Scopes:** `memory-store.sorted-map:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `sorted_map_id` | path | `string` | Yes | The sorted-map ID. | | `item_id` | path | `string` | Yes | The item ID. | > **Verify mutations:** If your API key lacks the required scope (`memory-store.sorted-map:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK **Example:** ```bash curl -X DELETE -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/memory-store/sorted-maps/{SORTED_MAP_ID}/items/{ITEM_ID}" ``` ### POST `/cloud/v2/universes/{universe_id}/memory-store:flush` Flush Memory Store Asynchronously flush all data structures in the universe. **Scopes:** `memory-store:flush` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `scope` | query | `LIVE \| TEST` | No | The scope of the memory store to flush. Possible values: \| Value \| Description \| \| --- \| --- \| \| LIVE \| Flush the live memory store scope. This is the default. \| \| TEST \| Flush the test memory store scope. \| Valid values: `LIVE`, `TEST` | **Responses:** - `200`: OK → `Operation` **Response fields** (`Operation`) See [Operation](#operation) in Models. **Response example:** ```json { "path": "string", "metadata": { "@type": "string" }, "done": false, "error": { "code": 0, "message": "string", "details": [ "..." ] }, "response": { "@type": "string" } } ``` **Example:** ```bash curl -X POST -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/memory-store:flush" ``` ### GET `/cloud/v2/universes/{universe_id}/ordered-data-stores/{ordered_data_store_id}/scopes/{scope_id}/entries` List Ordered Data Store Entries Returns a list of entries from an ordered data store. **Scopes:** `universe.ordered-data-store.scope.entry:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `ordered_data_store_id` | path | `string` | Yes | The ordered-data-store ID. | | `scope_id` | path | `string` | Yes | The scope ID. | | `maxPageSize` | query | `integer` | No | The maximum number of ordered data store entries to return. The service might return fewer than this value. If unspecified, at most 10 ordered data store entries are returned. The maximum value is 100 and higher values are set to 100. | | `pageToken` | query | `string` | No | A page token, received from a previous call, to retrieve a subsequent page. When paginating, all other parameters provided to the subsequent call must match the call that provided the page token. | | `orderBy` | query | `string` | No | If specified, results are ordered according to the specified fields. Values must be a comma-separated list of fields, with an optional, per-field " desc" suffix to sort by descending rather than ascending values. You can access subfields with a `.` operator. Results may be ordered by the following fields: value. Example: "value desc" | | `filter` | query | `string` | No | This field may be set in order to filter the resources returned. We support two comparison operators for this operation: `<=` and `>=`.These comparison operators act as a minValue and maxValue for the values returned. If filtering is needed for a value between a minValue and maxValue the user can use the logical operator `&&`. All tokens in the filter expression must be separated by a single space. Example filters: `entry <= 10`; `entry >= 10 && entry <= 30` | **Responses:** - `200`: OK → `ListOrderedDataStoreEntriesResponse` **Response fields** (`ListOrderedDataStoreEntriesResponse`) See [ListOrderedDataStoreEntriesResponse](#listordereddatastoreentriesresponse) in Models. **Response example:** ```json { "orderedDataStoreEntries": [ { "path": "...", "value": "...", "id": "..." } ], "nextPageToken": "string" } ``` **Rate Limit Note:** Ordered data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/ordered-data-stores/{ORDERED_DATA_STORE_ID}/scopes/{SCOPE_ID}/entries" ``` ### POST `/cloud/v2/universes/{universe_id}/ordered-data-stores/{ordered_data_store_id}/scopes/{scope_id}/entries` Create Ordered Data Store Entry Creates an entry with the provided ID and value. Returns a 400 Bad Request if the entry exists. **Scopes:** `universe.ordered-data-store.scope.entry:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `ordered_data_store_id` | path | `string` | Yes | The ordered-data-store ID. | | `scope_id` | path | `string` | Yes | The scope ID. | | `id` | query | `string` | No | The ID to use for the ordered data store entry, which will become the final component of the ordered data store entry's resource path. This value should be A 1-63 character string. We strongly recommend using only lowercase letters, numeric digits, and hyphens. | **Request Body:** `application/json` — Type: `OrderedDataStoreEntry` See [OrderedDataStoreEntry](#ordereddatastoreentry) in Models. **Request example:** ```json { "path": "string", "value": 0, "id": "string" } ``` > **Verify mutations:** If your API key lacks the required scope (`universe.ordered-data-store.scope.entry:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `OrderedDataStoreEntry` **Response fields** (`OrderedDataStoreEntry`) See [OrderedDataStoreEntry](#ordereddatastoreentry) in Models. **Response example:** ```json { "path": "string", "value": 0, "id": "string" } ``` **Rate Limit Note:** Ordered data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -X POST -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/ordered-data-stores/{ORDERED_DATA_STORE_ID}/scopes/{SCOPE_ID}/entries" \ -H "Content-Type: application/json" \ -d '{ "path": "string", "value": 0, "id": "string" }' ``` ### GET `/cloud/v2/universes/{universe_id}/ordered-data-stores/{ordered_data_store_id}/scopes/{scope_id}/entries/{entry_id}` Get Ordered Data Store Entry Gets the specified entry. **Scopes:** `universe.ordered-data-store.scope.entry:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `ordered_data_store_id` | path | `string` | Yes | The ordered-data-store ID. | | `scope_id` | path | `string` | Yes | The scope ID. | | `entry_id` | path | `string` | Yes | The entry ID. | **Responses:** - `200`: OK → `OrderedDataStoreEntry` **Response fields** (`OrderedDataStoreEntry`) See [OrderedDataStoreEntry](#ordereddatastoreentry) in Models. **Response example:** ```json { "path": "string", "value": 0, "id": "string" } ``` **Rate Limit Note:** Ordered data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/ordered-data-stores/{ORDERED_DATA_STORE_ID}/scopes/{SCOPE_ID}/entries/{ENTRY_ID}" ``` ### PATCH `/cloud/v2/universes/{universe_id}/ordered-data-stores/{ordered_data_store_id}/scopes/{scope_id}/entries/{entry_id}` Update Ordered Data Store Entry Updates the value of an entry. **Scopes:** `universe.ordered-data-store.scope.entry:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `ordered_data_store_id` | path | `string` | Yes | The ordered-data-store ID. | | `scope_id` | path | `string` | Yes | The scope ID. | | `entry_id` | path | `string` | Yes | The entry ID. | | `allowMissing` | query | `boolean` | No | If set to true, and the ordered data store entry is not found, a ordered data store entry is created. | **Request Body:** `application/json` — Type: `OrderedDataStoreEntry` See [OrderedDataStoreEntry](#ordereddatastoreentry) in Models. **Request example:** ```json { "path": "string", "value": 0, "id": "string" } ``` > **Verify mutations:** If your API key lacks the required scope (`universe.ordered-data-store.scope.entry:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `OrderedDataStoreEntry` **Response fields** (`OrderedDataStoreEntry`) See [OrderedDataStoreEntry](#ordereddatastoreentry) in Models. **Response example:** ```json { "path": "string", "value": 0, "id": "string" } ``` **Rate Limit Note:** Ordered data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -X PATCH -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/ordered-data-stores/{ORDERED_DATA_STORE_ID}/scopes/{SCOPE_ID}/entries/{ENTRY_ID}" \ -H "Content-Type: application/json" \ -d '{ "path": "string", "value": 0, "id": "string" }' ``` ### DELETE `/cloud/v2/universes/{universe_id}/ordered-data-stores/{ordered_data_store_id}/scopes/{scope_id}/entries/{entry_id}` Delete Ordered Data Store Entry Deletes the specified entry. On success, returns 200 OK. If the entry doesn't exist, returns 404 Not Found. **Scopes:** `universe.ordered-data-store.scope.entry:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `ordered_data_store_id` | path | `string` | Yes | The ordered-data-store ID. | | `scope_id` | path | `string` | Yes | The scope ID. | | `entry_id` | path | `string` | Yes | The entry ID. | > **Verify mutations:** If your API key lacks the required scope (`universe.ordered-data-store.scope.entry:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK **Rate Limit Note:** Ordered data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -X DELETE -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/ordered-data-stores/{ORDERED_DATA_STORE_ID}/scopes/{SCOPE_ID}/entries/{ENTRY_ID}" ``` ### POST `/cloud/v2/universes/{universe_id}/ordered-data-stores/{ordered_data_store_id}/scopes/{scope_id}/entries/{entry_id}:increment` Increment Ordered Data Store Entry Increments the value of the specified entry. Both the existing value and the increment amount must be integers. If the entry doesn't exist, creates an entry with the specified value. Known issue: the value may be incremented past the valid range of values. When this happens, the returned value will be clamped to the valid range, but the backend may persist the original value. This behavior is maintained for backwards compatibility reasons, but may change in a future version of this API. **Scopes:** `universe.ordered-data-store.scope.entry:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `ordered_data_store_id` | path | `string` | Yes | The ordered-data-store ID. | | `scope_id` | path | `string` | Yes | The scope ID. | | `entry_id` | path | `string` | Yes | The entry ID. | **Request Body:** `application/json` — Type: `IncrementOrderedDataStoreEntryRequest` See [IncrementOrderedDataStoreEntryRequest](#incrementordereddatastoreentryrequest) in Models. **Request example:** ```json { "amount": 0 } ``` > **Verify mutations:** If your API key lacks the required scope (`universe.ordered-data-store.scope.entry:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `OrderedDataStoreEntry` **Response fields** (`OrderedDataStoreEntry`) See [OrderedDataStoreEntry](#ordereddatastoreentry) in Models. **Response example:** ```json { "path": "string", "value": 0, "id": "string" } ``` **Rate Limit Note:** Ordered data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -X POST -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/ordered-data-stores/{ORDERED_DATA_STORE_ID}/scopes/{SCOPE_ID}/entries/{ENTRY_ID}:increment" \ -H "Content-Type: application/json" \ -d '{ "amount": 0 }' ``` ### GET `/cloud/v2/universes/{universe_id}/places/{place_id}` Get Place Gets the specified place. **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `place_id` | path | `string` | Yes | The place ID. | **Responses:** - `200`: OK → `Place` **Response fields** (`Place`) See [Place](#place) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:00Z", "displayName": "string", "description": "string", "serverSize": 0 } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/places/{PLACE_ID}" ``` ### PATCH `/cloud/v2/universes/{universe_id}/places/{place_id}` Update Place Updates the specified place. **Scopes:** `universe.place:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `place_id` | path | `string` | Yes | The place ID. | | `updateMask` | query | `string` | No | The list of fields to update. | **Request Body:** `application/json` — Type: `Place` See [Place](#place) in Models. **Request example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:00Z", "displayName": "string", "description": "string", "serverSize": 0 } ``` > **Verify mutations:** If your API key lacks the required scope (`universe.place:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Place` **Response fields** (`Place`) See [Place](#place) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:00Z", "displayName": "string", "description": "string", "serverSize": 0 } ``` **Example:** ```bash curl -X PATCH -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/places/{PLACE_ID}" \ -H "Content-Type: application/json" \ -d '{ "path": "string", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:00Z", "displayName": "string", "description": "string", "serverSize": 0 }' ``` ### GET `/cloud/v2/universes/{universe_id}/places/{place_id}/instances/{instance_id}` Get Instance Gets an instance and its property data. The maximum supported response data size is 500,000 bytes. If this limit is exceeded, the returned `Operation` will be completed with an error result that has an error code of `422`. **Scopes:** `universe.place.instance:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `place_id` | path | `string` | Yes | The place ID. | | `instance_id` | path | `string` | Yes | The instance ID. | **Responses:** - `200`: OK → `Operation` **Response fields** (`Operation`) See [Operation](#operation) in Models. **Response example:** ```json { "path": "string", "metadata": { "@type": "string" }, "done": false, "error": { "code": 0, "message": "string", "details": [ "..." ] }, "response": { "@type": "string" } } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/places/{PLACE_ID}/instances/{INSTANCE_ID}" ``` ### PATCH `/cloud/v2/universes/{universe_id}/places/{place_id}/instances/{instance_id}` Update Instance Updates an instance's property data. When updating a `Script` instance's source property, the maximum supported property size is 200,000 bytes after UTF-8 encoding. **Scopes:** `universe.place.instance:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `place_id` | path | `string` | Yes | The place ID. | | `instance_id` | path | `string` | Yes | The instance ID. | | `updateMask` | query | `string` | No | The list of fields to update. | **Request Body:** `application/json` — Type: `Instance` See [Instance](#instance) in Models. **Request example:** ```json { "path": "string", "hasChildren": false, "engineInstance": { "Id": "string", "Parent": "string", "Name": "string", "Details": { "Folder": "...", "LocalScript": "...", "ModuleScript": "...", "Script": "..." } } } ``` > **Verify mutations:** If your API key lacks the required scope (`universe.place.instance:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Operation` **Response fields** (`Operation`) See [Operation](#operation) in Models. **Response example:** ```json { "path": "string", "metadata": { "@type": "string" }, "done": false, "error": { "code": 0, "message": "string", "details": [ "..." ] }, "response": { "@type": "string" } } ``` **Example:** ```bash curl -X PATCH -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/places/{PLACE_ID}/instances/{INSTANCE_ID}" \ -H "Content-Type: application/json" \ -d '{ "path": "string", "hasChildren": false, "engineInstance": { "Id": "string", "Parent": "string", "Name": "string", "Details": { "Folder": "...", "LocalScript": "...", "ModuleScript": "...", "Script": "..." } } }' ``` ### GET `/cloud/v2/universes/{universe_id}/places/{place_id}/instances/{instance_id}:listChildren` List Instance Children Lists an instance's children. The maximum supported response data size is 500,000 bytes. If this limit is exceeded, the returned `Operation` will be completed with an error result that has an error code of `422`. **Scopes:** `universe.place.instance:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `place_id` | path | `string` | Yes | The place ID. | | `instance_id` | path | `string` | Yes | The instance ID. | | `maxPageSize` | query | `integer` | No | The maximum number of child instance to return. The service may return fewer than this value. If unspecified, at most 200 children will be returned. The maximum value is 200; values above 200 will be coerced to 200. | | `pageToken` | query | `string` | No | A page token, received from a previous `ListInstanceChildrenRequest` call. Provide this to retrieve the subsequent page. When paginating, all other parameters provided to `ListInstanceChildrenRequest` must match the call that provided the page token. | **Responses:** - `200`: OK → `Operation` **Response fields** (`Operation`) See [Operation](#operation) in Models. **Response example:** ```json { "path": "string", "metadata": { "@type": "string" }, "done": false, "error": { "code": 0, "message": "string", "details": [ "..." ] }, "response": { "@type": "string" } } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/places/{PLACE_ID}/instances/{INSTANCE_ID}:listChildren" ``` ### POST `/cloud/v2/universes/{universe_id}/places/{place_id}/luau-execution-session-tasks` Create Luau Execution Session Task Creates a task but does not wait for the task to complete. To check whether a task has completed, call the `GetLuauExecutionSessionTask` method and inspect the `state` field of the returned resource. Quotas: * 5 calls per minute per API key owner * 45 calls per minute per IP address **Scopes:** `universe.place.luau-execution-session:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `place_id` | path | `string` | Yes | The place ID. | **Request Body:** `application/json` — Type: `LuauExecutionSessionTask` See [LuauExecutionSessionTask](#luauexecutionsessiontask) in Models. **Request example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:00Z", "user": "string", "state": "STATE_UNSPECIFIED", "script": "string" } ``` > **Verify mutations:** If your API key lacks the required scope (`universe.place.luau-execution-session:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `LuauExecutionSessionTask` **Response fields** (`LuauExecutionSessionTask`) See [LuauExecutionSessionTask](#luauexecutionsessiontask) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:00Z", "user": "string", "state": "STATE_UNSPECIFIED", "script": "string" } ``` **Example:** ```bash curl -X POST -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/places/{PLACE_ID}/luau-execution-session-tasks" \ -H "Content-Type: application/json" \ -d '{ "path": "string", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:00Z", "user": "string", "state": "STATE_UNSPECIFIED", "script": "string" }' ``` ### GET `/cloud/v2/universes/{universe_id}/places/{place_id}/user-restrictions` List User Restrictions List user restrictions for users that have ever been banned in either a universe or a specific place. **Scopes:** `universe.user-restriction:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `place_id` | path | `string` | Yes | The place ID. | | `maxPageSize` | query | `integer` | No | The maximum number of user restrictions to return. The service might return fewer than this value. If unspecified, at most 10 user restrictions are returned. The maximum value is 100 and higher values are set to 100. | | `pageToken` | query | `string` | No | A page token, received from a previous call, to retrieve a subsequent page. When paginating, all other parameters provided to the subsequent call must match the call that provided the page token. | **Responses:** - `200`: OK → `ListUserRestrictionsResponse` **Response fields** (`ListUserRestrictionsResponse`) See [ListUserRestrictionsResponse](#listuserrestrictionsresponse) in Models. **Response example:** ```json { "userRestrictions": [ { "path": "...", "updateTime": "...", "user": "...", "gameJoinRestriction": "..." } ], "nextPageToken": "string" } ``` **Rate Limit Note:** User Restrictions are subject to the additional rate limits, applied per universe * Get User Restriction: 50 req/s * List User Restrictions: 50 req/s * Update User Restrictions: 10 req/s * List User Restriction Logs: 50 req/s Additionally, we impose the following rate limit for the same user within a universe: * Update User Restriction: 2 req/min For example, Update User Restriction may not be called for user 123 more than twice every minute. **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/places/{PLACE_ID}/user-restrictions" ``` ### GET `/cloud/v2/universes/{universe_id}/places/{place_id}/user-restrictions/{user_restriction_id}` Get User Restriction Get the user restriction. **Scopes:** `universe.user-restriction:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `place_id` | path | `string` | Yes | The place ID. | | `user_restriction_id` | path | `string` | Yes | The user-restriction ID. | **Responses:** - `200`: OK → `UserRestriction` **Response fields** (`UserRestriction`) See [UserRestriction](#userrestriction) in Models. **Response example:** ```json { "path": "string", "updateTime": "2024-01-01T00:00:00Z", "user": "string", "gameJoinRestriction": { "active": false, "startTime": "2024-01-01T00:00:00Z", "duration": "string", "privateReason": "string", "displayReason": "string", "excludeAltAccounts": false } } ``` **Rate Limit Note:** User Restrictions are subject to the additional rate limits, applied per universe * Get User Restriction: 50 req/s * List User Restrictions: 50 req/s * Update User Restrictions: 10 req/s * List User Restriction Logs: 50 req/s Additionally, we impose the following rate limit for the same user within a universe: * Update User Restriction: 2 req/min For example, Update User Restriction may not be called for user 123 more than twice every minute. **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/places/{PLACE_ID}/user-restrictions/{USER_RESTRICTION_ID}" ``` ### PATCH `/cloud/v2/universes/{universe_id}/places/{place_id}/user-restrictions/{user_restriction_id}` Update User Restriction Update the user restriction. **Scopes:** `universe.user-restriction:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `place_id` | path | `string` | Yes | The place ID. | | `user_restriction_id` | path | `string` | Yes | The user-restriction ID. | | `updateMask` | query | `string` | No | The list of fields to update. The `game_join_restriction` field must be updated atomically; field masks that index into `game_join_restriction` (such as `"game_join_restriction.active"`) are not supported. | | `idempotencyKey.key` | query | `string` | No | The unique key to use for idempotency. | | `idempotencyKey.firstSent` | query | `string` | No | The timestamp at which the first request was sent. If this is further in the past than the lifetime of the idempotency key (which *may* exceed the annotated minimum lifetime), the server *must* return an error. | **Request Body:** `application/json` — Type: `UserRestriction` See [UserRestriction](#userrestriction) in Models. **Request example:** ```json { "path": "string", "updateTime": "2024-01-01T00:00:00Z", "user": "string", "gameJoinRestriction": { "active": false, "startTime": "2024-01-01T00:00:00Z", "duration": "string", "privateReason": "string", "displayReason": "string", "excludeAltAccounts": false } } ``` > **Verify mutations:** If your API key lacks the required scope (`universe.user-restriction:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `UserRestriction` **Response fields** (`UserRestriction`) See [UserRestriction](#userrestriction) in Models. **Response example:** ```json { "path": "string", "updateTime": "2024-01-01T00:00:00Z", "user": "string", "gameJoinRestriction": { "active": false, "startTime": "2024-01-01T00:00:00Z", "duration": "string", "privateReason": "string", "displayReason": "string", "excludeAltAccounts": false } } ``` **Rate Limit Note:** User Restrictions are subject to the additional rate limits, applied per universe * Get User Restriction: 50 req/s * List User Restrictions: 50 req/s * Update User Restrictions: 10 req/s * List User Restriction Logs: 50 req/s Additionally, we impose the following rate limit for the same user within a universe: * Update User Restriction: 2 req/min For example, Update User Restriction may not be called for user 123 more than twice every minute. **Example:** ```bash curl -X PATCH -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/places/{PLACE_ID}/user-restrictions/{USER_RESTRICTION_ID}" \ -H "Content-Type: application/json" \ -d '{ "path": "string", "updateTime": "2024-01-01T00:00:00Z", "user": "string", "gameJoinRestriction": { "active": false, "startTime": "2024-01-01T00:00:00Z", "duration": "string", "privateReason": "string", "displayReason": "string", "excludeAltAccounts": false } }' ``` ### POST `/cloud/v2/universes/{universe_id}/places/{place_id}/versions/{version_id}/luau-execution-session-tasks` Create Luau Execution Session Task Creates a task but does not wait for the task to complete. To check whether a task has completed, call the `GetLuauExecutionSessionTask` method and inspect the `state` field of the returned resource. Quotas: * 5 calls per minute per API key owner * 45 calls per minute per IP address **Scopes:** `universe.place.luau-execution-session:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `place_id` | path | `string` | Yes | The place ID. | | `version_id` | path | `string` | Yes | The version ID. | **Request Body:** `application/json` — Type: `LuauExecutionSessionTask` See [LuauExecutionSessionTask](#luauexecutionsessiontask) in Models. **Request example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:00Z", "user": "string", "state": "STATE_UNSPECIFIED", "script": "string" } ``` > **Verify mutations:** If your API key lacks the required scope (`universe.place.luau-execution-session:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `LuauExecutionSessionTask` **Response fields** (`LuauExecutionSessionTask`) See [LuauExecutionSessionTask](#luauexecutionsessiontask) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:00Z", "user": "string", "state": "STATE_UNSPECIFIED", "script": "string" } ``` **Example:** ```bash curl -X POST -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/places/{PLACE_ID}/versions/{VERSION_ID}/luau-execution-session-tasks" \ -H "Content-Type: application/json" \ -d '{ "path": "string", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:00Z", "user": "string", "state": "STATE_UNSPECIFIED", "script": "string" }' ``` ### GET `/cloud/v2/universes/{universe_id}/places/{place_id}/versions/{version_id}/luau-execution-sessions/{luau_execution_session_id}/tasks/{task_id}` Get Luau Execution Session Task Gets information about a task. Quotas: * 45 calls per minute per API key owner or IP address **Scopes:** `universe.place.luau-execution-session:read`, `universe.place.luau-execution-session:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `place_id` | path | `string` | Yes | The place ID. | | `version_id` | path | `string` | Yes | The version ID. | | `luau_execution_session_id` | path | `string` | Yes | The luau-execution-session ID. | | `task_id` | path | `string` | Yes | The task ID. | | `view` | query | `VIEW_UNSPECIFIED \| BASIC \| FULL` | No | The view in which to retrieve the luau execution session task. Supports BASIC and FULL. Defaults to BASIC. Possible values: \| Value \| Description \| \| --- \| --- \| \| VIEW_UNSPECIFIED \| The luau execution session task view is not specified; the default will be used. \| \| BASIC \| Excludes the `script` field. \| \| FULL \| Includes all fields. \| Valid values: `VIEW_UNSPECIFIED`, `BASIC`, `FULL` | **Responses:** - `200`: OK → `LuauExecutionSessionTask` **Response fields** (`LuauExecutionSessionTask`) See [LuauExecutionSessionTask](#luauexecutionsessiontask) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:00Z", "user": "string", "state": "STATE_UNSPECIFIED", "script": "string" } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/places/{PLACE_ID}/versions/{VERSION_ID}/luau-execution-sessions/{LUAU_EXECUTION_SESSION_ID}/tasks/{TASK_ID}" ``` ### GET `/cloud/v2/universes/{universe_id}/places/{place_id}/versions/{version_id}/luau-execution-sessions/{luau_execution_session_id}/tasks/{task_id}/logs` List Luau Execution Session Task Logs Lists log chunks generated by a `LuauExecutionSessionTask`. Quotas: * 45 calls per minute per API key owner or IP address **Scopes:** `universe.place.luau-execution-session:read`, `universe.place.luau-execution-session:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `place_id` | path | `string` | Yes | The place ID. | | `version_id` | path | `string` | Yes | The version ID. | | `luau_execution_session_id` | path | `string` | Yes | The luau-execution-session ID. | | `task_id` | path | `string` | Yes | The task ID. | | `maxPageSize` | query | `integer` | No | The maximum number of luau execution session task logs to return. The service might return fewer than this value. If unspecified, at most 10000 luau execution session task logs are returned. The maximum value is 10000 and higher values are set to 10000. | | `pageToken` | query | `string` | No | A page token, received from a previous call, to retrieve a subsequent page. When paginating, all other parameters provided to the subsequent call must match the call that provided the page token. | | `view` | query | `VIEW_UNSPECIFIED \| FLAT \| STRUCTURED` | No | The view in which to retrieve the luau execution session task log. Supports FLAT and STRUCTURED. Defaults to FLAT. Possible values: \| Value \| Description \| \| --- \| --- \| \| VIEW_UNSPECIFIED \| The luau execution session task log view is not specified; the default will be used. \| \| FLAT \| If this view is selected, the `messages` field will be populated (and the `structuredMessages` field will not). Each entry of the `messages` array contains only the log message, without additional medata. This is the default. \| \| STRUCTURED \| If this view is selected, the `structuredMessages` field will be populated (and the `messages` field will not). Each entry of the `structuredMessages` array contains the log message plus additional metadata (see `LogMessage` for details). \| Valid values: `VIEW_UNSPECIFIED`, `FLAT`, `STRUCTURED` | **Responses:** - `200`: OK → `ListLuauExecutionSessionTaskLogsResponse` **Response fields** (`ListLuauExecutionSessionTaskLogsResponse`) See [ListLuauExecutionSessionTaskLogsResponse](#listluauexecutionsessiontasklogsresponse) in Models. **Response example:** ```json { "luauExecutionSessionTaskLogs": [ { "path": "...", "messages": "...", "structuredMessages": "..." } ], "nextPageToken": "string" } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/places/{PLACE_ID}/versions/{VERSION_ID}/luau-execution-sessions/{LUAU_EXECUTION_SESSION_ID}/tasks/{TASK_ID}/logs" ``` ### GET `/cloud/v2/universes/{universe_id}/subscription-products/{subscription_product_id}/subscriptions/{subscription_id}` Get Subscription Get the subscription. The `universe.subscription-product.subscription:read` scope only allows reading subscriptions of the user making the request. Because of this, the subscription ID must match the user ID of the user making the request. Note that this scope might be more relevant for OAuth 2.0 apps. To read all subscriptions made by users for a particular universe, use the `universe:write` scope instead. **Scopes:** `universe:write`, `universe.subscription-product.subscription:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `subscription_product_id` | path | `string` | Yes | The subscription-product ID. | | `subscription_id` | path | `string` | Yes | The subscription ID. | | `view` | query | `VIEW_UNSPECIFIED \| BASIC \| FULL` | No | The view in which to retrieve the subscription. Supports BASIC and FULL. Defaults to BASIC. Possible values: \| Value \| Description \| \| --- \| --- \| \| VIEW_UNSPECIFIED \| The subscription view is not specified; the default will be used. \| \| BASIC \| Includes only the `active` and `renewing` fields. \| \| FULL \| Includes all fields. \| Valid values: `VIEW_UNSPECIFIED`, `BASIC`, `FULL` | **Responses:** - `200`: OK → `Subscription` **Response fields** (`Subscription`) See [Subscription](#subscription) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:00Z", "active": false, "willRenew": false, "lastBillingTime": "2024-01-01T00:00:00Z" } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/subscription-products/{SUBSCRIPTION_PRODUCT_ID}/subscriptions/{SUBSCRIPTION_ID}" ``` ### GET `/cloud/v2/universes/{universe_id}/user-restrictions` List User Restrictions List user restrictions for users that have ever been banned in either a universe or a specific place. **Scopes:** `universe.user-restriction:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `maxPageSize` | query | `integer` | No | The maximum number of user restrictions to return. The service might return fewer than this value. If unspecified, at most 10 user restrictions are returned. The maximum value is 100 and higher values are set to 100. | | `pageToken` | query | `string` | No | A page token, received from a previous call, to retrieve a subsequent page. When paginating, all other parameters provided to the subsequent call must match the call that provided the page token. | **Responses:** - `200`: OK → `ListUserRestrictionsResponse` **Response fields** (`ListUserRestrictionsResponse`) See [ListUserRestrictionsResponse](#listuserrestrictionsresponse) in Models. **Response example:** ```json { "userRestrictions": [ { "path": "...", "updateTime": "...", "user": "...", "gameJoinRestriction": "..." } ], "nextPageToken": "string" } ``` **Rate Limit Note:** User Restrictions are subject to the additional rate limits, applied per universe * Get User Restriction: 50 req/s * List User Restrictions: 50 req/s * Update User Restrictions: 10 req/s * List User Restriction Logs: 50 req/s Additionally, we impose the following rate limit for the same user within a universe: * Update User Restriction: 2 req/min For example, Update User Restriction may not be called for user 123 more than twice every minute. **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/user-restrictions" ``` ### GET `/cloud/v2/universes/{universe_id}/user-restrictions/{user_restriction_id}` Get User Restriction Get the user restriction. **Scopes:** `universe.user-restriction:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `user_restriction_id` | path | `string` | Yes | The user-restriction ID. | **Responses:** - `200`: OK → `UserRestriction` **Response fields** (`UserRestriction`) See [UserRestriction](#userrestriction) in Models. **Response example:** ```json { "path": "string", "updateTime": "2024-01-01T00:00:00Z", "user": "string", "gameJoinRestriction": { "active": false, "startTime": "2024-01-01T00:00:00Z", "duration": "string", "privateReason": "string", "displayReason": "string", "excludeAltAccounts": false } } ``` **Rate Limit Note:** User Restrictions are subject to the additional rate limits, applied per universe * Get User Restriction: 50 req/s * List User Restrictions: 50 req/s * Update User Restrictions: 10 req/s * List User Restriction Logs: 50 req/s Additionally, we impose the following rate limit for the same user within a universe: * Update User Restriction: 2 req/min For example, Update User Restriction may not be called for user 123 more than twice every minute. **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/user-restrictions/{USER_RESTRICTION_ID}" ``` ### PATCH `/cloud/v2/universes/{universe_id}/user-restrictions/{user_restriction_id}` Update User Restriction Update the user restriction. **Scopes:** `universe.user-restriction:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `user_restriction_id` | path | `string` | Yes | The user-restriction ID. | | `updateMask` | query | `string` | No | The list of fields to update. The `game_join_restriction` field must be updated atomically; field masks that index into `game_join_restriction` (such as `"game_join_restriction.active"`) are not supported. | | `idempotencyKey.key` | query | `string` | No | The unique key to use for idempotency. | | `idempotencyKey.firstSent` | query | `string` | No | The timestamp at which the first request was sent. If this is further in the past than the lifetime of the idempotency key (which *may* exceed the annotated minimum lifetime), the server *must* return an error. | **Request Body:** `application/json` — Type: `UserRestriction` See [UserRestriction](#userrestriction) in Models. **Request example:** ```json { "path": "string", "updateTime": "2024-01-01T00:00:00Z", "user": "string", "gameJoinRestriction": { "active": false, "startTime": "2024-01-01T00:00:00Z", "duration": "string", "privateReason": "string", "displayReason": "string", "excludeAltAccounts": false } } ``` > **Verify mutations:** If your API key lacks the required scope (`universe.user-restriction:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `UserRestriction` **Response fields** (`UserRestriction`) See [UserRestriction](#userrestriction) in Models. **Response example:** ```json { "path": "string", "updateTime": "2024-01-01T00:00:00Z", "user": "string", "gameJoinRestriction": { "active": false, "startTime": "2024-01-01T00:00:00Z", "duration": "string", "privateReason": "string", "displayReason": "string", "excludeAltAccounts": false } } ``` **Rate Limit Note:** User Restrictions are subject to the additional rate limits, applied per universe * Get User Restriction: 50 req/s * List User Restrictions: 50 req/s * Update User Restrictions: 10 req/s * List User Restriction Logs: 50 req/s Additionally, we impose the following rate limit for the same user within a universe: * Update User Restriction: 2 req/min For example, Update User Restriction may not be called for user 123 more than twice every minute. **Example:** ```bash curl -X PATCH -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/user-restrictions/{USER_RESTRICTION_ID}" \ -H "Content-Type: application/json" \ -d '{ "path": "string", "updateTime": "2024-01-01T00:00:00Z", "user": "string", "gameJoinRestriction": { "active": false, "startTime": "2024-01-01T00:00:00Z", "duration": "string", "privateReason": "string", "displayReason": "string", "excludeAltAccounts": false } }' ``` ### GET `/cloud/v2/universes/{universe_id}/user-restrictions:listLogs` List User Restriction Logs List changes to UserRestriction resources within a given universe. This includes both universe-level and place-level restrictions. For universe-level restriction logs, the `place` field will be empty. **Scopes:** `universe.user-restriction:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `maxPageSize` | query | `integer` | No | The maximum number of UserRestrictionLogs to return. The service may return fewer than this value. If unspecified, at most 10 UserRestrictionLogs are returned. The maximum value is 100 and higher values are set to 100. | | `pageToken` | query | `string` | No | A page token, received from a previous call, to retrieve a subsequent page. When paginating, all other parameters provided to the subsequent call must match the call that provided the page token. | | `filter` | query | `string` | No | This field may be set to filter the logs returned. The `filter` field supports a very small number of CEL: * `user` * `place` * The `==` comparison operator is available. * The `&&` logical operator is also supported. As an example, filtering for a user and a place takes the form `filter="user == 'users/123'" && "place == 'places/456'"` | **Responses:** - `200`: OK → `ListUserRestrictionLogsResponse` **Response fields** (`ListUserRestrictionLogsResponse`) See [ListUserRestrictionLogsResponse](#listuserrestrictionlogsresponse) in Models. **Response example:** ```json { "logs": [ { "user": "...", "place": "...", "moderator": "...", "createTime": "...", "active": "...", "startTime": "..." } ], "nextPageToken": "string" } ``` **Rate Limit Note:** User Restrictions are subject to the additional rate limits, applied per universe * Get User Restriction: 50 req/s * List User Restrictions: 50 req/s * Update User Restrictions: 10 req/s * List User Restriction Logs: 50 req/s Additionally, we impose the following rate limit for the same user within a universe: * Update User Restriction: 2 req/min For example, Update User Restriction may not be called for user 123 more than twice every minute. **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/user-restrictions:listLogs" ``` ### POST `/cloud/v2/universes/{universe_id}:generateSpeechAsset` Generate Speech Asset Generates an English speech audio asset from the specified text. This endpoint requires the `asset:read` and `asset:write` scopes in addition to the `universe:write` scope. The response returns an `Operation` object that must be prefixed with `/assets/v1`. For example, the URL to discover the result of the operation could be `https://apis.roblox.com/assets/v1/operations/8b42ef30-9c17-4526-b8cf-2ff0136ca94d`. **Scopes:** `universe:write`, `asset:read`, `asset:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | **Request Body:** `application/json` — Type: `GenerateSpeechAssetRequest` See [GenerateSpeechAssetRequest](#generatespeechassetrequest) in Models. **Request example:** ```json { "text": "string", "speechStyle": { "voiceId": "string", "pitch": 0, "speed": 0 } } ``` > **Verify mutations:** If your API key lacks the required scope (`universe:write`, `asset:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Operation` **Response fields** (`Operation`) See [Operation](#operation) in Models. **Response example:** ```json { "path": "string", "metadata": { "@type": "string" }, "done": false, "error": { "code": 0, "message": "string", "details": [ "..." ] }, "response": { "@type": "string" } } ``` **Example:** ```bash curl -X POST -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}:generateSpeechAsset" \ -H "Content-Type: application/json" \ -d '{ "text": "string", "speechStyle": { "voiceId": "string", "pitch": 0, "speed": 0 } }' ``` ### POST `/cloud/v2/universes/{universe_id}:publishMessage` Publish Universe Message Publishes a message to the universe's live servers. Servers can consume messages via [MessagingService](https://create.roblox.com/docs/reference/engine/classes/MessagingService). **Scopes:** `universe-messaging-service:publish` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | **Request Body:** `application/json` — Type: `PublishUniverseMessageRequest` See [PublishUniverseMessageRequest](#publishuniversemessagerequest) in Models. **Request example:** ```json { "topic": "string", "message": "string" } ``` **Responses:** - `200`: OK **Rate Limit Note:** Messaging Service requests are subject to additional throttling limits described in the [Open Cloud guide for Messaging Service](https://create.roblox.com/docs/cloud/guides/usage-messaging#limits). **Example:** ```bash curl -X POST -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}:publishMessage" \ -H "Content-Type: application/json" \ -d '{ "topic": "string", "message": "string" }' ``` ### POST `/cloud/v2/universes/{universe_id}:restartServers` Restart Universe Servers Restarts active servers for a specific universe. Defaults to only restarting servers running older versions, but can be configured to restart all servers regardless of version. Used for releasing experience updates. **Scopes:** `universe:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | **Request Body:** `application/json` — Type: `RestartUniverseServersRequest` See [RestartUniverseServersRequest](#restartuniverseserversrequest) in Models. **Request example:** ```json { "placeIds": [ 0 ], "closeAllVersions": false, "bleedOffServers": false, "bleedOffDurationMinutes": 0 } ``` > **Verify mutations:** If your API key lacks the required scope (`universe:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `RestartUniverseServersResponse` **Response fields** (`RestartUniverseServersResponse`) See [RestartUniverseServersResponse](#restartuniverseserversresponse) in Models. **Response example:** ```json {} ``` **Example:** ```bash curl -X POST -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}:restartServers" \ -H "Content-Type: application/json" \ -d '{ "placeIds": [ 0 ], "closeAllVersions": false, "bleedOffServers": false, "bleedOffDurationMinutes": 0 }' ``` ### POST `/cloud/v2/universes/{universe_id}:translateText` Translate Text Translates the provided text from one language to another. **Scopes:** `universe:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | **Request Body:** `application/json` — Type: `TranslateTextRequest` See [TranslateTextRequest](#translatetextrequest) in Models. **Request example:** ```json { "text": "string", "sourceLanguageCode": "string", "targetLanguageCodes": [ "string" ] } ``` > **Verify mutations:** If your API key lacks the required scope (`universe:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `TranslateTextResponse` **Response fields** (`TranslateTextResponse`) See [TranslateTextResponse](#translatetextresponse) in Models. **Response example:** ```json { "sourceLanguageCode": "string", "translations": "..." } ``` **Rate Limit Note:** Text translation requests are subject to additional rate limits [documented here](https://create.roblox.com/docs/production/localization/auto-translate-dynamic-content#limits). **Example:** ```bash curl -X POST -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}:translateText" \ -H "Content-Type: application/json" \ -d '{ "text": "string", "sourceLanguageCode": "string", "targetLanguageCodes": [ "string" ] }' ``` ### GET `/cloud/v2/users/{user_id}` Get User Gets a user's basic and advanced information. To access a user's public information, no additional scopes are required. To access a user's verification status, you need the following scopes: * user.advanced:read To access a user's social account information, you need the following scopes: * user.social:read **Scopes:** `user.advanced:read`, `user.social:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `user_id` | path | `string` | Yes | The user ID. | **Responses:** - `200`: OK → `User` **Response fields** (`User`) See [User](#user) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "id": "string", "name": "string", "displayName": "string", "about": "string" } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/users/{USER_ID}" ``` ### GET `/cloud/v2/users/{user_id}/asset-quotas` List Asset Quotas Returns a list of asset quotas. **Scopes:** `asset:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `user_id` | path | `string` | Yes | The user ID. | | `maxPageSize` | query | `integer` | No | The maximum number of asset quotas to return. The service might return fewer than this value. If unspecified, at most 10 asset quotas are returned. The maximum value is 100 and higher values are set to 100. | | `pageToken` | query | `string` | No | A page token, received from a previous call, to retrieve a subsequent page. When paginating, all other parameters provided to the subsequent call must match the call that provided the page token. | | `filter` | query | `string` | No | This field may be set in order to filter the resources returned. Supports the following subset of CEL: * Only the `quotaType` and `assetType` fields are supported. * Only the `==` and `&&` operator are supported. For example: `quotaType == RateLimitUpload && assetType == Audio` | **Responses:** - `200`: OK → `ListAssetQuotasResponse` **Response fields** (`ListAssetQuotasResponse`) See [ListAssetQuotasResponse](#listassetquotasresponse) in Models. **Response example:** ```json { "assetQuotas": [ { "path": "...", "quotaType": "...", "assetType": "...", "usage": "...", "capacity": "...", "period": "..." } ], "nextPageToken": "string" } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/users/{USER_ID}/asset-quotas" ``` ### GET `/cloud/v2/users/{user_id}/inventory-items` List Inventory Items List the inventory items in a user's inventory. The inventory items returned depend on the target user’s choice under **Settings > Privacy > Who can see my inventory?**: * If the user granted inventory visibility to "Everyone," then any API key or OAuth2 token can be used to view the target’s inventory, no matter what scopes it has or who created it. * If the user has not granted inventory visibility to "Everyone": * Their inventory can still be viewed with an API key created by the target user with **Inventory: Read** permission. * Their inventory can still be viewed with an OAuth2 token if the target user authorizes an app requesting permissions for the `user.inventory-item:read` scope. **Scopes:** `user.inventory-item:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `user_id` | path | `string` | Yes | The user ID. | | `maxPageSize` | query | `integer` | No | The maximum number of inventory items to return. The service might return fewer than this value. If unspecified, at most 10 inventory items are returned. The maximum value is 100 and higher values are set to 100. | | `pageToken` | query | `string` | No | A page token, received from a previous call, to retrieve a subsequent page. When paginating, all other parameters provided to the subsequent call must match the call that provided the page token. | | `filter` | query | `string` | No | This field may be set in order to filter the resources returned. See the [filtering](/docs/en-us/cloud/reference/patterns.md#list-inventory-items) documentation for more information. | **Responses:** - `200`: OK → `ListInventoryItemsResponse` **Response fields** (`ListInventoryItemsResponse`) See [ListInventoryItemsResponse](#listinventoryitemsresponse) in Models. **Response example:** ```json { "inventoryItems": [ { "path": "...", "assetDetails": "...", "badgeDetails": "...", "gamePassDetails": "...", "privateServerDetails": "...", "addTime": "..." } ], "nextPageToken": "string" } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/users/{USER_ID}/inventory-items" ``` ### POST `/cloud/v2/users/{user_id}/notifications` Create User Notification Sends a notification to a user. **Scopes:** `user.user-notification:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `user_id` | path | `string` | Yes | The user ID. | **Request Body:** `application/json` — Type: `UserNotification` See [UserNotification](#usernotification) in Models. **Request example:** ```json { "path": "string", "id": "string", "source": { "universe": "string" }, "payload": { "type": "TYPE_UNSPECIFIED", "messageId": "string", "parameters": "...", "joinExperience": { "launchData": "..." }, "analyticsData": { "category": "..." } } } ``` > **Verify mutations:** If your API key lacks the required scope (`user.user-notification:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `UserNotification` **Response fields** (`UserNotification`) See [UserNotification](#usernotification) in Models. **Response example:** ```json { "path": "string", "id": "string", "source": { "universe": "string" }, "payload": { "type": "TYPE_UNSPECIFIED", "messageId": "string", "parameters": "...", "joinExperience": { "launchData": "..." }, "analyticsData": { "category": "..." } } } ``` **Example:** ```bash curl -X POST -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/users/{USER_ID}/notifications" \ -H "Content-Type: application/json" \ -d '{ "path": "string", "id": "string", "source": { "universe": "string" }, "payload": { "type": "TYPE_UNSPECIFIED", "messageId": "string", "parameters": "...", "joinExperience": { "launchData": "..." }, "analyticsData": { "category": "..." } } }' ``` ### GET `/cloud/v2/users/{user_id}:generateThumbnail` Generate User Thumbnail Generates and returns the URL for the user's avatar thumbnail. **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `user_id` | path | `string` | Yes | The user ID. | | `size` | query | `integer` | No | Size of the generated thumbnail. The generated thumbnail will have `size * size` dimension. Currently supported values: 48, 50, 60, 75, 100, 110, 150, 180, 352, 420, 720 Default is 420. | | `format` | query | `FORMAT_UNSPECIFIED \| PNG \| JPEG` | No | Specify the format of the generated thumbnail. Default is `PNG`. Possible values: \| Value \| Description \| \| --- \| --- \| \| FORMAT_UNSPECIFIED \| Default UserThumbnail Format -- set to png \| \| PNG \| Generate thumbnail in `.png` format \| \| JPEG \| Generate thumbnail in `.jpg` format \| Valid values: `FORMAT_UNSPECIFIED`, `PNG`, `JPEG` | | `shape` | query | `SHAPE_UNSPECIFIED \| ROUND \| SQUARE` | No | Specify the shape of the thumbnail. Default is `ROUND` (circular). Possible values: \| Value \| Description \| \| --- \| --- \| \| SHAPE_UNSPECIFIED \| Default UserThumbnail Shape -- set to round \| \| ROUND \| Generate thumbnail as a circle. \| \| SQUARE \| Generate thumbnail as a rectangle. \| Valid values: `SHAPE_UNSPECIFIED`, `ROUND`, `SQUARE` | **Responses:** - `200`: OK → `Operation` **Response fields** (`Operation`) See [Operation](#operation) in Models. **Response example:** ```json { "path": "string", "metadata": { "@type": "string" }, "done": false, "error": { "code": 0, "message": "string", "details": [ "..." ] }, "response": { "@type": "string" } } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/cloud/v2/users/{USER_ID}:generateThumbnail" ``` --- name: "Ordered data stores (beta)" last_updated: 2026-06-29T19:34:06Z type: opencloud api_base_url: "https://apis.roblox.com/ordered-data-stores" version: "0.0.1" endpoints: 6 auth: [api-key] description: "This page includes reference documentation for working with the Open Cloud v1 API for ordered data stores" --- # Ordered data stores (beta) This page includes reference documentation for working with the Open Cloud v1 API for ordered data stores. For information on using the v2 API, see the [usage guide](/docs/en-us/cloud/guides/data-stores.md). **API Version:** 0.0.1 **Base URL(s):** - `https://apis.roblox.com/ordered-data-stores` ## Authentication Each endpoint requires one of the following authentication methods: - **API Key**: Pass your key in the `x-api-key` HTTP header. Create keys at [Creator Dashboard](https://create.roblox.com/dashboard/credentials). ``` # API Key example curl -H "x-api-key: YOUR_API_KEY" https://apis.roblox.com/... # OAuth 2.0 example curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" https://apis.roblox.com/... ``` ## Endpoints ### GET `/v1/universes/{universeId}/orderedDataStores/{orderedDataStore}/scopes/{scope}/entries` [BETA] Returns a list of entries from an ordered data store. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe.ordered-data-store.scope.entry:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `string` | Yes | The identifier of the experience with ordered data stores that you want to access. You can find your experience's universe ID on Creator Hub. | | `orderedDataStore` | path | `string` | Yes | The name of the target ordered data store. | | `scope` | path | `string` | Yes | The name of the data store scope. See [Scopes](/docs/en-us/cloud/guides/data-stores/request-handling.md#scopes). | | `max_page_size` | query | `integer` | No | The maximum number of entries to return. The service may return fewer than this value. The default value is `10`. The maximum value is `100`, and any input above 100 is coerced to `100`. | | `page_token` | query | `string` | No | A page token received from a previous `List` call. Provide this to retrieve the subsequent page. When paginating, all other parameters provided to `List` must match the call providing the page token. | | `order_by` | query | `string` | No | The enumeration direction. The order by default is ascending. Input a `desc` suffix for descending. | | `filter` | query | `string` | No | The range of qualifying values of entries to return. See [Filters](/docs/en-us/cloud/guides/data-stores/request-handling.md#filters). | **Responses:** - `200`: OK → `ListEntriesResponse` - `400`: Bad Request: invalid orderedDataStore, scope or entry name or encoding. - `403`: Forbidden: studio access to APIs is not allowed, incorrect API key or scope. - `429`: Too Many Requests. **Response fields** (`ListEntriesResponse`) See [ListEntriesResponse](#listentriesresponse) in Models. **Response example:** ```json { "entries": [ { "path": "...", "id": "...", "value": "..." } ], "nextPageToken": "string" } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Rate Limit Note:** See [Throttling](/docs/en-us/cloud/guides/data-stores/throttling.md). **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/ordered-data-stores/v1/universes/{UNIVERSEID}/orderedDataStores/{ORDEREDDATASTORE}/scopes/{SCOPE}/entries" ``` ### POST `/v1/universes/{universeId}/orderedDataStores/{orderedDataStore}/scopes/{scope}/entries` [BETA] Creates a new entry with the content value provided. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe.ordered-data-store.scope.entry:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `string` | Yes | The identifier of the experience with ordered data stores that you want to access. You can find your experience's universe ID on Creator Hub. | | `orderedDataStore` | path | `string` | Yes | The name of the ordered data store. | | `scope` | path | `string` | Yes | The name of the data store scope. See [Scopes](/docs/en-us/cloud/guides/data-stores/request-handling.md#scopes). | | `id` | query | `string` | Yes | The name of the entry. | **Request Body:** `application/json` — Type: `CreateEntryRequest` See [CreateEntryRequest](#createentryrequest) in Models. **Request example:** ```json { "value": 0 } ``` > **Verify mutations:** If your API key lacks the required scope (`universe.ordered-data-store.scope.entry:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Entry` - `400`: Bad Request: invalid orderedDataStore, scope or entry name or encoding. - `403`: Forbidden: studio access to APIs is not allowed, incorrect API key or scope. - `404`: Not found. - `429`: Too Many Requests. **Response fields** (`Entry`) See [Entry](#entry) in Models. **Response example:** ```json { "path": "string", "id": "string", "value": 0 } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Rate Limit Note:** See [Throttling](/docs/en-us/cloud/guides/data-stores/throttling.md). **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/ordered-data-stores/v1/universes/{UNIVERSEID}/orderedDataStores/{ORDEREDDATASTORE}/scopes/{SCOPE}/entries?id={VALUE}" \ -H "Content-Type: application/json" \ -d '{ "value": 0 }' ``` ### GET `/v1/universes/{universeId}/orderedDataStores/{orderedDataStore}/scopes/{scope}/entries/{entry}` [BETA] Gets and returns the specified entry. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe.ordered-data-store.scope.entry:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `string` | Yes | The identifier of the experience with ordered data stores that you want to access. You can find your experience's universe ID on Creator Hub. | | `orderedDataStore` | path | `string` | Yes | The name of the ordered data store. | | `scope` | path | `string` | Yes | The name of the data store scope. See [Scopes](/docs/en-us/cloud/guides/data-stores/request-handling.md#scopes). | | `entry` | path | `string` | Yes | The entry ID. | **Responses:** - `200`: OK → `Entry` - `400`: Bad Request: invalid orderedDataStore, scope or entry name or encoding. - `403`: Forbidden: studio access to APIs is not allowed, incorrect API key or scope. - `404`: Not found. - `429`: Too Many Requests. **Response fields** (`Entry`) See [Entry](#entry) in Models. **Response example:** ```json { "path": "string", "id": "string", "value": 0 } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Rate Limit Note:** See [Throttling](/docs/en-us/cloud/guides/data-stores/throttling.md). **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/ordered-data-stores/v1/universes/{UNIVERSEID}/orderedDataStores/{ORDEREDDATASTORE}/scopes/{SCOPE}/entries/{ENTRY}" ``` ### PATCH `/v1/universes/{universeId}/orderedDataStores/{orderedDataStore}/scopes/{scope}/entries/{entry}` [BETA] Updates an entry value and returns the updated entry. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe.ordered-data-store.scope.entry:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `string` | Yes | The identifier of the experience with ordered data stores that you want to access. You can find your experience's universe ID on Creator Hub. | | `orderedDataStore` | path | `string` | Yes | The name of the ordered data store. | | `scope` | path | `string` | Yes | The name of the data store scope. See [Scopes](/docs/en-us/cloud/guides/data-stores/request-handling.md#scopes). | | `entry` | path | `string` | Yes | The entry ID. | | `allow_missing` | query | `boolean` | No | The flag to allow the creation of an entry if the entry doesn't exist. See [Allow missing flags](/docs/en-us/cloud/guides/data-stores/request-handling.md.md#allow-missing-flags). | **Request Body:** `application/json` — Type: `UpdateEntryRequest` See [UpdateEntryRequest](#updateentryrequest) in Models. **Request example:** ```json { "value": 0 } ``` > **Verify mutations:** If your API key lacks the required scope (`universe.ordered-data-store.scope.entry:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Entry` - `400`: Bad Request: invalid orderedDataStore, scope or entry name or encoding. - `403`: Forbidden: studio access to APIs is not allowed, incorrect API key or scope. - `404`: Not found. - `409`: Aborted. - `429`: Too Many Requests. **Response fields** (`Entry`) See [Entry](#entry) in Models. **Response example:** ```json { "path": "string", "id": "string", "value": 0 } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Rate Limit Note:** See [Throttling](/docs/en-us/cloud/guides/data-stores/throttling.md). **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/ordered-data-stores/v1/universes/{UNIVERSEID}/orderedDataStores/{ORDEREDDATASTORE}/scopes/{SCOPE}/entries/{ENTRY}" \ -H "Content-Type: application/json" \ -d '{ "value": 0 }' ``` ### DELETE `/v1/universes/{universeId}/orderedDataStores/{orderedDataStore}/scopes/{scope}/entries/{entry}` [BETA] Deletes the specified entry. Unlike standard data stores, which mark entries for deletion, ordered data store entries are deleted immediately. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe.ordered-data-store.scope.entry:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `string` | Yes | The identifier of the experience with ordered data stores that you want to access. You can find your experience's universe ID on Creator Hub. | | `orderedDataStore` | path | `string` | Yes | The name of the ordered data store. | | `scope` | path | `string` | Yes | The name of the data store scope. See [Scopes](/docs/en-us/cloud/guides/data-stores/request-handling.md#scopes). | | `entry` | path | `string` | Yes | The entry ID. | > **Verify mutations:** If your API key lacks the required scope (`universe.ordered-data-store.scope.entry:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: Success: the entry was successfully deleted or didn't exist. - `400`: Bad Request: invalid orderedDataStore, scope or entry name or encoding. - `403`: Forbidden: Studio access to APIs is not allowed, incorrect API key or scope. - `404`: Not found. - `429`: Too Many Requests. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Rate Limit Note:** See [Throttling](/docs/en-us/cloud/guides/data-stores/throttling.md). **Example:** ```bash curl -X DELETE -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/ordered-data-stores/v1/universes/{UNIVERSEID}/orderedDataStores/{ORDEREDDATASTORE}/scopes/{SCOPE}/entries/{ENTRY}" ``` ### POST `/v1/universes/{universeId}/orderedDataStores/{orderedDataStore}/scopes/{scope}/entries/{entry}:increment` [BETA] Increments the value of the key by the provided amount and returns the updated entry. Known issue: Entry values can increment past the valid range and this may persist in the backend. Returned values will clamp to the valid range. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe.ordered-data-store.scope.entry:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `string` | Yes | The identifier of the experience with ordered data stores that you want to access. You can find your experience's universe ID on Creator Hub. | | `orderedDataStore` | path | `string` | Yes | The name of the ordered data store. | | `scope` | path | `string` | Yes | The name of the data store scope. See [Scopes](/docs/en-us/cloud/guides/data-stores/request-handling.md#scopes). | | `entry` | path | `string` | Yes | The entry ID. | **Request Body:** `application/json` — Type: `IncrementEntryRequest` See [IncrementEntryRequest](#incremententryrequest) in Models. **Request example:** ```json { "amount": 0 } ``` > **Verify mutations:** If your API key lacks the required scope (`universe.ordered-data-store.scope.entry:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Entry` - `400`: Bad Request: invalid orderedDataStore, scope or entry name or encoding. - `403`: Forbidden: studio access to APIs is not allowed, incorrect API key or scope. - `404`: Not found. - `429`: Too Many Requests. **Response fields** (`Entry`) See [Entry](#entry) in Models. **Response example:** ```json { "path": "string", "id": "string", "value": 0 } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Rate Limit Note:** See [Throttling](/docs/en-us/cloud/guides/data-stores/throttling.md). **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/ordered-data-stores/v1/universes/{UNIVERSEID}/orderedDataStores/{ORDEREDDATASTORE}/scopes/{SCOPE}/entries/{ENTRY}:increment" \ -H "Content-Type: application/json" \ -d '{ "amount": 0 }' ``` ## Models ### Entry Represents an entry. | Property | Type | Required | Description | |----------|------|----------|-------------| | `path` | `string` | No | The resource path of the request. | | `id` | `string` | No | The name of the entry | | `value` | `integer` | No | The value of the entry. | ### IncrementEntryRequest Increments entry value. | Property | Type | Required | Description | |----------|------|----------|-------------| | `amount` | `integer` | Yes | The amount to increment by the entry value. If the input value exceeds the maximum value supported by int64, which is 9,223,372,036,854,775,807, the request fails with a 400 Bad Request error. | ### UpdateEntryRequest Updates the entry provided with a new value. | Property | Type | Required | Description | |----------|------|----------|-------------| | `value` | `integer` | Yes | The value to update the entry. If the input value exceeds the maximum value supported by int64, which is 9,223,372,036,854,775,807, the request fails with a 400 Bad Request error. | ### CreateEntryRequest Creates a new entry with the value provided. | Property | Type | Required | Description | |----------|------|----------|-------------| | `value` | `integer` | Yes | The value to set the new entry. If the input value exceeds the maximum value supported by int64, which is 9,223,372,036,854,775,807, the request fails with a 400 Bad Request error. | ### ListEntriesResponse A list of Entries in the parent collection. | Property | Type | Required | Description | |----------|------|----------|-------------| | `entries` | `Entry[]` | No | The Entries from the specified Scope. | | `nextPageToken` | `string` | No | A token, which can be sent as `page_token` to retrieve the next page. If this field is omitted, there are no subsequent pages. | --- name: "Standard data stores" last_updated: 2026-06-29T19:34:06Z type: opencloud api_base_url: "https://apis.roblox.com/datastores" version: "v1" endpoints: 8 auth: [api-key] description: "This page includes reference documentation for working with the Open Cloud v1 API for standard data stores" --- # Standard data stores This page includes reference documentation for working with the Open Cloud v1 API for standard data stores. For information on using the v2 API, see the [usage guide](/docs/en-us/cloud/guides/data-stores.md). **API Version:** v1 **Base URL(s):** - `https://apis.roblox.com/datastores` ## Authentication Each endpoint requires one of the following authentication methods: - **API Key**: Pass your key in the `x-api-key` HTTP header. Create keys at [Creator Dashboard](https://create.roblox.com/dashboard/credentials). ``` # API Key example curl -H "x-api-key: YOUR_API_KEY" https://apis.roblox.com/... # OAuth 2.0 example curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" https://apis.roblox.com/... ``` ## Endpoints ### GET `/v1/universes/{universeId}/standard-datastores` [BETA] List data stores in an experience Returns a list of an experience's data stores. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe-datastores.control:list` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The identifier of the experience with data stores that you want to access. You can find your experience's universe ID on Creator Hub. | | `cursor` | query | `string` | No | Provide to request the next set of data. | | `limit` | query | `integer` | No | The maximum number of items to return. Each call only reads one partition, so it can return fewer than the given value when running out of objectives on one partition. | | `prefix` | query | `string` | No | Provide to return only data stores with this prefix. | **Responses:** - `200`: → `object` **Response fields** (`object`) | Property | Type | Required | Description | |----------|------|----------|-------------| | `data` | `DataStore[]` | No | An array of data stores in the target experience. | | `nextPageCursor` | `string` | No | Indicates that there is more data available in the requested result set. | **Response example:** ```json { "data": [ { "name": "...", "createdTime": "..." } ], "nextPageCursor": "string" } ``` **Rate Limit Note:** See [Throttling](/docs/en-us/cloud/guides/data-stores/throttling.md). **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/datastores/v1/universes/{UNIVERSEID}/standard-datastores" ``` ### GET `/v1/universes/{universeId}/standard-datastores/datastore/entries` [BETA] List entries Returns a list of entry keys within a data store. Entries marked deleted with a tombstone version are still included in the response if they have yet to be permanently deleted. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe-datastores.objects:list` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The identifier of the experience with data stores that you want to access. You can find your experience's universe ID on Creator Hub. | | `datastoreName` | query | `string` | No | The name of the data store. | | `scope` | query | `string` | No | The value is `global` by default. See [Scopes](/docs/en-us/cloud-services/data-stores.md#scopes). | | `allScopes` | query | `boolean` | No | Set to true to return keys from all scopes. | | `prefix` | query | `string` | No | Provide to return only keys with this prefix. | | `cursor` | query | `string` | No | Provide to request the next set of data. | | `limit` | query | `integer` | No | The maximum number of items to return. Each call only reads one partition, so it can return fewer than the given value when running out of objectives on one partition. | **Responses:** - `200`: → `object` **Response fields** (`object`) | Property | Type | Required | Description | |----------|------|----------|-------------| | `keys` | `string[]` | No | An array of entry keys within the target data store. | | `nextPageCursor` | `string` | No | Indicates that there is more data available in the requested result set. | **Response example:** ```json { "keys": [ "string" ], "nextPageCursor": "string" } ``` **Rate Limit Note:** See [Throttling](/docs/en-us/cloud/guides/data-stores/throttling.md). **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/datastores/v1/universes/{UNIVERSEID}/standard-datastores/datastore/entries" ``` ### GET `/v1/universes/{universeId}/standard-datastores/datastore/entries/entry` [BETA] Get entry. Returns the value and metadata associated with an entry. Entries marked deleted with a tombstone version will return 404 Not Found. Metadata can be found in the response headers like the following: ```text content-md5: zuYxEhwuySMv0i8CitXImw== roblox-entry-version: 08D9E6A3F2188CFF.0000000001.08D9E6A3F2188CFF.01 roblox-entry-created-time: 2022-02-02T23:30:06.5388799+00:00 roblox-entry-version-created-time: 2022-02-02T23:30:06.5388799+00:00 roblox-entry-attributes: { "myAttribute": "myValue" } roblox-entry-userids: [1, 2, 3] ``` | Header | Description | |---|---| | `content-md5` | The base64-encoded MD5 checksum of the content. See [Content-MD5](/docs/en-us/cloud/guides/data-stores/request-handling.md#content-md5). | | `roblox-entry-version` | The version of the returned entry. | | `roblox-entry-created-time` | The time at which the entry was created. | | `roblox-entry-version-created-time` | The time at which this particular version was created. | | `roblox-entry-attributes` | Attributes tagged with the entry. Serialized JSON map object. | | `roblox-entry-userids` | Comma-separated list of Roblox user IDs tagged with the entry. | **Auth:** API Key (`x-api-key` header) **Scopes:** `universe-datastores.objects:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The identifier of the experience with data stores that you want to access. You can find your experience's universe ID on Creator Hub. | | `datastoreName` | query | `string` | No | The name of the data store. | | `entryKey` | query | `string` | No | The key identifying the entry. | | `scope` | query | `string` | No | The value is `global` by default. See [Scopes](/docs/en-us/cloud-services/data-stores.md#scopes). | **Responses:** - `200`: Successfully retrieved the entry. → `object` - `204`: The key is marked as deleted. **Rate Limit Note:** See [Throttling](/docs/en-us/cloud/guides/data-stores/throttling.md). **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/datastores/v1/universes/{UNIVERSEID}/standard-datastores/datastore/entries/entry" ``` ### POST `/v1/universes/{universeId}/standard-datastores/datastore/entries/entry` [BETA] Set entry. Sets the value, metadata and user IDs associated with an entry. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe-datastores.objects:update`, `universe-datastores.objects:create`, `universe-datastores.control:create` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The identifier of the experience with data stores that you want to access. You can find your experience's universe ID on Creator Hub. | | `datastoreName` | query | `string` | No | The name of the data store. | | `entryKey` | query | `string` | No | The key identifying the entry. | | `matchVersion` | query | `string` | No | Provide to update only if the current version matches this. | | `exclusiveCreate` | query | `boolean` | No | Create the entry only if it does not exist. | | `scope` | query | `string` | No | The value is `global` by default. See [Scopes](/docs/en-us/cloud-services/data-stores.md#scopes). | | `roblox-entry-attributes` | header | `string` | No | Attributes to be associated with new version of the entry. Serialized by JSON map objects. If not provided, existing attributes are cleared. | | `roblox-entry-userids` | header | `string` | No | Comma-separated list of Roblox user IDs tagged with the entry. If not provided, existing user IDs are cleared. | | `content-md5` | header | `string` | No | The base64-encoded MD5 checksum of the content. See [Content-MD5](/docs/en-us/cloud/guides/data-stores/request-handling.md#content-md5). | **Request Body:** `application/json` — Type: `string` **Responses:** - `200`: → `EntryVersion` **Response fields** (`EntryVersion`) See [EntryVersion](#entryversion) in Models. **Response example:** ```json { "version": "string", "deleted": false, "contentLength": 0, "createdTime": "2024-01-01T00:00:00Z", "objectCreatedTime": "2024-01-01T00:00:00Z" } ``` **Rate Limit Note:** See [Throttling](/docs/en-us/cloud/guides/data-stores/throttling.md). **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/datastores/v1/universes/{UNIVERSEID}/standard-datastores/datastore/entries/entry" \ -H "Content-Type: application/json" \ -d '"string"' ``` ### DELETE `/v1/universes/{universeId}/standard-datastores/datastore/entries/entry` [BETA] Delete entry. Marks the entry as deleted by creating a tombstone version. Entries are deleted permanently after 30 days. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe-datastores.objects:delete` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The identifier of the experience with data stores that you want to access. You can find your experience's universe ID on Creator Hub. | | `datastoreName` | query | `string` | No | The name of the data store. | | `entryKey` | query | `string` | No | The key identifying the entry. | | `scope` | query | `string` | No | The value is `global` by default. See [Scopes](/docs/en-us/cloud-services/data-stores.md#scopes). | **Responses:** - `204`: The entry is deleted. **Rate Limit Note:** See [Throttling](/docs/en-us/cloud/guides/data-stores/throttling.md). **Example:** ```bash curl -X DELETE -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/datastores/v1/universes/{UNIVERSEID}/standard-datastores/datastore/entries/entry" ``` ### POST `/v1/universes/{universeId}/standard-datastores/datastore/entries/entry/increment` [BETA] Increment entry Increments the value for an entry by a given amount, or create a new entry with that amount. Returns the entry and metadata. Metadata can be found in the response headers like the following: ```text content-md5: zuYxEhwuySMv0i8CitXImw== roblox-entry-version: 08D9E6A3F2188CFF.0000000001.08D9E6A3F2188CFF.01 roblox-entry-created-time: 2022-02-02T23:30:06.5388799+00:00 roblox-entry-version-created-time: 2022-02-02T23:30:06.5388799+00:00 roblox-entry-attributes: { "myAttribute": "myValue" } roblox-entry-userids: [1, 2, 3] ``` | Header | Description | |---|---| | `content-md5` | The base64-encoded MD5 checksum of the content. See [Content-MD5](/docs/en-us/cloud/guides/data-stores/request-handling.md#content-md5). | | `roblox-entry-version` | The version of the returned entry. | | `roblox-entry-created-time` | The time at which the entry was created. | | `roblox-entry-version-created-time` | The time at which this particular version was created. | | `roblox-entry-attributes` | Attributes tagged with the entry. Serialized JSON map object. | | `roblox-entry-userids` | Comma-separated list of Roblox user IDs tagged with the entry. | **Auth:** API Key (`x-api-key` header) **Scopes:** `universe-datastores.objects:update`, `universe-datastores.objects:create`, `universe-datastores.control:create` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The identifier of the experience with data stores that you want to access. You can find your experience's universe ID on Creator Hub. | | `datastoreName` | query | `string` | No | The name of the data store. | | `entryKey` | query | `string` | No | The key identifying the entry. | | `incrementBy` | query | `integer` | No | The amount by which the entry should be incremented, or the starting value if it doesn't exist. | | `scope` | query | `string` | No | The value is `global` by default. See [Scopes](/docs/en-us/cloud-services/data-stores.md#scopes). | | `roblox-entry-attributes` | header | `string` | No | Attributes to be associated with new version of the entry. Serialized by JSON map objects. If not provided, existing attributes are cleared. | | `roblox-entry-userids` | header | `string` | No | A comma-separated list of Roblox user IDs that the entry is tagged with. If not provided, existing user IDs are cleared. | **Responses:** - `200`: Returns the latest version of the entry after it has been incremented. → `object` - `204`: The key is marked as deleted. **Rate Limit Note:** See [Throttling](/docs/en-us/cloud/guides/data-stores/throttling.md). **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/datastores/v1/universes/{UNIVERSEID}/standard-datastores/datastore/entries/entry/increment" ``` ### GET `/v1/universes/{universeId}/standard-datastores/datastore/entries/entry/versions/version` [BETA] Get entry version. Returns the value and metadata of a specific version of an entry. Metadata can be found in the response headers like the following: ```text content-md5: zuYxEhwuySMv0i8CitXImw== roblox-entry-version: 08D9E6A3F2188CFF.0000000001.08D9E6A3F2188CFF.01 roblox-entry-created-time: 2022-02-02T23:30:06.5388799+00:00 roblox-entry-version-created-time: 2022-02-02T23:30:06.5388799+00:00 roblox-entry-attributes: { "myAttribute": "myValue" } roblox-entry-userids: [1, 2, 3] ``` | Header | Description | |---|---| | `content-md5` | The base64-encoded MD5 checksum of the content. See [Content-MD5](/docs/en-us/cloud/guides/data-stores/request-handling.md#content-md5). | | `roblox-entry-version` | The version of the returned entry. | | `roblox-entry-created-time` | The time at which the entry was created. | | `roblox-entry-version-created-time` | The time at which this particular version was created. | | `roblox-entry-attributes` | Attributes tagged with the entry. Serialized JSON map object. | | `roblox-entry-userids` | Comma-separated list of Roblox user IDs tagged with the entry. | **Auth:** API Key (`x-api-key` header) **Scopes:** `universe-datastores.versions:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The identifier of the experience with data stores that you want to access. You can find your experience's universe ID on Creator Hub. | | `datastoreName` | query | `string` | No | The name of the data store. | | `entryKey` | query | `string` | No | The key identifying the entry. | | `versionId` | query | `string` | No | The version to inspect. | | `scope` | query | `string` | No | The value is `global` by default. See [Scopes](/docs/en-us/cloud-services/data-stores.md#scopes). | **Responses:** - `200`: Successfully retrieved the entry. → `object` **Rate Limit Note:** See [Throttling](/docs/en-us/cloud/guides/data-stores/throttling.md). **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/datastores/v1/universes/{UNIVERSEID}/standard-datastores/datastore/entries/entry/versions/version" ``` ### GET `/v1/universes/{universeId}/standard-datastores/datastore/entries/entry/versions` [BETA] List entry versions Returns a list of versions for an entry. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe-datastores.versions:list` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The identifier of the experience with data stores that you want to access. You can find your experience's universe ID on Creator Hub. | | `datastoreName` | query | `string` | No | The name of the data store. | | `entryKey` | query | `string` | No | The key identifying the entry. | | `scope` | query | `string` | No | The value is `global` by default. See [Scopes](/docs/en-us/cloud-services/data-stores.md#scopes). | | `cursor` | query | `string` | No | Provide to request the next set of data. | | `startTime` | query | `string` | No | Provide to not include versions earlier than this timestamp. | | `endTime` | query | `string` | No | Provide to not include versions later than this timestamp. | | `sortOrder` | query | `string` | No | Either `Ascending` (earlier versions first) or `Descending` (later versions first). | | `limit` | query | `integer` | No | The maximum number of items to return. Each call only reads one partition, so it can return fewer than the given value when running out of objectives on one partition. | **Responses:** - `200`: → `EntryVersion` - `400`: Invalid request / Invalid file content. - `403`: Publish not allowed on place. - `404`: The experience or data store was not found. - `429`: Too Many Requests. **Response fields** (`EntryVersion`) See [EntryVersion](#entryversion) in Models. **Response example:** ```json { "version": "string", "deleted": false, "contentLength": 0, "createdTime": "2024-01-01T00:00:00Z", "objectCreatedTime": "2024-01-01T00:00:00Z" } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Rate Limit Note:** See [Throttling](/docs/en-us/cloud/guides/data-stores/throttling.md). **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/datastores/v1/universes/{UNIVERSEID}/standard-datastores/datastore/entries/entry/versions" ``` ## Models ### DataStore The data store object with its name and created time. | Property | Type | Required | Description | |----------|------|----------|-------------| | `name` | `string` | No | The name of your data store. | | `createdTime` | `string` | No | The timestamp of when the data store was created in the ISO time format. | ### EntryVersion The entry version object returned by the `List Entry Versions` method. | Property | Type | Required | Description | |----------|------|----------|-------------| | `version` | `string` | No | The version name of the qualifying entry. | | `deleted` | `boolean` | No | Indicates whether the entry has been deleted. | | `contentLength` | `number` | No | The length of the content. | | `createdTime` | `string` | No | The timestamp of when the version was created in the ISO time format. | | `objectCreatedTime` | `string` | No | The timestamp of when the data store was created in the ISO time format. | --- name: "developer-products-api" last_updated: 2026-06-29T19:34:06Z type: opencloud api_base_url: "https://apis.roblox.com" version: "v1" endpoints: 4 auth: [api-key, cookie] --- # developer-products-api **API Version:** v1 **Base URL(s):** - `https://apis.roblox.com` ## Authentication Each endpoint requires one of the following authentication methods: - **API Key**: Pass your key in the `x-api-key` HTTP header. Create keys at [Creator Dashboard](https://create.roblox.com/dashboard/credentials). - **Cookie** *(not recommended)*: `.ROBLOSECURITY` cookie. Do not use in production. ``` # API Key example curl -H "x-api-key: YOUR_API_KEY" https://apis.roblox.com/... # OAuth 2.0 example curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" https://apis.roblox.com/... ``` ## Endpoints ### POST `/developer-products/v2/universes/{universeId}/developer-products` [BETA] Create developer product Creates a new developer product with the provided configuration details. **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) **Scopes:** `developer-product:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe ID. | **Request Body:** `multipart/form-data` — Type: `object` | Property | Type | Required | Description | |----------|------|----------|-------------| | `name` | `string` | Yes | The name of the developer product. | | `description` | `string` | No | The description of the developer product. | | `isForSale` | `boolean` | No | Whether the developer product should be on sale. | | `price` | `integer` | No | The default price of the developer product. | | `imageFile` | `string` | No | The thumbnail image file to be uploaded. | | `isRegionalPricingEnabled` | `boolean` | No | Whether regional pricing should be enabled for the developer product. | **Request example:** ```json { "name": "string", "description": "string", "isForSale": false, "price": 0, "imageFile": "string", "isRegionalPricingEnabled": false } ``` > **Verify mutations:** If your API key lacks the required scope (`developer-product:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: Success → `DeveloperProductConfigV2` - `400`: Bad Request → `ErrorResponse` - `401`: Unauthorized → `ErrorResponse` - `403`: Forbidden → `ErrorResponse` - `404`: Not Found → `ErrorResponse` **Response fields** (`DeveloperProductConfigV2`) See [DeveloperProductConfigV2](#developerproductconfigv2) in Models. **Response example:** ```json { "productId": 0, "name": "string", "description": "string", "iconImageAssetId": 0, "universeId": 0, "isForSale": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 3/second **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/developer-products/v2/universes/{UNIVERSEID}/developer-products" \ -F "name=string" \ -F "description=string" \ -F "isForSale=false" \ -F "price=0" \ -F "imageFile=@file.bin;type=application/octet-stream" \ -F "isRegionalPricingEnabled=false" ``` ### PATCH `/developer-products/v2/universes/{universeId}/developer-products/{productId}` [BETA] Update developer product Updates a developer product with the provided configuration details. Note that only fields provided in the request will be updated. **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) **Scopes:** `developer-product:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe ID. | | `productId` | path | `integer` | Yes | The product ID of the developer product. | **Request Body:** `multipart/form-data` — Type: `object` | Property | Type | Required | Description | |----------|------|----------|-------------| | `name` | `string` | No | The name of the developer product. | | `description` | `string` | No | The description of the developer product. | | `isForSale` | `boolean` | No | Whether the developer product should be on sale. | | `price` | `integer` | No | The default price of the developer product. | | `imageFile` | `string` | No | The thumbnail image file to be uploaded. | | `isRegionalPricingEnabled` | `boolean` | No | Whether regional pricing should be enabled for the developer product. | | `storePageEnabled` | `boolean` | No | Whether the developer product should be available for purchase on the external store page. | **Request example:** ```json { "name": "string", "description": "string", "isForSale": false, "price": 0, "imageFile": "string", "isRegionalPricingEnabled": false } ``` > **Verify mutations:** If your API key lacks the required scope (`developer-product:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `204`: No Content - `400`: Bad Request → `ErrorResponse` - `401`: Unauthorized → `ErrorResponse` - `403`: Forbidden → `ErrorResponse` - `404`: Not Found → `ErrorResponse` - `409`: Conflict → `ErrorResponse` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 3/second **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/developer-products/v2/universes/{UNIVERSEID}/developer-products/{PRODUCTID}" \ -F "name=string" \ -F "description=string" \ -F "isForSale=false" \ -F "price=0" \ -F "imageFile=@file.bin;type=application/octet-stream" \ -F "isRegionalPricingEnabled=false" \ -F "storePageEnabled=false" ``` ### GET `/developer-products/v2/universes/{universeId}/developer-products/{productId}/creator` [BETA] Get developer product with configuration details **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) **Scopes:** `developer-product:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe ID. | | `productId` | path | `integer` | Yes | The product ID of the developer product. | **Responses:** - `200`: Success → `DeveloperProductConfigV2` - `401`: Unauthorized → `ErrorResponse` - `403`: Forbidden → `ErrorResponse` - `404`: Not Found → `ErrorResponse` **Response fields** (`DeveloperProductConfigV2`) See [DeveloperProductConfigV2](#developerproductconfigv2) in Models. **Response example:** ```json { "productId": 0, "name": "string", "description": "string", "iconImageAssetId": 0, "universeId": 0, "isForSale": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 10/second **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/developer-products/v2/universes/{UNIVERSEID}/developer-products/{PRODUCTID}/creator" ``` ### GET `/developer-products/v2/universes/{universeId}/developer-products/creator` [BETA] List developer products by universe with configuration details **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) **Scopes:** `developer-product:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe ID. | | `pageSize` | query | `integer` | No | The number of results to return. Defaults to 50. | | `pageToken` | query | `string` | No | The cursor token for pagination. | **Responses:** - `200`: Success → `ListDeveloperProductConfigsV2Response` - `400`: Bad Request → `ErrorResponse` - `401`: Unauthorized → `ErrorResponse` - `403`: Forbidden → `ErrorResponse` - `404`: Not Found → `ErrorResponse` **Response fields** (`ListDeveloperProductConfigsV2Response`) See [ListDeveloperProductConfigsV2Response](#listdeveloperproductconfigsv2response) in Models. **Response example:** ```json { "developerProducts": [ { "productId": "...", "name": "...", "description": "...", "iconImageAssetId": "...", "universeId": "...", "isForSale": "..." } ], "nextPageToken": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 10/second **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/developer-products/v2/universes/{UNIVERSEID}/developer-products/creator" ``` ## Models ### DeveloperProductConfigV2 Creator-facing representation of a developer product configuration. | Property | Type | Required | Description | |----------|------|----------|-------------| | `productId` | `integer` | Yes | The product ID of the developer product. | | `name` | `string` | Yes | The name of the developer product. | | `description` | `string` | Yes | The description of the developer product. | | `iconImageAssetId` | `integer` | Yes | The icon image (thumbnail) asset ID of the developer product. | | `universeId` | `integer` | Yes | The universe ID that the developer product belongs to. | | `isForSale` | `boolean` | Yes | Whether the developer product is currently on sale. | | `storePageEnabled` | `boolean` | Yes | Whether the developer product is currently available for purchase on the external store page. | | `priceInformation` | `any` | Yes | The pricing configuration associated with the product. | | `isImmutable` | `boolean` | Yes | Whether the developer product cannot be modified, such as when created as a commerce product. | | `createdTimestamp` | `string` | Yes | The timestamp when the developer product was created. | | `updatedTimestamp` | `string` | Yes | The timestamp when the developer product was last updated. | ### ErrorResponse This is the base class for all custom Exceptions that are thrown in this API. | Property | Type | Required | Description | |----------|------|----------|-------------| | `errorCode` | `any` | No | The ErrorCode for the exception. | | `errorMessage` | `string` | No | The human readable error message. | | `field` | `string` | No | The relevant field that caused the error. | | `hint` | `string` | No | A hint as to what caused the error, if applicable. | ### ListDeveloperProductConfigsV2Response Response that contains the list of developer products with their corresponding configuration details and pagination information. | Property | Type | Required | Description | |----------|------|----------|-------------| | `developerProducts` | `DeveloperProductConfigV2[]` | Yes | The list of developer products with their corresponding configuration details. | | `nextPageToken` | `string` | Yes | The next page token. | ### PriceInformationStruct The pricing configuration information. | Property | Type | Required | Description | |----------|------|----------|-------------| | `defaultPriceInRobux` | `integer` | Yes | The configured default price in Robux. | | `enabledFeatures` | `PricingFeature[]` | Yes | The enabled pricing features for the developer product. | --- name: "game-passes-http-service" last_updated: 2026-06-29T19:34:06Z type: opencloud api_base_url: "https://apis.roblox.com" version: "v1" endpoints: 4 auth: [api-key, cookie] --- # game-passes-http-service **API Version:** v1 **Base URL(s):** - `https://apis.roblox.com` ## Authentication Each endpoint requires one of the following authentication methods: - **API Key**: Pass your key in the `x-api-key` HTTP header. Create keys at [Creator Dashboard](https://create.roblox.com/dashboard/credentials). - **Cookie** *(not recommended)*: `.ROBLOSECURITY` cookie. Do not use in production. ``` # API Key example curl -H "x-api-key: YOUR_API_KEY" https://apis.roblox.com/... # OAuth 2.0 example curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" https://apis.roblox.com/... ``` ## Endpoints ### POST `/game-passes/v1/universes/{universeId}/game-passes` [BETA] Create game pass Creates a new game pass with the provided configuration details. **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) **Scopes:** `game-pass:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe ID. | **Request Body:** `multipart/form-data` — Type: `object` | Property | Type | Required | Description | |----------|------|----------|-------------| | `name` | `string` | Yes | The name of the game pass. | | `description` | `string` | No | The description of the game pass. | | `imageFile` | `string` | No | The thumbnail image file to be uploaded. | | `isForSale` | `boolean` | No | Whether the game pass should be on sale. | | `price` | `integer` | No | The default price of the game pass. | | `isRegionalPricingEnabled` | `boolean` | No | Whether regional pricing should be enabled for the game pass. | **Request example:** ```json { "name": "string", "description": "string", "imageFile": "string", "isForSale": false, "price": 0, "isRegionalPricingEnabled": false } ``` > **Verify mutations:** If your API key lacks the required scope (`game-pass:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: Success → `GamePassConfigV2` - `400`: Bad Request → `ErrorResponse` - `401`: Unauthorized → `ErrorResponse` - `403`: Forbidden → `ErrorResponse` - `404`: Not Found → `ErrorResponse` **Response fields** (`GamePassConfigV2`) See [GamePassConfigV2](#gamepassconfigv2) in Models. **Response example:** ```json { "gamePassId": 0, "name": "string", "description": "string", "isForSale": false, "iconAssetId": 0, "createdTimestamp": "2024-01-01T00:00:00Z" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 5/second **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/game-passes/v1/universes/{UNIVERSEID}/game-passes" \ -F "name=string" \ -F "description=string" \ -F "imageFile=@file.bin;type=application/octet-stream" \ -F "isForSale=false" \ -F "price=0" \ -F "isRegionalPricingEnabled=false" ``` ### PATCH `/game-passes/v1/universes/{universeId}/game-passes/{gamePassId}` [BETA] Update game pass Updates a game pass with the provided configuration details. Note that only fields provided in the request will be updated. **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) **Scopes:** `game-pass:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe ID. | | `gamePassId` | path | `integer` | Yes | The game pass ID. | **Request Body:** `multipart/form-data` — Type: `object` | Property | Type | Required | Description | |----------|------|----------|-------------| | `name` | `string` | No | The name of the game pass. | | `description` | `string` | No | The description of the game pass. | | `file` | `string` | No | The thumbnail image file to be uploaded. | | `isForSale` | `boolean` | No | Whether the game pass should be on sale. | | `price` | `integer` | No | The default price of the game pass. | | `isRegionalPricingEnabled` | `boolean` | No | Whether regional pricing should be enabled for the game pass. | **Request example:** ```json { "name": "string", "description": "string", "file": "string", "isForSale": false, "price": 0, "isRegionalPricingEnabled": false } ``` > **Verify mutations:** If your API key lacks the required scope (`game-pass:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `204`: No Content - `400`: Bad Request → `ErrorResponse` - `401`: Unauthorized → `ErrorResponse` - `403`: Forbidden → `ErrorResponse` - `404`: Not Found → `ErrorResponse` - `409`: Conflict → `ErrorResponse` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 5/second **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/game-passes/v1/universes/{UNIVERSEID}/game-passes/{GAMEPASSID}" \ -F "name=string" \ -F "description=string" \ -F "file=@file.bin;type=application/octet-stream" \ -F "isForSale=false" \ -F "price=0" \ -F "isRegionalPricingEnabled=false" ``` ### GET `/game-passes/v1/universes/{universeId}/game-passes/{gamePassId}/creator` [BETA] Get game pass with configuration details **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) **Scopes:** `game-pass:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe ID. | | `gamePassId` | path | `integer` | Yes | The game pass ID. | **Responses:** - `200`: Success → `GamePassConfigV2` - `401`: Unauthorized → `ErrorResponse` - `403`: Forbidden → `ErrorResponse` - `404`: Not Found → `ErrorResponse` **Response fields** (`GamePassConfigV2`) See [GamePassConfigV2](#gamepassconfigv2) in Models. **Response example:** ```json { "gamePassId": 0, "name": "string", "description": "string", "isForSale": false, "iconAssetId": 0, "createdTimestamp": "2024-01-01T00:00:00Z" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 10/second **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/game-passes/v1/universes/{UNIVERSEID}/game-passes/{GAMEPASSID}/creator" ``` ### GET `/game-passes/v1/universes/{universeId}/game-passes/creator` [BETA] List game passes by universe with configuration details **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) **Scopes:** `game-pass:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe ID. | | `pageSize` | query | `integer` | No | The number of results to return. Defaults to 50 if not provided. | | `pageToken` | query | `string` | No | The cursor token for pagination. | **Responses:** - `200`: Success → `ListGamePassConfigsByUniverseResponse` - `400`: Bad Request → `ErrorResponse` - `401`: Unauthorized → `ErrorResponse` - `403`: Forbidden → `ErrorResponse` - `404`: Not Found → `ErrorResponse` **Response fields** (`ListGamePassConfigsByUniverseResponse`) See [ListGamePassConfigsByUniverseResponse](#listgamepassconfigsbyuniverseresponse) in Models. **Response example:** ```json { "gamePasses": [ { "gamePassId": "...", "name": "...", "description": "...", "isForSale": "...", "iconAssetId": "...", "createdTimestamp": "..." } ], "nextPageToken": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 10/second **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/game-passes/v1/universes/{UNIVERSEID}/game-passes/creator" ``` ## Models ### ErrorResponse This is the base class for all custom Exceptions that are thrown in this API. | Property | Type | Required | Description | |----------|------|----------|-------------| | `errorCode` | `any` | No | The ErrorCode for the exception. | | `errorMessage` | `string` | No | The human readable error message. | | `field` | `string` | No | The relevant field that caused the error. | | `hint` | `string` | No | A hint as to what caused the error, if applicable. | ### GamePassConfigV2 Creator-facing representation of a game pass configuration. | Property | Type | Required | Description | |----------|------|----------|-------------| | `gamePassId` | `integer` | Yes | The game pass ID. | | `name` | `string` | Yes | The name of the game pass. | | `description` | `string` | Yes | The description of the game pass. | | `isForSale` | `boolean` | Yes | Whether the game pass is currently on sale. | | `iconAssetId` | `integer` | Yes | The icon image (thumbnail) asset ID of the game pass. | | `createdTimestamp` | `string` | Yes | The timestamp when the game pass was created. | | `updatedTimestamp` | `string` | Yes | The timestamp when the game pass was last updated. | | `priceInformation` | `any` | Yes | The pricing configuration associated with the game pass. | ### ListGamePassConfigsByUniverseResponse Response for listing game pass configuration details by universe | Property | Type | Required | Description | |----------|------|----------|-------------| | `gamePasses` | `GamePassConfigV2[]` | Yes | The list of game passes with their corresponding configuration details. | | `nextPageToken` | `string` | Yes | The next page token. | ### PriceInformationStruct The pricing configuration information. | Property | Type | Required | Description | |----------|------|----------|-------------| | `defaultPriceInRobux` | `integer` | Yes | The configured default price in Robux. | | `enabledFeatures` | `PricingFeature[]` | Yes | The enabled pricing features for the game pass. | --- name: "Messaging Service" last_updated: 2026-06-29T19:34:06Z type: opencloud api_base_url: "https://apis.roblox.com/messaging-service/" version: "v1" endpoints: 1 auth: [api-key, oauth2] description: "This page contains reference documentation for the Open Cloud v1 Messaging Service API, which can broadcast messages across all servers in your experience" --- # Messaging Service This page contains reference documentation for the Open Cloud v1 Messaging Service API, which can broadcast messages across all servers in your experience. The v2 API has its own version of the [Publish Message](/docs/en-us/cloud/reference/Universe.md#Cloud_PublishUniverseMessage) method. For information on using the v2 method, see the [Messaging usage guide](/docs/en-us/cloud/guides/usage-messaging.md). **API Version:** v1 **Base URL(s):** - `https://apis.roblox.com/messaging-service/` ## Authentication Each endpoint requires one of the following authentication methods: - **API Key**: Pass your key in the `x-api-key` HTTP header. Create keys at [Creator Dashboard](https://create.roblox.com/dashboard/credentials). - **OAuth 2.0**: Use Bearer token in the `Authorization` header. Authorization URL: `https://apis.roblox.com/oauth/v1/authorize`, Token URL: `https://apis.roblox.com/oauth/v1/token` ``` # API Key example curl -H "x-api-key: YOUR_API_KEY" https://apis.roblox.com/... # OAuth 2.0 example curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" https://apis.roblox.com/... ``` ## Endpoints ### POST `/v1/universes/{universeId}/topics/{topic}` [BETA] Publish a cross-server message to a universe Publish a message to a pre-defined topic of an experience, with the size of the message up to 1,024 characters (1 KB). Requires the **Publish** permission for API keys and the **universe-messaging-service:publish** scope for OAuth 2.0 apps. See [Cross-server messaging](/docs/en-us/cloud-services/cross-server-messaging.md#subscribe-users-to-receive-messages) for defining and subscribing users to a topic. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `universe-messaging-service:publish` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The identifier of the experience in which you want to send your messages to. You can [copy your experience's Universe ID](/docs/en-us/cloud/guides/usage-messaging.md#publishing-messages-to-live-servers) on **Creator Dashboard**. | | `topic` | path | `string` | Yes | The topic that you want to publish your message to, with up to 80 characters. | **Request Body:** `application/json-patch+json` — Type: `PublishRequest` See [PublishRequest](#publishrequest) in Models. **Request example:** ```json { "message": "string" } ``` **Responses:** - `200`: Returns an empty response body. - `400`: Invalid request. - `401`: The API key is not valid for this operation / You don't have the authorization. - `403`: Publishing is not allowed on this experience. - `500`: Server internal error / Unknown error. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perIp: 10000/minute, perApiKeyOwner: 5000/minute, perOauth2Authorization: 5000/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/messaging-service//v1/universes/{UNIVERSEID}/topics/{TOPIC}" \ -H "Content-Type: application/json" \ -d '{ "message": "string" }' ``` ## Models ### PublishRequest The request body object with the message string that you want to publish to the live server. | Property | Type | Required | Description | |----------|------|----------|-------------| | `message` | `string` | No | The message content that you want to publish to the live server. | --- name: "OpenEvalApi" last_updated: 2026-06-29T19:34:06Z type: opencloud api_base_url: "https://apis.roblox.com" version: "1.0" endpoints: 2 auth: [api-key, cookie] --- # OpenEvalApi **API Version:** 1.0 **Base URL(s):** - `https://apis.roblox.com` ## Authentication Each endpoint requires one of the following authentication methods: - **API Key**: Pass your key in the `x-api-key` HTTP header. Create keys at [Creator Dashboard](https://create.roblox.com/dashboard/credentials). - **Cookie** *(not recommended)*: `.ROBLOSECURITY` cookie. Do not use in production. ``` # API Key example curl -H "x-api-key: YOUR_API_KEY" https://apis.roblox.com/... # OAuth 2.0 example curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" https://apis.roblox.com/... ``` ## Endpoints ### POST `/open-eval-api/v1/eval` [BETA] Performs the evaluation of the Lua script. **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) **Scopes:** `studio-evaluations:create` **Request Body:** `application/json-patch+json` — Type: `EvalRequest` See [EvalRequest](#evalrequest) in Models. **Request example:** ```json { "name": "string", "description": "string", "use_reference_mode": false, "input_script": "string", "custom_llm_info": { "name": "string", "model_version": "string", "url": "string", "api_key": "string" } } ``` **Responses:** - `200`: OK → `EvalResponse` **Response fields** (`EvalResponse`) See [EvalResponse](#evalresponse) in Models. **Response example:** ```json { "job_id": "string", "status_code": 0, "error": "string" } ``` **Rate Limits:** perApiKeyOwner: 100/day **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/open-eval-api/v1/eval" \ -H "Content-Type: application/json" \ -d '{ "name": "string", "description": "string", "use_reference_mode": false, "input_script": "string", "custom_llm_info": { "name": "string", "model_version": "string", "url": "string", "api_key": "string" } }' ``` ### GET `/open-eval-api/v1/eval-records/{jobId}` [BETA] Gets the evaluation record by job ID. **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) **Scopes:** `studio-evaluations:create` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `jobId` | path | `string` | Yes | The job ID of the evaluation record. | **Responses:** - `200`: OK → `EvalRecord` **Response fields** (`EvalRecord`) See [EvalRecord](#evalrecord) in Models. **Response example:** ```json { "id": 0, "name": "string", "user": { "id": "string" }, "useReferenceMode": "string", "description": "string", "jobId": "string" } ``` **Rate Limits:** perApiKeyOwner: 60/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/open-eval-api/v1/eval-records/{JOBID}" ``` ## Models ### CustomLlmInfo Contains information to override the default LLM endpoint and credentials. | Property | Type | Required | Description | |----------|------|----------|-------------| | `name` | `string` | Yes | The name of the custom LLM provider (claude, gemini, openai). | | `model_version` | `string` | No | The specific model version to use (e.g., "gemini-2.5-pro-preview-03-25"). If not provided, falls back to the provider name. | | `url` | `string` | Yes | The custom LLM endpoint URL. | | `api_key` | `string` | No | The API key to access the custom LLM. | ### EvalJsonRecord | Property | Type | Required | Description | |----------|------|----------|-------------| | `passes` | `integer` | No | | | `fails` | `integer` | No | | | `checks` | `integer` | No | | | `warning` | `string` | No | | | `error` | `string` | No | | | `interruptions` | `InterruptionRecord[]` | No | | ### EvalRecord | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | | | `name` | `string` | Yes | | | `user` | `User` | No | | | `useReferenceMode` | `string` | No | | | `description` | `string` | No | | | `jobId` | `string` | Yes | | | `inputScript` | `string` | Yes | | | `jobStatus` | `string` | No | | | `results` | `EvalResultRecord[]` | No | | | `fullLogs` | `string` | No | | | `evalSucceeded` | `string` | No | | | `createUtc` | `string` | No | | ### EvalRequest The request model for the evaluation. | Property | Type | Required | Description | |----------|------|----------|-------------| | `name` | `string` | Yes | | | `description` | `string` | No | | | `use_reference_mode` | `boolean` | No | | | `input_script` | `string` | Yes | | | `custom_llm_info` | `CustomLlmInfo` | No | | ### EvalResponse The response model for the evaluation. | Property | Type | Required | Description | |----------|------|----------|-------------| | `job_id` | `string` | No | | | `status_code` | `integer` | No | Gets or sets the status code of the response. | | `error` | `string` | No | Gets or sets the error message associated with the response. | ### EvalResultRecord | Property | Type | Required | Description | |----------|------|----------|-------------| | `mode` | `string` | No | | | `result` | `EvalJsonRecord` | No | | ### InterruptionRecord | Property | Type | Required | Description | |----------|------|----------|-------------| | `check` | `integer` | No | | | `type` | `string` | No | | ### User | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `string` | No | | --- name: "Roblox API" last_updated: 2026-06-29T19:34:06Z type: opencloud api_base_url: "https://apis.roblox.com" version: "1.0.0" endpoints: 780 auth: [api-key, oauth2, cookie] description: "The Roblox API" --- # Roblox API The Roblox API. See https://create.roblox.com/docs/cloud/open-cloud for more details. **API Version:** 1.0.0 **Base URL(s):** - `https://apis.roblox.com` ## Authentication Each endpoint requires one of the following authentication methods: - **API Key**: Pass your key in the `x-api-key` HTTP header. Create keys at [Creator Dashboard](https://create.roblox.com/dashboard/credentials). - **OAuth 2.0**: Use Bearer token in the `Authorization` header. Authorization URL: `https://apis.roblox.com/oauth/v1/authorize`, Token URL: `https://apis.roblox.com/oauth/v1/token` - **Cookie** *(not recommended)*: `.ROBLOSECURITY` cookie. Do not use in production. ``` # API Key example curl -H "x-api-key: YOUR_API_KEY" https://apis.roblox.com/... # OAuth 2.0 example curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" https://apis.roblox.com/... ``` ## Endpoints ### GET `/asset-delivery-api/v1/assetId/{assetId}` Retrieves an asset by its ID with OpenCloud auth. Returns an object containing a `location` property which is a temporary CDN URL for the asset content. All asset types are supported. You should request that URL with the `Accept-Encoding: gzip` header and decompress the result if the response is gzipped. If you are using cURL, the `--compressed` flag will automate these steps for you. This endpoint is expected to be called with API key authentication through `apis.roblox.com/asset-delivery-api/v1/assetId/{assetId}`. While you are able to make requests to this endpoint with Cookie authentication via `assetdelivery.roblox.com/v1/openCloud/assetId/{assetId}`, we highly discourage use this way. Expect unannounced removal of this second route in the future. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-asset:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetId` | path | `integer` | Yes | The ID of the asset to retrieve. | | `Accept-Encoding` | header | `string` | No | The Accept-Encoding header value specifying compression formats (e.g., "gzip, deflate"). Defaults to "gzip, deflate" if not provided. | | `Roblox-Place-Id` | header | `integer` | No | The Roblox-Place-Id header value identifying the place making the request. | | `AssetType` | header | `string` | No | The AssetType header value specifying the expected asset type. | | `Accept` | header | `string` | No | The Accept header value specifying the preferred response content type. | | `AssetFormat` | header | `string` | No | The AssetFormat header value specifying the desired asset format. Overridden by robloxAssetFormat if both are provided. | | `Roblox-AssetFormat` | header | `string` | No | The Roblox-AssetFormat header value specifying the preferred Roblox-specific asset format. Takes precedence over assetFormat. | | `skipSigningScripts` | query | `boolean` | No | Whether to skip script signing for the returned asset. Used for script assets that don't require signing. | | `clientInsert` | query | `integer` | No | Set to 1 to indicate this is a client insert request. | | `scriptinsert` | query | `integer` | No | Set to 1 to indicate this is a script insert request. | | `modulePlaceId` | query | `integer` | No | The place ID of the module making the request. | | `serverplaceid` | query | `integer` | No | The server place ID making the request. | | `expectedAssetType` | query | `string` | No | The expected asset type as a fallback when assetType header is not provided. | | `doNotFallbackToBaselineRepresentation` | query | `boolean` | No | Whether to prevent fallback to baseline representation when specific content representations are not available. | | `contentRepresentationPriorityList` | query | `string` | No | Base64URL-encoded JSON string specifying the priority list of desired content representations (format, version, fidelity). | | `accessContext` | query | `string` | No | | | `usageContext` | query | `integer` | No | | **Responses:** - `200`: OK → `Roblox.Web.Assets.AssetResponseItemV1` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Web.Assets.AssetResponseItemV1`) See [Roblox.Web.Assets.AssetResponseItemV1](#roblox-web-assets-assetresponseitemv1) in Models. **Response example:** ```json { "location": "string", "errors": [ { "Code": "...", "Message": "...", "CustomErrorCode": "..." } ], "requestId": "string", "isArchived": false, "assetTypeId": 0, "contentRepresentationSpecifier": { "format": "string", "majorVersion": "string", "fidelity": "string", "skipGenerationIfNotExist": false } } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Rate Limits:** perApiKeyOwner: 1000/minute, perOauth2Authorization: 1000/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/asset-delivery-api/v1/assetId/{ASSETID}" ``` ### GET `/asset-delivery-api/v1/assetId/{assetId}/version/{versionNumber}` Retrieves an asset by its ID and version number with OpenCloud auth. Refer to the assetId endpoint for details on usage. This endpoint is expected to be called with API key authentication through `apis.roblox.com/asset-delivery-api/v1/assetId/{assetId}/version/{versionNumber}`. While you are able to make requests to this endpoint with Cookie authentication via `assetdelivery.roblox.com/v1/openCloud/assetId/{assetId}/version/{versionNumber}`, we highly discourage use this way. Expect unannounced removal of this second route in the future. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-asset:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetId` | path | `integer` | Yes | The ID of the asset to retrieve. | | `versionNumber` | path | `integer` | Yes | The version number of the asset to retrieve. | | `Accept-Encoding` | header | `string` | No | | | `Roblox-Place-Id` | header | `integer` | No | | | `AssetType` | header | `string` | No | | | `Accept` | header | `string` | No | | | `AssetFormat` | header | `string` | No | | | `Roblox-AssetFormat` | header | `string` | No | | | `skipSigningScripts` | query | `boolean` | No | | | `clientInsert` | query | `integer` | No | | | `scriptinsert` | query | `integer` | No | | | `modulePlaceId` | query | `integer` | No | | | `serverplaceid` | query | `integer` | No | | | `expectedAssetType` | query | `string` | No | | | `doNotFallbackToBaselineRepresentation` | query | `boolean` | No | | | `contentRepresentationPriorityList` | query | `string` | No | | | `accessContext` | query | `string` | No | | | `usageContext` | query | `integer` | No | | **Responses:** - `200`: OK → `Roblox.Web.Assets.AssetResponseItemV1` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Web.Assets.AssetResponseItemV1`) See [Roblox.Web.Assets.AssetResponseItemV1](#roblox-web-assets-assetresponseitemv1) in Models. **Response example:** ```json { "location": "string", "errors": [ { "Code": "...", "Message": "...", "CustomErrorCode": "..." } ], "requestId": "string", "isArchived": false, "assetTypeId": 0, "contentRepresentationSpecifier": { "format": "string", "majorVersion": "string", "fidelity": "string", "skipGenerationIfNotExist": false } } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Rate Limits:** perApiKeyOwner: 1000/minute, perOauth2Authorization: 1000/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/asset-delivery-api/v1/assetId/{ASSETID}/version/{VERSIONNUMBER}" ``` ### PATCH `/asset-permissions-api/v1/assets/permissions` [BETA] Grant a subject permission to multiple assets. Authorization is required to grant permissions to the subject and asset IDs in the request. **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) **Scopes:** `asset-permissions:write` **Request Body:** `application/json-patch+json` — Type: `BatchGrantPermissionsRequest` See [BatchGrantPermissionsRequest](#batchgrantpermissionsrequest) in Models. **Request example:** ```json { "subjectType": "Invalid", "subjectId": "string", "action": "Invalid", "requests": [ { "assetId": "...", "grantToDependencies": "...", "parentVersionNumber": "..." } ], "assetIds": [ 0 ], "enableDeepAccessCheck": false } ``` > **Verify mutations:** If your API key lacks the required scope (`asset-permissions:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `BatchGrantPermissionsResponse` - `400`: Bad Request → `AssetPermissions.ErrorResponse` - `403`: Forbidden → `AssetPermissions.ErrorResponse` - `500`: Internal Server Error → `AssetPermissions.ErrorResponse` **Response fields** (`BatchGrantPermissionsResponse`) See [BatchGrantPermissionsResponse](#batchgrantpermissionsresponse) in Models. **Response example:** ```json { "successAssetIds": [ 0 ], "errors": [ { "assetId": "...", "code": "..." } ] } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/asset-permissions-api/v1/assets/permissions" \ -H "Content-Type: application/json" \ -d '{ "subjectType": "Invalid", "subjectId": "string", "action": "Invalid", "requests": [ { "assetId": "...", "grantToDependencies": "...", "parentVersionNumber": "..." } ], "assetIds": [ 0 ], "enableDeepAccessCheck": false }' ``` ### POST `/assets/v1/assets` [BETA] Creates an asset with provided content and metadata. Creates an asset with provided content and metadata. You can't add [SocialLink](#SocialLink) objects when you create an asset. Instead, use [Update Asset](#PATCH-v1-assets-_assetId_). Provide the [Asset](#Asset), binary asset file path, and [content type](/docs/en-us/cloud/guides/usage-assets.md#supported-asset-types-and-limits) in the form data. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `asset:read`, `asset:write` **Request Body:** `multipart/form-data` — Type: `object` | Property | Type | Required | Description | |----------|------|----------|-------------| | `request` | [Asset](#asset) | Yes | Asset attributes to create. | | `request.assetType` | `string` | No | The asset type. Required for [Create Asset](#POST-v1-assets). | | `request.assetId` | `integer` | No | The unique identifier of the asset. Required for [Update Asset](#PATCH-v1-assets-_asset_). | | `request.creationContext` | [CreationContext](#creationcontext) | No | | | `request.description` | `string` | No | The description of the asset. Limit to 1000 characters. Required for [Create Asset](#POST-v1-assets) | | `request.displayName` | `string` | No | Display name of the asset. Required for [Create Asset](#POST-v1-assets). | | `request.path` | `string` | No | The returned resource path of the asset. Format: `assets/{assetId}`. Example: `assets/2205400862`. | | `fileContent` | `string` | Yes | The binary asset file path and the content type. See [Asset types and limits](/docs/en-us/cloud/guides/usage-assets.md#supported-as | **Request example:** ```json { "request": { "assetType": "string", "assetId": 0, "creationContext": { "assetPrivacy": "...", "creator": "...", "expectedPrice": "..." }, "description": "string", "displayName": "string", "path": "string" }, "fileContent": "string" } ``` > **Verify mutations:** If your API key lacks the required scope (`asset:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: Returns the Operation ID for checking the creation status. → `OCV1.Assets.Operation` - `400`: Invalid argument. Failed to parse the request or the file. → `OCV1.Assets.Status` - `401`: The API key is not valid for this operation / You don't have the authorization. - `500`: Server internal error / Unknown error. **Response fields** (`OCV1.Assets.Operation`) See [OCV1.Assets.Operation](#ocv1-assets-operation) in Models. **Response example:** ```json { "path": "string", "done": false, "error": { "code": 0, "message": "string" }, "response": { "assetType": "string", "assetId": 0, "creationContext": { "assetPrivacy": "...", "creator": "...", "expectedPrice": "..." }, "description": "string", "displayName": "string", "path": "string" } } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Rate Limits:** perApiKeyOwner: 120/minute, perOauth2Authorization: 120/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/assets/v1/assets" \ -F "request={"assetType":"string","assetId":0,"creationContext":{"assetPrivacy":"default","creator":{"userId":"...","groupId":"..."},"expectedPrice":0},"description":"string","displayName":"string","path":"string"};type=application/json" \ -F "fileContent=@file.bin;type=application/octet-stream" ``` ### GET `/assets/v1/assets/{assetId}` [BETA] Retrieve specific asset metadata. Include the `readMask` parameter for additional asset metadata. Retrieve specific asset metadata. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `asset:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetId` | path | `string` | Yes | The unique identifier of the asset. | | `readMask` | query | `string` | No | Asset metadata fields to retrieve, including the description, display name, icon, social links, and previews. Examples: `description%2CdisplayName`, `previews%2CtwitchSocialLink`. | **Responses:** - `200`: Asset resource retrieved successfully. → `Asset` - `400`: Malformed request, likely due to an invalid read mask. - `401`: The API key is not valid for this operation / You don't have the authorization. - `403`: Doesn't have the required permission. - `404`: Asset doesn't exist. - `500`: Server internal error / Unknown error. **Response fields** (`Asset`) See [Asset](#asset) in Models. **Response example:** ```json { "assetType": "string", "assetId": 0, "creationContext": { "assetPrivacy": "default", "creator": { "userId": "...", "groupId": "..." }, "expectedPrice": 0 }, "description": "string", "displayName": "string", "path": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 120/minute, perOauth2Authorization: 120/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/assets/v1/assets/{ASSETID}" ``` ### PATCH `/assets/v1/assets/{assetId}` [BETA] Updates an asset with provided content and metadata. Updates an asset with provided content and metadata, including the description, display name, icon, social links, and previews. Currently can only update the content body for **Models**. Icons and Previews must be **Image** assets. Icons must have square dimensions. Provide the [Asset](#Asset), binary asset file path, and [content type](/docs/en-us/cloud/guides/usage-assets.md#supported-asset-types-and-limits) in the form data. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `asset:read`, `asset:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetId` | path | `string` | Yes | The unique identifier of the asset. | | `updateMask` | query | `string` | No | Asset metadata fields to update, including the description, display name, icon, and previews. Examples: `description%2CdisplayName`, `previews%2CtwitchSocialLink`. | **Request Body:** `multipart/form-data` — Type: `object` | Property | Type | Required | Description | |----------|------|----------|-------------| | `request` | [Asset](#asset) | Yes | Asset attributes to update. | | `request.assetType` | `string` | No | The asset type. Required for [Create Asset](#POST-v1-assets). | | `request.assetId` | `integer` | No | The unique identifier of the asset. Required for [Update Asset](#PATCH-v1-assets-_asset_). | | `request.creationContext` | [CreationContext](#creationcontext) | No | | | `request.description` | `string` | No | The description of the asset. Limit to 1000 characters. Required for [Create Asset](#POST-v1-assets) | | `request.displayName` | `string` | No | Display name of the asset. Required for [Create Asset](#POST-v1-assets). | | `request.path` | `string` | No | The returned resource path of the asset. Format: `assets/{assetId}`. Example: `assets/2205400862`. | | `fileContent` | `string` | Yes | The binary asset file path and the content type. See [Asset types and limits](/docs/en-us/cloud/guides/usage-assets.md#supported-as | **Request example:** ```json { "request": { "assetType": "string", "assetId": 0, "creationContext": { "assetPrivacy": "...", "creator": "...", "expectedPrice": "..." }, "description": "string", "displayName": "string", "path": "string" }, "fileContent": "string" } ``` > **Verify mutations:** If your API key lacks the required scope (`asset:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: Returns the Operation ID for checking the update status / Returns the updated metadata fields. → `OCV1.Assets.Operation` - `400`: Invalid argument. Failed to parse the request or the file. → `OCV1.Assets.Status` - `401`: The API key is not valid for this operation / You don't have the authorization. - `500`: Server internal error / Unknown error. **Response fields** (`OCV1.Assets.Operation`) See [OCV1.Assets.Operation](#ocv1-assets-operation) in Models. **Response example:** ```json { "path": "string", "done": false, "error": { "code": 0, "message": "string" }, "response": { "assetType": "string", "assetId": 0, "creationContext": { "assetPrivacy": "...", "creator": "...", "expectedPrice": "..." }, "description": "string", "displayName": "string", "path": "string" } } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Rate Limits:** perApiKeyOwner: 120/minute, perOauth2Authorization: 120/minute **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/assets/v1/assets/{ASSETID}" \ -F "request={"assetType":"string","assetId":0,"creationContext":{"assetPrivacy":"default","creator":{"userId":"...","groupId":"..."},"expectedPrice":0},"description":"string","displayName":"string","path":"string"};type=application/json" \ -F "fileContent=@file.bin;type=application/octet-stream" ``` ### GET `/assets/v1/assets/{assetId}/versions` [BETA] List Asset Versions of an Asset List all versions of a specific asset, with optional pagination. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `asset:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetId` | path | `string` | Yes | The unique identifier of the asset. | | `maxPageSize` | query | `integer` | No | Specifies the number of asset versions to include in the response. Valid values range from 1 to 50 (inclusive). Defaults to 8 when not provided. | | `pageToken` | query | `string` | No | A token for pagination. The value is obtained from a previous request and allows for retrieving the next page of asset versions. | **Responses:** - `200`: Asset versions listed successfully. → `AssetVersion[]` - `400`: Bad request - invalid parameters. - `403`: Forbidden - API key without Read scope or user doesn't have access. - `404`: Asset not found. **Response fields** (`AssetVersion[]`) See [AssetVersion](#assetversion) in Models. **Error handling:** `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/assets/v1/assets/{ASSETID}/versions" ``` ### GET `/assets/v1/assets/{assetId}/versions/{versionNumber}` [BETA] Get Asset Version Retrieve a specific asset version by the asset ID and the version number. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `asset:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetId` | path | `string` | Yes | The unique identifier of the asset. | | `versionNumber` | path | `string` | Yes | The version number. | **Responses:** - `200`: Asset version retrieved successfully. → `AssetVersion` - `403`: Forbidden - API key without Read scope or user doesn't have access. - `404`: Asset or Asset Version not found. **Response fields** (`AssetVersion`) See [AssetVersion](#assetversion) in Models. **Response example:** ```json { "creationContext": { "assetPrivacy": "default", "creator": { "userId": "...", "groupId": "..." }, "expectedPrice": 0 }, "path": "string", "moderationResult": { "moderationState": "string" }, "published": false } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/assets/v1/assets/{ASSETID}/versions/{VERSIONNUMBER}" ``` ### POST `/assets/v1/assets/{assetId}/versions:rollback` [BETA] Rollback an asset to a previous version. Rollback an asset to a specific previous version. Provide the asset version path in the form data. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `asset:read`, `asset:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetId` | path | `string` | Yes | The unique identifier of the asset. | **Request Body:** `multipart/form-data` — Type: `object` | Property | Type | Required | Description | |----------|------|----------|-------------| | `assetVersion` | `string` | Yes | The asset version path in the format of `assets/{assetId}/versions/{versionNumber}`. | **Request example:** ```json { "assetVersion": "string" } ``` > **Verify mutations:** If your API key lacks the required scope (`asset:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: Asset rolled back successfully. → `AssetVersion` - `400`: Bad request - invalid request body. - `403`: Forbidden - API key without Write scope or user doesn't have access. - `404`: Asset or Asset Version not found. **Response fields** (`AssetVersion`) See [AssetVersion](#assetversion) in Models. **Response example:** ```json { "creationContext": { "assetPrivacy": "default", "creator": { "userId": "...", "groupId": "..." }, "expectedPrice": 0 }, "path": "string", "moderationResult": { "moderationState": "string" }, "published": false } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/assets/v1/assets/{ASSETID}/versions:rollback" \ -F "assetVersion=string" ``` ### POST `/assets/v1/assets/{assetId}:archive` [BETA] Archives the asset. Archives the asset. Archived assets disappear from the website and are no longer usable or visible in Roblox experiences, but you can [restore](#POST-v1-assets-{assetId}:restore) them. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `asset:read`, `asset:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetId` | path | `string` | Yes | The unique identifier of the asset. | > **Verify mutations:** If your API key lacks the required scope (`asset:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: Asset archived succesfully successfully. → `Asset` - `400`: Bad request - invalid request. - `403`: Forbidden - API key without Write scope or user doesn't have access. - `404`: Asset not found. **Response fields** (`Asset`) See [Asset](#asset) in Models. **Response example:** ```json { "assetType": "string", "assetId": 0, "creationContext": { "assetPrivacy": "default", "creator": { "userId": "...", "groupId": "..." }, "expectedPrice": 0 }, "description": "string", "displayName": "string", "path": "string" } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/assets/v1/assets/{ASSETID}:archive" ``` ### POST `/assets/v1/assets/{assetId}:restore` [BETA] Restores an archived asset. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `asset:read`, `asset:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetId` | path | `string` | Yes | The unique identifier of the asset. | > **Verify mutations:** If your API key lacks the required scope (`asset:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: Asset restored successfully. → `Asset` - `400`: Bad request - invalid request. - `403`: Forbidden - API key without Write scope or user doesn't have access. - `404`: Asset not found. **Response fields** (`Asset`) See [Asset](#asset) in Models. **Response example:** ```json { "assetType": "string", "assetId": 0, "creationContext": { "assetPrivacy": "default", "creator": { "userId": "...", "groupId": "..." }, "expectedPrice": 0 }, "description": "string", "displayName": "string", "path": "string" } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/assets/v1/assets/{ASSETID}:restore" ``` ### GET `/assets/v1/operations/{operationId}` [BETA] Get the result of an asset creation or update. Get the result of an asset creation or update using the returned Operation ID. Requires **Read** for the API key permission and **asset:read** for OAuth 2.0 apps. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `asset:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `operationId` | path | `string` | Yes | The unique identifier of the operation. | **Responses:** - `200`: Operation result retrieved successfully. → `OCV1.Assets.Operation` - `400`: Invalid argument. Failed to parse the request or the file. → `OCV1.Assets.Status` - `401`: The API key is not valid for this operation / You don't have the authorization. - `500`: Server internal error / Unknown error. **Response fields** (`OCV1.Assets.Operation`) See [OCV1.Assets.Operation](#ocv1-assets-operation) in Models. **Response example:** ```json { "path": "string", "done": false, "error": { "code": 0, "message": "string" }, "response": { "assetType": "string", "assetId": 0, "creationContext": { "assetPrivacy": "...", "creator": "...", "expectedPrice": "..." }, "description": "string", "displayName": "string", "path": "string" } } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Rate Limits:** perApiKeyOwner: 300/minute, perOauth2Authorization: 300/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/assets/v1/operations/{OPERATIONID}" ``` ### POST `/cloud/v2/creator-store-products` [BETA] Create Creator Store Product Add a Creator Store product. Only use this method if your product has never been distributed on the Creator Store; otherwise, use the `PATCH` method to update the product. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `creator-store-product:write` **Request Body:** `application/json` — Type: `CreatorStoreProduct` See [CreatorStoreProduct](#creatorstoreproduct) in Models. **Request example:** ```json { "path": "string", "basePrice": { "currencyCode": "string", "quantity": { "significand": "...", "exponent": "..." } }, "purchasePrice": { "currencyCode": "string", "quantity": { "significand": "...", "exponent": "..." } }, "published": false, "restrictions": [ "RESTRICTION_UNSPECIFIED" ], "purchasable": false } ``` > **Verify mutations:** If your API key lacks the required scope (`creator-store-product:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `CreatorStoreProduct` **Response fields** (`CreatorStoreProduct`) See [CreatorStoreProduct](#creatorstoreproduct) in Models. **Response example:** ```json { "path": "string", "basePrice": { "currencyCode": "string", "quantity": { "significand": "...", "exponent": "..." } }, "purchasePrice": { "currencyCode": "string", "quantity": { "significand": "...", "exponent": "..." } }, "published": false, "restrictions": [ "RESTRICTION_UNSPECIFIED" ], "purchasable": false } ``` **Rate Limits:** perApiKeyOwner: 30/minute, perOauth2Authorization: 30/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/creator-store-products" \ -H "Content-Type: application/json" \ -d '{"path":"string","basePrice":{"currencyCode":"string","quantity":{"significand":"...","exponent":"..."}},"purchasePrice":{"currencyCode":"string","quantity":{"significand":"...","exponent":"..."}},"published":false,"restrictions":["RESTRICTION_UNSPECIFIED"],"purchasable":false}' ``` ### GET `/cloud/v2/creator-store-products/{creator_store_product_id}` [BETA] Get Creator Store Product Get a Creator Store product. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `creator-store-product:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `creator_store_product_id` | path | `string` | Yes | The creator-store-product ID. | **Responses:** - `200`: OK → `CreatorStoreProduct` **Response fields** (`CreatorStoreProduct`) See [CreatorStoreProduct](#creatorstoreproduct) in Models. **Response example:** ```json { "path": "string", "basePrice": { "currencyCode": "string", "quantity": { "significand": "...", "exponent": "..." } }, "purchasePrice": { "currencyCode": "string", "quantity": { "significand": "...", "exponent": "..." } }, "published": false, "restrictions": [ "RESTRICTION_UNSPECIFIED" ], "purchasable": false } ``` **Rate Limits:** perApiKeyOwner: 30/minute, perOauth2Authorization: 30/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/creator-store-products/{CREATOR_STORE_PRODUCT_ID}" ``` ### PATCH `/cloud/v2/creator-store-products/{creator_store_product_id}` [BETA] Update Creator Store Product Update a Creator Store product. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `creator-store-product:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `creator_store_product_id` | path | `string` | Yes | The creator-store-product ID. | | `updateMask` | query | `string` | No | The list of fields to update. | | `allowMissing` | query | `boolean` | No | If set to true, and the creator store product is not found, a creator store product is created. In this situation, `update_mask` is ignored. | **Request Body:** `application/json` — Type: `CreatorStoreProduct` See [CreatorStoreProduct](#creatorstoreproduct) in Models. **Request example:** ```json { "path": "string", "basePrice": { "currencyCode": "string", "quantity": { "significand": "...", "exponent": "..." } }, "purchasePrice": { "currencyCode": "string", "quantity": { "significand": "...", "exponent": "..." } }, "published": false, "restrictions": [ "RESTRICTION_UNSPECIFIED" ], "purchasable": false } ``` > **Verify mutations:** If your API key lacks the required scope (`creator-store-product:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `CreatorStoreProduct` **Response fields** (`CreatorStoreProduct`) See [CreatorStoreProduct](#creatorstoreproduct) in Models. **Response example:** ```json { "path": "string", "basePrice": { "currencyCode": "string", "quantity": { "significand": "...", "exponent": "..." } }, "purchasePrice": { "currencyCode": "string", "quantity": { "significand": "...", "exponent": "..." } }, "published": false, "restrictions": [ "RESTRICTION_UNSPECIFIED" ], "purchasable": false } ``` **Rate Limits:** perApiKeyOwner: 30/minute, perOauth2Authorization: 30/minute **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/creator-store-products/{CREATOR_STORE_PRODUCT_ID}" \ -H "Content-Type: application/json" \ -d '{"path":"string","basePrice":{"currencyCode":"string","quantity":{"significand":"...","exponent":"..."}},"purchasePrice":{"currencyCode":"string","quantity":{"significand":"...","exponent":"..."}},"published":false,"restrictions":["RESTRICTION_UNSPECIFIED"],"purchasable":false}' ``` ### GET `/cloud/v2/groups/{group_id}` [BETA] Get Group Gets the specified group. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `group_id` | path | `string` | Yes | The group ID. | **Responses:** - `200`: OK → `Group` **Response fields** (`Group`) See [Group](#group) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:00Z", "id": "string", "displayName": "string", "description": "string" } ``` **Rate Limits:** perApiKeyOwner: 150/minute, perOauth2Authorization: 30/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/groups/{GROUP_ID}" ``` ### GET `/cloud/v2/groups/{group_id}/forum-categories` [BETA] List Group Forum Categories Lists forum categories in the group. Supports filtering. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `group-forum:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `group_id` | path | `string` | Yes | The group ID. | | `maxPageSize` | query | `integer` | No | The maximum number of group forum categories to return. The service might return fewer than this value. If unspecified, at most 10 group forum categories are returned. The maximum value is 10 and higher values are set to 10. | | `pageToken` | query | `string` | No | A page token, received from a previous call, to retrieve a subsequent page. When paginating, all other parameters provided to the subsequent call must match the call that provided the page token. | | `filter` | query | `string` | No | This field may be set in order to filter the resources returned. Filtering conforms to Common Expression Language (CEL). Only the boolean field `archived` and `==` operator are supported. If `archived=false`, archived categories are not returned. The default value is `false`. Example: `"filter=archived==true"` | **Responses:** - `200`: OK → `ListGroupForumCategoriesResponse` **Response fields** (`ListGroupForumCategoriesResponse`) See [ListGroupForumCategoriesResponse](#listgroupforumcategoriesresponse) in Models. **Response example:** ```json { "groupForumCategories": [ { "path": "...", "createTime": "...", "updateTime": "...", "groupForumCategoryId": "...", "displayName": "...", "creator": "..." } ], "nextPageToken": "string" } ``` **Rate Limits:** perApiKeyOwner: 150/minute, perOauth2Authorization: 30/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/groups/{GROUP_ID}/forum-categories" ``` ### GET `/cloud/v2/groups/{group_id}/forum-categories/{forum_category_id}/posts` [BETA] List Group Forum Posts Lists forum posts in the group's forum category. Supports filtering. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `group-forum:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `group_id` | path | `string` | Yes | The group ID. | | `forum_category_id` | path | `string` | Yes | The forum-category ID. | | `maxPageSize` | query | `integer` | No | The maximum number of group forum posts to return. The service might return fewer than this value. If unspecified, at most 10 group forum posts are returned. The maximum value is 100 and higher values are set to 100. | | `pageToken` | query | `string` | No | A page token, received from a previous call, to retrieve a subsequent page. When paginating, all other parameters provided to the subsequent call must match the call that provided the page token. | | `filter` | query | `string` | No | This field may be set in order to filter the resources returned. Filtering conforms to Common Expression Language (CEL). Only the query `filter=pinned==true` is supported, where only pinned posts are returned. `filter=pinned==false`is not supported. | | `view` | query | `VIEW_UNSPECIFIED \| FULL \| FULL_WITH_FIRST_COMMENT` | No | The view in which to retrieve the group forum post. Supports FULL and FULL_WITH_FIRST_COMMENT. Defaults to FULL. Possible values: \| Value \| Description \| \| --- \| --- \| \| VIEW_UNSPECIFIED \| The group forum post view is not specified; the default will be used. \| \| FULL \| Includes all fields but does not dereference `first_comment`. Only the `path` field is populated. \| \| FULL_WITH_FIRST_COMMENT \| Includes all fields and also fully populates `first_comment`. The default view. \| Valid values: `VIEW_UNSPECIFIED`, `FULL`, `FULL_WITH_FIRST_COMMENT` | **Responses:** - `200`: OK → `ListGroupForumPostsResponse` **Response fields** (`ListGroupForumPostsResponse`) See [ListGroupForumPostsResponse](#listgroupforumpostsresponse) in Models. **Response example:** ```json { "groupForumPosts": [ { "path": "...", "createTime": "...", "updateTime": "...", "pinned": "...", "locked": "...", "groupForumPostId": "..." } ], "nextPageToken": "string" } ``` **Rate Limits:** perApiKeyOwner: 150/minute, perOauth2Authorization: 30/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/groups/{GROUP_ID}/forum-categories/{FORUM_CATEGORY_ID}/posts" ``` ### GET `/cloud/v2/groups/{group_id}/forum-categories/{forum_category_id}/posts/{post_id}/comments` [BETA] List Group Forum Comments Lists forum comments on a group's forum post. Supports filtering. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `group-forum:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `group_id` | path | `string` | Yes | The group ID. | | `forum_category_id` | path | `string` | Yes | The forum-category ID. | | `post_id` | path | `string` | Yes | The post ID. | | `maxPageSize` | query | `integer` | No | The maximum number of group forum comments to return. The service might return fewer than this value. If unspecified, at most 10 group forum comments are returned. The maximum value is 100 and higher values are set to 100. | | `pageToken` | query | `string` | No | A page token, received from a previous call, to retrieve a subsequent page. When paginating, all other parameters provided to the subsequent call must match the call that provided the page token. | | `filter` | query | `string` | No | This field may be set in order to filter the resources returned. Filtering conforms to Common Expression Language (CEL). Only the string field `repliesToCommentId` and the `==` operator are supported. If `repliesToCommentId` is specified, only comments that respond to the comment are returned. The default value is `''`. If `repliesToCommentId=''`, only comments responding directly to the post are returned. Example: `"filter=repliesToCommentId==01234567-89ab-cdef-0123-456789abcdef"` | **Responses:** - `200`: OK → `ListGroupForumCommentsResponse` **Response fields** (`ListGroupForumCommentsResponse`) See [ListGroupForumCommentsResponse](#listgroupforumcommentsresponse) in Models. **Response example:** ```json { "groupForumComments": [ { "path": "...", "groupForumCommentId": "...", "message": "..." } ], "nextPageToken": "string" } ``` **Rate Limits:** perApiKeyOwner: 150/minute, perOauth2Authorization: 30/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/groups/{GROUP_ID}/forum-categories/{FORUM_CATEGORY_ID}/posts/{POST_ID}/comments" ``` ### GET `/cloud/v2/groups/{group_id}/join-requests` [BETA] List Group Join Requests List join requests under a group. Supports filtering. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `group:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `group_id` | path | `string` | Yes | The group ID. | | `maxPageSize` | query | `integer` | No | The maximum number of group join requests to return. The service might return fewer than this value. If unspecified, at most 10 group join requests are returned. The maximum value is 20 and higher values are set to 20. | | `pageToken` | query | `string` | No | A page token, received from a previous call, to retrieve a subsequent page. When paginating, all other parameters provided to the subsequent call must match the call that provided the page token. | | `filter` | query | `string` | No | This field may be set in order to filter the resources returned. Filtering conforms to Common Expression Language (CEL). Only the `user` field and `==` operator are supported. Example: `"user == 'users/156'"` | **Responses:** - `200`: OK → `ListGroupJoinRequestsResponse` **Response fields** (`ListGroupJoinRequestsResponse`) See [ListGroupJoinRequestsResponse](#listgroupjoinrequestsresponse) in Models. **Response example:** ```json { "groupJoinRequests": [ { "path": "...", "createTime": "...", "user": "..." } ], "nextPageToken": "string" } ``` **Rate Limits:** perApiKeyOwner: 300/minute, perOauth2Authorization: 90/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/groups/{GROUP_ID}/join-requests" ``` ### POST `/cloud/v2/groups/{group_id}/join-requests/{join_request_id}:accept` [BETA] Accept Group Join Request Accepts a join request. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `group:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `group_id` | path | `string` | Yes | The group ID. | | `join_request_id` | path | `string` | Yes | The join-request ID. | **Request Body:** `application/json` — Type: `AcceptGroupJoinRequestRequest` See [AcceptGroupJoinRequestRequest](#acceptgroupjoinrequestrequest) in Models. **Request example:** ```json {} ``` > **Verify mutations:** If your API key lacks the required scope (`group:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 90/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/groups/{GROUP_ID}/join-requests/{JOIN_REQUEST_ID}:accept" \ -H "Content-Type: application/json" \ -d '{}' ``` ### POST `/cloud/v2/groups/{group_id}/join-requests/{join_request_id}:decline` [BETA] Decline Group Join Request Declines a join request. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `group:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `group_id` | path | `string` | Yes | The group ID. | | `join_request_id` | path | `string` | Yes | The join-request ID. | **Request Body:** `application/json` — Type: `DeclineGroupJoinRequestRequest` See [DeclineGroupJoinRequestRequest](#declinegroupjoinrequestrequest) in Models. **Request example:** ```json {} ``` > **Verify mutations:** If your API key lacks the required scope (`group:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 90/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/groups/{GROUP_ID}/join-requests/{JOIN_REQUEST_ID}:decline" \ -H "Content-Type: application/json" \ -d '{}' ``` ### GET `/cloud/v2/groups/{group_id}/memberships` [BETA] List Group Memberships List group members in a group. Supports filtering. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `group_id` | path | `string` | Yes | The group ID. | | `maxPageSize` | query | `integer` | No | The maximum number of group memberships to return. The service might return fewer than this value. If unspecified, at most 10 group memberships are returned. The maximum value is 100 and higher values are set to 100. | | `pageToken` | query | `string` | No | A page token, received from a previous call, to retrieve a subsequent page. When paginating, all other parameters provided to the subsequent call must match the call that provided the page token. | | `filter` | query | `string` | No | This field may be set in order to filter the resources returned. See the [filtering](/docs/en-us/cloud/reference/patterns.md#list-group-memberships) documentation for more information. | **Responses:** - `200`: OK → `ListGroupMembershipsResponse` **Response fields** (`ListGroupMembershipsResponse`) See [ListGroupMembershipsResponse](#listgroupmembershipsresponse) in Models. **Response example:** ```json { "groupMemberships": [ { "path": "...", "createTime": "...", "updateTime": "...", "user": "...", "role": "...", "roles": "..." } ], "nextPageToken": "string" } ``` **Rate Limits:** perApiKeyOwner: 300/minute, perOauth2Authorization: 90/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/groups/{GROUP_ID}/memberships" ``` ### PATCH `/cloud/v2/groups/{group_id}/memberships/{membership_id}` [BETA] Update Group Membership **Deprecated.** Use AssignGroupRole and UnassignGroupRole instead. Updates the group membership for a particular group member. This action requires the requester to be able to manage lower ranked members. Guest or Owner ranks cannot be assigned, and a requester cannot change their own rank. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `group:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `group_id` | path | `string` | Yes | The group ID. | | `membership_id` | path | `string` | Yes | The membership ID. | **Request Body:** `application/json` — Type: `GroupMembership` See [GroupMembership](#groupmembership) in Models. **Request example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:00Z", "user": "string", "role": "string", "roles": [ "string" ] } ``` > **Verify mutations:** If your API key lacks the required scope (`group:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `GroupMembership` **Response fields** (`GroupMembership`) See [GroupMembership](#groupmembership) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:00Z", "user": "string", "role": "string", "roles": [ "string" ] } ``` **Rate Limits:** perApiKeyOwner: 300/minute, perOauth2Authorization: 90/minute **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/groups/{GROUP_ID}/memberships/{MEMBERSHIP_ID}" \ -H "Content-Type: application/json" \ -d '{ "path": "string", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:00Z", "user": "string", "role": "string", "roles": [ "string" ] }' ``` ### POST `/cloud/v2/groups/{group_id}/memberships/{membership_id}:assignRole` [BETA] Assign Role Group Membership Assigns a specific role to a user within a group. If the user already holds the specified role, no action is taken. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `group:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `group_id` | path | `string` | Yes | The group ID. | | `membership_id` | path | `string` | Yes | The membership ID. | **Request Body:** `application/json` — Type: `AssignRoleGroupMembershipRequest` See [AssignRoleGroupMembershipRequest](#assignrolegroupmembershiprequest) in Models. **Request example:** ```json { "role": "string" } ``` > **Verify mutations:** If your API key lacks the required scope (`group:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `GroupMembership` **Response fields** (`GroupMembership`) See [GroupMembership](#groupmembership) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:00Z", "user": "string", "role": "string", "roles": [ "string" ] } ``` **Rate Limits:** perApiKeyOwner: 300/minute, perOauth2Authorization: 90/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/groups/{GROUP_ID}/memberships/{MEMBERSHIP_ID}:assignRole" \ -H "Content-Type: application/json" \ -d '{ "role": "string" }' ``` ### POST `/cloud/v2/groups/{group_id}/memberships/{membership_id}:unassignRole` [BETA] Unassign Role Group Membership Unassigns a specific role from a user within a group. If the user does not hold the specified role, no action is taken. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `group:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `group_id` | path | `string` | Yes | The group ID. | | `membership_id` | path | `string` | Yes | The membership ID. | **Request Body:** `application/json` — Type: `UnassignRoleGroupMembershipRequest` See [UnassignRoleGroupMembershipRequest](#unassignrolegroupmembershiprequest) in Models. **Request example:** ```json { "role": "string" } ``` > **Verify mutations:** If your API key lacks the required scope (`group:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `GroupMembership` **Response fields** (`GroupMembership`) See [GroupMembership](#groupmembership) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:00Z", "user": "string", "role": "string", "roles": [ "string" ] } ``` **Rate Limits:** perApiKeyOwner: 300/minute, perOauth2Authorization: 90/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/groups/{GROUP_ID}/memberships/{MEMBERSHIP_ID}:unassignRole" \ -H "Content-Type: application/json" \ -d '{ "role": "string" }' ``` ### GET `/cloud/v2/groups/{group_id}/roles` [BETA] List Group Roles List roles in a group. The permissions field for roles is viewable based on the requester's access and scopes. Permissions for the guest role are always visible - a scope is not needed. If the requester is a member of the group and has the `group:read` scope, permissions in their role are visible. If the requester is the owner of the group and has the `group:read` scope, permissions in all roles are visible. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `group:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `group_id` | path | `string` | Yes | The group ID. | | `maxPageSize` | query | `integer` | No | The maximum number of group roles to return. The service might return fewer than this value. If unspecified, at most 10 group roles are returned. The maximum value is 20 and higher values are set to 20. | | `pageToken` | query | `string` | No | A page token, received from a previous call, to retrieve a subsequent page. When paginating, all other parameters provided to the subsequent call must match the call that provided the page token. | **Responses:** - `200`: OK → `ListGroupRolesResponse` **Response fields** (`ListGroupRolesResponse`) See [ListGroupRolesResponse](#listgrouprolesresponse) in Models. **Response example:** ```json { "groupRoles": [ { "path": "...", "createTime": "...", "updateTime": "...", "id": "...", "displayName": "...", "description": "..." } ], "nextPageToken": "string" } ``` **Rate Limits:** perApiKeyOwner: 300/minute, perOauth2Authorization: 90/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/groups/{GROUP_ID}/roles" ``` ### GET `/cloud/v2/groups/{group_id}/roles/{role_id}` [BETA] Get Group Role Get the group role **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `group:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `group_id` | path | `string` | Yes | The group ID. | | `role_id` | path | `string` | Yes | The role ID. | **Responses:** - `200`: OK → `GroupRole` **Response fields** (`GroupRole`) See [GroupRole](#grouprole) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:00Z", "id": "string", "displayName": "string", "description": "string" } ``` **Rate Limits:** perApiKeyOwner: 300/minute, perOauth2Authorization: 90/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/groups/{GROUP_ID}/roles/{ROLE_ID}" ``` ### GET `/cloud/v2/universes/{universeId}/secrets` [BETA] List Secrets Lists all secrets defined for a universe. Secret content is not returned for security reasons - only metadata such as ID, domain, creation and update timestamps are included. Only the owner of the universe can list secrets. For group-owned universes, only the group owner or authorized members can list secrets. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `universe.secret:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe ID | | `limit` | query | `integer` | No | Number of secrets to return per page (1-500, default 10) | | `cursor` | query | `string` | No | Pagination cursor from previous response | **Responses:** - `200`: OK → `SecretPaginatedList` - `400`: Bad Request → `SecretsStoreService.ProblemDetails` - `403`: Forbidden → `SecretsStoreService.ProblemDetails` **Response fields** (`SecretPaginatedList`) See [SecretPaginatedList](#secretpaginatedlist) in Models. **Response example:** ```json { "secrets": [ { "id": "...", "secret": "...", "key_id": "...", "domain": "...", "create_time": "...", "update_time": "..." } ], "nextPageCursor": "string", "previousPageCursor": "string" } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 120/minute, perOauth2Authorization: 120/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSEID}/secrets" ``` ### POST `/cloud/v2/universes/{universeId}/secrets` [BETA] Create Secret Creates a new secret. A maximum of 500 secrets per universe is allowed. Only the owner of the universe can create secrets. For group-owned universes, only the group owner or authorized members can create secrets. To encrypt the secret: 1. Get the public key using the Get Public Key endpoint 2. Encrypt your secret using LibSodium sealed box 3. Base64 encode the encrypted content Include the key_id from the public key response in the request. For an example, see the [Secrets store guide](https://create.roblox.com/docs/cloud/guides/secrets-store). **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `universe.secret:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe ID | **Request Body:** `application/json` — Type: `Secret` See [Secret](#secret) in Models. **Request example:** ```json { "id": "string", "secret": "string", "key_id": "string", "domain": "string", "create_time": "string", "update_time": "string" } ``` > **Verify mutations:** If your API key lacks the required scope (`universe.secret:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `201`: Created → `Secret` - `400`: Bad Request → `SecretsStoreService.ProblemDetails` - `403`: Forbidden → `SecretsStoreService.ProblemDetails` - `409`: Conflict → `SecretsStoreService.ProblemDetails` **Response fields** (`Secret`) See [Secret](#secret) in Models. **Response example:** ```json { "id": "string", "secret": "string", "key_id": "string", "domain": "string", "create_time": "string", "update_time": "string" } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 120/minute, perOauth2Authorization: 120/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSEID}/secrets" \ -H "Content-Type: application/json" \ -d '{ "id": "string", "secret": "string", "key_id": "string", "domain": "string", "create_time": "string", "update_time": "string" }' ``` ### GET `/cloud/v2/universes/{universeId}/secrets/public-key` [BETA] Get Public Key Retrieves the public key for a universe. You need this key to encrypt secret content before sending it to Roblox. Only the owner of the universe can retrieve the public key. For group-owned universes, only the group owner or authorized members can retrieve the public key. The secret id field is static and always returns "public-key". The returned public key in the secret field is universe-specific and derived from a master key using the universe ID. Use this key with LibSodium sealed box encryption to encrypt your secret content before creating or updating secrets. Include the key_id from the public key response in the request to create or update a secret. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `universe.secret:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe ID | **Responses:** - `200`: OK → `Secret` - `400`: Bad Request → `SecretsStoreService.ProblemDetails` - `403`: Forbidden → `SecretsStoreService.ProblemDetails` **Response fields** (`Secret`) See [Secret](#secret) in Models. **Response example:** ```json { "id": "string", "secret": "string", "key_id": "string", "domain": "string", "create_time": "string", "update_time": "string" } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 120/minute, perOauth2Authorization: 120/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSEID}/secrets/public-key" ``` ### PATCH `/cloud/v2/universes/{universeId}/secrets/{secretId}` [BETA] Update Secret Updates an existing secret. Only the owner of the universe can update secrets. For group-owned universes, only the group owner or authorized members can update secrets. Only the secret content, key_id, and domain can be updated - the secret ID cannot be changed. To encrypt the updated secret: 1. Get the current public key using the GetPublicKey endpoint 2. Encrypt your new secret content using LibSodium sealed box 3. Base64 encode the encrypted content Include the key_id from the public key response in the request. For an example, see the [Secrets store guide](https://create.roblox.com/docs/cloud/guides/secrets-store). **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `universe.secret:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe ID | | `secretId` | path | `string` | Yes | The ID of the secret to update | **Request Body:** `application/json` — Type: `Secret` See [Secret](#secret) in Models. **Request example:** ```json { "id": "string", "secret": "string", "key_id": "string", "domain": "string", "create_time": "string", "update_time": "string" } ``` > **Verify mutations:** If your API key lacks the required scope (`universe.secret:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Secret` - `400`: Bad Request → `SecretsStoreService.ProblemDetails` - `403`: Forbidden → `SecretsStoreService.ProblemDetails` - `404`: Not Found → `SecretsStoreService.ProblemDetails` **Response fields** (`Secret`) See [Secret](#secret) in Models. **Response example:** ```json { "id": "string", "secret": "string", "key_id": "string", "domain": "string", "create_time": "string", "update_time": "string" } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 120/minute, perOauth2Authorization: 120/minute **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSEID}/secrets/{SECRETID}" \ -H "Content-Type: application/json" \ -d '{ "id": "string", "secret": "string", "key_id": "string", "domain": "string", "create_time": "string", "update_time": "string" }' ``` ### DELETE `/cloud/v2/universes/{universeId}/secrets/{secretId}` [BETA] Delete Secret Permanently deletes a secret from a universe. Only the owner of the universe can delete secrets. For group-owned universes, only the group owner or authorized members can delete secrets. This operation is irreversible. Make sure you no longer need the secret before deleting it. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `universe.secret:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe ID | | `secretId` | path | `string` | Yes | The ID of the secret to delete | > **Verify mutations:** If your API key lacks the required scope (`universe.secret:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK - `400`: Bad Request → `SecretsStoreService.ProblemDetails` - `403`: Forbidden → `SecretsStoreService.ProblemDetails` - `404`: Not Found → `SecretsStoreService.ProblemDetails` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 120/minute, perOauth2Authorization: 120/minute **Example:** ```bash curl -X DELETE -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSEID}/secrets/{SECRETID}" ``` ### GET `/cloud/v2/universes/{universe_id}` [STABLE] Get Universe Gets the specified universe. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | **Responses:** - `200`: OK → `Universe` **Response fields** (`Universe`) See [Universe](#universe) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:00Z", "displayName": "string", "description": "string", "user": "string" } ``` **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}" ``` ### PATCH `/cloud/v2/universes/{universe_id}` [STABLE] Update Universe Updates the specified universe. This method is guaranteed to return all updated fields. This method may additionally return the full resource. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `universe:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `updateMask` | query | `string` | No | The list of fields to update. | **Request Body:** `application/json` — Type: `Universe` See [Universe](#universe) in Models. **Request example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:00Z", "displayName": "string", "description": "string", "user": "string" } ``` > **Verify mutations:** If your API key lacks the required scope (`universe:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Universe` **Response fields** (`Universe`) See [Universe](#universe) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:00Z", "displayName": "string", "description": "string", "user": "string" } ``` **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}" \ -H "Content-Type: application/json" \ -d '{ "path": "string", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:00Z", "displayName": "string", "description": "string", "user": "string" }' ``` ### GET `/cloud/v2/universes/{universe_id}/data-stores` [STABLE] List Data Stores Returns a list of data stores. Data stores scheduled for permanent deletion are omitted from the results by default (or when `showDeleted` is set to `false`). When this is the case, the operation will check up to 512 data stores. If all checked data stores are deleted, it will return an empty list with a page token to continue iteration. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe-datastores.control:list` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `maxPageSize` | query | `integer` | No | The maximum number of data stores to return. The service might return fewer than this value. If unspecified, at most 10 data stores are returned. The maximum value is 100 and higher values are set to 100. | | `pageToken` | query | `string` | No | A page token, received from a previous call, to retrieve a subsequent page. When paginating, all other parameters provided to the subsequent call must match the call that provided the page token. | | `filter` | query | `string` | No | This field may be set in order to filter the resources returned. The `filter` field supports a very small subset of CEL: * Only the `id` field is supported. * Only the `startsWith` function is available; no other operators nor built-ins are supported. Example filter: `id.startsWith("foo")` | | `showDeleted` | query | `boolean` | No | If true, resources marked for pending deletion will be included in the results. | **Responses:** - `200`: OK → `ListDataStoresResponse` **Response fields** (`ListDataStoresResponse`) See [ListDataStoresResponse](#listdatastoresresponse) in Models. **Response example:** ```json { "dataStores": [ { "path": "...", "createTime": "...", "expireTime": "...", "state": "...", "id": "..." } ], "nextPageToken": "string" } ``` **Rate Limits:** perApiKeyOwner: 1000000/minute **Rate Limit Note:** Data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/data-stores" ``` ### DELETE `/cloud/v2/universes/{universe_id}/data-stores/{data_store_id}` [BETA] Delete Data Store Schedules the specified data store for permanent deletion. This operation assigns the data store an expiry time 30 days in the future, at which point permanent deletion begins. To cancel, use the `UndeleteDataStore` operation before the data store's expiry time. Permanent deletion consists of deleting all of the entries in the data store and then the data store resource itself. The data store is no longer returned by the `ListDataStores` Open Cloud endpoint or `ListDataStoresAsync` Luau API, and you can reuse the data store's name. The duration of the permanent deletion process depends on the number of entries in the data store. However, you can expect a data store with 1 million or fewer entries to be permanently deleted within 3 days. Data stores scheduled for permanent deletion are returned by the `ListDataStores` Open Cloud endpoint when the query parameter `showDeleted` is set to `true`. In the return value, each data store will have a `DELETED` state and an `expireTime` field. Data stores scheduled for permanent deletion are immediately made inaccessible, meaning attempts to read or write to their entries will fail. Note: Due to caching in the backend service, attempts to read from or write to entries in these data stores can continue to succeed for a limited time after the deletion: * `GetDataStoreEntry`: can succeed for up to 24 hours. * All other endpoints: can succeed for several minutes. If the data store is already in a `DELETED` state, this operation is a no-op, and the data store is returned as-is. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe-datastores.control:delete` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `data_store_id` | path | `string` | Yes | The data-store ID. | **Responses:** - `200`: OK → `DataStore` **Response fields** (`DataStore`) See [DataStore](#datastore) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "expireTime": "2024-01-01T00:00:00Z", "state": "STATE_UNSPECIFIED", "id": "string" } ``` **Rate Limits:** perApiKeyOwner: 1000000/minute **Rate Limit Note:** Data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -X DELETE -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/data-stores/{DATA_STORE_ID}" ``` ### GET `/cloud/v2/universes/{universe_id}/data-stores/{data_store_id}/entries` [STABLE] List Data Store Entries Returns a list of entries from a data store. Only the `path` and `id` fields are populated; use `GetDataStoreEntry` to retrieve other fields. Specify the wildcard scope (`-`) to list entries from all scopes. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe-datastores.objects:list` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `data_store_id` | path | `string` | Yes | The data-store ID. | | `maxPageSize` | query | `integer` | No | The maximum number of data store entries to return. The service might return fewer than this value. If unspecified, at most 10 data store entries are returned. The maximum value is 256 and higher values are set to 256. | | `pageToken` | query | `string` | No | A page token, received from a previous call, to retrieve a subsequent page. When paginating, all other parameters provided to the subsequent call must match the call that provided the page token. | | `filter` | query | `string` | No | This field may be set in order to filter the resources returned. The `filter` field supports a very small subset of CEL: * Only the `id` field is supported. * Only the `startsWith` function is available; no other operators nor built-ins are supported. Example filter: `id.startsWith("foo")` | | `showDeleted` | query | `boolean` | No | If true, resources marked for pending deletion will be included in the results. | **Responses:** - `200`: OK → `ListDataStoreEntriesResponse` **Response fields** (`ListDataStoreEntriesResponse`) See [ListDataStoreEntriesResponse](#listdatastoreentriesresponse) in Models. **Response example:** ```json { "dataStoreEntries": [ { "path": "...", "createTime": "...", "revisionId": "...", "revisionCreateTime": "...", "state": "...", "etag": "..." } ], "nextPageToken": "string" } ``` **Rate Limits:** perApiKeyOwner: 1000000/minute **Rate Limit Note:** Data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/data-stores/{DATA_STORE_ID}/entries" ``` ### POST `/cloud/v2/universes/{universe_id}/data-stores/{data_store_id}/entries` [STABLE] Create Data Store Entry Creates an entry with the provided ID and value. Returns a 400 Bad Request if the entry exists. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe-datastores.objects:create` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `data_store_id` | path | `string` | Yes | The data-store ID. | | `id` | query | `string` | No | The ID to use for the data store entry, which will become the final component of the data store entry's resource path. This value should be a 1-50 character string. We strongly recommend using only lowercase letters, numeric digits, and hyphens. | **Request Body:** `application/json` — Type: `DataStoreEntry` See [DataStoreEntry](#datastoreentry) in Models. **Request example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "revisionId": "string", "revisionCreateTime": "2024-01-01T00:00:00Z", "state": "STATE_UNSPECIFIED", "etag": "string" } ``` **Responses:** - `200`: OK → `DataStoreEntry` **Response fields** (`DataStoreEntry`) See [DataStoreEntry](#datastoreentry) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "revisionId": "string", "revisionCreateTime": "2024-01-01T00:00:00Z", "state": "STATE_UNSPECIFIED", "etag": "string" } ``` **Rate Limits:** perApiKeyOwner: 1000000/minute **Rate Limit Note:** Data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/data-stores/{DATA_STORE_ID}/entries" \ -H "Content-Type: application/json" \ -d '{ "path": "string", "createTime": "2024-01-01T00:00:00Z", "revisionId": "string", "revisionCreateTime": "2024-01-01T00:00:00Z", "state": "STATE_UNSPECIFIED", "etag": "string" }' ``` ### GET `/cloud/v2/universes/{universe_id}/data-stores/{data_store_id}/entries/{entry_id}` [STABLE] Get Data Store Entry Gets the specified entry. To get the entry at a specific revision, add `@` to the end of the path. For example, to get `my-entry` at the revision ID `08DC3D3F43F9FCC1.0000000001.08DC3D3F43F9FCC1.01`, use the path `/cloud/v2/universes/1234/data-stores/5678/entries/my-entry@08DC3D3F43F9FCC1.0000000001.08DC3D3F43F9FCC1.01`. If your entry ID contains one or more `@` characters, and you want to get the latest version rather than at any specific revision, append the special revision ID `@latest` to the end of the path. Otherwise, the segment of the entry ID after the last `@` will be interpreted as a revision ID. For example, to get the latest revision of `my-entry`, use the path `/cloud/v2/universes/1234/data-stores/5678/entries/my@entry@latest`. To get the entry that was current at a specific time, add `@latest:` to the end of the path, where `` is RFC-3339 formatted. The given timestamp must be after the Unix epoch (1/1/1970) and not more than ten minutes in the future. For example, to get the revision of `my-entry` that was current on 12/2/2024 at 06:00 UTC, use the path `/cloud/v2/universes/1234/data-stores/5678/entries/my-entry@latest:2024-12-02T06:00:00Z`. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe-datastores.objects:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `data_store_id` | path | `string` | Yes | The data-store ID. | | `entry_id` | path | `string` | Yes | The entry ID. | **Responses:** - `200`: OK → `DataStoreEntry` **Response fields** (`DataStoreEntry`) See [DataStoreEntry](#datastoreentry) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "revisionId": "string", "revisionCreateTime": "2024-01-01T00:00:00Z", "state": "STATE_UNSPECIFIED", "etag": "string" } ``` **Rate Limits:** perApiKeyOwner: 1000000/minute **Rate Limit Note:** Data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/data-stores/{DATA_STORE_ID}/entries/{ENTRY_ID}" ``` ### PATCH `/cloud/v2/universes/{universe_id}/data-stores/{data_store_id}/entries/{entry_id}` [STABLE] Update Data Store Entry Updates the value, attributes, and users of an entry. Updating specific revisions of the entry is **unsupported**. If you specify a revision ID in the path and `allow_missing` is `true`, the update request will instead create a new entry with the `@` suffix as part of the key. Partial update is **unsupported**. If attributes or users are not provided when updating the value, they will be cleared. Value must always be provided when updating an entry. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe-datastores.objects:update` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `data_store_id` | path | `string` | Yes | The data-store ID. | | `entry_id` | path | `string` | Yes | The entry ID. | | `allowMissing` | query | `boolean` | No | If set to true, and the data store entry is not found, a data store entry is created. | **Request Body:** `application/json` — Type: `DataStoreEntry` See [DataStoreEntry](#datastoreentry) in Models. **Request example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "revisionId": "string", "revisionCreateTime": "2024-01-01T00:00:00Z", "state": "STATE_UNSPECIFIED", "etag": "string" } ``` **Responses:** - `200`: OK → `DataStoreEntry` **Response fields** (`DataStoreEntry`) See [DataStoreEntry](#datastoreentry) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "revisionId": "string", "revisionCreateTime": "2024-01-01T00:00:00Z", "state": "STATE_UNSPECIFIED", "etag": "string" } ``` **Rate Limits:** perApiKeyOwner: 1000000/minute **Rate Limit Note:** Data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/data-stores/{DATA_STORE_ID}/entries/{ENTRY_ID}" \ -H "Content-Type: application/json" \ -d '{ "path": "string", "createTime": "2024-01-01T00:00:00Z", "revisionId": "string", "revisionCreateTime": "2024-01-01T00:00:00Z", "state": "STATE_UNSPECIFIED", "etag": "string" }' ``` ### DELETE `/cloud/v2/universes/{universe_id}/data-stores/{data_store_id}/entries/{entry_id}` [STABLE] Delete Data Store Entry Marks the specified entry for deletion. Entries are not be deleted immediately; instead, the `state` field will be set to `DELETED`. Permanent deletion occurs after 30 days. On success, returns 200 OK. If the entry doesn't exist, returns 404 Not Found. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe-datastores.objects:delete` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `data_store_id` | path | `string` | Yes | The data-store ID. | | `entry_id` | path | `string` | Yes | The entry ID. | **Responses:** - `200`: OK **Rate Limits:** perApiKeyOwner: 1000000/minute **Rate Limit Note:** Data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -X DELETE -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/data-stores/{DATA_STORE_ID}/entries/{ENTRY_ID}" ``` ### POST `/cloud/v2/universes/{universe_id}/data-stores/{data_store_id}/entries/{entry_id}:increment` [STABLE] Increment Data Store Entry Increments the value of the specified entry. Both the existing value and the increment amount must be integers. If the entry doesn't exist, creates an entry with the specified value. Incrementing specific revisions of the entry is **unsupported**. If you specify a revision ID in the path, the increment request will create a new entry with the `@` suffix as part of the key. Known issue: the value may be incremented past the valid range of values. When this happens, the returned value will be clamped to the valid range, but the backend may persist the original value. This behavior is maintained for backwards compatibility reasons, but may change in a future version of this API. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe-datastores.objects:create`, `universe-datastores.objects:update` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `data_store_id` | path | `string` | Yes | The data-store ID. | | `entry_id` | path | `string` | Yes | The entry ID. | **Request Body:** `application/json` — Type: `IncrementDataStoreEntryRequest` See [IncrementDataStoreEntryRequest](#incrementdatastoreentryrequest) in Models. **Request example:** ```json { "amount": 0, "users": [ "string" ], "attributes": "..." } ``` **Responses:** - `200`: OK → `DataStoreEntry` **Response fields** (`DataStoreEntry`) See [DataStoreEntry](#datastoreentry) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "revisionId": "string", "revisionCreateTime": "2024-01-01T00:00:00Z", "state": "STATE_UNSPECIFIED", "etag": "string" } ``` **Rate Limits:** perApiKeyOwner: 1000000/minute **Rate Limit Note:** Data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/data-stores/{DATA_STORE_ID}/entries/{ENTRY_ID}:increment" \ -H "Content-Type: application/json" \ -d '{ "amount": 0, "users": [ "string" ], "attributes": "..." }' ``` ### GET `/cloud/v2/universes/{universe_id}/data-stores/{data_store_id}/entries/{entry_id}:listRevisions` [STABLE] List Data Store Entry Revisions List revisions of the data store entry. This method returns partial data store entries. In particular, only the `path`, `id`, `createTime`, `revisionCreateTime`, `revisionId`, `etag`, and `state` fields are populated. Both the `path` and `id` fields will have an `@` suffix. In order to get the full entry at a revision, you can use the provided `path` field with the `GetDataStoreEntry` method, i.e. `GET /cloud/v2/universes/1234/data-stores/5678/entries/my-entry@`. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe-datastores.versions:list` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `data_store_id` | path | `string` | Yes | The data-store ID. | | `entry_id` | path | `string` | Yes | The entry ID. | | `maxPageSize` | query | `integer` | No | The maximum number of revisions to return per page. The service might return fewer than the maximum number of revisions. If unspecified, at most 10 revisions are returned. The maximum value is 100 values and higher values are set to 100. | | `pageToken` | query | `string` | No | A page token, received from a previous call, to retrieve a subsequent page. When paginating, all other parameters provided to the subsequent call must match the call that provided the page token. | | `filter` | query | `string` | No | Supports the following subset of CEL: * Only the `&&`, `<=`, and `>=` operators are supported. * Only the `revision_create_time` field is supported. For example: `"revision_create_time >= 2000-01-01T00:00:00Z && revision_create_time <= 2001-01-01T00:00:00Z"` | **Responses:** - `200`: OK → `ListDataStoreEntryRevisionsResponse` **Response fields** (`ListDataStoreEntryRevisionsResponse`) See [ListDataStoreEntryRevisionsResponse](#listdatastoreentryrevisionsresponse) in Models. **Response example:** ```json { "dataStoreEntries": [ { "path": "...", "createTime": "...", "revisionId": "...", "revisionCreateTime": "...", "state": "...", "etag": "..." } ], "nextPageToken": "string" } ``` **Rate Limits:** perApiKeyOwner: 1000000/minute **Rate Limit Note:** Data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/data-stores/{DATA_STORE_ID}/entries/{ENTRY_ID}:listRevisions" ``` ### GET `/cloud/v2/universes/{universe_id}/data-stores/{data_store_id}/scopes/{scope_id}/entries` [STABLE] List Data Store Entries Returns a list of entries from a data store. Only the `path` and `id` fields are populated; use `GetDataStoreEntry` to retrieve other fields. Specify the wildcard scope (`-`) to list entries from all scopes. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe-datastores.objects:list` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `data_store_id` | path | `string` | Yes | The data-store ID. | | `scope_id` | path | `string` | Yes | The scope ID. | | `maxPageSize` | query | `integer` | No | The maximum number of data store entries to return. The service might return fewer than this value. If unspecified, at most 10 data store entries are returned. The maximum value is 256 and higher values are set to 256. | | `pageToken` | query | `string` | No | A page token, received from a previous call, to retrieve a subsequent page. When paginating, all other parameters provided to the subsequent call must match the call that provided the page token. | | `filter` | query | `string` | No | This field may be set in order to filter the resources returned. The `filter` field supports a very small subset of CEL: * Only the `id` field is supported. * Only the `startsWith` function is available; no other operators nor built-ins are supported. Example filter: `id.startsWith("foo")` | | `showDeleted` | query | `boolean` | No | If true, resources marked for pending deletion will be included in the results. | **Responses:** - `200`: OK → `ListDataStoreEntriesResponse` **Response fields** (`ListDataStoreEntriesResponse`) See [ListDataStoreEntriesResponse](#listdatastoreentriesresponse) in Models. **Response example:** ```json { "dataStoreEntries": [ { "path": "...", "createTime": "...", "revisionId": "...", "revisionCreateTime": "...", "state": "...", "etag": "..." } ], "nextPageToken": "string" } ``` **Rate Limits:** perApiKeyOwner: 1000000/minute **Rate Limit Note:** Data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/data-stores/{DATA_STORE_ID}/scopes/{SCOPE_ID}/entries" ``` ### POST `/cloud/v2/universes/{universe_id}/data-stores/{data_store_id}/scopes/{scope_id}/entries` [STABLE] Create Data Store Entry Creates an entry with the provided ID and value. Returns a 400 Bad Request if the entry exists. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe-datastores.objects:create` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `data_store_id` | path | `string` | Yes | The data-store ID. | | `scope_id` | path | `string` | Yes | The scope ID. | | `id` | query | `string` | No | The ID to use for the data store entry, which will become the final component of the data store entry's resource path. This value should be a 1-50 character string. We strongly recommend using only lowercase letters, numeric digits, and hyphens. | **Request Body:** `application/json` — Type: `DataStoreEntry` See [DataStoreEntry](#datastoreentry) in Models. **Request example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "revisionId": "string", "revisionCreateTime": "2024-01-01T00:00:00Z", "state": "STATE_UNSPECIFIED", "etag": "string" } ``` **Responses:** - `200`: OK → `DataStoreEntry` **Response fields** (`DataStoreEntry`) See [DataStoreEntry](#datastoreentry) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "revisionId": "string", "revisionCreateTime": "2024-01-01T00:00:00Z", "state": "STATE_UNSPECIFIED", "etag": "string" } ``` **Rate Limits:** perApiKeyOwner: 1000000/minute **Rate Limit Note:** Data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/data-stores/{DATA_STORE_ID}/scopes/{SCOPE_ID}/entries" \ -H "Content-Type: application/json" \ -d '{ "path": "string", "createTime": "2024-01-01T00:00:00Z", "revisionId": "string", "revisionCreateTime": "2024-01-01T00:00:00Z", "state": "STATE_UNSPECIFIED", "etag": "string" }' ``` ### GET `/cloud/v2/universes/{universe_id}/data-stores/{data_store_id}/scopes/{scope_id}/entries/{entry_id}` [STABLE] Get Data Store Entry Gets the specified entry. To get the entry at a specific revision, add `@` to the end of the path. For example, to get `my-entry` at the revision ID `08DC3D3F43F9FCC1.0000000001.08DC3D3F43F9FCC1.01`, use the path `/cloud/v2/universes/1234/data-stores/5678/entries/my-entry@08DC3D3F43F9FCC1.0000000001.08DC3D3F43F9FCC1.01`. If your entry ID contains one or more `@` characters, and you want to get the latest version rather than at any specific revision, append the special revision ID `@latest` to the end of the path. Otherwise, the segment of the entry ID after the last `@` will be interpreted as a revision ID. For example, to get the latest revision of `my-entry`, use the path `/cloud/v2/universes/1234/data-stores/5678/entries/my@entry@latest`. To get the entry that was current at a specific time, add `@latest:` to the end of the path, where `` is RFC-3339 formatted. The given timestamp must be after the Unix epoch (1/1/1970) and not more than ten minutes in the future. For example, to get the revision of `my-entry` that was current on 12/2/2024 at 06:00 UTC, use the path `/cloud/v2/universes/1234/data-stores/5678/entries/my-entry@latest:2024-12-02T06:00:00Z`. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe-datastores.objects:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `data_store_id` | path | `string` | Yes | The data-store ID. | | `scope_id` | path | `string` | Yes | The scope ID. | | `entry_id` | path | `string` | Yes | The entry ID. | **Responses:** - `200`: OK → `DataStoreEntry` **Response fields** (`DataStoreEntry`) See [DataStoreEntry](#datastoreentry) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "revisionId": "string", "revisionCreateTime": "2024-01-01T00:00:00Z", "state": "STATE_UNSPECIFIED", "etag": "string" } ``` **Rate Limits:** perApiKeyOwner: 1000000/minute **Rate Limit Note:** Data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/data-stores/{DATA_STORE_ID}/scopes/{SCOPE_ID}/entries/{ENTRY_ID}" ``` ### PATCH `/cloud/v2/universes/{universe_id}/data-stores/{data_store_id}/scopes/{scope_id}/entries/{entry_id}` [STABLE] Update Data Store Entry Updates the value, attributes, and users of an entry. Updating specific revisions of the entry is **unsupported**. If you specify a revision ID in the path and `allow_missing` is `true`, the update request will instead create a new entry with the `@` suffix as part of the key. Partial update is **unsupported**. If attributes or users are not provided when updating the value, they will be cleared. Value must always be provided when updating an entry. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe-datastores.objects:update` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `data_store_id` | path | `string` | Yes | The data-store ID. | | `scope_id` | path | `string` | Yes | The scope ID. | | `entry_id` | path | `string` | Yes | The entry ID. | | `allowMissing` | query | `boolean` | No | If set to true, and the data store entry is not found, a data store entry is created. | **Request Body:** `application/json` — Type: `DataStoreEntry` See [DataStoreEntry](#datastoreentry) in Models. **Request example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "revisionId": "string", "revisionCreateTime": "2024-01-01T00:00:00Z", "state": "STATE_UNSPECIFIED", "etag": "string" } ``` **Responses:** - `200`: OK → `DataStoreEntry` **Response fields** (`DataStoreEntry`) See [DataStoreEntry](#datastoreentry) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "revisionId": "string", "revisionCreateTime": "2024-01-01T00:00:00Z", "state": "STATE_UNSPECIFIED", "etag": "string" } ``` **Rate Limits:** perApiKeyOwner: 1000000/minute **Rate Limit Note:** Data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/data-stores/{DATA_STORE_ID}/scopes/{SCOPE_ID}/entries/{ENTRY_ID}" \ -H "Content-Type: application/json" \ -d '{ "path": "string", "createTime": "2024-01-01T00:00:00Z", "revisionId": "string", "revisionCreateTime": "2024-01-01T00:00:00Z", "state": "STATE_UNSPECIFIED", "etag": "string" }' ``` ### DELETE `/cloud/v2/universes/{universe_id}/data-stores/{data_store_id}/scopes/{scope_id}/entries/{entry_id}` [STABLE] Delete Data Store Entry Marks the specified entry for deletion. Entries are not be deleted immediately; instead, the `state` field will be set to `DELETED`. Permanent deletion occurs after 30 days. On success, returns 200 OK. If the entry doesn't exist, returns 404 Not Found. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe-datastores.objects:delete` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `data_store_id` | path | `string` | Yes | The data-store ID. | | `scope_id` | path | `string` | Yes | The scope ID. | | `entry_id` | path | `string` | Yes | The entry ID. | **Responses:** - `200`: OK **Rate Limits:** perApiKeyOwner: 1000000/minute **Rate Limit Note:** Data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -X DELETE -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/data-stores/{DATA_STORE_ID}/scopes/{SCOPE_ID}/entries/{ENTRY_ID}" ``` ### POST `/cloud/v2/universes/{universe_id}/data-stores/{data_store_id}/scopes/{scope_id}/entries/{entry_id}:increment` [STABLE] Increment Data Store Entry Increments the value of the specified entry. Both the existing value and the increment amount must be integers. If the entry doesn't exist, creates an entry with the specified value. Incrementing specific revisions of the entry is **unsupported**. If you specify a revision ID in the path, the increment request will create a new entry with the `@` suffix as part of the key. Known issue: the value may be incremented past the valid range of values. When this happens, the returned value will be clamped to the valid range, but the backend may persist the original value. This behavior is maintained for backwards compatibility reasons, but may change in a future version of this API. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe-datastores.objects:create`, `universe-datastores.objects:update` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `data_store_id` | path | `string` | Yes | The data-store ID. | | `scope_id` | path | `string` | Yes | The scope ID. | | `entry_id` | path | `string` | Yes | The entry ID. | **Request Body:** `application/json` — Type: `IncrementDataStoreEntryRequest` See [IncrementDataStoreEntryRequest](#incrementdatastoreentryrequest) in Models. **Request example:** ```json { "amount": 0, "users": [ "string" ], "attributes": "..." } ``` **Responses:** - `200`: OK → `DataStoreEntry` **Response fields** (`DataStoreEntry`) See [DataStoreEntry](#datastoreentry) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "revisionId": "string", "revisionCreateTime": "2024-01-01T00:00:00Z", "state": "STATE_UNSPECIFIED", "etag": "string" } ``` **Rate Limits:** perApiKeyOwner: 1000000/minute **Rate Limit Note:** Data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/data-stores/{DATA_STORE_ID}/scopes/{SCOPE_ID}/entries/{ENTRY_ID}:increment" \ -H "Content-Type: application/json" \ -d '{ "amount": 0, "users": [ "string" ], "attributes": "..." }' ``` ### GET `/cloud/v2/universes/{universe_id}/data-stores/{data_store_id}/scopes/{scope_id}/entries/{entry_id}:listRevisions` [STABLE] List Data Store Entry Revisions List revisions of the data store entry. This method returns partial data store entries. In particular, only the `path`, `id`, `createTime`, `revisionCreateTime`, `revisionId`, `etag`, and `state` fields are populated. Both the `path` and `id` fields will have an `@` suffix. In order to get the full entry at a revision, you can use the provided `path` field with the `GetDataStoreEntry` method, i.e. `GET /cloud/v2/universes/1234/data-stores/5678/entries/my-entry@`. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe-datastores.versions:list` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `data_store_id` | path | `string` | Yes | The data-store ID. | | `scope_id` | path | `string` | Yes | The scope ID. | | `entry_id` | path | `string` | Yes | The entry ID. | | `maxPageSize` | query | `integer` | No | The maximum number of revisions to return per page. The service might return fewer than the maximum number of revisions. If unspecified, at most 10 revisions are returned. The maximum value is 100 values and higher values are set to 100. | | `pageToken` | query | `string` | No | A page token, received from a previous call, to retrieve a subsequent page. When paginating, all other parameters provided to the subsequent call must match the call that provided the page token. | | `filter` | query | `string` | No | Supports the following subset of CEL: * Only the `&&`, `<=`, and `>=` operators are supported. * Only the `revision_create_time` field is supported. For example: `"revision_create_time >= 2000-01-01T00:00:00Z && revision_create_time <= 2001-01-01T00:00:00Z"` | **Responses:** - `200`: OK → `ListDataStoreEntryRevisionsResponse` **Response fields** (`ListDataStoreEntryRevisionsResponse`) See [ListDataStoreEntryRevisionsResponse](#listdatastoreentryrevisionsresponse) in Models. **Response example:** ```json { "dataStoreEntries": [ { "path": "...", "createTime": "...", "revisionId": "...", "revisionCreateTime": "...", "state": "...", "etag": "..." } ], "nextPageToken": "string" } ``` **Rate Limits:** perApiKeyOwner: 1000000/minute **Rate Limit Note:** Data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/data-stores/{DATA_STORE_ID}/scopes/{SCOPE_ID}/entries/{ENTRY_ID}:listRevisions" ``` ### POST `/cloud/v2/universes/{universe_id}/data-stores/{data_store_id}:undelete` [BETA] Undelete Data Store Restore the data store **Auth:** API Key (`x-api-key` header) **Scopes:** `universe-datastores.control:delete` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `data_store_id` | path | `string` | Yes | The data-store ID. | **Request Body:** `application/json` — Type: `UndeleteDataStoreRequest` See [UndeleteDataStoreRequest](#undeletedatastorerequest) in Models. **Request example:** ```json {} ``` **Responses:** - `200`: OK → `DataStore` **Response fields** (`DataStore`) See [DataStore](#datastore) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "expireTime": "2024-01-01T00:00:00Z", "state": "STATE_UNSPECIFIED", "id": "string" } ``` **Rate Limits:** perApiKeyOwner: 1000000/minute **Rate Limit Note:** Data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/data-stores/{DATA_STORE_ID}:undelete" \ -H "Content-Type: application/json" \ -d '{}' ``` ### POST `/cloud/v2/universes/{universe_id}/data-stores:snapshot` [STABLE] Snapshot Data Stores Takes a new snapshot of the data stores in an experience. After a snapshot, the next write to every key in the experience will create a versioned backup of the previous data, regardless of the time of the last write. In effect, all data current at the time of the snapshot is guaranteed to be available as a versioned backup for at least 30 days. Snapshots can be taken once per UTC day, per experience. If the latest snapshot was taken within the same UTC day, this operation is a no-op and the time of the latest snapshot will be returned. For more information on using snapshots, see the [Data stores](https://create.roblox.com/docs/cloud-services/data-stores#snapshots) Engine guide. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe-datastores.control:snapshot` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | **Request Body:** `application/json` — Type: `SnapshotDataStoresRequest` See [SnapshotDataStoresRequest](#snapshotdatastoresrequest) in Models. **Request example:** ```json {} ``` **Responses:** - `200`: OK → `SnapshotDataStoresResponse` **Response fields** (`SnapshotDataStoresResponse`) See [SnapshotDataStoresResponse](#snapshotdatastoresresponse) in Models. **Response example:** ```json { "newSnapshotTaken": false, "latestSnapshotTime": "2024-01-01T00:00:00Z" } ``` **Rate Limits:** perApiKeyOwner: 60/minute **Rate Limit Note:** Data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/data-stores:snapshot" \ -H "Content-Type: application/json" \ -d '{}' ``` ### POST `/cloud/v2/universes/{universe_id}/luau-execution-session-task-binary-inputs` [STABLE] Create Luau Execution Session Task Binary Input Create a new `LuauExecutionSessionTaskBinaryInput`. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe.place.luau-execution-session:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | **Request Body:** `application/json` — Type: `LuauExecutionSessionTaskBinaryInput` See [LuauExecutionSessionTaskBinaryInput](#luauexecutionsessiontaskbinaryinput) in Models. **Request example:** ```json { "path": "string", "size": 0, "uploadUri": "string" } ``` > **Verify mutations:** If your API key lacks the required scope (`universe.place.luau-execution-session:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `LuauExecutionSessionTaskBinaryInput` **Response fields** (`LuauExecutionSessionTaskBinaryInput`) See [LuauExecutionSessionTaskBinaryInput](#luauexecutionsessiontaskbinaryinput) in Models. **Response example:** ```json { "path": "string", "size": 0, "uploadUri": "string" } ``` **Rate Limits:** perApiKeyOwner: 5/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/luau-execution-session-task-binary-inputs" \ -H "Content-Type: application/json" \ -d '{ "path": "string", "size": 0, "uploadUri": "string" }' ``` ### GET `/cloud/v2/universes/{universe_id}/memory-store/operations/{operation_id}` [BETA] Get Memory Store Flush Operation Retrieves the status of the operation to [flush the memory stores of a universe](https://create.roblox.com/docs/cloud/reference/features/memory-stores#Cloud_FlushMemoryStore). **Auth:** API Key (`x-api-key` header) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `operation_id` | path | `string` | Yes | The operation ID. | | `scope` | query | `LIVE \| TEST` | No | The scope of the memory store flush operation. Possible values: \| Value \| Description \| \| --- \| --- \| \| LIVE \| Flush the live memory store scope. This is the default. \| \| TEST \| Flush the test memory store scope. \| Valid values: `LIVE`, `TEST` | **Responses:** - `200`: OK → `OCV2.Operations.Operation` **Response fields** (`OCV2.Operations.Operation`) See [OCV2.Operations.Operation](#ocv2-operations-operation) in Models. **Response example:** ```json { "path": "string", "metadata": { "@type": "string" }, "done": false, "error": { "code": 0, "message": "string", "details": [ "..." ] }, "response": { "@type": "string" } } ``` **Rate Limits:** perApiKeyOwner: 1000000/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/memory-store/operations/{OPERATION_ID}" ``` ### POST `/cloud/v2/universes/{universe_id}/memory-store/queues/{queue_id}/items` [STABLE] Create Memory Store Queue Item Creates a new queue item. If `ttl` is set, the item will automatically be removed from the queue after the time interval specified. If a numerical `priority` is set, the item will be inserted into the queue based on the priority value. The higher the value, the closer to the front of the queue the item will be. If priority values are the same then the item will be inserted after existing values with the same priority. **Auth:** API Key (`x-api-key` header) **Scopes:** `memory-store.queue:add` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `queue_id` | path | `string` | Yes | The queue ID. | **Request Body:** `application/json` — Type: `MemoryStoreQueueItem` See [MemoryStoreQueueItem](#memorystorequeueitem) in Models. **Request example:** ```json { "path": "string", "data": "...", "priority": 0, "ttl": "string", "expireTime": "2024-01-01T00:00:00Z", "id": "string" } ``` **Responses:** - `200`: OK → `MemoryStoreQueueItem` **Response fields** (`MemoryStoreQueueItem`) See [MemoryStoreQueueItem](#memorystorequeueitem) in Models. **Response example:** ```json { "path": "string", "data": "...", "priority": 0, "ttl": "string", "expireTime": "2024-01-01T00:00:00Z", "id": "string" } ``` **Rate Limits:** perApiKeyOwner: 1000000/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/memory-store/queues/{QUEUE_ID}/items" \ -H "Content-Type: application/json" \ -d '{ "path": "string", "data": "...", "priority": 0, "ttl": "string", "expireTime": "2024-01-01T00:00:00Z", "id": "string" }' ``` ### POST `/cloud/v2/universes/{universe_id}/memory-store/queues/{queue_id}/items:discard` [STABLE] Discard Memory Store Queue Items Discards read items from the front of the queue. Takes a `readId` from a previous `Read` operation. **Auth:** API Key (`x-api-key` header) **Scopes:** `memory-store.queue:discard` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `queue_id` | path | `string` | Yes | The queue ID. | **Request Body:** `application/json` — Type: `DiscardMemoryStoreQueueItemsRequest` See [DiscardMemoryStoreQueueItemsRequest](#discardmemorystorequeueitemsrequest) in Models. **Request example:** ```json { "readId": "string" } ``` **Responses:** - `200`: OK **Rate Limits:** perApiKeyOwner: 1000000/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/memory-store/queues/{QUEUE_ID}/items:discard" \ -H "Content-Type: application/json" \ -d '{ "readId": "string" }' ``` ### GET `/cloud/v2/universes/{universe_id}/memory-store/queues/{queue_id}/items:read` [STABLE] Read Memory Store Queue Items Returns the specified number of items at the front of the queue. **Auth:** API Key (`x-api-key` header) **Scopes:** `memory-store.queue:dequeue` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `queue_id` | path | `string` | Yes | The queue ID. | | `count` | query | `integer` | No | The number of items to read from the queue If unspecified, 1 item will be returned. The maximum value is 200; values above 200 will be coerced to 200. | | `allOrNothing` | query | `boolean` | No | If `all_or_nothing` is true and the requested number of objects is not available, will return a 404 Error. Otherwise, will return the path and read_id of the read operation and a list of the MemoryStoreQueue items. | | `invisibilityWindow` | query | `string` | No | Invisibility window for items read, in seconds. Items read are invisible in subsequent reads during the invisibility window duration. It must be written in seconds greater than 0 and end with `s`. Defaults to `30s`. | **Responses:** - `200`: OK → `ReadMemoryStoreQueueItemsResponse` **Response fields** (`ReadMemoryStoreQueueItemsResponse`) See [ReadMemoryStoreQueueItemsResponse](#readmemorystorequeueitemsresponse) in Models. **Response example:** ```json { "readId": "string", "items": [ { "path": "...", "data": "...", "priority": "...", "ttl": "...", "expireTime": "...", "id": "..." } ] } ``` **Rate Limits:** perApiKeyOwner: 1000000/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/memory-store/queues/{QUEUE_ID}/items:read" ``` ### GET `/cloud/v2/universes/{universe_id}/memory-store/sorted-maps/{sorted_map_id}/items` [STABLE] List Memory Store Sorted Map Items Gets and returns items in the map with a given order and filter. **Auth:** API Key (`x-api-key` header) **Scopes:** `memory-store.sorted-map:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `sorted_map_id` | path | `string` | Yes | The sorted-map ID. | | `maxPageSize` | query | `integer` | No | The maximum number of memory store sorted map items to return. The service might return fewer than this value. If unspecified, at most 1 memory store sorted map items are returned. The maximum value is 100 and higher values are set to 100. | | `pageToken` | query | `string` | No | A page token, received from a previous call, to retrieve a subsequent page. When paginating, all other parameters provided to the subsequent call must match the call that provided the page token. | | `orderBy` | query | `string` | No | If specified, results are ordered according to the specified fields. Values must be a comma-separated list of fields, with an optional, per-field " desc" suffix to sort by descending rather than ascending values. You can access subfields with a `.` operator. Results may be ordered by the following fields: id. Example: "id desc" | | `filter` | query | `string` | No | This field may be set in order to filter the resources returned. Filtering conforms to Common Expression Language (CEL). Only the `id` and `sortKey` fields are supported. In terms of operators, only `<`, `>` and `&&` are allowed' Example: `id > "key-001" && id < "key-100"` | **Responses:** - `200`: OK → `ListMemoryStoreSortedMapItemsResponse` **Response fields** (`ListMemoryStoreSortedMapItemsResponse`) See [ListMemoryStoreSortedMapItemsResponse](#listmemorystoresortedmapitemsresponse) in Models. **Response example:** ```json { "memoryStoreSortedMapItems": [ { "path": "...", "value": "...", "etag": "...", "ttl": "...", "expireTime": "...", "id": "..." } ], "nextPageToken": "string" } ``` **Rate Limits:** perApiKeyOwner: 1000000/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/memory-store/sorted-maps/{SORTED_MAP_ID}/items" ``` ### POST `/cloud/v2/universes/{universe_id}/memory-store/sorted-maps/{sorted_map_id}/items` [STABLE] Create Memory Store Sorted Map Item Creates the specified map item if it doesn't exist. **Auth:** API Key (`x-api-key` header) **Scopes:** `memory-store.sorted-map:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `sorted_map_id` | path | `string` | Yes | The sorted-map ID. | | `id` | query | `string` | No | The ID to use for the memory store sorted map item, which will become the final component of the memory store sorted map item's resource path. This value should be a 1-127 character string that supports alphanumeric and special characters. This id is case sensitive. The id must be url encoded if it contains any url breaking special characters. | **Request Body:** `application/json` — Type: `MemoryStoreSortedMapItem` See [MemoryStoreSortedMapItem](#memorystoresortedmapitem) in Models. **Request example:** ```json { "path": "string", "value": "...", "etag": "string", "ttl": "string", "expireTime": "2024-01-01T00:00:00Z", "id": "string" } ``` > **Verify mutations:** If your API key lacks the required scope (`memory-store.sorted-map:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `MemoryStoreSortedMapItem` **Response fields** (`MemoryStoreSortedMapItem`) See [MemoryStoreSortedMapItem](#memorystoresortedmapitem) in Models. **Response example:** ```json { "path": "string", "value": "...", "etag": "string", "ttl": "string", "expireTime": "2024-01-01T00:00:00Z", "id": "string" } ``` **Rate Limits:** perApiKeyOwner: 1000000/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/memory-store/sorted-maps/{SORTED_MAP_ID}/items" \ -H "Content-Type: application/json" \ -d '{ "path": "string", "value": "...", "etag": "string", "ttl": "string", "expireTime": "2024-01-01T00:00:00Z", "id": "string" }' ``` ### GET `/cloud/v2/universes/{universe_id}/memory-store/sorted-maps/{sorted_map_id}/items/{item_id}` [STABLE] Get Memory Store Sorted Map Item Gets and returns the value of the given key in the map. **Auth:** API Key (`x-api-key` header) **Scopes:** `memory-store.sorted-map:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `sorted_map_id` | path | `string` | Yes | The sorted-map ID. | | `item_id` | path | `string` | Yes | The item ID. | **Responses:** - `200`: OK → `MemoryStoreSortedMapItem` **Response fields** (`MemoryStoreSortedMapItem`) See [MemoryStoreSortedMapItem](#memorystoresortedmapitem) in Models. **Response example:** ```json { "path": "string", "value": "...", "etag": "string", "ttl": "string", "expireTime": "2024-01-01T00:00:00Z", "id": "string" } ``` **Rate Limits:** perApiKeyOwner: 1000000/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/memory-store/sorted-maps/{SORTED_MAP_ID}/items/{ITEM_ID}" ``` ### PATCH `/cloud/v2/universes/{universe_id}/memory-store/sorted-maps/{sorted_map_id}/items/{item_id}` [STABLE] Update Memory Store Sorted Map Item Updates the specified map item. **Auth:** API Key (`x-api-key` header) **Scopes:** `memory-store.sorted-map:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `sorted_map_id` | path | `string` | Yes | The sorted-map ID. | | `item_id` | path | `string` | Yes | The item ID. | | `allowMissing` | query | `boolean` | No | If set to true, and the memory store sorted map item is not found, a memory store sorted map item is created. | **Request Body:** `application/json` — Type: `MemoryStoreSortedMapItem` See [MemoryStoreSortedMapItem](#memorystoresortedmapitem) in Models. **Request example:** ```json { "path": "string", "value": "...", "etag": "string", "ttl": "string", "expireTime": "2024-01-01T00:00:00Z", "id": "string" } ``` > **Verify mutations:** If your API key lacks the required scope (`memory-store.sorted-map:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `MemoryStoreSortedMapItem` **Response fields** (`MemoryStoreSortedMapItem`) See [MemoryStoreSortedMapItem](#memorystoresortedmapitem) in Models. **Response example:** ```json { "path": "string", "value": "...", "etag": "string", "ttl": "string", "expireTime": "2024-01-01T00:00:00Z", "id": "string" } ``` **Rate Limits:** perApiKeyOwner: 1000000/minute **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/memory-store/sorted-maps/{SORTED_MAP_ID}/items/{ITEM_ID}" \ -H "Content-Type: application/json" \ -d '{ "path": "string", "value": "...", "etag": "string", "ttl": "string", "expireTime": "2024-01-01T00:00:00Z", "id": "string" }' ``` ### DELETE `/cloud/v2/universes/{universe_id}/memory-store/sorted-maps/{sorted_map_id}/items/{item_id}` [STABLE] Delete Memory Store Sorted Map Item Deletes the specified item from the map. **Auth:** API Key (`x-api-key` header) **Scopes:** `memory-store.sorted-map:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `sorted_map_id` | path | `string` | Yes | The sorted-map ID. | | `item_id` | path | `string` | Yes | The item ID. | > **Verify mutations:** If your API key lacks the required scope (`memory-store.sorted-map:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK **Rate Limits:** perApiKeyOwner: 1000000/minute **Example:** ```bash curl -X DELETE -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/memory-store/sorted-maps/{SORTED_MAP_ID}/items/{ITEM_ID}" ``` ### POST `/cloud/v2/universes/{universe_id}/memory-store:flush` [STABLE] Flush Memory Store Asynchronously flush all data structures in the universe. **Auth:** API Key (`x-api-key` header) **Scopes:** `memory-store:flush` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `scope` | query | `LIVE \| TEST` | No | The scope of the memory store to flush. Possible values: \| Value \| Description \| \| --- \| --- \| \| LIVE \| Flush the live memory store scope. This is the default. \| \| TEST \| Flush the test memory store scope. \| Valid values: `LIVE`, `TEST` | **Responses:** - `200`: OK → `Operation` **Response fields** (`Operation`) See [Operation](#operation) in Models. **Response example:** ```json { "path": "string", "metadata": { "@type": "string" }, "done": false, "error": { "code": 0, "message": "string", "details": [ "..." ] }, "response": { "@type": "string" } } ``` **Rate Limits:** perApiKeyOwner: 1000000/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/memory-store:flush" ``` ### GET `/cloud/v2/universes/{universe_id}/ordered-data-stores/{ordered_data_store_id}/scopes/{scope_id}/entries` [STABLE] List Ordered Data Store Entries Returns a list of entries from an ordered data store. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe.ordered-data-store.scope.entry:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `ordered_data_store_id` | path | `string` | Yes | The ordered-data-store ID. | | `scope_id` | path | `string` | Yes | The scope ID. | | `maxPageSize` | query | `integer` | No | The maximum number of ordered data store entries to return. The service might return fewer than this value. If unspecified, at most 10 ordered data store entries are returned. The maximum value is 100 and higher values are set to 100. | | `pageToken` | query | `string` | No | A page token, received from a previous call, to retrieve a subsequent page. When paginating, all other parameters provided to the subsequent call must match the call that provided the page token. | | `orderBy` | query | `string` | No | If specified, results are ordered according to the specified fields. Values must be a comma-separated list of fields, with an optional, per-field " desc" suffix to sort by descending rather than ascending values. You can access subfields with a `.` operator. Results may be ordered by the following fields: value. Example: "value desc" | | `filter` | query | `string` | No | This field may be set in order to filter the resources returned. We support two comparison operators for this operation: `<=` and `>=`.These comparison operators act as a minValue and maxValue for the values returned. If filtering is needed for a value between a minValue and maxValue the user can use the logical operator `&&`. All tokens in the filter expression must be separated by a single space. Example filters: `entry <= 10`; `entry >= 10 && entry <= 30` | **Responses:** - `200`: OK → `ListOrderedDataStoreEntriesResponse` **Response fields** (`ListOrderedDataStoreEntriesResponse`) See [ListOrderedDataStoreEntriesResponse](#listordereddatastoreentriesresponse) in Models. **Response example:** ```json { "orderedDataStoreEntries": [ { "path": "...", "value": "...", "id": "..." } ], "nextPageToken": "string" } ``` **Rate Limits:** perApiKeyOwner: 1000000/minute **Rate Limit Note:** Ordered data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/ordered-data-stores/{ORDERED_DATA_STORE_ID}/scopes/{SCOPE_ID}/entries" ``` ### POST `/cloud/v2/universes/{universe_id}/ordered-data-stores/{ordered_data_store_id}/scopes/{scope_id}/entries` [STABLE] Create Ordered Data Store Entry Creates an entry with the provided ID and value. Returns a 400 Bad Request if the entry exists. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe.ordered-data-store.scope.entry:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `ordered_data_store_id` | path | `string` | Yes | The ordered-data-store ID. | | `scope_id` | path | `string` | Yes | The scope ID. | | `id` | query | `string` | No | The ID to use for the ordered data store entry, which will become the final component of the ordered data store entry's resource path. This value should be A 1-63 character string. We strongly recommend using only lowercase letters, numeric digits, and hyphens. | **Request Body:** `application/json` — Type: `OrderedDataStoreEntry` See [OrderedDataStoreEntry](#ordereddatastoreentry) in Models. **Request example:** ```json { "path": "string", "value": 0, "id": "string" } ``` > **Verify mutations:** If your API key lacks the required scope (`universe.ordered-data-store.scope.entry:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `OrderedDataStoreEntry` **Response fields** (`OrderedDataStoreEntry`) See [OrderedDataStoreEntry](#ordereddatastoreentry) in Models. **Response example:** ```json { "path": "string", "value": 0, "id": "string" } ``` **Rate Limits:** perApiKeyOwner: 1000000/minute **Rate Limit Note:** Ordered data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/ordered-data-stores/{ORDERED_DATA_STORE_ID}/scopes/{SCOPE_ID}/entries" \ -H "Content-Type: application/json" \ -d '{ "path": "string", "value": 0, "id": "string" }' ``` ### GET `/cloud/v2/universes/{universe_id}/ordered-data-stores/{ordered_data_store_id}/scopes/{scope_id}/entries/{entry_id}` [STABLE] Get Ordered Data Store Entry Gets the specified entry. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe.ordered-data-store.scope.entry:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `ordered_data_store_id` | path | `string` | Yes | The ordered-data-store ID. | | `scope_id` | path | `string` | Yes | The scope ID. | | `entry_id` | path | `string` | Yes | The entry ID. | **Responses:** - `200`: OK → `OrderedDataStoreEntry` **Response fields** (`OrderedDataStoreEntry`) See [OrderedDataStoreEntry](#ordereddatastoreentry) in Models. **Response example:** ```json { "path": "string", "value": 0, "id": "string" } ``` **Rate Limits:** perApiKeyOwner: 1000000/minute **Rate Limit Note:** Ordered data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/ordered-data-stores/{ORDERED_DATA_STORE_ID}/scopes/{SCOPE_ID}/entries/{ENTRY_ID}" ``` ### PATCH `/cloud/v2/universes/{universe_id}/ordered-data-stores/{ordered_data_store_id}/scopes/{scope_id}/entries/{entry_id}` [STABLE] Update Ordered Data Store Entry Updates the value of an entry. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe.ordered-data-store.scope.entry:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `ordered_data_store_id` | path | `string` | Yes | The ordered-data-store ID. | | `scope_id` | path | `string` | Yes | The scope ID. | | `entry_id` | path | `string` | Yes | The entry ID. | | `allowMissing` | query | `boolean` | No | If set to true, and the ordered data store entry is not found, a ordered data store entry is created. | **Request Body:** `application/json` — Type: `OrderedDataStoreEntry` See [OrderedDataStoreEntry](#ordereddatastoreentry) in Models. **Request example:** ```json { "path": "string", "value": 0, "id": "string" } ``` > **Verify mutations:** If your API key lacks the required scope (`universe.ordered-data-store.scope.entry:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `OrderedDataStoreEntry` **Response fields** (`OrderedDataStoreEntry`) See [OrderedDataStoreEntry](#ordereddatastoreentry) in Models. **Response example:** ```json { "path": "string", "value": 0, "id": "string" } ``` **Rate Limits:** perApiKeyOwner: 1000000/minute **Rate Limit Note:** Ordered data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/ordered-data-stores/{ORDERED_DATA_STORE_ID}/scopes/{SCOPE_ID}/entries/{ENTRY_ID}" \ -H "Content-Type: application/json" \ -d '{ "path": "string", "value": 0, "id": "string" }' ``` ### DELETE `/cloud/v2/universes/{universe_id}/ordered-data-stores/{ordered_data_store_id}/scopes/{scope_id}/entries/{entry_id}` [STABLE] Delete Ordered Data Store Entry Deletes the specified entry. On success, returns 200 OK. If the entry doesn't exist, returns 404 Not Found. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe.ordered-data-store.scope.entry:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `ordered_data_store_id` | path | `string` | Yes | The ordered-data-store ID. | | `scope_id` | path | `string` | Yes | The scope ID. | | `entry_id` | path | `string` | Yes | The entry ID. | > **Verify mutations:** If your API key lacks the required scope (`universe.ordered-data-store.scope.entry:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK **Rate Limits:** perApiKeyOwner: 1000000/minute **Rate Limit Note:** Ordered data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -X DELETE -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/ordered-data-stores/{ORDERED_DATA_STORE_ID}/scopes/{SCOPE_ID}/entries/{ENTRY_ID}" ``` ### POST `/cloud/v2/universes/{universe_id}/ordered-data-stores/{ordered_data_store_id}/scopes/{scope_id}/entries/{entry_id}:increment` [STABLE] Increment Ordered Data Store Entry Increments the value of the specified entry. Both the existing value and the increment amount must be integers. If the entry doesn't exist, creates an entry with the specified value. Known issue: the value may be incremented past the valid range of values. When this happens, the returned value will be clamped to the valid range, but the backend may persist the original value. This behavior is maintained for backwards compatibility reasons, but may change in a future version of this API. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe.ordered-data-store.scope.entry:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `ordered_data_store_id` | path | `string` | Yes | The ordered-data-store ID. | | `scope_id` | path | `string` | Yes | The scope ID. | | `entry_id` | path | `string` | Yes | The entry ID. | **Request Body:** `application/json` — Type: `IncrementOrderedDataStoreEntryRequest` See [IncrementOrderedDataStoreEntryRequest](#incrementordereddatastoreentryrequest) in Models. **Request example:** ```json { "amount": 0 } ``` > **Verify mutations:** If your API key lacks the required scope (`universe.ordered-data-store.scope.entry:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `OrderedDataStoreEntry` **Response fields** (`OrderedDataStoreEntry`) See [OrderedDataStoreEntry](#ordereddatastoreentry) in Models. **Response example:** ```json { "path": "string", "value": 0, "id": "string" } ``` **Rate Limits:** perApiKeyOwner: 1000000/minute **Rate Limit Note:** Ordered data stores requests are subject to additional throttling limits described in the [Open Cloud guide for data stores](https://create.roblox.com/docs/cloud/guides/data-stores/throttling). **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/ordered-data-stores/{ORDERED_DATA_STORE_ID}/scopes/{SCOPE_ID}/entries/{ENTRY_ID}:increment" \ -H "Content-Type: application/json" \ -d '{ "amount": 0 }' ``` ### GET `/cloud/v2/universes/{universe_id}/places/{place_id}` [STABLE] Get Place Gets the specified place. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `place_id` | path | `string` | Yes | The place ID. | **Responses:** - `200`: OK → `Place` **Response fields** (`Place`) See [Place](#place) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:00Z", "displayName": "string", "description": "string", "serverSize": 0 } ``` **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/places/{PLACE_ID}" ``` ### PATCH `/cloud/v2/universes/{universe_id}/places/{place_id}` [STABLE] Update Place Updates the specified place. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `universe.place:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `place_id` | path | `string` | Yes | The place ID. | | `updateMask` | query | `string` | No | The list of fields to update. | **Request Body:** `application/json` — Type: `Place` See [Place](#place) in Models. **Request example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:00Z", "displayName": "string", "description": "string", "serverSize": 0 } ``` > **Verify mutations:** If your API key lacks the required scope (`universe.place:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Place` **Response fields** (`Place`) See [Place](#place) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:00Z", "displayName": "string", "description": "string", "serverSize": 0 } ``` **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/places/{PLACE_ID}" \ -H "Content-Type: application/json" \ -d '{ "path": "string", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:00Z", "displayName": "string", "description": "string", "serverSize": 0 }' ``` ### GET `/cloud/v2/universes/{universe_id}/places/{place_id}/instances/{instance_id}` [BETA] Get Instance Gets an instance and its property data. The maximum supported response data size is 500,000 bytes. If this limit is exceeded, the returned `Operation` will be completed with an error result that has an error code of `422`. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe.place.instance:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `place_id` | path | `string` | Yes | The place ID. | | `instance_id` | path | `string` | Yes | The instance ID. | **Responses:** - `200`: OK → `Operation` **Response fields** (`Operation`) See [Operation](#operation) in Models. **Response example:** ```json { "path": "string", "metadata": { "@type": "string" }, "done": false, "error": { "code": 0, "message": "string", "details": [ "..." ] }, "response": { "@type": "string" } } ``` **Rate Limits:** perApiKeyOwner: 45/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/places/{PLACE_ID}/instances/{INSTANCE_ID}" ``` ### PATCH `/cloud/v2/universes/{universe_id}/places/{place_id}/instances/{instance_id}` [BETA] Update Instance Updates an instance's property data. When updating a `Script` instance's source property, the maximum supported property size is 200,000 bytes after UTF-8 encoding. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe.place.instance:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `place_id` | path | `string` | Yes | The place ID. | | `instance_id` | path | `string` | Yes | The instance ID. | | `updateMask` | query | `string` | No | The list of fields to update. | **Request Body:** `application/json` — Type: `Instance` See [Instance](#instance) in Models. **Request example:** ```json { "path": "string", "hasChildren": false, "engineInstance": { "Id": "string", "Parent": "string", "Name": "string", "Details": { "Folder": "...", "LocalScript": "...", "ModuleScript": "...", "Script": "..." } } } ``` > **Verify mutations:** If your API key lacks the required scope (`universe.place.instance:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Operation` **Response fields** (`Operation`) See [Operation](#operation) in Models. **Response example:** ```json { "path": "string", "metadata": { "@type": "string" }, "done": false, "error": { "code": 0, "message": "string", "details": [ "..." ] }, "response": { "@type": "string" } } ``` **Rate Limits:** perApiKeyOwner: 45/minute **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/places/{PLACE_ID}/instances/{INSTANCE_ID}" \ -H "Content-Type: application/json" \ -d '{ "path": "string", "hasChildren": false, "engineInstance": { "Id": "string", "Parent": "string", "Name": "string", "Details": { "Folder": "...", "LocalScript": "...", "ModuleScript": "...", "Script": "..." } } }' ``` ### GET `/cloud/v2/universes/{universe_id}/places/{place_id}/instances/{instance_id}/operations/{operation_id}` [BETA] Get Update Instance Operation Retrieves the status of the operation to [update an instance](https://create.roblox.com/docs/cloud/reference/features/instances#Cloud_UpdateInstance). **Auth:** API Key (`x-api-key` header) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `place_id` | path | `string` | Yes | The place ID. | | `instance_id` | path | `string` | Yes | The instance ID. | | `operation_id` | path | `string` | Yes | The operation ID. | **Responses:** - `200`: OK → `OCV2.Operations.Operation` **Response fields** (`OCV2.Operations.Operation`) See [OCV2.Operations.Operation](#ocv2-operations-operation) in Models. **Response example:** ```json { "path": "string", "metadata": { "@type": "string" }, "done": false, "error": { "code": 0, "message": "string", "details": [ "..." ] }, "response": { "@type": "string" } } ``` **Rate Limits:** perApiKeyOwner: 45/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/places/{PLACE_ID}/instances/{INSTANCE_ID}/operations/{OPERATION_ID}" ``` ### GET `/cloud/v2/universes/{universe_id}/places/{place_id}/instances/{instance_id}:listChildren` [BETA] List Instance Children Lists an instance's children. The maximum supported response data size is 500,000 bytes. If this limit is exceeded, the returned `Operation` will be completed with an error result that has an error code of `422`. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe.place.instance:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `place_id` | path | `string` | Yes | The place ID. | | `instance_id` | path | `string` | Yes | The instance ID. | | `maxPageSize` | query | `integer` | No | The maximum number of child instance to return. The service may return fewer than this value. If unspecified, at most 200 children will be returned. The maximum value is 200; values above 200 will be coerced to 200. | | `pageToken` | query | `string` | No | A page token, received from a previous `ListInstanceChildrenRequest` call. Provide this to retrieve the subsequent page. When paginating, all other parameters provided to `ListInstanceChildrenRequest` must match the call that provided the page token. | **Responses:** - `200`: OK → `Operation` **Response fields** (`Operation`) See [Operation](#operation) in Models. **Response example:** ```json { "path": "string", "metadata": { "@type": "string" }, "done": false, "error": { "code": 0, "message": "string", "details": [ "..." ] }, "response": { "@type": "string" } } ``` **Rate Limits:** perApiKeyOwner: 45/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/places/{PLACE_ID}/instances/{INSTANCE_ID}:listChildren" ``` ### POST `/cloud/v2/universes/{universe_id}/places/{place_id}/luau-execution-session-tasks` [STABLE] Create Luau Execution Session Task Creates a task but does not wait for the task to complete. To check whether a task has completed, call the `GetLuauExecutionSessionTask` method and inspect the `state` field of the returned resource. Quotas: * 5 calls per minute per API key owner * 45 calls per minute per IP address **Auth:** API Key (`x-api-key` header) **Scopes:** `universe.place.luau-execution-session:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `place_id` | path | `string` | Yes | The place ID. | **Request Body:** `application/json` — Type: `LuauExecutionSessionTask` See [LuauExecutionSessionTask](#luauexecutionsessiontask) in Models. **Request example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:00Z", "user": "string", "state": "STATE_UNSPECIFIED", "script": "string" } ``` > **Verify mutations:** If your API key lacks the required scope (`universe.place.luau-execution-session:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `LuauExecutionSessionTask` **Response fields** (`LuauExecutionSessionTask`) See [LuauExecutionSessionTask](#luauexecutionsessiontask) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:00Z", "user": "string", "state": "STATE_UNSPECIFIED", "script": "string" } ``` **Rate Limits:** perApiKeyOwner: 40/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/places/{PLACE_ID}/luau-execution-session-tasks" \ -H "Content-Type: application/json" \ -d '{ "path": "string", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:00Z", "user": "string", "state": "STATE_UNSPECIFIED", "script": "string" }' ``` ### GET `/cloud/v2/universes/{universe_id}/places/{place_id}/user-restrictions` [BETA] List User Restrictions List user restrictions for users that have ever been banned in either a universe or a specific place. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `universe.user-restriction:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `place_id` | path | `string` | Yes | The place ID. | | `maxPageSize` | query | `integer` | No | The maximum number of user restrictions to return. The service might return fewer than this value. If unspecified, at most 10 user restrictions are returned. The maximum value is 100 and higher values are set to 100. | | `pageToken` | query | `string` | No | A page token, received from a previous call, to retrieve a subsequent page. When paginating, all other parameters provided to the subsequent call must match the call that provided the page token. | **Responses:** - `200`: OK → `ListUserRestrictionsResponse` **Response fields** (`ListUserRestrictionsResponse`) See [ListUserRestrictionsResponse](#listuserrestrictionsresponse) in Models. **Response example:** ```json { "userRestrictions": [ { "path": "...", "updateTime": "...", "user": "...", "gameJoinRestriction": "..." } ], "nextPageToken": "string" } ``` **Rate Limits:** perApiKeyOwner: 150/second, perOauth2Authorization: 30/second **Rate Limit Note:** User Restrictions are subject to the additional rate limits, applied per universe * Get User Restriction: 50 req/s * List User Restrictions: 50 req/s * Update User Restrictions: 10 req/s * List User Restriction Logs: 50 req/s Additionally, we impose the following rate limit for the same user within a universe: * Update User Restriction: 2 req/min For example, Update User Restriction may not be called for user 123 more than twice every minute. **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/places/{PLACE_ID}/user-restrictions" ``` ### GET `/cloud/v2/universes/{universe_id}/places/{place_id}/user-restrictions/{user_restriction_id}` [BETA] Get User Restriction Get the user restriction. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `universe.user-restriction:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `place_id` | path | `string` | Yes | The place ID. | | `user_restriction_id` | path | `string` | Yes | The user-restriction ID. | **Responses:** - `200`: OK → `UserRestriction` **Response fields** (`UserRestriction`) See [UserRestriction](#userrestriction) in Models. **Response example:** ```json { "path": "string", "updateTime": "2024-01-01T00:00:00Z", "user": "string", "gameJoinRestriction": { "active": false, "startTime": "2024-01-01T00:00:00Z", "duration": "string", "privateReason": "string", "displayReason": "string", "excludeAltAccounts": false } } ``` **Rate Limits:** perApiKeyOwner: 150/second, perOauth2Authorization: 30/second **Rate Limit Note:** User Restrictions are subject to the additional rate limits, applied per universe * Get User Restriction: 50 req/s * List User Restrictions: 50 req/s * Update User Restrictions: 10 req/s * List User Restriction Logs: 50 req/s Additionally, we impose the following rate limit for the same user within a universe: * Update User Restriction: 2 req/min For example, Update User Restriction may not be called for user 123 more than twice every minute. **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/places/{PLACE_ID}/user-restrictions/{USER_RESTRICTION_ID}" ``` ### PATCH `/cloud/v2/universes/{universe_id}/places/{place_id}/user-restrictions/{user_restriction_id}` [BETA] Update User Restriction Update the user restriction. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `universe.user-restriction:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `place_id` | path | `string` | Yes | The place ID. | | `user_restriction_id` | path | `string` | Yes | The user-restriction ID. | | `updateMask` | query | `string` | No | The list of fields to update. The `game_join_restriction` field must be updated atomically; field masks that index into `game_join_restriction` (such as `"game_join_restriction.active"`) are not supported. | | `idempotencyKey.key` | query | `string` | No | The unique key to use for idempotency. | | `idempotencyKey.firstSent` | query | `string` | No | The timestamp at which the first request was sent. If this is further in the past than the lifetime of the idempotency key (which *may* exceed the annotated minimum lifetime), the server *must* return an error. | **Request Body:** `application/json` — Type: `UserRestriction` See [UserRestriction](#userrestriction) in Models. **Request example:** ```json { "path": "string", "updateTime": "2024-01-01T00:00:00Z", "user": "string", "gameJoinRestriction": { "active": false, "startTime": "2024-01-01T00:00:00Z", "duration": "string", "privateReason": "string", "displayReason": "string", "excludeAltAccounts": false } } ``` > **Verify mutations:** If your API key lacks the required scope (`universe.user-restriction:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `UserRestriction` **Response fields** (`UserRestriction`) See [UserRestriction](#userrestriction) in Models. **Response example:** ```json { "path": "string", "updateTime": "2024-01-01T00:00:00Z", "user": "string", "gameJoinRestriction": { "active": false, "startTime": "2024-01-01T00:00:00Z", "duration": "string", "privateReason": "string", "displayReason": "string", "excludeAltAccounts": false } } ``` **Rate Limits:** perApiKeyOwner: 150/second, perOauth2Authorization: 30/second **Rate Limit Note:** User Restrictions are subject to the additional rate limits, applied per universe * Get User Restriction: 50 req/s * List User Restrictions: 50 req/s * Update User Restrictions: 10 req/s * List User Restriction Logs: 50 req/s Additionally, we impose the following rate limit for the same user within a universe: * Update User Restriction: 2 req/min For example, Update User Restriction may not be called for user 123 more than twice every minute. **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/places/{PLACE_ID}/user-restrictions/{USER_RESTRICTION_ID}" \ -H "Content-Type: application/json" \ -d '{ "path": "string", "updateTime": "2024-01-01T00:00:00Z", "user": "string", "gameJoinRestriction": { "active": false, "startTime": "2024-01-01T00:00:00Z", "duration": "string", "privateReason": "string", "displayReason": "string", "excludeAltAccounts": false } }' ``` ### POST `/cloud/v2/universes/{universe_id}/places/{place_id}/versions/{version_id}/luau-execution-session-tasks` [STABLE] Create Luau Execution Session Task Creates a task but does not wait for the task to complete. To check whether a task has completed, call the `GetLuauExecutionSessionTask` method and inspect the `state` field of the returned resource. Quotas: * 5 calls per minute per API key owner * 45 calls per minute per IP address **Auth:** API Key (`x-api-key` header) **Scopes:** `universe.place.luau-execution-session:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `place_id` | path | `string` | Yes | The place ID. | | `version_id` | path | `string` | Yes | The version ID. | **Request Body:** `application/json` — Type: `LuauExecutionSessionTask` See [LuauExecutionSessionTask](#luauexecutionsessiontask) in Models. **Request example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:00Z", "user": "string", "state": "STATE_UNSPECIFIED", "script": "string" } ``` > **Verify mutations:** If your API key lacks the required scope (`universe.place.luau-execution-session:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `LuauExecutionSessionTask` **Response fields** (`LuauExecutionSessionTask`) See [LuauExecutionSessionTask](#luauexecutionsessiontask) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:00Z", "user": "string", "state": "STATE_UNSPECIFIED", "script": "string" } ``` **Rate Limits:** perApiKeyOwner: 5/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/places/{PLACE_ID}/versions/{VERSION_ID}/luau-execution-session-tasks" \ -H "Content-Type: application/json" \ -d '{ "path": "string", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:00Z", "user": "string", "state": "STATE_UNSPECIFIED", "script": "string" }' ``` ### GET `/cloud/v2/universes/{universe_id}/places/{place_id}/versions/{version_id}/luau-execution-sessions/{luau_execution_session_id}/tasks/{task_id}` [STABLE] Get Luau Execution Session Task Gets information about a task. Quotas: * 45 calls per minute per API key owner or IP address **Auth:** API Key (`x-api-key` header) **Scopes:** `universe.place.luau-execution-session:read`, `universe.place.luau-execution-session:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `place_id` | path | `string` | Yes | The place ID. | | `version_id` | path | `string` | Yes | The version ID. | | `luau_execution_session_id` | path | `string` | Yes | The luau-execution-session ID. | | `task_id` | path | `string` | Yes | The task ID. | | `view` | query | `VIEW_UNSPECIFIED \| BASIC \| FULL` | No | The view in which to retrieve the luau execution session task. Supports BASIC and FULL. Defaults to BASIC. Possible values: \| Value \| Description \| \| --- \| --- \| \| VIEW_UNSPECIFIED \| The luau execution session task view is not specified; the default will be used. \| \| BASIC \| Excludes the `script` field. \| \| FULL \| Includes all fields. \| Valid values: `VIEW_UNSPECIFIED`, `BASIC`, `FULL` | **Responses:** - `200`: OK → `LuauExecutionSessionTask` **Response fields** (`LuauExecutionSessionTask`) See [LuauExecutionSessionTask](#luauexecutionsessiontask) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:00Z", "user": "string", "state": "STATE_UNSPECIFIED", "script": "string" } ``` **Rate Limits:** perApiKeyOwner: 200/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/places/{PLACE_ID}/versions/{VERSION_ID}/luau-execution-sessions/{LUAU_EXECUTION_SESSION_ID}/tasks/{TASK_ID}" ``` ### GET `/cloud/v2/universes/{universe_id}/places/{place_id}/versions/{version_id}/luau-execution-sessions/{luau_execution_session_id}/tasks/{task_id}/logs` [STABLE] List Luau Execution Session Task Logs Lists log chunks generated by a `LuauExecutionSessionTask`. Quotas: * 45 calls per minute per API key owner or IP address **Auth:** API Key (`x-api-key` header) **Scopes:** `universe.place.luau-execution-session:read`, `universe.place.luau-execution-session:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `place_id` | path | `string` | Yes | The place ID. | | `version_id` | path | `string` | Yes | The version ID. | | `luau_execution_session_id` | path | `string` | Yes | The luau-execution-session ID. | | `task_id` | path | `string` | Yes | The task ID. | | `maxPageSize` | query | `integer` | No | The maximum number of luau execution session task logs to return. The service might return fewer than this value. If unspecified, at most 10000 luau execution session task logs are returned. The maximum value is 10000 and higher values are set to 10000. | | `pageToken` | query | `string` | No | A page token, received from a previous call, to retrieve a subsequent page. When paginating, all other parameters provided to the subsequent call must match the call that provided the page token. | | `view` | query | `VIEW_UNSPECIFIED \| FLAT \| STRUCTURED` | No | The view in which to retrieve the luau execution session task log. Supports FLAT and STRUCTURED. Defaults to FLAT. Possible values: \| Value \| Description \| \| --- \| --- \| \| VIEW_UNSPECIFIED \| The luau execution session task log view is not specified; the default will be used. \| \| FLAT \| If this view is selected, the `messages` field will be populated (and the `structuredMessages` field will not). Each entry of the `messages` array contains only the log message, without additional medata. This is the default. \| \| STRUCTURED \| If this view is selected, the `structuredMessages` field will be populated (and the `messages` field will not). Each entry of the `structuredMessages` array contains the log message plus additional metadata (see `LogMessage` for details). \| Valid values: `VIEW_UNSPECIFIED`, `FLAT`, `STRUCTURED` | **Responses:** - `200`: OK → `ListLuauExecutionSessionTaskLogsResponse` **Response fields** (`ListLuauExecutionSessionTaskLogsResponse`) See [ListLuauExecutionSessionTaskLogsResponse](#listluauexecutionsessiontasklogsresponse) in Models. **Response example:** ```json { "luauExecutionSessionTaskLogs": [ { "path": "...", "messages": "...", "structuredMessages": "..." } ], "nextPageToken": "string" } ``` **Rate Limits:** perApiKeyOwner: 45/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/places/{PLACE_ID}/versions/{VERSION_ID}/luau-execution-sessions/{LUAU_EXECUTION_SESSION_ID}/tasks/{TASK_ID}/logs" ``` ### GET `/cloud/v2/universes/{universe_id}/subscription-products/{subscription_product_id}/subscriptions/{subscription_id}` [BETA] Get Subscription Get the subscription. The `universe.subscription-product.subscription:read` scope only allows reading subscriptions of the user making the request. Because of this, the subscription ID must match the user ID of the user making the request. Note that this scope might be more relevant for OAuth 2.0 apps. To read all subscriptions made by users for a particular universe, use the `universe:write` scope instead. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `universe:write`, `universe.subscription-product.subscription:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `subscription_product_id` | path | `string` | Yes | The subscription-product ID. | | `subscription_id` | path | `string` | Yes | The subscription ID. | | `view` | query | `VIEW_UNSPECIFIED \| BASIC \| FULL` | No | The view in which to retrieve the subscription. Supports BASIC and FULL. Defaults to BASIC. Possible values: \| Value \| Description \| \| --- \| --- \| \| VIEW_UNSPECIFIED \| The subscription view is not specified; the default will be used. \| \| BASIC \| Includes only the `active` and `renewing` fields. \| \| FULL \| Includes all fields. \| Valid values: `VIEW_UNSPECIFIED`, `BASIC`, `FULL` | **Responses:** - `200`: OK → `Subscription` **Response fields** (`Subscription`) See [Subscription](#subscription) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:00Z", "active": false, "willRenew": false, "lastBillingTime": "2024-01-01T00:00:00Z" } ``` **Rate Limits:** perApiKeyOwner: 500/minute, perOauth2Authorization: 3/second **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/subscription-products/{SUBSCRIPTION_PRODUCT_ID}/subscriptions/{SUBSCRIPTION_ID}" ``` ### GET `/cloud/v2/universes/{universe_id}/user-restrictions` [BETA] List User Restrictions List user restrictions for users that have ever been banned in either a universe or a specific place. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `universe.user-restriction:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `maxPageSize` | query | `integer` | No | The maximum number of user restrictions to return. The service might return fewer than this value. If unspecified, at most 10 user restrictions are returned. The maximum value is 100 and higher values are set to 100. | | `pageToken` | query | `string` | No | A page token, received from a previous call, to retrieve a subsequent page. When paginating, all other parameters provided to the subsequent call must match the call that provided the page token. | **Responses:** - `200`: OK → `ListUserRestrictionsResponse` **Response fields** (`ListUserRestrictionsResponse`) See [ListUserRestrictionsResponse](#listuserrestrictionsresponse) in Models. **Response example:** ```json { "userRestrictions": [ { "path": "...", "updateTime": "...", "user": "...", "gameJoinRestriction": "..." } ], "nextPageToken": "string" } ``` **Rate Limits:** perApiKeyOwner: 150/second, perOauth2Authorization: 30/second **Rate Limit Note:** User Restrictions are subject to the additional rate limits, applied per universe * Get User Restriction: 50 req/s * List User Restrictions: 50 req/s * Update User Restrictions: 10 req/s * List User Restriction Logs: 50 req/s Additionally, we impose the following rate limit for the same user within a universe: * Update User Restriction: 2 req/min For example, Update User Restriction may not be called for user 123 more than twice every minute. **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/user-restrictions" ``` ### GET `/cloud/v2/universes/{universe_id}/user-restrictions/{user_restriction_id}` [BETA] Get User Restriction Get the user restriction. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `universe.user-restriction:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `user_restriction_id` | path | `string` | Yes | The user-restriction ID. | **Responses:** - `200`: OK → `UserRestriction` **Response fields** (`UserRestriction`) See [UserRestriction](#userrestriction) in Models. **Response example:** ```json { "path": "string", "updateTime": "2024-01-01T00:00:00Z", "user": "string", "gameJoinRestriction": { "active": false, "startTime": "2024-01-01T00:00:00Z", "duration": "string", "privateReason": "string", "displayReason": "string", "excludeAltAccounts": false } } ``` **Rate Limits:** perApiKeyOwner: 150/second, perOauth2Authorization: 30/second **Rate Limit Note:** User Restrictions are subject to the additional rate limits, applied per universe * Get User Restriction: 50 req/s * List User Restrictions: 50 req/s * Update User Restrictions: 10 req/s * List User Restriction Logs: 50 req/s Additionally, we impose the following rate limit for the same user within a universe: * Update User Restriction: 2 req/min For example, Update User Restriction may not be called for user 123 more than twice every minute. **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/user-restrictions/{USER_RESTRICTION_ID}" ``` ### PATCH `/cloud/v2/universes/{universe_id}/user-restrictions/{user_restriction_id}` [BETA] Update User Restriction Update the user restriction. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `universe.user-restriction:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `user_restriction_id` | path | `string` | Yes | The user-restriction ID. | | `updateMask` | query | `string` | No | The list of fields to update. The `game_join_restriction` field must be updated atomically; field masks that index into `game_join_restriction` (such as `"game_join_restriction.active"`) are not supported. | | `idempotencyKey.key` | query | `string` | No | The unique key to use for idempotency. | | `idempotencyKey.firstSent` | query | `string` | No | The timestamp at which the first request was sent. If this is further in the past than the lifetime of the idempotency key (which *may* exceed the annotated minimum lifetime), the server *must* return an error. | **Request Body:** `application/json` — Type: `UserRestriction` See [UserRestriction](#userrestriction) in Models. **Request example:** ```json { "path": "string", "updateTime": "2024-01-01T00:00:00Z", "user": "string", "gameJoinRestriction": { "active": false, "startTime": "2024-01-01T00:00:00Z", "duration": "string", "privateReason": "string", "displayReason": "string", "excludeAltAccounts": false } } ``` > **Verify mutations:** If your API key lacks the required scope (`universe.user-restriction:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `UserRestriction` **Response fields** (`UserRestriction`) See [UserRestriction](#userrestriction) in Models. **Response example:** ```json { "path": "string", "updateTime": "2024-01-01T00:00:00Z", "user": "string", "gameJoinRestriction": { "active": false, "startTime": "2024-01-01T00:00:00Z", "duration": "string", "privateReason": "string", "displayReason": "string", "excludeAltAccounts": false } } ``` **Rate Limits:** perApiKeyOwner: 150/second, perOauth2Authorization: 30/second **Rate Limit Note:** User Restrictions are subject to the additional rate limits, applied per universe * Get User Restriction: 50 req/s * List User Restrictions: 50 req/s * Update User Restrictions: 10 req/s * List User Restriction Logs: 50 req/s Additionally, we impose the following rate limit for the same user within a universe: * Update User Restriction: 2 req/min For example, Update User Restriction may not be called for user 123 more than twice every minute. **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/user-restrictions/{USER_RESTRICTION_ID}" \ -H "Content-Type: application/json" \ -d '{ "path": "string", "updateTime": "2024-01-01T00:00:00Z", "user": "string", "gameJoinRestriction": { "active": false, "startTime": "2024-01-01T00:00:00Z", "duration": "string", "privateReason": "string", "displayReason": "string", "excludeAltAccounts": false } }' ``` ### GET `/cloud/v2/universes/{universe_id}/user-restrictions:listLogs` [BETA] List User Restriction Logs List changes to UserRestriction resources within a given universe. This includes both universe-level and place-level restrictions. For universe-level restriction logs, the `place` field will be empty. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `universe.user-restriction:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | | `maxPageSize` | query | `integer` | No | The maximum number of UserRestrictionLogs to return. The service may return fewer than this value. If unspecified, at most 10 UserRestrictionLogs are returned. The maximum value is 100 and higher values are set to 100. | | `pageToken` | query | `string` | No | A page token, received from a previous call, to retrieve a subsequent page. When paginating, all other parameters provided to the subsequent call must match the call that provided the page token. | | `filter` | query | `string` | No | This field may be set to filter the logs returned. The `filter` field supports a very small number of CEL: * `user` * `place` * The `==` comparison operator is available. * The `&&` logical operator is also supported. As an example, filtering for a user and a place takes the form `filter="user == 'users/123'" && "place == 'places/456'"` | **Responses:** - `200`: OK → `ListUserRestrictionLogsResponse` **Response fields** (`ListUserRestrictionLogsResponse`) See [ListUserRestrictionLogsResponse](#listuserrestrictionlogsresponse) in Models. **Response example:** ```json { "logs": [ { "user": "...", "place": "...", "moderator": "...", "createTime": "...", "active": "...", "startTime": "..." } ], "nextPageToken": "string" } ``` **Rate Limits:** perApiKeyOwner: 150/second, perOauth2Authorization: 30/second **Rate Limit Note:** User Restrictions are subject to the additional rate limits, applied per universe * Get User Restriction: 50 req/s * List User Restrictions: 50 req/s * Update User Restrictions: 10 req/s * List User Restriction Logs: 50 req/s Additionally, we impose the following rate limit for the same user within a universe: * Update User Restriction: 2 req/min For example, Update User Restriction may not be called for user 123 more than twice every minute. **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}/user-restrictions:listLogs" ``` ### POST `/cloud/v2/universes/{universe_id}:generateSpeechAsset` [BETA] Generate Speech Asset Generates an English speech audio asset from the specified text. This endpoint requires the `asset:read` and `asset:write` scopes in addition to the `universe:write` scope. The response returns an `Operation` object that must be prefixed with `/assets/v1`. For example, the URL to discover the result of the operation could be `https://apis.roblox.com/assets/v1/operations/8b42ef30-9c17-4526-b8cf-2ff0136ca94d`. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `universe:write`, `asset:read`, `asset:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | **Request Body:** `application/json` — Type: `GenerateSpeechAssetRequest` See [GenerateSpeechAssetRequest](#generatespeechassetrequest) in Models. **Request example:** ```json { "text": "string", "speechStyle": { "voiceId": "string", "pitch": 0, "speed": 0 } } ``` > **Verify mutations:** If your API key lacks the required scope (`universe:write`, `asset:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Operation` **Response fields** (`Operation`) See [Operation](#operation) in Models. **Response example:** ```json { "path": "string", "metadata": { "@type": "string" }, "done": false, "error": { "code": 0, "message": "string", "details": [ "..." ] }, "response": { "@type": "string" } } ``` **Rate Limits:** perApiKeyOwner: 4/second, perOauth2Authorization: 4/second **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}:generateSpeechAsset" \ -H "Content-Type: application/json" \ -d '{ "text": "string", "speechStyle": { "voiceId": "string", "pitch": 0, "speed": 0 } }' ``` ### POST `/cloud/v2/universes/{universe_id}:publishMessage` [STABLE] Publish Universe Message Publishes a message to the universe's live servers. Servers can consume messages via [MessagingService](https://create.roblox.com/docs/reference/engine/classes/MessagingService). **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `universe-messaging-service:publish` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | **Request Body:** `application/json` — Type: `PublishUniverseMessageRequest` See [PublishUniverseMessageRequest](#publishuniversemessagerequest) in Models. **Request example:** ```json { "topic": "string", "message": "string" } ``` **Responses:** - `200`: OK **Rate Limits:** perApiKeyOwner: 5000/minute, perOauth2Authorization: 5000/minute **Rate Limit Note:** Messaging Service requests are subject to additional throttling limits described in the [Open Cloud guide for Messaging Service](https://create.roblox.com/docs/cloud/guides/usage-messaging#limits). **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}:publishMessage" \ -H "Content-Type: application/json" \ -d '{ "topic": "string", "message": "string" }' ``` ### POST `/cloud/v2/universes/{universe_id}:restartServers` [STABLE] Restart Universe Servers Restarts active servers for a specific universe. Defaults to only restarting servers running older versions, but can be configured to restart all servers regardless of version. Used for releasing experience updates. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `universe:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | **Request Body:** `application/json` — Type: `RestartUniverseServersRequest` See [RestartUniverseServersRequest](#restartuniverseserversrequest) in Models. **Request example:** ```json { "placeIds": [ 0 ], "closeAllVersions": false, "bleedOffServers": false, "bleedOffDurationMinutes": 0 } ``` > **Verify mutations:** If your API key lacks the required scope (`universe:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `RestartUniverseServersResponse` **Response fields** (`RestartUniverseServersResponse`) See [RestartUniverseServersResponse](#restartuniverseserversresponse) in Models. **Response example:** ```json {} ``` **Rate Limits:** perApiKeyOwner: 30/minute, perOauth2Authorization: 30/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}:restartServers" \ -H "Content-Type: application/json" \ -d '{ "placeIds": [ 0 ], "closeAllVersions": false, "bleedOffServers": false, "bleedOffDurationMinutes": 0 }' ``` ### POST `/cloud/v2/universes/{universe_id}:translateText` [BETA] Translate Text Translates the provided text from one language to another. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universe_id` | path | `string` | Yes | The universe ID. | **Request Body:** `application/json` — Type: `TranslateTextRequest` See [TranslateTextRequest](#translatetextrequest) in Models. **Request example:** ```json { "text": "string", "sourceLanguageCode": "string", "targetLanguageCodes": [ "string" ] } ``` > **Verify mutations:** If your API key lacks the required scope (`universe:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `TranslateTextResponse` **Response fields** (`TranslateTextResponse`) See [TranslateTextResponse](#translatetextresponse) in Models. **Response example:** ```json { "sourceLanguageCode": "string", "translations": "..." } ``` **Rate Limits:** perApiKeyOwner: 10000/minute **Rate Limit Note:** Text translation requests are subject to additional rate limits [documented here](https://create.roblox.com/docs/production/localization/auto-translate-dynamic-content#limits). **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSE_ID}:translateText" \ -H "Content-Type: application/json" \ -d '{ "text": "string", "sourceLanguageCode": "string", "targetLanguageCodes": [ "string" ] }' ``` ### GET `/cloud/v2/users/{user_id}` [BETA] Get User Gets a user's basic and advanced information. To access a user's public information, no additional scopes are required. To access a user's verification status, you need the following scopes: * user.advanced:read To access a user's social account information, you need the following scopes: * user.social:read **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `user.advanced:read`, `user.social:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `user_id` | path | `string` | Yes | The user ID. | **Responses:** - `200`: OK → `User` **Response fields** (`User`) See [User](#user) in Models. **Response example:** ```json { "path": "string", "createTime": "2024-01-01T00:00:00Z", "id": "string", "name": "string", "displayName": "string", "about": "string" } ``` **Rate Limits:** perApiKeyOwner: 1000/minute, perOauth2Authorization: 10/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/users/{USER_ID}" ``` ### GET `/cloud/v2/users/{user_id}/asset-quotas` [BETA] List Asset Quotas Returns a list of asset quotas. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `asset:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `user_id` | path | `string` | Yes | The user ID. | | `maxPageSize` | query | `integer` | No | The maximum number of asset quotas to return. The service might return fewer than this value. If unspecified, at most 10 asset quotas are returned. The maximum value is 100 and higher values are set to 100. | | `pageToken` | query | `string` | No | A page token, received from a previous call, to retrieve a subsequent page. When paginating, all other parameters provided to the subsequent call must match the call that provided the page token. | | `filter` | query | `string` | No | This field may be set in order to filter the resources returned. Supports the following subset of CEL: * Only the `quotaType` and `assetType` fields are supported. * Only the `==` and `&&` operator are supported. For example: `quotaType == RateLimitUpload && assetType == Audio` | **Responses:** - `200`: OK → `ListAssetQuotasResponse` **Response fields** (`ListAssetQuotasResponse`) See [ListAssetQuotasResponse](#listassetquotasresponse) in Models. **Response example:** ```json { "assetQuotas": [ { "path": "...", "quotaType": "...", "assetType": "...", "usage": "...", "capacity": "...", "period": "..." } ], "nextPageToken": "string" } ``` **Rate Limits:** perApiKeyOwner: 60/minute, perOauth2Authorization: 60/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/users/{USER_ID}/asset-quotas" ``` ### GET `/cloud/v2/users/{user_id}/inventory-items` [BETA] List Inventory Items List the inventory items in a user's inventory. The inventory items returned depend on the target user’s choice under **Settings > Privacy > Who can see my inventory?**: * If the user granted inventory visibility to "Everyone," then any API key or OAuth2 token can be used to view the target’s inventory, no matter what scopes it has or who created it. * If the user has not granted inventory visibility to "Everyone": * Their inventory can still be viewed with an API key created by the target user with **Inventory: Read** permission. * Their inventory can still be viewed with an OAuth2 token if the target user authorizes an app requesting permissions for the `user.inventory-item:read` scope. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `user.inventory-item:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `user_id` | path | `string` | Yes | The user ID. | | `maxPageSize` | query | `integer` | No | The maximum number of inventory items to return. The service might return fewer than this value. If unspecified, at most 10 inventory items are returned. The maximum value is 100 and higher values are set to 100. | | `pageToken` | query | `string` | No | A page token, received from a previous call, to retrieve a subsequent page. When paginating, all other parameters provided to the subsequent call must match the call that provided the page token. | | `filter` | query | `string` | No | This field may be set in order to filter the resources returned. See the [filtering](/docs/en-us/cloud/reference/patterns.md#list-inventory-items) documentation for more information. | **Responses:** - `200`: OK → `ListInventoryItemsResponse` **Response fields** (`ListInventoryItemsResponse`) See [ListInventoryItemsResponse](#listinventoryitemsresponse) in Models. **Response example:** ```json { "inventoryItems": [ { "path": "...", "assetDetails": "...", "badgeDetails": "...", "gamePassDetails": "...", "privateServerDetails": "...", "addTime": "..." } ], "nextPageToken": "string" } ``` **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 20/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/users/{USER_ID}/inventory-items" ``` ### POST `/cloud/v2/users/{user_id}/notifications` [STABLE] Create User Notification Sends a notification to a user. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `user.user-notification:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `user_id` | path | `string` | Yes | The user ID. | **Request Body:** `application/json` — Type: `UserNotification` See [UserNotification](#usernotification) in Models. **Request example:** ```json { "path": "string", "id": "string", "source": { "universe": "string" }, "payload": { "type": "TYPE_UNSPECIFIED", "messageId": "string", "parameters": "...", "joinExperience": { "launchData": "..." }, "analyticsData": { "category": "..." } } } ``` > **Verify mutations:** If your API key lacks the required scope (`user.user-notification:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `UserNotification` **Response fields** (`UserNotification`) See [UserNotification](#usernotification) in Models. **Response example:** ```json { "path": "string", "id": "string", "source": { "universe": "string" }, "payload": { "type": "TYPE_UNSPECIFIED", "messageId": "string", "parameters": "...", "joinExperience": { "launchData": "..." }, "analyticsData": { "category": "..." } } } ``` **Rate Limits:** perApiKeyOwner: 4000/second, perOauth2Authorization: 4000/second **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/users/{USER_ID}/notifications" \ -H "Content-Type: application/json" \ -d '{ "path": "string", "id": "string", "source": { "universe": "string" }, "payload": { "type": "TYPE_UNSPECIFIED", "messageId": "string", "parameters": "...", "joinExperience": { "launchData": "..." }, "analyticsData": { "category": "..." } } }' ``` ### GET `/cloud/v2/users/{user_id}/operations/{operation_id}` [BETA] Get User Thumbnail Generation Operation Retrieves the status of the operation to [generate a user thumbnail](https://create.roblox.com/docs/cloud/reference/features/users#Cloud_GenerateUserThumbnail). **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `user_id` | path | `string` | Yes | The user ID. | | `operation_id` | path | `string` | Yes | The operation ID. | **Responses:** - `200`: OK → `OCV2.Operations.Operation` **Response fields** (`OCV2.Operations.Operation`) See [OCV2.Operations.Operation](#ocv2-operations-operation) in Models. **Response example:** ```json { "path": "string", "metadata": { "@type": "string" }, "done": false, "error": { "code": 0, "message": "string", "details": [ "..." ] }, "response": { "@type": "string" } } ``` **Rate Limits:** perApiKeyOwner: 1000/minute, perOauth2Authorization: 10/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/users/{USER_ID}/operations/{OPERATION_ID}" ``` ### GET `/cloud/v2/users/{user_id}:generateThumbnail` [BETA] Generate User Thumbnail Generates and returns the URL for the user's avatar thumbnail. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `user_id` | path | `string` | Yes | The user ID. | | `size` | query | `integer` | No | Size of the generated thumbnail. The generated thumbnail will have `size * size` dimension. Currently supported values: 48, 50, 60, 75, 100, 110, 150, 180, 352, 420, 720 Default is 420. | | `format` | query | `FORMAT_UNSPECIFIED \| PNG \| JPEG` | No | Specify the format of the generated thumbnail. Default is `PNG`. Possible values: \| Value \| Description \| \| --- \| --- \| \| FORMAT_UNSPECIFIED \| Default UserThumbnail Format -- set to png \| \| PNG \| Generate thumbnail in `.png` format \| \| JPEG \| Generate thumbnail in `.jpg` format \| Valid values: `FORMAT_UNSPECIFIED`, `PNG`, `JPEG` | | `shape` | query | `SHAPE_UNSPECIFIED \| ROUND \| SQUARE` | No | Specify the shape of the thumbnail. Default is `ROUND` (circular). Possible values: \| Value \| Description \| \| --- \| --- \| \| SHAPE_UNSPECIFIED \| Default UserThumbnail Shape -- set to round \| \| ROUND \| Generate thumbnail as a circle. \| \| SQUARE \| Generate thumbnail as a rectangle. \| Valid values: `SHAPE_UNSPECIFIED`, `ROUND`, `SQUARE` | **Responses:** - `200`: OK → `Operation` **Response fields** (`Operation`) See [Operation](#operation) in Models. **Response example:** ```json { "path": "string", "metadata": { "@type": "string" }, "done": false, "error": { "code": 0, "message": "string", "details": [ "..." ] }, "response": { "@type": "string" } } ``` **Rate Limits:** perApiKeyOwner: 1000/minute, perOauth2Authorization: 10/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/users/{USER_ID}:generateThumbnail" ``` ### GET `/creator-configs-public-api/v1/configs/universes/{universeId}/repositories/{repository}` [BETA] Gets published config values without metadata. Returns the latest published config values without metadata and decorators. This response can be used as a direct copy-and-paste to the draft update endpoints. **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `universe:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `string` | Yes | | | `repository` | path | `any` | Yes | InExperienceConfig RecommendationServicesConfig DataStoresConfig ExtendedServicesConfig LeaderboardsConfig ExperienceUserConfig | **Responses:** - `200`: OK → `ConfigRepositoryValues` - `400`: Bad Request → `ActionResult` - `401`: Unauthorized → `ActionResult` - `404`: Not Found → `ActionResult` **Response fields** (`ConfigRepositoryValues`) See [ConfigRepositoryValues](#configrepositoryvalues) in Models. **Response example:** ```json { "metadata": "...", "entries": "...", "conditionalRules": "..." } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/creator-configs-public-api/v1/configs/universes/{UNIVERSEID}/repositories/{REPOSITORY}" ``` ### GET `/creator-configs-public-api/v1/configs/universes/{universeId}/repositories/{repository}/draft` [BETA] Gets the current draft. Returns the current draft changes for the configuration repository. **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `universe:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `string` | Yes | | | `repository` | path | `any` | Yes | InExperienceConfig RecommendationServicesConfig DataStoresConfig ExtendedServicesConfig LeaderboardsConfig ExperienceUserConfig | **Responses:** - `200`: OK → `ConfigRepositoryDraft` - `400`: Bad Request → `ActionResult` - `401`: Unauthorized → `ActionResult` - `404`: Not Found → `ActionResult` **Response fields** (`ConfigRepositoryDraft`) See [ConfigRepositoryDraft](#configrepositorydraft) in Models. **Response example:** ```json { "draftHash": "string", "entries": "...", "conditionalRules": "..." } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/creator-configs-public-api/v1/configs/universes/{UNIVERSEID}/repositories/{REPOSITORY}/draft" ``` ### PATCH `/creator-configs-public-api/v1/configs/universes/{universeId}/repositories/{repository}/draft` [BETA] Partially updates the draft. Updates the draft treating the payload as a partial changeset. If draftHash is provided, this call will fail if the hash doesn't match. A key not included will not be changed. To indicate deletion, set key to null. To discard a change, set key to its currently published value. When `conditionalRules` is omitted or empty (`{}`), the CMS update omits the conditional-rules field so existing published rules remain at publish time; send a non-empty `conditionalRules` object to merge rule changes. Entry count and per-value size limits are enforced here via CreatorConfigsPublicApi.Utils.DraftValidationHelper; further schema validation is performed by Config Management. **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `universe:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `string` | Yes | | | `repository` | path | `any` | Yes | InExperienceConfig RecommendationServicesConfig DataStoresConfig ExtendedServicesConfig LeaderboardsConfig ExperienceUserConfig | **Request Body:** `application/json-patch+json` — Type: `any` > **Verify mutations:** If your API key lacks the required scope (`universe:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `DraftHashResponse` - `400`: Bad Request → `ActionResult` - `401`: Unauthorized → `ActionResult` - `404`: Not Found → `ActionResult` **Response fields** (`DraftHashResponse`) See [DraftHashResponse](#drafthashresponse) in Models. **Response example:** ```json { "draftHash": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Rate Limits:** perApiKeyOwner: 50/minute, perOauth2Authorization: 50/minute **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/creator-configs-public-api/v1/configs/universes/{UNIVERSEID}/repositories/{REPOSITORY}/draft" \ -H "Content-Type: application/json" \ -d '"..."' ``` ### DELETE `/creator-configs-public-api/v1/configs/universes/{universeId}/repositories/{repository}/draft` [BETA] Resets (deletes) the current draft. Resets the current draft area. If draftHash is provided, this call will fail if the hash doesn't match. **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `universe:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `string` | Yes | | | `repository` | path | `any` | Yes | InExperienceConfig RecommendationServicesConfig DataStoresConfig ExtendedServicesConfig LeaderboardsConfig ExperienceUserConfig | **Request Body:** `application/json-patch+json` — Type: `any` > **Verify mutations:** If your API key lacks the required scope (`universe:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `DraftHashResponse` - `400`: Bad Request → `ActionResult` - `401`: Unauthorized → `ActionResult` - `404`: Not Found → `ActionResult` **Response fields** (`DraftHashResponse`) See [DraftHashResponse](#drafthashresponse) in Models. **Response example:** ```json { "draftHash": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Rate Limits:** perApiKeyOwner: 50/minute, perOauth2Authorization: 50/minute **Example:** ```bash curl -X DELETE -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/creator-configs-public-api/v1/configs/universes/{UNIVERSEID}/repositories/{REPOSITORY}/draft" \ -H "Content-Type: application/json" \ -d '"..."' ``` ### PUT `/creator-configs-public-api/v1/configs/universes/{universeId}/repositories/{repository}/draft:overwrite` [BETA] Overwrites the entire draft with the request payload. Full overwrite of current draft. The payload is the expected final state after publish (not a merge with the draft or published config from the client’s perspective). The service aligns that intent with CMS validation by emitting explicit deletions for published keys and conditional branches omitted from the body. If a key is not included, it is interpreted as removed. When `conditionalRules` is included, `conditionalRules.rules` is authoritative for rule definitions: any conditional rule that exists on the latest published configuration but is omitted from `rules` is removed (same as sending an empty RPN rule). When `conditionalRules` is omitted, all published conditional rules are removed; entries must not use conditional branches or `.RBX.conditional.*` keys unless you send `conditionalRules`. If draftHash is provided, this call will fail if the hash doesn't match. Entry count and per-value size limits are enforced here via CreatorConfigsPublicApi.Utils.DraftValidationHelper; further schema validation is performed by Config Management. **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `universe:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `string` | Yes | | | `repository` | path | `any` | Yes | InExperienceConfig RecommendationServicesConfig DataStoresConfig ExtendedServicesConfig LeaderboardsConfig ExperienceUserConfig | **Request Body:** `application/json-patch+json` — Type: `any` > **Verify mutations:** If your API key lacks the required scope (`universe:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `DraftHashResponse` - `400`: Bad Request → `ActionResult` - `401`: Unauthorized → `ActionResult` - `404`: Not Found → `ActionResult` **Response fields** (`DraftHashResponse`) See [DraftHashResponse](#drafthashresponse) in Models. **Response example:** ```json { "draftHash": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Rate Limits:** perApiKeyOwner: 50/minute, perOauth2Authorization: 50/minute **Example:** ```bash curl -X PUT -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/creator-configs-public-api/v1/configs/universes/{UNIVERSEID}/repositories/{REPOSITORY}/draft:overwrite" \ -H "Content-Type: application/json" \ -d '"..."' ``` ### GET `/creator-configs-public-api/v1/configs/universes/{universeId}/repositories/{repository}/full` [BETA] Gets full config with metadata. Returns the full config including metadata (version, descriptions, last modified/accessed times). **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `universe:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `string` | Yes | | | `repository` | path | `any` | Yes | InExperienceConfig RecommendationServicesConfig DataStoresConfig ExtendedServicesConfig LeaderboardsConfig ExperienceUserConfig | **Responses:** - `200`: OK → `ConfigRepositoryFull` - `400`: Bad Request → `ActionResult` - `401`: Unauthorized → `ActionResult` - `404`: Not Found → `ActionResult` **Response fields** (`ConfigRepositoryFull`) See [ConfigRepositoryFull](#configrepositoryfull) in Models. **Response example:** ```json { "entries": "...", "metadata": "...", "conditionalRules": "..." } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/creator-configs-public-api/v1/configs/universes/{UNIVERSEID}/repositories/{REPOSITORY}/full" ``` ### POST `/creator-configs-public-api/v1/configs/universes/{universeId}/repositories/{repository}/publish` [BETA] Publishes draft changes. Applies the current draft to the published config. Requires a draft hash and optional message and deployment strategy. **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `universe:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `string` | Yes | | | `repository` | path | `any` | Yes | InExperienceConfig RecommendationServicesConfig DataStoresConfig ExtendedServicesConfig LeaderboardsConfig ExperienceUserConfig | **Request Body:** `application/json-patch+json` — Type: `any` > **Verify mutations:** If your API key lacks the required scope (`universe:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `PublishDraftResponse` - `400`: Bad Request → `ActionResult` - `401`: Unauthorized → `ActionResult` - `404`: Not Found → `ActionResult` **Response fields** (`PublishDraftResponse`) See [PublishDraftResponse](#publishdraftresponse) in Models. **Response example:** ```json { "configVersion": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Rate Limits:** perApiKeyOwner: 10/minute, perOauth2Authorization: 10/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/creator-configs-public-api/v1/configs/universes/{UNIVERSEID}/repositories/{REPOSITORY}/publish" \ -H "Content-Type: application/json" \ -d '"..."' ``` ### GET `/creator-configs-public-api/v1/configs/universes/{universeId}/repositories/{repository}/revisions` [BETA] Lists revision history. Returns the list of previous revisions to the repository. Supports optional start/end time and pagination. **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `universe:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `string` | Yes | | | `repository` | path | `any` | Yes | InExperienceConfig RecommendationServicesConfig DataStoresConfig ExtendedServicesConfig LeaderboardsConfig ExperienceUserConfig | | `startTime` | query | `string` | No | Optional start time for filtering revisions. | | `endTime` | query | `string` | No | Optional end time for filtering revisions. | | `MaxPageSize` | query | `string` | No | Maximum page size. | | `Skip` | query | `string` | No | Number of items to skip. | | `SearchKey` | query | `string` | No | Search key to filter by. | | `SortOrder` | query | `string` | No | Sort order (e.g., "SORT_ORDER_DESCENDING", "SORT_ORDER_ASCENDING"). | | `SortKey` | query | `string` | No | Sort key (e.g., "SORT_KEY_LAST_MODIFIED_TIME"). | **Responses:** - `200`: OK → `ListRevisionsResponse` - `400`: Bad Request → `ActionResult` - `401`: Unauthorized → `ActionResult` - `404`: Not Found → `ActionResult` **Response fields** (`ListRevisionsResponse`) See [ListRevisionsResponse](#listrevisionsresponse) in Models. **Response example:** ```json { "revisions": [ { "revisionId": "...", "version": "...", "time": "...", "publishedBy": "...", "message": "...", "deploymentResult": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/creator-configs-public-api/v1/configs/universes/{UNIVERSEID}/repositories/{REPOSITORY}/revisions" ``` ### POST `/creator-configs-public-api/v1/configs/universes/{universeId}/repositories/{repository}/revisions/{revisionId}/restore` [BETA] Stages a revert to a previous revision. Attempts to restore configs to the provided revision. This will clear the staging and stage a revert commit. You must call publish for the revert to take effect. **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `universe:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `string` | Yes | | | `repository` | path | `any` | Yes | InExperienceConfig RecommendationServicesConfig DataStoresConfig ExtendedServicesConfig LeaderboardsConfig ExperienceUserConfig | | `revisionId` | path | `string` | Yes | | > **Verify mutations:** If your API key lacks the required scope (`universe:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `DraftHashResponse` - `400`: Bad Request → `ActionResult` - `401`: Unauthorized → `ActionResult` - `404`: Not Found → `ActionResult` **Response fields** (`DraftHashResponse`) See [DraftHashResponse](#drafthashresponse) in Models. **Response example:** ```json { "draftHash": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Rate Limits:** perApiKeyOwner: 10/minute, perOauth2Authorization: 10/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/creator-configs-public-api/v1/configs/universes/{UNIVERSEID}/repositories/{REPOSITORY}/revisions/{REVISIONID}/restore" ``` ### GET `/datastores/v1/universes/{universeId}/standard-datastores` [BETA] List data stores in an experience Returns a list of an experience's data stores. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe-datastores.control:list` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The identifier of the experience with data stores that you want to access. You can find your experience's universe ID on Creator Hub. | | `cursor` | query | `string` | No | Provide to request the next set of data. | | `limit` | query | `integer` | No | The maximum number of items to return. Each call only reads one partition, so it can return fewer than the given value when running out of objectives on one partition. | | `prefix` | query | `string` | No | Provide to return only data stores with this prefix. | **Responses:** - `200`: → `object` **Response fields** (`object`) | Property | Type | Required | Description | |----------|------|----------|-------------| | `data` | `OCV1.DataStores.DataStore[]` | No | An array of data stores in the target experience. | | `nextPageCursor` | `string` | No | Indicates that there is more data available in the requested result set. | **Response example:** ```json { "data": [ { "name": "...", "createdTime": "..." } ], "nextPageCursor": "string" } ``` **Rate Limits:** perApiKeyOwner: 5000/minute **Rate Limit Note:** See [Throttling](/docs/en-us/cloud/guides/data-stores/throttling.md). **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/datastores/v1/universes/{UNIVERSEID}/standard-datastores" ``` ### GET `/datastores/v1/universes/{universeId}/standard-datastores/datastore/entries` [BETA] List entries Returns a list of entry keys within a data store. Entries marked deleted with a tombstone version are still included in the response if they have yet to be permanently deleted. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe-datastores.objects:list` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The identifier of the experience with data stores that you want to access. You can find your experience's universe ID on Creator Hub. | | `datastoreName` | query | `string` | No | The name of the data store. | | `scope` | query | `string` | No | The value is `global` by default. See [Scopes](/docs/en-us/cloud-services/data-stores.md#scopes). | | `allScopes` | query | `boolean` | No | Set to true to return keys from all scopes. | | `prefix` | query | `string` | No | Provide to return only keys with this prefix. | | `cursor` | query | `string` | No | Provide to request the next set of data. | | `limit` | query | `integer` | No | The maximum number of items to return. Each call only reads one partition, so it can return fewer than the given value when running out of objectives on one partition. | **Responses:** - `200`: → `object` **Response fields** (`object`) | Property | Type | Required | Description | |----------|------|----------|-------------| | `keys` | `string[]` | No | An array of entry keys within the target data store. | | `nextPageCursor` | `string` | No | Indicates that there is more data available in the requested result set. | **Response example:** ```json { "keys": [ "string" ], "nextPageCursor": "string" } ``` **Rate Limits:** perApiKeyOwner: 5000/minute **Rate Limit Note:** See [Throttling](/docs/en-us/cloud/guides/data-stores/throttling.md). **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/datastores/v1/universes/{UNIVERSEID}/standard-datastores/datastore/entries" ``` ### GET `/datastores/v1/universes/{universeId}/standard-datastores/datastore/entries/entry` [BETA] Get entry. Returns the value and metadata associated with an entry. Entries marked deleted with a tombstone version will return 404 Not Found. Metadata can be found in the response headers like the following: ```text content-md5: zuYxEhwuySMv0i8CitXImw== roblox-entry-version: 08D9E6A3F2188CFF.0000000001.08D9E6A3F2188CFF.01 roblox-entry-created-time: 2022-02-02T23:30:06.5388799+00:00 roblox-entry-version-created-time: 2022-02-02T23:30:06.5388799+00:00 roblox-entry-attributes: { "myAttribute": "myValue" } roblox-entry-userids: [1, 2, 3] ``` | Header | Description | |---|---| | `content-md5` | The base64-encoded MD5 checksum of the content. See [Content-MD5](/docs/en-us/cloud/guides/data-stores/request-handling.md#content-md5). | | `roblox-entry-version` | The version of the returned entry. | | `roblox-entry-created-time` | The time at which the entry was created. | | `roblox-entry-version-created-time` | The time at which this particular version was created. | | `roblox-entry-attributes` | Attributes tagged with the entry. Serialized JSON map object. | | `roblox-entry-userids` | Comma-separated list of Roblox user IDs tagged with the entry. | **Auth:** API Key (`x-api-key` header) **Scopes:** `universe-datastores.objects:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The identifier of the experience with data stores that you want to access. You can find your experience's universe ID on Creator Hub. | | `datastoreName` | query | `string` | No | The name of the data store. | | `entryKey` | query | `string` | No | The key identifying the entry. | | `scope` | query | `string` | No | The value is `global` by default. See [Scopes](/docs/en-us/cloud-services/data-stores.md#scopes). | **Responses:** - `200`: Successfully retrieved the entry. → `object` - `204`: The key is marked as deleted. **Rate Limits:** perApiKeyOwner: 5000/minute **Rate Limit Note:** See [Throttling](/docs/en-us/cloud/guides/data-stores/throttling.md). **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/datastores/v1/universes/{UNIVERSEID}/standard-datastores/datastore/entries/entry" ``` ### POST `/datastores/v1/universes/{universeId}/standard-datastores/datastore/entries/entry` [BETA] Set entry. Sets the value, metadata and user IDs associated with an entry. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe-datastores.objects:update`, `universe-datastores.objects:create`, `universe-datastores.control:create` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The identifier of the experience with data stores that you want to access. You can find your experience's universe ID on Creator Hub. | | `datastoreName` | query | `string` | No | The name of the data store. | | `entryKey` | query | `string` | No | The key identifying the entry. | | `matchVersion` | query | `string` | No | Provide to update only if the current version matches this. | | `exclusiveCreate` | query | `boolean` | No | Create the entry only if it does not exist. | | `scope` | query | `string` | No | The value is `global` by default. See [Scopes](/docs/en-us/cloud-services/data-stores.md#scopes). | | `roblox-entry-attributes` | header | `string` | No | Attributes to be associated with new version of the entry. Serialized by JSON map objects. If not provided, existing attributes are cleared. | | `roblox-entry-userids` | header | `string` | No | Comma-separated list of Roblox user IDs tagged with the entry. If not provided, existing user IDs are cleared. | | `content-md5` | header | `string` | No | The base64-encoded MD5 checksum of the content. See [Content-MD5](/docs/en-us/cloud/guides/data-stores/request-handling.md#content-md5). | **Request Body:** `application/json` — Type: `string` **Responses:** - `200`: → `EntryVersion` **Response fields** (`EntryVersion`) See [EntryVersion](#entryversion) in Models. **Response example:** ```json { "version": "string", "deleted": false, "contentLength": 0, "createdTime": "2024-01-01T00:00:00Z", "objectCreatedTime": "2024-01-01T00:00:00Z" } ``` **Rate Limits:** perApiKeyOwner: 5000/minute **Rate Limit Note:** See [Throttling](/docs/en-us/cloud/guides/data-stores/throttling.md). **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/datastores/v1/universes/{UNIVERSEID}/standard-datastores/datastore/entries/entry" \ -H "Content-Type: application/json" \ -d '"string"' ``` ### DELETE `/datastores/v1/universes/{universeId}/standard-datastores/datastore/entries/entry` [BETA] Delete entry. Marks the entry as deleted by creating a tombstone version. Entries are deleted permanently after 30 days. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe-datastores.objects:delete` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The identifier of the experience with data stores that you want to access. You can find your experience's universe ID on Creator Hub. | | `datastoreName` | query | `string` | No | The name of the data store. | | `entryKey` | query | `string` | No | The key identifying the entry. | | `scope` | query | `string` | No | The value is `global` by default. See [Scopes](/docs/en-us/cloud-services/data-stores.md#scopes). | **Responses:** - `204`: The entry is deleted. **Rate Limits:** perApiKeyOwner: 5000/minute **Rate Limit Note:** See [Throttling](/docs/en-us/cloud/guides/data-stores/throttling.md). **Example:** ```bash curl -X DELETE -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/datastores/v1/universes/{UNIVERSEID}/standard-datastores/datastore/entries/entry" ``` ### POST `/datastores/v1/universes/{universeId}/standard-datastores/datastore/entries/entry/increment` [BETA] Increment entry Increments the value for an entry by a given amount, or create a new entry with that amount. Returns the entry and metadata. Metadata can be found in the response headers like the following: ```text content-md5: zuYxEhwuySMv0i8CitXImw== roblox-entry-version: 08D9E6A3F2188CFF.0000000001.08D9E6A3F2188CFF.01 roblox-entry-created-time: 2022-02-02T23:30:06.5388799+00:00 roblox-entry-version-created-time: 2022-02-02T23:30:06.5388799+00:00 roblox-entry-attributes: { "myAttribute": "myValue" } roblox-entry-userids: [1, 2, 3] ``` | Header | Description | |---|---| | `content-md5` | The base64-encoded MD5 checksum of the content. See [Content-MD5](/docs/en-us/cloud/guides/data-stores/request-handling.md#content-md5). | | `roblox-entry-version` | The version of the returned entry. | | `roblox-entry-created-time` | The time at which the entry was created. | | `roblox-entry-version-created-time` | The time at which this particular version was created. | | `roblox-entry-attributes` | Attributes tagged with the entry. Serialized JSON map object. | | `roblox-entry-userids` | Comma-separated list of Roblox user IDs tagged with the entry. | **Auth:** API Key (`x-api-key` header) **Scopes:** `universe-datastores.objects:update`, `universe-datastores.objects:create`, `universe-datastores.control:create` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The identifier of the experience with data stores that you want to access. You can find your experience's universe ID on Creator Hub. | | `datastoreName` | query | `string` | No | The name of the data store. | | `entryKey` | query | `string` | No | The key identifying the entry. | | `incrementBy` | query | `integer` | No | The amount by which the entry should be incremented, or the starting value if it doesn't exist. | | `scope` | query | `string` | No | The value is `global` by default. See [Scopes](/docs/en-us/cloud-services/data-stores.md#scopes). | | `roblox-entry-attributes` | header | `string` | No | Attributes to be associated with new version of the entry. Serialized by JSON map objects. If not provided, existing attributes are cleared. | | `roblox-entry-userids` | header | `string` | No | A comma-separated list of Roblox user IDs that the entry is tagged with. If not provided, existing user IDs are cleared. | **Responses:** - `200`: Returns the latest version of the entry after it has been incremented. → `object` - `204`: The key is marked as deleted. **Rate Limits:** perApiKeyOwner: 5000/minute **Rate Limit Note:** See [Throttling](/docs/en-us/cloud/guides/data-stores/throttling.md). **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/datastores/v1/universes/{UNIVERSEID}/standard-datastores/datastore/entries/entry/increment" ``` ### GET `/datastores/v1/universes/{universeId}/standard-datastores/datastore/entries/entry/versions` [BETA] List entry versions Returns a list of versions for an entry. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe-datastores.versions:list` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The identifier of the experience with data stores that you want to access. You can find your experience's universe ID on Creator Hub. | | `datastoreName` | query | `string` | No | The name of the data store. | | `entryKey` | query | `string` | No | The key identifying the entry. | | `scope` | query | `string` | No | The value is `global` by default. See [Scopes](/docs/en-us/cloud-services/data-stores.md#scopes). | | `cursor` | query | `string` | No | Provide to request the next set of data. | | `startTime` | query | `string` | No | Provide to not include versions earlier than this timestamp. | | `endTime` | query | `string` | No | Provide to not include versions later than this timestamp. | | `sortOrder` | query | `string` | No | Either `Ascending` (earlier versions first) or `Descending` (later versions first). | | `limit` | query | `integer` | No | The maximum number of items to return. Each call only reads one partition, so it can return fewer than the given value when running out of objectives on one partition. | **Responses:** - `200`: → `EntryVersion` - `400`: Invalid request / Invalid file content. - `403`: Publish not allowed on place. - `404`: The experience or data store was not found. - `429`: Too Many Requests. **Response fields** (`EntryVersion`) See [EntryVersion](#entryversion) in Models. **Response example:** ```json { "version": "string", "deleted": false, "contentLength": 0, "createdTime": "2024-01-01T00:00:00Z", "objectCreatedTime": "2024-01-01T00:00:00Z" } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 5000/minute **Rate Limit Note:** See [Throttling](/docs/en-us/cloud/guides/data-stores/throttling.md). **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/datastores/v1/universes/{UNIVERSEID}/standard-datastores/datastore/entries/entry/versions" ``` ### GET `/datastores/v1/universes/{universeId}/standard-datastores/datastore/entries/entry/versions/version` [BETA] Get entry version. Returns the value and metadata of a specific version of an entry. Metadata can be found in the response headers like the following: ```text content-md5: zuYxEhwuySMv0i8CitXImw== roblox-entry-version: 08D9E6A3F2188CFF.0000000001.08D9E6A3F2188CFF.01 roblox-entry-created-time: 2022-02-02T23:30:06.5388799+00:00 roblox-entry-version-created-time: 2022-02-02T23:30:06.5388799+00:00 roblox-entry-attributes: { "myAttribute": "myValue" } roblox-entry-userids: [1, 2, 3] ``` | Header | Description | |---|---| | `content-md5` | The base64-encoded MD5 checksum of the content. See [Content-MD5](/docs/en-us/cloud/guides/data-stores/request-handling.md#content-md5). | | `roblox-entry-version` | The version of the returned entry. | | `roblox-entry-created-time` | The time at which the entry was created. | | `roblox-entry-version-created-time` | The time at which this particular version was created. | | `roblox-entry-attributes` | Attributes tagged with the entry. Serialized JSON map object. | | `roblox-entry-userids` | Comma-separated list of Roblox user IDs tagged with the entry. | **Auth:** API Key (`x-api-key` header) **Scopes:** `universe-datastores.versions:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The identifier of the experience with data stores that you want to access. You can find your experience's universe ID on Creator Hub. | | `datastoreName` | query | `string` | No | The name of the data store. | | `entryKey` | query | `string` | No | The key identifying the entry. | | `versionId` | query | `string` | No | The version to inspect. | | `scope` | query | `string` | No | The value is `global` by default. See [Scopes](/docs/en-us/cloud-services/data-stores.md#scopes). | **Responses:** - `200`: Successfully retrieved the entry. → `object` **Rate Limits:** perApiKeyOwner: 5000/minute **Rate Limit Note:** See [Throttling](/docs/en-us/cloud/guides/data-stores/throttling.md). **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/datastores/v1/universes/{UNIVERSEID}/standard-datastores/datastore/entries/entry/versions/version" ``` ### POST `/developer-products/v2/universes/{universeId}/developer-products` [BETA] Create developer product Creates a new developer product with the provided configuration details. **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `developer-product:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe ID. | **Request Body:** `multipart/form-data` — Type: `object` | Property | Type | Required | Description | |----------|------|----------|-------------| | `name` | `string` | Yes | The name of the developer product. | | `description` | `string` | No | The description of the developer product. | | `isForSale` | `boolean` | No | Whether the developer product should be on sale. | | `price` | `integer` | No | The default price of the developer product. | | `imageFile` | `string` | No | The thumbnail image file to be uploaded. | | `isRegionalPricingEnabled` | `boolean` | No | Whether regional pricing should be enabled for the developer product. | | `isManagedPricingEnabled` | `boolean` | No | Whether managed pricing should be enabled for the developer product. | **Request example:** ```json { "name": "string", "description": "string", "isForSale": false, "price": 0, "imageFile": "string", "isRegionalPricingEnabled": false } ``` > **Verify mutations:** If your API key lacks the required scope (`developer-product:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: Success → `DeveloperProductConfigV2` - `400`: Bad Request → `ErrorResponse` - `401`: Unauthorized → `ErrorResponse` - `403`: Forbidden → `ErrorResponse` - `404`: Not Found → `ErrorResponse` **Response fields** (`DeveloperProductConfigV2`) See [DeveloperProductConfigV2](#developerproductconfigv2) in Models. **Response example:** ```json { "productId": 0, "name": "string", "description": "string", "iconImageAssetId": 0, "universeId": 0, "isForSale": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 3/second, perOauth2Authorization: 3/second **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/developer-products/v2/universes/{UNIVERSEID}/developer-products" \ -F "name=string" \ -F "description=string" \ -F "isForSale=false" \ -F "price=0" \ -F "imageFile=@file.bin;type=application/octet-stream" \ -F "isRegionalPricingEnabled=false" \ -F "isManagedPricingEnabled=false" ``` ### GET `/developer-products/v2/universes/{universeId}/developer-products/creator` [BETA] List developer products by universe with configuration details **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `developer-product:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe ID. | | `pageSize` | query | `integer` | No | The number of results to return. Defaults to 50. | | `pageToken` | query | `string` | No | The cursor token for pagination. | **Responses:** - `200`: Success → `ListDeveloperProductConfigsV2Response` - `400`: Bad Request → `ErrorResponse` - `401`: Unauthorized → `ErrorResponse` - `403`: Forbidden → `ErrorResponse` - `404`: Not Found → `ErrorResponse` **Response fields** (`ListDeveloperProductConfigsV2Response`) See [ListDeveloperProductConfigsV2Response](#listdeveloperproductconfigsv2response) in Models. **Response example:** ```json { "developerProducts": [ { "productId": "...", "name": "...", "description": "...", "iconImageAssetId": "...", "universeId": "...", "isForSale": "..." } ], "nextPageToken": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 10/second, perOauth2Authorization: 10/second **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/developer-products/v2/universes/{UNIVERSEID}/developer-products/creator" ``` ### PATCH `/developer-products/v2/universes/{universeId}/developer-products/{productId}` [BETA] Update developer product Updates a developer product with the provided configuration details. Note that only fields provided in the request will be updated. **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `developer-product:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe ID. | | `productId` | path | `integer` | Yes | The product ID of the developer product. | **Request Body:** `multipart/form-data` — Type: `object` | Property | Type | Required | Description | |----------|------|----------|-------------| | `name` | `string` | No | The name of the developer product. | | `description` | `string` | No | The description of the developer product. | | `isForSale` | `boolean` | No | Whether the developer product should be on sale. | | `price` | `integer` | No | The default price of the developer product. | | `imageFile` | `string` | No | The thumbnail image file to be uploaded. | | `isRegionalPricingEnabled` | `boolean` | No | Whether regional pricing should be enabled for the developer product. | | `storePageEnabled` | `boolean` | No | Whether the developer product should be available for purchase on the external store page. | | `isManagedPricingEnabled` | `boolean` | No | Whether managed pricing should be enabled for the developer product. | **Request example:** ```json { "name": "string", "description": "string", "isForSale": false, "price": 0, "imageFile": "string", "isRegionalPricingEnabled": false } ``` > **Verify mutations:** If your API key lacks the required scope (`developer-product:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `204`: No Content - `400`: Bad Request → `ErrorResponse` - `401`: Unauthorized → `ErrorResponse` - `403`: Forbidden → `ErrorResponse` - `404`: Not Found → `ErrorResponse` - `409`: Conflict → `ErrorResponse` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 3/second, perOauth2Authorization: 3/second **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/developer-products/v2/universes/{UNIVERSEID}/developer-products/{PRODUCTID}" \ -F "name=string" \ -F "description=string" \ -F "isForSale=false" \ -F "price=0" \ -F "imageFile=@file.bin;type=application/octet-stream" \ -F "isRegionalPricingEnabled=false" \ -F "storePageEnabled=false" \ -F "isManagedPricingEnabled=false" ``` ### GET `/developer-products/v2/universes/{universeId}/developer-products/{productId}/creator` [BETA] Get developer product with configuration details **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `developer-product:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe ID. | | `productId` | path | `integer` | Yes | The product ID of the developer product. | **Responses:** - `200`: Success → `DeveloperProductConfigV2` - `401`: Unauthorized → `ErrorResponse` - `403`: Forbidden → `ErrorResponse` - `404`: Not Found → `ErrorResponse` **Response fields** (`DeveloperProductConfigV2`) See [DeveloperProductConfigV2](#developerproductconfigv2) in Models. **Response example:** ```json { "productId": 0, "name": "string", "description": "string", "iconImageAssetId": 0, "universeId": 0, "isForSale": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 10/second, perOauth2Authorization: 10/second **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/developer-products/v2/universes/{UNIVERSEID}/developer-products/{PRODUCTID}/creator" ``` ### POST `/game-passes/v1/universes/{universeId}/game-passes` [BETA] Create game pass Creates a new game pass with the provided configuration details. **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `game-pass:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe ID. | **Request Body:** `multipart/form-data` — Type: `object` | Property | Type | Required | Description | |----------|------|----------|-------------| | `name` | `string` | Yes | The name of the game pass. | | `description` | `string` | No | The description of the game pass. | | `imageFile` | `string` | No | The thumbnail image file to be uploaded. | | `isForSale` | `boolean` | No | Whether the game pass should be on sale. | | `price` | `integer` | No | The default price of the game pass. | | `isRegionalPricingEnabled` | `boolean` | No | Whether regional pricing should be enabled for the game pass. | | `isManagedPricingEnabled` | `boolean` | No | Whether managed pricing should be enabled for the game pass. | **Request example:** ```json { "name": "string", "description": "string", "imageFile": "string", "isForSale": false, "price": 0, "isRegionalPricingEnabled": false } ``` > **Verify mutations:** If your API key lacks the required scope (`game-pass:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: Success → `GamePassConfigV2` - `400`: Bad Request → `GamePasses.ErrorResponse` - `401`: Unauthorized → `GamePasses.ErrorResponse` - `403`: Forbidden → `GamePasses.ErrorResponse` - `404`: Not Found → `GamePasses.ErrorResponse` **Response fields** (`GamePassConfigV2`) See [GamePassConfigV2](#gamepassconfigv2) in Models. **Response example:** ```json { "gamePassId": 0, "name": "string", "description": "string", "isForSale": false, "iconAssetId": 0, "createdTimestamp": "2024-01-01T00:00:00Z" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 5/second, perOauth2Authorization: 5/second **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/game-passes/v1/universes/{UNIVERSEID}/game-passes" \ -F "name=string" \ -F "description=string" \ -F "imageFile=@file.bin;type=application/octet-stream" \ -F "isForSale=false" \ -F "price=0" \ -F "isRegionalPricingEnabled=false" \ -F "isManagedPricingEnabled=false" ``` ### GET `/game-passes/v1/universes/{universeId}/game-passes/creator` [BETA] List game passes by universe with configuration details **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `game-pass:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe ID. | | `pageSize` | query | `integer` | No | The number of results to return. Defaults to 50 if not provided. | | `pageToken` | query | `string` | No | The cursor token for pagination. | **Responses:** - `200`: Success → `ListGamePassConfigsByUniverseResponse` - `400`: Bad Request → `GamePasses.ErrorResponse` - `401`: Unauthorized → `GamePasses.ErrorResponse` - `403`: Forbidden → `GamePasses.ErrorResponse` - `404`: Not Found → `GamePasses.ErrorResponse` **Response fields** (`ListGamePassConfigsByUniverseResponse`) See [ListGamePassConfigsByUniverseResponse](#listgamepassconfigsbyuniverseresponse) in Models. **Response example:** ```json { "gamePasses": [ { "gamePassId": "...", "name": "...", "description": "...", "isForSale": "...", "iconAssetId": "...", "createdTimestamp": "..." } ], "nextPageToken": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 10/second, perOauth2Authorization: 10/second **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/game-passes/v1/universes/{UNIVERSEID}/game-passes/creator" ``` ### PATCH `/game-passes/v1/universes/{universeId}/game-passes/{gamePassId}` [BETA] Update game pass Updates a game pass with the provided configuration details. Note that only fields provided in the request will be updated. **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `game-pass:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe ID. | | `gamePassId` | path | `integer` | Yes | The game pass ID. | **Request Body:** `multipart/form-data` — Type: `object` | Property | Type | Required | Description | |----------|------|----------|-------------| | `name` | `string` | No | The name of the game pass. | | `description` | `string` | No | The description of the game pass. | | `file` | `string` | No | The thumbnail image file to be uploaded. Alias for imageFile - if both are provided, imageFile will be used. | | `imageFile` | `string` | No | The thumbnail image file to be uploaded. | | `isForSale` | `boolean` | No | Whether the game pass should be on sale. | | `price` | `integer` | No | The default price of the game pass. | | `isRegionalPricingEnabled` | `boolean` | No | Whether regional pricing should be enabled for the game pass. | | `isManagedPricingEnabled` | `boolean` | No | Whether managed pricing should be enabled for the game pass. | **Request example:** ```json { "name": "string", "description": "string", "file": "string", "imageFile": "string", "isForSale": false, "price": 0 } ``` > **Verify mutations:** If your API key lacks the required scope (`game-pass:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `204`: No Content - `400`: Bad Request → `GamePasses.ErrorResponse` - `401`: Unauthorized → `GamePasses.ErrorResponse` - `403`: Forbidden → `GamePasses.ErrorResponse` - `404`: Not Found → `GamePasses.ErrorResponse` - `409`: Conflict → `GamePasses.ErrorResponse` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 5/second, perOauth2Authorization: 5/second **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/game-passes/v1/universes/{UNIVERSEID}/game-passes/{GAMEPASSID}" \ -F "name=string" \ -F "description=string" \ -F "file=@file.bin;type=application/octet-stream" \ -F "imageFile=@file.bin;type=application/octet-stream" \ -F "isForSale=false" \ -F "price=0" \ -F "isRegionalPricingEnabled=false" \ -F "isManagedPricingEnabled=false" ``` ### GET `/game-passes/v1/universes/{universeId}/game-passes/{gamePassId}/creator` [BETA] Get game pass with configuration details **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `game-pass:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe ID. | | `gamePassId` | path | `integer` | Yes | The game pass ID. | **Responses:** - `200`: Success → `GamePassConfigV2` - `401`: Unauthorized → `GamePasses.ErrorResponse` - `403`: Forbidden → `GamePasses.ErrorResponse` - `404`: Not Found → `GamePasses.ErrorResponse` **Response fields** (`GamePassConfigV2`) See [GamePassConfigV2](#gamepassconfigv2) in Models. **Response example:** ```json { "gamePassId": 0, "name": "string", "description": "string", "isForSale": false, "iconAssetId": 0, "createdTimestamp": "2024-01-01T00:00:00Z" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 10/second, perOauth2Authorization: 10/second **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/game-passes/v1/universes/{UNIVERSEID}/game-passes/{GAMEPASSID}/creator" ``` ### PATCH `/legacy-badges/v1/badges/{badgeId}` Updates badge configuration. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-universe.badge:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `badgeId` | path | `integer` | Yes | The badge Id. | **Request Body:** `application/json` — Type: `Roblox.Badges.Api.UpdateBadgeRequest` See [Roblox.Badges.Api.UpdateBadgeRequest](#roblox-badges-api-updatebadgerequest) in Models. **Request example:** ```json { "name": "string", "description": "string", "enabled": false } ``` > **Verify mutations:** If your API key lacks the required scope (`legacy-universe.badge:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 6: Text moderated. 14: Invalid badge name. 15: Invalid badge description. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: You do not have permission to manage this badge. - `404`: 1: Badge is invalid or does not exist. 3: The game is invalid or does not exist. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-badges/v1/badges/{BADGEID}" \ -H "Content-Type: application/json" \ -d '{ "name": "string", "description": "string", "enabled": false }' ``` ### POST `/legacy-badges/v1/universes/{universeId}/badges` Creates a new badge. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-universe.badge:manage-and-spend-robux` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The ID of the universe to create the badge for. | **Request Body:** `multipart/form-data` — Type: `object` | Property | Type | Required | Description | |----------|------|----------|-------------| | `name` | `string` | No | The badge name. | | `description` | `string` | No | The badge description. | | `paymentSourceType` | `1 \| 2` | No | Whether or not to pay for the badge with user funds, or group funds. ['User' = 1, 'Group' = 2] | | `files` | `string` | No | The badge icon. | | `expectedCost` | `integer` | No | User expected cost of a badge. | | `isActive` | `boolean` | No | Whether or not the badge should be created in the active state. | **Request example:** ```json { "name": "string", "description": "string", "paymentSourceType": 1, "files": "string", "expectedCost": 0, "isActive": false } ``` > **Verify mutations:** If your API key lacks the required scope (`legacy-universe.badge:manage-and-spend-robux`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.Web.Responses.Badges.BadgeResponseV2` - `400`: 11: The badge icon is invalid. 14: Invalid badge name. 15: Invalid badge description. 16: Payment source is invalid. 18: Expected badge cost is different from the actual badge cost. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 6: Text moderated. 12: You do not have permission to manage this game's badges. 17: Insufficient funds. - `404`: 3: The game is invalid or does not exist. - `429`: 13: Too many requests, try again later. **Response fields** (`Roblox.Web.Responses.Badges.BadgeResponseV2`) See [Roblox.Web.Responses.Badges.BadgeResponseV2](#roblox-web-responses-badges-badgeresponsev2) in Models. **Response example:** ```json { "id": 0, "name": "string", "description": "string", "displayName": "string", "displayDescription": "string", "enabled": false } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-badges/v1/universes/{UNIVERSEID}/badges" \ -F "name=string" \ -F "description=string" \ -F "paymentSourceType=1" \ -F "files=@file.bin;type=application/octet-stream" \ -F "expectedCost=0" \ -F "isActive=false" ``` ### GET `/legacy-develop/v1/places/{placeId}/teamcreate/active_session/members` List of users in the active Team Create session **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-team-collaboration:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `placeId` | path | `integer` | Yes | The place Id. | | `limit` | query | `10 \| 25 \| 50 \| 100` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Web.Responses.Users.SkinnyUserResponse_` - `400`: 1: The universe is invalid. 5: The place is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: 2: Not authorized to perform this action. 4: TeamCreate on universe is disabled. - `404`: 0: An unknown error occurred. - `500`: 6: Multiple active sessions in a Team Create place. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Web.Responses.Users.SkinnyUserResponse_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Web.Responses.Users.SkinnyUserResponse_](#roblox-web-webapi-models-apiarrayresponse-roblox-web-responses-users-skinnyuserresponse-) in Models. **Response example:** ```json { "data": [ { "id": "...", "name": "...", "displayName": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-develop/v1/places/{PLACEID}/teamcreate/active_session/members" ``` ### GET `/legacy-develop/v1/universes/multiget/permissions` Returns an array of granted and declined permissions related to the universes with the ids in ids for the authenticated user. If a universe can not be found for a given ID (such as -1) it will be skipped. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-universe:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `ids` | query | `integer[]` | Yes | The universe ids. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Api.Develop.Models.UniverseIdPermissionsModel_` - `400`: 8: No universe IDs sent to get. 9: Too many universe IDs sent to get, the limit is: - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Api.Develop.Models.UniverseIdPermissionsModel_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Api.Develop.Models.UniverseIdPermissionsModel_](#roblox-web-webapi-models-apiarrayresponse-roblox-api-develop-models-universeidpermissionsmodel-) in Models. **Response example:** ```json { "data": [ { "universeId": "...", "canManage": "...", "canCloudEdit": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-develop/v1/universes/multiget/permissions?ids={VALUE}" ``` ### GET `/legacy-develop/v1/universes/multiget/teamcreate` Gets TeamCreate settings for multiple universes specified by Ids **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-team-collaboration:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `ids` | query | `integer[]` | Yes | The universe Ids. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Api.Develop.Models.UniverseTeamCreateSettingsModel_` - `400`: Roblox.Api.Develop.ResponseEnums.TeamCreateErrors.TooManyUniverseIdsSent - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Api.Develop.Models.UniverseTeamCreateSettingsModel_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Api.Develop.Models.UniverseTeamCreateSettingsModel_](#roblox-web-webapi-models-apiarrayresponse-roblox-api-develop-models-universeteamcreatesettingsmodel-) in Models. **Response example:** ```json { "data": [ { "id": "...", "isEnabled": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-develop/v1/universes/multiget/teamcreate?ids={VALUE}" ``` ### POST `/legacy-develop/v1/universes/{universeId}/activate` Activates a universes. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-universe:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe id. | > **Verify mutations:** If your API key lacks the required scope (`legacy-universe:manage`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: The universe does not exist. 2: This universe does not have a root place. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 3: You are not authorized to configure this universe. 6: The root place for this universe is under review and can not be activated. 7: Creator already has the maximum number of places active. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-develop/v1/universes/{UNIVERSEID}/activate" ``` ### POST `/legacy-develop/v1/universes/{universeId}/deactivate` Deactivates a universe. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-universe:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe id. | > **Verify mutations:** If your API key lacks the required scope (`legacy-universe:manage`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: The universe does not exist. 2: This universe does not have a root place. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 3: You are not authorized to configure this universe. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-develop/v1/universes/{UNIVERSEID}/deactivate" ``` ### GET `/legacy-develop/v1/universes/{universeId}/permissions` Returns list of granted and declined permissions related to the universe with the id universeId for authenticated user **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-universe:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe id. | **Responses:** - `200`: OK → `Roblox.Api.Develop.Models.UniversePermissionsModel` - `400`: 1: The universe does not exist. - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Api.Develop.Models.UniversePermissionsModel`) See [Roblox.Api.Develop.Models.UniversePermissionsModel](#roblox-api-develop-models-universepermissionsmodel) in Models. **Response example:** ```json { "canManage": false, "canCloudEdit": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-develop/v1/universes/{UNIVERSEID}/permissions" ``` ### GET `/legacy-develop/v1/universes/{universeId}/teamcreate` Gets TeamCreate settings for an Roblox.Platform.Universes.IUniverse. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-team-collaboration:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe Id. | **Responses:** - `200`: OK → `Roblox.Api.Develop.Models.Response.TeamCreateSettingsResponse` - `400`: Roblox.Api.Develop.ResponseEnums.TeamCreateErrors.InvalidUniverse - `401`: 0: Authorization has been denied for this request. - `403`: Roblox.Api.Develop.ResponseEnums.TeamCreateErrors.Unauthorized **Response fields** (`Roblox.Api.Develop.Models.Response.TeamCreateSettingsResponse`) See [Roblox.Api.Develop.Models.Response.TeamCreateSettingsResponse](#roblox-api-develop-models-response-teamcreatesettingsresponse) in Models. **Response example:** ```json { "isEnabled": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-develop/v1/universes/{UNIVERSEID}/teamcreate" ``` ### PATCH `/legacy-develop/v1/universes/{universeId}/teamcreate` Edit team create settings for a universe. Enables, or disables team create for a universe. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-team-collaboration:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe Id. | > **Verify mutations:** If your API key lacks the required scope (`legacy-team-collaboration:manage`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: Roblox.Api.Develop.ResponseEnums.TeamCreateErrors.InvalidUniverse - `401`: 0: Authorization has been denied for this request. - `403`: Roblox.Api.Develop.ResponseEnums.TeamCreateErrors.Unauthorized 0: Token Validation Failed **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-develop/v1/universes/{UNIVERSEID}/teamcreate" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### DELETE `/legacy-develop/v1/universes/{universeId}/teamcreate/memberships` Removes a user from a TeamCreate permissions list. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-team-collaboration:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe Id. | > **Verify mutations:** If your API key lacks the required scope (`legacy-team-collaboration:manage`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: Roblox.Api.Develop.ResponseEnums.TeamCreateErrors.InvalidUniverse OR Roblox.Api.Develop.ResponseEnums.TeamCreateErrors.InvalidUser - `401`: 0: Authorization has been denied for this request. - `403`: Roblox.Api.Develop.ResponseEnums.TeamCreateErrors.TeamCreateDisabled 0: Token Validation Failed **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X DELETE -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-develop/v1/universes/{UNIVERSEID}/teamcreate/memberships" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### GET `/legacy-develop/v1/user/groups/canmanage` Gets a list of Groups that a user can manage. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-group:manage` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Api.Develop.Models.GroupModel_` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Api.Develop.Models.GroupModel_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Api.Develop.Models.GroupModel_](#roblox-web-webapi-models-apiarrayresponse-roblox-api-develop-models-groupmodel-) in Models. **Response example:** ```json { "data": [ { "id": "...", "name": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-develop/v1/user/groups/canmanage" ``` ### DELETE `/legacy-develop/v2/teamtest/{placeId}` Close a game instance that is being used for team testing **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-team-collaboration:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `placeId` | path | `integer` | Yes | The Id of the place we are setting the metadata for. | | `gameId` | query | `string` | Yes | the Guid of the game instance System.Guid | > **Verify mutations:** If your API key lacks the required scope (`legacy-team-collaboration:manage`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X DELETE -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-develop/v2/teamtest/{PLACEID}?gameId={VALUE}" ``` ### GET `/legacy-followings/v1/users/{userId}/universes` Gets all the followings between a user with userId and universes **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-universe.following:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | | **Responses:** - `200`: OK → `Roblox.Followings.Api.Models.UserFollowingUniverseResponse[]` - `401`: 0: Authorization has been denied for this request. - `403`: User is not authorized for this action. **Response fields** (`Roblox.Followings.Api.Models.UserFollowingUniverseResponse[]`) See [Roblox.Followings.Api.Models.UserFollowingUniverseResponse](#roblox-followings-api-models-userfollowinguniverseresponse) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-followings/v1/users/{USERID}/universes" ``` ### POST `/legacy-followings/v1/users/{userId}/universes/{universeId}` Creates the following between a user with userId and universe with universeId **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-universe.following:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | | | `universeId` | path | `integer` | Yes | | > **Verify mutations:** If your API key lacks the required scope (`legacy-universe.following:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.Followings.Api.Models.UserFollowingUniverseResponse` - `400`: The user has reached the limit of number of followed universes. - `401`: 0: Authorization has been denied for this request. - `403`: User is not authorized for this action. 0: Token Validation Failed **Response fields** (`Roblox.Followings.Api.Models.UserFollowingUniverseResponse`) See [Roblox.Followings.Api.Models.UserFollowingUniverseResponse](#roblox-followings-api-models-userfollowinguniverseresponse) in Models. **Response example:** ```json { "universeId": 0, "userId": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-followings/v1/users/{USERID}/universes/{UNIVERSEID}" ``` ### DELETE `/legacy-followings/v1/users/{userId}/universes/{universeId}` Deletes the following between a user with userId and universe with universeId **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-universe.following:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | | | `universeId` | path | `integer` | Yes | | > **Verify mutations:** If your API key lacks the required scope (`legacy-universe.following:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.Followings.Api.Models.UserFollowingUniverseResponse` - `401`: 0: Authorization has been denied for this request. - `403`: User is not authorized for this action. 0: Token Validation Failed **Response fields** (`Roblox.Followings.Api.Models.UserFollowingUniverseResponse`) See [Roblox.Followings.Api.Models.UserFollowingUniverseResponse](#roblox-followings-api-models-userfollowinguniverseresponse) in Models. **Response example:** ```json { "universeId": 0, "userId": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X DELETE -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-followings/v1/users/{USERID}/universes/{UNIVERSEID}" ``` ### GET `/legacy-followings/v1/users/{userId}/universes/{universeId}/status` Gets the status of a following relationship between a user and a universe. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-universe.following:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | | | `universeId` | path | `integer` | Yes | | **Responses:** - `200`: OK → `Roblox.Followings.Api.Models.UserFollowingUniverseStatusResponse` - `401`: 0: Authorization has been denied for this request. - `403`: User is not authorized for this action. **Response fields** (`Roblox.Followings.Api.Models.UserFollowingUniverseStatusResponse`) See [Roblox.Followings.Api.Models.UserFollowingUniverseStatusResponse](#roblox-followings-api-models-userfollowinguniversestatusresponse) in Models. **Response example:** ```json { "UniverseId": 0, "UserId": 0, "CanFollow": false, "IsFollowing": false, "FollowingCountByType": 0, "FollowingLimitByType": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-followings/v1/users/{USERID}/universes/{UNIVERSEID}/status" ``` ### GET `/legacy-followings/v2/users/{userId}/universes` Gets all universes followed by a user. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-universe.following:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user ID. | **Responses:** - `200`: OK → `Roblox.Followings.Api.Models.FollowsByTypeResponse` - `401`: 0: Authorization has been denied for this request. - `403`: User is not authorized for this action. **Response fields** (`Roblox.Followings.Api.Models.FollowsByTypeResponse`) See [Roblox.Followings.Api.Models.FollowsByTypeResponse](#roblox-followings-api-models-followsbytyperesponse) in Models. **Response example:** ```json { "followerType": 0, "followerId": 0, "sourceType": 0, "followedSources": "..." } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-followings/v2/users/{USERID}/universes" ``` ### PATCH `/legacy-game-internationalization/v1/badges/{badgeId}/description/language-codes/{languageCode}` Update localized description of a badge **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-badge:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `badgeId` | path | `integer` | Yes | The badge id | | `languageCode` | path | `string` | Yes | The language code of the description to update | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.UpdateBadgeDescriptionRequest` See [Roblox.GameInternationalization.Api.UpdateBadgeDescriptionRequest](#roblox-gameinternationalization-api-updatebadgedescriptionrequest) in Models. **Request example:** ```json { "description": "string" } ``` > **Verify mutations:** If your API key lacks the required scope (`legacy-badge:manage`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.UpdateBadgeDescriptionResponse` - `400`: 13: Request body can't be null 19: New name is null or whitespaces or new name/description is too long 20: New name or description is moderated 22: Invalid language code 26: You can't update translations for source language 53: Language is not supported for the game. 62: Invalid game badge id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.UpdateBadgeDescriptionResponse`) See [Roblox.GameInternationalization.Api.UpdateBadgeDescriptionResponse](#roblox-gameinternationalization-api-updatebadgedescriptionresponse) in Models. **Response example:** ```json { "description": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-game-internationalization/v1/badges/{BADGEID}/description/language-codes/{LANGUAGECODE}" \ -H "Content-Type: application/json" \ -d '{ "description": "string" }' ``` ### GET `/legacy-game-internationalization/v1/badges/{badgeId}/icons` Get all icons for a badge **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-badge:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `badgeId` | path | `integer` | Yes | The id of the badge | | `width` | query | `integer` | No | The width of the icon to request | | `height` | query | `integer` | No | The height of the icon to request | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.GetBadgeIconResponse_` - `400`: 52: Image dimensions are invalid 62: Invalid game badge id - `401`: 0: Authorization has been denied for this request. - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.GetBadgeIconResponse_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.GetBadgeIconResponse_](#roblox-web-webapi-models-apiarrayresponse-roblox-gameinternationalization-api-getbadgeiconresponse-) in Models. **Response example:** ```json { "data": [ { "imageId": "...", "imageUrl": "...", "state": "...", "languageCode": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-game-internationalization/v1/badges/{BADGEID}/icons" ``` ### POST `/legacy-game-internationalization/v1/badges/{badgeId}/icons/language-codes/{languageCode}` Update a badge's icon **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-badge:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `badgeId` | path | `integer` | Yes | The id of the badge | | `languageCode` | path | `string` | Yes | The language code of this icon to update | > **Verify mutations:** If your API key lacks the required scope (`legacy-badge:manage`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 22: Invalid language code 26: You can't update translations for source language 45: File uploaded does not match known image format 46: File not present in request 53: Language is not supported for the game. 62: Invalid game badge id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `429`: 24: Too many attempts.Please try again later. - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-game-internationalization/v1/badges/{BADGEID}/icons/language-codes/{LANGUAGECODE}" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### DELETE `/legacy-game-internationalization/v1/badges/{badgeId}/icons/language-codes/{languageCode}` Delete a localized icon from a badge **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-badge:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `badgeId` | path | `integer` | Yes | The id of the badge | | `languageCode` | path | `string` | Yes | The language code of the localized icon to delete | > **Verify mutations:** If your API key lacks the required scope (`legacy-badge:manage`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 22: Invalid language code 23: You can't delete translations for source language 53: Language is not supported for the game. 62: Invalid game badge id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X DELETE -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-game-internationalization/v1/badges/{BADGEID}/icons/language-codes/{LANGUAGECODE}" ``` ### GET `/legacy-game-internationalization/v1/badges/{badgeId}/name-description` **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-badge:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `badgeId` | path | `integer` | Yes | | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.NameDescription_` - `400`: 62: Invalid game badge id - `401`: 0: Authorization has been denied for this request. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.NameDescription_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.NameDescription_](#roblox-web-webapi-models-apiarrayresponse-roblox-gameinternationalization-api-namedescription-) in Models. **Response example:** ```json { "data": [ { "name": "...", "description": "...", "updateType": "...", "languageCode": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-game-internationalization/v1/badges/{BADGEID}/name-description" ``` ### PATCH `/legacy-game-internationalization/v1/badges/{badgeId}/name-description/language-codes/{languageCode}` Update localized name and description of a badge **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-badge:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `badgeId` | path | `integer` | Yes | The badge id | | `languageCode` | path | `string` | Yes | The language code of the name and description to Update | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.UpdateBadgeNameDescriptionRequest` See [Roblox.GameInternationalization.Api.UpdateBadgeNameDescriptionRequest](#roblox-gameinternationalization-api-updatebadgenamedescriptionrequest) in Models. **Request example:** ```json { "name": "string", "description": "string" } ``` > **Verify mutations:** If your API key lacks the required scope (`legacy-badge:manage`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.UpdateBadgeNameDescriptionResponse` - `400`: 13: Request body can't be null 19: New name is null or whitespaces or new name/description is too long 20: New name or description is moderated 22: Invalid language code 26: You can't update translations for source language 53: Language is not supported for the game. 62: Invalid game badge id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.UpdateBadgeNameDescriptionResponse`) See [Roblox.GameInternationalization.Api.UpdateBadgeNameDescriptionResponse](#roblox-gameinternationalization-api-updatebadgenamedescriptionresponse) in Models. **Response example:** ```json { "name": "string", "description": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-game-internationalization/v1/badges/{BADGEID}/name-description/language-codes/{LANGUAGECODE}" \ -H "Content-Type: application/json" \ -d '{ "name": "string", "description": "string" }' ``` ### DELETE `/legacy-game-internationalization/v1/badges/{badgeId}/name-description/language-codes/{languageCode}` Delete localized name and description of a badge **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-badge:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `badgeId` | path | `integer` | Yes | The badge id | | `languageCode` | path | `string` | Yes | The language code of the name and description to delete | > **Verify mutations:** If your API key lacks the required scope (`legacy-badge:manage`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 22: Invalid language code 23: You can't delete translations for source language 53: Language is not supported for the game. 62: Invalid game badge id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X DELETE -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-game-internationalization/v1/badges/{BADGEID}/name-description/language-codes/{LANGUAGECODE}" ``` ### PATCH `/legacy-game-internationalization/v1/badges/{badgeId}/name/language-codes/{languageCode}` Update localized name of a badge **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-badge:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `badgeId` | path | `integer` | Yes | The badge id | | `languageCode` | path | `string` | Yes | The language code of the name to update | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.UpdateBadgeNameRequest` See [Roblox.GameInternationalization.Api.UpdateBadgeNameRequest](#roblox-gameinternationalization-api-updatebadgenamerequest) in Models. **Request example:** ```json { "name": "string" } ``` > **Verify mutations:** If your API key lacks the required scope (`legacy-badge:manage`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.UpdateBadgeNameResponse` - `400`: 13: Request body can't be null 19: New name is null or whitespaces or new name/description is too long 20: New name or description is moderated 22: Invalid language code 26: You can't update translations for source language 53: Language is not supported for the game. 62: Invalid game badge id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.UpdateBadgeNameResponse`) See [Roblox.GameInternationalization.Api.UpdateBadgeNameResponse](#roblox-gameinternationalization-api-updatebadgenameresponse) in Models. **Response example:** ```json { "name": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-game-internationalization/v1/badges/{BADGEID}/name/language-codes/{LANGUAGECODE}" \ -H "Content-Type: application/json" \ -d '{ "name": "string" }' ``` ### PATCH `/legacy-game-internationalization/v1/developer-products/{developerProductId}/description/language-codes/{languageCode}` Update localized description of a developer product **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-developer-product:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `developerProductId` | path | `integer` | Yes | The developer product id | | `languageCode` | path | `string` | Yes | The language code of the description to update | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.UpdateDeveloperProductDescriptionRequest` See [Roblox.GameInternationalization.Api.UpdateDeveloperProductDescriptionRequest](#roblox-gameinternationalization-api-updatedeveloperproductdescriptionrequest) in Models. **Request example:** ```json { "description": "string" } ``` > **Verify mutations:** If your API key lacks the required scope (`legacy-developer-product:manage`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.UpdateDeveloperProductDescriptionResponse` - `400`: 13: Request body can't be null 19: New name is null or whitespaces or new name/description is too long 20: New name or description is moderated 22: Invalid language code 26: You can't update translations for source language 53: Language is not supported for the game. 70: Invalid developer product id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.UpdateDeveloperProductDescriptionResponse`) See [Roblox.GameInternationalization.Api.UpdateDeveloperProductDescriptionResponse](#roblox-gameinternationalization-api-updatedeveloperproductdescriptionresponse) in Models. **Response example:** ```json { "description": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-game-internationalization/v1/developer-products/{DEVELOPERPRODUCTID}/description/language-codes/{LANGUAGECODE}" \ -H "Content-Type: application/json" \ -d '{ "description": "string" }' ``` ### GET `/legacy-game-internationalization/v1/developer-products/{developerProductId}/icons` Get all icons for a developer product **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-developer-product:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `developerProductId` | path | `integer` | Yes | The id of the developer product | | `width` | query | `integer` | No | The width of the icon to request | | `height` | query | `integer` | No | The height of the icon to request | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.GetDeveloperProductIconResponse_` - `400`: 52: Image dimensions are invalid 70: Invalid developer product id - `401`: 0: Authorization has been denied for this request. - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.GetDeveloperProductIconResponse_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.GetDeveloperProductIconResponse_](#roblox-web-webapi-models-apiarrayresponse-roblox-gameinternationalization-api-getdeveloperproducticonresponse-) in Models. **Response example:** ```json { "data": [ { "imageId": "...", "imageUrl": "...", "state": "...", "languageCode": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-game-internationalization/v1/developer-products/{DEVELOPERPRODUCTID}/icons" ``` ### POST `/legacy-game-internationalization/v1/developer-products/{developerProductId}/icons/language-codes/{languageCode}` Update a developer product's icon **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-developer-product:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `developerProductId` | path | `integer` | Yes | The id of the developer product | | `languageCode` | path | `string` | Yes | The language code of this icon to update | > **Verify mutations:** If your API key lacks the required scope (`legacy-developer-product:manage`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 22: Invalid language code 26: You can't update translations for source language 45: File uploaded does not match known image format 46: File not present in request 53: Language is not supported for the game. 70: Invalid developer product id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `429`: 24: Too many attempts.Please try again later. - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-game-internationalization/v1/developer-products/{DEVELOPERPRODUCTID}/icons/language-codes/{LANGUAGECODE}" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### DELETE `/legacy-game-internationalization/v1/developer-products/{developerProductId}/icons/language-codes/{languageCode}` Delete a localized icon from a developer product **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-developer-product:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `developerProductId` | path | `integer` | Yes | The id of the developer product | | `languageCode` | path | `string` | Yes | The language code of the localized icon to delete | > **Verify mutations:** If your API key lacks the required scope (`legacy-developer-product:manage`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 22: Invalid language code 23: You can't delete translations for source language 53: Language is not supported for the game. 70: Invalid developer product id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X DELETE -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-game-internationalization/v1/developer-products/{DEVELOPERPRODUCTID}/icons/language-codes/{LANGUAGECODE}" ``` ### GET `/legacy-game-internationalization/v1/developer-products/{developerProductId}/name-description` Get all names and descriptions of a developer product **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-developer-product:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `developerProductId` | path | `integer` | Yes | The developer product Id | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.NameDescription_` - `400`: 70: Invalid developer product id - `401`: 0: Authorization has been denied for this request. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.NameDescription_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.NameDescription_](#roblox-web-webapi-models-apiarrayresponse-roblox-gameinternationalization-api-namedescription-) in Models. **Response example:** ```json { "data": [ { "name": "...", "description": "...", "updateType": "...", "languageCode": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-game-internationalization/v1/developer-products/{DEVELOPERPRODUCTID}/name-description" ``` ### PATCH `/legacy-game-internationalization/v1/developer-products/{developerProductId}/name-description/language-codes/{languageCode}` Update localized name and description of a developer product **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-developer-product:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `developerProductId` | path | `integer` | Yes | The developer product id | | `languageCode` | path | `string` | Yes | The language code of the name and description to Update | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.UpdateDeveloperProductNameDescriptionRequest` See [Roblox.GameInternationalization.Api.UpdateDeveloperProductNameDescriptionRequest](#roblox-gameinternationalization-api-updatedeveloperproductnamedescriptionrequest) in Models. **Request example:** ```json { "name": "string", "description": "string" } ``` > **Verify mutations:** If your API key lacks the required scope (`legacy-developer-product:manage`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.UpdateDeveloperProductNameDescriptionResponse` - `400`: 13: Request body can't be null 19: New name is null or whitespaces or new name/description is too long 20: New name or description is moderated 22: Invalid language code 26: You can't update translations for source language 53: Language is not supported for the game. 70: Invalid developer product id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.UpdateDeveloperProductNameDescriptionResponse`) See [Roblox.GameInternationalization.Api.UpdateDeveloperProductNameDescriptionResponse](#roblox-gameinternationalization-api-updatedeveloperproductnamedescriptionresponse) in Models. **Response example:** ```json { "name": "string", "description": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-game-internationalization/v1/developer-products/{DEVELOPERPRODUCTID}/name-description/language-codes/{LANGUAGECODE}" \ -H "Content-Type: application/json" \ -d '{ "name": "string", "description": "string" }' ``` ### DELETE `/legacy-game-internationalization/v1/developer-products/{developerProductId}/name-description/language-codes/{languageCode}` Delete localized name and description of a developer product **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-developer-product:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `developerProductId` | path | `integer` | Yes | The developer product id | | `languageCode` | path | `string` | Yes | The language code of the name and description to delete | > **Verify mutations:** If your API key lacks the required scope (`legacy-developer-product:manage`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 22: Invalid language code 23: You can't delete translations for source language 53: Language is not supported for the game. 70: Invalid developer product id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X DELETE -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-game-internationalization/v1/developer-products/{DEVELOPERPRODUCTID}/name-description/language-codes/{LANGUAGECODE}" ``` ### PATCH `/legacy-game-internationalization/v1/developer-products/{developerProductId}/name/language-codes/{languageCode}` Update localized name of a developer product **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-developer-product:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `developerProductId` | path | `integer` | Yes | The developer product id | | `languageCode` | path | `string` | Yes | The language code of the name to update | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.UpdateDeveloperProductNameRequest` See [Roblox.GameInternationalization.Api.UpdateDeveloperProductNameRequest](#roblox-gameinternationalization-api-updatedeveloperproductnamerequest) in Models. **Request example:** ```json { "name": "string" } ``` > **Verify mutations:** If your API key lacks the required scope (`legacy-developer-product:manage`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.UpdateDeveloperProductNameResponse` - `400`: 13: Request body can't be null 19: New name is null or whitespaces or new name/description is too long 20: New name or description is moderated 22: Invalid language code 26: You can't update translations for source language 53: Language is not supported for the game. 70: Invalid developer product id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.UpdateDeveloperProductNameResponse`) See [Roblox.GameInternationalization.Api.UpdateDeveloperProductNameResponse](#roblox-gameinternationalization-api-updatedeveloperproductnameresponse) in Models. **Response example:** ```json { "name": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-game-internationalization/v1/developer-products/{DEVELOPERPRODUCTID}/name/language-codes/{LANGUAGECODE}" \ -H "Content-Type: application/json" \ -d '{ "name": "string" }' ``` ### GET `/legacy-game-internationalization/v1/game-icon/games/{gameId}` Get all icons for a game **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-universe:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The id of the game | | `width` | query | `integer` | No | The width of the icon to request | | `height` | query | `integer` | No | The height of the icon to request | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.GetGameIconResponse_` - `400`: 14: Invalid game id 52: Image dimensions are invalid - `401`: 0: Authorization has been denied for this request. - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.GetGameIconResponse_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.GetGameIconResponse_](#roblox-web-webapi-models-apiarrayresponse-roblox-gameinternationalization-api-getgameiconresponse-) in Models. **Response example:** ```json { "data": [ { "imageId": "...", "imageUrl": "...", "state": "...", "languageCode": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-game-internationalization/v1/game-icon/games/{GAMEID}" ``` ### POST `/legacy-game-internationalization/v1/game-icon/games/{gameId}/language-codes/{languageCode}` Update a game's icon **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-universe:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The id of the game | | `languageCode` | path | `string` | Yes | The language code of this icon to update | > **Verify mutations:** If your API key lacks the required scope (`legacy-universe:manage`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 14: Invalid game id 22: Invalid language code 26: You can't update translations for source language 45: File uploaded does not match known image format 46: File not present in request 53: Language is not supported for the game. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `429`: 24: Too many attempts.Please try again later. - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-game-internationalization/v1/game-icon/games/{GAMEID}/language-codes/{LANGUAGECODE}" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### DELETE `/legacy-game-internationalization/v1/game-icon/games/{gameId}/language-codes/{languageCode}` Delete a localized icon from a game **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-universe:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The id of the game | | `languageCode` | path | `string` | Yes | The language code of the localized icon to delete | > **Verify mutations:** If your API key lacks the required scope (`legacy-universe:manage`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 14: Invalid game id 22: Invalid language code 23: You can't delete translations for source language 53: Language is not supported for the game. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X DELETE -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-game-internationalization/v1/game-icon/games/{GAMEID}/language-codes/{LANGUAGECODE}" ``` ### PATCH `/legacy-game-internationalization/v1/game-passes/{gamePassId}/description/language-codes/{languageCode}` Update localized description of a game pass **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-game-pass:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gamePassId` | path | `integer` | Yes | The game pass id | | `languageCode` | path | `string` | Yes | The language code of description to update | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.UpdateGamePassDescriptionRequest` See [Roblox.GameInternationalization.Api.UpdateGamePassDescriptionRequest](#roblox-gameinternationalization-api-updategamepassdescriptionrequest) in Models. **Request example:** ```json { "description": "string" } ``` > **Verify mutations:** If your API key lacks the required scope (`legacy-game-pass:manage`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.UpdateGamePassDescriptionResponse` - `400`: 13: Request body can't be null 19: New name is null or whitespaces or new name/description is too long 20: New name or description is moderated 22: Invalid language code 26: You can't update translations for source language 53: Language is not supported for the game. 61: Invalid game pass id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.UpdateGamePassDescriptionResponse`) See [Roblox.GameInternationalization.Api.UpdateGamePassDescriptionResponse](#roblox-gameinternationalization-api-updategamepassdescriptionresponse) in Models. **Response example:** ```json { "description": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-game-internationalization/v1/game-passes/{GAMEPASSID}/description/language-codes/{LANGUAGECODE}" \ -H "Content-Type: application/json" \ -d '{ "description": "string" }' ``` ### GET `/legacy-game-internationalization/v1/game-passes/{gamePassId}/icons` Get all icons for a game pass **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-game-pass:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gamePassId` | path | `integer` | Yes | The game pass id | | `width` | query | `integer` | No | The width of the icon to request | | `height` | query | `integer` | No | The height of the icon to request | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.GetGamePassIconResponse_` - `400`: 52: Image dimensions are invalid 61: Invalid game pass id - `401`: 0: Authorization has been denied for this request. - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.GetGamePassIconResponse_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.GetGamePassIconResponse_](#roblox-web-webapi-models-apiarrayresponse-roblox-gameinternationalization-api-getgamepassiconresponse-) in Models. **Response example:** ```json { "data": [ { "imageId": "...", "imageUrl": "...", "state": "...", "languageCode": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-game-internationalization/v1/game-passes/{GAMEPASSID}/icons" ``` ### POST `/legacy-game-internationalization/v1/game-passes/{gamePassId}/icons/language-codes/{languageCode}` Update a game pass's icon **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-game-pass:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gamePassId` | path | `integer` | Yes | The game pass id | | `languageCode` | path | `string` | Yes | The language code of this icon to update | > **Verify mutations:** If your API key lacks the required scope (`legacy-game-pass:manage`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 22: Invalid language code 26: You can't update translations for source language 45: File uploaded does not match known image format 46: File not present in request 53: Language is not supported for the game. 61: Invalid game pass id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `429`: 24: Too many attempts.Please try again later. - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-game-internationalization/v1/game-passes/{GAMEPASSID}/icons/language-codes/{LANGUAGECODE}" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### DELETE `/legacy-game-internationalization/v1/game-passes/{gamePassId}/icons/language-codes/{languageCode}` Delete a localized icon from a game pass **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-game-pass:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gamePassId` | path | `integer` | Yes | The game pass id | | `languageCode` | path | `string` | Yes | The language code of the localized icon to delete | > **Verify mutations:** If your API key lacks the required scope (`legacy-game-pass:manage`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 22: Invalid language code 23: You can't delete translations for source language 53: Language is not supported for the game. 61: Invalid game pass id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X DELETE -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-game-internationalization/v1/game-passes/{GAMEPASSID}/icons/language-codes/{LANGUAGECODE}" ``` ### GET `/legacy-game-internationalization/v1/game-passes/{gamePassId}/name-description` Get all names and descriptions of a game pass **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-game-pass:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gamePassId` | path | `integer` | Yes | The game pass Id | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.NameDescription_` - `400`: 61: Invalid game pass id - `401`: 0: Authorization has been denied for this request. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.NameDescription_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.NameDescription_](#roblox-web-webapi-models-apiarrayresponse-roblox-gameinternationalization-api-namedescription-) in Models. **Response example:** ```json { "data": [ { "name": "...", "description": "...", "updateType": "...", "languageCode": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-game-internationalization/v1/game-passes/{GAMEPASSID}/name-description" ``` ### PATCH `/legacy-game-internationalization/v1/game-passes/{gamePassId}/name-description/language-codes/{languageCode}` Update localized name and description of a game pass **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-game-pass:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gamePassId` | path | `integer` | Yes | The game pass id | | `languageCode` | path | `string` | Yes | The language code of the name/description to update | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.UpdateGamePassNameDescriptionRequest` See [Roblox.GameInternationalization.Api.UpdateGamePassNameDescriptionRequest](#roblox-gameinternationalization-api-updategamepassnamedescriptionrequest) in Models. **Request example:** ```json { "name": "string", "description": "string" } ``` > **Verify mutations:** If your API key lacks the required scope (`legacy-game-pass:manage`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.UpdateGamePassNameDescriptionResponse` - `400`: 13: Request body can't be null 19: New name is null or whitespaces or new name/description is too long 20: New name or description is moderated 22: Invalid language code 26: You can't update translations for source language 53: Language is not supported for the game. 61: Invalid game pass id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.UpdateGamePassNameDescriptionResponse`) See [Roblox.GameInternationalization.Api.UpdateGamePassNameDescriptionResponse](#roblox-gameinternationalization-api-updategamepassnamedescriptionresponse) in Models. **Response example:** ```json { "name": "string", "description": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-game-internationalization/v1/game-passes/{GAMEPASSID}/name-description/language-codes/{LANGUAGECODE}" \ -H "Content-Type: application/json" \ -d '{ "name": "string", "description": "string" }' ``` ### DELETE `/legacy-game-internationalization/v1/game-passes/{gamePassId}/name-description/language-codes/{languageCode}` Delete localized name and description of a game pass **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-game-pass:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gamePassId` | path | `integer` | Yes | The game pass id | | `languageCode` | path | `string` | Yes | The language code of the name and description to delete | > **Verify mutations:** If your API key lacks the required scope (`legacy-game-pass:manage`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 22: Invalid language code 23: You can't delete translations for source language 53: Language is not supported for the game. 61: Invalid game pass id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X DELETE -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-game-internationalization/v1/game-passes/{GAMEPASSID}/name-description/language-codes/{LANGUAGECODE}" ``` ### PATCH `/legacy-game-internationalization/v1/game-passes/{gamePassId}/name/language-codes/{languageCode}` Update localized name of a game pass **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-game-pass:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gamePassId` | path | `integer` | Yes | The game pass id | | `languageCode` | path | `string` | Yes | The language code of the name to update | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.UpdateGamePassNameRequest` See [Roblox.GameInternationalization.Api.UpdateGamePassNameRequest](#roblox-gameinternationalization-api-updategamepassnamerequest) in Models. **Request example:** ```json { "name": "string" } ``` > **Verify mutations:** If your API key lacks the required scope (`legacy-game-pass:manage`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.UpdateGamePassNameResponse` - `400`: 13: Request body can't be null 19: New name is null or whitespaces or new name/description is too long 20: New name or description is moderated 22: Invalid language code 26: You can't update translations for source language 53: Language is not supported for the game. 61: Invalid game pass id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.UpdateGamePassNameResponse`) See [Roblox.GameInternationalization.Api.UpdateGamePassNameResponse](#roblox-gameinternationalization-api-updategamepassnameresponse) in Models. **Response example:** ```json { "name": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-game-internationalization/v1/game-passes/{GAMEPASSID}/name/language-codes/{LANGUAGECODE}" \ -H "Content-Type: application/json" \ -d '{ "name": "string" }' ``` ### POST `/legacy-game-internationalization/v1/game-thumbnails/games/{gameId}/language-codes/{languageCode}/alt-text` Updates the game thumbnail alt text. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-universe:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The game identifier. | | `languageCode` | path | `string` | Yes | The language code. | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.UpdateThumbnailAltTextRequest` See [Roblox.GameInternationalization.Api.UpdateThumbnailAltTextRequest](#roblox-gameinternationalization-api-updatethumbnailalttextrequest) in Models. **Request example:** ```json { "thumbnailId": 0, "altText": "string" } ``` > **Verify mutations:** If your API key lacks the required scope (`legacy-universe:manage`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `string` - `400`: 14: Invalid game id 19: New name is null or whitespaces or new name/description is too long 20: New name or description is moderated 22: Invalid language code 26: You can't update translations for source language 45: File uploaded does not match known image format 53: Language is not supported for the game. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `429`: 24: Too many attempts.Please try again later. - `500`: 0: An unknown error occurred. 88: Failed to filter text - `503`: 17: Feature is disabled **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-game-internationalization/v1/game-thumbnails/games/{GAMEID}/language-codes/{LANGUAGECODE}/alt-text" \ -H "Content-Type: application/json" \ -d '{ "thumbnailId": 0, "altText": "string" }' ``` ### POST `/legacy-game-internationalization/v1/game-thumbnails/games/{gameId}/language-codes/{languageCode}/image` Uploads the game thumbnail. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-universe:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The game identifier. | | `languageCode` | path | `string` | Yes | The language code. | > **Verify mutations:** If your API key lacks the required scope (`legacy-universe:manage`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.Models.Response.UploadImageForGameThumbnailResponse` - `400`: 14: Invalid game id 22: Invalid language code 26: You can't update translations for source language 53: Language is not supported for the game. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `429`: 24: Too many attempts.Please try again later. - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.Models.Response.UploadImageForGameThumbnailResponse`) See [Roblox.GameInternationalization.Api.Models.Response.UploadImageForGameThumbnailResponse](#roblox-gameinternationalization-api-models-response-uploadimageforgamethumbnailresponse) in Models. **Response example:** ```json { "mediaAssetId": "string" } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-game-internationalization/v1/game-thumbnails/games/{GAMEID}/language-codes/{LANGUAGECODE}/image" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### POST `/legacy-game-internationalization/v1/game-thumbnails/games/{gameId}/language-codes/{languageCode}/images/order` Orders the specified image Ids for the game thumbnails. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-universe:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The game identifier. | | `languageCode` | path | `string` | Yes | The language code. | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.SortImageIdsRequest` See [Roblox.GameInternationalization.Api.SortImageIdsRequest](#roblox-gameinternationalization-api-sortimageidsrequest) in Models. **Request example:** ```json { "mediaAssetIds": [ 0 ] } ``` > **Verify mutations:** If your API key lacks the required scope (`legacy-universe:manage`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 14: Invalid game id 22: Invalid language code 26: You can't update translations for source language 53: Language is not supported for the game. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-game-internationalization/v1/game-thumbnails/games/{GAMEID}/language-codes/{LANGUAGECODE}/images/order" \ -H "Content-Type: application/json" \ -d '{ "mediaAssetIds": [ 0 ] }' ``` ### DELETE `/legacy-game-internationalization/v1/game-thumbnails/games/{gameId}/language-codes/{languageCode}/images/{imageId}` Deletes the game thumbnail. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-universe:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The game identifier. | | `languageCode` | path | `string` | Yes | The language code. | | `imageId` | path | `integer` | Yes | The image identifier. | > **Verify mutations:** If your API key lacks the required scope (`legacy-universe:manage`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 14: Invalid game id 22: Invalid language code 23: You can't delete translations for source language 53: Language is not supported for the game. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X DELETE -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-game-internationalization/v1/game-thumbnails/games/{GAMEID}/language-codes/{LANGUAGECODE}/images/{IMAGEID}" ``` ### POST `/legacy-game-internationalization/v1/name-description/games/translation-history` Gets the history for name or description in a provided language. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-universe:manage` **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.GetNameDescriptionHistoryV2Request` See [Roblox.GameInternationalization.Api.GetNameDescriptionHistoryV2Request](#roblox-gameinternationalization-api-getnamedescriptionhistoryv2request) in Models. **Request example:** ```json { "contentId": 0, "contentType": "UniverseDisplayNames", "languageCode": "string", "cursor": "string", "count": 0, "sortOrder": "Asc" } ``` > **Verify mutations:** If your API key lacks the required scope (`legacy-universe:manage`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.GetNameDescriptionHistoryResponse` - `400`: 13: Request body can't be null 14: Invalid game id 18: You do not have permission to manage this game 22: Invalid language code 39: Count should be at least 1 and less than 50. 53: Language is not supported for the game. 54: No history available for source data 55: Invalid exclusive start Id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.GetNameDescriptionHistoryResponse`) See [Roblox.GameInternationalization.Api.GetNameDescriptionHistoryResponse](#roblox-gameinternationalization-api-getnamedescriptionhistoryresponse) in Models. **Response example:** ```json { "history": [ { "translationText": "...", "translator": "...", "created": "..." } ], "lastEvaluatedId": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-game-internationalization/v1/name-description/games/translation-history" \ -H "Content-Type: application/json" \ -d '{ "contentId": 0, "contentType": "UniverseDisplayNames", "languageCode": "string", "cursor": "string", "count": 0, "sortOrder": "Asc" }' ``` ### PATCH `/legacy-game-internationalization/v1/name-description/games/{gameId}` Updates a game's name and/or description in multiple languages. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-universe:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The id of the game. | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.UpdateNameDescriptionsRequest` See [Roblox.GameInternationalization.Api.UpdateNameDescriptionsRequest](#roblox-gameinternationalization-api-updatenamedescriptionsrequest) in Models. **Request example:** ```json { "data": [ { "name": "...", "description": "...", "updateType": "...", "languageCode": "..." } ] } ``` > **Verify mutations:** If your API key lacks the required scope (`legacy-universe:manage`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.UpdateNameDescriptionsResponse` - `400`: 14: Invalid game id 19: New name is null or whitespaces or new name/description is too long 20: New name or description is moderated 22: Invalid language code 23: You can't delete translations for source language 26: You can't update translations for source language 53: Language is not supported for the game. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.UpdateNameDescriptionsResponse`) See [Roblox.GameInternationalization.Api.UpdateNameDescriptionsResponse](#roblox-gameinternationalization-api-updatenamedescriptionsresponse) in Models. **Response example:** ```json { "successOperations": [ { "name": "...", "description": "...", "updateType": "...", "languageCode": "..." } ], "failedOperations": [ { "languageCode": "...", "errorCode": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-game-internationalization/v1/name-description/games/{GAMEID}" \ -H "Content-Type: application/json" \ -d '{ "data": [ { "name": "...", "description": "...", "updateType": "...", "languageCode": "..." } ] }' ``` ### PATCH `/legacy-game-internationalization/v1/source-language/games/{gameId}` Sets the source language of a game **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-universe:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | | | `languageCode` | query | `string` | Yes | | > **Verify mutations:** If your API key lacks the required scope (`legacy-universe:manage`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 14: Invalid game id 22: Invalid language code - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `500`: 85: Failed to disable automatic translation status for languages - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-game-internationalization/v1/source-language/games/{GAMEID}?languageCode={VALUE}" ``` ### PATCH `/legacy-game-internationalization/v1/supported-languages/games/{gameId}` Add or remove supported languages for a game. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-universe:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The id of the game. | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.PatchLanguage[]` See [Roblox.GameInternationalization.Api.PatchLanguage](#roblox-gameinternationalization-api-patchlanguage) in Models. > **Verify mutations:** If your API key lacks the required scope (`legacy-universe:manage`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 14: Invalid game id 22: Invalid language code 49: Duplicate language codes are not allowed. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-game-internationalization/v1/supported-languages/games/{GAMEID}" \ -H "Content-Type: application/json" \ -d '[ { "languageCodeType": "Language", "languageCode": "string", "delete": false } ]' ``` ### GET `/legacy-game-internationalization/v1/supported-languages/games/{gameId}/automatic-translation-status` Get the automatic translation status of supported languages for a game. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-universe:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The id of the game. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.LanguageOrLocaleSettings_` - `400`: 14: Invalid game id - `401`: 0: Authorization has been denied for this request. - `403`: 18: You do not have permission to manage this game - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.LanguageOrLocaleSettings_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.LanguageOrLocaleSettings_](#roblox-web-webapi-models-apiarrayresponse-roblox-gameinternationalization-api-languageorlocalesettings-) in Models. **Response example:** ```json { "data": [ { "languageCodeType": "...", "languageCode": "...", "isAutomaticTranslationEnabled": "...", "isImageTranslationEnabled": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-game-internationalization/v1/supported-languages/games/{GAMEID}/automatic-translation-status" ``` ### PATCH `/legacy-game-internationalization/v1/supported-languages/games/{gameId}/languages/{languageCode}/automatic-translation-status` Enable or disable automatic translation for a game and language. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-universe:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The id of the game. | | `languageCode` | path | `string` | Yes | The language to enable or disable for automatic translation. | **Request Body:** `application/json` — Type: `boolean` > **Verify mutations:** If your API key lacks the required scope (`legacy-universe:manage`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.EditAutomaticTranslationStatusForGameAndLanguageResponse` - `400`: 14: Invalid game id 22: Invalid language code 53: Language is not supported for the game. 72: Automatic translation cannot be enabled for game. 75: Automatic translation cannot be enabled for language. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.EditAutomaticTranslationStatusForGameAndLanguageResponse`) See [Roblox.GameInternationalization.Api.EditAutomaticTranslationStatusForGameAndLanguageResponse](#roblox-gameinternationalization-api-editautomatictranslationstatusforgameandlanguageresponse) in Models. **Response example:** ```json { "gameId": 0, "languageCode": "string", "isAutomaticTranslationEnabled": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-game-internationalization/v1/supported-languages/games/{GAMEID}/languages/{LANGUAGECODE}/automatic-translation-status" \ -H "Content-Type: application/json" \ -d 'false' ``` ### PATCH `/legacy-game-internationalization/v1/supported-languages/games/{gameId}/languages/{languageCode}/image-translation-status` Enable or disable image translation for a game and language. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The id of the game. | | `languageCode` | path | `string` | Yes | The language to enable or disable for image translation. | **Request Body:** `application/json` — Type: `boolean` **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.EditImageTranslationStatusForGameAndLanguageResponse` - `400`: 14: Invalid game id 22: Invalid language code 53: Language is not supported for the game. 93: Image translation cannot be enabled for language. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.EditImageTranslationStatusForGameAndLanguageResponse`) See [Roblox.GameInternationalization.Api.EditImageTranslationStatusForGameAndLanguageResponse](#roblox-gameinternationalization-api-editimagetranslationstatusforgameandlanguageresponse) in Models. **Response example:** ```json { "gameId": 0, "languageCode": "string", "isImageTranslationEnabled": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-game-internationalization/v1/supported-languages/games/{GAMEID}/languages/{LANGUAGECODE}/image-translation-status" \ -H "Content-Type: application/json" \ -d 'false' ``` ### PATCH `/legacy-game-internationalization/v1/supported-languages/games/{gameId}/languages/{languageCode}/universe-display-info-automatic-translation-settings` Update the switch which controls if the UniverseDisplayInformation should be automatically translated. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-universe:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The game id. | | `languageCode` | path | `string` | Yes | The language code. | **Request Body:** `application/json` — Type: `boolean` > **Verify mutations:** If your API key lacks the required scope (`legacy-universe:manage`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.UpdateUniverseDisplayInfoAutomaticTranslationSettingsResponse` - `400`: 14: Invalid game id 72: Automatic translation cannot be enabled for game. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `500`: 77: Content localization set settings return error code invalid 79: Invalid content instance settings 80: Invalid quota settings 81: Invalid change agent 82: Failed to update UniverseDisplayInformation content instance auto translation settings **Response fields** (`Roblox.GameInternationalization.Api.UpdateUniverseDisplayInfoAutomaticTranslationSettingsResponse`) See [Roblox.GameInternationalization.Api.UpdateUniverseDisplayInfoAutomaticTranslationSettingsResponse](#roblox-gameinternationalization-api-updateuniversedisplayinfoautomatictranslationsettingsresponse) in Models. **Response example:** ```json { "gameId": 0, "languageCode": "string", "isUniverseDisplayInfoAutomaticTranslationEnabled": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-game-internationalization/v1/supported-languages/games/{GAMEID}/languages/{LANGUAGECODE}/universe-display-info-automatic-translation-settings" \ -H "Content-Type: application/json" \ -d 'false' ``` ### GET `/legacy-game-internationalization/v1/supported-languages/games/{gameId}/universe-display-info-automatic-translation-settings` Get UniverseDisplayInfo automatic translation settings. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-universe:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The game id. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.UniverseDisplayInfoAutomaticTranslationSettings_` - `400`: 14: Invalid game id - `401`: 0: Authorization has been denied for this request. - `403`: 18: You do not have permission to manage this game - `500`: 0: An unknown error occurred. 22: Invalid language code 83: Failed to get UniverseDisplayInformation content instance auto translation settings 84: Count of language code is larger than max batch get size **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.UniverseDisplayInfoAutomaticTranslationSettings_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.UniverseDisplayInfoAutomaticTranslationSettings_](#roblox-web-webapi-models-apiarrayresponse-roblox-gameinternationalization-api-universedisplayinfoautomatictranslationsettings-) in Models. **Response example:** ```json { "data": [ { "languageCode": "...", "isUniverseDisplayInfoAutomaticTranslationEnabled": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-game-internationalization/v1/supported-languages/games/{GAMEID}/universe-display-info-automatic-translation-settings" ``` ### POST `/legacy-groups/v1/groups/policies` Gets group policy info used for compliance. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-group:manage` **Request Body:** `application/json` — Type: `Roblox.Groups.Api.GroupPolicyRequest` See [Roblox.Groups.Api.GroupPolicyRequest](#roblox-groups-api-grouppolicyrequest) in Models. **Request example:** ```json { "groupIds": [ 0 ] } ``` > **Verify mutations:** If your API key lacks the required scope (`legacy-group:manage`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupPoliciesResponse` - `400`: 1: Too many ids in request. 2: Ids could not be parsed from request. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Groups.Api.GroupPoliciesResponse`) See [Roblox.Groups.Api.GroupPoliciesResponse](#roblox-groups-api-grouppoliciesresponse) in Models. **Response example:** ```json { "groups": [ { "canViewGroup": "...", "groupId": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-groups/v1/groups/policies" \ -H "Content-Type: application/json" \ -d '{ "groupIds": [ 0 ] }' ``` ### GET `/legacy-groups/v1/groups/{groupId}/audit-log` Gets the Group's audit log **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-group:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The id of the group the user is in. | | `actionType` | query | `string enum (74 values)` | No | Filter for specific type of action performed Valid values: `DeletePost`, `RemoveMember`, `AcceptJoinRequest`, `DeclineJoinRequest`, `PostStatus`, `ChangeRank`, `BuyAd`, `SendAllyRequest`, `CreateEnemy`, `AcceptAllyRequest`, `DeclineAllyRequest`, `DeleteAlly`, `DeleteEnemy`, `AddGroupPlace`, `RemoveGroupPlace`, `CreateItems`, `ConfigureItems`, `SpendGroupFunds`, `ChangeOwner`, `Delete`, `AdjustCurrencyAmounts`, `Abandon`, `Claim`, `Rename`, `ChangeDescription`, `InviteToClan`, `KickFromClan`, `CancelClanInvite`, `BuyClan`, `CreateGroupAsset`, `UpdateGroupAsset`, `ConfigureGroupAsset`, `RevertGroupAsset`, `CreateGroupDeveloperProduct`, `ConfigureGroupGame`, `CreateGroupDeveloperSubscriptionProduct`, `Lock`, `Unlock`, `CreateGamePass`, `CreateBadge`, `ConfigureBadge`, `SavePlace`, `PublishPlace`, `UpdateRolesetRank`, `UpdateRolesetData`, `BanMember`, `UnbanMember`, `CreateForumCategory`, `UpdateForumCategory`, `ArchiveForumCategory`, `DeleteForumCategory`, `DeleteForumPost`, `DeleteForumComment`, `PinForumPost`, `UnpinForumPost`, `LockForumPost`, `UnlockForumPost`, `CreateRoleset`, `DeleteRoleset`, `CreateCommerceProduct`, `SetCommerceProductActive`, `ArchiveCommerceProduct`, `AcceptCommerceProductBundlingFee`, `SetCommerceProductInactive`, `ConnectMerchant`, `DisconnectMerchant`, `JoinGroup`, `LeaveGroup`, `UpdateGroupIcon`, `UpdateGroupCoverPhoto`, `AssignRole`, `UnassignRole`, `PublishAnnouncement`, `DeleteAnnouncement` | | `userId` | query | `integer` | No | Filter for specific user id | | `limit` | query | `10 \| 25 \| 50 \| 100` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `Asc \| Desc` | No | The order the results are sorted in. Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupAuditLogPageResponse_Roblox.Groups.Api.Models.Response.GroupAuditLogResponseItem_` - `400`: 1: Group is invalid or does not exist. 8: Invalid or missing pagination parameters - `401`: 0: Authorization has been denied for this request. - `403`: 23: Insufficient permissions to complete the request. **Response fields** (`Roblox.Groups.Api.GroupAuditLogPageResponse_Roblox.Groups.Api.Models.Response.GroupAuditLogResponseItem_`) See [Roblox.Groups.Api.GroupAuditLogPageResponse_Roblox.Groups.Api.Models.Response.GroupAuditLogResponseItem_](#roblox-groups-api-groupauditlogpageresponse-roblox-groups-api-models-response-groupauditlogresponseitem-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "actor": "...", "actionType": "...", "description": "...", "created": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-groups/v1/groups/{GROUPID}/audit-log" ``` ### PATCH `/legacy-groups/v1/groups/{groupId}/description` Updates the groups description **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-group:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The id of the group the user is in. | **Request Body:** `application/json` — Type: `Roblox.Groups.Api.UpdateGroupDescriptionRequest` See [Roblox.Groups.Api.UpdateGroupDescriptionRequest](#roblox-groups-api-updategroupdescriptionrequest) in Models. **Request example:** ```json { "description": "string" } ``` > **Verify mutations:** If your API key lacks the required scope (`legacy-group:manage`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupDescriptionResponse` - `400`: 1: Group is invalid or does not exist. 29: Your group description was empty. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: The description is too long. 23: Insufficient permissions to complete the request. **Response fields** (`Roblox.Groups.Api.GroupDescriptionResponse`) See [Roblox.Groups.Api.GroupDescriptionResponse](#roblox-groups-api-groupdescriptionresponse) in Models. **Response example:** ```json { "newDescription": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-groups/v1/groups/{GROUPID}/description" \ -H "Content-Type: application/json" \ -d '{ "description": "string" }' ``` ### PATCH `/legacy-groups/v1/groups/{groupId}/notification-preference` Updates the group's settings **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-user:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The id of the group the user is in. | **Request Body:** `application/json` — Type: `Roblox.Groups.Api.UpdateGroupNotificationPreferenceRequest` See [Roblox.Groups.Api.UpdateGroupNotificationPreferenceRequest](#roblox-groups-api-updategroupnotificationpreferencerequest) in Models. **Request example:** ```json { "notificationsEnabled": false, "type": 0 } ``` > **Verify mutations:** If your API key lacks the required scope (`legacy-user:manage`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `0 \| 1 \| 2 \| 3` - `400`: 1: Group is invalid or does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-groups/v1/groups/{GROUPID}/notification-preference" \ -H "Content-Type: application/json" \ -d '{ "notificationsEnabled": false, "type": 0 }' ``` ### GET `/legacy-groups/v1/groups/{groupId}/settings` Gets the Group's settings **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-group:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The id of the group the user is in. | **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupSettingsResponse` - `400`: 1: Group is invalid or does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 23: Insufficient permissions to complete the request. **Response fields** (`Roblox.Groups.Api.GroupSettingsResponse`) See [Roblox.Groups.Api.GroupSettingsResponse](#roblox-groups-api-groupsettingsresponse) in Models. **Response example:** ```json { "isApprovalRequired": false, "isBuildersClubRequired": false, "areEnemiesAllowed": false, "areGroupFundsVisible": false, "areGroupGamesVisible": false, "isGroupNameChangeEnabled": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-groups/v1/groups/{GROUPID}/settings" ``` ### PATCH `/legacy-groups/v1/groups/{groupId}/settings` Updates the group's settings **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-group:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The id of the group the user is in. | **Request Body:** `application/json` — Type: `Roblox.Groups.Api.UpdateGroupSettingsRequest` See [Roblox.Groups.Api.UpdateGroupSettingsRequest](#roblox-groups-api-updategroupsettingsrequest) in Models. **Request example:** ```json { "isApprovalRequired": false, "areEnemiesAllowed": false, "areGroupFundsVisible": false, "areGroupGamesVisible": false, "verificationLevel": 0, "accountTenureRequirement": 0 } ``` > **Verify mutations:** If your API key lacks the required scope (`legacy-group:manage`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: Group is invalid or does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 23: Insufficient permissions to complete the request. - `503`: 31: Service is currently unavailable. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-groups/v1/groups/{GROUPID}/settings" \ -H "Content-Type: application/json" \ -d '{ "isApprovalRequired": false, "areEnemiesAllowed": false, "areGroupFundsVisible": false, "areGroupGamesVisible": false, "verificationLevel": 0, "accountTenureRequirement": 0 }' ``` ### PATCH `/legacy-groups/v1/groups/{groupId}/status` Sets group status **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-group:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The group Id. | **Request Body:** `application/json` — Type: `Roblox.Groups.Api.PostGroupStatusRequest` See [Roblox.Groups.Api.PostGroupStatusRequest](#roblox-groups-api-postgroupstatusrequest) in Models. **Request example:** ```json { "message": "string" } ``` > **Verify mutations:** If your API key lacks the required scope (`legacy-group:manage`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.Groups.Api.ShoutResponse` - `400`: 1: Group is invalid or does not exist. 7: Missing group status content. 32: Description was filtered. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 6: You are not authorized to set the status of this group **Response fields** (`Roblox.Groups.Api.ShoutResponse`) See [Roblox.Groups.Api.ShoutResponse](#roblox-groups-api-shoutresponse) in Models. **Response example:** ```json { "body": "string", "poster": { "buildersClubMembershipType": 0, "hasVerifiedBadge": false, "userId": 0, "username": "string", "displayName": "string" }, "created": "2024-01-01T00:00:00Z", "updated": "2024-01-01T00:00:00Z" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-groups/v1/groups/{GROUPID}/status" \ -H "Content-Type: application/json" \ -d '{ "message": "string" }' ``` ### GET `/legacy-groups/v1/user/groups/pending` Gets groups that the authenticated user has requested to join **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-group:manage` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Groups.Api.GroupDetailResponse_` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Groups.Api.GroupDetailResponse_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Groups.Api.GroupDetailResponse_](#roblox-web-webapi-models-apiarrayresponse-roblox-groups-api-groupdetailresponse-) in Models. **Response example:** ```json { "data": [ { "id": "...", "name": "...", "description": "...", "owner": "...", "shout": "...", "memberCount": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-groups/v1/user/groups/pending" ``` ### POST `/legacy-localization-tables/v1/autolocalization/games/{gameId}/autolocalizationtable` **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-universe:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | | > **Verify mutations:** If your API key lacks the required scope (`legacy-universe:manage`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.LocalizationTables.Api.GameAutolocalizationInformationResponse` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.LocalizationTables.Api.GameAutolocalizationInformationResponse`) See [Roblox.LocalizationTables.Api.GameAutolocalizationInformationResponse](#roblox-localizationtables-api-gameautolocalizationinformationresponse) in Models. **Response example:** ```json { "isAutolocalizationEnabled": false, "isAutomaticEntriesSettingEnabled": false, "isAutomaticEntriesDeletionEnabled": false, "shouldUseLocalizationTable": false, "autoLocalizationTableId": "string", "sourceLanguage": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-localization-tables/v1/autolocalization/games/{GAMEID}/autolocalizationtable" ``` ### PATCH `/legacy-localization-tables/v1/autolocalization/games/{gameId}/settings` Sets a game's auto-localization related settings **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-universe:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The id of the game. | **Request Body:** `application/json` — Type: `Roblox.LocalizationTables.Api.SetAutolocalizationSettingsForGameRequest` See [Roblox.LocalizationTables.Api.SetAutolocalizationSettingsForGameRequest](#roblox-localizationtables-api-setautolocalizationsettingsforgamerequest) in Models. **Request example:** ```json { "isAutolocalizationEnabled": false, "isAutomaticEntriesSettingEnabled": false, "isAutomaticEntriesDeletionsEnabled": false, "shouldUseLocalizationTable": false } ``` > **Verify mutations:** If your API key lacks the required scope (`legacy-universe:manage`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 14: Invalid game id 61: IsAutomaticEntriesSettingEnabled can only be enabled if IsAutolocalizationEnabled is also enabled. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-localization-tables/v1/autolocalization/games/{GAMEID}/settings" \ -H "Content-Type: application/json" \ -d '{ "isAutolocalizationEnabled": false, "isAutomaticEntriesSettingEnabled": false, "isAutomaticEntriesDeletionsEnabled": false, "shouldUseLocalizationTable": false }' ``` ### GET `/legacy-localization-tables/v1/autolocalization/metadata` Metadata for AutoLocalization Configuration **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-universe:manage` **Responses:** - `200`: OK → `Roblox.LocalizationTables.Api.AutoLocalizationMetadataResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.LocalizationTables.Api.AutoLocalizationMetadataResponse`) See [Roblox.LocalizationTables.Api.AutoLocalizationMetadataResponse](#roblox-localizationtables-api-autolocalizationmetadataresponse) in Models. **Response example:** ```json { "isReactVersionEnabledForAutoLocalizationSettings": false, "isTabbedUIEnabledForConfigureLocalizationPage": false, "isAutomaticTranslationToggleUIEnabled": false, "isAutomaticTranslationQuotaUIEnabled": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-localization-tables/v1/autolocalization/metadata" ``` ### GET `/legacy-localization-tables/v1/localization-table/limits` Get limits for translation table entries operations **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-universe:manage` **Responses:** - `200`: OK → `Roblox.LocalizationTables.Api.GetLimitsResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.LocalizationTables.Api.GetLimitsResponse`) See [Roblox.LocalizationTables.Api.GetLimitsResponse](#roblox-localizationtables-api-getlimitsresponse) in Models. **Response example:** ```json { "entryOperationLimits": { "maxContextLength": 0, "maxKeyLength": 0, "maxSourceLength": 0, "maxExampleLength": 0, "maxGameLocationPathLength": 0 }, "tableOperationLimits": { "maxEntriesPerUpdate": 0 } } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-localization-tables/v1/localization-table/limits" ``` ### GET `/legacy-localization-tables/v1/localization-table/tables/{assetId}` Get table information by the assetId of the table. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-universe:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetId` | path | `integer` | Yes | The asset id associated with the table. | **Responses:** - `200`: OK → `Roblox.LocalizationTables.Api.GetTableResponse` - `400`: 12: Invalid asset id. - `401`: 0: Authorization has been denied for this request. - `403`: 2: You do not have permission to get this table. **Response fields** (`Roblox.LocalizationTables.Api.GetTableResponse`) See [Roblox.LocalizationTables.Api.GetTableResponse](#roblox-localizationtables-api-gettableresponse) in Models. **Response example:** ```json { "id": "string", "name": "string", "ownerType": "User", "ownerId": 0, "assetId": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-localization-tables/v1/localization-table/tables/{ASSETID}" ``` ### GET `/legacy-localization-tables/v1/localization-table/tables/{tableId}` Get table information by the id of the table. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-universe:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `tableId` | path | `string` | Yes | | **Responses:** - `200`: OK → `Roblox.LocalizationTables.Api.GetTableResponse` - `400`: 3: Invalid table id. - `401`: 0: Authorization has been denied for this request. - `403`: 2: You do not have permission to get this table. **Response fields** (`Roblox.LocalizationTables.Api.GetTableResponse`) See [Roblox.LocalizationTables.Api.GetTableResponse](#roblox-localizationtables-api-gettableresponse) in Models. **Response example:** ```json { "id": "string", "name": "string", "ownerType": "User", "ownerId": 0, "assetId": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-localization-tables/v1/localization-table/tables/{TABLEID}" ``` ### PATCH `/legacy-localization-tables/v1/localization-table/tables/{tableId}` Updates the tables contents based on what is provided. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-universe:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `tableId` | path | `string` | Yes | The table guid for the table to update. | | `gameId` | query | `integer` | No | The game id. | **Request Body:** `application/json` — Type: `Roblox.LocalizationTables.Api.UpdateTableContentsRequest` See [Roblox.LocalizationTables.Api.UpdateTableContentsRequest](#roblox-localizationtables-api-updatetablecontentsrequest) in Models. **Request example:** ```json { "name": "string", "entries": [ { "identifier": "...", "metadata": "...", "translations": "...", "delete": "..." } ] } ``` > **Verify mutations:** If your API key lacks the required scope (`legacy-universe:manage`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.LocalizationTables.Api.UpdateTableContentsResponse` - `400`: 3: Invalid table id. 4: Table does not exist. 10: Maximum entries exceeded. Please keep the number of entries per request below the maximum. 13: Request body can't be null 14: Invalid game id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 6: You do not have permission to create this table. - `503`: 17: Feature is disabled **Response fields** (`Roblox.LocalizationTables.Api.UpdateTableContentsResponse`) See [Roblox.LocalizationTables.Api.UpdateTableContentsResponse](#roblox-localizationtables-api-updatetablecontentsresponse) in Models. **Response example:** ```json { "failedEntriesAndTranslations": [ { "error": "...", "identifier": "...", "metadata": "...", "translations": "...", "createdTime": "..." } ], "modifiedEntriesAndTranslations": [ { "identifier": "...", "translations": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-localization-tables/v1/localization-table/tables/{TABLEID}" \ -H "Content-Type: application/json" \ -d '{ "name": "string", "entries": [ { "identifier": "...", "metadata": "...", "translations": "...", "delete": "..." } ] }' ``` ### GET `/legacy-localization-tables/v1/localization-table/tables/{tableId}/entries` Gets a batch of entries for a table. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-universe:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `tableId` | path | `string` | Yes | | | `cursor` | query | `string` | No | If null, there are no more entries in the table and you've reached the last page. | | `gameId` | query | `integer` | No | | | `entryFormat` | query | `Invalid \| Legacy \| Icu` | No | Valid values: `Invalid`, `Legacy`, `Icu` | **Responses:** - `200`: OK → `Roblox.LocalizationTables.Api.GetTableEntriesPagedResponse` - `400`: 3: Invalid table id. 14: Invalid game id - `401`: 0: Authorization has been denied for this request. - `403`: 2: You do not have permission to get this table. **Response fields** (`Roblox.LocalizationTables.Api.GetTableEntriesPagedResponse`) See [Roblox.LocalizationTables.Api.GetTableEntriesPagedResponse](#roblox-localizationtables-api-gettableentriespagedresponse) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "identifier": "...", "metadata": "...", "translations": "...", "createdTime": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-localization-tables/v1/localization-table/tables/{TABLEID}/entries" ``` ### POST `/legacy-localization-tables/v1/localization-table/tables/{tableId}/entries/translation-history` Gets the translation history for each entry passed in. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-universe:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `tableId` | path | `string` | Yes | The entries' tableId. | | `gameId` | query | `integer` | No | The game id. | **Request Body:** `application/json` — Type: `Roblox.LocalizationTables.Api.GetTableEntriesTranslationHistoryRequest` See [Roblox.LocalizationTables.Api.GetTableEntriesTranslationHistoryRequest](#roblox-localizationtables-api-gettableentriestranslationhistoryrequest) in Models. **Request example:** ```json { "locale": "string", "entries": [ { "cursor": "...", "identifier": "...", "count": "...", "sortOrder": "..." } ] } ``` > **Verify mutations:** If your API key lacks the required scope (`legacy-universe:manage`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.LocalizationTables.Api.GetTableEntriesTranslationHistoryResponse` - `400`: 3: Invalid table id. 13: Request body can't be null 14: Invalid game id 16: Entries can't be null or empty 35: The entries provided are invalid 37: Invalid locale code. 38: Invalid entry identifier. 39: Count should be at least 1. 45: Invalid exclusive start id. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: You do not have permission to get this table. - `429`: 24: Too many attempts.Please try again later. - `503`: 17: Feature is disabled **Response fields** (`Roblox.LocalizationTables.Api.GetTableEntriesTranslationHistoryResponse`) See [Roblox.LocalizationTables.Api.GetTableEntriesTranslationHistoryResponse](#roblox-localizationtables-api-gettableentriestranslationhistoryresponse) in Models. **Response example:** ```json { "tableId": "string", "locale": "string", "entries": [ { "identifier": "...", "history": "...", "nextCursor": "..." } ], "failedEntries": [ { "identifier": "...", "count": "...", "error": "..." } ] } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-localization-tables/v1/localization-table/tables/{TABLEID}/entries/translation-history" \ -H "Content-Type: application/json" \ -d '{ "locale": "string", "entries": [ { "cursor": "...", "identifier": "...", "count": "...", "sortOrder": "..." } ] }' ``` ### GET `/legacy-localization-tables/v1/localization-table/tables/{tableId}/entry-count` Gets the number of entries in the specified table **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-universe:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `tableId` | path | `string` | Yes | The table id | | `gameId` | query | `integer` | No | The game id | | `entryFormat` | query | `Invalid \| Legacy \| Icu` | No | Valid values: `Invalid`, `Legacy`, `Icu` | **Responses:** - `200`: OK → `Roblox.LocalizationTables.Api.GetTableEntryCountResponse` - `400`: 3: Invalid table id. 14: Invalid game id - `401`: 0: Authorization has been denied for this request. - `403`: 2: You do not have permission to get this table. **Response fields** (`Roblox.LocalizationTables.Api.GetTableEntryCountResponse`) See [Roblox.LocalizationTables.Api.GetTableEntryCountResponse](#roblox-localizationtables-api-gettableentrycountresponse) in Models. **Response example:** ```json { "id": "string", "entryCount": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-localization-tables/v1/localization-table/tables/{TABLEID}/entry-count" ``` ### POST `/legacy-publish/v1/badges/{badgeId}/icon` Overwrites a badge icon with a new one. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `legacy-badge:manage` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `badgeId` | path | `integer` | Yes | The badge Id. | > **Verify mutations:** If your API key lacks the required scope (`legacy-badge:manage`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Roblox.Publish.Api.UploadResponse` - `400`: 2: File not present in request. 12: Name or description is moderated. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 5: You do not have permission to manage this item. - `404`: 4: Target item is invalid or does not exist. - `429`: 3: You're uploading too much, please wait and try again later. **Response fields** (`Roblox.Publish.Api.UploadResponse`) See [Roblox.Publish.Api.UploadResponse](#roblox-publish-api-uploadresponse) in Models. **Response example:** ```json { "targetId": 0 } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/legacy-publish/v1/badges/{BADGEID}/icon" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### GET `/matchmaking-api/v1/client-status` [BETA] Get the client-status **Auth:** **Request Body:** `application/json-patch+json` — Type: `ClientStatusGetRequest` See [ClientStatusGetRequest](#clientstatusgetrequest) in Models. **Request example:** ```json { "browserTrackerId": 0 } ``` **Responses:** - `200`: Success **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/matchmaking-api/v1/client-status" \ -H "Content-Type: application/json" \ -d '{ "browserTrackerId": 0 }' ``` ### POST `/matchmaking-api/v1/client-status` [BETA] Set the client-status **Auth:** **Request Body:** `application/json-patch+json` — Type: `ClientStatusSetRequest` See [ClientStatusSetRequest](#clientstatussetrequest) in Models. **Request example:** ```json { "status": "string", "browserTrackerId": 0 } ``` **Responses:** - `200`: Success **Example:** ```bash curl -X POST -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/matchmaking-api/v1/client-status" \ -H "Content-Type: application/json" \ -d '{ "status": "string", "browserTrackerId": 0 }' ``` ### POST `/matchmaking-api/v1/game-instances/forecast-update` [BETA] Forecast the outcome of launching an update **Auth:** **Request Body:** `application/json-patch+json` — Type: `ForecastUpdateRequest` See [ForecastUpdateRequest](#forecastupdaterequest) in Models. **Request example:** ```json { "universeId": 0 } ``` **Responses:** - `200`: Success → `ForecastUpdateResponse` **Response fields** (`ForecastUpdateResponse`) See [ForecastUpdateResponse](#forecastupdateresponse) in Models. **Response example:** ```json { "placeSummaries": [ { "placeId": "...", "playersToBeKicked": "...", "totalPlayers": "...", "instancesToBeClosed": "...", "totalInstances": "...", "placeVersion": "..." } ] } ``` **Example:** ```bash curl -X POST -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/matchmaking-api/v1/game-instances/forecast-update" \ -H "Content-Type: application/json" \ -d '{ "universeId": 0 }' ``` ### GET `/matchmaking-api/v1/game-instances/get-update-status` [BETA] Get the rollout status of an update **Auth:** **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | query | `integer` | No | | **Responses:** - `200`: Success → `GetUpdateStatusResponse` **Response fields** (`GetUpdateStatusResponse`) See [GetUpdateStatusResponse](#getupdatestatusresponse) in Models. **Response example:** ```json { "updateStatusList": [ { "id": "...", "universeId": "...", "startTime": "...", "closeOldVersionsOnly": "...", "bleedOffEndTime": "...", "placeUpdateStatuses": "..." } ] } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/matchmaking-api/v1/game-instances/get-update-status" ``` ### POST `/matchmaking-api/v1/game-instances/launch-update` [BETA] Launch a game update **Auth:** **Request Body:** `application/json-patch+json` — Type: `LaunchUpdateRequest` See [LaunchUpdateRequest](#launchupdaterequest) in Models. **Request example:** ```json { "universeId": 0, "placeIds": [ 0 ], "closeOldVersionsOnly": false, "bleedOffServers": false, "bleedOffDurationMinutes": 0, "placeIdToVersions": "..." } ``` **Responses:** - `200`: Success → `LaunchUpdateResponse` **Response fields** (`LaunchUpdateResponse`) See [LaunchUpdateResponse](#launchupdateresponse) in Models. **Response example:** ```json { "updateId": "string", "numPlayersToBeKicked": 0, "instancesToBeClosed": 0 } ``` **Example:** ```bash curl -X POST -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/matchmaking-api/v1/game-instances/launch-update" \ -H "Content-Type: application/json" \ -d '{ "universeId": 0, "placeIds": [ 0 ], "closeOldVersionsOnly": false, "bleedOffServers": false, "bleedOffDurationMinutes": 0, "placeIdToVersions": "..." }' ``` ### POST `/matchmaking-api/v1/game-instances/shutdown` [BETA] Shutdown game instances. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json-patch+json` — Type: `ShutdownGameInstancesRequest` See [ShutdownGameInstancesRequest](#shutdowngameinstancesrequest) in Models. **Request example:** ```json { "placeId": 0, "privateServerId": 0, "gameId": "string" } ``` **Responses:** - `200`: Success → `ShutdownGameInstancesResponse` **Response fields** (`ShutdownGameInstancesResponse`) See [ShutdownGameInstancesResponse](#shutdowngameinstancesresponse) in Models. **Response example:** ```json { "placeId": 0, "privateServerId": 0, "gameId": "string" } ``` **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://apis.roblox.com/matchmaking-api/v1/game-instances/shutdown" \ -H "Content-Type: application/json" \ -d '{ "placeId": 0, "privateServerId": 0, "gameId": "string" }' ``` ### POST `/matchmaking-api/v1/game-instances/shutdown-all` [BETA] Shutdown all game instances. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `multipart/form-data` — Type: `object` | Property | Type | Required | Description | |----------|------|----------|-------------| | `PlaceId` | `integer` | No | The place ID to shut down. | | `ReplaceInstances` | `boolean` | No | Whether to replace instances or not. | **Request example:** ```json { "PlaceId": 0, "ReplaceInstances": false } ``` **Responses:** - `200`: Success → `ShutdownAllGameInstancesResponse` **Response fields** (`ShutdownAllGameInstancesResponse`) See [ShutdownAllGameInstancesResponse](#shutdownallgameinstancesresponse) in Models. **Response example:** ```json { "placeId": 0, "replaceInstances": false } ``` **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://apis.roblox.com/matchmaking-api/v1/game-instances/shutdown-all" \ -F "PlaceId=0" \ -F "ReplaceInstances=false" ``` ### POST `/matchmaking-api/v1/matchmaking/player-attribute` [BETA] Create a PlayerAttributeDefinition. **Auth:** **Request Body:** `application/json-patch+json` — Type: `CreateMatchmakingPlayerAttributeDefinitionRequest` See [CreateMatchmakingPlayerAttributeDefinitionRequest](#creatematchmakingplayerattributedefinitionrequest) in Models. **Request example:** ```json { "universeId": 0, "name": "string", "dataType": "Invalid", "defaultValue": { "boolValue": false, "numericValue": 0, "stringValue": "string", "type": "Invalid" }, "attributeValueLocation": { "locationCase": "Invalid", "dataStoreLocation": { "dataStoreName": "...", "scope": "...", "keyTemplate": "...", "valuePath": "..." } } } ``` **Responses:** - `200`: Success → `CreateMatchmakingPlayerAttributeDefinitionResponse` **Response fields** (`CreateMatchmakingPlayerAttributeDefinitionResponse`) See [CreateMatchmakingPlayerAttributeDefinitionResponse](#creatematchmakingplayerattributedefinitionresponse) in Models. **Response example:** ```json { "playerAttributeDefinition": { "id": "string", "universeId": 0, "name": "string", "dataType": "Invalid", "defaultValue": { "boolValue": "...", "numericValue": "...", "stringValue": "...", "type": "..." }, "attributeValueLocation": { "locationCase": "...", "dataStoreLocation": "..." } } } ``` **Example:** ```bash curl -X POST -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/matchmaking-api/v1/matchmaking/player-attribute" \ -H "Content-Type: application/json" \ -d '{"universeId":0,"name":"string","dataType":"Invalid","defaultValue":{"boolValue":false,"numericValue":0,"stringValue":"string","type":"Invalid"},"attributeValueLocation":{"locationCase":"Invalid","dataStoreLocation":{"dataStoreName":"...","scope":"...","keyTemplate":"...","valuePath":"..."}}}' ``` ### PATCH `/matchmaking-api/v1/matchmaking/player-attribute/{attributeId}` [BETA] Update the PlayerAttributeDefinition specified by attributeId. **Auth:** **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `attributeId` | path | `string` | Yes | | **Request Body:** `application/json-patch+json` — Type: `UpdateMatchmakingPlayerAttributeDefinitionRequest` See [UpdateMatchmakingPlayerAttributeDefinitionRequest](#updatematchmakingplayerattributedefinitionrequest) in Models. **Request example:** ```json { "attributeId": "string", "defaultValue": { "boolValue": false, "numericValue": 0, "stringValue": "string", "type": "Invalid" }, "attributeValueLocation": { "locationCase": "Invalid", "dataStoreLocation": { "dataStoreName": "...", "scope": "...", "keyTemplate": "...", "valuePath": "..." } } } ``` **Responses:** - `200`: Success → `UpdateMatchmakingPlayerAttributeDefinitionResponse` **Response fields** (`UpdateMatchmakingPlayerAttributeDefinitionResponse`) See [UpdateMatchmakingPlayerAttributeDefinitionResponse](#updatematchmakingplayerattributedefinitionresponse) in Models. **Response example:** ```json { "playerAttributeDefinition": { "id": "string", "universeId": 0, "name": "string", "dataType": "Invalid", "defaultValue": { "boolValue": "...", "numericValue": "...", "stringValue": "...", "type": "..." }, "attributeValueLocation": { "locationCase": "...", "dataStoreLocation": "..." } } } ``` **Example:** ```bash curl -X PATCH -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/matchmaking-api/v1/matchmaking/player-attribute/{ATTRIBUTEID}" \ -H "Content-Type: application/json" \ -d '{"attributeId":"string","defaultValue":{"boolValue":false,"numericValue":0,"stringValue":"string","type":"Invalid"},"attributeValueLocation":{"locationCase":"Invalid","dataStoreLocation":{"dataStoreName":"...","scope":"...","keyTemplate":"...","valuePath":"..."}}}' ``` ### DELETE `/matchmaking-api/v1/matchmaking/player-attribute/{attributeId}` [BETA] Delete the PlayerAttributeDefinition specified by attributeId. **Auth:** **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `attributeId` | path | `string` | Yes | | **Responses:** - `200`: Success → `DeleteMatchmakingPlayerAttributeDefinitionResponse` **Response fields** (`DeleteMatchmakingPlayerAttributeDefinitionResponse`) See [DeleteMatchmakingPlayerAttributeDefinitionResponse](#deletematchmakingplayerattributedefinitionresponse) in Models. **Example:** ```bash curl -X DELETE -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/matchmaking-api/v1/matchmaking/player-attribute/{ATTRIBUTEID}" ``` ### GET `/matchmaking-api/v1/matchmaking/player-attributes/{universeId}` [BETA] List all PlayerAttributeDefinitions of a universe. **Auth:** **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | | **Responses:** - `200`: Success → `ListMatchmakingPlayerAttributeDefinitionsResponse` **Response fields** (`ListMatchmakingPlayerAttributeDefinitionsResponse`) See [ListMatchmakingPlayerAttributeDefinitionsResponse](#listmatchmakingplayerattributedefinitionsresponse) in Models. **Response example:** ```json { "playerAttributeSchema": [ { "id": "...", "universeId": "...", "name": "...", "dataType": "...", "defaultValue": "...", "attributeValueLocation": "..." } ] } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/matchmaking-api/v1/matchmaking/player-attributes/{UNIVERSEID}" ``` ### POST `/matchmaking-api/v1/matchmaking/scoring-configuration` [BETA] Creates a matchmaking scoring configuration. **Auth:** **Request Body:** `application/json-patch+json` — Type: `CreateMatchmakingScoringConfigurationRequest` See [CreateMatchmakingScoringConfigurationRequest](#creatematchmakingscoringconfigurationrequest) in Models. **Request example:** ```json { "universeId": 0, "name": "string", "matchmakingSignalWeights": { "Invalid": 0, "Occupancy": 0, "Age": 0, "Language": 0, "Latency": 0, "PreferredPlayers": 0 }, "customSignalWeights": "...", "template": "NotApplicable" } ``` **Responses:** - `200`: Success → `CreateMatchmakingScoringConfigurationResponse` **Response fields** (`CreateMatchmakingScoringConfigurationResponse`) See [CreateMatchmakingScoringConfigurationResponse](#creatematchmakingscoringconfigurationresponse) in Models. **Response example:** ```json { "scoringConfiguration": { "id": "string", "name": "string", "signalWeights": { "Invalid": "...", "Occupancy": "...", "Age": "...", "Language": "...", "Latency": "...", "PreferredPlayers": "..." }, "customSignals": "...", "createdTime": "2024-01-01T00:00:00Z", "updatedTime": "2024-01-01T00:00:00Z" } } ``` **Example:** ```bash curl -X POST -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/matchmaking-api/v1/matchmaking/scoring-configuration" \ -H "Content-Type: application/json" \ -d '{ "universeId": 0, "name": "string", "matchmakingSignalWeights": { "Invalid": 0, "Occupancy": 0, "Age": 0, "Language": 0, "Latency": 0, "PreferredPlayers": 0 }, "customSignalWeights": "...", "template": "NotApplicable" }' ``` ### GET `/matchmaking-api/v1/matchmaking/scoring-configuration/default-weights` [BETA] **Auth:** **Responses:** - `200`: Success → `GetMatchmakingScoringDefaultWeightsResponse` **Response fields** (`GetMatchmakingScoringDefaultWeightsResponse`) See [GetMatchmakingScoringDefaultWeightsResponse](#getmatchmakingscoringdefaultweightsresponse) in Models. **Response example:** ```json { "weights": { "Invalid": 0, "Occupancy": 0, "Age": 0, "Language": 0, "Latency": 0, "PreferredPlayers": 0 } } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/matchmaking-api/v1/matchmaking/scoring-configuration/default-weights" ``` ### GET `/matchmaking-api/v1/matchmaking/scoring-configuration/generate-mock-servers` [BETA] **Auth:** **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `count` | query | `integer` | No | | | `capacity` | query | `integer` | No | | | `playerAge` | query | `integer` | No | | | `playerPlayHistory` | query | `number` | No | | | `isPlayerVoiceChatEnabled` | query | `boolean` | No | | **Responses:** - `200`: Success → `GenerateMockServerSignalValuesResponse` **Response fields** (`GenerateMockServerSignalValuesResponse`) See [GenerateMockServerSignalValuesResponse](#generatemockserversignalvaluesresponse) in Models. **Response example:** ```json { "exampleGameSignalValues": [ { "capacity": "...", "occupancy": "...", "hasPreferredPlayers": "...", "playerAge": "...", "serverAveragePlayerAge": "...", "commonLanguagePlayers": "..." } ] } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/matchmaking-api/v1/matchmaking/scoring-configuration/generate-mock-servers" ``` ### POST `/matchmaking-api/v1/matchmaking/scoring-configuration/place` [BETA] Sets a matchmaking scoring configuration for a place. **Auth:** **Request Body:** `application/json-patch+json` — Type: `SetPlaceMatchmakingScoringConfigurationRequest` See [SetPlaceMatchmakingScoringConfigurationRequest](#setplacematchmakingscoringconfigurationrequest) in Models. **Request example:** ```json { "placeId": 0, "scoringConfigurationId": "string" } ``` **Responses:** - `200`: Success → `SetMatchmakingScoringConfigurationResponse` **Response fields** (`SetMatchmakingScoringConfigurationResponse`) See [SetMatchmakingScoringConfigurationResponse](#setmatchmakingscoringconfigurationresponse) in Models. **Response example:** ```json { "scoringConfiguration": { "id": "string", "name": "string", "signalWeights": { "Invalid": "...", "Occupancy": "...", "Age": "...", "Language": "...", "Latency": "...", "PreferredPlayers": "..." }, "customSignals": "...", "createdTime": "2024-01-01T00:00:00Z", "updatedTime": "2024-01-01T00:00:00Z" } } ``` **Example:** ```bash curl -X POST -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/matchmaking-api/v1/matchmaking/scoring-configuration/place" \ -H "Content-Type: application/json" \ -d '{ "placeId": 0, "scoringConfigurationId": "string" }' ``` ### DELETE `/matchmaking-api/v1/matchmaking/scoring-configuration/place/{placeId}` [BETA] Removes the matchmaking scoring configuration for a place. **Auth:** **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `placeId` | path | `integer` | Yes | | **Responses:** - `200`: Success → `RemovePlaceMatchmakingScoringConfigurationResponse` **Response fields** (`RemovePlaceMatchmakingScoringConfigurationResponse`) See [RemovePlaceMatchmakingScoringConfigurationResponse](#removeplacematchmakingscoringconfigurationresponse) in Models. **Example:** ```bash curl -X DELETE -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/matchmaking-api/v1/matchmaking/scoring-configuration/place/{PLACEID}" ``` ### GET `/matchmaking-api/v1/matchmaking/scoring-configuration/{scoringConfigurationId}` [BETA] Updates a matchmaking scoring configuration. **Auth:** **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `scoringConfigurationId` | path | `string` | Yes | | **Responses:** - `200`: Success → `GetMatchmakingScoringConfigurationResponse` **Response fields** (`GetMatchmakingScoringConfigurationResponse`) See [GetMatchmakingScoringConfigurationResponse](#getmatchmakingscoringconfigurationresponse) in Models. **Response example:** ```json { "scoringConfiguration": { "id": "string", "name": "string", "signalWeights": { "Invalid": "...", "Occupancy": "...", "Age": "...", "Language": "...", "Latency": "...", "PreferredPlayers": "..." }, "customSignals": "...", "createdTime": "2024-01-01T00:00:00Z", "updatedTime": "2024-01-01T00:00:00Z" } } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/matchmaking-api/v1/matchmaking/scoring-configuration/{SCORINGCONFIGURATIONID}" ``` ### PATCH `/matchmaking-api/v1/matchmaking/scoring-configuration/{scoringConfigurationId}` [BETA] Updates a matchmaking scoring configuration. **Auth:** **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `scoringConfigurationId` | path | `string` | Yes | | **Request Body:** `application/json-patch+json` — Type: `UpdateMatchmakingScoringConfigurationRequest` See [UpdateMatchmakingScoringConfigurationRequest](#updatematchmakingscoringconfigurationrequest) in Models. **Request example:** ```json { "scoringConfigurationId": "string", "name": "string", "matchmakingSignalWeights": { "Invalid": 0, "Occupancy": 0, "Age": 0, "Language": 0, "Latency": 0, "PreferredPlayers": 0 }, "customSignalWeights": "..." } ``` **Responses:** - `200`: Success → `UpdateMatchmakingScoringConfigurationResponse` **Response fields** (`UpdateMatchmakingScoringConfigurationResponse`) See [UpdateMatchmakingScoringConfigurationResponse](#updatematchmakingscoringconfigurationresponse) in Models. **Response example:** ```json { "scoringConfiguration": { "id": "string", "name": "string", "signalWeights": { "Invalid": "...", "Occupancy": "...", "Age": "...", "Language": "...", "Latency": "...", "PreferredPlayers": "..." }, "customSignals": "...", "createdTime": "2024-01-01T00:00:00Z", "updatedTime": "2024-01-01T00:00:00Z" } } ``` **Example:** ```bash curl -X PATCH -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/matchmaking-api/v1/matchmaking/scoring-configuration/{SCORINGCONFIGURATIONID}" \ -H "Content-Type: application/json" \ -d '{ "scoringConfigurationId": "string", "name": "string", "matchmakingSignalWeights": { "Invalid": 0, "Occupancy": 0, "Age": 0, "Language": 0, "Latency": 0, "PreferredPlayers": 0 }, "customSignalWeights": "..." }' ``` ### DELETE `/matchmaking-api/v1/matchmaking/scoring-configuration/{scoringConfigurationId}` [BETA] Deletes a matchmaking scoring configuration. **Auth:** **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `scoringConfigurationId` | path | `string` | Yes | | **Responses:** - `200`: Success → `DeleteMatchmakingScoringConfigurationResponse` **Response fields** (`DeleteMatchmakingScoringConfigurationResponse`) See [DeleteMatchmakingScoringConfigurationResponse](#deletematchmakingscoringconfigurationresponse) in Models. **Example:** ```bash curl -X DELETE -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/matchmaking-api/v1/matchmaking/scoring-configuration/{SCORINGCONFIGURATIONID}" ``` ### POST `/matchmaking-api/v1/matchmaking/scoring-configuration/{scoringConfigurationId}/signals` [BETA] Creates a matchmaking scoring configuration signal. **Auth:** **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `scoringConfigurationId` | path | `string` | Yes | | **Request Body:** `application/json-patch+json` — Type: `CreateCustomMatchmakingSignalRequest` See [CreateCustomMatchmakingSignalRequest](#createcustommatchmakingsignalrequest) in Models. **Request example:** ```json { "scoringConfigurationId": "string", "signalConfiguration": { "name": "string", "weight": 0, "customSignalType": "Invalid", "playerCategoricalSignalConfiguration": { "playerAttribute": "...", "curveType": "..." }, "serverCategoricalSignalConfiguration": { "serverAttribute": "...", "comparisonType": "...", "playerAttribute": "...", "constantValue": "..." }, "serverNumericalSignalConfiguration": { "serverAttribute": "...", "comparisonType": "...", "maxRelevantDifference": "...", "constantValue": "...", "playerAttribute": "..." } } } ``` **Responses:** - `200`: Success → `CreateCustomMatchmakingSignalResponse` **Response fields** (`CreateCustomMatchmakingSignalResponse`) See [CreateCustomMatchmakingSignalResponse](#createcustommatchmakingsignalresponse) in Models. **Response example:** ```json { "scoringConfiguration": { "id": "string", "name": "string", "signalWeights": { "Invalid": "...", "Occupancy": "...", "Age": "...", "Language": "...", "Latency": "...", "PreferredPlayers": "..." }, "customSignals": "...", "createdTime": "2024-01-01T00:00:00Z", "updatedTime": "2024-01-01T00:00:00Z" } } ``` **Example:** ```bash curl -X POST -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/matchmaking-api/v1/matchmaking/scoring-configuration/{SCORINGCONFIGURATIONID}/signals" \ -H "Content-Type: application/json" \ -d '{"scoringConfigurationId":"string","signalConfiguration":{"name":"string","weight":0,"customSignalType":"Invalid","playerCategoricalSignalConfiguration":{"playerAttribute":"...","curveType":"..."},"serverCategoricalSignalConfiguration":{"serverAttribute":"...","comparisonType":"...","playerAttribute":"...","constantValue":"..."},"serverNumericalSignalConfiguration":{"serverAttribute":"...","comparisonType":"...","maxRelevantDifference":"...","constantValue":"...","playerAttribute":"..."}}}' ``` ### PATCH `/matchmaking-api/v1/matchmaking/scoring-configuration/{scoringConfigurationId}/signals/{signalName}` [BETA] Updates a matchmaking scoring configuration signal. **Auth:** **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `scoringConfigurationId` | path | `string` | Yes | | | `signalName` | path | `string` | Yes | | **Request Body:** `application/json-patch+json` — Type: `UpdateCustomMatchmakingSignalRequest` See [UpdateCustomMatchmakingSignalRequest](#updatecustommatchmakingsignalrequest) in Models. **Request example:** ```json { "scoringConfigurationId": "string", "signalConfiguration": { "name": "string", "weight": 0, "customSignalType": "Invalid", "playerCategoricalSignalConfiguration": { "playerAttribute": "...", "curveType": "..." }, "serverCategoricalSignalConfiguration": { "serverAttribute": "...", "comparisonType": "...", "playerAttribute": "...", "constantValue": "..." }, "serverNumericalSignalConfiguration": { "serverAttribute": "...", "comparisonType": "...", "maxRelevantDifference": "...", "constantValue": "...", "playerAttribute": "..." } } } ``` **Responses:** - `200`: Success → `UpdateCustomMatchmakingSignalResponse` **Response fields** (`UpdateCustomMatchmakingSignalResponse`) See [UpdateCustomMatchmakingSignalResponse](#updatecustommatchmakingsignalresponse) in Models. **Response example:** ```json { "scoringConfiguration": { "id": "string", "name": "string", "signalWeights": { "Invalid": "...", "Occupancy": "...", "Age": "...", "Language": "...", "Latency": "...", "PreferredPlayers": "..." }, "customSignals": "...", "createdTime": "2024-01-01T00:00:00Z", "updatedTime": "2024-01-01T00:00:00Z" } } ``` **Example:** ```bash curl -X PATCH -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/matchmaking-api/v1/matchmaking/scoring-configuration/{SCORINGCONFIGURATIONID}/signals/{SIGNALNAME}" \ -H "Content-Type: application/json" \ -d '{"scoringConfigurationId":"string","signalConfiguration":{"name":"string","weight":0,"customSignalType":"Invalid","playerCategoricalSignalConfiguration":{"playerAttribute":"...","curveType":"..."},"serverCategoricalSignalConfiguration":{"serverAttribute":"...","comparisonType":"...","playerAttribute":"...","constantValue":"..."},"serverNumericalSignalConfiguration":{"serverAttribute":"...","comparisonType":"...","maxRelevantDifference":"...","constantValue":"...","playerAttribute":"..."}}}' ``` ### DELETE `/matchmaking-api/v1/matchmaking/scoring-configuration/{scoringConfigurationId}/signals/{signalName}` [BETA] Deletes a matchmaking scoring configuration custom signal. **Auth:** **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `scoringConfigurationId` | path | `string` | Yes | | | `signalName` | path | `string` | Yes | | **Responses:** - `200`: Success → `DeleteCustomMatchmakingSignalResponse` **Response fields** (`DeleteCustomMatchmakingSignalResponse`) See [DeleteCustomMatchmakingSignalResponse](#deletecustommatchmakingsignalresponse) in Models. **Response example:** ```json { "scoringConfiguration": { "id": "string", "name": "string", "signalWeights": { "Invalid": "...", "Occupancy": "...", "Age": "...", "Language": "...", "Latency": "...", "PreferredPlayers": "..." }, "customSignals": "...", "createdTime": "2024-01-01T00:00:00Z", "updatedTime": "2024-01-01T00:00:00Z" } } ``` **Example:** ```bash curl -X DELETE -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/matchmaking-api/v1/matchmaking/scoring-configuration/{SCORINGCONFIGURATIONID}/signals/{SIGNALNAME}" ``` ### GET `/matchmaking-api/v1/matchmaking/scoring-configurations/{universeId}` [BETA] List all matchmaking scoring configurations for a universe. **Auth:** **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | | **Responses:** - `200`: Success → `ListMatchmakingScoringConfigurationsResponse` **Response fields** (`ListMatchmakingScoringConfigurationsResponse`) See [ListMatchmakingScoringConfigurationsResponse](#listmatchmakingscoringconfigurationsresponse) in Models. **Response example:** ```json { "scoringConfigurations": [ { "id": "...", "name": "...", "signalWeights": "...", "customSignals": "...", "createdTime": "...", "updatedTime": "..." } ] } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/matchmaking-api/v1/matchmaking/scoring-configurations/{UNIVERSEID}" ``` ### GET `/matchmaking-api/v1/matchmaking/scoring-configurations/{universeId}/places` [BETA] List all places with a matchmaking scoring configuration for a universe. **Auth:** **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | | **Responses:** - `200`: Success → `ListPlaceMatchmakingScoringConfigurationsResponse` **Response fields** (`ListPlaceMatchmakingScoringConfigurationsResponse`) See [ListPlaceMatchmakingScoringConfigurationsResponse](#listplacematchmakingscoringconfigurationsresponse) in Models. **Response example:** ```json { "placeScoringConfigurations": "..." } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/matchmaking-api/v1/matchmaking/scoring-configurations/{UNIVERSEID}/places" ``` ### POST `/matchmaking-api/v1/matchmaking/server-attribute` [BETA] Create a ServerAttributeDefinition. **Auth:** **Request Body:** `application/json-patch+json` — Type: `CreateMatchmakingServerAttributeDefinitionRequest` See [CreateMatchmakingServerAttributeDefinitionRequest](#creatematchmakingserverattributedefinitionrequest) in Models. **Request example:** ```json { "universeId": 0, "name": "string", "dataType": "Invalid", "defaultValue": { "sourceCase": "Invalid", "constant": { "boolValue": "...", "numericValue": "...", "stringValue": "...", "type": "..." }, "playerAttributeReference": { "id": "..." } } } ``` **Responses:** - `200`: Success → `CreateMatchmakingServerAttributeDefinitionResponse` **Response fields** (`CreateMatchmakingServerAttributeDefinitionResponse`) See [CreateMatchmakingServerAttributeDefinitionResponse](#creatematchmakingserverattributedefinitionresponse) in Models. **Response example:** ```json { "playerAttributeDefinition": { "id": "string", "universeId": 0, "name": "string", "dataType": "Invalid", "defaultValue": { "sourceCase": "...", "constant": "...", "playerAttributeReference": "..." }, "createdTime": "2024-01-01T00:00:00Z" } } ``` **Example:** ```bash curl -X POST -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/matchmaking-api/v1/matchmaking/server-attribute" \ -H "Content-Type: application/json" \ -d '{"universeId":0,"name":"string","dataType":"Invalid","defaultValue":{"sourceCase":"Invalid","constant":{"boolValue":"...","numericValue":"...","stringValue":"...","type":"..."},"playerAttributeReference":{"id":"..."}}}' ``` ### PATCH `/matchmaking-api/v1/matchmaking/server-attribute/{attributeId}` [BETA] Update the ServerAttributeDefinition specified by attributeId. **Auth:** **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `attributeId` | path | `string` | Yes | | **Request Body:** `application/json-patch+json` — Type: `UpdateMatchmakingServerAttributeDefinitionRequest` See [UpdateMatchmakingServerAttributeDefinitionRequest](#updatematchmakingserverattributedefinitionrequest) in Models. **Request example:** ```json { "attributeId": "string", "defaultValue": { "sourceCase": "Invalid", "constant": { "boolValue": "...", "numericValue": "...", "stringValue": "...", "type": "..." }, "playerAttributeReference": { "id": "..." } } } ``` **Responses:** - `200`: Success → `UpdateMatchmakingServerAttributeDefinitionResponse` **Response fields** (`UpdateMatchmakingServerAttributeDefinitionResponse`) See [UpdateMatchmakingServerAttributeDefinitionResponse](#updatematchmakingserverattributedefinitionresponse) in Models. **Response example:** ```json { "playerAttributeDefinition": { "id": "string", "universeId": 0, "name": "string", "dataType": "Invalid", "defaultValue": { "sourceCase": "...", "constant": "...", "playerAttributeReference": "..." }, "createdTime": "2024-01-01T00:00:00Z" } } ``` **Example:** ```bash curl -X PATCH -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/matchmaking-api/v1/matchmaking/server-attribute/{ATTRIBUTEID}" \ -H "Content-Type: application/json" \ -d '{ "attributeId": "string", "defaultValue": { "sourceCase": "Invalid", "constant": { "boolValue": "...", "numericValue": "...", "stringValue": "...", "type": "..." }, "playerAttributeReference": { "id": "..." } } }' ``` ### DELETE `/matchmaking-api/v1/matchmaking/server-attribute/{attributeId}` [BETA] Delete the ServerAttributeDefinition specified by attributeId. **Auth:** **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `attributeId` | path | `string` | Yes | | **Responses:** - `200`: Success → `DeleteMatchmakingServerAttributeDefinitionResponse` **Response fields** (`DeleteMatchmakingServerAttributeDefinitionResponse`) See [DeleteMatchmakingServerAttributeDefinitionResponse](#deletematchmakingserverattributedefinitionresponse) in Models. **Example:** ```bash curl -X DELETE -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/matchmaking-api/v1/matchmaking/server-attribute/{ATTRIBUTEID}" ``` ### GET `/matchmaking-api/v1/matchmaking/server-attributes/{universeId}` [BETA] List all ServerAttributeDefinitions of a universe. **Auth:** **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | | **Responses:** - `200`: Success → `ListMatchmakingServerAttributeDefinitionsResponse` **Response fields** (`ListMatchmakingServerAttributeDefinitionsResponse`) See [ListMatchmakingServerAttributeDefinitionsResponse](#listmatchmakingserverattributedefinitionsresponse) in Models. **Response example:** ```json { "serverAttributeSchema": [ { "id": "...", "universeId": "...", "name": "...", "dataType": "...", "defaultValue": "...", "createdTime": "..." } ] } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/matchmaking-api/v1/matchmaking/server-attributes/{UNIVERSEID}" ``` ### GET `/matchmaking-api/v1/matchmaking/universe/{universeId}/feature-flags` [BETA] Gets feature flags for a customized matchmaking for a given universe. **Auth:** **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | | **Responses:** - `200`: Success → `GetMatchmakingCustomizationFeatureFlagsResponse` **Response fields** (`GetMatchmakingCustomizationFeatureFlagsResponse`) See [GetMatchmakingCustomizationFeatureFlagsResponse](#getmatchmakingcustomizationfeatureflagsresponse) in Models. **Response example:** ```json { "featureFlags": { "isMatchmakingCustomizationAllowed": false, "isMatchmakingCustomizationExperimentsAllowed": false, "isMatchmakingTextChatSignalEnabled": false } } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://apis.roblox.com/matchmaking-api/v1/matchmaking/universe/{UNIVERSEID}/feature-flags" ``` ### POST `/messaging-service/v1/universes/{universeId}/topics/{topic}` [BETA] Publish a cross-server message to a universe Publish a message to a pre-defined topic of an experience, with the size of the message up to 1,024 characters (1 KB). Requires the **Publish** permission for API keys and the **universe-messaging-service:publish** scope for OAuth 2.0 apps. See [Cross-server messaging](/docs/en-us/cloud-services/cross-server-messaging.md#subscribe-users-to-receive-messages) for defining and subscribing users to a topic. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `universe-messaging-service:publish` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The identifier of the experience in which you want to send your messages to. You can [copy your experience's Universe ID](/docs/en-us/cloud/guides/usage-messaging.md#publishing-messages-to-live-servers) on **Creator Dashboard**. | | `topic` | path | `string` | Yes | The topic that you want to publish your message to, with up to 80 characters. | **Request Body:** `application/json-patch+json` — Type: `PublishRequest` See [PublishRequest](#publishrequest) in Models. **Request example:** ```json { "message": "string" } ``` **Responses:** - `200`: Returns an empty response body. - `400`: Invalid request. - `401`: The API key is not valid for this operation / You don't have the authorization. - `403`: Publishing is not allowed on this experience. - `500`: Server internal error / Unknown error. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 5000/minute, perOauth2Authorization: 5000/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/messaging-service/v1/universes/{UNIVERSEID}/topics/{TOPIC}" \ -H "Content-Type: application/json" \ -d '{ "message": "string" }' ``` ### GET `/ordered-data-stores/v1/universes/{universeId}/orderedDataStores/{orderedDataStore}/scopes/{scope}/entries` [BETA] Returns a list of entries from an ordered data store. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe.ordered-data-store.scope.entry:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `string` | Yes | The identifier of the experience with ordered data stores that you want to access. You can find your experience's universe ID on Creator Hub. | | `orderedDataStore` | path | `string` | Yes | The name of the target ordered data store. | | `scope` | path | `string` | Yes | The name of the data store scope. See [Scopes](/docs/en-us/cloud/guides/data-stores/request-handling.md#scopes). | | `max_page_size` | query | `integer` | No | The maximum number of entries to return. The service may return fewer than this value. The default value is `10`. The maximum value is `100`, and any input above 100 is coerced to `100`. | | `page_token` | query | `string` | No | A page token received from a previous `List` call. Provide this to retrieve the subsequent page. When paginating, all other parameters provided to `List` must match the call providing the page token. | | `order_by` | query | `string` | No | The enumeration direction. The order by default is ascending. Input a `desc` suffix for descending. | | `filter` | query | `string` | No | The range of qualifying values of entries to return. See [Filters](/docs/en-us/cloud/guides/data-stores/request-handling.md#filters). | **Responses:** - `200`: OK → `ListEntriesResponse` - `400`: Bad Request: invalid orderedDataStore, scope or entry name or encoding. - `403`: Forbidden: studio access to APIs is not allowed, incorrect API key or scope. - `429`: Too Many Requests. **Response fields** (`ListEntriesResponse`) See [ListEntriesResponse](#listentriesresponse) in Models. **Response example:** ```json { "entries": [ { "path": "...", "id": "...", "value": "..." } ], "nextPageToken": "string" } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 500/minute **Rate Limit Note:** See [Throttling](/docs/en-us/cloud/guides/data-stores/throttling.md). **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/ordered-data-stores/v1/universes/{UNIVERSEID}/orderedDataStores/{ORDEREDDATASTORE}/scopes/{SCOPE}/entries" ``` ### POST `/ordered-data-stores/v1/universes/{universeId}/orderedDataStores/{orderedDataStore}/scopes/{scope}/entries` [BETA] Creates a new entry with the content value provided. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe.ordered-data-store.scope.entry:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `string` | Yes | The identifier of the experience with ordered data stores that you want to access. You can find your experience's universe ID on Creator Hub. | | `orderedDataStore` | path | `string` | Yes | The name of the ordered data store. | | `scope` | path | `string` | Yes | The name of the data store scope. See [Scopes](/docs/en-us/cloud/guides/data-stores/request-handling.md#scopes). | | `id` | query | `string` | Yes | The name of the entry. | **Request Body:** `application/json` — Type: `CreateEntryRequest` See [CreateEntryRequest](#createentryrequest) in Models. **Request example:** ```json { "value": 0 } ``` > **Verify mutations:** If your API key lacks the required scope (`universe.ordered-data-store.scope.entry:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Entry` - `400`: Bad Request: invalid orderedDataStore, scope or entry name or encoding. - `403`: Forbidden: studio access to APIs is not allowed, incorrect API key or scope. - `404`: Not found. - `429`: Too Many Requests. **Response fields** (`Entry`) See [Entry](#entry) in Models. **Response example:** ```json { "path": "string", "id": "string", "value": 0 } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 500/minute **Rate Limit Note:** See [Throttling](/docs/en-us/cloud/guides/data-stores/throttling.md). **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/ordered-data-stores/v1/universes/{UNIVERSEID}/orderedDataStores/{ORDEREDDATASTORE}/scopes/{SCOPE}/entries?id={VALUE}" \ -H "Content-Type: application/json" \ -d '{ "value": 0 }' ``` ### GET `/ordered-data-stores/v1/universes/{universeId}/orderedDataStores/{orderedDataStore}/scopes/{scope}/entries/{entry}` [BETA] Gets and returns the specified entry. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe.ordered-data-store.scope.entry:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `string` | Yes | The identifier of the experience with ordered data stores that you want to access. You can find your experience's universe ID on Creator Hub. | | `orderedDataStore` | path | `string` | Yes | The name of the ordered data store. | | `scope` | path | `string` | Yes | The name of the data store scope. See [Scopes](/docs/en-us/cloud/guides/data-stores/request-handling.md#scopes). | | `entry` | path | `string` | Yes | The entry ID. | **Responses:** - `200`: OK → `Entry` - `400`: Bad Request: invalid orderedDataStore, scope or entry name or encoding. - `403`: Forbidden: studio access to APIs is not allowed, incorrect API key or scope. - `404`: Not found. - `429`: Too Many Requests. **Response fields** (`Entry`) See [Entry](#entry) in Models. **Response example:** ```json { "path": "string", "id": "string", "value": 0 } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 500/minute **Rate Limit Note:** See [Throttling](/docs/en-us/cloud/guides/data-stores/throttling.md). **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/ordered-data-stores/v1/universes/{UNIVERSEID}/orderedDataStores/{ORDEREDDATASTORE}/scopes/{SCOPE}/entries/{ENTRY}" ``` ### PATCH `/ordered-data-stores/v1/universes/{universeId}/orderedDataStores/{orderedDataStore}/scopes/{scope}/entries/{entry}` [BETA] Updates an entry value and returns the updated entry. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe.ordered-data-store.scope.entry:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `string` | Yes | The identifier of the experience with ordered data stores that you want to access. You can find your experience's universe ID on Creator Hub. | | `orderedDataStore` | path | `string` | Yes | The name of the ordered data store. | | `scope` | path | `string` | Yes | The name of the data store scope. See [Scopes](/docs/en-us/cloud/guides/data-stores/request-handling.md#scopes). | | `entry` | path | `string` | Yes | The entry ID. | | `allow_missing` | query | `boolean` | No | The flag to allow the creation of an entry if the entry doesn't exist. See [Allow missing flags](/docs/en-us/cloud/guides/data-stores/request-handling.md.md#allow-missing-flags). | **Request Body:** `application/json` — Type: `UpdateEntryRequest` See [UpdateEntryRequest](#updateentryrequest) in Models. **Request example:** ```json { "value": 0 } ``` > **Verify mutations:** If your API key lacks the required scope (`universe.ordered-data-store.scope.entry:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Entry` - `400`: Bad Request: invalid orderedDataStore, scope or entry name or encoding. - `403`: Forbidden: studio access to APIs is not allowed, incorrect API key or scope. - `404`: Not found. - `409`: Aborted. - `429`: Too Many Requests. **Response fields** (`Entry`) See [Entry](#entry) in Models. **Response example:** ```json { "path": "string", "id": "string", "value": 0 } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 500/minute **Rate Limit Note:** See [Throttling](/docs/en-us/cloud/guides/data-stores/throttling.md). **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/ordered-data-stores/v1/universes/{UNIVERSEID}/orderedDataStores/{ORDEREDDATASTORE}/scopes/{SCOPE}/entries/{ENTRY}" \ -H "Content-Type: application/json" \ -d '{ "value": 0 }' ``` ### DELETE `/ordered-data-stores/v1/universes/{universeId}/orderedDataStores/{orderedDataStore}/scopes/{scope}/entries/{entry}` [BETA] Deletes the specified entry. Unlike standard data stores, which mark entries for deletion, ordered data store entries are deleted immediately. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe.ordered-data-store.scope.entry:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `string` | Yes | The identifier of the experience with ordered data stores that you want to access. You can find your experience's universe ID on Creator Hub. | | `orderedDataStore` | path | `string` | Yes | The name of the ordered data store. | | `scope` | path | `string` | Yes | The name of the data store scope. See [Scopes](/docs/en-us/cloud/guides/data-stores/request-handling.md#scopes). | | `entry` | path | `string` | Yes | The entry ID. | > **Verify mutations:** If your API key lacks the required scope (`universe.ordered-data-store.scope.entry:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: Success: the entry was successfully deleted or didn't exist. - `400`: Bad Request: invalid orderedDataStore, scope or entry name or encoding. - `403`: Forbidden: Studio access to APIs is not allowed, incorrect API key or scope. - `404`: Not found. - `429`: Too Many Requests. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 500/minute **Rate Limit Note:** See [Throttling](/docs/en-us/cloud/guides/data-stores/throttling.md). **Example:** ```bash curl -X DELETE -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/ordered-data-stores/v1/universes/{UNIVERSEID}/orderedDataStores/{ORDEREDDATASTORE}/scopes/{SCOPE}/entries/{ENTRY}" ``` ### POST `/ordered-data-stores/v1/universes/{universeId}/orderedDataStores/{orderedDataStore}/scopes/{scope}/entries/{entry}:increment` [BETA] Increments the value of the key by the provided amount and returns the updated entry. Known issue: Entry values can increment past the valid range and this may persist in the backend. Returned values will clamp to the valid range. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe.ordered-data-store.scope.entry:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `string` | Yes | The identifier of the experience with ordered data stores that you want to access. You can find your experience's universe ID on Creator Hub. | | `orderedDataStore` | path | `string` | Yes | The name of the ordered data store. | | `scope` | path | `string` | Yes | The name of the data store scope. See [Scopes](/docs/en-us/cloud/guides/data-stores/request-handling.md#scopes). | | `entry` | path | `string` | Yes | The entry ID. | **Request Body:** `application/json` — Type: `IncrementEntryRequest` See [IncrementEntryRequest](#incremententryrequest) in Models. **Request example:** ```json { "amount": 0 } ``` > **Verify mutations:** If your API key lacks the required scope (`universe.ordered-data-store.scope.entry:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Entry` - `400`: Bad Request: invalid orderedDataStore, scope or entry name or encoding. - `403`: Forbidden: studio access to APIs is not allowed, incorrect API key or scope. - `404`: Not found. - `429`: Too Many Requests. **Response fields** (`Entry`) See [Entry](#entry) in Models. **Response example:** ```json { "path": "string", "id": "string", "value": 0 } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 500/minute **Rate Limit Note:** See [Throttling](/docs/en-us/cloud/guides/data-stores/throttling.md). **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/ordered-data-stores/v1/universes/{UNIVERSEID}/orderedDataStores/{ORDEREDDATASTORE}/scopes/{SCOPE}/entries/{ENTRY}:increment" \ -H "Content-Type: application/json" \ -d '{ "amount": 0 }' ``` ### GET `/server-management/v1/universes/{universeId}/places/{placeId}/game-servers:filter-options` [BETA] Gets available filter options for game servers. **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `universe:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe ID | | `placeId` | path | `integer` | Yes | The place ID | | `Filter` | query | `any` | No | The specific filter to query. If null or empty, the endpoint returns all available filters. | **Responses:** - `200`: Success → `FilterOptionsResponse` - `400`: Bad Request → `ServerManagementService.ProblemDetails` - `401`: Unauthorized → `ServerManagementService.ProblemDetails` - `403`: Forbidden → `ServerManagementService.ProblemDetails` **Response fields** (`FilterOptionsResponse`) See [FilterOptionsResponse](#filteroptionsresponse) in Models. **Response example:** ```json { "filters": "..." } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/server-management/v1/universes/{UNIVERSEID}/places/{PLACEID}/game-servers:filter-options" ``` ### GET `/server-management/v1/universes/{universeId}/places/{placeId}/versions/{versionNumber}/game-servers` [BETA] Lists game servers for a specific place version. **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `universe:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe ID | | `placeId` | path | `integer` | Yes | The place ID | | `versionNumber` | path | `string` | Yes | The version number | | `MaxPageSize` | query | `integer` | No | The maximum number of game servers to return. The service might return fewer than this value. If unspecified, at most 25 game servers are returned. The maximum value is 100 and higher values are set to 100. | | `PageToken` | query | `string` | No | A page token, received from a previous call, to retrieve a subsequent page. When paginating, all other parameters provided to the subsequent call must match the call that provided the page token. | | `OrderBy` | query | `string` | No | Sorting is supported only for a single field at a time. Example: "orderBy": "uptime" Defaults to ascending, but descending ordering is also supported by including the " desc" suffix. For example: "orderBy": "uptime desc". | | `Filter` | query | `string` | No | This field may be set in order to filter the resources returned. - CEL filtering is supported on all fields. - Supported operators &&, <, <=, >, >=, ==, and `in` | **Responses:** - `200`: Success → `ServerManagementService.ListGameServersResponse` - `400`: Bad Request → `ServerManagementService.ProblemDetails` - `401`: Unauthorized → `ServerManagementService.ProblemDetails` - `403`: Forbidden → `ServerManagementService.ProblemDetails` **Response fields** (`ServerManagementService.ListGameServersResponse`) See [ServerManagementService.ListGameServersResponse](#servermanagementservice-listgameserversresponse) in Models. **Response example:** ```json { "gameServers": [ { "placeId": "...", "placeVersion": "...", "jobId": "...", "engineVersion": "...", "createTime": "...", "uptime": "..." } ], "nextPageToken": "string", "previousPageToken": "string", "totalCount": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/server-management/v1/universes/{UNIVERSEID}/places/{PLACEID}/versions/{VERSIONNUMBER}/game-servers" ``` ### GET `/server-management/v1/universes/{universeId}/places/{placeId}/versions/{versionNumber}/game-servers/{jobId}/logs` [BETA] Lists game server logs for a specific server job id **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `universe:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe id | | `placeId` | path | `integer` | Yes | The place id | | `versionNumber` | path | `string` | Yes | The version number | | `jobId` | path | `string` | Yes | The server job id | | `MaxPageSize` | query | `integer` | No | The maximum number of game servers to return. The service might return fewer than this value. If unspecified, at most 25 game servers are returned. The maximum value is 100 and higher values are set to 100. | | `PageToken` | query | `string` | No | A page token, received from a previous call, to retrieve a subsequent page. When paginating, all other parameters provided to the subsequent call must match the call that provided the page token. | | `OrderBy` | query | `string` | No | Sorting is supported only for a single field at a time. Example: "orderBy": "uptime" Defaults to ascending, but descending ordering is also supported by including the " desc" suffix. For example: "orderBy": "uptime desc". | | `Filter` | query | `string` | No | This field may be set in order to filter the resources returned. - CEL filtering is supported on all fields. - Supported operators &&, <, <=, >, >=, ==, and `in` | **Responses:** - `200`: Success → `ListGameServerLogsResponse` - `400`: Bad Request → `ServerManagementService.ProblemDetails` - `401`: Unauthorized → `ServerManagementService.ProblemDetails` - `403`: Forbidden → `ServerManagementService.ProblemDetails` **Response fields** (`ListGameServerLogsResponse`) See [ListGameServerLogsResponse](#listgameserverlogsresponse) in Models. **Response example:** ```json { "gameServerLogs": [ { "messageTimestampMs": "...", "universeId": "...", "placeId": "...", "placeVersion": "...", "jobId": "...", "severity": "..." } ], "nextPageToken": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/server-management/v1/universes/{UNIVERSEID}/places/{PLACEID}/versions/{VERSIONNUMBER}/game-servers/{JOBID}/logs" ``` ### GET `/server-management/v1/universes/{universeId}/restarts` [BETA] List restart statuses for a universe. **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `universe:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe ID | **Responses:** - `200`: Success → `ListRestartStatusesResponse` - `400`: Bad Request → `ServerManagementService.ProblemDetails` - `401`: Unauthorized → `ServerManagementService.ProblemDetails` - `403`: Forbidden → `ServerManagementService.ProblemDetails` **Response fields** (`ListRestartStatusesResponse`) See [ListRestartStatusesResponse](#listrestartstatusesresponse) in Models. **Response example:** ```json { "restartStatuses": "..." } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/server-management/v1/universes/{UNIVERSEID}/restarts" ``` ### POST `/server-management/v1/universes/{universeId}/restarts` [BETA] Launch a game server restart for a universe. **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `universe:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe ID | **Request Body:** `application/json` — Type: `any` > **Verify mutations:** If your API key lacks the required scope (`universe:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: Success → `LaunchRestartResponse` - `400`: Bad Request → `ServerManagementService.ProblemDetails` - `401`: Unauthorized → `ServerManagementService.ProblemDetails` - `403`: Forbidden → `ServerManagementService.ProblemDetails` **Response fields** (`LaunchRestartResponse`) See [LaunchRestartResponse](#launchrestartresponse) in Models. **Response example:** ```json { "id": "string", "playersImpacted": 0, "instancesImpacted": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/server-management/v1/universes/{UNIVERSEID}/restarts" \ -H "Content-Type: application/json" \ -d '"..."' ``` ### GET `/server-management/v1/universes/{universeId}/restarts:forecast` [BETA] Forecast the impact of restarting game servers for a universe. **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `universe:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe ID | **Responses:** - `200`: Success → `ForecastRestartResponse` - `400`: Bad Request → `ServerManagementService.ProblemDetails` - `401`: Unauthorized → `ServerManagementService.ProblemDetails` - `403`: Forbidden → `ServerManagementService.ProblemDetails` **Response fields** (`ForecastRestartResponse`) See [ForecastRestartResponse](#forecastrestartresponse) in Models. **Response example:** ```json { "placeForecasts": "..." } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/server-management/v1/universes/{UNIVERSEID}/restarts:forecast" ``` ### GET `/toolbox-service/v1/saves` [BETA] Gets saves from a collection. **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `creator-store-save:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `targetType` | query | `any` | No | The asset type to filter by | | `targetId` | query | `integer` | No | The ID of the asset to filter by. If provided, targetType must also be provided. | | `collectionName` | query | `string` | No | Custom collections are not currently supported. This field should be omitted. | | `sortBy` | query | `any` | No | The field to sort by | | `sortDirection` | query | `any` | No | The direction to sort by | | `limit` | query | `integer` | No | The maximum number of saves to return | | `page` | query | `integer` | No | The page number to return, starting from 1 | | `keyword` | query | `string` | No | The keyword to filter by | | `hideOwnedAssets` | query | `boolean` | No | Whether to hide owned assets | **Responses:** - `200`: Success → `GetSavesResponse` - `400`: Bad Request → `any` **Response fields** (`GetSavesResponse`) See [GetSavesResponse](#getsavesresponse) in Models. **Response example:** ```json { "totalCount": 0, "saves": [ { "owned": "...", "dateSaved": "...", "creatorStoreAsset": "..." } ] } ``` **Rate Limits:** perApiKeyOwner: 200/minute, perOauth2Authorization: 200/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/toolbox-service/v1/saves" ``` ### POST `/toolbox-service/v1/saves` [BETA] Creates a save. **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `creator-store-save:write` **Request Body:** `application/json-patch+json` — Type: `CreateSaveRequest` See [CreateSaveRequest](#createsaverequest) in Models. **Request example:** ```json { "targetType": "...", "targetId": 0, "collectionName": "string" } ``` > **Verify mutations:** If your API key lacks the required scope (`creator-store-save:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `201`: Created - `400`: Bad Request → `any` - `404`: Not Found → `any` - `409`: Conflict → `any` **Rate Limits:** perApiKeyOwner: 200/minute, perOauth2Authorization: 200/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/toolbox-service/v1/saves" \ -H "Content-Type: application/json" \ -d '{ "targetType": "...", "targetId": 0, "collectionName": "string" }' ``` ### DELETE `/toolbox-service/v1/saves` [BETA] Deletes a save. **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `creator-store-save:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `targetType` | query | `any` | No | The type of asset being deleted | | `targetId` | query | `integer` | No | The ID of the asset being deleted | | `collectionName` | query | `string` | No | Custom collections are not currently supported. This field should be omitted. | > **Verify mutations:** If your API key lacks the required scope (`creator-store-save:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `204`: No Content - `400`: Bad Request → `any` - `404`: Not Found → `any` **Rate Limits:** perApiKeyOwner: 200/minute, perOauth2Authorization: 200/minute **Example:** ```bash curl -X DELETE -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/toolbox-service/v1/saves" ``` ### POST `/toolbox-service/v1/saves:bulkDelete` [BETA] Bulk deletes saves. Max of 5000 saves per request. **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `creator-store-save:write` **Request Body:** `application/json-patch+json` — Type: `BulkDeleteSavesRequest` See [BulkDeleteSavesRequest](#bulkdeletesavesrequest) in Models. **Request example:** ```json { "targets": [ { "targetType": "...", "targetId": "...", "collectionName": "..." } ] } ``` > **Verify mutations:** If your API key lacks the required scope (`creator-store-save:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: Success → `BulkDeleteSavesResponse` - `400`: Bad Request → `any` - `404`: Not Found → `any` **Response fields** (`BulkDeleteSavesResponse`) See [BulkDeleteSavesResponse](#bulkdeletesavesresponse) in Models. **Response example:** ```json { "deletedCount": 0 } ``` **Rate Limits:** perApiKeyOwner: 200/minute, perOauth2Authorization: 200/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/toolbox-service/v1/saves:bulkDelete" \ -H "Content-Type: application/json" \ -d '{ "targets": [ { "targetType": "...", "targetId": "...", "collectionName": "..." } ] }' ``` ### GET `/toolbox-service/v2/assets/{id}` [BETA] Get Creator Store Asset Details Get details for a single Creator Store asset. **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `creator-store-product:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `id` | path | `integer` | Yes | The asset ID to retrieve details for. | **Responses:** - `200`: Success → `CreatorStoreAsset` - `403`: Forbidden → `any` - `404`: Not Found → `any` - `429`: Too Many Requests → `any` **Response fields** (`CreatorStoreAsset`) See [CreatorStoreAsset](#creatorstoreasset) in Models. **Response example:** ```json { "voting": "...", "creator": "...", "creatorStoreProduct": "...", "asset": "..." } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 1000/minute, perOauth2Authorization: 1000/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/toolbox-service/v2/assets/{ID}" ``` ### GET `/toolbox-service/v2/assets:search` [BETA] Search Creator Store Assets Search Creator Store for assets. **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `creator-store-product:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `searchCategoryType` | query | `any` | No | The asset type to search within. Optional when categoryPath is present in the request; otherwise required. | | `query` | query | `string` | No | The search terms used to filter the results. | | `modelSubTypes` | query | `ModelSubType[]` | No | When searching for models, the subtypes associated with the search results. | | `excludedModelSubTypes` | query | `ModelSubType[]` | No | When searching for models, the subtypes not associated with the search results. | | `creator` | query | `string` | No | Deprecated: Please refer to the 'userId' and 'groupId' properties instead. The creator type and ID. E.g. "user/123" or "group/456" | | `userId` | query | `integer` | No | The User ID of the creator. Only one of 'userId' and 'groupId' can be present in a query. | | `groupId` | query | `integer` | No | The Group ID of the creator. Only one of 'userId' and 'groupId' can be present in a query. | | `pageToken` | query | `string` | No | The identifier for the desired search results page. Only one of 'pageNumber' and 'pageToken' can be present in a query. | | `pageNumber` | query | `integer` | No | The page number to retrieve, starting from 0. Only one of 'pageNumber' and 'pageToken' can be present in a query. | | `maxPageSize` | query | `integer` | No | The number of assets to be returned. Cannot be larger than 100. | | `sortDirection` | query | `any` | No | The sort direction of the search results. | | `sortCategory` | query | `any` | No | The category to sort the search results by. | | `audioMinDurationSeconds` | query | `integer` | No | When searching for audio, the minimum duration of the audio assets. If included, must be greater than or equal to 0. | | `audioMaxDurationSeconds` | query | `integer` | No | When searching for audio, the maximum duration of the audio assets. If included, must be greater than or equal to 0. | | `audioArtist` | query | `string` | No | When searching for audio, the artist name of the audio assets. | | `audioAlbum` | query | `string` | No | When searching for audio, the album name of the audio assets. | | `audioTypes` | query | `SearchAudioTypeModel[]` | No | When searching for audio, the type of the audio assets. | | `includedInstanceTypes` | query | `ModelInstanceType[]` | No | When searching for models, this filters that the following [Instance](https://create.roblox.com/docs/reference/engine/classes/Instance) types are included in the model. | | `includeOnlyVerifiedCreators` | query | `boolean` | No | Whether the results should only include assets created by verified creators. | | `minPriceCents` | query | `integer` | No | The minimum price of the asset in cents. If included, must be greater than or equal to 0. | | `maxPriceCents` | query | `integer` | No | The maximum price of the asset in cents. If included, must be greater than or equal to 0. | | `facets` | query | `string[]` | No | Additional keywords to query by. | | `tags` | query | `string[]` | No | The tags used to filter the results. | | `categoryPath` | query | `string` | No | | | `searchView` | query | `any` | No | Indicates which fields will be populated in the response. | | `musicChartType` | query | `any` | No | Indicates which music charts to filter from. | | `swimlane` | query | `string` | No | Indicates which swimlane to filter from. | **Responses:** - `200`: Success → `SearchCreatorStoreAssetsResponse` - `400`: Bad Request → `any` - `429`: Too Many Requests → `any` - `500`: Server Error **Response fields** (`SearchCreatorStoreAssetsResponse`) See [SearchCreatorStoreAssetsResponse](#searchcreatorstoreassetsresponse) in Models. **Response example:** ```json { "nextPageToken": "string", "queryFacets": "...", "creatorStoreAssets": [ { "voting": "...", "creator": "...", "creatorStoreProduct": "...", "asset": "..." } ], "totalResults": 0, "queryCorrection": "...", "filteredKeyword": "string" } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). **Rate Limits:** perApiKeyOwner: 1000/minute, perOauth2Authorization: 1000/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/toolbox-service/v2/assets:search" ``` ### POST `/toolbox-service/v2/assets:search` [BETA] Search Creator Store Assets Search Creator Store for assets. **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `creator-store-product:read` **Request Body:** `application/json-patch+json` — Type: `SearchCreatorStoreAssetsRequest` See [SearchCreatorStoreAssetsRequest](#searchcreatorstoreassetsrequest) in Models. **Request example:** ```json { "searchCategoryType": "...", "query": "string", "image": "string", "userId": 0, "groupId": 0, "pageToken": "string" } ``` **Responses:** - `200`: Success → `SearchCreatorStoreAssetsResponse` - `400`: Bad Request → `any` - `429`: Too Many Requests → `any` - `500`: Server Error **Response fields** (`SearchCreatorStoreAssetsResponse`) See [SearchCreatorStoreAssetsResponse](#searchcreatorstoreassetsresponse) in Models. **Response example:** ```json { "nextPageToken": "string", "queryFacets": "...", "creatorStoreAssets": [ { "voting": "...", "creator": "...", "creatorStoreProduct": "...", "asset": "..." } ], "totalResults": 0, "queryCorrection": "...", "filteredKeyword": "string" } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). **Rate Limits:** perApiKeyOwner: 1000/minute, perOauth2Authorization: 1000/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/toolbox-service/v2/assets:search" \ -H "Content-Type: application/json" \ -d '{ "searchCategoryType": "...", "query": "string", "image": "string", "userId": 0, "groupId": 0, "pageToken": "string" }' ``` ### POST `/universes/v1/{universeId}/places/{placeId}/versions` [BETA] Publish a new place or update an existing place with a new version. Provide a RBXL or RBXLX file in the data-binary. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe-places:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The identifier of the experience in which you want to publish your place to. You can [copy your experience's Universe ID](/docs/en-us/cloud/guides/usage-place-publishing.md#publishing-a-place) on **Creator Dashboard**. | | `placeId` | path | `integer` | Yes | The identifier of your place. See [Publishing places with API keys](/docs/en-us/cloud/guides/usage-place-publishing.md) on obtaining a Place ID. | | `versionType` | query | `string` | No | Can only be either: - `Saved`: the place file should be saved, but not published. - `Published`: the place file should be saved and published. | > **Verify mutations:** If your API key lacks the required scope (`universe-places:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: Returns the published place's version number. → `string` - `400`: Invalid request / Invalid file content. - `401`: API key not valid for operation, user does not have authorization. - `403`: Publish not allowed on place. - `404`: Place or universe does not exist. - `409`: Place not part of the universe. - `500`: Server internal error / Unknown error. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 30/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/universes/v1/{UNIVERSEID}/places/{PLACEID}/versions" ``` ### GET `/v1/account/settings/account-country` Get a user's current account country setting. **Server:** `https://accountsettings.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.AccountSettings.Api.Models.Response.AccountCountrySettingsResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.AccountSettings.Api.Models.Response.AccountCountrySettingsResponse`) See [Roblox.AccountSettings.Api.Models.Response.AccountCountrySettingsResponse](#roblox-accountsettings-api-models-response-accountcountrysettingsresponse) in Models. **Response example:** ```json { "value": { "countryName": "string", "subdivisionIso": "string", "localizedSubdivision": "string", "localizedName": "string", "countryId": 0 } } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountsettings.roblox.com/v1/account/settings/account-country" ``` ### POST `/v1/account/settings/account-country` Updates the user's account country. **Server:** `https://accountsettings.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.AccountSettings.Api.UpdateAccountCountryRequest` See [Roblox.AccountSettings.Api.UpdateAccountCountryRequest](#roblox-accountsettings-api-updateaccountcountryrequest) in Models. **Request example:** ```json { "targetCountryId": 0 } ``` **Responses:** - `200`: OK → `Roblox.AccountSettings.Api.Models.Response.UpdateAccountCountryResponse` - `400`: 1: InvalidRequest - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: OperationNotPermitted - `404`: 2: OperationNotPermitted - `500`: 0: Unknown **Response fields** (`Roblox.AccountSettings.Api.Models.Response.UpdateAccountCountryResponse`) See [Roblox.AccountSettings.Api.Models.Response.UpdateAccountCountryResponse](#roblox-accountsettings-api-models-response-updateaccountcountryresponse) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountsettings.roblox.com/v1/account/settings/account-country" \ -H "Content-Type: application/json" \ -d '{ "targetCountryId": 0 }' ``` ### GET `/v1/account/settings/metadata` Returns metadata used by the account settings page **Server:** `https://accountsettings.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.AccountSettings.Api.Models.AccountsSettingsMetadataModel` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.AccountSettings.Api.Models.AccountsSettingsMetadataModel`) See [Roblox.AccountSettings.Api.Models.AccountsSettingsMetadataModel](#roblox-accountsettings-api-models-accountssettingsmetadatamodel) in Models. **Response example:** ```json { "IsAccountsRestrictionsSpamBugFixEnabled": false, "MaximumParentalControlsMonthlySpendLimitInUSD": 0, "IsParentalMonthlyLimitInUIEnabled": false, "IsParentalNotificationSettingsInUIEnabled": false, "IsContentControlsEnabled": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountsettings.roblox.com/v1/account/settings/metadata" ``` ### GET `/v1/announcements` Migrate from RobloxWebsite project, return news notification for Private Message page **Server:** `https://privatemessages.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.PrivateMessages.Api.Models.GetAnnouncementsResponse` - `400`: 2: Message does not exist or the current user is not authorized to view it. - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.PrivateMessages.Api.Models.GetAnnouncementsResponse`) See [Roblox.PrivateMessages.Api.Models.GetAnnouncementsResponse](#roblox-privatemessages-api-models-getannouncementsresponse) in Models. **Response example:** ```json { "collection": [ { "id": "...", "sender": "...", "subject": "...", "body": "...", "created": "...", "updated": "..." } ], "totalCollectionSize": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://privatemessages.roblox.com/v1/announcements" ``` ### GET `/v1/announcements/metadata` **Server:** `https://privatemessages.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.PrivateMessages.Api.Models.AnnouncementsMetadataResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.PrivateMessages.Api.Models.AnnouncementsMetadataResponse`) See [Roblox.PrivateMessages.Api.Models.AnnouncementsMetadataResponse](#roblox-privatemessages-api-models-announcementsmetadataresponse) in Models. **Response example:** ```json { "numOfAnnouncements": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://privatemessages.roblox.com/v1/announcements/metadata" ``` ### GET `/v1/asset` **Server:** `https://assetdelivery.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `Accept-Encoding` | header | `string` | Yes | | | `Roblox-Place-Id` | header | `integer` | Yes | | | `AssetType` | header | `string` | Yes | | | `Accept` | header | `string` | Yes | | | `AssetFormat` | header | `string` | Yes | | | `Roblox-AssetFormat` | header | `string` | Yes | | | `id` | query | `integer` | No | | | `userAssetId` | query | `integer` | No | | | `assetVersionId` | query | `integer` | No | | | `version` | query | `integer` | No | | | `universeId` | query | `integer` | No | | | `clientInsert` | query | `integer` | No | | | `scriptinsert` | query | `integer` | No | | | `modulePlaceId` | query | `integer` | No | | | `serverplaceid` | query | `string` | No | | | `assetName` | query | `string` | No | | | `hash` | query | `string` | No | | | `marAssetHash` | query | `string` | No | | | `marCheckSum` | query | `string` | No | | | `expectedAssetType` | query | `string` | No | | | `skipSigningScripts` | query | `boolean` | No | | | `permissionContext` | query | `string` | No | | | `doNotFallbackToBaselineRepresentation` | query | `boolean` | No | | | `contentRepresentationPriorityList` | query | `string` | No | | | `assetResolutionMode` | query | `string` | No | | | `accessContext` | query | `string` | No | | | `usageContext` | query | `integer` | No | | **Responses:** - `200`: OK **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://assetdelivery.roblox.com/v1/asset" ``` ### GET `/v1/asset-quotas` List asset quotas of the given resource type and asset type. **Server:** `https://publish.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `resourceType` | query | `string` | Yes | Resource type of the asset quota | | `assetType` | query | `string` | Yes | Asset type of the asset quota | | `useDummyData` | query | `boolean` | No | Use dummy data for testing. This is for internal use only | **Responses:** - `200`: OK → `Roblox.Publish.Api.AssetQuotasResponse` - `400`: 7: The asset type is not appropriate for this request. 8: The resource type is not appropriate for this request. - `401`: 0: Authorization has been denied for this request. - `500`: 0: Reserved for base level errors. Do not use in your endpoint directly, do not document. **Response fields** (`Roblox.Publish.Api.AssetQuotasResponse`) See [Roblox.Publish.Api.AssetQuotasResponse](#roblox-publish-api-assetquotasresponse) in Models. **Response example:** ```json { "quotas": [ { "duration": "...", "usage": "...", "capacity": "...", "expirationTime": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://publish.roblox.com/v1/asset-quotas?resourceType={VALUE}&assetType={VALUE}" ``` ### GET `/v1/asset-thumbnail-animated` [STABLE] Thumbnails asset animated. **Server:** `https://thumbnails.roblox.com` **Auth:** **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetId` | query | `integer` | Yes | The asset id. | | `Roblox-Place-Id` | header | `integer` | No | (optional) placeid | **Responses:** - `200`: OK - `400`: 4: The requested Ids are invalid, of an invalid type or missing. **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://thumbnails.roblox.com/v1/asset-thumbnail-animated?assetId={VALUE}" ``` ### GET `/v1/asset-to-category` Lists a mapping for assets to category IDs to convert from inventory ID to catalog ID. Creates a mapping to link 'Get More' button in inventory page to the relevant catalog page. **Server:** `https://catalog.roblox.com` **Auth:** **Responses:** - `200`: OK → `object` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://catalog.roblox.com/v1/asset-to-category" ``` ### GET `/v1/asset-to-subcategory` Lists a mapping for assets to subcategory IDs to convert from inventory ID to catalog ID. Creates a mapping to link 'Get More' button in inventory page to the relevant catalog page. **Server:** `https://catalog.roblox.com` **Auth:** **Responses:** - `200`: OK → `object` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://catalog.roblox.com/v1/asset-to-subcategory" ``` ### GET `/v1/assetId/{assetId}` Retrieves an asset by its ID **Server:** `https://assetdelivery.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetId` | path | `integer` | Yes | The ID of the asset to retrieve. | | `Accept-Encoding` | header | `string` | Yes | | | `Roblox-Place-Id` | header | `integer` | Yes | | | `AssetType` | header | `string` | Yes | | | `Accept` | header | `string` | Yes | | | `AssetFormat` | header | `string` | Yes | | | `Roblox-AssetFormat` | header | `string` | Yes | | | `skipSigningScripts` | query | `boolean` | No | | | `clientInsert` | query | `integer` | No | | | `scriptinsert` | query | `integer` | No | | | `modulePlaceId` | query | `integer` | No | | | `serverplaceid` | query | `integer` | No | | | `expectedAssetType` | query | `string` | No | | | `doNotFallbackToBaselineRepresentation` | query | `boolean` | No | | | `contentRepresentationPriorityList` | query | `string` | No | | | `accessContext` | query | `string` | No | | | `usageContext` | query | `integer` | No | | **Responses:** - `200`: OK → `Roblox.Web.Assets.AssetResponseItemV1` **Response fields** (`Roblox.Web.Assets.AssetResponseItemV1`) See [Roblox.Web.Assets.AssetResponseItemV1](#roblox-web-assets-assetresponseitemv1) in Models. **Response example:** ```json { "location": "string", "errors": [ { "Code": "...", "Message": "...", "CustomErrorCode": "..." } ], "requestId": "string", "isArchived": false, "assetTypeId": 0, "contentRepresentationSpecifier": { "format": "string", "majorVersion": "string", "fidelity": "string", "skipGenerationIfNotExist": false } } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://assetdelivery.roblox.com/v1/assetId/{ASSETID}" ``` ### GET `/v1/assetId/{assetId}/version/{versionNumber}` Retrieves an asset by its ID and version number. **Server:** `https://assetdelivery.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetId` | path | `integer` | Yes | The ID of the asset to retrieve. | | `versionNumber` | path | `integer` | Yes | The version of the asset to retrieve. | | `Accept-Encoding` | header | `string` | Yes | | | `Roblox-Place-Id` | header | `integer` | Yes | | | `AssetType` | header | `string` | Yes | | | `Accept` | header | `string` | Yes | | | `AssetFormat` | header | `string` | Yes | | | `Roblox-AssetFormat` | header | `string` | Yes | | | `skipSigningScripts` | query | `boolean` | No | | | `clientInsert` | query | `integer` | No | | | `scriptinsert` | query | `integer` | No | | | `modulePlaceId` | query | `integer` | No | | | `serverplaceid` | query | `integer` | No | | | `expectedAssetType` | query | `string` | No | | | `doNotFallbackToBaselineRepresentation` | query | `boolean` | No | | | `contentRepresentationPriorityList` | query | `string` | No | | | `accessContext` | query | `string` | No | | | `usageContext` | query | `integer` | No | | **Responses:** - `200`: OK → `Roblox.Web.Assets.AssetResponseItemV1` **Response fields** (`Roblox.Web.Assets.AssetResponseItemV1`) See [Roblox.Web.Assets.AssetResponseItemV1](#roblox-web-assets-assetresponseitemv1) in Models. **Response example:** ```json { "location": "string", "errors": [ { "Code": "...", "Message": "...", "CustomErrorCode": "..." } ], "requestId": "string", "isArchived": false, "assetTypeId": 0, "contentRepresentationSpecifier": { "format": "string", "majorVersion": "string", "fidelity": "string", "skipGenerationIfNotExist": false } } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://assetdelivery.roblox.com/v1/assetId/{ASSETID}/version/{VERSIONNUMBER}" ``` ### GET `/v1/assets#ThumbnailsApi` [STABLE] Thumbnails assets. **Server:** `https://thumbnails.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetIds` | query | `integer[]` | Yes | The asset ids. | | `Roblox-Place-Id` | header | `integer` | No | (optional) placeid | | `returnPolicy` | query | `PlaceHolder \| ForcePlaceHolder \| AutoGenerated \| ForceAutoGenerated` | No | Optional policy to use in selecting thumbnail to return (default = PlaceHolder). Valid values: `PlaceHolder`, `ForcePlaceHolder`, `AutoGenerated`, `ForceAutoGenerated` | | `size` | query | `string enum (30 values)` | No | The thumbnail size, formatted widthxheight Valid values: `30x30`, `42x42`, `50x50`, `60x62`, `75x75`, `110x110`, `140x140`, `150x150`, `160x100`, `160x600`, `250x250`, `256x144`, `300x250`, `304x166`, `384x216`, `396x216`, `420x420`, `480x270`, `512x512`, `576x324`, `700x700`, `728x90`, `768x432`, `1200x80`, `330x110`, `660x220`, `1320x440`, `720x228`, `1440x456`, `930x480` | | `format` | query | `Png \| Jpeg \| Webp` | No | The thumbnail format Valid values: `Png`, `Jpeg`, `Webp` | | `isCircular` | query | `true \| false` | No | The circle thumbnail output parameter, true or false Valid values: `true`, `false` | **Responses:** - `200`: OK - `400`: 1: There are too many requested Ids. 2: The requested image format is invalid. Please see documentation for valid thumbnail format parameter name and values. 3: The requested size is invalid. Please see documentation for valid thumbnail size parameter name and format. 4: The requested Ids are invalid, of an invalid type or missing. 8: The requested return policy is invalid (must be PlaceHolder, AutoGenerated or ForceAutoGenerated). 10: Circular thumbnail requests are not allowed - `403`: 9: User not authorized to use AutoGenerated or ForceAutoGenerated return policies. **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://thumbnails.roblox.com/v1/assets#ThumbnailsApi?assetIds={VALUE}" ``` ### GET `/v1/assets-thumbnail-3d` [BETA] Thumbnails assets. **Server:** `https://thumbnails.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `thumbnail:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetId` | query | `integer` | Yes | The asset id. | | `useGltf` | query | `boolean` | No | (optional) formatType | | `Roblox-Place-Id` | header | `integer` | No | (optional) placeid | **Responses:** - `200`: OK - `400`: 4: The requested Ids are invalid, of an invalid type or missing. **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://thumbnails.roblox.com/v1/assets-thumbnail-3d?assetId={VALUE}" ``` ### POST `/v1/assets/batch` **Server:** `https://assetdelivery.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `Roblox-Place-Id` | header | `integer` | Yes | | | `Accept` | header | `string` | Yes | | | `Roblox-Browser-Asset-Request` | header | `string` | Yes | | **Request Body:** `application/json` — Type: `Roblox.Web.Assets.BatchAssetRequestItem[]` See [Roblox.Web.Assets.BatchAssetRequestItem](#roblox-web-assets-batchassetrequestitem) in Models. **Responses:** - `200`: OK → `Roblox.Web.Assets.AssetResponseItemV1[]` **Response fields** (`Roblox.Web.Assets.AssetResponseItemV1[]`) See [Roblox.Web.Assets.AssetResponseItemV1](#roblox-web-assets-assetresponseitemv1) in Models. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://assetdelivery.roblox.com/v1/assets/batch" \ -H "Content-Type: application/json" \ -d '[ { "assetName": "string", "assetType": "string", "clientInsert": false, "placeId": 0, "requestId": "string", "scriptInsert": false } ]' ``` ### GET `/v1/assets/voting` *(deprecated)* Gets the voting information of the given assets Please use toolbox service to get asset voting information. **Server:** `https://develop.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetIds` | query | `integer[]` | Yes | The ids of the Roblox.Platform.Assets.IAsset. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Api.Develop.Models.Response.AssetVotingModel_` **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Api.Develop.Models.Response.AssetVotingModel_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Api.Develop.Models.Response.AssetVotingModel_](#roblox-web-webapi-models-apiarrayresponse-roblox-api-develop-models-response-assetvotingmodel-) in Models. **Response example:** ```json { "data": [ { "assetId": "...", "hasUserVoted": "...", "canUserVote": "...", "shouldShowVotes": "...", "upVotes": "...", "downVotes": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/assets/voting?assetIds={VALUE}" ``` ### GET `/v1/assets/{assetId}/bundles` Lists the bundles a particular asset belongs to. Use the Id of the last bundle in the response to get the next page. **Server:** `https://catalog.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetId` | path | `integer` | Yes | | | `limit` | query | `10 \| 25 \| 50 \| 100` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `Asc \| Desc` | No | The order the results are sorted in. Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Catalog.Api.BundleDetailsModel_` - `400`: 1: Invalid assetId 4: Invalid Cursor. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Catalog.Api.BundleDetailsModel_`) See [Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Catalog.Api.BundleDetailsModel_](#roblox-web-webapi-models-apipageresponse-roblox-catalog-api-bundledetailsmodel-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "id": "...", "name": "...", "description": "...", "bundleType": "...", "isRecolorable": "...", "items": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://catalog.roblox.com/v1/assets/{ASSETID}/bundles" ``` ### POST `/v1/audio` Published an audio file and returns the new asset info. **Server:** `https://publish.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Publish.Api.UploadAudioRequest` See [Roblox.Publish.Api.UploadAudioRequest](#roblox-publish-api-uploadaudiorequest) in Models. **Request example:** ```json { "name": "string", "file": "string", "groupId": 0, "paymentSource": "string", "estimatedFileSize": 0, "estimatedDuration": 0 } ``` **Responses:** - `200`: OK → `Roblox.Publish.Api.PublishAudioResponse` - `400`: 3: The request did not contain a file to be uploaded. 4: The file in the request is too large. 5: The duration of the audio file is too long. 7: Failed to parse the file. 8: The file type is not supported. 9: The file is corrupted 11: Missing permissions to spend group funds. 14: The user/group does not have suffiecient funds to publish. 14: The user/group does not have suffiecient funds to publish. 15: The audio file has already been reviewed and rejected. 18: Too many requests. Try again later. 20: Error while trying to purchase the product. 22: The file size estimation error was greater than the acceptable margin of error. 23: The duration estimation error was greater than the acceptable margin of error. 24: Asset privacy is invalid. 29: Invalid argument in the request. - `401`: 0: Authorization has been denied for this request. 1: The request did not include an authorization. - `403`: 0: Token Validation Failed - `500`: 19: Asset creation was unavailable. Please try again. **Response fields** (`Roblox.Publish.Api.PublishAudioResponse`) See [Roblox.Publish.Api.PublishAudioResponse](#roblox-publish-api-publishaudioresponse) in Models. **Response example:** ```json { "Id": 0, "Name": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://publish.roblox.com/v1/audio" \ -H "Content-Type: application/json" \ -d '{ "name": "string", "file": "string", "groupId": 0, "paymentSource": "string", "estimatedFileSize": 0, "estimatedDuration": 0 }' ``` ### POST `/v1/audio/verify` Verifies an audio file and returns a product that you can purchase to publish the audio file. **Server:** `https://publish.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Publish.Api.VerifyAudioRequest` See [Roblox.Publish.Api.VerifyAudioRequest](#roblox-publish-api-verifyaudiorequest) in Models. **Request example:** ```json { "name": "string", "file": "string", "groupId": 0, "paymentSource": "string", "fileSize": 0, "duration": 0 } ``` **Responses:** - `200`: OK → `Roblox.Publish.Api.VerifyAudioResponse` - `400`: 3: The request did not contain a file to be uploaded. 4: The file in the request is too large. 5: The duration of the audio file is too long. 7: Failed to parse the file. 8: The file type is not supported. 9: The file is corrupted 18: Too many requests. Try again later. - `401`: 0: Authorization has been denied for this request. 1: The request did not include an authorization. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Publish.Api.VerifyAudioResponse`) See [Roblox.Publish.Api.VerifyAudioResponse](#roblox-publish-api-verifyaudioresponse) in Models. **Response example:** ```json { "name": "string", "price": 0, "balance": 0, "canAfford": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://publish.roblox.com/v1/audio/verify" \ -H "Content-Type: application/json" \ -d '{ "name": "string", "file": "string", "groupId": 0, "paymentSource": "string", "fileSize": 0, "duration": 0 }' ``` ### POST `/v1/auto-localization-table/games/{gameId}/assets-generation-request` Generates localization asset of a game. **Server:** `https://localizationtables.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The game id. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 3: Invalid table id. 14: Invalid game id 29: You do not have permission to generate asset for this table. 32: LocalizationTable is not available for the game. 34: Actor provided is invalid - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `429`: 24: Too many attempts.Please try again later. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://localizationtables.roblox.com/v1/auto-localization-table/games/{GAMEID}/assets-generation-request" ``` ### POST `/v1/auto-localization-table/games/{gameId}/auto-scrape-cleanup-request` Enqueues an event to flush the auto scraped entries which doesn't have translations. **Server:** `https://localizationtables.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The id of the game. | **Request Body:** `application/json` — Type: `Roblox.LocalizationTables.Api.RaiseEventForAutoScrapedEntriesCleanupRequest` See [Roblox.LocalizationTables.Api.RaiseEventForAutoScrapedEntriesCleanupRequest](#roblox-localizationtables-api-raiseeventforautoscrapedentriescleanuprequest) in Models. **Request example:** ```json { "maxAgeForFlush": "string" } ``` **Responses:** - `200`: OK → `object` - `400`: 14: Invalid game id 31: You do not have permission to flush auto scraped entries asset for this game. 32: LocalizationTable is not available for the game. 34: Actor provided is invalid - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `429`: 33: Too many attempts to flush the game.Please try again later. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://localizationtables.roblox.com/v1/auto-localization-table/games/{GAMEID}/auto-scrape-cleanup-request" \ -H "Content-Type: application/json" \ -d '{ "maxAgeForFlush": "string" }' ``` ### PATCH `/v1/auto-localization-table/games/{gameId}/ingestion` Ingests entries for auto localization. Needs to be an authorized user. **Server:** `https://localizationtables.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The game id. | **Responses:** - `200`: OK → `object` - `400`: 10: Maximum entries exceeded. Please keep the number of entries per request below the maximum. 13: Request body can't be null 16: Entries can't be null or empty 34: Actor provided is invalid - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `503`: 17: Feature is disabled **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://localizationtables.roblox.com/v1/auto-localization-table/games/{GAMEID}/ingestion" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### POST `/v1/autolocalization/games/{gameId}/autolocalizationtable` *(deprecated)* Use the Autolocalization controller in LocalizationTables API **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | | **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.GameAutolocalizationInformationResponse` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.GameInternationalization.Api.GameAutolocalizationInformationResponse`) See [Roblox.GameInternationalization.Api.GameAutolocalizationInformationResponse](#roblox-gameinternationalization-api-gameautolocalizationinformationresponse) in Models. **Response example:** ```json { "isAutolocalizationEnabled": false, "shouldUseLocalizationTable": false, "autoLocalizationTableId": "string", "assetId": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/autolocalization/games/{GAMEID}/autolocalizationtable" ``` ### PATCH `/v1/autolocalization/games/{gameId}/autolocalizationtable` *(deprecated)* Use the Autolocalization controller in LocalizationTables API **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.SetAutolocalizationTableForGameRequest` See [Roblox.GameInternationalization.Api.SetAutolocalizationTableForGameRequest](#roblox-gameinternationalization-api-setautolocalizationtableforgamerequest) in Models. **Request example:** ```json { "tableId": "string" } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/autolocalization/games/{GAMEID}/autolocalizationtable" \ -H "Content-Type: application/json" \ -d '{ "tableId": "string" }' ``` ### POST `/v1/autolocalization/games/{gameId}/autolocalizationtable#LocalizationTablesApi` **Server:** `https://localizationtables.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | | **Responses:** - `200`: OK → `Roblox.LocalizationTables.Api.GameAutolocalizationInformationResponse` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.LocalizationTables.Api.GameAutolocalizationInformationResponse`) See [Roblox.LocalizationTables.Api.GameAutolocalizationInformationResponse](#roblox-localizationtables-api-gameautolocalizationinformationresponse) in Models. **Response example:** ```json { "isAutolocalizationEnabled": false, "isAutomaticEntriesSettingEnabled": false, "isAutomaticEntriesDeletionEnabled": false, "shouldUseLocalizationTable": false, "autoLocalizationTableId": "string", "sourceLanguage": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://localizationtables.roblox.com/v1/autolocalization/games/{GAMEID}/autolocalizationtable#LocalizationTablesApi" ``` ### PATCH `/v1/autolocalization/games/{gameId}/autolocalizationtable#LocalizationTablesApi` **Server:** `https://localizationtables.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | | **Request Body:** `application/json` — Type: `Roblox.LocalizationTables.Api.SetAutolocalizationTableForGameRequest` See [Roblox.LocalizationTables.Api.SetAutolocalizationTableForGameRequest](#roblox-localizationtables-api-setautolocalizationtableforgamerequest) in Models. **Request example:** ```json { "tableId": "string" } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://localizationtables.roblox.com/v1/autolocalization/games/{GAMEID}/autolocalizationtable#LocalizationTablesApi" \ -H "Content-Type: application/json" \ -d '{ "tableId": "string" }' ``` ### PATCH `/v1/autolocalization/games/{gameId}/settings` *(deprecated)* Sets a game's auto-localization related settings Use the Autolocalization controller in LocalizationTables API **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The id of the game. | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.SetAutolocalizationSettingsForGameRequest` See [Roblox.GameInternationalization.Api.SetAutolocalizationSettingsForGameRequest](#roblox-gameinternationalization-api-setautolocalizationsettingsforgamerequest) in Models. **Request example:** ```json { "isAutolocalizationEnabled": false, "shouldUseLocalizationTable": false } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 14: Invalid game id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/autolocalization/games/{GAMEID}/settings" \ -H "Content-Type: application/json" \ -d '{ "isAutolocalizationEnabled": false, "shouldUseLocalizationTable": false }' ``` ### PATCH `/v1/autolocalization/games/{gameId}/settings#LocalizationTablesApi` Sets a game's auto-localization related settings **Server:** `https://localizationtables.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The id of the game. | **Request Body:** `application/json` — Type: `Roblox.LocalizationTables.Api.SetAutolocalizationSettingsForGameRequest` See [Roblox.LocalizationTables.Api.SetAutolocalizationSettingsForGameRequest](#roblox-localizationtables-api-setautolocalizationsettingsforgamerequest) in Models. **Request example:** ```json { "isAutolocalizationEnabled": false, "isAutomaticEntriesSettingEnabled": false, "isAutomaticEntriesDeletionsEnabled": false, "shouldUseLocalizationTable": false } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 14: Invalid game id 61: IsAutomaticEntriesSettingEnabled can only be enabled if IsAutolocalizationEnabled is also enabled. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://localizationtables.roblox.com/v1/autolocalization/games/{GAMEID}/settings#LocalizationTablesApi" \ -H "Content-Type: application/json" \ -d '{ "isAutolocalizationEnabled": false, "isAutomaticEntriesSettingEnabled": false, "isAutomaticEntriesDeletionsEnabled": false, "shouldUseLocalizationTable": false }' ``` ### GET `/v1/autolocalization/metadata` *(deprecated)* Metadata for AutoLocalization Configuration Use the Autolocalization controller in LocalizationTables API **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.AutoLocalizationMetadataResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.GameInternationalization.Api.AutoLocalizationMetadataResponse`) See [Roblox.GameInternationalization.Api.AutoLocalizationMetadataResponse](#roblox-gameinternationalization-api-autolocalizationmetadataresponse) in Models. **Response example:** ```json { "isReactVersionEnabledForAutoLocalizationSettings": false, "isTabbedUIEnabledForConfigureLocalizationPage": false, "isAutomaticTranslationToggleUIEnabled": false, "isAutomaticTranslationQuotaUIEnabled": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/autolocalization/metadata" ``` ### GET `/v1/autolocalization/metadata#LocalizationTablesApi` Metadata for AutoLocalization Configuration **Server:** `https://localizationtables.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.LocalizationTables.Api.AutoLocalizationMetadataResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.LocalizationTables.Api.AutoLocalizationMetadataResponse`) See [Roblox.LocalizationTables.Api.AutoLocalizationMetadataResponse](#roblox-localizationtables-api-autolocalizationmetadataresponse) in Models. **Response example:** ```json { "isReactVersionEnabledForAutoLocalizationSettings": false, "isTabbedUIEnabledForConfigureLocalizationPage": false, "isAutomaticTranslationToggleUIEnabled": false, "isAutomaticTranslationQuotaUIEnabled": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://localizationtables.roblox.com/v1/autolocalization/metadata#LocalizationTablesApi" ``` ### GET `/v1/automatic-translation/games/{gameId}/feature-status` Checks if automatic translation can be enabled for a game. The user must still have proper permissions for the game to get this info. **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The game id. | **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.GetAutomaticTranslationFeatureStatusForGameResponse` - `401`: 0: Authorization has been denied for this request. - `403`: 18: You do not have permission to manage this game - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.GetAutomaticTranslationFeatureStatusForGameResponse`) See [Roblox.GameInternationalization.Api.GetAutomaticTranslationFeatureStatusForGameResponse](#roblox-gameinternationalization-api-getautomatictranslationfeaturestatusforgameresponse) in Models. **Response example:** ```json { "gameId": 0, "isAutomaticTranslationAllowed": false, "isAutomaticTranslationSwitchesUIEnabled": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/automatic-translation/games/{GAMEID}/feature-status" ``` ### GET `/v1/automatic-translation/games/{gameId}/quota` Returns the automatic translation quota info for a game. The user must still have proper permissions for the game to get this info. **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The game id. | **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.GetAutomaticTranslationQuotaForGameResponse` - `400`: 14: Invalid game id - `401`: 0: Authorization has been denied for this request. - `403`: 18: You do not have permission to manage this game - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.GetAutomaticTranslationQuotaForGameResponse`) See [Roblox.GameInternationalization.Api.GetAutomaticTranslationQuotaForGameResponse](#roblox-gameinternationalization-api-getautomatictranslationquotaforgameresponse) in Models. **Response example:** ```json { "monthlyQuota": { "previousRefreshDate": "2024-01-01T00:00:00Z", "nextRefreshDate": "2024-01-01T00:00:00Z", "remaining": 0, "capacity": 0 }, "bankQuota": { "remaining": 0, "capacity": 0 } } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/automatic-translation/games/{GAMEID}/quota" ``` ### GET `/v1/automatic-translation/languages/{languageCode}/target-languages` Checks if the requested target languages are allowed for automatic translation based on the source language. If there are no requested target languages, then all allowed target languages for the source language will be returned. **Server:** `https://gameinternationalization.roblox.com` **Auth:** **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `languageCode` | path | `string` | Yes | The source language. | | `targetLanguages` | query | `string[]` | No | Optional target languages. If not passed in, all allowed target languages for the source language will be returned. | | `gameId` | query | `integer` | No | Optional gameId. If not passed in, we'll return the default list of languages allowed. | **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.GetAllowedAutomaticTranslationStatusForLanguagesResponse` - `400`: 73: Maximum languages exceeded. Please keep the number of languages per request below the maximum. 74: A target language cannot be null or whitespace. - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.GetAllowedAutomaticTranslationStatusForLanguagesResponse`) See [Roblox.GameInternationalization.Api.GetAllowedAutomaticTranslationStatusForLanguagesResponse](#roblox-gameinternationalization-api-getallowedautomatictranslationstatusforlanguagesresponse) in Models. **Response example:** ```json { "sourceLanguage": "string", "targetLanguages": [ { "languageCode": "...", "isAutomaticTranslationAllowed": "..." } ] } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://gameinternationalization.roblox.com/v1/automatic-translation/languages/{LANGUAGECODE}/target-languages" ``` ### GET `/v1/avatar` Returns details about the authenticated user's avatar. **Server:** `https://avatar.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `Roblox-Place-Id` | header | `integer` | No | | **Responses:** - `200`: OK → `Roblox.Api.Avatar.Models.AvatarModelV2` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Api.Avatar.Models.AvatarModelV2`) See [Roblox.Api.Avatar.Models.AvatarModelV2](#roblox-api-avatar-models-avatarmodelv2) in Models. **Response example:** ```json { "scales": { "height": 0, "width": 0, "head": 0, "depth": 0, "proportion": 0, "bodyType": 0 }, "playerAvatarType": 1, "bodyColors": { "headColorId": 0, "torsoColorId": 0, "rightArmColorId": 0, "leftArmColorId": 0, "rightLegColorId": 0, "leftLegColorId": 0 }, "assets": [ { "id": "...", "name": "...", "assetType": "...", "currentVersionId": "...", "meta": "...", "availabilityStatus": "..." } ], "defaultShirtApplied": false, "defaultPantsApplied": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://avatar.roblox.com/v1/avatar" ``` ### GET `/v1/avatar-rules` Returns the business rules related to avatars. BodyColorsPalette is a list of valid brickColors you can choose for your avatar. WearableAssetTypes contains a list of asset types with names, ids, and the maximum number that you can wear at a time. Does not include packages because they cannot be worn on your avatar directly. PlayerAvatarTypes are the types of avatars you can choose between. **Server:** `https://avatar.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `Roblox-Place-Id` | header | `integer` | No | | **Responses:** - `200`: OK → `Roblox.Api.Avatar.Models.AvatarRulesModel` **Response fields** (`Roblox.Api.Avatar.Models.AvatarRulesModel`) See [Roblox.Api.Avatar.Models.AvatarRulesModel](#roblox-api-avatar-models-avatarrulesmodel) in Models. **Response example:** ```json { "playerAvatarTypes": [ 1 ], "scales": "...", "wearableAssetTypes": [ { "maxNumber": "...", "id": "...", "name": "..." } ], "accessoryRefinementTypes": [ 0 ], "accessoryRefinementLowerBounds": "...", "accessoryRefinementUpperBounds": "..." } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://avatar.roblox.com/v1/avatar-rules" ``` ### GET `/v1/avatar/metadata` Returns metadata used by the avatar page of the website. **Server:** `https://avatar.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Api.Avatar.Models.AvatarMetadataModel` **Response fields** (`Roblox.Api.Avatar.Models.AvatarMetadataModel`) See [Roblox.Api.Avatar.Models.AvatarMetadataModel](#roblox-api-avatar-models-avatarmetadatamodel) in Models. **Response example:** ```json { "enableDefaultClothingMessage": false, "isAvatarScaleEmbeddedInTab": false, "isBodyTypeScaleOutOfTab": false, "scaleHeightIncrement": 0, "scaleWidthIncrement": 0, "scaleHeadIncrement": 0 } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://avatar.roblox.com/v1/avatar/metadata" ``` ### POST `/v1/avatar/redraw-thumbnail` Requests the authenticated user's thumbnail be redrawn. **Server:** `https://avatar.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `Roblox-Place-Id` | header | `integer` | No | | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `429`: 1: Redrawing your avatar thumbnail is floodchecked at this time. 1: Redrawing your avatar thumbnail is floodchecked at this time **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://avatar.roblox.com/v1/avatar/redraw-thumbnail" ``` ### POST `/v1/avatar/set-body-colors` Sets the authenticated user's body colors. **Server:** `https://avatar.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `Roblox-Place-Id` | header | `integer` | No | | **Request Body:** `application/json` — Type: `Roblox.Api.Avatar.Models.BodyColorsModel` See [Roblox.Api.Avatar.Models.BodyColorsModel](#roblox-api-avatar-models-bodycolorsmodel) in Models. **Request example:** ```json { "headColorId": 0, "torsoColorId": 0, "rightArmColorId": 0, "leftArmColorId": 0, "rightLegColorId": 0, "leftLegColorId": 0 } ``` **Responses:** - `200`: OK → `Roblox.Api.Avatar.Models.AvatarApiSuccessResponse` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Api.Avatar.Models.AvatarApiSuccessResponse`) See [Roblox.Api.Avatar.Models.AvatarApiSuccessResponse](#roblox-api-avatar-models-avatarapisuccessresponse) in Models. **Response example:** ```json { "success": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://avatar.roblox.com/v1/avatar/set-body-colors" \ -H "Content-Type: application/json" \ -d '{ "headColorId": 0, "torsoColorId": 0, "rightArmColorId": 0, "leftArmColorId": 0, "rightLegColorId": 0, "leftLegColorId": 0 }' ``` ### POST `/v1/avatar/set-player-avatar-type` Sets the authenticated user's player avatar type (e.g. R6 or R15). This is the avatar type chosen on the Avatar page. Some games can override this and force your character to be R6 or R15. **Server:** `https://avatar.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `Roblox-Place-Id` | header | `integer` | No | | **Request Body:** `application/json` — Type: `Roblox.Api.Avatar.Models.PlayerAvatarTypeModel` See [Roblox.Api.Avatar.Models.PlayerAvatarTypeModel](#roblox-api-avatar-models-playeravatartypemodel) in Models. **Request example:** ```json { "playerAvatarType": 1 } ``` **Responses:** - `200`: OK → `Roblox.Api.Avatar.Models.AvatarApiSuccessResponse` - `400`: 1: Invalid playerAvatarType. Valid values are: - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: You are not allowed to change player avatar type. **Response fields** (`Roblox.Api.Avatar.Models.AvatarApiSuccessResponse`) See [Roblox.Api.Avatar.Models.AvatarApiSuccessResponse](#roblox-api-avatar-models-avatarapisuccessresponse) in Models. **Response example:** ```json { "success": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://avatar.roblox.com/v1/avatar/set-player-avatar-type" \ -H "Content-Type: application/json" \ -d '{ "playerAvatarType": 1 }' ``` ### POST `/v1/avatar/set-scales` Sets the authenticated user's scales. **Server:** `https://avatar.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `Roblox-Place-Id` | header | `integer` | No | | **Request Body:** `application/json` — Type: `Roblox.Web.Responses.Avatar.ScaleModel` See [Roblox.Web.Responses.Avatar.ScaleModel](#roblox-web-responses-avatar-scalemodel) in Models. **Request example:** ```json { "height": 0, "width": 0, "head": 0, "depth": 0, "proportion": 0, "bodyType": 0 } ``` **Responses:** - `200`: OK → `Roblox.Api.Avatar.Models.AvatarApiSuccessResponse` - `400`: 1: Please pass in the scales JSON - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 3: The user does not have permissions to change scales. **Response fields** (`Roblox.Api.Avatar.Models.AvatarApiSuccessResponse`) See [Roblox.Api.Avatar.Models.AvatarApiSuccessResponse](#roblox-api-avatar-models-avatarapisuccessresponse) in Models. **Response example:** ```json { "success": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://avatar.roblox.com/v1/avatar/set-scales" \ -H "Content-Type: application/json" \ -d '{ "height": 0, "width": 0, "head": 0, "depth": 0, "proportion": 0, "bodyType": 0 }' ``` ### GET `/v1/badges/icons` [STABLE] Thumbnails badge icons. **Server:** `https://thumbnails.roblox.com` **Auth:** **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `badgeIds` | query | `integer[]` | Yes | The badge ids. | | `size` | query | `150x150` | No | The thumbnail size, formatted widthxheight Valid values: `150x150` | | `format` | query | `Png \| Webp` | No | The thumbnail format Valid values: `Png`, `Webp` | | `isCircular` | query | `true \| false` | No | The circle thumbnail output parameter, true or false Valid values: `true`, `false` | **Responses:** - `200`: OK - `400`: 1: There are too many requested Ids. 2: The requested image format is invalid. Please see documentation for valid thumbnail format parameter name and values. 3: The requested size is invalid. Please see documentation for valid thumbnail size parameter name and format. 4: The requested Ids are invalid, of an invalid type or missing. 10: Circular thumbnail requests are not allowed **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://thumbnails.roblox.com/v1/badges/icons?badgeIds={VALUE}" ``` ### GET `/v1/badges/metadata` Gets metadata about the badges system. **Server:** `https://badges.roblox.com` **Auth:** **Responses:** - `200`: OK → `Roblox.Badges.Api.BadgeMetadataResponse` **Response fields** (`Roblox.Badges.Api.BadgeMetadataResponse`) See [Roblox.Badges.Api.BadgeMetadataResponse](#roblox-badges-api-badgemetadataresponse) in Models. **Response example:** ```json { "badgeCreationPrice": 0, "maxBadgeNameLength": 0, "maxBadgeDescriptionLength": 0 } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://badges.roblox.com/v1/badges/metadata" ``` ### GET `/v1/badges/{badgeId}` Gets badge information by the badge Id. **Server:** `https://badges.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `badgeId` | path | `integer` | Yes | The badge Id. | **Responses:** - `200`: OK → `Roblox.Badges.Api.BadgeResponse` - `404`: 1: Badge is invalid or does not exist. 3: The game is invalid or does not exist. **Response fields** (`Roblox.Badges.Api.BadgeResponse`) See [Roblox.Badges.Api.BadgeResponse](#roblox-badges-api-badgeresponse) in Models. **Response example:** ```json { "id": 0, "name": "string", "description": "string", "displayName": "string", "displayDescription": "string", "enabled": false } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://badges.roblox.com/v1/badges/{BADGEID}" ``` ### PATCH `/v1/badges/{badgeId}` Updates badge configuration. **Server:** `https://badges.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `badgeId` | path | `integer` | Yes | The badge Id. | **Request Body:** `application/json` — Type: `Roblox.Badges.Api.UpdateBadgeRequest` See [Roblox.Badges.Api.UpdateBadgeRequest](#roblox-badges-api-updatebadgerequest) in Models. **Request example:** ```json { "name": "string", "description": "string", "enabled": false } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 6: Text moderated. 14: Invalid badge name. 15: Invalid badge description. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: You do not have permission to manage this badge. - `404`: 1: Badge is invalid or does not exist. 3: The game is invalid or does not exist. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://badges.roblox.com/v1/badges/{BADGEID}" \ -H "Content-Type: application/json" \ -d '{ "name": "string", "description": "string", "enabled": false }' ``` ### PATCH `/v1/badges/{badgeId}/description/language-codes/{languageCode}` Update localized description of a badge **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `badgeId` | path | `integer` | Yes | The badge id | | `languageCode` | path | `string` | Yes | The language code of the description to update | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.UpdateBadgeDescriptionRequest` See [Roblox.GameInternationalization.Api.UpdateBadgeDescriptionRequest](#roblox-gameinternationalization-api-updatebadgedescriptionrequest) in Models. **Request example:** ```json { "description": "string" } ``` **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.UpdateBadgeDescriptionResponse` - `400`: 13: Request body can't be null 19: New name is null or whitespaces or new name/description is too long 20: New name or description is moderated 22: Invalid language code 26: You can't update translations for source language 53: Language is not supported for the game. 62: Invalid game badge id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.UpdateBadgeDescriptionResponse`) See [Roblox.GameInternationalization.Api.UpdateBadgeDescriptionResponse](#roblox-gameinternationalization-api-updatebadgedescriptionresponse) in Models. **Response example:** ```json { "description": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/badges/{BADGEID}/description/language-codes/{LANGUAGECODE}" \ -H "Content-Type: application/json" \ -d '{ "description": "string" }' ``` ### POST `/v1/badges/{badgeId}/icon` Overwrites a badge icon with a new one. **Server:** `https://badges.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `badgeId` | path | `integer` | Yes | The badge Id. | **Request Body:** `multipart/form-data` — Type: `object` | Property | Type | Required | Description | |----------|------|----------|-------------| | `Files` | `string` | No | | **Request example:** ```json { "Files": "string" } ``` **Responses:** - `200`: OK → `Roblox.Badges.Api.IconUploadResponse` - `400`: 6: Text moderated. 22: Icon file is not present in the request. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: You do not have permission to manage this badge. - `404`: 1: Badge is invalid or does not exist. - `429`: 13: Too many requests, try again later. **Response fields** (`Roblox.Badges.Api.IconUploadResponse`) See [Roblox.Badges.Api.IconUploadResponse](#roblox-badges-api-iconuploadresponse) in Models. **Response example:** ```json { "targetId": 0 } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://badges.roblox.com/v1/badges/{BADGEID}/icon" \ -F "Files=@file.bin;type=application/octet-stream" ``` ### POST `/v1/badges/{badgeId}/icon#PublishApi` Overwrites a badge icon with a new one. **Server:** `https://publish.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `badgeId` | path | `integer` | Yes | The badge Id. | **Responses:** - `200`: OK → `Roblox.Publish.Api.UploadResponse` - `400`: 2: File not present in request. 12: Name or description is moderated. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 5: You do not have permission to manage this item. - `404`: 4: Target item is invalid or does not exist. - `429`: 3: You're uploading too much, please wait and try again later. **Response fields** (`Roblox.Publish.Api.UploadResponse`) See [Roblox.Publish.Api.UploadResponse](#roblox-publish-api-uploadresponse) in Models. **Response example:** ```json { "targetId": 0 } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://publish.roblox.com/v1/badges/{BADGEID}/icon#PublishApi" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### GET `/v1/badges/{badgeId}/icons` Get all icons for a badge **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `badgeId` | path | `integer` | Yes | The id of the badge | | `width` | query | `integer` | No | The width of the icon to request | | `height` | query | `integer` | No | The height of the icon to request | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.GetBadgeIconResponse_` - `400`: 52: Image dimensions are invalid 62: Invalid game badge id - `401`: 0: Authorization has been denied for this request. - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.GetBadgeIconResponse_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.GetBadgeIconResponse_](#roblox-web-webapi-models-apiarrayresponse-roblox-gameinternationalization-api-getbadgeiconresponse-) in Models. **Response example:** ```json { "data": [ { "imageId": "...", "imageUrl": "...", "state": "...", "languageCode": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/badges/{BADGEID}/icons" ``` ### POST `/v1/badges/{badgeId}/icons/language-codes/{languageCode}` Update a badge's icon **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `badgeId` | path | `integer` | Yes | The id of the badge | | `languageCode` | path | `string` | Yes | The language code of this icon to update | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 22: Invalid language code 26: You can't update translations for source language 45: File uploaded does not match known image format 46: File not present in request 53: Language is not supported for the game. 62: Invalid game badge id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `429`: 24: Too many attempts.Please try again later. - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/badges/{BADGEID}/icons/language-codes/{LANGUAGECODE}" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### DELETE `/v1/badges/{badgeId}/icons/language-codes/{languageCode}` Delete a localized icon from a badge **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `badgeId` | path | `integer` | Yes | The id of the badge | | `languageCode` | path | `string` | Yes | The language code of the localized icon to delete | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 22: Invalid language code 23: You can't delete translations for source language 53: Language is not supported for the game. 62: Invalid game badge id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/badges/{BADGEID}/icons/language-codes/{LANGUAGECODE}" ``` ### GET `/v1/badges/{badgeId}/name-description` **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `badgeId` | path | `integer` | Yes | | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.NameDescription_` - `400`: 62: Invalid game badge id - `401`: 0: Authorization has been denied for this request. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.NameDescription_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.NameDescription_](#roblox-web-webapi-models-apiarrayresponse-roblox-gameinternationalization-api-namedescription-) in Models. **Response example:** ```json { "data": [ { "name": "...", "description": "...", "updateType": "...", "languageCode": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/badges/{BADGEID}/name-description" ``` ### PATCH `/v1/badges/{badgeId}/name-description/language-codes/{languageCode}` Update localized name and description of a badge **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `badgeId` | path | `integer` | Yes | The badge id | | `languageCode` | path | `string` | Yes | The language code of the name and description to Update | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.UpdateBadgeNameDescriptionRequest` See [Roblox.GameInternationalization.Api.UpdateBadgeNameDescriptionRequest](#roblox-gameinternationalization-api-updatebadgenamedescriptionrequest) in Models. **Request example:** ```json { "name": "string", "description": "string" } ``` **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.UpdateBadgeNameDescriptionResponse` - `400`: 13: Request body can't be null 19: New name is null or whitespaces or new name/description is too long 20: New name or description is moderated 22: Invalid language code 26: You can't update translations for source language 53: Language is not supported for the game. 62: Invalid game badge id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.UpdateBadgeNameDescriptionResponse`) See [Roblox.GameInternationalization.Api.UpdateBadgeNameDescriptionResponse](#roblox-gameinternationalization-api-updatebadgenamedescriptionresponse) in Models. **Response example:** ```json { "name": "string", "description": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/badges/{BADGEID}/name-description/language-codes/{LANGUAGECODE}" \ -H "Content-Type: application/json" \ -d '{ "name": "string", "description": "string" }' ``` ### DELETE `/v1/badges/{badgeId}/name-description/language-codes/{languageCode}` Delete localized name and description of a badge **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `badgeId` | path | `integer` | Yes | The badge id | | `languageCode` | path | `string` | Yes | The language code of the name and description to delete | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 22: Invalid language code 23: You can't delete translations for source language 53: Language is not supported for the game. 62: Invalid game badge id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/badges/{BADGEID}/name-description/language-codes/{LANGUAGECODE}" ``` ### PATCH `/v1/badges/{badgeId}/name/language-codes/{languageCode}` Update localized name of a badge **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `badgeId` | path | `integer` | Yes | The badge id | | `languageCode` | path | `string` | Yes | The language code of the name to update | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.UpdateBadgeNameRequest` See [Roblox.GameInternationalization.Api.UpdateBadgeNameRequest](#roblox-gameinternationalization-api-updatebadgenamerequest) in Models. **Request example:** ```json { "name": "string" } ``` **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.UpdateBadgeNameResponse` - `400`: 13: Request body can't be null 19: New name is null or whitespaces or new name/description is too long 20: New name or description is moderated 22: Invalid language code 26: You can't update translations for source language 53: Language is not supported for the game. 62: Invalid game badge id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.UpdateBadgeNameResponse`) See [Roblox.GameInternationalization.Api.UpdateBadgeNameResponse](#roblox-gameinternationalization-api-updatebadgenameresponse) in Models. **Response example:** ```json { "name": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/badges/{BADGEID}/name/language-codes/{LANGUAGECODE}" \ -H "Content-Type: application/json" \ -d '{ "name": "string" }' ``` ### POST `/v1/batch` [STABLE] Returns a list of thumbnails with varying types and sizes **Server:** `https://thumbnails.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `Roblox-Place-Id` | header | `integer` | No | (optional) placeid | **Request Body:** `application/json` — Type: `Roblox.Thumbnails.Apis.Models.ThumbnailBatchRequest[]` See [Roblox.Thumbnails.Apis.Models.ThumbnailBatchRequest](#roblox-thumbnails-apis-models-thumbnailbatchrequest) in Models. **Responses:** - `200`: OK - `400`: 1: There are too many requested Ids. 2: The requested image format is invalid. Please see documentation for valid thumbnail format parameter name and values. 3: The requested size is invalid. Please see documentation for valid thumbnail size parameter name and format. 4: The requested Ids are invalid, of an invalid type or missing. 7: The specified type is not supported by the batch endpoint - `403`: 9: User not authorized to use AutoGenerated or ForceAutoGenerated return policies. **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://thumbnails.roblox.com/v1/batch" \ -H "Content-Type: application/json" \ -d '[ { "requestId": "string", "targetId": 0, "token": "string", "alias": "string", "type": "Avatar", "size": "string" } ]' ``` ### GET `/v1/birthdate` *(deprecated)* Get the user's birthdate Replaced by users.roblox.com/v1/birthdate **Server:** `https://accountinformation.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.AccountInformation.Api.Models.BirthdateResponse` - `400`: 1: User not found. - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.AccountInformation.Api.Models.BirthdateResponse`) See [Roblox.AccountInformation.Api.Models.BirthdateResponse](#roblox-accountinformation-api-models-birthdateresponse) in Models. **Response example:** ```json { "birthMonth": 0, "birthDay": 0, "birthYear": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountinformation.roblox.com/v1/birthdate" ``` ### POST `/v1/birthdate` Update the user's birthdate **Server:** `https://users.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Users.Api.BirthdateRequest` See [Roblox.Users.Api.BirthdateRequest](#roblox-users-api-birthdaterequest) in Models. **Request example:** ```json { "birthMonth": 0, "birthDay": 0, "birthYear": 0, "password": "string" } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: User not found. 4: The birthdate provided is invalid. 8: Password is incorrect. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: PIN is locked. 5: Invalid birthdate change. - `500`: 0: An unknown error occured. 5: Invalid birthdate change. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://users.roblox.com/v1/birthdate" \ -H "Content-Type: application/json" \ -d '{ "birthMonth": 0, "birthDay": 0, "birthYear": 0, "password": "string" }' ``` ### GET `/v1/birthdate#UsersApi` Get the user's birthdate **Server:** `https://users.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Users.Api.BirthdateResponse` - `400`: 1: User not found. - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Users.Api.BirthdateResponse`) See [Roblox.Users.Api.BirthdateResponse](#roblox-users-api-birthdateresponse) in Models. **Response example:** ```json { "birthMonth": 0, "birthDay": 0, "birthYear": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://users.roblox.com/v1/birthdate#UsersApi" ``` ### GET `/v1/bundles/details` Returns details about the given bundleIds. **Server:** `https://catalog.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `bundleIds` | query | `integer[]` | Yes | | **Responses:** - `200`: OK → `Roblox.Catalog.Api.BundleDetailsModel[]` - `400`: 3: Cannot request so many bundles at once. **Response fields** (`Roblox.Catalog.Api.BundleDetailsModel[]`) See [Roblox.Catalog.Api.BundleDetailsModel](#roblox-catalog-api-bundledetailsmodel) in Models. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://catalog.roblox.com/v1/bundles/details?bundleIds={VALUE}" ``` ### GET `/v1/bundles/thumbnails` [STABLE] Get bundle thumbnails for the given CSV of bundle ids **Server:** `https://thumbnails.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `bundleIds` | query | `integer[]` | Yes | CSV for the bundle ids to get bundle thumbnails | | `size` | query | `150x150 \| 420x420` | No | The thumbnail size, formatted widthxheight Valid values: `150x150`, `420x420` | | `format` | query | `Png \| Webp` | No | The thumbnail format Valid values: `Png`, `Webp` | | `isCircular` | query | `true \| false` | No | The circle thumbnail output parameter, true or false Valid values: `true`, `false` | **Responses:** - `200`: OK - `400`: 1: There are too many requested Ids. 2: The requested image format is invalid. Please see documentation for valid thumbnail format parameter name and values. 3: The requested size is invalid. Please see documentation for valid thumbnail size parameter name and format. 4: The requested Ids are invalid, of an invalid type or missing. 10: Circular thumbnail requests are not allowed **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://thumbnails.roblox.com/v1/bundles/thumbnails?bundleIds={VALUE}" ``` ### GET `/v1/bundles/{bundleId}/details` Returns details about the given bundleId. **Server:** `https://catalog.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `bundleId` | path | `integer` | Yes | | **Responses:** - `200`: OK → `Roblox.Catalog.Api.BundleDetailsModel` - `400`: 1: Invalid bundle **Response fields** (`Roblox.Catalog.Api.BundleDetailsModel`) See [Roblox.Catalog.Api.BundleDetailsModel](#roblox-catalog-api-bundledetailsmodel) in Models. **Response example:** ```json { "id": 0, "name": "string", "description": "string", "bundleType": "string", "isRecolorable": false, "items": [ { "owned": "...", "id": "...", "name": "...", "type": "...", "supportsHeadShapes": "...", "assetType": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://catalog.roblox.com/v1/bundles/{BUNDLEID}/details" ``` ### GET `/v1/bundles/{bundleId}/recommendations` Gets recommendations for a given bundle, bundleId of 0 returns randomized bundles - Accepts both public and authenticated users. **Server:** `https://catalog.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `bundleId` | path | `integer` | Yes | | | `numItems` | query | `integer` | No | The number of recommended items to return. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Catalog.Api.BundleDetailsModel_` - `400`: 1: Invalid bundle 2: Error retrieving bundles 3: Error getting bundle recommendations 4: NumItems exceed maximum **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Catalog.Api.BundleDetailsModel_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Catalog.Api.BundleDetailsModel_](#roblox-web-webapi-models-apiarrayresponse-roblox-catalog-api-bundledetailsmodel-) in Models. **Response example:** ```json { "data": [ { "id": "...", "name": "...", "description": "...", "bundleType": "...", "isRecolorable": "...", "items": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://catalog.roblox.com/v1/bundles/{BUNDLEID}/recommendations" ``` ### POST `/v1/catalog/items/details` Returns details for one or more catalog items. There is an item count limit per request. Exceeding this returns 400 Bad Request. **Server:** `https://catalog.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Catalog.Api.MultigetItemDetailsRequestModel` See [Roblox.Catalog.Api.MultigetItemDetailsRequestModel](#roblox-catalog-api-multigetitemdetailsrequestmodel) in Models. **Request example:** ```json { "items": [ { "itemType": "...", "id": "..." } ] } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Catalog.Api.CatalogSearchDetailedResponseItemV2_` - `400`: 2: Invalid count - `403`: 0: Token Validation Failed 7: User is unauthorized. - `429`: 8: The flood limit has been exceeded. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Catalog.Api.CatalogSearchDetailedResponseItemV2_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Catalog.Api.CatalogSearchDetailedResponseItemV2_](#roblox-web-webapi-models-apiarrayresponse-roblox-catalog-api-catalogsearchdetailedresponseitemv2-) in Models. **Response example:** ```json { "data": [ { "bundledItems": "...", "taxonomy": "...", "itemCreatedUtc": "...", "discountInformation": "...", "id": "...", "itemType": "..." } ] } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://catalog.roblox.com/v1/catalog/items/details" \ -H "Content-Type: application/json" \ -d '{ "items": [ { "itemType": "...", "id": "..." } ] }' ``` ### GET `/v1/categories` Lists Category Names and their Ids. **Server:** `https://catalog.roblox.com` **Auth:** **Responses:** - `200`: OK → `Roblox.Catalog.Api.CategoryModel[]` **Response fields** (`Roblox.Catalog.Api.CategoryModel[]`) See [Roblox.Catalog.Api.CategoryModel](#roblox-catalog-api-categorymodel) in Models. **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://catalog.roblox.com/v1/categories" ``` ### GET `/v1/client-version/{binaryType}` Get client version information for specific binary type **Server:** `https://clientsettings.roblox.com` **Auth:** **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `binaryType` | path | `string` | Yes | Platform(WindowsPlayer, WindowsStudio, MacPlayer or MacStudio) for which we want the latest version | **Responses:** - `200`: OK → `Roblox.ClientSettings.Api.Models.Response.ClientVersionResponse` **Response fields** (`Roblox.ClientSettings.Api.Models.Response.ClientVersionResponse`) See [Roblox.ClientSettings.Api.Models.Response.ClientVersionResponse](#roblox-clientsettings-api-models-response-clientversionresponse) in Models. **Response example:** ```json { "version": "string", "clientVersionUpload": "string", "bootstrapperVersion": "string", "nextClientVersionUpload": "string", "nextClientVersion": "string" } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://clientsettings.roblox.com/v1/client-version/{BINARYTYPE}" ``` ### POST `/v1/collections/items/{itemType}/{itemTargetId}` Adds an item to the appropriate collection **Server:** `https://inventory.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `itemType` | path | `0 \| 1 \| 2 \| 3` | Yes | Type of the item (i.e. Asset, Bundle) Valid values: `0`, `1`, `2`, `3` | | `itemTargetId` | path | `integer` | Yes | ID of the item | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: The item type does not exist. 2: The asset does not exist. 3: The bundle does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 4: You don't own the specified item. 5: Assets of this type are not allowed in collections. 6: Items of this type are not allowed in collections. 7: The item is already in the collection. 9: The collection is full. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://inventory.roblox.com/v1/collections/items/{ITEMTYPE}/{ITEMTARGETID}" ``` ### DELETE `/v1/collections/items/{itemType}/{itemTargetId}` Removes an item to the appropriate collection **Server:** `https://inventory.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `itemType` | path | `0 \| 1 \| 2 \| 3` | Yes | Type of the item (i.e. Asset, Bundle) Valid values: `0`, `1`, `2`, `3` | | `itemTargetId` | path | `integer` | Yes | ID of the item | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: The item type does not exist. 2: The asset does not exist. 3: The bundle does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 8: The item is not in the collection. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://inventory.roblox.com/v1/collections/items/{ITEMTYPE}/{ITEMTARGETID}" ``` ### GET `/v1/country-regions` Get list of country regions sorted by localized name **Server:** `https://locale.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `locale` | query | `string` | No | | **Responses:** - `200`: OK → `Roblox.Locale.Api.CountryRegionListResponse` - `400`: 2: Invalid supported locale code. - `403`: 7: Feature is disabled **Response fields** (`Roblox.Locale.Api.CountryRegionListResponse`) See [Roblox.Locale.Api.CountryRegionListResponse](#roblox-locale-api-countryregionlistresponse) in Models. **Response example:** ```json { "countryRegionList": [ { "code": "...", "name": "...", "displayName": "..." } ] } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://locale.roblox.com/v1/country-regions" ``` ### POST `/v1/creations/get-asset-details` Gets the asset status and other configuration details for the given assetIds list. **Server:** `https://itemconfiguration.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.ItemConfiguration.Api.AssetCreationsDetailsRequest` See [Roblox.ItemConfiguration.Api.AssetCreationsDetailsRequest](#roblox-itemconfiguration-api-assetcreationsdetailsrequest) in Models. **Request example:** ```json { "AssetIds": [ 0 ] } ``` **Responses:** - `200`: OK → `Roblox.ItemConfiguration.Api.AssetCreationsDetailsResponse[]` - `400`: 1: Missing AssetIds parameters 2: Invalid asset Ids - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `414`: 3: Too many asset Ids - `429`: 9: Flood Limit Exceeded - `503`: 6: Service Unavailable **Response fields** (`Roblox.ItemConfiguration.Api.AssetCreationsDetailsResponse[]`) See [Roblox.ItemConfiguration.Api.AssetCreationsDetailsResponse](#roblox-itemconfiguration-api-assetcreationsdetailsresponse) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://itemconfiguration.roblox.com/v1/creations/get-asset-details" \ -H "Content-Type: application/json" \ -d '{ "AssetIds": [ 0 ] }' ``` ### GET `/v1/creations/get-assets` Gets the user created asset information filtered by the given asset type. **Server:** `https://itemconfiguration.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetType` | query | `string` | Yes | | | `isArchived` | query | `boolean` | No | | | `groupId` | query | `integer` | No | | | `limit` | query | `10 \| 25 \| 50 \| 100` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.ItemConfiguration.Api.AssetCreationsResponse_` - `400`: 5: Invalid assetType 10: Invalid Asset Category - `401`: 0: Authorization has been denied for this request. - `403`: 7: User does not have necessary permissions for group 8: Asset type does not have necessary permissions for group - `429`: 9: Flood Limit Exceeded - `503`: 6: Service Unavailable **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.ItemConfiguration.Api.AssetCreationsResponse_`) See [Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.ItemConfiguration.Api.AssetCreationsResponse_](#roblox-web-webapi-models-apipageresponse-roblox-itemconfiguration-api-assetcreationsresponse-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "assetId": "...", "name": "..." } ] } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://itemconfiguration.roblox.com/v1/creations/get-assets?assetType={VALUE}" ``` ### GET `/v1/description` *(deprecated)* Get the user's description Replaced by users.roblox.com/v1/description **Server:** `https://accountinformation.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.AccountInformation.Api.Models.DescriptionResponse` - `400`: 1: User not found. - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.AccountInformation.Api.Models.DescriptionResponse`) See [Roblox.AccountInformation.Api.Models.DescriptionResponse](#roblox-accountinformation-api-models-descriptionresponse) in Models. **Response example:** ```json { "description": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountinformation.roblox.com/v1/description" ``` ### POST `/v1/description` *(deprecated)* Update the user's description Replaced by users.roblox.com/v1/description **Server:** `https://accountinformation.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.AccountInformation.Api.Models.DescriptionRequest` See [Roblox.AccountInformation.Api.Models.DescriptionRequest](#roblox-accountinformation-api-models-descriptionrequest) in Models. **Request example:** ```json { "description": "string" } ``` **Responses:** - `200`: OK → `Roblox.AccountInformation.Api.Models.DescriptionResponse` - `400`: 1: User not found. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `500`: 0: An unknown error occured. - `503`: 3: This feature is currently disabled. Please try again later. **Response fields** (`Roblox.AccountInformation.Api.Models.DescriptionResponse`) See [Roblox.AccountInformation.Api.Models.DescriptionResponse](#roblox-accountinformation-api-models-descriptionresponse) in Models. **Response example:** ```json { "description": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountinformation.roblox.com/v1/description" \ -H "Content-Type: application/json" \ -d '{ "description": "string" }' ``` ### GET `/v1/description#UsersApi` Get the user's description **Server:** `https://users.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Users.Api.DescriptionResponse` - `400`: 1: User not found. - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Users.Api.DescriptionResponse`) See [Roblox.Users.Api.DescriptionResponse](#roblox-users-api-descriptionresponse) in Models. **Response example:** ```json { "description": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://users.roblox.com/v1/description#UsersApi" ``` ### POST `/v1/description#UsersApi` Update the user's description **Server:** `https://users.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Users.Api.DescriptionRequest` See [Roblox.Users.Api.DescriptionRequest](#roblox-users-api-descriptionrequest) in Models. **Request example:** ```json { "description": "string" } ``` **Responses:** - `200`: OK → `Roblox.Users.Api.DescriptionResponse` - `400`: 1: User not found. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: PIN is locked. - `500`: 0: An unknown error occured. - `503`: 3: This feature is currently disabled. Please try again later. **Response fields** (`Roblox.Users.Api.DescriptionResponse`) See [Roblox.Users.Api.DescriptionResponse](#roblox-users-api-descriptionresponse) in Models. **Response example:** ```json { "description": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://users.roblox.com/v1/description#UsersApi" \ -H "Content-Type: application/json" \ -d '{ "description": "string" }' ``` ### GET `/v1/developer-products/icons` [STABLE] Thumbnails developer product icons. **Server:** `https://thumbnails.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `developerProductIds` | query | `integer[]` | Yes | The developer product ids. | | `size` | query | `150x150 \| 420x420` | No | The thumbnail size, formatted widthxheight Valid values: `150x150`, `420x420` | | `format` | query | `Png \| Webp` | No | The thumbnail format Valid values: `Png`, `Webp` | | `isCircular` | query | `true \| false` | No | The circle thumbnail output parameter, true or false Valid values: `true`, `false` | **Responses:** - `200`: OK - `400`: 1: There are too many requested Ids. 2: The requested image format is invalid. Please see documentation for valid thumbnail format parameter name and values. 3: The requested size is invalid. Please see documentation for valid thumbnail size parameter name and format. 4: The requested Ids are invalid, of an invalid type or missing. 10: Circular thumbnail requests are not allowed **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://thumbnails.roblox.com/v1/developer-products/icons?developerProductIds={VALUE}" ``` ### PATCH `/v1/developer-products/{developerProductId}/description/language-codes/{languageCode}` Update localized description of a developer product **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `developerProductId` | path | `integer` | Yes | The developer product id | | `languageCode` | path | `string` | Yes | The language code of the description to update | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.UpdateDeveloperProductDescriptionRequest` See [Roblox.GameInternationalization.Api.UpdateDeveloperProductDescriptionRequest](#roblox-gameinternationalization-api-updatedeveloperproductdescriptionrequest) in Models. **Request example:** ```json { "description": "string" } ``` **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.UpdateDeveloperProductDescriptionResponse` - `400`: 13: Request body can't be null 19: New name is null or whitespaces or new name/description is too long 20: New name or description is moderated 22: Invalid language code 26: You can't update translations for source language 53: Language is not supported for the game. 70: Invalid developer product id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.UpdateDeveloperProductDescriptionResponse`) See [Roblox.GameInternationalization.Api.UpdateDeveloperProductDescriptionResponse](#roblox-gameinternationalization-api-updatedeveloperproductdescriptionresponse) in Models. **Response example:** ```json { "description": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/developer-products/{DEVELOPERPRODUCTID}/description/language-codes/{LANGUAGECODE}" \ -H "Content-Type: application/json" \ -d '{ "description": "string" }' ``` ### GET `/v1/developer-products/{developerProductId}/icons` Get all icons for a developer product **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `developerProductId` | path | `integer` | Yes | The id of the developer product | | `width` | query | `integer` | No | The width of the icon to request | | `height` | query | `integer` | No | The height of the icon to request | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.GetDeveloperProductIconResponse_` - `400`: 52: Image dimensions are invalid 70: Invalid developer product id - `401`: 0: Authorization has been denied for this request. - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.GetDeveloperProductIconResponse_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.GetDeveloperProductIconResponse_](#roblox-web-webapi-models-apiarrayresponse-roblox-gameinternationalization-api-getdeveloperproducticonresponse-) in Models. **Response example:** ```json { "data": [ { "imageId": "...", "imageUrl": "...", "state": "...", "languageCode": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/developer-products/{DEVELOPERPRODUCTID}/icons" ``` ### POST `/v1/developer-products/{developerProductId}/icons/language-codes/{languageCode}` Update a developer product's icon **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `developerProductId` | path | `integer` | Yes | The id of the developer product | | `languageCode` | path | `string` | Yes | The language code of this icon to update | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 22: Invalid language code 26: You can't update translations for source language 45: File uploaded does not match known image format 46: File not present in request 53: Language is not supported for the game. 70: Invalid developer product id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `429`: 24: Too many attempts.Please try again later. - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/developer-products/{DEVELOPERPRODUCTID}/icons/language-codes/{LANGUAGECODE}" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### DELETE `/v1/developer-products/{developerProductId}/icons/language-codes/{languageCode}` Delete a localized icon from a developer product **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `developerProductId` | path | `integer` | Yes | The id of the developer product | | `languageCode` | path | `string` | Yes | The language code of the localized icon to delete | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 22: Invalid language code 23: You can't delete translations for source language 53: Language is not supported for the game. 70: Invalid developer product id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/developer-products/{DEVELOPERPRODUCTID}/icons/language-codes/{LANGUAGECODE}" ``` ### GET `/v1/developer-products/{developerProductId}/name-description` Get all names and descriptions of a developer product **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `developerProductId` | path | `integer` | Yes | The developer product Id | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.NameDescription_` - `400`: 70: Invalid developer product id - `401`: 0: Authorization has been denied for this request. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.NameDescription_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.NameDescription_](#roblox-web-webapi-models-apiarrayresponse-roblox-gameinternationalization-api-namedescription-) in Models. **Response example:** ```json { "data": [ { "name": "...", "description": "...", "updateType": "...", "languageCode": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/developer-products/{DEVELOPERPRODUCTID}/name-description" ``` ### PATCH `/v1/developer-products/{developerProductId}/name-description/language-codes/{languageCode}` Update localized name and description of a developer product **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `developerProductId` | path | `integer` | Yes | The developer product id | | `languageCode` | path | `string` | Yes | The language code of the name and description to Update | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.UpdateDeveloperProductNameDescriptionRequest` See [Roblox.GameInternationalization.Api.UpdateDeveloperProductNameDescriptionRequest](#roblox-gameinternationalization-api-updatedeveloperproductnamedescriptionrequest) in Models. **Request example:** ```json { "name": "string", "description": "string" } ``` **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.UpdateDeveloperProductNameDescriptionResponse` - `400`: 13: Request body can't be null 19: New name is null or whitespaces or new name/description is too long 20: New name or description is moderated 22: Invalid language code 26: You can't update translations for source language 53: Language is not supported for the game. 70: Invalid developer product id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.UpdateDeveloperProductNameDescriptionResponse`) See [Roblox.GameInternationalization.Api.UpdateDeveloperProductNameDescriptionResponse](#roblox-gameinternationalization-api-updatedeveloperproductnamedescriptionresponse) in Models. **Response example:** ```json { "name": "string", "description": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/developer-products/{DEVELOPERPRODUCTID}/name-description/language-codes/{LANGUAGECODE}" \ -H "Content-Type: application/json" \ -d '{ "name": "string", "description": "string" }' ``` ### DELETE `/v1/developer-products/{developerProductId}/name-description/language-codes/{languageCode}` Delete localized name and description of a developer product **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `developerProductId` | path | `integer` | Yes | The developer product id | | `languageCode` | path | `string` | Yes | The language code of the name and description to delete | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 22: Invalid language code 23: You can't delete translations for source language 53: Language is not supported for the game. 70: Invalid developer product id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/developer-products/{DEVELOPERPRODUCTID}/name-description/language-codes/{LANGUAGECODE}" ``` ### PATCH `/v1/developer-products/{developerProductId}/name/language-codes/{languageCode}` Update localized name of a developer product **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `developerProductId` | path | `integer` | Yes | The developer product id | | `languageCode` | path | `string` | Yes | The language code of the name to update | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.UpdateDeveloperProductNameRequest` See [Roblox.GameInternationalization.Api.UpdateDeveloperProductNameRequest](#roblox-gameinternationalization-api-updatedeveloperproductnamerequest) in Models. **Request example:** ```json { "name": "string" } ``` **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.UpdateDeveloperProductNameResponse` - `400`: 13: Request body can't be null 19: New name is null or whitespaces or new name/description is too long 20: New name or description is moderated 22: Invalid language code 26: You can't update translations for source language 53: Language is not supported for the game. 70: Invalid developer product id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.UpdateDeveloperProductNameResponse`) See [Roblox.GameInternationalization.Api.UpdateDeveloperProductNameResponse](#roblox-gameinternationalization-api-updatedeveloperproductnameresponse) in Models. **Response example:** ```json { "name": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/developer-products/{DEVELOPERPRODUCTID}/name/language-codes/{LANGUAGECODE}" \ -H "Content-Type: application/json" \ -d '{ "name": "string" }' ``` ### GET `/v1/display-names/validate` Validate a display name for a new user. **Server:** `https://users.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `displayName` | query | `string` | Yes | The display name. | | `birthdate` | query | `string` | Yes | The new user's birthdate | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: Display name is too short 2: Display name is too long 3: Display name contains invalid characters 4: Display name has been moderated 6: Request must contain a birthdate 8: Display name has too many combinations of character sets - `429`: 5: Display name updates for this user have been throttled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://users.roblox.com/v1/display-names/validate?displayName={VALUE}&birthdate={VALUE}" ``` ### GET `/v1/email` Gets the authenticated user's email address and verified status **Server:** `https://accountsettings.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.AccountSettings.Api.EmailResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.AccountSettings.Api.EmailResponse`) See [Roblox.AccountSettings.Api.EmailResponse](#roblox-accountsettings-api-emailresponse) in Models. **Response example:** ```json { "emailAddress": "string", "verified": false, "canBypassPasswordForEmailUpdate": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountsettings.roblox.com/v1/email" ``` ### POST `/v1/email` Updates the authenticated user's email address **Server:** `https://accountsettings.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 8: Password is incorrect. 9: Invalid email address. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: This feature is currently disabled. Please try again later. 3: There are too many accounts associated with this email address. 11: You must be on the Corporate network to log in. - `409`: 4: This is already the current email. - `429`: 6: Too many attempts to update email. Please try again later. 7: Too many attempts to send verification email. Please try again later. - `503`: 2: This feature is currently disabled. Please try again later. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountsettings.roblox.com/v1/email" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### PATCH `/v1/email` Updates the authenticated user's email address **Server:** `https://accountsettings.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 8: Password is incorrect. 9: Invalid email address. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: This feature is currently disabled. Please try again later. 3: There are too many accounts associated with this email address. 11: You must be on the Corporate network to log in. - `409`: 4: This is already the current email. - `429`: 6: Too many attempts to update email. Please try again later. 7: Too many attempts to send verification email. Please try again later. - `503`: 2: This feature is currently disabled. Please try again later. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountsettings.roblox.com/v1/email" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### POST `/v1/email/verify` Verify the user's email address from token **Server:** `https://accountinformation.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.AccountInformation.Api.Models.VerifyEmailRequest` See [Roblox.AccountInformation.Api.Models.VerifyEmailRequest](#roblox-accountinformation-api-models-verifyemailrequest) in Models. **Request example:** ```json { "ticket": "string" } ``` **Responses:** - `200`: OK → `Roblox.AccountInformation.Api.Models.VerifyEmailResponse` - `403`: 0: Token Validation Failed **Response fields** (`Roblox.AccountInformation.Api.Models.VerifyEmailResponse`) See [Roblox.AccountInformation.Api.Models.VerifyEmailResponse](#roblox-accountinformation-api-models-verifyemailresponse) in Models. **Response example:** ```json { "verifiedUserHatAssetId": 0 } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountinformation.roblox.com/v1/email/verify" \ -H "Content-Type: application/json" \ -d '{ "ticket": "string" }' ``` ### POST `/v1/email/verify#AccountSettingsApi` Send verify email to the authenticated user's email address **Server:** `https://accountsettings.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.AccountSettings.Api.SendVerifyEmailRequest` See [Roblox.AccountSettings.Api.SendVerifyEmailRequest](#roblox-accountsettings-api-sendverifyemailrequest) in Models. **Request example:** ```json { "freeItem": false, "isAdsAccount": false } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 10: No email address is associated with the account. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: This feature is currently disabled. Please try again later. 11: You must be on the Corporate network to log in. - `409`: 5: The email is already verified. - `429`: 7: Too many attempts to send verification email. Please try again later. - `503`: 2: This feature is currently disabled. Please try again later. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountsettings.roblox.com/v1/email/verify#AccountSettingsApi" \ -H "Content-Type: application/json" \ -d '{ "freeItem": false, "isAdsAccount": false }' ``` ### GET `/v1/emails` Gets the authenticated user's verified email and pending (unverified) email if one exists **Server:** `https://accountsettings.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.AccountSettings.Api.CurrentEmailsResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.AccountSettings.Api.CurrentEmailsResponse`) See [Roblox.AccountSettings.Api.CurrentEmailsResponse](#roblox-accountsettings-api-currentemailsresponse) in Models. **Response example:** ```json { "verifiedEmail": "string", "pendingEmail": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountsettings.roblox.com/v1/emails" ``` ### GET `/v1/favorites/assets/{assetId}/count` Gets the favorite count for the given asset Id. **Server:** `https://catalog.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetId` | path | `integer` | Yes | | **Responses:** - `200`: OK → `integer` - `400`: 2: Invalid asset Id. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://catalog.roblox.com/v1/favorites/assets/{ASSETID}/count" ``` ### GET `/v1/favorites/bundles/{bundleId}/count` Gets the favorite count for the given bundle Id. **Server:** `https://catalog.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `bundleId` | path | `integer` | Yes | | **Responses:** - `200`: OK → `integer` - `400`: 2: Invalid bundle Id. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://catalog.roblox.com/v1/favorites/bundles/{BUNDLEID}/count" ``` ### GET `/v1/favorites/users/{userId}/assets/{assetId}/favorite` Gets the favorite model for the asset and user. **Server:** `https://catalog.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | | | `assetId` | path | `integer` | Yes | | **Responses:** - `200`: OK → `Roblox.Catalog.Api.AssetFavoriteModel` - `400`: 1: Invalid user Id. 2: Invalid asset Id. - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Catalog.Api.AssetFavoriteModel`) See [Roblox.Catalog.Api.AssetFavoriteModel](#roblox-catalog-api-assetfavoritemodel) in Models. **Response example:** ```json { "assetId": 0, "userId": 0, "created": "2024-01-01T00:00:00Z" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://catalog.roblox.com/v1/favorites/users/{USERID}/assets/{ASSETID}/favorite" ``` ### POST `/v1/favorites/users/{userId}/assets/{assetId}/favorite` Create a favorite for an asset by the authenticated user. **Server:** `https://catalog.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | | | `assetId` | path | `integer` | Yes | | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: Invalid user Id. 2: Invalid asset Id. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 6: You are not authorized to perform this action. - `409`: 3: Asset is already favorited. - `429`: 5: This action was floodchecked. Please try again later. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://catalog.roblox.com/v1/favorites/users/{USERID}/assets/{ASSETID}/favorite" ``` ### DELETE `/v1/favorites/users/{userId}/assets/{assetId}/favorite` Delete a favorite for an asset by the authenticated user. **Server:** `https://catalog.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | | | `assetId` | path | `integer` | Yes | | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: Invalid user Id. 2: Invalid asset Id. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 6: You are not authorized to perform this action. - `409`: 4: Asset is already not favorited. - `429`: 5: This action was floodchecked. Please try again later. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://catalog.roblox.com/v1/favorites/users/{USERID}/assets/{ASSETID}/favorite" ``` ### GET `/v1/favorites/users/{userId}/bundles/{bundleId}/favorite` Gets the favorite model for the bundle and user. **Server:** `https://catalog.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | | | `bundleId` | path | `integer` | Yes | | **Responses:** - `200`: OK → `Roblox.Catalog.Api.BundleFavoriteModel` - `400`: 1: Invalid user Id. 2: Invalid bundle Id. - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Catalog.Api.BundleFavoriteModel`) See [Roblox.Catalog.Api.BundleFavoriteModel](#roblox-catalog-api-bundlefavoritemodel) in Models. **Response example:** ```json { "bundleId": 0, "userId": 0, "created": "2024-01-01T00:00:00Z" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://catalog.roblox.com/v1/favorites/users/{USERID}/bundles/{BUNDLEID}/favorite" ``` ### POST `/v1/favorites/users/{userId}/bundles/{bundleId}/favorite` Create a favorite for the bundle by the authenticated user. **Server:** `https://catalog.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | | | `bundleId` | path | `integer` | Yes | | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: Invalid user Id. 2: Invalid bundle Id. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 6: You are not authorized to perform this action. - `409`: 3: Bundle is already favorited. - `429`: 5: This action was floodchecked. Please try again later. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://catalog.roblox.com/v1/favorites/users/{USERID}/bundles/{BUNDLEID}/favorite" ``` ### DELETE `/v1/favorites/users/{userId}/bundles/{bundleId}/favorite` Delete favorite for the bundle by the authenticated user. **Server:** `https://catalog.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | | | `bundleId` | path | `integer` | Yes | | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: Invalid user Id. 2: Invalid bundle Id. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 6: You are not authorized to perform this action. - `409`: 4: Bundle is already not favorited. - `429`: 5: This action was floodchecked. Please try again later. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://catalog.roblox.com/v1/favorites/users/{USERID}/bundles/{BUNDLEID}/favorite" ``` ### GET `/v1/favorites/users/{userId}/favorites/{assetTypeId}/assets` Lists the marketplace assets favorited by a given user with the given assetTypeId. **Server:** `https://catalog.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | | | `assetTypeId` | path | `integer` | Yes | | | `limit` | query | `integer enum (6 values)` | No | The number of results per request. Valid values: `10`, `18`, `24`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `Asc \| Desc` | No | The order the results are sorted in. Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Catalog.Api.CatalogSearchDetailedResponseItem_` - `400`: 1: Invalid user Id. 8: Ascending order is not allowed. 11: Invalid asset type id. - `500`: 99: Internal server error. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Catalog.Api.CatalogSearchDetailedResponseItem_`) See [Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Catalog.Api.CatalogSearchDetailedResponseItem_](#roblox-web-webapi-models-apipageresponse-roblox-catalog-api-catalogsearchdetailedresponseitem-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "id": "...", "itemType": "...", "assetType": "...", "bundleType": "...", "isRecolorable": "...", "name": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://catalog.roblox.com/v1/favorites/users/{USERID}/favorites/{ASSETTYPEID}/assets" ``` ### GET `/v1/favorites/users/{userId}/favorites/{subtypeId}/bundles` Lists the bundles favorited by a given user with the given bundle subtypeId.Switched to EAAS style pagination cursors since July 2024. **Server:** `https://catalog.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | | | `subtypeId` | path | `integer` | Yes | | | `itemsPerPage` | query | `integer` | No | | | `cursor` | query | `string` | No | | | `isPrevious` | query | `boolean` | No | | **Responses:** - `200`: OK → `Roblox.Catalog.Api.FavoriteBundlesResponse` - `400`: 1: Invalid user Id. 3: Cannot request so many bundles at once. 10: Invalid previous pagination request. Please provide a cursor when isPrevious is true - `401`: 0: Authorization has been denied for this request. - `403`: 6: You are not authorized to perform this action. - `500`: 11: Internal server error. Please check if you have provided correct pagination cursor **Response fields** (`Roblox.Catalog.Api.FavoriteBundlesResponse`) See [Roblox.Catalog.Api.FavoriteBundlesResponse](#roblox-catalog-api-favoritebundlesresponse) in Models. **Response example:** ```json { "favorites": [ { "id": "...", "name": "...", "description": "...", "bundleType": "...", "isRecolorable": "...", "items": "..." } ], "moreFavorites": false, "nextCursor": "string", "previousCursor": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://catalog.roblox.com/v1/favorites/users/{USERID}/favorites/{SUBTYPEID}/bundles" ``` ### GET `/v1/featured-content/event` Gets the featured event for a group **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | query | `integer` | Yes | The group Id. | **Responses:** - `200`: OK → `Roblox.Groups.Client.GroupFeaturedContentResponse` **Response fields** (`Roblox.Groups.Client.GroupFeaturedContentResponse`) See [Roblox.Groups.Client.GroupFeaturedContentResponse](#roblox-groups-client-groupfeaturedcontentresponse) in Models. **Response example:** ```json { "groupId": 0, "contentType": "string", "contentId": "string" } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/featured-content/event?groupId={VALUE}" ``` ### POST `/v1/featured-content/event` Sets the featured event for a group **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | query | `integer` | Yes | The group Id. | | `eventId` | query | `integer` | Yes | The event Id. | **Responses:** - `200`: OK → `Roblox.Groups.Client.GroupFeaturedContentResponse` - `403`: 0: Token Validation Failed 3: User is not authorized to set featured content for this group. **Response fields** (`Roblox.Groups.Client.GroupFeaturedContentResponse`) See [Roblox.Groups.Client.GroupFeaturedContentResponse](#roblox-groups-client-groupfeaturedcontentresponse) in Models. **Response example:** ```json { "groupId": 0, "contentType": "string", "contentId": "string" } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/featured-content/event?groupId={VALUE}&eventId={VALUE}" ``` ### DELETE `/v1/featured-content/event` Deletes the featured event for a group **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | query | `integer` | Yes | The group Id. | | `eventId` | query | `integer` | Yes | The event Id. | **Responses:** - `200`: OK - `403`: 0: Token Validation Failed 3: User is not authorized to set featured content for this group. **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/featured-content/event?groupId={VALUE}&eventId={VALUE}" ``` ### GET `/v1/game-icon/games/{gameId}` Get all icons for a game **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The id of the game | | `width` | query | `integer` | No | The width of the icon to request | | `height` | query | `integer` | No | The height of the icon to request | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.GetGameIconResponse_` - `400`: 14: Invalid game id 52: Image dimensions are invalid - `401`: 0: Authorization has been denied for this request. - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.GetGameIconResponse_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.GetGameIconResponse_](#roblox-web-webapi-models-apiarrayresponse-roblox-gameinternationalization-api-getgameiconresponse-) in Models. **Response example:** ```json { "data": [ { "imageId": "...", "imageUrl": "...", "state": "...", "languageCode": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/game-icon/games/{GAMEID}" ``` ### POST `/v1/game-icon/games/{gameId}/language-codes/{languageCode}` Update a game's icon **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The id of the game | | `languageCode` | path | `string` | Yes | The language code of this icon to update | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 14: Invalid game id 22: Invalid language code 26: You can't update translations for source language 45: File uploaded does not match known image format 46: File not present in request 53: Language is not supported for the game. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `429`: 24: Too many attempts.Please try again later. - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/game-icon/games/{GAMEID}/language-codes/{LANGUAGECODE}" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### DELETE `/v1/game-icon/games/{gameId}/language-codes/{languageCode}` Delete a localized icon from a game **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The id of the game | | `languageCode` | path | `string` | Yes | The language code of the localized icon to delete | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 14: Invalid game id 22: Invalid language code 23: You can't delete translations for source language 53: Language is not supported for the game. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/game-icon/games/{GAMEID}/language-codes/{LANGUAGECODE}" ``` ### PATCH `/v1/game-localization-roles/games/{gameId}` Assigns or revokes a role **Server:** `https://translationroles.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The id of the game | **Request Body:** `application/json` — Type: `Roblox.TranslationRoles.Api.UpdateRoleRequest` See [Roblox.TranslationRoles.Api.UpdateRoleRequest](#roblox-translationroles-api-updaterolerequest) in Models. **Request example:** ```json { "assigneeId": 0, "assigneeType": "user", "role": "translator", "revoke": false } ``` **Responses:** - `200`: OK → `TranslationRolesApi.Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 3: Invalid game id 4: Invalid assignee id 6: Request body can't be null 7: The role you are assigning has reached max limit - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 1: You must be authorized to use this endpoint - `429`: 5: Too many attempts. Please try again later. - `503`: 2: Feature is disabled **Response fields** (`TranslationRolesApi.Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [TranslationRolesApi.Roblox.Web.WebAPI.ApiEmptyResponseModel](#translationrolesapi-roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://translationroles.roblox.com/v1/game-localization-roles/games/{GAMEID}" \ -H "Content-Type: application/json" \ -d '{ "assigneeId": 0, "assigneeType": "user", "role": "translator", "revoke": false }' ``` ### GET `/v1/game-localization-roles/games/{gameId}/current-user/roles` Retrieves the list of roles granted to current logged-in user **Server:** `https://translationroles.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The id of the game | **Responses:** - `200`: OK → `TranslationRolesApi.Roblox.Web.WebAPI.Models.ApiArrayResponse_System.String_` - `400`: 3: Invalid game id - `401`: 0: Authorization has been denied for this request. - `503`: 2: Feature is disabled **Response fields** (`TranslationRolesApi.Roblox.Web.WebAPI.Models.ApiArrayResponse_System.String_`) See [TranslationRolesApi.Roblox.Web.WebAPI.Models.ApiArrayResponse_System.String_](#translationrolesapi-roblox-web-webapi-models-apiarrayresponse-system-string-) in Models. **Response example:** ```json { "data": [ "string" ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://translationroles.roblox.com/v1/game-localization-roles/games/{GAMEID}/current-user/roles" ``` ### GET `/v1/game-localization-roles/games/{gameId}/roles/{role}/assignees` Gets list of users assigned a specific role in a game. **Server:** `https://translationroles.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The id of the game | | `role` | path | `translator` | Yes | The Roblox.GameLocalization.Client.GameLocalizationRoles.GameLocalizationRoleType Valid values: `translator` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.TranslationRoles.Api.Assignee_` - `400`: 3: Invalid game id - `401`: 0: Authorization has been denied for this request. - `403`: 1: You must be authorized to use this endpoint - `503`: 2: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.TranslationRoles.Api.Assignee_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.TranslationRoles.Api.Assignee_](#roblox-web-webapi-models-apiarrayresponse-roblox-translationroles-api-assignee-) in Models. **Response example:** ```json { "data": [ { "id": "...", "name": "...", "type": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://translationroles.roblox.com/v1/game-localization-roles/games/{GAMEID}/roles/{ROLE}/assignees" ``` ### GET `/v1/game-localization-roles/roles/{role}/current-user` Gets the list of games and associated role assignment info for the requested user and role. **Server:** `https://translationroles.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `role` | path | `translator` | Yes | The Roblox.GameLocalization.Client.GameLocalizationRoles.GameLocalizationRoleType Valid values: `translator` | | `exclusiveStartKey` | query | `string` | No | Part of pagination. Last primary key of returned items in previous operation. | | `pageSize` | query | `integer` | No | Part of pagination. Maximum number of items that might be returned in the page. | | `groupId` | query | `integer` | No | Optional seleted groupId of resources requested for the user and role. | **Responses:** - `200`: OK → `Roblox.TranslationRoles.Api.GetGameLocalizationRoleAssignmentsForUserResponse` - `400`: 10: Invalid page size 11: Maximum page size exceeded 12: Invalid exclusive start key - `401`: 0: Authorization has been denied for this request. - `500`: 0: An unknown error occurred - `503`: 2: Feature is disabled **Response fields** (`Roblox.TranslationRoles.Api.GetGameLocalizationRoleAssignmentsForUserResponse`) See [Roblox.TranslationRoles.Api.GetGameLocalizationRoleAssignmentsForUserResponse](#roblox-translationroles-api-getgamelocalizationroleassignmentsforuserresponse) in Models. **Response example:** ```json { "games": [ { "gameId": "...", "assignee": "..." } ], "previousPageCursor": "string", "nextPageCursor": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://translationroles.roblox.com/v1/game-localization-roles/roles/{ROLE}/current-user" ``` ### GET `/v1/game-localization-status/translation-counts-for-language-or-locale` Gets the language translation counts for the specified table. The languages to retrieve must be provided. **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameIds` | query | `integer[]` | Yes | List of game ids to retrieve translation counts for. | | `languageOrLocaleCode` | query | `string` | Yes | The code for the language or locale. | | `languageOrLocaleType` | query | `Language \| Locale` | Yes | Indicates whether the languageOrLocaleCode represents a language or locale. Valid values: `Language`, `Locale` | **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.GetTranslationCountsForLanguageOrLocaleResponse` - `400`: 66: Games can't be null or empty 67: Maximum games exceeded. Please keep the number of games per request below the maximum. 68: LanguageOrLocaleCode is null or whitespace - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.GameInternationalization.Api.GetTranslationCountsForLanguageOrLocaleResponse`) See [Roblox.GameInternationalization.Api.GetTranslationCountsForLanguageOrLocaleResponse](#roblox-gameinternationalization-api-gettranslationcountsforlanguageorlocaleresponse) in Models. **Response example:** ```json { "languageOrLocaleCode": "string", "languageOrLocaleType": "Language", "games": [ { "gameId": "...", "status": "...", "categories": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/game-localization-status/translation-counts-for-language-or-locale?gameIds={VALUE}&languageOrLocaleCode={VALUE}&languageOrLocaleType={VALUE}" ``` ### GET `/v1/game-localization-status/{gameId}/translation-counts` Gets the language translation counts for all languages of a game **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | GameID of the game to get translation counts for | **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.GetTranslationCountsForGameResponse` - `400`: 4: Table does not exist. 14: Invalid game id - `401`: 0: Authorization has been denied for this request. - `500`: 0: An unknown error occurred. **Response fields** (`Roblox.GameInternationalization.Api.GetTranslationCountsForGameResponse`) See [Roblox.GameInternationalization.Api.GetTranslationCountsForGameResponse](#roblox-gameinternationalization-api-gettranslationcountsforgameresponse) in Models. **Response example:** ```json { "gameId": 0, "languagesOrLocales": [ { "status": "...", "categories": "...", "name": "...", "languageCodeType": "...", "languageCode": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/game-localization-status/{GAMEID}/translation-counts" ``` ### GET `/v1/game-passes` [STABLE] Thumbnails game pass icons. **Server:** `https://thumbnails.roblox.com` **Auth:** **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gamePassIds` | query | `integer[]` | Yes | The game pass ids. | | `size` | query | `150x150` | No | The thumbnail size, formatted widthxheight Valid values: `150x150` | | `format` | query | `Png \| Webp` | No | The thumbnail format Valid values: `Png`, `Webp` | | `isCircular` | query | `true \| false` | No | The circle thumbnail output parameter, true or false Valid values: `true`, `false` | **Responses:** - `200`: OK - `400`: 1: There are too many requested Ids. 2: The requested image format is invalid. Please see documentation for valid thumbnail format parameter name and values. 3: The requested size is invalid. Please see documentation for valid thumbnail size parameter name and format. 4: The requested Ids are invalid, of an invalid type or missing. 10: Circular thumbnail requests are not allowed **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://thumbnails.roblox.com/v1/game-passes?gamePassIds={VALUE}" ``` ### PATCH `/v1/game-passes/{gamePassId}/description/language-codes/{languageCode}` Update localized description of a game pass **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gamePassId` | path | `integer` | Yes | The game pass id | | `languageCode` | path | `string` | Yes | The language code of description to update | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.UpdateGamePassDescriptionRequest` See [Roblox.GameInternationalization.Api.UpdateGamePassDescriptionRequest](#roblox-gameinternationalization-api-updategamepassdescriptionrequest) in Models. **Request example:** ```json { "description": "string" } ``` **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.UpdateGamePassDescriptionResponse` - `400`: 13: Request body can't be null 19: New name is null or whitespaces or new name/description is too long 20: New name or description is moderated 22: Invalid language code 26: You can't update translations for source language 53: Language is not supported for the game. 61: Invalid game pass id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.UpdateGamePassDescriptionResponse`) See [Roblox.GameInternationalization.Api.UpdateGamePassDescriptionResponse](#roblox-gameinternationalization-api-updategamepassdescriptionresponse) in Models. **Response example:** ```json { "description": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/game-passes/{GAMEPASSID}/description/language-codes/{LANGUAGECODE}" \ -H "Content-Type: application/json" \ -d '{ "description": "string" }' ``` ### GET `/v1/game-passes/{gamePassId}/icons` Get all icons for a game pass **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gamePassId` | path | `integer` | Yes | The game pass id | | `width` | query | `integer` | No | The width of the icon to request | | `height` | query | `integer` | No | The height of the icon to request | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.GetGamePassIconResponse_` - `400`: 52: Image dimensions are invalid 61: Invalid game pass id - `401`: 0: Authorization has been denied for this request. - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.GetGamePassIconResponse_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.GetGamePassIconResponse_](#roblox-web-webapi-models-apiarrayresponse-roblox-gameinternationalization-api-getgamepassiconresponse-) in Models. **Response example:** ```json { "data": [ { "imageId": "...", "imageUrl": "...", "state": "...", "languageCode": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/game-passes/{GAMEPASSID}/icons" ``` ### POST `/v1/game-passes/{gamePassId}/icons/language-codes/{languageCode}` Update a game pass's icon **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gamePassId` | path | `integer` | Yes | The game pass id | | `languageCode` | path | `string` | Yes | The language code of this icon to update | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 22: Invalid language code 26: You can't update translations for source language 45: File uploaded does not match known image format 46: File not present in request 53: Language is not supported for the game. 61: Invalid game pass id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `429`: 24: Too many attempts.Please try again later. - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/game-passes/{GAMEPASSID}/icons/language-codes/{LANGUAGECODE}" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### DELETE `/v1/game-passes/{gamePassId}/icons/language-codes/{languageCode}` Delete a localized icon from a game pass **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gamePassId` | path | `integer` | Yes | The game pass id | | `languageCode` | path | `string` | Yes | The language code of the localized icon to delete | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 22: Invalid language code 23: You can't delete translations for source language 53: Language is not supported for the game. 61: Invalid game pass id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/game-passes/{GAMEPASSID}/icons/language-codes/{LANGUAGECODE}" ``` ### GET `/v1/game-passes/{gamePassId}/name-description` Get all names and descriptions of a game pass **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gamePassId` | path | `integer` | Yes | The game pass Id | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.NameDescription_` - `400`: 61: Invalid game pass id - `401`: 0: Authorization has been denied for this request. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.NameDescription_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.NameDescription_](#roblox-web-webapi-models-apiarrayresponse-roblox-gameinternationalization-api-namedescription-) in Models. **Response example:** ```json { "data": [ { "name": "...", "description": "...", "updateType": "...", "languageCode": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/game-passes/{GAMEPASSID}/name-description" ``` ### PATCH `/v1/game-passes/{gamePassId}/name-description/language-codes/{languageCode}` Update localized name and description of a game pass **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gamePassId` | path | `integer` | Yes | The game pass id | | `languageCode` | path | `string` | Yes | The language code of the name/description to update | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.UpdateGamePassNameDescriptionRequest` See [Roblox.GameInternationalization.Api.UpdateGamePassNameDescriptionRequest](#roblox-gameinternationalization-api-updategamepassnamedescriptionrequest) in Models. **Request example:** ```json { "name": "string", "description": "string" } ``` **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.UpdateGamePassNameDescriptionResponse` - `400`: 13: Request body can't be null 19: New name is null or whitespaces or new name/description is too long 20: New name or description is moderated 22: Invalid language code 26: You can't update translations for source language 53: Language is not supported for the game. 61: Invalid game pass id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.UpdateGamePassNameDescriptionResponse`) See [Roblox.GameInternationalization.Api.UpdateGamePassNameDescriptionResponse](#roblox-gameinternationalization-api-updategamepassnamedescriptionresponse) in Models. **Response example:** ```json { "name": "string", "description": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/game-passes/{GAMEPASSID}/name-description/language-codes/{LANGUAGECODE}" \ -H "Content-Type: application/json" \ -d '{ "name": "string", "description": "string" }' ``` ### DELETE `/v1/game-passes/{gamePassId}/name-description/language-codes/{languageCode}` Delete localized name and description of a game pass **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gamePassId` | path | `integer` | Yes | The game pass id | | `languageCode` | path | `string` | Yes | The language code of the name and description to delete | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 22: Invalid language code 23: You can't delete translations for source language 53: Language is not supported for the game. 61: Invalid game pass id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/game-passes/{GAMEPASSID}/name-description/language-codes/{LANGUAGECODE}" ``` ### PATCH `/v1/game-passes/{gamePassId}/name/language-codes/{languageCode}` Update localized name of a game pass **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gamePassId` | path | `integer` | Yes | The game pass id | | `languageCode` | path | `string` | Yes | The language code of the name to update | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.UpdateGamePassNameRequest` See [Roblox.GameInternationalization.Api.UpdateGamePassNameRequest](#roblox-gameinternationalization-api-updategamepassnamerequest) in Models. **Request example:** ```json { "name": "string" } ``` **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.UpdateGamePassNameResponse` - `400`: 13: Request body can't be null 19: New name is null or whitespaces or new name/description is too long 20: New name or description is moderated 22: Invalid language code 26: You can't update translations for source language 53: Language is not supported for the game. 61: Invalid game pass id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.UpdateGamePassNameResponse`) See [Roblox.GameInternationalization.Api.UpdateGamePassNameResponse](#roblox-gameinternationalization-api-updategamepassnameresponse) in Models. **Response example:** ```json { "name": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/game-passes/{GAMEPASSID}/name/language-codes/{LANGUAGECODE}" \ -H "Content-Type: application/json" \ -d '{ "name": "string" }' ``` ### GET `/v1/game-start-info` The server will call this on game server start to request general information about the universe This is version 1.1, which returns an entry from the UniverseAvatarType enum. During mixed mode this may return unreliable results. **Server:** `https://avatar.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | query | `integer` | Yes | | | `Roblox-Place-Id` | header | `integer` | No | | **Responses:** - `200`: OK → `Roblox.Api.Avatar.Models.GameStartInfoResponse` **Response fields** (`Roblox.Api.Avatar.Models.GameStartInfoResponse`) See [Roblox.Api.Avatar.Models.GameStartInfoResponse](#roblox-api-avatar-models-gamestartinforesponse) in Models. **Response example:** ```json { "gameAvatarType": "string", "allowCustomAnimations": "string", "universeAvatarCollisionType": "string", "universeAvatarBodyType": "string", "jointPositioningType": "string", "message": "string" } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://avatar.roblox.com/v1/game-start-info?universeId={VALUE}" ``` ### GET `/v1/game-thumbnails/games/{gameId}/images` Get the localized image ids in all languages for a game. **Server:** `https://gameinternationalization.roblox.com` **Auth:** **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The game identifier. | | `width` | query | `integer` | No | The width. | | `height` | query | `integer` | No | The height. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.GetGameThumbnailsResponse_` - `400`: 14: Invalid game id 52: Image dimensions are invalid - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.GetGameThumbnailsResponse_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.GetGameThumbnailsResponse_](#roblox-web-webapi-models-apiarrayresponse-roblox-gameinternationalization-api-getgamethumbnailsresponse-) in Models. **Response example:** ```json { "data": [ { "languageCode": "...", "mediaAssets": "..." } ] } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://gameinternationalization.roblox.com/v1/game-thumbnails/games/{GAMEID}/images" ``` ### POST `/v1/game-thumbnails/games/{gameId}/language-codes/{languageCode}/alt-text` Updates the game thumbnail alt text. **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The game identifier. | | `languageCode` | path | `string` | Yes | The language code. | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.UpdateThumbnailAltTextRequest` See [Roblox.GameInternationalization.Api.UpdateThumbnailAltTextRequest](#roblox-gameinternationalization-api-updatethumbnailalttextrequest) in Models. **Request example:** ```json { "thumbnailId": 0, "altText": "string" } ``` **Responses:** - `200`: OK → `string` - `400`: 14: Invalid game id 19: New name is null or whitespaces or new name/description is too long 20: New name or description is moderated 22: Invalid language code 26: You can't update translations for source language 45: File uploaded does not match known image format 53: Language is not supported for the game. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `429`: 24: Too many attempts.Please try again later. - `500`: 0: An unknown error occurred. 88: Failed to filter text - `503`: 17: Feature is disabled **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/game-thumbnails/games/{GAMEID}/language-codes/{LANGUAGECODE}/alt-text" \ -H "Content-Type: application/json" \ -d '{ "thumbnailId": 0, "altText": "string" }' ``` ### POST `/v1/game-thumbnails/games/{gameId}/language-codes/{languageCode}/image` Uploads the game thumbnail. **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The game identifier. | | `languageCode` | path | `string` | Yes | The language code. | **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.Models.Response.UploadImageForGameThumbnailResponse` - `400`: 14: Invalid game id 22: Invalid language code 26: You can't update translations for source language 53: Language is not supported for the game. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `429`: 24: Too many attempts.Please try again later. - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.Models.Response.UploadImageForGameThumbnailResponse`) See [Roblox.GameInternationalization.Api.Models.Response.UploadImageForGameThumbnailResponse](#roblox-gameinternationalization-api-models-response-uploadimageforgamethumbnailresponse) in Models. **Response example:** ```json { "mediaAssetId": "string" } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/game-thumbnails/games/{GAMEID}/language-codes/{LANGUAGECODE}/image" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### POST `/v1/game-thumbnails/games/{gameId}/language-codes/{languageCode}/images/order` Orders the specified image Ids for the game thumbnails. **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The game identifier. | | `languageCode` | path | `string` | Yes | The language code. | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.SortImageIdsRequest` See [Roblox.GameInternationalization.Api.SortImageIdsRequest](#roblox-gameinternationalization-api-sortimageidsrequest) in Models. **Request example:** ```json { "mediaAssetIds": [ 0 ] } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 14: Invalid game id 22: Invalid language code 26: You can't update translations for source language 53: Language is not supported for the game. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/game-thumbnails/games/{GAMEID}/language-codes/{LANGUAGECODE}/images/order" \ -H "Content-Type: application/json" \ -d '{ "mediaAssetIds": [ 0 ] }' ``` ### DELETE `/v1/game-thumbnails/games/{gameId}/language-codes/{languageCode}/images/{imageId}` Deletes the game thumbnail. **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The game identifier. | | `languageCode` | path | `string` | Yes | The language code. | | `imageId` | path | `integer` | Yes | The image identifier. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 14: Invalid game id 22: Invalid language code 23: You can't delete translations for source language 53: Language is not supported for the game. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/game-thumbnails/games/{GAMEID}/language-codes/{LANGUAGECODE}/images/{IMAGEID}" ``` ### GET `/v1/games` Gets a list of games' detail **Server:** `https://games.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeIds` | query | `integer[]` | Yes | A list of universe Ids. Cannot exceed a maximum of 50 IDs. | | `fields` | query | `string` | No | Optional comma-separated list of field names to include in the response. When omitted, all fields are returned. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Games.Api.Models.Response.GameDetailResponse_` - `400`: 8: The universe IDs specified are invalid. 9: Too many universe IDs were requested. - `429`: 4: Too many requests have been made. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Games.Api.Models.Response.GameDetailResponse_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Games.Api.Models.Response.GameDetailResponse_](#roblox-web-webapi-models-apiarrayresponse-roblox-games-api-models-response-gamedetailresponse-) in Models. **Response example:** ```json { "data": [ { "id": "...", "rootPlaceId": "...", "name": "...", "description": "...", "sourceName": "...", "sourceDescription": "..." } ] } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://games.roblox.com/v1/games?universeIds={VALUE}" ``` ### GET `/v1/games/games-product-info` Gets a list of games' product info, used to purchase a game **Server:** `https://games.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeIds` | query | `integer[]` | Yes | A list of universe Ids. Cannot exceed a maximum of 100 IDs. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Games.Api.Models.Response.GameProductResponse_` - `400`: 8: The universe IDs specified are invalid. 9: Too many universe IDs were requested. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Games.Api.Models.Response.GameProductResponse_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Games.Api.Models.Response.GameProductResponse_](#roblox-web-webapi-models-apiarrayresponse-roblox-games-api-models-response-gameproductresponse-) in Models. **Response example:** ```json { "data": [ { "universeId": "...", "isForSale": "...", "productId": "...", "price": "...", "sellerId": "...", "fiatPurchaseData": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://games.roblox.com/v1/games/games-product-info?universeIds={VALUE}" ``` ### GET `/v1/games/icons` [STABLE] Fetches game icon URLs for a list of universes' root places. Ids that do not correspond to a valid universe will be filtered out. The ordering of the results is not guaranteed to be the same as the inputs. In order to correlated inputs with outputs please use the 'targetId' of the objects in the result array. **Server:** `https://thumbnails.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeIds` | query | `integer[]` | Yes | The universe ids. | | `returnPolicy` | query | `PlaceHolder \| ForcePlaceHolder \| AutoGenerated \| ForceAutoGenerated` | No | Optional policy to use in selecting game icon to return (default = PlaceHolder). Valid values: `PlaceHolder`, `ForcePlaceHolder`, `AutoGenerated`, `ForceAutoGenerated` | | `size` | query | `string enum (6 values)` | No | The thumbnail size, formatted widthxheight Valid values: `50x50`, `128x128`, `150x150`, `256x256`, `420x420`, `512x512` | | `format` | query | `Png \| Jpeg \| Webp` | No | The thumbnail format Valid values: `Png`, `Jpeg`, `Webp` | | `isCircular` | query | `true \| false` | No | The circle thumbnail output parameter, true or false Valid values: `true`, `false` | **Responses:** - `200`: OK - `400`: 1: There are too many requested Ids. 2: The requested image format is invalid. Please see documentation for valid thumbnail format parameter name and values. 3: The requested size is invalid. Please see documentation for valid thumbnail size parameter name and format. 4: The requested Ids are invalid, of an invalid type or missing. 8: The requested return policy is invalid (must be PlaceHolder, AutoGenerated or ForceAutoGenerated). 10: Circular thumbnail requests are not allowed - `403`: 9: User not authorized to use AutoGenerated or ForceAutoGenerated return policies. **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://thumbnails.roblox.com/v1/games/icons?universeIds={VALUE}" ``` ### GET `/v1/games/multiget-place-details` Get place details **Server:** `https://games.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `placeIds` | query | `integer[]` | Yes | List of placeId to uniquely Identify a place | **Responses:** - `200`: OK → `Roblox.Games.Api.Models.Response.PlaceDetails[]` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Games.Api.Models.Response.PlaceDetails[]`) See [Roblox.Games.Api.Models.Response.PlaceDetails](#roblox-games-api-models-response-placedetails) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://games.roblox.com/v1/games/multiget-place-details?placeIds={VALUE}" ``` ### GET `/v1/games/multiget-playability-status` Gets a list of universe playability statuses for the authenticated user **Server:** `https://games.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeIds` | query | `integer[]` | Yes | A list of universe Ids. Cannot exceed a maximum of 50 IDs. | **Responses:** - `200`: OK → `Roblox.Games.Api.Models.Response.PlayabilityStatusResponse[]` - `400`: 8: The universe IDs specified are invalid. 9: Too many universe IDs were requested. **Response fields** (`Roblox.Games.Api.Models.Response.PlayabilityStatusResponse[]`) See [Roblox.Games.Api.Models.Response.PlayabilityStatusResponse](#roblox-games-api-models-response-playabilitystatusresponse) in Models. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://games.roblox.com/v1/games/multiget-playability-status?universeIds={VALUE}" ``` ### GET `/v1/games/multiget/thumbnails` [STABLE] Fetch game thumbnail URLs for a list of universe IDs. **Server:** `https://thumbnails.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeIds` | query | `integer[]` | Yes | comma-delimited list of universe IDs | | `countPerUniverse` | query | `integer` | No | max number of thumbnails to return per universe | | `defaults` | query | `boolean` | No | true if defaults (if any) should be returned if no media exists | | `size` | query | `768x432 \| 576x324 \| 480x270 \| 384x216 \| 256x144` | No | The thumbnail size, formatted widthxheight Valid values: `768x432`, `576x324`, `480x270`, `384x216`, `256x144` | | `format` | query | `Png \| Jpeg \| Webp` | No | The thumbnail format Valid values: `Png`, `Jpeg`, `Webp` | | `isCircular` | query | `true \| false` | No | The circle thumbnail output parameter, true or false Valid values: `true`, `false` | **Responses:** - `200`: OK - `400`: 0: Unknown error 1: There are too many requested Ids. 4: The requested Ids are invalid, of an invalid type or missing. 5: The requested universe does not exist. 10: Circular thumbnail requests are not allowed **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://thumbnails.roblox.com/v1/games/multiget/thumbnails?universeIds={VALUE}" ``` ### GET `/v1/games/recommendations/game/{universeId}` Get games recommendations based on a given universe **Server:** `https://games.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe to base recommendations on | | `PaginationKey` | query | `string` | Yes | The key of a page, which includes the start row index and all other necessary information to query the data. This parameter is usually not needed for the first page. | | `MaxRows` | query | `integer` | Yes | The requested number of rows. | | `IsTruncatedResultsEnabled` | query | `boolean` | Yes | Truncated Results | **Responses:** - `200`: OK → `Roblox.Games.Api.Models.Response.GameRecommendationsResponse` - `400`: 1: The pagination key is invalid. - `404`: 2: The requested universe does not exist. **Response fields** (`Roblox.Games.Api.Models.Response.GameRecommendationsResponse`) See [Roblox.Games.Api.Models.Response.GameRecommendationsResponse](#roblox-games-api-models-response-gamerecommendationsresponse) in Models. **Response example:** ```json { "games": [ { "creatorId": "...", "creatorName": "...", "creatorType": "...", "creatorHasVerifiedBadge": "...", "totalUpVotes": "...", "totalDownVotes": "..." } ], "nextPaginationKey": "string" } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://games.roblox.com/v1/games/recommendations/game/{UNIVERSEID}?PaginationKey={VALUE}&MaxRows={VALUE}&IsTruncatedResultsEnabled={VALUE}" ``` ### POST `/v1/games/vip-servers/{universeId}` **Server:** `https://games.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | | **Request Body:** `application/json` — Type: `any` **Responses:** - `200`: Success → `GameServerResponse` **Response fields** (`GameServerResponse`) See [GameServerResponse](#gameserverresponse) in Models. **Response example:** ```json { "id": "string", "maxPlayers": 0, "playing": 0, "playerTokens": [ "string" ], "players": [ { "id": "...", "name": "...", "displayName": "...", "playerToken": "..." } ], "fps": 0 } ``` **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://games.roblox.com/v1/games/vip-servers/{UNIVERSEID}" \ -H "Content-Type: application/json" \ -d '"..."' ``` ### GET `/v1/games/votes` **Server:** `https://games.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeIds` | query | `integer[]` | No | | **Responses:** - `200`: Success → `GameVoteResponseApiArrayResponse` **Response fields** (`GameVoteResponseApiArrayResponse`) See [GameVoteResponseApiArrayResponse](#gamevoteresponseapiarrayresponse) in Models. **Response example:** ```json { "data": [ { "id": "...", "upVotes": "...", "downVotes": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://games.roblox.com/v1/games/votes" ``` ### POST `/v1/games/{gameId}/thumbnail/image` Uploads a game thumbnail. **Server:** `https://publish.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The universe Id. | **Responses:** - `200`: OK → `Roblox.Publish.Api.UploadResponse` - `400`: 1: File uploaded does not match known image format. Try converting to png. 2: File not present in request. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 5: You do not have permission to manage this item. - `404`: 4: Target item is invalid or does not exist. - `429`: 3: You're uploading too much, please wait and try again later. **Response fields** (`Roblox.Publish.Api.UploadResponse`) See [Roblox.Publish.Api.UploadResponse](#roblox-publish-api-uploadresponse) in Models. **Response example:** ```json { "targetId": 0 } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://publish.roblox.com/v1/games/{GAMEID}/thumbnail/image" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### GET `/v1/games/{placeId}/private-servers` **Server:** `https://games.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `placeId` | path | `integer` | Yes | | | `exclusiveStartKeyCursor` | query | `any` | No | | | `excludeFriendServers` | query | `boolean` | No | | **Responses:** - `200`: Success → `GetPrivateServerListResponse` **Response fields** (`GetPrivateServerListResponse`) See [GetPrivateServerListResponse](#getprivateserverlistresponse) in Models. **Response example:** ```json { "data": [ { "id": "...", "maxPlayers": "...", "playing": "...", "playerTokens": "...", "players": "...", "fps": "..." } ], "previousPageCursor": "string", "nextPageCursor": "string", "gameJoinRestricted": false } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://games.roblox.com/v1/games/{PLACEID}/private-servers" ``` ### GET `/v1/games/{universeId}/favorites` Returns if a game was marked as favorite for the authenticated user **Server:** `https://games.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The Id of the universe. | **Responses:** - `200`: OK → `Roblox.Games.Api.Models.Response.GameFavoriteResponse` - `400`: 3: The universe's root place is invalid. - `401`: 0: Authorization has been denied for this request. - `404`: 2: The requested universe does not exist. **Response fields** (`Roblox.Games.Api.Models.Response.GameFavoriteResponse`) See [Roblox.Games.Api.Models.Response.GameFavoriteResponse](#roblox-games-api-models-response-gamefavoriteresponse) in Models. **Response example:** ```json { "isFavorited": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://games.roblox.com/v1/games/{UNIVERSEID}/favorites" ``` ### POST `/v1/games/{universeId}/favorites` Favors (or unfavors) a game for the authenticated user **Server:** `https://games.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The Id of the universe. | **Request Body:** `application/json` — Type: `Roblox.Games.Api.Models.Request.GameFavoritesRequest` See [Roblox.Games.Api.Models.Request.GameFavoritesRequest](#roblox-games-api-models-request-gamefavoritesrequest) in Models. **Request example:** ```json { "isFavorited": false } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 3: The universe's root place is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 11: You are not authorized to perform this action. - `404`: 2: The requested universe does not exist. - `429`: 4: Too many requests have been made. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://games.roblox.com/v1/games/{UNIVERSEID}/favorites" \ -H "Content-Type: application/json" \ -d '{ "isFavorited": false }' ``` ### GET `/v1/games/{universeId}/favorites/count` Get the favorites count of a specific game. **Server:** `https://games.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The Id of the universe. | **Responses:** - `200`: OK → `Roblox.Games.Api.Models.Response.GameFavoritesCountResponse` - `400`: 3: The universe's root place is invalid. - `404`: 2: The requested universe does not exist. **Response fields** (`Roblox.Games.Api.Models.Response.GameFavoritesCountResponse`) See [Roblox.Games.Api.Models.Response.GameFavoritesCountResponse](#roblox-games-api-models-response-gamefavoritescountresponse) in Models. **Response example:** ```json { "favoritesCount": 0 } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://games.roblox.com/v1/games/{UNIVERSEID}/favorites/count" ``` ### GET `/v1/games/{universeId}/media` *(deprecated)* Get the game media data Use https://games.roblox.com/v2/games/{universeId}/media instead **Server:** `https://games.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The id of the universe we get media data from. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Games.Api.Models.Response.GameMediaItem_` - `400`: 3: The universe's root place is invalid. - `404`: 2: The requested universe does not exist. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Games.Api.Models.Response.GameMediaItem_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Games.Api.Models.Response.GameMediaItem_](#roblox-web-webapi-models-apiarrayresponse-roblox-games-api-models-response-gamemediaitem-) in Models. **Response example:** ```json { "data": [ { "id": "...", "assetTypeId": "...", "assetType": "...", "imageId": "...", "videoHash": "...", "videoTitle": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://games.roblox.com/v1/games/{UNIVERSEID}/media" ``` ### GET `/v1/games/{universeId}/thumbnails` [STABLE] Fetches game thumbnail URLs for a list of universes' thumbnail ids. Ids that do not correspond to a valid thumbnail will be filtered out. **Server:** `https://thumbnails.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | | | `thumbnailIds` | query | `integer[]` | Yes | | | `size` | query | `768x432 \| 576x324 \| 480x270 \| 384x216 \| 256x144` | No | The thumbnail size, formatted widthxheight Valid values: `768x432`, `576x324`, `480x270`, `384x216`, `256x144` | | `format` | query | `Png \| Jpeg \| Webp` | No | The thumbnail format Valid values: `Png`, `Jpeg`, `Webp` | | `isCircular` | query | `true \| false` | No | The circle thumbnail output parameter, true or false Valid values: `true`, `false` | **Responses:** - `200`: OK - `400`: 1: There are too many requested Ids. 2: The requested image format is invalid. Please see documentation for valid thumbnail format parameter name and values. 3: The requested size is invalid. Please see documentation for valid thumbnail size parameter name and format. 4: The requested Ids are invalid, of an invalid type or missing. 10: Circular thumbnail requests are not allowed - `404`: 5: The requested universe does not exist. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://thumbnails.roblox.com/v1/games/{UNIVERSEID}/thumbnails?thumbnailIds={VALUE}" ``` ### PATCH `/v1/games/{universeId}/user-votes` **Server:** `https://games.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | | **Request Body:** `application/json-patch+json` — Type: `any` **Responses:** - `200`: Success → `ApiEmptyResponseModel` **Response fields** (`ApiEmptyResponseModel`) See [ApiEmptyResponseModel](#apiemptyresponsemodel) in Models. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://games.roblox.com/v1/games/{UNIVERSEID}/user-votes" \ -H "Content-Type: application/json" \ -d '"..."' ``` ### GET `/v1/games/{universeId}/votes` **Server:** `https://games.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | | **Responses:** - `200`: Success → `GameVoteResponse` **Response fields** (`GameVoteResponse`) See [GameVoteResponse](#gamevoteresponse) in Models. **Response example:** ```json { "id": 0, "upVotes": 0, "downVotes": 0 } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://games.roblox.com/v1/games/{UNIVERSEID}/votes" ``` ### GET `/v1/games/{universeId}/votes/user` **Server:** `https://games.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | | **Responses:** - `200`: Success → `UserGameVoteResponse` **Response fields** (`UserGameVoteResponse`) See [UserGameVoteResponse](#usergamevoteresponse) in Models. **Response example:** ```json { "canVote": false, "userVote": false, "reasonForNotVoteable": "string" } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://games.roblox.com/v1/games/{UNIVERSEID}/votes/user" ``` ### GET `/v1/gametemplates` Gets a page of templates that can be used to start off making games. Templates subject to change without notice. Sort order of templates specified by Roblox. **Server:** `https://develop.roblox.com` **Auth:** **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Api.Develop.Models.GameTemplateModel_` **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Api.Develop.Models.GameTemplateModel_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Api.Develop.Models.GameTemplateModel_](#roblox-web-webapi-models-apiarrayresponse-roblox-api-develop-models-gametemplatemodel-) in Models. **Response example:** ```json { "data": [ { "gameTemplateType": "...", "hasTutorials": "...", "universe": "..." } ] } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://develop.roblox.com/v1/gametemplates" ``` ### GET `/v1/gender` *(deprecated)* Get the user's gender Replaced by users.roblox.com/v1/gender **Server:** `https://accountinformation.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.AccountInformation.Api.Models.GenderResponse` - `400`: 1: User not found. - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.AccountInformation.Api.Models.GenderResponse`) See [Roblox.AccountInformation.Api.Models.GenderResponse](#roblox-accountinformation-api-models-genderresponse) in Models. **Response example:** ```json { "gender": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountinformation.roblox.com/v1/gender" ``` ### POST `/v1/gender` *(deprecated)* Update the user's gender Replaced by users.roblox.com/v1/gender **Server:** `https://accountinformation.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.AccountInformation.Api.Models.GenderRequest` See [Roblox.AccountInformation.Api.Models.GenderRequest](#roblox-accountinformation-api-models-genderrequest) in Models. **Request example:** ```json { "gender": "string" } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: User not found. 6: The gender provided is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `500`: 0: An unknown error occured. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountinformation.roblox.com/v1/gender" \ -H "Content-Type: application/json" \ -d '{ "gender": "string" }' ``` ### GET `/v1/gender#UsersApi` Get the user's gender **Server:** `https://users.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Users.Api.GenderResponse` - `400`: 1: User not found. - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Users.Api.GenderResponse`) See [Roblox.Users.Api.GenderResponse](#roblox-users-api-genderresponse) in Models. **Response example:** ```json { "gender": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://users.roblox.com/v1/gender#UsersApi" ``` ### POST `/v1/gender#UsersApi` Update the user's gender **Server:** `https://users.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Users.Api.GenderRequest` See [Roblox.Users.Api.GenderRequest](#roblox-users-api-genderrequest) in Models. **Request example:** ```json { "gender": 1 } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: User not found. 6: The gender provided is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: PIN is locked. - `500`: 0: An unknown error occured. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://users.roblox.com/v1/gender#UsersApi" \ -H "Content-Type: application/json" \ -d '{ "gender": 1 }' ``` ### GET `/v1/groups/configuration/metadata` Gets Group configuration contextual information **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupConfigurationDisplayOptionsResponse` **Response fields** (`Roblox.Groups.Api.GroupConfigurationDisplayOptionsResponse`) See [Roblox.Groups.Api.GroupConfigurationDisplayOptionsResponse](#roblox-groups-api-groupconfigurationdisplayoptionsresponse) in Models. **Response example:** ```json { "groupConfiguration": { "nameMaxLength": 0, "descriptionMaxLength": 0, "iconMaxFileSizeMb": 0, "coverPhotoMaxFileSizeMb": 0, "validCoverPhotoDimensions": "string", "cost": 0 }, "recurringPayoutsConfiguration": { "maxPayoutPartners": 0 }, "roleConfiguration": { "nameMaxLength": 0, "descriptionMaxLength": 0, "limit": 0, "cost": 0, "minRank": 0, "maxRank": 0 }, "groupNameChangeConfiguration": { "cost": 0, "cooldownInDays": 0, "ownershipCooldownInDays": 0 }, "isPremiumPayoutsEnabled": false, "isDefaultEmblemPolicyEnabled": false } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/configuration/metadata" ``` ### POST `/v1/groups/create` Creates a new group. This endpoint will charge Robux for the group purchase. Accepts "icon" and "coverPhoto" in Files object. Defaults to first file if "icon" is not present. Http status code 413 is thrown when the group icon file size is too large. **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `multipart/form-data` — Type: `object` | Property | Type | Required | Description | |----------|------|----------|-------------| | `name` | `string` | No | The name of the group. | | `description` | `string` | No | The group description. | | `publicGroup` | `boolean` | No | Whether or not the group is open for anyone to join. | | `buildersClubMembersOnly` | `boolean` | No | Whether or not the group is only open to join for builders club members. | | `Files` | `string` | No | | **Request example:** ```json { "name": "string", "description": "string", "publicGroup": false, "buildersClubMembersOnly": false, "Files": "string" } ``` **Responses:** - `200`: OK → `Roblox.Web.Responses.Groups.GroupResponseV2` - `400`: 13: The name is invalid. 15: The group icon is invalid. 16: The group icon is missing from the request. 18: The description is too long. 19: The name is too long. 20: The name has been taken. 46: Invalid file format or dimensions for group cover photo. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 10: User must have builders club membership. 11: User is in maximum number of groups. 12: Insufficient Robux funds. 14: The name is moderated. - `409`: 37: The name was in use too recently. - `413`: 0: Unknown error. - `429`: 17: Too many requests. - `503`: 21: Group creation is currently disabled. **Response fields** (`Roblox.Web.Responses.Groups.GroupResponseV2`) See [Roblox.Web.Responses.Groups.GroupResponseV2](#roblox-web-responses-groups-groupresponsev2) in Models. **Response example:** ```json { "id": 0, "name": "string", "description": "string", "owner": { "id": 0, "type": 0, "name": "string" }, "memberCount": 0, "created": "2024-01-01T00:00:00Z" } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/create" \ -F "name=string" \ -F "description=string" \ -F "publicGroup=false" \ -F "buildersClubMembersOnly=false" \ -F "Files=@file.bin;type=application/octet-stream" ``` ### PATCH `/v1/groups/icon` Updates the group icon. **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | query | `integer` | Yes | The group Id. | **Request Body:** `multipart/form-data` — Type: `object` | Property | Type | Required | Description | |----------|------|----------|-------------| | `Files` | `string` | No | | **Request example:** ```json { "Files": "string" } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: Group is invalid or does not exist. 16: The group icon is missing from the request. 17: Too many requests. 30: Invalid file type for group icon. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 23: Insufficient permissions to complete the request. - `413`: 0: Unknown error. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/icon?groupId={VALUE}" \ -F "Files=@file.bin;type=application/octet-stream" ``` ### GET `/v1/groups/icons` [STABLE] Fetches thumbnail URLs for a list of groups. Ids that do not correspond to groups will be filtered out. **Server:** `https://thumbnails.roblox.com` **Auth:** **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupIds` | query | `integer[]` | Yes | | | `size` | query | `150x150 \| 420x420` | No | The thumbnail size, formatted widthxheight Valid values: `150x150`, `420x420` | | `format` | query | `Png \| Webp` | No | The thumbnail format Valid values: `Png`, `Webp` | | `isCircular` | query | `true \| false` | No | The circle thumbnail output parameter, true or false Valid values: `true`, `false` | **Responses:** - `200`: OK - `400`: 1: There are too many requested Ids. 2: The requested image format is invalid. Please see documentation for valid thumbnail format parameter name and values. 3: The requested size is invalid. Please see documentation for valid thumbnail size parameter name and format. 4: The requested Ids are invalid, of an invalid type or missing. 10: Circular thumbnail requests are not allowed **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://thumbnails.roblox.com/v1/groups/icons?groupIds={VALUE}" ``` ### GET `/v1/groups/metadata` Gets Groups contextual information: Max number of groups a user can be part of. Current number of groups a user is a member of. Whether to show/hide certain features based on device type. **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupsDisplayOptionsResponse` **Response fields** (`Roblox.Groups.Api.GroupsDisplayOptionsResponse`) See [Roblox.Groups.Api.GroupsDisplayOptionsResponse](#roblox-groups-api-groupsdisplayoptionsresponse) in Models. **Response example:** ```json { "groupLimit": 0, "currentGroupCount": 0, "groupStatusMaxLength": 0, "groupPostMaxLength": 0, "isGroupWallNotificationsEnabled": false, "groupWallNotificationsSubscribeIntervalInMilliseconds": 0 } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/metadata" ``` ### POST `/v1/groups/policies` Gets group policy info used for compliance. **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Groups.Api.GroupPolicyRequest` See [Roblox.Groups.Api.GroupPolicyRequest](#roblox-groups-api-grouppolicyrequest) in Models. **Request example:** ```json { "groupIds": [ 0 ] } ``` **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupPoliciesResponse` - `400`: 1: Too many ids in request. 2: Ids could not be parsed from request. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Groups.Api.GroupPoliciesResponse`) See [Roblox.Groups.Api.GroupPoliciesResponse](#roblox-groups-api-grouppoliciesresponse) in Models. **Response example:** ```json { "groups": [ { "canViewGroup": "...", "groupId": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/policies" \ -H "Content-Type: application/json" \ -d '{ "groupIds": [ 0 ] }' ``` ### GET `/v1/groups/search` Search for groups by keyword. **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `keyword` | query | `string` | Yes | The keyword or phrase to use as the search parameter. | | `prioritizeExactMatch` | query | `boolean` | No | Whether or not to prioritize the exact match for the keyword (optional, defaults to false). | | `limit` | query | `10 \| 25 \| 50 \| 100` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupSearchPageResponse` - `400`: 2: Search term not appropriate for Roblox. 3: Search term was left empty. 4: Search terms can be 2 to 50 characters long. **Response fields** (`Roblox.Groups.Api.GroupSearchPageResponse`) See [Roblox.Groups.Api.GroupSearchPageResponse](#roblox-groups-api-groupsearchpageresponse) in Models. **Response example:** ```json { "keyword": "string", "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "id": "...", "name": "...", "description": "...", "memberCount": "...", "previousName": "...", "publicEntryAllowed": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/search?keyword={VALUE}" ``` ### GET `/v1/groups/search/lookup` Looks up groups by a name. Prioritizes an exact match as the first result. Should only be used for direct lookups where a user is inputting a group name, shouldn't be used for search pages. **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupName` | query | `string` | Yes | The group name. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Web.Responses.Groups.GroupBasicResponse_` - `400`: 1: Name is missing or has invalid characters. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Web.Responses.Groups.GroupBasicResponse_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Web.Responses.Groups.GroupBasicResponse_](#roblox-web-webapi-models-apiarrayresponse-roblox-web-responses-groups-groupbasicresponse-) in Models. **Response example:** ```json { "data": [ { "id": "...", "name": "...", "memberCount": "...", "hasVerifiedBadge": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/search/lookup?groupName={VALUE}" ``` ### GET `/v1/groups/search/metadata` Get suggested groups and other miscellaneous information needed for the group/join page like flags Although there is no reason for this to require an authenticated user right now, in the future, we will use coco to return different suggested groups based upon that user's request context **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupSearchMetadataResponse` - `404`: 5: No Localized Version of group search category exists **Response fields** (`Roblox.Groups.Api.GroupSearchMetadataResponse`) See [Roblox.Groups.Api.GroupSearchMetadataResponse](#roblox-groups-api-groupsearchmetadataresponse) in Models. **Response example:** ```json { "SuggestedGroupKeywords": [ "string" ], "ShowFriendsGroupsSort": false } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/search/metadata" ``` ### GET `/v1/groups/{groupId}` Gets group information **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The group Id. | **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupDetailResponse` - `400`: 1: Group is invalid or does not exist. **Response fields** (`Roblox.Groups.Api.GroupDetailResponse`) See [Roblox.Groups.Api.GroupDetailResponse](#roblox-groups-api-groupdetailresponse) in Models. **Response example:** ```json { "id": 0, "name": "string", "description": "string", "owner": { "buildersClubMembershipType": 0, "hasVerifiedBadge": false, "userId": 0, "username": "string", "displayName": "string" }, "shout": { "body": "string", "poster": { "buildersClubMembershipType": "...", "hasVerifiedBadge": "...", "userId": "...", "username": "...", "displayName": "..." }, "created": "2024-01-01T00:00:00Z", "updated": "2024-01-01T00:00:00Z" }, "memberCount": 0 } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}" ``` ### GET `/v1/groups/{groupId}/audit-log` Gets the Group's audit log **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The id of the group the user is in. | | `actionType` | query | `string enum (74 values)` | No | Filter for specific type of action performed Valid values: `DeletePost`, `RemoveMember`, `AcceptJoinRequest`, `DeclineJoinRequest`, `PostStatus`, `ChangeRank`, `BuyAd`, `SendAllyRequest`, `CreateEnemy`, `AcceptAllyRequest`, `DeclineAllyRequest`, `DeleteAlly`, `DeleteEnemy`, `AddGroupPlace`, `RemoveGroupPlace`, `CreateItems`, `ConfigureItems`, `SpendGroupFunds`, `ChangeOwner`, `Delete`, `AdjustCurrencyAmounts`, `Abandon`, `Claim`, `Rename`, `ChangeDescription`, `InviteToClan`, `KickFromClan`, `CancelClanInvite`, `BuyClan`, `CreateGroupAsset`, `UpdateGroupAsset`, `ConfigureGroupAsset`, `RevertGroupAsset`, `CreateGroupDeveloperProduct`, `ConfigureGroupGame`, `CreateGroupDeveloperSubscriptionProduct`, `Lock`, `Unlock`, `CreateGamePass`, `CreateBadge`, `ConfigureBadge`, `SavePlace`, `PublishPlace`, `UpdateRolesetRank`, `UpdateRolesetData`, `BanMember`, `UnbanMember`, `CreateForumCategory`, `UpdateForumCategory`, `ArchiveForumCategory`, `DeleteForumCategory`, `DeleteForumPost`, `DeleteForumComment`, `PinForumPost`, `UnpinForumPost`, `LockForumPost`, `UnlockForumPost`, `CreateRoleset`, `DeleteRoleset`, `CreateCommerceProduct`, `SetCommerceProductActive`, `ArchiveCommerceProduct`, `AcceptCommerceProductBundlingFee`, `SetCommerceProductInactive`, `ConnectMerchant`, `DisconnectMerchant`, `JoinGroup`, `LeaveGroup`, `UpdateGroupIcon`, `UpdateGroupCoverPhoto`, `AssignRole`, `UnassignRole`, `PublishAnnouncement`, `DeleteAnnouncement` | | `userId` | query | `integer` | No | Filter for specific user id | | `limit` | query | `10 \| 25 \| 50 \| 100` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `Asc \| Desc` | No | The order the results are sorted in. Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupAuditLogPageResponse_Roblox.Groups.Api.Models.Response.GroupAuditLogResponseItem_` - `400`: 1: Group is invalid or does not exist. 8: Invalid or missing pagination parameters - `401`: 0: Authorization has been denied for this request. - `403`: 23: Insufficient permissions to complete the request. **Response fields** (`Roblox.Groups.Api.GroupAuditLogPageResponse_Roblox.Groups.Api.Models.Response.GroupAuditLogResponseItem_`) See [Roblox.Groups.Api.GroupAuditLogPageResponse_Roblox.Groups.Api.Models.Response.GroupAuditLogResponseItem_](#roblox-groups-api-groupauditlogpageresponse-roblox-groups-api-models-response-groupauditlogresponseitem-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "actor": "...", "actionType": "...", "description": "...", "created": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/audit-log" ``` ### GET `/v1/groups/{groupId}/bans` Gets the bans for the group **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The group Id. | | `limit` | query | `10 \| 25 \| 50 \| 100` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `Asc \| Desc` | No | The order the results are sorted in. Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Groups.Api.GroupBanMemberResponse_` - `401`: 0: Authorization has been denied for this request. - `403`: 19: You have insufficient permissions for this request. - `404`: 1: The group is invalid or does not exist. - `405`: 18: The operation is temporarily unavailable. Please try again later. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Groups.Api.GroupBanMemberResponse_`) See [Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Groups.Api.GroupBanMemberResponse_](#roblox-web-webapi-models-apipageresponse-roblox-groups-api-groupbanmemberresponse-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "user": "...", "actingUser": "...", "created": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/bans" ``` ### GET `/v1/groups/{groupId}/bans/{userId}` Fetch the group ban of a user from a group **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The group Id. | | `userId` | path | `integer` | Yes | The user Id. | **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupBanMemberResponse` - `401`: 0: Authorization has been denied for this request. - `403`: 19: You have insufficient permissions for this request. - `404`: 1: The group is invalid or does not exist. 3: The user is invalid or does not exist. - `405`: 18: The operation is temporarily unavailable. Please try again later. **Response fields** (`Roblox.Groups.Api.GroupBanMemberResponse`) See [Roblox.Groups.Api.GroupBanMemberResponse](#roblox-groups-api-groupbanmemberresponse) in Models. **Response example:** ```json { "user": { "buildersClubMembershipType": 0, "hasVerifiedBadge": false, "userId": 0, "username": "string", "displayName": "string" }, "actingUser": { "user": { "buildersClubMembershipType": "...", "hasVerifiedBadge": "...", "userId": "...", "username": "...", "displayName": "..." }, "role": { "id": "...", "name": "...", "description": "...", "rank": "...", "memberCount": "...", "isBase": "..." } }, "created": "2024-01-01T00:00:00Z" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/bans/{USERID}" ``` ### POST `/v1/groups/{groupId}/bans/{userId}` Bans a user from a group **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The group Id. | | `userId` | path | `integer` | Yes | The Id of the user being banned. | **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupBanMemberResponse` - `400`: 28: The user is already banned from the group. 31: You cannot perform this action against the group owner. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 19: You have insufficient permissions for this request. - `404`: 1: The group is invalid or does not exist. 3: The user is invalid or does not exist. 3: The user is invalid or does not exist. 15: User is not a member of the group. - `405`: 18: The operation is temporarily unavailable. Please try again later. **Response fields** (`Roblox.Groups.Api.GroupBanMemberResponse`) See [Roblox.Groups.Api.GroupBanMemberResponse](#roblox-groups-api-groupbanmemberresponse) in Models. **Response example:** ```json { "user": { "buildersClubMembershipType": 0, "hasVerifiedBadge": false, "userId": 0, "username": "string", "displayName": "string" }, "actingUser": { "user": { "buildersClubMembershipType": "...", "hasVerifiedBadge": "...", "userId": "...", "username": "...", "displayName": "..." }, "role": { "id": "...", "name": "...", "description": "...", "rank": "...", "memberCount": "...", "isBase": "..." } }, "created": "2024-01-01T00:00:00Z" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/bans/{USERID}" ``` ### DELETE `/v1/groups/{groupId}/bans/{userId}` Unbans a user from a group **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The group Id. | | `userId` | path | `integer` | Yes | The Id of the user being unbanned. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 29: The user is not banned from the group. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 19: You have insufficient permissions for this request. - `404`: 1: The group is invalid or does not exist. - `405`: 18: The operation is temporarily unavailable. Please try again later. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/bans/{USERID}" ``` ### GET `/v1/groups/{groupId}/blocked-keywords` Retrieves a paginated list of blocked keywords for a specific Group. **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | | | `limit` | query | `10 \| 25 \| 50 \| 100` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `Asc \| Desc` | No | The order the results are sorted in. Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Groups.Api.BlockedKeywordPageResponse_Roblox.Groups.Client.BlockedKeywordModel_` - `400`: 1: Group is invalid or does not exist. 6: Limit is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: 9: User is invalid or does not exist. 10: Insufficient permissions to complete the request. - `405`: 31: Service is currently unavailable. **Response fields** (`Roblox.Groups.Api.BlockedKeywordPageResponse_Roblox.Groups.Client.BlockedKeywordModel_`) See [Roblox.Groups.Api.BlockedKeywordPageResponse_Roblox.Groups.Client.BlockedKeywordModel_](#roblox-groups-api-blockedkeywordpageresponse-roblox-groups-client-blockedkeywordmodel-) in Models. **Response example:** ```json { "totalActiveKeywordsCount": 0, "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "id": "...", "keyword": "...", "createdBy": "...", "isPrivate": "...", "createdAt": "...", "updatedAt": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/blocked-keywords" ``` ### POST `/v1/groups/{groupId}/blocked-keywords` Adds new blocked keyword(s) to the specified Group. **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | | **Request Body:** `application/json` — Type: `Roblox.Groups.Api.CreateBlockedKeywordsRequest` See [Roblox.Groups.Api.CreateBlockedKeywordsRequest](#roblox-groups-api-createblockedkeywordsrequest) in Models. **Request example:** ```json { "keywords": "string", "isPrivate": false } ``` **Responses:** - `200`: OK → `Roblox.Groups.Client.CreateBlockedKeywordsResponse` - `400`: 1: Group is invalid or does not exist. 2: One or more keywords are invalid. 4: Invalid request. 12: The provided content was moderated. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 8: Insufficient permissions to complete the request. 9: User is invalid or does not exist. 10: Insufficient permissions to complete the request. - `405`: 31: Service is currently unavailable. - `409`: 11: There was a conflict in your request. **Response fields** (`Roblox.Groups.Client.CreateBlockedKeywordsResponse`) See [Roblox.Groups.Client.CreateBlockedKeywordsResponse](#roblox-groups-client-createblockedkeywordsresponse) in Models. **Response example:** ```json { "createdKeywords": [ { "id": "...", "keyword": "...", "createdBy": "...", "isPrivate": "...", "createdAt": "...", "updatedAt": "..." } ], "hadModeratedKeywords": false, "hadDuplicateKeywords": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/blocked-keywords" \ -H "Content-Type: application/json" \ -d '{ "keywords": "string", "isPrivate": false }' ``` ### PATCH `/v1/groups/{groupId}/blocked-keywords/{keywordId}` Updates an existing blocked keyword for the specified Group. **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | | | `keywordId` | path | `string` | Yes | | **Request Body:** `application/json` — Type: `Roblox.Groups.Api.UpdateBlockedKeywordRequest` See [Roblox.Groups.Api.UpdateBlockedKeywordRequest](#roblox-groups-api-updateblockedkeywordrequest) in Models. **Request example:** ```json { "keyword": "string", "isPrivate": false } ``` **Responses:** - `200`: OK → `Roblox.Groups.Client.BlockedKeywordModel` - `400`: 1: Group is invalid or does not exist. 3: KeywordId is invalid. 4: Invalid request. 12: The provided content was moderated. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 8: Insufficient permissions to complete the request. 9: User is invalid or does not exist. 10: Insufficient permissions to complete the request. - `404`: 7: Not found. - `405`: 31: Service is currently unavailable. - `409`: 11: There was a conflict in your request. **Response fields** (`Roblox.Groups.Client.BlockedKeywordModel`) See [Roblox.Groups.Client.BlockedKeywordModel](#roblox-groups-client-blockedkeywordmodel) in Models. **Response example:** ```json { "id": "string", "keyword": "string", "createdBy": 0, "isPrivate": false, "createdAt": "2024-01-01T00:00:00Z", "updatedAt": "2024-01-01T00:00:00Z" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/blocked-keywords/{KEYWORDID}" \ -H "Content-Type: application/json" \ -d '{ "keyword": "string", "isPrivate": false }' ``` ### DELETE `/v1/groups/{groupId}/blocked-keywords/{keywordId}` Deletes a specific blocked keyword from the specified Group. **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | | | `keywordId` | path | `string` | Yes | | **Responses:** - `200`: OK - `400`: 1: Group is invalid or does not exist. 3: KeywordId is invalid. 4: Invalid request. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 9: User is invalid or does not exist. 10: Insufficient permissions to complete the request. - `404`: 7: Not found. - `405`: 31: Service is currently unavailable. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/blocked-keywords/{KEYWORDID}" ``` ### POST `/v1/groups/{groupId}/change-owner` Changes the group owner to another user. **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The group Id. | **Request Body:** `application/json` — Type: `Roblox.Groups.Api.ChangeOwnerRequest` See [Roblox.Groups.Api.ChangeOwnerRequest](#roblox-groups-api-changeownerrequest) in Models. **Request example:** ```json { "userId": 0 } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: The group is invalid or does not exist. 3: The user is invalid or does not exist. 15: User is not a member of the group. 16: The user does not have the necessary level of premium membership. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 17: You are not authorized to change the owner of this group. 25: 2-Step Verification is required to make further transactions. Go to Settings > Security to complete 2-Step Verification. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/change-owner" \ -H "Content-Type: application/json" \ -d '{ "userId": 0 }' ``` ### POST `/v1/groups/{groupId}/claim-ownership` Claims ownership of the group as the authenticated user **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The group Id. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: The group is invalid or does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 11: You are not authorized to claim this group 12: This group already has an owner 13: Too many attempts to claim groups. Please try again later. - `503`: 18: The operation is temporarily unavailable. Please try again later. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/claim-ownership" ``` ### GET `/v1/groups/{groupId}/community-feature-freezes` Gets the freeze status of the community features for a group. **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | | **Responses:** - `200`: OK → `Roblox.Groups.Api.GetCommunityFeatureFreezesResponse` **Response fields** (`Roblox.Groups.Api.GetCommunityFeatureFreezesResponse`) See [Roblox.Groups.Api.GetCommunityFeatureFreezesResponse](#roblox-groups-api-getcommunityfeaturefreezesresponse) in Models. **Response example:** ```json { "features": [ { "feature": "...", "isDisabled": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/community-feature-freezes" ``` ### GET `/v1/groups/{groupId}/configuration` Gets group configuration information **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The group Id. | **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupConfigurationDetailsResponse` - `400`: 1: Group is invalid or does not exist. 15: User is not a member of the group. - `403`: 1: Group is invalid or does not exist. 23: Insufficient permissions to complete the request. - `503`: 31: Service is currently unavailable. **Response fields** (`Roblox.Groups.Api.GroupConfigurationDetailsResponse`) See [Roblox.Groups.Api.GroupConfigurationDetailsResponse](#roblox-groups-api-groupconfigurationdetailsresponse) in Models. **Response example:** ```json { "groupId": 0, "emblemId": 0, "coverPhotoId": 0 } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/configuration" ``` ### PATCH `/v1/groups/{groupId}/description` Updates the groups description **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The id of the group the user is in. | **Request Body:** `application/json` — Type: `Roblox.Groups.Api.UpdateGroupDescriptionRequest` See [Roblox.Groups.Api.UpdateGroupDescriptionRequest](#roblox-groups-api-updategroupdescriptionrequest) in Models. **Request example:** ```json { "description": "string" } ``` **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupDescriptionResponse` - `400`: 1: Group is invalid or does not exist. 29: Your group description was empty. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: The description is too long. 23: Insufficient permissions to complete the request. **Response fields** (`Roblox.Groups.Api.GroupDescriptionResponse`) See [Roblox.Groups.Api.GroupDescriptionResponse](#roblox-groups-api-groupdescriptionresponse) in Models. **Response example:** ```json { "newDescription": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/description" \ -H "Content-Type: application/json" \ -d '{ "description": "string" }' ``` ### GET `/v1/groups/{groupId}/emotes` Gets a groups emote sets. **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | | **Responses:** - `200`: OK → `Roblox.Groups.Client.GetGroupEmoteSetsResponse` **Response fields** (`Roblox.Groups.Client.GetGroupEmoteSetsResponse`) See [Roblox.Groups.Client.GetGroupEmoteSetsResponse](#roblox-groups-client-getgroupemotesetsresponse) in Models. **Response example:** ```json { "emoteSets": [ { "id": "...", "name": "...", "emotes": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/emotes" ``` ### GET `/v1/groups/{groupId}/features` Gets the freeze status of all features and the lock status for a group. **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | | **Responses:** - `200`: OK → `Roblox.Groups.Api.GetGroupFeaturesResponse` - `400`: 1: Group is invalid or does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 23: Insufficient permissions to complete the request. 49: User is invalid or does not exist **Response fields** (`Roblox.Groups.Api.GetGroupFeaturesResponse`) See [Roblox.Groups.Api.GetGroupFeaturesResponse](#roblox-groups-api-getgroupfeaturesresponse) in Models. **Response example:** ```json { "isLocked": false, "features": [ { "feature": "...", "isFeatureBlocked": "...", "expiration": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/features" ``` ### PATCH `/v1/groups/{groupId}/features` Sets the desired status of group features. Currently only removes active freezes for features set to Roblox.Groups.Api.FeatureStatus.On. **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | | **Request Body:** `application/json` — Type: `Roblox.Groups.Api.SetFeaturesRequestModel` See [Roblox.Groups.Api.SetFeaturesRequestModel](#roblox-groups-api-setfeaturesrequestmodel) in Models. **Request example:** ```json { "Features": { "Payouts": "On", "ContentUpload": "On", "GroupOwnershipTransfer": "On", "GameOwnershipTransfer": "On", "ForumRead": "On", "ForumWrite": "On" } } ``` **Responses:** - `200`: OK → `Roblox.Groups.Api.SetFeaturesResponseModel` - `400`: 1: Group is invalid or does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 23: Insufficient permissions to complete the request. 49: User is invalid or does not exist **Response fields** (`Roblox.Groups.Api.SetFeaturesResponseModel`) See [Roblox.Groups.Api.SetFeaturesResponseModel](#roblox-groups-api-setfeaturesresponsemodel) in Models. **Response example:** ```json { "Updated": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/features" \ -H "Content-Type: application/json" \ -d '{ "Features": { "Payouts": "On", "ContentUpload": "On", "GroupOwnershipTransfer": "On", "GameOwnershipTransfer": "On", "ForumRead": "On", "ForumWrite": "On" } }' ``` ### GET `/v1/groups/{groupId}/features/status` Checks whether a group has ANY feature disabled (includes feature freezes and group lock). Used to display a banner on Creator Hub/Studio to inform group members that some features may not be available. **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | | **Responses:** - `200`: OK → `Roblox.Groups.Api.HasGroupFeaturesBlockedResponse` - `400`: 1: Group is invalid or does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 23: Insufficient permissions to complete the request. 49: User is invalid or does not exist **Response fields** (`Roblox.Groups.Api.HasGroupFeaturesBlockedResponse`) See [Roblox.Groups.Api.HasGroupFeaturesBlockedResponse](#roblox-groups-api-hasgroupfeaturesblockedresponse) in Models. **Response example:** ```json { "hasFeaturesBlocked": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/features/status" ``` ### GET `/v1/groups/{groupId}/join-requests` Gets a page of Group Join Requests for a group. **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The group id. | | `limit` | query | `10 \| 25 \| 50 \| 100` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `Asc \| Desc` | No | The order the results are sorted in. Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Groups.Api.GroupJoinRequestResponse_` - `400`: 1: The group is invalid or does not exist. 36: The pagination cursor is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: 19: You have insufficient permissions for this request. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Groups.Api.GroupJoinRequestResponse_`) See [Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Groups.Api.GroupJoinRequestResponse_](#roblox-web-webapi-models-apipageresponse-roblox-groups-api-groupjoinrequestresponse-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "requester": "...", "created": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/join-requests" ``` ### POST `/v1/groups/{groupId}/join-requests` Batch accepts group join requests **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The group id. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: The group is invalid or does not exist. 3: The user is invalid or does not exist. 20: The group join request is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 6: You are already in the maximum number of groups. 19: You have insufficient permissions for this request. - `500`: 0: Something went wrong. - `503`: 18: The operation is temporarily unavailable. Please try again later. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/join-requests" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### DELETE `/v1/groups/{groupId}/join-requests` Batch declines group join requests **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The group id. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: The group is invalid or does not exist. 3: The user is invalid or does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/join-requests" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### GET `/v1/groups/{groupId}/join-requests/users/{userId}` Gets a group join request by userId. **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The group Id. | | `userId` | path | `integer` | Yes | The user Id. | **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupJoinRequestResponse` - `400`: 1: The group is invalid or does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 19: You have insufficient permissions for this request. **Response fields** (`Roblox.Groups.Api.GroupJoinRequestResponse`) See [Roblox.Groups.Api.GroupJoinRequestResponse](#roblox-groups-api-groupjoinrequestresponse) in Models. **Response example:** ```json { "requester": { "buildersClubMembershipType": 0, "hasVerifiedBadge": false, "userId": 0, "username": "string", "displayName": "string" }, "created": "2024-01-01T00:00:00Z" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/join-requests/users/{USERID}" ``` ### POST `/v1/groups/{groupId}/join-requests/users/{userId}` Accepts a group join request. **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The group Id. | | `userId` | path | `integer` | Yes | The user Id. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: The group is invalid or does not exist. 3: The user is invalid or does not exist. 20: The group join request is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 6: You are already in the maximum number of groups. 19: You have insufficient permissions for this request. - `503`: 18: The operation is temporarily unavailable. Please try again later. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/join-requests/users/{USERID}" ``` ### DELETE `/v1/groups/{groupId}/join-requests/users/{userId}` Declines/cancels a group join request. **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The group Id. | | `userId` | path | `integer` | Yes | The user Id. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: The group is invalid or does not exist. 3: The user is invalid or does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 4: You do not have permission to manage this member. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/join-requests/users/{USERID}" ``` ### GET `/v1/groups/{groupId}/membership` Gets group membership information in the context of the authenticated user **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The group Id. | | `includeNotificationPreferences` | query | `boolean` | Yes | | **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupMembershipMetadataResponse` - `400`: 1: The group is invalid or does not exist. **Response fields** (`Roblox.Groups.Api.GroupMembershipMetadataResponse`) See [Roblox.Groups.Api.GroupMembershipMetadataResponse](#roblox-groups-api-groupmembershipmetadataresponse) in Models. **Response example:** ```json { "groupId": 0, "isPrimary": false, "isPendingJoin": false, "userRole": { "user": { "buildersClubMembershipType": "...", "hasVerifiedBadge": "...", "userId": "...", "username": "...", "displayName": "..." }, "role": { "id": "...", "name": "...", "description": "...", "rank": "...", "memberCount": "...", "isBase": "..." } }, "permissions": { "groupPostsPermissions": { "viewWall": "...", "postToWall": "...", "deleteFromWall": "...", "viewStatus": "...", "postToStatus": "..." }, "groupForumsPermissions": { "viewForums": "...", "manageCategories": "...", "createPosts": "...", "removePosts": "...", "lockPosts": "...", "pinPosts": "..." }, "groupContentModerationPermissions": { "manageKeywordBlockList": "...", "viewKeywordBlockList": "..." }, "groupMembershipPermissions": { "changeRank": "...", "inviteMembers": "...", "removeMembers": "...", "banMembers": "..." }, "groupManagementPermissions": { "manageRelationships": "...", "manageClan": "...", "viewAuditLogs": "...", "bypassSlowmode": "...", "viewCommunityAnalytics": "..." }, "groupEconomyPermissions": { "spendGroupFunds": "...", "advertiseGroup": "...", "createItems": "...", "manageItems": "...", "addGroupPlaces": "...", "manageGroupGames": "..." } }, "channelPermissions": [ { "channelId": "...", "groupForumsPermissions": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/membership?includeNotificationPreferences={VALUE}" ``` ### PATCH `/v1/groups/{groupId}/name` Updates the group's name. This endpoint will charge Robux for the group rename. **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The id of the group the user is in. | **Request Body:** `application/json` — Type: `Roblox.Groups.Api.UpdateGroupNameRequest` See [Roblox.Groups.Api.UpdateGroupNameRequest](#roblox-groups-api-updategroupnamerequest) in Models. **Request example:** ```json { "name": "string" } ``` **Responses:** - `200`: OK → `Roblox.Groups.Api.UpdateGroupNameResponse` - `400`: 1: Group is invalid or does not exist. 13: The name is invalid. 19: The name is too long. 20: The name has been taken. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 12: Insufficient Robux funds. 14: The name is moderated. 23: Insufficient permissions to complete the request. 38: Your account must be verified in order to change your group's name. 39: The group ownership was changed too recently. - `409`: 36: The name was changed too recently. 37: The name was in use too recently. - `413`: 0: Unknown error. - `429`: 17: Too many requests. **Response fields** (`Roblox.Groups.Api.UpdateGroupNameResponse`) See [Roblox.Groups.Api.UpdateGroupNameResponse](#roblox-groups-api-updategroupnameresponse) in Models. **Response example:** ```json { "newName": "string" } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/name" \ -H "Content-Type: application/json" \ -d '{ "name": "string" }' ``` ### GET `/v1/groups/{groupId}/name-history` Gets the Group's name change history. **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The id of the group. | | `limit` | query | `10 \| 25 \| 50 \| 100` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `Asc \| Desc` | No | The order the results are sorted in. Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Groups.Api.Models.Response.GroupNameHistoryResponseItem_` - `400`: 1: Group is invalid or does not exist. - `403`: 23: Insufficient permissions to complete the request. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Groups.Api.Models.Response.GroupNameHistoryResponseItem_`) See [Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Groups.Api.Models.Response.GroupNameHistoryResponseItem_](#roblox-web-webapi-models-apipageresponse-roblox-groups-api-models-response-groupnamehistoryresponseitem-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "name": "...", "created": "..." } ] } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/name-history" ``` ### PATCH `/v1/groups/{groupId}/notification-preference` Updates the group's settings **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The id of the group the user is in. | **Request Body:** `application/json` — Type: `Roblox.Groups.Api.UpdateGroupNotificationPreferenceRequest` See [Roblox.Groups.Api.UpdateGroupNotificationPreferenceRequest](#roblox-groups-api-updategroupnotificationpreferencerequest) in Models. **Request example:** ```json { "notificationsEnabled": false, "type": 0 } ``` **Responses:** - `200`: OK → `0 \| 1 \| 2 \| 3` - `400`: 1: Group is invalid or does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/notification-preference" \ -H "Content-Type: application/json" \ -d '{ "notificationsEnabled": false, "type": 0 }' ``` ### GET `/v1/groups/{groupId}/payout-restriction` Gets a value indicating whether the group can use payout feature **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The group id. | **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupPayoutRestrictionResponse` - `400`: 1: Group is invalid or does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 9: You don't have permission to view this group's payouts. **Response fields** (`Roblox.Groups.Api.GroupPayoutRestrictionResponse`) See [Roblox.Groups.Api.GroupPayoutRestrictionResponse](#roblox-groups-api-grouppayoutrestrictionresponse) in Models. **Response example:** ```json { "canUseRecurringPayout": false, "canUseOneTimePayout": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/payout-restriction" ``` ### GET `/v1/groups/{groupId}/payouts` Gets a list of the group payout percentages **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The group id. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Groups.Api.GroupPayoutResponse_` - `400`: 1: Group is invalid or does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 9: You don't have permission to view this group's payouts. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Groups.Api.GroupPayoutResponse_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Groups.Api.GroupPayoutResponse_](#roblox-web-webapi-models-apiarrayresponse-roblox-groups-api-grouppayoutresponse-) in Models. **Response example:** ```json { "data": [ { "user": "...", "percentage": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/payouts" ``` ### POST `/v1/groups/{groupId}/payouts` Pays out a user in Robux. **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The group Id. | **Responses:** - `200`: OK → `Roblox.Groups.Api.OneTimePayoutResponse` - `400`: 1: Group is invalid or does not exist. 12: Insufficient Robux funds. 24: Invalid payout type. 25: The amount is invalid. 26: Too many recipients. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 23: Insufficient permissions to complete the request. 28: Group has paid out too recently. Please wait and try again. 35: 2-Step Verification is required to make further transactions. Go to Settings > Security to complete 2-Step Verification. 52: Group has paid out to this recipient too many times recently. Please try again later. - `503`: 22: The feature is disabled. **Response fields** (`Roblox.Groups.Api.OneTimePayoutResponse`) See [Roblox.Groups.Api.OneTimePayoutResponse](#roblox-groups-api-onetimepayoutresponse) in Models. **Response example:** ```json { "status": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/payouts" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### POST `/v1/groups/{groupId}/payouts/recurring` Updates recurring payouts. This endpoint will remove any recipients not sent in the request. If a recipient in the request is not a valid member in the group they will not be added to the recurring payouts. **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The group Id. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: Group is invalid or does not exist. 24: Invalid payout type. 25: The amount is invalid. 26: Too many recipients. 27: The recipients are invalid. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 12: Insufficient Robux funds. 28: Group has paid out too recently. Please wait and try again. 35: 2-Step Verification is required to make further transactions. Go to Settings > Security to complete 2-Step Verification. - `503`: 22: The feature is disabled. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/payouts/recurring" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### GET `/v1/groups/{groupId}/relationships/{groupRelationshipType}` Gets a group's relationships **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The group Id. | | `groupRelationshipType` | path | `string` | Yes | The group relationship type, enemies or allies. | | `StartRowIndex` | query | `integer` | Yes | The start index of the page request | | `MaxRows` | query | `integer` | Yes | The maximum number of rows for the page request, should be at least 1. | **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupRelationshipsResponse` - `400`: 1: Group is invalid or does not exist. 4: Group relationship type or request type is invalid. 8: Invalid or missing pagination parameters **Response fields** (`Roblox.Groups.Api.GroupRelationshipsResponse`) See [Roblox.Groups.Api.GroupRelationshipsResponse](#roblox-groups-api-grouprelationshipsresponse) in Models. **Response example:** ```json { "groupId": 0, "relationshipType": 1, "totalGroupCount": 0, "relatedGroups": [ { "id": "...", "name": "...", "description": "...", "owner": "...", "shout": "...", "memberCount": "..." } ], "nextRowIndex": 0 } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/relationships/{GROUPRELATIONSHIPTYPE}?StartRowIndex={VALUE}&MaxRows={VALUE}" ``` ### GET `/v1/groups/{groupId}/relationships/{groupRelationshipType}/requests` Gets a group's relationship requests **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The group Id. | | `groupRelationshipType` | path | `string` | Yes | The group relationship type of the request, only allies are supported. | | `StartRowIndex` | query | `integer` | Yes | The start index of the page request | | `MaxRows` | query | `integer` | Yes | The maximum number of rows for the page request, should be at least 1. | **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupRelationshipsResponse` - `400`: 1: Group is invalid or does not exist. 4: Group relationship type or request type is invalid. 8: Invalid or missing pagination parameters - `401`: 0: Authorization has been denied for this request. - `403`: 5: You don't have permission to manage this group's relationships. **Response fields** (`Roblox.Groups.Api.GroupRelationshipsResponse`) See [Roblox.Groups.Api.GroupRelationshipsResponse](#roblox-groups-api-grouprelationshipsresponse) in Models. **Response example:** ```json { "groupId": 0, "relationshipType": 1, "totalGroupCount": 0, "relatedGroups": [ { "id": "...", "name": "...", "description": "...", "owner": "...", "shout": "...", "memberCount": "..." } ], "nextRowIndex": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/relationships/{GROUPRELATIONSHIPTYPE}/requests?StartRowIndex={VALUE}&MaxRows={VALUE}" ``` ### POST `/v1/groups/{groupId}/relationships/{groupRelationshipType}/requests` Batch accepts group affiliate requests **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The group id. | | `groupRelationshipType` | path | `string` | Yes | The type of group relationship being made | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/relationships/{GROUPRELATIONSHIPTYPE}/requests" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### DELETE `/v1/groups/{groupId}/relationships/{groupRelationshipType}/requests` Batch declines group affiliate requests **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The group id. | | `groupRelationshipType` | path | `string` | Yes | The type of group relationship being made | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/relationships/{GROUPRELATIONSHIPTYPE}/requests" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### POST `/v1/groups/{groupId}/relationships/{groupRelationshipType}/requests/{relatedGroupId}` Accepts a group relationship request. **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The group id. | | `groupRelationshipType` | path | `string` | Yes | The group relationship type, enemies or allies, only allies are supported. | | `relatedGroupId` | path | `integer` | Yes | The id of the group you want to accept the relationship request with. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: Group relationship type or request type is invalid. 2: Invalid group. 3: Target group is invalid or does not exist. 10: Relationship request does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 9: Insufficient permissions. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/relationships/{GROUPRELATIONSHIPTYPE}/requests/{RELATEDGROUPID}" ``` ### DELETE `/v1/groups/{groupId}/relationships/{groupRelationshipType}/requests/{relatedGroupId}` Declines a group relationship request. **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The group id. | | `groupRelationshipType` | path | `string` | Yes | The group relationship type, enemies or allies. | | `relatedGroupId` | path | `integer` | Yes | The id of the group you want to accept the relationship request with. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: Group relationship type or request type is invalid. 2: Invalid group. 3: Target group is invalid or does not exist. 10: Relationship request does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 9: Insufficient permissions. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/relationships/{GROUPRELATIONSHIPTYPE}/requests/{RELATEDGROUPID}" ``` ### POST `/v1/groups/{groupId}/relationships/{groupRelationshipType}/{relatedGroupId}` Create a group relationship. **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The group id. | | `groupRelationshipType` | path | `string` | Yes | The group relationship type, enemies or allies. | | `relatedGroupId` | path | `integer` | Yes | The id of the group you want to create a relationship with. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: Group relationship type or request type is invalid. 2: Invalid group. 3: Target group is invalid or does not exist. 4: Your group cannot establish a relationship with itself. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 5: Your group does not allow enemy declarations. 6: Other group does not allow enemy declarations. 7: Your group already has a relationship with the target group. 8: You are blocked from communicating with this user. 9: Insufficient permissions. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/relationships/{GROUPRELATIONSHIPTYPE}/{RELATEDGROUPID}" ``` ### DELETE `/v1/groups/{groupId}/relationships/{groupRelationshipType}/{relatedGroupId}` Deletes a group relationship. **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The group id. | | `groupRelationshipType` | path | `string` | Yes | The group relationship type, enemies or allies. | | `relatedGroupId` | path | `integer` | Yes | The id of the group you want to delete the relationship with. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 2: Invalid group. 3: Target group is invalid or does not exist. 11: Relationship does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 8: You are blocked from communicating with this user. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/relationships/{GROUPRELATIONSHIPTYPE}/{RELATEDGROUPID}" ``` ### GET `/v1/groups/{groupId}/revenue/summary/{timeFrame}` **Server:** `https://economy.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | | | `timeFrame` | path | `any` | Yes | | **Responses:** - `200`: Success → `RevenueSummaryResponse` **Response fields** (`RevenueSummaryResponse`) See [RevenueSummaryResponse](#revenuesummaryresponse) in Models. **Response example:** ```json { "recurringRobuxStipend": 0, "itemSaleRobux": 0, "purchasedRobux": 0, "tradeSystemRobux": 0, "pendingRobux": 0, "groupPayoutRobux": 0 } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://economy.roblox.com/v1/groups/{GROUPID}/revenue/summary/{TIMEFRAME}" ``` ### GET `/v1/groups/{groupId}/roles` Gets a list of the rolesets in a group. **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The group id. | **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupAllRolesResponse` - `400`: 1: The group is invalid or does not exist. **Response fields** (`Roblox.Groups.Api.GroupAllRolesResponse`) See [Roblox.Groups.Api.GroupAllRolesResponse](#roblox-groups-api-groupallrolesresponse) in Models. **Response example:** ```json { "groupId": 0, "roles": [ { "id": "...", "name": "...", "description": "...", "rank": "...", "memberCount": "...", "isBase": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/roles" ``` ### GET `/v1/groups/{groupId}/roles/guest/permissions` Gets the permissions for a group's guest roleset. These can be viewed by all (members and guests) users. **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The group id. | **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupPermissionsResponse` - `400`: 1: Group is invalid or does not exist. **Response fields** (`Roblox.Groups.Api.GroupPermissionsResponse`) See [Roblox.Groups.Api.GroupPermissionsResponse](#roblox-groups-api-grouppermissionsresponse) in Models. **Response example:** ```json { "groupId": 0, "role": { "id": 0, "name": "string", "description": "string", "rank": 0, "memberCount": 0, "isBase": false }, "permissions": { "groupPostsPermissions": { "viewWall": "...", "postToWall": "...", "deleteFromWall": "...", "viewStatus": "...", "postToStatus": "..." }, "groupForumsPermissions": { "viewForums": "...", "manageCategories": "...", "createPosts": "...", "removePosts": "...", "lockPosts": "...", "pinPosts": "..." }, "groupContentModerationPermissions": { "manageKeywordBlockList": "...", "viewKeywordBlockList": "..." }, "groupMembershipPermissions": { "changeRank": "...", "inviteMembers": "...", "removeMembers": "...", "banMembers": "..." }, "groupManagementPermissions": { "manageRelationships": "...", "manageClan": "...", "viewAuditLogs": "...", "bypassSlowmode": "...", "viewCommunityAnalytics": "..." }, "groupEconomyPermissions": { "spendGroupFunds": "...", "advertiseGroup": "...", "createItems": "...", "manageItems": "...", "addGroupPlaces": "...", "manageGroupGames": "..." } } } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/roles/guest/permissions" ``` ### GET `/v1/groups/{groupId}/roles/permissions` Gets all permissions for each role **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The group id. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Groups.Api.GroupPermissionsResponse_` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Groups.Api.GroupPermissionsResponse_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Groups.Api.GroupPermissionsResponse_](#roblox-web-webapi-models-apiarrayresponse-roblox-groups-api-grouppermissionsresponse-) in Models. **Response example:** ```json { "data": [ { "groupId": "...", "role": "...", "permissions": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/roles/permissions" ``` ### GET `/v1/groups/{groupId}/roles/{roleSetId}/permissions` Gets the permissions for a group's roleset. The authorized user must either be the group owner or the roleset being requested, except for guest roles, which can be viewed by all (members and guests). **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The group id. | | `roleSetId` | path | `integer` | Yes | The group's role set id. | **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupPermissionsResponse` - `400`: 1: Group is invalid or does not exist. 2: The roleset is invalid or does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 3: You are not authorized to view/edit permissions for this role. **Response fields** (`Roblox.Groups.Api.GroupPermissionsResponse`) See [Roblox.Groups.Api.GroupPermissionsResponse](#roblox-groups-api-grouppermissionsresponse) in Models. **Response example:** ```json { "groupId": 0, "role": { "id": 0, "name": "string", "description": "string", "rank": 0, "memberCount": 0, "isBase": false }, "permissions": { "groupPostsPermissions": { "viewWall": "...", "postToWall": "...", "deleteFromWall": "...", "viewStatus": "...", "postToStatus": "..." }, "groupForumsPermissions": { "viewForums": "...", "manageCategories": "...", "createPosts": "...", "removePosts": "...", "lockPosts": "...", "pinPosts": "..." }, "groupContentModerationPermissions": { "manageKeywordBlockList": "...", "viewKeywordBlockList": "..." }, "groupMembershipPermissions": { "changeRank": "...", "inviteMembers": "...", "removeMembers": "...", "banMembers": "..." }, "groupManagementPermissions": { "manageRelationships": "...", "manageClan": "...", "viewAuditLogs": "...", "bypassSlowmode": "...", "viewCommunityAnalytics": "..." }, "groupEconomyPermissions": { "spendGroupFunds": "...", "advertiseGroup": "...", "createItems": "...", "manageItems": "...", "addGroupPlaces": "...", "manageGroupGames": "..." } } } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/roles/{ROLESETID}/permissions" ``` ### PATCH `/v1/groups/{groupId}/roles/{roleSetId}/permissions` Updates the permissions for a group's roleset. The authorized user must be the group owner. **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The group's id. | | `roleSetId` | path | `integer` | Yes | The roleset's id. | **Request Body:** `application/json` — Type: `Roblox.Groups.Api.UpdatePermissionsRequest` See [Roblox.Groups.Api.UpdatePermissionsRequest](#roblox-groups-api-updatepermissionsrequest) in Models. **Request example:** ```json { "permissions": { "DeleteFromWall": false, "PostToWall": false, "InviteMembers": false, "PostToStatus": false, "RemoveMembers": false, "BanMembers": false } } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: Group is invalid or does not exist. 2: The roleset is invalid or does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 3: You are not authorized to view/edit permissions for this role. 4: This role's permissions can not be modified. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/roles/{ROLESETID}/permissions" \ -H "Content-Type: application/json" \ -d '{ "permissions": { "DeleteFromWall": false, "PostToWall": false, "InviteMembers": false, "PostToStatus": false, "RemoveMembers": false, "BanMembers": false } }' ``` ### GET `/v1/groups/{groupId}/roles/{roleSetId}/users` Gets a list of users in a group for a specific roleset. **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The group id. | | `roleSetId` | path | `integer` | Yes | The group's role set id. | | `limit` | query | `10 \| 25 \| 50 \| 100` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `Asc \| Desc` | No | The order the results are sorted in. Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Groups.Api.Models.Response.UserModel_` - `400`: 1: The group is invalid or does not exist. 36: The pagination cursor is invalid. - `403`: 2: The roleset is invalid or does not exist. 35: You do not have permission to view this group's member list. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Groups.Api.Models.Response.UserModel_`) See [Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Groups.Api.Models.Response.UserModel_](#roblox-web-webapi-models-apipageresponse-roblox-groups-api-models-response-usermodel-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "buildersClubMembershipType": "...", "hasVerifiedBadge": "...", "userId": "...", "username": "...", "displayName": "..." } ] } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/roles/{ROLESETID}/users" ``` ### POST `/v1/groups/{groupId}/rolesets/create` Creates new group roleset. **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The group Id. | **Request Body:** `application/json` — Type: `Roblox.Groups.Api.Models.Request.CreateRoleSetRequest` See [Roblox.Groups.Api.Models.Request.CreateRoleSetRequest](#roblox-groups-api-models-request-createrolesetrequest) in Models. **Request example:** ```json { "name": "string", "description": "string", "rank": 0, "usingGroupFunds": false } ``` **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupRoleResponse` - `400`: 0: Something went wrong. 3: You do not have enough funds to purchase this role. 5: Role name already exists. 6: Rank value is out of bounds. 7: The role name is too long. 8: The role description is too long. 10: This group does not exist. 11: Failed to process payment to purchase role. 12: Limit for roles have been reached on this group. 14: Role name can not be empty. 15: This role does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 9: You do not have permissions to perform this action. **Response fields** (`Roblox.Groups.Api.GroupRoleResponse`) See [Roblox.Groups.Api.GroupRoleResponse](#roblox-groups-api-grouproleresponse) in Models. **Response example:** ```json { "id": 0, "name": "string", "description": "string", "rank": 0, "memberCount": 0, "isBase": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/rolesets/create" \ -H "Content-Type: application/json" \ -d '{ "name": "string", "description": "string", "rank": 0, "usingGroupFunds": false }' ``` ### PATCH `/v1/groups/{groupId}/rolesets/{rolesetId}` Updates existing group roleset. **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The group Id. | | `rolesetId` | path | `integer` | Yes | The roleset Id. | **Request Body:** `application/json` — Type: `Roblox.Groups.Api.Models.Request.UpdateRoleSetRequest` See [Roblox.Groups.Api.Models.Request.UpdateRoleSetRequest](#roblox-groups-api-models-request-updaterolesetrequest) in Models. **Request example:** ```json { "name": "string", "description": "string", "rank": 0, "color": 0 } ``` **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupRoleResponse` - `400`: 5: Role name already exists. 6: Rank value is out of bounds. 7: The role name is too long. 8: The role description is too long. 10: This group does not exist. 14: Role name can not be empty. 15: This role does not exist. 19: Cannot update Guest role. 20: Cannot update Owner role rank. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 9: You do not have permissions to perform this action. **Response fields** (`Roblox.Groups.Api.GroupRoleResponse`) See [Roblox.Groups.Api.GroupRoleResponse](#roblox-groups-api-grouproleresponse) in Models. **Response example:** ```json { "id": 0, "name": "string", "description": "string", "rank": 0, "memberCount": 0, "isBase": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/rolesets/{ROLESETID}" \ -H "Content-Type: application/json" \ -d '{ "name": "string", "description": "string", "rank": 0, "color": 0 }' ``` ### DELETE `/v1/groups/{groupId}/rolesets/{rolesetId}` Deletes existing group roleset. **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The group Id. | | `rolesetId` | path | `integer` | Yes | The roleset Id. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 10: This group does not exist. 15: This role does not exist. 17: Cannot remove any more roles 18: Cannot delete this role. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 9: You do not have permissions to perform this action. 16: There are users in this role. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/rolesets/{ROLESETID}" ``` ### GET `/v1/groups/{groupId}/settings` Gets the Group's settings **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The id of the group the user is in. | **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupSettingsResponse` - `400`: 1: Group is invalid or does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 23: Insufficient permissions to complete the request. **Response fields** (`Roblox.Groups.Api.GroupSettingsResponse`) See [Roblox.Groups.Api.GroupSettingsResponse](#roblox-groups-api-groupsettingsresponse) in Models. **Response example:** ```json { "isApprovalRequired": false, "isBuildersClubRequired": false, "areEnemiesAllowed": false, "areGroupFundsVisible": false, "areGroupGamesVisible": false, "isGroupNameChangeEnabled": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/settings" ``` ### PATCH `/v1/groups/{groupId}/settings` Updates the group's settings **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The id of the group the user is in. | **Request Body:** `application/json` — Type: `Roblox.Groups.Api.UpdateGroupSettingsRequest` See [Roblox.Groups.Api.UpdateGroupSettingsRequest](#roblox-groups-api-updategroupsettingsrequest) in Models. **Request example:** ```json { "isApprovalRequired": false, "areEnemiesAllowed": false, "areGroupFundsVisible": false, "areGroupGamesVisible": false, "verificationLevel": 0, "accountTenureRequirement": 0 } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: Group is invalid or does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 23: Insufficient permissions to complete the request. - `503`: 31: Service is currently unavailable. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/settings" \ -H "Content-Type: application/json" \ -d '{ "isApprovalRequired": false, "areEnemiesAllowed": false, "areGroupFundsVisible": false, "areGroupGamesVisible": false, "verificationLevel": 0, "accountTenureRequirement": 0 }' ``` ### GET `/v1/groups/{groupId}/social-links` Get social link data associated with a group **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The Id of the game | **Responses:** - `200`: OK → `Roblox.Groups.Api.GetSocialLinkResponse` - `400`: 1: Group is invalid or does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 13: Only users who are over thirteen years of age may edit social links. - `404`: 11: Social links cannot be processed as this time. **Response fields** (`Roblox.Groups.Api.GetSocialLinkResponse`) See [Roblox.Groups.Api.GetSocialLinkResponse](#roblox-groups-api-getsociallinkresponse) in Models. **Response example:** ```json { "data": [ { "id": "...", "type": "...", "url": "...", "title": "..." } ], "socialLinksVerificationStatus": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/social-links" ``` ### POST `/v1/groups/{groupId}/social-links` Posts a social links **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The id of the group | **Request Body:** `application/json` — Type: `Roblox.Groups.Api.SocialLinkRequest` See [Roblox.Groups.Api.SocialLinkRequest](#roblox-groups-api-sociallinkrequest) in Models. **Request example:** ```json { "type": 0, "url": "string", "title": "string" } ``` **Responses:** - `200`: OK → `Roblox.Groups.Api.SocialLinkResponse` - `400`: 3: The social link title is too long. 4: The social link title cannot be empty. 5: The social link url cannot be empty. 7: The request was null. 9: The social link type is invalid. 12: The social link title was moderated. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: You do not have permission to configure this social link. - `404`: 8: The requested group or social link was not found. - `503`: 11: Social links cannot be processed as this time. **Response fields** (`Roblox.Groups.Api.SocialLinkResponse`) See [Roblox.Groups.Api.SocialLinkResponse](#roblox-groups-api-sociallinkresponse) in Models. **Response example:** ```json { "id": 0, "type": 0, "url": "string", "title": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/social-links" \ -H "Content-Type: application/json" \ -d '{ "type": 0, "url": "string", "title": "string" }' ``` ### PATCH `/v1/groups/{groupId}/social-links/{socialLinkId}` Updates a social link **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The id of the universe | | `socialLinkId` | path | `integer` | Yes | The id of the social link being updated | **Request Body:** `application/json` — Type: `Roblox.Groups.Api.SocialLinkRequest` See [Roblox.Groups.Api.SocialLinkRequest](#roblox-groups-api-sociallinkrequest) in Models. **Request example:** ```json { "type": 0, "url": "string", "title": "string" } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: Group is invalid or does not exist. 3: The social link title is too long. 4: The social link title cannot be empty. 5: The social link url cannot be empty. 6: The social link url was improperly formatted. 7: The request was null. 8: The requested group or social link was not found. 9: The social link type is invalid. 10: The social link is not for a group. 12: The social link title was moderated. 16: A social link with this type already exists on this group. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: You do not have permission to configure this social link. - `404`: 11: Social links cannot be processed as this time. - `503`: 11: Social links cannot be processed as this time. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/social-links/{SOCIALLINKID}" \ -H "Content-Type: application/json" \ -d '{ "type": 0, "url": "string", "title": "string" }' ``` ### DELETE `/v1/groups/{groupId}/social-links/{socialLinkId}` Deletes a social link **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The id of the game you are editing, required for permissions checking | | `socialLinkId` | path | `integer` | Yes | The id of the social link | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: Group is invalid or does not exist. 10: The social link is not for a group. 15: The social link id doesn't match the group id. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: You do not have permission to configure this social link. 13: Only users who are over thirteen years of age may edit social links. - `404`: 11: Social links cannot be processed as this time. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/social-links/{SOCIALLINKID}" ``` ### GET `/v1/groups/{groupId}/universes` Gets a list of universes for the given group. **Server:** `https://develop.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The group id. | | `isArchived` | query | `boolean` | No | Whether or not to return archived games. | | `limit` | query | `10 \| 25 \| 50 \| 100` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `Asc \| Desc` | No | Sorted by universeId Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Api.Develop.Models.UniverseModel_` - `400`: Invalid groupId. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Api.Develop.Models.UniverseModel_`) See [Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Api.Develop.Models.UniverseModel_](#roblox-web-webapi-models-apipageresponse-roblox-api-develop-models-universemodel-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "id": "...", "name": "...", "description": "...", "isArchived": "...", "rootPlaceId": "...", "isActive": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/groups/{GROUPID}/universes" ``` ### GET `/v1/groups/{groupId}/users` Gets a list of users in a group. **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The group id. | | `limit` | query | `10 \| 25 \| 50 \| 100` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `Asc \| Desc` | No | The order the results are sorted in. Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Groups.Api.UserGroupRoleResponse_` - `400`: 1: The group is invalid or does not exist. 36: The pagination cursor is invalid. - `403`: 35: You do not have permission to view this group's member list. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Groups.Api.UserGroupRoleResponse_`) See [Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Groups.Api.UserGroupRoleResponse_](#roblox-web-webapi-models-apipageresponse-roblox-groups-api-usergrouproleresponse-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "user": "...", "role": "..." } ] } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/users" ``` ### POST `/v1/groups/{groupId}/users` Joins a group **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The group Id. | | `Roblox-Place-Id` | header | `integer` | No | The place ID of the experience the player is in. | | `Roblox-Game-Id` | header | `string` | No | The player's current game Id. | | `Roblox-Session-Id` | header | `string` | No | The player's current session Id. | **Request Body:** `application/json` — Type: `Roblox.Groups.Api.JoinGroupRequest` See [Roblox.Groups.Api.JoinGroupRequest](#roblox-groups-api-joingrouprequest) in Models. **Request example:** ```json { "sessionId": "string", "redemptionToken": "string", "captchaId": "string", "captchaToken": "string", "captchaProvider": "string", "challengeId": "string" } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: The group is invalid or does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 6: You are already in the maximum number of groups. 9: You do not have the builders club membership necessary to join this group. 14: You cannot join a closed group. 33: You do not have the required verification level to join this group. 34: You do not have the required account tenure to join this group. - `409`: 7: You have already requested to join this group. 8: You are already a member of this group. - `429`: 10: Too many attempts to join the group. Please try again later. - `503`: 18: The operation is temporarily unavailable. Please try again later. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/users" \ -H "Content-Type: application/json" \ -d '{ "sessionId": "string", "redemptionToken": "string", "captchaId": "string", "captchaToken": "string", "captchaProvider": "string", "challengeId": "string" }' ``` ### PATCH `/v1/groups/{groupId}/users/{userId}` Updates a users role in a group. **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The id of the group the user is in. | | `userId` | path | `integer` | Yes | The id of the user being updated. | **Request Body:** `application/json` — Type: `Roblox.Groups.Api.UpdateUserRoleRequest` See [Roblox.Groups.Api.UpdateUserRoleRequest](#roblox-groups-api-updateuserrolerequest) in Models. **Request example:** ```json { "roleId": 0 } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: The group is invalid or does not exist. 2: The roleset is invalid or does not exist. 3: The user is invalid or does not exist. 23: You cannot change your own role. 26: You cannot change the user's role to the same role. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 4: You do not have permission to manage this member. - `503`: 18: The operation is temporarily unavailable. Please try again later. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/users/{USERID}" \ -H "Content-Type: application/json" \ -d '{ "roleId": 0 }' ``` ### DELETE `/v1/groups/{groupId}/users/{userId}` Removes a user from a group **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The group Id. | | `userId` | path | `integer` | Yes | The Id of the user being removed. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: The group is invalid or does not exist. 3: The user is invalid or does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 4: You do not have permission to manage this member. 25: 2-Step Verification is required to make further transactions. Go to Settings > Security to complete 2-Step Verification. - `503`: 18: The operation is temporarily unavailable. Please try again later. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/users/{USERID}" ``` ### GET `/v1/groups/{groupId}/users/{userId}/permissions` Gets the permissions a user has in a group. Only available to group owner and RCC **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The group id. | | `userId` | path | `integer` | Yes | The user id. | **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupPermissionsResponse` - `400`: 1: Group is invalid or does not exist. - `403`: 3: You are not authorized to view/edit permissions for this role. - `404`: 3: The user is invalid or does not exist. **Response fields** (`Roblox.Groups.Api.GroupPermissionsResponse`) See [Roblox.Groups.Api.GroupPermissionsResponse](#roblox-groups-api-grouppermissionsresponse) in Models. **Response example:** ```json { "groupId": 0, "role": { "id": 0, "name": "string", "description": "string", "rank": 0, "memberCount": 0, "isBase": false }, "permissions": { "groupPostsPermissions": { "viewWall": "...", "postToWall": "...", "deleteFromWall": "...", "viewStatus": "...", "postToStatus": "..." }, "groupForumsPermissions": { "viewForums": "...", "manageCategories": "...", "createPosts": "...", "removePosts": "...", "lockPosts": "...", "pinPosts": "..." }, "groupContentModerationPermissions": { "manageKeywordBlockList": "...", "viewKeywordBlockList": "..." }, "groupMembershipPermissions": { "changeRank": "...", "inviteMembers": "...", "removeMembers": "...", "banMembers": "..." }, "groupManagementPermissions": { "manageRelationships": "...", "manageClan": "...", "viewAuditLogs": "...", "bypassSlowmode": "...", "viewCommunityAnalytics": "..." }, "groupEconomyPermissions": { "spendGroupFunds": "...", "advertiseGroup": "...", "createItems": "...", "manageItems": "...", "addGroupPlaces": "...", "manageGroupGames": "..." } } } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/users/{USERID}/permissions" ``` ### GET `/v1/groups/{groupId}/wall/posts` Gets a list of group wall posts. **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The group id. | | `limit` | query | `10 \| 25 \| 50 \| 100` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `Asc \| Desc` | No | Sorted by group wall post Id Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Groups.Api.Models.Response.GroupWallPostModel_` - `400`: 1: The group is invalid or does not exist. - `403`: 2: You do not have permission to access this group wall. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Groups.Api.Models.Response.GroupWallPostModel_`) See [Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Groups.Api.Models.Response.GroupWallPostModel_](#roblox-web-webapi-models-apipageresponse-roblox-groups-api-models-response-groupwallpostmodel-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "id": "...", "poster": "...", "body": "...", "created": "...", "updated": "..." } ] } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/wall/posts" ``` ### GET `/v1/installer-cdns` Get information about which CDNs to use for installation. **Server:** `https://clientsettings.roblox.com` **Auth:** **Responses:** - `200`: OK **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://clientsettings.roblox.com/v1/installer-cdns" ``` ### GET `/v1/locales` Get list of Supported locales with user locus information. **Server:** `https://locale.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `displayValueLocale` | query | `string` | No | | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Locale.Api.SupportedLocaleLocus_` - `403`: Feature is turned off temporary - `500`: Internal server error **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Locale.Api.SupportedLocaleLocus_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Locale.Api.SupportedLocaleLocus_](#roblox-web-webapi-models-apiarrayresponse-roblox-locale-api-supportedlocalelocus-) in Models. **Response example:** ```json { "data": [ { "locale": "...", "isEnabledForFullExperience": "...", "isEnabledForSignupAndLogin": "...", "isEnabledForInGameUgc": "..." } ] } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://locale.roblox.com/v1/locales" ``` ### POST `/v1/locales/set-show-roblox-translations` Sets whether translations suggested by Roblox will be shown to the user. **Server:** `https://locale.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Locale.Api.SetShowRobloxTranslationsRequest` See [Roblox.Locale.Api.SetShowRobloxTranslationsRequest](#roblox-locale-api-setshowrobloxtranslationsrequest) in Models. **Request example:** ```json { "showRobloxTranslations": false } ``` **Responses:** - `200`: OK → `Roblox.Locale.Api.SuccessResponse` - `400`: Bad Request - `401`: Unauthorized 0: Authorization has been denied for this request. - `403`: Feature is turned off temporary 0: Token Validation Failed - `500`: Internal server error **Response fields** (`Roblox.Locale.Api.SuccessResponse`) See [Roblox.Locale.Api.SuccessResponse](#roblox-locale-api-successresponse) in Models. **Response example:** ```json { "success": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://locale.roblox.com/v1/locales/set-show-roblox-translations" \ -H "Content-Type: application/json" \ -d '{ "showRobloxTranslations": false }' ``` ### POST `/v1/locales/set-user-supported-locale` Sets user's supported locale. Null supported locale will clear out user's supported locale (set users' supported locale to null) **Server:** `https://locale.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Locale.Api.SetSupportedLocaleForUserRequest` See [Roblox.Locale.Api.SetSupportedLocaleForUserRequest](#roblox-locale-api-setsupportedlocaleforuserrequest) in Models. **Request example:** ```json { "supportedLocaleCode": "string" } ``` **Responses:** - `200`: OK → `Roblox.Locale.Api.SuccessResponse` - `400`: Bad Request - `401`: Unauthorized 0: Authorization has been denied for this request. - `403`: Feature is turned off temporary 0: Token Validation Failed - `500`: Internal server error **Response fields** (`Roblox.Locale.Api.SuccessResponse`) See [Roblox.Locale.Api.SuccessResponse](#roblox-locale-api-successresponse) in Models. **Response example:** ```json { "success": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://locale.roblox.com/v1/locales/set-user-supported-locale" \ -H "Content-Type: application/json" \ -d '{ "supportedLocaleCode": "string" }' ``` ### GET `/v1/locales/supported-locales` Get list of supported locales sorted by the Native Name property. **Server:** `https://locale.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Locale.Api.SupportedLocalesResponse` - `403`: Feature is turned off temporary - `500`: Internal server error **Response fields** (`Roblox.Locale.Api.SupportedLocalesResponse`) See [Roblox.Locale.Api.SupportedLocalesResponse](#roblox-locale-api-supportedlocalesresponse) in Models. **Response example:** ```json { "supportedLocales": [ { "id": "...", "locale": "...", "name": "...", "nativeName": "...", "language": "..." } ] } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://locale.roblox.com/v1/locales/supported-locales" ``` ### GET `/v1/locales/supported-locales-for-creators` **Server:** `https://locale.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `displayValueLocale` | query | `string` | No | | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Locale.Api.SupportedLocaleLocus_` **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Locale.Api.SupportedLocaleLocus_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Locale.Api.SupportedLocaleLocus_](#roblox-web-webapi-models-apiarrayresponse-roblox-locale-api-supportedlocalelocus-) in Models. **Response example:** ```json { "data": [ { "locale": "...", "isEnabledForFullExperience": "...", "isEnabledForSignupAndLogin": "...", "isEnabledForInGameUgc": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://locale.roblox.com/v1/locales/supported-locales-for-creators" ``` ### GET `/v1/locales/user-locale` Gets user locale. If user is absent returns, locale from http request object. **Server:** `https://locale.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Locale.Api.UserLocaleResponse` - `500`: Internal server error **Response fields** (`Roblox.Locale.Api.UserLocaleResponse`) See [Roblox.Locale.Api.UserLocaleResponse](#roblox-locale-api-userlocaleresponse) in Models. **Response example:** ```json { "supportedLocale": { "id": 0, "locale": "string", "name": "string", "nativeName": "string", "language": { "id": "...", "name": "...", "nativeName": "...", "languageCode": "...", "isRightToLeft": "..." } }, "nativeLanguage": { "id": 0, "name": "string", "nativeName": "string", "languageCode": "string", "isRightToLeft": false } } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://locale.roblox.com/v1/locales/user-locale" ``` ### GET `/v1/locales/user-localization-locus-supported-locales` Gets each of a user's localization locus supported locales. A localization locus supported locale is a page (or group of pages) that have been defined by the International team which need independent locale support. If the user is null we will attempt to return the locales appropriate for the user's device language. **Server:** `https://locale.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Locale.Api.UserLocalizationLocusLocalesResponse` - `500`: Internal server error **Response fields** (`Roblox.Locale.Api.UserLocalizationLocusLocalesResponse`) See [Roblox.Locale.Api.UserLocalizationLocusLocalesResponse](#roblox-locale-api-userlocalizationlocuslocalesresponse) in Models. **Response example:** ```json { "signupAndLogin": { "id": 0, "locale": "string", "name": "string", "nativeName": "string", "language": { "id": "...", "name": "...", "nativeName": "...", "languageCode": "...", "isRightToLeft": "..." } }, "generalExperience": { "id": 0, "locale": "string", "name": "string", "nativeName": "string", "language": { "id": "...", "name": "...", "nativeName": "...", "languageCode": "...", "isRightToLeft": "..." } }, "ugc": { "id": 0, "locale": "string", "name": "string", "nativeName": "string", "language": { "id": "...", "name": "...", "nativeName": "...", "languageCode": "...", "isRightToLeft": "..." } }, "showRobloxTranslations": false } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://locale.roblox.com/v1/locales/user-localization-locus-supported-locales" ``` ### GET `/v1/localization-table/limits` Get limits for translation table entries operations **Server:** `https://localizationtables.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.LocalizationTables.Api.GetLimitsResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.LocalizationTables.Api.GetLimitsResponse`) See [Roblox.LocalizationTables.Api.GetLimitsResponse](#roblox-localizationtables-api-getlimitsresponse) in Models. **Response example:** ```json { "entryOperationLimits": { "maxContextLength": 0, "maxKeyLength": 0, "maxSourceLength": 0, "maxExampleLength": 0, "maxGameLocationPathLength": 0 }, "tableOperationLimits": { "maxEntriesPerUpdate": 0 } } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://localizationtables.roblox.com/v1/localization-table/limits" ``` ### GET `/v1/localization-table/metadata` Get metadata for localization UI **Server:** `https://localizationtables.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.LocalizationTables.Api.LocalizationTablesMetadataResponse` **Response fields** (`Roblox.LocalizationTables.Api.LocalizationTablesMetadataResponse`) See [Roblox.LocalizationTables.Api.LocalizationTablesMetadataResponse](#roblox-localizationtables-api-localizationtablesmetadataresponse) in Models. **Response example:** ```json { "isBulkUploadFeatureEnabled": false, "isCsvDownloadEnabled": false, "isAccessToTranslationMetaDataEnabled": false, "isTranslationManagementRedirectionEnabled": false, "isUntranslatedFilterEnabled": false, "isAutomaticTranslationFilterEnabled": false } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://localizationtables.roblox.com/v1/localization-table/metadata" ``` ### POST `/v1/localization-table/tables` Creates a Localization Table with the given data. Note that this endpoint simply creates a table and does not associate it with any universe, so if intending to use this to create tables usable in experience more setup will be needed to grant those experiences access. **Server:** `https://localizationtables.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.LocalizationTables.Api.CreateTableRequest` See [Roblox.LocalizationTables.Api.CreateTableRequest](#roblox-localizationtables-api-createtablerequest) in Models. **Request example:** ```json { "name": "string", "ownerType": "User", "ownerId": 0 } ``` **Responses:** - `200`: OK → `Roblox.LocalizationTables.Api.CreateTableResponse` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: You do not have permission to get this table. **Response fields** (`Roblox.LocalizationTables.Api.CreateTableResponse`) See [Roblox.LocalizationTables.Api.CreateTableResponse](#roblox-localizationtables-api-createtableresponse) in Models. **Response example:** ```json { "id": "string", "assetId": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://localizationtables.roblox.com/v1/localization-table/tables" \ -H "Content-Type: application/json" \ -d '{ "name": "string", "ownerType": "User", "ownerId": 0 }' ``` ### GET `/v1/localization-table/tables/{assetId}` Get table information by the assetId of the table. **Server:** `https://localizationtables.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetId` | path | `integer` | Yes | The asset id associated with the table. | **Responses:** - `200`: OK → `Roblox.LocalizationTables.Api.GetTableResponse` - `400`: 12: Invalid asset id. - `401`: 0: Authorization has been denied for this request. - `403`: 2: You do not have permission to get this table. **Response fields** (`Roblox.LocalizationTables.Api.GetTableResponse`) See [Roblox.LocalizationTables.Api.GetTableResponse](#roblox-localizationtables-api-gettableresponse) in Models. **Response example:** ```json { "id": "string", "name": "string", "ownerType": "User", "ownerId": 0, "assetId": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://localizationtables.roblox.com/v1/localization-table/tables/{ASSETID}" ``` ### GET `/v1/localization-table/tables/{tableId}` Get table information by the id of the table. **Server:** `https://localizationtables.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `tableId` | path | `string` | Yes | | **Responses:** - `200`: OK → `Roblox.LocalizationTables.Api.GetTableResponse` - `400`: 3: Invalid table id. - `401`: 0: Authorization has been denied for this request. - `403`: 2: You do not have permission to get this table. **Response fields** (`Roblox.LocalizationTables.Api.GetTableResponse`) See [Roblox.LocalizationTables.Api.GetTableResponse](#roblox-localizationtables-api-gettableresponse) in Models. **Response example:** ```json { "id": "string", "name": "string", "ownerType": "User", "ownerId": 0, "assetId": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://localizationtables.roblox.com/v1/localization-table/tables/{TABLEID}" ``` ### PATCH `/v1/localization-table/tables/{tableId}` Updates the tables contents based on what is provided. **Server:** `https://localizationtables.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `tableId` | path | `string` | Yes | The table guid for the table to update. | | `gameId` | query | `integer` | No | The game id. | **Request Body:** `application/json` — Type: `Roblox.LocalizationTables.Api.UpdateTableContentsRequest` See [Roblox.LocalizationTables.Api.UpdateTableContentsRequest](#roblox-localizationtables-api-updatetablecontentsrequest) in Models. **Request example:** ```json { "name": "string", "entries": [ { "identifier": "...", "metadata": "...", "translations": "...", "delete": "..." } ] } ``` **Responses:** - `200`: OK → `Roblox.LocalizationTables.Api.UpdateTableContentsResponse` - `400`: 3: Invalid table id. 4: Table does not exist. 10: Maximum entries exceeded. Please keep the number of entries per request below the maximum. 13: Request body can't be null 14: Invalid game id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 6: You do not have permission to create this table. - `503`: 17: Feature is disabled **Response fields** (`Roblox.LocalizationTables.Api.UpdateTableContentsResponse`) See [Roblox.LocalizationTables.Api.UpdateTableContentsResponse](#roblox-localizationtables-api-updatetablecontentsresponse) in Models. **Response example:** ```json { "failedEntriesAndTranslations": [ { "error": "...", "identifier": "...", "metadata": "...", "translations": "...", "createdTime": "..." } ], "modifiedEntriesAndTranslations": [ { "identifier": "...", "translations": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://localizationtables.roblox.com/v1/localization-table/tables/{TABLEID}" \ -H "Content-Type: application/json" \ -d '{ "name": "string", "entries": [ { "identifier": "...", "metadata": "...", "translations": "...", "delete": "..." } ] }' ``` ### GET `/v1/localization-table/tables/{tableId}/entries` Gets a batch of entries for a table. **Server:** `https://localizationtables.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `tableId` | path | `string` | Yes | | | `cursor` | query | `string` | No | If null, there are no more entries in the table and you've reached the last page. | | `gameId` | query | `integer` | No | | | `entryFormat` | query | `Invalid \| Legacy \| Icu` | No | Valid values: `Invalid`, `Legacy`, `Icu` | **Responses:** - `200`: OK → `Roblox.LocalizationTables.Api.GetTableEntriesPagedResponse` - `400`: 3: Invalid table id. 14: Invalid game id - `401`: 0: Authorization has been denied for this request. - `403`: 2: You do not have permission to get this table. **Response fields** (`Roblox.LocalizationTables.Api.GetTableEntriesPagedResponse`) See [Roblox.LocalizationTables.Api.GetTableEntriesPagedResponse](#roblox-localizationtables-api-gettableentriespagedresponse) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "identifier": "...", "metadata": "...", "translations": "...", "createdTime": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://localizationtables.roblox.com/v1/localization-table/tables/{TABLEID}/entries" ``` ### POST `/v1/localization-table/tables/{tableId}/entries/translation-feedback` Gets the translation feedback for each entry passed in. **Server:** `https://localizationtables.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `tableId` | path | `string` | Yes | The entries' tableId. | | `gameId` | query | `integer` | No | The game id. | **Request Body:** `application/json` — Type: `Roblox.LocalizationTables.Api.GetTableEntriesTranslationFeedbackRequest` See [Roblox.LocalizationTables.Api.GetTableEntriesTranslationFeedbackRequest](#roblox-localizationtables-api-gettableentriestranslationfeedbackrequest) in Models. **Request example:** ```json { "sourceLocale": "string", "entries": [ { "translation": "...", "key": "...", "context": "...", "source": "...", "entryFormat": "..." } ] } ``` **Responses:** - `200`: OK → `Roblox.LocalizationTables.Api.GetTableEntriesTranslationFeedbackResponse` - `400`: 3: Invalid table id. 13: Request body can't be null 14: Invalid game id 16: Entries can't be null or empty 35: The entries provided are invalid 37: Invalid locale code. 38: Invalid entry identifier. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: You do not have permission to get this table. - `429`: 24: Too many attempts.Please try again later. - `503`: 17: Feature is disabled **Response fields** (`Roblox.LocalizationTables.Api.GetTableEntriesTranslationFeedbackResponse`) See [Roblox.LocalizationTables.Api.GetTableEntriesTranslationFeedbackResponse](#roblox-localizationtables-api-gettableentriestranslationfeedbackresponse) in Models. **Response example:** ```json { "tableId": "string", "entries": [ { "identifier": "...", "feedbackCount": "...", "playerSuggestionText": "...", "reasons": "...", "robloxSuggestionText": "..." } ] } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://localizationtables.roblox.com/v1/localization-table/tables/{TABLEID}/entries/translation-feedback" \ -H "Content-Type: application/json" \ -d '{ "sourceLocale": "string", "entries": [ { "translation": "...", "key": "...", "context": "...", "source": "...", "entryFormat": "..." } ] }' ``` ### POST `/v1/localization-table/tables/{tableId}/entries/translation-history` Gets the translation history for each entry passed in. **Server:** `https://localizationtables.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `tableId` | path | `string` | Yes | The entries' tableId. | | `gameId` | query | `integer` | No | The game id. | **Request Body:** `application/json` — Type: `Roblox.LocalizationTables.Api.GetTableEntriesTranslationHistoryRequest` See [Roblox.LocalizationTables.Api.GetTableEntriesTranslationHistoryRequest](#roblox-localizationtables-api-gettableentriestranslationhistoryrequest) in Models. **Request example:** ```json { "locale": "string", "entries": [ { "cursor": "...", "identifier": "...", "count": "...", "sortOrder": "..." } ] } ``` **Responses:** - `200`: OK → `Roblox.LocalizationTables.Api.GetTableEntriesTranslationHistoryResponse` - `400`: 3: Invalid table id. 13: Request body can't be null 14: Invalid game id 16: Entries can't be null or empty 35: The entries provided are invalid 37: Invalid locale code. 38: Invalid entry identifier. 39: Count should be at least 1. 45: Invalid exclusive start id. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: You do not have permission to get this table. - `429`: 24: Too many attempts.Please try again later. - `503`: 17: Feature is disabled **Response fields** (`Roblox.LocalizationTables.Api.GetTableEntriesTranslationHistoryResponse`) See [Roblox.LocalizationTables.Api.GetTableEntriesTranslationHistoryResponse](#roblox-localizationtables-api-gettableentriestranslationhistoryresponse) in Models. **Response example:** ```json { "tableId": "string", "locale": "string", "entries": [ { "identifier": "...", "history": "...", "nextCursor": "..." } ], "failedEntries": [ { "identifier": "...", "count": "...", "error": "..." } ] } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://localizationtables.roblox.com/v1/localization-table/tables/{TABLEID}/entries/translation-history" \ -H "Content-Type: application/json" \ -d '{ "locale": "string", "entries": [ { "cursor": "...", "identifier": "...", "count": "...", "sortOrder": "..." } ] }' ``` ### GET `/v1/localization-table/tables/{tableId}/entry-count` Gets the number of entries in the specified table **Server:** `https://localizationtables.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `tableId` | path | `string` | Yes | The table id | | `gameId` | query | `integer` | No | The game id | | `entryFormat` | query | `Invalid \| Legacy \| Icu` | No | Valid values: `Invalid`, `Legacy`, `Icu` | **Responses:** - `200`: OK → `Roblox.LocalizationTables.Api.GetTableEntryCountResponse` - `400`: 3: Invalid table id. 14: Invalid game id - `401`: 0: Authorization has been denied for this request. - `403`: 2: You do not have permission to get this table. **Response fields** (`Roblox.LocalizationTables.Api.GetTableEntryCountResponse`) See [Roblox.LocalizationTables.Api.GetTableEntryCountResponse](#roblox-localizationtables-api-gettableentrycountresponse) in Models. **Response example:** ```json { "id": "string", "entryCount": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://localizationtables.roblox.com/v1/localization-table/tables/{TABLEID}/entry-count" ``` ### PATCH `/v1/localizationtable/gametables/{gameId}` **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.AssociateLocalizationTablesToGameRequest` See [Roblox.GameInternationalization.Api.AssociateLocalizationTablesToGameRequest](#roblox-gameinternationalization-api-associatelocalizationtablestogamerequest) in Models. **Request example:** ```json { "tables": [ { "id": "...", "dissociate": "..." } ] } ``` **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.AssociateLocalizationTablesToGameResponse` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.GameInternationalization.Api.AssociateLocalizationTablesToGameResponse`) See [Roblox.GameInternationalization.Api.AssociateLocalizationTablesToGameResponse](#roblox-gameinternationalization-api-associatelocalizationtablestogameresponse) in Models. **Response example:** ```json { "success": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/localizationtable/gametables/{GAMEID}" \ -H "Content-Type: application/json" \ -d '{ "tables": [ { "id": "...", "dissociate": "..." } ] }' ``` ### GET `/v1/marAssetHash/{marAssetHash}/marCheckSum/{marCheckSum}` Retrieves an asset by its mar (moderation agnostic) hash and mar (moderation agnostic) checksum. **Server:** `https://assetdelivery.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `marAssetHash` | path | `string` | Yes | The mar (moderation agnostic) hash of the asset to retrieve. | | `marCheckSum` | path | `string` | Yes | The mar (moderation agnostic) checksum of the asset to retrieve. | | `Accept-Encoding` | header | `string` | Yes | | | `Roblox-Place-Id` | header | `integer` | Yes | | | `AssetType` | header | `string` | Yes | | | `Accept` | header | `string` | Yes | | | `AssetFormat` | header | `string` | Yes | | | `Roblox-AssetFormat` | header | `string` | Yes | | | `skipSigningScripts` | query | `boolean` | No | | | `clientInsert` | query | `integer` | No | | | `scriptinsert` | query | `integer` | No | | | `modulePlaceId` | query | `integer` | No | | | `serverplaceid` | query | `integer` | No | | | `expectedAssetType` | query | `string` | No | | **Responses:** - `200`: OK → `Roblox.Web.Assets.AssetResponseItemV1` - `400`: 2: invalid server request 3: Encoding cannot be empty - `404`: 5: Asset hash cannot be empty **Response fields** (`Roblox.Web.Assets.AssetResponseItemV1`) See [Roblox.Web.Assets.AssetResponseItemV1](#roblox-web-assets-assetresponseitemv1) in Models. **Response example:** ```json { "location": "string", "errors": [ { "Code": "...", "Message": "...", "CustomErrorCode": "..." } ], "requestId": "string", "isArchived": false, "assetTypeId": 0, "contentRepresentationSpecifier": { "format": "string", "majorVersion": "string", "fidelity": "string", "skipGenerationIfNotExist": false } } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://assetdelivery.roblox.com/v1/marAssetHash/{MARASSETHASH}/marCheckSum/{MARCHECKSUM}" ``` ### GET `/v1/messages` Gets a user's messages. **Server:** `https://privatemessages.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `pageNumber` | query | `integer` | No | | | `pageSize` | query | `integer` | No | | | `messageTab` | query | `Inbox \| Sent \| Archive` | No | Valid values: `Inbox`, `Sent`, `Archive` | **Responses:** - `200`: OK → `Roblox.PrivateMessages.Api.Models.GetMessagesResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.PrivateMessages.Api.Models.GetMessagesResponse`) See [Roblox.PrivateMessages.Api.Models.GetMessagesResponse](#roblox-privatemessages-api-models-getmessagesresponse) in Models. **Response example:** ```json { "collection": [ { "id": "...", "sender": "...", "recipient": "...", "subject": "...", "body": "...", "created": "..." } ], "totalCollectionSize": 0, "totalPages": 0, "pageNumber": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://privatemessages.roblox.com/v1/messages" ``` ### POST `/v1/messages/archive` Archives a batch of messages. **Server:** `https://privatemessages.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.PrivateMessages.Api.Models.BatchMessagesResponse` - `400`: 5: Too many ids in a batch request. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.PrivateMessages.Api.Models.BatchMessagesResponse`) See [Roblox.PrivateMessages.Api.Models.BatchMessagesResponse](#roblox-privatemessages-api-models-batchmessagesresponse) in Models. **Response example:** ```json { "failedMessages": [ { "messageId": "...", "errorMessage": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://privatemessages.roblox.com/v1/messages/archive" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### POST `/v1/messages/mark-read` Marks a batch of messages as read. **Server:** `https://privatemessages.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.PrivateMessages.Api.Models.BatchMessagesResponse` - `400`: 5: Too many ids in a batch request. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.PrivateMessages.Api.Models.BatchMessagesResponse`) See [Roblox.PrivateMessages.Api.Models.BatchMessagesResponse](#roblox-privatemessages-api-models-batchmessagesresponse) in Models. **Response example:** ```json { "failedMessages": [ { "messageId": "...", "errorMessage": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://privatemessages.roblox.com/v1/messages/mark-read" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### POST `/v1/messages/mark-unread` Marks a batch of messages as unread. **Server:** `https://privatemessages.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.PrivateMessages.Api.Models.BatchMessagesResponse` - `400`: 5: Too many ids in a batch request. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.PrivateMessages.Api.Models.BatchMessagesResponse`) See [Roblox.PrivateMessages.Api.Models.BatchMessagesResponse](#roblox-privatemessages-api-models-batchmessagesresponse) in Models. **Response example:** ```json { "failedMessages": [ { "messageId": "...", "errorMessage": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://privatemessages.roblox.com/v1/messages/mark-unread" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### POST `/v1/messages/unarchive` Unarchives a batch of messages. **Server:** `https://privatemessages.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.PrivateMessages.Api.Models.BatchMessagesResponse` - `400`: 5: Too many ids in a batch request. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.PrivateMessages.Api.Models.BatchMessagesResponse`) See [Roblox.PrivateMessages.Api.Models.BatchMessagesResponse](#roblox-privatemessages-api-models-batchmessagesresponse) in Models. **Response example:** ```json { "failedMessages": [ { "messageId": "...", "errorMessage": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://privatemessages.roblox.com/v1/messages/unarchive" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### GET `/v1/messages/unread/count` Gets unread messages for the authenticated user. **Server:** `https://privatemessages.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.PrivateMessages.Api.Models.UnreadMessagesCountResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.PrivateMessages.Api.Models.UnreadMessagesCountResponse`) See [Roblox.PrivateMessages.Api.Models.UnreadMessagesCountResponse](#roblox-privatemessages-api-models-unreadmessagescountresponse) in Models. **Response example:** ```json { "count": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://privatemessages.roblox.com/v1/messages/unread/count" ``` ### GET `/v1/messages/{messageId}` Gets a message's details. **Server:** `https://privatemessages.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `messageId` | path | `integer` | Yes | | **Responses:** - `200`: OK → `Roblox.PrivateMessages.Api.Models.MessageDetailsResponse` - `400`: 2: Message does not exist or the current user is not authorized to view it. - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.PrivateMessages.Api.Models.MessageDetailsResponse`) See [Roblox.PrivateMessages.Api.Models.MessageDetailsResponse](#roblox-privatemessages-api-models-messagedetailsresponse) in Models. **Response example:** ```json { "id": 0, "sender": { "hasVerifiedBadge": false, "id": 0, "name": "string", "displayName": "string" }, "recipient": { "hasVerifiedBadge": false, "id": 0, "name": "string", "displayName": "string" }, "subject": "string", "body": "string", "created": "2024-01-01T00:00:00Z" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://privatemessages.roblox.com/v1/messages/{MESSAGEID}" ``` ### GET `/v1/metadata` Get the metadata **Server:** `https://accountinformation.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.AccountInformation.Api.Models.MetadataResponse` **Response fields** (`Roblox.AccountInformation.Api.Models.MetadataResponse`) See [Roblox.AccountInformation.Api.Models.MetadataResponse](#roblox-accountinformation-api-models-metadataresponse) in Models. **Response example:** ```json { "isAllowedNotificationsEndpointDisabled": false, "isAccountSettingsPolicyEnabled": false, "isPhoneNumberEnabled": false, "MaxUserDescriptionLength": 0, "isUserDescriptionEnabled": false, "isUserBlockEndpointsUpdated": false } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountinformation.roblox.com/v1/metadata" ``` ### GET `/v1/metadata#FriendsApi` **Server:** `https://friends.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `targetUserId` | query | `integer` | No | | **Responses:** - `200`: OK → `Roblox.Friends.Api.Models.Response.FriendsPageMetadataResponse` **Response fields** (`Roblox.Friends.Api.Models.Response.FriendsPageMetadataResponse`) See [Roblox.Friends.Api.Models.Response.FriendsPageMetadataResponse](#roblox-friends-api-models-response-friendspagemetadataresponse) in Models. **Response example:** ```json { "isFriendsFilterBarEnabled": false, "isFriendsPageSortExperimentEnabled": false, "isFriendsUserDataStoreCacheEnabled": false, "frequentFriendSortRollout": 0, "userName": "string", "displayName": "string" } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://friends.roblox.com/v1/metadata#FriendsApi" ``` ### GET `/v1/metadata#TwoStepVerificationApi` Gets two step verification system metadata. The metadata endpoint takes in optional request parameters to output additional context for when the user is unauthenticated but attempting to login with two step verification. When supplied, all three request parameters must be sent and match up. **Server:** `https://twostepverification.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | query | `integer` | No | The user ID. | | `challengeId` | query | `string` | No | The active two step verification challenge ID if there is one. | | `actionType` | query | `integer enum (9 values)` | No | The Roblox.TwoStepVerification.Client.TwoStepVerificationActionType associated with the challenge. Valid values: `0`, `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8` | **Responses:** - `200`: OK → `Roblox.TwoStepVerification.Api.MetadataResponse` **Response fields** (`Roblox.TwoStepVerification.Api.MetadataResponse`) See [Roblox.TwoStepVerification.Api.MetadataResponse](#roblox-twostepverification-api-metadataresponse) in Models. **Response example:** ```json { "twoStepVerificationEnabled": false, "authenticatorQrCodeSize": "string", "emailCodeLength": 0, "authenticatorCodeLength": 0, "authenticatorHelpSiteAddress": "string", "isPasswordRequiredForEnablingAuthenticator": false } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/metadata#TwoStepVerificationApi" ``` ### GET `/v1/mobile-client-version` Get mobile client version information based on app version parameter **Server:** `https://clientsettings.roblox.com` **Auth:** **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `appVersion` | query | `string` | Yes | AppiOSV2.13, AppVersioniOS2.0.1, etc | **Responses:** - `200`: OK → `Roblox.ClientSettings.Api.Models.Response.MobileClientVersionResponse` - `400`: 2: Invalid binaryType. **Response fields** (`Roblox.ClientSettings.Api.Models.Response.MobileClientVersionResponse`) See [Roblox.ClientSettings.Api.Models.Response.MobileClientVersionResponse](#roblox-clientsettings-api-models-response-mobileclientversionresponse) in Models. **Response example:** ```json { "activeVersion": "string", "upgradeSource": "string", "MD5Sum": "string", "data": { "UpgradeAction": "string" } } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://clientsettings.roblox.com/v1/mobile-client-version?appVersion={VALUE}" ``` ### GET `/v1/my/friends/count` Get the number of friends a user has **Server:** `https://friends.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Friends.Api.FriendsCountResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Friends.Api.FriendsCountResponse`) See [Roblox.Friends.Api.FriendsCountResponse](#roblox-friends-api-friendscountresponse) in Models. **Response example:** ```json { "count": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://friends.roblox.com/v1/my/friends/count" ``` ### POST `/v1/my/friends/refresh-qr-session` **Server:** `https://friends.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Friends.Api.Models.Response.RefreshQrSessionResponse` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Friends.Api.Models.Response.RefreshQrSessionResponse`) See [Roblox.Friends.Api.Models.Response.RefreshQrSessionResponse](#roblox-friends-api-models-response-refreshqrsessionresponse) in Models. **Response example:** ```json { "Success": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://friends.roblox.com/v1/my/friends/refresh-qr-session" ``` ### GET `/v1/my/friends/requests` Get all users that friend requests with targetUserId using exclusive start paging **Server:** `https://friends.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `limit` | query | `integer` | No | The number of results per request. | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sessionId` | query | `string` | No | Optional session identifier. | | `friendRequestSort` | query | `0 \| 1 \| 2` | No | Valid values: `0`, `1`, `2` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Friends.Api.FriendRequestResponse_` - `400`: 1: The target user is invalid or does not exist. 6: Invalid parameters. - `401`: 0: Authorization has been denied for this request. - `403`: 2: The user is banned from performing operation. 3: The user is blocked from performing this action. - `429`: 9: The flood limit has been exceeded. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Friends.Api.FriendRequestResponse_`) See [Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Friends.Api.FriendRequestResponse_](#roblox-web-webapi-models-apipageresponse-roblox-friends-api-friendrequestresponse-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "friendRequest": "...", "mutualFriendsList": "...", "hasVerifiedBadge": "...", "description": "...", "created": "...", "isBanned": "..." } ] } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://friends.roblox.com/v1/my/friends/requests" ``` ### GET `/v1/my/friends/{userId}/check-qr-session` **Server:** `https://friends.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | user Id that shows the qr code | **Responses:** - `200`: OK → `boolean` - `401`: 0: Authorization has been denied for this request. **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://friends.roblox.com/v1/my/friends/{USERID}/check-qr-session" ``` ### GET `/v1/my/new-friend-requests/count` **Server:** `https://friends.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Friends.Api.Models.Response.NewFriendRequestsCountResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Friends.Api.Models.Response.NewFriendRequestsCountResponse`) See [Roblox.Friends.Api.Models.Response.NewFriendRequestsCountResponse](#roblox-friends-api-models-response-newfriendrequestscountresponse) in Models. **Response example:** ```json { "count": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://friends.roblox.com/v1/my/new-friend-requests/count" ``` ### POST `/v1/name-description/games/translation-history` Gets the history for name or description in a provided language. **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.GetNameDescriptionHistoryV2Request` See [Roblox.GameInternationalization.Api.GetNameDescriptionHistoryV2Request](#roblox-gameinternationalization-api-getnamedescriptionhistoryv2request) in Models. **Request example:** ```json { "contentId": 0, "contentType": "UniverseDisplayNames", "languageCode": "string", "cursor": "string", "count": 0, "sortOrder": "Asc" } ``` **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.GetNameDescriptionHistoryResponse` - `400`: 13: Request body can't be null 14: Invalid game id 18: You do not have permission to manage this game 22: Invalid language code 39: Count should be at least 1 and less than 50. 53: Language is not supported for the game. 54: No history available for source data 55: Invalid exclusive start Id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.GetNameDescriptionHistoryResponse`) See [Roblox.GameInternationalization.Api.GetNameDescriptionHistoryResponse](#roblox-gameinternationalization-api-getnamedescriptionhistoryresponse) in Models. **Response example:** ```json { "history": [ { "translationText": "...", "translator": "...", "created": "..." } ], "lastEvaluatedId": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/name-description/games/translation-history" \ -H "Content-Type: application/json" \ -d '{ "contentId": 0, "contentType": "UniverseDisplayNames", "languageCode": "string", "cursor": "string", "count": 0, "sortOrder": "Asc" }' ``` ### GET `/v1/name-description/games/{gameId}` Gets a game's name and description in all supported languages **Server:** `https://gameinternationalization.roblox.com` **Auth:** **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The id of the game | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.NameDescription_` - `400`: 14: Invalid game id - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.NameDescription_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.NameDescription_](#roblox-web-webapi-models-apiarrayresponse-roblox-gameinternationalization-api-namedescription-) in Models. **Response example:** ```json { "data": [ { "name": "...", "description": "...", "updateType": "...", "languageCode": "..." } ] } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://gameinternationalization.roblox.com/v1/name-description/games/{GAMEID}" ``` ### PATCH `/v1/name-description/games/{gameId}` Updates a game's name and/or description in multiple languages. **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The id of the game. | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.UpdateNameDescriptionsRequest` See [Roblox.GameInternationalization.Api.UpdateNameDescriptionsRequest](#roblox-gameinternationalization-api-updatenamedescriptionsrequest) in Models. **Request example:** ```json { "data": [ { "name": "...", "description": "...", "updateType": "...", "languageCode": "..." } ] } ``` **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.UpdateNameDescriptionsResponse` - `400`: 14: Invalid game id 19: New name is null or whitespaces or new name/description is too long 20: New name or description is moderated 22: Invalid language code 23: You can't delete translations for source language 26: You can't update translations for source language 53: Language is not supported for the game. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.UpdateNameDescriptionsResponse`) See [Roblox.GameInternationalization.Api.UpdateNameDescriptionsResponse](#roblox-gameinternationalization-api-updatenamedescriptionsresponse) in Models. **Response example:** ```json { "successOperations": [ { "name": "...", "description": "...", "updateType": "...", "languageCode": "..." } ], "failedOperations": [ { "languageCode": "...", "errorCode": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/name-description/games/{GAMEID}" \ -H "Content-Type: application/json" \ -d '{ "data": [ { "name": "...", "description": "...", "updateType": "...", "languageCode": "..." } ] }' ``` ### GET `/v1/name-description/metadata` Rollout settings for name/description migration to new page **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.Models.Response.GameNameDescriptionMetadataResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.GameInternationalization.Api.Models.Response.GameNameDescriptionMetadataResponse`) See [Roblox.GameInternationalization.Api.Models.Response.GameNameDescriptionMetadataResponse](#roblox-gameinternationalization-api-models-response-gamenamedescriptionmetadataresponse) in Models. **Response example:** ```json { "isNameDescriptionMigrationEnabled": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/name-description/metadata" ``` ### POST `/v1/outfits/{userOutfitId}/delete` Deletes the outfit. You are only allowed to delete outfits you created. **Server:** `https://avatar.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userOutfitId` | path | `integer` | Yes | The user outfit id. | | `Roblox-Place-Id` | header | `integer` | No | | **Responses:** - `200`: OK → `Roblox.Api.Avatar.Models.AvatarApiSuccessResponse` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: You don't have permission to delete this outfit. - `404`: 1: The specified userOutfitId is invalid! - `500`: 3: An error occurred while deleting the outfit. **Response fields** (`Roblox.Api.Avatar.Models.AvatarApiSuccessResponse`) See [Roblox.Api.Avatar.Models.AvatarApiSuccessResponse](#roblox-api-avatar-models-avatarapisuccessresponse) in Models. **Response example:** ```json { "success": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://avatar.roblox.com/v1/outfits/{USEROUTFITID}/delete" ``` ### GET `/v1/outfits/{userOutfitId}/details` Gets details about the contents of an outfit. **Server:** `https://avatar.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userOutfitId` | path | `integer` | Yes | The user outfit id. | | `Roblox-Place-Id` | header | `integer` | No | | **Responses:** - `200`: OK → `Roblox.Api.Avatar.Models.OutfitDetailsModel` - `400`: 2: The outfit for the specified userOutfit is invalid. - `403`: 3: The requester does not have access to the details for the given user outfit. - `404`: 1: The specified userOutfitId is invalid. **Response fields** (`Roblox.Api.Avatar.Models.OutfitDetailsModel`) See [Roblox.Api.Avatar.Models.OutfitDetailsModel](#roblox-api-avatar-models-outfitdetailsmodel) in Models. **Response example:** ```json { "id": 0, "universeId": 0, "name": "string", "assets": [ { "id": "...", "name": "...", "assetType": "...", "currentVersionId": "...", "meta": "...", "availabilityStatus": "..." } ], "bodyColors": { "headColorId": 0, "torsoColorId": 0, "rightArmColorId": 0, "leftArmColorId": 0, "rightLegColorId": 0, "leftLegColorId": 0 }, "scale": { "height": 0, "width": 0, "head": 0, "depth": 0, "proportion": 0, "bodyType": 0 } } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://avatar.roblox.com/v1/outfits/{USEROUTFITID}/details" ``` ### GET `/v1/packages/{packageId}/assets` *(deprecated)* Given a package ID, returns the list of asset IDs for that package Packages have been migrated to bundles. Use catalog.roblox.com/v1/bundles/{bundleId}/details **Server:** `https://inventory.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `packageID` | path | `integer` | Yes | The asset ID of the package | **Responses:** - `200`: OK → `Roblox.Inventory.Api.Models.AssetIdListModel` **Response fields** (`Roblox.Inventory.Api.Models.AssetIdListModel`) See [Roblox.Inventory.Api.Models.AssetIdListModel](#roblox-inventory-api-models-assetidlistmodel) in Models. **Response example:** ```json { "assetIds": [ 0 ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://inventory.roblox.com/v1/packages/{packageId}/assets" ``` ### GET `/v1/phone` Get Verified Phone Number **Server:** `https://accountinformation.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.AccountInformation.Api.Models.PhoneResponse` - `401`: 0: Authorization has been denied for this request. - `500`: 0: An unknown error occured. **Response fields** (`Roblox.AccountInformation.Api.Models.PhoneResponse`) See [Roblox.AccountInformation.Api.Models.PhoneResponse](#roblox-accountinformation-api-models-phoneresponse) in Models. **Response example:** ```json { "countryCode": "string", "prefix": "string", "phone": "string", "isVerified": false, "verificationCodeLength": 0, "canBypassPasswordForPhoneUpdate": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountinformation.roblox.com/v1/phone" ``` ### POST `/v1/phone` Set Phone Number **Server:** `https://accountinformation.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Platform.UserPhoneNumberVerification.Models.PendingVerificationResponse` - `400`: 2: Invalid Phone Number 3: Phone Number Already Associated 8: Invalid Phone Number Type - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 5: Incorrect Password 10: - `429`: 6: Flooded - `500`: 0: An unknown error occured. - `503`: 1: This feature is currently disabled. Please try again later. **Response fields** (`Roblox.Platform.UserPhoneNumberVerification.Models.PendingVerificationResponse`) See [Roblox.Platform.UserPhoneNumberVerification.Models.PendingVerificationResponse](#roblox-platform-userphonenumberverification-models-pendingverificationresponse) in Models. **Response example:** ```json { "verificationChannel": "string", "data": "string" } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountinformation.roblox.com/v1/phone" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### POST `/v1/phone/delete` Delete Phone **Server:** `https://accountinformation.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 5: Incorrect Password - `429`: 6: Flooded - `500`: 0: An unknown error occured. - `503`: 1: This feature is currently disabled. Please try again later. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountinformation.roblox.com/v1/phone/delete" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### POST `/v1/phone/resend` Resend Phone code **Server:** `https://accountinformation.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.AccountInformation.Api.Models.EmptyRequest` See [Roblox.AccountInformation.Api.Models.EmptyRequest](#roblox-accountinformation-api-models-emptyrequest) in Models. **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `429`: 6: Flooded - `500`: 0: An unknown error occured. - `503`: 1: This feature is currently disabled. Please try again later. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountinformation.roblox.com/v1/phone/resend" \ -H "Content-Type: application/json" \ -d '"..."' ``` ### POST `/v1/phone/verify` Verify Phone **Server:** `https://accountinformation.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.AccountInformation.Api.Models.VerifyPhoneRequest` See [Roblox.AccountInformation.Api.Models.VerifyPhoneRequest](#roblox-accountinformation-api-models-verifyphonerequest) in Models. **Request example:** ```json { "code": "string" } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 2: Invalid Phone Number 3: Phone Number Already Associated 7: Invalid Code - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `429`: 6: Flooded - `500`: 0: An unknown error occured. - `503`: 1: This feature is currently disabled. Please try again later. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountinformation.roblox.com/v1/phone/verify" \ -H "Content-Type: application/json" \ -d '{ "code": "string" }' ``` ### GET `/v1/places/gameicons` [STABLE] Fetches game icon URLs for a list of places. Ids that do not correspond to a valid place will be filtered out. **Server:** `https://thumbnails.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `placeIds` | query | `integer[]` | Yes | The place ids. | | `returnPolicy` | query | `PlaceHolder \| ForcePlaceHolder \| AutoGenerated \| ForceAutoGenerated` | No | Optional policy to use in selecting game icon to return (default = PlaceHolder). Valid values: `PlaceHolder`, `ForcePlaceHolder`, `AutoGenerated`, `ForceAutoGenerated` | | `size` | query | `string enum (6 values)` | No | The thumbnail size, formatted widthxheight Valid values: `50x50`, `128x128`, `150x150`, `256x256`, `420x420`, `512x512` | | `format` | query | `Png \| Jpeg \| Webp` | No | The thumbnail format Valid values: `Png`, `Jpeg`, `Webp` | | `isCircular` | query | `true \| false` | No | The circle thumbnail output parameter, true or false Valid values: `true`, `false` | **Responses:** - `200`: OK - `400`: 1: There are too many requested Ids. 2: The requested image format is invalid. Please see documentation for valid thumbnail format parameter name and values. 3: The requested size is invalid. Please see documentation for valid thumbnail size parameter name and format. 4: The requested Ids are invalid, of an invalid type or missing. 8: The requested return policy is invalid (must be PlaceHolder, AutoGenerated or ForceAutoGenerated). 10: Circular thumbnail requests are not allowed - `403`: 9: User not authorized to use AutoGenerated or ForceAutoGenerated return policies. **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://thumbnails.roblox.com/v1/places/gameicons?placeIds={VALUE}" ``` ### POST `/v1/places/{placeId}` Updates the place configuration for the place with the id placeId Currently the only supported functionality for updating the configuration is around Name, and Description. **Server:** `https://develop.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `placeId` | path | `integer` | Yes | The place id for the place to be updated. | **Responses:** - `200`: OK → `Roblox.Api.Develop.Models.PlaceModel` - `400`: placeId is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: Authenticated user is not authorized to manage this place. 0: Token Validation Failed **Response fields** (`Roblox.Api.Develop.Models.PlaceModel`) See [Roblox.Api.Develop.Models.PlaceModel](#roblox-api-develop-models-placemodel) in Models. **Response example:** ```json { "id": 0, "universeId": 0, "name": "string", "description": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/places/{PLACEID}" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### PATCH `/v1/places/{placeId}` Updates the place configuration for the place with the id placeId Currently the only supported functionality for updating the configuration is around Name, and Description. **Server:** `https://develop.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `placeId` | path | `integer` | Yes | The place id for the place to be updated. | **Responses:** - `200`: OK → `Roblox.Api.Develop.Models.PlaceModel` - `400`: placeId is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: Authenticated user is not authorized to manage this place. 0: Token Validation Failed **Response fields** (`Roblox.Api.Develop.Models.PlaceModel`) See [Roblox.Api.Develop.Models.PlaceModel](#roblox-api-develop-models-placemodel) in Models. **Response example:** ```json { "id": 0, "universeId": 0, "name": "string", "description": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/places/{PLACEID}" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### GET `/v1/places/{placeId}/teamcreate/active_session/members` List of users in the active Team Create session **Server:** `https://develop.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `placeId` | path | `integer` | Yes | The place Id. | | `limit` | query | `10 \| 25 \| 50 \| 100` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Web.Responses.Users.SkinnyUserResponse_` - `400`: 1: The universe is invalid. 5: The place is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: 2: Not authorized to perform this action. 4: TeamCreate on universe is disabled. - `404`: 0: An unknown error occurred. - `500`: 6: Multiple active sessions in a Team Create place. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Web.Responses.Users.SkinnyUserResponse_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Web.Responses.Users.SkinnyUserResponse_](#roblox-web-webapi-models-apiarrayresponse-roblox-web-responses-users-skinnyuserresponse-) in Models. **Response example:** ```json { "data": [ { "id": "...", "name": "...", "displayName": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/places/{PLACEID}/teamcreate/active_session/members" ``` ### GET `/v1/plugins` Gets plugin details by ids. **Server:** `https://develop.roblox.com` **Auth:** **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `pluginIds` | query | `integer[]` | Yes | The plugin ids. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Web.Responses.Plugins.PluginResponse_` - `400`: 1: Too many ids. 2: The format of the ids are invalid. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Web.Responses.Plugins.PluginResponse_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Web.Responses.Plugins.PluginResponse_](#roblox-web-webapi-models-apiarrayresponse-roblox-web-responses-plugins-pluginresponse-) in Models. **Response example:** ```json { "data": [ { "id": "...", "name": "...", "description": "...", "commentsEnabled": "...", "versionId": "...", "created": "..." } ] } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://develop.roblox.com/v1/plugins?pluginIds={VALUE}" ``` ### PATCH `/v1/plugins/{pluginId}` Updates a plugin. **Server:** `https://develop.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `pluginId` | path | `integer` | Yes | The id of the plugin. | **Request Body:** `application/json` — Type: `Roblox.Develop.Api.UpdatePluginRequest` See [Roblox.Develop.Api.UpdatePluginRequest](#roblox-develop-api-updatepluginrequest) in Models. **Request example:** ```json { "name": "string", "description": "string", "commentsEnabled": false } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 5: Description too long. 6: Text moderated. 7: Invalid name. 8: The request body is missing. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 4: Insufficient permissions. - `404`: 3: The id is invalid. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/plugins/{PLUGINID}" \ -H "Content-Type: application/json" \ -d '{ "name": "string", "description": "string", "commentsEnabled": false }' ``` ### POST `/v1/plugins/{pluginId}/icon` Overwrites a plugin icon with a new one. **Server:** `https://publish.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `pluginId` | path | `integer` | Yes | The plugin Id. | **Responses:** - `200`: OK → `Roblox.Publish.Api.UploadResponse` - `400`: 2: File not present in request. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 5: You do not have permission to manage this item. - `404`: 4: Target item is invalid or does not exist. - `429`: 3: You're uploading too much, please wait and try again later. **Response fields** (`Roblox.Publish.Api.UploadResponse`) See [Roblox.Publish.Api.UploadResponse](#roblox-publish-api-uploadresponse) in Models. **Response example:** ```json { "targetId": 0 } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://publish.roblox.com/v1/plugins/{PLUGINID}/icon" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### POST `/v1/presence/users` **Server:** `https://presence.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `any` **Responses:** - `200`: Success → `UserPresencesResponse` - `400`: Bad Request → `PresenceApi.ErrorResponse` - `403`: Forbidden → `PresenceApi.ErrorResponse` - `429`: Too Many Requests → `PresenceApi.ErrorResponse` **Response fields** (`UserPresencesResponse`) See [UserPresencesResponse](#userpresencesresponse) in Models. **Response example:** ```json { "userPresences": [ { "userPresenceType": "...", "lastLocation": "...", "placeId": "...", "rootPlaceId": "...", "gameId": "...", "universeId": "..." } ] } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://presence.roblox.com/v1/presence/users" \ -H "Content-Type: application/json" \ -d '"..."' ``` ### GET `/v1/private-servers/enabled-in-universe/{universeId}` **Server:** `https://games.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | | **Responses:** - `200`: Success → `PrivateServersEnabledInUniverseResponse` **Response fields** (`PrivateServersEnabledInUniverseResponse`) See [PrivateServersEnabledInUniverseResponse](#privateserversenabledinuniverseresponse) in Models. **Response example:** ```json { "privateServersEnabled": false } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://games.roblox.com/v1/private-servers/enabled-in-universe/{UNIVERSEID}" ``` ### GET `/v1/private-servers/my-private-servers` **Server:** `https://games.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `privateServersTab` | query | `any` | No | | | `itemsPerPage` | query | `integer` | No | | | `cursor` | query | `string` | No | | **Responses:** - `200`: Success → `MyPrivateServersResponse` **Response fields** (`MyPrivateServersResponse`) See [MyPrivateServersResponse](#myprivateserversresponse) in Models. **Response example:** ```json { "nextPageCursor": "string", "previousPageCursor": "string", "data": [ { "active": "...", "universeId": "...", "placeId": "...", "name": "...", "ownerId": "...", "ownerName": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://games.roblox.com/v1/private-servers/my-private-servers" ``` ### GET `/v1/promotion-channels` Get the user's promotion channels **Server:** `https://accountinformation.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `alwaysReturnUrls` | query | `boolean` | No | Whether all promotion channel links should be returned as full URLs. | | `filterLink` | query | `boolean` | No | Whether all promotion channel links should be filtered. | | `onlyShortenTwitter` | query | `boolean` | No | Whether all promotion channels links except for Twitter should be returned as full URLs. If false, all promotion channels will be shortened. | **Responses:** - `200`: OK → `Roblox.AccountInformation.Api.Models.PromotionChannelsResponse` - `400`: 1: User not found. - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.AccountInformation.Api.Models.PromotionChannelsResponse`) See [Roblox.AccountInformation.Api.Models.PromotionChannelsResponse](#roblox-accountinformation-api-models-promotionchannelsresponse) in Models. **Response example:** ```json { "promotionChannelsVisibilityPrivacy": "string", "facebook": "string", "twitter": "string", "youtube": "string", "twitch": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountinformation.roblox.com/v1/promotion-channels" ``` ### POST `/v1/promotion-channels` Update the user's promotion channels **Server:** `https://accountinformation.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.AccountInformation.Api.Models.PromotionChannelsRequest` See [Roblox.AccountInformation.Api.Models.PromotionChannelsRequest](#roblox-accountinformation-api-models-promotionchannelsrequest) in Models. **Request example:** ```json { "facebook": "string", "twitter": "string", "youtube": "string", "twitch": "string", "promotionChannelsVisibilityPrivacy": "string" } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 2: The request was empty. 11: The Facebook profile url is invalid. 12: The Twitter handle is invalid. 13: The YouTube url is invalid. 14: The Twitch profile url is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 4: Only users who are over twelve years of age may edit social network channels. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountinformation.roblox.com/v1/promotion-channels" \ -H "Content-Type: application/json" \ -d '{ "facebook": "string", "twitter": "string", "youtube": "string", "twitch": "string", "promotionChannelsVisibilityPrivacy": "string" }' ``` ### GET `/v1/resize/{hash}/{width}/{height}/{type}/{format}/{filterType}` Resizes larger thumbnails to specified size and format **Server:** `https://thumbnailsresizer.roblox.com` **Auth:** **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `hash` | path | `string` | Yes | Hash of larger thumbnail | | `width` | path | `integer` | Yes | Desired width of thumbnail | | `height` | path | `integer` | Yes | Desired height of thumbnail | | `type` | path | `string` | Yes | Thumbnail Type | | `format` | path | `string` | Yes | Desired image format of the thumbnail | | `filterType` | query | `string` | Yes | E.g. is output circular | | `shouldModify` | query | `boolean` | No | | **Responses:** - `200`: OK **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://thumbnailsresizer.roblox.com/v1/resize/{HASH}/{WIDTH}/{HEIGHT}/{TYPE}/{FORMAT}/{filterType}?filterType={VALUE}" ``` ### GET `/v1/roles` Gets the Roles by their ids. **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `ids` | query | `integer[]` | Yes | A list of role ids | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Groups.Api.GroupRoleDetailResponse_` - `400`: 1: Ids could not be parsed from request. 2: Too many ids in request. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Groups.Api.GroupRoleDetailResponse_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Groups.Api.GroupRoleDetailResponse_](#roblox-web-webapi-models-apiarrayresponse-roblox-groups-api-grouproledetailresponse-) in Models. **Response example:** ```json { "data": [ { "groupId": "...", "id": "...", "name": "...", "description": "...", "rank": "...", "memberCount": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/roles?ids={VALUE}" ``` ### GET `/v1/secureresize/{thumbPrint}/{hash}/{width}/{height}/{type}/{format}/{filterType}` Decrypts and Resizes larger thumbnails to specified size and format **Server:** `https://thumbnailsresizer.roblox.com` **Auth:** **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `thumbPrint` | path | `string` | Yes | The thumbPrint that represents the key version | | `hash` | path | `string` | Yes | Hash of larger thumbnail | | `width` | path | `integer` | Yes | Desired width of thumbnail | | `height` | path | `integer` | Yes | Desired height of thumbnail | | `type` | path | `string` | Yes | Thumbnail Type | | `format` | path | `string` | Yes | Desired image format of the thumbnail | | `filterType` | query | `string` | No | E.g. is output circular | **Responses:** - `200`: OK **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://thumbnailsresizer.roblox.com/v1/secureresize/{THUMBPRINT}/{HASH}/{WIDTH}/{HEIGHT}/{TYPE}/{FORMAT}/{filterType}" ``` ### GET `/v1/source-language/games/{gameId}` Gets the source language of a game **Server:** `https://gameinternationalization.roblox.com` **Auth:** **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | | **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.Language` - `400`: 14: Invalid game id - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.Language`) See [Roblox.GameInternationalization.Api.Language](#roblox-gameinternationalization-api-language) in Models. **Response example:** ```json { "name": "string", "nativeName": "string", "languageCode": "string" } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://gameinternationalization.roblox.com/v1/source-language/games/{GAMEID}" ``` ### PATCH `/v1/source-language/games/{gameId}` Sets the source language of a game **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | | | `languageCode` | query | `string` | Yes | | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 14: Invalid game id 22: Invalid language code - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `500`: 85: Failed to disable automatic translation status for languages - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/source-language/games/{GAMEID}?languageCode={VALUE}" ``` ### GET `/v1/source-language/games/{gameId}/language-with-locales` Gets the source language of a game **Server:** `https://gameinternationalization.roblox.com` **Auth:** **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | | **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.SourceLanguageWithLocales` - `400`: 14: Invalid game id - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.SourceLanguageWithLocales`) See [Roblox.GameInternationalization.Api.SourceLanguageWithLocales](#roblox-gameinternationalization-api-sourcelanguagewithlocales) in Models. **Response example:** ```json { "languageFamily": { "name": "string", "nativeName": "string", "languageCode": "string" }, "defaultLocale": { "id": 0, "locale": "en_us", "localeCode": "string", "name": "string", "nativeName": "string", "language": { "id": "...", "name": "...", "nativeName": "...", "languageCode": "...", "isRightToLeft": "..." } }, "childLocales": [ { "id": "...", "locale": "...", "localeCode": "...", "name": "...", "nativeName": "...", "language": "..." } ] } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://gameinternationalization.roblox.com/v1/source-language/games/{GAMEID}/language-with-locales" ``` ### GET `/v1/subcategories` Lists Subcategory Names and their Ids. **Server:** `https://catalog.roblox.com` **Auth:** **Responses:** - `200`: OK → `object` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://catalog.roblox.com/v1/subcategories" ``` ### GET `/v1/supported-languages/games/{gameId}` Get the supported languages for a game. **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The id of the game. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.LanguageOrLocale_` - `400`: 14: Invalid game id - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.LanguageOrLocale_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.LanguageOrLocale_](#roblox-web-webapi-models-apiarrayresponse-roblox-gameinternationalization-api-languageorlocale-) in Models. **Response example:** ```json { "data": [ { "name": "...", "languageCodeType": "...", "languageCode": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/supported-languages/games/{GAMEID}" ``` ### PATCH `/v1/supported-languages/games/{gameId}` Add or remove supported languages for a game. **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The id of the game. | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.PatchLanguage[]` See [Roblox.GameInternationalization.Api.PatchLanguage](#roblox-gameinternationalization-api-patchlanguage) in Models. **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 14: Invalid game id 22: Invalid language code 49: Duplicate language codes are not allowed. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/supported-languages/games/{GAMEID}" \ -H "Content-Type: application/json" \ -d '[ { "languageCodeType": "Language", "languageCode": "string", "delete": false } ]' ``` ### GET `/v1/supported-languages/games/{gameId}/automatic-translation-status` Get the automatic translation status of supported languages for a game. **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The id of the game. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.LanguageOrLocaleSettings_` - `400`: 14: Invalid game id - `401`: 0: Authorization has been denied for this request. - `403`: 18: You do not have permission to manage this game - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.LanguageOrLocaleSettings_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.LanguageOrLocaleSettings_](#roblox-web-webapi-models-apiarrayresponse-roblox-gameinternationalization-api-languageorlocalesettings-) in Models. **Response example:** ```json { "data": [ { "languageCodeType": "...", "languageCode": "...", "isAutomaticTranslationEnabled": "...", "isImageTranslationEnabled": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/supported-languages/games/{GAMEID}/automatic-translation-status" ``` ### GET `/v1/supported-languages/games/{gameId}/in-experience-language-selection` Get the user's in-experience language selector languages for a game. **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The id of the game. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.LanguageOrLocale_` - `400`: 14: Invalid game id - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.LanguageOrLocale_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.LanguageOrLocale_](#roblox-web-webapi-models-apiarrayresponse-roblox-gameinternationalization-api-languageorlocale-) in Models. **Response example:** ```json { "data": [ { "name": "...", "languageCodeType": "...", "languageCode": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/supported-languages/games/{GAMEID}/in-experience-language-selection" ``` ### PATCH `/v1/supported-languages/games/{gameId}/languages/{languageCode}/automatic-translation-status` Enable or disable automatic translation for a game and language. **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The id of the game. | | `languageCode` | path | `string` | Yes | The language to enable or disable for automatic translation. | **Request Body:** `application/json` — Type: `boolean` **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.EditAutomaticTranslationStatusForGameAndLanguageResponse` - `400`: 14: Invalid game id 22: Invalid language code 53: Language is not supported for the game. 72: Automatic translation cannot be enabled for game. 75: Automatic translation cannot be enabled for language. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.EditAutomaticTranslationStatusForGameAndLanguageResponse`) See [Roblox.GameInternationalization.Api.EditAutomaticTranslationStatusForGameAndLanguageResponse](#roblox-gameinternationalization-api-editautomatictranslationstatusforgameandlanguageresponse) in Models. **Response example:** ```json { "gameId": 0, "languageCode": "string", "isAutomaticTranslationEnabled": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/supported-languages/games/{GAMEID}/languages/{LANGUAGECODE}/automatic-translation-status" \ -H "Content-Type: application/json" \ -d 'false' ``` ### PATCH `/v1/supported-languages/games/{gameId}/languages/{languageCode}/image-translation-status` Enable or disable image translation for a game and language. **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The id of the game. | | `languageCode` | path | `string` | Yes | The language to enable or disable for image translation. | **Request Body:** `application/json` — Type: `boolean` **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.EditImageTranslationStatusForGameAndLanguageResponse` - `400`: 14: Invalid game id 22: Invalid language code 53: Language is not supported for the game. 93: Image translation cannot be enabled for language. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.EditImageTranslationStatusForGameAndLanguageResponse`) See [Roblox.GameInternationalization.Api.EditImageTranslationStatusForGameAndLanguageResponse](#roblox-gameinternationalization-api-editimagetranslationstatusforgameandlanguageresponse) in Models. **Response example:** ```json { "gameId": 0, "languageCode": "string", "isImageTranslationEnabled": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/supported-languages/games/{GAMEID}/languages/{LANGUAGECODE}/image-translation-status" \ -H "Content-Type: application/json" \ -d 'false' ``` ### PATCH `/v1/supported-languages/games/{gameId}/languages/{languageCode}/universe-display-info-automatic-translation-settings` Update the switch which controls if the UniverseDisplayInformation should be automatically translated. **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The game id. | | `languageCode` | path | `string` | Yes | The language code. | **Request Body:** `application/json` — Type: `boolean` **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.UpdateUniverseDisplayInfoAutomaticTranslationSettingsResponse` - `400`: 14: Invalid game id 72: Automatic translation cannot be enabled for game. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `500`: 77: Content localization set settings return error code invalid 79: Invalid content instance settings 80: Invalid quota settings 81: Invalid change agent 82: Failed to update UniverseDisplayInformation content instance auto translation settings **Response fields** (`Roblox.GameInternationalization.Api.UpdateUniverseDisplayInfoAutomaticTranslationSettingsResponse`) See [Roblox.GameInternationalization.Api.UpdateUniverseDisplayInfoAutomaticTranslationSettingsResponse](#roblox-gameinternationalization-api-updateuniversedisplayinfoautomatictranslationsettingsresponse) in Models. **Response example:** ```json { "gameId": 0, "languageCode": "string", "isUniverseDisplayInfoAutomaticTranslationEnabled": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/supported-languages/games/{GAMEID}/languages/{LANGUAGECODE}/universe-display-info-automatic-translation-settings" \ -H "Content-Type: application/json" \ -d 'false' ``` ### GET `/v1/supported-languages/games/{gameId}/universe-display-info-automatic-translation-settings` Get UniverseDisplayInfo automatic translation settings. **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The game id. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.UniverseDisplayInfoAutomaticTranslationSettings_` - `400`: 14: Invalid game id - `401`: 0: Authorization has been denied for this request. - `403`: 18: You do not have permission to manage this game - `500`: 0: An unknown error occurred. 22: Invalid language code 83: Failed to get UniverseDisplayInformation content instance auto translation settings 84: Count of language code is larger than max batch get size **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.UniverseDisplayInfoAutomaticTranslationSettings_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.UniverseDisplayInfoAutomaticTranslationSettings_](#roblox-web-webapi-models-apiarrayresponse-roblox-gameinternationalization-api-universedisplayinfoautomatictranslationsettings-) in Models. **Response example:** ```json { "data": [ { "languageCode": "...", "isUniverseDisplayInfoAutomaticTranslationEnabled": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/supported-languages/games/{GAMEID}/universe-display-info-automatic-translation-settings" ``` ### GET `/v1/supported-languages/metadata` Rollout settings for supported languages of a game **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.SupportedLanguagesMetadataResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.GameInternationalization.Api.SupportedLanguagesMetadataResponse`) See [Roblox.GameInternationalization.Api.SupportedLanguagesMetadataResponse](#roblox-gameinternationalization-api-supportedlanguagesmetadataresponse) in Models. **Response example:** ```json { "isFeatureEnabled": false, "areAllLanguagesEnabled": false, "minimumUniverseIdForFeature": 0, "isHumanTranslationProgressUIEnabled": false, "isAutomaticTranslationProgressUIEnabled": false, "isSupportedLanguagesChildLocalesUIEnabled": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/supported-languages/metadata" ``` ### GET `/v1/themes/types` returns all the enabled theme types. **Server:** `https://accountsettings.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_System.String_` **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_System.String_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_System.String_](#roblox-web-webapi-models-apiarrayresponse-system-string-) in Models. **Response example:** ```json { "data": [ "string" ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountsettings.roblox.com/v1/themes/types" ``` ### GET `/v1/themes/{consumerType}/{consumerId}` returns the theme type for a specific consumer. **Server:** `https://accountsettings.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `consumerType` | path | `1` | Yes | The consumer type Valid values: `1` | | `consumerId` | path | `string` | Yes | The consumer's theme configuration to get. If the consumerType is User always return the AuthenticatedUser's theme type. | **Responses:** - `200`: OK → `Roblox.AccountSettings.Api.ThemeConfigurationResponse` - `400`: 3: Invalid consumer type. - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.AccountSettings.Api.ThemeConfigurationResponse`) See [Roblox.AccountSettings.Api.ThemeConfigurationResponse](#roblox-accountsettings-api-themeconfigurationresponse) in Models. **Response example:** ```json { "themeType": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountsettings.roblox.com/v1/themes/{CONSUMERTYPE}/{CONSUMERID}" ``` ### PATCH `/v1/themes/{consumerType}/{consumerId}` Modify the theme type for consumer. **Server:** `https://accountsettings.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `consumerType` | path | `1` | Yes | The consumer type Valid values: `1` | | `consumerId` | path | `integer` | Yes | The consumer's theme configuration to modify. If the consumerType is User always modify the AuthenticatedUser's theme type. | **Request Body:** `application/json` — Type: `Roblox.AccountSettings.Api.ThemeConfigurationRequest` See [Roblox.AccountSettings.Api.ThemeConfigurationRequest](#roblox-accountsettings-api-themeconfigurationrequest) in Models. **Request example:** ```json { "themeType": "string" } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 2: Invalid theme type. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountsettings.roblox.com/v1/themes/{CONSUMERTYPE}/{CONSUMERID}" \ -H "Content-Type: application/json" \ -d '{ "themeType": "string" }' ``` ### POST `/v1/topic/get-topics` Get topic given TopicRequestModel. **Server:** `https://catalog.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Catalog.Api.TopicRequestModel` See [Roblox.Catalog.Api.TopicRequestModel](#roblox-catalog-api-topicrequestmodel) in Models. **Request example:** ```json { "items": [ { "TargetId": "...", "ItemType": "..." } ], "selectTopics": [ "string" ], "inputQuery": "string", "maxResult": 0, "genderType": 1 } ``` **Responses:** - `200`: OK → `Roblox.Catalog.Api.TopicResponse` - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Catalog.Api.TopicResponse`) See [Roblox.Catalog.Api.TopicResponse](#roblox-catalog-api-topicresponse) in Models. **Response example:** ```json { "topics": [ { "displayName": "...", "originalTopicName": "..." } ], "error": { "Message": "string", "Code": 0 } } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://catalog.roblox.com/v1/topic/get-topics" \ -H "Content-Type: application/json" \ -d '{ "items": [ { "TargetId": "...", "ItemType": "..." } ], "selectTopics": [ "string" ], "inputQuery": "string", "maxResult": 0, "genderType": 1 }' ``` ### GET `/v1/trade-privacy` Get a user's trade privacy setting **Server:** `https://accountsettings.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.AccountSettings.Api.TradePrivacyResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.AccountSettings.Api.TradePrivacyResponse`) See [Roblox.AccountSettings.Api.TradePrivacyResponse](#roblox-accountsettings-api-tradeprivacyresponse) in Models. **Response example:** ```json { "tradePrivacy": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountsettings.roblox.com/v1/trade-privacy" ``` ### POST `/v1/trade-privacy` Updates a user's trade privacy setting **Server:** `https://accountsettings.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.AccountSettings.Api.UpdateTradePrivacyRequest` See [Roblox.AccountSettings.Api.UpdateTradePrivacyRequest](#roblox-accountsettings-api-updatetradeprivacyrequest) in Models. **Request example:** ```json { "tradePrivacy": 0 } ``` **Responses:** - `200`: OK → `Roblox.AccountSettings.Api.TradePrivacyUpdateResponse` - `400`: Roblox.AccountSettings.Api.ResponseEnums.TradeSettingsErrors.InvalidTradePrivacy - `401`: 0: Authorization has been denied for this request. - `403`: Roblox.AccountSettings.Api.ResponseEnums.TradeSettingsErrors.AccountLocked OR Roblox.AccountSettings.Api.ResponseEnums.TradeSettingsErrors.UserCannotTrade 0: Token Validation Failed **Response fields** (`Roblox.AccountSettings.Api.TradePrivacyUpdateResponse`) See [Roblox.AccountSettings.Api.TradePrivacyUpdateResponse](#roblox-accountsettings-api-tradeprivacyupdateresponse) in Models. **Response example:** ```json { "tradePrivacy": 0, "inventoryPrivacy": 1, "privacySettingResponse": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountsettings.roblox.com/v1/trade-privacy" \ -H "Content-Type: application/json" \ -d '{ "tradePrivacy": 0 }' ``` ### GET `/v1/trade-value` Get a user's trade quality filter setting **Server:** `https://accountsettings.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.AccountSettings.Api.TradeValueResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.AccountSettings.Api.TradeValueResponse`) See [Roblox.AccountSettings.Api.TradeValueResponse](#roblox-accountsettings-api-tradevalueresponse) in Models. **Response example:** ```json { "tradeValue": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountsettings.roblox.com/v1/trade-value" ``` ### POST `/v1/trade-value` Updates a user's trade quality filter setting **Server:** `https://accountsettings.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.AccountSettings.Api.TradeValueRequest` See [Roblox.AccountSettings.Api.TradeValueRequest](#roblox-accountsettings-api-tradevaluerequest) in Models. **Request example:** ```json { "tradeValue": 0 } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: Roblox.AccountSettings.Api.ResponseEnums.TradeSettingsErrors.InvalidTradeValue - `401`: 0: Authorization has been denied for this request. - `403`: Roblox.AccountSettings.Api.ResponseEnums.TradeSettingsErrors.AccountLocked OR Roblox.AccountSettings.Api.ResponseEnums.TradeSettingsErrors.UserCannotTrade 0: Token Validation Failed **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountsettings.roblox.com/v1/trade-value" \ -H "Content-Type: application/json" \ -d '{ "tradeValue": 0 }' ``` ### POST `/v1/trades/expire-outdated` Deprecated. TradeSession are automatically set to expire while the inbound/outbound trades are fetched. Expires Outdated Inbound Trades for User **Server:** `https://trades.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://trades.roblox.com/v1/trades/expire-outdated" ``` ### GET `/v1/trades/metadata` Gets metadata about the trade system. **Server:** `https://trades.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Trades.Api.TradeMetadata` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Trades.Api.TradeMetadata`) See [Roblox.Trades.Api.TradeMetadata](#roblox-trades-api-trademetadata) in Models. **Response example:** ```json { "maxItemsPerSide": 0, "minValueRatio": 0, "tradeSystemMaxRobuxPercent": 0, "tradeSystemRobuxFee": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://trades.roblox.com/v1/trades/metadata" ``` ### POST `/v1/trades/send` Sends a trade. **Server:** `https://trades.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Trades.Api.NewTradeResponse` - `400`: 7: The user cannot trade. See field for whether the user who cannot trade is the sender or receiver. 8: The trade request should include offers. 9: Invalid number of trade offers. 10: Invalid trade partner. See field for whether the invalid partner is the sender or receiver. 11: Cannot add negative Robux amounts to a trade. 12: One or more userAssets are invalid. See fieldData for details. 13: Invalid number of userAssets in one side of the trade. 15: The trade is unbalanced. 16: Trade value ratio is not sufficient. 17: You have insufficient Robux to make this offer. 18: Too many Robux in one side of the offer. See field for whether the side is the sender or receiver. 19: Unknown error while processing the trade. 21: Cannot trade with yourself. 22: User's privacy settings are too strict to allow trading. See field for whether the user is the sender or receiver. 23: The trade reaches Two Step Verification thresholds and the user has not verified in the past time threshold. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `429`: 14: You are sending too many trade requests. Please slow down and try again later. - `502`: 0: An unknown error occured. - `503`: 5: Trading system is unavailable **Response fields** (`Roblox.Trades.Api.NewTradeResponse`) See [Roblox.Trades.Api.NewTradeResponse](#roblox-trades-api-newtraderesponse) in Models. **Response example:** ```json { "id": 0 } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://trades.roblox.com/v1/trades/send" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### GET `/v1/trades/{tradeId}` Gets detailed information about a trade. **Server:** `https://trades.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `tradeId` | path | `integer` | Yes | The trade id. | **Responses:** - `200`: OK → `Roblox.Trades.Api.TradeDetailResponse` - `400`: 2: The trade cannot be found or you are not authorized to view it. - `401`: 0: Authorization has been denied for this request. 4: You are not authorized to modify this trade. - `404`: 2: The trade cannot be found or you are not authorized to view it. - `500`: 0: An unknown error occured. **Response fields** (`Roblox.Trades.Api.TradeDetailResponse`) See [Roblox.Trades.Api.TradeDetailResponse](#roblox-trades-api-tradedetailresponse) in Models. **Response example:** ```json { "offers": [ { "user": "...", "userAssets": "...", "robux": "..." } ], "id": 0, "user": { "id": 0, "name": "string", "displayName": "string" }, "created": "2024-01-01T00:00:00Z", "expiration": "2024-01-01T00:00:00Z", "isActive": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://trades.roblox.com/v1/trades/{TRADEID}" ``` ### POST `/v1/trades/{tradeId}/accept` Accepts a trade. **Server:** `https://trades.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `tradeId` | path | `integer` | Yes | The trade id. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 2: The trade cannot be found or you are not authorized to view it. 3: The trade is inactive. 4: You are not authorized to modify this trade. 6: Trade needs to be confirmed by the other party. 6: Trade needs to be confirmed by the other party. 7: The user cannot trade. See field for whether the user who cannot trade is the sender or receiver. 23: The trade reaches Two Step Verification thresholds and the user has not verified in the past time threshold. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `503`: 5: Trading system is unavailable **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://trades.roblox.com/v1/trades/{TRADEID}/accept" ``` ### POST `/v1/trades/{tradeId}/counter` Counters a trade. **Server:** `https://trades.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `tradeId` | path | `integer` | Yes | The trade id. | **Responses:** - `200`: OK → `Roblox.Trades.Api.NewTradeResponse` - `400`: 2: The trade cannot be found or you are not authorized to view it. 4: You are not authorized to modify this trade. 7: The user cannot trade. See field for whether the user who cannot trade is the sender or receiver. 8: The trade request should include offers. 9: Invalid number of trade offers. 10: Invalid trade partner. See field for whether the invalid partner is the sender or receiver. 11: Cannot add negative Robux amounts to a trade. 12: One or more userAssets are invalid. See fieldData for details. 13: Invalid number of userAssets in one side of the trade. 15: The trade is unbalanced. 16: Trade value ratio is not sufficient. 17: You have insufficient Robux to make this offer. 18: Too many Robux in one side of the offer. See field for whether the side is the sender or receiver. 19: Unknown error while processing the trade. 21: Cannot trade with yourself. 22: User's privacy settings are too strict to allow trading. See field for whether the user is the sender or receiver. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `429`: 14: You are sending too many trade requests. Please slow down and try again later. - `502`: 0: An unknown error occured. - `503`: 5: Trading system is unavailable **Response fields** (`Roblox.Trades.Api.NewTradeResponse`) See [Roblox.Trades.Api.NewTradeResponse](#roblox-trades-api-newtraderesponse) in Models. **Response example:** ```json { "id": 0 } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://trades.roblox.com/v1/trades/{TRADEID}/counter" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### POST `/v1/trades/{tradeId}/decline` Declines a trade. **Server:** `https://trades.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `tradeId` | path | `integer` | Yes | The trade id. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 2: The trade cannot be found or you are not authorized to view it. 3: The trade is inactive. 4: You are not authorized to modify this trade. 7: The user cannot trade. See field for whether the user who cannot trade is the sender or receiver. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `503`: 5: Trading system is unavailable **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://trades.roblox.com/v1/trades/{TRADEID}/decline" ``` ### GET `/v1/trades/{tradeStatusType}` Fetches a list of the authenticated user's trades. **Server:** `https://trades.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `tradeStatusType` | path | `1 \| 2 \| 3 \| 4` | Yes | The trade status type. Valid values: `1`, `2`, `3`, `4` | | `limit` | query | `10 \| 25 \| 50 \| 100` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `Asc \| Desc` | No | Sorted by trade creation date Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Trades.Api.TradeResponse_` - `400`: 1: Invalid trade status type. - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Trades.Api.TradeResponse_`) See [Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Trades.Api.TradeResponse_](#roblox-web-webapi-models-apipageresponse-roblox-trades-api-traderesponse-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "id": "...", "user": "...", "created": "...", "expiration": "...", "isActive": "...", "status": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://trades.roblox.com/v1/trades/{TRADESTATUSTYPE}" ``` ### GET `/v1/trades/{tradeStatusType}/count` Gets the total number of pending trades for the authenticated user. Inbound is the only accepted tradeStatusType. **Server:** `https://trades.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `tradeStatusType` | path | `1 \| 2 \| 3 \| 4` | Yes | The trade status type to fetch a total count for. Valid values: `1`, `2`, `3`, `4` | **Responses:** - `200`: OK → `Roblox.Trades.Api.TradeCountResponse` - `400`: 1: Invalid trade status type. - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Trades.Api.TradeCountResponse`) See [Roblox.Trades.Api.TradeCountResponse](#roblox-trades-api-tradecountresponse) in Models. **Response example:** ```json { "count": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://trades.roblox.com/v1/trades/{TRADESTATUSTYPE}/count" ``` ### GET `/v1/translation-analytics/games/{gameId}/download-translation-analytics-report` Download translation analytics report after the report is ready **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The game's id | | `startDateTime` | query | `string` | Yes | The inclusive start dateTime of report in UTC | | `endDateTime` | query | `string` | Yes | The exclusive end dateTime of report in UTC | | `reportType` | query | `GameTranslationStatus \| GameTranslationStatusForTranslator \| GameTranslationStatusForTranslatorGroup \| Test` | Yes | The report type Valid values: `GameTranslationStatus`, `GameTranslationStatusForTranslator`, `GameTranslationStatusForTranslatorGroup`, `Test` | | `reportSubjectTargetId` | query | `integer` | Yes | The translator group id | **Responses:** - `200`: OK → `object` - `400`: 14: Invalid game id 56: You need to provide a valid translator group id to get report. 58: Start datetime or end datetime is invlaid. 59: Report type is invalid - `401`: 0: Authorization has been denied for this request. - `403`: 57: You do not have permission to request translation analytics report. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/translation-analytics/games/{GAMEID}/download-translation-analytics-report?startDateTime={VALUE}&endDateTime={VALUE}&reportType={VALUE}&reportSubjectTargetId={VALUE}" ``` ### POST `/v1/translation-analytics/games/{gameId}/request-translation-analytics-report` Request translation analytics report to be generated **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The game's id | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.RequestTranslationAnalyticsReportRequest` See [Roblox.GameInternationalization.Api.RequestTranslationAnalyticsReportRequest](#roblox-gameinternationalization-api-requesttranslationanalyticsreportrequest) in Models. **Request example:** ```json { "startDateTime": "2024-01-01T00:00:00Z", "endDateTime": "2024-01-01T00:00:00Z", "reportType": "GameTranslationStatus", "reportSubjectTargetId": 0 } ``` **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.RequestTranslationAnalyticsReportResponse` - `400`: 14: Invalid game id 56: You need to provide a valid translator group id to get report. 58: Start datetime or end datetime is invlaid. 59: Report type is invalid - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 57: You do not have permission to request translation analytics report. **Response fields** (`Roblox.GameInternationalization.Api.RequestTranslationAnalyticsReportResponse`) See [Roblox.GameInternationalization.Api.RequestTranslationAnalyticsReportResponse](#roblox-gameinternationalization-api-requesttranslationanalyticsreportresponse) in Models. **Response example:** ```json { "reportGenerationStatus": "inProgress" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/translation-analytics/games/{GAMEID}/request-translation-analytics-report" \ -H "Content-Type: application/json" \ -d '{ "startDateTime": "2024-01-01T00:00:00Z", "endDateTime": "2024-01-01T00:00:00Z", "reportType": "GameTranslationStatus", "reportSubjectTargetId": 0 }' ``` ### GET `/v1/translation-analytics/metadata` Get metadata related to UI and rollout settings **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.TranslationAnalyticsMetadataResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.GameInternationalization.Api.TranslationAnalyticsMetadataResponse`) See [Roblox.GameInternationalization.Api.TranslationAnalyticsMetadataResponse](#roblox-gameinternationalization-api-translationanalyticsmetadataresponse) in Models. **Response example:** ```json { "isFeatureEnabledOnUI": false, "reportRequestPollingIntervalSeconds": 0, "minimumDateTimeForAnalyticsReport": "2024-01-01T00:00:00Z" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/translation-analytics/metadata" ``` ### GET `/v1/ui-configurations` Get ui configurations for frontend to use. **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.GetUiConfigurationsResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.GameInternationalization.Api.GetUiConfigurationsResponse`) See [Roblox.GameInternationalization.Api.GetUiConfigurationsResponse](#roblox-gameinternationalization-api-getuiconfigurationsresponse) in Models. **Response example:** ```json { "isGameProductsEnabled": false, "isBadgeIconEnabled": false, "isGamePassEnabled": false, "isDeveloperProductEnabled": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/ui-configurations" ``` ### GET `/v1/universe-payout-history` Gets the engagement payout history for a specific universe and a given date range, specified by start and end dates. **Server:** `https://engagementpayouts.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | query | `integer` | Yes | The ID of the universe in question. | | `startDate` | query | `string` | Yes | The first date in the range, specified as yyyy-MM-dd. | | `endDate` | query | `string` | Yes | The last date in the range, specified as yyyy-MM-dd. | **Responses:** - `200`: OK → `object` - `400`: 1: InvalidUniverseId 2: InvalidStartDate 3: InvalidEndDate 4: InvalidDateRange 5: Forbidden 6: TooManyDays - `401`: 0: Authorization has been denied for this request. **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://engagementpayouts.roblox.com/v1/universe-payout-history?universeId={VALUE}&startDate={VALUE}&endDate={VALUE}" ``` ### GET `/v1/universes/multiget` Gets a list of universes. If a universe can not be found for a given ID (such as -1) it will be skipped. **Server:** `https://develop.roblox.com` **Auth:** **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `ids` | query | `integer[]` | Yes | The universe IDs to get. Limit 100. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Api.Develop.Models.UniverseModel_` - `400`: 8: No universe IDs sent to get. 9: Too many universe IDs sent to get, the limit is: **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Api.Develop.Models.UniverseModel_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Api.Develop.Models.UniverseModel_](#roblox-web-webapi-models-apiarrayresponse-roblox-api-develop-models-universemodel-) in Models. **Response example:** ```json { "data": [ { "id": "...", "name": "...", "description": "...", "isArchived": "...", "rootPlaceId": "...", "isActive": "..." } ] } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://develop.roblox.com/v1/universes/multiget?ids={VALUE}" ``` ### GET `/v1/universes/multiget/permissions` Returns an array of granted and declined permissions related to the universes with the ids in ids for the authenticated user. If a universe can not be found for a given ID (such as -1) it will be skipped. **Server:** `https://develop.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `ids` | query | `integer[]` | Yes | The universe ids. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Api.Develop.Models.UniverseIdPermissionsModel_` - `400`: 8: No universe IDs sent to get. 9: Too many universe IDs sent to get, the limit is: - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Api.Develop.Models.UniverseIdPermissionsModel_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Api.Develop.Models.UniverseIdPermissionsModel_](#roblox-web-webapi-models-apiarrayresponse-roblox-api-develop-models-universeidpermissionsmodel-) in Models. **Response example:** ```json { "data": [ { "universeId": "...", "canManage": "...", "canCloudEdit": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/universes/multiget/permissions?ids={VALUE}" ``` ### GET `/v1/universes/multiget/teamcreate` Gets TeamCreate settings for multiple universes specified by Ids **Server:** `https://develop.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `ids` | query | `integer[]` | Yes | The universe Ids. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Api.Develop.Models.UniverseTeamCreateSettingsModel_` - `400`: Roblox.Api.Develop.ResponseEnums.TeamCreateErrors.TooManyUniverseIdsSent - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Api.Develop.Models.UniverseTeamCreateSettingsModel_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Api.Develop.Models.UniverseTeamCreateSettingsModel_](#roblox-web-webapi-models-apiarrayresponse-roblox-api-develop-models-universeteamcreatesettingsmodel-) in Models. **Response example:** ```json { "data": [ { "id": "...", "isEnabled": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/universes/multiget/teamcreate?ids={VALUE}" ``` ### GET `/v1/universes/user-public-publish-eligibility` Returns the result of various checks for a user's eligibility to publish a public universe **Server:** `https://develop.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Api.Develop.Models.UserPublicPublishEligibilityResponse` **Response fields** (`Roblox.Api.Develop.Models.UserPublicPublishEligibilityResponse`) See [Roblox.Api.Develop.Models.UserPublicPublishEligibilityResponse](#roblox-api-develop-models-userpublicpublisheligibilityresponse) in Models. **Response example:** ```json { "isEligible": false, "hasTransactions": 0, "idVerified": 0, "hasDevex": 0 } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/universes/user-public-publish-eligibility" ``` ### GET `/v1/universes/{universeId}` Gets a Roblox.Api.Develop.Models.UniverseModel. **Server:** `https://develop.roblox.com` **Auth:** **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The Universe id. | **Responses:** - `200`: OK → `Roblox.Api.Develop.Models.UniverseModel` - `400`: 1: The universe does not exist. **Response fields** (`Roblox.Api.Develop.Models.UniverseModel`) See [Roblox.Api.Develop.Models.UniverseModel](#roblox-api-develop-models-universemodel) in Models. **Response example:** ```json { "id": 0, "name": "string", "description": "string", "isArchived": false, "rootPlaceId": 0, "isActive": false } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://develop.roblox.com/v1/universes/{UNIVERSEID}" ``` ### POST `/v1/universes/{universeId}/activate` Activates a universes. **Server:** `https://develop.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe id. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: The universe does not exist. 2: This universe does not have a root place. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 3: You are not authorized to configure this universe. 6: The root place for this universe is under review and can not be activated. 7: Creator already has the maximum number of places active. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/universes/{UNIVERSEID}/activate" ``` ### GET `/v1/universes/{universeId}/activation-eligibility` Returns the result of various checks for a user's eligibility to activate a given universe from private to public universeId for authenticated user **Server:** `https://develop.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe id. | **Responses:** - `200`: OK → `Roblox.Api.Develop.Models.ActivationEligibilityResponse` **Response fields** (`Roblox.Api.Develop.Models.ActivationEligibilityResponse`) See [Roblox.Api.Develop.Models.ActivationEligibilityResponse](#roblox-api-develop-models-activationeligibilityresponse) in Models. **Response example:** ```json { "isEligible": false, "maturityRated": false, "isUserEligibleForPublicPublish": false, "remainingPublicPublishCount": 0, "isPublicPublish": false, "isPublishToExistingUniverse": false } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/universes/{UNIVERSEID}/activation-eligibility" ``` ### GET `/v1/universes/{universeId}/badges` Gets badges by their awarding game. **Server:** `https://badges.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe Id. | | `sortBy` | query | `Rank \| DateCreated` | No | The key to sort badges by. Valid values: `Rank`, `DateCreated` | | `limit` | query | `10 \| 25 \| 50 \| 100` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `Asc \| Desc` | No | The order the results are sorted in. Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Badges.Api.BadgeResponse_` - `400`: 26: The pagination cursor is invalid or incompatible with the current request. - `404`: 3: The game is invalid or does not exist. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Badges.Api.BadgeResponse_`) See [Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Badges.Api.BadgeResponse_](#roblox-web-webapi-models-apipageresponse-roblox-badges-api-badgeresponse-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "id": "...", "name": "...", "description": "...", "displayName": "...", "displayDescription": "...", "enabled": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://badges.roblox.com/v1/universes/{UNIVERSEID}/badges" ``` ### POST `/v1/universes/{universeId}/badges` Creates a new badge. **Server:** `https://badges.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The ID of the universe to create the badge for. | **Request Body:** `multipart/form-data` — Type: `object` | Property | Type | Required | Description | |----------|------|----------|-------------| | `name` | `string` | No | The badge name. | | `description` | `string` | No | The badge description. | | `paymentSourceType` | `1 \| 2` | No | Whether or not to pay for the badge with user funds, or group funds. ['User' = 1, 'Group' = 2] | | `files` | `string` | No | The badge icon. | | `expectedCost` | `integer` | No | User expected cost of a badge. | | `isActive` | `boolean` | No | Whether or not the badge should be created in the active state. | **Request example:** ```json { "name": "string", "description": "string", "paymentSourceType": 1, "files": "string", "expectedCost": 0, "isActive": false } ``` **Responses:** - `200`: OK → `Roblox.Web.Responses.Badges.BadgeResponseV2` - `400`: 11: The badge icon is invalid. 14: Invalid badge name. 15: Invalid badge description. 16: Payment source is invalid. 18: Expected badge cost is different from the actual badge cost. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 6: Text moderated. 12: You do not have permission to manage this game's badges. 17: Insufficient funds. - `404`: 3: The game is invalid or does not exist. - `429`: 13: Too many requests, try again later. **Response fields** (`Roblox.Web.Responses.Badges.BadgeResponseV2`) See [Roblox.Web.Responses.Badges.BadgeResponseV2](#roblox-web-responses-badges-badgeresponsev2) in Models. **Response example:** ```json { "id": 0, "name": "string", "description": "string", "displayName": "string", "displayDescription": "string", "enabled": false } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://badges.roblox.com/v1/universes/{UNIVERSEID}/badges" \ -F "name=string" \ -F "description=string" \ -F "paymentSourceType=1" \ -F "files=@file.bin;type=application/octet-stream" \ -F "expectedCost=0" \ -F "isActive=false" ``` ### GET `/v1/universes/{universeId}/configuration` Get settings for an owned universe. **Server:** `https://develop.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe Id. | **Responses:** - `200`: OK → `Roblox.Api.Develop.Models.UniverseSettingsResponse` - `400`: 1: The universe does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 2: You are not authorized to configure this universe. **Response fields** (`Roblox.Api.Develop.Models.UniverseSettingsResponse`) See [Roblox.Api.Develop.Models.UniverseSettingsResponse](#roblox-api-develop-models-universesettingsresponse) in Models. **Response example:** ```json { "allowPrivateServers": false, "privateServerPrice": 0, "isMeshTextureApiAccessAllowed": false, "isRewardedOnDemandAdsAllowed": false, "id": 0, "name": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/universes/{UNIVERSEID}/configuration" ``` ### PATCH `/v1/universes/{universeId}/configuration` Update universe settings for an owned universe. **Server:** `https://develop.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universeId. | **Request Body:** `application/json` — Type: `Roblox.Api.Develop.Models.UniverseSettingsRequest` See [Roblox.Api.Develop.Models.UniverseSettingsRequest](#roblox-api-develop-models-universesettingsrequest) in Models. **Request example:** ```json { "name": "string", "universeAvatarType": 1, "universeScaleType": 1, "universeAnimationType": 1, "universeCollisionType": 1, "universeBodyType": 1 } ``` **Responses:** - `200`: OK → `Roblox.Api.Develop.Models.UniverseSettingsResponse` - `400`: 1: The universe does not exist. 3: Invalid UniverseAvatarType. 4: Invalid UniverseScaleType. 5: Invalid UniverseAnimationType. 6: Invalid UniverseCollisionType. 7: New universe name or description has been rejected. 8: New universe name is too long. 10: Invalid UniverseBodyType. 11: Invalid UniverseJointPositioningType. 12: The universe has no root place. 15: Price is required when isForSale is true. 16: This game cannot be offered for sale because it is not public. 17: This game cannot be offered for sale because it has private servers enabled. 18: The game price is outside of the allowed range. 19: Invalid genre. 20: The request body is missing. 21: Invalid device type. 22: Invalid asset type. 23: Invalid value, the min must be less than or equal to the max 24: Invalid scale value 41: You cannot change the private server price again so soon after the previous change. Please try again later. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: You are not authorized to configure this universe. 14: You are not authorized to sell games. - `409`: 9: Failed to shutdown all intances of game after changing AvatarType. The change has been reverted. **Response fields** (`Roblox.Api.Develop.Models.UniverseSettingsResponse`) See [Roblox.Api.Develop.Models.UniverseSettingsResponse](#roblox-api-develop-models-universesettingsresponse) in Models. **Response example:** ```json { "allowPrivateServers": false, "privateServerPrice": 0, "isMeshTextureApiAccessAllowed": false, "isRewardedOnDemandAdsAllowed": false, "id": 0, "name": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/universes/{UNIVERSEID}/configuration" \ -H "Content-Type: application/json" \ -d '{ "name": "string", "universeAvatarType": 1, "universeScaleType": 1, "universeAnimationType": 1, "universeCollisionType": 1, "universeBodyType": 1 }' ``` ### GET `/v1/universes/{universeId}/configuration/vip-servers` Get settings for an owned universe's VIP servers. **Server:** `https://develop.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe Id. | **Responses:** - `200`: OK → `Roblox.Api.Develop.Models.PrivateServerDetailsResponse` - `400`: 1: The universe does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 2: You are not authorized to configure this universe. **Response fields** (`Roblox.Api.Develop.Models.PrivateServerDetailsResponse`) See [Roblox.Api.Develop.Models.PrivateServerDetailsResponse](#roblox-api-develop-models-privateserverdetailsresponse) in Models. **Response example:** ```json { "isEnabled": false, "price": 0, "activeServersCount": 0, "activeSubscriptionsCount": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/universes/{UNIVERSEID}/configuration/vip-servers" ``` ### POST `/v1/universes/{universeId}/deactivate` Deactivates a universe. **Server:** `https://develop.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe id. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: The universe does not exist. 2: This universe does not have a root place. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 3: You are not authorized to configure this universe. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/universes/{UNIVERSEID}/deactivate" ``` ### GET `/v1/universes/{universeId}/free-badges-quota` Gets the number of free badges left for the current UTC day by their awarding game. **Server:** `https://badges.roblox.com` **Auth:** **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe Id. | **Responses:** - `200`: OK → `integer` - `404`: 3: The game is invalid or does not exist. **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://badges.roblox.com/v1/universes/{UNIVERSEID}/free-badges-quota" ``` ### GET `/v1/universes/{universeId}/permissions` Returns list of granted and declined permissions related to the universe with the id universeId for authenticated user **Server:** `https://develop.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe id. | **Responses:** - `200`: OK → `Roblox.Api.Develop.Models.UniversePermissionsModel` - `400`: 1: The universe does not exist. - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Api.Develop.Models.UniversePermissionsModel`) See [Roblox.Api.Develop.Models.UniversePermissionsModel](#roblox-api-develop-models-universepermissionsmodel) in Models. **Response example:** ```json { "canManage": false, "canCloudEdit": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/universes/{UNIVERSEID}/permissions" ``` ### GET `/v1/universes/{universeId}/places` Gets a list of places for a universe. **Server:** `https://develop.roblox.com` **Auth:** **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The asset id. | | `isUniverseCreation` | query | `boolean` | No | | | `limit` | query | `10 \| 25 \| 50 \| 100` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `Asc \| Desc` | No | Sorted by placeId Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Api.Develop.Models.IPlaceModel_` **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Api.Develop.Models.IPlaceModel_`) See [Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Api.Develop.Models.IPlaceModel_](#roblox-web-webapi-models-apipageresponse-roblox-api-develop-models-iplacemodel-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ "..." ] } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://develop.roblox.com/v1/universes/{UNIVERSEID}/places" ``` ### GET `/v1/universes/{universeId}/stats` Get statistics data for a universe. **Server:** `https://economycreatorstats.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe id. | | `Type` | query | `PremiumUpsells \| PremiumVisits` | Yes | Valid values: `PremiumUpsells`, `PremiumVisits` | | `StartTime` | query | `string` | Yes | | | `EndTime` | query | `string` | Yes | | **Responses:** - `200`: OK → `Roblox.EconomyCreatorStats.Api.Models.StatisticsResponse` - `400`: 1: The Universe is invalid. 3: Too many data points requested. 4: The requested data type is not known. - `401`: 0: Authorization has been denied for this request. 2: Not authorized to perform this action. **Response fields** (`Roblox.EconomyCreatorStats.Api.Models.StatisticsResponse`) See [Roblox.EconomyCreatorStats.Api.Models.StatisticsResponse](#roblox-economycreatorstats-api-models-statisticsresponse) in Models. **Response example:** ```json { "dataGranularity": 0, "data": "..." } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://economycreatorstats.roblox.com/v1/universes/{UNIVERSEID}/stats?Type={VALUE}&StartTime={VALUE}&EndTime={VALUE}" ``` ### GET `/v1/universes/{universeId}/teamcreate` Gets TeamCreate settings for an Roblox.Platform.Universes.IUniverse. **Server:** `https://develop.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe Id. | **Responses:** - `200`: OK → `Roblox.Api.Develop.Models.Response.TeamCreateSettingsResponse` - `400`: Roblox.Api.Develop.ResponseEnums.TeamCreateErrors.InvalidUniverse - `401`: 0: Authorization has been denied for this request. - `403`: Roblox.Api.Develop.ResponseEnums.TeamCreateErrors.Unauthorized **Response fields** (`Roblox.Api.Develop.Models.Response.TeamCreateSettingsResponse`) See [Roblox.Api.Develop.Models.Response.TeamCreateSettingsResponse](#roblox-api-develop-models-response-teamcreatesettingsresponse) in Models. **Response example:** ```json { "isEnabled": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/universes/{UNIVERSEID}/teamcreate" ``` ### PATCH `/v1/universes/{universeId}/teamcreate` Edit team create settings for a universe. Enables, or disables team create for a universe. **Server:** `https://develop.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe Id. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: Roblox.Api.Develop.ResponseEnums.TeamCreateErrors.InvalidUniverse - `401`: 0: Authorization has been denied for this request. - `403`: Roblox.Api.Develop.ResponseEnums.TeamCreateErrors.Unauthorized 0: Token Validation Failed **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/universes/{UNIVERSEID}/teamcreate" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### DELETE `/v1/universes/{universeId}/teamcreate/memberships` Removes a user from a TeamCreate permissions list. **Server:** `https://develop.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe Id. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: Roblox.Api.Develop.ResponseEnums.TeamCreateErrors.InvalidUniverse OR Roblox.Api.Develop.ResponseEnums.TeamCreateErrors.InvalidUser - `401`: 0: Authorization has been denied for this request. - `403`: Roblox.Api.Develop.ResponseEnums.TeamCreateErrors.TeamCreateDisabled 0: Token Validation Failed **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/universes/{UNIVERSEID}/teamcreate/memberships" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### GET `/v1/user-localization-settings/player-choice/{universeId}` Get user player choice settings for universe. **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe's ID. | **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.Models.Response.GetPlayerChoiceUniverseSettingsResponse` - `400`: 14: Invalid game id - `401`: 0: Authorization has been denied for this request. - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.Models.Response.GetPlayerChoiceUniverseSettingsResponse`) See [Roblox.GameInternationalization.Api.Models.Response.GetPlayerChoiceUniverseSettingsResponse](#roblox-gameinternationalization-api-models-response-getplayerchoiceuniversesettingsresponse) in Models. **Response example:** ```json { "IsPlayerChoiceEnabled": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/user-localization-settings/player-choice/{UNIVERSEID}" ``` ### GET `/v1/user-localization-settings/universe/{universeId}` Get user localization settings for universe. **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe's ID. | **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.Models.Response.GetUserLocalizationSettingsForUniverseResponse` - `400`: 14: Invalid game id 21: The language is not supported 22: Invalid language code - `401`: 0: Authorization has been denied for this request. - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.Models.Response.GetUserLocalizationSettingsForUniverseResponse`) See [Roblox.GameInternationalization.Api.Models.Response.GetUserLocalizationSettingsForUniverseResponse](#roblox-gameinternationalization-api-models-response-getuserlocalizationsettingsforuniverseresponse) in Models. **Response example:** ```json { "userUniverseLocalizationSettingValue": { "settingType": "LanguageFamily", "settingTargetId": 0, "settingTargetCode": "string" } } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/user-localization-settings/universe/{UNIVERSEID}" ``` ### POST `/v1/user-localization-settings/universe/{universeId}` Set user localization settings for universe. **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe's ID. | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.Models.Request.SetUserLocalizationSettingsRequest` See [Roblox.GameInternationalization.Api.Models.Request.SetUserLocalizationSettingsRequest](#roblox-gameinternationalization-api-models-request-setuserlocalizationsettingsrequest) in Models. **Request example:** ```json { "settingValue": { "settingType": "LanguageFamily", "settingTargetId": 0, "settingTargetCode": "string" } } ``` **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.Models.Response.SetUserLocalizationSettingsResponse` - `400`: 14: Invalid game id 22: Invalid language code - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.Models.Response.SetUserLocalizationSettingsResponse`) See [Roblox.GameInternationalization.Api.Models.Response.SetUserLocalizationSettingsResponse](#roblox-gameinternationalization-api-models-response-setuserlocalizationsettingsresponse) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/user-localization-settings/universe/{UNIVERSEID}" \ -H "Content-Type: application/json" \ -d '{ "settingValue": { "settingType": "LanguageFamily", "settingTargetId": 0, "settingTargetCode": "string" } }' ``` ### DELETE `/v1/user/badges/{badgeId}` Removes a badge from the authenticated user. **Server:** `https://badges.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `badgeId` | path | `integer` | Yes | The badge Id. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `404`: 1: Badge is invalid or does not exist. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://badges.roblox.com/v1/user/badges/{BADGEID}" ``` ### GET `/v1/user/currency` Gets currency for the authenticated user. Currency can only be retrieved for the authenticated user. **Server:** `https://economy.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Web.Responses.Economy.CurrencyResponse` - `401`: 0: Authorization has been denied for this request. - `403`: 1: The user is invalid. **Response fields** (`Roblox.Web.Responses.Economy.CurrencyResponse`) See [Roblox.Web.Responses.Economy.CurrencyResponse](#roblox-web-responses-economy-currencyresponse) in Models. **Response example:** ```json { "robux": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://economy.roblox.com/v1/user/currency" ``` ### POST `/v1/user/following-exists` Returns whether or not the current user is following each userId in a list of userIds **Server:** `https://friends.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Friends.Api.FollowingExistsRequestModel` See [Roblox.Friends.Api.FollowingExistsRequestModel](#roblox-friends-api-followingexistsrequestmodel) in Models. **Request example:** ```json { "targetUserIds": [ 0 ] } ``` **Responses:** - `200`: OK → `Roblox.Friends.Api.Models.Response.FollowingExistsResponseModel` - `400`: 0: An invalid userId was passed in. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `503`: 1: Followers are disabled at this time. **Response fields** (`Roblox.Friends.Api.Models.Response.FollowingExistsResponseModel`) See [Roblox.Friends.Api.Models.Response.FollowingExistsResponseModel](#roblox-friends-api-models-response-followingexistsresponsemodel) in Models. **Response example:** ```json { "followings": [ { "isFollowing": "...", "isFollowed": "...", "userId": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://friends.roblox.com/v1/user/following-exists" \ -H "Content-Type: application/json" \ -d '{ "targetUserIds": [ 0 ] }' ``` ### GET `/v1/user/friend-requests/count` Return the number of pending friend requests. **Server:** `https://friends.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Friends.Api.PendingFriendRequestCountModel` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Friends.Api.PendingFriendRequestCountModel`) See [Roblox.Friends.Api.PendingFriendRequestCountModel](#roblox-friends-api-pendingfriendrequestcountmodel) in Models. **Response example:** ```json { "count": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://friends.roblox.com/v1/user/friend-requests/count" ``` ### POST `/v1/user/get-tags` Gets the tags for multiple users **Server:** `https://contacts.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Contacts.Api.Request.GetUserTagsRequestModel` See [Roblox.Contacts.Api.Request.GetUserTagsRequestModel](#roblox-contacts-api-request-getusertagsrequestmodel) in Models. **Request example:** ```json { "targetUserIds": [ 0 ] } ``` **Responses:** - `200`: OK → `Roblox.Contacts.Api.Response.GetUserTagsResponseModel[]` - `400`: 4: Invalid parameters. 8: Too many user Tags are requested. - `401`: 0: Authorization has been denied for this request. - `429`: 10: The flood limit has been exceeded. **Response fields** (`Roblox.Contacts.Api.Response.GetUserTagsResponseModel[]`) See [Roblox.Contacts.Api.Response.GetUserTagsResponseModel](#roblox-contacts-api-response-getusertagsresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://contacts.roblox.com/v1/user/get-tags" \ -H "Content-Type: application/json" \ -d '{ "targetUserIds": [ 0 ] }' ``` ### GET `/v1/user/groups/canmanage` Gets a list of Groups that a user can manage. **Server:** `https://develop.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Api.Develop.Models.GroupModel_` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Api.Develop.Models.GroupModel_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Api.Develop.Models.GroupModel_](#roblox-web-webapi-models-apiarrayresponse-roblox-api-develop-models-groupmodel-) in Models. **Response example:** ```json { "data": [ { "id": "...", "name": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/user/groups/canmanage" ``` ### GET `/v1/user/groups/canmanagegamesoritems` Gets a list of groups a user can manage games or items for. **Server:** `https://develop.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Api.Develop.Models.GroupModel_` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Api.Develop.Models.GroupModel_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Api.Develop.Models.GroupModel_](#roblox-web-webapi-models-apiarrayresponse-roblox-api-develop-models-groupmodel-) in Models. **Response example:** ```json { "data": [ { "id": "...", "name": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/user/groups/canmanagegamesoritems" ``` ### GET `/v1/user/groups/pending` Gets groups that the authenticated user has requested to join **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Groups.Api.GroupDetailResponse_` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Groups.Api.GroupDetailResponse_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Groups.Api.GroupDetailResponse_](#roblox-web-webapi-models-apiarrayresponse-roblox-groups-api-groupdetailresponse-) in Models. **Response example:** ```json { "data": [ { "id": "...", "name": "...", "description": "...", "owner": "...", "shout": "...", "memberCount": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/user/groups/pending" ``` ### POST `/v1/user/groups/primary` Sets the authenticated user's primary group **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Groups.Api.PrimaryGroupRequest` See [Roblox.Groups.Api.PrimaryGroupRequest](#roblox-groups-api-primarygrouprequest) in Models. **Request example:** ```json { "groupId": 0 } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: Group is invalid or does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: You aren't a member of the group specified. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/user/groups/primary" \ -H "Content-Type: application/json" \ -d '{ "groupId": 0 }' ``` ### DELETE `/v1/user/groups/primary` Removes the authenticated user's primary group **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/user/groups/primary" ``` ### POST `/v1/user/tag` Sets the tag for a user **Server:** `https://contacts.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Contacts.Api.Request.SetUserTagRequestModel` See [Roblox.Contacts.Api.Request.SetUserTagRequestModel](#roblox-contacts-api-request-setusertagrequestmodel) in Models. **Request example:** ```json { "targetUserId": 0, "userTag": "string" } ``` **Responses:** - `200`: OK → `Roblox.Contacts.Api.Response.SetUserTagResponseModel` - `400`: 2: The target user is invalid or does not exist. 4: Invalid parameters. 6: The userTag is too long. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 5: The user cannot tag themselves. - `429`: 10: The flood limit has been exceeded. **Response fields** (`Roblox.Contacts.Api.Response.SetUserTagResponseModel`) See [Roblox.Contacts.Api.Response.SetUserTagResponseModel](#roblox-contacts-api-response-setusertagresponsemodel) in Models. **Response example:** ```json { "status": "Success" } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://contacts.roblox.com/v1/user/tag" \ -H "Content-Type: application/json" \ -d '{ "targetUserId": 0, "userTag": "string" }' ``` ### GET `/v1/user/tag/validate` Validates the tag for a user **Server:** `https://contacts.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `alias` | query | `string` | No | The tag to validate | **Responses:** - `200`: OK → `Roblox.Contacts.Api.Response.ValidateUserTagResponseModel` - `400`: 4: Invalid parameters. - `401`: 0: Authorization has been denied for this request. - `429`: 10: The flood limit has been exceeded. **Response fields** (`Roblox.Contacts.Api.Response.ValidateUserTagResponseModel`) See [Roblox.Contacts.Api.Response.ValidateUserTagResponseModel](#roblox-contacts-api-response-validateusertagresponsemodel) in Models. **Response example:** ```json { "status": "Success" } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://contacts.roblox.com/v1/user/tag/validate" ``` ### GET `/v1/user/universes` Gets a list of universes for the authenticated user. **Server:** `https://develop.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `isArchived` | query | `boolean` | No | Whether or not to return archived games. | | `limit` | query | `10 \| 25 \| 50 \| 100` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `Asc \| Desc` | No | Sorted by universeId Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Api.Develop.Models.UniverseModel_` - `400`: cursor is not valid. - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Api.Develop.Models.UniverseModel_`) See [Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Api.Develop.Models.UniverseModel_](#roblox-web-webapi-models-apipageresponse-roblox-api-develop-models-universemodel-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "id": "...", "name": "...", "description": "...", "isArchived": "...", "rootPlaceId": "...", "isActive": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/user/universes" ``` ### POST `/v1/user/{userId}/multiget-are-friends` Check if the requesting user is friends with the specified users. **Server:** `https://friends.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The requesting userId. | **Request Body:** `application/json` — Type: `Roblox.Friends.Api.MultigetAreFriendsRequestModel` See [Roblox.Friends.Api.MultigetAreFriendsRequestModel](#roblox-friends-api-multigetarefriendsrequestmodel) in Models. **Request example:** ```json { "targetUserIds": [ 0 ] } ``` **Responses:** - `200`: OK → `Roblox.Friends.Api.MultigetAreFriendsResponse` - `400`: 1: The target user is invalid or does not exist. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Friends.Api.MultigetAreFriendsResponse`) See [Roblox.Friends.Api.MultigetAreFriendsResponse](#roblox-friends-api-multigetarefriendsresponse) in Models. **Response example:** ```json { "friendsId": [ 0 ] } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://friends.roblox.com/v1/user/{USERID}/multiget-are-friends" \ -H "Content-Type: application/json" \ -d '{ "targetUserIds": [ 0 ] }' ``` ### POST `/v1/usernames/users` Get users by usernames. This endpoint will also check previous usernames. Does not require X-CSRF-Token protection because this is essentially a get request but as a POST to avoid URI limits. **Server:** `https://users.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Users.Api.MultiGetByUsernameRequest` See [Roblox.Users.Api.MultiGetByUsernameRequest](#roblox-users-api-multigetbyusernamerequest) in Models. **Request example:** ```json { "usernames": [ "string" ], "excludeBannedUsers": false } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Users.Api.MultiGetUserByNameResponse_` - `400`: 2: Too many usernames. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Users.Api.MultiGetUserByNameResponse_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Users.Api.MultiGetUserByNameResponse_](#roblox-web-webapi-models-apiarrayresponse-roblox-users-api-multigetuserbynameresponse-) in Models. **Response example:** ```json { "data": [ { "requestedUsername": "...", "hasVerifiedBadge": "...", "id": "...", "name": "...", "displayName": "..." } ] } ``` **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://users.roblox.com/v1/usernames/users" \ -H "Content-Type: application/json" \ -d '{ "usernames": [ "string" ], "excludeBannedUsers": false }' ``` ### POST `/v1/users` Get users by ids. Does not require X-CSRF-Token protection because this is essentially a get request but as a POST to avoid URI limits. **Server:** `https://users.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Users.Api.MultiGetByUserIdRequest` See [Roblox.Users.Api.MultiGetByUserIdRequest](#roblox-users-api-multigetbyuseridrequest) in Models. **Request example:** ```json { "userIds": [ 0 ], "excludeBannedUsers": false } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Users.Api.MultiGetUserResponse_` - `400`: 1: Too many ids. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Users.Api.MultiGetUserResponse_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Users.Api.MultiGetUserResponse_](#roblox-web-webapi-models-apiarrayresponse-roblox-users-api-multigetuserresponse-) in Models. **Response example:** ```json { "data": [ { "hasVerifiedBadge": "...", "id": "...", "name": "...", "displayName": "..." } ] } ``` **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://users.roblox.com/v1/users" \ -H "Content-Type: application/json" \ -d '{ "userIds": [ 0 ], "excludeBannedUsers": false }' ``` ### GET `/v1/users/authenticated` Gets the minimal authenticated user. **Server:** `https://users.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Users.Api.AuthenticatedGetUserResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Users.Api.AuthenticatedGetUserResponse`) See [Roblox.Users.Api.AuthenticatedGetUserResponse](#roblox-users-api-authenticatedgetuserresponse) in Models. **Response example:** ```json { "id": 0, "name": "string", "displayName": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://users.roblox.com/v1/users/authenticated" ``` ### GET `/v1/users/authenticated/age-bracket` Gets the age bracket of the authenticated user. **Server:** `https://users.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Users.Api.UserAgeBracketResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Users.Api.UserAgeBracketResponse`) See [Roblox.Users.Api.UserAgeBracketResponse](#roblox-users-api-useragebracketresponse) in Models. **Response example:** ```json { "ageBracket": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://users.roblox.com/v1/users/authenticated/age-bracket" ``` ### GET `/v1/users/authenticated/country-code` Gets the country code of the authenticated user. **Server:** `https://users.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Users.Api.UserCountryCodeResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Users.Api.UserCountryCodeResponse`) See [Roblox.Users.Api.UserCountryCodeResponse](#roblox-users-api-usercountrycoderesponse) in Models. **Response example:** ```json { "countryCode": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://users.roblox.com/v1/users/authenticated/country-code" ``` ### GET `/v1/users/authenticated/roles` Gets the (public) roles of the authenticated user, such as `"Soothsayer"` and `"BetaTester"`. **Server:** `https://users.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Users.Api.UserRolesResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Users.Api.UserRolesResponse`) See [Roblox.Users.Api.UserRolesResponse](#roblox-users-api-userrolesresponse) in Models. **Response example:** ```json { "roles": [ "string" ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://users.roblox.com/v1/users/authenticated/roles" ``` ### GET `/v1/users/avatar` [STABLE] Get Avatar Full body shots for the given CSV of userIds **Server:** `https://thumbnails.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userIds` | query | `integer[]` | Yes | CSV for the userIds to get avatar full body shots | | `includeBackground` | query | `boolean` | No | Whether to include a background in the thumbnail (defaults to false) | | `size` | query | `string enum (14 values)` | No | The thumbnail size, formatted widthxheight Valid values: `30x30`, `48x48`, `60x60`, `75x75`, `100x100`, `110x110`, `140x140`, `150x150`, `150x200`, `180x180`, `250x250`, `352x352`, `420x420`, `720x720` | | `format` | query | `Png \| Jpeg \| Webp` | No | The thumbnail format Valid values: `Png`, `Jpeg`, `Webp` | | `isCircular` | query | `true \| false` | No | The circle thumbnail output parameter, true or false Valid values: `true`, `false` | **Responses:** - `200`: OK - `400`: 1: There are too many requested Ids. 2: The requested image format is invalid. Please see documentation for valid thumbnail format parameter name and values. 3: The requested size is invalid. Please see documentation for valid thumbnail size parameter name and format. 4: The requested Ids are invalid, of an invalid type or missing. 10: Circular thumbnail requests are not allowed **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://thumbnails.roblox.com/v1/users/avatar?userIds={VALUE}" ``` ### GET `/v1/users/avatar-3d` [BETA] Get Avatar 3d object for a user **Server:** `https://thumbnails.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `thumbnail:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | query | `integer` | Yes | user Id for avatar | **Responses:** - `200`: OK - `400`: 4: The requested Ids are invalid, of an invalid type or missing. **Rate Limits:** perApiKeyOwner: 500/minute, perOauth2Authorization: 500/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://thumbnails.roblox.com/v1/users/avatar-3d?userId={VALUE}" ``` ### GET `/v1/users/avatar-bust` [STABLE] Get Avatar Busts for the given CSV of userIds **Server:** `https://thumbnails.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userIds` | query | `integer[]` | Yes | CSV for the userIds to get avatar headshots | | `includeBackground` | query | `boolean` | No | Whether to include a background in the thumbnail (defaults to false) | | `size` | query | `string enum (9 values)` | No | The thumbnail size, formatted widthxheight Valid values: `48x48`, `50x50`, `60x60`, `75x75`, `100x100`, `150x150`, `180x180`, `352x352`, `420x420` | | `format` | query | `Png \| Webp` | No | The thumbnail format Valid values: `Png`, `Webp` | | `isCircular` | query | `true \| false` | No | The circle thumbnail output parameter, true or false Valid values: `true`, `false` | **Responses:** - `200`: OK - `400`: 1: There are too many requested Ids. 2: The requested image format is invalid. Please see documentation for valid thumbnail format parameter name and values. 3: The requested size is invalid. Please see documentation for valid thumbnail size parameter name and format. 4: The requested Ids are invalid, of an invalid type or missing. 10: Circular thumbnail requests are not allowed **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://thumbnails.roblox.com/v1/users/avatar-bust?userIds={VALUE}" ``` ### GET `/v1/users/avatar-headshot` [STABLE] Get Avatar Headshots for the given CSV of userIds **Server:** `https://thumbnails.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userIds` | query | `integer[]` | Yes | CSV for the userIds to get avatar headshots | | `includeBackground` | query | `boolean` | No | Whether to include a background in the thumbnail (defaults to false) | | `size` | query | `string enum (11 values)` | No | The thumbnail size, formatted widthxheight Valid values: `48x48`, `50x50`, `60x60`, `75x75`, `100x100`, `110x110`, `150x150`, `180x180`, `352x352`, `420x420`, `720x720` | | `format` | query | `Png \| Jpeg \| Webp` | No | The thumbnail format Valid values: `Png`, `Jpeg`, `Webp` | | `isCircular` | query | `true \| false` | No | The circle thumbnail output parameter, true or false Valid values: `true`, `false` | **Responses:** - `200`: OK - `400`: 1: There are too many requested Ids. 2: The requested image format is invalid. Please see documentation for valid thumbnail format parameter name and values. 3: The requested size is invalid. Please see documentation for valid thumbnail size parameter name and format. 4: The requested Ids are invalid, of an invalid type or missing. 10: Circular thumbnail requests are not allowed **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://thumbnails.roblox.com/v1/users/avatar-headshot?userIds={VALUE}" ``` ### GET `/v1/users/outfit-3d` [BETA] Get 3d object for an outfit **Server:** `https://thumbnails.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `thumbnail:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `outfitId` | query | `integer` | Yes | CSV for the userIds to get user outfits | **Responses:** - `200`: OK **Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://thumbnails.roblox.com/v1/users/outfit-3d?outfitId={VALUE}" ``` ### GET `/v1/users/outfits` [STABLE] Get outfits for the given CSV of userOutfitIds **Server:** `https://thumbnails.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userOutfitIds` | query | `integer[]` | Yes | CSV for the userOutfitIds to get user outfits | | `includeBackground` | query | `boolean` | No | Whether to include a background in the thumbnail (defaults to false) | | `size` | query | `150x150 \| 420x420` | No | The thumbnail size, formatted widthxheight Valid values: `150x150`, `420x420` | | `format` | query | `Png \| Webp` | No | The thumbnail format Valid values: `Png`, `Webp` | | `isCircular` | query | `true \| false` | No | The circle thumbnail output parameter, true or false Valid values: `true`, `false` | **Responses:** - `200`: OK - `400`: 1: There are too many requested Ids. 2: The requested image format is invalid. Please see documentation for valid thumbnail format parameter name and values. 3: The requested size is invalid. Please see documentation for valid thumbnail size parameter name and format. 4: The requested Ids are invalid, of an invalid type or missing. 10: Circular thumbnail requests are not allowed **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://thumbnails.roblox.com/v1/users/outfits?userOutfitIds={VALUE}" ``` ### GET `/v1/users/search` Searches for users by keyword. **Server:** `https://users.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `keyword` | query | `string` | Yes | The search keyword. | | `sessionId` | query | `string` | No | | | `limit` | query | `10 \| 25 \| 50 \| 100` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Users.Api.SearchGetUserResponse_` - `400`: 5: The keyword was filtered. 6: The keyword is too short. - `429`: 4: Too many requests. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Users.Api.SearchGetUserResponse_`) See [Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Users.Api.SearchGetUserResponse_](#roblox-web-webapi-models-apipageresponse-roblox-users-api-searchgetuserresponse-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "previousUsernames": "...", "hasVerifiedBadge": "...", "id": "...", "name": "...", "displayName": "..." } ] } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://users.roblox.com/v1/users/search?keyword={VALUE}" ``` ### POST `/v1/users/{targetUserId}/follow` Creates the following between a user and user with targetUserId **Server:** `https://friends.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `targetUserId` | path | `integer` | Yes | | **Request Body:** `application/json` — Type: `Roblox.Web.Captcha.Models.Request.CaptchaTokenRequest` See [Roblox.Web.Captcha.Models.Request.CaptchaTokenRequest](#roblox-web-captcha-models-request-captchatokenrequest) in Models. **Request example:** ```json { "captchaId": "string", "captchaToken": "string", "captchaProvider": "string", "challengeId": "string" } ``` **Responses:** - `200`: OK → `Roblox.Friends.Api.CaptchaStatusResponseModel` - `400`: 1: The target user is invalid or does not exist. 6: Invalid parameters. 8: The user cannot follow itself. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: The user is banned from performing operation. 3: The user is blocked from performing this action. 14: The user has not passed the captcha. - `429`: 9: The flood limit has been exceeded. **Response fields** (`Roblox.Friends.Api.CaptchaStatusResponseModel`) See [Roblox.Friends.Api.CaptchaStatusResponseModel](#roblox-friends-api-captchastatusresponsemodel) in Models. **Response example:** ```json { "success": false, "isCaptchaRequired": false } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://friends.roblox.com/v1/users/{TARGETUSERID}/follow" \ -H "Content-Type: application/json" \ -d '{ "captchaId": "string", "captchaToken": "string", "captchaProvider": "string", "challengeId": "string" }' ``` ### GET `/v1/users/{targetUserId}/followers` Get all users that follow user with targetUserId in page response format **Server:** `https://friends.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `targetUserId` | path | `integer` | Yes | | | `limit` | query | `10 \| 18 \| 25 \| 50 \| 100` | No | The number of results per request. Valid values: `10`, `18`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `Asc \| Desc` | No | The order the results are sorted in. Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Friends.Api.Models.Response.UserResponse_` - `400`: 1: The target user is invalid or does not exist. 6: Invalid parameters. - `403`: 2: The user is banned from performing operation. 3: The user is blocked from performing this action. - `429`: 9: The flood limit has been exceeded. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Friends.Api.Models.Response.UserResponse_`) See [Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Friends.Api.Models.Response.UserResponse_](#roblox-web-webapi-models-apipageresponse-roblox-friends-api-models-response-userresponse-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "isOnline": "...", "presenceType": "...", "isDeleted": "...", "friendFrequentScore": "...", "friendFrequentRank": "...", "hasVerifiedBadge": "..." } ] } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://friends.roblox.com/v1/users/{TARGETUSERID}/followers" ``` ### GET `/v1/users/{targetUserId}/followers/count` Get the number of following a user has **Server:** `https://friends.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `targetUserId` | path | `integer` | Yes | | **Responses:** - `200`: OK → `Roblox.Friends.Api.FollowCountResponse` - `400`: 1: The target user is invalid or does not exist. **Response fields** (`Roblox.Friends.Api.FollowCountResponse`) See [Roblox.Friends.Api.FollowCountResponse](#roblox-friends-api-followcountresponse) in Models. **Response example:** ```json { "count": 0 } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://friends.roblox.com/v1/users/{TARGETUSERID}/followers/count" ``` ### GET `/v1/users/{targetUserId}/followings` Get all users that user with targetUserId is following in page response format **Server:** `https://friends.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `targetUserId` | path | `integer` | Yes | | | `limit` | query | `10 \| 18 \| 25 \| 50 \| 100` | No | The number of results per request. Valid values: `10`, `18`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `Asc \| Desc` | No | The order the results are sorted in. Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Friends.Api.Models.Response.UserResponse_` - `400`: 1: The target user is invalid or does not exist. 6: Invalid parameters. - `403`: 2: The user is banned from performing operation. 3: The user is blocked from performing this action. - `429`: 9: The flood limit has been exceeded. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Friends.Api.Models.Response.UserResponse_`) See [Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Friends.Api.Models.Response.UserResponse_](#roblox-web-webapi-models-apipageresponse-roblox-friends-api-models-response-userresponse-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "isOnline": "...", "presenceType": "...", "isDeleted": "...", "friendFrequentScore": "...", "friendFrequentRank": "...", "hasVerifiedBadge": "..." } ] } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://friends.roblox.com/v1/users/{TARGETUSERID}/followings" ``` ### GET `/v1/users/{targetUserId}/followings/count` Get the number of following a user has **Server:** `https://friends.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `targetUserId` | path | `integer` | Yes | | **Responses:** - `200`: OK → `Roblox.Friends.Api.FollowCountResponse` - `400`: 1: The target user is invalid or does not exist. **Response fields** (`Roblox.Friends.Api.FollowCountResponse`) See [Roblox.Friends.Api.FollowCountResponse](#roblox-friends-api-followcountresponse) in Models. **Response example:** ```json { "count": 0 } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://friends.roblox.com/v1/users/{TARGETUSERID}/followings/count" ``` ### POST `/v1/users/{targetUserId}/unfollow` Deletes the following between a user and user with targetUserId **Server:** `https://friends.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `targetUserId` | path | `integer` | Yes | | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: The target user is invalid or does not exist. 6: Invalid parameters. 8: The user cannot follow itself. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: The user is banned from performing operation. 3: The user is blocked from performing this action. 14: The user has not passed the captcha. - `429`: 9: The flood limit has been exceeded. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://friends.roblox.com/v1/users/{TARGETUSERID}/unfollow" ``` ### GET `/v1/users/{userId}` Gets detailed user information by id. **Server:** `https://users.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user id. | **Responses:** - `200`: OK → `Roblox.Users.Api.GetUserResponse` - `404`: 3: The user id is invalid. **Response fields** (`Roblox.Users.Api.GetUserResponse`) See [Roblox.Users.Api.GetUserResponse](#roblox-users-api-getuserresponse) in Models. **Response example:** ```json { "description": "string", "created": "2024-01-01T00:00:00Z", "isBanned": false, "externalAppDisplayName": "string", "hasVerifiedBadge": false, "id": 0 } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://users.roblox.com/v1/users/{USERID}" ``` ### GET `/v1/users/{userId}/assets/collectibles` Gets all collectible assets owned by the specified user. **Server:** `https://inventory.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The userid of the owner of the collectibles. | | `assetType` | query | `integer enum (85 values)` | No | The asset type for the collectibles you're trying to get. Valid values: `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`, `10`, `11`, `12`, `13`, `16`, `17`, `18`, `19`, `21`, `22`, `24`, `25`, `26`, `27`, `28`, `29`, `30`, `31`, `32`, `33`, `34`, `35`, `37`, `38`, `39`, `40`, `41`, `42`, `43`, `44`, `45`, `46`, `47`, `48`, `49`, `50`, `51`, `52`, `53`, `54`, `55`, `56`, `59`, `60`, `61`, `62`, `63`, `64`, `65`, `66`, `67`, `68`, `69`, `70`, `71`, `72`, `73`, `74`, `75`, `76`, `77`, `78`, `79`, `80`, `81`, `82`, `83`, `84`, `85`, `86`, `87`, `88`, `89`, `90`, `91`, `92` | | `limit` | query | `10 \| 25 \| 50 \| 100` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `Asc \| Desc` | No | Sorted by userAssetId Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Inventory.Api.Models.CollectibleUserAssetModel_` - `400`: The specified asset type(s) are invalid. - `403`: The specified user's inventory is hidden. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Inventory.Api.Models.CollectibleUserAssetModel_`) See [Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Inventory.Api.Models.CollectibleUserAssetModel_](#roblox-web-webapi-models-apipageresponse-roblox-inventory-api-models-collectibleuserassetmodel-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "userAssetId": "...", "serialNumber": "...", "assetId": "...", "name": "...", "recentAveragePrice": "...", "originalPrice": "..." } ] } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://inventory.roblox.com/v1/users/{USERID}/assets/collectibles" ``` ### GET `/v1/users/{userId}/avatar` Returns details about a specified user's avatar. Includes assets, bodycolors, and playerAvatarType. **Server:** `https://avatar.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | | | `Roblox-Place-Id` | header | `integer` | No | | **Responses:** - `200`: OK → `Roblox.Api.Avatar.Models.AvatarModelV2` - `400`: 1: The specified user does not exist. 2: An account for the given userId does not exist! **Response fields** (`Roblox.Api.Avatar.Models.AvatarModelV2`) See [Roblox.Api.Avatar.Models.AvatarModelV2](#roblox-api-avatar-models-avatarmodelv2) in Models. **Response example:** ```json { "scales": { "height": 0, "width": 0, "head": 0, "depth": 0, "proportion": 0, "bodyType": 0 }, "playerAvatarType": 1, "bodyColors": { "headColorId": 0, "torsoColorId": 0, "rightArmColorId": 0, "leftArmColorId": 0, "rightLegColorId": 0, "leftLegColorId": 0 }, "assets": [ { "id": "...", "name": "...", "assetType": "...", "currentVersionId": "...", "meta": "...", "availabilityStatus": "..." } ], "defaultShirtApplied": false, "defaultPantsApplied": false } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://avatar.roblox.com/v1/users/{USERID}/avatar" ``` ### GET `/v1/users/{userId}/badges` Gets a list of badges a user has been awarded. **Server:** `https://badges.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user Id. | | `limit` | query | `10 \| 25 \| 50 \| 100` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `Asc \| Desc` | No | The order the results are sorted in. Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Badges.Api.GetBadgesByUserResponse_` - `404`: 4: User is invalid or does not exist. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Badges.Api.GetBadgesByUserResponse_`) See [Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Badges.Api.GetBadgesByUserResponse_](#roblox-web-webapi-models-apipageresponse-roblox-badges-api-getbadgesbyuserresponse-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "creator": "...", "id": "...", "name": "...", "description": "...", "displayName": "...", "displayDescription": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://badges.roblox.com/v1/users/{USERID}/badges" ``` ### GET `/v1/users/{userId}/badges/awarded-dates` Gets timestamps for when badges were awarded to a user. **Server:** `https://badges.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user Id. | | `badgeIds` | query | `integer[]` | Yes | The CSV of badge Ids. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Badges.Api.BadgeAwardResponse_` - `400`: 5: Too many badge Ids. - `404`: 4: User is invalid or does not exist. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Badges.Api.BadgeAwardResponse_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Badges.Api.BadgeAwardResponse_](#roblox-web-webapi-models-apiarrayresponse-roblox-badges-api-badgeawardresponse-) in Models. **Response example:** ```json { "data": [ { "badgeId": "...", "awardedDate": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://badges.roblox.com/v1/users/{USERID}/badges/awarded-dates?badgeIds={VALUE}" ``` ### GET `/v1/users/{userId}/badges/{badgeId}/awarded-date` Gets timestamp for when a single badge was awarded to a user. **Server:** `https://badges.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | User id. | | `badgeId` | path | `integer` | Yes | Badge id. | **Responses:** - `200`: OK - `404`: 4: User is invalid or does not exist. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://badges.roblox.com/v1/users/{USERID}/badges/{BADGEID}/awarded-date" ``` ### GET `/v1/users/{userId}/bundles` Lists the bundles owned by a given user. **Server:** `https://catalog.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | | | `cursor` | query | `string` | No | | | `limit` | query | `integer` | No | | | `sortOrder` | query | `1 \| 2` | No | Valid values: `1`, `2` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Catalog.Api.OwnedBundleModel_` - `400`: 1: Invalid bundle **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Catalog.Api.OwnedBundleModel_`) See [Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Catalog.Api.OwnedBundleModel_](#roblox-web-webapi-models-apipageresponse-roblox-catalog-api-ownedbundlemodel-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "id": "...", "name": "...", "bundleType": "...", "creator": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://catalog.roblox.com/v1/users/{USERID}/bundles" ``` ### GET `/v1/users/{userId}/bundles/{bundleType}` **Server:** `https://catalog.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | | | `bundleType` | path | `1 \| 2 \| 3 \| 4` | Yes | Valid values: `1`, `2`, `3`, `4` | | `cursor` | query | `string` | Yes | | | `limit` | query | `integer` | No | | | `sortOrder` | query | `1 \| 2` | No | Valid values: `1`, `2` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Catalog.Api.OwnedBundleModel_` **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Catalog.Api.OwnedBundleModel_`) See [Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Catalog.Api.OwnedBundleModel_](#roblox-web-webapi-models-apipageresponse-roblox-catalog-api-ownedbundlemodel-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "id": "...", "name": "...", "bundleType": "...", "creator": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://catalog.roblox.com/v1/users/{USERID}/bundles/{BUNDLETYPE}?cursor={VALUE}" ``` ### GET `/v1/users/{userId}/can-trade-with` Returns whether you can trade with another user. **Server:** `https://trades.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The other user's id. | **Responses:** - `200`: OK → `Roblox.Trades.Api.CanTradeResponse` - `400`: 10: Invalid trade partner. See field for whether the invalid partner is the sender or receiver. - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Trades.Api.CanTradeResponse`) See [Roblox.Trades.Api.CanTradeResponse](#roblox-trades-api-cantraderesponse) in Models. **Response example:** ```json { "canTrade": false, "status": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://trades.roblox.com/v1/users/{USERID}/can-trade-with" ``` ### GET `/v1/users/{userId}/can-view-inventory` Gets whether the specified user's inventory can be viewed. **Server:** `https://inventory.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user identifier. | **Responses:** - `200`: OK → `Roblox.Inventory.Api.Models.CanViewInventoryResponse` - `400`: 1: The specified user does not exist! **Response fields** (`Roblox.Inventory.Api.Models.CanViewInventoryResponse`) See [Roblox.Inventory.Api.Models.CanViewInventoryResponse](#roblox-inventory-api-models-canviewinventoryresponse) in Models. **Response example:** ```json { "canView": false } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://inventory.roblox.com/v1/users/{USERID}/can-view-inventory" ``` ### GET `/v1/users/{userId}/categories` Return inventory categories for a user **Server:** `https://inventory.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | | **Responses:** - `200`: OK → `Roblox.Inventory.Api.Models.CategoriesModel` **Response fields** (`Roblox.Inventory.Api.Models.CategoriesModel`) See [Roblox.Inventory.Api.Models.CategoriesModel](#roblox-inventory-api-models-categoriesmodel) in Models. **Response example:** ```json { "categories": [ { "name": "...", "displayName": "...", "categoryType": "...", "items": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://inventory.roblox.com/v1/users/{USERID}/categories" ``` ### GET `/v1/users/{userId}/categories/favorites` Return favorites categories for a user **Server:** `https://inventory.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | | **Responses:** - `200`: OK → `Roblox.Inventory.Api.Models.CategoriesModel` **Response fields** (`Roblox.Inventory.Api.Models.CategoriesModel`) See [Roblox.Inventory.Api.Models.CategoriesModel](#roblox-inventory-api-models-categoriesmodel) in Models. **Response example:** ```json { "categories": [ { "name": "...", "displayName": "...", "categoryType": "...", "items": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://inventory.roblox.com/v1/users/{USERID}/categories/favorites" ``` ### POST `/v1/users/{userId}/challenges/authenticator/verify` Verifies a two step verification challenge code via authenticator app. **Server:** `https://twostepverification.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user ID. | **Responses:** - `200`: OK → `Roblox.TwoStepVerification.Api.VerifyCodeResponse` - `400`: 1: Invalid challenge ID. 2: The user ID is invalid. 10: The two step verification challenge code is invalid. - `403`: 0: Token Validation Failed - `429`: 5: Too many requests. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.TwoStepVerification.Api.VerifyCodeResponse`) See [Roblox.TwoStepVerification.Api.VerifyCodeResponse](#roblox-twostepverification-api-verifycoderesponse) in Models. **Response example:** ```json { "verificationToken": "string" } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/challenges/authenticator/verify" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### POST `/v1/users/{userId}/challenges/cross-device/retract` Reverts a user's dialog state from ACTIVE to PENDING. **Server:** `https://twostepverification.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user ID. | **Request Body:** `application/json` — Type: `Roblox.TwoStepVerification.Api.RetractDialogRequest` See [Roblox.TwoStepVerification.Api.RetractDialogRequest](#roblox-twostepverification-api-retractdialogrequest) in Models. **Request example:** ```json { "challengeId": "string", "actionType": 0 } ``` **Responses:** - `200`: OK → `Roblox.TwoStepVerification.Api.RetractDialogResponse` - `400`: 1: Invalid challenge ID. 2: The user ID is invalid. - `403`: 0: Token Validation Failed 19: Challenge denied. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.TwoStepVerification.Api.RetractDialogResponse`) See [Roblox.TwoStepVerification.Api.RetractDialogResponse](#roblox-twostepverification-api-retractdialogresponse) in Models. **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/challenges/cross-device/retract" \ -H "Content-Type: application/json" \ -d '{ "challengeId": "string", "actionType": 0 }' ``` ### POST `/v1/users/{userId}/challenges/cross-device/retry` Retry a Cross Device two step verification approval. **Server:** `https://twostepverification.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user ID. | **Request Body:** `application/json` — Type: `Roblox.TwoStepVerification.Api.RetryApprovalRequest` See [Roblox.TwoStepVerification.Api.RetryApprovalRequest](#roblox-twostepverification-api-retryapprovalrequest) in Models. **Request example:** ```json { "challengeId": "string", "actionType": 0 } ``` **Responses:** - `200`: OK → `Roblox.TwoStepVerification.Api.RetryApprovalResponse` - `400`: 1: Invalid challenge ID. 2: The user ID is invalid. - `403`: 0: Token Validation Failed 19: Challenge denied. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.TwoStepVerification.Api.RetryApprovalResponse`) See [Roblox.TwoStepVerification.Api.RetryApprovalResponse](#roblox-twostepverification-api-retryapprovalresponse) in Models. **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/challenges/cross-device/retry" \ -H "Content-Type: application/json" \ -d '{ "challengeId": "string", "actionType": 0 }' ``` ### POST `/v1/users/{userId}/challenges/cross-device/verify` Verifies a two step verification approval via Cross Device. Cross Device approval does not use a verification code. **Server:** `https://twostepverification.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user ID. | **Request Body:** `application/json` — Type: `Roblox.TwoStepVerification.Api.VerifyApprovalRequest` See [Roblox.TwoStepVerification.Api.VerifyApprovalRequest](#roblox-twostepverification-api-verifyapprovalrequest) in Models. **Request example:** ```json { "challengeId": "string", "actionType": 0 } ``` **Responses:** - `200`: OK → `Roblox.TwoStepVerification.Api.VerifyApprovalResponse` - `400`: 0: An unknown error occurred with the request. 1: Invalid challenge ID. 2: The user ID is invalid. - `403`: 0: Token Validation Failed 19: Challenge denied. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.TwoStepVerification.Api.VerifyApprovalResponse`) See [Roblox.TwoStepVerification.Api.VerifyApprovalResponse](#roblox-twostepverification-api-verifyapprovalresponse) in Models. **Response example:** ```json { "verificationToken": "string" } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/challenges/cross-device/verify" \ -H "Content-Type: application/json" \ -d '{ "challengeId": "string", "actionType": 0 }' ``` ### POST `/v1/users/{userId}/challenges/email/send-code` Sends a two step verification challenge code via email. **Server:** `https://twostepverification.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user ID. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: Invalid challenge ID. - `403`: 0: Token Validation Failed 2: The user ID is invalid. - `429`: 5: Too many requests. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/challenges/email/send-code" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### POST `/v1/users/{userId}/challenges/email/verify` Verifies a two step verification challenge with a code sent via email. **Server:** `https://twostepverification.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user ID. | **Responses:** - `200`: OK → `Roblox.TwoStepVerification.Api.VerifyCodeResponse` - `400`: 1: Invalid challenge ID. 10: The two step verification challenge code is invalid. - `403`: 0: Token Validation Failed 2: The user ID is invalid. - `429`: 5: Too many requests. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.TwoStepVerification.Api.VerifyCodeResponse`) See [Roblox.TwoStepVerification.Api.VerifyCodeResponse](#roblox-twostepverification-api-verifycoderesponse) in Models. **Response example:** ```json { "verificationToken": "string" } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/challenges/email/verify" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### POST `/v1/users/{userId}/challenges/passkey/verify-finish` Validates the assertion data returned by the passkey. **Server:** `https://twostepverification.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user ID. | **Responses:** - `200`: OK → `Roblox.TwoStepVerification.Api.VerifyCodeResponse` - `400`: 1: Invalid challenge ID. 2: The user ID is invalid. 10: The two step verification challenge code is invalid. - `403`: 0: Token Validation Failed - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.TwoStepVerification.Api.VerifyCodeResponse`) See [Roblox.TwoStepVerification.Api.VerifyCodeResponse](#roblox-twostepverification-api-verifycoderesponse) in Models. **Response example:** ```json { "verificationToken": "string" } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/challenges/passkey/verify-finish" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### POST `/v1/users/{userId}/challenges/passkey/verify-start` Provides a challenge for the passkey to authenticate. **Server:** `https://twostepverification.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user ID. | **Responses:** - `200`: OK → `Roblox.TwoStepVerification.Api.VerifyStartPasskeyResponse` - `400`: 1: Invalid challenge ID. 2: The user ID is invalid. - `403`: 0: Token Validation Failed 8: The user is not allowed to perform the requested action. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.TwoStepVerification.Api.VerifyStartPasskeyResponse`) See [Roblox.TwoStepVerification.Api.VerifyStartPasskeyResponse](#roblox-twostepverification-api-verifystartpasskeyresponse) in Models. **Response example:** ```json { "authenticationOptions": "string", "sessionId": "string" } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/challenges/passkey/verify-start" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### POST `/v1/users/{userId}/challenges/password/verify` Verifies a two step verification challenge with a password (code). **Server:** `https://twostepverification.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user ID. | **Responses:** - `200`: OK → `Roblox.TwoStepVerification.Api.VerifyCodeResponse` - `400`: 1: Invalid challenge ID. 4: The password is invalid. - `403`: 0: Token Validation Failed 2: The user ID is invalid. - `429`: 5: Too many requests. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.TwoStepVerification.Api.VerifyCodeResponse`) See [Roblox.TwoStepVerification.Api.VerifyCodeResponse](#roblox-twostepverification-api-verifycoderesponse) in Models. **Response example:** ```json { "verificationToken": "string" } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/challenges/password/verify" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### POST `/v1/users/{userId}/challenges/recovery-codes/verify` Verifies a two step verification challenge via a recovery code. Once a recovery code has been used to verify a challenge it cannot be used again. **Server:** `https://twostepverification.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user ID. | **Responses:** - `200`: OK → `Roblox.TwoStepVerification.Api.VerifyCodeResponse` - `400`: 1: Invalid challenge ID. 2: The user ID is invalid. 10: The two step verification challenge code is invalid. - `403`: 0: Token Validation Failed - `429`: 5: Too many requests. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.TwoStepVerification.Api.VerifyCodeResponse`) See [Roblox.TwoStepVerification.Api.VerifyCodeResponse](#roblox-twostepverification-api-verifycoderesponse) in Models. **Response example:** ```json { "verificationToken": "string" } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/challenges/recovery-codes/verify" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### POST `/v1/users/{userId}/challenges/security-key/verify-finish` Validates the assertion data returned by the security key. **Server:** `https://twostepverification.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user ID. | **Responses:** - `200`: OK → `Roblox.TwoStepVerification.Api.VerifyCodeResponse` - `400`: 1: Invalid challenge ID. 2: The user ID is invalid. 10: The two step verification challenge code is invalid. - `403`: 0: Token Validation Failed - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.TwoStepVerification.Api.VerifyCodeResponse`) See [Roblox.TwoStepVerification.Api.VerifyCodeResponse](#roblox-twostepverification-api-verifycoderesponse) in Models. **Response example:** ```json { "verificationToken": "string" } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/challenges/security-key/verify-finish" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### POST `/v1/users/{userId}/challenges/security-key/verify-start` Provides a challenge for the security key to authenticate. **Server:** `https://twostepverification.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user ID. | **Responses:** - `200`: OK → `Roblox.TwoStepVerification.Api.VerifyStartSecurityKeyResponse` - `400`: 1: Invalid challenge ID. 2: The user ID is invalid. - `403`: 0: Token Validation Failed 8: The user is not allowed to perform the requested action. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.TwoStepVerification.Api.VerifyStartSecurityKeyResponse`) See [Roblox.TwoStepVerification.Api.VerifyStartSecurityKeyResponse](#roblox-twostepverification-api-verifystartsecuritykeyresponse) in Models. **Response example:** ```json { "authenticationOptions": "string", "sessionId": "string" } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/challenges/security-key/verify-start" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### POST `/v1/users/{userId}/challenges/sms/send-code` Sends a two step verification code via SMS for the specified user. **Server:** `https://twostepverification.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user ID. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: Invalid challenge ID. - `403`: 0: Token Validation Failed 2: The user ID is invalid. - `429`: 5: Too many requests. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/challenges/sms/send-code" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### POST `/v1/users/{userId}/challenges/sms/verify` Verifies a two step verification challenge with a code sent via SMS. **Server:** `https://twostepverification.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user ID. | **Responses:** - `200`: OK → `Roblox.TwoStepVerification.Api.VerifyCodeResponse` - `400`: 1: Invalid challenge ID. 2: The user ID is invalid. 10: The two step verification challenge code is invalid. - `403`: 0: Token Validation Failed - `429`: 5: Too many requests. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.TwoStepVerification.Api.VerifyCodeResponse`) See [Roblox.TwoStepVerification.Api.VerifyCodeResponse](#roblox-twostepverification-api-verifycoderesponse) in Models. **Response example:** ```json { "verificationToken": "string" } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/challenges/sms/verify" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### GET `/v1/users/{userId}/configuration` Gets two step verification configuration for the specified user. **Server:** `https://twostepverification.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The Id of the user to get the configuration for. | | `challengeId` | query | `string` | No | The active challenge for the user (as an alternative when the user is unauthenticated). | | `actionType` | query | `integer enum (9 values)` | No | The action type the challengeId is associated with. Valid values: `0`, `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8` | **Responses:** - `200`: OK → `Roblox.TwoStepVerification.Api.UserConfiguration` - `400`: 1: Invalid challenge ID. - `403`: 2: The user ID is invalid. **Response fields** (`Roblox.TwoStepVerification.Api.UserConfiguration`) See [Roblox.TwoStepVerification.Api.UserConfiguration](#roblox-twostepverification-api-userconfiguration) in Models. **Response example:** ```json { "primaryMediaType": 0, "methods": [ { "mediaType": "...", "enabled": "...", "updated": "..." } ] } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/configuration" ``` ### POST `/v1/users/{userId}/configuration/authenticator/disable` Disables two step verification via authenticator for the specified user. **Server:** `https://twostepverification.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user ID. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 2: The user ID is invalid. 4: The password is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 8: The user is not allowed to perform the requested action. - `429`: 5: Too many requests. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/configuration/authenticator/disable" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### POST `/v1/users/{userId}/configuration/authenticator/enable` Initiates enabling authenticator-based two step verification for the specified user. **Server:** `https://twostepverification.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user ID. | **Request Body:** `application/json` — Type: `Roblox.TwoStepVerification.Api.EnableTwoStepVerificationRequest` See [Roblox.TwoStepVerification.Api.EnableTwoStepVerificationRequest](#roblox-twostepverification-api-enabletwostepverificationrequest) in Models. **Request example:** ```json { "password": "string", "secureAuthenticationIntent": { "clientPublicKey": "string", "clientEpochTimestamp": 0, "saiSignature": "string", "serverNonce": "string" } } ``` **Responses:** - `200`: OK → `Roblox.TwoStepVerification.Api.EnableAuthenticatorResponse` - `400`: 2: The user ID is invalid. 3: The email is invalid. 4: The password is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 11: The two step verification configuration is already enabled. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.TwoStepVerification.Api.EnableAuthenticatorResponse`) See [Roblox.TwoStepVerification.Api.EnableAuthenticatorResponse](#roblox-twostepverification-api-enableauthenticatorresponse) in Models. **Response example:** ```json { "setupToken": "string", "qrCodeImageUrl": "string", "manualEntryKey": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/configuration/authenticator/enable" \ -H "Content-Type: application/json" \ -d '{ "password": "string", "secureAuthenticationIntent": { "clientPublicKey": "string", "clientEpochTimestamp": 0, "saiSignature": "string", "serverNonce": "string" } }' ``` ### POST `/v1/users/{userId}/configuration/authenticator/enable-verify` Finishes enabling authenticator-based two step verification for the specified user. Enabling authenticator-based two step verification requires two parts to help ensure the user has properly stored the authenticator key in their authenticator app. **Server:** `https://twostepverification.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user ID. | **Request Body:** `application/json` — Type: `Roblox.TwoStepVerification.Api.EnableVerifyAuthenticatorRequest` See [Roblox.TwoStepVerification.Api.EnableVerifyAuthenticatorRequest](#roblox-twostepverification-api-enableverifyauthenticatorrequest) in Models. **Request example:** ```json { "setupToken": "string", "code": "string", "password": "string", "secureAuthenticationIntent": { "clientPublicKey": "string", "clientEpochTimestamp": 0, "saiSignature": "string", "serverNonce": "string" } } ``` **Responses:** - `200`: OK → `Roblox.TwoStepVerification.Api.EnableVerifyAuthenticatorResponse` - `400`: 2: The user ID is invalid. 4: The password is invalid. 10: The two step verification challenge code is invalid. 12: Invalid setup token. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 11: The two step verification configuration is already enabled. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.TwoStepVerification.Api.EnableVerifyAuthenticatorResponse`) See [Roblox.TwoStepVerification.Api.EnableVerifyAuthenticatorResponse](#roblox-twostepverification-api-enableverifyauthenticatorresponse) in Models. **Response example:** ```json { "recoveryCodes": [ "string" ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/configuration/authenticator/enable-verify" \ -H "Content-Type: application/json" \ -d '{ "setupToken": "string", "code": "string", "password": "string", "secureAuthenticationIntent": { "clientPublicKey": "string", "clientEpochTimestamp": 0, "saiSignature": "string", "serverNonce": "string" } }' ``` ### POST `/v1/users/{userId}/configuration/email/disable` Disables two step verification via email for the specified user. **Server:** `https://twostepverification.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user ID. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 4: The password is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: The user ID is invalid. 8: The user is not allowed to perform the requested action. - `429`: 5: Too many requests. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/configuration/email/disable" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### POST `/v1/users/{userId}/configuration/email/enable` Enables two step verification via email for the specified user. **Server:** `https://twostepverification.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user ID. | **Request Body:** `application/json` — Type: `Roblox.TwoStepVerification.Api.EnableTwoStepVerificationRequest` See [Roblox.TwoStepVerification.Api.EnableTwoStepVerificationRequest](#roblox-twostepverification-api-enabletwostepverificationrequest) in Models. **Request example:** ```json { "password": "string", "secureAuthenticationIntent": { "clientPublicKey": "string", "clientEpochTimestamp": 0, "saiSignature": "string", "serverNonce": "string" } } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 3: The email is invalid. 4: The password is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: The user ID is invalid. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/configuration/email/enable" \ -H "Content-Type: application/json" \ -d '{ "password": "string", "secureAuthenticationIntent": { "clientPublicKey": "string", "clientEpochTimestamp": 0, "saiSignature": "string", "serverNonce": "string" } }' ``` ### POST `/v1/users/{userId}/configuration/security-key/disable` Disables a batch of credentials for the specified user. **Server:** `https://twostepverification.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user ID. | **Request Body:** `application/json` — Type: `Roblox.TwoStepVerification.Api.DisableSecurityKeyRequest` See [Roblox.TwoStepVerification.Api.DisableSecurityKeyRequest](#roblox-twostepverification-api-disablesecuritykeyrequest) in Models. **Request example:** ```json { "credentialNicknames": [ "string" ] } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 4: The password is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 8: The user is not allowed to perform the requested action. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/configuration/security-key/disable" \ -H "Content-Type: application/json" \ -d '{ "credentialNicknames": [ "string" ] }' ``` ### POST `/v1/users/{userId}/configuration/security-key/enable` Initiates security key registration by providing credential creation options. **Server:** `https://twostepverification.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user ID. | **Responses:** - `200`: OK → `Roblox.TwoStepVerification.Api.EnableSecurityKeyResponse` - `400`: 2: The user ID is invalid. 4: The password is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 9: The two step verification configuration is invalid for this action. 16: Reached limit of security keys registered. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.TwoStepVerification.Api.EnableSecurityKeyResponse`) See [Roblox.TwoStepVerification.Api.EnableSecurityKeyResponse](#roblox-twostepverification-api-enablesecuritykeyresponse) in Models. **Response example:** ```json { "creationOptions": "string", "sessionId": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/configuration/security-key/enable" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### POST `/v1/users/{userId}/configuration/security-key/enable-verify` Finishes security key registration and stores credential. Enables security key as a 2sv media type if it is a user's first key. **Server:** `https://twostepverification.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user ID. | **Request Body:** `application/json` — Type: `Roblox.TwoStepVerification.Api.EnableVerifySecurityKeyRequest` See [Roblox.TwoStepVerification.Api.EnableVerifySecurityKeyRequest](#roblox-twostepverification-api-enableverifysecuritykeyrequest) in Models. **Request example:** ```json { "sessionId": "string", "credentialNickname": "string", "attestationResponse": "string" } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 2: The user ID is invalid. 17: Invalid security key nickname. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 17: Invalid security key nickname. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/configuration/security-key/enable-verify" \ -H "Content-Type: application/json" \ -d '{ "sessionId": "string", "credentialNickname": "string", "attestationResponse": "string" }' ``` ### POST `/v1/users/{userId}/configuration/security-key/list` List a user's registered security keys. **Server:** `https://twostepverification.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user ID. | **Responses:** - `200`: OK → `Roblox.TwoStepVerification.Api.ListSecurityKeyResponse` - `400`: 2: The user ID is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.TwoStepVerification.Api.ListSecurityKeyResponse`) See [Roblox.TwoStepVerification.Api.ListSecurityKeyResponse](#roblox-twostepverification-api-listsecuritykeyresponse) in Models. **Response example:** ```json { "credentials": [ { "nickname": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/configuration/security-key/list" ``` ### POST `/v1/users/{userId}/configuration/sms/disable` Disables two step verification via SMS for the specified user. **Server:** `https://twostepverification.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user ID. | **Request Body:** `application/json` — Type: `Roblox.TwoStepVerification.Api.DisableTwoStepVerificationRequest` See [Roblox.TwoStepVerification.Api.DisableTwoStepVerificationRequest](#roblox-twostepverification-api-disabletwostepverificationrequest) in Models. **Request example:** ```json { "password": "string", "reauthenticationToken": "string" } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 4: The password is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: The user ID is invalid. 8: The user is not allowed to perform the requested action. - `429`: 5: Too many requests. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/configuration/sms/disable" \ -H "Content-Type: application/json" \ -d '{ "password": "string", "reauthenticationToken": "string" }' ``` ### POST `/v1/users/{userId}/configuration/sms/enable` Enables two step verification via SMS for the specified user. **Server:** `https://twostepverification.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user ID. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 4: The password is invalid. 15: The phone number is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: The user ID is invalid. - `503`: 7: Two step verification is currently under maintenance. 8: The user is not allowed to perform the requested action. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/configuration/sms/enable" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### GET `/v1/users/{userId}/currently-wearing` Gets a list of asset ids that the user is currently wearing. **Server:** `https://avatar.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user id. | | `Roblox-Place-Id` | header | `integer` | No | | **Responses:** - `200`: OK → `Roblox.Api.Avatar.Models.AssetIdListModel` - `400`: 1: The specified user does not exist. 2: An account for the given userId does not exist! **Response fields** (`Roblox.Api.Avatar.Models.AssetIdListModel`) See [Roblox.Api.Avatar.Models.AssetIdListModel](#roblox-api-avatar-models-assetidlistmodel) in Models. **Response example:** ```json { "assetIds": [ 0 ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://avatar.roblox.com/v1/users/{USERID}/currently-wearing" ``` ### PATCH `/v1/users/{userId}/display-names` Set the display name for the authorized user. **Server:** `https://users.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | the user id | **Request Body:** `application/json` — Type: `Roblox.Users.Api.SetDisplayNameRequest` See [Roblox.Users.Api.SetDisplayNameRequest](#roblox-users-api-setdisplaynamerequest) in Models. **Request example:** ```json { "newDisplayName": "string" } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: Display name is too short 2: Display name is too long 3: Display name contains invalid characters 4: Display name has been moderated 8: Display name has too many combinations of character sets - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 7: The user id is invalid. - `429`: 5: Display name updates for this user have been throttled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://users.roblox.com/v1/users/{USERID}/display-names" \ -H "Content-Type: application/json" \ -d '{ "newDisplayName": "string" }' ``` ### GET `/v1/users/{userId}/display-names/validate` Validate a display name for an existing user. **Server:** `https://users.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user id. | | `displayName` | query | `string` | Yes | The display name. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: Display name is too short 2: Display name is too long 3: Display name contains invalid characters 4: Display name has been moderated 8: Display name has too many combinations of character sets - `401`: 0: Authorization has been denied for this request. - `403`: 7: The user id is invalid. - `429`: 5: Display name updates for this user have been throttled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://users.roblox.com/v1/users/{USERID}/display-names/validate?displayName={VALUE}" ``` ### GET `/v1/users/{userId}/friends` Get list of all friends for the specified user. **Server:** `https://friends.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user Id to get the friends for. | | `userSort` | query | `0 \| 1 \| 2` | No | Specifies how to sort the returned friends. Valid values: `0`, `1`, `2` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Friends.Api.Models.Response.UserResponse_` - `400`: 1: The target user is invalid or does not exist. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Friends.Api.Models.Response.UserResponse_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Friends.Api.Models.Response.UserResponse_](#roblox-web-webapi-models-apiarrayresponse-roblox-friends-api-models-response-userresponse-) in Models. **Response example:** ```json { "data": [ { "isOnline": "...", "presenceType": "...", "isDeleted": "...", "friendFrequentScore": "...", "friendFrequentRank": "...", "hasVerifiedBadge": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://friends.roblox.com/v1/users/{USERID}/friends" ``` ### GET `/v1/users/{userId}/friends/count` Get the number of friends a user has **Server:** `https://friends.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | | **Responses:** - `200`: OK → `Roblox.Friends.Api.FriendsCountResponse` - `400`: 1: The target user is invalid or does not exist. **Response fields** (`Roblox.Friends.Api.FriendsCountResponse`) See [Roblox.Friends.Api.FriendsCountResponse](#roblox-friends-api-friendscountresponse) in Models. **Response example:** ```json { "count": 0 } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://friends.roblox.com/v1/users/{USERID}/friends/count" ``` ### GET `/v1/users/{userId}/friends/find` Get a paginated list of all friends for the specified user. **Server:** `https://friends.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user Id to get the friends for. | | `userSort` | query | `0 \| 1 \| 2` | No | Specifies how to sort the returned friends. Valid values: `0`, `1`, `2` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `limit` | query | `integer` | No | The number of results per request. | | `findFriendsType` | query | `0 \| 1` | No | Valid values: `0`, `1` | **Responses:** - `200`: OK → `Roblox.Paging.CursoredPagedResult_Roblox.Friends.Api.Models.Response.FriendResponse_` - `400`: 1: The target user is invalid or does not exist. 6: Invalid parameters. **Response fields** (`Roblox.Paging.CursoredPagedResult_Roblox.Friends.Api.Models.Response.FriendResponse_`) See [Roblox.Paging.CursoredPagedResult_Roblox.Friends.Api.Models.Response.FriendResponse_](#roblox-paging-cursoredpagedresult-roblox-friends-api-models-response-friendresponse-) in Models. **Response example:** ```json { "PreviousCursor": "string", "PageItems": [ { "id": "...", "hasVerifiedBadge": "..." } ], "NextCursor": "string", "HasMore": false } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://friends.roblox.com/v1/users/{USERID}/friends/find" ``` ### GET `/v1/users/{userId}/friends/groups/roles` Gets a list of all groups the specified users' friends are in. **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user id. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Groups.Api.UserGroupMembershipResponse_` - `400`: 3: The user is invalid or does not exist. - `403`: 3: The user is invalid or does not exist. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Groups.Api.UserGroupMembershipResponse_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Groups.Api.UserGroupMembershipResponse_](#roblox-web-webapi-models-apiarrayresponse-roblox-groups-api-usergroupmembershipresponse-) in Models. **Response example:** ```json { "data": [ { "user": "...", "groups": "..." } ] } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/users/{USERID}/friends/groups/roles" ``` ### GET `/v1/users/{userId}/friends/inactive` Get list of inactive friends for the specified user. **Server:** `https://friends.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user Id to get the friends for. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Friends.Api.Models.Response.UserResponse_` - `400`: 1: The target user is invalid or does not exist. 6: Invalid parameters. - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Friends.Api.Models.Response.UserResponse_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Friends.Api.Models.Response.UserResponse_](#roblox-web-webapi-models-apiarrayresponse-roblox-friends-api-models-response-userresponse-) in Models. **Response example:** ```json { "data": [ { "isOnline": "...", "presenceType": "...", "isDeleted": "...", "friendFrequentScore": "...", "friendFrequentRank": "...", "hasVerifiedBadge": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://friends.roblox.com/v1/users/{USERID}/friends/inactive" ``` ### GET `/v1/users/{userId}/friends/online` Get list of all online friends for the specified user. **Server:** `https://friends.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user Id to get the friends for. | | `userSort` | query | `0 \| 1 \| 2` | No | The sort order to return the friends. Valid values: `0`, `1`, `2` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Friends.Api.Models.Response.UserPresenceResponse_` - `400`: 1: The target user is invalid or does not exist. 6: Invalid parameters. - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Friends.Api.Models.Response.UserPresenceResponse_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Friends.Api.Models.Response.UserPresenceResponse_](#roblox-web-webapi-models-apiarrayresponse-roblox-friends-api-models-response-userpresenceresponse-) in Models. **Response example:** ```json { "data": [ { "userPresence": "...", "sortScore": "...", "id": "...", "name": "...", "displayName": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://friends.roblox.com/v1/users/{USERID}/friends/online" ``` ### GET `/v1/users/{userId}/friends/search` Search for friends by name using a text query. **Server:** `https://friends.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user Id to get the friends for. | | `query` | query | `string` | No | The string to search names of friends for. | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `limit` | query | `integer` | No | The number of results per request. | **Responses:** - `200`: OK → `Roblox.Paging.CursoredPagedResult_Roblox.Friends.Api.Models.Response.FriendResponse_` - `400`: 1: The target user is invalid or does not exist. 6: Invalid parameters. - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Paging.CursoredPagedResult_Roblox.Friends.Api.Models.Response.FriendResponse_`) See [Roblox.Paging.CursoredPagedResult_Roblox.Friends.Api.Models.Response.FriendResponse_](#roblox-paging-cursoredpagedresult-roblox-friends-api-models-response-friendresponse-) in Models. **Response example:** ```json { "PreviousCursor": "string", "PageItems": [ { "id": "...", "hasVerifiedBadge": "..." } ], "NextCursor": "string", "HasMore": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://friends.roblox.com/v1/users/{USERID}/friends/search" ``` ### GET `/v1/users/{userId}/friends/statuses` Gets a list of friend statuses of specified users against the specified user. **Server:** `https://friends.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user to check the friend statuses against. | | `userIds` | query | `integer[]` | Yes | | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Friends.Api.FriendStatusResponse_` - `400`: 1: The target user is invalid or does not exist. 15: Too many ids. 16: Invalid ids. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Friends.Api.FriendStatusResponse_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Friends.Api.FriendStatusResponse_](#roblox-web-webapi-models-apiarrayresponse-roblox-friends-api-friendstatusresponse-) in Models. **Response example:** ```json { "data": [ { "id": "...", "status": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://friends.roblox.com/v1/users/{USERID}/friends/statuses?userIds={VALUE}" ``` ### GET `/v1/users/{userId}/groups/primary/role` Gets a user's primary group. **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user id. | **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupMembershipDetailResponse` - `400`: 4: User is invalid or does not exist. **Response fields** (`Roblox.Groups.Api.GroupMembershipDetailResponse`) See [Roblox.Groups.Api.GroupMembershipDetailResponse](#roblox-groups-api-groupmembershipdetailresponse) in Models. **Response example:** ```json { "group": { "id": 0, "name": "string", "description": "string", "owner": { "buildersClubMembershipType": "...", "hasVerifiedBadge": "...", "userId": "...", "username": "...", "displayName": "..." }, "shout": { "body": "...", "poster": "...", "created": "...", "updated": "..." }, "memberCount": 0 }, "role": { "id": 0, "name": "string", "description": "string", "rank": 0, "memberCount": 0, "isBase": false }, "isPrimaryGroup": false, "isNotificationsEnabled": false, "notificationPreferences": [ { "type": "...", "enabled": "...", "name": "...", "description": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/users/{USERID}/groups/primary/role" ``` ### GET `/v1/users/{userId}/groups/roles` Gets a list of all group roles for groups the specified user is in. **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user id. | | `includeLocked` | query | `boolean` | No | | | `includeNotificationPreferences` | query | `boolean` | No | | | `discoveryType` | query | `0 \| 1` | No | Valid values: `0`, `1` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Groups.Api.GroupMembershipDetailResponse_` - `400`: 3: The user is invalid or does not exist. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Groups.Api.GroupMembershipDetailResponse_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Groups.Api.GroupMembershipDetailResponse_](#roblox-web-webapi-models-apiarrayresponse-roblox-groups-api-groupmembershipdetailresponse-) in Models. **Response example:** ```json { "data": [ { "group": "...", "role": "...", "isPrimaryGroup": "...", "isNotificationsEnabled": "...", "notificationPreferences": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/users/{USERID}/groups/roles" ``` ### GET `/v1/users/{userId}/items/{itemType}/{itemTargetId}` Gets owned items of the specified item type. Game Servers can make requests for any user, but can only make requests for game passes that belong to the place sending the request. Place creators can make requests as if they were the Game Server. **Server:** `https://inventory.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | ID of the user in question | | `itemType` | path | `0 \| 1 \| 2 \| 3 \| 4` | Yes | Type of the item in question (i.e. Asset, GamePass, Badge, Bundle) Valid values: `0`, `1`, `2`, `3`, `4` | | `itemTargetId` | path | `integer` | Yes | ID of the item in question | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Inventory.Api.Models.IItemModel_` - `400`: 1: The specified user does not exist! 5: The specified game pass does not exist! Are you using the new game pass ID? 6: The specified item type does not exist. 7: The specified Asset does not exist! 10: The specified asset is not a badge! 12: The specified bundle does not exist! **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Inventory.Api.Models.IItemModel_`) See [Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Inventory.Api.Models.IItemModel_](#roblox-web-webapi-models-apipageresponse-roblox-inventory-api-models-iitemmodel-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "id": "...", "name": "...", "type": "...", "instanceId": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://inventory.roblox.com/v1/users/{USERID}/items/{ITEMTYPE}/{ITEMTARGETID}" ``` ### GET `/v1/users/{userId}/items/{itemType}/{itemTargetId}/is-owned` Gets whether a user owns an item of type itemType with id itemTargetId. **Server:** `https://inventory.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | ID of the user in question | | `itemType` | path | `0 \| 1 \| 2 \| 3 \| 4` | Yes | Type of the item in question (i.e. Asset, GamePass, Badge, Bundle) Valid values: `0`, `1`, `2`, `3`, `4` | | `itemTargetId` | path | `integer` | Yes | ID of the item in question | **Responses:** - `200`: OK → `boolean` - `400`: 1: The specified user does not exist! 5: The specified game pass does not exist! Are you using the new game pass ID? 6: The specified item type does not exist. 7: The specified Asset does not exist! 10: The specified asset is not a badge! 12: The specified bundle does not exist! **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://inventory.roblox.com/v1/users/{USERID}/items/{ITEMTYPE}/{ITEMTARGETID}/is-owned" ``` ### GET `/v1/users/{userId}/outfits` Deprecated, user v2. Gets a list of outfits for the specified user. **Server:** `https://avatar.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user id. | | `outfitType` | query | `string` | No | The outfit type being searched for, null will return all outfitTypes. | | `page` | query | `integer` | No | The page number of the current page of requests, default is 1. | | `itemsPerPage` | query | `integer` | No | The max number of outfits that can be returned. | | `isEditable` | query | `boolean` | No | Whether the outfits are editable. A null value will lead to no filtering. | | `Roblox-Place-Id` | header | `integer` | No | The placeId of the caller, not required to be passed in. | **Responses:** - `200`: OK → `Roblox.Api.Avatar.Models.AvatarFilteredPageResponse_Roblox.Api.Avatar.Models.OutfitModel_` - `400`: 1: The specified user does not exist. 2: An account for the given userId does not exist! **Response fields** (`Roblox.Api.Avatar.Models.AvatarFilteredPageResponse_Roblox.Api.Avatar.Models.OutfitModel_`) See [Roblox.Api.Avatar.Models.AvatarFilteredPageResponse_Roblox.Api.Avatar.Models.OutfitModel_](#roblox-api-avatar-models-avatarfilteredpageresponse-roblox-api-avatar-models-outfitmodel-) in Models. **Response example:** ```json { "filteredCount": 0, "data": [ { "id": "...", "name": "...", "isEditable": "...", "outfitType": "..." } ], "total": 0 } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://avatar.roblox.com/v1/users/{USERID}/outfits" ``` ### GET `/v1/users/{userId}/places/inventory` Gets Created, MyGames, or OtherGames places inventory for a user **Server:** `https://inventory.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | | | `placesTab` | query | `integer enum (6 values)` | Yes | Valid values: `0`, `1`, `2`, `3`, `4`, `5` | | `itemsPerPage` | query | `integer` | Yes | | | `cursor` | query | `integer` | Yes | | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Inventory.Api.Models.PlaceModel_` - `400`: 6: Invalid request - `403`: 3: Insufficient permission. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Inventory.Api.Models.PlaceModel_`) See [Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Inventory.Api.Models.PlaceModel_](#roblox-web-webapi-models-apipageresponse-roblox-inventory-api-models-placemodel-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "universeId": "...", "placeId": "...", "name": "...", "creator": "...", "priceInRobux": "..." } ] } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://inventory.roblox.com/v1/users/{USERID}/places/inventory?placesTab={VALUE}&itemsPerPage={VALUE}&cursor={VALUE}" ``` ### GET `/v1/users/{userId}/premium-upsell-precheck` Premium upsell precheck **Server:** `https://premiumfeatures.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | User ID | | `universeId` | query | `integer` | Yes | | | `placeId` | query | `integer` | Yes | | **Responses:** - `200`: OK - `401`: 0: Authorization has been denied for this request. **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://premiumfeatures.roblox.com/v1/users/{USERID}/premium-upsell-precheck?universeId={VALUE}&placeId={VALUE}" ``` ### GET `/v1/users/{userId}/promotion-channels` Get promotion channels for a given user ID **Server:** `https://accountinformation.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The ID of the user to fetch the promotion channels for. | | `alwaysReturnUrls` | query | `boolean` | No | Whether all promotion channel links should be returned as full URLs. | | `filterLink` | query | `boolean` | No | Whether all promotion channel links should be filtered. | **Responses:** - `200`: OK → `Roblox.AccountInformation.Api.Models.PromotionChannelsByUserIdResponse` - `400`: 1: User not found. **Response fields** (`Roblox.AccountInformation.Api.Models.PromotionChannelsByUserIdResponse`) See [Roblox.AccountInformation.Api.Models.PromotionChannelsByUserIdResponse](#roblox-accountinformation-api-models-promotionchannelsbyuseridresponse) in Models. **Response example:** ```json { "facebook": "string", "twitter": "string", "youtube": "string", "twitch": "string" } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountinformation.roblox.com/v1/users/{USERID}/promotion-channels" ``` ### GET `/v1/users/{userId}/recovery-codes` Gets the current status of recovery codes for a user. **Server:** `https://twostepverification.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user ID. | **Responses:** - `200`: OK → `Roblox.TwoStepVerification.Api.RecoveryCodesStatusResponse` - `400`: 2: The user ID is invalid. - `401`: 0: Authorization has been denied for this request. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.TwoStepVerification.Api.RecoveryCodesStatusResponse`) See [Roblox.TwoStepVerification.Api.RecoveryCodesStatusResponse](#roblox-twostepverification-api-recoverycodesstatusresponse) in Models. **Response example:** ```json { "activeCount": 0, "created": "2024-01-01T00:00:00Z" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/recovery-codes" ``` ### POST `/v1/users/{userId}/recovery-codes/clear` Clears any existing recovery codes for the user. Clearing recovery codes voids any recovery codes previously generated for the user. New recovery codes will have to be generated to pass two step verification via recovery code. **Server:** `https://twostepverification.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user ID. | **Request Body:** `application/json` — Type: `Roblox.TwoStepVerification.Api.ClearRecoveryCodesRequest` See [Roblox.TwoStepVerification.Api.ClearRecoveryCodesRequest](#roblox-twostepverification-api-clearrecoverycodesrequest) in Models. **Request example:** ```json { "password": "string" } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 2: The user ID is invalid. 4: The password is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `429`: 5: Too many requests. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/recovery-codes/clear" \ -H "Content-Type: application/json" \ -d '{ "password": "string" }' ``` ### POST `/v1/users/{userId}/recovery-codes/regenerate` Clears any existing recovery codes and generates a new batch of recovery codes. Two step verification recovery codes do not enforce that two step verification must be passed when logging in. At least one two step verification media type must be enabled to trigger the two step verification flow. Recovery codes are intended to be used to pass two step verification when the enabled media type is unavailable. Recovery codes generated by this endpoint do not have an expiration. Once a recovery code generated by this endpoint has been used it cannot be used again. **Server:** `https://twostepverification.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user ID to generate recovery codes for. | **Request Body:** `application/json` — Type: `Roblox.TwoStepVerification.Api.RegenerateRecoveryCodesRequest` See [Roblox.TwoStepVerification.Api.RegenerateRecoveryCodesRequest](#roblox-twostepverification-api-regeneraterecoverycodesrequest) in Models. **Request example:** ```json { "password": "string" } ``` **Responses:** - `200`: OK → `Roblox.TwoStepVerification.Api.RegenerateRecoveryCodesResponse` - `400`: 2: The user ID is invalid. 4: The password is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `429`: 5: Too many requests. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.TwoStepVerification.Api.RegenerateRecoveryCodesResponse`) See [Roblox.TwoStepVerification.Api.RegenerateRecoveryCodesResponse](#roblox-twostepverification-api-regeneraterecoverycodesresponse) in Models. **Response example:** ```json { "recoveryCodes": [ "string" ] } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/recovery-codes/regenerate" \ -H "Content-Type: application/json" \ -d '{ "password": "string" }' ``` ### GET `/v1/users/{userId}/roblox-badges` Returns a list of Roblox badges belonging to a user. **Server:** `https://accountinformation.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | | **Responses:** - `200`: OK → `Roblox.AccountInformation.Api.RobloxBadgeResponse[]` **Response fields** (`Roblox.AccountInformation.Api.RobloxBadgeResponse[]`) See [Roblox.AccountInformation.Api.RobloxBadgeResponse](#roblox-accountinformation-api-robloxbadgeresponse) in Models. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountinformation.roblox.com/v1/users/{USERID}/roblox-badges" ``` ### GET `/v1/users/{userId}/universes` Gets all the followings between a user with userId and universes **Server:** `https://followings.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | | **Responses:** - `200`: OK → `Roblox.Followings.Api.Models.UserFollowingUniverseResponse[]` - `401`: 0: Authorization has been denied for this request. - `403`: User is not authorized for this action. **Response fields** (`Roblox.Followings.Api.Models.UserFollowingUniverseResponse[]`) See [Roblox.Followings.Api.Models.UserFollowingUniverseResponse](#roblox-followings-api-models-userfollowinguniverseresponse) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://followings.roblox.com/v1/users/{USERID}/universes" ``` ### POST `/v1/users/{userId}/universes/{universeId}` Creates the following between a user with userId and universe with universeId **Server:** `https://followings.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | | | `universeId` | path | `integer` | Yes | | **Responses:** - `200`: OK → `Roblox.Followings.Api.Models.UserFollowingUniverseResponse` - `400`: The user has reached the limit of number of followed universes. - `401`: 0: Authorization has been denied for this request. - `403`: User is not authorized for this action. 0: Token Validation Failed **Response fields** (`Roblox.Followings.Api.Models.UserFollowingUniverseResponse`) See [Roblox.Followings.Api.Models.UserFollowingUniverseResponse](#roblox-followings-api-models-userfollowinguniverseresponse) in Models. **Response example:** ```json { "universeId": 0, "userId": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://followings.roblox.com/v1/users/{USERID}/universes/{UNIVERSEID}" ``` ### DELETE `/v1/users/{userId}/universes/{universeId}` Deletes the following between a user with userId and universe with universeId **Server:** `https://followings.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | | | `universeId` | path | `integer` | Yes | | **Responses:** - `200`: OK → `Roblox.Followings.Api.Models.UserFollowingUniverseResponse` - `401`: 0: Authorization has been denied for this request. - `403`: User is not authorized for this action. 0: Token Validation Failed **Response fields** (`Roblox.Followings.Api.Models.UserFollowingUniverseResponse`) See [Roblox.Followings.Api.Models.UserFollowingUniverseResponse](#roblox-followings-api-models-userfollowinguniverseresponse) in Models. **Response example:** ```json { "universeId": 0, "userId": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://followings.roblox.com/v1/users/{USERID}/universes/{UNIVERSEID}" ``` ### GET `/v1/users/{userId}/universes/{universeId}/status` Gets the status of a following relationship between a user and a universe. **Server:** `https://followings.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | | | `universeId` | path | `integer` | Yes | | **Responses:** - `200`: OK → `Roblox.Followings.Api.Models.UserFollowingUniverseStatusResponse` - `401`: 0: Authorization has been denied for this request. - `403`: User is not authorized for this action. **Response fields** (`Roblox.Followings.Api.Models.UserFollowingUniverseStatusResponse`) See [Roblox.Followings.Api.Models.UserFollowingUniverseStatusResponse](#roblox-followings-api-models-userfollowinguniversestatusresponse) in Models. **Response example:** ```json { "UniverseId": 0, "UserId": 0, "CanFollow": false, "IsFollowing": false, "FollowingCountByType": 0, "FollowingLimitByType": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://followings.roblox.com/v1/users/{USERID}/universes/{UNIVERSEID}/status" ``` ### GET `/v1/users/{userId}/username-history` Retrieves the username history for a particular user. **Server:** `https://users.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | | | `limit` | query | `10 \| 25 \| 50 \| 100` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `Asc \| Desc` | No | The order the results are sorted in. Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Users.Api.UsernameHistoryResponse_` - `400`: 3: The user id is invalid. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Users.Api.UsernameHistoryResponse_`) See [Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Users.Api.UsernameHistoryResponse_](#roblox-web-webapi-models-apipageresponse-roblox-users-api-usernamehistoryresponse-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "name": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://users.roblox.com/v1/users/{USERID}/username-history" ``` ### GET `/v1/users/{userId}/validate-membership` Get if a user has a Premium membership **Server:** `https://premiumfeatures.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | User ID | **Responses:** - `200`: OK - `401`: 0: Authorization has been denied for this request. **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://premiumfeatures.roblox.com/v1/users/{USERID}/validate-membership" ``` ### GET `/v1/vip-server/can-invite/{userId}` **Server:** `https://games.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | | **Responses:** - `200`: Success → `CanInviteUserResponse` **Response fields** (`CanInviteUserResponse`) See [CanInviteUserResponse](#caninviteuserresponse) in Models. **Response example:** ```json { "canInvite": false, "doesOwnerPrivacyRestrictJoins": false, "inviteResponseType": "..." } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://games.roblox.com/v1/vip-server/can-invite/{USERID}" ``` ### GET `/v1/vip-servers/my-private-servers` **Server:** `https://games.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `privateServersTab` | query | `any` | No | | | `itemsPerPage` | query | `integer` | No | | | `cursor` | query | `string` | No | | **Responses:** - `200`: Success → `MyPrivateServersResponse` **Response fields** (`MyPrivateServersResponse`) See [MyPrivateServersResponse](#myprivateserversresponse) in Models. **Response example:** ```json { "nextPageCursor": "string", "previousPageCursor": "string", "data": [ { "active": "...", "universeId": "...", "placeId": "...", "name": "...", "ownerId": "...", "ownerName": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://games.roblox.com/v1/vip-servers/my-private-servers" ``` ### GET `/v1/vip-servers/{id}` **Server:** `https://games.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `id` | path | `integer` | Yes | | **Responses:** - `200`: Success → `PrivateServerResponse` **Response fields** (`PrivateServerResponse`) See [PrivateServerResponse](#privateserverresponse) in Models. **Response example:** ```json { "id": 0, "name": "string", "game": "...", "joinCode": "string", "active": false, "subscription": "..." } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://games.roblox.com/v1/vip-servers/{ID}" ``` ### PATCH `/v1/vip-servers/{id}` **Server:** `https://games.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `id` | path | `integer` | Yes | | **Request Body:** `application/json` — Type: `any` **Responses:** - `200`: Success → `PrivateServerResponse` **Response fields** (`PrivateServerResponse`) See [PrivateServerResponse](#privateserverresponse) in Models. **Response example:** ```json { "id": 0, "name": "string", "game": "...", "joinCode": "string", "active": false, "subscription": "..." } ``` **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://games.roblox.com/v1/vip-servers/{ID}" \ -H "Content-Type: application/json" \ -d '"..."' ``` ### PATCH `/v1/vip-servers/{id}/permissions` **Server:** `https://games.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `id` | path | `integer` | Yes | | **Request Body:** `application/json` — Type: `any` **Responses:** - `200`: Success → `PrivateServerPermissionsResponse` **Response fields** (`PrivateServerPermissionsResponse`) See [PrivateServerPermissionsResponse](#privateserverpermissionsresponse) in Models. **Response example:** ```json { "clanAllowed": false, "enemyClanId": 0, "friendsAllowed": false, "users": [ { "id": "...", "name": "...", "displayName": "..." } ] } ``` **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://games.roblox.com/v1/vip-servers/{ID}/permissions" \ -H "Content-Type: application/json" \ -d '"..."' ``` ### PATCH `/v1/vip-servers/{id}/subscription` **Server:** `https://games.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `id` | path | `integer` | Yes | | **Request Body:** `application/json` — Type: `any` **Responses:** - `200`: Success → `PrivateServerSubscriptionResponse` **Response fields** (`PrivateServerSubscriptionResponse`) See [PrivateServerSubscriptionResponse](#privateserversubscriptionresponse) in Models. **Response example:** ```json { "active": false, "expired": false, "expirationDate": "2024-01-01T00:00:00Z", "price": 0, "canRenew": false, "hasInsufficientFunds": false } ``` **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://games.roblox.com/v1/vip-servers/{ID}/subscription" \ -H "Content-Type: application/json" \ -d '"..."' ``` ### GET `/v2/android-binaries/{version}/channels/{channelName}` Retrieve the Android binary information for a given version and channel name. **Server:** `https://clientsettings.roblox.com` **Auth:** **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `version` | path | `string` | Yes | The version string of the application. i.e. 2.660.392 | | `channelName` | path | `string` | Yes | The name of the channel. E.g. ZFlag, ZIntegration | **Responses:** - `200`: OK → `Roblox.ClientSettings.Api.Models.Response.AndroidBinaryResponse` **Response fields** (`Roblox.ClientSettings.Api.Models.Response.AndroidBinaryResponse`) See [Roblox.ClientSettings.Api.Models.Response.AndroidBinaryResponse](#roblox-clientsettings-api-models-response-androidbinaryresponse) in Models. **Response example:** ```json { "moduleName": "string", "libraryNames": { "engine": "string" }, "supportsAndroidBinaries": false } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://clientsettings.roblox.com/v2/android-binaries/{VERSION}/channels/{CHANNELNAME}" ``` ### GET `/v2/asset` **Server:** `https://assetdelivery.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `Accept-Encoding` | header | `string` | Yes | | | `Roblox-Place-Id` | header | `integer` | Yes | | | `AssetType` | header | `string` | Yes | | | `Accept` | header | `string` | Yes | | | `AssetFormat` | header | `string` | Yes | | | `Roblox-AssetFormat` | header | `string` | Yes | | | `id` | query | `integer` | No | | | `userAssetId` | query | `integer` | No | | | `assetVersionId` | query | `integer` | No | | | `version` | query | `integer` | No | | | `universeId` | query | `integer` | No | | | `clientInsert` | query | `integer` | No | | | `scriptinsert` | query | `integer` | No | | | `modulePlaceId` | query | `integer` | No | | | `serverplaceid` | query | `string` | No | | | `assetName` | query | `string` | No | | | `hash` | query | `string` | No | | | `marAssetHash` | query | `string` | No | | | `marCheckSum` | query | `string` | No | | | `expectedAssetType` | query | `string` | No | | | `skipSigningScripts` | query | `boolean` | No | | | `permissionContext` | query | `string` | No | | | `doNotFallbackToBaselineRepresentation` | query | `boolean` | No | | | `contentRepresentationPriorityList` | query | `string` | No | | | `accessContext` | query | `string` | No | | | `usageContext` | query | `integer` | No | | **Responses:** - `200`: OK → `Roblox.Web.Assets.AssetResponseItemV2` **Response fields** (`Roblox.Web.Assets.AssetResponseItemV2`) See [Roblox.Web.Assets.AssetResponseItemV2](#roblox-web-assets-assetresponseitemv2) in Models. **Response example:** ```json { "locations": [ { "assetFormat": "...", "location": "...", "assetMetadatas": "..." } ], "errors": [ { "Code": "...", "Message": "...", "CustomErrorCode": "..." } ], "requestId": "string", "isArchived": false, "assetTypeId": 0, "contentRepresentationSpecifier": { "format": "string", "majorVersion": "string", "fidelity": "string", "skipGenerationIfNotExist": false } } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://assetdelivery.roblox.com/v2/asset" ``` ### GET `/v2/assetId/{assetId}` **Server:** `https://assetdelivery.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetId` | path | `integer` | Yes | | | `Accept-Encoding` | header | `string` | Yes | | | `Roblox-Place-Id` | header | `integer` | Yes | | | `AssetType` | header | `string` | Yes | | | `Accept` | header | `string` | Yes | | | `AssetFormat` | header | `string` | Yes | | | `Roblox-AssetFormat` | header | `string` | Yes | | | `skipSigningScripts` | query | `boolean` | No | | | `clientInsert` | query | `integer` | No | | | `scriptinsert` | query | `integer` | No | | | `modulePlaceId` | query | `integer` | No | | | `serverplaceid` | query | `integer` | No | | | `expectedAssetType` | query | `string` | No | | | `doNotFallbackToBaselineRepresentation` | query | `boolean` | No | | | `contentRepresentationPriorityList` | query | `string` | No | | | `accessContext` | query | `string` | No | | | `usageContext` | query | `integer` | No | | **Responses:** - `200`: OK → `Roblox.Web.Assets.AssetResponseItemV2` **Response fields** (`Roblox.Web.Assets.AssetResponseItemV2`) See [Roblox.Web.Assets.AssetResponseItemV2](#roblox-web-assets-assetresponseitemv2) in Models. **Response example:** ```json { "locations": [ { "assetFormat": "...", "location": "...", "assetMetadatas": "..." } ], "errors": [ { "Code": "...", "Message": "...", "CustomErrorCode": "..." } ], "requestId": "string", "isArchived": false, "assetTypeId": 0, "contentRepresentationSpecifier": { "format": "string", "majorVersion": "string", "fidelity": "string", "skipGenerationIfNotExist": false } } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://assetdelivery.roblox.com/v2/assetId/{ASSETID}" ``` ### GET `/v2/assetId/{assetId}/version/{versionNumber}` **Server:** `https://assetdelivery.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetId` | path | `integer` | Yes | | | `versionNumber` | path | `integer` | Yes | | | `Accept-Encoding` | header | `string` | Yes | | | `Roblox-Place-Id` | header | `integer` | Yes | | | `AssetType` | header | `string` | Yes | | | `Accept` | header | `string` | Yes | | | `AssetFormat` | header | `string` | Yes | | | `Roblox-AssetFormat` | header | `string` | Yes | | | `skipSigningScripts` | query | `boolean` | No | | | `clientInsert` | query | `integer` | No | | | `scriptinsert` | query | `integer` | No | | | `modulePlaceId` | query | `integer` | No | | | `serverplaceid` | query | `integer` | No | | | `expectedAssetType` | query | `string` | No | | | `doNotFallbackToBaselineRepresentation` | query | `boolean` | No | | | `contentRepresentationPriorityList` | query | `string` | No | | | `accessContext` | query | `string` | No | | | `usageContext` | query | `integer` | No | | **Responses:** - `200`: OK → `Roblox.Web.Assets.AssetResponseItemV2` **Response fields** (`Roblox.Web.Assets.AssetResponseItemV2`) See [Roblox.Web.Assets.AssetResponseItemV2](#roblox-web-assets-assetresponseitemv2) in Models. **Response example:** ```json { "locations": [ { "assetFormat": "...", "location": "...", "assetMetadatas": "..." } ], "errors": [ { "Code": "...", "Message": "...", "CustomErrorCode": "..." } ], "requestId": "string", "isArchived": false, "assetTypeId": 0, "contentRepresentationSpecifier": { "format": "string", "majorVersion": "string", "fidelity": "string", "skipGenerationIfNotExist": false } } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://assetdelivery.roblox.com/v2/assetId/{ASSETID}/version/{VERSIONNUMBER}" ``` ### POST `/v2/assets/batch` **Server:** `https://assetdelivery.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `Roblox-Place-Id` | header | `integer` | Yes | | | `Accept` | header | `string` | Yes | | | `Roblox-Browser-Asset-Request` | header | `string` | Yes | | **Request Body:** `application/json` — Type: `Roblox.Web.Assets.BatchAssetRequestItem[]` See [Roblox.Web.Assets.BatchAssetRequestItem](#roblox-web-assets-batchassetrequestitem) in Models. **Responses:** - `200`: OK → `Roblox.Web.Assets.AssetResponseItemV2[]` **Response fields** (`Roblox.Web.Assets.AssetResponseItemV2[]`) See [Roblox.Web.Assets.AssetResponseItemV2](#roblox-web-assets-assetresponseitemv2) in Models. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://assetdelivery.roblox.com/v2/assets/batch" \ -H "Content-Type: application/json" \ -d '[ { "assetName": "string", "assetType": "string", "clientInsert": false, "placeId": 0, "requestId": "string", "scriptInsert": false } ]' ``` ### GET `/v2/assets/{assetId}/bundles` Lists bundles that contain the given asset (hydrated search-detail shape for marketplace). **Server:** `https://catalog.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetId` | path | `integer` | Yes | Asset id. | | `Roblox-Place-Id` | header | `integer` | Yes | Roblox-Place-Id header. | | `Roblox-Game-Id` | header | `string` | Yes | Roblox-Game-Id header. | | `limit` | query | `10 \| 25 \| 50 \| 100` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `Asc \| Desc` | No | The order the results are sorted in. Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Catalog.Api.CatalogSearchDetailedResponseItem_` - `400`: 1: Invalid assetId 4: Invalid Cursor. - `403`: 7: User is unauthorized. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Catalog.Api.CatalogSearchDetailedResponseItem_`) See [Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Catalog.Api.CatalogSearchDetailedResponseItem_](#roblox-web-webapi-models-apipageresponse-roblox-catalog-api-catalogsearchdetailedresponseitem-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "id": "...", "itemType": "...", "assetType": "...", "bundleType": "...", "isRecolorable": "...", "name": "..." } ] } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://catalog.roblox.com/v2/assets/{ASSETID}/bundles" ``` ### GET `/v2/assets/{assetId}/owners` Gets a list of owners of an asset. **Server:** `https://inventory.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetId` | path | `integer` | Yes | The asset id. | | `limit` | query | `10 \| 25 \| 50 \| 100` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `Asc \| Desc` | No | Sorted by userAssetId Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Inventory.Api.V2.AssetOwnerResponse_` - `400`: 1: The asset id is invalid. - `403`: 2: You do not have permission to view the owners of this asset. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Inventory.Api.V2.AssetOwnerResponse_`) See [Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Inventory.Api.V2.AssetOwnerResponse_](#roblox-web-webapi-models-apipageresponse-roblox-inventory-api-v2-assetownerresponse-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "id": "...", "collectibleItemInstanceId": "...", "serialNumber": "...", "owner": "...", "created": "...", "updated": "..." } ] } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://inventory.roblox.com/v2/assets/{ASSETID}/owners" ``` ### GET `/v2/assets/{id}/versions` *(deprecated)* Retrieves asset information for the specified asset ID. The authenticated user must be able to manage the asset or granted by package permission. Use OpenCloud Assets API instead. **Server:** `https://develop.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `id` | path | `integer` | Yes | The ID of the asset.Roblox.Platform.Assets.IAsset | | `Roblox-Place-Id` | header | `integer` | Yes | The ID of the place.Roblox.Platform.Assets.IPlace | | `limit` | query | `10 \| 25 \| 50 \| 100` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `Asc \| Desc` | No | Sort by version number, default is desc. Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Api.Develop.AssetVersion_` **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Api.Develop.AssetVersion_`) See [Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Api.Develop.AssetVersion_](#roblox-web-webapi-models-apipageresponse-roblox-api-develop-assetversion-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "Id": "...", "assetId": "...", "assetVersionNumber": "...", "creatorType": "...", "creatorTargetId": "...", "creatingUniverseId": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v2/assets/{ID}/versions" ``` ### GET `/v2/avatar/avatar` Returns details about the authenticated user's avatar. **Server:** `https://avatar.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `Roblox-Place-Id` | header | `integer` | No | | | `checkAssetAvailability` | query | `boolean` | No | Whether to return assets with availability status. | **Responses:** - `200`: OK → `Roblox.Api.Avatar.Models.AvatarModelV3` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Api.Avatar.Models.AvatarModelV3`) See [Roblox.Api.Avatar.Models.AvatarModelV3](#roblox-api-avatar-models-avatarmodelv3) in Models. **Response example:** ```json { "scales": { "height": 0, "width": 0, "head": 0, "depth": 0, "proportion": 0, "bodyType": 0 }, "playerAvatarType": 1, "bodyColor3s": { "headColor3": "string", "torsoColor3": "string", "rightArmColor3": "string", "leftArmColor3": "string", "rightLegColor3": "string", "leftLegColor3": "string" }, "assets": [ { "id": "...", "name": "...", "assetType": "...", "currentVersionId": "...", "meta": "...", "availabilityStatus": "..." } ], "defaultShirtApplied": false, "defaultPantsApplied": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://avatar.roblox.com/v2/avatar/avatar" ``` ### POST `/v2/avatar/set-body-colors` Sets the authenticated user's body colors. **Server:** `https://avatar.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `Roblox-Place-Id` | header | `integer` | No | | **Request Body:** `application/json` — Type: `Roblox.Platform.Avatar.BodyColorsModelV2` See [Roblox.Platform.Avatar.BodyColorsModelV2](#roblox-platform-avatar-bodycolorsmodelv2) in Models. **Request example:** ```json { "headColor3": "string", "torsoColor3": "string", "rightArmColor3": "string", "leftArmColor3": "string", "rightLegColor3": "string", "leftLegColor3": "string" } ``` **Responses:** - `200`: OK → `Roblox.Api.Avatar.Models.AvatarApiSuccessResponse` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Api.Avatar.Models.AvatarApiSuccessResponse`) See [Roblox.Api.Avatar.Models.AvatarApiSuccessResponse](#roblox-api-avatar-models-avatarapisuccessresponse) in Models. **Response example:** ```json { "success": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://avatar.roblox.com/v2/avatar/set-body-colors" \ -H "Content-Type: application/json" \ -d '{ "headColor3": "string", "torsoColor3": "string", "rightArmColor3": "string", "leftArmColor3": "string", "rightLegColor3": "string", "leftLegColor3": "string" }' ``` ### POST `/v2/avatar/set-wearing-assets` Sets the avatar's current assets to the list. Only allows items that you own, are not expired, and are wearable asset types. Any assets being worn before this method is called are automatically removed. **Server:** `https://avatar.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `Roblox-Place-Id` | header | `integer` | No | | **Request Body:** `application/json` — Type: `Roblox.Api.Avatar.Models.WearRequestModel` See [Roblox.Api.Avatar.Models.WearRequestModel](#roblox-api-avatar-models-wearrequestmodel) in Models. **Request example:** ```json { "assets": [ { "id": "...", "meta": "..." } ] } ``` **Responses:** - `200`: OK → `Roblox.Api.Avatar.Models.WearResponseModel` - `400`: 3: Invalid assetId 5: Meta does not apply to specified asset type 7: Required meta is not provided for the specific asset type - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `500`: 2: Failed to wear asset. **Response fields** (`Roblox.Api.Avatar.Models.WearResponseModel`) See [Roblox.Api.Avatar.Models.WearResponseModel](#roblox-api-avatar-models-wearresponsemodel) in Models. **Response example:** ```json { "invalidAssets": [ { "id": "...", "name": "...", "assetType": "...", "currentVersionId": "...", "meta": "...", "availabilityStatus": "..." } ], "invalidAssetIds": [ 0 ], "success": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://avatar.roblox.com/v2/avatar/set-wearing-assets" \ -H "Content-Type: application/json" \ -d '{ "assets": [ { "id": "...", "meta": "..." } ] }' ``` ### GET `/v2/avatar/users/{userId}/avatar` Returns details about a specified user's avatar. Includes assets, bodycolors, and playerAvatarType. **Server:** `https://avatar.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | | | `Roblox-Place-Id` | header | `integer` | No | | | `checkAssetAvailability` | query | `boolean` | No | Whether to return assets with availability status. | **Responses:** - `200`: OK → `Roblox.Api.Avatar.Models.AvatarModelV3` - `400`: 1: The specified user does not exist. 2: An account for the given userId does not exist! **Response fields** (`Roblox.Api.Avatar.Models.AvatarModelV3`) See [Roblox.Api.Avatar.Models.AvatarModelV3](#roblox-api-avatar-models-avatarmodelv3) in Models. **Response example:** ```json { "scales": { "height": 0, "width": 0, "head": 0, "depth": 0, "proportion": 0, "bodyType": 0 }, "playerAvatarType": 1, "bodyColor3s": { "headColor3": "string", "torsoColor3": "string", "rightArmColor3": "string", "leftArmColor3": "string", "rightLegColor3": "string", "leftLegColor3": "string" }, "assets": [ { "id": "...", "name": "...", "assetType": "...", "currentVersionId": "...", "meta": "...", "availabilityStatus": "..." } ], "defaultShirtApplied": false, "defaultPantsApplied": false } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://avatar.roblox.com/v2/avatar/users/{USERID}/avatar" ``` ### GET `/v2/avatar/users/{userId}/outfits` Gets a list of outfits for the specified user. **Server:** `https://avatar.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user id. | | `paginationToken` | query | `string` | No | The token received from the response to get the next page. For the first request, this value should be empty. Note : If no value is sent the 1st page will be returned. | | `outfitType` | query | `string` | No | The outfit type being searched for, null will return all outfitTypes. | | `page` | query | `integer` | No | The page number of the current page of requests, default is 1. | | `itemsPerPage` | query | `integer` | No | The max number of outfits that can be returned. | | `isEditable` | query | `boolean` | No | Whether the outfits are editable. A null value will lead to no filtering. | | `Roblox-Place-Id` | header | `integer` | No | The placeId of the caller, not required to be passed in. | **Responses:** - `200`: OK → `Roblox.Api.Avatar.Models.AvatarPageResponse_Roblox.Api.Avatar.Models.OutfitModel_` - `400`: 1: The specified user does not exist. 2: An account for the given userId does not exist! **Response fields** (`Roblox.Api.Avatar.Models.AvatarPageResponse_Roblox.Api.Avatar.Models.OutfitModel_`) See [Roblox.Api.Avatar.Models.AvatarPageResponse_Roblox.Api.Avatar.Models.OutfitModel_](#roblox-api-avatar-models-avatarpageresponse-roblox-api-avatar-models-outfitmodel-) in Models. **Response example:** ```json { "data": [ { "id": "...", "name": "...", "isEditable": "...", "outfitType": "..." } ], "paginationToken": "string" } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://avatar.roblox.com/v2/avatar/users/{USERID}/outfits" ``` ### GET `/v2/client-version/{binaryType}` Get client version information for specific binary type **Server:** `https://clientsettings.roblox.com` **Auth:** **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `binaryType` | path | `string` | Yes | Platform(WindowsPlayer, WindowsStudio, MacPlayer or MacStudio) for which we want the latest version | **Responses:** - `200`: OK → `Roblox.ClientSettings.Api.Models.Response.ClientVersionResponse` **Response fields** (`Roblox.ClientSettings.Api.Models.Response.ClientVersionResponse`) See [Roblox.ClientSettings.Api.Models.Response.ClientVersionResponse](#roblox-clientsettings-api-models-response-clientversionresponse) in Models. **Response example:** ```json { "version": "string", "clientVersionUpload": "string", "bootstrapperVersion": "string", "nextClientVersionUpload": "string", "nextClientVersion": "string" } ``` **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://clientsettings.roblox.com/v2/client-version/{BINARYTYPE}" ``` ### GET `/v2/client-version/{binaryType}/channel/{channelName}` Get client version information for specific binary type **Server:** `https://clientsettings.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `binaryType` | path | `string` | Yes | Platform(WindowsPlayer, WindowsStudio, MacPlayer or MacStudio) for which we want the latest version | | `channelName` | path | `string` | Yes | Channel Name | **Responses:** - `200`: OK → `Roblox.ClientSettings.Api.Models.Response.ClientVersionResponse` - `401`: 5: Not authorized to perform this action. **Response fields** (`Roblox.ClientSettings.Api.Models.Response.ClientVersionResponse`) See [Roblox.ClientSettings.Api.Models.Response.ClientVersionResponse](#roblox-clientsettings-api-models-response-clientversionresponse) in Models. **Response example:** ```json { "version": "string", "clientVersionUpload": "string", "bootstrapperVersion": "string", "nextClientVersionUpload": "string", "nextClientVersion": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://clientsettings.roblox.com/v2/client-version/{BINARYTYPE}/channel/{CHANNELNAME}" ``` ### GET `/v2/collectible-items/{collectibleItemId}/owners` Gets a list of owners of a collectible item. **Server:** `https://inventory.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `collectibleItemId` | path | `string` | Yes | The collectible item ID. | | `limit` | query | `integer` | No | The number of results per request. | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `1 \| 2` | No | The order the results are sorted in. Valid values: `1`, `2` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Inventory.Api.V2.CollectibleItemOwnerResponse_` - `400`: 1: The collectible item id is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: 2: You do not have permission to view the owners of this item. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Inventory.Api.V2.CollectibleItemOwnerResponse_`) See [Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Inventory.Api.V2.CollectibleItemOwnerResponse_](#roblox-web-webapi-models-apipageresponse-roblox-inventory-api-v2-collectibleitemownerresponse-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "collectibleItemInstanceId": "...", "serialNumber": "...", "owner": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://inventory.roblox.com/v2/collectible-items/{COLLECTIBLEITEMID}/owners" ``` ### GET `/v2/compression-dictionaries` Returns a listing of all known compression dictionaries, including their SHA256 and creation date. This will be sorted by creation date, with the most recent dictionaries first. **Server:** `https://clientsettings.roblox.com` **Auth:** **Responses:** - `200`: OK **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://clientsettings.roblox.com/v2/compression-dictionaries" ``` ### GET `/v2/compression-dictionaries/{dictionarySha256}` Returns the specified compression dictionary as a file download. **Server:** `https://clientsettings.roblox.com` **Auth:** **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `dictionarySha256` | path | `string` | Yes | The SHA256 of the dictionary we wish to download. | **Responses:** - `200`: OK **Example:** ```bash curl -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://clientsettings.roblox.com/v2/compression-dictionaries/{DICTIONARYSHA256}" ``` ### GET `/v2/games/{universeId}/media` Get the game media data **Server:** `https://games.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The id of the universe we get media data from. | | `fetchAllExperienceRelatedMedia` | query | `boolean` | No | to tell if the API query is to fetch all related media for this experience | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Web.Responses.Games.GameMediaItemResponseV2_` - `400`: 3: The universe's root place is invalid. - `404`: 2: The requested universe does not exist. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Web.Responses.Games.GameMediaItemResponseV2_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Web.Responses.Games.GameMediaItemResponseV2_](#roblox-web-webapi-models-apiarrayresponse-roblox-web-responses-games-gamemediaitemresponsev2-) in Models. **Response example:** ```json { "data": [ { "assetTypeId": "...", "assetType": "...", "imageId": "...", "videoHash": "...", "videoTitle": "...", "approved": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://games.roblox.com/v2/games/{UNIVERSEID}/media" ``` ### GET `/v2/groups` Multi-get groups information by Ids. If a group comes back as null, it will not be returned in the response. **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupIds` | query | `integer[]` | Yes | The group Ids. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Web.Responses.Groups.GroupResponseV2_` - `400`: 2: Too many ids in request. 3: Ids could not be parsed from request. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Web.Responses.Groups.GroupResponseV2_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Web.Responses.Groups.GroupResponseV2_](#roblox-web-webapi-models-apiarrayresponse-roblox-web-responses-groups-groupresponsev2-) in Models. **Response example:** ```json { "data": [ { "id": "...", "name": "...", "description": "...", "owner": "...", "memberCount": "...", "created": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v2/groups?groupIds={VALUE}" ``` ### GET `/v2/groups/{groupId}/games` Gets experiences created by the specified group id. **Server:** `https://games.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The group Id | | `accessFilter` | query | `1 \| 2 \| 4` | No | The access type of the experiences. Valid values: `1`, `2`, `4` | | `limit` | query | `10 \| 25 \| 50 \| 100` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `Asc \| Desc` | No | The order the results are sorted in. Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Groups.Api.Models.Response.GroupExperienceResponse_` - `501`: 47: Code path is not implemented. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Groups.Api.Models.Response.GroupExperienceResponse_`) See [Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Groups.Api.Models.Response.GroupExperienceResponse_](#roblox-web-webapi-models-apipageresponse-roblox-groups-api-models-response-groupexperienceresponse-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "id": "...", "name": "...", "description": "...", "creator": "...", "rootPlace": "...", "created": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://games.roblox.com/v2/groups/{GROUPID}/games" ``` ### GET `/v2/groups/{groupId}/gamesV2` Gets experiences created by the specified group id. **Server:** `https://games.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | The group Id | | `accessFilter` | query | `1 \| 2 \| 4` | No | The access type of the experiences. Valid values: `1`, `2`, `4` | | `limit` | query | `10 \| 25 \| 50 \| 100` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `Asc \| Desc` | No | The order the results are sorted in. Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Groups.Api.Models.Response.GroupExperienceResponse_` - `501`: 47: Code path is not implemented. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Groups.Api.Models.Response.GroupExperienceResponse_`) See [Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Groups.Api.Models.Response.GroupExperienceResponse_](#roblox-web-webapi-models-apipageresponse-roblox-groups-api-models-response-groupexperienceresponse-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "id": "...", "name": "...", "description": "...", "creator": "...", "rootPlace": "...", "created": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://games.roblox.com/v2/groups/{GROUPID}/gamesV2" ``` ### GET `/v2/groups/{groupId}/transactions` **Server:** `https://economy.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer` | Yes | | | `exclusiveStartRequest` | query | `any` | No | | | `transactionType` | query | `any` | No | | **Responses:** - `200`: Success → `TransactionResponseApiPageResponse` **Response fields** (`TransactionResponseApiPageResponse`) See [TransactionResponseApiPageResponse](#transactionresponseapipageresponse) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "id": "...", "idHash": "...", "created": "...", "isPending": "...", "agent": "...", "details": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://economy.roblox.com/v2/groups/{GROUPID}/transactions" ``` ### DELETE `/v2/inventory/asset/{assetId}` Give up an asset owned by the authenticated user. Assets that are created by Roblox user or are limited edition are not eligible for deletion and will return NotEligibleForDelete. **Server:** `https://inventory.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetId` | path | `integer` | Yes | ID of the asset to delete. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `401`: 0: Authorization has been denied for this request. 4: You are not authorized. - `403`: 0: Token Validation Failed 2: You don't own the specified item. 3: The item is not allowed to be deleted. - `404`: 1: The item does not exist. - `500`: 0: An unknown error occured. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://inventory.roblox.com/v2/inventory/asset/{ASSETID}" ``` ### GET `/v2/marAssetHash/{marAssetHash}/marCheckSum/{marCheckSum}` **Server:** `https://assetdelivery.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `marAssetHash` | path | `string` | Yes | | | `marCheckSum` | path | `string` | Yes | | | `Accept-Encoding` | header | `string` | Yes | | | `Roblox-Place-Id` | header | `integer` | Yes | | | `AssetType` | header | `string` | Yes | | | `Accept` | header | `string` | Yes | | | `AssetFormat` | header | `string` | Yes | | | `Roblox-AssetFormat` | header | `string` | Yes | | | `skipSigningScripts` | query | `boolean` | No | | | `clientInsert` | query | `integer` | No | | | `scriptinsert` | query | `integer` | No | | | `modulePlaceId` | query | `integer` | No | | | `serverplaceid` | query | `integer` | No | | | `expectedAssetType` | query | `string` | No | | **Responses:** - `200`: OK → `Roblox.Web.Assets.AssetResponseItemV2` **Response fields** (`Roblox.Web.Assets.AssetResponseItemV2`) See [Roblox.Web.Assets.AssetResponseItemV2](#roblox-web-assets-assetresponseitemv2) in Models. **Response example:** ```json { "locations": [ { "assetFormat": "...", "location": "...", "assetMetadatas": "..." } ], "errors": [ { "Code": "...", "Message": "...", "CustomErrorCode": "..." } ], "requestId": "string", "isArchived": false, "assetTypeId": 0, "contentRepresentationSpecifier": { "format": "string", "majorVersion": "string", "fidelity": "string", "skipGenerationIfNotExist": false } } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://assetdelivery.roblox.com/v2/marAssetHash/{MARASSETHASH}/marCheckSum/{MARCHECKSUM}" ``` ### GET `/v2/ota-version/{binaryType}` Get OTA information for a specific binary type with a given version on some channel. Returns empty list if no updates are found or channel/application with the given version does not exist in CVS. **Server:** `https://clientsettings.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `binaryType` | path | `string` | Yes | Binary type of the application to get info for | | `channel` | query | `string` | No | Channel name. If not provided, production is assumed. | | `version` | query | `string` | No | Application version | | `tag` | query | `string` | No | Tag to filter results by. Only applicable to non-studio application types. | | `name` | query | `string` | No | Name to filter results by. Only applicable to non-studio application types. | **Responses:** - `200`: OK → `Roblox.ClientSettings.Api.Models.Response.OtaVersionResponse[]` - `400`: 2: Invalid binaryType. 4: Invalid app version. 6: Missing or invalid channel. 7: Unsupported binaryType. - `401`: 5: Not authorized to perform this action. **Response fields** (`Roblox.ClientSettings.Api.Models.Response.OtaVersionResponse[]`) See [Roblox.ClientSettings.Api.Models.Response.OtaVersionResponse](#roblox-clientsettings-api-models-response-otaversionresponse) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://clientsettings.roblox.com/v2/ota-version/{BINARYTYPE}" ``` ### POST `/v2/outfits/create` *(deprecated)* Creates a new outfit. Fails if any of the assetIds are not owned by the user, or not wearable types. The name property of the request is optional as one will be auto-generated when the request has a null name. please use v3/outfits/create **Server:** `https://avatar.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `Roblox-Place-Id` | header | `integer` | No | | **Request Body:** `application/json` — Type: `Roblox.Api.Avatar.Models.OutfitUpdateModelV2` See [Roblox.Api.Avatar.Models.OutfitUpdateModelV2](#roblox-api-avatar-models-outfitupdatemodelv2) in Models. **Request example:** ```json { "name": "string", "bodyColors": { "headColorId": 0, "torsoColorId": 0, "rightArmColorId": 0, "leftArmColorId": 0, "rightLegColorId": 0, "leftLegColorId": 0 }, "assets": [ { "id": "...", "meta": "..." } ], "scale": { "height": 0, "width": 0, "head": 0, "depth": 0, "proportion": 0, "bodyType": 0 }, "playerAvatarType": "string", "outfitType": 0 } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 3: Body colors must be valid BrickColor IDs 4: Invalid outfit name 5: Asset is not wearable by you and was not added to the outfit 7: Invalid Player Avatar Type. Valid types are R6 and R15 8: Invalid assetIds 9: Meta does not apply to specified asset type 10: Required meta is not provided for the specific asset type 12: Outfit type invalid or not permitted 13: Invalid Scale - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 1: You already have the maximum number of outfits - `500`: 6: An error occurred while creating the outfit **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://avatar.roblox.com/v2/outfits/create" \ -H "Content-Type: application/json" \ -d '{"name":"string","bodyColors":{"headColorId":0,"torsoColorId":0,"rightArmColorId":0,"leftArmColorId":0,"rightLegColorId":0,"leftLegColorId":0},"assets":[{"id":"...","meta":"..."}],"scale":{"height":0,"width":0,"head":0,"depth":0,"proportion":0,"bodyType":0},"playerAvatarType":"string","outfitType":0}' ``` ### PATCH `/v2/outfits/{userOutfitId}` *(deprecated)* Updates the contents of an outfit. Fails if the user does not own any of the assetIds or if they are not wearable asset types. Accepts partial updates. Please use PATCH v3/outfits/{userOutfitId} **Server:** `https://avatar.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userOutfitId` | path | `integer` | Yes | The user outfit id. | | `Roblox-Place-Id` | header | `integer` | No | | **Request Body:** `application/json` — Type: `Roblox.Api.Avatar.Models.OutfitUpdateModelV2` See [Roblox.Api.Avatar.Models.OutfitUpdateModelV2](#roblox-api-avatar-models-outfitupdatemodelv2) in Models. **Request example:** ```json { "name": "string", "bodyColors": { "headColorId": 0, "torsoColorId": 0, "rightArmColorId": 0, "leftArmColorId": 0, "rightLegColorId": 0, "leftLegColorId": 0 }, "assets": [ { "id": "...", "meta": "..." } ], "scale": { "height": 0, "width": 0, "head": 0, "depth": 0, "proportion": 0, "bodyType": 0 }, "playerAvatarType": "string", "outfitType": 0 } ``` **Responses:** - `200`: OK → `Roblox.Api.Avatar.Models.OutfitModel` - `400`: 3: Body colors must be valid BrickColor IDs 4: Invalid outfit name 5: Asset is not wearable by you 8: Invalid Player Avatar Type. Valid types are R6 and R15 11: Meta does not apply to specified asset type 12: Meta is required for this specific asset type 13: Invalid Outfit Type 14: Invalid scale - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: You don't have permission to update this outfit. - `404`: 1: The specified userOutfit does not exist! - `500`: 6: An error occurred while trying to update the outfit **Response fields** (`Roblox.Api.Avatar.Models.OutfitModel`) See [Roblox.Api.Avatar.Models.OutfitModel](#roblox-api-avatar-models-outfitmodel) in Models. **Response example:** ```json { "id": 0, "name": "string", "isEditable": false, "outfitType": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://avatar.roblox.com/v2/outfits/{USEROUTFITID}" \ -H "Content-Type: application/json" \ -d '{"name":"string","bodyColors":{"headColorId":0,"torsoColorId":0,"rightArmColorId":0,"leftArmColorId":0,"rightLegColorId":0,"leftLegColorId":0},"assets":[{"id":"...","meta":"..."}],"scale":{"height":0,"width":0,"head":0,"depth":0,"proportion":0,"bodyType":0},"playerAvatarType":"string","outfitType":0}' ``` ### GET `/v2/places/{placeId}` Gets the place configuration for the place with the id placeId **Server:** `https://develop.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `placeId` | path | `integer` | Yes | The place id for the place to be updated. | **Responses:** - `200`: OK → `Roblox.Api.Develop.Models.PlaceModelV2` - `401`: 0: Authorization has been denied for this request. - `403`: Authenticated user is not authorized to manage this place. - `404`: placeId Place not found. **Response fields** (`Roblox.Api.Develop.Models.PlaceModelV2`) See [Roblox.Api.Develop.Models.PlaceModelV2](#roblox-api-develop-models-placemodelv2) in Models. **Response example:** ```json { "maxPlayerCount": 0, "socialSlotType": "string", "customSocialSlotsCount": 0, "allowCopying": false, "currentSavedVersion": 0, "isAllGenresAllowed": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v2/places/{PLACEID}" ``` ### PATCH `/v2/places/{placeId}` Updates the place configuration for the place with the id placeId **Server:** `https://develop.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `placeId` | path | `integer` | Yes | The place id for the place to be updated. | **Request Body:** `application/json` — Type: `Roblox.Api.Develop.Models.PlaceConfigurationModelV2` See [Roblox.Api.Develop.Models.PlaceConfigurationModelV2](#roblox-api-develop-models-placeconfigurationmodelv2) in Models. **Request example:** ```json { "name": "string", "description": "string", "maxPlayerCount": 0, "socialSlotType": "string", "customSocialSlotsCount": 0, "allowCopying": false } ``` **Responses:** - `200`: OK → `Roblox.Api.Develop.Models.PlaceModelV2` - `401`: 0: Authorization has been denied for this request. - `403`: Authenticated user is not authorized to manage this place. 0: Token Validation Failed - `404`: placeId Place not found. **Response fields** (`Roblox.Api.Develop.Models.PlaceModelV2`) See [Roblox.Api.Develop.Models.PlaceModelV2](#roblox-api-develop-models-placemodelv2) in Models. **Response example:** ```json { "maxPlayerCount": 0, "socialSlotType": "string", "customSocialSlotsCount": 0, "allowCopying": false, "currentSavedVersion": 0, "isAllGenresAllowed": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v2/places/{PLACEID}" \ -H "Content-Type: application/json" \ -d '{ "name": "string", "description": "string", "maxPlayerCount": 0, "socialSlotType": "string", "customSocialSlotsCount": 0, "allowCopying": false }' ``` ### GET `/v2/push-notifications/chrome-manifest` Get Chrome Manifest to link GCM project to Chrome Browser **Server:** `https://notifications.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Api.Notifications.Models.ChromeManifestModel` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Api.Notifications.Models.ChromeManifestModel`) See [Roblox.Api.Notifications.Models.ChromeManifestModel](#roblox-api-notifications-models-chromemanifestmodel) in Models. **Response example:** ```json { "name": "string", "gcm_sender_id": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://notifications.roblox.com/v2/push-notifications/chrome-manifest" ``` ### POST `/v2/push-notifications/deregister-all-devices` De-register all devices to disable push notifications **Server:** `https://notifications.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Api.Notifications.Models.SuccessResponseModel` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Api.Notifications.Models.SuccessResponseModel`) See [Roblox.Api.Notifications.Models.SuccessResponseModel](#roblox-api-notifications-models-successresponsemodel) in Models. **Response example:** ```json { "statusMessage": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://notifications.roblox.com/v2/push-notifications/deregister-all-devices" ``` ### POST `/v2/push-notifications/deregister-current-device` De-register current device to disable push notifications **Server:** `https://notifications.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Api.Notifications.Models.SuccessResponseModel` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Api.Notifications.Models.SuccessResponseModel`) See [Roblox.Api.Notifications.Models.SuccessResponseModel](#roblox-api-notifications-models-successresponsemodel) in Models. **Response example:** ```json { "statusMessage": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://notifications.roblox.com/v2/push-notifications/deregister-current-device" ``` ### POST `/v2/push-notifications/deregister-current-device-ios-pushkit` De-register current device to disable pushkit notifications **Server:** `https://notifications.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Api.Notifications.Models.SuccessResponseModel` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Api.Notifications.Models.SuccessResponseModel`) See [Roblox.Api.Notifications.Models.SuccessResponseModel](#roblox-api-notifications-models-successresponsemodel) in Models. **Response example:** ```json { "statusMessage": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://notifications.roblox.com/v2/push-notifications/deregister-current-device-ios-pushkit" ``` ### GET `/v2/push-notifications/get-current-device-destination` Gets the current device destination **Server:** `https://notifications.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Api.Notifications.Models.GetCurrentPushDestinationResponseModel` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Api.Notifications.Models.GetCurrentPushDestinationResponseModel`) See [Roblox.Api.Notifications.Models.GetCurrentPushDestinationResponseModel](#roblox-api-notifications-models-getcurrentpushdestinationresponsemodel) in Models. **Response example:** ```json { "destination": { "user": { "name": "...", "userId": "..." }, "name": "string", "notificationToken": "string", "supportsUpdateNotifications": false, "userPushNotificationDestinationId": 0, "application": "string" }, "statusMessage": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://notifications.roblox.com/v2/push-notifications/get-current-device-destination" ``` ### GET `/v2/push-notifications/get-destinations` Gets valid destinations associated with the signed user **Server:** `https://notifications.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Api.Notifications.Models.GetPushDestinationsResponseModel` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Api.Notifications.Models.GetPushDestinationsResponseModel`) See [Roblox.Api.Notifications.Models.GetPushDestinationsResponseModel](#roblox-api-notifications-models-getpushdestinationsresponsemodel) in Models. **Response example:** ```json { "destinations": [ { "user": "...", "name": "...", "notificationToken": "...", "supportsUpdateNotifications": "...", "userPushNotificationDestinationId": "...", "application": "..." } ], "statusMessage": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://notifications.roblox.com/v2/push-notifications/get-destinations" ``` ### GET `/v2/push-notifications/metadata` Gets the corresponding metadata for the specified notification **Server:** `https://notifications.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `notificationToken` | query | `string` | Yes | Token for the notification | | `notificationId` | query | `string` | Yes | Id of the specified notification | **Responses:** - `200`: OK → `Roblox.Api.Notifications.Models.GetMetadataResponseModel` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Api.Notifications.Models.GetMetadataResponseModel`) See [Roblox.Api.Notifications.Models.GetMetadataResponseModel](#roblox-api-notifications-models-getmetadataresponsemodel) in Models. **Response example:** ```json { "metadata": { "notificationId": "string", "type": "string", "detail": "...", "fallbackDelivered": false }, "statusMessage": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://notifications.roblox.com/v2/push-notifications/metadata?notificationToken={VALUE}¬ificationId={VALUE}" ``` ### POST `/v2/push-notifications/register-android-native` Register Android Native for push notifications **Server:** `https://notifications.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Api.Notifications.Models.RegisterAndroidRequestModel` See [Roblox.Api.Notifications.Models.RegisterAndroidRequestModel](#roblox-api-notifications-models-registerandroidrequestmodel) in Models. **Request example:** ```json { "notificationToken": "string", "authorizeForUser": false, "oldNotificationToken": "string", "deviceName": "string" } ``` **Responses:** - `200`: OK → `Roblox.Api.Notifications.Models.RegistrationResponseModel` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Api.Notifications.Models.RegistrationResponseModel`) See [Roblox.Api.Notifications.Models.RegistrationResponseModel](#roblox-api-notifications-models-registrationresponsemodel) in Models. **Response example:** ```json { "registration": { "userPushNotificationDestinationId": 0, "name": "string", "notificationToken": "string", "application": "string", "platform": "ChromeOnDesktop" }, "statusMessage": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://notifications.roblox.com/v2/push-notifications/register-android-native" \ -H "Content-Type: application/json" \ -d '{ "notificationToken": "string", "authorizeForUser": false, "oldNotificationToken": "string", "deviceName": "string" }' ``` ### POST `/v2/push-notifications/register-ios-native` Registers IOS device for push notifications **Server:** `https://notifications.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Api.Notifications.Models.RegisterIOSNativeRequestModel` See [Roblox.Api.Notifications.Models.RegisterIOSNativeRequestModel](#roblox-api-notifications-models-registeriosnativerequestmodel) in Models. **Request example:** ```json { "notificationToken": "string", "destinationIdentifier": "string", "authorizeForUser": false, "oldNotificationToken": "string", "deviceName": "string" } ``` **Responses:** - `200`: OK → `Roblox.Api.Notifications.Models.RegistrationResponseModel` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Api.Notifications.Models.RegistrationResponseModel`) See [Roblox.Api.Notifications.Models.RegistrationResponseModel](#roblox-api-notifications-models-registrationresponsemodel) in Models. **Response example:** ```json { "registration": { "userPushNotificationDestinationId": 0, "name": "string", "notificationToken": "string", "application": "string", "platform": "ChromeOnDesktop" }, "statusMessage": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://notifications.roblox.com/v2/push-notifications/register-ios-native" \ -H "Content-Type: application/json" \ -d '{ "notificationToken": "string", "destinationIdentifier": "string", "authorizeForUser": false, "oldNotificationToken": "string", "deviceName": "string" }' ``` ### POST `/v2/push-notifications/register-ios-pushkit` Registers IOS device for pushkit notifications **Server:** `https://notifications.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Api.Notifications.Models.RegisterIOSPushKitRequestModel` See [Roblox.Api.Notifications.Models.RegisterIOSPushKitRequestModel](#roblox-api-notifications-models-registeriospushkitrequestmodel) in Models. **Request example:** ```json { "notificationToken": "string", "destinationIdentifier": "string", "authorizeForUser": false, "oldNotificationToken": "string", "deviceName": "string" } ``` **Responses:** - `200`: OK → `Roblox.Api.Notifications.Models.RegistrationResponseModel` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Api.Notifications.Models.RegistrationResponseModel`) See [Roblox.Api.Notifications.Models.RegistrationResponseModel](#roblox-api-notifications-models-registrationresponsemodel) in Models. **Response example:** ```json { "registration": { "userPushNotificationDestinationId": 0, "name": "string", "notificationToken": "string", "application": "string", "platform": "ChromeOnDesktop" }, "statusMessage": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://notifications.roblox.com/v2/push-notifications/register-ios-pushkit" \ -H "Content-Type: application/json" \ -d '{ "notificationToken": "string", "destinationIdentifier": "string", "authorizeForUser": false, "oldNotificationToken": "string", "deviceName": "string" }' ``` ### POST `/v2/sales/sales-report-download` **Server:** `https://economy.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json-patch+json` — Type: `any` **Responses:** - `200`: Success → `HttpResponseMessage` **Response fields** (`HttpResponseMessage`) See [HttpResponseMessage](#httpresponsemessage) in Models. **Response example:** ```json { "version": "string", "content": "...", "statusCode": "...", "reasonPhrase": "string", "headers": [ { "key": "...", "value": "..." } ], "trailingHeaders": [ { "key": "...", "value": "..." } ] } ``` **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://economy.roblox.com/v2/sales/sales-report-download" \ -H "Content-Type: application/json" \ -d '"..."' ``` ### GET `/v2/search/items/details` Search for catalog items. This endpoint is for search by item type ids. **Server:** `https://catalog.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `Taxonomy` | query | `string` | No | | | `AssetTypeIds` | query | `integer[]` | No | | | `BundleTypeIds` | query | `integer[]` | No | | | `CategoryFilter` | query | `integer enum (8 values)` | No | Valid values: `0`, `1`, `2`, `3`, `4`, `5`, `6`, `7` | | `SortAggregation` | query | `integer enum (6 values)` | No | Valid values: `0`, `1`, `2`, `3`, `4`, `5` | | `SortType` | query | `integer enum (6 values)` | No | Valid values: `0`, `1`, `2`, `3`, `4`, `5` | | `CreatorType` | query | `0 \| 1 \| 2` | No | Valid values: `0`, `1`, `2` | | `CreatorTargetId` | query | `integer` | No | | | `CreatorName` | query | `string` | No | | | `MaxPrice` | query | `integer` | No | | | `MinPrice` | query | `integer` | No | | | `Keyword` | query | `string` | No | | | `IncludeNotForSale` | query | `boolean` | No | | | `TriggeredByTopicDiscovery` | query | `boolean` | No | | | `SalesTypeFilter` | query | `0 \| 1 \| 2 \| 3 \| 4` | No | Valid values: `0`, `1`, `2`, `3`, `4` | | `Topics` | query | `string` | No | The input topics format is split by ",". E.g "topics=cat,hat,red". | | `limit` | query | `10 \| 28 \| 30 \| 60 \| 120` | No | The number of results per request. Valid values: `10`, `28`, `30`, `60`, `120` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `Desc` | No | The order the results are sorted in. Valid values: `Desc` | **Responses:** - `200`: OK → `Roblox.Catalog.Api.CatalogSearchPageResponse_Roblox.Catalog.Api.CatalogSearchDetailedResponseItemV2_` - `400`: 1: Category subcategory selection not supported. 2: Creator id not found. 3: Creator type not found or cannot search by CreatorTargetId with CreatorType.All 4: Genre not found. 5: Sort combination not supported. 6: Invalid price range. 10: StartRequest is invalid. - `403`: 7: User is unauthorized. 22: In-experience search is denied for this place or universe. - `429`: 8: The flood limit has been exceeded. 8: The flood limit has been exceeded. - `503`: 18: Search request timed out **Response fields** (`Roblox.Catalog.Api.CatalogSearchPageResponse_Roblox.Catalog.Api.CatalogSearchDetailedResponseItemV2_`) See [Roblox.Catalog.Api.CatalogSearchPageResponse_Roblox.Catalog.Api.CatalogSearchDetailedResponseItemV2_](#roblox-catalog-api-catalogsearchpageresponse-roblox-catalog-api-catalogsearchdetailedresponseitemv2-) in Models. **Response example:** ```json { "keyword": "string", "elasticsearchDebugInfo": { "elasticsearchQuery": "string", "isFromCache": false, "indexName": "string", "isTerminatedEarly": false, "isForceTerminationEnabledByRequest": false, "searchResultDataSource": "string" }, "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "bundledItems": "...", "taxonomy": "...", "itemCreatedUtc": "...", "discountInformation": "...", "id": "...", "itemType": "..." } ] } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://catalog.roblox.com/v2/search/items/details" ``` ### GET `/v2/sponsored-campaigns` Gets a page of Roblox.AdConfiguration.Api.SponsoredCampaignModel with specified input parameters. **Server:** `https://adconfiguration.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `campaignTargetType` | query | `0 \| 1 \| 2 \| 3` | Yes | The campaign target type enum value Valid values: `0`, `1`, `2`, `3` | | `campaignTargetId` | query | `integer` | Yes | The id of the campaign target | | `includeReportingStats` | query | `boolean` | No | Indicates whether to include reporting stats in the response | | `isArchived` | query | `boolean` | No | Indicates whether to retrieve archived ads | | `pageCursor` | query | `string` | No | The cursor of the page to retrieve. If empty, fetches the first page | **Responses:** - `200`: OK → `Roblox.AdConfiguration.Api.GetSponsoredCampaignsResponse` - `400`: 22: Invalid campaign target ID. 23: Invalid campaign target type. - `401`: 0: Authorization has been denied for this request. - `503`: 1: This feature is disabled. **Response fields** (`Roblox.AdConfiguration.Api.GetSponsoredCampaignsResponse`) See [Roblox.AdConfiguration.Api.GetSponsoredCampaignsResponse](#roblox-adconfiguration-api-getsponsoredcampaignsresponse) in Models. **Response example:** ```json { "sponsoredCampaigns": [ { "adId": "...", "adSetId": "...", "adName": "...", "adStatus": "...", "creativeType": "...", "creativeTargetId": "..." } ], "previousPageCursor": "string", "nextPageCursor": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://adconfiguration.roblox.com/v2/sponsored-campaigns?campaignTargetType={VALUE}&campaignTargetId={VALUE}" ``` ### POST `/v2/sponsored-campaigns/create` Creates a complete ad. Including ad campaign, ad set, escrow, and the ad. Currently intended for creation of sponsorships only. **Server:** `https://adconfiguration.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.AdConfiguration.Api.Models.CreateSponsoredCampaignRequest` See [Roblox.AdConfiguration.Api.Models.CreateSponsoredCampaignRequest](#roblox-adconfiguration-api-models-createsponsoredcampaignrequest) in Models. **Request example:** ```json { "campaignTargetId": 0, "campaignTargetType": 0, "targetGender": 1, "targetAgeBracket": 1, "startDate": "2024-01-01T00:00:00Z", "endDate": "2024-01-01T00:00:00Z" } ``` **Responses:** - `200`: OK → `integer` - `400`: 2: Daily budget is lower than minimum allowed. 3: Total budget must be greater than 0. 4: Ad name cannot be empty. 5: Start date must not be a future date. 6: End date must be a future date. 7: Start date must be earlier than end date. 8: Total budget does not match daily spend and number of days being scheduled 9: Cannot load the universe for the specified universe id. 11: Invalid target age bracket. 12: Invalid target gender. 13: Invalid target device type. 14: Invalid ad set id. 15: Ad name cannot exceed 255 characters. 16: Insufficient Robux balance. 17: Name has already been taken. 18: Daily budget is higher than maximum allowed. 19: Invalid group id. 20: Number of days scheduled exceeded maximum days allowed. 21: Your experience is currently not eligible for advertising. 22: Invalid campaign target ID. 23: Invalid campaign target type. 24: Invalid creative ID. 25: Invalid creative type. 28: Total budget must be campaign duration * daily bid 29: The target is not eligible for new campaigns 30: Invalid user ID - `401`: 0: Authorization has been denied for this request. 10: Insufficient permissions. - `403`: 0: Token Validation Failed - `500`: 0: An unknown error occurred. 31: Internal server error - `503`: 1: This feature is disabled. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://adconfiguration.roblox.com/v2/sponsored-campaigns/create" \ -H "Content-Type: application/json" \ -d '{ "campaignTargetId": 0, "campaignTargetType": 0, "targetGender": 1, "targetAgeBracket": 1, "startDate": "2024-01-01T00:00:00Z", "endDate": "2024-01-01T00:00:00Z" }' ``` ### GET `/v2/sponsored-campaigns/eligible-asset-type-ids` Get all asset type IDs that are eligible to be sponsored. **Server:** `https://adconfiguration.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `integer[]` - `401`: 0: Authorization has been denied for this request. - `403`: 10: Insufficient permissions. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://adconfiguration.roblox.com/v2/sponsored-campaigns/eligible-asset-type-ids" ``` ### POST `/v2/sponsored-campaigns/eligible-campaign-targets` Returns a collection of Roblox.AdConfiguration.Api.Models.CampaignTargetModel that the user is authorized to sponsor, ordered by most recently advertised **Server:** `https://adconfiguration.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.AdConfiguration.Api.Models.GetEligibleCampaignTargetsRequest` See [Roblox.AdConfiguration.Api.Models.GetEligibleCampaignTargetsRequest](#roblox-adconfiguration-api-models-geteligiblecampaigntargetsrequest) in Models. **Request example:** ```json { "campaignTargetTypes": [ 0 ], "groupId": 0 } ``` **Responses:** - `200`: OK → `Roblox.AdConfiguration.Api.Models.GetCampaignTargetsResponse` - `400`: 19: Invalid group id. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 10: Insufficient permissions. **Response fields** (`Roblox.AdConfiguration.Api.Models.GetCampaignTargetsResponse`) See [Roblox.AdConfiguration.Api.Models.GetCampaignTargetsResponse](#roblox-adconfiguration-api-models-getcampaigntargetsresponse) in Models. **Response example:** ```json { "campaignTargetModels": [ { "campaignTargetType": "...", "campaignTargetId": "...", "name": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://adconfiguration.roblox.com/v2/sponsored-campaigns/eligible-campaign-targets" \ -H "Content-Type: application/json" \ -d '{ "campaignTargetTypes": [ 0 ], "groupId": 0 }' ``` ### GET `/v2/sponsored-campaigns/multi-get-can-user-sponsor` Checks whether the targets are eligible for sponsorship, and if the user is authorized to sponsor the targets. **Server:** `https://adconfiguration.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `campaignTargetType` | query | `0 \| 1 \| 2 \| 3` | Yes | Ads.Management.Service.CampaignTargetType. Valid values: `0`, `1`, `2`, `3` | | `campaignTargetIds` | query | `integer[]` | Yes | The IDs of the campaign targets. | **Responses:** - `200`: OK → `object` - `400`: Bad Request - `401`: 0: Authorization has been denied for this request. - `403`: Forbidden - `500`: Server Error **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://adconfiguration.roblox.com/v2/sponsored-campaigns/multi-get-can-user-sponsor?campaignTargetType={VALUE}&campaignTargetIds={VALUE}" ``` ### POST `/v2/sponsored-campaigns/stop` Stops a sponsored campaign / ad (ad set) from running. Initiated by a user. **Server:** `https://adconfiguration.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.AdConfiguration.Api.Models.StopSponsoredCampaignRequest` See [Roblox.AdConfiguration.Api.Models.StopSponsoredCampaignRequest](#roblox-adconfiguration-api-models-stopsponsoredcampaignrequest) in Models. **Request example:** ```json { "adSetId": 0 } ``` **Responses:** - `200`: OK → `integer` - `400`: 10: Insufficient permissions. 14: Invalid ad set id. 31: Internal server error - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `503`: 1: This feature is disabled. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://adconfiguration.roblox.com/v2/sponsored-campaigns/stop" \ -H "Content-Type: application/json" \ -d '{ "adSetId": 0 }' ``` ### GET `/v2/sponsored-games` Gets a page of Roblox.AdConfiguration.Api.SponsoredGameV2Model with specified input parameters. **Server:** `https://adconfiguration.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | query | `integer` | Yes | The universe id of the ad campaign. | | `includeReportingStats` | query | `boolean` | No | Indicates whether to include reporting stats in the response. | | `isArchived` | query | `boolean` | No | Indicates whether to retrieve archived ads. | | `pageCursor` | query | `string` | No | The cursor of the page to retrieve. | **Responses:** - `200`: OK → `Roblox.AdConfiguration.Api.GetSponsoredGamesResponse` - `400`: 9: Cannot load the universe for the specified universe id. 9: Cannot load the universe for the specified universe id. - `401`: 0: Authorization has been denied for this request. - `503`: 1: This feature is disabled. **Response fields** (`Roblox.AdConfiguration.Api.GetSponsoredGamesResponse`) See [Roblox.AdConfiguration.Api.GetSponsoredGamesResponse](#roblox-adconfiguration-api-getsponsoredgamesresponse) in Models. **Response example:** ```json { "sponsoredGames": [ { "adId": "...", "adSetId": "...", "adName": "...", "adStatus": "...", "creativeType": "...", "creativeTargetId": "..." } ], "previousPageCursor": "string", "nextPageCursor": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://adconfiguration.roblox.com/v2/sponsored-games?universeId={VALUE}" ``` ### POST `/v2/sponsored-games/create` Creates a new sponsored game ad with specified input parameters. **Server:** `https://adconfiguration.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.AdConfiguration.Api.CreateSponsoredGameV2Request` See [Roblox.AdConfiguration.Api.CreateSponsoredGameV2Request](#roblox-adconfiguration-api-createsponsoredgamev2request) in Models. **Request example:** ```json { "universeId": 0, "targetGender": 1, "targetAgeBracket": 1, "budgetInRobux": 0, "startDate": "2024-01-01T00:00:00Z", "endDate": "2024-01-01T00:00:00Z" } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 2: Daily budget is lower than minimum allowed. 3: Total budget must be greater than 0. 4: Ad name cannot be empty. 5: Start date must not be a future date. 6: End date must be a future date. 7: Start date must be earlier than end date. 8: Total budget does not match daily spend and number of days being scheduled 9: Cannot load the universe for the specified universe id. 11: Invalid target age bracket. 12: Invalid target gender. 13: Invalid target device type. 14: Invalid ad set id. 15: Ad name cannot exceed 255 characters. 16: Insufficient Robux balance. 17: Name has already been taken. 18: Daily budget is higher than maximum allowed. 19: Invalid group id. 20: Number of days scheduled exceeded maximum days allowed. 21: Your experience is currently not eligible for advertising. 22: Invalid campaign target ID. 23: Invalid campaign target type. 24: Invalid creative ID. 25: Invalid creative type. 28: Total budget must be campaign duration * daily bid 29: The target is not eligible for new campaigns 30: Invalid user ID - `401`: 0: Authorization has been denied for this request. 10: Insufficient permissions. - `403`: 0: Token Validation Failed - `500`: 0: An unknown error occurred. 31: Internal server error - `503`: 1: This feature is disabled. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://adconfiguration.roblox.com/v2/sponsored-games/create" \ -H "Content-Type: application/json" \ -d '{ "universeId": 0, "targetGender": 1, "targetAgeBracket": 1, "budgetInRobux": 0, "startDate": "2024-01-01T00:00:00Z", "endDate": "2024-01-01T00:00:00Z" }' ``` ### POST `/v2/sponsored-games/stop` To stop a sponsored-game ad (ad set) from running, initiated by a user. **Server:** `https://adconfiguration.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.AdConfiguration.Api.Models.StopSponsoredGameV2Request` See [Roblox.AdConfiguration.Api.Models.StopSponsoredGameV2Request](#roblox-adconfiguration-api-models-stopsponsoredgamev2request) in Models. **Request example:** ```json { "adSetId": 0 } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 14: Invalid ad set id. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 10: Insufficient permissions. - `503`: 1: This feature is disabled. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://adconfiguration.roblox.com/v2/sponsored-games/stop" \ -H "Content-Type: application/json" \ -d '{ "adSetId": 0 }' ``` ### GET `/v2/sponsored-games/universes` Gets a list of universes for the authenticated user, or the given group, ordered by most recently created sponsored game ads. **Server:** `https://adconfiguration.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | query | `integer` | No | The group id, if applicable. | **Responses:** - `200`: OK → `Roblox.AdConfiguration.Api.Models.GetRecentAdsRankedUniversesResponse` - `400`: 19: Invalid group id. - `401`: 0: Authorization has been denied for this request. - `403`: 10: Insufficient permissions. **Response fields** (`Roblox.AdConfiguration.Api.Models.GetRecentAdsRankedUniversesResponse`) See [Roblox.AdConfiguration.Api.Models.GetRecentAdsRankedUniversesResponse](#roblox-adconfiguration-api-models-getrecentadsrankeduniversesresponse) in Models. **Response example:** ```json { "universes": [ { "id": "...", "name": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://adconfiguration.roblox.com/v2/sponsored-games/universes" ``` ### POST `/v2/stream-notifications/clear-unread` Clears the unread Notification stream count **Server:** `https://notifications.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Api.Notifications.Models.SuccessResponseModel` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Api.Notifications.Models.SuccessResponseModel`) See [Roblox.Api.Notifications.Models.SuccessResponseModel](#roblox-api-notifications-models-successresponsemodel) in Models. **Response example:** ```json { "statusMessage": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://notifications.roblox.com/v2/stream-notifications/clear-unread" ``` ### GET `/v2/stream-notifications/get-latest-game-updates` Get the latest non aggregated Game Updates sent to the logged in user **Server:** `https://notifications.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeIds` | query | `integer[]` | Yes | List of universe IDs | | `sinceDateTime` | query | `string` | No | For retrieving only updates that created after a time point. | **Responses:** - `200`: OK → `Roblox.Api.Notifications.Models.GameUpdateNotificationModel[]` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Api.Notifications.Models.GameUpdateNotificationModel[]`) See [Roblox.Api.Notifications.Models.GameUpdateNotificationModel](#roblox-api-notifications-models-gameupdatenotificationmodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://notifications.roblox.com/v2/stream-notifications/get-latest-game-updates?universeIds={VALUE}" ``` ### GET `/v2/stream-notifications/get-recent` Gets the recent entries from the notification stream **Server:** `https://notifications.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `startIndex` | query | `integer` | No | Index to start the entries from. (Optional : Defaults to 0 which means the most recent entry) | | `maxRows` | query | `integer` | No | Number of entries to be returned. (Optional : Defaults to 10 entries) | **Responses:** - `200`: OK → `Roblox.Api.Notifications.Models.NotificationStreamEntriesModel[]` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Api.Notifications.Models.NotificationStreamEntriesModel[]`) See [Roblox.Api.Notifications.Models.NotificationStreamEntriesModel](#roblox-api-notifications-models-notificationstreamentriesmodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://notifications.roblox.com/v2/stream-notifications/get-recent" ``` ### GET `/v2/stream-notifications/metadata` Get Notification Stream metadata. **Server:** `https://notifications.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Api.Notifications.Models.ResponseModels.NotificationStreamMetadataResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Api.Notifications.Models.ResponseModels.NotificationStreamMetadataResponse`) See [Roblox.Api.Notifications.Models.ResponseModels.NotificationStreamMetadataResponse](#roblox-api-notifications-models-responsemodels-notificationstreammetadataresponse) in Models. **Response example:** ```json { "bannerDismissTimeSpan": 0, "signalRDisconnectionResponseInMilliseconds": 0, "canLaunchGameFromGameUpdate": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://notifications.roblox.com/v2/stream-notifications/metadata" ``` ### GET `/v2/stream-notifications/unread-count` Gets the count of unread Notification stream entries **Server:** `https://notifications.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Api.Notifications.Models.UnreadStreamNotificationsModel` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Api.Notifications.Models.UnreadStreamNotificationsModel`) See [Roblox.Api.Notifications.Models.UnreadStreamNotificationsModel](#roblox-api-notifications-models-unreadstreamnotificationsmodel) in Models. **Response example:** ```json { "unreadNotifications": 0, "statusMessage": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://notifications.roblox.com/v2/stream-notifications/unread-count" ``` ### GET `/v2/supported-languages/games/{gameId}` Get the supported languages for a game. **Server:** `https://gameinternationalization.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer` | Yes | The id of the game. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.LanguageWithLocales_` - `400`: 14: Invalid game id - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.LanguageWithLocales_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.GameInternationalization.Api.LanguageWithLocales_](#roblox-web-webapi-models-apiarrayresponse-roblox-gameinternationalization-api-languagewithlocales-) in Models. **Response example:** ```json { "data": [ { "languageFamily": "...", "childLocales": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v2/supported-languages/games/{GAMEID}" ``` ### DELETE `/v2/teamtest/{placeId}` Close a game instance that is being used for team testing **Server:** `https://develop.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `placeId` | path | `integer` | Yes | The Id of the place we are setting the metadata for. | | `gameId` | query | `string` | Yes | the Guid of the game instance System.Guid | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v2/teamtest/{PLACEID}?gameId={VALUE}" ``` ### POST `/v2/trades/send` Sends a new trade. **Server:** `https://trades.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Trades.Api.Models.V2.NewTradeResponse` - `401`: 0: Authorization has been denied for this request. 4: You are not authorized to modify this trade. - `403`: 0: Token Validation Failed - `404`: 0: An unknown error occured. **Response fields** (`Roblox.Trades.Api.Models.V2.NewTradeResponse`) See [Roblox.Trades.Api.Models.V2.NewTradeResponse](#roblox-trades-api-models-v2-newtraderesponse) in Models. **Response example:** ```json { "tradeId": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://trades.roblox.com/v2/trades/send" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### GET `/v2/trades/{tradeId}` Gets the details of a trade. **Server:** `https://trades.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `tradeId` | path | `integer` | Yes | The id of the trade. | **Responses:** - `200`: OK → `Roblox.Trades.Api.Models.V2.TradeDetailsResponse` - `400`: 0: An unknown error occured. - `401`: 0: Authorization has been denied for this request. 4: You are not authorized to modify this trade. - `403`: 4: You are not authorized to modify this trade. - `404`: 0: An unknown error occured. **Response fields** (`Roblox.Trades.Api.Models.V2.TradeDetailsResponse`) See [Roblox.Trades.Api.Models.V2.TradeDetailsResponse](#roblox-trades-api-models-v2-tradedetailsresponse) in Models. **Response example:** ```json { "tradeId": 0, "status": "Unknown", "participantAOffer": { "user": { "id": "...", "name": "...", "displayName": "..." }, "robux": 0, "items": [ "..." ] }, "participantBOffer": { "user": { "id": "...", "name": "...", "displayName": "..." }, "robux": 0, "items": [ "..." ] } } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://trades.roblox.com/v2/trades/{TRADEID}" ``` ### POST `/v2/trades/{tradeId}/counter` Counters an existing trade. **Server:** `https://trades.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `tradeId` | path | `integer` | Yes | | **Responses:** - `200`: OK → `Roblox.Trades.Api.Models.V2.NewTradeResponse` - `401`: 0: Authorization has been denied for this request. 4: You are not authorized to modify this trade. - `403`: 0: Token Validation Failed - `404`: 0: An unknown error occured. **Response fields** (`Roblox.Trades.Api.Models.V2.NewTradeResponse`) See [Roblox.Trades.Api.Models.V2.NewTradeResponse](#roblox-trades-api-models-v2-newtraderesponse) in Models. **Response example:** ```json { "tradeId": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://trades.roblox.com/v2/trades/{TRADEID}/counter" \ -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` ### PATCH `/v2/universes/{universeId}/configuration` Update universe settings for an owned universe. V2 Contains data for avatar scale and asset override. **Server:** `https://develop.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universeId. | **Request Body:** `application/json` — Type: `Roblox.Api.Develop.Models.UniverseSettingsRequestV2` See [Roblox.Api.Develop.Models.UniverseSettingsRequestV2](#roblox-api-develop-models-universesettingsrequestv2) in Models. **Request example:** ```json { "allowPrivateServers": false, "privateServerPrice": 0, "name": "string", "description": "string", "universeAvatarType": 1, "universeAnimationType": 1 } ``` **Responses:** - `200`: OK → `Roblox.Api.Develop.Models.UniverseSettingsResponseV2` - `400`: 1: The universe does not exist. 3: Invalid UniverseAvatarType. 4: Invalid UniverseScaleType. 5: Invalid UniverseAnimationType. 6: Invalid UniverseCollisionType. 7: New universe name or description has been rejected. 8: New universe name is too long. 10: Invalid UniverseBodyType. 11: Invalid UniverseJointPositioningType. 12: The universe has no root place. 15: Price is required when isForSale is true. 16: This game cannot be offered for sale because it is not public. 17: This game cannot be offered for sale because it has private servers enabled. 18: The game price is outside of the allowed range. 19: Invalid genre. 20: The request body is missing. 21: Invalid device type. 22: Invalid asset type. 23: Invalid value, the min must be less than or equal to the max 24: Invalid scale value 28: OptIn/Out Regions Not Supported. 41: You cannot change the private server price again so soon after the previous change. Please try again later. 44: The provided audience configuration is invalid. Ensure the audience list contains only supported audience values. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: You are not authorized to configure this universe. 14: You are not authorized to sell games. 29: Luobu app terms of service user agreement is missing. 30: Unknown error while updating Opt in out region. 45: The creator of this experience is not eligible to set this audience. - `409`: 9: Failed to shutdown all intances of game after changing AvatarType. The change has been reverted. - `500`: 43: Failed to update the audience configuration. The change was not applied. Please try again. **Response fields** (`Roblox.Api.Develop.Models.UniverseSettingsResponseV2`) See [Roblox.Api.Develop.Models.UniverseSettingsResponseV2](#roblox-api-develop-models-universesettingsresponsev2) in Models. **Response example:** ```json { "allowPrivateServers": false, "privateServerPrice": 0, "optInRegions": [ { "region": "...", "status": "..." } ], "isMeshTextureApiAccessAllowed": false, "isRewardedOnDemandAdsAllowed": false, "id": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v2/universes/{UNIVERSEID}/configuration" \ -H "Content-Type: application/json" \ -d '{ "allowPrivateServers": false, "privateServerPrice": 0, "name": "string", "description": "string", "universeAvatarType": 1, "universeAnimationType": 1 }' ``` ### GET `/v2/user-channel` Get channel name for currently logged in user **Server:** `https://clientsettings.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `binaryType` | query | `string` | No | | **Responses:** - `200`: OK → `Roblox.ClientSettings.Api.Models.Response.UserChannelResponse` **Response fields** (`Roblox.ClientSettings.Api.Models.Response.UserChannelResponse`) See [Roblox.ClientSettings.Api.Models.Response.UserChannelResponse](#roblox-clientsettings-api-models-response-userchannelresponse) in Models. **Response example:** ```json { "channelName": "string", "channelAssignmentType": 0, "token": "string", "program": { "name": "string", "id": "string" }, "isFlagOnly": false } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://clientsettings.roblox.com/v2/user-channel" ``` ### GET `/v2/users/me/can-trade` Checks if the calling user can trade with others. **Server:** `https://trades.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Trades.Api.Models.V2.CanTradeResponse` - `401`: 0: Authorization has been denied for this request. 4: You are not authorized to modify this trade. **Response fields** (`Roblox.Trades.Api.Models.V2.CanTradeResponse`) See [Roblox.Trades.Api.Models.V2.CanTradeResponse](#roblox-trades-api-models-v2-cantraderesponse) in Models. **Response example:** ```json { "userId": 0, "canTrade": false, "tradeEligibility": "Unknown" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://trades.roblox.com/v2/users/me/can-trade" ``` ### GET `/v2/users/{userId}/can-trade-with` Checks if the user can trade with a specific user. **Server:** `https://trades.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | | **Responses:** - `200`: OK → `Roblox.Trades.Api.Models.V2.CanTradeWithResponse` - `401`: 0: Authorization has been denied for this request. 4: You are not authorized to modify this trade. **Response fields** (`Roblox.Trades.Api.Models.V2.CanTradeWithResponse`) See [Roblox.Trades.Api.Models.V2.CanTradeWithResponse](#roblox-trades-api-models-v2-cantradewithresponse) in Models. **Response example:** ```json { "userId": 0, "targetUserId": 0, "canTrade": false, "mutualTradeEligibility": "Unknown" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://trades.roblox.com/v2/users/{USERID}/can-trade-with" ``` ### GET `/v2/users/{userId}/favorite/games` Gets users favorite games. **Server:** `https://games.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user Id. | | `accessFilter` | query | `1 \| 2 \| 4` | No | Filtering option via access level. Valid values: `1`, `2`, `4` | | `limit` | query | `10 \| 25 \| 50 \| 100` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `Asc \| Desc` | No | The order the results are sorted in. Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Web.Responses.Games.GameFavoriteResponseModel_` **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Web.Responses.Games.GameFavoriteResponseModel_`) See [Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Web.Responses.Games.GameFavoriteResponseModel_](#roblox-web-webapi-models-apipageresponse-roblox-web-responses-games-gamefavoriteresponsemodel-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "price": "...", "id": "...", "name": "...", "description": "...", "creator": "...", "rootPlace": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://games.roblox.com/v2/users/{USERID}/favorite/games" ``` ### GET `/v2/users/{userId}/games` Gets games created by the specified user. **Server:** `https://games.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user Id. | | `accessFilter` | query | `1 \| 2 \| 4` | No | Filtering option via access level. Valid values: `1`, `2`, `4` | | `limit` | query | `10 \| 25 \| 50` | No | The number of results per request. Valid values: `10`, `25`, `50` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `Asc \| Desc` | No | The order the results are sorted in. Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Web.Responses.Games.GameResponseV2_` **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Web.Responses.Games.GameResponseV2_`) See [Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Web.Responses.Games.GameResponseV2_](#roblox-web-webapi-models-apipageresponse-roblox-web-responses-games-gameresponsev2-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "id": "...", "name": "...", "description": "...", "creator": "...", "rootPlace": "...", "created": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://games.roblox.com/v2/users/{USERID}/games" ``` ### GET `/v2/users/{userId}/groups/roles` Gets a list of all group roles for groups the specified user is in. **Server:** `https://groups.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user id. | | `includeLocked` | query | `boolean` | Yes | | | `includeNotificationPreferences` | query | `boolean` | Yes | | | `discoveryType` | query | `0 \| 1` | Yes | Valid values: `0`, `1` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Groups.Api.GroupMembershipResponse_` - `400`: 3: The user is invalid or does not exist. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Groups.Api.GroupMembershipResponse_`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse_Roblox.Groups.Api.GroupMembershipResponse_](#roblox-web-webapi-models-apiarrayresponse-roblox-groups-api-groupmembershipresponse-) in Models. **Response example:** ```json { "data": [ { "group": "...", "role": "...", "isNotificationsEnabled": "...", "notificationPreferences": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v2/users/{USERID}/groups/roles?includeLocked={VALUE}&includeNotificationPreferences={VALUE}&discoveryType={VALUE}" ``` ### GET `/v2/users/{userId}/inventory` Get user's inventory by multiple Roblox.Platform.Assets.AssetType. GamePass and Badges not allowed. **Server:** `https://inventory.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The inventory owner's userId. | | `assetTypes` | query | `string enum (85 values)[]` | Yes | The asset types to query. | | `filterDisapprovedAssets` | query | `boolean` | No | Filters moderated assets when enabled. | | `showApprovedOnly` | query | `boolean` | No | Filters moderated assets and assets pending review when enabled. | | `limit` | query | `10 \| 25 \| 50 \| 100` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `Asc \| Desc` | No | The order the results are sorted in. Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Inventory.Api.V2.UserAssetItemModelV2_` - `400`: 1: Invalid user Id. 2: Invalid asset type Id. - `403`: 3: Insufficient permission. 4: You are not authorized to view this user's inventory. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Inventory.Api.V2.UserAssetItemModelV2_`) See [Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Inventory.Api.V2.UserAssetItemModelV2_](#roblox-web-webapi-models-apipageresponse-roblox-inventory-api-v2-userassetitemmodelv2-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "assetId": "...", "name": "...", "assetType": "...", "created": "..." } ] } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://inventory.roblox.com/v2/users/{USERID}/inventory?assetTypes={VALUE}" ``` ### GET `/v2/users/{userId}/inventory/{assetTypeId}` Gets user's inventory based on specific asset type **Server:** `https://inventory.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user Id of the inventory owner | | `assetTypeId` | path | `integer` | Yes | The asset type Id of the items to get | | `limit` | query | `10 \| 25 \| 50 \| 100` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `Asc \| Desc` | No | The order the results are sorted in. Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Inventory.Api.Models.InventoryItemModel_` - `400`: 1: Invalid user Id. 2: Invalid asset type Id. - `403`: 3: Insufficient permission. 4: You are not authorized to view this user's inventory. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Inventory.Api.Models.InventoryItemModel_`) See [Roblox.Web.WebAPI.Models.ApiPageResponse_Roblox.Inventory.Api.Models.InventoryItemModel_](#roblox-web-webapi-models-apipageresponse-roblox-inventory-api-models-inventoryitemmodel-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "expireAt": "...", "userAssetId": "...", "assetId": "...", "assetName": "...", "collectibleItemId": "...", "collectibleItemInstanceId": "..." } ] } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://inventory.roblox.com/v2/users/{USERID}/inventory/{ASSETTYPEID}" ``` ### GET `/v2/users/{userId}/tradableItems` Gets tradable items for a user. **Server:** `https://trades.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The id of the user. | | `search` | query | `string` | No | Optional search query to filter items by. | | `itemTargetTypes` | query | `string enum (18 values)[]` | No | Optional list of item target types to filter by. | | `sortBy` | query | `Unknown \| CreationTime \| AcquisitionTime` | No | The key to sort tradable items by. Valid values: `Unknown`, `CreationTime`, `AcquisitionTime` | | `sortOrder` | query | `0 \| 1 \| 2` | No | The sort order for the tradable items. Valid values: `0`, `1`, `2` | | `limit` | query | `integer` | No | The maximum number of items to return. | | `cursor` | query | `string` | No | The pagination cursor. | **Responses:** - `200`: OK → `Roblox.Trades.Api.Models.V2.GetUserTradableItemsResponse` - `400`: 25: The cursor provided is invalid. - `401`: 0: Authorization has been denied for this request. 4: You are not authorized to modify this trade. - `403`: 4: You are not authorized to modify this trade. - `404`: 0: An unknown error occured. **Response fields** (`Roblox.Trades.Api.Models.V2.GetUserTradableItemsResponse`) See [Roblox.Trades.Api.Models.V2.GetUserTradableItemsResponse](#roblox-trades-api-models-v2-getusertradableitemsresponse) in Models. **Response example:** ```json { "userId": 0, "items": [ { "collectibleItemId": "...", "itemTarget": "...", "itemName": "...", "originalPrice": "...", "recentAveragePrice": "...", "assetStock": "..." } ], "nextPageCursor": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://trades.roblox.com/v2/users/{USERID}/tradableItems" ``` ### GET `/v2/users/{userId}/transaction-totals` **Server:** `https://economy.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | | | `usedTypes` | query | `integer` | No | | | `timeFrame` | query | `any` | No | | | `transactionType` | query | `any` | No | | | `exclusiveStartCursor` | query | `any` | No | | **Responses:** - `200`: Success → `TransactionTotalsResponse` **Response fields** (`TransactionTotalsResponse`) See [TransactionTotalsResponse](#transactiontotalsresponse) in Models. **Response example:** ```json { "salesTotal": 0, "purchasesTotal": 0, "affiliateSalesTotal": 0, "groupPayoutsTotal": 0, "currencyPurchasesTotal": 0, "premiumStipendsTotal": 0 } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://economy.roblox.com/v2/users/{USERID}/transaction-totals" ``` ### GET `/v2/users/{userId}/transaction-types` **Server:** `https://economy.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | | **Responses:** - `200`: Success → `TransactionUsedTypesResponse` **Response fields** (`TransactionUsedTypesResponse`) See [TransactionUsedTypesResponse](#transactionusedtypesresponse) in Models. **Response example:** ```json { "HasPurchase": false, "HasSale": false, "HasAffiliatePayout": false, "HasAffiliateSale": false, "HasGroupPayout": false, "HasCurrencyPurchase": false } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://economy.roblox.com/v2/users/{USERID}/transaction-types" ``` ### GET `/v2/users/{userId}/transactions` **Server:** `https://economy.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | | | `exclusiveStartCursor` | query | `any` | No | | | `transactionType` | query | `any` | No | | | `itemPricingType` | query | `any` | No | | **Responses:** - `200`: Success → `TransactionRecordResponseApiPageResponse` **Response fields** (`TransactionRecordResponseApiPageResponse`) See [TransactionRecordResponseApiPageResponse](#transactionrecordresponseapipageresponse) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "id": "...", "idHash": "...", "transactionType": "...", "created": "...", "isPending": "...", "agent": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://economy.roblox.com/v2/users/{USERID}/transactions" ``` ### GET `/v2/users/{userId}/universes` Gets all universes followed by a user. **Server:** `https://followings.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer` | Yes | The user ID. | **Responses:** - `200`: OK → `Roblox.Followings.Api.Models.FollowsByTypeResponse` - `401`: 0: Authorization has been denied for this request. - `403`: User is not authorized for this action. **Response fields** (`Roblox.Followings.Api.Models.FollowsByTypeResponse`) See [Roblox.Followings.Api.Models.FollowsByTypeResponse](#roblox-followings-api-models-followsbytyperesponse) in Models. **Response example:** ```json { "followerType": 0, "followerId": 0, "sourceType": 0, "followedSources": "..." } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://followings.roblox.com/v2/users/{USERID}/universes" ``` ### POST `/v3/outfits/create` Creates a new outfit. Fails if any of the assetIds are not owned by the user, or not wearable types. The name property of the request is optional as one will be auto-generated when the request has a null name. **Server:** `https://avatar.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `Roblox-Place-Id` | header | `integer` | No | | **Request Body:** `application/json` — Type: `Roblox.Api.Avatar.Models.OutfitUpdateModelV3` See [Roblox.Api.Avatar.Models.OutfitUpdateModelV3](#roblox-api-avatar-models-outfitupdatemodelv3) in Models. **Request example:** ```json { "name": "string", "bodyColor3s": { "headColor3": "string", "torsoColor3": "string", "rightArmColor3": "string", "leftArmColor3": "string", "rightLegColor3": "string", "leftLegColor3": "string" }, "assets": [ { "id": "...", "meta": "..." } ], "scale": { "height": 0, "width": 0, "head": 0, "depth": 0, "proportion": 0, "bodyType": 0 }, "playerAvatarType": "string", "outfitType": 0 } ``` **Responses:** - `200`: OK → `Roblox.Api.Avatar.Models.OutfitModel` - `400`: 3: Body colors must be valid BrickColor IDs 4: Invalid outfit name 5: Asset is not wearable by you and was not added to the outfit 7: Invalid Player Avatar Type. Valid types are R6 and R15 8: Invalid assetIds 9: Meta does not apply to specified asset type 10: Required meta is not provided for the specific asset type 12: Outfit type invalid or not permitted 13: Invalid Scale - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 1: You already have the maximum number of outfits - `500`: 6: An error occurred while creating the outfit **Response fields** (`Roblox.Api.Avatar.Models.OutfitModel`) See [Roblox.Api.Avatar.Models.OutfitModel](#roblox-api-avatar-models-outfitmodel) in Models. **Response example:** ```json { "id": 0, "name": "string", "isEditable": false, "outfitType": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://avatar.roblox.com/v3/outfits/create" \ -H "Content-Type: application/json" \ -d '{"name":"string","bodyColor3s":{"headColor3":"string","torsoColor3":"string","rightArmColor3":"string","leftArmColor3":"string","rightLegColor3":"string","leftLegColor3":"string"},"assets":[{"id":"...","meta":"..."}],"scale":{"height":0,"width":0,"head":0,"depth":0,"proportion":0,"bodyType":0},"playerAvatarType":"string","outfitType":0}' ``` ### PATCH `/v3/outfits/{userOutfitId}` Updates the contents of an outfit. Fails if the user does not own any of the assetIds or if they are not wearable asset types. Accepts partial updates. **Server:** `https://avatar.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userOutfitId` | path | `integer` | Yes | The user outfit id. | | `Roblox-Place-Id` | header | `integer` | No | | **Request Body:** `application/json` — Type: `Roblox.Api.Avatar.Models.OutfitUpdateModelV3` See [Roblox.Api.Avatar.Models.OutfitUpdateModelV3](#roblox-api-avatar-models-outfitupdatemodelv3) in Models. **Request example:** ```json { "name": "string", "bodyColor3s": { "headColor3": "string", "torsoColor3": "string", "rightArmColor3": "string", "leftArmColor3": "string", "rightLegColor3": "string", "leftLegColor3": "string" }, "assets": [ { "id": "...", "meta": "..." } ], "scale": { "height": 0, "width": 0, "head": 0, "depth": 0, "proportion": 0, "bodyType": 0 }, "playerAvatarType": "string", "outfitType": 0 } ``` **Responses:** - `200`: OK → `Roblox.Api.Avatar.Models.OutfitModel` - `400`: 3: Body colors must be valid BrickColor IDs 4: Invalid outfit name 5: Asset is not wearable by you 8: Invalid Player Avatar Type. Valid types are R6 and R15 11: Meta does not apply to specified asset type 12: Meta is required for this specific asset type 13: Invalid Outfit Type 14: Invalid scale - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: You don't have permission to update this outfit. - `404`: 1: The specified userOutfit does not exist! - `500`: 6: An error occurred while trying to update the outfit **Response fields** (`Roblox.Api.Avatar.Models.OutfitModel`) See [Roblox.Api.Avatar.Models.OutfitModel](#roblox-api-avatar-models-outfitmodel) in Models. **Response example:** ```json { "id": 0, "name": "string", "isEditable": false, "outfitType": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://avatar.roblox.com/v3/outfits/{USEROUTFITID}" \ -H "Content-Type: application/json" \ -d '{"name":"string","bodyColor3s":{"headColor3":"string","torsoColor3":"string","rightArmColor3":"string","leftArmColor3":"string","rightLegColor3":"string","leftLegColor3":"string"},"assets":[{"id":"...","meta":"..."}],"scale":{"height":0,"width":0,"head":0,"depth":0,"proportion":0,"bodyType":0},"playerAvatarType":"string","outfitType":0}' ``` ### GET `/v3/outfits/{userOutfitId}/details` Gets details about the contents of an outfit. **Server:** `https://avatar.roblox.com` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userOutfitId` | path | `integer` | Yes | The user outfit id. | | `Roblox-Place-Id` | header | `integer` | No | | | `checkAssetAvailability` | query | `boolean` | No | Whether to return assets with availability status. | **Responses:** - `200`: OK → `Roblox.Api.Avatar.Models.OutfitDetailsModelV2` - `400`: 2: The outfit for the specified userOutfit is invalid. - `403`: 3: The requester does not have access to the details for the given user outfit. - `404`: 1: The specified userOutfitId is invalid. **Response fields** (`Roblox.Api.Avatar.Models.OutfitDetailsModelV2`) See [Roblox.Api.Avatar.Models.OutfitDetailsModelV2](#roblox-api-avatar-models-outfitdetailsmodelv2) in Models. **Response example:** ```json { "id": 0, "name": "string", "assets": [ { "id": "...", "name": "...", "assetType": "...", "currentVersionId": "...", "meta": "...", "availabilityStatus": "..." } ], "bodyColor3s": { "headColor3": "string", "torsoColor3": "string", "rightArmColor3": "string", "leftArmColor3": "string", "rightLegColor3": "string", "leftLegColor3": "string" }, "scale": { "height": 0, "width": 0, "head": 0, "depth": 0, "proportion": 0, "bodyType": 0 }, "playerAvatarType": "string" } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://avatar.roblox.com/v3/outfits/{USEROUTFITID}/details" ``` --- name: "SecretsStoreService" last_updated: 2026-06-29T19:34:06Z type: opencloud api_base_url: "https://apis.roblox.com" version: "1.0" endpoints: 5 auth: [api-key, oauth2] --- # SecretsStoreService **API Version:** 1.0 **Base URL(s):** - `https://apis.roblox.com` ## Authentication Each endpoint requires one of the following authentication methods: - **API Key**: Pass your key in the `x-api-key` HTTP header. Create keys at [Creator Dashboard](https://create.roblox.com/dashboard/credentials). - **OAuth 2.0**: Use Bearer token in the `Authorization` header. Authorization URL: `https://apis.roblox.com/oauth/v1/authorize`, Token URL: `https://apis.roblox.com/oauth/v1/token` ``` # API Key example curl -H "x-api-key: YOUR_API_KEY" https://apis.roblox.com/... # OAuth 2.0 example curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" https://apis.roblox.com/... ``` ## Endpoints ### GET `/cloud/v2/universes/{universeId}/secrets` [BETA] List Secrets Lists all secrets defined for a universe. Secret content is not returned for security reasons - only metadata such as ID, domain, creation and update timestamps are included. Only the owner of the universe can list secrets. For group-owned universes, only the group owner or authorized members can list secrets. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `universe.secret:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe ID | | `limit` | query | `integer` | No | Number of secrets to return per page (1-500, default 10) | | `cursor` | query | `string` | No | Pagination cursor from previous response | **Responses:** - `200`: OK → `SecretPaginatedList` - `400`: Bad Request → `ProblemDetails` - `403`: Forbidden → `ProblemDetails` **Response fields** (`SecretPaginatedList`) See [SecretPaginatedList](#secretpaginatedlist) in Models. **Response example:** ```json { "secrets": [ { "id": "...", "secret": "...", "key_id": "...", "domain": "...", "create_time": "...", "update_time": "..." } ], "nextPageCursor": "string", "previousPageCursor": "string" } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 120/minute, perOauth2Authorization: 120/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSEID}/secrets" ``` ### POST `/cloud/v2/universes/{universeId}/secrets` [BETA] Create Secret Creates a new secret. A maximum of 500 secrets per universe is allowed. Only the owner of the universe can create secrets. For group-owned universes, only the group owner or authorized members can create secrets. To encrypt the secret: 1. Get the public key using the Get Public Key endpoint 2. Encrypt your secret using LibSodium sealed box 3. Base64 encode the encrypted content Include the key_id from the public key response in the request. For an example, see the [Secrets store guide](https://create.roblox.com/docs/cloud/guides/secrets-store). **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `universe.secret:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe ID | **Request Body:** `application/json` — Type: `Secret` See [Secret](#secret) in Models. **Request example:** ```json { "id": "string", "secret": "string", "key_id": "string", "domain": "string", "create_time": "string", "update_time": "string" } ``` > **Verify mutations:** If your API key lacks the required scope (`universe.secret:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `201`: Created → `Secret` - `400`: Bad Request → `ProblemDetails` - `403`: Forbidden → `ProblemDetails` - `409`: Conflict → `ProblemDetails` **Response fields** (`Secret`) See [Secret](#secret) in Models. **Response example:** ```json { "id": "string", "secret": "string", "key_id": "string", "domain": "string", "create_time": "string", "update_time": "string" } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 120/minute, perOauth2Authorization: 120/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSEID}/secrets" \ -H "Content-Type: application/json" \ -d '{ "id": "string", "secret": "string", "key_id": "string", "domain": "string", "create_time": "string", "update_time": "string" }' ``` ### PATCH `/cloud/v2/universes/{universeId}/secrets/{secretId}` [BETA] Update Secret Updates an existing secret. Only the owner of the universe can update secrets. For group-owned universes, only the group owner or authorized members can update secrets. Only the secret content, key_id, and domain can be updated - the secret ID cannot be changed. To encrypt the updated secret: 1. Get the current public key using the GetPublicKey endpoint 2. Encrypt your new secret content using LibSodium sealed box 3. Base64 encode the encrypted content Include the key_id from the public key response in the request. For an example, see the [Secrets store guide](https://create.roblox.com/docs/cloud/guides/secrets-store). **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `universe.secret:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe ID | | `secretId` | path | `string` | Yes | The ID of the secret to update | **Request Body:** `application/json` — Type: `Secret` See [Secret](#secret) in Models. **Request example:** ```json { "id": "string", "secret": "string", "key_id": "string", "domain": "string", "create_time": "string", "update_time": "string" } ``` > **Verify mutations:** If your API key lacks the required scope (`universe.secret:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK → `Secret` - `400`: Bad Request → `ProblemDetails` - `403`: Forbidden → `ProblemDetails` - `404`: Not Found → `ProblemDetails` **Response fields** (`Secret`) See [Secret](#secret) in Models. **Response example:** ```json { "id": "string", "secret": "string", "key_id": "string", "domain": "string", "create_time": "string", "update_time": "string" } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 120/minute, perOauth2Authorization: 120/minute **Example:** ```bash curl -X PATCH -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSEID}/secrets/{SECRETID}" \ -H "Content-Type: application/json" \ -d '{ "id": "string", "secret": "string", "key_id": "string", "domain": "string", "create_time": "string", "update_time": "string" }' ``` ### DELETE `/cloud/v2/universes/{universeId}/secrets/{secretId}` [BETA] Delete Secret Permanently deletes a secret from a universe. Only the owner of the universe can delete secrets. For group-owned universes, only the group owner or authorized members can delete secrets. This operation is irreversible. Make sure you no longer need the secret before deleting it. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `universe.secret:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe ID | | `secretId` | path | `string` | Yes | The ID of the secret to delete | > **Verify mutations:** If your API key lacks the required scope (`universe.secret:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: OK - `400`: Bad Request → `ProblemDetails` - `403`: Forbidden → `ProblemDetails` - `404`: Not Found → `ProblemDetails` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 120/minute, perOauth2Authorization: 120/minute **Example:** ```bash curl -X DELETE -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSEID}/secrets/{SECRETID}" ``` ### GET `/cloud/v2/universes/{universeId}/secrets/public-key` [BETA] Get Public Key Retrieves the public key for a universe. You need this key to encrypt secret content before sending it to Roblox. Only the owner of the universe can retrieve the public key. For group-owned universes, only the group owner or authorized members can retrieve the public key. The secret id field is static and always returns "public-key". The returned public key in the secret field is universe-specific and derived from a master key using the universe ID. Use this key with LibSodium sealed box encryption to encrypt your secret content before creating or updating secrets. Include the key_id from the public key response in the request to create or update a secret. **Auth:** API Key (`x-api-key` header) or OAuth 2.0 Bearer token **Scopes:** `universe.secret:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The universe ID | **Responses:** - `200`: OK → `Secret` - `400`: Bad Request → `ProblemDetails` - `403`: Forbidden → `ProblemDetails` **Response fields** (`Secret`) See [Secret](#secret) in Models. **Response example:** ```json { "id": "string", "secret": "string", "key_id": "string", "domain": "string", "create_time": "string", "update_time": "string" } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 120/minute, perOauth2Authorization: 120/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/cloud/v2/universes/{UNIVERSEID}/secrets/public-key" ``` ## Models ### ProblemDetails | Property | Type | Required | Description | |----------|------|----------|-------------| | `type` | `string` | No | | | `title` | `string` | No | | | `status` | `string` | No | | | `detail` | `string` | No | | | `instance` | `string` | No | | ### Secret Universe-specific secret, identified by `id`, and belonging to a specific `environment`. | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `string` | No | The user-specified secret name. Examples: "aws", "gcp", "discord". Static when getting the public key for a universe. Must be alphanumeric or underscore, 1-64 characters, not starting with a number. | | `secret` | `string` | No | The binary secret content. Examples: API key content (text), private keys. When created, the secret must be encrypted using LibSodium sealed box and encoded in base64 with the universe's public key. Contains the public key when getting the public key for a universe. | | `key_id` | `string` | No | Encryption key identifier. Identifies the key that was used to encrypt the secret content. | | `domain` | `string` | No | The domain wildcard that restricts the purpose of the key. You can restrict the URLs callable via HttpService to a specific domain, e.g. "api.example.com" or "*.myservice.org". An empty or null domain means that the secret is a private key and cannot be transformed with addPrefix/addSuffix or sent as a header or URL. In order to make the secret accessible for all domains, use "*" | | `create_time` | `string` | No | Date and time when the secret was originally created. | | `update_time` | `string` | No | Date and time when the secret was last updated | ### SecretPaginatedList Wrapper for the paginated collection output. Complies with the REST conventions, containing next/previous page, and a data field containing the actual list. Needed only to simplify JSON serialization, and can technically be AutoMapper-ed from the EaaS or any other data provider. | Property | Type | Required | Description | |----------|------|----------|-------------| | `secrets` | `Secret[]` | No | Gets the actual list of items to return | | `nextPageCursor` | `string` | No | Gets the cursor where the pagination stopped after fetching `data`. `null` if there is no more data available. | | `previousPageCursor` | `string` | No | Gets the cursor pointing at the previous page. `null` when it's the first page. | --- name: "Toolbox.Service" last_updated: 2026-06-29T19:34:06Z type: opencloud api_base_url: "https://apis.roblox.com" version: "1.0" endpoints: 2 auth: [api-key, cookie] --- # Toolbox.Service **API Version:** 1.0 **Base URL(s):** - `https://apis.roblox.com` ## Authentication Each endpoint requires one of the following authentication methods: - **API Key**: Pass your key in the `x-api-key` HTTP header. Create keys at [Creator Dashboard](https://create.roblox.com/dashboard/credentials). - **Cookie** *(not recommended)*: `.ROBLOSECURITY` cookie. Do not use in production. ``` # API Key example curl -H "x-api-key: YOUR_API_KEY" https://apis.roblox.com/... # OAuth 2.0 example curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" https://apis.roblox.com/... ``` ## Endpoints ### GET `/toolbox-service/v2/assets:search` [BETA] Search Creator Store Assets Search Creator Store for assets. **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) **Scopes:** `creator-store-product:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `searchCategoryType` | query | `any` | Yes | The asset type to search within. | | `query` | query | `string` | No | The search terms used to filter the results. | | `modelSubTypes` | query | `ModelSubType[]` | No | When searching for models, the subtypes associated with the search results. | | `excludedModelSubTypes` | query | `ModelSubType[]` | No | When searching for models, the subtypes not associated with the search results. | | `creator` | query | `string` | No | Deprecated: Please refer to the 'userId' and 'groupId' properties instead. The creator type and ID. E.g. "user/123" or "group/456" | | `userId` | query | `integer` | No | The User ID of the creator. Only one of 'userId' and 'groupId' can be present in a query. | | `groupId` | query | `integer` | No | The Group ID of the creator. Only one of 'userId' and 'groupId' can be present in a query. | | `pageToken` | query | `string` | No | The identifier for the desired search results page. Only one of 'pageNumber' and 'pageToken' can be present in a query. | | `pageNumber` | query | `integer` | No | The page number to retrieve, starting from 0. Only one of 'pageNumber' and 'pageToken' can be present in a query. | | `maxPageSize` | query | `integer` | No | The number of assets to be returned. Cannot be larger than 100. | | `sortDirection` | query | `any` | No | The sort direction of the search results. | | `sortCategory` | query | `any` | No | The category to sort the search results by. | | `audioMinDurationSeconds` | query | `integer` | No | When searching for audio, the minimum duration of the audio assets. If included, must be greater than or equal to 0. | | `audioMaxDurationSeconds` | query | `integer` | No | When searching for audio, the maximum duration of the audio assets. If included, must be greater than or equal to 0. | | `audioArtist` | query | `string` | No | When searching for audio, the artist name of the audio assets. | | `audioAlbum` | query | `string` | No | When searching for audio, the album name of the audio assets. | | `includeTopCharts` | query | `boolean` | No | | | `audioTypes` | query | `SearchAudioTypeModel[]` | No | When searching for audio, the type of the audio assets. | | `includedInstanceTypes` | query | `ModelInstanceType[]` | No | When searching for models, this filters that the following [Instance](https://create.roblox.com/docs/reference/engine/classes/Instance) types are included in the model. | | `includeOnlyVerifiedCreators` | query | `boolean` | No | Whether the results should only include assets created by verified creators. | | `minPriceCents` | query | `integer` | No | The minimum price of the asset in cents. If included, must be greater than or equal to 0. | | `maxPriceCents` | query | `integer` | No | The maximum price of the asset in cents. If included, must be greater than or equal to 0. | | `facets` | query | `string[]` | No | Additional keywords to query by. | | `categoryPath` | query | `string` | No | | | `searchView` | query | `any` | No | Indicates which fields will be populated in the response. | | `musicChartType` | query | `any` | No | Indicates which music charts to filter from. | **Responses:** - `200`: Success → `SearchCreatorStoreAssetsResponse` - `400`: Bad Request → `any` - `429`: Too Many Requests → `any` - `500`: Server Error **Response fields** (`SearchCreatorStoreAssetsResponse`) See [SearchCreatorStoreAssetsResponse](#searchcreatorstoreassetsresponse) in Models. **Response example:** ```json { "nextPageToken": "string", "queryFacets": "...", "creatorStoreAssets": [ { "voting": "...", "creator": "...", "creatorStoreProduct": "...", "asset": "..." } ], "totalResults": 0, "filteredKeyword": "string" } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). **Rate Limits:** perApiKeyOwner: 1000/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/toolbox-service/v2/assets:search?searchCategoryType={VALUE}" ``` ### GET `/toolbox-service/v2/assets/{id}` [BETA] Get Creator Store Asset Details Get details for a single Creator Store asset. **Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) **Scopes:** `creator-store-product:read` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `id` | path | `integer` | Yes | The asset ID to retrieve details for. | **Responses:** - `200`: Success → `CreatorStoreAsset` - `403`: Forbidden → `any` - `404`: Not Found → `any` - `429`: Too Many Requests → `any` **Response fields** (`CreatorStoreAsset`) See [CreatorStoreAsset](#creatorstoreasset) in Models. **Response example:** ```json { "voting": "...", "creator": "...", "creatorStoreProduct": "...", "asset": "..." } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perApiKeyOwner: 1000/minute **Example:** ```bash curl -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/toolbox-service/v2/assets/{ID}" ``` --- name: "Place publishing" last_updated: 2026-06-29T19:34:06Z type: opencloud api_base_url: "https://apis.roblox.com/universes/" version: "v1" endpoints: 1 auth: [api-key] description: "You can send and receive the following request and response payloads to publish and update your places on Roblox" --- # Place publishing You can send and receive the following request and response payloads to publish and update your places on Roblox. For information on the usage of the API, see the [usage guide](/docs/en-us/cloud/guides/usage-place-publishing.md). **API Version:** v1 **Base URL(s):** - `https://apis.roblox.com/universes/` ## Authentication Each endpoint requires one of the following authentication methods: - **API Key**: Pass your key in the `x-api-key` HTTP header. Create keys at [Creator Dashboard](https://create.roblox.com/dashboard/credentials). ``` # API Key example curl -H "x-api-key: YOUR_API_KEY" https://apis.roblox.com/... # OAuth 2.0 example curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" https://apis.roblox.com/... ``` ## Endpoints ### POST `/v1/{universeId}/places/{placeId}/versions` [BETA] Publish a new place or update an existing place with a new version. Provide a RBXL or RBXLX file in the data-binary. **Auth:** API Key (`x-api-key` header) **Scopes:** `universe-places:write` **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | The identifier of the experience in which you want to publish your place to. You can [copy your experience's Universe ID](/docs/en-us/cloud/guides/usage-place-publishing.md#publishing-a-place) on **Creator Dashboard**. | | `placeId` | path | `integer` | Yes | The identifier of your place. See [Publishing places with API keys](/docs/en-us/cloud/guides/usage-place-publishing.md) on obtaining a Place ID. | | `versionType` | query | `string` | No | Can only be either: - `Saved`: the place file should be saved, but not published. - `Published`: the place file should be saved and published. | > **Verify mutations:** If your API key lacks the required scope (`universe-places:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource. **Responses:** - `200`: Returns the published place's version number. → `string` - `400`: Invalid request / Invalid file content. - `401`: API key not valid for operation, user does not have authorization. - `403`: Publish not allowed on place. - `404`: Place or universe does not exist. - `409`: Place not part of the universe. - `500`: Server internal error / Unknown error. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Rate Limits:** perIp: 30/minute, perApiKeyOwner: 30/minute **Example:** ```bash curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \ "https://apis.roblox.com/universes//v1/{UNIVERSEID}/places/{PLACEID}/versions" ``` --- name: Accessory last_updated: 2026-06-29T19:34:06Z inherits: - Accoutrement - Instance - Object type: class memory_category: Instances summary: "An item that a Character can wear." --- # Class: Accessory > An item that a Character can wear. ## Description The Accessory Instance is the parent Instance of all accessories (regardless of their specific accessory type). It typically has a child Handle with a child Attachment and a WrapLayer in the case of Layered Clothing. The Accessory class is the successor to the legacy Hat system. It's cross-compatible with both the legacy R6 character system and the new R15 character system. If you insert an [Attachment](/docs/reference/engine/classes/Attachment.md) into the Accessory's Handle with the same name as an [Attachment](/docs/reference/engine/classes/Attachment.md) in one of the character's limbs, they connect and ignore properties inherited from the [Accoutrement](/docs/reference/engine/classes/Accoutrement.md) class. Otherwise, the Accessory functions identically to a [Hat](/docs/reference/engine/classes/Hat.md). Note: If there are two matching `Attachments`, the resulting [Weld](/docs/reference/engine/classes/Weld.md) is a child of the Accessory's Handle. This differs from the legacy behavior of Hats where the Weld is always a child of the Head of the character. ## Properties ### Property: Accessory.AccessoryType ```json { "type": "AccessoryType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarAppearance" ] } ``` Specifies the AccessoryType of the Accessory. Is [AccessoryType.Unknown](/docs/reference/engine/enums/AccessoryType.md) unless you equip the Accessory through the player spawning process or [Humanoid:ApplyDescriptionAsync()](/docs/reference/engine/classes/Humanoid.md). If available on the Marketplace, you can set [AccessoryType](/docs/reference/engine/enums/AccessoryType.md) to categorize the Accessory item (for example, "Hat" or "Face"). ## Inherited Members ### From [Accoutrement](/docs/reference/engine/classes/Accoutrement.md) - **Property `AttachmentForward`** (`Vector3`): Sets the offset position of the object on the Player. *(hidden)* - **Property `AttachmentPoint`** (`CFrame`): The exact CFrame of the Accoutrement. - **Property `AttachmentPos`** (`Vector3`): Sets the position of the object on the Player. *(hidden)* - **Property `AttachmentRight`** (`Vector3`): Sets the offset position of the object on the Player. *(hidden)* - **Property `AttachmentUp`** (`Vector3`): Sets the offset position of the object on the Player. *(hidden)* ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AccessoryDescription last_updated: 2026-06-29T19:34:06Z inherits: - Instance - Object type: class memory_category: Instances summary: "Describes the appearance of an Accessory for the HumanoidDescription." --- # Class: AccessoryDescription > Describes the appearance of an [Accessory](/docs/reference/engine/classes/Accessory.md) for the > [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). ## Description **AccessoryDescription** is an object that stores the description for an [Accessory](/docs/reference/engine/classes/Accessory.md). It is meant to be placed underneath a [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) in order to work with [Humanoid:ApplyDescriptionAsync()](/docs/reference/engine/classes/Humanoid.md). ## Properties ### Property: AccessoryDescription.AccessoryType ```json { "type": "AccessoryType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarAppearance" ] } ``` The [AccessoryType](/docs/reference/engine/enums/AccessoryType.md) of the [Accessory](/docs/reference/engine/classes/Accessory.md) referred to by this description. ### Property: AccessoryDescription.AssetId ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarAppearance" ] } ``` The asset ID that should be applied when applying this [AccessoryDescription](/docs/reference/engine/classes/AccessoryDescription.md). ### Property: AccessoryDescription.Instance ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarAppearance" ] } ``` A reference to the [Instance](/docs/reference/engine/classes/Instance.md) that should be applied when applying this [AccessoryDescription](/docs/reference/engine/classes/AccessoryDescription.md). This property can be used instead of [AssetId](/docs/reference/engine/classes/AccessoryDescription.md) to apply accessories without uploading them to the platform. ### Property: AccessoryDescription.IsLayered ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarAppearance" ] } ``` Whether the [Accessory](/docs/reference/engine/classes/Accessory.md) is layered or rigid. This is intended to be read-only. It will be corrected after calling [Humanoid:ApplyDescriptionAsync()](/docs/reference/engine/classes/Humanoid.md) if this property doesn't match the actual [Accessory](/docs/reference/engine/classes/Accessory.md) contents. ### Property: AccessoryDescription.Order ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarAppearance" ] } ``` The [WrapLayer.Order](/docs/reference/engine/classes/WrapLayer.md) value when applying the [Accessory](/docs/reference/engine/classes/Accessory.md), if it is layered. ### Property: AccessoryDescription.Position ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarAppearance" ] } ``` The accessory adjustment position offset. Only applies if the [Accessory](/docs/reference/engine/classes/Accessory.md) is rigid. ### Property: AccessoryDescription.Puffiness ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarAppearance" ] } ``` The [WrapLayer.Puffiness](/docs/reference/engine/classes/WrapLayer.md) value when applying the [Accessory](/docs/reference/engine/classes/Accessory.md), if it is layered. ### Property: AccessoryDescription.Rotation ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarAppearance" ] } ``` The accessory adjustment rotation offset. Only applies if the [Accessory](/docs/reference/engine/classes/Accessory.md) is rigid. ### Property: AccessoryDescription.Scale ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarAppearance" ] } ``` The accessory adjustment scale. Only applies if the [Accessory](/docs/reference/engine/classes/Accessory.md) is rigid. ## Methods ### Method: AccessoryDescription:GetAppliedInstance **Signature:** `AccessoryDescription:GetAppliedInstance(): Instance` Returns the applied [Accessory](/docs/reference/engine/classes/Accessory.md) if this [AccessoryDescription](/docs/reference/engine/classes/AccessoryDescription.md) is the child of an applied [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) parented to a [Humanoid](/docs/reference/engine/classes/Humanoid.md). *Security: None · Thread Safety: Unsafe · Capabilities: AvatarAppearance* **Returns:** `Instance` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Accoutrement last_updated: 2026-06-29T19:34:06Z inherits: - Instance - Object type: class memory_category: Instances summary: "An object that can attach to a player's character." --- # Class: Accoutrement > An object that can attach to a player's character. ## Description An Accoutrement welds its child [part](/docs/reference/engine/classes/Part.md) called "Handle" to the player's character. You can change the position and rotation of the Handle part using the [AttachmentPos](/docs/reference/engine/classes/Accoutrement.md), [Right](/docs/reference/engine/classes/Accoutrement.md), [Forward](/docs/reference/engine/classes/Accoutrement.md), and [Up](/docs/reference/engine/classes/Accoutrement.md) properties. Parts descending from an accoutrement are massless when attach to other parts (e.g. with a Weld) as long as they are not the root part of the assembly that [GetRootPart()](/docs/reference/engine/classes/BasePart.md) returns. [GetMass()](/docs/reference/engine/classes/BasePart.md) returns 0 for parts in this case, and it doesn't add to the total mass or rotational inertia of the Assembly. This doesn't apply to a part descending from an accoutrement when an accoutrement welds to another part that is massless or one if its parts otherwise becomes root. This also doesn't apply for the root part, and it has mass like a normal part. ## Properties ### Property: Accoutrement.AttachmentPoint ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Transform", "capabilities": [ "AvatarAppearance" ] } ``` The exact CFrame of the Accoutrement. ### Property: Accoutrement.AttachmentForward *(hidden)* ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Transform", "capabilities": [ "AvatarAppearance" ] } ``` Sets the offset position of the object on the Player. ### Property: Accoutrement.AttachmentPos *(hidden)* ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Transform", "capabilities": [ "AvatarAppearance" ] } ``` Sets the position of the object on the Player. ### Property: Accoutrement.AttachmentRight *(hidden)* ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Transform", "capabilities": [ "AvatarAppearance" ] } ``` Sets the offset position of the object on the Player. ### Property: Accoutrement.AttachmentUp *(hidden)* ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Transform", "capabilities": [ "AvatarAppearance" ] } ``` Sets the offset position of the object on the Player. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Actor last_updated: 2026-06-29T19:34:06Z inherits: - Model - PVInstance - Instance - Object type: class memory_category: BaseParts summary: "An Actor is a container for code that can be safely split into its own thread." --- # Class: Actor > An [Actor](/docs/reference/engine/classes/Actor.md) is a container for code that can be safely split into its own > thread. ## Description An [Actor](/docs/reference/engine/classes/Actor.md) is a container for code that can be safely split into its own thread using [task.desynchronize()](/docs/reference/engine/globals/task.md). It should also contain the instances used by its scripts. To learn more about using multiple Actors to optimize script performance, see [Parallel Luau](/docs/en-us/scripting/multithreading.md). #### Notes - Each `Actor` runs in its own Luau VM. [ModuleScripts](/docs/reference/engine/classes/ModuleScript.md) required by an `Actor` are **not** shared or cached across `Actors` or with the main thread. Each VM executes its own copy of the module, so module-level state is isolated per `Actor`. Design accordingly if your modules hold state you intend to share. - `script:GetActor()` returns `nil` when called from a [ModuleScript](/docs/reference/engine/classes/ModuleScript.md) unless the `ModuleScript` is a descendant of the `Actor`. `ModuleScripts` stored elsewhere (for example, [ReplicatedStorage](/docs/reference/engine/classes/ReplicatedStorage.md)) and required by an `Actor` are not considered descendants of the `Actor`. ## Methods ### Method: Actor:BindToMessage **Signature:** `Actor:BindToMessage(topic: string, function: Function): RBXScriptConnection` This method is used to bind a Luau callback to a message with the specified topic. When a message is sent (using [SendMessage()](/docs/reference/engine/classes/Actor.md)) to the topic specified the provided callback will be called in a _serial_ execution context. Multiple Luau callbacks may be bound to a single actor and even to a single message topic. Note: Only the scripts which are descendants of an Actor may bind to its messages. ```lua local actor = script:GetActor() -- Print out a message when a greeting message is sent to the Actor -- this script is a descendant of. local connection = actor:BindToMessage("Greeting", function(message) print("Received Greeting Message:", message) end) ``` *Security: None · Thread Safety: Safe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `topic` | `string` | | The topic used to identify the type of message. | | `function` | `Function` | | | **Returns:** `RBXScriptConnection` — This connection object may be used to disconnect the Luau callback from receiving messages. ### Method: Actor:BindToMessageParallel **Signature:** `Actor:BindToMessageParallel(topic: string, function: Function): RBXScriptConnection` This method is used to bind a Luau callback to a message with the specified topic. When a message is sent (using [SendMessage()](/docs/reference/engine/classes/Actor.md)) to the topic specified the provided callback will be called in a _parallel_ execution context. Multiple Luau callbacks may be bound to a single actor and even to a single message topic. Note: Only the scripts which are descendants of an Actor may bind to its messages. ```lua local actor = script:GetActor() -- Print out a message when a greeting message is sent to the Actor -- this script is a descendant of. local connection = actor:BindToMessageParallel("Greeting", function(message) print("Received Greeting Message:", message) end) ``` *Security: None · Thread Safety: Safe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `topic` | `string` | | The topic used to identify the type of message. | | `function` | `Function` | | | **Returns:** `RBXScriptConnection` — This connection object may be used to disconnect the Luau callback from receiving messages. ### Method: Actor:SendMessage **Signature:** `Actor:SendMessage(topic: string, message: Tuple): ()` Sends a message to an Actor. Messages are sent asynchronously, so the sender will not block or yield when calling the [SendMessage()](/docs/reference/engine/classes/Actor.md) method. Since a single Actor may receive different kinds of messages, a `topic` parameter is used to distinguish between various kinds of messages. See [BindToMessage()](/docs/reference/engine/classes/Actor.md) for details on receiving a message sent using [SendMessage()](/docs/reference/engine/classes/Actor.md). Note that message arguments are **passed by copy** across Luau VM boundaries, not by reference. Sending large tables incurs a full copy on each call. Functions are bound to a specific VM and cannot be passed in messages. ```lua -- Assume `actor` is a local variable referring to an Actor instance actor:SendMessage("Greeting", "Hello World") ``` *Security: None · Thread Safety: Safe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `topic` | `string` | | The topic used to identify the message being sent. | | `message` | `Tuple` | | The contents of the message to send to the Actor. | **Returns:** `()` ## Inherited Members ### From [Model](/docs/reference/engine/classes/Model.md) - **Property `LevelOfDetail`** (`ModelLevelOfDetail`): Sets the level of detail on the model for experiences with instance - **Property `ModelStreamingMode`** (`ModelStreamingMode`): Controls the model streaming behavior on Models when - **Property `PrimaryPart`** (`BasePart`): The primary part of the Model, or `nil` if not explicitly set. - **Property `Scale`** (`float`): Editor-only property used to scale the model around its pivot. Setting - **Property `WorldPivot`** (`CFrame`): Determines where the pivot of a Model which does **not** have a - **Method `AddPersistentPlayer(playerInstance?: Player): ()`**: Sets this model to be persistent for the specified player. - **Method `BreakJoints(): ()`**: Breaks connections between `BaseParts`, including surface connections with *(deprecated)* - **Method `breakJoints(): ()`**: *(deprecated)* - **Method `GetBoundingBox(): Tuple`**: Returns a description of a volume that contains all parts of a Model. - **Method `GetExtentsSize(): Vector3`**: Returns the size of the smallest bounding box that contains all of the - **Method `GetModelCFrame(): CFrame`**: This value historically returned the CFrame of a central position in the *(deprecated)* - **Method `GetModelSize(): Vector3`**: Returns the Vector3 size of the Model. *(deprecated)* - **Method `GetPersistentPlayers(): List`**: Returns all the Player objects that this model object is - **Method `GetPrimaryPartCFrame(): CFrame`**: Returns the CFrame of the model's Model.PrimaryPart. *(deprecated)* - **Method `GetScale(): float`**: Returns the canonical scale of the model, which defaults to 1 for newly - **Method `MakeJoints(): ()`**: Goes through all BaseParts in the Model. If any *(deprecated)* - **Method `makeJoints(): ()`**: *(deprecated)* - **Method `move(location: Vector3): ()`**: *(deprecated)* - **Method `MoveTo(position: Vector3): ()`**: Moves the PrimaryPart to the given position. If - **Method `moveTo(location: Vector3): ()`**: *(deprecated)* - **Method `RemovePersistentPlayer(playerInstance?: Player): ()`**: Makes this model no longer persistent for the specified player. - **Method `ResetOrientationToIdentity(): ()`**: Resets the rotation of the model's parts to the previously set identity *(deprecated)* - **Method `ScaleTo(newScaleFactor: float): ()`**: Sets the scale factor of the model, adjusting the sizing and location of - **Method `SetIdentityOrientation(): ()`**: Sets the identity rotation of the given model, allowing you to reset the *(deprecated)* - **Method `SetPrimaryPartCFrame(cframe: CFrame): ()`**: Sets the BasePart.CFrame of the model's Model.PrimaryPart. *(deprecated)* - **Method `TranslateBy(delta: Vector3): ()`**: Shifts a Model by the given Vector3 offset, preserving ### From [PVInstance](/docs/reference/engine/classes/PVInstance.md) - **Property `Origin`** (`CFrame`): - **Property `Pivot Offset`** (`CFrame`): - **Method `GetPivot(): CFrame`**: Gets the pivot of a PVInstance. - **Method `PivotTo(targetCFrame: CFrame): ()`**: Transforms the PVInstance along with all of its descendant ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AdGui last_updated: 2026-06-29T19:34:06Z inherits: - SurfaceGuiBase - LayerCollector - GuiBase2d - GuiBase - Instance - Object type: class memory_category: Internal --- # Class: AdGui ## Properties ### Property: AdGui.AdShape ```json { "type": "AdShape", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` ### Property: AdGui.EnableVideoAds ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI", "Monetization" ] } ``` The property enables the AdGui to serve video ads. ### Property: AdGui.FallbackImage ```json { "type": "ContentId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "UI" ] } ``` ### Property: AdGui.Status ```json { "type": "AdUnitStatus", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "UI", "Monetization" ] } ``` ## Callbacks ### Callback: AdGui.OnAdEvent **Signature:** `AdGui.OnAdEvent(eventInfo: Dictionary): boolean` The callback function used to react to the AdGui events. *Security: None · Thread Safety: Unsafe · Capabilities: UI, Monetization* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `eventInfo` | `Dictionary` | | Options table for the method: - `PlayerId` – The user ID of the player related to the ad event. - `AdEventType` – A [AdEventType](/docs/reference/engine/enums/AdEventType.md) enum value describing the AdGui events. | **Returns:** `boolean` — Whether the callback has successfully reacted to the event. ## Inherited Members ### From [SurfaceGuiBase](/docs/reference/engine/classes/SurfaceGuiBase.md) - **Property `Active`** (`boolean`): - **Property `Adornee`** (`Instance`): BasePart on which to apply the SurfaceGui, overriding the - **Property `Face`** (`NormalId`): NormalId face upon which to apply the SurfaceGui. ### From [LayerCollector](/docs/reference/engine/classes/LayerCollector.md) - **Property `Enabled`** (`boolean`): Toggles the visibility of this LayerCollector. - **Property `ResetOnSpawn`** (`boolean`): Determines if the LayerCollector resets (deletes itself and - **Property `ZIndexBehavior`** (`ZIndexBehavior`): Controls how GuiObject.ZIndex behaves on all descendants of this - **Method `GetLayoutNodeTree(): Dictionary`**: *(deprecated)* ### From [GuiBase2d](/docs/reference/engine/classes/GuiBase2d.md) - **Property `AbsolutePosition`** (`Vector2`): Describes the actual screen position of a GuiBase2d element, in - **Property `AbsoluteRotation`** (`float`): Describes the actual screen rotation of a GuiBase2d element, in - **Property `AbsoluteSize`** (`Vector2`): Describes the actual screen size of a GuiBase2d element, in - **Property `AutoLocalize`** (`boolean`): When set to `true`, localization will be applied to this GuiBase2d - **Property `Localize`** (`boolean`): Automatically set to true when a localization table's *(deprecated, hidden)* - **Property `RootLocalizationTable`** (`LocalizationTable`): A reference to a LocalizationTable to be used to apply automated - **Property `SelectionBehaviorDown`** (`SelectionBehavior`): Customizes gamepad selection behavior in the down direction. - **Property `SelectionBehaviorLeft`** (`SelectionBehavior`): Customizes gamepad selection behavior in the left direction. - **Property `SelectionBehaviorRight`** (`SelectionBehavior`): Customizes gamepad selection behavior in the right direction. - **Property `SelectionBehaviorUp`** (`SelectionBehavior`): Customizes gamepad selection behavior in the up direction. - **Property `SelectionGroup`** (`boolean`): Allows customization of gamepad selection movement. - **Event `SelectionChanged`**: Fires when the gamepad selection moves to, leaves, or changes within the ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AdPortal last_updated: 2026-06-29T19:34:06Z inherits: - Instance - Object type: class memory_category: Instances --- # Class: AdPortal ## Properties ### Property: AdPortal.PortalInvalidReason ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Monetization" ] } ``` ### Property: AdPortal.Status ```json { "type": "AdUnitStatus", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Monetization" ] } ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AdService last_updated: 2026-06-29T19:34:06Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service summary: "A class that allows the display of mobile video ads." --- # Class: AdService > A class that allows the display of mobile video ads. ## Description A service for displaying mobile video ads as a form of monetization. Use it to implement rewarded video ads in your experience. ## Methods ### Method: AdService:CreateAdRewardFromDevProductId **Signature:** `AdService:CreateAdRewardFromDevProductId(devProductId: int64): AdReward` Creates a reward to give users who watch an entire video ad. *Security: None · Thread Safety: Unsafe · Capabilities: Monetization* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `devProductId` | `int64` | | The ID of the developer product you want to grant as a reward. | **Returns:** `AdReward` ### Method: AdService:GetAdAvailabilityNowAsync **Signature:** `AdService:GetAdAvailabilityNowAsync(adFormat: AdFormat): GetAdAvailabilityNowResult` Checks if a video ad is available to be played to the current user inside the experience. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Monetization* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `adFormat` | `AdFormat` | | The format of the requested ad. For example, `RewardedVideo`. | **Returns:** `GetAdAvailabilityNowResult` — A dictionary with the [AdAvailabilityResult](/docs/reference/engine/enums/AdAvailabilityResult.md) for the requested ad format. ```lua local adAvailabilityResult = AdService:GetAdAvailabilityNow(Enum.AdFormat.RewardedVideo) ``` ### Method: AdService:GetCampaignEligibilityAsync **Signature:** `AdService:GetCampaignEligibilityAsync(campaignId: string, player?: Player?): GetCampaignEligibilityResult` *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Monetization* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `campaignId` | `string` | | The id of the campaign that you want to check a `Player`'s eligibility for. | | `player` | `Player?` | `nil` | The `Player` you want to check eligibility for. If omitted, it will default to `Players.LocalPlayer`. | **Returns:** `GetCampaignEligibilityResult` — A dictionary that looks like `{ IsEligible: boolean }`. ### Method: AdService:RegisterAdOpportunityAsync **Signature:** `AdService:RegisterAdOpportunityAsync(instance: Instance, placementId: int64?): ()` Tracks how many times a user had the chance to watch a video ad and the rate at which they actually watched the ad. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Monetization* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `instance` | `Instance` | | | | `placementId` | `int64?` | | The ID of the placement of the rewarded video ad inside the experience. Allows for reporting on the performance of individual ad placements. | **Returns:** `()` **Track ad opportunities** Tracks ad opportunities for rewarded video ads. ```lua local AdService = game:GetService("AdService") local playerGui = game:GetService("Players").LocalPlayer:WaitForChild("PlayerGui") -- Create the ScreenGui and the Button local screenGui = Instance.new("ScreenGui", playerGui) local adButton = Instance.new("TextButton", screenGui) -- Register the button with AdService local success, err = pcall(function() -- The second parameter is an optional Placement ID (Number) parameter generated in the Creator Hub. -- If you choose not to provide a Placement ID, the ad opportunity is tracked under the default placement. AdService:RegisterAdOpportunityAsync(adButton, 1234567891234567) end) if not success then warn("Failed to register ad opportunity:", err) end ``` ### Method: AdService:RegisterDisclosureButton **Signature:** `AdService:RegisterDisclosureButton(disclosureButton: GuiButton, adIntegrationPlacementId: string): ()` *Security: None · Thread Safety: Unsafe · Capabilities: Monetization* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `disclosureButton` | `GuiButton` | | The `GuiButton` you want to mark as an ad disclosure. | | `adIntegrationPlacementId` | `string` | | The id of the placement that this disclosure is attached to. | **Returns:** `()` ### Method: AdService:ShowRewardedVideoAdAsync **Signature:** `AdService:ShowRewardedVideoAdAsync(player: Player, reward: AdReward, placementId: int64?): ShowAdResult` Plays the video ad to the current user inside the experience. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: UI, Monetization* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Player` | | The `Player` object for whom you are fetching the ad for. | | `reward` | `AdReward` | | The reward object for the reward you want to grant the user who watches an ad to completion. | | `placementId` | `int64?` | | The ID of the placement of the rewarded video ad inside the experience. Allows for reporting on the performance of individual ad placements. | **Returns:** `ShowAdResult` ```lua local reward = AdService:CreateAdRewardFromDevProductId(100) local result = AdService:ShowRewardedVideoAdAsync(player, reward) ``` ### Method: AdService:UnregisterAdOpportunity **Signature:** `AdService:UnregisterAdOpportunity(instance: Instance): ()` *Security: None · Thread Safety: Unsafe · Capabilities: Monetization* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `instance` | `Instance` | | | **Returns:** `()` ### Method: AdService:ShowVideoAd **Signature:** `AdService:ShowVideoAd(): ()` > **Deprecated:** [ShowVideoAd](/docs/reference/engine/classes/AdService.md) has been decommissioned and is no longer operational. Show mobile video advertisements. *Security: None · Thread Safety: Unsafe · Capabilities: Monetization* **Returns:** `()` ## Events ### Event: AdService.VideoAdClosed **Signature:** `AdService.VideoAdClosed(adShown: boolean)` > **Deprecated:** [VideoAdClosed](/docs/reference/engine/classes/AdService.md) has been decommissioned and is no longer operational. Fires when an [AdService](/docs/reference/engine/classes/AdService.md) video closes. *Security: None · Capabilities: Monetization* **Parameters:** | Name | Type | Description | |------|------|-------------| | `adShown` | `boolean` | | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AdvancedDragger last_updated: 2026-06-29T19:34:06Z inherits: - Instance - Object type: class memory_category: Instances summary: "An unfinished advanced variant of the Dragger class." --- # Class: AdvancedDragger > An unfinished advanced variant of the [Dragger](/docs/reference/engine/classes/Dragger.md) class. ## Description An unfinished advanced variant of the [Dragger](/docs/reference/engine/classes/Dragger.md) class. Internally, this object is an identical implementation of the Dragger class. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AirController last_updated: 2026-06-29T19:34:06Z inherits: - ControllerBase - Instance - Object type: class memory_category: Instances --- # Class: AirController ## Properties ### Property: AirController.BalanceMaxTorque ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Balance", "capabilities": [ "Basic" ], "simulationAccess": true } ``` The maximum torque the character can use to remain balanced upright. When misaligned, this amount of torque is applied to reach the [BalanceSpeed](/docs/reference/engine/classes/AirController.md) and realign the character. A higher torque means more force is required to cause the character to tilt. A lower torque means it's easer for the character to flip in the air. This property is hidden and has no effect when [ControllerBase.BalanceRigidityEnabled](/docs/reference/engine/classes/ControllerBase.md) is true. ### Property: AirController.BalanceSpeed ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Balance", "capabilities": [ "Basic" ], "simulationAccess": true } ``` The maximum angular speed used to align the character upright. A lower value means it takes longer for the character to recover to the upright position when misaligned. A higher value results in a quicker recovery. ### Property: AirController.MaintainAngularMomentum ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Turn", "capabilities": [ "Basic" ], "simulationAccess": true } ``` Determines whether angular momentum is preserved when input has stopped. If false, the character will apply [TurningMaxTorque](/docs/reference/engine/classes/AirController.md) to bring the angular velocity toward 0 when there is no input. ### Property: AirController.MaintainLinearMomentum ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Movement", "capabilities": [ "Basic" ], "simulationAccess": true } ``` Determines whether linear momentum is preserved when input has stopped. If false, the character will apply [MoveMaxForce](/docs/reference/engine/classes/AirController.md) to bring the linear velocity towards 0 when there is no input. ### Property: AirController.MoveMaxForce ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Movement", "capabilities": [ "Basic" ], "simulationAccess": true } ``` The maximum force that can be applied on the [ControllerManager.RootPart](/docs/reference/engine/classes/ControllerManager.md) for moving in the [ControllerManager.MovingDirection](/docs/reference/engine/classes/ControllerManager.md). This affects how quickly different target linear velocities are reached and how quick the linear acceleration is if [MaintainLinearMomentum](/docs/reference/engine/classes/AirController.md) is off. ### Property: AirController.TurnMaxTorque ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Turn", "capabilities": [ "Basic" ], "simulationAccess": true } ``` The maximum torque that can be applied on the [ControllerManager.RootPart](/docs/reference/engine/classes/ControllerManager.md) for turning towards the [ControllerManager.FacingDirection](/docs/reference/engine/classes/ControllerManager.md). This effects how quickly different target angular velocities are reached and how quick angular deceleration is if [MaintainAngularMomentum](/docs/reference/engine/classes/AirController.md) is off. ### Property: AirController.TurnSpeedFactor ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Turn", "capabilities": [ "Basic" ], "simulationAccess": true } ``` The value multiplied by the [ControllerManager.BaseTurnSpeed](/docs/reference/engine/classes/ControllerManager.md) to determine the final target angular velocity while this controller is active. The angular velocity is applied when turning towards the [ControllerManager.FacingDirection](/docs/reference/engine/classes/ControllerManager.md). ## Inherited Members ### From [ControllerBase](/docs/reference/engine/classes/ControllerBase.md) - **Property `Active`** (`boolean`): - **Property `BalanceRigidityEnabled`** (`boolean`): - **Property `MoveSpeedFactor`** (`float`): The value multiplied by the ControllerManager.BaseMoveSpeed. ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AlignOrientation last_updated: 2026-06-29T19:34:07Z inherits: - Constraint - Instance - Object type: class memory_category: BaseParts summary: "Constraint which applies torque to align two attachments, or to align one attachment with a goal orientation." --- # Class: AlignOrientation > Constraint which applies torque to align two attachments, or to align one > attachment with a goal orientation. ## Description The `AlignOrientation` constraint applies torque to align two attachments, or to align one attachment with a goal orientation. As indicated by the name, it only affects the **orientation** of the attachments, not their position (to align attachments **positionally**, see [AlignPosition](/docs/reference/engine/classes/AlignPosition.md)). Torque created by `AlignOrientation` is applied about the center of mass of the parent of the attachments, or the center of mass of parts rigidly connected to the parents. When configuring this constraint, it may be helpful to study [Roblox Units](/docs/en-us/physics/units.md) to understand how Roblox units compare to metric units. #### Affected Axes The axes affected by torque are controlled through the constraint's [AlignType](/docs/reference/engine/classes/AlignOrientation.md) property. When set to [PrimaryAxisParallel](/docs/reference/engine/enums/AlignType.md), [PrimaryAxisPerpendicular](/docs/reference/engine/enums/AlignType.md) or [PrimaryAxisLookAt](/docs/reference/engine/enums/AlignType.md), torque will only occur when the primary axes become misaligned. Otherwise, the constraint will apply torque about all 3 axes to achieve alignment. #### Reactionary Torque By default, the constraint only applies torque to [Attachment0](/docs/reference/engine/classes/Constraint.md) while [Attachment1](/docs/reference/engine/classes/Constraint.md) remains unaffected. If desired, torque can be applied to both attachments in **equal and opposite directions** by enabling [ReactionTorqueEnabled](/docs/reference/engine/classes/AlignOrientation.md). #### Torque Magnitude You can configure this constraint to apply the maximum torque that constraints allow through the [RigidityEnabled](/docs/reference/engine/classes/AlignOrientation.md) property. When `true`, the physics solver reacts as quickly as possible to complete the alignment. When `false`, the torque is determined by [MaxTorque](/docs/reference/engine/classes/AlignOrientation.md), [MaxAngularVelocity](/docs/reference/engine/classes/AlignOrientation.md), and [Responsiveness](/docs/reference/engine/classes/AlignOrientation.md). #### Attachment Mode This constraint can use either **one** or **two** attachments in calculating its goal. See [Mode](/docs/reference/engine/classes/AlignOrientation.md) for details. ## Properties ### Property: AlignOrientation.AlignType ```json { "type": "AlignType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "AlignOrientation", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Specifies the desired relationship between the primary axes of [Attachment0](/docs/reference/engine/classes/Constraint.md) and the goal. Available options are [AllAxes](/docs/reference/engine/enums/AlignType.md), [PrimaryAxisParallel](/docs/reference/engine/enums/AlignType.md), [PrimaryAxisPerpendicular](/docs/reference/engine/enums/AlignType.md), and [PrimaryAxisLookAt](/docs/reference/engine/enums/AlignType.md). The constraint will attempt to maintain the specified relationship, as given by the [AlignType](/docs/reference/engine/enums/AlignType.md), by applying torques onto the relevant axes. ### Property: AlignOrientation.CFrame ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "TargetOrientation", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The [CFrame](/docs/reference/engine/datatypes/CFrame.md) orientation (translation component ignored) with which the constraint will attempt to match the orientation of [Attachment0](/docs/reference/engine/classes/Constraint.md). Only used when [Mode](/docs/reference/engine/classes/AlignOrientation.md) is set to [OneAttachment](/docs/reference/engine/enums/OrientationAlignmentMode.md). ### Property: AlignOrientation.LookAtPosition ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "TargetOrientation", "capabilities": [ "Physics" ], "simulationAccess": true } ``` A [Vector3](/docs/reference/engine/datatypes/Vector3.md) world space location toward which the primary axis will attempt to align. This is only active when [AlignType](/docs/reference/engine/classes/AlignOrientation.md) is set to [PrimaryAxisLookAt](/docs/reference/engine/enums/AlignType.md) and [Mode](/docs/reference/engine/classes/AlignOrientation.md) is set to [OneAttachment](/docs/reference/engine/enums/OrientationAlignmentMode.md). ### Property: AlignOrientation.MaxAngularVelocity ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Compliance", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Maximum angular velocity the constraint can use to reach its goal. Only used if [RigidityEnabled](/docs/reference/engine/classes/AlignOrientation.md) is `false`. Note that [MaxAngularVelocity](/docs/reference/engine/classes/AlignOrientation.md), as well as [MaxTorque](/docs/reference/engine/classes/AlignOrientation.md), are **caps** to the angular velocity and torque respectively. The actual scale is determined by [Responsiveness](/docs/reference/engine/classes/AlignOrientation.md). ### Property: AlignOrientation.MaxTorque ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Compliance", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Maximum torque the constraint can use to reach its goal. Only used if [RigidityEnabled](/docs/reference/engine/classes/AlignOrientation.md) is `false`. Note that [MaxTorque](/docs/reference/engine/classes/AlignOrientation.md), as well as [MaxAngularVelocity](/docs/reference/engine/classes/AlignOrientation.md), are **caps** to the torque and angular velocity respectively. The actual scale is determined by [Responsiveness](/docs/reference/engine/classes/AlignOrientation.md). ### Property: AlignOrientation.Mode ```json { "type": "OrientationAlignmentMode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "AlignmentMode", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Whether the constraint uses **one** or **two** attachments in calculating its goal. By default, this is [TwoAttachment](/docs/reference/engine/enums/OrientationAlignmentMode.md), meaning that the constraint attempts to match the orientation of [Attachment0](/docs/reference/engine/classes/Constraint.md) with the orientation of [Attachment1](/docs/reference/engine/classes/Constraint.md), disregarding [CFrame](/docs/reference/engine/classes/AlignOrientation.md), [PrimaryAxis](/docs/reference/engine/classes/AlignOrientation.md), and [SecondaryAxis](/docs/reference/engine/classes/AlignOrientation.md). If set to [OneAttachment](/docs/reference/engine/enums/OrientationAlignmentMode.md), the constraint disregards [Attachment1](/docs/reference/engine/classes/Constraint.md) and attempts to match the orientation of [Attachment0](/docs/reference/engine/classes/Constraint.md) with the orientation of [CFrame](/docs/reference/engine/classes/AlignOrientation.md), or match the attachment's [Axis](/docs/reference/engine/classes/Attachment.md) and [SecondaryAxis](/docs/reference/engine/classes/Attachment.md) with the constraint's [PrimaryAxis](/docs/reference/engine/classes/AlignOrientation.md) and [SecondaryAxis](/docs/reference/engine/classes/AlignOrientation.md) properties respectively. ### Property: AlignOrientation.PrimaryAxis ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "TargetOrientation", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The direction of the goal's **X** axis, represented as a unit [Vector3](/docs/reference/engine/datatypes/Vector3.md). Only used when [Mode](/docs/reference/engine/classes/AlignOrientation.md) is [OneAttachment](/docs/reference/engine/enums/OrientationAlignmentMode.md). ### Property: AlignOrientation.PrimaryAxisOnly ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "AlignOrientation", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Determines how the constraint's axes are affected by torque. If `false` (default), the constraint will apply torque about all 3 axes to achieve alignment. If `true`, torque will only occur when the primary axes become misaligned. Enabling [PrimaryAxisOnly](/docs/reference/engine/classes/AlignOrientation.md) also enables the [AlignType](/docs/reference/engine/classes/AlignOrientation.md) property. ### Property: AlignOrientation.ReactionTorqueEnabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "AlignOrientation", "capabilities": [ "Physics" ], "simulationAccess": true } ``` If `false` (default), the constraint only applies torque to [Attachment0](/docs/reference/engine/classes/Constraint.md) while [Attachment1](/docs/reference/engine/classes/Constraint.md) remains unaffected. If `true`, the constraint applies torque to both attachments in **equal and opposite directions**. ### Property: AlignOrientation.Responsiveness ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Compliance", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Controls how quickly the constraint reaches its goal. Higher values cause the attachment(s) to align more rapidly. Value can be between 5 and 200. ### Property: AlignOrientation.RigidityEnabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "AlignOrientation", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Whether torque is dependent on other properties, or if the physics solver reacts as quickly as possible to complete the alignment. If `false` (default), the torque is determined by [MaxTorque](/docs/reference/engine/classes/AlignOrientation.md), [MaxAngularVelocity](/docs/reference/engine/classes/AlignOrientation.md), and [Responsiveness](/docs/reference/engine/classes/AlignOrientation.md). If `true`, the physics solver reacts as quickly as possible to complete the alignment. ### Property: AlignOrientation.SecondaryAxis ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "TargetOrientation", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The direction of the goal's **Y** axis, represented as a unit [Vector3](/docs/reference/engine/datatypes/Vector3.md). Only used when [Mode](/docs/reference/engine/classes/AlignOrientation.md) is [OneAttachment](/docs/reference/engine/enums/OrientationAlignmentMode.md). ## Inherited Members ### From [Constraint](/docs/reference/engine/classes/Constraint.md) - **Property `Active`** (`boolean`): Indicates if the constraint is currently active in the world. - **Property `Attachment0`** (`Attachment`): The Attachment that is connected to - **Property `Attachment1`** (`Attachment`): The Attachment that is connected to - **Property `Color`** (`BrickColor`): The color of the constraint. - **Property `Enabled`** (`boolean`): Toggles whether or not the constraint is enabled. - **Property `Visible`** (`boolean`): Toggles the constraint's visibility. - **Method `GetDebugAppliedForce(bodyId: int): Vector3`**: *(deprecated)* - **Method `GetDebugAppliedTorque(bodyId: int): Vector3`**: *(deprecated)* ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AlignPosition last_updated: 2026-06-29T19:34:07Z inherits: - Constraint - Instance - Object type: class memory_category: BaseParts summary: "Constraint which applies force to move two attachments together, or to move one attachment to a goal position." --- # Class: AlignPosition > Constraint which applies force to move two attachments together, or to move > one attachment to a goal position. ## Description The `AlignPosition` constraint applies force to move two attachments together, or to move one attachment to a goal position. As indicated by the name, it only affects the **position** of the attachments, not their orientation (to align attachments by **orientation**, see [AlignOrientation](/docs/reference/engine/classes/AlignOrientation.md)). When configuring this constraint, it may be helpful to study [Roblox Units](/docs/en-us/physics/units.md) to understand how Roblox units compare to metric units. #### Force Location By default, force is applied to the parent of [Attachment0](/docs/reference/engine/classes/Constraint.md) at that attachment's location, meaning that if the parent's center of mass is not aligned with the direction of the force, torque will be applied as well as force. Alternatively, force can be applied to the parents' center of mass by toggling on [ApplyAtCenterOfMass](/docs/reference/engine/classes/AlignPosition.md). #### Reactionary Force By default, the constraint only applies force to [Attachment0](/docs/reference/engine/classes/Constraint.md) while [Attachment1](/docs/reference/engine/classes/Constraint.md) remains unaffected. If desired, force can be applied to both attachments in **equal and opposite directions** by enabling [ReactionForceEnabled](/docs/reference/engine/classes/AlignPosition.md). #### Force Limits You can configure this constraint to apply the maximum force that constraints allow through the [RigidityEnabled](/docs/reference/engine/classes/AlignPosition.md) property. When `true`, the physics solver reacts as quickly as possible to complete the alignment. When `false`, the force applied by the constraint is limited based on [ForceLimitMode](/docs/reference/engine/classes/AlignPosition.md), [MaxVelocity](/docs/reference/engine/classes/AlignPosition.md), and [Responsiveness](/docs/reference/engine/classes/AlignPosition.md). See [ForceLimitMode](/docs/reference/engine/classes/AlignPosition.md) for further details. #### Attachment Mode This constraint can use either **one** or **two** attachments in calculating its goal. See [Mode](/docs/reference/engine/classes/AlignPosition.md) for details. ## Properties ### Property: AlignPosition.ApplyAtCenterOfMass ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "AlignPosition", "capabilities": [ "Physics" ], "simulationAccess": true } ``` When `false` (default), force is applied to the parent of [Attachment0](/docs/reference/engine/classes/Constraint.md) at that attachment's location, meaning that if the parent's center of mass is not aligned with the direction of the force, torque will be applied as well as force. When `true`, force is applied at the parents' center of mass. ### Property: AlignPosition.ForceLimitMode ```json { "type": "ForceLimitMode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Compliance", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Determines how the constraint force will be limited when [RigidityEnabled](/docs/reference/engine/classes/AlignPosition.md) is `false`. When set to [Magnitude](/docs/reference/engine/enums/ForceLimitMode.md), the constraint force will be limited such that the magnitude is less than [MaxForce](/docs/reference/engine/classes/AlignPosition.md). When set to [PerAxis](/docs/reference/engine/enums/ForceLimitMode.md), the constraint force along each axis will be limited by [MaxAxesForce](/docs/reference/engine/classes/AlignPosition.md). The axes along which the force will be limited are based on the [ForceRelativeTo](/docs/reference/engine/classes/AlignPosition.md) property. ### Property: AlignPosition.ForceRelativeTo ```json { "type": "ActuatorRelativeTo", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Compliance", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Determines the axes that the constraint uses to limit the force. Only applies when [RigidityEnabled](/docs/reference/engine/classes/AlignPosition.md) is `false` and [AlignPosition.ForceLimitMode](/docs/reference/engine/classes/AlignPosition.md) is [PerAxis](/docs/reference/engine/enums/ForceLimitMode.md). When set to [World](/docs/reference/engine/enums/ActuatorRelativeTo.md), the constraint force is computed in the world reference frame and the force limits specified in [MaxAxesForce](/docs/reference/engine/classes/AlignPosition.md) refer to the axes of the world coordinate system. When set to [Attachment0](/docs/reference/engine/enums/ActuatorRelativeTo.md) or [Attachment1](/docs/reference/engine/enums/ActuatorRelativeTo.md), the force limits specified in [MaxAxesForce](/docs/reference/engine/classes/AlignPosition.md) refer to the axes of the specified attachment coordinate system. ### Property: AlignPosition.MaxAxesForce ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Compliance", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Maximum force along each axis that the constraint can apply to achieve its goal. Only used if [RigidityEnabled](/docs/reference/engine/classes/AlignPosition.md) is `false` and [ForceLimitMode](/docs/reference/engine/classes/AlignPosition.md) is [PerAxis](/docs/reference/engine/enums/ForceLimitMode.md). The axes used to apply to the limit are specified using the [ForceRelativeTo](/docs/reference/engine/classes/AlignPosition.md) property. ### Property: AlignPosition.MaxForce ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Compliance", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Maximum force magnitude the constraint can apply to achieve its goal. Only used if [RigidityEnabled](/docs/reference/engine/classes/AlignPosition.md) is `false` and [ForceLimitMode](/docs/reference/engine/classes/AlignPosition.md) is [Magnitude](/docs/reference/engine/enums/ForceLimitMode.md). Note that [MaxForce](/docs/reference/engine/classes/AlignPosition.md), as well as [MaxVelocity](/docs/reference/engine/classes/AlignPosition.md), are **caps** to the force and velocity respectively. The actual scale is determined by [Responsiveness](/docs/reference/engine/classes/AlignPosition.md). ### Property: AlignPosition.MaxVelocity ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Compliance", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Maximum speed the attachments can move when converging. Only used if [RigidityEnabled](/docs/reference/engine/classes/AlignPosition.md) is `false`. Note that [MaxVelocity](/docs/reference/engine/classes/AlignPosition.md), as well as [MaxForce](/docs/reference/engine/classes/AlignPosition.md), are **caps** to the velocity and force respectively. The actual scale is determined by [Responsiveness](/docs/reference/engine/classes/AlignPosition.md). ### Property: AlignPosition.Mode ```json { "type": "PositionAlignmentMode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "AlignmentMode", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Whether the constraint uses **one** or **two** attachments in calculating its goal. By default, this is [TwoAttachment](/docs/reference/engine/enums/PositionAlignmentMode.md), meaning that the constraint disregards [Position](/docs/reference/engine/classes/AlignPosition.md) and attempts to move [Attachment0](/docs/reference/engine/classes/Constraint.md) to the position of [Attachment1](/docs/reference/engine/classes/Constraint.md). If set to [OneAttachment](/docs/reference/engine/enums/PositionAlignmentMode.md), the constraint disregards [Attachment1](/docs/reference/engine/classes/Constraint.md) and attempts to move [Attachment0](/docs/reference/engine/classes/Constraint.md) to [Position](/docs/reference/engine/classes/AlignPosition.md). ### Property: AlignPosition.Position ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "AlignPosition", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The position to which the constraint should move its [Attachment0](/docs/reference/engine/classes/Constraint.md). Only used if [Mode](/docs/reference/engine/classes/AlignPosition.md) is set to [OneAttachment](/docs/reference/engine/enums/PositionAlignmentMode.md), in which case [Attachment1](/docs/reference/engine/classes/Constraint.md) is disregarded. ### Property: AlignPosition.ReactionForceEnabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "AlignPosition", "capabilities": [ "Physics" ], "simulationAccess": true } ``` If `false` (default), the constraint only applies force to [Attachment0](/docs/reference/engine/classes/Constraint.md) while [Attachment1](/docs/reference/engine/classes/Constraint.md) remains unaffected. If `true`, the constraint applies force to both attachments in **equal and opposite directions**. ### Property: AlignPosition.Responsiveness ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Compliance", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Controls how quickly the constraint reaches its goal. Higher values cause the attachment(s) to align more rapidly. Value can be between 5 and 200. ### Property: AlignPosition.RigidityEnabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "AlignPosition", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Whether force is dependent on other properties, or if the physics solver reacts as quickly as possible to complete the alignment. If `false` (default), the force is determined by [MaxForce](/docs/reference/engine/classes/AlignPosition.md), [MaxVelocity](/docs/reference/engine/classes/AlignPosition.md), and [Responsiveness](/docs/reference/engine/classes/AlignPosition.md). If `true`, the physics solver reacts as quickly as possible to complete the alignment. ## Inherited Members ### From [Constraint](/docs/reference/engine/classes/Constraint.md) - **Property `Active`** (`boolean`): Indicates if the constraint is currently active in the world. - **Property `Attachment0`** (`Attachment`): The Attachment that is connected to - **Property `Attachment1`** (`Attachment`): The Attachment that is connected to - **Property `Color`** (`BrickColor`): The color of the constraint. - **Property `Enabled`** (`boolean`): Toggles whether or not the constraint is enabled. - **Property `Visible`** (`boolean`): Toggles the constraint's visibility. - **Method `GetDebugAppliedForce(bodyId: int): Vector3`**: *(deprecated)* - **Method `GetDebugAppliedTorque(bodyId: int): Vector3`**: *(deprecated)* ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AnalyticsService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated summary: "Collection of methods that allows you to track how users interact with your experiences." --- # Class: AnalyticsService > Collection of methods that allows you to track how users interact with your > experiences. ## Description `AnalyticsService` is a collection of methods that allows you to track how users interact with your experiences, specifically player progression, in-experience economy, funnels, and custom events. For more information on using this service, see [Event types](/docs/en-us/production/analytics/event-types.md). ## Properties ### Property: AnalyticsService.ApiKey ```json { "type": "string", "access": "ReadOnly", "security": { "read": "LocalUserSecurity", "write": "LocalUserSecurity" }, "serialization": { "can_load": true, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` This property contains the game's PlayFab API key. It must be set and valid in order to use [FireEvent](/docs/reference/engine/classes/AnalyticsService.md). ## Methods ### Method: AnalyticsService:GetPlayerSegmentsAsync **Signature:** `AnalyticsService:GetPlayerSegmentsAsync(player: Player): Dictionary` This server-only method returns coarse segment buckets for a player in the current experience. If a cached result is not already available, the method may yield while the engine fetches the segment data. Successfully fetched results are cached per player for the lifetime of the server session. If segment data is unavailable, this method does not throw. Instead, it returns `HasData = false` and all enum fields are set to `Unknown`. This method throws only when called from the client or when the `player` argument is invalid. The returned dictionary has the following structure: | Name | Type | Description | | --- | --- | --- | | `HasData` | bool | Whether segment data was successfully retrieved for the player. | | `ActivePayerStatus` | [ActivePayerStatus](/docs/reference/engine/enums/ActivePayerStatus.md) | The player's current payer status bucket for this experience. | | `WhenUserFirstPlayed` | [WhenUserFirstPlayed](/docs/reference/engine/enums/WhenUserFirstPlayed.md) | When the player first played this experience, represented as a bucket. | | `PlatformSpenderStatus` | [PlayerPlatformSpenderStatus](/docs/reference/engine/enums/PlayerPlatformSpenderStatus.md) | The player's platform-wide spender status bucket, including whether the player falls outside the active spender bucket. | *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Player` | | The player whose segment buckets should be returned. | **Returns:** `Dictionary` — A dictionary containing coarse player segment buckets for the current experience. **Configure server-side experiences with player segments** This server-side sample retrieves runtime player segments when a player joins and uses them to choose onboarding and offer behavior. ```lua local Players = game:GetService("Players") local AnalyticsService = game:GetService("AnalyticsService") local function configurePlayerExperience(player) local segments = AnalyticsService:GetPlayerSegmentsAsync(player) if not segments.HasData then return end if segments.WhenUserFirstPlayed == Enum.WhenUserFirstPlayed.Days0To30 then print("Show additional onboarding for", player.Name) end if segments.ActivePayerStatus == Enum.ActivePayerStatus.Top15Percent then print("Show high-value offer to", player.Name) end end Players.PlayerAdded:Connect(configurePlayerExperience) ``` ### Method: AnalyticsService:LogCustomEvent **Signature:** `AnalyticsService:LogCustomEvent(player: Player, eventName: string, value?: double, customFields?: Dictionary): ()` Logs an event used to track custom metrics of a user in experience. For more information, see [Custom events](/docs/en-us/production/analytics/custom-events.md). *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Player` | | The user who triggered the event. | | `eventName` | `string` | | The name of the custom event. | | `value` | `double` | `1` | The value of the event that will be used in aggregation. | | `customFields` | `Dictionary` | `nil` | Optional dictionary of custom fields that will provide breakdowns in Roblox-provided charts. Only specific keys, provided by [AnalyticsCustomFieldKeys](/docs/reference/engine/enums/AnalyticsCustomFieldKeys.md), will be used for these breakdowns. Limited to 8,000 unique combinations of values across the three custom fields per experience. | **Returns:** `()` **Log Custom Event** This example uses [AnalyticsService:LogCustomEvent](/docs/reference/engine/classes/AnalyticsService.md) to log two custom events: `MissionStarted` and `MissionCompletedDuration`. ```lua local AnalyticsService = game:GetService("AnalyticsService") -- Log when the mission starts AnalyticsService:LogCustomEvent( player, "MissionStarted" -- Custom event name ) -- Log when the mission is completed with the time it took AnalyticsService:LogCustomEvent( player, "MissionCompletedDuration", -- Custom event name 120 -- Event value used in aggregation ) ``` ### Method: AnalyticsService:LogEconomyEvent **Signature:** `AnalyticsService:LogEconomyEvent(player: Player, flowType: AnalyticsEconomyFlowType, currencyType: string, amount: float, endingBalance: float, transactionType: string, itemSku: string, customFields?: Dictionary): ()` Logs an event used to track player actions related in experience. For more information, see [Economy events](/docs/en-us/production/analytics/economy-events.md). *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Player` | | The user who triggered the event. | | `flowType` | `AnalyticsEconomyFlowType` | | Should specify the direction that currency is flowing using [AnalyticsEconomyFlowType](/docs/reference/engine/enums/AnalyticsEconomyFlowType.md). | | `currencyType` | `string` | | The name of the currency being added or removed, for example `"gold"`, `"gems"`, or `"energy"`. Limited to 5 unique currency types per experience. | | `amount` | `float` | | The amount of currency being added or removed. This value should always be positive. | | `endingBalance` | `float` | | The user's balance after the currency has been added or removed. This value should always be greater than or equal to `0`. | | `transactionType` | `string` | | The type of transaction that occurred. While you're free to use any transaction type, it's recommended to use the provided types from [AnalyticsEconomyTransactionType](/docs/reference/engine/enums/AnalyticsEconomyTransactionType.md) such as `"IAP"` or `"ContextualPurchase"` to enable future insights from Roblox tools and charts. Because this field type is a string, you'll need to pass the `Name` value of the enum. For example [Enum.AnalyticsEconomyTransactionType.IAP.Name](/docs/reference/engine/enums/AnalyticsEconomyTransactionType.md). Limited to 20 unique types per experience. | | `itemSku` | `string` | | Optional SKU of the item or bundle being purchased. This is a unique identifier for the item being purchased. Limited to 100 unique SKUs per experience. | | `customFields` | `Dictionary` | `nil` | Optional dictionary of custom fields that will provide breakdowns in Roblox-provided charts. Only specific keys, provided by [AnalyticsCustomFieldKeys](/docs/reference/engine/enums/AnalyticsCustomFieldKeys.md), will be used for these breakdowns. Limited to 8,000 unique combinations of values across the three custom fields per experience. | **Returns:** `()` **Tracking an in-app purchase** The following sample tracks a Robux purchase of a 1000-coin bundle, using the IAP (in-app purchase) transaction type. Note the item name provided as an optional parameter when compared to the previous sample. ```lua local AnalyticsService = game:GetService("AnalyticsService") AnalyticsService:LogEconomyEvent( player, Enum.AnalyticsEconomyFlowType.Source, "Coins", 1000, -- How many coins are in the bundle 1020, -- balance after transaction Enum.AnalyticsEconomyTransactionType.IAP.Name, "1000CoinBundle" -- Unique identifier of the coin bundle ) ``` ### Method: AnalyticsService:LogFunnelStepEvent **Signature:** `AnalyticsService:LogFunnelStepEvent(player: Player, funnelName: string, funnelSessionId: string, step?: int, stepName: string, customFields?: Dictionary): ()` Logs an event used to track user actions stepping through a pre-planned funnel. Funnel breakdowns only consider the user and event values from the first step in a funnel session. See [Funnel events](/docs/en-us/production/analytics/funnel-events.md). *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Player` | | The user who triggered the event. | | `funnelName` | `string` | | The name of the funnel. This should be the same for all steps in the funnel. Limited to 10 unique funnels per experience. | | `funnelSessionId` | `string` | | Optional unique identifier for the funnel session. This should be the same for all steps in the funnel. Note that this field is only necessary for **recurring** funnels, for example a purchase flow funnel or an item upgrade funnel. If you don't have a natural funnel session identifier, it's recommended to use [HttpService:GenerateGUID()](/docs/reference/engine/classes/HttpService.md). | | `step` | `int` | `1` | The step number in the funnel. This should be unique for each step in the funnel. All funnels start at step 1. Limited to steps 1-100. Repeated steps by the same user in the same funnel session, or when `funnelSessionId` is `nil` will be ignored. Note that if any steps are skipped, the intermediate steps will be considered completed. | | `stepName` | `string` | | Optional name of the step in the funnel. This field is only used for display purposes in Roblox-provided charts. | | `customFields` | `Dictionary` | `nil` | Optional dictionary of custom fields that will provide breakdowns in Roblox-provided charts. Only specific keys, provided by [AnalyticsCustomFieldKeys](/docs/reference/engine/enums/AnalyticsCustomFieldKeys.md), will be used for these breakdowns. Limited to 8,000 unique combinations of values across the three custom fields per experience. | **Returns:** `()` **Tracking Shop steps** The following sample tracks some basic events for each user beginning the process to buy an item from an "armory" shop. Note the funnelSessionId used to distinguish between different sessions of the same user opening the shop. ```lua local AnalyticsService = game:GetService("AnalyticsService") local HttpService = game:GetService("HttpService") funnelSessionId = HttpService:GenerateGUID() -- Log when the user opens the store AnalyticsService:LogFunnelStepEvent( player, "ArmoryCheckout", -- Funnel name used to group steps together funnelSessionId, -- Funnel session ID for this unique checkout session 1, -- Step number "Opened Store" -- Step name ) -- Log when the user views an item AnalyticsService:LogFunnelStepEvent( player, "ArmoryCheckout", -- Funnel name used to group steps together funnelSessionId, -- Funnel session ID for this unique checkout session 2, -- Step number "Viewed Item" -- Step name ) -- Log when the user views adds to cart AnalyticsService:LogFunnelStepEvent( player, "ArmoryCheckout", -- Funnel name used to group steps together funnelSessionId, -- Funnel session ID for this unique checkout session 3, -- Step number "Added to Cart" -- Step name ) ``` ### Method: AnalyticsService:LogOnboardingFunnelStepEvent **Signature:** `AnalyticsService:LogOnboardingFunnelStepEvent(player: Player, step: int, stepName: string, customFields?: Dictionary): ()` Logs an event used to track user actions stepping through an onboarding funnel. Funnel breakdowns only consider the user and event values from the first step in a funnel session. See [Funnel events](/docs/en-us/production/analytics/funnel-events.md). *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Player` | | The user who triggered the event. | | `step` | `int` | | The step number in the funnel. This should be unique for each step in the funnel. All funnels start at step 1. Limited to steps 1-100. Note that if any steps are skipped, the intermediate steps will be considered completed. | | `stepName` | `string` | | Optional name of the step in the funnel. This field is only used for display purposes in Roblox-provided charts. | | `customFields` | `Dictionary` | `nil` | Optional dictionary of custom fields that will provide breakdowns in Roblox-provided charts. Only specific keys, provided by [AnalyticsCustomFieldKeys](/docs/reference/engine/enums/AnalyticsCustomFieldKeys.md), will be used for these breakdowns. Limited to 8,000 unique combinations of values across the three custom fields per experience. | **Returns:** `()` **Tracking onboarding steps** The following sample demonstrates how to log two steps of an onboarding funnel. An onboarding funnel typically introduces players to the game's core loop. ```lua local AnalyticsService = game:GetService("AnalyticsService") -- Log the first step of the FTUE AnalyticsService:LogOnboardingFunnelStepEvent( player, 1, -- Step number "Joined Game" -- Step name ) -- Log the second step of the FTUE AnalyticsService:LogOnboardingFunnelStepEvent( player, 2, -- Step number "Choose Class" -- Step name ) ``` ### Method: AnalyticsService:LogProgressionCompleteEvent **Signature:** `AnalyticsService:LogProgressionCompleteEvent(player: Player, progressionPathName: string, level: int, levelName: string, customFields?: Dictionary): ()` Logs an event for when a user has completed a level attempt. This event does not currently display in any Roblox-provided charts. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Player` | | The player who triggered the event. | | `progressionPathName` | `string` | | | | `level` | `int` | | | | `levelName` | `string` | | | | `customFields` | `Dictionary` | `nil` | | **Returns:** `()` ### Method: AnalyticsService:LogProgressionEvent **Signature:** `AnalyticsService:LogProgressionEvent(player: Player, progressionPathName: string, status: AnalyticsProgressionType, level: int, levelName: string, customFields?: Dictionary): ()` Logs an event for when a user has started, completed, or failed a level attempt. This event does not currently display in any Roblox-provided charts. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Player` | | The player who triggered the event. | | `progressionPathName` | `string` | | | | `status` | `AnalyticsProgressionType` | | | | `level` | `int` | | | | `levelName` | `string` | | | | `customFields` | `Dictionary` | `nil` | | **Returns:** `()` ### Method: AnalyticsService:LogProgressionFailEvent **Signature:** `AnalyticsService:LogProgressionFailEvent(player: Player, progressionPathName: string, level: int, levelName: string, customFields?: Dictionary): ()` Logs an event for when a user has failed a level attempt. This event does not currently display in any Roblox-provided charts. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Player` | | The user who triggered the event. | | `progressionPathName` | `string` | | | | `level` | `int` | | | | `levelName` | `string` | | | | `customFields` | `Dictionary` | `nil` | | **Returns:** `()` ### Method: AnalyticsService:LogProgressionStartEvent **Signature:** `AnalyticsService:LogProgressionStartEvent(player: Player, progressionPathName: string, level: int, levelName: string, customFields?: Dictionary): ()` Logs an event for when a user has started a level attempt. This event does not currently display in any Roblox-provided charts. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Player` | | The player who triggered the event. | | `progressionPathName` | `string` | | | | `level` | `int` | | | | `levelName` | `string` | | | | `customFields` | `Dictionary` | `nil` | | **Returns:** `()` ### Method: AnalyticsService:FireCustomEvent **Signature:** `AnalyticsService:FireCustomEvent(player: Instance, eventCategory: string, customData: Variant): ()` > **Deprecated:** This deprecated function is a variant of [AnalyticsService:LogCustomEvent()](/docs/reference/engine/classes/AnalyticsService.md) which should be used instead. This function triggers a custom event with a custom event name data. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Instance` | | The player who triggered the custom event. `nil` if not player related. | | `eventCategory` | `string` | | User defined category. This should be the name of the event. | | `customData` | `Variant` | | Optional. User defined data, could be a string, a number or a table. | **Returns:** `()` ### Method: AnalyticsService:FireEvent **Signature:** `AnalyticsService:FireEvent(category: string, value: Variant): ()` > **Deprecated:** This function has been deprecated in favor of more descriptive methods, including [AnalyticsService:LogCustomEvent()](/docs/reference/engine/classes/AnalyticsService.md), [AnalyticsService:LogEconomyEvent()](/docs/reference/engine/classes/AnalyticsService.md), and [AnalyticsService:LogProgressionEvent()](/docs/reference/engine/classes/AnalyticsService.md). This function reports a custom event to PlayFab. The event is reported using a **category** and **value**, where the category is a string and the value can be a string or table. In order to use PlayFab, you must have a valid [ApiKey](/docs/reference/engine/classes/AnalyticsService.md) set. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `category` | `string` | | 'The category of event to report. Cannot contain the following characters: comma `,`, double quote `"` or newline characters `\r\n`'. | | `value` | `Variant` | | A value to be serialized and reported. Serialized length must not exceed 1 KiB, or 1024 bytes. | **Returns:** `()` ### Method: AnalyticsService:FireInGameEconomyEvent **Signature:** `AnalyticsService:FireInGameEconomyEvent(player: Instance, itemName: string, economyAction: AnalyticsEconomyAction, itemCategory: string, amount: int, currency: string, location: Variant, customData: Variant): ()` > **Deprecated:** This deprecated function is a variant of [AnalyticsService:LogEconomyEvent()](/docs/reference/engine/classes/AnalyticsService.md) which should be used instead. This function triggers an event used to track player actions pertaining to the in-game economy. For example, it should be called to track when players acquire or spend virtual items within the economy like currency. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Instance` | | The player who triggered the economy event. | | `itemName` | `string` | | The name of the item. | | `economyAction` | `AnalyticsEconomyAction` | | Indicates the acquisition or spending of an in game resource. | | `itemCategory` | `string` | | A user defined category for items such as "Vehicle," "Weapon.". | | `amount` | `int` | | The amount of the currency. | | `currency` | `string` | | The currency used. Examples: 'gold', 'gems', 'life.'. | | `location` | `Variant` | | The event location. A dictionary that each key-value represents an entry of location data. The key-value is a string-string pair. With this you can query which are the most popular "stores" then maybe you want to increase/lower the price for the stores. See the example below: ```lua local location = { ["placeDesc"] = "Dungeon1", ["levelDesc"] = "level2", ["mapDesc"] = "LeftChamberMap", ["storeName"] = "DarkSmith", ["userDefinedKey"] = "0005" } ``` | | `customData` | `Variant` | | Optional. User defined data, could be a string, a number or a table. | **Returns:** `()` — No return. ### Method: AnalyticsService:FireLogEvent **Signature:** `AnalyticsService:FireLogEvent(player: Instance, logLevel: AnalyticsLogLevel, message: string, debugInfo: Variant, customData: Variant): ()` This function triggers an event used to track errors and warnings experienced by players. For example, it could be called to indicate when a function call fails, such as a datastore save or [TeleportService:Teleport()](/docs/reference/engine/classes/TeleportService.md). See the example below. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Instance` | | The player who triggered the error event, `nil` if not player related. | | `logLevel` | `AnalyticsLogLevel` | | The specified log level (e.g. Debug, Error). | | `message` | `string` | | User defined message. | | `debugInfo` | `Variant` | | Optional. A dictionary which contains predefined keys including "errorCode" and "stackTrace". Both keys values are strings. stackTrace is a traceback of the current function call stack. | | `customData` | `Variant` | | Optional. User defined data, could be a string, a number or a table. | **Returns:** `()` — No return. ### Method: AnalyticsService:FirePlayerProgressionEvent **Signature:** `AnalyticsService:FirePlayerProgressionEvent(player: Instance, category: string, progressionStatus: AnalyticsProgressionStatus, location: Variant, statistics: Variant, customData: Variant): ()` > **Deprecated:** This deprecated function is a variant of [AnalyticsService:LogProgressionEvent()](/docs/reference/engine/classes/AnalyticsService.md) which should be used instead. This function triggers an event used to track player progression through the game. For example, it should be called when a player starts an in-game tutorial and again that player finishes the tutorial. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Instance` | | The player who triggered the event. | | `category` | `string` | | A user defined category for progression. | | `progressionStatus` | `AnalyticsProgressionStatus` | | Indicates the status of the progression. | | `location` | `Variant` | | The event location. A dictionary that each key-value represents an entry of location data. The key-value is a string-string pair. With this developers can query where is the most frequent location for a specific progression event category. For example, the category could be "LevelUp". | | `statistics` | `Variant` | | Optional. A dictionary that each key-value represents an entry of statistics data that allows developers to track any specific data that they want to collect as players progress through their game. Key-Value is a string-number pair. | | `customData` | `Variant` | | Optional. User defined data, could be a string, a number or a table. | **Returns:** `()` — No return. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AngularVelocity last_updated: 2026-06-29T19:34:07Z inherits: - Constraint - Instance - Object type: class memory_category: BaseParts summary: "Applies torque on an assembly to maintain a constant angular velocity." --- # Class: AngularVelocity > Applies torque on an assembly to maintain a constant angular velocity. ## Description The `AngularVelocity` constraint applies torque on an assembly to maintain a **constant** angular velocity. Alternatively: - If you want to control the amount of torque applied, use a [Torque](/docs/reference/engine/classes/Torque.md) constraint. - If you only need **initial** angular velocity, set the [AssemblyAngularVelocity](/docs/reference/engine/classes/BasePart.md) method directly on the assembly. When configuring this constraint, it may be helpful to study [Roblox Units](/docs/en-us/physics/units.md) to understand how Roblox units compare to metric units. #### Relativity Application of velocity can be controlled through the constraint's [RelativeTo](/docs/reference/engine/classes/AngularVelocity.md) property. If set to [World](/docs/reference/engine/enums/ActuatorRelativeTo.md), the angular velocity vector is used as is. If set to [Attachment1](/docs/reference/engine/enums/ActuatorRelativeTo.md) and the constraint's [Attachment1](/docs/reference/engine/classes/Constraint.md) property is set to another attachment, the angular velocity will be affected by that of the other attachment. Setting [RelativeTo](/docs/reference/engine/classes/AngularVelocity.md) to [Attachment1](/docs/reference/engine/enums/ActuatorRelativeTo.md) also exposes the [ReactionTorqueEnabled](/docs/reference/engine/classes/AngularVelocity.md) property. ## Properties ### Property: AngularVelocity.AngularVelocity ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Torque", "capabilities": [ "Physics" ], "simulationAccess": true } ``` A [Vector3](/docs/reference/engine/datatypes/Vector3.md) that gives the desired or target angular velocity. This vector is set in the [CFrame](/docs/reference/engine/datatypes/CFrame.md) expressed by the [RelativeTo](/docs/reference/engine/classes/AngularVelocity.md) property. ### Property: AngularVelocity.MaxTorque ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Torque", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Magnitude of the maximum torque the constraint can apply. ### Property: AngularVelocity.ReactionTorqueEnabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Torque", "capabilities": [ "Physics" ], "simulationAccess": true } ``` This property, when enabled, causes the constraint to apply equal and opposite reaction forces. This is important if the two attached parts can collide, since without reaction forces collisions can create energy that would otherwise be disregarded. When enabled, the reaction forces cause the constraint to act like an angular motor between the two attachments. Only meaningful when [RelativeTo](/docs/reference/engine/classes/AngularVelocity.md) is set to [Attachment1](/docs/reference/engine/enums/ActuatorRelativeTo.md). ### Property: AngularVelocity.RelativeTo ```json { "type": "ActuatorRelativeTo", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Torque", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The [CFrame](/docs/reference/engine/datatypes/CFrame.md) in which the `AngularVelocity` force is specified. If set to [World](/docs/reference/engine/enums/ActuatorRelativeTo.md), the angular velocity vector is used as is. If set to [Attachment1](/docs/reference/engine/enums/ActuatorRelativeTo.md), the angular velocity is transformed by the [CFrame](/docs/reference/engine/datatypes/CFrame.md) of the assigned attachment. [RelativeTo](/docs/reference/engine/classes/AngularVelocity.md) can also be set to [Attachment0](/docs/reference/engine/enums/ActuatorRelativeTo.md), but it makes no physical sense and will lead to unpredictable behaviors. ## Inherited Members ### From [Constraint](/docs/reference/engine/classes/Constraint.md) - **Property `Active`** (`boolean`): Indicates if the constraint is currently active in the world. - **Property `Attachment0`** (`Attachment`): The Attachment that is connected to - **Property `Attachment1`** (`Attachment`): The Attachment that is connected to - **Property `Color`** (`BrickColor`): The color of the constraint. - **Property `Enabled`** (`boolean`): Toggles whether or not the constraint is enabled. - **Property `Visible`** (`boolean`): Toggles the constraint's visibility. - **Method `GetDebugAppliedForce(bodyId: int): Vector3`**: *(deprecated)* - **Method `GetDebugAppliedTorque(bodyId: int): Vector3`**: *(deprecated)* ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Animation last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Animation summary: "References an animation asset which can be loaded by an AnimationController." --- # Class: Animation > References an animation asset which can be loaded by an > [AnimationController](/docs/reference/engine/classes/AnimationController.md). ## Description An object that references an animation asset ([AnimationId](/docs/reference/engine/classes/Animation.md)) which can be loaded by an [AnimationController](/docs/reference/engine/classes/AnimationController.md). #### Loading an Animation on Client or Server In order for [AnimationTracks](/docs/reference/engine/classes/AnimationTrack.md) to replicate correctly, it's important to know when they should be loaded on the client or on the server: - If an [Animator](/docs/reference/engine/classes/Animator.md) is a descendant of a [Humanoid](/docs/reference/engine/classes/Humanoid.md) or [AnimationController](/docs/reference/engine/classes/AnimationController.md) in a player's [Player.Character](/docs/reference/engine/classes/Player.md), animations started on that player's client will be replicated to the server and other clients. - If the [Animator](/docs/reference/engine/classes/Animator.md) is **not** a descendant of a player character, its animations must be loaded and started on the server to replicate. The [Animator](/docs/reference/engine/classes/Animator.md) object must be initially created on the server and replicated to clients for animation replication to work at all. If an [Animator](/docs/reference/engine/classes/Animator.md) is created locally, then [AnimationTracks](/docs/reference/engine/classes/AnimationTrack.md) loaded with that [Animator](/docs/reference/engine/classes/Animator.md) will not replicate. See also [Animation Editor](/docs/en-us/animation/editor.md) and [Using Animations](/docs/en-us/animation/using.md) to learn how to create and add pre-built or custom animations to your game. ## Properties ### Property: Animation.AnimationId ```json { "type": "ContentId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Animation" ] } ``` This property is the asset ID of the animation an [Animation](/docs/reference/engine/classes/Animation.md) object is referencing. Once an animation has been created and uploaded to Roblox, the ID can be copied from the [Creator Dashboard](https://create.roblox.com/dashboard/creations?activeTab=Animation). Note that the animation will need to be loaded onto an [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md) in order to play it. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AnimationClip last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable summary: "Represents all types of animation data that the Roblox animation system can consume." --- # Class: AnimationClip > Represents all types of animation data that the Roblox animation system can > consume. ## Description The non-creatable [AnimationClip](/docs/reference/engine/classes/AnimationClip.md) instance type represents abstract animation data that can be fed to the Roblox animation system. [KeyframeSequence](/docs/reference/engine/classes/KeyframeSequence.md) and [CurveAnimation](/docs/reference/engine/classes/CurveAnimation.md) are two current instance types that inherit from [AnimationClip](/docs/reference/engine/classes/AnimationClip.md). There are different ways to represent animation data. To simplify the use of Roblox's animation system, all such representations are their own instance types but inherit from the [AnimationClip](/docs/reference/engine/classes/AnimationClip.md) instance. Animation clips published to Roblox via the [Animation Editor](/docs/en-us/animation/editor.md) can be loaded into the Roblox Engine using an [Animation](/docs/reference/engine/classes/Animation.md) instance. ## Properties ### Property: AnimationClip.Length ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Animation" ] } ``` Returns the length (in seconds) of this [AnimationClip](/docs/reference/engine/classes/AnimationClip.md). This will return `0` until the animation has fully loaded and thus may not be immediately available. ### Property: AnimationClip.Loop ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Animation" ] } ``` Determines whether the animation stored in this [AnimationClip](/docs/reference/engine/classes/AnimationClip.md) is intended to loop. When set to true, the animation will continuously repeat each time it finishes. Note that [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md) instances internally load an [AnimationClip](/docs/reference/engine/classes/AnimationClip.md) when an [Animation](/docs/reference/engine/classes/Animation.md) is requested via its [AnimationId](/docs/reference/engine/classes/Animation.md), and the [AnimationTrack.Looped](/docs/reference/engine/classes/AnimationTrack.md) property will default to the original [AnimationClip](/docs/reference/engine/classes/AnimationClip.md) value. Note also that this value can be overwritten. ### Property: AnimationClip.Priority ```json { "type": "AnimationPriority", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Animation" ] } ``` Determines which clip takes priority when multiple animations are playing simultaneously. Multiple playing animations look to this property to figure out which [Keyframe](/docs/reference/engine/classes/Keyframe.md) poses should be played over one another. Note that [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md) instances internally load an [AnimationClip](/docs/reference/engine/classes/AnimationClip.md) when an [Animation](/docs/reference/engine/classes/Animation.md) is requested via its [AnimationId](/docs/reference/engine/classes/Animation.md), and the [AnimationTrack.Priority](/docs/reference/engine/classes/AnimationTrack.md) property will default to the original [AnimationClip](/docs/reference/engine/classes/AnimationClip.md) value. Note also that this value can be overwritten. **KeyframeSequence Instantiation** This sample demonstrates how a basic [KeyframeSequence](/docs/reference/engine/classes/KeyframeSequence.md) can be created. ```lua -- Create the sequence local keyframeSequence = Instance.new("KeyframeSequence") keyframeSequence.Loop = false keyframeSequence.Priority = Enum.AnimationPriority.Action -- Create a keyframe local keyframe = Instance.new("Keyframe") keyframe.Time = 0 -- Create sample poses local rootPose = Instance.new("Pose") rootPose.Name = "HumanoidRootPart" rootPose.Weight = 0 local lowerTorsoPose = Instance.new("Pose") lowerTorsoPose.Name = "LowerTorso" lowerTorsoPose.Weight = 1 -- Set the sequence hierarchy rootPose:AddSubPose(lowerTorsoPose) keyframe:AddPose(rootPose) keyframeSequence:AddKeyframe(keyframe) -- Parent the sequence keyframeSequence.Parent = workspace ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AnimationClipProvider last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Animation tags: - NotCreatable - Service - NotReplicated summary: "Provides functions to load and preview AnimationClips." --- # Class: AnimationClipProvider > Provides functions to load and preview [AnimationClips](/docs/reference/engine/classes/AnimationClip.md). ## Description Provides functions to load and preview [AnimationClips](/docs/reference/engine/classes/AnimationClip.md). It includes a number of functions that are useful when working with an [Animation](/docs/reference/engine/classes/Animation.md). The [AnimationClipProvider](/docs/reference/engine/classes/AnimationClipProvider.md) replaces the deprecated [KeyframeSequenceProvider](/docs/reference/engine/classes/KeyframeSequenceProvider.md) that was used to download `KeyframeSequences` by content ID. The AnimationClipProvider has a number of uses. - Download the [AnimationClip](/docs/reference/engine/classes/AnimationClip.md) associated with an animation content ID from the Roblox website, regardless of the underlying type of [AnimationClip](/docs/reference/engine/classes/AnimationClip.md) ([KeyframeSequence](/docs/reference/engine/classes/KeyframeSequence.md) or [CurveAnimation](/docs/reference/engine/classes/CurveAnimation.md)). - Generate a temporary ID to locally preview an animation. - Fetch the content IDs of animations owned by a particular user. ## Methods ### Method: AnimationClipProvider:GetAnimationClipAsync **Signature:** `AnimationClipProvider:GetAnimationClipAsync(assetId: ContentId): AnimationClip` Fetches an [AnimationClip](/docs/reference/engine/classes/AnimationClip.md) based on the specified assetId. The assetId must correspond to an animation asset in Roblox. The function will yield until the [AnimationClip](/docs/reference/engine/classes/AnimationClip.md) is loaded from the website and should be wrapped in a `pcall`. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `assetId` | `ContentId` | | The content ID of the animation. | **Returns:** `AnimationClip` — The [AnimationClip](/docs/reference/engine/classes/AnimationClip.md) found. ### Method: AnimationClipProvider:GetAnimationsAsync **Signature:** `AnimationClipProvider:GetAnimationsAsync(userId: User): Instance` This function returns an [InventoryPages](/docs/reference/engine/classes/InventoryPages.md) object which can be used to iterate over animations owned by a specific user. This function has a number of potential uses, such as allowing users to browse and import animations into a custom animation plugin. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `userId` | `User` | | The user ID of the user. | **Returns:** `Instance` — An [InventoryPages](/docs/reference/engine/classes/InventoryPages.md) of animations. ### Method: AnimationClipProvider:GetClipEvaluatorAsync **Signature:** `AnimationClipProvider:GetClipEvaluatorAsync(assetId: ContentId): ClipEvaluator` *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `assetId` | `ContentId` | | | **Returns:** `ClipEvaluator` ### Method: AnimationClipProvider:RegisterActiveAnimationClip **Signature:** `AnimationClipProvider:RegisterActiveAnimationClip(animationClip: AnimationClip): ContentId` Generates a temporary asset ID from a [AnimationClip](/docs/reference/engine/classes/AnimationClip.md) that can be used for localized testing of an animation. This performs the same function as [RegisterAnimationClip()](/docs/reference/engine/classes/AnimationClipProvider.md) but generates an `active://` URL instead of a hash. The generated ID can be used as an [AnimationId](/docs/reference/engine/classes/Animation.md) property for testing. The asset ID generated by this function is temporary and cannot be used outside of Studio. Developers wishing to generate an asset ID that can be used online should upload the [AnimationClip](/docs/reference/engine/classes/AnimationClip.md) to Roblox. *Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `animationClip` | `AnimationClip` | | The [AnimationClip](/docs/reference/engine/classes/AnimationClip.md) to be used. | **Returns:** `ContentId` — A temporary asset ID generated for localized animation playback. ### Method: AnimationClipProvider:RegisterAnimationClip **Signature:** `AnimationClipProvider:RegisterAnimationClip(animationClip: AnimationClip): ContentId` Generates a temporary asset ID from a [AnimationClip](/docs/reference/engine/classes/AnimationClip.md) that can be used for localized testing of an animation. This function performs the same function to [RegisterActiveAnimationClip](/docs/reference/engine/classes/AnimationClipProvider.md) yet generates a hash instead of an `active://` URL. The ID generated can be used for the [Animation.AnimationId](/docs/reference/engine/classes/Animation.md) property to test animations. The asset ID generated by this function is temporary and cannot be used outside of Studio. Developers wishing to generate an asset ID that can be used online should upload the [AnimationClip](/docs/reference/engine/classes/AnimationClip.md) to Roblox. *Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `animationClip` | `AnimationClip` | | The [AnimationClip](/docs/reference/engine/classes/AnimationClip.md) to be used. | **Returns:** `ContentId` — A temporary asset ID generated for localized animation playback. ### Method: AnimationClipProvider:GetAnimationClip **Signature:** `AnimationClipProvider:GetAnimationClip(assetId: ContentId): AnimationClip` > **Deprecated:** This function is deprecated and can lead to the game freezing until the animation is loaded. Developers are recommended to use `GetAnimationClipAsync` instead. Returns a [AnimationClip](/docs/reference/engine/classes/AnimationClip.md) from a given asset URL. *Security: PluginSecurity · Thread Safety: Unsafe · Capabilities: Animation* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `assetId` | `ContentId` | | The content ID of the animation. | **Returns:** `AnimationClip` — The [AnimationClip](/docs/reference/engine/classes/AnimationClip.md) found. ### Method: AnimationClipProvider:GetAnimationClipById **Signature:** `AnimationClipProvider:GetAnimationClipById(assetId: int64, useCache: boolean): AnimationClip` > **Deprecated:** This function is deprecated and can lead to the game freezing until the animation is loaded. Developers are recommended to use `GetAnimationClipAsync` instead. Returns a [AnimationClip](/docs/reference/engine/classes/AnimationClip.md) from the supplied assetId. Can optionally cache to reduce unnecessary loading freezes. *Security: PluginSecurity · Thread Safety: Unsafe · Capabilities: Animation* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `assetId` | `int64` | | The content ID of the animation. | | `useCache` | `boolean` | | True if a cached version can be returned. | **Returns:** `AnimationClip` — The [AnimationClip](/docs/reference/engine/classes/AnimationClip.md) found. ### Method: AnimationClipProvider:GetAnimations **Signature:** `AnimationClipProvider:GetAnimations(userId: User): Instance` This function returns an [InventoryPages](/docs/reference/engine/classes/InventoryPages.md) object which can be used to iterate over animations owned by a specific user. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `userId` | `User` | | The user ID of the user. | **Returns:** `Instance` — An [InventoryPages](/docs/reference/engine/classes/InventoryPages.md) of animations. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AnimationConstraint last_updated: 2026-06-29T19:34:07Z inherits: - Constraint - Instance - Object type: class memory_category: BaseParts summary: "Aligns two BaseParts with an animate-able kinematic or force-based joint that supports physical simulation (ragdoll, arm strength). The default joint type for R15 avatar rigs." --- # Class: AnimationConstraint > Aligns two [BaseParts](/docs/reference/engine/classes/BasePart.md) with an animate-able kinematic or > force-based joint that supports physical simulation (ragdoll, arm strength). > The default joint type for R15 avatar rigs. ## Description #### Replaces Motor6D for Avatar rigs As part of the [Avatar Joint Upgrade](https://devforum.roblox.com/t/avatar-joint-upgrade-for-physically-simulated-character-movement-is-now-live/4298561), `AnimationConstraint` is the **replacement for [Motor6D](/docs/reference/engine/classes/Motor6D.md)** in R15 player character rigs. When [AvatarJointUpgrade](/docs/reference/engine/classes/StarterPlayer.md) is enabled (the default for new experiences), player characters spawn with AnimationConstraints instead of Motor6Ds. Unlike Motor6D, AnimationConstraint supports both kinematic animation and force-based physical simulation — enabling ragdoll physics, arm strength, and other physically simulated character movement without rebuilding the rig. #### Migrating from Motor6D If you have existing code that uses Motor6D for character rigs, note these key differences. See also the [Phase 2 migration recommendations](https://devforum.roblox.com/t/avatar-joint-upgrade-aju-phase-2-rollout-updated-migration-recommendations/4656414). - **Finding joints**: Use `:FindFirstChildWhichIsA("AnimationConstraint")` instead of `:FindFirstChildOfClass("Motor6D")`. For code that must support both old and new rigs, check for AnimationConstraint first, then fall back to Motor6D. - **C0, C1, Part0, Part1**: These properties exist on AnimationConstraint as **read-only** aliases for backwards compatibility. They map to `Attachment0.CFrame`, `Attachment1.CFrame`, `Attachment0.Parent`, and `Attachment1.Parent` respectively. Do not attempt to write to them. - **Do not modify RigAttachment.CFrame directly** — this disrupts animation retargeting and causes performance issues. - **Transform**: Works identically to [Motor6D.Transform](/docs/reference/engine/classes/Motor6D.md) — the [Animator](/docs/reference/engine/classes/Animator.md) writes to it each frame. Layer procedural animations by multiplying into `Transform` during [RunService.PreSimulation](/docs/reference/engine/classes/RunService.md), which stacks with active animation tracks without breaking retargeting. - **IsKinematic**: When `true` (default), behavior is equivalent to Motor6D. Set to `false` to enable force-based physical simulation. - **Type checks**: `animConstraint:IsA("Motor6D")` returns `false`. Update any `IsA("Motor6D")` guards to also accept `"AnimationConstraint"`. - **Server replication**: Instead of setting C0 on the server, use client-side animation evaluation and synchronize data through custom Attributes or [UnreliableRemoteEvent](/docs/reference/engine/classes/UnreliableRemoteEvent.md). ##### Example: Procedural neck rotation ```lua -- Before (Motor6D): writing to C0 directly local originalC0 = neck.C0 RunService.RenderStepped:Connect(function() neck.C0 = originalC0 * computeNeckRotation() end) -- After (AnimationConstraint): multiplying into Transform during PreSimulation RunService.PreSimulation:Connect(function() if not animator.EvaluationThrottled then neck.Transform = computeNeckRotation() * neck.Transform end end) ``` #### Description An `AnimationConstraint` constrains its [Attachments](/docs/reference/engine/classes/Attachment.md) so that they're offset by [Transform](/docs/reference/engine/classes/AnimationConstraint.md). When [IsKinematic](/docs/reference/engine/classes/AnimationConstraint.md) is `true`, the parts follow the transform perfectly (identical to Motor6D behavior). When `false`, the constraint applies forces and torques limited by [MaxForce](/docs/reference/engine/classes/AnimationConstraint.md) and [MaxTorque](/docs/reference/engine/classes/AnimationConstraint.md), enabling physically simulated character movement. ## Properties ### Property: AnimationConstraint.AngularDamping ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Compliance", "capabilities": [ "Physics", "Animation" ], "simulationAccess": true } ``` The damping ratio (ζ) applied to the rotational part of the constraint. Only used if [IsKinematic](/docs/reference/engine/classes/AnimationConstraint.md) is `false`. A value of `1` corresponds to **critical damping**, where the constraint reaches its target orientation as fast as possible without overshoot in the absence of other forces or constraints. Values less than `1` are under-damped and oscillate around the target before settling. Values greater than `1` are over-damped and approach the target more slowly without oscillation. A value of `0` applies no angular damping, causing the constraint to oscillate. Even with this property set to `1`, an `AnimationConstraint` at the root of a multi-body mechanism may still exhibit low-frequency oscillation because it does not "see" the full effective mass of the downstream chain. You can compensate by increasing both [AngularStrength](/docs/reference/engine/classes/AnimationConstraint.md) and `AngularDamping` beyond their defaults. ### Property: AnimationConstraint.AngularStrength ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Compliance", "capabilities": [ "Physics", "Animation" ], "simulationAccess": true } ``` Controls how rigidly the constraint enforces the rotational part of its target [Transform](/docs/reference/engine/classes/AnimationConstraint.md). Only used if [IsKinematic](/docs/reference/engine/classes/AnimationConstraint.md) is `false`. The resulting torque is capped by [MaxTorque](/docs/reference/engine/classes/AnimationConstraint.md). `AngularStrength` is defined as a normalized natural frequency, `AngularStrength = f / 60`, where `f` is the target natural frequency in Hz. The default value of `1` corresponds to a target frequency of 60 Hz and produces strong tracking of the target orientation. A value of `0` applies no torque. Values less than `1` produce a softer, more compliant rotational response, and values greater than `1` produce a stiffer response. Use values significantly greater than `1` with caution, as they may cause the simulation to become unstable. ### Property: AnimationConstraint.IsKinematic ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Physics", "Animation" ], "simulationAccess": true } ``` When `true`, the connected parts follow the [Transform](/docs/reference/engine/classes/AnimationConstraint.md) perfectly without participating in physics simulation. When `false`, the connected parts follow the trajectory using forces and torques limited by [MaxForce](/docs/reference/engine/classes/AnimationConstraint.md) and [MaxTorque](/docs/reference/engine/classes/AnimationConstraint.md). ### Property: AnimationConstraint.LinearDamping ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Compliance", "capabilities": [ "Physics", "Animation" ], "simulationAccess": true } ``` The damping ratio (ζ) applied to the translational part of the constraint. Only used if [IsKinematic](/docs/reference/engine/classes/AnimationConstraint.md) is `false`. A value of `1` corresponds to **critical damping**, where the constraint reaches its target position as fast as possible without overshoot in the absence of other forces or constraints. Values less than `1` are under-damped and oscillate around the target before settling. Values greater than `1` are over-damped and approach the target more slowly without oscillation. A value of `0` applies no linear damping, causing the constraint to oscillate. `LinearDamping` has no effect when [LinearStrength](/docs/reference/engine/classes/AnimationConstraint.md) is `0`. ### Property: AnimationConstraint.LinearStrength ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Compliance", "capabilities": [ "Physics", "Animation" ], "simulationAccess": true } ``` Controls how rigidly the constraint enforces the translational part of its target [Transform](/docs/reference/engine/classes/AnimationConstraint.md). Only used if [IsKinematic](/docs/reference/engine/classes/AnimationConstraint.md) is `false`. The resulting force is capped by [MaxForce](/docs/reference/engine/classes/AnimationConstraint.md). `LinearStrength` uses the same normalized natural frequency definition as [AngularStrength](/docs/reference/engine/classes/AnimationConstraint.md), `LinearStrength = f / 60`, where `f` is the target natural frequency in Hz. The default value of `1` corresponds to a target frequency of 60 Hz and produces strong tracking of the target position. A value of `0` applies no force. Values less than `1` produce a softer, more compliant positional response, and values greater than `1` produce a stiffer response. Splitting angular and linear parameters lets you make orientation tracking stiff while keeping positional tracking soft, or vice-versa. As with [AngularStrength](/docs/reference/engine/classes/AnimationConstraint.md), use values significantly greater than `1` with caution, as they may cause the simulation to become unstable. ### Property: AnimationConstraint.MaxForce ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Compliance", "capabilities": [ "Physics", "Animation" ], "simulationAccess": true } ``` Maximum force magnitude the constraint can apply to achieve its goal. Only used if [IsKinematic](/docs/reference/engine/classes/AnimationConstraint.md) is `false`. ### Property: AnimationConstraint.MaxTorque ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Compliance", "capabilities": [ "Physics", "Animation" ], "simulationAccess": true } ``` Maximum torque the constraint can use to reach its goal. Only used if [IsKinematic](/docs/reference/engine/classes/AnimationConstraint.md) is `false`. ### Property: AnimationConstraint.Transform ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Physics", "Animation" ], "simulationAccess": true } ``` The internal [CFrame](/docs/reference/engine/datatypes/CFrame.md) that is manipulated when the constraint is being animated. Note that `AnimationConstraint` transforms are not applied immediately, but rather as a batch in a parallel job after [RunService.PreSimulation](/docs/reference/engine/classes/RunService.md), immediately before physics steps. The deferred batch update is much more efficient than many immediate updates. If the `AnimationConstraint` is part of an animated model with an [Animator](/docs/reference/engine/classes/Animator.md), then [Transform](/docs/reference/engine/classes/AnimationConstraint.md) is usually overwritten every frame by the [Animator](/docs/reference/engine/classes/Animator.md) after [RunService.PreAnimation](/docs/reference/engine/classes/RunService.md) and before [RunService.PreSimulation](/docs/reference/engine/classes/RunService.md). ## Inherited Members ### From [Constraint](/docs/reference/engine/classes/Constraint.md) - **Property `Active`** (`boolean`): Indicates if the constraint is currently active in the world. - **Property `Attachment0`** (`Attachment`): The Attachment that is connected to - **Property `Attachment1`** (`Attachment`): The Attachment that is connected to - **Property `Color`** (`BrickColor`): The color of the constraint. - **Property `Enabled`** (`boolean`): Toggles whether or not the constraint is enabled. - **Property `Visible`** (`boolean`): Toggles the constraint's visibility. - **Method `GetDebugAppliedForce(bodyId: int): Vector3`**: *(deprecated)* - **Method `GetDebugAppliedTorque(bodyId: int): Vector3`**: *(deprecated)* ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AnimationController last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Animation summary: "Allows animations to be loaded and applied to a character or model in place of a Humanoid." --- # Class: AnimationController > Allows animations to be loaded and applied to a character or model in place of > a [Humanoid](/docs/reference/engine/classes/Humanoid.md). ## Description An object which allows animations to be loaded and applied to a character or model in place of a [Humanoid](/docs/reference/engine/classes/Humanoid.md). Creates an [Animator](/docs/reference/engine/classes/Animator.md) and loads animations to update [Motor6Ds](/docs/reference/engine/classes/Motor6D.md) of said character to react in the way that is described within the animation asset referenced by an [Animation](/docs/reference/engine/classes/Animation.md) object. Note that the [LoadAnimation()](/docs/reference/engine/classes/AnimationController.md) method of this class has been deprecated. Instead, you should call [Animator:LoadAnimation()](/docs/reference/engine/classes/Animator.md) directly from an [Animator](/docs/reference/engine/classes/Animator.md) which can be created manually in Studio and directly referenced in scripts. When the deprecated method is called from an [AnimationController](/docs/reference/engine/classes/AnimationController.md), the controller itself does nothing regarding the animation intended to be loaded, except to automatically generate an [Animator](/docs/reference/engine/classes/Animator.md), onto which the loading call and animation ID are transferred. In this way, the [AnimationController](/docs/reference/engine/classes/AnimationController.md) can be thought of as nothing more than an empty shell for a child [Animator](/docs/reference/engine/classes/Animator.md) object which handles any actual functionality regarding animations. ## Code Samples **Using an AnimationController to animation non-player objects** This code sample demonstrates how an `AnimationController` can be used in place of a `Humanoid` for non player character objects. A basic rig is loaded using `InsertService` and the default `Humanoid` is replaced with an `AnimationController`. An `AnimationTrack` is then created and played. ```lua local InsertService = game:GetService("InsertService") -- Load a model for demonstration local npcModel = InsertService:LoadAsset(516159357):GetChildren()[1] npcModel.Name = "NPC" npcModel.PrimaryPart.Anchored = true npcModel:SetPrimaryPartCFrame(CFrame.new(0, 5, 0)) npcModel.Parent = workspace -- Replace the humanoid with an animationcontroller local humanoid = npcModel:FindFirstChildOfClass("Humanoid") humanoid:Destroy() local animationController = Instance.new("AnimationController") animationController.Parent = npcModel -- Create and load an animation local animation = Instance.new("Animation") animation.AnimationId = "http://www.roblox.com/asset/?id=507771019" -- Roblox dance emote local animationTrack = animationController:LoadAnimation(animation) -- Play the animation animationTrack:Play() ``` ## Methods ### Method: AnimationController:GetPlayingAnimationTracks **Signature:** `AnimationController:GetPlayingAnimationTracks(): Array` Returns an array of all [AnimationTracks](/docs/reference/engine/classes/AnimationTrack.md) that are currently being played by the [AnimationController](/docs/reference/engine/classes/AnimationController.md). A typical use for this function is stopping currently playing tracks using [AnimationTrack:Stop()](/docs/reference/engine/classes/AnimationTrack.md). Note this function will not return [AnimationTracks](/docs/reference/engine/classes/AnimationTrack.md) that have loaded but are not playing. If the developer wishes to track these they will need to index them manually. See below for one example of how this could be achieved: ``` local animationTracks = {} local track = animationController:LoadTrack(animation) table.insert(animationTracks, track) ``` *Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Returns:** `Array` — An array of playing [AnimationTracks](/docs/reference/engine/classes/AnimationTrack.md). **Stop All Tracks** This sample contains a quick function to stop all playing AnimationTracks on an AnimationController or Humanoid. ```lua local function stopAllTracks(animationController) for _, track in pairs(animationController:GetPlayingAnimationTracks()) do track:Stop() end end local animationController = script.Parent:FindFirstChild("AnimationController") stopAllTracks(animationController) ``` ### Method: AnimationController:LoadAnimation **Signature:** `AnimationController:LoadAnimation(animation: Animation): AnimationTrack` > **Deprecated:** This function is deprecated in favor of using [Animator:LoadAnimation()](/docs/reference/engine/classes/Animator.md) directly (the [Animator](/docs/reference/engine/classes/Animator.md) may be created while editing or at runtime). This function loads an [Animation](/docs/reference/engine/classes/Animation.md) onto an [AnimationController](/docs/reference/engine/classes/AnimationController.md), returning an [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md) that can be used for playback. *Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `animation` | `Animation` | | The [Animation](/docs/reference/engine/classes/Animation.md) to be used. | **Returns:** `AnimationTrack` **Using an AnimationController to animation non-player objects** This code sample demonstrates how an `AnimationController` can be used in place of a `Humanoid` for non player character objects. A basic rig is loaded using `InsertService` and the default `Humanoid` is replaced with an `AnimationController`. An `AnimationTrack` is then created and played. ```lua local InsertService = game:GetService("InsertService") -- Load a model for demonstration local npcModel = InsertService:LoadAsset(516159357):GetChildren()[1] npcModel.Name = "NPC" npcModel.PrimaryPart.Anchored = true npcModel:SetPrimaryPartCFrame(CFrame.new(0, 5, 0)) npcModel.Parent = workspace -- Replace the humanoid with an animationcontroller local humanoid = npcModel:FindFirstChildOfClass("Humanoid") humanoid:Destroy() local animationController = Instance.new("AnimationController") animationController.Parent = npcModel -- Create and load an animation local animation = Instance.new("Animation") animation.AnimationId = "http://www.roblox.com/asset/?id=507771019" -- Roblox dance emote local animationTrack = animationController:LoadAnimation(animation) -- Play the animation animationTrack:Play() ``` ## Events ### Event: AnimationController.AnimationPlayed **Signature:** `AnimationController.AnimationPlayed(animationTrack: AnimationTrack)` This event fires whenever the [AnimationController](/docs/reference/engine/classes/AnimationController.md) begins playing an animation. It returns the [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md) playing. The [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md) can be used to access the animation's playback functions and events. It will only fire for animations playing on the specific [AnimationController](/docs/reference/engine/classes/AnimationController.md). See [Humanoid.AnimationPlayed](/docs/reference/engine/classes/Humanoid.md) for the [Humanoid](/docs/reference/engine/classes/Humanoid.md) variant of this function. *Security: None · Capabilities: Animation* **Parameters:** | Name | Type | Description | |------|------|-------------| | `animationTrack` | `AnimationTrack` | The [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md) that was played. | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AnimationFromVideoCreatorService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service --- # Class: AnimationFromVideoCreatorService ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AnimationNodeDefinition last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances --- # Class: AnimationNodeDefinition ## Properties ### Property: AnimationNodeDefinition.NodeId ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Animation" ] } ``` ### Property: AnimationNodeDefinition.NodeType ```json { "type": "AnimationNodeType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Animation", "capabilities": [ "Animation" ] } ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AnimationRigData last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances summary: "Used to store information regarding the model an animation was authored for." --- # Class: AnimationRigData > Used to store information regarding the model an animation was authored for. ## Description An AnimationRigData instance commonly appears in the Data Model as a child of an AnimationClip. It is used to store information regarding the source rig an animation was authored for. It is currently only used for AnimationClips authored on R15 rigs. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AnimationTrack last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Animation tags: - NotCreatable summary: "Controls the playback of an animation on an Animator." --- # Class: AnimationTrack > Controls the playback of an animation on an [Animator](/docs/reference/engine/classes/Animator.md). ## Description Controls the playback of an animation on an [Animator](/docs/reference/engine/classes/Animator.md). This object cannot be created, instead it is returned by the [Animator:LoadAnimation()](/docs/reference/engine/classes/Animator.md) method. ## Properties ### Property: AnimationTrack.Animation ```json { "type": "Animation", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Animation" ] } ``` The [Animation](/docs/reference/engine/classes/Animation.md) object that was used to create this [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md). To create an [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md), you must load an [Animation](/docs/reference/engine/classes/Animation.md) object onto an [Animator](/docs/reference/engine/classes/Animator.md) using the [Animator:LoadAnimation()](/docs/reference/engine/classes/Animator.md) method. **Listen For New Animations** The following code sample prints the name of an animation whenever an [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md) plays on a [Humanoid](/docs/reference/engine/classes/Humanoid.md). This script can be placed in a model with a [Humanoid](/docs/reference/engine/classes/Humanoid.md) child. ```lua local humanoid = script.Parent:WaitForChild("Humanoid") local animator = humanoid:WaitForChild("Animator") animator.AnimationPlayed:Connect(function(animationTrack) local animationName = animationTrack.Animation.Name print("Animation playing " .. animationName) end) ``` ### Property: AnimationTrack.IsPlaying ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Animation" ], "simulationAccess": true } ``` A read-only property that returns true when the [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md) is playing. This property can be used to check if an animation is already playing before playing it (as that would cause it to restart). If you want to obtain all playing [AnimationTracks](/docs/reference/engine/classes/AnimationTrack.md) on an [Animator](/docs/reference/engine/classes/Animator.md) or a [Humanoid](/docs/reference/engine/classes/Humanoid.md), they should use [Animator:GetPlayingAnimationTracks()](/docs/reference/engine/classes/Animator.md) **AnimationTrack IsPlaying** This code sample includes a simple function that will play an AnimationTrack if it is not playing, or otherwise adjust its speed and weight to match the new parameters given. ```lua local function playOrAdjust(animationTrack, fadeTime, weight, speed) if not animationTrack.IsPlaying then animationTrack:Play(fadeTime, weight, speed) else animationTrack:AdjustSpeed(speed) animationTrack:AdjustWeight(weight, fadeTime) end end local animation = Instance.new("Animation") animation.AnimationId = "rbxassetid://507765644" local humanoid = script.Parent:WaitForChild("Humanoid") local animator = humanoid:WaitForChild("Animator") local animationTrack = animator:LoadAnimation(animation) playOrAdjust(animationTrack, 1, 0.6, 1) ``` ### Property: AnimationTrack.Length ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Animation" ] } ``` A read-only property that returns the length (in seconds) of an [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md). This will return `0` until the animation has fully loaded and thus may not be immediately available. When the [AnimationTrack.Speed](/docs/reference/engine/classes/AnimationTrack.md) of an [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md) is equal to `1`, the animation will take [AnimationTrack.Length](/docs/reference/engine/classes/AnimationTrack.md) (in seconds) to complete. **Playing Animation for a Specific Duration** The following function will play an AnimationTrack for a specific duration. This is done by changing the speed of the animation to the length of the animation divided by the desired playback duration. This could be used in situations where a developer wants to play a standard animation for different duration (for example, recharging different abilities). ```lua local function playAnimationForDuration(animationTrack, duration) local speed = animationTrack.Length / duration animationTrack:Play() animationTrack:AdjustSpeed(speed) end local animation = Instance.new("Animation") animation.AnimationId = "rbxassetid://507765000" local humanoid = script.Parent:WaitForChild("Humanoid") local animator = humanoid:WaitForChild("Animator") local animationTrack = animator:LoadAnimation(animation) playAnimationForDuration(animationTrack, 3) ``` ### Property: AnimationTrack.Looped ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Animation" ], "simulationAccess": true } ``` This property sets whether the animation will repeat after finishing. If it is changed while playing the result will take effect after the animation finishes. This property defaults to how it was set in the [Animation Editor](/docs/en-us/animation/editor.md). However, this property can be changed, allowing control over the [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md) while it is running. This property also correctly handles animations played in reverse (negative [AnimationTrack.Speed](/docs/reference/engine/classes/AnimationTrack.md)). After the first keyframe is reached, it will restart at the last keyframe. **Animation Looping** The animation in this example normally loops. After the player and the animation are loaded the animation is played in a non-looped fashion then in a looped fashion. ```lua local Players = game:GetService("Players") local localPlayer = Players.LocalPlayer while not localPlayer.Character do task.wait() end local character = localPlayer.Character local humanoid = character:WaitForChild("Humanoid") local animator = humanoid:WaitForChild("Animator") local animation = Instance.new("Animation") animation.AnimationId = "rbxassetid://507770453" local animationTrack = animator:LoadAnimation(animation) animationTrack.Looped = false task.wait(3) animationTrack:Play() task.wait(4) animationTrack.Looped = true animationTrack:Play() ``` **Play AnimationTrack for a Number of Loops** The function in this code sample will play an AnimationTrack on a loop, for a specific number of loops, before stopping the animation. In some cases the developer may want to stop a looped animation after a certain number of loops have completed, rather than after a certain amount of time. This is where the DidLoop event can be used. ```lua local function playForNumberLoops(animationTrack, number) animationTrack.Looped = true animationTrack:Play() local numberOfLoops = 0 local connection = nil connection = animationTrack.DidLoop:Connect(function() numberOfLoops = numberOfLoops + 1 print("loop: ", numberOfLoops) if numberOfLoops >= number then animationTrack:Stop() connection:Disconnect() -- it's important to disconnect connections when they are no longer needed end end) end local animation = Instance.new("Animation") animation.AnimationId = "rbxassetid://507765644" local humanoid = script.Parent:WaitForChild("Humanoid") local animator = humanoid:WaitForChild("Animator") local animationTrack = animator:LoadAnimation(animation) playForNumberLoops(animationTrack, 5) ``` ### Property: AnimationTrack.Priority ```json { "type": "AnimationPriority", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Animation" ], "simulationAccess": true } ``` This property sets the priority of an [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md). Depending on what this is set to, playing multiple animations at once will look to this property to figure out which [Keyframe](/docs/reference/engine/classes/Keyframe.md) poses should be played over one another. It uses [AnimationPriority](/docs/reference/engine/enums/AnimationPriority.md) which has 7 priority levels: 1. [Action4](/docs/reference/engine/enums/AnimationPriority.md) (highest priority) 2. [Action3](/docs/reference/engine/enums/AnimationPriority.md) 3. [Action2](/docs/reference/engine/enums/AnimationPriority.md) 4. [Action](/docs/reference/engine/enums/AnimationPriority.md) 5. [Movement](/docs/reference/engine/enums/AnimationPriority.md) 6. [Idle](/docs/reference/engine/enums/AnimationPriority.md) 7. [Core](/docs/reference/engine/enums/AnimationPriority.md) (lowest priority) Properly set animation priorities, either through the [Animation Editor](/docs/en-us/animation/editor.md) or through this property, allow multiple animations to be played without them clashing. Where two playing animations direct the target to move the same limb in different ways, the [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md) with the highest priority will show. If both animations have the same priority, the weights of the tracks will be used to combine the animations. ### Property: AnimationTrack.Speed ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Animation" ], "simulationAccess": true } ``` This read-only property gives the current playback speed of the [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md). When equal to `1`, the amount of time an animation takes to complete is equal to [AnimationTrack.Length](/docs/reference/engine/classes/AnimationTrack.md), in seconds. If the speed is adjusted through [AnimationTrack:AdjustSpeed()](/docs/reference/engine/classes/AnimationTrack.md), the actual time it will take a track to play can be computed by dividing the length by the speed. Speed is a unitless quantity. **Animation Speed** In this example a player and an animation is loaded. The Length of an AnimationTrack determines how long the track would take to play if the speed is at 1. If the speed is adjusted, then the actual time it will take a track to play can be computed by dividing the length by the speed. ```lua local ContentProvider = game:GetService("ContentProvider") local Players = game:GetService("Players") local localPlayer = Players.LocalPlayer while not localPlayer.Character do task.wait() end local character = localPlayer.Character local humanoid = character:WaitForChild("Humanoid") local animator = humanoid:WaitForChild("Animator") local animation = Instance.new("Animation") animation.AnimationId = "rbxassetid://507770453" ContentProvider:PreloadAsync({ animation }) local animationTrack = animator:LoadAnimation(animation) local normalSpeedTime = animationTrack.Length / animationTrack.Speed animationTrack:AdjustSpeed(3) local fastSpeedTime = animationTrack.Length / animationTrack.Speed print("At normal speed the animation will play for", normalSpeedTime, "seconds") print("At 3x speed the animation will play for", fastSpeedTime, "seconds") ``` **Playing Animation for a Specific Duration** The following function will play an AnimationTrack for a specific duration. This is done by changing the speed of the animation to the length of the animation divided by the desired playback duration. This could be used in situations where a developer wants to play a standard animation for different duration (for example, recharging different abilities). ```lua local function playAnimationForDuration(animationTrack, duration) local speed = animationTrack.Length / duration animationTrack:Play() animationTrack:AdjustSpeed(speed) end local animation = Instance.new("Animation") animation.AnimationId = "rbxassetid://507765000" local humanoid = script.Parent:WaitForChild("Humanoid") local animator = humanoid:WaitForChild("Animator") local animationTrack = animator:LoadAnimation(animation) playAnimationForDuration(animationTrack, 3) ``` ### Property: AnimationTrack.TimePosition ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Animation" ], "simulationAccess": true } ``` Returns the position in time in seconds that an [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md) is through playing its source animation. Can be set to make the track jump to a specific moment in the animation, but the [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md) must be playing to do so. It can also be used in combination with [AnimationTrack:AdjustSpeed()](/docs/reference/engine/classes/AnimationTrack.md) to freeze the animation at a desired point by setting speed to `0`. **Freeze Animation at Position** The following code sample includes two functions that demonstrate how AdjustSpeed and TimePosition can be used to freeze an animation at a particular point. The first function freezes an animation at a particular point in time (defined in seconds). The second freezes at it at a percentage (between 0 or 100) by multiplying the percentage by the track length. As TimePosition can not be used when an AnimationTrack is not playing, the functions check to make sure the animation is playing before proceeding. ```lua function freezeAnimationAtTime(animationTrack, timePosition) if not animationTrack.IsPlaying then -- Play the animation if it is not playing animationTrack:Play() end -- Set the speed to 0 to freeze the animation animationTrack:AdjustSpeed(0) -- Jump to the desired TimePosition animationTrack.TimePosition = timePosition end function freezeAnimationAtPercent(animationTrack, percentagePosition) if not animationTrack.IsPlaying then -- Play the animation if it is not playing animationTrack:Play() end -- Set the speed to 0 to freeze the animation animationTrack:AdjustSpeed(0) -- Jump to the desired TimePosition animationTrack.TimePosition = (percentagePosition / 100) * animationTrack.Length end local animation = Instance.new("Animation") animation.AnimationId = "rbxassetid://507765644" local humanoid = script.Parent:WaitForChild("Humanoid") local animator = humanoid:WaitForChild("Animator") local animationTrack = animator:LoadAnimation(animation) freezeAnimationAtTime(animationTrack, 0.5) freezeAnimationAtPercent(animationTrack, 50) ``` ### Property: AnimationTrack.WeightCurrent ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Animation" ], "simulationAccess": true } ``` When weight is set in an [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md) it does not change instantaneously but moves from [AnimationTrack.WeightCurrent](/docs/reference/engine/classes/AnimationTrack.md) to [AnimationTrack.WeightTarget](/docs/reference/engine/classes/AnimationTrack.md). The time it takes to do this is determined by the `fadeTime` parameter given when the animation is played, or the weight is adjusted. [AnimationTrack.WeightCurrent](/docs/reference/engine/classes/AnimationTrack.md) can be checked against [AnimationTrack.WeightTarget](/docs/reference/engine/classes/AnimationTrack.md) to see if the desired weight has been reached. Note that these values should not be checked for equality with the `==` operator, as both of these values are floats. To see if [AnimationTrack.WeightCurrent](/docs/reference/engine/classes/AnimationTrack.md) has reached the target weight, it is recommended to see if the distance between those values is sufficiently small. **WeightCurrent and WeightTarget** This code sample loads two animations onto the local player's Humanoid and demonstrates how the fadeTime paramater in AnimationTrack.Play determines how long it takes for an AnimationTrack's WeightCurrent to reach it's WeightTarget. As WeightCurrent and WeightTarget are floats the == operator cannot be used to compare, instead it is more appropriate to check that the difference between them is sufficiently small to assume the weight fade has completed. ```lua local Players = game:GetService("Players") local localPlayer = Players.LocalPlayer while not localPlayer.Character do task.wait() end local character = localPlayer.Character local humanoid = character:WaitForChild("Humanoid") local animator = humanoid:WaitForChild("Animator") local animation1 = Instance.new("Animation") animation1.AnimationId = "rbxassetid://507770453" local animation2 = Instance.new("Animation") animation2.AnimationId = "rbxassetid://507771019" task.wait(3) -- arbitrary wait time to allow the character to fall into place local animationTrack1 = animator:LoadAnimation(animation1) local animationTrack2 = animator:LoadAnimation(animation2) animationTrack1.Priority = Enum.AnimationPriority.Movement animationTrack2.Priority = Enum.AnimationPriority.Action animationTrack1:Play(0.1, 5, 1) animationTrack2:Play(10, 3, 1) local done = false while not done and task.wait(0.1) do if math.abs(animationTrack2.WeightCurrent - animationTrack2.WeightTarget) < 0.001 then print("got there") done = true end end ``` ### Property: AnimationTrack.WeightTarget ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Animation" ], "simulationAccess": true } ``` This read-only property gives the current weight of the [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md). It has a default value of `1` and is set when [AnimationTrack:Play()](/docs/reference/engine/classes/AnimationTrack.md), [AnimationTrack:Stop()](/docs/reference/engine/classes/AnimationTrack.md) or [AnimationTrack:AdjustWeight()](/docs/reference/engine/classes/AnimationTrack.md) is called. When weight is set in an [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md) it does not change instantaneously but moves from [AnimationTrack.WeightCurrent](/docs/reference/engine/classes/AnimationTrack.md) to [AnimationTrack.WeightTarget](/docs/reference/engine/classes/AnimationTrack.md). The time it takes to do this is determined by the `fadeTime` parameter given when the animation is played, or the weight is adjusted. [AnimationTrack.WeightCurrent](/docs/reference/engine/classes/AnimationTrack.md) can be checked against [AnimationTrack.WeightTarget](/docs/reference/engine/classes/AnimationTrack.md) to see if the desired weight has been reached. Note that these values should not be checked for equality with the `==` operator, as both of these values are floats. To see if [AnimationTrack.WeightCurrent](/docs/reference/engine/classes/AnimationTrack.md) has reached the target weight, it is recommended to see if the distance between those values is sufficiently small. **WeightCurrent and WeightTarget** This code sample loads two animations onto the local player's Humanoid and demonstrates how the fadeTime paramater in AnimationTrack.Play determines how long it takes for an AnimationTrack's WeightCurrent to reach it's WeightTarget. As WeightCurrent and WeightTarget are floats the == operator cannot be used to compare, instead it is more appropriate to check that the difference between them is sufficiently small to assume the weight fade has completed. ```lua local Players = game:GetService("Players") local localPlayer = Players.LocalPlayer while not localPlayer.Character do task.wait() end local character = localPlayer.Character local humanoid = character:WaitForChild("Humanoid") local animator = humanoid:WaitForChild("Animator") local animation1 = Instance.new("Animation") animation1.AnimationId = "rbxassetid://507770453" local animation2 = Instance.new("Animation") animation2.AnimationId = "rbxassetid://507771019" task.wait(3) -- arbitrary wait time to allow the character to fall into place local animationTrack1 = animator:LoadAnimation(animation1) local animationTrack2 = animator:LoadAnimation(animation2) animationTrack1.Priority = Enum.AnimationPriority.Movement animationTrack2.Priority = Enum.AnimationPriority.Action animationTrack1:Play(0.1, 5, 1) animationTrack2:Play(10, 3, 1) local done = false while not done and task.wait(0.1) do if math.abs(animationTrack2.WeightCurrent - animationTrack2.WeightTarget) < 0.001 then print("got there") done = true end end ``` ## Methods ### Method: AnimationTrack:AdjustSpeed **Signature:** `AnimationTrack:AdjustSpeed(speed?: float): ()` This method changes the [AnimationTrack.Speed](/docs/reference/engine/classes/AnimationTrack.md) of an animation. A positive value for speed plays the animation forward, a negative one plays it backwards, and `0` pauses it. A track's initial speed is set as a parameter in [AnimationTrack:Play()](/docs/reference/engine/classes/AnimationTrack.md). However a track's [AnimationTrack.Speed](/docs/reference/engine/classes/AnimationTrack.md) can be changed during playback using this method. When speed is equal to `1`, the amount of time an animation takes to complete is equal to [AnimationTrack.Length](/docs/reference/engine/classes/AnimationTrack.md) (in seconds). When is adjusted, then the actual time it will take a track to play can be computed by dividing the length by the speed. [AnimationTrack.Speed](/docs/reference/engine/classes/AnimationTrack.md) is a unitless quantity. *Security: None · Thread Safety: Unsafe · Capabilities: Animation · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `speed` | `float` | `1` | The playback speed the animation is to be changed to. | **Returns:** `()` **Playing Animation for a Specific Duration** The following function will play an AnimationTrack for a specific duration. This is done by changing the speed of the animation to the length of the animation divided by the desired playback duration. This could be used in situations where a developer wants to play a standard animation for different duration (for example, recharging different abilities). ```lua local function playAnimationForDuration(animationTrack, duration) local speed = animationTrack.Length / duration animationTrack:Play() animationTrack:AdjustSpeed(speed) end local animation = Instance.new("Animation") animation.AnimationId = "rbxassetid://507765000" local humanoid = script.Parent:WaitForChild("Humanoid") local animator = humanoid:WaitForChild("Animator") local animationTrack = animator:LoadAnimation(animation) playAnimationForDuration(animationTrack, 3) ``` **Animation Speed** In this example a player and an animation is loaded. The Length of an AnimationTrack determines how long the track would take to play if the speed is at 1. If the speed is adjusted, then the actual time it will take a track to play can be computed by dividing the length by the speed. ```lua local ContentProvider = game:GetService("ContentProvider") local Players = game:GetService("Players") local localPlayer = Players.LocalPlayer while not localPlayer.Character do task.wait() end local character = localPlayer.Character local humanoid = character:WaitForChild("Humanoid") local animator = humanoid:WaitForChild("Animator") local animation = Instance.new("Animation") animation.AnimationId = "rbxassetid://507770453" ContentProvider:PreloadAsync({ animation }) local animationTrack = animator:LoadAnimation(animation) local normalSpeedTime = animationTrack.Length / animationTrack.Speed animationTrack:AdjustSpeed(3) local fastSpeedTime = animationTrack.Length / animationTrack.Speed print("At normal speed the animation will play for", normalSpeedTime, "seconds") print("At 3x speed the animation will play for", fastSpeedTime, "seconds") ``` ### Method: AnimationTrack:AdjustWeight **Signature:** `AnimationTrack:AdjustWeight(weight?: float, fadeTime?: float): ()` Changes the weight of an animation, with the optional `fadeTime` parameter determining how long it takes for [AnimationTrack.WeightCurrent](/docs/reference/engine/classes/AnimationTrack.md) to reach [AnimationTrack.WeightTarget](/docs/reference/engine/classes/AnimationTrack.md). When weight is set in an [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md) it does not change instantaneously but moves from [AnimationTrack.WeightCurrent](/docs/reference/engine/classes/AnimationTrack.md) to [AnimationTrack.WeightTarget](/docs/reference/engine/classes/AnimationTrack.md). The time it takes to do this is determined by the `fadeTime` parameter given when the animation is played, or the weight is adjusted. The animation weighting system is used to determine how [AnimationTracks](/docs/reference/engine/classes/AnimationTrack.md) playing at the same priority are blended together. The default weight is `1`, and no movement will be visible on an [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md) with a weight of `0`. The pose that is shown at any point in time is determined by the weighted average of all the [Poses](/docs/reference/engine/classes/Pose.md) and the [AnimationTrack.WeightCurrent](/docs/reference/engine/classes/AnimationTrack.md) of each [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md). See below for an example of animation blending in practice. In most cases blending animations is not required and using [AnimationTrack.Priority](/docs/reference/engine/classes/AnimationTrack.md) is more suitable. *Security: None · Thread Safety: Unsafe · Capabilities: Animation · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `weight` | `float` | `1` | The weight the animation is to be changed to. | | `fadeTime` | `float` | `0.100000001` | The duration of time that the animation will fade between the old weight and the new weight for. | **Returns:** `()` **AnimationTrack Change Weight** This code sample includes a function that changes the weight of an AnimationTrack and yields until the weight has changed to the new target weight. The purpose of this sample is to demonstrate how the fadeTime parameter of AnimationTrack.AdjustWeight works. In most cases, if a developer wishes to yield over the fadeTime it is recommended they use wait(fadeTime). ```lua local function changeWeight(animationTrack, weight, fadeTime) animationTrack:AdjustWeight(weight, fadeTime) local startTime = tick() while math.abs(animationTrack.WeightCurrent - weight) > 0.001 do task.wait() end print("Time taken to change weight " .. tostring(tick() - startTime)) end local animation = Instance.new("Animation") animation.AnimationId = "rbxassetid://507765644" local humanoid = script.Parent:WaitForChild("Humanoid") local animator = humanoid:WaitForChild("Animator") local animationTrack = animator:LoadAnimation(animation) changeWeight(animationTrack, 0.6, 1) ``` ### Method: AnimationTrack:GetMarkerReachedSignal **Signature:** `AnimationTrack:GetMarkerReachedSignal(name: string): RBXScriptSignal` This method returns an [RBXScriptSignal](/docs/reference/engine/datatypes/RBXScriptSignal.md) (event) similar to the [AnimationTrack.KeyframeReached](/docs/reference/engine/classes/AnimationTrack.md) event, except it only fires when a specified [KeyframeMarker](/docs/reference/engine/classes/KeyframeMarker.md) has been hit in an [animation](/docs/reference/engine/classes/Animation.md). The difference allows for greater control of when the event will fire. To learn more about using this method, see [here](/docs/en-us/animation/events.md). #### See Also - [KeyframeMarker](/docs/reference/engine/classes/KeyframeMarker.md) - [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md), controls the playback of an animation on a [Humanoid](/docs/reference/engine/classes/Humanoid.md) or [AnimationController](/docs/reference/engine/classes/AnimationController.md) - [Keyframe](/docs/reference/engine/classes/Keyframe.md), holds the [Poses](/docs/reference/engine/classes/Pose.md) applied to joints in a [Model](/docs/reference/engine/classes/Model.md) at a given point of time in an animation - [Keyframe:AddMarker()](/docs/reference/engine/classes/Keyframe.md) - [Keyframe:RemoveMarker()](/docs/reference/engine/classes/Keyframe.md) - [Keyframe:GetMarkers()](/docs/reference/engine/classes/Keyframe.md) *Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name` | `string` | | The name of the [KeyframeMarker](/docs/reference/engine/classes/KeyframeMarker.md) the signal is being created for. Not to be confused with the name of the [Keyframe](/docs/reference/engine/classes/Keyframe.md). | **Returns:** `RBXScriptSignal` — The signal created and fired when the animation reaches the created [KeyframeMarker](/docs/reference/engine/classes/KeyframeMarker.md). **Listening to Keyframe Markers** This `LocalScript` code waits for the local player's `Humanoid` object to load, then it creates a new `Animation` instance with the proper [Animation.AnimationId](/docs/reference/engine/classes/Animation.md). The animation is then loaded onto the humanoid, creating an `AnimationTrack`, and the track is played with [AnimationTrack:Play()](/docs/reference/engine/classes/AnimationTrack.md). Following that, the [AnimationTrack:GetMarkerReachedSignal()](/docs/reference/engine/classes/AnimationTrack.md) function detects when the "KickEnd" marker is hit. ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local character = player.Character or player.Character:Wait() local humanoid = character:WaitForChild("Humanoid") -- Create new "Animation" instance local kickAnimation = Instance.new("Animation") -- Set its "AnimationId" to the corresponding animation asset ID kickAnimation.AnimationId = "rbxassetid://2515090838" -- Load animation onto the humanoid local kickAnimationTrack = humanoid:LoadAnimation(kickAnimation) -- Play animation track kickAnimationTrack:Play() -- If a named event was defined for the animation, connect it to "GetMarkerReachedSignal()" kickAnimationTrack:GetMarkerReachedSignal("KickEnd"):Connect(function(paramString) print(paramString) end) ``` ### Method: AnimationTrack:GetParameter **Signature:** `AnimationTrack:GetParameter(key: string): Variant` *Security: None · Thread Safety: Unsafe · Capabilities: Animation · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | | **Returns:** `Variant` ### Method: AnimationTrack:GetParameterDefaults **Signature:** `AnimationTrack:GetParameterDefaults(): Dictionary` *Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Returns:** `Dictionary` ### Method: AnimationTrack:GetTargetInstance **Signature:** `AnimationTrack:GetTargetInstance(name: string): Instance?` *Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name` | `string` | | | **Returns:** `Instance?` ### Method: AnimationTrack:GetTargetNames **Signature:** `AnimationTrack:GetTargetNames(): Array` *Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Returns:** `Array` ### Method: AnimationTrack:GetTimeOfKeyframe **Signature:** `AnimationTrack:GetTimeOfKeyframe(keyframeName: string): double` Returns the time position of the first [Keyframe](/docs/reference/engine/classes/Keyframe.md) of the given name in an [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md). If multiple [Keyframes](/docs/reference/engine/classes/Keyframe.md) share the same name, it will return the earliest one in the animation. This method will return an error if it is uses with an invalid keyframe name (one that does not exist for example) or if the underlying [Animation](/docs/reference/engine/classes/Animation.md) has not yet loaded. To address this make sure only correct keyframe names are used and the animation has loaded before calling this method. To check if the animation has loaded, verify that the [AnimationTrack.Length](/docs/reference/engine/classes/AnimationTrack.md) is greater than zero. *Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `keyframeName` | `string` | | The name associated with the [Keyframe](/docs/reference/engine/classes/Keyframe.md) to be found. | **Returns:** `double` — The time, in seconds, the [Keyframe](/docs/reference/engine/classes/Keyframe.md) occurs at normal playback speed. **Jump To Keyframe** This sample includes a function that will jump to the first keyframe of a specified name in an AnimationTrack. As AnimationTrack.TimePosition cannot be set while the animation is not playing the function first checks to see if the animation is playing. This sample will only work once an Animation has loaded. ```lua local function jumpToKeyframe(animationTrack, keyframeName) local timePosition = animationTrack:GetTimeOfKeyframe(keyframeName) if not animationTrack.IsPlaying then animationTrack:Play() end animationTrack.TimePosition = timePosition end local ANIMATION_ID = 0 local KEYFRAME_NAME = "Test" local animation = Instance.new("Animation") animation.AnimationId = "rbxassetid://" .. ANIMATION_ID local humanoid = script.Parent:WaitForChild("Humanoid") local animator = humanoid:WaitForChild("Animator") local animationTrack = animator:LoadAnimation(animation) jumpToKeyframe(animationTrack, KEYFRAME_NAME) ``` ### Method: AnimationTrack:Play **Signature:** `AnimationTrack:Play(fadeTime?: float, weight?: float, speed?: float): ()` When [AnimationTrack:Play()](/docs/reference/engine/classes/AnimationTrack.md) is called the track's animation will begin playing and the weight of the animation will increase from `0` to the specified `weight` (defaults to `1`) over the specified `fadeTime`. The speed the [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md) will play at is determined by the speed parameter (defaults to `1`). When the speed is equal to `1` the number of seconds the track will take to complete is equal to the track's [AnimationTrack.Length](/docs/reference/engine/classes/AnimationTrack.md) property. For example, a speed of `2` will cause the track to play twice as fast. The weight and speed of the animation can also be changed after the animation has begun playing by using the [AnimationTrack:AdjustWeight()](/docs/reference/engine/classes/AnimationTrack.md) and [AnimationTrack:AdjustSpeed()](/docs/reference/engine/classes/AnimationTrack.md) methods. If you want to start the animation at a specific point using [AnimationTrack.TimePosition](/docs/reference/engine/classes/AnimationTrack.md), it's important the animation is played before this is done. *Security: None · Thread Safety: Unsafe · Capabilities: Animation · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `fadeTime` | `float` | `0.100000001` | The duration of time that the animation's weight should be faded in for. | | `weight` | `float` | `1` | The weight the animation is to be played at. | | `speed` | `float` | `1` | The playback speed of the animation. | **Returns:** `()` **Playing Animation for a Specific Duration** The following function will play an AnimationTrack for a specific duration. This is done by changing the speed of the animation to the length of the animation divided by the desired playback duration. This could be used in situations where a developer wants to play a standard animation for different duration (for example, recharging different abilities). ```lua local function playAnimationForDuration(animationTrack, duration) local speed = animationTrack.Length / duration animationTrack:Play() animationTrack:AdjustSpeed(speed) end local animation = Instance.new("Animation") animation.AnimationId = "rbxassetid://507765000" local humanoid = script.Parent:WaitForChild("Humanoid") local animator = humanoid:WaitForChild("Animator") local animationTrack = animator:LoadAnimation(animation) playAnimationForDuration(animationTrack, 3) ``` **Freeze Animation at Position** The following code sample includes two functions that demonstrate how AdjustSpeed and TimePosition can be used to freeze an animation at a particular point. The first function freezes an animation at a particular point in time (defined in seconds). The second freezes at it at a percentage (between 0 or 100) by multiplying the percentage by the track length. As TimePosition can not be used when an AnimationTrack is not playing, the functions check to make sure the animation is playing before proceeding. ```lua function freezeAnimationAtTime(animationTrack, timePosition) if not animationTrack.IsPlaying then -- Play the animation if it is not playing animationTrack:Play() end -- Set the speed to 0 to freeze the animation animationTrack:AdjustSpeed(0) -- Jump to the desired TimePosition animationTrack.TimePosition = timePosition end function freezeAnimationAtPercent(animationTrack, percentagePosition) if not animationTrack.IsPlaying then -- Play the animation if it is not playing animationTrack:Play() end -- Set the speed to 0 to freeze the animation animationTrack:AdjustSpeed(0) -- Jump to the desired TimePosition animationTrack.TimePosition = (percentagePosition / 100) * animationTrack.Length end local animation = Instance.new("Animation") animation.AnimationId = "rbxassetid://507765644" local humanoid = script.Parent:WaitForChild("Humanoid") local animator = humanoid:WaitForChild("Animator") local animationTrack = animator:LoadAnimation(animation) freezeAnimationAtTime(animationTrack, 0.5) freezeAnimationAtPercent(animationTrack, 50) ``` ### Method: AnimationTrack:SetParameter **Signature:** `AnimationTrack:SetParameter(key: string, value: Variant): ()` *Security: None · Thread Safety: Unsafe · Capabilities: Animation · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | | | `value` | `Variant` | | | **Returns:** `()` ### Method: AnimationTrack:SetTargetInstance **Signature:** `AnimationTrack:SetTargetInstance(name: string, target: Instance?): ()` *Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name` | `string` | | | | `target` | `Instance?` | | | **Returns:** `()` ### Method: AnimationTrack:Stop **Signature:** `AnimationTrack:Stop(fadeTime?: float): ()` Stops the [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md). Once called, the weight of the animation will move towards zero over a length of time specified by the optional `fadeTime` parameter. For example, if `Stop()` is called with a `fadeTime` of `2`, it will take two seconds for the weight of the track to reach zero and its effects completely end. Please note this will be the case regardless of the initial weight of the animation. #### Usage Notes After calling `Stop()`, subsequent calls to `Stop()` and [AdjustWeight()](/docs/reference/engine/classes/AnimationTrack.md) will not work as expected. To adjust a track that's fading out, you must first call [Play()](/docs/reference/engine/classes/AnimationTrack.md) to reset the [IsPlaying](/docs/reference/engine/classes/AnimationTrack.md) property to `true`. It is not recommended to use a `fadeTime` of `0` in an attempt to override this effect and end the animation immediately for [Motor6Ds](/docs/reference/engine/classes/Motor6D.md) that have their [Motor.MaxVelocity](/docs/reference/engine/classes/Motor.md) set to zero, as this causes the joints to freeze in place. If it must end immediately, ensure the [Motor.MaxVelocity](/docs/reference/engine/classes/Motor.md) of [Motor6Ds](/docs/reference/engine/classes/Motor6D.md) in your rig are high enough for them to snap properly. *Security: None · Thread Safety: Unsafe · Capabilities: Animation · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `fadeTime` | `float` | `0.100000001` | The time, in seconds, for which animation weight is to be faded out over. | **Returns:** `()` **AnimationTrack Stop** This code sample includes a function that stops an AnimationTrack with a specific fadeTime, and yields until the fade is completed and the weight of the AnimationTrack is equal to zero. The purpose of this sample is to demonstrate how the fadeTime parameter of AnimationTrack.Stop works. In most cases, if a developer wishes to yield over the fadeTime it is recommended they use wait(fadeTime). ```lua local function fadeOut(animationTrack, fadeTime) animationTrack:Stop(fadeTime) local startTime = tick() while animationTrack.WeightCurrent > 0 do task.wait() end local timeTaken = tick() - startTime print("Time taken for weight to reset: " .. tostring(timeTaken)) end local animation = Instance.new("Animation") animation.AnimationId = "rbxassetid://507765644" local humanoid = script.Parent:WaitForChild("Humanoid") local animator = humanoid:WaitForChild("Animator") local animationTrack = animator:LoadAnimation(animation) animationTrack:Play() fadeOut(animationTrack, 1) ``` ## Events ### Event: AnimationTrack.DidLoop **Signature:** `AnimationTrack.DidLoop()` This event fires whenever a looped [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md) completes a loop, on the next update. Currently it may also fire at the exact end of a non looped animation track but this behavior should not be relied upon. *Security: None · Capabilities: Animation* **Play AnimationTrack for a Number of Loops** The function in this code sample will play an AnimationTrack on a loop, for a specific number of loops, before stopping the animation. In some cases the developer may want to stop a looped animation after a certain number of loops have completed, rather than after a certain amount of time. This is where the DidLoop event can be used. ```lua local function playForNumberLoops(animationTrack, number) animationTrack.Looped = true animationTrack:Play() local numberOfLoops = 0 local connection = nil connection = animationTrack.DidLoop:Connect(function() numberOfLoops = numberOfLoops + 1 print("loop: ", numberOfLoops) if numberOfLoops >= number then animationTrack:Stop() connection:Disconnect() -- it's important to disconnect connections when they are no longer needed end end) end local animation = Instance.new("Animation") animation.AnimationId = "rbxassetid://507765644" local humanoid = script.Parent:WaitForChild("Humanoid") local animator = humanoid:WaitForChild("Animator") local animationTrack = animator:LoadAnimation(animation) playForNumberLoops(animationTrack, 5) ``` ### Event: AnimationTrack.Ended **Signature:** `AnimationTrack.Ended()` Fires when the [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md) is completely done moving anything in the world, meaning the animation has finished playing, the "fade out" is finished, and the subject is in a neutral pose. You can use this to take action when the animation track's subject is back in a neutral pose that's unaffected by the [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md) or to clean up the [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md). *Security: None · Capabilities: Animation* **AnimationTrack Ended** The function in this code sample plays an animationTrack and yields until it has stopped and ended, printing at each step along the way. ```lua local InsertService = game:GetService("InsertService") local Players = game:GetService("Players") -- Create an NPC model to animate. local npcModel = Players:CreateHumanoidModelFromUserIdAsync(129687796) npcModel.Name = "JoeNPC" npcModel.Parent = workspace npcModel:MoveTo(Vector3.new(0, 15, 4)) local humanoid = npcModel:WaitForChild("Humanoid") -- Load an animation. local animationModel = InsertService:LoadAsset(2510238627) local animation = animationModel:FindFirstChildWhichIsA("Animation", true) local animator = humanoid:WaitForChild("Animator") local animationTrack = animator:LoadAnimation(animation) -- Connect to Stopped event. This fires when animation stops of -- it's own accord, or we explicitly call Stop. animationTrack.Stopped:Connect(function() print("Animation stopped") end) -- Connect to Ended event. This fires when when animation is completely -- finished affecting the world. In this case it will fire 3 seconds -- after we call animationTrack:Stop because we pass in a 3 -- second fadeOut. animationTrack.Ended:Connect(function() print("Animation ended") animationTrack:Destroy() end) -- Run, give it a bit to play, then stop. print("Calling Play") animationTrack:Play() task.wait(10) print("Calling Stop") animationTrack:Stop(3) ``` ### Event: AnimationTrack.KeyframeReached **Signature:** `AnimationTrack.KeyframeReached(keyframeName: string)` Fires every time playback of an [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md) reaches a [Keyframe](/docs/reference/engine/classes/Keyframe.md) that does not have the default name of `Keyframe`. This event lets you run code at predefined points in an animation (set by [Keyframe](/docs/reference/engine/classes/Keyframe.md) names). [Keyframe](/docs/reference/engine/classes/Keyframe.md) names do not need to be unique. For example, if an animation has three keyframes named `Particles`, this event will fire each time one of these keyframes is reached. [Keyframe](/docs/reference/engine/classes/Keyframe.md) names can be set in the [Animation Editor](/docs/en-us/animation/editor.md) when creating or editing an animation. They cannot however be set by a [Script](/docs/reference/engine/classes/Script.md) on an existing animation prior to playing it. *Security: None · Capabilities: Animation* **Parameters:** | Name | Type | Description | |------|------|-------------| | `keyframeName` | `string` | The name of the [Keyframe](/docs/reference/engine/classes/Keyframe.md) reached. | ### Event: AnimationTrack.Stopped **Signature:** `AnimationTrack.Stopped()` Fires whenever the [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md) finishes playing. This event has a number of uses. It can be used to wait until an [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md) has stopped before continuing (for example, if chaining a series of animations to play after each other). It can also be used to clean up any [Instances](/docs/reference/engine/classes/Instance.md) created during the animation playback. *Security: None · Capabilities: Animation* **AnimationTrack Stopped** The function in this code sample will play an animationTrack and yield until it has stopped, before printing. ```lua local function yieldPlayAnimation(animationTrack, fadeTime, weight, speed) animationTrack:Play(fadeTime, weight, speed) animationTrack.Stopped:Wait() print("Animation has stopped") end local animation = Instance.new("Animation") animation.AnimationId = "rbxassetid://507765644" local humanoid = script.Parent:WaitForChild("Humanoid") local animator = humanoid:WaitForChild("Animator") local animationTrack = animator:LoadAnimation(animation) yieldPlayAnimation(animationTrack, 1, 0.6, 1) ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Animator last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances summary: "Responsible for the playback and replication of Animations." --- # Class: Animator > Responsible for the playback and replication of [Animations](/docs/reference/engine/classes/Animation.md). ## Description `Animator` is the main class responsible for the playback and replication of [Animations](/docs/reference/engine/classes/Animation.md). All replication of playing [AnimationTracks](/docs/reference/engine/classes/AnimationTrack.md) is handled through the `Animator` instance. See [Animation in Roblox](/docs/en-us/animation.md) to learn how to create and add pre-built or custom animations to your game. ## Properties ### Property: Animator.EvaluationThrottled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "Safe", "category": "Behavior", "capabilities": [ "Animation" ] } ``` ### Property: Animator.PreferLodEnabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Animation" ] } ``` ### Property: Animator.RootMotion ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "Safe", "category": "Behavior", "capabilities": [ "Animation" ] } ``` ### Property: Animator.RootMotionWeight ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "Safe", "category": "Behavior", "capabilities": [ "Animation" ] } ``` ## Methods ### Method: Animator:ApplyJointVelocities **Signature:** `Animator:ApplyJointVelocities(motors: Variant): ()` Given the current set of [AnimationTracks](/docs/reference/engine/classes/AnimationTrack.md) playing and their current times and play speeds, this method computes relative velocities between the parts and applies them to [Motor6D.Part1](/docs/reference/engine/classes/Motor6D.md) (the part which `Animator` considers the "child" part). These relative velocity calculations and assignments happen in the order provided. This method doesn't apply velocities for a given joint if both of the joint's parts are currently part of the same assembly; for example, if they are still connected directly or indirectly by motors or welds. Note that this method doesn't disable or remove the joints for you. You must disable or otherwise remove the rigid joints from the assembly before calling this method. The given [Motor6Ds](/docs/reference/engine/classes/Motor6D.md) are not required to be descendants of the [DataModel](/docs/reference/engine/classes/DataModel.md). Removing the joints from the [DataModel](/docs/reference/engine/classes/DataModel.md) before calling this method is supported. *Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `motors` | `Variant` | | | **Returns:** `()` ### Method: Animator:GetPlayingAnimationTracks **Signature:** `Animator:GetPlayingAnimationTracks(): Array` Returns the list of currently active [AnimationTracks](/docs/reference/engine/classes/AnimationTrack.md) on this [Animator](/docs/reference/engine/classes/Animator.md). This includes tracks that are fading out and does **not** depend on [AnimationTrack.IsPlaying](/docs/reference/engine/classes/AnimationTrack.md) being `true`; as a result, [GetTrackByAnimationId()](/docs/reference/engine/classes/Animator.md) may be a better option to get a specific [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md) by its asset ID. *Security: None · Thread Safety: Unsafe · Capabilities: Animation · Simulation Access: true* **Returns:** `Array` ### Method: Animator:GetTrackByAnimationId **Signature:** `Animator:GetTrackByAnimationId(animationId: ContentId): AnimationTrack?` Returns an existing [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md) on this [Animator](/docs/reference/engine/classes/Animator.md) that was loaded from an [Animation](/docs/reference/engine/classes/Animation.md) with the given animation ID. Unlike [LoadAnimation()](/docs/reference/engine/classes/Animator.md), this method does **not** create a new [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md) instance; it only looks up a track that was previously loaded. If multiple tracks have been loaded for the same animation ID, this method returns the first match. If no matching track exists, it returns `nil`. *Security: None · Thread Safety: Unsafe · Capabilities: Animation · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `animationId` | `ContentId` | | The asset ID of the [Animation](/docs/reference/engine/classes/Animation.md) whose loaded track should be retrieved. | **Returns:** `AnimationTrack?` — The first [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md) on this `Animator` that was loaded from the given animation ID, or `nil` if none has been loaded. **Get Animation Track by ID** The following [LocalScript](/docs/reference/engine/classes/LocalScript.md), when placed in [StarterPlayerScripts](/docs/reference/engine/classes/StarterPlayerScripts.md), loads a "kick" animation onto the player's character and plays it. Then, after 1 second within a [RunService:BindToSimulation()](/docs/reference/engine/classes/RunService.md) binding, it gets the animation track by its animation asset ID and uses that reference to stop playing the track. ```lua local Players = game:GetService("Players") local RunService = game:GetService("RunService") local ANIM_ID = "rbxassetid://2515090838" local player = Players.LocalPlayer local character = player.Character or player.CharacterAdded:Wait() -- Ensure that the character's humanoid contains an Animator object local humanoid = character:WaitForChild("Humanoid") local animator = humanoid:WaitForChild("Animator") -- Create a new Animation instance and assign an animation asset ID local kickAnimation = Instance.new("Animation") kickAnimation.AnimationId = ANIM_ID -- Load the animation onto the animator local kickAnimationTrack = animator:LoadAnimation(kickAnimation) -- Play the animation track after a short delay task.wait(3) kickAnimationTrack:Play() local startTime = tick() RunService:BindToSimulation(function(deltaTime: number) if tick() - startTime > 1 then -- Get track by ID and stop its playback local kickTrack = animator:GetTrackByAnimationId(ANIM_ID) if kickTrack and kickTrack.IsPlaying then kickTrack:Stop() end end end) ``` ### Method: Animator:LoadAnimation **Signature:** `Animator:LoadAnimation(animation: Animation): AnimationTrack` This method loads the given [Animation](/docs/reference/engine/classes/Animation.md) onto this `Animator`, returning a playable [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md). When called on an `Animator` within models that the client has network ownership of, for example the local player's character or from [BasePart:SetNetworkOwner()](/docs/reference/engine/classes/BasePart.md), this method also loads the animation for the server as well. Note that the `Animator` must be in the [Workspace](/docs/reference/engine/classes/Workspace.md) before making a call to `LoadAnimation()` or else it will be unable to retrieve the [AnimationClipProvider](/docs/reference/engine/classes/AnimationClipProvider.md) service and throw an error. #### Warning Do **not** use `LoadAnimation()` in an attempt to retrieve an existing track. Calling `LoadAnimation()` always creates a **new** [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md) instance which may impact game performance if overused. Instead, use [GetTrackByAnimationId()](/docs/reference/engine/classes/Animator.md) when you need to look up a track that was already loaded. #### Loading an Animation on Client or Server In order for [AnimationTracks](/docs/reference/engine/classes/AnimationTrack.md) to replicate correctly, it's important to know when they should be loaded on the client or on the server: - If an `Animator` is a descendant of a [Humanoid](/docs/reference/engine/classes/Humanoid.md) or [AnimationController](/docs/reference/engine/classes/AnimationController.md) in a player's [Player.Character](/docs/reference/engine/classes/Player.md), animations started on that player's client will be replicated to the server and other clients. - If the `Animator` is **not** a descendant of a player character, its animations must be loaded and started on the server to replicate. The `Animator` object must be initially created on the server and replicated to clients for animation replication to work at all. If an `Animator` is created locally, then [AnimationTracks](/docs/reference/engine/classes/AnimationTrack.md) loaded with that `Animator` will not replicate. *Security: None · Thread Safety: Unsafe · Capabilities: Animation · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `animation` | `Animation` | | The [Animation](/docs/reference/engine/classes/Animation.md) to be used. | **Returns:** `AnimationTrack` ### Method: Animator:RegisterEvaluationParallelCallback **Signature:** `Animator:RegisterEvaluationParallelCallback(callback: Function): ()` *Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `callback` | `Function` | | | **Returns:** `()` ### Method: Animator:StepAnimations **Signature:** `Animator:StepAnimations(deltaTime: float): ()` Increments the [AnimationTrack.TimePosition](/docs/reference/engine/classes/AnimationTrack.md) of all playing [AnimationTracks](/docs/reference/engine/classes/AnimationTrack.md) that are loaded onto the `Animator`, applying the offsets to the model associated with the `Animator`. For use in the command bar or by plugins only. The `deltaTime` parameter determines the number of seconds to increment on the animation's progress. Typically this method will be called in a loop to preview the length of an animation (see example). Note that once animations have stopped playing, the model's joints will need to be manually reset to their original positions (see example). *Security: PluginSecurity · Thread Safety: Unsafe · Capabilities: Animation* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `deltaTime` | `float` | | The amount of time in seconds animation playback is to be incremented. by. | **Returns:** `()` **Preview Animation in Studio** This code sample includes a function that can be used to preview an animation on a character model in Roblox Studio, without needing to run the game. ```lua local RunService = game:GetService("RunService") local function previewAnimation(model, animation) -- Find the AnimationController and Animator local animationController = model:FindFirstChildOfClass("AnimationController") local animator = animationController and animationController:FindFirstChildOfClass("Animator") if not animator then return end -- Load the Animation to create an AnimationTrack local track = animationController:LoadAnimation(animation) track:Play() -- Preview the animation local startTime = tick() while (tick() - startTime) < track.Length do local step = RunService.Heartbeat:wait() animator:StepAnimations(step) end -- Stop the animation track:Stop(0) animator:StepAnimations(0) -- Reset the joints for _, descendant in pairs(model:GetDescendants()) do if descendant:IsA("Motor6D") then local joint = descendant joint.CurrentAngle = 0 joint.Transform = CFrame.new() end end end local character = script.Parent local animation = Instance.new("Animation") animation.AnimationId = "rbxassetid://507765644" previewAnimation(character, animation) ``` ## Events ### Event: Animator.AnimationPlayed **Signature:** `Animator.AnimationPlayed(animationTrack: AnimationTrack)` Fires for all [AnimationTrack:Play()](/docs/reference/engine/classes/AnimationTrack.md) calls on [AnimationTracks](/docs/reference/engine/classes/AnimationTrack.md) created and owned by the `Animator`. *Security: None · Capabilities: Animation* **Parameters:** | Name | Type | Description | |------|------|-------------| | `animationTrack` | `AnimationTrack` | | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Annotation last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances --- # Class: Annotation ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ArcHandles last_updated: 2026-06-29T19:34:07Z inherits: - HandlesBase - PartAdornment - GuiBase3d - GuiBase - Instance - Object type: class memory_category: Instances summary: "The ArcHandles object places 3D arc handles around any 3D object that its Adornee is set to." --- # Class: ArcHandles > The [ArcHandles](/docs/reference/engine/classes/ArcHandles.md) object places 3D arc handles around any 3D object that > its [Adornee](/docs/reference/engine/classes/PartAdornment.md) is set to. ## Description The **ArcHandles** object places 3D arc handles around any 3D object that its [Adornee](/docs/reference/engine/classes/PartAdornment.md) is set to. For handles to be interactive, it must be parented to a player's [PlayerGui](/docs/reference/engine/classes/PlayerGui.md) or the [CoreGui](/docs/reference/engine/classes/CoreGui.md). ![ArcHandles example](../../../assets/engine-api/classes/ArcHandles/ArcHandles-Example.jpg) ## Properties ### Property: ArcHandles.Axes ```json { "type": "Axes", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic", "UI", "Input" ] } ``` Sets the current Axes ArcHandles will show. ## Events ### Event: ArcHandles.MouseButton1Down **Signature:** `ArcHandles.MouseButton1Down(axis: Axis)` Fired when the left mouse button goes down on one of the GUI handles. *Security: None · Capabilities: Basic, UI, Input* **Parameters:** | Name | Type | Description | |------|------|-------------| | `axis` | `Axis` | | ### Event: ArcHandles.MouseButton1Up **Signature:** `ArcHandles.MouseButton1Up(axis: Axis)` Fired when the left mouse button is released on one of the GUI handles. *Security: None · Capabilities: Basic, UI, Input* **Parameters:** | Name | Type | Description | |------|------|-------------| | `axis` | `Axis` | | ### Event: ArcHandles.MouseDrag **Signature:** `ArcHandles.MouseDrag(axis: Axis, relativeAngle: float, deltaRadius: float)` Fired when the mouse moves while the MouseButton1Down event has fired, but the left mouse button has not been released yet. *Security: None · Capabilities: Basic, UI, Input* **Parameters:** | Name | Type | Description | |------|------|-------------| | `axis` | `Axis` | | | `relativeAngle` | `float` | | | `deltaRadius` | `float` | | ### Event: ArcHandles.MouseEnter **Signature:** `ArcHandles.MouseEnter(axis: Axis)` Fired when a mouse "enters" the GUI handle. *Security: None · Capabilities: Basic, UI, Input* **Parameters:** | Name | Type | Description | |------|------|-------------| | `axis` | `Axis` | | ### Event: ArcHandles.MouseLeave **Signature:** `ArcHandles.MouseLeave(axis: Axis)` Fired when the mouse leaves the GUI handle. *Security: None · Capabilities: Basic, UI, Input* **Parameters:** | Name | Type | Description | |------|------|-------------| | `axis` | `Axis` | | ## Inherited Members ### From [PartAdornment](/docs/reference/engine/classes/PartAdornment.md) - **Property `Adornee`** (`BasePart`): Sets the object to adorn to. ### From [GuiBase3d](/docs/reference/engine/classes/GuiBase3d.md) - **Property `Color`** (`BrickColor`): Sets the color of a GUI object. *(deprecated, hidden)* - **Property `Color3`** (`Color3`): Sets the color of this GuiBase3d object. - **Property `Transparency`** (`float`): Sets the transparency of this GuiBase3d object. - **Property `Visible`** (`boolean`): Determines whether this GuiBase3d object and its descendants will ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AssetDeliveryProxy last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated --- # Class: AssetDeliveryProxy ## Properties ### Property: AssetDeliveryProxy.Interface ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: AssetDeliveryProxy.Port ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: AssetDeliveryProxy.StartServer ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AssetPatchSettings last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated --- # Class: AssetPatchSettings ## Properties ### Property: AssetPatchSettings.ContentId ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: AssetPatchSettings.OutputPath ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: AssetPatchSettings.PatchId ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AssetService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service summary: "A non-replicated service that handles asset-related queries to the Roblox web API." --- # Class: AssetService > A non-replicated service that handles asset-related queries to the Roblox web > API. ## Description **AssetService** is a non-replicated service that handles asset-related queries to the Roblox web API. ## Properties ### Property: AssetService.AllowInsertFreeAssets ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior" } ``` This property can only be modified in Studio's **Experience Settings** by changing **Allow Loading Third Party Assets**. When `false` (default), [AssetService:LoadAssetAsync()](/docs/reference/engine/classes/AssetService.md) can only load assets that meet one of the following: - The asset must be **created or owned** by the game creator. - The asset must be **shared** by the asset owner. - The asset must be owned by Roblox. When `true`, [AssetService:LoadAssetAsync()](/docs/reference/engine/classes/AssetService.md) can additionally load any public free asset on the Creator Store. ## Methods ### Method: AssetService:ComposeDecalAsync **Signature:** `AssetService:ComposeDecalAsync(decal: Decal, layers: Array): ()` Modifies an existing [Decal](/docs/reference/engine/classes/Decal.md) to contain a composed texture derived from one or more layered texture sets. Each set can include color, roughness, metalness, and normal maps. Each layer in `layers` is composited in the order they are provided, with the color map alpha channel used to determine blending. - There is a limit of 8 layers. - Layering order is bottom-to-top: the first layer in the list provides the bottom-most textures. - Each dictionary table should contain the following key-value pairs: - **ColorMap**: A [Content](/docs/reference/engine/datatypes/Content.md) referencing a color (albedo) texture by assetId. - **RoughnessMap**: A [Content](/docs/reference/engine/datatypes/Content.md) referencing a roughness texture by assetId. - **MetalnessMap**: A [Content](/docs/reference/engine/datatypes/Content.md) referencing a metalness texture by assetId. - **NormalMap**: A [Content](/docs/reference/engine/datatypes/Content.md) referencing a normal texture by assetId. - `ColorMap` is mandatory in every layer. The ColorMap's alpha channel is used to control blending of the entire layer. - `NormalMap`, `MetalnessMap`, `RoughnessMap` are optional. If omitted, this layer doesn't perform any blending for those maps. *Yields · Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `decal` | `Decal` | | A [Decal](/docs/reference/engine/classes/Decal.md) instance that will be modified to contain a representation of the layers. Any existing maps on this instance will be cleared. | | `layers` | `Array` | | An array of dictionary tables that maps PBR names to [Content](/docs/reference/engine/datatypes/Content.md) IDs. | **Returns:** `()` ```lua local AssetService = game:GetService("AssetService") -- Create two decal instances local decalA = Instance.new("Decal") local decalB = Instance.new("Decal") -- Build composite info table local compositeInfo = { { ColorMap = decalA.ColorMapContent, RoughnessMap = decalA.RoughnessMapContent, MetalnessMap = decalA.MetalnessMapContent, NormalMap = decalA.NormalMapContent, }, { ColorMap = Content.fromAssetId(123456), RoughnessMap = decalB.RoughnessMapContent, MetalnessMap = decalB.MetalnessMapContent, NormalMap = decalB.NormalMapContent, } } -- Create a decal to compose onto local compositeDecal = Instance.new("Decal") -- Compose an existing decal using AssetService local success, errorMessage = pcall(function() AssetService:ComposeDecalAsync(compositeDecal, compositeInfo) end) if success then print("Decal composed successfully!") -- Logic to show/use the decal goes here else warn("Failed to compose decal:", errorMessage) end ``` ### Method: AssetService:CreateAssetAsync **Signature:** `AssetService:CreateAssetAsync(object: Object, assetType: AssetType, requestParameters?: Dictionary): Tuple` Uploads a new asset to Roblox from the given object. Currently, this method can only be used in locally loaded plugins and uploads assets without prompting first. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetCreateUpdate* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `object` | `Object` | | The object to be created as an asset. | | `assetType` | `AssetType` | | Currently supported types are: - [AssetType.Model](/docs/reference/engine/enums/AssetType.md) – with `object` as any valid [Instance](/docs/reference/engine/classes/Instance.md) root. - [AssetType.Plugin](/docs/reference/engine/enums/AssetType.md) – with `object` as any valid [Instance](/docs/reference/engine/classes/Instance.md) root. - [AssetType.Mesh](/docs/reference/engine/enums/AssetType.md) – with `object` as any valid [EditableMesh](/docs/reference/engine/classes/EditableMesh.md) root. - [AssetType.Image](/docs/reference/engine/enums/AssetType.md) – with `object` as any valid [EditableImage](/docs/reference/engine/classes/EditableImage.md) root. | | `requestParameters` | `Dictionary` | `nil` | Options table containing asset metadata: - `Name` – Name of the asset as a string. Defaults to `[object.Name]`. - `Description` – Description of the asset as a string. Defaults to `"Created with AssetService:CreateAssetAsync"`. - `CreatorId` – ID of the asset creator as a number. Defaults to the logged in Roblox Studio user for Plugin context. Required for Open Cloud Luau Execution context. - `CreatorType` – [AssetCreatorType](/docs/reference/engine/enums/AssetCreatorType.md) indicating the type of asset creator. Defaults to [AssetCreatorType.User](/docs/reference/engine/enums/AssetCreatorType.md) in Plugin context. Required for Open Cloud Luau Execution context. - `IsPackage` – Boolean value, only applicable to the [AssetType.Model](/docs/reference/engine/enums/AssetType.md) type. Defaults to `true`. | **Returns:** `Tuple` — The [CreateAssetResult](/docs/reference/engine/enums/CreateAssetResult.md) and asset ID pair if successful. **AssetService:CreateAssetAsync** The following code creates a Mesh asset from an EditableMesh. ```lua local AssetService = game:GetService("AssetService") local editableMesh = AssetService:CreateEditableMesh() -- add vertices, faces, and uvs to the mesh local requestParameters = { CreatorId = 123, CreatorType = Enum.AssetCreatorType.User, Name = "My asset", Description = "a good asset", } local ok, result, idOrUploadErr = pcall(function() return AssetService:CreateAssetAsync(editableMesh, Enum.AssetType.Mesh, requestParameters) end) if not ok then warn(`error calling CreateAssetAsync: {result}`) elseif result == Enum.CreateAssetResult.Success then print(`success, new asset ID: {idOrUploadErr}`) else warn(`upload error in CreateAssetAsync: {result}, {idOrUploadErr}`) end ``` ### Method: AssetService:CreateAssetVersionAsync **Signature:** `AssetService:CreateAssetVersionAsync(object: Object, assetType: AssetType, assetId: int64, requestParameters?: Dictionary): Tuple` Uploads a new version for an existing asset from the given object. Currently, this method can only be used in locally loaded plugins and uploads assets without prompting first. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetCreateUpdate* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `object` | `Object` | | The object to be created as an asset. | | `assetType` | `AssetType` | | Currently supported types are: - [AssetType.Model](/docs/reference/engine/enums/AssetType.md) – with `object` as any valid [Instance](/docs/reference/engine/classes/Instance.md) root. - [AssetType.Plugin](/docs/reference/engine/enums/AssetType.md) – with `object` as any valid [Instance](/docs/reference/engine/classes/Instance.md) root. - [AssetType.Mesh](/docs/reference/engine/enums/AssetType.md) – with `object` as any valid [EditableMesh](/docs/reference/engine/classes/EditableMesh.md) root. - [AssetType.Image](/docs/reference/engine/enums/AssetType.md) – with `object` as any valid [EditableImage](/docs/reference/engine/classes/EditableImage.md) root. | | `assetId` | `int64` | | The ID of the asset for the new version. | | `requestParameters` | `Dictionary` | `nil` | Options table containing asset metadata: - `Name` – A `string`. Name of the asset. Default: object.Name. - `Description` – A `string`. Description of the asset. Default: "Created with AssetService:CreateAssetAsync". - `CreatorId` – A `number`. ID of the asset creator. Default: The logged in Roblox Studio user for Plugin context. Required for Open Cloud Luau Execution context. - `CreatorType` – A [AssetCreatorType](/docs/reference/engine/enums/AssetCreatorType.md). Type of asset creator. Default: [AssetCreatorType.User](/docs/reference/engine/enums/AssetCreatorType.md) in Plugin context. Required for Open Cloud Luau Execution context. - `IsPackage` – A `bool`. Only applicable to the [AssetType.Model](/docs/reference/engine/enums/AssetType.md) type. Default: true. | **Returns:** `Tuple` — The [CreateAssetResult](/docs/reference/engine/enums/CreateAssetResult.md) and asset version number pair if successful. **AssetService:CreateAssetVersionAsync** The following code creates a new Model asset version. ```lua local AssetService = game:GetService("AssetService") local assetIdToUpdate = 321 local model = Instance.new("Model") local requestParameters = { CreatorId = 123, CreatorType = Enum.AssetCreatorType.User, } local ok, result, versionOrUploadErr = pcall(function() return AssetService:CreateAssetVersionAsync(model, Enum.AssetType.Model, assetIdToUpdate, requestParameters) end) if not ok then warn(`error calling CreateAssetVersionAsync: {result}`) elseif result == Enum.CreateAssetResult.Success then print(`success, new asset version: {versionOrUploadErr}`) else warn(`upload error in CreateAssetVersionAsync: {result}, {versionOrUploadErr}`) end ``` ### Method: AssetService:CreateDataModelContentAsync **Signature:** `AssetService:CreateDataModelContentAsync(content: Content, options: Dictionary?): Tuple` Creates ephemeral, [DataModel](/docs/reference/engine/classes/DataModel.md)-scoped content from the provided content input. If the server storage budget is exhausted during this call, the creation will fail and the method will return [CreateContentResult.StorageLimitExceeded](/docs/reference/engine/enums/CreateContentResult.md) alongside an empty [Content](/docs/reference/engine/datatypes/Content.md) object. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `content` | `Content` | | Reference to the input content. Currently, this only supports [Content](/docs/reference/engine/datatypes/Content.md) wrapping a [EditableMesh](/docs/reference/engine/classes/EditableMesh.md) or [EditableImage](/docs/reference/engine/classes/EditableImage.md). | | `options` | `Dictionary?` | | Optional dictionary containing configuration controls for the created [DataModel](/docs/reference/engine/classes/DataModel.md) content. Currently no controls are surfaced and this parameter exists for future functionality. | **Returns:** `Tuple` — A tuple containing an [CreateContentResult](/docs/reference/engine/enums/CreateContentResult.md) indicating the success or failure of the request, and the resulting [DataModel](/docs/reference/engine/classes/DataModel.md)-scoped [Opaque](/docs/reference/engine/enums/ContentSourceType.md) [Content](/docs/reference/engine/datatypes/Content.md). ### Method: AssetService:CreateEditableImage **Signature:** `AssetService:CreateEditableImage(editableImageOptions: Dictionary?): EditableImage` Creates a new [EditableImage](/docs/reference/engine/classes/EditableImage.md). By default, the resolution is set at 512×512, but you can specify a different size using the method's option table. If the device‑specific editable memory budget is exhausted, creation fails and this method returns `nil`. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `editableImageOptions` | `Dictionary?` | | Options table containing controls for the method: - `Size` – A [Vector2](/docs/reference/engine/datatypes/Vector2.md) that specifies the image's desired width and height. | **Returns:** `EditableImage` ### Method: AssetService:CreateEditableImageAsync **Signature:** `AssetService:CreateEditableImageAsync(content: Content, editableImageOptions: Dictionary?): EditableImage` Creates a new [EditableImage](/docs/reference/engine/classes/EditableImage.md) object populated with the given texture. Non-asset texture IDs such as `rbxthumb://` are supported. If using an image asset, it must be associated with and/or owned by a creator of the experience, or it must have been created inside the experience. If the device-specific editable memory budget is exhausted, creation will fail and this method will return `nil`. See the [EditableImage](/docs/reference/engine/classes/EditableImage.md) documentation for special considerations when using this API. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetRead* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `content` | `Content` | | Reference to asset content stored externally or as an object within the place, wrapping a single value of one of the supported [ContentSourceType](/docs/reference/engine/enums/ContentSourceType.md) values. | | `editableImageOptions` | `Dictionary?` | | Table containing options for the created [EditableImage](/docs/reference/engine/classes/EditableImage.md). Currently no options are available since resizing via [Size](/docs/reference/engine/classes/EditableImage.md) is not supported. | **Returns:** `EditableImage` — A new [EditableImage](/docs/reference/engine/classes/EditableImage.md) containing the provided image. ### Method: AssetService:CreateEditableMesh **Signature:** `AssetService:CreateEditableMesh(editableMeshOptions: Dictionary?): EditableMesh` Creates a new, empty [EditableMesh](/docs/reference/engine/classes/EditableMesh.md). Vertices, triangles, and their attributes can be added dynamically to it. If the device‑specific editable memory budget is exhausted, creation will fail and this method will return `nil`. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `editableMeshOptions` | `Dictionary?` | | Table containing options for the created [EditableMesh](/docs/reference/engine/classes/EditableMesh.md). Currently no options are available since [FixedSize](/docs/reference/engine/classes/EditableMesh.md) will always be `false` for empty editable meshes. | **Returns:** `EditableMesh` ### Method: AssetService:CreateEditableMeshAsync **Signature:** `AssetService:CreateEditableMeshAsync(content: Content, editableMeshOptions: Dictionary?): EditableMesh` Returns a new [EditableMesh](/docs/reference/engine/classes/EditableMesh.md) object created from an existing [EditableMesh](/docs/reference/engine/classes/EditableMesh.md) or mesh [Content](/docs/reference/engine/datatypes/Content.md) ID. By default, an [EditableMesh](/docs/reference/engine/classes/EditableMesh.md) created from this method will be fixed size such that mesh data can only be modified, not added nor removed. A fixed size [EditableMesh](/docs/reference/engine/classes/EditableMesh.md) consumes less memory and should be preferred when possible. If the device-specific editable memory budget is exhausted, creation will fail and this method will return `nil`. See the **Enabling for Published Experiences** and **Permissions** sections of [EditableMesh](/docs/reference/engine/classes/EditableMesh.md) for special considerations when using this API. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetRead* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `content` | `Content` | | Reference to asset content stored externally or as an object within the place, wrapping a single value of one of the supported [ContentSourceType](/docs/reference/engine/enums/ContentSourceType.md) values. | | `editableMeshOptions` | `Dictionary?` | | Options table containing controls for the method: - `FixedSize` – A `bool`. Default value is `true`, and the returned [EditableMesh](/docs/reference/engine/classes/EditableMesh.md) will not allow you to add or remove vertices, only modify their values. Set to `false` if the ability to change the mesh topology is required, at the expense of using more memory. | **Returns:** `EditableMesh` — The new [EditableMesh](/docs/reference/engine/classes/EditableMesh.md) object. ### Method: AssetService:CreateMeshPartAsync **Signature:** `AssetService:CreateMeshPartAsync(meshContent: Content, options?: Dictionary): MeshPart` This method creates a [MeshPart](/docs/reference/engine/classes/MeshPart.md) with a specified [CollisionFidelity](/docs/reference/engine/classes/MeshPart.md), [RenderFidelity](/docs/reference/engine/classes/MeshPart.md), and [FluidFidelity](/docs/reference/engine/classes/MeshPart.md). Because [MeshPart.MeshId](/docs/reference/engine/classes/MeshPart.md) is read only, this method is for creating a mesh with any mesh ID through scripts, without having to clone an existing [MeshPart](/docs/reference/engine/classes/MeshPart.md). It throws errors if creation fails. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `meshContent` | `Content` | | Reference to asset content stored externally or as an object within the place, wrapping a single value of one of the supported [ContentSourceType](/docs/reference/engine/enums/ContentSourceType.md) values. | | `options` | `Dictionary` | `nil` | Options table containing one or more controls for the method: - `CollisionFidelity` – The value of [CollisionFidelity](/docs/reference/engine/classes/MeshPart.md) in the resulting part. Defaults to [CollisionFidelity.Default](/docs/reference/engine/enums/CollisionFidelity.md) if the option is absent or the `options` table is `nil`. - `RenderFidelity` – The value of [RenderFidelity](/docs/reference/engine/classes/MeshPart.md) in the resulting part. Defaults to [RenderFidelity.Automatic](/docs/reference/engine/enums/RenderFidelity.md) if the option is absent or the `options` table is `nil`. - `FluidFidelity` – The value of [FluidFidelity](/docs/reference/engine/classes/MeshPart.md) in the resulting part. Defaults to [FluidFidelity.Automatic](/docs/reference/engine/enums/FluidFidelity.md) if the option is absent or the `options` table is `nil`. | **Returns:** `MeshPart` ### Method: AssetService:CreatePlaceAsync **Signature:** `AssetService:CreatePlaceAsync(placeName: string, templatePlaceID: int64, description: string): int64` Clones a place through the given `templatePlaceID` and returns the [PlaceId](/docs/reference/engine/classes/DataModel.md) of the new place, which you can use with [TeleportService](/docs/reference/engine/classes/TeleportService.md). The clone place displays within the inventory of the place's creator with the given name and description. Note that the template place must have template copying enabled through place settings. You cannot use this method to clone places that you don't own. Frequent use of this API is not recommended, particularly if the created places contain scripts, as updating the code in a large volume of places quickly becomes infeasible. For user-generated worlds, consider serializing user creations and saving them in [DataStores](/docs/reference/engine/classes/DataStore.md) instead. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetCreateUpdate* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `placeName` | `string` | | Name of the new place. | | `templatePlaceID` | `int64` | | [PlaceId](/docs/reference/engine/classes/DataModel.md) of the place to clone. | | `description` | `string` | | Description of the new place. | **Returns:** `int64` — [PlaceId](/docs/reference/engine/classes/DataModel.md) of the new place. ### Method: AssetService:CreatePlaceInPlayerInventoryAsync **Signature:** `AssetService:CreatePlaceInPlayerInventoryAsync(player: Instance, placeName: string, templatePlaceID: int64, description: string): int64` Clones a place through the given `templatePlaceID` and puts it into the inventory of the given player. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetCreateUpdate* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Instance` | | | | `placeName` | `string` | | | | `templatePlaceID` | `int64` | | | | `description` | `string` | | | **Returns:** `int64` ### Method: AssetService:CreateSurfaceAppearanceAsync **Signature:** `AssetService:CreateSurfaceAppearanceAsync(content: Dictionary): SurfaceAppearance` Creates a new [SurfaceAppearance](/docs/reference/engine/classes/SurfaceAppearance.md) object using the provided content maps. Currently, content only supports [EditableImage](/docs/reference/engine/classes/EditableImage.md) and only content maps can be specified, but functionality will expand to include asset IDs as input. If you need to achieve this today, you can create an [EditableImage](/docs/reference/engine/classes/EditableImage.md) from an asset ID using the following: [AssetService:CreateEditableImageAsync(Content.fromUri(uri))](/docs/reference/engine/classes/AssetService.md) Default values will be used for all other [SurfaceAppearance](/docs/reference/engine/classes/SurfaceAppearance.md) properties. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `content` | `Dictionary` | | Dictionary containing the following key-value pairs: - `ColorMap` — A [Content](/docs/reference/engine/datatypes/Content.md) object that contains the color map. Default is `nil`. - `MetalnessMap` — A [Content](/docs/reference/engine/datatypes/Content.md) object that contains the metalness map. If more than one channel is present, only the red channel is used. Default is `nil`. - `NormalMap` — A [Content](/docs/reference/engine/datatypes/Content.md) object that contains the normal map. Default is `nil`. - `RoughnessMap` — A [Content](/docs/reference/engine/datatypes/Content.md) object that contains the roughness map. If more than one channel is present, only the red channel is used. Default is `nil`. - `EmissiveMask` — A [Content](/docs/reference/engine/datatypes/Content.md) object that contains the emissive mask. If more than one channel is present, only the red channel is used. Default is `nil`. | **Returns:** `SurfaceAppearance` — A new [SurfaceAppearance](/docs/reference/engine/classes/SurfaceAppearance.md) instance with the given maps from the `content` parameter. **AssetService:CreateSurfaceAppearanceAsync** The following code creates a [SurfaceAppearance](/docs/reference/engine/classes/SurfaceAppearance.md) instance with the given [EditableImage](/docs/reference/engine/classes/EditableImage.md) content and assigns it to a [MeshPart](/docs/reference/engine/classes/MeshPart.md). ```lua local AssetService = game:GetService("AssetService") local Workspace = game:GetService("Workspace") -- Red color local colorEditableImage = AssetService:CreateEditableImage({ Size = Vector2.new(4, 4) }) colorEditableImage:DrawRectangle(Vector2.zero, colorEditableImage.Size, Color3.fromRGB(255, 0, 0), 0, Enum.ImageCombineType.Overwrite) -- Metalness (only look at the red channel) local metalnessEditableImage = AssetService:CreateEditableImage({ Size = Vector2.new(1, 1) }) metalnessEditableImage:DrawRectangle(Vector2.zero, metalnessEditableImage.Size, Color3.new(1, 0, 0), 0, Enum.ImageCombineType.Overwrite) -- Roughness (only look at the red channel) local roughnessEditableImage = AssetService:CreateEditableImage({ Size = Vector2.new(1, 1) }) roughnessEditableImage:DrawRectangle(Vector2.zero, roughnessEditableImage.Size, Color3.new(0.2, 0, 0), 0, Enum.ImageCombineType.Overwrite) local surfaceAppearance = AssetService:CreateSurfaceAppearanceAsync({ ColorMap = Content.fromObject(colorEditableImage), MetalnessMap = Content.fromObject(metalnessEditableImage), RoughnessMap = Content.fromObject(roughnessEditableImage) }) local meshPart = Instance.new("MeshPart") meshPart.Parent = Workspace -- Apply surface appearance to mesh part surfaceAppearance.Parent = meshPart ``` ### Method: AssetService:GetAssetIdsForPackageAsync **Signature:** `AssetService:GetAssetIdsForPackageAsync(packageAssetId: int64): Array` Returns an array of asset IDs that are contained in a specified package. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetRead* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `packageAssetId` | `int64` | | | **Returns:** `Array` — Asset IDs that are contained in a specified package. ### Method: AssetService:GetAudioMetadataAsync **Signature:** `AssetService:GetAudioMetadataAsync(idList: Array): Array` Provides relevant metadata about a specific audio source (artist, title, duration, type, etc.). *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetRead* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `idList` | `Array` | | Array of asset or content IDs for which to retrieve metadata. Max batch size is 30. | **Returns:** `Array` — Array of dictionary tables in the same order as the request, where each dictionary contains the following metadata for its asset/content: - `AssetId` (string) - `Title` (string) - `Artist` (string) - `Duration` (number) in seconds - `AudioType` ([AudioSubType](/docs/reference/engine/enums/AudioSubType.md)) Note that if an error occurs on fetching metadata for any of the requested assets, for example the asset ID doesn't exist, its dictionary table is still included in the returned array but it only contains the `AssetId` field for reference purposes. Additionally, if the `AudioType` cannot be determined for a given asset (perhaps because it's private audio), the resulting dictionary will not contain an `AudioType` entry. ```lua local AssetService = game:GetService("AssetService") local SoundService = game:GetService("SoundService") local trackIDs = { SoundService.Sound1.SoundId, SoundService.Sound2.SoundId, SoundService.Sound3.SoundId, SoundService.Sound4.SoundId, } local success, result = pcall(function() return AssetService:GetAudioMetadataAsync(trackIDs) end) if success then for i = 1, #trackIDs do local contentId = "rbxassetid://" .. result[i].AssetId if trackIDs[i] == contentId then print(result[i].Title, "by", result[i].Artist) else warn("No metadata fetched for requested asset #" .. tostring(i)) end end end ``` ### Method: AssetService:GetBundleDetailsAsync **Signature:** `AssetService:GetBundleDetailsAsync(bundleId: int64): Dictionary` This function returns details of the contents of the specified bundle. If the bundle ID does not exist, it throws `HTTP 400 (Bad Request)`. If `bundleId` is not convertible to an integer, it throws `Unable to cast string to int64`. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetRead* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `bundleId` | `int64` | | The ID of the specified bundle. | **Returns:** `Dictionary` — Dictionary with the following key-value pairs containing details about the specified bundle: - `Id` — Bundle ID (same as passed `bundleId` argument) - `Name` — Bundle name - `Description` — Bundle description - `BundleType` — String representing the [BundleType](/docs/reference/engine/enums/BundleType.md), for example `"BodyParts"` or `"DynamicHead"` - `Items` — Array of items in the bundle, each with details represented through the following keys: - `Id` — Item ID - `Name` — Item name - `Type` — Item type such as `"Asset"` - `AssetType` — String representing the [AvatarAssetType](/docs/reference/engine/enums/AvatarAssetType.md) - `SupportsHeadShapes` — Whether the asset supports head shape swapping. Only present if `AssetType` is `"DynamicHead"`. **Getting Bundle Details** ```lua local AssetService = game:GetService("AssetService") local BUNDLE_ID = 14 local success, result = pcall(function() return AssetService:GetBundleDetailsAsync(BUNDLE_ID) end) if success then print(result) --[[ { ["BundleType"] = "BodyParts", ["Description"] = "The year is 5003, Battlebot 5000 must face his mightiest foe, or face becoming obsolete.", ["Id"] = 14, ["Items"] = { [1] = {...}, [2] = { ["Id"] = 1678225030, ["Name"] = "SinisterBot 5001 Left Arm", ["Type"] = "Asset" }, [3] = {...}, [4] = {...}, [5] = {...}, [6] = {...}, [7] = {...} }, ["Name"] = "SinisterBot 5001" } --]] end ``` ### Method: AssetService:GetGamePlacesAsync **Signature:** `AssetService:GetGamePlacesAsync(): Instance` Returns a [StandardPages](/docs/reference/engine/classes/StandardPages.md) object which contains the name and [PlaceId](/docs/reference/engine/classes/DataModel.md) of places within the current experience. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetRead* **Returns:** `Instance` **AssetService:GetGamePlacesAsync** The following code prints the name and [PlaceId](/docs/reference/engine/classes/DataModel.md) of each place in the experience. ```lua local AssetService = game:GetService("AssetService") local placePages = AssetService:GetGamePlacesAsync() while true do for _, place in placePages:GetCurrentPage() do print("Name:", place.Name) print("PlaceId:", place.PlaceId) end if placePages.IsFinished then break end placePages:AdvanceToNextPageAsync() end ``` ### Method: AssetService:LoadAssetAsync **Signature:** `AssetService:LoadAssetAsync(assetId: int64): Instance` Loads the latest version of an asset from the given `assetId` and returns it wrapped in a [Model](/docs/reference/engine/classes/Model.md). This method is the modern replacement for [InsertService:LoadAsset()](/docs/reference/engine/classes/InsertService.md) and [InsertService:LoadAssetVersion()](/docs/reference/engine/classes/InsertService.md) methods. Calls to this function may fail if the asset does not exist, or if the server providing the model is having problems. It is recommended to wrap calls to this function in `pcall()` to handle potential errors. ```lua local AssetService = game:GetService("AssetService") local assetId = 257489726 local success, model = pcall(AssetService.LoadAssetAsync, AssetService, assetId) if success and model then print("Model loaded successfully") model.Parent = workspace else warn("Model failed to load:", model) -- 'model' will contain the error message end ``` #### Script Sandbox Security For enhanced security, the returned [Model](/docs/reference/engine/classes/Model.md) is sandboxed by default ([Sandboxed](/docs/reference/engine/classes/Model.md) is `true`) and has no script [Capabilities](/docs/reference/engine/enums/SecurityCapability.md). This prevents untrusted scripts descending from the returned Model from running. To enable scripts in a model you trust, you can manually grant a safe set of [Capabilities](/docs/reference/engine/enums/SecurityCapability.md) on the Model after loading. #### Third-Party Asset Loading Unlike [InsertService:LoadAsset()](/docs/reference/engine/classes/InsertService.md), this method can load public assets created by **third parties** (assets not owned by the experience creator). To enable this functionality, toggle on **Allow Loading Third Party Assets** in Studio's **Experience Settings**. If this setting is disabled (which it is by default), `LoadAssetAsync` will only load assets that are owned by the experience creator, behaving identically to the old [InsertService:LoadAsset()](/docs/reference/engine/classes/InsertService.md) security check. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: LoadUnownedAsset* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `assetId` | `int64` | | The asset ID number of the asset being loaded. | **Returns:** `Instance` — A [Model](/docs/reference/engine/classes/Model.md) instance containing the loaded asset. ### Method: AssetService:PromptCreateAssetAsync **Signature:** `AssetService:PromptCreateAssetAsync(player: Player, instance: Instance, assetType: AssetType): Tuple` Allows in-experience asset creation for users by prompting a publish dialog. When called, it presents a dialog to the user, allowing them to enter a name, description, and preview the asset. Upon submitting, it saves the asset to the user's inventory. Can only be invoked on the server side. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetCreateUpdate* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Player` | | The user who submits an asset creation. | | `instance` | `Instance` | | The asset to be created. Currently can't contain scripts or nest non-public assets. | | `assetType` | `AssetType` | | The asset type. Currently can only be [AssetType.Model](/docs/reference/engine/enums/AssetType.md). | **Returns:** `Tuple` — The [PromptCreateAssetResult](/docs/reference/engine/enums/PromptCreateAssetResult.md) and asset ID pair if successful. ### Method: AssetService:PromptImportAnimationClipFromVideoAsync **Signature:** `AssetService:PromptImportAnimationClipFromVideoAsync(player: Player, progressCallback: Function): Tuple` *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetManagement* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Player` | | | | `progressCallback` | `Function` | | | **Returns:** `Tuple` ### Method: AssetService:SavePlaceAsync **Signature:** `AssetService:SavePlaceAsync(requestParameters: Dictionary?): ()` Saves the current state of the place. Keep in mind the following guidelines and restrictions: - This method only works for places that are created with [AssetService:CreatePlaceAsync()](/docs/reference/engine/classes/AssetService.md) or that have the API enabled through the place's settings. - This method overwrites the previous state of the place. To revert a save, publish an older version of the place. - There are cases when saves can occur simultaneously in Studio and in multiple experience servers. The order of the saves happen in the order they are called. - An active Team Create session in Studio blocks all saves from occurring. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetCreateUpdate* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `requestParameters` | `Dictionary?` | | Optional dictionary that includes `SaveWithoutPublish`, a boolean indicating whether to save with publish or without publish, and `PlaceId`, the destination place ID to save over. An example usage would be: AssetService:SavePlaceAsync({PlaceId = 1, SaveWithoutPublish = true}). If PlaceId is not provided, the default behavior will save over the current original place which is calling SavePlaceAsync. If SaveWithoutPublish is not provided, the default behavior is SaveWithoutPublish=false. | **Returns:** `()` ### Method: AssetService:SearchAudioAsync **Signature:** `AssetService:SearchAudioAsync(searchParameters: AudioSearchParams): AudioPages` Returns a [AudioPages](/docs/reference/engine/classes/AudioPages.md) object containing the result of the given search. Will not return fields with empty values. Note that this method has a low HTTP request limit and can throw an error, so it should always be wrapped in `pcall()` for error handling. Possible error messages include: | Error Message | Reason | | --- | --- | | HTTP 429 (Too Many Requests) | [AssetService:SearchAudio()](/docs/reference/engine/classes/AssetService.md) has been called too many times. | | Unexpected type for data, expected array got null | The keyword argument was filtered. | *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetRead* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `searchParameters` | `AudioSearchParams` | | | **Returns:** `AudioPages` **Getting Music Search Titles** This code gets the music assets returned by the keyword "happy" and prints out their titles. ```lua local AssetService = game:GetService("AssetService") local audioSearchParams = Instance.new("AudioSearchParams") audioSearchParams.SearchKeyword = "happy" local success, result = pcall(function() return AssetService:SearchAudioAsync(audioSearchParams) end) if success then local currentPage = result:GetCurrentPage() for _, audio in currentPage do print(audio.Title) end else warn("AssetService error: " .. result) end --[[ Returned data format { "AudioType": string, "Artist": string, "Title": string, "Tags": { "string" }, "Id": number, "IsEndorsed": boolean, "Description": string, "Duration": number, "CreateTime": string, "UpdateTime": string, "Creator": { "Id": number, "Name": string, "Type": number, "IsVerifiedCreator": boolean } } --]] ``` ### Method: AssetService:GetAssetIdsForPackage **Signature:** `AssetService:GetAssetIdsForPackage(packageAssetId: int64): Array` Returns an array of asset IDs that are contained in a specified package. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetRead* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `packageAssetId` | `int64` | | | **Returns:** `Array` — Asset IDs that are contained in a specified package. ### Method: AssetService:GetCreatorAssetID **Signature:** `AssetService:GetCreatorAssetID(creationID: int64): int64` > **Deprecated:** This item is deprecated and no longer functions correctly. Do not use it for new work. The GetCreatorAssetID function returns the [Player.UserId](/docs/reference/engine/classes/Player.md) of the account who created the _creationID_ asset. This member is broken and doesn't function correctly. Avoid using it. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetRead* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `creationID` | `int64` | | | **Returns:** `int64` ### Method: AssetService:SearchAudio **Signature:** `AssetService:SearchAudio(searchParameters: AudioSearchParams): AudioPages` Finds audio assets matching a variety of search criteria. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetRead* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `searchParameters` | `AudioSearchParams` | | | **Returns:** `AudioPages` **Getting Music Search Titles** This code gets the music assets returned by the keyword "happy" and prints out their titles. ```lua local AssetService = game:GetService("AssetService") local audioSearchParams = Instance.new("AudioSearchParams") audioSearchParams.SearchKeyword = "happy" local success, result = pcall(function() return AssetService:SearchAudioAsync(audioSearchParams) end) if success then local currentPage = result:GetCurrentPage() for _, audio in currentPage do print(audio.Title) end else warn("AssetService error: " .. result) end --[[ Returned data format { "AudioType": string, "Artist": string, "Title": string, "Tags": { "string" }, "Id": number, "IsEndorsed": boolean, "Description": string, "Duration": number, "CreateTime": string, "UpdateTime": string, "Creator": { "Id": number, "Name": string, "Type": number, "IsVerifiedCreator": boolean } } --]] ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Atmosphere last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances summary: "The Atmosphere object pushes Roblox closer toward realistic environments where sunlight scatters in different ways depending on density and other air particle properties." --- # Class: Atmosphere > The [Atmosphere](/docs/reference/engine/classes/Atmosphere.md) object pushes Roblox closer toward realistic > environments where sunlight scatters in different ways depending on density > and other air particle properties. ## Description Fog properties are hidden when Lighting contains an Atmosphere object. The **Atmosphere** object pushes Roblox closer toward realistic environments where sunlight scatters in different ways depending on density and other air particle properties. It simulates real-world "aerial perspective" and lets you control light transmission from the background sky through distant objects. Furthermore, it controls haze and glare conditions, letting you tune a perfect sunset, foggy afternoon, and more. See also: - [Atmospheric Effects](/docs/en-us/environment/atmosphere.md) for property comparisons and example environments. - [Skybox](/docs/en-us/environment/skybox.md) for how to change the default skybox for games and customize the lighting. - [Post-Processing Effects](/docs/en-us/environment/post-processing-effects.md) for how post-processing effects can quickly improve a game's visuals with a variety of customizable filters. ## Properties ### Property: Atmosphere.Color ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Environment" ] } ``` A [Color3](/docs/reference/engine/datatypes/Color3.md) value which changes the [Atmosphere](/docs/reference/engine/classes/Atmosphere.md) hue for subtle environmental moods. This is best combined with increased [Atmosphere.Haze](/docs/reference/engine/classes/Atmosphere.md) to expand the visible effect. ### Property: Atmosphere.Decay ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Environment" ] } ``` Defines the hue of the [Atmosphere](/docs/reference/engine/classes/Atmosphere.md) away from the sun, gradually falling off from [Atmosphere.Color](/docs/reference/engine/classes/Atmosphere.md) towards this value. Must be used with [Atmosphere.Haze](/docs/reference/engine/classes/Atmosphere.md) and [Atmosphere.Glare](/docs/reference/engine/classes/Atmosphere.md) levels higher than 0 to see any effect. ### Property: Atmosphere.Density ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` Defines the amount of particles in the air. The higher the density, the more particles and the more in-game objects/terrain will be obscured by them. Note that density does not **directly** affect the skybox — it merely affects in-game objects/terrain and visibility of the skybox through them. ### Property: Atmosphere.Glare ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Environment" ] } ``` Specifies the glow/glare of the [Atmosphere](/docs/reference/engine/classes/Atmosphere.md) around the sun. More glare results in an increased effect of sunlight cast onto the sky and world. Must be used with a [Atmosphere.Haze](/docs/reference/engine/classes/Atmosphere.md) level higher than 0 to see any effect. ### Property: Atmosphere.Haze ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Environment" ] } ``` Defines the haziness of the [Atmosphere](/docs/reference/engine/classes/Atmosphere.md) with a visible effect both above the horizon and into the distance. This can be combined with [Atmosphere.Color](/docs/reference/engine/classes/Atmosphere.md) to create environmental moods, like a grey tint for a polluted alien planet. ### Property: Atmosphere.Offset ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` Controls how light transmits between the camera and the sky background. Increase this value to create a horizon silhouette against the sky or reduce it to blend distant objects into the sky for an endless and seamless open world. Offset should be balanced against [Atmosphere.Density](/docs/reference/engine/classes/Atmosphere.md) and carefully tested in your place. A low offset may cause "ghosting" where the skybox can be seen through objects/terrain. This can be corrected by increasing the offset, which more clearly silhouettes distant objects/terrain against the sky, but too much offset may reveal level-of-detail "popping" for far distant terrain and meshes. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AtmosphereSensor last_updated: 2026-06-29T19:34:07Z inherits: - SensorBase - Instance - Object type: class memory_category: Instances summary: "A SensorBase that outputs data about the AirDensity and RelativeWindVelocity at the sensor's position." --- # Class: AtmosphereSensor > A [SensorBase](/docs/reference/engine/classes/SensorBase.md) that outputs data about the > [AirDensity](/docs/reference/engine/classes/AtmosphereSensor.md) and > [RelativeWindVelocity](/docs/reference/engine/classes/AtmosphereSensor.md) at the > sensor's position. ## Properties ### Property: AtmosphereSensor.AirDensity ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Output", "capabilities": [ "Basic" ], "simulationAccess": true } ``` Measures the density of the air where the sensor is positioned. ### Property: AtmosphereSensor.RelativeWindVelocity ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Output", "capabilities": [ "Basic" ], "simulationAccess": true } ``` Reports the wind velocity relative to the sensor. ## Inherited Members ### From [SensorBase](/docs/reference/engine/classes/SensorBase.md) - **Property `UpdateType`** (`SensorUpdateType`): Determines how the sensor will update its output data. - **Method `Sense(): ()`**: *(deprecated)* - **Event `OnSensorOutputChanged`**: ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Attachment last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances summary: "Defines a point and orientation relative to an ancestor PVInstance, Bone, or another Attachment." --- # Class: Attachment > Defines a point and orientation relative to an ancestor [PVInstance](/docs/reference/engine/classes/PVInstance.md), > [Bone](/docs/reference/engine/classes/Bone.md), or another [Attachment](/docs/reference/engine/classes/Attachment.md). ## Description An `Attachment` defines a point and orientation relative to an ancestor [PVInstance](/docs/reference/engine/classes/PVInstance.md), [Bone](/docs/reference/engine/classes/Bone.md), or another `Attachment`. The offset is stored in the [CFrame](/docs/reference/engine/classes/Attachment.md) property. The offset can also be set through other properties, such as [WorldCFrame](/docs/reference/engine/classes/Attachment.md). If no ancestral [PVInstance](/docs/reference/engine/classes/PVInstance.md) or [Attachment](/docs/reference/engine/classes/Attachment.md) exists, then [CFrame](/docs/reference/engine/classes/Attachment.md) and [WorldCFrame](/docs/reference/engine/classes/Attachment.md) are the same. Attachments are used by several kinds of [Constraints](/docs/reference/engine/classes/Constraint.md) and are also valid alternatives to [BasePart](/docs/reference/engine/classes/BasePart.md) as a parent for objects such as: - [ParticleEmitters](/docs/reference/engine/classes/ParticleEmitter.md) which will emit particles from the attachment's specific position/orientation instead of the [BasePart](/docs/reference/engine/classes/BasePart.md) bounds. - Light-emitting objects like [PointLight](/docs/reference/engine/classes/PointLight.md) and [SpotLight](/docs/reference/engine/classes/SpotLight.md) which will shine from the attachment's position/orientation instead of the [BasePart](/docs/reference/engine/classes/BasePart.md) center. - [AudioEmitter](/docs/reference/engine/classes/AudioEmitter.md) which will use the attachment's position as the audio's point of emission. ## Properties ### Property: Attachment.Axis ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Derived Data", "capabilities": [ "Basic" ], "simulationAccess": true } ``` Direction of the **X** axis of the attachment, represented as a unit [Vector3](/docs/reference/engine/datatypes/Vector3.md). ### Property: Attachment.CFrame ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Transform", "capabilities": [ "Basic" ], "simulationAccess": true } ``` The [CFrame](/docs/reference/engine/datatypes/CFrame.md) offset of the attachment. Changes to this property will reflect onto the [Position](/docs/reference/engine/classes/Attachment.md) and [Rotation](/docs/reference/engine/classes/Attachment.md) properties of this object. Similarly, a change to either of those properties will reflect onto this property. ### Property: Attachment.SecondaryAxis ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Derived Data", "capabilities": [ "Basic" ], "simulationAccess": true } ``` Direction of the **Y** axis of the attachment, represented as a unit [Vector3](/docs/reference/engine/datatypes/Vector3.md). ### Property: Attachment.Visible ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` Toggles the in-experience visibility of the attachment. ### Property: Attachment.WorldAxis ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Derived World Data", "capabilities": [ "Basic" ], "simulationAccess": true } ``` Direction of the **X** axis of the attachment relative to the world, represented as a unit [Vector3](/docs/reference/engine/datatypes/Vector3.md) with a length of 1. ### Property: Attachment.WorldCFrame ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Derived World Data", "capabilities": [ "Basic" ], "simulationAccess": true } ``` The exact [CFrame](/docs/reference/engine/datatypes/CFrame.md) of the attachment in world space coordinates, independent of its parent. The value of this property is equivalent to multiplying the [CFrame](/docs/reference/engine/datatypes/CFrame.md) of the attachment's parent by its own [CFrame](/docs/reference/engine/datatypes/CFrame.md). ### Property: Attachment.WorldSecondaryAxis ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Derived World Data", "capabilities": [ "Basic" ], "simulationAccess": true } ``` Direction of the **Y** axis of the attachment relative to the world, represented as a unit [Vector3](/docs/reference/engine/datatypes/Vector3.md) with a length of 1. ### Property: Attachment.Orientation *(hidden)* ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Transform", "capabilities": [ "Basic" ], "simulationAccess": true } ``` Orientation of the attachment relative to the orientation of its parent. Rotations are in **Z**, **X**, **Y** order. ### Property: Attachment.Position *(hidden)* ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Transform", "capabilities": [ "Basic" ], "simulationAccess": true } ``` Positional offset of the attachment, relative to the position and orientation of its parent. ### Property: Attachment.Rotation *(hidden)* ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Transform", "capabilities": [ "Basic" ], "simulationAccess": true } ``` > **Deprecated:** This property is deprecated and should not be used in new work. See [Orientation](/docs/reference/engine/classes/Attachment.md) instead. Rotation of the attachment relative to the rotation of its parent. Rotations are in **Z**, **Y**, **X** order. ### Property: Attachment.WorldOrientation *(hidden)* ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Derived World Data", "capabilities": [ "Basic" ], "simulationAccess": true } ``` Orientation of the attachment relative to the world rather than its own parent. Rotations are in **Z**, **X**, **Y** order. ### Property: Attachment.WorldPosition *(hidden)* ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Derived World Data", "capabilities": [ "Basic" ], "simulationAccess": true } ``` Position of the attachment relative to the world rather than its own parent. ### Property: Attachment.WorldRotation *(hidden)* ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Derived World Data", "capabilities": [ "Basic" ], "simulationAccess": true } ``` > **Deprecated:** This item has been superseded by [WorldOrientation](/docs/reference/engine/classes/Attachment.md) which should be used in new work. Rotation of the attachment relative to the world rather than its own parent. ## Methods ### Method: Attachment:GetConstraints **Signature:** `Attachment:GetConstraints(): List` Returns a list of [Constraints](/docs/reference/engine/classes/Constraint.md) connected to the attachment. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Returns:** `List` ### Method: Attachment:GetAxis **Signature:** `Attachment:GetAxis(): Vector3` > **Deprecated:** This method is deprecated and should not be used in new work. Returns the value of the attachment's [Axis](/docs/reference/engine/classes/Attachment.md). *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Returns:** `Vector3` ### Method: Attachment:GetSecondaryAxis **Signature:** `Attachment:GetSecondaryAxis(): Vector3` > **Deprecated:** This method is deprecated and should not be used in new work. Returns the value of the attachment's [SecondaryAxis](/docs/reference/engine/classes/Attachment.md). *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Returns:** `Vector3` ### Method: Attachment:SetAxis **Signature:** `Attachment:SetAxis(axis: Vector3): ()` > **Deprecated:** This method is deprecated and should not be used in new work. Sets the value of the attachment's [Axis](/docs/reference/engine/classes/Attachment.md). *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `axis` | `Vector3` | | | **Returns:** `()` ### Method: Attachment:SetSecondaryAxis **Signature:** `Attachment:SetSecondaryAxis(axis: Vector3): ()` > **Deprecated:** This method is deprecated and should not be used in new work. Sets the value of the attachment's [SecondaryAxis](/docs/reference/engine/classes/Attachment.md). *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `axis` | `Vector3` | | | **Returns:** `()` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AudioAnalyzer last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Internal summary: "Takes measurements from audio streams that are connected to it via one or more Wires." --- # Class: AudioAnalyzer > Takes measurements from audio streams that are connected to it via one or more > [Wires](/docs/reference/engine/classes/Wire.md). ## Description [AudioAnalyzer](/docs/reference/engine/classes/AudioAnalyzer.md) takes measurements from audio streams that are wired to it through [Wire](/docs/reference/engine/classes/Wire.md). It provides a single **Input** pin but does not produce any output streams. Note that all audio processing is disabled on the server in order to conserve resources; Properties and methods of [AudioAnalyzer](/docs/reference/engine/classes/AudioAnalyzer.md) return empty or zero results when used from server scripts. ## Properties ### Property: AudioAnalyzer.PeakLevel ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Metering", "capabilities": [ "Audio" ] } ``` The loudest volume observed during the last audio buffer. This property changes more often than the framerate and does not fire changed events. On the server, this property is always `0`. ### Property: AudioAnalyzer.RmsLevel ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Metering", "capabilities": [ "Audio" ] } ``` The root-mean-square average volume observed during the last audio buffer. This property is generally more stable than [PeakLevel](/docs/reference/engine/classes/AudioAnalyzer.md) but it may not capture momentary volume spikes. This property changes more often than the framerate and does not fire changed events. On the server, this property is always `0`. ### Property: AudioAnalyzer.SpectrumEnabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Metering", "capabilities": [ "Audio" ] } ``` Enables usage of [GetSpectrum()](/docs/reference/engine/classes/AudioAnalyzer.md). If `false`, [GetSpectrum()](/docs/reference/engine/classes/AudioAnalyzer.md) returns an empty array, but the CPU overhead of the [AudioAnalyzer](/docs/reference/engine/classes/AudioAnalyzer.md) is dramatically reduced. This means that if you are only analyzing the **volume** of an audio stream, you can disable this property to improve performance. ### Property: AudioAnalyzer.WindowSize ```json { "type": "AudioWindowSize", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` Controls the size of arrays returned by [GetSpectrum()](/docs/reference/engine/classes/AudioAnalyzer.md). Larger windows contain more data (so more-accurate frequency responses) but change less often. ## Methods ### Method: AudioAnalyzer:GetConnectedWires **Signature:** `AudioAnalyzer:GetConnectedWires(pin: string): List` Returns an array of [Wires](/docs/reference/engine/classes/Wire.md) that are connected to the specified pin. [AudioAnalyzer](/docs/reference/engine/classes/AudioAnalyzer.md) has one "Input" pin. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `pin` | `string` | | An input or output pin on this instance | **Returns:** `List` — An array of [Wires](/docs/reference/engine/classes/Wire.md) ### Method: AudioAnalyzer:GetInputPins **Signature:** `AudioAnalyzer:GetInputPins(): Array` Gets the list of pins that [Wire](/docs/reference/engine/classes/Wire.md) can use in [Wire.TargetName](/docs/reference/engine/classes/Wire.md) to connect to this instance via its [Wire.TargetInstance](/docs/reference/engine/classes/Wire.md) property. For [AudioAnalyzer](/docs/reference/engine/classes/AudioAnalyzer.md), this is `Input` only. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Array` — An array of strings representing valid pin names. ### Method: AudioAnalyzer:GetOutputPins **Signature:** `AudioAnalyzer:GetOutputPins(): Array` Gets the list of pins that [Wire](/docs/reference/engine/classes/Wire.md) can use in [Wire.SourceName](/docs/reference/engine/classes/Wire.md) to connect to this instance via its [Wire.SourceInstance](/docs/reference/engine/classes/Wire.md) property. For [AudioAnalyzer](/docs/reference/engine/classes/AudioAnalyzer.md), there are none. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Array` — An array of strings representing valid pin names. ### Method: AudioAnalyzer:GetSpectrum **Signature:** `AudioAnalyzer:GetSpectrum(): Array` Returns the frequency spectrum of the last audio buffer, as an array of numbers. The elements of the array are root-mean-square volume levels, evenly spaced from 0 hertz to 24,000 hertz. If any of the analyzer's inputs come from an [AudioDeviceInput](/docs/reference/engine/classes/AudioDeviceInput.md), or this method is used from a server script, it returns an empty array. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Array` — A table of numbers representing the amplitude levels of various frequencies of audio within the last buffer, or an empty table if a spectrum cannot be obtained. ## Events ### Event: AudioAnalyzer.WiringChanged **Signature:** `AudioAnalyzer.WiringChanged(connected: boolean, pin: string, wire: Wire, instance: Instance)` Event that fires after a [Wire](/docs/reference/engine/classes/Wire.md) becomes connected or disconnected, and that [Wire](/docs/reference/engine/classes/Wire.md) is now or was previously connected to a pin on the [AudioAnalyzer](/docs/reference/engine/classes/AudioAnalyzer.md) and to some other wirable instance. *Security: None · Capabilities: Audio* **Parameters:** | Name | Type | Description | |------|------|-------------| | `connected` | `boolean` | Whether the instance got connected or disconnected. | | `pin` | `string` | The pin on the [AudioAnalyzer](/docs/reference/engine/classes/AudioAnalyzer.md) that the [Wire](/docs/reference/engine/classes/Wire.md) targets. | | `wire` | `Wire` | The [Wire](/docs/reference/engine/classes/Wire.md) between the [AudioAnalyzer](/docs/reference/engine/classes/AudioAnalyzer.md) and the other instance. | | `instance` | `Instance` | The other instance that is or was connected through the [Wire](/docs/reference/engine/classes/Wire.md). | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AudioChannelMixer last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Internal summary: "Combines multiple audio streams into a single, multichannel audio stream." --- # Class: AudioChannelMixer > Combines multiple audio streams into a single, multichannel audio stream. ## Description [AudioChannelMixer](/docs/reference/engine/classes/AudioChannelMixer.md) mixes multiple audio streams into a single, multichannel stream. It provides one combined **Input** pin, one **Output** pin, as well as the following secondary input pins, all of which can be connected to/from by [Wires](/docs/reference/engine/classes/Wire.md): **Left**, **Right**, **Center**, **SurroundLeft**, **SurroundRight**, **Sub**, **BackLeft**, **BackRight**, **TopLeft**, **TopRight**, **TopBackLeft**, and **TopBackRight**. ![Diagram showing position of all potential channels.](/assets/engine-api/enums/AudioChannelLayout/Surround_7_1_4.jpg) ## Code Samples **Splitting & Mixing Channels** ```lua local Workspace = game:GetService("Workspace") local function wireUp(source: Instance, target: Instance, sourceName: string?, targetName: string?) local wire = Instance.new("Wire", source) wire.SourceInstance = source wire.TargetInstance = target if sourceName then wire.SourceName = sourceName end if targetName then wire.TargetName = targetName end return wire end local listener = Instance.new("AudioListener") listener.Parent = Workspace.CurrentCamera local output = Instance.new("AudioDeviceOutput") output.Parent = Workspace local splitter = Instance.new("AudioChannelSplitter") splitter.Parent = Workspace local mixer = Instance.new("AudioChannelMixer") mixer.Parent = Workspace -- Send what the listener hears to a splitter and send a mix to the final output wireUp(listener, splitter) wireUp(mixer, output) -- Set up both the splitter and mixer to use a quadrophonic layout splitter.Layout = Enum.AudioChannelLayout.Quad mixer.Layout = Enum.AudioChannelLayout.Quad -- Give each of the four channels its own pitch shifter local frontLeft = Instance.new("AudioPitchShifter") frontLeft.Name = "Front Left" frontLeft.Pitch = 1.25 frontLeft.Parent = Workspace local backLeft = Instance.new("AudioPitchShifter") backLeft.Name = "Back Left" backLeft.Pitch = 0.5 backLeft.Parent = Workspace local frontRight = Instance.new("AudioPitchShifter") frontRight.Name = "Front Right" frontRight.Pitch = 1.5 frontRight.Parent = Workspace local backRight = Instance.new("AudioPitchShifter") backRight.Name = "Back Right" backRight.Pitch = 0.75 backRight.Parent = Workspace wireUp(splitter, frontLeft, "Left") wireUp(splitter, backLeft, "BackLeft") wireUp(splitter, frontRight, "Right") wireUp(splitter, backRight, "BackRight") wireUp(frontLeft, mixer, nil, "Left") wireUp(backLeft, mixer, nil, "BackLeft") wireUp(frontRight, mixer, nil, "Right") wireUp(backRight, mixer, nil, "BackRight") -- Configure a part to emit audio local part = Instance.new("Part") part.Shape = Enum.PartType.Ball part.Size = Vector3.new(4, 4, 4) part.Material = Enum.Material.SmoothPlastic part.CastShadow = false part.Position = Vector3.new(0, 4, -12) part.Anchored = true part.Parent = Workspace local analyzer = Instance.new("AudioAnalyzer") analyzer.Parent = part local emitter = Instance.new("AudioEmitter") emitter.Parent = part local assetPlayer = Instance.new("AudioPlayer") assetPlayer.Looping = true assetPlayer.Asset = "rbxassetid://97799489309320" assetPlayer.Parent = emitter wireUp(assetPlayer, emitter) wireUp(assetPlayer, analyzer) -- Start playing the audio assetPlayer:Play() -- Adjust the part's color as the audio plays while true do local peak = math.sqrt(analyzer.PeakLevel) part.Color = Color3.new(peak, peak, peak) task.wait() end ``` ## Properties ### Property: AudioChannelMixer.Layout ```json { "type": "AudioChannelLayout", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` Controls the output channel layout to be mixed to. The **Input** pin of [AudioChannelMixer](/docs/reference/engine/classes/AudioChannelMixer.md) is always forwarded "as-is" to **Output**, but depending on the value of `Layout`: - For [Mono](/docs/reference/engine/enums/AudioChannelLayout.md), the **Center** pin consumes audio streams. - For [Stereo](/docs/reference/engine/enums/AudioChannelLayout.md), the **Left** and **Right** pins consume audio streams. - For [Quad](/docs/reference/engine/enums/AudioChannelLayout.md), the **Left**, **Right**, **BackLeft**, and **BackRight** pins consume audio streams. - [Surround_5](/docs/reference/engine/enums/AudioChannelLayout.md) is the same as `Quad`, plus **Center** consumes an audio stream. - [Surround_5_1](/docs/reference/engine/enums/AudioChannelLayout.md) is the same as `Surround_5`, plus **Sub** consumes an audio stream. - [Surround_7_1](/docs/reference/engine/enums/AudioChannelLayout.md) is the same as `Surround_5_1`, plus **SurroundLeft** and **SurroundRight** consume audio streams. - For [Surround_7_1_4](/docs/reference/engine/enums/AudioChannelLayout.md), all secondary input pins consume audio streams. ## Methods ### Method: AudioChannelMixer:GetConnectedWires **Signature:** `AudioChannelMixer:GetConnectedWires(pin: string): List` Returns an array of [Wires](/docs/reference/engine/classes/Wire.md) that are connected to the specified pin. For [AudioChannelMixer](/docs/reference/engine/classes/AudioChannelMixer.md), this can be one "Output" pin or a number of input pins - see [AudioChannelMixer.GetInputPins](/docs/reference/engine/classes/AudioChannelMixer.md) for a full list. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `pin` | `string` | | An input or output pin on this instance | **Returns:** `List` — An array of [Wires](/docs/reference/engine/classes/Wire.md) ### Method: AudioChannelMixer:GetInputPins **Signature:** `AudioChannelMixer:GetInputPins(): Array` Gets the list of pins that [Wire](/docs/reference/engine/classes/Wire.md) can use in [Wire.TargetName](/docs/reference/engine/classes/Wire.md) to connect to this instance via its [Wire.TargetInstance](/docs/reference/engine/classes/Wire.md) property. For [AudioChannelMixer](/docs/reference/engine/classes/AudioChannelMixer.md), these are: - `"Input"` - `"Left"` - `"Right"` - `"Center"` - `"SurroundLeft"` - `"SurroundRight"` - `"BackLeft"` - `"BackRight"` - `"Sub"` - `"TopLeft"` - `"TopRight"` - `"TopBackLeft"` - `"TopBackRight"` *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Array` — An array of strings representing valid pin names. ### Method: AudioChannelMixer:GetOutputPins **Signature:** `AudioChannelMixer:GetOutputPins(): Array` Gets the list of pins that [Wire](/docs/reference/engine/classes/Wire.md) can use in [Wire.SourceName](/docs/reference/engine/classes/Wire.md) to connect to this instance via its [Wire.SourceInstance](/docs/reference/engine/classes/Wire.md) property. For [AudioChannelMixer](/docs/reference/engine/classes/AudioChannelMixer.md), this is `Output` only. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Array` — An array of strings representing valid pin names. ## Events ### Event: AudioChannelMixer.WiringChanged **Signature:** `AudioChannelMixer.WiringChanged(connected: boolean, pin: string, wire: Wire, instance: Instance)` Event that fires after a [Wire](/docs/reference/engine/classes/Wire.md) becomes connected or disconnected, and that [Wire](/docs/reference/engine/classes/Wire.md) is now or was previously connected to a pin on the [AudioChannelMixer](/docs/reference/engine/classes/AudioChannelMixer.md) and to some other wirable instance. *Security: None · Capabilities: Audio* **Parameters:** | Name | Type | Description | |------|------|-------------| | `connected` | `boolean` | Whether the instance got connected or disconnected. | | `pin` | `string` | The pin on the [AudioChannelMixer](/docs/reference/engine/classes/AudioChannelMixer.md) that the [Wire](/docs/reference/engine/classes/Wire.md) targets. | | `wire` | `Wire` | The [Wire](/docs/reference/engine/classes/Wire.md) between the [AudioChannelMixer](/docs/reference/engine/classes/AudioChannelMixer.md) and the other instance. | | `instance` | `Instance` | The other instance that is or was connected through the [Wire](/docs/reference/engine/classes/Wire.md). | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AudioChannelSplitter last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Internal summary: "Splits an audio stream into component channels so that each can be processed independently." --- # Class: AudioChannelSplitter > Splits an audio stream into component channels so that each can be processed > independently. ## Description [AudioChannelSplitter](/docs/reference/engine/classes/AudioChannelSplitter.md) splits an audio stream into component channels so that each can be processed independently. It provides one **Input** pin, one combined **Output** pin, as well as the following secondary output pins, all of which can be connected to/from by [Wires](/docs/reference/engine/classes/Wire.md): **Left**, **Right**, **Center**, **SurroundLeft**, **SurroundRight**, **Sub**, **BackLeft**, **BackRight**, **TopLeft**, **TopRight**, **TopBackLeft**, and **TopBackRight**. ![Diagram showing position of all potential channels.](/assets/engine-api/enums/AudioChannelLayout/Surround_7_1_4.jpg) ## Code Samples **Splitting & Mixing Channels** ```lua local Workspace = game:GetService("Workspace") local function wireUp(source: Instance, target: Instance, sourceName: string?, targetName: string?) local wire = Instance.new("Wire", source) wire.SourceInstance = source wire.TargetInstance = target if sourceName then wire.SourceName = sourceName end if targetName then wire.TargetName = targetName end return wire end local listener = Instance.new("AudioListener") listener.Parent = Workspace.CurrentCamera local output = Instance.new("AudioDeviceOutput") output.Parent = Workspace local splitter = Instance.new("AudioChannelSplitter") splitter.Parent = Workspace local mixer = Instance.new("AudioChannelMixer") mixer.Parent = Workspace -- Send what the listener hears to a splitter and send a mix to the final output wireUp(listener, splitter) wireUp(mixer, output) -- Set up both the splitter and mixer to use a quadrophonic layout splitter.Layout = Enum.AudioChannelLayout.Quad mixer.Layout = Enum.AudioChannelLayout.Quad -- Give each of the four channels its own pitch shifter local frontLeft = Instance.new("AudioPitchShifter") frontLeft.Name = "Front Left" frontLeft.Pitch = 1.25 frontLeft.Parent = Workspace local backLeft = Instance.new("AudioPitchShifter") backLeft.Name = "Back Left" backLeft.Pitch = 0.5 backLeft.Parent = Workspace local frontRight = Instance.new("AudioPitchShifter") frontRight.Name = "Front Right" frontRight.Pitch = 1.5 frontRight.Parent = Workspace local backRight = Instance.new("AudioPitchShifter") backRight.Name = "Back Right" backRight.Pitch = 0.75 backRight.Parent = Workspace wireUp(splitter, frontLeft, "Left") wireUp(splitter, backLeft, "BackLeft") wireUp(splitter, frontRight, "Right") wireUp(splitter, backRight, "BackRight") wireUp(frontLeft, mixer, nil, "Left") wireUp(backLeft, mixer, nil, "BackLeft") wireUp(frontRight, mixer, nil, "Right") wireUp(backRight, mixer, nil, "BackRight") -- Configure a part to emit audio local part = Instance.new("Part") part.Shape = Enum.PartType.Ball part.Size = Vector3.new(4, 4, 4) part.Material = Enum.Material.SmoothPlastic part.CastShadow = false part.Position = Vector3.new(0, 4, -12) part.Anchored = true part.Parent = Workspace local analyzer = Instance.new("AudioAnalyzer") analyzer.Parent = part local emitter = Instance.new("AudioEmitter") emitter.Parent = part local assetPlayer = Instance.new("AudioPlayer") assetPlayer.Looping = true assetPlayer.Asset = "rbxassetid://97799489309320" assetPlayer.Parent = emitter wireUp(assetPlayer, emitter) wireUp(assetPlayer, analyzer) -- Start playing the audio assetPlayer:Play() -- Adjust the part's color as the audio plays while true do local peak = math.sqrt(analyzer.PeakLevel) part.Color = Color3.new(peak, peak, peak) task.wait() end ``` ## Properties ### Property: AudioChannelSplitter.Layout ```json { "type": "AudioChannelLayout", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` Controls the input channel layout to be split from. When changed, all audio streams prior to this channel splitter's input may need to be up-mixed (widened to at least as many channels as the input requires). The **Output** pin produces a copy of the stream wired to **Input** but, depending on the value of [AudioChannelSplitter.Layout](/docs/reference/engine/classes/AudioChannelSplitter.md): - For [Mono](/docs/reference/engine/enums/AudioChannelLayout.md), the **Center** pin produces an audio stream. - For [Stereo](/docs/reference/engine/enums/AudioChannelLayout.md), the **Left** and **Right** pins produce audio streams. - For [Quad](/docs/reference/engine/enums/AudioChannelLayout.md), the **Left**, **Right**, **BackLeft**, and **BackRight** pins produce audio streams. - [Surround_5](/docs/reference/engine/enums/AudioChannelLayout.md) is the same as `Quad`, plus **Center** produces an audio stream. - [Surround_5_1](/docs/reference/engine/enums/AudioChannelLayout.md) is the same as `Surround_5`, plus **Sub** produces an audio stream. - [Surround_7_1](/docs/reference/engine/enums/AudioChannelLayout.md) is the same as `Surround_5_1`, plus **SurroundLeft** and **SurroundRight** produce audio streams. - For [Surround_7_1_4](/docs/reference/engine/enums/AudioChannelLayout.md), all secondary output pins produce audio streams. ## Methods ### Method: AudioChannelSplitter:GetConnectedWires **Signature:** `AudioChannelSplitter:GetConnectedWires(pin: string): List` Returns an array of [Wires](/docs/reference/engine/classes/Wire.md) that are connected to the specified pin. For [AudioChannelSplitter](/docs/reference/engine/classes/AudioChannelSplitter.md), this can be one "Input" pin or a number of output pins - see [AudioChannelSplitter.GetInputPins](/docs/reference/engine/classes/AudioChannelSplitter.md) for a full list. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `pin` | `string` | | An input or output pin on this instance | **Returns:** `List` — An array of [Wires](/docs/reference/engine/classes/Wire.md) ### Method: AudioChannelSplitter:GetInputPins **Signature:** `AudioChannelSplitter:GetInputPins(): Array` Gets the list of pins that [Wire](/docs/reference/engine/classes/Wire.md) can use in [Wire.TargetName](/docs/reference/engine/classes/Wire.md) to connect to this instance via its [Wire.TargetInstance](/docs/reference/engine/classes/Wire.md) property. For [AudioChannelSplitter](/docs/reference/engine/classes/AudioChannelSplitter.md), this is `Input` only. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Array` — An array of strings representing valid pin names. ### Method: AudioChannelSplitter:GetOutputPins **Signature:** `AudioChannelSplitter:GetOutputPins(): Array` Gets the list of pins that [Wire](/docs/reference/engine/classes/Wire.md) can use in [Wire.SourceName](/docs/reference/engine/classes/Wire.md) to connect to this instance via its [Wire.SourceInstance](/docs/reference/engine/classes/Wire.md) property. For [AudioChannelSplitter](/docs/reference/engine/classes/AudioChannelSplitter.md), these are: - `"Output"` - `"Left"` - `"Right"` - `"Center"` - `"SurroundLeft"` - `"SurroundRight"` - `"BackLeft"` - `"BackRight"` - `"Sub"` - `"TopLeft"` - `"TopRight"` - `"TopBackLeft"` - `"TopBackRight"` *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Array` — An array of strings representing valid pin names. ## Events ### Event: AudioChannelSplitter.WiringChanged **Signature:** `AudioChannelSplitter.WiringChanged(connected: boolean, pin: string, wire: Wire, instance: Instance)` Event that fires after a [Wire](/docs/reference/engine/classes/Wire.md) becomes connected or disconnected, and that [Wire](/docs/reference/engine/classes/Wire.md) is now or was previously connected to a pin on the [AudioChannelSplitter](/docs/reference/engine/classes/AudioChannelSplitter.md) and to some other wirable instance. *Security: None · Capabilities: Audio* **Parameters:** | Name | Type | Description | |------|------|-------------| | `connected` | `boolean` | Whether the instance got connected or disconnected. | | `pin` | `string` | The pin on the [AudioChannelSplitter](/docs/reference/engine/classes/AudioChannelSplitter.md) that the [Wire](/docs/reference/engine/classes/Wire.md) targets. | | `wire` | `Wire` | The [Wire](/docs/reference/engine/classes/Wire.md) between the [AudioChannelSplitter](/docs/reference/engine/classes/AudioChannelSplitter.md) and the other instance. | | `instance` | `Instance` | The other instance that is or was connected through the [Wire](/docs/reference/engine/classes/Wire.md). | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AudioChorus last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Internal summary: "Makes an audio stream sound more voluminous. If applied to a single voice, it may sound like multiple voices." --- # Class: AudioChorus > Makes an audio stream sound more voluminous. If applied to a single voice, it > may sound like multiple voices. ## Description [AudioChorus](/docs/reference/engine/classes/AudioChorus.md) makes an audio stream sound more voluminous. It provides one **Input** pin and one **Output** pin which can be connected to/from by [Wires](/docs/reference/engine/classes/Wire.md). [AudioChorus](/docs/reference/engine/classes/AudioChorus.md) is implemented by duplicating the input stream and modulating the pitch of several delayed copies so that the overall result sounds like a cloud of streams. If applied to a single voice, it may sound like multiple voices. ## Properties ### Property: AudioChorus.Bypass ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` If `true`, audio streams are passed-through unaffected by this effect. ### Property: AudioChorus.Depth ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` Controls how strong the chorus effect is by changing the maximum delay time of the copy streams. Ranges from 0 to 1 which corresponds to 0 to 100 milliseconds of maximum delay. ### Property: AudioChorus.Mix ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` Controls the balance of plain input stream to modified output stream. Ranges from 0 to 1. ### Property: AudioChorus.Rate ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` Frequency controlling the rate of pitch modulations, in hertz. Ranges from 0 to 20. ## Methods ### Method: AudioChorus:GetConnectedWires **Signature:** `AudioChorus:GetConnectedWires(pin: string): List` Returns an array of [Wires](/docs/reference/engine/classes/Wire.md) that are connected to the specified pin. [AudioChorus](/docs/reference/engine/classes/AudioChorus.md) has one "Input" pin and one "Output" pin. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `pin` | `string` | | An input or output pin on this instance | **Returns:** `List` — An array of [Wires](/docs/reference/engine/classes/Wire.md) ### Method: AudioChorus:GetInputPins **Signature:** `AudioChorus:GetInputPins(): Array` Gets the list of pins that [Wire](/docs/reference/engine/classes/Wire.md) can use in [Wire.TargetName](/docs/reference/engine/classes/Wire.md) to connect to this instance via its [Wire.TargetInstance](/docs/reference/engine/classes/Wire.md) property. For [AudioChorus](/docs/reference/engine/classes/AudioChorus.md), this is `Input` only. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Array` — An array of strings representing valid pin names. ### Method: AudioChorus:GetOutputPins **Signature:** `AudioChorus:GetOutputPins(): Array` Gets the list of pins that [Wire](/docs/reference/engine/classes/Wire.md) can use in [Wire.SourceName](/docs/reference/engine/classes/Wire.md) to connect to this instance via its [Wire.SourceInstance](/docs/reference/engine/classes/Wire.md) property. For [AudioChorus](/docs/reference/engine/classes/AudioChorus.md), this is `Output` only. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Array` — An array of strings representing valid pin names. ## Events ### Event: AudioChorus.WiringChanged **Signature:** `AudioChorus.WiringChanged(connected: boolean, pin: string, wire: Wire, instance: Instance)` Event that fires after a [Wire](/docs/reference/engine/classes/Wire.md) becomes connected or disconnected, and that [Wire](/docs/reference/engine/classes/Wire.md) is now or was previously connected to a pin on the [AudioChorus](/docs/reference/engine/classes/AudioChorus.md) and to some other wirable instance. *Security: None · Capabilities: Audio* **Parameters:** | Name | Type | Description | |------|------|-------------| | `connected` | `boolean` | Whether the instance got connected or disconnected. | | `pin` | `string` | The pin on the [AudioChorus](/docs/reference/engine/classes/AudioChorus.md) that the [Wire](/docs/reference/engine/classes/Wire.md) targets. | | `wire` | `Wire` | The [Wire](/docs/reference/engine/classes/Wire.md) between the [AudioChorus](/docs/reference/engine/classes/AudioChorus.md) and the other instance. | | `instance` | `Instance` | The other instance that is or was connected through the [Wire](/docs/reference/engine/classes/Wire.md). | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AudioCompressor last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Internal summary: "Adjusts the dynamic range of input streams." --- # Class: AudioCompressor > Adjusts the dynamic range of input streams. ## Description [AudioCompressor](/docs/reference/engine/classes/AudioCompressor.md) adjusts the dynamic range of audio streams. Any momentary bursts of loudness will be clamped down according to the compressor's properties. [AudioCompressor](/docs/reference/engine/classes/AudioCompressor.md) provides **Input** and **Sidechain** pins that can be targeted by [Wire.TargetName](/docs/reference/engine/classes/Wire.md), and an **Output** pin that can be used by [Wire.SourceName](/docs/reference/engine/classes/Wire.md). ## Code Samples **Sidechain Compression** By using Sidechain compression, the background ambience can be ducked around an explosion ```lua local deviceOutput: AudioDeviceOutput = Instance.new("AudioDeviceOutput") deviceOutput.Parent = workspace local explosionPlayer: AudioPlayer = Instance.new("AudioPlayer") explosionPlayer.Parent = workspace explosionPlayer.AssetId = "rbxassetid://1835333184" local ambiencePlayer = Instance.new("AudioPlayer") ambiencePlayer.AssetId = "rbxassetid://9112854440" local compressor = Instance.new("AudioCompressor") local wireToCompressor = Instance.new("Wire") local wireToSidechain = Instance.new("Wire") local wireToOutput = Instance.new("Wire") ambiencePlayer.Parent = workspace compressor.Parent = workspace wireToCompressor.Parent = workspace wireToSidechain.Parent = workspace wireToOutput.Parent = workspace wireToCompressor.SourceInstance = ambiencePlayer wireToCompressor.TargetInstance = compressor wireToSidechain.SourceInstance = explosionPlayer wireToSidechain.TargetInstance = compressor wireToSidechain.TargetName = "Sidechain" wireToOutput.SourceInstance = compressor wireToOutput.TargetInstance = deviceOutput ambiencePlayer:Play() ``` ## Properties ### Property: AudioCompressor.Attack ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` Time, in seconds, denoting how quickly the compressor will clamp down on volume after it surpasses [Threshold](/docs/reference/engine/classes/AudioCompressor.md). Ranges from 0.0001 to 0.5. ### Property: AudioCompressor.Bypass ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` If `true`, audio streams are passed-through unaffected by this effect. ### Property: AudioCompressor.MakeupGain ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` Gain value to be applied after compression, in decibels. After limiting the dynamic range, the resulting stream may be very quiet and this property can be used to compensate. Ranges from -30 to 30. ### Property: AudioCompressor.Ratio ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` Ratio of input volume to output volume, to be applied when surpassing [Threshold](/docs/reference/engine/classes/AudioCompressor.md). For example, a value of 2 will cut the amount by which the input stream exceeds the threshold in half whenever the input stream does so. Ranges from 1 to 50. ### Property: AudioCompressor.Release ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` Time, in seconds, denoting how quickly the compressor will unclamp after its stream volume dips back below [Threshold](/docs/reference/engine/classes/AudioCompressor.md). Ranges from 0.01 to 5. ### Property: AudioCompressor.Threshold ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` Gain value at which the compressor will start to modify the input stream, in decibels, with a range of -60 to 0. When the input stream's volume surpasses [Threshold](/docs/reference/engine/classes/AudioCompressor.md), the compressor will take [Attack](/docs/reference/engine/classes/AudioCompressor.md) seconds to kick in. When the input stream's volume recedes below [Threshold](/docs/reference/engine/classes/AudioCompressor.md), the compressor will take [Release](/docs/reference/engine/classes/AudioCompressor.md) seconds to stop acting. If any [Wires](/docs/reference/engine/classes/Wire.md) are connected to the **Sidechain** pin of the compressor, this threshold analyzes those streams instead of the **Input** streams; this can be used to duck the volume of one stream in response to another. ## Methods ### Method: AudioCompressor:GetConnectedWires **Signature:** `AudioCompressor:GetConnectedWires(pin: string): List` Returns an array of [Wires](/docs/reference/engine/classes/Wire.md) that are connected to the specified pin. [AudioCompressor](/docs/reference/engine/classes/AudioCompressor.md) has one "Input" pin, one "Sidechain" pin, and one "Output" pin. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `pin` | `string` | | An input or output pin on this instance | **Returns:** `List` — An array of [Wires](/docs/reference/engine/classes/Wire.md) ### Method: AudioCompressor:GetInputPins **Signature:** `AudioCompressor:GetInputPins(): Array` Gets the list of pins that [Wire](/docs/reference/engine/classes/Wire.md) can use in [Wire.TargetName](/docs/reference/engine/classes/Wire.md) to connect to this instance via its [Wire.TargetInstance](/docs/reference/engine/classes/Wire.md) property. For [AudioCompressor](/docs/reference/engine/classes/AudioCompressor.md), these are: - `Input` - `Sidechain` *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Array` — An array of strings representing valid pin names. ### Method: AudioCompressor:GetOutputPins **Signature:** `AudioCompressor:GetOutputPins(): Array` Gets the list of pins that [Wire](/docs/reference/engine/classes/Wire.md) can use in [Wire.SourceName](/docs/reference/engine/classes/Wire.md) to connect to this instance via its [Wire.SourceInstance](/docs/reference/engine/classes/Wire.md) property. For [AudioCompressor](/docs/reference/engine/classes/AudioCompressor.md), this is `Output` only. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Array` — An array of strings representing valid pin names. ## Events ### Event: AudioCompressor.WiringChanged **Signature:** `AudioCompressor.WiringChanged(connected: boolean, pin: string, wire: Wire, instance: Instance)` Event that fires after a [Wire](/docs/reference/engine/classes/Wire.md) becomes connected or disconnected, and that [Wire](/docs/reference/engine/classes/Wire.md) is now or was previously connected to a pin on the [AudioCompressor](/docs/reference/engine/classes/AudioCompressor.md) and to some other wirable instance. *Security: None · Capabilities: Audio* **Parameters:** | Name | Type | Description | |------|------|-------------| | `connected` | `boolean` | Whether the instance got connected or disconnected. | | `pin` | `string` | The pin on the [AudioCompressor](/docs/reference/engine/classes/AudioCompressor.md) that the [Wire](/docs/reference/engine/classes/Wire.md) targets. | | `wire` | `Wire` | The [Wire](/docs/reference/engine/classes/Wire.md) between the [AudioCompressor](/docs/reference/engine/classes/AudioCompressor.md) and the other instance. | | `instance` | `Instance` | The other instance that is or was connected through the [Wire](/docs/reference/engine/classes/Wire.md). | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AudioDeviceInput last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Internal summary: "Produces audio streams from physical devices, such as microphones." --- # Class: AudioDeviceInput > Produces audio streams from physical devices, such as microphones. ## Description [AudioDeviceInput](/docs/reference/engine/classes/AudioDeviceInput.md) produces audio streams from physical devices, such as microphones. It provides a single **Output** pin which can be connected to other pins via [Wires](/docs/reference/engine/classes/Wire.md). [AudioDeviceInput](/docs/reference/engine/classes/AudioDeviceInput.md) has properties for selecting which [Player](/docs/reference/engine/classes/Player.md) is producing the stream, and controlling whether or not they are muted. ## Properties ### Property: AudioDeviceInput.AccessType ```json { "type": "AccessModifierType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Permissions", "capabilities": [ "Audio", "Input" ] } ``` Determines whether the list of user IDs provided to [SetUserIdAccessList](/docs/reference/engine/classes/AudioDeviceInput.md) is treated as an allow-list or deny-list. If [AccessType](/docs/reference/engine/classes/AudioDeviceInput.md) is [AccessModifierType.Allow](/docs/reference/engine/enums/AccessModifierType.md), then **only** the supplied user IDs are **permitted** to hear this [AudioDeviceInput](/docs/reference/engine/classes/AudioDeviceInput.md). If [AccessType](/docs/reference/engine/classes/AudioDeviceInput.md) is [AccessModifierType.Deny](/docs/reference/engine/enums/AccessModifierType.md), then **only** the supplied user IDs are **blocked** from hearing this [AudioDeviceInput](/docs/reference/engine/classes/AudioDeviceInput.md). Since player voices are networked, this property should only be assigned from the server in order to replicate properly. ### Property: AudioDeviceInput.Active ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "None", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Recording", "capabilities": [ "Audio", "Input" ] } ``` Controls whether the physical device is actively recording. This property is only set by Roblox core scripts, but it may be read by user scripts. Generally, an [AudioDeviceInput](/docs/reference/engine/classes/AudioDeviceInput.md) may only be producing sound if [Active](/docs/reference/engine/classes/AudioDeviceInput.md) is true **and** [Muted](/docs/reference/engine/classes/AudioDeviceInput.md) is false. ### Property: AudioDeviceInput.IsReady ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Recording", "capabilities": [ "Audio", "Input" ] } ``` Denotes whether this [AudioDeviceInput](/docs/reference/engine/classes/AudioDeviceInput.md) is ready to produce sound, meaning all network connections have been established. ### Property: AudioDeviceInput.Muted ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Recording", "capabilities": [ "Audio", "Input" ] } ``` Controls whether this [AudioDeviceInput](/docs/reference/engine/classes/AudioDeviceInput.md) is muted. Unlike [Active](/docs/reference/engine/classes/AudioDeviceInput.md), this property is publicly scriptable. Generally, an [AudioDeviceInput](/docs/reference/engine/classes/AudioDeviceInput.md) may only be heard if [Active](/docs/reference/engine/classes/AudioDeviceInput.md) is true **and** [Muted](/docs/reference/engine/classes/AudioDeviceInput.md) is false. **Push-to-talk** ```lua local players = game:GetService("Players") local userInput = game:GetService("UserInputService") local audioIn: AudioDeviceInput = players.LocalPlayer:WaitForChild("AudioDeviceInput") audioIn.Muted = true local pushToTalkKey = Enum.KeyCode.V userInput.InputBegan:Connect(function(input: InputObject) if input.KeyCode == pushToTalkKey then audioIn.Muted = false end end) userInput.InputEnded:Connect(function(input: InputObject) if input.KeyCode == pushToTalkKey then audioIn.Muted = true end end) ``` ### Property: AudioDeviceInput.Player ```json { "type": "Player", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Recording", "capabilities": [ "Audio", "Input", "Players" ] } ``` Determines whose device is producing sound. In order to replicate properly, this should only be assigned from the server. Assigning this property locally generally does not work, unless [Player](/docs/reference/engine/classes/AudioDeviceInput.md) is [Players.LocalPlayer](/docs/reference/engine/classes/Players.md). ### Property: AudioDeviceInput.Volume ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio", "Input" ] } ``` Volume level which is multiplied onto the output audio stream. Ranges from 0 to 3. ## Methods ### Method: AudioDeviceInput:GetConnectedWires **Signature:** `AudioDeviceInput:GetConnectedWires(pin: string): List` Returns an array of [Wires](/docs/reference/engine/classes/Wire.md) that are connected to the specified pin. [AudioDeviceInput](/docs/reference/engine/classes/AudioDeviceInput.md) has one "Output" pin. *Security: None · Thread Safety: Unsafe · Capabilities: Audio, Input* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `pin` | `string` | | An input or output pin on this instance | **Returns:** `List` — An array of [Wires](/docs/reference/engine/classes/Wire.md) ### Method: AudioDeviceInput:GetInputPins **Signature:** `AudioDeviceInput:GetInputPins(): Array` Gets the list of pins that [Wire](/docs/reference/engine/classes/Wire.md) can use in [Wire.TargetName](/docs/reference/engine/classes/Wire.md) to connect to this instance via its [Wire.TargetInstance](/docs/reference/engine/classes/Wire.md) property. For [AudioDeviceInput](/docs/reference/engine/classes/AudioDeviceInput.md), there are none. *Security: None · Thread Safety: Unsafe · Capabilities: Audio, Input* **Returns:** `Array` — An array of strings representing valid pin names. ### Method: AudioDeviceInput:GetOutputPins **Signature:** `AudioDeviceInput:GetOutputPins(): Array` Gets the list of pins that [Wire](/docs/reference/engine/classes/Wire.md) can use in [Wire.SourceName](/docs/reference/engine/classes/Wire.md) to connect to this instance via its [Wire.SourceInstance](/docs/reference/engine/classes/Wire.md) property. For [AudioDeviceInput](/docs/reference/engine/classes/AudioDeviceInput.md), this is `Output` only. *Security: None · Thread Safety: Unsafe · Capabilities: Audio, Input* **Returns:** `Array` — An array of strings representing valid pin names. ### Method: AudioDeviceInput:GetUserIdAccessList **Signature:** `AudioDeviceInput:GetUserIdAccessList(): Array` Returns a list of user IDs that are either permitted to hear or blocked from hearing this [AudioDeviceInput](/docs/reference/engine/classes/AudioDeviceInput.md), depending on the [AccessType](/docs/reference/engine/classes/AudioDeviceInput.md). *Security: None · Thread Safety: Unsafe · Capabilities: Audio, Input* **Returns:** `Array` ### Method: AudioDeviceInput:SetUserIdAccessList **Signature:** `AudioDeviceInput:SetUserIdAccessList(userIds: Array): ()` Sets a list of user IDs that are either permitted to hear or blocked from hearing this [AudioDeviceInput](/docs/reference/engine/classes/AudioDeviceInput.md), depending on the [AccessType](/docs/reference/engine/classes/AudioDeviceInput.md). Note that this method replicates from server to client; in general, it should only be called from the server in order to replicate properly. *Security: None · Thread Safety: Unsafe · Capabilities: Audio, Input* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `userIds` | `Array` | | | **Returns:** `()` ## Events ### Event: AudioDeviceInput.WiringChanged **Signature:** `AudioDeviceInput.WiringChanged(connected: boolean, pin: string, wire: Wire, instance: Instance)` Event that fires after a [Wire](/docs/reference/engine/classes/Wire.md) becomes connected or disconnected, and that [Wire](/docs/reference/engine/classes/Wire.md) is now or was previously connected to a pin on the [AudioDeviceInput](/docs/reference/engine/classes/AudioDeviceInput.md) and to some other wirable instance. *Security: None · Capabilities: Audio, Input* **Parameters:** | Name | Type | Description | |------|------|-------------| | `connected` | `boolean` | Whether the instance got connected or disconnected. | | `pin` | `string` | The pin on the [AudioDeviceInput](/docs/reference/engine/classes/AudioDeviceInput.md) that the [Wire](/docs/reference/engine/classes/Wire.md) targets. | | `wire` | `Wire` | The [Wire](/docs/reference/engine/classes/Wire.md) between the [AudioDeviceInput](/docs/reference/engine/classes/AudioDeviceInput.md) and the other instance. | | `instance` | `Instance` | The other instance that is or was connected through the [Wire](/docs/reference/engine/classes/Wire.md). | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AudioDeviceOutput last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Internal summary: "Accepts audio streams to be rendered out to physical hardware devices such as speakers or headphones." --- # Class: AudioDeviceOutput > Accepts audio streams to be rendered out to physical hardware devices such as > speakers or headphones. ## Description [AudioDeviceOutput](/docs/reference/engine/classes/AudioDeviceOutput.md) accepts audio streams to be rendered out to physical hardware devices such as speakers or headphones. It provides a single **Input** pin that can be targeted by [Wires](/docs/reference/engine/classes/Wire.md). Any audio streams wired to an [AudioDeviceOutput](/docs/reference/engine/classes/AudioDeviceOutput.md) are heard. ## Code Samples **Outputting Audio to Device** ```lua local audioPlayer: AudioPlayer = Instance.new("AudioPlayer") audioPlayer.Parent = workspace audioPlayer.AssetId = "rbxassetid://9112854440" local deviceOutput = Instance.new("AudioDeviceOutput") deviceOutput.Parent = workspace local wire = Instance.new("Wire") wire.Parent = workspace wire.SourceInstance = audioPlayer wire.TargetInstance = deviceOutput audioPlayer:Play() ``` ## Properties ### Property: AudioDeviceOutput.Player ```json { "type": "Player", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` An optional [Player](/docs/reference/engine/classes/Player.md) who is intended to hear the connected audio streams. If left unspecified, the streams wired to this [AudioDeviceOutput](/docs/reference/engine/classes/AudioDeviceOutput.md) are heard by everyone. ## Methods ### Method: AudioDeviceOutput:GetConnectedWires **Signature:** `AudioDeviceOutput:GetConnectedWires(pin: string): List` Returns an array of [Wires](/docs/reference/engine/classes/Wire.md) that are connected to the specified pin. [AudioDeviceOutput](/docs/reference/engine/classes/AudioDeviceOutput.md) has one "Input" pin. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `pin` | `string` | | An input or output pin on this instance | **Returns:** `List` — An array of [Wires](/docs/reference/engine/classes/Wire.md) ### Method: AudioDeviceOutput:GetInputPins **Signature:** `AudioDeviceOutput:GetInputPins(): Array` Gets the list of pins that [Wire](/docs/reference/engine/classes/Wire.md) can use in [Wire.TargetName](/docs/reference/engine/classes/Wire.md) to connect to this instance via its [Wire.TargetInstance](/docs/reference/engine/classes/Wire.md) property. For [AudioDeviceOutput](/docs/reference/engine/classes/AudioDeviceOutput.md), this is `Input` only. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Array` — An array of strings representing valid pin names. ### Method: AudioDeviceOutput:GetOutputPins **Signature:** `AudioDeviceOutput:GetOutputPins(): Array` Gets the list of pins that [Wire](/docs/reference/engine/classes/Wire.md) can use in [Wire.SourceName](/docs/reference/engine/classes/Wire.md) to connect to this instance via its [Wire.SourceInstance](/docs/reference/engine/classes/Wire.md) property. For [AudioDeviceOutput](/docs/reference/engine/classes/AudioDeviceOutput.md), there are none. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Array` — An array of strings representing valid pin names. ## Events ### Event: AudioDeviceOutput.WiringChanged **Signature:** `AudioDeviceOutput.WiringChanged(connected: boolean, pin: string, wire: Wire, instance: Instance)` Event that fires after a [Wire](/docs/reference/engine/classes/Wire.md) becomes connected or disconnected, and that [Wire](/docs/reference/engine/classes/Wire.md) is now or was previously connected to a pin on the [AudioDeviceOutput](/docs/reference/engine/classes/AudioDeviceOutput.md) and to some other wirable instance. *Security: None · Capabilities: Audio* **Parameters:** | Name | Type | Description | |------|------|-------------| | `connected` | `boolean` | Whether the instance got connected or disconnected. | | `pin` | `string` | The pin on the [AudioDeviceOutput](/docs/reference/engine/classes/AudioDeviceOutput.md) that the [Wire](/docs/reference/engine/classes/Wire.md) targets. | | `wire` | `Wire` | The [Wire](/docs/reference/engine/classes/Wire.md) between the [AudioDeviceOutput](/docs/reference/engine/classes/AudioDeviceOutput.md) and the other instance. | | `instance` | `Instance` | The other instance that is or was connected through the [Wire](/docs/reference/engine/classes/Wire.md). | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AudioDistortion last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Internal summary: "Distorts audio streams, making them sound fuzzier, grittier, and louder." --- # Class: AudioDistortion > Distorts audio streams, making them sound fuzzier, grittier, and louder. ## Description [AudioDistortion](/docs/reference/engine/classes/AudioDistortion.md) distorts audio streams, making them sound fuzzier, grittier, and louder. It provides one **Input** pin and one **Output** pin which can be connected to/from by [Wires](/docs/reference/engine/classes/Wire.md). ## Properties ### Property: AudioDistortion.Bypass ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` If `true`, audio streams are passed-through unaffected by this effect. ### Property: AudioDistortion.Level ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` Controls how distorted the input stream will become. Ranges from 0 to 1. ## Methods ### Method: AudioDistortion:GetConnectedWires **Signature:** `AudioDistortion:GetConnectedWires(pin: string): List` Returns an array of [Wires](/docs/reference/engine/classes/Wire.md) that are connected to the specified pin. [AudioDistortion](/docs/reference/engine/classes/AudioDistortion.md) has one "Input" pin and one "Output" pin. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `pin` | `string` | | An input or output pin on this instance | **Returns:** `List` — An array of [Wires](/docs/reference/engine/classes/Wire.md) ### Method: AudioDistortion:GetInputPins **Signature:** `AudioDistortion:GetInputPins(): Array` Gets the list of pins that [Wire](/docs/reference/engine/classes/Wire.md) can use in [Wire.TargetName](/docs/reference/engine/classes/Wire.md) to connect to this instance via its [Wire.TargetInstance](/docs/reference/engine/classes/Wire.md) property. For [AudioDistortion](/docs/reference/engine/classes/AudioDistortion.md), this is `Input` only. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Array` — An array of strings representing valid pin names. ### Method: AudioDistortion:GetOutputPins **Signature:** `AudioDistortion:GetOutputPins(): Array` Gets the list of pins that [Wire](/docs/reference/engine/classes/Wire.md) can use in [Wire.SourceName](/docs/reference/engine/classes/Wire.md) to connect to this instance via its [Wire.SourceInstance](/docs/reference/engine/classes/Wire.md) property. For [AudioDistortion](/docs/reference/engine/classes/AudioDistortion.md), this is `Output` only. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Array` — An array of strings representing valid pin names. ## Events ### Event: AudioDistortion.WiringChanged **Signature:** `AudioDistortion.WiringChanged(connected: boolean, pin: string, wire: Wire, instance: Instance)` Event that fires after a [Wire](/docs/reference/engine/classes/Wire.md) becomes connected or disconnected, and that [Wire](/docs/reference/engine/classes/Wire.md) is now or was previously connected to a pin on the [AudioDistortion](/docs/reference/engine/classes/AudioDistortion.md) and to some other wirable instance. *Security: None · Capabilities: Audio* **Parameters:** | Name | Type | Description | |------|------|-------------| | `connected` | `boolean` | Whether the instance got connected or disconnected. | | `pin` | `string` | The pin on the [AudioDistortion](/docs/reference/engine/classes/AudioDistortion.md) that the [Wire](/docs/reference/engine/classes/Wire.md) targets. | | `wire` | `Wire` | The [Wire](/docs/reference/engine/classes/Wire.md) between the [AudioDistortion](/docs/reference/engine/classes/AudioDistortion.md) and the other instance. | | `instance` | `Instance` | The other instance that is or was connected through the [Wire](/docs/reference/engine/classes/Wire.md). | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AudioEcho last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Internal summary: "Overlays delayed copies of audio streams." --- # Class: AudioEcho > Overlays delayed copies of audio streams. ## Description [AudioEcho](/docs/reference/engine/classes/AudioEcho.md) overlays delayed copies of audio streams. It provides one **Input** pin and one **Output** pin which can be connected to/from by [Wires](/docs/reference/engine/classes/Wire.md). ## Properties ### Property: AudioEcho.Bypass ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` If `true`, audio streams are passed-through unaffected by this effect. ### Property: AudioEcho.DelayTime ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` The amount of time between echoes, in seconds. Ranges from 0.001 to 5. ### Property: AudioEcho.DryLevel ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` Gain level, in decibels, determining how loud the original, unaltered audio stream will be. Ranges from -80 to 10. ### Property: AudioEcho.Feedback ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` Determines how slowly echoes fade away, with a range of 0 to 1. A value of 0 means that only a single echo is heard, whereas a value of 1 means that old echoes never fully disappear. ### Property: AudioEcho.RampTime ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` The time, in seconds, the [AudioEcho](/docs/reference/engine/classes/AudioEcho.md) takes to interpolate between different [DelayTime](/docs/reference/engine/classes/AudioEcho.md) values, when changed. This can be used to smooth out changes in the [DelayTime](/docs/reference/engine/classes/AudioEcho.md), or create a pitch bend effect when the [DelayTime](/docs/reference/engine/classes/AudioEcho.md) is modified at a constant rate. Ranges from 0 to 60000. ### Property: AudioEcho.WetLevel ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` Gain level, in decibels, determining how loud the echoed stream will be. Ranges from -80 to 10. ## Methods ### Method: AudioEcho:GetConnectedWires **Signature:** `AudioEcho:GetConnectedWires(pin: string): List` Returns an array of [Wires](/docs/reference/engine/classes/Wire.md) that are connected to the specified pin. [AudioEcho](/docs/reference/engine/classes/AudioEcho.md) has one "Input" pin and one "Output" pin. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `pin` | `string` | | An input or output pin on this instance | **Returns:** `List` — An array of [Wires](/docs/reference/engine/classes/Wire.md) ### Method: AudioEcho:GetInputPins **Signature:** `AudioEcho:GetInputPins(): Array` Gets the list of pins that [Wire](/docs/reference/engine/classes/Wire.md) can use in [Wire.TargetName](/docs/reference/engine/classes/Wire.md) to connect to this instance via its [Wire.TargetInstance](/docs/reference/engine/classes/Wire.md) property. For [AudioEcho](/docs/reference/engine/classes/AudioEcho.md), this is `Input` only. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Array` — An array of strings representing valid pin names. ### Method: AudioEcho:GetOutputPins **Signature:** `AudioEcho:GetOutputPins(): Array` Gets the list of pins that [Wire](/docs/reference/engine/classes/Wire.md) can use in [Wire.SourceName](/docs/reference/engine/classes/Wire.md) to connect to this instance via its [Wire.SourceInstance](/docs/reference/engine/classes/Wire.md) property. For [AudioEcho](/docs/reference/engine/classes/AudioEcho.md), this is `Output` only. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Array` — An array of strings representing valid pin names. ## Events ### Event: AudioEcho.WiringChanged **Signature:** `AudioEcho.WiringChanged(connected: boolean, pin: string, wire: Wire, instance: Instance)` Event that fires after a [Wire](/docs/reference/engine/classes/Wire.md) becomes connected or disconnected, and that [Wire](/docs/reference/engine/classes/Wire.md) is now or was previously connected to a pin on the [AudioEcho](/docs/reference/engine/classes/AudioEcho.md) and to some other wirable instance. *Security: None · Capabilities: Audio* **Parameters:** | Name | Type | Description | |------|------|-------------| | `connected` | `boolean` | Whether the instance got connected or disconnected. | | `pin` | `string` | The pin on the [AudioEcho](/docs/reference/engine/classes/AudioEcho.md) that the [Wire](/docs/reference/engine/classes/Wire.md) targets. | | `wire` | `Wire` | The [Wire](/docs/reference/engine/classes/Wire.md) between the [AudioEcho](/docs/reference/engine/classes/AudioEcho.md) and the other instance. | | `instance` | `Instance` | The other instance that is or was connected through the [Wire](/docs/reference/engine/classes/Wire.md). | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AudioEmitter last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Internal summary: "Emits audio streams into the world." --- # Class: AudioEmitter > Emits audio streams into the world. ## Description [AudioEmitter](/docs/reference/engine/classes/AudioEmitter.md) emits audio streams into the world. It provides a single **Input** pin that can be connected to by one or more [Wires](/docs/reference/engine/classes/Wire.md). Any streams wired to an [AudioEmitter](/docs/reference/engine/classes/AudioEmitter.md) get broadcasted into the world from the emitter's parent's position. If the parent is an [Attachment](/docs/reference/engine/classes/Attachment.md), [Camera](/docs/reference/engine/classes/Camera.md), or [PVInstance](/docs/reference/engine/classes/PVInstance.md), the parent's world-position will be used. If the parent is not one of these classes, the [AudioEmitter](/docs/reference/engine/classes/AudioEmitter.md) is effectively silent. [AudioEmitters](/docs/reference/engine/classes/AudioEmitter.md) are heard by [AudioListeners](/docs/reference/engine/classes/AudioListener.md) in order to implement 3D spatialization. ## Code Samples **Playing one asset from multiple 3d locations at once** ```lua local part1: BasePart = workspace.Speakers.Left local part2: BasePart = workspace.Speakers.Right local player: AudioPlayer = workspace.AudioPlayer local leftEmitter = Instance.new("AudioEmitter") local rightEmitter = Instance.new("AudioEmitter") local toLeft = Instance.new("Wire") local toRight = Instance.new("Wire") leftEmitter.Parent = part1 rightEmitter.Parent = part2 toLeft.Parent = leftEmitter toLeft.SourceInstance = player toLeft.TargetInstance = leftEmitter toRight.Parent = rightEmitter toRight.SourceInstance = player toRight.TargetInstance = rightEmitter player:Play() ``` ## Properties ### Property: AudioEmitter.AcousticSimulationEnabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Simulation", "capabilities": [ "Audio" ] } ``` Determines whether sound from this [AudioEmitter](/docs/reference/engine/classes/AudioEmitter.md) shoud automatically implement features of acoustic simulation, such as occlusion (being muffled through walls), diffraction (bending around corners), and reverberation (echoing off of walls), when being heard by an [AudioListener](/docs/reference/engine/classes/AudioListener.md). Note that the [AudioListener](/docs/reference/engine/classes/AudioListener.md) also needs its [AcousticSimulationEnabled](/docs/reference/engine/classes/AudioListener.md) property set to `true` for the effects to occur. ### Property: AudioEmitter.AngleAttenuation ```json { "type": "Flyweight", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Emission", "capabilities": [ "Audio" ] } ``` Represents a volume-over-angle curve that affects how loudly a [AudioListener](/docs/reference/engine/classes/AudioListener.md) will hear the [AudioEmitter](/docs/reference/engine/classes/AudioEmitter.md), based on the angle between them and the [LookVector](/docs/reference/engine/datatypes/CFrame.md) associated with the [AudioEmitter](/docs/reference/engine/classes/AudioEmitter.md). This property is internal and can't be accessed by scripts; it exists to support replication. See [SetAngleAttenuation()](/docs/reference/engine/classes/AudioEmitter.md) for usage details. ### Property: AudioEmitter.AudioInteractionGroup ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Interactions", "capabilities": [ "Audio" ] } ``` If an [AudioEmitter](/docs/reference/engine/classes/AudioEmitter.md) and an [AudioListener](/docs/reference/engine/classes/AudioListener.md) share an interaction group, then the listener is capable of hearing the emitter. ### Property: AudioEmitter.DistanceAttenuation ```json { "type": "Flyweight", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Emission", "capabilities": [ "Audio" ] } ``` Represents a volume-over-distance curve that affects how loudly a [AudioListener](/docs/reference/engine/classes/AudioListener.md) will hear the [AudioEmitter](/docs/reference/engine/classes/AudioEmitter.md), based on the distance between them. This property is internal and can't be accessed by scripts; it exists to support replication. See [SetDistanceAttenuation()](/docs/reference/engine/classes/AudioEmitter.md) for usage details. ### Property: AudioEmitter.SimulationFidelity ```json { "type": "AudioSimulationFidelity", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Simulation", "capabilities": [ "Audio" ] } ``` Controls how detailed the audio simulation should be for this [AudioEmitter](/docs/reference/engine/classes/AudioEmitter.md), similar to [MeshPart.CollisionFidelity](/docs/reference/engine/classes/MeshPart.md) or [MeshPart.RenderFidelity](/docs/reference/engine/classes/MeshPart.md). [AudioEmitters](/docs/reference/engine/classes/AudioEmitter.md) and [AudioListeners](/docs/reference/engine/classes/AudioListener.md) which disagree on the necessary level of detail will use the less-detailed option. ## Methods ### Method: AudioEmitter:GetAngleAttenuation **Signature:** `AudioEmitter:GetAngleAttenuation(): Dictionary` Returns a table mapping angle to volume. Keys are numbers between `0` and `180` (inclusive), while values are numbers between `0` and `1` (inclusive) describing how volume attenuates depending on direction. This method returns an empty table if the default angle attenuation curve is being used. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Dictionary` — Table mapping angle to volume, as described above. ### Method: AudioEmitter:GetAudibilityFor **Signature:** `AudioEmitter:GetAudibilityFor(listener: AudioListener): float` Calculates how audible this emitter is for a particular [AudioListener](/docs/reference/engine/classes/AudioListener.md). The resulting volume, ranging from `0` to `1`, accounts for distance and angle attenuation on both the emitter and listener. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `listener` | `AudioListener` | | | **Returns:** `float` ### Method: AudioEmitter:GetConnectedWires **Signature:** `AudioEmitter:GetConnectedWires(pin: string): List` Returns an array of [Wires](/docs/reference/engine/classes/Wire.md) that are connected to the specified pin. [AudioEmitter](/docs/reference/engine/classes/AudioEmitter.md) has one "Input" pin. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `pin` | `string` | | An input or output pin on this instance | **Returns:** `List` — An array of [Wires](/docs/reference/engine/classes/Wire.md) ### Method: AudioEmitter:GetDistanceAttenuation **Signature:** `AudioEmitter:GetDistanceAttenuation(): Dictionary` Returns a table mapping distance to volume. Keys are numbers greater than or equal to 0, while values are numbers between 0 and 1 (inclusive) describing how volume attenuates over distance. This method returns an empty table if the default distance attenuation curve is being used. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Dictionary` ### Method: AudioEmitter:GetInputPins **Signature:** `AudioEmitter:GetInputPins(): Array` Gets the list of pins that [Wire](/docs/reference/engine/classes/Wire.md) can use in [Wire.TargetName](/docs/reference/engine/classes/Wire.md) to connect to this instance via its [Wire.TargetInstance](/docs/reference/engine/classes/Wire.md) property. For [AudioEmitter](/docs/reference/engine/classes/AudioEmitter.md), this is `Input` only. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Array` — An array of strings representing valid pin names. ### Method: AudioEmitter:GetInteractingListeners **Signature:** `AudioEmitter:GetInteractingListeners(): List` Returns an array of [AudioListeners](/docs/reference/engine/classes/AudioListener.md) that share an [AudioInteractionGroup](/docs/reference/engine/classes/AudioEmitter.md) with the emitter. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `List` ### Method: AudioEmitter:GetOutputPins **Signature:** `AudioEmitter:GetOutputPins(): Array` Gets the list of pins that [Wire](/docs/reference/engine/classes/Wire.md) can use in [Wire.SourceName](/docs/reference/engine/classes/Wire.md) to connect to this instance via its [Wire.SourceInstance](/docs/reference/engine/classes/Wire.md) property. For [AudioEmitter](/docs/reference/engine/classes/AudioEmitter.md), there are none. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Array` — An array of strings representing valid pin names. ### Method: AudioEmitter:SetAngleAttenuation **Signature:** `AudioEmitter:SetAngleAttenuation(curve: Dictionary): ()` Sets a volume-over-angle curve that affects how loudly a [AudioListener](/docs/reference/engine/classes/AudioListener.md) will hear the [AudioEmitter](/docs/reference/engine/classes/AudioEmitter.md), based on the angle between them and the [LookVector](/docs/reference/engine/datatypes/CFrame.md) associated with the [AudioEmitter](/docs/reference/engine/classes/AudioEmitter.md). The curve is represented by a table mapping angle keys to volume values. Keys are expected to be unique numbers between `0` and `180` (inclusive), while values are expected to be numbers between `0` and `1` (inclusive). Tables containing up to 400 key-value pairs are supported. The volume of the [AudioEmitter](/docs/reference/engine/classes/AudioEmitter.md) from the perspective of a [AudioListener](/docs/reference/engine/classes/AudioListener.md) at an angle `a` is determined by linearly interpolating between the volume levels for the points on the curve whose angle values are directly above and below `a`. If there is either no point below `a` or no point above `a`, the volume level of the other point is chosen. Essentially, the curve is a sequence of points connected by straight lines, and beyond its left and right endpoints the curve extends outward at their respective volume levels. This volume will be multiplied with the volumes from all other attenuation curves (including the ones on the receiving [AudioListener](/docs/reference/engine/classes/AudioListener.md)) to obtain the final audibility. If the table is empty or `nil`, the [AudioEmitter](/docs/reference/engine/classes/AudioEmitter.md) defaults to using an angle attenuation curve with the constant volume value of `1`. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `curve` | `Dictionary` | | | **Returns:** `()` ### Method: AudioEmitter:SetDistanceAttenuation **Signature:** `AudioEmitter:SetDistanceAttenuation(curve: Dictionary): ()` Sets a volume-over-distance curve that affects how loudly a [AudioListener](/docs/reference/engine/classes/AudioListener.md) will hear the [AudioEmitter](/docs/reference/engine/classes/AudioEmitter.md), based on the distance between them. The curve is represented by a table mapping distance keys to volume values. Keys are expected to be unique numbers greater than or equal to 0, while values are expected to be numbers between 0 and 1 (inclusive). Tables containing up to 400 key-value pairs are supported. The volume of the [AudioEmitter](/docs/reference/engine/classes/AudioEmitter.md) from the perspective of a [AudioListener](/docs/reference/engine/classes/AudioListener.md) at a distance `d` is determined by linearly interpolating between the volume levels for the points on the curve whose distance values are directly above and below `d`. If there is either no point below `d` or no point above `d`, the volume level of the other point is chosen. Essentially, the curve is a sequence of points connected by straight lines, and beyond its left and right endpoints the curve extends outward infinitely at their respective volume levels. This volume will be multiplied with the volumes from all other attenuation curves (including the ones on the receiving [AudioListener](/docs/reference/engine/classes/AudioListener.md)) to obtain the final audibility. If the table is empty or `nil`, the [AudioEmitter](/docs/reference/engine/classes/AudioEmitter.md) defaults to using a distance attenuation curve determined by the inverse-square law. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `curve` | `Dictionary` | | | **Returns:** `()` **Custom Distance Attenuation** ```lua local Players = game:GetService("Players") local emitterPart = Instance.new("Part") emitterPart.Anchored = true emitterPart.Position = Vector3.new(0, 0, 0) emitterPart.Parent = workspace local emitter: AudioEmitter = Instance.new("AudioEmitter") emitter.Parent = emitterPart local curve = {} curve[10] = 1 -- Within a distance of 10 studs, listeners hear this emitter at full volume curve[100] = 0.4 -- At a distance of 100 studs, listeners hear this emitter at 40% volume curve[300] = 0 -- At any distance farther than 300 studs, listeners cannot hear this emitter emitter:SetDistanceAttenuation(curve) -- Replicate the rolloff curve from the old voice implementation -- Default voice without the new audio API uses quadratic rolloff ranging from 7 to 80 studs local MIN_DISTANCE = 7 local MAX_DISTANCE = 80 local CURVE_STEP_SIZE = 2 local voiceCurve = {} for i = MIN_DISTANCE, MAX_DISTANCE, CURVE_STEP_SIZE do voiceCurve[i] = ((i - MIN_DISTANCE) - (MAX_DISTANCE - MIN_DISTANCE)) ^ 2 / (MAX_DISTANCE - MIN_DISTANCE) ^ 2 end voiceCurve[MAX_DISTANCE] = 0 local function setVoiceCurve(character) local voiceEmitter: AudioEmitter = character:WaitForChild("AudioEmitter") voiceEmitter:SetDistanceAttenuation(voiceCurve) end for _, player in Players:GetPlayers() do if player.Character then setVoiceCurve(player.Character) end player.CharacterAdded:Connect(setVoiceCurve) end ``` ## Events ### Event: AudioEmitter.WiringChanged **Signature:** `AudioEmitter.WiringChanged(connected: boolean, pin: string, wire: Wire, instance: Instance)` Event that fires after a [Wire](/docs/reference/engine/classes/Wire.md) becomes connected or disconnected, and that [Wire](/docs/reference/engine/classes/Wire.md) is now or was previously connected to a pin on the [AudioEmitter](/docs/reference/engine/classes/AudioEmitter.md) and to some other wirable instance. *Security: None · Capabilities: Audio* **Parameters:** | Name | Type | Description | |------|------|-------------| | `connected` | `boolean` | Whether the instance got connected or disconnected. | | `pin` | `string` | The pin on the [AudioEmitter](/docs/reference/engine/classes/AudioEmitter.md) that the [Wire](/docs/reference/engine/classes/Wire.md) targets. | | `wire` | `Wire` | The [Wire](/docs/reference/engine/classes/Wire.md) between the [AudioEmitter](/docs/reference/engine/classes/AudioEmitter.md) and the other instance. | | `instance` | `Instance` | The other instance that is or was connected through the [Wire](/docs/reference/engine/classes/Wire.md). | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AudioEqualizer last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Internal summary: "Adjusts the frequency content of audio streams." --- # Class: AudioEqualizer > Adjusts the frequency content of audio streams. ## Description [AudioEqualizer](/docs/reference/engine/classes/AudioEqualizer.md) adjusts the frequency content of audio streams. It provides one **Input** pin and one **Output** pin which can be connected to/from by [Wires](/docs/reference/engine/classes/Wire.md). [AudioEqualizer](/docs/reference/engine/classes/AudioEqualizer.md) has 3 frequency bands whose gain values can be controlled, and the crossover points between bands can be moved. ## Code Samples **Listener Equalization** An AudioEqualizer can be used to change the frequency content of audio streams. This can be done before emission, or after listening, and can be used to implement your own, custom RollOff logic! In this example, we use an AudioEqualizer to make an AudioPlayer's high frequencies more muffled as the AudioListener looks away from the AudioEmitter. We also reduce both the low and high frequencies as the listener gets further away. ```lua local function wireUp(source: Instance, target: Instance): Wire local wire = Instance.new("Wire") wire.Parent = target wire.SourceInstance = source wire.TargetInstance = target return wire end local function getCFrameFrom(inst: Instance): CFrame? local parent = inst.Parent if not parent then return nil elseif parent:IsA("Model") then return parent.WorldPivot elseif parent:IsA("BasePart") then return parent.CFrame elseif parent:IsA("Attachment") then return parent.WorldCFrame elseif parent:IsA("Camera") then return parent.CFrame else return nil end end local function rescale(value: number, oldRange: NumberRange, newRange: NumberRange): number local clamped = math.clamp(value, oldRange.Min, oldRange.Max) local normalized = clamped - oldRange.Min / (oldRange.Max - oldRange.Min) return normalized * (newRange.Max - newRange.Min) + newRange.Min end local assetPlayer = Instance.new("AudioPlayer") assetPlayer.AssetId = "rbxassetid://142376088" assetPlayer.Parent = workspace local equalizer = Instance.new("AudioEqualizer") equalizer.MidRange = NumberRange.new(400, 3000) equalizer.Parent = workspace local emitterPart = Instance.new("Part") emitterPart.Anchored = true emitterPart.Position = Vector3.new(0, 5, 0) emitterPart.Parent = workspace local emitter = Instance.new("AudioEmitter") emitter.Parent = emitterPart local listener = Instance.new("AudioListener") listener.Parent = workspace.CurrentCamera local output = Instance.new("AudioDeviceOutput") output.Parent = workspace wireUp(assetPlayer, equalizer) wireUp(equalizer, emitter) wireUp(listener, output) assetPlayer.Looping = true assetPlayer:Play() while true do local emitterFrame = getCFrameFrom(emitter) local listenerFrame = getCFrameFrom(listener) if emitterFrame and listenerFrame then local towardEmitter = emitterFrame.Position - listenerFrame.Position local look = towardEmitter.Unit:Dot(listenerFrame.LookVector) -- ranges from [-1, 1] look = rescale(look, NumberRange.new(-1, 1), NumberRange.new(-20, 0)) local distance = math.max(towardEmitter.Magnitude, 1) local rolloff = 1 / distance -- ranges from [0, 1] rolloff = rescale(rolloff, NumberRange.new(0, 1), NumberRange.new(-10, 10)) equalizer.HighGain = look + rolloff equalizer.LowGain = rolloff end task.wait() end ``` ## Properties ### Property: AudioEqualizer.Bypass ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` If `true`, audio streams are passed-through unaffected by this effect. ### Property: AudioEqualizer.HighGain ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` Gain value, in decibels, to be applied to the frequency content of the highest band in the equalizer. Ranges from -80 to 10. ### Property: AudioEqualizer.LowGain ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` Gain value, in decibels, to be applied to the frequency content of the lowest band in the equalizer. Ranges from -80 to 10. ### Property: AudioEqualizer.MidGain ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` Gain value, in decibels, to be applied to the frequency content of the middle band in the equalizer. Ranges from -80 to 10. ### Property: AudioEqualizer.MidRange ```json { "type": "NumberRange", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` The frequency range in hertz of the band influenced by [MidGain](/docs/reference/engine/classes/AudioEqualizer.md). The lower value of the range determines the crossover frequency between the low and mid bands. The higher value of the range determines the crossover frequency between the mid and high bands. Both crossover frequencies range from 200 to 20,000. ## Methods ### Method: AudioEqualizer:GetConnectedWires **Signature:** `AudioEqualizer:GetConnectedWires(pin: string): List` Returns an array of [Wires](/docs/reference/engine/classes/Wire.md) that are connected to the specified pin. [AudioEqualizer](/docs/reference/engine/classes/AudioEqualizer.md) has one "Input" pin and one "Output" pin. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `pin` | `string` | | An input or output pin on this instance | **Returns:** `List` — An array of [Wires](/docs/reference/engine/classes/Wire.md) ### Method: AudioEqualizer:GetInputPins **Signature:** `AudioEqualizer:GetInputPins(): Array` Gets the list of pins that [Wire](/docs/reference/engine/classes/Wire.md) can use in [Wire.TargetName](/docs/reference/engine/classes/Wire.md) to connect to this instance via its [Wire.TargetInstance](/docs/reference/engine/classes/Wire.md) property. For [AudioEqualizer](/docs/reference/engine/classes/AudioEqualizer.md), this is `Input` only. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Array` — An array of strings representing valid pin names. ### Method: AudioEqualizer:GetOutputPins **Signature:** `AudioEqualizer:GetOutputPins(): Array` Gets the list of pins that [Wire](/docs/reference/engine/classes/Wire.md) can use in [Wire.SourceName](/docs/reference/engine/classes/Wire.md) to connect to this instance via its [Wire.SourceInstance](/docs/reference/engine/classes/Wire.md) property. For [AudioEqualizer](/docs/reference/engine/classes/AudioEqualizer.md), this is `Output` only. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Array` — An array of strings representing valid pin names. ## Events ### Event: AudioEqualizer.WiringChanged **Signature:** `AudioEqualizer.WiringChanged(connected: boolean, pin: string, wire: Wire, instance: Instance)` Event that fires after a [Wire](/docs/reference/engine/classes/Wire.md) becomes connected or disconnected, and that [Wire](/docs/reference/engine/classes/Wire.md) is now or was previously connected to a pin on the [AudioEqualizer](/docs/reference/engine/classes/AudioEqualizer.md) and to some other wirable instance. *Security: None · Capabilities: Audio* **Parameters:** | Name | Type | Description | |------|------|-------------| | `connected` | `boolean` | Whether the instance got connected or disconnected. | | `pin` | `string` | The pin on the [AudioEqualizer](/docs/reference/engine/classes/AudioEqualizer.md) that the [Wire](/docs/reference/engine/classes/Wire.md) targets. | | `wire` | `Wire` | The [Wire](/docs/reference/engine/classes/Wire.md) between the [AudioEqualizer](/docs/reference/engine/classes/AudioEqualizer.md) and the other instance. | | `instance` | `Instance` | The other instance that is or was connected through the [Wire](/docs/reference/engine/classes/Wire.md). | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AudioFader last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Internal summary: "Adjusts the volume of audio streams." --- # Class: AudioFader > Adjusts the volume of audio streams. ## Description [AudioFader](/docs/reference/engine/classes/AudioFader.md) adjusts the volume of audio streams. It provides one **Input** pin and one **Output** pin which can be connected to/from by [Wires](/docs/reference/engine/classes/Wire.md). ## Properties ### Property: AudioFader.Bypass ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` If `true`, audio streams are passed-through unaffected by this effect. ### Property: AudioFader.Volume ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` Volume level which is multiplied onto the input stream. Ranges from 0 to 3. ## Methods ### Method: AudioFader:GetConnectedWires **Signature:** `AudioFader:GetConnectedWires(pin: string): List` Returns an array of [Wires](/docs/reference/engine/classes/Wire.md) that are connected to the specified pin. [AudioFader](/docs/reference/engine/classes/AudioFader.md) has one "Input" pin and one "Output" pin. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `pin` | `string` | | An input or output pin on this instance | **Returns:** `List` — An array of [Wires](/docs/reference/engine/classes/Wire.md) ### Method: AudioFader:GetInputPins **Signature:** `AudioFader:GetInputPins(): Array` Gets the list of pins that [Wire](/docs/reference/engine/classes/Wire.md) can use in [Wire.TargetName](/docs/reference/engine/classes/Wire.md) to connect to this instance via its [Wire.TargetInstance](/docs/reference/engine/classes/Wire.md) property. For [AudioFader](/docs/reference/engine/classes/AudioFader.md), this is `Input` only. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Array` — An array of strings representing valid pin names. ### Method: AudioFader:GetOutputPins **Signature:** `AudioFader:GetOutputPins(): Array` Gets the list of pins that [Wire](/docs/reference/engine/classes/Wire.md) can use in [Wire.SourceName](/docs/reference/engine/classes/Wire.md) to connect to this instance via its [Wire.SourceInstance](/docs/reference/engine/classes/Wire.md) property. For [AudioFader](/docs/reference/engine/classes/AudioFader.md), this is `Output` only. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Array` — An array of strings representing valid pin names. ## Events ### Event: AudioFader.WiringChanged **Signature:** `AudioFader.WiringChanged(connected: boolean, pin: string, wire: Wire, instance: Instance)` Event that fires after a [Wire](/docs/reference/engine/classes/Wire.md) becomes connected or disconnected, and that [Wire](/docs/reference/engine/classes/Wire.md) is now or was previously connected to a pin on the [AudioFader](/docs/reference/engine/classes/AudioFader.md) and to some other wirable instance. *Security: None · Capabilities: Audio* **Parameters:** | Name | Type | Description | |------|------|-------------| | `connected` | `boolean` | Whether the instance got connected or disconnected. | | `pin` | `string` | The pin on the [AudioFader](/docs/reference/engine/classes/AudioFader.md) that the [Wire](/docs/reference/engine/classes/Wire.md) targets. | | `wire` | `Wire` | The [Wire](/docs/reference/engine/classes/Wire.md) between the [AudioFader](/docs/reference/engine/classes/AudioFader.md) and the other instance. | | `instance` | `Instance` | The other instance that is or was connected through the [Wire](/docs/reference/engine/classes/Wire.md). | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AudioFilter last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Internal summary: "Adjusts the frequency content of audio streams." --- # Class: AudioFilter > Adjusts the frequency content of audio streams. ## Description [AudioFilter](/docs/reference/engine/classes/AudioFilter.md) adjusts the frequency content of audio streams. It provides one **Input** pin and one **Output** pin which can be connected to/from by [Wires](/docs/reference/engine/classes/Wire.md). [AudioFilter](/docs/reference/engine/classes/AudioFilter.md) uses its [FilterType](/docs/reference/engine/classes/AudioFilter.md), [Gain](/docs/reference/engine/classes/AudioFilter.md), and [Q](/docs/reference/engine/classes/AudioFilter.md) properties to determine what to do around a particular cutoff [Frequency](/docs/reference/engine/classes/AudioFilter.md). ## Code Samples **Emitter Filtering** An AudioFilter can be used to change the frequency content of audio streams. In this example, an AudioFilter is used to make the AudioEmitter output more muffled when there's a wall between it and the AudioListener. ```lua -- This assumes the workspace contains a Part with an AudioEmitter and an AudioPlayer, and the camera has an AudioListener local RunService = game:GetService("RunService") local part: BasePart = workspace.Part local camera: Camera = workspace.CurrentCamera local audioPlayer: AudioPlayer = part.AudioPlayer local audioEmitter: AudioEmitter = part.AudioEmitter local audioListener: AudioListener = camera.AudioListener local raycastParams = RaycastParams.new() raycastParams.FilterDescendantsInstances = { audioEmitter.Parent } raycastParams.FilterType = Enum.RaycastFilterType.Exclude -- Create a new AudioFilter local filter: AudioFilter = Instance.new("AudioFilter") filter.FilterType = Enum.AudioFilterType.Lowpass12dB filter.Frequency = 22000 filter.Q = math.sqrt(2) / 2 -- This Q value produces a flat lowpass for the 12dB slope type filter.Parent = part -- Put the AudioFilter between the player and the emitter local function wireTo(source: Instance, target: Instance): Wire local wire = Instance.new("Wire") wire.SourceInstance = source wire.TargetInstance = target wire.Parent = target end wireTo(audioPlayer, filter) wireTo(filter, audioEmitter) -- Update the filter based on the positions of the emitter and listener RunService.Heartbeat:Connect(function() local emitterPos: Vector3 = part.Position local listenerPos: Vector3 = camera.CFrame.Position local raycastResult = workspace:Raycast(emitterPos, (listenerPos - emitterPos), raycastParams) filter.Frequency = if raycastResult then 500 else 22000 end) ``` ## Properties ### Property: AudioFilter.Bypass ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` If `true`, audio streams are passed-through unaffected by this effect. ### Property: AudioFilter.FilterType ```json { "type": "AudioFilterType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` The type of frequency response curve that will be used to filter the audio signal. Each type of curve affects the frequency content of the audio in different ways. ### Property: AudioFilter.Frequency ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` The central frequency in hertz of the curve represented by the filter. Generally, adjusting this value up or down corresponds to a horizontal shift in the overall frequency curve. Ranges from 20 to 22000. ### Property: AudioFilter.Gain ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` The gain value in decibels used to determine the volume level of the curve represented by the filter. Only applies when the [FilterType](/docs/reference/engine/classes/AudioFilter.md) is `Peak`, `LowShelf`, or `HighShelf`. Ranges from -30 to 30. ### Property: AudioFilter.Q ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` The quality value used to determine the slope or resonance of the curve represented by the filter. Only applies when the [FilterType](/docs/reference/engine/classes/AudioFilter.md) is `Peak`, `Lowpass[x]dB`, `Highpass[x]dB`, `Bandpass`, or `Notch`. Ranges from 0.1 to 10. For [FilterType](/docs/reference/engine/classes/AudioFilter.md) values of [Lowpass12dB](/docs/reference/engine/enums/AudioFilterType.md) and [Highpass12dB](/docs/reference/engine/enums/AudioFilterType.md), a **Q** value of `sqrt(2) / 2`, or `0.707`, corresponds to a flat filter at a 12dB/octave slope. ## Methods ### Method: AudioFilter:GetConnectedWires **Signature:** `AudioFilter:GetConnectedWires(pin: string): List` Returns an array of [Wires](/docs/reference/engine/classes/Wire.md) that are connected to the specified pin. [AudioFilter](/docs/reference/engine/classes/AudioFilter.md) has one "Input" pin and one "Output" pin. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `pin` | `string` | | An input or output pin on this instance | **Returns:** `List` — An array of [Wires](/docs/reference/engine/classes/Wire.md) ### Method: AudioFilter:GetGainAt **Signature:** `AudioFilter:GetGainAt(frequency: float): float` Returns the gain value, in decibels, of the frequency response curve represented by the filter at the given frequency, in hertz. This can be used to sample the exact shape of the filter in key places or as a whole. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `frequency` | `float` | | The frequency, in hertz, to sample. | **Returns:** `float` — The gain value, in decibels, at the given frequency. ### Method: AudioFilter:GetInputPins **Signature:** `AudioFilter:GetInputPins(): Array` Gets the list of pins that [Wire](/docs/reference/engine/classes/Wire.md) can use in [Wire.TargetName](/docs/reference/engine/classes/Wire.md) to connect to this instance via its [Wire.TargetInstance](/docs/reference/engine/classes/Wire.md) property. For [AudioFilter](/docs/reference/engine/classes/AudioFilter.md), this is `Input` only. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Array` — An array of strings representing valid pin names. ### Method: AudioFilter:GetOutputPins **Signature:** `AudioFilter:GetOutputPins(): Array` Gets the list of pins that [Wire](/docs/reference/engine/classes/Wire.md) can use in [Wire.SourceName](/docs/reference/engine/classes/Wire.md) to connect to this instance via its [Wire.SourceInstance](/docs/reference/engine/classes/Wire.md) property. For [AudioFilter](/docs/reference/engine/classes/AudioFilter.md), this is `Output` only. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Array` — An array of strings representing valid pin names. ## Events ### Event: AudioFilter.WiringChanged **Signature:** `AudioFilter.WiringChanged(connected: boolean, pin: string, wire: Wire, instance: Instance)` Event that fires after a [Wire](/docs/reference/engine/classes/Wire.md) becomes connected or disconnected, and that [Wire](/docs/reference/engine/classes/Wire.md) is now or was previously connected to a pin on the [AudioFilter](/docs/reference/engine/classes/AudioFilter.md) and to some other wirable instance. *Security: None · Capabilities: Audio* **Parameters:** | Name | Type | Description | |------|------|-------------| | `connected` | `boolean` | Whether the instance got connected or disconnected. | | `pin` | `string` | The pin on the [AudioFilter](/docs/reference/engine/classes/AudioFilter.md) that the [Wire](/docs/reference/engine/classes/Wire.md) targets. | | `wire` | `Wire` | The [Wire](/docs/reference/engine/classes/Wire.md) between the [AudioFilter](/docs/reference/engine/classes/AudioFilter.md) and the other instance. | | `instance` | `Instance` | The other instance that is or was connected through the [Wire](/docs/reference/engine/classes/Wire.md). | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AudioFlanger last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Internal summary: "Imparts a whooshing or sweeping sound on audio streams." --- # Class: AudioFlanger > Imparts a whooshing or sweeping sound on audio streams. ## Description [AudioFlanger](/docs/reference/engine/classes/AudioFlanger.md) imparts a whooshing or sweeping sound on audio streams by overlaying a delayed copy of the input stream and modulating the pitch of the copy. It provides one **Input** pin and one **Output** pin which can be connected to/from by [Wires](/docs/reference/engine/classes/Wire.md). ## Properties ### Property: AudioFlanger.Bypass ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` If `true`, audio streams are passed-through unaffected by this effect. ### Property: AudioFlanger.Depth ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` Controls how strong the pitch modulation of the flanger is. Ranges from 0.01 to 1 which determines the maximum delay time of the modulated signal. A value of 1 corresponds to 10 milliseconds of maximum delay; beyond this, the modulated signal would begin to sound more like an echo than a flange. ### Property: AudioFlanger.Mix ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` Controls the balance of plain input stream to modified output stream. Ranges from 0 to 1. ### Property: AudioFlanger.Rate ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` Frequency controlling the rate of pitch modulations, in hertz. Ranges from 0 to 20. ## Methods ### Method: AudioFlanger:GetConnectedWires **Signature:** `AudioFlanger:GetConnectedWires(pin: string): List` Returns an array of [Wires](/docs/reference/engine/classes/Wire.md) that are connected to the specified pin. [AudioFlanger](/docs/reference/engine/classes/AudioFlanger.md) has one "Input" pin and one "Output" pin. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `pin` | `string` | | An input or output pin on this instance | **Returns:** `List` — An array of [Wires](/docs/reference/engine/classes/Wire.md) ### Method: AudioFlanger:GetInputPins **Signature:** `AudioFlanger:GetInputPins(): Array` Gets the list of pins that [Wire](/docs/reference/engine/classes/Wire.md) can use in [Wire.TargetName](/docs/reference/engine/classes/Wire.md) to connect to this instance via its [Wire.TargetInstance](/docs/reference/engine/classes/Wire.md) property. For [AudioFlanger](/docs/reference/engine/classes/AudioFlanger.md), this is `Input` only. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Array` — An array of strings representing valid pin names. ### Method: AudioFlanger:GetOutputPins **Signature:** `AudioFlanger:GetOutputPins(): Array` Gets the list of pins that [Wire](/docs/reference/engine/classes/Wire.md) can use in [Wire.SourceName](/docs/reference/engine/classes/Wire.md) to connect to this instance via its [Wire.SourceInstance](/docs/reference/engine/classes/Wire.md) property. For [AudioFlanger](/docs/reference/engine/classes/AudioFlanger.md), this is `Output` only. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Array` — An array of strings representing valid pin names. ## Events ### Event: AudioFlanger.WiringChanged **Signature:** `AudioFlanger.WiringChanged(connected: boolean, pin: string, wire: Wire, instance: Instance)` Event that fires after a [Wire](/docs/reference/engine/classes/Wire.md) becomes connected or disconnected, and that [Wire](/docs/reference/engine/classes/Wire.md) is now or was previously connected to a pin on the [AudioFlanger](/docs/reference/engine/classes/AudioFlanger.md) and to some other wirable instance. *Security: None · Capabilities: Audio* **Parameters:** | Name | Type | Description | |------|------|-------------| | `connected` | `boolean` | Whether the instance got connected or disconnected. | | `pin` | `string` | The pin on the [AudioFlanger](/docs/reference/engine/classes/AudioFlanger.md) that the [Wire](/docs/reference/engine/classes/Wire.md) targets. | | `wire` | `Wire` | The [Wire](/docs/reference/engine/classes/Wire.md) between the [AudioFlanger](/docs/reference/engine/classes/AudioFlanger.md) and the other instance. | | `instance` | `Instance` | The other instance that is or was connected through the [Wire](/docs/reference/engine/classes/Wire.md). | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AudioGate last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Internal summary: "Mutes audio streams that fall below a certain volume threshold." --- # Class: AudioGate > Mutes audio streams that fall below a certain volume threshold. ## Description [AudioGate](/docs/reference/engine/classes/AudioGate.md) behaves like a noise gate, muting audio streams when their volume drops below a specified [Threshold](/docs/reference/engine/classes/AudioGate.md). It is useful for removing background noise from microphone input or cleaning up audio tracks. It provides one **Input** pin and one **Output** pin which can be connected to/from by [Wires](/docs/reference/engine/classes/Wire.md). ## Properties ### Property: AudioGate.Attack ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` The duration, in seconds, over which the gate will unmute the audio stream when the signal level rises above the [Threshold](/docs/reference/engine/classes/AudioGate.md). Ranges from 0.001 to 5. ### Property: AudioGate.Bypass ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` If `true`, audio streams are passed-through unaffected by this effect. ### Property: AudioGate.Release ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` The duration, in seconds, over which the gate will mute the audio stream when the signal level drops below the [Threshold](/docs/reference/engine/classes/AudioGate.md). Ranges from 0.001 to 5. ### Property: AudioGate.Threshold ```json { "type": "NumberRange", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` The gain value(s), in decibels, at which the gate will transition from being closed (i.e. muted) to being open (i.e. unmuted). [AudioGate.Threshold](/docs/reference/engine/classes/AudioGate.md) is a [NumberRange](/docs/reference/engine/classes/NumberRange.md) with a "low" and a "high" threshold. The gate starts out muted - when the input stream's volume rises above the "high" threshold, the gate will unmute the audio stream over the course of [Attack](/docs/reference/engine/classes/AudioGate.md) seconds. If the input stream's volume then dips below the "low" threshold, the gate will mute the audio stream over the course of [Release](/docs/reference/engine/classes/AudioGate.md) seconds. The two "low" and "high" thresholds create a hysteresis region to prevent the gate from opening and closing very rapidly. Ranges from -80 to 30, and defaults to -36 (low) and -24 (high). ## Methods ### Method: AudioGate:GetConnectedWires **Signature:** `AudioGate:GetConnectedWires(pin: string): List` Returns an array of [Wires](/docs/reference/engine/classes/Wire.md) that are connected to the specified pin. [AudioGate](/docs/reference/engine/classes/AudioGate.md) has one "Input" pin and one "Output" pin. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `pin` | `string` | | An input or output pin on this instance | **Returns:** `List` — An array of [Wires](/docs/reference/engine/classes/Wire.md) ### Method: AudioGate:GetInputPins **Signature:** `AudioGate:GetInputPins(): Array` Gets the list of pins that [Wire](/docs/reference/engine/classes/Wire.md) can use in [Wire.TargetName](/docs/reference/engine/classes/Wire.md) to connect to this instance via its [Wire.TargetInstance](/docs/reference/engine/classes/Wire.md) property. For [AudioGate](/docs/reference/engine/classes/AudioGate.md), this is `Input` only. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Array` — An array of strings representing valid pin names. ### Method: AudioGate:GetOutputPins **Signature:** `AudioGate:GetOutputPins(): Array` Gets the list of pins that [Wire](/docs/reference/engine/classes/Wire.md) can use in [Wire.SourceName](/docs/reference/engine/classes/Wire.md) to connect to this instance via its [Wire.SourceInstance](/docs/reference/engine/classes/Wire.md) property. For [AudioGate](/docs/reference/engine/classes/AudioGate.md), this is `Output` only. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Array` — An array of strings representing valid pin names. ## Events ### Event: AudioGate.WiringChanged **Signature:** `AudioGate.WiringChanged(connected: boolean, pin: string, wire: Wire, instance: Instance)` Event that fires after a [Wire](/docs/reference/engine/classes/Wire.md) becomes connected or disconnected, and that [Wire](/docs/reference/engine/classes/Wire.md) is now or was previously connected to a pin on the [AudioGate](/docs/reference/engine/classes/AudioGate.md) and to some other wirable instance. *Security: None · Capabilities: Audio* **Parameters:** | Name | Type | Description | |------|------|-------------| | `connected` | `boolean` | Whether the instance got connected or disconnected. | | `pin` | `string` | The pin on the [AudioGate](/docs/reference/engine/classes/AudioGate.md) that the [Wire](/docs/reference/engine/classes/Wire.md) targets. | | `wire` | `Wire` | The [Wire](/docs/reference/engine/classes/Wire.md) between the [AudioGate](/docs/reference/engine/classes/AudioGate.md) and the other instance. | | `instance` | `Instance` | The other instance that is or was connected through the [Wire](/docs/reference/engine/classes/Wire.md). | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AudioLimiter last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Internal summary: "Limits how loud audio streams are allowed to be." --- # Class: AudioLimiter > Limits how loud audio streams are allowed to be. ## Description `AudioLimiter` limits how loud audio streams are allowed to be. Whenever its input stream exceeds a specified maximum level, the stream's volume is reduced for a moment. `AudioLimiter` provides a single **Input** pin, and a single **Output** pin that can be connected to/from by [Wires](/docs/reference/engine/classes/Wires.md). ## Properties ### Property: AudioLimiter.Bypass ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` If `true`, audio streams are passed-through unaffected by this effect. ### Property: AudioLimiter.MaxLevel ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` The maximum volume, in decibels, that the limiter will allow to pass through without reduction. Whenever the input stream exceeds [MaxLevel](/docs/reference/engine/classes/AudioLimiter.md), the output stream's volume will be reduced to compensate. This value ranges from `-12` to `0`. ### Property: AudioLimiter.Release ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` The amount of time, in seconds, that it takes for any previously (but not currently) limited streams to return to their normal volume. This value ranges from `0.001` to `1`. ## Methods ### Method: AudioLimiter:GetConnectedWires **Signature:** `AudioLimiter:GetConnectedWires(pin: string): List` Returns an array of [Wires](/docs/reference/engine/classes/Wire.md) that are connected to the specified pin. [AudioLimiter](/docs/reference/engine/classes/AudioLimiter.md) has one **Input** pin and one **Output** pin. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `pin` | `string` | | An input or output pin on this instance | **Returns:** `List` — An array of [Wires](/docs/reference/engine/classes/Wire.md) ### Method: AudioLimiter:GetInputPins **Signature:** `AudioLimiter:GetInputPins(): Array` Gets the list of pins that [Wire](/docs/reference/engine/classes/Wire.md) can use in [Wire.TargetName](/docs/reference/engine/classes/Wire.md) to connect to this instance via its [Wire.TargetInstance](/docs/reference/engine/classes/Wire.md) property. For [AudioLimiter](/docs/reference/engine/classes/AudioLimiter.md), this is `Input` only. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Array` — An array of strings representing valid pin names. ### Method: AudioLimiter:GetOutputPins **Signature:** `AudioLimiter:GetOutputPins(): Array` Gets the list of pins that [Wire](/docs/reference/engine/classes/Wire.md) can use in [Wire.SourceName](/docs/reference/engine/classes/Wire.md) to connect to this instance via its [Wire.SourceInstance](/docs/reference/engine/classes/Wire.md) property. For [AudioLimiter](/docs/reference/engine/classes/AudioLimiter.md), this is `Output` only. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Array` — An array of strings representing valid pin names. ## Events ### Event: AudioLimiter.WiringChanged **Signature:** `AudioLimiter.WiringChanged(connected: boolean, pin: string, wire: Wire, instance: Instance)` Event that fires after a [Wire](/docs/reference/engine/classes/Wire.md) becomes connected or disconnected, and that [Wire](/docs/reference/engine/classes/Wire.md) is now or was previously connected to a pin on the [AudioLimiter](/docs/reference/engine/classes/AudioLimiter.md) and to some other wirable instance. *Security: None · Capabilities: Audio* **Parameters:** | Name | Type | Description | |------|------|-------------| | `connected` | `boolean` | Whether the instance got connected or disconnected. | | `pin` | `string` | The pin on the [AudioLimiter](/docs/reference/engine/classes/AudioLimiter.md) that the [Wire](/docs/reference/engine/classes/Wire.md) targets. | | `wire` | `Wire` | The [Wire](/docs/reference/engine/classes/Wire.md) between the [AudioLimiter](/docs/reference/engine/classes/AudioLimiter.md) and the other instance. | | `instance` | `Instance` | The other instance that is or was connected through the [Wire](/docs/reference/engine/classes/Wire.md). | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AudioListener last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Internal summary: "Records an audio stream from its surrounding AudioEmitters in the 3D world." --- # Class: AudioListener > Records an audio stream from its surrounding > [AudioEmitters](/docs/reference/engine/classes/AudioEmitter.md) in the 3D world. ## Description [AudioListener](/docs/reference/engine/classes/AudioListener.md) records an audio stream from its surrounding [AudioEmitters](/docs/reference/engine/classes/AudioEmitter.md) in the 3D world. It provides a single **Output** pin which can be connected to other pins via [Wires](/docs/reference/engine/classes/Wire.md). If the parent is an [Attachment](/docs/reference/engine/classes/Attachment.md), [Camera](/docs/reference/engine/classes/Camera.md), or [PVInstance](/docs/reference/engine/classes/PVInstance.md), the parent's world [CFrame](/docs/reference/engine/datatypes/CFrame.md) will be used for listening. If the parent is not one of these classes, the [AudioListener](/docs/reference/engine/classes/AudioListener.md) effectively hears nothing. ## Code Samples **Camera Listener** ```lua local listener = Instance.new("AudioListener") local output = Instance.new("AudioDeviceOutput") local wire = Instance.new("Wire") listener.Parent = workspace.Camera wire.Parent = listener output.Parent = wire wire.SourceInstance = listener wire.TargetInstance = output ``` ## Properties ### Property: AudioListener.AcousticSimulationEnabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Simulation", "capabilities": [ "Audio" ] } ``` Determines whether sound being heard by this [AudioListener](/docs/reference/engine/classes/AudioListener.md) shoud automatically implement features of acoustic simulation, such as occlusion (being muffled through walls), diffraction (bending around corners), and reverberation (echoing off of walls), when being sourced from an [AudioEmitter](/docs/reference/engine/classes/AudioEmitter.md). Note that the [AudioEmitter](/docs/reference/engine/classes/AudioEmitter.md) also needs its [AcousticSimulationEnabled](/docs/reference/engine/classes/AudioEmitter.md) property set to `true` for the effects to occur. ### Property: AudioListener.AngleAttenuation ```json { "type": "Flyweight", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Listening", "capabilities": [ "Audio" ] } ``` Represents a volume-over-angle curve that affects how loudly a [AudioListener](/docs/reference/engine/classes/AudioListener.md) will hear the [AudioEmitter](/docs/reference/engine/classes/AudioEmitter.md), based on the angle between them and the [LookVector](/docs/reference/engine/datatypes/CFrame.md) associated with the [AudioListener](/docs/reference/engine/classes/AudioListener.md). This property is internal and can't be accessed by scripts; it exists to support replication. See [SetAngleAttenuation()](/docs/reference/engine/classes/AudioListener.md) for usage details. ### Property: AudioListener.AudioInteractionGroup ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Interactions", "capabilities": [ "Audio" ] } ``` Controls which [AudioEmitters](/docs/reference/engine/classes/AudioEmitter.md) are audible to this [AudioListener](/docs/reference/engine/classes/AudioListener.md). Emitters that share an interaction group can be heard by this Listener. ### Property: AudioListener.DistanceAttenuation ```json { "type": "Flyweight", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Listening", "capabilities": [ "Audio" ] } ``` Represents a volume-over-distance curve that affects how loudly the [AudioListener](/docs/reference/engine/classes/AudioListener.md) hears any [AudioEmitters](/docs/reference/engine/classes/AudioEmitter.md), based on the distance between them. This property is internal and can't be accessed by scripts; it exists to support replication. See [SetDistanceAttenuation()](/docs/reference/engine/classes/AudioListener.md) for usage details. ### Property: AudioListener.SimulationFidelity ```json { "type": "AudioSimulationFidelity", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Simulation", "capabilities": [ "Audio" ] } ``` Controls how detailed the audio simulation should be for this [AudioListener](/docs/reference/engine/classes/AudioListener.md), similar to [MeshPart.CollisionFidelity](/docs/reference/engine/classes/MeshPart.md) or [MeshPart.RenderFidelity](/docs/reference/engine/classes/MeshPart.md). [AudioListeners](/docs/reference/engine/classes/AudioListener.md) and [AudioEmitters](/docs/reference/engine/classes/AudioEmitter.md) which disagree on the necessary level of detail will use the less-detailed option. ## Methods ### Method: AudioListener:GetAngleAttenuation **Signature:** `AudioListener:GetAngleAttenuation(): Dictionary` Returns a table mapping angle to volume. Keys are numbers between `0` and `180` (inclusive), while values are numbers between `0` and `1` (inclusive) describing how volume attenuates depending on direction. This method returns an empty table if the default angle attenuation curve is being used. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Dictionary` — Table mapping angle to volume, as described above. ### Method: AudioListener:GetAudibilityFor **Signature:** `AudioListener:GetAudibilityFor(emitter: AudioEmitter): float` Calculates how audible an [AudioEmitter](/docs/reference/engine/classes/AudioEmitter.md) is for this listener. The resulting volume, ranging from `0` to `1`, accounts for distance and angle attenuation on both the emitter and listener. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `emitter` | `AudioEmitter` | | | **Returns:** `float` ### Method: AudioListener:GetConnectedWires **Signature:** `AudioListener:GetConnectedWires(pin: string): List` Returns an array of [Wires](/docs/reference/engine/classes/Wire.md) that are connected to the specified pin. [AudioListener](/docs/reference/engine/classes/AudioListener.md) has one "Output" pin. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `pin` | `string` | | An input or output pin on this instance | **Returns:** `List` — An array of [Wires](/docs/reference/engine/classes/Wire.md) ### Method: AudioListener:GetDistanceAttenuation **Signature:** `AudioListener:GetDistanceAttenuation(): Dictionary` Returns a table mapping distance to volume. Keys are numbers greater than or equal to 0, while values are numbers between 0 and 1 (inclusive) describing how volume attenuates over distance. This method returns an empty table if the default distance attenuation curve is being used. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Dictionary` ### Method: AudioListener:GetInputPins **Signature:** `AudioListener:GetInputPins(): Array` Gets the list of pins that [Wire](/docs/reference/engine/classes/Wire.md) can use in [Wire.TargetName](/docs/reference/engine/classes/Wire.md) to connect to this instance via its [Wire.TargetInstance](/docs/reference/engine/classes/Wire.md) property. For [AudioListener](/docs/reference/engine/classes/AudioListener.md), there are none. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Array` — An array of strings representing valid pin names. ### Method: AudioListener:GetInteractingEmitters **Signature:** `AudioListener:GetInteractingEmitters(): List` Returns an array of [AudioEmitters](/docs/reference/engine/classes/AudioEmitter.md) that share an [AudioInteractionGroup](/docs/reference/engine/classes/AudioListener.md) with the listener. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `List` ### Method: AudioListener:GetOutputPins **Signature:** `AudioListener:GetOutputPins(): Array` Gets the list of pins that [Wire](/docs/reference/engine/classes/Wire.md) can use in [Wire.SourceName](/docs/reference/engine/classes/Wire.md) to connect to this instance via its [Wire.SourceInstance](/docs/reference/engine/classes/Wire.md) property. For [AudioListener](/docs/reference/engine/classes/AudioListener.md), this is `Output` only. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Array` — An array of strings representing valid pin names. ### Method: AudioListener:SetAngleAttenuation **Signature:** `AudioListener:SetAngleAttenuation(curve: Dictionary): ()` Sets a volume-over-angle curve that affects how loudly a [AudioListener](/docs/reference/engine/classes/AudioListener.md) will hear the [AudioEmitter](/docs/reference/engine/classes/AudioEmitter.md), based on the angle between them and the [LookVector](/docs/reference/engine/datatypes/CFrame.md) associated with the [AudioListener](/docs/reference/engine/classes/AudioListener.md). The curve is represented by a table mapping angle keys to volume values. Keys are expected to be unique numbers between `0` and `180` (inclusive), while values are expected to be numbers between `0` and `1` (inclusive). Tables containing up to 400 key-value pairs are supported. The volume of a [AudioEmitter](/docs/reference/engine/classes/AudioEmitter.md) from the perspective of the [AudioListener](/docs/reference/engine/classes/AudioListener.md) at an angle `a` is determined by linearly interpolating between the volume levels for the points on the curve whose angle values are directly above and below `a`. If there is either no point below `a` or no point above `a`, the volume level of the other point is chosen. Essentially, the curve is a sequence of points connected by straight lines, and beyond its left and right endpoints the curve extends outward at their respective volume levels. This volume will be multiplied with the volumes from all other attenuation curves (including the ones on the sending [AudioEmitter](/docs/reference/engine/classes/AudioEmitter.md)) to obtain the final audibility. If the table is empty or `nil`, the [AudioListener](/docs/reference/engine/classes/AudioListener.md) defaults to using an angle attenuation curve with the constant volume value of `1`. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `curve` | `Dictionary` | | | **Returns:** `()` **Camera Listener** ```lua local Workspace = game:GetService("Workspace") local currentCamera = Workspace.CurrentCamera local listener = Instance.new("AudioListener") listener.Parent = currentCamera local wire = Instance.new("Wire") wire.Parent = listener local output = Instance.new("AudioDeviceOutput") output.Parent = wire listener:SetAngleAttenuation({ [0] = 1, -- Emitters directly in front of the listener will be full volume [180] = 0.5 -- Emitters directly behind the listener will be half volume }) wire.SourceInstance = listener wire.TargetInstance = output ``` ### Method: AudioListener:SetDistanceAttenuation **Signature:** `AudioListener:SetDistanceAttenuation(curve: Dictionary): ()` Sets a volume-over-distance curve that affects how loudly the [AudioListener](/docs/reference/engine/classes/AudioListener.md) will hear any [AudioEmitters](/docs/reference/engine/classes/AudioEmitter.md), based on the distance between them. The curve is represented by a table mapping distance keys to volume values. Keys are expected to be unique numbers greater than or equal to 0, while values are expected to be numbers between 0 and 1 (inclusive). Tables containing up to 400 key-value pairs are supported. The volume of a [AudioEmitter](/docs/reference/engine/classes/AudioEmitter.md) from the perspective of the [AudioListener](/docs/reference/engine/classes/AudioListener.md) at a distance `d` is determined by linearly interpolating between the volume levels for the points on the curve whose distance values are directly above and below `d`. If there is either no point below `d` or no point above `d`, the volume level of the other point is chosen. Essentially, the curve is a sequence of points connected by straight lines, and beyond its left and right endpoints the curve extends outward infinitely at their respective volume levels. This volume will be multiplied with the volumes from all other attenuation curves (including the ones on the sending [AudioEmitter](/docs/reference/engine/classes/AudioEmitter.md)) to obtain the final audibility. If the table is empty or `nil`, the [AudioListener](/docs/reference/engine/classes/AudioListener.md) defaults to applying a constant volume of 1 everywhere. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `curve` | `Dictionary` | | | **Returns:** `()` ## Events ### Event: AudioListener.WiringChanged **Signature:** `AudioListener.WiringChanged(connected: boolean, pin: string, wire: Wire, instance: Instance)` Event that fires after a [Wire](/docs/reference/engine/classes/Wire.md) becomes connected or disconnected, and that [Wire](/docs/reference/engine/classes/Wire.md) is now or was previously connected to a pin on the [AudioListener](/docs/reference/engine/classes/AudioListener.md) and to some other wirable instance. *Security: None · Capabilities: Audio* **Parameters:** | Name | Type | Description | |------|------|-------------| | `connected` | `boolean` | Whether the instance got connected or disconnected. | | `pin` | `string` | The pin on the [AudioListener](/docs/reference/engine/classes/AudioListener.md) that the [Wire](/docs/reference/engine/classes/Wire.md) targets. | | `wire` | `Wire` | The [Wire](/docs/reference/engine/classes/Wire.md) between the [AudioListener](/docs/reference/engine/classes/AudioListener.md) and the other instance. | | `instance` | `Instance` | The other instance that is or was connected through the [Wire](/docs/reference/engine/classes/Wire.md). | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AudioPages last_updated: 2026-06-29T19:34:07Z inherits: - Pages - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated summary: "A special version of the Pages class returned by AssetService:SearchAudioAsync()." --- # Class: AudioPages > A special version of the [Pages](/docs/reference/engine/classes/Pages.md) class returned by > [AssetService:SearchAudioAsync()](/docs/reference/engine/classes/AssetService.md). ## Description A special version of the [Pages](/docs/reference/engine/classes/Pages.md) class returned by [AssetService:SearchAudioAsync()](/docs/reference/engine/classes/AssetService.md). See the following code sample for reference on the returned data format. ## Code Samples **Getting Music Search Titles** This code gets the music assets returned by the keyword "happy" and prints out their titles. ```lua local AssetService = game:GetService("AssetService") local audioSearchParams = Instance.new("AudioSearchParams") audioSearchParams.SearchKeyword = "happy" local success, result = pcall(function() return AssetService:SearchAudioAsync(audioSearchParams) end) if success then local currentPage = result:GetCurrentPage() for _, audio in currentPage do print(audio.Title) end else warn("AssetService error: " .. result) end --[[ Returned data format { "AudioType": string, "Artist": string, "Title": string, "Tags": { "string" }, "Id": number, "IsEndorsed": boolean, "Description": string, "Duration": number, "CreateTime": string, "UpdateTime": string, "Creator": { "Id": number, "Name": string, "Type": number, "IsVerifiedCreator": boolean } } --]] ``` ## Inherited Members ### From [Pages](/docs/reference/engine/classes/Pages.md) - **Property `IsFinished`** (`boolean`): Whether or not the current page is the last page available. - **Method `AdvanceToNextPageAsync(): ()`**: Iterates to the next page in the pages object, if possible. - **Method `GetCurrentPage(): Array`**: Returns the items on the current page. The keys in the item are determined ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AudioPitchShifter last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Internal summary: "Adjusts the perceived pitch of audio streams." --- # Class: AudioPitchShifter > Adjusts the perceived pitch of audio streams. ## Description [AudioPitchShifter](/docs/reference/engine/classes/AudioPitchShifter.md) adjusts the perceived pitch of audio streams. It provides one **Input** pin and one **Output** pin which can be connected to/from by [Wires](/docs/reference/engine/classes/Wire.md). [AudioPitchShifter](/docs/reference/engine/classes/AudioPitchShifter.md) performs its modifications in the frequency domain and may introduce artifacts with extreme pitch changes. ## Properties ### Property: AudioPitchShifter.Bypass ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` If `true`, audio streams are passed-through unaffected by this effect. ### Property: AudioPitchShifter.Pitch ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` Pitch modification to be multiplied by the pitch of the input stream. Ranges from 0.5 to 2. ### Property: AudioPitchShifter.WindowSize ```json { "type": "AudioWindowSize", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` Controls how much audio will be buffered and shifted at once. Larger windows result in higher-quality pitch shifting, but introduce more latency. ## Methods ### Method: AudioPitchShifter:GetConnectedWires **Signature:** `AudioPitchShifter:GetConnectedWires(pin: string): List` Returns an array of [Wires](/docs/reference/engine/classes/Wire.md) that are connected to the specified pin. [AudioPitchShifter](/docs/reference/engine/classes/AudioPitchShifter.md) has one "Input" pin and one "Output" pin. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `pin` | `string` | | An input or output pin on this instance | **Returns:** `List` — An array of [Wires](/docs/reference/engine/classes/Wire.md) ### Method: AudioPitchShifter:GetInputPins **Signature:** `AudioPitchShifter:GetInputPins(): Array` Gets the list of pins that [Wire](/docs/reference/engine/classes/Wire.md) can use in [Wire.TargetName](/docs/reference/engine/classes/Wire.md) to connect to this instance via its [Wire.TargetInstance](/docs/reference/engine/classes/Wire.md) property. For [AudioPitchShifter](/docs/reference/engine/classes/AudioPitchShifter.md), this is `Input` only. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Array` — An array of strings representing valid pin names. ### Method: AudioPitchShifter:GetOutputPins **Signature:** `AudioPitchShifter:GetOutputPins(): Array` Gets the list of pins that [Wire](/docs/reference/engine/classes/Wire.md) can use in [Wire.SourceName](/docs/reference/engine/classes/Wire.md) to connect to this instance via its [Wire.SourceInstance](/docs/reference/engine/classes/Wire.md) property. For [AudioPitchShifter](/docs/reference/engine/classes/AudioPitchShifter.md), this is `Output` only. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Array` — An array of strings representing valid pin names. ## Events ### Event: AudioPitchShifter.WiringChanged **Signature:** `AudioPitchShifter.WiringChanged(connected: boolean, pin: string, wire: Wire, instance: Instance)` Event that fires after a [Wire](/docs/reference/engine/classes/Wire.md) becomes connected or disconnected, and that [Wire](/docs/reference/engine/classes/Wire.md) is now or was previously connected to a pin on the [AudioPitchShifter](/docs/reference/engine/classes/AudioPitchShifter.md) and to some other wirable instance. *Security: None · Capabilities: Audio* **Parameters:** | Name | Type | Description | |------|------|-------------| | `connected` | `boolean` | Whether the instance got connected or disconnected. | | `pin` | `string` | The pin on the [AudioPitchShifter](/docs/reference/engine/classes/AudioPitchShifter.md) that the [Wire](/docs/reference/engine/classes/Wire.md) targets. | | `wire` | `Wire` | The [Wire](/docs/reference/engine/classes/Wire.md) between the [AudioPitchShifter](/docs/reference/engine/classes/AudioPitchShifter.md) and the other instance. | | `instance` | `Instance` | The other instance that is or was connected through the [Wire](/docs/reference/engine/classes/Wire.md). | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AudioPlayer last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Internal summary: "Used to play audio assets." --- # Class: AudioPlayer > Used to play audio assets. ## Description [AudioPlayer](/docs/reference/engine/classes/AudioPlayer.md) is used to play audio assets. It provides a single **Output** pin which can be connected to other pins via [Wires](/docs/reference/engine/classes/Wire.md). ## Code Samples **Outputting Audio to Device** ```lua local audioPlayer: AudioPlayer = Instance.new("AudioPlayer") audioPlayer.Parent = workspace audioPlayer.AssetId = "rbxassetid://9112854440" local deviceOutput = Instance.new("AudioDeviceOutput") deviceOutput.Parent = workspace local wire = Instance.new("Wire") wire.Parent = workspace wire.SourceInstance = audioPlayer wire.TargetInstance = deviceOutput audioPlayer:Play() ``` ## Properties ### Property: AudioPlayer.Asset ```json { "type": "ContentId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Asset", "capabilities": [ "Audio" ] } ``` The asset to be loaded into the [AudioPlayer](/docs/reference/engine/classes/AudioPlayer.md). If [AutoLoad](/docs/reference/engine/classes/AudioPlayer.md) is `true`, the asset loads immediately once this property is assigned. When loading is complete, [IsReady](/docs/reference/engine/classes/AudioPlayer.md) becomes `true`. ### Property: AudioPlayer.AutoLoad ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Asset", "capabilities": [ "Audio" ] } ``` Controls whether [Asset](/docs/reference/engine/classes/AudioPlayer.md) loads automatically once assigned. If `false`, the asset will load upon the first attempt to play. ### Property: AudioPlayer.AutoPlay ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Playback", "capabilities": [ "Audio" ] } ``` Denotes whether this [AudioPlayer](/docs/reference/engine/classes/AudioPlayer.md) starts playing as soon as it enters the [DataModel](/docs/reference/engine/classes/DataModel.md) for the first time. This only applies to [AudioPlayers](/docs/reference/engine/classes/AudioPlayer.md) that are created or deserialized locally, and does not apply to [AudioPlayers](/docs/reference/engine/classes/AudioPlayer.md) generated via replication. This property is primarily used at edit time in Studio to have an [AudioPlayer](/docs/reference/engine/classes/AudioPlayer.md) begin when entering a play session. ### Property: AudioPlayer.IsPlaying ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "None", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Playback", "capabilities": [ "Audio" ] } ``` Denotes whether this [AudioPlayer](/docs/reference/engine/classes/AudioPlayer.md) is currently playing or planning to play. This property is read-only, but replicates. To play and stop an [AudioPlayer](/docs/reference/engine/classes/AudioPlayer.md) at runtime, use the [Play()](/docs/reference/engine/classes/AudioPlayer.md) and [Stop()](/docs/reference/engine/classes/AudioPlayer.md) methods. ### Property: AudioPlayer.IsReady ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Asset", "capabilities": [ "Audio" ] } ``` Denotes whether this [AudioPlayer](/docs/reference/engine/classes/AudioPlayer.md) is loaded, buffered, and ready to play. Although uncommon, [AudioPlayers](/docs/reference/engine/classes/AudioPlayer.md) may have their assets unloaded at runtime if there is extreme memory pressure, in which case [IsReady](/docs/reference/engine/classes/AudioPlayer.md) will become false. ### Property: AudioPlayer.Looping ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Playback", "capabilities": [ "Audio" ] } ``` Controls whether this [AudioPlayer](/docs/reference/engine/classes/AudioPlayer.md) loops when exceeding the end of its [TimeLength](/docs/reference/engine/classes/AudioPlayer.md), [LoopRegion](/docs/reference/engine/classes/AudioPlayer.md), or [PlaybackRegion](/docs/reference/engine/classes/AudioPlayer.md). ### Property: AudioPlayer.LoopRegion ```json { "type": "NumberRange", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Regions", "capabilities": [ "Audio" ] } ``` A range, in seconds, denoting a desired loop start and loop end within the [PlaybackRegion](/docs/reference/engine/classes/AudioPlayer.md) of this [AudioPlayer](/docs/reference/engine/classes/AudioPlayer.md). If the [LoopRegion](/docs/reference/engine/classes/AudioPlayer.md) minimum is **greater** than the [PlaybackRegion](/docs/reference/engine/classes/AudioPlayer.md) minimum, the loop starts from the [LoopRegion](/docs/reference/engine/classes/AudioPlayer.md) minimum. If the [LoopRegion](/docs/reference/engine/classes/AudioPlayer.md) minimum is **less** than the [PlaybackRegion](/docs/reference/engine/classes/AudioPlayer.md) minimum, the loop starts from the [PlaybackRegion](/docs/reference/engine/classes/AudioPlayer.md) minimum. If the [LoopRegion](/docs/reference/engine/classes/AudioPlayer.md) maximum is **greater** than the [PlaybackRegion](/docs/reference/engine/classes/AudioPlayer.md) maximum, the loop ends at the [PlaybackRegion](/docs/reference/engine/classes/AudioPlayer.md) maximum. If the [LoopRegion](/docs/reference/engine/classes/AudioPlayer.md) maximum is **less** than the [PlaybackRegion](/docs/reference/engine/classes/AudioPlayer.md) maximum, the loop ends at exactly the [LoopRegion](/docs/reference/engine/classes/AudioPlayer.md) maximum. If the [LoopRegion](/docs/reference/engine/classes/AudioPlayer.md) minimum **equals** the [LoopRegion](/docs/reference/engine/classes/AudioPlayer.md) maximum, the [AudioPlayer](/docs/reference/engine/classes/AudioPlayer.md) uses the [PlaybackRegion](/docs/reference/engine/classes/AudioPlayer.md) property instead. ### Property: AudioPlayer.PlaybackRegion ```json { "type": "NumberRange", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Regions", "capabilities": [ "Audio" ] } ``` Range in seconds denoting a desired start time (minimum) and stop time (maximum) within the [TimeLength](/docs/reference/engine/classes/AudioPlayer.md). If the [PlaybackRegion](/docs/reference/engine/classes/AudioPlayer.md) minimum is **greater** than 0, the sound begins playing from the [PlaybackRegion](/docs/reference/engine/classes/AudioPlayer.md) minimum time. If the [PlaybackRegion](/docs/reference/engine/classes/AudioPlayer.md) minimum is **less** than 0, the sound begins playing from 0. If the [PlaybackRegion](/docs/reference/engine/classes/AudioPlayer.md) maximum is **greater** than the [TimeLength](/docs/reference/engine/classes/AudioPlayer.md), the sound stops at [TimeLength](/docs/reference/engine/classes/AudioPlayer.md). If the [PlaybackRegion](/docs/reference/engine/classes/AudioPlayer.md) maximum is **less** than the [TimeLength](/docs/reference/engine/classes/AudioPlayer.md), the sound stops at exactly the [PlaybackRegion](/docs/reference/engine/classes/AudioPlayer.md) maximum. If the [PlaybackRegion](/docs/reference/engine/classes/AudioPlayer.md) minimum **equals** the [PlaybackRegion](/docs/reference/engine/classes/AudioPlayer.md) maximum, the sound plays in its entirety. ### Property: AudioPlayer.PlaybackSpeed ```json { "type": "double", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Playback", "capabilities": [ "Audio" ] } ``` Multiplier controlling how quickly the asset will be played, directly controlling its perceived pitch. Ranges from 0 to 20. ### Property: AudioPlayer.TimeLength ```json { "type": "double", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Asset", "capabilities": [ "Audio" ] } ``` Denotes the length of the loaded [Asset](/docs/reference/engine/classes/AudioPlayer.md) in seconds. ### Property: AudioPlayer.TimePosition ```json { "type": "double", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Playback", "capabilities": [ "Audio" ] } ``` Tracks and controls the current position of the playhead within the [Asset](/docs/reference/engine/classes/AudioPlayer.md), in seconds. ### Property: AudioPlayer.Volume ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` Volume level which is multiplied onto the output audio stream, controlling how loudly the asset will be played. Ranges from 0 to 10. ### Property: AudioPlayer.AssetId *(hidden)* ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Asset", "capabilities": [ "Audio" ] } ``` > **Deprecated:** This property is deprecated; use [Asset](/docs/reference/engine/classes/AudioPlayer.md) instead. The asset to be loaded into the [AudioPlayer](/docs/reference/engine/classes/AudioPlayer.md). If [AutoLoad](/docs/reference/engine/classes/AudioPlayer.md) is `true`, the asset loads immediately once this property is assigned. When loading is complete, [IsReady](/docs/reference/engine/classes/AudioPlayer.md) becomes `true`. ### Property: AudioPlayer.AudioContent *(hidden)* ```json { "type": "Content", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Asset", "capabilities": [ "Audio" ] } ``` The audio content to be loaded into the [AudioPlayer](/docs/reference/engine/classes/AudioPlayer.md). If [AutoLoad](/docs/reference/engine/classes/AudioPlayer.md) is `true`, the asset loads immediately once this property is assigned. When loading is complete, [IsReady](/docs/reference/engine/classes/AudioPlayer.md) becomes `true`. ## Methods ### Method: AudioPlayer:Cancel **Signature:** `AudioPlayer:Cancel(actionId: int64?): boolean` Attempts to cancel a pre-planned future `Play` or `Stop` command. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `actionId` | `int64?` | | The unique-ID of a pre-planned `Play` or `Stop` command. | **Returns:** `boolean` — Whether the cancellation was successful. Returns false if the action has already occurred, or otherwise does not exist. ### Method: AudioPlayer:GetConnectedWires **Signature:** `AudioPlayer:GetConnectedWires(pin: string): List` Returns an array of [Wires](/docs/reference/engine/classes/Wire.md) that are connected to the specified pin. [AudioPlayer](/docs/reference/engine/classes/AudioPlayer.md) has one "Output" pin. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `pin` | `string` | | An input or output pin on this instance | **Returns:** `List` — An array of [Wires](/docs/reference/engine/classes/Wire.md) ### Method: AudioPlayer:GetInputPins **Signature:** `AudioPlayer:GetInputPins(): Array` Gets the list of pins that [Wire](/docs/reference/engine/classes/Wire.md) can use in [Wire.TargetName](/docs/reference/engine/classes/Wire.md) to connect to this instance via its [Wire.TargetInstance](/docs/reference/engine/classes/Wire.md) property. For [AudioPlayer](/docs/reference/engine/classes/AudioPlayer.md), there are none. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Array` — An array of strings representing valid pin names. ### Method: AudioPlayer:GetOutputPins **Signature:** `AudioPlayer:GetOutputPins(): Array` Gets the list of pins that [Wire](/docs/reference/engine/classes/Wire.md) can use in [Wire.SourceName](/docs/reference/engine/classes/Wire.md) to connect to this instance via its [Wire.SourceInstance](/docs/reference/engine/classes/Wire.md) property. For [AudioPlayer](/docs/reference/engine/classes/AudioPlayer.md), this is `Output` only. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Array` — An array of strings representing valid pin names. ### Method: AudioPlayer:GetWaveformAsync **Signature:** `AudioPlayer:GetWaveformAsync(timeRange: NumberRange, samples: int): Array` Returns a sampling of the waveform data for the loaded [Asset](/docs/reference/engine/classes/AudioPlayer.md). This can be used to check the volume of an asset over the course of its duration without playing it. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `timeRange` | `NumberRange` | | The start and end time (in seconds) of the segment to read. | | `samples` | `int` | | The number of samples to return for the specified range. | **Returns:** `Array` — A table of `samples` numbers ranging between -1 and 1 representing the sampled waveform, or an empty table if a waveform could not be read. ### Method: AudioPlayer:Play **Signature:** `AudioPlayer:Play(atTime: double?): int64?` Plays the [AudioPlayer](/docs/reference/engine/classes/AudioPlayer.md) from wherever its [TimePosition](/docs/reference/engine/classes/AudioPlayer.md) is. Replicates from server to client. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `atTime` | `double?` | | A specific time, based on [GetMixerTime](/docs/reference/engine/classes/SoundService.md), that this `AudioPlayer` should begin playing at. | **Returns:** `int64?` — If `atTime` was provided, a unique ID, which can be passed to [Cancel()](/docs/reference/engine/classes/AudioPlayer.md). ### Method: AudioPlayer:Stop **Signature:** `AudioPlayer:Stop(atTime: double?): int64?` Stops the [AudioPlayer](/docs/reference/engine/classes/AudioPlayer.md) wherever its [TimePosition](/docs/reference/engine/classes/AudioPlayer.md) is. Replicates from server to client. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `atTime` | `double?` | | A specific time, based on [GetMixerTime](/docs/reference/engine/classes/SoundService.md), that this `AudioPlayer` should stop playing at. | **Returns:** `int64?` — If `atTime` was provided, a unique ID, which can be passed to [Cancel()](/docs/reference/engine/classes/AudioPlayer.md). ## Events ### Event: AudioPlayer.Ended **Signature:** `AudioPlayer.Ended()` Fires after the [AudioPlayer](/docs/reference/engine/classes/AudioPlayer.md) has completed playback and stopped. Note this event will **not** fire for audio with [Looping](/docs/reference/engine/classes/AudioPlayer.md) set to `true` since it continues playing upon reaching its end. This event will also **not** fire when the audio is stopped before playback has completed; for this, use [AudioPlayer:GetPropertyChangedSignal()](/docs/reference/engine/classes/AudioPlayer.md) on the [IsPlaying](/docs/reference/engine/classes/AudioPlayer.md) property. This event is often used to destroy an [AudioPlayer](/docs/reference/engine/classes/AudioPlayer.md) when it has completed playback. *Security: None · Capabilities: Audio* ### Event: AudioPlayer.Looped **Signature:** `AudioPlayer.Looped()` Event that fires after the [AudioPlayer](/docs/reference/engine/classes/AudioPlayer.md) loops. This happens when the audio reaches the end of its content (or the end of the [LoopRegion](/docs/reference/engine/classes/AudioPlayer.md) if it is active) and [Looping](/docs/reference/engine/classes/AudioPlayer.md) is `true`. This event does **not** fire if the audio is looped manually by changing its [TimePosition](/docs/reference/engine/classes/AudioPlayer.md). *Security: None · Capabilities: Audio* ### Event: AudioPlayer.WiringChanged **Signature:** `AudioPlayer.WiringChanged(connected: boolean, pin: string, wire: Wire, instance: Instance)` Event that fires after a [Wire](/docs/reference/engine/classes/Wire.md) becomes connected or disconnected, and that [Wire](/docs/reference/engine/classes/Wire.md) is now or was previously connected to a pin on the [AudioPlayer](/docs/reference/engine/classes/AudioPlayer.md) and to some other wirable instance. *Security: None · Capabilities: Audio* **Parameters:** | Name | Type | Description | |------|------|-------------| | `connected` | `boolean` | Whether the instance got connected or disconnected. | | `pin` | `string` | The pin on the [AudioPlayer](/docs/reference/engine/classes/AudioPlayer.md) that the [Wire](/docs/reference/engine/classes/Wire.md) targets. | | `wire` | `Wire` | The [Wire](/docs/reference/engine/classes/Wire.md) between the [AudioPlayer](/docs/reference/engine/classes/AudioPlayer.md) and the other instance. | | `instance` | `Instance` | The other instance that is or was connected through the [Wire](/docs/reference/engine/classes/Wire.md). | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AudioRecorder last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Internal tags: - NotBrowsable summary: "Records audio streams in-experience." --- # Class: AudioRecorder > Records audio streams in-experience. ## Description **This API is in development and is not available yet.** `AudioRecorder` records audio streams in-experience with a fixed time limit of 60 seconds. The results can be loaded into an [AudioPlayer](/docs/reference/engine/classes/AudioPlayer.md) for playback. At this time, [AudioDeviceInput](/docs/reference/engine/classes/AudioDeviceInput.md) cannot be recorded. The [GetUnrecordableInstancesAsync()](/docs/reference/engine/classes/AudioRecorder.md) method can be used to check specifically which instances aren't recordable. ## Code Samples **Audio Recorder** ```lua local Workspace = game:GetService("Workspace") local audioRecorder = Instance.new("AudioRecorder") audioRecorder.Parent = Workspace local audioPlayer = Instance.new("AudioPlayer") audioPlayer.Asset = "rbxassetid://5829815715" audioPlayer.Volume = 0.8 audioPlayer.Parent = Workspace -- Wire AudioPlayer into the AudioRecorder local wire1 = Instance.new("Wire") wire1.SourceInstance = audioPlayer wire1.TargetInstance = audioRecorder wire1.Parent = audioRecorder -- There is no exact way to determine when audio buffer enters in to trigger the recording properly -- Recording will have pre-head empty silence compared to the original asset audioPlayer:Play() audioRecorder:RecordAsync() -- Start recording the AudioPlayer print("Recording...") task.wait(5) audioRecorder:Stop() -- Stop recording print("Stopped recording!") audioPlayer:Stop() audioPlayer.TimePosition = 0 -- Create output to listen the results local audioOutput = Instance.new("AudioDeviceOutput") audioOutput.Parent = Workspace local wire2 = Instance.new("Wire") wire2.SourceInstance = audioPlayer wire2.TargetInstance = audioOutput wire2.Parent = audioOutput -- Get the recorded content and play it in the AudioPlayer local resultUri = audioRecorder:GetTemporaryContent().Uri audioPlayer.Asset = resultUri if not audioPlayer.IsReady then audioPlayer:GetPropertyChangedSignal("IsReady"):Wait() end audioPlayer:Play() ``` ## Properties ### Property: AudioRecorder.IsRecording ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "None", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` Returns whether the `AudioRecorder` is currently recording. ### Property: AudioRecorder.TimeLength ```json { "type": "double", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Audio" ] } ``` Returns the current length of the recording in seconds. ## Methods ### Method: AudioRecorder:CanRecordAsync **Signature:** `AudioRecorder:CanRecordAsync(): boolean` Returns whether the `AudioRecorder` can currently record. For instance, this will return `false` if the current recording data has reached the recording time limit. To clear the recording, use [Clear()](/docs/reference/engine/classes/AudioRecorder.md). *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `boolean` — `true` if recording is possible, `false` otherwise. ### Method: AudioRecorder:Clear **Signature:** `AudioRecorder:Clear(): ()` Clears out the recording from the `AudioRecorder`. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `()` ### Method: AudioRecorder:GetConnectedWires **Signature:** `AudioRecorder:GetConnectedWires(pin: string): List` Returns an array of [Wires](/docs/reference/engine/classes/Wire.md) that are connected to the specified pin. [AudioRecorder](/docs/reference/engine/classes/AudioRecorder.md) has one "Input" pin. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `pin` | `string` | | An input or output pin on this instance | **Returns:** `List` — An array of [Wires](/docs/reference/engine/classes/Wire.md) ### Method: AudioRecorder:GetInputPins **Signature:** `AudioRecorder:GetInputPins(): Array` Gets the list of pins that [Wire](/docs/reference/engine/classes/Wire.md) can use in [Wire.TargetName](/docs/reference/engine/classes/Wire.md) to connect to this instance via its [Wire.TargetInstance](/docs/reference/engine/classes/Wire.md) property. For [AudioRecorder](/docs/reference/engine/classes/AudioRecorder.md), this is `Input` only. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Array` — An array of strings representing valid pin names. ### Method: AudioRecorder:GetOutputPins **Signature:** `AudioRecorder:GetOutputPins(): Array` Gets the list of pins that [Wire](/docs/reference/engine/classes/Wire.md) can use in [Wire.SourceName](/docs/reference/engine/classes/Wire.md) to connect to this instance via its [Wire.SourceInstance](/docs/reference/engine/classes/Wire.md) property. For [AudioRecorder](/docs/reference/engine/classes/AudioRecorder.md), there are none. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Array` — An array of strings representing valid pin names. ### Method: AudioRecorder:GetTemporaryContent **Signature:** `AudioRecorder:GetTemporaryContent(): Content` Returns recorded content that can be played back with [AudioPlayer](/docs/reference/engine/classes/AudioPlayer.md). The content retrieved from this method is only valid in the current session. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Content` — A temporary `Content` ID that can be used with [AudioPlayer](/docs/reference/engine/classes/AudioPlayer.md). ### Method: AudioRecorder:GetUnrecordableInstancesAsync **Signature:** `AudioRecorder:GetUnrecordableInstancesAsync(): List` Traverses the audio graph, starting from this recorder's inputs, to find unrecordable instances. Currently, [AudioDeviceInput](/docs/reference/engine/classes/AudioDeviceInput.md) is not recordable. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `List` — A table of [Instances](/docs/reference/engine/classes/Instance.md) that are not recordable. ### Method: AudioRecorder:RecordAsync **Signature:** `AudioRecorder:RecordAsync(): ()` Starts recording audio. If [CanRecordAsync()](/docs/reference/engine/classes/AudioRecorder.md) returns `true`, recording begins. If recording cannot begin, this method produces an error. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `()` ### Method: AudioRecorder:Stop **Signature:** `AudioRecorder:Stop(): ()` Stops recording audio. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `()` ## Events ### Event: AudioRecorder.WiringChanged **Signature:** `AudioRecorder.WiringChanged(connected: boolean, pin: string, wire: Wire, instance: Instance)` Event that fires after a [Wire](/docs/reference/engine/classes/Wire.md) becomes connected or disconnected, and that [Wire](/docs/reference/engine/classes/Wire.md) is now or was previously connected to a pin on the [AudioRecorder](/docs/reference/engine/classes/AudioRecorder.md) and to some other wirable instance. *Security: None · Capabilities: Audio* **Parameters:** | Name | Type | Description | |------|------|-------------| | `connected` | `boolean` | Whether the instance got connected or disconnected. | | `pin` | `string` | The pin on the [AudioRecorder](/docs/reference/engine/classes/AudioRecorder.md) that the [Wire](/docs/reference/engine/classes/Wire.md) targets. | | `wire` | `Wire` | The [Wire](/docs/reference/engine/classes/Wire.md) between the [AudioRecorder](/docs/reference/engine/classes/AudioRecorder.md) and the other instance. | | `instance` | `Instance` | The other instance that is or was connected through the [Wire](/docs/reference/engine/classes/Wire.md). | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AudioReverb last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Internal summary: "Reverberates audio streams." --- # Class: AudioReverb > Reverberates audio streams. ## Description [AudioReverb](/docs/reference/engine/classes/AudioReverb.md) reverberates audio streams, modeling the natural echoes of a room or enclosed space. It provides one **Input** pin and one **Output** pin which can be connected to/from by [Wires](/docs/reference/engine/classes/Wire.md). ## Properties ### Property: AudioReverb.Bypass ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` If `true`, audio streams are passed-through unaffected by this effect. ### Property: AudioReverb.DecayRatio ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` A ratio of high frequency decay time to total decay time, where high frequencies are determined to be anything above [ReferenceFrequency](/docs/reference/engine/classes/AudioReverb.md), in hertz. Ranges from 0.1 to 1. ### Property: AudioReverb.DecayTime ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` Time, in seconds, that it will take for the reverb to fully decay. Ranges from 0.1 to 20. ### Property: AudioReverb.Density ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` A number controlling how many reflections are generated. Ranges from 0.1 to 1. ### Property: AudioReverb.Diffusion ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` A number controlling how smooth and reflective the reverb's simulated surfaces are. Ranges from 0.1 to 1. ### Property: AudioReverb.DryLevel ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` Gain level, in decibels, determining how loud the original, unaltered audio stream will be. Ranges from -80 to 20. ### Property: AudioReverb.EarlyDelayTime ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` Time, in seconds, before any reverberation begins. Ranges from 0 to 0.3. ### Property: AudioReverb.HighCutFrequency ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` Frequency, in hertz, which acts as a cutoff for a low-pass filter. Any audio that has a higher frequency than this is excluded from the reverb. Ranges from 20 to 20,000. ### Property: AudioReverb.LateDelayTime ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` Time in seconds, following early delays, before diffuse reverberations begin. Ranges from 0 to 0.1. ### Property: AudioReverb.LowShelfFrequency ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` Frequency, in hertz, below which audio can be boosted or reduced in the reverb via [LowShelfGain](/docs/reference/engine/classes/AudioReverb.md). Ranges from 20 to 20,000. ### Property: AudioReverb.LowShelfGain ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` Gain level, in decibels, that controls the presence of low frequency content in the reverb. Ranges from -36 to 12. ### Property: AudioReverb.ReferenceFrequency ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` Frequency, in hertz, that separates low frequency decay speeds from high frequency decay speeds. This is used by [DecayRatio](/docs/reference/engine/classes/AudioReverb.md) to determine whether low or high frequencies decay faster. Ranges from 20 to 20,000. ### Property: AudioReverb.WetLevel ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` Gain level, in decibels, determining how loud the reverberated stream will be. Ranges from -80 to 20. ## Methods ### Method: AudioReverb:GetConnectedWires **Signature:** `AudioReverb:GetConnectedWires(pin: string): List` Returns an array of [Wires](/docs/reference/engine/classes/Wire.md) that are connected to the specified pin. [AudioReverb](/docs/reference/engine/classes/AudioReverb.md) has one "Input" pin and one "Output" pin. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `pin` | `string` | | An input or output pin on this instance | **Returns:** `List` — An array of [Wires](/docs/reference/engine/classes/Wire.md) ### Method: AudioReverb:GetInputPins **Signature:** `AudioReverb:GetInputPins(): Array` Gets the list of pins that [Wire](/docs/reference/engine/classes/Wire.md) can use in [Wire.TargetName](/docs/reference/engine/classes/Wire.md) to connect to this instance via its [Wire.TargetInstance](/docs/reference/engine/classes/Wire.md) property. For [AudioReverb](/docs/reference/engine/classes/AudioReverb.md), this is `Input` only. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Array` — An array of strings representing valid pin names. ### Method: AudioReverb:GetOutputPins **Signature:** `AudioReverb:GetOutputPins(): Array` Gets the list of pins that [Wire](/docs/reference/engine/classes/Wire.md) can use in [Wire.SourceName](/docs/reference/engine/classes/Wire.md) to connect to this instance via its [Wire.SourceInstance](/docs/reference/engine/classes/Wire.md) property. For [AudioReverb](/docs/reference/engine/classes/AudioReverb.md), this is `Output` only. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Array` — An array of strings representing valid pin names. ## Events ### Event: AudioReverb.WiringChanged **Signature:** `AudioReverb.WiringChanged(connected: boolean, pin: string, wire: Wire, instance: Instance)` Event that fires after a [Wire](/docs/reference/engine/classes/Wire.md) becomes connected or disconnected, and that [Wire](/docs/reference/engine/classes/Wire.md) is now or was previously connected to a pin on the [AudioReverb](/docs/reference/engine/classes/AudioReverb.md) and to some other wirable instance. *Security: None · Capabilities: Audio* **Parameters:** | Name | Type | Description | |------|------|-------------| | `connected` | `boolean` | Whether the instance got connected or disconnected. | | `pin` | `string` | The pin on the [AudioReverb](/docs/reference/engine/classes/AudioReverb.md) that the [Wire](/docs/reference/engine/classes/Wire.md) targets. | | `wire` | `Wire` | The [Wire](/docs/reference/engine/classes/Wire.md) between the [AudioReverb](/docs/reference/engine/classes/AudioReverb.md) and the other instance. | | `instance` | `Instance` | The other instance that is or was connected through the [Wire](/docs/reference/engine/classes/Wire.md). | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AudioSearchParams last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotReplicated summary: "Instance to be passed to AssetService:SearchAudioAsync() to search for audio assets." --- # Class: AudioSearchParams > Instance to be passed to [AssetService:SearchAudioAsync()](/docs/reference/engine/classes/AssetService.md) to search for > audio assets. ## Properties ### Property: AudioSearchParams.Album ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AssetRead" ] } ``` Searches for audio which belongs to this album. Needs to be an exact match. ### Property: AudioSearchParams.Artist ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AssetRead" ] } ``` Searches for audio which was created by this artist. Needs to be an exact match. ### Property: AudioSearchParams.AudioSubType ```json { "type": "AudioSubType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AssetRead" ] } ``` Searches for audio with this subtype, corresponding to the [AudioSubType](/docs/reference/engine/enums/AudioSubType.md) enum. Defaults to [AudioSubType.Music](/docs/reference/engine/enums/AudioSubType.md). ### Property: AudioSearchParams.MaxDuration ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AssetRead" ] } ``` Searches for audio with a duration less than or equal to this value. ### Property: AudioSearchParams.MinDuration ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AssetRead" ] } ``` Searches for audio with a duration greater than or equal to this value. ### Property: AudioSearchParams.SearchKeyword ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AssetRead" ] } ``` Searches for audio relevant to this keyword. ### Property: AudioSearchParams.Tag ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AssetRead" ] } ``` Searches for audio which has this tag. Needs to be an exact match. ### Property: AudioSearchParams.Title ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AssetRead" ] } ``` Searches for audio with this title. Needs to be an exact match. ### Property: AudioSearchParams.AudioSubtype ```json { "type": "AudioSubType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AssetRead" ] } ``` Searches for audio with this subtype, corresponding to the [AudioSubType](/docs/reference/engine/enums/AudioSubType.md) enum. Defaults to [AudioSubType.Music](/docs/reference/engine/enums/AudioSubType.md). ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AudioSpeechToText last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Internal summary: "Converts spoken audio into text." --- # Class: AudioSpeechToText > Converts spoken audio into text. ## Description [AudioSpeechToText](/docs/reference/engine/classes/AudioSpeechToText.md) is used to convert speech audio into text. This class provides a single **Input** pin that can be connected to other pins via [Wires](/docs/reference/engine/classes/Wire.md). Roblox uses the following formula to throttle requests for this API based on the number of players in your experience: `max requests per minute per experience = 1 + (5 * number_of_concurrent_users)`. You can purchase additional usage using [Extended Services](/docs/en-us/cloud-services/extended-services.md). For more information, see [Speech-to-text](/docs/en-us/audio/objects.md#speech-to-text). ## Code Samples **Convert speech to text** On character spawn, adds an [AudioSpeechToText](/docs/reference/engine/classes/AudioSpeechToText.md) instance and puts its output into a new [TextLabel](/docs/reference/engine/classes/TextLabel.md). This script should be used in a [LocalScript](/docs/reference/engine/classes/LocalScript.md) or a [Script](/docs/reference/engine/classes/Script.md) with [RunContext](/docs/reference/engine/classes/BaseScript.md) set to [RunContext.Client](/docs/reference/engine/enums/RunContext.md). ```lua local players = game:GetService("Players") local function connect(src: Instance, dst: Instance) local wire = Instance.new("Wire", dst) wire.SourceInstance = src wire.TargetInstance = dst end local textLabel = nil local stt = nil local function onCharacterSpawned(player: Player, character: Model) local input = Instance.new("AudioDeviceInput", player) input.Player = player local audioSpeechToText = Instance.new("AudioSpeechToText", character) audioSpeechToText.Enabled = true connect(player.AudioDeviceInput, audioSpeechToText) local screenGui = Instance.new("ScreenGui", player.PlayerGui) textLabel = Instance.new("TextLabel", screenGui) textLabel.Text = "Hello!" textLabel.Size = UDim2.fromOffset(160,90) textLabel.Position = UDim2.fromOffset(20,20) textLabel.BackgroundTransparency = 0 textLabel.FontFace.Weight = Enum.FontWeight.Bold textLabel.TextColor3 = Color3.new(1,1,1) textLabel.TextStrokeColor3 = Color3.new(0,0,0) textLabel.TextStrokeTransparency = 0.5 audioSpeechToText:GetPropertyChangedSignal("Text"):Connect(function() if audioSpeechToText.Text ~= '' then print(audioSpeechToText.Text) textLabel.Text = audioSpeechToText.Text audioSpeechToText.Text = '' end end) stt = audioSpeechToText end local function onPlayerAdded(player: Player) if player.Character then onCharacterSpawned(player, player.Character) end player.CharacterAdded:Connect(function() onCharacterSpawned(player, player.Character) end) end players.PlayerAdded:Connect(onPlayerAdded) for _, player in players:GetPlayers() do onPlayerAdded(player) end local vad = false while true do wait(0.1) if stt and textLabel then if(stt.VoiceDetected ~= vad) then vad = stt.VoiceDetected print(vad) if(vad) then textLabel.BackgroundColor3 = Color3.new(0.7,0,0) else textLabel.BackgroundColor3 = Color3.new(0,0,0) end end end end ``` ## Properties ### Property: AudioSpeechToText.Enabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` Whether the [AudioSpeechToText](/docs/reference/engine/classes/AudioSpeechToText.md) object is enabled for processing input audio into text. ### Property: AudioSpeechToText.Text ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` The text resulting from the conversion of speech audio. ### Property: AudioSpeechToText.VoiceDetected ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` Whether the [AudioSpeechToText](/docs/reference/engine/classes/AudioSpeechToText.md) object is detecting speech in the incoming audio signal. [AudioSpeechToText](/docs/reference/engine/classes/AudioSpeechToText.md) records the speech audio until it ceases or until a maximum amount of 10 seconds has been recorded. Then it converts the recorded audio into text. ## Methods ### Method: AudioSpeechToText:GetConnectedWires **Signature:** `AudioSpeechToText:GetConnectedWires(pin: string): List` Returns an array of [Wires](/docs/reference/engine/classes/Wire.md) that are connected to the specified pin. [AudioSpeechToText](/docs/reference/engine/classes/AudioSpeechToText.md) has one "Input" pin. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `pin` | `string` | | An input or output pin on this instance | **Returns:** `List` — An array of [Wires](/docs/reference/engine/classes/Wire.md) ## Events ### Event: AudioSpeechToText.WiringChanged **Signature:** `AudioSpeechToText.WiringChanged(connected: boolean, pin: string, wire: Wire, instance: Instance)` Event that fires after a [Wire](/docs/reference/engine/classes/Wire.md) becomes connected or disconnected, and that [Wire](/docs/reference/engine/classes/Wire.md) is now or was previously connected to a pin on the [AudioSpeechToText](/docs/reference/engine/classes/AudioSpeechToText.md) and to some other wirable instance. *Security: None · Capabilities: Audio* **Parameters:** | Name | Type | Description | |------|------|-------------| | `connected` | `boolean` | Whether the instance got connected or disconnected. | | `pin` | `string` | The pin on the [AudioSpeechToText](/docs/reference/engine/classes/AudioSpeechToText.md) that the [Wire](/docs/reference/engine/classes/Wire.md) targets. | | `wire` | `Wire` | The [Wire](/docs/reference/engine/classes/Wire.md) between the [AudioSpeechToText](/docs/reference/engine/classes/AudioSpeechToText.md) and the other instance. | | `instance` | `Instance` | The other instance that is or was connected through the [Wire](/docs/reference/engine/classes/Wire.md). | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AudioTextToSpeech last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Internal summary: "Plays text as speech audio." --- # Class: AudioTextToSpeech > Plays text as speech audio. ## Description [AudioTextToSpeech](/docs/reference/engine/classes/AudioTextToSpeech.md) is used to play text as speech audio. It provides a single **Output** pin which can be connected to other pins via [Wires](/docs/reference/engine/classes/Wire.md). Roblox uses the following formula to throttle requests for this API based on the number of players in your experience: `max requests per minute per experience = 1 + (6 * number_of_concurrent_users)`. You can purchase additional usage using [Extended Services](/docs/en-us/cloud-services/extended-services.md). For a more in-depth look, see [Text-to-speech](/docs/en-us/audio/objects.md#text-to-speech). ## Code Samples **Outputting Text as Speech** ```lua local audioTextToSpeech: AudioTextToSpeech = Instance.new("AudioTextToSpeech") audioTextToSpeech.Parent = workspace audioTextToSpeech.Text = "Hello! Converting text into speech is fun!" audioTextToSpeech.VoiceId = "1" local deviceOutput = Instance.new("AudioDeviceOutput") deviceOutput.Parent = workspace local wire = Instance.new("Wire") wire.Parent = workspace wire.SourceInstance = audioTextToSpeech wire.TargetInstance = deviceOutput local count = 0 local connection = nil connection = audioTextToSpeech.Ended:Connect(function() audioTextToSpeech.Text = "I can count to " .. count .. " because I am very smart" audioTextToSpeech.VoiceId = "2" audioTextToSpeech.TimePosition = 0 audioTextToSpeech:Play() count += 1 if count > 10 then connection:Disconnect() end end) audioTextToSpeech:Play() ``` ## Properties ### Property: AudioTextToSpeech.IsLoaded ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Asset", "capabilities": [ "Audio" ] } ``` Denotes whether the [AudioTextToSpeech](/docs/reference/engine/classes/AudioTextToSpeech.md) object is loaded, buffered, and ready to play. Although uncommon, [AudioTextToSpeech](/docs/reference/engine/classes/AudioTextToSpeech.md) objects may have their assets unloaded at runtime if there is extreme memory pressure, in which case [IsLoaded](/docs/reference/engine/classes/AudioTextToSpeech.md) will become false. ### Property: AudioTextToSpeech.IsPlaying ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "None", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Playback", "capabilities": [ "Audio" ] } ``` Denotes whether the [AudioTextToSpeech](/docs/reference/engine/classes/AudioTextToSpeech.md) object is currently playing. This property is read-only, but replicates. To play and stop an [AudioTextToSpeech](/docs/reference/engine/classes/AudioTextToSpeech.md) object at runtime, use the [Play()](/docs/reference/engine/classes/AudioTextToSpeech.md) and [Pause()](/docs/reference/engine/classes/AudioTextToSpeech.md) methods. ### Property: AudioTextToSpeech.Looping ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Playback", "capabilities": [ "Audio" ] } ``` Controls whether the [AudioTextToSpeech](/docs/reference/engine/classes/AudioTextToSpeech.md) object loops when exceeding the end of its [TimeLength](/docs/reference/engine/classes/AudioTextToSpeech.md). ### Property: AudioTextToSpeech.Pitch ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Asset", "capabilities": [ "Audio" ] } ``` A value in musical semitones. The pitch of the generated speech audio is shifted from its default value by `AudioTextToSpeech.Pitch` semitones. Ranges from -12.0 to 12.0. ### Property: AudioTextToSpeech.PlaybackSpeed ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Playback", "capabilities": [ "Audio" ] } ``` Multiplier controlling how quickly the speech audio will be played, directly controlling its perceived pitch. Ranges from 0 to 20. ### Property: AudioTextToSpeech.Speed ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Asset", "capabilities": [ "Audio" ] } ``` Multiplier controlling the speed of the generated speech audio. Ranges from 0.5 to 2.0. ### Property: AudioTextToSpeech.Text ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Asset", "capabilities": [ "Audio" ] } ``` The text to be converted into speech audio by [AudioTextToSpeech](/docs/reference/engine/classes/AudioTextToSpeech.md). The text has a maximum length of 300 characters. ### Property: AudioTextToSpeech.TimeLength ```json { "type": "double", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Asset", "capabilities": [ "Audio" ] } ``` Denotes the generated speech audio in seconds. ### Property: AudioTextToSpeech.TimePosition ```json { "type": "double", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Playback", "capabilities": [ "Audio" ] } ``` Tracks and controls the current position of the playhead within the generated speech audio, in seconds. ### Property: AudioTextToSpeech.VoiceId ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Asset", "capabilities": [ "Audio" ] } ``` The voice style to be used by [AudioTextToSpeech](/docs/reference/engine/classes/AudioTextToSpeech.md). A list of available voices and their corresponding VoiceIds can be found in the [text-to-speech guide](/docs/en-us/audio/objects.md#text-to-speech). ### Property: AudioTextToSpeech.Volume ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` Volume level which is multiplied onto the output audio stream, controlling how loudly the generated speech audio will be played. Ranges from 0 to 3. ## Methods ### Method: AudioTextToSpeech:GetConnectedWires **Signature:** `AudioTextToSpeech:GetConnectedWires(pin: string): List` Returns an array of [Wires](/docs/reference/engine/classes/Wire.md) that are connected to the specified pin. [AudioTextToSpeech](/docs/reference/engine/classes/AudioTextToSpeech.md) has one "Output" pin. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `pin` | `string` | | An input or output pin on this instance | **Returns:** `List` — An array of [Wires](/docs/reference/engine/classes/Wire.md) ### Method: AudioTextToSpeech:GetWaveformAsync **Signature:** `AudioTextToSpeech:GetWaveformAsync(timeRange: NumberRange, samples: int): Array` Returns a sampling of the waveform data for the generated text-to-speech audio. This can be used to check the volume of the audio over the course of its duration without playing it. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `timeRange` | `NumberRange` | | The start and end time (in seconds) of the segment to read. | | `samples` | `int` | | The number of samples to return for the specified range. | **Returns:** `Array` — A table of `samples` numbers ranging between -1 and 1 representing the sampled waveform, ### Method: AudioTextToSpeech:LoadAsync **Signature:** `AudioTextToSpeech:LoadAsync(): AssetFetchStatus` A blocking call that begins the generation of speech audio based on the current parameters. It will yield until the speech generation either completes or fails. Status is returned by an AssetFetchStatus value. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `AssetFetchStatus` ### Method: AudioTextToSpeech:Pause **Signature:** `AudioTextToSpeech:Pause(): ()` Pauses the [AudioTextToSpeech](/docs/reference/engine/classes/AudioTextToSpeech.md) object wherever its [TimePosition](/docs/reference/engine/classes/AudioTextToSpeech.md) is. Replicates from server to client. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `()` ### Method: AudioTextToSpeech:Play **Signature:** `AudioTextToSpeech:Play(): ()` Plays the [AudioTextToSpeech](/docs/reference/engine/classes/AudioTextToSpeech.md) from wherever its [TimePosition](/docs/reference/engine/classes/AudioTextToSpeech.md) is. Replicates from server to client. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `()` ### Method: AudioTextToSpeech:Unload **Signature:** `AudioTextToSpeech:Unload(): ()` Frees resources by unloading the generated speech audio. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `()` ## Events ### Event: AudioTextToSpeech.Ended **Signature:** `AudioTextToSpeech.Ended()` Fires after the [AudioTextToSpeech](/docs/reference/engine/classes/AudioTextToSpeech.md) object has completed playback and paused. Note this event will **not** fire for audio with [Looped](/docs/reference/engine/classes/AudioTextToSpeech.md) set to `true` since it continues playing upon reaching its end. This event will also **not** fire when the audio is paused before playback has completed; for this, use [AudioTextToSpeech:GetPropertyChangedSignal()](/docs/reference/engine/classes/AudioTextToSpeech.md) on the [IsPlaying](/docs/reference/engine/classes/AudioTextToSpeech.md) property. This event may be used to destroy an [AudioTextToSpeech](/docs/reference/engine/classes/AudioTextToSpeech.md) object when it has completed playback. *Security: None · Capabilities: Audio* ### Event: AudioTextToSpeech.Looped **Signature:** `AudioTextToSpeech.Looped()` Event that fires after the [AudioTextToSpeech](/docs/reference/engine/classes/AudioTextToSpeech.md) object loops. This happens when the audio reaches the end of its content and [Looping](/docs/reference/engine/classes/AudioTextToSpeech.md) is `true`. This event does **not** fire if the audio is looped manually by changing its [TimePosition](/docs/reference/engine/classes/AudioTextToSpeech.md). *Security: None · Capabilities: Audio* ### Event: AudioTextToSpeech.WiringChanged **Signature:** `AudioTextToSpeech.WiringChanged(connected: boolean, pin: string, wire: Wire, instance: Instance)` Event that fires after a [Wire](/docs/reference/engine/classes/Wire.md) becomes connected or disconnected, and that [Wire](/docs/reference/engine/classes/Wire.md) is now or was previously connected to a pin on the [AudioTextToSpeech](/docs/reference/engine/classes/AudioTextToSpeech.md) and to some other wirable instance. *Security: None · Capabilities: Audio* **Parameters:** | Name | Type | Description | |------|------|-------------| | `connected` | `boolean` | Whether the instance got connected or disconnected. | | `pin` | `string` | The pin on the [AudioTextToSpeech](/docs/reference/engine/classes/AudioTextToSpeech.md) that the [Wire](/docs/reference/engine/classes/Wire.md) targets. | | `wire` | `Wire` | The [Wire](/docs/reference/engine/classes/Wire.md) between the [AudioTextToSpeech](/docs/reference/engine/classes/AudioTextToSpeech.md) and the other instance. | | `instance` | `Instance` | The other instance that is or was connected through the [Wire](/docs/reference/engine/classes/Wire.md). | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AudioTremolo last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Internal summary: "Creates a trembling effect on a sound by varying the volume of the sound up and down." --- # Class: AudioTremolo > Creates a trembling effect on a sound by varying the volume of the sound up > and down. ## Description [AudioTremolo](/docs/reference/engine/classes/AudioTremolo.md) creates a trembling effect on a sound by varying the volume of the sound up and down. It provides one **Input** pin and one **Output** pin which can be connected to/from by [Wires](/docs/reference/engine/classes/Wire.md). ## Properties ### Property: AudioTremolo.Bypass ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` If `true`, audio streams are passed-through unaffected by this effect. ### Property: AudioTremolo.Depth ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` Controls how much the volume will raise and lower, ranging between `0` and `1` with a default of `1`. If set to `0`, the volume will not oscillate at all. ### Property: AudioTremolo.Duty ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` Controls how long the effect will be active during one volume oscillation in the range of `0` (0%) to `1` (100%). Default is `0.5`. ### Property: AudioTremolo.Frequency ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` Sets how often the effect will oscillate the volume, measured in Hz, in the range of `0.1` to `20`. Default is `5`. ### Property: AudioTremolo.Shape ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` Morphs the LFO (low frequency oscillation) shape in a range of `0` to `1` where `0` is triangle and `1` is sine. Default is `0`. ### Property: AudioTremolo.Skew ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` Controls the time-skewing of the LFO (low frequency oscillation) cycle in the range of `-1` to `1`. No skewing occurs at `0`, with more extremal forwards and backwards skewing towards `+1`/`-1`. Default is `0`. ### Property: AudioTremolo.Square ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Audio" ] } ``` Determines the squareness of the oscillations in the range of `0` to `1`. The original shape is retained when `0` and the shape flattens more as the value increases. Default is `0`. ## Methods ### Method: AudioTremolo:GetConnectedWires **Signature:** `AudioTremolo:GetConnectedWires(pin: string): List` Returns an array of [Wires](/docs/reference/engine/classes/Wire.md) that are connected to the specified pin. [AudioTremolo](/docs/reference/engine/classes/AudioTremolo.md) has one "Input" pin and one "Output" pin. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `pin` | `string` | | An input or output pin on this instance | **Returns:** `List` — An array of [Wires](/docs/reference/engine/classes/Wire.md) ### Method: AudioTremolo:GetInputPins **Signature:** `AudioTremolo:GetInputPins(): Array` Gets the list of pins that [Wire](/docs/reference/engine/classes/Wire.md) can use in [Wire.TargetName](/docs/reference/engine/classes/Wire.md) to connect to this instance via its [Wire.TargetInstance](/docs/reference/engine/classes/Wire.md) property. For [AudioTremolo](/docs/reference/engine/classes/AudioTremolo.md), this is `Input` only. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Array` — An array of strings representing valid pin names. ### Method: AudioTremolo:GetOutputPins **Signature:** `AudioTremolo:GetOutputPins(): Array` Gets the list of pins that [Wire](/docs/reference/engine/classes/Wire.md) can use in [Wire.SourceName](/docs/reference/engine/classes/Wire.md) to connect to this instance via its [Wire.SourceInstance](/docs/reference/engine/classes/Wire.md) property. For [AudioTremolo](/docs/reference/engine/classes/AudioTremolo.md), this is `Output` only. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Array` — An array of strings representing valid pin names. ## Events ### Event: AudioTremolo.WiringChanged **Signature:** `AudioTremolo.WiringChanged(connected: boolean, pin: string, wire: Wire, instance: Instance)` Event that fires after a [Wire](/docs/reference/engine/classes/Wire.md) becomes connected or disconnected, and that [Wire](/docs/reference/engine/classes/Wire.md) is now or was previously connected to a pin on the [AudioTremolo](/docs/reference/engine/classes/AudioTremolo.md) and to some other wirable instance. *Security: None · Capabilities: Audio* **Parameters:** | Name | Type | Description | |------|------|-------------| | `connected` | `boolean` | Whether the instance got connected or disconnected. | | `pin` | `string` | The pin on the [AudioTremolo](/docs/reference/engine/classes/AudioTremolo.md) that the [Wire](/docs/reference/engine/classes/Wire.md) targets. | | `wire` | `Wire` | The [Wire](/docs/reference/engine/classes/Wire.md) between the [AudioTremolo](/docs/reference/engine/classes/AudioTremolo.md) and the other instance. | | `instance` | `Instance` | The other instance that is or was connected through the [Wire](/docs/reference/engine/classes/Wire.md). | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AvatarCreationService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service summary: "A service to support developer avatar creators." --- # Class: AvatarCreationService > A service to support developer avatar creators. ## Description `AvatarCreationService` is a service that supports developer avatar creators, providing methods that support the prompting of avatar creation from within experiences. ## Methods ### Method: AvatarCreationService:AutoSetupAvatarAsync **Signature:** `AvatarCreationService:AutoSetupAvatarAsync(player: Player, autoSetupParams: Dictionary, progressCallback: Function?): string` Automatically sets up a custom [Model](/docs/reference/engine/classes/Model.md) as an avatar asset. This method returns a `string` generation ID which can be passed to [LoadGeneratedAvatarAsync()](/docs/reference/engine/classes/AvatarCreationService.md) to load the generated avatar and return a [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) with all the generated instances and properties. The load method can be called on both the server and client, allowing the generated avatar to be loaded in both places (on the client for previewing, and on the server for saving the generated avatar to the player's inventory using [PromptCreateAvatarAsync()](/docs/reference/engine/classes/AvatarCreationService.md)). Auto-setup has specific model requirements and accepts certain configurations of models. For more information, see [Auto-setup requirements](/docs/en-us/avatar-setup/auto-setup-requirements.md). In addition to the above requirements, the input model must use [EditableMesh](/docs/reference/engine/classes/EditableMesh.md) and [EditableImage](/docs/reference/engine/classes/EditableImage.md) objects for all mesh and texture assets. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Player` | | The [Player](/docs/reference/engine/classes/Player.md) that the avatar is being set up for. | | `autoSetupParams` | `Dictionary` | | A table containing the arguments. Type: `type AutoSetupAccessory = { AccessoryType: Enum.AccessoryType, IsLayered: bool, Instance: Model, } type AutoSetupParams = { Body: Model?, Accessories: {AutoSetupAccessory}, }` | | `progressCallback` | `Function?` | | Optional callback function that will be invoked periodically with a progressInfo table with the overall progress (from 0 to 1). Type: `(progressInfo: { Progress: number }) -> ()` | **Returns:** `string` — A unique identifier for the generated avatar. **AutoSetupAvatarAsync** The following code invokes AutoSetup on the given body Model and layered shirt Model. The setup avatar is then loaded on the server and an event is sent to the client to load the avatar for local preview. ```lua local AvatarCreationService = game:GetService("AvatarCreationService") local ReplicatedStorage = game:GetService("ReplicatedStorage") local LoadAvatarEvent = ReplicatedStorage.LoadAvatarEvent local generatedAvatars = {} -- Must be called on the server local function setupAvatar(player: Player, avatarModel: Model, layeredShirtModel : Model) local arguments = { Body = avatarModel, Accessories = {} } local accessoryArgument = { AccessoryType = Enum.AccessoryType.Shirt, IsLayered = true, Instance = layeredShirtModel, } table.insert(arguments.Accessories, accessoryArgument) local pcallSuccess, result = pcall(function() local generationId = AvatarCreationService:AutoSetupAvatarAsync( player, arguments, function(progressInfo) print(`AutoSetup progress: {progressInfo.Progress * 100}%`) end ) -- Load and store on the server to use with PromptCreateAvatarAsync local humanoidDescription = AvatarCreationService:LoadGeneratedAvatarAsync(generationId) generatedAvatars[generationId] = humanoidDescription -- Signal the client so it can load the avatar for local preview LoadAvatarEvent:FireClient(player, generationId) end) if not pcallSuccess then warn("Avatar setup failed:", result) end end ``` ### Method: AvatarCreationService:GenerateAvatar2DPreviewAsync **Signature:** `AvatarCreationService:GenerateAvatar2DPreviewAsync(avatarGeneration2dPreviewParams: Dictionary, progressCallback: Function?): string` Creates a 2D avatar image preview, taking as input the FileId from [PromptSelectAvatarGenerationImageAsync()](/docs/reference/engine/classes/AvatarCreationService.md) and an optional text prompt. It returns a `previewId` which can be passed to [LoadAvatar2DPreviewAsync()](/docs/reference/engine/classes/AvatarCreationService.md) to retrieve the preview image on the client. This API can only be used on the game server. ```lua local AvatarCreationService = game:GetService("AvatarCreationService") function generateAvatar2DPreview(sessionId, fileId, textPrompt) local pcallSuccess, result = pcall(function() local args = {} args.SessionId = sessionId args.FileId = fileId args.TextPrompt = textPrompt return AvatarCreationService:GenerateAvatar2DPreviewAsync(args) end) if not pcallSuccess then warn("Generating 2D preview failed: ", result) end return result end ``` *Yields · Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `avatarGeneration2dPreviewParams` | `Dictionary` | | A table of arguments for 2D preview generation. Type: `avatarGeneration2dPreviewParams: {SessionId: string, FileId: string, TextPrompt: string?}` | | `progressCallback` | `Function?` | | Optional callback function that will be invoked periodically with a progressInfo table with the overall progress (from 0 to 1). Type: `(progressInfo: { Progress: number }) -> ()` | **Returns:** `string` — A string previewId ### Method: AvatarCreationService:GenerateAvatarAsync **Signature:** `AvatarCreationService:GenerateAvatarAsync(avatarGenerationParams: Dictionary, progressCallback: Function?): string` Generate an avatar from a preview and return the generationId. The [LoadGeneratedAvatarAsync()](/docs/reference/engine/classes/AvatarCreationService.md) method is then called to retrieve the generated [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) avatar. This API can only be used on the game server. ```lua local AvatarCreationService = game:GetService("AvatarCreationService") function generateAvatar(sessionId, previewId) local pcallSuccess, result = pcall(function() local args = {} args.SessionId = sessionId args.PreviewId = previewId return AvatarCreationService:GenerateAvatarAsync(args) end) if not pcallSuccess then warn("Generating avatar failed: ", result) end return result end ``` *Yields · Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `avatarGenerationParams` | `Dictionary` | | A table of arguments for generating an avatar. Type: `avatarGenerationParams: {SessionId: string, PreviewId: string}` | | `progressCallback` | `Function?` | | Optional callback function that will be invoked periodically with a progressInfo table with the overall progress (from 0 to 1). Type: `(progressInfo: { Progress: number }) -> ()` | **Returns:** `string` — A string generationId. ### Method: AvatarCreationService:GetBatchTokenDetailsAsync **Signature:** `AvatarCreationService:GetBatchTokenDetailsAsync(tokenIds: Array): Array` Gets the avatar creation token details for a list of avatar creation tokens at once (tokens are generated through the [token creation](/docs/en-us/production/monetization/avatar-creation-token.md) process). Returns an array of avatar creation token details; each token detail is a dictionary with the fields indicated in the example result below: ```lua { ["Name"] = "string", ["Description"] = "string", ["UniverseId"] = 0, ["CreatorId"] = 0, ["CreatorType"] = Enum.CreatorType.User, ["OnSale"] = true, ["Price"] = 0, ["OffSaleReasons"] = { "string", } } ``` *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Monetization* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `tokenIds` | `Array` | | The list of avatar creation token IDs to get details of. | **Returns:** `Array` — Array of avatar creation token details as outlined above. ### Method: AvatarCreationService:GetValidationRules **Signature:** `AvatarCreationService:GetValidationRules(): Dictionary` Gets data regarding rules that assets must abide by to pass UGC validation. Validation is an essential step before creating avatars and there are various checks that occur, including mesh triangle limits, texture sizes, body part size limits, attachment positions, and more. The returned dictionary of validation rules takes the following form: ``` { ["MeshRules"] = { ["BodyPartMaxTriangles"] = { Enum.AssetType.DynamicHead: number, Enum.AssetType.LeftArm: number, Enum.AssetType.RightArm: number, Enum.AssetType.Torso: number, Enum.AssetType.LeftLeg: number, Enum.AssetType.RightLeg: number, }, ["AccessoryMaxTriangles"]: number, ["MeshVertColor"]: Color3, ["CageMeshMaxDistanceFromRenderMesh"]: number, }, ["TextureRules"] = { ["MaxTextureSize"]: number, }, ["BodyPartRules"] = { [Enum.AssetType.DynamicHead] = { ["Bounds"] = { ["Classic"] = { ["MinSize"]: Vector3, ["MaxSize"]: Vector3, }, ["ProportionsSlender"] = { ["MinSize"]: Vector3, ["MaxSize"]: Vector3, }, ["ProportionsNormal"] = { ["MinSize"]: Vector3, ["MaxSize"]: Vector3, }, }, ["SubParts"] = { ["Head"] = { ["NeckRigAttachment"] = { ["LowerBound"]: Vector3, ["UpperBound"]: Vector3, }, ["FaceFrontAttachment"] = { ["LowerBound"]: Vector3, ["UpperBound"]: Vector3, }, ["HatAttachment"] = { ["LowerBound"]: Vector3, ["UpperBound"]: Vector3, }, ["HairAttachment"] = { ["LowerBound"]: Vector3, ["UpperBound"]: Vector3, }, ["FaceCenterAttachment"] = { ["LowerBound"]: Vector3, ["UpperBound"]: Vector3, }, }, }, }, [Enum.AssetType.LeftArm] = { ["Bounds"] = { ["Classic"] = { ["MinSize"]: Vector3, ["MaxSize"]: Vector3, }, ["ProportionsSlender"] = { ["MinSize"]: Vector3, ["MaxSize"]: Vector3, }, ["ProportionsNormal"] = { ["MinSize"]: Vector3, ["MaxSize"]: Vector3, }, }, ["SubParts"] = { ["LeftHand"] = { ["LeftWristRigAttachment"] = { ["LowerBound"]: Vector3, ["UpperBound"]: Vector3, }, ["LeftGripAttachment"] = { ["LowerBound"]: Vector3, ["UpperBound"]: Vector3, }, }, ["LeftUpperArm"] = { ["LeftShoulderRigAttachment"] = { ["LowerBound"]: Vector3, ["UpperBound"]: Vector3, }, ["LeftShoulderAttachment"] = { ["LowerBound"]: Vector3, ["UpperBound"]: Vector3, }, ["LeftElbowRigAttachment"] = { ["LowerBound"]: Vector3, ["UpperBound"]: Vector3, }, }, ["LeftLowerArm"] = { ["LeftElbowRigAttachment"] = { ["LowerBound"]: Vector3, ["UpperBound"]: Vector3, }, ["LeftWristRigAttachment"] = { ["LowerBound"]: Vector3, ["UpperBound"]: Vector3, }, }, }, }, ... }, ["AccessoryRules"] = { [Enum.AssetType.HairAccessory] = { ["Attachments"] = { { ["Size"]: Vector3, ["Offset"]: Vector3, ["Name"]: string, }, }, ["RigidAllowed"]: boolean, }, ... }, ["MakeupRules"] = { ["ReferenceCageMeshId"]: number, ["WrapTextureTransferUVBounds"] = { ["MinBound"]: Vector2, ["MaxBound"]: Vector2, }, ["ExcludeUVBounds"] = { [Enum.AssetType.FaceMakeup] = { ["LeftEye"] = { ["MinBound"]: Vector2, ["MaxBound"]: Vector2, }, ["RightEye"] = { ["MinBound"]: Vector2, ["MaxBound"]: Vector2, }, ["Lips"] = { ["MinBound"]: Vector2, ["MaxBound"]: Vector2, }, }, }, ["IncludeUVBounds"] = { [Enum.AssetType.LipMakeup] = { ["MinBound"]: Vector2, ["MaxBound"]: Vector2, }, [Enum.AssetType.EyeMakeup] = { ["MinBound"]: Vector2, ["MaxBound"]: Vector2, }, } } } ``` *Security: None · Thread Safety: Unsafe* **Returns:** `Dictionary` — Dictionary of validation rules as detailed above. ### Method: AvatarCreationService:LoadAvatar2DPreviewAsync **Signature:** `AvatarCreationService:LoadAvatar2DPreviewAsync(previewId: string): EditableImage` Load an AvatarGeneration 2D preview as an [EditableImage](/docs/reference/engine/classes/EditableImage.md) on the client for the given previewId. This API can only be used on the client. ```lua local AvatarCreationService = game:GetService("AvatarCreationService") function loadAvatar2DPreview(previewId) local pcallSuccess, result = pcall(function() return AvatarCreationService:LoadAvatar2DPreviewAsync(previewId) end) if not pcallSuccess then warn("Failed to load preview: ", result) end return result end ``` *Yields · Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `previewId` | `string` | | Load the preview generated from [GenerateAvatar2DPreviewAsync()](/docs/reference/engine/classes/AvatarCreationService.md). | **Returns:** `EditableImage` — An [EditableImage](/docs/reference/engine/classes/EditableImage.md) containing the preview image. ### Method: AvatarCreationService:LoadGeneratedAvatarAsync **Signature:** `AvatarCreationService:LoadGeneratedAvatarAsync(generationId: string): HumanoidDescription` Loads a generated avatar using an avatar generation ID, as returned by [AutoSetupAvatarAsync()](/docs/reference/engine/classes/AvatarCreationService.md) or [GenerateAvatarAsync()](/docs/reference/engine/classes/AvatarCreationService.md). The generated avatar will be returned as a [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) with all the generated instances and properties. Mesh and texture assets will be provided as [EditableMesh](/docs/reference/engine/classes/EditableMesh.md) and [EditableImage](/docs/reference/engine/classes/EditableImage.md) objects, respectively, to allow continued editing of the generated avatar. This method can be called on both the server and client, allowing the generated avatar to be loaded in both places (on the client for previewing, and on the server for saving the generated avatar to the player's inventory using [PromptCreateAvatarAsync()](/docs/reference/engine/classes/AvatarCreationService.md)). Once the method has been invoked on both the client and server, the data associated with the generation ID will be erased and subsequent calls to his method with the same generation ID will fail. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `generationId` | `string` | | A unique string that identifies the generated avatar, as returned by [ AutoSetupAvatarAsync()](/docs/reference/engine/classes/AvatarCreationService.md). | **Returns:** `HumanoidDescription` — The [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) of the generated avatar, which includes all the generated instances and properties. **LoadGeneratedAvatarAsync** The following code loads a generated avatar using an avatar generation ID provided by [AvatarCreationService:AutoSetupAvatarAsync()](/docs/reference/engine/classes/AvatarCreationService.md). The result is returned as a [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). ```lua local AvatarCreationService = game:GetService("AvatarCreationService") local Players = game:GetService("Players") -- Can be called on either the server or client local function loadAvatar(generationId: string): Model? local pcallSuccess, result = pcall(function() local humanoidDescription = AvatarCreationService:LoadGeneratedAvatarAsync(generationId) local model = Players:CreateHumanoidModelFromDescriptionAsync(humanoidDescription, Enum.HumanoidRigType.R15) return model end) if pcallSuccess then return result else print("Avatar failed to load:", result) return nil end end ``` ### Method: AvatarCreationService:PrepareAvatarForPreviewAsync **Signature:** `AvatarCreationService:PrepareAvatarForPreviewAsync(humanoidModel: Model): ()` Triggers HSR generation and attachment point updating for in-experience avatar previews. This allows for previewing of avatars created in experience with layered clothing and accessories in the same way they will appear when published through [PromptCreateAvatarAsync()](/docs/reference/engine/classes/AvatarCreationService.md). When developers use [EditableMesh](/docs/reference/engine/classes/EditableMesh.md) and [WrapDeformer](/docs/reference/engine/classes/WrapDeformer.md) to modify avatars before publishing, the original HSR data may not accurately account for the new deformations. This method generates updated HSR data and corrects attachment points based on the [WrapDeformer](/docs/reference/engine/classes/WrapDeformer.md) modifications, ensuring consistent preview and published avatar appearance. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AvatarAppearance* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `humanoidModel` | `Model` | | The [Model](/docs/reference/engine/classes/Model.md) containing [MeshPart](/docs/reference/engine/classes/MeshPart.md) children with [WrapDeformer](/docs/reference/engine/classes/WrapDeformer.md) instances that require HSR data updating for preview. | **Returns:** `()` ### Method: AvatarCreationService:PromptCreateAvatarAssetAsync **Signature:** `AvatarCreationService:PromptCreateAvatarAssetAsync(tokenId: string, player: Player, assetInstance: Instance, assetType: AvatarAssetType): Tuple` Prompts a [Player](/docs/reference/engine/classes/Player.md) to purchase and create an avatar asset from an [Instance](/docs/reference/engine/classes/Instance.md). The price of the creation is dictated by the price attributed to the avatar creation token. This creation token is required for the purchasing and creation of the asset and can be generated by following the [token creation](/docs/en-us/production/monetization/avatar-creation-token.md) process. For avatar asset creation, the [Instance](/docs/reference/engine/classes/Instance.md) is expected to include a new accessory to be created. This includes the following types: [Hat](/docs/reference/engine/enums/AvatarAssetType.md), [Hair](/docs/reference/engine/enums/AvatarAssetType.md), [FaceAccessory](/docs/reference/engine/enums/AvatarAssetType.md), [NeckAccessory](/docs/reference/engine/enums/AvatarAssetType.md), [ShoulderAccessory](/docs/reference/engine/enums/AvatarAssetType.md), [FrontAccessory](/docs/reference/engine/enums/AvatarAssetType.md), [BackAccessory](/docs/reference/engine/enums/AvatarAssetType.md), [WaistAccessory](/docs/reference/engine/enums/AvatarAssetType.md), [TShirtAccessory](/docs/reference/engine/enums/AvatarAssetType.md), [ShirtAccessory](/docs/reference/engine/enums/AvatarAssetType.md), [PantsAccessory](/docs/reference/engine/enums/AvatarAssetType.md), [JacketAccessory](/docs/reference/engine/enums/AvatarAssetType.md), [SweaterAccessory](/docs/reference/engine/enums/AvatarAssetType.md), [ShortsAccessory](/docs/reference/engine/enums/AvatarAssetType.md), [DressSkirtAccessory](/docs/reference/engine/enums/AvatarAssetType.md). To support this, the [Instance](/docs/reference/engine/classes/Instance.md) should be an [Accessory](/docs/reference/engine/classes/Accessory.md) instance or contain an [Accessory](/docs/reference/engine/classes/Accessory.md) child instance. This should include a [MeshPart](/docs/reference/engine/classes/MeshPart.md) child instance which makes up the accessory. The avatar asset [MeshPart](/docs/reference/engine/classes/MeshPart.md) will also need to include: - An [EditableImage](/docs/reference/engine/classes/EditableImage.md). - An [EditableMesh](/docs/reference/engine/classes/EditableMesh.md). If creating a layered clothing accessory such as a shirt, the [MeshPart](/docs/reference/engine/classes/MeshPart.md) should include a [WrapDeformer](/docs/reference/engine/classes/WrapDeformer.md) with an [EditableMesh](/docs/reference/engine/classes/EditableMesh.md). Finally, the provided [AvatarAssetType](/docs/reference/engine/enums/AvatarAssetType.md) should match the creation type of the token and the type of [Accessory](/docs/reference/engine/classes/Accessory.md) for upload. *Yields · Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `tokenId` | `string` | | The ID of a creation token. The token must be valid in that the universe the method is called from is the same universe the token was created for. Furthermore, the token creator must maintain ID verification and [Roblox Premium](https://www.roblox.com/premium/membership). To create a token for utilization in this API, follow the [token creation](/docs/en-us/production/monetization/avatar-creation-token.md) process. The token's creation type must match the [AvatarAssetType](/docs/reference/engine/enums/AvatarAssetType.md) passed in to the method. | | `player` | `Player` | | The [Player](/docs/reference/engine/classes/Player.md) intended to be presented with the creation prompt. | | `assetInstance` | `Instance` | | The [Instance](/docs/reference/engine/classes/Instance.md) of the avatar asset intended for creation. | | `assetType` | `AvatarAssetType` | | The [AvatarAssetType](/docs/reference/engine/enums/AvatarAssetType.md) of the expected creation. This must match the creation type of the provided token. | **Returns:** `Tuple` — A tuple containing, in order: - An [PromptCreateAssetResult](/docs/reference/engine/enums/PromptCreateAssetResult.md) indicating the result of the creation prompt. - A string result. In the case of [PromptCreateAssetResult.Success](/docs/reference/engine/enums/PromptCreateAssetResult.md), this will indicate the asset ID. In the case of any failure enum, this will indicate the resultant error message. **PromptCreateAvatarAssetAsync** The following code prompts for avatar asset creation, and responds to the result. ```lua local AvatarCreationService = game:GetService("AvatarCreationService") local function publishAvatarAsset(tokenId: string, player: Player, assetInstance: Accessory, assetType: Enum.AvatarAssetType) local pcallSuccess, result, resultMessage = pcall(function() return AvatarCreationService:PromptCreateAvatarAssetAsync(tokenId, player, assetInstance, assetType) end) if pcallSuccess then if result == Enum.PromptCreateAssetResult.Success then print("Successfully uploaded with AssetId: ", resultMessage) else print("Unsuccessfully uploaded with error message:", resultMessage) end else print("Avatar asset failed to create.") end end ``` ### Method: AvatarCreationService:PromptCreateAvatarAsync **Signature:** `AvatarCreationService:PromptCreateAvatarAsync(tokenId: string, player: Player, humanoidDescription: HumanoidDescription): Tuple` Prompts a [Player](/docs/reference/engine/classes/Player.md) to purchase and create an avatar from a [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). The price of the creation is dictated by the price attributed to the avatar creation token. This avatar creation token is required for the purchasing and creation of the body and can be generated by following the [token creation](/docs/en-us/production/monetization/avatar-creation-token.md) process. For avatar creation, the [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) is expected to include new assets to be created for each of the 6 body parts ([Head](/docs/reference/engine/enums/BodyPart.md), [Torso](/docs/reference/engine/enums/BodyPart.md), [RightLeg](/docs/reference/engine/enums/BodyPart.md), [LeftLeg](/docs/reference/engine/enums/BodyPart.md), [RightArm](/docs/reference/engine/enums/BodyPart.md), [LeftArm](/docs/reference/engine/enums/BodyPart.md)). Optionally, it can also include a new [Hair](/docs/reference/engine/enums/AccessoryType.md) accessory. To support this, the [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) should include 6 [BodyPartDescription](/docs/reference/engine/classes/BodyPartDescription.md) children (one for each body part). For each, the [BodyPartDescription.Instance](/docs/reference/engine/classes/BodyPartDescription.md) property references a [Folder](/docs/reference/engine/classes/Folder.md) which includes all of the [MeshPart](/docs/reference/engine/classes/MeshPart.md) instances which make up the body part, for example a `LeftArm` folder which has `LeftHand`, `LeftUpperArm`, and `LeftLowerArm` [MeshParts](/docs/reference/engine/classes/MeshPart.md). The [BodyPartDescription.BodyPart](/docs/reference/engine/classes/BodyPartDescription.md) property should also be set to the relevant [BodyPart](/docs/reference/engine/enums/BodyPart.md). Each body part [MeshPart](/docs/reference/engine/classes/MeshPart.md) will also need to include: - An [EditableImage](/docs/reference/engine/classes/EditableImage.md). - A [WrapDeformer](/docs/reference/engine/classes/WrapDeformer.md) with an [EditableMesh](/docs/reference/engine/classes/EditableMesh.md). If including an accessory such as hair, the [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) should include a child [AccessoryDescription](/docs/reference/engine/classes/AccessoryDescription.md) where: - The [AccessoryDescription.Instance](/docs/reference/engine/classes/AccessoryDescription.md) property references the [Accessory](/docs/reference/engine/classes/Accessory.md) instance. - The [AccessoryDescription.AccessoryType](/docs/reference/engine/classes/AccessoryDescription.md) property is set to the relevant [AccessoryType](/docs/reference/engine/enums/AccessoryType.md). Finally, the [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) should include the humanoid scales of [BodyTypeScale](/docs/reference/engine/classes/HumanoidDescription.md), [HeadScale](/docs/reference/engine/classes/HumanoidDescription.md), [HeightScale](/docs/reference/engine/classes/HumanoidDescription.md), [WidthScale](/docs/reference/engine/classes/HumanoidDescription.md), and [ProportionScale](/docs/reference/engine/classes/HumanoidDescription.md). Be mindful of the scales that a base body is imported with so that they match the scales provided to the [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetCreateUpdate, Monetization* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `tokenId` | `string` | | The ID of an avatar creation token. The token must be valid in that the universe the method is called from is the same universe the token was created for. Furthermore, the token creator must maintain ID verification and [Roblox Premium](https://www.roblox.com/premium/membership). To create a token for utilization in this API, follow the [token creation](/docs/en-us/production/monetization/avatar-creation-token.md) process. | | `player` | `Player` | | The [Player](/docs/reference/engine/classes/Player.md) intended to be presented with the creation prompt. | | `humanoidDescription` | `HumanoidDescription` | | The [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) of the avatar intended for creation. | **Returns:** `Tuple` — A tuple containing, in order: - An [PromptCreateAvatarResult](/docs/reference/engine/enums/PromptCreateAvatarResult.md) indicating the result of the creation prompt. - A string result. In the case of [PromptCreateAvatarResult.Success](/docs/reference/engine/enums/PromptCreateAvatarResult.md), this will indicate the bundle ID. In the case of any failure enum, this will indicate the resultant error message. - A secondary optional string result. In the case of [PromptCreateAvatarResult.Success](/docs/reference/engine/enums/PromptCreateAvatarResult.md), this will indicate the outfit ID. In the case of any failure enum, this will be `nil`. **PromptCreateAvatarAsync** The following code populates a [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) in the expected format, prompts for avatar creation, and responds to the result. ```lua local AvatarCreationService = game:GetService("AvatarCreationService") export type BodyPartInfo = { bodyPart: Enum.BodyPart, instance: Instance, --Folder with Created MeshParts } export type BodyPartList = { BodyPartInfo } local function publishAvatar(bodyPartInstances: BodyPartList, player: Player, tokenId: string) local humanoidDescription = Instance.new("HumanoidDescription") for _, bodyPartInfo in bodyPartInstances do local bodyPartDescription = Instance.new("BodyPartDescription") bodyPartDescription.Instance = bodyPartInfo.instance bodyPartDescription.BodyPart = bodyPartInfo.bodyPart bodyPartDescription.Parent = humanoidDescription end local pcallSuccess, result, resultMessage = pcall(function() return AvatarCreationService:PromptCreateAvatarAsync(tokenId, player, humanoidDescription) end) if pcallSuccess then if result == Enum.PromptCreateAvatarResult.Success then print("Successfully uploaded with BundleId: ", resultMessage) else print("Unsuccessfully uploaded with error message:", resultMessage) end else print("Avatar failed to create.") end end ``` ### Method: AvatarCreationService:PromptSelectAvatarGenerationImageAsync **Signature:** `AvatarCreationService:PromptSelectAvatarGenerationImageAsync(player: Player): string` Prompt the [Player](/docs/reference/engine/classes/Player.md) to take a selfie and return the FileId of the selfie. This FileId is then passed as an argument to [GenerateAvatar2DPreviewAsync()](/docs/reference/engine/classes/AvatarCreationService.md). This API can only be used on the game server. ```lua local AvatarCreationService = game:GetService("AvatarCreationService") function promptSelectAvatarGenerationImage(player) local pcallSuccess, result = pcall(function() return AvatarCreationService:PromptSelectAvatarGenerationImageAsync(player) end) if not pcallSuccess then errName, errDesc = unpack(string.split(result, ": ")) if errName == "SelfieConsentDenied" then warn("Player must accept consent to use feature.") else warn("Failed to prompt for image: ", errDesc) end end return result end ``` On failure, the result string has the format `ErrorName: Error description`, allowing the error type to be identified programmatically. | Error name | Error description | | --- | --- | | `SelfieConsentDenied` | Selfie consent not accepted | | `QRTooManyRequests` | Failure to generate QR url due to too many requests; please wait and try again later | | `MediaPermissionsDenied` | Permissions not granted for taking photo | | `FeatureUnavailable` | Feature not available for player | | `GenerationInProgress` | Generation already in progress for player | *Yields · Security: None · Thread Safety: Unsafe · Capabilities: SensitiveInput* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Player` | | The [Player](/docs/reference/engine/classes/Player.md) to prompt for taking a selfie. | **Returns:** `string` — A string FileId of the selfie on success, or an error string describing the reason for failure. ### Method: AvatarCreationService:RequestAvatarGenerationSessionAsync **Signature:** `AvatarCreationService:RequestAvatarGenerationSessionAsync(player: Player, callback: Function): Tuple` Request an AvatarGeneration session for a [Player](/docs/reference/engine/classes/Player.md). Information about the session is returned via the callback, including the SessionId which is passed to the [GenerateAvatar2DPreviewAsync()](/docs/reference/engine/classes/AvatarCreationService.md) and [GenerateAvatarAsync()](/docs/reference/engine/classes/AvatarCreationService.md) methods. Additional session information includes allowed 2d preview generations, allowed 3d avatar generations, and the session time. The method returns a `Tuple` with an [RBXScriptConnection](/docs/reference/engine/datatypes/RBXScriptConnection.md) and estimated `waitTime`. The [RBXScriptConnection](/docs/reference/engine/datatypes/RBXScriptConnection.md) allows canceling a session request. The estimated `waitTime` is used to provide the player an estimated time in seconds until the session will be ready. This API can only be used on the game server. ```lua local AvatarCreationService = game:GetService("AvatarCreationService") local playerSessions = {} local playerSessionRequests = {} function requestAvatarGenerationSession(player) local pcallSuccess, conn, waitTime = pcall(function() return AvatarCreationService:RequestAvatarGenerationSessionAsync(player, function(sessionInfo) playerSessions[player.UserID] = sessionInfo playerSessionRequests[player.UserID] = nil end) end) if not pcallSuccess then warn("Failed to request Avatar generation session: ", conn) end playerSessionRequests[player.UserID] = conn print("Wait time for session: ", tostring(waitTime)) end function cancelAvatarGenerationSessionRequest(player) if playerSessionRequests[player.UserID] ~= nil then playerSessionRequests[player.UserID]:Disconnect() end end ``` *Yields · Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Player` | | The [Player](/docs/reference/engine/classes/Player.md) to request an AvatarGeneration session for. | | `callback` | `Function` | | Callback function that is invoked with a SessionInfo table, with information about the session. Type: `(SessionInfo: { SessionId: string, Allowed2DGenerations: number, Allowed3DGenerations: number, SessionTime: number }) -> ()` | **Returns:** `Tuple` — A tuple containing a [RBXScriptConnection](/docs/reference/engine/datatypes/RBXScriptConnection.md) that can be used to cancel the session request and the estimated wait time in seconds. ### Method: AvatarCreationService:ValidateUGCAccessoryAsync **Signature:** `AvatarCreationService:ValidateUGCAccessoryAsync(player: Player, accessory: Instance, accessoryType: AccessoryType): Tuple` Studio only. Given a [Player](/docs/reference/engine/classes/Player.md) and [Instance](/docs/reference/engine/classes/Instance.md) for an [AccessoryType](/docs/reference/engine/enums/AccessoryType.md), determines if UGC validation passes. *Yields · Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Player` | | The [Player](/docs/reference/engine/classes/Player.md) validation is completed for. | | `accessory` | `Instance` | | The instance validation is run on. | | `accessoryType` | `AccessoryType` | | [AccessoryType](/docs/reference/engine/enums/AccessoryType.md) the instance is expected to be. Expects [Eyebrow](/docs/reference/engine/enums/AccessoryType.md), [Eyelash](/docs/reference/engine/enums/AccessoryType.md), or [Hair](/docs/reference/engine/enums/AccessoryType.md). | **Returns:** `Tuple` — A tuple containing, in order: - A boolean indicating if validation was successful for the accessory. - An optional table of strings. This includes failure reasons if validation was unsuccessful; otherwise `nil` if validation was successful. ### Method: AvatarCreationService:ValidateUGCBodyPartAsync **Signature:** `AvatarCreationService:ValidateUGCBodyPartAsync(player: Player, instance: Instance, bodyPart: BodyPart): Tuple` Studio only. Given a [Player](/docs/reference/engine/classes/Player.md) and [Instance](/docs/reference/engine/classes/Instance.md) for an [BodyPart](/docs/reference/engine/enums/BodyPart.md), determines if UGC validation passes. The `instance` parameter is expected as a [Folder](/docs/reference/engine/classes/Folder.md) in the following example format with relevant [MeshParts](/docs/reference/engine/classes/MeshPart.md): - `LeftArm` ([Folder](/docs/reference/engine/classes/Folder.md)) - `R15ArtistIntent` ([Folder](/docs/reference/engine/classes/Folder.md)) - `LeftLowerArm` ([MeshPart](/docs/reference/engine/classes/MeshPart.md)) - `LeftUpperArm` ([MeshPart](/docs/reference/engine/classes/MeshPart.md)) - `LeftHand` ([MeshPart](/docs/reference/engine/classes/MeshPart.md)) However, if the expected `bodyPart` is [BodyPart.Head](/docs/reference/engine/enums/BodyPart.md), the function takes a singular `Head` [MeshPart](/docs/reference/engine/classes/MeshPart.md) directly. *Yields · Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Player` | | The [Player](/docs/reference/engine/classes/Player.md) validation is completed for. | | `instance` | `Instance` | | The instance validation is run on. | | `bodyPart` | `BodyPart` | | [BodyPart](/docs/reference/engine/enums/BodyPart.md) the instance is expected to be. | **Returns:** `Tuple` — A tuple containing, in order: - A boolean indicating if validation was successful for the body part. - An optional table of strings. This includes failure reasons if validation was unsuccessful; otherwise `nil` if validation was successful. ### Method: AvatarCreationService:ValidateUGCFullBodyAsync **Signature:** `AvatarCreationService:ValidateUGCFullBodyAsync(player: Player, humanoidDescription: HumanoidDescription): Tuple` Studio only. Given a [Player](/docs/reference/engine/classes/Player.md) and [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md), all instances in the [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) will be validated. The [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) is expected to include instances set on [BodyPartDescription](/docs/reference/engine/classes/BodyPartDescription.md) children for each of the 6 required [BodyPart](/docs/reference/engine/enums/BodyPart.md) values. Optionally, it can include instances set on [AccessoryDescription](/docs/reference/engine/classes/AccessoryDescription.md) children for `Eyebrow`, `Eyelash`, and `Hair` [AccessoryTypes](/docs/reference/engine/enums/AccessoryType.md). *Yields · Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Player` | | The [Player](/docs/reference/engine/classes/Player.md) validation is completed for. | | `humanoidDescription` | `HumanoidDescription` | | [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) representing the body that validation is run on. | **Returns:** `Tuple` — A tuple containing, in order: - A boolean indicating if validation was successful for the body. - An optional table of strings. This includes failure reasons if validation was unsuccessful; otherwise `nil` if validation was successful. ## Events ### Event: AvatarCreationService.AvatarAssetModerationCompleted **Signature:** `AvatarCreationService.AvatarAssetModerationCompleted(assetId: int64, moderationStatus: ModerationStatus)` Fires when an in-experience-created avatar asset's moderation status has been updated from pending. This event provides a streamlined way to know when an avatar asset created through [PromptCreateAvatarAssetAsync()](/docs/reference/engine/classes/AvatarCreationService.md) has completed the moderation process and is ready for use in-experience. Note that this event only fires for avatar assets created within the current experience and will trigger when the [ModerationStatus](/docs/reference/engine/enums/ModerationStatus.md) changes from [NotReviewed](/docs/reference/engine/enums/ModerationStatus.md) to any other status. The event fires on the client only. *Security: None · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Description | |------|------|-------------| | `assetId` | `int64` | The asset ID of the asset that has completed moderation. | | `moderationStatus` | `ModerationStatus` | The final [ModerationStatus](/docs/reference/engine/enums/ModerationStatus.md) result after moderation completed. | ```lua local AvatarCreationService = game:GetService("AvatarCreationService") local conn = AvatarCreationService.AvatarAssetModerationCompleted:Connect(function(assetId, moderationStatus) print("Asset, " .. assetId .. ", ready with moderation status: " .. moderationStatus) end) ``` ### Event: AvatarCreationService.AvatarModerationCompleted **Signature:** `AvatarCreationService.AvatarModerationCompleted(outfitId: int64, moderationStatus: ModerationStatus)` Fires when an in-experience-created avatar's moderation status has been updated from pending. This event provides a streamlined way to know when an avatar created through [PromptCreateAvatarAsync()](/docs/reference/engine/classes/AvatarCreationService.md) has completed the moderation process and is ready for use in-experience. Note that this event only fires for avatars created within the current experience and will trigger when the [ModerationStatus](/docs/reference/engine/enums/ModerationStatus.md) changes from [NotReviewed](/docs/reference/engine/enums/ModerationStatus.md) to any other status. The event fires on the client only. [GetOutfitDetailsAsync](/docs/reference/engine/classes/AvatarEditorService.md) can be used on the server to verify the current moderation status if needed. *Security: None · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Description | |------|------|-------------| | `outfitId` | `int64` | The outfit ID of the avatar that has completed moderation. | | `moderationStatus` | `ModerationStatus` | The final [ModerationStatus](/docs/reference/engine/enums/ModerationStatus.md) result after moderation completion. | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: AvatarEditorService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service summary: "A service to support developer Avatar Editors." --- # Class: AvatarEditorService > A service to support developer Avatar Editors. ## Description AvatarEditorService is a service to support developer Avatar Editors. It provides methods to modify the player's platform avatar, request information about a user's inventory, and request information about the catalog. For more information regarding the Avatar Editor, see [Avatar Editor Service](/docs/en-us/players/avatar-editor.md). #### Throttling The following endpoints on `AvatarEditorService` have experience-level throttling: - [AvatarEditorService.GetItemDetailsAsync](/docs/reference/engine/classes/AvatarEditorService.md) - [AvatarEditorService.GetBatchItemDetailsAsync](/docs/reference/engine/classes/AvatarEditorService.md) - [AvatarEditorService.GetRecommendedAssetsAsync](/docs/reference/engine/classes/AvatarEditorService.md) - [AvatarEditorService.GetRecommendedBundlesAsync](/docs/reference/engine/classes/AvatarEditorService.md) - [AvatarEditorService.SearchCatalogAsync](/docs/reference/engine/classes/AvatarEditorService.md) - [AvatarEditorService.PromptSetFavorite](/docs/reference/engine/classes/AvatarEditorService.md) - [AvatarEditorService.GetFavoriteAsync](/docs/reference/engine/classes/AvatarEditorService.md) For each experience, this throttling allows you to send up to 100 requests per second to these `AvatarEditorService` endpoints, regardless of the number of servers or user count. Exceeding these limits returns a `429 Too Many Requests` error. #### Item Details Response The following methods return item data in a shared response format: - [AvatarEditorService:GetItemDetailsAsync()](/docs/reference/engine/classes/AvatarEditorService.md) - [AvatarEditorService:GetBatchItemDetailsAsync()](/docs/reference/engine/classes/AvatarEditorService.md) - [AvatarEditorService:GetBundlesByAssetIdAsync()](/docs/reference/engine/classes/AvatarEditorService.md) - [AvatarEditorService:SearchCatalogAsync()](/docs/reference/engine/classes/AvatarEditorService.md) ```lua { "Id": 0, "ItemType": "Asset", "AssetType": "Image", "BundleType": "BodyParts", "Name": "string", "Description": "string", "ProductId": 0, "ItemStatus": ["New"], "ItemRestrictions": ["Collectible"], "BundledItems": [ { "Id": 0, "Name": "string", "Type": "Asset", "AssetType": "string", "SupportsHeadShapes": false, "Owned": false } ], "IsRecolorable": false, "CollectibleItemId": "string", "TotalQuantity": 0, "UnitsAvailableForConsumption": 0, "QuantityLimitPerUser": 0, "HasResellers": false, "OffSaleDeadline": null, "Price": 0, "PremiumPricing": { "PremiumDiscountPercentage": 0, "PremiumPriceInRobux": 0 }, "LowestPrice": 0, "LowestResalePrice": 0, "PriceStatus": "string", "SaleLocationType": "ShopAndAllExperiences", "PurchaseCount": 0, "FavoriteCount": 0, "CreatorType": "User", "CreatorTargetId": 0, "CreatorName": "string", "CreatorHasVerifiedBadge": false, "SupportsHeadShapes": false } ``` ##### Basic Information | Field | Type | Description | | --- | --- | --- | | `Id` | number | The unique identifier of the item. | | `ItemType` | string | The type of item: `"Asset"` or `"Bundle"`. Corresponds to [AvatarItemType](/docs/reference/engine/enums/AvatarItemType.md). | | `AssetType` | string | The asset type. Corresponds to [AvatarAssetType](/docs/reference/engine/enums/AvatarAssetType.md) values (e.g., `"Hat"`, `"Shirt"`). Only present if `ItemType` is `"Asset"`. | | `SupportsHeadShapes` | boolean | Whether the asset supports head shape swapping. Only present if `AssetType` is `"DynamicHead"`. | | `BundleType` | string | The bundle type. Corresponds to [BundleType](/docs/reference/engine/enums/BundleType.md) values (e.g., `"BodyParts"`, `"Animations"`). Only present if `ItemType` is `"Bundle"`. | | `Name` | string | The display name of the item. | | `Description` | string | The item's description text. | | `ProductId` | number | The product ID associated with this item. | | `ItemStatus` | array | An array of status strings (e.g., `"New"`, `"Sale"`, `"XboxExclusive"`, `"AmazonExclusive"`). | | `ItemRestrictions` | array | An array of restriction strings. See the `itemRestrictions` table below. | ##### Bundle Information | Field | Type | Description | | --- | --- | --- | | `BundledItems` | array | An array of items contained in the bundle. Only present if `ItemType` is `"Bundle"`. Each entry contains: | | | `Id`: The unique identifier of the bundled item. | | | `Name`: The display name of the bundled item. | | | `Type`: The type of the bundled item (e.g., `"Asset"`). | | | `AssetType`: The asset type as a string. Corresponds to [AvatarAssetType](/docs/reference/engine/enums/AvatarAssetType.md) values (e.g., `"Hat"`, `"DynamicHead"`). | | | `SupportsHeadShapes`: Whether the asset supports head shape swapping. Only present if `AssetType` is `"DynamicHead"`. | | | `Owned`: Whether the bundled item is owned by the current user. | | `IsRecolorable` | boolean | Whether the bundle supports skin tone matching. Only applies to BodyParts and DynamicHead bundles. | ##### Collectible Information | Field | Type | Description | | --- | --- | --- | | `CollectibleItemId` | string | The unique item ID of the collectible. | | `TotalQuantity` | number | The total quantity of the collectible available for purchase (not resale). | | `UnitsAvailableForConsumption` | number | The number of units available for purchase. Only applies to Limited items with remaining stock. | | `QuantityLimitPerUser` | number | Maximum number of the same collectible item a user can own. | | `HasResellers` | boolean | `true` when item is a Limited collectible and there are copies available for resale. | | `OffSaleDeadline` | string | The date/time when the item goes off sale. | ##### Pricing and Sale Information | Field | Type | Description | | --- | --- | --- | | `Price` | number | The price in Robux. `0` if free or not for sale. | | `PremiumPricing` | table | Premium pricing information. Contains the following fields: | | | `PremiumDiscountPercentage`: The discount percentage for Premium members. | | | `PremiumPriceInRobux`: The discounted price in Robux for Premium members. | | `LowestPrice` | number | The lowest resale price for Limited items. | | `LowestResalePrice` | number | The lowest resale price for the collectible in Robux. | | `PriceStatus` | string | The price status (e.g., `"Free"`, `"Off Sale"`, `"No Resellers"`). | | `SaleLocationType` | string | The type of sale location setting. See `SaleLocationType` values below. | | `PurchaseCount` | number | The total number of times this item has been purchased. | | `FavoriteCount` | number | The total number of users who have favorited this item. | ##### Creator Information | Field | Type | Description | | --- | --- | --- | | `CreatorType` | string | Either `User` or `Group`. See [CreatorType](/docs/reference/engine/enums/CreatorType.md). | | `CreatorTargetId` | number | The ID of the creator user or group. | | `CreatorName` | string | The display name of the creator. | | `CreatorHasVerifiedBadge` | boolean | Boolean of whether the creator has a verified badge. | ##### SaleLocationType Values | Value | Description | | :----------------------- | :------------------------------------------------------------------------------------- | | `NotApplicable` | Default value, should not occur in practice. | | `ShopOnly` | Item can only be purchased in the Roblox catalog shop. | | `MyExperiencesOnly` | Item can only be purchased in the creator's experiences. | | `ShopAndMyExperiences` | Item can be purchased in the Roblox catalog shop or the creator's experiences. | | `ExperiencesById` | Item can only be purchased in a specific list of experiences. | | `ShopAndAllExperiences` | Item can be purchased in the Roblox catalog shop and all experiences. | | `ExperiencesDevApiOnly` | Item can only be purchased in experiences via developer APIs. | | `ShopAndExperiencesById` | Item can be purchased in the Roblox catalog shop or a whitelisted list of experiences. | ##### itemRestrictions Values | itemRestrictions | Limited or Unlimited | | :--------------: | :-------------------: | | `empty` | Unlimited | | `Collectible` | UGC Limited | | `Limited` | Roblox Limited | | `LimitedUnique` | Roblox Limited Unique | ## Methods ### Method: AvatarEditorService:CheckApplyDefaultClothingAsync **Signature:** `AvatarEditorService:CheckApplyDefaultClothingAsync(humanoidDescription: HumanoidDescription): HumanoidDescription` Returns a new [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) with the Shirt and Pants properties updated if necessary. Returns `nil` if default clothing was not needed. Default clothing is necessary if the HumanoidDescription does not currently have Shirt and Pants equipped and the body colors are too similar. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AvatarAppearance* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `humanoidDescription` | `HumanoidDescription` | | The HumanoidDescription to check if default clothing is required. | **Returns:** `HumanoidDescription` — Returns a HumanoidDescription if default clothing was necessary. Otherwise returns `nil`. ### Method: AvatarEditorService:ConformToAvatarRulesAsync **Signature:** `AvatarEditorService:ConformToAvatarRulesAsync(humanoidDescription: HumanoidDescription): HumanoidDescription` This method also remaps classic face and classic head assets to their corresponding Dynamic Head asset IDs, with the appropriate [HeadShape](/docs/reference/engine/classes/BodyPartDescription.md) pre-applied. This remapping happens automatically and requires no code changes. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AvatarAppearance* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `humanoidDescription` | `HumanoidDescription` | | The [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) to conform. | **Returns:** `HumanoidDescription` — A new [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) that conforms to the platform Avatar rules. ### Method: AvatarEditorService:GetAccessoryType **Signature:** `AvatarEditorService:GetAccessoryType(avatarAssetType: AvatarAssetType): AccessoryType` *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `avatarAssetType` | `AvatarAssetType` | | | **Returns:** `AccessoryType` ### Method: AvatarEditorService:GetAvatarRulesAsync **Signature:** `AvatarEditorService:GetAvatarRulesAsync(): Dictionary` This function returns the platform Avatar rules for things like scaling, default shirts and pants, number of wearable assets, ect. The returned table includes the following fields: ```lua { "PlayerAvatarTypes": [ "R6" ], "Scales": {}, "WearableAssetTypes": [ { "MaxNumber": 0, "Id": 0, "Name": "string" } ], "BodyColorsPalette": [ { "BrickColorId": 0, "NexColor": "string", "Name": "string" } ], "BasicBodyColorsPalette": [ { "BrickColorId": 0, "HexColor": "string", "Name": "string" } ], "MinimumDeltaEBodyColorDifference": 0, "ProportionsAndBodyTypeEnabledForUser": true, "DefaultClothingAssetLists": { "DefaultShirtAssetIds": [ 0 ], "DefaultPantAssetIds": [ 0 ] }, "BundlesEnabledForUser": true, "EmotesEnabledForUser": true } ``` *Yields · Security: None · Thread Safety: Unsafe* **Returns:** `Dictionary` — A dictionary containing the platform Avatar rules for things like scaling, default shirts and pants, number of wearable assets, ect. See the example return in the main description above. ### Method: AvatarEditorService:GetBatchItemDetailsAsync **Signature:** `AvatarEditorService:GetBatchItemDetailsAsync(itemIds: Array, itemType: AvatarItemType): Array` Gets the item details for a list of items at once. More efficient than [AvatarEditorService:GetItemDetailsAsync()](/docs/reference/engine/classes/AvatarEditorService.md) if you need to get the item details for multiple items. Returns an array of items (see Item Details Response above in summary). *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetRead* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `itemIds` | `Array` | | The list of item ids to get details of. | | `itemType` | `AvatarItemType` | | The type of the item ids provided. | **Returns:** `Array` — Returns an array of item details. ### Method: AvatarEditorService:GetBundlesByAssetIdAsync **Signature:** `AvatarEditorService:GetBundlesByAssetIdAsync(assetId: int64, limit?: int64): CatalogPages` Returns a paginated list of bundles that contain the specified asset. Results are returned in ascending order by date created. Each page entry uses the **Item Details Response** format (see service description above). *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetRead* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `assetId` | `int64` | | The ID of the asset to find bundles for. | | `limit` | `int64` | `10` | The number of results per page. Accepts `10`, `25`, `50`, and `100`. | **Returns:** `CatalogPages` — A [CatalogPages](/docs/reference/engine/classes/CatalogPages.md) object containing bundles that include the given asset. ### Method: AvatarEditorService:GetFavoriteAsync **Signature:** `AvatarEditorService:GetFavoriteAsync(itemId: int64, itemType: AvatarItemType): boolean` This function returns if the [Players.LocalPlayer](/docs/reference/engine/classes/Players.md) has favorited the given bundle or asset. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Players, AssetRead* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `itemId` | `int64` | | The ID of the specified asset or bundle. | | `itemType` | `AvatarItemType` | | The [AvatarItemType](/docs/reference/engine/enums/AvatarItemType.md) of the specified asset or bundle. | **Returns:** `boolean` — Whether the `LocalPlayer` has favorited the given bundle or asset. ### Method: AvatarEditorService:GetHeadShapesAsync **Signature:** `AvatarEditorService:GetHeadShapesAsync(): Array` Each head shape corresponds to a classic head owned by the user and can be applied to any Dynamic Head that supports shape swapping (see [BodyPartDescription.HeadShape](/docs/reference/engine/classes/BodyPartDescription.md)). This method requires the user to have accepted the [AvatarEditorService:PromptAllowInventoryReadAccess()](/docs/reference/engine/classes/AvatarEditorService.md) prompt. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Players, AssetRead* **Returns:** `Array` — An array of strings, each identifying a head shape owned by [Players.LocalPlayer](/docs/reference/engine/classes/Players.md). Possible values include: `"RobloxClassic"`, `"Blockhead"`, `"Cheeks"`, `"Chiseled"`, `"CoolThing"`, `"EraserHead"`, `"FatHead"`, `"FlatTop"`, `"GoldenKorbloxGeneral"`, `"GoldenMrRobot"`, `"KnightOfChivalry"`, `"KnightOfCourage"`, `"ManHead"`, `"MercilessNinja"`, `"Narrow"`, `"Paragon"`, `"Peabrain"`, `"Perfection"`, `"Roll"`, `"Roundy"`, `"RoxBox"`, `"TheEngineer"`, `"Trim"`, `"WomanHead"`. **Getting owned head shapes** This returns a list of head shapes. Each head shape corresponds to a classic head owned by the user and can be applied to any Dynamic Head that supports shape swapping. ```lua TEST local AvatarEditorService = game:GetService("AvatarEditorService") AvatarEditorService:PromptAllowInventoryReadAccess() local result = AvatarEditorService.PromptAllowInventoryReadAccessCompleted:Wait() if result == Enum.AvatarPromptResult.Success then local shapes = AvatarEditorService:GetHeadShapesAsync() for i, shape in ipairs(shapes) do print(i, shape) end end ``` ### Method: AvatarEditorService:GetInventoryAsync **Signature:** `AvatarEditorService:GetInventoryAsync(assetTypes: Array): InventoryPages` Returns an [InventoryPages](/docs/reference/engine/classes/InventoryPages.md) object with information about owned items in the users inventory with the given [AvatarAssetTypes](/docs/reference/engine/enums/AvatarAssetType.md). The returned table includes the following fields: ```lua [ { "AssetId": 0, "AssetType" : "string", "Created": "string", "Name": "string", } ] ``` *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Players, AssetRead* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `assetTypes` | `Array` | | The [AvatarAssetType](/docs/reference/engine/enums/AvatarAssetType.md) that can will be checked for in the player's inventory. | **Returns:** `InventoryPages` ### Method: AvatarEditorService:GetItemDetailsAsync **Signature:** `AvatarEditorService:GetItemDetailsAsync(itemId: int64, itemType: AvatarItemType): Dictionary` This function returns the item details for the given item. It accepts two parameters - the first indicating the ID of the item being retrieved and the second indicating its [AvatarItemType](/docs/reference/engine/enums/AvatarItemType.md). The response includes all fields from Item Details Response (see above in summary), plus the following additional fields specific to single-item queries: | Field | Type | Description | | --- | --- | --- | | `Owned` | boolean | Whether the item is owned by the current user. | | `IsPurchasable` | boolean | Whether the item can be purchased by the current user. | | `ExpectedSellerId` | number | The user ID of the lowest private seller if resellable, or the creator's target ID otherwise. Used when calling purchase APIs. | | `CreatingUniverseId` | number | If this asset was created in an experience, this is the universe ID where it was created. `nil` if not an in-experience creation. | *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetRead* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `itemId` | `int64` | | The ID of the item whose details are being retrieved. | | `itemType` | `AvatarItemType` | | An enum value indicating the type of item whose details are being retrieved. | **Returns:** `Dictionary` — A table containing the item info for the retrieved item. ### Method: AvatarEditorService:GetOutfitDetailsAsync **Signature:** `AvatarEditorService:GetOutfitDetailsAsync(outfitId: int64): Dictionary` This function returns the outfit details for the given outfit. It accepts one parameter: the ID of the outfit. Data returns in the following format: ```lua { "Assets": [ { "AssetType": { "Id": 31, "Name": "RightLeg" }, "CurrentVersionId": 16447385805, "Id": 11584239464, "Name": "Anime Female - Right Leg", "SupportsHeadShapes": false } ], "BodyColors": { "HeadColor": Color3(204, 142, 105), "LeftArmColor": Color3(204, 142, 105), "LeftLegColor": Color3(204, 142, 105), "RightArmColor": Color3(204, 142, 105), "RightLegColor": Color3(204, 142, 105), "TorsoColor": Color3(204, 142, 105) }, "Id": 14703770624, "IsEditable": true, "Name": "Your Costume", "OutfitType": "Avatar", "PlayerAvatarType": "R15", "Scale": { "BodyType": 0, "Depth": 1, "Head": 1, "Height": 1, "Proportion": 0, "Width": 1 }, } ``` *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetRead* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `outfitId` | `int64` | | The ID of the outfit whose details are being retrieved. | **Returns:** `Dictionary` — A table containing the outfit info for the retrieved outfit. See above for a sample table. ### Method: AvatarEditorService:GetOutfitsAsync **Signature:** `AvatarEditorService:GetOutfitsAsync(outfitSource?: OutfitSource, outfitType?: OutfitType): OutfitPages` This function returns outfit data for the [Players.LocalPlayer](/docs/reference/engine/classes/Players.md). This would be used with [Players:GetHumanoidDescriptionFromOutfitIdAsync()](/docs/reference/engine/classes/Players.md) to update the players character to the outfit. Access to this would also depend on [AvatarEditorService:PromptAllowInventoryReadAccess()](/docs/reference/engine/classes/AvatarEditorService.md) being accepted by the user. The returned table includes the following fields: ```lua [ { "Id": 0, "Name": "string", "IsEditable": true } ] ``` | Name | type | Description | | --- | --- | --- | | id | int | | | name | string | | | isEditable | boolean | | *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Players, AssetRead* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `outfitSource` | `OutfitSource` | `All` | | | `outfitType` | `OutfitType` | `All` | | **Returns:** `OutfitPages` ### Method: AvatarEditorService:GetRecommendedAssetsAsync **Signature:** `AvatarEditorService:GetRecommendedAssetsAsync(assetType: AvatarAssetType, contextAssetId?: int64): Array` Returns a list of recommended assets based on a given [AssetType](/docs/reference/engine/enums/AssetType.md) and asset ID. Use this to gather a list of similar assets to the asset provided. Take a look at the code sample below for more information on possible usages for this function. Data is in the format: ```lua [ { "Item": { "AssetId": 0, "Name": "string", "Price": 0, "PremiumPrice": 0 }, "Creator": { "CreatorId": 0, "CreatorType": "string", "Name": "string" }, "Product": { "Id": 0, "PriceInRobux": 0, "IsForSale": true, "IsResellable": true, "IsLimited": true, "IsLimitedUnique": true, "TotalPrivateSales": 0, "OffsaleDeadline": "string", "IsFree": true } } ] ``` *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetRead* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `assetType` | `AvatarAssetType` | | The type of asset recommendations to retrieve recommendations for. Only affects the response when item based recommendations don't exist for the given `contextAssetId`. | | `contextAssetId` | `int64` | `0` | The ID of an asset with a type matching the provided assetType used for context when retrieving recommendations. | **Returns:** `Array` — A list of recommendations based on the given [AssetType](/docs/reference/engine/enums/AssetType.md). **Getting a Hat Recommendation** This will return a list of similar hats much like how similar assets are displayed when viewing the catalog page on the website. The contextAssetId is optional and if not provided it will return some popular items from that category. ```lua local AvatarEditorService = game:GetService("AvatarEditorService") local assets = AvatarEditorService:GetRecommendedAssetsAsync(Enum.AvatarAssetType.Hat, 9255093) for _, asset in ipairs(assets) do print(asset.Item.Name) end ``` ### Method: AvatarEditorService:GetRecommendedBundlesAsync **Signature:** `AvatarEditorService:GetRecommendedBundlesAsync(bundleId: int64): Array` This function returns a list of recommended bundles for a given bundle id. Data is in the format: ```lua [ { "Id": 0, "Name": "string", "Description": "string", "BundleType": "string", "Items": [ { "Owned": true, "Id": 0, "Name": "string", "Type": "string", "AssetType": "string", "SupportsHeadShapes": false } ], "Creator": { "Id": 0, "Name": "string", "Type": "string" }, "Product": { "Id": 0, "Type": "string", "IsPublicDomain": true, "IsForSale": true, "PriceInRobux": 0, "PremiumPricing": { "PremiumDiscountPercentage": 0, "PremiumPriceInRobux": 0 } } } ] ``` *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetRead* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `bundleId` | `int64` | | A list of recommended bundles. | **Returns:** `Array` — The bundle ID that the recommended bundles will be returned for. ### Method: AvatarEditorService:PromptAllowInventoryReadAccess **Signature:** `AvatarEditorService:PromptAllowInventoryReadAccess(): ()` Prompts the [Players.LocalPlayer](/docs/reference/engine/classes/Players.md) to allow the developer to read what items the user has in their inventory and other avatar editor related information. The prompt needs to be confirmed by the user for the developer to use [AvatarEditorService:GetInventoryAsync()](/docs/reference/engine/classes/AvatarEditorService.md), [AvatarEditorService:GetOutfitsAsync()](/docs/reference/engine/classes/AvatarEditorService.md) and [AvatarEditorService:GetFavoriteAsync()](/docs/reference/engine/classes/AvatarEditorService.md). Permission does not persist between sessions. *Security: None · Thread Safety: Unsafe · Capabilities: Players, AssetRead* **Returns:** `()` ### Method: AvatarEditorService:PromptCreateOutfit **Signature:** `AvatarEditorService:PromptCreateOutfit(outfit: HumanoidDescription, rigType: HumanoidRigType, outfitOptions?: Dictionary, outfitType: Variant): ()` Prompts the [Players.LocalPlayer](/docs/reference/engine/classes/Players.md) to save the given [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) as an outfit. Does not yield. The result can be retrieved by listening to the [AvatarEditorService.PromptCreateOutfitCompleted](/docs/reference/engine/classes/AvatarEditorService.md) event. *Security: None · Thread Safety: Unsafe · Capabilities: Players, PlatformAvatarEditing* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `outfit` | `HumanoidDescription` | | The `Outfit` that the player will be prompted to created. | | `rigType` | `HumanoidRigType` | | The [RigType](/docs/reference/engine/enums/RigType.md) that the outfit will be created for if the player confirms the prompt. | | `outfitOptions` | `Dictionary` | `nil` | Reserved for future options. Must be empty or omitted. | | `outfitType` | `Variant` | | Optional [OutfitType](/docs/reference/engine/enums/OutfitType.md). Only `Avatar` and `Makeup` values are accepted. When omitted, the type is inferred from the description. | **Returns:** `()` ### Method: AvatarEditorService:PromptDeleteOutfit **Signature:** `AvatarEditorService:PromptDeleteOutfit(outfitId: int64): ()` Prompts the [Players.LocalPlayer](/docs/reference/engine/classes/Players.md) to delete the given outfit. Does not yield. The result can be retrieved by listening to the [AvatarEditorService.PromptDeleteOutfitCompleted](/docs/reference/engine/classes/AvatarEditorService.md) event. *Security: None · Thread Safety: Unsafe · Capabilities: Players, PlatformAvatarEditing* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `outfitId` | `int64` | | The outfitId of the outfit to delete. | **Returns:** `()` ### Method: AvatarEditorService:PromptRenameOutfit **Signature:** `AvatarEditorService:PromptRenameOutfit(outfitId: int64): ()` Prompts the [Players.LocalPlayer](/docs/reference/engine/classes/Players.md) to rename the given outfit. Does not yield. The result can be retrieved by listening to the [AvatarEditorService.PromptRenameOutfitCompleted](/docs/reference/engine/classes/AvatarEditorService.md) event. *Security: None · Thread Safety: Unsafe · Capabilities: Players, PlatformAvatarEditing* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `outfitId` | `int64` | | The outfitId of the outfit to rename. | **Returns:** `()` ### Method: AvatarEditorService:PromptSaveAvatar **Signature:** `AvatarEditorService:PromptSaveAvatar(humanoidDescription: HumanoidDescription, rigType: HumanoidRigType): ()` This function prompts the [Players.LocalPlayer](/docs/reference/engine/classes/Players.md) to update their avatar based on the given [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) and [RigType](/docs/reference/engine/enums/RigType.md) (R6 or R15). Does not yield and can get the result by listening to the PromptSaveAvatarCompleted event. This is similar to how other prompts such as PromptPurchase work. *Security: None · Thread Safety: Unsafe · Capabilities: Players, PlatformAvatarEditing* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `humanoidDescription` | `HumanoidDescription` | | The given [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) being prompted to save. | | `rigType` | `HumanoidRigType` | | The [RigType](/docs/reference/engine/enums/RigType.md) that the avatar will be saved for if the player confirms the prompt. | **Returns:** `()` ### Method: AvatarEditorService:PromptSetFavorite **Signature:** `AvatarEditorService:PromptSetFavorite(itemId: int64, itemType: AvatarItemType, shouldFavorite: boolean): ()` This function prompts the [Players.LocalPlayer](/docs/reference/engine/classes/Players.md) to favorite or unfavorite the given asset or bundle. *Security: None · Thread Safety: Unsafe · Capabilities: Players, PlatformAvatarEditing* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `itemId` | `int64` | | The ItemId of the item being prompted to favorite. | | `itemType` | `AvatarItemType` | | The type of item being prompted to favorite. | | `shouldFavorite` | `boolean` | | | **Returns:** `()` ### Method: AvatarEditorService:PromptUpdateOutfit **Signature:** `AvatarEditorService:PromptUpdateOutfit(outfitId: int64, updatedOutfit: HumanoidDescription, rigType: HumanoidRigType): ()` Prompts the [Players.LocalPlayer](/docs/reference/engine/classes/Players.md) to update the given outfit with the given HumanoidDescription. *Security: None · Thread Safety: Unsafe · Capabilities: Players, PlatformAvatarEditing* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `outfitId` | `int64` | | The outfitId of the outfit to update. | | `updatedOutfit` | `HumanoidDescription` | | A HumanoidDescription that represents the new outfit data. | | `rigType` | `HumanoidRigType` | | The HumanoidRigType to update the outfit to. | **Returns:** `()` ### Method: AvatarEditorService:SearchCatalogAsync **Signature:** `AvatarEditorService:SearchCatalogAsync(searchParameters: CatalogSearchParams): CatalogPages` This function returns a [CatalogPages](/docs/reference/engine/classes/CatalogPages.md) object containing the result of the given search. Each item in the returned pages uses the Item Details Response format (see above in summary). *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetRead* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `searchParameters` | `CatalogSearchParams` | | An object containing the parameters used for the search. | **Returns:** `CatalogPages` — A [CatalogPages](/docs/reference/engine/classes/CatalogPages.md) object containing the search results. ### Method: AvatarEditorService:CheckApplyDefaultClothing **Signature:** `AvatarEditorService:CheckApplyDefaultClothing(humanoidDescription: HumanoidDescription): HumanoidDescription` Used to apply default clothing to the [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) if necessary. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AvatarAppearance* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `humanoidDescription` | `HumanoidDescription` | | The HumanoidDescription to check if default clothing is required. | **Returns:** `HumanoidDescription` — Returns a HumanoidDescription if default clothing was necessary. Otherwise returns `nil`. ### Method: AvatarEditorService:ConformToAvatarRules **Signature:** `AvatarEditorService:ConformToAvatarRules(humanoidDescription: HumanoidDescription): HumanoidDescription` *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AvatarAppearance* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `humanoidDescription` | `HumanoidDescription` | | | **Returns:** `HumanoidDescription` ### Method: AvatarEditorService:GetAvatarRules **Signature:** `AvatarEditorService:GetAvatarRules(): Dictionary` Returns the platform Avatar rules for things such as scaling, default shirts and pants, number of wearable assets. *Yields · Security: None · Thread Safety: Unsafe* **Returns:** `Dictionary` — A dictionary containing the platform Avatar rules for things like scaling, default shirts and pants, number of wearable assets, ect. See the example return in the main description above. ### Method: AvatarEditorService:GetBatchItemDetails **Signature:** `AvatarEditorService:GetBatchItemDetails(itemIds: Array, itemType: AvatarItemType): Array` Gets the item details for a list of items at once. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetRead* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `itemIds` | `Array` | | The list of item ids to get details of. | | `itemType` | `AvatarItemType` | | The type of the item ids provided. | **Returns:** `Array` — Returns an array of item details. See [AvatarEditorService:GetBatchItemDetailsAsync()](/docs/reference/engine/classes/AvatarEditorService.md) for the response format. ### Method: AvatarEditorService:GetFavorite **Signature:** `AvatarEditorService:GetFavorite(itemId: int64, itemType: AvatarItemType): boolean` Returns if the [Players.LocalPlayer](/docs/reference/engine/classes/Players.md) has favorited the given bundle or asset. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Players, AssetRead* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `itemId` | `int64` | | The ID of the specified asset or bundle. | | `itemType` | `AvatarItemType` | | The [AvatarItemType](/docs/reference/engine/enums/AvatarItemType.md) of the specified asset or bundle. | **Returns:** `boolean` — Whether the `LocalPlayer` has favorited the given bundle or asset. ### Method: AvatarEditorService:GetInventory **Signature:** `AvatarEditorService:GetInventory(assetTypes: Array): InventoryPages` Returns an [InventoryPages](/docs/reference/engine/classes/InventoryPages.md) object with information about owned items in the users inventory with the given AvatarAssetTypes. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Players, AssetRead* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `assetTypes` | `Array` | | The [AvatarAssetType](/docs/reference/engine/enums/AvatarAssetType.md) that can will be checked for in the player's inventory. | **Returns:** `InventoryPages` ### Method: AvatarEditorService:GetItemDetails **Signature:** `AvatarEditorService:GetItemDetails(itemId: int64, itemType: AvatarItemType): Dictionary` Returns the item details for the given item. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetRead* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `itemId` | `int64` | | The ID of the item whose details are being retrieved. | | `itemType` | `AvatarItemType` | | An enum value indicating the type of item whose details are being retrieved. | **Returns:** `Dictionary` — A table containing the item info for the retrieved item. See above for a sample table. ### Method: AvatarEditorService:GetOutfitDetails **Signature:** `AvatarEditorService:GetOutfitDetails(outfitId: int64): Dictionary` Returns the outfit details for the given outfit. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetRead* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `outfitId` | `int64` | | The ID of the outfit whose details are being retrieved. | **Returns:** `Dictionary` — A table containing the outfit info for the retrieved outfit. See above for a sample table. ### Method: AvatarEditorService:GetOutfits **Signature:** `AvatarEditorService:GetOutfits(outfitSource?: OutfitSource, outfitType?: OutfitType): OutfitPages` Returns outfit data for the [Players.LocalPlayer](/docs/reference/engine/classes/Players.md). *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Players, AssetRead* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `outfitSource` | `OutfitSource` | `All` | | | `outfitType` | `OutfitType` | `All` | | **Returns:** `OutfitPages` ### Method: AvatarEditorService:GetRecommendedAssets **Signature:** `AvatarEditorService:GetRecommendedAssets(assetType: AvatarAssetType, contextAssetId?: int64): Array` Returns a list of recommended assets based on a given [AssetType](/docs/reference/engine/enums/AssetType.md) and asset ID. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetRead* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `assetType` | `AvatarAssetType` | | The type of asset recommendations to retrieve recommendations for. Only affects the response when item based recommendations don't exist for the given `contextAssetId`. | | `contextAssetId` | `int64` | `0` | The ID of an asset with a type matching the provided assetType used for context when retrieving recommendations. | **Returns:** `Array` — A list of recommendations based on the given [AssetType](/docs/reference/engine/enums/AssetType.md). **Getting a Hat Recommendation** This will return a list of similar hats much like how similar assets are displayed when viewing the catalog page on the website. The contextAssetId is optional and if not provided it will return some popular items from that category. ```lua local AvatarEditorService = game:GetService("AvatarEditorService") local assets = AvatarEditorService:GetRecommendedAssetsAsync(Enum.AvatarAssetType.Hat, 9255093) for _, asset in ipairs(assets) do print(asset.Item.Name) end ``` ### Method: AvatarEditorService:GetRecommendedBundles **Signature:** `AvatarEditorService:GetRecommendedBundles(bundleId: int64): Array` Returns a list of recommended bundles for a given bundle id. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetRead* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `bundleId` | `int64` | | A list of recommended bundles. | **Returns:** `Array` — The bundle ID that the recommended bundles will be returned for. ### Method: AvatarEditorService:SearchCatalog **Signature:** `AvatarEditorService:SearchCatalog(searchParameters: CatalogSearchParams): CatalogPages` Returns a [CatalogPages](/docs/reference/engine/classes/CatalogPages.md) object containing the result of the given search. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetRead* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `searchParameters` | `CatalogSearchParams` | | An object containing the parameters used for the search. | **Returns:** `CatalogPages` ## Events ### Event: AvatarEditorService.PromptAllowInventoryReadAccessCompleted **Signature:** `AvatarEditorService.PromptAllowInventoryReadAccessCompleted(result: AvatarPromptResult)` This event fires when the [AvatarEditorService:PromptAllowInventoryReadAccess()](/docs/reference/engine/classes/AvatarEditorService.md) prompt is responded to by the user. It can only return the Success or PermissionDenied [enum](/docs/reference/engine/enums/AvatarPromptResult.md) statuses as it does not perform any web requests which could fail. *Security: None · Capabilities: Players, AssetRead* **Parameters:** | Name | Type | Description | |------|------|-------------| | `result` | `AvatarPromptResult` | The result of the prompt. | ### Event: AvatarEditorService.PromptCreateOutfitCompleted **Signature:** `AvatarEditorService.PromptCreateOutfitCompleted(result: AvatarPromptResult, failureType: Variant)` This event fires when the PromptSaveOutfit operation is completed. It gives a status [enum](/docs/reference/engine/enums/AvatarPromptResult.md) indicating whether the prompt succeeded, failed or permission was not granted by the user. *Security: None · Capabilities: Players, PlatformAvatarEditing* **Parameters:** | Name | Type | Description | |------|------|-------------| | `result` | `AvatarPromptResult` | The result of the prompt. | | `failureType` | `Variant` | | ### Event: AvatarEditorService.PromptDeleteOutfitCompleted **Signature:** `AvatarEditorService.PromptDeleteOutfitCompleted(result: AvatarPromptResult)` Fires when the PromptDeleteOutfit operation is completed. It gives a status [enum](/docs/reference/engine/enums/AvatarPromptResult.md) indicating whether the prompt succeeded, failed or permission was not granted by the user. *Security: None · Capabilities: Players, PlatformAvatarEditing* **Parameters:** | Name | Type | Description | |------|------|-------------| | `result` | `AvatarPromptResult` | The result of the prompt. | ### Event: AvatarEditorService.PromptRenameOutfitCompleted **Signature:** `AvatarEditorService.PromptRenameOutfitCompleted(result: AvatarPromptResult)` Fires when the PromptRenameOutfit operation is completed. It gives a status [enum](/docs/reference/engine/enums/AvatarPromptResult.md) indicating whether the prompt succeeded, failed or permission was not granted by the user. *Security: None · Capabilities: Players, PlatformAvatarEditing* **Parameters:** | Name | Type | Description | |------|------|-------------| | `result` | `AvatarPromptResult` | The result of the prompt. | ### Event: AvatarEditorService.PromptSaveAvatarCompleted **Signature:** `AvatarEditorService.PromptSaveAvatarCompleted(result: AvatarPromptResult, humanoidDescription: HumanoidDescription)` This event fires when the [AvatarEditorService:PromptSaveAvatar()](/docs/reference/engine/classes/AvatarEditorService.md) operation is completed. It gives a status [enum](/docs/reference/engine/enums/AvatarPromptResult.md) indicating whether the prompt succeeded, failed or permission was not granted by the user. *Security: None · Capabilities: Players, PlatformAvatarEditing* **Parameters:** | Name | Type | Description | |------|------|-------------| | `result` | `AvatarPromptResult` | The result of the prompt. | | `humanoidDescription` | `HumanoidDescription` | | ### Event: AvatarEditorService.PromptSetFavoriteCompleted **Signature:** `AvatarEditorService.PromptSetFavoriteCompleted(result: AvatarPromptResult)` Fires when the [AvatarEditorService:PromptSetFavorite()](/docs/reference/engine/classes/AvatarEditorService.md) operation is completed. It gives a status [enum](/docs/reference/engine/enums/AvatarPromptResult.md) indicating whether the prompt succeeded, failed or permission was not granted by the user. *Security: None · Capabilities: Players, PlatformAvatarEditing* **Parameters:** | Name | Type | Description | |------|------|-------------| | `result` | `AvatarPromptResult` | The result of the prompt. | ### Event: AvatarEditorService.PromptUpdateOutfitCompleted **Signature:** `AvatarEditorService.PromptUpdateOutfitCompleted(result: AvatarPromptResult)` Fires when the [AvatarEditorService:PromptUpdateOutfit()](/docs/reference/engine/classes/AvatarEditorService.md) operation is completed. It gives a status [enum](/docs/reference/engine/enums/AvatarPromptResult.md) indicating whether the prompt succeeded, failed or permission was not granted by the user. *Security: None · Capabilities: Players, PlatformAvatarEditing* **Parameters:** | Name | Type | Description | |------|------|-------------| | `result` | `AvatarPromptResult` | The result of the prompt. | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Backpack last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances summary: "A container object that holds a player's inventory. Any Tool in a player's `Backpack` will be displayed in their inventory at the bottom of the screen." --- # Class: Backpack > A container object that holds a player's inventory. Any [Tool](/docs/reference/engine/classes/Tool.md) in a > player's `Backpack` will be displayed in their inventory at the bottom of the > screen. ## Description [Tool](/docs/reference/engine/classes/Tool.md) objects in the `Backpack` container make up the player's inventory and display as icon buttons at the bottom of the screen. See [in‑game tools](/docs/en-us/players/tools.md) for more information. When a player's character spawns, the contents of [StarterPack](/docs/reference/engine/classes/StarterPack.md) and the player's [StarterGear](/docs/reference/engine/classes/StarterGear.md) clone to `Backpack`. When the character dies, the client destroys the `Backpack` and replaces it with a new one, populated again with the contents of [StarterPack](/docs/reference/engine/classes/StarterPack.md) and [StarterGear](/docs/reference/engine/classes/StarterGear.md). To disable the default Roblox inventory GUI and replace it with your own, call [StarterGui:SetCoreGuiEnabled()](/docs/reference/engine/classes/StarterGui.md) in a [LocalScript](/docs/reference/engine/classes/LocalScript.md) as outlined in [disable default UI](/docs/en-us/players/disable-ui.md). `Backpack` can be accessed from both the client and the server: ```lua local Players = game:GetService("Players") -- Accessing Backpack from a server script local backpack = Players.PlayerName.Backpack -- Accessing Backpack from a client script local backpack = Players.LocalPlayer.Backpack ``` ## Code Samples **Backpack Give Tool** This sample includes a simple function demonstrating how a [Tool](/docs/reference/engine/classes/Tool.md) can be given to a [Player](/docs/reference/engine/classes/Player.md) by parenting it to their [Backpack](/docs/reference/engine/classes/Backpack.md). ```lua local Players = game:GetService("Players") local function onCharacterAdded(character) local player = Players:GetPlayerFromCharacter(character) local tool = Instance.new("Tool") local backpack = player:FindFirstChildOfClass("Backpack") tool.Parent = backpack end local function onPlayerAdded(player) if player.Character then onCharacterAdded(player.Character) end player.CharacterAdded:Connect(onCharacterAdded) end Players.PlayerAdded:Connect(onPlayerAdded) ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: BackpackItem last_updated: 2026-06-29T19:34:07Z inherits: - Model - PVInstance - Instance - Object type: class memory_category: BaseParts tags: - NotCreatable summary: "BackpackItem is an abstract class for backpack items such as HopperBins and Tools." --- # Class: BackpackItem > BackpackItem is an abstract class for backpack items such as HopperBins and > Tools. ## Properties ### Property: BackpackItem.TextureContent ```json { "type": "Content", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` The texture icon that is displayed for a tool in the player's backpack. This property should be set to the content ID of an image uploaded to the Roblox website. Only asset URIs are supported for this property. If this property is left blank, the [Backpack](/docs/reference/engine/classes/Backpack.md) GUI will display the name of the tool instead. ### Property: BackpackItem.TextureId ```json { "type": "ContentId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` The texture icon that is displayed for a tool in the player's backpack. This property should be set to the content ID of an image uploaded to the Roblox website. If this property is left blank, the [Backpack](/docs/reference/engine/classes/Backpack.md) GUI will display the name of the tool instead. ## Inherited Members ### From [Model](/docs/reference/engine/classes/Model.md) - **Property `LevelOfDetail`** (`ModelLevelOfDetail`): Sets the level of detail on the model for experiences with instance - **Property `ModelStreamingMode`** (`ModelStreamingMode`): Controls the model streaming behavior on Models when - **Property `PrimaryPart`** (`BasePart`): The primary part of the Model, or `nil` if not explicitly set. - **Property `Scale`** (`float`): Editor-only property used to scale the model around its pivot. Setting - **Property `WorldPivot`** (`CFrame`): Determines where the pivot of a Model which does **not** have a - **Method `AddPersistentPlayer(playerInstance?: Player): ()`**: Sets this model to be persistent for the specified player. - **Method `BreakJoints(): ()`**: Breaks connections between `BaseParts`, including surface connections with *(deprecated)* - **Method `breakJoints(): ()`**: *(deprecated)* - **Method `GetBoundingBox(): Tuple`**: Returns a description of a volume that contains all parts of a Model. - **Method `GetExtentsSize(): Vector3`**: Returns the size of the smallest bounding box that contains all of the - **Method `GetModelCFrame(): CFrame`**: This value historically returned the CFrame of a central position in the *(deprecated)* - **Method `GetModelSize(): Vector3`**: Returns the Vector3 size of the Model. *(deprecated)* - **Method `GetPersistentPlayers(): List`**: Returns all the Player objects that this model object is - **Method `GetPrimaryPartCFrame(): CFrame`**: Returns the CFrame of the model's Model.PrimaryPart. *(deprecated)* - **Method `GetScale(): float`**: Returns the canonical scale of the model, which defaults to 1 for newly - **Method `MakeJoints(): ()`**: Goes through all BaseParts in the Model. If any *(deprecated)* - **Method `makeJoints(): ()`**: *(deprecated)* - **Method `move(location: Vector3): ()`**: *(deprecated)* - **Method `MoveTo(position: Vector3): ()`**: Moves the PrimaryPart to the given position. If - **Method `moveTo(location: Vector3): ()`**: *(deprecated)* - **Method `RemovePersistentPlayer(playerInstance?: Player): ()`**: Makes this model no longer persistent for the specified player. - **Method `ResetOrientationToIdentity(): ()`**: Resets the rotation of the model's parts to the previously set identity *(deprecated)* - **Method `ScaleTo(newScaleFactor: float): ()`**: Sets the scale factor of the model, adjusting the sizing and location of - **Method `SetIdentityOrientation(): ()`**: Sets the identity rotation of the given model, allowing you to reset the *(deprecated)* - **Method `SetPrimaryPartCFrame(cframe: CFrame): ()`**: Sets the BasePart.CFrame of the model's Model.PrimaryPart. *(deprecated)* - **Method `TranslateBy(delta: Vector3): ()`**: Shifts a Model by the given Vector3 offset, preserving ### From [PVInstance](/docs/reference/engine/classes/PVInstance.md) - **Property `Origin`** (`CFrame`): - **Property `Pivot Offset`** (`CFrame`): - **Method `GetPivot(): CFrame`**: Gets the pivot of a PVInstance. - **Method `PivotTo(targetCFrame: CFrame): ()`**: Transforms the PVInstance along with all of its descendant ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: BadgeService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service summary: "Provides information on badges and awards them." --- # Class: BadgeService > Provides information on badges and awards them. ## Description `BadgeService` provides information and functionality related to [badges](/docs/en-us/production/publishing/badges.md). Badges are used across the platform to recognize a player's achievements and activity. Upon awarding a badge to a player, it is added to their inventory and displayed on their profile page. ## Methods ### Method: BadgeService:AwardBadgeAsync **Signature:** `BadgeService:AwardBadgeAsync(userId: User, badgeId: int64): boolean` Grants a [Player](/docs/reference/engine/classes/Player.md) a badge with the [UserId](/docs/reference/engine/classes/Player.md) and the badge ID. In order to successfully award a badge: - The player must be presently connected to the experience. - The player must not already have the badge (note that a player may delete an awarded badge from their profile and be awarded the badge again). - The badge must be awarded from a server-side [Script](/docs/reference/engine/classes/Script.md) or a [ModuleScript](/docs/reference/engine/classes/ModuleScript.md) eventually required by a [Script](/docs/reference/engine/classes/Script.md), not from a [LocalScript](/docs/reference/engine/classes/LocalScript.md). - The badge must be awarded in a place that is part of the experience associated with the badge. - The badge must be enabled; check this using the `IsEnabled` property of the badge fetched through [BadgeService:GetBadgeInfoAsync()](/docs/reference/engine/classes/BadgeService.md). Rate limit is `50 + 35 * [number of users]` per minute. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetManagement* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `userId` | `User` | | The [Player.UserId](/docs/reference/engine/classes/Player.md) of the user the badge is to be awarded to. | | `badgeId` | `int64` | | The ID of the badge to be awarded. | **Returns:** `boolean` — Boolean of `true` if the badge was awarded successfully. **Awarding a Badge** The following example creates an `awardBadge()` function that handles potential errors that may occur when awarding a badge. ```lua local BadgeService = game:GetService("BadgeService") local Players = game:GetService("Players") local BADGE_ID = 0 local function awardBadge(player, badgeId) -- Fetch badge information local success, badgeInfo = pcall(function() return BadgeService:GetBadgeInfoAsync(badgeId) end) if success then -- Confirm that badge can be awarded if badgeInfo.IsEnabled then -- Award badge local awarded, errorMessage = pcall(BadgeService.AwardBadgeAsync, BadgeService, player.UserId, badgeId) if not awarded then warn("Error while awarding badge:", errorMessage) end end else warn("Error while fetching badge info: " .. badgeInfo) end end local function onPlayerAdded(player) awardBadge(player, BADGE_ID) end Players.PlayerAdded:Connect(onPlayerAdded) ``` ### Method: BadgeService:CheckUserBadgesAsync **Signature:** `BadgeService:CheckUserBadgesAsync(userId: User, badgeIds: Array): Array` Checks a list of badge IDs against a [UserId](/docs/reference/engine/classes/Player.md) and returns a list of badge IDs that the player owns. This method supports batches of up to 10 badges; use [BadgeService:UserHasBadgeAsync()](/docs/reference/engine/classes/BadgeService.md) for single badge lookups. When called from a [Script](/docs/reference/engine/classes/Script.md), any [UserId](/docs/reference/engine/classes/Player.md) can be used. If the user with the target user ID has not recently been in the server, only badge IDs that are associated with the requesting experience will be checked, and any badge IDs that are not associated with the requesting experience will not be included in the response (as if they were not owned). For users that have recently been in the server, any badges for any experiences can be queried, no matter who created the badge or which experience it is used for. In a [LocalScript](/docs/reference/engine/classes/LocalScript.md), only the [UserId](/docs/reference/engine/classes/Player.md) of the local user whose client is running the script can be used. Rate limit is `10 + 5 * [number of players]` per minute in each server. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetManagement* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `userId` | `User` | | The [UserId](/docs/reference/engine/classes/Player.md) of the player to check for ownership of the specified badges. | | `badgeIds` | `Array` | | The list of IDs of the badges to check ownership of. Maximum length of 10. | **Returns:** `Array` — The list of badge IDs the given user owns out of the provided badge IDs. Empty if none of the provided badges are owned by the given user. Not guaranteed to be in the same order as the input list. Some badge IDs may be omitted if the user with the target `userId` has not recently been in the server and the badge IDs are not associated with the requesting experience. **Checking Earned Badges in Batches** This script checks which badges a user owns when they join the experience. ```lua local BadgeService = game:GetService("BadgeService") local Players = game:GetService("Players") local badgeIds = { 0000000000, 1111111111, 2222222222 } -- Change this to a list of your badge IDs local function onPlayerAdded(player) -- Check if the player has any of the badges local success, result = pcall(function() return BadgeService:CheckUserBadgesAsync(player.UserId, badgeIds) end) -- If there's an error, issue a warning and exit the function if not success then warn("Error while checking if player", player.Name, "has badges:", result) return end local ownedBadgeIds = result if #ownedBadgeIds == 0 then print(player.Name, "does not have any of the badges") else print(player.Name, "has the following badges:", table.concat(ownedBadgeIds, ", ")) end end Players.PlayerAdded:Connect(onPlayerAdded) ``` ### Method: BadgeService:GetBadgeInfoAsync **Signature:** `BadgeService:GetBadgeInfoAsync(badgeId: int64): Dictionary` This method fetches information about a badge given its ID and returns a dictionary with the following fields: | Key | Type | Description | | --- | --- | --- | | `Name` | string | The name of the badge. | | `Description` | string | The description of the badge. | | `IconImageId` | int64 | The asset ID of the image for the badge. | | `IsEnabled` | boolean | Indicates whether the badge is available to be awarded. | This method takes a brief moment to load the information from Roblox; repeated calls will cache for a short duration. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetManagement* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `badgeId` | `int64` | | The badge ID of the badge whose information should be fetched. | **Returns:** `Dictionary` — A dictionary of information about the specified badge. **Getting Badge Info** This sample prints badge information fetched via [BadgeService:GetBadgeInfoAsync()](/docs/reference/engine/classes/BadgeService.md). ```lua local BadgeService = game:GetService("BadgeService") -- Fetch badge information local success, result = pcall(function() return BadgeService:GetBadgeInfoAsync(00000000) -- Change this to desired badge ID end) -- Output the information if success then print("Badge:", result.Name) print("Enabled:", result.IsEnabled) print("Description:", result.Description) print("Icon:", "rbxassetid://" .. result.IconImageId) else warn("Error while fetching badge info:", result) end ``` ### Method: BadgeService:UserHasBadgeAsync **Signature:** `BadgeService:UserHasBadgeAsync(userId: User, badgeId: int64): boolean` Checks and returns whether a [Player](/docs/reference/engine/classes/Player.md) owns a badge given their [UserId](/docs/reference/engine/classes/Player.md) and the badge ID. You can call this method from the server in a [Script](/docs/reference/engine/classes/Script.md) or [ModuleScript](/docs/reference/engine/classes/ModuleScript.md) eventually required by a [Script](/docs/reference/engine/classes/Script.md). When calling this method from the client in a [LocalScript](/docs/reference/engine/classes/LocalScript.md), it only works for the local user whose client is running the script. When called from a [Script](/docs/reference/engine/classes/Script.md), any [UserId](/docs/reference/engine/classes/Player.md) can be used. If the user with the target user ID has not recently been in the server, only badge IDs that are associated with the requesting experience will be checked. If the requested badge ID is not associated with the requesting experience and the target user has not recently been in the server, the method behaves as if the badge is not owned. For users that have recently been in the server, any badge for any experience can be queried, no matter who created the badge or which experience it is used for. Rate limit is `50 + 35 * [number of players]` per minute. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetManagement* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `userId` | `User` | | The [Player.UserId](/docs/reference/engine/classes/Player.md) of the player to check for ownership of the specified badge. | | `badgeId` | `int64` | | The badge ID of the badge whose ownership will be checked. | **Returns:** `boolean` — Indicates if the specified user has the specified badge. **Checking Earned Badges** The following script waits for any player to enter the game and checks if they own a specific badge. This is useful for creating a restricted area with [collision filtering](/docs/en-us/workspace/collisions.md#collision-filtering) or [teleportation](/docs/en-us/projects/teleporting.md) that only works if a player owns a special badge. ```lua local BadgeService = game:GetService("BadgeService") local Players = game:GetService("Players") local badgeId = 00000000 -- Change this to your badge ID local function onPlayerAdded(player) -- Check if the player has the badge local success, hasBadge = pcall(function() return BadgeService:UserHasBadgeAsync(player.UserId, badgeId) end) -- If there's an error, issue a warning and exit the function if not success then warn("Error while checking if player has badge!") return end if hasBadge then -- Handle player's badge ownership as needed print(player.Name, "has badge", badgeId) end end -- Connect "PlayerAdded" events to the "onPlayerAdded()" function Players.PlayerAdded:Connect(onPlayerAdded) ``` ### Method: BadgeService:AwardBadge **Signature:** `BadgeService:AwardBadge(userId: User, badgeId: int64): boolean` Award a badge to a player given the ID of each. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetManagement* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `userId` | `User` | | The [Player.UserId](/docs/reference/engine/classes/Player.md) of the user the badge is to be awarded to. | | `badgeId` | `int64` | | The ID of the badge to be awarded. | **Returns:** `boolean` — Boolean of `true` if the badge was awarded successfully. ### Method: BadgeService:IsDisabled **Signature:** `BadgeService:IsDisabled(badgeId: int64): boolean` > **Deprecated:** This function is deprecated. Do not use it for new work. Instead, it can be checked by calling BadgeService:GetBadgeInfoAsync() and checking the IsEnabled field. This function returns whether the badge with the given ID is marked **disabled** on the Roblox website. A badge can be disabled by its owner on its page on the Roblox website, in the settings sub-menu. When a badge is disabled, this function returns true and the badge can no longer be awarded. A badge may be quickly re-enabled through the same menu. In Studio, a badge can only be tested if it is **disabled**. Calling this function with an enabled badge in Studio will return **true** and produce a warning "Sorry, badges can only be tested if they are disabled on Roblox game servers". Note that even if a badge is enabled it may not necessarily be awardable (for example if it isn't associated with the current game). Badges that are associated with special events are a common reason for a badge to be disabled. Often, it is easier to simply disable a badge instead of hard-coding a time check for when some event ends. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetManagement* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `badgeId` | `int64` | | The ID of the badge. | **Returns:** `boolean` — True if the specified badge is not available to be awarded. **[Deprecated] Checking if a Badge is Disabled** If the below were to be ran in offline mode, it would print > Sorry, badges can only be tested if they are disabled on Roblox game servers However, if ran in online mode, it would print true/false to the in-game output in the escape menu (or similar). ```lua local BadgeService = game:GetService("BadgeService") print(BadgeService:IsDisabled(17468517)) ``` ### Method: BadgeService:IsLegal **Signature:** `BadgeService:IsLegal(badgeId: int64): boolean` > **Deprecated:** This function is deprecated and will always return true. Do not use it for new work. This function determines if a given badge is associated with the current game. It returns true if the badge is associated with the current game. Badges can only be awarded from a place that is part of the game associated with the badge. This means, for example, a developer cannot award a badge associated with another developer's game. Even if this returns true, a badge may still not be award-able. For example, it may be disabled. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetManagement* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `badgeId` | `int64` | | The badge ID of the badge. | **Returns:** `boolean` — True if the badge is associated with the current game. ### Method: BadgeService:UserHasBadge **Signature:** `BadgeService:UserHasBadge(userId: User, badgeId: int64): boolean` > **Deprecated:** This method has been superseded by [BadgeService:UserHasBadgeAsync()](/docs/reference/engine/classes/BadgeService.md) which should be used for new work instead. Checks and returns whether a [Player](/docs/reference/engine/classes/Player.md) owns a badge given their [UserId](/docs/reference/engine/classes/Player.md) and the badge ID. You can call the function from the server in a [Script](/docs/reference/engine/classes/Script.md) or [ModuleScript](/docs/reference/engine/classes/ModuleScript.md) eventually required by a [Script](/docs/reference/engine/classes/Script.md). When calling the method from the client in a [LocalScript](/docs/reference/engine/classes/LocalScript.md), it only works for the local user whose client is running the script. When called from a [Script](/docs/reference/engine/classes/Script.md), any [UserId](/docs/reference/engine/classes/Player.md) can be used. If the user with the target user ID has not recently been in the server, only badge IDs that are associated with the requesting experience will be checked. If the requested badge ID is not associated with the requesting experience and the target user has not recently been in the server, the method behaves as if the badge is not owned. For users that have recently been in the server, any badge for any experience can be queried, no matter who created the badge or which experience it is used for. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetManagement* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `userId` | `User` | | The user ID of the user. | | `badgeId` | `int64` | | The badge ID of the badge. | **Returns:** `boolean` — True if the user has the badge. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: BallSocketConstraint last_updated: 2026-06-29T19:34:07Z inherits: - Constraint - Instance - Object type: class memory_category: BaseParts summary: "Forces its two attachments into the same position and allows them to freely rotate about all three axes, with optional limits to restrict both tilt and twist." --- # Class: BallSocketConstraint > Forces its two attachments into the same position and allows them to freely > rotate about all three axes, with optional limits to restrict both tilt and > twist. ## Description A `BallSocketConstraint` constrains its [Attachments](/docs/reference/engine/classes/Attachment.md) so that they occupy the same position. By default it allows both attachments to freely rotate about all of their axes, but if [LimitsEnabled](/docs/reference/engine/classes/BallSocketConstraint.md) is `true`, the attachments can only rotate in a limited cone. Note that if this constraint attaches one part (**A**) to another part (**B**) that is anchored or connected to an anchored part (**Z**), part **A** will not be locally simulated when interacting with a player. When configuring this constraint, it may be helpful to study [Roblox Units](/docs/en-us/physics/units.md) to understand how Roblox units compare to metric units. #### Limits You can set limits to restrict both **tilt** and **twist** of a ball socket, similar to how a human's head can tilt and turn within a limited axial range. Enabling the [LimitsEnabled](/docs/reference/engine/classes/BallSocketConstraint.md) property exposes the [UpperAngle](/docs/reference/engine/classes/BallSocketConstraint.md) value to restrict **tilt** within a cone; it also exposes the [TwistLimitsEnabled](/docs/reference/engine/classes/BallSocketConstraint.md) property which, when enabled, lets you restrict **twist** rotation through the [TwistLowerAngle](/docs/reference/engine/classes/BallSocketConstraint.md) and [TwistUpperAngle](/docs/reference/engine/classes/BallSocketConstraint.md) limits. ## Properties ### Property: BallSocketConstraint.LimitsEnabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "BallSocket", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Sets whether the `BallSocketConstraint` has a limit on rotation based on [UpperAngle](/docs/reference/engine/classes/BallSocketConstraint.md). When `true`, it enforces that its [Attachment1](/docs/reference/engine/classes/Constraint.md) isn't rotated more than a set angle from its [Attachment0](/docs/reference/engine/classes/Constraint.md). ### Property: BallSocketConstraint.MaxFrictionTorque ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Friction", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Sets the maximum frictional torque applied to keep its [Attachments](/docs/reference/engine/classes/Attachment.md) aligned. `MaxFrictionTorque` specifies the stiffness of the `BallSocketConstraint` (how much it resists rotation around its [Attachments](/docs/reference/engine/classes/Attachment.md)). ### Property: BallSocketConstraint.Radius ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The visualized radius of the `BallSocketConstraint`. ### Property: BallSocketConstraint.Restitution ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Limits", "capabilities": [ "Physics" ], "simulationAccess": true } ``` How elastic [Attachments](/docs/reference/engine/classes/Attachment.md) connected by a `BallSocketConstraint` will be when they reach the end of the range specified by [UpperAngle](/docs/reference/engine/classes/BallSocketConstraint.md) when [LimitsEnabled](/docs/reference/engine/classes/BallSocketConstraint.md) is `true`. Constrained between 0 and 1. ### Property: BallSocketConstraint.TwistLimitsEnabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Limits", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Sets whether the `BallSocketConstraint` sets a limit on twist rotation based on [TwistUpperAngle](/docs/reference/engine/classes/BallSocketConstraint.md) and [TwistLowerAngle](/docs/reference/engine/classes/BallSocketConstraint.md). The twist angle is defined as the angle between the **Y** axis of [Attachment1](/docs/reference/engine/classes/Constraint.md) and the **Y** axis of [Attachment0](/docs/reference/engine/classes/Constraint.md). ### Property: BallSocketConstraint.TwistLowerAngle ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "TwistLimits", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Sets the lower twist rotation limit of the `BallSocketConstraint`, as long as [TwistLimitsEnabled](/docs/reference/engine/classes/BallSocketConstraint.md) is `true`. ### Property: BallSocketConstraint.TwistUpperAngle ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "TwistLimits", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Sets the upper twist rotation limit of the `BallSocketConstraint`, as long as [TwistLimitsEnabled](/docs/reference/engine/classes/BallSocketConstraint.md) is `true`. ### Property: BallSocketConstraint.UpperAngle ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Limits", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Sets the upper rotation limit of the `BallSocketConstraint`, as long as [LimitsEnabled](/docs/reference/engine/classes/BallSocketConstraint.md) is `true`. ## Inherited Members ### From [Constraint](/docs/reference/engine/classes/Constraint.md) - **Property `Active`** (`boolean`): Indicates if the constraint is currently active in the world. - **Property `Attachment0`** (`Attachment`): The Attachment that is connected to - **Property `Attachment1`** (`Attachment`): The Attachment that is connected to - **Property `Color`** (`BrickColor`): The color of the constraint. - **Property `Enabled`** (`boolean`): Toggles whether or not the constraint is enabled. - **Property `Visible`** (`boolean`): Toggles the constraint's visibility. - **Method `GetDebugAppliedForce(bodyId: int): Vector3`**: *(deprecated)* - **Method `GetDebugAppliedTorque(bodyId: int): Vector3`**: *(deprecated)* ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: BanHistoryPages last_updated: 2026-06-29T19:34:07Z inherits: - Pages - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated summary: "Returned by Players:GetBanHistoryAsync to view the entire ban and unban history of any UserId." --- # Class: BanHistoryPages > Returned by [Players:GetBanHistoryAsync](/docs/reference/engine/classes/Players.md) to view the entire ban and > unban history of any UserId. ## Description [BanHistoryPages](/docs/reference/engine/classes/BanHistoryPages.md) is returned by [Players:GetBanHistoryAsync()](/docs/reference/engine/classes/Players.md). This instance allows an experience to view the entire ban and unban history of any player within the experience's universe. The items within [BanHistoryPages](/docs/reference/engine/classes/BanHistoryPages.md) are tables with the following fields: | Name | Type | Description | | --- | --- | --- | | `DisplayReason` | string | The `DisplayReason` that was shown to the user | | `PrivateReason` | string | The `PrivateReason` that was sent with this ban. | | `StartTime` | string | The time that this ban was applied in ISO 8601 format. | | `Duration` | number | The length of the ban in seconds. If it is a permanent ban, it will be `-1`. | | `Ban` | boolean | If the action was a ban, it will be `true`. If the action was an unban, it will be `false`. | | `PlaceId` | number | The place in which a ban or unban was explicitly updated. This field is `-1` if the action was applied on the universe level. | ## Inherited Members ### From [Pages](/docs/reference/engine/classes/Pages.md) - **Property `IsFinished`** (`boolean`): Whether or not the current page is the last page available. - **Method `AdvanceToNextPageAsync(): ()`**: Iterates to the next page in the pages object, if possible. - **Method `GetCurrentPage(): Array`**: Returns the items on the current page. The keys in the item are determined ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: BaseCoreGuiConfiguration last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated --- # Class: BaseCoreGuiConfiguration ## Properties ### Property: BaseCoreGuiConfiguration.Enabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State" } ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: BasePart last_updated: 2026-06-29T19:34:07Z inherits: - PVInstance - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotBrowsable summary: "The abstract base class for in-world objects that physically interact." --- # Class: BasePart > The abstract base class for in-world objects that physically interact. ## Description [BasePart](/docs/reference/engine/classes/BasePart.md) is an abstract base class for in-world objects that render and are physically simulated while in the [Workspace](/docs/reference/engine/classes/Workspace.md). There are several implementations of [BasePart](/docs/reference/engine/classes/BasePart.md), the most common being [Part](/docs/reference/engine/classes/Part.md) and [MeshPart](/docs/reference/engine/classes/MeshPart.md). Others include [WedgePart](/docs/reference/engine/classes/WedgePart.md), [SpawnLocation](/docs/reference/engine/classes/SpawnLocation.md), and the singleton [Terrain](/docs/reference/engine/classes/Terrain.md) object. Generally, when documentation refers to a "part," most [BasePart](/docs/reference/engine/classes/BasePart.md) implementations will work and not just [Part](/docs/reference/engine/classes/Part.md). For information on how [BaseParts](/docs/reference/engine/classes/BasePart.md) are grouped into simulated rigid bodies, see [Assemblies](/docs/en-us/physics/assemblies.md). There are many different objects that interact with [BasePart](/docs/reference/engine/classes/BasePart.md) (other than [Terrain](/docs/reference/engine/classes/Terrain.md)), including: - Several [BaseParts](/docs/reference/engine/classes/BasePart.md) may be grouped within a [Model](/docs/reference/engine/classes/Model.md) and moved at the same time using [PVInstance:PivotTo()](/docs/reference/engine/classes/PVInstance.md). See [Models](/docs/en-us/parts/models.md). - A [Decal](/docs/reference/engine/classes/Decal.md) applies a stretched image texture to the faces of a [BasePart](/docs/reference/engine/classes/BasePart.md), while a [Texture](/docs/reference/engine/classes/Texture.md) applies a tiled image texture to the faces. See [Textures and Decals](/docs/en-us/parts/textures-decals.md). - A [SurfaceGui](/docs/reference/engine/classes/SurfaceGui.md) renders [GuiObjects](/docs/reference/engine/classes/GuiObject.md) on the face of a part. See [In-Experience UI Containers](/docs/en-us/ui/in-experience-containers.md). - [Attachments](/docs/reference/engine/classes/Attachment.md) can be added to a [BasePart](/docs/reference/engine/classes/BasePart.md) to specify [CFrames](/docs/reference/engine/datatypes/CFrame.md) relative to the part. These are often used by physical [Constraint](/docs/reference/engine/classes/Constraint.md) objects as outlined in [Mechanical Constraints](/docs/en-us/physics/mechanical-constraints.md) and [Mover Constraints](/docs/en-us/physics/mover-constraints.md). - [ParticleEmitter](/docs/reference/engine/classes/ParticleEmitter.md) objects emit particles uniformly in the volume of the [BasePart](/docs/reference/engine/classes/BasePart.md) to which they are parented. See [Particle Emitters](/docs/en-us/effects/particle-emitters.md). - Light objects like [PointLight](/docs/reference/engine/classes/PointLight.md) emit light from the center of a [BasePart](/docs/reference/engine/classes/BasePart.md) as illustrated in [Light Sources](/docs/en-us/effects/light-sources.md). - If parented to a [Tool](/docs/reference/engine/classes/Tool.md) and given the name **Handle**, a [BasePart](/docs/reference/engine/classes/BasePart.md) can be held by characters. See [In-Experience Tools](/docs/en-us/players/tools.md). ## Properties ### Property: BasePart.Anchored ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Part", "simulationAccess": true } ``` The `Anchored` property determines whether the part will be immovable by physics. When enabled, a part will never change position due to gravity, other part collisions, overlapping other parts, or any other physics-related causes. As a result, two anchored parts will never fire the [Touched](/docs/reference/engine/classes/BasePart.md) event on each other. An anchored part may still be moved by changing its [CFrame](/docs/reference/engine/classes/BasePart.md) or [Position](/docs/reference/engine/classes/BasePart.md), and it still may have a nonzero [AssemblyLinearVelocity](/docs/reference/engine/classes/BasePart.md) and [AssemblyAngularVelocity](/docs/reference/engine/classes/BasePart.md). Finally, if an unanchored part is joined with an anchored part through an object like a [Weld](/docs/reference/engine/classes/Weld.md), it too will act anchored. If such a joint breaks, the part may be affected by physics again. See [Assemblies](/docs/en-us/physics/assemblies.md) for more details. Network ownership cannot be set on anchored parts. If a part's anchored status changes on the server, the network ownership of that part will be affected. **Part Anchored Toggle** This code sample will allow a part to be clicked to toggle its anchored property. When toggled, the visual appearance of the part is updated (red means anchored, yellow means free). ```lua local part = script.Parent -- Create a ClickDetector so we can tell when the part is clicked local cd = Instance.new("ClickDetector", part) -- This function updates how the part looks based on its Anchored state local function updateVisuals() if part.Anchored then -- When the part is anchored... part.BrickColor = BrickColor.new("Bright red") part.Material = Enum.Material.DiamondPlate else -- When the part is unanchored... part.BrickColor = BrickColor.new("Bright yellow") part.Material = Enum.Material.Wood end end local function onToggle() -- Toggle the anchored property part.Anchored = not part.Anchored -- Update visual state of the brick updateVisuals() end -- Update, then start listening for clicks updateVisuals() cd.MouseClick:Connect(onToggle) ``` ### Property: BasePart.AssemblyAngularVelocity ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Assembly", "simulationAccess": true } ``` The angular velocity vector of this part's assembly. It's the rate of change of orientation in radians per second. Angular velocity is the same at every point of the assembly. Setting the velocity directly may lead to unrealistic motion. Using [Torque](/docs/reference/engine/classes/Torque.md) or [AngularVelocity](/docs/reference/engine/classes/AngularVelocity.md) constraint is preferred, or use [ApplyAngularImpulse()](/docs/reference/engine/classes/BasePart.md) if you want instantaneous change in velocity. If the part is [owned](/docs/en-us/physics/network-ownership.md) by the server, this property must be changed from a server [Script](/docs/reference/engine/classes/Script.md) (not from a [LocalScript](/docs/reference/engine/classes/LocalScript.md) or a [Script](/docs/reference/engine/classes/Script.md) with [RunContext](/docs/reference/engine/classes/BaseScript.md) set to [RunContext.Client](/docs/reference/engine/enums/RunContext.md)). If the part is owned by a client through **automatic** ownership, this property can be changed from either a client script **or** a server script; changing it from a client script for a server-owned part will have no effect. ### Property: BasePart.AssemblyCenterOfMass ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Assembly", "simulationAccess": true } ``` A position calculated via the [Mass](/docs/reference/engine/classes/BasePart.md) and [Position](/docs/reference/engine/classes/BasePart.md) of all the parts in the assembly. If the assembly has an anchored part, that part's center of mass will be the assembly's center of mass, and the assembly will have infinite mass. Knowing the center of mass can help the assembly maintain stability. A force applied to the center of mass will not cause angular acceleration, only linear. An assembly with a low center of mass will have a better time staying upright under the effect of gravity. ### Property: BasePart.AssemblyLinearVelocity ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Assembly", "simulationAccess": true } ``` The linear velocity vector of this part's assembly. It's the rate of change in position of [AssemblyCenterOfMass](/docs/reference/engine/classes/BasePart.md) in studs per second. If you want to know the velocity at a point other than the assembly's center of mass, use [GetVelocityAtPosition()](/docs/reference/engine/classes/BasePart.md). Setting the velocity directly may lead to unrealistic motion. Using a [VectorForce](/docs/reference/engine/classes/VectorForce.md) constraint is preferred, or use [ApplyImpulse()](/docs/reference/engine/classes/BasePart.md) if you want instantaneous change in velocity. If the part is [owned](/docs/en-us/physics/network-ownership.md) by the server, this property must be changed from a server [Script](/docs/reference/engine/classes/Script.md) (not from a [LocalScript](/docs/reference/engine/classes/LocalScript.md) or a [Script](/docs/reference/engine/classes/Script.md) with [RunContext](/docs/reference/engine/classes/BaseScript.md) set to [RunContext.Client](/docs/reference/engine/enums/RunContext.md)). If the part is owned by a client through **automatic** ownership, this property can be changed from either a client script **or** a server script; changing it from a client script for a server-owned part will have no effect. ### Property: BasePart.AssemblyMass ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Assembly", "simulationAccess": true } ``` The sum of the mass of all the [BaseParts](/docs/reference/engine/classes/BasePart.md) in this part's assembly. Parts that are [Massless](/docs/reference/engine/classes/BasePart.md) and are not the assembly's root part will not contribute to the `AssemblyMass`. If the assembly has an anchored part, the assembly's mass is considered infinite. Constraints and other physical interactions between unanchored assemblies with a large difference in mass may cause instabilities. ### Property: BasePart.AssemblyRootPart ```json { "type": "BasePart", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Assembly", "simulationAccess": true } ``` This property indicates the [BasePart](/docs/reference/engine/classes/BasePart.md) automatically chosen to represent the assembly's root part. If the part is not parented to the [Workspace](/docs/reference/engine/classes/Workspace.md), this property will be `nil`. The root part can be changed by changing the [RootPriority](/docs/reference/engine/classes/BasePart.md) of the parts in the assembly. Parts that all share the same `AssemblyRootPart` are in the same assembly. For more information on root parts, see [Assemblies](/docs/en-us/physics/assemblies.md). ### Property: BasePart.AudioCanCollide ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Collision", "simulationAccess": true } ``` `AudioCanCollide` determines whether the part will physically interact with audio simulation, similar to [CastShadow](/docs/reference/engine/classes/BasePart.md) for lighting. When disabled, audio passes through the part; it is not occluded or reflected. ### Property: BasePart.BackSurface ```json { "type": "SurfaceType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Surface" } ``` The `BackSurface` property determines the type of surface used for the positive **Z** direction of a part. When two parts' faces are placed next to each other, they may create a joint between them. ### Property: BasePart.BottomSurface ```json { "type": "SurfaceType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Surface" } ``` The `BottomSurface` property determines the type of surface used for the negative **Y** direction of a part. When two parts' faces are placed next to each other, they may create a joint between them. ### Property: BasePart.BrickColor ```json { "type": "BrickColor", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Appearance" } ``` This property determines the color of a part. If the part has a [Material](/docs/reference/engine/classes/BasePart.md), this also determines the color used when rendering the material texture. For more control over the color, the [Color](/docs/reference/engine/classes/BasePart.md) property can be used and this property will use the closest `BrickColor`. Other visual properties of a part are determined by [Transparency](/docs/reference/engine/classes/BasePart.md) and [Reflectance](/docs/reference/engine/classes/BasePart.md). ### Property: BasePart.CanCollide ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Collision", "simulationAccess": true } ``` `CanCollide` determines whether a part will physically interact with other parts. When disabled, other parts can pass through the part uninterrupted. Parts used for **decoration** usually have `CanCollide` disabled, as they need not be considered by the physics engine. If a part is not [Anchored](/docs/reference/engine/classes/BasePart.md) and has `CanCollide` disabled, it may fall out of the world to be eventually destroyed by [Workspace.FallenPartsDestroyHeight](/docs/reference/engine/classes/Workspace.md). When `CanCollide` is disabled, parts may still fire the [Touched](/docs/reference/engine/classes/BasePart.md) event (as well the other parts touching them). You can disable this with [CanTouch](/docs/reference/engine/classes/BasePart.md). For more information on collisions, see [Collisions](/docs/en-us/workspace/collisions.md). **Fade Door** This code sample shows how a part can fade away when touched by a Humanoid then reappear a moment after to create a passable door. ```lua -- Paste into a Script inside a tall part local part = script.Parent local OPEN_TIME = 1 -- Can the door be opened at the moment? local debounce = false local function open() part.CanCollide = false part.Transparency = 0.7 part.BrickColor = BrickColor.new("Black") end local function close() part.CanCollide = true part.Transparency = 0 part.BrickColor = BrickColor.new("Bright blue") end local function onTouch(otherPart) -- If the door was already open, do nothing if debounce then print("D") return end -- Check if touched by a Humanoid local human = otherPart.Parent:FindFirstChildOfClass("Humanoid") if not human then print("not human") return end -- Perform the door opening sequence debounce = true open() task.wait(OPEN_TIME) close() debounce = false end part.Touched:Connect(onTouch) close() ``` ### Property: BasePart.CanQuery ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Collision", "simulationAccess": true } ``` This property determines whether the part is considered during spatial query operations, such as [GetPartBoundsInBox](/docs/reference/engine/classes/WorldRoot.md) or [Raycast](/docs/reference/engine/classes/WorldRoot.md). Note that [CanCollide](/docs/reference/engine/classes/BasePart.md) must be disabled for `CanQuery` to take effect, and spatial query functions will never include parts with `CanQuery` of `false`. Beyond this property, it is also possible to exclude parts which are descendants of a given list of parts using an [OverlapParams](/docs/reference/engine/datatypes/OverlapParams.md) or [RaycastParams](/docs/reference/engine/datatypes/RaycastParams.md) object when calling the spatial query functions. ### Property: BasePart.CanTouch ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Collision", "simulationAccess": true } ``` This property determines if [Touched](/docs/reference/engine/classes/BasePart.md) and [TouchEnded](/docs/reference/engine/classes/BasePart.md) events fire on the part. If `true`, other touching parts must also have `CanTouch` set to `true` for touch events to fire. If `false`, touch events cannot be set up for the part and attempting to do so will throw an error. Similarly, if the property is set to `false` after a touch event is connected, the event will be disconnected and the [TouchTransmitter](/docs/reference/engine/classes/TouchTransmitter.md) removed. Note that this collision logic can be set to respect [collision groups](/docs/en-us/workspace/collisions.md#collision-filtering) through the [Workspace.TouchesUseCollisionGroups](/docs/reference/engine/classes/Workspace.md) property. If `true`, parts in non-colliding groups will ignore both collisions **and** touch events, thereby making this property irrelevant. #### Performance There is a small performance gain on parts that have both `CanTouch` and [CanCollide](/docs/reference/engine/classes/BasePart.md) set to `false`, as these parts will never need to compute any kind of part to part collisions. However, they can still be hit by [Raycasts](/docs/reference/engine/classes/WorldRoot.md) and [OverlapParams](/docs/reference/engine/datatypes/OverlapParams.md) queries. ### Property: BasePart.CastShadow ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance" } ``` Determines whether or not a part casts a shadow. Disabling this property for a given part can cause visual artifacts on the shadows cast upon that part. This property is not designed for performance enhancement, but in complex scenes, strategically disabling it on certain parts can improve performance. Due to the possibility of visual artifacts, we recommend leaving it enabled on all parts in most situations. ### Property: BasePart.CenterOfMass ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Part", "simulationAccess": true } ``` The `CenterOfMass` property describes the **local** position of a part's center of mass. If this is a single part assembly, this is the [AssemblyCenterOfMass](/docs/reference/engine/classes/BasePart.md) converted from world space to local. On simple [Parts](/docs/reference/engine/classes/Part.md), the center of mass is always `(0, 0, 0)`, but it can vary for [WedgePart](/docs/reference/engine/classes/WedgePart.md) or [MeshPart](/docs/reference/engine/classes/MeshPart.md). ### Property: BasePart.CFrame ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Transform", "simulationAccess": true } ``` The `CFrame` property determines both the position and orientation of the [BasePart](/docs/reference/engine/classes/BasePart.md) in the world. It acts as an arbitrary reference location on the geometry, but [ExtentsCFrame](/docs/reference/engine/classes/BasePart.md) represents the actual [CFrame](/docs/reference/engine/datatypes/CFrame.md) of its physical center. When setting `CFrame` on a part, other joined parts are also moved relative to the part, but it is recommended that you use [PVInstance:PivotTo()](/docs/reference/engine/classes/PVInstance.md) to move an entire model, such as when teleporting a player's character. Unlike setting [BasePart.Position](/docs/reference/engine/classes/BasePart.md), setting `CFrame` will always move the part to the exact given [CFrame](/docs/reference/engine/datatypes/CFrame.md); in other words: **no overlap checking is done** and the physics solver will attempt to resolve any overlap unless both parts are [Anchored](/docs/reference/engine/classes/BasePart.md). For keeping track of positions relative to a part's [CFrame](/docs/reference/engine/datatypes/CFrame.md), an [Attachment](/docs/reference/engine/classes/Attachment.md) may be useful. **Setting Part CFrame** This code sample demonstrates setting a part's CFrame in many different ways. It showcases how to create and compose CFrame values. It references a sibling part called "OtherPart" for demonstrating relative positioning. ```lua local part = script.Parent:WaitForChild("Part") local otherPart = script.Parent:WaitForChild("OtherPart") -- Reset the part's CFrame to (0, 0, 0) with no rotation. -- This is sometimes called the "identity" CFrame part.CFrame = CFrame.new() -- Set to a specific position (X, Y, Z) part.CFrame = CFrame.new(0, 25, 10) -- Same as above, but use a Vector3 instead local point = Vector3.new(0, 25, 10) part.CFrame = CFrame.new(point) -- Set the part's CFrame to be at one point, looking at another local lookAtPoint = Vector3.new(0, 20, 15) part.CFrame = CFrame.lookAt(point, lookAtPoint) -- Rotate the part's CFrame by pi/2 radians on local X axis part.CFrame = part.CFrame * CFrame.Angles(math.pi / 2, 0, 0) -- Rotate the part's CFrame by 45 degrees on local Y axis part.CFrame = part.CFrame * CFrame.Angles(0, math.rad(45), 0) -- Rotate the part's CFrame by 180 degrees on global Z axis (note the order!) part.CFrame = CFrame.Angles(0, 0, math.pi) * part.CFrame -- Pi radians is equal to 180 degrees -- Composing two CFrames is done using * (the multiplication operator) part.CFrame = CFrame.new(2, 3, 4) * CFrame.new(4, 5, 6) --> equal to CFrame.new(6, 8, 10) -- Unlike algebraic multiplication, CFrame composition is NOT communitative: a * b is not necessarily b * a! -- Imagine * as an ORDERED series of actions. For example, the following lines produce different CFrames: -- 1) Slide the part 5 units on X. -- 2) Rotate the part 45 degrees around its Y axis. part.CFrame = CFrame.new(5, 0, 0) * CFrame.Angles(0, math.rad(45), 0) -- 1) Rotate the part 45 degrees around its Y axis. -- 2) Slide the part 5 units on X. part.CFrame = CFrame.Angles(0, math.rad(45), 0) * CFrame.new(5, 0, 0) -- There is no "CFrame division", but instead simply "doing the inverse operation". part.CFrame = CFrame.new(4, 5, 6) * CFrame.new(4, 5, 6):Inverse() --> is equal to CFrame.new(0, 0, 0) part.CFrame = CFrame.Angles(0, 0, math.pi) * CFrame.Angles(0, 0, math.pi):Inverse() --> equal to CFrame.Angles(0, 0, 0) -- Position a part relative to another (in this case, put our part on top of otherPart) part.CFrame = otherPart.CFrame * CFrame.new(0, part.Size.Y / 2 + otherPart.Size.Y / 2, 0) ``` ### Property: BasePart.CollisionGroup ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Collision", "simulationAccess": true } ``` The `CollisionGroup` property describes the name of the part's collision group (maximum of 100 characters). Parts start off in the default group whose name is `"Default"`. This value cannot be empty. Although this property itself is non-replicated, the engine internally replicates the value through another private property to solve backward compatibility issues. **PhysicsService:RegisterCollisionGroup** This example demonstrates one basic use of collision groups. It assigns **BallPart** to `"CollisionGroupBall"` and **DoorPart** to `"CollisionGroupDoor"`, then makes the two groups non-collidable using [PhysicsService:CollisionGroupSetCollidable()](/docs/reference/engine/classes/PhysicsService.md). ```lua local PhysicsService = game:GetService("PhysicsService") local collisionGroupBall = "CollisionGroupBall" local collisionGroupDoor = "CollisionGroupDoor" -- Register collision groups PhysicsService:RegisterCollisionGroup(collisionGroupBall) PhysicsService:RegisterCollisionGroup(collisionGroupDoor) -- Assign parts to collision groups script.Parent.BallPart.CollisionGroup = collisionGroupBall script.Parent.DoorPart.CollisionGroup = collisionGroupDoor -- Set groups as non-collidable with each other and check the result PhysicsService:CollisionGroupSetCollidable(collisionGroupBall, collisionGroupDoor, false) print(PhysicsService:CollisionGroupsAreCollidable(collisionGroupBall, collisionGroupDoor)) --> false ``` ### Property: BasePart.Color ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Appearance" } ``` The `Color` property determines the color of a part. If the part has a [Material](/docs/reference/engine/classes/BasePart.md), this also determines the color used when rendering the material texture. If this property is set, [BrickColor](/docs/reference/engine/classes/BasePart.md) will use the closest match to this `Color` value. Other visual properties of a part are determined by [Transparency](/docs/reference/engine/classes/BasePart.md) and [Reflectance](/docs/reference/engine/classes/BasePart.md). **Character Health Body Color** This code sample colors a player's entire character based on how much health they have. It generates a color based on their max health, then sets the color properties of objects within their character, removing any extra objects. ```lua -- Paste into a Script within StarterCharacterScripts -- Then play the game, and fiddle with your character's health local char = script.Parent local human = char.Humanoid local colorHealthy = Color3.new(0.4, 1, 0.2) local colorUnhealthy = Color3.new(1, 0.4, 0.2) local function setColor(color) for _, child in pairs(char:GetChildren()) do if child:IsA("BasePart") then child.Color = color while child:FindFirstChildOfClass("Decal") do child:FindFirstChildOfClass("Decal"):Destroy() end elseif child:IsA("Accessory") then child.Handle.Color = color local mesh = child.Handle:FindFirstChildOfClass("SpecialMesh") if mesh then mesh.TextureId = "" end elseif child:IsA("Shirt") or child:IsA("Pants") then child:Destroy() end end end local function update() local percentage = human.Health / human.MaxHealth -- Create a color by tweening based on the percentage of your health -- The color goes from colorHealthy (100%) ----- > colorUnhealthy (0%) local color = Color3.new( colorHealthy.R * percentage + colorUnhealthy.r * (1 - percentage), colorHealthy.G * percentage + colorUnhealthy.g * (1 - percentage), colorHealthy.B * percentage + colorUnhealthy.b * (1 - percentage) ) setColor(color) end update() human.HealthChanged:Connect(update) ``` ### Property: BasePart.CurrentPhysicalProperties ```json { "type": "PhysicalProperties", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Part" } ``` `CurrentPhysicalProperties` indicates the current physical properties of the part. You can set custom values for the physical properties per part, [custom material](/docs/en-us/parts/materials.md), and material override. The Roblox engine prioritizes more granular definitions when determining the effective physical properties of a part. The values in the following list are in order from highest to lowest priority: - Custom physical properties of the part - Custom physical properties of the part's custom material - Custom physical properties of the material override of the part's material - Default physical properties of the part's material ### Property: BasePart.CustomPhysicalProperties ```json { "type": "PhysicalProperties", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Part", "simulationAccess": true } ``` `CustomPhysicalProperties` lets you customize various physical aspects of a part, such as its density, friction, and elasticity. If enabled, this property let's you configure these physical properties. If disabled, these physical properties are determined by the [Material](/docs/reference/engine/classes/BasePart.md) of the part. **Set CustomPhysicalProperties** This code sample demonstrates how to set the CustomPhysicalProperties property of a part. ```lua local part = script.Parent -- This will make the part light and bouncy! local DENSITY = 0.3 local FRICTION = 0.1 local ELASTICITY = 1 local FRICTION_WEIGHT = 1 local ELASTICITY_WEIGHT = 1 local physProperties = PhysicalProperties.new(DENSITY, FRICTION, ELASTICITY, FRICTION_WEIGHT, ELASTICITY_WEIGHT) part.CustomPhysicalProperties = physProperties ``` ### Property: BasePart.EnableFluidForces ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "simulationAccess": true } ``` When `true`, and when [Workspace.FluidForces](/docs/reference/engine/classes/Workspace.md) is enabled, causes the physics engine to compute aerodynamic forces on this [BasePart](/docs/reference/engine/classes/BasePart.md). ### Property: BasePart.ExtentsCFrame ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Transform", "simulationAccess": true } ``` The [CFrame](/docs/reference/engine/datatypes/CFrame.md) of the physical extents of the [BasePart](/docs/reference/engine/classes/BasePart.md), representing its physical center. ### Property: BasePart.ExtentsSize ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Transform", "simulationAccess": true } ``` The actual physical size of the [BasePart](/docs/reference/engine/classes/BasePart.md) as regarded by the physics engine, for example in [collision detection](/docs/en-us/workspace/collisions.md). ### Property: BasePart.FrontSurface ```json { "type": "SurfaceType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Surface" } ``` The `FrontSurface` property determines the type of surface used for the negative **Z** direction of a part. When two parts' faces are placed next to each other, they may create a joint between them. ### Property: BasePart.LeftSurface ```json { "type": "SurfaceType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Surface" } ``` The `LeftSurface` property determines the type of surface used for the negative **X** direction of a part. When two parts' faces are placed next to each other, they may create a joint between them. ### Property: BasePart.Locked ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "simulationAccess": true } ``` The `Locked` property determines whether a part (or a [Model](/docs/reference/engine/classes/Model.md) it is contained within) may be selected in Studio by clicking on it. This property is most often enabled on parts within environment models that aren't being edited at the moment. **Recursive Unlock** This code sample uses the concept of recursion to unlock all parts that are a descendant of a model. ```lua -- Paste into a Script within a Model you want to unlock local model = script.Parent -- This function recurses through a model's heirarchy and unlocks -- every part that it encounters. local function recursiveUnlock(object) if object:IsA("BasePart") then object.Locked = false end -- Call the same function on the children of the object -- The recursive process stops if an object has no children for _, child in pairs(object:GetChildren()) do recursiveUnlock(child) end end recursiveUnlock(model) ``` ### Property: BasePart.Mass ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Part", "simulationAccess": true } ``` `Mass` is a read-only property that describes the product of a part's volume and density. It is returned by the [GetMass()](/docs/reference/engine/classes/BasePart.md) function. - The volume of a part is determined by its [Size](/docs/reference/engine/classes/BasePart.md) and its [Shape](/docs/reference/engine/classes/Part.md), which varies depending on the kind of [BasePart](/docs/reference/engine/classes/BasePart.md) used, such as [WedgePart](/docs/reference/engine/classes/WedgePart.md). - The density of a part is determined by its [Material](/docs/reference/engine/classes/BasePart.md) or [CustomPhysicalProperties](/docs/reference/engine/classes/BasePart.md), if specified. ### Property: BasePart.Massless ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Part", "simulationAccess": true } ``` If this property is enabled, the part will not contribute to the total mass or inertia of its assembly as long as it is welded to another part that has mass. If the part is its own root part according to [AssemblyRootPart](/docs/reference/engine/classes/BasePart.md), this will be ignored for that part, and it will still contribute mass and inertia to its assembly like a normal part. Parts that are massless should never become an assembly root part unless all other parts in the assembly are also massless. This might be useful for things like optional accessories on vehicles that you don't want to affect the handling of the car or a massless render mesh welded to a simpler collision mesh. See also [Assemblies](/docs/en-us/physics/assemblies.md), an article documenting what root parts are and how to use them. ### Property: BasePart.Material ```json { "type": "Material", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "simulationAccess": true } ``` The `Material` property allows you to set a part's texture and default physical properties (in the case that [CustomPhysicalProperties](/docs/reference/engine/classes/BasePart.md) is unset). The default [Plastic](/docs/reference/engine/enums/Material.md) material has a very light texture, while the [SmoothPlastic](/docs/reference/engine/enums/Material.md) material has no texture at all. Some material textures like [DiamondPlate](/docs/reference/engine/enums/Material.md) and [Granite](/docs/reference/engine/enums/Material.md) have very visible textures. Each material's texture reflects sunlight differently, especially [Foil](/docs/reference/engine/enums/Material.md). Setting this property then enabling [CustomPhysicalProperties](/docs/reference/engine/classes/BasePart.md) will use the default physical properties of a material. For instance, [DiamondPlate](/docs/reference/engine/enums/Material.md) is a very dense material while [Wood](/docs/reference/engine/enums/Material.md) is very light. A part's density determines whether it will float in terrain water. The [Glass](/docs/reference/engine/enums/Material.md) material changes rendering behavior on moderate graphics settings by applying a bit of reflectiveness (similar to [Reflectance](/docs/reference/engine/classes/BasePart.md)) and perspective distortion. The effect is especially pronounced on sphere-shaped parts. Semi‑transparent parts behind [Glass](/docs/reference/engine/enums/Material.md) parts are not visible. ### Property: BasePart.MaterialVariant ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Appearance", "simulationAccess": true } ``` The system searches the [MaterialVariant](/docs/reference/engine/classes/MaterialVariant.md) instance with the specified `MaterialVariant` name and [Material](/docs/reference/engine/classes/BasePart.md) type. If it successfully finds a matching [MaterialVariant](/docs/reference/engine/classes/MaterialVariant.md) instance, it uses that instance to replace the default material. The default material can be the built-in material or an override [MaterialVariant](/docs/reference/engine/classes/MaterialVariant.md) specified in [MaterialService](/docs/reference/engine/classes/MaterialService.md). ### Property: BasePart.PivotOffset ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Pivot", "simulationAccess": true } ``` This property specifies the offset of the part's pivot from its [CFrame](/docs/reference/engine/datatypes/CFrame.md), that is [BasePart:GetPivot()](/docs/reference/engine/classes/BasePart.md) is the same as [BasePart.CFrame](/docs/reference/engine/classes/BasePart.md) multiplied by [BasePart.PivotOffset](/docs/reference/engine/classes/BasePart.md). This is convenient for setting the pivot to a location in **local** space, but setting a part's pivot to a location in **world** space can be done as follows: ``` local Workspace = game:GetService("Workspace") local part = Workspace.BluePart local desiredPivotCFrameInWorldSpace = CFrame.new(0, 10, 0) part.PivotOffset = part.CFrame:ToObjectSpace(desiredPivotCFrameInWorldSpace) ``` **Reset Pivot** This code sample shows a custom function for resetting the pivot of a model back to the center of that model's bounding box. ```lua local function resetPivot(model) local boundsCFrame = model:GetBoundingBox() if model.PrimaryPart then model.PrimaryPart.PivotOffset = model.PrimaryPart.CFrame:ToObjectSpace(boundsCFrame) else model.WorldPivot = boundsCFrame end end resetPivot(script.Parent) ``` **Clock Hands** This code sample creates a clock at the origin with a minute, second, and hour hand, and makes it tick, displaying the local time. ```lua local function createHand(length, width, yOffset) local part = Instance.new("Part") part.Size = Vector3.new(width, 0.1, length) part.Material = Enum.Material.Neon part.PivotOffset = CFrame.new(0, -(yOffset + 0.1), length / 2) part.Anchored = true part.Parent = workspace return part end local function positionHand(hand, fraction) hand:PivotTo(CFrame.fromEulerAnglesXYZ(0, -fraction * 2 * math.pi, 0)) end -- Create dial for i = 0, 11 do local dialPart = Instance.new("Part") dialPart.Size = Vector3.new(0.2, 0.2, 1) dialPart.TopSurface = Enum.SurfaceType.Smooth if i == 0 then dialPart.Size = Vector3.new(0.2, 0.2, 2) dialPart.Color = Color3.new(1, 0, 0) end dialPart.PivotOffset = CFrame.new(0, -0.1, 10.5) dialPart.Anchored = true dialPart:PivotTo(CFrame.fromEulerAnglesXYZ(0, (i / 12) * 2 * math.pi, 0)) dialPart.Parent = workspace end -- Create hands local hourHand = createHand(7, 1, 0) local minuteHand = createHand(10, 0.6, 0.1) local secondHand = createHand(11, 0.2, 0.2) -- Run clock while true do local components = os.date("*t") positionHand(hourHand, (components.hour + components.min / 60) / 12) positionHand(minuteHand, (components.min + components.sec / 60) / 60) positionHand(secondHand, components.sec / 60) task.wait() end ``` ### Property: BasePart.Reflectance ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance" } ``` The `Reflectance` property determines how much a part reflects the sky. A value of `0` indicates the part is not reflective at all, and a value of `1` indicates the part should fully reflect. Reflectance is not affected by [Transparency](/docs/reference/engine/classes/BasePart.md) unless the part is fully transparent, in which case reflectance will not render at all. Reflectance may or may not be ignored depending on the [Material](/docs/reference/engine/classes/BasePart.md) of the part. ### Property: BasePart.ResizeableFaces ```json { "type": "Faces", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` The `ResizeableFaces` property uses a [Faces](/docs/reference/engine/datatypes/Faces.md) object to describe the different faces on which a part may be resized. For most implementations of [BasePart](/docs/reference/engine/classes/BasePart.md), such as [Part](/docs/reference/engine/classes/Part.md) and [WedgePart](/docs/reference/engine/classes/WedgePart.md), this property includes all faces. However, [TrussPart](/docs/reference/engine/classes/TrussPart.md) will set its `ResizeableFaces` set to only two faces since those kinds of parts must have two [Size](/docs/reference/engine/classes/BasePart.md) dimensions of length `2`. This property is most commonly used with tools for building and manipulating parts and has little use outside of that context. The [Handles](/docs/reference/engine/classes/Handles.md) class, which has the [Handles.Faces](/docs/reference/engine/classes/Handles.md) property, can be used in conjunction with this property to display only the handles on faces that can be resized on a part. **Resize Handles** This code sample creates a Handles object and shows how to set the Faces property of the object. It also references ResizeableFaces of a part. Try placing this script in multiple kinds of parts to see how ResizeableFaces varies. ```lua -- Put this Script in several kinds of BasePart, like -- Part, TrussPart, WedgePart, CornerWedgePart, etc. local part = script.Parent -- Create a handles object for this part local handles = Instance.new("Handles") handles.Adornee = part handles.Parent = part -- Manually specify the faces applicable for this handle handles.Faces = Faces.new(Enum.NormalId.Top, Enum.NormalId.Front, Enum.NormalId.Left) -- Alternatively, use the faces on which the part can be resized. -- If part is a TrussPart with only two Size dimensions -- of length 2, then ResizeableFaces will only have two -- enabled faces. For other parts, all faces will be enabled. handles.Faces = part.ResizeableFaces ``` ### Property: BasePart.ResizeIncrement ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` The `ResizeIncrement` property is a read-only property that describes the smallest change in size allowable by the [Resize()](/docs/reference/engine/classes/BasePart.md) method. It differs between implementations of the [BasePart](/docs/reference/engine/classes/BasePart.md) abstract class; for instance, [Part](/docs/reference/engine/classes/Part.md) has this set to `1` while [TrussPart](/docs/reference/engine/classes/TrussPart.md) has this set to `2` since individual truss sections are 2×2×2 in size. ### Property: BasePart.RightSurface ```json { "type": "SurfaceType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Surface" } ``` The `RightSurface` property determines the type of surface used for the positive **X** direction of a part. When two parts' faces are placed next to each other, they may create a joint between them. ### Property: BasePart.RootPriority ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Part" } ``` This property is an integer between `-127` and `127` that takes precedence over all other rules for root part sort. When considering multiple parts that are not [Anchored](/docs/reference/engine/classes/BasePart.md) and which share the same [Massless](/docs/reference/engine/classes/BasePart.md) value, a part with a higher `RootPriority` will take priority over those with lower `RootPriority`. You can use this property to control which part of an assembly is the root part and keep the root part stable if size changes. See also [Assemblies](/docs/en-us/physics/assemblies.md), an article documenting what root parts are and how to use them. ### Property: BasePart.Rotation ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "simulationAccess": true } ``` The rotation of the part in degrees for the three axes. When setting this property, any [Welds](/docs/reference/engine/classes/Weld.md) or [Motor6Ds](/docs/reference/engine/classes/Motor6D.md) connected to this part will have the matching [C0](/docs/reference/engine/classes/JointInstance.md) or [C1](/docs/reference/engine/classes/JointInstance.md) property updated to allow the part to move relative to any other parts it is joined to. [WeldConstraints](/docs/reference/engine/classes/WeldConstraint.md) will also be temporarily disabled and re-enabled during the move. ### Property: BasePart.Size ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Transform", "simulationAccess": true } ``` A part's `Size` property determines its **visual** dimensions, while [ExtentsSize](/docs/reference/engine/classes/BasePart.md) represents the actual size used by the physics engine, such as in [collision detection](/docs/en-us/workspace/collisions.md). The individual dimensions (length, width, height) can be as low as `0.001` and as high as `2048`. Size dimensions below `0.05` will be **visually** represented as if the part's dimensions are `0.05`. A part's `Size` is used in a variety of additional ways: - To influence its mass as given by [GetMass()](/docs/reference/engine/classes/BasePart.md). - By [ParticleEmitter](/docs/reference/engine/classes/ParticleEmitter.md) to determine the area from which particles are spawned. - By [BlockMesh](/docs/reference/engine/classes/BlockMesh.md) to partially determine the rendered rectangular prism. - By [SpecialMesh](/docs/reference/engine/classes/SpecialMesh.md) for certain [MeshTypes](/docs/reference/engine/classes/SpecialMesh.md) to determine the size of the rendered mesh. - By [SurfaceLight](/docs/reference/engine/classes/SurfaceLight.md) to determine the space to illuminate. **Pyramid Builder** This code sample constructs a pyramid by stacking parts that get progressively smaller. It also colors the parts so they blend between a start color and end color. ```lua local TOWER_BASE_SIZE = 30 local position = Vector3.new(50, 50, 50) local hue = math.random() local color0 = Color3.fromHSV(hue, 1, 1) local color1 = Color3.fromHSV((hue + 0.35) % 1, 1, 1) local model = Instance.new("Model") model.Name = "Tower" for i = TOWER_BASE_SIZE, 1, -2 do local part = Instance.new("Part") part.Size = Vector3.new(i, 2, i) part.Position = position part.Anchored = true part.Parent = model -- Tween from color0 and color1 local perc = i / TOWER_BASE_SIZE part.Color = Color3.new( color0.R * perc + color1.R * (1 - perc), color0.G * perc + color1.G * (1 - perc), color0.B * perc + color1.B * (1 - perc) ) position = position + Vector3.new(0, part.Size.Y, 0) end model.Parent = workspace ``` ### Property: BasePart.TopSurface ```json { "type": "SurfaceType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Surface" } ``` The `TopSurface` property determines the type of surface used for the positive **Y** direction of a part. When two parts' faces are placed next to each other, they may create a joint between them. ### Property: BasePart.Transparency ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance" } ``` The `Transparency` property controls the visibility of a part on a scale of `0` to `1` where `0` is completely visible (opaque) and `1` is completely invisible (not rendered at all). While fully transparent parts are not rendered at all, partially transparent objects have some significant rendering costs. Having many translucent parts may impact performance. When transparent parts overlap, render order may act unpredictably, so you should avoid semi-transparent parts from overlapping. See also [LocalTransparencyModifier](/docs/reference/engine/classes/BasePart.md) as a multiplier to `Transparency` that's only visible to the local client. **Fade Door** This code sample shows how a part can fade away when touched by a Humanoid then reappear a moment after to create a passable door. ```lua -- Paste into a Script inside a tall part local part = script.Parent local OPEN_TIME = 1 -- Can the door be opened at the moment? local debounce = false local function open() part.CanCollide = false part.Transparency = 0.7 part.BrickColor = BrickColor.new("Black") end local function close() part.CanCollide = true part.Transparency = 0 part.BrickColor = BrickColor.new("Bright blue") end local function onTouch(otherPart) -- If the door was already open, do nothing if debounce then print("D") return end -- Check if touched by a Humanoid local human = otherPart.Parent:FindFirstChildOfClass("Humanoid") if not human then print("not human") return end -- Perform the door opening sequence debounce = true open() task.wait(OPEN_TIME) close() debounce = false end part.Touched:Connect(onTouch) close() ``` ### Property: BasePart.brickColor ```json { "type": "BrickColor", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Appearance" } ``` > **Deprecated:** This deprecated property is an old Camel Case variant of the Pascal Case [BasePart.BrickColor](/docs/reference/engine/classes/BasePart.md), which should be used instead. ### Property: BasePart.CollisionGroupId ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Collision", "simulationAccess": true } ``` The [BasePart.CollisionGroupId](/docs/reference/engine/classes/BasePart.md) property describes the ID number of the part's collision group. Parts start off in the `"Default"` group whose ID is 0. If a part is unregistered, the value becomes -1. This value cannot be less than -1 and it cannot exceed [PhysicsService:GetMaxCollisionGroups()](/docs/reference/engine/classes/PhysicsService.md). Invalid IDs are clamped. Although this property can be directly changed, it's recommended that you specify the collision group by setting [BasePart.CollisionGroup](/docs/reference/engine/classes/BasePart.md) to the collision group's **name**. ### Property: BasePart.SpecificGravity ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data" } ``` > **Deprecated:** This item is deprecated. See [BasePart.CustomPhysicalProperties](/docs/reference/engine/classes/BasePart.md) to see how to configure the physical properties of BaseParts. Do not use it for new work. The ratio of the part's density to the density of water determined by the [BasePart.Material](/docs/reference/engine/classes/BasePart.md). Effects the part's behavior when in a water terrain cell. Essentially, SpecificGravity refers to how many times more dense a part is than water. | Material | SpecificGravity | | --- | --- | | Plastic | 0.7 | | Wood | 0.35 | | Slate | 2.7 | | Concrete | 2.4 | | CorrodedMetal | 7.85 | | DiamondMetal | 7.85 | | Foil | 7.6 | | Grass | 0.9 | | Ice | 0.91 | | Marble | 2.56 | | Granite | 2.7 | | Brick | 1.92 | | Pebble | 2.4 | | Sand | 1.6 | | Fabric | 0.7 | | SmoothPlastic | 0.7 | | Metal | 7.85 | | WoodPlanks | 0.35 | | Cobblestone | 2.7 | ### Property: BasePart.BackParamA *(hidden)* ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Surface Inputs" } ``` The `BackParamA` property is relevant when a part's [BasePart.BackSurface](/docs/reference/engine/classes/BasePart.md) is set to Motor or SteppingMotor and [BasePart.BackSurfaceInput](/docs/reference/engine/classes/BasePart.md) is set to Sin. It determines the **amplitude** of the motor's rotational velocity. **Motor Control** This code sample demonstrates how surface properties can be set using only a NormalId (Top, Front, etc). It switches a motor's -SurfaceInput from NoInput, Constant and Sin to show how each work using -ParamA and -ParamB properties. ```lua -- Paste this into a Script inside a part with a Motor SurfaceType local partMotor = script.Parent -- Place a brick called "MovingPart" so it is touching the Motor surface -- For this example, we use TopSurface, TopSurfaceInput, TopParamA and TopParamB -- However, this will work for all faces (NormalId): Top, Bottom, Left, Right, Front and Back -- A function to quickly set all surface properties at once local function setFaceSurfaceInputParams(normalId, surfaceType, inputType, paramA, paramB) local surfaceName = normalId.Name -- e.g. "Top", "Bottom", etc -- Syntax Note: in Lua, part.Something is the same as part["Something"] -- The difference is that the latter allows us to use a string ("Something"), while -- the former requires use of an identifier (.Something). Below, we build of each the surface -- properties below by concatenating the surface name with the property postfix. -- Set "___Surface", eg "TopSurface" partMotor[surfaceName .. "Surface"] = surfaceType -- Set "___SurfaceInput", eg "TopSurfaceInput" partMotor[surfaceName .. "SurfaceInput"] = inputType -- Set "___ParamA", eg "TopParamA" partMotor[surfaceName .. "ParamA"] = paramA -- Set "___ParamB", eg "TopParamB" partMotor[surfaceName .. "ParamB"] = paramB end local normalId = Enum.NormalId.Top while true do -- Set to NoInput, where the motor will not operate at all setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.NoInput, 0, 0) task.wait(1) -- Set to Constant, where motor rotational velocity = paramB setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.Constant, 0, 0.25) task.wait(2) -- Set to Sin, where motor rotational velocity = paramA * math.sin(time * paramB) -- Since we're using pi (~3.14), the frequency of rotation is 1 second (per definition of sine function) setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.Sin, 0.25, math.pi) task.wait(3) end ``` ### Property: BasePart.BackParamB *(hidden)* ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Surface Inputs" } ``` The `BackParamB` property is relevant when a part's [BasePart.BackSurface](/docs/reference/engine/classes/BasePart.md) is set to Motor or SteppingMotor and [BasePart.BackSurfaceInput](/docs/reference/engine/classes/BasePart.md) is set to Constant or Sin. For Constant, it determines the constant rotational velocity of the motor. For Sin, it determines the **frequency** of the motor's rotational velocity. **Motor Control** This code sample demonstrates how surface properties can be set using only a NormalId (Top, Front, etc). It switches a motor's -SurfaceInput from NoInput, Constant and Sin to show how each work using -ParamA and -ParamB properties. ```lua -- Paste this into a Script inside a part with a Motor SurfaceType local partMotor = script.Parent -- Place a brick called "MovingPart" so it is touching the Motor surface -- For this example, we use TopSurface, TopSurfaceInput, TopParamA and TopParamB -- However, this will work for all faces (NormalId): Top, Bottom, Left, Right, Front and Back -- A function to quickly set all surface properties at once local function setFaceSurfaceInputParams(normalId, surfaceType, inputType, paramA, paramB) local surfaceName = normalId.Name -- e.g. "Top", "Bottom", etc -- Syntax Note: in Lua, part.Something is the same as part["Something"] -- The difference is that the latter allows us to use a string ("Something"), while -- the former requires use of an identifier (.Something). Below, we build of each the surface -- properties below by concatenating the surface name with the property postfix. -- Set "___Surface", eg "TopSurface" partMotor[surfaceName .. "Surface"] = surfaceType -- Set "___SurfaceInput", eg "TopSurfaceInput" partMotor[surfaceName .. "SurfaceInput"] = inputType -- Set "___ParamA", eg "TopParamA" partMotor[surfaceName .. "ParamA"] = paramA -- Set "___ParamB", eg "TopParamB" partMotor[surfaceName .. "ParamB"] = paramB end local normalId = Enum.NormalId.Top while true do -- Set to NoInput, where the motor will not operate at all setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.NoInput, 0, 0) task.wait(1) -- Set to Constant, where motor rotational velocity = paramB setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.Constant, 0, 0.25) task.wait(2) -- Set to Sin, where motor rotational velocity = paramA * math.sin(time * paramB) -- Since we're using pi (~3.14), the frequency of rotation is 1 second (per definition of sine function) setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.Sin, 0.25, math.pi) task.wait(3) end ``` ### Property: BasePart.BackSurfaceInput *(hidden)* ```json { "type": "InputType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Surface Inputs" } ``` The `BackSurfaceInput` property determines the kind of input provided to a part's [BasePart.BackSurface](/docs/reference/engine/classes/BasePart.md). This is only relevant for Motor or SteppingMotor SurfaceTypes. This property determines how [BasePart.BackParamA](/docs/reference/engine/classes/BasePart.md) and [BasePart.BackParamB](/docs/reference/engine/classes/BasePart.md) are used. For brevity, these properties will be referred to as ParamA and ParamB, respectively. - By default, this is set to NoInput. This stops the motor altogether. - For Constant, the motor rotates at a constant velocity equal to `ParamB`. - For Sin, the motor rotates at a velocity equal to `ParamA * math.sin(workspace.DistributedGameTime * ParamB)`. See [Workspace.DistributedGameTime](/docs/reference/engine/classes/Workspace.md). **Motor Control** This code sample demonstrates how surface properties can be set using only a NormalId (Top, Front, etc). It switches a motor's -SurfaceInput from NoInput, Constant and Sin to show how each work using -ParamA and -ParamB properties. ```lua -- Paste this into a Script inside a part with a Motor SurfaceType local partMotor = script.Parent -- Place a brick called "MovingPart" so it is touching the Motor surface -- For this example, we use TopSurface, TopSurfaceInput, TopParamA and TopParamB -- However, this will work for all faces (NormalId): Top, Bottom, Left, Right, Front and Back -- A function to quickly set all surface properties at once local function setFaceSurfaceInputParams(normalId, surfaceType, inputType, paramA, paramB) local surfaceName = normalId.Name -- e.g. "Top", "Bottom", etc -- Syntax Note: in Lua, part.Something is the same as part["Something"] -- The difference is that the latter allows us to use a string ("Something"), while -- the former requires use of an identifier (.Something). Below, we build of each the surface -- properties below by concatenating the surface name with the property postfix. -- Set "___Surface", eg "TopSurface" partMotor[surfaceName .. "Surface"] = surfaceType -- Set "___SurfaceInput", eg "TopSurfaceInput" partMotor[surfaceName .. "SurfaceInput"] = inputType -- Set "___ParamA", eg "TopParamA" partMotor[surfaceName .. "ParamA"] = paramA -- Set "___ParamB", eg "TopParamB" partMotor[surfaceName .. "ParamB"] = paramB end local normalId = Enum.NormalId.Top while true do -- Set to NoInput, where the motor will not operate at all setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.NoInput, 0, 0) task.wait(1) -- Set to Constant, where motor rotational velocity = paramB setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.Constant, 0, 0.25) task.wait(2) -- Set to Sin, where motor rotational velocity = paramA * math.sin(time * paramB) -- Since we're using pi (~3.14), the frequency of rotation is 1 second (per definition of sine function) setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.Sin, 0.25, math.pi) task.wait(3) end ``` ### Property: BasePart.BottomParamA *(hidden)* ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Surface Inputs" } ``` The `BottomParamA` property is relevant when a part's [BasePart.BottomSurface](/docs/reference/engine/classes/BasePart.md) is set to Motor or SteppingMotor and [BasePart.BottomSurfaceInput](/docs/reference/engine/classes/BasePart.md) is set to Sin. It determines the **amplitude** of the motor's rotational velocity. **Motor Control** This code sample demonstrates how surface properties can be set using only a NormalId (Top, Front, etc). It switches a motor's -SurfaceInput from NoInput, Constant and Sin to show how each work using -ParamA and -ParamB properties. ```lua -- Paste this into a Script inside a part with a Motor SurfaceType local partMotor = script.Parent -- Place a brick called "MovingPart" so it is touching the Motor surface -- For this example, we use TopSurface, TopSurfaceInput, TopParamA and TopParamB -- However, this will work for all faces (NormalId): Top, Bottom, Left, Right, Front and Back -- A function to quickly set all surface properties at once local function setFaceSurfaceInputParams(normalId, surfaceType, inputType, paramA, paramB) local surfaceName = normalId.Name -- e.g. "Top", "Bottom", etc -- Syntax Note: in Lua, part.Something is the same as part["Something"] -- The difference is that the latter allows us to use a string ("Something"), while -- the former requires use of an identifier (.Something). Below, we build of each the surface -- properties below by concatenating the surface name with the property postfix. -- Set "___Surface", eg "TopSurface" partMotor[surfaceName .. "Surface"] = surfaceType -- Set "___SurfaceInput", eg "TopSurfaceInput" partMotor[surfaceName .. "SurfaceInput"] = inputType -- Set "___ParamA", eg "TopParamA" partMotor[surfaceName .. "ParamA"] = paramA -- Set "___ParamB", eg "TopParamB" partMotor[surfaceName .. "ParamB"] = paramB end local normalId = Enum.NormalId.Top while true do -- Set to NoInput, where the motor will not operate at all setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.NoInput, 0, 0) task.wait(1) -- Set to Constant, where motor rotational velocity = paramB setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.Constant, 0, 0.25) task.wait(2) -- Set to Sin, where motor rotational velocity = paramA * math.sin(time * paramB) -- Since we're using pi (~3.14), the frequency of rotation is 1 second (per definition of sine function) setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.Sin, 0.25, math.pi) task.wait(3) end ``` ### Property: BasePart.BottomParamB *(hidden)* ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Surface Inputs" } ``` The `BottomParamB` property is relevant when a part's [BasePart.BottomSurface](/docs/reference/engine/classes/BasePart.md) is set to Motor or SteppingMotor and [BasePart.BottomSurfaceInput](/docs/reference/engine/classes/BasePart.md) is set to Constant or Sin. For Constant, it determines the constant rotational velocity of the motor. For Sin, it determines the **frequency** of the motor's rotational velocity. **Motor Control** This code sample demonstrates how surface properties can be set using only a NormalId (Top, Front, etc). It switches a motor's -SurfaceInput from NoInput, Constant and Sin to show how each work using -ParamA and -ParamB properties. ```lua -- Paste this into a Script inside a part with a Motor SurfaceType local partMotor = script.Parent -- Place a brick called "MovingPart" so it is touching the Motor surface -- For this example, we use TopSurface, TopSurfaceInput, TopParamA and TopParamB -- However, this will work for all faces (NormalId): Top, Bottom, Left, Right, Front and Back -- A function to quickly set all surface properties at once local function setFaceSurfaceInputParams(normalId, surfaceType, inputType, paramA, paramB) local surfaceName = normalId.Name -- e.g. "Top", "Bottom", etc -- Syntax Note: in Lua, part.Something is the same as part["Something"] -- The difference is that the latter allows us to use a string ("Something"), while -- the former requires use of an identifier (.Something). Below, we build of each the surface -- properties below by concatenating the surface name with the property postfix. -- Set "___Surface", eg "TopSurface" partMotor[surfaceName .. "Surface"] = surfaceType -- Set "___SurfaceInput", eg "TopSurfaceInput" partMotor[surfaceName .. "SurfaceInput"] = inputType -- Set "___ParamA", eg "TopParamA" partMotor[surfaceName .. "ParamA"] = paramA -- Set "___ParamB", eg "TopParamB" partMotor[surfaceName .. "ParamB"] = paramB end local normalId = Enum.NormalId.Top while true do -- Set to NoInput, where the motor will not operate at all setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.NoInput, 0, 0) task.wait(1) -- Set to Constant, where motor rotational velocity = paramB setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.Constant, 0, 0.25) task.wait(2) -- Set to Sin, where motor rotational velocity = paramA * math.sin(time * paramB) -- Since we're using pi (~3.14), the frequency of rotation is 1 second (per definition of sine function) setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.Sin, 0.25, math.pi) task.wait(3) end ``` ### Property: BasePart.BottomSurfaceInput *(hidden)* ```json { "type": "InputType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Surface Inputs" } ``` The `BottomSurfaceInput` property determines the kind of input provided to a part's [BasePart.BottomSurface](/docs/reference/engine/classes/BasePart.md). This is only relevant for Motor or SteppingMotor SurfaceTypes. This property determines how [BasePart.BottomParamA](/docs/reference/engine/classes/BasePart.md) and [BasePart.BottomParamB](/docs/reference/engine/classes/BasePart.md) are used. For brevity, these properties will be referred to as ParamA and ParamB, respectively. - By default, this is set to NoInput. This stops the motor altogether. - For Constant, the motor rotates at a constant velocity equal to `ParamB`. - For Sin, the motor rotates at a velocity equal to `ParamA * math.sin(workspace.DistributedGameTime * ParamB)`. See [Workspace.DistributedGameTime](/docs/reference/engine/classes/Workspace.md). **Motor Control** This code sample demonstrates how surface properties can be set using only a NormalId (Top, Front, etc). It switches a motor's -SurfaceInput from NoInput, Constant and Sin to show how each work using -ParamA and -ParamB properties. ```lua -- Paste this into a Script inside a part with a Motor SurfaceType local partMotor = script.Parent -- Place a brick called "MovingPart" so it is touching the Motor surface -- For this example, we use TopSurface, TopSurfaceInput, TopParamA and TopParamB -- However, this will work for all faces (NormalId): Top, Bottom, Left, Right, Front and Back -- A function to quickly set all surface properties at once local function setFaceSurfaceInputParams(normalId, surfaceType, inputType, paramA, paramB) local surfaceName = normalId.Name -- e.g. "Top", "Bottom", etc -- Syntax Note: in Lua, part.Something is the same as part["Something"] -- The difference is that the latter allows us to use a string ("Something"), while -- the former requires use of an identifier (.Something). Below, we build of each the surface -- properties below by concatenating the surface name with the property postfix. -- Set "___Surface", eg "TopSurface" partMotor[surfaceName .. "Surface"] = surfaceType -- Set "___SurfaceInput", eg "TopSurfaceInput" partMotor[surfaceName .. "SurfaceInput"] = inputType -- Set "___ParamA", eg "TopParamA" partMotor[surfaceName .. "ParamA"] = paramA -- Set "___ParamB", eg "TopParamB" partMotor[surfaceName .. "ParamB"] = paramB end local normalId = Enum.NormalId.Top while true do -- Set to NoInput, where the motor will not operate at all setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.NoInput, 0, 0) task.wait(1) -- Set to Constant, where motor rotational velocity = paramB setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.Constant, 0, 0.25) task.wait(2) -- Set to Sin, where motor rotational velocity = paramA * math.sin(time * paramB) -- Since we're using pi (~3.14), the frequency of rotation is 1 second (per definition of sine function) setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.Sin, 0.25, math.pi) task.wait(3) end ``` ### Property: BasePart.Elasticity *(hidden)* ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Part" } ``` > **Deprecated:** This is only one of multiple physics-related properties. It has been deprecated in favor of [BasePart.CustomPhysicalProperties](/docs/reference/engine/classes/BasePart.md), which combines these properties into one. The Elasticity of a part is now determined by either its [Material](/docs/reference/engine/enums/Material.md) or its `CustomPhysicalProperties`. ### Property: BasePart.Friction *(hidden)* ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Part" } ``` > **Deprecated:** This is only one of multiple physics-related properties. It has been deprecated in favor of [BasePart.CustomPhysicalProperties](/docs/reference/engine/classes/BasePart.md), which combines these properties into one. Used to control the Friction of the part, but now it no longer does anything. The Friction of a part is now determined by either its [Material](/docs/reference/engine/classes/BasePart.md) or its [CustomPhysicalProperties](/docs/reference/engine/classes/BasePart.md). ### Property: BasePart.FrontParamA *(hidden)* ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Surface Inputs" } ``` The `FrontParamA` property is relevant when a part's [BasePart.FrontSurface](/docs/reference/engine/classes/BasePart.md) is set to Motor or SteppingMotor and [BasePart.FrontSurfaceInput](/docs/reference/engine/classes/BasePart.md) is set to Sin. It determines the **amplitude** of the motor's rotational velocity. **Motor Control** This code sample demonstrates how surface properties can be set using only a NormalId (Top, Front, etc). It switches a motor's -SurfaceInput from NoInput, Constant and Sin to show how each work using -ParamA and -ParamB properties. ```lua -- Paste this into a Script inside a part with a Motor SurfaceType local partMotor = script.Parent -- Place a brick called "MovingPart" so it is touching the Motor surface -- For this example, we use TopSurface, TopSurfaceInput, TopParamA and TopParamB -- However, this will work for all faces (NormalId): Top, Bottom, Left, Right, Front and Back -- A function to quickly set all surface properties at once local function setFaceSurfaceInputParams(normalId, surfaceType, inputType, paramA, paramB) local surfaceName = normalId.Name -- e.g. "Top", "Bottom", etc -- Syntax Note: in Lua, part.Something is the same as part["Something"] -- The difference is that the latter allows us to use a string ("Something"), while -- the former requires use of an identifier (.Something). Below, we build of each the surface -- properties below by concatenating the surface name with the property postfix. -- Set "___Surface", eg "TopSurface" partMotor[surfaceName .. "Surface"] = surfaceType -- Set "___SurfaceInput", eg "TopSurfaceInput" partMotor[surfaceName .. "SurfaceInput"] = inputType -- Set "___ParamA", eg "TopParamA" partMotor[surfaceName .. "ParamA"] = paramA -- Set "___ParamB", eg "TopParamB" partMotor[surfaceName .. "ParamB"] = paramB end local normalId = Enum.NormalId.Top while true do -- Set to NoInput, where the motor will not operate at all setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.NoInput, 0, 0) task.wait(1) -- Set to Constant, where motor rotational velocity = paramB setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.Constant, 0, 0.25) task.wait(2) -- Set to Sin, where motor rotational velocity = paramA * math.sin(time * paramB) -- Since we're using pi (~3.14), the frequency of rotation is 1 second (per definition of sine function) setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.Sin, 0.25, math.pi) task.wait(3) end ``` ### Property: BasePart.FrontParamB *(hidden)* ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Surface Inputs" } ``` The `FrontParamB` property is relevant when a part's [BasePart.FrontSurface](/docs/reference/engine/classes/BasePart.md) is set to Motor or SteppingMotor and [BasePart.FrontSurfaceInput](/docs/reference/engine/classes/BasePart.md) is set to Constant or Sin. For Constant, it determines the constant rotational velocity of the motor. For Sin, it determines the **frequency** of the motor's rotational velocity. **Motor Control** This code sample demonstrates how surface properties can be set using only a NormalId (Top, Front, etc). It switches a motor's -SurfaceInput from NoInput, Constant and Sin to show how each work using -ParamA and -ParamB properties. ```lua -- Paste this into a Script inside a part with a Motor SurfaceType local partMotor = script.Parent -- Place a brick called "MovingPart" so it is touching the Motor surface -- For this example, we use TopSurface, TopSurfaceInput, TopParamA and TopParamB -- However, this will work for all faces (NormalId): Top, Bottom, Left, Right, Front and Back -- A function to quickly set all surface properties at once local function setFaceSurfaceInputParams(normalId, surfaceType, inputType, paramA, paramB) local surfaceName = normalId.Name -- e.g. "Top", "Bottom", etc -- Syntax Note: in Lua, part.Something is the same as part["Something"] -- The difference is that the latter allows us to use a string ("Something"), while -- the former requires use of an identifier (.Something). Below, we build of each the surface -- properties below by concatenating the surface name with the property postfix. -- Set "___Surface", eg "TopSurface" partMotor[surfaceName .. "Surface"] = surfaceType -- Set "___SurfaceInput", eg "TopSurfaceInput" partMotor[surfaceName .. "SurfaceInput"] = inputType -- Set "___ParamA", eg "TopParamA" partMotor[surfaceName .. "ParamA"] = paramA -- Set "___ParamB", eg "TopParamB" partMotor[surfaceName .. "ParamB"] = paramB end local normalId = Enum.NormalId.Top while true do -- Set to NoInput, where the motor will not operate at all setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.NoInput, 0, 0) task.wait(1) -- Set to Constant, where motor rotational velocity = paramB setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.Constant, 0, 0.25) task.wait(2) -- Set to Sin, where motor rotational velocity = paramA * math.sin(time * paramB) -- Since we're using pi (~3.14), the frequency of rotation is 1 second (per definition of sine function) setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.Sin, 0.25, math.pi) task.wait(3) end ``` ### Property: BasePart.FrontSurfaceInput *(hidden)* ```json { "type": "InputType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Surface Inputs" } ``` The `FrontSurfaceInput` property determines the kind of input provided to a part's [BasePart.FrontSurface](/docs/reference/engine/classes/BasePart.md). This is only relevant for Motor or SteppingMotor SurfaceTypes. This property determines how [BasePart.FrontParamA](/docs/reference/engine/classes/BasePart.md) and [BasePart.FrontParamB](/docs/reference/engine/classes/BasePart.md) are used. For brevity, these properties will be referred to as ParamA and ParamB, respectively. - By default, this is set to NoInput. This stops the motor altogether. - For Constant, the motor rotates at a constant velocity equal to `ParamB`. - For Sin, the motor rotates at a velocity equal to `ParamA * math.sin(workspace.DistributedGameTime * ParamB)`. See [Workspace.DistributedGameTime](/docs/reference/engine/classes/Workspace.md). **Motor Control** This code sample demonstrates how surface properties can be set using only a NormalId (Top, Front, etc). It switches a motor's -SurfaceInput from NoInput, Constant and Sin to show how each work using -ParamA and -ParamB properties. ```lua -- Paste this into a Script inside a part with a Motor SurfaceType local partMotor = script.Parent -- Place a brick called "MovingPart" so it is touching the Motor surface -- For this example, we use TopSurface, TopSurfaceInput, TopParamA and TopParamB -- However, this will work for all faces (NormalId): Top, Bottom, Left, Right, Front and Back -- A function to quickly set all surface properties at once local function setFaceSurfaceInputParams(normalId, surfaceType, inputType, paramA, paramB) local surfaceName = normalId.Name -- e.g. "Top", "Bottom", etc -- Syntax Note: in Lua, part.Something is the same as part["Something"] -- The difference is that the latter allows us to use a string ("Something"), while -- the former requires use of an identifier (.Something). Below, we build of each the surface -- properties below by concatenating the surface name with the property postfix. -- Set "___Surface", eg "TopSurface" partMotor[surfaceName .. "Surface"] = surfaceType -- Set "___SurfaceInput", eg "TopSurfaceInput" partMotor[surfaceName .. "SurfaceInput"] = inputType -- Set "___ParamA", eg "TopParamA" partMotor[surfaceName .. "ParamA"] = paramA -- Set "___ParamB", eg "TopParamB" partMotor[surfaceName .. "ParamB"] = paramB end local normalId = Enum.NormalId.Top while true do -- Set to NoInput, where the motor will not operate at all setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.NoInput, 0, 0) task.wait(1) -- Set to Constant, where motor rotational velocity = paramB setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.Constant, 0, 0.25) task.wait(2) -- Set to Sin, where motor rotational velocity = paramA * math.sin(time * paramB) -- Since we're using pi (~3.14), the frequency of rotation is 1 second (per definition of sine function) setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.Sin, 0.25, math.pi) task.wait(3) end ``` ### Property: BasePart.LeftParamA *(hidden)* ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Surface Inputs" } ``` The `LeftParamA` property is relevant when a part's [BasePart.LeftSurface](/docs/reference/engine/classes/BasePart.md) is set to Motor or SteppingMotor and [BasePart.LeftSurfaceInput](/docs/reference/engine/classes/BasePart.md) is set to Sin. It determines the **amplitude** of the motor's rotational velocity. **Motor Control** This code sample demonstrates how surface properties can be set using only a NormalId (Top, Front, etc). It switches a motor's -SurfaceInput from NoInput, Constant and Sin to show how each work using -ParamA and -ParamB properties. ```lua -- Paste this into a Script inside a part with a Motor SurfaceType local partMotor = script.Parent -- Place a brick called "MovingPart" so it is touching the Motor surface -- For this example, we use TopSurface, TopSurfaceInput, TopParamA and TopParamB -- However, this will work for all faces (NormalId): Top, Bottom, Left, Right, Front and Back -- A function to quickly set all surface properties at once local function setFaceSurfaceInputParams(normalId, surfaceType, inputType, paramA, paramB) local surfaceName = normalId.Name -- e.g. "Top", "Bottom", etc -- Syntax Note: in Lua, part.Something is the same as part["Something"] -- The difference is that the latter allows us to use a string ("Something"), while -- the former requires use of an identifier (.Something). Below, we build of each the surface -- properties below by concatenating the surface name with the property postfix. -- Set "___Surface", eg "TopSurface" partMotor[surfaceName .. "Surface"] = surfaceType -- Set "___SurfaceInput", eg "TopSurfaceInput" partMotor[surfaceName .. "SurfaceInput"] = inputType -- Set "___ParamA", eg "TopParamA" partMotor[surfaceName .. "ParamA"] = paramA -- Set "___ParamB", eg "TopParamB" partMotor[surfaceName .. "ParamB"] = paramB end local normalId = Enum.NormalId.Top while true do -- Set to NoInput, where the motor will not operate at all setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.NoInput, 0, 0) task.wait(1) -- Set to Constant, where motor rotational velocity = paramB setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.Constant, 0, 0.25) task.wait(2) -- Set to Sin, where motor rotational velocity = paramA * math.sin(time * paramB) -- Since we're using pi (~3.14), the frequency of rotation is 1 second (per definition of sine function) setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.Sin, 0.25, math.pi) task.wait(3) end ``` ### Property: BasePart.LeftParamB *(hidden)* ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Surface Inputs" } ``` The LeftParamB property is relevant when a part's [BasePart.LeftSurface](/docs/reference/engine/classes/BasePart.md) is set to Motor or SteppingMotor and [BasePart.LeftSurfaceInput](/docs/reference/engine/classes/BasePart.md) is set to Constant or Sin. For Constant, it determines the constant rotational velocity of the motor. For Sin, it determines the **frequency** of the motor's rotational velocity. **Motor Control** This code sample demonstrates how surface properties can be set using only a NormalId (Top, Front, etc). It switches a motor's -SurfaceInput from NoInput, Constant and Sin to show how each work using -ParamA and -ParamB properties. ```lua -- Paste this into a Script inside a part with a Motor SurfaceType local partMotor = script.Parent -- Place a brick called "MovingPart" so it is touching the Motor surface -- For this example, we use TopSurface, TopSurfaceInput, TopParamA and TopParamB -- However, this will work for all faces (NormalId): Top, Bottom, Left, Right, Front and Back -- A function to quickly set all surface properties at once local function setFaceSurfaceInputParams(normalId, surfaceType, inputType, paramA, paramB) local surfaceName = normalId.Name -- e.g. "Top", "Bottom", etc -- Syntax Note: in Lua, part.Something is the same as part["Something"] -- The difference is that the latter allows us to use a string ("Something"), while -- the former requires use of an identifier (.Something). Below, we build of each the surface -- properties below by concatenating the surface name with the property postfix. -- Set "___Surface", eg "TopSurface" partMotor[surfaceName .. "Surface"] = surfaceType -- Set "___SurfaceInput", eg "TopSurfaceInput" partMotor[surfaceName .. "SurfaceInput"] = inputType -- Set "___ParamA", eg "TopParamA" partMotor[surfaceName .. "ParamA"] = paramA -- Set "___ParamB", eg "TopParamB" partMotor[surfaceName .. "ParamB"] = paramB end local normalId = Enum.NormalId.Top while true do -- Set to NoInput, where the motor will not operate at all setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.NoInput, 0, 0) task.wait(1) -- Set to Constant, where motor rotational velocity = paramB setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.Constant, 0, 0.25) task.wait(2) -- Set to Sin, where motor rotational velocity = paramA * math.sin(time * paramB) -- Since we're using pi (~3.14), the frequency of rotation is 1 second (per definition of sine function) setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.Sin, 0.25, math.pi) task.wait(3) end ``` ### Property: BasePart.LeftSurfaceInput *(hidden)* ```json { "type": "InputType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Surface Inputs" } ``` The `LeftSurfaceInput` property determines the kind of input provided to a part's [BasePart.LeftSurface](/docs/reference/engine/classes/BasePart.md). This is only relevant for Motor or SteppingMotor SurfaceTypes. This property determines how [BasePart.LeftParamA](/docs/reference/engine/classes/BasePart.md) and [BasePart.LeftParamB](/docs/reference/engine/classes/BasePart.md) are used. For brevity, these properties will be referred to as ParamA and ParamB, respectively. - By default, this is set to NoInput. This stops the motor altogether. - For Constant, the motor rotates at a constant velocity equal to `ParamB`. - For Sin, the motor rotates at a velocity equal to `ParamA * math.sin(workspace.DistributedGameTime * ParamB)`. See [Workspace.DistributedGameTime](/docs/reference/engine/classes/Workspace.md). **Motor Control** This code sample demonstrates how surface properties can be set using only a NormalId (Top, Front, etc). It switches a motor's -SurfaceInput from NoInput, Constant and Sin to show how each work using -ParamA and -ParamB properties. ```lua -- Paste this into a Script inside a part with a Motor SurfaceType local partMotor = script.Parent -- Place a brick called "MovingPart" so it is touching the Motor surface -- For this example, we use TopSurface, TopSurfaceInput, TopParamA and TopParamB -- However, this will work for all faces (NormalId): Top, Bottom, Left, Right, Front and Back -- A function to quickly set all surface properties at once local function setFaceSurfaceInputParams(normalId, surfaceType, inputType, paramA, paramB) local surfaceName = normalId.Name -- e.g. "Top", "Bottom", etc -- Syntax Note: in Lua, part.Something is the same as part["Something"] -- The difference is that the latter allows us to use a string ("Something"), while -- the former requires use of an identifier (.Something). Below, we build of each the surface -- properties below by concatenating the surface name with the property postfix. -- Set "___Surface", eg "TopSurface" partMotor[surfaceName .. "Surface"] = surfaceType -- Set "___SurfaceInput", eg "TopSurfaceInput" partMotor[surfaceName .. "SurfaceInput"] = inputType -- Set "___ParamA", eg "TopParamA" partMotor[surfaceName .. "ParamA"] = paramA -- Set "___ParamB", eg "TopParamB" partMotor[surfaceName .. "ParamB"] = paramB end local normalId = Enum.NormalId.Top while true do -- Set to NoInput, where the motor will not operate at all setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.NoInput, 0, 0) task.wait(1) -- Set to Constant, where motor rotational velocity = paramB setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.Constant, 0, 0.25) task.wait(2) -- Set to Sin, where motor rotational velocity = paramA * math.sin(time * paramB) -- Since we're using pi (~3.14), the frequency of rotation is 1 second (per definition of sine function) setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.Sin, 0.25, math.pi) task.wait(3) end ``` ### Property: BasePart.LocalTransparencyModifier *(hidden)* ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` The `LocalTransparencyModifier` property is a multiplier to [Transparency](/docs/reference/engine/classes/BasePart.md) that is only visible to the local client. It does not replicate from client to server and is useful for when a part should not render for a specific client, such as how the player does not see their character's body parts when they zoom into first person mode. This property modifies the local part's transparency through the following formula, with resulting values clamped between `0` and `1`. `1` - ((`1` - [Transparency](/docs/reference/engine/classes/BasePart.md)) × (`1` - `LocalTransparencyModifier`)) | [Transparency](/docs/reference/engine/classes/BasePart.md) | `LocalTransparencyModifier` | Server-Side | Client-Side | | --- | --- | --- | --- | | `0.5` | `0` | `0.5` | `0.5` | | `0.5` | `0.25` | `0.5` | `0.625` | | `0.5` | `0.5` | `0.5` | `0.75` | | `0.5` | `0.75` | `0.5` | `0.875` | | `0.5` | `1` | `0.5` | `1` | ### Property: BasePart.Orientation *(hidden)* ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Transform", "simulationAccess": true } ``` The `Orientation` property describes the part's rotation in degrees around the **X**, **Y**, and **Z** axes using a [Vector3](/docs/reference/engine/datatypes/Vector3.md). The rotations are applied in **Y** ⟩ **X** ⟩ **Z** order. This differs from proper [Euler][1] angles and is instead [Tait-Bryan][2] angles which describe **yaw**, **pitch**, and **roll**. It is also worth noting how this property differs from the [CFrame.Angles()](/docs/reference/engine/datatypes/CFrame.md) constructor which applies rotations in a different order (**Z** ⟩ **Y** ⟩ **X**). For better control over the rotation of a part, it's recommended that [CFrame](/docs/reference/engine/classes/BasePart.md) is set instead. [1]: https://en.wikipedia.org/wiki/Euler_angles [2]: https://en.wikipedia.org/wiki/Euler_angles#Tait-Bryan_angles When setting this property, any [Welds](/docs/reference/engine/classes/Weld.md) or [Motor6Ds](/docs/reference/engine/classes/Motor6D.md) connected to this part will have the matching [C0](/docs/reference/engine/classes/JointInstance.md) or [C1](/docs/reference/engine/classes/JointInstance.md) property updated to allow the part to move relative to any other parts it is joined to. [WeldConstraints](/docs/reference/engine/classes/WeldConstraint.md) will also be temporarily disabled and re-enabled during the move. **Part Spinner** This code sample rotates a part continually on the Y axis. ```lua local part = script.Parent local INCREMENT = 360 / 20 -- Rotate the part continually while true do for degrees = 0, 360, INCREMENT do -- Set only the Y axis rotation part.Rotation = Vector3.new(0, degrees, 0) -- A better way to do this would be setting CFrame --part.CFrame = CFrame.new(part.Position) * CFrame.Angles(0, math.rad(degrees), 0) task.wait() end end ``` ### Property: BasePart.Position *(hidden)* ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Transform", "simulationAccess": true } ``` The `Position` property describes the coordinates of a part using a [Vector3](/docs/reference/engine/datatypes/Vector3.md). It reflects the position of the part's [CFrame](/docs/reference/engine/classes/BasePart.md), however it can also be set. When setting this property, any [Welds](/docs/reference/engine/classes/Weld.md) or [Motor6Ds](/docs/reference/engine/classes/Motor6D.md) connected to this part will have the matching [C0](/docs/reference/engine/classes/JointInstance.md) or [C1](/docs/reference/engine/classes/JointInstance.md) property updated to allow the part to move relative to any other parts it is joined to. [WeldConstraints](/docs/reference/engine/classes/WeldConstraint.md) will also be temporarily disabled and re-enabled during the move. ### Property: BasePart.ReceiveAge *(hidden)* ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Part" } ``` Indicates the time in seconds since the part's physics were last updated on the local client or the server. This value will be `0` when the part has no physics ([Anchored](/docs/reference/engine/classes/BasePart.md) is `true`). ### Property: BasePart.RightParamA *(hidden)* ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Surface Inputs" } ``` The `RightParamA` property is relevant when a part's [BasePart.RightSurface](/docs/reference/engine/classes/BasePart.md) is set to Motor or SteppingMotor and [BasePart.RightSurfaceInput](/docs/reference/engine/classes/BasePart.md) is set to Sin. It determines the **amplitude** of the motor's rotational velocity. **Motor Control** This code sample demonstrates how surface properties can be set using only a NormalId (Top, Front, etc). It switches a motor's -SurfaceInput from NoInput, Constant and Sin to show how each work using -ParamA and -ParamB properties. ```lua -- Paste this into a Script inside a part with a Motor SurfaceType local partMotor = script.Parent -- Place a brick called "MovingPart" so it is touching the Motor surface -- For this example, we use TopSurface, TopSurfaceInput, TopParamA and TopParamB -- However, this will work for all faces (NormalId): Top, Bottom, Left, Right, Front and Back -- A function to quickly set all surface properties at once local function setFaceSurfaceInputParams(normalId, surfaceType, inputType, paramA, paramB) local surfaceName = normalId.Name -- e.g. "Top", "Bottom", etc -- Syntax Note: in Lua, part.Something is the same as part["Something"] -- The difference is that the latter allows us to use a string ("Something"), while -- the former requires use of an identifier (.Something). Below, we build of each the surface -- properties below by concatenating the surface name with the property postfix. -- Set "___Surface", eg "TopSurface" partMotor[surfaceName .. "Surface"] = surfaceType -- Set "___SurfaceInput", eg "TopSurfaceInput" partMotor[surfaceName .. "SurfaceInput"] = inputType -- Set "___ParamA", eg "TopParamA" partMotor[surfaceName .. "ParamA"] = paramA -- Set "___ParamB", eg "TopParamB" partMotor[surfaceName .. "ParamB"] = paramB end local normalId = Enum.NormalId.Top while true do -- Set to NoInput, where the motor will not operate at all setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.NoInput, 0, 0) task.wait(1) -- Set to Constant, where motor rotational velocity = paramB setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.Constant, 0, 0.25) task.wait(2) -- Set to Sin, where motor rotational velocity = paramA * math.sin(time * paramB) -- Since we're using pi (~3.14), the frequency of rotation is 1 second (per definition of sine function) setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.Sin, 0.25, math.pi) task.wait(3) end ``` ### Property: BasePart.RightParamB *(hidden)* ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Surface Inputs" } ``` The `RightParamB` property is relevant when a part's [BasePart.RightSurface](/docs/reference/engine/classes/BasePart.md) is set to Motor or SteppingMotor and [BasePart.RightSurfaceInput](/docs/reference/engine/classes/BasePart.md) is set to Constant or Sin. For Constant, it determines the constant rotational velocity of the motor. For Sin, it determines the **frequency** of the motor's rotational velocity. **Motor Control** This code sample demonstrates how surface properties can be set using only a NormalId (Top, Front, etc). It switches a motor's -SurfaceInput from NoInput, Constant and Sin to show how each work using -ParamA and -ParamB properties. ```lua -- Paste this into a Script inside a part with a Motor SurfaceType local partMotor = script.Parent -- Place a brick called "MovingPart" so it is touching the Motor surface -- For this example, we use TopSurface, TopSurfaceInput, TopParamA and TopParamB -- However, this will work for all faces (NormalId): Top, Bottom, Left, Right, Front and Back -- A function to quickly set all surface properties at once local function setFaceSurfaceInputParams(normalId, surfaceType, inputType, paramA, paramB) local surfaceName = normalId.Name -- e.g. "Top", "Bottom", etc -- Syntax Note: in Lua, part.Something is the same as part["Something"] -- The difference is that the latter allows us to use a string ("Something"), while -- the former requires use of an identifier (.Something). Below, we build of each the surface -- properties below by concatenating the surface name with the property postfix. -- Set "___Surface", eg "TopSurface" partMotor[surfaceName .. "Surface"] = surfaceType -- Set "___SurfaceInput", eg "TopSurfaceInput" partMotor[surfaceName .. "SurfaceInput"] = inputType -- Set "___ParamA", eg "TopParamA" partMotor[surfaceName .. "ParamA"] = paramA -- Set "___ParamB", eg "TopParamB" partMotor[surfaceName .. "ParamB"] = paramB end local normalId = Enum.NormalId.Top while true do -- Set to NoInput, where the motor will not operate at all setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.NoInput, 0, 0) task.wait(1) -- Set to Constant, where motor rotational velocity = paramB setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.Constant, 0, 0.25) task.wait(2) -- Set to Sin, where motor rotational velocity = paramA * math.sin(time * paramB) -- Since we're using pi (~3.14), the frequency of rotation is 1 second (per definition of sine function) setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.Sin, 0.25, math.pi) task.wait(3) end ``` ### Property: BasePart.RightSurfaceInput *(hidden)* ```json { "type": "InputType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Surface Inputs" } ``` The RightSurfaceInput property determines the kind of input provided to a - For Sin, the motor rotates at a velocity equal to `ParamA * math.sin(workspace.DistributedGameTime * ParamB)`. See [Workspace.DistributedGameTime](/docs/reference/engine/classes/Workspace.md). **Motor Control** This code sample demonstrates how surface properties can be set using only a NormalId (Top, Front, etc). It switches a motor's -SurfaceInput from NoInput, Constant and Sin to show how each work using -ParamA and -ParamB properties. ```lua -- Paste this into a Script inside a part with a Motor SurfaceType local partMotor = script.Parent -- Place a brick called "MovingPart" so it is touching the Motor surface -- For this example, we use TopSurface, TopSurfaceInput, TopParamA and TopParamB -- However, this will work for all faces (NormalId): Top, Bottom, Left, Right, Front and Back -- A function to quickly set all surface properties at once local function setFaceSurfaceInputParams(normalId, surfaceType, inputType, paramA, paramB) local surfaceName = normalId.Name -- e.g. "Top", "Bottom", etc -- Syntax Note: in Lua, part.Something is the same as part["Something"] -- The difference is that the latter allows us to use a string ("Something"), while -- the former requires use of an identifier (.Something). Below, we build of each the surface -- properties below by concatenating the surface name with the property postfix. -- Set "___Surface", eg "TopSurface" partMotor[surfaceName .. "Surface"] = surfaceType -- Set "___SurfaceInput", eg "TopSurfaceInput" partMotor[surfaceName .. "SurfaceInput"] = inputType -- Set "___ParamA", eg "TopParamA" partMotor[surfaceName .. "ParamA"] = paramA -- Set "___ParamB", eg "TopParamB" partMotor[surfaceName .. "ParamB"] = paramB end local normalId = Enum.NormalId.Top while true do -- Set to NoInput, where the motor will not operate at all setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.NoInput, 0, 0) task.wait(1) -- Set to Constant, where motor rotational velocity = paramB setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.Constant, 0, 0.25) task.wait(2) -- Set to Sin, where motor rotational velocity = paramA * math.sin(time * paramB) -- Since we're using pi (~3.14), the frequency of rotation is 1 second (per definition of sine function) setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.Sin, 0.25, math.pi) task.wait(3) end ``` ### Property: BasePart.RotVelocity *(hidden)* ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data", "simulationAccess": true } ``` > **Deprecated:** This property is deprecated. Use `AssemblyAngularVelocity` instead. The `RotVelocity` of a [part](/docs/reference/engine/classes/BasePart.md) describes how its [BasePart.Orientation](/docs/reference/engine/classes/BasePart.md) is presently changing. In other words, this property describes how the fast part is rotating. The part only rotates if it is not anchored. The unit of this property is **radians per second**. Using this in conjunction with [AlignOrientation](/docs/reference/engine/classes/AlignOrientation.md) allows for aligned parts to have matching RotVelocity and Orientation values. **Rotating a Part with RotVelocity** The script below creates a new `BasePart|part` and sets its [Part.RotVelocity](/docs/reference/engine/classes/Part.md) to `Vector3.new(0, 10, 0)` every [RunService.RenderStepped](/docs/reference/engine/classes/RunService.md) so that the part rotates in a circular motion. ```lua local RunService = game:GetService("RunService") local part = Instance.new("Part") part.Name = "RotatingPart" part.Position = Vector3.new(0, 1, 0) part.Parent = workspace local function renderStepped() part.RotVelocity = Vector3.new(0, 10, 0) end RunService.RenderStepped:Connect(renderStepped) ``` ### Property: BasePart.TopParamA *(hidden)* ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Surface Inputs" } ``` The TopParamA property is relevant when a part's [BasePart.TopSurface](/docs/reference/engine/classes/BasePart.md) is set to Motor or SteppingMotor and [BasePart.TopSurfaceInput](/docs/reference/engine/classes/BasePart.md) is set to Sin. It determines the **amplitude** of the motor's rotational velocity. **Motor Control** This code sample demonstrates how surface properties can be set using only a NormalId (Top, Front, etc). It switches a motor's -SurfaceInput from NoInput, Constant and Sin to show how each work using -ParamA and -ParamB properties. ```lua -- Paste this into a Script inside a part with a Motor SurfaceType local partMotor = script.Parent -- Place a brick called "MovingPart" so it is touching the Motor surface -- For this example, we use TopSurface, TopSurfaceInput, TopParamA and TopParamB -- However, this will work for all faces (NormalId): Top, Bottom, Left, Right, Front and Back -- A function to quickly set all surface properties at once local function setFaceSurfaceInputParams(normalId, surfaceType, inputType, paramA, paramB) local surfaceName = normalId.Name -- e.g. "Top", "Bottom", etc -- Syntax Note: in Lua, part.Something is the same as part["Something"] -- The difference is that the latter allows us to use a string ("Something"), while -- the former requires use of an identifier (.Something). Below, we build of each the surface -- properties below by concatenating the surface name with the property postfix. -- Set "___Surface", eg "TopSurface" partMotor[surfaceName .. "Surface"] = surfaceType -- Set "___SurfaceInput", eg "TopSurfaceInput" partMotor[surfaceName .. "SurfaceInput"] = inputType -- Set "___ParamA", eg "TopParamA" partMotor[surfaceName .. "ParamA"] = paramA -- Set "___ParamB", eg "TopParamB" partMotor[surfaceName .. "ParamB"] = paramB end local normalId = Enum.NormalId.Top while true do -- Set to NoInput, where the motor will not operate at all setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.NoInput, 0, 0) task.wait(1) -- Set to Constant, where motor rotational velocity = paramB setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.Constant, 0, 0.25) task.wait(2) -- Set to Sin, where motor rotational velocity = paramA * math.sin(time * paramB) -- Since we're using pi (~3.14), the frequency of rotation is 1 second (per definition of sine function) setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.Sin, 0.25, math.pi) task.wait(3) end ``` ### Property: BasePart.TopParamB *(hidden)* ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Surface Inputs" } ``` The TopParamB property is relevant when a part's [BasePart.TopSurface](/docs/reference/engine/classes/BasePart.md) is set to Motor or SteppingMotor and [BasePart.TopSurfaceInput](/docs/reference/engine/classes/BasePart.md) is set to Constant or Sin. For Constant, it determines the constant rotational velocity of the motor. For Sin, it determines the **frequency** of the motor's rotational velocity. **Motor Control** This code sample demonstrates how surface properties can be set using only a NormalId (Top, Front, etc). It switches a motor's -SurfaceInput from NoInput, Constant and Sin to show how each work using -ParamA and -ParamB properties. ```lua -- Paste this into a Script inside a part with a Motor SurfaceType local partMotor = script.Parent -- Place a brick called "MovingPart" so it is touching the Motor surface -- For this example, we use TopSurface, TopSurfaceInput, TopParamA and TopParamB -- However, this will work for all faces (NormalId): Top, Bottom, Left, Right, Front and Back -- A function to quickly set all surface properties at once local function setFaceSurfaceInputParams(normalId, surfaceType, inputType, paramA, paramB) local surfaceName = normalId.Name -- e.g. "Top", "Bottom", etc -- Syntax Note: in Lua, part.Something is the same as part["Something"] -- The difference is that the latter allows us to use a string ("Something"), while -- the former requires use of an identifier (.Something). Below, we build of each the surface -- properties below by concatenating the surface name with the property postfix. -- Set "___Surface", eg "TopSurface" partMotor[surfaceName .. "Surface"] = surfaceType -- Set "___SurfaceInput", eg "TopSurfaceInput" partMotor[surfaceName .. "SurfaceInput"] = inputType -- Set "___ParamA", eg "TopParamA" partMotor[surfaceName .. "ParamA"] = paramA -- Set "___ParamB", eg "TopParamB" partMotor[surfaceName .. "ParamB"] = paramB end local normalId = Enum.NormalId.Top while true do -- Set to NoInput, where the motor will not operate at all setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.NoInput, 0, 0) task.wait(1) -- Set to Constant, where motor rotational velocity = paramB setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.Constant, 0, 0.25) task.wait(2) -- Set to Sin, where motor rotational velocity = paramA * math.sin(time * paramB) -- Since we're using pi (~3.14), the frequency of rotation is 1 second (per definition of sine function) setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.Sin, 0.25, math.pi) task.wait(3) end ``` ### Property: BasePart.TopSurfaceInput *(hidden)* ```json { "type": "InputType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Surface Inputs" } ``` The `TopSurfaceInput` property determines the kind of input provided to a part's [BasePart.TopSurface](/docs/reference/engine/classes/BasePart.md). This is only relevant for Motor or SteppingMotor SurfaceTypes. This property determines how [BasePart.TopParamA](/docs/reference/engine/classes/BasePart.md) and [BasePart.TopParamB](/docs/reference/engine/classes/BasePart.md) are used. For brevity, these properties will be referred to as ParamA and ParamB, respectively. - By default, this is set to NoInput. This stops the motor altogether, - For Constant, the motor rotates at a constant velocity equal to `ParamB`. - For Sin, the motor rotates at a velocity equal to `ParamA * math.sin(workspace.DistributedGameTime * ParamB)`. See [Workspace.DistributedGameTime](/docs/reference/engine/classes/Workspace.md). **Motor Control** This code sample demonstrates how surface properties can be set using only a NormalId (Top, Front, etc). It switches a motor's -SurfaceInput from NoInput, Constant and Sin to show how each work using -ParamA and -ParamB properties. ```lua -- Paste this into a Script inside a part with a Motor SurfaceType local partMotor = script.Parent -- Place a brick called "MovingPart" so it is touching the Motor surface -- For this example, we use TopSurface, TopSurfaceInput, TopParamA and TopParamB -- However, this will work for all faces (NormalId): Top, Bottom, Left, Right, Front and Back -- A function to quickly set all surface properties at once local function setFaceSurfaceInputParams(normalId, surfaceType, inputType, paramA, paramB) local surfaceName = normalId.Name -- e.g. "Top", "Bottom", etc -- Syntax Note: in Lua, part.Something is the same as part["Something"] -- The difference is that the latter allows us to use a string ("Something"), while -- the former requires use of an identifier (.Something). Below, we build of each the surface -- properties below by concatenating the surface name with the property postfix. -- Set "___Surface", eg "TopSurface" partMotor[surfaceName .. "Surface"] = surfaceType -- Set "___SurfaceInput", eg "TopSurfaceInput" partMotor[surfaceName .. "SurfaceInput"] = inputType -- Set "___ParamA", eg "TopParamA" partMotor[surfaceName .. "ParamA"] = paramA -- Set "___ParamB", eg "TopParamB" partMotor[surfaceName .. "ParamB"] = paramB end local normalId = Enum.NormalId.Top while true do -- Set to NoInput, where the motor will not operate at all setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.NoInput, 0, 0) task.wait(1) -- Set to Constant, where motor rotational velocity = paramB setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.Constant, 0, 0.25) task.wait(2) -- Set to Sin, where motor rotational velocity = paramA * math.sin(time * paramB) -- Since we're using pi (~3.14), the frequency of rotation is 1 second (per definition of sine function) setFaceSurfaceInputParams(normalId, Enum.SurfaceType.Motor, Enum.InputType.Sin, 0.25, math.pi) task.wait(3) end ``` ### Property: BasePart.Velocity *(hidden)* ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data", "simulationAccess": true } ``` > **Deprecated:** This property is deprecated. Use `AssemblyLinearVelocity` instead. The Velocity of a part describes how its [BasePart.Position](/docs/reference/engine/classes/BasePart.md) is presently changing. The unit of this property is **studs per second**. For reference, the default Roblox character moves at 16 studs per second via [Humanoid.WalkSpeed](/docs/reference/engine/classes/Humanoid.md). The acceleration due to gravity is found in [Workspace.Gravity](/docs/reference/engine/classes/Workspace.md) (by default, -196.2 studs per second per second). Setting the Velocity of a part that is [BasePart.Anchored](/docs/reference/engine/classes/BasePart.md) will cause it to act like a conveyor belt. Any object that touches the part will begin to move in accordance with the Velocity. Some [BodyMover](/docs/reference/engine/classes/BodyMover.md) objects will apply forces and thus change the Velocity of a part over time. The simplest of these is a [BodyForce](/docs/reference/engine/classes/BodyForce.md) which can be used to counteract the acceleration due to gravity on a single part (set the +Y axis of the [BodyForce.Force](/docs/reference/engine/classes/BodyForce.md) to the product of the mass ([BasePart:GetMass()](/docs/reference/engine/classes/BasePart.md)) and the gravity constant). **Projectile Firing** This code sample fires a part from one position toward another. It calculates the velocity needed to reach the destination in time, and applies an anti-gravity effect using a BodyForce. In addition, it adds a Trail to better visualize the path of the projectile as it arcs through the air. ```lua -- Put this Script in a Part, preferably bullet-shaped :) local part = script.Parent part.Shape = Enum.PartType.Ball part.Size = Vector3.new(2, 2, 2) part.BrickColor = BrickColor.new("Really black") part.CanCollide = false local MY_START_POINT = Vector3.new(0, 50, 0) local MY_TARGET_POINT = Vector3.new(50, 100, 0) local TRAVEL_TIME = 1 local ANTI_GRAVITY = 0.5 -- Anti-gravity effect: add a BodyForce to counter gravity local bf = Instance.new("BodyForce") bf.Force = Vector3.new(0, workspace.Gravity * part:GetMass() * ANTI_GRAVITY, 0) bf.Parent = part local a0 = Instance.new("Attachment") a0.Position = Vector3.new(1, 0, 0) a0.Parent = part local a1 = Instance.new("Attachment") a1.Position = Vector3.new(-1, 0, 0) a1.Parent = part local trail = Instance.new("Trail") trail.Parent = part trail.Attachment0 = a0 trail.Attachment1 = a1 trail.FaceCamera = true trail.Transparency = NumberSequence.new({ NumberSequenceKeypoint.new(0, 0), NumberSequenceKeypoint.new(1, 1), }) trail.Lifetime = 0.35 local function fire(startPoint, targetPoint) -- Calculate how far we have to travel local distance = (targetPoint - startPoint).magnitude local speed = distance / TRAVEL_TIME part.CFrame = CFrame.new(startPoint, targetPoint) -- Shoot the part part.Velocity = part.CFrame.LookVector * speed end while true do fire(MY_START_POINT, MY_TARGET_POINT) task.wait(TRAVEL_TIME) end ``` ## Methods ### Method: BasePart:AngularAccelerationToTorque **Signature:** `BasePart:AngularAccelerationToTorque(angAcceleration: Vector3, angVelocity?: Vector3): Vector3` Returns the world-space torque vector that must be applied to the part's assembly to achieve the specified angular acceleration. This result can be used to determine the torque that should be applied via a [Torque](/docs/reference/engine/classes/Torque.md) instance or [BasePart:ApplyAngularImpulse()](/docs/reference/engine/classes/BasePart.md). The calculation uses Euler's rotation equation where _I_ is the assembly's world-space inertia tensor, _α_ is the desired angular acceleration, and _ω_ is the angular velocity. _τ = I · α + ω × (I · ω)_ If the part is not a descendant of [Workspace](/docs/reference/engine/classes/Workspace.md), this method returns a vector of infinity. If the assembly is anchored, it similarly returns a vector of infinity. #### Gyroscopic Effects When a spinning assembly has an asymmetric inertia tensor, the torque required to achieve a specified angular acceerlation is augmented due to the changing orientation of the body. The optional `angVelocity` parameter accounts for these gyroscopic effects. If omitted, these gyroscopic effects are omitted from the calculation. ```lua local part = workspace.Part local angularVelocity = part.AssemblyAngularVelocity local desiredAcceleration = Vector3.new(0, 10, 0) -- Compute the torque needed to achieve the desired acceleration local torque = part:AngularAccelerationToTorque(desiredAcceleration, angularVelocity) ``` *Security: None · Thread Safety: Unsafe · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `angAcceleration` | `Vector3` | | The desired angular acceleration vector in world space. | | `angVelocity` | `Vector3` | `0, 0, 0` | The current angular velocity of the assembly in world space. Defaults to `(0, 0, 0)`. Supply the assembly's angular velocity to account for gyroscopic effects. | **Returns:** `Vector3` — A [Vector3](/docs/reference/engine/datatypes/Vector3.md) representing the world-space torque required to produce the specified angular acceleration. ### Method: BasePart:ApplyAngularImpulse **Signature:** `BasePart:ApplyAngularImpulse(impulse: Vector3): ()` Applies an instant angular force impulse to this part's assembly, causing the assembly to spin. The resulting angular velocity from the impulse relies on the assembly's [mass](/docs/reference/engine/classes/BasePart.md). So a higher impulse is required to move more massive assemblies. Impulses are useful for cases where you want a force applied instantly, such as an explosion or collision. If the part is [owned](/docs/en-us/physics/network-ownership.md) by the server, this function must be called from a server [Script](/docs/reference/engine/classes/Script.md) (not from a [LocalScript](/docs/reference/engine/classes/LocalScript.md) or a [Script](/docs/reference/engine/classes/Script.md) with [RunContext](/docs/reference/engine/classes/BaseScript.md) set to [RunContext.Client](/docs/reference/engine/enums/RunContext.md)). If the part is owned by a client through **automatic** ownership, this function can be called from either a client script **or** a server script; calling it from a client script for a server-owned part will have no effect. *Security: None · Thread Safety: Unsafe · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `impulse` | `Vector3` | | An angular impulse vector to be applied to the assembly. | **Returns:** `()` ### Method: BasePart:ApplyImpulse **Signature:** `BasePart:ApplyImpulse(impulse: Vector3): ()` This function applies an instant force impulse to this part's assembly. The force is applied at the assembly's [center of mass](/docs/reference/engine/classes/BasePart.md), so the resulting movement will only be linear. The resulting velocity from the impulse relies on the assembly's [mass](/docs/reference/engine/classes/BasePart.md). So a higher impulse is required to move more massive assemblies. Impulses are useful for cases where you want a force applied instantly, such as an explosion or collision. If the part is [owned](/docs/en-us/physics/network-ownership.md) by the server, this function must be called from a server [Script](/docs/reference/engine/classes/Script.md) (not from a [LocalScript](/docs/reference/engine/classes/LocalScript.md) or a [Script](/docs/reference/engine/classes/Script.md) with [RunContext](/docs/reference/engine/classes/BaseScript.md) set to [RunContext.Client](/docs/reference/engine/enums/RunContext.md)). If the part is owned by a client through **automatic** ownership, this function can be called from either a client script **or** a server script; calling it from a client script for a server-owned part will have no effect. *Security: None · Thread Safety: Unsafe · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `impulse` | `Vector3` | | A linear impulse vector to be applied to the assembly. | **Returns:** `()` ### Method: BasePart:ApplyImpulseAtPosition **Signature:** `BasePart:ApplyImpulseAtPosition(impulse: Vector3, position: Vector3): ()` This function applies an instant force impulse to this part's assembly, at the specified position in world space. If the position is not at the assembly's [center of mass](/docs/reference/engine/classes/BasePart.md), the impulse will cause a positional and rotational movement. The resulting velocity from the impulse relies on the assembly's [mass](/docs/reference/engine/classes/BasePart.md). So a higher impulse is required to move more massive assemblies. Impulses are useful for cases where developers want a force applied instantly, such as an explosion or collision. If the part is [owned](/docs/en-us/physics/network-ownership.md) by the server, this function must be called from a server [Script](/docs/reference/engine/classes/Script.md) (not from a [LocalScript](/docs/reference/engine/classes/LocalScript.md) or a [Script](/docs/reference/engine/classes/Script.md) with [RunContext](/docs/reference/engine/classes/BaseScript.md) set to [RunContext.Client](/docs/reference/engine/enums/RunContext.md)). If the part is owned by a client through **automatic** ownership, this function can be called from either a client script **or** a server script; calling it from a client script for a server-owned part will have no effect. *Security: None · Thread Safety: Unsafe · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `impulse` | `Vector3` | | An impulse vector to be applied to the assembly. | | `position` | `Vector3` | | The position, in world space, to apply the impulse. | **Returns:** `()` ### Method: BasePart:CanCollideWith **Signature:** `BasePart:CanCollideWith(part: BasePart): boolean` Returns whether the parts can collide with each other or not. This function takes into account the collision groups of the two parts. This function will error if the specified part is not a BasePart. *Security: None · Thread Safety: Safe · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `part` | `BasePart` | | The specified part being checked for collidability. | **Returns:** `boolean` — Whether the parts can collide with each other. ### Method: BasePart:CanSetNetworkOwnership **Signature:** `BasePart:CanSetNetworkOwnership(): Tuple` The CanSetNetworkOwnership function checks whether you can set a part's network ownership. The function's return value verifies whether or not you can call [BasePart:SetNetworkOwner()](/docs/reference/engine/classes/BasePart.md) or [BasePart:SetNetworkOwnershipAuto()](/docs/reference/engine/classes/BasePart.md) without encountering an error. It returns true if you can modify/read the network ownership, or returns false and the reason you can't, as a string. *Security: None · Thread Safety: Unsafe · Simulation Access: true* **Returns:** `Tuple` — Whether you can modify or read the network ownership and the reason. **Check if a Part's Network Ownership Can Be Set** This example checks whether or not the network ownership of the first `BasePart` named _Part_ in the `Workspace` can be set. ```lua local part = workspace:FindFirstChild("Part") if part and part:IsA("BasePart") then local canSet, errorReason = part:CanSetNetworkOwnership() if canSet then print(part:GetFullName() .. "'s Network Ownership can be changed!") else warn("Cannot change the Network Ownership of " .. part:GetFullName() .. " because: " .. errorReason) end end ``` ### Method: BasePart:GetClosestPointOnSurface **Signature:** `BasePart:GetClosestPointOnSurface(position: Vector3): Vector3` Returns the closest point on the part's surface to the given world-space position. If the provided position is inside the part, it is returned as-is. For [MeshPart](/docs/reference/engine/classes/MeshPart.md) and [PartOperation](/docs/reference/engine/classes/PartOperation.md) instances, this method respects the part's [CollisionFidelity](/docs/reference/engine/enums/CollisionFidelity.md) value. Return values can differ based on whether the mesh is being treated as a box, a hull, or a more complex shape. *Security: None · Thread Safety: Unsafe · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `position` | `Vector3` | | The world-space point to find the closest surface point to. | **Returns:** `Vector3` — The closest point on the part's surface in world space. ### Method: BasePart:GetConnectedParts **Signature:** `BasePart:GetConnectedParts(recursive?: boolean): List` Returns a table of parts connected to the object by any kind of rigid joint. If `recursive` is true this function will return all of the parts in the assembly rigidly connected to the BasePart. #### Rigid Joints When a joint connects two parts together `(Part0 → Part1)`, a joint is **rigid** if the physics of `Part1` are completely locked down by `Part0`. This only applies to the following joint types: - [Weld](/docs/reference/engine/classes/Weld.md) - [Snap](/docs/reference/engine/classes/Snap.md) - [ManualWeld](/docs/reference/engine/classes/ManualWeld.md) - [Motor](/docs/reference/engine/classes/Motor.md) - [Motor6D](/docs/reference/engine/classes/Motor6D.md) - [WeldConstraint](/docs/reference/engine/classes/WeldConstraint.md) *Security: None · Thread Safety: Safe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `recursive` | `boolean` | `false` | A table of parts connected to the object by any kind of [joint](/docs/reference/engine/classes/JointInstance.md). | **Returns:** `List` ### Method: BasePart:GetJoints **Signature:** `BasePart:GetJoints(): Instances` Return all Joints or Constraints that is connected to this Part. *Security: None · Thread Safety: Safe · Simulation Access: true* **Returns:** `Instances` — An array of all Joints or Constraints connected to the Part. ### Method: BasePart:GetMass **Signature:** `BasePart:GetMass(): float` **GetMass** returns the value of the read-only [Mass](/docs/reference/engine/classes/BasePart.md) property. This function predates the Mass property. It remains supported for backward-compatibility; you should use the Mass property directly. *Security: None · Thread Safety: Safe · Simulation Access: true* **Returns:** `float` — The part's mass. **Finding a Part's Mass** This example creates a new part, myPart, in the game's Workspace, with dimensions 4x6x4 studs. The part is also anchored. Then, myMass is set to equal the mass of the new part. The mass of the part is printed at the end of the print statement: > My part's mass is ... ```lua local myPart = Instance.new("Part") myPart.Size = Vector3.new(4, 6, 4) myPart.Anchored = true myPart.Parent = workspace local myMass = myPart:GetMass() print("My part's mass is " .. myMass) ``` ### Method: BasePart:GetNetworkOwner **Signature:** `BasePart:GetNetworkOwner(): Instance` Returns the current player who is the network owner of this part, or `nil` in case of the server. *Security: None · Thread Safety: Safe · Simulation Access: true* **Returns:** `Instance` — The current player who is the network owner of this part, or `nil` in case of the server. ### Method: BasePart:GetNetworkOwnershipAuto **Signature:** `BasePart:GetNetworkOwnershipAuto(): boolean` Returns true if the game engine automatically decides the network owner for this part. *Security: None · Thread Safety: Safe · Simulation Access: true* **Returns:** `boolean` — Whether the game engine automatically decides the network owner for this part. ### Method: BasePart:GetNoCollisionConstraints **Signature:** `BasePart:GetNoCollisionConstraints(): Instances` *Security: None · Thread Safety: Unsafe · Simulation Access: true* **Returns:** `Instances` ### Method: BasePart:GetTouchingParts **Signature:** `BasePart:GetTouchingParts(): Instances` Returns a table of all parts that are physically interacting with this part. If the part itself has CanCollide set to false, then this function returns an empty table unless the part has a [TouchInterest](/docs/reference/engine/classes/TouchTransmitter.md) object parented to it (meaning something is connected to its Touched event). Parts that are adjacent but not intersecting are not considered touching. This function predates the [WorldRoot:GetPartsInPart()](/docs/reference/engine/classes/WorldRoot.md) function, which provides more flexibility and avoids the special [TouchInterest](/docs/reference/engine/classes/TouchTransmitter.md) rules described above. Use [WorldRoot:GetPartsInPart()](/docs/reference/engine/classes/WorldRoot.md) instead. *Security: None · Thread Safety: Unsafe · Simulation Access: true* **Returns:** `Instances` — A table of all parts that intersect and can collide with this part. ### Method: BasePart:GetVelocityAtPosition **Signature:** `BasePart:GetVelocityAtPosition(position: Vector3): Vector3` Returns the linear velocity of the part's assembly at the given position relative to this part. It can be used to identify the linear velocity of parts in an assembly other than the root part. If the assembly has no angular velocity, than the linear velocity will always be the same for every position. *Security: None · Thread Safety: Safe · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `position` | `Vector3` | | | **Returns:** `Vector3` ### Method: BasePart:IntersectAsync **Signature:** `BasePart:IntersectAsync(parts: Instances, collisionfidelity?: CollisionFidelity, renderFidelity?: RenderFidelity): Instance` Creates a new [IntersectOperation](/docs/reference/engine/classes/IntersectOperation.md) from the intersecting geometry of the part and the other parts in the given array. Only [Parts](/docs/reference/engine/classes/Part.md) are supported, not [Terrain](/docs/reference/engine/classes/Terrain.md) or [MeshParts](/docs/reference/engine/classes/MeshPart.md). Similar to [Clone()](/docs/reference/engine/classes/Instance.md), the returned object has no set [Parent](/docs/reference/engine/classes/Instance.md). The following properties from the calling part are applied to the resulting [IntersectOperation](/docs/reference/engine/classes/IntersectOperation.md): - [Color](/docs/reference/engine/classes/BasePart.md), [Material](/docs/reference/engine/classes/BasePart.md), [MaterialVariant](/docs/reference/engine/classes/BasePart.md), [Reflectance](/docs/reference/engine/classes/BasePart.md), [Transparency](/docs/reference/engine/classes/BasePart.md) - [CanCollide](/docs/reference/engine/classes/BasePart.md) - [Anchored](/docs/reference/engine/classes/BasePart.md), [Density](/docs/reference/engine/classes/BasePart.md), [Elasticity](/docs/reference/engine/classes/BasePart.md), [ElasticityWeight](/docs/reference/engine/classes/BasePart.md), [Friction](/docs/reference/engine/classes/BasePart.md), [FrictionWeight](/docs/reference/engine/classes/BasePart.md) In the following image comparison, [IntersectAsync()](/docs/reference/engine/classes/BasePart.md) is called on the purple block using a table containing the blue block. The resulting [IntersectOperation](/docs/reference/engine/classes/IntersectOperation.md) resolves into a shape of the intersecting geometry of both parts. ![Two block parts overlapping](../../../assets/modeling/solid-modeling/Separate-Parts-To-Intersect.jpg)_Separate parts_ ![Parts intersected into a new solid model](../../../assets/modeling/solid-modeling/Intersect-Result.jpg)_Resulting [IntersectOperation](/docs/reference/engine/classes/IntersectOperation.md)_ #### Notes - The original parts remain intact following a successful intersect operation. In most cases, you should [Destroy()](/docs/reference/engine/classes/Instance.md) all of the original parts and parent the returned [IntersectOperation](/docs/reference/engine/classes/IntersectOperation.md) to the same place as the calling [BasePart](/docs/reference/engine/classes/BasePart.md). - By default, the face colors of the resulting intersection are borrowed from the [Color](/docs/reference/engine/classes/BasePart.md) property of the original parts. To change the entire intersection to a specific color, set its [UsePartColor](/docs/reference/engine/classes/PartOperation.md) property to `true`. - If an intersect operation would result in a part with more than 20,000 triangles, it will be simplified to 20,000 triangles. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: CSG* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `parts` | `Instances` | | The objects taking part in the intersection. | | `collisionfidelity` | `CollisionFidelity` | `Default` | The [CollisionFidelity](/docs/reference/engine/enums/CollisionFidelity.md) value for the resulting [IntersectOperation](/docs/reference/engine/classes/IntersectOperation.md). | | `renderFidelity` | `RenderFidelity` | `Automatic` | The [RenderFidelity](/docs/reference/engine/enums/RenderFidelity.md) value of the resulting [PartOperation](/docs/reference/engine/classes/PartOperation.md). | **Returns:** `Instance` — Resulting [IntersectOperation](/docs/reference/engine/classes/IntersectOperation.md) with default name **Intersect**. ### Method: BasePart:IsGrounded **Signature:** `BasePart:IsGrounded(): boolean` Returns true if the object is connected to a part that will hold it in place (eg an [Anchored](/docs/reference/engine/classes/BasePart.md) part), otherwise returns false. In an assembly that has an [Anchored](/docs/reference/engine/classes/BasePart.md) part, every other part is grounded. *Security: None · Thread Safety: Safe · Simulation Access: true* **Returns:** `boolean` — Whether the object is connected to a part that will hold it in place. ### Method: BasePart:Resize **Signature:** `BasePart:Resize(normalId: NormalId, deltaAmount: int): boolean` Changes the size of an object just like using the Studio resize tool. *Security: None · Thread Safety: Unsafe · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `normalId` | `NormalId` | | The side to resize. | | `deltaAmount` | `int` | | How much to grow/shrink on the specified side. | **Returns:** `boolean` — Whether the part is resized. ### Method: BasePart:SetNetworkOwner **Signature:** `BasePart:SetNetworkOwner(playerInstance?: Player): ()` Sets the given player as network owner for this and all connected parts. When playerInstance is `nil`, the server will be the owner instead of a player. *Security: None · Thread Safety: Unsafe · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `playerInstance` | `Player` | `nil` | The player being given network ownership of the part. | **Returns:** `()` ### Method: BasePart:SetNetworkOwnershipAuto **Signature:** `BasePart:SetNetworkOwnershipAuto(): ()` Lets the game engine dynamically decide who will handle the part's physics (one of the clients or the server). *Security: None · Thread Safety: Unsafe · Simulation Access: true* **Returns:** `()` ### Method: BasePart:SubtractAsync **Signature:** `BasePart:SubtractAsync(parts: Instances, collisionfidelity?: CollisionFidelity, renderFidelity?: RenderFidelity): Instance` Creates a new [UnionOperation](/docs/reference/engine/classes/UnionOperation.md) from the part, minus the geometry occupied by the parts in the given array. Only [Parts](/docs/reference/engine/classes/Part.md) are supported, not [Terrain](/docs/reference/engine/classes/Terrain.md) or [MeshParts](/docs/reference/engine/classes/MeshPart.md). Similar to [Clone()](/docs/reference/engine/classes/Instance.md), the returned object has no set [Parent](/docs/reference/engine/classes/Instance.md). Note that the resulting union cannot be empty due to subtractions. If the operation would result in completely empty geometry, it will fail. In the following image comparison, [SubtractAsync()](/docs/reference/engine/classes/BasePart.md) is called on the blue cylinder using a table containing the purple block. The resulting [UnionOperation](/docs/reference/engine/classes/UnionOperation.md) resolves into a shape that omits the block's geometry from that of the cylinder. ![Longer block overlapping a cylinder](../../../assets/modeling/solid-modeling/Separate-Parts-To-Subtract.jpg)_Separate parts_ ![Block part subtracted from cylinder](../../../assets/modeling/solid-modeling/Negate-Result.jpg)_Resulting [UnionOperation](/docs/reference/engine/classes/UnionOperation.md)_ *Yields · Security: None · Thread Safety: Unsafe · Capabilities: CSG* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `parts` | `Instances` | | The objects taking part in the subtraction. | | `collisionfidelity` | `CollisionFidelity` | `Default` | The [CollisionFidelity](/docs/reference/engine/enums/CollisionFidelity.md) value for the resulting [UnionOperation](/docs/reference/engine/classes/UnionOperation.md). | | `renderFidelity` | `RenderFidelity` | `Automatic` | The [RenderFidelity](/docs/reference/engine/enums/RenderFidelity.md) value of the resulting [PartOperation](/docs/reference/engine/classes/PartOperation.md). | **Returns:** `Instance` — Resulting [UnionOperation](/docs/reference/engine/classes/UnionOperation.md) with default name **Union**. **BasePart:SubtractAsync()** This example demonstrates how to subtract part(s) from another [BasePart](/docs/reference/engine/classes/BasePart.md) to form a negated [UnionOperation](/docs/reference/engine/classes/UnionOperation.md). ```lua local Workspace = game:GetService("Workspace") local mainPart = script.Parent.PartA local otherParts = { script.Parent.PartB, script.Parent.PartC } -- Perform subtract operation local success, newSubtract = pcall(function() return mainPart:SubtractAsync(otherParts) end) -- If operation succeeds, position it at the same location and parent it to the workspace if success and newSubtract then newSubtract.Position = mainPart.Position newSubtract.Parent = Workspace end -- Destroy original parts which remain intact after operation mainPart:Destroy() for _, part in otherParts do part:Destroy() end ``` ### Method: BasePart:TorqueToAngularAcceleration **Signature:** `BasePart:TorqueToAngularAcceleration(torque: Vector3, angVelocity?: Vector3): Vector3` Returns the world-space angular acceleration that would result from applying the specified torque to the part's assembly, optionally taking into account gyroscopic effects. This is the inverse of [BasePart:AngularAccelerationToTorque()](/docs/reference/engine/classes/BasePart.md). It is useful for predicting how an assembly will respond to an applied torque. The calculation uses: **α = I⁻¹ · (τ − ω × (I · ω))** where **I** is the assembly's world-space inertia tensor, **τ** is the applied torque, and **ω** is the angular velocity. If the part is not a descendant of [Workspace](/docs/reference/engine/classes/Workspace.md), returns `(0, 0, 0)`. If the assembly is anchored, returns `(0, 0, 0)`. #### Gyroscopic Effects When a spinning assembly has an asymmetric inertia tensor, the acceleration resulting from an applied torque also depends on the current angular velocity. The `angVelocity` parameter accounts for these gyroscopic effects. If omitted (defaults to zero), the result neglects this contribution from the result. ```lua local part = workspace.Part local angularVelocity = part.AssemblyAngularVelocity local appliedTorque = Vector3.new(0, 100, 0) -- Predict the angular acceleration from this torque local angAccel = part:TorqueToAngularAcceleration(appliedTorque, angularVelocity) ``` *Security: None · Thread Safety: Unsafe · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `torque` | `Vector3` | | The torque vector applied to the assembly in world space. | | `angVelocity` | `Vector3` | `0, 0, 0` | The current angular velocity of the assembly in world space. Defaults to `(0, 0, 0)`. Supply the assembly's angular velocity to account for gyroscopic effects. | **Returns:** `Vector3` — A [Vector3](/docs/reference/engine/datatypes/Vector3.md) representing the resulting angular acceleration in world space. ### Method: BasePart:UnionAsync **Signature:** `BasePart:UnionAsync(parts: Instances, collisionfidelity?: CollisionFidelity, renderFidelity?: RenderFidelity): Instance` Creates a new [UnionOperation](/docs/reference/engine/classes/UnionOperation.md) from the part, plus the geometry occupied by the parts in the given array. Only [Parts](/docs/reference/engine/classes/Part.md) are supported, not [Terrain](/docs/reference/engine/classes/Terrain.md) or [MeshParts](/docs/reference/engine/classes/MeshPart.md). Similar to [Clone()](/docs/reference/engine/classes/Instance.md), the returned object has no set [Parent](/docs/reference/engine/classes/Instance.md). The following properties from the calling part are applied to the resulting [UnionOperation](/docs/reference/engine/classes/UnionOperation.md): - [Color](/docs/reference/engine/classes/BasePart.md), [Material](/docs/reference/engine/classes/BasePart.md), [MaterialVariant](/docs/reference/engine/classes/BasePart.md), [Reflectance](/docs/reference/engine/classes/BasePart.md), [Transparency](/docs/reference/engine/classes/BasePart.md) - [CanCollide](/docs/reference/engine/classes/BasePart.md) - [Anchored](/docs/reference/engine/classes/BasePart.md), [Density](/docs/reference/engine/classes/BasePart.md), [Elasticity](/docs/reference/engine/classes/BasePart.md), [ElasticityWeight](/docs/reference/engine/classes/BasePart.md), [Friction](/docs/reference/engine/classes/BasePart.md), [FrictionWeight](/docs/reference/engine/classes/BasePart.md) In the following image comparison, [UnionAsync()](/docs/reference/engine/classes/BasePart.md) is called on the blue block using a table containing the purple cylinder. The resulting [UnionOperation](/docs/reference/engine/classes/UnionOperation.md) resolves into a shape of the combined geometry of both parts. ![Block and cylinder parts overlapping](../../../assets/modeling/solid-modeling/Separate-Parts-To-Union.jpg)_Separate parts_ ![Parts joined together into a single solid union](../../../assets/modeling/solid-modeling/Union-Result.jpg)_Resulting [UnionOperation](/docs/reference/engine/classes/UnionOperation.md)_ #### Notes - The original parts remain intact following a successful union operation. In most cases, you should [Destroy()](/docs/reference/engine/classes/Instance.md) all of the original parts and parent the returned [UnionOperation](/docs/reference/engine/classes/UnionOperation.md) to the same place as the calling [BasePart](/docs/reference/engine/classes/BasePart.md). - By default, the resulting union respects the [Color](/docs/reference/engine/classes/BasePart.md) property of each of its parts. To change the entire union to a specific color, set its [UsePartColor](/docs/reference/engine/classes/PartOperation.md) property to `true`. - If a union operation would result in a part with more than 20,000 triangles, it will be simplified to 20,000 triangles. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: CSG* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `parts` | `Instances` | | The objects taking part in the union with the calling part. | | `collisionfidelity` | `CollisionFidelity` | `Default` | The [CollisionFidelity](/docs/reference/engine/enums/CollisionFidelity.md) value for the resulting [UnionOperation](/docs/reference/engine/classes/UnionOperation.md). | | `renderFidelity` | `RenderFidelity` | `Automatic` | The [RenderFidelity](/docs/reference/engine/enums/RenderFidelity.md) value of the resulting [PartOperation](/docs/reference/engine/classes/PartOperation.md). | **Returns:** `Instance` — Resulting [UnionOperation](/docs/reference/engine/classes/UnionOperation.md) with default name **Union**. **BasePart:UnionAsync()** This example demonstrates how to combine the geometry of one [BasePart](/docs/reference/engine/classes/BasePart.md) with the geometry of other part(s) to form a [UnionOperation](/docs/reference/engine/classes/UnionOperation.md). ```lua local Workspace = game:GetService("Workspace") local mainPart = script.Parent.PartA local otherParts = { script.Parent.PartB, script.Parent.PartC } -- Perform union operation local success, newUnion = pcall(function() return mainPart:UnionAsync(otherParts) end) -- If operation succeeds, position it at the same location and parent it to the workspace if success and newUnion then newUnion.Position = mainPart.Position newUnion.Parent = Workspace end -- Destroy original parts which remain intact after operation mainPart:Destroy() for _, part in otherParts do part:Destroy() end ``` ### Method: BasePart:BreakJoints **Signature:** `BasePart:BreakJoints(): ()` > **Deprecated:** This method is deprecated. To break specific joints, iterate over the part's connections using [BasePart:GetJoints()](/docs/reference/engine/classes/BasePart.md) and call [Instance:Destroy()](/docs/reference/engine/classes/Instance.md) on the joints you want to remove. Breaks any surface connection with any adjacent part, including [Weld](/docs/reference/engine/classes/Weld.md) and other [JointInstance](/docs/reference/engine/classes/JointInstance.md). *Security: None · Thread Safety: Unsafe · Simulation Access: true* **Returns:** `()` ### Method: BasePart:breakJoints **Signature:** `BasePart:breakJoints(): ()` > **Deprecated:** This deprecated function is a variant of [BasePart:BreakJoints()](/docs/reference/engine/classes/BasePart.md) which should be used instead. *Security: None · Thread Safety: Unsafe* **Returns:** `()` ### Method: BasePart:getMass **Signature:** `BasePart:getMass(): float` > **Deprecated:** This Camel Case property has been deprecated in favor of its Pascal Case variant, [BasePart:GetMass()](/docs/reference/engine/classes/BasePart.md). *Security: None · Thread Safety: Unsafe* **Returns:** `float` ### Method: BasePart:GetRenderCFrame **Signature:** `BasePart:GetRenderCFrame(): CFrame` > **Deprecated:** This item is been deprecated since interpolation is now applied to the [CFrame](/docs/reference/engine/datatypes/CFrame.md) directly. Do not use it for new work. This function used to be relevant when Roblox's lag-compensating interpolation of parts online was internal. The interpolation is now applied to the [CFrame](/docs/reference/engine/datatypes/CFrame.md) directly. *Security: None · Thread Safety: Unsafe* **Returns:** `CFrame` ### Method: BasePart:GetRootPart **Signature:** `BasePart:GetRootPart(): Instance` Returns the base part of an assembly. When moving an assembly of parts using a [CFrame](/docs/reference/engine/datatypes/CFrame.md). it is important to move this base part (this will move all other parts connected to it accordingly). More information is available in the [Assemblies](/docs/en-us/physics/assemblies.md) article. This function predates the [AssemblyRootPart](/docs/reference/engine/classes/BasePart.md) property. It remains supported for backwards compatibility, but you should use [AssemblyRootPart](/docs/reference/engine/classes/BasePart.md) directly. *Security: None · Thread Safety: Safe · Simulation Access: true* **Returns:** `Instance` — The base part of an assembly (a collection of parts connected together). ### Method: BasePart:MakeJoints **Signature:** `BasePart:MakeJoints(): ()` > **Deprecated:** SurfaceType based joining is deprecated, do not use MakeJoints for new projects. [WeldConstraints](/docs/reference/engine/classes/WeldConstraint.md) and [HingeConstraints](/docs/reference/engine/classes/HingeConstraint.md) should be used instead. Creates a joint on any side of the [Part](/docs/reference/engine/classes/BasePart.md) that has a [SurfaceType](/docs/reference/engine/enums/SurfaceType.md) that can make a joint it will create a joint with any adjacent parts. Joints will be created between the sides and any planar touching surfaces, depending on the sides' surfaces. - Smooth surfaces will not create joints - Glue surfaces will create a [Glue](/docs/reference/engine/classes/Glue.md) joint - Weld will create a [Weld](/docs/reference/engine/classes/Weld.md) joint with any surface except for Unjoinable - Studs, Inlet, or Universal will each create a [Snap](/docs/reference/engine/classes/Snap.md) joint with either of other the other two surfaces (e.g. Studs with Inlet and Universal) - Hinge and Motor surfaces create [Rotate](/docs/reference/engine/classes/Rotate.md) and [RotateV](/docs/reference/engine/classes/RotateV.md) joint instances Unlike [Model:MakeJoints()](/docs/reference/engine/classes/Model.md), this function requires an array of parts as a parameter. This array is given as follows: ``` part:MakeJoints({part1, part2, part3}) ``` Joints are broken if enough force is applied to them due to an [Explosion](/docs/reference/engine/classes/Explosion.md), unless a [ForceField](/docs/reference/engine/classes/ForceField.md) object is parented to the [BasePart](/docs/reference/engine/classes/BasePart.md) or ancestor [Model](/docs/reference/engine/classes/Model.md). For this reason, they are often used to make simple destructible buildings and other models. *Security: None · Thread Safety: Unsafe* **Returns:** `()` ### Method: BasePart:makeJoints **Signature:** `BasePart:makeJoints(): ()` > **Deprecated:** This deprecated function is a variant of [BasePart:MakeJoints()](/docs/reference/engine/classes/BasePart.md) which should be used instead. *Security: None · Thread Safety: Unsafe* **Returns:** `()` ### Method: BasePart:resize **Signature:** `BasePart:resize(normalId: NormalId, deltaAmount: int): boolean` > **Deprecated:** This deprecated function is a variant of [BasePart:Resize()](/docs/reference/engine/classes/BasePart.md) which should be used instead. *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `normalId` | `NormalId` | | | | `deltaAmount` | `int` | | | **Returns:** `boolean` ## Events ### Event: BasePart.Touched **Signature:** `BasePart.Touched(otherPart: BasePart)` The **Touched** event fires when a part comes in contact with another part. For instance, if **PartA** bumps into **PartB**, then [PartA.Touched](/docs/reference/engine/classes/BasePart.md) fires with **PartB**, and [PartB.Touched](/docs/reference/engine/classes/BasePart.md) fires with **PartA**. This event only fires as a result of physical movement, so it will not fire if the [CFrame](/docs/reference/engine/classes/BasePart.md) property was changed such that the part overlaps another part. This also means that at least one of the parts involved must **not** be [Anchored](/docs/reference/engine/classes/BasePart.md) at the time of the collision. This event works in conjunction with [Workspace.TouchesUseCollisionGroups](/docs/reference/engine/classes/Workspace.md) to specify whether [collision groups](/docs/en-us/workspace/collisions.md#collision-filtering) are acknowledged for detection. *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `otherPart` | `BasePart` | The other part that came in contact with the given part. | **Touching Parts Count** This code sample creates a BillboardGui on a part that displays the number of parts presently touching it. ```lua local part = script.Parent local billboardGui = Instance.new("BillboardGui") billboardGui.Size = UDim2.new(0, 200, 0, 50) billboardGui.Adornee = part billboardGui.AlwaysOnTop = true billboardGui.Parent = part local tl = Instance.new("TextLabel") tl.Size = UDim2.new(1, 0, 1, 0) tl.BackgroundTransparency = 1 tl.Parent = billboardGui local numTouchingParts = 0 local function onTouch(otherPart) print("Touch started: " .. otherPart.Name) numTouchingParts = numTouchingParts + 1 tl.Text = numTouchingParts end local function onTouchEnded(otherPart) print("Touch ended: " .. otherPart.Name) numTouchingParts = numTouchingParts - 1 tl.Text = numTouchingParts end part.Touched:Connect(onTouch) part.TouchEnded:Connect(onTouchEnded) ``` **Model Touched** This code sample demonstrates how to connect the [BasePart.Touched](/docs/reference/engine/classes/BasePart.md) event of multiple parts in a [Model](/docs/reference/engine/classes/Model.md) to one function. ```lua local model = script.Parent local function onTouched(otherPart) -- Ignore instances of the model coming in contact with itself if otherPart:IsDescendantOf(model) then return end print(model.Name .. " collided with " .. otherPart.Name) end for _, child in pairs(model:GetChildren()) do if child:IsA("BasePart") then child.Touched:Connect(onTouched) end end ``` ### Event: BasePart.TouchEnded **Signature:** `BasePart.TouchEnded(otherPart: BasePart)` Fires when a part stops touching another part under similar conditions to those of [BasePart.Touched](/docs/reference/engine/classes/BasePart.md). This event works in conjunction with [Workspace.TouchesUseCollisionGroups](/docs/reference/engine/classes/Workspace.md) to specify whether [collision groups](/docs/en-us/workspace/collisions.md#collision-filtering) are acknowledged for detection. *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `otherPart` | `BasePart` | | **Touching Parts Count** This code sample creates a BillboardGui on a part that displays the number of parts presently touching it. ```lua local part = script.Parent local billboardGui = Instance.new("BillboardGui") billboardGui.Size = UDim2.new(0, 200, 0, 50) billboardGui.Adornee = part billboardGui.AlwaysOnTop = true billboardGui.Parent = part local tl = Instance.new("TextLabel") tl.Size = UDim2.new(1, 0, 1, 0) tl.BackgroundTransparency = 1 tl.Parent = billboardGui local numTouchingParts = 0 local function onTouch(otherPart) print("Touch started: " .. otherPart.Name) numTouchingParts = numTouchingParts + 1 tl.Text = numTouchingParts end local function onTouchEnded(otherPart) print("Touch ended: " .. otherPart.Name) numTouchingParts = numTouchingParts - 1 tl.Text = numTouchingParts end part.Touched:Connect(onTouch) part.TouchEnded:Connect(onTouchEnded) ``` ### Event: BasePart.LocalSimulationTouched **Signature:** `BasePart.LocalSimulationTouched(part: BasePart)` > **Deprecated:** This event is deprecated in favor of [BasePart.Touched](/docs/reference/engine/classes/BasePart.md). Fired when another part comes in contact with another object. This event only sends data to the client notifying it that two parts have collided, whereas [BasePart.Touched](/docs/reference/engine/classes/BasePart.md) sends data to the server. *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `part` | `BasePart` | | **BasePart.LocalSimulationTouched** ```lua workspace.Part.LocalSimulationTouched:Connect(function(part) print(part.Name) end) ``` ### Event: BasePart.OutfitChanged **Signature:** `BasePart.OutfitChanged()` > **Deprecated:** This event is deprecated. Do not use it for new work. Fired if the part's appearance is affected by the [Shirt](/docs/reference/engine/classes/Shirt.md) class. *Security: None* ### Event: BasePart.StoppedTouching **Signature:** `BasePart.StoppedTouching(otherPart: BasePart)` > **Deprecated:** This event is deprecated in favor of [BasePart.TouchEnded](/docs/reference/engine/classes/BasePart.md), which should be used instead. *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `otherPart` | `BasePart` | | ## Inherited Members ### From [PVInstance](/docs/reference/engine/classes/PVInstance.md) - **Property `Origin`** (`CFrame`): - **Property `Pivot Offset`** (`CFrame`): - **Method `GetPivot(): CFrame`**: Gets the pivot of a PVInstance. - **Method `PivotTo(targetCFrame: CFrame): ()`**: Transforms the PVInstance along with all of its descendant ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: BasePlayerGui last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable summary: "`BasePlayerGui` is an abstract class which the GUI drawing storage classes inherit from." --- # Class: BasePlayerGui > `BasePlayerGui` is an abstract class which the GUI drawing storage classes > inherit from. ## Description `BasePlayerGui` is an abstract class that all GUI drawing storage classes inherit from. ## Methods ### Method: BasePlayerGui:GetGuiObjectsAtPosition **Signature:** `BasePlayerGui:GetGuiObjectsAtPosition(x: int, y: int): Instances` Takes a screen position and returns a list of all the [GuiObject](/docs/reference/engine/classes/GuiObject.md) instances which are occupying that screen position, sorted in the order they appear on screen from top to bottom as the first and last index, respectively. The main use case is to get GUI objects under the player's mouse or touch inputs to allow for selection or highlighting. Since the child classes of [BasePlayerGui](/docs/reference/engine/classes/BasePlayerGui.md) inherit this function, it can be fired by container classes such as [PlayerGui](/docs/reference/engine/classes/PlayerGui.md) and [StarterGui](/docs/reference/engine/classes/StarterGui.md). *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `int` | | The **X** position on the screen relative to the top-left corner after the GUI inset is applied (see [GuiService:GetGuiInset()](/docs/reference/engine/classes/GuiService.md)). | | `y` | `int` | | The **Y** position on the screen relative to the top-left corner after the GUI inset is applied (see [GuiService:GetGuiInset()](/docs/reference/engine/classes/GuiService.md)). | **Returns:** `Instances` — A table of the [GuiObject](/docs/reference/engine/classes/GuiObject.md) instances that occupy the given screen space. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: BaseRemoteEvent last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable --- # Class: BaseRemoteEvent ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: BaseScript last_updated: 2026-06-29T19:34:07Z inherits: - LuaSourceContainer - Instance - Object type: class memory_category: Script tags: - NotCreatable summary: "The base class for all script objects which run automatically." --- # Class: BaseScript > The base class for all script objects which run automatically. ## Properties ### Property: BaseScript.Disabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior" } ``` Determines whether a [BaseScript](/docs/reference/engine/classes/BaseScript.md) will run or not. If a script is disabled by changing this property to true while the script is running, the current running thread of the script will be terminated. If this property is changed from true to false, the script will run again. This means that [Disabled](/docs/reference/engine/classes/BaseScript.md) can be toggled to restart a script: ```lua scriptObject.Disabled = true scriptObject.Disabled = false ``` Note that the above code snippet cannot be used within the script itself, since disabling the script from within itself will terminate the thread and the line to reenable it will never execute. ### Property: BaseScript.Enabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Behavior" } ``` Determines whether a [BaseScript](/docs/reference/engine/classes/BaseScript.md) will run or not. This should be used in favor of the similar but opposite [Disabled](/docs/reference/engine/classes/BaseScript.md) property. If a script is disabled by changing this property to false while the script is running, the current running thread of the script will be terminated. If this property is changed from false to true, the script will run again. This means that [Enabled](/docs/reference/engine/classes/BaseScript.md) can be toggled to restart a script: ```lua scriptObject.Enabled = false scriptObject.Enabled = true ``` Note that the above code snippet cannot be used within the script itself, since disabling the script from within itself will terminate the thread and the line to reenable it will never execute. ### Property: BaseScript.RunContext ```json { "type": "RunContext", "access": "ReadOnly", "security": { "read": "None", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior" } ``` Determines the context under which the script will run. When using the Legacy RunContext scripts will only run when parented to certain containers dependent on whether they are a [Script](/docs/reference/engine/classes/Script.md) or [LocalScript](/docs/reference/engine/classes/LocalScript.md). If RunContext is assigned while the script is running any threads created by the script will be terminated and the script will start running under the new context if possible. Note, RunContext cannot be used from a LocalScript. ### Property: BaseScript.LinkedSource ```json { "type": "ContentId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "LoadUnownedAsset" ] } ``` > **Deprecated:** This property is now replaced by [packages](/docs/en-us/projects/assets/packages.md) which has greater functionality. The content ID of an uploaded script. When set binds the uploaded code to the script's [Script.Source](/docs/reference/engine/classes/Script.md). By default, this property is set to _'[Embedded]'_. This means the source of the script is not linked to an upload script and is instead written in the script. ```lua script.LinkedSource = "http://www.roblox.com/asset/?id=1014476" -- link source ``` Developers should remove a linked source via the properties window, rather than setting the property to _'[Embedded]'_. For the LinkedSource property for [ModuleScripts](/docs/reference/engine/classes/ModuleScript.md), please see [ModuleScript.LinkedSource](/docs/reference/engine/classes/ModuleScript.md). ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: BaseWrap last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable --- # Class: BaseWrap ## Description The base class for [WrapTarget](/docs/reference/engine/classes/WrapTarget.md) and [WrapLayer](/docs/reference/engine/classes/WrapLayer.md) objects. Note that [MeshPart](/docs/reference/engine/classes/MeshPart.md) is the only valid parent type for [BaseWrap](/docs/reference/engine/classes/BaseWrap.md) and that it behaves more like a component of [MeshPart](/docs/reference/engine/classes/MeshPart.md) than an independent object. ## Properties ### Property: BaseWrap.CageMeshContent ```json { "type": "Content", "access": "ReadOnly", "security": { "read": "None", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "AvatarAppearance" ] } ``` ### Property: BaseWrap.CageMeshId ```json { "type": "ContentId", "access": "ReadOnly", "security": { "read": "None", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "AvatarAppearance" ] } ``` This property is set up automatically by the 3D Importer. Asset ID for cage mesh. ### Property: BaseWrap.CageOrigin ```json { "type": "CFrame", "access": "ReadOnly", "security": { "read": "None", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "AvatarAppearance" ] } ``` This property is set up automatically by the 3D Importer. Cage mesh offset relative to parent [MeshPart](/docs/reference/engine/classes/MeshPart.md). ### Property: BaseWrap.CageOriginWorld ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarAppearance" ] } ``` Cage mesh offset in world space. ### Property: BaseWrap.HSRAssetId ```json { "type": "ContentId", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarAppearance" ] } ``` ### Property: BaseWrap.ImportOrigin ```json { "type": "CFrame", "access": "ReadOnly", "security": { "read": "None", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "AvatarAppearance" ] } ``` This property is set up automatically by the 3D Importer. Describes where a global zero was while authoring the cage mesh in an asset creation tool such as Blender or Maya. This property is not used by the deformer but it is useful for tools/aligning scripts, for example aligning two parts by matching their pivots as follows: ```lua local function alignWraps() local selectionService = game:GetService("Selection") local selectedObjects = selectionService:Get() local alignObjects = {} for _, obj in selectedObjects do if obj:IsA("BaseWrap") then --print("Wrap: " .. obj.Name) table.insert(alignObjects, obj) else print("Ignore: " .. obj.Name) end end if #alignObjects < 2 then warn("You need to select at least two wraps") return end local anchorWrap = alignObjects[1] local worldA_from_Wrap = anchorWrap.ImportOriginWorld print("Anchor: " .. anchorWrap.Name) for i = 2, #alignObjects do local wrapToAlign = alignObjects[i] print("Align: " .. wrapToAlign.Name) local wrap_from_WorldB = wrapToAlign.ImportOriginWorld:Inverse() local worldA_from_WorldB = worldA_from_Wrap * wrap_from_WorldB local worldB = wrapToAlign.Parent.CFrame -- Note: adjust CFrame of the parent part wrapToAlign.Parent.CFrame = (worldB_from_WorldB * worldB) end end ``` ### Property: BaseWrap.ImportOriginWorld ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarAppearance" ] } ``` Describes where the origin (in world space) was while authoring the cage mesh in an asset creation tool such as Blender or Maya. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Beam last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances summary: "Connects two Attachments by drawing a texture between them." --- # Class: Beam > Connects two [Attachments](/docs/reference/engine/classes/Attachment.md) by drawing a texture between them. ## Description A **Beam** object connects two [Attachments](/docs/reference/engine/classes/Attachment.md) by drawing a texture between them. To display, a beam must be a descendant of the [Workspace](/docs/reference/engine/classes/Workspace.md) with its [Attachment0](/docs/reference/engine/classes/Beam.md) and [Attachment1](/docs/reference/engine/classes/Beam.md) properties set to [Attachments](/docs/reference/engine/classes/Attachment.md) also descending from the [Workspace](/docs/reference/engine/classes/Workspace.md). The beam's appearance can be customized using the range of properties outlined below. Also see the [Beams](/docs/en-us/effects/beams.md) guide for visual examples. #### Beam Curvature Beams are configured to use a cubic Bézier curve formed by four control points. This means they are not constrained to straight lines and the curve of the beam can be modified by changing [CurveSize0](/docs/reference/engine/classes/Beam.md), [CurveSize1](/docs/reference/engine/classes/Beam.md), and the orientation of the beam's [Attachments](/docs/reference/engine/classes/Attachment.md). - **P0** — The start of the beam; position of [Attachment0](/docs/reference/engine/classes/Beam.md). - **P1** — [CurveSize0](/docs/reference/engine/classes/Beam.md) studs away from [Attachment0](/docs/reference/engine/classes/Beam.md), in the positive **X** direction of [Attachment0](/docs/reference/engine/classes/Beam.md). - **P2** — [CurveSize1](/docs/reference/engine/classes/Beam.md) studs away from [Attachment1](/docs/reference/engine/classes/Beam.md), in the negative **X** direction of [Attachment1](/docs/reference/engine/classes/Beam.md). - **P3** — The end of the beam; position of [Attachment1](/docs/reference/engine/classes/Beam.md) ![Beam curvature diagram](/assets/engine-api/classes/Beam/Curvature-Diagram.png) ## Code Samples **Creating a Beam From Scratch** This code sample demonstrates how a `Beam` effect can be created from scratch by creating a `Beam`, setting all of its properties and configuring it's `Attachment`s. ```lua -- create attachments local att0 = Instance.new("Attachment") local att1 = Instance.new("Attachment") -- parent to terrain (can be part instead) att0.Parent = workspace.Terrain att1.Parent = workspace.Terrain -- position attachments att0.Position = Vector3.new(0, 10, 0) att1.Position = Vector3.new(0, 10, 10) -- create beam local beam = Instance.new("Beam") beam.Attachment0 = att0 beam.Attachment1 = att1 -- appearance properties beam.Color = ColorSequence.new({ -- a color sequence shifting from white to blue ColorSequenceKeypoint.new(0, Color3.fromRGB(255, 255, 255)), ColorSequenceKeypoint.new(1, Color3.fromRGB(0, 255, 255)), }) beam.LightEmission = 1 -- use additive blending beam.LightInfluence = 0 -- beam not influenced by light beam.Texture = "rbxasset://textures/particles/sparkles_main.dds" -- a built in sparkle texture beam.TextureMode = Enum.TextureMode.Wrap -- wrap so length can be set by TextureLength beam.TextureLength = 1 -- repeating texture is 1 stud long beam.TextureSpeed = 1 -- slow texture speed beam.Transparency = NumberSequence.new({ -- beam fades out at the end NumberSequenceKeypoint.new(0, 0), NumberSequenceKeypoint.new(0.8, 0), NumberSequenceKeypoint.new(1, 1), }) beam.ZOffset = 0 -- render at the position of the beam without offset -- shape properties beam.CurveSize0 = 2 -- create a curved beam beam.CurveSize1 = -2 -- create a curved beam beam.FaceCamera = true -- beam is visible from every angle beam.Segments = 10 -- default curve resolution beam.Width0 = 0.2 -- starts small beam.Width1 = 2 -- ends big -- parent beam beam.Enabled = true beam.Parent = att0 ``` ## Properties ### Property: Beam.Attachment0 ```json { "type": "Attachment", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Shape", "capabilities": [ "Basic" ] } ``` The [Attachment](/docs/reference/engine/classes/Attachment.md) the beam originates from. This attachment is the first control point on the beam's cubic Bézier curve; its orientation, alongside the [CurveSize0](/docs/reference/engine/classes/Beam.md) property, determines the position of the second control point. See [Beams](/docs/en-us/effects/beams.md#curve) for more details. For the [Attachment](/docs/reference/engine/classes/Attachment.md) where the beam ends, see [Attachment1](/docs/reference/engine/classes/Beam.md). ### Property: Beam.Attachment1 ```json { "type": "Attachment", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Shape", "capabilities": [ "Basic" ] } ``` The [Attachment](/docs/reference/engine/classes/Attachment.md) the beam ends at. This attachment is the fourth and final control point on the beam's cubic Bézier curve; its orientation, alongside the [CurveSize1](/docs/reference/engine/classes/Beam.md) property, determines the position of the third control point. See [Beams](/docs/en-us/effects/beams.md#curve) for more details. For the [Attachment](/docs/reference/engine/classes/Attachment.md) where the beam originates from, see [Attachment0](/docs/reference/engine/classes/Beam.md). ### Property: Beam.Brightness ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` Scales the light emitted from the beam when [LightInfluence](/docs/reference/engine/classes/Beam.md) is less than 1. This property is 1 by default and can set to any number within the range of 0 to 10000. Increasing the value of [LightInfluence](/docs/reference/engine/classes/Beam.md) decreases the effect of this property's value. ### Property: Beam.Color ```json { "type": "ColorSequence", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` Determines the color of the beam across its [Segments](/docs/reference/engine/classes/Beam.md). If [Texture](/docs/reference/engine/classes/Beam.md) is set, this color will be applied to the beam's texture. If no [Texture](/docs/reference/engine/classes/Beam.md) is set, the [Beam](/docs/reference/engine/classes/Beam.md) will appear as a solid line colored in accordance with this property. This property is a [ColorSequence](/docs/reference/engine/datatypes/ColorSequence.md), allowing the color to be configured to vary across the length of the beam. Consider the following [ColorSequence](/docs/reference/engine/datatypes/ColorSequence.md) which, when applied to a beam, would yield the pictured result. ```lua local colorSequence = ColorSequence.new({ ColorSequenceKeypoint.new(0, Color3.fromRGB(255, 0, 0)), -- Red ColorSequenceKeypoint.new(0.5, Color3.fromRGB(0, 188, 203)), -- Cyan ColorSequenceKeypoint.new(1, Color3.fromRGB(196, 0, 255)), -- Purple } ) ``` Note the beam's coloration also depends on the number of [Segments](/docs/reference/engine/classes/Beam.md) the [Beam](/docs/reference/engine/classes/Beam.md) has. Each segment of the beam can only show a transition between two colors. Therefore a [Beam](/docs/reference/engine/classes/Beam.md) will need to have at least `n-1` segments in order for the color to display correctly, where `n` is the number of [ColorSequenceKeypoints](/docs/reference/engine/datatypes/ColorSequenceKeypoint.md) in the [ColorSequence](/docs/reference/engine/datatypes/ColorSequence.md). ### Property: Beam.CurveSize0 ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Shape", "capabilities": [ "Basic" ] } ``` Determines, along with [Attachment0](/docs/reference/engine/classes/Beam.md), the position of the second control point in the beam's Bézier curve. See [Beams](/docs/en-us/effects/beams.md#curve) for more details. The position of this point can be determined by the following equation: ```lua local controlPoint2 = Beam.Attachment0.WorldPosition + (Beam.Attachment0.CFrame.RightVector * Beam.CurveSize0) ``` ### Property: Beam.CurveSize1 ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Shape", "capabilities": [ "Basic" ] } ``` Determines, along with [Attachment1](/docs/reference/engine/classes/Beam.md), the position of the third control point in the beam's Bézier curve. See [Beams](/docs/en-us/effects/beams.md#curve) for more details. The position of this point can be determined by the following equation: ```lua local controlPoint3 = Beam.Attachment1.WorldPosition - (Beam.Attachment1.CFrame.RightVector * Beam.CurveSize1) ``` ### Property: Beam.Enabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` Determines whether the beam is visible or not. When this property is set to false, the beam's [Segments](/docs/reference/engine/classes/Beam.md) will not be displayed. ### Property: Beam.FaceCamera ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Shape", "capabilities": [ "Basic" ] } ``` A [Beam](/docs/reference/engine/classes/Beam.md) is a 2D projection existing in 3D space, meaning that it may not be visible from every angle. The **FaceCamera** property, when set to `true`, ensures that the beam always faces the [CurrentCamera](/docs/reference/engine/classes/Workspace.md), regardless of its orientation. ### Property: Beam.LightEmission ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` Determines to what degree the colors of the beam are blended with the colors behind it. It should be set in the range of 0 to 1. A value of 0 uses normal blending modes and a value of 1 uses additive blending. This property should not be confused with [LightInfluence](/docs/reference/engine/classes/Beam.md) which determines how the beam is affected by environmental light. This property does **not** cause the beam to light the environment. ### Property: Beam.LightInfluence ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` Determines the degree to which the beam is influenced by the environment's lighting, clamped between 0 and 1. When 0, the beam will be unaffected by the environment's lighting. When 1, it will be fully affected by lighting as a [BasePart](/docs/reference/engine/classes/BasePart.md) would be. See also [LightEmission](/docs/reference/engine/classes/Beam.md) which specifies to what degree the colors of the beam are blended with the colors behind it. ### Property: Beam.Segments ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Shape", "capabilities": [ "Basic" ] } ``` Rather than being a perfect curve, a beam is made up of straight segments. The more segments, the higher the resolution of the curve. The **Segments** property sets how many straight segments the beam is made up of, with a default value of 10. Note that the [Color](/docs/reference/engine/classes/Beam.md) and [Transparency](/docs/reference/engine/classes/Beam.md) properties require a certain number of segments to display correctly. This is because each segment can only show a transition between two colors or transparencies. Therefore a [Beam](/docs/reference/engine/classes/Beam.md) requires at least `n-1` segments to display correctly, where `n` is the number of keypoint associated with the beam's [Color](/docs/reference/engine/classes/Beam.md) and [Transparency](/docs/reference/engine/classes/Beam.md). ### Property: Beam.Texture ```json { "type": "ContentId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` The content ID of the texture to be displayed on the beam. If this property is not set, the beam will be displayed as a solid line; this also occurs when the texture is set to an invalid content ID or the image associated with the texture has not yet loaded. The appearance of the texture can be further modified by other beam properties including [Color](/docs/reference/engine/classes/Beam.md) and [Transparency](/docs/reference/engine/classes/Beam.md). Scaling of the texture is determined by the [TextureMode](/docs/reference/engine/classes/Beam.md), [TextureLength](/docs/reference/engine/classes/Beam.md), [Width0](/docs/reference/engine/classes/Beam.md), and [Width1](/docs/reference/engine/classes/Beam.md) properties. ### Property: Beam.TextureLength ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` Sets the length of the beam's texture, dependent on [TextureMode](/docs/reference/engine/classes/Beam.md). ### Property: Beam.TextureMode ```json { "type": "TextureMode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` This property, alongside [TextureLength](/docs/reference/engine/classes/Beam.md), determines how a beam's [Texture](/docs/reference/engine/classes/Beam.md) repeats. When set to [TextureMode.Wrap](/docs/reference/engine/enums/TextureMode.md) or [TextureMode.Static](/docs/reference/engine/enums/TextureMode.md), the texture repetitions will equal the beam's overall length (in studs) divided by its [TextureLength](/docs/reference/engine/classes/Beam.md). ![TextureMode diagram with Wrap mode](/assets/engine-api/enums/TextureMode/Wrap-Static.png) When set to [TextureMode.Stretch](/docs/reference/engine/enums/TextureMode.md), the texture will repeat [TextureLength](/docs/reference/engine/classes/Beam.md) times across the beam's overall length. ![TextureMode diagram with Stretch mode](/assets/engine-api/enums/TextureMode/Stretch.png) ### Property: Beam.TextureSpeed ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` Sets the number of texture cycles per second at which the [Texture](/docs/reference/engine/classes/Beam.md) image moves along the beam, where one cycle is a full traversal of the texture's UV range. When this property is a positive value, the beam's texture will move from [Attachment0](/docs/reference/engine/classes/Beam.md) to [Attachment1](/docs/reference/engine/classes/Beam.md). This direction can be inverted by setting this property to a negative number. The default value is `1`. How far one cycle travels along the beam in studs depends on [TextureMode](/docs/reference/engine/classes/Beam.md) and [TextureLength](/docs/reference/engine/classes/Beam.md). #### Examples - `-2` — Texture scrolls from [Attachment1](/docs/reference/engine/classes/Beam.md) toward [Attachment0](/docs/reference/engine/classes/Beam.md), completing two cycles per second. - `0` — Texture is static. - `1` — Texture scrolls from [Attachment0](/docs/reference/engine/classes/Beam.md) toward [Attachment1](/docs/reference/engine/classes/Beam.md), completing one cycle per second (default). - `2` — Texture scrolls from [Attachment0](/docs/reference/engine/classes/Beam.md) toward [Attachment1](/docs/reference/engine/classes/Beam.md), completing two cycles per second. ### Property: Beam.Transparency ```json { "type": "NumberSequence", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` Determines the transparency of the beam across its segments. This property is a [NumberSequence](/docs/reference/engine/datatypes/NumberSequence.md), allowing the transparency to be configured to vary across the length of the beam. Consider the following [NumberSequence](/docs/reference/engine/datatypes/NumberSequence.md) which, when applied to a beam, would yield the pictured result. ```lua local numberSequence = NumberSequence.new({ NumberSequenceKeypoint.new(0, 0), -- Opaque NumberSequenceKeypoint.new(0.5, 1), -- Transparent NumberSequenceKeypoint.new(1, 0), -- Opaque } ) ``` Note that the beam's transparency also depends on the number of [Segments](/docs/reference/engine/classes/Beam.md). Each segment of the beam can only show a transition between two transparencies. Therefore a beam will need to have at least `n-1` segments in order to display correctly, where `n` is the number of [NumberSequenceKeypoints](/docs/reference/engine/datatypes/NumberSequenceKeypoint.md) in the [NumberSequence](/docs/reference/engine/datatypes/NumberSequence.md). ### Property: Beam.Width0 ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Shape", "capabilities": [ "Basic" ] } ``` The width of the beam at its origin ([Attachment0](/docs/reference/engine/classes/Beam.md)), in studs. The beam's width will change linearly to [Width1](/docs/reference/engine/classes/Beam.md) studs at its end ([Attachment1](/docs/reference/engine/classes/Beam.md)). ### Property: Beam.Width1 ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Shape", "capabilities": [ "Basic" ] } ``` The width of the beam at its end ([Attachment1](/docs/reference/engine/classes/Beam.md)), in studs. The beam's width will change linearly from [Width0](/docs/reference/engine/classes/Beam.md) studs at its origin ([Attachment0](/docs/reference/engine/classes/Beam.md)). ### Property: Beam.ZOffset ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` The distance, in studs, the beam display is offset relative to the [CurrentCamera](/docs/reference/engine/classes/Workspace.md). When 0, the beam will be displayed in its standard position between [Attachment0](/docs/reference/engine/classes/Beam.md) and [Attachment1](/docs/reference/engine/classes/Beam.md). **ZOffset** can be either positive or negative. This property is particularly useful to avoid "Z‑fighting" when using multiple [Beams](/docs/reference/engine/classes/Beam.md) between the same [Attachments](/docs/reference/engine/classes/Attachment.md). **Layering Beams** This code sample uses the [Beam.ZOffset](/docs/reference/engine/classes/Beam.md) property to layer multiple beams between the same attachments. ```lua -- Create beams local beam1 = Instance.new("Beam") beam1.Color = ColorSequence.new(Color3.new(1, 0, 0)) beam1.FaceCamera = true beam1.Width0 = 3 beam1.Width1 = 3 local beam2 = Instance.new("Beam") beam2.Color = ColorSequence.new(Color3.new(0, 1, 0)) beam2.FaceCamera = true beam2.Width0 = 2 beam2.Width1 = 2 local beam3 = Instance.new("Beam") beam3.Color = ColorSequence.new(Color3.new(0, 0, 1)) beam3.FaceCamera = true beam3.Width0 = 1 beam3.Width1 = 1 -- Layer beams beam1.ZOffset = 0 beam2.ZOffset = 0.01 beam3.ZOffset = 0.02 -- Create attachments local attachment0 = Instance.new("Attachment") attachment0.Position = Vector3.new(0, -10, 0) attachment0.Parent = workspace.Terrain local attachment1 = Instance.new("Attachment") attachment1.Position = Vector3.new(0, 10, 0) attachment1.Parent = workspace.Terrain -- Connect beams beam1.Attachment0 = attachment0 beam1.Attachment1 = attachment1 beam2.Attachment0 = attachment0 beam2.Attachment1 = attachment1 beam3.Attachment0 = attachment0 beam3.Attachment1 = attachment1 -- Parent beams beam1.Parent = workspace beam2.Parent = workspace beam3.Parent = workspace ``` ### Property: Beam.LocalTransparencyModifier *(hidden)* ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` ## Methods ### Method: Beam:SetTextureOffset **Signature:** `Beam:SetTextureOffset(offset?: float): ()` The offset of a beam's texture cycle represents the progress of its texture animation. This method sets the current offset of the beam's texture cycle; hence, it can be used to reset the cycle by passing `0` as the `offset` parameter. #### Notes - The given `offset` parameter is expected to be a value between 0 and 1, but greater values can be used. - The texture cycle wraps at 0 and 1, meaning the texture is in the same position when the offset is at 0 or 1. - If the [Texture](/docs/reference/engine/classes/Beam.md) property is not set, this method does nothing. - Increasing the offset will act in the inverse direction to the [TextureSpeed](/docs/reference/engine/classes/Beam.md) property, meaning it will move the texture in the opposite direction to the direction the texture animates when [TextureSpeed](/docs/reference/engine/classes/Beam.md) is greater than 0. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `offset` | `float` | `0` | The desired offset of the texture cycle. | **Returns:** `()` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: BevelMesh last_updated: 2026-06-29T19:34:07Z inherits: - DataModelMesh - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotBrowsable - Deprecated summary: "An abstract class that BlockMesh and CylinderMesh inherit from." --- # Class: BevelMesh > An abstract class that [BlockMesh](/docs/reference/engine/classes/BlockMesh.md) and [CylinderMesh](/docs/reference/engine/classes/CylinderMesh.md) inherit > from. ## Description BevelMesh is an abstract class that [BlockMesh](/docs/reference/engine/classes/BlockMesh.md) and [CylinderMesh](/docs/reference/engine/classes/CylinderMesh.md) inherit from. ## Inherited Members ### From [DataModelMesh](/docs/reference/engine/classes/DataModelMesh.md) - **Property `Offset`** (`Vector3`): The Offset of a mesh determines the relative position from the - **Property `Scale`** (`Vector3`): The Scale of a mesh determines the size of the mesh relative to its - **Property `VertexColor`** (`Vector3`): Changes the hue of a mesh's texture, used with FileMesh.TextureId. ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: BillboardGui last_updated: 2026-06-29T19:34:07Z inherits: - LayerCollector - GuiBase2d - GuiBase - Instance - Object type: class memory_category: Instances summary: "A container for GuiObjects that renders in 3D space facing the camera." --- # Class: BillboardGui > A container for [GuiObjects](/docs/reference/engine/classes/GuiObject.md) that renders in 3D space facing > the camera. ## Description [BillboardGui](/docs/reference/engine/classes/BillboardGui.md) is a container for UI objects to appear in the 3D space but always face the camera. The container's position is relative to the parent [BasePart](/docs/reference/engine/classes/BasePart.md) or [Attachment](/docs/reference/engine/classes/Attachment.md) (or the [Adornee](/docs/reference/engine/classes/BillboardGui.md)). For [BaseParts](/docs/reference/engine/classes/BasePart.md), the [Position](/docs/reference/engine/classes/BasePart.md) property is used, while for [Attachments](/docs/reference/engine/classes/Attachment.md), the [WorldPosition](/docs/reference/engine/classes/Attachment.md) property is used. ![BillboardGui with a TextLabel describing the screen console it floats above.](/assets/ui/in-experience/BillboardGui-Diagram.jpg) A billboard's [Size](/docs/reference/engine/classes/BillboardGui.md) property works slightly differently than [GuiObject.Size](/docs/reference/engine/classes/GuiObject.md). While the **offset** components work the same, the **scale** components are used as stud sizes in 3D space. When creating a size-scaled [BillboardGui](/docs/reference/engine/classes/BillboardGui.md) that contains a [TextLabel](/docs/reference/engine/classes/TextLabel.md), it's useful to enable the label's [TextScaled](/docs/reference/engine/classes/TextLabel.md) property so that its text scales along with the billboard canvas as the camera distance changes. Note that interactive UI elements like [ImageButtons](/docs/reference/engine/classes/ImageButton.md) and [TextButtons](/docs/reference/engine/classes/TextButton.md) inside a [BillboardGui](/docs/reference/engine/classes/BillboardGui.md) will only receive user input if they are parented to the [PlayerGui](/docs/reference/engine/classes/PlayerGui.md), typically via placing the [BillboardGui](/docs/reference/engine/classes/BillboardGui.md) inside [StarterGui](/docs/reference/engine/classes/StarterGui.md). The [Adornee](/docs/reference/engine/classes/BillboardGui.md) property can be used to target a part or attachment in the 3D world while the [BillboardGui](/docs/reference/engine/classes/BillboardGui.md) itself remains in the [PlayerGui](/docs/reference/engine/classes/PlayerGui.md). See [In-Experience UI](/docs/en-us/ui/in-experience-containers.md#billboard-ui) for a guide on working with [BillboardGui](/docs/reference/engine/classes/BillboardGui.md) containers. ##### Caching Behavior To help improve performance, the appearance of a [BillboardGui](/docs/reference/engine/classes/BillboardGui.md) is cached until one of the following occurs, after which its appearance will be recomputed on the next rendering frame. - A descendant is added to or removed from the [BillboardGui](/docs/reference/engine/classes/BillboardGui.md). - A property of a descendant of the [BillboardGui](/docs/reference/engine/classes/BillboardGui.md) changes. - A property of the [BillboardGui](/docs/reference/engine/classes/BillboardGui.md) itself changes. ## Properties ### Property: BillboardGui.Active ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` Controls whether the descendants will receive input events. If the [BillboardGui](/docs/reference/engine/classes/BillboardGui.md) contains a [GuiButton](/docs/reference/engine/classes/GuiButton.md), that button will become clickable only if [Active](/docs/reference/engine/classes/BillboardGui.md) is set to `true` on both the [BillboardGui](/docs/reference/engine/classes/BillboardGui.md) **and** the button. Note that interactive UI elements like [ImageButtons](/docs/reference/engine/classes/ImageButton.md) and [TextButtons](/docs/reference/engine/classes/TextButton.md) inside a [BillboardGui](/docs/reference/engine/classes/BillboardGui.md) will only receive user input if they are parented to the [PlayerGui](/docs/reference/engine/classes/PlayerGui.md), typically via placing the [BillboardGui](/docs/reference/engine/classes/BillboardGui.md) inside [StarterGui](/docs/reference/engine/classes/StarterGui.md). The [Adornee](/docs/reference/engine/classes/BillboardGui.md) property can be used to target a part or attachment in the 3D world while the [BillboardGui](/docs/reference/engine/classes/BillboardGui.md) itself remains in the [PlayerGui](/docs/reference/engine/classes/PlayerGui.md). ### Property: BillboardGui.Adornee ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` Sets the target [BasePart](/docs/reference/engine/classes/BasePart.md) or [Attachment](/docs/reference/engine/classes/Attachment.md) that the [BillboardGui](/docs/reference/engine/classes/BillboardGui.md) is positioned relative to, overriding the parent part or attachment. ### Property: BillboardGui.AlwaysOnTop ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` This property determines whether the [BillboardGui](/docs/reference/engine/classes/BillboardGui.md) will always render on top of other 3D objects. When set to `false` (default), the [BillboardGui](/docs/reference/engine/classes/BillboardGui.md) renders like other 3D content and is occluded by other 3D objects. When set to `true`, the [BillboardGui](/docs/reference/engine/classes/BillboardGui.md) always renders on top of 3D content and the appearance changes significantly: - Colors match how they appear inside a [ScreenGui](/docs/reference/engine/classes/ScreenGui.md). - Text may appear sharper on high DPI devices. - [LightInfluence](/docs/reference/engine/classes/BillboardGui.md) is treated as though it's `0`. - [Brightness](/docs/reference/engine/classes/BillboardGui.md) has no effect. ### Property: BillboardGui.Brightness ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` This property determines the factor by which the [BillboardGui](/docs/reference/engine/classes/BillboardGui.md) container's light is scaled when [LightInfluence](/docs/reference/engine/classes/BillboardGui.md) is `0`. By default, this property is `1` and can be set to any number between `0` and `1000`. By modifying this property, the apparent brightness of a [BillboardGui](/docs/reference/engine/classes/BillboardGui.md) can be better matched to its environment. For instance, a video billboard can be brightened inside a dark room by increasing [Brightness](/docs/reference/engine/classes/BillboardGui.md) to `10`. Note that [Brightness](/docs/reference/engine/classes/BillboardGui.md) is inaccessible in Studio and has no effect when either [LightInfluence](/docs/reference/engine/classes/BillboardGui.md) is `1` or [AlwaysOnTop](/docs/reference/engine/classes/BillboardGui.md) is `true`. ### Property: BillboardGui.ClipsDescendants ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "UI" ] } ``` When set to `true` (default), portions of [GuiObjects](/docs/reference/engine/classes/GuiObject.md) that fall outside of the [BillboardGui](/docs/reference/engine/classes/BillboardGui.md) canvas borders will not be drawn. Even when this property is `false`, [GuiObjects](/docs/reference/engine/classes/GuiObject.md) that are **completely** outside of the canvas will not render. ### Property: BillboardGui.CurrentDistance ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` The current distance in studs that the [BillboardGui](/docs/reference/engine/classes/BillboardGui.md) is from the player's camera. A changed event does not fire for this property unless [DistanceStep](/docs/reference/engine/classes/BillboardGui.md) is more than `0`. ### Property: BillboardGui.DistanceStep ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` Determines the size [CurrentDistance](/docs/reference/engine/classes/BillboardGui.md) increments and decrements in studs as the player's camera moves closer and further from the [BillboardGui](/docs/reference/engine/classes/BillboardGui.md). The property defaults to `0`. ### Property: BillboardGui.ExtentsOffset ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` This property determines how the [BillboardGui](/docs/reference/engine/classes/BillboardGui.md) is offset from its [Adornee](/docs/reference/engine/classes/BillboardGui.md), relative to the [Camera](/docs/reference/engine/classes/Camera.md) orientation, in units half the dimensions of the model's [Camera](/docs/reference/engine/classes/Camera.md)-aligned bounding box. See also [StudsOffset](/docs/reference/engine/classes/BillboardGui.md) which works similarly but uses stud units, or [ExtentsOffsetWorldSpace](/docs/reference/engine/classes/BillboardGui.md) which works similarly except the offset orientation is relative to the global axes. ### Property: BillboardGui.ExtentsOffsetWorldSpace ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` This property determines how the [BillboardGui](/docs/reference/engine/classes/BillboardGui.md) is offset from its [Adornee](/docs/reference/engine/classes/BillboardGui.md), relative to the global axes, in units half the dimensions of the model's axis-aligned bounding box. See also [StudsOffset](/docs/reference/engine/classes/BillboardGui.md) which works similarly but uses stud units, or [ExtentsOffset](/docs/reference/engine/classes/BillboardGui.md) which works similarly except the offset orientation is relative to the [Camera](/docs/reference/engine/classes/Camera.md). ### Property: BillboardGui.LightInfluence ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` Controls how much the [BillboardGui](/docs/reference/engine/classes/BillboardGui.md) is influenced by environmental lighting, in a range from `0` to `1`. Setting this to `1` means that surrounding lighting has complete control over the appearance, while setting it to `0` means that the lighting has no effect. ### Property: BillboardGui.MaxDistance ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` This property controls how far from the camera the [BillboardGui](/docs/reference/engine/classes/BillboardGui.md) will be displayed before it stops rendering. A value of `0` or `inf` (default) means there is no limit and it will render infinitely far away. For [BillboardGuis](/docs/reference/engine/classes/BillboardGui.md) that appear outdoors, it's recommended that [MaxDistance](/docs/reference/engine/classes/BillboardGui.md) is high enough to ensure that the container's UI is sufficiently small on the screen when it appears or disappears, minimizing the sudden pop‑in/out effect. ### Property: BillboardGui.PlayerToHideFrom ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` Used by scripts to hide the [BillboardGui](/docs/reference/engine/classes/BillboardGui.md) from a specific player. To hide a [BillboardGui](/docs/reference/engine/classes/BillboardGui.md) from more than one player, place it into [StarterGui](/docs/reference/engine/classes/StarterGui.md) and use a script to set the [Enabled](/docs/reference/engine/classes/BillboardGui.md) property according to whether the [LocalPlayer](/docs/reference/engine/classes/Players.md) should be able to see it. The [Adornee](/docs/reference/engine/classes/BillboardGui.md) property can be used to attach the [BillboardGui](/docs/reference/engine/classes/BillboardGui.md) to a [BasePart](/docs/reference/engine/classes/BasePart.md) or [Attachment](/docs/reference/engine/classes/Attachment.md) in the [Workspace](/docs/reference/engine/classes/Workspace.md), instead of parenting it. ### Property: BillboardGui.Size ```json { "type": "UDim2", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` Controls the size that the [BillboardGui](/docs/reference/engine/classes/BillboardGui.md) will have on screen. Unlike [GuiObject.Size](/docs/reference/engine/classes/GuiObject.md), the [scale](/docs/en-us/ui/position-and-size.md#size) components of this property set the billboard's stud size in 3D space. ### Property: BillboardGui.SizeOffset ```json { "type": "Vector2", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` A 2D offset in size-relative units that acts like an anchor point. This can be used similarly to the [GuiObject.AnchorPoint](/docs/reference/engine/classes/GuiObject.md) property, but the values are different. Size Offset | Explanation | `0, 0` | The default in which UI will be anchored at its center. | | --- | --- | | `0.5, 0.5` | UI will anchor at the bottom left. | | `0.5, -0.5` | UI will anchor at the top left. | | `-0.5, 0.5` | UI will anchor at the top right. | | `-0.5, -0.5` | UI will anchor at the bottom right. | See also [StudsOffset](/docs/reference/engine/classes/BillboardGui.md), [StudsOffsetWorldSpace](/docs/reference/engine/classes/BillboardGui.md), [ExtentsOffset](/docs/reference/engine/classes/BillboardGui.md), and [ExtentsOffsetWorldSpace](/docs/reference/engine/classes/BillboardGui.md) which are offset properties that work in 3D space instead. ### Property: BillboardGui.StudsOffset ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` This property determines how the [BillboardGui](/docs/reference/engine/classes/BillboardGui.md) is offset from its [Adornee](/docs/reference/engine/classes/BillboardGui.md) in studs, relative to the [Camera](/docs/reference/engine/classes/Camera.md) orientation. See also [StudsOffsetWorldSpace](/docs/reference/engine/classes/BillboardGui.md) which works similarly except the offset orientation is relative to the global axes. ### Property: BillboardGui.StudsOffsetWorldSpace ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` This property determines how the [BillboardGui](/docs/reference/engine/classes/BillboardGui.md) is offset from its [Adornee](/docs/reference/engine/classes/BillboardGui.md) in studs, relative to the global axes. See also [StudsOffset](/docs/reference/engine/classes/BillboardGui.md) which works similarly except the offset orientation is relative to the [Camera](/docs/reference/engine/classes/Camera.md). ### Property: BillboardGui.DistanceLowerLimit ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` Determines the distance in studs at which the [BillboardGui](/docs/reference/engine/classes/BillboardGui.md) will stop scaling larger in size relative to the player's current camera, with a default of `0`. If the [CurrentDistance](/docs/reference/engine/classes/BillboardGui.md) of the [BillboardGui](/docs/reference/engine/classes/BillboardGui.md) is below this value, it will not scale any larger than it would at this distance. ### Property: BillboardGui.DistanceUpperLimit ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` Determines the distance in studs at which the [BillboardGui](/docs/reference/engine/classes/BillboardGui.md) will stop scaling smaller in size relative to the player's current camera. If the [CurrentDistance](/docs/reference/engine/classes/BillboardGui.md) of the [BillboardGui](/docs/reference/engine/classes/BillboardGui.md) is above this value, it will not scale any smaller than it would at this distance. This property is ignored if the value is less than `0`. The default value is `-1`, meaning the property is ignored by default. ## Inherited Members ### From [LayerCollector](/docs/reference/engine/classes/LayerCollector.md) - **Property `Enabled`** (`boolean`): Toggles the visibility of this LayerCollector. - **Property `ResetOnSpawn`** (`boolean`): Determines if the LayerCollector resets (deletes itself and - **Property `ZIndexBehavior`** (`ZIndexBehavior`): Controls how GuiObject.ZIndex behaves on all descendants of this - **Method `GetLayoutNodeTree(): Dictionary`**: *(deprecated)* ### From [GuiBase2d](/docs/reference/engine/classes/GuiBase2d.md) - **Property `AbsolutePosition`** (`Vector2`): Describes the actual screen position of a GuiBase2d element, in - **Property `AbsoluteRotation`** (`float`): Describes the actual screen rotation of a GuiBase2d element, in - **Property `AbsoluteSize`** (`Vector2`): Describes the actual screen size of a GuiBase2d element, in - **Property `AutoLocalize`** (`boolean`): When set to `true`, localization will be applied to this GuiBase2d - **Property `Localize`** (`boolean`): Automatically set to true when a localization table's *(deprecated, hidden)* - **Property `RootLocalizationTable`** (`LocalizationTable`): A reference to a LocalizationTable to be used to apply automated - **Property `SelectionBehaviorDown`** (`SelectionBehavior`): Customizes gamepad selection behavior in the down direction. - **Property `SelectionBehaviorLeft`** (`SelectionBehavior`): Customizes gamepad selection behavior in the left direction. - **Property `SelectionBehaviorRight`** (`SelectionBehavior`): Customizes gamepad selection behavior in the right direction. - **Property `SelectionBehaviorUp`** (`SelectionBehavior`): Customizes gamepad selection behavior in the up direction. - **Property `SelectionGroup`** (`boolean`): Allows customization of gamepad selection movement. - **Event `SelectionChanged`**: Fires when the gamepad selection moves to, leaves, or changes within the ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: BinaryStringValue last_updated: 2026-06-29T19:34:07Z inherits: - ValueBase - Instance - Object type: class memory_category: Instances summary: "An internal type of StringValue object, that stores a `BinaryString` value." --- # Class: BinaryStringValue > An internal type of [StringValue](/docs/reference/engine/classes/StringValue.md) object, that stores a `BinaryString` > value. ## Events ### Event: BinaryStringValue.Changed **Signature:** `BinaryStringValue.Changed(value: BinaryString)` Fires if the [BinaryStringValue.Value](/docs/reference/engine/classes/BinaryStringValue.md) of the [BinaryStringValue](/docs/reference/engine/classes/BinaryStringValue.md) is changed by the engine. In practice, this object is stored out of reach from normal scripts, so this event cannot be connected to. If a BinaryStringValue is created by a script, the engine will not do anything with it, so the event will never fire. Equivalent changed events exist for similar objects, such as [NumberValue](/docs/reference/engine/classes/NumberValue.md) and [StringValue](/docs/reference/engine/classes/StringValue.md), depending on what object type best suits the need. *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `value` | `BinaryString` | The new value after the change. | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: BindableEvent last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances summary: "An object which enables custom events through asynchronous one-way communication between scripts on the same side of the client-server boundary. Scripts firing a BindableEvent do not yield." --- # Class: BindableEvent > An object which enables custom events through asynchronous one-way > communication between scripts on the same side of the client-server boundary. > Scripts firing a [BindableEvent](/docs/reference/engine/classes/BindableEvent.md) do not yield. ## Description The **BindableEvent** object enables custom events through asynchronous one-way communication between scripts on the same side of the [client-server](/docs/en-us/projects/client-server.md) boundary. When you fire a [BindableEvent](/docs/reference/engine/classes/BindableEvent.md) through the [BindableEvent:Fire()](/docs/reference/engine/classes/BindableEvent.md) method, the firing script does **not** yield and the target function receives the passed arguments with certain [limitations](#argument-limitations). [BindableEvents](/docs/reference/engine/classes/BindableEvent.md) create threads of each connected function, so even if one firing errors, others continue. As an alternative for two-way communication between two scripts on the same side of the client-server boundary, consider [BindableFunction](/docs/reference/engine/classes/BindableFunction.md). As stated, [BindableEvents](/docs/reference/engine/classes/BindableEvent.md) do not allow for communication between the server and clients. If you are looking for this functionality, use a [RemoteEvent](/docs/reference/engine/classes/RemoteEvent.md) as outlined in [Remote Events and Callbacks](/docs/en-us/scripting/events/remote.md). See [Bindable events and callbacks](/docs/en-us/scripting/events/bindable.md) for code samples and further details on [BindableEvent](/docs/reference/engine/classes/BindableEvent.md). #### Parameter Limitations Any type of Roblox object such as an [Enum](/docs/reference/engine/datatypes/Enum.md), [Instance](/docs/reference/engine/classes/Instance.md), or others can be passed as a parameter when a [BindableEvent](/docs/reference/engine/classes/BindableEvent.md) is fired, as well as Luau types such as numbers, strings, and booleans, although you should carefully explore the [limitations](/docs/en-us/scripting/events/bindable.md#argument-limitations). ## Methods ### Method: BindableEvent:Fire **Signature:** `BindableEvent:Fire(arguments: Tuple): ()` Fires the [BindableEvent](/docs/reference/engine/classes/BindableEvent.md) which in turn fires the [Event](/docs/reference/engine/classes/BindableEvent.md) event. This method does not yield, even if no script has connected to the event, and even if a connected function yields. Any type of Roblox object such as an [Enum](/docs/reference/engine/datatypes/Enum.md), [Instance](/docs/reference/engine/classes/Instance.md), or others can be passed as a parameter to [Fire()](/docs/reference/engine/classes/BindableEvent.md), as well as Luau types such as numbers, strings, and booleans, although you should carefully explore the [limitations](/docs/en-us/scripting/events/bindable.md#argument-limitations). See [Bindable events and callbacks](/docs/en-us/scripting/events/bindable.md) for code samples and further details on [Fire()](/docs/reference/engine/classes/BindableEvent.md). *Security: None · Thread Safety: Safe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `arguments` | `Tuple` | | Values to pass to [Event](/docs/reference/engine/classes/BindableEvent.md) events connected to the same [BindableEvent](/docs/reference/engine/classes/BindableEvent.md). | **Returns:** `()` ## Events ### Event: BindableEvent.Event **Signature:** `BindableEvent.Event(arguments: Tuple)` Fires when any script calls the [Fire()](/docs/reference/engine/classes/BindableEvent.md) method on the same [BindableEvent](/docs/reference/engine/classes/BindableEvent.md) instance, using the same arguments as parameters. See [Bindable events and callbacks](/docs/en-us/scripting/events/bindable.md) for code samples and further details on [Event](/docs/reference/engine/classes/BindableEvent.md). *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `arguments` | `Tuple` | The parameters sent through [Fire()](/docs/reference/engine/classes/BindableEvent.md). | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: BindableFunction last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances summary: "An object which allows for synchronous two-way communication between scripts on the same side of the client-server boundary. Scripts invoking a BindableFunction yield until the corresponding callback is found." --- # Class: BindableFunction > An object which allows for synchronous two-way communication between scripts > on the same side of the client-server boundary. Scripts invoking a > [BindableFunction](/docs/reference/engine/classes/BindableFunction.md) yield until the corresponding callback is found. ## Description The **BindableFunction** object allows for synchronous two-way communication between scripts on the same side of the [client-server](/docs/en-us/projects/client-server.md) boundary. You can use it to define a custom callback function and invoke it manually by calling [BindableFunction:Invoke()](/docs/reference/engine/classes/BindableFunction.md). The code invoking the function **yields** until the corresponding callback is found, and the callback receives the arguments that you passed to [Invoke()](/docs/reference/engine/classes/BindableFunction.md). If the callback was never set, the script that invokes it will not resume execution. As an alternative for one-way communication between two scripts on the same side of the client-server boundary, consider [BindableEvent](/docs/reference/engine/classes/BindableEvent.md) which does **not** yield for a return. As stated, [BindableFunctions](/docs/reference/engine/classes/BindableFunction.md) do not allow for communication between the server and clients. If you are looking for this functionality, use a [RemoteFunction](/docs/reference/engine/classes/RemoteFunction.md) as outlined in [Remote events and callbacks](/docs/en-us/scripting/events/remote.md). See [Bindable events and callbacks](/docs/en-us/scripting/events/bindable.md) for code samples and further details on [BindableFunction](/docs/reference/engine/classes/BindableFunction.md). #### Parameter Limitations Any type of Roblox object such as an [Enum](/docs/reference/engine/datatypes/Enum.md), [Instance](/docs/reference/engine/classes/Instance.md), or others can be passed as a parameter when a [BindableFunction](/docs/reference/engine/classes/BindableFunction.md) is invoked, as well as Luau types such as numbers, strings, and booleans, although you should carefully explore the [limitations](/docs/en-us/scripting/events/bindable.md#argument-limitations). ## Methods ### Method: BindableFunction:Invoke **Signature:** `BindableFunction:Invoke(arguments: Tuple): Tuple` Invokes the [BindableFunction](/docs/reference/engine/classes/BindableFunction.md) which in turn calls the [OnInvoke](/docs/reference/engine/classes/BindableFunction.md) callback, returning any values returned by the callback. Invocations yield until the corresponding callback is found, and if the callback was never set, the script that invokes it will not resume execution. Any type of Roblox object such as an [Enum](/docs/reference/engine/datatypes/Enum.md), [Instance](/docs/reference/engine/classes/Instance.md), or others can be passed as a parameter to [Invoke()](/docs/reference/engine/classes/BindableFunction.md), as well as Luau types such as numbers, strings, and booleans, although you should carefully explore the [limitations](/docs/en-us/scripting/events/bindable.md#argument-limitations). Only one function can be bound to [Invoke()](/docs/reference/engine/classes/BindableFunction.md) at a time. If you assign multiple functions, only the last one assigned will be used. See [Bindable events and callbacks](/docs/en-us/scripting/events/bindable.md) for code samples and further details on [Invoke()](/docs/reference/engine/classes/BindableFunction.md). *Yields · Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `arguments` | `Tuple` | | Values to pass to the [OnInvoke](/docs/reference/engine/classes/BindableFunction.md) callback. | **Returns:** `Tuple` — Values returned from the [OnInvoke](/docs/reference/engine/classes/BindableFunction.md) callback. ## Callbacks ### Callback: BindableFunction.OnInvoke **Signature:** `BindableFunction.OnInvoke(arguments: Tuple): Tuple` This callback is called when the [BindableFunction](/docs/reference/engine/classes/BindableFunction.md) is invoked with [Invoke()](/docs/reference/engine/classes/BindableFunction.md). It can be set multiple times but cannot be called directly. Invocations will yield until this callback is found and, if it is never set, the script that invoked it will not resume execution. See [Bindable events and callbacks](/docs/en-us/scripting/events/bindable.md) for code samples and further details on [OnInvoke](/docs/reference/engine/classes/BindableFunction.md). *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `arguments` | `Tuple` | | The parameters sent through [Invoke()](/docs/reference/engine/classes/BindableFunction.md). | **Returns:** `Tuple` — Values returned by the callback function. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: BlockMesh last_updated: 2026-06-29T19:34:07Z inherits: - BevelMesh - DataModelMesh - Instance - Object type: class memory_category: BaseParts summary: "The BlockMesh object applies a 'brick' mesh to the BasePart it is parented to. It behaves identically to a SpecialMesh with SpecialMesh.MeshType set to 'Brick'." --- # Class: BlockMesh > The BlockMesh object applies a 'brick' mesh to the [BasePart](/docs/reference/engine/classes/BasePart.md) it is > parented to. It behaves identically to a [SpecialMesh](/docs/reference/engine/classes/SpecialMesh.md) with > [SpecialMesh.MeshType](/docs/reference/engine/classes/SpecialMesh.md) set to 'Brick'. ## Description The BlockMesh object applies a 'brick' mesh to the [BasePart](/docs/reference/engine/classes/BasePart.md) it is parented to. It behaves identically to a [SpecialMesh](/docs/reference/engine/classes/SpecialMesh.md) with [SpecialMesh.MeshType](/docs/reference/engine/classes/SpecialMesh.md) set to 'brick'. ## What does a BlockMesh do? A BlockMesh gives the [BasePart](/docs/reference/engine/classes/BasePart.md) it was applied to a brick shaped mesh. It is identical in appearance to a standard Roblox [Part](/docs/reference/engine/classes/Part.md). The dimensions of the mesh will scale linearly in all directions with [BasePart.Size](/docs/reference/engine/classes/BasePart.md), this means a part containing a BlockMesh can be resized the same way as any other part. The additional functionality a BlockMesh brings however, is the ability to set the [DataModelMesh.Scale](/docs/reference/engine/classes/DataModelMesh.md) and [DataModelMesh.Offset](/docs/reference/engine/classes/DataModelMesh.md) properties. These allow the position and dimensions of the mesh that is displayed to be changed without changing the [BasePart.Position](/docs/reference/engine/classes/BasePart.md) or [BasePart.Size](/docs/reference/engine/classes/BasePart.md) of the [BasePart](/docs/reference/engine/classes/BasePart.md) the mesh is parented to. Note as the [BlockMesh](/docs/reference/engine/classes/BlockMesh.md) object does not include a texture the [DataModelMesh.VertexColor](/docs/reference/engine/classes/DataModelMesh.md) property does not do anything. ## Code Samples **BlockMesh Instantiation** A simple demonstration of how a `BlockMesh` can be created and how the [DataModelMesh.Scale](/docs/reference/engine/classes/DataModelMesh.md) and [DataModelMesh.Offset](/docs/reference/engine/classes/DataModelMesh.md) properties can be used. ```lua local part = Instance.new("Part") part.Position = Vector3.new(0, 2, 0) part.Size = Vector3.new(5, 2, 5) part.Anchored = true local mesh = Instance.new("BlockMesh") mesh.Scale = Vector3.new(0.5, 0.5, 0.5) mesh.Offset = Vector3.new(0, 2, 0) mesh.Parent = part local adornment = Instance.new("SelectionBox") adornment.Adornee = part adornment.Parent = part part.Parent = workspace ``` ## Inherited Members ### From [DataModelMesh](/docs/reference/engine/classes/DataModelMesh.md) - **Property `Offset`** (`Vector3`): The Offset of a mesh determines the relative position from the - **Property `Scale`** (`Vector3`): The Scale of a mesh determines the size of the mesh relative to its - **Property `VertexColor`** (`Vector3`): Changes the hue of a mesh's texture, used with FileMesh.TextureId. ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: BloomEffect last_updated: 2026-06-29T19:34:07Z inherits: - PostEffect - Instance - Object type: class memory_category: Instances summary: "Simulates the camera viewing a very bright light." --- # Class: BloomEffect > Simulates the camera viewing a very bright light. ## Description The **BloomEffect** simulates the camera viewing a very bright light. It causes brighter colors to glow, similar to applying the neon [Material](/docs/reference/engine/classes/BasePart.md) to everything, including the [Sky](/docs/reference/engine/classes/Sky.md). Multiple **BloomEffect** objects can be applied at once and they will compose their effects together. Like other post-processing effects, **BloomEffect** will only work while [Enabled](/docs/reference/engine/classes/PostEffect.md) and when parented to [Lighting](/docs/reference/engine/classes/Lighting.md) or [Workspace.CurrentCamera](/docs/reference/engine/classes/Workspace.md). Also, it may render differently depending on your Studio settings (see the **Quality Level** settings in **Rendering** → **Performance**). For more details on this effect and others, see [Post-Processing Effects](/docs/en-us/environment/post-processing-effects.md). ## Properties ### Property: BloomEffect.Intensity ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Environment" ] } ``` Intensity determines how intensely the colors that bloom (determined by the [Threshold](/docs/reference/engine/classes/BloomEffect.md)) will additively blend with themselves. Higher values will produce brighter colors. ### Property: BloomEffect.Size ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Environment" ] } ``` Size determines the radius of the bloom effect in pixels in a similar manner to [BlurEffect.Size](/docs/reference/engine/classes/BlurEffect.md). Larger values create a wider bloom effect, and a value of 0 will disable the bleed (but not the color adjustment). ### Property: BloomEffect.Threshold ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Environment" ] } ``` Threshold determines how bright a color can be before it blooms. If set to 1, only pure white colors will bloom. If set to 0, all colors will bloom. ## Inherited Members ### From [PostEffect](/docs/reference/engine/classes/PostEffect.md) - **Property `Enabled`** (`boolean`): Toggles whether or not the PostEffect is enabled. ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: BlurEffect last_updated: 2026-06-29T19:34:07Z inherits: - PostEffect - Instance - Object type: class memory_category: Instances summary: "Applies a blur to the entire game world." --- # Class: BlurEffect > Applies a blur to the entire game world. ## Description The **BlurEffect** applies a Gaussian blur to the entire rendered game world. The strength of the blur is controlled by the [BlurEffect.Size](/docs/reference/engine/classes/BlurEffect.md). Only one **BlurEffect** can be applied at once (the instance with the greatest [Size](/docs/reference/engine/classes/BlurEffect.md) takes priority). Like other post-processing effects, **BlurEffect** will only work while [Enabled](/docs/reference/engine/classes/PostEffect.md) and when parented to [Lighting](/docs/reference/engine/classes/Lighting.md) or [Workspace.CurrentCamera](/docs/reference/engine/classes/Workspace.md). Also, it may render differently on low-end devices and/or depending on your Studio settings (see the **Quality Level** settings in **Rendering** → **Performance**). For more details on this effect and others, see [Post-Processing Effects](/docs/en-us/environment/post-processing-effects.md). ## Properties ### Property: BlurEffect.Size ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Environment" ] } ``` Size controls the blur radius, measured in pixels. The larger the size, the blurrier the screen will become. ## Inherited Members ### From [PostEffect](/docs/reference/engine/classes/PostEffect.md) - **Property `Enabled`** (`boolean`): Toggles whether or not the PostEffect is enabled. ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: BodyAngularVelocity last_updated: 2026-06-29T19:34:07Z inherits: - BodyMover - Instance - Object type: class memory_category: Instances tags: - Deprecated summary: "Applies a torque to maintain a constant angular velocity." --- # Class: BodyAngularVelocity > Applies a torque to maintain a constant angular velocity. ## Description The `BodyAngularVelocity` object applies a torque (rotational force) on an assembly such that it maintains a constant angular velocity as determined by its [AngularVelocity](/docs/reference/engine/classes/BodyAngularVelocity.md) property. This allows for the creation of assemblies that continually rotate. It is the rotational counterpart to a [BodyVelocity](/docs/reference/engine/classes/BodyVelocity.md). If you would like to maintain a constant angular displacement, use a [BodyGyro](/docs/reference/engine/classes/BodyGyro.md) instead. ## Properties ### Property: BodyAngularVelocity.AngularVelocity ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Goals", "capabilities": [ "Physics" ] } ``` This property is a [Vector3](/docs/reference/engine/datatypes/Vector3.md) which determines the goal angular velocity a [BodyAngularVelocity](/docs/reference/engine/classes/BodyAngularVelocity.md) should maintain through the exertion of torque. For this property, the direction of the vector is the axis of rotation. The magnitude is the angular velocity in **radians per second**. You can multiply a [Vector3](/docs/reference/engine/datatypes/Vector3.md) by [math.rad(360)](/docs/reference/engine/globals/math.md), or `2π`, in order to convert angular frequency (rotations per second) into the desired angular velocity (radians per second). ### Property: BodyAngularVelocity.MaxTorque ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Goals", "capabilities": [ "Physics" ] } ``` This property determines the limit of the torque that may be exerted on each world axis. If an assembly isn't moving, consider raising this value and also check that it is not [Anchored](/docs/reference/engine/classes/BasePart.md) or attached to another anchored assembly). See also [P](/docs/reference/engine/classes/BodyAngularVelocity.md) (power). ### Property: BodyAngularVelocity.P ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Goals", "capabilities": [ "Physics" ] } ``` This property determines how much power is used while applying torque in order to reach the goal [AngularVelocity](/docs/reference/engine/classes/BodyAngularVelocity.md). The higher this value, the more power will be used and the faster it will be used. ### Property: BodyAngularVelocity.angularvelocity ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Goals", "capabilities": [ "Physics" ] } ``` > **Deprecated:** This deprecated property is a variant of [BodyAngularVelocity.AngularVelocity](/docs/reference/engine/classes/BodyAngularVelocity.md) which should be used instead. ### Property: BodyAngularVelocity.maxTorque ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Goals", "capabilities": [ "Physics" ] } ``` > **Deprecated:** This deprecated property is a variant of [BodyAngularVelocity.MaxTorque](/docs/reference/engine/classes/BodyAngularVelocity.md) which should be used instead. The maxTorque property is a deprecated variant of [BodyAngularVelocity.MaxTorque](/docs/reference/engine/classes/BodyAngularVelocity.md) that lets you set how much force could be applied to each axis. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: BodyColors last_updated: 2026-06-29T19:34:07Z inherits: - CharacterAppearance - Instance - Object type: class memory_category: Instances summary: "For a Humanoid character, the BodyColors will specify the colors for the different body parts." --- # Class: BodyColors > For a Humanoid character, the BodyColors will specify the colors for the > different body parts. ## Description BodyColors is a utility object used by Roblox to load avatar body colors from the website. Avatars that are loaded from the website will automatically have a BodyColors object corresponding to said avatar's body color configuration. When parented inside of a character with a [Humanoid](/docs/reference/engine/classes/Humanoid.md), it will apply the colors to each specified limb. ## Properties ### Property: BodyColors.HeadColor ```json { "type": "BrickColor", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "AvatarAppearance" ] } ``` Sets the color of the head, as a [BrickColor](/docs/reference/engine/datatypes/BrickColor.md). Setting this will also set [BodyColors.HeadColor3](/docs/reference/engine/classes/BodyColors.md). ### Property: BodyColors.HeadColor3 ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "AvatarAppearance" ] } ``` Sets the color of the head, as a [Color3](/docs/reference/engine/datatypes/Color3.md). Setting this will also set HeadColor to the closest [BrickColor](/docs/reference/engine/datatypes/BrickColor.md). ### Property: BodyColors.LeftArmColor ```json { "type": "BrickColor", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "AvatarAppearance" ] } ``` Sets the color of the left arm, as a [BrickColor](/docs/reference/engine/datatypes/BrickColor.md). Setting this will also set BodyColors.LeftArmColor3 ### Property: BodyColors.LeftArmColor3 ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "AvatarAppearance" ] } ``` Sets the color of the left arm, as a [Color3](/docs/reference/engine/datatypes/Color3.md). Setting this will also set LeftArmColor to the closest [BrickColor](/docs/reference/engine/datatypes/BrickColor.md). ### Property: BodyColors.LeftLegColor ```json { "type": "BrickColor", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "AvatarAppearance" ] } ``` Sets the color of the left leg, as a [BrickColor](/docs/reference/engine/datatypes/BrickColor.md). Setting this will also set [BodyColors.LeftLegColor3](/docs/reference/engine/classes/BodyColors.md). ### Property: BodyColors.LeftLegColor3 ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "AvatarAppearance" ] } ``` Sets the color of the left leg, as a [Color3](/docs/reference/engine/datatypes/Color3.md). Setting this will also set LeftLegColor to the closest [BrickColor](/docs/reference/engine/datatypes/BrickColor.md). ### Property: BodyColors.RightArmColor ```json { "type": "BrickColor", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "AvatarAppearance" ] } ``` Sets the color of the right arm, as a [BrickColor](/docs/reference/engine/datatypes/BrickColor.md). Setting this will also set [BodyColors.RightArmColor3](/docs/reference/engine/classes/BodyColors.md). ### Property: BodyColors.RightArmColor3 ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "AvatarAppearance" ] } ``` Sets the color of the right arm, as a [Color3](/docs/reference/engine/datatypes/Color3.md). Setting this will also set RightArmColor to the closest [BrickColor](/docs/reference/engine/datatypes/BrickColor.md). ### Property: BodyColors.RightLegColor ```json { "type": "BrickColor", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "AvatarAppearance" ] } ``` Sets the color of the right leg, as a [BrickColor](/docs/reference/engine/datatypes/BrickColor.md). Setting this will also set [BodyColors.RightLegColor3](/docs/reference/engine/classes/BodyColors.md). ### Property: BodyColors.RightLegColor3 ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "AvatarAppearance" ] } ``` Sets the color of the right leg, as a [Color3](/docs/reference/engine/datatypes/Color3.md). Setting this will also set RightLegColor to the closest [BrickColor](/docs/reference/engine/datatypes/BrickColor.md). ### Property: BodyColors.TorsoColor ```json { "type": "BrickColor", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "AvatarAppearance" ] } ``` Sets the color of the torso, as a [BrickColor](/docs/reference/engine/datatypes/BrickColor.md). Setting this will also set [BodyColors.TorsoColor3](/docs/reference/engine/classes/BodyColors.md). ### Property: BodyColors.TorsoColor3 ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "AvatarAppearance" ] } ``` Sets the color of the torso, as a [Color3](/docs/reference/engine/datatypes/Color3.md). Setting this will also set TorsoColor to the closest [BrickColor](/docs/reference/engine/datatypes/BrickColor.md). ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: BodyForce last_updated: 2026-06-29T19:34:07Z inherits: - BodyMover - Instance - Object type: class memory_category: Instances tags: - Deprecated summary: "Applies a constant force to an object." --- # Class: BodyForce > Applies a constant force to an object. ## Description The `BodyForce` object applies (or exerts) a force on the assembly to which it is parented. If the magnitude of such a force is great enough, assemblies can begin to accelerate. The force is determined by the [Force](/docs/reference/engine/classes/BodyForce.md) property, and is defined on the three world axes. A `BodyForce` alone cannot apply a torque (it cannot cause the parent to rotate on its own). To apply a force at a specific point or apply forces relative to the orientation of the assembly, use a [BodyThrust](/docs/reference/engine/classes/BodyThrust.md) instead. ## Properties ### Property: BodyForce.Force ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Goals", "capabilities": [ "Physics" ] } ``` This property determines the magnitude of force exerted on each axis, relative to the world. ### Property: BodyForce.force ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Goals", "capabilities": [ "Physics" ] } ``` > **Deprecated:** This deprecated property is a variant of [BodyForce.Force](/docs/reference/engine/classes/BodyForce.md) which should be used instead. A deprecated variant of [BodyForce.Force](/docs/reference/engine/classes/BodyForce.md) that indicates the amount of force applied on each axis. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: BodyGyro last_updated: 2026-06-29T19:34:07Z inherits: - BodyMover - Instance - Object type: class memory_category: Instances tags: - Deprecated summary: "Applies a torque to maintain a constant orientation." --- # Class: BodyGyro > Applies a torque to maintain a constant orientation. ## Description The `BodyGyro` object applies a torque (rotational force) on an assembly such that it maintains a constant angular displacement, or orientation. This allows for the creation of assemblies that point in a certain direction, as if a real gyroscope were acting upon it. Essentially, it's the rotational counterpart to a [BodyPosition](/docs/reference/engine/classes/BodyPosition.md). If you would like to maintain a constant angular velocity, use a [BodyAngularVelocity](/docs/reference/engine/classes/BodyAngularVelocity.md) instead. The [CFrame](/docs/reference/engine/classes/BodyGyro.md) property controls the goal orientation. Only the angular components of the [CFrame](/docs/reference/engine/datatypes/CFrame.md) are used; position will make no difference. [MaxTorque](/docs/reference/engine/classes/BodyGyro.md) limits the amount of angular force that may be applied, [P](/docs/reference/engine/classes/BodyGyro.md) controls the power used in achieving the goal orientation, and [D](/docs/reference/engine/classes/BodyGyro.md) controls dampening behavior. ## Properties ### Property: BodyGyro.CFrame ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Goals", "capabilities": [ "Physics" ] } ``` This property (not to be confused with [BasePart.CFrame](/docs/reference/engine/classes/BasePart.md)) determines the target orientation towards which torque will be exerted. Since [BodyGyro](/docs/reference/engine/classes/BodyGyro.md) does not apply translational force, the translational/positional component of the [CFrame](/docs/reference/engine/datatypes/CFrame.md) is ignored. Consider using one of the following CFrame constructors in setting this property: [CFrame.fromAxisAngle()](/docs/reference/engine/datatypes/CFrame.md), [CFrame.fromEulerAnglesXYZ()](/docs/reference/engine/datatypes/CFrame.md), or [CFrame.fromEulerAnglesYXZ()](/docs/reference/engine/datatypes/CFrame.md). ### Property: BodyGyro.D ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Goals", "capabilities": [ "Physics" ] } ``` This property defines how much dampening will be applied to the torque used to reach the goal [CFrame](/docs/reference/engine/classes/BodyGyro.md). When the assembly approaches the goal orientation, it needs to decelerate, otherwise it will rotate past the goal and have to stop and re-accelerate back toward the goal. This often creates an undesirable "rubber‑banding" effect, avoided by applying dampening. The higher this value is set, the greater the dampening curve becomes, or the slower the assembly will approach the goal orientation. ### Property: BodyGyro.MaxTorque ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Goals", "capabilities": [ "Physics" ] } ``` This property determines the limit on the amount of torque that may be applied on each axis in reaching the goal orientation ([CFrame](/docs/reference/engine/classes/BodyGyro.md)). If an assembly isn't moving, consider increasing this value and also check that it is not [Anchored](/docs/reference/engine/classes/BasePart.md) or attached to any anchored assemblies. ### Property: BodyGyro.P ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Goals", "capabilities": [ "Physics" ] } ``` This property determines how much power is used while applying torque in order to reach the goal [CFrame](/docs/reference/engine/classes/BodyGyro.md). The higher this value, the more power will be used and the faster it will be used. ### Property: BodyGyro.cframe ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Goals", "capabilities": [ "Physics" ] } ``` > **Deprecated:** This property is a deprecated variant of [BodyGyro.CFrame](/docs/reference/engine/classes/BodyGyro.md) which should be used instead. ### Property: BodyGyro.maxTorque ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Goals", "capabilities": [ "Physics" ] } ``` > **Deprecated:** This property is a deprecated variant of [BodyGyro.MaxTorque](/docs/reference/engine/classes/BodyGyro.md) which should be used instead. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: BodyMover last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Deprecated summary: "Base class for objects that continually exert forces to assemblies." --- # Class: BodyMover > Base class for objects that continually exert forces to assemblies. ## Description `BodyMover` is the abstract base class for the set of legacy objects that exert forces to assemblies in different ways. In general, the subclasses of `BodyMover` are: - [BodyForce](/docs/reference/engine/classes/BodyForce.md) exerts a force relative to world coordinates. - [BodyPosition](/docs/reference/engine/classes/BodyPosition.md) exerts force to maintain a certain world position. - [BodyVelocity](/docs/reference/engine/classes/BodyVelocity.md) exerts force to maintain a certain velocity. - [BodyThrust](/docs/reference/engine/classes/BodyThrust.md) exerts a force relative to object coordinates which applies torque if positioned in a certain way. - [BodyGyro](/docs/reference/engine/classes/BodyGyro.md) exerts torque to maintain a certain orientation. - [BodyAngularVelocity](/docs/reference/engine/classes/BodyAngularVelocity.md) exerts torque to maintain a certain angular velocity. - [RocketPropulsion](/docs/reference/engine/classes/RocketPropulsion.md) exerts both translational and rotational forces to cause an assembly to track down another. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: BodyPartDescription last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances summary: "Describes the appearance of an avatar body part for the HumanoidDescription." --- # Class: BodyPartDescription > Describes the appearance of an avatar body part for the > [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). ## Description **BodyPartDescription** is an object that stores the description for an avatar's body parts, such as the [Color](/docs/reference/engine/classes/BodyPartDescription.md). It is meant to be placed underneath a [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) in order to work with [Humanoid:ApplyDescriptionAsync()](/docs/reference/engine/classes/Humanoid.md). ## Properties ### Property: BodyPartDescription.AssetId ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarAppearance" ] } ``` The asset ID that should be applied when applying this [BodyPartDescription](/docs/reference/engine/classes/BodyPartDescription.md). ### Property: BodyPartDescription.BodyPart ```json { "type": "BodyPart", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarAppearance" ] } ``` The type of body part. ### Property: BodyPartDescription.Color ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarAppearance" ] } ``` The [Color3](/docs/reference/engine/datatypes/Color3.md) for this body part. ### Property: BodyPartDescription.HeadShape ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarAppearance" ] } ``` **HeadShape** specifies a Classic Head shape identifier to apply to a compatible Dynamic Head. This property is only relevant when [BodyPart](/docs/reference/engine/classes/BodyPartDescription.md) is set to [Head](/docs/reference/engine/enums/BodyPart.md) and [AssetId](/docs/reference/engine/classes/BodyPartDescription.md) references a Dynamic Head that supports head shapes. #### Example ```lua local AvatarEditorService = game:GetService("AvatarEditorService") -- Get available head shapes for the user local headShapes = AvatarEditorService:GetHeadShapesAsync(userId) -- Create a BodyPartDescription for a Dynamic Head with a Classic shape local bodyPartDescription = Instance.new("BodyPartDescription") bodyPartDescription.BodyPart = Enum.BodyPart.Head bodyPartDescription.AssetId = dynamicHeadAssetId bodyPartDescription.HeadShape = headShapes[1] -- Apply the first owned head shape bodyPartDescription.Parent = humanoidDescription -- Apply the description humanoid:ApplyDescriptionAsync(humanoidDescription) ``` See also: - [AvatarEditorService:GetHeadShapesAsync()](/docs/reference/engine/classes/AvatarEditorService.md), which returns head shape identifiers that can be used with this property - [AssetId](/docs/reference/engine/classes/BodyPartDescription.md), which specifies the Dynamic Head asset to apply the shape to - [BodyPart](/docs/reference/engine/classes/BodyPartDescription.md), which must be set to [Head](/docs/reference/engine/enums/BodyPart.md) for this property to have effect - [Humanoid:ApplyDescriptionAsync()](/docs/reference/engine/classes/Humanoid.md), which applies the description containing this [BodyPartDescription](/docs/reference/engine/classes/BodyPartDescription.md) ### Property: BodyPartDescription.Instance ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarAppearance" ] } ``` A reference to the [Instance](/docs/reference/engine/classes/Instance.md) that should be applied when applying this [BodyPartDescription](/docs/reference/engine/classes/BodyPartDescription.md). This property can be used instead of [AssetId](/docs/reference/engine/classes/BodyPartDescription.md) to apply accessories without uploading them to the platform. This can reference either a body part with R15, **R15ArtistIntent** and **R6** folders, or only reference a single subfolder. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: BodyPosition last_updated: 2026-06-29T19:34:07Z inherits: - BodyMover - Instance - Object type: class memory_category: Instances tags: - Deprecated summary: "Applies a force to maintain a constant position." --- # Class: BodyPosition > Applies a force to maintain a constant position. ## Description The `BodyPosition` object applies a force on an assembly such that it will maintain a constant position in the world. The [Position](/docs/reference/engine/classes/BodyPosition.md) property, not to be confused with [BasePart.Position](/docs/reference/engine/classes/BasePart.md), controls the target world position. This is the translational counterpart to a [BodyGyro](/docs/reference/engine/classes/BodyGyro.md). If you need further control on a force applied to an object, consider using a [BodyForce](/docs/reference/engine/classes/BodyForce.md) or [BodyThrust](/docs/reference/engine/classes/BodyThrust.md) instead. The strength of the force applied by this object is controlled by several factors, namely the distance to the goal position: the force is stronger when farther away from the goal. This is amplified by [P](/docs/reference/engine/classes/BodyPosition.md) (power). The present velocity will also dampen the force applied by this object, and this is amplified by [D](/docs/reference/engine/classes/BodyPosition.md) (dampening). The resulting force is then capped by [MaxForce](/docs/reference/engine/classes/BodyPosition.md). Note the force applied on the assembly to achieve the goal position may vary on a per-axis basis. ## Properties ### Property: BodyPosition.D ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Goals", "capabilities": [ "Physics" ] } ``` This property determines how much dampening will be applied to the force used toward reaching the goal [Position](/docs/reference/engine/classes/BodyPosition.md). When the assembly approaches the goal position it needs to decelerate, otherwise it will move past the goal and have to stop and re-accelerate back toward the goal. This is often creates an undesirable "rubber‑banding" effect, avoided by applying dampening. The higher this value is set, the greater the dampening curve becomes, or the slower the assembly will approach the goal position. ### Property: BodyPosition.MaxForce ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Goals", "capabilities": [ "Physics" ] } ``` This property determines the limit on the amount of force that may be applied on each axis in reaching the goal [Position](/docs/reference/engine/classes/BodyPosition.md). If an assembly isn't moving, consider increasing this value and also check that it is not [Anchored](/docs/reference/engine/classes/BasePart.md) or attached to any anchored assemblies. ### Property: BodyPosition.P ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Goals", "capabilities": [ "Physics" ] } ``` This property determines how much power is used while applying force in order to reach the goal [Position](/docs/reference/engine/classes/BodyPosition.md). The higher this value, the more power will be used and the faster it will be used. The force the [BodyPosition](/docs/reference/engine/classes/BodyPosition.md) exerts increases as the difference between the assembly's current position and the goal position increases. This property is multiplied to this force to either amplify or diminish it. ### Property: BodyPosition.Position ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Goals", "capabilities": [ "Physics" ] } ``` This property determines the goal position towards which the [BodyPosition](/docs/reference/engine/classes/BodyPosition.md) will apply force. ### Property: BodyPosition.maxForce ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Goals", "capabilities": [ "Physics" ] } ``` > **Deprecated:** This property is a deprecated variant of [BodyPosition.MaxForce](/docs/reference/engine/classes/BodyPosition.md) which should be used instead. ### Property: BodyPosition.position ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Goals", "capabilities": [ "Physics" ] } ``` > **Deprecated:** This is a deprecated variant of [BodyPosition.Position](/docs/reference/engine/classes/BodyPosition.md) which should be used instead. ## Methods ### Method: BodyPosition:GetLastForce **Signature:** `BodyPosition:GetLastForce(): Vector3` This function returns the last force in the object. *Security: None · Thread Safety: Unsafe · Capabilities: Physics* **Returns:** `Vector3` ### Method: BodyPosition:lastForce **Signature:** `BodyPosition:lastForce(): Vector3` > **Deprecated:** This property has been superseded by [BodyPosition:GetLastForce()](/docs/reference/engine/classes/BodyPosition.md) which should be used in new work instead, The lastForce function returns the last force in the object. *Security: None · Thread Safety: Unsafe · Capabilities: Physics* **Returns:** `Vector3` ## Events ### Event: BodyPosition.ReachedTarget **Signature:** `BodyPosition.ReachedTarget()` Fired when the Parent of the BodyPosition reaches the desired [BodyPosition.Position](/docs/reference/engine/classes/BodyPosition.md) (within .1 studs). Once this event fires it will not fire again until [BodyPosition.Position](/docs/reference/engine/classes/BodyPosition.md) is updated. *Security: None · Capabilities: Physics* ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: BodyThrust last_updated: 2026-06-29T19:34:07Z inherits: - BodyMover - Instance - Object type: class memory_category: Instances tags: - Deprecated summary: "Applies a constant force to an object at a specific point." --- # Class: BodyThrust > Applies a constant force to an object at a specific point. ## Description The `BodyThrust` object applies a force relative to the assembly to which it is parented at a specific location. It behaves similar to a [BodyForce](/docs/reference/engine/classes/BodyForce.md) except that this object's force applies at a specific point ([Location](/docs/reference/engine/classes/BodyThrust.md)), allowing you to exert a torque (rotational force). To apply a force dynamically so that an assembly maintains a constant angular velocity, use a [BodyAngularVelocity](/docs/reference/engine/classes/BodyAngularVelocity.md) instead. To apply a force dynamically so that an assembly maintains a constant orientation (angular position), use a [BodyGyro](/docs/reference/engine/classes/BodyGyro.md). ## Properties ### Property: BodyThrust.Force ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Goals", "capabilities": [ "Physics" ] } ``` This property determines the amount of force exerted on each axis relative to the assembly. The force is exerted at the [Location](/docs/reference/engine/classes/BodyThrust.md) which is also relative to the assembly. ### Property: BodyThrust.Location ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Goals", "capabilities": [ "Physics" ] } ``` This property determines the relative position where the [Force](/docs/reference/engine/classes/BodyThrust.md) is exerted. This is the primary means for turning force into torque. ### Property: BodyThrust.force ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Goals", "capabilities": [ "Physics" ] } ``` > **Deprecated:** This property is a deprecated variant of [Force](/docs/reference/engine/classes/BodyThrust.md) which should be used instead. ### Property: BodyThrust.location ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Goals", "capabilities": [ "Physics" ] } ``` > **Deprecated:** This property is a deprecated variant of [BodyThrust.Location](/docs/reference/engine/classes/BodyThrust.md) which should be used instead. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: BodyVelocity last_updated: 2026-06-29T19:34:07Z inherits: - BodyMover - Instance - Object type: class memory_category: Instances tags: - Deprecated summary: "Applies a force to maintain a constant velocity." --- # Class: BodyVelocity > Applies a force to maintain a constant velocity. ## Description The `BodyVelocity` object applies a force on an assembly such that it will maintain a constant velocity. The [Velocity](/docs/reference/engine/classes/BodyVelocity.md) property, not to be confused with [BasePart.AssemblyLinearVelocity](/docs/reference/engine/classes/BasePart.md), controls the goal velocity. [BodyVelocity](/docs/reference/engine/classes/BodyVelocity.md) is the linear counterpart to [BodyAngularVelocity](/docs/reference/engine/classes/BodyAngularVelocity.md). If you need the assembly to move toward a goal position, use [BodyPosition](/docs/reference/engine/classes/BodyPosition.md) instead. If you need further control on a force applied to an object, consider using a [BodyForce](/docs/reference/engine/classes/BodyForce.md) or [BodyThrust](/docs/reference/engine/classes/BodyThrust.md) instead. The strength of the force applied by this object is controlled by several factors, namely the difference between the assembly's current velocity and the goal velocity. This is multiplied by [P](/docs/reference/engine/classes/BodyVelocity.md) (power) to either amplify or diminish it. The resulting force is then capped by [MaxForce](/docs/reference/engine/classes/BodyVelocity.md). ## Properties ### Property: BodyVelocity.MaxForce ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Goals", "capabilities": [ "Physics" ] } ``` This property determines the limit on the amount of force that may be applied on each axis in reaching the goal [Velocity](/docs/reference/engine/classes/BodyVelocity.md). If an assembly isn't moving, consider increasing this value and also check that it is not [Anchored](/docs/reference/engine/classes/BasePart.md) or attached to any anchored assemblies. ### Property: BodyVelocity.P ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Goals", "capabilities": [ "Physics" ] } ``` This property determines how much power is used while applying force in order to reach the goal [Velocity](/docs/reference/engine/classes/BodyVelocity.md). The higher this value, the more power will be used and the faster it will be used. The force the [BodyVelocity](/docs/reference/engine/classes/BodyVelocity.md) exerts increases as the difference between the assembly's current velocity and the goal velocity increases. This property is multiplied to this force to either amplify or diminish it. ### Property: BodyVelocity.Velocity ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Goals", "capabilities": [ "Physics" ] } ``` This property (not to be confused with [BasePart.Velocity](/docs/reference/engine/classes/BasePart.md)) determines the target velocity towards which force will be exerted. It is specified relative to the world, not the assembly. ### Property: BodyVelocity.maxForce ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Goals", "capabilities": [ "Physics" ] } ``` > **Deprecated:** This property is a deprecated variant of [BodyVelocity.MaxForce](/docs/reference/engine/classes/BodyVelocity.md) which should be used instead. ### Property: BodyVelocity.velocity ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Goals", "capabilities": [ "Physics" ] } ``` > **Deprecated:** This property is a deprecated variant of [BodyVelocity.Velocity](/docs/reference/engine/classes/BodyVelocity.md) which should be used instead. ## Methods ### Method: BodyVelocity:GetLastForce **Signature:** `BodyVelocity:GetLastForce(): Vector3` This method is not implemented and it will always return the `0` vector. You are advised to use [AlignPosition](/docs/reference/engine/classes/AlignPosition.md) instead. *Security: None · Thread Safety: Unsafe · Capabilities: Physics* **Returns:** `Vector3` ### Method: BodyVelocity:lastForce **Signature:** `BodyVelocity:lastForce(): Vector3` Returns the last force in the object. *Security: None · Thread Safety: Unsafe · Capabilities: Physics* **Returns:** `Vector3` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Bone last_updated: 2026-06-29T19:34:07Z inherits: - Attachment - Instance - Object type: class memory_category: Instances summary: "Bones are non-rendered objects that drive the movement of one or more parts for the purposes of animation, or creating clothing and characters." --- # Class: Bone > Bones are non-rendered objects that drive the movement of one or more parts > for the purposes of animation, or creating clothing and characters. ## Description Bones are non-rendered objects that drive the movement of one or more parts for the purposes of animation, or creating clothing and characters. Bones are part of a [Model](/docs/reference/engine/classes/Model.md) or [MeshPart](/docs/reference/engine/classes/MeshPart.md) object's skeletal **rig** that you typically access and animate through the [Animation Editor](/docs/en-us/animation/editor.md). Rigs are created during the modeling process in third-party software such as Blender or Maya. After importing the rigged model into Studio, you can add the model directly to your experience, or save and share the model as an asset. See [Rigging](/docs/en-us/art/modeling/rigging.md) for more details on creating and using rigged models. Note that you can parent [Bones](/docs/reference/engine/classes/Bone.md) under other [Bones](/docs/reference/engine/classes/Bone.md) and parts. When parenting a bone to another bone, the child bone's world position will be relative to the parent bone's position, and the hierarchy of parented [Bone](/docs/reference/engine/classes/Bone.md) objects can change the behavior of affected parts during posing or animation. ##### Relationship with Motor6D To support animations with older rigs using joints, such as [Motor6D](/docs/reference/engine/classes/Motor6D.md), you can use the [Bone.Transform](/docs/reference/engine/classes/Bone.md) property in the same way as [Motor6D.Transform](/docs/reference/engine/classes/Motor6D.md). Roblox uses the offset of the bones from the default pose to drive an animation, and bones are not replicated or serialized. ##### Bone.CFrame Bones inherit the [CFrame](/docs/reference/engine/classes/Attachment.md) property of [Attachments](/docs/reference/engine/classes/Attachment.md) which Roblox uses as the bone's reference position. The inherited [WorldCFrame](/docs/reference/engine/classes/Attachment.md) and other world properties return the initial un-transformed position. ## Properties ### Property: Bone.Transform ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Transform", "capabilities": [ "Basic", "Animation" ] } ``` **Transform** determines the current animated offset of the bone relative to its [CFrame](/docs/reference/engine/classes/Attachment.md). This property is set by Roblox when animations on skinned meshes are played, although it can be manipulated manually in a manner similar to [Motor6D.Transform](/docs/reference/engine/classes/Motor6D.md). See also: - [Motor6D.Transform](/docs/reference/engine/classes/Motor6D.md), a property which plays a similar role in character rig animation - [TransformedCFrame](/docs/reference/engine/classes/Bone.md) and [TransformedWorldCFrame](/docs/reference/engine/classes/Bone.md), whose values are partially determined by this property ### Property: Bone.TransformedWorldCFrame ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "Unsafe", "category": "Derived World Data", "capabilities": [ "Basic", "Animation" ] } ``` **TransformedWorldCFrame** describes the combined [CFrame](/docs/reference/engine/classes/Attachment.md) offset of the bone and the current animation offset ([Transform](/docs/reference/engine/classes/Bone.md)) in world space. See also: - [Transform](/docs/reference/engine/classes/Bone.md), a property which partially determines this property's value - [Bone.TransformedCFrame](/docs/reference/engine/classes/Bone.md), a local-space variant of this property ### Property: Bone.TransformedCFrame *(hidden)* ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Derived Data", "capabilities": [ "Basic", "Animation" ] } ``` **TransformedCFrame** describes the combined [CFrame](/docs/reference/engine/classes/Attachment.md) offset of the bone and the current animation offset ([Transform](/docs/reference/engine/classes/Bone.md)) in the bone's local space. See also: - [Transform](/docs/reference/engine/classes/Bone.md), a property which partially determines this property's value - [Bone.TransformedWorldCFrame](/docs/reference/engine/classes/Bone.md), a world-space variant of this property ## Inherited Members ### From [Attachment](/docs/reference/engine/classes/Attachment.md) - **Property `Axis`** (`Vector3`): Direction of the **X** axis of the attachment, represented as a unit - **Property `CFrame`** (`CFrame`): CFrame offset of the attachment. - **Property `Orientation`** (`Vector3`): Orientation of the attachment relative to the orientation of its parent. *(hidden)* - **Property `Position`** (`Vector3`): Positional offset of the attachment, relative to the position and *(hidden)* - **Property `Rotation`** (`Vector3`): Rotation of the attachment relative to the rotation of its parent. *(deprecated, hidden)* - **Property `SecondaryAxis`** (`Vector3`): Direction of the **Y** axis of the attachment, represented as a unit - **Property `Visible`** (`boolean`): Toggles the in-experience visibility of the attachment. - **Property `WorldAxis`** (`Vector3`): Direction of the **X** axis of the attachment relative to the world, - **Property `WorldCFrame`** (`CFrame`): The exact CFrame of the attachment in world space coordinates. - **Property `WorldOrientation`** (`Vector3`): Orientation of the attachment relative to the world rather than its own *(hidden)* - **Property `WorldPosition`** (`Vector3`): Position of the attachment relative to the world rather than its own *(hidden)* - **Property `WorldRotation`** (`Vector3`): Rotation of the attachment relative to the world rather than its own *(deprecated, hidden)* - **Property `WorldSecondaryAxis`** (`Vector3`): Direction of the **Y** axis of the attachment relative to the world, - **Method `GetAxis(): Vector3`**: Returns the value of the attachment's Axis. *(deprecated)* - **Method `GetConstraints(): List`**: Returns a list of Constraints connected to the - **Method `GetSecondaryAxis(): Vector3`**: Returns the value of the attachment's *(deprecated)* - **Method `SetAxis(axis: Vector3): ()`**: Sets the value of the attachment's Axis. *(deprecated)* - **Method `SetSecondaryAxis(axis: Vector3): ()`**: Sets the value of the attachment's *(deprecated)* ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: BoolValue last_updated: 2026-06-29T19:34:07Z inherits: - ValueBase - Instance - Object type: class memory_category: Instances summary: "A container object for a single boolean value." --- # Class: BoolValue > A container object for a single boolean value. ## Description Stores a single boolean value. The value can be used for many things, including to communicate between scripts. Like all [ValueBase](/docs/reference/engine/classes/ValueBase.md) objects, this single value is stored in the [Value](/docs/reference/engine/classes/BoolValue.md) property. The `Changed` event fires with the new value being stored in the object, instead of a string representing the property being changed. ## Properties ### Property: BoolValue.Value ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "simulationAccess": true } ``` Used to hold a boolean value. ## Events ### Event: BoolValue.Changed **Signature:** `BoolValue.Changed(value: boolean)` Fires whenever the [BoolValue.Value](/docs/reference/engine/classes/BoolValue.md) changes. It runs with the new value being stored in the argument object, instead of a string representing the property being changed. Listening for the `Changed` signal can be useful in games that use `BoolValues` to track switch or enabled states. *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `value` | `boolean` | The new value after the change. | **BoolValue.Changed** This example prints the BoolValue's new value each time it changes. ```lua local boolValue = script.Parent.BoolValue local function printValue(value) print(value) end boolValue.Changed:Connect(printValue) boolValue.Value = true ``` ### Event: BoolValue.changed **Signature:** `BoolValue.changed(value: boolean)` > **Deprecated:** This event is a deprecated variant of [BoolValue.Changed](/docs/reference/engine/classes/BoolValue.md) which should be used instead. *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `value` | `boolean` | | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: BoxHandleAdornment last_updated: 2026-06-29T19:34:07Z inherits: - HandleAdornment - PVAdornment - GuiBase3d - GuiBase - Instance - Object type: class memory_category: Instances summary: "A box-shaped handle that can be adorned to a BasePart." --- # Class: BoxHandleAdornment > A box-shaped handle that can be adorned to a [BasePart](/docs/reference/engine/classes/BasePart.md). ## Description `BoxHandleAdornment` is a box-shaped handle that can be adorned to a [BasePart](/docs/reference/engine/classes/BasePart.md). If parented to a player's [PlayerGui](/docs/reference/engine/classes/PlayerGui.md) or the [CoreGui](/docs/reference/engine/classes/CoreGui.md), handles can listen to input events for purposes such as making dragger tools. ## Properties ### Property: BoxHandleAdornment.Shading ```json { "type": "AdornShading", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic", "UI" ] } ``` ### Property: BoxHandleAdornment.Size ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic", "UI" ] } ``` The size of the adornment in studs. ## Inherited Members ### From [HandleAdornment](/docs/reference/engine/classes/HandleAdornment.md) - **Property `AdornCullingMode`** (`AdornCullingMode`): Determines whether to automatically cull the adornment. - **Property `AlwaysOnTop`** (`boolean`): Forces this adornment to render on top of all 3D objects in the workspace. - **Property `CFrame`** (`CFrame`): The position and rotation of the object relative to its - **Property `SizeRelativeOffset`** (`Vector3`): The positional offset of the adornment based on the adornee's - **Property `ZIndex`** (`int`): Determines the draw order of this HandleAdornment when - **Event `MouseButton1Down`**: Fires when a player presses down on their left mouse button while hovering - **Event `MouseButton1Up`**: Fires when a player releases their left mouse button while hovering over - **Event `MouseEnter`**: Fires when a player moves their mouse over the adornment. - **Event `MouseLeave`**: Fires when a player moves their mouse out of the adornment. ### From [PVAdornment](/docs/reference/engine/classes/PVAdornment.md) - **Property `Adornee`** (`PVInstance`): The PVInstance which this PVAdornment is attached to. ### From [GuiBase3d](/docs/reference/engine/classes/GuiBase3d.md) - **Property `Color`** (`BrickColor`): Sets the color of a GUI object. *(deprecated, hidden)* - **Property `Color3`** (`Color3`): Sets the color of this GuiBase3d object. - **Property `Transparency`** (`float`): Sets the transparency of this GuiBase3d object. - **Property `Visible`** (`boolean`): Determines whether this GuiBase3d object and its descendants will ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: BrickColorValue last_updated: 2026-06-29T19:34:07Z inherits: - ValueBase - Instance - Object type: class memory_category: Instances summary: "A container object for a single BrickColor value." --- # Class: BrickColorValue > A container object for a single BrickColor value. ## Description An instance which is used to store a BrickColor value. ## Properties ### Property: BrickColorValue.Value ```json { "type": "BrickColor", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "simulationAccess": true } ``` Used to hold a [BrickColor](/docs/reference/engine/datatypes/BrickColor.md) value. ## Events ### Event: BrickColorValue.Changed **Signature:** `BrickColorValue.Changed(value: BrickColor)` Fired whenever the [BrickColorValue.Value](/docs/reference/engine/classes/BrickColorValue.md) of the [BrickColorValue](/docs/reference/engine/classes/BrickColorValue.md) is changed. It will run with the new value being stored in the argument object, instead of a string representing the property being changed. This event, like other changed events, can be used to track when an BrickColorValue changes and to track the different values that it may change to. Equivalent changed events exist for similar objects, such as [NumberValue](/docs/reference/engine/classes/NumberValue.md) and [StringValue](/docs/reference/engine/classes/StringValue.md), depending on what object type best suits the need. *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `value` | `BrickColor` | The new value after the change. | **BrickColorValue.Changed** This example prints the BrickColorValue's new value each time it changes. ```lua local brickColorValue = script.Parent.BrickColorValue brickColorValue.Changed:Connect(print) brickColorValue.Value = BrickColor.new("Bright red") ``` ### Event: BrickColorValue.changed **Signature:** `BrickColorValue.changed(value: BrickColor)` > **Deprecated:** This event is a deprecated variant of [BrickColorValue.Changed](/docs/reference/engine/classes/BrickColorValue.md) which should be used instead. *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `value` | `BrickColor` | | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: BrowserService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated --- # Class: BrowserService ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: BubbleChatConfiguration last_updated: 2026-06-29T19:34:07Z inherits: - TextChatConfigurations - Instance - Object type: class memory_category: Instances tags: - NotCreatable summary: "Allows for customization of text chat bubbles through TextChatService." --- # Class: BubbleChatConfiguration > Allows for customization of text chat bubbles through [TextChatService](/docs/reference/engine/classes/TextChatService.md). ## Description Allows for customization of text chat bubbles through [TextChatService](/docs/reference/engine/classes/TextChatService.md). See [Bubble Chat](/docs/en-us/chat/bubble-chat.md) for implementation details. ## Properties ### Property: BubbleChatConfiguration.AdorneeName ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Chat" ] } ``` String name of the body part or [Attachment](/docs/reference/engine/classes/Attachment.md) that bubbles attach to; if multiple instances of the same name exist, the system attaches to the first instance found. Default is `"HumanoidRootPart"`. ### Property: BubbleChatConfiguration.BackgroundColor3 ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Chat" ] } ``` [Color3](/docs/reference/engine/datatypes/Color3.md) background color of bubbles. Default is [Color3.fromRGB(250, 250, 250)](/docs/reference/engine/datatypes/Color3.md). ### Property: BubbleChatConfiguration.BackgroundTransparency ```json { "type": "double", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Chat" ] } ``` Determines the background transparency of the default bubble chat as a number between 0 and 1. This value is multiplied with the user's [GuiService.PreferredTransparency](/docs/reference/engine/classes/GuiService.md) to create the effective background transparency used by the bubble chat, which may be more opaque than this value set here. Default value is 0.1. ### Property: BubbleChatConfiguration.BubbleDuration ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Chat" ] } ``` Time before a bubble fades out, in seconds. Default is `30`. ### Property: BubbleChatConfiguration.BubblesSpacing ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Chat" ] } ``` Vertical space between stacked bubbles, in pixels. Default is `6`. ### Property: BubbleChatConfiguration.Enabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Chat" ] } ``` Whether text chat bubbles are enabled. Default is `false`. ### Property: BubbleChatConfiguration.FontFace ```json { "type": "Datatype.Font", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Chat" ] } ``` [Font](/docs/reference/engine/datatypes/Font.md) of the bubble text. Similar to [BubbleChatConfiguration.Font](/docs/reference/engine/classes/BubbleChatConfiguration.md) but allows setting fonts that don't exist in [Font](/docs/reference/engine/enums/Font.md). ### Property: BubbleChatConfiguration.LocalPlayerStudsOffset ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Chat" ] } ``` If adorned to the local player, [Vector3](/docs/reference/engine/datatypes/Vector3.md) offset of bubbles from their adornee, in studs, relative to the camera orientation. ### Property: BubbleChatConfiguration.MaxBubbles ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Chat" ] } ``` Maximum number of text chat bubbles shown per user. If the number of bubbles exceeds this maximum, the system removes bubbles from the oldest one. Default is `3`. ### Property: BubbleChatConfiguration.MaxDistance ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Chat" ] } ``` Maximum distance from the camera that bubbles are shown. Default is `100`. ### Property: BubbleChatConfiguration.MinimizeDistance ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Chat" ] } ``` Distance from the camera when bubbles turn into a single bubble with an ellipsis (**⋯**) to indicate chatter. ### Property: BubbleChatConfiguration.TailVisible ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Chat" ] } ``` Determines if the tail at the bottom of the text chat bubbles is visible. Default is `True`. ### Property: BubbleChatConfiguration.TextColor3 ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Chat" ] } ``` [Color3](/docs/reference/engine/datatypes/Color3.md) color of bubble text. Default is [Color3.fromRGB(57, 59, 61)](/docs/reference/engine/datatypes/Color3.md). ### Property: BubbleChatConfiguration.TextSize ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Chat" ] } ``` Size of bubble text. Default is `16`. ### Property: BubbleChatConfiguration.VerticalStudsOffset ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Chat" ] } ``` Extra space between bubbles and their adornee, in studs. Default is `0`. ### Property: BubbleChatConfiguration.Font *(hidden)* ```json { "type": "Enum.Font", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Chat" ] } ``` [Font](/docs/reference/engine/datatypes/Font.md) of the bubble text. Default is [Font.GothamMedium](/docs/reference/engine/enums/Font.md). ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: BubbleChatMessageProperties last_updated: 2026-06-29T19:34:07Z inherits: - TextChatMessageProperties - Instance - Object type: class memory_category: Instances --- # Class: BubbleChatMessageProperties ## Properties ### Property: BubbleChatMessageProperties.BackgroundColor3 ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Chat" ] } ``` Background color of bubbles in [Color3](/docs/reference/engine/datatypes/Color3.md). ### Property: BubbleChatMessageProperties.BackgroundTransparency ```json { "type": "double", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Chat" ] } ``` Background transparency of bubbles. ### Property: BubbleChatMessageProperties.FontFace ```json { "type": "Datatype.Font", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Chat" ] } ``` Font of the bubble text in [Font](/docs/reference/engine/datatypes/Font.md). ### Property: BubbleChatMessageProperties.TailVisible ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Chat" ] } ``` ### Property: BubbleChatMessageProperties.TextColor3 ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Chat" ] } ``` Color of bubble text in [Color3](/docs/reference/engine/datatypes/Color3.md). ### Property: BubbleChatMessageProperties.TextSize ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Chat" ] } ``` Size of bubble text. ## Inherited Members ### From [TextChatMessageProperties](/docs/reference/engine/classes/TextChatMessageProperties.md) - **Property `PrefixText`** (`string`): The TextChatMessage.PrefixText to override. - **Property `Text`** (`string`): The TextChatMessage.Text to override. - **Property `Translation`** (`string`): The TextChatMessage.Translation to override. ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: BuoyancySensor last_updated: 2026-06-29T19:34:07Z inherits: - SensorBase - Instance - Object type: class memory_category: Instances summary: "A SensorBase that outputs data about how its BasePart is interacting with Terrain water." --- # Class: BuoyancySensor > A [SensorBase](/docs/reference/engine/classes/SensorBase.md) that outputs data about how its [BasePart](/docs/reference/engine/classes/BasePart.md) is > interacting with [Terrain](/docs/reference/engine/classes/Terrain.md) water. ## Description A [SensorBase](/docs/reference/engine/classes/SensorBase.md) that outputs data about how its [BasePart](/docs/reference/engine/classes/BasePart.md) is interacting with [Terrain](/docs/reference/engine/classes/Terrain.md) water, allowing you to determine if the part is or is not in water. ## Properties ### Property: BuoyancySensor.FullySubmerged ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Output", "capabilities": [ "Basic" ], "simulationAccess": true } ``` This property is true when the entirety of the [BasePart](/docs/reference/engine/classes/BasePart.md) is submerged in [Terrain](/docs/reference/engine/classes/Terrain.md) water with at least one voxel of water above it. ### Property: BuoyancySensor.TouchingSurface ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Output", "capabilities": [ "Basic" ], "simulationAccess": true } ``` This property is true when any position on the [BasePart](/docs/reference/engine/classes/BasePart.md) is touching [Terrain](/docs/reference/engine/classes/Terrain.md) water. The detection measurements are not exact and may use the bounding box of the part instead of its exact geometry. ## Inherited Members ### From [SensorBase](/docs/reference/engine/classes/SensorBase.md) - **Property `UpdateType`** (`SensorUpdateType`): Determines how the sensor will update its output data. - **Method `Sense(): ()`**: *(deprecated)* - **Event `OnSensorOutputChanged`**: ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: CFrameValue last_updated: 2026-06-29T19:34:07Z inherits: - ValueBase - Instance - Object type: class memory_category: Instances summary: "A container object for a single CFrame value." --- # Class: CFrameValue > A container object for a single CFrame value. ## Description A container object for a single [CFrame](/docs/reference/engine/datatypes/CFrame.md) value. ## Code Samples **Store the Camera's CFrame** This code sample creates a CFrameValue whose Value is set to the camera's current CFrame. This CFrame can be later recalled back into the camera's CFrame. ```lua -- Create a CFrame that stores the camera's current position/orientation local vSnapshot = Instance.new("CFrameValue") vSnapshot.Value = workspace.CurrentCamera.CFrame vSnapshot.Name = "Snapshot" vSnapshot.Parent = workspace -- Later, we can load the CFrame back into the camera workspace.CurrentCamera.CFrame = vSnapshot.Value ``` ## Properties ### Property: CFrameValue.Value ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "simulationAccess": true } ``` Used to hold a [CFrame](/docs/reference/engine/datatypes/CFrame.md) value. ## Events ### Event: CFrameValue.Changed **Signature:** `CFrameValue.Changed(value: CFrame)` Fired whenever the [CFrameValue.Value](/docs/reference/engine/classes/CFrameValue.md) of the [CFrameValue](/docs/reference/engine/classes/CFrameValue.md) is changed. It will run with the new value being stored in the argument object, instead of a string representing the property being changed. This event, like other changed events, can be used to track when an CFrameValue changes and to track the different values that it may change to. For instance, this even may be useful in games that rely on CFrameValues to track game object [CFrame](/docs/reference/engine/datatypes/CFrame.md) positions and movements. Equivalent changed events exist for similar objects, such as [NumberValue](/docs/reference/engine/classes/NumberValue.md) and [StringValue](/docs/reference/engine/classes/StringValue.md), depending on what object type best suits the need. *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `value` | `CFrame` | The new value after the change. | **CFrameValue.Changed** This example prints the CFrameValue's new value each time it changes. ```lua local cframeValue = script.Parent.CFrameValue cframeValue.Changed:Connect(print) cframeValue.Value = CFrame.new(1, 2, 3) ``` ### Event: CFrameValue.changed **Signature:** `CFrameValue.changed(value: CFrame)` > **Deprecated:** This event is a deprecated variant of [CFrameValue.Changed](/docs/reference/engine/classes/CFrameValue.md) which should be used instead. *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `value` | `CFrame` | | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: CacheableContentProvider last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated summary: "A variant of the ContentProvider that caches assets that have already been received. This service is not used directly, but it is used by the services that inherit from it." --- # Class: CacheableContentProvider > A variant of the [ContentProvider](/docs/reference/engine/classes/ContentProvider.md) that caches assets that have already > been received. This service is not used directly, but it is used by the > services that inherit from it. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Camera last_updated: 2026-06-29T19:34:07Z inherits: - PVInstance - Instance - Object type: class memory_category: Instances tags: - NotReplicated summary: "A class which defines a view of the 3D world." --- # Class: Camera > A class which defines a view of the 3D world. ## Description The `Camera` object defines a view of the 3D world. In a running experience, each client has its own `Camera` object which resides in that client's local [Workspace](/docs/reference/engine/classes/Workspace.md), accessible through the [Workspace.CurrentCamera](/docs/reference/engine/classes/Workspace.md) property. The most important camera properties are: - [CFrame](/docs/reference/engine/classes/Camera.md) which represents the position and orientation of the camera. - [CameraType](/docs/reference/engine/classes/Camera.md) which is read by the experience's camera scripts and determines how the camera should update each frame. - [CameraSubject](/docs/reference/engine/classes/Camera.md) which is read by the experience's camera scripts and determines what object the camera should follow. - [FieldOfView](/docs/reference/engine/classes/Camera.md) which represents the visible extent of the observable world. - [Focus](/docs/reference/engine/classes/Camera.md) which represents the point the camera is looking at. It's important this property is set, as certain visuals will be more detailed and will update more frequently depending on how close they are to the focus point. See [Customizing the Camera](/docs/en-us/workspace/camera.md) for more information on how to adjust and customize the camera's behavior. #### Storing Multiple Cameras Note that when changing [Workspace.CurrentCamera](/docs/reference/engine/classes/Workspace.md) to a new [Camera](/docs/reference/engine/classes/Camera.md), all other [Cameras](/docs/reference/engine/classes/Camera.md) directly descending from [Workspace](/docs/reference/engine/classes/Workspace.md) will be destroyed. If you need to store multiple cameras and swap between them on demand, it's recommended that you store them in a [Folder](/docs/reference/engine/classes/Folder.md) or [Model](/docs/reference/engine/classes/Model.md) under [Workspace](/docs/reference/engine/classes/Workspace.md), inside which they will remain even when [CurrentCamera](/docs/reference/engine/classes/Workspace.md) is changed. ## Properties ### Property: Camera.CameraSubject ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Camera", "capabilities": [ "Basic" ] } ``` `CameraSubject` accepts a variety of [Instances](/docs/reference/engine/classes/Instance.md). The default camera scripts respond differently to the available settings: - By default, the camera scripts follow the local character's [Humanoid](/docs/reference/engine/classes/Humanoid.md), factoring in the humanoid's current state and [Humanoid.CameraOffset](/docs/reference/engine/classes/Humanoid.md). - When set to a [BasePart](/docs/reference/engine/classes/BasePart.md), the camera scripts follow its position, with a vertical offset in the case of [VehicleSeats](/docs/reference/engine/classes/VehicleSeat.md). `CameraSubject` cannot be set to `nil`. Attempting to do so will revert it to its previous value. To restore `CameraSubject` to its default value, set it to the local character's [Humanoid](/docs/reference/engine/classes/Humanoid.md): ```lua local Players = game:GetService("Players") local Workspace = game:GetService("Workspace") local localPlayer = Players.LocalPlayer local camera = Workspace.CurrentCamera local function resetCameraSubject() if camera and localPlayer.Character then local humanoid = localPlayer.Character:FindFirstChildWhichIsA("Humanoid") if humanoid then camera.CameraSubject = humanoid end end end ``` ### Property: Camera.CameraType ```json { "type": "CameraType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Camera", "capabilities": [ "Basic" ] } ``` The default Roblox camera scripts have several built-in behaviors. Setting this property toggles between the various [CameraType](/docs/reference/engine/enums/CameraType.md) behaviors. Note that some camera types require a valid [CameraSubject](/docs/reference/engine/classes/Camera.md) to work correctly. The default camera scripts will not move or update the camera if `CameraType` is set to [CameraType.Scriptable](/docs/reference/engine/enums/CameraType.md). For more information on positioning and orienting the camera manually, see [CFrame](/docs/reference/engine/classes/Camera.md). For all `CameraType` settings **except** [CameraType.Scriptable](/docs/reference/engine/enums/CameraType.md), the [CameraSubject](/docs/reference/engine/classes/Camera.md) property represents the object whose position the camera's [Focus](/docs/reference/engine/classes/Camera.md) is set to. ### Property: Camera.CFrame ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ], "simulationAccess": true } ``` This property is the [CFrame](/docs/reference/engine/datatypes/CFrame.md) of the [Camera](/docs/reference/engine/classes/Camera.md), defining its position and orientation in the 3D world. Note that some transformations, such as the rotation of the head when using VR devices, are not reflected in this property, so you should use [GetRenderCFrame()](/docs/reference/engine/classes/Camera.md) to obtain the "true" [CFrame](/docs/reference/engine/datatypes/CFrame.md) of the camera. You can move the camera by setting this property. However, the default camera scripts also set it, so you should either: - Set the camera [CameraType](/docs/reference/engine/classes/Camera.md) to [CameraType.Scriptable](/docs/reference/engine/enums/CameraType.md) so that the default camera scripts will not update the camera's [CFrame](/docs/reference/engine/datatypes/CFrame.md). This method is simplest and recommended in most cases. - Completely replace the default camera scripts with alternatives. This approach is only recommended if you do not need any default camera functionality. The most intuitive way to position and orient the [Camera](/docs/reference/engine/classes/Camera.md) is by using the [CFrame.lookAt()](/docs/reference/engine/datatypes/CFrame.md) constructor. In the following example, the [Camera](/docs/reference/engine/classes/Camera.md) is positioned at [Vector3.new(0, 10, 0)](/docs/reference/engine/datatypes/Vector3.md) and is oriented to be looking towards [Vector3.new(10, 0, 0)](/docs/reference/engine/datatypes/Vector3.md). ```lua local Workspace = game:GetService("Workspace") local camera = Workspace.CurrentCamera camera.CameraType = Enum.CameraType.Scriptable local pos = Vector3.new(0, 10, 0) local lookAtPos = Vector3.new(10, 0, 0) Workspace.CurrentCamera.CFrame = CFrame.lookAt(pos, lookAtPos) ``` Although the camera can be placed in the manner demonstrated above, you may want to animate it to move smoothly from one [CFrame](/docs/reference/engine/datatypes/CFrame.md) to another. For this, you can either: - Set the camera's position/orientation every frame with [RunService:BindToRenderStep()](/docs/reference/engine/classes/RunService.md) and the [CFrame:Lerp()](/docs/reference/engine/datatypes/CFrame.md) method. - Create and play a [Tween](/docs/reference/engine/classes/Tween.md) that animates the position/orientation of the camera: ```lua local Players = game:GetService("Players") local TweenService = game:GetService("TweenService") local Workspace = game:GetService("Workspace") local camera = Workspace.CurrentCamera camera.CameraType = Enum.CameraType.Scriptable local player = Players.LocalPlayer local character = player.Character if not character or character.Parent == nil then character = player.CharacterAdded:Wait() end local pos = camera.CFrame * Vector3.new(0, 20, 0) local lookAtPos = character.PrimaryPart.Position local targetCFrame = CFrame.lookAt(pos, lookAtPos) local tween = TweenService:Create(camera, TweenInfo.new(2), {CFrame = targetCFrame}) tween:Play() ``` ### Property: Camera.DiagonalFieldOfView ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Camera", "capabilities": [ "Basic" ] } ``` Sets how many degrees in the diagonal direction (from one corner of the viewport to its opposite corner) the camera can view. See [FieldOfView](/docs/reference/engine/classes/Camera.md) for a more general explanation of field of view. Note that `DiagonalFieldOfView` represents the field of view that is visible by the [Camera](/docs/reference/engine/classes/Camera.md) rendering into the fullscreen area which may be occluded by notches or screen cutouts on some devices. See [ViewportSize](/docs/reference/engine/classes/Camera.md) for more information. ### Property: Camera.FieldOfView ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Camera", "capabilities": [ "Basic" ] } ``` The `FieldOfView` (FOV) property sets how many degrees in the vertical direction the camera can view. This property is clamped between `1` and `120` degrees and defaults at `70`. Very low or very high fields of view are not recommended as they can be disorientating to players. Note that uniform scaling is enforced, meaning the vertical and horizontal field of view are always related by the aspect ratio of the screen. Suggested uses for `FieldOfView` include: - Reducing FOV to give the impression of magnification, for example when using binoculars. - Increasing FOV when the player is "sprinting" to give the impression of a lack of control. Note that `FieldOfView` represents the field of view that is visible by the [Camera](/docs/reference/engine/classes/Camera.md) rendering into the fullscreen area which may be occluded by notches or screen cutouts on some devices. See [ViewportSize](/docs/reference/engine/classes/Camera.md) for more information. ### Property: Camera.FieldOfViewMode ```json { "type": "FieldOfViewMode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Camera", "capabilities": [ "Basic" ] } ``` The camera's [FieldOfView](/docs/reference/engine/classes/Camera.md) (FOV) must be updated to reflect [ViewportSize](/docs/reference/engine/classes/Camera.md) changes. The value of `FieldOfViewMode` determines which FOV value will be kept constant. For example, when this property is set to [FieldOfViewMode.Vertical](/docs/reference/engine/enums/FieldOfViewMode.md), the horizontal FOV is updated when the viewport is resized, but the vertical FOV is kept constant. If this property is set to [FieldOfViewMode.Diagonal](/docs/reference/engine/enums/FieldOfViewMode.md), both horizontal and vertical FOV might be changed to keep the diagonal FOV constant. ### Property: Camera.Focus ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ], "simulationAccess": true } ``` Certain graphical operations the engine performs, such as updating lighting, can take time or computational effort to complete. The camera's `Focus` property tells the engine which area in 3D space to prioritize when performing such operations. For example, dynamic lighting from objects such as [PointLights](/docs/reference/engine/classes/PointLight.md) may not render at distances far from the focus. The default Roblox camera scripts automatically set `Focus` to follow the [CameraSubject](/docs/reference/engine/classes/Camera.md) (usually a [Humanoid](/docs/reference/engine/classes/Humanoid.md)). However, `Focus` will **not** automatically update when [CameraType](/docs/reference/engine/classes/Camera.md) is set to [CameraType.Scriptable](/docs/reference/engine/enums/CameraType.md) or when the default camera scripts are not being used. In these cases, you should update `Focus` every frame, using [RunService:BindToRenderStep()](/docs/reference/engine/classes/RunService.md) method at the [RenderPriority.Camera](/docs/reference/engine/enums/RenderPriority.md) priority. `Focus` has no bearing on the position or orientation of the camera; see [CFrame](/docs/reference/engine/classes/Camera.md) for this. ### Property: Camera.HeadLocked ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` Toggles whether the camera will automatically track the head motion of a player using a VR device. When `true` (default), the engine combines [CFrame](/docs/reference/engine/classes/Camera.md) with the [UserCFrame](/docs/reference/engine/enums/UserCFrame.md) of the user's head to render the player's view with head tracking factored in. The view will be rendered at the following [CFrame](/docs/reference/engine/datatypes/CFrame.md): ```lua local UserInputService = game:GetService("UserInputService") local Workspace = game:GetService("Workspace") local camera = Workspace.CurrentCamera local headCFrame = UserInputService:GetUserCFrame(Enum.UserCFrame.Head) headCFrame = headCFrame.Rotation + headCFrame.Position * camera.HeadScale -- This will be equivalent to Camera:GetRenderCFrame() local renderCFrame = camera.CFrame * headCFrame ``` It is recommended to **not** disable this property for the following reasons: - Players may experience motion sickness if an equivalent head tracking solution is not added. - The Roblox Engine performs latency optimizations when `HeadLocked` is true. ##### See Also - [VRService:GetUserCFrame()](/docs/reference/engine/classes/VRService.md) which can be used to obtain the [CFrame](/docs/reference/engine/datatypes/CFrame.md) of the head. - [VRService:RecenterUserHeadCFrame()](/docs/reference/engine/classes/VRService.md) which is used to recenter the head to the current position and orientation of the VR device. - The [GetRenderCFrame()](/docs/reference/engine/classes/Camera.md) method which returns the [CFrame](/docs/reference/engine/classes/Camera.md) combined with the [CFrame](/docs/reference/engine/datatypes/CFrame.md) of the user's head. ### Property: Camera.HeadScale ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` `HeadScale` is the scale of the user's perspective of the world when using VR. The size of 1 stud in VR is `0.3 meters / HeadScale`, meaning that larger `HeadScale` values equate to the world looking smaller from the user's perspective when using VR devices. For example, a part that's 1 stud tall appears to be 0.6 meters tall to a VR player with a `HeadScale` of `0.5`. This property is automatically controlled by [VRService.AutomaticScaling](/docs/reference/engine/classes/VRService.md) to align the player's perspective with the size of their avatar. If you intend to control `HeadScale` yourself or use custom characters, toggle [VRService.AutomaticScaling](/docs/reference/engine/classes/VRService.md) to [VRScaling.Off](/docs/reference/engine/enums/VRScaling.md). This property should not be confused with [Humanoid.HeadScale](/docs/reference/engine/classes/Humanoid.md) which is a [NumberValue](/docs/reference/engine/classes/NumberValue.md) parented to a [Humanoid](/docs/reference/engine/classes/Humanoid.md) to control its scaling. ### Property: Camera.MaxAxisFieldOfView ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Camera", "capabilities": [ "Basic" ] } ``` The `MaxAxisFieldOfView` property sets how many degrees along the longest viewport axis the camera can view. When the longest axis is the vertical axis, this property will behave similar to the [FieldOfView](/docs/reference/engine/classes/Camera.md) property. This is generally the case when a device is in a portrait orientation. In a landscape orientation, the longest axis will be the horizontal axis; in this case, the property describes the horizontal field of view of the [Camera](/docs/reference/engine/classes/Camera.md). ### Property: Camera.NearPlaneZ ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` The `NearPlaneZ` property describes how far away the camera's near clipping plane is, in studs. The near clipping plane is a geometric plane that sits in front of the camera's [CFrame](/docs/reference/engine/classes/Camera.md). Anything between this plane and the camera will not render, creating a cutaway view when viewing objects at very short distances. The value of `NearPlaneZ` varies across different platforms and is currently always between `-0.1` and `-0.5`. ![Diagram showing how the NearPlaneZ clips (does not render) 3D content between the plane and the camera.](/assets/engine-api/classes/Camera/NearPlaneZ.jpg) ### Property: Camera.ViewportSize ```json { "type": "Vector2", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` `ViewportSize` returns the dimensions of the device safe area on the current screen. This area is a rectangle which includes the Roblox top bar area but does not include any device notches or screen cutouts. The units of `ViewportSize` are Roblox UI offset units which may be different from native display pixels. ![Mobile device screen with cutout showing device safe area.](../../../assets/engine-api/classes/Camera/DeviceSafeAreaVsFullscreen.png) As noted above, `ViewportSize` is not equal to the fullscreen area size on displays with cutouts or notches. To obtain the fullscreen area size on all displays, you can query the [AbsoluteSize](/docs/reference/engine/classes/ScreenGui.md) property of a [ScreenGui](/docs/reference/engine/classes/ScreenGui.md) with [ScreenInsets](/docs/reference/engine/classes/ScreenGui.md) set to [None](/docs/reference/engine/enums/ScreenInsets.md). See [ScreenInsets](/docs/reference/engine/enums/ScreenInsets.md) for a more information about how screen areas are defined. Finally, note that `ViewportSize` is not the actual viewport size the camera uses for rendering (the camera renders in the fullscreen area). Also, the [FieldOfView](/docs/reference/engine/classes/Camera.md) and [DiagonalFieldOfView](/docs/reference/engine/classes/Camera.md) properties are based on the fullscreen area, not `ViewportSize`. ##### Camera Updates Only the [Camera](/docs/reference/engine/classes/Camera.md) currently referred to by [Workspace.CurrentCamera](/docs/reference/engine/classes/Workspace.md) has its `ViewportSize` updated each frame during the [PreRender](/docs/reference/engine/classes/RunService.md) step. The `ViewportSize` of all other cameras in your experience won't be updated, including those used for [ViewportFrames](/docs/reference/engine/classes/ViewportFrame.md). ### Property: Camera.VRTiltAndRollEnabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` This property toggles whether to apply tilt and roll from the [CFrame](/docs/reference/engine/classes/Camera.md) property while the player is using a VR device. To prevent motion sickness, the horizon should remain level. Tilting and rolling the player's view while using a VR device can cause a disconnect between the player's physical space and the virtual space they are viewing. Changing the apparent downwards direction can cause players to lose balance or experience dizziness. For these reasons, it is generally advisable to leave this property disabled, unless you have extensively tested your experience for these effects. Even with tilt and roll enabled, you may want to ensure the player always has a stable reference frame, such as the interior of a vehicle or a floor that can help the player ground themselves in their physical space. ### Property: Camera.focus ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` > **Deprecated:** This property is a deprecated variant of [Focus](/docs/reference/engine/classes/Camera.md) which should be used instead. ### Property: Camera.CoordinateFrame *(hidden)* ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ], "simulationAccess": true } ``` > **Deprecated:** This item has been superseded by [Camera.CFrame](/docs/reference/engine/classes/Camera.md) which should be used in all new work. The old version of the [CFrame](/docs/reference/engine/classes/Camera.md) property which functions identically to it. This item should be used in a [LocalScript](/docs/reference/engine/classes/LocalScript.md) in order to work as expected online. ## Methods ### Method: Camera:GetPartsObscuringTarget **Signature:** `Camera:GetPartsObscuringTarget(castPoints: Array, ignoreList: Instances): Instances` This method returns an array of [BaseParts](/docs/reference/engine/classes/BasePart.md) that are obscuring the lines of sight between the camera's [CFrame](/docs/reference/engine/classes/Camera.md) and [Vector3](/docs/reference/engine/datatypes/Vector3.md) positions in the `castPoints` array. Any [Instances](/docs/reference/engine/classes/Instance.md) included in the `ignoreList` array will be ignored, along with their descendants. The `castPoints` parameter is given as an array of [Vector3](/docs/reference/engine/datatypes/Vector3.md) positions. Note that the array of [BaseParts](/docs/reference/engine/classes/BasePart.md) returned is in an arbitrary order, and no additional raycast data is provided. If you need data such as hit position, hit material, or surface normal, you should opt for the [WorldRoot:Raycast()](/docs/reference/engine/classes/WorldRoot.md) method. ```lua local Workspace = game:GetService("Workspace") local camera = Workspace.CurrentCamera local castPoints = { Vector3.new(0, 10, 0), Vector3.new(0, 15, 0) } local ignoreList = {} local partsObscuringTarget = camera:GetPartsObscuringTarget(castPoints, ignoreList) ``` If [Terrain](/docs/reference/engine/classes/Terrain.md) obscures a cast point, [BaseParts](/docs/reference/engine/classes/BasePart.md) obscuring the cast point between the obscuring [Terrain](/docs/reference/engine/classes/Terrain.md) and the cast point will not be returned. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `castPoints` | `Array` | | An array of [Vector3](/docs/reference/engine/datatypes/Vector3.md) positions of cast points. | | `ignoreList` | `Instances` | | An array of [Instances](/docs/reference/engine/classes/Instance.md) that should be ignored, along with their descendants. | **Returns:** `Instances` — An array of [BaseParts](/docs/reference/engine/classes/BasePart.md) that obscure the lines of sight between the camera's [CFrame](/docs/reference/engine/classes/Camera.md) and the `castPoints`. ### Method: Camera:GetRenderCFrame **Signature:** `Camera:GetRenderCFrame(): CFrame` This method returns the actual [CFrame](/docs/reference/engine/datatypes/CFrame.md) of the [Camera](/docs/reference/engine/classes/Camera.md) as it is rendered, including the impact of VR (VR head transformations are not applied to the [CFrame](/docs/reference/engine/classes/Camera.md) property, so it is best practice to use [GetRenderCFrame()](/docs/reference/engine/classes/Camera.md) to obtain the "true" [CFrame](/docs/reference/engine/datatypes/CFrame.md) of a player's view). For example, when using VR, the [Camera](/docs/reference/engine/classes/Camera.md) is actually rendered at the following [CFrame](/docs/reference/engine/datatypes/CFrame.md): ```lua local UserInputService = game:GetService("UserInputService") local Workspace = game:GetService("Workspace") local camera = Workspace.CurrentCamera local headCFrame = UserInputService:GetUserCFrame(Enum.UserCFrame.Head) headCFrame = headCFrame.Rotation + headCFrame.Position * camera.HeadScale renderCFrame = camera.CFrame * headCFrame ``` The camera's render [CFrame](/docs/reference/engine/datatypes/CFrame.md) will only be changed to account for the head when the [HeadLocked](/docs/reference/engine/classes/Camera.md) property is true. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Returns:** `CFrame` — The [CFrame](/docs/reference/engine/datatypes/CFrame.md) the [Camera](/docs/reference/engine/classes/Camera.md) is being rendered at. ### Method: Camera:GetRoll **Signature:** `Camera:GetRoll(): float` This method returns, in radians, the current roll applied to the [Camera](/docs/reference/engine/classes/Camera.md) using [SetRoll()](/docs/reference/engine/classes/Camera.md). Roll is defined as rotation around the camera's Z-axis. This method only returns roll applied using the [SetRoll()](/docs/reference/engine/classes/Camera.md) method. Roll manually applied to the camera's [CFrame](/docs/reference/engine/classes/Camera.md) is not accounted for. To obtain the actual roll of the [Camera](/docs/reference/engine/classes/Camera.md), including roll manually applied, you can use the following snippet: ```lua local Workspace = game:GetService("Workspace") local function getActualRoll() local camera = Workspace.CurrentCamera local trueUp = Vector3.new(0, 1, 0) local cameraUp = camera:GetRenderCFrame().upVector return math.acos(trueUp:Dot(cameraUp)) end ``` *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Returns:** `float` — The current roll applied by [SetRoll()](/docs/reference/engine/classes/Camera.md), in radians. **Camera:GetRoll** This example, when used in a [LocalScript](/docs/reference/engine/classes/LocalScript.md), will retrieve the current roll of the camera in degrees. Then, if the camera roll angle is not 20 degrees, the camera roll angle is set to 20 degrees. ```lua local currentRoll = math.deg(workspace.CurrentCamera:GetRoll()) -- Gets the current roll of the camera in degrees. if currentRoll ~= 20 then workspace.CurrentCamera:SetRoll(math.rad(20)) -- If the camera isn't at 20 degrees roll, the roll is set to 20 degrees. end ``` ### Method: Camera:ScreenPointToRay **Signature:** `Camera:ScreenPointToRay(x: float, y: float, depth?: float): Ray` This method creates a unit [Ray](/docs/reference/engine/datatypes/Ray.md) from a 2D position on the screen (defined in pixels), accounting for the GUI inset. The [Ray](/docs/reference/engine/datatypes/Ray.md) originates from the [Vector3](/docs/reference/engine/datatypes/Vector3.md) equivalent of the 2D position in the world at the given depth (in studs) away from the [Camera](/docs/reference/engine/classes/Camera.md). As this method acknowledges the GUI inset, the offset applied to GUI elements (such as from the top bar) is accounted for. This means the screen position specified will start in the top left corner below the top bar. For an otherwise identical method that does not account for the GUI offset, use [ViewportPointToRay()](/docs/reference/engine/classes/Camera.md). As the [Ray](/docs/reference/engine/datatypes/Ray.md) created is a unit ray, it is only one stud long. To create a longer ray, you can do the following: ```lua local Workspace = game:GetService("Workspace") local camera = Workspace.CurrentCamera local length = 500 local unitRay = camera:ScreenPointToRay(100, 100) local extendedRay = Ray.new(unitRay.Origin, unitRay.Direction * length) ``` This method only works for the current [Workspace](/docs/reference/engine/classes/Workspace.md) camera. Other cameras, such as those you create for a [ViewportFrame](/docs/reference/engine/classes/ViewportFrame.md), have an initial viewport size of `(1, 1)` and are only updated after you set them to [Workspace.CurrentCamera](/docs/reference/engine/classes/Workspace.md). The mismatch in viewport size causes the camera to return a ray with an incorrect [Ray.Direction](/docs/reference/engine/datatypes/Ray.md). *Security: None · Thread Safety: Safe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `float` | | The position on the **X** axis, in pixels, of the screen point at which to originate the [Ray](/docs/reference/engine/datatypes/Ray.md). This position accounts for the GUI inset. | | `y` | `float` | | The position on the **Y** axis, in pixels, of the screen point at which to originate the [Ray](/docs/reference/engine/datatypes/Ray.md). This position accounts for the GUI inset. | | `depth` | `float` | `0` | The depth from the [Camera](/docs/reference/engine/classes/Camera.md), in studs, from which to offset the origin of the [Ray](/docs/reference/engine/datatypes/Ray.md). | **Returns:** `Ray` — A unit [Ray](/docs/reference/engine/datatypes/Ray.md), originating from the equivalent [Vector3](/docs/reference/engine/datatypes/Vector3.md) world position of the given screen coordinates at the given depth away from the [Camera](/docs/reference/engine/classes/Camera.md). This ray is orientated in the direction of the [Camera](/docs/reference/engine/classes/Camera.md). ### Method: Camera:SetRoll **Signature:** `Camera:SetRoll(rollAngle: float): ()` This method is outdated and no longer considered best practice. This method sets the current roll, in radians, of the [Camera](/docs/reference/engine/classes/Camera.md). The roll is applied after the [CFrame](/docs/reference/engine/classes/Camera.md) and represents the rotation around the camera's Z-axis. For example, the following would invert the [Camera](/docs/reference/engine/classes/Camera.md): ```lua local Workspace = game:GetService("Workspace") Workspace.CurrentCamera:SetRoll(math.pi) -- math.pi radians = 180 degrees ``` SetRoll has no effect on any roll applied using the [CFrame](/docs/reference/engine/classes/Camera.md) property. Roll applied using SetRoll is not reflected in the [CFrame](/docs/reference/engine/classes/Camera.md) property but is reflected in the [CFrame](/docs/reference/engine/datatypes/CFrame.md) returned by[GetRenderCFrame()](/docs/reference/engine/classes/Camera.md). This method can only be used when the [CameraType](/docs/reference/engine/classes/Camera.md) is set to `Scriptable`, regardless of whether the default camera scripts are being used. If it is used with any other [CameraType](/docs/reference/engine/classes/Camera.md) a warning is given in the output. Any roll applied using this method will be lost when the [CameraType](/docs/reference/engine/classes/Camera.md) is changed from `Scriptable`. To obtain the roll set using this method use [GetRoll()](/docs/reference/engine/classes/Camera.md). As this method is outdated, you are advised to instead apply roll to the [Camera](/docs/reference/engine/classes/Camera.md) using the [CFrame](/docs/reference/engine/classes/Camera.md) property. For example: ```lua local Workspace = game:GetService("Workspace") local currentCFrame = Workspace.CurrentCamera.CFrame local rollCFrame = CFrame.Angles(0, 0, roll) Workspace.CurrentCamera.CFrame = currentCFrame * rollCFrame ``` *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `rollAngle` | `float` | | The roll angle, in radians, to be applied to the [Camera](/docs/reference/engine/classes/Camera.md). | **Returns:** `()` ### Method: Camera:ViewportPointToRay **Signature:** `Camera:ViewportPointToRay(x: float, y: float, depth?: float): Ray` This method creates a unit [Ray](/docs/reference/engine/datatypes/Ray.md) from a 2D position in device safe viewport coordinates, defined in pixels. The ray originates from the [Vector3](/docs/reference/engine/datatypes/Vector3.md) equivalent of the 2D position in the world at the given depth (in studs) away from the [Camera](/docs/reference/engine/classes/Camera.md). As illustrated below, `(0, 0)` corresponds to the top‑left point of the Roblox top bar. This means that the input 2D position does **not** account for the [CoreUISafeInsets](/docs/reference/engine/enums/ScreenInsets.md) inset, but it does account for any [DeviceSafeInsets](/docs/reference/engine/enums/ScreenInsets.md). ![Diagram showing the origin of the device safe area viewport coordinate system.](../../../assets/engine-api/classes/Camera/ViewportPointToRayOrigin.png) Note that UI instances use a different coordinate system ([GuiObject.AbsolutePosition](/docs/reference/engine/classes/GuiObject.md) uses the [CoreUISafeInsets](/docs/reference/engine/enums/ScreenInsets.md) viewport coordinate system while this method uses the [DeviceSafeInsets](/docs/reference/engine/enums/ScreenInsets.md) viewport coordinate system). If you would like to specify position in core UI coordinates, please use [ScreenPointToRay()](/docs/reference/engine/classes/Camera.md). Also note that this method only works for the [Workspace.CurrentCamera](/docs/reference/engine/classes/Workspace.md) camera. Other cameras, such as those you create for a [ViewportFrame](/docs/reference/engine/classes/ViewportFrame.md), have an initial viewport size of `(1, 1)` and are only updated after you set them to [CurrentCamera](/docs/reference/engine/classes/Workspace.md). The mismatch in viewport size causes the camera to return a ray with an incorrect [Ray.Direction](/docs/reference/engine/datatypes/Ray.md). This method can be used in conjunction with the [ViewportSize](/docs/reference/engine/classes/Camera.md) property to create a ray from the centre of the screen, for example: ```lua local Workspace = game:GetService("Workspace") local camera = Workspace.CurrentCamera local viewportPoint = camera.ViewportSize / 2 local unitRay = camera:ViewportPointToRay(viewportPoint.X, viewportPoint.Y, 0) ``` As the [Ray](/docs/reference/engine/datatypes/Ray.md) created is a unit ray, it is only one stud long. To create a longer ray, you can do the following: ```lua local Workspace = game:GetService("Workspace") local camera = Workspace.CurrentCamera local length = 500 local unitRay = camera:ScreenPointToRay(100, 100) local extendedRay = Ray.new(unitRay.Origin, unitRay.Direction * length) ``` *Security: None · Thread Safety: Safe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `float` | | The position on the **X** axis, in pixels, of the viewport point at which to originate the [Ray](/docs/reference/engine/datatypes/Ray.md), in device safe area coordinates. | | `y` | `float` | | The position on the **Y** axis, in pixels, of the viewport point at which to originate the [Ray](/docs/reference/engine/datatypes/Ray.md), in device safe area coordinates. | | `depth` | `float` | `0` | The depth from the [Camera](/docs/reference/engine/classes/Camera.md), in studs, from which to offset the origin of the [Ray](/docs/reference/engine/datatypes/Ray.md). | **Returns:** `Ray` — A unit [Ray](/docs/reference/engine/datatypes/Ray.md), originating from the equivalent [Vector3](/docs/reference/engine/datatypes/Vector3.md) world position of the given viewport coordinates at the given depth away from the [Camera](/docs/reference/engine/classes/Camera.md). This ray is orientated in the direction of the [Camera](/docs/reference/engine/classes/Camera.md). ### Method: Camera:WorldToScreenPoint **Signature:** `Camera:WorldToScreenPoint(worldPoint: Vector3): Tuple` This method returns the screen location and depth of a [Vector3](/docs/reference/engine/datatypes/Vector3.md) `worldPoint` and whether this point is within the bounds of the screen. This method takes in account the current GUI inset, such as the space occupied by the top bar, meaning that the 2D position returned is in the same term as GUI positions and can be used to place GUI elements. For an otherwise identical method that ignores the GUI inset, see [WorldToViewportPoint()](/docs/reference/engine/classes/Camera.md). ```lua local Workspace = game:GetService("Workspace") local camera = Workspace.CurrentCamera local worldPoint = Vector3.new(0, 10, 0) local vector, onScreen = camera:WorldToScreenPoint(worldPoint) local screenPoint = Vector2.new(vector.X, vector.Y) local depth = vector.Z ``` Note this method does not perform any raycasting and the boolean indicating whether `worldPoint` is within the bounds of the screen will be `true` regardless of whether the point is obscured by [BaseParts](/docs/reference/engine/classes/BasePart.md) or [Terrain](/docs/reference/engine/classes/Terrain.md). *Security: None · Thread Safety: Safe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `worldPoint` | `Vector3` | | The [Vector3](/docs/reference/engine/datatypes/Vector3.md) world position. | **Returns:** `Tuple` — A tuple containing, in order: - A [Vector3](/docs/reference/engine/datatypes/Vector3.md) whose **X** and **Y** components represent the offset of the `worldPoint` from the top left corner of the screen, in pixels. The [Vector3](/docs/reference/engine/datatypes/Vector3.md) **Z** component represents the depth of the `worldPoint` from the screen (in studs). - A boolean indicating if the `worldPoint` is within the bounds of the screen. ### Method: Camera:WorldToViewportPoint **Signature:** `Camera:WorldToViewportPoint(worldPoint: Vector3): Tuple` This method returns the screen location and depth of a [Vector3](/docs/reference/engine/datatypes/Vector3.md) `worldPoint` and whether this point is within the bounds of the screen. This method does not take in account the current GUI inset, such as the space occupied by the top bar, meaning that the 2D position returned is taken from the top left corner of the viewport. Unless you are using [ScreenGui.IgnoreGuiInset](/docs/reference/engine/classes/ScreenGui.md), this position is not appropriate for placing GUI elements. For an otherwise identical method that accounts for the GUI inset, see [WorldToScreenPoint()](/docs/reference/engine/classes/Camera.md). ```lua local Workspace = game:GetService("Workspace") local camera = Workspace.CurrentCamera local worldPoint = Vector3.new(0, 10, 0) local vector, onScreen = camera:WorldToViewportPoint(worldPoint) local viewportPoint = Vector2.new(vector.X, vector.Y) local depth = vector.Z ``` Note this method does not perform any raycasting and the boolean indicating whether `worldPoint` is within the bounds of the screen will be `true` regardless of whether the point is obscured by [BaseParts](/docs/reference/engine/classes/BasePart.md) or [Terrain](/docs/reference/engine/classes/Terrain.md). *Security: None · Thread Safety: Safe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `worldPoint` | `Vector3` | | The [Vector3](/docs/reference/engine/datatypes/Vector3.md) world position. | **Returns:** `Tuple` — A tuple containing, in order: - A [Vector3](/docs/reference/engine/datatypes/Vector3.md) whose **X** and **Y** components represent the offset of the `worldPoint` from the top left corner of the viewport, in pixels. The [Vector3](/docs/reference/engine/datatypes/Vector3.md) **Z** component represents the depth of the `worldPoint` from the screen (in studs). - A boolean indicating if the `worldPoint` is within the bounds of the screen. ### Method: Camera:ZoomToExtents **Signature:** `Camera:ZoomToExtents(boundingBoxCFrame: CFrame, boundingBoxSize: Vector3): ()` This method adjusts the [CFrame](/docs/reference/engine/classes/Camera.md) so that a specified bounding box is fully visible within the camera's viewport, without cropping any part of the box. The bounding box is defined by its center and orientation (`boundingBoxCFrame`) and its size (`boundingBoxSize`). This can be used to focus the camera on a 3D object or model, such as when framing content in a [ViewportFrame](/docs/reference/engine/classes/ViewportFrame.md), taking screenshots, or smoothly transitioning the camera to highlight specific content. The method automatically accounts for the camera's field of view and viewport aspect ratio to ensure the entire bounding box fits. ```lua local Workspace = game:GetService("Workspace") local camera = Workspace.CurrentCamera local model = Workspace:FindFirstChild("MyModel") if model then local modelCFrame = model:GetModelCFrame() local extentsSize = model:GetExtentsSize() camera:ZoomToExtents(modelCFrame, extentsSize) end ``` *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `boundingBoxCFrame` | `CFrame` | | The [CFrame](/docs/reference/engine/datatypes/CFrame.md) representing the center and orientation of the bounding box to fit into the viewport. | | `boundingBoxSize` | `Vector3` | | The [Vector3](/docs/reference/engine/datatypes/Vector3.md) size of the bounding box to fit into the viewport. | **Returns:** `()` ### Method: Camera:GetLargestCutoffDistance **Signature:** `Camera:GetLargestCutoffDistance(ignoreList: Instances): float` This method is used by `PopperCam` in the default camera scripts to ensure obstructions do not come between the [Camera](/docs/reference/engine/classes/Camera.md) and its subject. This method will check all [BaseParts](/docs/reference/engine/classes/BasePart.md) and [Terrain](/docs/reference/engine/classes/Terrain.md) in the [Workspace](/docs/reference/engine/classes/Workspace.md) with the following exceptions: - Any [Instance](/docs/reference/engine/classes/Instance.md) specified in the `ignoreList` (including its descendants) will be ignored - [BaseParts](/docs/reference/engine/classes/BasePart.md) with [BasePart.CanCollide](/docs/reference/engine/classes/BasePart.md) set to false are ignored - [BaseParts](/docs/reference/engine/classes/BasePart.md) with a [BasePart.Transparency](/docs/reference/engine/classes/BasePart.md) greater than 0.95 will be ignored Water [Terrain](/docs/reference/engine/classes/Terrain.md) is ignored Note, as this method requires an `ignoreList` to run, you should pass an empty table when none is required. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `ignoreList` | `Instances` | | An array of [Instances](/docs/reference/engine/classes/Instance.md) to ignore. Descendants of these instances will also be ignored. | **Returns:** `float` — The distance, in studs, that the [Camera](/docs/reference/engine/classes/Camera.md) needs to be pushed towards its [Focus](/docs/reference/engine/classes/Camera.md) to ensure there are no obstructions between the [Focus](/docs/reference/engine/classes/Camera.md) and [CFrame](/docs/reference/engine/classes/Camera.md) of the [Camera](/docs/reference/engine/classes/Camera.md). ### Method: Camera:GetPanSpeed **Signature:** `Camera:GetPanSpeed(): float` > **Deprecated:** This method has been deprecated and no longer works. It should not be used in new work. This method is broken and should not be used. This method returns the current pan speed of the [Camera](/docs/reference/engine/classes/Camera.md). The pan speed of the [Camera](/docs/reference/engine/classes/Camera.md) describes the speed at which the [Camera](/docs/reference/engine/classes/Camera.md) is rotating around its [Focus](/docs/reference/engine/classes/Camera.md) around the **Y** axis. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Returns:** `float` — The speed at which the [Camera](/docs/reference/engine/classes/Camera.md) is rotating around its [Focus](/docs/reference/engine/classes/Camera.md) on the **Y** axis. ### Method: Camera:GetTiltSpeed **Signature:** `Camera:GetTiltSpeed(): float` > **Deprecated:** This method has been deprecated and no longer works. This method is broken and should not be used. This method returns the current tilt speed of the [Camera](/docs/reference/engine/classes/Camera.md). The tilt speed of the [Camera](/docs/reference/engine/classes/Camera.md) describes the speed at which the [Camera](/docs/reference/engine/classes/Camera.md) is rotating around its [Focus](/docs/reference/engine/classes/Camera.md) around the camera's **X** axis. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Returns:** `float` — The speed at which the [Camera](/docs/reference/engine/classes/Camera.md) is rotating around its [Focus](/docs/reference/engine/classes/Camera.md) around the camera's **X** axis. ### Method: Camera:Interpolate **Signature:** `Camera:Interpolate(endPos: CFrame, endFocus: CFrame, duration: float): ()` > **Deprecated:** This method has been deprecated. Instead use [TweenService](/docs/reference/engine/classes/TweenService.md) to smoothly animate the [Camera](/docs/reference/engine/classes/Camera.md), see the code snippets below for an example. This method tweens the [Camera](/docs/reference/engine/classes/Camera.md) in a linear fashion towards a new [CFrame](/docs/reference/engine/classes/Camera.md) and [Focus](/docs/reference/engine/classes/Camera.md) over a given duration, for example: ```lua local Workspace = game:GetService("Workspace") local camera = Workspace.CurrentCamera camera.CameraType = Enum.CameraType.Scriptable camera:Interpolate( CFrame.new(0, 10, 100), CFrame.new(0, 0, 100), 5 ) ``` Throughout the tween, the camera's [CFrame](/docs/reference/engine/classes/Camera.md) will be orientated towards the camera's [Focus](/docs/reference/engine/classes/Camera.md). When the tween has completed, the camera's [InterpolationFinished](/docs/reference/engine/classes/Camera.md) event will fire. If this method is called while the [Camera](/docs/reference/engine/classes/Camera.md) is already tweening, the older tween will be stopped (without firing [InterpolationFinished](/docs/reference/engine/classes/Camera.md)) and overridden by the new tween. Interpolate can only be used if the current [CameraType](/docs/reference/engine/classes/Camera.md) is `Scriptable`, regardless of whether the default camera scripts are being used. If it is used with any other [CameraType](/docs/reference/engine/classes/Camera.md) an error will be thrown. You are advised to use [TweenService](/docs/reference/engine/classes/TweenService.md) to tween the [Camera](/docs/reference/engine/classes/Camera.md) instead as it is more reliable and offers a variety of easing styles. See below for an example: ```lua local TweenService = game:GetService("TweenService") local Workspace = game:GetService("Workspace") local camera = Workspace.CurrentCamera camera.CameraType = Enum.CameraType.Scriptable local tween = TweenService:Create( camera, TweenInfo.new(5, Enum.EasingStyle.Quad, Enum.EasingDirection.Out), { CFrame = CFrame.new(0, 10, 100), Focus = CFrame.new(0, 0, 100) } ) tween:Play() ``` *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `endPos` | `CFrame` | | The [CFrame](/docs/reference/engine/datatypes/CFrame.md) for the [Camera](/docs/reference/engine/classes/Camera.md) to tween to. | | `endFocus` | `CFrame` | | The [CFrame](/docs/reference/engine/datatypes/CFrame.md) for the camera's [Focus](/docs/reference/engine/classes/Camera.md) to tween to. | | `duration` | `float` | | The duration, in seconds, of the tween. | **Returns:** `()` ### Method: Camera:PanUnits **Signature:** `Camera:PanUnits(units: int): ()` > **Deprecated:** This method was used for legacy camera controls and has since been deprecated. Do not use in new work. This method pans the [Camera](/docs/reference/engine/classes/Camera.md) around the [Focus](/docs/reference/engine/classes/Camera.md) in 45 degree increments around the **Y** axis. The rotation is applied to the camera's [CFrame](/docs/reference/engine/classes/Camera.md) property. This method pans the [Camera](/docs/reference/engine/classes/Camera.md) in 45 degree increments, for example: ```lua local Workspace = game:GetService("Workspace") Workspace.CurrentCamera:PanUnits(1) -- 45 degrees Workspace.CurrentCamera:PanUnits(-2) -- -90 degrees ``` PanUnits does not require the [CameraType](/docs/reference/engine/classes/Camera.md) to be `Scriptable`. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `units` | `int` | | The number of 45 degree increments by which to pan the [Camera](/docs/reference/engine/classes/Camera.md). | **Returns:** `()` ### Method: Camera:SetCameraPanMode **Signature:** `Camera:SetCameraPanMode(mode?: CameraPanMode): ()` > **Deprecated:** This method has been deprecated and should not be used in new work. This method sets the [CameraPanMode](/docs/reference/engine/enums/CameraPanMode.md) to be used by the [Camera](/docs/reference/engine/classes/Camera.md) on mobile devices. When the \*'EdgeBump' [CameraPanMode](/docs/reference/engine/enums/CameraPanMode.md) is used, swipe to pan is disabled and the edge bump camera controls are enabled. SetCameraPan mode has no effect on Windows or Mac users. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `mode` | `CameraPanMode` | `Classic` | The [CameraPanMode](/docs/reference/engine/enums/CameraPanMode.md) to set the [Camera](/docs/reference/engine/classes/Camera.md) to. | **Returns:** `()` ### Method: Camera:TiltUnits **Signature:** `Camera:TiltUnits(units: int): boolean` > **Deprecated:** This method was used for legacy camera controls and has been deprecated. Do not use in new work. This method tilts the [Camera](/docs/reference/engine/classes/Camera.md) by rotating it around the [Focus](/docs/reference/engine/classes/Camera.md) around the camera's **X** axis by a given multiple of 10 degrees. The rotation is applied to the camera's [CFrame](/docs/reference/engine/classes/Camera.md) property and is constrained between `-81.05` and `81.05` degrees. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `units` | `int` | | The number of 10 degree units by which to tilt the [Camera](/docs/reference/engine/classes/Camera.md). | **Returns:** `boolean` — Whether the [Camera](/docs/reference/engine/classes/Camera.md) tilt applied was constrained. ## Events ### Event: Camera.InterpolationFinished **Signature:** `Camera.InterpolationFinished()` This event fires when the [Camera](/docs/reference/engine/classes/Camera.md) has finished interpolating using the [Camera:Interpolate()](/docs/reference/engine/classes/Camera.md) method. It will not fire if a tween is interrupted due to [Camera:Interpolate()](/docs/reference/engine/classes/Camera.md) being called again. You are advised to use [TweenService](/docs/reference/engine/classes/TweenService.md) to animate the [Camera](/docs/reference/engine/classes/Camera.md) instead, as it is more reliable and provides more options for easing styles. *Security: None · Capabilities: Basic* ## Inherited Members ### From [PVInstance](/docs/reference/engine/classes/PVInstance.md) - **Property `Origin`** (`CFrame`): - **Property `Pivot Offset`** (`CFrame`): - **Method `GetPivot(): CFrame`**: Gets the pivot of a PVInstance. - **Method `PivotTo(targetCFrame: CFrame): ()`**: Transforms the PVInstance along with all of its descendant ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: CanvasGroup last_updated: 2026-06-29T19:34:07Z inherits: - GuiObject - GuiBase2d - GuiBase - Instance - Object type: class memory_category: Gui summary: "Blends descendants as a group with color/transparency." --- # Class: CanvasGroup > Blends descendants as a group with color/transparency. ## Description `CanvasGroup` renders descendants as a group with color and transparency applied to the render result. [UIComponent](/docs/reference/engine/classes/UIComponent.md) visual modifiers such as [UICorner](/docs/reference/engine/classes/UICorner.md) and [UIGradient](/docs/reference/engine/classes/UIGradient.md) under a `CanvasGroup` will also apply to the whole group. Note that `CanvasGroup` always has [ClipsDescendants](/docs/reference/engine/classes/GuiObject.md) set to `true` and all descendants will render within group's viewport. #### Important Notes - Descendants of `CanvasGroup` will be rendered as a flattened texture only when the ancestor [LayerCollector](/docs/reference/engine/classes/LayerCollector.md) has its [ZIndexBehavior](/docs/reference/engine/classes/LayerCollector.md) set to [ZIndexBehavior.Sibling](/docs/reference/engine/enums/ZIndexBehavior.md). - `CanvasGroup` consumes extra texture memory. The quality of the texture and total memory usage is limited by the [QualityLevel](/docs/reference/engine/enums/QualityLevel.md) of the client. When exceeding the memory cap, `CanvasGroup` will render as a blank texture. - It's recommended to use `CanvasGroup` with static sizes, otherwise a new texture would need to be created to accommodate new sizes. ## Properties ### Property: CanvasGroup.GroupColor3 ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "UI" ] } ``` Color tint that applies to all descendants. ### Property: CanvasGroup.GroupTransparency ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "UI" ] } ``` Transparency that applies to all descendants. ## Inherited Members ### From [GuiObject](/docs/reference/engine/classes/GuiObject.md) - **Property `Active`** (`boolean`): Determines whether this UI element sinks input. - **Property `AnchorPoint`** (`Vector2`): Determines the origin point of a GuiObject, relative to its - **Property `AutomaticSize`** (`AutomaticSize`): Determines whether resizing occurs based on child content. - **Property `BackgroundColor`** (`BrickColor`): Determines the color of the GuiObject background. *(deprecated, hidden)* - **Property `BackgroundColor3`** (`Color3`): Determines the GuiObject background color. - **Property `BackgroundTransparency`** (`float`): Determines the transparency of the GuiObject background and - **Property `BorderColor`** (`BrickColor`): Determines the color of the GuiObject border. *(deprecated, hidden)* - **Property `BorderColor3`** (`Color3`): Determines the color of the GuiObject border. - **Property `BorderMode`** (`BorderMode`): Determines in what manner the GuiObject border is laid out - **Property `BorderSizePixel`** (`int`): Determines the pixel width of the GuiObject border. - **Property `ClipsDescendants`** (`boolean`): Determines if descendant GuiObjects outside of the - **Property `Draggable`** (`boolean`): Determines whether a GuiObject (and its descendants) can be *(deprecated)* - **Property `GuiState`** (`GuiState`): Determines whether the player's mouse is being actively pressed on the - **Property `InputSink`** (`InputSink`): - **Property `Interactable`** (`boolean`): Determines whether the GuiButton can be interacted with or not, or - **Property `LayoutOrder`** (`int`): Controls the sort order of the GuiObject when used with a - **Property `NextSelectionDown`** (`GuiObject`): Sets the GuiObject which will be selected when the gamepad - **Property `NextSelectionLeft`** (`GuiObject`): Sets the GuiObject which will be selected when the gamepad - **Property `NextSelectionRight`** (`GuiObject`): Sets the GuiObject which will be selected when the gamepad - **Property `NextSelectionUp`** (`GuiObject`): Sets the GuiObject which will be selected when the gamepad - **Property `Position`** (`UDim2`): Determines the pixel and scalar position of the GuiObject. - **Property `Rotation`** (`float`): Determines the number of degrees by which the GuiObject is - **Property `Selectable`** (`boolean`): Determine whether the GuiObject can be selected by a gamepad. - **Property `SelectionImageObject`** (`GuiObject`): Overrides the default selection adornment used for gamepads. - **Property `SelectionOrder`** (`int`): The order of GuiObjects selected by the gamepad UI - **Property `Size`** (`UDim2`): Determines the pixel and scalar size of the GuiObject. - **Property `SizeConstraint`** (`SizeConstraint`): Sets the Size axes that the GuiObject will - **Property `Transparency`** (`float`): A mixed property of *(hidden)* - **Property `Visible`** (`boolean`): Determines whether the GuiObject and its descendants will be - **Property `ZIndex`** (`int`): Determines the order in which a GuiObject renders relative to - **Method `TweenPosition(endPosition: UDim2, easingDirection?: EasingDirection, easingStyle?: EasingStyle, time?: float, override?: boolean, callback?: Function): boolean`**: Smoothly moves a GUI to a new UDim2. - **Method `TweenSize(endSize: UDim2, easingDirection?: EasingDirection, easingStyle?: EasingStyle, time?: float, override?: boolean, callback?: Function): boolean`**: Smoothly resizes a GuiObject to a new UDim2. - **Method `TweenSizeAndPosition(endSize: UDim2, endPosition: UDim2, easingDirection?: EasingDirection, easingStyle?: EasingStyle, time?: float, override?: boolean, callback?: Function): boolean`**: Smoothly moves a GUI to a new size and position. - **Event `DragBegin`**: Fired when a player begins dragging the object. *(deprecated)* - **Event `DragStopped`**: Fired when a player stops dragging the object. *(deprecated)* - **Event `InputBegan`**: Fired when a user begins interacting via a Human-Computer Interface device - **Event `InputChanged`**: Fired when a user changes how they're interacting via a Human-Computer - **Event `InputEnded`**: Fired when a user stops interacting via a Human-Computer Interface device - **Event `MouseEnter`**: Fires when a user moves their mouse into a GUI element. - **Event `MouseLeave`**: Fires when a user moves their mouse out of a GUI element. - **Event `MouseMoved`**: Fires whenever a user moves their mouse while it is inside a GUI element. - **Event `MouseWheelBackward`**: Fires when a user scrolls their mouse wheel back when the mouse is over a - **Event `MouseWheelForward`**: Fires when a user scrolls their mouse wheel forward when the mouse is over - **Event `SelectionGained`**: Fired when the GuiObject is being focused on with the Gamepad selector. - **Event `SelectionLost`**: Fired when the Gamepad selector stops focusing on the GuiObject. - **Event `TouchLongPress`**: Fires when the player starts, continues and stops long-pressing the UI - **Event `TouchPan`**: Fires when the player moves their finger on the UI element. - **Event `TouchPinch`**: Fires when the player performs a pinch or pull gesture using two fingers - **Event `TouchRotate`**: Fires when the player performs a rotation gesture using two fingers on the - **Event `TouchSwipe`**: Fires when the player performs a swipe gesture on the UI element. - **Event `TouchTap`**: Fires when the player performs a tap gesture on the UI element. ### From [GuiBase2d](/docs/reference/engine/classes/GuiBase2d.md) - **Property `AbsolutePosition`** (`Vector2`): Describes the actual screen position of a GuiBase2d element, in - **Property `AbsoluteRotation`** (`float`): Describes the actual screen rotation of a GuiBase2d element, in - **Property `AbsoluteSize`** (`Vector2`): Describes the actual screen size of a GuiBase2d element, in - **Property `AutoLocalize`** (`boolean`): When set to `true`, localization will be applied to this GuiBase2d - **Property `Localize`** (`boolean`): Automatically set to true when a localization table's *(deprecated, hidden)* - **Property `RootLocalizationTable`** (`LocalizationTable`): A reference to a LocalizationTable to be used to apply automated - **Property `SelectionBehaviorDown`** (`SelectionBehavior`): Customizes gamepad selection behavior in the down direction. - **Property `SelectionBehaviorLeft`** (`SelectionBehavior`): Customizes gamepad selection behavior in the left direction. - **Property `SelectionBehaviorRight`** (`SelectionBehavior`): Customizes gamepad selection behavior in the right direction. - **Property `SelectionBehaviorUp`** (`SelectionBehavior`): Customizes gamepad selection behavior in the up direction. - **Property `SelectionGroup`** (`boolean`): Allows customization of gamepad selection movement. - **Event `SelectionChanged`**: Fires when the gamepad selection moves to, leaves, or changes within the ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Capture last_updated: 2026-06-29T19:34:07Z inherits: - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated summary: "A class which defines a piece of content, such as a screenshot or video, taken in-experience." --- # Class: Capture > A class which defines a piece of content, such as a screenshot or video, taken > in-experience. ## Properties ### Property: Capture.CaptureTime ```json { "type": "DateTime", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Capture" ] } ``` ### Property: Capture.CaptureType ```json { "type": "CaptureType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Capture" ] } ``` ### Property: Capture.FilePathString ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Capture" ] } ``` ### Property: Capture.LocalId ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Capture" ] } ``` ### Property: Capture.SourcePlaceId ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Capture" ] } ``` ### Property: Capture.SourceUniverseId ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Capture" ] } ``` ## Inherited Members ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: CaptureService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service summary: "A service which provides control over screenshot and video capture features." --- # Class: CaptureService > A service which provides control over screenshot and video capture features. ## Description `CaptureService` is a client-side service that allows developers to control how the screenshot and video capture feature integrates with their experiences. It can be used to include preset moments where a capture is automatically taken for a user, and that user can then save, share, or delete the capture. ## Methods ### Method: CaptureService:CaptureScreenshot **Signature:** `CaptureService:CaptureScreenshot(onCaptureReady: Function): ()` This method captures a screenshot for the user but does not immediately save it to their **Captures** gallery within the experience's main menu. Instead, a temporary `contentId` is created to identify the new capture. Note that any screenshots taken by this method will not be accessible via [ReadCapturesFromGalleryAsync()](/docs/reference/engine/classes/CaptureService.md) or be uploadable via [UploadCaptureAsync()](/docs/reference/engine/classes/CaptureService.md). The `onCaptureReady` callback can be used to prompt the user to save or share the screenshot: ```lua local CaptureService = game:GetService("CaptureService") -- Reference to an ImageLabel parent of the script containing this code local imageLabel = script.Parent local function onCaptureReady(contentId) imageLabel.Image = contentId end CaptureService:CaptureScreenshot(onCaptureReady) ``` *Security: None · Thread Safety: Unsafe · Capabilities: Capture* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `onCaptureReady` | `Function` | | A callback function that is called with the `contentId` of the new capture once it is ready. | **Returns:** `()` ### Method: CaptureService:CheckUploadCaptureStatusAsync **Signature:** `CaptureService:CheckUploadCaptureStatusAsync(token: string): Tuple` *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Capture* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `token` | `string` | | | **Returns:** `Tuple` ### Method: CaptureService:PromptCaptureGalleryPermissionAsync **Signature:** `CaptureService:PromptCaptureGalleryPermissionAsync(captureGalleryPermission: CaptureGalleryPermission): boolean` This client-side function prompts a user for permission to access their local captures. Once they have accepted or rejected the prompt, it returns a boolean representing their choice. This function is a necessary prerequisite to `ReadCapturesFromGalleryAsync` and `UploadCaptureAsync`, as both of these require gallery permissions to work. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Capture* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `captureGalleryPermission` | `CaptureGalleryPermission` | | An [CaptureGalleryPermission](/docs/reference/engine/enums/CaptureGalleryPermission.md) representing the type of access for which the user will be prompted. | **Returns:** `boolean` — A boolean representing whether or not the user has allowed access to their captures. **Prompt capture gallery permission** This code snippet demonstrates how to prompt a user for access to their captures gallery. ```lua local CaptureService = game:GetService("CaptureService") local function isGalleryAccessGranted() local accepted = CaptureService:PromptCaptureGalleryPermissionAsync(Enum.CaptureGalleryPermission.ReadAndUpload) return accepted end ``` ### Method: CaptureService:PromptSaveCapturesToGallery **Signature:** `CaptureService:PromptSaveCapturesToGallery(captures: Array, resultCallback: Function): ()` This method prompts the user to save the captures identified by the provided `contentIds` or [Capture](/docs/reference/engine/classes/Capture.md) objects to their **Captures** gallery within the experience's main menu. *Security: None · Thread Safety: Unsafe · Capabilities: Capture* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `captures` | `Array` | | An array of content IDs and/or [Capture](/docs/reference/engine/classes/Capture.md) objects. | | `resultCallback` | `Function` | | A callback function that will be invoked with a dictionary mapping each `contentId` and/or [Capture](/docs/reference/engine/classes/Capture.md) object to a boolean indicating if the user accepted saving that capture. | **Returns:** `()` ### Method: CaptureService:PromptShareCapture **Signature:** `CaptureService:PromptShareCapture(captureContent: Content, launchData: string, onAcceptedCallback: Function, onDeniedCallback: Function): ()` This method prompts the user to share the capture identified by the provided `contentId` or [Capture](/docs/reference/engine/classes/Capture.md) object using the native share sheet on their device. The capture is shared along with an invite link to the experience when supported. Not all devices support including both a screenshot or video and an invite link. The `launchData` will be available in the `launchData` field for users who join through the invite link. For users or devices who are not eligible to use share sheets, this method prompts them to download the capture instead. ```lua local CaptureService = game:GetService("CaptureService") local function onShareAccepted() print("Capture share was accepted!") end local function onShareDenied() print("Capture share was denied!") end local onCaptureReady = function(result, videoCapture) if videoCapture then local captureContent = Content.fromObject(videoCapture) CaptureService:PromptShareCapture(captureContent, "launchData" , onShareAccepted, onShareDenied) end end CaptureService:StartVideoCaptureAsync(onCaptureReady, {}) ``` *Security: None · Thread Safety: Unsafe · Capabilities: Capture* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `captureContent` | `Content` | | A [Content](/docs/reference/engine/datatypes/Content.md) containing a `contentId` or [Capture](/docs/reference/engine/classes/Capture.md) object. | | `launchData` | `string` | | An optional string to include as launch data in the invite link. | | `onAcceptedCallback` | `Function` | | An optional callback function invoked if the user accepts sharing. | | `onDeniedCallback` | `Function` | | An optional callback function invoked if the user denies sharing. | **Returns:** `()` ### Method: CaptureService:ReadCapturesFromGalleryAsync **Signature:** `CaptureService:ReadCapturesFromGalleryAsync(captureTypeFilters?: Array, readFromAllEligibleExperiences?: boolean): Tuple` This client-side function returns a paginated list of captures from the user's gallery as a [CapturesPages](/docs/reference/engine/classes/CapturesPages.md) object sorted reverse-chronologically, which can be used to iterate through the captures. The results can be filtered by capture type with the `captureTypeFilters` parameter. If this parameter isn't provided, the function returns all captures. If `readFromAllEligibleExperiences` is `true`, captures from all eligible experiences will be read. If `false`, only those from the current experience will be read. Additionally, captures taken with [CaptureScreenshot()](/docs/reference/engine/classes/CaptureService.md) and subsequently saved with [PromptSaveCapturesToGallery()](/docs/reference/engine/classes/CaptureService.md) will not be returned by this method. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Capture* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `captureTypeFilters` | `Array` | `{}` | An array of [CaptureType](/docs/reference/engine/enums/CaptureType.md). | | `readFromAllEligibleExperiences` | `boolean` | `false` | A boolean; default is `false`. | **Returns:** `Tuple` — Tuple of (result: [ReadCapturesFromGalleryResult](/docs/reference/engine/enums/ReadCapturesFromGalleryResult.md), capturesPages: [CapturesPages](/docs/reference/engine/classes/CapturesPages.md)) **Read captures from gallery and iterate over pages** This code snippet demonstrates how to fetch captures from a user gallery, iterate over the result pages, and return all captures. ```lua local CaptureService = game:GetService("CaptureService") local function readCapturesFromGallery() local allCaptures = {} local capturesPages local success, result = pcall(function() local readResult, pages = CaptureService:ReadCapturesFromGalleryAsync({}, false) if readResult == Enum.ReadCapturesFromGalleryResult.Success then capturesPages = pages end return readResult end) if not success or result ~= Enum.ReadCapturesFromGalleryResult.Success then -- Likely a permissions error, see CaptureService:PromptCaptureGalleryPermissionAsync() warn("Failed to fetch initial captures: " .. result.Name) return end -- Iterate through current page local currentPage = capturesPages:GetCurrentPage() for _, capture in currentPage do table.insert(allCaptures, capture) end -- Advance to next page until finished while not capturesPages.IsFinished do local advanceToNextPageSuccess, _ = pcall(function() capturesPages:AdvanceToNextPageAsync() end) if not advanceToNextPageSuccess then return end currentPage = capturesPages:GetCurrentPage() for _, capture in currentPage do table.insert(allCaptures, capture) end end return allCaptures end ``` ### Method: CaptureService:StartUploadCaptureAsync **Signature:** `CaptureService:StartUploadCaptureAsync(capture: Capture): Tuple` *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Capture* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `capture` | `Capture` | | | **Returns:** `Tuple` ### Method: CaptureService:StartVideoCaptureAsync **Signature:** `CaptureService:StartVideoCaptureAsync(onCaptureReady: Function, captureParams?: Dictionary): VideoCaptureStartedResult` This method initiates a video capture recording. The recording will continue until the [StopVideoCapture()](/docs/reference/engine/classes/CaptureService.md) method is called, or when 30 seconds have passed, whichever comes first. During the video recording, all user voices are muted. The `onCaptureReady` callback can be used to prompt the user to save or share the video capture. The `captureParams` parameter is currently non-operational. ```lua local CaptureService = game:GetService("CaptureService") local parent = script.Parent local onCaptureReady = function(result, videoCapture) if result ~= Enum.VideoCaptureResult.Success and result ~= Enum.VideoCaptureResult.TimeLimitReached or not videoCapture then print("Video recording failed to complete for reason:", result) else -- Prompt the user to save the video capture to their gallery CaptureService:PromptSaveCapturesToGallery({ videoCapture }, function() print("Capture saved") end) end end parent.Activated:Connect(function() local videoRecordingAllowedResult: Enum.VideoCaptureStartedResult = CaptureService:StartVideoCaptureAsync(onCaptureReady, {}) if videoRecordingAllowedResult ~= Enum.VideoCaptureStartedResult.Success then print("Video recording failed to start for reason:", videoRecordingAllowedResult) end end) ``` *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Capture* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `onCaptureReady` | `Function` | | A callback function that is called on video capture completion with a [VideoCaptureResult](/docs/reference/engine/enums/VideoCaptureResult.md) and, if successful, a [VideoCapture](/docs/reference/engine/classes/VideoCapture.md). | | `captureParams` | `Dictionary` | `nil` | | **Returns:** `VideoCaptureStartedResult` ### Method: CaptureService:StopVideoCapture **Signature:** `CaptureService:StopVideoCapture(): ()` This method ends a video capture that was started by the [StartVideoCaptureAsync()](/docs/reference/engine/classes/CaptureService.md) method. *Security: None · Thread Safety: Unsafe · Capabilities: Capture* **Returns:** `()` ### Method: CaptureService:TakeScreenshotCaptureAsync **Signature:** `CaptureService:TakeScreenshotCaptureAsync(onCaptureReady: Function, captureParams?: Dictionary): ()` This method initiates a screenshot capture. The `onCaptureReady` callback can be used to prompt the user to save or share the screenshot capture. Use the [UICaptureMode](/docs/reference/engine/enums/UICaptureMode.md) parameter in `captureParams` to specify whether UI elements should be in the screenshot. By default, this is set to [UICaptureMode.None](/docs/reference/engine/enums/UICaptureMode.md). ```lua local CaptureService = game:GetService("CaptureService") local parent = script.Parent local onCaptureReady = function(result, screenshotCapture) if result ~= Enum.ScreenshotCaptureResult.Success or not screenshotCapture then print("Screenshot failed to complete for reason:", result) else -- Prompt the user to save the screenshot capture to their gallery CaptureService:PromptSaveCapturesToGallery({ screenshotCapture }, function(results) for capture, result in pairs(results) do if result then print("Capture saved") end end end) end end parent.Activated:Connect(function() CaptureService:TakeScreenshotCaptureAsync(onCaptureReady, { UICaptureMode = Enum.UICaptureMode.All }) end) ``` *Security: None · Thread Safety: Unsafe · Capabilities: Capture* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `onCaptureReady` | `Function` | | A callback function that is called on screenshot capture completion with a [ScreenshotCaptureResult](/docs/reference/engine/enums/ScreenshotCaptureResult.md) and, if successful, a [ScreenshotCapture](/docs/reference/engine/classes/ScreenshotCapture.md). | | `captureParams` | `Dictionary` | `nil` | A dictionary that modifies capture behavior. | **Returns:** `()` ### Method: CaptureService:UploadCaptureAsync **Signature:** `CaptureService:UploadCaptureAsync(capture: Capture): Tuple` This client-side function uploads a capture, like one retrieved from `ReadCapturesFromGalleryAsync`, to the asset system. It returns a tuple of the result and the asset ID. Notes: - This function will only work properly if the Maturity and Compliance questionnaire has been filled out for the experience. - For video capture types, there is an upload limit of 20 videos per day per user. - This function can take up to several minutes to finish executing, and the user will need to remain in the experience. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Capture* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `capture` | `Capture` | | The [Capture](/docs/reference/engine/classes/Capture.md) to upload. | **Returns:** `Tuple` — Tuple of (result: [UploadCaptureResult](/docs/reference/engine/enums/UploadCaptureResult.md), assetId: `number`) **Upload capture to asset system** This code snippet demonstrates how to upload a capture to the asset system. ```lua local CaptureService = game:GetService("CaptureService") local function uploadCapture(capture: Capture) local success, result = pcall(function() local uploadCaptureResult, assetId = CaptureService:UploadCaptureAsync(capture) if uploadCaptureResult ~= Enum.UploadCaptureResult.Success then -- Handle failure return nil end return assetId end) if not success then -- Handle error return nil end return result end ``` ## Events ### Event: CaptureService.CaptureBegan **Signature:** `CaptureService.CaptureBegan(captureType: CaptureType)` This event fires right before a new capture is taken. It can be used to customize the capture experience, for example by hiding certain GUI elements. *Security: None · Capabilities: Capture* **Parameters:** | Name | Type | Description | |------|------|-------------| | `captureType` | `CaptureType` | | ### Event: CaptureService.CaptureEnded **Signature:** `CaptureService.CaptureEnded(captureType: CaptureType)` This event fires after a new capture completes. It can be used to restore any changes made when the [CaptureBegan](/docs/reference/engine/classes/CaptureService.md) event fired. *Security: None · Capabilities: Capture* **Parameters:** | Name | Type | Description | |------|------|-------------| | `captureType` | `CaptureType` | | ### Event: CaptureService.UserCaptureSaved **Signature:** `CaptureService.UserCaptureSaved(captureContentId: ContentId)` This event fires when the user saves a screenshot using the Roblox screenshot capture UI. It can be used for analytics or to prompt the user to share their capture. *Security: None · Capabilities: Capture* **Parameters:** | Name | Type | Description | |------|------|-------------| | `captureContentId` | `ContentId` | The `contentId` identifying the screenshot that the user saved. | ### Event: CaptureService.CaptureSaved **Signature:** `CaptureService.CaptureSaved(captureInfo: Dictionary)` > **Deprecated:** This event has been superseded by the [UserCaptureSaved](/docs/reference/engine/classes/CaptureService.md) event. *Security: None · Capabilities: Capture* **Parameters:** | Name | Type | Description | |------|------|-------------| | `captureInfo` | `Dictionary` | | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: CapturesPages last_updated: 2026-06-29T19:34:07Z inherits: - Pages - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated summary: "A special version of the Pages class returned by CaptureService.ReadCapturesFromGalleryAsync." --- # Class: CapturesPages > A special version of the [Pages](/docs/reference/engine/classes/Pages.md) class returned by > [CaptureService.ReadCapturesFromGalleryAsync](/docs/reference/engine/classes/CaptureService.md). ## Description A special version of the [Pages](/docs/reference/engine/classes/Pages.md) class returned by [CaptureService:ReadCapturesFromGalleryAsync()](/docs/reference/engine/classes/CaptureService.md). See the following code sample for reference on the returned data format. ## Code Samples **Read captures from gallery and iterate over pages** This code snippet demonstrates how to fetch captures from a user gallery, iterate over the result pages, and return all captures. ```lua local CaptureService = game:GetService("CaptureService") local function readCapturesFromGallery() local allCaptures = {} local capturesPages local success, result = pcall(function() local readResult, pages = CaptureService:ReadCapturesFromGalleryAsync({}, false) if readResult == Enum.ReadCapturesFromGalleryResult.Success then capturesPages = pages end return readResult end) if not success or result ~= Enum.ReadCapturesFromGalleryResult.Success then -- Likely a permissions error, see CaptureService:PromptCaptureGalleryPermissionAsync() warn("Failed to fetch initial captures: " .. result.Name) return end -- Iterate through current page local currentPage = capturesPages:GetCurrentPage() for _, capture in currentPage do table.insert(allCaptures, capture) end -- Advance to next page until finished while not capturesPages.IsFinished do local advanceToNextPageSuccess, _ = pcall(function() capturesPages:AdvanceToNextPageAsync() end) if not advanceToNextPageSuccess then return end currentPage = capturesPages:GetCurrentPage() for _, capture in currentPage do table.insert(allCaptures, capture) end end return allCaptures end ``` ## Inherited Members ### From [Pages](/docs/reference/engine/classes/Pages.md) - **Property `IsFinished`** (`boolean`): Whether or not the current page is the last page available. - **Method `AdvanceToNextPageAsync(): ()`**: Iterates to the next page in the pages object, if possible. - **Method `GetCurrentPage(): Array`**: Returns the items on the current page. The keys in the item are determined ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: CapturesViewConfiguration last_updated: 2026-06-29T19:34:07Z inherits: - BaseCoreGuiConfiguration - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated --- # Class: CapturesViewConfiguration ## Properties ### Property: CapturesViewConfiguration.Open ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "UI", "Capture" ] } ``` ## Inherited Members ### From [BaseCoreGuiConfiguration](/docs/reference/engine/classes/BaseCoreGuiConfiguration.md) - **Property `Enabled`** (`boolean`): ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: CatalogPages last_updated: 2026-06-29T19:34:07Z inherits: - Pages - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated --- # Class: CatalogPages ## Inherited Members ### From [Pages](/docs/reference/engine/classes/Pages.md) - **Property `IsFinished`** (`boolean`): Whether or not the current page is the last page available. - **Method `AdvanceToNextPageAsync(): ()`**: Iterates to the next page in the pages object, if possible. - **Method `GetCurrentPage(): Array`**: Returns the items on the current page. The keys in the item are determined ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ChangeHistoryService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service summary: "**Must** be used by plugins to communicate to Studio how to undo and redo the changes which they make to the experience." --- # Class: ChangeHistoryService > **Must** be used by plugins to communicate to Studio how to undo and redo the > changes which they make to the experience. ## Description Plugin developers **must** use [ChangeHistoryService](/docs/reference/engine/classes/ChangeHistoryService.md) to tell Studio how to undo and redo changes that their plugins make to experiences by recording. Before making changes, a plugin calls [ChangeHistoryService:TryBeginRecording()](/docs/reference/engine/classes/ChangeHistoryService.md), remembering the identifier it assigns, then after making changes, the Plugin calls [ChangeHistoryService:FinishRecording()](/docs/reference/engine/classes/ChangeHistoryService.md) to complete the recording. Plugins may also programmatically invoke an undo or redo through [ChangeHistoryService:Undo()](/docs/reference/engine/classes/ChangeHistoryService.md) or [ChangeHistoryService:Redo()](/docs/reference/engine/classes/ChangeHistoryService.md). [ChangeHistoryService](/docs/reference/engine/classes/ChangeHistoryService.md) is not enabled at runtime, so calling its methods in a running experience has no effect. ## Methods ### Method: ChangeHistoryService:FinishRecording **Signature:** `ChangeHistoryService:FinishRecording(identifier: string, operation: FinishRecordingOperation, finalOptions: Dictionary?): ()` Communicates to Studio that the identified recording is finished and to take the final operation to complete the recording. *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `identifier` | `string` | | Identifies the recording from the previous call to [TryBeginRecording()](/docs/reference/engine/classes/ChangeHistoryService.md). If the operation is [FinishRecordingOperation.Cancel](/docs/reference/engine/enums/FinishRecordingOperation.md), this value is ignored, and the recording is determined by context. | | `operation` | `FinishRecordingOperation` | | Specifies the operation to take. | | `finalOptions` | `Dictionary?` | | Optional table of values to pass to [OnFinishRecording](/docs/reference/engine/classes/ChangeHistoryService.md). | **Returns:** `()` **ChangeHistoryService:TryBeginRecording** To commit an undo/redo record, you need to first call [TryBeginRecording()](/docs/reference/engine/classes/ChangeHistoryService.md) followed by calling [FinishRecording()](/docs/reference/engine/classes/ChangeHistoryService.md). ```lua local ChangeHistoryService = game:GetService("ChangeHistoryService") local Selection = game:GetService("Selection") local toolbar = plugin:CreateToolbar("Example Plugin") local button = toolbar:CreateButton("Neon it up", "", "") button.Click:Connect(function() local parts = {} for _, part in pairs(Selection:Get()) do if part:IsA("BasePart") then parts[#parts + 1] = part end end if #parts < 1 then -- Nothing to do. return end local recording = ChangeHistoryService:TryBeginRecording("Set selection to neon") if not recording then -- Handle error here. This indidcates that your plugin began a previous -- recording and never completed it. You may only have one recording -- per plugin active at a time. return end for _, part in pairs(parts) do part.Material = Enum.Material.Neon end ChangeHistoryService:FinishRecording(recording, Enum.FinishRecordingOperation.Commit) end) ``` ### Method: ChangeHistoryService:GetCanRedo **Signature:** `ChangeHistoryService:GetCanRedo(): Tuple` Returns whether there are actions that can be redone, and, if there are, returns the last of them. *Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `Tuple` ### Method: ChangeHistoryService:GetCanUndo **Signature:** `ChangeHistoryService:GetCanUndo(): Tuple` Returns whether there are actions that can be undone, and, if there are, returns the last of them. *Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `Tuple` ### Method: ChangeHistoryService:IsRecordingInProgress **Signature:** `ChangeHistoryService:IsRecordingInProgress(identifier: string?): boolean` *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `identifier` | `string?` | | | **Returns:** `boolean` ### Method: ChangeHistoryService:Redo **Signature:** `ChangeHistoryService:Redo(): ()` Executes the last action that was undone. *Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `()` ### Method: ChangeHistoryService:ResetWaypoints **Signature:** `ChangeHistoryService:ResetWaypoints(): ()` Clears the history, causing all undo/redo waypoints to be removed. *Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `()` ### Method: ChangeHistoryService:SetEnabled **Signature:** `ChangeHistoryService:SetEnabled(state: boolean): ()` Sets whether or not the ChangeHistoryService is enabled. When set to false, the undo/redo list is cleared, and does not repopulate. When set to true again, the original list is not restored, but further operations append to the list once more *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `state` | `boolean` | | | **Returns:** `()` ### Method: ChangeHistoryService:SetWaypoint **Signature:** `ChangeHistoryService:SetWaypoint(name: string): ()` This method will be **deprecated soon** in favor of [TryBeginRecording()](/docs/reference/engine/classes/ChangeHistoryService.md). [ChangeHistoryService](/docs/reference/engine/classes/ChangeHistoryService.md) tracks plugin history as a stream of property changes. [SetWaypoint()](/docs/reference/engine/classes/ChangeHistoryService.md) creates a cut in that stream of property changes so that the undo and redo actions know where to stop. By convention, user-invoked actions in Studio **must** call [SetWaypoint()](/docs/reference/engine/classes/ChangeHistoryService.md) _after_ completing their set of changes to the experience. Calling it **before** a set of changes may clean up another misbehaving plugin which failed to set a waypoint, but it's a poor reason to justify such usage in your own plugin. *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name` | `string` | | | **Returns:** `()` **ChangeHistoryService:SetWaypoint** In order for the waypoints to work correctly, you need to set one both before AND after you perform the action that should be able to be undone. ```lua local ChangeHistoryService = game:GetService("ChangeHistoryService") local Selection = game:GetService("Selection") local toolbar = plugin:CreateToolbar("Example Plugin") local button = toolbar:CreateButton("Neon it up", "", "") button.Click:Connect(function() local parts = {} for _, part in pairs(Selection:Get()) do if part:IsA("BasePart") then parts[#parts + 1] = part end end if #parts > 0 then -- Calling SetWaypoint before the work will not cause any issues, however -- it is redundant, only the call AFTER the work is needed. --ChangeHistoryService:SetWaypoint("Setting selection to neon") for _, part in pairs(parts) do part.Material = Enum.Material.Neon end -- Call SetWaypoint AFTER completing the work ChangeHistoryService:SetWaypoint("Set selection to neon") else -- Nothing to do. You do not need to call SetWaypoint in the case where -- the action did not end up making any changes to the experience. end end) ``` ### Method: ChangeHistoryService:TryBeginRecording **Signature:** `ChangeHistoryService:TryBeginRecording(name: string, displayName: string?): string?` This method begins a recording to track changes to the data model. You **must** call it prior to making changes to avoid future warnings or errors. When the recording is completed, you call [FinishRecording()](/docs/reference/engine/classes/ChangeHistoryService.md) with the returned recording identifier to complete the recording and update the undo/redo stack. This method will return `nil` if it fails to begin a recording. Recordings fail if the plugin already has a recording in progress, or if the user is in a solo playtest. You may use [IsRecordingInProgress()](/docs/reference/engine/classes/ChangeHistoryService.md) to check the recording status of the plugin. *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name` | `string` | | Name of the action being performed suitable for logging and coding purposes. | | `displayName` | `string?` | | Name of the action being performed to display to the user. | **Returns:** `string?` **ChangeHistoryService:TryBeginRecording** To commit an undo/redo record, you need to first call [TryBeginRecording()](/docs/reference/engine/classes/ChangeHistoryService.md) followed by calling [FinishRecording()](/docs/reference/engine/classes/ChangeHistoryService.md). ```lua local ChangeHistoryService = game:GetService("ChangeHistoryService") local Selection = game:GetService("Selection") local toolbar = plugin:CreateToolbar("Example Plugin") local button = toolbar:CreateButton("Neon it up", "", "") button.Click:Connect(function() local parts = {} for _, part in pairs(Selection:Get()) do if part:IsA("BasePart") then parts[#parts + 1] = part end end if #parts < 1 then -- Nothing to do. return end local recording = ChangeHistoryService:TryBeginRecording("Set selection to neon") if not recording then -- Handle error here. This indidcates that your plugin began a previous -- recording and never completed it. You may only have one recording -- per plugin active at a time. return end for _, part in pairs(parts) do part.Material = Enum.Material.Neon end ChangeHistoryService:FinishRecording(recording, Enum.FinishRecordingOperation.Commit) end) ``` ### Method: ChangeHistoryService:Undo **Signature:** `ChangeHistoryService:Undo(): ()` Undos the last action taken, for which there exists a waypoint. *Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `()` ## Events ### Event: ChangeHistoryService.OnRecordingFinished **Signature:** `ChangeHistoryService.OnRecordingFinished(name: string, displayName: string?, identifier: string?, operation: FinishRecordingOperation, finalOptions: Dictionary?)` Fired when the user completes an action. Parameters come from [TryBeginRecording()](/docs/reference/engine/classes/ChangeHistoryService.md) and [FinishRecording()](/docs/reference/engine/classes/ChangeHistoryService.md). *Security: PluginSecurity* **Parameters:** | Name | Type | Description | |------|------|-------------| | `name` | `string` | Name of the action being performed suitable for logging and coding purposes. | | `displayName` | `string?` | Name of the action being performed to display to the user. | | `identifier` | `string?` | The identifier for the recording. | | `operation` | `FinishRecordingOperation` | | | `finalOptions` | `Dictionary?` | Optional table from [ FinishOperation()](/docs/reference/engine/classes/ChangeHistoryService.md). | ### Event: ChangeHistoryService.OnRecordingStarted **Signature:** `ChangeHistoryService.OnRecordingStarted(name: string, displayName: string?)` Fired when the user begins an action. Parameters come from [TryBeginRecording()](/docs/reference/engine/classes/ChangeHistoryService.md). *Security: PluginSecurity* **Parameters:** | Name | Type | Description | |------|------|-------------| | `name` | `string` | Name of the action being performed suitable for logging and coding purposes. | | `displayName` | `string?` | Name of the action being performed to display to the user. | ### Event: ChangeHistoryService.OnRedo **Signature:** `ChangeHistoryService.OnRedo(waypoint: string)` Fired when the user reverses the undo command. Waypoint describes the type action that has been redone. *Security: PluginSecurity* **Parameters:** | Name | Type | Description | |------|------|-------------| | `waypoint` | `string` | | ### Event: ChangeHistoryService.OnUndo **Signature:** `ChangeHistoryService.OnUndo(waypoint: string)` Fired when the user undoes an action in studio. Waypoint describes the type action that has been undone. *Security: PluginSecurity* **Parameters:** | Name | Type | Description | |------|------|-------------| | `waypoint` | `string` | | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ChannelTabsConfiguration last_updated: 2026-06-29T19:34:07Z inherits: - TextChatConfigurations - Instance - Object type: class memory_category: Instances tags: - NotCreatable summary: "Configures properties of the optional channel tabs in the default chat window." --- # Class: ChannelTabsConfiguration > Configures properties of the optional channel tabs in the default chat window. ## Description Configures properties of the optional channel tabs in the default chat window. It is parented to [TextChatService](/docs/reference/engine/classes/TextChatService.md). To learn more, see [Customizing the Chat Window](/docs/en-us/chat/chat-window.md). ## Properties ### Property: ChannelTabsConfiguration.AbsolutePosition ```json { "type": "Vector2", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Chat" ] } ``` Read-only property that provides the screen position of the channel tab bar in pixels. Behaves similarly to [GuiBase2d.AbsolutePosition](/docs/reference/engine/classes/GuiBase2d.md). ### Property: ChannelTabsConfiguration.AbsoluteSize ```json { "type": "Vector2", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Chat" ] } ``` Read-only property that provides the screen size of the channel tab bar in pixels. Behaves similarly to [GuiBase2d.AbsoluteSize](/docs/reference/engine/classes/GuiBase2d.md). ### Property: ChannelTabsConfiguration.BackgroundColor3 ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Chat" ] } ``` Background color of the channel tabs. If the background color is not overridden, this value will respect the user's [GuiService.PreferredTransparency](/docs/reference/engine/classes/GuiService.md) by making the menu more gray as the transparency of the menu decreases. Default value is [Color3.new(25, 27, 29)](/docs/reference/engine/datatypes/Color3.md). ### Property: ChannelTabsConfiguration.BackgroundTransparency ```json { "type": "double", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Chat" ] } ``` Background transparency of the channel tabs as a number between `0` and `1`. This value is multiplied with the user's [GuiService.PreferredTransparency](/docs/reference/engine/classes/GuiService.md) to create the effective background transparency used by the channel tabs, which may be more opaque than this value set here. Default value is `0`. ### Property: ChannelTabsConfiguration.Enabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Chat" ] } ``` Whether to show the channel tabs. Set to `false` to hide. ### Property: ChannelTabsConfiguration.FontFace ```json { "type": "Datatype.Font", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Chat" ] } ``` Font used to render text in the channel tabs. Default is [Font.BuilderSansBold](/docs/reference/engine/enums/Font.md). ### Property: ChannelTabsConfiguration.HoverBackgroundColor3 ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Chat" ] } ``` Background color of a channel tab when hovering over it. Default value is [Color3.new(125, 125, 125)](/docs/reference/engine/datatypes/Color3.md). ### Property: ChannelTabsConfiguration.SelectedTabTextColor3 ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Chat" ] } ``` Color of text in a selected tab. Default value is [Color3.new(255, 255, 255)](/docs/reference/engine/datatypes/Color3.md). ### Property: ChannelTabsConfiguration.TextColor3 ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Chat" ] } ``` Color of text in an unselected tab. Default value is [Color3.new(175, 175, 175)](/docs/reference/engine/datatypes/Color3.md). ### Property: ChannelTabsConfiguration.TextSize ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Chat" ] } ``` Size of the text in channel tabs. Default value is `14`. ### Property: ChannelTabsConfiguration.TextStrokeColor3 ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Chat" ] } ``` Color of the text stroke for text in channel tabs. Default value is [Color3.new(0, 0, 0)](/docs/reference/engine/datatypes/Color3.md). ### Property: ChannelTabsConfiguration.TextStrokeTransparency ```json { "type": "double", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Chat" ] } ``` Transparency of the text stroke for text in channel tabs. Default value is `1`. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: CharacterAppearance last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable summary: "The base class for objects that change the character's appearance, such as the BodyColors, CharacterMesh, ShirtGraphic, Pants and Shirt objects." --- # Class: CharacterAppearance > The base class for objects that change the character's appearance, such as the > [BodyColors](/docs/reference/engine/classes/BodyColors.md), [CharacterMesh](/docs/reference/engine/classes/CharacterMesh.md), [ShirtGraphic](/docs/reference/engine/classes/ShirtGraphic.md), [Pants](/docs/reference/engine/classes/Pants.md) > and [Shirt](/docs/reference/engine/classes/Shirt.md) objects. ## Description Base class for objects that change a character's appearance. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: CharacterMesh last_updated: 2026-06-29T19:34:07Z inherits: - CharacterAppearance - Instance - Object type: class memory_category: Instances summary: "Modifies the appearance of an R6 body part." --- # Class: CharacterMesh > Modifies the appearance of an R6 body part. ## Description This property modifies the appearance of an R6 body part. It has no effect in R15 characters. ## Properties ### Property: CharacterMesh.BaseTextureContent ```json { "type": "Content", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarAppearance" ] } ``` ### Property: CharacterMesh.BaseTextureId ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarAppearance" ] } ``` The texture of a CharacterMesh. It can be overridden by Shirts, Pants, T-Shirts, and the [CharacterMesh.OverlayTextureId](/docs/reference/engine/classes/CharacterMesh.md) property. ### Property: CharacterMesh.BodyPart ```json { "type": "BodyPart", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarAppearance" ] } ``` The part of the Character's body that is affected. ### Property: CharacterMesh.MeshContent ```json { "type": "Content", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarAppearance" ] } ``` ### Property: CharacterMesh.MeshId ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarAppearance" ] } ``` Used to load a mesh file, and apply it to the given BodyPart. ### Property: CharacterMesh.OverlayTextureContent ```json { "type": "Content", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarAppearance" ] } ``` ### Property: CharacterMesh.OverlayTextureId ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarAppearance" ] } ``` The assetId of the overlay texture. The overlay covers Shirts, Pants, T-Shirts, and the [CharacterMesh.BaseTextureId](/docs/reference/engine/classes/CharacterMesh.md). ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Chat last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated summary: "Houses the Luau code responsible for running the legacy chat system." --- # Class: Chat > Houses the Luau code responsible for running the legacy chat system. ## Description The **Chat** service houses the Luau code responsible for running the [legacy chat system](/docs/en-us/chat/in-experience-text-chat.md#migrate-from-legacy-chat). Similar to [StarterPlayerScripts](/docs/reference/engine/classes/StarterPlayerScripts.md), default objects like [Scripts](/docs/reference/engine/classes/Script.md) and [ModuleScripts](/docs/reference/engine/classes/ModuleScript.md) are inserted into the service. ## Properties ### Property: Chat.BubbleChatEnabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Chat" ] } ``` If true, entering a message in the chat will result in a chat bubble popping up above the player's [Player.Character](/docs/reference/engine/classes/Player.md). This behavior can either be enabled by directly ticking this checkbox in Studio, or by using a [LocalScript](/docs/reference/engine/classes/LocalScript.md): ```lua local ChatService = game:GetService("Chat") ChatService.BubbleChatEnabled = true ``` This must be done on the client, toggling this value in a server-side [Script](/docs/reference/engine/classes/Script.md) will have no effect. ### Property: Chat.LoadDefaultChat ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "None", "write": "NotAccessibleSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Chat" ] } ``` Toggles whether the default chat framework should be automatically loaded when the game runs. ## Methods ### Method: Chat:CanUserChatAsync **Signature:** `Chat:CanUserChatAsync(userId: int64): boolean` Will return false if the player with the specified [Player.UserId](/docs/reference/engine/classes/Player.md) is not allowed to chat because of their account settings. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Chat* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `userId` | `int64` | | | **Returns:** `boolean` ### Method: Chat:CanUsersChatAsync **Signature:** `Chat:CanUsersChatAsync(userIdFrom: int64, userIdTo: int64): boolean` Will return false if the two users cannot communicate because their account settings do not allow it. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Chat* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `userIdFrom` | `int64` | | | | `userIdTo` | `int64` | | | **Returns:** `boolean` ### Method: Chat:Chat **Signature:** `Chat:Chat(partOrCharacter: Instance, message: string, color?: ChatColor): ()` The Chat function fires the [Chat.Chatted](/docs/reference/engine/classes/Chat.md) event with the parameters specified in this method. By default, there is a [LocalScript](/docs/reference/engine/classes/LocalScript.md) inside of each player's [PlayerScripts](/docs/reference/engine/classes/PlayerScripts.md) object named _BubbleChat_, which causes a dialog-like billboard to appear above the _partOrCharacter_ when the chatted event is fired. _Note:_ Since dialogs are controlled by a LocalScript, you will not be able to see any dialogs created from this method unless you are running in _Play Solo_ mode. *Security: None · Thread Safety: Unsafe · Capabilities: Chat* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `partOrCharacter` | `Instance` | | An instance that is the part or character which the _BubbleChat_ dialog should appear above. | | `message` | `string` | | The message string being chatted. | | `color` | `ChatColor` | `Blue` | An [ChatColor](/docs/reference/engine/enums/ChatColor.md) specifying the color of the chatted message. | **Returns:** `()` **Chat:Chat** The below example would create a part in Workspace and cause it to exclaim "Blame John!" ```lua local ChatService = game:GetService("Chat") local part = Instance.new("Part") part.Anchored = true part.Parent = workspace ChatService:Chat(part, "Blame John!", "Red") ``` ### Method: Chat:FilterStringAsync **Signature:** `Chat:FilterStringAsync(stringToFilter: string, playerFrom: Player, playerTo: Player): string` **Partial Deprecation Warning**: Calling this function from the client using a [LocalScript](/docs/reference/engine/classes/LocalScript.md) is deprecated, and will be disabled in the future. Text filtering should be done from a [Script](/docs/reference/engine/classes/Script.md) on the server using the similarly-named [TextService:FilterStringAsync()](/docs/reference/engine/classes/TextService.md), which uses a different set of parameters and return type. Games that do not properly filter player-generated text might be subject to moderation action. Please be sure a game properly filters text before publishing it. **FilterStringAsync** filters a string using filtering that is appropriate for the sending and receiving player. If the filtered string is to be used for a persistent message, such as the name of a shop, writing on a plaque, etc, then the function should be called with the author as both the sender and receiver. This function should be used **every time** a player can enter custom text in **any context**, most commonly using a [TextBox](/docs/reference/engine/classes/TextBox.md). Some examples of text to be filtered: - Custom chat messages - Custom character names - Names for a shop in a tycoon-style game *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Chat* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `stringToFilter` | `string` | | The raw string to be filtered, exactly as entered by the player. | | `playerFrom` | `Player` | | The author of the text. | | `playerTo` | `Player` | | The intended recipient of the provided text; use the author if the text is persistent (see description). | **Returns:** `string` ### Method: Chat:FilterStringForBroadcast **Signature:** `Chat:FilterStringForBroadcast(stringToFilter: string, playerFrom: Player): string` Filters a string sent from _playerFrom_ for broadcast to no particular target. The filtered message has more restrictions than [Chat:FilterStringAsync()](/docs/reference/engine/classes/Chat.md). Some examples of where this method could be used: - Message walls - Cross-server shouts - User-created signs Calling FilterString from [LocalScripts](/docs/reference/engine/classes/LocalScript.md) is deprecated and will be disabled in the future. Text filtering should be done from server-side [Scripts](/docs/reference/engine/classes/Script.md) using FilterStringAsync. _Note:_ A game not using this filter function for custom chat or other user generated text may be subjected to moderation action. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Chat* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `stringToFilter` | `string` | | Message string being filtered. | | `playerFrom` | `Player` | | Instance of the player sending the message. | **Returns:** `string` — Filtered message string. **Chat:FilterStringForBroadcast** The following example shows a simple way to use the FilterStringForBroadcast function. The example uses the message variable as the _stringToFilter_ argument and the local player as the _playerFrom_ argument. The example then prints the result of the filtering function, _FilteredString_. ```lua local Players = game:GetService("Players") local Chat = game:GetService("Chat") local playerFrom = Players.LocalPlayer local message = "Hello world!" -- Filter the string and store the result in the 'FilteredString' variable local filteredString = Chat:FilterStringForBroadcast(message, playerFrom) print(filteredString) ``` **Expected output:** Hello world! ### Method: Chat:InvokeChatCallback **Signature:** `Chat:InvokeChatCallback(callbackType: ChatCallbackType, callbackArguments: Tuple): Tuple` InvokeChatCallback will call a function registered by [RegisterChatCallback](/docs/reference/engine/classes/Chat.md), given the ChatCallbackType enum and the arguments to send the function. It will return the result of the registered function, or raise an error if no function has been registered. This function is called by the Luau Chat System so that chat callbacks may be registered to change the behavior of certain features. Unless you are replacing the default Luau Chat System with your own, you should not need to call this function. You can read about the different callback functions at [Chat:RegisterChatCallback()](/docs/reference/engine/classes/Chat.md). *Security: None · Thread Safety: Unsafe · Capabilities: Chat* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `callbackType` | `ChatCallbackType` | | The type of callback to invoke. | | `callbackArguments` | `Tuple` | | The arguments that will be sent to the registered callback function. | **Returns:** `Tuple` — The values returned by the function registered to the given ChatCallbackType. ### Method: Chat:RegisterChatCallback **Signature:** `Chat:RegisterChatCallback(callbackType: ChatCallbackType, callbackFunction: Function): ()` RegisterChatCallback binds a function to some chat system event in order to affect the behavior of the Luau chat system. The first argument determines the event (using the [ChatCallbackType](/docs/reference/engine/enums/ChatCallbackType.md) enum) to which the second argument, the function, shall be bound. The default Luau chat system uses [InvokeChatCallback](/docs/reference/engine/classes/Chat.md) to invoke registered functions. Attempting to register a server- or client- only callback on a peer that isn't a server or client respectively will raise an error. The following sections describe in what ways registered functions will be used. #### OnCreatingChatWindow Client-only. Invoked before the client constructs the chat window. Must return a table of settings to be merged into the information returned by the ChatSettings module. #### OnClientFormattingMessage Client-only. Invoked before the client displays a message (whether it is a player chat message, system message, or /me command). This function is invoked with the message object and may (or may not) return a table to be merged into `message.ExtraData`. #### OnClientSendingMessage Not invoked at this time. #### OnServerReceivingMessage Server-only. Invoked when the server receives a message from a speaker (note that speakers may not necessarily be a [Player](/docs/reference/engine/classes/Player.md) chatting). This callback is called with the Message object. The function can make changes to the Message object to change the manner in which the message is processed. **The Message object must be returned for this callback to do anything.** Setting this callback can allow the server to, for example: - Set `message.ShouldDeliver` to false in order to cancel delivery of the message to players (useful for implementing a chat exclusion list) - Get/set the speaker's name color (`message.ExtraData.NameColor`, a Color3) on a message-by-message basis *Security: None · Thread Safety: Unsafe · Capabilities: Chat* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `callbackType` | `ChatCallbackType` | | The callback to which the function shall be registered (this determines in what way the function is called). | | `callbackFunction` | `Function` | | The function to call when the callback is invoked using Chat:InvokeChatCallback. | **Returns:** `()` ### Method: Chat:SetBubbleChatSettings **Signature:** `Chat:SetBubbleChatSettings(settings: Variant): ()` This function customizes various settings of the in-game bubble chat. Before using this, make sure that bubble chat is enabled by setting [Chat.BubbleChatEnabled](/docs/reference/engine/classes/Chat.md) to true. The settings argument is a table where the keys are the names of the settings you want to edit and the values are what you want to change these settings to. Note that you don't have to include all of them in the settings argument, omitting some will result in them keeping their default value. This function is client-side only, attempting to call it on the server will trigger an error. *Security: None · Thread Safety: Unsafe · Capabilities: Chat* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `settings` | `Variant` | | A settings table. | **Returns:** `()` **Customize visual aspects** When run from a `LocalScript`, this snippet will make all the chat bubbles appear with bigger text under a different font and a light blue background. Note that all the other settings will keep their default value. ```lua local ChatService = game:GetService("Chat") ChatService:SetBubbleChatSettings({ BackgroundColor3 = Color3.fromRGB(180, 210, 228), TextSize = 20, Font = Enum.Font.Cartoon, }) ``` **Restore default settings** If you want to reset the bubble chat to its default look, you can call this function with an empty table, because any setting you omit from the argument will result in it returning to its default value: ```lua local ChatService = game:GetService("Chat") ChatService:SetBubbleChatSettings({}) ``` ### Method: Chat:FilterStringForPlayerAsync **Signature:** `Chat:FilterStringForPlayerAsync(stringToFilter: string, playerToFilterFor: Player): string` > **Deprecated:** This item has been superseded by [Chat:FilterStringAsync()](/docs/reference/engine/classes/Chat.md) and [Chat:FilterStringForBroadcast()](/docs/reference/engine/classes/Chat.md) which should be used in all new work The FilterStringForPlayerAsync function filters a string appropriate to the given player's age settings, so they see what is appropriate to them. This function will only work if called from a [Script](/docs/reference/engine/classes/Script.md) on the server. If called on a client it will fail. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Chat* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `stringToFilter` | `string` | | String being filtered. | | `playerToFilterFor` | `Player` | | Player that the string is being filtered for. | **Returns:** `string` — Filtered string result. ## Events ### Event: Chat.Chatted **Signature:** `Chat.Chatted(part: Instance, message: string, color: ChatColor)` Fires when [Chat:Chat()](/docs/reference/engine/classes/Chat.md) is called. *Security: None · Capabilities: Chat* **Parameters:** | Name | Type | Description | |------|------|-------------| | `part` | `Instance` | | | `message` | `string` | | | `color` | `ChatColor` | | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ChatInputBarConfiguration last_updated: 2026-06-29T19:34:07Z inherits: - TextChatConfigurations - Instance - Object type: class memory_category: Instances tags: - NotCreatable summary: "Configures properties of the default chat input bar." --- # Class: ChatInputBarConfiguration > Configures properties of the default chat input bar. ## Description Configures properties of the default text chat input bar. It is parented to [TextChatService](/docs/reference/engine/classes/TextChatService.md). #### Chat UI composition The default chat UI is composed of two separate elements: the **chat window** (message list) configured by [ChatWindowConfiguration](/docs/reference/engine/classes/ChatWindowConfiguration.md), and the **chat input bar** configured by this object. Each element reports its own `AbsoluteSize` and `AbsolutePosition` independently. To determine the total screen bounds of the entire chat UI, combine values from both configurations by computing the union of the two rectangles. ## Code Samples **Get Total Chat UI Bounds** Computes the total screen bounds of the entire chat UI by combining the `AbsolutePosition` and `AbsoluteSize` of both `ChatWindowConfiguration` and `ChatInputBarConfiguration`. Note that the reported bounds do not include the small decorative bevel areas at the top and bottom edges of the chat window, so the actual visual footprint on screen is slightly larger than the computed region. ```lua local Players = game:GetService("Players") local TextChatService = game:GetService("TextChatService") local player = Players.LocalPlayer local playerGui = player:WaitForChild("PlayerGui") local windowConfig = TextChatService.ChatWindowConfiguration local inputBarConfig = TextChatService.ChatInputBarConfiguration local function getTotalChatBounds() local windowPos = windowConfig.AbsolutePosition local windowSize = windowConfig.AbsoluteSize local inputPos = inputBarConfig.AbsolutePosition local inputSize = inputBarConfig.AbsoluteSize -- Compute the union of both rectangles to get total chat UI bounds. -- The reported values do not include the decorative bevel at the top -- and bottom edges of the chat window. local topLeft = Vector2.new( math.min(windowPos.X, inputPos.X), math.min(windowPos.Y, inputPos.Y) ) local bottomRight = Vector2.new( math.max(windowPos.X + windowSize.X, inputPos.X + inputSize.X), math.max(windowPos.Y + windowSize.Y, inputPos.Y + inputSize.Y) ) return topLeft, bottomRight - topLeft end -- Wait for chat UI to initialize and report absolute values task.wait(2) local position, size = getTotalChatBounds() print("Total chat UI position:", position) print("Total chat UI size:", size) -- Overlay a semi-transparent frame to visually verify the bounds local screenGui = Instance.new("ScreenGui") screenGui.Name = "ChatBoundsDebug" screenGui.DisplayOrder = 100 screenGui.Parent = playerGui local frame = Instance.new("Frame") frame.Position = UDim2.fromOffset(position.X, position.Y) frame.Size = UDim2.fromOffset(size.X, size.Y) frame.BackgroundColor3 = Color3.fromRGB(255, 0, 0) frame.BackgroundTransparency = 0.5 frame.BorderSizePixel = 2 frame.BorderColor3 = Color3.fromRGB(255, 0, 0) frame.Parent = screenGui ``` ## Properties ### Property: ChatInputBarConfiguration.AbsolutePosition ```json { "type": "Vector2", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "Unsafe", "category": "Data", "capabilities": [ "Chat" ] } ``` Read-only property that provides the screen position of the default chat input bar in pixels. Behaves similarly to [GuiBase2d.AbsolutePosition](/docs/reference/engine/classes/GuiBase2d.md). This value represents only the input bar portion of the chat UI, not the entire chat interface. To get the total bounds including the message list, see [ChatWindowConfiguration.AbsolutePosition](/docs/reference/engine/classes/ChatWindowConfiguration.md) and combine both regions. ### Property: ChatInputBarConfiguration.AbsoluteSize ```json { "type": "Vector2", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "Unsafe", "category": "Data", "capabilities": [ "Chat" ] } ``` Read-only property that provides the screen size of the default chat input bar in pixels. Behaves similarly to [GuiBase2d.AbsoluteSize](/docs/reference/engine/classes/GuiBase2d.md). This value represents only the input bar portion of the chat UI, not the entire chat interface. To get the total bounds including the message list, see [ChatWindowConfiguration.AbsoluteSize](/docs/reference/engine/classes/ChatWindowConfiguration.md) and combine both regions. ### Property: ChatInputBarConfiguration.AutocompleteEnabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Chat" ] } ``` Whether to enable autocomplete for the chat input bar. Set to `false` to disable autocomplete. ### Property: ChatInputBarConfiguration.BackgroundColor3 ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Chat" ] } ``` Background color of the default chat input bar. Default value is [Color3.new(25, 27, 29)](/docs/reference/engine/datatypes/Color3.md). ### Property: ChatInputBarConfiguration.BackgroundTransparency ```json { "type": "double", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Chat" ] } ``` Background transparency of the default chat input bar as a number between `0` and `1`. This value is multiplied with the user's [GuiService.PreferredTransparency](/docs/reference/engine/classes/GuiService.md) to create the effective background transparency used by the chat input bar, which may be more opaque than this value set here. Default value is `0.2`. ### Property: ChatInputBarConfiguration.Enabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Chat" ] } ``` Whether to show the default chat input bar. Set to `false` to hide. ### Property: ChatInputBarConfiguration.FontFace ```json { "type": "Datatype.Font", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Chat" ] } ``` Font used to render text in the default chat input bar. Default is [Font.BuilderSansMedium](/docs/reference/engine/enums/Font.md). ### Property: ChatInputBarConfiguration.IsFocused ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "Unsafe", "category": "Data", "capabilities": [ "Chat" ] } ``` Indicates whether the default chat input bar is focused or not. Useful for firing property changed events so you can implement callback functions that respond to changes in the input bar's focus state. **Typing Indicator Bubble** The code below includes a simple way to create a typing indicator bubble above a user's avatar when the user is typing. Paste into a LocalScript. ```lua local Players = game:GetService("Players") local TextChatService = game:GetService("TextChatService") local ChatInputBarConfiguration = TextChatService:FindFirstChildOfClass("ChatInputBarConfiguration") local BubbleChatConfiguration = TextChatService:FindFirstChildOfClass("BubbleChatConfiguration") local LocalPlayer = Players.LocalPlayer local Character = LocalPlayer.Character or LocalPlayer.CharacterAdded:Wait() -- Set up TextLabel local textLabel = Instance.new("TextLabel") textLabel.Size = UDim2.fromScale(1, 1) textLabel.Text = ". . ." textLabel.BackgroundColor3 = BubbleChatConfiguration.BackgroundColor3 textLabel.BorderColor3 = BubbleChatConfiguration.BackgroundColor3 textLabel.BackgroundTransparency = BubbleChatConfiguration.BackgroundTransparency textLabel.TextColor3 = BubbleChatConfiguration.TextColor3 textLabel.FontFace = BubbleChatConfiguration.FontFace textLabel.TextSize = BubbleChatConfiguration.TextSize -- Parent a UICorner to the TextLabel to have rounded corners local uiCorner = Instance.new("UICorner") uiCorner.CornerRadius = UDim.new(0, 12) uiCorner.Parent = textLabel -- Set up Billboard local typingIndicatorBillboard = Instance.new("BillboardGui") typingIndicatorBillboard.Enabled = false typingIndicatorBillboard.Size = UDim2.fromScale(1, 1) typingIndicatorBillboard.StudsOffsetWorldSpace = Vector3.new(-0, 4, 0) typingIndicatorBillboard.Adornee = Character textLabel.Parent = typingIndicatorBillboard typingIndicatorBillboard.Parent = LocalPlayer:FindFirstChildOfClass("PlayerGui") ChatInputBarConfiguration:GetPropertyChangedSignal("IsFocused"):Connect(function() -- Enable the typing indicator when the input bar is focused and disable otherwise typingIndicatorBillboard.Enabled = ChatInputBarConfiguration.IsFocused end) ``` ### Property: ChatInputBarConfiguration.KeyboardKeyCode ```json { "type": "KeyCode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Chat" ] } ``` Additional key users can press to trigger focusing on the default chat input bar. Useful when you want to have an extra hotkey for focusing in addition to the / key. ### Property: ChatInputBarConfiguration.PlaceholderColor3 ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Chat" ] } ``` Color of the text of the placeholder text in the default chat input bar. Default value is [Color3.new(178, 178, 178)](/docs/reference/engine/datatypes/Color3.md). ### Property: ChatInputBarConfiguration.TargetTextChannel ```json { "type": "TextChannel", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Chat" ] } ``` Determines which [TextChannel](/docs/reference/engine/classes/TextChannel.md) to use when the user sends a message with the default chat input bar. ### Property: ChatInputBarConfiguration.TextBox ```json { "type": "TextBox", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Chat" ] } ``` Reference to a designated [TextBox](/docs/reference/engine/classes/TextBox.md) instance that sends messages on behalf of the user. You can use it to further integrate your custom chat input bar UI into your experience by freely manipulating appearance, location, and layout. When opting to set this property to a custom [TextBox](/docs/reference/engine/classes/TextBox.md), you don't need to write any code for the following behavior: - When a user types a message and presses [KeyCode.Return](/docs/reference/engine/enums/KeyCode.md), the message will be sent to [ChatInputBarConfiguration.TargetTextChannel](/docs/reference/engine/classes/ChatInputBarConfiguration.md). - When a message is sent, [TextBox.Text](/docs/reference/engine/classes/TextBox.md) will automatically clear. For security, some limitations are imposed on the [TextBox](/docs/reference/engine/classes/TextBox.md) when it is promoted to [ChatInputBarConfiguration.TextBox](/docs/reference/engine/classes/ChatInputBarConfiguration.md). Luau code will not be able to: - Change the [TextBox.Text](/docs/reference/engine/classes/TextBox.md) property. - Use the [TextBox:CaptureFocus()](/docs/reference/engine/classes/TextBox.md) or [TextBox:ReleaseFocus()](/docs/reference/engine/classes/TextBox.md) methods. ### Property: ChatInputBarConfiguration.TextColor3 ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Chat" ] } ``` Color of the text in default chat input bar. Default value is [Color3.new(255, 255, 255)](/docs/reference/engine/datatypes/Color3.md). ### Property: ChatInputBarConfiguration.TextSize ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Chat" ] } ``` Size of the text in default chat input bar. Default value is `14`. ### Property: ChatInputBarConfiguration.TextStrokeColor3 ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Chat" ] } ``` Color of the text stroke for text in default chat input bar. Default value is [Color3.new(0, 0, 0)](/docs/reference/engine/datatypes/Color3.md). ### Property: ChatInputBarConfiguration.TextStrokeTransparency ```json { "type": "double", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Chat" ] } ``` Transparency of the text stroke for text in default chat input bar. Default value is `0.5`. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ChatWindowConfiguration last_updated: 2026-06-29T19:34:07Z inherits: - TextChatConfigurations - Instance - Object type: class memory_category: Instances tags: - NotCreatable summary: "Configures properties of the default chat window." --- # Class: ChatWindowConfiguration > Configures properties of the default chat window. ## Description Configures properties of the default text chat window. It is parented to [TextChatService](/docs/reference/engine/classes/TextChatService.md). #### Chat UI composition The default chat UI is composed of two separate elements: the **chat window** (message list) configured by this object, and the **chat input bar** configured by [ChatInputBarConfiguration](/docs/reference/engine/classes/ChatInputBarConfiguration.md). Each element reports its own `AbsoluteSize` and `AbsolutePosition` independently. To determine the total screen bounds of the entire chat UI, combine values from both configurations by computing the union of the two rectangles. ## Code Samples **Get Total Chat UI Bounds** Computes the total screen bounds of the entire chat UI by combining the `AbsolutePosition` and `AbsoluteSize` of both `ChatWindowConfiguration` and `ChatInputBarConfiguration`. Note that the reported bounds do not include the small decorative bevel areas at the top and bottom edges of the chat window, so the actual visual footprint on screen is slightly larger than the computed region. ```lua local Players = game:GetService("Players") local TextChatService = game:GetService("TextChatService") local player = Players.LocalPlayer local playerGui = player:WaitForChild("PlayerGui") local windowConfig = TextChatService.ChatWindowConfiguration local inputBarConfig = TextChatService.ChatInputBarConfiguration local function getTotalChatBounds() local windowPos = windowConfig.AbsolutePosition local windowSize = windowConfig.AbsoluteSize local inputPos = inputBarConfig.AbsolutePosition local inputSize = inputBarConfig.AbsoluteSize -- Compute the union of both rectangles to get total chat UI bounds. -- The reported values do not include the decorative bevel at the top -- and bottom edges of the chat window. local topLeft = Vector2.new( math.min(windowPos.X, inputPos.X), math.min(windowPos.Y, inputPos.Y) ) local bottomRight = Vector2.new( math.max(windowPos.X + windowSize.X, inputPos.X + inputSize.X), math.max(windowPos.Y + windowSize.Y, inputPos.Y + inputSize.Y) ) return topLeft, bottomRight - topLeft end -- Wait for chat UI to initialize and report absolute values task.wait(2) local position, size = getTotalChatBounds() print("Total chat UI position:", position) print("Total chat UI size:", size) -- Overlay a semi-transparent frame to visually verify the bounds local screenGui = Instance.new("ScreenGui") screenGui.Name = "ChatBoundsDebug" screenGui.DisplayOrder = 100 screenGui.Parent = playerGui local frame = Instance.new("Frame") frame.Position = UDim2.fromOffset(position.X, position.Y) frame.Size = UDim2.fromOffset(size.X, size.Y) frame.BackgroundColor3 = Color3.fromRGB(255, 0, 0) frame.BackgroundTransparency = 0.5 frame.BorderSizePixel = 2 frame.BorderColor3 = Color3.fromRGB(255, 0, 0) frame.Parent = screenGui ``` ## Properties ### Property: ChatWindowConfiguration.AbsolutePosition ```json { "type": "Vector2", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "Unsafe", "category": "Data", "capabilities": [ "Chat" ] } ``` Read-only property that provides the screen position of the default chat window (message list) in pixels. Behaves similarly to [GuiBase2d.AbsolutePosition](/docs/reference/engine/classes/GuiBase2d.md). This value represents only the message list portion of the chat UI, not the entire chat interface. To get the total bounds including the input bar, see [ChatInputBarConfiguration.AbsolutePosition](/docs/reference/engine/classes/ChatInputBarConfiguration.md) and combine both regions. See the code example in the class description above. ### Property: ChatWindowConfiguration.AbsoluteSize ```json { "type": "Vector2", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "Unsafe", "category": "Data", "capabilities": [ "Chat" ] } ``` Read-only property that provides the screen size of the default chat window (message list) in pixels. Behaves similarly to [GuiBase2d.AbsoluteSize](/docs/reference/engine/classes/GuiBase2d.md). This value represents only the message list portion of the chat UI, not the entire chat interface. To get the total bounds including the input bar, see [ChatInputBarConfiguration.AbsoluteSize](/docs/reference/engine/classes/ChatInputBarConfiguration.md) and combine both regions. See the code example in the class description above. ### Property: ChatWindowConfiguration.BackgroundColor3 ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Chat" ] } ``` Background color of the default chat window. If the background color is not overridden, this value will respect the user's [GuiService.PreferredTransparency](/docs/reference/engine/classes/GuiService.md) by making the menu more gray as the transparency of the menu decreases. Default value is [Color3.new(25, 27, 29)](/docs/reference/engine/datatypes/Color3.md). ### Property: ChatWindowConfiguration.BackgroundTransparency ```json { "type": "double", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Chat" ] } ``` Background transparency of the default chat window as a number between `0` and `1`. This value is multiplied with the user's [GuiService.PreferredTransparency](/docs/reference/engine/classes/GuiService.md) to create the effective background transparency used by the chat window, which may be more opaque than this value set here. Default value is `0.3`. ### Property: ChatWindowConfiguration.Enabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Chat" ] } ``` Whether to show the default chat window. Set to `false` to hide. ### Property: ChatWindowConfiguration.FontFace ```json { "type": "Datatype.Font", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Chat" ] } ``` Font used to render text in the default chat window. Default is [Font.BuilderSansMedium](/docs/reference/engine/enums/Font.md). ### Property: ChatWindowConfiguration.HeightScale ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Chat" ] } ``` Factor by which the height of the default chat window should be scaled. Must be a value between `0.5` and `2`. Defining a value outside of range clamps the actual value to the closest bound. ### Property: ChatWindowConfiguration.HorizontalAlignment ```json { "type": "HorizontalAlignment", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "Unsafe", "category": "Data", "capabilities": [ "Chat" ] } ``` Horizontal alignment of the chat window. Behaves similarly to [UIGridStyleLayout.HorizontalAlignment](/docs/reference/engine/classes/UIGridStyleLayout.md). Setting to [Left](/docs/reference/engine/enums/HorizontalAlignment.md) or [Right](/docs/reference/engine/enums/HorizontalAlignment.md) adding a small padding away from touching the corresponding horizontal edge of the screen. Setting to [Center](/docs/reference/engine/enums/HorizontalAlignment.md) aligns the window in the horizontal middle of the screen. Default value is [Left](/docs/reference/engine/enums/HorizontalAlignment.md). ### Property: ChatWindowConfiguration.TextColor3 ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Chat" ] } ``` Color of the text in default chat window. Default value is [Color3.new(255, 255, 255)](/docs/reference/engine/datatypes/Color3.md). ### Property: ChatWindowConfiguration.TextSize ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Chat" ] } ``` Size of the text in default chat window. Default value is `14`. ### Property: ChatWindowConfiguration.TextStrokeColor3 ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Chat" ] } ``` Color of the text stroke for text in default chat window. Default value is [Color3.new(0, 0, 0)](/docs/reference/engine/datatypes/Color3.md). ### Property: ChatWindowConfiguration.TextStrokeTransparency ```json { "type": "double", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Chat" ] } ``` Transparency of the text stroke for text in default chat window. Default value is `0.5`. ### Property: ChatWindowConfiguration.VerticalAlignment ```json { "type": "VerticalAlignment", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "Unsafe", "category": "Data", "capabilities": [ "Chat" ] } ``` Vertical alignment of the chat window. Behaves similarly to [UIGridStyleLayout.VerticalAlignment](/docs/reference/engine/classes/UIGridStyleLayout.md). Setting to [Top](/docs/reference/engine/enums/VerticalAlignment.md) or [Bottom](/docs/reference/engine/enums/VerticalAlignment.md) adds a small padding away from touching the corresponding edge of the screen. Setting to [Center](/docs/reference/engine/enums/VerticalAlignment.md) aligns the window in the vertical middle of the screen. Default value is [Top](/docs/reference/engine/enums/VerticalAlignment.md). ### Property: ChatWindowConfiguration.WidthScale ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Chat" ] } ``` Factor by which the width of the default chat window should be scaled. Must be a value between `0.5` and `2`. Defining a value outside of range clamps the actual value to the closest bound. ## Methods ### Method: ChatWindowConfiguration:DeriveNewMessageProperties **Signature:** `ChatWindowConfiguration:DeriveNewMessageProperties(): ChatWindowMessageProperties` Creates a new [ChatWindowMessageProperties](/docs/reference/engine/classes/ChatWindowMessageProperties.md) instance that can be used to customize the appearance of messages in the chat window. [ChatWindowMessageProperties](/docs/reference/engine/classes/ChatWindowMessageProperties.md) inherits from [TextChatMessageProperties](/docs/reference/engine/classes/TextChatMessageProperties.md). This is intended to be used during custom [TextChatService.OnChatWindowAdded](/docs/reference/engine/classes/TextChatService.md) callbacks. ```lua local TextChatService = game:GetService("TextChatService") local ChatWindowConfiguration = TextChatService.ChatWindowConfiguration TextChatService.OnChatWindowAdded = function(textChatMessage) local properties = ChatWindowConfiguration:DeriveNewMessageProperties() if textChatMessage.Metadata == "Important" then properties.TextColor3 = Color3.fromRGB(255, 0, 0) end return properties end ``` *Security: None · Thread Safety: Unsafe · Capabilities: Chat* **Returns:** `ChatWindowMessageProperties` **CanUsersDirectChatAsync** This example checks if two users can chat, creates a new [TextChannel](/docs/reference/engine/classes/TextChannel.md), and adds them to it. ```lua local TextChatService = game:GetService("TextChatService") local directChatParticipants = TextChatService:CanUsersDirectChatAsync(userId1, { userId2 }) -- Check for eligible participants if #directChatParticipants > 0 then local directChannel = Instance.new("TextChannel") directChannel.Parent = TextChatService for _, participant in directChatParticipants do directChannel:AddUserAsync(participant) end return directChannel end warn("Could not create TextChannel. Not enough eligible users.") return nil ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ChatWindowMessageProperties last_updated: 2026-06-29T19:34:07Z inherits: - TextChatMessageProperties - Instance - Object type: class memory_category: Instances tags: - NotCreatable --- # Class: ChatWindowMessageProperties ## Description Can be used to customize the appearance of text chat messages sent to the ChatWindow. Inherits from [TextChatMessageProperies](/docs/reference/engine/classes/TextChatMessageProperies.md). To create a new `ChatWindowMessageProperties` instance, use [ChatWindowConfiguration:DeriveNewMessageProperties()](/docs/reference/engine/classes/ChatWindowConfiguration.md) which will have the current properties from [ChatWindowConfiguration](/docs/reference/engine/classes/ChatWindowConfiguration.md). ```lua local TextChatService = game:GetService("TextChatService") local ChatWindowConfiguration = TextChatService:FindFirstChildOfClass("ChatWindowConfiguration") TextChatService.OnChatWindowAdded = function(message: TextChatMessage) -- Derive chat message properties local properties = ChatWindowConfiguration:DeriveNewMessageProperties() -- Change color of message within the chat window properties.TextColor3 = Color3.fromRGB(255, 121, 121) return properties end ``` ## Properties ### Property: ChatWindowMessageProperties.FontFace ```json { "type": "Datatype.Font", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Chat" ] } ``` Determines the font used to render text in the chat window. Default is [Font.BuilderSansMedium](/docs/reference/engine/enums/Font.md). ### Property: ChatWindowMessageProperties.PrefixTextProperties ```json { "type": "ChatWindowMessageProperties", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Chat" ] } ``` Determines the properties of the [TextChatMessage.PrefixText](/docs/reference/engine/classes/TextChatMessage.md) preceding the chat message. ### Property: ChatWindowMessageProperties.TextColor3 ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Chat" ] } ``` Determines the color of the text in the chat window. Default value is [Color3.fromRGB(255, 255, 255)](/docs/reference/engine/datatypes/Color3.md) (white). ### Property: ChatWindowMessageProperties.TextSize ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Chat" ] } ``` Determines the size of the text in the chat window. ### Property: ChatWindowMessageProperties.TextStrokeColor3 ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Chat" ] } ``` Determines the stroke color applied to text in the chat window. Default value is [Color3.fromRGB(0, 0, 0)](/docs/reference/engine/datatypes/Color3.md) (black). ### Property: ChatWindowMessageProperties.TextStrokeTransparency ```json { "type": "double", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Chat" ] } ``` Determines the transparency of the stroke applied to text in the chat window. Default value is `0.5`. ## Inherited Members ### From [TextChatMessageProperties](/docs/reference/engine/classes/TextChatMessageProperties.md) - **Property `PrefixText`** (`string`): The TextChatMessage.PrefixText to override. - **Property `Text`** (`string`): The TextChatMessage.Text to override. - **Property `Translation`** (`string`): The TextChatMessage.Translation to override. ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ChorusSoundEffect last_updated: 2026-06-29T19:34:07Z inherits: - SoundEffect - Instance - Object type: class memory_category: Instances summary: "Makes audio more voluminous by simulating multiple voices playing the same part." --- # Class: ChorusSoundEffect > Makes audio more voluminous by simulating multiple voices playing the same > part. ## Description A ChorusSoundEffect simulates the effect of multiple vocals or instruments playing the same part. It does this by taking the original sound and overlaying copies of that sound. These copies are not exact matches to the original but instead vary in pitch slightly. This simulates a real chorus, as different singers or instruments will have slight variations. This effect can be applied to either an individual sound or to a sound group by parenting it to the desired instance. Like all other [SoundEffect](/docs/reference/engine/classes/SoundEffect.md), a ChorusSoundEffect can be applied either to a [Sound](/docs/reference/engine/classes/Sound.md) or [SoundGroup](/docs/reference/engine/classes/SoundGroup.md) by being parented to either. ## Properties ### Property: ChorusSoundEffect.Depth ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "LegacySound" ] } ``` Range: 0 to 1 (default 0.15) Controls how intense the effect is. ### Property: ChorusSoundEffect.Mix ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "LegacySound" ] } ``` Range: 0 to 1 (default 0.5) Percentage of the original sound that will be applied to the filter. Raising this value will make the effect sound louder and be more apparent. ### Property: ChorusSoundEffect.Rate ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "LegacySound" ] } ``` Range: 0 to 20 (default 0.5) How frequently the pitch variation changes. Measured in Hz. ## Inherited Members ### From [SoundEffect](/docs/reference/engine/classes/SoundEffect.md) - **Property `Enabled`** (`boolean`): Toggles the effect on and off. - **Property `Priority`** (`int`): Determines the order the effect will be applied in relation to other ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ClickDetector last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances summary: "An object that provides user input on in-experience BaseParts and Models." --- # Class: ClickDetector > An object that provides user input on in-experience [BaseParts](/docs/reference/engine/classes/BasePart.md) > and [Models](/docs/reference/engine/classes/Model.md). ## Description `ClickDetector` allows [Scripts](/docs/reference/engine/classes/Script.md) and [LocalScripts](/docs/reference/engine/classes/LocalScript.md) to receive pointer input on 3D objects through their [MouseClick](/docs/reference/engine/classes/ClickDetector.md) event. They work when parented to [BasePart](/docs/reference/engine/classes/BasePart.md), [Model](/docs/reference/engine/classes/Model.md), or [Folder](/docs/reference/engine/classes/Folder.md) objects. They detect basic mouse events: enter, leave, left click and right click. Touch input on [TouchEnabled](/docs/reference/engine/classes/UserInputService.md) devices also fires click events. The default control scripts bind [ButtonR2](/docs/reference/engine/enums/KeyCode.md) to interact with [ClickDetectors](/docs/reference/engine/classes/ClickDetector.md) using [ContextActionService:BindActivate()](/docs/reference/engine/classes/ContextActionService.md), which can also be used to override this. When using gamepads, the center dot triggers [MouseHoverEnter](/docs/reference/engine/classes/ClickDetector.md) and [MouseHoverLeave](/docs/reference/engine/classes/ClickDetector.md). The bound activation button fires [MouseClick](/docs/reference/engine/classes/ClickDetector.md). [MaxActivationDistance](/docs/reference/engine/classes/ClickDetector.md) can be used to limit the distance a player may be from a click detector before it is no longer clickable. [ClickDetector](/docs/reference/engine/classes/ClickDetector.md) events fire on both the client and the server. Since a [LocalScript](/docs/reference/engine/classes/LocalScript.md) will only run if it descends from a [Player](/docs/reference/engine/classes/Player.md) or player [Character](/docs/reference/engine/classes/Player.md), it's usually not useful to put a [LocalScript](/docs/reference/engine/classes/LocalScript.md) inside a [ClickDetector](/docs/reference/engine/classes/ClickDetector.md), since the script won't run or the object won't be clickable. If you need a [LocalScript](/docs/reference/engine/classes/LocalScript.md) to detect [ClickDetector](/docs/reference/engine/classes/ClickDetector.md) events, [StarterPlayerScripts](/docs/reference/engine/classes/StarterPlayerScripts.md) may be a better place instead. #### Input Priority If multiple [ClickDetectors](/docs/reference/engine/classes/ClickDetector.md) may detect user input, only the deepest will fire events. If two [ClickDetectors](/docs/reference/engine/classes/ClickDetector.md) are siblings, the first will take priority. If an action bound with [ContextActionService](/docs/reference/engine/classes/ContextActionService.md) uses the same input as a [ClickDetector](/docs/reference/engine/classes/ClickDetector.md), the action bound with [ContextActionService](/docs/reference/engine/classes/ContextActionService.md) will take priority over the click detector's events. [UserInputService.InputBegan](/docs/reference/engine/classes/UserInputService.md) will fire before [ClickDetector](/docs/reference/engine/classes/ClickDetector.md) events. ## Code Samples **ClickDetector Example** Place this code inside a [Script](/docs/reference/engine/classes/Script.md) inside a [ClickDetector](/docs/reference/engine/classes/ClickDetector.md). The code sample creates a reference to the parent and defines a function to show a message that greets a player. Finally, it connects the [MouseClick](/docs/reference/engine/classes/ClickDetector.md) event to the defined function. ```lua local clickDetector = script.Parent local function onClicked(player) -- Show a message to the player local msg = Instance.new("Message") msg.Parent = player:FindFirstChild("PlayerGui") msg.Text = "Hello, " .. player.Name wait(2.5) msg:Destroy() end -- Connect the function to the MouseClick event clickDetector.MouseClick:Connect(onClicked) ``` **Part Anchored Toggle** This code sample will allow a part to be clicked to toggle its anchored property. When toggled, the visual appearance of the part is updated (red means anchored, yellow means free). ```lua local part = script.Parent -- Create a ClickDetector so we can tell when the part is clicked local cd = Instance.new("ClickDetector", part) -- This function updates how the part looks based on its Anchored state local function updateVisuals() if part.Anchored then -- When the part is anchored... part.BrickColor = BrickColor.new("Bright red") part.Material = Enum.Material.DiamondPlate else -- When the part is unanchored... part.BrickColor = BrickColor.new("Bright yellow") part.Material = Enum.Material.Wood end end local function onToggle() -- Toggle the anchored property part.Anchored = not part.Anchored -- Update visual state of the brick updateVisuals() end -- Update, then start listening for clicks updateVisuals() cd.MouseClick:Connect(onToggle) ``` ## Properties ### Property: ClickDetector.CursorIcon ```json { "type": "ContentId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Image", "capabilities": [ "Input" ] } ``` Sets the cursor icon to display when the mouse is hovered over the parent of this [ClickDetector](/docs/reference/engine/classes/ClickDetector.md) or [DragDetector](/docs/reference/engine/classes/DragDetector.md). If this property is left blank, the detector will use the default icon. To change the cursor icon, set this property to the asset ID of the image you'd like to use. ### Property: ClickDetector.CursorIconContent ```json { "type": "Content", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Image", "capabilities": [ "Input" ] } ``` Sets the cursor icon to display when the mouse is hovered over the parent of this [ClickDetector](/docs/reference/engine/classes/ClickDetector.md) or [DragDetector](/docs/reference/engine/classes/DragDetector.md). If this property is left blank, the detector will use the default icon. To change the cursor icon, set this property to the asset ID of the image you'd like to use. Only asset URIs are supported for this property. ### Property: ClickDetector.MaxActivationDistance ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Input" ] } ``` This property controls the maximum distance, in studs, between a [Character](/docs/reference/engine/classes/Player.md) and the [ClickDetector](/docs/reference/engine/classes/ClickDetector.md) or [DragDetector](/docs/reference/engine/classes/DragDetector.md) for the player to be able to interact with it. For instance, a character within 10 studs of a [ClickDetector](/docs/reference/engine/classes/ClickDetector.md) or [DragDetector](/docs/reference/engine/classes/DragDetector.md) with a max activation distance of 5 would not be able to use the detector because they are out of range. ## Events ### Event: ClickDetector.MouseClick **Signature:** `ClickDetector.MouseClick(playerWhoClicked: Player)` This event fires from either a [Script](/docs/reference/engine/classes/Script.md) or [LocalScript](/docs/reference/engine/classes/LocalScript.md) when a player interacts with a [ClickDetector](/docs/reference/engine/classes/ClickDetector.md) or [DragDetector](/docs/reference/engine/classes/DragDetector.md) via the following inputs: - On platforms with a mouse, when the player left mouse clicks. - On [TouchEnabled](/docs/reference/engine/classes/UserInputService.md) platforms, when the player taps. - On [GamepadEnabled](/docs/reference/engine/classes/UserInputService.md) platforms, when the center dot is over the same model and the **A** button is pressed and released. Note that the player's [Character](/docs/reference/engine/classes/Player.md) must be within the [MaxActivationDistance](/docs/reference/engine/classes/ClickDetector.md) of the detector. *Security: None · Capabilities: Input* **Parameters:** | Name | Type | Description | |------|------|-------------| | `playerWhoClicked` | `Player` | The [Player](/docs/reference/engine/classes/Player.md) who clicked on the parent of a [ClickDetector](/docs/reference/engine/classes/ClickDetector.md) or [DragDetector](/docs/reference/engine/classes/DragDetector.md). | ### Event: ClickDetector.MouseHoverEnter **Signature:** `ClickDetector.MouseHoverEnter(playerWhoHovered: Player)` This event fires from either a [Script](/docs/reference/engine/classes/Script.md) or [LocalScript](/docs/reference/engine/classes/LocalScript.md) when the parent of a [ClickDetector](/docs/reference/engine/classes/ClickDetector.md) or [DragDetector](/docs/reference/engine/classes/DragDetector.md) is hovered over by a player. This does not entail explicit interaction with the detector, for which you can listen to either [MouseClick](/docs/reference/engine/classes/ClickDetector.md) and [RightMouseClick](/docs/reference/engine/classes/ClickDetector.md) events. Due to the nature of user input, you should not depend on all [MouseHoverEnter](/docs/reference/engine/classes/ClickDetector.md) events firing a corresponding [MouseHoverLeave](/docs/reference/engine/classes/ClickDetector.md) event. *Security: None · Capabilities: Input* **Parameters:** | Name | Type | Description | |------|------|-------------| | `playerWhoHovered` | `Player` | The [Player](/docs/reference/engine/classes/Player.md) who started hovering over the parent of a [ClickDetector](/docs/reference/engine/classes/ClickDetector.md) or [DragDetector](/docs/reference/engine/classes/DragDetector.md). | **Hovering Over and Off a ClickDetector** The following code will print "[PlayerName] hovered over my parent!" when a player's cursor hovers over the [ClickDetector](/docs/reference/engine/classes/ClickDetector.md) parent. It will also print "[PlayerName] hovered off my parent!" when the player's cursor moves off the [ClickDetector](/docs/reference/engine/classes/ClickDetector.md) parent. In order for this example to work as expected, it must be placed in a `Script` or `LocalScript` whose parent is a [ClickDetector](/docs/reference/engine/classes/ClickDetector.md). ```lua local clickDetector = script.Parent:FindFirstChildOfClass("ClickDetector") clickDetector.MouseHoverEnter:Connect(function(player) print(player.Name .. " hovered over my parent!") end) clickDetector.MouseHoverLeave:Connect(function(player) print(player.Name .. " hovered off my parent!") end) ``` ### Event: ClickDetector.MouseHoverLeave **Signature:** `ClickDetector.MouseHoverLeave(playerWhoHovered: Player)` This event fires from either a [Script](/docs/reference/engine/classes/Script.md) or [LocalScript](/docs/reference/engine/classes/LocalScript.md) when a player's cursor hovers off the parent of a [ClickDetector](/docs/reference/engine/classes/ClickDetector.md) or [DragDetector](/docs/reference/engine/classes/DragDetector.md). This does not entail explicit interaction with the detector, for which you can listen to either [MouseClick](/docs/reference/engine/classes/ClickDetector.md) and [RightMouseClick](/docs/reference/engine/classes/ClickDetector.md) events. Due to the nature of user input, you should not depend on all [MouseHoverLeave](/docs/reference/engine/classes/ClickDetector.md) events firing after a corresponding [MouseHoverEnter](/docs/reference/engine/classes/ClickDetector.md) event. *Security: None · Capabilities: Input* **Parameters:** | Name | Type | Description | |------|------|-------------| | `playerWhoHovered` | `Player` | The [Player](/docs/reference/engine/classes/Player.md) whose cursor hovered off the parent of a [ClickDetector](/docs/reference/engine/classes/ClickDetector.md) or [DragDetector](/docs/reference/engine/classes/DragDetector.md). | **Hovering Over and Off a ClickDetector** The following code will print "[PlayerName] hovered over my parent!" when a player's cursor hovers over the [ClickDetector](/docs/reference/engine/classes/ClickDetector.md) parent. It will also print "[PlayerName] hovered off my parent!" when the player's cursor moves off the [ClickDetector](/docs/reference/engine/classes/ClickDetector.md) parent. In order for this example to work as expected, it must be placed in a `Script` or `LocalScript` whose parent is a [ClickDetector](/docs/reference/engine/classes/ClickDetector.md). ```lua local clickDetector = script.Parent:FindFirstChildOfClass("ClickDetector") clickDetector.MouseHoverEnter:Connect(function(player) print(player.Name .. " hovered over my parent!") end) clickDetector.MouseHoverLeave:Connect(function(player) print(player.Name .. " hovered off my parent!") end) ``` ### Event: ClickDetector.RightMouseClick **Signature:** `ClickDetector.RightMouseClick(playerWhoClicked: Player)` This event fires from either a [Script](/docs/reference/engine/classes/Script.md) or [LocalScript](/docs/reference/engine/classes/LocalScript.md) when a player right clicks their mouse cursor on a [ClickDetector](/docs/reference/engine/classes/ClickDetector.md) or [DragDetector](/docs/reference/engine/classes/DragDetector.md). Note that the player's [Character](/docs/reference/engine/classes/Player.md) must be within the [MaxActivationDistance](/docs/reference/engine/classes/ClickDetector.md) of the detector. *Security: None · Capabilities: Input* **Parameters:** | Name | Type | Description | |------|------|-------------| | `playerWhoClicked` | `Player` | The [Player](/docs/reference/engine/classes/Player.md) who right clicked their mouse cursor on the parent of a [ClickDetector](/docs/reference/engine/classes/ClickDetector.md) or [DragDetector](/docs/reference/engine/classes/DragDetector.md). | ### Event: ClickDetector.mouseClick **Signature:** `ClickDetector.mouseClick(playerWhoClicked: Player)` > **Deprecated:** This deprecated event is a variant of [ClickDetector.MouseClick](/docs/reference/engine/classes/ClickDetector.md), which should be used instead. *Security: None · Capabilities: Input* **Parameters:** | Name | Type | Description | |------|------|-------------| | `playerWhoClicked` | `Player` | | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ClientReplicator last_updated: 2026-06-29T19:34:07Z inherits: - NetworkReplicator - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated --- # Class: ClientReplicator ## Description The ClientReplicator is in charge of replicating changes from the server over to the client. It represents the client's connection to a server. ## Inherited Members ### From [NetworkReplicator](/docs/reference/engine/classes/NetworkReplicator.md) - **Method `GetPlayer(): Instance`**: Returns the player that is connected to the NetworkReplicator. ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ClimbController last_updated: 2026-06-29T19:34:07Z inherits: - ControllerBase - Instance - Object type: class memory_category: Instances --- # Class: ClimbController ## Properties ### Property: ClimbController.AccelerationTime ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Movement", "capabilities": [ "Basic" ], "simulationAccess": true } ``` The amount of time taken to reach the desired climb velocity from 0. May be inaccurate depending on max force and gravity. ### Property: ClimbController.BalanceMaxTorque ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Balance", "capabilities": [ "Basic" ], "simulationAccess": true } ``` The maximum torque used to keep the [ControllerManager.RootPart](/docs/reference/engine/classes/ControllerManager.md) aligned upright and aligned to the [ControllerManager.ClimbSensor.HitNormal](/docs/reference/engine/classes/ControllerManager.md). When misaligned, this amount of torque is applied to reach the [BalanceSpeed](/docs/reference/engine/classes/ClimbController.md) and realign the root part. A higher torque means more force is required to cause the root part to tilt. This property is hidden and has no effect when [ControllerBase.BalanceRigidityEnabled](/docs/reference/engine/classes/ControllerBase.md) is true. ### Property: ClimbController.BalanceSpeed ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Balance", "capabilities": [ "Basic" ], "simulationAccess": true } ``` The maximum angular speed used to align the [ControllerManager.RootPart](/docs/reference/engine/classes/ControllerManager.md) upright and aligned to the [ControllerManager.ClimbSensor.HitNormal](/docs/reference/engine/classes/ControllerManager.md). A lower value means it takes longer for the root part to recover to the align position when misaligned. A higher value results in a quicker recovery. ### Property: ClimbController.MoveMaxForce ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Movement", "capabilities": [ "Basic" ], "simulationAccess": true } ``` The maximum force used by the climbing "motor" to move the [ControllerManager.RootPart](/docs/reference/engine/classes/ControllerManager.md) or keep it stationary. ## Inherited Members ### From [ControllerBase](/docs/reference/engine/classes/ControllerBase.md) - **Property `Active`** (`boolean`): - **Property `BalanceRigidityEnabled`** (`boolean`): - **Property `MoveSpeedFactor`** (`float`): The value multiplied by the ControllerManager.BaseMoveSpeed. ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Clothing last_updated: 2026-06-29T19:34:07Z inherits: - CharacterAppearance - Instance - Object type: class memory_category: Instances tags: - NotCreatable summary: "Clothing is the base class for the Shirt and Pants clothing objects." --- # Class: Clothing > Clothing is the base class for the [Shirt](/docs/reference/engine/classes/Shirt.md) and [Pants](/docs/reference/engine/classes/Pants.md) clothing > objects. ## Description The base class for clothing objects. ## Properties ### Property: Clothing.Color3 ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "AvatarAppearance" ] } ``` This property determines the colorization to be applied to the [Clothing](/docs/reference/engine/classes/Clothing.md) texture. The default colorization value is [Color3.new(1, 1, 1)](/docs/reference/engine/datatypes/Color3.md) (white), at which no colorization occurs. This property is useful for creating outfits that have many different color variations but share the same basic look, possibly for a character creator in a roleplaying game or generating a lot of different varied NPCs. See also [ShirtGraphic.Color3](/docs/reference/engine/classes/ShirtGraphic.md) which determines the colorization to be applied to the [ShirtGraphic](/docs/reference/engine/classes/ShirtGraphic.md) texture. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Clouds last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances summary: "Renders realistic clouds that drift slowly across the sky." --- # Class: Clouds > Renders realistic clouds that drift slowly across the sky. ## Description The **Clouds** object renders realistic clouds that drift slowly across the sky. Both cloud cover and density can be adjusted, as well as cloud color to achieve atmospheres like stormy skies, moody sunsets, alien worlds, etc. See the Dynamic Clouds article for a summary of properties and expected results. ## Properties ### Property: Clouds.Color ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Environment" ] } ``` This property controls the material color of cloud particles. However, cloud color is influenced by several [Lighting](/docs/reference/engine/classes/Lighting.md) and [Atmosphere](/docs/reference/engine/classes/Atmosphere.md) properties, so it is not intended as a dedicated property to simulate colored sunsets, sunrises, etc. See the Dynamic Clouds article for a summary of properties and expected results. ### Property: Clouds.Cover ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` This property defines the cloud cover within the overall skyscape layer. Valid range is from 0 to 1 (sparse cloud cover to full cloud cover). See the Dynamic Clouds article for a summary of properties and expected results. ### Property: Clouds.Density ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` This property controls the particulate density of clouds (the proportion of airborne water vapor particles at full cloud cover). See the Dynamic Clouds article for a summary of properties and expected results. ### Property: Clouds.Enabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Environment" ] } ``` This property toggles rendering of the [Clouds](/docs/reference/engine/classes/Clouds.md) object. Useful for toggling on/off different [Clouds](/docs/reference/engine/classes/Clouds.md) objects that exist in the same place. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ClusterPacketCache last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated summary: "An internal service meant to cache cluster packets." --- # Class: ClusterPacketCache > An internal service meant to cache cluster packets. ## Description An internal service meant to cache cluster packets. This service will only be present if a [NetworkServer](/docs/reference/engine/classes/NetworkServer.md) is present. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: CollectionService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service summary: "A service which manages instance collections using assigned tags." --- # Class: CollectionService > A service which manages instance collections using assigned tags. ## Description `CollectionService` manages groups (collections) of instances with **tags**. Tags are sets of strings applied to instances that replicate from the server to the client. They are also serialized when places are saved. The primary use of `CollectionService` is to register instances with specific tags that you can use to extend their behavior. If you find yourself adding the same script to many different instances, a script that uses `CollectionService` may be better. Tags can be added or removed through this class' methods such as [AddTag()](/docs/reference/engine/classes/CollectionService.md) or [RemoveTag()](/docs/reference/engine/classes/CollectionService.md). They can also be managed directly in Studio through the [Tags](/docs/en-us/studio/properties.md#instance-tags) section of an instance's properties. ##### Replication When tags replicate, **all tags on an instance replicate at the same time**. Therefore, if you set a tag on an instance from the client then add/remove a **different** tag on the same instance from the server, the client's local tags on the instance are overwritten. In [StreamingEnabled](/docs/reference/engine/classes/Workspace.md) places, instances can be unloaded as they leave the client's streamed area. If such an instance re-enters the streamed area, properties and tags will be re-synchronized from the server. This can cause changes made by [LocalScripts](/docs/reference/engine/classes/LocalScript.md) to be overwritten/removed. ## Methods ### Method: CollectionService:AddTag **Signature:** `CollectionService:AddTag(instance: Instance, tag: string): ()` This method applies a tag to an [Instance](/docs/reference/engine/classes/Instance.md), doing nothing if the tag is already applied to that instance. Successfully adding a tag will fire a signal created by [GetInstanceAddedSignal()](/docs/reference/engine/classes/CollectionService.md) with the given tag. ##### Warnings - An instance's tags that were added client-side will be dropped if the server later adds or removes a tag on that instance because the server replicates all tags together and overwrites previous tags. - When tagging an instance, it is common that some resources are used to give the tag its functionality, for example event connections or tables. To prevent memory leaks, it's a good idea to clean these up (disconnect, set to `nil`, etc.) when no longer needed for a tag. Do this when calling [RemoveTag()](/docs/reference/engine/classes/CollectionService.md), calling [Instance:Destroy()](/docs/reference/engine/classes/Instance.md) or in a function connected to a signal returned by [GetInstanceRemovedSignal()](/docs/reference/engine/classes/CollectionService.md). *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `instance` | `Instance` | | | | `tag` | `string` | | | **Returns:** `()` ### Method: CollectionService:GetAllTags **Signature:** `CollectionService:GetAllTags(): Array` Returns an array of all tags in the experience. *Security: None · Thread Safety: Safe · Capabilities: Basic* **Returns:** `Array` ### Method: CollectionService:GetInstanceAddedSignal **Signature:** `CollectionService:GetInstanceAddedSignal(tag: string): RBXScriptSignal` Given a tag (string), this method returns a signal which fires under two conditions: - The tag is assigned to an instance within the [DataModel](/docs/reference/engine/classes/DataModel.md) using [CollectionService:AddTag()](/docs/reference/engine/classes/CollectionService.md) or [Instance:AddTag()](/docs/reference/engine/classes/Instance.md). - An instance with the given tag is added as a descendant of the [DataModel](/docs/reference/engine/classes/DataModel.md), for example by setting [Instance.Parent](/docs/reference/engine/classes/Instance.md) or similar. Subsequent calls to this method with the same tag return the same signal object. Consider also calling [GetTagged()](/docs/reference/engine/classes/CollectionService.md) to get a list of instances that already have a tag (and thus won't fire the event if they already are in the [DataModel](/docs/reference/engine/classes/DataModel.md)). See also [GetInstanceRemovedSignal()](/docs/reference/engine/classes/CollectionService.md) which returns an event that fires under similar conditions. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `tag` | `string` | | The tag to watch for. | **Returns:** `RBXScriptSignal` — An event that fires when you add the tag to an instance. **Deadly Bricks using CollectionService** This code sample causes any [BasePart](/docs/reference/engine/classes/BasePart.md) with the tag `Deadly` to kill any [Humanoid](/docs/reference/engine/classes/Humanoid.md) that touches it. It does this using a common pattern with [CollectionService](/docs/reference/engine/classes/CollectionService.md) to listen for all parts with the tag and make a connection, then disconnect the connection when the tag is removed. ```lua local CollectionService = game:GetService("CollectionService") local tag = "Deadly" local function onDeadlyPartTouched(otherPart) if not otherPart.Parent then return end local humanoid = otherPart.Parent:FindFirstChildOfClass("Humanoid") if humanoid then humanoid.Health = 0 end end -- Save the connections so they can be disconnected when the tag is removed local connections = {} local function onInstanceAdded(object) -- Confirm that the object with this tag is a BasePart if object:IsA("BasePart") then connections[object] = object.Touched:Connect(onDeadlyPartTouched) end end local function onInstanceRemoved(object) -- If there is a stored connection on this object, disconnect/remove it if connections[object] then connections[object]:Disconnect() connections[object] = nil end end -- Listen for this tag being applied to objects CollectionService:GetInstanceAddedSignal(tag):Connect(onInstanceAdded) CollectionService:GetInstanceRemovedSignal(tag):Connect(onInstanceRemoved) -- Also detect any objects that already have the tag for _, object in pairs(CollectionService:GetTagged(tag)) do onInstanceAdded(object) end ``` **Expected output:** Try tagging a part using `CollectionService` by pasting this into the Command bar: `game:GetService("CollectionService"):AddTag(workspace.Part, "Deadly")` Then, when you press Play and touch the brick with your character they should die. ### Method: CollectionService:GetInstanceRemovedSignal **Signature:** `CollectionService:GetInstanceRemovedSignal(tag: string): RBXScriptSignal` Given a tag (string), this method returns a signal which fires under two conditions: - The tag is removed from an instance within the [DataModel](/docs/reference/engine/classes/DataModel.md) using [CollectionService:RemoveTag()](/docs/reference/engine/classes/CollectionService.md) or [Instance:RemoveTag()](/docs/reference/engine/classes/Instance.md). - An instance with the given tag is removed as a descendant of the [DataModel](/docs/reference/engine/classes/DataModel.md), for example by un‑setting [Instance.Parent](/docs/reference/engine/classes/Instance.md) or similar. Subsequent calls to this method with the same tag return the same signal object. The signal is useful for cleaning up resources used by instances that once had tags, such as disconnecting connections. See also [GetInstanceAddedSignal()](/docs/reference/engine/classes/CollectionService.md) which returns an event that fires under similar conditions. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `tag` | `string` | | The tag to watch for. | **Returns:** `RBXScriptSignal` — An event that fires when you remove the tag from an instance. **Deadly Bricks using CollectionService** This code sample causes any [BasePart](/docs/reference/engine/classes/BasePart.md) with the tag `Deadly` to kill any [Humanoid](/docs/reference/engine/classes/Humanoid.md) that touches it. It does this using a common pattern with [CollectionService](/docs/reference/engine/classes/CollectionService.md) to listen for all parts with the tag and make a connection, then disconnect the connection when the tag is removed. ```lua local CollectionService = game:GetService("CollectionService") local tag = "Deadly" local function onDeadlyPartTouched(otherPart) if not otherPart.Parent then return end local humanoid = otherPart.Parent:FindFirstChildOfClass("Humanoid") if humanoid then humanoid.Health = 0 end end -- Save the connections so they can be disconnected when the tag is removed local connections = {} local function onInstanceAdded(object) -- Confirm that the object with this tag is a BasePart if object:IsA("BasePart") then connections[object] = object.Touched:Connect(onDeadlyPartTouched) end end local function onInstanceRemoved(object) -- If there is a stored connection on this object, disconnect/remove it if connections[object] then connections[object]:Disconnect() connections[object] = nil end end -- Listen for this tag being applied to objects CollectionService:GetInstanceAddedSignal(tag):Connect(onInstanceAdded) CollectionService:GetInstanceRemovedSignal(tag):Connect(onInstanceRemoved) -- Also detect any objects that already have the tag for _, object in pairs(CollectionService:GetTagged(tag)) do onInstanceAdded(object) end ``` **Expected output:** Try tagging a part using `CollectionService` by pasting this into the Command bar: `game:GetService("CollectionService"):AddTag(workspace.Part, "Deadly")` Then, when you press Play and touch the brick with your character they should die. ### Method: CollectionService:GetTagged **Signature:** `CollectionService:GetTagged(tag: string): Instances` This method returns an array of instances with a given tag which are descendants of the [DataModel](/docs/reference/engine/classes/DataModel.md). Removing a tag using [CollectionService:RemoveTag()](/docs/reference/engine/classes/CollectionService.md) or [Instance:RemoveTag()](/docs/reference/engine/classes/Instance.md) ensures this method does not return them. If you want to detect all instances with a tag, both present **and** future, use this method to iterate over instances while also making a connection to a signal returned by [GetInstanceAddedSignal()](/docs/reference/engine/classes/CollectionService.md). This method does not guarantee any ordering of the returned instances. Additionally, it's possible that instances can have the given tag assigned to them but not be a descendant of the [DataModel](/docs/reference/engine/classes/DataModel.md), for example its parent is `nil`; this method will not return such instances. *Security: None · Thread Safety: Safe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `tag` | `string` | | The tag to search for. | **Returns:** `Instances` — An array of all instances with the tag. **Deadly Bricks using CollectionService** This code sample causes any [BasePart](/docs/reference/engine/classes/BasePart.md) with the tag `Deadly` to kill any [Humanoid](/docs/reference/engine/classes/Humanoid.md) that touches it. It does this using a common pattern with [CollectionService](/docs/reference/engine/classes/CollectionService.md) to listen for all parts with the tag and make a connection, then disconnect the connection when the tag is removed. ```lua local CollectionService = game:GetService("CollectionService") local tag = "Deadly" local function onDeadlyPartTouched(otherPart) if not otherPart.Parent then return end local humanoid = otherPart.Parent:FindFirstChildOfClass("Humanoid") if humanoid then humanoid.Health = 0 end end -- Save the connections so they can be disconnected when the tag is removed local connections = {} local function onInstanceAdded(object) -- Confirm that the object with this tag is a BasePart if object:IsA("BasePart") then connections[object] = object.Touched:Connect(onDeadlyPartTouched) end end local function onInstanceRemoved(object) -- If there is a stored connection on this object, disconnect/remove it if connections[object] then connections[object]:Disconnect() connections[object] = nil end end -- Listen for this tag being applied to objects CollectionService:GetInstanceAddedSignal(tag):Connect(onInstanceAdded) CollectionService:GetInstanceRemovedSignal(tag):Connect(onInstanceRemoved) -- Also detect any objects that already have the tag for _, object in pairs(CollectionService:GetTagged(tag)) do onInstanceAdded(object) end ``` **Expected output:** Try tagging a part using `CollectionService` by pasting this into the Command bar: `game:GetService("CollectionService"):AddTag(workspace.Part, "Deadly")` Then, when you press Play and touch the brick with your character they should die. ### Method: CollectionService:GetTags **Signature:** `CollectionService:GetTags(instance: Instance): Array` Given an [Instance](/docs/reference/engine/classes/Instance.md), this method returns an array of strings which are the tags applied to the instance. This method is useful when you want to do something with multiple instance tags at once, but it's inefficient to check for the existence of a single tag. For this, use [HasTag()](/docs/reference/engine/classes/CollectionService.md) to check for a single tag. *Security: None · Thread Safety: Safe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `instance` | `Instance` | | The instance whose tags should be returned. | **Returns:** `Array` — An array of strings which are the tags applied to the given instance. **Using Tags and CollectionService** This code sample demonstrates adding, removing and querying a tag from an object using [CollectionService](/docs/reference/engine/classes/CollectionService.md). ```lua local CollectionService = game:GetService("CollectionService") local Workspace = game:GetService("Workspace") local object = Workspace.Part -- Add a tag CollectionService:AddTag(object, "Deadly") -- Query for a tag if CollectionService:HasTag(object, "Deadly") then print(object:GetFullName() .. " is deadly") end -- List tags on an object local tags = CollectionService:GetTags(object) print("The object " .. object:GetFullName() .. " has tags: " .. table.concat(tags, ", ")) -- Remove a tag CollectionService:RemoveTag(object, "Deadly") ``` **Expected output:** When run, you should see "Workspace.Part is deadly" followed by "The object Workspace.Part has tags: Deadly" in the Output. ### Method: CollectionService:HasTag **Signature:** `CollectionService:HasTag(instance: Instance, tag: string): boolean` This method returns whether a given [Instance](/docs/reference/engine/classes/Instance.md) has a tag. By extension, any tags returned by a call to [GetTags()](/docs/reference/engine/classes/CollectionService.md) on an instance will return `true` when used with this method. *Security: None · Thread Safety: Safe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `instance` | `Instance` | | The instance to check for the presence of a tag. | | `tag` | `string` | | The tag to check for. | **Returns:** `boolean` — Whether the instance has the tag. **Using Tags and CollectionService** This code sample demonstrates adding, removing and querying a tag from an object using [CollectionService](/docs/reference/engine/classes/CollectionService.md). ```lua local CollectionService = game:GetService("CollectionService") local Workspace = game:GetService("Workspace") local object = Workspace.Part -- Add a tag CollectionService:AddTag(object, "Deadly") -- Query for a tag if CollectionService:HasTag(object, "Deadly") then print(object:GetFullName() .. " is deadly") end -- List tags on an object local tags = CollectionService:GetTags(object) print("The object " .. object:GetFullName() .. " has tags: " .. table.concat(tags, ", ")) -- Remove a tag CollectionService:RemoveTag(object, "Deadly") ``` **Expected output:** When run, you should see "Workspace.Part is deadly" followed by "The object Workspace.Part has tags: Deadly" in the Output. ### Method: CollectionService:RemoveTag **Signature:** `CollectionService:RemoveTag(instance: Instance, tag: string): ()` This method removes a tag from an instance. Successfully removing a tag will fire a signal created by [GetInstanceRemovedSignal()](/docs/reference/engine/classes/CollectionService.md) with the given tag. When removing a tag, it's common that some resources are used to give the tag its functionality, for example event connections or tables. To prevent memory leaks, it's a good idea to clean these up (disconnect, set to `nil`, etc.) when no longer needed for a tag. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `instance` | `Instance` | | The instance to remove the tag from. | | `tag` | `string` | | The tag to remove from the instance. | **Returns:** `()` **Using Tags and CollectionService** This code sample demonstrates adding, removing and querying a tag from an object using [CollectionService](/docs/reference/engine/classes/CollectionService.md). ```lua local CollectionService = game:GetService("CollectionService") local Workspace = game:GetService("Workspace") local object = Workspace.Part -- Add a tag CollectionService:AddTag(object, "Deadly") -- Query for a tag if CollectionService:HasTag(object, "Deadly") then print(object:GetFullName() .. " is deadly") end -- List tags on an object local tags = CollectionService:GetTags(object) print("The object " .. object:GetFullName() .. " has tags: " .. table.concat(tags, ", ")) -- Remove a tag CollectionService:RemoveTag(object, "Deadly") ``` **Expected output:** When run, you should see "Workspace.Part is deadly" followed by "The object Workspace.Part has tags: Deadly" in the Output. ### Method: CollectionService:GetCollection **Signature:** `CollectionService:GetCollection(class: string): Instances` > **Deprecated:** This item has been superseded by a [CollectionService](/docs/reference/engine/classes/CollectionService.md) tagging method. The equivalent function using the new method is [CollectionService:GetTagged()](/docs/reference/engine/classes/CollectionService.md) which should be used in new work. This function returns all instances of a given class which are in the [DataModel](/docs/reference/engine/classes/DataModel.md). Only works for [Configuration](/docs/reference/engine/classes/Configuration.md), [CustomEvent](/docs/reference/engine/classes/CustomEvent.md), [CustomEventReceiver](/docs/reference/engine/classes/CustomEventReceiver.md), [Dialog](/docs/reference/engine/classes/Dialog.md), and [VehicleSeat](/docs/reference/engine/classes/VehicleSeat.md). *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `class` | `string` | | | **Returns:** `Instances` **CollectionService:GetCollection1** ```lua local CollectionService = game:GetService("CollectionService") for _index = 1, 10 do local configuration = Instance.new("Configuration") configuration.Parent = workspace end local objects = CollectionService:GetCollection("Configuration") print(#objects) ``` ## Events ### Event: CollectionService.TagAdded **Signature:** `CollectionService.TagAdded(tag: string)` This event fires when a tag is added to an instance and the added tag is the only occurrence of that tag in the place. *Security: None · Capabilities: Basic* **Parameters:** | Name | Type | Description | |------|------|-------------| | `tag` | `string` | | ### Event: CollectionService.TagRemoved **Signature:** `CollectionService.TagRemoved(tag: string)` This event fires when a tag is removed from an instance and the removed tag is no longer used anywhere in the place. *Security: None · Capabilities: Basic* **Parameters:** | Name | Type | Description | |------|------|-------------| | `tag` | `string` | | ### Event: CollectionService.ItemAdded **Signature:** `CollectionService.ItemAdded(instance: Instance)` > **Deprecated:** This item has been superseded by a [CollectionService](/docs/reference/engine/classes/CollectionService.md) tagging method. There is currently no means of checking when a tag is added. This function fires when a [Configuration](/docs/reference/engine/classes/Configuration.md), [CustomEvent](/docs/reference/engine/classes/CustomEvent.md), [CustomEventReceiver](/docs/reference/engine/classes/CustomEventReceiver.md), [Dialog](/docs/reference/engine/classes/Dialog.md), or [VehicleSeat](/docs/reference/engine/classes/VehicleSeat.md) is added to the [DataModel](/docs/reference/engine/classes/DataModel.md). *Security: None · Capabilities: Basic* **Parameters:** | Name | Type | Description | |------|------|-------------| | `instance` | `Instance` | | **CollectionService.ItemAdded1** ```lua local CollectionService = game:GetService("CollectionService") CollectionService.ItemAdded:Connect(function(instance) print(instance.ClassName .. " added to DataModel") end) local part = Instance.new("Part") part.Parent = workspace --// Doesn't fire because CollectionService doesn't watch Parts local dialog = Instance.new("Dialog") dialog.Parent = workspace --> Dialog added to DataModel ``` ### Event: CollectionService.ItemRemoved **Signature:** `CollectionService.ItemRemoved(instance: Instance)` > **Deprecated:** This item has been superseded by a [CollectionService](/docs/reference/engine/classes/CollectionService.md) tagging method. There is currently no means of checking when a tag is removed. This function fires when a [Configuration](/docs/reference/engine/classes/Configuration.md), [CustomEvent](/docs/reference/engine/classes/CustomEvent.md), [CustomEventReceiver](/docs/reference/engine/classes/CustomEventReceiver.md), [Dialog](/docs/reference/engine/classes/Dialog.md), or [VehicleSeat](/docs/reference/engine/classes/VehicleSeat.md) is removed from the [DataModel](/docs/reference/engine/classes/DataModel.md). *Security: None · Capabilities: Basic* **Parameters:** | Name | Type | Description | |------|------|-------------| | `instance` | `Instance` | | **CollectionService.ItemRemoved1** ```lua local CollectionService = game:GetService("CollectionService") CollectionService.ItemRemoved:Connect(function(instance) print(instance.ClassName .. " removed from the DataModel") end) local part = Instance.new("Part") part.Parent = workspace part:Destroy() --// Doesn't fire because CollectionService ignores Parts local dialog = Instance.new("Dialog") dialog.Parent = workspace dialog:Destroy() --> Dialog removed from the DataModel ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Color3Value last_updated: 2026-06-29T19:34:07Z inherits: - ValueBase - Instance - Object type: class memory_category: Instances summary: "A container object for a single Color3 value." --- # Class: Color3Value > A container object for a single Color3 value. ## Description A container object for a single [Color3](/docs/reference/engine/datatypes/Color3.md) value. ## Code Samples **Set Color3Value** This code sample sets the Value property of a Color3Value to Red. ```lua local myColor3Value = script.Parent myColor3Value.Value = Color3.new(1, 0, 0) -- Red -- You can also store the color of a BrickColor value by accessing BrickColor's Color property, which is a Color3: local someBrickColor = BrickColor.new("Really red") myColor3Value.Value = someBrickColor.Color ``` ## Properties ### Property: Color3Value.Value ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "simulationAccess": true } ``` The stored [Color3](/docs/reference/engine/datatypes/Color3.md). ## Events ### Event: Color3Value.Changed **Signature:** `Color3Value.Changed(value: Color3)` Fired whenever the [Color3Value.Value](/docs/reference/engine/classes/Color3Value.md) of the [Color3Value](/docs/reference/engine/classes/Color3Value.md) is changed. It will run with the new value being stored in the argument object, instead of a string representing the property being changed. This event, like other changed events, can be used to track when an Color3Value changes and to track the different values that it may change to. For instance, this may be useful in games that rely on Color3Values to track values such as colors for games using customizable outfits or items. Equivalent changed events exist for similar objects, such as [NumberValue](/docs/reference/engine/classes/NumberValue.md) and [StringValue](/docs/reference/engine/classes/StringValue.md), depending on what object type best suits the need. *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `value` | `Color3` | The new value after the change. | **How to Use Color3Value.Changed** The below example, assuming all referenced objects existed, would print the Color3Value's new value each time it changed. In the example below it would print _"0.196078, 0.196078, 0.196078"_. ```lua local value = Instance.new("Color3Value") value.Parent = workspace value.Changed:Connect(function(NewValue) print(NewValue) end) value.Value = Color3.fromRGB(50, 50, 50) ``` **Expected output:** "0.196078, 0.196078, 0.196078" ### Event: Color3Value.changed **Signature:** `Color3Value.changed(value: Color3)` > **Deprecated:** This deprecated event is a variant of [Color3Value.Changed](/docs/reference/engine/classes/Color3Value.md) which should be used instead. *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `value` | `Color3` | | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ColorCorrectionEffect last_updated: 2026-06-29T19:34:07Z inherits: - PostEffect - Instance - Object type: class memory_category: Instances summary: "Adjusts color-related properties of the rendered world like saturation, tint, brightness, and contrast." --- # Class: ColorCorrectionEffect > Adjusts color-related properties of the rendered world like saturation, tint, > brightness, and contrast. ## Description [ColorCorrectionEffect](/docs/reference/engine/classes/ColorCorrectionEffect.md) can be used to adjust several color-related properties at once, including [Saturation](/docs/reference/engine/classes/ColorCorrectionEffect.md), [TintColor](/docs/reference/engine/classes/ColorCorrectionEffect.md), [Brightness](/docs/reference/engine/classes/ColorCorrectionEffect.md) and [Contrast](/docs/reference/engine/classes/ColorCorrectionEffect.md). It's useful for fine-tuning the visual aesthetic of a world or communicating status effects to the player. Multiple [ColorCorrectionEffect](/docs/reference/engine/classes/ColorCorrectionEffect.md) objects can be applied at the same time and they will compose their effects together. Like other [post-processing effects](/docs/en-us/environment/post-processing-effects.md), [ColorCorrectionEffect](/docs/reference/engine/classes/ColorCorrectionEffect.md) will only work while [Enabled](/docs/reference/engine/classes/PostEffect.md) and when parented to [Lighting](/docs/reference/engine/classes/Lighting.md) or [Workspace.CurrentCamera](/docs/reference/engine/classes/Workspace.md). Also, it may render differently depending on your Studio settings (see **Editor Quality Level** in **Rendering**). ## Properties ### Property: ColorCorrectionEffect.Brightness ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Environment" ] } ``` This property determines by how much the colors of pixels will be shifted. A value of `-1` will cause all pixels to be completely black while a value of `1` will cause them to be white. ### Property: ColorCorrectionEffect.Contrast ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Environment" ] } ``` This property determines the separation between the dark and light colors. Values less than `0` have reduced contrast while values greater than `0` have increased contrast. ### Property: ColorCorrectionEffect.Saturation ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Environment" ] } ``` This property determines the change in intensity of pixel colors. Values above `1` will cause colors to be more vivid while values below `0` will make colors more dull, eventually reaching full desaturation at `-1`. ### Property: ColorCorrectionEffect.TintColor ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Environment" ] } ``` This property determines by what factors the RGB channels of pixel colors are scaled. The effect is multiplicative, so changing this to `[255, 0, 0]` (red) would cause the green and blue channels to be multiplied by `0`. ## Inherited Members ### From [PostEffect](/docs/reference/engine/classes/PostEffect.md) - **Property `Enabled`** (`boolean`): Toggles whether or not the PostEffect is enabled. ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ColorGradingEffect last_updated: 2026-06-29T19:34:07Z inherits: - PostEffect - Instance - Object type: class memory_category: Instances summary: "Modifies how color values calculated by the renderer should be converted to the screen's color range." --- # Class: ColorGradingEffect > Modifies how color values calculated by the renderer should be converted to > the screen's color range. ## Description The **ColorGradingEffect** effect modifies how color values calculated by the renderer should be converted to the screen's color range, impacting the mood and appearance of your place. **ColorGradingEffect** is expected to be parented to [Lighting](/docs/reference/engine/classes/Lighting.md) and will be ignored if parented elsewhere. Multiple instances cannot be combined and only the most recently parented instance to [Lighting](/docs/reference/engine/classes/Lighting.md) will be applied. For more details on this effect and others, see [Post-Processing Effects](/docs/en-us/environment/post-processing-effects.md). ## Properties ### Property: ColorGradingEffect.TonemapperPreset ```json { "type": "TonemapperPreset", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Environment" ] } ``` Specifies which [TonemapperPreset](/docs/reference/engine/enums/TonemapperPreset.md) to use. [TonemapperPreset.Retro](/docs/reference/engine/enums/TonemapperPreset.md) is meant to imitate the look of the pre‑2019 Roblox lighting system. ## Inherited Members ### From [PostEffect](/docs/reference/engine/classes/PostEffect.md) - **Property `Enabled`** (`boolean`): Toggles whether or not the PostEffect is enabled. ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: CommerceService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service summary: "Supports real-world purchases that you can bundle with digital benefits." --- # Class: CommerceService > Supports real-world purchases that you can bundle with digital benefits. ## Description [CommerceService](/docs/reference/engine/classes/CommerceService.md) is a service that supports real-world purchases that you can bundle with virtual items. For information on eligibility and implementation, see [Commerce products](/docs/en-us/production/monetization/commerce-products.md). ## Methods ### Method: CommerceService:GetCommerceProductInfoAsync **Signature:** `CommerceService:GetCommerceProductInfoAsync(commerceProductId: string): Dictionary` Retrieves information about the products that you are selling and surface them within your experience. How you surface products to your users is entirely up to you. | Name | string | Localized name of of the physical item | | --- | --- | --- | | Description | string | Localized description of the physical item | | IconImageAssetId | number | The image asset ID of main default image of the physical item | | DisplayPrice | string | Localized price string with currency symbol of the physical item. e.g. “$4.99“ | | IsPurchasable | bool | If the item can be added to a merchant checkout session, i.e. item is in stock, or can be backordered | *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Monetization* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `commerceProductId` | `string` | | | **Returns:** `Dictionary` ### Method: CommerceService:PromptCommerceProductPurchase **Signature:** `CommerceService:PromptCommerceProductPurchase(user: Player, commerceProductId: string): ()` Prompts a user to purchase a commerce product using the provided `commerceProductId`. Opens a webview that guides the user through the purchasing flow. *Security: None · Thread Safety: Unsafe · Capabilities: Monetization* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `user` | `Player` | | | | `commerceProductId` | `string` | | | **Returns:** `()` ### Method: CommerceService:PromptRealWorldCommerceBrowser **Signature:** `CommerceService:PromptRealWorldCommerceBrowser(player: Player, url: string): ()` This is a legacy endpoint that is not meant for use. To open the webview to the purchasing flow for real world commerce, see [CommerceService:PromptCommerceProductPurchase](/docs/reference/engine/classes/CommerceService.md). For more information, see [Commerce products](/docs/en-us/production/monetization/commerce-products.md) *Security: None · Thread Safety: Unsafe · Capabilities: Monetization* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Player` | | | | `url` | `string` | | | **Returns:** `()` ### Method: CommerceService:UserEligibleForRealWorldCommerceAsync **Signature:** `CommerceService:UserEligibleForRealWorldCommerceAsync(): boolean` This is a legacy endpoint that is not meant for use. To check if a user is eligible for real world commerce, see [PolicyService.IsEligibleToPurchaseCommerceProduct](/docs/reference/engine/classes/PolicyService.md). For more information, see [Commerce products](/docs/en-us/production/monetization/commerce-products.md) *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Monetization* **Returns:** `boolean` ## Events ### Event: CommerceService.PromptCommerceProductPurchaseFinished **Signature:** `CommerceService.PromptCommerceProductPurchaseFinished(user: Player, productId: string)` Use this signal to detect when a user has completed the purchasing flow and the webview has closed to resume gameplay within the experience. **This signal does not indicate a successful purchase**, so do not grant virtual items solely from this signal. While optional, it is recommended to use this signal to reorient your users on Android, as the commerce purchasing flow will have forced them into portrait mode. *Security: None · Capabilities: Monetization* **Parameters:** | Name | Type | Description | |------|------|-------------| | `user` | `Player` | | | `productId` | `string` | | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: CompositeValueCurve last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances summary: "An animation curve that groups child FloatCurves which each animate a different component of a non-unary value." --- # Class: CompositeValueCurve > An animation curve that groups child [FloatCurves](/docs/reference/engine/classes/FloatCurve.md) which each > animate a different component of a non-unary value. ## Description An animation curve that groups child [FloatCurves](/docs/reference/engine/classes/FloatCurve.md) which each animate a different component of a non-unary value. The [CurveType](/docs/reference/engine/classes/CompositeValueCurve.md) property specifies the type of value to be animated and, depending on this value, differently named children are used to drive the animation of the components of the value. As follows are the names of the child curves that drive the animation for each possible [CompositeValueCurveType](/docs/reference/engine/enums/CompositeValueCurveType.md) value for [CurveType](/docs/reference/engine/classes/CompositeValueCurve.md): - [ColorRGB](/docs/reference/engine/enums/CompositeValueCurveType.md): { `"R"`, `"G"`, `"B"` } - [ColorHSV](/docs/reference/engine/enums/CompositeValueCurveType.md): { `"H"`, `"S"`, `"V"` } - [NumberRange](/docs/reference/engine/enums/CompositeValueCurveType.md): { `"Min"`, `"Max"` } - [Rect](/docs/reference/engine/enums/CompositeValueCurveType.md): { `"MinX"`, `"MaxX"`, `"MinY"`, `"MaxY"` } - [UDim](/docs/reference/engine/enums/CompositeValueCurveType.md): { `"Scale"`, `"Offset"` } - [UDim2](/docs/reference/engine/enums/CompositeValueCurveType.md): { `"ScaleX"`, `"OffsetX"`, `"ScaleY"`, `"OffsetY"` } - [Vector2](/docs/reference/engine/enums/CompositeValueCurveType.md): { `"X"`, `"Y"`} - [Vector3](/docs/reference/engine/enums/CompositeValueCurveType.md): { `"X"`, `"Y"`, `"Z"` } The children that drive the animation can be accessed via the [GetComponentCurves()](/docs/reference/engine/classes/CompositeValueCurve.md) method which returns an array of curves in the order specified above. The value of the curve at a given time in the animation may be sampled by the [GetValueAtTime()](/docs/reference/engine/classes/CompositeValueCurve.md) method. ## Properties ### Property: CompositeValueCurve.CurveType ```json { "type": "CompositeValueCurveType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` The type of value animated by this [CompositeValueCurve](/docs/reference/engine/classes/CompositeValueCurve.md). See [CompositeValueCurveType](/docs/reference/engine/enums/CompositeValueCurveType.md) for options. ## Methods ### Method: CompositeValueCurve:GetComponentCurves **Signature:** `CompositeValueCurve:GetComponentCurves(): List` This method returns the child curves with the given names for the [CurveType](/docs/reference/engine/classes/CompositeValueCurve.md) of this [CompositeValueCurve](/docs/reference/engine/classes/CompositeValueCurve.md), in the order given by the list of child curves outlined at the top of this page. Any curve that does not exist prior to calling this method will be created and added as a child with the appropriate name. That new curve will be an empty [FloatCurve](/docs/reference/engine/classes/FloatCurve.md). *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Returns:** `List` ### Method: CompositeValueCurve:GetValueAtTime **Signature:** `CompositeValueCurve:GetValueAtTime(time: float): Variant` This method returns the sampled animated value at the passed `time` argument. The type of the returned value will match that specified by the [CurveType](/docs/reference/engine/classes/CompositeValueCurve.md) property. If any child curve is missing from the children of the curve, the corresponding value in the returned property will be `0`. For example, if a curve has a [CurveType](/docs/reference/engine/classes/CompositeValueCurve.md) of [CompositeValueCurveType.ColorRGB](/docs/reference/engine/enums/CompositeValueCurveType.md) but only one [FloatCurve](/docs/reference/engine/classes/FloatCurve.md) child named `"G"`, then the returned [FloatCurve](/docs/reference/engine/classes/FloatCurve.md) will have values of `0` for `"R"` and `"B"`, but the `"G"` value will be sampled from the child curve. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `time` | `float` | | Time at which to get the value. | **Returns:** `Variant` — The value of the curve at the passed time. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: CompressorSoundEffect last_updated: 2026-06-29T19:34:07Z inherits: - SoundEffect - Instance - Object type: class memory_category: Instances summary: "Adjusts the dynamic range of audio." --- # Class: CompressorSoundEffect > Adjusts the dynamic range of audio. ## Description A [CompressorSoundEffect](/docs/reference/engine/classes/CompressorSoundEffect.md) is used to reduce the dynamic range of audio by moving the highs and lows of a signal closer together. It does this by lowering the volume of the highest parts of a source while at the same time raising the overall volume. This type of effect is useful when you have many sounds playing and you want to make sure the quieter ones are still audible. This effect can be applied to either an individual [Sound](/docs/reference/engine/classes/Sound.md) or [SoundGroup](/docs/reference/engine/classes/SoundGroup.md) by parenting it to the desired instance. A compressor has several properties which determine how it works. The [Threshold](/docs/reference/engine/classes/CompressorSoundEffect.md) is the audio level where the compressor will start to lower the volume. As soon as the source goes below the threshold, the compressor will stop lowering the volume. The [Attack](/docs/reference/engine/classes/CompressorSoundEffect.md) determines how long it takes for the compressed effect to fully apply. After the threshold has been crossed the compressor will lower the volume over time until the desired ratio has been reached. It will take the time specified by [Attack](/docs/reference/engine/classes/CompressorSoundEffect.md) to reach this ratio. The [Release](/docs/reference/engine/classes/CompressorSoundEffect.md) determines how long it takes for the compressor to remove its effect. After the volume of the source is under the threshold, the compressor will restore the volume back to the original over the time specified by [Release](/docs/reference/engine/classes/CompressorSoundEffect.md). Along with lowering the volume when the sound has passed the threshold, a compressor will also amplify the entire sound (after any threshold lowering has taken effect). This allows quieter sounds to be amplified while louder sounds can stay about the same. The [GainMakeup](/docs/reference/engine/classes/CompressorSoundEffect.md) determines how much the effect amplifies the sound. ## Properties ### Property: CompressorSoundEffect.Attack ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "LegacySound" ] } ``` The time the effect takes to become active after its [Threshold](/docs/reference/engine/classes/CompressorSoundEffect.md) has been reached. Range is `0.1` to `1` (default `0.1`), measured in seconds. ### Property: CompressorSoundEffect.GainMakeup ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "LegacySound" ] } ``` The overall amplification applied to the effect's [Sound](/docs/reference/engine/classes/Sound.md) or [SoundGroup](/docs/reference/engine/classes/SoundGroup.md) after attenuation of sounds above the threshold. Keep in mind this amplification will occur as long as the effect is active, regardless of whether the [Threshold](/docs/reference/engine/classes/CompressorSoundEffect.md) has been reached or not. Range is `0` to `30` (default `0`), measured in dB. ### Property: CompressorSoundEffect.Ratio ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "LegacySound" ] } ``` The ratio between the [SideChain](/docs/reference/engine/classes/CompressorSoundEffect.md) sound effect, and this sound effect. ### Property: CompressorSoundEffect.Release ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "LegacySound" ] } ``` The time the effect takes to become inactive after its sound is below the [Threshold](/docs/reference/engine/classes/CompressorSoundEffect.md). Range is `0` to `5` (default `0.1`), measured in seconds. ### Property: CompressorSoundEffect.SideChain ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "LegacySound" ] } ``` Applies a ducking effect to the compressor sound effect. The behavior of the sidechain depends on the [Sound](/docs/reference/engine/classes/Sound.md) or [SoundGroup](/docs/reference/engine/classes/SoundGroup.md) linked to it. ### Property: CompressorSoundEffect.Threshold ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "LegacySound" ] } ``` Volume level at which point the compressor applies its effect. If the effect's [Sound](/docs/reference/engine/classes/Sound.md) or [SoundGroup](/docs/reference/engine/classes/SoundGroup.md) is below the effect will not attenuate the sound, although the [GainMakeup](/docs/reference/engine/classes/CompressorSoundEffect.md) will still be applied. Range is `-80` to `0` (default `0`), measured in dB. ## Inherited Members ### From [SoundEffect](/docs/reference/engine/classes/SoundEffect.md) - **Property `Enabled`** (`boolean`): Toggles the effect on and off. - **Property `Priority`** (`int`): Determines the order the effect will be applied in relation to other ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ConeHandleAdornment last_updated: 2026-06-29T19:34:07Z inherits: - HandleAdornment - PVAdornment - GuiBase3d - GuiBase - Instance - Object type: class memory_category: Instances summary: "A cone-shaped handle that can be adorned to a BasePart." --- # Class: ConeHandleAdornment > A cone-shaped handle that can be adorned to a [BasePart](/docs/reference/engine/classes/BasePart.md). ## Description `ConeHandleAdornment` is a cone-shaped handle that can be adorned to a [BasePart](/docs/reference/engine/classes/BasePart.md). If parented to a player's [PlayerGui](/docs/reference/engine/classes/PlayerGui.md) or the [CoreGui](/docs/reference/engine/classes/CoreGui.md), handles can listen to input events for purposes such as making dragger tools. ## Properties ### Property: ConeHandleAdornment.Height ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic", "UI" ] } ``` The height of the cone adornment in studs. ### Property: ConeHandleAdornment.Hollow ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic", "UI" ] } ``` ### Property: ConeHandleAdornment.Radius ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic", "UI" ] } ``` The radius of the cone adornment in studs. ### Property: ConeHandleAdornment.Shading ```json { "type": "AdornShading", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic", "UI" ] } ``` ## Inherited Members ### From [HandleAdornment](/docs/reference/engine/classes/HandleAdornment.md) - **Property `AdornCullingMode`** (`AdornCullingMode`): Determines whether to automatically cull the adornment. - **Property `AlwaysOnTop`** (`boolean`): Forces this adornment to render on top of all 3D objects in the workspace. - **Property `CFrame`** (`CFrame`): The position and rotation of the object relative to its - **Property `SizeRelativeOffset`** (`Vector3`): The positional offset of the adornment based on the adornee's - **Property `ZIndex`** (`int`): Determines the draw order of this HandleAdornment when - **Event `MouseButton1Down`**: Fires when a player presses down on their left mouse button while hovering - **Event `MouseButton1Up`**: Fires when a player releases their left mouse button while hovering over - **Event `MouseEnter`**: Fires when a player moves their mouse over the adornment. - **Event `MouseLeave`**: Fires when a player moves their mouse out of the adornment. ### From [PVAdornment](/docs/reference/engine/classes/PVAdornment.md) - **Property `Adornee`** (`PVInstance`): The PVInstance which this PVAdornment is attached to. ### From [GuiBase3d](/docs/reference/engine/classes/GuiBase3d.md) - **Property `Color`** (`BrickColor`): Sets the color of a GUI object. *(deprecated, hidden)* - **Property `Color3`** (`Color3`): Sets the color of this GuiBase3d object. - **Property `Transparency`** (`float`): Sets the transparency of this GuiBase3d object. - **Property `Visible`** (`boolean`): Determines whether this GuiBase3d object and its descendants will ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ConfigService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated summary: "A game service that gives access to in-experience configuration with updates in real time." --- # Class: ConfigService > A game service that gives access to in-experience configuration with updates > in real time. ## Description **ConfigService** exposes methods for getting [ConfigSnapshot](/docs/reference/engine/classes/ConfigSnapshot.md) objects. Configs can only be accessed by game servers, so you can't use [ConfigService](/docs/reference/engine/classes/ConfigService.md) within client scripts. See [Experience configs](/docs/en-us/production/configs.md) for an in-depth guide on managing keys, deploying real-time updates, error handling, limits, and more. ## Methods ### Method: ConfigService:ClearTestingValue **Signature:** `ConfigService:ClearTestingValue(key: string): ()` Removes a testing override created by the [SetTestingValue()](/docs/reference/engine/classes/ConfigService.md) function. The actual configured value will be returned after calling this function. *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | Key of the configuration to clear testing value for. | **Returns:** `()` ### Method: ConfigService:GetConfigAsync **Signature:** `ConfigService:GetConfigAsync(): ConfigSnapshot` This function retrieves the latest available configuration as a [ConfigSnapshot](/docs/reference/engine/classes/ConfigSnapshot.md) instance without player targeting. Snapshots can be refreshed to the latest values by calling [ConfigSnapshot:Refresh()](/docs/reference/engine/classes/ConfigSnapshot.md). Experiments are not applied since there is no player context. If configuration fails to load and there is no previously available configuration, this function throws an error. *Yields · Security: None · Thread Safety: Unsafe* **Returns:** `ConfigSnapshot` — A [ConfigSnapshot](/docs/reference/engine/classes/ConfigSnapshot.md) instance with the latest configuration. ### Method: ConfigService:GetConfigForPlayerAsync **Signature:** `ConfigService:GetConfigForPlayerAsync(player: Player): ConfigSnapshot` Retrieves the latest available configuration as a [ConfigSnapshot](/docs/reference/engine/classes/ConfigSnapshot.md) instance targeting to the given player. Snapshots can be refreshed to the latest values by calling [ConfigSnapshot:Refresh()](/docs/reference/engine/classes/ConfigSnapshot.md). This function applies all experiments running for the player. If configuration fails to load and there is no previously available configuration, this function throws an error. *Yields · Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Player` | | The player to get the configuration for. | **Returns:** `ConfigSnapshot` — A [ConfigSnapshot](/docs/reference/engine/classes/ConfigSnapshot.md) instance with the latest configuration targeted to the player. ### Method: ConfigService:SetTestingValue **Signature:** `ConfigService:SetTestingValue(key: string, value: Variant): ()` Sets a testing override for the specified configuration key in the current server instance. When a testing value is set, calls to [ConfigSnapshot:GetValue()](/docs/reference/engine/classes/ConfigSnapshot.md) for that key will return the testing value instead of the actual configured value. Testing values can be cleared by calling the [ClearTestingValue()](/docs/reference/engine/classes/ConfigService.md) function. *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | Key of the configuration to override with a testing value. | | `value` | `Variant` | | Value to set as the testing override. | **Returns:** `()` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ConfigSnapshot last_updated: 2026-06-29T19:34:07Z inherits: - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated summary: "A snapshot of configuration values at a given version. Can be player-specific." --- # Class: ConfigSnapshot > A snapshot of configuration values at a given version. Can be player-specific. ## Description An object with the configuration values at a specific version. A snapshot can be targeted to a single player to apply experimentation values. This is returned by [ConfigService:GetConfigAsync()](/docs/reference/engine/classes/ConfigService.md) and [ConfigService:GetConfigForPlayerAsync()](/docs/reference/engine/classes/ConfigService.md). For more information, see [Experience configs](/docs/en-us/production/configs.md). ## Properties ### Property: ConfigSnapshot.Error ```json { "type": "ConfigSnapshotErrorState", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` Populated if snapshot was in an error state. ### Property: ConfigSnapshot.Outdated ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` If true, indicates the snapshot is outdated and can be refreshed to newer values. ## Methods ### Method: ConfigSnapshot:GetValue **Signature:** `ConfigSnapshot:GetValue(key: string): Variant` Returns the value for the provided key in the snapshot. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | Key of the configuration to get the value for. | **Returns:** `Variant` — The value of the configuration for the given key. ### Method: ConfigSnapshot:GetValueChangedSignal **Signature:** `ConfigSnapshot:GetValueChangedSignal(key: string): RBXScriptSignal` Returns a signal that fires when the value for the provided key changes after calling [ConfigSnapshot:Refresh()](/docs/reference/engine/classes/ConfigSnapshot.md). *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | Key of the configuration to get the change signal for. | **Returns:** `RBXScriptSignal` — A `DataType.RBXScriptSignal` that fires when the value for the given key changes upon refresh. ### Method: ConfigSnapshot:Refresh **Signature:** `ConfigSnapshot:Refresh(): ()` Refreshes the snapshot to the latest configuration values from the service. If not called, the snapshot will continue to reflect the values at the time it was created or last refreshed. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Returns:** `()` ## Events ### Event: ConfigSnapshot.UpdateAvailable **Signature:** `ConfigSnapshot.UpdateAvailable()` Fires when a newer version of the configuration is available than the current snapshot. You can perform validation and/or call [ConfigSnapshot:Refresh()](/docs/reference/engine/classes/ConfigSnapshot.md) to update to the latest values. *Security: None · Capabilities: Basic* ## Inherited Members ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Configuration last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances summary: "A container object designed to hold value objects. Makes values used in Tools or any model using Scripts more accessible." --- # Class: Configuration > A container object designed to hold value objects. Makes values used in > [Tools](/docs/reference/engine/classes/Tool.md) or any model using [Scripts](/docs/reference/engine/classes/Script.md) more accessible. ## Description The `Configuration` object is a container object. It's designed to hold value objects to make their values more accessible to [Tools](/docs/reference/engine/classes/Tool.md) or any model that uses [Scripts](/docs/reference/engine/classes/Script.md). ## How does the Configuration object work? The `Configuration` object is just a container and does not automatically offer any additional functionality to a [Folder](/docs/reference/engine/classes/Folder.md). Configurations should hold value objects ([BrickColorValue](/docs/reference/engine/classes/BrickColorValue.md), [NumberValue](/docs/reference/engine/classes/NumberValue.md), [IntValue](/docs/reference/engine/classes/IntValue.md), [ObjectValue](/docs/reference/engine/classes/ObjectValue.md), etc.). These value objects should be read by the [Script](/docs/reference/engine/classes/Script.md) or [LocalScript](/docs/reference/engine/classes/LocalScript.md) associated with the configuration to determine constants such as damage, speed, or color. For example, a script might include a damage variable: ```lua local damage = 10 ``` With a `Configuration` object, that code becomes: ```lua local configuration = tool:FindFirstChildWhichIsA("Configuration", true) damage = configuration:FindFirstChild("Damage").Value -- a NumberValue ``` The `Configuration` object is intended to be placed inside a [BasePart](/docs/reference/engine/classes/BasePart.md) in a [Model](/docs/reference/engine/classes/Model.md) or [Tool](/docs/reference/engine/classes/Tool.md). You can then edit its values in the Roblox Studio Properties window rather than having to update the code. Typically, you would place the script within the same model or tool as the configuration, so you would have some confidence that the configuration existed and had certain values. If you fetch the configuration from an outside script, you should program more defensively, with `if` statements to check that the configuration and values exist and fallback values in the event that the `Find()` methods fail. ## Why should I use the Configuration object? Configurations can be convenient for tools and models that you want to use across a range of games or distribute in the Creator Store. The object makes it clear that all variables for the tool or model can be found in this one place, and other creators can make changes without having to modify your code. For variables that you only want to use within a single game and that you want the flexibility to tune without publishing your experience, see [Experience configs](/docs/en-us/production/configs.md). ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ConfigureServerService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service --- # Class: ConfigureServerService ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Constraint last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: BaseParts tags: - NotCreatable summary: "The base class for constraint-based objects." --- # Class: Constraint > The base class for constraint-based objects. ## Description The base class for constraint-based objects. See [mover constraints](/docs/en-us/physics/mover-constraints.md) and [mechanical constraints](/docs/en-us/physics/mechanical-constraints.md) for more details. ## Properties ### Property: Constraint.Active ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Physics" ], "simulationAccess": true } ``` This property is `true` if the constraint and both of its parts are in the [Workspace](/docs/reference/engine/classes/Workspace.md) and the constraint's [Constraint.Enabled](/docs/reference/engine/classes/Constraint.md) property is `true`. ### Property: Constraint.Attachment0 ```json { "type": "Attachment", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Attachments", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The [Attachment](/docs/reference/engine/classes/Attachment.md) that is connected to [Constraint.Attachment1](/docs/reference/engine/classes/Constraint.md) ### Property: Constraint.Attachment1 ```json { "type": "Attachment", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Attachments", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The [Attachment](/docs/reference/engine/classes/Attachment.md) that is connected to [Constraint.Attachment0](/docs/reference/engine/classes/Constraint.md) ### Property: Constraint.Color ```json { "type": "BrickColor", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Physics" ] } ``` The color of the constraint. ### Property: Constraint.Enabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Toggles whether or not the constraint is enabled. ### Property: Constraint.Visible ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Physics" ] } ``` Toggles the constraint's visibility. ## Methods ### Method: Constraint:GetDebugAppliedForce **Signature:** `Constraint:GetDebugAppliedForce(bodyId: int): Vector3` > **Deprecated:** This method should not be used in new work. *Security: None · Thread Safety: Unsafe · Capabilities: Physics* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `bodyId` | `int` | | | **Returns:** `Vector3` ### Method: Constraint:GetDebugAppliedTorque **Signature:** `Constraint:GetDebugAppliedTorque(bodyId: int): Vector3` > **Deprecated:** This method should not be used in new work. *Security: None · Thread Safety: Unsafe · Capabilities: Physics* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `bodyId` | `int` | | | **Returns:** `Vector3` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ContentProvider last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated summary: "Service that is used to load content, or assets, into a game." --- # Class: ContentProvider > Service that is used to load content, or assets, into a game. ## Description Service that loads content (assets) into a game. Roblox servers stream all assets to the client at runtime: objects in the Workspace, mesh assets, texture assets, etc. Assets such as mesh visual data, textures, decals, and sounds are streamed in as required, regardless of whether [Streaming](/docs/en-us/workspace/streaming.md) is enabled. In some cases, this behavior is undesirable, as it can lead to a delay before the content loads into the experience. `ContentProvider` lets you preload assets into an experience using the [ContentProvider:PreloadAsync()](/docs/reference/engine/classes/ContentProvider.md) method. You might want to display a loading screen, preload critical assets, and only then allow the player into the experience. #### Best Practices for Preloading - Only preload essential assets, **not** the entire [Workspace](/docs/reference/engine/classes/Workspace.md). You might get occasional pop-in, but it decreases load times and generally doesn't disrupt the player experience. Assets that are good candidates for preloading include those required for the loading screen, the UI, or the starting area. - Let players skip the loading screen, or automatically skip it after a certain amount of time. ## Code Samples **ContentProvider** In this example a Decal and Sound are preloaded into a game. Once they have finished loading the script will print a message to the output. ```lua local ContentProvider = game:GetService("ContentProvider") local LOGO_ID = "rbxassetid://658743164" local PAGE_TURN_ID = "rbxassetid://12222076" local decal = Instance.new("Decal") decal.ColorMapContent = Content.fromUri(LOGO_ID) local sound = Instance.new("Sound") sound.SoundId = PAGE_TURN_ID local assets = { decal, sound } ContentProvider:PreloadAsync(assets) print("All assets loaded.") ``` ## Properties ### Property: ContentProvider.BaseUrl ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AssetManagement" ] } ``` Used by the [ContentProvider](/docs/reference/engine/classes/ContentProvider.md) to download assets from the Roblox website. This URL points to a Roblox hosted website from which assets are downloaded and is pulled from the AppSettings.xml file, located in the version-hash folder. It is possible to overwrite this property using the [ContentProvider:SetBaseUrl()](/docs/reference/engine/classes/ContentProvider.md) function in the command bar; however, this is not recommended and may cause asset loading issues. ### Property: ContentProvider.RequestQueueSize ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AssetManagement" ] } ``` Gives the number of items in the [ContentProvider](/docs/reference/engine/classes/ContentProvider.md) request queue that need to be downloaded. Items are added to the client's request queue when an asset is used for the first time or [ContentProvider:PreloadAsync()](/docs/reference/engine/classes/ContentProvider.md) is called. Developers are advised not to use RequestQueueSize to create loading bars. This is because the queue size can both increase and decrease over time as new assets are added and downloaded. Developers looking to display loading progress should load assets one at a time (see example below). **ContentProvider Loading Bar** This code sample demonstrates how [ContentProvider:PreloadAsync()](/docs/reference/engine/classes/ContentProvider.md) can be used to create a simple loading bar in a game, by loading the assets one at a time. ```lua local ContentProvider = game:GetService("ContentProvider") local Players = game:GetService("Players") local localPlayer = Players.LocalPlayer local playerGui = localPlayer:WaitForChild("PlayerGui") local screenGui = Instance.new("ScreenGui") screenGui.Parent = playerGui -- create a basic loading bar local frame = Instance.new("Frame") frame.Size = UDim2.new(0.5, 0, 0.1, 0) frame.Position = UDim2.new(0.5, 0, 0.5, 0) frame.AnchorPoint = Vector2.new(0.5, 0.5) frame.Parent = screenGui local bar = Instance.new("Frame") bar.Size = UDim2.new(0, 0, 1, 0) bar.Position = UDim2.new(0, 0, 0, 0) bar.BackgroundColor3 = Color3.new(0, 0, 1) bar.Parent = frame local sound = Instance.new("Sound") sound.SoundId = "rbxassetid://9120386436" local sound2 = Instance.new("Sound") sound2.SoundId = "rbxassetid://9120385974" local assets = { sound, sound2, } task.wait(3) for i = 1, #assets do local asset = assets[i] ContentProvider:PreloadAsync({ asset }) -- 1 at a time, yields local progress = i / #assets bar.Size = UDim2.new(progress, 0, 1, 0) end print("loading done") ``` ## Methods ### Method: ContentProvider:GetAssetFetchStatus **Signature:** `ContentProvider:GetAssetFetchStatus(contentId: ContentId): AssetFetchStatus` Gets the current [AssetFetchStatus](/docs/reference/engine/enums/AssetFetchStatus.md) of the `contentId` provided. Use [GetAssetFetchStatusChangedSignal()](/docs/reference/engine/classes/ContentProvider.md) to listen for changes to this value. *Security: None · Thread Safety: Unsafe · Capabilities: AssetManagement* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `contentId` | `ContentId` | | The ID of the content to fetch the status for. | **Returns:** `AssetFetchStatus` — The [AssetFetchStatus](/docs/reference/engine/enums/AssetFetchStatus.md) of the content. **Monitoring AssetFetchStatus** Retrieves the initial [AssetFetchStatus](/docs/reference/engine/enums/AssetFetchStatus.md) of an asset and listens for future updates. ```lua local ContentProvider = game:GetService("ContentProvider") -- An example asset to load local ASSET_ID = "rbxassetid://9120386436" local exampleAsset = Instance.new("Sound") exampleAsset.SoundId = ASSET_ID -- Output the current AssetFetchStatus of the asset local initialAssetFetchStatus = ContentProvider:GetAssetFetchStatus(ASSET_ID) print("Initial AssetFetchStatus:", initialAssetFetchStatus) -- Listen for updates local assetFetchStatusChangedSignal = ContentProvider:GetAssetFetchStatusChangedSignal(ASSET_ID) local function onAssetFetchStatusChanged(newAssetFetchStatus: Enum.AssetFetchStatus) print(`New AssetFetchStatus: {newAssetFetchStatus}`) end assetFetchStatusChangedSignal:Connect(onAssetFetchStatusChanged) -- Trigger the asset to preload local function onAssetRequestComplete(contentId: string, assetFetchStatus: Enum.AssetFetchStatus) print(`Preload status {contentId}: {assetFetchStatus.Name}`) end ContentProvider:PreloadAsync({ exampleAsset }, onAssetRequestComplete) ``` ### Method: ContentProvider:GetAssetFetchStatusChangedSignal **Signature:** `ContentProvider:GetAssetFetchStatusChangedSignal(contentId: ContentId): RBXScriptSignal` A signal that fires when the [AssetFetchStatus](/docs/reference/engine/enums/AssetFetchStatus.md) of the provided content changes. Connect to this signal by using a callback with one argument of type [AssetFetchStatus](/docs/reference/engine/enums/AssetFetchStatus.md). This is particularly useful for assets that might update themselves automatically like the thumbnail of a user when they change clothes. *Security: None · Thread Safety: Unsafe · Capabilities: AssetManagement* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `contentId` | `ContentId` | | | **Returns:** `RBXScriptSignal` **Monitoring AssetFetchStatus** Retrieves the initial [AssetFetchStatus](/docs/reference/engine/enums/AssetFetchStatus.md) of an asset and listens for future updates. ```lua local ContentProvider = game:GetService("ContentProvider") -- An example asset to load local ASSET_ID = "rbxassetid://9120386436" local exampleAsset = Instance.new("Sound") exampleAsset.SoundId = ASSET_ID -- Output the current AssetFetchStatus of the asset local initialAssetFetchStatus = ContentProvider:GetAssetFetchStatus(ASSET_ID) print("Initial AssetFetchStatus:", initialAssetFetchStatus) -- Listen for updates local assetFetchStatusChangedSignal = ContentProvider:GetAssetFetchStatusChangedSignal(ASSET_ID) local function onAssetFetchStatusChanged(newAssetFetchStatus: Enum.AssetFetchStatus) print(`New AssetFetchStatus: {newAssetFetchStatus}`) end assetFetchStatusChangedSignal:Connect(onAssetFetchStatusChanged) -- Trigger the asset to preload local function onAssetRequestComplete(contentId: string, assetFetchStatus: Enum.AssetFetchStatus) print(`Preload status {contentId}: {assetFetchStatus.Name}`) end ContentProvider:PreloadAsync({ exampleAsset }, onAssetRequestComplete) ``` ### Method: ContentProvider:ListEncryptedAssets **Signature:** `ContentProvider:ListEncryptedAssets(): Array` *Security: None · Thread Safety: Unsafe · Capabilities: AssetManagement* **Returns:** `Array` ### Method: ContentProvider:PreloadAsync **Signature:** `ContentProvider:PreloadAsync(contentIdList: Array, callbackFunction?: Function): ()` Yields until all of the assets associated with the given [Instances](/docs/reference/engine/classes/Instance.md) have loaded. This can be used to pause a script and not use content until it is certain that the content has been loaded into the experience. When called, the engine identifies links to content for each item in the list. For any of the [Instances](/docs/reference/engine/classes/Instance.md) which have properties that define links to content, such as a [Decal](/docs/reference/engine/classes/Decal.md) or a [Sound](/docs/reference/engine/classes/Sound.md), the engine attempts to load these assets from Roblox. For each requested asset, the callback function runs, indicating the asset's final [AssetFetchStatus](/docs/reference/engine/enums/AssetFetchStatus.md). If any of the assets fail to load, an error message appears in the output. The method itself will not error and it will continue executing until it has processed each requested instance. ##### Limitations [SurfaceAppearance](/docs/reference/engine/classes/SurfaceAppearance.md) and [MaterialVariant](/docs/reference/engine/classes/MaterialVariant.md) are not supported by [PreloadAsync()](/docs/reference/engine/classes/ContentProvider.md) because these objects rely on processed texture pack assets rather than directly loading individual textures. Calling it on a [SurfaceAppearance](/docs/reference/engine/classes/SurfaceAppearance.md) instance will not do anything, but the associated textures will still be streamed in during runtime. If PreloadAsync is called on [Instances](/docs/reference/engine/classes/Instance.md) that are not currently visible, such as a [Decal](/docs/reference/engine/classes/Decal.md) or an [ImageLabel](/docs/reference/engine/classes/ImageLabel.md), the Engine will download and store textures used by those Instances in its disk cache. Because the [Instances](/docs/reference/engine/classes/Instance.md) are not visible in these cases, the Engine may reduce memory consumption by unloading the textures after preloading them. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetManagement* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `contentIdList` | `Array` | | An array of instances to load. | | `callbackFunction` | `Function` | `nil` | The function called when each asset request completes. Returns the [content](/docs/reference/engine/datatypes/Content.md) string and the asset's final [AssetFetchStatus](/docs/reference/engine/enums/AssetFetchStatus.md). | **Returns:** `()` **Preloading Assets** In this code sample, a sound and a texture are preloaded using [Sound](/docs/reference/engine/classes/Sound.md) and [Decal](/docs/reference/engine/classes/Decal.md) instances. ```lua local ContentProvider = game:GetService("ContentProvider") local sound = Instance.new("Sound") sound.SoundId = "rbxassetid://9120386436" local decal = Instance.new("Decal") decal.ColorMapContent = Content.fromUri("rbxassetid://5447528495") local assets = { decal, sound, } -- This will be hit as each asset resolves local callback = function(assetId, assetFetchStatus) print("PreloadAsync() resolved asset ID:", assetId) print("PreloadAsync() final AssetFetchStatus:", assetFetchStatus) end -- Preload the content and time it local startTime = os.clock() ContentProvider:PreloadAsync(assets, callback) local deltaTime = os.clock() - startTime print(("Preloading complete, took %.2f seconds"):format(deltaTime)) ``` ### Method: ContentProvider:RegisterDefaultEncryptionKey **Signature:** `ContentProvider:RegisterDefaultEncryptionKey(encryptionKey: string): ()` *Security: None · Thread Safety: Unsafe · Capabilities: AssetManagement* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `encryptionKey` | `string` | | | **Returns:** `()` ### Method: ContentProvider:RegisterDefaultSessionKey **Signature:** `ContentProvider:RegisterDefaultSessionKey(sessionKey: string): ()` *Security: None · Thread Safety: Unsafe · Capabilities: AssetManagement* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `sessionKey` | `string` | | | **Returns:** `()` ### Method: ContentProvider:RegisterEncryptedAsset **Signature:** `ContentProvider:RegisterEncryptedAsset(assetId: ContentId, encryptionKey: string): ()` *Security: None · Thread Safety: Unsafe · Capabilities: AssetManagement* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `assetId` | `ContentId` | | | | `encryptionKey` | `string` | | | **Returns:** `()` ### Method: ContentProvider:RegisterSessionEncryptedAsset **Signature:** `ContentProvider:RegisterSessionEncryptedAsset(contentId: ContentId, sessionKey: string): ()` *Security: None · Thread Safety: Unsafe · Capabilities: AssetManagement* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `contentId` | `ContentId` | | | | `sessionKey` | `string` | | | **Returns:** `()` ### Method: ContentProvider:UnregisterDefaultEncryptionKey **Signature:** `ContentProvider:UnregisterDefaultEncryptionKey(): ()` *Security: None · Thread Safety: Unsafe · Capabilities: AssetManagement* **Returns:** `()` ### Method: ContentProvider:UnregisterEncryptedAsset **Signature:** `ContentProvider:UnregisterEncryptedAsset(assetId: ContentId): ()` *Security: None · Thread Safety: Unsafe · Capabilities: AssetManagement* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `assetId` | `ContentId` | | | **Returns:** `()` ### Method: ContentProvider:Preload **Signature:** `ContentProvider:Preload(contentId: ContentId): ()` > **Deprecated:** This item has been superseded by [ContentProvider:PreloadAsync()](/docs/reference/engine/classes/ContentProvider.md) which should be used in all new work. Usually, content is loaded only when it starts being used. That explains why it often takes a moment for an image to appear in a [GuiObject](/docs/reference/engine/classes/GuiObject.md), or a `Mesh|mesh` to appear in a [part](/docs/reference/engine/classes/BasePart.md), or why a [sound](/docs/reference/engine/classes/Sound.md) doesn't play for the first time. All because the asset has not yet finished loading. Preload is used to load this content beforehand, so that it works instantly. *Security: None · Thread Safety: Unsafe · Capabilities: AssetManagement* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `contentId` | `ContentId` | | | **Returns:** `()` **ContentProvider:Preload** This example will preload the asset with ID "12222058" when a LocalPlayer loads a game. ```lua local ContentProvider = game:GetService("ContentProvider") ContentProvider:Preload("http://www.roblox.com/asset/?id=12222058") ``` ## Events ### Event: ContentProvider.AssetFetchFailed **Signature:** `ContentProvider.AssetFetchFailed(assetId: ContentId)` *Security: None · Capabilities: AssetManagement* **Parameters:** | Name | Type | Description | |------|------|-------------| | `assetId` | `ContentId` | | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ContextActionService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service summary: "A service used to bind user input to contextual actions." --- # Class: ContextActionService > A service used to bind user input to contextual actions. ## Description Allows an experience to bind user input to contextual actions, or actions that are only enabled under some condition or period of time. For example, allowing a player to open a door only while close by. In code, an action is simply a string (the name of the action) used by the service to differentiate between unique actions. The action string is provided to [BindAction](/docs/reference/engine/classes/ContextActionService.md) and [UnbindAction](/docs/reference/engine/classes/ContextActionService.md), among other member functions. If two actions are bound to the same input, the most recently bound will take priority. When the most recent action is unbound, the one bound before that takes control again. Since this service deals with user input, you can only use it in client-side [LocalScripts](/docs/reference/engine/classes/LocalScript.md). #### Context and Action A **context** is simply a condition during which a player may perform some action. Some examples include holding a [Tool](/docs/reference/engine/classes/Tool.md), being [seated](/docs/reference/engine/classes/Seat.md) in a car or standing near a door. Whatever the case may be, it is up to your [LocalScripts](/docs/reference/engine/classes/LocalScript.md) to call [BindAction](/docs/reference/engine/classes/ContextActionService.md) when the context is entered and [UnbindAction](/docs/reference/engine/classes/ContextActionService.md) when the context is left. An **action** is simply some input that can be performed by the player while in that context. Such an action could open/close some menu, trigger a secondary tool action or send a request to the server using [RemoteFunction:InvokeServer()](/docs/reference/engine/classes/RemoteFunction.md). An action is identified by a unique string as the first parameter of both [BindAction](/docs/reference/engine/classes/ContextActionService.md) and [UnbindAction](/docs/reference/engine/classes/ContextActionService.md). The string can be anything, but it should reflect the **action being performed, not the input being used**. For example, don't use "KeyH" as an action name - use "CarHorn" instead. It is best to define your actions as a constant at the top of your script since you will use it in at least three different places in your code. #### Binding Actions Contextually It's better to use ContextActionService's [BindAction](/docs/reference/engine/classes/ContextActionService.md) than [UserInputService.InputBegan](/docs/reference/engine/classes/UserInputService.md) for most cases. For [UserInputService.InputBegan](/docs/reference/engine/classes/UserInputService.md), your connected function would have to check if the player is in the context of the action being performed. In most cases, this is harder than just calling a function when a context is entered/ left. For example, if you want to have the `H` key trigger a car horn sound while the player is sitting in it, the player might type "hello" in chat or otherwise use the `H` key for something else. It is harder to determine if something else is using the H key (like chat) - the car might honk when the player didn't mean to. If you instead use [BindAction](/docs/reference/engine/classes/ContextActionService.md) and [UnbindAction](/docs/reference/engine/classes/ContextActionService.md) when the player enters/leaves the car, [ContextActionService](/docs/reference/engine/classes/ContextActionService.md) will make sure that `H` key presses trigger the honk action only when it is the most recently bound action. If something else (like chat) takes control, you won't have to worry about checking that. #### Inspecting Bound Actions To see a list of actions and their bound inputs, you can inspect the "Action Bindings" tab in the Developer Console (F9 while in game). This shows all bindings, including those bound by Roblox core scripts and default camera/control scripts too. This is useful for debugging if your actions are being bound/unbound at the correct times, or if some other action is stealing input from your actions. For example, if you are attempting to bind WASD, it may be the case that default character movement scripts are binding over those same keys. Similarly, the camera control script can steal right-click input if the script runs after yours. #### Keyboardless Input This service is especially useful for supporting gamepad and touch input. For gamepad input, you might choose to bind the B button to an action that returns the user to the previous menu when they enter another menu. For touch, on-screen touch buttons can be used in place of key presses: these buttons display only while the action is bound, and the position, text and/or images of these buttons can be configured through this service. They're somewhat limited in the amount of customization provided by this service; it's usually a better idea to make your own on-screen buttons using [ImageButton](/docs/reference/engine/classes/ImageButton.md) or [TextButton](/docs/reference/engine/classes/TextButton.md). ## Code Samples **ContextActionService Tool Reload** This example properly shows how to use ContextActionService in binding user input to a contextual action. The context is the tool being equipped; the action is reloading some weapon. Test this code sample by placing it in a LocalScript parented to a Tool. When the Tool is equipped, a "Reload" action is bound, and when the Tool is unequipped the "Reload" action is unbound. When the player presses R with the Tool equipped, the message "Reloading!" will appear. ```lua local ContextActionService = game:GetService("ContextActionService") local ACTION_RELOAD = "Reload" local tool = script.Parent local function handleAction(actionName, inputState, _inputObject) if actionName == ACTION_RELOAD and inputState == Enum.UserInputState.Begin then print("Reloading!") end end tool.Equipped:Connect(function() ContextActionService:BindAction(ACTION_RELOAD, handleAction, true, Enum.KeyCode.R) end) tool.Unequipped:Connect(function() ContextActionService:UnbindAction(ACTION_RELOAD) end) ``` ## Methods ### Method: ContextActionService:BindAction **Signature:** `ContextActionService:BindAction(actionName: string, functionToBind: Function, createTouchButton: boolean, inputTypes: Tuple): ()` Bind an action to user input given an action handling function. Upon a matching input being performed, the action handler function will be called with the arguments listed below. Valid input enum items include those within the following: [KeyCode](/docs/reference/engine/enums/KeyCode.md), [UserInputType](/docs/reference/engine/enums/UserInputType.md) or [PlayerActions](/docs/reference/engine/enums/PlayerActions.md) . Call this function when a player **enters the context** in which an action can be performed. When the player leaves the context, call [UnbindAction()](/docs/reference/engine/classes/ContextActionService.md) with the same `actionName`. The code sample below shows how a [Sound](/docs/reference/engine/classes/Sound.md) can be [played](/docs/reference/engine/classes/Sound.md) while a key (H), game pad button, or touch screen button is pressed. ```lua local ContextActionService = game:GetService("ContextActionService") -- A car horn sound local honkSound = Instance.new("Sound", workspace) honkSound.Looped = true honkSound.SoundId = "rbxassetid://9120386436" local function handleAction(actionName, inputState, inputObject) if actionName == "HonkHorn" then if inputState == Enum.UserInputState.Begin then honkSound:Play() else honkSound:Pause() end end end -- When the player sits in the vehicle: ContextActionService:BindAction("HonkHorn", handleAction, true, Enum.KeyCode.H, Enum.KeyCode.ButtonY) -- When the player gets out: ContextActionService:UnbindAction("HonkHorn") ``` #### Action Handler Parameters The action handler functions are called with the following parameters: | # | Type | Description | | --- | --- | --- | | 1 | `string` | The same string that was originally passed to [BindAction()](/docs/reference/engine/classes/ContextActionService.md). This allows one function to handle multiple actions at once, if necessary. | | 2 | [UserInputState](/docs/reference/engine/enums/UserInputState.md) | The state of the input. [Cancel](/docs/reference/engine/enums/UserInputState\.md) is sent if some input was in progress and another action bound over that in-progress input, or if the in-progress bound action was unbound through [UnbindAction()](/docs/reference/engine/classes/ContextActionService.md). | | 3 | `InputObject` | An object that contains information about the input (varies based on [UserInputType](/docs/reference/engine/enums/UserInputType.md)). The [InputObject](/docs/reference/engine/classes/InputObject.md) sometimes won't match the inputs the action was bound to: when the [Cancel](/docs/reference/engine/enums/UserInputState\.md) state is sent, this object will be [KeyCode.Unknown](/docs/reference/engine/enums/KeyCode.md) and [UserInputType.None](/docs/reference/engine/enums/UserInputType.md). | #### Action Bindings Stack Action bindings behave like a stack: if two actions are bound to the same user input, the **most recently bound** action handler will be used. If an action handler returns [ContextActionResult.Pass](/docs/reference/engine/enums/ContextActionResult.md), the next most recently bound action handler will be called, and so on until a handler sinks the input (by returning `nil` or [ContextActionResult.Sink](/docs/reference/engine/enums/ContextActionResult.md)). When [UnbindAction](/docs/reference/engine/classes/ContextActionService.md) is called, the action handler is removed from the stack. This stack behavior can be overridden using [BindActionAtPriority](/docs/reference/engine/classes/ContextActionService.md), where an additional priority parameter after `createTouchButton` may override the order in which actions are bound (higher before lower). #### Touch Buttons In addition to input types, this function's third parameter controls whether a button is created for [TouchEnabled](/docs/reference/engine/classes/UserInputService.md) devices. Upon the first touch button's creation, a [ScreenGui](/docs/reference/engine/classes/ScreenGui.md) named "ContextActionGui" is added to the [PlayerGui](/docs/reference/engine/classes/PlayerGui.md). Inside the ScreenGui is a [Frame](/docs/reference/engine/classes/Frame.md) called "ContextButtonFrame" is added. It is in this frame in which [ImageButtons](/docs/reference/engine/classes/ImageButton.md) for bound actions are parented; you can use [GetButton()](/docs/reference/engine/classes/ContextActionService.md) to retrieve such buttons for customization. A maximum of 7 touch buttons can be created through [BindAction()](/docs/reference/engine/classes/ContextActionService.md). *Security: None · Thread Safety: Unsafe · Capabilities: Input* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `actionName` | `string` | | A string representing the action being performed (e.g. "HonkHorn" or "OpenDoor"). | | `functionToBind` | `Function` | | The action-handling function, called with the following parameters when the bound inputs are triggered: string (actionName), [UserInputState](/docs/reference/engine/enums/UserInputState.md) and an InputObject. | | `createTouchButton` | `boolean` | | Whether a GUI button should be created for the action on touch input devices. | | `inputTypes` | `Tuple` | | Any number of [KeyCode](/docs/reference/engine/enums/KeyCode.md) or [UserInputType](/docs/reference/engine/enums/UserInputType.md) representing the inputs to bind to the action. | **Returns:** `()` **ContextActionService Tool Reload** This example properly shows how to use ContextActionService in binding user input to a contextual action. The context is the tool being equipped; the action is reloading some weapon. Test this code sample by placing it in a LocalScript parented to a Tool. When the Tool is equipped, a "Reload" action is bound, and when the Tool is unequipped the "Reload" action is unbound. When the player presses R with the Tool equipped, the message "Reloading!" will appear. ```lua local ContextActionService = game:GetService("ContextActionService") local ACTION_RELOAD = "Reload" local tool = script.Parent local function handleAction(actionName, inputState, _inputObject) if actionName == ACTION_RELOAD and inputState == Enum.UserInputState.Begin then print("Reloading!") end end tool.Equipped:Connect(function() ContextActionService:BindAction(ACTION_RELOAD, handleAction, true, Enum.KeyCode.R) end) tool.Unequipped:Connect(function() ContextActionService:UnbindAction(ACTION_RELOAD) end) ``` **General Action Handler** This code sample uses ContextActionService to bind an action named "BoundAction" to a general action handler function on the `F` key. Place this in a LocalScript inside StarterPlayerScripts and press `F` to see the message "Handling action: BoundAction". ```lua local ContextActionService = game:GetService("ContextActionService") local function handleAction(actionName, inputState, inputObj) if inputState == Enum.UserInputState.Begin then print("Handling action: " .. actionName) print(inputObj.UserInputType) end -- Since this function does not return anything, this handler will -- "sink" the input and no other action handlers will be called after -- this one. end ContextActionService:BindAction("BoundAction", handleAction, false, Enum.KeyCode.F) ``` **Stacked Action Handlers** This code sample demonstrates how BindAction acts like a stack. It binds two actions, FirstAction (`Z`, `X`, and `C` keys) and SecondAction (`Z` and `X` keys) to two action handling functions. The second one will pass on a certain input (the `X` key). Both actions use the `Z` and `X` keys, however the second handler will pass input only if `X` is pressed. So, when `X` is pressed, the second handler is called and then the first. The first action is also bound to the `C` key, and can be triggered even though the other two inputs are "covered" by the second action. Test this code out by pasting it into a LocalScript within StarterPlayerScripts, then pressing `Z`, `X` and `C`. Observe which action handlers are called with what actions. ```lua local ContextActionService = game:GetService("ContextActionService") -- Define an action handler for FirstAction local function actionHandlerOne(actionName, inputState, _inputObj) if inputState == Enum.UserInputState.Begin then print("Action Handler One: " .. actionName) end -- This action handler returns nil, so it is assumed that -- it properly handles the action. end -- Binding the action FirstAction (it's on the bottom of the stack) ContextActionService:BindAction("FirstAction", actionHandlerOne, false, Enum.KeyCode.Z, Enum.KeyCode.X, Enum.KeyCode.C) -- Define an action handler for SecondAction local function actionHandlerTwo(actionName, inputState, inputObj) if inputState == Enum.UserInputState.Begin then print("Action Handler Two: " .. actionName) end if inputObj.KeyCode == Enum.KeyCode.X then return Enum.ContextActionResult.Pass else -- Returning nil implicitly Sinks inputs return Enum.ContextActionResult.Sink end end -- Binding SecondAction over the first action (since it bound more recently, it is on the top of the stack) -- Note that SecondAction uses the same keys as ContextActionService:BindAction("SecondAction", actionHandlerTwo, false, Enum.KeyCode.Z, Enum.KeyCode.X) ``` ### Method: ContextActionService:BindActionAtPriority **Signature:** `ContextActionService:BindActionAtPriority(actionName: string, functionToBind: Function, createTouchButton: boolean, priorityLevel: int, inputTypes: Tuple): ()` BindActionAtPriority behaves like [BindAction](/docs/reference/engine/classes/ContextActionService.md) but also allows a priority to be assigned to the bound action. If multiple actions are bound to the same input, the higher priority function is called regardless of the order in which the actions were bound. In other words, this function overrides the normal "stack" behavior of BindAction. *Security: None · Thread Safety: Unsafe · Capabilities: Input* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `actionName` | `string` | | A string representing the action being performed (e.g. "HonkHorn" or "OpenDoor"). | | `functionToBind` | `Function` | | The action-handling function, called with the following parameters when the bound inputs are triggered: string (actionName), [UserInputState](/docs/reference/engine/enums/UserInputState.md) and an InputObject. | | `createTouchButton` | `boolean` | | Whether a GUI button should be created for the action on touch input devices. | | `priorityLevel` | `int` | | The priority level at which the action should be bound (higher considered before lower). | | `inputTypes` | `Tuple` | | Any number of Enum.KeyCode or Enum.UserInputType representing the inputs to bind to the action. | **Returns:** `()` **ContextActionService BindAction Priorities** This code sample demonstrates how [ContextActionService:BindActionAtPriority()](/docs/reference/engine/classes/ContextActionService.md) can be used to bind actions out of order yet still have the same priority levels. Normally, [BindAction()](/docs/reference/engine/classes/ContextActionService.md) would operate on order (last bound action has highest priority), but priority levels override this. You can test this code by pasting it into a `Script` with `RunContext` = `Client` in `ReplicatedStorage`. ```lua local ContextActionService = game:GetService("ContextActionService") local INPUT_KEY1 = Enum.KeyCode.Q local INPUT_KEY2 = Enum.KeyCode.E local function handleThrow(actionName: string, inputState: Enum.UserInputState, inputObject: InputObject) if inputState ~= Enum.UserInputState.Begin then return Enum.ContextActionResult.Pass end print(`Action [{actionName}] occurred. KeyCode [{inputObject.KeyCode}] pressed.`) return Enum.ContextActionResult.Sink end local function handlePunch(actionName: string, inputState: Enum.UserInputState, inputObject: InputObject) if inputState ~= Enum.UserInputState.Begin then return Enum.ContextActionResult.Pass end print(`Action [{actionName}] occurred. KeyCode [{inputObject.KeyCode}] pressed.`) return Enum.ContextActionResult.Sink end -- Without specifying priority, the most recently bound action is called first, -- so pressing INPUT_KEY1 prints "Punch" and then sinks the input. ContextActionService:BindAction("DefaultThrow", handleThrow, false, INPUT_KEY1) ContextActionService:BindAction("DefaultPunch", handlePunch, false, INPUT_KEY1) -- Here we bind both functions in the same order as above, but with explicitly swapped priorities. -- That is, we give "Throw" a higher priority of 2 so it will be called first, -- despite "Punch" still being bound more recently. -- Pressing INPUT_KEY2 prints "Throw" and then sinks the input. ContextActionService:BindActionAtPriority("PriorityThrow", handleThrow, false, 2, INPUT_KEY2) ContextActionService:BindActionAtPriority("PriorityPunch", handlePunch, false, 1, INPUT_KEY2) ``` ### Method: ContextActionService:BindActivate **Signature:** `ContextActionService:BindActivate(userInputTypeForActivation: UserInputType, keyCodesForActivation: Tuple): ()` Bind a [KeyCode](/docs/reference/engine/enums/KeyCode.md) that can be used with a [UserInputType](/docs/reference/engine/enums/UserInputType.md) to activate [ClickDetector](/docs/reference/engine/classes/ClickDetector.md) events, [Tools](/docs/reference/engine/classes/Tool.md), and [GuiButtons](/docs/reference/engine/classes/GuiButton.md). When the given key/button is pressed, it fires the [Mouse.Button1Down](/docs/reference/engine/classes/Mouse.md) event on the mouse sent to [Tool.Equipped](/docs/reference/engine/classes/Tool.md). This in turn fires the [Tool.Activated](/docs/reference/engine/classes/Tool.md) event if [Tool.ManualActivationOnly](/docs/reference/engine/classes/Tool.md) is not set to true. For gamepad input, this function is called by the default control scripts in order to bind the ButtonR2 [KeyCode](/docs/reference/engine/enums/KeyCode.md). Note that the [UserInputType](/docs/reference/engine/enums/UserInputType.md) specified must be `Keyboard` or `Gamepad1` through `Gamepad8` in order to be valid. *Security: None · Thread Safety: Unsafe · Capabilities: Input* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `userInputTypeForActivation` | `UserInputType` | | Must be Keyboard or Gamepad1 through Gamepad8. | | `keyCodesForActivation` | `Tuple` | | | **Returns:** `()` ### Method: ContextActionService:GetAllBoundActionInfo **Signature:** `ContextActionService:GetAllBoundActionInfo(): Dictionary` GetAllBoundActioninfo returns a table which maps all actions' names (those originally passed to [BindAction](/docs/reference/engine/classes/ContextActionService.md)) to a table returned by [GetBoundActionInfo](/docs/reference/engine/classes/ContextActionService.md) when called with the action name itself. Using this function, you can inspect all presently bound actions. This is useful when debugging their priority levels or stack orders. *Security: None · Thread Safety: Unsafe · Capabilities: Input* **Returns:** `Dictionary` ### Method: ContextActionService:GetBoundActionInfo **Signature:** `ContextActionService:GetBoundActionInfo(actionName: string): Dictionary` GetBoundActionInfo returns a table with the following keys describing a bound action given its name. To get the same information for all actions at once, use [GetAllBoundActionInfo](/docs/reference/engine/classes/ContextActionService.md). | Name | Type | Description | | --- | --- | --- | | `stackOrder` | number | Describes the index of the action on the stack (increasing) | | `priorityLevel`* | number | Describes the [priority](/docs/reference/engine/classes/ContextActionService.md) level of the action | | `createTouchButton` | bool | Describes whether a touch button should be created on [TouchEnabled](/docs/reference/engine/classes/UserInputService.md) devices | | `inputTypes` | table | The input types passed to [BindAction](/docs/reference/engine/classes/ContextActionService.md) for which this action will trigger | | `description`† | string | The description of action set by [SetDescription](/docs/reference/engine/classes/ContextActionService.md) | | `title`† | string | The title of the action set by [SetTitle](/docs/reference/engine/classes/ContextActionService.md) | | `image`† | string | The image of the action's touch button set by [SetImage](/docs/reference/engine/classes/ContextActionService.md) | \* Priority level will still be included even if [BindActionAtPriority](/docs/reference/engine/classes/ContextActionService.md) wasn't used - by default it will be 2000. † Indicates that this field will be `nil` if the associated method was not called for the given action. *Security: None · Thread Safety: Unsafe · Capabilities: Input* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `actionName` | `string` | | | **Returns:** `Dictionary` ### Method: ContextActionService:GetButton **Signature:** `ContextActionService:GetButton(actionName: string): Instance` GetButton returns the [ImageButton](/docs/reference/engine/classes/ImageButton.md) created by [BindAction](/docs/reference/engine/classes/ContextActionService.md) if its third parameter was true and the device is [TouchEnabled](/docs/reference/engine/classes/UserInputService.md). The only parameter to this function must match exactly the name of the action originally sent to BindAction. If no such action was bound or if a button was not created, this function returns `nil`. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Input* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `actionName` | `string` | | The name of the action originally passed to BindAction. | **Returns:** `Instance` — An ImageButton created by BindAction. ### Method: ContextActionService:GetCurrentLocalToolIcon **Signature:** `ContextActionService:GetCurrentLocalToolIcon(): string` GetCurrentLocalToolIcon will return the [BackpackItem.TextureId](/docs/reference/engine/classes/BackpackItem.md) of a [Tool](/docs/reference/engine/classes/Tool.md) currently [equipped](/docs/reference/engine/classes/Tool.md) by the [Player](/docs/reference/engine/classes/Player.md), or `nil` if there is no such Tool or if the player lacks a [Character](/docs/reference/engine/classes/Player.md). *Security: None · Thread Safety: Unsafe · Capabilities: Input* **Returns:** `string` — A content string from the Tool's TextureId, or `nil` if one could not be found. ### Method: ContextActionService:SetDescription **Signature:** `ContextActionService:SetDescription(actionName: string, description: string): ()` SetDescription will set the description of an action bound by [BindAction](/docs/reference/engine/classes/ContextActionService.md). In a list of available actions, this would be text that describes the given action. Although the name may suggest that this method is related to the family of functions that customize a touch button for actions that create them ([SetTitle](/docs/reference/engine/classes/ContextActionService.md), [SetImage](/docs/reference/engine/classes/ContextActionService.md) and [SetPosition](/docs/reference/engine/classes/ContextActionService.md)), this method does not affect such a button. This method merely sets a text description of an action, and nothing more. *Security: None · Thread Safety: Unsafe · Capabilities: Input* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `actionName` | `string` | | The name of the action originally passed to BindAction. | | `description` | `string` | | A text description of the action, such as "Honk the car's horn" or "Open the inventory". | **Returns:** `()` **ContextActionService Touch Button** This code sample demonstrates binding an "Inspect" action to a touch button created automatically by `ContextActionService`. The button is customized using [SetImage()](/docs/reference/engine/classes/ContextActionService.md), [SetTitle()](/docs/reference/engine/classes/ContextActionService.md), [SetDescription()](/docs/reference/engine/classes/ContextActionService.md) and [SetPosition()](/docs/reference/engine/classes/ContextActionService.md). The button is further customized by using [GetButton()](/docs/reference/engine/classes/ContextActionService.md) to get a reference to the `ImageButton` itself and tinting it green by setting [ImageButton.ImageColor3](/docs/reference/engine/classes/ImageButton.md). Paste this code into a `LocalScript` placed within `StarterPlayerScripts` to test it. In Studio, be sure to toggle the touch device emulator in order for the button to actually be created. ```lua local ContextActionService = game:GetService("ContextActionService") local ACTION_INSPECT = "Inspect" local INPUT_INSPECT = Enum.KeyCode.E local IMAGE_INSPECT = "rbxassetid://1826746856" -- Image of a speech bubble with ? in it local function handleAction(actionName, inputState, _inputObject) if actionName == ACTION_INSPECT and inputState == Enum.UserInputState.End then print("Inspecting") end end -- For touch devices, a button is created on-screen automatically via the 3rd parameter ContextActionService:BindAction(ACTION_INSPECT, handleAction, true, INPUT_INSPECT) -- We can use these functions to customize the button: ContextActionService:SetImage(ACTION_INSPECT, IMAGE_INSPECT) ContextActionService:SetTitle(ACTION_INSPECT, "Look") ContextActionService:SetDescription(ACTION_INSPECT, "Inspect something.") ContextActionService:SetPosition(ACTION_INSPECT, UDim2.new(0, 0, 0, 0)) -- We can manipulate the button directly using ContextActionService:GetButton local imgButton = ContextActionService:GetButton(ACTION_INSPECT) if imgButton then -- Remember: non-touch devices won't return anything! imgButton.ImageColor3 = Color3.new(0.5, 1, 0.5) -- Tint the ImageButton green end ``` **Expected output:** When run on a touch-enabled device, a green-tinted button will appear. The message "Inspecting" will be printed when the button is pressed then released, or when the E key is pressed. ### Method: ContextActionService:SetImage **Signature:** `ContextActionService:SetImage(actionName: string, image: string): ()` This method sets the image shown on a touch button created by [BindAction()](/docs/reference/engine/classes/ContextActionService.md). Specifically, it sets the [ImageLabel.Image](/docs/reference/engine/classes/ImageLabel.md) property of the [ImageLabel](/docs/reference/engine/classes/ImageLabel.md) within the [ImageButton](/docs/reference/engine/classes/ImageButton.md) that would be returned by [GetButton](/docs/reference/engine/classes/ContextActionService.md). If no such bound action exists (e.g. nothing is returned by GetButton), this function does nothing and throws no error. This function is part of a family of methods that customize the touch button of an action. Others in this family include [SetPosition](/docs/reference/engine/classes/ContextActionService.md) and [SetTitle](/docs/reference/engine/classes/ContextActionService.md). *Security: None · Thread Safety: Unsafe · Capabilities: Input* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `actionName` | `string` | | The name of the action originally passed to BindAction. | | `image` | `string` | | The value to which the Image property should be set. | **Returns:** `()` **ContextActionService Touch Button** This code sample demonstrates binding an "Inspect" action to a touch button created automatically by `ContextActionService`. The button is customized using [SetImage()](/docs/reference/engine/classes/ContextActionService.md), [SetTitle()](/docs/reference/engine/classes/ContextActionService.md), [SetDescription()](/docs/reference/engine/classes/ContextActionService.md) and [SetPosition()](/docs/reference/engine/classes/ContextActionService.md). The button is further customized by using [GetButton()](/docs/reference/engine/classes/ContextActionService.md) to get a reference to the `ImageButton` itself and tinting it green by setting [ImageButton.ImageColor3](/docs/reference/engine/classes/ImageButton.md). Paste this code into a `LocalScript` placed within `StarterPlayerScripts` to test it. In Studio, be sure to toggle the touch device emulator in order for the button to actually be created. ```lua local ContextActionService = game:GetService("ContextActionService") local ACTION_INSPECT = "Inspect" local INPUT_INSPECT = Enum.KeyCode.E local IMAGE_INSPECT = "rbxassetid://1826746856" -- Image of a speech bubble with ? in it local function handleAction(actionName, inputState, _inputObject) if actionName == ACTION_INSPECT and inputState == Enum.UserInputState.End then print("Inspecting") end end -- For touch devices, a button is created on-screen automatically via the 3rd parameter ContextActionService:BindAction(ACTION_INSPECT, handleAction, true, INPUT_INSPECT) -- We can use these functions to customize the button: ContextActionService:SetImage(ACTION_INSPECT, IMAGE_INSPECT) ContextActionService:SetTitle(ACTION_INSPECT, "Look") ContextActionService:SetDescription(ACTION_INSPECT, "Inspect something.") ContextActionService:SetPosition(ACTION_INSPECT, UDim2.new(0, 0, 0, 0)) -- We can manipulate the button directly using ContextActionService:GetButton local imgButton = ContextActionService:GetButton(ACTION_INSPECT) if imgButton then -- Remember: non-touch devices won't return anything! imgButton.ImageColor3 = Color3.new(0.5, 1, 0.5) -- Tint the ImageButton green end ``` **Expected output:** When run on a touch-enabled device, a green-tinted button will appear. The message "Inspecting" will be printed when the button is pressed then released, or when the E key is pressed. ### Method: ContextActionService:SetPosition **Signature:** `ContextActionService:SetPosition(actionName: string, position: UDim2): ()` This method sets the position of a touch button created by [BindAction()](/docs/reference/engine/classes/ContextActionService.md). Specifically, it sets the [GuiObject.Position](/docs/reference/engine/classes/GuiObject.md) property of the [ImageButton](/docs/reference/engine/classes/ImageButton.md) that would be returned by [GetButton](/docs/reference/engine/classes/ContextActionService.md). If no such bound action exists (e.g. nothing is returned by GetButton), this function does nothing and throws no error. This function is part of a family of methods that customize the touch button of an action. Others in this family include [SetImage](/docs/reference/engine/classes/ContextActionService.md) and [SetTitle](/docs/reference/engine/classes/ContextActionService.md). *Security: None · Thread Safety: Unsafe · Capabilities: Input* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `actionName` | `string` | | The name of the action originally passed to BindAction. | | `position` | `UDim2` | | The position within the ContextButtonFrame. | **Returns:** `()` **ContextActionService Touch Button** This code sample demonstrates binding an "Inspect" action to a touch button created automatically by `ContextActionService`. The button is customized using [SetImage()](/docs/reference/engine/classes/ContextActionService.md), [SetTitle()](/docs/reference/engine/classes/ContextActionService.md), [SetDescription()](/docs/reference/engine/classes/ContextActionService.md) and [SetPosition()](/docs/reference/engine/classes/ContextActionService.md). The button is further customized by using [GetButton()](/docs/reference/engine/classes/ContextActionService.md) to get a reference to the `ImageButton` itself and tinting it green by setting [ImageButton.ImageColor3](/docs/reference/engine/classes/ImageButton.md). Paste this code into a `LocalScript` placed within `StarterPlayerScripts` to test it. In Studio, be sure to toggle the touch device emulator in order for the button to actually be created. ```lua local ContextActionService = game:GetService("ContextActionService") local ACTION_INSPECT = "Inspect" local INPUT_INSPECT = Enum.KeyCode.E local IMAGE_INSPECT = "rbxassetid://1826746856" -- Image of a speech bubble with ? in it local function handleAction(actionName, inputState, _inputObject) if actionName == ACTION_INSPECT and inputState == Enum.UserInputState.End then print("Inspecting") end end -- For touch devices, a button is created on-screen automatically via the 3rd parameter ContextActionService:BindAction(ACTION_INSPECT, handleAction, true, INPUT_INSPECT) -- We can use these functions to customize the button: ContextActionService:SetImage(ACTION_INSPECT, IMAGE_INSPECT) ContextActionService:SetTitle(ACTION_INSPECT, "Look") ContextActionService:SetDescription(ACTION_INSPECT, "Inspect something.") ContextActionService:SetPosition(ACTION_INSPECT, UDim2.new(0, 0, 0, 0)) -- We can manipulate the button directly using ContextActionService:GetButton local imgButton = ContextActionService:GetButton(ACTION_INSPECT) if imgButton then -- Remember: non-touch devices won't return anything! imgButton.ImageColor3 = Color3.new(0.5, 1, 0.5) -- Tint the ImageButton green end ``` **Expected output:** When run on a touch-enabled device, a green-tinted button will appear. The message "Inspecting" will be printed when the button is pressed then released, or when the E key is pressed. ### Method: ContextActionService:SetTitle **Signature:** `ContextActionService:SetTitle(actionName: string, title: string): ()` SetTitle will set the text shown on a touch button created by [BindAction](/docs/reference/engine/classes/ContextActionService.md). Specifically, this sets the [TextLabel.Text](/docs/reference/engine/classes/TextLabel.md) property of a [TextLabel](/docs/reference/engine/classes/TextLabel.md) within the [ImageButton](/docs/reference/engine/classes/ImageButton.md) that would be returned by [GetButton](/docs/reference/engine/classes/ContextActionService.md). If no such bound action exists (e.g. nothing is returned by GetButton), this function does nothing and throws no error. This function is part of a family of methods that customize the touch button of an action. Others in this family include [SetImage](/docs/reference/engine/classes/ContextActionService.md) and [SetPosition](/docs/reference/engine/classes/ContextActionService.md). *Security: None · Thread Safety: Unsafe · Capabilities: Input* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `actionName` | `string` | | The name of the action originally passed to BindAction. | | `title` | `string` | | The text to display on the button. | **Returns:** `()` **ContextActionService Touch Button** This code sample demonstrates binding an "Inspect" action to a touch button created automatically by `ContextActionService`. The button is customized using [SetImage()](/docs/reference/engine/classes/ContextActionService.md), [SetTitle()](/docs/reference/engine/classes/ContextActionService.md), [SetDescription()](/docs/reference/engine/classes/ContextActionService.md) and [SetPosition()](/docs/reference/engine/classes/ContextActionService.md). The button is further customized by using [GetButton()](/docs/reference/engine/classes/ContextActionService.md) to get a reference to the `ImageButton` itself and tinting it green by setting [ImageButton.ImageColor3](/docs/reference/engine/classes/ImageButton.md). Paste this code into a `LocalScript` placed within `StarterPlayerScripts` to test it. In Studio, be sure to toggle the touch device emulator in order for the button to actually be created. ```lua local ContextActionService = game:GetService("ContextActionService") local ACTION_INSPECT = "Inspect" local INPUT_INSPECT = Enum.KeyCode.E local IMAGE_INSPECT = "rbxassetid://1826746856" -- Image of a speech bubble with ? in it local function handleAction(actionName, inputState, _inputObject) if actionName == ACTION_INSPECT and inputState == Enum.UserInputState.End then print("Inspecting") end end -- For touch devices, a button is created on-screen automatically via the 3rd parameter ContextActionService:BindAction(ACTION_INSPECT, handleAction, true, INPUT_INSPECT) -- We can use these functions to customize the button: ContextActionService:SetImage(ACTION_INSPECT, IMAGE_INSPECT) ContextActionService:SetTitle(ACTION_INSPECT, "Look") ContextActionService:SetDescription(ACTION_INSPECT, "Inspect something.") ContextActionService:SetPosition(ACTION_INSPECT, UDim2.new(0, 0, 0, 0)) -- We can manipulate the button directly using ContextActionService:GetButton local imgButton = ContextActionService:GetButton(ACTION_INSPECT) if imgButton then -- Remember: non-touch devices won't return anything! imgButton.ImageColor3 = Color3.new(0.5, 1, 0.5) -- Tint the ImageButton green end ``` **Expected output:** When run on a touch-enabled device, a green-tinted button will appear. The message "Inspecting" will be printed when the button is pressed then released, or when the E key is pressed. ### Method: ContextActionService:UnbindAction **Signature:** `ContextActionService:UnbindAction(actionName: string): ()` UnbindAction will unbind an action by name from user inputs so that the action handler function will no longer be called. Call this function when the context for some action is no longer applicable, such as closing a user interface, exiting a car or [unequipping](/docs/reference/engine/classes/Tool.md) a [Tool](/docs/reference/engine/classes/Tool.md). See [BindAction](/docs/reference/engine/classes/ContextActionService.md) for more information on how bound actions operate. This function **will not** throw an error if there is no such action bound with the given string. Using [GetAllBoundActionInfo](/docs/reference/engine/classes/ContextActionService.md) or the Developer Console's "Action Bindings" tab, you can find out what actions are presently bound. *Security: None · Thread Safety: Unsafe · Capabilities: Input* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `actionName` | `string` | | | **Returns:** `()` **ContextActionService Tool Reload** This example properly shows how to use ContextActionService in binding user input to a contextual action. The context is the tool being equipped; the action is reloading some weapon. Test this code sample by placing it in a LocalScript parented to a Tool. When the Tool is equipped, a "Reload" action is bound, and when the Tool is unequipped the "Reload" action is unbound. When the player presses R with the Tool equipped, the message "Reloading!" will appear. ```lua local ContextActionService = game:GetService("ContextActionService") local ACTION_RELOAD = "Reload" local tool = script.Parent local function handleAction(actionName, inputState, _inputObject) if actionName == ACTION_RELOAD and inputState == Enum.UserInputState.Begin then print("Reloading!") end end tool.Equipped:Connect(function() ContextActionService:BindAction(ACTION_RELOAD, handleAction, true, Enum.KeyCode.R) end) tool.Unequipped:Connect(function() ContextActionService:UnbindAction(ACTION_RELOAD) end) ``` ### Method: ContextActionService:UnbindActivate **Signature:** `ContextActionService:UnbindActivate(userInputTypeForActivation: UserInputType, keyCodeForActivation?: KeyCode): ()` UnbindActivate unbinds an [KeyCode](/docs/reference/engine/enums/KeyCode.md) used with an [UserInputType](/docs/reference/engine/enums/UserInputType.md) for activating a [Tool](/docs/reference/engine/classes/Tool.md) (or a [HopperBin](/docs/reference/engine/classes/HopperBin.md)) using [BindActivate](/docs/reference/engine/classes/ContextActionService.md). This function essentially undoes the action performed by that function. *Security: None · Thread Safety: Unsafe · Capabilities: Input* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `userInputTypeForActivation` | `UserInputType` | | The same UserInputType originally sent to BindActivate. | | `keyCodeForActivation` | `KeyCode` | `Unknown` | The same KeyCode originally sent to BindActivate. | **Returns:** `()` ### Method: ContextActionService:UnbindAllActions **Signature:** `ContextActionService:UnbindAllActions(): ()` Removes all functions bound. No actionNames will remain. All touch buttons will be removed. If a button was manipulated manually there is no guarantee it will be cleaned up. *Security: None · Thread Safety: Unsafe · Capabilities: Input* **Returns:** `()` ### Method: ContextActionService:BindActionToInputTypes **Signature:** `ContextActionService:BindActionToInputTypes(actionName: string, functionToBind: Function, createTouchButton: boolean, inputTypes: Tuple): ()` > **Deprecated:** This item has been superseded by [ContextActionService:BindAction()](/docs/reference/engine/classes/ContextActionService.md) which should be used in all new work. This function binds _functionToBind_ to input events such as key presses, mouse movement, or controller input. The specific input types the engine listens for are listed as parameters of BindAction. Whenever a player uses any of these input types, the Roblox Engine calls "functionToBind". BindAction sets the priorityLevel via [ContextActionPriority](/docs/reference/engine/enums/ContextActionPriority.md) to Default.Value, which is 2000. Use [ContextActionService:GetButton()](/docs/reference/engine/classes/ContextActionService.md) to control the priority of bound events. In addition to input types, BindAction has a createTouchButton parameter. When this is set to true it creates an [ImageButton](/docs/reference/engine/classes/ImageButton.md) on any device with a touchscreen. A [ScreenGui](/docs/reference/engine/classes/ScreenGui.md) is also created to put the context buttons into named ContextActionGui and is parented to [PlayerGui](/docs/reference/engine/classes/PlayerGui.md). The created ImageButton is parented to this ContextActionGui. GetButton can be used to retrieve the button that was created. If an input has more than one function bound to it, each function will be placed on a stack. A stack obeys the principle of last in first out. So the first object placed on the stack will be on the top. The next object placed on the stack becomes the top and the previous object moves one position down (like a stack of books). When the input is triggered, the function at the top of the stack is called. If the function returns [ContextActionResult](/docs/reference/engine/enums/ContextActionResult.md).Pass this will continue down the stack. To remove a function from being called by all input that it was bound by use [ContextActionService:UnbindAction()](/docs/reference/engine/classes/ContextActionService.md). BindAction allows control over whether or not a bound action should be processed by other actions on the stack using [ContextActionResult](/docs/reference/engine/enums/ContextActionResult.md). If [ContextActionResult.Pass](/docs/reference/engine/enums/ContextActionResult.md) is returned in the callback function, every action below it in the stack (last function called gets executed first) will get a chance to process it. Anything other than Pass will be treated as [ContextActionResult.Sink](/docs/reference/engine/enums/ContextActionResult.md), including `nil`. It will also sink if the callback is yielded. *Security: None · Thread Safety: Unsafe · Capabilities: Input* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `actionName` | `string` | | | | `functionToBind` | `Function` | | | | `createTouchButton` | `boolean` | | | | `inputTypes` | `Tuple` | | | **Returns:** `()` ## Events ### Event: ContextActionService.LocalToolEquipped **Signature:** `ContextActionService.LocalToolEquipped(toolEquipped: Instance)` Fires when the current player equips a [Tool](/docs/reference/engine/classes/Tool.md). *Security: None · Capabilities: Input* **Parameters:** | Name | Type | Description | |------|------|-------------| | `toolEquipped` | `Instance` | | ### Event: ContextActionService.LocalToolUnequipped **Signature:** `ContextActionService.LocalToolUnequipped(toolUnequipped: Instance)` Fires when the current player unequips a [Tool](/docs/reference/engine/classes/Tool.md). *Security: None · Capabilities: Input* **Parameters:** | Name | Type | Description | |------|------|-------------| | `toolUnequipped` | `Instance` | | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Controller last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable summary: "The base class for controller objects, such as the HumanoidController object." --- # Class: Controller > The base class for controller objects, such as the [HumanoidController](/docs/reference/engine/classes/HumanoidController.md) > object. ## Methods ### Method: Controller:BindButton **Signature:** `Controller:BindButton(button: Button, caption: string): ()` Activates an overriding bind on the specified button. *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `button` | `Button` | | | | `caption` | `string` | | | **Returns:** `()` **Controller:BindButton1** The example below when placed inside a [LocalScript](/docs/reference/engine/classes/LocalScript.md), would result in a GUI saying "Press Backspace to win!". ```lua local ControllerService = game:GetService("ControllerService") local humanoidController = ControllerService:FindFirstChildOfClass("HumanoidController") humanoidController:BindButton(Enum.Button.Dismount, "win!") ``` ### Method: Controller:GetButton **Signature:** `Controller:GetButton(button: Button): boolean` Returns whether or not Button is being pressed. *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `button` | `Button` | | | **Returns:** `boolean` ### Method: Controller:UnbindButton **Signature:** `Controller:UnbindButton(button: Button): ()` Removes the bind on button. *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `button` | `Button` | | | **Returns:** `()` ### Method: Controller:bindButton **Signature:** `Controller:bindButton(button: Button, caption: string): ()` > **Deprecated:** This function is a deprecated variant of [Controller:BindButton()](/docs/reference/engine/classes/Controller.md) which should be used instead. *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `button` | `Button` | | | | `caption` | `string` | | | **Returns:** `()` ### Method: Controller:getButton **Signature:** `Controller:getButton(button: Button): boolean` > **Deprecated:** This function is a deprecated variant of [Controller:GetButton()](/docs/reference/engine/classes/Controller.md) which should be used instead. *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `button` | `Button` | | | **Returns:** `boolean` ## Events ### Event: Controller.ButtonChanged **Signature:** `Controller.ButtonChanged(button: Button)` Fired when the pressed state of a bound button is changed. This event can be used in conjunction with [Controller:GetButton()](/docs/reference/engine/classes/Controller.md) to see whether a bound button is being pressed down or not. *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `button` | `Button` | | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ControllerBase last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable --- # Class: ControllerBase ## Properties ### Property: ControllerBase.Active ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "simulationAccess": true } ``` ### Property: ControllerBase.BalanceRigidityEnabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Balance", "simulationAccess": true } ``` ### Property: ControllerBase.MoveSpeedFactor ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Movement", "simulationAccess": true } ``` The value multiplied by the [ControllerManager.BaseMoveSpeed](/docs/reference/engine/classes/ControllerManager.md) to determine the final desired linear velocity while this controller is active. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ControllerManager last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances summary: "Manages simulated motion control for its assigned ControllerManager.RootPart ." --- # Class: ControllerManager > Manages simulated motion control for its assigned > [ControllerManager.RootPart](/docs/reference/engine/classes/ControllerManager.md) . ## Description The [ControllerManager](/docs/reference/engine/classes/ControllerManager.md) instance manages simulated motion control for its assigned [ControllerManager.RootPart](/docs/reference/engine/classes/ControllerManager.md). It can be used to build a physics-based character controller. ## Properties ### Property: ControllerManager.ActiveController ```json { "type": "ControllerBase", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "AvatarBehavior" ], "simulationAccess": true } ``` The [ControllerBase](/docs/reference/engine/classes/ControllerBase.md) that is set to be activated on the character. It does not guarantee that the specified [ControllerBase](/docs/reference/engine/classes/ControllerBase.md) is, in fact, active. If the [ControllerBase](/docs/reference/engine/classes/ControllerBase.md) cannot be activated for whatever reason, such as being outside of the character's [WorldRoot](/docs/reference/engine/classes/WorldRoot.md) or no [Part](/docs/reference/engine/classes/Part.md) being found to use as the floor for a [GroundController](/docs/reference/engine/classes/GroundController.md), it will remain set and the [ControllerManager](/docs/reference/engine/classes/ControllerManager.md) will attempt to activate it in the next frame. This property can be set to `nil` to disable the physics controller. ### Property: ControllerManager.BaseMoveSpeed ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "AvatarBehavior" ], "simulationAccess": true } ``` The base linear movement speed used by all controllers. Controllers individually customize speed by setting the [ControllerBase.MoveSpeedFactor](/docs/reference/engine/classes/ControllerBase.md) property. ### Property: ControllerManager.BaseTurnSpeed ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "AvatarBehavior" ], "simulationAccess": true } ``` The base angular turning speed used by all controllers to align the character to face the desired direction. ### Property: ControllerManager.ClimbSensor ```json { "type": "ControllerSensor", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "AvatarBehavior" ], "simulationAccess": true } ``` A reference to the sensor data used while a [ClimbController](/docs/reference/engine/classes/ClimbController.md) is active. A [ClimbController](/docs/reference/engine/classes/ClimbController.md) will use the [ControllerPartSensor.HitPart](/docs/reference/engine/classes/ControllerPartSensor.md), [ControllerPartSensor.HitFrame](/docs/reference/engine/classes/ControllerPartSensor.md), and [ControllerPartSensor.HitNormal](/docs/reference/engine/classes/ControllerPartSensor.md) for climb movement computations. Typically a [ControllerPartSensor](/docs/reference/engine/classes/ControllerPartSensor.md) set to [SensorMode.Ladder](/docs/reference/engine/enums/SensorMode.md) is used here. Otherwise, you can override the sensor's outputs to direct what sensor data you want the [ClimbController](/docs/reference/engine/classes/ClimbController.md) to use. ### Property: ControllerManager.FacingDirection ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Input", "capabilities": [ "AvatarBehavior" ], "simulationAccess": true } ``` The unit vector describing the desired direction to face. Aligns the [LookVector](/docs/reference/engine/datatypes/CFrame.md) of the [ControllerManager.RootPart](/docs/reference/engine/classes/ControllerManager.md) to this. Any [Vector3](/docs/reference/engine/datatypes/Vector3.md) assigned will be automatically unitized. ### Property: ControllerManager.GroundSensor ```json { "type": "ControllerSensor", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "AvatarBehavior" ], "simulationAccess": true } ``` A reference to the sensor data used while a [GroundController](/docs/reference/engine/classes/GroundController.md) is active. A [GroundController](/docs/reference/engine/classes/GroundController.md) will use the [ControllerPartSensor.HitPart](/docs/reference/engine/classes/ControllerPartSensor.md), [ControllerPartSensor.HitFrame](/docs/reference/engine/classes/ControllerPartSensor.md), and [ControllerPartSensor.HitNormal](/docs/reference/engine/classes/ControllerPartSensor.md) for ground movement computations. Typically a [ControllerPartSensor](/docs/reference/engine/classes/ControllerPartSensor.md) set to [SensorMode.Floor](/docs/reference/engine/enums/SensorMode.md) is used here. Otherwise, you can override the sensor's outputs to direct what sensor data you want the [GroundController](/docs/reference/engine/classes/GroundController.md) to use. ### Property: ControllerManager.MovingDirection ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Input", "capabilities": [ "AvatarBehavior" ], "simulationAccess": true } ``` The vector describing the desired direction to move in, with a magnitude between 0 and 1. This is multiplied by [BaseMoveSpeed](/docs/reference/engine/classes/ControllerManager.md) to determine a final target move velocity. The [RootPart](/docs/reference/engine/classes/ControllerManager.md) will attempt to move in this direction based on the rules defined by the [ActiveController](/docs/reference/engine/classes/ControllerManager.md). ### Property: ControllerManager.RootPart ```json { "type": "BasePart", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "AvatarBehavior" ], "simulationAccess": true } ``` The [BasePart](/docs/reference/engine/classes/BasePart.md) where the controller's forces and torques are applied. With a typical [Humanoid](/docs/reference/engine/classes/Humanoid.md)-based character, the [Humanoid.RootPart](/docs/reference/engine/classes/Humanoid.md) is assigned as the [ControllerManager.RootPart](/docs/reference/engine/classes/ControllerManager.md). ### Property: ControllerManager.UpDirection ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Input", "capabilities": [ "AvatarBehavior" ], "simulationAccess": true } ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ControllerPartSensor last_updated: 2026-06-29T19:34:07Z inherits: - ControllerSensor - SensorBase - Instance - Object type: class memory_category: Instances summary: "A SensorBase that outputs data about another BasePart based on Humanoid floor and ladder detection logic." --- # Class: ControllerPartSensor > A [SensorBase](/docs/reference/engine/classes/SensorBase.md) that outputs data about another [BasePart](/docs/reference/engine/classes/BasePart.md) based on > [Humanoid](/docs/reference/engine/classes/Humanoid.md) floor and ladder detection logic. ## Description A [SensorBase](/docs/reference/engine/classes/SensorBase.md) that outputs data about another [BasePart](/docs/reference/engine/classes/BasePart.md) based on [Humanoid](/docs/reference/engine/classes/Humanoid.md) floor and ladder detection logic. It is primarily used for sending data to a character controller. Using a [ControllerPartSensor](/docs/reference/engine/classes/ControllerPartSensor.md) allows you to detect [BaseParts](/docs/reference/engine/classes/BasePart.md) in the same manner as the [Humanoid](/docs/reference/engine/classes/Humanoid.md) uses for detecting floors and ladders. ## Properties ### Property: ControllerPartSensor.HitFrame ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Output", "capabilities": [ "Basic" ], "simulationAccess": true } ``` The position in world space where the sensor hit the [ControllerPartSensor.SensedPart](/docs/reference/engine/classes/ControllerPartSensor.md). ### Property: ControllerPartSensor.HitNormal ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Output", "capabilities": [ "Basic" ], "simulationAccess": true } ``` The surface normal at the position where the sensor hit the SensedPart. ### Property: ControllerPartSensor.LadderSearchHeight ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Input", "capabilities": [ "Basic" ], "simulationAccess": true } ``` ### Property: ControllerPartSensor.LadderSearchOffset ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Input", "capabilities": [ "Basic" ], "simulationAccess": true } ``` ### Property: ControllerPartSensor.SearchDistance ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Input", "capabilities": [ "Basic" ], "simulationAccess": true } ``` The distance from the sensor's parent [BasePart](/docs/reference/engine/classes/BasePart.md) to use when sensing other parts. ### Property: ControllerPartSensor.SensedMaterial ```json { "type": "Material", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ], "simulationAccess": true } ``` ### Property: ControllerPartSensor.SensedPart ```json { "type": "BasePart", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Output", "capabilities": [ "Basic" ], "simulationAccess": true } ``` A reference to the [BasePart](/docs/reference/engine/classes/BasePart.md) hit by the sensor. ### Property: ControllerPartSensor.SensorMode ```json { "type": "SensorMode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Input", "capabilities": [ "Basic" ], "simulationAccess": true } ``` Determines what behavior this [SensorBase](/docs/reference/engine/classes/SensorBase.md) uses when sensing other parts. A setting of [SensorMode.Ladder](/docs/reference/engine/enums/SensorMode.md) performs the same ladder detection logic used by a [Humanoid](/docs/reference/engine/classes/Humanoid.md) to populate sensor output. It currently always senses along the forward vector of its parent [BasePart](/docs/reference/engine/classes/BasePart.md). A setting of [SensorMode.Floor](/docs/reference/engine/enums/SensorMode.md) performs the same floor detection logic used by a [Humanoid](/docs/reference/engine/classes/Humanoid.md) to populate sensor output. It currently always senses along the negative up vector of the world. ## Inherited Members ### From [SensorBase](/docs/reference/engine/classes/SensorBase.md) - **Property `UpdateType`** (`SensorUpdateType`): Determines how the sensor will update its output data. - **Method `Sense(): ()`**: *(deprecated)* - **Event `OnSensorOutputChanged`**: ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ControllerSensor last_updated: 2026-06-29T19:34:07Z inherits: - SensorBase - Instance - Object type: class memory_category: Instances tags: - NotCreatable --- # Class: ControllerSensor ## Inherited Members ### From [SensorBase](/docs/reference/engine/classes/SensorBase.md) - **Property `UpdateType`** (`SensorUpdateType`): Determines how the sensor will update its output data. - **Method `Sense(): ()`**: *(deprecated)* - **Event `OnSensorOutputChanged`**: ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ControllerService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated summary: "Container class for the HumanoidController among other classes." --- # Class: ControllerService > Container class for the [HumanoidController](/docs/reference/engine/classes/HumanoidController.md) among other classes. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: CookiesService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service summary: "A backend service used by Roblox to control HTTP cookies. Its functions are not available to developers." --- # Class: CookiesService > A backend service used by Roblox to control HTTP cookies. Its functions are > not available to developers. ## Description Used by Roblox to control HTTP cookies for analytical purposes. Can only be used by Roblox's backend servers, and therefore is not available to developers in any shape or form. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: CoreGui last_updated: 2026-06-29T19:34:07Z inherits: - BasePlayerGui - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated summary: "The CoreGui is a service used to store Guis created in-game by Roblox for the core user interface found in every game (such as the game menu, the playerlist, the backpack, etc.). It can also be used by Plugins in Roblox Studio." --- # Class: CoreGui > The CoreGui is a service used to store Guis created in-game by Roblox for the > core user interface found in every game (such as the game menu, the > playerlist, the backpack, etc.). It can also be used by [Plugins](/docs/reference/engine/classes/Plugin.md) > in Roblox Studio. ## Description The CoreGui is a service used to store Guis created in-game by Roblox for the core user interface found in every game (such as the game menu, the playerlist, the backpack, etc.). It can also be used by [Plugins](/docs/reference/engine/classes/Plugin.md) in Roblox Studio. You can use the [StarterGui:SetCoreGuiEnabled()](/docs/reference/engine/classes/StarterGui.md) and [StarterGui:GetCoreGuiEnabled()](/docs/reference/engine/classes/StarterGui.md) methods in a [LocalScript](/docs/reference/engine/classes/LocalScript.md) to enable and disable most elements of the CoreGui. You can also use [PlayerGui:SetTopbarTransparency()](/docs/reference/engine/classes/PlayerGui.md) to set the transparency of the top bar. ## Properties ### Property: CoreGui.SelectionImageObject ```json { "type": "GuiObject", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "UI" ] } ``` ### Property: CoreGui.Version ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` The current version of the CoreGui. Every time the CoreGui is majorly changed, this number is increased. ## Inherited Members ### From [BasePlayerGui](/docs/reference/engine/classes/BasePlayerGui.md) - **Method `GetGuiObjectsAtPosition(x: int, y: int): Instances`**: Returns a list of all GuiObject instances occupying the given ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: CoreGuiConfiguration last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated --- # Class: CoreGuiConfiguration ## Properties ### Property: CoreGuiConfiguration.CapturesViewConfiguration ```json { "type": "CapturesViewConfiguration", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI", "Capture" ] } ``` ### Property: CoreGuiConfiguration.PlayerListConfiguration ```json { "type": "PlayerListConfiguration", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` ### Property: CoreGuiConfiguration.SelfViewConfiguration ```json { "type": "SelfViewConfiguration", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: CoreScriptDebuggingManagerHelper last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service --- # Class: CoreScriptDebuggingManagerHelper ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: CornerWedgePart last_updated: 2026-06-29T19:34:07Z inherits: - BasePart - PVInstance - Instance - Object type: class memory_category: Instances --- # Class: CornerWedgePart ## Description This is a corner piece which has the same properties as a [Part](/docs/reference/engine/classes/Part.md). ## Inherited Members ### From [BasePart](/docs/reference/engine/classes/BasePart.md) - **Property `Anchored`** (`boolean`): Determines whether a part is immovable by physics. - **Property `AssemblyAngularVelocity`** (`Vector3`): The angular velocity of the part's assembly. - **Property `AssemblyCenterOfMass`** (`Vector3`): The center of mass of the part's assembly in world space. - **Property `AssemblyLinearVelocity`** (`Vector3`): The linear velocity of the part's assembly. - **Property `AssemblyMass`** (`float`): The total mass of the part's assembly. - **Property `AssemblyRootPart`** (`BasePart`): A reference to the root part of the assembly. - **Property `AudioCanCollide`** (`boolean`): Determines whether the part will physically interact with audio - **Property `BackParamA`** (`float`): Determines the first parameter for the SurfaceType on the Back face of a *(deprecated, hidden)* - **Property `BackParamB`** (`float`): Determines the second parameter for the SurfaceType on the Back face of a *(deprecated, hidden)* - **Property `BackSurface`** (`SurfaceType`): Determines the type of surface for the back face of a part. - **Property `BackSurfaceInput`** (`InputType`): Determines the kind of input for the Back face of a part. *(deprecated, hidden)* - **Property `BottomParamA`** (`float`): Determines the first parameter for the SurfaceType on the Bottom face of a *(deprecated, hidden)* - **Property `BottomParamB`** (`float`): Determines the second parameter for the SurfaceType on the Bottom face of *(deprecated, hidden)* - **Property `BottomSurface`** (`SurfaceType`): Determines the type of surface for the bottom face of a part. - **Property `BottomSurfaceInput`** (`InputType`): Determines the kind of input for the Bottom face of a part. *(deprecated, hidden)* - **Property `BrickColor`** (`BrickColor`): Determines the color of a part. - **Property `brickColor`** (`BrickColor`): *(deprecated)* - **Property `CanCollide`** (`boolean`): Determines whether a part may collide with other parts. - **Property `CanQuery`** (`boolean`): Determines whether the part is considered during spatial query operations. - **Property `CanTouch`** (`boolean`): Determines if Touched and - **Property `CastShadow`** (`boolean`): Determines whether or not a part casts a shadow. - **Property `CenterOfMass`** (`Vector3`): Describes the world position in which a part's center of mass is located. - **Property `CFrame`** (`CFrame`): Determines the position and orientation of the BasePart in the - **Property `CollisionGroup`** (`string`): Describes the name of a part's collision group. - **Property `CollisionGroupId`** (`int`): Describes the automatically set ID number of a part's collision group. *(deprecated)* - **Property `Color`** (`Color3`): Determines the color of a part. - **Property `CurrentPhysicalProperties`** (`PhysicalProperties`): Indicates the current physical properties of the part. - **Property `CustomPhysicalProperties`** (`PhysicalProperties`): Determines several physical properties of a part. - **Property `Elasticity`** (`float`): Used to control the Elasticity of the part, but it no longer does *(deprecated, hidden)* - **Property `EnableFluidForces`** (`boolean`): Used to enable or disable aerodynamic forces on parts and assemblies. - **Property `ExtentsCFrame`** (`CFrame`): The CFrame of the physical extents of the BasePart. - **Property `ExtentsSize`** (`Vector3`): The actual physical size of the BasePart as regarded by the - **Property `Friction`** (`float`): Used to control the Friction of the part, but now it no longer does *(deprecated, hidden)* - **Property `FrontParamA`** (`float`): Determines the first parameter for the SurfaceType on the Front face of a *(deprecated, hidden)* - **Property `FrontParamB`** (`float`): Determines the second parameter for the SurfaceType on the Front face of a *(deprecated, hidden)* - **Property `FrontSurface`** (`SurfaceType`): Determines the type of surface for the front face of a part. - **Property `FrontSurfaceInput`** (`InputType`): Determines the kind of input for the Front face of a part (-Z direction). *(deprecated, hidden)* - **Property `LeftParamA`** (`float`): Determines the first parameter for the SurfaceType on the Left face of a *(deprecated, hidden)* - **Property `LeftParamB`** (`float`): Determines the second parameter for the SurfaceType on the Left face of a *(deprecated, hidden)* - **Property `LeftSurface`** (`SurfaceType`): Determines the type of surface for the left face of a part. - **Property `LeftSurfaceInput`** (`InputType`): Determines the kind of input for the Left face of a part. *(deprecated, hidden)* - **Property `LocalTransparencyModifier`** (`float`): Determines a multiplier for BasePart.Transparency that is only *(hidden)* - **Property `Locked`** (`boolean`): Determines whether a part is selectable in Studio. - **Property `Mass`** (`float`): Describes the mass of the part, the product of its density and volume. - **Property `Massless`** (`boolean`): Determines whether the part contributes to the total mass or inertia of - **Property `Material`** (`Material`): Determines the texture and default physical properties of a part. - **Property `MaterialVariant`** (`string`): The name of MaterialVariant. - **Property `Orientation`** (`Vector3`): Describes the rotation of the part in the world. *(hidden)* - **Property `PivotOffset`** (`CFrame`): Specifies the offset of the part's pivot from its CFrame. - **Property `Position`** (`Vector3`): Describes the position of the part in the world. *(hidden)* - **Property `ReceiveAge`** (`float`): Time since last recorded physics update. *(hidden)* - **Property `Reflectance`** (`float`): Determines how much a part reflects the skybox. - **Property `ResizeableFaces`** (`Faces`): Describes the faces on which a part may be resized. - **Property `ResizeIncrement`** (`int`): Describes the smallest change in size allowable by the - **Property `RightParamA`** (`float`): Determines the first parameter for the SurfaceType on the Right face of a *(deprecated, hidden)* - **Property `RightParamB`** (`float`): Determines the second parameter for the SurfaceType on the Right face of a *(deprecated, hidden)* - **Property `RightSurface`** (`SurfaceType`): Determines the type of surface for the right face of a part. - **Property `RightSurfaceInput`** (`InputType`): Determines the kind of input for the Right face of a part (-X direction). *(deprecated, hidden)* - **Property `RootPriority`** (`int`): The main rule in determining the root part of an assembly. - **Property `Rotation`** (`Vector3`): The rotation of the part in degrees for the three axes. - **Property `RotVelocity`** (`Vector3`): Determines a part's change in orientation over time. *(deprecated, hidden)* - **Property `Size`** (`Vector3`): Determines the dimensions of a part (length, width, height). - **Property `SpecificGravity`** (`float`): The ratio of the part's density to the density of water determined by the *(deprecated)* - **Property `TopParamA`** (`float`): Determines the first parameter for the SurfaceType on the Top face of a *(deprecated, hidden)* - **Property `TopParamB`** (`float`): Determines the second parameter for the SurfaceType on the Top face of a *(deprecated, hidden)* - **Property `TopSurface`** (`SurfaceType`): Determines the type of surface for the top face of a part. - **Property `TopSurfaceInput`** (`InputType`): Determines the kind of input for the Top face of a part (+Y direction). *(deprecated, hidden)* - **Property `Transparency`** (`float`): Determines how much a part can be seen through (the inverse of part - **Property `Velocity`** (`Vector3`): Determines a part's change in position over time. *(deprecated, hidden)* - **Method `AngularAccelerationToTorque(angAcceleration: Vector3, angVelocity?: Vector3): Vector3`**: Returns the torque needed to achieve a given angular acceleration on this - **Method `ApplyAngularImpulse(impulse: Vector3): ()`**: Apply an angular impulse to the assembly. - **Method `ApplyImpulse(impulse: Vector3): ()`**: Apply an impulse to the assembly at the assembly's - **Method `ApplyImpulseAtPosition(impulse: Vector3, position: Vector3): ()`**: Apply an impulse to the assembly at specified position. - **Method `BreakJoints(): ()`**: Breaks any surface connection with any adjacent part, including *(deprecated)* - **Method `breakJoints(): ()`**: *(deprecated)* - **Method `CanCollideWith(part: BasePart): boolean`**: Returns whether the parts can collide with each other. - **Method `CanSetNetworkOwnership(): Tuple`**: Checks whether you can set a part's network ownership. - **Method `GetClosestPointOnSurface(position: Vector3): Vector3`**: Returns the closest point on the part's surface to the given point. - **Method `GetConnectedParts(recursive?: boolean): List`**: Returns a table of parts connected to the object by any kind of rigid - **Method `GetJoints(): Instances`**: Return all Joints or Constraints that is connected to this Part. - **Method `GetMass(): float`**: Returns the value of the Mass property. - **Method `getMass(): float`**: *(deprecated)* - **Method `GetNetworkOwner(): Instance`**: Returns the current player who is the network owner of this part, or `nil` - **Method `GetNetworkOwnershipAuto(): boolean`**: Returns true if the game engine automatically decides the network owner - **Method `GetNoCollisionConstraints(): Instances`**: - **Method `GetRenderCFrame(): CFrame`**: OBSOLETE. Returns a CFrame describing where the part is being rendered at. *(deprecated)* - **Method `GetRootPart(): Instance`**: Returns the base part of an assembly of parts. *(deprecated)* - **Method `GetTouchingParts(): Instances`**: Returns a table of all BasePart.CanCollide true parts that - **Method `GetVelocityAtPosition(position: Vector3): Vector3`**: Returns the linear velocity of the part's assembly at the given position - **Method `IntersectAsync(parts: Instances, collisionfidelity?: CollisionFidelity, renderFidelity?: RenderFidelity): Instance`**: Note: It is highly recommended to use the newer - **Method `IsGrounded(): boolean`**: Returns true if the object is connected to a part that will hold it in - **Method `MakeJoints(): ()`**: Creates a joint on any side of the object that has a surface ID that can *(deprecated)* - **Method `makeJoints(): ()`**: *(deprecated)* - **Method `Resize(normalId: NormalId, deltaAmount: int): boolean`**: Changes the size of an object just like using the Studio resize tool. - **Method `resize(normalId: NormalId, deltaAmount: int): boolean`**: *(deprecated)* - **Method `SetNetworkOwner(playerInstance?: Player): ()`**: Sets the given player as network owner for this and all connected parts. - **Method `SetNetworkOwnershipAuto(): ()`**: Lets the game engine dynamically decide who will handle the part's physics - **Method `SubtractAsync(parts: Instances, collisionfidelity?: CollisionFidelity, renderFidelity?: RenderFidelity): Instance`**: Note: It is highly recommended to use the newer - **Method `TorqueToAngularAcceleration(torque: Vector3, angVelocity?: Vector3): Vector3`**: Returns the angular acceleration that would result from applying a given - **Method `UnionAsync(parts: Instances, collisionfidelity?: CollisionFidelity, renderFidelity?: RenderFidelity): Instance`**: Note: It is highly recommended to use the newer - **Event `LocalSimulationTouched`**: *(deprecated)* - **Event `OutfitChanged`**: *(deprecated)* - **Event `StoppedTouching`**: *(deprecated)* - **Event `Touched`**: Fires when a part touches another part as a result of physical movement. - **Event `TouchEnded`**: Fires when a part stops touching another part as a result of physical ### From [PVInstance](/docs/reference/engine/classes/PVInstance.md) - **Property `Origin`** (`CFrame`): - **Property `Pivot Offset`** (`CFrame`): - **Method `GetPivot(): CFrame`**: Gets the pivot of a PVInstance. - **Method `PivotTo(targetCFrame: CFrame): ()`**: Transforms the PVInstance along with all of its descendant ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: CreatorStoreService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service --- # Class: CreatorStoreService ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: CurveAnimation last_updated: 2026-06-29T19:34:07Z inherits: - AnimationClip - Instance - Object type: class memory_category: Instances summary: "Stores animation data in the form of curves for each individual channel to animate." --- # Class: CurveAnimation > Stores animation data in the form of curves for each individual channel to > animate. ## Description `CurveAnimation` is a subtype of [AnimationClip](/docs/reference/engine/classes/AnimationClip.md) consumed by Roblox's animation system. It stores animation data for each animated channel in a rig as a separate, individual curve. For example, `CurveAnimation` stores the position channel for an articulated joint as a [Vector3Curve](/docs/reference/engine/classes/Vector3Curve.md), and it might store the rotation channel as a [EulerRotationCurve](/docs/reference/engine/classes/EulerRotationCurve.md) or [RotationCurve](/docs/reference/engine/classes/RotationCurve.md). #### Structure `CurveAnimation` stores curves in a hierarchical manner, matching the hierarchy of the structure of [Motor6Ds](/docs/reference/engine/classes/Motor6D.md) or [Bones](/docs/reference/engine/classes/Bone.md) in the animated model. Under each `CurveAnimation` instance lies a hierarchy of [Folder](/docs/reference/engine/classes/Folder.md) instances representing animated joints in the model. Under each such folder instance, several possible instances may exist. An instance named `Position` of type [Vector3Curve](/docs/reference/engine/classes/Vector3Curve.md) can drive the local translation of the [Motor6D](/docs/reference/engine/classes/Motor6D.md) or [Bone](/docs/reference/engine/classes/Bone.md) on the animated model, while an instance named `Rotation` of type [EulerRotationCurve](/docs/reference/engine/classes/EulerRotationCurve.md) or [RotationCurve](/docs/reference/engine/classes/RotationCurve.md) can drive the local rotation of the [Motor6D](/docs/reference/engine/classes/Motor6D.md) or [Bone](/docs/reference/engine/classes/Bone.md) on the animated model. #### Partial matching of hierarchy You can match partial hierarchies to a model when playing a `CurveAnimation` in Roblox's animation system. This means that not all joints need to be present in the hierarchy for the existing joints to apply properly. Furthermore, you can match hierarchies in a "relative" manner. For example, the first child [Folder](/docs/reference/engine/classes/Folder.md) instance root can be `UpperTorso` and the animation system matches that to any existing sub-hierarchies in the model. #### Animating miscellaneous channels `CurveAnimation` can also animate other numerical values in a model. For example, you can animate FACS controls for facial animations by creating a [Folder](/docs/reference/engine/classes/Folder.md) under the `CurveAnimation` instance named after an existing [FaceControls](/docs/reference/engine/classes/FaceControls.md) instance in the model. Then, to animate individual facial controllers, you can store individual [FloatCurve](/docs/reference/engine/classes/FloatCurve.md) instances named after the animated [FaceControls](/docs/reference/engine/classes/FaceControls.md) property. #### Usage when making animations As with other [AnimationClip](/docs/reference/engine/classes/AnimationClip.md) types such as [KeyframeSequence](/docs/reference/engine/classes/KeyframeSequence.md), you must first upload `CurveAnimation` instances to Roblox before playing them. If you want to preview an animation before uploading it to Roblox, you can generate a temporary ID using [AnimationClipProvider:RegisterAnimationClip()](/docs/reference/engine/classes/AnimationClipProvider.md); this generates a hash ID that you can use for localized animation testing. If you want to download the `CurveAnimation` corresponding to an existing uploaded animation using Luau scripts, use [AnimationClipProvider:GetAnimationClipAsync()](/docs/reference/engine/classes/AnimationClipProvider.md). ## Inherited Members ### From [AnimationClip](/docs/reference/engine/classes/AnimationClip.md) - **Property `Length`** (`float`): Returns the length (in seconds) of this AnimationClip. This will - **Property `Loop`** (`boolean`): Determines whether the animation stored in this AnimationClip is - **Property `Priority`** (`AnimationPriority`): Determines which clip takes priority when multiple animations are playing ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: CustomEvent last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - Deprecated --- # Class: CustomEvent ## Methods ### Method: CustomEvent:GetAttachedReceivers **Signature:** `CustomEvent:GetAttachedReceivers(): Instances` This function returns the [CustomEventReceivers](/docs/reference/engine/classes/CustomEventReceiver.md) that are connected to the [CustomEvent](/docs/reference/engine/classes/CustomEvent.md). *Security: None · Thread Safety: Unsafe* **Returns:** `Instances` ### Method: CustomEvent:SetValue **Signature:** `CustomEvent:SetValue(newValue: float): ()` This function sets the value of the [CustomEvent](/docs/reference/engine/classes/CustomEvent.md) and fires the [CustomEventReceiver.SourceValueChanged](/docs/reference/engine/classes/CustomEventReceiver.md) event for all connected `CustomEventReceiver|receivers`. *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `newValue` | `float` | | | **Returns:** `()` ## Events ### Event: CustomEvent.ReceiverConnected **Signature:** `CustomEvent.ReceiverConnected(receiver: Instance)` This event fires when a receiver is connected to the [CustomEvent](/docs/reference/engine/classes/CustomEvent.md) *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `receiver` | `Instance` | | ### Event: CustomEvent.ReceiverDisconnected **Signature:** `CustomEvent.ReceiverDisconnected(receiver: Instance)` This event fires when a receiver is disconnected from the [CustomEvent](/docs/reference/engine/classes/CustomEvent.md). *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `receiver` | `Instance` | | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: CustomEventReceiver last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - Deprecated --- # Class: CustomEventReceiver ## Properties ### Property: CustomEventReceiver.Source ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` This property attaches the [CustomEventReceiver](/docs/reference/engine/classes/CustomEventReceiver.md) object to a [CustomEvent](/docs/reference/engine/classes/CustomEvent.md). ## Methods ### Method: CustomEventReceiver:GetCurrentValue **Signature:** `CustomEventReceiver:GetCurrentValue(): float` This function returns the current value of the receiver's [CustomEventReceiver.Source](/docs/reference/engine/classes/CustomEventReceiver.md) property. *Security: None · Thread Safety: Unsafe* **Returns:** `float` ## Events ### Event: CustomEventReceiver.EventConnected **Signature:** `CustomEventReceiver.EventConnected(event: Instance)` This event fires when the receiver is attached to a different [CustomEvent](/docs/reference/engine/classes/CustomEvent.md), when the [CustomEventReceiver.Source](/docs/reference/engine/classes/CustomEventReceiver.md) property is changed. *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `event` | `Instance` | | ### Event: CustomEventReceiver.EventDisconnected **Signature:** `CustomEventReceiver.EventDisconnected(event: Instance)` This event fires when the receiver is attached to a different [CustomEvent](/docs/reference/engine/classes/CustomEvent.md), when the [CustomEventReceiver.Source](/docs/reference/engine/classes/CustomEventReceiver.md) property is changed. *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `event` | `Instance` | | ### Event: CustomEventReceiver.SourceValueChanged **Signature:** `CustomEventReceiver.SourceValueChanged(newValue: float)` Fires when the value of the CustomEvent's source is changed, passing the CustomEvent's new value. *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `newValue` | `float` | | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: CustomLog last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotReplicated --- # Class: CustomLog ## Methods ### Method: CustomLog:Close **Signature:** `CustomLog:Close(): ()` *Security: None · Thread Safety: Unsafe · Capabilities: InternalTest* **Returns:** `()` ### Method: CustomLog:GetLogPath **Signature:** `CustomLog:GetLogPath(): string` *Security: None · Thread Safety: Unsafe · Capabilities: InternalTest* **Returns:** `string` ### Method: CustomLog:Open **Signature:** `CustomLog:Open(): ()` *Security: None · Thread Safety: Unsafe · Capabilities: InternalTest* **Returns:** `()` ### Method: CustomLog:WriteAppend **Signature:** `CustomLog:WriteAppend(append: string): ()` *Security: None · Thread Safety: Unsafe · Capabilities: InternalTest* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `append` | `string` | | | **Returns:** `()` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: CylinderHandleAdornment last_updated: 2026-06-29T19:34:07Z inherits: - HandleAdornment - PVAdornment - GuiBase3d - GuiBase - Instance - Object type: class memory_category: Instances summary: "A cylinder-shaped handle that can be adorned to a BasePart." --- # Class: CylinderHandleAdornment > A cylinder-shaped handle that can be adorned to a [BasePart](/docs/reference/engine/classes/BasePart.md). ## Description `CylinderHandleAdornment` is a cylinder-shaped handle that can be adorned to a [BasePart](/docs/reference/engine/classes/BasePart.md). If parented to a player's [PlayerGui](/docs/reference/engine/classes/PlayerGui.md) or the [CoreGui](/docs/reference/engine/classes/CoreGui.md), handles can listen to input events for purposes such as making dragger tools. ## Properties ### Property: CylinderHandleAdornment.Angle ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic", "UI" ] } ``` The angle of the cylinder handle as in a "pie slice" sector. For example, if set to `90`, a quarter cylinder will render instead of a full cylinder. This property can be combined with [InnerRadius](/docs/reference/engine/classes/CylinderHandleAdornment.md) to render a sector of a hollow cylinder. ### Property: CylinderHandleAdornment.Height ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic", "UI" ] } ``` The height of the cylinder adornment in studs. ### Property: CylinderHandleAdornment.InnerRadius ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic", "UI" ] } ``` The inner radius with which to render a hollow cylinder, in studs. For example, if [Radius](/docs/reference/engine/classes/CylinderHandleAdornment.md) is `2` and `InnerRadius` is `1`, the adornment will be a hollow cylinder with a "wall thickness" of 1 stud. This property can be combined with [Angle](/docs/reference/engine/classes/CylinderHandleAdornment.md) to render a sector of a hollow cylinder. ### Property: CylinderHandleAdornment.Radius ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic", "UI" ] } ``` The radius of the cylinder adornment in studs. ### Property: CylinderHandleAdornment.Shading ```json { "type": "AdornShading", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic", "UI" ] } ``` ## Inherited Members ### From [HandleAdornment](/docs/reference/engine/classes/HandleAdornment.md) - **Property `AdornCullingMode`** (`AdornCullingMode`): Determines whether to automatically cull the adornment. - **Property `AlwaysOnTop`** (`boolean`): Forces this adornment to render on top of all 3D objects in the workspace. - **Property `CFrame`** (`CFrame`): The position and rotation of the object relative to its - **Property `SizeRelativeOffset`** (`Vector3`): The positional offset of the adornment based on the adornee's - **Property `ZIndex`** (`int`): Determines the draw order of this HandleAdornment when - **Event `MouseButton1Down`**: Fires when a player presses down on their left mouse button while hovering - **Event `MouseButton1Up`**: Fires when a player releases their left mouse button while hovering over - **Event `MouseEnter`**: Fires when a player moves their mouse over the adornment. - **Event `MouseLeave`**: Fires when a player moves their mouse out of the adornment. ### From [PVAdornment](/docs/reference/engine/classes/PVAdornment.md) - **Property `Adornee`** (`PVInstance`): The PVInstance which this PVAdornment is attached to. ### From [GuiBase3d](/docs/reference/engine/classes/GuiBase3d.md) - **Property `Color`** (`BrickColor`): Sets the color of a GUI object. *(deprecated, hidden)* - **Property `Color3`** (`Color3`): Sets the color of this GuiBase3d object. - **Property `Transparency`** (`float`): Sets the transparency of this GuiBase3d object. - **Property `Visible`** (`boolean`): Determines whether this GuiBase3d object and its descendants will ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: CylinderMesh last_updated: 2026-06-29T19:34:07Z inherits: - BevelMesh - DataModelMesh - Instance - Object type: class memory_category: BaseParts tags: - Deprecated summary: "The CylinderMesh object applies a 'cylinder' mesh to the BasePart it is parented to." --- # Class: CylinderMesh > The CylinderMesh object applies a 'cylinder' mesh to the [BasePart](/docs/reference/engine/classes/BasePart.md) it > is parented to. ## Description The CylinderMesh object applies a 'cylinder' mesh to the [BasePart](/docs/reference/engine/classes/BasePart.md) it is parented to. ## What does a CylinderMesh do? A CylinderMesh gives the [BasePart](/docs/reference/engine/classes/BasePart.md) it was applied to a cylinder shaped mesh. The mesh applied gives the same appearance as that due to the [SpecialMesh.MeshType](/docs/reference/engine/classes/SpecialMesh.md) of a [SpecialMesh](/docs/reference/engine/classes/SpecialMesh.md) being set to 'Cylinder' or [Part.Shape](/docs/reference/engine/classes/Part.md) being set to 'Cylinder'. However, unlike those two cases, it is orientated so that the height of the cylinder is along the [BasePart](/docs/reference/engine/classes/BasePart.md) Y axis. The dimensions of the mesh scale relative to the [BasePart.Size](/docs/reference/engine/classes/BasePart.md) of the [BasePart](/docs/reference/engine/classes/BasePart.md). This scale is uniformly along the [BasePart](/docs/reference/engine/classes/BasePart.md) Y axis and maintaining a 1:1 ratio for the part's X and Z axis, using the lowest value. This means the [BasePart](/docs/reference/engine/classes/BasePart.md) can be resized normally, but the cross section of the cylinder will always remain a circle and cannot be stretched or compressed. Note as the CylinderMesh object does not include a texture the [DataModelMesh.VertexColor](/docs/reference/engine/classes/DataModelMesh.md) property does not do anything. ## Why use a CylinderMesh? The advantage of using a mesh over setting the [Part.Shape](/docs/reference/engine/classes/Part.md) property of a part to 'Cylinder' is that the [DataModelMesh.Scale](/docs/reference/engine/classes/DataModelMesh.md) and [DataModelMesh.Offset](/docs/reference/engine/classes/DataModelMesh.md) properties are exposed. These allow the position and dimensions of the mesh that is displayed to be changed without changing the [BasePart.Position](/docs/reference/engine/classes/BasePart.md) or [BasePart.Size](/docs/reference/engine/classes/BasePart.md) of the [Part](/docs/reference/engine/classes/Part.md) the mesh is parented to. The key difference between a CylinderMesh or a [SpecialMesh](/docs/reference/engine/classes/SpecialMesh.md) with [SpecialMesh.MeshType](/docs/reference/engine/classes/SpecialMesh.md) set to 'Cylinder' is the orientation of the cylinder mesh. With a CylinderMesh, the height of the cylinder is aligned with the height (Y axis) of the part. With a [SpecialMesh](/docs/reference/engine/classes/SpecialMesh.md) (or [Part](/docs/reference/engine/classes/Part.md) with [Part.Shape](/docs/reference/engine/classes/Part.md) set to 'Cylinder'), the height of the cylinder is aligned with the X axis. ## Code Samples **CylinderMesh Instantiation** This code sample includes a demonstration of how a `CylinderMesh` can be used, and how it scales so as to maintain a constant ratio of length to width. ```lua local part = Instance.new("Part") part.Position = Vector3.new(0, 2, 0) part.Size = Vector3.new(10, 2, 5) part.Anchored = true local mesh = Instance.new("CylinderMesh") mesh.Parent = part mesh.Scale = Vector3.new(1, 1, 1) mesh.Offset = Vector3.new(0, 0, 0) local adornment = Instance.new("SelectionBox") adornment.Adornee = part adornment.Parent = part part.Parent = workspace ``` ## Inherited Members ### From [DataModelMesh](/docs/reference/engine/classes/DataModelMesh.md) - **Property `Offset`** (`Vector3`): The Offset of a mesh determines the relative position from the - **Property `Scale`** (`Vector3`): The Scale of a mesh determines the size of the mesh relative to its - **Property `VertexColor`** (`Vector3`): Changes the hue of a mesh's texture, used with FileMesh.TextureId. ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: CylindricalConstraint last_updated: 2026-06-29T19:34:07Z inherits: - SlidingBallConstraint - Constraint - Instance - Object type: class memory_category: BaseParts summary: "Constrains two attachments on two parts to have a relative linear and rotational motion." --- # Class: CylindricalConstraint > Constrains two attachments on two parts to have a relative linear and > rotational motion. ## Description A **CylindricalConstraint** allows its attachments to slide along one axis and rotate about another axis. It can be thought of like a combination of a [PrismaticConstraint](/docs/reference/engine/classes/PrismaticConstraint.md) and a [HingeConstraint](/docs/reference/engine/classes/HingeConstraint.md). The sliding axis is determined by the **X** axis of the constraint's [Attachment0](/docs/reference/engine/classes/Constraint.md). The rotation axis is centered at the constraint's [Attachment1](/docs/reference/engine/classes/Constraint.md) and is angled off of the sliding constraint by the constraint's [InclinationAngle](/docs/reference/engine/classes/CylindricalConstraint.md). This constraint, along with a [SpringConstraint](/docs/reference/engine/classes/SpringConstraint.md), is ideal for building vehicle suspension. Note that if this constraint attaches one part (**A**) to another part (**B**) that is anchored or connected to an anchored part (**Z**), part **A** will not be locally simulated when interacting with a player. When configuring this constraint, it may be helpful to study [Roblox Units](/docs/en-us/physics/units.md) to understand how Roblox units compare to metric units. #### Angular Power If a cylindrical's [AngularActuatorType](/docs/reference/engine/classes/CylindricalConstraint.md) is set to [Motor](/docs/reference/engine/enums/ActuatorType.md), it attempts to rotate the attachments with the goal of reaching its [AngularVelocity](/docs/reference/engine/classes/CylindricalConstraint.md). You can further control this rotation through both [MotorMaxAngularAcceleration](/docs/reference/engine/classes/CylindricalConstraint.md) and [MotorMaxTorque](/docs/reference/engine/classes/CylindricalConstraint.md). If a cylindrical's [AngularActuatorType](/docs/reference/engine/classes/CylindricalConstraint.md) is set to [Servo](/docs/reference/engine/enums/ActuatorType.md), it attempts to rotate to an angle specified by [TargetAngle](/docs/reference/engine/classes/CylindricalConstraint.md). This rotation is controlled by [AngularSpeed](/docs/reference/engine/classes/CylindricalConstraint.md), [AngularResponsiveness](/docs/reference/engine/classes/CylindricalConstraint.md), and [ServoMaxTorque](/docs/reference/engine/classes/CylindricalConstraint.md). #### Linear Power If a cylindrical's [ActuatorType](/docs/reference/engine/classes/CylindricalConstraint.md) is set to [Motor](/docs/reference/engine/enums/ActuatorType.md), it attempts to translate the attachments with the goal of reaching [Velocity](/docs/reference/engine/classes/CylindricalConstraint.md). You can further control this translation through both [MotorMaxAcceleration](/docs/reference/engine/classes/CylindricalConstraint.md) and [MotorMaxForce](/docs/reference/engine/classes/CylindricalConstraint.md). If a cylindrical's [ActuatorType](/docs/reference/engine/classes/CylindricalConstraint.md) is set to [Servo](/docs/reference/engine/enums/ActuatorType.md), it attempts to translate the attachments to a set separation specified by [TargetPosition](/docs/reference/engine/classes/CylindricalConstraint.md). This translation is controlled by [Speed](/docs/reference/engine/classes/CylindricalConstraint.md), [LinearResponsiveness](/docs/reference/engine/classes/CylindricalConstraint.md), and [ServoMaxForce](/docs/reference/engine/classes/CylindricalConstraint.md). #### Limits You can set limits to restrict both the **sliding range** and **rotation** of a cylindrical constraint. Enabling the [LimitsEnabled](/docs/reference/engine/classes/CylindricalConstraint.md) property exposes the [LowerLimit](/docs/reference/engine/classes/CylindricalConstraint.md) and [UpperLimit](/docs/reference/engine/classes/CylindricalConstraint.md) values, as well as [Restitution](/docs/reference/engine/classes/CylindricalConstraint.md) which defines the elasticity of the attachments when they reach either limit. Enabling the [AngularLimitsEnabled](/docs/reference/engine/classes/CylindricalConstraint.md) property exposes the [LowerAngle](/docs/reference/engine/classes/CylindricalConstraint.md) and [UpperAngle](/docs/reference/engine/classes/CylindricalConstraint.md) limits, as well as [AngularRestitution](/docs/reference/engine/classes/CylindricalConstraint.md) which defines the elasticity of the attachments when they reach either limit. #### Inclination Angle [InclinationAngle](/docs/reference/engine/classes/CylindricalConstraint.md) defines the direction of the rotation axis as an angle from the **X** axis in the **XY** plane of [Attachment0](/docs/reference/engine/classes/Constraint.md), from -180 to 180. This lets you tilt the rotating element without changing the sliding axis. ## Properties ### Property: CylindricalConstraint.AngularActuatorType ```json { "type": "ActuatorType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Cylinder", "capabilities": [ "Physics" ], "simulationAccess": true } ``` If a cylindrical's [AngularActuatorType](/docs/reference/engine/classes/CylindricalConstraint.md) is set to [Motor](/docs/reference/engine/enums/ActuatorType.md), it attempts to rotate the attachments with the goal of reaching its [AngularVelocity](/docs/reference/engine/classes/CylindricalConstraint.md). You can further control this rotation through both [MotorMaxAngularAcceleration](/docs/reference/engine/classes/CylindricalConstraint.md) and [MotorMaxTorque](/docs/reference/engine/classes/CylindricalConstraint.md). If a cylindrical's [AngularActuatorType](/docs/reference/engine/classes/CylindricalConstraint.md) is set to [Servo](/docs/reference/engine/enums/ActuatorType.md), it attempts to rotate to an angle specified by [TargetAngle](/docs/reference/engine/classes/CylindricalConstraint.md). This rotation is controlled by [AngularSpeed](/docs/reference/engine/classes/CylindricalConstraint.md), [AngularResponsiveness](/docs/reference/engine/classes/CylindricalConstraint.md), and [ServoMaxTorque](/docs/reference/engine/classes/CylindricalConstraint.md). ### Property: CylindricalConstraint.AngularLimitsEnabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Cylinder", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Enables the angular limits around the rotation axis. ### Property: CylindricalConstraint.AngularResponsiveness ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "AngularServo", "capabilities": [ "Physics" ], "simulationAccess": true } ``` This property specifies the sharpness of the angular servo motor in reaching the [TargetAngle](/docs/reference/engine/classes/CylindricalConstraint.md), when [AngularActuatorType](/docs/reference/engine/classes/CylindricalConstraint.md) is set to [Servo](/docs/reference/engine/enums/ActuatorType.md). Larger values correspond to a faster response and smaller values results in more damping and a slower response. ### Property: CylindricalConstraint.AngularRestitution ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "AngularLimits", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Restitution of the two limits, or how elastic they are. Constrained between 0 and 1. ### Property: CylindricalConstraint.AngularSpeed ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "AngularServo", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Target angular speed. This value is unsigned as the servo will always move toward its target. In radians per second. ### Property: CylindricalConstraint.AngularVelocity ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "AngularMotor", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The target angular velocity of the motor in radians per second around the rotation axis. ### Property: CylindricalConstraint.CurrentAngle ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Derived", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Signed angle (in degrees) between the reference axis and the secondary axis of [Attachment1](/docs/reference/engine/classes/Constraint.md) around the rotation axis. Valid range between -180 and 180. ### Property: CylindricalConstraint.InclinationAngle ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Cylinder", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Direction of the rotation axis as an angle from the **X** axis in the **XY** plane of [Attachment0](/docs/reference/engine/classes/Constraint.md). Valid range between -180 and 180. ### Property: CylindricalConstraint.LowerAngle ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "AngularLimits", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Lower limit for the angle (in degrees) between the reference axis and the SecondaryAxis of [Attachment1](/docs/reference/engine/classes/Constraint.md) around the rotation axis. Valid range between -180 and 180. ### Property: CylindricalConstraint.MotorMaxAngularAcceleration ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "AngularMotor", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The maximum angular acceleration of the motor in radians per second squared. ### Property: CylindricalConstraint.MotorMaxTorque ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "AngularMotor", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The maximum torque the motor can apply to achieve the target angular velocity. Units are mass × studs²/second². ### Property: CylindricalConstraint.RotationAxisVisible ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Physics" ] } ``` Enable the visibility of the rotation axis. ### Property: CylindricalConstraint.ServoMaxTorque ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "AngularServo", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Maximum torque the servo motor can apply. Units are mass × studs²/second². ### Property: CylindricalConstraint.TargetAngle ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "AngularServo", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Target angle (in degrees) between the reference axis and the secondary axis of [Attachment1](/docs/reference/engine/classes/Constraint.md) around the rotation axis. Valid range between -180 and 180. ### Property: CylindricalConstraint.UpperAngle ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "AngularLimits", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Upper limit for the angle (in degrees) between the reference axis and the secondary axis of [Attachment1](/docs/reference/engine/classes/Constraint.md) around the rotation axis. Valid range between -180 and 180. ### Property: CylindricalConstraint.WorldRotationAxis ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Derived", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The unit vector direction of the rotation axis in world coordinates. ### Property: CylindricalConstraint.SoftlockAngularServoUponReachingTarget ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "AngularServo", "capabilities": [ "Physics" ], "simulationAccess": true } ``` > **Deprecated:** This property should not be used in new work. ## Inherited Members ### From [SlidingBallConstraint](/docs/reference/engine/classes/SlidingBallConstraint.md) - **Property `ActuatorType`** (`ActuatorType`): Sets whether the translation of the SlidingBallConstraint is - **Property `CurrentPosition`** (`float`): The current offset between the constraint's - **Property `LimitsEnabled`** (`boolean`): Sets whether the SlidingBallConstraint will limit the range of - **Property `LinearResponsiveness`** (`float`): Specifies the "sharpness" of the linear servo motor in reaching the - **Property `LowerLimit`** (`float`): The lower positional limit along the **X** axis of - **Property `MotorMaxAcceleration`** (`float`): The constraint's maximum acceleration when - **Property `MotorMaxForce`** (`float`): The constraint's maximum force when - **Property `Restitution`** (`float`): The elasticity of the constraint's Attachments when - **Property `ServoMaxForce`** (`float`): The constraint's maximum force when - **Property `Size`** (`float`): The constraint's visualized size. - **Property `SoftlockServoUponReachingTarget`** (`boolean`): *(deprecated)* - **Property `Speed`** (`float`): The constraint's desired speed when - **Property `TargetPosition`** (`float`): The constraint's attempted target position when - **Property `UpperLimit`** (`float`): The upper positional limit along the **X** axis of - **Property `Velocity`** (`float`): The constraint's attempted velocity when ### From [Constraint](/docs/reference/engine/classes/Constraint.md) - **Property `Active`** (`boolean`): Indicates if the constraint is currently active in the world. - **Property `Attachment0`** (`Attachment`): The Attachment that is connected to - **Property `Attachment1`** (`Attachment`): The Attachment that is connected to - **Property `Color`** (`BrickColor`): The color of the constraint. - **Property `Enabled`** (`boolean`): Toggles whether or not the constraint is enabled. - **Property `Visible`** (`boolean`): Toggles the constraint's visibility. - **Method `GetDebugAppliedForce(bodyId: int): Vector3`**: *(deprecated)* - **Method `GetDebugAppliedTorque(bodyId: int): Vector3`**: *(deprecated)* ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: DataModel last_updated: 2026-06-29T19:34:07Z inherits: - ServiceProvider - Instance - Object type: class memory_category: Instances tags: - NotCreatable summary: "The root of Roblox's parent-child hierarchy. Its direct children are services, such as Workspace and Lighting, that act as the fundamental components of a Roblox game." --- # Class: DataModel > The root of Roblox's parent-child hierarchy. Its direct children are services, > such as [Workspace](/docs/reference/engine/classes/Workspace.md) and [Lighting](/docs/reference/engine/classes/Lighting.md), that act as the fundamental > components of a Roblox game. ## Description The [Data Model](/docs/en-us/projects/data-model.md) (commonly known as `game` after the global variable used to access it) is the root of Roblox's parent-child hierarchy. Its direct children are services, such as [Workspace](/docs/reference/engine/classes/Workspace.md) and [Lighting](/docs/reference/engine/classes/Lighting.md), that act as the fundamental components of a Roblox game. ## Code Samples **GetService()** Demonstrates using `game`, the root instance of [DataModel](/docs/reference/engine/classes/DataModel.md), to get services such as [Workspace](/docs/reference/engine/classes/Workspace.md) and [Lighting](/docs/reference/engine/classes/Lighting.md). ```lua local Workspace = game:GetService("Workspace") local Lighting = game:GetService("Lighting") -- Examples of modifying properties of these services Workspace.Gravity = 20 Lighting.ClockTime = 4 ``` ## Properties ### Property: DataModel.CreatorId ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "State" } ``` This property describes the ID of the user or group that owns the place. If the [DataModel.CreatorType](/docs/reference/engine/classes/DataModel.md) property is `User` then `CreatorId` will be the [Player.UserId](/docs/reference/engine/classes/Player.md) of the place's owner. If the [DataModel.CreatorType](/docs/reference/engine/classes/DataModel.md) is `Group` then `CreatorId` will be the ID of the group that owns the place. **Detect when the place owner joins the game** This code sample will print an output when the user that owns the game, or a member of the group that owns the game joins the server. To run this script, place it inside a `Script` in `ServerScriptService` ```lua local Players = game:GetService("Players") Players.PlayerAdded:Connect(function(player) if game.CreatorType == Enum.CreatorType.User then if player.UserId == game.CreatorId then print("The place owner has joined the game!") end elseif game.CreatorType == Enum.CreatorType.Group then if player:IsInGroupAsync(game.CreatorId) then print("A member of the group that owns the place has joined the game!") end end end) ``` ### Property: DataModel.CreatorType ```json { "type": "CreatorType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "State" } ``` This property describes the [CreatorType](/docs/reference/engine/enums/CreatorType.md) of the place, whether the place is owned by a user or a group. If `User`, then the [DataModel.CreatorId](/docs/reference/engine/classes/DataModel.md) property will describe the [UserId](/docs/reference/engine/classes/Player.md) of the account that owns the game. If `Group`, then it will describe the group ID. **Detect when the place owner joins the game** This code sample will print an output when the user that owns the game, or a member of the group that owns the game joins the server. To run this script, place it inside a `Script` in `ServerScriptService` ```lua local Players = game:GetService("Players") Players.PlayerAdded:Connect(function(player) if game.CreatorType == Enum.CreatorType.User then if player.UserId == game.CreatorId then print("The place owner has joined the game!") end elseif game.CreatorType == Enum.CreatorType.Group then if player:IsInGroupAsync(game.CreatorId) then print("A member of the group that owns the place has joined the game!") end end end) ``` ### Property: DataModel.Environment ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "State" } ``` ### Property: DataModel.GameId ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "State" } ``` This property describes the ID of the experience that the place running on the server belongs to. #### See Also - [DataModel.PlaceId](/docs/reference/engine/classes/DataModel.md), which describes the ID of the place running on the server - [DataModel.JobId](/docs/reference/engine/classes/DataModel.md), which is a unique identifier for the server game instance running - [TeleportService](/docs/reference/engine/classes/TeleportService.md), which is a service that can be used to transport [Players](/docs/reference/engine/classes/Player.md) between games ### Property: DataModel.Genre ```json { "type": "Genre", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "State" } ``` This property is broken and should not be used. This property historically described the [Genre](/docs/reference/engine/enums/Genre.md) of the place as set on the Roblox website. This property, along with [DataModel.GearGenreSetting](/docs/reference/engine/classes/DataModel.md), no longer functions correctly due to genres existing on the Roblox website that are not reflected in the [Genre](/docs/reference/engine/enums/Genre.md) enum. As a result, attempting to read this property may throw an error. ### Property: DataModel.JobId ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "JobInfo" } ``` This property is a unique identifier for the running game server instance. It is a universally unique identifier (UUID), meaning that no two servers, past or present, will ever have the same ID. Defaults to an empty string in Studio. #### See Also - [TeleportService:GetPlayerPlaceInstanceAsync()](/docs/reference/engine/classes/TeleportService.md) which can be used to retrieve the [DataModel.JobId](/docs/reference/engine/classes/DataModel.md) of a user's current server. - [TeleportService:TeleportToPlaceInstance()](/docs/reference/engine/classes/TeleportService.md) which can be used to teleport a [Player](/docs/reference/engine/classes/Player.md) to a specific server. - [DataModel.PrivateServerId](/docs/reference/engine/classes/DataModel.md) describes the ID of the private server the game server instance belongs to. - [HttpService:GenerateGUID()](/docs/reference/engine/classes/HttpService.md), a function that can be used to generate your own UUIDs. ### Property: DataModel.MatchmakingType ```json { "type": "MatchmakingType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` This property represents how players in the server are handled by matchmaking. Players with different [MatchmakingTypes](/docs/reference/engine/classes/DataModel.md) cannot be in or teleport to the same server. Note that this property is only valid on the server [DataModel](/docs/reference/engine/classes/DataModel.md) and it will be the [MatchmakingType.Default](/docs/reference/engine/enums/MatchmakingType.md) value for all clients, so only reference this property inside of a server‑side [Script](/docs/reference/engine/classes/Script.md). ### Property: DataModel.PlaceId ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "State" } ``` This property describes the ID of the place running on the server. If the place has not been published to Roblox, this ID will correspond with the template being used. #### See Also - [DataModel.GameId](/docs/reference/engine/classes/DataModel.md), which describes the ID of the experience that the current place belongs to - [DataModel.JobId](/docs/reference/engine/classes/DataModel.md), which is a unique identifier for the server game instance running - [TeleportService](/docs/reference/engine/classes/TeleportService.md), which is a service that can be used to transport [Players](/docs/reference/engine/classes/Player.md) between places ### Property: DataModel.PlaceVersion ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "State" } ``` This property describes the version of the place the server is running on. This version number corresponds with the version number shown under the **Version History** section of the place's settings. It is not the current version of the Roblox client. This property is 0 for all unpublished experiences. When a server instance is created for a place, it uses the place's current version. If the place is later updated while this server is running, the server will remain at its current version. This property can be used to display a [ScreenGui](/docs/reference/engine/classes/ScreenGui.md) showing the current version of the game to [Players](/docs/reference/engine/classes/Player.md) to assist with debugging. **Server version number GUI** This code sample will place a simple GUI in the `StarterGui` showing the place version the server is running at. To use this sample, place it inside a `Script` in `ServerScriptService`. ```lua local StarterGui = game:GetService("StarterGui") local versionGui = Instance.new("ScreenGui") local textLabel = Instance.new("TextLabel") textLabel.Position = UDim2.new(1, -10, 1, 0) textLabel.AnchorPoint = Vector2.new(1, 1) textLabel.Size = UDim2.new(0, 150, 0, 40) textLabel.BackgroundTransparency = 1 textLabel.TextColor3 = Color3.new(1, 1, 1) textLabel.TextStrokeTransparency = 0 textLabel.TextXAlignment = Enum.TextXAlignment.Right textLabel.TextScaled = true local placeVersion = game.PlaceVersion textLabel.Text = string.format("Server version: %s", placeVersion) textLabel.Parent = versionGui versionGui.Parent = StarterGui ``` ### Property: DataModel.PrivateServerId ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` This property describes the private server ID of the server, if the server is a private server. If the server is not a private server, then this property will be an empty string. #### Private servers Private servers refer to the following: - [Private servers](/docs/en-us/production/monetization/private-servers.md) that users can purchase from the games page - Reserved servers, private servers created by the developer using [TeleportService:ReserveServerAsync()](/docs/reference/engine/classes/TeleportService.md) #### PrivateServerId vs JobId The PrivateServerId of a server is different from the [DataModel.JobId](/docs/reference/engine/classes/DataModel.md). The [JobId](/docs/reference/engine/classes/DataModel.md) is the unique identifier of the current server instance. Private servers (private or reserved servers) can have multiple server instances associated with them over time. This is because, although only one server instance can be running at once for a private server, new server instances can open and close as players join and leave the game. For example, no server instance is running when nobody is playing in the server. The PrivateServerId will be consistent across all of these server instances, and the [DataModel.JobId](/docs/reference/engine/classes/DataModel.md) will be unique for each one. See also: - [DataModel.PrivateServerOwnerId](/docs/reference/engine/classes/DataModel.md), a property describing the owner of a private server - [TeleportService:ReserveServerAsync()](/docs/reference/engine/classes/TeleportService.md), a function which creates a reserved server **Detecting Private Servers** [DataModel.PrivateServerId](/docs/reference/engine/classes/DataModel.md) and [DataModel.PrivateServerOwnerId](/docs/reference/engine/classes/DataModel.md) can be used to detect if the current server instance is a standard server, a VIP server or a reserved server. ```lua local function getServerType() if game.PrivateServerId ~= "" then if game.PrivateServerOwnerId ~= 0 then return "VIPServer" else return "ReservedServer" end else return "StandardServer" end end print(getServerType()) ``` ### Property: DataModel.PrivateServerOwnerId ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` This property describes the [UserId](/docs/reference/engine/classes/Player.md) of the [Player](/docs/reference/engine/classes/Player.md) that owns the [private server](/docs/en-us/production/monetization/private-servers.md) if the server is private. If the server is a standard or reserved server then this property will be set to `0`. This property could be used to identify if a [Player](/docs/reference/engine/classes/Player.md) is the owner of the private server, for example: ```lua local Players = game:GetService("Players") -- is this a private server? if game.PrivateServerId ~= "" and game.PrivateServerOwnerId ~= 0 then -- listen for new players being added Players.PlayerAdded:Connect(function(player) -- check if the player is the server owner if player.UserId == game.PrivateServerOwnerId then print("The private server owner has joined the game") end end) end ``` See also: - [DataModel.PrivateServerId](/docs/reference/engine/classes/DataModel.md), a property describing the unique ID of private and [reserved servers](/docs/reference/engine/classes/TeleportService.md) **Detecting Private Servers** [DataModel.PrivateServerId](/docs/reference/engine/classes/DataModel.md) and [DataModel.PrivateServerOwnerId](/docs/reference/engine/classes/DataModel.md) can be used to detect if the current server instance is a standard server, a VIP server or a reserved server. ```lua local function getServerType() if game.PrivateServerId ~= "" then if game.PrivateServerOwnerId ~= 0 then return "VIPServer" else return "ReservedServer" end else return "StandardServer" end end print(getServerType()) ``` ### Property: DataModel.RunService ```json { "type": "RunService", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: DataModel.Workspace ```json { "type": "Workspace", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` This property is a reference to the [Workspace](/docs/reference/engine/classes/Workspace.md) service. It always points to [Workspace](/docs/reference/engine/classes/Workspace.md) and will never be `nil`. ### Property: DataModel.GearGenreSetting ```json { "type": "GearGenreSetting", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "State" } ``` > **Deprecated:** This property is deprecated and is no longer functional. It should not be used. This property, along with [DataModel.Genre](/docs/reference/engine/classes/DataModel.md), no longer functions correctly and attempting to read it may throw an error. ### Property: DataModel.lighting ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data" } ``` > **Deprecated:** This item has been superseded by [game:GetService("Lighting")](/docs/reference/engine/classes/ServiceProvider.md), which should be used instead. This property was once used to get the game's [Lighting](/docs/reference/engine/classes/Lighting.md) service. ### Property: DataModel.workspace ```json { "type": "Workspace", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data" } ``` > **Deprecated:** This deprecated property is a variant of [DataModel.Workspace](/docs/reference/engine/classes/DataModel.md) which should be used instead. ### Property: DataModel.VIPServerId *(hidden)* ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data" } ``` > **Deprecated:** This property has been deprecated. Use [DataModel.PrivateServerId](/docs/reference/engine/classes/DataModel.md) instead. This property was string that could identify the current server as a private server. ### Property: DataModel.VIPServerOwnerId *(hidden)* ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data" } ``` > **Deprecated:** This property has been deprecated. Use [DataModel.PrivateServerOwnerId](/docs/reference/engine/classes/DataModel.md) instead. This property indicates the [UserId](/docs/reference/engine/classes/Player.md) of the account who owns the private server. ## Methods ### Method: DataModel:BindToClose **Signature:** `DataModel:BindToClose(function: Function): ()` Binds a function to be called before the server shuts down. If the bound function accepts a parameter, it passes [CloseReason](/docs/reference/engine/enums/CloseReason.md) specifying the reason for the server shutdown. You can bind multiple functions by calling [BindToClose()](/docs/reference/engine/classes/DataModel.md) repeatedly. Bound functions are called in parallel and run at the same time. The experience server waits 30 seconds for all bound functions to stop running before it shuts down. After 30 seconds, the server shuts down even if functions are still running. To verify that the current session is not in Roblox Studio, use [RunService:IsStudio()](/docs/reference/engine/classes/RunService.md). This prevents bound functions from completing their run in offline testing sessions. When you use [DataStoreService](/docs/reference/engine/classes/DataStoreService.md), you should also use `BindToClose` to bind a function saving all unsaved data to [DataStores](/docs/reference/engine/classes/GlobalDataStore.md). This prevents data loss if the server shuts down unexpectedly. See also: - [CloseReason](/docs/reference/engine/enums/CloseReason.md) for reasons for the experience server shutdown. - [PluginGui:BindToClose()](/docs/reference/engine/classes/PluginGui.md), which binds a function to a [PluginGui](/docs/reference/engine/classes/PluginGui.md) close button. *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `function` | `Function` | | A function called before the experience server shuts down. If the bound function accepts a parameter, it passes [CloseReason](/docs/reference/engine/enums/CloseReason.md) specifying the reason for the server shutdown. | **Returns:** `()` **Saving player data before shutting down** The following code sample is an example of how [DataModel:BindToClose()](/docs/reference/engine/classes/DataModel.md) can be used to save player data in the event of a server shutdown. In this example, player data is stored in a dictionary named _playerData_ with [UserIds](/docs/reference/engine/classes/Player.md) as keys. ```lua local DataStoreService = game:GetService("DataStoreService") local RunService = game:GetService("RunService") local playerDataStore = DataStoreService:GetDataStore("PlayerData") local allPlayerSessionDataCache = {} local function savePlayerDataAsync(userId, data) return playerDataStore:UpdateAsync(userId, function(oldData) return data end) end local function onServerShutdown(closeReason) if RunService:IsStudio() then -- Avoid writing studio data to production and stalling test session closing return end -- Reference for yielding and resuming later local mainThread = coroutine.running() -- Counts up for each new thread, down when the thread finishes. When 0 is reached, -- the individual thread knows it's the last thread to finish and should resume the main thread local numThreadsRunning = 0 -- Calling this function later starts on a new thread because of coroutine.wrap local startSaveThread = coroutine.wrap(function(userId, sessionData) -- Perform the save operation local success, result = pcall(savePlayerDataAsync, userId, sessionData) if not success then -- Could implement a retry warn(string.format("Failed to save %d's data: %s", userId, result)) end -- Thread finished, decrement counter numThreadsRunning -= 1 if numThreadsRunning == 0 then -- This was the last thread to finish, resume main thread coroutine.resume(mainThread) end end) -- This assumes playerData gets cleared from the data table during a final save on PlayerRemoving, -- so this is iterating over all the data of players still in the game that hasn't been saved for userId, sessionData in pairs(allPlayerSessionDataCache) do numThreadsRunning += 1 -- This loop finishes running and counting numThreadsRunning before any of -- the save threads start because coroutine.wrap has built-in deferral on start startSaveThread(userId, sessionData) end if numThreadsRunning > 0 then -- Stall shutdown until save threads finish. Resumed by the last save thread when it finishes coroutine.yield() end end game:BindToClose(onServerShutdown) ``` **Binding to and Handling Game Shutdown** The following example prints the close reason to the output. It prints `Done` after three seconds, and then allows Roblox to shut down the experience server. The close reason matches one of the values in [CloseReason](/docs/reference/engine/enums/CloseReason.md). ```lua game:BindToClose(function(closeReason) print(`Closing with reason {closeReason}`) task.wait(3) print("Done") end) ``` ### Method: DataModel:GetJobsInfo **Signature:** `DataModel:GetJobsInfo(): Array` Returns a table containing basic information about the jobs performed by the task scheduler. In computing, a task scheduler is a system responsible for executing key tasks at the appropriate intervals. You can also find live task scheduler statistics in the Task Scheduler window in Roblox Studio. The first entry in the table returned is a reference dictionary containing the statistics (or headings) available. It is in the following format: ```lua { ["name"] = "name", ["averageDutyCycle"] = "averageDutyCycle", ["averageStepsPerSecond"] = "averageStepsPerSecond", ["averageStepTime"] = "averageStepTime", ["averageError"] = "averageError", ["isRunning"] = "isRunning", } ``` The subsequent entries in the table returned are dictionaries containing the above statistics for jobs performed by the task scheduler. For example: ```lua { ["name"] = "Heartbeat", ["averageDutyCycle"] = 0, ["averageStepsPerSecond"] = 0, ["averageStepTime"] = 0, ["averageError"] = 0, ["isRunning"] = false, } ``` See also: - [TaskScheduler](/docs/reference/engine/classes/TaskScheduler.md) *Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `Array` — A table containing information about the jobs performed by the task scheduler, see above for the format. **Getting Jobs Info** Here is an example of iterating over the job info. ```lua local jobInfo = game:GetJobsInfo() local jobTitles = jobInfo[1] table.remove(jobInfo, 1) local divider = string.rep("-", 120) print(divider) warn("JOB INFO:") print(divider) for _, job in pairs(jobInfo) do for jobIndex, jobValue in pairs(job) do local jobTitle = jobTitles[jobIndex] warn(jobTitle, "=", jobValue) end print(divider) end ``` ### Method: DataModel:GetObjects **Signature:** `DataModel:GetObjects(url: ContentId): Instances` This method returns an array of [Instances](/docs/reference/engine/classes/Instance.md) associated with the given content URL. It can be used to insert content from the Roblox library. It's not possible to insert [Sounds](/docs/reference/engine/classes/Sound.md) using this method as they do not have an [Instance](/docs/reference/engine/classes/Instance.md) associated with them and have only a content URL. Unlike [InsertService:LoadAsset()](/docs/reference/engine/classes/InsertService.md), `DataModel:GetObjects()` does not require an asset to be "trusted," meaning that an asset doesn't need to be owned by the logged in user, or created by Roblox, to be inserted. However, if the asset is not owned by the logged in user it must be freely available. Due to this function's security context it can only be used by plugins or the command bar. For an alternative that can be used in [Scripts](/docs/reference/engine/classes/Script.md) and [LocalScripts](/docs/reference/engine/classes/LocalScript.md), see [InsertService:LoadAsset()](/docs/reference/engine/classes/InsertService.md). *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `url` | `ContentId` | | The given content URL. | **Returns:** `Instances` — An array of [Instances](/docs/reference/engine/classes/Instance.md) associated with the content URL. **View a plugin's source code** If you want to view a plugin's source code without installing it, you can use [DataModel:GetObjects()](/docs/reference/engine/classes/DataModel.md) to download the plugin. The code sample below includes a function that will take a plugin's website URL and insert the plugin into the currently selected `Instance` or the `Workspace`. Due to [GetObjects'](/docs/reference/engine/classes/DataModel.md) security context, this function can only be used in the command line or in a plugin. ```lua local Selection = game:GetService("Selection") local WEB_URL = "plugin URL here" local function downloadPlugin(webURL) -- get the content URL local contentID = string.match(webURL, "%d+") local contentURL = "rbxassetid://" .. contentID -- download the objects local objects = game:GetObjects(contentURL) -- decide where to parent them local selection = Selection:Get() local parent = #selection == 1 and selection[1] or workspace -- parent the objects for _, object in pairs(objects) do object.Parent = parent end end downloadPlugin(WEB_URL) ``` **Batch convert decal IDs** The content ID of a `Decal` on the Roblox website is associated with a `Decal` `Instance` rather than the actual content ID of the texture. The code below, will use [DataModel:GetObjects()](/docs/reference/engine/classes/DataModel.md) to insert `Decal` objects into place and read their [Decal.ColorMapContent](/docs/reference/engine/classes/Decal.md) property to obtain the image content IDs. To use this code sample, enter the web URLs of the decals you'd like to convert into the array named _IMAGES_ (as strings). A dictionary with the converted textures will be outputted. ```lua local IMAGES = { -- Insert Decal web URLs in an array here (as strings) } -- open the dictionary local outputString = "textures = {" -- utility function to add a new entry to the dictionary (as a string) local function addEntryToDictionary(original, new) outputString = outputString .. "\n" -- new line .. " " -- indent .. '["' .. original .. '"]' -- key .. ' = "' .. new .. '",' -- value end print("Starting conversion") for _, webURL in pairs(IMAGES) do -- get the content URL local contentID = string.match(webURL, "%d+") local contentURL = "rbxassetid://" .. contentID local success, result = pcall(function() local objects = game:GetObjects(contentURL) return objects[1].Texture end) if success then addEntryToDictionary(webURL, result) else addEntryToDictionary(webURL, "Error downloading decal") end task.wait() end print("Conversion complete") -- close the dictionary outputString = outputString .. "\n}" -- print the dictionary print(outputString) ``` **Expected output:** Starting conversion Conversion complete textures = { -- a dictionary with the original web URLs as keys, and the texture content IDs as values } ### Method: DataModel:IsLoaded **Signature:** `DataModel:IsLoaded(): boolean` When all initial [Instances](/docs/reference/engine/classes/Instance.md) in the game have finished replicating to the client, this function returns true. Unless they are parented to [ReplicatedFirst](/docs/reference/engine/classes/ReplicatedFirst.md), [LocalScripts](/docs/reference/engine/classes/LocalScript.md) do not run until the game has loaded. The following snippet, run from a [LocalScript](/docs/reference/engine/classes/LocalScript.md) in [ReplicatedFirst](/docs/reference/engine/classes/ReplicatedFirst.md) yields until the game has loaded: ```lua if not game:IsLoaded() then game.Loaded:Wait() end ``` See also: - [Replication order](/docs/en-us/scripting/attributes.md#replication-order) for a more detailed summary of the loading process. - [DataModel.Loaded](/docs/reference/engine/classes/DataModel.md), an event that fires when the game has loaded - [Instance:WaitForChild()](/docs/reference/engine/classes/Instance.md), a function which can be used to wait for an individual [Instance](/docs/reference/engine/classes/Instance.md) to replicate without having to wait for the whole game to finish loading. *Security: None · Thread Safety: Unsafe* **Returns:** `boolean` — Whether the client has finished loading the game for the first time. **Custom Loading Screen** This sample demonstrates a custom loading screen with a basic `TextLabel`. The code should be placed in a `LocalScript` within `ReplicatedFirst`. To expand on this sample with loading screen animations, see the [`Custom Loading Screens`](/docs/en-us/ui/customizing-loading-screens.md) article. ```lua local Players = game:GetService("Players") local ReplicatedFirst = game:GetService("ReplicatedFirst") local player = Players.LocalPlayer local playerGui = player:WaitForChild("PlayerGui") -- Create a basic loading screen local screenGui = Instance.new("ScreenGui") screenGui.IgnoreGuiInset = true local textLabel = Instance.new("TextLabel") textLabel.Size = UDim2.new(1, 0, 1, 0) textLabel.BackgroundColor3 = Color3.fromRGB(0, 20, 40) textLabel.Font = Enum.Font.GothamMedium textLabel.TextColor3 = Color3.new(0.8, 0.8, 0.8) textLabel.Text = "Loading" textLabel.TextSize = 28 textLabel.Parent = screenGui -- Parent entire screen GUI to player GUI screenGui.Parent = playerGui -- Remove the default loading screen ReplicatedFirst:RemoveDefaultLoadingScreen() --task.wait(3) -- Optionally force screen to appear for a minimum number of seconds if not game:IsLoaded() then game.Loaded:Wait() end screenGui:Destroy() ``` ### Method: DataModel:SetPlaceId **Signature:** `DataModel:SetPlaceId(placeId: int64): ()` This function sets the [DataModel.PlaceId](/docs/reference/engine/classes/DataModel.md) of the game instance to the given `placeId`. *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `placeId` | `int64` | | The ID to set the [DataModel.PlaceId](/docs/reference/engine/classes/DataModel.md) to. | **Returns:** `()` ### Method: DataModel:SetUniverseId **Signature:** `DataModel:SetUniverseId(universeId: int64): ()` This function sets the [DataModel.GameId](/docs/reference/engine/classes/DataModel.md) of the current game instance to the given `universeId`. This is useful when testing local .rbxl files that have not been published to Roblox. To access the [DataStoreService](/docs/reference/engine/classes/DataStoreService.md) in an unpublished place, both [DataModel:SetUniverseId()](/docs/reference/engine/classes/DataModel.md) and [DataModel:SetPlaceId()](/docs/reference/engine/classes/DataModel.md) must be set. *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `universeId` | `int64` | | The ID to set the [DataModel.GameId](/docs/reference/engine/classes/DataModel.md) to. | **Returns:** `()` ### Method: DataModel:GetMessage **Signature:** `DataModel:GetMessage(): string` > **Deprecated:** This item is deprecated since the system was phased out a very long time ago, and recently the APIs for setting this message were removed. This function will always return a blank string. It was originally used to set the message displayed on screen while the game was loading. This system was phased out a very long time ago, and recently the APIs for setting this message were removed. *Security: None · Thread Safety: Unsafe* **Returns:** `string` ### Method: DataModel:GetRemoteBuildMode **Signature:** `DataModel:GetRemoteBuildMode(): boolean` > **Deprecated:** This item is deprecated. Use [RunService:IsServer()](/docs/reference/engine/classes/RunService.md) to see if your code is running on the server. This method is no longer useful and will always return false. Use [RunService:IsServer()](/docs/reference/engine/classes/RunService.md) to see if your code is running on the server. *Security: None · Thread Safety: Unsafe* **Returns:** `boolean` ### Method: DataModel:IsGearTypeAllowed **Signature:** `DataModel:IsGearTypeAllowed(gearType: GearType): boolean` > **Deprecated:** This property is deprecated and is no longer functional. It should not be used. Currently this function only returns the correct value on the client This function returns whether gear of the given [GearType](/docs/reference/engine/enums/GearType.md) is permitted to be added to [Players'](/docs/reference/engine/classes/Player.md) [StarterGears](/docs/reference/engine/classes/StarterGear.md). For example: ```lua local meleeWeaponsAllowed = game:IsGearTypeAllowed(Enum.GearType.MeleeWeapons) ``` Whether gear of a specific [GearType](/docs/reference/engine/enums/GearType.md) is permitted in the game is determined in a place's settings page under 'Permissions'. Note, all of a gear's associated [GearTypes](/docs/reference/engine/enums/GearType.md) must be enabled for it to be permitted in a place. *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `gearType` | `GearType` | | The given [GearType](/docs/reference/engine/enums/GearType.md). | **Returns:** `boolean` — Whether gear of the given [GearType](/docs/reference/engine/enums/GearType.md) is permitted in the game. ### Method: DataModel:SavePlace **Signature:** `DataModel:SavePlace(saveFilter?: SaveFilter): boolean` > **Deprecated:** This item is deprecated. Do not use it for new work. This function was used by an ancient data persistence method to save the current place. Note: - In order for this method to work the save place API has to be enabled for the current place. *Yields · Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `saveFilter` | `SaveFilter` | `SaveAll` | | **Returns:** `boolean` ## Events ### Event: DataModel.GraphicsQualityChangeRequest **Signature:** `DataModel.GraphicsQualityChangeRequest(betterQuality: boolean)` Fires when the user prompts an increase or decrease in graphics quality using the hotkeys. This event fires under the following conditions: - If the user presses F10, this event fires with a `betterQuality` argument of `true`. - If the user presses ShiftF10, this event fires with a `betterQuality` argument of `false`. This event does not provide the current graphics quality level or cover all updates to the graphics quality. For example, changes made in the core GUI escape menu are not registered. You can retrieve a user's [SavedQualitySetting](/docs/reference/engine/enums/SavedQualitySetting.md) using [UserGameSettings](/docs/reference/engine/classes/UserGameSettings.md) with the following snippet: ```lua UserSettings():GetService("UserGameSettings").SavedQualityLevel ``` If the user's graphics settings are set to automatic then the [SavedQualitySetting](/docs/reference/engine/enums/SavedQualitySetting.md) will be [Automatic](/docs/reference/engine/enums/SavedQualitySetting.md). There is currently no way for developers to reliably get the current graphics quality level of a user's machine. *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `betterQuality` | `boolean` | Whether the user has prompted an increase (_true_) or a decrease (_false_) in graphics quality. | **Handling User Changes in Graphics Quality** ```lua game.GraphicsQualityChangeRequest:Connect(function(betterQuality) if betterQuality then print("The user has requested an increase in graphics quality!") else print("The user has requested a decrease in graphics quality!") end end) ``` ### Event: DataModel.Loaded **Signature:** `DataModel.Loaded()` This event fires on the client when all initial [Instances](/docs/reference/engine/classes/Instance.md) in the game have finished replicating to the client. Unless they are parented to [ReplicatedFirst](/docs/reference/engine/classes/ReplicatedFirst.md), [LocalScripts](/docs/reference/engine/classes/LocalScript.md) do not run until the game has loaded. The following snippet, run from a [LocalScript](/docs/reference/engine/classes/LocalScript.md) in [ReplicatedFirst](/docs/reference/engine/classes/ReplicatedFirst.md) yields until the game has loaded: ```lua if not game:IsLoaded() then game.Loaded:Wait() end ``` See also: - [Replication order](/docs/en-us/scripting/attributes.md#replication-order) for a more detailed summary of the loading process. - [DataModel.Loaded](/docs/reference/engine/classes/DataModel.md), an event that fires when the game has loaded - [Instance:WaitForChild()](/docs/reference/engine/classes/Instance.md), a function which can be used to wait for an individual [Instance](/docs/reference/engine/classes/Instance.md) to replicate without having to wait for the whole game to finish loading. *Security: None* ### Event: DataModel.ServerRestartScheduled **Signature:** `DataModel.ServerRestartScheduled(restartTime: DateTime, source: CloseReason, attributes: Dictionary)` Fires when the server has been scheduled to restart, for example when you restart servers from the Creator Hub or [Open Cloud](https://create.roblox.com/docs/cloud/reference/features/universes#Restarts_LaunchRestart) to [release an experience update](/docs/en-us/projects/update-games.md). You can use this event to inform players on the server of the impending restart. You could then let them save their progress or teleport them to updated servers at the most convenient time for your game. *Security: None · Capabilities: ServerCommunication* **Parameters:** | Name | Type | Description | |------|------|-------------| | `restartTime` | `DateTime` | A [DateTime](/docs/reference/engine/datatypes/DateTime.md) indicating when the server is scheduled to shut down. The actual restart may happen slightly after this projected time, but not before. | | `source` | `CloseReason` | An [CloseReason](/docs/reference/engine/enums/CloseReason.md) describing what triggered the restart. The value will be [CloseReason.DeveloperUpdate](/docs/reference/engine/enums/CloseReason.md) for place version updates. | | `attributes` | `Dictionary` | A dictionary of developer-supplied metadata associated with the restart. If no attributes are provided, then this will be an empty table. | **ServerRestartScheduled** Connects a callback to the `ServerRestartScheduled` event to teleport players to an updated game server before the scheduled restart when there is a break in between game rounds. ```lua local roundInProgress = false -- Set elsewhere in game code whenever a game round starts or ends local restartPending = false game.ServerRestartScheduled:Connect(function(restartTime, source, attributes) local msg = attributes and attributes.message or "" local timeStr = restartTime:FormatLocalTime("LTS", "en-us") -- since this is run on the server, it will be in UTC -- Forward these fields to clients via remote:FireAllClients() to display customized messages in your UI print(string.format("Server restart at %s from source: %s. Reason: %s.", timeStr, tostring(source), msg)) if not roundInProgress then print("No round in progress. Teleporting players in 10 seconds") task.wait(10) -- Teleporting players when a restart has been scheduled will bring them to a server on the latest place version game:GetService("TeleportService"):TeleportAsync(game.PlaceId, game:GetService("Players"):GetPlayers()) else -- You can add code to teleport players after a game round ends if restartPending is true. restartPending = true end end) ``` ### Event: DataModel.AllowedGearTypeChanged **Signature:** `DataModel.AllowedGearTypeChanged()` > **Deprecated:** This item is deprecated . Do not use it for new work. This event fires when SetGearSettings is called with a different value for `allowedGenres`. *Security: None* ### Event: DataModel.ItemChanged **Signature:** `DataModel.ItemChanged(object: Instance, descriptor: string)` > **Deprecated:** This function has been superseded by [Object.Changed](/docs/reference/engine/classes/Object.md), which should be used in new work instead. This event fires when a property of any object in the [DataModel](/docs/reference/engine/classes/DataModel.md) is changed. *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `object` | `Instance` | | | `descriptor` | `string` | | **DataModel.ItemChanged** ```lua game.ItemChanged:Connect(function(item, property) print(string.format("%s.%s has changed", item.Name, property)) end) --> Workspace.DistributedGameTime has changed ``` ## Callbacks ### Callback: DataModel.OnClose **Signature:** `DataModel.OnClose(): Tuple` > **Deprecated:** This function is deprecated. It is recommended to use [DataModel:BindToClose()](/docs/reference/engine/classes/DataModel.md) instead. Invoked before the game is shut down. When this callback returns, or the timeout period is hit, the game finishes shutting down. *Security: None · Thread Safety: Unsafe* **Returns:** `Tuple` **DataModel.OnClose1** The example below would print "Closing, waiting" to the output. It would wait three seconds and then print "Done". At this point Roblox will be able to shut down the server. ```lua game.OnClose = function() print("Closing, waiting") task.wait(3) print("Done") end ``` ## Inherited Members ### From [ServiceProvider](/docs/reference/engine/classes/ServiceProvider.md) - **Method `FindService(className: string): Instance`**: Returns the service specified by the given className if it's already - **Method `GetService(className: string): Instance`**: Returns the service with the requested class name, creating it if it does - **Method `getService(className: string): Instance`**: *(deprecated)* - **Method `service(className: string): Instance`**: *(deprecated)* - **Event `Close`**: Fires when the current place is exited. - **Event `ServiceAdded`**: Fired when a service is created. - **Event `ServiceRemoving`**: Fired when a service is about to be removed. ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: DataModelMesh last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotBrowsable summary: "The DataModelMesh is an abstract class from which mesh classes descend." --- # Class: DataModelMesh > The DataModelMesh is an abstract class from which mesh classes descend. ## Description The DataModelMesh is an abstract class from which mesh classes descend. Mesh classes are objects that, when parented to [BaseParts](/docs/reference/engine/classes/BasePart.md) alter the appearance of the part to that of a predefined mesh. Note, they only alter the appearance of the part and not the physics/collision boundaries of the part. Developers looking to apply a mesh to a part that alters the part's collision should use [MeshParts](/docs/reference/engine/classes/MeshPart.md). Note the [MeshPart](/docs/reference/engine/classes/MeshPart.md) and [CharacterMesh](/docs/reference/engine/classes/CharacterMesh.md) classes do not descend from DataModelMesh. ## Properties ### Property: DataModelMesh.Offset ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` The Offset of a mesh determines the distance from the [BasePart.Position](/docs/reference/engine/classes/BasePart.md) of a [BasePart](/docs/reference/engine/classes/BasePart.md) that the mesh will be displayed. #### How to use mesh offset The Offset property changes the relative position the mesh will be rendered at. For example, an offset of 0, 5, 0 will cause the mesh to be displayed 5 studs above the position of the [BasePart](/docs/reference/engine/classes/BasePart.md). The position of the [BasePart](/docs/reference/engine/classes/BasePart.md) remains unchanged, meaning the physics collision box of the part will remain in the same location. This is demonstrated in the image below where the green outline (a [SelectionBox](/docs/reference/engine/classes/SelectionBox.md)) shows the extents of the [BasePart](/docs/reference/engine/classes/BasePart.md). #### Other uses for mesh offset There are a number of interesting uses for the mesh offset property. - Offset and [DataModelMesh.Scale](/docs/reference/engine/classes/DataModelMesh.md) can be animated using [TweenService](/docs/reference/engine/classes/TweenService.md) relatively inexpensively as the engine does not need to make any physics/collision calculations as the [BasePart](/docs/reference/engine/classes/BasePart.md) is not moved. - Changing the relationship between the mesh and its collision extents (determined by the [BasePart](/docs/reference/engine/classes/BasePart.md)) **Mesh Offset and Scale** In this code sample a `BasePart` is instanced with a `SpecialMesh`. The [DataModelMesh.Scale](/docs/reference/engine/classes/DataModelMesh.md) and [DataModelMesh.Offset](/docs/reference/engine/classes/DataModelMesh.md) properties of the `SpecialMesh` are then animated using `TweenService`. ```lua local TweenService = game:GetService("TweenService") -- instance a part and a mesh local part = Instance.new("Part") part.Size = Vector3.new(4, 8, 4) part.Position = Vector3.new(0, 4, 0) part.Anchored = true part.CanCollide = false local mesh = Instance.new("SpecialMesh") mesh.MeshType = Enum.MeshType.FileMesh mesh.MeshId = "rbxassetid://1086413449" mesh.TextureId = "rbxassetid://1461576423" mesh.Offset = Vector3.new(0, 0, 0) mesh.Scale = Vector3.new(4, 4, 4) mesh.Parent = part -- selection box to show part extents local box = Instance.new("SelectionBox") box.Adornee = part box.Parent = part -- parent part to workspace part.Parent = workspace -- animate offset and scale with a tween local tween = TweenService:Create( mesh, TweenInfo.new(1, Enum.EasingStyle.Linear, Enum.EasingDirection.Out, -1, true, 0), { Scale = Vector3.new(1, 1, 1), Offset = Vector3.new(0, 3, 0) } ) tween:Play() ``` ### Property: DataModelMesh.Scale ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` The Scale of a mesh determines the size of the mesh relative to its original dimensions. #### How to use mesh scale The scale property works slightly differently depending on the type of mesh being used. Note the size of the [BasePart](/docs/reference/engine/classes/BasePart.md) remains unchanged, meaning the physics collision box of the part will remain the same. - [SpecialMesh](/docs/reference/engine/classes/SpecialMesh.md) objects with [SpecialMesh.FileType](/docs/reference/engine/classes/SpecialMesh.md) set to 'FileMesh' scale relative to the original dimensions of the mesh when it was uploaded to Roblox - [BlockMesh](/docs/reference/engine/classes/BlockMesh.md) objects or [SpecialMesh](/docs/reference/engine/classes/SpecialMesh.md) objects with [SpecialMesh.FileType](/docs/reference/engine/classes/SpecialMesh.md) set to 'Brick', 'Wedge' or 'Sphere' scale uniformly relative to the [BasePart.Size](/docs/reference/engine/classes/BasePart.md) of their parent - [CylinderMesh](/docs/reference/engine/classes/CylinderMesh.md) objects or [SpecialMesh](/docs/reference/engine/classes/SpecialMesh.md) objects with [SpecialMesh.FileType](/docs/reference/engine/classes/SpecialMesh.md) set to 'Cylinder' scale relative to the [BasePart.Size](/docs/reference/engine/classes/BasePart.md) of their parent. Uniformly for the cylinders height axis and maintaining a 1:1 ratio for the length and width of the cylinder, using the lowest value. - [SpecialMesh](/docs/reference/engine/classes/SpecialMesh.md) objects with [SpecialMesh.FileType](/docs/reference/engine/classes/SpecialMesh.md) set to 'Head' currently scale in a non standard manner. Developers should not rely on this as there are plans to change this behavior. - [SpecialMesh](/docs/reference/engine/classes/SpecialMesh.md) objects with [SpecialMesh.FileType](/docs/reference/engine/classes/SpecialMesh.md) set to 'Torso' scale in a non standard manner. Developers should not rely on this as there are plans to deprecate this mesh type. #### Mesh scale demonstration The above behavior can be seen in the following demonstration images. Linear scaling relative to part size for 'Brick', 'Wedge' and 'Sphere' meshes. Linear scaling relative to original uploaded mesh for 'FileMesh' meshes Non-uniform constrained scaling for 'Cylinder' meshes #### Other uses for mesh scale There are a number of interesting uses for the mesh offset property. - [DataModelMesh.Offset](/docs/reference/engine/classes/DataModelMesh.md) and Scale can be animated using [TweenService](/docs/reference/engine/classes/TweenService.md) relatively inexpensively as the engine does not need to make any physics/collision calculations as the [BasePart](/docs/reference/engine/classes/BasePart.md) is not changed. - Changing the relationship between the mesh and its collision extents (determined by the [BasePart](/docs/reference/engine/classes/BasePart.md)) **Mesh Offset and Scale** In this code sample a `BasePart` is instanced with a `SpecialMesh`. The [DataModelMesh.Scale](/docs/reference/engine/classes/DataModelMesh.md) and [DataModelMesh.Offset](/docs/reference/engine/classes/DataModelMesh.md) properties of the `SpecialMesh` are then animated using `TweenService`. ```lua local TweenService = game:GetService("TweenService") -- instance a part and a mesh local part = Instance.new("Part") part.Size = Vector3.new(4, 8, 4) part.Position = Vector3.new(0, 4, 0) part.Anchored = true part.CanCollide = false local mesh = Instance.new("SpecialMesh") mesh.MeshType = Enum.MeshType.FileMesh mesh.MeshId = "rbxassetid://1086413449" mesh.TextureId = "rbxassetid://1461576423" mesh.Offset = Vector3.new(0, 0, 0) mesh.Scale = Vector3.new(4, 4, 4) mesh.Parent = part -- selection box to show part extents local box = Instance.new("SelectionBox") box.Adornee = part box.Parent = part -- parent part to workspace part.Parent = workspace -- animate offset and scale with a tween local tween = TweenService:Create( mesh, TweenInfo.new(1, Enum.EasingStyle.Linear, Enum.EasingDirection.Out, -1, true, 0), { Scale = Vector3.new(1, 1, 1), Offset = Vector3.new(0, 3, 0) } ) tween:Play() ``` ### Property: DataModelMesh.VertexColor ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` **VertexColor** determines the hue change of the [Texture](/docs/reference/engine/classes/FileMesh.md) of a [FileMesh](/docs/reference/engine/classes/FileMesh.md). Note that this property is a [Vector3](/docs/reference/engine/datatypes/Vector3.md) rather than a [Color3](/docs/reference/engine/datatypes/Color3.md); to convert, use the [Color3.R](/docs/reference/engine/datatypes/Color3.md), [Color3.G](/docs/reference/engine/datatypes/Color3.md), and [Color3.B](/docs/reference/engine/datatypes/Color3.md) properties. Although this property allows basic modification of a texture, changing a texture entirely provides more control. See [MeshPart](/docs/reference/engine/classes/MeshPart.md) for more details. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: DataModelSession last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable --- # Class: DataModelSession ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: DataStore last_updated: 2026-06-29T19:34:07Z inherits: - GlobalDataStore - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated --- # Class: DataStore ## Description See [Data stores](/docs/en-us/cloud-services/data-stores.md) for an in-depth guide on data structure, management, error handling, limits, and more. ## Methods ### Method: DataStore:GetVersionAsync **Signature:** `DataStore:GetVersionAsync(key: string, version: string): Tuple` This function retrieves the specified key version as well as a [DataStoreKeyInfo](/docs/reference/engine/classes/DataStoreKeyInfo.md) instance. A version identifier can be found through [DataStore:ListVersionsAsync()](/docs/reference/engine/classes/DataStore.md) or alternatively be the identifier returned by [GlobalDataStore:SetAsync()](/docs/reference/engine/classes/GlobalDataStore.md). *Yields · Security: None · Thread Safety: Unsafe · Capabilities: DataStore* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | Key name for which the version info is requested. If [DataStoreOptions.AllScopes](/docs/reference/engine/classes/DataStoreOptions.md) was set to true when accessing the data store through [DataStoreService:GetDataStore()](/docs/reference/engine/classes/DataStoreService.md), this key name must be prepended with the original scope as in "scope/key". | | `version` | `string` | | Version number of the key for which the version info is requested. | **Returns:** `Tuple` — The value of the key at the specified version and a [DataStoreKeyInfo](/docs/reference/engine/classes/DataStoreKeyInfo.md) instance that includes the version number, date and time the version was created, and functions to retrieve [UserIds](/docs/reference/engine/classes/Player.md) and metadata. ### Method: DataStore:GetVersionAtTimeAsync **Signature:** `DataStore:GetVersionAtTimeAsync(key: string, timestamp: int64): Tuple` This function retrieves the key version that was current at a given time as well as a [DataStoreKeyInfo](/docs/reference/engine/classes/DataStoreKeyInfo.md) instance. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: DataStore* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | Key name for which the version info is requested. If [DataStoreOptions.AllScopes](/docs/reference/engine/classes/DataStoreOptions.md) was set to true when accessing the data store through [DataStoreService:GetDataStore()](/docs/reference/engine/classes/DataStoreService.md), this key name must be prepended with the original scope as in "scope/key". | | `timestamp` | `int64` | | Unix timestamp in milliseconds for which the requested version was current. Must be greater than zero. Must not be more than ten minutes in the future. | **Returns:** `Tuple` — The value of the key that was current at the specified time and a [DataStoreKeyInfo](/docs/reference/engine/classes/DataStoreKeyInfo.md) instance that includes the version number, date and time the version was created, and functions to retrieve [UserIds](/docs/reference/engine/classes/Player.md) and metadata. `nil` if no available version was current at the requested time. **Retrieving DataStore Versions by Time** The following code sample retrieves data store key versions using timestamps. ```lua local DataStoreService = game:GetService("DataStoreService") local dataStore = DataStoreService:GetDataStore("DataStore") local key = "key-123" function setData(data) local success, result = pcall(function() dataStore:SetAsync(key, data) end) if not success then warn(result) end end function getVersionAtTime(timestamp) local success, result, keyInfo = pcall(function() return dataStore:GetVersionAtTimeAsync(key, timestamp.UnixTimestampMillis) end) if success then if result == nil then print("No version found at time") else print(result, keyInfo.Version) end else warn(result) end end -- Previously ran at 2024/12/02 6:00 UTC setData("version 1") -- Previously ran at 2024/12/02 9:00 UTC setData("version 2") -- Prints "No version found at time" local time1 = DateTime.fromUniversalTime(2024, 12, 02, 05, 00) getVersionAtTime(time1) -- Prints "version 1 " local time2 = DateTime.fromUniversalTime(2024, 12, 02, 07, 00) getVersionAtTime(time2) -- Prints "version 2 " local time3 = DateTime.fromUniversalTime(2024, 12, 02, 10, 00) getVersionAtTime(time3) ``` ### Method: DataStore:ListKeysAsync **Signature:** `DataStore:ListKeysAsync(prefix: string, pageSize?: int, cursor: string, excludeDeleted?: boolean): DataStoreKeyPages` This function returns a [DataStoreKeyPages](/docs/reference/engine/classes/DataStoreKeyPages.md) object for enumerating through keys of a data store. It accepts an optional `prefix` parameter to only locate keys whose names start with the provided prefix. If [DataStoreOptions.AllScopes](/docs/reference/engine/classes/DataStoreOptions.md) was set to true when accessing the data store through [DataStoreService:GetDataStore()](/docs/reference/engine/classes/DataStoreService.md), keys will be returned with all scopes as prefixes. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: DataStore* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `prefix` | `string` | | **(Optional)** Prefix to use for locating keys. | | `pageSize` | `int` | `0` | **(Optional)** Number of items to be returned in each page. If no value is given, the engine sends a default value of 0 to the data store web service, which in turn defaults to 50 items per page. | | `cursor` | `string` | | **(Optional)** Cursor to continue iteration. | | `excludeDeleted` | `boolean` | `false` | **(Optional)** Exclude deleted keys from being returned. When enabled ListKeys will check up to 512 keys. If all checked keys are deleted then it will return an empty list with a cursor to continue iteration. | **Returns:** `DataStoreKeyPages` — A [DataStoreKeyPages](/docs/reference/engine/classes/DataStoreKeyPages.md) instance that enumerates the keys as [DataStoreKey](/docs/reference/engine/classes/DataStoreKey.md) instances. ### Method: DataStore:ListVersionsAsync **Signature:** `DataStore:ListVersionsAsync(key: string, sortDirection?: SortDirection, minDate?: int64, maxDate?: int64, pageSize?: int): DataStoreVersionPages` This function enumerates versions of the specified key in either ascending or descending order specified by a [SortDirection](/docs/reference/engine/enums/SortDirection.md) parameter. It can optionally filter the returned versions by minimum and maximum timestamp. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: DataStore* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | Key name for the versions to list. If [DataStoreOptions.AllScopes](/docs/reference/engine/classes/DataStoreOptions.md) was set to true when accessing the data store through [DataStoreService:GetDataStore()](/docs/reference/engine/classes/DataStoreService.md), this key name must be prepended with the original scope as in "scope/key". | | `sortDirection` | `SortDirection` | `Ascending` | **(Optional)** Enum specifying ascending or descending sort order. | | `minDate` | `int64` | `0` | **(Optional)** Unix timestamp in milliseconds after which the versions should be listed. | | `maxDate` | `int64` | `0` | **(Optional)** Unix timestamp in milliseconds up to which the versions should be listed. | | `pageSize` | `int` | `0` | **(Optional)** Number of items to be returned in each page. If no value is given, the engine sends a default value of 0 to the data store web service, which in turn defaults to 1024 items per page. | **Returns:** `DataStoreVersionPages` — A [DataStoreVersionPages](/docs/reference/engine/classes/DataStoreVersionPages.md) instance that enumerates all the versions of the key as [DataStoreObjectVersionInfo](/docs/reference/engine/classes/DataStoreObjectVersionInfo.md) instances. **Retrieving DataStore Versions With A Date Filter** The following code sample retrieves all versions after a specified starting time, sorted in ascending order. ```lua local DataStoreService = game:GetService("DataStoreService") local experienceStore = DataStoreService:GetDataStore("PlayerExperience") local time = DateTime.fromUniversalTime(2020, 10, 09, 01, 42) local listSuccess, pages = pcall(function() return experienceStore:ListVersionsAsync("User_1234", nil, time.UnixTimestampMillis) end) if listSuccess then local items = pages:GetCurrentPage() for key, info in pairs(items) do print("Key:", key, "; Version:", info.Version, "; Created:", info.CreatedTime, "; Deleted:", info.IsDeleted) end end ``` ### Method: DataStore:RemoveVersionAsync **Signature:** `DataStore:RemoveVersionAsync(key: string, version: string): ()` This function permanently deletes the specified version of a key. Version identifiers can be found through [DataStore:ListVersionsAsync()](/docs/reference/engine/classes/DataStore.md). Unlike [GlobalDataStore:RemoveAsync()](/docs/reference/engine/classes/GlobalDataStore.md), this function does not create a new "tombstone" version and the removed value cannot be retrieved later. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: DataStore* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | Key name for which a version is to be removed. If [DataStoreOptions.AllScopes](/docs/reference/engine/classes/DataStoreOptions.md) was set to true when accessing the data store through [DataStoreService:GetDataStore()](/docs/reference/engine/classes/DataStoreService.md), this key name must be prepended with the original scope as in "scope/key". | | `version` | `string` | | Version number of the key to remove. | **Returns:** `()` ## Inherited Members ### From [GlobalDataStore](/docs/reference/engine/classes/GlobalDataStore.md) - **Method `GetAsync(key: string, options?: DataStoreGetOptions): Tuple`**: Returns the value of a key in a specified data store and a - **Method `IncrementAsync(key: string, delta?: int, userIds?: Array, options?: DataStoreIncrementOptions): Variant`**: Increments the value of a key by the provided amount (both must be - **Method `OnUpdate(key: string, callback: Function): RBXScriptConnection`**: Sets a callback function to be executed any time the value associated with *(deprecated)* - **Method `RemoveAsync(key: string): Tuple`**: Removes the specified key while also retaining an accessible version. - **Method `SetAsync(key: string, value: Variant, userIds?: Array, options?: DataStoreSetOptions): Variant`**: Sets the value of the data store for the given key. - **Method `UpdateAsync(key: string, transformFunction: Function): Tuple`**: Updates a key's value with a new value from the specified callback ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: DataStoreGetOptions last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotReplicated --- # Class: DataStoreGetOptions ## Properties ### Property: DataStoreGetOptions.UseCache ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "DataStore" ] } ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: DataStoreIncrementOptions last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotReplicated summary: "Specifies additional parameters for a GlobalDataStore:IncrementAsync() call." --- # Class: DataStoreIncrementOptions > Specifies additional parameters for a [GlobalDataStore:IncrementAsync()](/docs/reference/engine/classes/GlobalDataStore.md) > call. ## Description An object that specifies additional parameters for a [GlobalDataStore:IncrementAsync()](/docs/reference/engine/classes/GlobalDataStore.md) call. See also: - [Data Stores](/docs/en-us/cloud-services/data-stores.md), an in-depth guide on data structure, management, error handling, etc. ## Methods ### Method: DataStoreIncrementOptions:GetMetadata **Signature:** `DataStoreIncrementOptions:GetMetadata(): Dictionary` This function gets custom metadata associated with this [DataStoreIncrementOptions](/docs/reference/engine/classes/DataStoreIncrementOptions.md) instance. *Security: None · Thread Safety: Unsafe · Capabilities: DataStore* **Returns:** `Dictionary` — Metadata associated with this [DataStoreIncrementOptions](/docs/reference/engine/classes/DataStoreIncrementOptions.md) instance. ### Method: DataStoreIncrementOptions:SetMetadata **Signature:** `DataStoreIncrementOptions:SetMetadata(attributes: Dictionary): ()` This function sets custom metadata used by [GlobalDataStore:IncrementAsync()](/docs/reference/engine/classes/GlobalDataStore.md) to associate metadata with a key. Metadata should be in key-value pair form. *Security: None · Thread Safety: Unsafe · Capabilities: DataStore* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `attributes` | `Dictionary` | | Metadata values to set for the key. | **Returns:** `()` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: DataStoreInfo last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated summary: "Object describing data store information." --- # Class: DataStoreInfo > Object describing data store information. ## Description Object describing data store information such as name, created time, and time last updated. This object is a member of the [DataStoreListingPages](/docs/reference/engine/classes/DataStoreListingPages.md) object returned by [DataStoreService:ListDataStoresAsync()](/docs/reference/engine/classes/DataStoreService.md). See also: - [Data Stores](/docs/en-us/cloud-services/data-stores.md), an in-depth guide on data structure, management, error handling, etc. ## Properties ### Property: DataStoreInfo.CreatedTime ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "DataStore" ] } ``` This property indicates when the data store was created in milliseconds since epoch. ### Property: DataStoreInfo.DataStoreName ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "DataStore" ] } ``` This property indicates the name of the data store. It is used as a unique identifier to retrieve a data store instance with [DataStoreService:GetDataStore()](/docs/reference/engine/classes/DataStoreService.md). ### Property: DataStoreInfo.UpdatedTime ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "DataStore" ] } ``` This property indicates the last time the data store was updated in milliseconds since epoch. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: DataStoreKey last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated summary: "Object representing a key on a DataStoreKeyPages object." --- # Class: DataStoreKey > Object representing a key on a [DataStoreKeyPages](/docs/reference/engine/classes/DataStoreKeyPages.md) object. ## Description Object representing a key on a [DataStoreKeyPages](/docs/reference/engine/classes/DataStoreKeyPages.md) object. It contains the key name as [DataStoreKey.KeyName](/docs/reference/engine/classes/DataStoreKey.md). This object is a member of the [DataStoreKeyPages](/docs/reference/engine/classes/DataStoreKeyPages.md) object returned by [DataStore:ListKeysAsync()](/docs/reference/engine/classes/DataStore.md). See also: - [Data Stores](/docs/en-us/cloud-services/data-stores.md), an in-depth guide on data structure, management, error handling, etc. ## Properties ### Property: DataStoreKey.KeyName ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "DataStore" ] } ``` This property indicates the name of the key. This name can then be used in other operations such as [GlobalDataStore:GetAsync()](/docs/reference/engine/classes/GlobalDataStore.md) and [GlobalDataStore:SetAsync()](/docs/reference/engine/classes/GlobalDataStore.md). If [DataStoreOptions.AllScopes](/docs/reference/engine/classes/DataStoreOptions.md) was set to true when accessing the data store through [DataStoreService:GetDataStore()](/docs/reference/engine/classes/DataStoreService.md), the name will include its scope as a prefix. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: DataStoreKeyInfo last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated summary: "An object specifying information about a particular version of the key." --- # Class: DataStoreKeyInfo > An object specifying information about a particular version of the key. ## Description An object describing information about a particular version of the key. This is returned as the second return value by [GlobalDataStore:GetAsync()](/docs/reference/engine/classes/GlobalDataStore.md), [GlobalDataStore:UpdateAsync()](/docs/reference/engine/classes/GlobalDataStore.md), [GlobalDataStore:IncrementAsync()](/docs/reference/engine/classes/GlobalDataStore.md), [GlobalDataStore:RemoveAsync()](/docs/reference/engine/classes/GlobalDataStore.md), and [DataStore:GetVersionAsync()](/docs/reference/engine/classes/DataStore.md). See also: - [Data Stores](/docs/en-us/cloud-services/data-stores.md), an in-depth guide on data structure, management, error handling, etc. ## Properties ### Property: DataStoreKeyInfo.CreatedTime ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "DataStore" ] } ``` This property indicates the date and time the object was created, formatted as the number of milliseconds since epoch. ### Property: DataStoreKeyInfo.UpdatedTime ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "DataStore" ] } ``` This property indicates the date and time the object was last updated, formatted as the number of milliseconds since epoch. ### Property: DataStoreKeyInfo.Version ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "DataStore" ] } ``` This property uniquely identifies the version of the object. It can be passed to [DataStore:GetVersionAsync()](/docs/reference/engine/classes/DataStore.md) or [DataStore:RemoveVersionAsync()](/docs/reference/engine/classes/DataStore.md) to get or remove the version respectively. ## Methods ### Method: DataStoreKeyInfo:GetMetadata **Signature:** `DataStoreKeyInfo:GetMetadata(): Dictionary` This function returns the metadata associated with the latest version of the object. *Security: None · Thread Safety: Unsafe · Capabilities: DataStore* **Returns:** `Dictionary` — Metadata associated with the key. ### Method: DataStoreKeyInfo:GetUserIds **Signature:** `DataStoreKeyInfo:GetUserIds(): Array` This function returns an array of [UserIds](/docs/reference/engine/classes/Player.md) tagged with the object. *Security: None · Thread Safety: Unsafe · Capabilities: DataStore* **Returns:** `Array` — An array of [UserIds](/docs/reference/engine/classes/Player.md) associated with the object. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: DataStoreKeyPages last_updated: 2026-06-29T19:34:07Z inherits: - Pages - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated summary: "A special type of Pages object whose pages contain DataStoreKey instances." --- # Class: DataStoreKeyPages > A special type of [Pages](/docs/reference/engine/classes/Pages.md) object whose pages contain > [DataStoreKey](/docs/reference/engine/classes/DataStoreKey.md) instances. ## Description A special type of [Pages](/docs/reference/engine/classes/Pages.md) object whose pages contain [DataStoreKey](/docs/reference/engine/classes/DataStoreKey.md) instances. [Pages:GetCurrentPage()](/docs/reference/engine/classes/Pages.md) can be used to retrieve an array of the [DataStoreKey](/docs/reference/engine/classes/DataStoreKey.md) instances. See also: - [Data Stores](/docs/en-us/cloud-services/data-stores.md), an in-depth guide on data structure, management, error handling, etc. ## Properties ### Property: DataStoreKeyPages.Cursor ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "DataStore" ] } ``` ## Inherited Members ### From [Pages](/docs/reference/engine/classes/Pages.md) - **Property `IsFinished`** (`boolean`): Whether or not the current page is the last page available. - **Method `AdvanceToNextPageAsync(): ()`**: Iterates to the next page in the pages object, if possible. - **Method `GetCurrentPage(): Array`**: Returns the items on the current page. The keys in the item are determined ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: DataStoreListingPages last_updated: 2026-06-29T19:34:07Z inherits: - Pages - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated summary: "A special type of Pages object whose pages contain DataStoreInfo instances." --- # Class: DataStoreListingPages > A special type of [Pages](/docs/reference/engine/classes/Pages.md) object whose pages contain > [DataStoreInfo](/docs/reference/engine/classes/DataStoreInfo.md) instances. ## Description A special type of [Pages](/docs/reference/engine/classes/Pages.md) object whose pages contain [DataStoreInfo](/docs/reference/engine/classes/DataStoreInfo.md) instances. [Pages:GetCurrentPage()](/docs/reference/engine/classes/Pages.md) can be used to retrieve an array of the [DataStoreInfo](/docs/reference/engine/classes/DataStoreInfo.md) instances. See also: - [Data Stores](/docs/en-us/cloud-services/data-stores.md), an in-depth guide on data structure, management, error handling, etc. ## Properties ### Property: DataStoreListingPages.Cursor ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "DataStore" ] } ``` ## Inherited Members ### From [Pages](/docs/reference/engine/classes/Pages.md) - **Property `IsFinished`** (`boolean`): Whether or not the current page is the last page available. - **Method `AdvanceToNextPageAsync(): ()`**: Iterates to the next page in the pages object, if possible. - **Method `GetCurrentPage(): Array`**: Returns the items on the current page. The keys in the item are determined ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: DataStoreObjectVersionInfo last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated summary: "An instance describing version information for a key." --- # Class: DataStoreObjectVersionInfo > An instance describing version information for a key. ## Description An instance describing version information for a key, including the version string, created time, and whether it has been marked as deleted. See also: - [Data Stores](/docs/en-us/cloud-services/data-stores.md), an in-depth guide on data structure, management, error handling, etc. ## Properties ### Property: DataStoreObjectVersionInfo.CreatedTime ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "DataStore" ] } ``` This property indicates when the version was created in milliseconds since epoch. ### Property: DataStoreObjectVersionInfo.IsDeleted ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "DataStore" ] } ``` This property describes whether the version has been marked as deleted. Deleted versions will be permanently deleted after 30 days. ### Property: DataStoreObjectVersionInfo.Version ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "DataStore" ] } ``` This property uniquely identifies a particular version of the key. It can be passed to [DataStore:GetVersionAsync()](/docs/reference/engine/classes/DataStore.md) or [DataStore:RemoveVersionAsync()](/docs/reference/engine/classes/DataStore.md) to get or remove the version respectively. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: DataStoreOptions last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotReplicated summary: "Object used to provide additional parameters to DataStoreService:GetDataStore()." --- # Class: DataStoreOptions > Object used to provide additional parameters to > [DataStoreService:GetDataStore()](/docs/reference/engine/classes/DataStoreService.md). ## Description Any object containing additional parameters that are used by [DataStoreService:GetDataStore()](/docs/reference/engine/classes/DataStoreService.md). ## Properties ### Property: DataStoreOptions.AllScopes ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "DataStore" ] } ``` This property specifies whether the [GlobalDataStore](/docs/reference/engine/classes/GlobalDataStore.md) should work with all scopes. ## Methods ### Method: DataStoreOptions:SetExperimentalFeatures **Signature:** `DataStoreOptions:SetExperimentalFeatures(experimentalFeatures: Dictionary): ()` This function currently has no effect. *Security: None · Thread Safety: Unsafe · Capabilities: DataStore* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `experimentalFeatures` | `Dictionary` | | Luau table in `key = value` format where the key is the experimental feature name and the value is a boolean which specifies whether to enable. | **Returns:** `()` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: DataStorePages last_updated: 2026-06-29T19:34:07Z inherits: - Pages - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated summary: "A Pages object that allows iteration through OrderedDataStore key/value pairs." --- # Class: DataStorePages > A [Pages](/docs/reference/engine/classes/Pages.md) object that allows iteration through [OrderedDataStore](/docs/reference/engine/classes/OrderedDataStore.md) > key/value pairs. ## Description A special type of [Pages](/docs/reference/engine/classes/Pages.md) object whose pages contain key/value pairs from an [OrderedDataStore](/docs/reference/engine/classes/OrderedDataStore.md). For this object, [GetCurrentPage()](/docs/reference/engine/classes/Pages.md) returns an array of tables, each containing keys named **key** and **value**; these reflect the key/value pair data. ## Code Samples **OrderedDataStore Basics** This code sample demonstrates usage of an `OrderedDataStore` and pages. ```lua local DataStoreService = game:GetService("DataStoreService") local pointsStore = DataStoreService:GetOrderedDataStore("Points") local function printTopTenPlayers() local isAscending = false local pageSize = 10 local pages = pointsStore:GetSortedAsync(isAscending, pageSize) local topTen = pages:GetCurrentPage() -- The data in 'topTen' is stored with the index being the index on the page -- For each item, 'data.key' is the key in the OrderedDataStore and 'data.value' is the value for rank, data in ipairs(topTen) do local name = data.key local points = data.value print(name .. " is ranked #" .. rank .. " with " .. points .. "points") end -- Potentially load the next page... --pages:AdvanceToNextPageAsync() end -- Create some data pointsStore:SetAsync("Alex", 55) pointsStore:SetAsync("Charley", 32) pointsStore:SetAsync("Sydney", 68) -- Display the top ten players printTopTenPlayers() ``` ## Inherited Members ### From [Pages](/docs/reference/engine/classes/Pages.md) - **Property `IsFinished`** (`boolean`): Whether or not the current page is the last page available. - **Method `AdvanceToNextPageAsync(): ()`**: Iterates to the next page in the pages object, if possible. - **Method `GetCurrentPage(): Array`**: Returns the items on the current page. The keys in the item are determined ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: DataStoreService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated summary: "A game service that gives access to persistent data storage across places in a game." --- # Class: DataStoreService > A game service that gives access to persistent data storage across places in a > game. ## Description **DataStoreService** exposes methods for getting [GlobalDataStore](/docs/reference/engine/classes/GlobalDataStore.md) and [OrderedDataStore](/docs/reference/engine/classes/OrderedDataStore.md) objects. Data stores can only be accessed by game servers, so you can only use [DataStoreService](/docs/reference/engine/classes/DataStoreService.md) within a [Script](/docs/reference/engine/classes/Script.md) or a [ModuleScript](/docs/reference/engine/classes/ModuleScript.md) that is used by a [Script](/docs/reference/engine/classes/Script.md). See [Data stores](/docs/en-us/cloud-services/data-stores.md) for an in-depth guide on data structure, management, error handling, limits, and more. ## Code Samples **DataStore Budget** This code sample prints the request budget for all data store request types. ```lua local DataStoreService = game:GetService("DataStoreService") for _, enumItem in pairs(Enum.DataStoreRequestType:GetEnumItems()) do print(enumItem.Name, DataStoreService:GetRequestBudgetForRequestType(enumItem)) end ``` ## Methods ### Method: DataStoreService:GetDataStore **Signature:** `DataStoreService:GetDataStore(name: string, scope?: string, options?: Instance): DataStore` This function creates a [DataStore](/docs/reference/engine/classes/DataStore.md) instance with the provided name and scope. Subsequent calls to this method with the same name/scope will return the same object. Using the `scope` parameter will restrict operations to that scope by automatically prepending the scope to keys in all operations done on the data store. This function also accepts an optional [DataStoreOptions](/docs/reference/engine/classes/DataStoreOptions.md) instance which includes options for enabling [AllScopes](/docs/reference/engine/classes/DataStoreOptions.md). See [Versioning, listing, and caching](/docs/en-us/cloud-services/data-stores/versioning-listing-and-caching.md#scopes) for details on scope. *Security: None · Thread Safety: Unsafe · Capabilities: DataStore* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name` | `string` | | Name of the data store. | | `scope` | `string` | `global` | **(Optional)** A string specifying the scope. | | `options` | `Instance` | `nil` | **(Optional)** A [DataStoreOptions](/docs/reference/engine/classes/DataStoreOptions.md) instance to enable experimental features and v2 API features. | **Returns:** `DataStore` — A [DataStore](/docs/reference/engine/classes/DataStore.md) instance with provided name and optional scope. ### Method: DataStoreService:GetGlobalDataStore **Signature:** `DataStoreService:GetGlobalDataStore(): DataStore` This function returns the default [GlobalDataStore](/docs/reference/engine/classes/GlobalDataStore.md). If you want to access a specific **named** data store instead, you should use the [GetDataStore()](/docs/reference/engine/classes/DataStoreService.md) function. Note that the [DataStore](/docs/reference/engine/classes/DataStore.md) returned by this function always uses the scope `u`. See [Data stores](/docs/en-us/cloud-services/data-stores.md) for details on scope. *Security: None · Thread Safety: Unsafe · Capabilities: DataStore* **Returns:** `DataStore` **Get GlobalDataStore Instance** The following example retrieves a default data store instance which behaves like a regular [Instance](/docs/reference/engine/classes/Instance.md). Since a [GlobalDataStore](/docs/reference/engine/classes/GlobalDataStore.md) is an [Instance](/docs/reference/engine/classes/Instance.md), functions such as [GlobalDataStore:GetChildren()](/docs/reference/engine/classes/GlobalDataStore.md) will execute without error. ```lua local DataStoreService = game:GetService("DataStoreService") local GlobalDataStore = DataStoreService:GetGlobalDataStore() print(GlobalDataStore.Name) ``` ### Method: DataStoreService:GetOrderedDataStore **Signature:** `DataStoreService:GetOrderedDataStore(name: string, scope?: string): OrderedDataStore` This method returns an [OrderedDataStore](/docs/reference/engine/classes/OrderedDataStore.md), similar to the way [GetDataStore()](/docs/reference/engine/classes/DataStoreService.md) does with [GlobalDataStores](/docs/reference/engine/classes/GlobalDataStore.md). Subsequent calls to this method with the same name/scope will return the same object. *Security: None · Thread Safety: Unsafe · Capabilities: DataStore* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name` | `string` | | | | `scope` | `string` | `global` | | **Returns:** `OrderedDataStore` **OrderedDataStore Basics** This code sample demonstrates usage of an `OrderedDataStore` and pages. ```lua local DataStoreService = game:GetService("DataStoreService") local pointsStore = DataStoreService:GetOrderedDataStore("Points") local function printTopTenPlayers() local isAscending = false local pageSize = 10 local pages = pointsStore:GetSortedAsync(isAscending, pageSize) local topTen = pages:GetCurrentPage() -- The data in 'topTen' is stored with the index being the index on the page -- For each item, 'data.key' is the key in the OrderedDataStore and 'data.value' is the value for rank, data in ipairs(topTen) do local name = data.key local points = data.value print(name .. " is ranked #" .. rank .. " with " .. points .. "points") end -- Potentially load the next page... --pages:AdvanceToNextPageAsync() end -- Create some data pointsStore:SetAsync("Alex", 55) pointsStore:SetAsync("Charley", 32) pointsStore:SetAsync("Sydney", 68) -- Display the top ten players printTopTenPlayers() ``` ### Method: DataStoreService:GetRequestBudgetForRequestType **Signature:** `DataStoreService:GetRequestBudgetForRequestType(requestType: DataStoreRequestType): int` This function returns the number of data store requests that the current place can make based on the given [DataStoreRequestType](/docs/reference/engine/enums/DataStoreRequestType.md). Any requests made that exceed this budget are subject to throttling. Monitoring and adjusting the frequency of data store requests using this function is recommended. *Security: None · Thread Safety: Unsafe · Capabilities: DataStore* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `requestType` | `DataStoreRequestType` | | | **Returns:** `int` **Print Request Budget** ```lua local DataStoreService = game:GetService("DataStoreService") local globalStore = DataStoreService:GetGlobalDataStore() local function printBudget() local budget = DataStoreService:GetRequestBudgetForRequestType(Enum.DataStoreRequestType.SetIncrementAsync) print("Current set/increment budget:", budget) end for i = 1, 5 do local key = "key" .. i local success, err = pcall(function() globalStore:SetAsync(key, true) end) if success then printBudget() else print(err) end end ``` ### Method: DataStoreService:ListDataStoresAsync **Signature:** `DataStoreService:ListDataStoresAsync(prefix: string, pageSize?: int, cursor: string): DataStoreListingPages` Returns a [DataStoreListingPages](/docs/reference/engine/classes/DataStoreListingPages.md) object for enumerating through all of the experience's data stores. It accepts an optional `prefix` parameter to only locate data stores whose names start with the provided prefix. Only data stores containing at least one object will be listed via this function. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: DataStore* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `prefix` | `string` | | **(Optional)** Prefix to enumerate data stores that start with the given prefix. | | `pageSize` | `int` | `0` | **(Optional)** Number of items to be returned in each page. If no value is given, the engine sends a default value of 0 to the data store web service, which in turn defaults to 32 items per page. | | `cursor` | `string` | | **(Optional)** Cursor to continue iteration. | **Returns:** `DataStoreListingPages` — [DataStoreListingPages](/docs/reference/engine/classes/DataStoreListingPages.md) instance containing [DataStoreInfo](/docs/reference/engine/classes/DataStoreInfo.md) instances that provide details such as name, creation time, and time last updated. ### Method: DataStoreService:SetRateLimitForRequestType **Signature:** `DataStoreService:SetRateLimitForRequestType(requestType: DataStoreRequestType, baseLimit: int, perPlayerLimit: int): ()` Sets the per-server rate limit (requests per minute) for a given Data Store request type. The configured limit overrides the default rate limit for that request type on the current server. The rate limit is calculated as `rateLimit = baseLimit + (perPlayerLimit * numPlayers)`, where `numPlayers` is the current number of active players on the server. [DataStoreRequestType.OnUpdate](/docs/reference/engine/enums/DataStoreRequestType.md) and [DataStoreRequestType.UpdateAsync](/docs/reference/engine/enums/DataStoreRequestType.md) cannot be configured with this function. Calling this API with those request types will result in an error. You should call this API **once per request type during server initialization**. We don't recommend calling this API during active experience logic. If called multiple times, the new limit definitions will immediately overwrite the previous ones. The `baseLimit` and `perPlayerLimit` have different constraints depending on the request type. See the table below for more information. ##### Constraints by Request Type | Request Type | baseLimit constraints | perPlayerLimit constraints | | :---------------------- | :-------------------- | :------------------------- | | GetAsync | [0, 60] | [0, 40] | | SetIncrementAsync | [0, 60] | [0, 40] | | UpdateAsync | N/A | N/A | | GetSortedAsync | [0, 5] | [0, 2] | | SetIncrementSortedAsync | [0, 30] | [0, 5] | | OnUpdate | N/A | N/A | | ListAsync | [0, 5] | [0, 2] | | GetVersionAsync | [0, 5] | [0, 2] | | RemoveVersionAsync | [0, 5] | [0, 2] | | StandardRead | [0, 10000] | [0, 200] | | StandardWrite | [0, 10000] | [0, 200] | | StandardList | [0, 10000] | [0, 200] | | StandardRemove | [0, 10000] | [0, 200] | | OrderedRead | [0, 10000] | [0, 200] | | OrderedWrite | [0, 10000] | [0, 200] | | OrderedList | [0, 10000] | [0, 200] | | OrderedRemove | [0, 10000] | [0, 200] | *Security: None · Thread Safety: Unsafe · Capabilities: DataStore* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `requestType` | `DataStoreRequestType` | | | | `baseLimit` | `int` | | | | `perPlayerLimit` | `int` | | | **Returns:** `()` **Set Rate Limits** The following example shows the initialization of a script that is used to loop through and migrate all of the data from one standard data store to two others, according to a hypothetical `MigrateScript` module script. It ultimately deletes the data from the source data store. ```lua local MigrateScript = require(script.Parent.MigrateScript) local DataStoreService = game:GetService("DataStoreService") DataStoreService:SetRateLimitForRequestType(Enum.DataStoreRequestType.StandardRead, 1000, 0) DataStoreService:SetRateLimitForRequestType(Enum.DataStoreRequestType.StandardWrite, 2000, 0) -- two writes, so needs double the limit as get DataStoreService:SetRateLimitForRequestType(Enum.DataStoreRequestType.StandardList, 100, 0) DataStoreService:SetRateLimitForRequestType(Enum.DataStoreRequestType.StandardRemove, 1000, 0) local sourceStore = DataStoreService:GetDataStore("ds_to_migrate") local destinationStore1 = DataStoreService:GetDataStore("ds_destination_1") local destinationStore2 = DataStoreService:GetDataStore("ds_destination_2") -- Helper to wait for budget local function waitForBudget(requestType) while (DataStoreService:GetRequestBudgetForRequestType(requestType) <= 0) do task.wait(1) end end waitForBudget(Enum.DataStoreRequestType.StandardList) local success, pages = pcall(function() return sourceStore:ListKeysAsync() end) if success then while true do local items = pages:GetCurrentPage() for _, item in ipairs(items) do -- 1. Fetch data from Source waitForBudget(Enum.DataStoreRequestType.StandardRead) local getSuccess, value = pcall(function() return sourceStore:GetAsync(item.KeyName) end) if getSuccess and value ~= nil then -- 2. Migrate data to Destinations waitForBudget(Enum.DataStoreRequestType.StandardWrite) local setSuccess, err = pcall(function() destinationStore1:SetAsync(item.KeyName, MigrateScript.Migrate1(value)) end) if setSuccess then print("Successfully migrated key to destination 1: " .. item.KeyName) else warn("Failed to migrate key to destination 1: " .. item.KeyName .. " - " .. tostring(err)) end waitForBudget(Enum.DataStoreRequestType.StandardWrite) local setSuccess, err = pcall(function() destinationStore2:SetAsync(item.KeyName, MigrateScript.Migrate2(value)) end) if setSuccess then print("Successfully migrated key to destination 2: " .. item.KeyName) else warn("Failed to migrate key to destination 2: " .. item.KeyName .. " - " .. tostring(err)) end -- 3. Delete data from source waitForBudget(Enum.DataStoreRequestType.StandardRemove) local deleteSuccess, err = pcall(function() sourceStore:RemoveAsync(item.KeyName) end) if deleteSuccess then print("Successfully deleted key from source: " .. item.KeyName) else warn("Failed to delete key from source: " .. item.KeyName .. " - " .. tostring(err)) end else warn("Failed to fetch key from source: " .. item.KeyName) end end if pages.IsFinished then break end -- 3. Advance Page waitForBudget(Enum.DataStoreRequestType.StandardList) local nextSuccess = pcall(function() pages:AdvanceToNextPageAsync() end) if not nextSuccess then warn("Failed to advance to next page.") break end end print("Migration process complete.") else warn("Failed to list keys.") end ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: DataStoreSetOptions last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotReplicated summary: "Specifies additional parameters for a GlobalDataStore:SetAsync() call." --- # Class: DataStoreSetOptions > Specifies additional parameters for a [GlobalDataStore:SetAsync()](/docs/reference/engine/classes/GlobalDataStore.md) call. ## Description An object that specifies additional parameters for a [GlobalDataStore:SetAsync()](/docs/reference/engine/classes/GlobalDataStore.md) call. See also: - [Data Stores](/docs/en-us/cloud-services/data-stores.md), an in-depth guide on data structure, management, error handling, etc. ## Methods ### Method: DataStoreSetOptions:GetMetadata **Signature:** `DataStoreSetOptions:GetMetadata(): Dictionary` This function gets custom metadata associated with this [DataStoreSetOptions](/docs/reference/engine/classes/DataStoreSetOptions.md) instance. *Security: None · Thread Safety: Unsafe · Capabilities: DataStore* **Returns:** `Dictionary` — Metadata associated with this [DataStoreSetOptions](/docs/reference/engine/classes/DataStoreSetOptions.md) instance. ### Method: DataStoreSetOptions:SetMetadata **Signature:** `DataStoreSetOptions:SetMetadata(attributes: Dictionary): ()` This function sets custom metadata used by [GlobalDataStore:SetAsync()](/docs/reference/engine/classes/GlobalDataStore.md) to associate metadata with a key. Metadata should be in key-value pair form. *Security: None · Thread Safety: Unsafe · Capabilities: DataStore* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `attributes` | `Dictionary` | | Metadata values to set for the key. | **Returns:** `()` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: DataStoreVersionPages last_updated: 2026-06-29T19:34:07Z inherits: - Pages - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated summary: "A special type of Pages object whose pages contain DataStoreObjectVersionInfo instances." --- # Class: DataStoreVersionPages > A special type of [Pages](/docs/reference/engine/classes/Pages.md) object whose pages contain > [DataStoreObjectVersionInfo](/docs/reference/engine/classes/DataStoreObjectVersionInfo.md) instances. ## Description A special type of [Pages](/docs/reference/engine/classes/Pages.md) object whose pages contain [DataStoreObjectVersionInfo](/docs/reference/engine/classes/DataStoreObjectVersionInfo.md) instances from a [GlobalDataStore](/docs/reference/engine/classes/GlobalDataStore.md). [Pages:GetCurrentPage()](/docs/reference/engine/classes/Pages.md) can be used to retrieve an array of the [DataStoreObjectVersionInfo](/docs/reference/engine/classes/DataStoreObjectVersionInfo.md) instances. See also: - [Data Stores](/docs/en-us/cloud-services/data-stores.md), an in-depth guide on data structure, management, error handling, etc. ## Inherited Members ### From [Pages](/docs/reference/engine/classes/Pages.md) - **Property `IsFinished`** (`boolean`): Whether or not the current page is the last page available. - **Method `AdvanceToNextPageAsync(): ()`**: Iterates to the next page in the pages object, if possible. - **Method `GetCurrentPage(): Array`**: Returns the items on the current page. The keys in the item are determined ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Debris last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service summary: "Allows scheduling the guaranteed destruction of an object without yielding." --- # Class: Debris > Allows scheduling the guaranteed destruction of an object without yielding. ## Description The **Debris** service allows scheduling guaranteed destruction of an object without yielding. #### Advantages Besides creating a bit of a mess, objects that are no longer required can use up system memory and cause an experience to run slower over time. For this reason, it's always advised to call [Instance:Destroy()](/docs/reference/engine/classes/Instance.md) on objects you no longer need. In some cases, however, an object may have a specific period of utility before it can be destroyed. Consider a wall being smashed into individual bricks. If you want a brick to linger for 3 seconds before being destroyed, you can use the following code: ```lua task.wait(3) brick:Destroy() ``` However, waiting causes the thread to yield which may be undesired. To avoid yielding, a callback function can be scheduled to run on a new thread after 3 seconds: ```lua task.delay(3, function() brick:Destroy() end) ``` Or in one line: ```lua task.delay(3, brick.Destroy, brick) ``` While this now avoids yielding, it has a potential drawback in that the scheduled callback will never run if the script is disabled or destroyed before the callback runs. This is where [Debris](/docs/reference/engine/classes/Debris.md) has a specific advantage, as it does not yield the current thread and runs outside the context of the script, guaranteeing the instance is eventually destroyed even if the script is disabled or destroyed. The following code does not yield and guarantees the instance will be destroyed: ```lua Debris:AddItem(brick, 3) ``` Note that [Debris](/docs/reference/engine/classes/Debris.md) has a hardcoded maximum of 1,000 objects, so if more than 1,000 items are added, the oldest debris will be destroyed instantly to make room for new debris. ## Code Samples **Debris AddItem** Creates parts on a loop and parents them to the Workspace, then uses Debris.AddItem to clean them up. ```lua local Debris = game:GetService("Debris") local ball = Instance.new("Part") ball.Anchored = false ball.Shape = Enum.PartType.Ball ball.TopSurface = Enum.SurfaceType.Smooth ball.BottomSurface = Enum.SurfaceType.Smooth ball.Size = Vector3.new(1, 1, 1) local RNG = Random.new() local MAX_VELOCITY = 10 while true do local newBall = ball:Clone() newBall.BrickColor = BrickColor.random() newBall.CFrame = CFrame.new(0, 30, 0) newBall.Velocity = Vector3.new(RNG:NextNumber(-MAX_VELOCITY, MAX_VELOCITY), 0, RNG:NextNumber(-MAX_VELOCITY, MAX_VELOCITY)) newBall.Parent = game.Workspace Debris:AddItem(newBall, 2) task.wait(0.1) end ``` ## Properties ### Property: Debris.MaxItems ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` > **Deprecated:** This property is deprecated and should not be used in new work. The maximum number of items that can be assigned to the Debris service at one time. If this number is exceeded, objects are automatically destroyed in order from oldest to newest until the amount is less than or equal to MaxItems. This property is currently restricted and will error if set. The value is hardcoded to 1,000 items. ## Methods ### Method: Debris:AddItem **Signature:** `Debris:AddItem(item: Instance, lifetime?: double): ()` Schedules a given [Instance](/docs/reference/engine/classes/Instance.md) for destruction within the specified lifetime. After the `lifetime` argument has elapsed, the object is destroyed in the same manner as [Instance:Destroy()](/docs/reference/engine/classes/Instance.md). Note that the `lifetime` argument is optional and defaults to 10 seconds. Note that [Debris](/docs/reference/engine/classes/Debris.md) has a hardcoded maximum of 1,000 objects, so if more than 1,000 items are added, the oldest debris will be destroyed instantly to make room for new debris. This means you should treat the `lifetime` parameter as a **maximum** lifetime, not an exact lifetime. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `item` | `Instance` | | The [Instance](/docs/reference/engine/classes/Instance.md) to add to [Debris](/docs/reference/engine/classes/Debris.md). | | `lifetime` | `double` | `10` | Number of seconds before the [Instance](/docs/reference/engine/classes/Instance.md) should be destroyed. | **Returns:** `()` **Debris AddItem** Creates parts on a loop and parents them to the Workspace, then uses Debris.AddItem to clean them up. ```lua local Debris = game:GetService("Debris") local ball = Instance.new("Part") ball.Anchored = false ball.Shape = Enum.PartType.Ball ball.TopSurface = Enum.SurfaceType.Smooth ball.BottomSurface = Enum.SurfaceType.Smooth ball.Size = Vector3.new(1, 1, 1) local RNG = Random.new() local MAX_VELOCITY = 10 while true do local newBall = ball:Clone() newBall.BrickColor = BrickColor.random() newBall.CFrame = CFrame.new(0, 30, 0) newBall.Velocity = Vector3.new(RNG:NextNumber(-MAX_VELOCITY, MAX_VELOCITY), 0, RNG:NextNumber(-MAX_VELOCITY, MAX_VELOCITY)) newBall.Parent = game.Workspace Debris:AddItem(newBall, 2) task.wait(0.1) end ``` ### Method: Debris:addItem **Signature:** `Debris:addItem(item: Instance, lifetime?: double): ()` > **Deprecated:** This function is a deprecated variant of [Debris:AddItem()](/docs/reference/engine/classes/Debris.md) which should be used instead. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `item` | `Instance` | | | | `lifetime` | `double` | `10` | | **Returns:** `()` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: DebugSettings last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated - NotBrowsable summary: "Collection of various developer-facing diagnostics information." --- # Class: DebugSettings > Collection of various developer-facing diagnostics information. ## Description The DebugSettings allows you to view diagnostics information regarding Roblox. It is labeled as **Diagnostics** in the Roblox Studio Settings menu. ## Properties ### Property: DebugSettings.DataModel ```json { "type": "int", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Performance" } ``` Describes whether a [DataModel](/docs/reference/engine/classes/DataModel.md) is actively in memory, as an integer (where 1 = true, and 0 = false). ### Property: DebugSettings.InstanceCount ```json { "type": "int", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Performance" } ``` The number of instances active in the simulation. ### Property: DebugSettings.IsScriptStackTracingEnabled ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Errors" } ``` Whether or not a stacktrace is displayed in the output for an error. ### Property: DebugSettings.JobCount ```json { "type": "int", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Performance" } ``` Returns the number of internal DataModel jobs actively being processed. ### Property: DebugSettings.PlayerCount ```json { "type": "int", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Performance" } ``` The number of players currently in the active game-instance. ### Property: DebugSettings.ReportSoundWarnings ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Errors" } ``` Whether or not sound warnings should be reported. ### Property: DebugSettings.RobloxVersion ```json { "type": "string", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Profile" } ``` The current client version of Roblox. Can also be retrieved by using the version() function. ### Property: DebugSettings.TickCountPreciseOverride ```json { "type": "TickCountSampleMethod", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Benchmarking" } ``` Sets the internal sampling method used to measure elapsed time with consistency across platforms. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Decal last_updated: 2026-06-29T19:34:07Z inherits: - FaceInstance - Instance - Object type: class memory_category: GraphicsTexture summary: "Applies an image texture to a face of a parent BasePart." --- # Class: Decal > Applies an image texture to a face of a parent [BasePart](/docs/reference/engine/classes/BasePart.md). ## Description The [Decal](/docs/reference/engine/classes/Decal.md) object applies an image texture to a face of a parent [BasePart](/docs/reference/engine/classes/BasePart.md). The affected face is dependent on the [Face](/docs/reference/engine/classes/Decal.md) property, and the size of the decal is dependent on the size of the face. The applied image texture is determined by its [ColorMapContent](/docs/reference/engine/classes/Decal.md) property. For details on how to upload images, see [asset management](/docs/en-us/projects/assets.md#asset-management). For more information, review [textures and decals](/docs/en-us/parts/textures-decals.md). See also: - [Texture](/docs/reference/engine/classes/Texture.md) for repeating surface images. - [MeshPart.TextureContent](/docs/reference/engine/classes/MeshPart.md) to apply an image texture to a [MeshPart](/docs/reference/engine/classes/MeshPart.md). - [SurfaceGui](/docs/reference/engine/classes/SurfaceGui.md) to apply an [ImageLabel](/docs/reference/engine/classes/ImageLabel.md) or [ImageButton](/docs/reference/engine/classes/ImageButton.md) to an in-experience 3D object. ## Properties ### Property: Decal.Color3 ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` The [Color3](/docs/reference/engine/datatypes/Color3.md) tint of the [Decal](/docs/reference/engine/classes/Decal.md). Note that this property only sets the **tint** of the decal rather than the color; unless the image associated with the [Decal](/docs/reference/engine/classes/Decal.md) was originally pure white, then the color cannot be freely changed using this property. **Decal Color3** This code sample creates a `Decal` in the workspace and changes its [Decal.Color3](/docs/reference/engine/classes/Decal.md) property on a loop using [TweenService](/docs/reference/engine/classes/TweenService.md). ```lua local TweenService = game:GetService("TweenService") local part = Instance.new("Part") part.Size = Vector3.new(10, 10, 1) part.Position = Vector3.new(0, 5, 0) part.Anchored = true part.Transparency = 1 local decal = Instance.new("Decal") decal.Face = Enum.NormalId.Front decal.ColorMapContent = Content.fromUri("http://www.roblox.com/asset/?id=1145367640") decal.Parent = part part.Parent = workspace local redTween = TweenService:Create( decal, TweenInfo.new(1, Enum.EasingStyle.Linear, Enum.EasingDirection.Out), { Color3 = Color3.new(1, 0, 0) } ) local greenTween = TweenService:Create( decal, TweenInfo.new(1, Enum.EasingStyle.Linear, Enum.EasingDirection.Out), { Color3 = Color3.new(0, 1, 0) } ) local blueTween = TweenService:Create( decal, TweenInfo.new(1, Enum.EasingStyle.Linear, Enum.EasingDirection.Out), { Color3 = Color3.new(0, 0, 1) } ) while true do redTween:Play() redTween.Completed:Wait() greenTween:Play() greenTween.Completed:Wait() blueTween:Play() blueTween.Completed:Wait() end ``` ### Property: Decal.ColorMap ```json { "type": "ContentId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` This property takes a content ID to determine the color and opacity of the surface. This texture is sometimes called the **albedo** texture. ### Property: Decal.ColorMapContent ```json { "type": "Content", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` This property takes a [Content](/docs/reference/engine/datatypes/Content.md) object to determine the color and opacity of the surface. This texture is sometimes called the **albedo** texture. ### Property: Decal.MetalnessMapContent ```json { "type": "Content", "access": "ReadOnly", "security": { "read": "None", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` ### Property: Decal.NormalMapContent ```json { "type": "Content", "access": "ReadOnly", "security": { "read": "None", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` ### Property: Decal.Rotation ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` This property controls the rotation angle of the decal texture in degrees. Rotation is applied to the UV coordinates in the clockwise direction. ### Property: Decal.RoughnessMapContent ```json { "type": "Content", "access": "ReadOnly", "security": { "read": "None", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` ### Property: Decal.Texture ```json { "type": "ContentId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` The content ID of the image to be applied by the [Decal](/docs/reference/engine/classes/Decal.md). ### Property: Decal.TextureContent ```json { "type": "Content", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` The texture content displayed by the [Decal](/docs/reference/engine/classes/Decal.md). Supports [asset URIs](/docs/en-us/projects/assets.md#asset-uris) and [EditableImage](/docs/reference/engine/classes/EditableImage.md) objects. ### Property: Decal.TexturePack ```json { "type": "ContentId", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` ### Property: Decal.Transparency ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` Determines the transparency of the [Decal](/docs/reference/engine/classes/Decal.md) with `0` being completely opaque and `1` completely transparent. Note that [LocalTransparencyModifier](/docs/reference/engine/classes/Decal.md) acts as a **multiplier** for this transparency level and should be used when the transparency of the decal is likely to be changed by another script. ### Property: Decal.UVOffset ```json { "type": "Vector2", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` This property controls the UV coordinate translation. The [X](/docs/reference/engine/datatypes/Vector2.md) value (**U** offset) and [Y](/docs/reference/engine/datatypes/Vector2.md) value (**V** offset) are added to every corresponding UV coordinate during texture mapping. ### Property: Decal.UVScale ```json { "type": "Vector2", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` This property controls the scale applied to the horizontal and vertical coordinates. The [X](/docs/reference/engine/datatypes/Vector2.md) value (**U** scale) and [Y](/docs/reference/engine/datatypes/Vector2.md) value (**V** scale) are multiplied by every corresponding UV coordinate during texture mapping. ### Property: Decal.ZIndex ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` This property determines the order in which decals on the same [Face](/docs/reference/engine/classes/Decal.md) of a [BasePart](/docs/reference/engine/classes/BasePart.md) are rendered. Lower values are rendered first, so a decal with a higher `ZIndex` renders on top of those with a lower `ZIndex`. If you're unsure whether you'll need to layer a decal between two already-existing decals in the future, use larger separations between `ZIndex` values such as `10` or `100` to ensure space for future decals between. ### Property: Decal.Shiny ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` > **Deprecated:** This non-functional property is deprecated and should not be used in new work. This property dictates how shiny the decal is. It takes a value between `0` and `1`, where `1` is "fully shiny." ### Property: Decal.Specular ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` > **Deprecated:** This property no longer functions correctly and is deprecated. It should not be used in new work. Sets the specularity, which is how the surface responds to light being shined on it. ### Property: Decal.LocalTransparencyModifier *(hidden)* ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` Acts as a multiplier for the decal's [Transparency](/docs/reference/engine/classes/Decal.md) property. The effect is only visible to the [LocalPlayer](/docs/reference/engine/classes/Players.md) and will not replicate to the server. When this property is set to `1`, the [Decal](/docs/reference/engine/classes/Decal.md) will be completely invisible regardless of its original transparency. When it is set to `0`, the decal's rendered transparency will match the [Transparency](/docs/reference/engine/classes/Decal.md) value. ### Property: Decal.MetalnessMap *(hidden)* ```json { "type": "ContentId", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` ### Property: Decal.NormalMap *(hidden)* ```json { "type": "ContentId", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` ### Property: Decal.RoughnessMap *(hidden)* ```json { "type": "ContentId", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` ## Inherited Members ### From [FaceInstance](/docs/reference/engine/classes/FaceInstance.md) - **Property `Face`** (`NormalId`): Sets which face of the parent BasePart the object appears on. ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: DepthOfFieldEffect last_updated: 2026-06-29T19:34:07Z inherits: - PostEffect - Instance - Object type: class memory_category: Instances summary: "Simulates a camera lens by blurring parts of a scene not in focus." --- # Class: DepthOfFieldEffect > Simulates a camera lens by blurring parts of a scene not in focus. ## Description The **DepthOfFieldEffect** simulates a camera lens by blurring parts of a scene not in focus. Distant objects can be blurred or this effect can be used to focus on specific parts of a scene, like an item in an in-game shop. Like other post-processing effects, **DepthOfFieldEffect** will only work while [Enabled](/docs/reference/engine/classes/PostEffect.md) and when parented to [Lighting](/docs/reference/engine/classes/Lighting.md) or [Workspace.CurrentCamera](/docs/reference/engine/classes/Workspace.md). Also, it may render differently on low-end devices or depending on your Studio settings (see the **Quality Level** settings in **Rendering** → **Performance**). For more details on this effect and others, see [Post-Processing Effects](/docs/en-us/environment/post-processing-effects.md). ## Properties ### Property: DepthOfFieldEffect.FarIntensity ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Environment" ] } ``` Intensity of the far field blur, moving out in distance from the [FocusDistance](/docs/reference/engine/classes/DepthOfFieldEffect.md) point plus [InFocusRadius](/docs/reference/engine/classes/DepthOfFieldEffect.md) value. ### Property: DepthOfFieldEffect.FocusDistance ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Environment" ] } ``` Controls the distance away from the camera (in studs) where objects are in focus. ### Property: DepthOfFieldEffect.InFocusRadius ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Environment" ] } ``` Controls the distance away from the [FocusDistance](/docs/reference/engine/classes/DepthOfFieldEffect.md) (on both sides) where no blur is applied. Measured in studs. ### Property: DepthOfFieldEffect.NearIntensity ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Environment" ] } ``` Intensity of the near field blur, between the camera and the [FocusDistance](/docs/reference/engine/classes/DepthOfFieldEffect.md) point minus [InFocusRadius](/docs/reference/engine/classes/DepthOfFieldEffect.md) value. ## Inherited Members ### From [PostEffect](/docs/reference/engine/classes/PostEffect.md) - **Property `Enabled`** (`boolean`): Toggles whether or not the PostEffect is enabled. ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Dialog last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances summary: "Creates NPC billboard-style dialog bubbles." --- # Class: Dialog > Creates NPC billboard-style dialog bubbles. ## Description The Dialog object allows users to create non-player characters (NPCs) that players can talk to using a list of choices. The Dialog object can be inserted into a part such as a Humanoid's head, and then a player will see a speech bubble above the part that they can click on to start a conversation. The creator of a place can choose what choices the player can say by inserting [DialogChoice](/docs/reference/engine/classes/DialogChoice.md) objects into the dialog. ## Properties ### Property: Dialog.BehaviorType ```json { "type": "DialogBehaviorType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` The BehaviorType of a Dialog determines whether multiple players can interact with a dialog at once. The default value for this property is SinglePlayer. #### SinglePlayer When a Dialog is configured to SinglePlayer, only one player can interact with it at a time. As soon as a player engages with a dialog, other players will not be able to initiate the dialog until the first player is finished. While a player is engaged with a dialog, the other players will see the dialog choices of the player who started the dialog, along with the responses. #### MultiplePlayers When a Dialog is set to MultiplePlayers, any player can initiate a dialog at any time, even if another player has already initiated the dialog. Unlike SinglePlayer however, Dialogs set to MultiplePlayers will not show the dialog choices and responses to anyone but the player in the conversation. ```lua local Workspace = game:GetService("Workspace") local singlePlayerDialog = Instance.new("Dialog") local singlePlayerPart = Workspace.SinglePlayerPart singlePlayerDialog.BehaviorType = Enum.DialogBehaviorType.SinglePlayer singlePlayerDialog.InitialPrompt = "Only one person can interact with me at once." singlePlayerDialog.Parent = singlePlayerPart local multiplePlayersDialog = Instance.new("Dialog") local multiplePlayersPart = Workspace.MultiplePlayersPart multiplePlayersDialog.BehaviorType = Enum.DialogBehaviorType.MultiplePlayers multiplePlayersDialog.InitialPrompt = "Any number of players can interact with me at once." multiplePlayersDialog.Parent = multiplePlayersPart ``` ### Property: Dialog.ConversationDistance ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` The furthest distance that a player can be from the Dialog's parent to start a conversation. ### Property: Dialog.GoodbyeChoiceActive ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` Toggles whether the goodbye option will be displayed. If true, the dialog will display the content of [Dialog.GoodbyeDialog](/docs/reference/engine/classes/Dialog.md) as the last option after other dialog choices. Clicking on the goodbye option will exit the dialog. ### Property: Dialog.GoodbyeDialog ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` Sets the sentence that the dialog will show to the player when the chat ends ### Property: Dialog.InitialPrompt ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` Sets the first sentence that the dialog will show to the player, once a chat is commenced. ### Property: Dialog.InUse ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` If true, this dialog is being used by at least one player. ### Property: Dialog.Purpose ```json { "type": "DialogPurpose", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` Sets the icon that the initial dialog displays. ### Property: Dialog.Tone ```json { "type": "DialogTone", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` Sets the color of the NPC's speech bubble. ### Property: Dialog.TriggerDistance ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` Sets the maximum distance that a dialog can be triggered from. ### Property: Dialog.TriggerOffset ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` Sets the offset of the dialog relative to the dialog's parent. ## Methods ### Method: Dialog:GetCurrentPlayers **Signature:** `Dialog:GetCurrentPlayers(): Instances` The GetCurrentPlayers function of a Dialog will return a list of [Player](/docs/reference/engine/classes/Player.md) currently using the Dialog. If there are no players using the dialog then the returned list will be empty. *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Returns:** `Instances` **Dialog:GetCurrentPlayers** ```lua local dialog = script.Parent local function onChoiceSelected(_player, _choice) local currentPlayers = dialog:GetCurrentPlayers() print("The current players in the dialog:") for _, player in ipairs(currentPlayers) do print(player) end end dialog.DialogChoiceSelected:Connect(onChoiceSelected) ``` ## Events ### Event: Dialog.DialogChoiceSelected **Signature:** `Dialog.DialogChoiceSelected(player: Instance, dialogChoice: Instance)` Fired when a player chooses something to say, through a [Dialog](/docs/reference/engine/classes/Dialog.md) instance. This event is client-side only and will not fire on the server. It should be connected to in either a [LocalScript](/docs/reference/engine/classes/LocalScript.md) or a [ModuleScript](/docs/reference/engine/classes/ModuleScript.md) required by a [LocalScript](/docs/reference/engine/classes/LocalScript.md). *Security: None · Capabilities: UI* **Parameters:** | Name | Type | Description | |------|------|-------------| | `player` | `Instance` | | | `dialogChoice` | `Instance` | | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: DialogChoice last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances summary: "Used to craft the further choices available to players who have started a dialog conversation with an NPC." --- # Class: DialogChoice > Used to craft the further choices available to players who have started a > dialog conversation with an NPC. ## Properties ### Property: DialogChoice.GoodbyeChoiceActive ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` Toggles whether the goodbye option will be displayed. If true, the dialog will display the content of [DialogChoice.GoodbyeDialog](/docs/reference/engine/classes/DialogChoice.md) as the last option after other dialog choices. Clicking on the goodbye option will exit the dialog. ### Property: DialogChoice.GoodbyeDialog ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` Sets the sentence that the dialog will show to the player when the chat ends ### Property: DialogChoice.ResponseDialog ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` Sets what the NPC will say when the player chooses this DialogChoice. ### Property: DialogChoice.UserDialog ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` Sets what the player will say when they choose this DialogChoice. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: DigitsRigDescription last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Animation summary: "Maps the 15 phalanx joints of one hand (5 fingers, 3 joints each) and exposes forward- and inverse-kinematics helpers for controlling finger poses at runtime." --- # Class: DigitsRigDescription > Maps the 15 phalanx joints of one hand (5 fingers, 3 joints each) and exposes > forward- and inverse-kinematics helpers for controlling finger poses at > runtime. ## Description Enables interoperability of animations across hand rigs and drives the hand finger solver. Each `DigitsRigDescription` maps the 15 phalanx joints of one hand (5 fingers with 3 joints each) to engine instances, stores their T-pose adjustments, and exposes forward- and inverse-kinematics helpers for controlling finger poses at runtime. ## Properties ### Property: DigitsRigDescription.Index1 ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Mapping", "capabilities": [ "Animation" ] } ``` The [Motor6D](/docs/reference/engine/classes/Motor6D.md), [AnimationConstraint](/docs/reference/engine/classes/AnimationConstraint.md), or [Bone](/docs/reference/engine/classes/Bone.md) instance mapped to the proximal (first) phalanx of the index finger. ### Property: DigitsRigDescription.Index1TposeAdjustment ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tpose", "capabilities": [ "Animation" ] } ``` `CoordinateFrame` applied to `Index1` to normalize the joint into its canonical T-pose orientation before retargeting or solving. ### Property: DigitsRigDescription.Index2 ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Mapping", "capabilities": [ "Animation" ] } ``` The `Instance` mapped to the medial (second) phalanx of the index finger. ### Property: DigitsRigDescription.Index2TposeAdjustment ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tpose", "capabilities": [ "Animation" ] } ``` `CoordinateFrame` applied to `Index2` to normalize the joint into its canonical T-pose orientation before retargeting or solving. ### Property: DigitsRigDescription.Index3 ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Mapping", "capabilities": [ "Animation" ] } ``` The `Instance` mapped to the distal (third) phalanx of the index finger. ### Property: DigitsRigDescription.Index3TposeAdjustment ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tpose", "capabilities": [ "Animation" ] } ``` `CoordinateFrame` applied to `Index3` to normalize the joint into its canonical T-pose orientation before retargeting or solving. ### Property: DigitsRigDescription.IndexRange ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` A `Vector3` describing the motion range of the index finger, used by the finger solver. ### Property: DigitsRigDescription.IndexSize ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Size", "capabilities": [ "Animation" ] } ``` Size of the index finger, used by the finger solver to preserve contacts. ### Property: DigitsRigDescription.Middle1 ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Mapping", "capabilities": [ "Animation" ] } ``` The `Instance` mapped to the proximal (first) phalanx of the middle finger. ### Property: DigitsRigDescription.Middle1TposeAdjustment ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tpose", "capabilities": [ "Animation" ] } ``` `CoordinateFrame` applied to `Middle1` to normalize the joint into its canonical T-pose orientation before retargeting or solving. ### Property: DigitsRigDescription.Middle2 ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Mapping", "capabilities": [ "Animation" ] } ``` The `Instance` mapped to the medial (second) phalanx of the middle finger. ### Property: DigitsRigDescription.Middle2TposeAdjustment ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tpose", "capabilities": [ "Animation" ] } ``` `CoordinateFrame` applied to `Middle2` to normalize the joint into its canonical T-pose orientation before retargeting or solving. ### Property: DigitsRigDescription.Middle3 ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Mapping", "capabilities": [ "Animation" ] } ``` The `Instance` mapped to the distal (third) phalanx of the middle finger. ### Property: DigitsRigDescription.Middle3TposeAdjustment ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tpose", "capabilities": [ "Animation" ] } ``` `CoordinateFrame` applied to `Middle3` to normalize the joint into its canonical T-pose orientation before retargeting or solving. ### Property: DigitsRigDescription.MiddleRange ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` A `Vector3` describing the motion range of the middle finger, used by the finger solver and scalable-property system. ### Property: DigitsRigDescription.MiddleSize ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Size", "capabilities": [ "Animation" ] } ``` Scale factor for the middle finger, used when the rig is scaled through the scalable-property system. ### Property: DigitsRigDescription.Pinky1 ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Mapping", "capabilities": [ "Animation" ] } ``` The `Instance` mapped to the proximal (first) phalanx of the pinky finger. ### Property: DigitsRigDescription.Pinky1TposeAdjustment ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tpose", "capabilities": [ "Animation" ] } ``` `CoordinateFrame` applied to `Pinky1` to normalize the joint into its canonical T-pose orientation before retargeting or solving. ### Property: DigitsRigDescription.Pinky2 ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Mapping", "capabilities": [ "Animation" ] } ``` The `Instance` mapped to the medial (second) phalanx of the pinky finger. ### Property: DigitsRigDescription.Pinky2TposeAdjustment ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tpose", "capabilities": [ "Animation" ] } ``` `CoordinateFrame` applied to `Pinky2` to normalize the joint into its canonical T-pose orientation before retargeting or solving. ### Property: DigitsRigDescription.Pinky3 ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Mapping", "capabilities": [ "Animation" ] } ``` The `Instance` mapped to the distal (third) phalanx of the pinky finger. ### Property: DigitsRigDescription.Pinky3TposeAdjustment ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tpose", "capabilities": [ "Animation" ] } ``` `CoordinateFrame` applied to `Pinky3` to normalize the joint into its canonical T-pose orientation before retargeting or solving. ### Property: DigitsRigDescription.PinkyRange ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` A `Vector3` describing the motion range of the pinky finger, used by the finger solver and scalable-property system. ### Property: DigitsRigDescription.PinkySize ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Size", "capabilities": [ "Animation" ] } ``` Scale factor for the pinky finger, used when the rig is scaled through the scalable-property system. ### Property: DigitsRigDescription.Ring1 ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Mapping", "capabilities": [ "Animation" ] } ``` The `Instance` mapped to the proximal (first) phalanx of the ring finger. ### Property: DigitsRigDescription.Ring1TposeAdjustment ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tpose", "capabilities": [ "Animation" ] } ``` `CoordinateFrame` applied to `Ring1` to normalize the joint into its canonical T-pose orientation before retargeting or solving. ### Property: DigitsRigDescription.Ring2 ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Mapping", "capabilities": [ "Animation" ] } ``` The `Instance` mapped to the medial (second) phalanx of the ring finger. ### Property: DigitsRigDescription.Ring2TposeAdjustment ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tpose", "capabilities": [ "Animation" ] } ``` `CoordinateFrame` applied to `Ring2` to normalize the joint into its canonical T-pose orientation before retargeting or solving. ### Property: DigitsRigDescription.Ring3 ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Mapping", "capabilities": [ "Animation" ] } ``` The `Instance` mapped to the distal (third) phalanx of the ring finger. ### Property: DigitsRigDescription.Ring3TposeAdjustment ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tpose", "capabilities": [ "Animation" ] } ``` `CoordinateFrame` applied to `Ring3` to normalize the joint into its canonical T-pose orientation before retargeting or solving. ### Property: DigitsRigDescription.RingRange ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` A `Vector3` describing the motion range of the ring finger, used by the finger solver and scalable-property system. ### Property: DigitsRigDescription.RingSize ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Size", "capabilities": [ "Animation" ] } ``` Scale factor for the ring finger, used when the rig is scaled through the scalable-property system. ### Property: DigitsRigDescription.Side ```json { "type": "DigitsRigDescriptionSide", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Animation" ] } ``` Which hand this description belongs to: `None`, `Left`, or `Right`. Used when mapping `HandRigLabel` joints to the corresponding `HumanoidRigLabel` entries on the parent `HumanoidRigDescription`. ### Property: DigitsRigDescription.Thumb1 ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Mapping", "capabilities": [ "Animation" ] } ``` The `Instance` mapped to the proximal (first) phalanx of the thumb. ### Property: DigitsRigDescription.Thumb1TposeAdjustment ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tpose", "capabilities": [ "Animation" ] } ``` `CoordinateFrame` applied to `Thumb1` to normalize the joint into its canonical T-pose orientation before retargeting or solving. ### Property: DigitsRigDescription.Thumb2 ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Mapping", "capabilities": [ "Animation" ] } ``` The `Instance` mapped to the medial (second) phalanx of the thumb. ### Property: DigitsRigDescription.Thumb2TposeAdjustment ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tpose", "capabilities": [ "Animation" ] } ``` `CoordinateFrame` applied to `Thumb2` to normalize the joint into its canonical T-pose orientation before retargeting or solving. ### Property: DigitsRigDescription.Thumb3 ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Mapping", "capabilities": [ "Animation" ] } ``` The `Instance` mapped to the distal (third) phalanx of the thumb. ### Property: DigitsRigDescription.Thumb3TposeAdjustment ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tpose", "capabilities": [ "Animation" ] } ``` `CoordinateFrame` applied to `Thumb3` to normalize the joint into its canonical T-pose orientation before retargeting or solving. ### Property: DigitsRigDescription.ThumbRange ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` A `Vector3` describing the motion range of the thumb, used by the finger solver and scalable-property system. ### Property: DigitsRigDescription.ThumbSize ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Size", "capabilities": [ "Animation" ] } ``` Scale factor for the thumb, used when the rig is scaled through the scalable-property system. ## Methods ### Method: DigitsRigDescription:GetFingerControl **Signature:** `DigitsRigDescription:GetFingerControl(fingerIndex: int): Vector3` Returns the current control `Vector3` for the finger at the given 1-based index (1 = Thumb … 5 = Pinky). The components are: - **X** — extension in `[0, 1]`: `0` is fully curled, `1` is fully extended. - **Y** — lateral (side) rotation in `[-1, 1]`. - **Z** — axial (twist) rotation in `[-1, 1]`. *Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `fingerIndex` | `int` | | | **Returns:** `Vector3` ### Method: DigitsRigDescription:GetFingerTip **Signature:** `DigitsRigDescription:GetFingerTip(fingerIndex: int): Vector3` Returns the world-space position of the fingertip for the finger at the given 1-based index (1 = Thumb … 5 = Pinky). Not yet implemented; currently returns `Vector3.zero`. *Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `fingerIndex` | `int` | | | **Returns:** `Vector3` ### Method: DigitsRigDescription:SetFingerControl **Signature:** `DigitsRigDescription:SetFingerControl(fingerIndex: int, control: Vector3): ()` Drives the three-joint chain of the finger at the given 1-based index (1 = Thumb … 5 = Pinky) using forward kinematics. The `control` components are clamped before application: - **X** — extension, clamped to `[0, 1]`. - **Y** — lateral rotation, clamped to `[-1, 1]` (maps to ±50°). - **Z** — axial rotation, clamped to `[-1, 1]` (maps to ±50°). Joint angles for the three phalanges are computed via Ptolemy's theorem applied to the chain lengths. *Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `fingerIndex` | `int` | | | | `control` | `Vector3` | | | **Returns:** `()` ### Method: DigitsRigDescription:SetFingerTip **Signature:** `DigitsRigDescription:SetFingerTip(fingerIndex: int, point: Vector3): ()` Moves the fingertip of the finger at the given 1-based index (1 = Thumb … 5 = Pinky) toward the world-space target `point` using inverse kinematics. The solver computes the required extension, lateral, and axial control values and then calls `SetFingerControl` internally. *Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `fingerIndex` | `int` | | | | `point` | `Vector3` | | | **Returns:** `()` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: DistortionSoundEffect last_updated: 2026-06-29T19:34:07Z inherits: - SoundEffect - Instance - Object type: class memory_category: Instances summary: "Distorts audio, making it sound fuzzier and overdriven." --- # Class: DistortionSoundEffect > Distorts audio, making it sound fuzzier and overdriven. ## Description A distortion effect is used to simulate the effect that would occur when overdriving older style audio equipment (such as vacuum tubes). This effect causes clipping in the sound and adds a general "fuzziness". Like all other [SoundEffect](/docs/reference/engine/classes/SoundEffect.md), a DistortionSoundEffect can be applied either to a [Sound](/docs/reference/engine/classes/Sound.md) or [SoundGroup](/docs/reference/engine/classes/SoundGroup.md) by being parented to either. ## Properties ### Property: DistortionSoundEffect.Level ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "LegacySound" ] } ``` Range: 0 to 1 (default 0.5) The intensity of the effect. Setting this property to its minimum (0) will cause no distortion at all. ## Inherited Members ### From [SoundEffect](/docs/reference/engine/classes/SoundEffect.md) - **Property `Enabled`** (`boolean`): Toggles the effect on and off. - **Property `Priority`** (`int`): Determines the order the effect will be applied in relation to other ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: DockWidgetPluginGui last_updated: 2026-06-29T19:34:07Z inherits: - PluginGui - LayerCollector - GuiBase2d - GuiBase - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated --- # Class: DockWidgetPluginGui ## Description **DockWidgetPluginGui** is a [PluginGui](/docs/reference/engine/classes/PluginGui.md) that displays its contents inside a dockable Roblox Studio window. It is used to create widgets similar to the built-in **Animation Editor** and **Terrain Tools**. This GUI can be created using [Plugin:CreateDockWidgetPluginGuiAsync()](/docs/reference/engine/classes/Plugin.md). ## Properties ### Property: DockWidgetPluginGui.HostWidgetWasRestored ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` Describes whether the previous state of this [DockWidgetPluginGui](/docs/reference/engine/classes/DockWidgetPluginGui.md) was restored when it was created. ## Inherited Members ### From [PluginGui](/docs/reference/engine/classes/PluginGui.md) - **Property `Title`** (`string`): The title that is displayed above the contents of the PluginGui. - **Method `BindToClose(function?: Function): ()`**: Binds a function to the PluginGui close button, overriding the - **Method `GetRelativeMousePosition(): Vector2`**: Returns the position of the mouse relative to the PluginGui. - **Event `PluginDragDropped`**: Fires when the user releases their mouse when hovering over a PluginGui - **Event `PluginDragEntered`**: Fires when the user's mouse enters a PluginGui during a drag operation - **Event `PluginDragLeft`**: Fires when the user's mouse leaves a PluginGui during a drag operation - **Event `PluginDragMoved`**: Fires when the user's mouse moves within a PluginGui during a drag - **Event `WindowFocused`**: Fires when the user begins interacting with the window of the PluginGui. - **Event `WindowFocusReleased`**: Fires when the user stops interacting with the window of the PluginGui. ### From [LayerCollector](/docs/reference/engine/classes/LayerCollector.md) - **Property `Enabled`** (`boolean`): Toggles the visibility of this LayerCollector. - **Property `ResetOnSpawn`** (`boolean`): Determines if the LayerCollector resets (deletes itself and - **Property `ZIndexBehavior`** (`ZIndexBehavior`): Controls how GuiObject.ZIndex behaves on all descendants of this - **Method `GetLayoutNodeTree(): Dictionary`**: *(deprecated)* ### From [GuiBase2d](/docs/reference/engine/classes/GuiBase2d.md) - **Property `AbsolutePosition`** (`Vector2`): Describes the actual screen position of a GuiBase2d element, in - **Property `AbsoluteRotation`** (`float`): Describes the actual screen rotation of a GuiBase2d element, in - **Property `AbsoluteSize`** (`Vector2`): Describes the actual screen size of a GuiBase2d element, in - **Property `AutoLocalize`** (`boolean`): When set to `true`, localization will be applied to this GuiBase2d - **Property `Localize`** (`boolean`): Automatically set to true when a localization table's *(deprecated, hidden)* - **Property `RootLocalizationTable`** (`LocalizationTable`): A reference to a LocalizationTable to be used to apply automated - **Property `SelectionBehaviorDown`** (`SelectionBehavior`): Customizes gamepad selection behavior in the down direction. - **Property `SelectionBehaviorLeft`** (`SelectionBehavior`): Customizes gamepad selection behavior in the left direction. - **Property `SelectionBehaviorRight`** (`SelectionBehavior`): Customizes gamepad selection behavior in the right direction. - **Property `SelectionBehaviorUp`** (`SelectionBehavior`): Customizes gamepad selection behavior in the up direction. - **Property `SelectionGroup`** (`boolean`): Allows customization of gamepad selection movement. - **Event `SelectionChanged`**: Fires when the gamepad selection moves to, leaves, or changes within the ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: DoubleConstrainedValue last_updated: 2026-06-29T19:34:07Z inherits: - ValueBase - Instance - Object type: class memory_category: Instances tags: - Deprecated summary: "An instance which is used to create a number value which can never be less than the MinValue or more than the MaxValue." --- # Class: DoubleConstrainedValue > An instance which is used to create a number value which can never be less > than the MinValue or more than the MaxValue. ## Properties ### Property: DoubleConstrainedValue.MaxValue ```json { "type": "double", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "simulationAccess": true } ``` The highest number that the [DoubleConstrainedValue.Value](/docs/reference/engine/classes/DoubleConstrainedValue.md) property can be. ### Property: DoubleConstrainedValue.MinValue ```json { "type": "double", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "simulationAccess": true } ``` The lowest number that the [DoubleConstrainedValue.Value](/docs/reference/engine/classes/DoubleConstrainedValue.md) property can be. ### Property: DoubleConstrainedValue.Value ```json { "type": "double", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "simulationAccess": true } ``` Used to hold a number value between [DoubleConstrainedValue.MinValue](/docs/reference/engine/classes/DoubleConstrainedValue.md) and [DoubleConstrainedValue.MaxValue](/docs/reference/engine/classes/DoubleConstrainedValue.md). ### Property: DoubleConstrainedValue.ConstrainedValue *(hidden)* ```json { "type": "double", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` ## Events ### Event: DoubleConstrainedValue.Changed **Signature:** `DoubleConstrainedValue.Changed(value: double)` Fired whenever the [DoubleConstrainedValue.Value](/docs/reference/engine/classes/DoubleConstrainedValue.md) of the [DoubleConstrainedValue](/docs/reference/engine/classes/DoubleConstrainedValue.md) is changed. *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `value` | `double` | | **DoubleConstrainedValue.Changed** The below example, assuming all referenced objects existed, would print the DoubleConstrainedValue's new value each time it's changed. In the example below it would print 2, assuming it's within DoubleConstrainedValue's MinValue and MaxValue range. Otherwise there will be no change. ```lua workspace.DoubleConstrainedValue.Changed:Connect(function(newValue) print(newValue) end) workspace.DoubleConstrainedValue.Value = 2 ``` ### Event: DoubleConstrainedValue.changed **Signature:** `DoubleConstrainedValue.changed(value: double)` > **Deprecated:** This event is a deprecated variant of [DoubleConstrainedValue.Changed](/docs/reference/engine/classes/DoubleConstrainedValue.md) which has also been deprecated. Neither event should be used in new work. *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `value` | `double` | | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: DraftsService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated --- # Class: DraftsService ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: DragDetector last_updated: 2026-06-29T19:34:07Z inherits: - ClickDetector - Instance - Object type: class memory_category: Instances summary: "Instance which facilitates and encourages interaction with 3D objects in an experience." --- # Class: DragDetector > Instance which facilitates and encourages interaction with 3D objects in an > experience. ## Description The [DragDetector](/docs/reference/engine/classes/DragDetector.md) instance facilitates and encourages interaction with 3D objects in an experience, such as opening doors and drawers, sliding a part around, and much more. Key features include: - Place a [DragDetector](/docs/reference/engine/classes/DragDetector.md) under any [BasePart](/docs/reference/engine/classes/BasePart.md) or [Model](/docs/reference/engine/classes/Model.md) to make it draggable via all inputs (mouse, touch, gamepad, and VR), all without a single line of code. - Choose from several [DragStyle](/docs/reference/engine/classes/DragDetector.md) options, define how the object responds to motion via [ResponseStyle](/docs/reference/engine/classes/DragDetector.md), and optionally apply axis or movement limits. - Scripts can respond to manipulation of dragged objects to drive UI or make logical decisions, such as adjusting the light level in a room based on a sliding wall switch dimmer. - Players can manipulate anchored parts or models and they'll stay exactly where you put them upon release. - [DragDetectors](/docs/reference/engine/classes/DragDetector.md) work in Studio as long as you're **not** using the **Select**, **Move**, **Scale**, or **Rotate** tools, making it easier to test and adjust draggable objects while editing. See the [3D Drag Detectors](/docs/en-us/ui/3D-drag-detectors.md) guide for details and usage examples. ## Properties ### Property: DragDetector.ActivatedCursorIcon ```json { "type": "ContentId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Image", "capabilities": [ "Physics", "Input" ] } ``` Sets the cursor icon to display when the mouse is activated over the parent of this [DragDetector](/docs/reference/engine/classes/DragDetector.md). If this property is left blank, the detector will use the default icon. To change the activated cursor icon, set this property to the asset ID of the image you'd like to use. ### Property: DragDetector.ActivatedCursorIconContent ```json { "type": "Content", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Image", "capabilities": [ "Physics", "Input" ] } ``` Sets the cursor icon to display when the mouse is activated over the parent of this [DragDetector](/docs/reference/engine/classes/DragDetector.md). If this property is left blank, the detector will use the default icon. To change the activated cursor icon, set this property to the asset ID of the image you'd like to use. Only asset URIs are supported for this property. ### Property: DragDetector.ApplyAtCenterOfMass ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Physics Response", "capabilities": [ "Physics", "Input" ] } ``` When false (default), constraint force is applied at the point the user clicks on. When true, force is applied at the object's center of mass. Only relevant if [ResponseStyle](/docs/reference/engine/classes/DragDetector.md) is [DragDetectorResponseStyle.Physical](/docs/reference/engine/enums/DragDetectorResponseStyle.md) and the parent object is unanchored. ### Property: DragDetector.Axis ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Drag Directions", "capabilities": [ "Physics", "Input" ] } ``` The primary axis of motion, expressed relative to the reference frame. For a [DragStyle](/docs/reference/engine/classes/DragDetector.md) of [DragDetectorDragStyle.TranslateLine](/docs/reference/engine/enums/DragDetectorDragStyle.md), the direction of translation; for [DragDetectorDragStyle.TranslatePlane](/docs/reference/engine/enums/DragDetectorDragStyle.md), the normal to the plane of motion; for [DragDetectorDragStyle.RotateAxis](/docs/reference/engine/enums/DragDetectorDragStyle.md), the axis of 1D rotation. Changing this value automatically updates [Orientation](/docs/reference/engine/classes/DragDetector.md) and vice versa. ### Property: DragDetector.DragFrame ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Dragged Amount", "capabilities": [ "Physics", "Input" ] } ``` If [ReferenceInstance](/docs/reference/engine/classes/DragDetector.md) is set, the [CFrame](/docs/reference/engine/datatypes/CFrame.md) of the pivot relative to the reference frame; otherwise, the [CFrame](/docs/reference/engine/datatypes/CFrame.md) of the pivot relative to its frame at the beginning of the drag. ### Property: DragDetector.DragStyle ```json { "type": "DragDetectorDragStyle", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Physics", "Input" ] } ``` The paradigm used to generate proposed motion, given a stream of cursor rays. See [DragDetectorDragStyle](/docs/reference/engine/enums/DragDetectorDragStyle.md) for options. ### Property: DragDetector.Enabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Physics", "Input" ] } ``` If true, the [DragDetector](/docs/reference/engine/classes/DragDetector.md) responds to user input; if false, it does not. ### Property: DragDetector.GamepadModeSwitchKeyCode ```json { "type": "KeyCode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Mode Switching", "capabilities": [ "Physics", "Input" ] } ``` During gamepad input, the [KeyCode](/docs/reference/engine/enums/KeyCode.md) for toggling the secondary mode of motion. Only applies if the drag detector's [DragStyle](/docs/reference/engine/classes/DragDetector.md) has both primary and secondary modes of motion. ### Property: DragDetector.KeyboardModeSwitchKeyCode ```json { "type": "KeyCode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Mode Switching", "capabilities": [ "Physics", "Input" ] } ``` During keyboard input, the [KeyCode](/docs/reference/engine/enums/KeyCode.md) for toggling the secondary mode of motion. Only applies if the drag detector's [DragStyle](/docs/reference/engine/classes/DragDetector.md) has both primary and secondary modes of motion. ### Property: DragDetector.MaxDragAngle ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Drag Limits", "capabilities": [ "Physics", "Input" ] } ``` If this is greater than [MinDragAngle](/docs/reference/engine/classes/DragDetector.md), translation will be clamped within that range. This is not a constraint; it merely impedes the drag detector's attempts to generate motion in order to remain within limits. See [AddConstraintFunction()](/docs/reference/engine/classes/DragDetector.md) to add custom constraint to a drag. Only relevant if [DragStyle](/docs/reference/engine/classes/DragDetector.md) is [DragDetectorDragStyle.RotateAxis](/docs/reference/engine/enums/DragDetectorDragStyle.md). ### Property: DragDetector.MaxDragTranslation ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Drag Limits", "capabilities": [ "Physics", "Input" ] } ``` In any dimension, if this is greater than [MinDragTranslation](/docs/reference/engine/classes/DragDetector.md), translation will be clamped within that range. This is not a constraint; it merely impedes the drag detector's attempts to generate motion in order to remain within limits. See [AddConstraintFunction()](/docs/reference/engine/classes/DragDetector.md) to add custom constraint to a drag. ### Property: DragDetector.MaxForce ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Physics Response", "capabilities": [ "Physics", "Input" ] } ``` Maximum force applied for the object to reach its goal. Only relevant if [ResponseStyle](/docs/reference/engine/classes/DragDetector.md) is [DragDetectorResponseStyle.Physical](/docs/reference/engine/enums/DragDetectorResponseStyle.md) and the parent object is unanchored. ### Property: DragDetector.MaxTorque ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Physics Response", "capabilities": [ "Physics", "Input" ] } ``` Maximum torque applied for the object to reach its goal. Only relevant if [ResponseStyle](/docs/reference/engine/classes/DragDetector.md) is [DragDetectorResponseStyle.Physical](/docs/reference/engine/enums/DragDetectorResponseStyle.md) and the parent object is unanchored. ### Property: DragDetector.MinDragAngle ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Drag Limits", "capabilities": [ "Physics", "Input" ] } ``` If this is less than [MaxDragAngle](/docs/reference/engine/classes/DragDetector.md), translation will be clamped within that range. This is not a constraint; it merely impedes the drag detector's attempts to generate motion in order to remain within limits. See [AddConstraintFunction()](/docs/reference/engine/classes/DragDetector.md) to add custom constraint to a drag. Only relevant if [DragStyle](/docs/reference/engine/classes/DragDetector.md) is [DragDetectorDragStyle.RotateAxis](/docs/reference/engine/enums/DragDetectorDragStyle.md). ### Property: DragDetector.MinDragTranslation ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Drag Limits", "capabilities": [ "Physics", "Input" ] } ``` In any dimension, if this is less than [MaxDragTranslation](/docs/reference/engine/classes/DragDetector.md), translation will be clamped within that range. This is not a constraint; it merely impedes the drag detector's attempts to generate motion in order to remain within limits. See [AddConstraintFunction()](/docs/reference/engine/classes/DragDetector.md) to add custom constraint to a drag. ### Property: DragDetector.Orientation ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Drag Directions", "capabilities": [ "Physics", "Input" ] } ``` Specifies the **YXZ** rotation of axes of motion relative to the reference frame (does not change the orientation of the reference frame itself). Linear translation and axial rotation will be on this reoriented **Y** axis, and planar translation in the **XZ** plane. Changing this value automatically updates [Axis](/docs/reference/engine/classes/DragDetector.md) and vice versa. ### Property: DragDetector.PermissionPolicy ```json { "type": "DragDetectorPermissionPolicy", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Physics", "Input" ] } ``` Controls the permission level for which players can interact with the [DragDetector](/docs/reference/engine/classes/DragDetector.md). Default is [DragDetectorPermissionPolicy.Everybody](/docs/reference/engine/enums/DragDetectorPermissionPolicy.md). ### Property: DragDetector.ReferenceInstance ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Physics", "Input" ] } ``` An instance whose [CFrame](/docs/reference/engine/datatypes/CFrame.md) is the reference frame for the drag detector. The [DragFrame](/docs/reference/engine/classes/DragDetector.md) is expressed relative to this [CFrame](/docs/reference/engine/datatypes/CFrame.md) which may be retrieved via the [GetReferenceFrame()](/docs/reference/engine/classes/DragDetector.md) method. If this instance is a [PVInstance](/docs/reference/engine/classes/PVInstance.md), the reference frame will be its pivot; if an [Attachment](/docs/reference/engine/classes/Attachment.md), then its world [CFrame](/docs/reference/engine/datatypes/CFrame.md). If it is `nil` or neither of the former, the reference frame will be based on the pivot of the drag detector's parent [BasePart](/docs/reference/engine/classes/BasePart.md) or [Model](/docs/reference/engine/classes/Model.md). ### Property: DragDetector.ResponseStyle ```json { "type": "DragDetectorResponseStyle", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Physics", "Input" ] } ``` Once the proposed motion has been computed and potentially constrained, this is the paradigm used to move, or not move, the objects affected by the [DragDetector](/docs/reference/engine/classes/DragDetector.md). See [DragDetectorResponseStyle](/docs/reference/engine/enums/DragDetectorResponseStyle.md) for options. ### Property: DragDetector.Responsiveness ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Physics Response", "capabilities": [ "Physics", "Input" ] } ``` Higher values cause the object to reach its goal more rapidly. Only relevant if [ResponseStyle](/docs/reference/engine/classes/DragDetector.md) is [DragDetectorResponseStyle.Physical](/docs/reference/engine/enums/DragDetectorResponseStyle.md) and the parent object is unanchored. ### Property: DragDetector.RunLocally ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Physics", "Input" ] } ``` If false (default), the client sends replicated signals ([DragStart](/docs/reference/engine/classes/DragDetector.md), [DragContinue](/docs/reference/engine/classes/DragDetector.md), [DragEnd](/docs/reference/engine/classes/DragDetector.md)) to the server which processes cursor rays, makes changes to the data model, and replicates them onwards to clients. If true, the client processes those signals itself and does not replicate them to the server. Client [LocalScripts](/docs/reference/engine/classes/LocalScript.md) may be used to respond to these events and [RemoteEvents](/docs/reference/engine/classes/RemoteEvent.md) may be used to send any resulting changes that should be replicated to the server. ### Property: DragDetector.SecondaryAxis ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Drag Directions", "capabilities": [ "Physics", "Input" ] } ``` The secondary axis of the motion. Relates to orientation using the same paradigm as [Attachments](/docs/reference/engine/classes/Attachment.md). ### Property: DragDetector.TrackballRadialPullFactor ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Physics", "Input" ] } ``` When the cursor is outside the trackball, the [DragDetector](/docs/reference/engine/classes/DragDetector.md) can apply a radial pull rotation that turns the ball as if it were trying to roll out toward the cursor. This property is a 0 to 1 multiplier for adding that rotation as a contribution to the total. Only relevant if [DragStyle](/docs/reference/engine/classes/DragDetector.md) is [DragDetectorDragStyle.RotateTrackball](/docs/reference/engine/enums/DragDetectorDragStyle.md). ### Property: DragDetector.TrackballRollFactor ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Physics", "Input" ] } ``` When the cursor is outside the trackball, the [DragDetector](/docs/reference/engine/classes/DragDetector.md) can apply a roll rotation that turns the ball as if it were mounted on a vinyl record facing the viewer. This property is a 0 to 1 multiplier for adding that roll rotation to the total. Only relevant if [DragStyle](/docs/reference/engine/classes/DragDetector.md) is [DragDetectorDragStyle.RotateTrackball](/docs/reference/engine/enums/DragDetectorDragStyle.md). ### Property: DragDetector.VRSwitchKeyCode ```json { "type": "KeyCode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Mode Switching", "capabilities": [ "Physics", "Input" ] } ``` During VR input, the [KeyCode](/docs/reference/engine/enums/KeyCode.md) for toggling the secondary mode of motion. Only applies if the drag detector's [DragStyle](/docs/reference/engine/classes/DragDetector.md) has both primary and secondary modes of motion. ### Property: DragDetector.WorldAxis ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Drag Directions", "capabilities": [ "Physics", "Input" ] } ``` The [Axis](/docs/reference/engine/classes/DragDetector.md) expressed in world space. Relates to orientation using the same paradigm as [Attachments](/docs/reference/engine/classes/Attachment.md). ### Property: DragDetector.WorldSecondaryAxis ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Drag Directions", "capabilities": [ "Physics", "Input" ] } ``` The [SecondaryAxis](/docs/reference/engine/classes/DragDetector.md) expressed in world space. Relates to orientation using the same paradigm as [Attachments](/docs/reference/engine/classes/Attachment.md). ## Methods ### Method: DragDetector:AddConstraintFunction **Signature:** `DragDetector:AddConstraintFunction(priority: int, function: Function): RBXScriptConnection` Adds a function to modify or constrain proposed motion. The function takes an input [CFrame](/docs/reference/engine/datatypes/CFrame.md) of **proposed** motion and returns a [CFrame](/docs/reference/engine/datatypes/CFrame.md) of **modified** or unmodified motion. Both the input and output are expressed relative to the reference frame. You can add multiple functions which will be called in order by `priority`, passing the results along in a chain. To remove an added constraint function, call [Disconnect()](/docs/reference/engine/datatypes/RBXScriptConnection.md) on the returned connection object. *Security: None · Thread Safety: Unsafe · Capabilities: Physics, Input* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `priority` | `int` | | The order of priority for functions added via this method. Higher values take precedence over lower values. | | `function` | `Function` | | Function for modifying or constraining proposed motion. This function takes an input [CFrame](/docs/reference/engine/datatypes/CFrame.md) of **proposed** motion and returns a [CFrame](/docs/reference/engine/datatypes/CFrame.md) of **modified** or unmodified motion, both relative to the reference frame. | **Returns:** `RBXScriptConnection` — Use this connection object to remove the constraint function. ### Method: DragDetector:GetReferenceFrame **Signature:** `DragDetector:GetReferenceFrame(): CFrame` Returns the reference [CFrame](/docs/reference/engine/datatypes/CFrame.md) in which motion is expressed; see the [ReferenceInstance](/docs/reference/engine/classes/DragDetector.md) property for more details. *Security: None · Thread Safety: Unsafe · Capabilities: Physics, Input* **Returns:** `CFrame` — The reference [CFrame](/docs/reference/engine/datatypes/CFrame.md) in which motion is expressed. ### Method: DragDetector:RestartDrag **Signature:** `DragDetector:RestartDrag(): ()` May be invoked from a script to restart the drag using new parameters, if parameters such as [DragStyle](/docs/reference/engine/classes/DragDetector.md), [Axis](/docs/reference/engine/classes/DragDetector.md), or [SecondaryAxis](/docs/reference/engine/classes/DragDetector.md) change. *Security: None · Thread Safety: Unsafe · Capabilities: Physics, Input* **Returns:** `()` ### Method: DragDetector:SetDragStyleFunction **Signature:** `DragDetector:SetDragStyleFunction(function: Function): ()` Passes a function to be used if and only if [DragStyle](/docs/reference/engine/classes/DragDetector.md) is set to [DragDetectorDragStyle.Scriptable](/docs/reference/engine/enums/DragDetectorDragStyle.md). The given function is called when responding to a [DragContinue](/docs/reference/engine/classes/DragDetector.md) signal, it receives the signal's world space cursor ray with type [Ray](/docs/reference/engine/datatypes/Ray.md), and it returns a [CFrame](/docs/reference/engine/datatypes/CFrame.md) containing the desired location and orientation of the pivot in world space. If the function returns `nil`, the object will not be moved. This is useful if the script has not yet collected all the information it needs to give the correct answer, or in temporary cases where you want the object to stay where it is. *Security: None · Thread Safety: Unsafe · Capabilities: Physics, Input* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `function` | `Function` | | Function for monitoring [DragContinue](/docs/reference/engine/classes/DragDetector.md) signals. This function receives the signal's world space cursor ray and it returns a [CFrame](/docs/reference/engine/datatypes/CFrame.md) containing the desired location and orientation of the pivot in world space. If this function returns `nil`, the object will not be moved. | **Returns:** `()` ### Method: DragDetector:SetPermissionPolicyFunction **Signature:** `DragDetector:SetPermissionPolicyFunction(function: Function): ()` Passes a function to be used if and only if [PermissionPolicy](/docs/reference/engine/classes/DragDetector.md) is set to [DragDetectorPermissionPolicy.Scriptable](/docs/reference/engine/enums/DragDetectorPermissionPolicy.md). The given function accepts a [Player](/docs/reference/engine/classes/Player.md) parameter for enabling/disabling the detector for a specific player. It also receives a `part` parameter indicating which specific [BasePart](/docs/reference/engine/classes/BasePart.md) was clicked, such as one part within a draggable [Model](/docs/reference/engine/classes/Model.md); this is useful for enabling/disabling the detector based on that part's [Name](/docs/reference/engine/classes/Instance.md), [Color](/docs/reference/engine/classes/BasePart.md), [HasTag()](/docs/reference/engine/classes/Instance.md) value, or other details. ```lua local dragDetector = script.Parent.DragDetector dragDetector.PermissionPolicy = Enum.DragDetectorPermissionPolicy.Scriptable dragDetector:SetPermissionPolicyFunction(function(player, part) if player and player:GetAttribute("IsInTurn") then return true elseif part and not part:GetAttribute("IsDraggable") then return false else return true end end) ``` *Security: None · Thread Safety: Unsafe · Capabilities: Physics, Input* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `function` | `Function` | | Function for setting the detector's interactivity. This function accepts a [Player](/docs/reference/engine/classes/Player.md) parameter for enabling/disabling the detector for a specific player. It also receives a `part` parameter indicating which specific [BasePart](/docs/reference/engine/classes/BasePart.md) was clicked, such as one part within a draggable [Model](/docs/reference/engine/classes/Model.md); this is useful for enabling/disabling the detector based on that part's [Name](/docs/reference/engine/classes/Instance.md), [Color](/docs/reference/engine/classes/BasePart.md), [HasTag()](/docs/reference/engine/classes/Instance.md) value, or other details. | **Returns:** `()` ## Events ### Event: DragDetector.DragContinue **Signature:** `DragDetector.DragContinue(playerWhoDragged: Player, cursorRay: Ray, viewFrame: CFrame, vrInputFrame: OptionalCoordinateFrame, isModeSwitchKeyDown: boolean)` Fires when a user continues dragging the object after [DragStart](/docs/reference/engine/classes/DragDetector.md) has been initiated. *Security: None · Capabilities: Physics, Input* **Parameters:** | Name | Type | Description | |------|------|-------------| | `playerWhoDragged` | `Player` | The [Player](/docs/reference/engine/classes/Player.md) who initiated the drag through [DragStart](/docs/reference/engine/classes/DragDetector.md) and is now continuing the drag. | | `cursorRay` | `Ray` | [Ray](/docs/reference/engine/datatypes/Ray.md) emanating from the cursor, aimed into the scene. | | `viewFrame` | `CFrame` | [CFrame](/docs/reference/engine/datatypes/CFrame.md) of the user's [Camera](/docs/reference/engine/classes/Camera.md). | | `vrInputFrame` | `OptionalCoordinateFrame` | If using a VR input device, the [CFrame](/docs/reference/engine/datatypes/CFrame.md) of the hand holding the cursor/pointer/controller. | | `isModeSwitchKeyDown` | `boolean` | If the drag detector's [DragStyle](/docs/reference/engine/classes/DragDetector.md) has both primary and secondary modes of motion, this parameter indicates whether the user is pressing the modifier input defined through [KeyboardModeSwitchKeyCode](/docs/reference/engine/classes/DragDetector.md), [GamepadModeSwitchKeyCode](/docs/reference/engine/classes/DragDetector.md), or [VRSwitchKeyCode](/docs/reference/engine/classes/DragDetector.md). | ### Event: DragDetector.DragEnd **Signature:** `DragDetector.DragEnd(playerWhoDragged: Player)` Fires when a user stops dragging the object. *Security: None · Capabilities: Physics, Input* **Parameters:** | Name | Type | Description | |------|------|-------------| | `playerWhoDragged` | `Player` | The [Player](/docs/reference/engine/classes/Player.md) who initiated the drag through [DragStart](/docs/reference/engine/classes/DragDetector.md) and has now ended (released) the drag. | ### Event: DragDetector.DragStart **Signature:** `DragDetector.DragStart(playerWhoDragged: Player, cursorRay: Ray, viewFrame: CFrame, hitFrame: CFrame, clickedPart: BasePart, vrInputFrame: OptionalCoordinateFrame, isModeSwitchKeyDown: boolean)` Fires when a user starts dragging the object. *Security: None · Capabilities: Physics, Input* **Parameters:** | Name | Type | Description | |------|------|-------------| | `playerWhoDragged` | `Player` | [Player](/docs/reference/engine/classes/Player.md) who initiated the drag. | | `cursorRay` | `Ray` | [Ray](/docs/reference/engine/datatypes/Ray.md) emanating from the cursor, aimed into the scene. | | `viewFrame` | `CFrame` | [CFrame](/docs/reference/engine/datatypes/CFrame.md) of the user's [Camera](/docs/reference/engine/classes/Camera.md). | | `hitFrame` | `CFrame` | The hit frame of the cursor raycast that initiated the drag. | | `clickedPart` | `BasePart` | The part that was hit by the cursor raycast that initiated the drag. | | `vrInputFrame` | `OptionalCoordinateFrame` | If using a VR input device, the [CFrame](/docs/reference/engine/datatypes/CFrame.md) of the hand holding the cursor/pointer/controller. | | `isModeSwitchKeyDown` | `boolean` | If the drag detector's [DragStyle](/docs/reference/engine/classes/DragDetector.md) has both primary and secondary modes of motion, this parameter indicates whether the user is pressing the modifier input defined through [KeyboardModeSwitchKeyCode](/docs/reference/engine/classes/DragDetector.md), [GamepadModeSwitchKeyCode](/docs/reference/engine/classes/DragDetector.md), or [VRSwitchKeyCode](/docs/reference/engine/classes/DragDetector.md). | ## Inherited Members ### From [ClickDetector](/docs/reference/engine/classes/ClickDetector.md) - **Property `CursorIcon`** (`ContentId`): Sets the cursor icon to display when the mouse is hovered over the parent - **Property `CursorIconContent`** (`Content`): Sets the cursor icon to display when the mouse is hovered over the parent - **Property `MaxActivationDistance`** (`float`): Maximum distance between a character and the ClickDetector or - **Event `MouseClick`**: Fires when a player interacts with the parent of a ClickDetector - **Event `mouseClick`**: *(deprecated)* - **Event `MouseHoverEnter`**: Fires when the parent of a ClickDetector or DragDetector - **Event `MouseHoverLeave`**: Fires when a player's cursor hovers off the parent of a - **Event `RightMouseClick`**: Fires when a player right clicks their mouse cursor on a ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Dragger last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances summary: "A helper object used to create tools that can drag parts." --- # Class: Dragger > A helper object used to create tools that can drag parts. ## Description The **Dragger** object is a helper object used to create tools that can drag parts. It is expected (but not required) to be used with [Mouse](/docs/reference/engine/classes/Mouse.md) events. Its implementation is primarily used in the RbxStamper library. ## Methods ### Method: Dragger:AxisRotate **Signature:** `Dragger:AxisRotate(axis?: Axis): ()` Rotates the currently dragged part(s) by 90 degrees on the given axis. *Security: None · Thread Safety: Unsafe · Capabilities: Physics* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `axis` | `Axis` | `X` | | **Returns:** `()` ### Method: Dragger:MouseDown **Signature:** `Dragger:MouseDown(mousePart: Instance, pointOnMousePart: Vector3, parts: Instances): ()` Initializes a dragging action, specifying which parts to use when dragging. *Security: None · Thread Safety: Unsafe · Capabilities: Physics* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `mousePart` | `Instance` | | | | `pointOnMousePart` | `Vector3` | | | | `parts` | `Instances` | | | **Returns:** `()` ### Method: Dragger:MouseMove **Signature:** `Dragger:MouseMove(mouseRay: Ray): ()` Tries to move the currently dragged part to the point where MouseRay hits another part. *Security: None · Thread Safety: Unsafe · Capabilities: Physics* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `mouseRay` | `Ray` | | | **Returns:** `()` ### Method: Dragger:MouseUp **Signature:** `Dragger:MouseUp(): ()` Stops the current dragging action (made by [Dragger:MouseDown()](/docs/reference/engine/classes/Dragger.md)) *Security: None · Thread Safety: Unsafe · Capabilities: Physics* **Returns:** `()` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: DraggerService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated --- # Class: DraggerService ## Properties ### Property: DraggerService.AlignDraggedObjects ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: DraggerService.AngleSnapEnabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: DraggerService.AngleSnapIncrement ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: DraggerService.AnimateHover ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: DraggerService.CollisionsEnabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: DraggerService.DraggerCoordinateSpace ```json { "type": "DraggerCoordinateSpace", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: DraggerService.DraggerMovementMode ```json { "type": "DraggerMovementMode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: DraggerService.GeometrySnapColor ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: DraggerService.HoverAnimateFrequency ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: DraggerService.HoverThickness ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: DraggerService.JointsEnabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: DraggerService.LinearSnapEnabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: DraggerService.LinearSnapIncrement ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: DraggerService.PartSnapEnabled ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: DraggerService.PivotSnapToGeometry ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: DraggerService.ShowHover ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: DraggerService.ShowPivotIndicator ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: DynamicRotate last_updated: 2026-06-29T19:34:07Z inherits: - JointInstance - Instance - Object type: class memory_category: BaseParts tags: - NotCreatable summary: "The base class for classic motor joints." --- # Class: DynamicRotate > The base class for classic motor joints. ## Properties ### Property: DynamicRotate.BaseAngle ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Physics" ] } ``` The base angle of the DynamicRotate object, in radians. ## Inherited Members ### From [JointInstance](/docs/reference/engine/classes/JointInstance.md) - **Property `Active`** (`boolean`): Determines if the joint is currently active in the world. - **Property `C0`** (`CFrame`): Determines how the offset point is attached to - **Property `C1`** (`CFrame`): Subtracted from the C0 property to create an - **Property `Enabled`** (`boolean`): Sets whether the joint is active or not. - **Property `Part0`** (`BasePart`): The first BasePart that the joint connects. - **Property `Part1`** (`BasePart`): The second BasePart that the joint connects. - **Property `part1`** (`BasePart`): *(deprecated, hidden)* ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: EchoSoundEffect last_updated: 2026-06-29T19:34:07Z inherits: - SoundEffect - Instance - Object type: class memory_category: Instances summary: "Adds delayed repetitions of a sound with diminishing volume." --- # Class: EchoSoundEffect > Adds delayed repetitions of a sound with diminishing volume. ## Description An echo effect causes a sound to repeat on a delay with diminishing volume, simulating the real effect of an echo. This effect can be applied to either an individual sound or to a sound group by parenting it to the desired instance. The effect is controlled by several properties. First, the Delay is how long the effect will wait to play the echoed sound. Feedback determines how much the original signal is diminished to play as the echoed sound. Note that this echoed sound also goes through the echo effect which will wait another delay and play another echo. This process will repeat until the volume of the echoed sound is negligible. You can also adjust the wet/dry mix of the effect. The dry component of the sound is the original sound that the effect is being applied to. You can adjust the volume of the dry sound by adjusting the DryLevel. The wet sound is the echoed effect itself, and its volume can be adjusted with WetLevel. It is recommended to only use the EchoSoundEffect with sound groups. If an echo effect is applied to a regular Sound, once that sound stops playing the echo effect will also be cut off. When applied to a SoundGroup, the echo effect will continue playing even if the original source sound has stopped.Like all other [SoundEffect](/docs/reference/engine/classes/SoundEffect.md), a EchoSoundEffect can be applied either to a [Sound](/docs/reference/engine/classes/Sound.md) or [SoundGroup](/docs/reference/engine/classes/SoundGroup.md) by being parented to either. ## Properties ### Property: EchoSoundEffect.Delay ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "LegacySound" ] } ``` Range: 0.01 to 5 (default 1) The amount of time between echoes. Measured in seconds. If a EchoSoundEffect is applied to a singular sound instead of a sound group, if the sound stops playing before the delay causes an echo, the echo will not play. Because of this, it is recommended to apply echo effects to SoundGroups and not Sounds. ### Property: EchoSoundEffect.DryLevel ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "LegacySound" ] } ``` Range: -80 to 10 (default 0) The output volume of the original sound. ### Property: EchoSoundEffect.Feedback ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "LegacySound" ] } ``` Range: 0 to 1 (default 0.5) The echo decay every time the echo plays. Setting this at it's minimum (0) will cause no feedback, meaning the echo won't be audible at all. Setting this at the maximum (1) will have no decay, meaning the echoed sound will play at the level of the original and will not diminish over time. ### Property: EchoSoundEffect.WetLevel ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "LegacySound" ] } ``` Range: -80 to 10 (default 0) The output volume of the echoed effect. ## Inherited Members ### From [SoundEffect](/docs/reference/engine/classes/SoundEffect.md) - **Property `Enabled`** (`boolean`): Toggles the effect on and off. - **Property `Priority`** (`int`): Determines the order the effect will be applied in relation to other ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: EditableImage last_updated: 2026-06-29T19:34:07Z inherits: - Object type: class memory_category: Instances tags: - NotCreatable summary: "Object which allows for the runtime creation and manipulation of images." --- # Class: EditableImage > Object which allows for the runtime creation and manipulation of images. ## Description `EditableImage` allows for the runtime creation and manipulation of images. To create a blank `EditableImage`, use [AssetService:CreateEditableImage()](/docs/reference/engine/classes/AssetService.md). To create an `EditableImage` from an existing image, use [AssetService:CreateEditableImageAsync()](/docs/reference/engine/classes/AssetService.md). `EditableImage` can be used in any [Content](/docs/reference/engine/datatypes/Content.md) property which takes an image, such as [ImageLabel.ImageContent](/docs/reference/engine/classes/ImageLabel.md) or [MeshPart.TextureContent](/docs/reference/engine/classes/MeshPart.md). This is done by setting the content property to [Content.fromObject(editableImage)](/docs/reference/engine/datatypes/Content.md). The `EditableImage` coordinate system is relative to the top left of the image: - Top-left: `(0, 0)` - Bottom-right: `(Size.X - 1, Size.Y - 1)` When you use [AssetService:PromptCreateAssetAsync()](/docs/reference/engine/classes/AssetService.md) to publish an object that has a [Content](/docs/reference/engine/datatypes/Content.md) property which references an `EditableImage`, the editable image is published as an image and the property is set to a new asset ID. #### Update Limitations Only a single `EditableImage` can be updated per frame on the display side. For example, if you update three `EditableImage` objects which are currently being displayed, it will take three frames for all of them to be updated. #### Enabling for Published Experiences For security purposes, using `EditableImage` fails by default for published experiences. To enable usage, you must be 13+ age verified and ID verified. After you are verified, open Studio's [Experience Settings](/docs/en-us/studio/experience-settings.md), select **Security**, and enable the **Allow Mesh / Image APIs** toggle. #### Permissions To prevent misuse, [AssetService:CreateEditableImageAsync()](/docs/reference/engine/classes/AssetService.md) only allows you to load and edit image assets if any of the following is true: - Owned by or explicitly shared with the experience owner. - Owned by or explicitly shared with the logged in Studio user. - Owned by or explicitly shared with the logged in player if the `EditableImage` is on the client side. - Owned by a group where the experience owner, Studio user, or player has a role with permission to edit the group's assets. See [Roles and permissions](/docs/en-us/projects/groups.md#roles-and-permissions) for more information. See [Grant permissions](/docs/en-us/projects/assets/privacy.md#grant-permissions) to learn how to share assets with users or groups. The APIs throw an error if they are used to load an asset that does not meet the criteria above. #### Memory Limits Editable assets are currently expensive for memory usage. To minimize its impact on client performance, `EditableImage` has strict client-side memory budgets, although the server, Studio, and plugins operate with unlimited memory. Linking one [EditableImage](/docs/reference/engine/classes/EditableImage.md) to multiple image-related [Content](/docs/reference/engine/datatypes/Content.md) data types (multi-referencing) can help with memory optimization. ## Properties ### Property: EditableImage.Size ```json { "type": "Vector2", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "DynamicGeneration" ] } ``` Size of the `EditableImage` in pixels. The maximum size is 1024×1024. An `EditableImage` cannot be resized; this property is read-only. In order to resize or crop an image, create a new `EditableImage` and use [DrawImageTransformed()](/docs/reference/engine/classes/EditableImage.md) to transfer the contents; then call [Destroy()](/docs/reference/engine/classes/EditableImage.md). ## Methods ### Method: EditableImage:Destroy **Signature:** `EditableImage:Destroy(): ()` Destroys the contents of the image, immediately reclaiming used memory. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Returns:** `()` ### Method: EditableImage:DrawCircle **Signature:** `EditableImage:DrawCircle(center: Vector2, radius: int, color: Color3, transparency: float, combineType: ImageCombineType, antiAliasing?: AntiAliasing): ()` Draws a circle at the specified point on the `EditableImage`. If the circle is semi-transparent, it will be blended with the pixels behind it using source over blending. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `center` | `Vector2` | | Center of the circle, relative to the top-left corner of the `EditableImage`. Positions outside the canvas bounds are allowed. | | `radius` | `int` | | Radius of the circle in pixels. | | `color` | `Color3` | | Color of the circle. | | `transparency` | `float` | | Transparency of the circle with 0 being fully opaque and 1 being fully transparent. | | `combineType` | `ImageCombineType` | | How the drawn pixels (source) are combined with the existing pixels of this image (destination). | | `antiAliasing` | `AntiAliasing` | `Enabled` | Determines whether anti-aliasing is applied to the circle. When set to [AntiAliasing.Enabled](/docs/reference/engine/enums/AntiAliasing.md), circle edges are soft. When set to [AntiAliasing.Disabled](/docs/reference/engine/enums/AntiAliasing.md), circle edges are hard. | **Returns:** `()` ### Method: EditableImage:DrawImage **Signature:** `EditableImage:DrawImage(position: Vector2, image: EditableImage, combineType: ImageCombineType): ()` Draws another `EditableImage` into this `EditableImage` at the given position. Positions outside the canvas bounds are allowed such that only part of the new image is drawn. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `position` | `Vector2` | | Position at which the top-left corner of the source image will be drawn. | | `image` | `EditableImage` | | The source `EditableImage` to draw into this `EditableImage`. | | `combineType` | `ImageCombineType` | | How the pixels of the source image are combined with the existing pixels of this image (destination). | **Returns:** `()` ### Method: EditableImage:DrawImageProjected **Signature:** `EditableImage:DrawImageProjected(mesh: EditableMesh, projection: Dictionary, brushConfig: Dictionary): ()` Projects another `EditableImage` into an [EditableMesh](/docs/reference/engine/classes/EditableMesh.md) and stores the result on this `EditableImage` by using the specified projection and brush configuration. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `mesh` | `EditableMesh` | | The [EditableMesh](/docs/reference/engine/classes/EditableMesh.md) used to project into. | | `projection` | `Dictionary` | | Projection configuration dictionary including the following key-value pairs: - `Direction` ([Vector3](/docs/reference/engine/datatypes/Vector3.md)) where the projector is facing. - `Position` ([Vector3](/docs/reference/engine/datatypes/Vector3.md)) as the position in local space with respect to the mesh. - `Size` ([Vector3](/docs/reference/engine/datatypes/Vector3.md)) as the size of the projector. - `Up` ([Vector3](/docs/reference/engine/datatypes/Vector3.md)) as the up vector of the projector in local space with respect to the mesh. | | `brushConfig` | `Dictionary` | | Brush configuration dictionary including the following key-value pairs: - `AlphaBlendType` ([ImageAlphaType](/docs/reference/engine/enums/ImageAlphaType.md)) which determines how this projection will blend alpha values. - `ColorBlendType` ([ImageCombineType](/docs/reference/engine/enums/ImageCombineType.md)) which determines how this projection will blend color values. - `Decal` (`EditableImage`) as the image used for projection. - `FadeAngle` (number) as the angle in degrees for the projection edges to start to fall off. The projection will be fully faded out at 90 degrees. An angle of 0 means fading starts immediately at 0 degrees and an angle of 90 means no fading but instead a hard edge at 90 degrees. An angle of 70 degrees would mean the projection starts to fade at 70 degrees and is fully faded out at 90 degrees. - `BlendIntensity` (number) as the value between `0` and `1` which controls how much of the projection is blended into the resulting image. | **Returns:** `()` ### Method: EditableImage:DrawImageTransformed **Signature:** `EditableImage:DrawImageTransformed(position: Vector2, scale: Vector2, rotation: float, image: EditableImage, options: Dictionary?): ()` This method lets you draw an `EditableImage` into this `EditableImage` with transformations applied, such as scaling and rotation. The position parameter specifies where the pivot point of the source image will be placed on this image after transformations. Positions outside the canvas bounds are allowed such that only part of the new image is drawn. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `position` | `Vector2` | | Position in pixels where the pivot point of the source image will be placed on this image. | | `scale` | `Vector2` | | Scaling factors for the source image along the X and Y axes. | | `rotation` | `float` | | The rotation angle in degrees, applied around the pivot point of the source image. | | `image` | `EditableImage` | | The source `EditableImage` to be drawn into this image. | | `options` | `Dictionary?` | | Optional dictionary for additional configuration: - `CombineType`: Specifies how the pixels of the source image blend with those of the destination. Default is [ImageCombineType.AlphaBlend](/docs/reference/engine/enums/ImageCombineType.md). - `SamplingMode`: Specifies the sampling method (e.g. `Default` for bilinear or `Pixelated` for nearest neighbor). Default is [ResamplerMode.Default](/docs/reference/engine/enums/ResamplerMode.md). - `PivotPoint`: Specifies the pivot point within the source image for scaling and rotation. Default is the center of the source image (i.e. `Image.Size / 2`). | **Returns:** `()` **EditableImage:DrawImageTransformed()** The following code draws a rotated and scaled image onto another. ```lua local AssetService = game:GetService("AssetService") -- Example of drawing a rotated and scaled image onto another EditableImage local srcImage = AssetService:CreateEditableImage({ Size = Vector2.new(256, 256) }) local dstImage = AssetService:CreateEditableImage({ Size = Vector2.new(512, 512) }) -- Drawing with a rotation of 45 degrees, scaling by 2x, and placing at (100, 100) dstImage:DrawImageTransformed( Vector2.new(100, 100), -- Position Vector2.new(2, 2), -- Scale 45, -- Rotation (degrees) srcImage, -- Source image { CombineType = Enum.ImageCombineType.AlphaBlend, -- Optional, default is AlphaBlend SamplingMode = Enum.ResamplerMode.Default, -- Optional, default is Default PivotPoint = srcImage.Size / 2, -- Optional, default is center of the source image } ) ``` **EditableImage:DrawImageTransformed() - Crop** The following code shows how cropping an image can be done using the [EditableImage:DrawImageTransformed()](/docs/reference/engine/classes/EditableImage.md) method. ```lua local AssetService = game:GetService("AssetService") -- Source image local srcImage = AssetService:CreateEditableImageAsync(Content.fromUri(assetUri)) -- Crop area defined by offset and size local cropOffset = Vector2.new(50, 50) local cropSize = Vector2.new(100, 100) -- Destination image with size of the crop area local dstImage = AssetService:CreateEditableImage({ Size = cropSize }) -- Position (top-left corner) local position = Vector2.new(0, 0) -- Scale factors (no scaling) local scale = Vector2.new(1, 1) -- Rotation angle (no rotation) local rotation = 0 -- Draw the source image onto the destination image with adjusted pivot to crop the image dstImage:DrawImageTransformed(position, scale, rotation, srcImage, { CombineType = Enum.ImageCombineType.Overwrite, PivotPoint = cropOffset, -- Set pivot point to cropOffset to start drawing from there }) ``` ### Method: EditableImage:DrawLine **Signature:** `EditableImage:DrawLine(p1: Vector2, p2: Vector2, color: Color3, transparency: float, combineType: ImageCombineType, antiAliasing?: AntiAliasing): ()` Draws a line on the `EditableImage` one pixel thick between the two provided points. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `p1` | `Vector2` | | Start point of the line. | | `p2` | `Vector2` | | End point of the line. | | `color` | `Color3` | | Color of the line. | | `transparency` | `float` | | Transparency of the line. | | `combineType` | `ImageCombineType` | | How the drawn pixels (source) are combined with the existing pixels of this image (destination). | | `antiAliasing` | `AntiAliasing` | `Enabled` | Determines whether anti-aliasing is applied to the line. When set to [AntiAliasing.Enabled](/docs/reference/engine/enums/AntiAliasing.md), line edges are soft. When set to [AntiAliasing.Disabled](/docs/reference/engine/enums/AntiAliasing.md), line edges are hard. | **Returns:** `()` ### Method: EditableImage:DrawRectangle **Signature:** `EditableImage:DrawRectangle(position: Vector2, size: Vector2, color: Color3, transparency: float, combineType: ImageCombineType): ()` Draws a rectangle on the `EditableImage` of the given size at the given top-left position. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `position` | `Vector2` | | Position of the top-left of the rectangle. Unlike other drawing methods, this cannot be outside the canvas bounds of the `EditableImage`. | | `size` | `Vector2` | | Size of the rectangle to draw, in pixels. | | `color` | `Color3` | | Color of the rectangle. | | `transparency` | `float` | | Transparency of the rectangle. | | `combineType` | `ImageCombineType` | | How the drawn pixels (source) are combined with the existing pixels of this image (destination). | **Returns:** `()` ### Method: EditableImage:ReadPixelsBuffer **Signature:** `EditableImage:ReadPixelsBuffer(position: Vector2, size: Vector2): buffer` Reads a rectangular region of pixels from an `EditableImage` and returns it as a buffer. Each number in the buffer is a single byte, with pixels stored in a sequence of four bytes (red, green, blue, and alpha). Note that this method uses alpha instead of transparency, unlike the `EditableImage` drawing methods. *Security: None · Thread Safety: Safe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `position` | `Vector2` | | Top-left corner of the rectangular region of pixels to read. | | `size` | `Vector2` | | Size of the rectangular region of pixels to read. | **Returns:** `buffer` — Buffer where each pixel is represented by four bytes (red, green, blue and alpha respectively). The length of the buffer can be calculated as `Size.X * Size.Y * 4` bytes. **EditableImage:ReadPixelsBuffer()** The following code reads two pixels from a [EditableImage](/docs/reference/engine/classes/EditableImage.md) and creates a part with the average color between them. ```lua local AssetService = game:GetService("AssetService") local options = { Size = Vector2.new(32, 32) } local editableImage = AssetService:CreateEditableImage(options) local pixelsBuffer = editableImage:ReadPixelsBuffer(Vector2.zero, Vector2.new(2, 1)) local color1 = Color3.fromRGB(buffer.readu8(pixelsBuffer, 0), buffer.readu8(pixelsBuffer, 1), buffer.readu8(pixelsBuffer, 2)) local transparency1 = (255 - buffer.readu8(pixelsBuffer, 3)) / 255 local color2 = Color3.fromRGB(buffer.readu8(pixelsBuffer, 4), buffer.readu8(pixelsBuffer, 5), buffer.readu8(pixelsBuffer, 6)) local transparency2 = (255 - buffer.readu8(pixelsBuffer, 7)) / 255 local averageColor = color1:Lerp(color2, 0.5) local averageTransparency = (transparency1 + transparency2) / 2 local part = Instance.new("Part") part.Color = averageColor part.Transparency = averageTransparency part.Parent = workspace ``` ### Method: EditableImage:WritePixelsBuffer **Signature:** `EditableImage:WritePixelsBuffer(position: Vector2, size: Vector2, buffer: buffer): ()` Writes a rectangular region of pixels to an `EditableImage` from a buffer. Each number in the buffer is a single byte, with pixels stored in a sequence of four bytes (red, green, blue, and alpha). Note that this method uses alpha instead of transparency, unlike the `EditableImage` drawing methods. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `position` | `Vector2` | | Top-left corner of the rectangular region to draw the pixels into. | | `size` | `Vector2` | | Size of the rectangular region of pixels to write. | | `buffer` | `buffer` | | A buffer where each pixel is represented by four bytes (red, green, blue, and alpha respectively). The length of the buffer should be `Size.X * Size.Y * 4` bytes. | **Returns:** `()` **EditableImage:WritePixelsBuffer()** The following code reads the pixels of a [EditableImage](/docs/reference/engine/classes/EditableImage.md) and writes back the inverted color values of those pixels. ```lua local AssetService = game:GetService("AssetService") local options = { Size = Vector2.new(32, 32) } local editableImage = AssetService:CreateEditableImage(options) local pixelsBuffer = editableImage:ReadPixelsBuffer(Vector2.zero, editableImage.Size) for i = 1, editableImage.Size.X * editableImage.Size.Y do local pixelIndex = (i - 1) * 4 buffer.writeu8(pixelsBuffer, pixelIndex, 255 - buffer.readu8(pixelsBuffer, pixelIndex)) buffer.writeu8(pixelsBuffer, pixelIndex + 1, 255 - buffer.readu8(pixelsBuffer, pixelIndex + 1)) buffer.writeu8(pixelsBuffer, pixelIndex + 2, 255 - buffer.readu8(pixelsBuffer, pixelIndex + 2)) -- Set alpha to 255 to make all pixels fully opaque. buffer.writeu8(pixelsBuffer, pixelIndex + 3, 255) end editableImage:WritePixelsBuffer(Vector2.zero, editableImage.Size, pixelsBuffer) ``` ## Inherited Members ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: EditableMesh last_updated: 2026-06-29T19:34:07Z inherits: - Object type: class memory_category: Instances tags: - NotCreatable summary: "Object which allows for the runtime creation and manipulation of meshes." --- # Class: EditableMesh > Object which allows for the runtime creation and manipulation of meshes. ## Description `EditableMesh` changes the applied visual mesh when linked to a [MeshPart](/docs/reference/engine/classes/MeshPart.md), allowing for querying and modification of the mesh both in Studio and in experience. #### Enabling for Published Experiences For security purposes, using `EditableMesh` fails by default for published experiences. To enable usage of `EditableMesh`, you must be 13+ age verified and ID verified. After you are verified, open Studio's [Experience Settings](/docs/en-us/studio/experience-settings.md), select **Security**, and enable the **Allow Mesh & Image APIs** toggle. Remember to review the [Terms of Use](https://en.help.roblox.com/hc/en-us/articles/115004647846-Roblox-Terms-of-Use#creators-restrictions-on-use) before enabling the toggle. #### Permissions To prevent misuse, [AssetService:CreateEditableMeshAsync()](/docs/reference/engine/classes/AssetService.md) only allows you to load and edit mesh assets if any of the following is true: - Owned by or explicitly shared with the experience owner. - Owned by or explicitly shared with the logged in Studio user. - Owned by or explicitly shared with the logged in player if the `EditableMesh` is on the client side. - Owned by a group where the experience owner, Studio user, or player has a role with permission to edit the group's assets. See [Roles and permissions](/docs/en-us/projects/groups.md#roles-and-permissions) for more information. See [Grant permissions](/docs/en-us/projects/assets/privacy.md#grant-permissions) to learn how to share assets with users or groups. The APIs throw an error if they are used to load an asset that does not meet the criteria above. #### Memory Limits Editable assets are currently expensive for memory usage. To minimize its impact on client performance, `EditableMesh` has strict client-side memory budgets, although the server, Studio, and plugins operate with unlimited memory. Using [FixedSize](/docs/reference/engine/classes/EditableMesh.md) may help you stay within the memory budget and, in some scenarios, linking one `EditableMesh` to multiple [MeshParts](/docs/reference/engine/classes/MeshPart.md) (multi-referencing) can help with memory optimization. #### Creation and Display An `EditableMesh` can be created from an existing [Content](/docs/reference/engine/datatypes/Content.md) of a [MeshPart](/docs/reference/engine/classes/MeshPart.md) or a mesh ID using [AssetService:CreateEditableMeshAsync()](/docs/reference/engine/classes/AssetService.md), or a blank `EditableMesh` can be created with [AssetService:CreateEditableMesh()](/docs/reference/engine/classes/AssetService.md). It can then be displayed, modified, and its collision model updated. Not all of the steps are necessary; for example, you might want to create an `EditableMesh` just to raycast without ever displaying it. ```lua local AssetService = game:GetService("AssetService") -- Create empty EditableMesh local editableMesh = AssetService:CreateEditableMesh() -- Create EditableMesh from asset ID local editableMeshFromAsset = nil local success, errorMessage = pcall(function() editableMeshFromAsset = AssetService:CreateEditableMeshAsync(Content.fromAssetId(ASSET_ID)) end) -- Create EditableMesh from another EditableMesh local editableMeshFromAnother = nil local success, errorMessage = pcall(function() editableMeshFromAnother = AssetService:CreateEditableMeshAsync(Content.fromObject(OTHER_EDITABLE_MESH)) end) -- Create EditableMesh from MeshPart local editableMeshFromMeshPart = nil local success, errorMessage = pcall(function() editableMeshFromMeshPart = AssetService:CreateEditableMeshAsync(MESH_PART.MeshContent) end) ``` An `EditableMesh` is displayed when it's linked to a new [MeshPart](/docs/reference/engine/classes/MeshPart.md), through [AssetService:CreateMeshPartAsync()](/docs/reference/engine/classes/AssetService.md). You can create more [MeshPart](/docs/reference/engine/classes/MeshPart.md) instances that reference the same `EditableMesh` [Content](/docs/reference/engine/datatypes/Content.md), or link to an existing [MeshPart](/docs/reference/engine/classes/MeshPart.md) through [MeshPart:ApplyMesh()](/docs/reference/engine/classes/MeshPart.md). ```lua local AssetService = game:GetService("AssetService") local Workspace = game:GetService("Workspace") -- Create EditableMesh from asset ID local editableMeshFromAsset = nil local success, errorMessage = pcall(function() editableMeshFromAsset = AssetService:CreateEditableMeshAsync(Content.fromAssetId(ASSET_ID)) end) -- Create new MeshPart linked to the EditableMesh local newMeshPart = nil local success, errorMessage = pcall(function() newMeshPart = AssetService:CreateMeshPartAsync(Content.fromObject(editableMeshFromAsset)) end) -- Alternatively, link the new MeshPart created above to an existing MeshPart local existingMeshPart = Workspace:FindFirstChild("EXISTING_MESH_PART") existingMeshPart:ApplyMesh(newMeshPart) ``` To recalculate collision and fluid geometry after editing, you can again call [AssetService:CreateMeshPartAsync()](/docs/reference/engine/classes/AssetService.md) and [MeshPart:ApplyMesh()](/docs/reference/engine/classes/MeshPart.md) to update an existing [MeshPart](/docs/reference/engine/classes/MeshPart.md). It's generally recommended to do this at the end of a conceptual edit, not after individual calls to methods that manipulate geometry. Visual changes to the mesh will always be immediately reflected by the engine, without the need to call [AssetService:CreateMeshPartAsync()](/docs/reference/engine/classes/AssetService.md). #### Fixed-Size Meshes When creating an `EditableMesh` from an existing mesh asset (via [AssetService:CreateEditableMeshAsync()](/docs/reference/engine/classes/AssetService.md)), the resulting editable mesh is fixed-size by default. Fixed-size meshes are more efficient in terms of memory but you cannot change the number of vertices, faces, or attributes. Only the values of vertex attributes and positions can be edited. ```lua local AssetService = game:GetService("AssetService") -- Create EditableMesh without fixed-size default local editableMeshFromAsset = nil local success, errorMessage = pcall(function() editableMeshFromAsset = AssetService:CreateEditableMeshAsync(Content.fromAssetId(ASSET_ID), {FixedSize = false}) end) ``` #### Stable Vertex/Face IDs Many `EditableMesh` methods take **vertex**, **normal**, **UV**, **color** and **face** IDs. These are represented as integers in Luau but they require some special handling. The main difference is that IDs are stable and they remain the same even if other parts of the mesh change. For example, if an `EditableMesh` has five vertices `{1, 2, 3, 4, 5}` and you remove vertex `4`, the new vertices will be `{1, 2, 3, 5}`. Note that the IDs are not guaranteed to be in order and there may be holes in the numbering, so when iterating through vertices or faces, you should iterate through the table returned by [GetVertices()](/docs/reference/engine/classes/EditableMesh.md) or [GetFaces()](/docs/reference/engine/classes/EditableMesh.md). #### Split Vertex Attributes A **vertex** is a corner of a face, and topologically connects faces together. Vertices can have several attributes: position, normal, UV coordinate, color, and transparency. Sometimes it's useful for all faces that touch a vertex to use the same attribute values, but sometimes you'll want different faces to use different attribute values on the same vertex. For example, on a smooth sphere, each vertex will only have a single normal. In contrast, at the corner of a cube, the vertex will have 3 different normals (one for each adjacent face). You can also have seams in the UV coordinates or sharp changes in the vertex colors. When creating faces, every vertex will by default have one of each attribute: one normal, one UV coordinate, and one color/transparency. If you want to create a seam, you should create new attributes and set them on the face. For example, this code will create a sharp cube: ```lua local AssetService = game:GetService("AssetService") -- Given 4 vertex IDs, adds a new normal and 2 triangles, making a sharp quad local function addSharpQuad(editableMesh, vid0, vid1, vid2, vid3) local nid = editableMesh:AddNormal() -- This creates a normal ID which is automatically computed local fid1 = editableMesh:AddTriangle(vid0, vid1, vid2) editableMesh:SetFaceNormals(fid1, {nid, nid, nid}) local fid2 = editableMesh:AddTriangle(vid0, vid2, vid3) editableMesh:SetFaceNormals(fid2, {nid, nid, nid}) end -- Makes a cube with creased edges between the 6 sides local function makeSharpCube() local editableMesh = AssetService:CreateEditableMesh() local v1 = editableMesh:AddVertex(Vector3.new(0, 0, 0)) local v2 = editableMesh:AddVertex(Vector3.new(1, 0, 0)) local v3 = editableMesh:AddVertex(Vector3.new(0, 1, 0)) local v4 = editableMesh:AddVertex(Vector3.new(1, 1, 0)) local v5 = editableMesh:AddVertex(Vector3.new(0, 0, 1)) local v6 = editableMesh:AddVertex(Vector3.new(1, 0, 1)) local v7 = editableMesh:AddVertex(Vector3.new(0, 1, 1)) local v8 = editableMesh:AddVertex(Vector3.new(1, 1, 1)) addSharpQuad(editableMesh, v5, v6, v8, v7) -- Front addSharpQuad(editableMesh, v1, v3, v4, v2) -- Back addSharpQuad(editableMesh, v1, v5, v7, v3) -- Left addSharpQuad(editableMesh, v2, v4, v8, v6) -- Right addSharpQuad(editableMesh, v1, v2, v6, v5) -- Bottom addSharpQuad(editableMesh, v3, v7, v8, v4) -- Top editableMesh:RemoveUnused() return editableMesh end ``` #### Winding Mesh faces have a front side and a back side. When drawing meshes, only the front of the faces are drawn by default, although you can change this by setting the mesh' [DoubleSided](/docs/reference/engine/classes/MeshPart.md) property to `true`. The order of the vertices around the face determines whether you are looking at the front or the back. The front of the face is visible when the vertices go counterclockwise around it. ![Order of the vertices around the face](../../../assets/engine-api/classes/EditableMesh/Winding.png) #### FACS Poses Animatable heads use the Facial Action Coding System (FACS). See the [FACS poses reference](/docs/en-us/avatar/dynamic-heads/facs-poses-reference.md) for helpful information when using [GetFacsPoses()](/docs/reference/engine/classes/EditableMesh.md) and similar methods. Each FACS pose is specified by an [FacsActionUnit](/docs/reference/engine/enums/FacsActionUnit.md) value. For the FACS pose, virtual bones can each have a [CFrame](/docs/reference/engine/datatypes/CFrame.md) that transforms the bones' initial [CFrame](/docs/reference/engine/datatypes/CFrame.md) in the bind pose of the mesh into the [CFrame](/docs/reference/engine/datatypes/CFrame.md) for that FACS action unit's pose. All bone [CFrames](/docs/reference/engine/datatypes/CFrame.md) are in the mesh's local space. These FACS poses are blended together during animation. Sometimes, the blending of the base poses produces poor results. In those cases, you can override the blending of specific combinations of base poses with a [corrective pose](/docs/en-us/art/characters/facial-animation/create-basic-heads.md#combination-poses) that is more pleasing. A corrective pose is specified by 2 or 3 [FacsActionUnit](/docs/reference/engine/enums/FacsActionUnit.md) values. Like a base FACS pose, for a corrective pose, virtual bones can each have a [CFrame](/docs/reference/engine/datatypes/CFrame.md) that transforms the bones' initial [CFrame](/docs/reference/engine/datatypes/CFrame.md) in the bind pose of the mesh into the [CFrame](/docs/reference/engine/datatypes/CFrame.md) for that FACS corrective. Please note that corrective poses that operate on both left and right action units are not currently expressible. For example, using `LeftCheekPuff` and `RightEyeClosed` together in a corrective pose is not currently possible. #### Limitations `EditableMesh` currently has a limit of 60,000 vertices and 20,000 triangles. Attempting to add too many vertices or triangles will cause an error. ## Properties ### Property: EditableMesh.FixedSize ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "None", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "DynamicGeneration" ] } ``` Fixed-sized meshes allow changing the values of vertex attributes but do not allow vertices and triangles to be added or deleted. ## Methods ### Method: EditableMesh:AddBone **Signature:** `EditableMesh:AddBone(boneProperties: Dictionary): int64` Adds a new bone and returns a stable bone ID. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `boneProperties` | `Dictionary` | | Options table containing bone parameters: - `Name` — A string that specifies the bone name. Note that all bone names in a mesh must be unique. - `ParentId` — Optional bone ID of the new bone's parent. - `CFrame` — Initial [CFrame](/docs/reference/engine/datatypes/CFrame.md) of the bone in the bind pose of the mesh, in the mesh's local space. - `Virtual` — Boolean that specifies whether this bone is virtual. Virtual bones can only be bound to a [FaceControls](/docs/reference/engine/classes/FaceControls.md) instance. | **Returns:** `int64` — Stable bone ID of the new bone. ### Method: EditableMesh:AddColor **Signature:** `EditableMesh:AddColor(color: Color3, alpha: float): int64` Adds a new color to the geometry and returns a stable color ID. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `color` | `Color3` | | The new color. | | `alpha` | `float` | | The color alpha (transparency). | **Returns:** `int64` — Stable color ID of the new color. ### Method: EditableMesh:AddNormal **Signature:** `EditableMesh:AddNormal(normal: Vector3?): int64` Adds a new normal to the geometry and returns a stable normal ID. If the normal value isn't specified, the normal will be automatically calculated. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `normal` | `Vector3?` | | The normal vector. If the normal value isn't specified, the normal will be automatically calculated. | **Returns:** `int64` — Stable normal ID of the new normal. ### Method: EditableMesh:AddTriangle **Signature:** `EditableMesh:AddTriangle(vertexId0: int64, vertexId1: int64, vertexId2: int64): int64` Adds a new triangle to the mesh and returns a stable face ID. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `vertexId0` | `int64` | | ID of the first vertex of the triangle. | | `vertexId1` | `int64` | | ID of the second vertex of the triangle. | | `vertexId2` | `int64` | | ID of the third vertex of the triangle. | **Returns:** `int64` — Stable face ID of the new face. ### Method: EditableMesh:AddUV **Signature:** `EditableMesh:AddUV(uv: Vector2): int64` Adds a new UV to the geometry and returns a stable UV ID. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `uv` | `Vector2` | | The new UV coordinate. | **Returns:** `int64` — Stable UV ID of the new UV. ### Method: EditableMesh:AddVertex **Signature:** `EditableMesh:AddVertex(p: Vector3): int64` Adds a new vertex to the geometry and returns a stable vertex ID. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `p` | `Vector3` | | Position in the mesh's local object space. | **Returns:** `int64` — Stable vertex ID of the new vertex. ### Method: EditableMesh:Destroy **Signature:** `EditableMesh:Destroy(): ()` Destroys the contents of the mesh, immediately reclaiming used memory. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Returns:** `()` ### Method: EditableMesh:FindClosestPointOnSurface **Signature:** `EditableMesh:FindClosestPointOnSurface(point: Vector3): Tuple` Finds the closest point on the mesh's surface. Returns the face ID, point on the mesh in local object space, and the barycentric coordinate of the position within the face. See [RaycastLocal()](/docs/reference/engine/classes/EditableMesh.md) for more information on barycentric coordinates. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `point` | `Vector3` | | Point position in the mesh's local object space. | **Returns:** `Tuple` — Tuple of the face ID, point on the mesh in local object space, and the barycentric coordinate of the position within the face. ### Method: EditableMesh:FindClosestVertex **Signature:** `EditableMesh:FindClosestVertex(toThisPoint: Vector3): int64` Finds the closest vertex to a specific point in space and returns a stable vertex ID. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `toThisPoint` | `Vector3` | | Point position in the mesh's local object space. | **Returns:** `int64` — Closest stable vertex ID to the specified point in space. ### Method: EditableMesh:FindVerticesWithinSphere **Signature:** `EditableMesh:FindVerticesWithinSphere(center: Vector3, radius: float): Array` Finds all vertices within a specific sphere and returns a list of stable vertex IDs. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `center` | `Vector3` | | Center of the sphere in the mesh's local object space. | | `radius` | `float` | | Radius of the sphere. | **Returns:** `Array` — List of stable vertex IDs within the requested sphere. ### Method: EditableMesh:GetAdjacentFaces **Signature:** `EditableMesh:GetAdjacentFaces(faceId: int64): Array` Given a stable face ID, returns a list of adjacent faces. ![Adjacent faces indicated around requested face](../../../assets/engine-api/classes/EditableMesh/GetAdjacentTriangles.png) *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `faceId` | `int64` | | | **Returns:** `Array` — List of face IDs adjacent to the given face. ### Method: EditableMesh:GetAdjacentVertices **Signature:** `EditableMesh:GetAdjacentVertices(vertexId: int64): Array` Given a stable vertex ID, returns a list of adjacent vertices. ![Adjacent vertices indicated around requested vertex](../../../assets/engine-api/classes/EditableMesh/GetAdjacentVertices.png) *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `vertexId` | `int64` | | Vertex ID around which to get adjacent vertices. | **Returns:** `Array` — List of IDs of adjacent vertices around the given vertex ID. ### Method: EditableMesh:GetBoneByName **Signature:** `EditableMesh:GetBoneByName(boneName: string): int64` Finds the bone ID of the bone with the given name. Errors if no bone with that name exists. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `boneName` | `string` | | Bone name to search for. | **Returns:** `int64` — Bone ID of the bone with the given name. ### Method: EditableMesh:GetBoneCFrame **Signature:** `EditableMesh:GetBoneCFrame(boneId: int64): CFrame` Returns the initial [CFrame](/docs/reference/engine/datatypes/CFrame.md) of the bone in the bind pose of the mesh, in the mesh's local space. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `boneId` | `int64` | | Bone ID for which to get the [CFrame](/docs/reference/engine/datatypes/CFrame.md). | **Returns:** `CFrame` — Initial [CFrame](/docs/reference/engine/datatypes/CFrame.md) of the bone in the bind pose of the mesh, in the mesh's local space. ### Method: EditableMesh:GetBoneIsVirtual **Signature:** `EditableMesh:GetBoneIsVirtual(boneId: int64): boolean` Returns `true` if the bone is virtual. Virtual bones can only be bound to a [FaceControls](/docs/reference/engine/classes/FaceControls.md) instance. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `boneId` | `int64` | | Bone ID for which to get whether the bone is virtual. | **Returns:** `boolean` — Whether the bone with the given bone ID is virtual. Virtual bones can only be bound to a [FaceControls](/docs/reference/engine/classes/FaceControls.md) instance. ### Method: EditableMesh:GetBoneName **Signature:** `EditableMesh:GetBoneName(boneId: int64): string` Returns the bone name. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `boneId` | `int64` | | Bone ID for which to get the name. | **Returns:** `string` — Name of the bone with the given bone ID. ### Method: EditableMesh:GetBoneParent **Signature:** `EditableMesh:GetBoneParent(boneId: int64): int64` Returns the parent bone ID, if any. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `boneId` | `int64` | | Bone ID for which to get the parent. | **Returns:** `int64` — Bone ID for the parent of the bone with the given bone ID. If there is no parent, returns `0`. ### Method: EditableMesh:GetBones **Signature:** `EditableMesh:GetBones(): Array` Returns all bones of the mesh as a list of stable bone IDs. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Returns:** `Array` — List of stable bone IDs. ### Method: EditableMesh:GetCenter **Signature:** `EditableMesh:GetCenter(): Vector3` *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Returns:** `Vector3` — Center of the bounding box of the `EditableMesh`. ### Method: EditableMesh:GetColor **Signature:** `EditableMesh:GetColor(colorId: int64): Color3?` Returns the color for the given color ID. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `colorId` | `int64` | | Color ID for which to get the color. | **Returns:** `Color3?` — Color for the requested stable color ID. ### Method: EditableMesh:GetColorAlpha **Signature:** `EditableMesh:GetColorAlpha(colorId: int64): float?` Returns the color alpha (transparency) at the given stable color ID. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `colorId` | `int64` | | Color ID for which to get the alpha. | **Returns:** `float?` — Color alpha at the request stable color ID. ### Method: EditableMesh:GetColors **Signature:** `EditableMesh:GetColors(): Array` Returns all colors of the mesh as a list of stable color IDs. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Returns:** `Array` — List of stable color IDs. ### Method: EditableMesh:GetFaceColors **Signature:** `EditableMesh:GetFaceColors(faceId: int64): Array` Returns the face's color IDs for the vertices on the face. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `faceId` | `int64` | | Face ID for which to get the color IDs. | **Returns:** `Array` — List of color IDs used for the vertices on the given face. ### Method: EditableMesh:GetFaceNormals **Signature:** `EditableMesh:GetFaceNormals(faceId: int64): Array` Returns the face's normal IDs for the vertices on the face. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `faceId` | `int64` | | Face ID for which to get the normal IDs. | **Returns:** `Array` — List of normal IDs used for the vertices on the given face. ### Method: EditableMesh:GetFaces **Signature:** `EditableMesh:GetFaces(): Array` Returns all faces of the mesh as a list of stable face IDs. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Returns:** `Array` — List of stable face IDs. ### Method: EditableMesh:GetFacesWithColor **Signature:** `EditableMesh:GetFacesWithColor(colorId: int64): Array` Returns an array of face IDs that use the given color ID. Use together with [GetVerticesWithColor()](/docs/reference/engine/classes/EditableMesh.md) to obtain all face/vertex pairs. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `colorId` | `int64` | | Color ID to find faces for. | **Returns:** `Array` — List of face IDs that use the provided color ID. ### Method: EditableMesh:GetFacesWithNormal **Signature:** `EditableMesh:GetFacesWithNormal(normalId: int64): Array` Returns an array of face IDs that use the given normal ID. Use together with [GetVerticesWithNormal()](/docs/reference/engine/classes/EditableMesh.md) to obtain all face/vertex pairs. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `normalId` | `int64` | | Normal ID to find faces for. | **Returns:** `Array` — List of face IDs that use the provided normal ID. ### Method: EditableMesh:GetFacesWithUV **Signature:** `EditableMesh:GetFacesWithUV(uvId: int64): Array` Returns an array of face IDs that use the given UV ID. Use together with [GetVerticesWithUV()](/docs/reference/engine/classes/EditableMesh.md) to obtain all face/vertex pairs. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `uvId` | `int64` | | UV ID to find faces for. | **Returns:** `Array` — List of face IDs that use the provided UV ID. ### Method: EditableMesh:GetFaceUVs **Signature:** `EditableMesh:GetFaceUVs(faceId: int64): Array` Returns the face's UV IDs for the vertices on the face. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `faceId` | `int64` | | Face ID for which to get the UV IDs. | **Returns:** `Array` — List of UV IDs used for the vertices on the given face. ### Method: EditableMesh:GetFaceVertices **Signature:** `EditableMesh:GetFaceVertices(faceId: int64): Array` Returns the face's vertex IDs. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `faceId` | `int64` | | | **Returns:** `Array` — List of vertex IDs around the given face. ### Method: EditableMesh:GetFacsCorrectivePose **Signature:** `EditableMesh:GetFacsCorrectivePose(actions: Array): Tuple` Returns bone IDs and bone [CFrames](/docs/reference/engine/datatypes/CFrame.md) for all bones in a specific FACS corrective pose. Each bone [CFrame](/docs/reference/engine/datatypes/CFrame.md) transforms the bone from the initial bone [CFrame](/docs/reference/engine/datatypes/CFrame.md) in the bind pose of the mesh to the combined bone [CFrame](/docs/reference/engine/datatypes/CFrame.md) for this pose. All [CFrames](/docs/reference/engine/datatypes/CFrame.md) are in the mesh's local space. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `actions` | `Array` | | Array or 2 or 3 [FacsActionUnit](/docs/reference/engine/enums/FacsActionUnit.md) values that specify a corrective pose. | **Returns:** `Tuple` — Array of bone IDs and corresponding array of bone [CFrames](/docs/reference/engine/datatypes/CFrame.md). ### Method: EditableMesh:GetFacsCorrectivePoses **Signature:** `EditableMesh:GetFacsCorrectivePoses(): Array` Returns all FACS corrective poses that are in use. Each corrective pose is specified by 2 or 3 [FacsActionUnit](/docs/reference/engine/enums/FacsActionUnit.md) values. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Returns:** `Array` — Array of corrective poses. Each corrective pose is specified by a small array of 2 or 3 [FacsActionUnit](/docs/reference/engine/enums/FacsActionUnit.md) values. ### Method: EditableMesh:GetFacsPose **Signature:** `EditableMesh:GetFacsPose(action: FacsActionUnit): Tuple` Returns bone IDs and bone [CFrames](/docs/reference/engine/datatypes/CFrame.md) for all bones in a specific FACS action unit. Each bone [CFrame](/docs/reference/engine/datatypes/CFrame.md) transforms the bone from the initial bone [CFrame](/docs/reference/engine/datatypes/CFrame.md) in the bind pose of the mesh to the combined bone [CFrame](/docs/reference/engine/datatypes/CFrame.md) for this pose. All [CFrames](/docs/reference/engine/datatypes/CFrame.md) are in the mesh's local space. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `action` | `FacsActionUnit` | | FACS action unit for which to get the pose. | **Returns:** `Tuple` — Array of bone IDs and corresponding array of bone [CFrame](/docs/reference/engine/datatypes/CFrame.md). ### Method: EditableMesh:GetFacsPoses **Signature:** `EditableMesh:GetFacsPoses(): Array` Returns all FACS action units that have poses defined. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Returns:** `Array` — Array of [FacsActionUnit](/docs/reference/engine/enums/FacsActionUnit.md), one for each FACS action unit that has a pose defined. ### Method: EditableMesh:GetNormal **Signature:** `EditableMesh:GetNormal(normalId: int64): Vector3?` Returns the normal vector for the given normal ID. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `normalId` | `int64` | | Normal ID for which to get the normal vector. | **Returns:** `Vector3?` — Normal vector at the requested normal ID. ### Method: EditableMesh:GetNormals **Signature:** `EditableMesh:GetNormals(): Array` Returns all normals of the mesh as a list of stable normal IDs. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Returns:** `Array` — List of stable normal IDs. ### Method: EditableMesh:GetPosition **Signature:** `EditableMesh:GetPosition(vertexId: int64): Vector3` Gets the position of a vertex in the mesh's local object space. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `vertexId` | `int64` | | Stable vertex ID for which to get the position. | **Returns:** `Vector3` — Position of a vertex in the mesh's local object space. ### Method: EditableMesh:GetSize **Signature:** `EditableMesh:GetSize(): Vector3` *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Returns:** `Vector3` — Size of the `EditableMesh`. ### Method: EditableMesh:GetUV **Signature:** `EditableMesh:GetUV(uvId: int64): Vector2?` Returns UV coordinates at the given UV ID. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `uvId` | `int64` | | UV ID for which to get the UV coordinate. | **Returns:** `Vector2?` — UV coordinates at the requested UV ID. ### Method: EditableMesh:GetUVs **Signature:** `EditableMesh:GetUVs(): Array` Returns all UVs of the mesh as a list of stable UV IDs. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Returns:** `Array` — List of stable UV IDs. ### Method: EditableMesh:GetVertexBones **Signature:** `EditableMesh:GetVertexBones(vertexId: int64): Array` Returns all bone IDs that are associated with the vertex for skinning. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `vertexId` | `int64` | | Vertex ID for which to get the associated bones. | **Returns:** `Array` — Bone IDs associated with the vertex for skinning. ### Method: EditableMesh:GetVertexBoneWeights **Signature:** `EditableMesh:GetVertexBoneWeights(vertexId: int64): Array` Returns skinning blend weights for each bone that is associated with the vertex. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `vertexId` | `int64` | | Vertex ID for which to get the associated bone weights. | **Returns:** `Array` — Skinning blend weights for each bone that is associated with the vertex. ### Method: EditableMesh:GetVertexColors **Signature:** `EditableMesh:GetVertexColors(vertexId: int64): Array` Returns the color IDs of the faces attached to the given vertex. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `vertexId` | `int64` | | Stable vertex ID to find color IDs. | **Returns:** `Array` — Array of color IDs of faces attached to the given vertex. ### Method: EditableMesh:GetVertexFaceColor **Signature:** `EditableMesh:GetVertexFaceColor(vertexId: int64, faceId: int64): int64` Returns the color ID of a vertex/face pair. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `vertexId` | `int64` | | Stable vertex ID. | | `faceId` | `int64` | | Stable face ID. | **Returns:** `int64` — Stable color ID of the vertex/face pair. ### Method: EditableMesh:GetVertexFaceNormal **Signature:** `EditableMesh:GetVertexFaceNormal(vertexId: int64, faceId: int64): int64` Returns the normal ID of a vertex/face pair. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `vertexId` | `int64` | | Stable vertex ID. | | `faceId` | `int64` | | Stable face ID. | **Returns:** `int64` — Stable normal ID of the vertex/face pair. ### Method: EditableMesh:GetVertexFaces **Signature:** `EditableMesh:GetVertexFaces(vertexId: int64): Array` Returns the face IDs of the faces attached to the given vertex. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `vertexId` | `int64` | | Stable vertex ID to find faces for. | **Returns:** `Array` — Array of face IDs attached to the given vertex. ### Method: EditableMesh:GetVertexFaceUV **Signature:** `EditableMesh:GetVertexFaceUV(vertexId: int64, faceId: int64): int64` Returns the UV ID of a vertex/face pair. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `vertexId` | `int64` | | Stable vertex ID. | | `faceId` | `int64` | | Stable face ID. | **Returns:** `int64` — Stable UV ID of the vertex/face pair. ### Method: EditableMesh:GetVertexNormals **Signature:** `EditableMesh:GetVertexNormals(vertexId: int64): Array` Returns the normal IDs of the faces attached to the given vertex. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `vertexId` | `int64` | | Stable vertex ID to find normal IDs. | **Returns:** `Array` — Array of normal IDs of faces attached to the given vertex. ### Method: EditableMesh:GetVertexUVs **Signature:** `EditableMesh:GetVertexUVs(vertexId: int64): Array` Returns the UV IDs of the faces attached to the given vertex. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `vertexId` | `int64` | | Stable vertex ID to find UV IDs. | **Returns:** `Array` — Array of UV IDs of faces attached to the given vertex. ### Method: EditableMesh:GetVertices **Signature:** `EditableMesh:GetVertices(): Array` Returns all vertices as a list of stable vertex IDs. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Returns:** `Array` — List of stable vertex IDs. ### Method: EditableMesh:GetVerticesWithColor **Signature:** `EditableMesh:GetVerticesWithColor(colorId: int64): Array` Returns an array of vertex IDs that use the given color ID. Use together with [GetFacesWithColor()](/docs/reference/engine/classes/EditableMesh.md) to obtain all face/vertex pairs. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `colorId` | `int64` | | Color ID to find faces for. | **Returns:** `Array` — List of face IDs that use the provided color ID. ### Method: EditableMesh:GetVerticesWithNormal **Signature:** `EditableMesh:GetVerticesWithNormal(normalId: int64): Array` Returns an array of vertex IDs that use the given normal ID. Use together with [GetFacesWithNormal()](/docs/reference/engine/classes/EditableMesh.md) to obtain all face/vertex pairs. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `normalId` | `int64` | | Normal ID to find vertices for. | **Returns:** `Array` — List of vertex IDs that use the provided normal ID. ### Method: EditableMesh:GetVerticesWithUV **Signature:** `EditableMesh:GetVerticesWithUV(uvId: int64): Array` Returns an array of vertex IDs that use the given UV ID. Use together with [GetFacesWithUV()](/docs/reference/engine/classes/EditableMesh.md) to obtain all face/vertex pairs. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `uvId` | `int64` | | UV ID to find vertices for. | **Returns:** `Array` — List of vertex IDs that use the provided UV ID. ### Method: EditableMesh:IdDebugString **Signature:** `EditableMesh:IdDebugString(id: int64): string` Returns a string describing a stable ID, useful for debugging purposes, like `f17` or `v12`, containing the type, ID number, and version. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `id` | `int64` | | ID for which to return a debugging information string. | **Returns:** `string` — String that describes the ID in human-readable format. ### Method: EditableMesh:MergeVertices **Signature:** `EditableMesh:MergeVertices(mergeTolerance: float): Map` Merges vertices that touch together, to use a single vertex ID but keep the other original attribute IDs. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `mergeTolerance` | `float` | | The distance at which the vertices are considered to touch each other. | **Returns:** `Map` — A mapping of old vertex ID to new vertex ID for vertices that have been merged. ### Method: EditableMesh:RaycastLocal **Signature:** `EditableMesh:RaycastLocal(origin: Vector3, direction: Vector3): Tuple` Casts a ray and returns a point of intersection, face ID, and barycentric coordinates. The inputs and outputs of this method are in the mesh's local object space. A **barycentric coordinate** is a way of specifying a point within a face as a weighted combination of the 3 vertices of the face. This is useful as a general way of blending vertex attributes. See this method's code sample as an illustration. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `origin` | `Vector3` | | Origin of the ray in the mesh's local object space. | | `direction` | `Vector3` | | Direction of the ray. | **Returns:** `Tuple` — Tuple of the point of intersection, face ID, and barycentric coordinates. **EditableMesh:RaycastLocal()** This code finds the position and UV coordinates of the closest point on an [EditableMesh](/docs/reference/engine/classes/EditableMesh.md) to the input point. ```lua local AssetService = game:GetService("AssetService") local Workspace = game:GetService("Workspace") -- Initialize EditableMesh in space local editableMesh = nil local success, errorMsg = pcall(function() editableMesh = AssetService:CreateEditableMeshAsync(Content.fromUri("rbxassetid://ASSET_ID")) end) local meshPart = nil if success and editableMesh then meshPart = AssetService:CreateMeshPartAsync( Content.fromObject(editableMesh), { CollisionFidelity = Enum.CollisionFidelity.Hull } ) meshPart.Parent = Workspace else warn(errorMsg) end -- Function that will cast a ray from the given point, returning the world point of the hit and the UV coordinate local function castRayFromCamera(meshPart: MeshPart, editableMesh: EditableMesh, viewportPoint: Vector3) if not meshPart then return end -- Calculate how much the object is being scaled in each dimension local renderScale = meshPart.Size / meshPart.MeshSize -- Create ray from camera along the direction of a clicked point local ray = Workspace.CurrentCamera:ViewportPointToRay(viewportPoint.X, viewportPoint.Y) -- Convert to object space to use with RaycastLocal() local relativeOrigin = meshPart.CFrame:PointToObjectSpace(ray.Origin) / renderScale local relativeTarget = meshPart.CFrame:PointToObjectSpace(ray.Origin + ray.Direction * 100) / renderScale local relativeDirection = relativeTarget - relativeOrigin local faceId, point, barycentricCoordinate, vertId1, vertId2, vertId3 = editableMesh:RaycastLocal(relativeOrigin, relativeDirection) if not faceId then -- Didn't hit any faces return end -- Compute the hit point in world space local worldHitPoint = meshPart.CFrame:PointToWorldSpace(point * renderScale) -- Get the UVs on the face local uvId1 = editableMesh:GetVertexFaceUV(vertId1, faceId) local uvId2 = editableMesh:GetVertexFaceUV(vertId2, faceId) local uvId3 = editableMesh:GetVertexFaceUV(vertId3, faceId) local uv1 = editableMesh:GetUV(uvId1) local uv2 = editableMesh:GetUV(uvId2) local uv3 = editableMesh:GetUV(uvId3) -- Interpolate UVs within the face based on the barycentric coordinate local u = (barycentricCoordinate.x * uv1.x) + (barycentricCoordinate.y * uv2.x) + (barycentricCoordinate.z * uv3.x) local v = (barycentricCoordinate.x * uv1.y) + (barycentricCoordinate.y * uv2.y) + (barycentricCoordinate.z * uv3.y) return worldHitPoint, Vector2.new(u, v) end ``` ### Method: EditableMesh:RemoveBone **Signature:** `EditableMesh:RemoveBone(boneId: int64): ()` Removes a bone using its stable bone ID. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `boneId` | `int64` | | | **Returns:** `()` ### Method: EditableMesh:RemoveFace **Signature:** `EditableMesh:RemoveFace(faceId: int64): ()` Removes a face using its stable face ID. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `faceId` | `int64` | | | **Returns:** `()` ### Method: EditableMesh:RemoveUnused **Signature:** `EditableMesh:RemoveUnused(): Array` Removes all vertices, normals, UVs, and colors which are not used in any face, and returns the removed IDs. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Returns:** `Array` — All of the removed stable IDs. ### Method: EditableMesh:ResetNormal **Signature:** `EditableMesh:ResetNormal(normalId: int64): ()` Reset this normal ID to be automatically calculated based on the shape of the mesh, instead of manually set. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `normalId` | `int64` | | Stable normal ID to reset. | **Returns:** `()` ### Method: EditableMesh:SetBoneCFrame **Signature:** `EditableMesh:SetBoneCFrame(boneId: int64, cframe: CFrame): ()` Set the initial [CFrame](/docs/reference/engine/datatypes/CFrame.md) for a bone in the mesh's bind pose, in the mesh's local space. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `boneId` | `int64` | | Bone ID for which to set the initial [CFrame](/docs/reference/engine/datatypes/CFrame.md). | | `cframe` | `CFrame` | | Initial [CFrame](/docs/reference/engine/datatypes/CFrame.md) for the bone in the mesh's bind pose, in the mesh's local space. | **Returns:** `()` ### Method: EditableMesh:SetBoneIsVirtual **Signature:** `EditableMesh:SetBoneIsVirtual(boneId: int64, virtual: boolean): ()` Set whether a bone is virtual. Virtual bones can only be bound to a [FaceControls](/docs/reference/engine/classes/FaceControls.md) instance. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `boneId` | `int64` | | Bone ID for which to set whether the bone is virtual. | | `virtual` | `boolean` | | Whether the bone should be virtual. | **Returns:** `()` ### Method: EditableMesh:SetBoneName **Signature:** `EditableMesh:SetBoneName(boneId: int64, name: string): ()` Sets the name for a bone. Bone names can be 100 characters long and must be unique in the mesh. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `boneId` | `int64` | | Bone ID for which to set the name. | | `name` | `string` | | Bone name to set. | **Returns:** `()` ### Method: EditableMesh:SetBoneParent **Signature:** `EditableMesh:SetBoneParent(boneId: int64, parentBoneId: int64): ()` Set a parent for a bone. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `boneId` | `int64` | | Bone ID for which to set the parent. | | `parentBoneId` | `int64` | | Parent bone ID. | **Returns:** `()` ### Method: EditableMesh:SetColor **Signature:** `EditableMesh:SetColor(colorId: int64, color: Color3): ()` Sets the color for a color ID. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `colorId` | `int64` | | Stable color ID for which to set the color. | | `color` | `Color3` | | Color to set. | **Returns:** `()` ### Method: EditableMesh:SetColorAlpha **Signature:** `EditableMesh:SetColorAlpha(colorId: int64, alpha: float): ()` Sets the color alpha (transparency) for a color ID. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `colorId` | `int64` | | Stable color ID for which to set the color alpha. | | `alpha` | `float` | | Alpha to set. | **Returns:** `()` ### Method: EditableMesh:SetFaceColors **Signature:** `EditableMesh:SetFaceColors(faceId: int64, ids: Array): ()` Sets the face's vertex colors to new color IDs. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `faceId` | `int64` | | Face ID for which to update the vertex colors. | | `ids` | `Array` | | List of new stable color IDs to use for the given face's vertices. | **Returns:** `()` ### Method: EditableMesh:SetFaceNormals **Signature:** `EditableMesh:SetFaceNormals(faceId: int64, ids: Array): ()` Sets the face's vertex normals to new normal IDs. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `faceId` | `int64` | | Face ID for which to update the vertex normals. | | `ids` | `Array` | | List of new stable normal IDs to use for the given face's vertices. | **Returns:** `()` ### Method: EditableMesh:SetFaceUVs **Signature:** `EditableMesh:SetFaceUVs(faceId: int64, ids: Array): ()` Sets the face's vertex UVs to new UV IDs. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `faceId` | `int64` | | Face ID for which to update the vertex UVs. | | `ids` | `Array` | | List of new stable UV IDs to use for the given face's vertices. | **Returns:** `()` ### Method: EditableMesh:SetFaceVertices **Signature:** `EditableMesh:SetFaceVertices(faceId: int64, ids: Array): ()` Sets the face's vertices to new vertex IDs. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `faceId` | `int64` | | Face ID for which to update the vertices. | | `ids` | `Array` | | List of new stable vertex IDs to use for the given face. | **Returns:** `()` ### Method: EditableMesh:SetFacsBonePose **Signature:** `EditableMesh:SetFacsBonePose(action: FacsActionUnit, boneId: int64, cframe: CFrame): ()` Set [CFrame](/docs/reference/engine/datatypes/CFrame.md) for an individual bone in a specific FACS action unit. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `action` | `FacsActionUnit` | | FACS action unit for which to set the pose. | | `boneId` | `int64` | | Bone to set a [CFrame](/docs/reference/engine/datatypes/CFrame.md) for this pose. | | `cframe` | `CFrame` | | [CFrame](/docs/reference/engine/datatypes/CFrame.md) which transforms the bone from the initial bone [CFrame](/docs/reference/engine/datatypes/CFrame.md) in the bind pose of the mesh to the combined bone [CFrame](/docs/reference/engine/datatypes/CFrame.md) for this pose. All [CFrames](/docs/reference/engine/datatypes/CFrame.md) are in the mesh's local space. | **Returns:** `()` ### Method: EditableMesh:SetFacsCorrectivePose **Signature:** `EditableMesh:SetFacsCorrectivePose(actions: Array, boneIds: Array, cframes: Array): ()` Set pose for all bones in a specific FACS corrective pose. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `actions` | `Array` | | Array or 2 or 3 [FacsActionUnit](/docs/reference/engine/enums/FacsActionUnit.md) values to apply as a corrective pose. | | `boneIds` | `Array` | | Bones to set a [CFrame](/docs/reference/engine/datatypes/CFrame.md) for this pose. | | `cframes` | `Array` | | [CFrame](/docs/reference/engine/datatypes/CFrame.md) transforms for the bones in this corrective pose. Each bone [CFrame](/docs/reference/engine/datatypes/CFrame.md) transforms the bone from the initial bone [CFrame](/docs/reference/engine/datatypes/CFrame.md) in the bind pose of the mesh to the combined bone [CFrame](/docs/reference/engine/datatypes/CFrame.md) for this pose. All [CFrames](/docs/reference/engine/datatypes/CFrame.md) are in the mesh's local space. | **Returns:** `()` ### Method: EditableMesh:SetFacsPose **Signature:** `EditableMesh:SetFacsPose(action: FacsActionUnit, boneIds: Array, cframes: Array): ()` Set pose for all bones in a specific FACS action unit. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `action` | `FacsActionUnit` | | FACS action unit to set the pose for. | | `boneIds` | `Array` | | Bones for which to set a [CFrame](/docs/reference/engine/datatypes/CFrame.md) for this pose. | | `cframes` | `Array` | | [CFrame](/docs/reference/engine/datatypes/CFrame.md) transforms for the bones in this pose. Each bone [CFrame](/docs/reference/engine/datatypes/CFrame.md) transforms the bone from the initial bone [CFrame](/docs/reference/engine/datatypes/CFrame.md) in the bind pose of the mesh to the combined bone [CFrame](/docs/reference/engine/datatypes/CFrame.md) for this pose. All [CFrames](/docs/reference/engine/datatypes/CFrame.md) are in the mesh's local space. | **Returns:** `()` ### Method: EditableMesh:SetNormal **Signature:** `EditableMesh:SetNormal(normalId: int64, normal: Vector3): ()` Set the normal for a normal ID. This will change the normal value for every face vertex which is using the normal ID. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `normalId` | `int64` | | Stable normal ID for which to set the normal vector. | | `normal` | `Vector3` | | Normal vector to set. | **Returns:** `()` ### Method: EditableMesh:SetPosition **Signature:** `EditableMesh:SetPosition(vertexId: int64, p: Vector3): ()` Sets a vertex position in the mesh's local object space. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `vertexId` | `int64` | | Stable vertex ID of the vertex to position. | | `p` | `Vector3` | | Position in the mesh's local object space. | **Returns:** `()` ### Method: EditableMesh:SetUV **Signature:** `EditableMesh:SetUV(uvId: int64, uv: Vector2): ()` Sets UV coordinates for a UV ID. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `uvId` | `int64` | | UV ID for which to set the UV coordinates. | | `uv` | `Vector2` | | UV coordinates. | **Returns:** `()` ### Method: EditableMesh:SetVertexBones **Signature:** `EditableMesh:SetVertexBones(vertexId: int64, boneIDs: Array): ()` Assign a list of bones with the vertex for skinning. Corresponds with the skinning blend weights used in [SetVertexBoneWeights()](/docs/reference/engine/classes/EditableMesh.md). In other words, [GetVertexBoneWeights(vertexId)[i]](/docs/reference/engine/classes/EditableMesh.md) is the weight on this vertex for [GetVertexBones(vertexId)[i]](/docs/reference/engine/classes/EditableMesh.md). This method should be called before calling [SetVertexBoneWeights()](/docs/reference/engine/classes/EditableMesh.md). *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `vertexId` | `int64` | | Vertex ID to set vertex skinning bones. | | `boneIDs` | `Array` | | Bone IDs to use with this vertex for skinning. | **Returns:** `()` ### Method: EditableMesh:SetVertexBoneWeights **Signature:** `EditableMesh:SetVertexBoneWeights(vertexId: int64, boneWeights: Array): ()` Sets skinning blend weights for each bone associated with the vertex. Corresponds with the bone IDs used in [SetVertexBones()](/docs/reference/engine/classes/EditableMesh.md). In other words, [GetVertexBoneWeights(vertexId)[i]](/docs/reference/engine/classes/EditableMesh.md) is the weight on this vertex for [GetVertexBones(vertexId)[i]](/docs/reference/engine/classes/EditableMesh.md). This method should be called after calling [SetVertexBones()](/docs/reference/engine/classes/EditableMesh.md). *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `vertexId` | `int64` | | Vertex ID on which to set skinning blend weights. | | `boneWeights` | `Array` | | Skinning blend weights to set on the vertex. | **Returns:** `()` ### Method: EditableMesh:SetVertexFaceColor **Signature:** `EditableMesh:SetVertexFaceColor(vertexId: int64, faceId: int64, colorId: int64): ()` Sets the color ID of a vertex/face pair. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `vertexId` | `int64` | | Stable vertex ID. | | `faceId` | `int64` | | Stable face ID. | | `colorId` | `int64` | | Stable color ID to set for the vertex/face pair. | **Returns:** `()` ### Method: EditableMesh:SetVertexFaceNormal **Signature:** `EditableMesh:SetVertexFaceNormal(vertexId: int64, faceId: int64, normalId: int64): ()` Sets the normal ID of a vertex/face pair. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `vertexId` | `int64` | | Stable vertex ID. | | `faceId` | `int64` | | Stable face ID. | | `normalId` | `int64` | | Stable normal ID to set for the vertex/face pair. | **Returns:** `()` ### Method: EditableMesh:SetVertexFaceUV **Signature:** `EditableMesh:SetVertexFaceUV(vertexId: int64, faceId: int64, uvId: int64): ()` Sets the UV ID of a vertex/face pair. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `vertexId` | `int64` | | Stable vertex ID. | | `faceId` | `int64` | | Stable face ID. | | `uvId` | `int64` | | Stable UV ID to set for the vertex/face pair. | **Returns:** `()` ### Method: EditableMesh:Triangulate **Signature:** `EditableMesh:Triangulate(): ()` Splits all faces on the mesh to be triangles. Currently this does nothing since only triangles can be created, but if your code relies on triangles, it's recommended that you call this method after calling [AssetService:CreateEditableMeshAsync()](/docs/reference/engine/classes/AssetService.md). *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Returns:** `()` ### Method: EditableMesh:GetFacesWithAttribute **Signature:** `EditableMesh:GetFacesWithAttribute(id: int64): Array` Returns a list of faces that use a given vertex ID, normal ID, UV ID, or color ID. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `id` | `int64` | | Attribute ID for which to find faces that use it. | **Returns:** `Array` — List of face IDs which use the given attribute ID. ### Method: EditableMesh:GetVerticesWithAttribute **Signature:** `EditableMesh:GetVerticesWithAttribute(id: int64): Array` Returns a list of vertices that use a given face ID, normal ID, UV ID, or color ID. *Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `id` | `int64` | | Attribute ID for which to find vertices that use it. | **Returns:** `Array` — List of vertex IDs which use the given attribute ID. ## Inherited Members ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: EncodingService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated summary: "Service providing common encoding, hashing, and compression methods." --- # Class: EncodingService > Service providing common encoding, hashing, and compression methods. ## Methods ### Method: EncodingService:Base64Decode **Signature:** `EncodingService:Base64Decode(input: buffer): buffer` Method takes a [buffer](/docs/reference/engine/globals/buffer.md) with data encoded in Base64 format and decodes it into a new [buffer](/docs/reference/engine/globals/buffer.md). If the input is not valid Base64 data, this method throws an error. Base64 data is most often used for binary data encoding, so these functions are designed to work on [buffer](/docs/reference/engine/globals/buffer.md) values. If the data is stored in a string, [buffer.fromstring](/docs/reference/engine/globals/buffer.md) can be used to quickly convert it into a buffer. *Security: None · Thread Safety: Safe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `input` | `buffer` | | [buffer](/docs/reference/engine/globals/buffer.md) containing Base64 data to decode | **Returns:** `buffer` — [buffer](/docs/reference/engine/globals/buffer.md) with the decoded result ### Method: EncodingService:Base64Encode **Signature:** `EncodingService:Base64Encode(input: buffer): buffer` Method takes a [buffer](/docs/reference/engine/globals/buffer.md) with binary data and encodes it using Base64. Encoded result is returned as a new [buffer](/docs/reference/engine/globals/buffer.md). *Security: None · Thread Safety: Safe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `input` | `buffer` | | [buffer](/docs/reference/engine/globals/buffer.md) containing binary data to encode | **Returns:** `buffer` — [buffer](/docs/reference/engine/globals/buffer.md) with the encoded result ### Method: EncodingService:CompressBuffer **Signature:** `EncodingService:CompressBuffer(input: buffer, algorithm: CompressionAlgorithm, compressionLevel?: int): buffer` Compresses the binary data stored in a [buffer](/docs/reference/engine/globals/buffer.md) using the specified compression algorithm and compression level. The higher the compression level, the longer it takes to compress, but the resulting compression ratio might increase. For [CompressionAlgorithm.Zstd](/docs/reference/engine/enums/CompressionAlgorithm.md), the allowed compression values are from -7 to 22 inclusive. *Security: None · Thread Safety: Safe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `input` | `buffer` | | [buffer](/docs/reference/engine/globals/buffer.md) with binary data to compress | | `algorithm` | `CompressionAlgorithm` | | [CompressionAlgorithm](/docs/reference/engine/enums/CompressionAlgorithm.md) to use for compression | | `compressionLevel` | `int` | `1` | optional integer compression level to use | **Returns:** `buffer` — [buffer](/docs/reference/engine/globals/buffer.md) with compressed binary data ### Method: EncodingService:ComputeBufferHash **Signature:** `EncodingService:ComputeBufferHash(input: buffer, algorithm: HashAlgorithm): buffer` Uses a [cryptographic hash function](https://en.wikipedia.org/wiki/Cryptographic_hash_function) to compute a hash of the input [buffer](/docs/reference/engine/globals/buffer.md). Returns a new [buffer](/docs/reference/engine/globals/buffer.md) with the binary data. See descriptions of [HashAlgorithm](/docs/reference/engine/enums/HashAlgorithm.md) to learn of the supported digest size for each hash. #### Do not use this for passwords None of these hashes are password hashing algorithms. They were designed to be fast to execute, whereas password hashing should never be fast. You shouldn't use this function for computing password hashes that will be stored, or for deriving keys from passwords. *Security: None · Thread Safety: Safe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `input` | `buffer` | | [buffer](/docs/reference/engine/globals/buffer.md) with binary data to compute hash for | | `algorithm` | `HashAlgorithm` | | [HashAlgorithm](/docs/reference/engine/enums/HashAlgorithm.md) cryptographic hash function | **Returns:** `buffer` — [buffer](/docs/reference/engine/globals/buffer.md) with the binary data of the hash ### Method: EncodingService:ComputeStringHash **Signature:** `EncodingService:ComputeStringHash(input: string, algorithm: HashAlgorithm): string` Uses a [cryptographic hash function](https://en.wikipedia.org/wiki/Cryptographic_hash_function) to compute a hash of the input string. Returns a new string with the binary data. See descriptions of [HashAlgorithm](/docs/reference/engine/enums/HashAlgorithm.md) to learn of the supported digest size for each hash. #### Do not use this for passwords None of these hashes are password hashing algorithms. They were designed to be fast to execute, whereas password hashing should never be fast. You shouldn't use this function for computing password hashes that will be stored, or for deriving keys from passwords. *Security: None · Thread Safety: Safe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `input` | `string` | | string to compute the hash for | | `algorithm` | `HashAlgorithm` | | [HashAlgorithm](/docs/reference/engine/enums/HashAlgorithm.md) cryptographic hash function | **Returns:** `string` — String with the binary data of the hash ### Method: EncodingService:DecompressBuffer **Signature:** `EncodingService:DecompressBuffer(input: buffer, algorithm: CompressionAlgorithm): buffer` Decompresses the binary data stored in a [buffer](/docs/reference/engine/globals/buffer.md) using the specified compression algorithm. Decompression might throw an error if the compressed data doesn't contain expected decompressed size, if it is larger than 1GB or if it is invalid. It is recommended to use [EncodingService.GetDecompressedBufferSize](/docs/reference/engine/classes/EncodingService.md) to find out the decompressed data size before attempting to decompress. This is very important if the input buffer contents are outside of your control (for example, from a client [RemoteEvent](/docs/reference/engine/classes/RemoteEvent.md)) *Security: None · Thread Safety: Safe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `input` | `buffer` | | [buffer](/docs/reference/engine/globals/buffer.md) with compressed binary data | | `algorithm` | `CompressionAlgorithm` | | [CompressionAlgorithm](/docs/reference/engine/enums/CompressionAlgorithm.md) to use for decompression | **Returns:** `buffer` — [buffer](/docs/reference/engine/globals/buffer.md) with the decompressed binary data ### Method: EncodingService:GetDecompressedBufferSize **Signature:** `EncodingService:GetDecompressedBufferSize(input: buffer, algorithm: CompressionAlgorithm): int?` Reports the size of the decompressed data stored in a compressed [buffer](/docs/reference/engine/globals/buffer.md). If the compressed data doesn't have a size or the size is corrupted or invalid (larger than 1GB), function returns `nil`. When a `nil` is returned, attempting to use [EncodingService.DecompressBuffer](/docs/reference/engine/classes/EncodingService.md) on this [buffer](/docs/reference/engine/globals/buffer.md) will throw an error. *Security: None · Thread Safety: Safe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `input` | `buffer` | | [buffer](/docs/reference/engine/globals/buffer.md) with compressed binary data | | `algorithm` | `CompressionAlgorithm` | | [CompressionAlgorithm](/docs/reference/engine/enums/CompressionAlgorithm.md) used to compress it | **Returns:** `int?` — Integer size of the decompressed data or 'nil' ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: EqualizerSoundEffect last_updated: 2026-06-29T19:34:07Z inherits: - SoundEffect - Instance - Object type: class memory_category: Instances summary: "Controls the volume of incoming audio across three frequency ranges." --- # Class: EqualizerSoundEffect > Controls the volume of incoming audio across three frequency ranges. ## Description An equalizer allows for control of the volume of various frequency ranges for the [Sound](/docs/reference/engine/classes/Sound.md) or [SoundGroup](/docs/reference/engine/classes/SoundGroup.md) the effect is parented to. This can be used to highlight particular elements of audio or minimize or outright eliminate others. The [EqualizerSoundEffect](/docs/reference/engine/classes/EqualizerSoundEffect.md) gives control over three ranges of frequency: Low, Mid, and High. Their specific frequencies are as follows: - Low: 0 - 400 Hz - Mid: 400 - 4000 Hz - High: 4000+ Hz ## Properties ### Property: EqualizerSoundEffect.HighGain ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "LegacySound" ] } ``` Range: -80 to 10 (default 0) The output volume of frequencies greater than 4000 Hz. Measured in dB. ### Property: EqualizerSoundEffect.LowGain ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "LegacySound" ] } ``` Range: -80 to 10 (default 0) The output volume of frequencies lower than 400 Hz. Measured in dB. ### Property: EqualizerSoundEffect.MidGain ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "LegacySound" ] } ``` Range: -80 to 10 (default 0) The output volume of frequencies between 400 and 4000 Hz. Measured in dB. ## Inherited Members ### From [SoundEffect](/docs/reference/engine/classes/SoundEffect.md) - **Property `Enabled`** (`boolean`): Toggles the effect on and off. - **Property `Priority`** (`int`): Determines the order the effect will be applied in relation to other ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: EulerRotationCurve last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances summary: "Represents a 3D rotation curve through a group of three FloatCurves." --- # Class: EulerRotationCurve > Represents a 3D rotation curve through a group of three > [FloatCurves](/docs/reference/engine/classes/FloatCurve.md). ## Description A **EulerRotationCurve** represents a 3D rotation curve through a group of three [FloatCurves](/docs/reference/engine/classes/FloatCurve.md). The rotation is decomposed in three Euler angles channels that can be accessed via [EulerRotationCurve:X()](/docs/reference/engine/classes/EulerRotationCurve.md), [EulerRotationCurve:Y()](/docs/reference/engine/classes/EulerRotationCurve.md), and [EulerRotationCurve:Z()](/docs/reference/engine/classes/EulerRotationCurve.md). The three axes can be sampled simultaneously via [EulerRotationCurve:GetAnglesAtTime()](/docs/reference/engine/classes/EulerRotationCurve.md), returning the three Euler angles as a [Vector3](/docs/reference/engine/datatypes/Vector3.md). Similarly, [EulerRotationCurve:GetRotationAtTime()](/docs/reference/engine/classes/EulerRotationCurve.md) samples all channels simultaneously but returns a [CFrame](/docs/reference/engine/datatypes/CFrame.md) rotated by `X`, `Y`, and `Z` according to the specified rotation order. ## Properties ### Property: EulerRotationCurve.RotationOrder ```json { "type": "RotationOrder", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` Euler angles rotation order ## Methods ### Method: EulerRotationCurve:GetAnglesAtTime **Signature:** `EulerRotationCurve:GetAnglesAtTime(time: float): Array` Samples the three [FloatCurves](/docs/reference/engine/classes/FloatCurve.md) (`X`, `Y`, `Z`) at the passed `time` argument and returns the result as three Euler angles. If a channel curve is missing or no key is found in the curve, the channel is evaluated as `nil`. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `time` | `float` | | | **Returns:** `Array` ### Method: EulerRotationCurve:GetRotationAtTime **Signature:** `EulerRotationCurve:GetRotationAtTime(time: float): CFrame` Samples the [EulerRotationCurve](/docs/reference/engine/classes/EulerRotationCurve.md) at a given `time` and returns the corresponding rotation. Empty Euler angles channels are interpreted as zero. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `time` | `float` | | | **Returns:** `CFrame` ### Method: EulerRotationCurve:X **Signature:** `EulerRotationCurve:X(): FloatCurve` Returns the [FloatCurve](/docs/reference/engine/classes/FloatCurve.md) controlling the `X` Euler angle channel. It is the first child instance of type [FloatCurve](/docs/reference/engine/classes/FloatCurve.md) named `X`. If none is found, an empty [FloatCurve](/docs/reference/engine/classes/FloatCurve.md) is created. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Returns:** `FloatCurve` ### Method: EulerRotationCurve:Y **Signature:** `EulerRotationCurve:Y(): FloatCurve` Returns the [FloatCurve](/docs/reference/engine/classes/FloatCurve.md) controlling the `Y` Euler angle channel. It is the first child instance of type [FloatCurve](/docs/reference/engine/classes/FloatCurve.md) named `Y`. If none is found, an empty [FloatCurve](/docs/reference/engine/classes/FloatCurve.md) is created. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Returns:** `FloatCurve` ### Method: EulerRotationCurve:Z **Signature:** `EulerRotationCurve:Z(): FloatCurve` Returns the [FloatCurve](/docs/reference/engine/classes/FloatCurve.md) controlling the `Z` Euler angle channel. It is the first child instance of type [FloatCurve](/docs/reference/engine/classes/FloatCurve.md) named `Z`. If none is found, an empty [FloatCurve](/docs/reference/engine/classes/FloatCurve.md) is created. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Returns:** `FloatCurve` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ExperienceInviteOptions last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotReplicated summary: "Used to customize a player invite prompt." --- # Class: ExperienceInviteOptions > Used to customize a player invite prompt. ## Description Used to customize a [player invite prompt](/docs/en-us/production/promotion/invite-prompts.md), such as to customize the prompt message, target a specific friend, or include launch data in the invite. ## Properties ### Property: ExperienceInviteOptions.InviteMessageId ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Social" ] } ``` Asset ID that maps to a **Notification** asset type. This asset is used to store/localize a custom string for the invite notification that friends receive. See [Setting Notification Options](/docs/en-us/production/promotion/invite-prompts.md#setting-notification-options) for details. ### Property: ExperienceInviteOptions.InviteUser ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Social" ] } ``` Roblox [UserId](/docs/reference/engine/classes/Player.md) of the specific friend to invite; if not provided, the player will be prompted to pick from a list of friends. ### Property: ExperienceInviteOptions.LaunchData ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Social" ] } ``` Used to set a parameter in [Player:GetJoinData()](/docs/reference/engine/classes/Player.md) when a friend joins from the invite notification; maximum of 200 characters. See [Include launch data](/docs/en-us/production/promotion/invite-prompts.md#include-launch-data) for a usage example. ### Property: ExperienceInviteOptions.PromptMessage ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Social" ] } ``` Custom text shown on the invite prompt for the sending player, for example "Ask your friends to join the adventure!" for a multi-friend invite prompt, or "Invite this friend to join the adventure!" for a specific friend invite prompt. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ExperienceNotificationService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated summary: "Service containing methods to validate users and prompt them to enable experience notifications." --- # Class: ExperienceNotificationService > Service containing methods to validate users and prompt them to enable > experience notifications. ## Description [Experience notifications](/docs/en-us/production/promotion/experience-notifications.md) are a way for 13+ users to keep up with their favorite experiences through timely, personalized notifications. This service contains methods to validate users and prompt them to enable notifications. ## Methods ### Method: ExperienceNotificationService:CanPromptOptInAsync **Signature:** `ExperienceNotificationService:CanPromptOptInAsync(): boolean` [CanPromptOptInAsync()](/docs/reference/engine/classes/ExperienceNotificationService.md) returns `true` if the local player can be prompted to enable notifications. You should always use the result of this method before calling [PromptOptIn()](/docs/reference/engine/classes/ExperienceNotificationService.md) since the ability to be prompted depends on various factors like the player's age or whether they've already enabled notifications for your experience. This method always infers the **local** player ([Players.LocalPlayer](/docs/reference/engine/classes/Players.md)) and it can only be called from a [LocalScript](/docs/reference/engine/classes/LocalScript.md) or from a [Script](/docs/reference/engine/classes/Script.md) with [RunContext](/docs/reference/engine/classes/BaseScript.md) set to [Client](/docs/reference/engine/enums/RunContext.md). It should also be called in a [pcall()](/docs/reference/engine/globals/LuaGlobals.md) since it's an asynchronous network call that may occasionally fail. See [Experience notifications](/docs/en-us/production/promotion/experience-notifications.md) for more details on implementing and customizing notifications, using launch data, and more. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Players, Social* **Returns:** `boolean` — Whether the local player can be prompted to enable notifications. **LocalScript - Notification Permission Prompt Implementation** ```lua local ExperienceNotificationService = game:GetService("ExperienceNotificationService") -- Function to check whether the player can be prompted to enable notifications local function canPromptOptIn() local success, canPrompt = pcall(function() return ExperienceNotificationService:CanPromptOptInAsync() end) return success and canPrompt end local canPrompt = canPromptOptIn() if canPrompt then local success, errorMessage = pcall(function() ExperienceNotificationService:PromptOptIn() end) end -- Listen to opt-in prompt closed event ExperienceNotificationService.OptInPromptClosed:Connect(function() print("Opt-in prompt closed") end) ``` ### Method: ExperienceNotificationService:PromptOptIn **Signature:** `ExperienceNotificationService:PromptOptIn(): ()` [PromptOptIn()](/docs/reference/engine/classes/ExperienceNotificationService.md) prompts the local player to enable notifications through an in-experience modal. You should always use the result of [CanPromptOptInAsync()](/docs/reference/engine/classes/ExperienceNotificationService.md) before calling this method since the ability to be prompted depends on various factors like the player's age or whether they've already enabled notifications for your experience. This method always infers the **local** player ([Players.LocalPlayer](/docs/reference/engine/classes/Players.md)) and it can only be called from a [LocalScript](/docs/reference/engine/classes/LocalScript.md) or from a [Script](/docs/reference/engine/classes/Script.md) with [RunContext](/docs/reference/engine/classes/BaseScript.md) set to [Client](/docs/reference/engine/enums/RunContext.md). See [Experience notifications](/docs/en-us/production/promotion/experience-notifications.md) for more details on implementing and customizing notifications, using launch data, and more. *Security: None · Thread Safety: Unsafe · Capabilities: Players, Social* **Returns:** `()` **LocalScript - Notification Permission Prompt Implementation** ```lua local ExperienceNotificationService = game:GetService("ExperienceNotificationService") -- Function to check whether the player can be prompted to enable notifications local function canPromptOptIn() local success, canPrompt = pcall(function() return ExperienceNotificationService:CanPromptOptInAsync() end) return success and canPrompt end local canPrompt = canPromptOptIn() if canPrompt then local success, errorMessage = pcall(function() ExperienceNotificationService:PromptOptIn() end) end -- Listen to opt-in prompt closed event ExperienceNotificationService.OptInPromptClosed:Connect(function() print("Opt-in prompt closed") end) ``` ## Events ### Event: ExperienceNotificationService.OptInPromptClosed **Signature:** `ExperienceNotificationService.OptInPromptClosed()` This event fires when the local player closes a prompt that was displayed through [PromptOptIn()](/docs/reference/engine/classes/ExperienceNotificationService.md). It can only be connected in a [LocalScript](/docs/reference/engine/classes/LocalScript.md) or in a [Script](/docs/reference/engine/classes/Script.md) with [RunContext](/docs/reference/engine/classes/BaseScript.md) set to [Client](/docs/reference/engine/enums/RunContext.md). See [Experience notifications](/docs/en-us/production/promotion/experience-notifications.md) for more details on implementing and customizing notifications, using launch data, and more. *Security: None · Capabilities: Players, Social* **LocalScript - Notification Permission Prompt Implementation** ```lua local ExperienceNotificationService = game:GetService("ExperienceNotificationService") -- Function to check whether the player can be prompted to enable notifications local function canPromptOptIn() local success, canPrompt = pcall(function() return ExperienceNotificationService:CanPromptOptInAsync() end) return success and canPrompt end local canPrompt = canPromptOptIn() if canPrompt then local success, errorMessage = pcall(function() ExperienceNotificationService:PromptOptIn() end) end -- Listen to opt-in prompt closed event ExperienceNotificationService.OptInPromptClosed:Connect(function() print("Opt-in prompt closed") end) ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Explosion last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances summary: "Applies force to BaseParts within the explosion's Explosion.BlastRadius. Breaks JointInstances and WeldConstraints between parts and kills Humanoid characters not protected by a ForceField." --- # Class: Explosion > Applies force to [BaseParts](/docs/reference/engine/classes/BasePart.md) within the explosion's > [Explosion.BlastRadius](/docs/reference/engine/classes/Explosion.md). Breaks [JointInstances](/docs/reference/engine/classes/JointInstance.md) and > [WeldConstraints](/docs/reference/engine/classes/WeldConstraint.md) between parts and kills > [Humanoid](/docs/reference/engine/classes/Humanoid.md) characters not protected by a [ForceField](/docs/reference/engine/classes/ForceField.md). ## Description An Explosion applies force to [BaseParts](/docs/reference/engine/classes/BasePart.md) within the explosion's [BlastRadius](/docs/reference/engine/classes/Explosion.md). This force breaks [JointInstances](/docs/reference/engine/classes/JointInstance.md) and [WeldConstraints](/docs/reference/engine/classes/WeldConstraint.md) between parts and kills [Humanoid](/docs/reference/engine/classes/Humanoid.md) characters not protected by a [ForceField](/docs/reference/engine/classes/ForceField.md). [Constraints](/docs/reference/engine/classes/Constraint.md) will not be broken by an explosion. If an [Explosion](/docs/reference/engine/classes/Explosion.md) is parented anywhere in the data model while the experience is running, it immediately sets off and, within a few seconds, it becomes unparented. It is not destroyed with [Instance:Destroy()](/docs/reference/engine/classes/Instance.md) in this case, so connections do not get disconnected and the parent is not locked. As with all instances, keeping a strong reference an [Explosion](/docs/reference/engine/classes/Explosion.md) will prevent it from being garbage collected. Note that an [Explosion](/docs/reference/engine/classes/Explosion.md) must be a descendant of [Workspace](/docs/reference/engine/classes/Workspace.md) for the explosion visuals to play and the physical/damaging effects to have an impact. #### Explosion Effects [Humanoids](/docs/reference/engine/classes/Humanoid.md) are killed by explosions, as the explosion breaks the character [Model](/docs/reference/engine/classes/Model.md) neck joint. Parenting a [ForceField](/docs/reference/engine/classes/ForceField.md) to a model will protect all of its children from the explosion kill effect. If you do not want joints between [BaseParts](/docs/reference/engine/classes/BasePart.md) to be broken, or you want to implement your own formula for damaging [Humanoids](/docs/reference/engine/classes/Humanoid.md), it's recommended that you set [DestroyJointRadiusPercent](/docs/reference/engine/classes/Explosion.md) to 0 and use the [Hit](/docs/reference/engine/classes/Explosion.md) event to handle the result of the explosion. Explosions can also be configured to damage [Terrain](/docs/reference/engine/classes/Terrain.md), creating craters, as configured through the [ExplosionType](/docs/reference/engine/classes/Explosion.md) property. Note that the effect of an explosion is **not** disrupted by obstacles, meaning that parts/terrain shielded behind other parts/terrain will still be affected. ## Code Samples **Explosion Instantiation** This code sample includes a brief snippet that creates a large explosion in the game at 0, 10, 0. ```lua local explosion = Instance.new("Explosion") explosion.BlastRadius = 60 explosion.ExplosionType = Enum.ExplosionType.Craters -- damages terrain explosion.Position = Vector3.new(0, 10, 0) explosion.Parent = workspace ``` ## Properties ### Property: Explosion.BlastPressure ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarBehavior" ] } ``` Used to determine the amount of force applied to [BaseParts](/docs/reference/engine/classes/BasePart.md) caught in the [Explosion.BlastRadius](/docs/reference/engine/classes/Explosion.md). Currently this level of force applied does not vary based on distance from [Explosion.Position](/docs/reference/engine/classes/Explosion.md). Unanchored `BaseParts` will accelerate equally away from the origin regardless of distance provided they are within the blast radius. The blast pressure determines the acceleration of parts due to an explosion. It does not determine the degree to which joints are broken. When [Explosion.DestroyJointRadiusPercent](/docs/reference/engine/classes/Explosion.md) is equal to 1 all joints between parts in the [Explosion.BlastRadius](/docs/reference/engine/classes/Explosion.md) will be destroyed provided BlastPressure is greater than 0. The BlastPressure also does not determine the amount of damage given to [Terrain](/docs/reference/engine/classes/Terrain.md). Provided BlastPressure is greater than 0 and [Explosion.ExplosionType](/docs/reference/engine/classes/Explosion.md) isn't set to Enum.ExplosionType.NoCraters the size of the crater created is determined exclusively by the [Explosion.BlastRadius](/docs/reference/engine/classes/Explosion.md). Setting BlastPressure to 0 eliminates the effect of the explosion and is useful when developers want to program their own custom behavior for explosions using the [Explosion.Hit](/docs/reference/engine/classes/Explosion.md) event. **Custom Explosion** This sample contains a simple function that creates a custom explosion. The custom explosion will not break joints as Explosion.DestroyJointRadiusPercent is equal to 0. This means Humanoids will not be instantly killed as their neck joints are broken. In this example explosion.BlastPressure is equal to zero so as not to apply force or damage terrain but this can be changed. The Explosion.Hit event is used to listen for contact. Once contact has been made the code will look for the parent model of the BasePart hit by the explosion. If that model exists, hasn't already been damaged and has a Humanoid in it damage will be applied based on distance from the explosion's origin. ```lua local function customExplosion(position, radius, maxDamage) local explosion = Instance.new("Explosion") explosion.BlastPressure = 0 -- this could be set higher to still apply velocity to parts explosion.DestroyJointRadiusPercent = 0 -- joints are safe explosion.BlastRadius = radius explosion.Position = position -- set up a table to track the models hit local modelsHit = {} -- listen for contact explosion.Hit:Connect(function(part, distance) local parentModel = part.Parent if parentModel then -- check to see if this model has already been hit if modelsHit[parentModel] then return end -- log this model as hit modelsHit[parentModel] = true -- look for a humanoid local humanoid = parentModel:FindFirstChild("Humanoid") if humanoid then local distanceFactor = distance / explosion.BlastRadius -- get the distance as a value between 0 and 1 distanceFactor = 1 - distanceFactor -- flip the amount, so that lower == closer == more damage humanoid:TakeDamage(maxDamage * distanceFactor) -- TakeDamage to respect ForceFields end end end) explosion.Parent = game.Workspace -- Roblox removes explosions after a few seconds, but does not destroy them. -- To ensure our .Hit connection gets disconnected, destroy the explosion once it's removed. explosion.AncestryChanged:Connect(function() if not explosion.Parent then explosion:Destroy() end end) end customExplosion(Vector3.new(0, 10, 0), 12, 50) ``` ### Property: Explosion.BlastRadius ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarBehavior" ] } ``` This property determines the radius of the [Explosion](/docs/reference/engine/classes/Explosion.md), in studs. This property accepts any value between 0 and 100. This radius determines the area of effect of the Explosion, not the size of the Explosion's visuals. The size of the Explosion's visual effect is the same regardless of BlastRadius (even if BlastRadius is 0). [BaseParts](/docs/reference/engine/classes/BasePart.md) within the BlastRadius will be affected by the explosion. Meaning, if [Explosion.BlastPressure](/docs/reference/engine/classes/Explosion.md) is greater than 0, force will be applied to parts. The degree to which joints are broken within the BlastRadius depends on [Explosion.DestroyJointRadiusPercent](/docs/reference/engine/classes/Explosion.md). [Explosion.Hit](/docs/reference/engine/classes/Explosion.md) will fire for any every [BasePart](/docs/reference/engine/classes/BasePart.md) within the radius. [BaseParts](/docs/reference/engine/classes/BasePart.md) are considered within [Explosion.BlastRadius](/docs/reference/engine/classes/Explosion.md) even if they are only partially in range. **Explosion Instantiation** This code sample includes a brief snippet that creates a large explosion in the game at 0, 10, 0. ```lua local explosion = Instance.new("Explosion") explosion.BlastRadius = 60 explosion.ExplosionType = Enum.ExplosionType.Craters -- damages terrain explosion.Position = Vector3.new(0, 10, 0) explosion.Parent = workspace ``` ### Property: Explosion.DestroyJointRadiusPercent ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarBehavior" ] } ``` Used to set the proportion of the [Explosion.BlastRadius](/docs/reference/engine/classes/Explosion.md), between 0 and 1, within which all joints will be destroyed. Anything outside of this range will only have the [Explosion](/docs/reference/engine/classes/Explosion.md) force applied to it. For example, if [Explosion.BlastRadius](/docs/reference/engine/classes/Explosion.md) is set to 100 and DestroyJointRadiusPercent is set to 0.5, any joints within a radius of 50 studs would be broken. Any joints between the ranges of 50 and 100 studs wouldn't be destroyed, but the [Explosion](/docs/reference/engine/classes/Explosion.md) force would still be applied to the [BaseParts](/docs/reference/engine/classes/BasePart.md). This property allows developers to make [Explosions](/docs/reference/engine/classes/Explosion.md) 'non-lethal' to [Humanoids](/docs/reference/engine/classes/Humanoid.md) by setting DestroyJointRadiusPercent to 0. This means the neck joint will not be broken when characters come into contact with the [Explosion](/docs/reference/engine/classes/Explosion.md). **Non lethal explosions** This sample includes an example of how Explosions can be made non lethal to player characters. ```lua local function onDescendantAdded(instance) if instance:IsA("Explosion") then local explosion = instance explosion.DestroyJointRadiusPercent = 0 local destroyJointRadiusPercent = 1 explosion.Hit:Connect(function(part, distance) -- check the part is in range to break joints if distance <= destroyJointRadiusPercent * explosion.BlastRadius then -- make sure the part does not belong to a character if not game.Players:GetPlayerFromCharacter(part.Parent) then part:BreakJoints() end end end) end end workspace.DescendantAdded:Connect(onDescendantAdded) ``` **Custom Explosion** This sample contains a simple function that creates a custom explosion. The custom explosion will not break joints as Explosion.DestroyJointRadiusPercent is equal to 0. This means Humanoids will not be instantly killed as their neck joints are broken. In this example explosion.BlastPressure is equal to zero so as not to apply force or damage terrain but this can be changed. The Explosion.Hit event is used to listen for contact. Once contact has been made the code will look for the parent model of the BasePart hit by the explosion. If that model exists, hasn't already been damaged and has a Humanoid in it damage will be applied based on distance from the explosion's origin. ```lua local function customExplosion(position, radius, maxDamage) local explosion = Instance.new("Explosion") explosion.BlastPressure = 0 -- this could be set higher to still apply velocity to parts explosion.DestroyJointRadiusPercent = 0 -- joints are safe explosion.BlastRadius = radius explosion.Position = position -- set up a table to track the models hit local modelsHit = {} -- listen for contact explosion.Hit:Connect(function(part, distance) local parentModel = part.Parent if parentModel then -- check to see if this model has already been hit if modelsHit[parentModel] then return end -- log this model as hit modelsHit[parentModel] = true -- look for a humanoid local humanoid = parentModel:FindFirstChild("Humanoid") if humanoid then local distanceFactor = distance / explosion.BlastRadius -- get the distance as a value between 0 and 1 distanceFactor = 1 - distanceFactor -- flip the amount, so that lower == closer == more damage humanoid:TakeDamage(maxDamage * distanceFactor) -- TakeDamage to respect ForceFields end end end) explosion.Parent = game.Workspace -- Roblox removes explosions after a few seconds, but does not destroy them. -- To ensure our .Hit connection gets disconnected, destroy the explosion once it's removed. explosion.AncestryChanged:Connect(function() if not explosion.Parent then explosion:Destroy() end end) end customExplosion(Vector3.new(0, 10, 0), 12, 50) ``` ### Property: Explosion.ExplosionType ```json { "type": "ExplosionType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarBehavior" ] } ``` This property determines how the [Explosion](/docs/reference/engine/classes/Explosion.md) will interact with [Terrain](/docs/reference/engine/classes/Terrain.md). It is an Enum.ExplosionType value and can be set to one of three options. - **NoCraters** - Explosions will not damage Terrain - **Craters** - Explosions will create craters in Terrain - **CratersAndDebris** - Redundant, behaves the same as Craters If ExplosionType is set to create craters in [Terrain](/docs/reference/engine/classes/Terrain.md), the radius of the crater will be roughly equal to the [Explosion.BlastRadius](/docs/reference/engine/classes/Explosion.md). Craters are created in all [Terrain](/docs/reference/engine/classes/Terrain.md) materials other than water. The size of the crater is not influenced by the material, although some materials create rougher edges than others. **Stop Explosions from Damaging Terrain** This code sample includes an example of how the Explosion.ExplosionType property can be used to stop Explosions from damaging terrain. It is recommended to set the ExplosionType to NoCraters at the point of Explosion instantiation, but if that is not practical the code below will work. ```lua local function onDescendantAdded(instance) if instance:IsA("Explosion") then instance.ExplosionType = Enum.ExplosionType.NoCraters instance:GetPropertyChangedSignal("ExplosionType"):Connect(function() instance.ExplosionType = Enum.ExplosionType.NoCraters end) end end workspace.DescendantAdded:Connect(onDescendantAdded) ``` ### Property: Explosion.Position ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarBehavior" ] } ``` This property is the position of the center of the [Explosion](/docs/reference/engine/classes/Explosion.md). It is defined in world-space and not influenced by the [Explosion](/docs/reference/engine/classes/Explosion.md) parent. [BaseParts](/docs/reference/engine/classes/BasePart.md) will be influenced by the [Explosion](/docs/reference/engine/classes/Explosion.md) if they are within [Explosion.BlastRadius](/docs/reference/engine/classes/Explosion.md) studs of the explosion's position. The effect of an explosion is instantaneous. This means that although the position of an explosion can be changed after it has been set it cannot affect two different areas. Once an explosion has been 'detonated', shortly after parenting it to a descendant of the [Workspace](/docs/reference/engine/classes/Workspace.md), it will not do so again. In some cases the visual effect of the explosion will move but it will have no effect. For this reason a new Explosion should be created if the developer wants an explosion to appear at a different position. **Explosion Instantiation** This code sample includes a brief snippet that creates a large explosion in the game at 0, 10, 0. ```lua local explosion = Instance.new("Explosion") explosion.BlastRadius = 60 explosion.ExplosionType = Enum.ExplosionType.Craters -- damages terrain explosion.Position = Vector3.new(0, 10, 0) explosion.Parent = workspace ``` ### Property: Explosion.TimeScale ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarBehavior" ] } ``` A value between 0 and 1 that controls the speed of the particle effect. At 1 it runs at normal speed, at 0.5 it runs at half speed, and at 0 it freezes time. ### Property: Explosion.Visible ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarBehavior" ] } ``` This property determines whether or not the visual effect of an [Explosion](/docs/reference/engine/classes/Explosion.md) is shown or not. When Visible is set to false, the explosion will still affect [BaseParts](/docs/reference/engine/classes/BasePart.md) in its [Explosion.BlastRadius](/docs/reference/engine/classes/Explosion.md), the only difference is it will not be seen. One use for this property would be for a developer to make their own custom explosion effects using a [ParticleEmitter](/docs/reference/engine/classes/ParticleEmitter.md), while retaining the default [Explosion](/docs/reference/engine/classes/Explosion.md) functionality. **Explosion Custom Visuals** This sample includes a function that will create an Explosion but replace the default Explosion visuals but those of a ParticleEmitter. ```lua local function customExplosion(position) local explosion = Instance.new("Explosion") explosion.Position = position explosion.Visible = false local attachment = Instance.new("Attachment") attachment.Position = position attachment.Parent = workspace.Terrain local particleEmitter = Instance.new("ParticleEmitter") particleEmitter.Enabled = false particleEmitter.Parent = attachment particleEmitter.Speed = NumberRange.new(5, 30) particleEmitter.SpreadAngle = Vector2.new(-90, 90) explosion.Parent = workspace particleEmitter:Emit(20) task.delay(5, function() if attachment then attachment:Destroy() end end) end customExplosion(Vector3.new(0, 10, 0)) ``` ### Property: Explosion.LocalTransparencyModifier *(hidden)* ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarBehavior" ] } ``` ## Events ### Event: Explosion.Hit **Signature:** `Explosion.Hit(part: BasePart, distance: float)` Fires when the [Explosion](/docs/reference/engine/classes/Explosion.md) hits a [BasePart](/docs/reference/engine/classes/BasePart.md) within its [Explosion.BlastRadius](/docs/reference/engine/classes/Explosion.md). Returns the part hit along with the distance of the part from [Explosion.Position](/docs/reference/engine/classes/Explosion.md). Note that the effect of an [Explosion](/docs/reference/engine/classes/Explosion.md) is not disrupted by obstacles, this means parts shielded behind other parts will still be hit, even if the [BasePart](/docs/reference/engine/classes/BasePart.md) they are shielded behind is anchored. This event will also fire when [Explosion.BlastPressure](/docs/reference/engine/classes/Explosion.md) is equal to zero. This means developers can program their own custom behavior for explosions by eliminating the explosion's influence on [BaseParts](/docs/reference/engine/classes/BasePart.md) and [Terrain](/docs/reference/engine/classes/Terrain.md). Note that this event will fire for every [BasePart](/docs/reference/engine/classes/BasePart.md) hit. This means it can fire multiple times for the same player character (as the character [Model](/docs/reference/engine/classes/Model.md) is made up of multiple parts). For this reason when dealing custom damage using the [Explosion.Hit](/docs/reference/engine/classes/Explosion.md) event it's recommended to implement a check to see if the character has already been hit by the [Explosion](/docs/reference/engine/classes/Explosion.md). *Security: None · Capabilities: AvatarBehavior* **Parameters:** | Name | Type | Description | |------|------|-------------| | `part` | `BasePart` | The [BasePart](/docs/reference/engine/classes/BasePart.md) hit by the [Explosion](/docs/reference/engine/classes/Explosion.md). | | `distance` | `float` | The distance of the hit from [Explosion.Position](/docs/reference/engine/classes/Explosion.md). | **Custom Explosion** This sample contains a simple function that creates a custom explosion. The custom explosion will not break joints as Explosion.DestroyJointRadiusPercent is equal to 0. This means Humanoids will not be instantly killed as their neck joints are broken. In this example explosion.BlastPressure is equal to zero so as not to apply force or damage terrain but this can be changed. The Explosion.Hit event is used to listen for contact. Once contact has been made the code will look for the parent model of the BasePart hit by the explosion. If that model exists, hasn't already been damaged and has a Humanoid in it damage will be applied based on distance from the explosion's origin. ```lua local function customExplosion(position, radius, maxDamage) local explosion = Instance.new("Explosion") explosion.BlastPressure = 0 -- this could be set higher to still apply velocity to parts explosion.DestroyJointRadiusPercent = 0 -- joints are safe explosion.BlastRadius = radius explosion.Position = position -- set up a table to track the models hit local modelsHit = {} -- listen for contact explosion.Hit:Connect(function(part, distance) local parentModel = part.Parent if parentModel then -- check to see if this model has already been hit if modelsHit[parentModel] then return end -- log this model as hit modelsHit[parentModel] = true -- look for a humanoid local humanoid = parentModel:FindFirstChild("Humanoid") if humanoid then local distanceFactor = distance / explosion.BlastRadius -- get the distance as a value between 0 and 1 distanceFactor = 1 - distanceFactor -- flip the amount, so that lower == closer == more damage humanoid:TakeDamage(maxDamage * distanceFactor) -- TakeDamage to respect ForceFields end end end) explosion.Parent = game.Workspace -- Roblox removes explosions after a few seconds, but does not destroy them. -- To ensure our .Hit connection gets disconnected, destroy the explosion once it's removed. explosion.AncestryChanged:Connect(function() if not explosion.Parent then explosion:Destroy() end end) end customExplosion(Vector3.new(0, 10, 0), 12, 50) ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: FaceControls last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Animation summary: "The FaceControls object defines a set of properties for controlling the facial expressions of a Dynamic Head." --- # Class: FaceControls > The [FaceControls](/docs/reference/engine/classes/FaceControls.md) object defines a set of properties for controlling > the facial expressions of a Dynamic Head. ## Description The [FaceControls](/docs/reference/engine/classes/FaceControls.md) object defines a set of properties for controlling the facial expressions of a character head capable of animation. The FaceControls properties are based on the Facial Action Coding System (FACS), a comprehensive system for describing all visually discernible facial movement based on anatomy. [FaceControls](/docs/reference/engine/classes/FaceControls.md) properties can only be set between 0 and 1. Different combinations of the [FaceControls](/docs/reference/engine/classes/FaceControls.md) property values create different facial expressions. Recording multiple facial expressions over time creates facial animation. ## What is an Animatable Head? An animatable head is a [MeshPart](/docs/reference/engine/classes/MeshPart.md) that implements a facial rig and is capable of playing facial animations and triggering facial expressions. A [FaceControls](/docs/reference/engine/classes/FaceControls.md) object that is a child of a head [MeshPart](/docs/reference/engine/classes/MeshPart.md) can change the facial expressions of the head. A head consists of the following three components: - Skinned MeshPart instance for the head geometry with an internal rig that deforms this skinned MeshPart - FaceControls instance that drives the internal rig when properties such as FaceControls.JawDrop are changed. - Cage [WrapTarget](/docs/reference/engine/classes/WrapTarget.md) instance for tight fitting facial accessories In a third-party modeling tool, such as Blender or Maya, an artist can create a joint-driven facial rig, pose the joints to match each of the individual FACS controls, and save as an FBX. When a head `.FBX` is imported in Studio, a facs-to-joint mapping is created. This mapping deforms the mesh geometry when FaceControls properties are changed. The mapping and the facial rig (including [Bone](/docs/reference/engine/classes/Bone.md) instances) are not exposed to developers and can only be accessed through the FaceControls instance. The [MeshPart](/docs/reference/engine/classes/MeshPart.md) for a Dynamic Head looks and behaves the same as a regular [MeshPart](/docs/reference/engine/classes/MeshPart.md) except when a FaceControls instance is a child of the MeshPart. Editing the properties of the FaceControls deforms the MeshPart's geometry. These properties are available to animate in the Animation Editor. See [Facial animation](/docs/en-us/avatar/dynamic-heads.md) for more information on usage and creation of an animatable head. ## Animatable Heads in the Marketplace If you are publishing your head to the Marketplace, your head asset must include a minimum subset of face controls. Roblox's publishing validation rejects assets without the following required poses: - EyesLookDown - EyesLookLeft - EyesLookRight - EyesLookUp - JawDrop - LeftEyeClosed - LeftLipCornerPuller - LeftLipStretcher - LeftLowerLipDepressor - LeftUpperLipRaiser - LipsTogether - Pucker - RightEyeClosed - RightLipCornerPuller - RightLipStretcher - RightLowerLipDepressor - RightUpperLipRaiser See [FACS poses reference](/docs/en-us/avatar/dynamic-heads/facs-poses-reference.md) for more information on usage and creation of an animatable head. ## Properties ### Property: FaceControls.ChinRaiser ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Mouth", "capabilities": [ "Animation" ] } ``` Raises the chin up; moves the lower lip upwards ### Property: FaceControls.ChinRaiserUpperLip ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Mouth", "capabilities": [ "Animation" ] } ``` Moves the upper lip when ChinRaiser is engaged and touching the upper lip ### Property: FaceControls.Corrugator ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Brows", "capabilities": [ "Animation" ] } ``` Brings the left and right brows inward together ### Property: FaceControls.EyesLookDown ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Eyes", "capabilities": [ "Animation" ] } ``` Moves gaze down. This is a required pose for avatars. ### Property: FaceControls.EyesLookLeft ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Eyes", "capabilities": [ "Animation" ] } ``` Moves gaze left. This is a required pose for avatars. ### Property: FaceControls.EyesLookRight ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Eyes", "capabilities": [ "Animation" ] } ``` Moves gaze right. This is a required pose for avatars. ### Property: FaceControls.EyesLookUp ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Eyes", "capabilities": [ "Animation" ] } ``` Moves gaze up. This is a required pose for avatars. ### Property: FaceControls.FlatPucker ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Mouth", "capabilities": [ "Animation" ] } ``` Also known as lip tightener; brings the corners of the mouth inward and pressing the lips back against the teeth ### Property: FaceControls.Funneler ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Mouth", "capabilities": [ "Animation" ] } ``` Makes a 'O' shape with the mouth ### Property: FaceControls.JawDrop ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Jaw", "capabilities": [ "Animation" ] } ``` Lowers the jaw downward opening the mouth. This is a required pose for avatars. ### Property: FaceControls.JawLeft ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Jaw", "capabilities": [ "Animation" ] } ``` Moves mouth and jaw to the left (character left). ### Property: FaceControls.JawRight ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Jaw", "capabilities": [ "Animation" ] } ``` Moves mouth and jaw to the right (character right) ### Property: FaceControls.LeftBrowLowerer ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Brows", "capabilities": [ "Animation" ] } ``` Lowers the left brow down ### Property: FaceControls.LeftCheekPuff ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Mouth", "capabilities": [ "Animation" ] } ``` Puffs up the left cheek ### Property: FaceControls.LeftCheekRaiser ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Eyes", "capabilities": [ "Animation" ] } ``` Squints the left eye ### Property: FaceControls.LeftDimpler ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Mouth", "capabilities": [ "Animation" ] } ``` Moves the corners of the mouth back in Z ### Property: FaceControls.LeftEyeClosed ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Eyes", "capabilities": [ "Animation" ] } ``` Closes the left eyelid. This is a required pose for avatars. ### Property: FaceControls.LeftEyeUpperLidRaiser ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Eyes", "capabilities": [ "Animation" ] } ``` Raises the left eyelid upwards to reveal more of the eye white above the iris ### Property: FaceControls.LeftInnerBrowRaiser ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Brows", "capabilities": [ "Animation" ] } ``` Raises the interior half of the left brow upwards ### Property: FaceControls.LeftLipCornerDown ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Mouth", "capabilities": [ "Animation" ] } ``` Lowers the corners of the mouth downwards in a frown ### Property: FaceControls.LeftLipCornerPuller ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Mouth", "capabilities": [ "Animation" ] } ``` Raises the corners of the mouth upwards in a smile. This is a required pose for avatars. ### Property: FaceControls.LeftLipStretcher ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Mouth", "capabilities": [ "Animation" ] } ``` Stretches the corners of the mouth apart. This is a required pose for avatars. ### Property: FaceControls.LeftLowerLipDepressor ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Mouth", "capabilities": [ "Animation" ] } ``` Lowers the lower lip down away from the upper lip revealing the lower teeth. This is a required pose for avatars. ### Property: FaceControls.LeftNoseWrinkler ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Brows", "capabilities": [ "Animation" ] } ``` Raise the left nostril, pulls the brow down slightly, and wrinkles on the side of the nose ### Property: FaceControls.LeftOuterBrowRaiser ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Brows", "capabilities": [ "Animation" ] } ``` Raises the outer part of the left brow upwards ### Property: FaceControls.LeftUpperLipRaiser ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Mouth", "capabilities": [ "Animation" ] } ``` Raises the left upper lip away from the lower lip revealing the upper teeth. This is a required pose for avatars. ### Property: FaceControls.LipPresser ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Mouth", "capabilities": [ "Animation" ] } ``` Presses the lips together ### Property: FaceControls.LipsTogether ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Mouth", "capabilities": [ "Animation" ] } ``` Brings the lips together relative to JawDrop. This is a required pose for avatars. ### Property: FaceControls.LowerLipSuck ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Mouth", "capabilities": [ "Animation" ] } ``` Rolls the lower lip up over the teeth ### Property: FaceControls.MouthLeft ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Mouth", "capabilities": [ "Animation" ] } ``` Moves the mouth left ### Property: FaceControls.MouthRight ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Mouth", "capabilities": [ "Animation" ] } ``` Moves the mouth right ### Property: FaceControls.Pucker ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Mouth", "capabilities": [ "Animation" ] } ``` Makes a kiss-like shape with the mouth. This is a required pose for avatars. ### Property: FaceControls.RightBrowLowerer ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Brows", "capabilities": [ "Animation" ] } ``` Lowers the right brow down ### Property: FaceControls.RightCheekPuff ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Mouth", "capabilities": [ "Animation" ] } ``` Puffs up the right cheek ### Property: FaceControls.RightCheekRaiser ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Eyes", "capabilities": [ "Animation" ] } ``` Squints the right eye ### Property: FaceControls.RightDimpler ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Mouth", "capabilities": [ "Animation" ] } ``` Moves the corners of the mouth back in Z ### Property: FaceControls.RightEyeClosed ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Eyes", "capabilities": [ "Animation" ] } ``` Closes the right eyelid. This is a required pose for avatars. ### Property: FaceControls.RightEyeUpperLidRaiser ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Eyes", "capabilities": [ "Animation" ] } ``` Raises the right eyelid upwards to reveal more of the eye white above the iris ### Property: FaceControls.RightInnerBrowRaiser ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Brows", "capabilities": [ "Animation" ] } ``` Raises the interior half of the right brow upwards ### Property: FaceControls.RightLipCornerDown ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Mouth", "capabilities": [ "Animation" ] } ``` Lowers the corners of the mouth downwards in a frown ### Property: FaceControls.RightLipCornerPuller ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Mouth", "capabilities": [ "Animation" ] } ``` Raises the corners of the mouth upwards in a smile. This is a required pose for avatars. ### Property: FaceControls.RightLipStretcher ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Mouth", "capabilities": [ "Animation" ] } ``` Stretches the corners of the mouth apart. This is a required pose for avatars. ### Property: FaceControls.RightLowerLipDepressor ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Mouth", "capabilities": [ "Animation" ] } ``` Lowers the lower lip down away from the upper lip revealing the lower teeth. This is a required pose for avatars. ### Property: FaceControls.RightNoseWrinkler ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Brows", "capabilities": [ "Animation" ] } ``` Raises the right nostril, pulls the brow down slightly, and wrinkles on the side of the nose ### Property: FaceControls.RightOuterBrowRaiser ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Brows", "capabilities": [ "Animation" ] } ``` Raises the outer part of the right brow upwards ### Property: FaceControls.RightUpperLipRaiser ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Mouth", "capabilities": [ "Animation" ] } ``` Raises the right upper lip away from the lower lip revealing the upper teeth. This is a required pose for avatars. ### Property: FaceControls.TongueDown ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Tongue", "capabilities": [ "Animation" ] } ``` Bends the tongue down ### Property: FaceControls.TongueOut ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Tongue", "capabilities": [ "Animation" ] } ``` Extends the tip of the tongue out of the mouth ### Property: FaceControls.TongueUp ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Tongue", "capabilities": [ "Animation" ] } ``` Bends the tongue up ### Property: FaceControls.UpperLipSuck ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Mouth", "capabilities": [ "Animation" ] } ``` Rolls the upper lip around the teeth ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: FaceInstance last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotBrowsable summary: "An abstract class from which the Decal and Texture classes inherit." --- # Class: FaceInstance > An abstract class from which the [Decal](/docs/reference/engine/classes/Decal.md) and [Texture](/docs/reference/engine/classes/Texture.md) classes > inherit. ## Description [FaceInstance](/docs/reference/engine/classes/FaceInstance.md) is an abstract class from which the [Decal](/docs/reference/engine/classes/Decal.md) and [Texture](/docs/reference/engine/classes/Texture.md) classes inherit. ## Properties ### Property: FaceInstance.Face ```json { "type": "NormalId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` Sets which face of the parent [BasePart](/docs/reference/engine/classes/BasePart.md) the object appears on. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Feature last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable summary: "The base class for the legacy motor system." --- # Class: Feature > The base class for the legacy motor system. ## Properties ### Property: Feature.FaceId ```json { "type": "NormalId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Physics" ] } ``` Sets what side of the Parent the object is on. ### Property: Feature.InOut ```json { "type": "InOut", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Physics" ] } ``` Controls how the Feature is positioned on it's parent's surface, in correspondence to the Feature's [Feature.LeftRight](/docs/reference/engine/classes/Feature.md) and [Feature.TopBottom](/docs/reference/engine/classes/Feature.md) properties. ### Property: Feature.LeftRight ```json { "type": "LeftRight", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Physics" ] } ``` Controls whether the feature is shifted to the left, center, or right on the surface. ### Property: Feature.TopBottom ```json { "type": "TopBottom", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Physics" ] } ``` Controls whether the feature is shifted to the top, center, or bottom on the surface. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: FeatureRestrictionManager last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service --- # Class: FeatureRestrictionManager ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: File last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated summary: "An asset loaded from a file on disk." --- # Class: File > An asset loaded from a file on disk. ## Description An object that represents an asset loaded from a file on a local disk. Files generate a temporary asset ID in the form `rbxtemp://` which can be used in Studio without uploading the asset, but will be destroyed when the [File](/docs/reference/engine/classes/File.md) is destroyed or when the Studio session ends. Temporary asset IDs are not shared across [collaborative](/docs/en-us/projects/collaboration.md) sessions. The default [Name](/docs/reference/engine/classes/Instance.md) of a [File](/docs/reference/engine/classes/File.md) instance will be the filename on disk, excluding the path but including the extension. ## Properties ### Property: File.Size *(hidden)* ```json { "type": "int64", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AssetRead" ] } ``` The file size (in bytes) of the local file associated with this [File](/docs/reference/engine/classes/File.md). ## Methods ### Method: File:GetBinaryContents **Signature:** `File:GetBinaryContents(): string` This function is used to read the contents of the [File](/docs/reference/engine/classes/File.md) as a raw binary string. This allows the file to be uploaded to web endpoints, or to be processed by plugins. *Security: PluginSecurity · Thread Safety: Unsafe · Capabilities: AssetRead* **Returns:** `string` — A raw binary string representation of the [File](/docs/reference/engine/classes/File.md) contents. ### Method: File:GetTemporaryId **Signature:** `File:GetTemporaryId(): ContentId` This function is used to retrieve a temporary asset ID associated with the [File](/docs/reference/engine/classes/File.md). The ID can be used like `rbxassetid://`, for example it can be assigned to the [Image](/docs/reference/engine/classes/ImageLabel.md) property of an [ImageLabel](/docs/reference/engine/classes/ImageLabel.md). Throws an error if the file does not exist on disk. *Security: PluginSecurity · Thread Safety: Unsafe · Capabilities: AssetRead* **Returns:** `ContentId` — The temporary asset ID. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: FileMesh last_updated: 2026-06-29T19:34:07Z inherits: - DataModelMesh - Instance - Object type: class memory_category: BaseParts summary: "The FileMesh object applies a mesh to a BasePart when parented to it. Its properties are inherited by the SpecialMesh object." --- # Class: FileMesh > The FileMesh object applies a mesh to a [BasePart](/docs/reference/engine/classes/BasePart.md) when parented to it. > Its properties are inherited by the [SpecialMesh](/docs/reference/engine/classes/SpecialMesh.md) object. ## Description The FileMesh object applies a textured mesh to a [BasePart](/docs/reference/engine/classes/BasePart.md) when parented to it. Its properties are inherited by the [SpecialMesh](/docs/reference/engine/classes/SpecialMesh.md) object. ## What is a FileMesh? FileMeshes allow user uploaded meshes to be applied to a [BasePart](/docs/reference/engine/classes/BasePart.md). The mesh that is applied is dependent on the [FileMesh.MeshId](/docs/reference/engine/classes/FileMesh.md) property. A texture can also be applied to this mesh using [FileMesh.TextureId](/docs/reference/engine/classes/FileMesh.md). Although it is not an abstract class, and can be used by developers, all [FileMesh](/docs/reference/engine/classes/FileMesh.md) properties are inherited by the [SpecialMesh](/docs/reference/engine/classes/SpecialMesh.md) object. A [SpecialMesh](/docs/reference/engine/classes/SpecialMesh.md) behaves identically to the FileMesh object when its [SpecialMesh.MeshType](/docs/reference/engine/classes/SpecialMesh.md) is set to 'FileMesh'. Although both objects are functional, the [SpecialMesh](/docs/reference/engine/classes/SpecialMesh.md) object is the official supported class. For more information on using meshes, please see the [SpecialMesh](/docs/reference/engine/classes/SpecialMesh.md) page. ## Code Samples **FileMesh Offset and Scale** In this code sample a `BasePart` is instanced with a `FileMesh`. The [DataModelMesh.Scale](/docs/reference/engine/classes/DataModelMesh.md) and [DataModelMesh.Offset](/docs/reference/engine/classes/DataModelMesh.md) properties of the `FileMesh` are then animated using `TweenService`. ```lua local TweenService = game:GetService("TweenService") local part = Instance.new("Part") part.Size = Vector3.new(4, 8, 4) part.Position = Vector3.new(0, 4, 0) part.Anchored = true part.CanCollide = false local mesh = Instance.new("FileMesh") -- advised to use SpecialMesh instead mesh.MeshId = "rbxassetid://1086413449" mesh.TextureId = "rbxassetid://1461576423" mesh.Offset = Vector3.new(0, 0, 0) mesh.Scale = Vector3.new(4, 4, 4) mesh.Parent = part local box = Instance.new("SelectionBox") box.Adornee = part box.Parent = part part.Parent = workspace local tween = TweenService:Create( mesh, TweenInfo.new(1, Enum.EasingStyle.Linear, Enum.EasingDirection.Out, -1, true, 0), { Scale = Vector3.new(1, 1, 1), Offset = Vector3.new(0, 3, 0) } ) tween:Play() ``` ## Properties ### Property: FileMesh.MeshId ```json { "type": "ContentId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` The MeshId is the content ID of the mesh that is to be displayed. The content ID for a mesh is generated when a developer uploads a mesh to Roblox. **Mesh VertexColor** In this code sample a `BasePart` is instanced with a `SpecialMesh`. The [DataModelMesh.VertexColor](/docs/reference/engine/classes/DataModelMesh.md) property of the `SpecialMesh` is then animated using `TweenService`. ```lua local TweenService = game:GetService("TweenService") -- instance a part and a mesh local part = Instance.new("Part") part.Size = Vector3.new(4, 8, 4) part.Position = Vector3.new(0, 4, 0) part.Anchored = true part.CanCollide = false local mesh = Instance.new("SpecialMesh") mesh.MeshType = Enum.MeshType.FileMesh mesh.MeshId = "rbxassetid://1086413449" mesh.TextureId = "rbxassetid://1461576423" mesh.Offset = Vector3.new(0, 0, 0) mesh.Scale = Vector3.new(4, 4, 4) mesh.VertexColor = Vector3.new(1, 1, 1) mesh.Parent = part -- parent part to workspace part.Parent = workspace -- create tweens local tweenInfo = TweenInfo.new(1, Enum.EasingStyle.Linear, Enum.EasingDirection.Out) local blackTween = TweenService:Create(mesh, tweenInfo, { VertexColor = Vector3.new(0, 0, 0) }) local redTween = TweenService:Create(mesh, tweenInfo, { VertexColor = Vector3.new(1, 0, 0) }) local greenTween = TweenService:Create(mesh, tweenInfo, { VertexColor = Vector3.new(0, 1, 0) }) local blueTween = TweenService:Create(mesh, tweenInfo, { VertexColor = Vector3.new(0, 0, 1) }) local resetTween = TweenService:Create(mesh, tweenInfo, { VertexColor = Vector3.new(1, 1, 1) }) -- animate while true do blackTween:Play() blackTween.Completed:Wait() redTween:Play() redTween.Completed:Wait() greenTween:Play() greenTween.Completed:Wait() blueTween:Play() blueTween.Completed:Wait() resetTween:Play() resetTween.Completed:Wait() task.wait() end ``` ### Property: FileMesh.TextureId ```json { "type": "ContentId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` The TextureId is the content ID of the image that is to be applied to used for the meshes texture. When the TextureId property is set to an empty string, no texture will be applied to the mesh. #### How can I change the texture of a mesh? Using the TextureId property, the texture of a mesh can be changed without having to reupload the mesh. To do this, a new image will need to be uploaded to Roblox with the desired texture. The original texture image file can be obtained by exporting the mesh using the 'Export Selection' option in Roblox Studio. The image file will be saved alongside the exported .obj file. The new texture can then be re-uploaded to Roblox as a Decal and its content ID can be applied to the mesh using the TextureId property. #### How can I make a textured mesh? A mesh can only be textured if the mesh has been UV mapped. UV mapping refers to the practice of projecting a texture map onto a mesh. This cannot be done using Roblox Studio and has to be done using an external 3D modelling application such as [Blender][1]. [1]: https://www.blender.org/ **Mesh VertexColor** In this code sample a `BasePart` is instanced with a `SpecialMesh`. The [DataModelMesh.VertexColor](/docs/reference/engine/classes/DataModelMesh.md) property of the `SpecialMesh` is then animated using `TweenService`. ```lua local TweenService = game:GetService("TweenService") -- instance a part and a mesh local part = Instance.new("Part") part.Size = Vector3.new(4, 8, 4) part.Position = Vector3.new(0, 4, 0) part.Anchored = true part.CanCollide = false local mesh = Instance.new("SpecialMesh") mesh.MeshType = Enum.MeshType.FileMesh mesh.MeshId = "rbxassetid://1086413449" mesh.TextureId = "rbxassetid://1461576423" mesh.Offset = Vector3.new(0, 0, 0) mesh.Scale = Vector3.new(4, 4, 4) mesh.VertexColor = Vector3.new(1, 1, 1) mesh.Parent = part -- parent part to workspace part.Parent = workspace -- create tweens local tweenInfo = TweenInfo.new(1, Enum.EasingStyle.Linear, Enum.EasingDirection.Out) local blackTween = TweenService:Create(mesh, tweenInfo, { VertexColor = Vector3.new(0, 0, 0) }) local redTween = TweenService:Create(mesh, tweenInfo, { VertexColor = Vector3.new(1, 0, 0) }) local greenTween = TweenService:Create(mesh, tweenInfo, { VertexColor = Vector3.new(0, 1, 0) }) local blueTween = TweenService:Create(mesh, tweenInfo, { VertexColor = Vector3.new(0, 0, 1) }) local resetTween = TweenService:Create(mesh, tweenInfo, { VertexColor = Vector3.new(1, 1, 1) }) -- animate while true do blackTween:Play() blackTween.Completed:Wait() redTween:Play() redTween.Completed:Wait() greenTween:Play() greenTween.Completed:Wait() blueTween:Play() blueTween.Completed:Wait() resetTween:Play() resetTween.Completed:Wait() task.wait() end ``` ## Inherited Members ### From [DataModelMesh](/docs/reference/engine/classes/DataModelMesh.md) - **Property `Offset`** (`Vector3`): The Offset of a mesh determines the relative position from the - **Property `Scale`** (`Vector3`): The Scale of a mesh determines the size of the mesh relative to its - **Property `VertexColor`** (`Vector3`): Changes the hue of a mesh's texture, used with FileMesh.TextureId. ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Fire last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances summary: "A preconfigured particle emitter with the visual aesthetic of fire." --- # Class: Fire > A preconfigured particle emitter with the visual aesthetic of fire. ## Description [Fire](/docs/reference/engine/classes/Fire.md) is one of several preconfigured particle-emitting classes, alongside [Smoke](/docs/reference/engine/classes/Smoke.md), [Sparkles](/docs/reference/engine/classes/Sparkles.md), and others. Like the others, it emits particles when parented to a [BasePart](/docs/reference/engine/classes/BasePart.md) or an [Attachment](/docs/reference/engine/classes/Attachment.md) while [Enabled](/docs/reference/engine/classes/Fire.md). This object is useful to create a quick visual effect for fire, but for more detailed work it's recommended that you use a [ParticleEmitter](/docs/reference/engine/classes/ParticleEmitter.md) instead. Particles emit from the center of the parent in an upward (**+Y**) direction, but a negative [Heat](/docs/reference/engine/classes/Fire.md) value may be used to emit particles downward (**-Y**). Using an [Attachment](/docs/reference/engine/classes/Attachment.md) as the parent instead of a [BasePart](/docs/reference/engine/classes/BasePart.md) allows for the emission position/direction to be modified by changing the [Attachment.CFrame](/docs/reference/engine/classes/Attachment.md) or related properties. [Fire](/docs/reference/engine/classes/Fire.md) objects consist of two emitters, each affected in various ways by the [Size](/docs/reference/engine/classes/Fire.md), [Heat](/docs/reference/engine/classes/Fire.md), [Color](/docs/reference/engine/classes/Fire.md), and [SecondaryColor](/docs/reference/engine/classes/Fire.md) properties. Particles which emit from the smaller, secondary emitter have a significantly longer lifetime (and rise farther) than those emitted by the primary emitter. When [Enabled](/docs/reference/engine/classes/Fire.md) is off, existing particles continue to render until they expire. However, if the [Fire](/docs/reference/engine/classes/Fire.md) object's [Parent](/docs/reference/engine/classes/Instance.md) is set to `nil`, all existing particles immediately disappear, similar to the behavior of [ParticleEmitter:Clear()](/docs/reference/engine/classes/ParticleEmitter.md). [Fire](/docs/reference/engine/classes/Fire.md) objects emit no light on their own. To help create a cohesive environment around a burning object, try adding a [PointLight](/docs/reference/engine/classes/PointLight.md) with an orange [Color](/docs/reference/engine/classes/Light.md). ## Code Samples **Lighting Torches** This code sample adds [Fire](/docs/reference/engine/classes/Fire.md) to all [BasePart](/docs/reference/engine/classes/BasePart.md) instances in the [Workspace](/docs/reference/engine/classes/Workspace.md) named `Torch`. ```lua local Workspace = game:GetService("Workspace") for _, descendant in Workspace:GetDescendants() do if descendant.Name == "Torch" and descendant:IsA("BasePart") then local fire = Instance.new("Fire") fire.Heat = 10 fire.SecondaryColor = Color3.new(1, 1, 1) fire.Size = math.max(descendant.Size.X, descendant.Size.Z) -- Pick the larger of the two dimensions fire.Parent = descendant end end ``` **Expected output:** When run, parts in the workspace named `Torch` should each have a `Class.Fire` object parented to them. The fire should be about the size of the part. ## Properties ### Property: Fire.Color ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` This property determines the color of the larger particles emitted by a [Fire](/docs/reference/engine/classes/Fire.md) object. It is essentially the color of the outer portion of the flame. In general, the cooler flames are on the outside of a fire. Therefore, fire looks more realistic if the outer portions are red or orange-yellow. A fire that is bright all throughout doesn't look very realistic, so avoid setting this property to yellow or white. ### Property: Fire.Enabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` This property, much like [ParticleEmitter.Enabled](/docs/reference/engine/classes/ParticleEmitter.md), determines whether flame particles are emitted. Any particles already emitted will continue to render until their lifetime expires. Since all fire particles are destroyed when the [Fire](/docs/reference/engine/classes/Fire.md) object's [Parent](/docs/reference/engine/classes/Instance.md) is set to `nil`, this property is useful in allowing existing particles the opportunity to expire before destroying the [Fire](/docs/reference/engine/classes/Fire.md) object altogether (see the code example below). ``` local Debris = game:GetService("Debris") local function douseFlames(fire) fire.Enabled = false -- No more new particles Debris:AddItem(fire, 2) -- Remove the object after existing particles have expired end douseFlames(part.Fire) ``` ### Property: Fire.Heat ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` This property determines how fast particles are emitted from the [Fire](/docs/reference/engine/classes/Fire.md) object. It is limited to the range of `-25` to `25` inclusive. Positive values are in the top (**+Y**) direction of the parent [BasePart](/docs/reference/engine/classes/BasePart.md) or [Attachment](/docs/reference/engine/classes/Attachment.md), while negative values are in the downward (**-Y**) direction. It also affects the [ParticleEmitter.Acceleration](/docs/reference/engine/classes/ParticleEmitter.md) of the inner particles. ### Property: Fire.SecondaryColor ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` This property determines the color of the smaller particles emitted by a [Fire](/docs/reference/engine/classes/Fire.md) object. It is essentially the color of the inner portion of the flame. Note that the inner particles use a [ParticleEmitter.LightEmission](/docs/reference/engine/classes/ParticleEmitter.md) of `1`, so darker colors will instead cause the particles to appear transparent, and black will stop rendering inner particles altogether. ### Property: Fire.Size ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` This property determines the size of the flame particles. It must be in the range of `2` to `30`. Unlike [ParticleEmitter.Size](/docs/reference/engine/classes/ParticleEmitter.md), the actual size of the flames will not match 1:1 with the equivalent size in studs; it is somewhat smaller. If you add a [PointLight](/docs/reference/engine/classes/PointLight.md) as a sibling to the [Fire](/docs/reference/engine/classes/Fire.md) object to generate light, try setting the light's [Brightness](/docs/reference/engine/classes/PointLight.md) and [Range](/docs/reference/engine/classes/PointLight.md) proportional to this property so that larger flames produce more light. ### Property: Fire.TimeScale ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` A value between `0` and `1` than controls the speed of the particle effect. `1` runs at normal speed, `0.5` runs at half speed, and `0` freezes time. ### Property: Fire.size ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` > **Deprecated:** This property is a deprecated variant of [Fire.Size](/docs/reference/engine/classes/Fire.md) which should be used instead. ### Property: Fire.LocalTransparencyModifier *(hidden)* ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` This property is a multiplier for the [Fire](/docs/reference/engine/classes/Fire.md) object's transparency that is only visible to the local client. It does not replicate from client to server and is useful for when the object should appear differently for a specific client. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Flag last_updated: 2026-06-29T19:34:07Z inherits: - Tool - BackpackItem - Model - PVInstance - Instance - Object type: class memory_category: BaseParts tags: - Deprecated summary: "The Flag object helps you make 'capture the flag' style games." --- # Class: Flag > The Flag object helps you make 'capture the flag' style games. ## Description The Flag is a unit spawned with a [FlagStand](/docs/reference/engine/classes/FlagStand.md) object, and will respawn when captured. When a player touches this object's Handle, which must be a child of the Flag object, which is a Part named "Handle", the flag will be added to the player's backpack and will appear in their hand. A player cannot select other weapons while carrying a flag, and can drop the flag at anytime by pressing "Backspace" on the keyboard. If the player carrying a flag steps onto another FlagStand of a different team color, the flag will be removed from the player's backpack and a point will be added to the user's leaderstats, if provided. The flag will then regenerate at the originating flag stand. ## Properties ### Property: Flag.TeamColor ```json { "type": "BrickColor", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic", "Input" ] } ``` The [Team](/docs/reference/engine/classes/Team.md) this flag is for. Corresponds with the TeamColors in the [Teams](/docs/reference/engine/classes/Teams.md) service. The [Flag](/docs/reference/engine/classes/Flag.md) and [FlagStand](/docs/reference/engine/classes/FlagStand.md) objects were created to allow developers to make 'capture the flag' style games quickly. ## Inherited Members ### From [Tool](/docs/reference/engine/classes/Tool.md) - **Property `CanBeDropped`** (`boolean`): Controls whether the player can drop the tool. - **Property `Enabled`** (`boolean`): Relates to whether or not the tool can be used. - **Property `Grip`** (`CFrame`): Stores the tool's "grip" properties as one CFrame. - **Property `GripForward`** (`Vector3`): Represents the `R02`, `R12`, and `R22` values of the grip *(hidden)* - **Property `GripPos`** (`Vector3`): The positional offset of the tool's weld matrix. *(hidden)* - **Property `GripRight`** (`Vector3`): Represents the `R00`, `R10`, and `R20` values of the grip *(hidden)* - **Property `GripUp`** (`Vector3`): Represents the `R01`, `R11`, and `R21` values of the grip *(hidden)* - **Property `ManualActivationOnly`** (`boolean`): Controls whether the Tool can be activated without executing - **Property `RequiresHandle`** (`boolean`): Determines whether a Tool functions without a handle. - **Property `ToolTip`** (`string`): Controls the message displayed when the player's mouse hovers over the - **Method `Activate(): ()`**: Simulates activation of the Tool. - **Method `Deactivate(): ()`**: Simulates deactivation of the Tool. - **Event `Activated`**: Fires when the player clicks while the tool is equipped. - **Event `Deactivated`**: Fires when the player releases their click while the tool is equipped and - **Event `Equipped`**: Fires when the tool is equipped. - **Event `Unequipped`**: Fires when the tool is unequipped. ### From [BackpackItem](/docs/reference/engine/classes/BackpackItem.md) - **Property `TextureContent`** (`Content`): The texture icon that is displayed for a tool in the player's backpack. - **Property `TextureId`** (`ContentId`): The texture icon that is displayed for a tool in the player's backpack. ### From [Model](/docs/reference/engine/classes/Model.md) - **Property `LevelOfDetail`** (`ModelLevelOfDetail`): Sets the level of detail on the model for experiences with instance - **Property `ModelStreamingMode`** (`ModelStreamingMode`): Controls the model streaming behavior on Models when - **Property `PrimaryPart`** (`BasePart`): The primary part of the Model, or `nil` if not explicitly set. - **Property `Scale`** (`float`): Editor-only property used to scale the model around its pivot. Setting - **Property `WorldPivot`** (`CFrame`): Determines where the pivot of a Model which does **not** have a - **Method `AddPersistentPlayer(playerInstance?: Player): ()`**: Sets this model to be persistent for the specified player. - **Method `BreakJoints(): ()`**: Breaks connections between `BaseParts`, including surface connections with *(deprecated)* - **Method `breakJoints(): ()`**: *(deprecated)* - **Method `GetBoundingBox(): Tuple`**: Returns a description of a volume that contains all parts of a Model. - **Method `GetExtentsSize(): Vector3`**: Returns the size of the smallest bounding box that contains all of the - **Method `GetModelCFrame(): CFrame`**: This value historically returned the CFrame of a central position in the *(deprecated)* - **Method `GetModelSize(): Vector3`**: Returns the Vector3 size of the Model. *(deprecated)* - **Method `GetPersistentPlayers(): List`**: Returns all the Player objects that this model object is - **Method `GetPrimaryPartCFrame(): CFrame`**: Returns the CFrame of the model's Model.PrimaryPart. *(deprecated)* - **Method `GetScale(): float`**: Returns the canonical scale of the model, which defaults to 1 for newly - **Method `MakeJoints(): ()`**: Goes through all BaseParts in the Model. If any *(deprecated)* - **Method `makeJoints(): ()`**: *(deprecated)* - **Method `move(location: Vector3): ()`**: *(deprecated)* - **Method `MoveTo(position: Vector3): ()`**: Moves the PrimaryPart to the given position. If - **Method `moveTo(location: Vector3): ()`**: *(deprecated)* - **Method `RemovePersistentPlayer(playerInstance?: Player): ()`**: Makes this model no longer persistent for the specified player. - **Method `ResetOrientationToIdentity(): ()`**: Resets the rotation of the model's parts to the previously set identity *(deprecated)* - **Method `ScaleTo(newScaleFactor: float): ()`**: Sets the scale factor of the model, adjusting the sizing and location of - **Method `SetIdentityOrientation(): ()`**: Sets the identity rotation of the given model, allowing you to reset the *(deprecated)* - **Method `SetPrimaryPartCFrame(cframe: CFrame): ()`**: Sets the BasePart.CFrame of the model's Model.PrimaryPart. *(deprecated)* - **Method `TranslateBy(delta: Vector3): ()`**: Shifts a Model by the given Vector3 offset, preserving ### From [PVInstance](/docs/reference/engine/classes/PVInstance.md) - **Property `Origin`** (`CFrame`): - **Property `Pivot Offset`** (`CFrame`): - **Method `GetPivot(): CFrame`**: Gets the pivot of a PVInstance. - **Method `PivotTo(targetCFrame: CFrame): ()`**: Transforms the PVInstance along with all of its descendant ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: FlagStand last_updated: 2026-06-29T19:34:07Z inherits: - Part - FormFactorPart - BasePart - PVInstance - Instance - Object type: class memory_category: BaseParts tags: - Deprecated summary: "The FlagStand object helps you make 'capture the flag' style games." --- # Class: FlagStand > The FlagStand object helps you make 'capture the flag' style games. ## Description The [Flag](/docs/reference/engine/classes/Flag.md) and [FlagStand](/docs/reference/engine/classes/FlagStand.md) objects allow you to make 'Capture the Flag' style games quickly. ## Properties ### Property: FlagStand.TeamColor ```json { "type": "BrickColor", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` The Team that owns the [FlagStand](/docs/reference/engine/classes/FlagStand.md). Corresponds with the TeamColors in the [Teams](/docs/reference/engine/classes/Teams.md) service. The [Flag](/docs/reference/engine/classes/Flag.md) and [FlagStand](/docs/reference/engine/classes/FlagStand.md) objects were created to allow developers to make 'capture the flag' style games quickly. ## Events ### Event: FlagStand.FlagCaptured **Signature:** `FlagStand.FlagCaptured(player: Instance)` This event fires when a player bearing an opposing flag, and having the same [Player.TeamColor](/docs/reference/engine/classes/Player.md) as the stand, touches the [FlagStand](/docs/reference/engine/classes/FlagStand.md). The [Flag](/docs/reference/engine/classes/Flag.md) and [FlagStand](/docs/reference/engine/classes/FlagStand.md) objects were created to allow developers to make 'capture the flag' style games quickly. *Security: None · Capabilities: Basic* **Parameters:** | Name | Type | Description | |------|------|-------------| | `player` | `Instance` | | ## Inherited Members ### From [Part](/docs/reference/engine/classes/Part.md) - **Property `Shape`** (`PartType`): Sets the overall shape of the object. ### From [FormFactorPart](/docs/reference/engine/classes/FormFactorPart.md) - **Property `FormFactor`** (`FormFactor`): *(deprecated)* - **Property `formFactor`** (`FormFactor`): *(deprecated, hidden)* ### From [BasePart](/docs/reference/engine/classes/BasePart.md) - **Property `Anchored`** (`boolean`): Determines whether a part is immovable by physics. - **Property `AssemblyAngularVelocity`** (`Vector3`): The angular velocity of the part's assembly. - **Property `AssemblyCenterOfMass`** (`Vector3`): The center of mass of the part's assembly in world space. - **Property `AssemblyLinearVelocity`** (`Vector3`): The linear velocity of the part's assembly. - **Property `AssemblyMass`** (`float`): The total mass of the part's assembly. - **Property `AssemblyRootPart`** (`BasePart`): A reference to the root part of the assembly. - **Property `AudioCanCollide`** (`boolean`): Determines whether the part will physically interact with audio - **Property `BackParamA`** (`float`): Determines the first parameter for the SurfaceType on the Back face of a *(deprecated, hidden)* - **Property `BackParamB`** (`float`): Determines the second parameter for the SurfaceType on the Back face of a *(deprecated, hidden)* - **Property `BackSurface`** (`SurfaceType`): Determines the type of surface for the back face of a part. - **Property `BackSurfaceInput`** (`InputType`): Determines the kind of input for the Back face of a part. *(deprecated, hidden)* - **Property `BottomParamA`** (`float`): Determines the first parameter for the SurfaceType on the Bottom face of a *(deprecated, hidden)* - **Property `BottomParamB`** (`float`): Determines the second parameter for the SurfaceType on the Bottom face of *(deprecated, hidden)* - **Property `BottomSurface`** (`SurfaceType`): Determines the type of surface for the bottom face of a part. - **Property `BottomSurfaceInput`** (`InputType`): Determines the kind of input for the Bottom face of a part. *(deprecated, hidden)* - **Property `BrickColor`** (`BrickColor`): Determines the color of a part. - **Property `brickColor`** (`BrickColor`): *(deprecated)* - **Property `CanCollide`** (`boolean`): Determines whether a part may collide with other parts. - **Property `CanQuery`** (`boolean`): Determines whether the part is considered during spatial query operations. - **Property `CanTouch`** (`boolean`): Determines if Touched and - **Property `CastShadow`** (`boolean`): Determines whether or not a part casts a shadow. - **Property `CenterOfMass`** (`Vector3`): Describes the world position in which a part's center of mass is located. - **Property `CFrame`** (`CFrame`): Determines the position and orientation of the BasePart in the - **Property `CollisionGroup`** (`string`): Describes the name of a part's collision group. - **Property `CollisionGroupId`** (`int`): Describes the automatically set ID number of a part's collision group. *(deprecated)* - **Property `Color`** (`Color3`): Determines the color of a part. - **Property `CurrentPhysicalProperties`** (`PhysicalProperties`): Indicates the current physical properties of the part. - **Property `CustomPhysicalProperties`** (`PhysicalProperties`): Determines several physical properties of a part. - **Property `Elasticity`** (`float`): Used to control the Elasticity of the part, but it no longer does *(deprecated, hidden)* - **Property `EnableFluidForces`** (`boolean`): Used to enable or disable aerodynamic forces on parts and assemblies. - **Property `ExtentsCFrame`** (`CFrame`): The CFrame of the physical extents of the BasePart. - **Property `ExtentsSize`** (`Vector3`): The actual physical size of the BasePart as regarded by the - **Property `Friction`** (`float`): Used to control the Friction of the part, but now it no longer does *(deprecated, hidden)* - **Property `FrontParamA`** (`float`): Determines the first parameter for the SurfaceType on the Front face of a *(deprecated, hidden)* - **Property `FrontParamB`** (`float`): Determines the second parameter for the SurfaceType on the Front face of a *(deprecated, hidden)* - **Property `FrontSurface`** (`SurfaceType`): Determines the type of surface for the front face of a part. - **Property `FrontSurfaceInput`** (`InputType`): Determines the kind of input for the Front face of a part (-Z direction). *(deprecated, hidden)* - **Property `LeftParamA`** (`float`): Determines the first parameter for the SurfaceType on the Left face of a *(deprecated, hidden)* - **Property `LeftParamB`** (`float`): Determines the second parameter for the SurfaceType on the Left face of a *(deprecated, hidden)* - **Property `LeftSurface`** (`SurfaceType`): Determines the type of surface for the left face of a part. - **Property `LeftSurfaceInput`** (`InputType`): Determines the kind of input for the Left face of a part. *(deprecated, hidden)* - **Property `LocalTransparencyModifier`** (`float`): Determines a multiplier for BasePart.Transparency that is only *(hidden)* - **Property `Locked`** (`boolean`): Determines whether a part is selectable in Studio. - **Property `Mass`** (`float`): Describes the mass of the part, the product of its density and volume. - **Property `Massless`** (`boolean`): Determines whether the part contributes to the total mass or inertia of - **Property `Material`** (`Material`): Determines the texture and default physical properties of a part. - **Property `MaterialVariant`** (`string`): The name of MaterialVariant. - **Property `Orientation`** (`Vector3`): Describes the rotation of the part in the world. *(hidden)* - **Property `PivotOffset`** (`CFrame`): Specifies the offset of the part's pivot from its CFrame. - **Property `Position`** (`Vector3`): Describes the position of the part in the world. *(hidden)* - **Property `ReceiveAge`** (`float`): Time since last recorded physics update. *(hidden)* - **Property `Reflectance`** (`float`): Determines how much a part reflects the skybox. - **Property `ResizeableFaces`** (`Faces`): Describes the faces on which a part may be resized. - **Property `ResizeIncrement`** (`int`): Describes the smallest change in size allowable by the - **Property `RightParamA`** (`float`): Determines the first parameter for the SurfaceType on the Right face of a *(deprecated, hidden)* - **Property `RightParamB`** (`float`): Determines the second parameter for the SurfaceType on the Right face of a *(deprecated, hidden)* - **Property `RightSurface`** (`SurfaceType`): Determines the type of surface for the right face of a part. - **Property `RightSurfaceInput`** (`InputType`): Determines the kind of input for the Right face of a part (-X direction). *(deprecated, hidden)* - **Property `RootPriority`** (`int`): The main rule in determining the root part of an assembly. - **Property `Rotation`** (`Vector3`): The rotation of the part in degrees for the three axes. - **Property `RotVelocity`** (`Vector3`): Determines a part's change in orientation over time. *(deprecated, hidden)* - **Property `Size`** (`Vector3`): Determines the dimensions of a part (length, width, height). - **Property `SpecificGravity`** (`float`): The ratio of the part's density to the density of water determined by the *(deprecated)* - **Property `TopParamA`** (`float`): Determines the first parameter for the SurfaceType on the Top face of a *(deprecated, hidden)* - **Property `TopParamB`** (`float`): Determines the second parameter for the SurfaceType on the Top face of a *(deprecated, hidden)* - **Property `TopSurface`** (`SurfaceType`): Determines the type of surface for the top face of a part. - **Property `TopSurfaceInput`** (`InputType`): Determines the kind of input for the Top face of a part (+Y direction). *(deprecated, hidden)* - **Property `Transparency`** (`float`): Determines how much a part can be seen through (the inverse of part - **Property `Velocity`** (`Vector3`): Determines a part's change in position over time. *(deprecated, hidden)* - **Method `AngularAccelerationToTorque(angAcceleration: Vector3, angVelocity?: Vector3): Vector3`**: Returns the torque needed to achieve a given angular acceleration on this - **Method `ApplyAngularImpulse(impulse: Vector3): ()`**: Apply an angular impulse to the assembly. - **Method `ApplyImpulse(impulse: Vector3): ()`**: Apply an impulse to the assembly at the assembly's - **Method `ApplyImpulseAtPosition(impulse: Vector3, position: Vector3): ()`**: Apply an impulse to the assembly at specified position. - **Method `BreakJoints(): ()`**: Breaks any surface connection with any adjacent part, including *(deprecated)* - **Method `breakJoints(): ()`**: *(deprecated)* - **Method `CanCollideWith(part: BasePart): boolean`**: Returns whether the parts can collide with each other. - **Method `CanSetNetworkOwnership(): Tuple`**: Checks whether you can set a part's network ownership. - **Method `GetClosestPointOnSurface(position: Vector3): Vector3`**: Returns the closest point on the part's surface to the given point. - **Method `GetConnectedParts(recursive?: boolean): List`**: Returns a table of parts connected to the object by any kind of rigid - **Method `GetJoints(): Instances`**: Return all Joints or Constraints that is connected to this Part. - **Method `GetMass(): float`**: Returns the value of the Mass property. - **Method `getMass(): float`**: *(deprecated)* - **Method `GetNetworkOwner(): Instance`**: Returns the current player who is the network owner of this part, or `nil` - **Method `GetNetworkOwnershipAuto(): boolean`**: Returns true if the game engine automatically decides the network owner - **Method `GetNoCollisionConstraints(): Instances`**: - **Method `GetRenderCFrame(): CFrame`**: OBSOLETE. Returns a CFrame describing where the part is being rendered at. *(deprecated)* - **Method `GetRootPart(): Instance`**: Returns the base part of an assembly of parts. *(deprecated)* - **Method `GetTouchingParts(): Instances`**: Returns a table of all BasePart.CanCollide true parts that - **Method `GetVelocityAtPosition(position: Vector3): Vector3`**: Returns the linear velocity of the part's assembly at the given position - **Method `IntersectAsync(parts: Instances, collisionfidelity?: CollisionFidelity, renderFidelity?: RenderFidelity): Instance`**: Note: It is highly recommended to use the newer - **Method `IsGrounded(): boolean`**: Returns true if the object is connected to a part that will hold it in - **Method `MakeJoints(): ()`**: Creates a joint on any side of the object that has a surface ID that can *(deprecated)* - **Method `makeJoints(): ()`**: *(deprecated)* - **Method `Resize(normalId: NormalId, deltaAmount: int): boolean`**: Changes the size of an object just like using the Studio resize tool. - **Method `resize(normalId: NormalId, deltaAmount: int): boolean`**: *(deprecated)* - **Method `SetNetworkOwner(playerInstance?: Player): ()`**: Sets the given player as network owner for this and all connected parts. - **Method `SetNetworkOwnershipAuto(): ()`**: Lets the game engine dynamically decide who will handle the part's physics - **Method `SubtractAsync(parts: Instances, collisionfidelity?: CollisionFidelity, renderFidelity?: RenderFidelity): Instance`**: Note: It is highly recommended to use the newer - **Method `TorqueToAngularAcceleration(torque: Vector3, angVelocity?: Vector3): Vector3`**: Returns the angular acceleration that would result from applying a given - **Method `UnionAsync(parts: Instances, collisionfidelity?: CollisionFidelity, renderFidelity?: RenderFidelity): Instance`**: Note: It is highly recommended to use the newer - **Event `LocalSimulationTouched`**: *(deprecated)* - **Event `OutfitChanged`**: *(deprecated)* - **Event `StoppedTouching`**: *(deprecated)* - **Event `Touched`**: Fires when a part touches another part as a result of physical movement. - **Event `TouchEnded`**: Fires when a part stops touching another part as a result of physical ### From [PVInstance](/docs/reference/engine/classes/PVInstance.md) - **Property `Origin`** (`CFrame`): - **Property `Pivot Offset`** (`CFrame`): - **Method `GetPivot(): CFrame`**: Gets the pivot of a PVInstance. - **Method `PivotTo(targetCFrame: CFrame): ()`**: Transforms the PVInstance along with all of its descendant ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: FlagStandService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service summary: "An internal service responsible for handling the now deprecated FlagStand and Flag objects." --- # Class: FlagStandService > An internal service responsible for handling the now deprecated > [FlagStand](/docs/reference/engine/classes/FlagStand.md) and [Flag](/docs/reference/engine/classes/Flag.md) objects. ## Description This class was an internal service responsible for handling the now deprecated [FlagStand](/docs/reference/engine/classes/FlagStand.md) and [Flag](/docs/reference/engine/classes/Flag.md) objects. The [Flag](/docs/reference/engine/classes/Flag.md) and [FlagStand](/docs/reference/engine/classes/FlagStand.md) objects were created to allow developers to make 'Capture the Flag' style games quickly. However they have been deprecated and developers are advised to design their own systems which will be more flexible and reliable. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: FlangeSoundEffect last_updated: 2026-06-29T19:34:07Z inherits: - SoundEffect - Instance - Object type: class memory_category: Instances summary: "Creates a sweeping or swooshing effect on a sound." --- # Class: FlangeSoundEffect > Creates a sweeping or swooshing effect on a sound. ## Description The FlangeSoundEffect creates a sweeping or swooshing effect on the Sound or SoundGroup it is applied to. It does this by copying the original audio signal and playing on top of the original but slightly offset and modulated. Like all other [SoundEffect](/docs/reference/engine/classes/SoundEffect.md), a FlangeSoundEffect can be applied either to a [Sound](/docs/reference/engine/classes/Sound.md) or [SoundGroup](/docs/reference/engine/classes/SoundGroup.md) by being parented to either. ## Properties ### Property: FlangeSoundEffect.Depth ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "LegacySound" ] } ``` Range: 0.01 to 1 (default 0.45) The intensity of the effect. This value determines how offset the duplicated signal will be from the original. This value is the percentage of the max offset (40ms). ### Property: FlangeSoundEffect.Mix ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "LegacySound" ] } ``` Range: 0 to 1 (default 0.85) Percentage of the original sound that will be applied to the filter. Raising this value will make the effect sound louder and be more apparent. ### Property: FlangeSoundEffect.Rate ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "LegacySound" ] } ``` Range: 0 to 20 (default 5) The frequency that the effect oscillates at. Measured in Hz. ## Inherited Members ### From [SoundEffect](/docs/reference/engine/classes/SoundEffect.md) - **Property `Enabled`** (`boolean`): Toggles the effect on and off. - **Property `Priority`** (`int`): Determines the order the effect will be applied in relation to other ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: FloatCurve last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances summary: "A sorted list of time-value pairs that define a curve. Used to animate a single numerical value." --- # Class: FloatCurve > A sorted list of time-value pairs that define a curve. Used to animate a > single numerical value. ## Description An instance representing a 1D float curve encoded via a sorted list of [FloatCurveKeys](/docs/reference/engine/datatypes/FloatCurveKey.md). The shape of the interpolation curve between two keys is determined by the [FloatCurveKey.Interpolation](/docs/reference/engine/datatypes/FloatCurveKey.md) type. ## Properties ### Property: FloatCurve.Length ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` Number of keys in the float curve. ## Methods ### Method: FloatCurve:GetKeyAtIndex **Signature:** `FloatCurve:GetKeyAtIndex(index: int): FloatCurveKey` Returns a copy of a key at a given index. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `index` | `int` | | | **Returns:** `FloatCurveKey` ### Method: FloatCurve:GetKeyIndicesAtTime **Signature:** `FloatCurve:GetKeyIndicesAtTime(time: float): Array` The first item in the returned array is the index of the last key with time less than or equal to `time` (or the lesser of either 1 or the curve length if no key was found). The second item in the returned array is the index of the first key with time greater than or equal to `time` (or the curve length if no key was found satisfying the inequality). *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `time` | `float` | | | **Returns:** `Array` ### Method: FloatCurve:GetKeys **Signature:** `FloatCurve:GetKeys(): Array` Returns a copy of all the keys in the FloatCurve as a Luau array of [FloatCurveKeys](/docs/reference/engine/datatypes/FloatCurveKey.md). *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Returns:** `Array` — Array of [FloatCurveKeys](/docs/reference/engine/datatypes/FloatCurveKey.md). ### Method: FloatCurve:GetValueAtTime **Signature:** `FloatCurve:GetValueAtTime(time: float): float?` Samples the float curve at a given time passed as argument. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `time` | `float` | | Time at which to sample the curve. | **Returns:** `float?` — Value of the curve at the requested `time`. ### Method: FloatCurve:InsertKey **Signature:** `FloatCurve:InsertKey(key: FloatCurveKey): Array` Adds the key passed as an argument to this curve. If a key at the same time is found, it will be replaced. In the returned array, the first value is `true` if a key was added or `false` if a previous key was replaced; the second value is the index at which the marker was added. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `FloatCurveKey` | | [FloatCurveKey](/docs/reference/engine/datatypes/FloatCurveKey.md) to insert. | **Returns:** `Array` — (see description) . ### Method: FloatCurve:RemoveKeyAtIndex **Signature:** `FloatCurve:RemoveKeyAtIndex(startingIndex: int, count?: int): int` Removes a given number (`count`) of keys starting from the `startingIndex` index and returns the number of keys that were removed. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `startingIndex` | `int` | | Starting index from which to remove keys. | | `count` | `int` | `1` | Number of keys to remove. | **Returns:** `int` — Number of keys removed. ### Method: FloatCurve:SetKeys **Signature:** `FloatCurve:SetKeys(keys: Array): int` Resets this curve's keys using the [FloatCurveKey](/docs/reference/engine/datatypes/FloatCurveKey.md) array passed as an argument. Keys in the `keys` array are sorted in ascending time order before insertion, and keys at duplicated times are removed in a stable manner. Returns the number of keys actually inserted. Keys previously stored in this curve are removed before the keys passed as arguments are added. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `keys` | `Array` | | Array of [FloatCurveKeys](/docs/reference/engine/datatypes/FloatCurveKey.md). | **Returns:** `int` — Number of keys inserted. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: FloorWire last_updated: 2026-06-29T19:34:07Z inherits: - GuiBase3d - GuiBase - Instance - Object type: class memory_category: Instances tags: - Deprecated summary: "A FloorWire attempts to make a wire from two of its properties: FloorWire.From and FloorWire.From, which both need to be set to a BasePart." --- # Class: FloorWire > A FloorWire attempts to make a wire from two of its properties: > [FloorWire.From](/docs/reference/engine/classes/FloorWire.md) and [FloorWire.From](/docs/reference/engine/classes/FloorWire.md), which both need to be set > to a [BasePart](/docs/reference/engine/classes/BasePart.md). ## Description A FloorWire attempts to make a wire from two of its properties: [FloorWire.From](/docs/reference/engine/classes/FloorWire.md) and [FloorWire.To](/docs/reference/engine/classes/FloorWire.md), which both need to be set to a [BasePart](/docs/reference/engine/classes/BasePart.md). It sometimes goes through bricks but the majority of the time it works fine. It starts at From's center and goes to To's center. Which side of each one it goes into depends on the BaseParts's positions. It chooses the fastest route. ## Properties ### Property: FloorWire.CycleOffset ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic", "UI" ] } ``` A decimal number between 0 and 1, through which you can control how far all of the decals are along the wire. ### Property: FloorWire.From ```json { "type": "BasePart", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic", "UI" ] } ``` The object that the FloorWire travels from. ### Property: FloorWire.StudsBetweenTextures ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic", "UI" ] } ``` The number of studs between each FloorWire segment. ### Property: FloorWire.Texture ```json { "type": "ContentId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic", "UI" ] } ``` Sets the texture to be displayed on the FloorWire. ### Property: FloorWire.TextureSize ```json { "type": "Vector2", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic", "UI" ] } ``` Sets the size of the texture used with the FloorWire. ### Property: FloorWire.To ```json { "type": "BasePart", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic", "UI" ] } ``` The object that the FloorWire travels to. ### Property: FloorWire.Velocity ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic", "UI" ] } ``` The speed that the textures flow along the wire. ### Property: FloorWire.WireRadius ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic", "UI" ] } ``` The radius of the wire. ## Inherited Members ### From [GuiBase3d](/docs/reference/engine/classes/GuiBase3d.md) - **Property `Color`** (`BrickColor`): Sets the color of a GUI object. *(deprecated, hidden)* - **Property `Color3`** (`Color3`): Sets the color of this GuiBase3d object. - **Property `Transparency`** (`float`): Sets the transparency of this GuiBase3d object. - **Property `Visible`** (`boolean`): Determines whether this GuiBase3d object and its descendants will ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: FluidForceSensor last_updated: 2026-06-29T19:34:07Z inherits: - SensorBase - Instance - Object type: class memory_category: Instances summary: "A SensorBase that outputs Force, Torque and CenterOfPressure." --- # Class: FluidForceSensor > A [SensorBase](/docs/reference/engine/classes/SensorBase.md) that outputs [Force](/docs/reference/engine/classes/FluidForceSensor.md), > [Torque](/docs/reference/engine/classes/FluidForceSensor.md) and > [CenterOfPressure](/docs/reference/engine/classes/FluidForceSensor.md). ## Description `FluidForceSensor` is a [SensorBase](/docs/reference/engine/classes/SensorBase.md) which outputs the results of fluid force simulation from the last physics frame for the part it is attached to. The sensor outputs the [Force](/docs/reference/engine/classes/FluidForceSensor.md), [Torque](/docs/reference/engine/classes/FluidForceSensor.md) and [CenterOfPressure](/docs/reference/engine/classes/FluidForceSensor.md) which were computed by the fluid force simulation on the last physics frame. ## Properties ### Property: FluidForceSensor.CenterOfPressure ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Output", "capabilities": [ "Basic" ], "simulationAccess": true } ``` Assembly center of pressure offset from its center of mass in world coordinates. ### Property: FluidForceSensor.Force ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Output", "capabilities": [ "Basic" ], "simulationAccess": true } ``` Assembly fluid force in world coordinates. ### Property: FluidForceSensor.Torque ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Output", "capabilities": [ "Basic" ], "simulationAccess": true } ``` Assembly fluid torque in world coordinates. ## Methods ### Method: FluidForceSensor:EvaluateAsync **Signature:** `FluidForceSensor:EvaluateAsync(linearVelocity: Vector3, angularVelocity: Vector3, cframe: CFrame): Tuple` Asynchronously computes force, torque, and center of pressure for the parent part of a sensor given provided inputs. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Basic · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `linearVelocity` | `Vector3` | | Linear velocity in world coordinates. | | `angularVelocity` | `Vector3` | | Angular velocity in world coordinates. | | `cframe` | `CFrame` | | [CFrame](/docs/reference/engine/datatypes/CFrame.md) to be used for evaluation. | **Returns:** `Tuple` — Tuple of [Force](/docs/reference/engine/classes/FluidForceSensor.md), [Torque](/docs/reference/engine/classes/FluidForceSensor.md) and [CenterOfPressure](/docs/reference/engine/classes/FluidForceSensor.md) calculated given the input parameters. ## Inherited Members ### From [SensorBase](/docs/reference/engine/classes/SensorBase.md) - **Property `UpdateType`** (`SensorUpdateType`): Determines how the sensor will update its output data. - **Method `Sense(): ()`**: *(deprecated)* - **Event `OnSensorOutputChanged`**: ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Folder last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances summary: "A simple container used to hold and organize Roblox instances." --- # Class: Folder > A simple container used to hold and organize Roblox instances. ## Description A simple container used to hold and organize Roblox instances. Unlike other container classes like [Model](/docs/reference/engine/classes/Model.md), a folder generally offers no additional functionality. Folders behave the same way as folders in a computer file system, meaning they can also be parented to each other. #### Usage in UI Hierarchies Each [Folder](/docs/reference/engine/classes/Folder.md) in your UI hierarchy can define its own [UILayout](/docs/reference/engine/classes/UILayout.md) ([UIListLayout](/docs/reference/engine/classes/UIListLayout.md), [UIGridLayout](/docs/reference/engine/classes/UIGridLayout.md), [UIPageLayout](/docs/reference/engine/classes/UIPageLayout.md), [UITableLayout](/docs/reference/engine/classes/UITableLayout.md)), or use a default position-based layout. For example, you can set up a [Frame](/docs/reference/engine/classes/Frame.md) with multiple [Folder](/docs/reference/engine/classes/Folder.md) children, each with a different [UILayout](/docs/reference/engine/classes/UILayout.md) type. Additionally, [Folder](/docs/reference/engine/classes/Folder.md) contents are exempt from the effects of a [UILayout](/docs/reference/engine/classes/UILayout.md) sibling. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ForceField last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances summary: "Protects a Humanoid from taking damage dealt through the Humanoid:TakeDamage() method and protects BaseParts from having their joints broken due to an Explosion." --- # Class: ForceField > Protects a [Humanoid](/docs/reference/engine/classes/Humanoid.md) from taking damage dealt through the > [Humanoid:TakeDamage()](/docs/reference/engine/classes/Humanoid.md) method and protects [BaseParts](/docs/reference/engine/classes/BasePart.md) > from having their joints broken due to an [Explosion](/docs/reference/engine/classes/Explosion.md). ## Description A **ForceField** protects a [Humanoid](/docs/reference/engine/classes/Humanoid.md) from taking damage dealt through the [Humanoid:TakeDamage()](/docs/reference/engine/classes/Humanoid.md) method and protects [BaseParts](/docs/reference/engine/classes/BasePart.md) from having their joints broken due to an [Explosion](/docs/reference/engine/classes/Explosion.md). A new **ForceField** is created when a character spawns on a [SpawnLocation](/docs/reference/engine/classes/SpawnLocation.md) and the [SpawnLocation.Duration](/docs/reference/engine/classes/SpawnLocation.md) property is greater than zero. #### Damage and Joints A **ForceField** influences the instance it's parented to. When parented to a [Model](/docs/reference/engine/classes/Model.md), it protects all of the [BaseParts](/docs/reference/engine/classes/BasePart.md) descending from that model. If parented to a [BasePart](/docs/reference/engine/classes/BasePart.md), the part's joints will only be protected if both the part and the part it's connected to also contain a **ForceField**. **ForceField** only protects [Humanoids](/docs/reference/engine/classes/Humanoid.md) from damage dealt by the [Humanoid:TakeDamage()](/docs/reference/engine/classes/Humanoid.md) method. Humanoids can still be damaged by setting [Humanoid.Health](/docs/reference/engine/classes/Humanoid.md) directly. For this reason, it's advised that you use [Humanoid:TakeDamage()](/docs/reference/engine/classes/Humanoid.md) to assign damage while accounting for force field protection. #### Visualization When [ForceField.Visible](/docs/reference/engine/classes/ForceField.md) is set to true, a particle effect is created. A number of rules determine where this effect will be emitted from: - When parented to a [Model](/docs/reference/engine/classes/Model.md), if the model includes a [Humanoid](/docs/reference/engine/classes/Humanoid.md) named **Humanoid** with [Humanoid.RigType](/docs/reference/engine/classes/Humanoid.md) set to R15, the effect will be emitted from the part named **UpperTorso**. Otherwise, the effect will be emitted from the part named **Torso**. - When parented to a [BasePart](/docs/reference/engine/classes/BasePart.md) the effect will be emitted from the part's [BasePart.Position](/docs/reference/engine/classes/BasePart.md). ## Code Samples **ForceField Instantiation** This code sample includes a function that will give a Player a ForceField for a specific duration. ```lua local Players = game:GetService("Players") local FORCE_FIELD_DURATION = 15 local function giveForcefield(player, duration) local character = player.Character if character then local forceField = Instance.new("ForceField") forceField.Visible = true forceField.Parent = character if duration then task.delay(duration, function() if forceField then forceField:Destroy() end end) end end end local function onPlayerAdded(player) player.CharacterAdded:Connect(function(_character) giveForcefield(player, FORCE_FIELD_DURATION) end) end Players.PlayerAdded(onPlayerAdded) ``` ## Properties ### Property: ForceField.Visible ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarBehavior" ] } ``` Determines whether or not the [ForceField](/docs/reference/engine/classes/ForceField.md) particle effect is visible. Setting this to `false` lets you replace the default particle effect with a custom effect as demonstrated in the following code sample. **Custom ForceField Effect** This sample includes a function that will replace the default ForceField particle effect with an effect using ParticleEmitters that can be modified by the developer. ```lua local Players = game:GetService("Players") local FORCE_FIELD_DURATION = 15 local function createCustomForcefield(player, duration) local character = player.Character if character then local humanoid = character:FindFirstChild("Humanoid") if humanoid then -- find the torso local torsoName = humanoid.RigType == Enum.HumanoidRigType.R15 and "UpperTorso" or "Torso" local torso = character:FindFirstChild(torsoName) if torso then -- create a forcefield local forceField = Instance.new("ForceField") forceField.Visible = false -- not visible -- create a particle effect local particleEmitter = Instance.new("ParticleEmitter") particleEmitter.Enabled = true particleEmitter.Parent = torso -- listen for the forcefield being removed forceField.AncestryChanged:Connect(function(_child, parent) if not parent then if particleEmitter and particleEmitter.Parent then particleEmitter:Destroy() end end end) -- parent the forcefield and set it to expire forceField.Parent = character if duration then task.delay(duration, function() if forceField then forceField:Destroy() end end) end end end end end local function onPlayerAdded(player) player.CharacterAdded:Connect(function(_character) createCustomForcefield(player, FORCE_FIELD_DURATION) end) end Players.PlayerAdded(onPlayerAdded) ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: FormFactorPart last_updated: 2026-06-29T19:34:07Z inherits: - BasePart - PVInstance - Instance - Object type: class memory_category: Instances tags: - NotCreatable summary: "The FormFactorPart class is an abstract class. It inherits from the BasePart class and adds the FormFactor property to classes that inherit from it." --- # Class: FormFactorPart > The FormFactorPart class is an abstract class. It inherits from the BasePart > class and adds the FormFactor property to classes that inherit from it. ## Description The FormFactorPart class is an abstract class. It inherits from the [BasePart](/docs/reference/engine/classes/BasePart.md) class and adds the [FormFactorPart.FormFactor](/docs/reference/engine/classes/FormFactorPart.md) property to classes that inherit from it. ## Properties ### Property: FormFactorPart.FormFactor ```json { "type": "FormFactor", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Part" } ``` > **Deprecated:** This property has been deprecated and should not be used in new work. This used to specify a grid constraint of the part's size. No longer does anything. ### Property: FormFactorPart.formFactor *(hidden)* ```json { "type": "FormFactor", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Part" } ``` > **Deprecated:** This property has been deprecated and should not be used in new work. Determines how a part acts when resized and the values that which its size can take. ## Inherited Members ### From [BasePart](/docs/reference/engine/classes/BasePart.md) - **Property `Anchored`** (`boolean`): Determines whether a part is immovable by physics. - **Property `AssemblyAngularVelocity`** (`Vector3`): The angular velocity of the part's assembly. - **Property `AssemblyCenterOfMass`** (`Vector3`): The center of mass of the part's assembly in world space. - **Property `AssemblyLinearVelocity`** (`Vector3`): The linear velocity of the part's assembly. - **Property `AssemblyMass`** (`float`): The total mass of the part's assembly. - **Property `AssemblyRootPart`** (`BasePart`): A reference to the root part of the assembly. - **Property `AudioCanCollide`** (`boolean`): Determines whether the part will physically interact with audio - **Property `BackParamA`** (`float`): Determines the first parameter for the SurfaceType on the Back face of a *(deprecated, hidden)* - **Property `BackParamB`** (`float`): Determines the second parameter for the SurfaceType on the Back face of a *(deprecated, hidden)* - **Property `BackSurface`** (`SurfaceType`): Determines the type of surface for the back face of a part. - **Property `BackSurfaceInput`** (`InputType`): Determines the kind of input for the Back face of a part. *(deprecated, hidden)* - **Property `BottomParamA`** (`float`): Determines the first parameter for the SurfaceType on the Bottom face of a *(deprecated, hidden)* - **Property `BottomParamB`** (`float`): Determines the second parameter for the SurfaceType on the Bottom face of *(deprecated, hidden)* - **Property `BottomSurface`** (`SurfaceType`): Determines the type of surface for the bottom face of a part. - **Property `BottomSurfaceInput`** (`InputType`): Determines the kind of input for the Bottom face of a part. *(deprecated, hidden)* - **Property `BrickColor`** (`BrickColor`): Determines the color of a part. - **Property `brickColor`** (`BrickColor`): *(deprecated)* - **Property `CanCollide`** (`boolean`): Determines whether a part may collide with other parts. - **Property `CanQuery`** (`boolean`): Determines whether the part is considered during spatial query operations. - **Property `CanTouch`** (`boolean`): Determines if Touched and - **Property `CastShadow`** (`boolean`): Determines whether or not a part casts a shadow. - **Property `CenterOfMass`** (`Vector3`): Describes the world position in which a part's center of mass is located. - **Property `CFrame`** (`CFrame`): Determines the position and orientation of the BasePart in the - **Property `CollisionGroup`** (`string`): Describes the name of a part's collision group. - **Property `CollisionGroupId`** (`int`): Describes the automatically set ID number of a part's collision group. *(deprecated)* - **Property `Color`** (`Color3`): Determines the color of a part. - **Property `CurrentPhysicalProperties`** (`PhysicalProperties`): Indicates the current physical properties of the part. - **Property `CustomPhysicalProperties`** (`PhysicalProperties`): Determines several physical properties of a part. - **Property `Elasticity`** (`float`): Used to control the Elasticity of the part, but it no longer does *(deprecated, hidden)* - **Property `EnableFluidForces`** (`boolean`): Used to enable or disable aerodynamic forces on parts and assemblies. - **Property `ExtentsCFrame`** (`CFrame`): The CFrame of the physical extents of the BasePart. - **Property `ExtentsSize`** (`Vector3`): The actual physical size of the BasePart as regarded by the - **Property `Friction`** (`float`): Used to control the Friction of the part, but now it no longer does *(deprecated, hidden)* - **Property `FrontParamA`** (`float`): Determines the first parameter for the SurfaceType on the Front face of a *(deprecated, hidden)* - **Property `FrontParamB`** (`float`): Determines the second parameter for the SurfaceType on the Front face of a *(deprecated, hidden)* - **Property `FrontSurface`** (`SurfaceType`): Determines the type of surface for the front face of a part. - **Property `FrontSurfaceInput`** (`InputType`): Determines the kind of input for the Front face of a part (-Z direction). *(deprecated, hidden)* - **Property `LeftParamA`** (`float`): Determines the first parameter for the SurfaceType on the Left face of a *(deprecated, hidden)* - **Property `LeftParamB`** (`float`): Determines the second parameter for the SurfaceType on the Left face of a *(deprecated, hidden)* - **Property `LeftSurface`** (`SurfaceType`): Determines the type of surface for the left face of a part. - **Property `LeftSurfaceInput`** (`InputType`): Determines the kind of input for the Left face of a part. *(deprecated, hidden)* - **Property `LocalTransparencyModifier`** (`float`): Determines a multiplier for BasePart.Transparency that is only *(hidden)* - **Property `Locked`** (`boolean`): Determines whether a part is selectable in Studio. - **Property `Mass`** (`float`): Describes the mass of the part, the product of its density and volume. - **Property `Massless`** (`boolean`): Determines whether the part contributes to the total mass or inertia of - **Property `Material`** (`Material`): Determines the texture and default physical properties of a part. - **Property `MaterialVariant`** (`string`): The name of MaterialVariant. - **Property `Orientation`** (`Vector3`): Describes the rotation of the part in the world. *(hidden)* - **Property `PivotOffset`** (`CFrame`): Specifies the offset of the part's pivot from its CFrame. - **Property `Position`** (`Vector3`): Describes the position of the part in the world. *(hidden)* - **Property `ReceiveAge`** (`float`): Time since last recorded physics update. *(hidden)* - **Property `Reflectance`** (`float`): Determines how much a part reflects the skybox. - **Property `ResizeableFaces`** (`Faces`): Describes the faces on which a part may be resized. - **Property `ResizeIncrement`** (`int`): Describes the smallest change in size allowable by the - **Property `RightParamA`** (`float`): Determines the first parameter for the SurfaceType on the Right face of a *(deprecated, hidden)* - **Property `RightParamB`** (`float`): Determines the second parameter for the SurfaceType on the Right face of a *(deprecated, hidden)* - **Property `RightSurface`** (`SurfaceType`): Determines the type of surface for the right face of a part. - **Property `RightSurfaceInput`** (`InputType`): Determines the kind of input for the Right face of a part (-X direction). *(deprecated, hidden)* - **Property `RootPriority`** (`int`): The main rule in determining the root part of an assembly. - **Property `Rotation`** (`Vector3`): The rotation of the part in degrees for the three axes. - **Property `RotVelocity`** (`Vector3`): Determines a part's change in orientation over time. *(deprecated, hidden)* - **Property `Size`** (`Vector3`): Determines the dimensions of a part (length, width, height). - **Property `SpecificGravity`** (`float`): The ratio of the part's density to the density of water determined by the *(deprecated)* - **Property `TopParamA`** (`float`): Determines the first parameter for the SurfaceType on the Top face of a *(deprecated, hidden)* - **Property `TopParamB`** (`float`): Determines the second parameter for the SurfaceType on the Top face of a *(deprecated, hidden)* - **Property `TopSurface`** (`SurfaceType`): Determines the type of surface for the top face of a part. - **Property `TopSurfaceInput`** (`InputType`): Determines the kind of input for the Top face of a part (+Y direction). *(deprecated, hidden)* - **Property `Transparency`** (`float`): Determines how much a part can be seen through (the inverse of part - **Property `Velocity`** (`Vector3`): Determines a part's change in position over time. *(deprecated, hidden)* - **Method `AngularAccelerationToTorque(angAcceleration: Vector3, angVelocity?: Vector3): Vector3`**: Returns the torque needed to achieve a given angular acceleration on this - **Method `ApplyAngularImpulse(impulse: Vector3): ()`**: Apply an angular impulse to the assembly. - **Method `ApplyImpulse(impulse: Vector3): ()`**: Apply an impulse to the assembly at the assembly's - **Method `ApplyImpulseAtPosition(impulse: Vector3, position: Vector3): ()`**: Apply an impulse to the assembly at specified position. - **Method `BreakJoints(): ()`**: Breaks any surface connection with any adjacent part, including *(deprecated)* - **Method `breakJoints(): ()`**: *(deprecated)* - **Method `CanCollideWith(part: BasePart): boolean`**: Returns whether the parts can collide with each other. - **Method `CanSetNetworkOwnership(): Tuple`**: Checks whether you can set a part's network ownership. - **Method `GetClosestPointOnSurface(position: Vector3): Vector3`**: Returns the closest point on the part's surface to the given point. - **Method `GetConnectedParts(recursive?: boolean): List`**: Returns a table of parts connected to the object by any kind of rigid - **Method `GetJoints(): Instances`**: Return all Joints or Constraints that is connected to this Part. - **Method `GetMass(): float`**: Returns the value of the Mass property. - **Method `getMass(): float`**: *(deprecated)* - **Method `GetNetworkOwner(): Instance`**: Returns the current player who is the network owner of this part, or `nil` - **Method `GetNetworkOwnershipAuto(): boolean`**: Returns true if the game engine automatically decides the network owner - **Method `GetNoCollisionConstraints(): Instances`**: - **Method `GetRenderCFrame(): CFrame`**: OBSOLETE. Returns a CFrame describing where the part is being rendered at. *(deprecated)* - **Method `GetRootPart(): Instance`**: Returns the base part of an assembly of parts. *(deprecated)* - **Method `GetTouchingParts(): Instances`**: Returns a table of all BasePart.CanCollide true parts that - **Method `GetVelocityAtPosition(position: Vector3): Vector3`**: Returns the linear velocity of the part's assembly at the given position - **Method `IntersectAsync(parts: Instances, collisionfidelity?: CollisionFidelity, renderFidelity?: RenderFidelity): Instance`**: Note: It is highly recommended to use the newer - **Method `IsGrounded(): boolean`**: Returns true if the object is connected to a part that will hold it in - **Method `MakeJoints(): ()`**: Creates a joint on any side of the object that has a surface ID that can *(deprecated)* - **Method `makeJoints(): ()`**: *(deprecated)* - **Method `Resize(normalId: NormalId, deltaAmount: int): boolean`**: Changes the size of an object just like using the Studio resize tool. - **Method `resize(normalId: NormalId, deltaAmount: int): boolean`**: *(deprecated)* - **Method `SetNetworkOwner(playerInstance?: Player): ()`**: Sets the given player as network owner for this and all connected parts. - **Method `SetNetworkOwnershipAuto(): ()`**: Lets the game engine dynamically decide who will handle the part's physics - **Method `SubtractAsync(parts: Instances, collisionfidelity?: CollisionFidelity, renderFidelity?: RenderFidelity): Instance`**: Note: It is highly recommended to use the newer - **Method `TorqueToAngularAcceleration(torque: Vector3, angVelocity?: Vector3): Vector3`**: Returns the angular acceleration that would result from applying a given - **Method `UnionAsync(parts: Instances, collisionfidelity?: CollisionFidelity, renderFidelity?: RenderFidelity): Instance`**: Note: It is highly recommended to use the newer - **Event `LocalSimulationTouched`**: *(deprecated)* - **Event `OutfitChanged`**: *(deprecated)* - **Event `StoppedTouching`**: *(deprecated)* - **Event `Touched`**: Fires when a part touches another part as a result of physical movement. - **Event `TouchEnded`**: Fires when a part stops touching another part as a result of physical ### From [PVInstance](/docs/reference/engine/classes/PVInstance.md) - **Property `Origin`** (`CFrame`): - **Property `Pivot Offset`** (`CFrame`): - **Method `GetPivot(): CFrame`**: Gets the pivot of a PVInstance. - **Method `PivotTo(targetCFrame: CFrame): ()`**: Transforms the PVInstance along with all of its descendant ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Frame last_updated: 2026-06-29T19:34:07Z inherits: - GuiObject - GuiBase2d - GuiBase - Instance - Object type: class memory_category: Gui summary: "A GuiObject that renders as a plain rectangle, generally used as a container." --- # Class: Frame > A [GuiObject](/docs/reference/engine/classes/GuiObject.md) that renders as a plain rectangle, generally used as a > container. ## Description [Frame](/docs/reference/engine/classes/Frame.md) is a [GuiObject](/docs/reference/engine/classes/GuiObject.md) that acts as a container for other [GuiObjects](/docs/reference/engine/classes/GuiObject.md). You can use it for UI that either displays on a user's [screen](/docs/en-us/ui/on-screen-containers.md) or on a [surface](/docs/en-us/ui/in-experience-containers.md) within the experience. [Frames](/docs/reference/engine/classes/Frame.md) are ideal containers for responsive layouts such as [list and flex layouts](/docs/en-us/ui/list-flex-layouts.md), allowing you to change the size of the frame and dynamically adjust how layout items fit within it. [Frames](/docs/reference/engine/classes/Frame.md) are also core [GuiObjects](/docs/reference/engine/classes/GuiObject.md), so you can customize properties such as [BackgroundColor3](/docs/reference/engine/classes/GuiObject.md), [Transparency](/docs/reference/engine/classes/GuiObject.md), apply a [background gradient](/docs/en-us/ui/appearance-modifiers.md#gradient) or [border](/docs/en-us/ui/appearance-modifiers.md#stroke), and more. ## Properties ### Property: Frame.Style ```json { "type": "FrameStyle", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` Sets what the frame looks like from a selection of pre-determined styles. See [FrameStyle](/docs/reference/engine/enums/FrameStyle.md) for a description of each style. ## Inherited Members ### From [GuiObject](/docs/reference/engine/classes/GuiObject.md) - **Property `Active`** (`boolean`): Determines whether this UI element sinks input. - **Property `AnchorPoint`** (`Vector2`): Determines the origin point of a GuiObject, relative to its - **Property `AutomaticSize`** (`AutomaticSize`): Determines whether resizing occurs based on child content. - **Property `BackgroundColor`** (`BrickColor`): Determines the color of the GuiObject background. *(deprecated, hidden)* - **Property `BackgroundColor3`** (`Color3`): Determines the GuiObject background color. - **Property `BackgroundTransparency`** (`float`): Determines the transparency of the GuiObject background and - **Property `BorderColor`** (`BrickColor`): Determines the color of the GuiObject border. *(deprecated, hidden)* - **Property `BorderColor3`** (`Color3`): Determines the color of the GuiObject border. - **Property `BorderMode`** (`BorderMode`): Determines in what manner the GuiObject border is laid out - **Property `BorderSizePixel`** (`int`): Determines the pixel width of the GuiObject border. - **Property `ClipsDescendants`** (`boolean`): Determines if descendant GuiObjects outside of the - **Property `Draggable`** (`boolean`): Determines whether a GuiObject (and its descendants) can be *(deprecated)* - **Property `GuiState`** (`GuiState`): Determines whether the player's mouse is being actively pressed on the - **Property `InputSink`** (`InputSink`): - **Property `Interactable`** (`boolean`): Determines whether the GuiButton can be interacted with or not, or - **Property `LayoutOrder`** (`int`): Controls the sort order of the GuiObject when used with a - **Property `NextSelectionDown`** (`GuiObject`): Sets the GuiObject which will be selected when the gamepad - **Property `NextSelectionLeft`** (`GuiObject`): Sets the GuiObject which will be selected when the gamepad - **Property `NextSelectionRight`** (`GuiObject`): Sets the GuiObject which will be selected when the gamepad - **Property `NextSelectionUp`** (`GuiObject`): Sets the GuiObject which will be selected when the gamepad - **Property `Position`** (`UDim2`): Determines the pixel and scalar position of the GuiObject. - **Property `Rotation`** (`float`): Determines the number of degrees by which the GuiObject is - **Property `Selectable`** (`boolean`): Determine whether the GuiObject can be selected by a gamepad. - **Property `SelectionImageObject`** (`GuiObject`): Overrides the default selection adornment used for gamepads. - **Property `SelectionOrder`** (`int`): The order of GuiObjects selected by the gamepad UI - **Property `Size`** (`UDim2`): Determines the pixel and scalar size of the GuiObject. - **Property `SizeConstraint`** (`SizeConstraint`): Sets the Size axes that the GuiObject will - **Property `Transparency`** (`float`): A mixed property of *(hidden)* - **Property `Visible`** (`boolean`): Determines whether the GuiObject and its descendants will be - **Property `ZIndex`** (`int`): Determines the order in which a GuiObject renders relative to - **Method `TweenPosition(endPosition: UDim2, easingDirection?: EasingDirection, easingStyle?: EasingStyle, time?: float, override?: boolean, callback?: Function): boolean`**: Smoothly moves a GUI to a new UDim2. - **Method `TweenSize(endSize: UDim2, easingDirection?: EasingDirection, easingStyle?: EasingStyle, time?: float, override?: boolean, callback?: Function): boolean`**: Smoothly resizes a GuiObject to a new UDim2. - **Method `TweenSizeAndPosition(endSize: UDim2, endPosition: UDim2, easingDirection?: EasingDirection, easingStyle?: EasingStyle, time?: float, override?: boolean, callback?: Function): boolean`**: Smoothly moves a GUI to a new size and position. - **Event `DragBegin`**: Fired when a player begins dragging the object. *(deprecated)* - **Event `DragStopped`**: Fired when a player stops dragging the object. *(deprecated)* - **Event `InputBegan`**: Fired when a user begins interacting via a Human-Computer Interface device - **Event `InputChanged`**: Fired when a user changes how they're interacting via a Human-Computer - **Event `InputEnded`**: Fired when a user stops interacting via a Human-Computer Interface device - **Event `MouseEnter`**: Fires when a user moves their mouse into a GUI element. - **Event `MouseLeave`**: Fires when a user moves their mouse out of a GUI element. - **Event `MouseMoved`**: Fires whenever a user moves their mouse while it is inside a GUI element. - **Event `MouseWheelBackward`**: Fires when a user scrolls their mouse wheel back when the mouse is over a - **Event `MouseWheelForward`**: Fires when a user scrolls their mouse wheel forward when the mouse is over - **Event `SelectionGained`**: Fired when the GuiObject is being focused on with the Gamepad selector. - **Event `SelectionLost`**: Fired when the Gamepad selector stops focusing on the GuiObject. - **Event `TouchLongPress`**: Fires when the player starts, continues and stops long-pressing the UI - **Event `TouchPan`**: Fires when the player moves their finger on the UI element. - **Event `TouchPinch`**: Fires when the player performs a pinch or pull gesture using two fingers - **Event `TouchRotate`**: Fires when the player performs a rotation gesture using two fingers on the - **Event `TouchSwipe`**: Fires when the player performs a swipe gesture on the UI element. - **Event `TouchTap`**: Fires when the player performs a tap gesture on the UI element. ### From [GuiBase2d](/docs/reference/engine/classes/GuiBase2d.md) - **Property `AbsolutePosition`** (`Vector2`): Describes the actual screen position of a GuiBase2d element, in - **Property `AbsoluteRotation`** (`float`): Describes the actual screen rotation of a GuiBase2d element, in - **Property `AbsoluteSize`** (`Vector2`): Describes the actual screen size of a GuiBase2d element, in - **Property `AutoLocalize`** (`boolean`): When set to `true`, localization will be applied to this GuiBase2d - **Property `Localize`** (`boolean`): Automatically set to true when a localization table's *(deprecated, hidden)* - **Property `RootLocalizationTable`** (`LocalizationTable`): A reference to a LocalizationTable to be used to apply automated - **Property `SelectionBehaviorDown`** (`SelectionBehavior`): Customizes gamepad selection behavior in the down direction. - **Property `SelectionBehaviorLeft`** (`SelectionBehavior`): Customizes gamepad selection behavior in the left direction. - **Property `SelectionBehaviorRight`** (`SelectionBehavior`): Customizes gamepad selection behavior in the right direction. - **Property `SelectionBehaviorUp`** (`SelectionBehavior`): Customizes gamepad selection behavior in the up direction. - **Property `SelectionGroup`** (`boolean`): Allows customization of gamepad selection movement. - **Event `SelectionChanged`**: Fires when the gamepad selection moves to, leaves, or changes within the ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: FriendPages last_updated: 2026-06-29T19:34:07Z inherits: - Pages - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated summary: "A special version of Pages that contains information about a player's friends." --- # Class: FriendPages > A special version of [Pages](/docs/reference/engine/classes/Pages.md) that contains information about a player's > friends. ## Description FriendPages is a special version of [Pages](/docs/reference/engine/classes/Pages.md) returned by [Players:GetFriendsAsync()](/docs/reference/engine/classes/Players.md). The items contained within include information about a player's friends and have the following structure: | Name | Type | Description | | --- | --- | --- | | `DisplayName` | string | The current display name of the friend. | | `Id` | int64 | The user ID of the friend. | | `Username` | string | The username of the friend. | See the code samples for how to iterate over a player's friends. ## Code Samples **Print Roblox Friends** This code sample loads the [Player.UserId](/docs/reference/engine/classes/Player.md) of the player whose username is provided at the top of the script by using [Players:GetUserIdFromNameAsync()](/docs/reference/engine/classes/Players.md). Then, it gets a `FriendPages` object by calling [Players:GetFriendsAsync()](/docs/reference/engine/classes/Players.md) and iterates over each entry using the `iterPageItems` function. The username of each friend is stored in a table, then printed at the end. ```lua local Players = game:GetService("Players") local USERNAME = "Cozecant" local function iterPageItems(pages) return coroutine.wrap(function() local pagenum = 1 while true do for _, item in ipairs(pages:GetCurrentPage()) do coroutine.yield(item, pagenum) end if pages.IsFinished then break end pages:AdvanceToNextPageAsync() pagenum = pagenum + 1 end end) end -- First, get the user ID of the player local userId = Players:GetUserIdFromNameAsync(USERNAME) -- Then, get a FriendPages object for their friends local friendPages = Players:GetFriendsAsync(userId) -- Iterate over the items in the pages. For FriendPages, these -- are tables of information about the friend, including Username. -- Collect each username in a table local usernames = {} for item, _pageNo in iterPageItems(friendPages) do table.insert(usernames, item.Username) end print("Connections of " .. USERNAME .. ": " .. table.concat(usernames, ", ")) ``` **Expected output:** When run, this code sample will load the friends of the player whose username is provided at the top of the script. ## Inherited Members ### From [Pages](/docs/reference/engine/classes/Pages.md) - **Property `IsFinished`** (`boolean`): Whether or not the current page is the last page available. - **Method `AdvanceToNextPageAsync(): ()`**: Iterates to the next page in the pages object, if possible. - **Method `GetCurrentPage(): Array`**: Returns the items on the current page. The keys in the item are determined ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: FriendService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service summary: "An internal service which is used to send, cancel, accept and decline friend requests in-game." --- # Class: FriendService > An internal service which is used to send, cancel, accept and decline friend > requests in-game. ## Description A service which is used to send, cancel, accept and decline friend requests in-game. It is primarily used by the PlayerListScript to send friend requests with the leaderboard. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: FunctionalTest last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - Deprecated --- # Class: FunctionalTest ## Properties ### Property: FunctionalTest.Description ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` The description of the FunctionalTest. ## Methods ### Method: FunctionalTest:Error **Signature:** `FunctionalTest:Error(message: string): ()` *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `message` | `string` | | | **Returns:** `()` ### Method: FunctionalTest:Failed **Signature:** `FunctionalTest:Failed(message: string): ()` Prints a red message to the output, prefixed by _"TestService: "_. *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `message` | `string` | | | **Returns:** `()` ### Method: FunctionalTest:Pass **Signature:** `FunctionalTest:Pass(message: string): ()` *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `message` | `string` | | | **Returns:** `()` ### Method: FunctionalTest:Passed **Signature:** `FunctionalTest:Passed(message: string): ()` *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `message` | `string` | | | **Returns:** `()` ### Method: FunctionalTest:Warn **Signature:** `FunctionalTest:Warn(message: string): ()` Prints if a condition is true, otherwise prints a warning. *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `message` | `string` | | | **Returns:** `()` **FunctionalTest:Warn1** This code would print Warning: this action is invalid to the output, in yellow text. ```lua local TestService = game:GetService("TestService") TestService:Warn(false, "this action is invalid") ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: GamePassService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service summary: "A service associated with the legacy game pass system. Use MarketplaceService for all new work." --- # Class: GamePassService > A service associated with the legacy game pass system. Use > [MarketplaceService](/docs/reference/engine/classes/MarketplaceService.md) for all new work. ## Description The GamePassService is a service that supports legacy game passes using _Asset IDs_. [MarketplaceService](/docs/reference/engine/classes/MarketplaceService.md) should be used for all new game passes. For more information about game passes, please see [Game Passes](/docs/en-us/production/monetization/passes.md). ## Methods ### Method: GamePassService:PlayerHasPass **Signature:** `GamePassService:PlayerHasPass(player: Player, gamePassId: int64): boolean` This function will not work with new game passes; use [MarketplaceService:UserOwnsGamePassAsync()](/docs/reference/engine/classes/MarketplaceService.md) instead. This function returns `true` if the [Player](/docs/reference/engine/classes/Player.md) has the specified legacy game pass. The result of this function may be cached, meaning it should not be relied on to give an up to date result. See [Passes](/docs/en-us/production/monetization/passes.md) for further information. #### Legacy Game Passes Historically, game passes on Roblox had an **asset ID** associated with them. Although such game passes still have an asset ID, they now also have a **pass ID**. All new game passes created **only** have a pass ID. Whether you are using an **Asset ID** or a **pass ID** determines which API members you can use. | | Asset ID (Legacy) | Pass ID (Current) | | --- | --- | --- | | Verify Ownership | [GamePassService:PlayerHasPass()](/docs/reference/engine/classes/GamePassService.md) | [MarketplaceService:UserOwnsGamePassAsync()](/docs/reference/engine/classes/MarketplaceService.md) | | Prompt a purchase | [MarketplaceService:PromptPurchase()](/docs/reference/engine/classes/MarketplaceService.md) | [MarketplaceService:PromptGamePassPurchase()](/docs/reference/engine/classes/MarketplaceService.md) | | Prompted purchase finished | [MarketplaceService.PromptPurchaseFinished](/docs/reference/engine/classes/MarketplaceService.md) | [MarketplaceService.PromptGamePassPurchaseFinished](/docs/reference/engine/classes/MarketplaceService.md) | *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Monetization* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Player` | | The [Player](/docs/reference/engine/classes/Player.md) for which to check ownership. | | `gamePassId` | `int64` | | The _Asset ID_ of the game pass. This is **not** the _Game Pass ID_ (see above). | **Returns:** `boolean` — Whether the [Player](/docs/reference/engine/classes/Player.md) owns the game pass. **GamePassService:PlayerHasPass** The below example will print whether or not the recently joined player owns the ' with the ID of 103728213. ```lua local Players = game:GetService("Players") local GAMEPASS_ID = 103728213 Players.PlayerAdded:Connect(function(player) if game:GetService("GamePassService"):PlayerHasPass(player, GAMEPASS_ID) then print(player.Name .. " has the game pass!") else print(player.Name .. " doesn't have the game pass...") end end) ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: GameSettings last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated - NotBrowsable summary: "A container for miscellaneous in-game options." --- # Class: GameSettings > A container for miscellaneous in-game options. ## Description Various miscellaneous in-game options. ## Properties ### Property: GameSettings.VideoCaptureEnabled ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Video" } ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: GamepadService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated summary: "The GamepadService is internally responsible for handling inputs from various controllers, such as Xbox One or PlayStation DualShock controllers." --- # Class: GamepadService > The GamepadService is internally responsible for handling inputs from various > controllers, such as Xbox One or PlayStation DualShock controllers. ## Description The GamepadService is internally responsible for handling inputs from various controllers, such as Xbox One or PlayStation DualShock controllers. It also handles APIs used with the gamepad virtual cursor. You can enable the gamepad cursor for your experience by setting [VirtualCursorMode](/docs/reference/engine/enums/VirtualCursorMode.md) under [StarterGui](/docs/reference/engine/classes/StarterGui.md) to `Enabled`. ## Properties ### Property: GamepadService.GamepadCursorEnabled ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "None", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Input" ] } ``` This boolean is a read only variable that maintains the state of the gamepad virtual cursor. You can read this property but not write to it. **GamepadService - Gamepad Cursor Enabled Property** ```lua local gamepadService = game.GamepadService gamepadService:GetPropertyChangedSignal("GamepadCursorEnabled"):Connect(function() local enabled = gamepadService.GamepadCursorEnabled if enabled then -- Custom code when the virtual cursor is enabled else -- Custom code when the virtual cursor is disabled end end) ``` ## Methods ### Method: GamepadService:DisableGamepadCursor **Signature:** `GamepadService:DisableGamepadCursor(): ()` This function disables the gamepad cursor, if it's currently enabled. *Security: None · Thread Safety: Unsafe · Capabilities: Input* **Returns:** `()` **GamepadService - Disable Gamepad Cursor** In this example, the cursor is disabled when the user presses [KeyCode.ButtonB](/docs/reference/engine/enums/KeyCode.md). ```lua local gamepadService = game.GamepadService local userInputService = game.UserInputService userInputService.InputBegan:Connect(function(inputObject, gameProcessed) if inputObject.KeyCode == Enum.KeyCode.ButtonB then gamepadService:DisableGamepadCursor() end end) ``` ### Method: GamepadService:EnableGamepadCursor **Signature:** `GamepadService:EnableGamepadCursor(guiObject: Instance): ()` This function enables the gamepad cursor if it's currently disabled. If the cursor is already enabled, calling the API updates the cursor's position. The function accepts a [GuiObject](/docs/reference/engine/classes/GuiObject.md) parameter, but there are some invalid cases. Please note that in order to set the cursor to the default position, `nil` must be passed in as a parameter. Providing no argument will result in an error. | Input GuiObject | Cursor Starting Position | | --- | --- | | `Nil` | Default position | | Not a GuiObject | Cursor does not appear, errors | | | Not a child of BasePlayerGui | Cursor does not appear, errors | | GuiObject has Visible 2 set to false or any parent visible set to false | Default position with warning | | Child of ScreenGui 1 with Enabled 1 set to false | Default position with warning | | Child of BillboardGui 4 or SurfaceGui | Default position with warning | | GuiObject outside the viewport (Or, any part of the gui object that is off screen) | Default position with warning | | Child of ScrollingFrame with clipping set to false: GuiObject outside of scrolling frame (Object child of scrolling frame and not visible on screen) | Default position with warning | | Child of ScrollingFrame with clipping set to true: GuiObject outside scrolling frame window but inside viewport | GuiObject moves into the scrolling frame and starts the cursor centered over the object | | GuiObject inside the frame and visible | Cursor starts centered on the GuiObject | *Security: None · Thread Safety: Unsafe · Capabilities: Input* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `guiObject` | `Instance` | | | **Returns:** `()` **GamepadService - Enable Gamepad Cursor** In this example, the cursor is enabled when the user presses [KeyCode.ButtonA](/docs/reference/engine/enums/KeyCode.md) with the starting button being this script's parent. ```lua local gamepadService = game.GamepadService local userInputService = game.UserInputService local startObject = script.Parent userInputService.InputBegan:Connect(function(inputObject, gameProcessed) if inputObject.KeyCode == Enum.KeyCode.ButtonA then gamepadService:EnableGamepadCursor(startObject) end end) ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: GeneratedFolder last_updated: 2026-06-29T19:34:07Z inherits: - Folder - Instance - Object type: class memory_category: BaseParts summary: "A container that stores ProceduralModel generation results." --- # Class: GeneratedFolder > A container that stores [ProceduralModel](/docs/reference/engine/classes/ProceduralModel.md) generation results. ## Description `GeneratedFolder` is a container used to represent non-source-of-truth data in the [DataModel](/docs/reference/engine/classes/DataModel.md). These are branches that are deterministically generated from other parts of the [DataModel](/docs/reference/engine/classes/DataModel.md) instead of containing source-of-truth data. `GeneratedFolder` is primarily used by [ProceduralModel](/docs/reference/engine/classes/ProceduralModel.md) to store generation results. It can also be used by plugins or other tools to organize generated content. ## Methods ### Method: GeneratedFolder:SetPrimaryPart **Signature:** `GeneratedFolder:SetPrimaryPart(part: BasePart): ()` This method allows a generator module to specify which part within the `GeneratedFolder` should be set as the [PrimaryPart](/docs/reference/engine/classes/Model.md) of the [ProceduralModel](/docs/reference/engine/classes/ProceduralModel.md) that results get parented to. Generation of results happens outside of the [DataModel](/docs/reference/engine/classes/DataModel.md) and doesn't have direct access to the [ProceduralModel](/docs/reference/engine/classes/ProceduralModel.md) instance being generated. As a result, the generator must use this method instead of setting the [PrimaryPart](/docs/reference/engine/classes/Model.md) directly. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `part` | `BasePart` | | | **Returns:** `()` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: GenerationService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service summary: "Service that allows developers to generate 3D objects from text prompts." --- # Class: GenerationService > Service that allows developers to generate 3D objects from text prompts. ## Description `GenerationService` enables you to generate 3D objects from text prompts using Roblox's Cube 3D foundation model. This enables the generation of objects like environmental props in-experience, as well as fully functional models like vehicles that drive, planes that fly, and weapons that shoot. ## Methods ### Method: GenerationService:GenerateMeshAsync **Signature:** `GenerationService:GenerateMeshAsync(inputs: Dictionary, player: Player, options: Dictionary, intermediateResultCallback: Function?): Tuple` Starts the generation of a new 3D mesh from a text prompt and returns unique IDs used to track and retrieve the result. Must be called from server scripts. You can optionally receive intermediate results, such as the untextured mesh, by providing an `intermediateResultCallback` function. After the generation is complete, use [LoadGeneratedMeshAsync()](/docs/reference/engine/classes/GenerationService.md) to load and display the generated mesh. #### Error Codes | Error | Description | Recommended action | | ------------------------------------------ | ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------ | | Rate limit exceeded | The maximum number of mesh generations has been exceeded for the minute. | Wait until the rate limit resets. | | Moderation failed | The mesh generation was flagged for moderation. | Review and modify the prompt to ensure it adheres to Roblox's moderation guidelines. | | Internal server error | An unexpected issue occurred on the server. | Retry the request later or check server status for issues. | | Character limit exceeded | The input prompt length for this generation request exceeded the limit. | Reduce the number of characters in the `Prompt` string of the `input` dictionary. | | Service overloaded | The service is overloaded. | Retry the request later. | | Size dimensions must all be greater than 0 | Invalid size provided. | Change the size to above `0`. | *Yields · Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `inputs` | `Dictionary` | | A dictionary containing the mesh generation prompts. Currently, the only supported key is the `Prompt` (string) that describes the mesh to generate. | | `player` | `Player` | | The [Player](/docs/reference/engine/classes/Player.md) requesting the generation. | | `options` | `Dictionary` | | A dictionary for additional generation options to influence the result. The optional `SuggestedSize` key ([Vector3](/docs/reference/engine/datatypes/Vector3.md)) represents a size guide for the generated asset, meaning `GenerationService` will attempt to create a model with a size and proportion similar to the provided [Vector3](/docs/reference/engine/datatypes/Vector3.md), although there is no guarantee on the final output size. | | `intermediateResultCallback` | `Function?` | | A callback function triggered with intermediate generation results. Useful for retrieving early mesh versions before textures are applied. | **Returns:** `Tuple` — A tuple of the generation ID (a unique ID returned for each invocation of `GenerateMeshAsync()`) and context ID (not currently used). ### Method: GenerationService:GenerateModelAsync **Signature:** `GenerationService:GenerateModelAsync(inputs: Dictionary, schema: Dictionary, options: Dictionary?): Tuple` Enables generation of multi-mesh geometries according to provided `inputs` and a `schema`. Provide creative direction through `inputs`: a `TextPrompt`, an `Image` to condition on visually, or both. Control the output structure through `schema`, supplying either a built-in `PredefinedSchema` or a custom `SchemaDefinition` that names the parts to produce. This method throws an error during the generation process for issues like prompt rejection, generation timeouts, `schema` mismatches, or backend service errors. It's recommended to wrap it in a [LuaGlobals.pcall()](/docs/reference/engine/globals/LuaGlobals.md) to catch such errors. See [this guide](/docs/en-us/parts/model-generation.md) for more detailed examples including usage of a `Behaviors` module to connect functional behaviors to generated models. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `inputs` | `Dictionary` | | A table representing conditioning inputs with the following keys: - `TextPrompt` — String description of the object to generate. Required unless an `Image` is provided; you must supply at least one of `TextPrompt` or `Image`. - `Image` — Optional [Content](/docs/reference/engine/datatypes/Content.md) referencing an image asset (for example, the result of [Content.fromAssetId()](/docs/reference/engine/datatypes/Content.md)) used to visually condition the generation. Supply it together with or instead of `TextPrompt`. - `Size` — Optional [Vector3](/docs/reference/engine/datatypes/Vector3.md) describing the object size to generate. This is only approximate; scale the model extents post‑generation if you need a specific size. - `MaxTriangles` — Optional integer describing the maximum number of triangles that the returned model will contain. Lower values result in more faceted and low-poly generations. - `GenerateTextures` — `true` (default) textures the resulting generation, while `false` will not generate textures. | | `schema` | `Dictionary` | | Table representing the generation schema. Provide exactly one of the following keys: - `PredefinedSchema` — String selecting a built-in schema. Current valid options are `Car5` for a basic vehicle chassis consisting of five [Models](/docs/reference/engine/classes/Model.md), or `Body1` which results in a single mesh output. - `SchemaDefinition` — Table describing a custom multi-part structure. It contains a `Groups` array of part-name strings (one entry per desired part); the returned [Model](/docs/reference/engine/classes/Model.md) is organized to match the named groups. Use this instead of `PredefinedSchema` when you need a structure the predefined schemas don't cover. | | `options` | `Dictionary?` | | Currently not used; reserved for future customization options. | **Returns:** `Tuple` — Tuple with the following elements: - A standard [Model](/docs/reference/engine/classes/Model.md) container instance that is compliant with the specified `schema`. - A table containing a UUID and other metadata about the generation. **Generation of Basic Car Chassis** The code below invokes [GenerationService:GenerateModelAsync()](/docs/reference/engine/classes/GenerationService.md) using the `Car5` schema to request output of a basic car chassis consisting of five [MeshParts](/docs/reference/engine/classes/MeshPart.md) (body, front‑left wheel, front‑right wheel, rear‑left wheel, rear‑right wheel). ```lua local GenerationService = game:GetService("GenerationService") local Workspace = game:GetService("Workspace") -- Set up inputs for the generated geometry local inputs = { TextPrompt = "a green dragon car with 4 wheels" } -- Set schema to the predefined five-model car chassis local schema = { PredefinedSchema = "Car5" } -- Make the call to generate the model local success, model, metadata = pcall(function() return GenerationService:GenerateModelAsync(inputs, schema) end) if success then -- Scale model to target size and position near world center local targetSize = 16 local modelSize = model:GetExtentsSize() local scaleFactor = targetSize / math.max(modelSize.X, modelSize.Y, modelSize.Z) model:ScaleTo(scaleFactor) model:PivotTo(CFrame.new(15, model:GetExtentsSize().Y / 2, 0)) -- Anchor all parts so that the meshes don't fall apart for _, descendant in model:GetDescendants() do if descendant:IsA("BasePart") then descendant.Anchored = true end end -- Name the model and parent it to workspace model.Name = "BasicDragonCarGeneration" model.Parent = Workspace end ``` **Generation With a Custom Schema** The code below invokes [GenerationService:GenerateModelAsync()](/docs/reference/engine/classes/GenerationService.md) with a custom `SchemaDefinition` instead of a `PredefinedSchema`. Each entry in the `Groups` array names a part to produce, so the returned [Model](/docs/reference/engine/classes/Model.md) is organized to match the named groups. ```lua local GenerationService = game:GetService("GenerationService") local Workspace = game:GetService("Workspace") -- Describe the object to generate local inputs = { TextPrompt = "a wooden cart with four wheels", } -- Define a custom multi-part structure instead of using a predefined schema. -- Each entry in `Groups` names a part the generation should produce. local schema = { SchemaDefinition = { Groups = { "body", "wheel_fl", "wheel_fr", "wheel_rl", "wheel_rr" }, }, } -- Make the call to generate the model local success, model, metadata = pcall(function() return GenerationService:GenerateModelAsync(inputs, schema) end) if success then -- Anchor all parts so that the meshes don't fall apart for _, descendant in model:GetDescendants() do if descendant:IsA("BasePart") then descendant.Anchored = true end end -- Name the model and parent it to workspace model.Name = "CustomSchemaGeneration" model.Parent = Workspace end ``` **Generation Conditioned on an Image** The code below invokes [GenerationService:GenerateModelAsync()](/docs/reference/engine/classes/GenerationService.md) with an `Image` input so the generation is conditioned on a reference image. When an `Image` is supplied the `TextPrompt` is optional, though you can provide both for additional direction. ```lua local GenerationService = game:GetService("GenerationService") local Workspace = game:GetService("Workspace") -- Reference image used to condition the generation. Replace `0` with the asset -- ID of your own uploaded image. local imageAssetId = 0 local inputs = { Image = Content.fromAssetId(imageAssetId), TextPrompt = "a low-poly treasure chest", } -- Produce a single-mesh output local schema = { PredefinedSchema = "Body1", } -- Make the call to generate the model local success, model, metadata = pcall(function() return GenerationService:GenerateModelAsync(inputs, schema) end) if success then -- Anchor all parts so that the meshes don't fall apart for _, descendant in model:GetDescendants() do if descendant:IsA("BasePart") then descendant.Anchored = true end end -- Name the model and parent it to workspace model.Name = "ImageConditionedGeneration" model.Parent = Workspace end ``` ### Method: GenerationService:LoadGeneratedMeshAsync **Signature:** `GenerationService:LoadGeneratedMeshAsync(generationId: string): MeshPart` Retrieves and loads a mesh generated by [GenerateMeshAsync()](/docs/reference/engine/classes/GenerationService.md) using the provided `generationId`. Must be called from client scripts. The mesh can be returned as a [MeshPart](/docs/reference/engine/classes/MeshPart.md) or [Model](/docs/reference/engine/classes/Model.md). Because editable meshes are not replicated, the loaded mesh is not replicated to any other clients and can only be loaded once per `generationId`. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: DynamicGeneration* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `generationId` | `string` | | The unique ID returned by [GenerateMeshAsync()](/docs/reference/engine/classes/GenerationService.md). Identifies the mesh to load. | **Returns:** `MeshPart` — The generated asset, returned as a [Model](/docs/reference/engine/classes/Model.md) with a single [MeshPart](/docs/reference/engine/classes/MeshPart.md) containing an [EditableMesh](/docs/reference/engine/classes/EditableMesh.md). ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: GenericChallengeService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service --- # Class: GenericChallengeService ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: GenericSettings last_updated: 2026-06-29T19:34:07Z inherits: - ServiceProvider - Instance - Object type: class memory_category: Instances tags: - NotCreatable summary: "The abstract class for settings database classes." --- # Class: GenericSettings > The abstract class for settings database classes. ## Inherited Members ### From [ServiceProvider](/docs/reference/engine/classes/ServiceProvider.md) - **Method `FindService(className: string): Instance`**: Returns the service specified by the given className if it's already - **Method `GetService(className: string): Instance`**: Returns the service with the requested class name, creating it if it does - **Method `getService(className: string): Instance`**: *(deprecated)* - **Method `service(className: string): Instance`**: *(deprecated)* - **Event `Close`**: Fires when the current place is exited. - **Event `ServiceAdded`**: Fired when a service is created. - **Event `ServiceRemoving`**: Fired when a service is about to be removed. ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Geometry last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service summary: "An internal Roblox service which cannot be used by developers." --- # Class: Geometry > An internal Roblox service which cannot be used by developers. ## Description An ancient internal Roblox service, which appears to be responsible for all raw geometry shown in the game. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: GeometryService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service summary: "Service containing geometric operations." --- # Class: GeometryService > Service containing geometric operations. ## Description Service containing geometric operations not directly related to specific objects. ## Methods ### Method: GeometryService:CalculateConstraintsToPreserve **Signature:** `GeometryService:CalculateConstraintsToPreserve(source: Instance, destination: Array, options?: Dictionary): Array` Returns a table of [Constraints](/docs/reference/engine/classes/Constraint.md) and [Attachments](/docs/reference/engine/classes/Attachment.md) which you may choose to preserve, along with their respective parents. Iterating over this table lets you decide whether to reparent recommended constraints and attachments to their respective parents. For more information and detailed examples, see https://create.roblox.com/docs/parts/solid-modeling#in-experience-solid-modeling. *Security: None · Thread Safety: Unsafe · Capabilities: CSG* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `source` | `Instance` | | An original object that the solid modeling operation was performed on, for example `part` in [UnionAsync()](/docs/reference/engine/classes/GeometryService.md). | | `destination` | `Array` | | | | `options` | `Dictionary` | `nil` | Options dictionary for the method: - `tolerance` — The distance tolerance, in regards to [Attachment](/docs/reference/engine/classes/Attachment.md) preservation, between the attachment and the closest point on the original part's surface versus the closest point on the resulting part's surface. If the resulting distance following the solid modeling operation is greater than this value, the [Parent](/docs/reference/engine/classes/Instance.md) of attachments and their associated constraints will be `nil` in the returned recommendation table. - `weldConstraintPreserve` — A [WeldConstraintPreserve](/docs/reference/engine/enums/WeldConstraintPreserve.md) enum value describing how [WeldConstraints](/docs/reference/engine/classes/WeldConstraint.md) are preserved in the resulting recommendation table. - `dropAttachmentsWithoutConstraints` — Boolean with default of `true`. If set to `false`, [Attachments](/docs/reference/engine/classes/Attachment.md) that have no [Constraints](/docs/reference/engine/classes/Constraint.md) will be preserved. | **Returns:** `Array` — Table containing information for general case [Constraints](/docs/reference/engine/classes/Constraint.md), [NoCollisionConstraints](/docs/reference/engine/classes/NoCollisionConstraint.md), and [WeldConstraints](/docs/reference/engine/classes/WeldConstraint.md). In cases where an [Attachment](/docs/reference/engine/classes/Attachment.md) or [Constraint](/docs/reference/engine/classes/Constraint.md) should be dropped, its respective parent will be `nil`. For general case [Constraints](/docs/reference/engine/classes/Constraint.md) such as [HingeConstraint](/docs/reference/engine/classes/HingeConstraint.md): | Key | Type | | --- | --- | | `Attachment` | [Attachment](/docs/reference/engine/classes/Attachment.md) | | `Constraint` | [Constraint](/docs/reference/engine/classes/Constraint.md) or `nil` | | `AttachmentParent` | [BasePart](/docs/reference/engine/classes/BasePart.md) or `nil` | | `ConstraintParent` | [BasePart](/docs/reference/engine/classes/BasePart.md) or `nil` | For [WeldConstraints](/docs/reference/engine/classes/WeldConstraint.md): | Key | Type | | --- | --- | | `WeldConstraint` | [WeldConstraint](/docs/reference/engine/classes/WeldConstraint.md) | | `WeldConstraintParent` | [BasePart](/docs/reference/engine/classes/BasePart.md) or `nil` | | `WeldConstraintPart0` | [BasePart](/docs/reference/engine/classes/BasePart.md) | | `WeldConstraintPart1` | [BasePart](/docs/reference/engine/classes/BasePart.md) | For [NoCollisionConstraints](/docs/reference/engine/classes/NoCollisionConstraint.md): | Key | Type | | --- | --- | | `NoCollisionConstraint` | [NoCollisionConstraint](/docs/reference/engine/classes/NoCollisionConstraint.md) | | `NoCollisionConstraintParent` | [BasePart](/docs/reference/engine/classes/BasePart.md) or `nil` | | `NoCollisionConstraintPart0` | [BasePart](/docs/reference/engine/classes/BasePart.md) | | `NoCollisionConstraintPart1` | [BasePart](/docs/reference/engine/classes/BasePart.md) | **Preserve Constraints** The following example shows how to preserve [Attachments](/docs/reference/engine/classes/Attachment.md) and [Constraints](/docs/reference/engine/classes/Constraint.md) based on a recommended table produced by [CalculateConstraintsToPreserve()](/docs/reference/engine/classes/GeometryService.md). ```lua local GeometryService = game:GetService("GeometryService") local main, other = workspace.Part1, workspace.Part2 local success, newParts = pcall(function() return GeometryService:SubtractAsync(main, {other}) end) if success and newParts then for _, p in newParts do p.Parent = workspace end for _, rec in GeometryService:CalculateConstraintsToPreserve(main, newParts) do if rec.Constraint then rec.Constraint.Parent = rec.ConstraintParent end end end main:Destroy() other:Destroy() ``` ### Method: GeometryService:FragmentAsync **Signature:** `GeometryService:FragmentAsync(part: BasePart, sites: Array, options?: Dictionary): Array` Breaks a [BasePart](/docs/reference/engine/classes/BasePart.md) into multiple [MeshPart](/docs/reference/engine/classes/MeshPart.md) instances, according to the pattern of points passed in, by using voronoi decomposition. [Terrain](/docs/reference/engine/classes/Terrain.md) is not supported. Similar to [Clone()](/docs/reference/engine/classes/Instance.md), the returned parts have no set [Parent](/docs/reference/engine/classes/Instance.md). For more information and detailed examples, see https://create.roblox.com/docs/parts/solid-modeling#in-experience-solid-modeling. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: CSG* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `part` | `BasePart` | | A [Part](/docs/reference/engine/classes/Part.md), [PartOperation](/docs/reference/engine/classes/PartOperation.md), or [MeshPart](/docs/reference/engine/classes/MeshPart.md) to operate on. | | `sites` | `Array` | | Array of `Vector3` defining the site positions. Each site will become a separate part. You can also provide a jagged 2D array of `Vector3` by including inner arrays of `Vector3` as elements of the outer array. Each inner array will have all of its voronoi cells merged into a single part. [GeometryService:GenerateFragmentSites](/docs/reference/engine/classes/GeometryService.md) can be used to easily create this input. | | `options` | `Dictionary` | `nil` | Options table containing all the controls for the method: - `CollisionFidelity` — The value of [CollisionFidelity](/docs/reference/engine/classes/TriangleMeshPart.md) in the resulting parts, with one caveat: If a 2D array of sites is provided, this collision fidelity will only be applied to parts which came from more than one site. The others will be given `Hull` precision. - `RenderFidelity` — The value of [RenderFidelity](/docs/reference/engine/classes/MeshPart.md) in the resulting parts. - `FluidFidelity` — The value of [FluidFidelity](/docs/reference/engine/classes/TriangleMeshPart.md) in the resulting parts. - `SplitApart` — Boolean controlling whether a part should be split into multiple parts if it contains multiple connected components. Default is `true` (split). | **Returns:** `Array` — Array of [MeshPart](/docs/reference/engine/classes/MeshPart.md) along with mapping info. Each array element is a Dictionary with two elements: `{ “Instance”: instance, “Index”: index }`. `Index` is the index in the outer array of sites; in other words, it tells you which group of sites this instance came from. Note that it is possible for multiple instances to have the same index, if SplitApart is `true`. **Fragment a Part** This example shatters the entire input part into pieces. ```lua local GeometryService = game:GetService("GeometryService") local inputPart = Instance.new("Part") inputPart.Position = Vector3.new(0, 0.7, 20) local sites = GeometryService:GenerateFragmentSites(inputPart) local success, fragments = pcall( function() return GeometryService:FragmentAsync(inputPart, sites) end) if success and fragments then for _, item in fragments do local instance = item.Instance instance.Parent = workspace end end ``` ### Method: GeometryService:GenerateFragmentSites **Signature:** `GeometryService:GenerateFragmentSites(part: BasePart, options?: Dictionary): Array` Provides an array of positions which can easily be passed into [FragmentAsync()](/docs/reference/engine/classes/GeometryService.md) to perform common types of destruction: Fragmenting an entire [BasePart](/docs/reference/engine/classes/BasePart.md) into pieces, or a localized area of a [BasePart](/docs/reference/engine/classes/BasePart.md) into pieces. The positions outputted are partially random, so the output should not be relied on to look exactly the same as the first time it is run with the same parameters. For more information and detailed examples, see https://create.roblox.com/docs/parts/solid-modeling#in-experience-solid-modeling. Luau code to mimic this API has also been provided on that page, which can be freely modified if a slightly different effect is desired. *Security: None · Thread Safety: Unsafe · Capabilities: CSG* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `part` | `BasePart` | | The [Part](/docs/reference/engine/classes/Part.md), [PartOperation](/docs/reference/engine/classes/PartOperation.md), or [MeshPart](/docs/reference/engine/classes/MeshPart.md) which you are planning to pass into [FragmentAsync()](/docs/reference/engine/classes/GeometryService.md). This is necessary to make the fragment site generation and the subsequent [FragmentAsync()](/docs/reference/engine/classes/GeometryService.md) call efficient. | | `options` | `Dictionary` | `nil` | Options table containing all the controls for the method: - `SiteSpacing` — The approximate distance between sites, which directly corresponds to the diameter of the resulting fragments. If not specified, a reasonable value will be chosen. - `Origin` — If provided, this will be the center of the area to be fragmented. If not provided, the entire object will be fragmented. - `Radius` — If provided, this will be the center of the area to be fragmented. Either `Origin` and `Radius` should both be provided, or neither. | **Returns:** `Array` — An array of `Vector3` which is typically passed into [FragmentAsync()](/docs/reference/engine/classes/GeometryService.md). The output depends on the options provided. If `Origin` and `Radius` are provided, then the output array will contain several `Vector3` elements which will all be located within the radius, but the first element of the array will be an inner array containing many `Vector3` sites which are outside the radius. If `Origin` and `Radius` are not provided, the output will simply be an array of `Vector3` positions within the extents of the input `part`. **Localized Fragment** This example shatters the corner of a part into pieces. ```lua local GeometryService = game:GetService("GeometryService") local inputPart = workspace.Part local pos = inputPart.Position + inputPart.Size / 2 local sites = GeometryService:GenerateFragmentSites(inputPart, {Origin = pos, Radius = 1.5}) local success, fragments = pcall( function() return GeometryService:FragmentAsync(inputPart, sites) end) if success and fragments then for _, item in fragments do local instance = item.Instance instance.Parent = inputPart.Parent end inputPart:Destroy() end ``` ### Method: GeometryService:IntersectAsync **Signature:** `GeometryService:IntersectAsync(part: Instance, parts: Array, options?: Dictionary): Array` Creates one or more [PartOperations](/docs/reference/engine/classes/PartOperation.md) or [MeshParts](/docs/reference/engine/classes/MeshPart.md) from the intersecting geometry of multiple parts. Primitive [Parts](/docs/reference/engine/classes/Part.md), [PartOperations](/docs/reference/engine/classes/PartOperation.md), and [MeshParts](/docs/reference/engine/classes/MeshPart.md) are supported as inputs, but not [Terrain](/docs/reference/engine/classes/Terrain.md). Similarly to [Clone()](/docs/reference/engine/classes/Instance.md), the returned parts have no set [Parent](/docs/reference/engine/classes/Instance.md). In most cases, you should parent the results to the same place as the main part, then [Destroy()](/docs/reference/engine/classes/Instance.md) the original parts. This function replaces [BasePart:IntersectAsync()](/docs/reference/engine/classes/BasePart.md). Go to that page for a description of the differences. For more information and detailed examples, see https://create.roblox.com/docs/parts/solid-modeling#in-experience-solid-modeling. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: CSG* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `part` | `Instance` | | Main [Part](/docs/reference/engine/classes/Part.md), [PartOperation](/docs/reference/engine/classes/PartOperation.md), or [MeshPart](/docs/reference/engine/classes/MeshPart.md) to operate on. | | `parts` | `Array` | | Array of other parts to intersect with the main part. | | `options` | `Dictionary` | `nil` | Options table containing all the controls for the method: - `CollisionFidelity` — The value of [CollisionFidelity](/docs/reference/engine/classes/TriangleMeshPart.md) in the resulting parts. - `RenderFidelity` — The value of [RenderFidelity](/docs/reference/engine/classes/PartOperation.md) or [RenderFidelity](/docs/reference/engine/classes/MeshPart.md) in the resulting parts. - `FluidFidelity` — The value of [FluidFidelity](/docs/reference/engine/classes/TriangleMeshPart.md) in the resulting parts. - `SplitApart` — Boolean controlling whether the objects should all be kept together or properly split apart. Default is `true` (split). | **Returns:** `Array` — One or more [PartOperations](/docs/reference/engine/classes/PartOperation.md) or [MeshParts](/docs/reference/engine/classes/MeshPart.md). If the input contained any [MeshParts](/docs/reference/engine/classes/MeshPart.md), then the results will always be [MeshParts](/docs/reference/engine/classes/MeshPart.md). **Union two parts** This example intersects the geometry of two blocks. ```lua local GeometryService = game:GetService("GeometryService") local mainPart = Instance.new("Part") local otherPart = Instance.new("Part") otherPart.Position = Vector3.new(1, 0.5, 1) local success, newParts = pcall(function() return GeometryService:IntersectAsync(mainPart, {otherPart}) end) if success and newParts then for _, newPart in pairs(newParts) do newPart.Parent = workspace end end ``` ### Method: GeometryService:SubtractAsync **Signature:** `GeometryService:SubtractAsync(part: Instance, parts: Array, options?: Dictionary): Array` Creates one or more [PartOperations](/docs/reference/engine/classes/PartOperation.md) or [MeshParts](/docs/reference/engine/classes/MeshPart.md) consisting of the space occupied by one part minus the space occupied by the other parts. Primitive [Parts](/docs/reference/engine/classes/Part.md), [PartOperations](/docs/reference/engine/classes/PartOperation.md), and [MeshParts](/docs/reference/engine/classes/MeshPart.md) are supported as inputs, but not [Terrain](/docs/reference/engine/classes/Terrain.md). Similarly to [Clone()](/docs/reference/engine/classes/Instance.md), the returned parts have no set [Parent](/docs/reference/engine/classes/Instance.md). In most cases, you should parent the results to the same place as the main part, then [Destroy()](/docs/reference/engine/classes/Instance.md) the original parts. This function replaces [BasePart:SubtractAsync()](/docs/reference/engine/classes/BasePart.md). Go to that page for a description of the differences. For more information and detailed examples, see https://create.roblox.com/docs/parts/solid-modeling#in-experience-solid-modeling. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: CSG* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `part` | `Instance` | | Main [Part](/docs/reference/engine/classes/Part.md), [PartOperation](/docs/reference/engine/classes/PartOperation.md), or [MeshPart](/docs/reference/engine/classes/MeshPart.md) to operate on. | | `parts` | `Array` | | Array of parts to subtract from the main part. | | `options` | `Dictionary` | `nil` | Options table containing all the controls for the method: - `CollisionFidelity` — The value of [CollisionFidelity](/docs/reference/engine/classes/TriangleMeshPart.md) in the resulting parts. - `RenderFidelity` — The value of [RenderFidelity](/docs/reference/engine/classes/PartOperation.md) or [RenderFidelity](/docs/reference/engine/classes/MeshPart.md) in the resulting parts. - `FluidFidelity` — The value of [FluidFidelity](/docs/reference/engine/classes/TriangleMeshPart.md) in the resulting parts. - `SplitApart` — Boolean controlling whether the objects should all be kept together or properly split apart. Default is `true` (split). | **Returns:** `Array` — One or more [PartOperations](/docs/reference/engine/classes/PartOperation.md) or [MeshParts](/docs/reference/engine/classes/MeshPart.md). If the input contained any [MeshParts](/docs/reference/engine/classes/MeshPart.md), then the results will always be [MeshParts](/docs/reference/engine/classes/MeshPart.md). **Subtract two parts** This example subtracts the geometry of two blocks. ```lua local GeometryService = game:GetService("GeometryService") local mainPart = Instance.new("Part") local otherPart = Instance.new("Part") otherPart.Position = Vector3.new(1, 0.5, 1) local success, newParts = pcall(function() return GeometryService:SubtractAsync(mainPart, {otherPart}) end) if success and newParts then for _, newPart in pairs(newParts) do newPart.Parent = workspace end end ``` ### Method: GeometryService:SweepPartAsync **Signature:** `GeometryService:SweepPartAsync(part: BasePart, cframes: Array, options?: Dictionary): MeshPart` Creates a [MeshPart](/docs/reference/engine/classes/MeshPart.md) which has the shape of the input part stretched/dragged through the given set of `CFrame` positions. The exact shape of the result is defined as the union of the convex hulls of each adjacent pair of `CFrames`. If a single `CFrame` is provided, the result will be a convex hull of the input part. For more information and detailed examples, see https://create.roblox.com/docs/parts/solid-modeling#in-experience-solid-modeling. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: CSG* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `part` | `BasePart` | | A [Part](/docs/reference/engine/classes/Part.md), [PartOperation](/docs/reference/engine/classes/PartOperation.md), or [MeshPart](/docs/reference/engine/classes/MeshPart.md) to operate on. | | `cframes` | `Array` | | Array of coordinate frames to sweep parts through. | | `options` | `Dictionary` | `nil` | Options table containing all the controls for the method: - `CollisionFidelity` — The value of [CollisionFidelity](/docs/reference/engine/classes/TriangleMeshPart.md) in the resulting parts. - `RenderFidelity` — The value of [RenderFidelity](/docs/reference/engine/classes/MeshPart.md) in the resulting parts. - `FluidFidelity` — The value of [FluidFidelity](/docs/reference/engine/classes/TriangleMeshPart.md) in the resulting parts. | **Returns:** `MeshPart` — A new [MeshPart](/docs/reference/engine/classes/MeshPart.md) with the swept geometry. **Sweep a Part** This example sweeps a block through a twisting path of `CFrames`. ```lua local GeometryService = game:GetService("GeometryService") local inputPart = Instance.new("Part") inputPart.Shape = Enum.PartType.Ball local cframeList = {} for i = 1, 50 do local rotation = CFrame.Angles(0, i * 0.2, 0) local position = Vector3.new(0, i * 0.1, -1) table.insert(cframeList, rotation * CFrame.new(position)) end local success, sweptPart = pcall( function() return GeometryService:SweepPartAsync(inputPart, cframeList) end) if success and sweptPart then sweptPart.Parent = workspace end ``` ### Method: GeometryService:UnionAsync **Signature:** `GeometryService:UnionAsync(part: Instance, parts: Array, options?: Dictionary): Array` Creates one or more [PartOperations](/docs/reference/engine/classes/PartOperation.md) or [MeshParts](/docs/reference/engine/classes/MeshPart.md) consisting of the space occupied by one part plus the space occupied by the other parts. Primitive [Parts](/docs/reference/engine/classes/Part.md), [PartOperations](/docs/reference/engine/classes/PartOperation.md), and [MeshParts](/docs/reference/engine/classes/MeshPart.md) are supported as inputs, but not [Terrain](/docs/reference/engine/classes/Terrain.md). Similarly to [Clone()](/docs/reference/engine/classes/Instance.md), the returned parts have no set [Parent](/docs/reference/engine/classes/Instance.md). In most cases, you should parent the results to the same place as the main part, then [Destroy()](/docs/reference/engine/classes/Instance.md) the original parts. This function replaces [BasePart:UnionAsync()](/docs/reference/engine/classes/BasePart.md). Go to that page for a description of the differences. For more information and detailed examples, see https://create.roblox.com/docs/parts/solid-modeling#in-experience-solid-modeling. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: CSG* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `part` | `Instance` | | Main [Part](/docs/reference/engine/classes/Part.md), [PartOperation](/docs/reference/engine/classes/PartOperation.md), or [MeshPart](/docs/reference/engine/classes/MeshPart.md) to operate on. | | `parts` | `Array` | | Array of parts to union with the main part. | | `options` | `Dictionary` | `nil` | Options table containing all the controls for the method: - `CollisionFidelity` — The value of [CollisionFidelity](/docs/reference/engine/classes/TriangleMeshPart.md) in the resulting parts. - `RenderFidelity` — The value of [RenderFidelity](/docs/reference/engine/classes/PartOperation.md) or [RenderFidelity](/docs/reference/engine/classes/MeshPart.md) in the resulting parts. - `FluidFidelity` — The value of [FluidFidelity](/docs/reference/engine/classes/TriangleMeshPart.md) in the resulting parts. - `SplitApart` — Boolean controlling whether the objects should all be kept together or properly split apart. Default is `true` (split). | **Returns:** `Array` — One or more [PartOperations](/docs/reference/engine/classes/PartOperation.md) or [MeshParts](/docs/reference/engine/classes/MeshPart.md). If the input contained any [MeshParts](/docs/reference/engine/classes/MeshPart.md), then the results will always be [MeshParts](/docs/reference/engine/classes/MeshPart.md). **Union two parts** This example combines the geometry of two blocks. ```lua local GeometryService = game:GetService("GeometryService") local mainPart = Instance.new("Part") local otherPart = Instance.new("Part") otherPart.Position = Vector3.new(1, 0.5, 1) local success, newParts = pcall(function() return GeometryService:UnionAsync(mainPart, {otherPart}) end) if success and newParts then for _, newPart in pairs(newParts) do newPart.Parent = workspace end end ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: GetTextBoundsParams last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotReplicated summary: "Use with TextService:GetTextBoundsAsync() to measure the size of text." --- # Class: GetTextBoundsParams > Use with [TextService:GetTextBoundsAsync()](/docs/reference/engine/classes/TextService.md) to measure the size of text. ## Description Pass this instance to [TextService:GetTextBoundsAsync()](/docs/reference/engine/classes/TextService.md) to measure the size of text. ## Code Samples **Measuring Text Size** ```lua local TextService = game:GetService("TextService") -- Declare parameters local params = Instance.new("GetTextBoundsParams") params.Text = "Hello world!" params.Font = Font.new("rbxasset://fonts/families/GrenzeGotisch.json", Enum.FontWeight.Thin) params.Size = 20 params.Width = 200 local success, bounds = pcall(function() return TextService:GetTextBoundsAsync(params) end) if success then print(bounds) end ``` ## Properties ### Property: GetTextBoundsParams.Font ```json { "type": "Datatype.Font", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` The [Font](/docs/reference/engine/datatypes/Font.md) of the text being measured. Corresponds to the [TextLabel.FontFace](/docs/reference/engine/classes/TextLabel.md) property on text objects. Not to be confused with [Font](/docs/reference/engine/enums/Font.md). This is an object that you can create using [Font.new()](/docs/reference/engine/datatypes/Font.md). ### Property: GetTextBoundsParams.RichText ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` ### Property: GetTextBoundsParams.Size ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` The size of the text that is being measured. Corresponds to the [TextLabel.TextSize](/docs/reference/engine/classes/TextLabel.md) property. ### Property: GetTextBoundsParams.Text ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` The text being measured. ### Property: GetTextBoundsParams.Width ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` The width of the container for line breaking. By default, the value is 0, which means no line breaking will be performed. You can set it to the width of the container that you'll be putting the text into. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: GlobalDataStore last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated summary: "An object that exposes methods to access a single data store." --- # Class: GlobalDataStore > An object that exposes methods to access a single data store. ## Description A **GlobalDataStore** exposes functions for saving and loading data for the [DataStoreService](/docs/reference/engine/classes/DataStoreService.md). See [Data stores](/docs/en-us/cloud-services/data-stores.md) for an in-depth guide on data structure, management, error handling, limits, and more. Ordered data stores do not support versioning and metadata, so [DataStoreKeyInfo](/docs/reference/engine/classes/DataStoreKeyInfo.md) is always `nil` for keys in an [OrderedDataStore](/docs/reference/engine/classes/OrderedDataStore.md). If you need versioning and metadata support, use a [DataStore](/docs/reference/engine/classes/DataStore.md). ## Methods ### Method: GlobalDataStore:GetAsync **Signature:** `GlobalDataStore:GetAsync(key: string, options?: DataStoreGetOptions): Tuple` This function returns the latest value of the provided key and a [DataStoreKeyInfo](/docs/reference/engine/classes/DataStoreKeyInfo.md) instance. If the key does not exist or if the latest version has been marked as deleted, both return values will be `nil`. Keys are cached locally for 4 seconds after the first read. A [GlobalDataStore:GetAsync()](/docs/reference/engine/classes/GlobalDataStore.md) call within these 4 seconds returns a value from the cache. Modifications to the key by [GlobalDataStore:SetAsync()](/docs/reference/engine/classes/GlobalDataStore.md) or [GlobalDataStore:UpdateAsync()](/docs/reference/engine/classes/GlobalDataStore.md) apply to the cache immediately and restart the 4 second timer. To get a specific version, such as a version before the latest, use [DataStore:GetVersionAsync()](/docs/reference/engine/classes/DataStore.md). *Yields · Security: None · Thread Safety: Unsafe · Capabilities: DataStore* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | The key name for which the value is requested. If [DataStoreOptions.AllScopes](/docs/reference/engine/classes/DataStoreOptions.md) was set to true when accessing the data store through [DataStoreService:GetDataStore()](/docs/reference/engine/classes/DataStoreService.md), this key name must be prepended with the original scope as in "scope/key". | | `options` | `DataStoreGetOptions` | `nil` | | **Returns:** `Tuple` — The value of the entry in the data store with the given key and a [DataStoreKeyInfo](/docs/reference/engine/classes/DataStoreKeyInfo.md) instance that includes the version number, date and time the version was created, and functions to retrieve [UserIds](/docs/reference/engine/classes/Player.md) and metadata. ### Method: GlobalDataStore:IncrementAsync **Signature:** `GlobalDataStore:IncrementAsync(key: string, delta?: int, userIds?: Array, options?: DataStoreIncrementOptions): Variant` This function increments the value of a key by the provided amount (both must be integers). Values in [GlobalDataStores](/docs/reference/engine/classes/GlobalDataStore.md) are **versioned** as outlined in [versioning](/docs/en-us/cloud-services/data-stores/versioning-listing-and-caching.md#versioning). [OrderedDataStores](/docs/reference/engine/classes/OrderedDataStore.md) do not support versioning, so calling this method on an ordered data store key will overwrite the current value with the incremented value and make previous versions inaccessible. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: DataStore* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | Key name for which the value should be updated. If [DataStoreOptions.AllScopes](/docs/reference/engine/classes/DataStoreOptions.md) was set to true when accessing the data store through [DataStoreService:GetDataStore()](/docs/reference/engine/classes/DataStoreService.md), this key name must be prepended with the original scope as in "scope/key". | | `delta` | `int` | `1` | Amount to increment the current value by. | | `userIds` | `Array` | `{}` | **(Optional)** A table of [UserIds](/docs/reference/engine/classes/Player.md) to associate with the key. | | `options` | `DataStoreIncrementOptions` | `nil` | **(Optional)** [DataStoreIncrementOptions](/docs/reference/engine/classes/DataStoreIncrementOptions.md) instance that combines multiple additional parameters as custom metadata and allows for future extensibility. | **Returns:** `Variant` — The updated value of the entry in the data store with the given key. ### Method: GlobalDataStore:RemoveAsync **Signature:** `GlobalDataStore:RemoveAsync(key: string): Tuple` This function marks the specified key as deleted by creating a new "tombstone" version of the key. Prior to this, it returns the latest version prior to the remove call. After a key is removed via this function, [GlobalDataStore:GetAsync()](/docs/reference/engine/classes/GlobalDataStore.md) calls for the key will return `nil`. Older versions of the key remain accessible through [DataStore:ListVersionsAsync()](/docs/reference/engine/classes/DataStore.md) and [DataStore:GetVersionAsync()](/docs/reference/engine/classes/DataStore.md), assuming they have not expired. [OrderedDataStore](/docs/reference/engine/classes/OrderedDataStore.md) does not support versioning, so calling [RemoveAsync()](/docs/reference/engine/classes/GlobalDataStore.md) on an [OrderedDataStore](/docs/reference/engine/classes/OrderedDataStore.md) key will permanently delete it. Removed objects will be deleted permanently after 30 days. If the previous values were already deleted via [GlobalDataStore:RemoveAsync()](/docs/reference/engine/classes/GlobalDataStore.md) or [DataStore:RemoveVersionAsync()](/docs/reference/engine/classes/DataStore.md), the function will return `nil`, `nil` for value and [DataStoreKeyInfo](/docs/reference/engine/classes/DataStoreKeyInfo.md) respectively. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: DataStore* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | Key name to be removed. If [DataStoreOptions.AllScopes](/docs/reference/engine/classes/DataStoreOptions.md) was set to true when accessing the data store through [DataStoreService:GetDataStore()](/docs/reference/engine/classes/DataStoreService.md), this key name must be prepended with the original scope as in "scope/key". | **Returns:** `Tuple` — The value of the data store prior to deletion and a [DataStoreKeyInfo](/docs/reference/engine/classes/DataStoreKeyInfo.md) instance that includes the version number, date and time the version was created, and functions to retrieve [UserIds](/docs/reference/engine/classes/Player.md) and metadata. ### Method: GlobalDataStore:SetAsync **Signature:** `GlobalDataStore:SetAsync(key: string, value: Variant, userIds?: Array, options?: DataStoreSetOptions): Variant` This function sets the latest value, [UserIds](/docs/reference/engine/classes/Player.md), and metadata for the given key. Values in [GlobalDataStores](/docs/reference/engine/classes/GlobalDataStore.md) are **versioned** as outlined in [versioning](/docs/en-us/cloud-services/data-stores/versioning-listing-and-caching.md#versioning). [OrderedDataStores](/docs/reference/engine/classes/OrderedDataStore.md) do not support versioning, so calling this method on an ordered data store key will overwrite the current value and make previous versions inaccessible. Metadata definitions must always be updated with a value, even if there are no changes to the current value; otherwise the current value will be lost. Any string being stored in a data store must be valid [UTF-8](/docs/reference/engine/globals/utf8.md). In UTF-8, values greater than 127 are used exclusively for encoding multi-byte codepoints, so a single byte greater than 127 will not be valid UTF-8 and the [GlobalDataStore:SetAsync()](/docs/reference/engine/classes/GlobalDataStore.md) attempt will fail. #### Set vs. Update [GlobalDataStore:SetAsync()](/docs/reference/engine/classes/GlobalDataStore.md) is best for a quick update of a specific key, and it only counts against the write limit. However, it may cause data inconsistency if two servers attempt to set the same key at the same time. [GlobalDataStore:UpdateAsync()](/docs/reference/engine/classes/GlobalDataStore.md) is safer for handling multi-server attempts because it reads the current key value (from whatever server last updated it) before making any changes. However, it's somewhat slower because it reads before it writes, and it also counts against both the read and write limit. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: DataStore* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | Key name for which the value should be set. If [DataStoreOptions.AllScopes](/docs/reference/engine/classes/DataStoreOptions.md) was set to true when accessing the data store through [DataStoreService:GetDataStore()](/docs/reference/engine/classes/DataStoreService.md), this key name must be prepended with the original scope as in "scope/key". | | `value` | `Variant` | | The value that the data store key will be set to. | | `userIds` | `Array` | `{}` | Table of [UserIds](/docs/reference/engine/classes/Player.md), highly recommended to assist with GDPR tracking/removal. | | `options` | `DataStoreSetOptions` | `nil` | **(Optional)** [DataStoreSetOptions](/docs/reference/engine/classes/DataStoreSetOptions.md) instance that allows for metadata specification on the key. | **Returns:** `Variant` — The version identifier of the newly created version. It can be used to retrieve key info using [GetVersionAsync()](/docs/reference/engine/classes/DataStore.md) or to remove it using [RemoveVersionAsync()](/docs/reference/engine/classes/DataStore.md). ### Method: GlobalDataStore:UpdateAsync **Signature:** `GlobalDataStore:UpdateAsync(key: string, transformFunction: Function): Tuple` This function retrieves the value and metadata of a key from the data store and updates it with a new value determined by the callback function specified through the second parameter. If the callback returns `nil`, the write operation is cancelled and the value remains unchanged. Values in [GlobalDataStores](/docs/reference/engine/classes/GlobalDataStore.md) are **versioned** as outlined in [versioning](/docs/en-us/cloud-services/data-stores/versioning-listing-and-caching.md#versioning). [OrderedDataStores](/docs/reference/engine/classes/OrderedDataStore.md) do not support versioning, so calling this method on an ordered data store key will overwrite the current value and make previous versions inaccessible. In cases where another game server updated the key in the short timespan between retrieving the key's current value and setting the key's value, [GlobalDataStore:UpdateAsync()](/docs/reference/engine/classes/GlobalDataStore.md) will call the function again, discarding the result of the previous call. The function will be called as many times as needed until the data is saved **or** until the callback function returns `nil`. This can be used to ensure that no data is overwritten. Any string being stored in a data store must be valid [UTF-8](/docs/reference/engine/globals/utf8.md). In UTF-8, values greater than 127 are used exclusively for encoding multi-byte codepoints, so a single byte greater than 127 will not be valid UTF-8 and the [GlobalDataStore:UpdateAsync()](/docs/reference/engine/classes/GlobalDataStore.md) attempt will fail. #### Set vs. Update [GlobalDataStore:SetAsync()](/docs/reference/engine/classes/GlobalDataStore.md) is best for a quick update of a specific key, and it only counts against the write limit. However, it may cause data inconsistency if two servers attempt to set the same key at the same time. [GlobalDataStore:UpdateAsync()](/docs/reference/engine/classes/GlobalDataStore.md) is safer for handling multi-server attempts because it reads the current key value (from whatever server last updated it) before making any changes. However, it's somewhat slower because it reads before it writes, and it also counts against both the read and write limit. #### Callback Function The callback function accepts two arguments: - Current value of the key prior to the update. - [DataStoreKeyInfo](/docs/reference/engine/classes/DataStoreKeyInfo.md) instance that contains the latest version information (this argument can be ignored if metadata is not being used). In turn, the callback function returns up to three values: - The new value to set for the key. - An array of [UserIds](/docs/reference/engine/classes/Player.md) to associate with the key. [DataStoreKeyInfo:GetUserIds()](/docs/reference/engine/classes/DataStoreKeyInfo.md) should be returned unless the existing IDs are being changed; otherwise all existing IDs will be cleared. - A Luau table containing metadata to associate with the key. [DataStoreKeyInfo:GetMetadata()](/docs/reference/engine/classes/DataStoreKeyInfo.md) should be returned unless the existing metadata is being changed; otherwise all existing metadata will be cleared. If the callback returns `nil` instead, the current server will stop attempting to update the key. The callback function cannot yield, so do **not** include calls like [task.wait()](/docs/reference/engine/globals/task.md). *Yields · Security: None · Thread Safety: Unsafe · Capabilities: DataStore* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | Key name for which the value should be updated. If [DataStoreOptions.AllScopes](/docs/reference/engine/classes/DataStoreOptions.md) was set to true when accessing the data store through [DataStoreService:GetDataStore()](/docs/reference/engine/classes/DataStoreService.md), this key name must be prepended with the original scope as in "scope/key". | | `transformFunction` | `Function` | | Transform function that takes the current value and [DataStoreKeyInfo](/docs/reference/engine/classes/DataStoreKeyInfo.md) as parameters and returns the new value along with optional [UserIds](/docs/reference/engine/classes/Player.md) and metadata. | **Returns:** `Tuple` — The updated value of the entry in the data store with the given key and a [DataStoreKeyInfo](/docs/reference/engine/classes/DataStoreKeyInfo.md) instance that includes the version number, date and time the version was created, and functions to retrieve [UserIds](/docs/reference/engine/classes/Player.md) and metadata. ### Method: GlobalDataStore:OnUpdate **Signature:** `GlobalDataStore:OnUpdate(key: string, callback: Function): RBXScriptConnection` > **Deprecated:** This function has been deprecated and should not be used in new work. You can use the [Cross Server Messaging Service](/docs/reference/engine/classes/MessagingService.md) to publish and subscribe to topics to receive near real-time updates, completely replacing the need for this function. This function sets `callback` as the function to be run any time the value associated with the `key` changes. Once every minute, OnUpdate polls for changes by other servers. Changes made on the same server will run the function immediately. In other words, functions like [IncrementAsync()](/docs/reference/engine/classes/GlobalDataStore.md), [SetAsync()](/docs/reference/engine/classes/GlobalDataStore.md), and [UpdateAsync()](/docs/reference/engine/classes/GlobalDataStore.md) change the key's value in the data store and will cause the function to run. It's recommended that you **disconnect** the connection when the subscription to the key is no longer needed. *Security: None · Thread Safety: Unsafe · Capabilities: DataStore* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | The key identifying the entry being retrieved from the data store. | | `callback` | `Function` | | The function to be executed any time the value associated with **key** is changed. | **Returns:** `RBXScriptConnection` — The connection to the key being tracked for updates. **Print Data Store Value on Update** The sample creates an [GlobalDataStore:OnUpdate()](/docs/reference/engine/classes/GlobalDataStore.md) connection with the key `myKey` and the `printOut()` function, then it sets the `myKey` store to 11. Since the connection with `myKey` is open, `printOut()` executes and prints the input (the updated value). Immediately after this occurs, the script disconnects the connection. ```lua local DataStoreService = game:GetService("DataStoreService") local sampleDataStore = DataStoreService:GetDataStore("MyDataStore") local connection local function printOut(input) print(input) connection:Disconnect() end connection = sampleDataStore:OnUpdate("myKey", printOut) local success, result = pcall(function() sampleDataStore:SetAsync("myKey", 11) end) if not success then warn(result) end ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: GlobalSettings last_updated: 2026-06-29T19:34:07Z inherits: - GenericSettings - ServiceProvider - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotBrowsable summary: "Collection of menu settings for Roblox Studio." --- # Class: GlobalSettings > Collection of menu settings for Roblox Studio. ## Description The base object used for Roblox Studio's settings menu. Can be accessed by using the `settings()` function. ## Settings classes under the GlobalSettings - [DebugSettings](/docs/reference/engine/classes/DebugSettings.md) - [GameSettings](/docs/reference/engine/classes/GameSettings.md) - [LuaSettings](/docs/reference/engine/classes/LuaSettings.md) - [NetworkSettings](/docs/reference/engine/classes/NetworkSettings.md) - [PhysicsSettings](/docs/reference/engine/classes/PhysicsSettings.md) - [RenderSettings](/docs/reference/engine/classes/RenderSettings.md) - [Studio](/docs/reference/engine/classes/Studio.md) ## Methods ### Method: GlobalSettings:GetFFlag **Signature:** `GlobalSettings:GetFFlag(name: string): boolean` Returns the value of an FFlag if it exists. *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name` | `string` | | | **Returns:** `boolean` ### Method: GlobalSettings:GetFVariable **Signature:** `GlobalSettings:GetFVariable(name: string): string` Returns the value of an FVariable, if it exists. *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name` | `string` | | | **Returns:** `string` ## Inherited Members ### From [ServiceProvider](/docs/reference/engine/classes/ServiceProvider.md) - **Method `FindService(className: string): Instance`**: Returns the service specified by the given className if it's already - **Method `GetService(className: string): Instance`**: Returns the service with the requested class name, creating it if it does - **Method `getService(className: string): Instance`**: *(deprecated)* - **Method `service(className: string): Instance`**: *(deprecated)* - **Event `Close`**: Fires when the current place is exited. - **Event `ServiceAdded`**: Fired when a service is created. - **Event `ServiceRemoving`**: Fired when a service is about to be removed. ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Glue last_updated: 2026-06-29T19:34:07Z inherits: - JointInstance - Instance - Object type: class memory_category: BaseParts tags: - Deprecated summary: "Glue is a type of joint that can break when enough force is applied." --- # Class: Glue > Glue is a type of joint that can break when enough force is applied. ## Properties ### Property: Glue.F0 ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Physics" ] } ``` F0 helps determining the Glue face of a [Glue](/docs/reference/engine/classes/Glue.md), which determines the amount of force needed to break the joint. ### Property: Glue.F1 ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Physics" ] } ``` F1 helps determining the Glue face of a [Glue](/docs/reference/engine/classes/Glue.md), which determines the amount of force needed to break the joint. ### Property: Glue.F2 ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Physics" ] } ``` F2 helps determining the Glue face of a [Glue](/docs/reference/engine/classes/Glue.md), which determines the amount of force needed to break the joint. ### Property: Glue.F3 ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Physics" ] } ``` F3 helps determining the Glue face of a [Glue](/docs/reference/engine/classes/Glue.md), which determines the amount of force needed to break the joint. ## Inherited Members ### From [JointInstance](/docs/reference/engine/classes/JointInstance.md) - **Property `Active`** (`boolean`): Determines if the joint is currently active in the world. - **Property `C0`** (`CFrame`): Determines how the offset point is attached to - **Property `C1`** (`CFrame`): Subtracted from the C0 property to create an - **Property `Enabled`** (`boolean`): Sets whether the joint is active or not. - **Property `Part0`** (`BasePart`): The first BasePart that the joint connects. - **Property `Part1`** (`BasePart`): The second BasePart that the joint connects. - **Property `part1`** (`BasePart`): *(deprecated, hidden)* ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: GroundController last_updated: 2026-06-29T19:34:07Z inherits: - ControllerBase - Instance - Object type: class memory_category: Instances --- # Class: GroundController ## Properties ### Property: GroundController.AccelerationTime ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Movement", "capabilities": [ "Basic" ], "simulationAccess": true } ``` Estimated time (in seconds) taken to reach the desired speed after walking input begins. The character will accelerate linearly at the computed rate, impacted by friction, so a given time may be less accurate with lower friction. ### Property: GroundController.BalanceMaxTorque ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Balance", "capabilities": [ "Basic" ], "simulationAccess": true } ``` The maximum torque used to keep the [ControllerManager.RootPart](/docs/reference/engine/classes/ControllerManager.md) aligned upright. When misaligned, this amount of torque is applied to reach the [BalanceSpeed](/docs/reference/engine/classes/GroundController.md) and realign the root part. A higher torque means more force is required to cause the root part to tilt. A lower torque means it's easer for the root part to get knocked over when running into things. This property is hidden and has no effect when [ControllerBase.BalanceRigidityEnabled](/docs/reference/engine/classes/ControllerBase.md) is true. ### Property: GroundController.BalanceSpeed ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Balance", "capabilities": [ "Basic" ], "simulationAccess": true } ``` The maximum angular speed used to align the [ControllerManager.RootPart](/docs/reference/engine/classes/ControllerManager.md) upright. A lower value means it takes longer for the root part to recover to the upright position when misaligned. A higher value results in a quicker recovery. ### Property: GroundController.DecelerationTime ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Movement", "capabilities": [ "Basic" ], "simulationAccess": true } ``` Estimated time (in seconds) taken to reach a complete stop from full speed after walking input ends. The character will decelerate linearly at the computed rate, impacted by friction, so a given time may be less accurate with lower friction. ### Property: GroundController.Friction ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Movement", "capabilities": [ "Basic" ], "simulationAccess": true } ``` The coefficient of friction for the character at the point between it and the ground. Determines how much force is available for locomotion or to keep the character stationary on slopes. ### Property: GroundController.FrictionWeight ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Movement", "capabilities": [ "Basic" ], "simulationAccess": true } ``` Amount the character's friction is weighed against the ground friction, equal in behavior to the [FrictionWeight](/docs/reference/engine/datatypes/PhysicalProperties.md) data type of [BasePart.CustomPhysicalProperties](/docs/reference/engine/classes/BasePart.md). Higher values mean more of a part's [Friction](/docs/reference/engine/datatypes/PhysicalProperties.md) will be used. ### Property: GroundController.GroundOffset ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Movement", "capabilities": [ "Basic" ], "simulationAccess": true } ``` The target distance above the [ControllerManager.GroundSensor.HitPosition](/docs/reference/engine/classes/ControllerManager.md) to keep the [ControllerManager.RootPart](/docs/reference/engine/classes/ControllerManager.md) at. If the root part is below this offset from the ground, a force is applied to raise up to this height. If it's above, nothing happens. ### Property: GroundController.TurnSpeedFactor ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Movement", "capabilities": [ "Basic" ], "simulationAccess": true } ``` The value multiplied by the [ControllerManager.BaseTurnSpeed](/docs/reference/engine/classes/ControllerManager.md) to determine the final target angular velocity while this controller is active. The angular velocity is applied when turning towards the [ControllerManager.FacingDirection.](/docs/reference/engine/classes/ControllerManager.md) ## Inherited Members ### From [ControllerBase](/docs/reference/engine/classes/ControllerBase.md) - **Property `Active`** (`boolean`): - **Property `BalanceRigidityEnabled`** (`boolean`): - **Property `MoveSpeedFactor`** (`float`): The value multiplied by the ControllerManager.BaseMoveSpeed. ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: GroupService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated summary: "GroupService is a service that allows developers to fetch information about a Roblox group from within a game." --- # Class: GroupService > GroupService is a service that allows developers to fetch information about a > Roblox group from within a game. ## Description `GroupService` is a service that allows developers to fetch information about a Roblox group from within a game. Basic information on the group, including its name, description, owner, roles and emblem can be fetched using [GroupService:GetGroupInfoAsync()](/docs/reference/engine/classes/GroupService.md). Lists of a group's allies and enemies can be fetched using [GroupService:GetAlliesAsync()](/docs/reference/engine/classes/GroupService.md) and [GroupService:GetEnemiesAsync()](/docs/reference/engine/classes/GroupService.md). `GroupService` can also be used to fetch a list of groups a player is a member of, using [GroupService:GetGroupsAsync()](/docs/reference/engine/classes/GroupService.md). If you wish to verify if a player is in a group, use the [Player:IsInGroupAsync()](/docs/reference/engine/classes/Player.md) method rather than [GroupService:GetGroupsAsync()](/docs/reference/engine/classes/GroupService.md). The service has a number of useful applications, such as detecting if a player is an ally or enemy upon joining the game, or prompting a player to join a group using the [GroupService:PromptJoinAsync()](/docs/reference/engine/classes/GroupService.md) method. ## Code Samples **Group Ally/Enemy Checker** This code sample demonstrates how `GroupService` and [Player:IsInGroupAsync()](/docs/reference/engine/classes/Player.md) can be used to determine whether a player is a member of a group, or any of its allies or enemies. Note as [GroupService:GetAlliesAsync()](/docs/reference/engine/classes/GroupService.md) and [GroupService:GetEnemiesAsync()](/docs/reference/engine/classes/GroupService.md) use `StandardPages` objects a utility function is used to convert them to allies. ```lua local GroupService = game:GetService("GroupService") local Players = game:GetService("Players") -- Define group ID here local GROUP_ID = 271454 -- Utility function for dealing with pages local function pagesToArray(pages) local array = {} while true do for _, v in ipairs(pages:GetCurrentPage()) do table.insert(array, v) end if pages.IsFinished then break end pages:AdvanceToNextPageAsync() end return array end -- Get lists of allies and enemies local alliesPages = GroupService:GetAlliesAsync(GROUP_ID) local enemiesPages = GroupService:GetEnemiesAsync(GROUP_ID) -- Convert to array local allies = pagesToArray(alliesPages) local enemies = pagesToArray(enemiesPages) local function playerAdded(player) -- Check to see if the player is in the group if player:IsInGroupAsync(GROUP_ID) then print(player.Name .. " is a member!") else local isAlly, isEnemy = false, false -- Check to see if the player is in any ally groups for _, groupInfo in ipairs(allies) do local groupId = groupInfo.Id if player:IsInGroupAsync(groupId) then isAlly = true break end end -- Check to see if the player is in any enemy groups for _, groupInfo in ipairs(enemies) do local groupId = groupInfo.Id if player:IsInGroupAsync(groupId) then isEnemy = true break end end if isAlly and not isEnemy then print(player.Name .. " is an ally!") elseif isEnemy and not isAlly then print(player.Name .. " is an enemy!") elseif isEnemy and isAlly then print(player.Name .. " is both an ally and an enemy!") else print(player.Name .. " is neither an ally or an enemy!") end end end -- Listen for new players being added Players.PlayerAdded:Connect(playerAdded) -- Handle players already in game for _, player in ipairs(Players:GetPlayers()) do playerAdded(player) end ``` ## Methods ### Method: GroupService:GetAlliesAsync **Signature:** `GroupService:GetAlliesAsync(groupId: int64): StandardPages` Returns a [StandardPages](/docs/reference/engine/classes/StandardPages.md) object including information on all of the specified group's allies. This pages does not include a list of group IDs but instead a list of group information tables, mirroring the format of those returned by [GroupService:GetGroupInfoAsync()](/docs/reference/engine/classes/GroupService.md). See below for the structure of these tables. ```lua group = { Name = "Knights of the Seventh Sanctum", Id = 377251, Owner = { Name = "Vilicus", Id = 23415609 }, EmblemUrl = "http://www.roblox.com/asset/?id=60428602", Description = "We fight alongside the balance to make sure no one becomes to powerful", Roles = { [1] = { Name = "Apprentice", Rank = 1 }, [2] = { Name = "Warrior", Rank = 2 }, [3] = { Name = "Earth Walker", Rank = 255 } } } ``` Note, as this function returns a [StandardPages](/docs/reference/engine/classes/StandardPages.md) object rather than an array, developers may wish to convert it to an array for ease of use (see examples). This function has a number of useful applications, including detecting if a player is a member of an allied group. For enemies, use [GroupService:GetEnemiesAsync()](/docs/reference/engine/classes/GroupService.md). *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Groups* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `groupId` | `int64` | | The group's ID. | **Returns:** `StandardPages` **GroupService:GetAlliesAsync** ```lua local Players = game:GetService("Players") local GroupService = game:GetService("GroupService") local GROUP_ID = 57 -- creates a table of all of the allies of a given group local allies = {} local pages = GroupService:GetAlliesAsync(GROUP_ID) while true do for _, group in pairs(pages:GetCurrentPage()) do table.insert(allies, group) end if pages.IsFinished then break end pages:AdvanceToNextPageAsync() end function onPlayerAdded(player) for _, group in pairs(allies) do if player:IsInGroupAsync(group.Id) then print("Player is an ally!") break end end end Players.PlayerAdded:Connect(onPlayerAdded) -- handle players who joined while the allies list was still loading for _, player in pairs(Players:GetPlayers()) do onPlayerAdded(player) end ``` **Group Ally/Enemy Checker** This code sample demonstrates how `GroupService` and [Player:IsInGroupAsync()](/docs/reference/engine/classes/Player.md) can be used to determine whether a player is a member of a group, or any of its allies or enemies. Note as [GroupService:GetAlliesAsync()](/docs/reference/engine/classes/GroupService.md) and [GroupService:GetEnemiesAsync()](/docs/reference/engine/classes/GroupService.md) use `StandardPages` objects a utility function is used to convert them to allies. ```lua local GroupService = game:GetService("GroupService") local Players = game:GetService("Players") -- Define group ID here local GROUP_ID = 271454 -- Utility function for dealing with pages local function pagesToArray(pages) local array = {} while true do for _, v in ipairs(pages:GetCurrentPage()) do table.insert(array, v) end if pages.IsFinished then break end pages:AdvanceToNextPageAsync() end return array end -- Get lists of allies and enemies local alliesPages = GroupService:GetAlliesAsync(GROUP_ID) local enemiesPages = GroupService:GetEnemiesAsync(GROUP_ID) -- Convert to array local allies = pagesToArray(alliesPages) local enemies = pagesToArray(enemiesPages) local function playerAdded(player) -- Check to see if the player is in the group if player:IsInGroupAsync(GROUP_ID) then print(player.Name .. " is a member!") else local isAlly, isEnemy = false, false -- Check to see if the player is in any ally groups for _, groupInfo in ipairs(allies) do local groupId = groupInfo.Id if player:IsInGroupAsync(groupId) then isAlly = true break end end -- Check to see if the player is in any enemy groups for _, groupInfo in ipairs(enemies) do local groupId = groupInfo.Id if player:IsInGroupAsync(groupId) then isEnemy = true break end end if isAlly and not isEnemy then print(player.Name .. " is an ally!") elseif isEnemy and not isAlly then print(player.Name .. " is an enemy!") elseif isEnemy and isAlly then print(player.Name .. " is both an ally and an enemy!") else print(player.Name .. " is neither an ally or an enemy!") end end end -- Listen for new players being added Players.PlayerAdded:Connect(playerAdded) -- Handle players already in game for _, player in ipairs(Players:GetPlayers()) do playerAdded(player) end ``` ### Method: GroupService:GetEnemiesAsync **Signature:** `GroupService:GetEnemiesAsync(groupId: int64): StandardPages` Returns a [StandardPages](/docs/reference/engine/classes/StandardPages.md) object including information on all of the specified group's enemies. This pages does not include a list of group IDs but instead a list of group information tables, mirroring the format of those returned by [GroupService:GetGroupInfoAsync()](/docs/reference/engine/classes/GroupService.md). See below for the structure of these tables. ```lua group = { Name = "Knights of the Seventh Sanctum", Id = 377251, Owner = { Name = "Vilicus", Id = 23415609 }, EmblemUrl = "http://www.roblox.com/asset/?id=60428602", Description = "We fight alongside the balance to make sure no one becomes to powerful", Roles = { [1] = { Name = "Apprentice", Rank = 1 }, [2] = { Name = "Warrior", Rank = 2 }, [3] = { Name = "Earth Walker", Rank = 255 } } } ``` Note, as this function returns a [StandardPages](/docs/reference/engine/classes/StandardPages.md) object rather than an array, developers may wish to convert it to an array for ease of use (see examples). This function has a number of useful applications, including detecting if a player is a member of an enemy group. For allies, use [GroupService:GetAlliesAsync()](/docs/reference/engine/classes/GroupService.md). *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Groups* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `groupId` | `int64` | | The group's ID. | **Returns:** `StandardPages` **GroupService:GetEnemiesAsync** ```lua local Players = game:GetService("Players") local GroupService = game:GetService("GroupService") local GROUP_ID = 57 -- creates a list of all of the enemies of a given group local enemies = {} local pages = GroupService:GetEnemiesAsync(GROUP_ID) while true do for _, group in pairs(pages:GetCurrentPage()) do table.insert(enemies, group) end if pages.IsFinished then break end pages:AdvanceToNextPageAsync() end function onPlayerAdded(player) for _, enemyGroup in pairs(enemies) do if player:IsInGroupAsync(enemyGroup.Id) then print("Player is an enemy!") break end end end Players.PlayerAdded:Connect(onPlayerAdded) -- handle players who joined while the enemies list was still loading for _, player in pairs(Players:GetPlayers()) do onPlayerAdded(player) end ``` **Group Ally/Enemy Checker** This code sample demonstrates how `GroupService` and [Player:IsInGroupAsync()](/docs/reference/engine/classes/Player.md) can be used to determine whether a player is a member of a group, or any of its allies or enemies. Note as [GroupService:GetAlliesAsync()](/docs/reference/engine/classes/GroupService.md) and [GroupService:GetEnemiesAsync()](/docs/reference/engine/classes/GroupService.md) use `StandardPages` objects a utility function is used to convert them to allies. ```lua local GroupService = game:GetService("GroupService") local Players = game:GetService("Players") -- Define group ID here local GROUP_ID = 271454 -- Utility function for dealing with pages local function pagesToArray(pages) local array = {} while true do for _, v in ipairs(pages:GetCurrentPage()) do table.insert(array, v) end if pages.IsFinished then break end pages:AdvanceToNextPageAsync() end return array end -- Get lists of allies and enemies local alliesPages = GroupService:GetAlliesAsync(GROUP_ID) local enemiesPages = GroupService:GetEnemiesAsync(GROUP_ID) -- Convert to array local allies = pagesToArray(alliesPages) local enemies = pagesToArray(enemiesPages) local function playerAdded(player) -- Check to see if the player is in the group if player:IsInGroupAsync(GROUP_ID) then print(player.Name .. " is a member!") else local isAlly, isEnemy = false, false -- Check to see if the player is in any ally groups for _, groupInfo in ipairs(allies) do local groupId = groupInfo.Id if player:IsInGroupAsync(groupId) then isAlly = true break end end -- Check to see if the player is in any enemy groups for _, groupInfo in ipairs(enemies) do local groupId = groupInfo.Id if player:IsInGroupAsync(groupId) then isEnemy = true break end end if isAlly and not isEnemy then print(player.Name .. " is an ally!") elseif isEnemy and not isAlly then print(player.Name .. " is an enemy!") elseif isEnemy and isAlly then print(player.Name .. " is both an ally and an enemy!") else print(player.Name .. " is neither an ally or an enemy!") end end end -- Listen for new players being added Players.PlayerAdded:Connect(playerAdded) -- Handle players already in game for _, player in ipairs(Players:GetPlayers()) do playerAdded(player) end ``` ### Method: GroupService:GetGroupInfoAsync **Signature:** `GroupService:GetGroupInfoAsync(groupId: int64): Variant` Returns a table containing information about the given group. The table returned is the same format as that returned in [GroupService:GetAlliesAsync()](/docs/reference/engine/classes/GroupService.md) and [GroupService:GetEnemiesAsync()](/docs/reference/engine/classes/GroupService.md). This format can be seen below. ```lua group = { Name = "Knights of the Seventh Sanctum", Id = 377251, Owner = { Name = "Vilicus", Id = 23415609 }, EmblemUrl = "http://www.roblox.com/asset/?id=60428602", Description = "We fight alongside the balance to make sure no one becomes to powerful", Roles = { [1] = { Name = "Apprentice", Rank = 1 }, [2] = { Name = "Warrior", Rank = 2 }, [3] = { Name = "Earth Walker", Rank = 255 } } } ``` Note, if a group has no owner the Owner field will be set to `nil`. This function has a number of useful applications, including loading the latest description and logo of a group for display in a group base. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Groups* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `groupId` | `int64` | | The group ID of the group. | **Returns:** `Variant` — A dictionary of information about the group. **GroupService:GetGroupInfoAsync** ```lua local GroupService = game:GetService("GroupService") local GROUP_ID = 377251 local group = GroupService:GetGroupInfoAsync(GROUP_ID) print(group.Name .. " has the following roles:") for _, role in ipairs(group.Roles) do print("Rank " .. role.Rank .. ": " .. role.Name) end ``` **Load Group Emblem** The code in this sample spawns a `Part` in the Workspace that includes a texture of the given group's emblem. ```lua local GroupService = game:GetService("GroupService") local function getEmblemAsync(groupId) local groupInfo = GroupService:GetGroupInfoAsync(groupId) return groupInfo.EmblemUrl end local part = Instance.new("Part") part.Anchored = true part.CanCollide = false part.Size = Vector3.new(5, 5, 1) part.Position = Vector3.new(0, 5, 0) local decal = Instance.new("Decal") decal.Parent = part part.Parent = workspace decal.ColorMapContent = Content.fromUri(getEmblemAsync(377251)) ``` ### Method: GroupService:GetGroupsAsync **Signature:** `GroupService:GetGroupsAsync(userId: User): Array` This function returns a list of tables containing information on all of the groups a given [Player](/docs/reference/engine/classes/Player.md) is a member of. The list returned will include an entry for every group the player is a member of. These entries are tables with the following fields. | Name | Description | | --- | --- | | **Name** | The group's name | | **Id** | The group ID | | **EmblemUrl** | An asset url linking to the group's thumbnail (for example: http://www.roblox.com/asset/?id=276165514) | | **EmblemId** | The assetId of the emblem, the same which is used in the EmblemUrl | | **Rank** _(deprecated)_ | The rankId the player has. Deprecated: players may now hold more than one role in a group. Use [GroupService:GetRolesInGroupAsync()](/docs/reference/engine/classes/GroupService.md) instead. | | **Role** _(deprecated)_ | The name of the player's group rank. Deprecated: players may now hold more than one role in a group. Use [GroupService:GetRolesInGroupAsync()](/docs/reference/engine/classes/GroupService.md) instead. | | **IsPrimary** | A boolean indicating if this is the player's primary group | | **IsInClan** _(deprecated)_ | Always `false`. Deprecated: the Clans feature has been sunset. | Note unlike [GroupService:GetAlliesAsync()](/docs/reference/engine/classes/GroupService.md) and [GroupService:GetEnemiesAsync()](/docs/reference/engine/classes/GroupService.md), GetGroupsAsync returns a table rather than a [StandardPages](/docs/reference/engine/classes/StandardPages.md) object. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Groups* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `userId` | `User` | | The [Player.UserId](/docs/reference/engine/classes/Player.md) of the user. | **Returns:** `Array` — An array of dictionaries containing information on the group's the [Player](/docs/reference/engine/classes/Player.md) is a member of. **Getting the Groups that a User is A Member Of** This code sample will print information on all of the groups a player is a member of when they join the game, using [GroupService:GetGroupsAsync()](/docs/reference/engine/classes/GroupService.md). ```lua local GroupService = game:GetService("GroupService") local Players = game:GetService("Players") local function playerAdded(player) -- load a list of info on all groups the player is a member of local groups = GroupService:GetGroupsAsync(player.UserId) for _, groupInfo in pairs(groups) do for key, value in pairs(groupInfo) do print(key .. ": " .. tostring(value)) end print("--") end end Players.PlayerAdded:Connect(playerAdded) -- go through existing players for _, player in pairs(Players:GetPlayers()) do playerAdded(player) end ``` ### Method: GroupService:GetRolesInGroupAsync **Signature:** `GroupService:GetRolesInGroupAsync(userId: User, groupId: int64): Variant` Returns a table describing all roles the specified user holds in the specified group. In a multi-role world, a user may belong to more than one role simultaneously. The returned table has the following structure: | Key | Type | Description | | -------- | ------- | ----------------------------------------------------------------- | | IsMember | boolean | `true` if the user is a member of the group | | Roles | array | Array of role tables (empty if not a member or no non-base roles) | Each entry in the `Roles` array has the following structure: | Key | Type | Description | | ---- | ------ | ------------------------ | | Id | int64 | Unique role ID | | Name | string | Display name of the role | | Rank | int | Rank value (0–255) | The `Roles` array is ordered as returned by the backend. Only public roles are included. This method supersedes [Player:GetRankInGroupAsync()](/docs/reference/engine/classes/Player.md) and [Player:GetRoleInGroupAsync()](/docs/reference/engine/classes/Player.md), which only return a single role and may produce arbitrary results when a user holds multiple roles. This call may not yield the most up-to-date information. Results are cached per user and group, so multiple calls with the same `userId` and `groupId` may yield the same result until the cache expires. The caching behavior is on a per-peer basis: a server does not share the same cache as a client. When a player joins a group in-experience due to a call to [GroupService:PromptJoinAsync()](/docs/reference/engine/classes/GroupService.md), any cached result for that player and group will be cleared on the client where the prompt was shown. ```lua local GroupService = game:GetService("GroupService") local Players = game:GetService("Players") local GROUP_ID = 377251 local localPlayer = Players.LocalPlayer local result = GroupService:GetRolesInGroupAsync(localPlayer.UserId, GROUP_ID) if result.IsMember then print("User is a member with", #result.Roles, "role(s)") for _, role in ipairs(result.Roles) do print(role.Name, "rank", role.Rank) end else print("User is not a member") end ``` *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Groups* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `userId` | `User` | | The user's ID. | | `groupId` | `int64` | | The group's ID. | **Returns:** `Variant` — A table with two fields: `IsMember` (boolean) and `Roles` (array of public role tables). Each role table contains `Id` (int64), `Name` (string), and `Rank` (int). The array is ordered as returned by the backend. ### Method: GroupService:PromptJoinAsync **Signature:** `GroupService:PromptJoinAsync(groupId: int64): GroupMembershipStatus` `PromptJoinAsync()` displays a prompt to the local player through which they may join the specified Roblox group. The group must exist and the player must meet the eligibility criteria to join. If the player is ineligible, this method will return [GroupMembershipStatus.None](/docs/reference/engine/enums/GroupMembershipStatus.md). Note that you can use [Player:IsInGroupAsync()](/docs/reference/engine/classes/Player.md) to check the player's current membership status before calling this method. If the player successfully joins, any cached results from [GroupService:GetRolesInGroupAsync()](/docs/reference/engine/classes/GroupService.md), [Player:GetRankInGroupAsync()](/docs/reference/engine/classes/Player.md), and [Player:GetRoleInGroupAsync()](/docs/reference/engine/classes/Player.md) for that player and group will be cleared on the client where the prompt was shown. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Groups* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `groupId` | `int64` | | ID of the group to prompt the player to join. This must be a valid group ID. | **Returns:** `GroupMembershipStatus` — [GroupMembershipStatus](/docs/reference/engine/enums/GroupMembershipStatus.md) indicating the player's group membership status after the prompt is closed. If the player closes the prompt without joining, this will return [GroupMembershipStatus.None](/docs/reference/engine/enums/GroupMembershipStatus.md) or their previous status if they were already a member. **Prompting a player to join a group** This code sample demonstrates how to prompt a player to join a group, using [GroupService:PromptJoinAsync()](/docs/reference/engine/classes/GroupService.md). ```lua local GroupService = game:GetService("GroupService") local GROUP_ID = 377251 -- This should be done in a script running on the client local success, result = pcall(function() return GroupService:PromptJoinAsync(GROUP_ID) end) if success then if result == Enum.GroupMembershipStatus.Joined then print("Player joined the group!") elseif result == Enum.GroupMembershipStatus.JoinRequestPending then print("Player has an outstanding join request") elseif result == Enum.GroupMembershipStatus.AlreadyMember then print("Already a member") else print("Did not join or not eligible") end else warn("Prompt failed:", result) end ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: GuiBase last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable summary: "GuiBase is an abstract class which most graphical user interface objects inherit from." --- # Class: GuiBase > GuiBase is an abstract class which most graphical user interface objects > inherit from. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: GuiBase2d last_updated: 2026-06-29T19:34:07Z inherits: - GuiBase - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotBrowsable summary: "An abstract class inherited by 2D GuiObjects." --- # Class: GuiBase2d > An abstract class inherited by 2D [GuiObjects](/docs/reference/engine/classes/GuiObject.md). ## Description [GuiBase2d](/docs/reference/engine/classes/GuiBase2d.md) is an abstract class inherited by 2D [GuiObjects](/docs/reference/engine/classes/GuiObject.md). ## Properties ### Property: GuiBase2d.AbsolutePosition ```json { "type": "Vector2", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "Unsafe", "category": "Data", "capabilities": [ "UI" ] } ``` [AbsolutePosition](/docs/reference/engine/classes/GuiBase2d.md) is a read-only property that provides the screen position of a [GuiBase2d](/docs/reference/engine/classes/GuiBase2d.md) element in pixels. This represents the actual pixel position at which an element renders as a result of its ancestors' sizes and positions. Note that [AbsolutePosition](/docs/reference/engine/classes/GuiBase2d.md) always represents the top-left corner of the [GuiBase2d](/docs/reference/engine/classes/GuiBase2d.md) element. If the [GuiBase2d](/docs/reference/engine/classes/GuiBase2d.md) is in a [ScreenGui](/docs/reference/engine/classes/ScreenGui.md), the [AbsolutePosition](/docs/reference/engine/classes/GuiBase2d.md) property uses the [CoreUISafeInsets](/docs/reference/engine/enums/ScreenInsets.md) viewport coordinate system. The origin of this coordinate system is located at the bottom-left corner of the Roblox top bar. Note that this is the same coordinate system used by the [InputObject.Position](/docs/reference/engine/classes/InputObject.md) property. ![Diagram showing the origin of the AbsolutePosition coordinate system.](/assets/engine-api/classes/GuiBase2d/AbsolutePositionCoordinateSystem.png) See also [AbsoluteRotation](/docs/reference/engine/classes/GuiBase2d.md) and [AbsoluteSize](/docs/reference/engine/classes/GuiBase2d.md). ### Property: GuiBase2d.AbsoluteRotation ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "Unsafe", "category": "Data", "capabilities": [ "UI" ] } ``` [AbsoluteRotation](/docs/reference/engine/classes/GuiBase2d.md) is a read-only property that describes the actual screen rotation of a [GuiBase2d](/docs/reference/engine/classes/GuiBase2d.md) element, in degrees. It does **not** perform bounds checking, so its value may not be in the range `0` to `360`. See also [AbsolutePosition](/docs/reference/engine/classes/GuiBase2d.md) and [AbsoluteSize](/docs/reference/engine/classes/GuiBase2d.md). ### Property: GuiBase2d.AbsoluteSize ```json { "type": "Vector2", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "Unsafe", "category": "Data", "capabilities": [ "UI" ] } ``` [AbsoluteSize](/docs/reference/engine/classes/GuiBase2d.md) is a read-only property that describes the actual screen size of a [GuiBase2d](/docs/reference/engine/classes/GuiBase2d.md) element, in pixels. See also [AbsolutePosition](/docs/reference/engine/classes/GuiBase2d.md) and [AbsoluteRotation](/docs/reference/engine/classes/GuiBase2d.md). ### Property: GuiBase2d.AutoLocalize ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Localization", "capabilities": [ "UI" ] } ``` When set to `true`, localization will be applied to this [GuiBase2d](/docs/reference/engine/classes/GuiBase2d.md) and its descendants. The entries used for localization are the same set of entries returned by [LocalizationService:GetTableEntries()](/docs/reference/engine/classes/LocalizationService.md). Entries with [AutoLocalize](/docs/reference/engine/classes/GuiBase2d.md) enabled are automatically re-translated after the cloud table loads if necessary. See also [RootLocalizationTable](/docs/reference/engine/classes/GuiBase2d.md). ### Property: GuiBase2d.RootLocalizationTable ```json { "type": "LocalizationTable", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Localization", "capabilities": [ "UI" ] } ``` A reference to a [LocalizationTable](/docs/reference/engine/classes/LocalizationTable.md) to be used to apply automated localization to this [GuiBase2d](/docs/reference/engine/classes/GuiBase2d.md) and its descendants. [AutoLocalize](/docs/reference/engine/classes/GuiBase2d.md) must be set to `true` on the [GuiBase2d](/docs/reference/engine/classes/GuiBase2d.md) and its ancestors for automated localization to be applied. You can set this to reference a [LocalizationTable](/docs/reference/engine/classes/LocalizationTable.md) anywhere in the [DataModel](/docs/reference/engine/classes/DataModel.md). The [GuiBase2d](/docs/reference/engine/classes/GuiBase2d.md) object and all of its children will try to use that specific [LocalizationTable](/docs/reference/engine/classes/LocalizationTable.md) and its ancestors for automatic text replacement before using the tables under [LocalizationService](/docs/reference/engine/classes/LocalizationService.md) in an undefined order and the cloud table. If there is no translation available in the referenced table, it will look for a translation in the parent of that table, if it is also a [LocalizationTable](/docs/reference/engine/classes/LocalizationTable.md), and so on. See also [LocalizationService:GetTableEntries()](/docs/reference/engine/classes/LocalizationService.md) which explains how the [RootLocalizationTable](/docs/reference/engine/classes/GuiBase2d.md) is used for automated localization. ### Property: GuiBase2d.SelectionBehaviorDown ```json { "type": "SelectionBehavior", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Selection", "capabilities": [ "UI" ] } ``` Customizes gamepad selection behavior in the down direction. ### Property: GuiBase2d.SelectionBehaviorLeft ```json { "type": "SelectionBehavior", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Selection", "capabilities": [ "UI" ] } ``` Customizes gamepad selection behavior in the left direction. ### Property: GuiBase2d.SelectionBehaviorRight ```json { "type": "SelectionBehavior", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Selection", "capabilities": [ "UI" ] } ``` Customizes gamepad selection behavior in the right direction. ### Property: GuiBase2d.SelectionBehaviorUp ```json { "type": "SelectionBehavior", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Selection", "capabilities": [ "UI" ] } ``` Customizes gamepad selection behavior in the up direction. ### Property: GuiBase2d.SelectionGroup ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Selection", "capabilities": [ "UI" ] } ``` Allows for customization of how gamepad selection can move between buttons, which are descendants of the selection group, leave the group, and select other buttons. Setting [SelectionGroup](/docs/reference/engine/classes/GuiBase2d.md) to `true` exposes the [SelectionBehaviorUp](/docs/reference/engine/classes/GuiBase2d.md), [SelectionBehaviorDown](/docs/reference/engine/classes/GuiBase2d.md), [SelectionBehaviorLeft](/docs/reference/engine/classes/GuiBase2d.md), and [SelectionBehaviorRight](/docs/reference/engine/classes/GuiBase2d.md) properties. For these selection behaviors, a setting of [SelectionBehavior.Escape](/docs/reference/engine/enums/SelectionBehavior.md) (default) means the gamepad selection tries to first find a selection within the selection group and only moves outside if it does not find a suitable button. Alternatively, a setting of [SelectionBehavior.Stop](/docs/reference/engine/enums/SelectionBehavior.md) means gamepad selection only looks within the selection group and does not move outside of the group from the selection behavior direction. ### Property: GuiBase2d.Localize *(hidden)* ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Localization", "capabilities": [ "UI" ] } ``` > **Deprecated:** This item is deprecated. Do not use it for new work. This property is automatically set to true when a localization table's [LocalizationTable.Root](/docs/reference/engine/classes/LocalizationTable.md) targets this object, or an ancestor of this object. [LocalizationTables](/docs/reference/engine/classes/LocalizationTable.md) with their [LocalizationTable.Root](/docs/reference/engine/classes/LocalizationTable.md) property pointed at an instance will localize all [TextLabel.TextButton](/docs/reference/engine/classes/TextLabel.md) that are descendants of the root instance. ## Events ### Event: GuiBase2d.SelectionChanged **Signature:** `GuiBase2d.SelectionChanged(amISelected: boolean, previousSelection: GuiObject, newSelection: GuiObject)` This event fires when the gamepad selection moves to, leaves, or changes within the connected [GuiBase2d](/docs/reference/engine/classes/GuiBase2d.md) or any descendant [GuiObjects](/docs/reference/engine/classes/GuiObject.md). When the selection highlight moves to a [GuiObject](/docs/reference/engine/classes/GuiObject.md), the event bubbles from that [GuiObject](/docs/reference/engine/classes/GuiObject.md) to all of its ancestors, informing them that the selection has changed/entered/exited to a [GuiObject](/docs/reference/engine/classes/GuiObject.md) in their descendant tree. *Security: None · Capabilities: UI* **Parameters:** | Name | Type | Description | |------|------|-------------| | `amISelected` | `boolean` | True if the new selection matches the attached GuiBase2d. | | `previousSelection` | `GuiObject` | | | `newSelection` | `GuiObject` | | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: GuiBase3d last_updated: 2026-06-29T19:34:07Z inherits: - GuiBase - Instance - Object type: class memory_category: Instances tags: - NotCreatable summary: "An abstract class for 3D GUI elements that are rendered in the world." --- # Class: GuiBase3d > An abstract class for 3D GUI elements that are rendered in the world. ## Properties ### Property: GuiBase3d.Color3 ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "UI" ] } ``` Sets the color of this [GuiBase3d](/docs/reference/engine/classes/GuiBase3d.md) object. ### Property: GuiBase3d.Transparency ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "UI" ] } ``` Sets the transparency of this [GuiBase3d](/docs/reference/engine/classes/GuiBase3d.md) object, where `1` is invisible and `0` is fully visible. ### Property: GuiBase3d.Visible ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` Determines whether this [GuiBase3d](/docs/reference/engine/classes/GuiBase3d.md) object and its descendants will be displayed. ### Property: GuiBase3d.Color *(hidden)* ```json { "type": "BrickColor", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "UI" ] } ``` > **Deprecated:** This property has been deprecated in favor of [GuiBase3d.Color3](/docs/reference/engine/classes/GuiBase3d.md), which allows for greater color customization and should be used in new work instead. Sets the color of a GUI object. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: GuiButton last_updated: 2026-06-29T19:34:07Z inherits: - GuiObject - GuiBase2d - GuiBase - Instance - Object type: class memory_category: Gui tags: - NotCreatable - NotBrowsable summary: "An abstract class for interactive 2D user interface elements." --- # Class: GuiButton > An abstract class for interactive 2D user interface elements. ## Description [GuiButton](/docs/reference/engine/classes/GuiButton.md) is an abstract class that inherits from [GuiObject](/docs/reference/engine/classes/GuiObject.md). It is the base class for the interactive, clickable [ImageButton](/docs/reference/engine/classes/ImageButton.md) and [TextButton](/docs/reference/engine/classes/TextButton.md) objects. This class also defines several properties for interactive behavior, namely [AutoButtonColor](/docs/reference/engine/classes/GuiButton.md) and [Modal](/docs/reference/engine/classes/GuiButton.md). The most important event of a [GuiButton](/docs/reference/engine/classes/GuiButton.md) is [Activated](/docs/reference/engine/classes/GuiButton.md), a **multi-platform event** that fires when the button is activated. When using a mouse, this means clicking the button and releasing with the cursor still over the UI object. For touch, the same applies but with a touch instead of button press. Finally, for gamepads, [Activated](/docs/reference/engine/classes/GuiButton.md) fires if a [GuiButton](/docs/reference/engine/classes/GuiButton.md) is selected when the **A** button is pressed and released. In short, this event is very useful for multi-platform user interface programming as it provides a nice general interface for a single user input. ## Properties ### Property: GuiButton.AutoButtonColor ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` This property determines whether the button automatically changes color when the user's [Mouse](/docs/reference/engine/classes/Mouse.md) hovers over or clicks on it. If `true`, the button will automatically change color when the mouse hovers over or clicks on it. If `false`, the button will not change. If you would like to customize how a button changes when the user's mouse hovers over or clicks on it, consider using an [ImageButton](/docs/reference/engine/classes/ImageButton.md) and changing the element's [HoverImage](/docs/reference/engine/classes/ImageButton.md) and [PressedImage](/docs/reference/engine/classes/ImageButton.md). Please note that this property will not have an effect on an [ImageButton](/docs/reference/engine/classes/ImageButton.md) if its [Image](/docs/reference/engine/classes/ImageButton.md) property is set to an image. Additionally, this property will not affect an [ImageButton](/docs/reference/engine/classes/ImageButton.md) on mouse hover when its [HoverImage](/docs/reference/engine/classes/ImageButton.md) is not `nil`, nor on mouse click if its [PressedImage](/docs/reference/engine/classes/ImageButton.md) is not `nil`. ### Property: GuiButton.HoverHapticEffect ```json { "type": "HapticEffect", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Haptic", "capabilities": [ "UI" ] } ``` A [HapticEffect](/docs/reference/engine/classes/HapticEffect.md) instance that will play when the [GuiButton](/docs/reference/engine/classes/GuiButton.md) is being hovered. ### Property: GuiButton.Modal ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` If `true` while the GUI element is visible, the mouse will not be locked unless the right mouse button is down. ### Property: GuiButton.PressHapticEffect ```json { "type": "HapticEffect", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Haptic", "capabilities": [ "UI" ] } ``` A [HapticEffect](/docs/reference/engine/classes/HapticEffect.md) instance that will play when the [GuiButton](/docs/reference/engine/classes/GuiButton.md) is being pressed. ### Property: GuiButton.Selected ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Selection", "capabilities": [ "UI" ] } ``` A boolean property which indicates whether the object has been selected. ### Property: GuiButton.Style ```json { "type": "ButtonStyle", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` Sets the style of the [GuiButton](/docs/reference/engine/classes/GuiButton.md) based on a list of pre-determined styles. ## Events ### Event: GuiButton.Activated **Signature:** `GuiButton.Activated(inputObject: InputObject, clickCount: int)` Fires when a left click press-and-release is detected on desktop, touch release is detected on mobile, or **A**/cross is activated in UI navigation mode on console. As this event doesn't fire on the server, it should only be used in a [LocalScript](/docs/reference/engine/classes/LocalScript.md), or in a [Script](/docs/reference/engine/classes/Script.md) with [RunContext](/docs/reference/engine/classes/Script.md) of [RunContext.Client](/docs/reference/engine/enums/RunContext.md). *Security: None · Capabilities: UI* **Parameters:** | Name | Type | Description | |------|------|-------------| | `inputObject` | `InputObject` | | | `clickCount` | `int` | | ### Event: GuiButton.MouseButton1Click **Signature:** `GuiButton.MouseButton1Click()` This event fires when the user's mouse fully left clicks the [GuiButton](/docs/reference/engine/classes/GuiButton.md). In regards to clicking, the mouse must be in bounds of the [GuiButton](/docs/reference/engine/classes/GuiButton.md) and the mouse button must be pressed down and up again before this event fires. If the mouse leaves the bounds of the [GuiButton](/docs/reference/engine/classes/GuiButton.md) and is released, the event will not fire. If you would like to avoid this limitation, you can use [MouseButton1Down](/docs/reference/engine/classes/GuiButton.md) and [MouseButton1Up](/docs/reference/engine/classes/GuiButton.md); these events are similar but will fire whenever the user presses their left mouse button down or up, respectively. *Security: None · Capabilities: UI* ### Event: GuiButton.MouseButton1Down **Signature:** `GuiButton.MouseButton1Down(x: int, y: int)` This event fires when the user presses their left mouse button down on the [GuiButton](/docs/reference/engine/classes/GuiButton.md). For an event requiring the user to press **and** release their left mouse on a [GuiButton](/docs/reference/engine/classes/GuiButton.md) in order for the event to fire, consider using [MouseButton1Click](/docs/reference/engine/classes/GuiButton.md). *Security: None · Capabilities: UI* **Parameters:** | Name | Type | Description | |------|------|-------------| | `x` | `int` | The mouse's **X** screen coordinate in pixels. | | `y` | `int` | The mouse's **Y** screen coordinate in pixels. | ### Event: GuiButton.MouseButton1Up **Signature:** `GuiButton.MouseButton1Up(x: int, y: int)` This event fires when the user releases their left mouse button off of the [GuiButton](/docs/reference/engine/classes/GuiButton.md). For an event requiring the user to press **and** release their left mouse on a [GuiButton](/docs/reference/engine/classes/GuiButton.md) in order for the event to fire, consider using [MouseButton1Click](/docs/reference/engine/classes/GuiButton.md). *Security: None · Capabilities: UI* **Parameters:** | Name | Type | Description | |------|------|-------------| | `x` | `int` | The mouse's **X** screen coordinate in pixels. | | `y` | `int` | The mouse's **Y** screen coordinate in pixels. | ### Event: GuiButton.MouseButton2Click **Signature:** `GuiButton.MouseButton2Click()` This event fires when the user's mouse fully right clicks the [GuiButton](/docs/reference/engine/classes/GuiButton.md). In regards to clicking, the mouse must be in bounds of the [GuiButton](/docs/reference/engine/classes/GuiButton.md) and the mouse button must be pressed down and up again before this event fires. If the mouse leaves the bounds of the [GuiButton](/docs/reference/engine/classes/GuiButton.md) and is released, the event will not fire. If you would like to avoid this limitation, you can use [MouseButton2Down](/docs/reference/engine/classes/GuiButton.md) and [MouseButton2Up](/docs/reference/engine/classes/GuiButton.md); these events are similar but will fire whenever the user presses their right mouse button down or up, respectively. *Security: None · Capabilities: UI* ### Event: GuiButton.MouseButton2Down **Signature:** `GuiButton.MouseButton2Down(x: int, y: int)` This event fires when the user presses their right mouse button down on the [GuiButton](/docs/reference/engine/classes/GuiButton.md). For an event requiring the user to press **and** release their right mouse on a [GuiButton](/docs/reference/engine/classes/GuiButton.md) in order for the event to fire, consider using [MouseButton2Click](/docs/reference/engine/classes/GuiButton.md). *Security: None · Capabilities: UI* **Parameters:** | Name | Type | Description | |------|------|-------------| | `x` | `int` | The mouse's **X** screen coordinate in pixels. | | `y` | `int` | The mouse's **Y** screen coordinate in pixels. | ### Event: GuiButton.MouseButton2Up **Signature:** `GuiButton.MouseButton2Up(x: int, y: int)` This event fires when the user releases their right mouse button off of the [GuiButton](/docs/reference/engine/classes/GuiButton.md). For an event requiring the user to press **and** release their right mouse on a [GuiButton](/docs/reference/engine/classes/GuiButton.md) in order for the event to fire, consider using [MouseButton2Click](/docs/reference/engine/classes/GuiButton.md). *Security: None · Capabilities: UI* **Parameters:** | Name | Type | Description | |------|------|-------------| | `x` | `int` | The mouse's **X** screen coordinate in pixels. | | `y` | `int` | The mouse's **Y** screen coordinate in pixels. | ### Event: GuiButton.SecondaryActivated **Signature:** `GuiButton.SecondaryActivated(inputObject: InputObject)` Fires when a right-click press-and-release is detected from a mouse, long press is detected on a touch input, `R3` is detected from a gamepad, or AltEnter is detected in UI navigation mode using keyboard. *Security: None · Capabilities: UI* **Parameters:** | Name | Type | Description | |------|------|-------------| | `inputObject` | `InputObject` | The input object representing the alternate activation action. | ## Inherited Members ### From [GuiObject](/docs/reference/engine/classes/GuiObject.md) - **Property `Active`** (`boolean`): Determines whether this UI element sinks input. - **Property `AnchorPoint`** (`Vector2`): Determines the origin point of a GuiObject, relative to its - **Property `AutomaticSize`** (`AutomaticSize`): Determines whether resizing occurs based on child content. - **Property `BackgroundColor`** (`BrickColor`): Determines the color of the GuiObject background. *(deprecated, hidden)* - **Property `BackgroundColor3`** (`Color3`): Determines the GuiObject background color. - **Property `BackgroundTransparency`** (`float`): Determines the transparency of the GuiObject background and - **Property `BorderColor`** (`BrickColor`): Determines the color of the GuiObject border. *(deprecated, hidden)* - **Property `BorderColor3`** (`Color3`): Determines the color of the GuiObject border. - **Property `BorderMode`** (`BorderMode`): Determines in what manner the GuiObject border is laid out - **Property `BorderSizePixel`** (`int`): Determines the pixel width of the GuiObject border. - **Property `ClipsDescendants`** (`boolean`): Determines if descendant GuiObjects outside of the - **Property `Draggable`** (`boolean`): Determines whether a GuiObject (and its descendants) can be *(deprecated)* - **Property `GuiState`** (`GuiState`): Determines whether the player's mouse is being actively pressed on the - **Property `InputSink`** (`InputSink`): - **Property `Interactable`** (`boolean`): Determines whether the GuiButton can be interacted with or not, or - **Property `LayoutOrder`** (`int`): Controls the sort order of the GuiObject when used with a - **Property `NextSelectionDown`** (`GuiObject`): Sets the GuiObject which will be selected when the gamepad - **Property `NextSelectionLeft`** (`GuiObject`): Sets the GuiObject which will be selected when the gamepad - **Property `NextSelectionRight`** (`GuiObject`): Sets the GuiObject which will be selected when the gamepad - **Property `NextSelectionUp`** (`GuiObject`): Sets the GuiObject which will be selected when the gamepad - **Property `Position`** (`UDim2`): Determines the pixel and scalar position of the GuiObject. - **Property `Rotation`** (`float`): Determines the number of degrees by which the GuiObject is - **Property `Selectable`** (`boolean`): Determine whether the GuiObject can be selected by a gamepad. - **Property `SelectionImageObject`** (`GuiObject`): Overrides the default selection adornment used for gamepads. - **Property `SelectionOrder`** (`int`): The order of GuiObjects selected by the gamepad UI - **Property `Size`** (`UDim2`): Determines the pixel and scalar size of the GuiObject. - **Property `SizeConstraint`** (`SizeConstraint`): Sets the Size axes that the GuiObject will - **Property `Transparency`** (`float`): A mixed property of *(hidden)* - **Property `Visible`** (`boolean`): Determines whether the GuiObject and its descendants will be - **Property `ZIndex`** (`int`): Determines the order in which a GuiObject renders relative to - **Method `TweenPosition(endPosition: UDim2, easingDirection?: EasingDirection, easingStyle?: EasingStyle, time?: float, override?: boolean, callback?: Function): boolean`**: Smoothly moves a GUI to a new UDim2. - **Method `TweenSize(endSize: UDim2, easingDirection?: EasingDirection, easingStyle?: EasingStyle, time?: float, override?: boolean, callback?: Function): boolean`**: Smoothly resizes a GuiObject to a new UDim2. - **Method `TweenSizeAndPosition(endSize: UDim2, endPosition: UDim2, easingDirection?: EasingDirection, easingStyle?: EasingStyle, time?: float, override?: boolean, callback?: Function): boolean`**: Smoothly moves a GUI to a new size and position. - **Event `DragBegin`**: Fired when a player begins dragging the object. *(deprecated)* - **Event `DragStopped`**: Fired when a player stops dragging the object. *(deprecated)* - **Event `InputBegan`**: Fired when a user begins interacting via a Human-Computer Interface device - **Event `InputChanged`**: Fired when a user changes how they're interacting via a Human-Computer - **Event `InputEnded`**: Fired when a user stops interacting via a Human-Computer Interface device - **Event `MouseEnter`**: Fires when a user moves their mouse into a GUI element. - **Event `MouseLeave`**: Fires when a user moves their mouse out of a GUI element. - **Event `MouseMoved`**: Fires whenever a user moves their mouse while it is inside a GUI element. - **Event `MouseWheelBackward`**: Fires when a user scrolls their mouse wheel back when the mouse is over a - **Event `MouseWheelForward`**: Fires when a user scrolls their mouse wheel forward when the mouse is over - **Event `SelectionGained`**: Fired when the GuiObject is being focused on with the Gamepad selector. - **Event `SelectionLost`**: Fired when the Gamepad selector stops focusing on the GuiObject. - **Event `TouchLongPress`**: Fires when the player starts, continues and stops long-pressing the UI - **Event `TouchPan`**: Fires when the player moves their finger on the UI element. - **Event `TouchPinch`**: Fires when the player performs a pinch or pull gesture using two fingers - **Event `TouchRotate`**: Fires when the player performs a rotation gesture using two fingers on the - **Event `TouchSwipe`**: Fires when the player performs a swipe gesture on the UI element. - **Event `TouchTap`**: Fires when the player performs a tap gesture on the UI element. ### From [GuiBase2d](/docs/reference/engine/classes/GuiBase2d.md) - **Property `AbsolutePosition`** (`Vector2`): Describes the actual screen position of a GuiBase2d element, in - **Property `AbsoluteRotation`** (`float`): Describes the actual screen rotation of a GuiBase2d element, in - **Property `AbsoluteSize`** (`Vector2`): Describes the actual screen size of a GuiBase2d element, in - **Property `AutoLocalize`** (`boolean`): When set to `true`, localization will be applied to this GuiBase2d - **Property `Localize`** (`boolean`): Automatically set to true when a localization table's *(deprecated, hidden)* - **Property `RootLocalizationTable`** (`LocalizationTable`): A reference to a LocalizationTable to be used to apply automated - **Property `SelectionBehaviorDown`** (`SelectionBehavior`): Customizes gamepad selection behavior in the down direction. - **Property `SelectionBehaviorLeft`** (`SelectionBehavior`): Customizes gamepad selection behavior in the left direction. - **Property `SelectionBehaviorRight`** (`SelectionBehavior`): Customizes gamepad selection behavior in the right direction. - **Property `SelectionBehaviorUp`** (`SelectionBehavior`): Customizes gamepad selection behavior in the up direction. - **Property `SelectionGroup`** (`boolean`): Allows customization of gamepad selection movement. - **Event `SelectionChanged`**: Fires when the gamepad selection moves to, leaves, or changes within the ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: GuiLabel last_updated: 2026-06-29T19:34:07Z inherits: - GuiObject - GuiBase2d - GuiBase - Instance - Object type: class memory_category: Gui tags: - NotCreatable summary: "An abstract class for non-interactive 2D user interface elements." --- # Class: GuiLabel > An abstract class for non-interactive 2D user interface elements. ## Description GuiLabel is an abstract class that inherits from [GuiObject](/docs/reference/engine/classes/GuiObject.md). It is the base class for [ImageLabel](/docs/reference/engine/classes/ImageLabel.md) and [TextLabel](/docs/reference/engine/classes/TextLabel.md). Unlike [GuiButton](/docs/reference/engine/classes/GuiButton.md), objects of this type will not register click events, but instead serve as non-interactive labels. It does not implement any further properties, events or methods. ## Inherited Members ### From [GuiObject](/docs/reference/engine/classes/GuiObject.md) - **Property `Active`** (`boolean`): Determines whether this UI element sinks input. - **Property `AnchorPoint`** (`Vector2`): Determines the origin point of a GuiObject, relative to its - **Property `AutomaticSize`** (`AutomaticSize`): Determines whether resizing occurs based on child content. - **Property `BackgroundColor`** (`BrickColor`): Determines the color of the GuiObject background. *(deprecated, hidden)* - **Property `BackgroundColor3`** (`Color3`): Determines the GuiObject background color. - **Property `BackgroundTransparency`** (`float`): Determines the transparency of the GuiObject background and - **Property `BorderColor`** (`BrickColor`): Determines the color of the GuiObject border. *(deprecated, hidden)* - **Property `BorderColor3`** (`Color3`): Determines the color of the GuiObject border. - **Property `BorderMode`** (`BorderMode`): Determines in what manner the GuiObject border is laid out - **Property `BorderSizePixel`** (`int`): Determines the pixel width of the GuiObject border. - **Property `ClipsDescendants`** (`boolean`): Determines if descendant GuiObjects outside of the - **Property `Draggable`** (`boolean`): Determines whether a GuiObject (and its descendants) can be *(deprecated)* - **Property `GuiState`** (`GuiState`): Determines whether the player's mouse is being actively pressed on the - **Property `InputSink`** (`InputSink`): - **Property `Interactable`** (`boolean`): Determines whether the GuiButton can be interacted with or not, or - **Property `LayoutOrder`** (`int`): Controls the sort order of the GuiObject when used with a - **Property `NextSelectionDown`** (`GuiObject`): Sets the GuiObject which will be selected when the gamepad - **Property `NextSelectionLeft`** (`GuiObject`): Sets the GuiObject which will be selected when the gamepad - **Property `NextSelectionRight`** (`GuiObject`): Sets the GuiObject which will be selected when the gamepad - **Property `NextSelectionUp`** (`GuiObject`): Sets the GuiObject which will be selected when the gamepad - **Property `Position`** (`UDim2`): Determines the pixel and scalar position of the GuiObject. - **Property `Rotation`** (`float`): Determines the number of degrees by which the GuiObject is - **Property `Selectable`** (`boolean`): Determine whether the GuiObject can be selected by a gamepad. - **Property `SelectionImageObject`** (`GuiObject`): Overrides the default selection adornment used for gamepads. - **Property `SelectionOrder`** (`int`): The order of GuiObjects selected by the gamepad UI - **Property `Size`** (`UDim2`): Determines the pixel and scalar size of the GuiObject. - **Property `SizeConstraint`** (`SizeConstraint`): Sets the Size axes that the GuiObject will - **Property `Transparency`** (`float`): A mixed property of *(hidden)* - **Property `Visible`** (`boolean`): Determines whether the GuiObject and its descendants will be - **Property `ZIndex`** (`int`): Determines the order in which a GuiObject renders relative to - **Method `TweenPosition(endPosition: UDim2, easingDirection?: EasingDirection, easingStyle?: EasingStyle, time?: float, override?: boolean, callback?: Function): boolean`**: Smoothly moves a GUI to a new UDim2. - **Method `TweenSize(endSize: UDim2, easingDirection?: EasingDirection, easingStyle?: EasingStyle, time?: float, override?: boolean, callback?: Function): boolean`**: Smoothly resizes a GuiObject to a new UDim2. - **Method `TweenSizeAndPosition(endSize: UDim2, endPosition: UDim2, easingDirection?: EasingDirection, easingStyle?: EasingStyle, time?: float, override?: boolean, callback?: Function): boolean`**: Smoothly moves a GUI to a new size and position. - **Event `DragBegin`**: Fired when a player begins dragging the object. *(deprecated)* - **Event `DragStopped`**: Fired when a player stops dragging the object. *(deprecated)* - **Event `InputBegan`**: Fired when a user begins interacting via a Human-Computer Interface device - **Event `InputChanged`**: Fired when a user changes how they're interacting via a Human-Computer - **Event `InputEnded`**: Fired when a user stops interacting via a Human-Computer Interface device - **Event `MouseEnter`**: Fires when a user moves their mouse into a GUI element. - **Event `MouseLeave`**: Fires when a user moves their mouse out of a GUI element. - **Event `MouseMoved`**: Fires whenever a user moves their mouse while it is inside a GUI element. - **Event `MouseWheelBackward`**: Fires when a user scrolls their mouse wheel back when the mouse is over a - **Event `MouseWheelForward`**: Fires when a user scrolls their mouse wheel forward when the mouse is over - **Event `SelectionGained`**: Fired when the GuiObject is being focused on with the Gamepad selector. - **Event `SelectionLost`**: Fired when the Gamepad selector stops focusing on the GuiObject. - **Event `TouchLongPress`**: Fires when the player starts, continues and stops long-pressing the UI - **Event `TouchPan`**: Fires when the player moves their finger on the UI element. - **Event `TouchPinch`**: Fires when the player performs a pinch or pull gesture using two fingers - **Event `TouchRotate`**: Fires when the player performs a rotation gesture using two fingers on the - **Event `TouchSwipe`**: Fires when the player performs a swipe gesture on the UI element. - **Event `TouchTap`**: Fires when the player performs a tap gesture on the UI element. ### From [GuiBase2d](/docs/reference/engine/classes/GuiBase2d.md) - **Property `AbsolutePosition`** (`Vector2`): Describes the actual screen position of a GuiBase2d element, in - **Property `AbsoluteRotation`** (`float`): Describes the actual screen rotation of a GuiBase2d element, in - **Property `AbsoluteSize`** (`Vector2`): Describes the actual screen size of a GuiBase2d element, in - **Property `AutoLocalize`** (`boolean`): When set to `true`, localization will be applied to this GuiBase2d - **Property `Localize`** (`boolean`): Automatically set to true when a localization table's *(deprecated, hidden)* - **Property `RootLocalizationTable`** (`LocalizationTable`): A reference to a LocalizationTable to be used to apply automated - **Property `SelectionBehaviorDown`** (`SelectionBehavior`): Customizes gamepad selection behavior in the down direction. - **Property `SelectionBehaviorLeft`** (`SelectionBehavior`): Customizes gamepad selection behavior in the left direction. - **Property `SelectionBehaviorRight`** (`SelectionBehavior`): Customizes gamepad selection behavior in the right direction. - **Property `SelectionBehaviorUp`** (`SelectionBehavior`): Customizes gamepad selection behavior in the up direction. - **Property `SelectionGroup`** (`boolean`): Allows customization of gamepad selection movement. - **Event `SelectionChanged`**: Fires when the gamepad selection moves to, leaves, or changes within the ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: GuiMain last_updated: 2026-06-29T19:34:07Z inherits: - ScreenGui - LayerCollector - GuiBase2d - GuiBase - Instance - Object type: class memory_category: Instances tags: - Deprecated summary: "The original name of the ScreenGui." --- # Class: GuiMain > The original name of the [ScreenGui](/docs/reference/engine/classes/ScreenGui.md). ## Inherited Members ### From [ScreenGui](/docs/reference/engine/classes/ScreenGui.md) - **Property `ClipToDeviceSafeArea`** (`boolean`): Whether to clip the contents of this ScreenGui to the device's - **Property `DisplayOrder`** (`int`): Controls the Z-index order in which multiple ScreenGui containers - **Property `IgnoreGuiInset`** (`boolean`): Determines whether the ScreenGui overflows into the range of - **Property `SafeAreaCompatibility`** (`SafeAreaCompatibility`): Specifies whether automatic UI compatibility transformations are applied - **Property `ScreenInsets`** (`ScreenInsets`): Controls the safe area insets that are applied to the contents of the ### From [LayerCollector](/docs/reference/engine/classes/LayerCollector.md) - **Property `Enabled`** (`boolean`): Toggles the visibility of this LayerCollector. - **Property `ResetOnSpawn`** (`boolean`): Determines if the LayerCollector resets (deletes itself and - **Property `ZIndexBehavior`** (`ZIndexBehavior`): Controls how GuiObject.ZIndex behaves on all descendants of this - **Method `GetLayoutNodeTree(): Dictionary`**: *(deprecated)* ### From [GuiBase2d](/docs/reference/engine/classes/GuiBase2d.md) - **Property `AbsolutePosition`** (`Vector2`): Describes the actual screen position of a GuiBase2d element, in - **Property `AbsoluteRotation`** (`float`): Describes the actual screen rotation of a GuiBase2d element, in - **Property `AbsoluteSize`** (`Vector2`): Describes the actual screen size of a GuiBase2d element, in - **Property `AutoLocalize`** (`boolean`): When set to `true`, localization will be applied to this GuiBase2d - **Property `Localize`** (`boolean`): Automatically set to true when a localization table's *(deprecated, hidden)* - **Property `RootLocalizationTable`** (`LocalizationTable`): A reference to a LocalizationTable to be used to apply automated - **Property `SelectionBehaviorDown`** (`SelectionBehavior`): Customizes gamepad selection behavior in the down direction. - **Property `SelectionBehaviorLeft`** (`SelectionBehavior`): Customizes gamepad selection behavior in the left direction. - **Property `SelectionBehaviorRight`** (`SelectionBehavior`): Customizes gamepad selection behavior in the right direction. - **Property `SelectionBehaviorUp`** (`SelectionBehavior`): Customizes gamepad selection behavior in the up direction. - **Property `SelectionGroup`** (`boolean`): Allows customization of gamepad selection movement. - **Event `SelectionChanged`**: Fires when the gamepad selection moves to, leaves, or changes within the ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: GuiObject last_updated: 2026-06-29T19:34:07Z inherits: - GuiBase2d - GuiBase - Instance - Object type: class memory_category: Gui tags: - NotCreatable - NotBrowsable summary: "An abstract class for all 2D user interface objects." --- # Class: GuiObject > An abstract class for all 2D user interface objects. ## Description [GuiObject](/docs/reference/engine/classes/GuiObject.md) is an abstract class (much like [BasePart](/docs/reference/engine/classes/BasePart.md)) for a 2D user interface object. It defines all the properties relating to the display of a graphical user interface (GUI) object such as [Size](/docs/reference/engine/classes/GuiObject.md) and [Position](/docs/reference/engine/classes/GuiObject.md). It also has some useful read‑only properties like [AbsolutePosition](/docs/reference/engine/classes/GuiObject.md), [AbsoluteSize](/docs/reference/engine/classes/GuiObject.md), and [AbsoluteRotation](/docs/reference/engine/classes/GuiObject.md). To manipulate the layout of GUI objects in special ways, you can use a layout structure such as [list/flex](/docs/en-us/ui/list-flex-layouts.md) or [grid](/docs/en-us/ui/grid-table-layouts.md), and you can style them beyond their core properties through [appearance modifiers](/docs/en-us/ui/appearance-modifiers.md). Although it's possible to detect mouse button events on any GUI object using [InputBegan](/docs/reference/engine/classes/GuiObject.md) and [InputEnded](/docs/reference/engine/classes/GuiObject.md), only [ImageButton](/docs/reference/engine/classes/ImageButton.md) and [TextButton](/docs/reference/engine/classes/TextButton.md) have convenient dedicated events such as [Activated](/docs/reference/engine/classes/TextButton.md) to detect click/press. ## Properties ### Property: GuiObject.Active ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` This property determines whether the [GuiObject](/docs/reference/engine/classes/GuiObject.md) will sink input to 3D space, such as underlying models with a [ClickDetector](/docs/reference/engine/classes/ClickDetector.md) class like [DragDetector](/docs/reference/engine/classes/DragDetector.md). For [GuiButton](/docs/reference/engine/classes/GuiButton.md) objects ([ImageButton](/docs/reference/engine/classes/ImageButton.md) and [TextButton](/docs/reference/engine/classes/TextButton.md)), this property determines whether [Activated](/docs/reference/engine/classes/GuiButton.md) fires ([AutoButtonColor](/docs/reference/engine/classes/GuiButton.md) will still work for those as well). The events [InputBegan](/docs/reference/engine/classes/GuiObject.md), [InputChanged](/docs/reference/engine/classes/GuiObject.md), and [InputEnded](/docs/reference/engine/classes/GuiObject.md) work as normal no matter the value of this property. **TextButton Active Debounce** This code sample demonstrates the usage of the Active property as a debounce for the Activated event. ```lua -- Place this LocalScript within a TextButton (or ImageButton) local textButton = script.Parent textButton.Text = "Click me" textButton.Active = true local function onActivated() -- This acts like a debounce textButton.Active = false -- Count backwards from 5 for i = 5, 1, -1 do textButton.Text = "Time: " .. i task.wait(1) end textButton.Text = "Click me" textButton.Active = true end textButton.Activated:Connect(onActivated) ``` ### Property: GuiObject.AnchorPoint ```json { "type": "Vector2", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` This property determines the origin point of a [GuiObject](/docs/reference/engine/classes/GuiObject.md), relative to its absolute size. The origin point determines from where the element is positioned (through [GuiObject.Position](/docs/reference/engine/classes/GuiObject.md)) and from which the rendered [GuiObject.Size](/docs/reference/engine/classes/GuiObject.md) expands. See [here](/docs/en-us/ui/position-and-size.md#anchorpoint) for illustrated diagrams and details. **AnchorPoint Demo** This code sample moves a UI element to different sides of the parent element. It starts at the top-left and ends at the bottom-right. Paste into a LocalScript in a Frame, within a ScreenGui. ```lua local guiObject = script.Parent while true do -- Top-left guiObject.AnchorPoint = Vector2.new(0, 0) guiObject.Position = UDim2.new(0, 0, 0, 0) task.wait(1) -- Top guiObject.AnchorPoint = Vector2.new(0.5, 0) guiObject.Position = UDim2.new(0.5, 0, 0, 0) task.wait(1) -- Top-right guiObject.AnchorPoint = Vector2.new(1, 0) guiObject.Position = UDim2.new(1, 0, 0, 0) task.wait(1) -- Left guiObject.AnchorPoint = Vector2.new(0, 0.5) guiObject.Position = UDim2.new(0, 0, 0.5, 0) task.wait(1) -- Dead center guiObject.AnchorPoint = Vector2.new(0.5, 0.5) guiObject.Position = UDim2.new(0.5, 0, 0.5, 0) task.wait(1) -- Right guiObject.AnchorPoint = Vector2.new(1, 0.5) guiObject.Position = UDim2.new(1, 0, 0.5, 0) task.wait(1) -- Bottom-left guiObject.AnchorPoint = Vector2.new(0, 1) guiObject.Position = UDim2.new(0, 0, 1, 0) task.wait(1) -- Bottom guiObject.AnchorPoint = Vector2.new(0.5, 1) guiObject.Position = UDim2.new(0.5, 0, 1, 0) task.wait(1) -- Bottom-right guiObject.AnchorPoint = Vector2.new(1, 1) guiObject.Position = UDim2.new(1, 0, 1, 0) task.wait(1) end ``` ### Property: GuiObject.AutomaticSize ```json { "type": "AutomaticSize", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` This property is used to automatically size parent UI objects based on the size of its descendants. You can use this property to dynamically add text and other content to a UI object at edit or run time, and the size will adjust to fit that content. When [AutomaticSize](/docs/reference/engine/classes/GuiObject.md) is set to an [AutomaticSize](/docs/reference/engine/enums/AutomaticSize.md) value to anything other than [None](/docs/reference/engine/enums/AutomaticSize.md), this UI object may resize depending on its child content. For more information on how to use this property and how it works, please see [here](/docs/en-us/ui/size-modifiers.md#automatic-sizing). **LocalScript in a ScreenGui** The following script creates an automatically-sized parent frame with a`UIListLayout`, then it inserts several automatically-sized `TextLabel` objects. Note how the parent `UIListLayout` automatically resizes to fit its child content and the labels automatically resize to fit their text content. This script can be parented to a `ScreenGui`. ```lua -- Array of text labels/fonts/sizes to output local labelArray = { { text = "Lorem", font = Enum.Font.Creepster, size = 50 }, { text = "ipsum", font = Enum.Font.IndieFlower, size = 35 }, { text = "dolor", font = Enum.Font.Antique, size = 55 }, { text = "sit", font = Enum.Font.SpecialElite, size = 65 }, { text = "amet", font = Enum.Font.FredokaOne, size = 40 }, } -- Create an automatically-sized parent frame local parentFrame = Instance.new("Frame") parentFrame.AutomaticSize = Enum.AutomaticSize.XY parentFrame.BackgroundColor3 = Color3.fromRGB(90, 90, 90) parentFrame.Size = UDim2.fromOffset(25, 100) parentFrame.Position = UDim2.fromScale(0.1, 0.1) parentFrame.Parent = script.Parent -- Add a list layout local listLayout = Instance.new("UIListLayout") listLayout.Padding = UDim.new(0, 5) listLayout.Parent = parentFrame -- Set rounded corners and padding for visual aesthetics local roundedCornerParent = Instance.new("UICorner") roundedCornerParent.Parent = parentFrame local uiPaddingParent = Instance.new("UIPadding") uiPaddingParent.PaddingTop = UDim.new(0, 5) uiPaddingParent.PaddingLeft = UDim.new(0, 5) uiPaddingParent.PaddingRight = UDim.new(0, 5) uiPaddingParent.PaddingBottom = UDim.new(0, 5) uiPaddingParent.Parent = parentFrame for i = 1, #labelArray do -- Create an automatically-sized text label from array local childLabel = Instance.new("TextLabel") childLabel.AutomaticSize = Enum.AutomaticSize.XY childLabel.Size = UDim2.fromOffset(75, 15) childLabel.Text = labelArray[i]["text"] childLabel.Font = labelArray[i]["font"] childLabel.TextSize = labelArray[i]["size"] childLabel.TextColor3 = Color3.new(1, 1, 1) childLabel.Parent = parentFrame -- Visual aesthetics local roundedCorner = Instance.new("UICorner") roundedCorner.Parent = childLabel local uiPadding = Instance.new("UIPadding") uiPadding.PaddingTop = UDim.new(0, 5) uiPadding.PaddingLeft = UDim.new(0, 5) uiPadding.PaddingRight = UDim.new(0, 5) uiPadding.PaddingBottom = UDim.new(0, 5) uiPadding.Parent = childLabel task.wait(2) end ``` ### Property: GuiObject.BackgroundColor3 ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` This property determines the color of a [GuiObject](/docs/reference/engine/classes/GuiObject.md) background (the fill color). If your element contains text, such as a [TextBox](/docs/reference/engine/classes/TextBox.md), [TextButton](/docs/reference/engine/classes/TextButton.md), or [TextLabel](/docs/reference/engine/classes/TextLabel.md), make sure the color of your background contrasts the text's color. Another property that determines the visual properties of the background is [GuiObject.BackgroundTransparency](/docs/reference/engine/classes/GuiObject.md); if this is set to `1`, neither the background nor the border will render. See also [BorderColor3](/docs/reference/engine/classes/GuiObject.md). **Rainbow Frame** This code sample causes a parent Frame to loop through all colors of the rainbow using Color3.fromHSV. ```lua -- Put this code in a LocalScript in a Frame local frame = script.Parent while true do for hue = 0, 255, 4 do -- HSV = hue, saturation, value -- If we loop from 0 to 1 repeatedly, we get a rainbow! frame.BorderColor3 = Color3.fromHSV(hue / 256, 1, 1) frame.BackgroundColor3 = Color3.fromHSV(hue / 256, 0.5, 0.8) task.wait() end end ``` ### Property: GuiObject.BackgroundTransparency ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` This property determines the transparency of the [GuiObject](/docs/reference/engine/classes/GuiObject.md) background and border. It does not, however, determine the transparency of text if the GUI is a [TextBox](/docs/reference/engine/classes/TextBox.md), [TextButton](/docs/reference/engine/classes/TextButton.md), or [TextLabel](/docs/reference/engine/classes/TextLabel.md); text transparency is determined [TextBox.TextTransparency](/docs/reference/engine/classes/TextBox.md), [TextButton.TextTransparency](/docs/reference/engine/classes/TextButton.md), and [TextLabel.TextTransparency](/docs/reference/engine/classes/TextLabel.md) respectively. If this property is set to `1`, neither the background nor the border will render and the GUI background will be completely transparent. ### Property: GuiObject.BorderColor3 ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` Determines the color of the [GuiObject](/docs/reference/engine/classes/GuiObject.md) rectangular border (also known as the stroke color). This is separate from the object's [GuiObject.BackgroundColor3](/docs/reference/engine/classes/GuiObject.md). You will not be able to see the object's border if its [GuiObject.BorderSizePixel](/docs/reference/engine/classes/GuiObject.md) property is set to `0`. Note that the [UIStroke](/docs/reference/engine/classes/UIStroke.md) component allows for more advanced border effects. **Button Highlight** This code sample causes the border of a parent GuiObject to highlight when the user hovers their mouse over the element. ```lua -- Put me inside some GuiObject, preferrably an ImageButton/TextButton local button = script.Parent local function onEnter() button.BorderSizePixel = 2 button.BorderColor3 = Color3.new(1, 1, 0) -- Yellow end local function onLeave() button.BorderSizePixel = 1 button.BorderColor3 = Color3.new(0, 0, 0) -- Black end -- Connect events button.MouseEnter:Connect(onEnter) button.MouseLeave:Connect(onLeave) -- Our default state is "not hovered" onLeave() ``` ### Property: GuiObject.BorderMode ```json { "type": "BorderMode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` This property determines in what manner the [GuiObject](/docs/reference/engine/classes/GuiObject.md) border is laid out relative to its dimensions using the enum of the same name, [BorderMode](/docs/reference/engine/enums/BorderMode.md). Note that [UIStroke](/docs/reference/engine/classes/UIStroke.md) can override this property and allow for more advanced border effects. ### Property: GuiObject.BorderSizePixel ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` This property determines how wide the [GuiObject](/docs/reference/engine/classes/GuiObject.md) border renders, in pixels. Setting this to 0 disables the border altogether. Note that [UIStroke](/docs/reference/engine/classes/UIStroke.md) can override this property and allow for more advanced border effects. **Button Highlight** This code sample causes the border of a parent GuiObject to highlight when the user hovers their mouse over the element. ```lua -- Put me inside some GuiObject, preferrably an ImageButton/TextButton local button = script.Parent local function onEnter() button.BorderSizePixel = 2 button.BorderColor3 = Color3.new(1, 1, 0) -- Yellow end local function onLeave() button.BorderSizePixel = 1 button.BorderColor3 = Color3.new(0, 0, 0) -- Black end -- Connect events button.MouseEnter:Connect(onEnter) button.MouseLeave:Connect(onLeave) -- Our default state is "not hovered" onLeave() ``` ### Property: GuiObject.ClipsDescendants ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "UI" ] } ``` This property determines if the [GuiObject](/docs/reference/engine/classes/GuiObject.md) will clip (make invisible) any portion of descendant GUI elements that would otherwise render outside the bounds of the rectangle. Note that [Rotation](/docs/reference/engine/classes/GuiObject.md) isn't supported by this property. If this or any ancestor GUI has a **non‑zero** [Rotation](/docs/reference/engine/classes/GuiObject.md), this property is **ignored** and descendant GUI elements will be rendered regardless of this property's value. ### Property: GuiObject.GuiState ```json { "type": "GuiState", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "UI" ] } ``` When the player's finger is being tapped and held on the [GuiObject](/docs/reference/engine/classes/GuiObject.md), the [GuiState](/docs/reference/engine/classes/GuiObject.md) of the [GuiObject](/docs/reference/engine/classes/GuiObject.md) will be set to [Press](/docs/reference/engine/enums/GuiState.md). Similarly, When the player's finger is being released from the [GuiObject](/docs/reference/engine/classes/GuiObject.md), the [GuiState](/docs/reference/engine/classes/GuiObject.md) of the [GuiObject](/docs/reference/engine/classes/GuiObject.md) will be set to [Idle](/docs/reference/engine/enums/GuiState.md), and when [Interactable](/docs/reference/engine/classes/GuiObject.md) is turned off on the [GuiObject](/docs/reference/engine/classes/GuiObject.md), the [GuiState](/docs/reference/engine/classes/GuiState.md) of the [GuiObject](/docs/reference/engine/classes/GuiObject.md) will be set to [NonInteractable](/docs/reference/engine/enums/GuiState.md). ### Property: GuiObject.InputSink ```json { "type": "InputSink", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` ### Property: GuiObject.Interactable ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` Determines whether the [GuiButton](/docs/reference/engine/classes/GuiButton.md) can be interacted with or not, or if the [GuiState](/docs/reference/engine/enums/GuiState.md) of the [GuiObject](/docs/reference/engine/classes/GuiObject.md) is changing or not. On a [GuiButton](/docs/reference/engine/classes/GuiButton.md): - When the [Interactable](/docs/reference/engine/classes/GuiObject.md) setting on the [GuiButton](/docs/reference/engine/classes/GuiButton.md) is set to `false`, the [GuiButton](/docs/reference/engine/classes/GuiButton.md) will no longer be able to be pressed or clicked, and the [GuiState](/docs/reference/engine/classes/GuiObject.md) will be constantly set to [NonInteractable](/docs/reference/engine/enums/GuiState.md). - When the [Interactable](/docs/reference/engine/classes/GuiObject.md) setting on the [GuiButton](/docs/reference/engine/classes/GuiButton.md) is set to `true`, the [GuiButton](/docs/reference/engine/classes/GuiButton.md) will behave normally again and the [GuiState](/docs/reference/engine/classes/GuiObject.md) will behave normally. On a [GuiObject](/docs/reference/engine/classes/GuiObject.md): - When the [Interactable](/docs/reference/engine/classes/GuiObject.md) setting on the [GuiButton](/docs/reference/engine/classes/GuiButton.md) is set to `false`, the [GuiState](/docs/reference/engine/classes/GuiObject.md) will be constantly set to [NonInteractable](/docs/reference/engine/enums/GuiState.md). - When the [Interactable](/docs/reference/engine/classes/GuiObject.md) setting on the [GuiButton](/docs/reference/engine/classes/GuiButton.md) is set to `true`, the [GuiState](/docs/reference/engine/classes/GuiObject.md) will behave normally again. ### Property: GuiObject.LayoutOrder ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` This property controls the sorting order of the [GuiObject](/docs/reference/engine/classes/GuiObject.md) when using a [UIGridStyleLayout](/docs/reference/engine/classes/UIGridStyleLayout.md) (such as [UIListLayout](/docs/reference/engine/classes/UIListLayout.md) or [UIPageLayout](/docs/reference/engine/classes/UIPageLayout.md)) with [SortOrder](/docs/reference/engine/classes/UIGridStyleLayout.md) set to [SortOrder.LayoutOrder](/docs/reference/engine/enums/SortOrder.md). It has no functionality if the object does not have a sibling UI layout structure. [GuiObjects](/docs/reference/engine/classes/GuiObject.md) are sorted in ascending order where lower values take priority over higher values. Objects with equal values fall back to the order they were added in. If you are unsure if you'll need to add an element between two existing elements in the future, it's a good practice to use multiples of `100` (`0`, `100`, `200`, etc.). This ensures a large gap of layout order values which you can use for elements ordered in-between other elements. See also [ZIndex](/docs/reference/engine/classes/GuiObject.md) which determines the object's **rendering** order instead of sorting order. ### Property: GuiObject.NextSelectionDown ```json { "type": "GuiObject", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Selection", "capabilities": [ "UI" ] } ``` This property sets the [GuiObject](/docs/reference/engine/classes/GuiObject.md) selected when the user moves the [gamepad](/docs/en-us/input/gamepad.md) selector downward. If this property is empty, moving the gamepad downward will not change the selected GUI. Moving the gamepad selector downward sets the [GuiService.SelectedObject](/docs/reference/engine/classes/GuiService.md) to this object unless the GUI is not [Selectable](/docs/reference/engine/classes/GuiObject.md). Note that this property can be set to a GUI element even if it is not [Selectable](/docs/reference/engine/classes/GuiObject.md), so you should ensure that the value of a GUI's selectable property matches your expected behavior. See also [NextSelectionUp](/docs/reference/engine/classes/GuiObject.md), [NextSelectionLeft](/docs/reference/engine/classes/GuiObject.md), and [NextSelectionRight](/docs/reference/engine/classes/GuiObject.md). **Creating a Gamepad Selection Grid** This example demonstrates how to enable Gamepad navigation through a grid of `GuiObject|GUI` elements without manually having to connect the [GuiObject.NextSelectionUp](/docs/reference/engine/classes/GuiObject.md), [GuiObject.NextSelectionDown](/docs/reference/engine/classes/GuiObject.md), and `GuiObject|NextSelectionRight`, and [GuiObject.NextSelectionLeft](/docs/reference/engine/classes/GuiObject.md) properties for every element in the grid. Note that this code sample assumes your `UIGridLayout` is sorted by [name](/docs/reference/engine/classes/UIGridLayout.md), where elements are named in successive numerical order. The code relies on this to set the NextSelection properties for all `GuiObjects` in the same level as the UIGridLayout. In our example, the UIGridLayoutObject and GUI elements within the grid are all children of a `Frame` named _"Container"_. The code [gets the children](/docs/reference/engine/classes/Instance.md) of _"Container"_ and loops through each child. Children that are not GuiObjects are ignored. For each GUI element, the code attempts to assigned the NextSelection properties using the following logic: 1. Starting with 1, the name of all GUI elements match their position in the grid 2. Left: The item to the left will always be numbered 1 less than the current element 3. Right: The item to the left will always be numbered 1 more than the current element 4. Up: The item above (up) will always be number of GUIs in a row 1 less than the current element 5. Down: The item below (down) will always be the number of GUIs in a row more than the current element This logic also allows for the GUI elements at the begging and end of rows (excluding the first and last element) to wrap around to the next and previous rows. If an element doesn't exist to the left, right, up, or down, the NextSelection will remain nil and moving the Gamepad selector in the direction will not change the selected GUI. This example also contains code to test the grid using the arrow keys (Up, Down, Left, Right) of your keyboard instead of a gamepad, just in case you don't have a gamepad to test with. This portion of code initially selects the element named _"1"_ by assigning it to the [GuiService.SelectedObject](/docs/reference/engine/classes/GuiService.md) property. ```lua -- Setup the Gamepad selection grid using the code below local container = script.Parent:FindFirstChild("Container") local grid = container:GetChildren() local rowSize = container:FindFirstChild("UIGridLayout").FillDirectionMaxCells for _, gui in pairs(grid) do if gui:IsA("GuiObject") then local pos = gui.Name -- Left edge gui.NextSelectionLeft = container:FindFirstChild(pos - 1) -- Right edge gui.NextSelectionRight = container:FindFirstChild(pos + 1) -- Above gui.NextSelectionUp = container:FindFirstChild(pos - rowSize) -- Below gui.NextSelectionDown = container:FindFirstChild(pos + rowSize) end end -- Test the Gamepad selection grid using the code below local GuiService = game:GetService("GuiService") local UserInputService = game:GetService("UserInputService") GuiService.SelectedObject = container:FindFirstChild("1") function updateSelection(input) if input.UserInputType == Enum.UserInputType.Keyboard then local key = input.KeyCode local selectedObject = GuiService.SelectedObject if not selectedObject then return end if key == Enum.KeyCode.Up then if not selectedObject.NextSelectionUp then GuiService.SelectedObject = selectedObject end elseif key == Enum.KeyCode.Down then if not selectedObject.NextSelectionDown then GuiService.SelectedObject = selectedObject end elseif key == Enum.KeyCode.Left then if not selectedObject.NextSelectionLeft then GuiService.SelectedObject = selectedObject end elseif key == Enum.KeyCode.Right then if not selectedObject.NextSelectionRight then GuiService.SelectedObject = selectedObject end end end end UserInputService.InputBegan:Connect(updateSelection) ``` ### Property: GuiObject.NextSelectionLeft ```json { "type": "GuiObject", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Selection", "capabilities": [ "UI" ] } ``` This property sets the [GuiObject](/docs/reference/engine/classes/GuiObject.md) selected when the user moves the [gamepad](/docs/en-us/input/gamepad.md) selector to the left. If this property is empty, moving the gamepad to the left will not change the selected GUI. Moving the gamepad selector to the left sets the [GuiService.SelectedObject](/docs/reference/engine/classes/GuiService.md) to this object unless the GUI is not [Selectable](/docs/reference/engine/classes/GuiObject.md). Note that this property can be set to a GUI element even if it is not [Selectable](/docs/reference/engine/classes/GuiObject.md), so you should ensure that the value of a GUI's selectable property matches your expected behavior. See also [NextSelectionUp](/docs/reference/engine/classes/GuiObject.md), [NextSelectionDown](/docs/reference/engine/classes/GuiObject.md), and [NextSelectionRight](/docs/reference/engine/classes/GuiObject.md). **Creating a Gamepad Selection Grid** This example demonstrates how to enable Gamepad navigation through a grid of `GuiObject|GUI` elements without manually having to connect the [GuiObject.NextSelectionUp](/docs/reference/engine/classes/GuiObject.md), [GuiObject.NextSelectionDown](/docs/reference/engine/classes/GuiObject.md), and `GuiObject|NextSelectionRight`, and [GuiObject.NextSelectionLeft](/docs/reference/engine/classes/GuiObject.md) properties for every element in the grid. Note that this code sample assumes your `UIGridLayout` is sorted by [name](/docs/reference/engine/classes/UIGridLayout.md), where elements are named in successive numerical order. The code relies on this to set the NextSelection properties for all `GuiObjects` in the same level as the UIGridLayout. In our example, the UIGridLayoutObject and GUI elements within the grid are all children of a `Frame` named _"Container"_. The code [gets the children](/docs/reference/engine/classes/Instance.md) of _"Container"_ and loops through each child. Children that are not GuiObjects are ignored. For each GUI element, the code attempts to assigned the NextSelection properties using the following logic: 1. Starting with 1, the name of all GUI elements match their position in the grid 2. Left: The item to the left will always be numbered 1 less than the current element 3. Right: The item to the left will always be numbered 1 more than the current element 4. Up: The item above (up) will always be number of GUIs in a row 1 less than the current element 5. Down: The item below (down) will always be the number of GUIs in a row more than the current element This logic also allows for the GUI elements at the begging and end of rows (excluding the first and last element) to wrap around to the next and previous rows. If an element doesn't exist to the left, right, up, or down, the NextSelection will remain nil and moving the Gamepad selector in the direction will not change the selected GUI. This example also contains code to test the grid using the arrow keys (Up, Down, Left, Right) of your keyboard instead of a gamepad, just in case you don't have a gamepad to test with. This portion of code initially selects the element named _"1"_ by assigning it to the [GuiService.SelectedObject](/docs/reference/engine/classes/GuiService.md) property. ```lua -- Setup the Gamepad selection grid using the code below local container = script.Parent:FindFirstChild("Container") local grid = container:GetChildren() local rowSize = container:FindFirstChild("UIGridLayout").FillDirectionMaxCells for _, gui in pairs(grid) do if gui:IsA("GuiObject") then local pos = gui.Name -- Left edge gui.NextSelectionLeft = container:FindFirstChild(pos - 1) -- Right edge gui.NextSelectionRight = container:FindFirstChild(pos + 1) -- Above gui.NextSelectionUp = container:FindFirstChild(pos - rowSize) -- Below gui.NextSelectionDown = container:FindFirstChild(pos + rowSize) end end -- Test the Gamepad selection grid using the code below local GuiService = game:GetService("GuiService") local UserInputService = game:GetService("UserInputService") GuiService.SelectedObject = container:FindFirstChild("1") function updateSelection(input) if input.UserInputType == Enum.UserInputType.Keyboard then local key = input.KeyCode local selectedObject = GuiService.SelectedObject if not selectedObject then return end if key == Enum.KeyCode.Up then if not selectedObject.NextSelectionUp then GuiService.SelectedObject = selectedObject end elseif key == Enum.KeyCode.Down then if not selectedObject.NextSelectionDown then GuiService.SelectedObject = selectedObject end elseif key == Enum.KeyCode.Left then if not selectedObject.NextSelectionLeft then GuiService.SelectedObject = selectedObject end elseif key == Enum.KeyCode.Right then if not selectedObject.NextSelectionRight then GuiService.SelectedObject = selectedObject end end end end UserInputService.InputBegan:Connect(updateSelection) ``` ### Property: GuiObject.NextSelectionRight ```json { "type": "GuiObject", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Selection", "capabilities": [ "UI" ] } ``` This property sets the [GuiObject](/docs/reference/engine/classes/GuiObject.md) selected when the user moves the [gamepad](/docs/en-us/input/gamepad.md) selector to the right. If this property is empty, moving the gamepad to the right will not change the selected GUI. Moving the gamepad selector to the right sets the [GuiService.SelectedObject](/docs/reference/engine/classes/GuiService.md) to this object unless the GUI is not [Selectable](/docs/reference/engine/classes/GuiObject.md). Note that this property can be set to a GUI element even if it is not [Selectable](/docs/reference/engine/classes/GuiObject.md), so you should ensure that the value of a GUI's selectable property matches your expected behavior. See also [NextSelectionUp](/docs/reference/engine/classes/GuiObject.md), [NextSelectionDown](/docs/reference/engine/classes/GuiObject.md), and [NextSelectionLeft](/docs/reference/engine/classes/GuiObject.md). **Creating a Gamepad Selection Grid** This example demonstrates how to enable Gamepad navigation through a grid of `GuiObject|GUI` elements without manually having to connect the [GuiObject.NextSelectionUp](/docs/reference/engine/classes/GuiObject.md), [GuiObject.NextSelectionDown](/docs/reference/engine/classes/GuiObject.md), and `GuiObject|NextSelectionRight`, and [GuiObject.NextSelectionLeft](/docs/reference/engine/classes/GuiObject.md) properties for every element in the grid. Note that this code sample assumes your `UIGridLayout` is sorted by [name](/docs/reference/engine/classes/UIGridLayout.md), where elements are named in successive numerical order. The code relies on this to set the NextSelection properties for all `GuiObjects` in the same level as the UIGridLayout. In our example, the UIGridLayoutObject and GUI elements within the grid are all children of a `Frame` named _"Container"_. The code [gets the children](/docs/reference/engine/classes/Instance.md) of _"Container"_ and loops through each child. Children that are not GuiObjects are ignored. For each GUI element, the code attempts to assigned the NextSelection properties using the following logic: 1. Starting with 1, the name of all GUI elements match their position in the grid 2. Left: The item to the left will always be numbered 1 less than the current element 3. Right: The item to the left will always be numbered 1 more than the current element 4. Up: The item above (up) will always be number of GUIs in a row 1 less than the current element 5. Down: The item below (down) will always be the number of GUIs in a row more than the current element This logic also allows for the GUI elements at the begging and end of rows (excluding the first and last element) to wrap around to the next and previous rows. If an element doesn't exist to the left, right, up, or down, the NextSelection will remain nil and moving the Gamepad selector in the direction will not change the selected GUI. This example also contains code to test the grid using the arrow keys (Up, Down, Left, Right) of your keyboard instead of a gamepad, just in case you don't have a gamepad to test with. This portion of code initially selects the element named _"1"_ by assigning it to the [GuiService.SelectedObject](/docs/reference/engine/classes/GuiService.md) property. ```lua -- Setup the Gamepad selection grid using the code below local container = script.Parent:FindFirstChild("Container") local grid = container:GetChildren() local rowSize = container:FindFirstChild("UIGridLayout").FillDirectionMaxCells for _, gui in pairs(grid) do if gui:IsA("GuiObject") then local pos = gui.Name -- Left edge gui.NextSelectionLeft = container:FindFirstChild(pos - 1) -- Right edge gui.NextSelectionRight = container:FindFirstChild(pos + 1) -- Above gui.NextSelectionUp = container:FindFirstChild(pos - rowSize) -- Below gui.NextSelectionDown = container:FindFirstChild(pos + rowSize) end end -- Test the Gamepad selection grid using the code below local GuiService = game:GetService("GuiService") local UserInputService = game:GetService("UserInputService") GuiService.SelectedObject = container:FindFirstChild("1") function updateSelection(input) if input.UserInputType == Enum.UserInputType.Keyboard then local key = input.KeyCode local selectedObject = GuiService.SelectedObject if not selectedObject then return end if key == Enum.KeyCode.Up then if not selectedObject.NextSelectionUp then GuiService.SelectedObject = selectedObject end elseif key == Enum.KeyCode.Down then if not selectedObject.NextSelectionDown then GuiService.SelectedObject = selectedObject end elseif key == Enum.KeyCode.Left then if not selectedObject.NextSelectionLeft then GuiService.SelectedObject = selectedObject end elseif key == Enum.KeyCode.Right then if not selectedObject.NextSelectionRight then GuiService.SelectedObject = selectedObject end end end end UserInputService.InputBegan:Connect(updateSelection) ``` ### Property: GuiObject.NextSelectionUp ```json { "type": "GuiObject", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Selection", "capabilities": [ "UI" ] } ``` This property sets the [GuiObject](/docs/reference/engine/classes/GuiObject.md) selected when the user moves the [gamepad](/docs/en-us/input/gamepad.md) selector upward. If this property is empty, moving the gamepad upward will not change the selected GUI. Moving the gamepad selector upward sets the [GuiService.SelectedObject](/docs/reference/engine/classes/GuiService.md) to this object unless the GUI is not [Selectable](/docs/reference/engine/classes/GuiObject.md). Note that this property can be set to a GUI element even if it is not [Selectable](/docs/reference/engine/classes/GuiObject.md), so you should ensure that the value of a GUI's selectable property matches your expected behavior. See also [NextSelectionDown](/docs/reference/engine/classes/GuiObject.md), [NextSelectionLeft](/docs/reference/engine/classes/GuiObject.md), [NextSelectionRight](/docs/reference/engine/classes/GuiObject.md). **Creating a Gamepad Selection Grid** This example demonstrates how to enable Gamepad navigation through a grid of `GuiObject|GUI` elements without manually having to connect the [GuiObject.NextSelectionUp](/docs/reference/engine/classes/GuiObject.md), [GuiObject.NextSelectionDown](/docs/reference/engine/classes/GuiObject.md), and `GuiObject|NextSelectionRight`, and [GuiObject.NextSelectionLeft](/docs/reference/engine/classes/GuiObject.md) properties for every element in the grid. Note that this code sample assumes your `UIGridLayout` is sorted by [name](/docs/reference/engine/classes/UIGridLayout.md), where elements are named in successive numerical order. The code relies on this to set the NextSelection properties for all `GuiObjects` in the same level as the UIGridLayout. In our example, the UIGridLayoutObject and GUI elements within the grid are all children of a `Frame` named _"Container"_. The code [gets the children](/docs/reference/engine/classes/Instance.md) of _"Container"_ and loops through each child. Children that are not GuiObjects are ignored. For each GUI element, the code attempts to assigned the NextSelection properties using the following logic: 1. Starting with 1, the name of all GUI elements match their position in the grid 2. Left: The item to the left will always be numbered 1 less than the current element 3. Right: The item to the left will always be numbered 1 more than the current element 4. Up: The item above (up) will always be number of GUIs in a row 1 less than the current element 5. Down: The item below (down) will always be the number of GUIs in a row more than the current element This logic also allows for the GUI elements at the begging and end of rows (excluding the first and last element) to wrap around to the next and previous rows. If an element doesn't exist to the left, right, up, or down, the NextSelection will remain nil and moving the Gamepad selector in the direction will not change the selected GUI. This example also contains code to test the grid using the arrow keys (Up, Down, Left, Right) of your keyboard instead of a gamepad, just in case you don't have a gamepad to test with. This portion of code initially selects the element named _"1"_ by assigning it to the [GuiService.SelectedObject](/docs/reference/engine/classes/GuiService.md) property. ```lua -- Setup the Gamepad selection grid using the code below local container = script.Parent:FindFirstChild("Container") local grid = container:GetChildren() local rowSize = container:FindFirstChild("UIGridLayout").FillDirectionMaxCells for _, gui in pairs(grid) do if gui:IsA("GuiObject") then local pos = gui.Name -- Left edge gui.NextSelectionLeft = container:FindFirstChild(pos - 1) -- Right edge gui.NextSelectionRight = container:FindFirstChild(pos + 1) -- Above gui.NextSelectionUp = container:FindFirstChild(pos - rowSize) -- Below gui.NextSelectionDown = container:FindFirstChild(pos + rowSize) end end -- Test the Gamepad selection grid using the code below local GuiService = game:GetService("GuiService") local UserInputService = game:GetService("UserInputService") GuiService.SelectedObject = container:FindFirstChild("1") function updateSelection(input) if input.UserInputType == Enum.UserInputType.Keyboard then local key = input.KeyCode local selectedObject = GuiService.SelectedObject if not selectedObject then return end if key == Enum.KeyCode.Up then if not selectedObject.NextSelectionUp then GuiService.SelectedObject = selectedObject end elseif key == Enum.KeyCode.Down then if not selectedObject.NextSelectionDown then GuiService.SelectedObject = selectedObject end elseif key == Enum.KeyCode.Left then if not selectedObject.NextSelectionLeft then GuiService.SelectedObject = selectedObject end elseif key == Enum.KeyCode.Right then if not selectedObject.NextSelectionRight then GuiService.SelectedObject = selectedObject end end end end UserInputService.InputBegan:Connect(updateSelection) ``` ### Property: GuiObject.Position ```json { "type": "UDim2", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` This property determines the [GuiObject](/docs/reference/engine/classes/GuiObject.md) pixel and scalar position using a [UDim2](/docs/reference/engine/datatypes/UDim2.md). Position is centered around the object's [GuiObject.AnchorPoint](/docs/reference/engine/classes/GuiObject.md). The scalar position is relative to the size of the parent GUI element, if any. The pixel portions of the [UDim2](/docs/reference/engine/datatypes/UDim2.md) value are the same regardless of the parent GUI's size. The values represent the position of the object in pixels. An object's actual pixel position can be read from the [GuiBase2d.AbsolutePosition](/docs/reference/engine/classes/GuiBase2d.md) property. ### Property: GuiObject.Rotation ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` This property determines the number of degrees by which the [GuiObject](/docs/reference/engine/classes/GuiObject.md) is rotated. Rotation is relative to the **center** of the object, **not** the [AnchorPoint](/docs/reference/engine/classes/GuiObject.md), meaning you cannot change the point of rotation. Additionally, this property is not compatible with [ClipsDescendants](/docs/reference/engine/classes/GuiObject.md). ### Property: GuiObject.Selectable ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Selection", "capabilities": [ "UI" ] } ``` This property determines whether the [GuiObject](/docs/reference/engine/classes/GuiObject.md) can be selected when navigating GUIs using a gamepad. If this property is `true`, a GUI can be selected. Selecting a GUI also sets the [GuiService.SelectedObject](/docs/reference/engine/classes/GuiService.md) property to that object. When this is `false`, the GUI cannot be selected. However, setting this to `false` when a GUI is selected will not deselect it nor change the value of the [GuiService.SelectedObject](/docs/reference/engine/classes/GuiService.md) property. Add [GuiObject.SelectionGained](/docs/reference/engine/classes/GuiObject.md) and [GuiObject.SelectionLost](/docs/reference/engine/classes/GuiObject.md) will not fire for the element. To deselect a GuiObject, you must change the [GuiService.SelectedObject](/docs/reference/engine/classes/GuiService.md) property. This property is useful if a GUI is connected to several GUIs via properties such as this [GuiObject.NextSelectionUp](/docs/reference/engine/classes/GuiObject.md), [GuiObject.NextSelectionDown](/docs/reference/engine/classes/GuiObject.md), [NextSelectionRight](/docs/reference/engine/classes/GuiObject.md), or [NextSelectionLeft](/docs/reference/engine/classes/GuiObject.md). Rather than change all of the properties so that the Gamepad cannot select the GUI, you can disable its Selectable property to temporarily prevent it from being selected. Then, when you want the gamepad selector to be able to select the GUI, simply re-enable its selectable property. **Limiting TextBox Selection** The example below offers a simple demonstration on how to use the [GuiObject.Selectable](/docs/reference/engine/classes/GuiObject.md) property to limit when a GUI element can be selected by the Gamepad navigator. When a `TextBox` has gains focus, it can be selected. However, when a TextBox loses focus it can no longer be selected. Although this is a simple demonstration, the property can also be used to prevent the navigator from selecting UI elements that exist for cosmetic rather than functional purposes. For instance, while the buttons on a menu screen should be selectable, the title image should not be. ```lua local GuiService = game:GetService("GuiService") local textBox = script.Parent local function gainFocus() textBox.Selectable = true GuiService.SelectedObject = textBox end local function loseFocus(_enterPressed, _inputObject) GuiService.SelectedObject = nil textBox.Selectable = false end -- The FocusLost and FocusGained event will fire because the textBox -- is of type TextBox textBox.Focused:Connect(gainFocus) textBox.FocusLost:Connect(loseFocus) ``` ### Property: GuiObject.SelectionImageObject ```json { "type": "GuiObject", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "UI" ] } ``` This property overrides the default selection adornment used for gamepads. Note that the chosen [SelectionImageObject](/docs/reference/engine/classes/GuiObject.md) overlays the selected [GuiObject](/docs/reference/engine/classes/GuiObject.md) with the [Size](/docs/reference/engine/classes/GuiObject.md) of the image. For best results, you should size the custom `SelectionImageObject` via the scale [UDim2](/docs/reference/engine/datatypes/UDim2.md) values to help ensure that the object scales properly over the selected element. Changing the [SelectionImageObject](/docs/reference/engine/classes/GuiObject.md) for a [GuiObject](/docs/reference/engine/classes/GuiObject.md) element only affects that element. To affect all of a user's GUI elements, set the [PlayerGui.SelectionImageObject](/docs/reference/engine/classes/PlayerGui.md) property. To determine or set which GUI element is selected by the user, you can use the [GuiService.SelectedObject](/docs/reference/engine/classes/GuiService.md) property. The player uses the gamepad to select different GUI elements, invoking the [NextSelectionUp](/docs/reference/engine/classes/GuiObject.md), [NextSelectionDown](/docs/reference/engine/classes/GuiObject.md), [NextSelectionLeft](/docs/reference/engine/classes/GuiObject.md), and [NextSelectionRight](/docs/reference/engine/classes/GuiObject.md) events. ### Property: GuiObject.SelectionOrder ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Selection", "capabilities": [ "UI" ] } ``` GuiObjects with a lower SelectionOrder are selected earlier than GuiObjects with a higher SelectionOrder when starting the gamepad selection or calling [GuiService:Select()](/docs/reference/engine/classes/GuiService.md) on an ancestor. This property does not affect directional navigation. Default value is 0. ### Property: GuiObject.Size ```json { "type": "UDim2", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` This property determines the [GuiObject](/docs/reference/engine/classes/GuiObject.md) scalar and pixel size using a [UDim2](/docs/reference/engine/datatypes/UDim2.md). The scalar size is relative to the size of the parent GUI element, if any. The pixel portions of the [UDim2](/docs/reference/engine/datatypes/UDim2.md) value are the same regardless of the parent GUI's size. The values represent the size of the object in pixels. An object's actual pixel size can be read from the [GuiBase2d.AbsoluteSize](/docs/reference/engine/classes/GuiBase2d.md) property. If the [GuiObject](/docs/reference/engine/classes/GuiObject.md) has a parent, its size along each axis is also influenced by the parent's [SizeConstraint](/docs/reference/engine/classes/GuiObject.md). **Health Bar** This code sample allows you to create a simple color-changing health bar using two nested Frames. Paste this into a LocalScript on the inner frame. ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer -- Paste script into a LocalScript that is -- parented to a Frame within a Frame local frame = script.Parent local container = frame.Parent container.BackgroundColor3 = Color3.new(0, 0, 0) -- black -- This function is called when the humanoid's health changes local function onHealthChanged() local human = player.Character.Humanoid local percent = human.Health / human.MaxHealth -- Change the size of the inner bar frame.Size = UDim2.new(percent, 0, 1, 0) -- Change the color of the health bar if percent < 0.1 then frame.BackgroundColor3 = Color3.new(1, 0, 0) -- black elseif percent < 0.4 then frame.BackgroundColor3 = Color3.new(1, 1, 0) -- yellow else frame.BackgroundColor3 = Color3.new(0, 1, 0) -- green end end -- This function runs is called the player spawns in local function onCharacterAdded(character) local human = character:WaitForChild("Humanoid") -- Pattern: update once now, then any time the health changes human.HealthChanged:Connect(onHealthChanged) onHealthChanged() end -- Connect our spawn listener; call it if already spawned player.CharacterAdded:Connect(onCharacterAdded) if player.Character then onCharacterAdded(player.Character) end ``` ### Property: GuiObject.SizeConstraint ```json { "type": "SizeConstraint", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` This property sets the [Size](/docs/reference/engine/classes/GuiObject.md) axes that the [GuiObject](/docs/reference/engine/classes/GuiObject.md) will be based on, relative to the size of its parent. This property is useful for creating GUI objects that are meant to scale with either the width **or** height of a parent object, but not both, effectively preserving the aspect ratio of the object. ### Property: GuiObject.Visible ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` This property whether the [GuiObject](/docs/reference/engine/classes/GuiObject.md) and its descendants will be rendered. The rendering of individual components of a [GuiObject](/docs/reference/engine/classes/GuiObject.md) can be controlled individually through transparency properties such as [GuiObject.BackgroundTransparency](/docs/reference/engine/classes/GuiObject.md), [TextLabel.TextTransparency](/docs/reference/engine/classes/TextLabel.md) and [ImageLabel.ImageTransparency](/docs/reference/engine/classes/ImageLabel.md). When this property is `false`, the [GuiObject](/docs/reference/engine/classes/GuiObject.md) will be ignored by layout structures such as [UIListLayout](/docs/reference/engine/classes/UIListLayout.md), [UIGridLayout](/docs/reference/engine/classes/UIGridLayout.md), and [UITableLayout](/docs/reference/engine/classes/UITableLayout.md). In other words, the space that the element would otherwise occupy in the layout is used by other elements instead. **UI Window** This code sample adds open/close functionality to a Window UI. Paste as a LocalScript that is a sibling of a Frame named Window, a TextButton/ImageButton named Window, and a TextButton/ImageButton within the Window called Close. ```lua local gui = script.Parent local window = gui:WaitForChild("Window") local toggleButton = gui:WaitForChild("ToggleWindow") local closeButton = window:WaitForChild("Close") local function toggleWindowVisbility() -- Flip a boolean using the `not` keyword window.Visible = not window.Visible end toggleButton.Activated:Connect(toggleWindowVisbility) closeButton.Activated:Connect(toggleWindowVisbility) ``` ### Property: GuiObject.ZIndex ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` This property determines the order in which a [GuiObject](/docs/reference/engine/classes/GuiObject.md) renders relative to others. By default, [GuiObjects](/docs/reference/engine/classes/GuiObject.md) render in ascending priority order where those with lower [ZIndex](/docs/reference/engine/classes/GuiObject.md) values are rendered under those with higher values. You can change the render order within a [ScreenGui](/docs/reference/engine/classes/ScreenGui.md), [SurfaceGui](/docs/reference/engine/classes/SurfaceGui.md), or [BillboardGui](/docs/reference/engine/classes/BillboardGui.md) by changing the value of its [ZIndexBehavior](/docs/reference/engine/classes/LayerCollector.md). If you are unsure if you'll need to add an element between two existing elements in the future, it's a good practice to use multiples of `100` (`0`, `100`, `200`, etc.). This ensures a large gap of render order values which you can use for elements layered in-between other elements. See also [LayoutOrder](/docs/reference/engine/classes/GuiObject.md) which controls the **sorting** order of a [GuiObject](/docs/reference/engine/classes/GuiObject.md) when used with a layout structure such as [UIListLayout](/docs/reference/engine/classes/UIListLayout.md) or [UIGridLayout](/docs/reference/engine/classes/UIGridLayout.md). ### Property: GuiObject.Draggable ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "UI" ] } ``` > **Deprecated:** This property is deprecated. Use [UIDragDetector](/docs/reference/engine/classes/UIDragDetector.md) instead, as it supports more input types and can be better customized. This indicates whether a [GuiObject](/docs/reference/engine/classes/GuiObject.md) (and its descendants) can be dragged around the screen. ### Property: GuiObject.BackgroundColor *(hidden)* ```json { "type": "BrickColor", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` > **Deprecated:** This property is deprecated in favor of the [Color3](/docs/reference/engine/datatypes/Color3.md) property [GuiObject.BackgroundColor3](/docs/reference/engine/classes/GuiObject.md), which should be used in new work instead. Determines the color of the [GuiObject](/docs/reference/engine/classes/GuiObject.md) background. ### Property: GuiObject.BorderColor *(hidden)* ```json { "type": "BrickColor", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` > **Deprecated:** This property is deprecated in favor of the [Color3](/docs/reference/engine/datatypes/Color3.md) property BorderColor3, which should be used in new work instead. Determines the color of the [GuiObject](/docs/reference/engine/classes/GuiObject.md) border. ### Property: GuiObject.Transparency *(hidden)* ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` A mixed property of [BackgroundTransparency](/docs/reference/engine/classes/GuiObject.md) and [TextTransparency](/docs/reference/engine/classes/TextLabel.md). ## Methods ### Method: GuiObject:TweenPosition **Signature:** `GuiObject:TweenPosition(endPosition: UDim2, easingDirection?: EasingDirection, easingStyle?: EasingStyle, time?: float, override?: boolean, callback?: Function): boolean` Smoothly moves a GUI to a new [UDim2](/docs/reference/engine/datatypes/UDim2.md) position in the specified time using the specified [EasingDirection](/docs/reference/engine/enums/EasingDirection.md) and [EasingStyle](/docs/reference/engine/enums/EasingStyle.md). This function will return whether the tween will play. It will not play if another tween is acting on the [GuiObject](/docs/reference/engine/classes/GuiObject.md) and the override parameter is `false`. See also [GuiObject:TweenSize()](/docs/reference/engine/classes/GuiObject.md) and [GuiObject:TweenSizeAndPosition()](/docs/reference/engine/classes/GuiObject.md). *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `endPosition` | `UDim2` | | Where the GUI should move to. | | `easingDirection` | `EasingDirection` | `Out` | The direction in which to ease the GUI to the _endPosition_. | | `easingStyle` | `EasingStyle` | `Quad` | The style in which to ease the GUI to the _endPosition_. | | `time` | `float` | `1` | How long, in seconds, the tween should take to complete. | | `override` | `boolean` | `false` | Whether the tween will override an in-progress tween. | | `callback` | `Function` | `nil` | A callback function to execute when the tween completes. | **Returns:** `boolean` — Whether the tween will play. **Tween a GUI's Position** This code sample demonstrates a more involved usage of TweenPosition by detecting when the tween completes/cancels by defining a callback function. It also prints whether the tween will play. ```lua local START_POSITION = UDim2.new(0, 0, 0, 0) local GOAL_POSITION = UDim2.new(1, 0, 1, 0) local guiObject = script.Parent local function callback(state) if state == Enum.TweenStatus.Completed then print("The tween completed uninterrupted") elseif state == Enum.TweenStatus.Canceled then print("Another tween cancelled this one") end end -- Initialize the GuiObject position, then start the tween: guiObject.Position = START_POSITION local willPlay = guiObject:TweenPosition( GOAL_POSITION, -- Final position the tween should reach Enum.EasingDirection.In, -- Direction of the easing Enum.EasingStyle.Sine, -- Kind of easing to apply 2, -- Duration of the tween in seconds true, -- Whether in-progress tweens are interrupted callback -- Function to be callled when on completion/cancelation ) if willPlay then print("The tween will play") else print("The tween will not play") end ``` ### Method: GuiObject:TweenSize **Signature:** `GuiObject:TweenSize(endSize: UDim2, easingDirection?: EasingDirection, easingStyle?: EasingStyle, time?: float, override?: boolean, callback?: Function): boolean` Smoothly resizes a [GuiObject](/docs/reference/engine/classes/GuiObject.md) to a new [UDim2](/docs/reference/engine/datatypes/UDim2.md) in the specified time using the specified [EasingDirection](/docs/reference/engine/enums/EasingDirection.md) and [EasingStyle](/docs/reference/engine/enums/EasingStyle.md). This function will return whether the tween will play. Normally this will always return `true`, but it will return `false` if another tween is active and override is set to `false`. See also [GuiObject:TweenSize()](/docs/reference/engine/classes/GuiObject.md) and [GuiObject:TweenSizeAndPosition()](/docs/reference/engine/classes/GuiObject.md). *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `endSize` | `UDim2` | | The size that the GUI should resize. | | `easingDirection` | `EasingDirection` | `Out` | The direction in which to ease the GUI to the _endSize_. | | `easingStyle` | `EasingStyle` | `Quad` | The style in which to ease the GUI to the _endSize_. | | `time` | `float` | `1` | How long, in seconds, the tween should take to complete. | | `override` | `boolean` | `false` | Whether the tween will override an in-progress tween. | | `callback` | `Function` | `nil` | A callback function to execute when the tween completes. | **Returns:** `boolean` — Whether the tween will play. **Tween a GuiObject's Size** This code sample demonstrates the usage of the [GuiObject:TweenSize()](/docs/reference/engine/classes/GuiObject.md) function. It initiates an animation on the parent's [GuiObject.Size](/docs/reference/engine/classes/GuiObject.md) property to `UDim2.new(0.5, 0, 0.5, 0)`, which is half the `GuiObject`'s parent size on both axes. Additionally, it demonstrates how the callback parameter can be used to detect when the tween stops (whether it was cancelled by another tween or completed). ```lua local guiObject = script.Parent local function callback(didComplete) if didComplete then print("The tween completed successfully") else print("The tween was cancelled") end end local willTween = guiObject:TweenSize( UDim2.new(0.5, 0, 0.5, 0), -- endSize (required) Enum.EasingDirection.In, -- easingDirection (default Out) Enum.EasingStyle.Sine, -- easingStyle (default Quad) 2, -- time (default: 1) true, -- should this tween override ones in-progress? (default: false) callback -- a function to call when the tween completes (default: nil) ) if willTween then print("The GuiObject will tween") else print("The GuiObject will not tween") end ``` **Expected output:** The GuiObject will tween The tween was completed successfully ### Method: GuiObject:TweenSizeAndPosition **Signature:** `GuiObject:TweenSizeAndPosition(endSize: UDim2, endPosition: UDim2, easingDirection?: EasingDirection, easingStyle?: EasingStyle, time?: float, override?: boolean, callback?: Function): boolean` Smoothly resizes and moves a GUI to a new [UDim2](/docs/reference/engine/datatypes/UDim2.md) size and position in the specified time using the specified [EasingDirection](/docs/reference/engine/enums/EasingDirection.md) and [EasingStyle](/docs/reference/engine/enums/EasingStyle.md). This function will return whether the tween will play. Normally this will always return `true`, but it will return `false` if another tween is active and override is set to `false`. See also [GuiObject:TweenSize()](/docs/reference/engine/classes/GuiObject.md) and [GuiObject:TweenSizeAndPosition()](/docs/reference/engine/classes/GuiObject.md). *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `endSize` | `UDim2` | | The size that the GUI should resize. | | `endPosition` | `UDim2` | | Where the GUI should move to. | | `easingDirection` | `EasingDirection` | `Out` | The direction in which to ease the GUI to the _endSize_ and _endPosition_. | | `easingStyle` | `EasingStyle` | `Quad` | The style in which to ease the GUI to the _endSize_ and _endPosition_. | | `time` | `float` | `1` | How long, in seconds, the tween should take to complete. | | `override` | `boolean` | `false` | Whether the tween will override an in-progress tween. | | `callback` | `Function` | `nil` | A callback function to execute when the tween completes. | **Returns:** `boolean` — Whether the tween will play. **Tween a GUI's Size and Position** The below example would tween a [Frame](/docs/reference/engine/classes/Frame.md) to the top left of the parent's size and resize it down to 0. ```lua local frame = script.Parent.Frame frame:TweenSizeAndPosition(UDim2.new(0, 0, 0, 0), UDim2.new(0, 0, 0, 0)) ``` ## Events ### Event: GuiObject.InputBegan **Signature:** `GuiObject.InputBegan(input: InputObject)` This event fires when a user begins interacting with the [GuiObject](/docs/reference/engine/classes/GuiObject.md) via a Human-Computer Interface device (Mouse button down, touch begin, keyboard button down, etc). The [UserInputService](/docs/reference/engine/classes/UserInputService.md) has a similarly named event that is not restricted to a specific UI element: [UserInputService.InputBegan](/docs/reference/engine/classes/UserInputService.md). This event will always fire regardless of game state. See also [GuiObject.InputEnded](/docs/reference/engine/classes/GuiObject.md) and [GuiObject.InputChanged](/docs/reference/engine/classes/GuiObject.md). *Security: None · Capabilities: UI* **Parameters:** | Name | Type | Description | |------|------|-------------| | `input` | `InputObject` | An [InputObject](/docs/reference/engine/classes/InputObject.md), which contains useful data for querying user input such as the [type of input](/docs/reference/engine/enums/UserInputType.md), [state of input](/docs/reference/engine/enums/UserInputState.md), and [screen coordinates of the input](/docs/reference/engine/classes/InputObject.md). | **Tracking the Beginning of Input on a GuiObject** The following example demonstrates one of many usage examples of handling user input from InputBegan depending on its type. In order for this to work as expected, it must be placed in a `LocalScript` and a child of _gui_. ```lua -- In order to use the InputBegan event, you must specify the GuiObject local gui = script.Parent -- A sample function providing multiple usage cases for various types of user input local function inputBegan(input) if input.UserInputType == Enum.UserInputType.Keyboard then print("A key is being pushed down! Key:", input.KeyCode) elseif input.UserInputType == Enum.UserInputType.MouseButton1 then print("The left mouse button has been pressed down at", input.Position) elseif input.UserInputType == Enum.UserInputType.MouseButton2 then print("The right mouse button has been pressed down at", input.Position) elseif input.UserInputType == Enum.UserInputType.Touch then print("A touchscreen input has started at", input.Position) elseif input.UserInputType == Enum.UserInputType.Gamepad1 then print("A button is being pressed on a gamepad! Button:", input.KeyCode) end end gui.InputBegan:Connect(inputBegan) ``` ### Event: GuiObject.InputChanged **Signature:** `GuiObject.InputChanged(input: InputObject)` This event fires when a user changes how they're interacting via a Human-Computer Interface device (Mouse button down, touch begin, keyboard button down, etc). The [UserInputService](/docs/reference/engine/classes/UserInputService.md) has a similarly named event that is not restricted to a specific UI element: [UserInputService.InputChanged](/docs/reference/engine/classes/UserInputService.md). This event will always fire regardless of game state. See also [GuiObject.InputBegan](/docs/reference/engine/classes/GuiObject.md) and [GuiObject.InputEnded](/docs/reference/engine/classes/GuiObject.md). *Security: None · Capabilities: UI* **Parameters:** | Name | Type | Description | |------|------|-------------| | `input` | `InputObject` | An [InputObject](/docs/reference/engine/classes/InputObject.md), which contains useful data for querying user input such as the [type of input](/docs/reference/engine/enums/UserInputType.md), [state of input](/docs/reference/engine/enums/UserInputState.md), and [screen coordinates of the input](/docs/reference/engine/classes/InputObject.md). | **GuiObject InputChanged Demo** The following example demonstrates one of many usage examples of handling user input from InputChanged depending on its type. In order for this to work as expected, it must be placed in a `LocalScript` and a child of _gui_. ```lua local UserInputService = game:GetService("UserInputService") local gui = script.Parent local function printMovement(input) print("Position:", input.Position) print("Movement Delta:", input.Delta) end local function inputChanged(input) if input.UserInputType == Enum.UserInputType.MouseMovement then print("The mouse has been moved!") printMovement(input) elseif input.UserInputType == Enum.UserInputType.MouseWheel then print("The mouse wheel has been scrolled!") print("Wheel Movement:", input.Position.Z) elseif input.UserInputType == Enum.UserInputType.Gamepad1 then if input.KeyCode == Enum.KeyCode.Thumbstick1 then print("The left thumbstick has been moved!") printMovement(input) elseif input.KeyCode == Enum.KeyCode.Thumbstick2 then print("The right thumbstick has been moved!") printMovement(input) elseif input.KeyCode == Enum.KeyCode.ButtonL2 then print("The pressure being applied to the left trigger has changed!") print("Pressure:", input.Position.Z) elseif input.KeyCode == Enum.KeyCode.ButtonR2 then print("The pressure being applied to the right trigger has changed!") print("Pressure:", input.Position.Z) end elseif input.UserInputType == Enum.UserInputType.Touch then print("The user's finger is moving on the screen!") printMovement(input) elseif input.UserInputType == Enum.UserInputType.Gyro then local _rotInput, rotCFrame = UserInputService:GetDeviceRotation() local rotX, rotY, rotZ = rotCFrame:toEulerAnglesXYZ() local rot = Vector3.new(math.deg(rotX), math.deg(rotY), math.deg(rotZ)) print("The rotation of the user's mobile device has been changed!") print("Position", rotCFrame.p) print("Rotation:", rot) elseif input.UserInputType == Enum.UserInputType.Accelerometer then print("The acceleration of the user's mobile device has been changed!") printMovement(input) end end gui.InputChanged:Connect(inputChanged) ``` ### Event: GuiObject.InputEnded **Signature:** `GuiObject.InputEnded(input: InputObject)` The InputEnded event fires when a user stops interacting via a Human-Computer Interface device (Mouse button down, touch begin, keyboard button down, etc). The [UserInputService](/docs/reference/engine/classes/UserInputService.md) has a similarly named event that is not restricted to a specific UI element: [UserInputService.InputEnded](/docs/reference/engine/classes/UserInputService.md). This event will always fire regardless of game state. See also [GuiObject.InputBegan](/docs/reference/engine/classes/GuiObject.md) and [GuiObject.InputChanged](/docs/reference/engine/classes/GuiObject.md). *Security: None · Capabilities: UI* **Parameters:** | Name | Type | Description | |------|------|-------------| | `input` | `InputObject` | An [InputObject](/docs/reference/engine/classes/InputObject.md), which contains useful data for querying user input such as the [UserInputType](/docs/reference/engine/enums/UserInputType.md), [UserInputState](/docs/reference/engine/enums/UserInputState.md), and [InputObject.Position](/docs/reference/engine/classes/InputObject.md). | **Tracking the End of Input on a GuiObject** The following example demonstrates one of many usage examples of handling user input from InputEnded depending on its type. In order for this to work as expected, it must be placed in a `LocalScript` and a child of _gui_. ```lua -- In order to use the InputChanged event, you must specify a GuiObject local gui = script.Parent -- A sample function providing multiple usage cases for various types of user input local function inputEnded(input) if input.UserInputType == Enum.UserInputType.Keyboard then print("A key has been released! Key:", input.KeyCode) elseif input.UserInputType == Enum.UserInputType.MouseButton1 then print("The left mouse button has been released at", input.Position) elseif input.UserInputType == Enum.UserInputType.MouseButton2 then print("The right mouse button has been released at", input.Position) elseif input.UserInputType == Enum.UserInputType.Touch then print("A touchscreen input has been released at", input.Position) elseif input.UserInputType == Enum.UserInputType.Gamepad1 then print("A button has been released on a gamepad! Button:", input.KeyCode) end end gui.InputEnded:Connect(inputEnded) ``` ### Event: GuiObject.MouseEnter **Signature:** `GuiObject.MouseEnter(x: int, y: int)` The MouseEnter event fires when a user moves their mouse into a [GuiObject](/docs/reference/engine/classes/GuiObject.md) element. Please do not rely on the `x` and `y` arguments passed by this event as a fool-proof way to determine where the user's mouse is when it enters a GUI. These coordinates may vary even when the mouse enters the GUI via the same edge - particularly when the mouse enters the element quickly. This is due to the fact the coordinates indicate the position of the mouse when the event fires rather than the exact moment the mouse enters the GUI. This event fires even when the GUI element renders beneath another element. If you would like to track when a user's mouse leaves a GUI element, you can use the [GuiObject.MouseLeave](/docs/reference/engine/classes/GuiObject.md) event. ##### See Also - [GuiObject.MouseLeave](/docs/reference/engine/classes/GuiObject.md) - [GuiObject.MouseMoved](/docs/reference/engine/classes/GuiObject.md) - [GuiObject.MouseWheelForward](/docs/reference/engine/classes/GuiObject.md) - [GuiObject.MouseWheelBackward](/docs/reference/engine/classes/GuiObject.md) *Security: None · Capabilities: UI* **Parameters:** | Name | Type | Description | |------|------|-------------| | `x` | `int` | The mouse's **X** screen coordinate in pixels, relative to the top left corner of the screen. | | `y` | `int` | The mouse's **Y** screen coordinate in pixels, relative to the top left corner of the screen. | **Printing where a Mouse Enters a GuiObject** The following example prints the mouse location, in pixels, when it enters GUI element. ```lua local guiObject = script.Parent guiObject.MouseEnter:Connect(function(x, y) print("The user's mouse cursor has entered the GuiObject at position", x, ",", y) end) ``` ### Event: GuiObject.MouseLeave **Signature:** `GuiObject.MouseLeave(x: int, y: int)` The MouseLeave event fires when a user moves their mouse out of a [GuiObject](/docs/reference/engine/classes/GuiObject.md) element. Please do not rely on the `x` and `y` arguments passed by this event as a fool-proof way to determine where the user's mouse is when it leaves a GUI. These coordinates may vary even when the mouse leaves the GUI via the same edge - particularly when the mouse leaves the element quickly. This is due to the fact the coordinates indicate the position of the mouse when the event fires rather than the exact moment the mouse leaves the GUI. This event fires even when the GUI element renders beneath another element. ##### See Also - [GuiObject.MouseEnter](/docs/reference/engine/classes/GuiObject.md) - [GuiObject.MouseMoved](/docs/reference/engine/classes/GuiObject.md) - [GuiObject.MouseWheelForward](/docs/reference/engine/classes/GuiObject.md) - [GuiObject.MouseWheelBackward](/docs/reference/engine/classes/GuiObject.md) *Security: None · Capabilities: UI* **Parameters:** | Name | Type | Description | |------|------|-------------| | `x` | `int` | The mouse's **X** screen coordinate in pixels, relative to the top left corner of the screen. | | `y` | `int` | The mouse's **Y** screen coordinate in pixels, relative to the top left corner of the screen. | ### Event: GuiObject.MouseMoved **Signature:** `GuiObject.MouseMoved(x: int, y: int)` Fires whenever a user moves their mouse while it is inside a [GuiObject](/docs/reference/engine/classes/GuiObject.md) element. It is similar to [Mouse.Move](/docs/reference/engine/classes/Mouse.md), which fires regardless whether the user's mouse is over a GUI element. Note, this event fires when the mouse's position is updated, therefore it will fire repeatedly while being moved. The `x` and `y` arguments indicate the updated screen coordinates of the user's mouse in pixels. These can be useful to determine the mouse's location on the GUI, screen, and delta since the mouse's previous position if it is being tracked in a global variable. The code below demonstrates how to determine the [Vector2](/docs/reference/engine/datatypes/Vector2.md) offset of the user's mouse relative to a GUI element: ```lua local Players = game:GetService("Players") local CustomScrollingFrame = script.Parent local SubFrame = CustomScrollingFrame:FindFirstChild("SubFrame") local mouse = Players.LocalPlayer:GetMouse() local function getPosition(X, Y) local gui_X = CustomScrollingFrame.AbsolutePosition.X local gui_Y = CustomScrollingFrame.AbsolutePosition.Y local pos = Vector2.new(math.abs(X - gui_X), math.abs(Y - gui_Y - 36)) print(pos) end CustomScrollingFrame.MouseMoved:Connect(getPosition) ``` Note that this event may not fire exactly when the user's mouse enters or exits a GUI element. Therefore, the `x` and `y` arguments may not match up perfectly to the coordinates of the GUI's edges. ##### See Also - [GuiObject.MouseEnter](/docs/reference/engine/classes/GuiObject.md) - [GuiObject.MouseLeave](/docs/reference/engine/classes/GuiObject.md) - [GuiObject.MouseWheelForward](/docs/reference/engine/classes/GuiObject.md) - [GuiObject.MouseWheelBackward](/docs/reference/engine/classes/GuiObject.md) *Security: None · Capabilities: UI* **Parameters:** | Name | Type | Description | |------|------|-------------| | `x` | `int` | The mouse's **X** screen coordinate in pixels, relative to the top left corner of the screen. | | `y` | `int` | The mouse's **Y** screen coordinate in pixels, relative to the top left corner of the screen. | ### Event: GuiObject.MouseWheelBackward **Signature:** `GuiObject.MouseWheelBackward(x: int, y: int)` The WheelBackward event fires when a user scrolls their mouse wheel back when the mouse is over a [GuiObject](/docs/reference/engine/classes/GuiObject.md) element. It is similar to [Mouse.WheelBackward](/docs/reference/engine/classes/Mouse.md), which fires regardless whether the user's mouse is over a GUI element. This event fires merely as an indicator of the wheel's backward movement. This means that the `x` and `y` mouse coordinate arguments don't change as a result of this event. These coordinates only change when the mouse moves, which can be tracked by the [GuiObject.MouseMoved](/docs/reference/engine/classes/GuiObject.md) event. ##### See Also - [GuiObject.MouseEnter](/docs/reference/engine/classes/GuiObject.md) - [GuiObject.MouseLeave](/docs/reference/engine/classes/GuiObject.md) - [GuiObject.MouseMoved](/docs/reference/engine/classes/GuiObject.md) - [GuiObject.MouseWheelForward](/docs/reference/engine/classes/GuiObject.md) *Security: None · Capabilities: UI* **Parameters:** | Name | Type | Description | |------|------|-------------| | `x` | `int` | The mouse's **X** screen coordinate in pixels, relative to the top left corner of the screen. | | `y` | `int` | The mouse's **Y** screen coordinate in pixels, relative to the top left corner of the screen. | ### Event: GuiObject.MouseWheelForward **Signature:** `GuiObject.MouseWheelForward(x: int, y: int)` The WheelForward event fires when a user scrolls their mouse wheel forward when the mouse is over a [GuiObject](/docs/reference/engine/classes/GuiObject.md) element. It is similar to [Mouse.WheelForward](/docs/reference/engine/classes/Mouse.md), which fires regardless whether the user's mouse is over a GUI element. This event fires merely as an indicator of the wheel's forward movement. This means that the **X** and **Y** mouse coordinate arguments do not change as a result of this event. These coordinates only change when the mouse moves, which can be tracked by the [GuiObject.MouseMoved](/docs/reference/engine/classes/GuiObject.md) event. ##### See Also - [GuiObject.MouseEnter](/docs/reference/engine/classes/GuiObject.md) - [GuiObject.MouseLeave](/docs/reference/engine/classes/GuiObject.md) - [GuiObject.MouseMoved](/docs/reference/engine/classes/GuiObject.md) - [GuiObject.MouseWheelBackward](/docs/reference/engine/classes/GuiObject.md) *Security: None · Capabilities: UI* **Parameters:** | Name | Type | Description | |------|------|-------------| | `x` | `int` | The mouse's **X** screen coordinate in pixels, relative to the top left corner of the screen. | | `y` | `int` | The **Y** coordinate of the user's mouse. | ### Event: GuiObject.SelectionGained **Signature:** `GuiObject.SelectionGained()` This event fires when the Gamepad selector starts focusing on the [GuiObject](/docs/reference/engine/classes/GuiObject.md). If you want to check from the Gamepad select stops focusing on the GUI element, you can use the [GuiObject.SelectionLost](/docs/reference/engine/classes/GuiObject.md) event. When a GUI gains selection focus, the value of the [SelectedObject](/docs/reference/engine/classes/GuiService.md) property also changes to the that gains selection. To determine which GUI gained selection, check the value of this property. *Security: None · Capabilities: UI* **Handling GUI Selection Gained** The following example prints a message when the user selects the object with a gamepad. In order for this to work as expected, it must be placed in a `LocalScript` and a child of _gui_. ```lua local guiObject = script.Parent local function selectionGained() print("The user has selected this button with a gamepad.") end guiObject.SelectionGained:Connect(selectionGained) ``` ### Event: GuiObject.SelectionLost **Signature:** `GuiObject.SelectionLost()` This event fires when the Gamepad selector stops focusing on the [GuiObject](/docs/reference/engine/classes/GuiObject.md). If you want to check from the Gamepad select starts focusing on the GUI element, you can use the [GuiObject.SelectionGained](/docs/reference/engine/classes/GuiObject.md) event. When a GUI loses selection focus, the value of the [SelectionObject](/docs/reference/engine/classes/GuiService.md) property changes either to `nil` or to the GUI element that gains selection focus. To determine which GUI gained selection, or if no GUI is selected, check the value of this property. *Security: None · Capabilities: UI* **Handling GUI Selection Lost** The following example prints a message when the element has its focus lost on a gamepad. In order for this to work as expected, it must be placed in a `LocalScript` and a child of _gui_. ```lua local guiObject = script.Parent local function selectionLost() print("The user no longer has this selected with their gamepad.") end guiObject.SelectionLost:Connect(selectionLost) ``` ### Event: GuiObject.TouchLongPress **Signature:** `GuiObject.TouchLongPress(touchPositions: Array, state: UserInputState)` This event fires after a brief moment when the player holds their finger on the UI element using a touch-enabled device. It fires with a table of [Vector2](/docs/reference/engine/datatypes/Vector2.md) that describe the relative screen positions of the fingers involved in the gesture. In addition, it fires multiple times: [UserInputState.Begin](/docs/reference/engine/enums/UserInputState.md) after a brief delay, [UserInputState.Change](/docs/reference/engine/enums/UserInputState.md) if the player moves their finger during the gesture, and finally [UserInputState.End](/docs/reference/engine/enums/UserInputState.md). The delay is platform dependent; in Studio it is a little longer than one second. *Security: None · Capabilities: UI* **Parameters:** | Name | Type | Description | |------|------|-------------| | `touchPositions` | `Array` | An array of [Vector2](/docs/reference/engine/datatypes/Vector2.md) that describe the **relative** positions of the fingers involved in the gesture. | | `state` | `UserInputState` | A [UserInputState](/docs/reference/engine/enums/UserInputState.md) that describes the state of the gesture: - Begin fires once at the beginning of the gesture (after the brief delay) - Change fires if the player moves their finger while pressing down - End fires once at the end of the gesture when they release their finger. | **Move UI Element with TouchLongPress** This code sample allows the player to manipulate the screen position of some UI element, like a `Frame`, by holding down on the UI element for a brief moment. Then, the player moves their finger and releases. During the gesture the Frame is colored blue with a white outline. ```lua local frame = script.Parent frame.Active = true local dragging = false local basePosition local startTouchPosition local borderColor3 local backgroundColor3 local function onTouchLongPress(touchPositions, state) if state == Enum.UserInputState.Begin and not dragging then -- Start a drag dragging = true basePosition = frame.Position startTouchPosition = touchPositions[1] -- Color the frame to indicate the drag is happening borderColor3 = frame.BorderColor3 backgroundColor3 = frame.BackgroundColor3 frame.BorderColor3 = Color3.new(1, 1, 1) -- White frame.BackgroundColor3 = Color3.new(0, 0, 1) -- Blue elseif state == Enum.UserInputState.Change then local touchPosition = touchPositions[1] local deltaPosition = UDim2.new(0, touchPosition.X - startTouchPosition.X, 0, touchPosition.Y - startTouchPosition.Y) frame.Position = basePosition + deltaPosition elseif state == Enum.UserInputState.End and dragging then -- Stop the drag dragging = false frame.BorderColor3 = borderColor3 frame.BackgroundColor3 = backgroundColor3 end end frame.TouchLongPress:Connect(onTouchLongPress) ``` **Expected output:** When the player hold their finger down on the Frame, it should turn blue to indicate the beginning of a TouchLongPress gesture. Moving their finger should move the Frame accordingly. Releasing it will return the Frame to its original color. ### Event: GuiObject.TouchPan **Signature:** `GuiObject.TouchPan(touchPositions: Array, totalTranslation: Vector2, velocity: Vector2, state: UserInputState)` This event fires when the player moves their finger on the UI element using a touch-enabled device. It fires shortly before [GuiObject.TouchSwipe](/docs/reference/engine/classes/GuiObject.md) would, and does not fire with [GuiObject.TouchTap](/docs/reference/engine/classes/GuiObject.md). This event is useful for allowing the player to manipulate the position of UI elements on the screen. This event fires with a table of [Vector2](/docs/reference/engine/datatypes/Vector2.md) that describe the relative screen positions of the fingers involved in the gesture. In addition, it fires multiple times: [UserInputState.Begin](/docs/reference/engine/enums/UserInputState.md) after a brief delay, [UserInputState.Change](/docs/reference/engine/enums/UserInputState.md) when the player moves their finger during the gesture, and finally with [UserInputState.End](/docs/reference/engine/enums/UserInputState.md). *Security: None · Capabilities: UI* **Parameters:** | Name | Type | Description | |------|------|-------------| | `touchPositions` | `Array` | A Luau array of [Vector2](/docs/reference/engine/datatypes/Vector2.md) objects, each indicating the position of all the fingers involved in the gesture. | | `totalTranslation` | `Vector2` | Indicates how far the pan gesture has gone from its starting point. | | `velocity` | `Vector2` | Indicates how quickly the gesture is being performed in each dimension. | | `state` | `UserInputState` | Indicates the [UserInputState](/docs/reference/engine/enums/UserInputState.md) of the gesture. | **Panning UI Element** This code sample is meant to be placed in a `LocalScript` within an inner `Frame` that is inside an outer `Frame`, or other `GuiObject`. It allows the player to manipulate the position of the inner frame by moving their finger on the outer frame. ```lua local innerFrame = script.Parent local outerFrame = innerFrame.Parent outerFrame.BackgroundTransparency = 0.75 outerFrame.Active = true outerFrame.Size = UDim2.new(1, 0, 1, 0) outerFrame.Position = UDim2.new(0, 0, 0, 0) outerFrame.AnchorPoint = Vector2.new(0, 0) outerFrame.ClipsDescendants = true local dragging = false local basePosition local function onTouchPan(_touchPositions, totalTranslation, _velocity, state) if state == Enum.UserInputState.Begin and not dragging then dragging = true basePosition = innerFrame.Position outerFrame.BackgroundTransparency = 0.25 elseif state == Enum.UserInputState.Change then innerFrame.Position = basePosition + UDim2.new(0, totalTranslation.X, 0, totalTranslation.Y) elseif state == Enum.UserInputState.End and dragging then dragging = false outerFrame.BackgroundTransparency = 0.75 end end outerFrame.TouchPan:Connect(onTouchPan) ``` **Expected output:** When the player begins moving their finger on the outer frame, the inner frame should move with their finger. ### Event: GuiObject.TouchPinch **Signature:** `GuiObject.TouchPinch(touchPositions: Array, scale: float, velocity: float, state: UserInputState)` This event fires when the player uses two fingers to make a pinch or pull gesture on the UI element using a touch-enabled device. A **pinch** happens when two or more fingers move closer together, and a **pull** happens when they move apart. This event fires in conjunction with [GuiObject.TouchPan](/docs/reference/engine/classes/GuiObject.md). This event is useful for allowing the player to manipulate the scale (size) of UI elements on the screen, and is most often used for zooming features. This event fires with a table of [Vector2](/docs/reference/engine/datatypes/Vector2.md) that describe the relative screen positions of the fingers involved in the gesture. In addition, it fires multiple times: [UserInputState.Begin](/docs/reference/engine/enums/UserInputState.md) after a brief delay, [UserInputState.Change](/docs/reference/engine/enums/UserInputState.md) when the player moves a finger during the gesture, and finally with [UserInputState.End](/docs/reference/engine/enums/UserInputState.md). It should be noted that the scale should be used **multiplicatively**. *Security: None · Capabilities: UI* **Parameters:** | Name | Type | Description | |------|------|-------------| | `touchPositions` | `Array` | A Luau array of [Vector2](/docs/reference/engine/datatypes/Vector2.md) objects, each indicating the position of all the fingers involved in the pinch gesture. | | `scale` | `float` | A float that indicates the difference from the beginning of the pinch gesture. | | `velocity` | `float` | A float indicating how quickly the pinch gesture is happening. | | `state` | `UserInputState` | Indicates the [UserInputState](/docs/reference/engine/enums/UserInputState.md) of the gesture. | **Pinch/Pull Scaling** This code sample is meant for a `LocalScript` within an inner `Frame` that is inside an outer `Frame`, or other `GuiObject`. It allows the player to scale the inner frame by performing a [GuiObject.TouchPinch](/docs/reference/engine/classes/GuiObject.md) gesture on the outer frame. ```lua local innerFrame = script.Parent local outerFrame = innerFrame.Parent outerFrame.BackgroundTransparency = 0.75 outerFrame.Active = true outerFrame.Size = UDim2.new(1, 0, 1, 0) outerFrame.Position = UDim2.new(0, 0, 0, 0) outerFrame.AnchorPoint = Vector2.new(0, 0) outerFrame.ClipsDescendants = true local dragging = false local uiScale = Instance.new("UIScale") uiScale.Parent = innerFrame local baseScale local function onTouchPinch(_touchPositions, scale, _velocity, state) if state == Enum.UserInputState.Begin and not dragging then dragging = true baseScale = uiScale.Scale outerFrame.BackgroundTransparency = 0.25 elseif state == Enum.UserInputState.Change then uiScale.Scale = baseScale * scale -- Notice the multiplication here elseif state == Enum.UserInputState.End and dragging then dragging = false outerFrame.BackgroundTransparency = 0.75 end end outerFrame.TouchPinch:Connect(onTouchPinch) ``` **Expected output:** When the player performs a pinch gesture, the inner frame should get smaller. When the player performs a pull gesture, the inner frame should get bigger. Note that if the inner frame renders text, there may be text rendering issues using a `UIScale`. ### Event: GuiObject.TouchRotate **Signature:** `GuiObject.TouchRotate(touchPositions: Array, rotation: float, velocity: float, state: UserInputState)` This event fires when the player uses two fingers to make a pinch or pull gesture on the UI element using a touch-enabled device. Rotation occurs when the angle of the line between two fingers changes. This event fires in conjunction with [GuiObject.TouchPan](/docs/reference/engine/classes/GuiObject.md). This event is useful for allowing the player to manipulate the rotation of UI elements on the screen. This event fires with a table of [Vector2](/docs/reference/engine/datatypes/Vector2.md) that describe the relative screen positions of the fingers involved in the gesture. In addition, it fires multiple times: [UserInputState.Begin](/docs/reference/engine/enums/UserInputState.md) after a brief delay, [UserInputState.Change](/docs/reference/engine/enums/UserInputState.md) when the player moves a finger during the gesture, and finally with [UserInputState.End](/docs/reference/engine/enums/UserInputState.md). *Security: None · Capabilities: UI* **Parameters:** | Name | Type | Description | |------|------|-------------| | `touchPositions` | `Array` | A Luau array of [Vector2](/docs/reference/engine/datatypes/Vector2.md) objects, each indicating the position of all the fingers involved in the gesture. | | `rotation` | `float` | A float indicating how much the rotation has gone from the start of the gesture. | | `velocity` | `float` | A float that indicates how quickly the gesture is being performed. | | `state` | `UserInputState` | Indicates the [UserInputState](/docs/reference/engine/enums/UserInputState.md) of the gesture. | **Touch Rotation** This code sample is meant for a `LocalScript` within an inner `Frame` that is inside an outer `Frame`, or other `GuiObject`. It allows the player to rotate the inner frame by performing a [GuiObject.TouchRotate](/docs/reference/engine/classes/GuiObject.md) gesture on the outer frame. ```lua local innerFrame = script.Parent local outerFrame = innerFrame.Parent outerFrame.BackgroundTransparency = 0.75 outerFrame.Active = true outerFrame.Size = UDim2.new(1, 0, 1, 0) outerFrame.Position = UDim2.new(0, 0, 0, 0) outerFrame.AnchorPoint = Vector2.new(0, 0) outerFrame.ClipsDescendants = true local dragging = false local baseRotation = innerFrame.Rotation local function onTouchRotate(_touchPositions, rotation, _velocity, state) if state == Enum.UserInputState.Begin and not dragging then dragging = true baseRotation = innerFrame.Rotation outerFrame.BackgroundTransparency = 0.25 elseif state == Enum.UserInputState.Change then innerFrame.Rotation = baseRotation + rotation elseif state == Enum.UserInputState.End and dragging then dragging = false outerFrame.BackgroundTransparency = 0.75 end end outerFrame.TouchRotate:Connect(onTouchRotate) ``` **Expected output:** When the player rotates two fingers on the outer frame, the inner frame should rotate accordingly. ### Event: GuiObject.TouchSwipe **Signature:** `GuiObject.TouchSwipe(swipeDirection: SwipeDirection, numberOfTouches: int)` This event fires when the player performs a swipe gesture on the UI element using a touch-enabled device. It fires with the direction of the gesture (Up, Down, Left or Right) and the number of touch points involved in the gesture. Swipe gestures are often used to change tabs in mobile UIs. *Security: None · Capabilities: UI* **Parameters:** | Name | Type | Description | |------|------|-------------| | `swipeDirection` | `SwipeDirection` | A [SwipeDirection](/docs/reference/engine/enums/SwipeDirection.md) indicating the direction of the swipe gesture (Up, Down, Left or Right). | | `numberOfTouches` | `int` | The number of touch points involved in the gesture (usually 1). | **Bouncing Color Picker** This code sample will cause a `Frame` (or other `GuiObject`) to bounce when a swipe gesture is performed on a touch-enabled device (or Studio's emulator). Horizontal swipes will change the hue of the [GuiObject.BackgroundColor3](/docs/reference/engine/classes/GuiObject.md), while vertical swipes will change the saturation. ```lua local frame = script.Parent frame.Active = true -- How far the frame should bounce on a successful swipe local BOUNCE_DISTANCE = 50 -- Current state of the frame local basePosition = frame.Position local hue = 0 local saturation = 128 local function updateColor() frame.BackgroundColor3 = Color3.fromHSV(hue / 256, saturation / 256, 1) end local function onTouchSwipe(swipeDir, _touchCount) -- Change the BackgroundColor3 based on the swipe direction local deltaPos if swipeDir == Enum.SwipeDirection.Right then deltaPos = UDim2.new(0, BOUNCE_DISTANCE, 0, 0) hue = (hue + 16) % 255 elseif swipeDir == Enum.SwipeDirection.Left then deltaPos = UDim2.new(0, -BOUNCE_DISTANCE, 0, 0) hue = (hue - 16) % 255 elseif swipeDir == Enum.SwipeDirection.Up then deltaPos = UDim2.new(0, 0, 0, -BOUNCE_DISTANCE) saturation = (saturation + 16) % 255 elseif swipeDir == Enum.SwipeDirection.Down then deltaPos = UDim2.new(0, 0, 0, BOUNCE_DISTANCE) saturation = (saturation - 16) % 255 else deltaPos = UDim2.new() end -- Update the color and bounce the frame a little updateColor() frame.Position = basePosition + deltaPos frame:TweenPosition(basePosition, Enum.EasingDirection.Out, Enum.EasingStyle.Bounce, 0.7, true) end frame.TouchSwipe:Connect(onTouchSwipe) updateColor() ``` **Expected output:** The Frame will bounce and change color in a direction when you perform a swipe gesture on it using a touch-enabled device (or Studio's emulator). ### Event: GuiObject.TouchTap **Signature:** `GuiObject.TouchTap(touchPositions: Array)` This event fires when the player performs a tap gesture on the UI element using a touch-enabled device. A tap is a quick single touch without any movement involved (a longer press would fire [GuiObject.TouchLongPress](/docs/reference/engine/classes/GuiObject.md), and moving during the touch would fire [GuiObject.TouchPan](/docs/reference/engine/classes/GuiObject.md) and/or [GuiObject.TouchSwipe](/docs/reference/engine/classes/GuiObject.md)). It fires with a table of [Vector2](/docs/reference/engine/datatypes/Vector2.md) objects that describe the relative positions of the fingers involved in the gesture. *Security: None · Capabilities: UI* **Parameters:** | Name | Type | Description | |------|------|-------------| | `touchPositions` | `Array` | An array of [Vector2](/docs/reference/engine/datatypes/Vector2.md) that describe the **relative** positions of the fingers involved in the gesture. | **Tap Transparency Toggle** This code sample will toggle the [GuiObject.BackgroundTransparency](/docs/reference/engine/classes/GuiObject.md) of a UI element, like a `Frame`, when it is tapped on a touch-enabled device. ```lua local frame = script.Parent frame.Active = true local function onTouchTap() -- Toggle background transparency if frame.BackgroundTransparency > 0 then frame.BackgroundTransparency = 0 else frame.BackgroundTransparency = 0.75 end end frame.TouchTap:Connect(onTouchTap) ``` **Expected output:** Upon briefly tapping the UI element, it should toggle the BackgroundTransparency. ### Event: GuiObject.DragBegin **Signature:** `GuiObject.DragBegin(initialPosition: UDim2)` > **Deprecated:** This property is deprecated. Use [UIDragDetector](/docs/reference/engine/classes/UIDragDetector.md) instead, as it supports more input types and can be better customized. This event fires when a player begins dragging the object. See also [GuiObject.DragStopped](/docs/reference/engine/classes/GuiObject.md). *Security: None · Capabilities: UI* **Parameters:** | Name | Type | Description | |------|------|-------------| | `initialPosition` | `UDim2` | A [UDim2](/docs/reference/engine/datatypes/UDim2.md) value of the position of the [GuiObject](/docs/reference/engine/classes/GuiObject.md) before any drag operation began. | **(Deprecated) Print the Position of a GUI When Dragging Begins** The below example assumes it's within a GuiObject such as a `TextButton`, and that the [GuiObject.Draggable](/docs/reference/engine/classes/GuiObject.md) property is set to true. It will print the UDim2 of where the player started dragging the object from. ```lua script.Parent.DragBegin:Connect(function(udim2) local x = udim2.X local y = udim2.Y local xScale = x.Scale local xOffset = x.Offset local yScale = y.Scale local yOffset = y.Offset print( ("The player started dragging the object at UDim2.new(%f, %d, %f, %d)"):format(xScale, xOffset, yScale, yOffset) ) end) ``` ### Event: GuiObject.DragStopped **Signature:** `GuiObject.DragStopped(x: int, y: int)` > **Deprecated:** This property is deprecated. Use [UIDragDetector](/docs/reference/engine/classes/UIDragDetector.md) instead, as it supports more input types and can be better customized. This event fires when a player stops dragging the object. See also [GuiObject.DragBegin](/docs/reference/engine/classes/GuiObject.md). *Security: None · Capabilities: UI* **Parameters:** | Name | Type | Description | |------|------|-------------| | `x` | `int` | The mouse's **X** screen location in pixels, relative to the top left corner of the screen. | | `y` | `int` | The mouse's **Y** screen location in pixels, relative to the top left corner of the screen. | **(Deprecated) Print the Position of a GUI When Dragging Stops** The below example assumes it's within a GuiObject such as a `TextButton`, and that the [GuiObject.Draggable](/docs/reference/engine/classes/GuiObject.md) property is set to true. It will print the X and Y of where the player stopped dragging the GUI. For example, if the player were to stop dragging the object at 189, 180 the below would print The player stopped dragging the GuiObject at (189, 180) ```lua script.Parent.DragStopped:Connect(function(x, Y) print(("The player stopped dragging the GuiObject at (%i, %i)"):format(x, Y)) end) ``` ## Inherited Members ### From [GuiBase2d](/docs/reference/engine/classes/GuiBase2d.md) - **Property `AbsolutePosition`** (`Vector2`): Describes the actual screen position of a GuiBase2d element, in - **Property `AbsoluteRotation`** (`float`): Describes the actual screen rotation of a GuiBase2d element, in - **Property `AbsoluteSize`** (`Vector2`): Describes the actual screen size of a GuiBase2d element, in - **Property `AutoLocalize`** (`boolean`): When set to `true`, localization will be applied to this GuiBase2d - **Property `Localize`** (`boolean`): Automatically set to true when a localization table's *(deprecated, hidden)* - **Property `RootLocalizationTable`** (`LocalizationTable`): A reference to a LocalizationTable to be used to apply automated - **Property `SelectionBehaviorDown`** (`SelectionBehavior`): Customizes gamepad selection behavior in the down direction. - **Property `SelectionBehaviorLeft`** (`SelectionBehavior`): Customizes gamepad selection behavior in the left direction. - **Property `SelectionBehaviorRight`** (`SelectionBehavior`): Customizes gamepad selection behavior in the right direction. - **Property `SelectionBehaviorUp`** (`SelectionBehavior`): Customizes gamepad selection behavior in the up direction. - **Property `SelectionGroup`** (`boolean`): Allows customization of gamepad selection movement. - **Event `SelectionChanged`**: Fires when the gamepad selection moves to, leaves, or changes within the ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: GuiService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated summary: "Offers numerous properties and methods for working with GuiObjects, player preferences, and other UI‑related tasks." --- # Class: GuiService > Offers numerous properties and methods for working with > [GuiObjects](/docs/reference/engine/classes/GuiObject.md), player preferences, and other UI‑related tasks. ## Description `GuiService` offers numerous properties and methods for working with [GuiObjects](/docs/reference/engine/classes/GuiObject.md), player preferences, and other UI‑related tasks. ## Properties ### Property: GuiService.AutoSelectGuiEnabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI", "Input" ] } ``` If activated, the Select button on a gamepad or Backslash will automatically set a GUI as the selected object. Disabling this means that GUI navigation will still work if [GuiNavigationEnabled](/docs/reference/engine/classes/GuiService.md) is enabled, but you will have to set [SelectedObject](/docs/reference/engine/classes/GuiService.md) manually to start navigation. ### Property: GuiService.GuiNavigationEnabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI", "Input" ] } ``` Used to enable and disable the default controller GUI navigation. ### Property: GuiService.MenuIsOpen ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` Returns `true` if any menu of [CoreGui](/docs/reference/engine/classes/CoreGui.md) is open. ### Property: GuiService.PreferredTextSize ```json { "type": "PreferredTextSize", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` Gets the player's preferred text size as an [PreferredTextSize](/docs/reference/engine/enums/PreferredTextSize.md) value of [Medium](/docs/reference/engine/enums/PreferredTextSize.md) (default), [Large](/docs/reference/engine/enums/PreferredTextSize.md), [Larger](/docs/reference/engine/enums/PreferredTextSize.md), or [Largest](/docs/reference/engine/enums/PreferredTextSize.md). This property maps to the **Text Size** setting available to players from the Roblox and in‑game **Settings** menus, and it can be combined with [Object.GetPropertyChangedSignal()](/docs/reference/engine/classes/Object.md) to detect text size setting changes for purposes of adjusting UI. When working with UI elements, note the following behaviors: - Text that is constrained to a minimum and/or maximum size through a [UITextSizeConstraint](/docs/reference/engine/classes/UITextSizeConstraint.md) will **not** shrink below or expand above the set [MinTextSize](/docs/reference/engine/classes/UITextSizeConstraint.md)/[MaxTextSize](/docs/reference/engine/classes/UITextSizeConstraint.md), regardless of the player's text size setting. - When [TextScaled](/docs/reference/engine/classes/TextLabel.md) is enabled for a [TextLabel](/docs/reference/engine/classes/TextLabel.md) or [TextButton](/docs/reference/engine/classes/TextButton.md), the element's text will **not** be scaled by the [PreferredTextSize](/docs/reference/engine/classes/GuiService.md) value. - UI elements with [AutomaticSize](/docs/reference/engine/classes/GuiObject.md) enabled will shrink/grow as [PreferredTextSize](/docs/reference/engine/classes/GuiService.md) decreases/increases (element bounds will resize to fit the resized text). - When [TextWrapped](/docs/reference/engine/classes/TextLabel.md) is enabled for a [TextLabel](/docs/reference/engine/classes/TextLabel.md) or [TextButton](/docs/reference/engine/classes/TextButton.md), the element's text will wrap to additional lines as [PreferredTextSize](/docs/reference/engine/classes/GuiService.md) increases, within limits of the element's absolute size. - The results returned by [TextService:GetTextSize()](/docs/reference/engine/classes/TextService.md) and [TextService:GetTextBoundsAsync()](/docs/reference/engine/classes/TextService.md) honor changes related to [PreferredTextSize](/docs/reference/engine/classes/GuiService.md). **Detect/Apply Changes to Preferred Text Size** Listens for changes to [GuiService.PreferredTextSize](/docs/reference/engine/classes/GuiService.md) to appropriately update a UI element's size. ```lua local GuiService = game:GetService("GuiService") local TextService = game:GetService("TextService") local FONT_SIZE = 25 local PADDING = 8 local textLabel = Instance.new("TextLabel") textLabel.Position = UDim2.new(0.5, 0, 0.5, 0) textLabel.AnchorPoint = Vector2.new(0.5, 0.5) textLabel.AutomaticSize = Enum.AutomaticSize.X textLabel.TextSize = FONT_SIZE + PADDING textLabel.Text = "Text Label Test" textLabel.Parent = script.Parent local function applyAddedTextHeight() local finalTextHeight = TextService:GetTextSize("", FONT_SIZE, Enum.Font.BuilderSans, Vector2.new(math.huge, math.huge)).Y local addedTextHeight = finalTextHeight - FONT_SIZE print("PreferredTextSize adds", addedTextHeight, " to default text size") textLabel.Size = UDim2.new(0, 0, 0, FONT_SIZE + addedTextHeight + PADDING) end applyAddedTextHeight() GuiService:GetPropertyChangedSignal("PreferredTextSize"):Connect(applyAddedTextHeight) ``` ### Property: GuiService.SelectedObject ```json { "type": "GuiObject", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI", "Input" ] } ``` Sets the [GuiObject](/docs/reference/engine/classes/GuiObject.md) currently being focused on by the GUI navigator. This may reset to `nil` if the object is off screen. This property is changed by the [SelectionGained](/docs/reference/engine/classes/GuiObject.md) and [SelectionLost](/docs/reference/engine/classes/GuiObject.md) events. If you would like to determine when this property changes without tracking these events for all GUI elements, you can use the [Changed](/docs/reference/engine/classes/Object.md) event. ### Property: GuiService.TopbarInset ```json { "type": "Rect", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` Returns a [Rect](/docs/reference/engine/datatypes/Rect.md) object representing the unoccupied area between the Roblox left-most controls and the edge of the device safe area. The value is dynamic and can be expected to change based on the visibility of UI controls such as changing the local player's [Health](/docs/reference/engine/classes/Humanoid.md) property, usage of [StarterGui:SetCoreGuiEnabled()](/docs/reference/engine/classes/StarterGui.md), changing the size and position of Roblox UI Controls, and/or others. For this reason, it's recommend that you detect and react to changes of this property with [Object:GetPropertyChangedSignal()](/docs/reference/engine/classes/Object.md). **Responsive Frame Within Available Top Bar Space** This code snippet creates a new [ScreenGui](/docs/reference/engine/classes/ScreenGui.md) with a [Frame](/docs/reference/engine/classes/Frame.md) that automatically adapts its size and position to a top bar space unoccupied by Roblox UI. ```lua local GuiService = game:GetService("GuiService") local Players = game:GetService("Players") local screenGui = Instance.new("ScreenGui") screenGui.IgnoreGuiInset = true screenGui.Parent = Players.LocalPlayer.PlayerGui local frame = Instance.new("Frame") frame.BackgroundColor3 = Color3.fromRGB(0, 255, 0) frame.Parent = screenGui GuiService:GetPropertyChangedSignal("TopbarInset"):Connect(function() local inset = GuiService.TopbarInset frame.Size = UDim2.new(0, inset.Width, 0, inset.Height) frame.Position = UDim2.new(0, inset.Min.X, 0, inset.Min.Y) end) ``` ### Property: GuiService.TouchControlsEnabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI", "Input" ] } ``` Used to enable and disable touch controls and touch control display UI. Defaults to `true`. ### Property: GuiService.ViewportDisplaySize ```json { "type": "DisplaySize", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` Read-only property which represents the physical rendering size of the viewport: - [DisplaySize.Small](/docs/reference/engine/enums/DisplaySize.md) — Most tablet/mobile/handheld devices - [DisplaySize.Medium](/docs/reference/engine/enums/DisplaySize.md) — Most laptops and monitors - [DisplaySize.Large](/docs/reference/engine/enums/DisplaySize.md) — Most TVs or larger You can listen for changes to this property through the [GetPropertyChangedSignal()](/docs/reference/engine/classes/Object.md) method to adapt UI to various display sizes. **ViewportDisplaySize Changes** This code snippet listens to [GuiService.ViewportDisplaySize](/docs/reference/engine/classes/GuiService.md) property change signals to adapt UI to various display sizes. ```lua local GuiService = game:GetService("GuiService") local function updateUI() if (GuiService.ViewportDisplaySize == Enum.DisplaySize.Small) then -- Update UI to small screen elseif (GuiService.ViewportDisplaySize == Enum.DisplaySize.Large) then -- Update UI to large screen else -- Update UI to medium/default screen end end GuiService:GetPropertyChangedSignal("ViewportDisplaySize"):Connect(updateUI) updateUI() ``` ### Property: GuiService.IsModalDialog ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` > **Deprecated:** This item is deprecated. Do not use it for new work. This property tells whether or not a modal dialog is visible, such as the game menu or a purchase prompt. ### Property: GuiService.IsWindows ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` > **Deprecated:** This item is deprecated. Do not use it for new work. The IsWindows property defines if the user is playing on a computer running Windows. ### Property: GuiService.CoreGuiNavigationEnabled *(hidden)* ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI", "Input" ] } ``` Toggles whether or not objects in the [CoreGui](/docs/reference/engine/classes/CoreGui.md) can be navigated using a gamepad. ### Property: GuiService.PreferredTransparency *(hidden)* ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` Gets the player's preferred transparency as a number between `0` and `1`. This property maps to the **Background Transparency** setting available to players from the Roblox and in‑experience **Settings** menus, and it can be combined with [Object.GetPropertyChangedSignal()](/docs/reference/engine/classes/Object.md) to detect transparency setting changes for purposes of adjusting UI. A value of `1` (default) indicates the player prefers the default background transparency, while a value of `0` indicates the player prefers fully opaque (non‑transparent) background transparency for improved readability and contrast. Multiplying a UI element's [BackgroundTransparency](/docs/reference/engine/classes/GuiObject.md) with [PreferredTransparency](/docs/reference/engine/classes/GuiService.md) is the recommended approach, such that backgrounds become more opaque as [PreferredTransparency](/docs/reference/engine/classes/GuiService.md) approaches `0`. **Detect/Apply Changes to Preferred Transparency** Listens for changes to [GuiService.PreferredTransparency](/docs/reference/engine/classes/GuiService.md) to adjust the background opacity of UI objects tagged with `TransparentBack` through [CollectionService](/docs/reference/engine/classes/CollectionService.md). ```lua local GuiService = game:GetService("GuiService") local CollectionService = game:GetService("CollectionService") local TAG = "TransparentBack" local transparentBackObjects = {} local function onInstanceAdded(object) if object.BackgroundTransparency then local defaultTransparency = object.BackgroundTransparency transparentBackObjects[object] = defaultTransparency object.BackgroundTransparency = defaultTransparency * GuiService.PreferredTransparency end end local function onInstanceRemoved(object) transparentBackObjects[object] = nil end -- Store initial tagged instances for _, object in CollectionService:GetTagged(TAG) do onInstanceAdded(object) end -- Detect when tagged instance is added or removed CollectionService:GetInstanceAddedSignal(TAG):Connect(onInstanceAdded) CollectionService:GetInstanceRemovedSignal(TAG):Connect(onInstanceRemoved) -- When in-game setting is changed, adjust tagged instances GuiService:GetPropertyChangedSignal("PreferredTransparency"):Connect(function() for object, defaultTransparency in transparentBackObjects do object.BackgroundTransparency = defaultTransparency * GuiService.PreferredTransparency end end) ``` ### Property: GuiService.ReducedMotionEnabled *(hidden)* ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` Returns `true` if the player has enabled reduced motion, indicating that they want motion effects and animations to be reduced or completely removed. This property maps to the **Reduce Motion** toggle available from the Roblox and in‑experience **Settings** menus. See [accessibility guidelines](/docs/en-us/production/publishing/accessibility.md#reduced-motion) for usage recommendations. ## Methods ### Method: GuiService:CloseInspectMenu **Signature:** `GuiService:CloseInspectMenu(): ()` This method closes the [Avatar Inspect Menu](/docs/en-us/players/avatar-inspect-menu.md), if open, when run from a [LocalScript](/docs/reference/engine/classes/LocalScript.md). #### See Also - [InspectPlayerFromHumanoidDescription()](/docs/reference/engine/classes/GuiService.md) which allows the avatar inspection menu to appear showing the assets listed in a [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) object. - [InspectPlayerFromUserId()](/docs/reference/engine/classes/GuiService.md) which allows the avatar inspection menu to appear showing the user that has the given [UserId](/docs/reference/engine/classes/Player.md). *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Returns:** `()` ### Method: GuiService:DismissNotification **Signature:** `GuiService:DismissNotification(notificationId: string): boolean` *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `notificationId` | `string` | | | **Returns:** `boolean` ### Method: GuiService:GetEmotesMenuOpen **Signature:** `GuiService:GetEmotesMenuOpen(): boolean` Returns a boolean indicating whether or not the player emotes menu is open. You can open or close the emotes menu by calling the [SetEmotesMenuOpen()](/docs/reference/engine/classes/GuiService.md) method. *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Returns:** `boolean` — Whether the emotes menu is open. ### Method: GuiService:GetGameplayPausedNotificationEnabled **Signature:** `GuiService:GetGameplayPausedNotificationEnabled(): boolean` This method returns whether or not the [Player.GameplayPaused](/docs/reference/engine/classes/Player.md) notification has been disabled through [SetGameplayPausedNotificationEnabled()](/docs/reference/engine/classes/GuiService.md). See also [Workspace.StreamingIntegrityMode](/docs/reference/engine/classes/Workspace.md) and [StreamingIntegrityMode](/docs/reference/engine/enums/StreamingIntegrityMode.md) for more details on when gameplay is paused. *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Returns:** `boolean` — Whether or not the [Player.GameplayPaused](/docs/reference/engine/classes/Player.md) notification has been disabled. ### Method: GuiService:GetGuiInset **Signature:** `GuiService:GetGuiInset(): Tuple` Returns two [Vector2](/docs/reference/engine/datatypes/Vector2.md) values representing the inset of user GUIs in pixels, from the top‑left corner of the screen and the bottom‑right corner of the screen respectively. Note that the inset values supplied by this method only take effect on [ScreenGuis](/docs/reference/engine/classes/ScreenGui.md) that have their [IgnoreGuiInset](/docs/reference/engine/classes/ScreenGui.md) property set to `false`. ```lua local GuiService = game:GetService("GuiService") local screenAreaTopLeft, screenAreaBottomRight = GuiService:GetGuiInset() print(screenAreaTopLeft, screenAreaBottomRight) ``` *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Returns:** `Tuple` — A tuple of two [Vector2](/docs/reference/engine/datatypes/Vector2.md) values describing the current specified GUI inset. ### Method: GuiService:GetInsetArea **Signature:** `GuiService:GetInsetArea(screenInsets: ScreenInsets): Rect` Takes an [ScreenInsets](/docs/reference/engine/enums/ScreenInsets.md) value and returns a [Rect](/docs/reference/engine/datatypes/Rect.md) describing the inset region relative to the [CoreUISafeInsets](/docs/reference/engine/enums/ScreenInsets.md) area, effectively the usable rectangle for a particular [ScreenInsets](/docs/reference/engine/enums/ScreenInsets.md) area. On a mobile device screen, for example, querying `GetInsetArea(Enum.ScreenInsets.None)` may return a result such as `-59, -58, 792, 334`, meaning the usable rectangle for [ScreenInsets.None](/docs/reference/engine/enums/ScreenInsets.md) has a top‑left corner `59` pixels left and `58` pixels up from the top‑left corner of the core UI area, and a bottom‑right corner `792` pixels right and `334` pixels down from the top‑left corner of the core UI area. ```lua local GuiService = game:GetService("GuiService") local noneRect = GuiService:GetInsetArea(Enum.ScreenInsets.None) print(noneRect) --> -59, -58, 792, 334 local deviceSafeRect = GuiService:GetInsetArea(Enum.ScreenInsets.DeviceSafeInsets) print(deviceSafeRect) --> 0, -58, 733, 313 local coreUISafeRect = GuiService:GetInsetArea(Enum.ScreenInsets.CoreUISafeInsets) print(coreUISafeRect) --> 0, 0, 733, 313 local topbarSafeRect = GuiService:GetInsetArea(Enum.ScreenInsets.TopbarSafeInsets) print(topbarSafeRect) --> 164, -58, 733, 0 ``` *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `screenInsets` | `ScreenInsets` | | | **Returns:** `Rect` ### Method: GuiService:GetInspectMenuEnabled **Signature:** `GuiService:GetInspectMenuEnabled(): boolean` This method returns whether the [Avatar Inspect Menu](/docs/en-us/players/avatar-inspect-menu.md) is currently enabled. The feature is enabled by default and can be disabled using the [SetInspectMenuEnabled()](/docs/reference/engine/classes/GuiService.md) method. *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Returns:** `boolean` — Whether the avatar inspection menu is enabled. ### Method: GuiService:InspectPlayerFromHumanoidDescription **Signature:** `GuiService:InspectPlayerFromHumanoidDescription(humanoidDescription: Instance, name: string): ()` This method allows the [Avatar Inspect Menu](/docs/en-us/players/avatar-inspect-menu.md) to appear showing the assets listed in a [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) object. This allows further customization with what is shown in the inspection menu when players inspect other players in your experience. See also [InspectPlayerFromUserId()](/docs/reference/engine/classes/GuiService.md) which allows the avatar inspection menu to appear showing the user that has the given [UserId](/docs/reference/engine/classes/Player.md). *Security: None · Thread Safety: Unsafe · Capabilities: UI, AvatarAppearance* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `humanoidDescription` | `Instance` | | A [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) object that contains the assets to show in the inspection menu. | | `name` | `string` | | The name of the player being inspected to show in the menu. | **Returns:** `()` ### Method: GuiService:InspectPlayerFromUserId **Signature:** `GuiService:InspectPlayerFromUserId(userId: User): ()` This method allows the [Avatar Inspect Menu](/docs/en-us/players/avatar-inspect-menu.md) to appear showing the user that has the given [UserId](/docs/reference/engine/classes/Player.md). This is especially useful when you want to inspect players who aren't in the current experience. See also [InspectPlayerFromHumanoidDescription()](/docs/reference/engine/classes/GuiService.md) which allows you to bring up the avatar inspection menu showing the assets listed in a [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) object. *Security: None · Thread Safety: Unsafe · Capabilities: UI, AvatarAppearance* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `userId` | `User` | | The [UserId](/docs/reference/engine/classes/Player.md) of the player to inspect. | **Returns:** `()` ### Method: GuiService:IsTenFootInterface **Signature:** `GuiService:IsTenFootInterface(): boolean` Returns `true` if the client is using the ten foot interface, a special version of Roblox's UI exclusive to consoles. Note that you should **not** use this property in an attempt to verify if the player is on a console or not. Instead, consider reading [ViewportDisplaySize](/docs/reference/engine/classes/GuiService.md). *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Returns:** `boolean` ### Method: GuiService:Select **Signature:** `GuiService:Select(selectionParent: Instance): ()` When called on an instance `selectionParent` that is the [PlayerGui](/docs/reference/engine/classes/PlayerGui.md) or a descendant of it, the engine searches all available selectable, visible and on-screen [GuiObjects](/docs/reference/engine/classes/GuiObject.md) that are descendants of `selectionParent` and sets the [SelectedObject](/docs/reference/engine/classes/GuiService.md) to the [GuiObject](/docs/reference/engine/classes/GuiObject.md) with the smallest [SelectionOrder](/docs/reference/engine/classes/GuiObject.md). *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `selectionParent` | `Instance` | | The parent of selection whose descendants are searched. | **Returns:** `()` ### Method: GuiService:SendNotification **Signature:** `GuiService:SendNotification(notificationInfo: Dictionary): string` *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `notificationInfo` | `Dictionary` | | | **Returns:** `string` ### Method: GuiService:SetEmotesMenuOpen **Signature:** `GuiService:SetEmotesMenuOpen(isOpen: boolean): ()` Opens or closes the player emotes menu. *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `isOpen` | `boolean` | | | **Returns:** `()` ### Method: GuiService:SetGameplayPausedNotificationEnabled **Signature:** `GuiService:SetGameplayPausedNotificationEnabled(enabled: boolean): ()` This method lets you disable the built-in notification when a player's gameplay is paused. You can then add in your own UI and customize it. You can query whether the notification is enabled by calling the [GetGameplayPausedNotificationEnabled()](/docs/reference/engine/classes/GuiService.md) method. See also [Workspace.StreamingIntegrityMode](/docs/reference/engine/classes/Workspace.md) and [StreamingIntegrityMode](/docs/reference/engine/enums/StreamingIntegrityMode.md) for more details on when gameplay is paused. *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `enabled` | `boolean` | | Whether or not the built-in notification GUI is disabled. | **Returns:** `()` ### Method: GuiService:SetInspectMenuEnabled **Signature:** `GuiService:SetInspectMenuEnabled(enabled: boolean): ()` This method allows you to enable or disable the [Avatar Inspect Menu](/docs/en-us/players/avatar-inspect-menu.md). The feature is enabled by default. *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `enabled` | `boolean` | | A boolean indicating whether to enable or disable the menu. | **Returns:** `()` ### Method: GuiService:AddSelectionParent **Signature:** `GuiService:AddSelectionParent(selectionName: string, selectionParent: Instance): ()` Creates a selection group where gamepad GUI navigation will only consider selectable objects that are within the group (children of `selectionParent`). An example is when you have a menu pop open and there are other selectable objects on the screen, possibly from previous menus, but you want the user to only be able to select GUI objects in the new menu. *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `selectionName` | `string` | | | | `selectionParent` | `Instance` | | | **Returns:** `()` ### Method: GuiService:AddSelectionTuple **Signature:** `GuiService:AddSelectionTuple(selectionName: string, selections: Tuple): ()` Functions similarly to [GuiService:AddSelectionParent()](/docs/reference/engine/classes/GuiService.md), but you can give it a tuple of [GuiObject](/docs/reference/engine/classes/GuiObject.md) that you want to be contained in the group. *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `selectionName` | `string` | | The name of the added selection. | | `selections` | `Tuple` | | The selection(s) added. | **Returns:** `()` ### Method: GuiService:RemoveSelectionGroup **Signature:** `GuiService:RemoveSelectionGroup(selectionName: string): ()` Removes a group that was created with [AddSelectionParent()](/docs/reference/engine/classes/GuiService.md) or [AddSelectionTuple()](/docs/reference/engine/classes/GuiService.md). *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `selectionName` | `string` | | | **Returns:** `()` ## Events ### Event: GuiService.MenuClosed **Signature:** `GuiService.MenuClosed()` Fires when the user **closes** the Roblox [CoreGui](/docs/reference/engine/classes/CoreGui.md) escape menu. *Security: None · Capabilities: UI* ### Event: GuiService.MenuOpened **Signature:** `GuiService.MenuOpened()` Fires when the user **opens** the Roblox [CoreGui](/docs/reference/engine/classes/CoreGui.md) escape menu. *Security: None · Capabilities: UI* ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: GuidRegistryService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service summary: "An internal service, whose functionality is not accessible to developers." --- # Class: GuidRegistryService > An internal service, whose functionality is not accessible to developers. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: HSRDataContentProvider last_updated: 2026-06-29T19:34:07Z inherits: - CacheableContentProvider - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated --- # Class: HSRDataContentProvider ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: HandleAdornment last_updated: 2026-06-29T19:34:07Z inherits: - PVAdornment - GuiBase3d - GuiBase - Instance - Object type: class memory_category: Instances tags: - NotCreatable summary: "An abstract class inherited by 3D handle adornments." --- # Class: HandleAdornment > An abstract class inherited by 3D handle adornments. ## Description `HandleAdornment` is an abstract class inherited by 3D handle adornments. If parented to a player's [PlayerGui](/docs/reference/engine/classes/PlayerGui.md) or the [CoreGui](/docs/reference/engine/classes/CoreGui.md), handles can listen to input events for purposes such as making dragger tools. ## Properties ### Property: HandleAdornment.AdornCullingMode ```json { "type": "AdornCullingMode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic", "UI" ] } ``` This property determines whether to automatically cull the adornment based on distance from the camera. ### Property: HandleAdornment.AlwaysOnTop ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic", "UI" ] } ``` If `true`, forces this adornment to render on top of all 3D objects in the workspace. As an exception, setting [ZIndex](/docs/reference/engine/classes/HandleAdornment.md) to `-1` will override an [AlwaysOnTop](/docs/reference/engine/classes/HandleAdornment.md) value of `true` and cause the adornment to appear either in front of or behind other adornments and objects based on their relative position in the 3D space. ### Property: HandleAdornment.CFrame ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic", "UI" ] } ``` [CFrame](/docs/reference/engine/datatypes/CFrame.md) position and rotation relative to its [PVAdornment.Adornee](/docs/reference/engine/classes/PVAdornment.md), applied after any translations due to [SizeRelativeOffset](/docs/reference/engine/classes/HandleAdornment.md). ### Property: HandleAdornment.SizeRelativeOffset ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic", "UI" ] } ``` By default, an adornment draws in the center of its [Adornee](/docs/reference/engine/classes/HandleAdornment.md), but this property shifts the adornment's relative position based on the adornee's [BasePart.Size](/docs/reference/engine/classes/BasePart.md). Note that the units of [SizeRelativeOffset](/docs/reference/engine/classes/HandleAdornment.md) are a **scale** based on the size of the adornee itself, such that a value of `1` will move the adornment to the corresponding edge of the adornee. For example, a value of [Vector3.new(0, 1, 0)](/docs/reference/engine/datatypes/Vector3.md) will shift the adornment to the exact top of its adornee. ### Property: HandleAdornment.ZIndex ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic", "UI" ] } ``` Only applies if [AlwaysOnTop](/docs/reference/engine/classes/HandleAdornment.md) is `true` and determines the draw order of this [HandleAdornment](/docs/reference/engine/classes/HandleAdornment.md) relative to other adornments. It does not relate to the [ZIndex](/docs/reference/engine/classes/GuiObject.md) property for [GuiObjects](/docs/reference/engine/classes/GuiObject.md). Valid values are from `-1` to `10` with higher values drawing on top (in front) of lesser values. This drawing order will be respected even if an adornment is in front of or behind another adornment in the 3D space. As an exception, setting [ZIndex](/docs/reference/engine/classes/HandleAdornment.md) to `-1` or [AlwaysOnTop](/docs/reference/engine/classes/HandleAdornment.md) to `false` will cause the adornment to appear either in front of or behind other adornments and objects based on their relative position in the 3D space. ## Events ### Event: HandleAdornment.MouseButton1Down **Signature:** `HandleAdornment.MouseButton1Down()` This event fires when a player presses down their left mouse button while hovering over the adornment. Only fires if the adornment is parented to a player's [PlayerGui](/docs/reference/engine/classes/PlayerGui.md) or the [CoreGui](/docs/reference/engine/classes/CoreGui.md). *Security: None · Capabilities: Basic, UI* ### Event: HandleAdornment.MouseButton1Up **Signature:** `HandleAdornment.MouseButton1Up()` This event fires when a player releases their left mouse button while hovering over the adornment. Only fires if the adornment is parented to a player's [PlayerGui](/docs/reference/engine/classes/PlayerGui.md) or the [CoreGui](/docs/reference/engine/classes/CoreGui.md). *Security: None · Capabilities: Basic, UI* ### Event: HandleAdornment.MouseEnter **Signature:** `HandleAdornment.MouseEnter()` This event fires when a player moves their mouse over the adornment. Only fires if the adornment is parented to a player's [PlayerGui](/docs/reference/engine/classes/PlayerGui.md) or the [CoreGui](/docs/reference/engine/classes/CoreGui.md). *Security: None · Capabilities: Basic, UI* ### Event: HandleAdornment.MouseLeave **Signature:** `HandleAdornment.MouseLeave()` This event fires when a player moves their mouse out of the adornment. Only fires if the adornment is parented to a player's [PlayerGui](/docs/reference/engine/classes/PlayerGui.md) or the [CoreGui](/docs/reference/engine/classes/CoreGui.md). *Security: None · Capabilities: Basic, UI* ## Inherited Members ### From [PVAdornment](/docs/reference/engine/classes/PVAdornment.md) - **Property `Adornee`** (`PVInstance`): The PVInstance which this PVAdornment is attached to. ### From [GuiBase3d](/docs/reference/engine/classes/GuiBase3d.md) - **Property `Color`** (`BrickColor`): Sets the color of a GUI object. *(deprecated, hidden)* - **Property `Color3`** (`Color3`): Sets the color of this GuiBase3d object. - **Property `Transparency`** (`float`): Sets the transparency of this GuiBase3d object. - **Property `Visible`** (`boolean`): Determines whether this GuiBase3d object and its descendants will ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Handles last_updated: 2026-06-29T19:34:07Z inherits: - HandlesBase - PartAdornment - GuiBase3d - GuiBase - Instance - Object type: class memory_category: Instances summary: "Places 3D handles around any object that its Adornee is set to." --- # Class: Handles > Places 3D handles around any object that its [Adornee](/docs/reference/engine/classes/Handles.md) > is set to. ## Description The `Handles` object places 3D handles around any object that its [Adornee](/docs/reference/engine/classes/Handles.md) is set to; this property must be set to a 3D object for the handles to appear. The color can be changed and the shape of the handles can be set to either arrows or spheres. For handles to be interactive, they must be parented to a player's [PlayerGui](/docs/reference/engine/classes/PlayerGui.md) or the [CoreGui](/docs/reference/engine/classes/CoreGui.md). ## Properties ### Property: Handles.Faces ```json { "type": "Faces", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic", "UI", "Input" ] } ``` Sets which sides the GUI handles will appear. ### Property: Handles.Style ```json { "type": "HandlesStyle", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic", "UI", "Input" ] } ``` Sets the GUI style of the handles. Currently there are only two types: [Resize](/docs/reference/engine/enums/HandlesStyle.md) and [Movement](/docs/reference/engine/enums/HandlesStyle.md). ## Events ### Event: Handles.MouseButton1Down **Signature:** `Handles.MouseButton1Down(face: NormalId)` Fired when the left mouse button goes down on one of the GUI handles. *Security: None · Capabilities: Basic, UI, Input* **Parameters:** | Name | Type | Description | |------|------|-------------| | `face` | `NormalId` | | ### Event: Handles.MouseButton1Up **Signature:** `Handles.MouseButton1Up(face: NormalId)` Fired when the left mouse button is released on one of the GUI handles. *Security: None · Capabilities: Basic, UI, Input* **Parameters:** | Name | Type | Description | |------|------|-------------| | `face` | `NormalId` | | ### Event: Handles.MouseDrag **Signature:** `Handles.MouseDrag(face: NormalId, distance: float)` Fired when the mouse moves while the MouseButton1Down event has fired, but the left mouse button has not been released yet. *Security: None · Capabilities: Basic, UI, Input* **Parameters:** | Name | Type | Description | |------|------|-------------| | `face` | `NormalId` | | | `distance` | `float` | | ### Event: Handles.MouseEnter **Signature:** `Handles.MouseEnter(face: NormalId)` Fired when a mouse "enters" the GUI handle. *Security: None · Capabilities: Basic, UI, Input* **Parameters:** | Name | Type | Description | |------|------|-------------| | `face` | `NormalId` | | ### Event: Handles.MouseLeave **Signature:** `Handles.MouseLeave(face: NormalId)` Fired when the mouse leaves the GUI handle. *Security: None · Capabilities: Basic, UI, Input* **Parameters:** | Name | Type | Description | |------|------|-------------| | `face` | `NormalId` | | ## Inherited Members ### From [PartAdornment](/docs/reference/engine/classes/PartAdornment.md) - **Property `Adornee`** (`BasePart`): Sets the object to adorn to. ### From [GuiBase3d](/docs/reference/engine/classes/GuiBase3d.md) - **Property `Color`** (`BrickColor`): Sets the color of a GUI object. *(deprecated, hidden)* - **Property `Color3`** (`Color3`): Sets the color of this GuiBase3d object. - **Property `Transparency`** (`float`): Sets the transparency of this GuiBase3d object. - **Property `Visible`** (`boolean`): Determines whether this GuiBase3d object and its descendants will ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: HandlesBase last_updated: 2026-06-29T19:34:07Z inherits: - PartAdornment - GuiBase3d - GuiBase - Instance - Object type: class memory_category: Instances tags: - NotCreatable summary: "An abstract class for Handle objects, such as ArcHandles and Handles." --- # Class: HandlesBase > An abstract class for Handle objects, such as [ArcHandles](/docs/reference/engine/classes/ArcHandles.md) and > [Handles](/docs/reference/engine/classes/Handles.md). ## Inherited Members ### From [PartAdornment](/docs/reference/engine/classes/PartAdornment.md) - **Property `Adornee`** (`BasePart`): Sets the object to adorn to. ### From [GuiBase3d](/docs/reference/engine/classes/GuiBase3d.md) - **Property `Color`** (`BrickColor`): Sets the color of a GUI object. *(deprecated, hidden)* - **Property `Color3`** (`Color3`): Sets the color of this GuiBase3d object. - **Property `Transparency`** (`float`): Sets the transparency of this GuiBase3d object. - **Property `Visible`** (`boolean`): Determines whether this GuiBase3d object and its descendants will ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: HapticEffect last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances --- # Class: HapticEffect ## Description Modern controllers and devices have motors built‑in to provide haptic feedback. Adding rumbles and vibrations can provide subtle feedback that is hard to convey through visuals or audio. Roblox supports haptics for the following devices: - Android and iOS phones supporting haptics including most iPhone, Pixel, and Samsung Galaxy devices - PlayStation gamepads - Xbox gamepads - Quest Touch controller ## Properties ### Property: HapticEffect.Looped ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Input" ] } ``` Whether the haptic effect loops continuously. ``` local Workspace = game:GetService("Workspace") local effect = Instance.new("HapticEffect") effect.Type = Enum.HapticEffectType.GameplayExplosion effect.Looped = true effect.Parent = Workspace -- Start the haptic effect effect:Play() -- After two seconds, stop the effect task.wait(2) effect:Stop() ``` ### Property: HapticEffect.Position ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Input" ] } ``` Along with [Radius](/docs/reference/engine/classes/HapticEffect.md), specifies the impact position relative to the input device and, effectively, how broadly that impact effects nearby motors. Note that some gamepads do not have both "small" and "large" motors, and that "gamepad large left/right" is not supported on PC. ``` local Workspace = game:GetService("Workspace") local effect = Instance.new("HapticEffect") -- Set the position and radius of impact effect.Position = Vector3.new(0.5, 0.5, 0) effect.Radius = 1 effect.Parent = Workspace effect:Play() ``` ### Property: HapticEffect.Radius ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Input" ] } ``` Along with [Position](/docs/reference/engine/classes/HapticEffect.md), specifies the impact radius relative to the input device and, effectively, how broadly that impact effects nearby motors. Note that some gamepads do not have both "small" and "large" motors, and that "gamepad large left/right" is not supported on PC. ``` local Workspace = game:GetService("Workspace") local effect = Instance.new("HapticEffect") -- Set the position and radius of impact effect.Position = Vector3.new(0.5, 0.5, 0) effect.Radius = 1 effect.Parent = Workspace -- Play the haptic effect effect:Play() ``` ### Property: HapticEffect.Type ```json { "type": "HapticEffectType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Input" ] } ``` The haptic type, such as [HapticEffectType.GameplayCollision](/docs/reference/engine/enums/HapticEffectType.md) for a large immediate rumble that dies down quickly. The [HapticEffectType.Custom](/docs/reference/engine/enums/HapticEffectType.md) value lets you specify a haptic with custom waveform keys defined through [SetWaveformKeys()](/docs/reference/engine/classes/HapticEffect.md). ## Methods ### Method: HapticEffect:Play **Signature:** `HapticEffect:Play(): ()` Plays the haptic effect. ``` local Workspace = game:GetService("Workspace") local effect = Instance.new("HapticEffect") effect.Type = Enum.HapticEffectType.GameplayExplosion effect.Parent = Workspace -- Play the haptic effect effect:Play() ``` *Security: None · Thread Safety: Unsafe · Capabilities: Input* **Returns:** `()` ### Method: HapticEffect:SetWaveformKeys **Signature:** `HapticEffect:SetWaveformKeys(keys: Array): ()` This method lets you define a custom waveform as a table and apply it to the haptic. It takes an array of [FloatCurveKey](/docs/reference/engine/datatypes/FloatCurveKey.md) objects, each constructed with three arguments: **time** in milliseconds, **value** ranging from `0` (no haptic) to `1` (full intensity), and an [KeyInterpolationMode](/docs/reference/engine/enums/KeyInterpolationMode.md) that controls how the curve interpolates. between keys. ``` local Workspace = game:GetService("Workspace") local effect = Instance.new("HapticEffect") -- Set effect type to custom in order to define a waveform effect.Type = Enum.HapticEffectType.Custom effect.Parent = Workspace -- Define the custom waveform curve through a table of FloatCurveKeys -- Time values are in milliseconds; values range from 0 (no haptic) to 1 (full intensity) local rampUpWaveform = { FloatCurveKey.new(0, 0.3, Enum.KeyInterpolationMode.Linear), FloatCurveKey.new(100, 0.4, Enum.KeyInterpolationMode.Linear), FloatCurveKey.new(300, 0.8, Enum.KeyInterpolationMode.Linear), FloatCurveKey.new(400, 1.0, Enum.KeyInterpolationMode.Linear) } -- Set waveform through the effect's method effect:SetWaveformKeys(rampUpWaveform) ``` *Security: None · Thread Safety: Unsafe · Capabilities: Input* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `keys` | `Array` | | | **Returns:** `()` ### Method: HapticEffect:Stop **Signature:** `HapticEffect:Stop(): ()` Stops the haptic effect. ``` local Workspace = game:GetService("Workspace") local effect = Instance.new("HapticEffect") effect.Type = Enum.HapticEffectType.GameplayExplosion effect.Looped = true effect.Parent = Workspace -- Start the haptic effect effect:Play() -- After two seconds, stop the effect task.wait(2) effect:Stop() ``` *Security: None · Thread Safety: Unsafe · Capabilities: Input* **Returns:** `()` ## Events ### Event: HapticEffect.Ended **Signature:** `HapticEffect.Ended()` Fires when the [HapticEffect](/docs/reference/engine/classes/HapticEffect.md) has completed playback and stopped. Note this event will **not** fire for haptics with [Looped](/docs/reference/engine/classes/HapticEffect.md) set to `true` since they continue playing upon reaching the end. This event will also **not** fire when the haptic is stopped before playback has completed. This event is often used to destroy an [HapticEffect](/docs/reference/engine/classes/HapticEffect.md) when it has completed playback. *Security: None · Capabilities: Input* ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: HapticService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated summary: "Provides haptic feedback to controllers and devices." --- # Class: HapticService > Provides haptic feedback to controllers and devices. ## Methods ### Method: HapticService:GetMotor **Signature:** `HapticService:GetMotor(inputType: UserInputType, vibrationMotor: VibrationMotor): Tuple` Returns the current vibration value set to the specified [UserInputType](/docs/reference/engine/classes/InputObject.md) and [VibrationMotor](/docs/reference/engine/enums/VibrationMotor.md). This will not return anything if [SetMotor()](/docs/reference/engine/classes/HapticService.md) has not been called prior. *Security: None · Thread Safety: Unsafe · Capabilities: Input* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `inputType` | `UserInputType` | | The specified [UserInputType](/docs/reference/engine/enums/UserInputType.md). | | `vibrationMotor` | `VibrationMotor` | | The specified [VibrationMotor](/docs/reference/engine/enums/VibrationMotor.md). | **Returns:** `Tuple` — The current vibration value set to the specified [UserInputType](/docs/reference/engine/enums/UserInputType.md) and [VibrationMotor](/docs/reference/engine/enums/VibrationMotor.md) or `nil` if [SetMotor()](/docs/reference/engine/classes/HapticService.md) has not been called prior. ### Method: HapticService:IsMotorSupported **Signature:** `HapticService:IsMotorSupported(inputType: UserInputType, vibrationMotor: VibrationMotor): boolean` Returns `true` if the specified motor is available to be used with the specified [UserInputType](/docs/reference/engine/enums/UserInputType.md). *Security: None · Thread Safety: Unsafe · Capabilities: Input* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `inputType` | `UserInputType` | | The specific [UserInputType](/docs/reference/engine/enums/UserInputType.md) being checked for [VibrationMotor](/docs/reference/engine/enums/VibrationMotor.md) support. | | `vibrationMotor` | `VibrationMotor` | | The specified [VibrationMotor](/docs/reference/engine/enums/VibrationMotor.md) checked to see if it supports the specified [UserInputType](/docs/reference/engine/enums/UserInputType.md). | **Returns:** `boolean` — Boolean of `true` if the specified motor is available to be used with the specified [UserInputType](/docs/reference/engine/enums/UserInputType.md), `false` if not. ### Method: HapticService:IsVibrationSupported **Signature:** `HapticService:IsVibrationSupported(inputType: UserInputType): boolean` Returns `true` if the specified [UserInputType](/docs/reference/engine/enums/UserInputType.md) supports haptic feedback. *Security: None · Thread Safety: Unsafe · Capabilities: Input* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `inputType` | `UserInputType` | | The specified [UserInputType](/docs/reference/engine/enums/UserInputType.md) checked to see if it supports haptic feedback. | **Returns:** `boolean` — Boolean of `true` if the specified [UserInputType](/docs/reference/engine/enums/UserInputType.md) supports haptic feedback. ### Method: HapticService:SetMotor **Signature:** `HapticService:SetMotor(inputType: UserInputType, vibrationMotor: VibrationMotor, vibrationValues: Tuple): ()` Sets the vibration intensity of the specified `inputType` and `vibrationMotor`. Note that almost all usage cases specify [UserInputType.Gamepad1](/docs/reference/engine/enums/UserInputType.md) for `inputType` which is internally mapped to the device's respective hardware. *Security: None · Thread Safety: Unsafe · Capabilities: Input* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `inputType` | `UserInputType` | | The specified [UserInputType](/docs/reference/engine/enums/UserInputType.md). | | `vibrationMotor` | `VibrationMotor` | | The specified [VibrationMotor](/docs/reference/engine/enums/VibrationMotor.md). | | `vibrationValues` | `Tuple` | | How intensely the motor should vibrate. Only uses the first value in the tuple, which should be a number. | **Returns:** `()` **HapticService:SetMotor()** This example makes the small motor vibrate depending on how much pressure is applied to the left trigger, and the large motor vibrate depending on how much pressure is applied to the right trigger. ```lua local UserInputService = game:GetService("UserInputService") local HapticService = game:GetService("HapticService") local cachedInputs = {} local keyToVibration = { [Enum.KeyCode.ButtonL2] = Enum.VibrationMotor.Small, [Enum.KeyCode.ButtonR2] = Enum.VibrationMotor.Large, } local function onInputChanged(property) if property == "Position" then HapticService:SetMotor(inputType, vibrationMotor, input.Position.Z) end end local function onInputBegan(input) if not cachedInputs[input] then local inputType = input.UserInputType if inputType.Name:find("Gamepad") then local vibrationMotor = keyToVibration[input.KeyCode] if vibrationMotor then -- Watch this input manually to accurately update the vibration motor cachedInputs[input] = input.Changed:Connect(onInputChanged) end end end end UserInputService.InputBegan:Connect(onInputBegan) ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Hat last_updated: 2026-06-29T19:34:07Z inherits: - Accoutrement - Instance - Object type: class memory_category: Instances tags: - Deprecated --- # Class: Hat ## Code Samples **Manually Position Hats** You can use this code to manually position hats on the character: ```lua local hat = script.Parent local character = hat.Parent hat.Handle.CFrame = character.Head.CFrame * CFrame.new(0, character.Head.Size.Y / 2, 0) * hat.AttachmentPoint:inverse() ``` ## Inherited Members ### From [Accoutrement](/docs/reference/engine/classes/Accoutrement.md) - **Property `AttachmentForward`** (`Vector3`): Sets the offset position of the object on the Player. *(hidden)* - **Property `AttachmentPoint`** (`CFrame`): The exact CFrame of the Accoutrement. - **Property `AttachmentPos`** (`Vector3`): Sets the position of the object on the Player. *(hidden)* - **Property `AttachmentRight`** (`Vector3`): Sets the offset position of the object on the Player. *(hidden)* - **Property `AttachmentUp`** (`Vector3`): Sets the offset position of the object on the Player. *(hidden)* ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: HeapProfilerService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service --- # Class: HeapProfilerService ## Methods ### Method: HeapProfilerService:ClientRequestDataAsync **Signature:** `HeapProfilerService:ClientRequestDataAsync(player: Player): string` *Yields · Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Player` | | | **Returns:** `string` ### Method: HeapProfilerService:ServerRequestDataAsync **Signature:** `HeapProfilerService:ServerRequestDataAsync(): string` *Yields · Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `string` ## Events ### Event: HeapProfilerService.OnNewData **Signature:** `HeapProfilerService.OnNewData(player: Player, jsonString: buffer, id: int, compressedLength: int, uncompressedLength: int)` *Security: PluginSecurity* **Parameters:** | Name | Type | Description | |------|------|-------------| | `player` | `Player` | | | `jsonString` | `buffer` | | | `id` | `int` | | | `compressedLength` | `int` | | | `uncompressedLength` | `int` | | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: HeightmapImporterService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - Service - NotReplicated --- # Class: HeightmapImporterService ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: HiddenSurfaceRemovalAsset last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances --- # Class: HiddenSurfaceRemovalAsset ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Highlight last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances summary: "A visual effect which you can use to call attention to a specific object within an experience." --- # Class: Highlight > A visual effect which you can use to call attention to a specific object > within an experience. ## Description The [Highlight](/docs/reference/engine/classes/Highlight.md) instance is a visual effect which you can use to call attention to a specific object within an experience. Every highlight effect has a silhouette **outline** that surrounds the object and a solid overlay **interior** that displays over the object. You can customize both of these components independently to modify the highlight's visual appearance. | _Base object_ | _White outline, 50% red interior_ | _Yellow outline, black interior_ | | --- | --- | --- | Useful applications of the highlight effect include: - Providing visual feedback that an object is important and/or interactable. - Making distant objects visible through objects that are closer to the user. - Indicating the current position and status of other characters. #### Limitations As a performance limit, Studio only displays 255 simultaneous [Highlight](/docs/reference/engine/classes/Highlight.md) instances on the client at a time. If you exceed this limit, the additional instances are silently ignored. Note that while a [Highlight](/docs/reference/engine/classes/Highlight.md) with [Enabled](/docs/reference/engine/classes/Highlight.md) set to `false` doesn't display, it still takes one of the 255 available slots, so if you plan to permanently disable a [Highlight](/docs/reference/engine/classes/Highlight.md) instance, it's best to delete it rather than disable it. ## Properties ### Property: Highlight.Adornee ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` The [Instance](/docs/reference/engine/classes/Instance.md) for which to apply the [Highlight](/docs/reference/engine/classes/Highlight.md), used to apply the effect to an [Instance](/docs/reference/engine/classes/Instance.md) outside of a child/parent relationship. ### Property: Highlight.DepthMode ```json { "type": "HighlightDepthMode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` Controls how the [Highlight](/docs/reference/engine/classes/Highlight.md) effect displays with respect to other objects in the world. You can set this property to one of the following options: - [AlwaysOnTop](/docs/reference/engine/enums/HighlightDepthMode.md) — Allows the highlight to display regardless if there are objects between the camera and the highlighted object. This means the viewer is always able to see the highlight regardless of what is between the highlighted object and the camera. - [Occluded](/docs/reference/engine/enums/HighlightDepthMode.md) — Hides the highlight if there are objects between the camera and the highlighted object. This means the viewer is only able to see the object if there are no obstructing objects between the highlighted object and the camera's view. | _DepthMode = AlwaysOnTop_ | _DepthMode = Occluded_ | | --- | --- | ### Property: Highlight.Enabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` Sets whether or not the highlight is enabled. This does not impact performance, but disabled [Highlight](/docs/reference/engine/classes/Highlight.md) instances will still take one of the 255 available slots. If you plan to permanently disable a [Highlight](/docs/reference/engine/classes/Highlight.md) instance, it's best to delete it rather than disable it. ### Property: Highlight.FillColor ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` Sets the [Color3](/docs/reference/engine/datatypes/Color3.md) value of the highlight's interior. | _FillColor = [255, 100, 50]_ | _FillColor = [0, 255, 125]_ | _FillColor = [75, 150, 255]_ | | --- | --- | --- | ### Property: Highlight.FillTransparency ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` Sets the visibility of the highlight's interior to any value between the default value of **0** (opaque) and **1** (invisible). You can use this property to determine how much of the object's existing color you want viewers to see. | _FillTransparency = 0_ | _FillTransparency = 0.5_ | _FillTransparency = 1_ | | --- | --- | --- | ### Property: Highlight.OutlineColor ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` Sets the [Color3](/docs/reference/engine/datatypes/Color3.md) value of the highlight's outline. | _OutlineColor = [255, 100, 50]_ | _OutlineColor = [0, 255, 125]_ | _OutlineColor = [75, 150, 255]_ | | --- | --- | --- | ### Property: Highlight.OutlineTransparency ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` Sets the visibility of the highlight's outline to a value between **0** (opaque) and **1** (transparent). | _OutlineTransparency = 0_ | _OutlineTransparency = 1_ | | | --- | --- | --- | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: HingeConstraint last_updated: 2026-06-29T19:34:07Z inherits: - Constraint - Instance - Object type: class memory_category: BaseParts summary: "Constrains its attachments to rotate about a single axis." --- # Class: HingeConstraint > Constrains its attachments to rotate about a single axis. ## Description A **HingeConstraint** allows two [Attachments](/docs/reference/engine/classes/Attachment.md) to rotate about one axis, constraining the two [Attachments](/docs/reference/engine/classes/Attachment.md) so that they both occupy the same position and that their **X** axes point in the same direction. Note that if this constraint attaches one part (**A**) to another part (**B**) that is anchored or connected to an anchored part (**Z**), part **A** will not be locally simulated when interacting with a player. When configuring this constraint, it may be helpful to study [Roblox Units](/docs/en-us/physics/units.md) to understand how Roblox units compare to metric units. #### Angular Power Hinges can be configured to actuate rotation. If a hinge's [ActuatorType](/docs/reference/engine/classes/HingeConstraint.md) is set to [Motor](/docs/reference/engine/enums/ActuatorType.md), it attempts to rotate the attachments with the goal of reaching its [AngularVelocity](/docs/reference/engine/classes/HingeConstraint.md). You can further control this rotation through both [MotorMaxAcceleration](/docs/reference/engine/classes/HingeConstraint.md) and [MotorMaxTorque](/docs/reference/engine/classes/HingeConstraint.md). If a hinge's [ActuatorType](/docs/reference/engine/classes/HingeConstraint.md) is set to [Servo](/docs/reference/engine/enums/ActuatorType.md), it attempts to rotate to an angle specified by [TargetAngle](/docs/reference/engine/classes/HingeConstraint.md). This rotation is controlled by both [AngularSpeed](/docs/reference/engine/classes/HingeConstraint.md) and [ServoMaxTorque](/docs/reference/engine/classes/HingeConstraint.md). #### Limits You can set limits to restrict the rotation of a hinge, useful for mechanisms like doors which should only swing open or closed within a set range. Enabling the [LimitsEnabled](/docs/reference/engine/classes/HingeConstraint.md) property exposes the [LowerAngle](/docs/reference/engine/classes/HingeConstraint.md) and [UpperAngle](/docs/reference/engine/classes/HingeConstraint.md) limits, as well as [Restitution](/docs/reference/engine/classes/HingeConstraint.md) which defines the elasticity of the attachments when they reach either limit. ## Properties ### Property: HingeConstraint.ActuatorType ```json { "type": "ActuatorType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Hinge", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Sets whether the rotation of the [HingeConstraint](/docs/reference/engine/classes/HingeConstraint.md) is actuated and, if so, what kind of actuation. - If [ActuatorType](/docs/reference/engine/classes/HingeConstraint.md) is set to [Motor](/docs/reference/engine/enums/ActuatorType.md), the hinge will attempt to rotate the attachments with the goal of reaching [AngularVelocity](/docs/reference/engine/classes/HingeConstraint.md). This rotation is limited by both [MotorMaxAcceleration](/docs/reference/engine/classes/HingeConstraint.md) and [MotorMaxTorque](/docs/reference/engine/classes/HingeConstraint.md). - If [ActuatorType](/docs/reference/engine/classes/HingeConstraint.md) is set to [Servo](/docs/reference/engine/enums/ActuatorType.md), the hinge will attempt to rotate to an angle specified by [TargetAngle](/docs/reference/engine/classes/HingeConstraint.md). This rotation is limited by both [AngularSpeed](/docs/reference/engine/classes/HingeConstraint.md) and [ServoMaxTorque](/docs/reference/engine/classes/HingeConstraint.md). Note that both actuated and free spinning rotation can be limited by setting [LimitsEnabled](/docs/reference/engine/classes/HingeConstraint.md) to true. ### Property: HingeConstraint.AngularResponsiveness ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Servo", "capabilities": [ "Physics" ], "simulationAccess": true } ``` This property specifies the sharpness of the servo motor in reaching the [TargetAngle](/docs/reference/engine/classes/HingeConstraint.md), when [ActuatorType](/docs/reference/engine/classes/HingeConstraint.md) is set to [Servo](/docs/reference/engine/enums/ActuatorType.md). Larger values correspond to a faster response and smaller values results in more damping and a slower response. ### Property: HingeConstraint.AngularSpeed ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Servo", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The desired angular speed a [HingeConstraint](/docs/reference/engine/classes/HingeConstraint.md) with [ActuatorType](/docs/reference/engine/classes/HingeConstraint.md) set to [Servo](/docs/reference/engine/enums/ActuatorType.md) will attempt to maintain while rotating towards its [TargetAngle](/docs/reference/engine/classes/HingeConstraint.md). Measured in radians/second. ### Property: HingeConstraint.AngularVelocity ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Motor", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The angular velocity a [HingeConstraint](/docs/reference/engine/classes/HingeConstraint.md) with [ActuatorType](/docs/reference/engine/classes/HingeConstraint.md) set to [Motor](/docs/reference/engine/enums/ActuatorType.md) will attempt to achieve. Measured in radians/second. ### Property: HingeConstraint.CurrentAngle ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Derived", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The current angle of the [HingeConstraint](/docs/reference/engine/classes/HingeConstraint.md). This angle is calculated by measuring the angle separation of the **Y** axes of the [Attachments](/docs/reference/engine/classes/Attachment.md). ### Property: HingeConstraint.LimitsEnabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Hinge", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Sets whether the [HingeConstraint](/docs/reference/engine/classes/HingeConstraint.md) will limit the range of rotation. If enabled, the constraint will only allow the [CurrentAngle](/docs/reference/engine/classes/HingeConstraint.md) to be between [LowerAngle](/docs/reference/engine/classes/HingeConstraint.md) and [UpperAngle](/docs/reference/engine/classes/HingeConstraint.md). If the [Attachment](/docs/reference/engine/classes/Attachment.md) reach the end of the limited range of rotation then they will stop rotating. If [Restitution](/docs/reference/engine/classes/HingeConstraint.md) is greater than 0 then the attachments will bounce when they hit the ends of the limited range. ### Property: HingeConstraint.LowerAngle ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Limits", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The minimum rotation angle the [HingeConstraint](/docs/reference/engine/classes/HingeConstraint.md) will allow if [LimitsEnabled](/docs/reference/engine/classes/HingeConstraint.md) is true. Measured in degrees. ### Property: HingeConstraint.MotorMaxAcceleration ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Motor", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The maximum angular acceleration a [HingeConstraint](/docs/reference/engine/classes/HingeConstraint.md) with [ActuatorType](/docs/reference/engine/classes/HingeConstraint.md) set to [Motor](/docs/reference/engine/enums/ActuatorType.md) can apply to achieve its [AngularVelocity](/docs/reference/engine/classes/HingeConstraint.md). Measured in radians/second². ### Property: HingeConstraint.MotorMaxTorque ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Motor", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The maximum torque a [HingeConstraint](/docs/reference/engine/classes/HingeConstraint.md) with [ActuatorType](/docs/reference/engine/classes/HingeConstraint.md) set to [Motor](/docs/reference/engine/enums/ActuatorType.md) can apply when trying to reach its desired [AngularVelocity](/docs/reference/engine/classes/HingeConstraint.md). ### Property: HingeConstraint.Radius ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The visualized radius of the [HingeConstraint](/docs/reference/engine/classes/HingeConstraint.md). ### Property: HingeConstraint.Restitution ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Limits", "capabilities": [ "Physics" ], "simulationAccess": true } ``` How elastic [Attachment](/docs/reference/engine/classes/Attachment.md) connected by a [HingeConstraint](/docs/reference/engine/classes/HingeConstraint.md) will be when they reach the end of the range when [LimitsEnabled](/docs/reference/engine/classes/HingeConstraint.md) is true. Constrained between 0 and 1. ### Property: HingeConstraint.ServoMaxTorque ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Servo", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The maximum torque a [HingeConstraint](/docs/reference/engine/classes/HingeConstraint.md) with [ActuatorType](/docs/reference/engine/classes/HingeConstraint.md) set to [Servo](/docs/reference/engine/enums/ActuatorType.md) can apply when trying to reach its desired [TargetAngle](/docs/reference/engine/classes/HingeConstraint.md). ### Property: HingeConstraint.TargetAngle ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Servo", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The target angle a [HingeConstraint](/docs/reference/engine/classes/HingeConstraint.md) will attempt to rotate to if its [ActuatorType](/docs/reference/engine/classes/HingeConstraint.md) is set to [Servo](/docs/reference/engine/enums/ActuatorType.md). Measured in degrees. ### Property: HingeConstraint.UpperAngle ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Limits", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The maximum rotation angle the [HingeConstraint](/docs/reference/engine/classes/HingeConstraint.md) will allow if [LimitsEnabled](/docs/reference/engine/classes/HingeConstraint.md) is true. Measured in degrees. ### Property: HingeConstraint.SoftlockServoUponReachingTarget ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Servo", "capabilities": [ "Physics" ], "simulationAccess": true } ``` > **Deprecated:** This property should not be used in new work. ## Inherited Members ### From [Constraint](/docs/reference/engine/classes/Constraint.md) - **Property `Active`** (`boolean`): Indicates if the constraint is currently active in the world. - **Property `Attachment0`** (`Attachment`): The Attachment that is connected to - **Property `Attachment1`** (`Attachment`): The Attachment that is connected to - **Property `Color`** (`BrickColor`): The color of the constraint. - **Property `Enabled`** (`boolean`): Toggles whether or not the constraint is enabled. - **Property `Visible`** (`boolean`): Toggles the constraint's visibility. - **Method `GetDebugAppliedForce(bodyId: int): Vector3`**: *(deprecated)* - **Method `GetDebugAppliedTorque(bodyId: int): Vector3`**: *(deprecated)* ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Hint last_updated: 2026-06-29T19:34:07Z inherits: - Message - Instance - Object type: class memory_category: Instances tags: - Deprecated summary: "A Hint is an object that creates a small black bar at the very top of the screen with text." --- # Class: Hint > A Hint is an object that creates a small black bar at the very top of the > screen with text. ## Description A Hint is an object that creates a small black bar at the very top of the screen with text. Its appearance cannot be customized in any way. Notes: - When a Hint is placed in the [Workspace](/docs/reference/engine/classes/Workspace.md), it will be visible to everyone - When placed under a player's [PlayerGui](/docs/reference/engine/classes/PlayerGui.md), it will be visible only to that player - Hints will render if placed in the [CoreGui](/docs/reference/engine/classes/CoreGui.md) ## Inherited Members ### From [Message](/docs/reference/engine/classes/Message.md) - **Property `Text`** (`string`): Sets the text of a Message or Hint. ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Hole last_updated: 2026-06-29T19:34:07Z inherits: - Feature - Instance - Object type: class memory_category: Instances tags: - Deprecated --- # Class: Hole ## Description A Hole is an unused type of surface joint. It can be connected to a [MotorFeature](/docs/reference/engine/classes/MotorFeature.md) object by using a [VelocityMotor](/docs/reference/engine/classes/VelocityMotor.md). ## Inherited Members ### From [Feature](/docs/reference/engine/classes/Feature.md) - **Property `FaceId`** (`NormalId`): Sets what side of the Parent the object is on. - **Property `InOut`** (`InOut`): Controls how the Feature is positioned on it's parent's surface, in - **Property `LeftRight`** (`LeftRight`): Controls whether the feature is shifted to the left, center, or right on - **Property `TopBottom`** (`TopBottom`): Controls whether the feature is shifted to the top, center, or bottom on ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Hopper last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - Deprecated summary: "Hopper is the original name of the StarterPack service." --- # Class: Hopper > Hopper is the original name of the StarterPack service. ## Description If this service is ever instantiated, it moves all of its children into the [StarterPack](/docs/reference/engine/classes/StarterPack.md) and then deletes itself. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: HopperBin last_updated: 2026-06-29T19:34:07Z inherits: - BackpackItem - Model - PVInstance - Instance - Object type: class memory_category: BaseParts tags: - Deprecated --- # Class: HopperBin ## Description HopperBins are an outdated system for creating tools that can be used by a player. In place of HopperBins, please use [Tool](/docs/reference/engine/classes/Tool.md) instead. Historically, only HopperBins worked without a 'Handle' [Part](/docs/reference/engine/classes/Part.md), but this is no longer the case thanks to the [Tool.RequiresHandle](/docs/reference/engine/classes/Tool.md) property of Tools. ## Properties ### Property: HopperBin.Active ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Input" ] } ``` ### Property: HopperBin.BinType ```json { "type": "BinType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Input" ] } ``` ## Events ### Event: HopperBin.Deselected **Signature:** `HopperBin.Deselected()` *Security: None · Capabilities: Input* ### Event: HopperBin.Selected **Signature:** `HopperBin.Selected(mouse: Instance)` *Security: None · Capabilities: Input* **Parameters:** | Name | Type | Description | |------|------|-------------| | `mouse` | `Instance` | | ## Inherited Members ### From [BackpackItem](/docs/reference/engine/classes/BackpackItem.md) - **Property `TextureContent`** (`Content`): The texture icon that is displayed for a tool in the player's backpack. - **Property `TextureId`** (`ContentId`): The texture icon that is displayed for a tool in the player's backpack. ### From [Model](/docs/reference/engine/classes/Model.md) - **Property `LevelOfDetail`** (`ModelLevelOfDetail`): Sets the level of detail on the model for experiences with instance - **Property `ModelStreamingMode`** (`ModelStreamingMode`): Controls the model streaming behavior on Models when - **Property `PrimaryPart`** (`BasePart`): The primary part of the Model, or `nil` if not explicitly set. - **Property `Scale`** (`float`): Editor-only property used to scale the model around its pivot. Setting - **Property `WorldPivot`** (`CFrame`): Determines where the pivot of a Model which does **not** have a - **Method `AddPersistentPlayer(playerInstance?: Player): ()`**: Sets this model to be persistent for the specified player. - **Method `BreakJoints(): ()`**: Breaks connections between `BaseParts`, including surface connections with *(deprecated)* - **Method `breakJoints(): ()`**: *(deprecated)* - **Method `GetBoundingBox(): Tuple`**: Returns a description of a volume that contains all parts of a Model. - **Method `GetExtentsSize(): Vector3`**: Returns the size of the smallest bounding box that contains all of the - **Method `GetModelCFrame(): CFrame`**: This value historically returned the CFrame of a central position in the *(deprecated)* - **Method `GetModelSize(): Vector3`**: Returns the Vector3 size of the Model. *(deprecated)* - **Method `GetPersistentPlayers(): List`**: Returns all the Player objects that this model object is - **Method `GetPrimaryPartCFrame(): CFrame`**: Returns the CFrame of the model's Model.PrimaryPart. *(deprecated)* - **Method `GetScale(): float`**: Returns the canonical scale of the model, which defaults to 1 for newly - **Method `MakeJoints(): ()`**: Goes through all BaseParts in the Model. If any *(deprecated)* - **Method `makeJoints(): ()`**: *(deprecated)* - **Method `move(location: Vector3): ()`**: *(deprecated)* - **Method `MoveTo(position: Vector3): ()`**: Moves the PrimaryPart to the given position. If - **Method `moveTo(location: Vector3): ()`**: *(deprecated)* - **Method `RemovePersistentPlayer(playerInstance?: Player): ()`**: Makes this model no longer persistent for the specified player. - **Method `ResetOrientationToIdentity(): ()`**: Resets the rotation of the model's parts to the previously set identity *(deprecated)* - **Method `ScaleTo(newScaleFactor: float): ()`**: Sets the scale factor of the model, adjusting the sizing and location of - **Method `SetIdentityOrientation(): ()`**: Sets the identity rotation of the given model, allowing you to reset the *(deprecated)* - **Method `SetPrimaryPartCFrame(cframe: CFrame): ()`**: Sets the BasePart.CFrame of the model's Model.PrimaryPart. *(deprecated)* - **Method `TranslateBy(delta: Vector3): ()`**: Shifts a Model by the given Vector3 offset, preserving ### From [PVInstance](/docs/reference/engine/classes/PVInstance.md) - **Property `Origin`** (`CFrame`): - **Property `Pivot Offset`** (`CFrame`): - **Method `GetPivot(): CFrame`**: Gets the pivot of a PVInstance. - **Method `PivotTo(targetCFrame: CFrame): ()`**: Transforms the PVInstance along with all of its descendant ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: HttpRbxApiService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service summary: "An internal service whose functionality is not available to developers." --- # Class: HttpRbxApiService > An internal service whose functionality is not available to developers. ## Description A version of the [HttpService](/docs/reference/engine/classes/HttpService.md) used by the admins. Unlike the regular service, this one can send GET/POST requests to roblox.com ## Methods ### Method: HttpRbxApiService:RequestLimitedAsync **Signature:** `HttpRbxApiService:RequestLimitedAsync(requestOptions: Dictionary, priority?: ThrottlingPriority, content_type?: HttpContentType, httpRequestType?: HttpRequestType): string` *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Network* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `requestOptions` | `Dictionary` | | | | `priority` | `ThrottlingPriority` | `Default` | | | `content_type` | `HttpContentType` | `ApplicationJson` | | | `httpRequestType` | `HttpRequestType` | `Default` | | **Returns:** `string` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: HttpService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service summary: "Allows sending HTTP requests and provides various web-related and JSON methods." --- # Class: HttpService > Allows sending HTTP requests and provides various web-related and JSON > methods. ## Description `HttpService` allows HTTP requests to be sent from experience servers using [RequestAsync](/docs/reference/engine/classes/HttpService.md), [GetAsync](/docs/reference/engine/classes/HttpService.md) and [PostAsync](/docs/reference/engine/classes/HttpService.md). This service allows experiences to be integrated with third-party web services such as analytics, data storage, remote server configuration, error reporting, advanced calculations, or real-time communication. Additionally, it can call a subset of the Open Cloud APIs. For more information about these use cases, see [In-experience HTTP requests](/docs/en-us/cloud-services/http-service.md). `HttpService` also houses the [JSONEncode](/docs/reference/engine/classes/HttpService.md) and [JSONDecode](/docs/reference/engine/classes/HttpService.md) methods, which are useful for communicating with services that use the [JSON](https://json.org) format. In addition, the [GenerateGUID](/docs/reference/engine/classes/HttpService.md) method provides random 128‑bit labels which can be treated as probabilistically unique in a variety of scenarios. Within Studio, use [CreateWebStreamClient()](/docs/reference/engine/classes/HttpService.md) to process data in real time from servers that support streaming protocols such as [SSE](https://en.wikipedia.org/wiki/Server-sent_events), [chunked transfer encoding](https://en.wikipedia.org/wiki/Chunked_transfer_encoding), and [WebSockets](https://en.wikipedia.org/wiki/WebSocket). You can connect callback functions to stream events, allowing you to process data immediately as it arrives instead of waiting for the entire response to complete. Only send HTTP requests to trusted third-party platforms to avoid introducing unnecessary security risks to your experience. ## Code Samples **Astronauts in Space** This code sample uses HttpService's GetAsync to make a request to [Open Notify](http://open-notify.org), a web service that provides data from NASA. The request is made to an endpoint that provides information on how many astronauts are currently in space. The response is provided in JSON format, so it is parsed using JSONDecode. Finally, the response data is then processed and printed to the Output. Test this script by pasting the source code into a Script (HttpService cannot be used by LocalScripts). Also, be sure to enable HTTP Requests in your Game Settings (Home > Game Settings). ```lua local HttpService = game:GetService("HttpService") local URL_ASTROS = "http://api.open-notify.org/astros.json" -- Make the request to our endpoint URL local response = HttpService:GetAsync(URL_ASTROS) -- Parse the JSON response local data = HttpService:JSONDecode(response) -- Information in the data table is dependent on the response JSON if data.message == "success" then print("There are currently " .. data.number .. " astronauts in space:") for i, person in pairs(data.people) do print(i .. ": " .. person.name .. " is on " .. person.craft) end end ``` **Where is the International Space Station?** This code sample uses HttpService's GetAsync to make a request to an endpoint at [Open Notify](http://open-notify.org/Open-Notify-API/ISS-Location-Now/), a website that provides information from NASA. The endpoint provides information on the current location of the International Space Station. This example uses a **defensive coding technique** that you should use when making web requests. It wraps the call to GetAsync and JSONDecode in pcall, which protects our script from raising an error if either of these fail. Then, it checks the raw response for all proper data before using it. All of this is put inside a function that returns true or false depending of the request's success. Whenever you're working with web requests, you should prepare for anything to go wrong. Perhaps your web endpoint changes or goes down - you don't want your game scripts raising errors and breaking your game. You want to handle both success **and failure** gracefully - have a plan in case your data is not available. Use `pcall` and make plenty of validity checks (if statements) on your data to make sure you're getting exactly what you expect. ```lua local HttpService = game:GetService("HttpService") -- Where is the International Space Station right now? local URL_ISS = "http://api.open-notify.org/iss-now.json" local function printISS() local response local data -- Use pcall in case something goes wrong pcall(function() response = HttpService:GetAsync(URL_ISS) data = HttpService:JSONDecode(response) end) -- Did our request fail or our JSON fail to parse? if not data then return false end -- Fully check our data for validity. This is dependent on what endpoint you're -- to which you're sending your requests. For this example, this endpoint is -- described here: http://open-notify.org/Open-Notify-API/ISS-Location-Now/ if data.message == "success" and data.iss_position then if data.iss_position.latitude and data.iss_position.longitude then print("The International Space Station is currently at:") print(data.iss_position.latitude .. ", " .. data.iss_position.longitude) return true end end return false end if printISS() then print("Success") else print("Something went wrong") end ``` **New Pastebin Post** Pastebin.com is a website that allows users to paste text (usually source code) for others to view publicly. This code sample uses HttpService PostAsync and the pastebin web API to automatically create a new public paste on the website. Since pastebin's API is designed to take data in as a URL encoded string, the code uses a for-loop to turn the `dataFields` table into a URL encoded string, such as hello=world&foo=bar. This is used as the HTTP POST data. Test this code by first going to [pastebin.com/api#1](https://pastebin.com/api#1) and getting an API key (you'll need a pastebin account to do this). Then, paste your unique developer API key into the field `api_dev_key` in the code sample's `dataFields` table. Fill in any other information about the post you want to make, then run this code in a Script (not a LocalScript). If all goes well, you'll get a URL to your new paste in the Output window (or some error string from pastebin). ```lua local HttpService = game:GetService("HttpService") local URL_PASTEBIN_NEW_PASTE = "https://pastebin.com/api/api_post.php" local dataFields = { -- Pastebin API developer key from -- https://pastebin.com/api#1 ["api_dev_key"] = "FILL THIS WITH YOUR API DEVELOPER KEY", ["api_option"] = "paste", -- keep as "paste" ["api_paste_name"] = "HttpService:PostAsync", -- paste name ["api_paste_code"] = "Hello, world", -- paste content ["api_paste_format"] = "text", -- paste format ["api_paste_expire_date"] = "10M", -- expire date ["api_paste_private"] = "0", -- 0=public, 1=unlisted, 2=private ["api_user_key"] = "", -- user key, if blank post as guest } -- The pastebin API uses a URL encoded string for post data -- Other APIs might use JSON, XML or some other format local data = "" for k, v in pairs(dataFields) do data = data .. ("&%s=%s"):format(HttpService:UrlEncode(k), HttpService:UrlEncode(v)) end data = data:sub(2) -- Remove the first & -- Here's the data we're sending print(data) -- Make the request local response = HttpService:PostAsync(URL_PASTEBIN_NEW_PASTE, data, Enum.HttpContentType.ApplicationUrlEncoded, false) -- The response will be the URL to the new paste (or an error string if something was wrong) print(response) ``` **Open Cloud via HttpService** This code sample demonstrates sending a single HTTP PATCH request with JSON data to the [Open Cloud Update Group Membership](/docs/en-us/cloud/reference/GroupMembership.md#Cloud_UpdateGroupMembership) endpoint. Every Open Cloud endpoint requires adding your [API key](/docs/en-us/cloud/auth/api-keys.md#create-api-keys) to the `x-api-key` header to receive a successful response. The generated API key must be stored as a [Secret](/docs/reference/engine/datatypes/Secret.md) that can later be retrieved with [HttpService:GetSecret("API KEY SECRET NAME")](/docs/reference/engine/classes/HttpService.md). To learn more about calling Open Cloud endpoints via `HttpService`, see [In-experience HTTP requests](/docs/en-us/cloud-services/http-service.md). ```lua -- Remember to set enable HTTP Requests in experience settings! local HttpService = game:GetService("HttpService") local groupId = "your_group_id" local membershipId = "your_membership_id" local roleId = "your_role_id" local function request() local response = HttpService:RequestAsync({ Url = `https://apis.roblox.com/cloud/v2/groups/{groupId}/memberships/{membershipId}`, -- Updates a user's group membership Method = "PATCH", Headers = { ["Content-Type"] = "application/json", -- When sending JSON, set this! ["x-api-key"] = HttpService:GetSecret("APIKey"), -- Set in Creator Hub }, Body = HttpService:JSONEncode({ role = `groups/{groupId}/roles/{roleId}` }), }) if response.Success then print("The response was successful:", response.StatusCode, response.StatusMessage) else print("The response returned an error:", response.StatusCode, response.StatusMessage) end print("Response body:\n", response.Body) print("Response headers:\n", HttpService:JSONEncode(response.Headers)) end -- Remember to wrap the function in a 'pcall' to prevent the script from breaking if the request fails local success, message = pcall(request) if not success then print("The Http request failed to send:", message) end ``` ## Properties ### Property: HttpService.HttpEnabled ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "None", "write": "LocalUserSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Network" ] } ``` When set to `true`, allows scripts to send requests to websites using [HttpService:GetAsync()](/docs/reference/engine/classes/HttpService.md), [HttpService:PostAsync()](/docs/reference/engine/classes/HttpService.md), and [HttpService:RequestAsync()](/docs/reference/engine/classes/HttpService.md). This property must be toggled on through the [Experience Settings](/docs/en-us/studio/experience-settings.md) interface in Studio, or for **unpublished** experiences by setting this property to `true` using the [Command Bar](/docs/en-us/studio/ui-overview.md#command-bar): `game:GetService("HttpService").HttpEnabled = true` ## Methods ### Method: HttpService:CreateWebStreamClient **Signature:** `HttpService:CreateWebStreamClient(streamClientType: WebStreamClientType, requestOptions: Dictionary): WebStreamClient` This method creates a client that establishes a long-lived connection to servers utilizing various streaming technologies, such as [Server-Sent Events (SSE)](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events). After the connection is established, the client fires signals that you can connect callback functions to with [RBXScriptConnection](/docs/reference/engine/datatypes/RBXScriptConnection.md). Use these callbacks to process messages as soon as they arrive, as well as respond to open, close, and error events. There is a limit of six total clients allowed at one time. Close streams that you no longer need with [WebStreamClient:Close()](/docs/reference/engine/classes/WebStreamClient.md). When the stream is no longer needed, you should disconnect any associated [RBXScriptConnections](/docs/reference/engine/datatypes/RBXScriptConnection.md) to avoid memory leaks. This method is available in Studio only. If you use it inside scripts, make sure to remove any references before publishing the experience. We encourage you to create plugins with this feature for reusability and ease of use. *Security: None · Thread Safety: Unsafe · Capabilities: Network* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `streamClientType` | `WebStreamClientType` | | The type of streaming connection to intiialize the client with. | | `requestOptions` | `Dictionary` | | A dictionary containing information to be requested from the server. It is identical to `requestOptions` in [HttpService:RequestAsync()](/docs/reference/engine/classes/HttpService.md). | **Returns:** `WebStreamClient` — A stateful client that emits events in the stream lifecycle. **HttpService CreateWebStreamClient SSE** This code sample uses `CreateWebStreamClient` to create a SSE client and connects a function to each event that the client fires, allowing the client to process messages from the Google Gemini API as they arrive. ```lua local HttpService = game:GetService("HttpService") local URL_GEMINI_SSE = "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:streamGenerateContent?alt=sse&key=" local geminiSecret = HttpService:GetSecret("gemini_secret") -- Assumes you have already uploaded your Gemini API key as a local secret local url_with_secret = geminiSecret:AddPrefix(URL_GEMINI_SSE) -- Remember that plugins can't access local secrets! local function handleOpen(responseStatusCode, headers) print("Stream opened with response code:", responseStatusCode) end local function handleMessage(message) -- Parse events depending on the stream type print("Stream message received:", message) end local function request() local sse_client = HttpService:CreateWebStreamClient(Enum.WebStreamClientType.RawStream, { Url = url_with_secret, Method = "POST", Headers = { ["Content-Type"] = "application/json", -- When sending JSON, set this! }, Body = HttpService:JSONEncode({ contents = { parts = { text ="How does the SSE protocol work?" }}}), }) local openConnection = sse_client.Opened:Connect(handleOpen) local messageConnection = sse_client.MessageReceived:Connect(handleMessage) sse_client.Closed:Wait() openConnection:Disconnect() messageConnection:Disconnect() end -- Remember to wrap the function in a 'pcall' to prevent the script from breaking if the request fails local success, message = pcall(request) if not success then print("WebStreamClient failed:", message) end ``` **HttpService CreateWebStreamClient RawStream** This code sample uses `CreateWebStreamClient()` to create a `RawStream` client and connects a function to each event that the client fires, allowing the client to process messages from the locally running Llama 3.3 LLM server as they arrive. ```lua local HttpService = game:GetService("HttpService") local LOCAL_LLM_PORT = "http://localhost:11434/api/generate?stream=true" local function handleMessage(message) -- Parse events depending on the stream type -- If response Content-Type header is JSON, you can decode it like this: local json = HttpService:JSONDecode(message) end local function handleError(responseStatusCode, errorMessage) print("Stream error with response code:", responseStatusCode) end local function request() local sse_client = HttpService:CreateWebStreamClient(Enum.WebStreamClientType.RawStream, { -- Llama3.2 LLM server running locally using Ollama Url = LOCAL_LLM_PORT, Method = "POST", Headers = { ["Content-Type"] = "application/json", -- When sending JSON, set this! }, Body = HttpService:JSONEncode({ model="llama3.2", prompt ="Tell me a funny joke"}), }) local messageConnection = sse_client.MessageReceived:Connect(handleMessage) local errorConnection = sse_client.Error:Connect(handleError) sse_client.Closed:Wait() messageConnection:Disconnect() errorConnection:Disconnect() sse_client:close() end -- Remember to wrap the function in a 'pcall' to prevent the script from breaking if the request fails local success, message = pcall(request) if not success then print("WebStreamClient failed:", message) end ``` ### Method: HttpService:GenerateGUID **Signature:** `HttpService:GenerateGUID(wrapInCurlyBraces?: boolean): string` This method generates a **random universally unique identifier** ([UUID](https://en.wikipedia.org/wiki/Universally_unique_identifier)) string. The sixteen octets of a UUID are represented as 32 hexadecimal (base 16) digits, displayed in five groups separated by hyphens in the form `8-4-4-4-12` for a total of 36 characters, for example `123e4567-e89b-12d3-a456-426655440000`. The UUID specification used is [Version 4 (random)](), variant 1 (DCE 1.1, ISO/IEC 11578:1996). UUIDs of this version are the most commonly used due to their simplicity, as they are entirely randomly generated. Note that this version does not have certain features that other UUID versions have, such as encoded timestamps, MAC addresses, or time-based sorting like [UUIDv7](https://uuid7.com/) or [ULID](https://github.com/ulid/spec). There are over 5.3×1036 unique v4 UUIDs, in which the probability of finding a duplicate within 103 trillion UUIDs is one in a billion. The `wrapInCurlyBraces` argument determines whether the returned string is wrapped in curly braces (`{}`). For instance: - `true`: `{94b717b2-d54f-4340-a504-bd809ef5bf5c}` - `false`: `db454790-7563-44ed-ab4b-397ff5df737b` *Security: None · Thread Safety: Safe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `wrapInCurlyBraces` | `boolean` | `true` | Whether the returned string should be wrapped in curly braces (`{}`). | **Returns:** `string` — The randomly generated UUID. **HttpService GenerateGUID** This example uses HttpService's GenerateGUID method to generate and print a universally unique identifier. ```lua local HttpService = game:GetService("HttpService") local result = HttpService:GenerateGUID(true) print(result) --> Example output: {4c50eba2-d2ed-4d79-bec1-02a967f49c58} ``` ### Method: HttpService:GetAsync **Signature:** `HttpService:GetAsync(url: Variant, nocache?: boolean, headers: Variant): string` This method sends an HTTP `GET` request. It functions similarly to [RequestAsync()](/docs/reference/engine/classes/HttpService.md) except that it accepts HTTP request parameters as method parameters instead of a single dictionary and returns only the body of the HTTP response. Generally, this method is useful only as a shorthand and [RequestAsync()](/docs/reference/engine/classes/HttpService.md) should be used in most cases. When `true`, the `nocache` parameter prevents this method from caching results from previous calls with the same `url`. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Network* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `url` | `Variant` | | The web address you are requesting data from. | | `nocache` | `boolean` | `false` | Whether the request stores (caches) the response. | | `headers` | `Variant` | | Used to specify some HTTP request headers. | **Returns:** `string` — The GET request's response body. **Astronauts in Space** This code sample uses HttpService's GetAsync to make a request to [Open Notify](http://open-notify.org), a web service that provides data from NASA. The request is made to an endpoint that provides information on how many astronauts are currently in space. The response is provided in JSON format, so it is parsed using JSONDecode. Finally, the response data is then processed and printed to the Output. Test this script by pasting the source code into a Script (HttpService cannot be used by LocalScripts). Also, be sure to enable HTTP Requests in your Game Settings (Home > Game Settings). ```lua local HttpService = game:GetService("HttpService") local URL_ASTROS = "http://api.open-notify.org/astros.json" -- Make the request to our endpoint URL local response = HttpService:GetAsync(URL_ASTROS) -- Parse the JSON response local data = HttpService:JSONDecode(response) -- Information in the data table is dependent on the response JSON if data.message == "success" then print("There are currently " .. data.number .. " astronauts in space:") for i, person in pairs(data.people) do print(i .. ": " .. person.name .. " is on " .. person.craft) end end ``` **Where is the International Space Station?** This code sample uses HttpService's GetAsync to make a request to an endpoint at [Open Notify](http://open-notify.org/Open-Notify-API/ISS-Location-Now/), a website that provides information from NASA. The endpoint provides information on the current location of the International Space Station. This example uses a **defensive coding technique** that you should use when making web requests. It wraps the call to GetAsync and JSONDecode in pcall, which protects our script from raising an error if either of these fail. Then, it checks the raw response for all proper data before using it. All of this is put inside a function that returns true or false depending of the request's success. Whenever you're working with web requests, you should prepare for anything to go wrong. Perhaps your web endpoint changes or goes down - you don't want your game scripts raising errors and breaking your game. You want to handle both success **and failure** gracefully - have a plan in case your data is not available. Use `pcall` and make plenty of validity checks (if statements) on your data to make sure you're getting exactly what you expect. ```lua local HttpService = game:GetService("HttpService") -- Where is the International Space Station right now? local URL_ISS = "http://api.open-notify.org/iss-now.json" local function printISS() local response local data -- Use pcall in case something goes wrong pcall(function() response = HttpService:GetAsync(URL_ISS) data = HttpService:JSONDecode(response) end) -- Did our request fail or our JSON fail to parse? if not data then return false end -- Fully check our data for validity. This is dependent on what endpoint you're -- to which you're sending your requests. For this example, this endpoint is -- described here: http://open-notify.org/Open-Notify-API/ISS-Location-Now/ if data.message == "success" and data.iss_position then if data.iss_position.latitude and data.iss_position.longitude then print("The International Space Station is currently at:") print(data.iss_position.latitude .. ", " .. data.iss_position.longitude) return true end end return false end if printISS() then print("Success") else print("Something went wrong") end ``` ### Method: HttpService:GetSecret **Signature:** `HttpService:GetSecret(key: string): Secret` This method returns a value previously added to the secrets store for the experience. The secret content is not printable and not available when the experience runs locally. The returned [Secret](/docs/reference/engine/datatypes/Secret.md) can be transformed using built-in methods such as [Secret:AddPrefix()](/docs/reference/engine/datatypes/Secret.md). It is expected to be sent as a part of an HTTP request. For more information, see the [usage guide](/docs/en-us/cloud-services/secrets.md). *Security: None · Thread Safety: Safe · Capabilities: Network* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | | **Returns:** `Secret` ### Method: HttpService:JSONDecode **Signature:** `HttpService:JSONDecode(input: string): Variant` This method transforms a JSON object or array into a Luau [table](/docs/en-us/luau/tables.md) with the following characteristics: - Keys of the table are strings or numbers but not both. If a JSON object contains both, string keys are ignored. - An empty JSON object generates an empty Luau table (`{}`). - If the `input` string is not a valid JSON object, this method will throw an error. To encode a Luau table into a JSON object, use the [HttpService:JSONEncode()](/docs/reference/engine/classes/HttpService.md) method. This method can be used regardless of whether HTTP requests are enabled. *Security: None · Thread Safety: Safe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `input` | `string` | | The JSON object being decoded. | **Returns:** `Variant` — The decoded JSON object as a Luau table. **HttpService JSONDecode** This code sample gives an example [JSON](https://www.json.org/) format string and parses it using HttpService's JSONDecode. It then verifies that the JSON was parsed correctly, and prints out some of the information within the object. Try editing the JSON string to experiment with the format. Also experiment with inspecting the data in Lua to get comfortable with the Lua representation of the data (tables and other values). ```lua local HttpService = game:GetService("HttpService") local jsonString = [[ { "message": "success", "info": { "points": 120, "isLeader": true, "user": { "id": 12345, "name": "JohnDoe" }, "past_scores": [50, 42, 95], "best_friend": null } } ]] local data = HttpService:JSONDecode(jsonString) if data.message == "success" then -- Since tab["hello"] and tab.hello are equivalent, -- you could also use data["info"]["points"] here: print("I have " .. data.info.points .. " points") if data.info.isLeader then print("I am the leader") end print("I have " .. #data.info.past_scores .. " past scores") print("All the information:") for key, value in pairs(data.info) do print(key, typeof(value), value) end end ``` ### Method: HttpService:JSONEncode **Signature:** `HttpService:JSONEncode(input: Variant): string` This method transforms a Luau [table](/docs/en-us/luau/tables.md) into a JSON object or array based on the following guidelines: - Keys of the table must be either strings or numbers. If a table contains both, an array takes priority (string keys are ignored). - An empty Luau table (`{}`) generates an empty JSON array (e.g. `[]`). - Whether passing a dictionary table or a numerically indexed table, avoid `nil` values for any index. - Cyclic table references cause an error. This method allows values such as `inf` and `nan` which are not valid JSON. This may cause problems if you want to use the outputted JSON elsewhere. This method also accepts [buffers](/docs/reference/engine/globals/buffer.md) up to 50 MiB, which it encodes to base64 (and often compresses) before converting to a JSON object. To reverse the encoding process and decode a JSON object, use the [HttpService:JSONDecode()](/docs/reference/engine/classes/HttpService.md) method. This method can be used regardless of whether HTTP requests are enabled. *Security: None · Thread Safety: Safe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `input` | `Variant` | | The input Luau table. | **Returns:** `string` — The returned JSON string. **HttpService JSONEncode** This code sample turns a Lua table `tab` into a [JSON string](https://www.json.org/) using HttpService's JSONEncode. Then, it prints out the string. Try editing the Lua table to see how the JSON output changes. ```lua local HttpService = game:GetService("HttpService") local tab = { -- Remember: these lines are equivalent --["message"] = "success", message = "success", info = { points = 123, isLeader = true, user = { id = 12345, name = "JohnDoe", }, past_scores = { 50, 42, 95 }, best_friend = nil, }, } local json = HttpService:JSONEncode(tab) print(json) ``` ### Method: HttpService:PostAsync **Signature:** `HttpService:PostAsync(url: Variant, data: string, content_type?: HttpContentType, compress?: boolean, headers: Variant): string` This method sends an HTTP `POST` request. It functions similarly to [RequestAsync()](/docs/reference/engine/classes/HttpService.md) except that it accepts HTTP request parameters as method parameters instead of a single dictionary and returns only the body of the HTTP response. Generally, this method is useful only as a shorthand and [RequestAsync()](/docs/reference/engine/classes/HttpService.md) should be used in most cases. When `true`, the `compress` parameter controls whether large request bodies will be compressed using **gzip**. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Network* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `url` | `Variant` | | The destination address for the data. | | `data` | `string` | | The data being sent. | | `content_type` | `HttpContentType` | `ApplicationJson` | Modifies the value in the `Content-Type` header sent with the request. | | `compress` | `boolean` | `false` | Determines whether the data is compressed (**gzipped**) when sent. | | `headers` | `Variant` | | Used to specify some HTTP request headers. | **Returns:** `string` — The HTTP response sent back indicating the request result. **New Pastebin Post** Pastebin.com is a website that allows users to paste text (usually source code) for others to view publicly. This code sample uses HttpService PostAsync and the pastebin web API to automatically create a new public paste on the website. Since pastebin's API is designed to take data in as a URL encoded string, the code uses a for-loop to turn the `dataFields` table into a URL encoded string, such as hello=world&foo=bar. This is used as the HTTP POST data. Test this code by first going to [pastebin.com/api#1](https://pastebin.com/api#1) and getting an API key (you'll need a pastebin account to do this). Then, paste your unique developer API key into the field `api_dev_key` in the code sample's `dataFields` table. Fill in any other information about the post you want to make, then run this code in a Script (not a LocalScript). If all goes well, you'll get a URL to your new paste in the Output window (or some error string from pastebin). ```lua local HttpService = game:GetService("HttpService") local URL_PASTEBIN_NEW_PASTE = "https://pastebin.com/api/api_post.php" local dataFields = { -- Pastebin API developer key from -- https://pastebin.com/api#1 ["api_dev_key"] = "FILL THIS WITH YOUR API DEVELOPER KEY", ["api_option"] = "paste", -- keep as "paste" ["api_paste_name"] = "HttpService:PostAsync", -- paste name ["api_paste_code"] = "Hello, world", -- paste content ["api_paste_format"] = "text", -- paste format ["api_paste_expire_date"] = "10M", -- expire date ["api_paste_private"] = "0", -- 0=public, 1=unlisted, 2=private ["api_user_key"] = "", -- user key, if blank post as guest } -- The pastebin API uses a URL encoded string for post data -- Other APIs might use JSON, XML or some other format local data = "" for k, v in pairs(dataFields) do data = data .. ("&%s=%s"):format(HttpService:UrlEncode(k), HttpService:UrlEncode(v)) end data = data:sub(2) -- Remove the first & -- Here's the data we're sending print(data) -- Make the request local response = HttpService:PostAsync(URL_PASTEBIN_NEW_PASTE, data, Enum.HttpContentType.ApplicationUrlEncoded, false) -- The response will be the URL to the new paste (or an error string if something was wrong) print(response) ``` ### Method: HttpService:RequestAsync **Signature:** `HttpService:RequestAsync(requestOptions: Dictionary): Dictionary` This method sends an HTTP request using a dictionary to specify the request data, such as the target URL, method, headers, and request body data. It returns a dictionary that describes the response data received. Optionally, the request can be compressed using [HttpCompression](/docs/reference/engine/enums/HttpCompression.md). ##### Request dictionary fields | Name | Type | Required | Description | | --- | --- | --- | --- | | `Url` | String | yes | The target URL for this request. Must use `http` or `https` protocols. | | `Method` | String | no | The HTTP method being used by this request, most often `GET` or `POST`. | | `Headers` | Dictionary | no | A dictionary of headers to be used with this request. Most HTTP headers are accepted here, but not all. | | `Body` | String | no | The request body. Can be any string, including binary data. Must be excluded when using the `GET` or `HEAD` HTTP methods. It might be necessary to specify the `Content-Type` header when sending JSON or other formats. | | `Compress` | [HttpCompression](/docs/reference/engine/enums/HttpCompression.md) | no | An optional compression field that will compress the data in the request. The value can either be [HttpCompression.None](/docs/reference/engine/enums/HttpCompression.md) or [HttpCompression.Gzip](/docs/reference/engine/enums/HttpCompression.md). | | `Timeout` | Integer | no | An optional timeout value in seconds to make requests time out more quickly. Values must be greater than zero and no greater than the default request timeout. This can be useful when debugging hanging requests. | ##### Supported HTTP methods The HTTP request methods specify the purpose of the request being made and what is expected if the request is successful. For instance, the `GET` request method tells the server at the requested address that a resource is being requested and, if it succeeds, the resource at that address will be returned. Similarly, the `HEAD` request method does the same except the server knows to return a response without a `Body` element. | Method | Description | Safe | | --- | --- | --- | | `GET` [ⓘ](https://developer.mozilla.org/docs/Web/HTTP/Methods/GET) | The `GET` method requests the resource at the specified address. Does not support use of the `Body` parameter. | Yes | | `HEAD` [ⓘ](https://developer.mozilla.org/docs/Web/HTTP/Methods/HEAD) | The `HEAD` method requests a response identical to a `GET` request, but with no response body. Does not support use of the `Body` parameter. | Yes | | `POST` [ⓘ](https://developer.mozilla.org/docs/Web/HTTP/Methods/POST) | The `POST` method submits the supplied `Body` data to the requested address. | No | | `PUT` [ⓘ](https://developer.mozilla.org/docs/Web/HTTP/Methods/PUT) | The `PUT` method replaces all current iterations of the resource specified within the supplied `Body` data. | No | | `DELETE` [ⓘ](https://developer.mozilla.org/docs/Web/HTTP/Methods/DELETE) | The `DELETE` method deletes the resource specified in the supplied `Body` data at the requested address. | No | | `OPTIONS` [ⓘ](https://developer.mozilla.org/docs/Web/HTTP/Methods/OPTIONS) | The `OPTIONS` method requests the permitted communication options for the supplied address. | Yes | | `TRACE` [ⓘ](https://developer.mozilla.org/docs/Web/HTTP/Methods/TRACE) | The `TRACE` method performs a message loop-back test along the path to the resource specified in the supplied `Body` data. | Yes | | `PATCH` [ⓘ](https://developer.mozilla.org/docs/Web/HTTP/Methods/PATCH) | The `PATCH` method applies partial changes to the resource specified in the supplied `Body` data at the requested address. | No | ##### HTTP headers In the request dictionary, you can specify custom HTTP headers to use in the request. However, some headers cannot be specified. For example, `Content-Length` is determined from the request body. `User-Agent` and `Roblox-Id` are locked by Roblox. Other headers like `Accept` or `Cache-Control` use default values but can be overridden. More commonly, some REST APIs may require API keys or other service authentication to be specified in request headers. The `RequestAsync()` method does not detect the format of body content. Many web servers require the `Content-Type` header be set appropriately when sending certain formats. Other methods of [HttpService](/docs/reference/engine/classes/HttpService.md) use the [HttpContentType](/docs/reference/engine/enums/HttpContentType.md) enum; for this method set the `Content-Type` header appropriately: `text/plain`, `text/xml`, `application/xml`, `application/json` or `application/x-www-form-urlencoded` are replacement `Content-Type` header values for the respective enum values. ##### Response dictionary fields `RequestAsync()` returns a dictionary containing the following fields: | Name | Type | Description | | --- | --- | --- | | `Success` | Boolean | The success status of the request. This is true if and only if the `StatusCode` lies within the range `200`-`299`. | | `StatusCode` | Integer | The HTTP response code identifying the status of the response. | | `StatusMessage` | String | The status message that was sent back. | | `Headers` | Dictionary | A dictionary of headers that were set in this response. | | `Body` | | The request body (content) received in the response. | ##### Error Cases `RequestAsync()` raises an error if the response times out or if the target server rejects the request. If a web service goes down for some reason, it can cause scripts that use this method to stop functioning altogether. It is often a good idea to wrap calls to this method in [LuaGlobals.pcall()](/docs/reference/engine/globals/LuaGlobals.md) and gracefully handle failure cases if the required information isn't available. ##### Limitations The current limitation for sending and receiving **external** HTTP requests is 500 requests per minute. There is also a separate limit of 2500 requests per minute for [Open Cloud requests](/docs/en-us/cloud-services/http-service.md#use-with-open-cloud). Requests over these thresholds will fail. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Network* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `requestOptions` | `Dictionary` | | A dictionary containing information to be requested from the server specified. | **Returns:** `Dictionary` — A dictionary containing response information from the server specified. **Sending an HTTP Request** This code sample demonstrates sending a single HTTP POST request with JSON data to the website httpbin.org, a website that helps debug HTTP requests. Here, we send some JSON data by using [HttpService:JSONEncode()](/docs/reference/engine/classes/HttpService.md) and also setting the `Content-Type` header. ```lua -- Remember to set enable HTTP Requests in experience settings! local HttpService = game:GetService("HttpService") local function request() local response = HttpService:RequestAsync({ Url = "http://httpbin.org/post", -- This website helps debug HTTP requests Method = "POST", Headers = { ["Content-Type"] = "application/json", -- When sending JSON, set this! }, Body = HttpService:JSONEncode({ hello = "world" }), }) if response.Success then print("Status code:", response.StatusCode, response.StatusMessage) print("Response body:\n", response.Body) else print("The request failed:", response.StatusCode, response.StatusMessage) end end -- Remember to wrap the function in a 'pcall' to prevent the script from breaking if the request fails local success, message = pcall(request) if not success then print("Http Request failed:", message) end ``` **Expected output:** Status code: 200 OK Response body: {"args":{},"data":"{\"hello\":\"world\"}","files":{},"form":{},"headers":{"Accept":"*/*","Accept-Encoding":"gzip, deflate","Cache-Control":"no-cache","Connection":"close","Content-Length":"17","Content-Type":"application/json","Host":"httpbin.org","Roblox-Id":"95206192","User-Agent":"RobloxStudio/WinInet"},"json":{"hello":"world"},"origin":"104.136.21.222","url":"http://httpbin.org/post"} **Open Cloud via HttpService** This code sample demonstrates sending a single HTTP PATCH request with JSON data to the [Open Cloud Update Group Membership](/docs/en-us/cloud/reference/GroupMembership.md#Cloud_UpdateGroupMembership) endpoint. Every Open Cloud endpoint requires adding your [API key](/docs/en-us/cloud/auth/api-keys.md#create-api-keys) to the `x-api-key` header to receive a successful response. The generated API key must be stored as a [Secret](/docs/reference/engine/datatypes/Secret.md) that can later be retrieved with [HttpService:GetSecret("API KEY SECRET NAME")](/docs/reference/engine/classes/HttpService.md). To learn more about calling Open Cloud endpoints via `HttpService`, see [In-experience HTTP requests](/docs/en-us/cloud-services/http-service.md). ```lua -- Remember to set enable HTTP Requests in experience settings! local HttpService = game:GetService("HttpService") local groupId = "your_group_id" local membershipId = "your_membership_id" local roleId = "your_role_id" local function request() local response = HttpService:RequestAsync({ Url = `https://apis.roblox.com/cloud/v2/groups/{groupId}/memberships/{membershipId}`, -- Updates a user's group membership Method = "PATCH", Headers = { ["Content-Type"] = "application/json", -- When sending JSON, set this! ["x-api-key"] = HttpService:GetSecret("APIKey"), -- Set in Creator Hub }, Body = HttpService:JSONEncode({ role = `groups/{groupId}/roles/{roleId}` }), }) if response.Success then print("The response was successful:", response.StatusCode, response.StatusMessage) else print("The response returned an error:", response.StatusCode, response.StatusMessage) end print("Response body:\n", response.Body) print("Response headers:\n", HttpService:JSONEncode(response.Headers)) end -- Remember to wrap the function in a 'pcall' to prevent the script from breaking if the request fails local success, message = pcall(request) if not success then print("The Http request failed to send:", message) end ``` ### Method: HttpService:UrlEncode **Signature:** `HttpService:UrlEncode(input: string): string` This method [percent-encodes](https://en.wikipedia.org/wiki/Percent-encoding) a given string so that reserved characters properly encoded with `%` and two hexadecimal characters. This is useful when formatting URLs for use with [HttpService:GetAsync()](/docs/reference/engine/classes/HttpService.md)/[HttpService:PostAsync()](/docs/reference/engine/classes/HttpService.md), or `POST` data of the media type `application/x-www-form-urlencoded` ([Enum.HttpContentType.ApplicationUrlEncoded](/docs/reference/engine/enums/HttpContentType.md)). For instance, when you encode the URL `https://www.roblox.com/discover#/`, this method returns `https%3A%2F%2Fwww%2Eroblox%2Ecom%2Fdiscover%23%2F`. *Security: None · Thread Safety: Safe · Capabilities: Network* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `input` | `string` | | The string (URL) to encode. | **Returns:** `string` — The encoded string. **HttpService UrlEncode** This code sample uses `UrlEncode` to turn a string into a safe, [percent-encoded][1] string that can be used in a URL as an argument. Notice how only unreserved characters (letters, numbers and `-_.~`) are not transformed into percent-encoded equivalents. Characters with accents are also transformed (for example `é` is transformed into `%C3`). [1]: https://en.wikipedia.org/wiki/Percent-encoding ```lua local HttpService = game:GetService("HttpService") local content = "Je suis allé au cinéma." -- French for "I went to the movies" local result = HttpService:UrlEncode(content) print(result) --> Je%20suis%20all%C3%A9%20au%20cinema%2E ``` **New Pastebin Post** Pastebin.com is a website that allows users to paste text (usually source code) for others to view publicly. This code sample uses HttpService PostAsync and the pastebin web API to automatically create a new public paste on the website. Since pastebin's API is designed to take data in as a URL encoded string, the code uses a for-loop to turn the `dataFields` table into a URL encoded string, such as hello=world&foo=bar. This is used as the HTTP POST data. Test this code by first going to [pastebin.com/api#1](https://pastebin.com/api#1) and getting an API key (you'll need a pastebin account to do this). Then, paste your unique developer API key into the field `api_dev_key` in the code sample's `dataFields` table. Fill in any other information about the post you want to make, then run this code in a Script (not a LocalScript). If all goes well, you'll get a URL to your new paste in the Output window (or some error string from pastebin). ```lua local HttpService = game:GetService("HttpService") local URL_PASTEBIN_NEW_PASTE = "https://pastebin.com/api/api_post.php" local dataFields = { -- Pastebin API developer key from -- https://pastebin.com/api#1 ["api_dev_key"] = "FILL THIS WITH YOUR API DEVELOPER KEY", ["api_option"] = "paste", -- keep as "paste" ["api_paste_name"] = "HttpService:PostAsync", -- paste name ["api_paste_code"] = "Hello, world", -- paste content ["api_paste_format"] = "text", -- paste format ["api_paste_expire_date"] = "10M", -- expire date ["api_paste_private"] = "0", -- 0=public, 1=unlisted, 2=private ["api_user_key"] = "", -- user key, if blank post as guest } -- The pastebin API uses a URL encoded string for post data -- Other APIs might use JSON, XML or some other format local data = "" for k, v in pairs(dataFields) do data = data .. ("&%s=%s"):format(HttpService:UrlEncode(k), HttpService:UrlEncode(v)) end data = data:sub(2) -- Remove the first & -- Here's the data we're sending print(data) -- Make the request local response = HttpService:PostAsync(URL_PASTEBIN_NEW_PASTE, data, Enum.HttpContentType.ApplicationUrlEncoded, false) -- The response will be the URL to the new paste (or an error string if something was wrong) print(response) ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Humanoid last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances summary: "A special object that gives models the functionality of a character." --- # Class: Humanoid > A special object that gives models the functionality of a character. ## Description The Humanoid is a special object that gives models the functionality of a character. It grants the model with the ability to physically walk around and interact with various components of a Roblox experience. Humanoids are always parented inside of a [Model](/docs/reference/engine/classes/Model.md), and the model is expected to be an assembly of [BasePart](/docs/reference/engine/classes/BasePart.md) and [Motor6D](/docs/reference/engine/classes/Motor6D.md); the root part of the assembly is expected to be named `HumanoidRootPart`. It also expects a part named `Head` to be connected to the character's torso part, either directly or indirectly. By default, there are two official types of character rigs supplied by Roblox, each with their own set of rules: ## R6 - A basic character rig that uses 6 parts for limbs. - The `Head` part must be attached to a part named `Torso`, or the Humanoid will die immediately. - BodyPart appearances are applied using [CharacterMesh](/docs/reference/engine/classes/CharacterMesh.md) objects. - Certain properties, such as [Humanoid.LeftLeg](/docs/reference/engine/classes/Humanoid.md) and [Humanoid.RightLeg](/docs/reference/engine/classes/Humanoid.md), only work with R6. ## R15 - More complex than R6, but also far more flexible and robust. - Uses 15 parts for limbs. - The `Head` part must be attached to a part named `UpperTorso` or the Humanoid will die immediately. - BodyPart appearances have to be assembled directly. - Can be dynamically rescaled by using special [NumberValue](/docs/reference/engine/classes/NumberValue.md) objects parented inside of the Humanoid. - The Humanoid will automatically create [Vector3Value](/docs/reference/engine/classes/Vector3Value.md) objects named `OriginalSize` inside of each limb. - If a NumberValue is parented inside of the Humanoid and is named one of the following, it will be used to control the scaling functionality: - BodyDepthScale - BodyHeightScale - BodyWidthScale - HeadScale ## Code Samples **Walking Camera Bobble Effect** This LocalScript makes the camera bobble as the player's character walks around, utilizing both the Humanoid's CameraOffset and MoveDirection. It should be parented inside of the StarterCharacterScripts so that it is distributed into a player's character as expected. ```lua local RunService = game:GetService("RunService") local playerModel = script.Parent local humanoid = playerModel:WaitForChild("Humanoid") local function updateBobbleEffect() local now = tick() if humanoid.MoveDirection.Magnitude > 0 then -- Is the character walking? local velocity = humanoid.RootPart.Velocity local bobble_X = math.cos(now * 9) / 5 local bobble_Y = math.abs(math.sin(now * 12)) / 5 local bobble = Vector3.new(bobble_X, bobble_Y, 0) * math.min(1, velocity.Magnitude / humanoid.WalkSpeed) humanoid.CameraOffset = humanoid.CameraOffset:lerp(bobble, 0.25) else -- Scale down the CameraOffset so that it shifts back to its regular position. humanoid.CameraOffset = humanoid.CameraOffset * 0.75 end end RunService.RenderStepped:Connect(updateBobbleEffect) ``` ## Properties ### Property: Humanoid.AutoJumpEnabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Jump Settings", "capabilities": [ "AvatarBehavior" ], "simulationAccess": true } ``` AutoJumpEnabled sets whether or not the [Humanoid](/docs/reference/engine/classes/Humanoid.md) will attempt to automatically jump over an obstacle it is walking towards. Currently, this property only works when the following conditions are true: - The Humanoid's character model is the [Player.Character](/docs/reference/engine/classes/Player.md) of a [Player](/docs/reference/engine/classes/Player.md). - The Player in question is using touch controls. When a player's character spawns, the property's value matches the player's [Player.AutoJumpEnabled](/docs/reference/engine/classes/Player.md) property - which in turn matches the [StarterPlayer.AutoJumpEnabled](/docs/reference/engine/classes/StarterPlayer.md) property. **Auto-Jump Toggle** This code sample is meant for a TextButton. It allows the player to toggle the auto-jumping behavior while on a mobile device. ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local button = script.Parent local function update() -- Update button text if player.AutoJumpEnabled then button.Text = "Auto-Jump is ON" else button.Text = "Auto-Jump is OFF" end -- Reflect the property in the player's character, if they have one if player.Character then local human = player.Character:FindFirstChild("Humanoid") if human then human.AutoJumpEnabled = player.AutoJumpEnabled end end end local function onActivated() -- Toggle auto-jump player.AutoJumpEnabled = not player.AutoJumpEnabled -- Update everything else update() end button.Activated:Connect(onActivated) update() ``` ### Property: Humanoid.AutomaticScalingEnabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Game", "capabilities": [ "AvatarAppearance" ] } ``` The Humanoid has six child scale values including `BodyDepthScale`, `BodyHeightScale`, `BodyProportionScale`, `BodyTypeScale`, `BodyWidthScale`, `HeadScale`. Changing the value of any of these causes the character's body parts and accessories to change size, but only if `AutomaticScalingEnabled` is true. ### Property: Humanoid.AutoRotate ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Control", "capabilities": [ "AvatarBehavior" ], "simulationAccess": true } ``` The AutoRotate property describes whether or not the Humanoid will automatically rotate to face in the direction they are moving. When set to true, the character model will gradually turn to face their movement direction as the Humanoid walks around. When set to false, the character model will remain fixated in its current rotation, unless a rotating force is applied to the _HumanoidRootPart_. If the character model happens to be the character of a player, then the behavior of the Humanoid's rotation is influenced by the UserGameSetting's RotateType property. When the AutoRotate property is set to true, the RotateType property has the following effects on the Humanoid's rotation: | RotationType | Behavior | Context | | --- | --- | --- | | MovementRelative | | | | CameraRelative | Character will rotate to face in the direction of the camera. | Player has their camera zoomed into first-person, or they are in shift-lock mode. | **AutoRotate Button** This script adds the functionality of a button to a part, which switches the AutoRotate property of whoever touches it. ```lua local button = script.Parent local enabled = true local ON_COLOR = BrickColor.Green() local OFF_COLOR = BrickColor.Red() local function touchButton(humanoid) if enabled then enabled = false button.BrickColor = OFF_COLOR if humanoid.AutoRotate then print(humanoid:GetFullName() .. " can no longer auto-rotate!") humanoid.AutoRotate = false else print(humanoid:GetFullName() .. " can now auto-rotate!") humanoid.AutoRotate = true end task.wait(1) button.BrickColor = ON_COLOR enabled = true end end local function onTouched(hit) local char = hit:FindFirstAncestorWhichIsA("Model") if char then local humanoid = char:FindFirstChildOfClass("Humanoid") if humanoid then touchButton(humanoid) end end end button.Touched:Connect(onTouched) button.BrickColor = ON_COLOR ``` ### Property: Humanoid.BreakJointsOnDeath ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "AvatarBehavior" ] } ``` Determines whether the humanoid's joints break when in the [HumanoidStateType.Dead](/docs/reference/engine/enums/HumanoidStateType.md) state. Defaults to true. ### Property: Humanoid.CameraOffset ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarBehavior" ] } ``` The CameraOffset property specifies an offset to the camera's subject position when its [Camera.CameraSubject](/docs/reference/engine/classes/Camera.md) is set to this [Humanoid](/docs/reference/engine/classes/Humanoid.md). The offset is applied in object-space, relative to the orientation of the Humanoid's _HumanoidRootPart_. For example, an offset [Vector3](/docs/reference/engine/datatypes/Vector3.md) value of _(0, 10, 0)_ offsets the player's camera to 10 studs above the player's humanoid. **Walking Camera Bobble Effect** This LocalScript makes the camera bobble as the player's character walks around, utilizing both the Humanoid's CameraOffset and MoveDirection. It should be parented inside of the StarterCharacterScripts so that it is distributed into a player's character as expected. ```lua local RunService = game:GetService("RunService") local playerModel = script.Parent local humanoid = playerModel:WaitForChild("Humanoid") local function updateBobbleEffect() local now = tick() if humanoid.MoveDirection.Magnitude > 0 then -- Is the character walking? local velocity = humanoid.RootPart.Velocity local bobble_X = math.cos(now * 9) / 5 local bobble_Y = math.abs(math.sin(now * 12)) / 5 local bobble = Vector3.new(bobble_X, bobble_Y, 0) * math.min(1, velocity.Magnitude / humanoid.WalkSpeed) humanoid.CameraOffset = humanoid.CameraOffset:lerp(bobble, 0.25) else -- Scale down the CameraOffset so that it shifts back to its regular position. humanoid.CameraOffset = humanoid.CameraOffset * 0.75 end end RunService.RenderStepped:Connect(updateBobbleEffect) ``` ### Property: Humanoid.DisplayDistanceType ```json { "type": "HumanoidDisplayDistanceType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarAppearance" ] } ``` The **DisplayDistanceType** property controls the distance behavior of the humanoid's name and health display. This property is set using the [HumanoidDisplayDistanceType](/docs/reference/engine/enums/HumanoidDisplayDistanceType.md) enum with three available values, each with their own set of rules: - When set to [Viewer](/docs/reference/engine/enums/HumanoidDisplayDistanceType.md), the humanoid sees the name/health of other humanoids within range of its own `NameDisplayDistance` and `HealthDisplayDistance`. - When set to [Subject](/docs/reference/engine/enums/HumanoidDisplayDistanceType.md), the humanoid takes **full control** over its own name and health display through its `NameDisplayDistance` and `HealthDisplayDistance` values. - When set to [None](/docs/reference/engine/enums/HumanoidDisplayDistanceType.md), the humanoid's name and health bar do not appear under any circumstances. See [Character Name/Health Display](/docs/en-us/characters/name-health-display.md) for an in-depth guide on controlling the appearance of character names and health bars. **Displaying a Humanoid's Health and Name** This example demonstrates how to set a `Humanoid`'s [Humanoid.DisplayerDistanceType](/docs/reference/engine/classes/Humanoid.md), [Humanoid.HealthDisplayDistance](/docs/reference/engine/classes/Humanoid.md), and [Humanoid.NameDisplayDistance](/docs/reference/engine/classes/Humanoid.md) properties. These properties determine how a humanoid's healthbar and name are rendered for a player. First, we change the DisplayDistanceType to Viewer using [HumanoidDisplayDistanceType](/docs/reference/engine/enums/HumanoidDisplayDistanceType.md). When set to viewer, the humanoid's Name and healthbar will be displayed based on the distance settings of the humanoid viewing them. Then, the humanoid's HealthDisplayDistance is set to 0. Setting the property to 0 hides the healthbar completely. It is not displayed at any distance. Finally, the humanoid's NameDisplayDistance is set to 100. This means that the humanoid's name will be visible to other humanoid's within 100 studs. This example should work as expected when placed inside a `Script` that is a child of the humanoid. ```lua local humanoid = script.Parent humanoid.DisplayDistanceType = Enum.HumanoidDisplayDistanceType.Viewer humanoid.HealthDisplayDistance = 0 humanoid.NameDisplayDistance = 100 ``` ### Property: Humanoid.DisplayName ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarAppearance" ] } ``` `DisplayName` is a property that determines the Humanoid's name display when visible. By default, a new Humanoid will have the value of an empty string. If `DisplayName` is an empty string, the humanoid's name display will default to the humanoid's parent's name property. #### Player Character Loading When players load their character, either automatically or through the use of [LoadCharacterAsync()](/docs/reference/engine/classes/Player.md), the Humanoid that is created by the engine will have its `DisplayName` property set to the player's `DisplayName` property. #### StarterCharacter and StarterHumanoid When a [Humanoid](/docs/reference/engine/classes/Humanoid.md) named `StarterHumanoid` is parented to [StarterPlayer](/docs/reference/engine/classes/StarterPlayer.md), or when a Humanoid is present in a Model named `StarterCharacter`, the DisplayName property will be respected when Characters are loaded by Players in the game. The engine will only override the `DisplayName` property of the Humanoid with the `DisplayName` property of the player if the [Humanoid.DisplayName](/docs/reference/engine/classes/Humanoid.md) of `StarterHumanoid` is an empty string. ### Property: Humanoid.EvaluateStateMachine ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "AvatarBehavior" ], "simulationAccess": true } ``` Used to disable the internal physics and state machine of the `Humanoid`. #### What does turning this off do? - **No forces** - The humanoid will not apply forces to any of its parts. The humanoid will not move the character in any way. - **No sensors** - The humanoid will not run any spatial queries to detect floors, ladders, or other obstacles (such as auto-jump). - **No collision changes** - The humanoid will not alter the collision state of any of the character parts. By default, only the torso and head have [Part.CanCollide](/docs/reference/engine/classes/Part.md) enabled. - **No state transitions or replication** - The humanoid will not automatically update its state. You can still set humanoid state with a script, but it will not automatically replicate. #### What does turning this off **not** do? - **State events** - If manually setting humanoid states, the events for each of the humanoid's states and [StateChanged](/docs/reference/engine/classes/Humanoid.md) will still fire, which means the Animate script will still receive them and animate the character based on its current humanoid state. - **Appearance** - [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md), clothes, accessories and other humanoid rendering behavior. - **Camera and player scripts** - The humanoid is still linked to [Players](/docs/reference/engine/classes/Players.md) and camera scripts, which ensures the camera will continue to follow the [Humanoid.RootPart](/docs/reference/engine/classes/Humanoid.md). - **Player input handling** - The [Humanoid.MoveDirection](/docs/reference/engine/classes/Humanoid.md) property will still be updated via the [Humanoid:Move()](/docs/reference/engine/classes/Humanoid.md) function and [PlayerScripts](/docs/reference/engine/classes/PlayerScripts.md) for input. ### Property: Humanoid.FloorMaterial ```json { "type": "Material", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Control", "capabilities": [ "AvatarBehavior" ], "simulationAccess": true } ``` This is a read-only property that describes the [Material](/docs/reference/engine/enums/Material.md) the [Humanoid](/docs/reference/engine/classes/Humanoid.md) is currently standing on. It works with both regular [Parts](/docs/reference/engine/classes/BasePart.md) and [Terrain](/docs/reference/engine/classes/Terrain.md) voxels. The code sample below demonstrates how to listen to when this property changes using [Object:GetPropertyChangedSignal()](/docs/reference/engine/classes/Object.md). When the material the humanoid is standing on changes, it will print a message indicating the new material being stood on. ```lua local Humanoid = route.to.humanoid Humanoid:GetPropertyChangedSignal("FloorMaterial"):Connect(function() print("New value for FloorMaterial: " .. tostring(Humanoid.FloorMaterial)) end) ``` #### Caveats - When the [Humanoid](/docs/reference/engine/classes/Humanoid.md) is not standing on a floor, the value of this property will be set to _Air_. - This occurs because Enum properties cannot have an empty value. - This can cause some confusion if a part has its material is set to Air, though in practice, parts are not supposed to use that material in the first place. - The character model of the [Humanoid](/docs/reference/engine/classes/Humanoid.md) must be able to collide with the floor, or else it will not be detected. - You cannot test if the [Humanoid](/docs/reference/engine/classes/Humanoid.md) is swimming with this property. You should instead use its [Humanoid:GetState()](/docs/reference/engine/classes/Humanoid.md) function. ### Property: Humanoid.Health ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Game", "capabilities": [ "AvatarBehavior" ] } ``` This property represents the current health of the [Humanoid](/docs/reference/engine/classes/Humanoid.md). The value is restricted to the range between 0 and [MaxHealth](/docs/reference/engine/classes/Humanoid.md). If the humanoid is dead, this property is continually set to 0. Note that the [TakeDamage()](/docs/reference/engine/classes/Humanoid.md) function may be used to subtract from [Health](/docs/reference/engine/classes/Humanoid.md) instead of setting the property directly. #### Health Regeneration By default, a passive health regeneration script is automatically inserted into humanoids. This causes non-dead player characters to regenerate 1% of [MaxHealth](/docs/reference/engine/classes/Humanoid.md) each second. To disable this regeneration behavior, add an empty [Script](/docs/reference/engine/classes/Script.md) named **Health** to [StarterCharacterScripts](/docs/reference/engine/classes/StarterCharacterScripts.md). #### Health Bar Display When [Health](/docs/reference/engine/classes/Humanoid.md) is less than [MaxHealth](/docs/reference/engine/classes/Humanoid.md), a health bar is displayed in-experience. The display behavior of the health bar is dependent on the [HealthDisplayDistance](/docs/reference/engine/classes/Humanoid.md) and [HealthDisplayType](/docs/reference/engine/classes/Humanoid.md). See [Character Name/Health Display](/docs/en-us/characters/name-health-display.md) for an in-depth guide on controlling the appearance of character names and health bars. #### Death When the value of the character's health reaches 0, the [Humanoid](/docs/reference/engine/classes/Humanoid.md) automatically transitions to the [HumanoidStateType.Dead](/docs/reference/engine/enums/HumanoidStateType.md) state. In this state, [Health](/docs/reference/engine/classes/Humanoid.md) is locked to 0; however, there is no error or warning for setting the [Health](/docs/reference/engine/classes/Humanoid.md) of a dead humanoid to a positive nonzero value. ### Property: Humanoid.HealthDisplayDistance ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarAppearance" ] } ``` This property is a number used in conjunction with the [DisplayDistanceType](/docs/reference/engine/classes/Humanoid.md) property to control the distance from which a humanoid's health bar can be seen. See [Character Name/Health Display](/docs/en-us/characters/name-health-display.md) for an in-depth guide on controlling the appearance of character names and health bars. ### Property: Humanoid.HealthDisplayType ```json { "type": "HumanoidHealthDisplayType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarAppearance" ] } ``` This property controls when a humanoid's health bar is allowed to be displayed. By default, this property is set to [DisplayWhenDamaged](/docs/reference/engine/enums/HumanoidHealthDisplayType.md), which makes the health bar only display when a humanoid's [Health](/docs/reference/engine/classes/Humanoid.md) is less than its [MaxHealth](/docs/reference/engine/classes/Humanoid.md). It can also be set to [AlwaysOn](/docs/reference/engine/enums/HumanoidHealthDisplayType.md), which makes the health bar always display, or [AlwaysOff](/docs/reference/engine/enums/HumanoidHealthDisplayType.md), which prevents it from ever displaying. Note that this property functions independently of the humanoid's [HealthDisplayDistance](/docs/reference/engine/classes/Humanoid.md) property which is responsible for making the health bar fade out at certain distances. If `Humanoid.HealthDisplayType|HealthDisplayType` is set to [AlwaysOn](/docs/reference/engine/enums/HumanoidHealthDisplayType.md), it will still fade out depending the how [HealthDisplayDistance](/docs/reference/engine/classes/Humanoid.md) is configured. See [Character Name/Health Display](/docs/en-us/characters/name-health-display.md) for an in-depth guide on controlling the appearance of character names and health bars. ### Property: Humanoid.HipHeight ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Game", "capabilities": [ "AvatarAppearance" ], "simulationAccess": true } ``` Determines the distance (in studs) off the ground the [RootPart](/docs/reference/engine/classes/Humanoid.md) should be when the humanoid is standing. The [RigType](/docs/reference/engine/classes/Humanoid.md) influences the way this property behaves. For R15 rigs, a suitable hip height is preset to ensure the height of the [RootPart](/docs/reference/engine/classes/Humanoid.md) is correct. The height of the legs is not used. The overall height of the humanoid can be described in the following formula: ```lua Height = (0.5 * RootPart.Size.Y) + HipHeight ``` For R6 rigs, [HipHeight](/docs/reference/engine/classes/Humanoid.md) instead describes a relative offset. The overall height of the humanoid can be described in the following formula: ```lua Height = LeftLeg.Size.Y + (0.5 * RootPart.Size.Y) + HipHeight ``` ### Property: Humanoid.Jump ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Control", "capabilities": [ "AvatarBehavior" ], "simulationAccess": true } ``` If `true`, the [Humanoid](/docs/reference/engine/classes/Humanoid.md) jumps with an upwards force equal to the value of [Humanoid.JumpPower](/docs/reference/engine/classes/Humanoid.md) or the height of [Humanoid.JumpHeight](/docs/reference/engine/classes/Humanoid.md), depending on the value of [Humanoid.UseJumpPower](/docs/reference/engine/classes/Humanoid.md). ### Property: Humanoid.JumpHeight ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Jump Settings", "capabilities": [ "AvatarBehavior" ], "simulationAccess": true } ``` Provides control over the height a [Humanoid](/docs/reference/engine/classes/Humanoid.md) jumps, in studs. The starting value of this property is determined by the value of [StarterPlayer.CharacterJumpHeight](/docs/reference/engine/classes/StarterPlayer.md) which defaults to 7.2. Although setting this property to 0 will effectively prevent the humanoid from jumping, it's recommended to disable jumping by disabling the [HumanoidStateType.Jumping](/docs/reference/engine/enums/HumanoidStateType.md) state through [Humanoid:SetStateEnabled()](/docs/reference/engine/classes/Humanoid.md). This property is only visible in the [Properties](/docs/en-us/studio/properties.md) window if [Humanoid.UseJumpPower](/docs/reference/engine/classes/Humanoid.md) is set to **false**, as it is not relevant otherwise (instead, [Humanoid.JumpPower](/docs/reference/engine/classes/Humanoid.md) is used). ### Property: Humanoid.JumpPower ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Jump Settings", "capabilities": [ "AvatarBehavior" ], "simulationAccess": true } ``` Determines how much upwards force is applied to the [Humanoid](/docs/reference/engine/classes/Humanoid.md) when jumping. The starting value of this property is determined by the value of [StarterPlayer.CharacterJumpPower](/docs/reference/engine/classes/StarterPlayer.md) which defaults to 50 and is constrained between 0 and 1000. Note that jumps are also influenced by the [Workspace.Gravity](/docs/reference/engine/classes/Workspace.md) property which determines the acceleration due to gravity. Although setting this property to 0 will effectively prevent the humanoid from jumping, it's recommended to disable jumping by disabling the [HumanoidStateType.Jumping](/docs/reference/engine/enums/HumanoidStateType.md) state through [Humanoid:SetStateEnabled()](/docs/reference/engine/classes/Humanoid.md). This property is only visible in the [Properties](/docs/en-us/studio/properties.md) window if [Humanoid.UseJumpPower](/docs/reference/engine/classes/Humanoid.md) is set to **true**, as it is not relevant otherwise (instead, [Humanoid.JumpHeight](/docs/reference/engine/classes/Humanoid.md) is used). ### Property: Humanoid.MaxHealth ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Game", "capabilities": [ "AvatarBehavior" ] } ``` The maximum value of a humanoid's [Health](/docs/reference/engine/classes/Humanoid.md). The value of this property is used alongside the [Health](/docs/reference/engine/classes/Humanoid.md) property to size the default health bar display. When a humanoid's [Health](/docs/reference/engine/classes/Humanoid.md) reaches [MaxHealth](/docs/reference/engine/classes/Humanoid.md), its health bar may not be displayed, depending on its [HealthDisplayType](/docs/reference/engine/classes/Humanoid.md) property. ### Property: Humanoid.MaxSlopeAngle ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Game", "capabilities": [ "AvatarBehavior" ] } ``` This property determines the maximum slope angle that a humanoid can climb. If the angle of a slope is greater than a humanoid's MaxSlopeAngle, they will slide down the slope. When a character spawns, this property is set according to the value of [StarterPlayer.CharacterMaxSlopeAngle](/docs/reference/engine/classes/StarterPlayer.md). The value of this property is constrained to values between 0° and 89°. It defaults to 89°, so humanoids can climb pretty much any slope they want by default. **Limiting The Slope a Humanoid Can Walk Up** The example below demonstrates the effect of the MaxSlopAngle property by limiting the maximum slope the [Players.LocalPlayer](/docs/reference/engine/classes/Players.md) can walk up to 30°. The local player will slide down any slope greater than 30°. This code below works as expected when placed in a `LocalScript`. ```lua local player = game.Players.LocalPlayer local char = player.CharacterAdded:wait() local h = char:FindFirstChild("Humanoid") h.MaxSlopeAngle = 30 ``` ### Property: Humanoid.MoveDirection ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Control", "capabilities": [ "AvatarBehavior" ], "simulationAccess": true } ``` **MoveDirection** is a read-only property that describes the direction a [Humanoid](/docs/reference/engine/classes/Humanoid.md) is walking in, as a unit vector or zero length vector. The direction is described in world space. Because this property is read-only, it cannot be set by a [Script](/docs/reference/engine/classes/Script.md) or [LocalScript](/docs/reference/engine/classes/LocalScript.md). **Walking Camera Bobble Effect** This LocalScript makes the camera bobble as the player's character walks around, utilizing both the Humanoid's CameraOffset and MoveDirection. It should be parented inside of the StarterCharacterScripts so that it is distributed into a player's character as expected. ```lua local RunService = game:GetService("RunService") local playerModel = script.Parent local humanoid = playerModel:WaitForChild("Humanoid") local function updateBobbleEffect() local now = tick() if humanoid.MoveDirection.Magnitude > 0 then -- Is the character walking? local velocity = humanoid.RootPart.Velocity local bobble_X = math.cos(now * 9) / 5 local bobble_Y = math.abs(math.sin(now * 12)) / 5 local bobble = Vector3.new(bobble_X, bobble_Y, 0) * math.min(1, velocity.Magnitude / humanoid.WalkSpeed) humanoid.CameraOffset = humanoid.CameraOffset:lerp(bobble, 0.25) else -- Scale down the CameraOffset so that it shifts back to its regular position. humanoid.CameraOffset = humanoid.CameraOffset * 0.75 end end RunService.RenderStepped:Connect(updateBobbleEffect) ``` ### Property: Humanoid.NameDisplayDistance ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarAppearance" ] } ``` The **NameDisplayDistance** property is a number used in conjunction with the [Humanoid.DisplayDistanceType](/docs/reference/engine/classes/Humanoid.md) property to control the distance from which a humanoid's name can be seen. See [Character Name/Health Display](/docs/en-us/characters/name-health-display.md) for an in-depth guide on controlling the appearance of character names and health bars. ### Property: Humanoid.NameOcclusion ```json { "type": "NameOcclusion", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarAppearance" ] } ``` Controls whether a humanoid's name and health bar can be seen behind walls or other objects. This property is a [NameOcclusion](/docs/reference/engine/enums/NameOcclusion.md) value and can be configured to occlude all names, enemy names, or disable occlusion entirely. In cases where the [LocalPlayer](/docs/reference/engine/classes/Players.md) has no [Humanoid](/docs/reference/engine/classes/Humanoid.md) associated with it, this property instead applies to the subject [Humanoid](/docs/reference/engine/classes/Humanoid.md). See [Character Name/Health Display](/docs/en-us/characters/name-health-display.md) for an in-depth guide on controlling the appearance of character names and health bars. **Occlude Player Names** In the below example, `Player|Players` will not be able to see each other's [Player.Character](/docs/reference/engine/classes/Player.md) names when they are obscured behind `BasePart|BaseParts`. ```lua local Players = game:GetService("Players") local function onCharacterAdded(character) local humanoid = character:WaitForChild("Humanoid") humanoid.NamOcclusion = Enum.NameOcclusion.OccludeAll end local function onPlayerAdded(player) player.CharacterAdded:Connect(onCharacterAdded) end Players.PlayerAdded:Connect(onPlayerAdded) ``` ### Property: Humanoid.PlatformStand ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Control", "capabilities": [ "AvatarBehavior" ] } ``` Determines whether the [Humanoid](/docs/reference/engine/classes/Humanoid.md) is currently in the [HumanoidStateType.PlatformStanding](/docs/reference/engine/enums/HumanoidStateType.md) state. When true, the Humanoid is in a state where it is free-falling and cannot move. This state behaves similar to sitting, except that jumping does not free the humanoid from the state. ### Property: Humanoid.RequiresNeck ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "AvatarBehavior" ] } ``` Allows developers to disable the behavior where a player `Character|character` dies if the Neck [Motor6D](/docs/reference/engine/classes/Motor6D.md) is removed or disconnected even momentarily. This property defaults to true. ### Property: Humanoid.RigType ```json { "type": "HumanoidRigType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarAppearance" ] } ``` **RigType** describes whether a [Humanoid](/docs/reference/engine/classes/Humanoid.md) is utilizing the legacy R6 character rig, or the newer R15 character rig. The R6 rig uses 6 visible [Parts](/docs/reference/engine/classes/Part.md) while the R15 rig uses 15 visible [Parts](/docs/reference/engine/classes/Part.md). R15 rigs have more joints than R6 rigs, making them much more versatile when being animated. Note that if this property is set incorrectly, the [Humanoid](/docs/reference/engine/classes/Humanoid.md) will not function correctly. For example, if a R15 humanoid's **RigType** is set to R6, the [Humanoid](/docs/reference/engine/classes/Humanoid.md) will die as there is no [BasePart](/docs/reference/engine/classes/BasePart.md) called **Torso** connected to a [BasePart](/docs/reference/engine/classes/BasePart.md) called **Head**. ### Property: Humanoid.RootPart ```json { "type": "BasePart", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarBehavior" ], "simulationAccess": true } ``` A reference to the humanoid's `HumanoidRootPart` object. The `HumanoidRootPart` is required for all assemblies that include a [Humanoid](/docs/reference/engine/classes/Humanoid.md) and is the primary driving part of the [Humanoid](/docs/reference/engine/classes/Humanoid.md) that controls a humanoid's movement through the 3D world. This part is normally invisible and typically resides in the local origin center of the character model. The `HumanoidRootPart` is included in all platform-wide avatar characters, and automatically created when a character model is imported into Studio. It's possible to manually add a `HumanoidRootPart` to a model by adding a [Part](/docs/reference/engine/classes/Part.md) named `HumanoidRootPart` and ensuring it's the root part of the assembly. Use this property to reference the specific `HumanoidRootPart` without searching the model hierarchy. You can get or set the [CFrame](/docs/reference/engine/classes/CFrame.md) of this part to change position or reorient the entire character model. For R15 or higher-fidelity characters, the [Model.PrimaryPart](/docs/reference/engine/classes/Model.md) of the [Player.Character](/docs/reference/engine/classes/Player.md) model is also set to `HumanoidRootPart`. While R6 characters also include a `HumanoidRootPart`, the R6 characters, [Model.PrimaryPart](/docs/reference/engine/classes/Model.md) is set to the `Head` part. ### Property: Humanoid.SeatPart ```json { "type": "BasePart", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Control", "capabilities": [ "AvatarBehavior" ], "simulationAccess": true } ``` SeatPart is a reference to the seat that a [Humanoid](/docs/reference/engine/classes/Humanoid.md) is currently sitting in, if any. The value of this property can be either a [Seat](/docs/reference/engine/classes/Seat.md), or a [VehicleSeat](/docs/reference/engine/classes/VehicleSeat.md). It will be _nil_ if the Humanoid is not currently sitting in a seat. Note: - For a bool describing if the [Humanoid](/docs/reference/engine/classes/Humanoid.md) is currently sitting or not, see [Humanoid.Sit](/docs/reference/engine/classes/Humanoid.md) ### Property: Humanoid.Sit ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Control", "capabilities": [ "AvatarBehavior" ], "simulationAccess": true } ``` The Sit property is a boolean that indicates whether the [Humanoid](/docs/reference/engine/classes/Humanoid.md) is currently sitting. [Humanoids](/docs/reference/engine/classes/Humanoid.md) can be forced into a sitting state by setting this property's value to true. If the [Humanoid](/docs/reference/engine/classes/Humanoid.md) isn't attached to a seat while in its sitting state, it will trip over with no collision in its legs. A [Humanoid](/docs/reference/engine/classes/Humanoid.md) can escape from the sitting state by jumping. Note: - The [Seat](/docs/reference/engine/classes/Seat.md) or [VehicleSeat](/docs/reference/engine/classes/VehicleSeat.md) the [Humanoid](/docs/reference/engine/classes/Humanoid.md) is sitting on can be obtained using the [Humanoid.SeatPart](/docs/reference/engine/classes/Humanoid.md) property - It is possible to detect when a Humanoid sits by connecting to the [Humanoid.Seated](/docs/reference/engine/classes/Humanoid.md) event. ### Property: Humanoid.TargetPoint ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Control", "capabilities": [ "AvatarBehavior" ] } ``` **Do not use** This property only works with Experimental Mode enabled, which has been entirely discontinued. This property describes a 3D position in space where the [Player](/docs/reference/engine/classes/Player.md) controlling this [Humanoid](/docs/reference/engine/classes/Humanoid.md) last clicked with a [Tool](/docs/reference/engine/classes/Tool.md) equipped. This property is primarily used by classic tools to determine what a humanoid is targeting when they activate a tool. If you give an NPC a classic rocket launcher, set their **TargetPoint**, and then call the tool's [Tool:Activate()](/docs/reference/engine/classes/Tool.md) function, you can make the NPC fire a rocket at the target point. ### Property: Humanoid.UseJumpPower ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Jump Settings", "capabilities": [ "AvatarBehavior" ], "simulationAccess": true } ``` When a character spawns, this property is set according to the value of [StarterPlayer.CharacterUseJumpPower](/docs/reference/engine/classes/StarterPlayer.md), which defaults to true. When jumping, with this set to false, the [Humanoid.JumpHeight](/docs/reference/engine/classes/Humanoid.md) value is used to ensure the humanoid jumps to that height. With this set to true, the [Humanoid.JumpPower](/docs/reference/engine/classes/Humanoid.md) value is used to apply an upward force. ### Property: Humanoid.WalkSpeed ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Game", "capabilities": [ "AvatarBehavior" ], "simulationAccess": true } ``` This property describes how quickly the [Humanoid](/docs/reference/engine/classes/Humanoid.md) is able to walk, in studs per second. It defaults to the value of [StarterPlayer.CharacterWalkSpeed](/docs/reference/engine/classes/StarterPlayer.md) (`16`), meaning a player character can move 16 studs in any direction each second. #### Notes - When controlled on a mobile device or a gamepad, a humanoid can walk slower than its [WalkSpeed](/docs/reference/engine/classes/Humanoid.md) if the controlling thumbstick is moved just a gradual degree from center. - You can freeze a humanoid in place by setting [WalkSpeed](/docs/reference/engine/classes/Humanoid.md) to `0`, effectively preventing the controlling player from moving it through the default movement mechanisms. Note, however, that [WalkSpeed](/docs/reference/engine/classes/Humanoid.md) is a client‑replicated property which can be modified locally, so it should **not** be used as a security mechanism or as the sole method for preventing humanoid movement. - The default animation script scales a humanoid's movement animations based on how fast it is moving relative to the default speed of 16 studs/second. - The speed at which the [Humanoid](/docs/reference/engine/classes/Humanoid.md) is currently walking can be obtained using the [Running](/docs/reference/engine/classes/Humanoid.md) event. ### Property: Humanoid.WalkToPart ```json { "type": "BasePart", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Control", "capabilities": [ "AvatarBehavior" ] } ``` `WalkToPart` is a reference to a [BasePart](/docs/reference/engine/classes/BasePart.md) that the humanoid is trying to reach, after having been prompted to do so via the [MoveTo()](/docs/reference/engine/classes/Humanoid.md) method. When `WalkToPart` is set and a humanoid is actively trying to reach the part, it will keep updating its [Vector3](/docs/reference/engine/datatypes/Vector3.md) goal to be the position of the part, plus the [WalkToPoint](/docs/reference/engine/classes/Humanoid.md) translated in object space relative to the rotation of the part. This can be described in Luau as: ```lua goal = humanoid.WalkToPart.CFrame:PointToObjectSpace(humanoid.WalkToPoint) ``` Note that simply setting the value of `WalkToPart` is not sufficient to make a humanoid start "following" a part. Call [MoveTo()](/docs/reference/engine/classes/Humanoid.md) to initiate movement. ### Property: Humanoid.WalkToPoint ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Control", "capabilities": [ "AvatarBehavior" ], "simulationAccess": true } ``` `WalkToPoint` describes the 3D position in space that a humanoid is trying to reach, after having been prompted to do so via the [MoveTo()](/docs/reference/engine/classes/Humanoid.md) method. If a humanoid's [WalkToPart](/docs/reference/engine/classes/Humanoid.md) is set, the goal is set by transforming `WalkToPoint` relative to the part's position and rotation. Otherwise, the humanoid will try to reach the 3D position specified by `WalkToPoint` directly. Note that simply setting the value of `WalkToPoint` is not sufficient to make a humanoid start moving toward a point. Call [MoveTo()](/docs/reference/engine/classes/Humanoid.md) to initiate movement. ### Property: Humanoid.CollisionType ```json { "type": "HumanoidCollisionType", "access": "ReadOnly", "security": { "read": "None", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Behavior" } ``` This property selects the [HumanoidCollisionType](/docs/reference/engine/enums/HumanoidCollisionType.md) for R15 and Rthro non-player characters. ### Property: Humanoid.maxHealth ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Game", "capabilities": [ "AvatarBehavior" ] } ``` > **Deprecated:** This deprecated property is a variant of [Humanoid.MaxHealth](/docs/reference/engine/classes/Humanoid.md) which should be used instead. ### Property: Humanoid.LeftLeg *(hidden)* ```json { "type": "BasePart", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarBehavior" ] } ``` > **Deprecated:** This instance only works with the old R6 rig. It will not work with the R15 rig and should not be used in new work not using the R6 rig. A reference to the humanoid's _Left Leg_ part. The value of this property will always be `nil` if the humanoid's [RigType](/docs/reference/engine/enums/RigType.md) is set to R15. ### Property: Humanoid.RightLeg *(hidden)* ```json { "type": "BasePart", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarBehavior" ] } ``` > **Deprecated:** This instance only works with the old R6 rig. It will not work with the R15 rig and should not be used in new work not using the R6 rig. A reference to the humanoid's _Right Leg_ part. The value of this property will always be `nil` if the humanoid's RigType is set to R15. ### Property: Humanoid.Torso *(hidden)* ```json { "type": "BasePart", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarBehavior" ] } ``` > **Deprecated:** This instance only works with the old R6 rig. It will not work with the R15 rig and should not be used in new work not using the R6 rig. A reference to a humanoid's root driving part. Contrary to the name of this property, it will only point to the Torso part if a humanoid doesn't have a HumanoidRootPart. ## Methods ### Method: Humanoid:AddAccessory **Signature:** `Humanoid:AddAccessory(accessory: Instance): ()` This method attaches the specified [Accessory](/docs/reference/engine/classes/Accessory.md) to the humanoid's parent. When this method is called, an [Accessory](/docs/reference/engine/classes/Accessory.md) is attached to the character by searching for an [Attachment](/docs/reference/engine/classes/Attachment.md) in the humanoid's parent that shares the same name as an [Attachment](/docs/reference/engine/classes/Attachment.md) in the accessory's **Handle** [Part](/docs/reference/engine/classes/Part.md). If one is found, the **Handle** part will be connected to the parent of the [Attachment](/docs/reference/engine/classes/Attachment.md) using a [Weld](/docs/reference/engine/classes/Weld.md), and the weld will be configured so the [Attachments](/docs/reference/engine/classes/Attachment.md) occupy the same space. If the required [Attachment](/docs/reference/engine/classes/Attachment.md) can not be found, then the [Accessory](/docs/reference/engine/classes/Accessory.md) will remain parented to the humanoid's parent but it will be unattached. Typically, accessory welds are created on the server, but they can be created on the client under certain circumstances. In these situations, client-sided calls to [AddAccessory()](/docs/reference/engine/classes/Humanoid.md) may not always produce the desired behavior and you can use [BuildRigFromAttachments()](/docs/reference/engine/classes/Humanoid.md) to force the expected weld creation. *Security: None · Thread Safety: Unsafe · Capabilities: AvatarAppearance* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `accessory` | `Instance` | | The [Accessory](/docs/reference/engine/classes/Accessory.md) to be attached. | **Returns:** `()` **[Humanoid] AddAccessory Example** This script generates the "Clockwork's Shades" Accessory from scratch, and then attaches it to the player's character using `Humanoid.AddAccessory` You should paste this code into a regular script, and then parent it inside of the `StarterPlayer`'s `StarterCharacterScripts` folder. ```lua local playerModel = script.Parent local humanoid = playerModel:WaitForChild("Humanoid") local clockworksShades = Instance.new("Accessory") clockworksShades.Name = "ClockworksShades" local handle = Instance.new("Part") handle.Name = "Handle" handle.Size = Vector3.new(1, 1.6, 1) handle.Parent = clockworksShades local faceFrontAttachment = Instance.new("Attachment") faceFrontAttachment.Name = "FaceFrontAttachment" faceFrontAttachment.Position = Vector3.new(0, -0.24, -0.45) faceFrontAttachment.Parent = handle local mesh = Instance.new("SpecialMesh") mesh.Name = "Mesh" mesh.Scale = Vector3.new(1, 1.3, 1) mesh.MeshId = "rbxassetid://1577360" mesh.TextureId = "rbxassetid://1577349" mesh.Parent = handle humanoid:AddAccessory(clockworksShades) ``` ### Method: Humanoid:ApplyDescriptionAsync **Signature:** `Humanoid:ApplyDescriptionAsync(humanoidDescription: HumanoidDescription, assetTypeVerification?: AssetTypeVerification): ()` This yielding method makes the character's look match that of the passed in [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). A copy of the passed [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) is cached as the [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) for the [Humanoid](/docs/reference/engine/classes/Humanoid.md). This method is optimized through making the assumption that only this method is used to change the appearance of the character, and no changes are made through other means between calls. If changes are made to the character between calls, then this method may not make the character reflect the passed in [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) accurately. If you want to use this method in conjunction with other means of updating the character, [Humanoid:ApplyDescriptionResetAsync()](/docs/reference/engine/classes/Humanoid.md) will always ensure the character reflects the passed in [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). #### See Also - [Humanoid:GetAppliedDescription()](/docs/reference/engine/classes/Humanoid.md) which returns the [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) currently applied to the humanoid. - [Players:GetHumanoidDescriptionFromUserIdAsync()](/docs/reference/engine/classes/Players.md) which returns a [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) describing the avatar for the passed in user. - [Players:GetHumanoidDescriptionFromOutfitIdAsync()](/docs/reference/engine/classes/Players.md) which returns a [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) whose parameters are initialized to match that of the passed in server-side outfit asset. - [Player:LoadCharacterWithHumanoidDescriptionAsync()](/docs/reference/engine/classes/Player.md) which spawns a player with the look from the passed in [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AvatarAppearance* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `humanoidDescription` | `HumanoidDescription` | | The [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) instance which you want to set the character to match. | | `assetTypeVerification` | `AssetTypeVerification` | `Default` | The asset type verification mode. | **Returns:** `()` ### Method: Humanoid:ApplyDescriptionResetAsync **Signature:** `Humanoid:ApplyDescriptionResetAsync(humanoidDescription: HumanoidDescription, assetTypeVerification?: AssetTypeVerification): ()` This yielding method makes the character's look match that of the passed in [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md), even after external changes. A copy of the passed [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) is cached as the [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) for the [Humanoid](/docs/reference/engine/classes/Humanoid.md). This method will always ensure the character reflects the passed in [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md), even if changes have been made to the character not using the [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) system (for example not using [ApplyDescriptionResetAsync()](/docs/reference/engine/classes/Humanoid.md) or [ApplyDescriptionAsync()](/docs/reference/engine/classes/Humanoid.md)). This is in contrast to [ApplyDescriptionAsync()](/docs/reference/engine/classes/Humanoid.md) which is optimized and may incorrectly apply a [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) if the character has been changed by means other than through the [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) system. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AvatarAppearance* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `humanoidDescription` | `HumanoidDescription` | | The [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) instance which you want to set the character to match. | | `assetTypeVerification` | `AssetTypeVerification` | `Default` | The asset type verification mode. | **Returns:** `()` ### Method: Humanoid:BuildRigFromAttachments **Signature:** `Humanoid:BuildRigFromAttachments(): ()` This method assembles a tree of [Motor6D](/docs/reference/engine/classes/Motor6D.md) joints for the [Humanoid](/docs/reference/engine/classes/Humanoid.md). [Motor6D](/docs/reference/engine/classes/Motor6D.md) joints are required for the playback of [Animations](/docs/reference/engine/classes/Animation.md). Starting from the humanoid's [RootPart](/docs/reference/engine/classes/Humanoid.md), this method collects all [Attachments](/docs/reference/engine/classes/Attachment.md) parented in the current part whose name ends with **RigAttachment**. It then searches for a matching attachment in the character that shares the same name as the attachment. Using those two attachments, a [Motor6D](/docs/reference/engine/classes/Motor6D.md) joint is generated based on the parts associated with the two attachments and the [CFrame](/docs/reference/engine/classes/Attachment.md) of the attachments. [Humanoid:BuildRigFromAttachments()](/docs/reference/engine/classes/Humanoid.md) also scales the character and sets body colors. *Security: None · Thread Safety: Unsafe · Capabilities: AvatarBehavior* **Returns:** `()` **Lua Port of BuildRigFromAttachments** A Lua port of the Humanoid's BuildRigFromAttachments function (AvatarJointUpgrade path). ```lua local function createJoint(jointName, att0, att1) local partForJoint = att1.Parent while partForJoint and not partForJoint:IsA("BasePart") do partForJoint = partForJoint.Parent end local oldJoint = partForJoint:FindFirstChild(jointName) if oldJoint and oldJoint:IsA("AnimationConstraint") then oldJoint.Attachment0 = att0 oldJoint.Attachment1 = att1 oldJoint.Parent = partForJoint else local ac = Instance.new("AnimationConstraint") ac.Name = jointName ac.IsKinematic = true ac.Attachment0 = att0 ac.Attachment1 = att1 ac.Parent = partForJoint end end local function collectRigAttachments(part) local rigAttachments = {} for _, child in pairs(part:GetChildren()) do if child:IsA("Attachment") and child.Name:find("RigAttachment$") then rigAttachments[child.Name] = child end end -- Bones may contain deeper RigAttachments that override the part-level -- ones (R15+ extended skeleton). for _, child in pairs(part:GetChildren()) do if child:IsA("Bone") then for _, descendant in pairs(child:GetDescendants()) do if descendant:IsA("Attachment") and descendant.Name:find("RigAttachment$") then rigAttachments[descendant.Name] = descendant end end end end return rigAttachments end local function buildJointsFromAttachments(part, characterParts, visitedParts) if not part or visitedParts[part] then return end visitedParts[part] = true local rigAttachments = collectRigAttachments(part) for attachmentName, attachment in pairs(rigAttachments) do local jointName = attachmentName:sub(1, #attachmentName - #"RigAttachment") if not part:FindFirstChild(jointName) then for _, characterPart in pairs(characterParts) do if not visitedParts[characterPart] then local matchingAttachment = characterPart:FindFirstChild(attachmentName) if matchingAttachment and matchingAttachment:IsA("Attachment") then createJoint(jointName, attachment, matchingAttachment) buildJointsFromAttachments(characterPart, characterParts, visitedParts) break end end end end end end local function buildRigFromAttachments(humanoid) local rootPart = humanoid.RootPart assert(rootPart, "Humanoid has no HumanoidRootPart.") local characterParts = {} for _, descendant in ipairs(humanoid.Parent:GetDescendants()) do if descendant:IsA("BasePart") then table.insert(characterParts, descendant) end end local visitedParts = {} buildJointsFromAttachments(rootPart, characterParts, visitedParts) end local humanoid = script.Parent:WaitForChild("Humanoid") buildRigFromAttachments(humanoid) ``` **R15 Package Importer** A script that generates an R15 character from scratch using a package's assetId. ```lua local AssetService = game:GetService("AssetService") local InsertService = game:GetService("InsertService") local MarketplaceService = game:GetService("MarketplaceService") local PACKAGE_ASSET_ID = 193700907 -- Circuit Breaker local function addAttachment(part, name, position, orientation) local attachment = Instance.new("Attachment") attachment.Name = name attachment.Parent = part if position then attachment.Position = position end if orientation then attachment.Orientation = orientation end return attachment end local function createBaseCharacter() local character = Instance.new("Model") local humanoid = Instance.new("Humanoid") humanoid.Parent = character local rootPart = Instance.new("Part") rootPart.Name = "HumanoidRootPart" rootPart.Size = Vector3.new(2, 2, 1) rootPart.Transparency = 1 rootPart.Parent = character addAttachment(rootPart, "RootRigAttachment") local head = Instance.new("Part") head.Name = "Head" head.Size = Vector3.new(2, 1, 1) head.Parent = character local headMesh = Instance.new("SpecialMesh") headMesh.Scale = Vector3.new(1.25, 1.25, 1.25) headMesh.MeshType = Enum.MeshType.Head headMesh.Parent = head local face = Instance.new("Decal") face.Name = "face" face.Texture = "rbxasset://textures/face.png" face.Parent = head addAttachment(head, "FaceCenterAttachment") addAttachment(head, "FaceFrontAttachment", Vector3.new(0, 0, -0.6)) addAttachment(head, "HairAttachment", Vector3.new(0, 0.6, 0)) addAttachment(head, "HatAttachment", Vector3.new(0, 0.6, 0)) addAttachment(head, "NeckRigAttachment", Vector3.new(0, -0.5, 0)) return character, humanoid end local function createR15Package(packageAssetId) local packageAssetInfo = MarketplaceService:GetProductInfoAsync(packageAssetId) local character, humanoid = createBaseCharacter() character.Name = packageAssetInfo.Name local assetIds = AssetService:GetAssetIdsForPackageAsync(packageAssetId) for _, assetId in pairs(assetIds) do local limb = InsertService:LoadAsset(assetId) local r15 = limb:FindFirstChild("R15") if r15 then for _, part in pairs(r15:GetChildren()) do part.Parent = character end else for _, child in pairs(limb:GetChildren()) do child.Parent = character end end end humanoid:BuildRigFromAttachments() return character end local r15Package = createR15Package(PACKAGE_ASSET_ID) r15Package.Parent = workspace ``` ### Method: Humanoid:ChangeState **Signature:** `Humanoid:ChangeState(state?: HumanoidStateType): ()` This method causes the [Humanoid](/docs/reference/engine/classes/Humanoid.md) to enter the given [HumanoidStateType](/docs/reference/engine/enums/HumanoidStateType.md), describing the activity the [Humanoid](/docs/reference/engine/classes/Humanoid.md) is currently doing. Please review the [HumanoidStateType](/docs/reference/engine/enums/HumanoidStateType.md) page for more information on the particular states, as some have unintuitive names. For example, [HumanoidStateType.Running](/docs/reference/engine/enums/HumanoidStateType.md) describes a state where the humanoid's legs are on the ground, including when stationary. Due to the default behavior of the [Humanoid](/docs/reference/engine/classes/Humanoid.md), some states will automatically be changed when set. For example: - Setting the state to [HumanoidStateType.Swimming](/docs/reference/engine/enums/HumanoidStateType.md) when the humanoid is not in the water will cause it to be automatically set to [HumanoidStateType.GettingUp](/docs/reference/engine/enums/HumanoidStateType.md). - As it is unused, setting the state to [HumanoidStateType.PlatformStanding](/docs/reference/engine/enums/HumanoidStateType.md) will cause the humanoid state to be automatically set to [HumanoidStateType.Running](/docs/reference/engine/enums/HumanoidStateType.md). Note that in order to set the [Humanoid](/docs/reference/engine/classes/Humanoid.md) state using this method, you must do so from a [LocalScript](/docs/reference/engine/classes/LocalScript.md) and the client must have [network ownership](/docs/en-us/physics/network-ownership.md) of the [Player.Character](/docs/reference/engine/classes/Player.md). Alternatively, you can call this method from a server-side [Script](/docs/reference/engine/classes/Script.md), but the server must have network ownership of the player character. See also [Humanoid:SetStateEnabled()](/docs/reference/engine/classes/Humanoid.md) to enable or disable a particular state, and [Humanoid:GetState()](/docs/reference/engine/classes/Humanoid.md) to get the current humanoid state. *Security: None · Thread Safety: Unsafe · Capabilities: AvatarBehavior · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `state` | `HumanoidStateType` | `None` | The [HumanoidStateType](/docs/reference/engine/enums/HumanoidStateType.md) that the [Humanoid](/docs/reference/engine/classes/Humanoid.md) is to perform. | **Returns:** `()` **Double Jump** This code, when placed inside a [LocalScript](/docs/reference/engine/classes/LocalScript.md) in [StarterPlayer.StarterCharacterScripts](/docs/reference/engine/classes/StarterPlayer.md), will allow the player's character to perform a double jump. ```lua local UserInputService = game:GetService("UserInputService") local character = script.Parent local humanoid = character:WaitForChild("Humanoid") local doubleJumpEnabled = false humanoid.StateChanged:Connect(function(_oldState, newState) if newState == Enum.HumanoidStateType.Jumping then if not doubleJumpEnabled then task.wait(0.2) if humanoid:GetState() == Enum.HumanoidStateType.Freefall then doubleJumpEnabled = true end end elseif newState == Enum.HumanoidStateType.Landed then doubleJumpEnabled = false end end) UserInputService.InputBegan:Connect(function(inputObject) if inputObject.KeyCode == Enum.KeyCode.Space then if doubleJumpEnabled then if humanoid:GetState() ~= Enum.HumanoidStateType.Jumping then humanoid:ChangeState(Enum.HumanoidStateType.Jumping) task.spawn(function() doubleJumpEnabled = false end) end end end end) ``` ### Method: Humanoid:EquipTool **Signature:** `Humanoid:EquipTool(tool: Instance): ()` This method makes the [Humanoid](/docs/reference/engine/classes/Humanoid.md) equip the given [Tool](/docs/reference/engine/classes/Tool.md). When this method is called, the [Humanoid](/docs/reference/engine/classes/Humanoid.md) will first automatically unequip all [Tools](/docs/reference/engine/classes/Tool.md) that it currently has equipped. Although they will be equipped, [Tools](/docs/reference/engine/classes/Tool.md) for which [Tool.RequiresHandle](/docs/reference/engine/classes/Tool.md) is `true` will not function if they have no handle, regardless if this method is used to equip them or not. See also [Humanoid:UnequipTools()](/docs/reference/engine/classes/Humanoid.md). *Security: None · Thread Safety: Unsafe · Capabilities: AvatarBehavior* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `tool` | `Instance` | | The [Tool](/docs/reference/engine/classes/Tool.md) to equip. | **Returns:** `()` ### Method: Humanoid:GetAccessories **Signature:** `Humanoid:GetAccessories(): Array` This method returns an array of [Accessory](/docs/reference/engine/classes/Accessory.md) objects that the humanoid's parent is currently wearing. All such [Accessory](/docs/reference/engine/classes/Accessory.md) objects will be included, regardless of whether they're attached or not. If the [Humanoid](/docs/reference/engine/classes/Humanoid.md) has no [Accessory](/docs/reference/engine/classes/Accessory.md) objects, an empty array will be returned. See also [Humanoid:AddAccessory()](/docs/reference/engine/classes/Humanoid.md) to attach an [Accessory](/docs/reference/engine/classes/Accessory.md) to a humanoid's parent. *Security: None · Thread Safety: Unsafe · Capabilities: AvatarAppearance* **Returns:** `Array` — An array of [Accessory](/docs/reference/engine/classes/Accessory.md) objects that are parented to the humanoid's parent. **Remove Accessories After Loading** This code sample will wait for accessories to fully load, print out how many there are, and then destroy them all. ```lua local Players = game:GetService("Players") local function onPlayerAddedAsync(player) local connection = player.CharacterAppearanceLoaded:Connect(function(character) -- All accessories have loaded at this point local humanoid = character:FindFirstChildOfClass("Humanoid") local numAccessories = #humanoid:GetAccessories() print(("Destroying %d accessories for %s"):format(numAccessories, player.Name)) humanoid:RemoveAccessories() end) -- Make sure we disconnect our connection to the player after they leave -- to allow the player to get garbage collected player.AncestryChanged:Wait() connection:Disconnect() end for _, player in Players:GetPlayers() do task.spawn(onPlayerAddedAsync, player) end Players.PlayerAdded:Connect(onPlayerAddedAsync) ``` ### Method: Humanoid:GetAppliedDescription **Signature:** `Humanoid:GetAppliedDescription(): HumanoidDescription` This method returns a copy of the humanoid's cached [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) which describes its current look. This can be used to quickly determine a character's look and to assign their look to other characters using the [Humanoid:ApplyDescriptionAsync()](/docs/reference/engine/classes/Humanoid.md) method. #### See Also - [Players:GetHumanoidDescriptionFromUserIdAsync()](/docs/reference/engine/classes/Players.md) which returns a [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) describing the avatar for the passed in user. - [Players:GetHumanoidDescriptionFromOutfitIdAsync()](/docs/reference/engine/classes/Players.md) which returns a [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) whose parameters are initialized to match that of the passed in server-side outfit asset. - [Player:LoadCharacterWithHumanoidDescriptionAsync()](/docs/reference/engine/classes/Player.md) which spawns a player with the look from the passed in [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). *Security: None · Thread Safety: Unsafe · Capabilities: AvatarAppearance* **Returns:** `HumanoidDescription` ### Method: Humanoid:GetBodyPartR15 **Signature:** `Humanoid:GetBodyPartR15(part: Instance): BodyPartR15` This method returns what [BodyPartR15](/docs/reference/engine/enums/BodyPartR15.md) a [Part](/docs/reference/engine/classes/Part.md) is, or [BodyPartR15.Unknown](/docs/reference/engine/enums/BodyPartR15.md) if the part is not an R15 body part. This method allows developers to retrieve player body parts independent of what the actual body part names are, instead returning an enum. It can be used in conjunction with [Humanoid:ReplaceBodyPartR15()](/docs/reference/engine/classes/Humanoid.md). For example, if a player's body part touches something, this function will return get a part instance. Developers can then look up what part of the body that was, like head or arm. Then depending on what that part was, developers can either perform some gameplay action or replace that part with some other part - perhaps showing damage. This method can be useful for games where hit location is important. For example, it can be used to determine if a player is hit in the leg and then slow them down based on the injury. *Security: None · Thread Safety: Unsafe · Capabilities: AvatarBehavior* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `part` | `Instance` | | The specified part being checked to see if it is an R15 body part. | **Returns:** `BodyPartR15` — The specified part's R15 body part type or unknown if the part is not a body part. ### Method: Humanoid:GetLimb **Signature:** `Humanoid:GetLimb(part: Instance): Limb` This method returns the [Limb](/docs/reference/engine/enums/Limb.md) enum that is associated with the given [Part](/docs/reference/engine/classes/Part.md). It works for both R15 and R6 rigs, for example: ```lua -- For R15 print(humanoid:GetLimb(character.LeftUpperLeg)) -- Enum.Limb.LeftLeg print(humanoid:GetLimb(character.LeftLowerLeg)) -- Enum.Limb.LeftLeg print(humanoid:GetLimb(character.LeftFoot)) -- Enum.Limb.LeftLeg -- For R6 print(humanoid:GetLimb(character:FindFirstChild("Left Leg"))) -- Enum.Limb.LeftLeg ``` Note that [Humanoid:GetLimb()](/docs/reference/engine/classes/Humanoid.md) will throw an error if the part's parent is not set to the humanoid's parent. *Security: None · Thread Safety: Unsafe · Capabilities: AvatarBehavior* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `part` | `Instance` | | The [Part](/docs/reference/engine/classes/Part.md) for which the [Enum.Limb](/docs/reference/engine/enums/Limb.md) is to be retrieved. | **Returns:** `Limb` — The [Limb](/docs/reference/engine/enums/Limb.md) the part corresponds with. **Getting a Humanoid's Limbs** Put this in a LocalScript. The output will vary based on if the humanoid is R6 or R15. ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local character = player.Character or player.CharacterAdded:Wait() local humanoid = character:WaitForChild("Humanoid") for _, child in pairs(character:GetChildren()) do local limb = humanoid:GetLimb(child) if limb ~= Enum.Limb.Unknown then print(child.Name .. " is part of limb " .. limb.Name) end end ``` ### Method: Humanoid:GetMoveVelocity **Signature:** `Humanoid:GetMoveVelocity(): Vector3` Returns the Humanoid's intended movement velocity as a [Vector3](/docs/reference/engine/datatypes/Vector3.md). The result is the product of [WalkSpeed](/docs/reference/engine/classes/Humanoid.md), the intended movement direction, and the current percent walk speed. If the Humanoid is not moving, this returns `(0, 0, 0)`. *Security: None · Thread Safety: Unsafe · Capabilities: AvatarBehavior* **Returns:** `Vector3` — A [Vector3](/docs/reference/engine/datatypes/Vector3.md) representing the Humanoid's intended movement velocity in world space. ### Method: Humanoid:GetRelativeVelocityAtFloor **Signature:** `Humanoid:GetRelativeVelocityAtFloor(): Vector3` Returns the humanoid's actual physical velocity relative to the surface it is standing on as a [Vector3](/docs/reference/engine/datatypes/Vector3.md) in world-space orientation. This accounts for moving platforms by subtracting floor velocity from the humanoid's body velocity. *Security: None · Thread Safety: Unsafe · Capabilities: AvatarBehavior · Simulation Access: true* **Returns:** `Vector3` ### Method: Humanoid:GetState **Signature:** `Humanoid:GetState(): HumanoidStateType` This method returns the humanoid's current [HumanoidStateType](/docs/reference/engine/enums/HumanoidStateType.md), describing the activity the [Humanoid](/docs/reference/engine/classes/Humanoid.md) is currently doing, such as jumping or swimming. See also [Humanoid:SetStateEnabled()](/docs/reference/engine/classes/Humanoid.md) to enable or disable a particular state, and [Humanoid:ChangeState()](/docs/reference/engine/classes/Humanoid.md) to change the current humanoid state. *Security: None · Thread Safety: Safe · Capabilities: AvatarBehavior · Simulation Access: true* **Returns:** `HumanoidStateType` — The current [HumanoidStateType](/docs/reference/engine/enums/HumanoidStateType.md) of the [Humanoid](/docs/reference/engine/classes/Humanoid.md). **Double Jump** This code, when placed inside a [LocalScript](/docs/reference/engine/classes/LocalScript.md) in [StarterPlayer.StarterCharacterScripts](/docs/reference/engine/classes/StarterPlayer.md), will allow the player's character to perform a double jump. ```lua local UserInputService = game:GetService("UserInputService") local character = script.Parent local humanoid = character:WaitForChild("Humanoid") local doubleJumpEnabled = false humanoid.StateChanged:Connect(function(_oldState, newState) if newState == Enum.HumanoidStateType.Jumping then if not doubleJumpEnabled then task.wait(0.2) if humanoid:GetState() == Enum.HumanoidStateType.Freefall then doubleJumpEnabled = true end end elseif newState == Enum.HumanoidStateType.Landed then doubleJumpEnabled = false end end) UserInputService.InputBegan:Connect(function(inputObject) if inputObject.KeyCode == Enum.KeyCode.Space then if doubleJumpEnabled then if humanoid:GetState() ~= Enum.HumanoidStateType.Jumping then humanoid:ChangeState(Enum.HumanoidStateType.Jumping) task.spawn(function() doubleJumpEnabled = false end) end end end end) ``` ### Method: Humanoid:GetStateEnabled **Signature:** `Humanoid:GetStateEnabled(state: HumanoidStateType): boolean` The GetStateEnabled method returns whether a [HumanoidStateType](/docs/reference/engine/enums/HumanoidStateType.md) is enabled for the [Humanoid](/docs/reference/engine/classes/Humanoid.md). The humanoid state describes the activity the humanoid is currently doing. When a particular [HumanoidStateType](/docs/reference/engine/enums/HumanoidStateType.md) is disabled, the humanoid can never enter that state. This is true regardless if the attempt to change state is made using [Humanoid:ChangeState()](/docs/reference/engine/classes/Humanoid.md) or Roblox internal humanoid code. See also: - For an event that fires when a humanoid state is enabled or disabled see [Humanoid.StateEnabledChanged](/docs/reference/engine/classes/Humanoid.md) - To enable or disable a [Humanoid](/docs/reference/engine/classes/Humanoid.md) state use [Humanoid:SetStateEnabled()](/docs/reference/engine/classes/Humanoid.md) *Security: None · Thread Safety: Safe · Capabilities: AvatarBehavior · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `state` | `HumanoidStateType` | | The given [HumanoidStateType](/docs/reference/engine/enums/HumanoidStateType.md). | **Returns:** `boolean` — Whether the given [HumanoidStateType](/docs/reference/engine/enums/HumanoidStateType.md) is enabled. **Setting and Getting Humanoid States** The code below sets the value of the [humanoid jumping state](/docs/reference/engine/enums/HumanoidStateType.md) to _false_ using [Humanoid:SetStateEnabled()](/docs/reference/engine/classes/Humanoid.md) and then retrieves and prints the value of this state (_false_) using [Humanoid:GetStateEnabled()](/docs/reference/engine/classes/Humanoid.md). ```lua local humanoid = script.Parent:WaitForChild("Humanoid") -- Set state humanoid:SetStateEnabled(Enum.HumanoidStateType.Jumping, false) -- Get state print(humanoid:GetStateEnabled(Enum.HumanoidStateType.Jumping)) -- false ``` ### Method: Humanoid:Move **Signature:** `Humanoid:Move(moveDirection: Vector3, relativeToCamera?: boolean): ()` This method causes the [Humanoid](/docs/reference/engine/classes/Humanoid.md) to walk in the given [Vector3](/docs/reference/engine/datatypes/Vector3.md) direction. By default, the direction is in world terms, but if the `relativeToCamera` parameter is `true`, the direction is relative to the [CFrame](/docs/reference/engine/datatypes/CFrame.md) of the [CurrentCamera](/docs/reference/engine/classes/Workspace.md). As the negative **Z** direction is considered "forwards" in Roblox, the following code will make the humanoid walk in the direction of the [CurrentCamera](/docs/reference/engine/classes/Workspace.md). ```lua humanoid:Move(Vector3.new(0, 0, -1), true) ``` When this method is called, the [Humanoid](/docs/reference/engine/classes/Humanoid.md) will move until the method is called again. However, this method will be overwritten in the next frame by Roblox's default character control script. This can be avoided by either calling this function every frame using [RunService:BindToRenderStep()](/docs/reference/engine/classes/RunService.md) (see example), or overwriting the control scripts in [StarterPlayerScripts](/docs/reference/engine/classes/StarterPlayerScripts.md). This method can be called on the server, but this should only be done when the server has [network ownership](/docs/en-us/physics/network-ownership.md) of the humanoid's assembly. See also [Humanoid:MoveTo()](/docs/reference/engine/classes/Humanoid.md) which makes a[Humanoid](/docs/reference/engine/classes/Humanoid.md) walk to a point, and [Player:Move()](/docs/reference/engine/classes/Player.md) which effectively calls this function. *Security: None · Thread Safety: Unsafe · Capabilities: AvatarBehavior · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `moveDirection` | `Vector3` | | The direction to walk in. | | `relativeToCamera` | `boolean` | `false` | Set to `true` if the `moveDirection` parameter should be taken as relative to the [CurrentCamera](/docs/reference/engine/classes/Workspace.md). | **Returns:** `()` **Moving a Humanoid Forwards** This code sample uses the [Humanoid:Move()](/docs/reference/engine/classes/Humanoid.md) function to make the player's [Character](/docs/reference/engine/classes/Player.md) walk in the direction of the [Camera](/docs/reference/engine/classes/Camera.md). [RunService:BindToRenderStep()](/docs/reference/engine/classes/RunService.md) is required here as the default control scripts will overwrite the player's movement every frame. To run this sample, place it inside a [LocalScript](/docs/reference/engine/classes/LocalScript.md) parented to [StarterCharacterScripts](/docs/reference/engine/classes/StarterCharacterScripts.md). ```lua local RunService = game:GetService("RunService") local Players = game:GetService("Players") local player = Players.LocalPlayer RunService:BindToRenderStep("move", Enum.RenderPriority.Character.Value + 1, function() if player.Character then local humanoid = player.Character:FindFirstChild("Humanoid") if humanoid then humanoid:Move(Vector3.new(0, 0, -1), true) end end end) ``` ### Method: Humanoid:MoveTo **Signature:** `Humanoid:MoveTo(location: Vector3, part?: Instance): ()` This method causes the [Humanoid](/docs/reference/engine/classes/Humanoid.md) to attempt to walk to a given location by setting the [WalkToPoint](/docs/reference/engine/classes/Humanoid.md) and [WalkToPart](/docs/reference/engine/classes/Humanoid.md) properties, corresponding to the `location` and `part` parameters. If the `part` parameter is specified, the [Humanoid](/docs/reference/engine/classes/Humanoid.md) will still attempt to walk to the `location` parameter's point. However, if the `part` moves, the point will move to be at the same position **relative to the part**. Note that the movement operation will time out after 8 seconds if the humanoid doesn't reach its goal (this timeout exists so that humanoids do not get stuck waiting for [MoveToFinished](/docs/reference/engine/classes/Humanoid.md) to fire). If you don't want this to happen, call `MoveTo()` at a repeated interval so that the timeout keeps resetting. [MoveTo()](/docs/reference/engine/classes/Humanoid.md) ends if any of the following conditions apply: - The character arrives at its destination, assuming a ~1 stud threshold to account for various humanoid speeds and framerates. - The character gets stuck and the timer expires. - The value of either [WalkToPoint](/docs/reference/engine/classes/Humanoid.md) or [WalkToPart](/docs/reference/engine/classes/Humanoid.md) changes. - A script calls [Move()](/docs/reference/engine/classes/Humanoid.md) with a new `moveDirection` parameter. - A script changes the [CFrame](/docs/reference/engine/classes/BasePart.md) property of the humanoid's [RootPart](/docs/reference/engine/classes/Humanoid.md). *Security: None · Thread Safety: Unsafe · Capabilities: AvatarBehavior · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `location` | `Vector3` | | The position to set [Humanoid.WalkToPoint](/docs/reference/engine/classes/Humanoid.md) to. | | `part` | `Instance` | `nil` | The [BasePart](/docs/reference/engine/classes/BasePart.md) to set [Humanoid.WalkToPart](/docs/reference/engine/classes/Humanoid.md) to. | **Returns:** `()` **Humanoid MoveTo Without Time out** This code sample includes a function that avoids the timeout on [Humanoid:MoveTo()](/docs/reference/engine/classes/Humanoid.md) by calling [Humanoid:MoveTo()](/docs/reference/engine/classes/Humanoid.md) again before the timeout elapses. It also includes an optional `andThen` parameter for which you can pass a function to be called when the humanoid reaches its destination. ```lua local function moveTo(humanoid, targetPoint, andThen) local targetReached = false -- listen for the humanoid reaching its target local connection connection = humanoid.MoveToFinished:Connect(function(reached) targetReached = true connection:Disconnect() connection = nil if andThen then andThen(reached) end end) -- start walking humanoid:MoveTo(targetPoint) -- execute on a new thread so as to not yield function task.spawn(function() while not targetReached do -- does the humanoid still exist? if not (humanoid and humanoid.Parent) then break end -- has the target changed? if humanoid.WalkToPoint ~= targetPoint then break end -- refresh the timeout humanoid:MoveTo(targetPoint) task.wait(6) end -- disconnect the connection if it is still connected if connection then connection:Disconnect() connection = nil end end) end local function andThen(reached) print((reached and "Destination reached!") or "Failed to reach destination!") end moveTo(script.Parent:WaitForChild("Humanoid"), Vector3.new(50, 0, 50), andThen) ``` ### Method: Humanoid:PlayEmoteAsync **Signature:** `Humanoid:PlayEmoteAsync(emoteName: string): boolean` If the emote could not be played because the emoteName is not found in the HumanoidDescription, this method will give an error. The method will return true to indicate that the emote was played successfully. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AvatarBehavior* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `emoteName` | `string` | | name of the emote to play. | **Returns:** `boolean` — successfully played. ### Method: Humanoid:RemoveAccessories **Signature:** `Humanoid:RemoveAccessories(): ()` This method removes all [Accessory](/docs/reference/engine/classes/Accessory.md) objects worn by the humanoid's parent. For player [Characters](/docs/reference/engine/classes/Player.md), this will remove all hats and other accessories. This method removes [Accessory](/docs/reference/engine/classes/Accessory.md) object by calling [Instance:Destroy()](/docs/reference/engine/classes/Instance.md) on them, meaning the [Parent](/docs/reference/engine/classes/Instance.md) of the accessories are set to `nil` and locked. See also [Humanoid:AddAccessory()](/docs/reference/engine/classes/Humanoid.md) to attach an [Accessory](/docs/reference/engine/classes/Accessory.md), and [Humanoid:GetAccessories()](/docs/reference/engine/classes/Humanoid.md) to get all [Accessory](/docs/reference/engine/classes/Accessory.md) objects belonging to a [Humanoid](/docs/reference/engine/classes/Humanoid.md). *Security: None · Thread Safety: Unsafe · Capabilities: AvatarAppearance* **Returns:** `()` **Remove Accessories After Loading** This code sample will wait for accessories to fully load, print out how many there are, and then destroy them all. ```lua local Players = game:GetService("Players") local function onPlayerAddedAsync(player) local connection = player.CharacterAppearanceLoaded:Connect(function(character) -- All accessories have loaded at this point local humanoid = character:FindFirstChildOfClass("Humanoid") local numAccessories = #humanoid:GetAccessories() print(("Destroying %d accessories for %s"):format(numAccessories, player.Name)) humanoid:RemoveAccessories() end) -- Make sure we disconnect our connection to the player after they leave -- to allow the player to get garbage collected player.AncestryChanged:Wait() connection:Disconnect() end for _, player in Players:GetPlayers() do task.spawn(onPlayerAddedAsync, player) end Players.PlayerAdded:Connect(onPlayerAddedAsync) ``` ### Method: Humanoid:ReplaceBodyPartR15 **Signature:** `Humanoid:ReplaceBodyPartR15(bodyPart: BodyPartR15, part: BasePart): boolean` Dynamically replaces a R15/Rthro limb part in a Humanoid with a different part. The part is automatically scaled as normal. This method is useful for modifying characters during gameplay or building characters from a base rig. The related method [GetBodyPartR15](/docs/reference/engine/classes/Humanoid.md) can come in handy when using this method. The name of the part passed in should match with the name of the BodyPartR15 Enum passed in. *Security: None · Thread Safety: Unsafe · Capabilities: AvatarAppearance* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `bodyPart` | `BodyPartR15` | | The body part to replace. [BodyPartR15.Unknown](/docs/reference/engine/enums/BodyPartR15.md) will fail. | | `part` | `BasePart` | | The [Part](/docs/reference/engine/classes/Part.md) [Instance](/docs/reference/engine/classes/Instance.md) which will be parented to the character. | **Returns:** `boolean` ### Method: Humanoid:SetStateEnabled **Signature:** `Humanoid:SetStateEnabled(state: HumanoidStateType, enabled: boolean): ()` This method sets whether a given [HumanoidStateType](/docs/reference/engine/enums/HumanoidStateType.md) is enabled for the [Humanoid](/docs/reference/engine/classes/Humanoid.md). When a particular [HumanoidStateType](/docs/reference/engine/enums/HumanoidStateType.md) is disabled, the [Humanoid](/docs/reference/engine/classes/Humanoid.md) can never enter that state. This is true regardless if the attempt to change state is made using [Humanoid:ChangeState()](/docs/reference/engine/classes/Humanoid.md) or Roblox internal [Humanoid](/docs/reference/engine/classes/Humanoid.md) code. Note that using `SetStateEnabled()` on the server does not replicate the change to the client, nor vice-versa. *Security: None · Thread Safety: Unsafe · Capabilities: AvatarBehavior · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `state` | `HumanoidStateType` | | The [HumanoidStateType](/docs/reference/engine/enums/HumanoidStateType.md) to be enabled or disabled. | | `enabled` | `boolean` | | `true` if `state` is to be enabled, `false` if `state` is to be disabled. | **Returns:** `()` **Jump Cooldown** The following sample will require a one second cooldown after a `Humanoid` has landed before it is able to jump again. To try this sample, place it inside a `LocalScript` in `StarterCharacterScripts|StarterPlayer.StarterCharacterScripts`. ```lua local character = script.Parent local JUMP_DEBOUNCE = 1 local humanoid = character:WaitForChild("Humanoid") local isJumping = false humanoid.StateChanged:Connect(function(_oldState, newState) if newState == Enum.HumanoidStateType.Jumping then if not isJumping then isJumping = true humanoid:SetStateEnabled(Enum.HumanoidStateType.Jumping, false) end elseif newState == Enum.HumanoidStateType.Landed then if isJumping then isJumping = false task.wait(JUMP_DEBOUNCE) humanoid:SetStateEnabled(Enum.HumanoidStateType.Jumping, true) end end end) ``` ### Method: Humanoid:TakeDamage **Signature:** `Humanoid:TakeDamage(amount: float): ()` This method lowers the [Humanoid.Health](/docs/reference/engine/classes/Humanoid.md) of the [Humanoid](/docs/reference/engine/classes/Humanoid.md) by the given _amount_ if it is not protected by a [ForceField](/docs/reference/engine/classes/ForceField.md) This method accepts negative values for the _amount_ parameter. This will increase the humanoid's [Humanoid.Health](/docs/reference/engine/classes/Humanoid.md). However this will only have an effect if no [ForceField](/docs/reference/engine/classes/ForceField.md) is present. #### How do ForceFields protect against TakeDamage A [Humanoid](/docs/reference/engine/classes/Humanoid.md) is considered protected by a [ForceField](/docs/reference/engine/classes/ForceField.md) if a [ForceField](/docs/reference/engine/classes/ForceField.md) meets one of the following criteria: - The [ForceField](/docs/reference/engine/classes/ForceField.md) shares the same [Instance.Parent](/docs/reference/engine/classes/Instance.md) as the [Humanoid](/docs/reference/engine/classes/Humanoid.md) - The [ForceField](/docs/reference/engine/classes/ForceField.md) is parented to the [Humanoid.RootPart](/docs/reference/engine/classes/Humanoid.md) of the [Humanoid](/docs/reference/engine/classes/Humanoid.md) - The [ForceField](/docs/reference/engine/classes/ForceField.md) is parented to an ancestor of the [Humanoid](/docs/reference/engine/classes/Humanoid.md) other than the [Workspace](/docs/reference/engine/classes/Workspace.md) To do damage to a [Humanoid](/docs/reference/engine/classes/Humanoid.md) irrespective of any [ForceFields](/docs/reference/engine/classes/ForceField.md) present, set [Humanoid.Health](/docs/reference/engine/classes/Humanoid.md) directly. For more information on how [ForceFields](/docs/reference/engine/classes/ForceField.md) protect [Humanoids](/docs/reference/engine/classes/Humanoid.md) see the [ForceField](/docs/reference/engine/classes/ForceField.md) page. *Security: None · Thread Safety: Unsafe · Capabilities: AvatarBehavior* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `amount` | `float` | | The damage, or amount to be deduced from the [Humanoid.Health](/docs/reference/engine/classes/Humanoid.md). | **Returns:** `()` **Damaging a Humanoid** This code, put in a `LocalScript`, would make the local player take 99 damage _only_ if a ForceField wasn't present. ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local character = player.Character or player.CharacterAdded:Wait() local humanoid = character:WaitForChild("Humanoid") humanoid:TakeDamage(99) ``` ### Method: Humanoid:UnequipTools **Signature:** `Humanoid:UnequipTools(): ()` This method unequips any [Tool](/docs/reference/engine/classes/Tool.md) currently equipped by the [Humanoid](/docs/reference/engine/classes/Humanoid.md). The unequipped [Tool](/docs/reference/engine/classes/Tool.md) will be parented to the [Backpack](/docs/reference/engine/classes/Backpack.md) of the [Player](/docs/reference/engine/classes/Player.md) associated with the [Humanoid](/docs/reference/engine/classes/Humanoid.md). If no [Tool](/docs/reference/engine/classes/Tool.md) is equipped, this method will do nothing. Although [Tools](/docs/reference/engine/classes/Tool.md) can be equipped by NPCs, this method only works on [Humanoids](/docs/reference/engine/classes/Humanoid.md) with a corresponding [Player](/docs/reference/engine/classes/Player.md), as a [Backpack](/docs/reference/engine/classes/Backpack.md) object is required to parent the unequipped [Tool](/docs/reference/engine/classes/Tool.md) to. See also [Humanoid:EquipTool()](/docs/reference/engine/classes/Humanoid.md). *Security: None · Thread Safety: Unsafe · Capabilities: AvatarBehavior* **Returns:** `()` ### Method: Humanoid:AddCustomStatus **Signature:** `Humanoid:AddCustomStatus(status: string): boolean` > **Deprecated:** This item is deprecated, as it was a part of the unfinished RbxStatus library which would have allowed you to add conditions to a Humanoid. Do not use it for new work. Adds a BoolValue to the Humanoid's _Status_ object, whose name is equal to the string passed as the _status_ argument. If the status already exists, a new BoolValue will not be created. *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `status` | `string` | | | **Returns:** `boolean` ### Method: Humanoid:AddStatus **Signature:** `Humanoid:AddStatus(status?: Status): boolean` > **Deprecated:** This item is deprecated, as it was a part of the unfinished RbxStatus library which would have allowed you to add conditions to a Humanoid. Do not use it for new work. Adds a BoolValue to the Humanoid's _Status_ object, whose name is equal to the name of the _Status_ enum passed as the _status_ argument. If the status already exists, a new BoolValue will not be created. *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `status` | `Status` | `Poison` | | **Returns:** `boolean` ### Method: Humanoid:ApplyDescription **Signature:** `Humanoid:ApplyDescription(humanoidDescription: HumanoidDescription, assetTypeVerification?: AssetTypeVerification): ()` > **Deprecated:** This method has been superseded by [ApplyDescriptionAsync()](/docs/reference/engine/classes/Humanoid.md). Makes the character's look match that of the passed in [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AvatarAppearance* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `humanoidDescription` | `HumanoidDescription` | | The [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) instance which you want to set the character to match. | | `assetTypeVerification` | `AssetTypeVerification` | `Default` | The asset type verification mode. | **Returns:** `()` ### Method: Humanoid:ApplyDescriptionReset **Signature:** `Humanoid:ApplyDescriptionReset(humanoidDescription: HumanoidDescription, assetTypeVerification?: AssetTypeVerification): ()` > **Deprecated:** This method has been superseded by [ApplyDescriptionResetAsync()](/docs/reference/engine/classes/Humanoid.md). Makes the character's look match that of the passed in [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md), even after external changes. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AvatarAppearance* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `humanoidDescription` | `HumanoidDescription` | | The [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) instance which you want to set the character to match. | | `assetTypeVerification` | `AssetTypeVerification` | `Default` | The asset type verification mode. | **Returns:** `()` ### Method: Humanoid:GetPlayingAnimationTracks **Signature:** `Humanoid:GetPlayingAnimationTracks(): Array` This method returns an array of all [AnimationTracks](/docs/reference/engine/classes/AnimationTrack.md) that are currently being played on the [Humanoid](/docs/reference/engine/classes/Humanoid.md). A typical use for this method is stopping currently playing tracks using [AnimationTrack:Stop()](/docs/reference/engine/classes/AnimationTrack.md). Beware that this method will not return [AnimationTracks](/docs/reference/engine/classes/AnimationTrack.md) that have loaded but are **not playing**. If you want to track these you will need to index them manually. *Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Returns:** `Array` — An array of currently playing [AnimationTracks](/docs/reference/engine/classes/AnimationTrack.md). ### Method: Humanoid:GetStatuses **Signature:** `Humanoid:GetStatuses(): Array` > **Deprecated:** This item is deprecated, as it was a part of the unfinished RbxStatus library which would have allowed you to add conditions to a Humanoid. Do not use it for new work. The GetStatuses method returns a table of the Humanoid's statuses, and custom statuses. *Security: None · Thread Safety: Unsafe* **Returns:** `Array` ### Method: Humanoid:HasCustomStatus **Signature:** `Humanoid:HasCustomStatus(status: string): boolean` > **Deprecated:** This item is deprecated, as it was a part of the unfinished RbxStatus library which would have allowed you to add conditions to a Humanoid. Do not use it for new work. The HasCustomStatus method returns boolean based on if custom statuses exist. *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `status` | `string` | | | **Returns:** `boolean` ### Method: Humanoid:HasStatus **Signature:** `Humanoid:HasStatus(status?: Status): boolean` > **Deprecated:** This item is deprecated, as it was a part of the unfinished RbxStatus library which would have allowed you to add conditions to a Humanoid. Do not use it for new work. The HasStatus method returns a boolean based on if a status exists. *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `status` | `Status` | `Poison` | | **Returns:** `boolean` ### Method: Humanoid:LoadAnimation **Signature:** `Humanoid:LoadAnimation(animation: Animation): AnimationTrack` > **Deprecated:** This function is deprecated in favor of using [Animator:LoadAnimation()](/docs/reference/engine/classes/Animator.md) directly (the [Animator](/docs/reference/engine/classes/Animator.md) may be created while editing or at runtime). This method loads an [Animation](/docs/reference/engine/classes/Animation.md) onto a [Humanoid](/docs/reference/engine/classes/Humanoid.md), returning an [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md) that can be used for playback. *Security: None · Thread Safety: Unsafe · Capabilities: Animation · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `animation` | `Animation` | | The [Animation](/docs/reference/engine/classes/Animation.md) to load. | **Returns:** `AnimationTrack` ### Method: Humanoid:loadAnimation **Signature:** `Humanoid:loadAnimation(animation: Animation): AnimationTrack` > **Deprecated:** This deprecated method is a variant of [Humanoid:LoadAnimation()](/docs/reference/engine/classes/Humanoid.md). [Animator:LoadAnimation()](/docs/reference/engine/classes/Animator.md) should be used instead. *Security: None · Thread Safety: Unsafe · Capabilities: Animation · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `animation` | `Animation` | | | **Returns:** `AnimationTrack` ### Method: Humanoid:PlayEmote **Signature:** `Humanoid:PlayEmote(emoteName: string): boolean` > **Deprecated:** This method has been superseded by [PlayEmoteAsync()](/docs/reference/engine/classes/Humanoid.md). Plays emotes and returns if was successfully ran. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AvatarBehavior* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `emoteName` | `string` | | name of the emote to play. | **Returns:** `boolean` — successfully played. ### Method: Humanoid:RemoveCustomStatus **Signature:** `Humanoid:RemoveCustomStatus(status: string): boolean` > **Deprecated:** This item is deprecated, as it was a part of the unfinished RbxStatus library which would have allowed you to add conditions to a Humanoid. Do not use it for new work. The RemoveCustomStatus method removes the defined custom status from the Status model in the Humanoid.. *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `status` | `string` | | | **Returns:** `boolean` ### Method: Humanoid:RemoveStatus **Signature:** `Humanoid:RemoveStatus(status?: Status): boolean` > **Deprecated:** This item is deprecated, as it was a part of the unfinished RbxStatus library which would have allowed you to add conditions to a Humanoid. Do not use it for new work. The RemoveStatus method removes the defined status from the Status model in the Humanoid. *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `status` | `Status` | `Poison` | | **Returns:** `boolean` ### Method: Humanoid:takeDamage **Signature:** `Humanoid:takeDamage(amount: float): ()` > **Deprecated:** This deprecated method is a variant of [Humanoid:TakeDamage()](/docs/reference/engine/classes/Humanoid.md), which should be used instead. *Security: None · Thread Safety: Unsafe · Capabilities: AvatarBehavior* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `amount` | `float` | | | **Returns:** `()` ## Events ### Event: Humanoid.ApplyDescriptionFinished **Signature:** `Humanoid.ApplyDescriptionFinished(description: HumanoidDescription)` *Security: None · Capabilities: AvatarAppearance* **Parameters:** | Name | Type | Description | |------|------|-------------| | `description` | `HumanoidDescription` | | ### Event: Humanoid.Climbing **Signature:** `Humanoid.Climbing(speed: float)` Fires when the speed at which a [Humanoid](/docs/reference/engine/classes/Humanoid.md) is climbing changes. [Humanoids](/docs/reference/engine/classes/Humanoid.md) can climb up ladders made out of [Parts](/docs/reference/engine/classes/BasePart.md) or [TrussParts](/docs/reference/engine/classes/TrussPart.md). [Humanoids](/docs/reference/engine/classes/Humanoid.md) climb at 70% of their [Humanoid.WalkSpeed](/docs/reference/engine/classes/Humanoid.md). This event will not always fire with a speed of 0 when the [Humanoid](/docs/reference/engine/classes/Humanoid.md) stops climbing. See also: - For swimming and running see the [Humanoid.Swimming](/docs/reference/engine/classes/Humanoid.md) and [Humanoid.Running](/docs/reference/engine/classes/Humanoid.md) events - You can also detect when a [Humanoid](/docs/reference/engine/classes/Humanoid.md) is climbing using the [Humanoid.StateChanged](/docs/reference/engine/classes/Humanoid.md) event - You can disable climbing using the [Humanoid:SetStateEnabled()](/docs/reference/engine/classes/Humanoid.md) function *Security: None · Capabilities: AvatarBehavior* **Parameters:** | Name | Type | Description | |------|------|-------------| | `speed` | `float` | The speed at which the [Humanoid](/docs/reference/engine/classes/Humanoid.md) is currently climbing. | **Humanoid.Climbing** ```lua local Players = game:GetService("Players") local function onCharacterClimbing(character, speed) print(character.Name, "is climbing at a speed of", speed, "studs / second.") end local function onCharacterAdded(character) character.Humanoid.Climbing:Connect(function(speed) onCharacterClimbing(character, speed) end) end local function onPlayerAdded(player) player.CharacterAdded:Connect(onCharacterAdded) end Players.PlayerAdded:Connect(onPlayerAdded) ``` ### Event: Humanoid.Died **Signature:** `Humanoid.Died()` This event fires when the [Humanoid](/docs/reference/engine/classes/Humanoid.md) dies, usually when [Humanoid.Health](/docs/reference/engine/classes/Humanoid.md) reaches 0. This could be caused either by disconnecting their head from their [Humanoid.Torso](/docs/reference/engine/classes/Humanoid.md), or directly setting the health property. This event only fires if the [Humanoid](/docs/reference/engine/classes/Humanoid.md) is a descendant of the [Workspace](/docs/reference/engine/classes/Workspace.md). If the `Dead` [HumanoidStateType](/docs/reference/engine/enums/HumanoidStateType.md) is disabled it will not fire. *Security: None · Capabilities: AvatarBehavior* **Humanoid.Died** The code below would print the player's name, followed by "has died!", whenever a player dies. For example, if the player was named "Shedletsky", "Shedletsky has died!" would be printed to the output when they died. ```lua local Players = game:GetService("Players") local function onPlayerAdded(player) local function onCharacterAdded(character) local humanoid = character:WaitForChild("Humanoid") local function onDied() print(player.Name, "has died!") end humanoid.Died:Connect(onDied) end player.CharacterAdded:Connect(onCharacterAdded) end Players.PlayerAdded:Connect(onPlayerAdded) ``` ### Event: Humanoid.FallingDown **Signature:** `Humanoid.FallingDown(active: boolean)` The FallingDown event fires when the [Humanoid](/docs/reference/engine/classes/Humanoid.md) enters and leaves the `FallingDown` [HumanoidStateType](/docs/reference/engine/enums/HumanoidStateType.md). The [Humanoid](/docs/reference/engine/classes/Humanoid.md) will enter the `GettingUp` state 3 seconds after the `FallingDown` state is enabled. When this happens this event will fire with an _active_ value of _false_, and [Humanoid.GettingUp](/docs/reference/engine/classes/Humanoid.md) will fire with an _active_ value of _true_. *Security: None · Capabilities: AvatarBehavior* **Parameters:** | Name | Type | Description | |------|------|-------------| | `active` | `boolean` | Describes whether the [Humanoid](/docs/reference/engine/classes/Humanoid.md) is entering or leaving the `FallingDown` [HumanoidStateType](/docs/reference/engine/enums/HumanoidStateType.md). | ### Event: Humanoid.FreeFalling **Signature:** `Humanoid.FreeFalling(active: boolean)` This event fires when the [Humanoid](/docs/reference/engine/classes/Humanoid.md) enters or leaves the `Freefall` [HumanoidStateType](/docs/reference/engine/enums/HumanoidStateType.md). The _active_ parameter represents whether the [Humanoid](/docs/reference/engine/classes/Humanoid.md) is entering or leaving the `Freefall` state. Although the `Freefall` state generally ends when the [Humanoid](/docs/reference/engine/classes/Humanoid.md) reaches the ground, this event may fire with _active_ equal to _false_ if the state is changed while the [Humanoid](/docs/reference/engine/classes/Humanoid.md) is falling. For this reason, you should use [Humanoid.StateChanged](/docs/reference/engine/classes/Humanoid.md) and listen for the `Landed` state to work out when a [Humanoid](/docs/reference/engine/classes/Humanoid.md) has landed. *Security: None · Capabilities: AvatarBehavior* **Parameters:** | Name | Type | Description | |------|------|-------------| | `active` | `boolean` | Whether the [Humanoid](/docs/reference/engine/classes/Humanoid.md) is entering or leaving the `Freefall` [HumanoidStateType](/docs/reference/engine/enums/HumanoidStateType.md). | ### Event: Humanoid.GettingUp **Signature:** `Humanoid.GettingUp(active: boolean)` This event fires when the [Humanoid](/docs/reference/engine/classes/Humanoid.md) enters or leaves the [HumanoidStateType.GettingUp](/docs/reference/engine/enums/HumanoidStateType.md) state, a transition state that is activated shortly after the [Humanoid](/docs/reference/engine/classes/Humanoid.md) enters the [FallingDown](/docs/reference/engine/enums/HumanoidStateType.md) (3 seconds) or [Ragdoll](/docs/reference/engine/enums/HumanoidStateType.md) (1 second) states. When a [Humanoid](/docs/reference/engine/classes/Humanoid.md) attempts to get back up, this event will first fire with an `active` parameter of `true` before shortly after firing again with an `active` parameter of `false`. To force a [Humanoid](/docs/reference/engine/classes/Humanoid.md) to fall over, use the [Humanoid:ChangeState()](/docs/reference/engine/classes/Humanoid.md) function with [HumanoidStateType.FallingDown](/docs/reference/engine/enums/HumanoidStateType.md). *Security: None · Capabilities: AvatarBehavior* **Parameters:** | Name | Type | Description | |------|------|-------------| | `active` | `boolean` | Whether the [Humanoid](/docs/reference/engine/classes/Humanoid.md) is entering or leaving the `GettingUp` [HumanoidStateType](/docs/reference/engine/enums/HumanoidStateType.md). | ### Event: Humanoid.HealthChanged **Signature:** `Humanoid.HealthChanged(health: float)` This event fires when the [Humanoid.Health](/docs/reference/engine/classes/Humanoid.md) changes. However, it will not fire if the health is increasing from a value equal to or greater than the [Humanoid.MaxHealth](/docs/reference/engine/classes/Humanoid.md). When [Humanoid.Health](/docs/reference/engine/classes/Humanoid.md) reaches zero, the [Humanoid](/docs/reference/engine/classes/Humanoid.md) will die and the [Humanoid.Died](/docs/reference/engine/classes/Humanoid.md) event will fire. This event will fire with a value of zero. *Security: None · Capabilities: AvatarBehavior* **Parameters:** | Name | Type | Description | |------|------|-------------| | `health` | `float` | The new value of [Humanoid.Health](/docs/reference/engine/classes/Humanoid.md). | **Humanoid.HealthChanged** The following example determines the change in health, printing it to the output. It will only work in a LocalScript. ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local function onCharacterAdded(character) local humanoid = character:WaitForChild("Humanoid") local currentHealth = humanoid.Health local function onHealthChanged(health) local change = math.abs(currentHealth - health) print("The humanoid's health", (currentHealth > health and "decreased by" or "increased by"), change) currentHealth = health end humanoid.HealthChanged:Connect(onHealthChanged) end player.CharacterAdded:Connect(onCharacterAdded) ``` **Health Bar** This code sample allows you to create a simple color-changing health bar using two nested Frames. Paste this into a LocalScript on the inner frame. ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer -- Paste script into a LocalScript that is -- parented to a Frame within a Frame local frame = script.Parent local container = frame.Parent container.BackgroundColor3 = Color3.new(0, 0, 0) -- black -- This function is called when the humanoid's health changes local function onHealthChanged() local human = player.Character.Humanoid local percent = human.Health / human.MaxHealth -- Change the size of the inner bar frame.Size = UDim2.new(percent, 0, 1, 0) -- Change the color of the health bar if percent < 0.1 then frame.BackgroundColor3 = Color3.new(1, 0, 0) -- black elseif percent < 0.4 then frame.BackgroundColor3 = Color3.new(1, 1, 0) -- yellow else frame.BackgroundColor3 = Color3.new(0, 1, 0) -- green end end -- This function runs is called the player spawns in local function onCharacterAdded(character) local human = character:WaitForChild("Humanoid") -- Pattern: update once now, then any time the health changes human.HealthChanged:Connect(onHealthChanged) onHealthChanged() end -- Connect our spawn listener; call it if already spawned player.CharacterAdded:Connect(onCharacterAdded) if player.Character then onCharacterAdded(player.Character) end ``` ### Event: Humanoid.Jumping **Signature:** `Humanoid.Jumping(active: boolean)` This event fires when the [Humanoid](/docs/reference/engine/classes/Humanoid.md) enters and leaves the `Jumping` [HumanoidStateType](/docs/reference/engine/enums/HumanoidStateType.md). When a [Humanoid](/docs/reference/engine/classes/Humanoid.md) jumps, this event fires with an `active` parameter of `true` before shortly afterwards firing again with an `active` parameter of `false`. This second firing does not correspond with a [Humanoid](/docs/reference/engine/classes/Humanoid.md) landing; for that, listen for the `Landed` [HumanoidStateType](/docs/reference/engine/enums/HumanoidStateType.md) using [Humanoid.StateChanged](/docs/reference/engine/classes/Humanoid.md). You can disable jumping using the [Humanoid:SetStateEnabled()](/docs/reference/engine/classes/Humanoid.md) function. *Security: None · Capabilities: AvatarBehavior* **Parameters:** | Name | Type | Description | |------|------|-------------| | `active` | `boolean` | Whether the [Humanoid](/docs/reference/engine/classes/Humanoid.md) is entering or leaving the `Jumping` [HumanoidStateType](/docs/reference/engine/enums/HumanoidStateType.md). | ### Event: Humanoid.MoveToFinished **Signature:** `Humanoid.MoveToFinished(reached: boolean)` This event fires when the [Humanoid](/docs/reference/engine/classes/Humanoid.md) finishes walking to a goal declared by the [Humanoid.WalkToPoint](/docs/reference/engine/classes/Humanoid.md) and [Humanoid.WalkToPart](/docs/reference/engine/classes/Humanoid.md) properties. The [Humanoid.WalkToPoint](/docs/reference/engine/classes/Humanoid.md) and [Humanoid.WalkToPart](/docs/reference/engine/classes/Humanoid.md) properties can be set individually, or using the [Humanoid:MoveTo()](/docs/reference/engine/classes/Humanoid.md) function. If the [Humanoid](/docs/reference/engine/classes/Humanoid.md) reaches its goal within 8 seconds, this event will return with _reached_ as true. If the goal is not reached within 8 seconds the [Humanoid](/docs/reference/engine/classes/Humanoid.md) will stop walking and _reached_ will be false. This timeout can be reset be calling [Humanoid:MoveTo()](/docs/reference/engine/classes/Humanoid.md) again within the timeout period. *Security: None · Capabilities: AvatarBehavior* **Parameters:** | Name | Type | Description | |------|------|-------------| | `reached` | `boolean` | A boolean indicating whether the [Humanoid](/docs/reference/engine/classes/Humanoid.md) reached is goal. _True_ if the [Humanoid](/docs/reference/engine/classes/Humanoid.md) is reached its goal, _false_ if the walk timed out before the goal could be reached. | **Humanoid MoveTo Without Time out** This code sample includes a function that avoids the timeout on [Humanoid:MoveTo()](/docs/reference/engine/classes/Humanoid.md) by calling [Humanoid:MoveTo()](/docs/reference/engine/classes/Humanoid.md) again before the timeout elapses. It also includes an optional `andThen` parameter for which you can pass a function to be called when the humanoid reaches its destination. ```lua local function moveTo(humanoid, targetPoint, andThen) local targetReached = false -- listen for the humanoid reaching its target local connection connection = humanoid.MoveToFinished:Connect(function(reached) targetReached = true connection:Disconnect() connection = nil if andThen then andThen(reached) end end) -- start walking humanoid:MoveTo(targetPoint) -- execute on a new thread so as to not yield function task.spawn(function() while not targetReached do -- does the humanoid still exist? if not (humanoid and humanoid.Parent) then break end -- has the target changed? if humanoid.WalkToPoint ~= targetPoint then break end -- refresh the timeout humanoid:MoveTo(targetPoint) task.wait(6) end -- disconnect the connection if it is still connected if connection then connection:Disconnect() connection = nil end end) end local function andThen(reached) print((reached and "Destination reached!") or "Failed to reach destination!") end moveTo(script.Parent:WaitForChild("Humanoid"), Vector3.new(50, 0, 50), andThen) ``` ### Event: Humanoid.PlatformStanding **Signature:** `Humanoid.PlatformStanding(active: boolean)` This event fires when the [Humanoid](/docs/reference/engine/classes/Humanoid.md) enters or leaves the `PlatformStanding` [HumanoidStateType](/docs/reference/engine/enums/HumanoidStateType.md). Whilst the [Humanoid](/docs/reference/engine/classes/Humanoid.md) is in the `PlatformStanding` state, the [Humanoid.PlatformStand](/docs/reference/engine/classes/Humanoid.md) property will be _true_. Whilst [Humanoid.PlatformStand](/docs/reference/engine/classes/Humanoid.md) is set to _true_, the [Humanoid](/docs/reference/engine/classes/Humanoid.md) will be unable to move. For more information please see the page for [Humanoid.PlatformStand](/docs/reference/engine/classes/Humanoid.md). The PlatformStand [HumanoidStateType](/docs/reference/engine/enums/HumanoidStateType.md) was associated with the now disabled [Platform](/docs/reference/engine/classes/Platform.md) part. Despite this, it can still be used by developers. *Security: None · Capabilities: AvatarBehavior* **Parameters:** | Name | Type | Description | |------|------|-------------| | `active` | `boolean` | Whether the [Humanoid](/docs/reference/engine/classes/Humanoid.md) is entering or leaving the `PlatformStanding` [HumanoidStateType](/docs/reference/engine/enums/HumanoidStateType.md). | ### Event: Humanoid.Ragdoll **Signature:** `Humanoid.Ragdoll(active: boolean)` This event fires when the [Humanoid](/docs/reference/engine/classes/Humanoid.md) enters or leaves the `Ragdoll` [HumanoidStateType](/docs/reference/engine/enums/HumanoidStateType.md). The `active` parameter will have the value `true` or `false` to indicate entering or leaving. Use [Humanoid:SetStateEnabled()](/docs/reference/engine/classes/Humanoid.md) to disable the GettingUp state to stay in the Ragdoll state. See also: - [Humanoid.FallingDown](/docs/reference/engine/classes/Humanoid.md) for the [Humanoid](/docs/reference/engine/classes/Humanoid.md) event connected with the `FallingDown` state, which behaves similarly to `Ragdoll` *Security: None · Capabilities: AvatarBehavior* **Parameters:** | Name | Type | Description | |------|------|-------------| | `active` | `boolean` | Whether the [Humanoid](/docs/reference/engine/classes/Humanoid.md) is entering or leaving the `Ragdoll` [HumanoidStateType](/docs/reference/engine/enums/HumanoidStateType.md). | ### Event: Humanoid.Running **Signature:** `Humanoid.Running(speed: float)` This event fires when the speed at which a [Humanoid](/docs/reference/engine/classes/Humanoid.md) is running changes. While running [Humanoids](/docs/reference/engine/classes/Humanoid.md) cover, on average, their [Humanoid.WalkSpeed](/docs/reference/engine/classes/Humanoid.md) in studs per second. When the [Humanoid](/docs/reference/engine/classes/Humanoid.md) stops running this event will fire with a speed of 0. See also: - For swimming and climbing see the [Humanoid.Swimming](/docs/reference/engine/classes/Humanoid.md) and [Humanoid.Climbing](/docs/reference/engine/classes/Humanoid.md) events - You can also detect when a [Humanoid](/docs/reference/engine/classes/Humanoid.md) is running using the [Humanoid.StateChanged](/docs/reference/engine/classes/Humanoid.md) event *Security: None · Capabilities: AvatarBehavior* **Parameters:** | Name | Type | Description | |------|------|-------------| | `speed` | `float` | The speed at which the [Humanoid](/docs/reference/engine/classes/Humanoid.md) is running. | **Humanoid Running** Demonstrates connecting to the [Humanoid.Running](/docs/reference/engine/classes/Humanoid.md) event. The event is connected to every player's humanoid that joins. The function connected will print whether or not the humanoid is running based on the speed. ```lua local Players = game:GetService("Players") local localPlayer = Players.LocalPlayer local character = localPlayer.Character or localPlayer.CharacterAdded:Wait() local humanoid = character:WaitForChild("Humanoid") local function onRunning(speed: number) if speed > 0 then print(`{localPlayer.Name} is running`) else print(`{localPlayer.Name} has stopped`) end end humanoid.Running:Connect(function(speed: number) onRunning(speed) end) ``` ### Event: Humanoid.Seated **Signature:** `Humanoid.Seated(active: boolean, currentSeatPart: BasePart)` This event fires when a [Humanoid](/docs/reference/engine/classes/Humanoid.md) either sits in or gets up from a [Seat](/docs/reference/engine/classes/Seat.md) or [VehicleSeat](/docs/reference/engine/classes/VehicleSeat.md). When a character comes into contact with a seat, they are attached to the seat and a sitting animation plays. For more information on this, see the [Seat](/docs/reference/engine/classes/Seat.md) page. - If the character is sitting down, the `active` parameter will be **true** and `currentSeatPart` will be the seat they are currently sitting in. - If the character got up from a seat, the `active` parameter will be **false** and `currentSeatPart` will be `nil`. See also: - [Humanoid.Sit](/docs/reference/engine/classes/Humanoid.md), which indicates if a Humanoid is currently sitting - [Humanoid.SeatPart](/docs/reference/engine/classes/Humanoid.md), which indicates the seat a Humanoid is currently sitting in, if any. *Security: None · Capabilities: AvatarBehavior* **Parameters:** | Name | Type | Description | |------|------|-------------| | `active` | `boolean` | True if the [Humanoid](/docs/reference/engine/classes/Humanoid.md) is sitting down. | | `currentSeatPart` | `BasePart` | The seat the [Humanoid](/docs/reference/engine/classes/Humanoid.md) is sat in if it is sitting down. | **Finding a Player's Seat** This code sample demonstrates when the local player's [Character](/docs/reference/engine/classes/Player.md) sits down or stands up. It should be placed inside a [LocalScript](/docs/reference/engine/classes/LocalScript.md) within [StarterCharacterScripts](/docs/reference/engine/classes/StarterCharacterScripts.md) in order to run when the player's character spawns in. ```lua local character = script.Parent local humanoid = character:WaitForChild("Humanoid") local function onSeated(isSeated, seat) if isSeated then print("I'm now sitting on: " .. seat.Name .. "!") else print("I'm not sitting on anything") end end humanoid.Seated:Connect(onSeated) ``` ### Event: Humanoid.StateChanged **Signature:** `Humanoid.StateChanged(old: HumanoidStateType, new: HumanoidStateType)` This event fires when the state of the [Humanoid](/docs/reference/engine/classes/Humanoid.md) is changed. As there is no "idle" humanoid state, you should instead use the [Humanoid.Running](/docs/reference/engine/classes/Humanoid.md) event or listen to the [RootPart](/docs/reference/engine/classes/Humanoid.md) part's [Velocity](/docs/reference/engine/classes/BasePart.md) to work out when the [Humanoid](/docs/reference/engine/classes/Humanoid.md) is standing still. #### See Also - [Humanoid:GetState()](/docs/reference/engine/classes/Humanoid.md) and [Humanoid:ChangeState()](/docs/reference/engine/classes/Humanoid.md) to get and set the state. - [Humanoid:SetStateEnabled()](/docs/reference/engine/classes/Humanoid.md) to enable and disable specific states. *Security: None · Capabilities: AvatarBehavior* **Parameters:** | Name | Type | Description | |------|------|-------------| | `old` | `HumanoidStateType` | The humanoid's previous state type. | | `new` | `HumanoidStateType` | The humanoid's current state type. | **Jumping Particles** Emits particles from the local player's [Player.Character](/docs/reference/engine/classes/Player.md) when they jump. To try this code sample, place it inside a [LocalScript](/docs/reference/engine/classes/LocalScript.md) parented to [StarterCharacterScripts](/docs/reference/engine/classes/StarterCharacterScripts.md). ```lua local character = script.Parent local primaryPart = character.PrimaryPart -- create particles local particles = Instance.new("ParticleEmitter") particles.Size = NumberSequence.new(1) particles.Transparency = NumberSequence.new(0, 1) particles.Acceleration = Vector3.new(0, -10, 0) particles.Lifetime = NumberRange.new(1) particles.Rate = 20 particles.EmissionDirection = Enum.NormalId.Back particles.Enabled = false particles.Parent = primaryPart local humanoid = character:WaitForChild("Humanoid") local isJumping = false -- listen to humanoid state local function onStateChanged(_oldState, newState) if newState == Enum.HumanoidStateType.Jumping then if not isJumping then isJumping = true particles.Enabled = true end elseif newState == Enum.HumanoidStateType.Landed then if isJumping then isJumping = false particles.Enabled = false end end end humanoid.StateChanged:Connect(onStateChanged) ``` **Jump Cooldown** The following sample will require a one second cooldown after a `Humanoid` has landed before it is able to jump again. To try this sample, place it inside a `LocalScript` in `StarterCharacterScripts|StarterPlayer.StarterCharacterScripts`. ```lua local character = script.Parent local JUMP_DEBOUNCE = 1 local humanoid = character:WaitForChild("Humanoid") local isJumping = false humanoid.StateChanged:Connect(function(_oldState, newState) if newState == Enum.HumanoidStateType.Jumping then if not isJumping then isJumping = true humanoid:SetStateEnabled(Enum.HumanoidStateType.Jumping, false) end elseif newState == Enum.HumanoidStateType.Landed then if isJumping then isJumping = false task.wait(JUMP_DEBOUNCE) humanoid:SetStateEnabled(Enum.HumanoidStateType.Jumping, true) end end end) ``` ### Event: Humanoid.StateEnabledChanged **Signature:** `Humanoid.StateEnabledChanged(state: HumanoidStateType, isEnabled: boolean)` The StateEnableChanged event fires when [Humanoid:SetStateEnabled()](/docs/reference/engine/classes/Humanoid.md) is called on the [Humanoid](/docs/reference/engine/classes/Humanoid.md). Parameters include the [HumanoidStateType](/docs/reference/engine/enums/HumanoidStateType.md) in question along with a bool indicating if this state is now enabled. See also: - To find if a state is currently enabled, use [Humanoid:GetStateEnabled()](/docs/reference/engine/classes/Humanoid.md) - To listen to [Humanoid](/docs/reference/engine/classes/Humanoid.md) state changes use [Humanoid.StateChanged](/docs/reference/engine/classes/Humanoid.md) *Security: None · Capabilities: AvatarBehavior* **Parameters:** | Name | Type | Description | |------|------|-------------| | `state` | `HumanoidStateType` | The [HumanoidStateType](/docs/reference/engine/enums/HumanoidStateType.md) for which the enabled state has been changed. | | `isEnabled` | `boolean` | True if the state is now enabled. | **Humanoid State Change Detector** When a humanoid state changes for the [Players.LocalPlayer](/docs/reference/engine/classes/Players.md), the code below prints whether the state has been enabled or disabled. This code should work as expected when placed in a `LocalScript`. ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local character = player.Character or player.CharacterAdded:Wait() local humanoid = character:WaitForChild("Humanoid") local function onStateEnabledChanged(state, enabled) if enabled then print(state.Name .. " has been enabled") else print(state.Name .. " has been disabled") end end humanoid.StateEnabledChanged:Connect(onStateEnabledChanged) ``` ### Event: Humanoid.Strafing **Signature:** `Humanoid.Strafing(active: boolean)` This event does not fire when the [Humanoid](/docs/reference/engine/classes/Humanoid.md) is strafing and should not be used by developers This event is fired when the [Humanoid](/docs/reference/engine/classes/Humanoid.md) enters or leaves the `StrafingNoPhysics` [HumanoidStateType](/docs/reference/engine/enums/HumanoidStateType.md). When the [Humanoid](/docs/reference/engine/classes/Humanoid.md) enters the `StrafingNoPhysics` state this event will fire with an _active_ parameter of _true_. The event will fire again with _active_ equal to _false_ when the [Humanoid](/docs/reference/engine/classes/Humanoid.md) leaves the `StrafingNoPhysics` state. This event is associated with the `StrafingNoPhysics` [Humanoid](/docs/reference/engine/classes/Humanoid.md) state and does **not** fire when the [Humanoid](/docs/reference/engine/classes/Humanoid.md) is moving perpendicular to the direction it is facing. This state is currently unused, if it is set using [Humanoid:ChangeState()](/docs/reference/engine/classes/Humanoid.md) the state will revert to `RunningNoPhysics`. *Security: None · Capabilities: AvatarBehavior* **Parameters:** | Name | Type | Description | |------|------|-------------| | `active` | `boolean` | Whether the [Humanoid](/docs/reference/engine/classes/Humanoid.md) is entering or leaving the `StrafingNoPhysics` [HumanoidStateType](/docs/reference/engine/enums/HumanoidStateType.md). | ### Event: Humanoid.Swimming **Signature:** `Humanoid.Swimming(speed: float)` This event fires when the speed at which a [Humanoid](/docs/reference/engine/classes/Humanoid.md) is swimming in [Terrain](/docs/reference/engine/classes/Terrain.md) water changes. [Humanoids](/docs/reference/engine/classes/Humanoid.md) swim at 87.5% of their [Humanoid.WalkSpeed](/docs/reference/engine/classes/Humanoid.md). This event will not always fire with a speed of 0 when the [Humanoid](/docs/reference/engine/classes/Humanoid.md) stops swimming. See also: - For running and climbing see the [Humanoid.Running](/docs/reference/engine/classes/Humanoid.md) and [Humanoid.Climbing](/docs/reference/engine/classes/Humanoid.md) events - You can also detect when a [Humanoid](/docs/reference/engine/classes/Humanoid.md) is swimming using the [Humanoid.StateChanged](/docs/reference/engine/classes/Humanoid.md) event - You can disable swimming using the [Humanoid:SetStateEnabled()](/docs/reference/engine/classes/Humanoid.md) function *Security: None · Capabilities: AvatarBehavior* **Parameters:** | Name | Type | Description | |------|------|-------------| | `speed` | `float` | The speed the [Humanoid](/docs/reference/engine/classes/Humanoid.md) is currently swimming at. | ### Event: Humanoid.Touched **Signature:** `Humanoid.Touched(touchingPart: BasePart, humanoidPart: BasePart)` This event fires when one of the humanoid's limbs comes in contact with another [BasePart](/docs/reference/engine/classes/BasePart.md). The [BasePart](/docs/reference/engine/classes/BasePart.md) which the limb is touching, along with the limb itself, is given. This event will not fire when limbs belonging to the [Humanoid](/docs/reference/engine/classes/Humanoid.md) come into contact with themselves. #### Alternatives Although the [Humanoid.Touched](/docs/reference/engine/classes/Humanoid.md) event is useful, you should consider if there are alternatives that better suit your needs. - In most cases, it's advised to connect a [BasePart.Touched](/docs/reference/engine/classes/BasePart.md) event for [BaseParts](/docs/reference/engine/classes/BasePart.md) of interest instead, as the [Humanoid.Touched](/docs/reference/engine/classes/Humanoid.md) event will constantly fire when the humanoid is moving. For example, in a dodgeball game, it would be more practical to connect a [Touched](/docs/reference/engine/classes/BasePart.md) event for the balls rather than use [Humanoid.Touched](/docs/reference/engine/classes/Humanoid.md). - When trying to work out when the [Humanoid](/docs/reference/engine/classes/Humanoid.md) has landed on the ground, the [Humanoid.StateChanged](/docs/reference/engine/classes/Humanoid.md) event is more suitable. Alternatively, you can check [Humanoid.FloorMaterial](/docs/reference/engine/classes/Humanoid.md) to see if the humanoid is standing on any non-air material. #### Notes - Connecting to this event will cause a [TouchTransmitter](/docs/reference/engine/classes/TouchTransmitter.md) to be created in every limb. - There is currently no equivalent of [BasePart.TouchEnded](/docs/reference/engine/classes/BasePart.md) for [Humanoids](/docs/reference/engine/classes/Humanoid.md). *Security: None · Capabilities: AvatarBehavior* **Parameters:** | Name | Type | Description | |------|------|-------------| | `touchingPart` | `BasePart` | The [BasePart](/docs/reference/engine/classes/BasePart.md) the [Humanoid](/docs/reference/engine/classes/Humanoid.md) has come in contact with. | | `humanoidPart` | `BasePart` | The limb of the [Humanoid](/docs/reference/engine/classes/Humanoid.md) that has been touched. | **Midas Touch** When placed inside a [Player.Character](/docs/reference/engine/classes/Player.md) model this code will give a player the 'Midas touch'. Everything their character touches will change to gold. When the `Humanoid` dies, this change is undone and the golden `BasePart|BaseParts` are returned to their original state. To test this out, place this code inside a `Script` and place it in `StarterCharacterScripts|StarterPlayer.StarterCharacterScripts`. ```lua local character = script.Parent local humanoid = character:WaitForChild("Humanoid") local partInfo = {} local debounce = false local function onHumanoidTouched(hit, _limb) if debounce then return end if not hit.CanCollide or hit.Transparency ~= 0 then return end if not partInfo[hit] then partInfo[hit] = { BrickColor = hit.BrickColor, Material = hit.Material, } hit.BrickColor = BrickColor.new("Gold") hit.Material = Enum.Material.Ice debounce = true task.wait(0.2) debounce = false end end local touchedConnection = humanoid.Touched:Connect(onHumanoidTouched) local function onHumanoidDied() if touchedConnection then touchedConnection:Disconnect() end -- undo all of the gold for part, info in pairs(partInfo) do if part and part.Parent then part.BrickColor = info.BrickColor part.Material = info.Material end end end humanoid.Died:Connect(onHumanoidDied) ``` ### Event: Humanoid.AnimationPlayed **Signature:** `Humanoid.AnimationPlayed(animationTrack: AnimationTrack)` The AnimationPlayed event fires when an [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md) begins playing on the [Humanoid](/docs/reference/engine/classes/Humanoid.md). A common use for this function is to connect the [AnimationTrack.KeyframeReached](/docs/reference/engine/classes/AnimationTrack.md) event for the playing AnimationTrack, so additional effects can be added to the animation (for example [Sounds](/docs/reference/engine/classes/Sound.md) and [ParticleEmitters](/docs/reference/engine/classes/ParticleEmitter.md)). This event can be used for any [Humanoid](/docs/reference/engine/classes/Humanoid.md) regardless if it belongs to the local player's client or not. See also: - For the [AnimationController](/docs/reference/engine/classes/AnimationController.md) equivalent of this event, please see [AnimationController.AnimationPlayed](/docs/reference/engine/classes/AnimationController.md) *Security: None · Capabilities: Animation* **Parameters:** | Name | Type | Description | |------|------|-------------| | `animationTrack` | `AnimationTrack` | The [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md) that has begun playing. | **Pausing An Animation When a Keyframe is Reached** The example below pauses any AnimationTrack playing on the humanoid when a keyframe named 'PausePlayback' is reached. ```lua local humanoid = script.Parent:WaitForChild("Humanoid") humanoid.AnimationPlayed:Connect(function(animationTrack) if not animationTrack then return end local connection = animationTrack.KeyframeReached:Connect(function(keyframeName) if keyframeName == "PausePlayback" then -- adjust speed to 0, pausing the animation animationTrack:AdjustSpeed(0) end end) animationTrack.Stopped:Wait() connection:Disconnect() end) ``` ### Event: Humanoid.CustomStatusAdded **Signature:** `Humanoid.CustomStatusAdded(status: string)` > **Deprecated:** This item is deprecated, as it was a part of the unfinished RbxStatus library which would have allowed you to add conditions to a Humanoid. Do not use it for new work. The CustomStatusAdded event fires when a status is added to the Humanoid via the [Humanoid:AddCustomStatus()](/docs/reference/engine/classes/Humanoid.md) method. *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `status` | `string` | | ### Event: Humanoid.CustomStatusRemoved **Signature:** `Humanoid.CustomStatusRemoved(status: string)` > **Deprecated:** This item is deprecated, as it was a part of the unfinished RbxStatus library which would have allowed you to add conditions to a Humanoid. Do not use it for new work. The CustomStatusRemoved event fires when a status is removed from the Humanoid via the [Humanoid:RemoveCustomStatus()](/docs/reference/engine/classes/Humanoid.md) method. *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `status` | `string` | | ### Event: Humanoid.StatusAdded **Signature:** `Humanoid.StatusAdded(status: Status)` > **Deprecated:** This item is deprecated, as it was a part of the unfinished RbxStatus library which would have allowed you to add conditions to a Humanoid. Do not use it for new work. The StatusAdded event fires when a status is added to the Humanoid. *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `status` | `Status` | | **Humanoid.StatusAdded** ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local character = player.Character or player.CharacterAdded:Wait() local humanoid = character:WaitForChild("Humanoid") local function onStatusAdded(status) if status == Enum.Status.Poison then print("The humanoid is now poisoned!") elseif status == Enum.Status.Confusion then print("The humanoid is now confused!") end end humanoid.StatusAdded:Connect(onStatusAdded) humanoid:AddStatus(Enum.Status.Poison) --> The humanoid is now poisoned! ``` ### Event: Humanoid.StatusRemoved **Signature:** `Humanoid.StatusRemoved(status: Status)` > **Deprecated:** This item is deprecated, as it was a part of the unfinished RbxStatus library which would have allowed you to add conditions to a Humanoid. Do not use it for new work. The StatusRemoved event fires when a status is removed from the Humanoid. *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `status` | `Status` | | **Humanoid.StatusRemoved** ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local character = player.Character or player.CharacterAdded:Wait() local humanoid = character:WaitForChild("Humanoid") local function onStatusRemoved(status) if status == Enum.Status.Poison then print("The humanoid has been cured of poison!") elseif status == Enum.Status.Confusion then print("The humanoid has been reoriented!") end end humanoid.StatusRemoved:Connect(onStatusRemoved) humanoid:RemoveStatus(Enum.Status.Poison) --> The humanoid has been cured of poison! ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: HumanoidController last_updated: 2026-06-29T19:34:07Z inherits: - Controller - Instance - Object type: class memory_category: Instances --- # Class: HumanoidController ## Description A HumanoidController is an internal object responsible for translating PlayerAction movements to the user's character (specifically, their [Humanoid](/docs/reference/engine/classes/Humanoid.md)). This object can be found inside of the [ControllerService](/docs/reference/engine/classes/ControllerService.md), via: ```lua local ControllerService = game:GetService("ControllerService") local HumanoidController = ControllerService:FindFirstChildOfClass("HumanoidController") ``` ## Inherited Members ### From [Controller](/docs/reference/engine/classes/Controller.md) - **Method `BindButton(button: Button, caption: string): ()`**: Activates an overriding bind on the specified button. - **Method `bindButton(button: Button, caption: string): ()`**: *(deprecated)* - **Method `GetButton(button: Button): boolean`**: Returns whether or not Button is being pressed. - **Method `getButton(button: Button): boolean`**: *(deprecated)* - **Method `UnbindButton(button: Button): ()`**: Removes the bind on button. - **Event `ButtonChanged`**: Fired when the pressed state of a bound button is changed. This event can ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: HumanoidDescription last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances summary: "Describes the appearance of a Humanoid character including body parts, accessories, colors, scales, animations, and emotes." --- # Class: HumanoidDescription > Describes the appearance of a Humanoid character including body parts, > accessories, colors, scales, animations, and emotes. ## Description **HumanoidDescription** is an object that stores a description of a [Humanoid](/docs/reference/engine/classes/Humanoid.md) for R6 and R15 rigs. It can be [applied](/docs/reference/engine/classes/Humanoid.md) in order to set a rig's scaling, clothing ([Shirt](/docs/reference/engine/classes/Shirt.md), [Pants](/docs/reference/engine/classes/Pants.md), [ShirtGraphic](/docs/reference/engine/classes/ShirtGraphic.md)), [Accessories](/docs/reference/engine/classes/Accessory.md), [Animations](/docs/reference/engine/classes/Animation.md) and [BodyColors](/docs/reference/engine/classes/BodyColors.md). You can get a HumanoidDescription by using the following functions: - [Players:GetHumanoidDescriptionFromUserIdAsync()](/docs/reference/engine/classes/Players.md), for an outfit currently being worn by a user on Roblox. - [Players:GetHumanoidDescriptionFromOutfitIdAsync()](/docs/reference/engine/classes/Players.md), for an outfit created by a user on Roblox. - You can create a Humanoid rig model from a HumanoidDescription through [Players:CreateHumanoidModelFromDescriptionAsync()](/docs/reference/engine/classes/Players.md). See also: - [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), for more information on [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). ## Properties ### Property: HumanoidDescription.AccessoryBlob ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Accessories", "capabilities": [ "AvatarAppearance" ] } ``` A JSON formatted array of Layered clothing where each table in the entry in the array describes an accessory's AssetId, AccessoryType, Order, and (optionally) Puffiness as key-value pairs. This can be edited in the properties windows for the [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). To make changes from Luau (which is recommended over editing the JSON directly), use [HumanoidDescription:SetAccessories()](/docs/reference/engine/classes/HumanoidDescription.md) and [HumanoidDescription:GetAccessories()](/docs/reference/engine/classes/HumanoidDescription.md). These methods can also be enabled to work with rigid accessories by setting IncludeRigidAccessories parameters to true. ### Property: HumanoidDescription.BackAccessory ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Accessories", "capabilities": [ "AvatarAppearance" ] } ``` **BackAccessory** is a comma-separated list of asset IDs that determine what accessories should be added when the description is [applied](/docs/reference/engine/classes/Humanoid.md). The list cannot contain duplicates. An error is thrown if you try to apply a new description which shares any assets with the existing description but a different accessory property. See also: - [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), for more information on [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). - [FaceAccessory](/docs/reference/engine/classes/HumanoidDescription.md), [FrontAccessory](/docs/reference/engine/classes/HumanoidDescription.md), [HairAccessory](/docs/reference/engine/classes/HumanoidDescription.md), [HatAccessory](/docs/reference/engine/classes/HumanoidDescription.md), [NeckAccessory](/docs/reference/engine/classes/HumanoidDescription.md), [ShouldersAccessory](/docs/reference/engine/classes/HumanoidDescription.md) and [WaistAccessory](/docs/reference/engine/classes/HumanoidDescription.md), which are similar properties that apply accessories like this one ### Property: HumanoidDescription.BodyTypeScale ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Scale", "capabilities": [ "AvatarAppearance" ] } ``` **BodyTypeScale** determines the factor by which the shape of a [Humanoid](/docs/reference/engine/classes/Humanoid.md) is interpolated from the standard R15 body shape (0) to a taller and more slender body type (1). Values outside the range of 0 to 1 are clamped. When the description is applied through [Humanoid:ApplyDescriptionAsync()](/docs/reference/engine/classes/Humanoid.md), this value maps to a **BodyTypeScale** [NumberValue](/docs/reference/engine/classes/NumberValue.md) within the [Humanoid](/docs/reference/engine/classes/Humanoid.md). Note that when the value of this property is 0, the [ProportionScale](/docs/reference/engine/classes/HumanoidDescription.md) property has no effect. #### See Also - [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), for more information on [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). - [ProportionScale](/docs/reference/engine/classes/HumanoidDescription.md), which also affects rig proportions when this property is non-zero - [WidthScale](/docs/reference/engine/classes/HumanoidDescription.md), [HeightScale](/docs/reference/engine/classes/HumanoidDescription.md) and [DepthScale](/docs/reference/engine/classes/HumanoidDescription.md), which provide finer control over the dimensions of a rig - [HeadScale](/docs/reference/engine/classes/HumanoidDescription.md), which provides specific control over the scale of the rig's head ### Property: HumanoidDescription.ClimbAnimation ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Animation", "capabilities": [ "AvatarAppearance" ] } ``` When this description is [applied](/docs/reference/engine/classes/Humanoid.md) to a [Humanoid](/docs/reference/engine/classes/Humanoid.md), **ClimbAnimation** determines the [Animation.AnimationId](/docs/reference/engine/classes/Animation.md) to play when its [state](/docs/reference/engine/classes/Humanoid.md) is [Climbing](/docs/reference/engine/enums/HumanoidStateType.md). See also: - [FallAnimation](/docs/reference/engine/classes/HumanoidDescription.md), [IdleAnimation](/docs/reference/engine/classes/HumanoidDescription.md), [JumpAnimation](/docs/reference/engine/classes/HumanoidDescription.md), [RunAnimation](/docs/reference/engine/classes/HumanoidDescription.md), [SwimAnimation](/docs/reference/engine/classes/HumanoidDescription.md) and [WalkAnimation](/docs/reference/engine/classes/HumanoidDescription.md), which are similar properties that determine animations to play on the rig ### Property: HumanoidDescription.DepthScale ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Scale", "capabilities": [ "AvatarAppearance" ] } ``` **DepthScale** determines by what factor the depth (back-to-front distance) of a [Humanoid](/docs/reference/engine/classes/Humanoid.md) is scaled, as well as all accessories not attached to its head. When the description is applied through [Humanoid:ApplyDescriptionAsync()](/docs/reference/engine/classes/Humanoid.md), this value maps to a **BodyDepthScale** [NumberValue](/docs/reference/engine/classes/NumberValue.md) within the Humanoid. #### See Also - [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), for more information on [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). - [BodyTypeScale](/docs/reference/engine/classes/HumanoidDescription.md) and [ProportionScale](/docs/reference/engine/classes/HumanoidDescription.md), which can provide more realistic rig proportions - [WidthScale](/docs/reference/engine/classes/HumanoidDescription.md) and [HeightScale](/docs/reference/engine/classes/HumanoidDescription.md) ### Property: HumanoidDescription.Face ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Body Parts", "capabilities": [ "AvatarAppearance" ] } ``` **Face** determines the asset ID of the Face to be [applied](/docs/reference/engine/classes/Humanoid.md) to a [Humanoid](/docs/reference/engine/classes/Humanoid.md). The type of the asset ID provided **must be for a Face** type asset and not a Decal or Image type asset. The actual face texture is rendered using a [Decal](/docs/reference/engine/classes/Decal.md) in the Head named "face" or "Face". See also: - [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), for more information on [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). - [GraphicTShirt](/docs/reference/engine/classes/HumanoidDescription.md), [Shirt](/docs/reference/engine/classes/HumanoidDescription.md) and [Pants](/docs/reference/engine/classes/HumanoidDescription.md), which also apply textures to a rig - [Head](/docs/reference/engine/classes/HumanoidDescription.md), which can change the mesh of the head - [FaceAccessory](/docs/reference/engine/classes/HumanoidDescription.md), which can apply one or more [Accessory](/docs/reference/engine/classes/Accessory.md) objects to the face ### Property: HumanoidDescription.FaceAccessory ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Accessories", "capabilities": [ "AvatarAppearance" ] } ``` **FaceAccessory** is a comma-separated list of asset IDs that determine what accessories should be added when the description is [applied](/docs/reference/engine/classes/Humanoid.md), usually those attached to the front of its face (such as glasses). The list does not contain duplicates. An error is thrown if you try to apply a new description which shares any assets with the existing description but a different accessory property. See also: - [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), for more information on [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). - [BackAccessory](/docs/reference/engine/classes/HumanoidDescription.md), [FrontAccessory](/docs/reference/engine/classes/HumanoidDescription.md), [HairAccessory](/docs/reference/engine/classes/HumanoidDescription.md), [HatAccessory](/docs/reference/engine/classes/HumanoidDescription.md), [NeckAccessory](/docs/reference/engine/classes/HumanoidDescription.md), [ShouldersAccessory](/docs/reference/engine/classes/HumanoidDescription.md) and [WaistAccessory](/docs/reference/engine/classes/HumanoidDescription.md), which are similar properties that apply accessories like this one - [Face](/docs/reference/engine/classes/HumanoidDescription.md), a property that determines what Face texture is used on the head ### Property: HumanoidDescription.FallAnimation ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Animation", "capabilities": [ "AvatarAppearance" ] } ``` When this description is [applied](/docs/reference/engine/classes/Humanoid.md) to a [Humanoid](/docs/reference/engine/classes/Humanoid.md), **FallAnimation** determines the [Animation.AnimationId](/docs/reference/engine/classes/Animation.md) to play when its [state](/docs/reference/engine/classes/Humanoid.md) is [Freefall](/docs/reference/engine/enums/HumanoidStateType.md). See also: - [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), for more information on [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). - [ClimbAnimation](/docs/reference/engine/classes/HumanoidDescription.md), [IdleAnimation](/docs/reference/engine/classes/HumanoidDescription.md), [JumpAnimation](/docs/reference/engine/classes/HumanoidDescription.md), [RunAnimation](/docs/reference/engine/classes/HumanoidDescription.md), [SwimAnimation](/docs/reference/engine/classes/HumanoidDescription.md) and [WalkAnimation](/docs/reference/engine/classes/HumanoidDescription.md), which are similar properties that determine animations to play on the rig ### Property: HumanoidDescription.FrontAccessory ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Accessories", "capabilities": [ "AvatarAppearance" ] } ``` **FrontAccessory** is a comma-separated list of asset IDs that determine what accessories should be added when the description is [applied](/docs/reference/engine/classes/Humanoid.md), usually those attached to front of its torso (such as medals or ties). The list does not contain duplicates. An error is thrown if you try to apply a new description which shares any assets with the existing description but a different accessory property. See also: - [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), for more information on [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). - [BackAccessory](/docs/reference/engine/classes/HumanoidDescription.md), [FaceAccessory](/docs/reference/engine/classes/HumanoidDescription.md), [HairAccessory](/docs/reference/engine/classes/HumanoidDescription.md), [HatAccessory](/docs/reference/engine/classes/HumanoidDescription.md), [NeckAccessory](/docs/reference/engine/classes/HumanoidDescription.md), [ShouldersAccessory](/docs/reference/engine/classes/HumanoidDescription.md) and [WaistAccessory](/docs/reference/engine/classes/HumanoidDescription.md), which are similar properties that apply accessories like this one ### Property: HumanoidDescription.GraphicTShirt ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Clothes", "capabilities": [ "AvatarAppearance" ] } ``` **GraphicTShirt** determines the [Graphic](/docs/reference/engine/classes/ShirtGraphic.md) used by a [ShirtGraphic](/docs/reference/engine/classes/ShirtGraphic.md) instance when [Humanoid:ApplyDescriptionAsync()](/docs/reference/engine/classes/Humanoid.md) is called on a [Humanoid](/docs/reference/engine/classes/Humanoid.md). The asset type must be for a **T‑Shirt**, not a **Decal** or **Image**. #### See Also - [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), for more information on [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). - [Shirt](/docs/reference/engine/classes/HumanoidDescription.md), which can provide the same functionality in addition to providing textures for the entire torso and arms - [TorsoColor](/docs/reference/engine/classes/HumanoidDescription.md), which can change the color of the torso underneath the t-shirt texture ### Property: HumanoidDescription.HairAccessory ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Accessories", "capabilities": [ "AvatarAppearance" ] } ``` **HairAccessory** is a comma-separated list of asset IDs that determine what accessories should be added when the description is [applied](/docs/reference/engine/classes/Humanoid.md), usually those attached to its head resembling hair. The list does not contain duplicates. An error is thrown if you try to apply a new description which shares any assets with the existing description but a different accessory property. See also: - [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), for more information on [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). - [BackAccessory](/docs/reference/engine/classes/HumanoidDescription.md), [FaceAccessory](/docs/reference/engine/classes/HumanoidDescription.md), [FrontAccessory](/docs/reference/engine/classes/HumanoidDescription.md), [HatAccessory](/docs/reference/engine/classes/HumanoidDescription.md), [NeckAccessory](/docs/reference/engine/classes/HumanoidDescription.md), [ShouldersAccessory](/docs/reference/engine/classes/HumanoidDescription.md) and [WaistAccessory](/docs/reference/engine/classes/HumanoidDescription.md), which are similar properties that apply accessories like this one ### Property: HumanoidDescription.HatAccessory ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Accessories", "capabilities": [ "AvatarAppearance" ] } ``` **HatAccessory** is a comma-separated list of asset IDs that determine what accessories should be added when the description is [applied](/docs/reference/engine/classes/Humanoid.md), usually those attached to its head. The list does not contain duplicates. An error is thrown if you try to apply a new description which shares any assets with the existing description but a different accessory property. See also: - [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), for more information on [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). - [BackAccessory](/docs/reference/engine/classes/HumanoidDescription.md), [FaceAccessory](/docs/reference/engine/classes/HumanoidDescription.md), [FrontAccessory](/docs/reference/engine/classes/HumanoidDescription.md), [HairAccessory](/docs/reference/engine/classes/HumanoidDescription.md), [NeckAccessory](/docs/reference/engine/classes/HumanoidDescription.md), [ShouldersAccessory](/docs/reference/engine/classes/HumanoidDescription.md) and [WaistAccessory](/docs/reference/engine/classes/HumanoidDescription.md), which are similar properties that apply accessories like this one ### Property: HumanoidDescription.Head ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Body Parts", "capabilities": [ "AvatarAppearance" ] } ``` **Head** determines the asset ID of the Head to be [applied](/docs/reference/engine/classes/Humanoid.md) to a [Humanoid](/docs/reference/engine/classes/Humanoid.md). See also: - [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), for more information on [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). - [Torso](/docs/reference/engine/classes/HumanoidDescription.md), [RightArm](/docs/reference/engine/classes/HumanoidDescription.md), [LeftArm](/docs/reference/engine/classes/HumanoidDescription.md), [RightLeg](/docs/reference/engine/classes/HumanoidDescription.md) and [LeftLeg](/docs/reference/engine/classes/HumanoidDescription.md), which are similar properties that also control body part - [HeadColor](/docs/reference/engine/classes/HumanoidDescription.md), which controls the color of this limb - [HatAccessory](/docs/reference/engine/classes/HumanoidDescription.md), [HairAccessory](/docs/reference/engine/classes/HumanoidDescription.md) and [FaceAccessory](/docs/reference/engine/classes/HumanoidDescription.md), which all can apply [Accessory](/docs/reference/engine/classes/Accessory.md) objects which are joined to the head ### Property: HumanoidDescription.HeadColor ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Body Colors", "capabilities": [ "AvatarAppearance" ] } ``` **HeadColor** determines the [BodyColors.HeadColor3](/docs/reference/engine/classes/BodyColors.md) and [BodyColors.HeadColor](/docs/reference/engine/classes/BodyColors.md) of a [Humanoid](/docs/reference/engine/classes/Humanoid.md) when the description is [applied](/docs/reference/engine/classes/Humanoid.md). See also: - [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), for more information on [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). - [TorsoColor](/docs/reference/engine/classes/HumanoidDescription.md), [LeftArmColor](/docs/reference/engine/classes/HumanoidDescription.md), [RightArmColor](/docs/reference/engine/classes/HumanoidDescription.md), [LeftLegColor](/docs/reference/engine/classes/HumanoidDescription.md), and [RightLegColor](/docs/reference/engine/classes/HumanoidDescription.md), which are similar properties that also control body colors - [Head](/docs/reference/engine/classes/HumanoidDescription.md), which controls the mesh used for the head - [Face](/docs/reference/engine/classes/HumanoidDescription.md), which applies a texture to the front of the head ### Property: HumanoidDescription.HeadScale ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Scale", "capabilities": [ "AvatarAppearance" ] } ``` **HeadScale** determines by what factor the **Head** object of a [Humanoid](/docs/reference/engine/classes/Humanoid.md) is scaled, as well as any accessories attached to it (such as those specified by [HatAccessory](/docs/reference/engine/classes/HumanoidDescription.md) and [HairAccessory](/docs/reference/engine/classes/HumanoidDescription.md)). When the description is applied through [Humanoid:ApplyDescriptionAsync()](/docs/reference/engine/classes/Humanoid.md), this value maps to a **HeadScale** [NumberValue](/docs/reference/engine/classes/NumberValue.md) within the [Humanoid](/docs/reference/engine/classes/Humanoid.md). #### See Also - [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), for more information on [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). - [BodyTypeScale](/docs/reference/engine/classes/HumanoidDescription.md) and [ProportionScale](/docs/reference/engine/classes/HumanoidDescription.md), which can provide realistic rig proportions - [WidthScale](/docs/reference/engine/classes/HumanoidDescription.md), [HeightScale](/docs/reference/engine/classes/HumanoidDescription.md) and [DepthScale](/docs/reference/engine/classes/HumanoidDescription.md), which provide finer control over other dimensions of a rig ### Property: HumanoidDescription.HeightScale ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Scale", "capabilities": [ "AvatarAppearance" ] } ``` **HeightScale** determines by what factor the height (top-to-bottom distance) of a [Humanoid](/docs/reference/engine/classes/Humanoid.md) is scaled, as well as all accessories not attached to its head. When the description is applied through [Humanoid:ApplyDescriptionAsync()](/docs/reference/engine/classes/Humanoid.md), this value maps to a **BodyHeightScale** [NumberValue](/docs/reference/engine/classes/NumberValue.md) within the [Humanoid](/docs/reference/engine/classes/Humanoid.md). #### See Also - [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), for more information on [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). - [BodyTypeScale](/docs/reference/engine/classes/HumanoidDescription.md) and [ProportionScale](/docs/reference/engine/classes/HumanoidDescription.md), which can provide more realistic rig proportions - [WidthScale](/docs/reference/engine/classes/HumanoidDescription.md) and [DepthScale](/docs/reference/engine/classes/HumanoidDescription.md) ### Property: HumanoidDescription.IdleAnimation ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Animation", "capabilities": [ "AvatarAppearance" ] } ``` When this description is [applied](/docs/reference/engine/classes/Humanoid.md) to a [Humanoid](/docs/reference/engine/classes/Humanoid.md), **IdleAnimation** determines the [Animation.AnimationId](/docs/reference/engine/classes/Animation.md) to play when its [state](/docs/reference/engine/classes/Humanoid.md) is [Running](/docs/reference/engine/enums/HumanoidStateType.md) at a speed near zero. See also: - [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), for more information on [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). - [ClimbAnimation](/docs/reference/engine/classes/HumanoidDescription.md), [FallAnimation](/docs/reference/engine/classes/HumanoidDescription.md), [JumpAnimation](/docs/reference/engine/classes/HumanoidDescription.md), [RunAnimation](/docs/reference/engine/classes/HumanoidDescription.md), [SwimAnimation](/docs/reference/engine/classes/HumanoidDescription.md) and [WalkAnimation](/docs/reference/engine/classes/HumanoidDescription.md), which are similar properties that determine animations to play on the rig ### Property: HumanoidDescription.JumpAnimation ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Animation", "capabilities": [ "AvatarAppearance" ] } ``` When this description is [applied](/docs/reference/engine/classes/Humanoid.md) to a [Humanoid](/docs/reference/engine/classes/Humanoid.md), **JumpAnimation** determines the [Animation.AnimationId](/docs/reference/engine/classes/Animation.md) to play when its [state](/docs/reference/engine/classes/Humanoid.md) is [Jumping](/docs/reference/engine/enums/HumanoidStateType.md). See also: - [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), for more information on [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). - [ClimbAnimation](/docs/reference/engine/classes/HumanoidDescription.md), [FallAnimation](/docs/reference/engine/classes/HumanoidDescription.md), [IdleAnimation](/docs/reference/engine/classes/HumanoidDescription.md), [RunAnimation](/docs/reference/engine/classes/HumanoidDescription.md), [SwimAnimation](/docs/reference/engine/classes/HumanoidDescription.md) and [WalkAnimation](/docs/reference/engine/classes/HumanoidDescription.md), which are similar properties that determine animations to play on the rig ### Property: HumanoidDescription.LeftArm ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Body Parts", "capabilities": [ "AvatarAppearance" ] } ``` **LeftArm** determines the asset ID of the LeftArm to be [applied](/docs/reference/engine/classes/Humanoid.md) to a [Humanoid](/docs/reference/engine/classes/Humanoid.md). See also: - [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), for more information on [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). - [Head](/docs/reference/engine/classes/HumanoidDescription.md), [Torso](/docs/reference/engine/classes/HumanoidDescription.md), [RightArm](/docs/reference/engine/classes/HumanoidDescription.md), [RightLeg](/docs/reference/engine/classes/HumanoidDescription.md) and [LeftLeg](/docs/reference/engine/classes/HumanoidDescription.md), which are similar properties that also control body part - [LeftArmColor](/docs/reference/engine/classes/HumanoidDescription.md), which controls the color of this limb ### Property: HumanoidDescription.LeftArmColor ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Body Colors", "capabilities": [ "AvatarAppearance" ] } ``` **LeftArmColor** determines the [BodyColors.LeftArmColor3](/docs/reference/engine/classes/BodyColors.md) and [BodyColors.LeftArmColor](/docs/reference/engine/classes/BodyColors.md) of a [Humanoid](/docs/reference/engine/classes/Humanoid.md) when the description is [applied](/docs/reference/engine/classes/Humanoid.md). For R15 and Rthro rigs, this property controls both the upper, lower, and hand parts of the left arm. See also: - [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), for more information on [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). - [HeadColor](/docs/reference/engine/classes/HumanoidDescription.md), [TorsoColor](/docs/reference/engine/classes/HumanoidDescription.md), [RightArmColor](/docs/reference/engine/classes/HumanoidDescription.md), [LeftLegColor](/docs/reference/engine/classes/HumanoidDescription.md), and [RightLegColor](/docs/reference/engine/classes/HumanoidDescription.md), which are similar properties that also control body colors - [LeftArm](/docs/reference/engine/classes/HumanoidDescription.md), which controls the mesh used for this limb - [Shirt](/docs/reference/engine/classes/HumanoidDescription.md), which can apply a texture to this limb ### Property: HumanoidDescription.LeftLeg ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Body Parts", "capabilities": [ "AvatarAppearance" ] } ``` **LeftLeg** determines the asset ID of the LeftLeg to be [applied](/docs/reference/engine/classes/Humanoid.md) to a [Humanoid](/docs/reference/engine/classes/Humanoid.md). See also: - [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), for more information on [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). - [Head](/docs/reference/engine/classes/HumanoidDescription.md), [Torso](/docs/reference/engine/classes/HumanoidDescription.md), [RightArm](/docs/reference/engine/classes/HumanoidDescription.md), [LeftArm](/docs/reference/engine/classes/HumanoidDescription.md), and [RightLeg](/docs/reference/engine/classes/HumanoidDescription.md), which are similar properties that also control body part - [LeftLegColor](/docs/reference/engine/classes/HumanoidDescription.md), which controls the color of this limb ### Property: HumanoidDescription.LeftLegColor ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Body Colors", "capabilities": [ "AvatarAppearance" ] } ``` **LeftLegColor** determines the [BodyColors.LeftLegColor3](/docs/reference/engine/classes/BodyColors.md) and [BodyColors.LeftLegColor](/docs/reference/engine/classes/BodyColors.md) of a [Humanoid](/docs/reference/engine/classes/Humanoid.md) when the description is [applied](/docs/reference/engine/classes/Humanoid.md). For R15 and Rthro rigs, this property controls both the upper, lower, and foot parts of the left leg. See also: - [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), for more information on [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). - [HeadColor](/docs/reference/engine/classes/HumanoidDescription.md), [TorsoColor](/docs/reference/engine/classes/HumanoidDescription.md), [LeftArmColor](/docs/reference/engine/classes/HumanoidDescription.md), [RightArmColor](/docs/reference/engine/classes/HumanoidDescription.md), and [RightLegColor](/docs/reference/engine/classes/HumanoidDescription.md), which are similar properties that also control body colors - [LeftLeg](/docs/reference/engine/classes/HumanoidDescription.md), which controls the mesh used for this limb - [Pants](/docs/reference/engine/classes/HumanoidDescription.md), which can apply a texture to this limb ### Property: HumanoidDescription.MoodAnimation ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Animation", "capabilities": [ "AvatarAppearance" ] } ``` ### Property: HumanoidDescription.NeckAccessory ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Accessories", "capabilities": [ "AvatarAppearance" ] } ``` **NeckAccessory** is a comma-separated list of asset IDs that determine what accessories should be added when the description is [applied](/docs/reference/engine/classes/Humanoid.md), usually those attached to its neck (such as scarves or necklaces). The list does not contain duplicates. Any accessory can used in this property, even if it is meant to go in a different accessory spot. For example, an accessory meant to go on your back (such as a cape) could be included in [HairAccessory](/docs/reference/engine/classes/HumanoidDescription.md). An error is thrown if you try to apply a new description which shares any assets with the existing description but a different accessory property. See also: - [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), for more information on [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). - [BackAccessory](/docs/reference/engine/classes/HumanoidDescription.md), [FaceAccessory](/docs/reference/engine/classes/HumanoidDescription.md), [FrontAccessory](/docs/reference/engine/classes/HumanoidDescription.md), [HairAccessory](/docs/reference/engine/classes/HumanoidDescription.md), [HatAccessory](/docs/reference/engine/classes/HumanoidDescription.md), [ShouldersAccessory](/docs/reference/engine/classes/HumanoidDescription.md) and [WaistAccessory](/docs/reference/engine/classes/HumanoidDescription.md), which are similar properties that apply accessories like this one ### Property: HumanoidDescription.Pants ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Clothes", "capabilities": [ "AvatarAppearance" ] } ``` **Pants** determines the [PantsTemplate](/docs/reference/engine/classes/Pants.md) used by a [Pants](/docs/reference/engine/classes/Pants.md) instance when [Humanoid:ApplyDescriptionAsync()](/docs/reference/engine/classes/Humanoid.md) is called on a [Humanoid](/docs/reference/engine/classes/Humanoid.md). The asset type must be for **Pants**, not a **Decal** or **Image**. #### See Also - [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), for more information on [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). - [Shirt](/docs/reference/engine/classes/HumanoidDescription.md), a similar property which applies to a [Shirt](/docs/reference/engine/classes/Shirt.md) object - [TorsoColor](/docs/reference/engine/classes/HumanoidDescription.md), [LeftLegColor](/docs/reference/engine/classes/HumanoidDescription.md) and [RightLegColor](/docs/reference/engine/classes/HumanoidDescription.md), which can change the color of the body parts underneath the pants texture ### Property: HumanoidDescription.ProportionScale ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Scale", "capabilities": [ "AvatarAppearance" ] } ``` **ProportionScale** determines how wide (0) or narrow (1) a [Humanoid](/docs/reference/engine/classes/Humanoid.md) rig is. Values outside the range of 0 to 1 are clamped. When the description is applied through [Humanoid:ApplyDescriptionAsync()](/docs/reference/engine/classes/Humanoid.md), this value maps to a **BodyProportionScale** [NumberValue](/docs/reference/engine/classes/NumberValue.md) within the [Humanoid](/docs/reference/engine/classes/Humanoid.md). Note that when the value of [BodyTypeScale](/docs/reference/engine/classes/HumanoidDescription.md) is 0, this property has no effect. #### See Also - [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), for more information on [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). - [BodyTypeScale](/docs/reference/engine/classes/HumanoidDescription.md), which also affects rig proportions - [WidthScale](/docs/reference/engine/classes/HumanoidDescription.md), [HeightScale](/docs/reference/engine/classes/HumanoidDescription.md) and [DepthScale](/docs/reference/engine/classes/HumanoidDescription.md), which provide finer control over the dimensions of a rig - [HeadScale](/docs/reference/engine/classes/HumanoidDescription.md), which provides specific control over the scale of the rig's head ### Property: HumanoidDescription.RightArm ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Body Parts", "capabilities": [ "AvatarAppearance" ] } ``` **RightArm** determines the asset ID of the RightArm to be [applied](/docs/reference/engine/classes/Humanoid.md) to a [Humanoid](/docs/reference/engine/classes/Humanoid.md). See also: - [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), for more information on [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). - [Head](/docs/reference/engine/classes/HumanoidDescription.md), [Torso](/docs/reference/engine/classes/HumanoidDescription.md), [LeftArm](/docs/reference/engine/classes/HumanoidDescription.md), [RightLeg](/docs/reference/engine/classes/HumanoidDescription.md) and [LeftLeg](/docs/reference/engine/classes/HumanoidDescription.md), which are similar properties that also control body part - [RightArmColor](/docs/reference/engine/classes/HumanoidDescription.md), which controls the color of this limb ### Property: HumanoidDescription.RightArmColor ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Body Colors", "capabilities": [ "AvatarAppearance" ] } ``` **RightArmColor** determines the [BodyColors.RightArmColor3](/docs/reference/engine/classes/BodyColors.md) and [BodyColors.RightArmColor](/docs/reference/engine/classes/BodyColors.md) of a [Humanoid](/docs/reference/engine/classes/Humanoid.md) when the description is [applied](/docs/reference/engine/classes/Humanoid.md). For R15 and Rthro rigs, this property controls both the upper, lower, and hand parts of the right arm. See also: - [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), for more information on [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). - [HeadColor](/docs/reference/engine/classes/HumanoidDescription.md), [TorsoColor](/docs/reference/engine/classes/HumanoidDescription.md), [LeftArmColor](/docs/reference/engine/classes/HumanoidDescription.md), [LeftLegColor](/docs/reference/engine/classes/HumanoidDescription.md), and [RightLegColor](/docs/reference/engine/classes/HumanoidDescription.md), which are similar properties that also control body colors - [RightArm](/docs/reference/engine/classes/HumanoidDescription.md), which controls the mesh used for this limb - [Shirt](/docs/reference/engine/classes/HumanoidDescription.md), which can apply a texture to this limb ### Property: HumanoidDescription.RightLeg ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Body Parts", "capabilities": [ "AvatarAppearance" ] } ``` **RightLeg** determines the asset ID of the RightLeg to be [applied](/docs/reference/engine/classes/Humanoid.md) to a [Humanoid](/docs/reference/engine/classes/Humanoid.md). See also: - [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), for more information on [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). - [Head](/docs/reference/engine/classes/HumanoidDescription.md), [Torso](/docs/reference/engine/classes/HumanoidDescription.md), [RightArm](/docs/reference/engine/classes/HumanoidDescription.md), [LeftArm](/docs/reference/engine/classes/HumanoidDescription.md) and [LeftLeg](/docs/reference/engine/classes/HumanoidDescription.md), which are similar properties that also control body part - [RightLegColor](/docs/reference/engine/classes/HumanoidDescription.md), which controls the color of this limb ### Property: HumanoidDescription.RightLegColor ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Body Colors", "capabilities": [ "AvatarAppearance" ] } ``` **RightLegColor** determines the [BodyColors.RightLegColor3](/docs/reference/engine/classes/BodyColors.md) and [BodyColors.RightLegColor](/docs/reference/engine/classes/BodyColors.md) of a [Humanoid](/docs/reference/engine/classes/Humanoid.md) when the description is [applied](/docs/reference/engine/classes/Humanoid.md). For R15 and Rthro rigs, this property controls both the upper, lower, and foot parts of the right leg. See also: - [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), for more information on [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). - [HeadColor](/docs/reference/engine/classes/HumanoidDescription.md), [TorsoColor](/docs/reference/engine/classes/HumanoidDescription.md), [LeftArmColor](/docs/reference/engine/classes/HumanoidDescription.md), [RightArmColor](/docs/reference/engine/classes/HumanoidDescription.md), and [LeftLegColor](/docs/reference/engine/classes/HumanoidDescription.md), which are similar properties that also control body colors - [RightLeg](/docs/reference/engine/classes/HumanoidDescription.md), which controls the mesh used for this limb - [Pants](/docs/reference/engine/classes/HumanoidDescription.md), which can apply a texture to this limb ### Property: HumanoidDescription.RunAnimation ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Animation", "capabilities": [ "AvatarAppearance" ] } ``` When this description is [applied](/docs/reference/engine/classes/Humanoid.md) to a [Humanoid](/docs/reference/engine/classes/Humanoid.md), **RunAnimation** determines the [Animation.AnimationId](/docs/reference/engine/classes/Animation.md) to play when its [state](/docs/reference/engine/classes/Humanoid.md) is [Running](/docs/reference/engine/enums/HumanoidStateType.md) at a moderate speed. See also: - [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), for more information on [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). - [ClimbAnimation](/docs/reference/engine/classes/HumanoidDescription.md), [FallAnimation](/docs/reference/engine/classes/HumanoidDescription.md), [IdleAnimation](/docs/reference/engine/classes/HumanoidDescription.md), [JumpAnimation](/docs/reference/engine/classes/HumanoidDescription.md), [SwimAnimation](/docs/reference/engine/classes/HumanoidDescription.md) and [WalkAnimation](/docs/reference/engine/classes/HumanoidDescription.md), which are similar properties that determine animations to play on the rig ### Property: HumanoidDescription.Shirt ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Clothes", "capabilities": [ "AvatarAppearance" ] } ``` **Shirt** determines the [ShirtTemplate](/docs/reference/engine/classes/Shirt.md) used by a [Shirt](/docs/reference/engine/classes/Shirt.md) instance when [Humanoid:ApplyDescriptionAsync()](/docs/reference/engine/classes/Humanoid.md) is called on a [Humanoid](/docs/reference/engine/classes/Humanoid.md). The asset type must be for **Shirt**, not a **Decal** or **Image**. #### See Also - [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), for more information on [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). - [Pants](/docs/reference/engine/classes/HumanoidDescription.md), a similar property which applies to a [Pants](/docs/reference/engine/classes/Pants.md) object - [GraphicTShirt](/docs/reference/engine/classes/HumanoidDescription.md), a similar property which applies to a [ShirtGraphic](/docs/reference/engine/classes/ShirtGraphic.md) object - [TorsoColor](/docs/reference/engine/classes/HumanoidDescription.md), [LeftArmColor](/docs/reference/engine/classes/HumanoidDescription.md) and [RightArmColor](/docs/reference/engine/classes/HumanoidDescription.md), which can change the color of the body parts underneath the shirt texture ### Property: HumanoidDescription.ShouldersAccessory ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Accessories", "capabilities": [ "AvatarAppearance" ] } ``` **ShouldersAccessory** is a comma-separated list of asset IDs that determine what accessories should be added when the description is [applied](/docs/reference/engine/classes/Humanoid.md), usually those attached to its shoulders (such as shoulder-mounted critters). The list does not contain duplicates. An error is thrown if you try to apply a new description which shares any assets with the existing description but a different accessory property. See also: - [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), for more information on [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). - [BackAccessory](/docs/reference/engine/classes/HumanoidDescription.md), [FaceAccessory](/docs/reference/engine/classes/HumanoidDescription.md), [FrontAccessory](/docs/reference/engine/classes/HumanoidDescription.md), [HairAccessory](/docs/reference/engine/classes/HumanoidDescription.md), [HatAccessory](/docs/reference/engine/classes/HumanoidDescription.md), [NeckAccessory](/docs/reference/engine/classes/HumanoidDescription.md) and [WaistAccessory](/docs/reference/engine/classes/HumanoidDescription.md), which are similar properties that apply accessories like this one ### Property: HumanoidDescription.StaticFacialAnimation ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Animation", "capabilities": [ "AvatarAppearance" ] } ``` **StaticFacialAnimation** controls whether facial animations play when this description is applied to a [Humanoid](/docs/reference/engine/classes/Humanoid.md) with a Dynamic Head. When set to `true`, the head displays a static mood pose without any animated facial movement, achieving visual parity with classic face decals. Even with `StaticFacialAnimation` set to `true`, [MoodAnimation](/docs/reference/engine/classes/HumanoidDescription.md) still applies a static pose to give the face its characteristic expression. Setting `StaticFacialAnimation` to `true` is different from setting `MoodAnimation` to `0`, which would result in the Dynamic Head's neutral rest pose. This is a per-description setting and does not affect other descriptions or the user's avatar globally. See also: - [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), for more information on [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). ### Property: HumanoidDescription.SwimAnimation ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Animation", "capabilities": [ "AvatarAppearance" ] } ``` When this description is [applied](/docs/reference/engine/classes/Humanoid.md) to a [Humanoid](/docs/reference/engine/classes/Humanoid.md), **SwimAnimation** determines the [Animation.AnimationId](/docs/reference/engine/classes/Animation.md) to play when its [state](/docs/reference/engine/classes/Humanoid.md) is [Swimming](/docs/reference/engine/enums/HumanoidStateType.md) See also: - [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), for more information on [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). - [ClimbAnimation](/docs/reference/engine/classes/HumanoidDescription.md), [FallAnimation](/docs/reference/engine/classes/HumanoidDescription.md), [IdleAnimation](/docs/reference/engine/classes/HumanoidDescription.md), [JumpAnimation](/docs/reference/engine/classes/HumanoidDescription.md), [RunAnimation](/docs/reference/engine/classes/HumanoidDescription.md) and [WalkAnimation](/docs/reference/engine/classes/HumanoidDescription.md), which are similar properties that determine animations to play on the rig ### Property: HumanoidDescription.Torso ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Body Parts", "capabilities": [ "AvatarAppearance" ] } ``` **Torso** determines the asset ID of the Torso to be [applied](/docs/reference/engine/classes/Humanoid.md) to a [Humanoid](/docs/reference/engine/classes/Humanoid.md). See also: - [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), for more information on [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). - [Head](/docs/reference/engine/classes/HumanoidDescription.md), [RightArm](/docs/reference/engine/classes/HumanoidDescription.md), [LeftArm](/docs/reference/engine/classes/HumanoidDescription.md), [RightLeg](/docs/reference/engine/classes/HumanoidDescription.md) and [LeftLeg](/docs/reference/engine/classes/HumanoidDescription.md), which are similar properties that also control body part - [TorsoColor](/docs/reference/engine/classes/HumanoidDescription.md), which controls the color of this limb ### Property: HumanoidDescription.TorsoColor ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Body Colors", "capabilities": [ "AvatarAppearance" ] } ``` **TorsoColor** determines the [BodyColors.TorsoColor3](/docs/reference/engine/classes/BodyColors.md) and [BodyColors.TorsoColor](/docs/reference/engine/classes/BodyColors.md) of a [Humanoid](/docs/reference/engine/classes/Humanoid.md) when the description is [applied](/docs/reference/engine/classes/Humanoid.md). For R15 and Rthro rigs, this property controls both the upper and lower parts of the torso. See also: - [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), for more information on [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). - [HeadColor](/docs/reference/engine/classes/HumanoidDescription.md), [TorsoColor](/docs/reference/engine/classes/HumanoidDescription.md), [LeftArmColor](/docs/reference/engine/classes/HumanoidDescription.md), [RightArmColor](/docs/reference/engine/classes/HumanoidDescription.md), [LeftLegColor](/docs/reference/engine/classes/HumanoidDescription.md), and [RightLegColor](/docs/reference/engine/classes/HumanoidDescription.md), which are similar properties that also control body colors - [Torso](/docs/reference/engine/classes/HumanoidDescription.md), which controls the mesh used for this body part - [GraphicTShirt](/docs/reference/engine/classes/HumanoidDescription.md) and [Shirt](/docs/reference/engine/classes/HumanoidDescription.md), which can apply a texture to this body part ### Property: HumanoidDescription.UseAvatarSettings ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarAppearance" ] } ``` When true, `Humanoid:ApplyDescriptionAsync` or `Players:CreateHumanoidModelFromDescriptionAsync` will also apply the Avatar Settings set for the universe to the model. ### Property: HumanoidDescription.WaistAccessory ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Accessories", "capabilities": [ "AvatarAppearance" ] } ``` **WaistAccessory** is a comma-separated list of asset IDs that determine what accessories should be added when the description is [applied](/docs/reference/engine/classes/Humanoid.md), usually those attached to its waist (such as belts). The list does not contain duplicates. An error is thrown if you try to apply a new description which shares any assets with the existing description but a different accessory property. See also: - [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), for more information on [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). - [BackAccessory](/docs/reference/engine/classes/HumanoidDescription.md), [FaceAccessory](/docs/reference/engine/classes/HumanoidDescription.md), [FrontAccessory](/docs/reference/engine/classes/HumanoidDescription.md), [HairAccessory](/docs/reference/engine/classes/HumanoidDescription.md), [HatAccessory](/docs/reference/engine/classes/HumanoidDescription.md), [NeckAccessory](/docs/reference/engine/classes/HumanoidDescription.md) and [ShouldersAccessory](/docs/reference/engine/classes/HumanoidDescription.md), which are similar properties that apply accessories like this one ### Property: HumanoidDescription.WalkAnimation ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Animation", "capabilities": [ "AvatarAppearance" ] } ``` When this description is [applied](/docs/reference/engine/classes/Humanoid.md) to a [Humanoid](/docs/reference/engine/classes/Humanoid.md), **WalkAnimation** determines the [Animation.AnimationId](/docs/reference/engine/classes/Animation.md) to play when its [state](/docs/reference/engine/classes/Humanoid.md) is [Running](/docs/reference/engine/enums/HumanoidStateType.md) at a low speed See also: - [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), for more information on [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). - [ClimbAnimation](/docs/reference/engine/classes/HumanoidDescription.md), [FallAnimation](/docs/reference/engine/classes/HumanoidDescription.md), [IdleAnimation](/docs/reference/engine/classes/HumanoidDescription.md), [JumpAnimation](/docs/reference/engine/classes/HumanoidDescription.md), [RunAnimation](/docs/reference/engine/classes/HumanoidDescription.md) and [SwimAnimation](/docs/reference/engine/classes/HumanoidDescription.md), which are similar properties that determine animations to play on the rig ### Property: HumanoidDescription.WidthScale ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Scale", "capabilities": [ "AvatarAppearance" ] } ``` **WidthScale** determines by what factor the width (left-to-right distance) of a [Humanoid](/docs/reference/engine/classes/Humanoid.md) is scaled, as well as all accessories not attached to its head. When the description is applied through [Humanoid:ApplyDescriptionAsync()](/docs/reference/engine/classes/Humanoid.md), this value maps to a **BodyWidthScale** [NumberValue](/docs/reference/engine/classes/NumberValue.md) within the [Humanoid](/docs/reference/engine/classes/Humanoid.md). #### See Also - [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), for more information on [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). - [BodyTypeScale](/docs/reference/engine/classes/HumanoidDescription.md) and [ProportionScale](/docs/reference/engine/classes/HumanoidDescription.md), which can provide more realistic rig proportions - [HeightScale](/docs/reference/engine/classes/HumanoidDescription.md) and [DepthScale](/docs/reference/engine/classes/HumanoidDescription.md) ## Methods ### Method: HumanoidDescription:AddEmote **Signature:** `HumanoidDescription:AddEmote(name: string, assetId: int64): ()` **AddEmote** will add an Emote asset to the description given a name and its asset ID. The asset ID must be for an "Emote" asset (see [Featured emotes](https://www.roblox.com/catalog?Category=0&Subcategory=39) in the Catalog). You can add multiple emotes of the same name. All emotes of the same name can be removed using [RemoveEmote](/docs/reference/engine/classes/HumanoidDescription.md). If an emote with the same ID is added under the same name, [EmotesChanged](/docs/reference/engine/classes/HumanoidDescription.md) fires. See also: - [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), for more information on [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). - [GetEmotes](/docs/reference/engine/classes/HumanoidDescription.md), which can be used to retrieve the emotes that have been added by this function - [SetEmotes](/docs/reference/engine/classes/HumanoidDescription.md) and [RemoveEmote](/docs/reference/engine/classes/HumanoidDescription.md), which also manipulate what emotes have been added - [EmotesChanged](/docs/reference/engine/classes/HumanoidDescription.md), which fires after this function is called *Security: None · Thread Safety: Unsafe · Capabilities: AvatarAppearance* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name` | `string` | | A string that identifies what emote is being added. Example: `"Salute"`. | | `assetId` | `int64` | | An emote asset ID. | **Returns:** `()` ### Method: HumanoidDescription:GetAccessories **Signature:** `HumanoidDescription:GetAccessories(includeRigidAccessories: boolean): List` Returns a table of an avatar's current accessories. If the second parameter (includeRigidAccessories) is true then the returned table will also include entries for rigid accessories from the rigid accessory properties. *Security: None · Thread Safety: Unsafe · Capabilities: AvatarAppearance* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `includeRigidAccessories` | `boolean` | | Set to true if rigid accessories from the rigid accessory properties should also be included in the return array. False means only include layered clothing accessories from the AccessoryBlob. | **Returns:** `List` — Returns an array where each entry specifies for an individual accessory the AccessoryType, AssetId, IsLayered, Order and Puffiness. **Get Accessories** This code sample prints out all the asset IDs and types of the accessories in the HumanoidDescription. ```lua local includeRigidAccessories = true local accessoriesTable = game:GetService("Players"):GetHumanoidDescriptionFromUserIdAsync(1):GetAccessories(includeRigidAccessories) for _, accessoryInfo in ipairs(accessoriesTable) do print(tostring(accessoryInfo.AssetId) .. " " .. tostring(accessoryInfo.AccessoryType)) end ``` **Expected output:** When run, all the asset IDs and types are printed out for the accessories in the HumanoidDescription. ### Method: HumanoidDescription:GetEmotes **Signature:** `HumanoidDescription:GetEmotes(): Dictionary` **GetEmotes** returns a dictionary of emotes that have been [added](/docs/reference/engine/classes/HumanoidDescription.md) or [set](/docs/reference/engine/classes/HumanoidDescription.md) to this description. The keys of this dictionary are the names of the emotes, and the values are a non-empty array of emote IDs for that name. #### Example ```lua local hd = Instance.new("HumanoidDescription") hd:AddEmote("Salute", 3360689775) local emotes = hd:GetEmotes() for name, ids in emotes do print(("The emote %s has %d ids:"):format(name, #ids)) for _, id in ids do print(id) end end ``` See also: - [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), for more information on [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). - [SetEmotes](/docs/reference/engine/classes/HumanoidDescription.md) and [AddEmote](/docs/reference/engine/classes/HumanoidDescription.md), which can add emotes that may be returned by this function - [EmotesChanged](/docs/reference/engine/classes/HumanoidDescription.md), which fires with the value returned this function after it may have changed *Security: None · Thread Safety: Unsafe · Capabilities: AvatarAppearance* **Returns:** `Dictionary` — A dictionary of emotes where the key is the emote name and the value is an array of emote asset IDs. Example: ```lua { Salute = {3360689775}, Agree = {4849487550}, Disagree = {4849495710} } ``` . ### Method: HumanoidDescription:GetEquippedEmotes **Signature:** `HumanoidDescription:GetEquippedEmotes(): Array` **GetEquippedEmotes** returns an array of tables which indicate the `Name` and `Slot` of each equipped emote as it was set by [SetEquippedEmotes](/docs/reference/engine/classes/HumanoidDescription.md). #### Example ```lua local hd = Instance.new("HumanoidDescription") hd:SetEmotes({Salute = {3360689775}, Agree = {4849487550}}) hd:SetEquippedEmotes({"Salute", "Agree"}) -- Iterate over the equipped emotes: for _, t in hd:GetEquippedEmotes() do print(("In slot %d: emote %s is equipped"):format(t.Slot, t.Name)) end ``` See also: - [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), for more information on [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). - [SetEquippedEmotes](/docs/reference/engine/classes/HumanoidDescription.md), which sets the currently equipped emotes and changes what this function returns - [EquippedEmotesChanged](/docs/reference/engine/classes/HumanoidDescription.md), which fires when the function returned by this value may have changed *Security: None · Thread Safety: Unsafe · Capabilities: AvatarAppearance* **Returns:** `Array` — An array of tables describing the name and slot which each emote is equipped. Example: ```lua { {Slot = 3, Name = "Salute"}, {Slot = 2, Name = "Agree"}, {Slot = 1, Name = "Disagree"}, } ``` . ### Method: HumanoidDescription:RemoveEmote **Signature:** `HumanoidDescription:RemoveEmote(name: string): ()` **RemoveEmote** removes all emotes from the description that have been [added](/docs/reference/engine/classes/HumanoidDescription.md) or [set](/docs/reference/engine/classes/HumanoidDescription.md) under the given name. If there are no added emotes with the given name, no error is thrown and [EmotesChanged](/docs/reference/engine/classes/HumanoidDescription.md) **does not** fire. See also: - [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), for more information on [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). - [SetEmotes](/docs/reference/engine/classes/HumanoidDescription.md) and [AddEmote](/docs/reference/engine/classes/HumanoidDescription.md), which can add emotes that may be removed - [GetEmotes](/docs/reference/engine/classes/HumanoidDescription.md), which can retrieve a dictionary of emotes that may be removed *Security: None · Thread Safety: Unsafe · Capabilities: AvatarAppearance* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name` | `string` | | The name of the emote as it was [set](/docs/reference/engine/classes/HumanoidDescription.md) or [added](/docs/reference/engine/classes/HumanoidDescription.md). | **Returns:** `()` ### Method: HumanoidDescription:SetAccessories **Signature:** `HumanoidDescription:SetAccessories(accessories: Array, includeRigidAccessories: boolean): ()` Accepts a table that sets the accessories and related properties for an avatar. If the second parameter (includeRigidAccessories) is true, then this function can also be used to set the rigid accessories in the rigid accessory properties. In this case any table entry that does not have an Order will be considered a rigid accessory and put in the appropriate property according to the AccessoryType. *Security: None · Thread Safety: Unsafe · Capabilities: AvatarAppearance* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `accessories` | `Array` | | Each entry specifies for an individual accessory the AccessoryType, AssetId, IsLayered, Order and Puffiness. | | `includeRigidAccessories` | `boolean` | | Set to true if rigid accessories are also included in the passed in array (they would have to not specify Order). | **Returns:** `()` **Set Accessories** This code sample adds two accessories to a HumanoidDescription. ```lua local humanoidDescription = Instance.new("HumanoidDescription") local originalSpecifications = { { Order = 1, AssetId = 123456789, Puffiness = 0.5, AccessoryType = Enum.AccessoryType.Sweater, }, } humanoidDescription:SetAccessories(originalSpecifications) local updatedSpecifications = humanoidDescription:GetAccessories(false) local newIndividualSpecification = { Order = 2, AssetId = 987654321, Puffiness = 0.7, AccessoryType = Enum.AccessoryType.Jacket, IsLayered = true, } updatedSpecifications[#updatedSpecifications + 1] = newIndividualSpecification humanoidDescription:SetAccessories(updatedSpecifications) ``` **Expected output:** When run, the HumanoidDescription will have two accessories. ### Method: HumanoidDescription:SetEmotes **Signature:** `HumanoidDescription:SetEmotes(emotes: Dictionary): ()` **SetEmotes** sets all of the emotes on this description given a table similar to that returned by [GetEmotes](/docs/reference/engine/classes/HumanoidDescription.md). It fires [EmotesChanged](/docs/reference/engine/classes/HumanoidDescription.md) #### Example ```lua local emotes = { Salute = {3360689775}, -- Syntax note: can also use ["Salute"] = ... Agree = {4849487550}, Disagree = {4849495710} } local hd = Instance.new("HumanoidDescription") hd:SetEmotes(emotes) ``` See also: - [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), for more information on [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). - [AddEmote](/docs/reference/engine/classes/HumanoidDescription.md) and [RemoveEmote](/docs/reference/engine/classes/HumanoidDescription.md) which can modify the added emotes on an individual level - [EmotesChanged](/docs/reference/engine/classes/HumanoidDescription.md), which fires when this function is called *Security: None · Thread Safety: Unsafe · Capabilities: AvatarAppearance* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `emotes` | `Dictionary` | | A dictionary of emotes where the key is the emote name and the value is an array of emote asset IDs. Example: ```lua { Salute = {3360689775}, Agree = {4849487550}, Disagree = {4849495710} } ``` . | **Returns:** `()` ### Method: HumanoidDescription:SetEquippedEmotes **Signature:** `HumanoidDescription:SetEquippedEmotes(equippedEmotes: Array): ()` **SetEquippedEmotes** sets the currently equipped emotes given an array of emote names as they were passed to [AddEmote](/docs/reference/engine/classes/HumanoidDescription.md) or [SetEmotes](/docs/reference/engine/classes/HumanoidDescription.md). It can also take an array of tables similar to that returned by [GetEquippedEmotes](/docs/reference/engine/classes/HumanoidDescription.md). Calling this function fires [EquippedEmotesChanged](/docs/reference/engine/classes/HumanoidDescription.md). #### Example ```lua local hd = Instance.new("HumanoidDescription") hd:SetEmotes({Salute = {3360689775}, Agree = {4849487550}}) -- Can provide either an array of strings... (index is slot number) hd:SetEquippedEmotes({"Salute", "Agree"}) -- ...or an array of tables as returned by GetEquippedEmotes (Slot and Name keys set) hd:SetEquippedEmotes({{Slot = 1, Name = "Salute"}, {Slot = 2, Name = "Agree"}}) ``` See also: - [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), for more information on [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). - [GetEquippedEmotes](/docs/reference/engine/classes/HumanoidDescription.md), which returns a value describing the equipped emotes set by this function - [EquippedEmotesChanged](/docs/reference/engine/classes/HumanoidDescription.md), which fires when this function is called *Security: None · Thread Safety: Unsafe · Capabilities: AvatarAppearance* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `equippedEmotes` | `Array` | | An array of emote names. Example: ```lua { "Disagree", "Agree", "Salute" } ``` – OR – An array of tables describing the name and slot which each emote is equipped. Example: ```lua { {Slot = 3, Name = "Salute"}, {Slot = 2, Name = "Agree"}, {Slot = 1, Name = "Disagree"}, } ``` . | **Returns:** `()` ## Events ### Event: HumanoidDescription.EmotesChanged **Signature:** `HumanoidDescription.EmotesChanged(newEmotes: Dictionary)` **EmotesChanged** fires when emotes are [added](/docs/reference/engine/classes/HumanoidDescription.md), [removed](/docs/reference/engine/classes/HumanoidDescription.md) or [set](/docs/reference/engine/classes/HumanoidDescription.md) on the description. The event fires with the new emote table as returned by [GetEmotes](/docs/reference/engine/classes/HumanoidDescription.md). If [AddEmote](/docs/reference/engine/classes/HumanoidDescription.md) is called with the same name and ID as an existing emote, this event fires. See also: - [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), for more information on [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). - [AddEmote](/docs/reference/engine/classes/HumanoidDescription.md), [RemoveEmote](/docs/reference/engine/classes/HumanoidDescription.md) and [HumanoidDescription:SetEmotes()](/docs/reference/engine/classes/HumanoidDescription.md), which can cause this event to be fired *Security: None · Capabilities: AvatarAppearance* **Parameters:** | Name | Type | Description | |------|------|-------------| | `newEmotes` | `Dictionary` | A dictionary of emotes where the key is the emote name and the value is an array of emote asset IDs. Example: ```lua { Salute = {3360689775}, Agree = {4849487550}, Disagree = {4849495710} } ``` . | ### Event: HumanoidDescription.EquippedEmotesChanged **Signature:** `HumanoidDescription.EquippedEmotesChanged(newEquippedEmotes: Array)` **EquippedEmotesChanged** fires when the equipped emotes are set on this description using [SetEquippedEmotes](/docs/reference/engine/classes/HumanoidDescription.md). It provides the new equipped emotes in a table like that returned by [GetEquippedEmotes](/docs/reference/engine/classes/HumanoidDescription.md). #### Example ```lua local hd = Instance.new("HumanoidDescription") hd.EquippedEmotesChanged:Connect(function(equippedEmotes) print(("We have %d emotes equipped"):format(#equippedEmotes)) for _, t in equippedEmotes do print(("In slot %d: emote %s is equipped"):format(t.Slot, t.Name)) end end) hd:SetEquippedEmotes({"Salute", "Agree"}) --> We have 2 emotes equipped ``` See also: - [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), for more information on [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). - [SetEquippedEmotes](/docs/reference/engine/classes/HumanoidDescription.md), which fires this event - [GetEquippedEmotes](/docs/reference/engine/classes/HumanoidDescription.md), which can be used to query the currently equipped emotes without this event firing *Security: None · Capabilities: AvatarAppearance* **Parameters:** | Name | Type | Description | |------|------|-------------| | `newEquippedEmotes` | `Array` | An array of tables describing the name and slot which each emote is equipped. Example: ```lua { {Slot = 3, Name = "Salute"}, {Slot = 2, Name = "Agree"}, {Slot = 1, Name = "Disagree"}, } ``` . | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: HumanoidRigDescription last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Animation summary: "Stores the joint mapping, T-pose, and per-joint properties for a 22-joint bipedal character rig. Joints may be AnimationConstraint, Motor6D, or Bone instances." --- # Class: HumanoidRigDescription > Stores the joint mapping, T-pose, and per-joint properties for a 22-joint > bipedal character rig. Joints may be [AnimationConstraint](/docs/reference/engine/classes/AnimationConstraint.md), > [Motor6D](/docs/reference/engine/classes/Motor6D.md), or [Bone](/docs/reference/engine/classes/Bone.md) instances. ## Description Stores the joint mapping, reference T-pose and per-joint properties for a bipedal `Humanoid` character rig. Each of the 22 joints in the rig hierarchy can reference an `Instance` (typically a `Motor6D`, `AnimationConstraint` or `Bone`), define a T-pose adjustment transform, rotation range limits, and carry a size for volumetric visualization and retargeting. For an example Blender and Maya rig hierarchy with all optional joints, see [Character specifications - Higher-fidelity rigs](/docs/en-us/avatar/character-bodies/specifications.md#higher-fidelity-rigs). Use `AutoRig` to automatically populate joint references from a character model, or assign joints individually. Supports both R6 (6-joint) and R15 (15-joint) rig subsets in addition to the full 22-joint standard set, as well as rigs with more than 22 joints by mapping the key subset of joints. ## Properties ### Property: HumanoidRigDescription.Chest ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Mapping", "capabilities": [ "Animation" ] } ``` The [Motor6D](/docs/reference/engine/classes/Motor6D.md), [AnimationConstraint](/docs/reference/engine/classes/AnimationConstraint.md), or [Bone](/docs/reference/engine/classes/Bone.md) joint instance for the chest joint. This joint is the branch point for the neck chain and both clavicle chains. ### Property: HumanoidRigDescription.ChestRangeMax ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` Maximum rotation range in degrees for the chest joint along each local axis (X, Y, Z). ### Property: HumanoidRigDescription.ChestRangeMin ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` Minimum rotation range in degrees for the chest joint along each local axis (X, Y, Z). ### Property: HumanoidRigDescription.ChestSize ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Size", "capabilities": [ "Animation" ] } ``` Size of the chest joint's volume sphere, used for rig visualization and collision. ### Property: HumanoidRigDescription.ChestTposeAdjustment ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tpose", "capabilities": [ "Animation" ] } ``` Coordinate frame adjustment applied to the chest joint to align it with the T-pose reference orientation. ### Property: HumanoidRigDescription.HeadBase ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Mapping", "capabilities": [ "Animation" ] } ``` The [Motor6D](/docs/reference/engine/classes/Motor6D.md), [AnimationConstraint](/docs/reference/engine/classes/AnimationConstraint.md), or [Bone](/docs/reference/engine/classes/Bone.md) joint instance for the head base joint. This is the terminal joint of the head chain. ### Property: HumanoidRigDescription.HeadBaseRangeMax ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` Maximum rotation range in degrees for the head base joint along each local axis (X, Y, Z). ### Property: HumanoidRigDescription.HeadBaseRangeMin ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` Minimum rotation range in degrees for the head base joint along each local axis (X, Y, Z). ### Property: HumanoidRigDescription.HeadBaseSize ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Size", "capabilities": [ "Animation" ] } ``` Diameter of the head base joint's volume sphere, used for rig visualization and collision. ### Property: HumanoidRigDescription.HeadBaseTposeAdjustment ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tpose", "capabilities": [ "Animation" ] } ``` Coordinate frame adjustment applied to the head base joint to align it with the T-pose reference orientation. ### Property: HumanoidRigDescription.LeftAnkle ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Mapping", "capabilities": [ "Animation" ] } ``` The [Motor6D](/docs/reference/engine/classes/Motor6D.md), [AnimationConstraint](/docs/reference/engine/classes/AnimationConstraint.md), or [Bone](/docs/reference/engine/classes/Bone.md) joint instance for the left ankle joint. ### Property: HumanoidRigDescription.LeftAnkleRangeMax ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` Maximum rotation range in degrees for the left ankle joint along each local axis (X, Y, Z). ### Property: HumanoidRigDescription.LeftAnkleRangeMin ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` Minimum rotation range in degrees for the left ankle joint along each local axis (X, Y, Z). ### Property: HumanoidRigDescription.LeftAnkleSize ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Size", "capabilities": [ "Animation" ] } ``` Diameter of the left ankle joint's volume sphere, used for rig visualization and collision. ### Property: HumanoidRigDescription.LeftAnkleTposeAdjustment ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tpose", "capabilities": [ "Animation" ] } ``` Coordinate frame adjustment applied to the left ankle joint to align it with the T-pose reference orientation. ### Property: HumanoidRigDescription.LeftClavicle ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Mapping", "capabilities": [ "Animation" ] } ``` The [Motor6D](/docs/reference/engine/classes/Motor6D.md), [AnimationConstraint](/docs/reference/engine/classes/AnimationConstraint.md), or [Bone](/docs/reference/engine/classes/Bone.md) joint instance for the left clavicle (collarbone) joint. ### Property: HumanoidRigDescription.LeftClavicleRangeMax ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` Maximum rotation range in degrees for the left clavicle joint along each local axis (X, Y, Z). ### Property: HumanoidRigDescription.LeftClavicleRangeMin ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` Minimum rotation range in degrees for the left clavicle joint along each local axis (X, Y, Z). ### Property: HumanoidRigDescription.LeftClavicleSize ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Size", "capabilities": [ "Animation" ] } ``` Diameter of the left clavicle joint's volume sphere, used for rig visualization and collision. ### Property: HumanoidRigDescription.LeftClavicleTposeAdjustment ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tpose", "capabilities": [ "Animation" ] } ``` Coordinate frame adjustment applied to the left clavicle joint to align it with the T-pose reference orientation. ### Property: HumanoidRigDescription.LeftElbow ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Mapping", "capabilities": [ "Animation" ] } ``` The [Motor6D](/docs/reference/engine/classes/Motor6D.md), [AnimationConstraint](/docs/reference/engine/classes/AnimationConstraint.md), or [Bone](/docs/reference/engine/classes/Bone.md) joint instance for the left elbow joint. ### Property: HumanoidRigDescription.LeftElbowRangeMax ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` Maximum rotation range in degrees for the left elbow joint along each local axis (X, Y, Z). ### Property: HumanoidRigDescription.LeftElbowRangeMin ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` Minimum rotation range in degrees for the left elbow joint along each local axis (X, Y, Z). ### Property: HumanoidRigDescription.LeftElbowSize ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Size", "capabilities": [ "Animation" ] } ``` Diameter of the left elbow joint's volume sphere, used for rig visualization and collision. ### Property: HumanoidRigDescription.LeftElbowTposeAdjustment ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tpose", "capabilities": [ "Animation" ] } ``` Coordinate frame adjustment applied to the left elbow joint to align it with the T-pose reference orientation. ### Property: HumanoidRigDescription.LeftHip ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Mapping", "capabilities": [ "Animation" ] } ``` The [Motor6D](/docs/reference/engine/classes/Motor6D.md), [AnimationConstraint](/docs/reference/engine/classes/AnimationConstraint.md), or [Bone](/docs/reference/engine/classes/Bone.md) joint instance for the left hip joint. This is the root of the left leg chain. ### Property: HumanoidRigDescription.LeftHipRangeMax ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` Maximum rotation range in degrees for the left hip joint along each local axis (X, Y, Z). ### Property: HumanoidRigDescription.LeftHipRangeMin ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` Minimum rotation range in degrees for the left hip joint along each local axis (X, Y, Z). ### Property: HumanoidRigDescription.LeftHipSize ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Size", "capabilities": [ "Animation" ] } ``` Diameter of the left hip joint's volume sphere, used for rig visualization and collision. ### Property: HumanoidRigDescription.LeftHipTposeAdjustment ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tpose", "capabilities": [ "Animation" ] } ``` Coordinate frame adjustment applied to the left hip joint to align it with the T-pose reference orientation. ### Property: HumanoidRigDescription.LeftKnee ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Mapping", "capabilities": [ "Animation" ] } ``` The [Motor6D](/docs/reference/engine/classes/Motor6D.md), [AnimationConstraint](/docs/reference/engine/classes/AnimationConstraint.md), or [Bone](/docs/reference/engine/classes/Bone.md) joint instance for the left knee joint. ### Property: HumanoidRigDescription.LeftKneeRangeMax ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` Maximum rotation range in degrees for the left knee joint along each local axis (X, Y, Z). ### Property: HumanoidRigDescription.LeftKneeRangeMin ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` Minimum rotation range in degrees for the left knee joint along each local axis (X, Y, Z). ### Property: HumanoidRigDescription.LeftKneeSize ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Size", "capabilities": [ "Animation" ] } ``` Diameter of the left knee joint's volume sphere, used for rig visualization and collision. ### Property: HumanoidRigDescription.LeftKneeTposeAdjustment ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tpose", "capabilities": [ "Animation" ] } ``` Coordinate frame adjustment applied to the left knee joint to align it with the T-pose reference orientation. ### Property: HumanoidRigDescription.LeftShoulder ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Mapping", "capabilities": [ "Animation" ] } ``` The [Motor6D](/docs/reference/engine/classes/Motor6D.md), [AnimationConstraint](/docs/reference/engine/classes/AnimationConstraint.md), or [Bone](/docs/reference/engine/classes/Bone.md) joint instance for the left shoulder joint. ### Property: HumanoidRigDescription.LeftShoulderRangeMax ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` Maximum rotation range in degrees for the left shoulder joint along each local axis (X, Y, Z). ### Property: HumanoidRigDescription.LeftShoulderRangeMin ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` Minimum rotation range in degrees for the left shoulder joint along each local axis (X, Y, Z). ### Property: HumanoidRigDescription.LeftShoulderSize ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Size", "capabilities": [ "Animation" ] } ``` Diameter of the left shoulder joint's volume sphere, used for rig visualization and collision. ### Property: HumanoidRigDescription.LeftShoulderTposeAdjustment ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tpose", "capabilities": [ "Animation" ] } ``` Coordinate frame adjustment applied to the left shoulder joint to align it with the T-pose reference orientation. ### Property: HumanoidRigDescription.LeftToeBase ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Mapping", "capabilities": [ "Animation" ] } ``` The [Motor6D](/docs/reference/engine/classes/Motor6D.md), [AnimationConstraint](/docs/reference/engine/classes/AnimationConstraint.md), or [Bone](/docs/reference/engine/classes/Bone.md) joint instance for the left toe base joint. This is the terminal joint of the left leg chain. ### Property: HumanoidRigDescription.LeftToeBaseRangeMax ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` Maximum rotation range in degrees for the left toe base joint along each local axis (X, Y, Z). ### Property: HumanoidRigDescription.LeftToeBaseRangeMin ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` Minimum rotation range in degrees for the left toe base joint along each local axis (X, Y, Z). ### Property: HumanoidRigDescription.LeftToeBaseSize ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Size", "capabilities": [ "Animation" ] } ``` Diameter of the left toe base joint's volume sphere, used for rig visualization and collision. ### Property: HumanoidRigDescription.LeftToeBaseTposeAdjustment ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tpose", "capabilities": [ "Animation" ] } ``` Coordinate frame adjustment applied to the left toe base joint to align it with the T-pose reference orientation. ### Property: HumanoidRigDescription.LeftWrist ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Mapping", "capabilities": [ "Animation" ] } ``` The [Motor6D](/docs/reference/engine/classes/Motor6D.md), [AnimationConstraint](/docs/reference/engine/classes/AnimationConstraint.md), or [Bone](/docs/reference/engine/classes/Bone.md) joint instance for the left wrist joint. This is the terminal joint of the left arm chain. ### Property: HumanoidRigDescription.LeftWristRangeMax ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` Maximum rotation range in degrees for the left wrist joint along each local axis (X, Y, Z). ### Property: HumanoidRigDescription.LeftWristRangeMin ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` Minimum rotation range in degrees for the left wrist joint along each local axis (X, Y, Z). ### Property: HumanoidRigDescription.LeftWristSize ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Size", "capabilities": [ "Animation" ] } ``` Diameter of the left wrist joint's volume sphere, used for rig visualization and collision. ### Property: HumanoidRigDescription.LeftWristTposeAdjustment ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tpose", "capabilities": [ "Animation" ] } ``` Coordinate frame adjustment applied to the left wrist joint to align it with the T-pose reference orientation. ### Property: HumanoidRigDescription.Neck ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Mapping", "capabilities": [ "Animation" ] } ``` The [Motor6D](/docs/reference/engine/classes/Motor6D.md), [AnimationConstraint](/docs/reference/engine/classes/AnimationConstraint.md), or [Bone](/docs/reference/engine/classes/Bone.md) joint instance for the neck joint. ### Property: HumanoidRigDescription.NeckRangeMax ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` Maximum rotation range in degrees for the neck joint along each local axis (X, Y, Z). ### Property: HumanoidRigDescription.NeckRangeMin ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` Minimum rotation range in degrees for the neck joint along each local axis (X, Y, Z). ### Property: HumanoidRigDescription.NeckSize ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Size", "capabilities": [ "Animation" ] } ``` Diameter of the neck joint's volume sphere, used for rig visualization and collision. ### Property: HumanoidRigDescription.NeckTposeAdjustment ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tpose", "capabilities": [ "Animation" ] } ``` Coordinate frame adjustment applied to the neck joint to align it with the T-pose reference orientation. ### Property: HumanoidRigDescription.OriginOffset ```json { "type": "CFrame", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Animation" ] } ``` Coordinate frame offset of the rig origin relative to the character model's primary part. Adjusts where the rig hierarchy is anchored within the character. ### Property: HumanoidRigDescription.RightAnkle ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Mapping", "capabilities": [ "Animation" ] } ``` The [Motor6D](/docs/reference/engine/classes/Motor6D.md), [AnimationConstraint](/docs/reference/engine/classes/AnimationConstraint.md), or [Bone](/docs/reference/engine/classes/Bone.md) joint instance for the right ankle joint. ### Property: HumanoidRigDescription.RightAnkleRangeMax ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` Maximum rotation range in degrees for the right ankle joint along each local axis (X, Y, Z). ### Property: HumanoidRigDescription.RightAnkleRangeMin ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` Minimum rotation range in degrees for the right ankle joint along each local axis (X, Y, Z). ### Property: HumanoidRigDescription.RightAnkleSize ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Size", "capabilities": [ "Animation" ] } ``` Diameter of the right ankle joint's volume sphere, used for rig visualization and collision. ### Property: HumanoidRigDescription.RightAnkleTposeAdjustment ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tpose", "capabilities": [ "Animation" ] } ``` Coordinate frame adjustment applied to the right ankle joint to align it with the T-pose reference orientation. ### Property: HumanoidRigDescription.RightClavicle ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Mapping", "capabilities": [ "Animation" ] } ``` The [Motor6D](/docs/reference/engine/classes/Motor6D.md), [AnimationConstraint](/docs/reference/engine/classes/AnimationConstraint.md), or [Bone](/docs/reference/engine/classes/Bone.md) joint instance for the right clavicle (collarbone) joint. ### Property: HumanoidRigDescription.RightClavicleRangeMax ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` Maximum rotation range in degrees for the right clavicle joint along each local axis (X, Y, Z). ### Property: HumanoidRigDescription.RightClavicleRangeMin ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` Minimum rotation range in degrees for the right clavicle joint along each local axis (X, Y, Z). ### Property: HumanoidRigDescription.RightClavicleSize ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Size", "capabilities": [ "Animation" ] } ``` Diameter of the right clavicle joint's volume sphere, used for rig visualization and collision. ### Property: HumanoidRigDescription.RightClavicleTposeAdjustment ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tpose", "capabilities": [ "Animation" ] } ``` Coordinate frame adjustment applied to the right clavicle joint to align it with the T-pose reference orientation. ### Property: HumanoidRigDescription.RightElbow ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Mapping", "capabilities": [ "Animation" ] } ``` The [Motor6D](/docs/reference/engine/classes/Motor6D.md), [AnimationConstraint](/docs/reference/engine/classes/AnimationConstraint.md), or [Bone](/docs/reference/engine/classes/Bone.md) joint instance for the right elbow joint. ### Property: HumanoidRigDescription.RightElbowRangeMax ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` Maximum rotation range in degrees for the right elbow joint along each local axis (X, Y, Z). ### Property: HumanoidRigDescription.RightElbowRangeMin ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` Minimum rotation range in degrees for the right elbow joint along each local axis (X, Y, Z). ### Property: HumanoidRigDescription.RightElbowSize ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Size", "capabilities": [ "Animation" ] } ``` Diameter of the right elbow joint's volume sphere, used for rig visualization and collision. ### Property: HumanoidRigDescription.RightElbowTposeAdjustment ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tpose", "capabilities": [ "Animation" ] } ``` Coordinate frame adjustment applied to the right elbow joint to align it with the T-pose reference orientation. ### Property: HumanoidRigDescription.RightHip ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Mapping", "capabilities": [ "Animation" ] } ``` The [Motor6D](/docs/reference/engine/classes/Motor6D.md), [AnimationConstraint](/docs/reference/engine/classes/AnimationConstraint.md), or [Bone](/docs/reference/engine/classes/Bone.md) joint instance for the right hip joint. This is the root of the right leg chain. ### Property: HumanoidRigDescription.RightHipRangeMax ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` Maximum rotation range in degrees for the right hip joint along each local axis (X, Y, Z). ### Property: HumanoidRigDescription.RightHipRangeMin ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` Minimum rotation range in degrees for the right hip joint along each local axis (X, Y, Z). ### Property: HumanoidRigDescription.RightHipSize ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Size", "capabilities": [ "Animation" ] } ``` Diameter of the right hip joint's volume sphere, used for rig visualization and collision. ### Property: HumanoidRigDescription.RightHipTposeAdjustment ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tpose", "capabilities": [ "Animation" ] } ``` Coordinate frame adjustment applied to the right hip joint to align it with the T-pose reference orientation. ### Property: HumanoidRigDescription.RightKnee ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Mapping", "capabilities": [ "Animation" ] } ``` The [Motor6D](/docs/reference/engine/classes/Motor6D.md), [AnimationConstraint](/docs/reference/engine/classes/AnimationConstraint.md), or [Bone](/docs/reference/engine/classes/Bone.md) joint instance for the right knee joint. ### Property: HumanoidRigDescription.RightKneeRangeMax ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` Maximum rotation range in degrees for the right knee joint along each local axis (X, Y, Z). ### Property: HumanoidRigDescription.RightKneeRangeMin ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` Minimum rotation range in degrees for the right knee joint along each local axis (X, Y, Z). ### Property: HumanoidRigDescription.RightKneeSize ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Size", "capabilities": [ "Animation" ] } ``` Diameter of the right knee joint's volume sphere, used for rig visualization and collision. ### Property: HumanoidRigDescription.RightKneeTposeAdjustment ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tpose", "capabilities": [ "Animation" ] } ``` Coordinate frame adjustment applied to the right knee joint to align it with the T-pose reference orientation. ### Property: HumanoidRigDescription.RightShoulder ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Mapping", "capabilities": [ "Animation" ] } ``` The [Motor6D](/docs/reference/engine/classes/Motor6D.md), [AnimationConstraint](/docs/reference/engine/classes/AnimationConstraint.md), or [Bone](/docs/reference/engine/classes/Bone.md) joint instance for the right shoulder joint. ### Property: HumanoidRigDescription.RightShoulderRangeMax ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` Maximum rotation range in degrees for the right shoulder joint along each local axis (X, Y, Z). ### Property: HumanoidRigDescription.RightShoulderRangeMin ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` Minimum rotation range in degrees for the right shoulder joint along each local axis (X, Y, Z). ### Property: HumanoidRigDescription.RightShoulderSize ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Size", "capabilities": [ "Animation" ] } ``` Diameter of the right shoulder joint's volume sphere, used for rig visualization and collision. ### Property: HumanoidRigDescription.RightShoulderTposeAdjustment ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tpose", "capabilities": [ "Animation" ] } ``` Coordinate frame adjustment applied to the right shoulder joint to align it with the T-pose reference orientation. ### Property: HumanoidRigDescription.RightToeBase ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Mapping", "capabilities": [ "Animation" ] } ``` The [Motor6D](/docs/reference/engine/classes/Motor6D.md), [AnimationConstraint](/docs/reference/engine/classes/AnimationConstraint.md), or [Bone](/docs/reference/engine/classes/Bone.md) joint instance for the right toe base joint. This is the terminal joint of the right leg chain. ### Property: HumanoidRigDescription.RightToeBaseRangeMax ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` Maximum rotation range in degrees for the right toe base joint along each local axis (X, Y, Z). ### Property: HumanoidRigDescription.RightToeBaseRangeMin ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` Minimum rotation range in degrees for the right toe base joint along each local axis (X, Y, Z). ### Property: HumanoidRigDescription.RightToeBaseSize ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Size", "capabilities": [ "Animation" ] } ``` Diameter of the right toe base joint's volume sphere, used for rig visualization and collision. ### Property: HumanoidRigDescription.RightToeBaseTposeAdjustment ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tpose", "capabilities": [ "Animation" ] } ``` Coordinate frame adjustment applied to the right toe base joint to align it with the T-pose reference orientation. ### Property: HumanoidRigDescription.RightWrist ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Mapping", "capabilities": [ "Animation" ] } ``` The [Motor6D](/docs/reference/engine/classes/Motor6D.md), [AnimationConstraint](/docs/reference/engine/classes/AnimationConstraint.md), or [Bone](/docs/reference/engine/classes/Bone.md) joint instance for the right wrist joint. This is the terminal joint of the right arm chain. ### Property: HumanoidRigDescription.RightWristRangeMax ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` Maximum rotation range in degrees for the right wrist joint along each local axis (X, Y, Z). ### Property: HumanoidRigDescription.RightWristRangeMin ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` Minimum rotation range in degrees for the right wrist joint along each local axis (X, Y, Z). ### Property: HumanoidRigDescription.RightWristSize ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Size", "capabilities": [ "Animation" ] } ``` Diameter of the right wrist joint's volume sphere, used for rig visualization and collision. ### Property: HumanoidRigDescription.RightWristTposeAdjustment ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tpose", "capabilities": [ "Animation" ] } ``` Coordinate frame adjustment applied to the right wrist joint to align it with the T-pose reference orientation. ### Property: HumanoidRigDescription.Root ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Mapping", "capabilities": [ "Animation" ] } ``` The [Motor6D](/docs/reference/engine/classes/Motor6D.md), [AnimationConstraint](/docs/reference/engine/classes/AnimationConstraint.md), or [Bone](/docs/reference/engine/classes/Bone.md) joint instance for the root joint, which is the base of the entire rig hierarchy. The waist, left hip, and right hip chains all originate from this joint. ### Property: HumanoidRigDescription.RootRangeMax ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` Maximum rotation range in degrees for the root joint along each local axis (X, Y, Z). ### Property: HumanoidRigDescription.RootRangeMin ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` Minimum rotation range in degrees for the root joint along each local axis (X, Y, Z). ### Property: HumanoidRigDescription.RootSize ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Size", "capabilities": [ "Animation" ] } ``` Diameter of the root joint's volume sphere, used for rig visualization and collision. ### Property: HumanoidRigDescription.RootTposeAdjustment ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tpose", "capabilities": [ "Animation" ] } ``` Coordinate frame adjustment applied to the root joint to align it with the T-pose reference orientation. ### Property: HumanoidRigDescription.Spine ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Mapping", "capabilities": [ "Animation" ] } ``` The [Motor6D](/docs/reference/engine/classes/Motor6D.md), [AnimationConstraint](/docs/reference/engine/classes/AnimationConstraint.md), or [Bone](/docs/reference/engine/classes/Bone.md) joint instance for the spine joint. ### Property: HumanoidRigDescription.SpineRangeMax ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` Maximum rotation range in degrees for the spine joint along each local axis (X, Y, Z). ### Property: HumanoidRigDescription.SpineRangeMin ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` Minimum rotation range in degrees for the spine joint along each local axis (X, Y, Z). ### Property: HumanoidRigDescription.SpineSize ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Size", "capabilities": [ "Animation" ] } ``` Diameter of the spine joint's volume sphere, used for rig visualization and collision. ### Property: HumanoidRigDescription.SpineTposeAdjustment ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tpose", "capabilities": [ "Animation" ] } ``` Coordinate frame adjustment applied to the spine joint to align it with the T-pose reference orientation. ### Property: HumanoidRigDescription.Waist ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Mapping", "capabilities": [ "Animation" ] } ``` The [Motor6D](/docs/reference/engine/classes/Motor6D.md), [AnimationConstraint](/docs/reference/engine/classes/AnimationConstraint.md), or [Bone](/docs/reference/engine/classes/Bone.md) joint instance for the waist joint. ### Property: HumanoidRigDescription.WaistRangeMax ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` Maximum rotation range in degrees for the waist joint along each local axis (X, Y, Z). ### Property: HumanoidRigDescription.WaistRangeMin ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Range", "capabilities": [ "Animation" ] } ``` Minimum rotation range in degrees for the waist joint along each local axis (X, Y, Z). ### Property: HumanoidRigDescription.WaistSize ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Size", "capabilities": [ "Animation" ] } ``` Diameter of the waist joint's volume sphere, used for rig visualization and collision. ### Property: HumanoidRigDescription.WaistTposeAdjustment ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tpose", "capabilities": [ "Animation" ] } ``` Coordinate frame adjustment applied to the waist joint to align it with the T-pose reference orientation. ## Methods ### Method: HumanoidRigDescription:GetJointFromName **Signature:** `HumanoidRigDescription:GetJointFromName(name: string): Instance` Returns the joint instance associated with the given joint name. Accepts both standard joint names (e.g., `"LeftShoulder"`) and R15 body part names (e.g., `"LeftLowerLeg"`). Name matching is case-insensitive. Returns `nil` if no joint with the given name is mapped. *Security: None · Thread Safety: Safe · Capabilities: Animation* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name` | `string` | | string | **Returns:** `Instance` — Instance ### Method: HumanoidRigDescription:GetJointNames **Signature:** `HumanoidRigDescription:GetJointNames(): Array` Returns an array of all 22 standard joint name strings for this rig description, in hierarchy order: Root, Waist, Spine, Chest, Neck, HeadBase, LeftClavicle, LeftShoulder, LeftElbow, LeftWrist, RightClavicle, RightShoulder, RightElbow, RightWrist, LeftHip, LeftKnee, LeftAnkle, LeftToeBase, RightHip, RightKnee, RightAnkle, RightToeBase. *Security: None · Thread Safety: Safe · Capabilities: Animation* **Returns:** `Array` — Array ### Method: HumanoidRigDescription:GetR15JointNames **Signature:** `HumanoidRigDescription:GetR15JointNames(): Array` Returns an array of the 15 joint name strings used by R15 rigs: Root, Waist, Neck, LeftShoulder, LeftElbow, LeftWrist, RightShoulder, RightElbow, RightWrist, LeftHip, LeftKnee, LeftAnkle, RightHip, RightKnee, RightAnkle. *Security: None · Thread Safety: Safe · Capabilities: Animation* **Returns:** `Array` — Array ### Method: HumanoidRigDescription:GetR6JointNames **Signature:** `HumanoidRigDescription:GetR6JointNames(): Array` Returns an array of the 6 joint name strings used by R6 rigs: Root, Neck, LeftShoulder, RightShoulder, LeftHip, RightHip. *Security: None · Thread Safety: Safe · Capabilities: Animation* **Returns:** `Array` — Array ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: IKControl last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Animation summary: "Specifies a control to generate a procedural animation pose using Inverse Kinematics." --- # Class: IKControl > Specifies a control to generate a procedural animation pose using Inverse > Kinematics. ## Description **IKControl** instances generate procedural animation poses using Inverse Kinematics (IK). They allow you to make characters respond realistically to their environment. For example, you can make a character place its hand on a door handle exactly, and the character will do so independently of its position. [IKControls](/docs/reference/engine/classes/IKControl.md) provide the advantage of needing to create much fewer animations for your game while giving your experience a more realistic and polished feel. [IKControls](/docs/reference/engine/classes/IKControl.md) must be a child of a [Humanoid](/docs/reference/engine/classes/Humanoid.md) or [AnimationController](/docs/reference/engine/classes/AnimationController.md) with an [Animator](/docs/reference/engine/classes/Animator.md) and have all of their required properties set properly, otherwise they don't have any effect. The required properties are [Type](/docs/reference/engine/classes/IKControl.md), [EndEffector](/docs/reference/engine/classes/IKControl.md), [Target](/docs/reference/engine/classes/IKControl.md), [ChainRoot](/docs/reference/engine/classes/IKControl.md). As soon as those are set, the [IKControl](/docs/reference/engine/classes/IKControl.md) modifies the pose of your character as you specify. The following code sample demonstrates how to set up your first [IKControl](/docs/reference/engine/classes/IKControl.md) and get started with creating more realistic animations for your game. You can use [IKControls](/docs/reference/engine/classes/IKControl.md) to make a character: - Rotate its head and torso to look at a point of interest in the world. - Modify its feet positions to respond to dynamic terrain. Adjust its legs and feet to place them accordingly on terrain with rocks and slopes. - Hold a gun and place its hands appropriately on the grip without needing to create animations for each gun in the game. - Aim at a point in the world, so that the tip of the gun point exactly at what you want to shoot. Especially useful in third person shooters. - Place its hands on the steering wheel of a car and follow it when it rotates. - Much more! [IKControl](/docs/reference/engine/classes/IKControl.md) will override the animation for all the parts between the [ChainRoot](/docs/reference/engine/classes/IKControl.md) and the [EndEffector](/docs/reference/engine/classes/IKControl.md). You can enable/disable it using [Enabled](/docs/reference/engine/classes/IKControl.md) or change how much they have an effect over the underlying animation using the [Weight](/docs/reference/engine/classes/IKControl.md). Be careful: if you do not set up your [IKControls](/docs/reference/engine/classes/IKControl.md) correctly, you might generate bad and unrealistic poses! ## Code Samples **IKControl setup** This sample shows the basic setup for an [IKControl](/docs/reference/engine/classes/IKControl.md) that moves a character's left arm to reach for a point in the world. ```lua local character = script.Parent.Character local humanoid = character.Humanoid local root = character.HumanoidRootPart -- Create a new attachment to use as the IKControl.Target local target = Instance.new("Attachment") target.CFrame = CFrame.new(-1, 0, -1) target.Parent = root local ikControl = Instance.new("IKControl") ikControl.Type = Enum.IKControlType.Position ikControl.EndEffector = character.LeftHand ikControl.ChainRoot = character.LeftUpperArm ikControl.Target = target ikControl.Parent = humanoid ``` ## Properties ### Property: IKControl.ChainRoot ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Animation" ], "simulationAccess": true } ``` By specifying a [ChainRoot](/docs/reference/engine/classes/IKControl.md) and an [EndEffector](/docs/reference/engine/classes/IKControl.md), you instruct the [IKControl](/docs/reference/engine/classes/IKControl.md) that it's allowed to move and rotate all parts between the two to move the [EndEffector](/docs/reference/engine/classes/IKControl.md) to the [Target](/docs/reference/engine/classes/IKControl.md). For example, if you specify the LeftHand as [EndEffector](/docs/reference/engine/classes/IKControl.md) and LeftUpperArm as the [ChainRoot](/docs/reference/engine/classes/IKControl.md), the control moves 3 parts: the LeftHand, the LeftLowerArm, and the LeftUpperArm. Avoid setting [ChainRoot](/docs/reference/engine/classes/IKControl.md) as the actual root of the character because that produces unrealistic results. ### Property: IKControl.Enabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Animation" ], "simulationAccess": true } ``` This property allows you to toggle the IK control on and off. It's on by default. When [Enabled](/docs/reference/engine/classes/IKControl.md) is false, the IK control is off and isn't resolved by the underlying solver. ### Property: IKControl.EndEffector ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Animation" ], "simulationAccess": true } ``` The [EndEffector](/docs/reference/engine/classes/IKControl.md) describes the last part in the chain of your character that you want to affect. For example, it could be the hand when you want to move the whole arm to reach a point. It can be a [BasePart](/docs/reference/engine/classes/BasePart.md) on a character, that has a [Motor6D](/docs/reference/engine/classes/Motor6D.md) as its child, a [Motor6D](/docs/reference/engine/classes/Motor6D.md) directly, a [Bone](/docs/reference/engine/classes/Bone.md), or a [Attachment](/docs/reference/engine/classes/Attachment.md). The pivot of the selected [EndEffector](/docs/reference/engine/classes/IKControl.md) moves to the [Target](/docs/reference/engine/classes/IKControl.md), so you can use [Attachments](/docs/reference/engine/classes/Attachment.md) to modify which point of a [BasePart](/docs/reference/engine/classes/BasePart.md) should reach the [Target](/docs/reference/engine/classes/IKControl.md). ### Property: IKControl.EndEffectorOffset ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Animation" ] } ``` The end-effector offset is an additional [CFrame](/docs/reference/engine/datatypes/CFrame.md) applied on top of the [Target](/docs/reference/engine/classes/IKControl.md) [CFrame](/docs/reference/engine/datatypes/CFrame.md) that produces the final [CFrame](/docs/reference/engine/datatypes/CFrame.md) used to place the [EndEffector](/docs/reference/engine/classes/IKControl.md). By default, it's the identity CFrame, so if you don't set it, it has no effect and the [EndEffector](/docs/reference/engine/classes/IKControl.md) uses the [Target](/docs/reference/engine/classes/IKControl.md) [CFrame](/docs/reference/engine/datatypes/CFrame.md) directly, which is specified in the local space of the [EndEffector](/docs/reference/engine/classes/IKControl.md). Alternatively, you can use Attachments by setting an Attachment as [EndEffector](/docs/reference/engine/classes/IKControl.md), which moves it to the [Target](/docs/reference/engine/classes/IKControl.md) instead of the parts it's attached to, effectively obtaining the same result. You can also use [EndEffectorOffset](/docs/reference/engine/classes/IKControl.md) to modify which axis of the [EndEffector](/docs/reference/engine/classes/IKControl.md) should point at the [Target](/docs/reference/engine/classes/IKControl.md) when using `LookAt` as [Type](/docs/reference/engine/classes/IKControl.md). ### Property: IKControl.Offset ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Animation" ] } ``` The offset is an additional [CFrame](/docs/reference/engine/datatypes/CFrame.md) applied on top of the [Target](/docs/reference/engine/classes/IKControl.md) [CFrame](/docs/reference/engine/datatypes/CFrame.md) that produces the final [CFrame](/docs/reference/engine/datatypes/CFrame.md) used to place the [EndEffector](/docs/reference/engine/classes/IKControl.md). It's identity by default, so if you don't set it, it has no effect and the [EndEffector](/docs/reference/engine/classes/IKControl.md) will use the [Target](/docs/reference/engine/classes/IKControl.md) [CFrame](/docs/reference/engine/datatypes/CFrame.md) directly. You can animate it to create procedural animations such as typing on a keyboard. It's useful when the [Target](/docs/reference/engine/classes/IKControl.md) and [EndEffector](/docs/reference/engine/classes/IKControl.md) aren't aligned and you need to fix it with an additional rotation or translation. ### Property: IKControl.Pole ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Animation" ] } ``` The [Pole](/docs/reference/engine/classes/IKControl.md) is an optional [Instance](/docs/reference/engine/classes/Instance.md) that gives you control over how intermediate parts in your character should bend. It can be anything that has a position in the world, such as [BasePart](/docs/reference/engine/classes/BasePart.md), [Attachment](/docs/reference/engine/classes/Attachment.md), [Bone](/docs/reference/engine/classes/Bone.md), [Motor6D](/docs/reference/engine/classes/Motor6D.md). It is by default `nil`. When you specify it, the underlying solver will make the parts bend towards it. When it is `nil`, the solver will try to make elbows and knees bend appropriately based on the limb of the character. The limb will be "Arm" when you select as [EndEffector](/docs/reference/engine/classes/IKControl.md) either the _LeftHand_ or _RightHand_ and as [ChainRoot](/docs/reference/engine/classes/IKControl.md) the corresponding _LeftUpperArm_ or _RightUpperArm_, and it will be "Leg" when you select as [EndEffector](/docs/reference/engine/classes/IKControl.md) either the _LeftFoot_ or _RightFoot_ and as [ChainRoot](/docs/reference/engine/classes/IKControl.md) the corresponding _LeftUpperLeg_ or _RightUpperLeg_. In all other cases, if you don't specify a pole, the chain might not bend as you expect. ### Property: IKControl.Priority ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Animation" ] } ``` When multiple controls are active on a character, the order in which they are solved by the underlying system affects the final generated pose. By changing this value, you specify the ordering in which controls are satisfied. Higher values have higher priority, and higher-priority controls are resolved later because their result might override the previous result of other controls. If you have multiple IK controls on a character and one is more important than the other, specify a lower priority for it. It is 0 by default, meaning all controls have the same priority. ### Property: IKControl.SmoothTime ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Animation" ] } ``` This value specifies the average number of seconds that it takes for the [EndEffector](/docs/reference/engine/classes/IKControl.md) to reach the [Target](/docs/reference/engine/classes/IKControl.md). The behavior is that of a critically-damped spring, where the rate of change is proportional to the distance to the target and no oscillations are present when approaching the target. Smaller values create a quicker convergence, and larger values create a slower convergence. A value of 0 disables smoothing. The default value is 0.05 to provide a very slight smoothing that makes the motion feel realistic. ### Property: IKControl.Target ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Animation" ], "simulationAccess": true } ``` The [Target](/docs/reference/engine/classes/IKControl.md) represents a point ([CFrame](/docs/reference/engine/datatypes/CFrame.md)) in the world that you want your [EndEffector](/docs/reference/engine/classes/IKControl.md) to reach. The exact behavior of reaching can be set via the [Type](/docs/reference/engine/classes/IKControl.md) property, and an additional [Offset](/docs/reference/engine/classes/IKControl.md) can be applied on top of it to modify it. If you set a [Target](/docs/reference/engine/classes/IKControl.md) that will be moved either by physics or a script, at each frame the [IKControl](/docs/reference/engine/classes/IKControl.md) will try to satisfy it, automatically updating the point to reach. ### Property: IKControl.Type ```json { "type": "IKControlType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Animation" ], "simulationAccess": true } ``` By changing the [Type](/docs/reference/engine/classes/IKControl.md), you can change the behavior of the control. These are the available options: - Transform: it's a full 6-DoF constraint. Aligns the [EndEffector](/docs/reference/engine/classes/IKControl.md) [CFrame](/docs/reference/engine/datatypes/CFrame.md) to that of the [Target](/docs/reference/engine/classes/IKControl.md). - Position: aligns the [EndEffector](/docs/reference/engine/classes/IKControl.md) position to that of the [Target](/docs/reference/engine/classes/IKControl.md). - Rotation: aligns the [EndEffector](/docs/reference/engine/classes/IKControl.md) rotation to that of the [Target](/docs/reference/engine/classes/IKControl.md). - LookAt: moves and orients the whole chain to make an axis (by default the forward axis) on the [EndEffector](/docs/reference/engine/classes/IKControl.md) point at a position in the world specified by [Target](/docs/reference/engine/classes/IKControl.md). ### Property: IKControl.Weight ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Animation" ], "simulationAccess": true } ``` You can control how much a given control affects the character pose by using this property. Values should be in the [0, 1] range. 0 means no effect, and 1 means full effect of the IK control. Values outside this range are truncated. Smoothly varying this value allows you to blend in or out a specific control to avoid jarring motion. It is 1 by default. The weight determines the interpolation factor between the End-Effector and the IK target. Setting the weight to 0 doesn't disable the IK Control because other factors, including the SmoothTime smoothing factor and Pole, can still change the pose. To truly disable the IK Control, turn the [Enabled](/docs/reference/engine/classes/IKControl.md) property to false. ## Methods ### Method: IKControl:GetChainCount **Signature:** `IKControl:GetChainCount(): int` *Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Returns:** `int` ### Method: IKControl:GetChainLength **Signature:** `IKControl:GetChainLength(): float` *Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Returns:** `float` ### Method: IKControl:GetNodeLocalCFrame **Signature:** `IKControl:GetNodeLocalCFrame(index: int): CFrame` *Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `index` | `int` | | | **Returns:** `CFrame` ### Method: IKControl:GetNodeWorldCFrame **Signature:** `IKControl:GetNodeWorldCFrame(index: int): CFrame` *Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `index` | `int` | | | **Returns:** `CFrame` ### Method: IKControl:GetRawFinalTarget **Signature:** `IKControl:GetRawFinalTarget(): CFrame` *Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Returns:** `CFrame` ### Method: IKControl:GetSmoothedFinalTarget **Signature:** `IKControl:GetSmoothedFinalTarget(): CFrame` *Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Returns:** `CFrame` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ILegacyStudioBridge last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service --- # Class: ILegacyStudioBridge ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ImageButton last_updated: 2026-06-29T19:34:07Z inherits: - GuiButton - GuiObject - GuiBase2d - GuiBase - Instance - Object type: class memory_category: Gui summary: "A 2D user interface element that displays an interactive image." --- # Class: ImageButton > A 2D user interface element that displays an interactive image. ## Description An [ImageButton](/docs/reference/engine/classes/ImageButton.md) behaves similarly to an [ImageLabel](/docs/reference/engine/classes/ImageLabel.md) in regards to rendering, with the additional behaviors of a [GuiButton](/docs/reference/engine/classes/GuiButton.md). ## Properties ### Property: ImageButton.HoverImage ```json { "type": "ContentId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Image", "capabilities": [ "UI" ] } ``` A texture ID that will be used when the [ImageButton](/docs/reference/engine/classes/ImageButton.md) is being hovered. ### Property: ImageButton.HoverImageContent ```json { "type": "Content", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Image", "capabilities": [ "UI" ] } ``` An image-type Content that can be set as an [ImageButton](/docs/reference/engine/classes/ImageButton.md) property. When the button is hovered, it will render this image. Only asset URIs are supported for this property. ### Property: ImageButton.Image ```json { "type": "ContentId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Image", "capabilities": [ "UI" ] } ``` This property is a content-type property that should hold the asset ID of a decal or image uploaded to Roblox. It functions identically to [Decal.Texture](/docs/reference/engine/classes/Decal.md) with regards to loading the image from Roblox. The rendered image will be colorized using [ImageColor3](/docs/reference/engine/classes/ImageButton.md). Note that it is possible to make the image render as tiled, scaled to fit, or 9-sliced by adjusting the [ScaleType](/docs/reference/engine/classes/ImageButton.md) property. **Image Hover Lock** This code sample causes an ImageLabel/ImageButton to display a red padlock. When the mouse is hovered, it changes to a green unlocked padlock. ```lua local imageLabel = script.Parent -- The images in this example are 64x64 imageLabel.Size = UDim2.new(0, 64, 0, 64) local function unlock() imageLabel.Image = "rbxassetid://284402785" -- Unlocked padlock (64x64) imageLabel.ImageColor3 = Color3.new(0, 0.5, 0) -- Dark green end local function lock() imageLabel.Image = "rbxassetid://284402752" -- Locked padlock (64x64) imageLabel.ImageColor3 = Color3.new(0.5, 0, 0) -- Dark red end -- Connect events; our default state is locked imageLabel.MouseEnter:Connect(unlock) imageLabel.MouseLeave:Connect(lock) lock() ``` ### Property: ImageButton.ImageColor3 ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Image", "capabilities": [ "UI" ] } ``` This property determines how an image is colorized. When set to white, no colorization occurs. This property is very useful for reusing image assets: If the source image is completely white with transparency, you can set the entire color of the image at once with this property. **Image Hover Lock** This code sample causes an ImageLabel/ImageButton to display a red padlock. When the mouse is hovered, it changes to a green unlocked padlock. ```lua local imageLabel = script.Parent -- The images in this example are 64x64 imageLabel.Size = UDim2.new(0, 64, 0, 64) local function unlock() imageLabel.Image = "rbxassetid://284402785" -- Unlocked padlock (64x64) imageLabel.ImageColor3 = Color3.new(0, 0.5, 0) -- Dark green end local function lock() imageLabel.Image = "rbxassetid://284402752" -- Locked padlock (64x64) imageLabel.ImageColor3 = Color3.new(0.5, 0, 0) -- Dark red end -- Connect events; our default state is locked imageLabel.MouseEnter:Connect(unlock) imageLabel.MouseLeave:Connect(lock) lock() ``` ### Property: ImageButton.ImageContent ```json { "type": "Content", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Image", "capabilities": [ "UI" ] } ``` This property should hold an [asset URI](/docs/en-us/projects/assets.md#asset-uris) or a reference to an [EditableImage](/docs/reference/engine/classes/EditableImage.md) object. The asset URI can reference a decal or image uploaded to Roblox. It functions identically to [Decal.Texture](/docs/reference/engine/classes/Decal.md) with regards to loading the image. The rendered image will be colorized using [ImageColor3](/docs/reference/engine/classes/ImageButton.md). It is possible to make the image render as tiled, scaled to fit, or 9‑sliced by adjusting the [ScaleType](/docs/reference/engine/classes/ImageButton.md) property. ### Property: ImageButton.ImageRectOffset ```json { "type": "Vector2", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Image", "capabilities": [ "UI" ] } ``` This property determines the pixel offset (from the top-left) of the image area to be displayed, allowing for the partial display of an image in conjunction with [ImageRectSize](/docs/reference/engine/classes/ImageButton.md). **Image Animation using Spritesheet** This code sample uses ImageRectOffset/ImageRectSize in order to play an animation of a man throwing a punch ```lua -- Place this in an ImageLabel/ImageButton with size 256x256 local imageLabel = script.Parent -- The following image is 1024x1024 with 12 frames (256x256) -- The frames play an animation of a man throwing a punch imageLabel.Image = "rbxassetid://848623155" imageLabel.ImageRectSize = Vector2.new(256, 256) -- The order of the frames to be displayed (left-to-right, then top-to-bottom) local frames = { Vector2.new(0, 0), Vector2.new(1, 0), Vector2.new(2, 0), Vector2.new(3, 0), Vector2.new(0, 1), Vector2.new(1, 1), Vector2.new(2, 1), Vector2.new(3, 1), Vector2.new(0, 2), Vector2.new(1, 2), Vector2.new(2, 2), Vector2.new(3, 2), } -- Animate the frames one at a time in a loop while true do for _, frame in ipairs(frames) do imageLabel.ImageRectOffset = frame * imageLabel.ImageRectSize task.wait(0.1) end end ``` ### Property: ImageButton.ImageRectSize ```json { "type": "Vector2", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Image", "capabilities": [ "UI" ] } ``` This property determines the pixel size of the image area to be displayed, allowing for the partial display of an image in conjunction with [ImageRectOffset](/docs/reference/engine/classes/ImageButton.md). If either dimension is set to `0`, the entire image is displayed instead. **Image Animation using Spritesheet** This code sample uses ImageRectOffset/ImageRectSize in order to play an animation of a man throwing a punch ```lua -- Place this in an ImageLabel/ImageButton with size 256x256 local imageLabel = script.Parent -- The following image is 1024x1024 with 12 frames (256x256) -- The frames play an animation of a man throwing a punch imageLabel.Image = "rbxassetid://848623155" imageLabel.ImageRectSize = Vector2.new(256, 256) -- The order of the frames to be displayed (left-to-right, then top-to-bottom) local frames = { Vector2.new(0, 0), Vector2.new(1, 0), Vector2.new(2, 0), Vector2.new(3, 0), Vector2.new(0, 1), Vector2.new(1, 1), Vector2.new(2, 1), Vector2.new(3, 1), Vector2.new(0, 2), Vector2.new(1, 2), Vector2.new(2, 2), Vector2.new(3, 2), } -- Animate the frames one at a time in a loop while true do for _, frame in ipairs(frames) do imageLabel.ImageRectOffset = frame * imageLabel.ImageRectSize task.wait(0.1) end end ``` ### Property: ImageButton.ImageTransparency ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Image", "capabilities": [ "UI" ] } ``` This property determines the alpha of the element's rendered image. A value of `0` is completely opaque and a value of `1` is completely transparent (invisible). This property behaves similarly to [GuiObject.BackgroundTransparency](/docs/reference/engine/classes/GuiObject.md) or [BasePart.Transparency](/docs/reference/engine/classes/BasePart.md). If you disable image rendering by setting [ImageTransparency](/docs/reference/engine/classes/ImageButton.md) to `1`, it will result in a plain rectangle that can be used as a button. However, it may be better to use a blank [TextButton](/docs/reference/engine/classes/TextButton.md) instead. ### Property: ImageButton.IsLoaded ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Image", "capabilities": [ "UI" ] } ``` This property indicates if the [Image](/docs/reference/engine/classes/ImageButton.md) property has finished loading from Roblox. Images declined by moderation will never load. **Image Load Time** This code sample measures how long an ImageLabel or ImageButton takes to load an image. If the image was already loaded, this will be 0. ```lua local imageLabel = script.Parent local startTime = workspace.DistributedGameTime -- Wait for the image to load while not imageLabel.IsLoaded do task.wait() end -- Measure and display how long it took to load local deltaTime = workspace.DistributedGameTime - startTime print(("Image loaded in %.3f seconds"):format(deltaTime)) ``` ### Property: ImageButton.PressedImage ```json { "type": "ContentId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Image", "capabilities": [ "UI" ] } ``` A texture ID that can be set as an [ImageButton](/docs/reference/engine/classes/ImageButton.md) property. When the button is pressed, it will render this image. ### Property: ImageButton.PressedImageContent ```json { "type": "Content", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Image", "capabilities": [ "UI" ] } ``` An image-type Content that can be set as an [ImageButton](/docs/reference/engine/classes/ImageButton.md) property. When the button is pressed, it will render this image. Only asset URIs are supported for this property. ### Property: ImageButton.ResampleMode ```json { "type": "ResamplerMode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Image", "capabilities": [ "UI" ] } ``` Determines how the image looks when it is scaled. By default, the image smooths out the texture when displayed either larger or smaller than its size in texture memory. In contrast, [Enum.ResamplerMode.Pixelated](/docs/reference/engine/enums/ResamplerMode.md) preserves the sharp edges of the image pixels. ### Property: ImageButton.ScaleType ```json { "type": "ScaleType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Image", "capabilities": [ "UI" ] } ``` This property determines in what way the [Image](/docs/reference/engine/classes/ImageButton.md) property is rendered when the UI element's absolute size differs from the source image's size. By default, this property is [Enum.ScaleType.Stretch](/docs/reference/engine/enums/ScaleType.md) which will simply stretch/compact the image dimensions so it fits the UI element's space exactly. Since transparent pixels are set to black when uploading to Roblox, transparent images should apply alpha blending to avoid a blackish outline around scaled images. For [ScaleType.Slice](/docs/reference/engine/enums/ScaleType.md), when scaling up, the corners will remain the source image size. The edges of the image will stretch to the width/height of the image. Finally, the center of the image will stretch to fill the center area of the image. To learn more about 9‑sliced images, see [UI 9‑Slice Design](/docs/en-us/ui/9-slice.md). For [ScaleType.Tile](/docs/reference/engine/enums/ScaleType.md), the size of each image tile is determined by the [TileSize](/docs/reference/engine/classes/ImageButton.md) property. ### Property: ImageButton.SliceCenter ```json { "type": "Rect", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Image", "capabilities": [ "UI" ] } ``` This property sets the slice boundaries of a 9-sliced image when [ScaleType](/docs/reference/engine/classes/ImageButton.md) is set to [Enum.ScaleType.Slice](/docs/reference/engine/enums/ScaleType.md). To learn more about 9‑sliced images, see [UI 9‑Slice Design](/docs/en-us/ui/9-slice.md). ### Property: ImageButton.SliceScale ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Image", "capabilities": [ "UI" ] } ``` Scales the 9-slice edges by the specified ratio. This means that the edges around the 9‑slice will grow as if you'd uploaded a new version of the texture upscaled. Defaults to `1.0`. As a multiplier for the borders of a 9-slice, it is useful for reusing one rounded corner image for multiple radii. See also [ScaleType](/docs/reference/engine/classes/ImageButton.md) which determines how an image will scale if displayed in a UI element whose size differs from the source image. ### Property: ImageButton.TileSize ```json { "type": "UDim2", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Image", "capabilities": [ "UI" ] } ``` Sets the tiling size of the [ImageButton](/docs/reference/engine/classes/ImageButton.md) starting at the upper-left corner of the image. The default [UDim2](/docs/reference/engine/datatypes/UDim2.md) values are `1, 0, 1, 0`; the scale components of the [UDim2](/docs/reference/engine/datatypes/UDim2.md) will scale the tile based on the size of the [ImageButton](/docs/reference/engine/classes/ImageButton.md) while the offset components are in raw pixels. For example, a scale of `0.5` means the tile will be half the size of the [ImageButton](/docs/reference/engine/classes/ImageButton.md) in the corresponding axis. This property is only active if the [ScaleType](/docs/reference/engine/classes/ImageButton.md) property is set to [ScaleType.Tile](/docs/reference/engine/enums/ScaleType.md). ## Inherited Members ### From [GuiButton](/docs/reference/engine/classes/GuiButton.md) - **Property `AutoButtonColor`** (`boolean`): Determines whether the button automatically changes color when the mouse - **Property `HoverHapticEffect`** (`HapticEffect`): A HapticEffect instance that will play when the GuiButton - **Property `Modal`** (`boolean`): If `true` while the GUI element is visible, the mouse will not be locked - **Property `PressHapticEffect`** (`HapticEffect`): A HapticEffect instance that will play when the GuiButton - **Property `Selected`** (`boolean`): A boolean property which indicates whether the object has been selected. - **Property `Style`** (`ButtonStyle`): Sets the style of the GuiButton based on a list of pre-determined - **Event `Activated`**: Fires when the button is activated. - **Event `MouseButton1Click`**: Fires when the user's mouse fully left clicks the GuiButton. - **Event `MouseButton1Down`**: Fires when the user presses their left mouse button down on the - **Event `MouseButton1Up`**: Fires when the user releases their left mouse button off of the - **Event `MouseButton2Click`**: Fires when the user's mouse fully right clicks the GuiButton. - **Event `MouseButton2Down`**: Fires when the user presses their right mouse button down on the - **Event `MouseButton2Up`**: Fires when the user releases their right mouse button off of the - **Event `SecondaryActivated`**: Fires when the button's "secondary" input is activated. ### From [GuiObject](/docs/reference/engine/classes/GuiObject.md) - **Property `Active`** (`boolean`): Determines whether this UI element sinks input. - **Property `AnchorPoint`** (`Vector2`): Determines the origin point of a GuiObject, relative to its - **Property `AutomaticSize`** (`AutomaticSize`): Determines whether resizing occurs based on child content. - **Property `BackgroundColor`** (`BrickColor`): Determines the color of the GuiObject background. *(deprecated, hidden)* - **Property `BackgroundColor3`** (`Color3`): Determines the GuiObject background color. - **Property `BackgroundTransparency`** (`float`): Determines the transparency of the GuiObject background and - **Property `BorderColor`** (`BrickColor`): Determines the color of the GuiObject border. *(deprecated, hidden)* - **Property `BorderColor3`** (`Color3`): Determines the color of the GuiObject border. - **Property `BorderMode`** (`BorderMode`): Determines in what manner the GuiObject border is laid out - **Property `BorderSizePixel`** (`int`): Determines the pixel width of the GuiObject border. - **Property `ClipsDescendants`** (`boolean`): Determines if descendant GuiObjects outside of the - **Property `Draggable`** (`boolean`): Determines whether a GuiObject (and its descendants) can be *(deprecated)* - **Property `GuiState`** (`GuiState`): Determines whether the player's mouse is being actively pressed on the - **Property `InputSink`** (`InputSink`): - **Property `Interactable`** (`boolean`): Determines whether the GuiButton can be interacted with or not, or - **Property `LayoutOrder`** (`int`): Controls the sort order of the GuiObject when used with a - **Property `NextSelectionDown`** (`GuiObject`): Sets the GuiObject which will be selected when the gamepad - **Property `NextSelectionLeft`** (`GuiObject`): Sets the GuiObject which will be selected when the gamepad - **Property `NextSelectionRight`** (`GuiObject`): Sets the GuiObject which will be selected when the gamepad - **Property `NextSelectionUp`** (`GuiObject`): Sets the GuiObject which will be selected when the gamepad - **Property `Position`** (`UDim2`): Determines the pixel and scalar position of the GuiObject. - **Property `Rotation`** (`float`): Determines the number of degrees by which the GuiObject is - **Property `Selectable`** (`boolean`): Determine whether the GuiObject can be selected by a gamepad. - **Property `SelectionImageObject`** (`GuiObject`): Overrides the default selection adornment used for gamepads. - **Property `SelectionOrder`** (`int`): The order of GuiObjects selected by the gamepad UI - **Property `Size`** (`UDim2`): Determines the pixel and scalar size of the GuiObject. - **Property `SizeConstraint`** (`SizeConstraint`): Sets the Size axes that the GuiObject will - **Property `Transparency`** (`float`): A mixed property of *(hidden)* - **Property `Visible`** (`boolean`): Determines whether the GuiObject and its descendants will be - **Property `ZIndex`** (`int`): Determines the order in which a GuiObject renders relative to - **Method `TweenPosition(endPosition: UDim2, easingDirection?: EasingDirection, easingStyle?: EasingStyle, time?: float, override?: boolean, callback?: Function): boolean`**: Smoothly moves a GUI to a new UDim2. - **Method `TweenSize(endSize: UDim2, easingDirection?: EasingDirection, easingStyle?: EasingStyle, time?: float, override?: boolean, callback?: Function): boolean`**: Smoothly resizes a GuiObject to a new UDim2. - **Method `TweenSizeAndPosition(endSize: UDim2, endPosition: UDim2, easingDirection?: EasingDirection, easingStyle?: EasingStyle, time?: float, override?: boolean, callback?: Function): boolean`**: Smoothly moves a GUI to a new size and position. - **Event `DragBegin`**: Fired when a player begins dragging the object. *(deprecated)* - **Event `DragStopped`**: Fired when a player stops dragging the object. *(deprecated)* - **Event `InputBegan`**: Fired when a user begins interacting via a Human-Computer Interface device - **Event `InputChanged`**: Fired when a user changes how they're interacting via a Human-Computer - **Event `InputEnded`**: Fired when a user stops interacting via a Human-Computer Interface device - **Event `MouseEnter`**: Fires when a user moves their mouse into a GUI element. - **Event `MouseLeave`**: Fires when a user moves their mouse out of a GUI element. - **Event `MouseMoved`**: Fires whenever a user moves their mouse while it is inside a GUI element. - **Event `MouseWheelBackward`**: Fires when a user scrolls their mouse wheel back when the mouse is over a - **Event `MouseWheelForward`**: Fires when a user scrolls their mouse wheel forward when the mouse is over - **Event `SelectionGained`**: Fired when the GuiObject is being focused on with the Gamepad selector. - **Event `SelectionLost`**: Fired when the Gamepad selector stops focusing on the GuiObject. - **Event `TouchLongPress`**: Fires when the player starts, continues and stops long-pressing the UI - **Event `TouchPan`**: Fires when the player moves their finger on the UI element. - **Event `TouchPinch`**: Fires when the player performs a pinch or pull gesture using two fingers - **Event `TouchRotate`**: Fires when the player performs a rotation gesture using two fingers on the - **Event `TouchSwipe`**: Fires when the player performs a swipe gesture on the UI element. - **Event `TouchTap`**: Fires when the player performs a tap gesture on the UI element. ### From [GuiBase2d](/docs/reference/engine/classes/GuiBase2d.md) - **Property `AbsolutePosition`** (`Vector2`): Describes the actual screen position of a GuiBase2d element, in - **Property `AbsoluteRotation`** (`float`): Describes the actual screen rotation of a GuiBase2d element, in - **Property `AbsoluteSize`** (`Vector2`): Describes the actual screen size of a GuiBase2d element, in - **Property `AutoLocalize`** (`boolean`): When set to `true`, localization will be applied to this GuiBase2d - **Property `Localize`** (`boolean`): Automatically set to true when a localization table's *(deprecated, hidden)* - **Property `RootLocalizationTable`** (`LocalizationTable`): A reference to a LocalizationTable to be used to apply automated - **Property `SelectionBehaviorDown`** (`SelectionBehavior`): Customizes gamepad selection behavior in the down direction. - **Property `SelectionBehaviorLeft`** (`SelectionBehavior`): Customizes gamepad selection behavior in the left direction. - **Property `SelectionBehaviorRight`** (`SelectionBehavior`): Customizes gamepad selection behavior in the right direction. - **Property `SelectionBehaviorUp`** (`SelectionBehavior`): Customizes gamepad selection behavior in the up direction. - **Property `SelectionGroup`** (`boolean`): Allows customization of gamepad selection movement. - **Event `SelectionChanged`**: Fires when the gamepad selection moves to, leaves, or changes within the ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ImageHandleAdornment last_updated: 2026-06-29T19:34:07Z inherits: - HandleAdornment - PVAdornment - GuiBase3d - GuiBase - Instance - Object type: class memory_category: Instances summary: "An image handle that can be adorned to a BasePart." --- # Class: ImageHandleAdornment > An image handle that can be adorned to a [BasePart](/docs/reference/engine/classes/BasePart.md). ## Description `ImageHandleAdornment` is an image handle that can be adorned to a [BasePart](/docs/reference/engine/classes/BasePart.md). If parented to a player's [PlayerGui](/docs/reference/engine/classes/PlayerGui.md) or the [CoreGui](/docs/reference/engine/classes/CoreGui.md), handles can listen to input events for purposes such as making dragger tools. ## Properties ### Property: ImageHandleAdornment.Image ```json { "type": "ContentId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic", "UI" ] } ``` The image to draw for the adornment. ### Property: ImageHandleAdornment.Size ```json { "type": "Vector2", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic", "UI" ] } ``` The size of the image in studs. ## Inherited Members ### From [HandleAdornment](/docs/reference/engine/classes/HandleAdornment.md) - **Property `AdornCullingMode`** (`AdornCullingMode`): Determines whether to automatically cull the adornment. - **Property `AlwaysOnTop`** (`boolean`): Forces this adornment to render on top of all 3D objects in the workspace. - **Property `CFrame`** (`CFrame`): The position and rotation of the object relative to its - **Property `SizeRelativeOffset`** (`Vector3`): The positional offset of the adornment based on the adornee's - **Property `ZIndex`** (`int`): Determines the draw order of this HandleAdornment when - **Event `MouseButton1Down`**: Fires when a player presses down on their left mouse button while hovering - **Event `MouseButton1Up`**: Fires when a player releases their left mouse button while hovering over - **Event `MouseEnter`**: Fires when a player moves their mouse over the adornment. - **Event `MouseLeave`**: Fires when a player moves their mouse out of the adornment. ### From [PVAdornment](/docs/reference/engine/classes/PVAdornment.md) - **Property `Adornee`** (`PVInstance`): The PVInstance which this PVAdornment is attached to. ### From [GuiBase3d](/docs/reference/engine/classes/GuiBase3d.md) - **Property `Color`** (`BrickColor`): Sets the color of a GUI object. *(deprecated, hidden)* - **Property `Color3`** (`Color3`): Sets the color of this GuiBase3d object. - **Property `Transparency`** (`float`): Sets the transparency of this GuiBase3d object. - **Property `Visible`** (`boolean`): Determines whether this GuiBase3d object and its descendants will ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ImageLabel last_updated: 2026-06-29T19:34:07Z inherits: - GuiLabel - GuiObject - GuiBase2d - GuiBase - Instance - Object type: class memory_category: Gui summary: "A 2D user interface element that displays a single non-interactive image." --- # Class: ImageLabel > A 2D user interface element that displays a single non-interactive image. ## Description An [ImageLabel](/docs/reference/engine/classes/ImageLabel.md) renders a rectangle, like a [Frame](/docs/reference/engine/classes/Frame.md) does, with an image asset. The display of the image can be manipulated through the [ImageColor3](/docs/reference/engine/classes/ImageLabel.md) and [ImageTransparency](/docs/reference/engine/classes/ImageLabel.md) properties. To display only the image and hide the rectangle, set [GuiObject.BackgroundTransparency](/docs/reference/engine/classes/GuiObject.md) to `1`. Advanced [ImageLabel](/docs/reference/engine/classes/ImageLabel.md) usage includes: - Tiled images can be created by setting [ScaleType](/docs/reference/engine/classes/ImageLabel.md) to [ScaleType.Tile](/docs/reference/engine/enums/ScaleType.md), then [TileSize](/docs/reference/engine/classes/ImageLabel.md) to the size of rendered tiles. - 9-slice images can be created by setting [ScaleType](/docs/reference/engine/classes/ImageLabel.md) to [ScaleType.Slice](/docs/reference/engine/enums/ScaleType.md), then [SliceCenter](/docs/reference/engine/classes/ImageLabel.md) to the center area of the 9‑slice image. - Sprite sheets can be implemented through the use of [ImageRectOffset](/docs/reference/engine/classes/ImageLabel.md) and [ImageRectSize](/docs/reference/engine/classes/ImageLabel.md). Packing multiple images into one and using this property can make your experience's image assets load much quicker, especially if you use many small icons in your GUIs. ## Properties ### Property: ImageLabel.Image ```json { "type": "ContentId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Image", "capabilities": [ "UI" ] } ``` This property is a content-type property that should hold the asset ID of a decal or image uploaded to Roblox. It functions identically to [Decal.Texture](/docs/reference/engine/classes/Decal.md) with regards to loading the image from Roblox. The rendered image can be modified using [ImageColor3](/docs/reference/engine/classes/ImageLabel.md) and [ImageTransparency](/docs/reference/engine/classes/ImageLabel.md). ### Property: ImageLabel.ImageColor3 ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Image", "capabilities": [ "UI" ] } ``` This property determines how an image is colorized. When set to white, no colorization occurs. This property is very useful for reusing image assets; if the source image is completely white with transparency, you can set the entire color of the image at once with this property. ### Property: ImageLabel.ImageContent ```json { "type": "Content", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Image", "capabilities": [ "UI" ] } ``` This property should hold an [asset URI](/docs/en-us/projects/assets.md#asset-uris) or a reference to an [EditableImage](/docs/reference/engine/classes/EditableImage.md) object. The asset URI can reference a decal or image uploaded to Roblox. It functions identically to [Decal.Texture](/docs/reference/engine/classes/Decal.md) with regards to loading the image. The rendered image will be colorized using [ImageButton.ImageColor3](/docs/reference/engine/classes/ImageButton.md). It is possible to make the image render as tiled, scaled to fit, or 9-sliced, by adjusting the [ImageButton.ScaleType](/docs/reference/engine/classes/ImageButton.md) property. ### Property: ImageLabel.ImageRectOffset ```json { "type": "Vector2", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Image", "capabilities": [ "UI" ] } ``` Allows the partial display of an image in conjunction with [ImageRectSize](/docs/reference/engine/classes/ImageLabel.md). This property determines the pixel offset (from the top-left) of the image area to be displayed. ### Property: ImageLabel.ImageRectSize ```json { "type": "Vector2", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Image", "capabilities": [ "UI" ] } ``` Allows the partial display of an image in conjunction with [ImageRectOffset](/docs/reference/engine/classes/ImageLabel.md). This property determines the pixel size of the image area to be displayed. If either dimension is set to `0`, the entire image is displayed instead. ### Property: ImageLabel.ImageTransparency ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Image", "capabilities": [ "UI" ] } ``` This property determines the alpha of a UI element's rendered image. A value of `0` is completely opaque and a value of `1` is completely transparent (invisible). **Oscillate ImageTransparency** This code sample oscillates the ImageTransparency of an ImageLabel/ImageButton from 0 to 1 using a sine wave. ```lua local RunService = game:GetService("RunService") local imageLabel = script.Parent local function onRenderStep() -- Oscillate ImageTransparency from 0 to 1 using a sine wave imageLabel.ImageTransparency = math.sin(workspace.DistributedGameTime * math.pi) * 0.5 + 0.5 end RunService.RenderStepped:Connect(onRenderStep) ``` ### Property: ImageLabel.IsLoaded ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Image", "capabilities": [ "UI" ] } ``` This property indicates if the [ImageLabel.Image](/docs/reference/engine/classes/ImageLabel.md) property has finished loading from Roblox. Images declined by moderation will never load. **Image Load Time** This code sample measures how long an ImageLabel or ImageButton takes to load an image. If the image was already loaded, this will be 0. ```lua local imageLabel = script.Parent local startTime = workspace.DistributedGameTime -- Wait for the image to load while not imageLabel.IsLoaded do task.wait() end -- Measure and display how long it took to load local deltaTime = workspace.DistributedGameTime - startTime print(("Image loaded in %.3f seconds"):format(deltaTime)) ``` ### Property: ImageLabel.ResampleMode ```json { "type": "ResamplerMode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Image", "capabilities": [ "UI" ] } ``` Determines how the image looks when it is scaled. By default, the image smooths out texturing when displayed on the screen larger or smaller than its size in texture memory. When set to [Enum.ResamplerMode.Pixelated](/docs/reference/engine/enums/ResamplerMode.md), the image preserves the sharp edges of pixels. ### Property: ImageLabel.ScaleType ```json { "type": "ScaleType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Image", "capabilities": [ "UI" ] } ``` This property determines in what way an [ImageLabel.Image](/docs/reference/engine/classes/ImageLabel.md) is rendered when the UI element's absolute size differs from the source image's size. By default, this property is [ScaleType.Stretch](/docs/reference/engine/enums/ScaleType.md) which will simply stretch/compact the image dimensions so it fits the UI element's space exactly. Since transparent pixels are set to black when uploading to Roblox, transparent images should apply alpha blending to avoid a blackish outline around scaled images. For [ScaleType.Slice](/docs/reference/engine/enums/ScaleType.md), the [SliceCenter](/docs/reference/engine/classes/ImageLabel.md) property will be revealed in the [Properties](/docs/en-us/studio/properties.md) window. This is for nine-slice UI: when scaling up, the corners will remain the source image size. The edges of the image will stretch to the width/height of the image. Finally, the center of the image will stretch to fill the center area of the image. Finally, for [ScaleType.Tile](/docs/reference/engine/enums/ScaleType.md), the [TileSize](/docs/reference/engine/classes/ImageLabel.md) property will be revealed in the [Properties](/docs/en-us/studio/properties.md) window. This is for tiled images, where the size of each image tile is determined by the [TileSize](/docs/reference/engine/classes/ImageLabel.md) property. ### Property: ImageLabel.SliceCenter ```json { "type": "Rect", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Image", "capabilities": [ "UI" ] } ``` This property sets the slice boundaries of a 9-sliced image when [ScaleType](/docs/reference/engine/classes/ImageLabel.md) is set to [Enum.ScaleType.Slice](/docs/reference/engine/enums/ScaleType.md). Please note that this property is only visible in the [Properties](/docs/en-us/studio/properties.md) window under this condition. To learn more about 9-slice images, see [UI 9 Slice Design](/docs/en-us/ui/9-slice.md). ### Property: ImageLabel.SliceScale ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Image", "capabilities": [ "UI" ] } ``` Scales the 9-slice edges by the specified ratio. This means that the edges around the 9-slice will grow as if you'd uploaded a new version of the texture upscaled. Defaults to `1.0`. See also [ScaleType](/docs/reference/engine/classes/ImageLabel.md), [SliceCenter](/docs/reference/engine/classes/ImageLabel.md), and [SliceScale](/docs/reference/engine/classes/ImageLabel.md). ### Property: ImageLabel.TileSize ```json { "type": "UDim2", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Image", "capabilities": [ "UI" ] } ``` This property sets the tiling size of the [ImageLabel](/docs/reference/engine/classes/ImageLabel.md) with a default of [UDim2.new(1, 0, 1, 0)](/docs/reference/engine/datatypes/UDim2.md). Tiling starts at the top-left corner of the image. This property is only active if the [ScaleType](/docs/reference/engine/classes/ImageLabel.md) for the [ImageLabel](/docs/reference/engine/classes/ImageLabel.md) is set to [ScaleType.Tile](/docs/reference/engine/enums/ScaleType.md). **Image ScaleType Demo** This code sample demonstrates the different ScaleType options - Stretch, Tile and Slice. It does this by resizing an ImageLabel/ImageButton in a circle. ```lua local imageLabel = script.Parent -- Set the source image to be a 64x64 padlock imageLabel.Image = "rbxassetid://284402752" imageLabel.BackgroundTransparency = 0 imageLabel.BackgroundColor3 = Color3.new(1, 1, 1) -- White imageLabel.ImageColor3 = Color3.new(0, 0, 0) -- Black local function resizeInACircle() for theta = 0, 2, 0.02 do imageLabel.Size = UDim2.new(0, 100 + math.cos(theta * 2 * math.pi) * 50, 0, 100 + math.sin(theta * 2 * math.pi) * 50) task.wait() end end while true do -- Stretch simply stretches the source image to fit -- the UI element's space imageLabel.ScaleType = Enum.ScaleType.Stretch resizeInACircle() -- Tile will render the source image multiple times -- enough to fill the UI element's space imageLabel.ScaleType = Enum.ScaleType.Tile imageLabel.TileSize = UDim2.new(0, 64, 0, 64) resizeInACircle() -- Slice will turn the image into a nine-slice UI. imageLabel.ScaleType = Enum.ScaleType.Slice imageLabel.SliceCenter = Rect.new(30, 30, 34, 34) resizeInACircle() end ``` ## Inherited Members ### From [GuiObject](/docs/reference/engine/classes/GuiObject.md) - **Property `Active`** (`boolean`): Determines whether this UI element sinks input. - **Property `AnchorPoint`** (`Vector2`): Determines the origin point of a GuiObject, relative to its - **Property `AutomaticSize`** (`AutomaticSize`): Determines whether resizing occurs based on child content. - **Property `BackgroundColor`** (`BrickColor`): Determines the color of the GuiObject background. *(deprecated, hidden)* - **Property `BackgroundColor3`** (`Color3`): Determines the GuiObject background color. - **Property `BackgroundTransparency`** (`float`): Determines the transparency of the GuiObject background and - **Property `BorderColor`** (`BrickColor`): Determines the color of the GuiObject border. *(deprecated, hidden)* - **Property `BorderColor3`** (`Color3`): Determines the color of the GuiObject border. - **Property `BorderMode`** (`BorderMode`): Determines in what manner the GuiObject border is laid out - **Property `BorderSizePixel`** (`int`): Determines the pixel width of the GuiObject border. - **Property `ClipsDescendants`** (`boolean`): Determines if descendant GuiObjects outside of the - **Property `Draggable`** (`boolean`): Determines whether a GuiObject (and its descendants) can be *(deprecated)* - **Property `GuiState`** (`GuiState`): Determines whether the player's mouse is being actively pressed on the - **Property `InputSink`** (`InputSink`): - **Property `Interactable`** (`boolean`): Determines whether the GuiButton can be interacted with or not, or - **Property `LayoutOrder`** (`int`): Controls the sort order of the GuiObject when used with a - **Property `NextSelectionDown`** (`GuiObject`): Sets the GuiObject which will be selected when the gamepad - **Property `NextSelectionLeft`** (`GuiObject`): Sets the GuiObject which will be selected when the gamepad - **Property `NextSelectionRight`** (`GuiObject`): Sets the GuiObject which will be selected when the gamepad - **Property `NextSelectionUp`** (`GuiObject`): Sets the GuiObject which will be selected when the gamepad - **Property `Position`** (`UDim2`): Determines the pixel and scalar position of the GuiObject. - **Property `Rotation`** (`float`): Determines the number of degrees by which the GuiObject is - **Property `Selectable`** (`boolean`): Determine whether the GuiObject can be selected by a gamepad. - **Property `SelectionImageObject`** (`GuiObject`): Overrides the default selection adornment used for gamepads. - **Property `SelectionOrder`** (`int`): The order of GuiObjects selected by the gamepad UI - **Property `Size`** (`UDim2`): Determines the pixel and scalar size of the GuiObject. - **Property `SizeConstraint`** (`SizeConstraint`): Sets the Size axes that the GuiObject will - **Property `Transparency`** (`float`): A mixed property of *(hidden)* - **Property `Visible`** (`boolean`): Determines whether the GuiObject and its descendants will be - **Property `ZIndex`** (`int`): Determines the order in which a GuiObject renders relative to - **Method `TweenPosition(endPosition: UDim2, easingDirection?: EasingDirection, easingStyle?: EasingStyle, time?: float, override?: boolean, callback?: Function): boolean`**: Smoothly moves a GUI to a new UDim2. - **Method `TweenSize(endSize: UDim2, easingDirection?: EasingDirection, easingStyle?: EasingStyle, time?: float, override?: boolean, callback?: Function): boolean`**: Smoothly resizes a GuiObject to a new UDim2. - **Method `TweenSizeAndPosition(endSize: UDim2, endPosition: UDim2, easingDirection?: EasingDirection, easingStyle?: EasingStyle, time?: float, override?: boolean, callback?: Function): boolean`**: Smoothly moves a GUI to a new size and position. - **Event `DragBegin`**: Fired when a player begins dragging the object. *(deprecated)* - **Event `DragStopped`**: Fired when a player stops dragging the object. *(deprecated)* - **Event `InputBegan`**: Fired when a user begins interacting via a Human-Computer Interface device - **Event `InputChanged`**: Fired when a user changes how they're interacting via a Human-Computer - **Event `InputEnded`**: Fired when a user stops interacting via a Human-Computer Interface device - **Event `MouseEnter`**: Fires when a user moves their mouse into a GUI element. - **Event `MouseLeave`**: Fires when a user moves their mouse out of a GUI element. - **Event `MouseMoved`**: Fires whenever a user moves their mouse while it is inside a GUI element. - **Event `MouseWheelBackward`**: Fires when a user scrolls their mouse wheel back when the mouse is over a - **Event `MouseWheelForward`**: Fires when a user scrolls their mouse wheel forward when the mouse is over - **Event `SelectionGained`**: Fired when the GuiObject is being focused on with the Gamepad selector. - **Event `SelectionLost`**: Fired when the Gamepad selector stops focusing on the GuiObject. - **Event `TouchLongPress`**: Fires when the player starts, continues and stops long-pressing the UI - **Event `TouchPan`**: Fires when the player moves their finger on the UI element. - **Event `TouchPinch`**: Fires when the player performs a pinch or pull gesture using two fingers - **Event `TouchRotate`**: Fires when the player performs a rotation gesture using two fingers on the - **Event `TouchSwipe`**: Fires when the player performs a swipe gesture on the UI element. - **Event `TouchTap`**: Fires when the player performs a tap gesture on the UI element. ### From [GuiBase2d](/docs/reference/engine/classes/GuiBase2d.md) - **Property `AbsolutePosition`** (`Vector2`): Describes the actual screen position of a GuiBase2d element, in - **Property `AbsoluteRotation`** (`float`): Describes the actual screen rotation of a GuiBase2d element, in - **Property `AbsoluteSize`** (`Vector2`): Describes the actual screen size of a GuiBase2d element, in - **Property `AutoLocalize`** (`boolean`): When set to `true`, localization will be applied to this GuiBase2d - **Property `Localize`** (`boolean`): Automatically set to true when a localization table's *(deprecated, hidden)* - **Property `RootLocalizationTable`** (`LocalizationTable`): A reference to a LocalizationTable to be used to apply automated - **Property `SelectionBehaviorDown`** (`SelectionBehavior`): Customizes gamepad selection behavior in the down direction. - **Property `SelectionBehaviorLeft`** (`SelectionBehavior`): Customizes gamepad selection behavior in the left direction. - **Property `SelectionBehaviorRight`** (`SelectionBehavior`): Customizes gamepad selection behavior in the right direction. - **Property `SelectionBehaviorUp`** (`SelectionBehavior`): Customizes gamepad selection behavior in the up direction. - **Property `SelectionGroup`** (`boolean`): Allows customization of gamepad selection movement. - **Event `SelectionChanged`**: Fires when the gamepad selection moves to, leaves, or changes within the ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: IncrementalPatchBuilder last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated --- # Class: IncrementalPatchBuilder ## Properties ### Property: IncrementalPatchBuilder.AddPathsToBundle ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: IncrementalPatchBuilder.BuildDebouncePeriod ```json { "type": "double", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: IncrementalPatchBuilder.HighCompression ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: IncrementalPatchBuilder.SerializePatch ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: IncrementalPatchBuilder.UseFileLevelCompressionInsteadOfChunk ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: IncrementalPatchBuilder.ZstdCompression ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: InputAction last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances summary: "Defines a gameplay action mechanic. These actions are then mapped to hardware inputs using InputBinding." --- # Class: InputAction > Defines a gameplay action mechanic. These actions are then mapped to hardware > inputs using [InputBinding](/docs/reference/engine/classes/InputBinding.md). ## Description `InputAction` defines a gameplay action mechanic such as "Jump," "Sprint," or "Shoot." These actions are then mapped to hardware inputs using [InputBinding](/docs/reference/engine/classes/InputBinding.md). An `InputAction` will check for its first ancestor type of [InputContext](/docs/reference/engine/classes/InputContext.md) and register itself to that context (if there is no ancestor context, it will be registered to a default context). ## Properties ### Property: InputAction.BoolState ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Input" ] } ``` A non-scriptable read-only property only visible in Studio, primarily useful for debugging while testing an `InputAction` with [Type](/docs/reference/engine/classes/InputAction.md) of [Bool](/docs/reference/engine/enums/InputActionType.md). ### Property: InputAction.Direction1DState ```json { "type": "float", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Input" ] } ``` A non-scriptable read-only property only visible in Studio, primarily useful for debugging while testing an `InputAction` with [Type](/docs/reference/engine/classes/InputAction.md) of [Direction1D](/docs/reference/engine/enums/InputActionType.md). ### Property: InputAction.Direction2DState ```json { "type": "Vector2", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Input" ] } ``` A non-scriptable read-only property only visible in Studio, primarily useful for debugging while testing an `InputAction` with [Type](/docs/reference/engine/classes/InputAction.md) of [Direction2D](/docs/reference/engine/enums/InputActionType.md). ### Property: InputAction.Direction3DState ```json { "type": "Vector3", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Input" ] } ``` A non-scriptable read-only property only visible in Studio, primarily useful for debugging while testing an `InputAction` with [Type](/docs/reference/engine/classes/InputAction.md) of [Direction3D](/docs/reference/engine/enums/InputActionType.md). ### Property: InputAction.Enabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Input" ], "simulationAccess": true } ``` Determines if the `InputAction` is enabled or not. The action state will be reset if this property is toggled to `false`. ### Property: InputAction.Type ```json { "type": "InputActionType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Input" ], "simulationAccess": true } ``` Specifies what type of input value the action is expecting. See [InputActionType](/docs/reference/engine/enums/InputActionType.md) for more details. ### Property: InputAction.ViewportPositionState ```json { "type": "Vector2", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Input" ] } ``` A non-scriptable read-only property only visible in Studio, primarily useful for debugging while testing an `InputAction` with [Type](/docs/reference/engine/classes/InputAction.md) of [ViewportPosition](/docs/reference/engine/enums/InputActionType.md). ## Methods ### Method: InputAction:Fire **Signature:** `InputAction:Fire(state: Variant): ()` Updates the `InputAction` to the given state and fires the appropriate signals. This method is most useful for script‑triggered "input" where the passed `state` should trigger events like [Pressed](/docs/reference/engine/classes/InputAction.md) or [StateChanged](/docs/reference/engine/classes/InputAction.md) regardless of whether the player triggered that state through normal inputs. This method will only accept a `state` parameter that matches the [Type](/docs/reference/engine/classes/InputAction.md) and attempting to call it with a mismatched type will cause an error, for example passing a state of `0.5` when the [Type](/docs/reference/engine/classes/InputAction.md) is [Bool](/docs/reference/engine/enums/InputActionType.md). Note that this method follows the conditions of [Pressed](/docs/reference/engine/classes/InputAction.md), [Released](/docs/reference/engine/classes/InputAction.md), and [StateChanged](/docs/reference/engine/classes/InputAction.md). For example, if you make multiple consecutive calls to `Fire()` with a state of `true`, [StateChanged](/docs/reference/engine/classes/InputAction.md) will only fire on the first state change and the subsequent calls to `Fire()` will do nothing. *Security: None · Thread Safety: Unsafe · Capabilities: Input · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `state` | `Variant` | | | **Returns:** `()` ### Method: InputAction:GetState **Signature:** `InputAction:GetState(): Variant` Returns the current state of the [InputAction](/docs/reference/engine/classes/InputAction.md), for example `true` for an action with [Type](/docs/reference/engine/classes/InputAction.md) set to [Bool](/docs/reference/engine/enums/InputActionType.md). *Security: None · Thread Safety: Safe · Capabilities: Input · Simulation Access: true* **Returns:** `Variant` — The current state of [InputAction](/docs/reference/engine/classes/InputAction.md). ## Events ### Event: InputAction.Pressed **Signature:** `InputAction.Pressed()` This event fires only when the [Type](/docs/reference/engine/classes/InputAction.md) is set to [Bool](/docs/reference/engine/enums/InputActionType.md), and only when the state transitions from `false` to `true`. *Security: None · Capabilities: Input* ### Event: InputAction.Released **Signature:** `InputAction.Released()` This event fires only when the [Type](/docs/reference/engine/classes/InputAction.md) is set to [Bool](/docs/reference/engine/enums/InputActionType.md), and only when the state transitions from `true` to `false`. *Security: None · Capabilities: Input* ### Event: InputAction.StateChanged **Signature:** `InputAction.StateChanged(value: Variant)` This event fires for all [InputActionType](/docs/reference/engine/enums/InputActionType.md) types whenever the state changes, except if the state attempts to transition to the same state. *Security: None · Capabilities: Input* **Parameters:** | Name | Type | Description | |------|------|-------------| | `value` | `Variant` | The new state of the [InputAction](/docs/reference/engine/classes/InputAction.md). | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: InputBinding last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances summary: "Defines which hardware binding should trigger the parent InputAction." --- # Class: InputBinding > Defines which hardware binding should trigger the parent [InputAction](/docs/reference/engine/classes/InputAction.md). ## Description An `InputBinding` defines which hardware binding should trigger the parent [InputAction](/docs/reference/engine/classes/InputAction.md), for example a key press, gamepad button, or tap on a touch‑enabled device. There can be multiple `InputBinding` instances parented to an [InputAction](/docs/reference/engine/classes/InputAction.md). ## Properties ### Property: InputBinding.Backward ```json { "type": "KeyCode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Composite Directions", "capabilities": [ "Input" ] } ``` Specifies an alternate [KeyCode](/docs/reference/engine/enums/KeyCode.md) for dispatching directionally "backward" inputs to [GetState()](/docs/reference/engine/classes/InputAction.md) and the [StateChanged](/docs/reference/engine/classes/InputAction.md) event of the parent [InputAction](/docs/reference/engine/classes/InputAction.md). Only applies when the parent action's [Type](/docs/reference/engine/classes/InputAction.md) is [Direction3D](/docs/reference/engine/enums/InputActionType.md), in which case the dispatched value will be a [Vector3](/docs/reference/engine/datatypes/Vector3.md) with its [Z](/docs/reference/engine/datatypes/Vector3.md) component between `0` and `1`. ### Property: InputBinding.ClampMagnitudeToOne ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Composite Directions", "capabilities": [ "Input" ] } ``` Determines whether the binding normalizes the combined composite directional vector to a maximum magnitude of `1` only if it exceeds that length. This prevents diagonal composite inputs (for example [Up](/docs/reference/engine/classes/InputBinding.md) and [Right](/docs/reference/engine/classes/InputBinding.md) pressed simultaneously) from producing a magnitude greater than `1`, ensuring consistent movement speed regardless of the keys held. Default is `true`. This property only applies when the parent action's [Type](/docs/reference/engine/classes/InputAction.md) is [Direction1D](/docs/reference/engine/enums/InputActionType.md), [Direction2D](/docs/reference/engine/enums/InputActionType.md), or [Direction3D](/docs/reference/engine/enums/InputActionType.md). ### Property: InputBinding.Down ```json { "type": "KeyCode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Composite Directions", "capabilities": [ "Input" ] } ``` Specifies an alternate [KeyCode](/docs/reference/engine/enums/KeyCode.md) for dispatching directionally "down" inputs to [GetState()](/docs/reference/engine/classes/InputAction.md) and the [StateChanged](/docs/reference/engine/classes/InputAction.md) event of the parent [InputAction](/docs/reference/engine/classes/InputAction.md). When the parent action's [Type](/docs/reference/engine/classes/InputAction.md) is [Direction1D](/docs/reference/engine/enums/InputActionType.md), the dispatched value will be a number between `0` and `-1`. When the parent action's [Type](/docs/reference/engine/classes/InputAction.md) is [Direction2D](/docs/reference/engine/enums/InputActionType.md), the dispatched value will be a [Vector2](/docs/reference/engine/datatypes/Vector2.md) with its [Y](/docs/reference/engine/datatypes/Vector2.md) component between `0` and `-1`. When the parent action's [Type](/docs/reference/engine/classes/InputAction.md) is [Direction3D](/docs/reference/engine/enums/InputActionType.md), the dispatched value will be a [Vector3](/docs/reference/engine/datatypes/Vector3.md) with its [Y](/docs/reference/engine/datatypes/Vector3.md) component between `0` and `-1`. Not applicable when the parent action's [Type](/docs/reference/engine/classes/InputAction.md) is [Bool](/docs/reference/engine/enums/InputActionType.md). ### Property: InputBinding.Forward ```json { "type": "KeyCode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Composite Directions", "capabilities": [ "Input" ] } ``` Specifies an alternate [KeyCode](/docs/reference/engine/enums/KeyCode.md) for dispatching directionally "forward" inputs to [GetState()](/docs/reference/engine/classes/InputAction.md) and the [StateChanged](/docs/reference/engine/classes/InputAction.md) event of the parent [InputAction](/docs/reference/engine/classes/InputAction.md). Only applies when the parent action's [Type](/docs/reference/engine/classes/InputAction.md) is [Direction3D](/docs/reference/engine/enums/InputActionType.md), in which case the dispatched value will be a [Vector3](/docs/reference/engine/datatypes/Vector3.md) with its [Z](/docs/reference/engine/datatypes/Vector3.md) component between `0` and `-1`. ### Property: InputBinding.KeyCode ```json { "type": "KeyCode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Input" ] } ``` Specifies the [KeyCode](/docs/reference/engine/enums/KeyCode.md) which triggers the parent [InputAction](/docs/reference/engine/classes/InputAction.md). The code type should match the input action's [Type](/docs/reference/engine/classes/InputAction.md), for example [KeyCode.E](/docs/reference/engine/enums/KeyCode.md) for an action type of [Bool](/docs/reference/engine/enums/InputActionType.md) or [KeyCode.Thumbstick1](/docs/reference/engine/enums/KeyCode.md) for an action type of [Direction2D](/docs/reference/engine/enums/InputActionType.md). Type mismatches will either not fire the [InputAction](/docs/reference/engine/classes/InputAction.md) or the [StateChanged](/docs/reference/engine/classes/InputAction.md) event will receive a converted value. ### Property: InputBinding.Left ```json { "type": "KeyCode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Composite Directions", "capabilities": [ "Input" ] } ``` Specifies an alternate [KeyCode](/docs/reference/engine/enums/KeyCode.md) for dispatching directionally "left" inputs to [GetState()](/docs/reference/engine/classes/InputAction.md) and the [StateChanged](/docs/reference/engine/classes/InputAction.md) event of the parent [InputAction](/docs/reference/engine/classes/InputAction.md). When the parent action's [Type](/docs/reference/engine/classes/InputAction.md) is [Direction2D](/docs/reference/engine/enums/InputActionType.md), the dispatched value will be a [Vector2](/docs/reference/engine/datatypes/Vector2.md) with its [X](/docs/reference/engine/datatypes/Vector2.md) component between `0` and `-1`. When the parent action's [Type](/docs/reference/engine/classes/InputAction.md) is [Direction3D](/docs/reference/engine/enums/InputActionType.md), the dispatched value will be a [Vector3](/docs/reference/engine/datatypes/Vector3.md) with its [X](/docs/reference/engine/datatypes/Vector3.md) component between `0` and `-1`. Not applicable when the parent action's [Type](/docs/reference/engine/classes/InputAction.md) is [Direction1D](/docs/reference/engine/enums/InputActionType.md) or [Bool](/docs/reference/engine/enums/InputActionType.md). ### Property: InputBinding.PointerIndex ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Input" ] } ``` ### Property: InputBinding.PressedThreshold ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Input" ] } ``` Numerical value above which to fire an [InputAction](/docs/reference/engine/classes/InputAction.md) with a [Type](/docs/reference/engine/classes/InputAction.md) of [Bool](/docs/reference/engine/enums/InputActionType.md), for example when a gamepad trigger such as [KeyCode.ButtonL2](/docs/reference/engine/enums/KeyCode.md) exceeds `0.5` (halfway pressed). Default is `0.5`. This property must be greater than or equal to [ReleasedThreshold](/docs/reference/engine/classes/InputBinding.md) or else it will be clamped to [ReleasedThreshold](/docs/reference/engine/classes/InputBinding.md). ### Property: InputBinding.PrimaryModifier ```json { "type": "KeyCode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Input" ] } ``` Specifies a [KeyCode](/docs/reference/engine/enums/KeyCode.md) that serves as a required modifier for the binding. The [InputBinding](/docs/reference/engine/classes/InputBinding.md) will only trigger the parent [InputAction](/docs/reference/engine/classes/InputAction.md) if this input is pressed prior to [KeyCode](/docs/reference/engine/classes/InputBinding.md), [UIButton](/docs/reference/engine/classes/InputBinding.md), or composite directions. There is no ordering requirement between [PrimaryModifier](/docs/reference/engine/classes/InputBinding.md) and [SecondaryModifier](/docs/reference/engine/classes/InputBinding.md). If both properties are set, both modifiers must be pressed to activate the binding, but the order in which they are pressed does not matter. If set to [KeyCode.Unknown](/docs/reference/engine/enums/KeyCode.md), no primary modifier is required. ### Property: InputBinding.ReleasedThreshold ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Input" ] } ``` Numerical value below which to fire an [InputAction](/docs/reference/engine/classes/InputAction.md) with a [Type](/docs/reference/engine/classes/InputAction.md) of [Bool](/docs/reference/engine/enums/InputActionType.md), for example when a gamepad trigger such as [KeyCode.ButtonL2](/docs/reference/engine/enums/KeyCode.md) falls below `0.5` (less than halfway pressed). Default is `0.2`. This property must be less than or equal to [PressedThreshold](/docs/reference/engine/classes/InputBinding.md) or else it will be clamped to [PressedThreshold](/docs/reference/engine/classes/InputBinding.md). ### Property: InputBinding.ResponseCurve ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Input" ] } ``` A numerical value to control the sensitivity of gamepad thumbstick input by applying a quadratic response curve. Commonly used for camera control to make small thumbstick inputs more precise, while ramping up quickly to allow for large movements with higher magnitude input. Ranges between `1` to `10`, with higher values providing a more intense curve. Default is `1`, at which input is returned exactly as received. Only applicable when the parent action's [Type](/docs/reference/engine/classes/InputAction.md) is [Direction2D](/docs/reference/engine/enums/InputActionType.md) and the [KeyCode](/docs/reference/engine/classes/InputBinding.md) is [Thumbstick1](/docs/reference/engine/enums/KeyCode.md) or [Thumbstick2](/docs/reference/engine/enums/KeyCode.md). ### Property: InputBinding.Right ```json { "type": "KeyCode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Composite Directions", "capabilities": [ "Input" ] } ``` Specifies an alternate [KeyCode](/docs/reference/engine/enums/KeyCode.md) for dispatching directionally "right" inputs to [GetState()](/docs/reference/engine/classes/InputAction.md) and the [StateChanged](/docs/reference/engine/classes/InputAction.md) event of the parent [InputAction](/docs/reference/engine/classes/InputAction.md). When the parent action's [Type](/docs/reference/engine/classes/InputAction.md) is [Direction2D](/docs/reference/engine/enums/InputActionType.md), the dispatched value will be a [Vector2](/docs/reference/engine/datatypes/Vector2.md) with its [X](/docs/reference/engine/datatypes/Vector2.md) component between `0` and `1`. When the parent action's [Type](/docs/reference/engine/classes/InputAction.md) is [Direction3D](/docs/reference/engine/enums/InputActionType.md), the dispatched value will be a [Vector3](/docs/reference/engine/datatypes/Vector3.md) with its [X](/docs/reference/engine/datatypes/Vector3.md) component between `0` and `1`. Not applicable when the parent action's [Type](/docs/reference/engine/classes/InputAction.md) is [Direction1D](/docs/reference/engine/enums/InputActionType.md) or [Bool](/docs/reference/engine/enums/InputActionType.md). ### Property: InputBinding.Scale ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Input" ] } ``` A scalar multiplier applied uniformly to every component of the output value. Applies to [InputAction](/docs/reference/engine/classes/InputAction.md) types [Direction1D](/docs/reference/engine/enums/InputActionType.md), [Direction2D](/docs/reference/engine/enums/InputActionType.md), and [Direction3D](/docs/reference/engine/enums/InputActionType.md). To scale individual components independently, use [Vector2Scale](/docs/reference/engine/classes/InputBinding.md) (for [Direction2D](/docs/reference/engine/enums/InputActionType.md)) or [Vector3Scale](/docs/reference/engine/classes/InputBinding.md) (for [Direction3D](/docs/reference/engine/enums/InputActionType.md)). When both `Scale` and a vector scale are set, both are applied. ### Property: InputBinding.SecondaryModifier ```json { "type": "KeyCode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Input" ] } ``` Specifies a [KeyCode](/docs/reference/engine/enums/KeyCode.md) that serves as a required modifier for the binding. The [InputBinding](/docs/reference/engine/classes/InputBinding.md) will only trigger the parent [InputAction](/docs/reference/engine/classes/InputAction.md) if this input is pressed prior to [KeyCode](/docs/reference/engine/classes/InputBinding.md), [UIButton](/docs/reference/engine/classes/InputBinding.md), or composite directions. There is no ordering requirement between [PrimaryModifier](/docs/reference/engine/classes/InputBinding.md) and [SecondaryModifier](/docs/reference/engine/classes/InputBinding.md). If both properties are set, both modifiers must be pressed to activate the binding, but the order in which they are pressed does not matter. If set to [KeyCode.Unknown](/docs/reference/engine/enums/KeyCode.md), no secondary modifier is required. ### Property: InputBinding.UIButton ```json { "type": "GuiButton", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Input" ] } ``` [GuiButton](/docs/reference/engine/classes/GuiButton.md) to connect to a boolean action. ### Property: InputBinding.UIModifier ```json { "type": "GuiButton", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Input" ] } ``` Specifies a [GuiButton](/docs/reference/engine/classes/GuiButton.md) that must be pressed for the [InputBinding](/docs/reference/engine/classes/InputBinding.md) to trigger the parent [InputAction](/docs/reference/engine/classes/InputAction.md). Behaves similarly to [PrimaryModifier](/docs/reference/engine/classes/InputBinding.md) and [SecondaryModifier](/docs/reference/engine/classes/InputBinding.md), but accepts a UI button reference rather than a [KeyCode](/docs/reference/engine/enums/KeyCode.md). If `nil`, no modifier is required. ### Property: InputBinding.Up ```json { "type": "KeyCode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Composite Directions", "capabilities": [ "Input" ] } ``` Specifies an alternate [KeyCode](/docs/reference/engine/enums/KeyCode.md) for dispatching directionally "up" inputs to [GetState()](/docs/reference/engine/classes/InputAction.md) and the [StateChanged](/docs/reference/engine/classes/InputAction.md) event of the parent [InputAction](/docs/reference/engine/classes/InputAction.md). When the parent action's [Type](/docs/reference/engine/classes/InputAction.md) is [Direction1D](/docs/reference/engine/enums/InputActionType.md), the dispatched value will be a number between `0` and `1`. When the parent action's [Type](/docs/reference/engine/classes/InputAction.md) is [Direction2D](/docs/reference/engine/enums/InputActionType.md), the dispatched value will be a [Vector2](/docs/reference/engine/datatypes/Vector2.md) with its [Y](/docs/reference/engine/datatypes/Vector2.md) component between `0` and `1`. When the parent action's [Type](/docs/reference/engine/classes/InputAction.md) is [Direction3D](/docs/reference/engine/enums/InputActionType.md), the dispatched value will be a [Vector3](/docs/reference/engine/datatypes/Vector3.md) with its [Y](/docs/reference/engine/datatypes/Vector3.md) component between `0` and `1`. Not applicable when the parent action's [Type](/docs/reference/engine/classes/InputAction.md) is [Bool](/docs/reference/engine/enums/InputActionType.md). ### Property: InputBinding.Vector2Scale ```json { "type": "Vector2", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Input" ] } ``` Amount by which each component of the output is independently linearly scaled. Only applies when the parent action's [Type](/docs/reference/engine/classes/InputAction.md) is [Direction2D](/docs/reference/engine/enums/InputActionType.md). ### Property: InputBinding.Vector3Scale ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Input" ] } ``` Amount by which each component of the output is independently linearly scaled. Only applies when the parent action's [Type](/docs/reference/engine/classes/InputAction.md) is [Direction3D](/docs/reference/engine/enums/InputActionType.md). ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: InputContext last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances summary: "Collection of actions which holds related actions and defines how they interact with other contexts/actions." --- # Class: InputContext > Collection of actions which holds related actions and defines how they > interact with other contexts/actions. ## Description An `InputContext` is a collection of actions which holds related [InputActions](/docs/reference/engine/classes/InputAction.md) and defines how they interact with other contexts and actions. Nested `InputContext` instances will have no effect and ordering/priority is managed through [Enabled](/docs/reference/engine/classes/InputContext.md), [Priority](/docs/reference/engine/classes/InputContext.md), and [Sink](/docs/reference/engine/classes/InputContext.md). ## Properties ### Property: InputContext.Enabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Input" ] } ``` Determines if this `InputContext` is enabled or not. When `false`, all of the descendant actions of the context do not receive any signals except when `Enabled` is toggled from `true` to `false`, in which case a final "end" signal is triggered if a key is pressed or a two-directional input is not zero. ### Property: InputContext.Priority ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Input" ] } ``` The priority level at which the context should be run (higher priority `InputContext` instances run before lower ones). ### Property: InputContext.Sink ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Input" ] } ``` When `Sink` is set to `true`, inputs will not be processed for connected [InputAction](/docs/reference/engine/classes/InputAction.md) bindings within contexts of lower [Priority](/docs/reference/engine/classes/InputContext.md). Contexts with the same priority will receive the input. For example, if multiple contexts contain an [InputAction](/docs/reference/engine/classes/InputAction.md) with a binding to [KeyCode.E](/docs/reference/engine/enums/KeyCode.md) and a higher priority context has `Sink` set to `true`, the lower priority contexts will not receive the input signal for [KeyCode.E](/docs/reference/engine/enums/KeyCode.md) and will fire no events for it. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: InputObject last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable summary: "An object created when an input begins that describes a particular user input." --- # Class: InputObject > An object created when an input begins that describes a particular user input. ## Description An `InputObject` represents a single user input, such as mouse movement, touches, key presses and more. It is created when an input begins. The properties of this object vary according the [UserInputType](/docs/reference/engine/classes/InputObject.md). Each kind of input will undergo various changes to its [UserInputState](/docs/reference/engine/classes/InputObject.md). During the lifetime of an input, other properties which further describe the input may change, such as [Position](/docs/reference/engine/classes/InputObject.md) and [Delta](/docs/reference/engine/classes/InputObject.md). Keyboard and gamepad button presses will have the [KeyCode](/docs/reference/engine/classes/InputObject.md) property set. Once created at the beginning of an input, the same object persists and is updated until the input ends. As a result, you can track the object's changes using the [Changed](/docs/reference/engine/classes/Object.md) event as the user changes the input in question. You can also place these objects into a list of active inputs track and interact with the object after it's creation by an event such as [UserInputService.InputBegan](/docs/reference/engine/classes/UserInputService.md). This is mostly useful for touch events, as each touch point will have a separate `InputObject`. See also [UserInputService](/docs/reference/engine/classes/UserInputService.md) whose events and functions often use `InputObject`, and [GuiObject](/docs/reference/engine/classes/GuiObject.md) whose events related to user input use `InputObject`. ## Properties ### Property: InputObject.Delta ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Input" ] } ``` A [Vector3](/docs/reference/engine/datatypes/Vector3.md) describing the delta (change) between input movements. This is useful when used with the input's [Position](/docs/reference/engine/classes/InputObject.md) to track the position and movement of the user's input, such as when you're creating custom movement or camera scripts. Consider tracking input object changes using the [Object.Changed](/docs/reference/engine/classes/Object.md) event or when user input changes via events such as [UserInputService.InputChanged](/docs/reference/engine/classes/UserInputService.md) and [GuiObject.InputChanged](/docs/reference/engine/classes/GuiObject.md). Note that an [InputObject](/docs/reference/engine/classes/InputObject.md) corresponding to [UserInputType.MouseButton1](/docs/reference/engine/enums/UserInputType.md) (left click) and [UserInputType.MouseButton2](/docs/reference/engine/enums/UserInputType.md) (right click) supplied from an [InputBegan](/docs/reference/engine/classes/UserInputService.md) callback will not have its [Delta](/docs/reference/engine/classes/InputObject.md) or [Position](/docs/reference/engine/classes/InputObject.md) updated once created, except for when the mouse input ends. In order to get updated deltas for mouse inputs, you must instead reference an [InputObject](/docs/reference/engine/classes/InputObject.md) from an [InputChanged](/docs/reference/engine/classes/UserInputService.md) callback, or call [GetMouseDelta()](/docs/reference/engine/classes/UserInputService.md). However, any [InputObjects](/docs/reference/engine/classes/InputObject.md) corresponding to touch inputs will have their delta and position updated every frame throughout their lifetime. ### Property: InputObject.KeyCode ```json { "type": "KeyCode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Input" ] } ``` Contains a [KeyCode](/docs/reference/engine/enums/KeyCode.md) enum that describes what kind of input was used. For types of input like keyboard, this describes what key was pressed. For inputs like the mouse, this provides no additional information. ### Property: InputObject.Position ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Input" ] } ``` A [Vector3](/docs/reference/engine/datatypes/Vector3.md) describing the positional value of this input. For mouse and touch input, this is the screen position of the mouse/touch, described in the **X** and **Y** components. The inset applied to GUI elements (such as from the top bar) is accounted for in the position. For the mouse wheel input, the **Z** component describes whether the wheel was moved forward (`1`), backwards (`-1`), or not at all (`0`). Note that an [InputObject](/docs/reference/engine/classes/InputObject.md) corresponding to [UserInputType.MouseButton1](/docs/reference/engine/enums/UserInputType.md) (left click) and [UserInputType.MouseButton2](/docs/reference/engine/enums/UserInputType.md) (right click) supplied from an [InputBegan](/docs/reference/engine/classes/UserInputService.md) callback will not have its [Delta](/docs/reference/engine/classes/InputObject.md) or [Position](/docs/reference/engine/classes/InputObject.md) updated once created, except for when the mouse input ends. In order to get updated positions for mouse inputs, you must instead reference an [InputObject](/docs/reference/engine/classes/InputObject.md) from an [InputChanged](/docs/reference/engine/classes/UserInputService.md) callback, or call [GetMouseLocation()](/docs/reference/engine/classes/UserInputService.md). However, any [InputObjects](/docs/reference/engine/classes/InputObject.md) corresponding to touch inputs will have their delta and position updated every frame throughout their lifetime. ### Property: InputObject.UserInputState ```json { "type": "UserInputState", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Input" ] } ``` This property describes the state of an input being performed, following a specific flow depending on the [UserInputType](/docs/reference/engine/classes/InputObject.md). It uses the enum of the same name, [UserInputState](/docs/reference/engine/enums/UserInputState.md). ### Property: InputObject.UserInputType ```json { "type": "UserInputType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Input" ] } ``` This property describes the kind of input this [InputObject](/docs/reference/engine/classes/InputObject.md) represents, such as mouse, keyboard, touch, or gamepad input. It uses the enum of the same name, [UserInputType](/docs/reference/engine/enums/UserInputType.md). ## Methods ### Method: InputObject:IsModifierKeyDown **Signature:** `InputObject:IsModifierKeyDown(modifierKey: ModifierKey): boolean` Returns `true` if the passed in `modifierKey` such as [Shift](/docs/reference/engine/enums/ModifierKey.md) is being held down. *Security: None · Thread Safety: Unsafe · Capabilities: Input* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `modifierKey` | `ModifierKey` | | | **Returns:** `boolean` — `true` if the passed in `modifierKey` is being held down; `false` otherwise. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: InsertService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service summary: "Used to insert assets from the Roblox website." --- # Class: InsertService > Used to insert assets from the Roblox website. ## Description InsertService is used to insert assets from the Roblox website, typically the [LoadAsset](/docs/reference/engine/classes/InsertService.md) function. To load an asset, it must be accessible by the creator of the experience loading it, which can be either a user or group. Should an experience be uploaded by a different creator, the asset data would not be accessible. See the [LoadAsset()](/docs/reference/engine/classes/InsertService.md) method for more details on this security check. Note that you should **not** use this service for loading API keys or other secrets. Use [HttpService:GetSecret()](/docs/reference/engine/classes/HttpService.md) instead. #### See Also - [AssetService](/docs/reference/engine/classes/AssetService.md), which can provide information about assets you might want to load using InsertService ## Properties ### Property: InsertService.AllowInsertFreeModels ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "LoadUnownedAsset" ] } ``` > **Deprecated:** This item was never released. Do not use it in new work. The AllowInsertFreeModels property toggles whether ''Free Models'' can be inserted into the game, regardless of whether the place owner owns the asset. ## Methods ### Method: InsertService:CreateMeshPartAsync **Signature:** `InsertService:CreateMeshPartAsync(meshId: ContentId, collisionFidelity: CollisionFidelity, renderFidelity: RenderFidelity): MeshPart` Creates a new [MeshPart](/docs/reference/engine/classes/MeshPart.md) with specified [CollisionFidelity](/docs/reference/engine/classes/MeshPart.md) and [RenderFidelity](/docs/reference/engine/classes/MeshPart.md). Because [MeshPart.MeshId](/docs/reference/engine/classes/MeshPart.md) is read only, this is the way to create a [MeshPart](/docs/reference/engine/classes/MeshPart.md) through scripts without having to clone an existing one. It throws errors if creation fails. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `meshId` | `ContentId` | | Mesh asset ID. | | `collisionFidelity` | `CollisionFidelity` | | Set [MeshPart.CollisionFidelity](/docs/reference/engine/classes/MeshPart.md). | | `renderFidelity` | `RenderFidelity` | | Set [MeshPart.RenderFidelity](/docs/reference/engine/classes/MeshPart.md). | **Returns:** `MeshPart` — New [MeshPart](/docs/reference/engine/classes/MeshPart.md) instance. ### Method: InsertService:GetFreeDecalsAsync **Signature:** `InsertService:GetFreeDecalsAsync(searchText: string, pageNum: int): Array` The GetFreeDecalsAsync function retrieves a list of free [Decals](/docs/reference/engine/classes/Decal.md) from the Catalog. The return type for this method is very odd, as it returns a single table wrapped in a table. The best way to explain it is to show a visual of the array returned: ```lua [1] = { CurrentStartIndex = 1, -- This can vary depending on the page you input. TotalCount = 21, -- Always 21. Results = { -- All parameters here are pseudo. They can vary depending on the asset. [1] = { Name = "Asset Name", AssetId = 0000000, AssetVersionId = 0000000, CreatorName = "Roblox", }, -- [2], [3], and so on... up to [21] }, } ``` An example for iterating over this list has been provided at the bottom of this page. Additionally, if you want to insert [Models](/docs/reference/engine/classes/Model.md) instead, you can use the [InsertService:GetFreeModelsAsync()](/docs/reference/engine/classes/InsertService.md) function. _Note:_ The page argument starts at 0. So Page 1 = 0, Page 2 = 1, etc. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetRead* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `searchText` | `string` | | String used to search for free decals in the Catalog. | | `pageNum` | `int` | | The page number in the Catalog to return. | **Returns:** `Array` — A single table (of returned free decals) wrapped in a table. **InsertService:GetFreeDecalsAsync** ```lua local InsertService = game:GetService("InsertService") local page = unpack(InsertService:GetFreeDecalsAsync("Cats", 0)) -- Search for "Cats" on Page 1. for i = 1, page.TotalCount do local item = page.Results[i] print("Item #" .. i) for key, value in pairs(item) do print(" " .. key .. ": " .. value) end end ``` ### Method: InsertService:GetFreeModelsAsync **Signature:** `InsertService:GetFreeModelsAsync(searchText: string, pageNum: int): Array` The GetFreeModelsAsync function retrieves a list of Free [Models](/docs/reference/engine/classes/Model.md) from the Catalog. The return type for this method is very odd, as it returns a single table wrapped in a table. The best way to explain it is to show a visual of the array returned: ```lua [1] = { CurrentStartIndex = 1, -- This can vary depending on the page you input. TotalCount = 21, -- Always 21. Results = { -- All parameters here are pseudo. They can vary depending on the asset. [1] = { Name = "Asset Name", AssetId = 0000000, AssetVersionId = 0000000, CreatorName = "Roblox", } -- [2], [3], and so on... up to [21] } } ``` An example for iterating over this list has been provided at the bottom of this page. Additionally, if you would like to insert free [Decals](/docs/reference/engine/classes/Decal.md), you can use the [InsertService:GetFreeDecalsAsync()](/docs/reference/engine/classes/InsertService.md) function. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetRead* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `searchText` | `string` | | String used to search for free decals in the Catalog. | | `pageNum` | `int` | | The page number in the Catalog to return. | **Returns:** `Array` — A single table (of returned free models) wrapped in a table. **InsertService:GetFreeModelsAsync** ```lua local InsertService = game:GetService("InsertService") local page = unpack(InsertService:GetFreeModelsAsync("Cats", 0)) -- Search for "Cats" on Page 1. for i = 1, page.TotalCount do local item = page.Results[i] print("Item #" .. i) for key, value in pairs(item) do print(" " .. key .. ": " .. value) end end ``` ### Method: InsertService:GetLatestAssetVersionAsync **Signature:** `InsertService:GetLatestAssetVersionAsync(assetId: int64): int64` Returns the latest AssetVersionId of an asset for assets created by the place creator. Can be used in combination with [InsertService:LoadAssetVersion()](/docs/reference/engine/classes/InsertService.md) to load the latest version of a model, even if it gets updated while the game is running. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetRead* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `assetId` | `int64` | | | **Returns:** `int64` ### Method: InsertService:LoadAsset **Signature:** `InsertService:LoadAsset(assetId: int64): Instance` The LoadAsset function fetches an asset given its ID and returns a [Model](/docs/reference/engine/classes/Model.md) containing the asset. For example, to load this public [Doge](https://www.roblox.com/library/257489726/Doge) [Model](/docs/reference/engine/classes/Model.md), which has the asset ID **_257489726_**, you can use: ```lua local InsertService = game:GetService("InsertService") local Workspace = game:GetService("Workspace") local assetId = 257489726 local model = InsertService:LoadAsset(assetId) model.Parent = Workspace ``` Calls to this function may fail if a server providing a model is having problems. As such, it's generally a good idea to wrap calls to this function in `pcall` to catch these kinds of errors. ```lua local InsertService = game:GetService("InsertService") local Workspace = game:GetService("Workspace") local assetId = 257489726 local success, model = pcall(InsertService.LoadAsset, InsertService, assetId) if success and model then print("Model loaded successfully") model.Parent = Workspace else print("Model failed to load!") end ``` #### Security Check An asset loaded by this function must meet one of the following: - The asset must be **created or owned** by the game creator. - The asset must be **shared** by the asset owner. - The asset must be owned by Roblox. Additionally, benign asset types such as t-shirts, shirts, pants and avatar accessories are loadable from any game as they are `OpenUse`. To load assets which do not meet the above criteria, such as free Models published on the Store, you must use [AssetService:LoadAssetAsync()](/docs/reference/engine/classes/AssetService.md) and enable [AssetService.AllowInsertFreeAssets](/docs/reference/engine/classes/AssetService.md). See also: - [AssetService:GetBundleDetailsAsync()](/docs/reference/engine/classes/AssetService.md), to find out which assets are associated with a bundle. - For plugins, see [DataModel:GetObjects()](/docs/reference/engine/classes/DataModel.md) *Yields · Security: None · Thread Safety: Unsafe · Capabilities: LoadOwnedAsset* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `assetId` | `int64` | | The asset ID of the asset being loaded. | **Returns:** `Instance` — An instance of the loaded asset. **InsertService:LoadAsset** ```lua local InsertService = game:GetService("InsertService") local ASSET_ID = 82353 local asset = InsertService:LoadAsset(ASSET_ID) asset.Parent = workspace ``` ### Method: InsertService:LoadAssetVersion **Signature:** `InsertService:LoadAssetVersion(assetVersionId: int64): Instance` Returns a model inserted into [InsertService](/docs/reference/engine/classes/InsertService.md) containing the asset with the given assetVersionId. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: LoadOwnedAsset* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `assetVersionId` | `int64` | | | **Returns:** `Instance` **InsertService:LoadAssetVersion** ```lua local InsertService = game:GetService("InsertService") local ASSET_VERSION_ID = 296050499 local asset = InsertService:LoadAssetVersion(ASSET_VERSION_ID) asset.Parent = game.Workspace ``` ### Method: InsertService:ApproveAssetId **Signature:** `InsertService:ApproveAssetId(assetId: int64): ()` > **Deprecated:** This item is deprecated. Do not use it for new work. *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `assetId` | `int64` | | | **Returns:** `()` ### Method: InsertService:ApproveAssetVersionId **Signature:** `InsertService:ApproveAssetVersionId(assetVersionId: int64): ()` > **Deprecated:** This item is deprecated. Do not use it for new work. *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `assetVersionId` | `int64` | | | **Returns:** `()` ### Method: InsertService:GetBaseCategories **Signature:** `InsertService:GetBaseCategories(): Array` > **Deprecated:** This item is deprecated. Do not use it for new work. *Yields · Security: None · Thread Safety: Unsafe* **Returns:** `Array` ### Method: InsertService:GetBaseSets **Signature:** `InsertService:GetBaseSets(): Array` > **Deprecated:** [Sets have been removed](https://devforum.roblox.com/t/sunsetting-sets/189402) from Roblox. Returns an array of dictionaries, containing information about various Roblox approved sets. *Yields · Security: None · Thread Safety: Unsafe* **Returns:** `Array` ### Method: InsertService:GetCollection **Signature:** `InsertService:GetCollection(categoryId: int64): Array` > **Deprecated:** [Sets have been removed](https://devforum.roblox.com/t/sunsetting-sets/189402) from Roblox. Returns the most recently uploaded models in the specified category. *Yields · Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `categoryId` | `int64` | | | **Returns:** `Array` **InsertService:GetCollection** The format of the returned table is as follows:
{ -- Array
{ -- Table
AssetId = <Integer>, -- for use with InsertService::LoadAsset()
AssetSetId = <Integer>, -- source set
AssetVersionId = <Integer>, -- for use with
InsertService::LoadAssetVersion()
IsTrusted = <Integer>, -- "trusted" flag
Name = <String>, -- model name
CreatorName = <String> -- creator name
}
}
```lua local InsertService = game:GetService("InsertService") local set = InsertService:GetBaseSets()[1] local list = InsertService:GetCollection(set["CategoryId"]) print(list) ``` ### Method: InsertService:GetFreeDecals **Signature:** `InsertService:GetFreeDecals(searchText: string, pageNum: int): Array` Retrieves a list of free Decals from the Catalog. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetRead* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `searchText` | `string` | | String used to search for free decals in the Catalog. | | `pageNum` | `int` | | The page number in the Catalog to return. | **Returns:** `Array` — A single table (of returned free decals) wrapped in a table. **InsertService:GetFreeDecalsAsync** ```lua local InsertService = game:GetService("InsertService") local page = unpack(InsertService:GetFreeDecalsAsync("Cats", 0)) -- Search for "Cats" on Page 1. for i = 1, page.TotalCount do local item = page.Results[i] print("Item #" .. i) for key, value in pairs(item) do print(" " .. key .. ": " .. value) end end ``` ### Method: InsertService:GetFreeModels **Signature:** `InsertService:GetFreeModels(searchText: string, pageNum: int): Array` Retrieves a list of Free Models from the Catalog. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetRead* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `searchText` | `string` | | String used to search for free models in the Catalog. | | `pageNum` | `int` | | The page number in the Catalog to return. | **Returns:** `Array` — A single table (of returned free models) wrapped in a table. **InsertService:GetFreeModelsAsync** ```lua local InsertService = game:GetService("InsertService") local page = unpack(InsertService:GetFreeModelsAsync("Cats", 0)) -- Search for "Cats" on Page 1. for i = 1, page.TotalCount do local item = page.Results[i] print("Item #" .. i) for key, value in pairs(item) do print(" " .. key .. ": " .. value) end end ``` ### Method: InsertService:GetUserCategories **Signature:** `InsertService:GetUserCategories(userId: User): Array` > **Deprecated:** This item is deprecated. Do not use it for new work. *Yields · Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `userId` | `User` | | | **Returns:** `Array` ### Method: InsertService:GetUserSets **Signature:** `InsertService:GetUserSets(userId: User): Array` > **Deprecated:** [Sets have been removed](https://devforum.roblox.com/t/sunsetting-sets/189402) from Roblox. Returns an array of dictionaries, containing information about sets owned by the user. This includes - Sets the user is subscribed to. - Sets that the user created. - A single set containing the models created by the user. - A single set containing the decals created by the user. Note: - All values in the dictionaries are strings, even if they are a number. | Name | Description | | --- | --- | | Name | The name of the set. | | Description | The description of the set. | | ImageAssetId | An assetId for the icon of the set. | | CreatorName | The creator of the set. | | AssetSetId | The set's unique ID on the website. | | CategoryId | Identical to AssetSetId | | SetType | The type of set that this set is. | *Yields · Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `userId` | `User` | | | **Returns:** `Array` ### Method: InsertService:Insert **Signature:** `InsertService:Insert(instance: Instance): ()` > **Deprecated:** This function has been superseded by [InsertService:LoadAsset()](/docs/reference/engine/classes/InsertService.md) which should be used in all new work. This function is a legacy method used to insert an [Instance](/docs/reference/engine/classes/Instance.md) into [Workspace](/docs/reference/engine/classes/Workspace.md). *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `instance` | `Instance` | | | **Returns:** `()` **InsertService:Insert** ```lua local InsertService = game:GetService("InsertService") local ASSET_ID = 123456789 local instance = InsertService:LoadAsset(ASSET_ID) if instance then InsertService:Insert(instance) end ``` ### Method: InsertService:loadAsset **Signature:** `InsertService:loadAsset(assetId: int64): Instance` > **Deprecated:** This function is a deprecated variant of [InsertService:LoadAsset()](/docs/reference/engine/classes/InsertService.md) which should be used instead. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: LoadOwnedAsset* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `assetId` | `int64` | | | **Returns:** `Instance` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some
--- name: Instance last_updated: 2026-06-29T19:34:07Z inherits: - Object type: class memory_category: Instances tags: - NotCreatable - NotBrowsable summary: "`Instance` is the base class for all classes in the Roblox class hierarchy which can be part of the DataModel tree." --- # Class: Instance > `Instance` is the base class for all classes in the Roblox class hierarchy > which can be part of the [DataModel](/docs/reference/engine/classes/DataModel.md) tree. ## Description `Instance` is the base class for all classes in the Roblox class hierarchy which can be part of the [DataModel](/docs/reference/engine/classes/DataModel.md) tree. It is not possible to directly create root `Instance` objects, but the special [Instance.new()](/docs/reference/engine/datatypes/Instance.md) constructor creates objects via code, taking the name of the class as a parameter and returning the created object. ## Properties ### Property: Instance.Archivable ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Behavior" } ``` This property determines whether the instance should be included when the experience is published or saved, or when [Clone()](/docs/reference/engine/classes/Instance.md) is called on one of the instance's ancestors. Calling [Clone()](/docs/reference/engine/classes/Instance.md) directly on an instance will return `nil` if that instance is **not** [Archivable](/docs/reference/engine/classes/Instance.md). Copying an object in Studio using the **Duplicate** or **Copy**/**Paste** options will ignore its own [Archivable](/docs/reference/engine/classes/Instance.md) property and set [Archivable](/docs/reference/engine/classes/Instance.md) to `true` for the copy. ``` local part = Instance.new("Part") print(part:Clone()) --> Part part.Archivable = false print(part:Clone()) --> nil ``` ### Property: Instance.Capabilities ```json { "type": "SecurityCapabilities", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Permissions", "capabilities": [ "CapabilityControl" ] } ``` The set of capabilities allowed to be used for scripts inside this instance. For the capabilities to take effect, [Instance.Sandboxed](/docs/reference/engine/classes/Instance.md) property must be enabled. This property is used by an experimental feature. See [script capabilities](/docs/en-us/scripting/capabilities.md) for further details. ### Property: Instance.Name ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` A non-unique identifier of the [Instance](/docs/reference/engine/classes/Instance.md). Names are used to keep the object hierarchy organized, along with allowing scripts to access specific objects. The name of an instance cannot exceed 100 characters in size. The name of an object is often used to access the object through the data model hierarchy using the following methods: ``` local Workspace = game:GetService("Workspace") local baseplate = Workspace.Baseplate local baseplate = Workspace["Baseplate"] local baseplate = Workspace:FindFirstChild("BasePlate") ``` In order to make an object accessible using the dot operator (`.`), its name must start with an underscore or letter, and the rest of the name can only contain letters, numbers, or underscores (no other special characters). If an object's name does not follow this syntax, it will not be accessible using the dot operator and Luau will not interpret its name as an identifier. If more than one object with the same name are siblings, any attempt to index an object by that name will return only one of the objects, similar to [Instance:FindFirstChild()](/docs/reference/engine/classes/Instance.md), but not always the desired object. If a specific object needs to be accessed through code, it's recommended to give it a unique name or guarantee that none of its siblings share the same name. See also [Instance:GetFullName()](/docs/reference/engine/classes/Instance.md) to obtain a full name including the object's hierarchy. ### Property: Instance.Parent ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` The `Parent` property determines the hierarchical parent of the [Instance](/docs/reference/engine/classes/Instance.md). The following terminology is commonly used when talking about how this property is set: - An object is a **child** of, or is **parented to**, another object when its `Parent` is set to that object. - The **descendants** of an [Instance](/docs/reference/engine/classes/Instance.md) are the children of that object, plus the descendants of the children as well. - The **ancestors** of an [Instance](/docs/reference/engine/classes/Instance.md) are all the objects that the instance is a descendant of. It is from the `Parent` property that many other API members get their name, such as [GetChildren()](/docs/reference/engine/classes/Instance.md) and [FindFirstChild()](/docs/reference/engine/classes/Instance.md). This property is also used to manage whether an object exists in the experience or needs to be removed. As long as an object's parent is in the [DataModel](/docs/reference/engine/classes/DataModel.md), is stored in a variable, or is referenced by another object's property, the object remains in the experience; otherwise, the object will automatically be removed. Calling [Destroy()](/docs/reference/engine/classes/Instance.md) will set the `Parent` of an [Instance](/docs/reference/engine/classes/Instance.md) and all of its descendants to `nil`, and also **lock** the `Parent` property. An error is raised when setting the `Parent` of a destroyed object. Newly created objects using [Instance.new()](/docs/reference/engine/datatypes/Instance.md) will not have a parent, and usually will not be visible or function until one is set. #### Object Replication An object created by the server will not replicate to clients until it is parented to some object that is replicated. When creating an object and setting many properties, it's recommended to set the `Parent` property **last**. This ensures the object replicates once, instead of replicating many property changes. ```lua local Workspace = game:GetService("Workspace") -- Set new instance's parent last (recommended) local part = Instance.new("Part") part.Position = Vector3.new(0, 10, 0) part.Parent = Workspace ``` However, if parenting parts to a [Model](/docs/reference/engine/classes/Model.md) whose parent hasn't been set yet, parenting each part to that model is acceptable since the model would not have replicated. ### Property: Instance.PredictionMode ```json { "type": "PredictionMode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: Instance.Sandboxed ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Permissions", "capabilities": [ "CapabilityControl" ] } ``` Turns the instance to be a **sandboxed container**, an experimental feature which limits the actions that scripts inside a particular container can perform. See [script capabilities](/docs/en-us/scripting/capabilities.md) for further details. ### Property: Instance.UniqueId ```json { "type": "UniqueId", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` A unique identifier for the instance, distinct from [Instance.Name](/docs/reference/engine/classes/Instance.md) which is not necessarily unique. ### Property: Instance.archivable *(hidden)* ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Behavior" } ``` > **Deprecated:** This deprecated property is a variant of [Instance.Archivable](/docs/reference/engine/classes/Instance.md) which should be used instead. ### Property: Instance.RobloxLocked *(hidden)* ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` This property used to protect objects in the [CoreGui](/docs/reference/engine/classes/CoreGui.md) service from being altered by users in an unauthorized manner. It has been deprecated and does not do anything. ## Methods ### Method: Instance:AddTag **Signature:** `Instance:AddTag(tag: string): ()` This method applies a tag to the instance, with no effect if the tag is already applied. Successfully adding a tag will fire a signal created by [CollectionService:GetInstanceAddedSignal()](/docs/reference/engine/classes/CollectionService.md) with the given tag. #### Warnings - An instance's tags that were added client-side will be dropped if the server later adds or removes a tag on that instance because the server replicates all tags together and overwrites previous tags. - When tagging an instance, it is common that some resources are used to give the tag its functionality, for example event connections or tables. To prevent memory leaks, it's a good idea to clean these up (disconnect, set to `nil`, etc.) when no longer needed for a tag. Do this when calling [Instance:RemoveTag()](/docs/reference/engine/classes/Instance.md), calling [Instance:Destroy()](/docs/reference/engine/classes/Instance.md), or in a function connected to a signal returned by [CollectionService:GetInstanceRemovedSignal()](/docs/reference/engine/classes/CollectionService.md). *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `tag` | `string` | | | **Returns:** `()` ### Method: Instance:ClearAllChildren **Signature:** `Instance:ClearAllChildren(): ()` This method destroys all of an instance's children and descendants. ``` local part = Instance.new("Part") -- Add some sparkles for i = 1, 3 do local sparkles = Instance.new("Sparkles") sparkles.Parent = part local sc = Instance.new("Sparkles") sc.Parent = sparkles end print("Children:", #part:GetChildren()) --> Children: 3 part:ClearAllChildren() print("Children:", #part:GetChildren()) --> Children: 0 ``` If you do not wish to destroy **all** children and descendants, use either [Instance:GetChildren()](/docs/reference/engine/classes/Instance.md) or [Instance:GetDescendants()](/docs/reference/engine/classes/Instance.md) to loop through those children/descendants and select what to destroy. For example, the following code sample will destroy all [BaseParts](/docs/reference/engine/classes/BasePart.md) descending from a [Model](/docs/reference/engine/classes/Model.md): ``` local Workspace = game:GetService("Workspace") local model = Workspace:FindFirstChild("TestModel") for _, descendant in model:GetDescendants() do if descendant:IsA("BasePart") then descendant:Destroy() end end ``` *Security: None · Thread Safety: Unsafe* **Returns:** `()` ### Method: Instance:Clone **Signature:** `Instance:Clone(): Instance` `Clone()` creates a copy of an instance and all of its descendants, ignoring all instances that are not [Archivable](/docs/reference/engine/classes/Instance.md). The copy of the root instance is returned by this method and its [Parent](/docs/reference/engine/classes/Instance.md) is set to `nil`. Note that if the instance itself has [Archivable](/docs/reference/engine/classes/Instance.md) set to `false`, this method will return `nil`. If a reference property such as [ObjectValue.Value](/docs/reference/engine/classes/ObjectValue.md) is set in a cloned instance, the value of the copy's property depends on the original's value: - If a reference property refers to an instance that was **also** cloned, the copy will refer to the copy. - If a reference property refers to an instance that was **not** cloned, the same value is maintained in the copy. *Security: None · Thread Safety: Unsafe · Capabilities: CreateInstances · Simulation Access: true* **Returns:** `Instance` **Cloning an Instance** Demonstrates cloning a model using [Instance:Clone()](/docs/reference/engine/classes/Instance.md). ```lua local Workspace = game:GetService("Workspace") -- Get a reference to an existing object local model = script.Parent.Model -- Create a clone of the model local clone = model:Clone() -- Move the clone so it's not overlapping the original model clone:PivotTo(model.PrimaryPart.CFrame - (Vector3.xAxis * 10)) -- Add the clone to the Workspace clone.Parent = Workspace ``` ### Method: Instance:Destroy **Signature:** `Instance:Destroy(): ()` Sets the [Instance.Parent](/docs/reference/engine/classes/Instance.md) property to `nil`, locks the [Instance.Parent](/docs/reference/engine/classes/Instance.md) property, disconnects all connections, and calls `Destroy()` on all children. This method is the correct way to dispose of objects that are no longer required. Disposing of unneeded objects is important, since unnecessary objects and connections in a place use up memory which can lead to serious performance issues over time. As a best practice after calling `Destroy()` on an object, set any variables referencing the object (or its descendants) to `nil`. This prevents your code from accessing anything to do with the object. ```lua local part = Instance.new("Part") part.Name = "Hello, world" part:Destroy() -- Don't do this: print(part.Name) --> "Hello, world" -- Do this to prevent the above line from working: part = nil ``` Once an [Instance](/docs/reference/engine/classes/Instance.md) has been destroyed by this method, it cannot be reused because the [Instance.Parent](/docs/reference/engine/classes/Instance.md) property is locked. To **temporarily** remove an object instead of destroying it, set [Parent](/docs/reference/engine/classes/Instance.md) to `nil`. For example: ``` local Workspace = game:GetService("Workspace") object.Parent = nil task.wait(2) object.Parent = Workspace ``` To destroy an object after a set amount of time, use [Debris:AddItem()](/docs/reference/engine/classes/Debris.md). *Security: None · Thread Safety: Unsafe* **Returns:** `()` **Instance:Destroy()** Demonstrates destroying a Part using the [Instance:Destroy()](/docs/reference/engine/classes/Instance.md) function. This function is the correct way to dispose of objects that are no longer required. ```lua local part = script.Parent.Part part:Destroy() ``` ### Method: Instance:FindFirstAncestor **Signature:** `Instance:FindFirstAncestor(name: string): Instance?` Returns the first ancestor of the [Instance](/docs/reference/engine/classes/Instance.md) whose [Instance.Name](/docs/reference/engine/classes/Instance.md) is equal to the given name. This method works upwards, meaning it starts at the instance's immediate [Instance.Parent](/docs/reference/engine/classes/Instance.md) and works up towards the [DataModel](/docs/reference/engine/classes/DataModel.md). If no matching ancestor is found, it returns `nil`. The following code snippet would find the first ancestor of the object named `Car`. ``` local car = object:FindFirstAncestor("Car") ``` For variants of this method that find ancestors of a specific class, please see [Instance:FindFirstAncestorOfClass()](/docs/reference/engine/classes/Instance.md) and [Instance:FindFirstAncestorWhichIsA()](/docs/reference/engine/classes/Instance.md). *Security: None · Thread Safety: Safe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name` | `string` | | The [Instance.Name](/docs/reference/engine/classes/Instance.md) to be looked for. | **Returns:** `Instance?` — The [Instance](/docs/reference/engine/classes/Instance.md) found. ### Method: Instance:FindFirstAncestorOfClass **Signature:** `Instance:FindFirstAncestorOfClass(className: string): Instance?` Returns the first ancestor of the [Instance](/docs/reference/engine/classes/Instance.md) whose [Object.ClassName](/docs/reference/engine/classes/Object.md) is equal to the given className. This method works upwards, meaning it starts at the instance's immediate [Instance.Parent](/docs/reference/engine/classes/Instance.md) and works up towards the [DataModel](/docs/reference/engine/classes/DataModel.md). If no matching ancestor is found, it returns `nil`. A common use of this method is finding the [Model](/docs/reference/engine/classes/Model.md) a [BasePart](/docs/reference/engine/classes/BasePart.md) belongs to. For example: ``` local model = part:FindFirstAncestorOfClass("Model") ``` This method is a variant of [Instance:FindFirstAncestor()](/docs/reference/engine/classes/Instance.md) which checks the [Object.ClassName](/docs/reference/engine/classes/Object.md) property rather than [Instance.Name](/docs/reference/engine/classes/Instance.md). [Instance:FindFirstAncestorWhichIsA()](/docs/reference/engine/classes/Instance.md) also exists, using the [Object:IsA()](/docs/reference/engine/classes/Object.md) method instead to respect class inheritance. *Security: None · Thread Safety: Safe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `className` | `string` | | The [Object.ClassName](/docs/reference/engine/classes/Object.md) to be looked for. | **Returns:** `Instance?` — The [Instance](/docs/reference/engine/classes/Instance.md) found. ### Method: Instance:FindFirstAncestorWhichIsA **Signature:** `Instance:FindFirstAncestorWhichIsA(className: string): Instance?` Returns the first ancestor of the [Instance](/docs/reference/engine/classes/Instance.md) for whom [Object:IsA()](/docs/reference/engine/classes/Object.md) returns true for the given className. This method works upwards, meaning it starts at the instance's immediate [Instance.Parent](/docs/reference/engine/classes/Instance.md) and works up towards the [DataModel](/docs/reference/engine/classes/DataModel.md). If no matching ancestor is found, it returns `nil`. Unlike [Instance:FindFirstAncestorOfClass()](/docs/reference/engine/classes/Instance.md), this method uses [Object:IsA()](/docs/reference/engine/classes/Object.md) which respects class inheritance. For example: ``` print(part:IsA("Part")) --> true print(part:IsA("BasePart")) --> true print(part:IsA("Instance")) --> true ``` Therefore, the following code sample will return the first [BasePart](/docs/reference/engine/classes/BasePart.md) ancestor, regardless of if it is a [WedgePart](/docs/reference/engine/classes/WedgePart.md), [MeshPart](/docs/reference/engine/classes/MeshPart.md) or [Part](/docs/reference/engine/classes/Part.md). ``` local part = object:FindFirstAncestorWhichIsA("BasePart") ``` See also [Instance:FindFirstAncestor()](/docs/reference/engine/classes/Instance.md). *Security: None · Thread Safety: Safe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `className` | `string` | | The [Object.ClassName](/docs/reference/engine/classes/Object.md) to be looked for. | **Returns:** `Instance?` — The [Instance](/docs/reference/engine/classes/Instance.md) found. ### Method: Instance:FindFirstChild **Signature:** `Instance:FindFirstChild(name: string, recursive?: boolean): Instance?` Returns the first child of the [Instance](/docs/reference/engine/classes/Instance.md) with the given name, or `nil` if no such child exists. If the optional `recursive` argument is `true`, this method searches all descendants rather than only the immediate children of the [Instance](/docs/reference/engine/classes/Instance.md). #### Checking the Existence of an Object `FindFirstChild()` is necessary if you need to verify an object exists before continuing. Attempting to index a child by name using the dot operator throws an error if the child doesn't exist. ```lua local Workspace = game:GetService("Workspace") -- The following line errors if the part doesn't exist in the workspace Workspace.Part.Transparency = 0.5 ``` A better approach is to use `FindFirstChild()` to first check for `Part`, then use an `if` statement to run code that needs it. ```lua local Workspace = game:GetService("Workspace") local part = Workspace:FindFirstChild("Part") if part then part.Transparency = 0.5 end ``` #### Finding a Child Whose Name Matches a Property Sometimes the [Name](/docs/reference/engine/classes/Instance.md) of an object is the same as that of a property of its [Parent](/docs/reference/engine/classes/Instance.md). When using the dot operator, properties take precedence over children if they share a name. In the following example, a [Folder](/docs/reference/engine/classes/Folder.md) called `Color` is added to a [Part](/docs/reference/engine/classes/Part.md), which also has the [Part.Color](/docs/reference/engine/classes/Part.md) property. Notice that `part.Color` refers to the [Color3](/docs/reference/engine/datatypes/Color3.md) property value, not the child [Folder](/docs/reference/engine/classes/Folder.md) instance. A benefit of using [FindFirstChild()](/docs/reference/engine/classes/Instance.md) is that the introduction of new properties does not impose a risk on your code. ```lua local part = Instance.new("Part") local folder = Instance.new("Folder") folder.Name = "Color" folder.Parent = part local c1 = part.Color -- The property local c2 = part:FindFirstChild("Color") -- The child folder ``` #### Performance Notes [FindFirstChild()](/docs/reference/engine/classes/Instance.md) takes about 20% longer than using the dot operator and almost 8 times longer than simply storing a reference to an object. Therefore, you should avoid calling it in performance-dependent code such as in tight loops or functions connected to [RunService.Heartbeat](/docs/reference/engine/classes/RunService.md) and [RunService.PreRender](/docs/reference/engine/classes/RunService.md). Instead, store the result in a variable, or consider using [ChildAdded](/docs/reference/engine/classes/Instance.md) or [WaitForChild()](/docs/reference/engine/classes/Instance.md) to detect when a child of a given name becomes available. *Security: None · Thread Safety: Safe · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name` | `string` | | The [Instance.Name](/docs/reference/engine/classes/Instance.md) to be searched for. | | `recursive` | `boolean` | `false` | Whether or not the search should be conducted recursively. | **Returns:** `Instance?` — The [Instance](/docs/reference/engine/classes/Instance.md) found. **Instance:FindFirstChild** The below would look in Workspace for an object name "Brick". If found, it will change the name of the object to "Foo". ```lua local found = workspace:FindFirstChild("Brick") if found then found.Name = "Foo" end ``` ### Method: Instance:FindFirstChildOfClass **Signature:** `Instance:FindFirstChildOfClass(className: string): Instance?` Returns the first child of the [Instance](/docs/reference/engine/classes/Instance.md) whose [ClassName](/docs/reference/engine/classes/Object.md) is equal to the given `className`. Unlike [Instance:FindFirstChildWhichIsA()](/docs/reference/engine/classes/Instance.md), this method only returns objects whose class matches `className`, ignoring class inheritance. If no matching child is found, this method returns `nil`. *Security: None · Thread Safety: Safe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `className` | `string` | | The [Object.ClassName](/docs/reference/engine/classes/Object.md) to be looked for. | **Returns:** `Instance?` — The [Instance](/docs/reference/engine/classes/Instance.md) found. **Instance:FindFirstChildOfClass** ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local character = player.Character or player.CharacterAdded:Wait() local humanoid while not humanoid do humanoid = character:FindFirstChildOfClass("Humanoid") if not humanoid then character.ChildAdded:Wait() end end ``` ### Method: Instance:FindFirstChildWhichIsA **Signature:** `Instance:FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?` Returns the first child of the [Instance](/docs/reference/engine/classes/Instance.md) for whom [Object:IsA()](/docs/reference/engine/classes/Object.md) returns true for the given className. If no matching child is found, this method returns `nil`. If the optional recursive argument is true, this method searches all descendants rather than only the immediate children of the [Instance](/docs/reference/engine/classes/Instance.md). Unlike [Instance:FindFirstChildOfClass()](/docs/reference/engine/classes/Instance.md), this method uses [Object:IsA()](/docs/reference/engine/classes/Object.md) which respects class inheritance. For example: ```lua print(part:IsA("Part")) --> true print(part:IsA("BasePart")) --> true print(part:IsA("Instance")) --> true ``` Therefore, the following code sample will return the first [BasePart](/docs/reference/engine/classes/BasePart.md) child, regardless of if it is a [WedgePart](/docs/reference/engine/classes/WedgePart.md), [MeshPart](/docs/reference/engine/classes/MeshPart.md) or [Part](/docs/reference/engine/classes/Part.md). ``` local part = object:FindFirstChildWhichIsA("BasePart") ``` Developers looking for a child by name, should use [Instance:FindFirstChild()](/docs/reference/engine/classes/Instance.md) instead. *Security: None · Thread Safety: Safe · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `className` | `string` | | The [Object.ClassName](/docs/reference/engine/classes/Object.md) to be searched for. | | `recursive` | `boolean` | `false` | Whether or not the search should be conducted recursively. | **Returns:** `Instance?` — The [Instance](/docs/reference/engine/classes/Instance.md) found. ### Method: Instance:FindFirstDescendant **Signature:** `Instance:FindFirstDescendant(name: string): Instance?` Returns the first descendant found with the given [Instance.Name](/docs/reference/engine/classes/Instance.md). This method is disabled and cannot be used. To find the first descendant of an instance, consider using the `recursive` parameter on [Instance:FindFirstChild()](/docs/reference/engine/classes/Instance.md) instead. *Security: None · Thread Safety: Safe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name` | `string` | | The [Instance.Name](/docs/reference/engine/classes/Instance.md) to search for. | **Returns:** `Instance?` — The [Instance](/docs/reference/engine/classes/Instance.md) found. ### Method: Instance:GetActor **Signature:** `Instance:GetActor(): Actor?` If the [Instance](/docs/reference/engine/classes/Instance.md) is an [Actor](/docs/reference/engine/classes/Actor.md), the [Actor](/docs/reference/engine/classes/Actor.md) itself is returned. Otherwise, its closest ancestor [Actor](/docs/reference/engine/classes/Actor.md) is returned. If no ancestor is an [Actor](/docs/reference/engine/classes/Actor.md), the result is `nil`. *Security: None · Thread Safety: Safe* **Returns:** `Actor?` — The [Actor](/docs/reference/engine/classes/Actor.md) found. ### Method: Instance:GetAttribute **Signature:** `Instance:GetAttribute(attribute: string): Variant` This method returns the value which has been assigned to the given attribute name. If no attribute has been assigned, `nil` is returned. For example, the following code snippet sets and then gets the value of the instance's `InitialPosition` attribute: ```lua local Workspace = game:GetService("Workspace") local part = Workspace.Part part:SetAttribute("InitialPosition", part.Position) local initialPosition = instance:GetAttribute("InitialPosition") print(initialPosition) ``` #### See Also - [Instance:SetAttribute()](/docs/reference/engine/classes/Instance.md) which sets the attribute with the given name to the given value. - [Instance:GetAttributes()](/docs/reference/engine/classes/Instance.md) which returns a dictionary of key‑value pairs for each of the instance's attributes. *Security: None · Thread Safety: Safe · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `attribute` | `string` | | The name of the attribute being retrieved. | **Returns:** `Variant` — The value which has been assigned to the given attribute name. If no attribute has been assigned, `nil` is returned. ### Method: Instance:GetAttributeChangedSignal **Signature:** `Instance:GetAttributeChangedSignal(attribute: string): RBXScriptSignal` This method returns an event that behaves exactly like the [Changed](/docs/reference/engine/classes/Object.md) event, except that it only fires when the specific given attribute changes; effectively it is similar to [GetPropertyChangedSignal()](/docs/reference/engine/classes/Object.md) but for attributes. It's generally a good idea to use this method instead of a connection to [Changed](/docs/reference/engine/classes/Object.md) with a function that checks the attribute name. Subsequent calls to this method on the same object with the same attribute name return the same event. The following code example returns a signal that fires the function `attributeChanged()` when the part's `InitialPosition` attribute changes: ```lua local Workspace = game:GetService("Workspace") local part = Workspace.Part part:SetAttribute("InitialPosition", part.Position) local function attributeChanged() print("Attribute changed") end part:GetAttributeChangedSignal("InitialPosition"):Connect(attributeChanged) ``` See also [Instance.AttributeChanged](/docs/reference/engine/classes/Instance.md) which fires whenever any attribute is changed on the instance. *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `attribute` | `string` | | The name of the specified attribute for which the change signal is being returned. | **Returns:** `RBXScriptSignal` — An event that fires when the given attribute changes. ### Method: Instance:GetAttributes **Signature:** `Instance:GetAttributes(): Dictionary` This method returns a dictionary of key‑value pairs for each attribute where the key is the attribute's name and the value is a non‑`nil` value. For example, the following code snippet outputs an instance's attributes and values: ```lua local Workspace = game:GetService("Workspace") local part = Workspace.Part part:SetAttribute("InitialPosition", part.Position) part:SetAttribute("CanUse", true) for name, value in part:GetAttributes() do print(name .. " = " .. value) end ``` See also [Instance:GetAttribute()](/docs/reference/engine/classes/Instance.md) which returns the value that has been assigned to the given attribute name. *Security: None · Thread Safety: Safe · Simulation Access: true* **Returns:** `Dictionary` — A dictionary of string → variant pairs for each attribute where the string is the name of the attribute and the variant is a non-nil value. ### Method: Instance:GetChildren **Signature:** `Instance:GetChildren(): Instances` Returns an array (a numerically indexed table) containing all of the instance's direct children, or every [Instance](/docs/reference/engine/classes/Instance.md) whose [Parent](/docs/reference/engine/classes/Instance.md) is equal to the object. The array can be iterated upon using either a numeric or generic `for` loop: ```lua local Workspace = game:GetService("Workspace") -- Numeric loop example local children = Workspace:GetChildren() for i = 1, #children do local child = children[i] print(child.Name .. " is child number " .. i) end ``` ```lua local Workspace = game:GetService("Workspace") -- Generic loop example local children = Workspace:GetChildren() for i, child in children do print(child.Name .. " is child number " .. i) end ``` Note that the returned array is **not** sorted in any particular order, so manual sorting (for example using [table.sort()](/docs/reference/engine/globals/table.md)) is advised for sorting the children returned by this method. See also the [GetDescendants()](/docs/reference/engine/classes/Instance.md) method. *Security: None · Thread Safety: Safe · Simulation Access: true* **Returns:** `Instances` — An array containing the instance's children. **Instance:GetChildren** The below would print the name of all objects currently in Workspace when ran. ```lua local children = workspace:GetChildren() for i = 1, #children do print(i, children[i].Name) end ``` ### Method: Instance:GetDebugId **Signature:** `Instance:GetDebugId(scopeLength?: int): string` Returns a coded string of the debug ID used internally by Roblox. Note that: - This item is protected. Attempting to use it in a [Script](/docs/reference/engine/classes/Script.md) or [LocalScript](/docs/reference/engine/classes/LocalScript.md) will cause an error. - A debug ID is an ID used in debugging processes. It allows a debugger to read each instruction before an application processes it. All objects in Roblox act like processes and each run instructions (or 'code') that can be debugged if needed. - This can be helpful for plugins which need to distinguish similar objects from one-another (such as objects that share the same name). *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `scopeLength` | `int` | `4` | The scope length. | **Returns:** `string` — The Debug ID string. **Instance:GetDebugId** ```lua print(workspace:GetDebugId()) --> 39FA_12 print(workspace:GetDebugId(10)) --> 39FA2FEF4D_12 print(workspace:GetDebugId(math.huge)) --> 12 ``` ### Method: Instance:GetDescendants **Signature:** `Instance:GetDescendants(): Instances` This object method returns an array that contains all of the descendants of that object. Unlike [Instance:GetChildren()](/docs/reference/engine/classes/Instance.md), which only returns the immediate children of an object, this method finds every child of the object, every child of those children, and so on. Note that the returned array is **not** sorted in any particular order, so manual sorting (for example using [table.sort()](/docs/reference/engine/globals/table.md)) is advised for sorting the descendants returned by this method. Also consdier the [QueryDescendants()](/docs/reference/engine/classes/Instance.md) method to query descendants by a combination of selectors and combinators. *Security: None · Thread Safety: Safe · Simulation Access: true* **Returns:** `Instances` — An array containing the instance's descendants. **Instance:GetDescendants** GetDescendants is often used to do something to all the descendants that are a particular type of object. The code in this example uses GetDescendants and [Instance:IsA()](/docs/reference/engine/classes/Instance.md) to find all of the parts in the workspace and turns them green. ```lua local descendants = workspace:GetDescendants() -- Loop through all of the descendants of the Workspace. If a -- BasePart is found, the code changes that parts color to green for _, descendant in pairs(descendants) do if descendant:IsA("BasePart") then descendant.BrickColor = BrickColor.Green() end end ``` ### Method: Instance:GetFullName **Signature:** `Instance:GetFullName(): string` Returns a string describing the instance's ancestry. The string is a concatenation of the [Name](/docs/reference/engine/classes/Instance.md) of the object and its ancestors, separated by periods. The [DataModel](/docs/reference/engine/classes/DataModel.md) (`game`) is not considered. For example, a [Part](/docs/reference/engine/classes/Part.md) in the [Workspace](/docs/reference/engine/classes/Workspace.md) may return [Workspace.Part](/docs/reference/engine/classes/Workspace.md). When called on an [Instance](/docs/reference/engine/classes/Instance.md) that is not a descendant of the [DataModel](/docs/reference/engine/classes/DataModel.md), this method considers all ancestors up to and including the topmost one without a [Parent](/docs/reference/engine/classes/Instance.md). This method is useful for logging and debugging. You shouldn't attempt to parse the returned string for any useful operation; this method does not escape periods (or any other symbol) in object names. In other words, although its output often appears to be a valid Luau identifier, it is not guaranteed. *Security: None · Thread Safety: Safe · Simulation Access: true* **Returns:** `string` — The full name of the [Instance](/docs/reference/engine/classes/Instance.md). **Instance:GetFullName** This code sample demonstrates the behavior of [Instance:GetFullName()](/docs/reference/engine/classes/Instance.md). It shows how the function behaves when called on an object not in the `DataModel` hierarchy, and it also shows how the return value does not escape special characters. ```lua -- Create a simple hierarchy local model = Instance.new("Model") local part = Instance.new("Part") part.Parent = model local fire = Instance.new("Fire") fire.Parent = part print(fire:GetFullName()) --> Model.Part.Fire model.Parent = workspace print(fire:GetFullName()) --> Workspace.Model.Part.Fire part.Name = "Hello, world" print(fire:GetFullName()) --> Workspace.Model.Hello, world.Fire ``` **Expected output:** Model.Part.Fire Workspace.Model.Part.Fire Workspace.Model.Hello, world.Fire **Instance:GetFullName Lua Implementation** This code sample re-implements the [Instance:GetFullName()](/docs/reference/engine/classes/Instance.md) function in Lua. ```lua local function getFullName(object) local result = object.Name object = object.Parent while object and object ~= game do -- Prepend parent name result = object.Name .. "." .. result -- Go up the hierarchy object = object.Parent end return result end print(getFullName(workspace.Camera)) --> Workspace.Camera ``` **Expected output:** Workspace.Camera ### Method: Instance:GetStyled **Signature:** `Instance:GetStyled(name: string, selector: string?): Variant` This method returns the styled or explicitly modified value of the specified property, or else the default property value if it hasn't been styled/modified. This differs slightly from accessing the property value directly, such as `[GuiObject].Rotation`, which returns the default or modified value of the property. ```lua local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") local player = Players.LocalPlayer local playerGui = player.PlayerGui local HUDContainer = playerGui:WaitForChild("HUDContainer") local coreSheet = ReplicatedStorage:FindFirstChild("CoreSheet") local rule = coreSheet:FindFirstChildWhichIsA("StyleRule") rule.Selector = "TextButton" -- Reference to a button local button = HUDContainer:FindFirstChildWhichIsA("TextButton") print(button:GetStyled("Rotation")) --> 0 (default value) print(button.Rotation) --> 0 (default value) -- Apply rotation through style rule property rule:SetProperty("Rotation", 30) print(button:GetStyled("Rotation")) --> 30 (styled value based on rule) print(button.Rotation) --> 0 (default value) -- Explicitly modify/override styled property button.Rotation = 45 print(button:GetStyled("Rotation")) --> 45 (modified value) print(button.Rotation) --> 45 (modified value) ``` *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name` | `string` | | Name of the property to query. | | `selector` | `string?` | | Optional selector for the pseudo instance you are targeting on the instance. | **Returns:** `Variant` — The styled or explicitly modified value of the specified property, or else the default property value if it hasn't been styled/modified. ### Method: Instance:GetStyledPropertyChangedSignal **Signature:** `Instance:GetStyledPropertyChangedSignal(property: string): RBXScriptSignal` This method returns an event that behaves exactly like the [StyledPropertiesChanged](/docs/reference/engine/classes/Instance.md) event, except that it only fires when the given style property changes. It's generally a good idea to use this method instead of a connection to [StyledPropertiesChanged](/docs/reference/engine/classes/Instance.md) with a function that checks the property name. Subsequent calls to this method on the same object with the same property name return the same event. Note that this event will not pass any arguments to a connected function, so the value of the changed property must be read directly within a script. ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local playerGui = player.PlayerGui local HUDContainer = playerGui:WaitForChild("HUDContainer") local meterBar = HUDContainer.MeterBar local function stylePropertyChanged() print("Style property changed!") end meterBar:GetStyledPropertyChangedSignal("AnchorPoint"):Connect(stylePropertyChanged) ``` *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `property` | `string` | | Name of the style property for which to listen for changes. | **Returns:** `RBXScriptSignal` — Event that fires when the given style property changes. ### Method: Instance:GetTags **Signature:** `Instance:GetTags(): Array` This method returns an array of the tags applied to the given instance, as strings. You can add tags either in Studio in the [Properties](/docs/en-us/studio/properties.md) window or at runtime with [AddTag()](/docs/reference/engine/classes/Instance.md). This method is useful when you want to do something with multiple tags on an instance at once. However, it is inefficient to use this method to check for the existence of a single tag; instead, use [HasTag()](/docs/reference/engine/classes/Instance.md) to check for a specific tag. *Security: None · Thread Safety: Safe · Simulation Access: true* **Returns:** `Array` ### Method: Instance:HasTag **Signature:** `Instance:HasTag(tag: string): boolean` This method returns `true` if the provided tag has been added to the object. You can add tags either in Studio in the [Properties](/docs/en-us/studio/properties.md) window or at runtime with [AddTag()](/docs/reference/engine/classes/Instance.md). *Security: None · Thread Safety: Safe · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `tag` | `string` | | | **Returns:** `boolean` ### Method: Instance:IsAncestorOf **Signature:** `Instance:IsAncestorOf(descendant: Instance): boolean` Returns true if an [Instance](/docs/reference/engine/classes/Instance.md) is an ancestor of the given descendant. An [Instance](/docs/reference/engine/classes/Instance.md) is considered the ancestor of an object if the object's [Instance.Parent](/docs/reference/engine/classes/Instance.md) or one of it's parent's [Instance.Parent](/docs/reference/engine/classes/Instance.md) is set to the [Instance](/docs/reference/engine/classes/Instance.md). See also, [Instance:IsDescendantOf()](/docs/reference/engine/classes/Instance.md). *Security: None · Thread Safety: Safe · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `descendant` | `Instance` | | The descendant [Instance](/docs/reference/engine/classes/Instance.md). | **Returns:** `boolean` — True if the [Instance](/docs/reference/engine/classes/Instance.md) is an ancestor of the given descendant. **Instance:IsAncestorOf()** Demonstrates determining if one instance is the ancestor of another using [Instance:IsAncestorOf()](/docs/reference/engine/classes/Instance.md) Workspace and SpawnLocation are ancestors of the SpawnLocation's decal. Workspace is an ancestor of SpawnLocation. SpawnLocation and its decal are descendants of Workspace, not ancenstors. Decal is a descendant to SpawnLocation, not an ancestor. ```lua local Workspace = game:GetService("Workspace") local spawnLocation = Workspace.SpawnLocation local decal = spawnLocation.Decal -- These statements are true print(Workspace:IsAncestorOf(spawnLocation)) print(Workspace:IsAncestorOf(decal)) print(spawnLocation:IsAncestorOf(decal)) -- These statements are false print(spawnLocation:IsAncestorOf(Workspace)) print(decal:IsAncestorOf(Workspace)) print(decal:IsAncestorOf(spawnLocation)) ``` ### Method: Instance:IsDescendantOf **Signature:** `Instance:IsDescendantOf(ancestor: Instance): boolean` Returns `true` if an [Instance](/docs/reference/engine/classes/Instance.md) is a descendant of the given ancestor. Note that `IsDescendantOf()` cannot be used with a parameter of `nil` to check if an object has been removed. See also [Instance:IsAncestorOf()](/docs/reference/engine/classes/Instance.md). *Security: None · Thread Safety: Safe · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `ancestor` | `Instance` | | The ancestor [Instance](/docs/reference/engine/classes/Instance.md). | **Returns:** `boolean` — True if the [Instance](/docs/reference/engine/classes/Instance.md) is a descendant of the given ancestor. **Instance:IsDescendantOf** ```lua local part = Instance.new("Part") print(part:IsDescendantOf(game)) --> false part.Parent = workspace print(part:IsDescendantOf(game)) --> true part.Parent = game print(part:IsDescendantOf(game)) --> true ``` ### Method: Instance:IsPropertyModified **Signature:** `Instance:IsPropertyModified(property: string): boolean` This method indicates whether a specific property has been modified from the code‑instantiated default. If called on an instance newly created via code ([Instance.new()](/docs/reference/engine/datatypes/Instance.md)), this method will return `false`. If called on an object newly created by Studio workflows such as [Explorer](/docs/en-us/studio/explorer.md) window insertion, the result may differ. For example, querying the [BackgroundColor3](/docs/reference/engine/classes/TextLabel.md) property of a [TextLabel](/docs/reference/engine/classes/TextLabel.md) inserted via the [Explorer](/docs/en-us/studio/explorer.md) will return `true`, but querying the same property of a [TextLabel](/docs/reference/engine/classes/TextLabel.md) created through [Instance.new()](/docs/reference/engine/datatypes/Instance.md) will return `false`, assuming no other changes were made to its [BackgroundColor3](/docs/reference/engine/classes/TextLabel.md). Note that this method only returns useful results for services and user‑creatable instances. Return values for system‑populated properties of instances like [PackageLink](/docs/reference/engine/classes/PackageLink.md) is undefined behavior. Also note that if this method returns `true`, styling will **not** affect the property because explicitly modifying a property takes precedence over styling it. To reset a property to its code‑instantiated default and allow styling on it, use [ResetPropertyToDefault()](/docs/reference/engine/classes/Instance.md). *Security: None · Thread Safety: Unsafe · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `property` | `string` | | Name of the property to query. | **Returns:** `boolean` — Boolean indicating whether the property is modified from its code‑instantiated default. ### Method: Instance:QueryDescendants **Signature:** `Instance:QueryDescendants(selector: string): Instances` This object method returns an array that contains all descendants of the object that match the provided `selector` string. `selector` can include multiple selectors and combinators to match characteristics such as the class name, instance name, and hierarchy relationships, as well as specific values of instance properties and attributes. The `selector` grammar is similar to the one used in [StyleRule](/docs/reference/engine/classes/StyleRule.md), with only a few differences regarding which parts of the grammar are supported or not. #### Selectors - `ClassName` — Matches instances of the specified class in a [Object:IsA()](/docs/reference/engine/classes/Object.md) relationship. - `.Tag` — Matches instances tagged with a [CollectionService](/docs/reference/engine/classes/CollectionService.md) tag. - `#Name` — Matches instances of a specific [Instance.Name](/docs/reference/engine/classes/Instance.md). - `[property = value]` — Matches instances which have the specified property and that property has the specified value. Boolean, number, and string values are supported. Letters, numbers, `_`, and `-` can be used without putting the value in quotation marks. - `[$attribute]` — Matches instances which have the specified [attribute](/docs/en-us/studio/properties.md#instance-attributes) set. - `[$attribute = value]` — Matches instances which have the specified attribute **and** that attribute has the specified value. Same limitations apply as for property selectors. If you want to check for the absence of an attribute, do not query it as `nil`; instead use the `:not` pseudo‑class as in `:not([$attribute])`. Selectors can also be combined. For example, `Model.Apple[$Variety = Fuji][$Flavor = 4]` will match instances which are a [Model](/docs/reference/engine/classes/Model.md) tagged as `Apple` and which have a `Variety` attribute equal to `Fuji` as well as a `Flavor` attribute equal to `4`. ```lua local Workspace = game:GetService("Workspace") -- Get all MeshPart descendants print(Workspace:QueryDescendants("MeshPart")) -- Get all descendants tagged as "Fruit" print(Workspace:QueryDescendants(".Fruit")) -- Get all descendants with the name "RedTree" print(Workspace:QueryDescendants("#RedTree")) -- Get all descendants with CanCollide property set to false print(Workspace:QueryDescendants("[CanCollide = false]")) -- Get all descendants with a "FuelCapacity" attribute assigned print(Workspace:QueryDescendants("[$FuelCapacity]")) -- Get all descendants with a "FuelCapacity" attribute set to 75 print(Workspace:QueryDescendants("[$FuelCapacity = 75]")) ``` #### Combinators Combinators let you mix basic selectors to match deeper hierarchy relationships. - `>` — Matches instances that are **direct children** of the previous filter matches. - `>>` — Matches instances that are **descendants** of the previous filter matches. This is the default first filter, meaning that `SpotLight.Red` is the same as `>> SpotLight.Red`. - `,` — Specifies a list of multiple independent selectors for the descendant query. Only elements matching the filter are placed in the returned array; `nil` will not be included for non‑matches in a query list. ```lua local Workspace = game:GetService("Workspace") -- Get direct children of any Model that are tagged as "SwordPart" print(Workspace:QueryDescendants("Model > .SwordPart")) -- Get direct children which are a Part and are tagged as "Apple" print(Workspace:QueryDescendants("> Part.Apple")) -- Get descendants of any Model with an attribute "OnFire" set to true print(Workspace:QueryDescendants("Model >> [$OnFire = true]")) -- Get MeshPart descendants tagged as "SwordPart" OR with an attribute "OnFire" set to true print(Workspace:QueryDescendants("MeshPart.SwordPart, MeshPart[$OnFire = true]")) ``` #### Pseudo-class selectors - `:not(complex-selector-list)` — Allows selecting instances which do **not** match any of the selectors inside, effectively performing a negation of the selectors. Since the expression inside is a list of complex selectors, it's possible to filter based on multiple conditions. - `:has(relative-selector-list)` — Allows selecting instances based on which instances they contain inside. The selector list is evaluated relative to each instance. ```lua local Workspace = game:GetService("Workspace") -- Get all descendants which are NOT of class SpotLight print(Workspace:QueryDescendants(":not(SpotLight)")) -- Get all descendants which are NOT of class SpotLight OR PointLight print(Workspace:QueryDescendants(":not(SpotLight, PointLight)")) -- Get all descendants which do NOT have an attribute "OnFire" print(Workspace:QueryDescendants(":not([$OnFire])")) -- Get all descendants EXCEPT children of a Model tagged as "SwordPart" print(Workspace:QueryDescendants(":not(Model > .SwordPart)")) -- Get descendants which contain a Tool instance as their own descendant print(Workspace:QueryDescendants(":has(Tool)")) -- Get MeshPart descendants which contain a direct child tagged as "SwordPart" print(Workspace:QueryDescendants("MeshPart:has(> .SwordPart)")) -- Get MeshPart descendants with direct children NOT of SurfaceAppearance or Texture class print(Workspace:QueryDescendants("MeshPart:has(> :not(SurfaceAppearance, Texture))")) ``` *Security: None · Thread Safety: Unsafe · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `selector` | `string` | | Selector string used to filter elements. | **Returns:** `Instances` — An array of instances (empty if nothing matched the selector). ### Method: Instance:RemoveTag **Signature:** `Instance:RemoveTag(tag: string): ()` This method removes a tag from an instance. It will not throw an error if the object does not have the tag. Successfully removing a tag will fire a signal created by [CollectionService:GetInstanceRemovedSignal()](/docs/reference/engine/classes/CollectionService.md) with the given tag. Note that when tagging an instance, it's common that some resources are used to give the tag its functionality, for example event connections or tables. To prevent memory leaks, it's a good idea to clean these up (disconnect, set to `nil`, etc.) when no longer needed for a tag. *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `tag` | `string` | | | **Returns:** `()` ### Method: Instance:ResetPropertyToDefault **Signature:** `Instance:ResetPropertyToDefault(property: string): ()` Resets a property to its default value. For example, calling `ResetPropertyToDefault("Rotation")` on a [TextLabel](/docs/reference/engine/classes/TextLabel.md) is equivalent to setting its [Rotation](/docs/reference/engine/classes/TextLabel.md) to `0` (the property's default value). This method can be used to ensure styling will override this property's default value. *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `property` | `string` | | Name of the property to reset. | **Returns:** `()` ### Method: Instance:SetAttribute **Signature:** `Instance:SetAttribute(attribute: string, value: Variant): ()` This method sets the attribute with the given name to the given value. If the value given is `nil`, the attribute will be removed, since `nil` is returned by default. For example, the following code snippet sets the instance's `InitialPosition` attribute to [Vector3.new(0, 10, 0)](/docs/reference/engine/datatypes/Vector3.md): ```lua local Workspace = game:GetService("Workspace") local part = Workspace.Part part:SetAttribute("InitialPosition", Vector3.new(0, 10, 0)) ``` #### Limitations Naming requirements and restrictions: - Names must only use alphanumeric characters. - You may also include periods, hyphens, slashes, and underscores. - No spaces or unique symbols (such as @, ~, !) are allowed. - Strings must be 100 characters or less. - Names are not allowed to start with **RBX** unless the caller is a Roblox core script (reserved for Roblox). When attempting to set an attribute to an unsupported type, an error will be thrown. #### See Also - [Instance:GetAttribute()](/docs/reference/engine/classes/Instance.md) which returns the value that has been assigned to the given attribute name. - [Instance:GetAttributes()](/docs/reference/engine/classes/Instance.md) which returns a dictionary of key‑value pairs for each of the instance's attributes. *Security: None · Thread Safety: Unsafe · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `attribute` | `string` | | The name of the attribute being set. | | `value` | `Variant` | | The value to set the specified attribute to. | **Returns:** `()` ### Method: Instance:WaitForChild **Signature:** `Instance:WaitForChild(childName: string, timeOut: double): Instance` Returns the child of the [Instance](/docs/reference/engine/classes/Instance.md) with the given name. If the child does not exist, it will yield the current thread until it does. If the `timeOut` parameter is specified, this method will time out after the specified number of seconds and return `nil`. #### Primary Usage [WaitForChild()](/docs/reference/engine/classes/Instance.md) is extremely important when working on code run by the client in a [LocalScript](/docs/reference/engine/classes/LocalScript.md). The Roblox engine does not guarantee the time or order in which objects are replicated from the server to the client. Additionally, if an experience has [Workspace.StreamingEnabled](/docs/reference/engine/classes/Workspace.md) set to true, [BaseParts](/docs/reference/engine/classes/BasePart.md) that are far away from the player's character may not be streamed to the client, potentially causing scripts to break when indexing objects that do not yet exist on the client. #### Notes - This method does not yield if a child with the given name exists when the call is made. - [Instance:FindFirstChild()](/docs/reference/engine/classes/Instance.md) is a more efficient alternative to [WaitForChild()](/docs/reference/engine/classes/Instance.md) for objects that are assumed to exist. - If a call to this method exceeds 5 seconds without returning, and no `timeOut` parameter has been specified, a warning will be printed to the output that the thread may yield indefinitely. *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `childName` | `string` | | The [Instance.Name](/docs/reference/engine/classes/Instance.md) to be looked for. | | `timeOut` | `double` | | An optional time out parameter. | **Returns:** `Instance` — The [Instance](/docs/reference/engine/classes/Instance.md) found. **Instance:WaitForChild** The following code waits for an instance named "Part" to be added to Workspace. ```lua local part = workspace:WaitForChild("Part") print(part.Name .. " has been added to the Workspace") ``` ### Method: Instance:children **Signature:** `Instance:children(): Instances` > **Deprecated:** This item has been superseded by [Instance:GetChildren()](/docs/reference/engine/classes/Instance.md) which should be used in all new work. The children function returns an array of the object's children. *Security: None · Thread Safety: Unsafe* **Returns:** `Instances` — Array of child objects/instances. ### Method: Instance:clone **Signature:** `Instance:clone(): Instance` > **Deprecated:** This deprecated function is a variant of [Instance:Clone()](/docs/reference/engine/classes/Instance.md) which should be used instead. *Security: None · Thread Safety: Unsafe · Capabilities: CreateInstances · Simulation Access: true* **Returns:** `Instance` ### Method: Instance:destroy **Signature:** `Instance:destroy(): ()` > **Deprecated:** This deprecated function is a variant of [Instance:Destroy()](/docs/reference/engine/classes/Instance.md) which should be used instead. *Security: None · Thread Safety: Unsafe* **Returns:** `()` ### Method: Instance:findFirstChild **Signature:** `Instance:findFirstChild(name: string, recursive?: boolean): Instance` > **Deprecated:** This deprecated function is a variant of [Instance:FindFirstChild()](/docs/reference/engine/classes/Instance.md) which should be used instead. *Security: None · Thread Safety: Unsafe · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name` | `string` | | | | `recursive` | `boolean` | `false` | | **Returns:** `Instance` ### Method: Instance:getChildren **Signature:** `Instance:getChildren(): Instances` > **Deprecated:** This deprecated function is a variant of [Instance:GetChildren()](/docs/reference/engine/classes/Instance.md) which should be used instead. *Security: None · Thread Safety: Unsafe* **Returns:** `Instances` ### Method: Instance:isDescendantOf **Signature:** `Instance:isDescendantOf(ancestor: Instance): boolean` > **Deprecated:** This deprecated function is a variant of [Instance:IsDescendantOf()](/docs/reference/engine/classes/Instance.md) which should be used instead. *Security: None · Thread Safety: Unsafe · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `ancestor` | `Instance` | | | **Returns:** `boolean` ### Method: Instance:Remove **Signature:** `Instance:Remove(): ()` > **Deprecated:** This item is deprecated in favor of [Instance:Destroy()](/docs/reference/engine/classes/Instance.md) and [Instance:ClearAllChildren()](/docs/reference/engine/classes/Instance.md). If you must remove an object from the game, and wish to use the object later, set its `Parent` property to `nil` instead of using this method. This method sets the object's [Instance.Parent](/docs/reference/engine/classes/Instance.md) to `nil`, and does the same for all its descendants. If the object is referenced before being removed, it is possible to retrieve the object at a later point. *Security: None · Thread Safety: Unsafe* **Returns:** `()` **Instance:Remove** The following code demonstrates how a part can be re-added to the DataModel after being removed: ```lua local part = Instance.new("Part") part.Parent = workspace print(part.Parent) --> Workspace part:Remove() print(part.Parent) --> nil part.Parent = workspace print(part.Parent) --> Workspace ``` ### Method: Instance:remove **Signature:** `Instance:remove(): ()` > **Deprecated:** This deprecated function is a variant of [Instance:Remove()](/docs/reference/engine/classes/Instance.md) which has also been deprecated. Neither function should be used in new work. *Security: None · Thread Safety: Unsafe* **Returns:** `()` ## Events ### Event: Instance.AncestryChanged **Signature:** `Instance.AncestryChanged(child: Instance, parent: Instance)` Fires when this instance is reparented or when any of its ancestors is reparented. You can use this event to track the deletion of an instance in Studio, such as manual deletion in the Explorer or through a plugin. If you need to detect when an instance is destroyed using [Instance:Destroy()](/docs/reference/engine/classes/Instance.md), use the [Instance.Destroying](/docs/reference/engine/classes/Instance.md) event instead. *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `child` | `Instance` | The instance whose [Parent](/docs/reference/engine/classes/Instance.md) property changed, which can be this instance or a more distant ancestor, **not** necessarily the instance the handler is connected to. | | `parent` | `Instance` | The new parent of the `child` parameter. | **Instance.AncestryChanged** Demonstrates detecting changes to an instance's ancestry by connecting to the [Instance.AncestryChanged](/docs/reference/engine/classes/Instance.md) event. The ChangingPart's Parent is set to different values overtime. The parent of the part is the part's ancestor, so the [Instance.AncestryChanged](/docs/reference/engine/classes/Instance.md) event will fire whenever it changes. ```lua local Workspace = game:GetService("Workspace") local redPart = script.Parent.RedPart local bluePart = script.Parent.BluePart local changingPart = script.Parent.ChangingPart -- Change the color of changingPart based on it's Parent local function onAncestryChanged(part: Part, parent: Instance) if parent == redPart then changingPart.Color = Color3.new(1, 0, 0) elseif parent == bluePart then changingPart.Color = Color3.new(0, 0, 1) else changingPart.Color = Color3.new(1, 1, 1) end print(`{part.Name} is now parented to {parent.Name}`) end changingPart.AncestryChanged:Connect(onAncestryChanged) -- Set changingPart's Parent property to different instances over time while true do task.wait(2) changingPart.Parent = redPart task.wait(2) changingPart.Parent = bluePart task.wait(2) changingPart.Parent = Workspace end ``` ### Event: Instance.AttributeChanged **Signature:** `Instance.AttributeChanged(attribute: string)` This event fires whenever any attribute is changed on the instance, including when an attribute is set to `nil`. The name of the changed attribute is passed to the connected function. For example, the following code snippet connects the `attributeChanged()` function to fire whenever one of the part's attributes changes: ```lua local Workspace = game:GetService("Workspace") local part = Workspace.Part local function attributeChanged(attributeName) print(attributeName, "changed") end part.AttributeChanged:Connect(attributeChanged) ``` See also [Instance:GetAttributeChangedSignal()](/docs/reference/engine/classes/Instance.md) which returns an event that fires when a specific given attribute changes. *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `attribute` | `string` | The name of the attribute that has been changed. | ### Event: Instance.ChildAdded **Signature:** `Instance.ChildAdded(child: Instance)` Fires after an object is parented to this [Instance](/docs/reference/engine/classes/Instance.md). Note, when using this method on a client to detect objects created by the server it is necessary to use [Instance:WaitForChild()](/docs/reference/engine/classes/Instance.md) when indexing these object's descendants. This is because the object and its descendants are not guaranteed to replicate from the server to the client simultaneously. For example: ``` local Workspace = game:GetService("Workspace") Workspace.ChildAdded:Connect(function(child) -- Use WaitForChild() since descendants may not have replicated yet local head = child:WaitForChild("Head") end) ``` Note, this event only operates for immediate children of the [Instance](/docs/reference/engine/classes/Instance.md). For an event that captures all descendants, use [Instance.DescendantAdded](/docs/reference/engine/classes/Instance.md). See also [Instance.ChildRemoved](/docs/reference/engine/classes/Instance.md). *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `child` | `Instance` | The [Instance](/docs/reference/engine/classes/Instance.md) that has been added. | **Instance.ChildAdded** This snippet prints the names of objects as they are added to the Workspace: ```lua local function onChildAdded(instance) print(instance.Name .. " added to the workspace") end workspace.ChildAdded:Connect(onChildAdded) local part = Instance.new("Part") part.Parent = workspace --> Part added to the Workspace ``` ### Event: Instance.ChildRemoved **Signature:** `Instance.ChildRemoved(child: Instance)` Fires after a child is removed from this [Instance](/docs/reference/engine/classes/Instance.md). Removed refers to when an object's parent is changed from this [Instance](/docs/reference/engine/classes/Instance.md) to something other than this [Instance](/docs/reference/engine/classes/Instance.md). Note, this event will also fire when a child is destroyed (using [Instance:Destroy()](/docs/reference/engine/classes/Instance.md)) as the destroy function sets an object's parent to `nil`. This event only operates for immediate children of the [Instance](/docs/reference/engine/classes/Instance.md). For an event that captures all descendants, use [Instance.DescendantRemoving](/docs/reference/engine/classes/Instance.md). See also [Instance.ChildAdded](/docs/reference/engine/classes/Instance.md). *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `child` | `Instance` | The [Instance](/docs/reference/engine/classes/Instance.md) that has been removed. | **Instance.ChildRemoved** This snippet prints the names of objects as they are removed from the Workspace: ```lua local function onChildRemoved(instance) print(instance.Name .. " removed from the workspace") end workspace.ChildRemoved:Connect(onChildRemoved) local part = Instance.new("Part") part.Parent = workspace task.wait(2) part:Destroy() ``` ### Event: Instance.DescendantAdded **Signature:** `Instance.DescendantAdded(descendant: Instance)` This event fires after a descendant is added to the [Instance](/docs/reference/engine/classes/Instance.md). As it fires for every descendant, parenting an object to the [Instance](/docs/reference/engine/classes/Instance.md) will fire the event for this object and all of its descendants individually. If you're only concerned with the **direct children** of the [Instance](/docs/reference/engine/classes/Instance.md), use [Instance.ChildAdded](/docs/reference/engine/classes/Instance.md) instead. See also [Instance.DescendantRemoving](/docs/reference/engine/classes/Instance.md). *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `descendant` | `Instance` | The [Instance](/docs/reference/engine/classes/Instance.md) that has been added. | **Instance.DescendantAdded** This following example will print the name of any object that is added to the Workspace: ```lua local function onDescendantAdded(descendant) print(descendant) end workspace.DescendantAdded:Connect(onDescendantAdded) local part = Instance.new("Part") part.Parent = workspace ``` ### Event: Instance.DescendantRemoving **Signature:** `Instance.DescendantRemoving(descendant: Instance)` This event fires **immediately before** the parent [Instance](/docs/reference/engine/classes/Instance.md) changes such that a **descendant** instance will no longer be a descendant. [Destroy()](/docs/reference/engine/classes/Instance.md) changes an instance's [Parent](/docs/reference/engine/classes/Instance.md) to `nil`, so calling that method on a descendant of the parent will cause this event to fire. Since this event fires **before** the descendant's removal, the parent of the descendant will be unchanged at the time of this event firing. If the descendant is also a **direct child** of the parent, this event will fire before [Instance.ChildRemoved](/docs/reference/engine/classes/Instance.md). If a descendant has children, this event fires with the descendant first, followed by its descendants. #### Warning This event fires with the descendant object that is being removed. Attempting to set the [Parent](/docs/reference/engine/classes/Instance.md) of the descendant to something else will fail. Below is an example that demonstrates this: ``` local Workspace = game:GetService("Workspace") Workspace.DescendantRemoving:Connect(function(descendant) -- Do not manipulate the parent of the descendant in this function! -- This event fires BECAUSE the parent was manipulated, and the change hasn't happened yet -- Therefore, it is problematic to change the parent like this: descendant.Parent = game end) local part = Instance.new("Part") part.Parent = Workspace part.Parent = nil ``` See also [DescendantAdded](/docs/reference/engine/classes/Instance.md). *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `descendant` | `Instance` | The [Instance](/docs/reference/engine/classes/Instance.md) that is being removed. | **Instance.DescendantRemoving** The following example prints the name of any descendant as it is being removed from the Workspace: ```lua workspace.DescendantRemoving:Connect(function(descendant) print(descendant.Name .. " is currently parented to " .. tostring(descendant.Parent)) end) local part = Instance.new("Part") part.Parent = workspace part.Parent = nil --> Part is currently parented to Workspace print(part.Parent) --> nil ``` ### Event: Instance.Destroying **Signature:** `Instance.Destroying()` The [Instance](/docs/reference/engine/classes/Instance.md) will never be deleted from memory while a connected function is still using it. However, if the function yields at any point, the [Instance](/docs/reference/engine/classes/Instance.md) and its descendants will be parented to `nil`. If the [Workspace.SignalBehavior](/docs/reference/engine/classes/Workspace.md) property is set to [SignalBehavior.Immediate](/docs/reference/engine/enums/SignalBehavior.md), this event fires immediately before the [Instance](/docs/reference/engine/classes/Instance.md) or one of its ancestors is destroyed with [Instance:Destroy()](/docs/reference/engine/classes/Instance.md). If the [Workspace.SignalBehavior](/docs/reference/engine/classes/Workspace.md) property is set to [SignalBehavior.Deferred](/docs/reference/engine/enums/SignalBehavior.md), this event fires at the next resumption point, which will be after the [Instance](/docs/reference/engine/classes/Instance.md) or one of its ancestors is destroyed with [Instance:Destroy()](/docs/reference/engine/classes/Instance.md). With [Deferred](/docs/reference/engine/enums/SignalBehavior.md) behavior, connecting a script to its own [Instance.Destroying](/docs/reference/engine/classes/Instance.md) event is problematic, as the script will be destroyed before the callback can be called (meaning it will not execute). When deleting an [Instance](/docs/reference/engine/classes/Instance.md) in Studio, such as manually deleting through the [Explorer](/docs/en-us/studio/explorer.md) or through a plugin, the [Instance](/docs/reference/engine/classes/Instance.md) isn't destroyed. Instead, the parent is set to `nil` which you can track with [Instance.AncestryChanged](/docs/reference/engine/classes/Instance.md). *Security: None* **Using the Destroying Event (Immediate signals)** This sample demonstrates how, when using Immediate signal behavior, an Instance being destroyed remains in place until the connected function yields. ```lua local part = Instance.new("Part", workspace) local function onPartDestroying() print("Before yielding:", part:GetFullName(), #part:GetChildren()) task.wait() print("After yielding:", part:GetFullName(), #part:GetChildren()) end part.Destroying:Connect(onPartDestroying) part:Destroy() ``` **Expected output:** Before yielding: Workspace.Part 1 After yielding: Part 0 **Using the Destroying Event (Deferred signals)** This sample demonstrates how, when using Deferred signal behavior, an Instance is destroyed before the signal fires. ```lua local part = Instance.new("Part", workspace) local function onPartDestroying() print("In signal:", part:GetFullName(), #part:GetChildren()) end part.Destroying:Connect(onPartDestroying) print("Before destroying:", part:GetFullName(), #part:GetChildren()) part:Destroy() print("After destroying:", part:GetFullName(), #part:GetChildren()) ``` **Expected output:** Before destroying: Workspace.Part 1 After destroying: Part 0 In signal: Part 0 ### Event: Instance.StyledPropertiesChanged **Signature:** `Instance.StyledPropertiesChanged()` This event fires whenever any style property is changed on the instance, including when a property is set to `nil`. ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local playerGui = player.PlayerGui local HUDContainer = playerGui:WaitForChild("HUDContainer") local meterBar = HUDContainer.MeterBar local function stylePropertyChanged() print("Styled properties changed") end meterBar:StyledPropertiesChanged():Connect(stylePropertyChanged) ``` *Security: None* ### Event: Instance.childAdded **Signature:** `Instance.childAdded(child: Instance)` > **Deprecated:** This deprecated event is a variant of [Instance.ChildAdded](/docs/reference/engine/classes/Instance.md) which should be used instead. *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `child` | `Instance` | | ## Inherited Members ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: InstanceAdornment last_updated: 2026-06-29T19:34:07Z inherits: - GuiBase3d - GuiBase - Instance - Object type: class memory_category: Instances tags: - NotCreatable summary: "A base class for all objects that adorn Instance classes." --- # Class: InstanceAdornment > A base class for all objects that adorn [Instance](/docs/reference/engine/classes/Instance.md) classes. ## Properties ### Property: InstanceAdornment.Adornee ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic", "UI" ] } ``` Since adornments are usually parented to some GUI, this property determines which [Instance](/docs/reference/engine/classes/Instance.md) they are adorning. They render in 3D positioned relative to the "adornee", not to their parent. ## Inherited Members ### From [GuiBase3d](/docs/reference/engine/classes/GuiBase3d.md) - **Property `Color`** (`BrickColor`): Sets the color of a GUI object. *(deprecated, hidden)* - **Property `Color3`** (`Color3`): Sets the color of this GuiBase3d object. - **Property `Transparency`** (`float`): Sets the transparency of this GuiBase3d object. - **Property `Visible`** (`boolean`): Determines whether this GuiBase3d object and its descendants will ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: InstanceFileSyncService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated summary: "A service for interacting with file sync from a plugin." --- # Class: InstanceFileSyncService > A service for interacting with file sync from a plugin. ## Description The `InstanceFileSyncService` service provides an interface for plugins to interact with the file sync system (currently known as **Script Sync**). It allows querying the sync status of instances and retrieving instances based on file paths. ## Methods ### Method: InstanceFileSyncService:GetAllInstances **Signature:** `InstanceFileSyncService:GetAllInstances(): Instances` Returns an array containing every [Instance](/docs/reference/engine/classes/Instance.md) that is currently being tracked or synchronized by file sync. *Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `Instances` — Returns an array of [Instances](/docs/reference/engine/classes/Instance.md). ### Method: InstanceFileSyncService:GetStatus **Signature:** `InstanceFileSyncService:GetStatus(instance: Instance): InstanceFileSyncStatus` Returns the current [InstanceFileSyncStatus](/docs/reference/engine/enums/InstanceFileSyncStatus.md) of the provided [Instance](/docs/reference/engine/classes/Instance.md). This indicates whether the instance is synced as a root, as a descendant, or if it is in an error state. *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `instance` | `Instance` | | The instance to check. | **Returns:** `InstanceFileSyncStatus` — Returns the [InstanceFileSyncStatus](/docs/reference/engine/enums/InstanceFileSyncStatus.md) for the instance. ### Method: InstanceFileSyncService:GetSyncedInstance **Signature:** `InstanceFileSyncService:GetSyncedInstance(filePath: string): Instance?` Returns the [Instance](/docs/reference/engine/classes/Instance.md) associated with the specified absolute file path. Returns `nil` if the path is not mapped to any instance. *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `filePath` | `string` | | The absolute path to the file. | **Returns:** `Instance?` — Returns the Instance being synced to the file path, if one exists. Otherwise, returns nil. ## Events ### Event: InstanceFileSyncService.StatusChanged **Signature:** `InstanceFileSyncService.StatusChanged(instance: Instance, status: InstanceFileSyncStatus)` This event fires when the [InstanceFileSyncStatus](/docs/reference/engine/enums/InstanceFileSyncStatus.md) of an [Instance](/docs/reference/engine/classes/Instance.md) changes. It passes the affected instance and the new file sync status enum value. *Security: PluginSecurity* **Parameters:** | Name | Type | Description | |------|------|-------------| | `instance` | `Instance` | The instance whose status changed. | | `status` | `InstanceFileSyncStatus` | The new synchronization status. | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: IntConstrainedValue last_updated: 2026-06-29T19:34:07Z inherits: - ValueBase - Instance - Object type: class memory_category: Instances tags: - Deprecated summary: "An IntConstrainedValue is used to store a value which can never be less than MinValue and can never be more than MaxValue." --- # Class: IntConstrainedValue > An IntConstrainedValue is used to store a value which can never be less than > MinValue and can never be more than MaxValue. ## Properties ### Property: IntConstrainedValue.MaxValue ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "simulationAccess": true } ``` The highest number that the [IntConstrainedValue.Value](/docs/reference/engine/classes/IntConstrainedValue.md) property can be. ### Property: IntConstrainedValue.MinValue ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "simulationAccess": true } ``` The lowest number that the [IntConstrainedValue.Value](/docs/reference/engine/classes/IntConstrainedValue.md) property can be. ### Property: IntConstrainedValue.Value ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "simulationAccess": true } ``` Used to hold an integer value between [IntConstrainedValue.MinValue](/docs/reference/engine/classes/IntConstrainedValue.md) and [IntConstrainedValue.MaxValue](/docs/reference/engine/classes/IntConstrainedValue.md). ### Property: IntConstrainedValue.ConstrainedValue *(hidden)* ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` Hold an `Integer` value between [IntConstrainedValue.MinValue](/docs/reference/engine/classes/IntConstrainedValue.md) and [IntConstrainedValue.MaxValue](/docs/reference/engine/classes/IntConstrainedValue.md). Replaced by [IntConstrainedValue.Value](/docs/reference/engine/classes/IntConstrainedValue.md), but still functional. ## Events ### Event: IntConstrainedValue.Changed **Signature:** `IntConstrainedValue.Changed(value: int64)` Fired whenever the Value of the IntConstrainedValue is changed. *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `value` | `int64` | | **IntConstrainedValue.Changed** The below example, assuming all referenced objects existed, would print the IntConstrainedValue's new value each time it's changed. In the example below it would print 2, assuming it's within IntConstrainedValue's MinValue and MaxValue range. Otherwise there will be no change. ```lua local function valueChanged(newValue) print(newValue) end workspace.IntConstrainedValue.Changed:Connect(valueChanged) workspace.IntConstrainedValue.Value = 2 ``` ### Event: IntConstrainedValue.changed **Signature:** `IntConstrainedValue.changed(value: int64)` > **Deprecated:** This deprecated event is a variant of [IntConstrainedValue.Changed](/docs/reference/engine/classes/IntConstrainedValue.md) which has also been deprecated. Neither event should be used in new work. *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `value` | `int64` | | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: IntValue last_updated: 2026-06-29T19:34:07Z inherits: - ValueBase - Instance - Object type: class memory_category: Instances summary: "A container object for a single integer." --- # Class: IntValue > A container object for a single integer. ## Description Stores a single signed [64-bit integer](/docs/en-us/luau/numbers.md#int64). The highest allowed value is 2^63-1 or around 9.2 quintillion (9.2^18). Attempting to store larger numbers causes an [integer overflow](https://en.wikipedia.org/wiki/Integer_overflow). The lowest allowed value is -2^63. Practically, however, working with integers larger than 2^53 (9^15) causes loss of precision since Luau uses double-precision floating-point to store numbers. It's possible to store values between 2^53 and 2^63-1 using the [Properties](/docs/en-us/studio/properties.md) window since it uses strings to pass data to the engine, but manipulating large values via Luau scripts results in loss of precision and rounding as mentioned above. The main advantage of using **IntValue** lies in its rounding of values to the nearest integer, with halfway cases rounded away from 0. For values outside of this range, use [NumberValue](/docs/reference/engine/classes/NumberValue.md) instead. Like all [ValueBase](/docs/reference/engine/classes/ValueBase.md) objects, this single value is stored in the [Value](/docs/reference/engine/classes/IntValue.md) property. The `Changed` event fires with the new value being stored in the object, instead of a string representing the property being changed. ## Properties ### Property: IntValue.Value ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "simulationAccess": true } ``` Used to hold an integer. ## Events ### Event: IntValue.Changed **Signature:** `IntValue.Changed(value: int64)` Fires whenever the [IntValue.Value](/docs/reference/engine/classes/IntValue.md) changes. It runs with the new value being stored in the argument object, instead of a string representing the property being changed. Listening for the `Changed` signal can be useful in games that use `IntValues` to track values like leaderboard statistics or item prices. *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `value` | `int64` | The new value after the change. | **How to Use IntValue.Changed** The below example, assuming all referenced objects existed, would print the IntValue's new value each time it changed. In the example below it would print 20. ```lua local value = Instance.new("IntValue") value.Parent = workspace local function onValueChanged(newValue) print(newValue) end value.Changed:Connect(onValueChanged) value.Value = 20 ``` **Expected output:** "20" ### Event: IntValue.changed **Signature:** `IntValue.changed(value: int64)` > **Deprecated:** This deprecated event is a variant of [IntValue.Changed](/docs/reference/engine/classes/IntValue.md) which should be used instead. *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `value` | `int64` | | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: IntersectOperation last_updated: 2026-06-29T19:34:07Z inherits: - PartOperation - TriangleMeshPart - BasePart - PVInstance - Instance - Object type: class memory_category: Instances summary: "Result of parts that have been intersected into a single solid model." --- # Class: IntersectOperation > Result of parts that have been intersected into a single solid model. ## Description An **IntersectOperation** is the result of individual parts that have been intersected into a single solid model through Studio's solid modeling **Intersect** tool, or through [BasePart:IntersectAsync()](/docs/reference/engine/classes/BasePart.md). See [Solid Modeling](/docs/en-us/parts/solid-modeling.md) to learn more about Studio's solid modeling tools and methods. ## Inherited Members ### From [PartOperation](/docs/reference/engine/classes/PartOperation.md) - **Property `RenderFidelity`** (`RenderFidelity`): The level of detail used to render the solid modeled part. - **Property `SmoothingAngle`** (`float`): An angle in degrees which affects the smooth shading of a solid modeled - **Property `TriangleCount`** (`int`): The number of polygons in this solid model. - **Property `UsePartColor`** (`boolean`): Sets whether the PartOperation can be recolored using inherited - **Method `SubstituteGeometry(source: Instance): ()`**: Substitutes the geometry of this PartOperation with the geometry ### From [TriangleMeshPart](/docs/reference/engine/classes/TriangleMeshPart.md) - **Property `CollisionFidelity`** (`CollisionFidelity`): Determines the level of detail the part's physics will adhere to its mesh. - **Property `FluidFidelity`** (`FluidFidelity`): Determines the geometric representation used to compute aerodynamic forces - **Property `MeshSize`** (`Vector3`): ### From [BasePart](/docs/reference/engine/classes/BasePart.md) - **Property `Anchored`** (`boolean`): Determines whether a part is immovable by physics. - **Property `AssemblyAngularVelocity`** (`Vector3`): The angular velocity of the part's assembly. - **Property `AssemblyCenterOfMass`** (`Vector3`): The center of mass of the part's assembly in world space. - **Property `AssemblyLinearVelocity`** (`Vector3`): The linear velocity of the part's assembly. - **Property `AssemblyMass`** (`float`): The total mass of the part's assembly. - **Property `AssemblyRootPart`** (`BasePart`): A reference to the root part of the assembly. - **Property `AudioCanCollide`** (`boolean`): Determines whether the part will physically interact with audio - **Property `BackParamA`** (`float`): Determines the first parameter for the SurfaceType on the Back face of a *(deprecated, hidden)* - **Property `BackParamB`** (`float`): Determines the second parameter for the SurfaceType on the Back face of a *(deprecated, hidden)* - **Property `BackSurface`** (`SurfaceType`): Determines the type of surface for the back face of a part. - **Property `BackSurfaceInput`** (`InputType`): Determines the kind of input for the Back face of a part. *(deprecated, hidden)* - **Property `BottomParamA`** (`float`): Determines the first parameter for the SurfaceType on the Bottom face of a *(deprecated, hidden)* - **Property `BottomParamB`** (`float`): Determines the second parameter for the SurfaceType on the Bottom face of *(deprecated, hidden)* - **Property `BottomSurface`** (`SurfaceType`): Determines the type of surface for the bottom face of a part. - **Property `BottomSurfaceInput`** (`InputType`): Determines the kind of input for the Bottom face of a part. *(deprecated, hidden)* - **Property `BrickColor`** (`BrickColor`): Determines the color of a part. - **Property `brickColor`** (`BrickColor`): *(deprecated)* - **Property `CanCollide`** (`boolean`): Determines whether a part may collide with other parts. - **Property `CanQuery`** (`boolean`): Determines whether the part is considered during spatial query operations. - **Property `CanTouch`** (`boolean`): Determines if Touched and - **Property `CastShadow`** (`boolean`): Determines whether or not a part casts a shadow. - **Property `CenterOfMass`** (`Vector3`): Describes the world position in which a part's center of mass is located. - **Property `CFrame`** (`CFrame`): Determines the position and orientation of the BasePart in the - **Property `CollisionGroup`** (`string`): Describes the name of a part's collision group. - **Property `CollisionGroupId`** (`int`): Describes the automatically set ID number of a part's collision group. *(deprecated)* - **Property `Color`** (`Color3`): Determines the color of a part. - **Property `CurrentPhysicalProperties`** (`PhysicalProperties`): Indicates the current physical properties of the part. - **Property `CustomPhysicalProperties`** (`PhysicalProperties`): Determines several physical properties of a part. - **Property `Elasticity`** (`float`): Used to control the Elasticity of the part, but it no longer does *(deprecated, hidden)* - **Property `EnableFluidForces`** (`boolean`): Used to enable or disable aerodynamic forces on parts and assemblies. - **Property `ExtentsCFrame`** (`CFrame`): The CFrame of the physical extents of the BasePart. - **Property `ExtentsSize`** (`Vector3`): The actual physical size of the BasePart as regarded by the - **Property `Friction`** (`float`): Used to control the Friction of the part, but now it no longer does *(deprecated, hidden)* - **Property `FrontParamA`** (`float`): Determines the first parameter for the SurfaceType on the Front face of a *(deprecated, hidden)* - **Property `FrontParamB`** (`float`): Determines the second parameter for the SurfaceType on the Front face of a *(deprecated, hidden)* - **Property `FrontSurface`** (`SurfaceType`): Determines the type of surface for the front face of a part. - **Property `FrontSurfaceInput`** (`InputType`): Determines the kind of input for the Front face of a part (-Z direction). *(deprecated, hidden)* - **Property `LeftParamA`** (`float`): Determines the first parameter for the SurfaceType on the Left face of a *(deprecated, hidden)* - **Property `LeftParamB`** (`float`): Determines the second parameter for the SurfaceType on the Left face of a *(deprecated, hidden)* - **Property `LeftSurface`** (`SurfaceType`): Determines the type of surface for the left face of a part. - **Property `LeftSurfaceInput`** (`InputType`): Determines the kind of input for the Left face of a part. *(deprecated, hidden)* - **Property `LocalTransparencyModifier`** (`float`): Determines a multiplier for BasePart.Transparency that is only *(hidden)* - **Property `Locked`** (`boolean`): Determines whether a part is selectable in Studio. - **Property `Mass`** (`float`): Describes the mass of the part, the product of its density and volume. - **Property `Massless`** (`boolean`): Determines whether the part contributes to the total mass or inertia of - **Property `Material`** (`Material`): Determines the texture and default physical properties of a part. - **Property `MaterialVariant`** (`string`): The name of MaterialVariant. - **Property `Orientation`** (`Vector3`): Describes the rotation of the part in the world. *(hidden)* - **Property `PivotOffset`** (`CFrame`): Specifies the offset of the part's pivot from its CFrame. - **Property `Position`** (`Vector3`): Describes the position of the part in the world. *(hidden)* - **Property `ReceiveAge`** (`float`): Time since last recorded physics update. *(hidden)* - **Property `Reflectance`** (`float`): Determines how much a part reflects the skybox. - **Property `ResizeableFaces`** (`Faces`): Describes the faces on which a part may be resized. - **Property `ResizeIncrement`** (`int`): Describes the smallest change in size allowable by the - **Property `RightParamA`** (`float`): Determines the first parameter for the SurfaceType on the Right face of a *(deprecated, hidden)* - **Property `RightParamB`** (`float`): Determines the second parameter for the SurfaceType on the Right face of a *(deprecated, hidden)* - **Property `RightSurface`** (`SurfaceType`): Determines the type of surface for the right face of a part. - **Property `RightSurfaceInput`** (`InputType`): Determines the kind of input for the Right face of a part (-X direction). *(deprecated, hidden)* - **Property `RootPriority`** (`int`): The main rule in determining the root part of an assembly. - **Property `Rotation`** (`Vector3`): The rotation of the part in degrees for the three axes. - **Property `RotVelocity`** (`Vector3`): Determines a part's change in orientation over time. *(deprecated, hidden)* - **Property `Size`** (`Vector3`): Determines the dimensions of a part (length, width, height). - **Property `SpecificGravity`** (`float`): The ratio of the part's density to the density of water determined by the *(deprecated)* - **Property `TopParamA`** (`float`): Determines the first parameter for the SurfaceType on the Top face of a *(deprecated, hidden)* - **Property `TopParamB`** (`float`): Determines the second parameter for the SurfaceType on the Top face of a *(deprecated, hidden)* - **Property `TopSurface`** (`SurfaceType`): Determines the type of surface for the top face of a part. - **Property `TopSurfaceInput`** (`InputType`): Determines the kind of input for the Top face of a part (+Y direction). *(deprecated, hidden)* - **Property `Transparency`** (`float`): Determines how much a part can be seen through (the inverse of part - **Property `Velocity`** (`Vector3`): Determines a part's change in position over time. *(deprecated, hidden)* - **Method `AngularAccelerationToTorque(angAcceleration: Vector3, angVelocity?: Vector3): Vector3`**: Returns the torque needed to achieve a given angular acceleration on this - **Method `ApplyAngularImpulse(impulse: Vector3): ()`**: Apply an angular impulse to the assembly. - **Method `ApplyImpulse(impulse: Vector3): ()`**: Apply an impulse to the assembly at the assembly's - **Method `ApplyImpulseAtPosition(impulse: Vector3, position: Vector3): ()`**: Apply an impulse to the assembly at specified position. - **Method `BreakJoints(): ()`**: Breaks any surface connection with any adjacent part, including *(deprecated)* - **Method `breakJoints(): ()`**: *(deprecated)* - **Method `CanCollideWith(part: BasePart): boolean`**: Returns whether the parts can collide with each other. - **Method `CanSetNetworkOwnership(): Tuple`**: Checks whether you can set a part's network ownership. - **Method `GetClosestPointOnSurface(position: Vector3): Vector3`**: Returns the closest point on the part's surface to the given point. - **Method `GetConnectedParts(recursive?: boolean): List`**: Returns a table of parts connected to the object by any kind of rigid - **Method `GetJoints(): Instances`**: Return all Joints or Constraints that is connected to this Part. - **Method `GetMass(): float`**: Returns the value of the Mass property. - **Method `getMass(): float`**: *(deprecated)* - **Method `GetNetworkOwner(): Instance`**: Returns the current player who is the network owner of this part, or `nil` - **Method `GetNetworkOwnershipAuto(): boolean`**: Returns true if the game engine automatically decides the network owner - **Method `GetNoCollisionConstraints(): Instances`**: - **Method `GetRenderCFrame(): CFrame`**: OBSOLETE. Returns a CFrame describing where the part is being rendered at. *(deprecated)* - **Method `GetRootPart(): Instance`**: Returns the base part of an assembly of parts. *(deprecated)* - **Method `GetTouchingParts(): Instances`**: Returns a table of all BasePart.CanCollide true parts that - **Method `GetVelocityAtPosition(position: Vector3): Vector3`**: Returns the linear velocity of the part's assembly at the given position - **Method `IntersectAsync(parts: Instances, collisionfidelity?: CollisionFidelity, renderFidelity?: RenderFidelity): Instance`**: Note: It is highly recommended to use the newer - **Method `IsGrounded(): boolean`**: Returns true if the object is connected to a part that will hold it in - **Method `MakeJoints(): ()`**: Creates a joint on any side of the object that has a surface ID that can *(deprecated)* - **Method `makeJoints(): ()`**: *(deprecated)* - **Method `Resize(normalId: NormalId, deltaAmount: int): boolean`**: Changes the size of an object just like using the Studio resize tool. - **Method `resize(normalId: NormalId, deltaAmount: int): boolean`**: *(deprecated)* - **Method `SetNetworkOwner(playerInstance?: Player): ()`**: Sets the given player as network owner for this and all connected parts. - **Method `SetNetworkOwnershipAuto(): ()`**: Lets the game engine dynamically decide who will handle the part's physics - **Method `SubtractAsync(parts: Instances, collisionfidelity?: CollisionFidelity, renderFidelity?: RenderFidelity): Instance`**: Note: It is highly recommended to use the newer - **Method `TorqueToAngularAcceleration(torque: Vector3, angVelocity?: Vector3): Vector3`**: Returns the angular acceleration that would result from applying a given - **Method `UnionAsync(parts: Instances, collisionfidelity?: CollisionFidelity, renderFidelity?: RenderFidelity): Instance`**: Note: It is highly recommended to use the newer - **Event `LocalSimulationTouched`**: *(deprecated)* - **Event `OutfitChanged`**: *(deprecated)* - **Event `StoppedTouching`**: *(deprecated)* - **Event `Touched`**: Fires when a part touches another part as a result of physical movement. - **Event `TouchEnded`**: Fires when a part stops touching another part as a result of physical ### From [PVInstance](/docs/reference/engine/classes/PVInstance.md) - **Property `Origin`** (`CFrame`): - **Property `Pivot Offset`** (`CFrame`): - **Method `GetPivot(): CFrame`**: Gets the pivot of a PVInstance. - **Method `PivotTo(targetCFrame: CFrame): ()`**: Transforms the PVInstance along with all of its descendant ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: InventoryPages last_updated: 2026-06-29T19:34:07Z inherits: - Pages - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated --- # Class: InventoryPages ## Description The InventoryPages class is used in the case of iterating over a specific category in a user's inventory. ## Inherited Members ### From [Pages](/docs/reference/engine/classes/Pages.md) - **Property `IsFinished`** (`boolean`): Whether or not the current page is the last page available. - **Method `AdvanceToNextPageAsync(): ()`**: Iterates to the next page in the pages object, if possible. - **Method `GetCurrentPage(): Array`**: Returns the items on the current page. The keys in the item are determined ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: JointInstance last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: BaseParts tags: - NotCreatable summary: "The base class for joints." --- # Class: JointInstance > The base class for joints. ## Description `JointInstance` is the base class for joints such as [Welds](/docs/reference/engine/classes/Weld.md) and [Motors](/docs/reference/engine/classes/Motor.md). [Weld](/docs/reference/engine/classes/Weld.md), [WeldConstraint](/docs/reference/engine/classes/WeldConstraint.md), [Motor](/docs/reference/engine/classes/Motor.md), and [Motor6D](/docs/reference/engine/classes/Motor6D.md) all combine multiple parts into the same [assembly](/docs/en-us/physics/assemblies.md). Every assembly has a root part ([BasePart:GetRootPart()](/docs/reference/engine/classes/BasePart.md)) and when [C0](/docs/reference/engine/classes/JointInstance.md)/[C1](/docs/reference/engine/classes/JointInstance.md) of a [JointInstance](/docs/reference/engine/classes/JointInstance.md) is modified, the root part will stay where it was. Welds do not have any directionality. You can imagine rigid joints forming a tree branching down from the root part. All the parts down the tree from root will move, and their welded "children" in this tree will move with them. ## Properties ### Property: JointInstance.Active ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Physics" ] } ``` This property determines if the joint is currently active in the world. If the [JointInstance](/docs/reference/engine/classes/JointInstance.md) is not in [Workspace](/docs/reference/engine/classes/Workspace.md) or [JointsService](/docs/reference/engine/classes/JointsService.md), or one of its parts is not in [Workspace](/docs/reference/engine/classes/Workspace.md), the joint will be inactive. Rigid joints like [Weld](/docs/reference/engine/classes/Weld.md), [Snap](/docs/reference/engine/classes/Snap.md), [WeldConstraint](/docs/reference/engine/classes/WeldConstraint.md), [Motor](/docs/reference/engine/classes/Motor.md), or [Motor6D](/docs/reference/engine/classes/Motor6D.md) may also be disabled due to conflicts with other rigid joints, such as joints between the same two parts or indirect cycles in the weld graph. Joints disabled this way may be re-enabled later when another joint or part is added or removed. ### Property: JointInstance.C0 ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Physics" ] } ``` `C0` is the position aspect of the orientation between two parts in a weld. [Part0](/docs/reference/engine/classes/JointInstance.md) and [Part1](/docs/reference/engine/classes/JointInstance.md) move accordingly to this value, which denotes their respective positions. When animating a [Motor6D](/docs/reference/engine/classes/Motor6D.md), it's recommended to tween its [Transform](/docs/reference/engine/classes/Motor6D.md) property instead of `C0`. ### Property: JointInstance.C1 ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Physics" ] } ``` This property is subtracted from the [C0](/docs/reference/engine/classes/JointInstance.md) property to create an offset point for [Part1](/docs/reference/engine/classes/JointInstance.md). When animating a [Motor6D](/docs/reference/engine/classes/Motor6D.md), it's recommended to tween its [Transform](/docs/reference/engine/classes/Motor6D.md) property instead of `C1`. ### Property: JointInstance.Enabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Physics" ] } ``` This property sets whether the joint is active or not. When this property is set to `true` and the joint's [Part0](/docs/reference/engine/classes/JointInstance.md) and [Part1](/docs/reference/engine/classes/JointInstance.md) properties are set, the joint will ensure that its two parts will be connected and behave according to joint type. See also [Active](/docs/reference/engine/classes/JointInstance.md) which determines if the joint is currently active in the world. ### Property: JointInstance.Part0 ```json { "type": "BasePart", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Physics" ] } ``` This property indicates the first [BasePart](/docs/reference/engine/classes/BasePart.md) that the joint connects. ### Property: JointInstance.Part1 ```json { "type": "BasePart", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Physics" ] } ``` This property indicates the second [BasePart](/docs/reference/engine/classes/BasePart.md) that the joint connects. ### Property: JointInstance.part1 *(hidden)* ```json { "type": "BasePart", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Physics" ] } ``` > **Deprecated:** This deprecated property is a variant of [Part1](/docs/reference/engine/classes/JointInstance.md) which should be used instead. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: JointsService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - Deprecated summary: "A service that stores joints created by surface connections." --- # Class: JointsService > A service that stores joints created by surface connections. ## Description The JointsService is a service that stores joints created by surface connections. It also has API available for visualizing surface to surface contact, and joining surfaces together. ## Methods ### Method: JointsService:ClearJoinAfterMoveJoints **Signature:** `JointsService:ClearJoinAfterMoveJoints(): ()` Will remove any 'create joints' that were made visible via the [JointsService:ShowPermissibleJoints()](/docs/reference/engine/classes/JointsService.md) method. *Security: None · Thread Safety: Unsafe · Capabilities: Physics* **Returns:** `()` ### Method: JointsService:CreateJoinAfterMoveJoints **Signature:** `JointsService:CreateJoinAfterMoveJoints(): ()` Updates all visible joints for the parts assigned by the [JointsService:SetJoinAfterMoveTarget()](/docs/reference/engine/classes/JointsService.md) and [JointsService:SetJoinAfterMoveInstance()](/docs/reference/engine/classes/JointsService.md) methods. *Security: None · Thread Safety: Unsafe · Capabilities: Physics* **Returns:** `()` ### Method: JointsService:SetJoinAfterMoveInstance **Signature:** `JointsService:SetJoinAfterMoveInstance(joinInstance: Instance): ()` Sets the PVInstance that will be connected with the target PVInstance specified by [JointsService:SetJoinAfterMoveTarget()](/docs/reference/engine/classes/JointsService.md). *Security: None · Thread Safety: Unsafe · Capabilities: Physics* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `joinInstance` | `Instance` | | | **Returns:** `()` ### Method: JointsService:SetJoinAfterMoveTarget **Signature:** `JointsService:SetJoinAfterMoveTarget(joinTarget: Instance): ()` Sets the PVInstance that will be connected with the PVInstance specified by [JointsService:SetJoinAfterMoveInstance()](/docs/reference/engine/classes/JointsService.md). *Security: None · Thread Safety: Unsafe · Capabilities: Physics* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `joinTarget` | `Instance` | | | **Returns:** `()` ### Method: JointsService:ShowPermissibleJoints **Signature:** `JointsService:ShowPermissibleJoints(): ()` When used it will visibly display a potential surface connection between the two [BasePart](/docs/reference/engine/classes/BasePart.md), which were set with [JointsService:SetJoinAfterMoveTarget()](/docs/reference/engine/classes/JointsService.md) and [JointsService:SetJoinAfterMoveInstance()](/docs/reference/engine/classes/JointsService.md). *Security: None · Thread Safety: Unsafe · Capabilities: Physics* **Returns:** `()` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: KeyboardService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service --- # Class: KeyboardService ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Keyframe last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Animation summary: "A Keyframe holds the Poses applied to joints in a Model at a given point of time in an animation. Keyframes are interpolated between during animation playback." --- # Class: Keyframe > A Keyframe holds the [Poses](/docs/reference/engine/classes/Pose.md) applied to joints in a [Model](/docs/reference/engine/classes/Model.md) > at a given point of time in an animation. [Keyframes](/docs/reference/engine/classes/Keyframe.md) are > interpolated between during animation playback. ## Description A Keyframe holds the [Poses](/docs/reference/engine/classes/Pose.md) applied to joints in a [Model](/docs/reference/engine/classes/Model.md) at a given point of time in an animation. [Keyframes](/docs/reference/engine/classes/Keyframe.md) are interpolated between during animation playback. Note, in most cases developers do not need to manipulate [KeyframeSequences](/docs/reference/engine/classes/KeyframeSequence.md) as the animation editor covers most animation functionality. However, in some cases a developer may wish to generate an animation from a [Script](/docs/reference/engine/classes/Script.md) or build their own plugin. ## Structure Keyframes are held within a [KeyframeSequence](/docs/reference/engine/classes/KeyframeSequence.md) and contain [Pose](/docs/reference/engine/classes/Pose.md) objects. The poses are named in accordance with the [BaseParts](/docs/reference/engine/classes/BasePart.md) they correspond to and are structured in terms of joint hierarchy. This means each [Pose](/docs/reference/engine/classes/Pose.md) is parented to the [Pose](/docs/reference/engine/classes/Pose.md) corresponding to the part it is attached to. Note, as [Poses](/docs/reference/engine/classes/Pose.md) are named in accordance with the [BaseParts](/docs/reference/engine/classes/BasePart.md) they correspond to, animations require distinct part names to play correctly. ## Interpolation During animation playback the poses in different keyframes are interpolated between. This allows a smooth animation to be created without needing to define every frame. Note, the style of interpolation is determined in the [Pose](/docs/reference/engine/classes/Pose.md) object. The Keyframe object merely holds the [Poses](/docs/reference/engine/classes/Pose.md) at a defined point of time in the animation ([Keyframe.Time](/docs/reference/engine/classes/Keyframe.md)). ## Code Samples **Keyframe Generate Poses** This sample includes a function that will generate a 'blank' keyframe containing blank poses for all of the model's connected parts in the correct hierarchical order. ```lua local function generateKeyframe(model) if not model.PrimaryPart then warn("No primary part set") return end local rootPart = model.PrimaryPart:GetRootPart() if not rootPart then warn("Root part not found") return end local partsAdded = {} partsAdded[rootPart] = true local function addPoses(part, parentPose) -- get all of the joints attached to the part for _, joint in pairs(part:GetJoints()) do -- we're only interested in Motor6Ds if joint:IsA("Motor6D") then -- find the connected part local connectedPart = nil if joint.Part0 == part then connectedPart = joint.Part1 elseif joint.Part1 == part then connectedPart = joint.Part0 end if connectedPart then -- make sure we haven't already added this part if not partsAdded[connectedPart] then partsAdded[connectedPart] = true -- create a pose local pose = Instance.new("Pose") pose.Name = connectedPart.Name parentPose:AddSubPose(pose) -- recurse addPoses(connectedPart, pose) end end end end end local keyframe = Instance.new("Keyframe") -- populate the keyframe local rootPose = Instance.new("Pose") rootPose.Name = rootPart.Name addPoses(rootPart, rootPose) keyframe:AddPose(rootPose) return keyframe end local character = script.Parent local keyframe = generateKeyframe(character) print(keyframe) ``` ## Properties ### Property: Keyframe.Time ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Animation" ] } ``` This property gives the [Keyframe](/docs/reference/engine/classes/Keyframe.md) time position (in seconds) in an animation. This determines the time at which the [Poses](/docs/reference/engine/classes/Pose.md) inside the keyframe will be shown. Note the [Keyframe](/docs/reference/engine/classes/Keyframe.md) with the highest time value in a [KeyframeSequence](/docs/reference/engine/classes/KeyframeSequence.md) is used to determine the length of the animation. **Get KeyframeSequence Length** This sample contains a simple function that will get the length of a KeyframeSequence by finding the Keyframe with the highest Keyframe.Time value. ```lua local function getSequenceLength(keyframeSequence) local length = 0 for _, keyframe in pairs(keyframeSequence:GetKeyframes()) do if keyframe.Time > length then length = keyframe.Time end end return length end local keyframeSequence = Instance.new("KeyframeSequence") getSequenceLength(keyframeSequence) ``` ## Methods ### Method: Keyframe:AddMarker **Signature:** `Keyframe:AddMarker(marker: Instance): ()` This function adds a [KeyframeMarker](/docs/reference/engine/classes/KeyframeMarker.md) to the [Keyframe](/docs/reference/engine/classes/Keyframe.md) by parenting it to the keyframe. It is functionally identical to setting the marker's [Instance.Parent](/docs/reference/engine/classes/Instance.md) to the Keyframe. Note, this function will not error when an instance other than a KeyframeMarker is given as the parameter and will parent it successfully. #### More about Keyframes [Keyframe](/docs/reference/engine/classes/Keyframe.md) names do not need to be unique. For example, if an Animation has three keyframes named "Particles" the connected event returned by [AnimationTrack:GetMarkerReachedSignal()](/docs/reference/engine/classes/AnimationTrack.md) will fire each time one of these keyframes is reached. [Keyframe](/docs/reference/engine/classes/Keyframe.md) names can be set in the Roblox Animation Editor when creating or editing an animation. They cannot however be set by a [Script](/docs/reference/engine/classes/Script.md) on an existing animation prior to playing it. See also: - [Keyframe:RemoveMarker()](/docs/reference/engine/classes/Keyframe.md) - [Keyframe:GetMarkers()](/docs/reference/engine/classes/Keyframe.md) - [AnimationTrack:GetMarkerReachedSignal()](/docs/reference/engine/classes/AnimationTrack.md) *Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `marker` | `Instance` | | The [KeyframeMarker](/docs/reference/engine/classes/KeyframeMarker.md) being parented to the [Keyframe](/docs/reference/engine/classes/Keyframe.md). | **Returns:** `()` **Add Marker/Remove Marker** This example demonstrates the [Keyframe:AddMarker()](/docs/reference/engine/classes/Keyframe.md) and [Keyframe:RemoveMarker()](/docs/reference/engine/classes/Keyframe.md) functions. Note these are functionally equivalent to [parenting](/docs/reference/engine/classes/Instance.md) and un-parenting the markers. ```lua local keyframe = Instance.new("Keyframe") keyframe.Parent = workspace local marker = Instance.new("KeyframeMarker") marker.Name = "FootStep" marker.Value = 100 keyframe:AddMarker(marker) --marker.Parent = keyframe task.wait(2) keyframe:RemoveMarker(marker) --marker.Parent = nil ``` ### Method: Keyframe:AddPose **Signature:** `Keyframe:AddPose(pose: Instance): ()` This function adds a [Pose](/docs/reference/engine/classes/Pose.md) to the [Keyframe](/docs/reference/engine/classes/Keyframe.md) by parenting it to the keyframe. It is functionally identical to setting the pose's [Instance.Parent](/docs/reference/engine/classes/Instance.md) to the keyframe. Note, this function will not error when an instance other than a [Pose](/docs/reference/engine/classes/Pose.md) is given as the pose parameter and will parent it successfully. *Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `pose` | `Instance` | | The [Pose](/docs/reference/engine/classes/Pose.md) to be added. | **Returns:** `()` **Keyframe Generate Poses** This sample includes a function that will generate a 'blank' keyframe containing blank poses for all of the model's connected parts in the correct hierarchical order. ```lua local function generateKeyframe(model) if not model.PrimaryPart then warn("No primary part set") return end local rootPart = model.PrimaryPart:GetRootPart() if not rootPart then warn("Root part not found") return end local partsAdded = {} partsAdded[rootPart] = true local function addPoses(part, parentPose) -- get all of the joints attached to the part for _, joint in pairs(part:GetJoints()) do -- we're only interested in Motor6Ds if joint:IsA("Motor6D") then -- find the connected part local connectedPart = nil if joint.Part0 == part then connectedPart = joint.Part1 elseif joint.Part1 == part then connectedPart = joint.Part0 end if connectedPart then -- make sure we haven't already added this part if not partsAdded[connectedPart] then partsAdded[connectedPart] = true -- create a pose local pose = Instance.new("Pose") pose.Name = connectedPart.Name parentPose:AddSubPose(pose) -- recurse addPoses(connectedPart, pose) end end end end end local keyframe = Instance.new("Keyframe") -- populate the keyframe local rootPose = Instance.new("Pose") rootPose.Name = rootPart.Name addPoses(rootPart, rootPose) keyframe:AddPose(rootPose) return keyframe end local character = script.Parent local keyframe = generateKeyframe(character) print(keyframe) ``` **Keyframe Add/Remove Pose** This sample demonstrates quickly the Keyframe.AddPose, Keyframe.RemovePose and Pose.AddSubPose and Pose.RemoveSubPose functions. Note these are functionally equivalent to parenting and un-parenting the poses. ```lua local keyframe = Instance.new("Keyframe") keyframe.Parent = workspace local pose = Instance.new("Pose") pose.EasingStyle = Enum.PoseEasingStyle.Cubic pose.EasingDirection = Enum.PoseEasingDirection.Out local pose2 = Instance.new("Pose") pose2.EasingStyle = Enum.PoseEasingStyle.Cubic pose2.EasingDirection = Enum.PoseEasingDirection.Out keyframe:AddPose(pose) -- pose.Parent = keyframe task.wait(2) keyframe:RemovePose(pose) -- pose.Parent = nil task.wait(2) keyframe:AddPose(pose) -- pose.Parent = keyframe task.wait(2) pose:AddSubPose(pose2) -- pose2.Parent = pose task.wait(2) pose:RemoveSubPose(pose2) -- pose2.Parent = nil ``` ### Method: Keyframe:GetMarkers **Signature:** `Keyframe:GetMarkers(): List` This function returns an array containing all [KeyframeMarkers](/docs/reference/engine/classes/KeyframeMarker.md) that have been added to the [Keyframe](/docs/reference/engine/classes/Keyframe.md). Note, this function will only return [instances](/docs/reference/engine/classes/Instance.md) of type KeyframeMarker. #### More about Keyframes [Keyframe](/docs/reference/engine/classes/Keyframe.md) names do not need to be unique. For example, if an Animation has three keyframes named "Particles" the connected event returned by [AnimationTrack:GetMarkerReachedSignal()](/docs/reference/engine/classes/AnimationTrack.md) will fire each time one of these keyframes is reached. [Keyframe](/docs/reference/engine/classes/Keyframe.md) names can be set in the Roblox Animation Editor when creating or editing an animation. They cannot however be set by a [Script](/docs/reference/engine/classes/Script.md) on an existing animation prior to playing it. See also: - [Keyframe:AddMarker()](/docs/reference/engine/classes/Keyframe.md) - [Keyframe:RemoveMarker()](/docs/reference/engine/classes/Keyframe.md) - [AnimationTrack:GetMarkerReachedSignal()](/docs/reference/engine/classes/AnimationTrack.md) *Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Returns:** `List` — An array containing all [KeyframeMarkers](/docs/reference/engine/classes/KeyframeMarker.md) that have been added to the [Keyframe](/docs/reference/engine/classes/Keyframe.md). **Get Keyframe Markers Attached to a Keyframe** This example demonstrates the [Keyframe:AddMarker()](/docs/reference/engine/classes/Keyframe.md) and [Keyframe:GetMarkers()](/docs/reference/engine/classes/Keyframe.md) functions. After adding two markers, _marker1_ and _marker2_ to the keyframe, this example gets and prints the names of the added markers. ```lua local keyframe = Instance.new("Keyframe") keyframe.Parent = workspace local marker1 = Instance.new("KeyframeMarker") marker1.Name = "FootStep" marker1.Value = 100 local marker2 = Instance.new("KeyframeMarker") marker2.Name = "Wave" marker2.Value = 100 keyframe:AddMarker(marker1) --marker.Parent = keyframe keyframe:AddMarker(marker2) --marker.Parent = keyframe local markers = keyframe:GetMarkers() for _, marker in pairs(markers) do print(marker.Name) end ``` ### Method: Keyframe:GetPoses **Signature:** `Keyframe:GetPoses(): List` This function returns an array containing all [Poses](/docs/reference/engine/classes/Pose.md) that have been added to a [Keyframe](/docs/reference/engine/classes/Keyframe.md). *Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Returns:** `List` — An array of [Poses](/docs/reference/engine/classes/Pose.md). **Keyframe Reset Poses** This code sample includes a function to reset the CFrame of the Poses in a Keyframe. ```lua local function resetPoses(parent) -- both functions are equivalent to GetChildren local poses = parent:IsA("Keyframe") and parent:GetPoses() or parent:IsA("Pose") and parent:GetSubPoses() for _, pose in pairs(poses) do if pose:IsA("Pose") then pose.CFrame = CFrame.new() -- recurse resetPoses(pose) end end end ``` ### Method: Keyframe:RemoveMarker **Signature:** `Keyframe:RemoveMarker(marker: Instance): ()` This function removes a [KeyframeMarker](/docs/reference/engine/classes/KeyframeMarker.md) from the [Keyframe](/docs/reference/engine/classes/Keyframe.md) by settings its [Instance.Parent](/docs/reference/engine/classes/Instance.md) to `nil`. The KeyframeMarker's [Instance.Parent](/docs/reference/engine/classes/Instance.md) is set to `nil` but it is not destroyed. This means, provided the marker is referenced it can be re-parented later. Note, this function will not error when an instance other than a KeyframeMarker is given as the parameter. #### More about Keyframes [Keyframe](/docs/reference/engine/classes/Keyframe.md) names do not need to be unique. For example, if an Animation has three keyframes named "Particles" the connected event returned by [AnimationTrack:GetMarkerReachedSignal()](/docs/reference/engine/classes/AnimationTrack.md) will fire each time one of these keyframes is reached. [Keyframe](/docs/reference/engine/classes/Keyframe.md) names can be set in the Roblox Animation Editor when creating or editing an animation. They cannot however be set by a [Script](/docs/reference/engine/classes/Script.md) on an existing animation prior to playing it. See also: - [Keyframe:AddMarker()](/docs/reference/engine/classes/Keyframe.md) - [Keyframe:GetMarkers()](/docs/reference/engine/classes/Keyframe.md) - [AnimationTrack:GetMarkerReachedSignal()](/docs/reference/engine/classes/AnimationTrack.md) *Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `marker` | `Instance` | | The marker being removed from the [Keyframe](/docs/reference/engine/classes/Keyframe.md). | **Returns:** `()` **Add Marker/Remove Marker** This example demonstrates the [Keyframe:AddMarker()](/docs/reference/engine/classes/Keyframe.md) and [Keyframe:RemoveMarker()](/docs/reference/engine/classes/Keyframe.md) functions. Note these are functionally equivalent to [parenting](/docs/reference/engine/classes/Instance.md) and un-parenting the markers. ```lua local keyframe = Instance.new("Keyframe") keyframe.Parent = workspace local marker = Instance.new("KeyframeMarker") marker.Name = "FootStep" marker.Value = 100 keyframe:AddMarker(marker) --marker.Parent = keyframe task.wait(2) keyframe:RemoveMarker(marker) --marker.Parent = nil ``` ### Method: Keyframe:RemovePose **Signature:** `Keyframe:RemovePose(pose: Instance): ()` This function removes a [Pose](/docs/reference/engine/classes/Pose.md) from the [Keyframe](/docs/reference/engine/classes/Keyframe.md) by setting its [Instance.Parent](/docs/reference/engine/classes/Instance.md) to `nil` without destroying it. This means the provided the pose is referenced and it can be re-parented later. Note, this function will not error when an instance other than a [Pose](/docs/reference/engine/classes/Pose.md) is given as the pose parameter. *Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `pose` | `Instance` | | The [Pose](/docs/reference/engine/classes/Pose.md) to be removed. | **Returns:** `()` **Keyframe Add/Remove Pose** This sample demonstrates quickly the Keyframe.AddPose, Keyframe.RemovePose and Pose.AddSubPose and Pose.RemoveSubPose functions. Note these are functionally equivalent to parenting and un-parenting the poses. ```lua local keyframe = Instance.new("Keyframe") keyframe.Parent = workspace local pose = Instance.new("Pose") pose.EasingStyle = Enum.PoseEasingStyle.Cubic pose.EasingDirection = Enum.PoseEasingDirection.Out local pose2 = Instance.new("Pose") pose2.EasingStyle = Enum.PoseEasingStyle.Cubic pose2.EasingDirection = Enum.PoseEasingDirection.Out keyframe:AddPose(pose) -- pose.Parent = keyframe task.wait(2) keyframe:RemovePose(pose) -- pose.Parent = nil task.wait(2) keyframe:AddPose(pose) -- pose.Parent = keyframe task.wait(2) pose:AddSubPose(pose2) -- pose2.Parent = pose task.wait(2) pose:RemoveSubPose(pose2) -- pose2.Parent = nil ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: KeyframeMarker last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Animation summary: "An instance meant to represent an event that will eventually be fired when a Keyframe is hit." --- # Class: KeyframeMarker > An instance meant to represent an event that will eventually be fired when a > [Keyframe](/docs/reference/engine/classes/Keyframe.md) is hit. ## Description A KeyframeMarker is an instance meant to represent an event that will eventually be fired when a [Keyframe](/docs/reference/engine/classes/Keyframe.md) is hit. ## Using a KeyframeMarker KeyframeMarkers should always be parented to a Keyframe via setting the parent directly or using the [Keyframe:AddMarker()](/docs/reference/engine/classes/Keyframe.md) function of Keyframe. KeyframeMarkers can also be removed directly or using the [Keyframe:RemoveMarker()](/docs/reference/engine/classes/Keyframe.md) function, and polled to check which markers are attached to a specific Keyframe using [Keyframe:GetMarkers()](/docs/reference/engine/classes/Keyframe.md). Whenever a Keyframe is detected as an animation is running, there will be an event fired for each KeyframeMarker that is parented to the Keyframe. These events are identifiable by the name of the KeyframeMarker. You can retrieve and listen to these events using the [AnimationTrack.GetKeyframeMarkerReached](/docs/reference/engine/classes/AnimationTrack.md) function. Optionally, you may set the [KeyframeMarker.Value](/docs/reference/engine/classes/KeyframeMarker.md) property of the KeyframeMarker in order to pass along a value with the event being fired. It inherits the [Keyframe.Name](/docs/reference/engine/classes/Instance.md) property from [Instance](/docs/reference/engine/classes/Instance.md) and behaves identically. Names are used for identification and do not need to be unique. When multiple `KeyframeMarker` instances with the same name are attached to a [Keyframe](/docs/reference/engine/classes/Keyframe.md), events such as the one returned by [AnimationTrack:GetMarkerReachedSignal()](/docs/reference/engine/classes/AnimationTrack.md) will fire for every marker. See also: - [Keyframe](/docs/reference/engine/classes/Keyframe.md), holds the [Poses](/docs/reference/engine/classes/Pose.md) applied to joints in a [Model](/docs/reference/engine/classes/Model.md) at a given point of time in an animation - [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md), controls the playback of an animation on a [Humanoid](/docs/reference/engine/classes/Humanoid.md) or [AnimationController](/docs/reference/engine/classes/AnimationController.md) - [Animation](/docs/reference/engine/classes/Animation.md), holds a reference to animation data required to play custom animations on characters or other models using the Roblox animation system ## Code Samples **Get Keyframe Markers Attached to a Keyframe** This example demonstrates the [Keyframe:AddMarker()](/docs/reference/engine/classes/Keyframe.md) and [Keyframe:GetMarkers()](/docs/reference/engine/classes/Keyframe.md) functions. After adding two markers, _marker1_ and _marker2_ to the keyframe, this example gets and prints the names of the added markers. ```lua local keyframe = Instance.new("Keyframe") keyframe.Parent = workspace local marker1 = Instance.new("KeyframeMarker") marker1.Name = "FootStep" marker1.Value = 100 local marker2 = Instance.new("KeyframeMarker") marker2.Name = "Wave" marker2.Value = 100 keyframe:AddMarker(marker1) --marker.Parent = keyframe keyframe:AddMarker(marker2) --marker.Parent = keyframe local markers = keyframe:GetMarkers() for _, marker in pairs(markers) do print(marker.Name) end ``` **Listening to Keyframe Markers** This `LocalScript` code waits for the local player's `Humanoid` object to load, then it creates a new `Animation` instance with the proper [Animation.AnimationId](/docs/reference/engine/classes/Animation.md). The animation is then loaded onto the humanoid, creating an `AnimationTrack`, and the track is played with [AnimationTrack:Play()](/docs/reference/engine/classes/AnimationTrack.md). Following that, the [AnimationTrack:GetMarkerReachedSignal()](/docs/reference/engine/classes/AnimationTrack.md) function detects when the "KickEnd" marker is hit. ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local character = player.Character or player.Character:Wait() local humanoid = character:WaitForChild("Humanoid") -- Create new "Animation" instance local kickAnimation = Instance.new("Animation") -- Set its "AnimationId" to the corresponding animation asset ID kickAnimation.AnimationId = "rbxassetid://2515090838" -- Load animation onto the humanoid local kickAnimationTrack = humanoid:LoadAnimation(kickAnimation) -- Play animation track kickAnimationTrack:Play() -- If a named event was defined for the animation, connect it to "GetMarkerReachedSignal()" kickAnimationTrack:GetMarkerReachedSignal("KickEnd"):Connect(function(paramString) print(paramString) end) ``` ## Properties ### Property: KeyframeMarker.Value ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Animation" ] } ``` A value that is specified for a [KeyframeMarker](/docs/reference/engine/classes/KeyframeMarker.md). Whenever the signal created from [AnimationTrack:GetMarkerReachedSignal()](/docs/reference/engine/classes/AnimationTrack.md) gets fired, this value will be passed into the connected function. See also: - [Keyframe](/docs/reference/engine/classes/Keyframe.md), holds the [Poses](/docs/reference/engine/classes/Pose.md) applied to joints in a [Model](/docs/reference/engine/classes/Model.md) at a given point of time in an animation - [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md), controls the playback of an animation on a [Humanoid](/docs/reference/engine/classes/Humanoid.md) or [AnimationController](/docs/reference/engine/classes/AnimationController.md) **Get Keyframe Markers Attached to a Keyframe** This example demonstrates the [Keyframe:AddMarker()](/docs/reference/engine/classes/Keyframe.md) and [Keyframe:GetMarkers()](/docs/reference/engine/classes/Keyframe.md) functions. After adding two markers, _marker1_ and _marker2_ to the keyframe, this example gets and prints the names of the added markers. ```lua local keyframe = Instance.new("Keyframe") keyframe.Parent = workspace local marker1 = Instance.new("KeyframeMarker") marker1.Name = "FootStep" marker1.Value = 100 local marker2 = Instance.new("KeyframeMarker") marker2.Name = "Wave" marker2.Value = 100 keyframe:AddMarker(marker1) --marker.Parent = keyframe keyframe:AddMarker(marker2) --marker.Parent = keyframe local markers = keyframe:GetMarkers() for _, marker in pairs(markers) do print(marker.Name) end ``` **Add Marker/Remove Marker** This example demonstrates the [Keyframe:AddMarker()](/docs/reference/engine/classes/Keyframe.md) and [Keyframe:RemoveMarker()](/docs/reference/engine/classes/Keyframe.md) functions. Note these are functionally equivalent to [parenting](/docs/reference/engine/classes/Instance.md) and un-parenting the markers. ```lua local keyframe = Instance.new("Keyframe") keyframe.Parent = workspace local marker = Instance.new("KeyframeMarker") marker.Name = "FootStep" marker.Value = 100 keyframe:AddMarker(marker) --marker.Parent = keyframe task.wait(2) keyframe:RemoveMarker(marker) --marker.Parent = nil ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: KeyframeSequence last_updated: 2026-06-29T19:34:07Z inherits: - AnimationClip - Instance - Object type: class memory_category: Animation summary: "This object stores all of the Keyframes and other data for the animation." --- # Class: KeyframeSequence > This object stores all of the [Keyframes](/docs/reference/engine/classes/Keyframe.md) and other data for > the animation. ## Description [KeyframeSequence](/docs/reference/engine/classes/KeyframeSequence.md) stores all the [Keyframes](/docs/reference/engine/classes/Keyframe.md) for an [Animation](/docs/reference/engine/classes/Animation.md), determines if the animation will [Loop](/docs/reference/engine/classes/KeyframeSequence.md), and determines its [Priority](/docs/reference/engine/classes/KeyframeSequence.md) against other animations. The last [Keyframe](/docs/reference/engine/classes/Keyframe.md) in the sequence, meaning the [Keyframe](/docs/reference/engine/classes/Keyframe.md) with the highest [Time](/docs/reference/engine/classes/Keyframe.md) property, determines the length of an animation. Although [Priority](/docs/reference/engine/classes/KeyframeSequence.md) and [Loop](/docs/reference/engine/classes/KeyframeSequence.md) save the priority and looped animation settings for the sequence, note that [AnimationTrack](/docs/reference/engine/classes/AnimationTrack.md) properties can eventually overwrite these properties at playback time. If you want to preview an [Animation](/docs/reference/engine/classes/Animation.md) before uploading it to Roblox, you can generate a temporary hash ID using [KeyframeSequenceProvider:RegisterKeyframeSequence()](/docs/reference/engine/classes/KeyframeSequenceProvider.md) for localized animation testing. #### Obtaining Sequences In some cases you may wish to download the [KeyframeSequence](/docs/reference/engine/classes/KeyframeSequence.md) corresponding to an existing uploaded [Animation](/docs/reference/engine/classes/Animation.md). You can use [AnimationClipProvider:GetAnimationClipAsync()](/docs/reference/engine/classes/AnimationClipProvider.md) to download an animation. ## Code Samples **Get KeyframeSequence Length** This sample contains a simple function that will get the length of a KeyframeSequence by finding the Keyframe with the highest Keyframe.Time value. ```lua local function getSequenceLength(keyframeSequence) local length = 0 for _, keyframe in pairs(keyframeSequence:GetKeyframes()) do if keyframe.Time > length then length = keyframe.Time end end return length end local keyframeSequence = Instance.new("KeyframeSequence") getSequenceLength(keyframeSequence) ``` **KeyframeSequence Instantiation** This sample demonstrates how a basic [KeyframeSequence](/docs/reference/engine/classes/KeyframeSequence.md) can be created. ```lua -- Create the sequence local keyframeSequence = Instance.new("KeyframeSequence") keyframeSequence.Loop = false keyframeSequence.Priority = Enum.AnimationPriority.Action -- Create a keyframe local keyframe = Instance.new("Keyframe") keyframe.Time = 0 -- Create sample poses local rootPose = Instance.new("Pose") rootPose.Name = "HumanoidRootPart" rootPose.Weight = 0 local lowerTorsoPose = Instance.new("Pose") lowerTorsoPose.Name = "LowerTorso" lowerTorsoPose.Weight = 1 -- Set the sequence hierarchy rootPose:AddSubPose(lowerTorsoPose) keyframe:AddPose(rootPose) keyframeSequence:AddKeyframe(keyframe) -- Parent the sequence keyframeSequence.Parent = workspace ``` ## Properties ### Property: KeyframeSequence.AuthoredHipHeight *(hidden)* ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Animation" ] } ``` Contains the hip height of the [Humanoid](/docs/reference/engine/classes/Humanoid.md) of the model that was used to author this [KeyframeSequence](/docs/reference/engine/classes/KeyframeSequence.md). Default value is `1.35` since that is the hip height set for a standard R15 character. ## Methods ### Method: KeyframeSequence:AddKeyframe **Signature:** `KeyframeSequence:AddKeyframe(keyframe: Instance): ()` This method adds a [Keyframe](/docs/reference/engine/classes/Keyframe.md) to the [KeyframeSequence](/docs/reference/engine/classes/KeyframeSequence.md) by parenting it to the [KeyframeSequence](/docs/reference/engine/classes/KeyframeSequence.md). It is functionally identical to setting the keyframe's [Instance.Parent](/docs/reference/engine/classes/Instance.md) to the [KeyframeSequence](/docs/reference/engine/classes/KeyframeSequence.md). Note that this method will not error when called with an instance other than a [Keyframe](/docs/reference/engine/classes/Keyframe.md) as the `keyframe` parameter and will parent it successfully. *Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `keyframe` | `Instance` | | The [Keyframe](/docs/reference/engine/classes/Keyframe.md) to be added. | **Returns:** `()` **KeyframeSequence Instantiation** This sample demonstrates how a basic [KeyframeSequence](/docs/reference/engine/classes/KeyframeSequence.md) can be created. ```lua -- Create the sequence local keyframeSequence = Instance.new("KeyframeSequence") keyframeSequence.Loop = false keyframeSequence.Priority = Enum.AnimationPriority.Action -- Create a keyframe local keyframe = Instance.new("Keyframe") keyframe.Time = 0 -- Create sample poses local rootPose = Instance.new("Pose") rootPose.Name = "HumanoidRootPart" rootPose.Weight = 0 local lowerTorsoPose = Instance.new("Pose") lowerTorsoPose.Name = "LowerTorso" lowerTorsoPose.Weight = 1 -- Set the sequence hierarchy rootPose:AddSubPose(lowerTorsoPose) keyframe:AddPose(rootPose) keyframeSequence:AddKeyframe(keyframe) -- Parent the sequence keyframeSequence.Parent = workspace ``` ### Method: KeyframeSequence:GetKeyframes **Signature:** `KeyframeSequence:GetKeyframes(): List` This method returns an array that contains all [Keyframes](/docs/reference/engine/classes/Keyframe.md) that have been added to a [KeyframeSequence](/docs/reference/engine/classes/KeyframeSequence.md). *Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Returns:** `List` — An array of [Keyframes](/docs/reference/engine/classes/Keyframe.md). **Get KeyframeSequence Length** This sample contains a simple function that will get the length of a KeyframeSequence by finding the Keyframe with the highest Keyframe.Time value. ```lua local function getSequenceLength(keyframeSequence) local length = 0 for _, keyframe in pairs(keyframeSequence:GetKeyframes()) do if keyframe.Time > length then length = keyframe.Time end end return length end local keyframeSequence = Instance.new("KeyframeSequence") getSequenceLength(keyframeSequence) ``` ### Method: KeyframeSequence:RemoveKeyframe **Signature:** `KeyframeSequence:RemoveKeyframe(keyframe: Instance): ()` This method removes a [Keyframe](/docs/reference/engine/classes/Keyframe.md) from the [KeyframeSequence](/docs/reference/engine/classes/KeyframeSequence.md) by setting its parent to `nil`. It is functionally identical to setting the keyframe's parent to `nil`. Note that while this sets the keyframe's parent to `nil`, it does not destroy it. Provided another reference to the keyframe remains, it can be re-parented later. Also note that this method will not error when called with an [Instance](/docs/reference/engine/classes/Instance.md) other than a [Keyframe](/docs/reference/engine/classes/Keyframe.md) as the keyframe parameter. *Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `keyframe` | `Instance` | | The [Keyframe](/docs/reference/engine/classes/Keyframe.md) to be removed. | **Returns:** `()` **KeyframeSequence RemoveKeyframe** This sample adds a [Keyframe](/docs/reference/engine/classes/Keyframe.md) to a [KeyframeSequence](/docs/reference/engine/classes/KeyframeSequence.md) before removing it and adding it again. Note that once a [Keyframe](/docs/reference/engine/classes/Keyframe.md) is removed, it is not destroyed, meaning it can be re-added later. ```lua local keyframeSequence = Instance.new("KeyframeSequence") keyframeSequence.Parent = workspace local keyframe = Instance.new("Keyframe") keyframeSequence:AddKeyframe(keyframe) task.wait(2) keyframeSequence:RemoveKeyframe(keyframe) ``` ## Inherited Members ### From [AnimationClip](/docs/reference/engine/classes/AnimationClip.md) - **Property `Length`** (`float`): Returns the length (in seconds) of this AnimationClip. This will - **Property `Loop`** (`boolean`): Determines whether the animation stored in this AnimationClip is - **Property `Priority`** (`AnimationPriority`): Determines which clip takes priority when multiple animations are playing ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: KeyframeSequenceProvider last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Animation tags: - NotCreatable - Service - NotReplicated summary: "Provides functions to load and preview KeyframeSequence." --- # Class: KeyframeSequenceProvider > Provides functions to load and preview [KeyframeSequence](/docs/reference/engine/classes/KeyframeSequence.md). ## Description The **KeyframeSequenceProvider** service provides functions to load and preview [KeyframeSequences](/docs/reference/engine/classes/KeyframeSequence.md). It includes a number of functions that are useful when working with [Animations](/docs/reference/engine/classes/Animation.md). A [KeyframeSequence](/docs/reference/engine/classes/KeyframeSequence.md) stores a series of [Poses](/docs/reference/engine/classes/Pose.md) that encode the hierarchy and motion of an animation. The animation data Roblox uses in the playback of an animation, referenced by the [Animation.AnimationId](/docs/reference/engine/classes/Animation.md) property, can be constructed from a [KeyframeSequence](/docs/reference/engine/classes/KeyframeSequence.md). [KeyframeSequences](/docs/reference/engine/classes/KeyframeSequence.md) are usually created by the Roblox [Animation Editor](/docs/en-us/animation/editor.md) but can be created through other plugins or even manually. ## Code Samples **Create temporary animation** This code sample contains a simple function to generate an [Animation](/docs/reference/engine/classes/Animation.md) with a generated hash ID to preview a [KeyframeSequence](/docs/reference/engine/classes/KeyframeSequence.md) locally. ```lua local KeyframeSequenceProvider = game:GetService("KeyframeSequenceProvider") local function createPreviewAnimation(keyframeSequence) local hashId = KeyframeSequenceProvider:RegisterKeyframeSequence(keyframeSequence) local Animation = Instance.new("Animation") Animation.AnimationId = hashId return Animation end local keyframeSequence = Instance.new("KeyframeSequence") local animation = createPreviewAnimation(keyframeSequence) print(animation) ``` ## Methods ### Method: KeyframeSequenceProvider:GetAnimationsAsync **Signature:** `KeyframeSequenceProvider:GetAnimationsAsync(userId: User): Instance` This function returns an [InventoryPages](/docs/reference/engine/classes/InventoryPages.md) object which can be used to iterate over animations owned by a specific user. This function has a number of potential uses, such as allowing users to browse and import animations into a custom animation plugin. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `userId` | `User` | | The user ID of the user. | **Returns:** `Instance` — An [InventoryPages](/docs/reference/engine/classes/InventoryPages.md) of animations. **KeyframeSequenceProvider GetAnimationsAsync** This code sample includes a demonstration of how the content IDs of animations belonging to a player can be downloaded using [KeyframeSequenceProvider:GetAnimationsAsync()](/docs/reference/engine/classes/KeyframeSequenceProvider.md). To run this code be sure to change the _USER_ID_ variable to the ID of the user whose animations you want to download. ```lua local KeyframeSequenceProvider = game:GetService("KeyframeSequenceProvider") local USER_ID = 0 -- Insert your UserId here local function extractPages(pagesObject) local array = {} while true do local thisPage = pagesObject:GetCurrentPage() for _, v in pairs(thisPage) do table.insert(array, v) end if pagesObject.IsFinished then break end pagesObject:AdvanceToNextPageAsync() end return array end local inventoryPages = KeyframeSequenceProvider:GetAnimationsAsync(USER_ID) local animationIds = extractPages(inventoryPages) for _, id in pairs(animationIds) do print(id) end print("total: ", #animationIds) ``` ### Method: KeyframeSequenceProvider:GetKeyframeSequenceAsync **Signature:** `KeyframeSequenceProvider:GetKeyframeSequenceAsync(assetId: ContentId): Instance` GetKeyframeSequenceAsync returns a [KeyframeSequence](/docs/reference/engine/classes/KeyframeSequence.md) based on the specified assetId. The assetId must correspond to an animation. The function will yield until the [KeyframeSequence](/docs/reference/engine/classes/KeyframeSequence.md) is loaded from the website. Because this is a webcall it should wrapped in a pcall. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `assetId` | `ContentId` | | The content ID of the animation. | **Returns:** `Instance` — The [KeyframeSequence](/docs/reference/engine/classes/KeyframeSequence.md) found. **Getting an animation's KeyframeSequence** Demonstrates getting an animation's KeyframeSequence using [KeyframeSequenceProvider:GetKeyframeSequenceAsync()](/docs/reference/engine/classes/KeyframeSequenceProvider.md) and printing the time of each keyframe. ```lua local KeyframeSequenceProvider = game:GetService("KeyframeSequenceProvider") local ANIMATION_ID = "rbxassetid://507771019" -- Get the keyframe sequence for the asset local keyframeSequence local success, err = pcall(function() keyframeSequence = KeyframeSequenceProvider:GetKeyframeSequenceAsync(ANIMATION_ID) end) if success then -- Iterate over each keyframe and print its time value local keyframeTable = keyframeSequence:GetKeyframes() for key, value in keyframeTable do print(`The time of keyframe number {key} is: {value.Time}`) end else print(`Error getting KeyframeSequence: {err}`) end ``` ### Method: KeyframeSequenceProvider:RegisterActiveKeyframeSequence **Signature:** `KeyframeSequenceProvider:RegisterActiveKeyframeSequence(keyframeSequence: Instance): ContentId` Generates a temporary asset ID from a [KeyframeSequence](/docs/reference/engine/classes/KeyframeSequence.md) that can be used for localized testing of an animation. This function performs the same function to [KeyframeSequenceProvider:RegisterKeyframeSequence()](/docs/reference/engine/classes/KeyframeSequenceProvider.md) however this function generates an _active://_ URL instead of a hash. The ID generated can be used in an [Animation.AnimationId](/docs/reference/engine/classes/Animation.md) property for testing. The asset ID generated by this function is temporary and cannot be used outside of Studio. Developers wishing to generate an asset ID that can be used online should upload the [KeyframeSequence](/docs/reference/engine/classes/KeyframeSequence.md) to Roblox. *Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `keyframeSequence` | `Instance` | | The [KeyframeSequence](/docs/reference/engine/classes/KeyframeSequence.md) to be used. | **Returns:** `ContentId` — A temporary asset ID generated for localized animation playback. **Create temporary animation** This code sample contains a simple function to generate an [Animation](/docs/reference/engine/classes/Animation.md) with a generated hash ID to preview a [KeyframeSequence](/docs/reference/engine/classes/KeyframeSequence.md) locally. ```lua local KeyframeSequenceProvider = game:GetService("KeyframeSequenceProvider") local function createPreviewAnimation(keyframeSequence) local hashId = KeyframeSequenceProvider:RegisterKeyframeSequence(keyframeSequence) local Animation = Instance.new("Animation") Animation.AnimationId = hashId return Animation end local keyframeSequence = Instance.new("KeyframeSequence") local animation = createPreviewAnimation(keyframeSequence) print(animation) ``` ### Method: KeyframeSequenceProvider:RegisterKeyframeSequence **Signature:** `KeyframeSequenceProvider:RegisterKeyframeSequence(keyframeSequence: Instance): ContentId` Generates a temporary asset ID from a [KeyframeSequence](/docs/reference/engine/classes/KeyframeSequence.md) that can be used for localized testing of an animation. This function performs the same function to [KeyframeSequenceProvider:RegisterActiveKeyframeSequence()](/docs/reference/engine/classes/KeyframeSequenceProvider.md) however this function generates a hash instead of an _active://_ URL. The ID generated can be used for the [Animation.AnimationId](/docs/reference/engine/classes/Animation.md) property to test animations. The asset ID generated by this function is temporary and cannot be used outside of Studio. Developers wishing to generate an asset ID that can be used online should upload the [KeyframeSequence](/docs/reference/engine/classes/KeyframeSequence.md) to Roblox. *Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `keyframeSequence` | `Instance` | | The [KeyframeSequence](/docs/reference/engine/classes/KeyframeSequence.md) to be used. | **Returns:** `ContentId` — A temporary asset ID generated for localized animation playback. **KeyframeSequenceProvider:RegisterKeyframeSequence** ```lua local KeyframeSequenceProvider = game:GetService("KeyframeSequenceProvider") local asset = KeyframeSequenceProvider:RegisterKeyframeSequence(workspace.KeyframeSequence) local animation = Instance.new("Animation") animation.Name = "TestAnimation" animation.AnimationId = asset animation.Parent = workspace ``` ### Method: KeyframeSequenceProvider:GetAnimations **Signature:** `KeyframeSequenceProvider:GetAnimations(userId: User): Instance` This function returns an [InventoryPages](/docs/reference/engine/classes/InventoryPages.md) object which can be used to iterate over animations owned by a specific user. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `userId` | `User` | | The user ID of the user. | **Returns:** `Instance` — An [InventoryPages](/docs/reference/engine/classes/InventoryPages.md) of animations. **KeyframeSequenceProvider GetAnimationsAsync** This code sample includes a demonstration of how the content IDs of animations belonging to a player can be downloaded using [KeyframeSequenceProvider:GetAnimationsAsync()](/docs/reference/engine/classes/KeyframeSequenceProvider.md). To run this code be sure to change the _USER_ID_ variable to the ID of the user whose animations you want to download. ```lua local KeyframeSequenceProvider = game:GetService("KeyframeSequenceProvider") local USER_ID = 0 -- Insert your UserId here local function extractPages(pagesObject) local array = {} while true do local thisPage = pagesObject:GetCurrentPage() for _, v in pairs(thisPage) do table.insert(array, v) end if pagesObject.IsFinished then break end pagesObject:AdvanceToNextPageAsync() end return array end local inventoryPages = KeyframeSequenceProvider:GetAnimationsAsync(USER_ID) local animationIds = extractPages(inventoryPages) for _, id in pairs(animationIds) do print(id) end print("total: ", #animationIds) ``` ### Method: KeyframeSequenceProvider:GetKeyframeSequence **Signature:** `KeyframeSequenceProvider:GetKeyframeSequence(assetId: ContentId): Instance` > **Deprecated:** This function is deprecated and can lead to the game freezing until the animation is loaded. Developers are recommended to use [KeyframeSequenceProvider:GetKeyframeSequenceAsync()](/docs/reference/engine/classes/KeyframeSequenceProvider.md) instead. Returns a [KeyframeSequence](/docs/reference/engine/classes/KeyframeSequence.md) from a given asset URL. *Security: PluginSecurity · Thread Safety: Unsafe · Capabilities: Animation* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `assetId` | `ContentId` | | The content ID of the animation. | **Returns:** `Instance` — The [KeyframeSequence](/docs/reference/engine/classes/KeyframeSequence.md) found. ### Method: KeyframeSequenceProvider:GetKeyframeSequenceById **Signature:** `KeyframeSequenceProvider:GetKeyframeSequenceById(assetId: int64, useCache: boolean): Instance` > **Deprecated:** This function is deprecated and can lead to the game freezing until the animation is loaded. Developers are recommended to use [KeyframeSequenceProvider:GetKeyframeSequenceAsync()](/docs/reference/engine/classes/KeyframeSequenceProvider.md) instead. Returns a [KeyframeSequence](/docs/reference/engine/classes/KeyframeSequence.md) from the supplied assetId. Can optionally cache to reduce unnecessary loading freezes. *Security: PluginSecurity · Thread Safety: Unsafe · Capabilities: Animation* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `assetId` | `int64` | | The content ID of the animation. | | `useCache` | `boolean` | | True if a cached version can be returned. | **Returns:** `Instance` — The [KeyframeSequence](/docs/reference/engine/classes/KeyframeSequence.md) found. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: LayerCollector last_updated: 2026-06-29T19:34:07Z inherits: - GuiBase2d - GuiBase - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotBrowsable summary: "The base class of 2D UI containers which render GuiObjects in layers." --- # Class: LayerCollector > The base class of 2D UI containers which render [GuiObjects](/docs/reference/engine/classes/GuiObject.md) > in layers. ## Description **LayerCollector** is the base class of 2D UI containers which render [GuiObject](/docs/reference/engine/classes/GuiObject.md) descendants, such as [ScreenGui](/docs/reference/engine/classes/ScreenGui.md). For performance improvements, the appearance of a [LayerCollector](/docs/reference/engine/classes/LayerCollector.md) is cached until one of the following events occurs: - A descendant is added to or removed from it. - A property of a descendant changes. - A property of the [LayerCollector](/docs/reference/engine/classes/LayerCollector.md) itself changes. ## Properties ### Property: LayerCollector.Enabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` Toggles the visibility of this [LayerCollector](/docs/reference/engine/classes/LayerCollector.md). When `false`, the UI contents will not render, process user input, or update in response to changes. ### Property: LayerCollector.ResetOnSpawn ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` When set to `false` and this [LayerCollector](/docs/reference/engine/classes/LayerCollector.md) is a direct child of [StarterGui](/docs/reference/engine/classes/StarterGui.md), it will only be cloned into each player's [PlayerGui](/docs/reference/engine/classes/PlayerGui.md) once and it will not be deleted when the player's character respawns. When set to `true` (default), or if this [LayerCollector](/docs/reference/engine/classes/LayerCollector.md) is an **indirect** descendant of [StarterGui](/docs/reference/engine/classes/StarterGui.md), it will be cloned into each player's [PlayerGui](/docs/reference/engine/classes/PlayerGui.md) when their character respawns, and it will delete itself when the player's character respawns again. ### Property: LayerCollector.ZIndexBehavior ```json { "type": "ZIndexBehavior", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` Controls how [GuiObject.ZIndex](/docs/reference/engine/classes/GuiObject.md) behaves on all descendants of this [LayerCollector](/docs/reference/engine/classes/LayerCollector.md). With [ZIndexBehavior.Sibling](/docs/reference/engine/enums/ZIndexBehavior.md) (default), children always render above their parents, and the [ZIndex](/docs/reference/engine/classes/GuiObject.md) is used to decide the order in which children of a single UI object will render over each other. [ZIndexBehavior.Global](/docs/reference/engine/enums/ZIndexBehavior.md) sorts all descendants according to the [ZIndex](/docs/reference/engine/classes/GuiObject.md), then breaks ties using the hierarchy order. As a result, descendants of a [GuiObject](/docs/reference/engine/classes/GuiObject.md) need to have a [ZIndex](/docs/reference/engine/classes/GuiObject.md) value that's at least as high as the parent, or they will render underneath their parent. ## Methods ### Method: LayerCollector:GetLayoutNodeTree **Signature:** `LayerCollector:GetLayoutNodeTree(): Dictionary` > **Deprecated:** This method should not be used for new work. *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Returns:** `Dictionary` ## Inherited Members ### From [GuiBase2d](/docs/reference/engine/classes/GuiBase2d.md) - **Property `AbsolutePosition`** (`Vector2`): Describes the actual screen position of a GuiBase2d element, in - **Property `AbsoluteRotation`** (`float`): Describes the actual screen rotation of a GuiBase2d element, in - **Property `AbsoluteSize`** (`Vector2`): Describes the actual screen size of a GuiBase2d element, in - **Property `AutoLocalize`** (`boolean`): When set to `true`, localization will be applied to this GuiBase2d - **Property `Localize`** (`boolean`): Automatically set to true when a localization table's *(deprecated, hidden)* - **Property `RootLocalizationTable`** (`LocalizationTable`): A reference to a LocalizationTable to be used to apply automated - **Property `SelectionBehaviorDown`** (`SelectionBehavior`): Customizes gamepad selection behavior in the down direction. - **Property `SelectionBehaviorLeft`** (`SelectionBehavior`): Customizes gamepad selection behavior in the left direction. - **Property `SelectionBehaviorRight`** (`SelectionBehavior`): Customizes gamepad selection behavior in the right direction. - **Property `SelectionBehaviorUp`** (`SelectionBehavior`): Customizes gamepad selection behavior in the up direction. - **Property `SelectionGroup`** (`boolean`): Allows customization of gamepad selection movement. - **Event `SelectionChanged`**: Fires when the gamepad selection moves to, leaves, or changes within the ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Light last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable summary: "Light is a root class for dynamic lighting related objects." --- # Class: Light > Light is a root class for dynamic lighting related objects. ## Properties ### Property: Light.Brightness ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` Sets how bright the emitted light is, defaults to 1. ### Property: Light.Color ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` The color of the emitted light. ### Property: Light.Enabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` If set to true, light will be emitted from the source object. ### Property: Light.Shadows ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` If set to true, will project shadows if light is blocked by an obstacle. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Lighting last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service summary: "The `Lighting` service controls global lighting in an experience. It includes a range of adjustable properties that you can use to change how lighting appears and interacts with other objects." --- # Class: Lighting > The `Lighting` service controls global lighting in an experience. It includes > a range of adjustable properties that you can use to change how lighting > appears and interacts with other objects. ## Description The `Lighting` service controls global lighting in an experience. It includes a range of adjustable properties that you can use to change how lighting appears and interacts with other objects, as summarized in [Lighting Properties](/docs/en-us/environment/lighting.md). `Lighting` may also contain an [Atmosphere](/docs/reference/engine/classes/Atmosphere.md) object to render realistic atmospheric effects, including particle density, haze, glare, and color. See [Atmospheric Effects](/docs/en-us/environment/atmosphere.md) for details. In addition, `Lighting` (along with [Workspace.CurrentCamera](/docs/reference/engine/classes/Workspace.md)) may contain [post‑processing effects](/docs/en-us/environment/post-processing-effects.md) such as [SunRaysEffect](/docs/reference/engine/classes/SunRaysEffect.md) and [BlurEffect](/docs/reference/engine/classes/BlurEffect.md). ## Properties ### Property: Lighting.Ambient ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` `Ambient` is the lighting hue applied to areas that are occluded from the sky, such as indoor areas. `Ambient` defaults to `[0, 0, 0]` (black). As long as the red, green, and blue channels of this property do not exceed the corresponding channels in [OutdoorAmbient](/docs/reference/engine/classes/Lighting.md), the change in hue will be reserved for areas occluded from the sun/moon. Note that when [GlobalShadows](/docs/reference/engine/classes/Lighting.md) is disabled, there is no distinction between areas occluded from the sky and non‑occluded areas. In this case, [OutdoorAmbient](/docs/reference/engine/classes/Lighting.md) will be ignored and the hue from the `Ambient` property will be applied everywhere. ### Property: Lighting.Brightness ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` The intensity of illumination in the place. Changing this value will influence the impact of the light source (sun or moon) on the place's lighting. Note that [Ambient](/docs/reference/engine/classes/Lighting.md) and [OutdoorAmbient](/docs/reference/engine/classes/Lighting.md) can also be used to influence how bright a place appears. For example, setting [OutdoorAmbient](/docs/reference/engine/classes/Lighting.md) to `[255, 255, 255]` will make the place appear brighter than its default value of `127, 127, 127` (as it is more white). ### Property: Lighting.ClockTime ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Environment" ] } ``` A numerical representation (in hours) of the current time of day used by `Lighting`. Note that this property does not correspond with the actual time of day and will not change during gameplay unless it has been changed by a script. For a measure of `Lighting` time formatted as a 24-hour string, use [TimeOfDay](/docs/reference/engine/classes/Lighting.md). Changing [TimeOfDay](/docs/reference/engine/classes/Lighting.md) or using [SetMinutesAfterMidnight()](/docs/reference/engine/classes/Lighting.md) will also change this property. ### Property: Lighting.ColorShift_Bottom ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` The hue represented in light reflected in the opposite surfaces to those facing the sun or moon. The surfaces of a [BasePart](/docs/reference/engine/classes/BasePart.md) influenced by `ColorShift_Bottom` depends on the position and orientation of the [BasePart](/docs/reference/engine/classes/BasePart.md) relative to the sun or moon's position. Where the sun is directly overhead a [BasePart](/docs/reference/engine/classes/BasePart.md), the shift in color will only apply to the bottom surface. This effect can be increased or reduced by altering [Brightness](/docs/reference/engine/classes/Lighting.md). Note that [ColorShift_Top](/docs/reference/engine/classes/Lighting.md) and `ColorShift_Bottom` will interact with the [Ambient](/docs/reference/engine/classes/Lighting.md) and [OutdoorAmbient](/docs/reference/engine/classes/Lighting.md) properties if they are greater than `[0, 0, 0]`. Also note that the influence of `ColorShift_Bottom` can be very hard to identify when [GlobalShadows](/docs/reference/engine/classes/Lighting.md) is enabled (default). ### Property: Lighting.ColorShift_Top ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` The hue represented in light reflected from surfaces facing the sun or moon. The surfaces of a [BasePart](/docs/reference/engine/classes/BasePart.md) influenced by `ColorShift_Top` depends on the position and orientation of the [BasePart](/docs/reference/engine/classes/BasePart.md) relative to the sun or moon's position. Where the sun is directly overhead a [BasePart](/docs/reference/engine/classes/BasePart.md), the shift in color will only apply to the top surface. This effect can be increased or reduced by altering [Brightness](/docs/reference/engine/classes/Lighting.md). Note that `ColorShift_Top` and [ColorShift_Bottom](/docs/reference/engine/classes/Lighting.md) will interact with the [Ambient](/docs/reference/engine/classes/Lighting.md) and [OutdoorAmbient](/docs/reference/engine/classes/Lighting.md) properties if they are greater than `[0, 0, 0]`. ### Property: Lighting.EnvironmentDiffuseScale ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` Ambient light that is derived from the environment with a default of `0`. This property is similar to [Ambient](/docs/reference/engine/classes/Lighting.md) and [OutdoorAmbient](/docs/reference/engine/classes/Lighting.md) but it's dynamic and can change according to the sky and time of day. When this property is increased, it's recommended to decrease [Ambient](/docs/reference/engine/classes/Lighting.md) and [OutdoorAmbient](/docs/reference/engine/classes/Lighting.md) accordingly. ### Property: Lighting.EnvironmentSpecularScale ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` Specular light derived from environment with a default of `0`. This property will make smooth objects reflect the environment and it is especially important to make metal look more realistic. ### Property: Lighting.ExposureCompensation ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Exposure", "capabilities": [ "Environment" ] } ``` This property determines the exposure compensation amount which applies a bias to the exposure level of the scene prior to the tonemap step. Defaults to `0` (no exposure compensation) and has a range from `-5` to `5`. A value of `1` indicates twice as much exposure and `-1` means half as much exposure. ### Property: Lighting.ExtendLightRangeTo120 ```json { "type": "RolloutState", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` ### Property: Lighting.FogColor ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Fog", "capabilities": [ "Environment" ] } ``` A [Color3](/docs/reference/engine/datatypes/Color3.md) value giving the hue of `Lighting` fog. Note that fog properties are hidden when `Lighting` contains an [Atmosphere](/docs/reference/engine/classes/Atmosphere.md) object. ### Property: Lighting.FogEnd ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Fog", "capabilities": [ "Environment" ] } ``` The depth from the [Workspace.CurrentCamera](/docs/reference/engine/classes/Workspace.md), in studs, at which fog will be completely opaque. Note that fog properties are hidden when `Lighting` contains an [Atmosphere](/docs/reference/engine/classes/Atmosphere.md) object. ### Property: Lighting.FogStart ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Fog", "capabilities": [ "Environment" ] } ``` The depth from the [Workspace.CurrentCamera](/docs/reference/engine/classes/Workspace.md), in studs, at which fog begins to show. Note that fog properties are hidden when `Lighting` contains an [Atmosphere](/docs/reference/engine/classes/Atmosphere.md) object. ### Property: Lighting.GeographicLatitude ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Environment" ] } ``` The geographic latitude, in degrees, of the scene, influencing the result of `Lighting` time on the position of the sun and moon. When calculating the position of the sun, the earth's tilt is also taken into account. Changing `GeographicLatitude` will alter the position of the sun at every [TimeOfDay](/docs/reference/engine/classes/Lighting.md). If you're looking to obtain the sun or moon's position, use [GetSunDirection()](/docs/reference/engine/classes/Lighting.md) or [GetMoonDirection()](/docs/reference/engine/classes/Lighting.md). ### Property: Lighting.GlobalShadows ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` Toggles voxel-based dynamic lighting in the place. When set to `true`, shadows are rendered in sheltered areas depending on the position of the sun and moon. The lighting hue applied to these sheltered areas is determined by the [Ambient](/docs/reference/engine/classes/Lighting.md) property while the lighting hue in all other areas is determined by the [OutdoorAmbient](/docs/reference/engine/classes/Lighting.md) property. When `false`, shadows are not drawn and no distinction is made between indoor and outdoor areas. As a result, the [Ambient](/docs/reference/engine/classes/Lighting.md) property determines the lighting hue and [OutdoorAmbient](/docs/reference/engine/classes/Lighting.md) will do nothing. Shadows are calculated using a voxel system and each lighting voxel is 4×4×4 studs. This means objects need to be larger than 4×4×4 studs to display a realistic shadow. Shadows are also recalculated when [BaseParts](/docs/reference/engine/classes/BasePart.md) are moving. ### Property: Lighting.LightingStyle ```json { "type": "LightingStyle", "access": "ReadOnly", "security": { "read": "None", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` `LightingStyle` indicates the artistic intent behind lighting in the experience, as an [LightingStyle](/docs/reference/engine/enums/LightingStyle.md) option. ### Property: Lighting.OutdoorAmbient ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` `OutdoorAmbient` is the lighting hue applied to outdoor areas. `OutdoorAmbient` defaults to `[127, 127, 127]`. As long as the red, green, and blue channels of [Ambient](/docs/reference/engine/classes/Lighting.md) do not exceed the corresponding channels in `OutdoorAmbient`, the hue of the lighting in outdoor areas will be determined by this property. The effective `OutdoorAmbient` value is clamped to be greater than or equal to [Ambient](/docs/reference/engine/classes/Lighting.md) in all channels, meaning that if a channel of [Ambient](/docs/reference/engine/classes/Lighting.md) exceeds its corresponding `OutdoorAmbient` channel, the hue of [Ambient](/docs/reference/engine/classes/Lighting.md) will begin to apply to outdoor areas. Note that when [GlobalShadows](/docs/reference/engine/classes/Lighting.md) is disabled, there is no distinction between areas occluded from the sky and non‑occluded areas. In this case, `OutdoorAmbient` will be ignored and the hue from the [Ambient](/docs/reference/engine/classes/Lighting.md) property will be applied everywhere. ### Property: Lighting.PrioritizeLightingQuality ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "None", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` This property indicates whether you prefer lighting/shading quality or view distance to scale down first. As the rendering quality level reduces, a setting of `true` prioritizes features such as advanced shadows and high‑quality shaders at closer distances, while a setting of `false` prioritizes view distance. ### Property: Lighting.ShadowSoftness ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` Controls how blurry the shadows are with a default of `0.2`. This property only works when [Technology](/docs/reference/engine/classes/Lighting.md) mode is [ShadowMap](/docs/reference/engine/enums/Technology.md) or [Future](/docs/reference/engine/enums/Technology.md) and the device is capable of rendering shadow maps. ### Property: Lighting.Technology ```json { "type": "Technology", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` Determines the lighting system for rendering the 3D world. This property is non‑scriptable and only modifiable in Studio. ### Property: Lighting.TimeOfDay ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Environment" ] } ``` A 24-hour string representation of the current time of day used by `Lighting`. For example: ```lua local Lighting = game:GetService("Lighting") Lighting.TimeOfDay = "11:00" -- Set time of day to 11:00 AM Lighting.TimeOfDay = "21:30" -- Set time of day to 9:30 PM ``` Note that this property does not correspond with the real-world time of day and will not change during gameplay unless it has been changed by a script. For a numeric measure of `Lighting` time, use [ClockTime](/docs/reference/engine/classes/Lighting.md). Changing [ClockTime](/docs/reference/engine/classes/Lighting.md) or using [SetMinutesAfterMidnight()](/docs/reference/engine/classes/Lighting.md) will also change this property. ### Property: Lighting.Outlines ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` > **Deprecated:** This item is no longer supported as the outlines feature was removed from the Roblox platform. This property determines whether outlines are enabled or disabled in a place. Outlines can be disabled on a global basis, using this `Lighting` property, or alternatively on a surface-by-surface basis for [BaseParts](/docs/reference/engine/classes/BasePart.md) using [SurfaceType](/docs/reference/engine/enums/SurfaceType.md). Although this property can be set by scripts, it recommended this property is set in Roblox Studio prior to publishing the place. ### Property: Lighting.ShadowColor ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` > **Deprecated:** This item is deprecated and has no current functionality. Do not use it for new work. This is supposed to change the color of player shadows, but currently doesn't do anything. ## Methods ### Method: Lighting:GetMinutesAfterMidnight **Signature:** `Lighting:GetMinutesAfterMidnight(): double` Returns the number of minutes that have passed after midnight for the purposes of lighting. This number will be nearly identical to [ClockTime](/docs/reference/engine/classes/Lighting.md) multiplied by `60`. Note that this number will not always be equal to the value given in [SetMinutesAfterMidnight()](/docs/reference/engine/classes/Lighting.md) as it returns minutes after midnight in the current day. *Security: None · Thread Safety: Safe · Capabilities: Environment* **Returns:** `double` — The number of minutes after midnight. ### Method: Lighting:GetMoonDirection **Signature:** `Lighting:GetMoonDirection(): Vector3` Returns a [Vector3](/docs/reference/engine/datatypes/Vector3.md) representing the direction of the moon from the position `(0, 0, 0)`. Note that when the moon has "set" and is no longer visible, the [Vector3](/docs/reference/engine/datatypes/Vector3.md) returned by this method will continue to point towards the moon below the horizon. [GetSunDirection()](/docs/reference/engine/classes/Lighting.md) is a variant of this method for obtaining the direction of the sun. *Security: None · Thread Safety: Safe · Capabilities: Environment* **Returns:** `Vector3` — [Vector3](/docs/reference/engine/datatypes/Vector3.md) representing the direction of the moon. ### Method: Lighting:GetMoonPhase **Signature:** `Lighting:GetMoonPhase(): float` Returns the moon's current phase. There is no way to change the moon's phase so this will always return `0.75`. *Security: None · Thread Safety: Unsafe · Capabilities: Environment* **Returns:** `float` ### Method: Lighting:GetSunDirection **Signature:** `Lighting:GetSunDirection(): Vector3` Returns a [Vector3](/docs/reference/engine/datatypes/Vector3.md) representing the direction of the sun from the position `(0, 0, 0)`. Note that when the sun has "set" and is no longer visible, the [Vector3](/docs/reference/engine/datatypes/Vector3.md) returned by this method will continue to point towards the sun below the horizon. [GetMoonDirection()](/docs/reference/engine/classes/Lighting.md) is a variant of this method for obtaining the direction of the moon. *Security: None · Thread Safety: Safe · Capabilities: Environment* **Returns:** `Vector3` — [Vector3](/docs/reference/engine/datatypes/Vector3.md) representing the direction of the sun. ### Method: Lighting:SetMinutesAfterMidnight **Signature:** `Lighting:SetMinutesAfterMidnight(minutes: double): ()` Sets [TimeOfDay](/docs/reference/engine/classes/Lighting.md) and [ClockTime](/docs/reference/engine/classes/Lighting.md) to the given number of minutes after midnight. This method allows a numerical value to be used, for example in a day/night cycle [Script](/docs/reference/engine/classes/Script.md), without the need to convert to a string in the format required by [TimeOfDay](/docs/reference/engine/classes/Lighting.md). It also allows values greater than 24 hours to be given that correspond to times in the next day. The following code sample includes a simple day/night cycle script. The speed of time and the initial time can be changed using the `TIME_SPEED` and `START_TIME` parameters. ```lua local Lighting = game:GetService("Lighting") local TIME_SPEED = 60 -- 1 min = 1 hour local START_TIME = 9 -- 9 AM local minutesAfterMidnight = START_TIME * 60 local waitTime = 60 / TIME_SPEED while true do minutesAfterMidnight = minutesAfterMidnight + 1 Lighting:SetMinutesAfterMidnight(minutesAfterMidnight) task.wait(waitTime) end ``` *Security: None · Thread Safety: Unsafe · Capabilities: Environment* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `minutes` | `double` | | The number of minutes after midnight. | **Returns:** `()` ### Method: Lighting:getMinutesAfterMidnight **Signature:** `Lighting:getMinutesAfterMidnight(): double` > **Deprecated:** This method is a deprecated variant of [Lighting:GetMinutesAfterMidnight()](/docs/reference/engine/classes/Lighting.md) which should be used instead. *Security: None · Thread Safety: Unsafe · Capabilities: Environment* **Returns:** `double` ### Method: Lighting:setMinutesAfterMidnight **Signature:** `Lighting:setMinutesAfterMidnight(minutes: double): ()` > **Deprecated:** This method is a deprecated variant of [Lighting:SetMinutesAfterMidnight()](/docs/reference/engine/classes/Lighting.md) which should be used instead. *Security: None · Thread Safety: Unsafe · Capabilities: Environment* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `minutes` | `double` | | | **Returns:** `()` ## Events ### Event: Lighting.LightingChanged **Signature:** `Lighting.LightingChanged(skyChanged: boolean)` This event fires when a `Lighting` property is changed or a [Sky](/docs/reference/engine/classes/Sky.md) is added or removed from `Lighting`, with some exceptions: - Changing [GlobalShadows](/docs/reference/engine/classes/Lighting.md) will not fire this event. - Changing fog properties [FogColor](/docs/reference/engine/classes/Lighting.md), [FogStart](/docs/reference/engine/classes/Lighting.md), or [FogEnd](/docs/reference/engine/classes/Lighting.md) will not fire this event. In cases where this behavior is not desired, the [Object.Changed](/docs/reference/engine/classes/Object.md) event or [Object:GetPropertyChangedSignal()](/docs/reference/engine/classes/Object.md) method can be used. *Security: None · Capabilities: Environment* **Parameters:** | Name | Type | Description | |------|------|-------------| | `skyChanged` | `boolean` | | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: LineForce last_updated: 2026-06-29T19:34:07Z inherits: - Constraint - Instance - Object type: class memory_category: BaseParts summary: "Applies a force along the theoretical line connecting its two Attachments." --- # Class: LineForce > Applies a force along the theoretical line connecting its two > [Attachments](/docs/reference/engine/classes/Attachment.md). ## Description The `LineForce` constraint applies a force along the theoretical line connecting its two [Attachments](/docs/reference/engine/classes/Attachment.md). As the end points (attachments) move, the direction of force will change accordingly. When configuring this constraint, it may be helpful to study [Roblox Units](/docs/en-us/physics/units.md) to understand how Roblox units compare to metric units. #### Force Location By default, force is applied to either parent at its attachment location. If desired, force can be focused at each parent's center of mass by enabling [ApplyAtCenterOfMass](/docs/reference/engine/classes/LineForce.md). #### Inverse Square Law When [InverseSquareLaw](/docs/reference/engine/classes/LineForce.md) is `true`, the force magnitude is multiplied by the inverse square of the distance, meaning the force will increase exponentially as the two attachments get closer together, like magnets. When using this setting, it's recommended that you set a [MaxForce](/docs/reference/engine/classes/LineForce.md) threshold to prevent infinite force if the attachments align precisely. #### Reactionary Force By default, the constraint only applies force to [Attachment0](/docs/reference/engine/classes/Constraint.md), while [Attachment1](/docs/reference/engine/classes/Constraint.md) remains unaffected. However, force can be applied to both attachments in **equal and opposite directions** by enabling [ReactionForceEnabled](/docs/reference/engine/classes/LineForce.md). ## Properties ### Property: LineForce.ApplyAtCenterOfMass ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "LineForce", "capabilities": [ "Physics" ], "simulationAccess": true } ``` When `true`, force is applied at the center of mass of the parent assembly of [Attachment0](/docs/reference/engine/classes/LineForce.md), and the line determining the direction of the force will start at the said center of mass. When `false`, force is applied at [Attachment0](/docs/reference/engine/classes/LineForce.md) and the line determining the direction will also start at [Attachment0](/docs/reference/engine/classes/LineForce.md). ### Property: LineForce.InverseSquareLaw ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "LineForce", "capabilities": [ "Physics" ], "simulationAccess": true } ``` When `true`, the force magnitude is multiplied by the inverse square of the distance. ### Property: LineForce.Magnitude ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "LineForce", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The magnitude of the force. ### Property: LineForce.MaxForce ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "LineForce", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The maximum absolute force that can be applied. This property is enabled only when [InverseSquareLaw](/docs/reference/engine/classes/LineForce.md) is also enabled. This property is mainly used to address the issue that the force of the body mover becomes infinite the closer the two attachments are, causing explosions that can't be prevented by scripts. This property bounds the force's absolute value. ### Property: LineForce.ReactionForceEnabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "LineForce", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Enables a reaction force (equal an opposite) to be applied to the parent of [Attachment1](/docs/reference/engine/classes/Constraint.md). By default line force only applies a force on the parent of [Attachment0](/docs/reference/engine/classes/Constraint.md) and uses [Attachment1](/docs/reference/engine/classes/Constraint.md) as the target direction without any dynamic relationship. ## Inherited Members ### From [Constraint](/docs/reference/engine/classes/Constraint.md) - **Property `Active`** (`boolean`): Indicates if the constraint is currently active in the world. - **Property `Attachment0`** (`Attachment`): The Attachment that is connected to - **Property `Attachment1`** (`Attachment`): The Attachment that is connected to - **Property `Color`** (`BrickColor`): The color of the constraint. - **Property `Enabled`** (`boolean`): Toggles whether or not the constraint is enabled. - **Property `Visible`** (`boolean`): Toggles the constraint's visibility. - **Method `GetDebugAppliedForce(bodyId: int): Vector3`**: *(deprecated)* - **Method `GetDebugAppliedTorque(bodyId: int): Vector3`**: *(deprecated)* ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: LineHandleAdornment last_updated: 2026-06-29T19:34:07Z inherits: - HandleAdornment - PVAdornment - GuiBase3d - GuiBase - Instance - Object type: class memory_category: Instances summary: "A line-shaped handle that can be adorned to a BasePart." --- # Class: LineHandleAdornment > A line-shaped handle that can be adorned to a [BasePart](/docs/reference/engine/classes/BasePart.md). ## Description `LineHandleAdornment` is a line-shaped handle that can be adorned to a [BasePart](/docs/reference/engine/classes/BasePart.md). This line starts at the center of the adornment's [CFrame](/docs/reference/engine/classes/LineHandleAdornment.md) (offset by its [SizeRelativeOffset](/docs/reference/engine/classes/LineHandleAdornment.md)) and is oriented along its [CFrame](/docs/reference/engine/classes/LineHandleAdornment.md). If parented to a player's [PlayerGui](/docs/reference/engine/classes/PlayerGui.md) or the [CoreGui](/docs/reference/engine/classes/CoreGui.md), handles can listen to input events for purposes such as making dragger tools. ## Properties ### Property: LineHandleAdornment.Length ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic", "UI" ] } ``` The length of the line in studs. ### Property: LineHandleAdornment.Thickness ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic", "UI" ] } ``` The thickness of the line in pixels. ## Inherited Members ### From [HandleAdornment](/docs/reference/engine/classes/HandleAdornment.md) - **Property `AdornCullingMode`** (`AdornCullingMode`): Determines whether to automatically cull the adornment. - **Property `AlwaysOnTop`** (`boolean`): Forces this adornment to render on top of all 3D objects in the workspace. - **Property `CFrame`** (`CFrame`): The position and rotation of the object relative to its - **Property `SizeRelativeOffset`** (`Vector3`): The positional offset of the adornment based on the adornee's - **Property `ZIndex`** (`int`): Determines the draw order of this HandleAdornment when - **Event `MouseButton1Down`**: Fires when a player presses down on their left mouse button while hovering - **Event `MouseButton1Up`**: Fires when a player releases their left mouse button while hovering over - **Event `MouseEnter`**: Fires when a player moves their mouse over the adornment. - **Event `MouseLeave`**: Fires when a player moves their mouse out of the adornment. ### From [PVAdornment](/docs/reference/engine/classes/PVAdornment.md) - **Property `Adornee`** (`PVInstance`): The PVInstance which this PVAdornment is attached to. ### From [GuiBase3d](/docs/reference/engine/classes/GuiBase3d.md) - **Property `Color`** (`BrickColor`): Sets the color of a GUI object. *(deprecated, hidden)* - **Property `Color3`** (`Color3`): Sets the color of this GuiBase3d object. - **Property `Transparency`** (`float`): Sets the transparency of this GuiBase3d object. - **Property `Visible`** (`boolean`): Determines whether this GuiBase3d object and its descendants will ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: LinearVelocity last_updated: 2026-06-29T19:34:07Z inherits: - Constraint - Instance - Object type: class memory_category: BaseParts summary: "Applies force on an assembly to maintain a constant linear velocity." --- # Class: LinearVelocity > Applies force on an assembly to maintain a constant linear velocity. ## Description The `LinearVelocity` constraint applies force on an assembly to maintain a **constant** linear velocity. It can be set to apply force along a [Vector3](/docs/reference/engine/datatypes/Vector3.md), line, or 2D plane. Alternatively: - If you want to control the amount of force applied, use a [VectorForce](/docs/reference/engine/classes/VectorForce.md) constraint. - If you only need **initial** linear velocity, set the [AssemblyLinearVelocity](/docs/reference/engine/classes/BasePart.md) property directly on the assembly. When configuring this constraint, it may be helpful to study [Roblox Units](/docs/en-us/physics/units.md) to understand how Roblox units compare to metric units. #### Relativity Application of velocity can be controlled through the constraint's [RelativeTo](/docs/reference/engine/classes/LinearVelocity.md) property. If set to [World](/docs/reference/engine/enums/ActuatorRelativeTo.md), force will be applied in world coordinates, independent of the parent or attachment orientations. If set to [Attachment0](/docs/reference/engine/enums/ActuatorRelativeTo.md) or [Attachment1](/docs/reference/engine/enums/ActuatorRelativeTo.md), force will be applied relative to [Attachment0](/docs/reference/engine/classes/Constraint.md) or [Attachment1](/docs/reference/engine/classes/Constraint.md) respectively. ## Properties ### Property: LinearVelocity.ForceLimitMode ```json { "type": "ForceLimitMode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Limits", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Determines how the constraint force will be limited. When set to [Magnitude](/docs/reference/engine/enums/ForceLimitMode.md), the constraint force will have a magnitude less than [MaxForce](/docs/reference/engine/classes/LinearVelocity.md). When set to [PerAxis](/docs/reference/engine/enums/ForceLimitMode.md), the force along each axis will be less than the corresponding value in [MaxAxesForce](/docs/reference/engine/classes/LinearVelocity.md) when [VelocityConstraintMode](/docs/reference/engine/classes/LinearVelocity.md) is [Vector](/docs/reference/engine/enums/VelocityConstraintMode.md) or the corresponding value in [MaxPlanarAxesForce](/docs/reference/engine/classes/LinearVelocity.md) when [VelocityConstraintMode](/docs/reference/engine/classes/LinearVelocity.md) is [Plane](/docs/reference/engine/enums/VelocityConstraintMode.md). Only used when [ForceLimitsEnabled](/docs/reference/engine/classes/LinearVelocity.md) is `true`. ### Property: LinearVelocity.ForceLimitsEnabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Mode", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Determines if the constraint force will be limited or if the physics solver can apply an unlimited force to achieve the target velocity. When enabled, the constraint force is limited based on [ForceLimitMode](/docs/reference/engine/classes/LinearVelocity.md). When disabled, the physics solver will always apply a force that is large enough to achieve the target velocity. ### Property: LinearVelocity.LineDirection ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Line", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The normalized [Vector3](/docs/reference/engine/datatypes/Vector3.md) direction for constraining the velocity along a line, when [VelocityConstraintMode](/docs/reference/engine/classes/LinearVelocity.md) is set to [Line](/docs/reference/engine/enums/VelocityConstraintMode.md). ### Property: LinearVelocity.LineVelocity ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Line", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Float value of the velocity when [VelocityConstraintMode](/docs/reference/engine/classes/LinearVelocity.md) is set to [Line](/docs/reference/engine/enums/VelocityConstraintMode.md). ### Property: LinearVelocity.MaxAxesForce ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Limits", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Maximum force along each axis that the constraint can apply to achieve the target velocity. Only used if [ForceLimitsEnabled](/docs/reference/engine/classes/LinearVelocity.md) is `true`, [ForceLimitMode](/docs/reference/engine/classes/LinearVelocity.md) is [PerAxis](/docs/reference/engine/enums/ForceLimitMode.md), and [VelocityConstraintMode](/docs/reference/engine/classes/LinearVelocity.md) is [Vector](/docs/reference/engine/enums/VelocityConstraintMode.md). The axes used to apply the limit correspond to the [RelativeTo](/docs/reference/engine/classes/LinearVelocity.md) property. ### Property: LinearVelocity.MaxForce ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Limits", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Maximum magnitude of the force vector the constraint can apply. Only used if [ForceLimitsEnabled](/docs/reference/engine/classes/LinearVelocity.md) is `true` and [ForceLimitMode](/docs/reference/engine/classes/LinearVelocity.md) is [Magnitude](/docs/reference/engine/enums/ForceLimitMode.md). ### Property: LinearVelocity.MaxPlanarAxesForce ```json { "type": "Vector2", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Limits", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Maximum force along each axis that the constraint can apply to achieve the plane velocity. Only used if [ForceLimitsEnabled](/docs/reference/engine/classes/LinearVelocity.md) is `true`, [ForceLimitMode](/docs/reference/engine/classes/LinearVelocity.md) is [PerAxis](/docs/reference/engine/enums/ForceLimitMode.md), and [VelocityConstraintMode](/docs/reference/engine/classes/LinearVelocity.md) is [Plane](/docs/reference/engine/enums/VelocityConstraintMode.md). The axes used to apply the limit correspond to the [RelativeTo](/docs/reference/engine/classes/LinearVelocity.md) property. ### Property: LinearVelocity.PlaneVelocity ```json { "type": "Vector2", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Plane", "capabilities": [ "Physics" ], "simulationAccess": true } ``` [Vector2](/docs/reference/engine/datatypes/Vector2.md) value of the velocity in each tangent direction of the plane, when [VelocityConstraintMode](/docs/reference/engine/classes/LinearVelocity.md) is set to [Plane](/docs/reference/engine/enums/VelocityConstraintMode.md). ### Property: LinearVelocity.PrimaryTangentAxis ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Plane", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The primary axis in the plane, when [VelocityConstraintMode](/docs/reference/engine/classes/LinearVelocity.md) is set to [Plane](/docs/reference/engine/enums/VelocityConstraintMode.md). Value depends on the value of [RelativeTo](/docs/reference/engine/classes/LinearVelocity.md) as follows: - If [RelativeTo](/docs/reference/engine/classes/LinearVelocity.md) is set to [Attachment0](/docs/reference/engine/enums/ActuatorRelativeTo.md), this axis is the [Axis](/docs/reference/engine/classes/Attachment.md) of [Attachment0](/docs/reference/engine/classes/Constraint.md). - If [RelativeTo](/docs/reference/engine/classes/LinearVelocity.md) is set to [Attachment1](/docs/reference/engine/enums/ActuatorRelativeTo.md), this axis is the [Axis](/docs/reference/engine/classes/Attachment.md) of [Attachment1](/docs/reference/engine/classes/Constraint.md). - If [RelativeTo](/docs/reference/engine/classes/LinearVelocity.md) is set to [World](/docs/reference/engine/enums/ActuatorRelativeTo.md), this value must be specified in the world space. ### Property: LinearVelocity.ReactionForceEnabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Mode", "capabilities": [ "Physics" ], "simulationAccess": true } ``` ### Property: LinearVelocity.RelativeTo ```json { "type": "ActuatorRelativeTo", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Mode", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Sets the [ActuatorRelativeTo](/docs/reference/engine/enums/ActuatorRelativeTo.md) property for the constraint. ### Property: LinearVelocity.SecondaryTangentAxis ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Plane", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The secondary axis in the plane, when [VelocityConstraintMode](/docs/reference/engine/classes/LinearVelocity.md) is set to [Plane](/docs/reference/engine/enums/VelocityConstraintMode.md). Value depends on the value of [RelativeTo](/docs/reference/engine/classes/LinearVelocity.md) as follows: - If [RelativeTo](/docs/reference/engine/classes/LinearVelocity.md) is set to [Attachment0](/docs/reference/engine/enums/ActuatorRelativeTo.md), this axis is the [SecondaryAxis](/docs/reference/engine/classes/Attachment.md) of [Attachment0](/docs/reference/engine/classes/Constraint.md). - If [RelativeTo](/docs/reference/engine/classes/LinearVelocity.md) is set to [Attachment1](/docs/reference/engine/enums/ActuatorRelativeTo.md), this axis is the [SecondaryAxis](/docs/reference/engine/classes/Attachment.md) of [Attachment1](/docs/reference/engine/classes/Constraint.md). - If [RelativeTo](/docs/reference/engine/classes/LinearVelocity.md) is set to [World](/docs/reference/engine/enums/ActuatorRelativeTo.md), this value must be specified in the world space. ### Property: LinearVelocity.VectorVelocity ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Vector", "capabilities": [ "Physics" ], "simulationAccess": true } ``` [Vector3](/docs/reference/engine/datatypes/Vector3.md) velocity value when [VelocityConstraintMode](/docs/reference/engine/classes/LinearVelocity.md) is set to [Vector](/docs/reference/engine/enums/VelocityConstraintMode.md). ### Property: LinearVelocity.VelocityConstraintMode ```json { "type": "VelocityConstraintMode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Mode", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The mode of the constraint: [Line](/docs/reference/engine/enums/VelocityConstraintMode.md), [Plane](/docs/reference/engine/enums/VelocityConstraintMode.md), or [Vector](/docs/reference/engine/enums/VelocityConstraintMode.md). Default is [Vector](/docs/reference/engine/enums/VelocityConstraintMode.md). ## Inherited Members ### From [Constraint](/docs/reference/engine/classes/Constraint.md) - **Property `Active`** (`boolean`): Indicates if the constraint is currently active in the world. - **Property `Attachment0`** (`Attachment`): The Attachment that is connected to - **Property `Attachment1`** (`Attachment`): The Attachment that is connected to - **Property `Color`** (`BrickColor`): The color of the constraint. - **Property `Enabled`** (`boolean`): Toggles whether or not the constraint is enabled. - **Property `Visible`** (`boolean`): Toggles the constraint's visibility. - **Method `GetDebugAppliedForce(bodyId: int): Vector3`**: *(deprecated)* - **Method `GetDebugAppliedTorque(bodyId: int): Vector3`**: *(deprecated)* ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: LocalScript last_updated: 2026-06-29T19:34:07Z inherits: - Script - BaseScript - LuaSourceContainer - Instance - Object type: class memory_category: Script summary: "An object that contains and runs Luau code on the client (player's device) instead of the server." --- # Class: LocalScript > An object that contains and runs Luau code on the client (player's device) > instead of the server. ## Description A [LocalScript](/docs/reference/engine/classes/LocalScript.md) is a Luau source container that runs its contents on the client (player's device) instead of the server. It is used to access client-only objects such as the player's [Camera](/docs/reference/engine/classes/Camera.md). For code run through [LocalScripts](/docs/reference/engine/classes/LocalScript.md), the [LocalPlayer](/docs/reference/engine/classes/Players.md) property of the [Players](/docs/reference/engine/classes/Players.md) service will return the player whose client is running the script. See [here](/docs/en-us/projects/data-model.md#client) for a table of valid container services from which [LocalScripts](/docs/reference/engine/classes/LocalScript.md) will execute. ## Inherited Members ### From [Script](/docs/reference/engine/classes/Script.md) - **Property `Source`** (`ProtectedString`): The code to be executed. ### From [BaseScript](/docs/reference/engine/classes/BaseScript.md) - **Property `Disabled`** (`boolean`): Determines whether a BaseScript will run or not. - **Property `Enabled`** (`boolean`): Determines whether a BaseScript will run or not. - **Property `LinkedSource`** (`ContentId`): The content ID of an uploaded script. When set binds the uploaded code to *(deprecated)* - **Property `RunContext`** (`RunContext`): Determines the context under which the script will run. ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: LocalizationService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated summary: "Handles automated translation." --- # Class: LocalizationService > Handles automated translation. ## Description LocalizationService is the service responsible for handling automated translation. It is used as a storage for [LocalizationTable](/docs/reference/engine/classes/LocalizationTable.md) objects used by automatic text replacement. LocalizationService will only use its child LocalizationTables for automatic text replacement unless [GuiBase2d.RootLocalizationTable](/docs/reference/engine/classes/GuiBase2d.md) is specified on a GUI object or its ancestors. ## Properties ### Property: LocalizationService.RobloxLocaleId ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Localization", "capabilities": [ "Basic" ] } ``` This property shows the locale ID used for the localization of core and internal features such as [CoreGui](/docs/reference/engine/classes/CoreGui.md). Returns a string with the two letter code (for example, `en-us`) for the locale. ### Property: LocalizationService.SystemLocaleId ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Localization", "capabilities": [ "Basic" ] } ``` This property shows the locale ID that the local player has set for their operating system. This will return a string with the two letter code (for example, "en-us") for the locale. See also [Player.LocaleId](/docs/reference/engine/classes/Player.md), the locale ID that a user has set for their Roblox account which is used for localizing in-experience content. This will be a different value when Roblox does not yet internally support that player's locale. ## Methods ### Method: LocalizationService:GetCorescriptLocalizations **Signature:** `LocalizationService:GetCorescriptLocalizations(): Instances` Returns a list of [LocalizationTable](/docs/reference/engine/classes/LocalizationTable.md) objects used for localizing core scripts. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Returns:** `Instances` ### Method: LocalizationService:GetCountryRegionForPlayerAsync **Signature:** `LocalizationService:GetCountryRegionForPlayerAsync(player: Instance): string` Returns a country/region code string according to player's client IP geolocation. The supported country/region codes are as follows: | Code | Country/Region | | --- | --- | | US | United States | | GB | United Kingdom | | CA | Canada | | AF | Afghanistan | | AX | Aland Islands | | AL | Albania | | DZ | Algeria | | AS | American Samoa | | AD | Andorra | | AO | Angola | | AI | Anguilla | | AQ | Antarctica | | AG | Antigua and Barbuda | | AR | Argentina | | AM | Armenia | | AW | Aruba | | AU | Australia | | AT | Austria | | AZ | Azerbaijan | | BS | Bahamas | | BH | Bahrain | | BD | Bangladesh | | BB | Barbados | | BY | Belarus | | BE | Belgium | | BZ | Belize | | BJ | Benin | | BM | Bermuda | | BT | Bhutan | | BO | Bolivia | | BQ | Bonaire, Saint Eustatius and Saba | | BA | Bosnia and Herzegovina | | BW | Botswana | | BV | Bouvet Island | | BR | Brazil | | IO | British Indian Ocean Territory | | BN | Brunei Darussalam | | BG | Bulgaria | | BF | Burkina Faso | | BI | Burundi | | KH | Cambodia | | CM | Cameroon | | CV | Cape Verde | | KY | Cayman Islands | | CF | Central African Republic | | TD | Chad | | CL | Chile | | CN | China | | CX | Christmas Island | | CC | Cocos Islands | | CO | Colombia | | KM | Comoros | | CG | Congo | | CD | Congo (DRC) | | CK | Cook Islands | | CR | Costa Rica | | CI | Ivory Coast | | HR | Croatia | | CW | Curaçao | | CY | Cyprus | | CZ | Czech Republic | | DK | Denmark | | DJ | Djibouti | | DM | Dominica | | DO | Dominican Republic | | EC | Ecuador | | EG | Egypt | | SV | El Salvador | | GQ | Equatorial Guinea | | ER | Eritrea | | EE | Estonia | | ET | Ethiopia | | FK | Falkland Islands (Malvinas) | | FO | Faroe Islands | | FJ | Fiji | | FI | Finland | | FR | France | | GF | French Guiana | | PF | French Polynesia | | TF | French Southern Territories | | GA | Gabon | | GM | Gambia | | GE | Georgia | | DE | Germany | | Code | Country/Region | | --- | --- | | GH | Ghana | | GI | Gibraltar | | GR | Greece | | GL | Greenland | | GD | Grenada | | GP | Guadeloupe | | GU | Guam | | GT | Guatemala | | GG | Guernsey | | GN | Guinea | | GW | Guinea-Bissau | | GY | Guyana | | HT | Haiti | | HM | Heard Island and the McDonald Islands | | VA | Holy See | | HN | Honduras | | HK | Hong Kong | | HU | Hungary | | IS | Iceland | | IN | India | | ID | Indonesia | | IQ | Iraq | | IE | Ireland | | IM | Isle of Man | | IL | Israel | | IT | Italy | | JM | Jamaica | | JP | Japan | | JE | Jersey | | JO | Jordan | | KZ | Kazakhstan | | KE | Kenya | | KI | Kiribati | | KR | Korea | | KW | Kuwait | | KG | Kyrgyzstan | | LA | Laos | | LV | Latvia | | LB | Lebanon | | LS | Lesotho | | LR | Liberia | | LY | Libya | | LI | Liechtenstein | | LT | Lithuania | | LU | Luxembourg | | MO | Macao | | MK | Macedonia | | MG | Madagascar | | MW | Malawi | | MY | Malaysia | | MV | Maldives | | ML | Mali | | MT | Malta | | MH | Marshall Islands | | MQ | Martinique | | MR | Mauritania | | MU | Mauritius | | YT | Mayotte | | MX | Mexico | | FM | Micronesia | | MD | Moldova | | MC | Monaco | | MN | Mongolia | | ME | Montenegro | | MS | Montserrat | | MA | Morocco | | MZ | Mozambique | | MM | Myanmar | | NA | Namibia | | NR | Nauru | | NP | Nepal | | NL | Netherlands | | AN | Netherlands Antilles | | NC | New Caledonia | | NZ | New Zealand | | NI | Nicaragua | | NE | Niger | | NG | Nigeria | | NU | Niue | | NF | Norfolk Island | | MP | Northern Mariana Islands | | NO | Norway | | OM | Oman | | Code | Country/Region | | --- | --- | | PK | Pakistan | | PW | Palau | | PS | Palestine | | PA | Panama | | PG | Papua New Guinea | | PY | Paraguay | | PE | Peru | | PH | Philippines | | PN | Pitcairn Islands | | PL | Poland | | PT | Portugal | | PR | Puerto Rico | | QA | Qatar | | RE | Reunion | | RO | Romania | | RU | Russian Federation | | RW | Rwanda | | BL | Saint Barthelemy | | SH | Saint Helena, Ascension and Tristan da Cunha | | KN | Saint Kitts and Nevis | | LC | Saint Lucia | | MF | Saint Martin | | PM | Saint Pierre and Miquelon | | VC | Saint Vincent and the Grenadines | | WS | Samoa | | SM | San Marino | | ST | Sao Tome and Principe | | SA | Saudi Arabia | | SN | Senegal | | RS | Serbia | | SC | Seychelles | | SL | Sierra Leone | | SG | Singapore | | SX | Sint Maarten | | SK | Slovakia | | SI | Slovenia | | SB | Solomon Islands | | SO | Somalia | | ZA | South Africa | | GS | South Georgia and the South Sandwich Islands | | SS | South Sudan | | ES | Spain | | LK | Sri Lanka | | SR | Suriname | | SJ | Svalbard and Jan Mayen | | SZ | Swaziland | | SE | Sweden | | CH | Switzerland | | TW | Taiwan | | TJ | Tajikistan | | TZ | Tanzania | | TH | Thailand | | TL | Timor-Leste | | TG | Togo | | TK | Tokelau | | TO | Tonga | | TT | Trinidad and Tobago | | TN | Tunisia | | TR | Türkiye (Turkey) | | TM | Turkmenistan | | TC | Turks and Caicos Islands | | TV | Tuvalu | | UG | Uganda | | UA | Ukraine | | AE | United Arab Emirates | | UM | United States Minor Outlying Islands | | UY | Uruguay | | UZ | Uzbekistan | | VU | Vanuatu | | VE | Venezuela | | VN | Vietnam | | VG | Virgin Islands (British) | | VI | Virgin Islands (US) | | WF | Wallis and Futuna | | EH | Western Sahara | | YE | Yemen | | ZM | Zambia | | ZW | Zimbabwe | | CU | Cuba | | IR | Iran | | SY | Syria | | KP | North Korea | See also: - [PolicyService:GetPolicyInfoForPlayerAsync()](/docs/reference/engine/classes/PolicyService.md), returns policy information about a player which is based on geolocation, age group and platform *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Instance` | | The player that you are getting country/region information for. | **Returns:** `string` — A string indicating the country/region code of a player. **Getting Country/Region Code for a Player** This code sample gets the country/region code for a local player and prints "Hello, friend from Canada!" if the player's client IP geolocation is Canada. ```lua local LocalizationService = game:GetService("LocalizationService") local Players = game:GetService("Players") local player = Players.LocalPlayer local result, code = pcall(LocalizationService.GetCountryRegionForPlayerAsync, LocalizationService, player) if result and code == "CA" then print("Hello, friend from Canada!") else print("GetCountryRegionForPlayerAsync failed: " .. code) end ``` ### Method: LocalizationService:GetTableEntries **Signature:** `LocalizationService:GetTableEntries(instance?: Instance): Array` Returns an `Array`, where each element of the returned `Array` is itself an `Array` of entries in the same format as described in [LocalizationTable:GetEntries()](/docs/reference/engine/classes/LocalizationTable.md). The order of the elements in the returned `Array` is the same order that the [LocalizationTables](/docs/reference/engine/classes/LocalizationTable.md) will be searched through to attempt automated localization for the provided [Instance](/docs/reference/engine/classes/Instance.md). The entry elements within a particular [LocalizationTable](/docs/reference/engine/classes/LocalizationTable.md) are returned in an unspecified order. This function returns entries regardless of whether the object is a [GuiBase2d](/docs/reference/engine/classes/GuiBase2d.md) with [GuiBase2d.AutoLocalize](/docs/reference/engine/classes/GuiBase2d.md) enabled. An object that is a [GuiBase2d](/docs/reference/engine/classes/GuiBase2d.md) will not actually be automatically localized unless [GuiBase2d.AutoLocalize](/docs/reference/engine/classes/GuiBase2d.md) is enabled. The ordering of the tables is as follows: - First, it looks for the earliest [GuiBase2d](/docs/reference/engine/classes/GuiBase2d.md) ancestor of the object (including the provided object) that has a [GuiBase2d.RootLocalizationTable](/docs/reference/engine/classes/GuiBase2d.md). Tables then append in the same order as described in [GuiBase2d.RootLocalizationTable](/docs/reference/engine/classes/GuiBase2d.md) by going up through the [LocalizationTable](/docs/reference/engine/classes/LocalizationTable.md) ancestors of that [GuiBase2d.RootLocalizationTable](/docs/reference/engine/classes/GuiBase2d.md). If no such [GuiBase2d.RootLocalizationTable](/docs/reference/engine/classes/GuiBase2d.md) is found, no tables append in this step. If `instance` is `nil`, no tables append in this step. - Next, tables from the [LocalizationTable](/docs/reference/engine/classes/LocalizationTable.md) hierarchy under [LocalizationService](/docs/reference/engine/classes/LocalizationService.md) append. For each child [LocalizationTable](/docs/reference/engine/classes/LocalizationTable.md) of [LocalizationService](/docs/reference/engine/classes/LocalizationService.md), it appends tables going up from the lowest descendant [LocalizationTable](/docs/reference/engine/classes/LocalizationTable.md) of the tables parented to the service, all the way up to the children of the service. If there are no children of [LocalizationService](/docs/reference/engine/classes/LocalizationService.md) that are [LocalizationTables](/docs/reference/engine/classes/LocalizationTable.md), then no tables append in this step. - Finally, the cloud [LocalizationTable](/docs/reference/engine/classes/LocalizationTable.md) appends to the array. If there is no cloud [LocalizationTable](/docs/reference/engine/classes/LocalizationTable.md), or the cloud [LocalizationTable](/docs/reference/engine/classes/LocalizationTable.md) has not yet loaded, then no table appends in this step. This function does not yield. It will not wait until the cloud [LocalizationTable](/docs/reference/engine/classes/LocalizationTable.md) has loaded. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `instance` | `Instance` | `nil` | | **Returns:** `Array` — An array of arrays, where each array is in the same format as described in [LocalizationTable:GetEntries()](/docs/reference/engine/classes/LocalizationTable.md). ### Method: LocalizationService:GetTranslatorForLocaleAsync **Signature:** `LocalizationService:GetTranslatorForLocaleAsync(locale: string): Instance` This function takes a locale code as an argument and yields until the cloud [LocalizationTable](/docs/reference/engine/classes/LocalizationTable.md) for that locale has been loaded, if available. It then returns a [Translator](/docs/reference/engine/classes/Translator.md) object which can be used to perform translations for that locale if any are available. The entries used for localization are the entries provided by the [LocalizationTable](/docs/reference/engine/classes/LocalizationTable.md) hierarchy under [LocalizationService](/docs/reference/engine/classes/LocalizationService.md) as well as the cloud table (if available). This will be the same set of entries returned by [LocalizationService:GetTableEntries(nil)](/docs/reference/engine/classes/LocalizationService.md). This function can error and thus should be wrapped in a `pcall()`. See also: - [LocalizationService:GetTranslatorForPlayer()](/docs/reference/engine/classes/LocalizationService.md) gets the translator corresponding to the locale of the provided player. This function is deprecated and should not be used in new work. - [LocalizationService:GetTranslatorForPlayerAsync()](/docs/reference/engine/classes/LocalizationService.md) yields until the cloud [LocalizationTable](/docs/reference/engine/classes/LocalizationTable.md) for the locale of the provided player has loaded and then gets the translator corresponding to the locale of the provided player. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `locale` | `string` | | A Roblox supported language or locale code. | **Returns:** `Instance` — The [Translator](/docs/reference/engine/classes/Translator.md) instance for the specified locale. **Getting and Using a Translator for a Locale** This code sample attempts to retrieve a `Translator` object for the locale "fr" (French). [LocalizationService:GetTranslatorForLocaleAsync()](/docs/reference/engine/classes/LocalizationService.md) is wrapped in a pcall because it may error. If it does not error and returns a Translator, prints "Hello in French:" followed by the French translation of "Hello World!". If the function errors, it prints "GetTranslatorForLocaleAsync failed:" followed by the error message. ```lua local LocalizationService = game:GetService("LocalizationService") local textLabel = script.Parent local success, translator = pcall(function() return LocalizationService:GetTranslatorForLocaleAsync("fr") end) if success then local result = translator:Translate(textLabel, "Hello World!") print("Hello in French: " .. result) else print("GetTranslatorForLocaleAsync failed: " .. translator) end ``` ### Method: LocalizationService:GetTranslatorForPlayer **Signature:** `LocalizationService:GetTranslatorForPlayer(player: Instance): Instance` This function takes a player as an argument and returns a [Translator](/docs/reference/engine/classes/Translator.md) instance which can be used to perform translations for that locale if any are available. The entries used for localization are the entries provided by the [LocalizationTable](/docs/reference/engine/classes/LocalizationTable.md) hierarchy under [LocalizationService](/docs/reference/engine/classes/LocalizationService.md) as well as the cloud [LocalizationTable](/docs/reference/engine/classes/LocalizationTable.md), if it is available and already loaded. This will be the same set of entries returned by [LocalizationService:GetTableEntries(nil)](/docs/reference/engine/classes/LocalizationService.md). This function does not yield. It will not wait until the cloud [LocalizationTable](/docs/reference/engine/classes/LocalizationTable.md) has loaded. See also: - [LocalizationService:GetTranslatorForPlayerAsync()](/docs/reference/engine/classes/LocalizationService.md) has the same functionality as this function, except that it yields until the cloud [LocalizationTable](/docs/reference/engine/classes/LocalizationTable.md) has been loaded. - [LocalizationService:GetTranslatorForLocaleAsync()](/docs/reference/engine/classes/LocalizationService.md), returns a Translator to be used for translations using the provided locale. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Instance` | | The [Player](/docs/reference/engine/classes/Player.md) that you are getting the [Translator](/docs/reference/engine/classes/Translator.md) for. | **Returns:** `Instance` — The [Translator](/docs/reference/engine/classes/Translator.md) instance for the specified locale. **Getting and Using a Translator for a Player** This code sample attempts to retrieve a `Translator` object for the local player. [LocalizationService:GetTranslatorForPlayerAsync()](/docs/reference/engine/classes/LocalizationService.md) is wrapped in a pcall because it may error. If it does not error and returns a Translator, it translates and prints "Hello World!" in the player's language. If the function errors, it prints "GetTranslatorForLocaleAsync failed:" followed by the error message. [LocalizationService:GetTranslatorForPlayer()](/docs/reference/engine/classes/LocalizationService.md) can also be used if you'd like to get the player's translator without yielding until the function returns. ```lua local LocalizationService = game:GetService("LocalizationService") local Players = game:GetService("Players") local textLabel = script.Parent local success, translator = pcall(function() return LocalizationService:GetTranslatorForPlayerAsync(Players.LocalPlayer) end) if success then local result = translator:Translate(textLabel, "Hello World!") print(result) else print("GetTranslatorForPlayerAsync failed: " .. translator) end ``` ### Method: LocalizationService:GetTranslatorForPlayerAsync **Signature:** `LocalizationService:GetTranslatorForPlayerAsync(player: Instance): Instance` This function takes a player as an argument and yields until the cloud [LocalizationTable](/docs/reference/engine/classes/LocalizationTable.md) for that player's locale has been loaded, if available. It then returns a [Translator](/docs/reference/engine/classes/Translator.md) object which can be used to perform translations for that locale if any are available. The entries used for localization are the entries provided by the [LocalizationTable](/docs/reference/engine/classes/LocalizationTable.md) hierarchy under [LocalizationService](/docs/reference/engine/classes/LocalizationService.md) as well as the cloud table (if available). This will be the same set of entries returned by [LocalizationService:GetTableEntries(nil)](/docs/reference/engine/classes/LocalizationService.md). This function can error and thus should be wrapped in a `pcall()`. See also: - [LocalizationService:GetTranslatorForPlayer()](/docs/reference/engine/classes/LocalizationService.md), same functionality as this function except that it does not yield and does not wait until the cloud [LocalizationTable](/docs/reference/engine/classes/LocalizationTable.md) for the player's locale has been loaded. This function is deprecated and should not be used in new work. - [LocalizationService:GetTranslatorForLocaleAsync()](/docs/reference/engine/classes/LocalizationService.md), returns a Translator to be used for translations using the provided locale. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Instance` | | The [Player](/docs/reference/engine/classes/Player.md) that you are getting the [Translator](/docs/reference/engine/classes/Translator.md) for. | **Returns:** `Instance` — The [Translator](/docs/reference/engine/classes/Translator.md) instance for the specified locale. **Getting and Using a Translator for a Player** This code sample attempts to retrieve a `Translator` object for the local player. [LocalizationService:GetTranslatorForPlayerAsync()](/docs/reference/engine/classes/LocalizationService.md) is wrapped in a pcall because it may error. If it does not error and returns a Translator, it translates and prints "Hello World!" in the player's language. If the function errors, it prints "GetTranslatorForLocaleAsync failed:" followed by the error message. [LocalizationService:GetTranslatorForPlayer()](/docs/reference/engine/classes/LocalizationService.md) can also be used if you'd like to get the player's translator without yielding until the function returns. ```lua local LocalizationService = game:GetService("LocalizationService") local Players = game:GetService("Players") local textLabel = script.Parent local success, translator = pcall(function() return LocalizationService:GetTranslatorForPlayerAsync(Players.LocalPlayer) end) if success then local result = translator:Translate(textLabel, "Hello World!") print(result) else print("GetTranslatorForPlayerAsync failed: " .. translator) end ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: LocalizationTable last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances summary: "A LocalizationTable is a database of translations. It contains source strings and translations for various languages." --- # Class: LocalizationTable > A LocalizationTable is a database of translations. It contains source strings > and translations for various languages. ## Description A LocalizationTable is a database of translations. It contains source strings and translations for various languages. It is used with the [Translator](/docs/reference/engine/classes/Translator.md) and [LocalizationService](/docs/reference/engine/classes/LocalizationService.md) auto-translator system to control text translations in the game. LocalizationTables are designed to be treated as resources, like a texture or a script. They are not optimized to be modified at runtime. Changing the contents of a table will cause the entire contents of the table to be replicated to all players. ## LocalizationTable Entries Each LocalizationTable contains a set of entries. Each entry contains the translations of the text, along with some special fields: - **Key** is an optional unique key for fast hash lookups in code. If it is non-empty it must be unique in the table. - **Source** is the original text in the source language that will be used by the [LocalizationService](/docs/reference/engine/classes/LocalizationService.md) automatic text replacement system to match GUI text and render a translation instead. The Source field can be filled by the text capture tools, or can be set manually. For key-based lookups the Source value can be used as a translation for [LocalizationTable.SourceLocaleId](/docs/reference/engine/classes/LocalizationTable.md) if the entry doesn't have a translation for that locale. If Source is empty then the entry will not be used by the automatic replacement system. - **Context** is the full Instance name for the object that the text appeared on. Context is used for disambiguation by the automatic text replacement system. When multiple matches for the Source are found, the system will pick the best match by matching backwards from the end of the Context string. There are other more robust ways to handle disambiguation available as well, like using multiple tables with [GuiBase2d.RootLocalizationTable](/docs/reference/engine/classes/GuiBase2d.md). - **Example** is whatever you want it to be. If the text capture tool guessed some parameters for a string the Example field will contain an example of them used in context. All of these fields are optional, but at least either Key or Source must be non-empty. No two entries can have the same Key, Source, and Context. See [Translating Dynamic Content](/docs/en-us/production/localization/translate-dynamic-content.md) for more information. ## Code Samples **LocalizationTable** ```lua local LocalizationService = game:GetService("LocalizationService") local function createLocalizationTable(entries): LocalizationTable local localTable = Instance.new("LocalizationTable") localTable.SourceLocaleId = LocalizationService.SystemLocaleId localTable:SetEntries(entries) return localTable end local entries = { { Key = "Hello_World", -- The 'expressionKey' to be used with GetString Values = { -- A dictionary of keys corresponding to IETF language tags, and their translations. ["ru"] = " !", -- Russian ["fr"] = "Bonjour le monde!", -- French ["de"] = "Hallo Welt!", -- German ["en-US"] = "Hello world!", -- English ["it"] = "Ciao mondo!", -- Italian ["pt-BR"] = "Ol Mundo!", -- Portuguese ["ja"] = "", -- Japanese ["es"] = "Hola Mundo!", -- Spanish }, }, } local helloWorldTable = createLocalizationTable(entries) local translatorEnglish = helloWorldTable:GetTranslator("en-US") print(translatorEnglish:FormatByKey("Hello_World")) ``` ## Properties ### Property: LocalizationTable.SourceLocaleId ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Localization", "capabilities": [ "Basic" ] } ``` The Roblox locale of the input key strings for this table, for example "en-us" or "es-es." This is typically the "development language" of the game. For a Translator that merges multiple LocalizationTable objects, it's the LocaleId of the Default LocalizationTable. Defaults to "en-us". ### Property: LocalizationTable.DevelopmentLanguage *(hidden)* ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Localization", "capabilities": [ "Basic" ] } ``` > **Deprecated:** This item has been superseded by [LocalizationTable.SourceLocaleId](/docs/reference/engine/classes/LocalizationTable.md) which should be used in all new work. The default IETF tag to use if the ''languageKey'' parameter is excluded from the [LocalizationTable:GetString()](/docs/reference/engine/classes/LocalizationTable.md) method. ### Property: LocalizationTable.Root *(hidden)* ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Basic" ] } ``` > **Deprecated:** This item is deprecated. Do not use it for new work. The object that is being targeted for localization by this table. Localization is applied to it and all of it's descendants. ## Methods ### Method: LocalizationTable:GetEntries **Signature:** `LocalizationTable:GetEntries(): Array` The GetEntries function returns an array of dictionaries contained in a given [LocalizationTable](/docs/reference/engine/classes/LocalizationTable.md), where each dictionary represents an entry of localization data. To set the entries of a LocalizationTable, you can use [LocalizationTable:SetEntries()](/docs/reference/engine/classes/LocalizationTable.md). Each dictionary in the array contains the following fields: | Index | Type | Description | | --- | --- | --- | | **Key** | [string](/docs/reference/engine/globals/string.md) | A lookup key for this specific entry in the LocalizationTable. | | **Source** | [string](/docs/reference/engine/globals/string.md) | The string used to format the localized string. Used as a lookup if a key is not provided. | | **Context** | [string](/docs/reference/engine/globals/string.md) | An [Instance:GetFullName()](/docs/reference/engine/classes/Instance.md) path to the object that was used to generate the LocalizationTable. Used as a lookup if a key is not provided. | | **Example** | [string](/docs/reference/engine/globals/string.md) | The string used to format the localization. Optional. | | **Values** | `Dictionary` | A dictionary of language translations for this localization entry. The keys of this dictionary are locale ids, and the values are strings that are used to apply localization for the language corresponding to the locale id. | *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Returns:** `Array` — An array of dictionaries, where each dictionary represents an entry of localization data. **Using a LocalizationTable** The following code sample creates a `LocalizationTable`, sets its entries, then gets and displays its entries. In order for this example to work, a LocalizationTable instance must be located inside the `LocalizationService` service. The _entries_ variable is a table of dictionaries, each with the format required to create a LocalizationTable with [LocalizationTable:SetEntries()](/docs/reference/engine/classes/LocalizationTable.md). The _get_results_ variable is a table of dictionaries - the same table that we created with the _entries_ variable. We then loop through each of the tables in this dictionary to display its _Values_/strings. ```lua local LocalizationService = game:GetService("LocalizationService") local localizationTable = LocalizationService:FindFirstChild("LocalizationTable") local entries = { { ["Key"] = "0001", ["Source"] = "en-us", ["Values"] = { ["0001"] = "Hello Muddah, hello Fadduh.", ["0002"] = "Here I am at Camp Granada.", ["0003"] = "Camp is very entertaining.", ["0004"] = "And they say we'll have some fun if it stops raining.", }, }, } localizationTable:SetEntries(entries) local get_results = localizationTable:GetEntries() for _index, dict in pairs(get_results) do for _key, value in pairs(dict["Values"]) do -- Loop through every key, value pair in the dictionary to print our strings print(value) end end ``` **Expected output:** ``` And they say we'll have some fun if it stops raining. Hello Muddah, hello Fadduh. Here I am at Camp Granada. Camp is very entertaining. ``` ### Method: LocalizationTable:GetTranslator **Signature:** `LocalizationTable:GetTranslator(localeId: string): Instance` Returns a [Translator](/docs/reference/engine/classes/Translator.md) for entries in this LocalizationTable, in the specified language. The translator will first search in this table and then look in ancestor tables. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `localeId` | `string` | | | **Returns:** `Instance` — The [Translator](/docs/reference/engine/classes/Translator.md) instance for the specified locale. ### Method: LocalizationTable:RemoveEntry **Signature:** `LocalizationTable:RemoveEntry(key: string, source: string, context: string): ()` Removes an entry from the LocalizationTable, using the specified `key`, `source`, and `context` to narrow down the specific entry to be removed. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | | | `source` | `string` | | | | `context` | `string` | | | **Returns:** `()` ### Method: LocalizationTable:RemoveEntryValue **Signature:** `LocalizationTable:RemoveEntryValue(key: string, source: string, context: string, localeId: string): ()` Removes a single language translation from the LocalizationTable, using the provided `key`, `source`, `context`, and `localeId` to narrow down the specific entry to be removed. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | | | `source` | `string` | | | | `context` | `string` | | | | `localeId` | `string` | | | **Returns:** `()` ### Method: LocalizationTable:RemoveTargetLocale **Signature:** `LocalizationTable:RemoveTargetLocale(localeId: string): ()` Removes all translations from the LocalizationTable with the specified localeId. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `localeId` | `string` | | | **Returns:** `()` ### Method: LocalizationTable:SetEntries **Signature:** `LocalizationTable:SetEntries(entries: Variant): ()` Sets the contents of the LocalizationTable. The entries parameter should be an array of dictionaries in the same format as the one returned from the [LocalizationTable:GetEntries()](/docs/reference/engine/classes/LocalizationTable.md) function. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `entries` | `Variant` | | | **Returns:** `()` ### Method: LocalizationTable:SetEntryContext **Signature:** `LocalizationTable:SetEntryContext(key: string, source: string, context: string, newContext: string): ()` Sets the **Context** field of a LocalizationTable entry to `newContext`, using the specified `key`, `source`, and `context` to narrow down the entry that will have this change applied. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | | | `source` | `string` | | | | `context` | `string` | | | | `newContext` | `string` | | | **Returns:** `()` ### Method: LocalizationTable:SetEntryExample **Signature:** `LocalizationTable:SetEntryExample(key: string, source: string, context: string, example: string): ()` Sets the **Example** field of a LocalizationTable entry to `example`, using the specified `key`, `source`, and `context` to narrow down the entry that will have this change applied. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | | | `source` | `string` | | | | `context` | `string` | | | | `example` | `string` | | | **Returns:** `()` ### Method: LocalizationTable:SetEntryKey **Signature:** `LocalizationTable:SetEntryKey(key: string, source: string, context: string, newKey: string): ()` Sets the **Key** field of a LocalizationTable entry to `newKey`, using the specified `key`, `source`, and `context` to narrow down the entry that will have this change applied. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | | | `source` | `string` | | | | `context` | `string` | | | | `newKey` | `string` | | | **Returns:** `()` ### Method: LocalizationTable:SetEntrySource **Signature:** `LocalizationTable:SetEntrySource(key: string, source: string, context: string, newSource: string): ()` Sets the **Source** field of a LocalizationTable entry to `newSource`, using the specified `key`, `source`, and `context` to narrow down the entry that will have this change applied. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | | | `source` | `string` | | | | `context` | `string` | | | | `newSource` | `string` | | | **Returns:** `()` ### Method: LocalizationTable:SetEntryValue **Signature:** `LocalizationTable:SetEntryValue(key: string, source: string, context: string, localeId: string, text: string): ()` Sets the text of the specified localeId in a LocalizationTable entry, using the specified `key`, `source`, and `context` to narrow down the entry that will have this change applied. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | | | `source` | `string` | | | | `context` | `string` | | | | `localeId` | `string` | | | | `text` | `string` | | | **Returns:** `()` ### Method: LocalizationTable:GetContents **Signature:** `LocalizationTable:GetContents(): string` > **Deprecated:** This item has been superseded by [LocalizationTable:GetEntries()](/docs/reference/engine/classes/LocalizationTable.md) which should be used in all new work. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Returns:** `string` ### Method: LocalizationTable:GetString **Signature:** `LocalizationTable:GetString(targetLocaleId: string, key: string): string` > **Deprecated:** This item has been superseded by [LocalizationTable:GetTranslator()](/docs/reference/engine/classes/LocalizationTable.md) which should be used in all new work. The GetString function returns a translation based on the specified language and key. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `targetLocaleId` | `string` | | Specified language. | | `key` | `string` | | An optional unique key for fast hash lookups in code. If it is non-empty it must be unique in the table. | **Returns:** `string` — Translated string. ### Method: LocalizationTable:RemoveKey **Signature:** `LocalizationTable:RemoveKey(key: string): ()` > **Deprecated:** This item has been superseded by [LocalizationTable:RemoveEntry()](/docs/reference/engine/classes/LocalizationTable.md) which should be used in all new work Deprecated in favor of [LocalizationTable:RemoveEntry()](/docs/reference/engine/classes/LocalizationTable.md). Calling RemoveKey is the same as making the following call to RemoveEntry: ```lua LocalizationTable:RemoveEntry(key, "", "") ``` *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | | **Returns:** `()` ### Method: LocalizationTable:SetContents **Signature:** `LocalizationTable:SetContents(contents: string): ()` > **Deprecated:** This item has been superseded by [LocalizationTable:SetEntries()](/docs/reference/engine/classes/LocalizationTable.md) which should be used in all new work The SetContents function sets the contents of the LocalizationTable, via the legacy JSON format. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `contents` | `string` | | | **Returns:** `()` ### Method: LocalizationTable:SetEntry **Signature:** `LocalizationTable:SetEntry(key: string, targetLocaleId: string, text: string): ()` > **Deprecated:** This item has been superseded by [LocalizationTable:SetEntries()](/docs/reference/engine/classes/LocalizationTable.md) which should be used in all new work *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | | | `targetLocaleId` | `string` | | | | `text` | `string` | | | **Returns:** `()` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: LogService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service summary: "A service that allows you to read outputted text." --- # Class: LogService > A service that allows you to read outputted text. ## Description `LogService` allows you to log structured log entries and read outputted text. #### Template Syntax Methods that accept a `context` table support `{key}` template placeholders in the message string. To include a literal brace character in the output, use double braces: `{{` produces a literal `{` and `}}` produces a literal `}`. ```lua local LogService = game:GetService("LogService") LogService:Info("Value = {{result}}: {val}", {val = 42}) -- Output: "Value = {result}: 42" ``` #### Warning This service might have unexpected or unreliable behavior and content might be truncated. Don't rely on contents of events and messages emitted by this service for any important game logic. ## Methods ### Method: LogService:ClearOutput **Signature:** `LogService:ClearOutput(): ()` Clears Roblox Studio's **Output** window. The log history is also cleared, such that [LogService:GetLogHistory()](/docs/reference/engine/classes/LogService.md) will not return any entries from before the `ClearOutput()` call. *Security: None · Thread Safety: Unsafe · Capabilities: Logging* **Returns:** `()` ### Method: LogService:Error **Signature:** `LogService:Error(message: string, context?: Dictionary): ()` Logs a message at the [MessageType.MessageError](/docs/reference/engine/enums/MessageType.md) level and throws a structured error with optional context. As this method always throws, use [LuaGlobals.pcall()](/docs/reference/engine/globals/LuaGlobals.md) to catch the error. The thrown error is a table with `message`, `template`, `context`, and `stack` fields, and a `__tostring` metamethod that returns the rendered message. When a `context` table is provided, template placeholders like `{key}` in the message are replaced with the corresponding context values. ```lua local LogService = game:GetService("LogService") local ok, err = pcall(function() LogService:Error("Failed: {reason}", {reason = "timeout"}) end) -- ok is false -- err.message == "Failed: timeout" -- err.context == {reason = "timeout"} -- tostring(err) == "Failed: timeout" ``` *Security: None · Thread Safety: Unsafe · Capabilities: Logging* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `message` | `string` | | The message string. Supports `{key}` template placeholders when a context table is provided. | | `context` | `Dictionary` | `nil` | An optional dictionary of key-value pairs. When provided, `{key}` placeholders in the message are replaced with the corresponding values. | **Returns:** `()` ### Method: LogService:GetLogHistory **Signature:** `LogService:GetLogHistory(): Array` Returns a table of tables, each with the message string, message type, and timestamp of a message that the client displays in the **Output** window. *Security: None · Thread Safety: Unsafe · Capabilities: Logging* **Returns:** `Array` ### Method: LogService:Info **Signature:** `LogService:Info(message: string, context?: Dictionary): ()` Logs a message at the [MessageType.MessageInfo](/docs/reference/engine/enums/MessageType.md) level. When a `context` table is provided, template placeholders like `{key}` in the message are replaced with the corresponding context values. The context is preserved as structured data for display in the Developer Console and Studio's **Output** window. ```lua local LogService = game:GetService("LogService") LogService:Info("User {name} has {count} items", {name = "Alice", count = 42}) -- Output: "User Alice has 42 items" ``` *Security: None · Thread Safety: Unsafe · Capabilities: Logging* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `message` | `string` | | The message string. Supports `{key}` template placeholders when a context table is provided. | | `context` | `Dictionary` | `nil` | An optional dictionary of key-value pairs. When provided, `{key}` placeholders in the message are replaced with the corresponding values. | **Returns:** `()` ### Method: LogService:Log **Signature:** `LogService:Log(messageType: MessageType, message: string, context?: Dictionary): ()` Logs a message at the specified [MessageType](/docs/reference/engine/enums/MessageType.md) level. This is a general-purpose method that combines the functionality of [Output()](/docs/reference/engine/classes/LogService.md), [Info()](/docs/reference/engine/classes/LogService.md), [Warn()](/docs/reference/engine/classes/LogService.md), and [Error()](/docs/reference/engine/classes/LogService.md) into a single call with an explicit message type parameter. When `messageType` is [MessageType.MessageError](/docs/reference/engine/enums/MessageType.md), this method throws a structured error object (same behavior as [Error()](/docs/reference/engine/classes/LogService.md)). ```lua local LogService = game:GetService("LogService") LogService:Log(Enum.MessageType.MessageInfo, "Event {action}", {action = "click"}) ``` *Security: None · Thread Safety: Unsafe · Capabilities: Logging* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `messageType` | `MessageType` | | The [MessageType](/docs/reference/engine/enums/MessageType.md) specifying the log level. | | `message` | `string` | | The message string. Supports `{key}` template placeholders when a context table is provided. | | `context` | `Dictionary` | `nil` | An optional dictionary of key-value pairs. When provided, `{key}` placeholders in the message are replaced with the corresponding values. | **Returns:** `()` ### Method: LogService:Output **Signature:** `LogService:Output(message: string, context?: Dictionary): ()` Logs a message at the [MessageType.MessageOutput](/docs/reference/engine/enums/MessageType.md) level. When a `context` table is provided, template placeholders like `{key}` in the message are replaced with the corresponding context values. The context is preserved as structured data for display in the Developer Console and Studio's **Output** window. ```lua local LogService = game:GetService("LogService") LogService:Output("Player {name} joined", {name = "Alice"}) -- Output: "Player Alice joined" ``` *Security: None · Thread Safety: Unsafe · Capabilities: Logging* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `message` | `string` | | The message string. Supports `{key}` template placeholders when a context table is provided. | | `context` | `Dictionary` | `nil` | An optional dictionary of key-value pairs. When provided, `{key}` placeholders in the message are replaced with the corresponding values. | **Returns:** `()` ### Method: LogService:Warn **Signature:** `LogService:Warn(message: string, context?: Dictionary): ()` Logs a message at the [MessageType.MessageWarning](/docs/reference/engine/enums/MessageType.md) level with optional structured context. When a `context` table is provided, template placeholders like `{key}` in the message are replaced with the corresponding context values. The context is preserved as structured data for display in the Developer Console and Studio's **Output** window. ```lua local LogService = game:GetService("LogService") LogService:Warn("Memory usage at {pct}%", {pct = 95}) -- Output: "Memory usage at 95%" ``` *Security: None · Thread Safety: Unsafe · Capabilities: Logging* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `message` | `string` | | The message string. Supports `{key}` template placeholders when a context table is provided. | | `context` | `Dictionary` | `nil` | An optional dictionary of key-value pairs. When provided, `{key}` placeholders in the message are replaced with the corresponding values. | **Returns:** `()` ## Events ### Event: LogService.MessageOut **Signature:** `LogService.MessageOut(message: string, messageType: MessageType, context: Dictionary)` Fires when the client outputs text. *Security: None · Capabilities: Logging* **Parameters:** | Name | Type | Description | |------|------|-------------| | `message` | `string` | | | `messageType` | `MessageType` | | | `context` | `Dictionary` | | **LogService.MessageOut** Code and output ```lua local LogService = game:GetService("LogService") local messageLabel = Instance.new("Message") messageLabel.Parent = workspace local function onMessageOut(message, messageType) messageLabel.Text = "The message was " .. message .. " and the type was " .. tostring(messageType) end LogService.MessageOut:Connect(onMessageOut) ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: LoginService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service summary: "An internal service whose functionality is not available to developers." --- # Class: LoginService > An internal service whose functionality is not available to developers. ## Description An unfinished service which apparently would have allowed a user to login from within a place. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: LuaSettings last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated --- # Class: LuaSettings ## Description The LuaSettings allows you to change certain properties, in regards to how Roblox handles Luau, its dialect of Lua. It is labeled as **Lua** in the Roblox Studio Settings menu. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: LuaSourceContainer last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotBrowsable summary: "The base class for all objects which contain Luau code." --- # Class: LuaSourceContainer > The base class for all objects which contain Luau code. ## Description The base class for all objects which contain Luau code. [Script](/docs/reference/engine/classes/Script.md), [LocalScript](/docs/reference/engine/classes/LocalScript.md), and [ModuleScript](/docs/reference/engine/classes/ModuleScript.md) all inherit from **LuaSourceContainer**. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: LuaWebService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service summary: "An internal service, which is responsible for retrieving HTTP data from websites. Used by a range of services, including MarketplaceService, InsertService and HttpService. Its functions cannot be accessed by developers." --- # Class: LuaWebService > An internal service, which is responsible for retrieving HTTP data from > websites. Used by a range of services, including [MarketplaceService](/docs/reference/engine/classes/MarketplaceService.md), > [InsertService](/docs/reference/engine/classes/InsertService.md) and [HttpService](/docs/reference/engine/classes/HttpService.md). Its functions cannot be > accessed by developers. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: MLService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated --- # Class: MLService ## Methods ### Method: MLService:CreateSessionAsync **Signature:** `MLService:CreateSessionAsync(assetId: string): MLSession` *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `assetId` | `string` | | | **Returns:** `MLSession` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: MLSession last_updated: 2026-06-29T19:34:07Z inherits: - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated --- # Class: MLSession ## Methods ### Method: MLSession:ForwardAsync **Signature:** `MLSession:ForwardAsync(data: Dictionary): Dictionary` *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `data` | `Dictionary` | | | **Returns:** `Dictionary` ## Inherited Members ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: MakeupDescription last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances summary: "Describes the appearance of a makeup item for the HumanoidDescription." --- # Class: MakeupDescription > Describes the appearance of a makeup item for the [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). ## Description `MakeupDescription` is an object that stores the description for a makeup item. It is meant to be placed underneath a [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) in order to work with [Humanoid:ApplyDescriptionAsync()](/docs/reference/engine/classes/Humanoid.md). ## Properties ### Property: MakeupDescription.AssetId ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarAppearance" ] } ``` The asset ID that should be applied when applying this [MakeupDescription](/docs/reference/engine/classes/MakeupDescription.md). ### Property: MakeupDescription.Instance ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarAppearance" ] } ``` A reference to the [Instance](/docs/reference/engine/classes/Instance.md) that should be used when applying this [MakeupDescription](/docs/reference/engine/classes/MakeupDescription.md). This property can be used instead of [AssetId](/docs/reference/engine/classes/MakeupDescription.md) to apply makeup without uploading it to the platform. ### Property: MakeupDescription.MakeupType ```json { "type": "MakeupType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarAppearance" ] } ``` The [MakeupType](/docs/reference/engine/enums/MakeupType.md) of the makeup item referred to by this description. ### Property: MakeupDescription.Order ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AvatarAppearance" ] } ``` The sort order used when layering this makeup item relative to other makeup items. Lower values are applied first, with higher values rendered on top. If the same ordered values is used, the tie breaker will be the AssetID. ## Methods ### Method: MakeupDescription:GetAppliedInstance **Signature:** `MakeupDescription:GetAppliedInstance(): Instance` Returns the applied makeup [Decal](/docs/reference/engine/classes/Decal.md) if this [MakeupDescription](/docs/reference/engine/classes/MakeupDescription.md) is the child of an applied [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) parented to a [Humanoid](/docs/reference/engine/classes/Humanoid.md). However, if multiple [MakeupDescription](/docs/reference/engine/classes/MakeupDescription.md) exist in the parented [MakeupDescription](/docs/reference/engine/classes/MakeupDescription.md), they will be composed into a single makeup [Decal](/docs/reference/engine/classes/Decal.md). *Security: None · Thread Safety: Unsafe · Capabilities: AvatarAppearance* **Returns:** `Instance` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ManualGlue last_updated: 2026-06-29T19:34:07Z inherits: - ManualSurfaceJointInstance - JointInstance - Instance - Object type: class memory_category: BaseParts tags: - Deprecated summary: "ManualGlue is a joint created in a similar manner to the ManualWeld class. It functions identically to the Glue class." --- # Class: ManualGlue > ManualGlue is a joint created in a similar manner to the [ManualWeld](/docs/reference/engine/classes/ManualWeld.md) > class. It functions identically to the [Glue](/docs/reference/engine/classes/Glue.md) class. ## Description **ManualGlue** is a joint created in a similar manner to the [ManualWeld](/docs/reference/engine/classes/ManualWeld.md) class. It functions identically to the [Glue](/docs/reference/engine/classes/Glue.md) class. ## Inherited Members ### From [JointInstance](/docs/reference/engine/classes/JointInstance.md) - **Property `Active`** (`boolean`): Determines if the joint is currently active in the world. - **Property `C0`** (`CFrame`): Determines how the offset point is attached to - **Property `C1`** (`CFrame`): Subtracted from the C0 property to create an - **Property `Enabled`** (`boolean`): Sets whether the joint is active or not. - **Property `Part0`** (`BasePart`): The first BasePart that the joint connects. - **Property `Part1`** (`BasePart`): The second BasePart that the joint connects. - **Property `part1`** (`BasePart`): *(deprecated, hidden)* ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ManualSurfaceJointInstance last_updated: 2026-06-29T19:34:07Z inherits: - JointInstance - Instance - Object type: class memory_category: BaseParts tags: - NotCreatable - Deprecated summary: "ManualSurfaceJointInstance is the base class for ManualGlue and ManualWeld." --- # Class: ManualSurfaceJointInstance > ManualSurfaceJointInstance is the base class for [ManualGlue](/docs/reference/engine/classes/ManualGlue.md) and > [ManualWeld](/docs/reference/engine/classes/ManualWeld.md). ## Description The ManualSurfaceJointInstance is the base class for [ManualGlue](/docs/reference/engine/classes/ManualGlue.md). This instance (when created) also used to cause the server to crash, however this behavior has since been fixed. ## Inherited Members ### From [JointInstance](/docs/reference/engine/classes/JointInstance.md) - **Property `Active`** (`boolean`): Determines if the joint is currently active in the world. - **Property `C0`** (`CFrame`): Determines how the offset point is attached to - **Property `C1`** (`CFrame`): Subtracted from the C0 property to create an - **Property `Enabled`** (`boolean`): Sets whether the joint is active or not. - **Property `Part0`** (`BasePart`): The first BasePart that the joint connects. - **Property `Part1`** (`BasePart`): The second BasePart that the joint connects. - **Property `part1`** (`BasePart`): *(deprecated, hidden)* ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ManualWeld last_updated: 2026-06-29T19:34:07Z inherits: - ManualSurfaceJointInstance - JointInstance - Instance - Object type: class memory_category: BaseParts tags: - Deprecated summary: "Holds two parts together and functions identically to Weld." --- # Class: ManualWeld > Holds two parts together and functions identically to [Weld](/docs/reference/engine/classes/Weld.md). ## Description An object that holds two parts together. It is commonly created when the _Join Always_ setting in Studio is turned on. Functions identically to [Weld](/docs/reference/engine/classes/Weld.md). See also [WeldConstraint](/docs/reference/engine/classes/WeldConstraint.md) for a newer alternative using the [constraints](/docs/en-us/physics/mechanical-constraints.md) system that does not require [C0](/docs/reference/engine/classes/JointInstance.md) or [C1](/docs/reference/engine/classes/JointInstance.md) properties to be manually set. ## Root part Every Assembly has a root part, see [BasePart:GetRootPart()](/docs/reference/engine/classes/BasePart.md). When a ManualWeld's [C0](/docs/reference/engine/classes/JointInstance.md)/[C1](/docs/reference/engine/classes/JointInstance.md) is modified the root part will stay where it was. ## Directionality ManualWelds do not have any directionality. [Part0](/docs/reference/engine/classes/JointInstance.md) or [Part1](/docs/reference/engine/classes/JointInstance.md), doesn't matter. You can imagine rigid joints forming a tree branching down from the root part. All the parts down the tree from root will move, and their welded "children" in this tree will move with them. ## Inherited Members ### From [JointInstance](/docs/reference/engine/classes/JointInstance.md) - **Property `Active`** (`boolean`): Determines if the joint is currently active in the world. - **Property `C0`** (`CFrame`): Determines how the offset point is attached to - **Property `C1`** (`CFrame`): Subtracted from the C0 property to create an - **Property `Enabled`** (`boolean`): Sets whether the joint is active or not. - **Property `Part0`** (`BasePart`): The first BasePart that the joint connects. - **Property `Part1`** (`BasePart`): The second BasePart that the joint connects. - **Property `part1`** (`BasePart`): *(deprecated, hidden)* ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: MarkerCurve last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances summary: "Represents a list of strings markers in chronological order." --- # Class: MarkerCurve > Represents a list of strings markers in chronological order. ## Description The MarkerCurve instance lets you place markers as string values at certain times on a timeline. The string at each marker cannot exceed 64 characters and must only contain printable characters. ## Properties ### Property: MarkerCurve.Length ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Animation" ] } ``` Returns the number of markers in the MarkerCurve. ## Methods ### Method: MarkerCurve:GetMarkerAtIndex **Signature:** `MarkerCurve:GetMarkerAtIndex(index: int): Dictionary` Returns the time and string value of the marker at the provided index. *Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `index` | `int` | | | **Returns:** `Dictionary` — A table containing the time and value of the marker at the provided index. ### Method: MarkerCurve:GetMarkers **Signature:** `MarkerCurve:GetMarkers(): Array` Returns the time and string value of all markers in the MarkerCurve. *Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Returns:** `Array` — An array of tables containing the time and value of all markers in the MarkerCurve. ### Method: MarkerCurve:InsertMarkerAtTime **Signature:** `MarkerCurve:InsertMarkerAtTime(time: float, marker: string): Array` Inserts a marker with the provided string value at the provided time. The provided string cannot exceed 64 characters and must only contain printable characters. *Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `time` | `float` | | Time at which the marker should be added. | | `marker` | `string` | | String value associated with this marker. | **Returns:** `Array` — A table containing a boolean and a number. The boolean is `true` if the call adds a new marker and `false` if it modifies an existing marker at the provided time. The number is the index of the added marker. ### Method: MarkerCurve:RemoveMarkerAtIndex **Signature:** `MarkerCurve:RemoveMarkerAtIndex(startingIndex: int, count?: int): int` Remove several markers in the MarkerCurve starting at the provided index. *Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `startingIndex` | `int` | | Index of the first marker to be removed in the MarkerCurve. | | `count` | `int` | `1` | How many markers to remove starting from the startingIndex. | **Returns:** `int` — How many markers were actually removed (can be less than count if the MarkerCurve was too few markers). ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: MarketplaceService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service summary: "The service responsible for in-experience transactions." --- # Class: MarketplaceService > The service responsible for in-experience transactions. ## Description [MarketplaceService](/docs/reference/engine/classes/MarketplaceService.md) is responsible for in-experience transactions. The most notable methods are [PromptProductPurchase](/docs/reference/engine/classes/MarketplaceService.md) and [PromptPurchase](/docs/reference/engine/classes/MarketplaceService.md), as well as the callback [ProcessReceipt](/docs/reference/engine/classes/MarketplaceService.md) which must be defined so that developer product transactions do not fail. [MarketplaceService](/docs/reference/engine/classes/MarketplaceService.md) also has methods that fetch information about [developer products](/docs/en-us/production/monetization/developer-products.md) ([GetProductInfoAsync](/docs/reference/engine/classes/MarketplaceService.md) and [GetDeveloperProductsAsync](/docs/reference/engine/classes/MarketplaceService.md)), [passes](/docs/en-us/production/monetization/passes.md) ([UserOwnsGamePassAsync()](/docs/reference/engine/classes/MarketplaceService.md)), and other assets ([PlayerOwnsAssetAsync](/docs/reference/engine/classes/MarketplaceService.md), [PlayerOwnsBundleAsync](/docs/reference/engine/classes/MarketplaceService.md)). Understanding [MarketplaceService](/docs/reference/engine/classes/MarketplaceService.md) is the first step towards learning to [monetize](/docs/en-us/production/monetization.md) an experience on Roblox, as well as learning to use [DataStoreService](/docs/reference/engine/classes/DataStoreService.md), which is responsible for saving and loading all data related to purchases. ## Methods ### Method: MarketplaceService:BindReceiptHandler **Signature:** `MarketplaceService:BindReceiptHandler(transactionType: ReceiptType, handler: Function, filter: Array?): RBXScriptConnection` `BindReceiptHandler` registers a callback to process receipts of a specific [ReceiptType](/docs/reference/engine/enums/ReceiptType.md). You can use it to handle Robux transfer receipts ([ReceiptType.RobuxTransferSender](/docs/reference/engine/enums/ReceiptType.md) and [ReceiptType.RobuxTransferReceiver](/docs/reference/engine/enums/ReceiptType.md)). The `handler` callback receives a receipt info dictionary and must return an [ReceiptDecision](/docs/reference/engine/enums/ReceiptDecision.md) value: - [ReceiptDecision.Processed](/docs/reference/engine/enums/ReceiptDecision.md) — Indicates the receipt was successfully processed and all benefits have been granted. The receipt is marked as complete. - [ReceiptDecision.NotProcessedYet](/docs/reference/engine/enums/ReceiptDecision.md) — Indicates the receipt has not been processed yet. The receipt remains unresolved and will be delivered again on the next opportunity. #### Receipt Info Dictionary The receipt info dictionary passed to the `handler` contains the following fields: | Key | Type | Description | | --- | --- | --- | | `PurchaseId` | string | A unique identifier for this specific receipt. | | `PlayerId` | number | The [Player.UserId](/docs/reference/engine/classes/Player.md) of the user associated with this receipt. | | `PlaceIdWherePurchased` | number | The place ID where the transaction was initiated. | | `ReceiptType` | [ReceiptType](/docs/reference/engine/enums/ReceiptType.md) | The type of this receipt. | | `CurrencySpent` | number | The amount of Robux involved in the transaction. For [ReceiptType.RobuxTransferSender](/docs/reference/engine/enums/ReceiptType.md) receipts, this is the amount sent. For [ReceiptType.RobuxTransferReceiver](/docs/reference/engine/enums/ReceiptType.md) receipts, this is the amount received. | | `TransferRequestId` | string | The transfer request ID from [MarketplaceService:PromptRobuxTransferAsync()](/docs/reference/engine/classes/MarketplaceService.md). Only present for [ReceiptType.RobuxTransferSender](/docs/reference/engine/enums/ReceiptType.md) and [ReceiptType.RobuxTransferReceiver](/docs/reference/engine/enums/ReceiptType.md) receipts. | #### Receipt Timing For Robux transfer receipts, `handler` is invoked once the transfer settles. A settled receipt is delivered to whichever server the user is currently in (the user does not need to rejoin). - [ReceiptType.RobuxTransferReceiver](/docs/reference/engine/enums/ReceiptType.md) — Delivered to the server the receiver is currently in once the transfer settles. If the receiver is offline at that time, delivery happens the next time they join a server. - [ReceiptType.RobuxTransferSender](/docs/reference/engine/enums/ReceiptType.md) — Delivered immediately if the transfer settles synchronously. If the transfer is gated on receiver approval (for example, parental consent), delivery happens once the receiver accepts, to whichever server the sender is currently in or on their next join if they are offline. The user must be on a server for `handler` to fire. #### Errors This method throws an error if: - A handler is already registered for the same [ReceiptType](/docs/reference/engine/enums/ReceiptType.md) and product ID combination. - A catch-all handler is already registered for the same [ReceiptType](/docs/reference/engine/enums/ReceiptType.md). *Security: None · Thread Safety: Unsafe · Capabilities: Monetization* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `transactionType` | `ReceiptType` | | The [ReceiptType](/docs/reference/engine/enums/ReceiptType.md) indicating which kind of receipt to handle. | | `handler` | `Function` | | A callback function that receives a receipt info dictionary and must return an [ReceiptDecision](/docs/reference/engine/enums/ReceiptDecision.md) value. | | `filter` | `Array?` | | An optional array of product IDs. When provided, the handler only fires for receipts matching those product IDs. Not supported for `RobuxTransferSender` or `RobuxTransferReceiver` receipt types. | **Returns:** `RBXScriptConnection` — A [RBXScriptConnection](/docs/reference/engine/datatypes/RBXScriptConnection.md) that can be disconnected to unregister the handler. **MarketplaceService:BindReceiptHandler** The following server-side [Script](/docs/reference/engine/classes/Script.md) example shows how to use [MarketplaceService:BindReceiptHandler()](/docs/reference/engine/classes/MarketplaceService.md) to register handlers for Robux transfer receipts. Both sender and receiver receipt types are handled to acknowledge the transfer on each side. ```lua -- NOTE: If your handler grants persistent benefits (currency, items), use -- DataStoreService:UpdateAsync() on the TransferRequestId to prevent -- double-granting when the same receipt is delivered to multiple servers. local MarketplaceService = game:GetService("MarketplaceService") local Players = game:GetService("Players") -- Handle sender receipts (the player who sent Robux) MarketplaceService:BindReceiptHandler( Enum.ReceiptType.RobuxTransferSender, function(receiptInfo) local player = Players:GetPlayerByUserId(receiptInfo.PlayerId) if not player then -- Player isn't on this server; defer so the receipt is -- redelivered to whichever server they're currently in return Enum.ReceiptDecision.NotProcessedYet end print( `{player.Name} sent {receiptInfo.CurrencySpent} Robux` .. ` (TransferRequestId: {receiptInfo.TransferRequestId})` ) -- Grant any sender-side acknowledgement or update UI here return Enum.ReceiptDecision.Processed end ) -- Handle receiver receipts (the player who received Robux) MarketplaceService:BindReceiptHandler( Enum.ReceiptType.RobuxTransferReceiver, function(receiptInfo) local player = Players:GetPlayerByUserId(receiptInfo.PlayerId) if not player then return Enum.ReceiptDecision.NotProcessedYet end print( `{player.Name} received {receiptInfo.CurrencySpent} Robux` .. ` (TransferRequestId: {receiptInfo.TransferRequestId})` ) -- Grant any receiver-side benefits or update UI here return Enum.ReceiptDecision.Processed end ) ``` ### Method: MarketplaceService:GetDeveloperProductsAsync **Signature:** `MarketplaceService:GetDeveloperProductsAsync(): Instance` Returns a [Pages](/docs/reference/engine/classes/Pages.md) object which contains information for all of the current experience's [developer products](/docs/en-us/production/monetization/developer-products.md). *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetRead* **Returns:** `Instance` **MarketplaceService:GetDeveloperProductsAsync** The below example would print the name, price, ID, description and icon AssetId for all of the developer products which belong to the current game. ```lua local MarketplaceService = game:GetService("MarketplaceService") local developerProducts = MarketplaceService:GetDeveloperProductsAsync():GetCurrentPage() for _, developerProduct in pairs(developerProducts) do for field, value in pairs(developerProduct) do print(field .. ": " .. value) end print(" ") end ``` ### Method: MarketplaceService:GetProductInfoAsync **Signature:** `MarketplaceService:GetProductInfoAsync(assetId: int64, infoType?: InfoType): Dictionary` This method provides information about an asset, [developer product](/docs/en-us/production/monetization/developer-products.md), or [pass](/docs/en-us/production/monetization/passes.md) based on the asset ID and the [InfoType](/docs/reference/engine/enums/InfoType.md). If an item with the given ID does not exist, this method throws an error. Information about the queried item is provided in a dictionary with the following keys. Note that not all information is provided or necessarily relevant for the kind of product you're querying. | Key | Type | Description | | --- | --- | --- | | `Name` | string | The name shown on the asset's page. | | `Description` | string | The description shown on the asset's page; can be `nil` if blank. | | `PriceInRobux` | number | The cost of purchasing the asset using Robux. | | `UserBasePriceInRobux` | number | The base price of the asset in Robux before any discounts are applied. | | `PriceDiscountDetails` | Array | An ordered list of discounts representing the difference between `UserBasePriceInRobux` and `PriceInRobux`. Each entry contains the following keys: | | | `Type`: The type of discount. `"RobloxPlusSubscription"` indicates that the discount was applied due to the user’s Roblox Plus subscription. | | | `AmountInRobux`: number — The value of the discount in Robux. | | | `Percent`: number — The percentage of the discount. | | `ProductId` | number | The product ID if [InfoType](/docs/reference/engine/enums/InfoType.md) is `Product`. | | `ProductType` | string | A string describing what the product is. Not to be confused with [MarketplaceProductType](/docs/reference/engine/enums/MarketplaceProductType.md). | | `Created` | string | Timestamp of when the asset was created, for example `2022-01-02T10:30:45Z`. Formatted using ISO 8601. | | `Updated` | string | Timestamp of when the asset was last updated by its creator, for example `2022-02-12T11:22:15Z`. Formatted using ISO 8601. | | `ContentRatingTypeId` | number | Indicates whether the item is marked as 13+ in catalog. | | `MinimumMembershipLevel` | number | The minimum subscription level necessary to purchase the item. | | `IsPublicDomain` | boolean | Describes whether the asset can be taken for free. | | `TargetId` | number | The ID of the product or asset. | #### Creator Information | Key | Type | Description | | --- | --- | --- | | `Creator` | table | Dictionary table of information describing the creator of the asset, containing the following fields: | | | `CreatorType`: Either `User` or `Group`. | | | `CreatorTargetId`: The ID of the creator user or group. | | | `HasVerifiedBadge`: Boolean of whether the creator has a verified badge. | | | `Name`: The name/username of the creator. | | | `Id`: Use `CreatorTargetId` instead. | #### Asset Information | Key | Type | Description | | --- | --- | --- | | `AssetId` | number | The asset ID if [InfoType](/docs/reference/engine/enums/InfoType.md) is `Asset`. | | `AssetTypeId` | number | The type of asset. See [AssetType](/docs/reference/engine/enums/AssetType.md) for the asset type ID numbers. | | `IconImageAssetId` | number | The asset ID of the product's icon, or `0` if there isn't one. | | `IsForSale` | boolean | Describes whether the asset is purchasable. | | `IsLimited` | boolean | Describes whether the asset is a Roblox Limited that is no longer (if ever) sold. | | `IsLimitedUnique` | boolean | Describes whether the asset is a unique Roblox Limited ("Limited U") item that only has a fixed number sold. | | `IsNew` | boolean | Describes whether the asset is marked as "new" in the catalog. | | `Remaining` | number | The remaining number of times a limited unique item may be sold. | | `Sales` | number | The number of times the asset has been sold. | #### Collectibles Information | Key | Type | Description | | --- | --- | --- | | `CollectibleItemId` | string | The unique item ID of the collectible. | | `CollectibleProductId` | string | The unique product ID of the collectible. | | `CollectiblesItemDetails` | table | Dictionary table of information describing the collectible, containing the following fields: | | | `CollectibleLowestAvailableResaleItemInstanceId`: The unique item instance ID of the lowest available resale for the collectible. | | | `CollectibleLowestAvailableResaleProductId`: The unique product ID of the lowest available resale for the collectible. | | | `CollectibleLowestResalePrice`: The lowest resale price for the collectible in Robux. | | | `IsForSale`: Boolean of whether the collectible is available for sale (not resale). | | | `IsLimited`: Boolean of whether or not the collectible is limited. | | | `TotalQuantity`: The total quantity of the collectible available for purchase (not resale). | #### Sale Location Settings | Key | Type | Description | | --- | --- | --- | | `CanBeSoldInThisGame` | boolean | Describes whether the asset is purchasable in the current experience. | | `SaleLocation` | table | Dictionary table of information describing where the item can be sold, containing the following fields: | | | `SaleLocationType`: The type of sale location setting. See [ProductLocationRestriction](/docs/reference/engine/enums/ProductLocationRestriction.md) for the sale location setting ID numbers. | | | `UniverseIds`: Array table of universes in which the item can be sold (not currently implemented). | *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetRead* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `assetId` | `int64` | | The asset ID of the specified product. | | `infoType` | `InfoType` | `Asset` | An [InfoType](/docs/reference/engine/enums/InfoType.md) enum value specifying the type of information being retrieved. | **Returns:** `Dictionary` — A dictionary containing information about the queried item, described in the previous tables. **Getting Product Info** The below example will print the name and description of the asset with an ID of 125378389. In this case it will print: _"Mr. Fancy Top Hat :: So fancy that even his top hat's top hat has a top hat."_ ```lua local MarketplaceService = game:GetService("MarketplaceService") local ASSET_ID = 125378389 local asset = MarketplaceService:GetProductInfoAsync(ASSET_ID) print(asset.Name .. " :: " .. asset.Description) ``` **Displaying Price Discounts** The example below will print discount information for a game pass. ```lua local MarketplaceService = game:GetService("MarketplaceService") local PASS_ID = 12345678 local textLabel = script.Parent local DiscountTypeDisplay = { RobloxPlusSubscription = "Roblox Plus Discount", } local productInfo = MarketplaceService:GetProductInfoAsync(PASS_ID, Enum.InfoType.GamePass) print(string.format("Original Price: %d", productInfo.UserBasePriceInRobux)) for _, discount in ipairs(productInfo.PriceDiscountDetails) do local displayName = DiscountTypeDisplay[discount.Type] or "Other Discount" print(string.format("%s (%d%%): -%d", displayName, discount.Percent, discount.AmountInRobux)) end print(string.format("You Pay: %d", productInfo.PriceInRobux)) ``` ### Method: MarketplaceService:GetRobloxSubscriptionDetailsAsync **Signature:** `MarketplaceService:GetRobloxSubscriptionDetailsAsync(user: Player): Dictionary` This method is a streamlined endpoint to check for a single, platform-wide Roblox subscription product. By providing `StartTime` (conditionally) and `IsOriginExperience` to reward long-term loyalists without compromising user data across the platform. The returned dictionary contains the following fields: | Field | Type | Description | | --- | --- | --- | | `IsSubscribed` | bool | Returns true if the user has an active Roblox Subscription membership. | | `IsOriginExperience` | bool | Returns true if the user originally subscribed to Roblox Subscription while inside the current Experience (Universe). | | `StartTime` | DateTime? | A [DateTime](/docs/reference/engine/datatypes/DateTime.md) object representing the time when the user’s subscription period first began. Note: For privacy reasons, this field is only returned if `IsOriginExperience` is true. If the user subscribed in a different experience or on the web, this field will be nil. | *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Monetization* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `user` | `Player` | | The user regarding whom to check the subscription status. | **Returns:** `Dictionary` — A dictionary containing subscription details such as `IsSubscribed`, `IsOriginExperience`, and optionally `StartTime`. **Check Roblox Subscription Details** Checks if a player has an active Roblox subscription and performs actions based on loyalty and attribution. ```lua local MarketplaceService = game:GetService("MarketplaceService") local Players = game:GetService("Players") Players.PlayerAdded:Connect(function(player) local success, details = pcall(function() return MarketplaceService:GetRobloxSubscriptionDetailsAsync(player) end) if success and details.IsSubscribed then -- 1. Check for Loyalty (e.g., Subscribed for > 60 days) local threeMonths = 60 * 24 * 60 * 60 if details.StartTime and (os.time() - details.StartTime.UnixTimestamp) > threeMonths then print("Awarding the '3-Month Subscription Veteran' skin!") end -- 2. Check for Attribution if details.IsOriginExperience then print("Attribution confirmed: User subscribed via this experience.") else print("User subscribed elsewhere (Website or another Experience).") end end end) ``` ### Method: MarketplaceService:GetSubscriptionProductInfoAsync **Signature:** `MarketplaceService:GetSubscriptionProductInfoAsync(subscriptionId: string): Dictionary` Returns the product information of a subscription for the given `subscriptionId`. Because it returns a localized price, you can only call this method from a [Script](/docs/reference/engine/classes/Script.md) with [RunContext.Client](/docs/reference/engine/enums/RunContext.md). | Key | Type | Description | | --- | --- | --- | | `Name` | string | The name of the subscription product. | | `Description` | string | The description of the subscription product. | | `IconImageAssetId` | number | The asset ID of the subscription product icon. | | `SubscriptionPeriod` | [SubscriptionPeriod](/docs/reference/engine/enums/SubscriptionPeriod.md) | The duration of the subscription (for example, `Month`, `Year`, etc.). | | `DisplayPrice` | string | Localized price with the appropriate currency symbol for display (for example, `$4.99`). For users in unsupported countries, `DisplayPrice` returns a string without specific price information. | | `DisplaySubscriptionPeriod` | string | Localized subscription period text for display (for example, `/month`). Can be used together with `DisplayPrice`. | | `SubscriptionProviderName` | string | Name of the subscription benefit provider (for example, the name of the associated experience). | | `IsForSale` | boolean | True if the subscription product is available for sale. | | `PriceTier` | number | A number that can be used to compare the price of different subscription products. This is not the actual price of the subscription (for example, 499). | | `PriceInRobux` | number | The equivalent cost of the subscription in Robux. Returns 0 if the subscription product is not available to be purchased in Robux. | *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetRead* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `subscriptionId` | `string` | | The ID of the subscription to check. | **Returns:** `Dictionary` ### Method: MarketplaceService:GetUsersPriceLevelsAsync **Signature:** `MarketplaceService:GetUsersPriceLevelsAsync(userIds: Array): List` Returns the regionalized price levels of users, representing the recommended price for an item in each user's regional market. For example, a price level of 100 means that the suggested price for that user (based on their region and purchasing power) is 100 Robux. See [Protect your trades and gifts](/docs/en-us/production/monetization/regional-pricing.md#protect-your-trades-and-gifts) for more information. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Monetization* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `userIds` | `Array` | | An array of user IDs. | **Returns:** `List` — Returns an array of `PriceLevelInfo` objects with a dictionary where the keys are user IDs (strings) and their values are the corresponding price levels (integers between 1 and 1000). **Get price levels for a list of users** The following example uses GetUsersPriceLevelsAsync to map each price level to a user ID and retrieve price levels for a list of users. ```lua -- Get the MarketplaceService local MarketplaceService = game:GetService("MarketplaceService") -- Define a function to retrieve price levels for a list of users local function getPriceLevels(userIds) local success, result = pcall(function() return MarketplaceService:GetUsersPriceLevelsAsync(userIds) end) if success then -- Map each PriceLevelInfo to a UserId -> PriceLevel lookup table local lookup = {} for _, info in ipairs(result) do lookup[info.UserId] = info.PriceLevel end return lookup else warn("Error getting price levels:", result) return nil end end -- Example using placeholder IDs local user1Id = 123456789 local user2Id = 987654321 -- Call the function and store the result local priceLevels = getPriceLevels({user1Id, user2Id}) -- If successful, print each user's level if priceLevels then print("Price level for User 1:", priceLevels[user1Id]) print("Price level for User 2:", priceLevels[user2Id]) else print("Failed to retrieve price levels.") end ``` ### Method: MarketplaceService:GetUserSubscriptionDetailsAsync **Signature:** `MarketplaceService:GetUserSubscriptionDetailsAsync(user: Player, subscriptionId: string): Dictionary` Returns a dictionary table containing the details of the user's subscription for the given `subscriptionId`. The table contains the following keys: | Key | Type | Description | | --- | --- | --- | | `SubscriptionState` | [SubscriptionState](/docs/reference/engine/enums/SubscriptionState.md) | Current state of this particular subscription. | | `NextRenewTime` | [DateTime](/docs/reference/engine/datatypes/DateTime.md) | Renewal time for this current subscription. May be in the past if the subscription is in [SubscribedRenewalPaymentPending](/docs/reference/engine/enums/SubscriptionState.md) state. This field is will be `nil` if the subscription will not renew, is [Expired](/docs/reference/engine/enums/SubscriptionState.md), or the user never subscribed. | | `ExpireTime` | [DateTime](/docs/reference/engine/datatypes/DateTime.md) | When this subscription expires. This field will be `nil` if the subscription is not cancelled or the user never subscribed. | | `ExpirationDetails` | [table](/docs/reference/engine/globals/table.md) | Table containing the details of the subscription expiration. This field will be `nil` if the subscription is not in the [Expired](/docs/reference/engine/enums/SubscriptionState.md) state. If populated, the table contains a `ExpirationReason` key of type [SubscriptionExpirationReason](/docs/reference/engine/enums/SubscriptionExpirationReason.md) describing why the subscription is expired. | Note that this method can only be called from a [Script](/docs/reference/engine/classes/Script.md) with [RunContext](/docs/reference/engine/classes/BaseScript.md) of [Server](/docs/reference/engine/enums/RunContext.md). If you only need to determine the `IsSubscribed` status of a user, it's recommended to use [GetUserSubscriptionStatusAsync](/docs/reference/engine/classes/MarketplaceService.md) as it is faster and more efficient for that particular purpose. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Monetization* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `user` | `Player` | | The [Player](/docs/reference/engine/classes/Player.md) object whose subscription details you want to check. | | `subscriptionId` | `string` | | The ID of the subscription to check. | **Returns:** `Dictionary` ### Method: MarketplaceService:GetUserSubscriptionPaymentHistoryAsync **Signature:** `MarketplaceService:GetUserSubscriptionPaymentHistoryAsync(user: Player, subscriptionId: string): Array` Returns an [Array](/docs/reference/engine/globals/table.md) that contains up to one year of the user's subscription payment history for the given `subscriptionId`, sorted from the most recent status to the least recent. You can only call this method from a [Script](/docs/reference/engine/classes/Script.md) with [RunContext.Server](/docs/reference/engine/enums/RunContext.md). Each entry in the payment history [Array](/docs/reference/engine/globals/table.md) contains the following keys: | Key | Type | Description | | --- | --- | --- | | `CycleStartTime` | [DateTime](/docs/reference/engine/datatypes/DateTime.md) | [DateTime](/docs/reference/engine/datatypes/DateTime.md) at the start of this particular subscription period. | | `CycleEndTime` | [DateTime](/docs/reference/engine/datatypes/DateTime.md) | [DateTime](/docs/reference/engine/datatypes/DateTime.md) at the end of this particular subscription period. | | `PaymentStatus` | [SubscriptionPaymentStatus](/docs/reference/engine/enums/SubscriptionPaymentStatus.md) | [SubscriptionPaymentStatus.Paid](/docs/reference/engine/enums/SubscriptionPaymentStatus.md) if the user paid for this particular subscription period. [SubscriptionPaymentStatus.Refunded](/docs/reference/engine/enums/SubscriptionPaymentStatus.md) if the user refunded this particular subscription period. | #### Payment History Length Only creators affiliated with the subscription product can access up to **one year** worth of the user's subscription payment history. Non-associated creators can only get the user's **current** subscription payment status or an empty [Array](/docs/reference/engine/globals/table.md) if the user has no active subscription. #### Grace Period Subscription renewal payments can have some processing time. Payment history doesn't return a table for this period. However, in order to preserve a user's subscription experience during the processing period, [GetUserSubscriptionStatusAsync](/docs/reference/engine/classes/MarketplaceService.md) returns `IsSubscribed: true` for the given user. Don't grant durable items or currency type subscription benefits to the user until after payment has been confirmed for the current cycle. For example, on August 31, 2023, User A's Subscription B is up for renewal. On September 1, 2023, the payment has yet to be processed. If you call [GetUserSubscriptionPaymentHistoryAsync](/docs/reference/engine/classes/MarketplaceService.md) on September 1, 2023 on User A for Subscription B, the first entry of the return value is: | Key | Value | | --- | --- | | `CycleStartTime` | ... | | `CycleEndTime` | August 31, 2023 | | `PaymentStatus` | [SubscriptionPaymentStatus.Paid](/docs/reference/engine/enums/SubscriptionPaymentStatus.md) | Note that since the user is within the grace period, the cycle they have yet to pay for (September 1, 2023) does not appear in the return value at all. This field only populates after the payment has been received and processed. At the same time, [GetUserSubscriptionStatusAsync](/docs/reference/engine/classes/MarketplaceService.md) returns the following result until the renewal payment process fails or the user cancels: | Key | Return | | --- | --- | | `IsSubscribed` | True | | `IsRenewing` | True | *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Monetization* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `user` | `Player` | | | | `subscriptionId` | `string` | | | **Returns:** `Array` **MarketplaceService:GetUserSubscriptionPaymentHistoryAsync** This code sample demonstrates how to check the last 12 months of a user's payment history for a given subscription ID. Rather than the raw values, it prints formatted values for easier readability. Roblox Studio includes several subscription IDs for testing purposes: - `EXP-0` represents an active and renewing subscription. - `EXP-1` represents a returning subscription. - `EXP-2` represents a refunded subscription. Depending on your subscription benefits, refunds can require special handling, so testing for this case is particularly useful. - `EXP-3` represents an active subscription that is pending successful payment for the current cycle. Continued failure to pay results in automatic cancellation of the subscription. ```lua local MarketplaceService = game:GetService("MarketplaceService") local Players = game:GetService("Players") local SUBSCRIPTION_ID = "EXP-0" local function checkSubscriptionHistory(player: Player) local subscriptionHistory = {} local success, err = pcall(function() subscriptionHistory = MarketplaceService:GetUserSubscriptionPaymentHistoryAsync(player, SUBSCRIPTION_ID) end) if not success then warn(`Error while checking subscription history: {err}`) return end if next(subscriptionHistory) then -- User has some subscription history within the past 12 months. -- Print the details of each payment entry from the subscription history. print(`Player {player.Name} has subscribed to {SUBSCRIPTION_ID} before:`) for entryNum, paymentEntry in subscriptionHistory do local paymentStatus = tostring(paymentEntry.PaymentStatus) local cycleStartTime = paymentEntry.CycleStartTime:FormatLocalTime("LLL", "en-us") local cycleEndTime = paymentEntry.CycleEndTime:FormatLocalTime("LLL", "en-us") print(`{entryNum}: {paymentStatus} ({cycleStartTime} - {cycleEndTime})`) end else print(`Player {player.Name} has never subscribed to {SUBSCRIPTION_ID} before.`) end end -- Call checkSubscriptionHistory for any players already in the game for _, player in ipairs(Players:GetPlayers()) do checkSubscriptionHistory(player) end -- Call checkSubscriptionHistory for all future players Players.PlayerAdded:Connect(checkSubscriptionHistory) ``` ### Method: MarketplaceService:GetUserSubscriptionStatusAsync **Signature:** `MarketplaceService:GetUserSubscriptionStatusAsync(user: Player, subscriptionId: string): Dictionary` Returns a [table](/docs/reference/engine/globals/table.md) that contains the subscription status of the user for the given `subscriptionId`. The table contains the following keys: | Key | Type | Description | | --- | --- | --- | | `IsSubscribed` | boolean | True if the user's subscription is active. | | `IsRenewing` | boolean | True if the user is set to renew this subscription after the current subscription period ends. | Note that `IsSubscribed` will be `true` only when a user has purchased the subscription and the payment has been successfully processed. If the payment for a user's initial subscription purchase is still processing or has failed, `IsSubscribed` returns `false`. To understand when a user's subscription status has changed, see the [Players.UserSubscriptionStatusChanged](/docs/reference/engine/classes/Players.md) event. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Monetization* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `user` | `Player` | | The [Player](/docs/reference/engine/classes/Player.md) object whose subscription status you want to check. | | `subscriptionId` | `string` | | The ID of the subscription to check for. | **Returns:** `Dictionary` **Check User Subscription Status** This code sample demonstrates how to check the subscription status of a user for a certain subscription. As a user enters the game, their account is checked for the status of that subscription and a message is printed. Notice how the call to GetUserSubscriptionStatusAsync is wrapped in `pcall` - this prevents the code from throwing an error in case the user's subscription status can't be checked for some reason. Should such an error occur, the `success` variable would be false. ```lua local MarketplaceService = game:GetService("MarketplaceService") local Players = game:GetService("Players") local subscriptionID = "EXP-00000" local function checkSubStatus(player) local subStatus = {} local success, message = pcall(function() -- returns IsRenewing and IsSubscribed subStatus = MarketplaceService:GetUserSubscriptionStatusAsync(player, subscriptionID) end) if not success then warn("Error while checking if player has subscription: " .. tostring(message)) return end if subStatus["IsSubscribed"] then print(player.Name .. " is subscribed with " .. subscriptionID) -- Give player permissions associated with the subscription end end Players.PlayerAdded:Connect(checkSubStatus) ``` ### Method: MarketplaceService:PlayerOwnsAssetAsync **Signature:** `MarketplaceService:PlayerOwnsAssetAsync(player: Instance, assetId: int64): boolean` Returns whether the inventory of a specific user contains an asset, based on the asset ID. This method throws an error if the query fails, so you should wrap calls to this method in `pcall()`. - This method should **not** be used for [passes](/docs/en-us/production/monetization/passes.md) since they use a separate ID system. Legacy passes that still depend on an asset ID should use [UserOwnsGamePassAsync()](/docs/reference/engine/classes/MarketplaceService.md) instead of this method. - This method cannot be used to check for [developer products](/docs/en-us/production/monetization/developer-products.md) since they can be purchased multiple times but not owned themselves. Instead, use a [data store](/docs/en-us/cloud-services/data-stores.md) to save when a user buys a developer product. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetRead* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Instance` | | The [Player](/docs/reference/engine/classes/Player.md) whose inventory is tested for ownership of the given asset. | | `assetId` | `int64` | | The asset ID for which the given player's inventory is tested. | **Returns:** `boolean` — Indicates whether the given player's inventory contains the given asset. **Check for Item Ownership** This code sample demonstrates how to check if a player owns a certain item. Here, we're checking for the item [Midnight Shades](https://www.roblox.com/catalog/30331986/Midnight-Shades), a hat that costs R$ 250. As a player enters the game, their account is checked for the ownership of that item and a message is printed. Notice how the call to PlayerOwnsAssetAsync is wrapped in `pcall` - this prevents the code from throwing an error in case the player's inventory can't be checked for some reason. Should such an error occur, the `success` variable would be false. ```lua local Players = game:GetService("Players") local MarketplaceService = game:GetService("MarketplaceService") -- The item we're checking for: https://www.roblox.com/catalog/30331986/Midnight-Shades local ASSET_ID = 30331986 local ASSET_NAME = "Midnight Shades" local function onPlayerAdded(player) local success, doesPlayerOwnAsset = pcall(MarketplaceService.PlayerOwnsAssetAsync, MarketplaceService, player, ASSET_ID) if not success then local errorMessage = doesPlayerOwnAsset warn(`Error checking if {player.Name} owns {ASSET_NAME}: {errorMessage}`) return end if doesPlayerOwnAsset then print(`{player.Name} owns {ASSET_NAME}`) else print(`{player.Name} doesn't own {ASSET_NAME}`) end end Players.PlayerAdded:Connect(onPlayerAdded) ``` **Expected output:** Player owns Midnight Shades ### Method: MarketplaceService:PlayerOwnsBundleAsync **Signature:** `MarketplaceService:PlayerOwnsBundleAsync(player: Player, bundleId: int64): boolean` Returns whether the inventory of a specific user contains a bundle, based on the bundle ID. This method throws an error if the query fails, so you should wrap calls to this method in `pcall()`. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetRead* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Player` | | The [Player](/docs/reference/engine/classes/Player.md) whose inventory is tested for ownership of the given bundle. | | `bundleId` | `int64` | | The bundle ID for which the given player's inventory is tested. | **Returns:** `boolean` — Indicates whether the given player's inventory contains the given bundle. **Check for Bundle Ownership** This code sample demonstrates how to check if a player owns a certain bundle. Here, we're checking for the bundle [Junkbot](https://www.roblox.com/bundles/589/Junkbot). As a player enters the game, their account is checked for the ownership of that item and a message is printed. Notice that a `pcall()` wraps the call to `MarketplaceService:PlayerOwnsBundleAsync()` to prevent the code from throwing an error in case the player's inventory can't be checked. If such an error occurs, the `success` variable is false. ```lua local Players = game:GetService("Players") local MarketplaceService = game:GetService("MarketplaceService") -- The bundle we're checking for: https://www.roblox.com/bundles/589/Junkbot local BUNDLE_ID = 589 local BUNDLE_NAME = "Junkbot" Players.PlayerAdded:Connect(function(player) local success, doesPlayerOwnBundle = pcall(function() return MarketplaceService:PlayerOwnsBundleAsync(player, BUNDLE_ID) end) if success == false then print("PlayerOwnsBundleAsync call failed: ", doesPlayerOwnBundle) return end if doesPlayerOwnBundle then print(player.Name .. " owns " .. BUNDLE_NAME) else print(player.Name .. " doesn't own " .. BUNDLE_NAME) end end) ``` **Expected output:** Player owns Junkbot ### Method: MarketplaceService:PromptBulkPurchase **Signature:** `MarketplaceService:PromptBulkPurchase(player: Player, lineItems: Array, options: Dictionary): ()` Prompts a user to purchase multiple avatar items with the given `assetId` or `bundleId`. Does not work with non-avatar items. `PromptBulkPurchase` only allows prompting from server scripts. For limited items, original copies are prompted until they run out, regardless of the price. Once original copies are out, resale copies are prompted. A maximum of 20 items can be added to a single bulk purchase prompt. *Security: None · Thread Safety: Unsafe · Capabilities: PromptExternalPurchase* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Player` | | The user to prompt to purchase items. | | `lineItems` | `Array` | | An array of avatar items to be included in the bulk purchase. Each line item contains the following structure: ```lua { Type: MarketplaceProductType, Id: string } ``` Each line item contains the following pairs: - `Type`: The corresponding [MarketplaceProductType](/docs/reference/engine/enums/MarketplaceProductType.md) (Enum). - `Id`: The ID of the asset or bundle. | | `options` | `Dictionary` | | Not available at this time. | **Returns:** `()` **Prompt Bulk Purchase Client** The following sample prompts players to buy "Beautiful Hair for Beautiful People" and "Blue Collar Cat" when they join the experience. Place this LocalScript somewhere on the client such as within Players -> StarterPlayerScripts so that it will be able to fire a remote event signal from a client to the server. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local promptBulkPurchaseEvent = ReplicatedStorage:WaitForChild("PromptBulkPurchaseEvent") local part = Instance.new("Part") part.Parent = workspace local clickDetector = Instance.new("ClickDetector") clickDetector.Parent = part clickDetector.MouseClick:Connect(function() promptBulkPurchaseEvent:FireServer({ { Type = Enum.MarketplaceProductType.AvatarAsset, Id = "16630147" }, { Type = Enum.MarketplaceProductType.AvatarBundle, Id = "182" }, }) end) ``` **Prompt Bulk Purchase Server** The following code is listening for a [RemoteEvent.OnServerEvent](/docs/reference/engine/classes/RemoteEvent.md) to fire to the Server from a Client. Place this script somewhere on the server such as within `ServerStorage`. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local MarketplaceService = game:GetService("MarketplaceService") local promptBulkPurchaseEvent = Instance.new("RemoteEvent") promptBulkPurchaseEvent.Name = "PromptBulkPurchaseEvent" promptBulkPurchaseEvent.Parent = ReplicatedStorage --Listen for the RemoteEvent to fire from a Client and then trigger the bulk purchase prompt promptBulkPurchaseEvent.OnServerEvent:Connect(function(player, items) MarketplaceService:PromptBulkPurchase(player, items, {}) end) ``` ### Method: MarketplaceService:PromptBundlePurchase **Signature:** `MarketplaceService:PromptBundlePurchase(player: Instance, bundleId: int64): ()` Prompts a user to purchase a bundle with the given `bundleId`. *Security: None · Thread Safety: Unsafe · Capabilities: PromptExternalPurchase* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Instance` | | | | `bundleId` | `int64` | | | **Returns:** `()` ### Method: MarketplaceService:PromptCancelSubscription **Signature:** `MarketplaceService:PromptCancelSubscription(user: Player, subscriptionId: string): ()` Prompts a user to cancel a subscription for the given `subscriptionId`. Once the user successfully cancels the subscription, the [Players.UserSubscriptionStatusChanged](/docs/reference/engine/classes/Players.md) event fires. *Security: None · Thread Safety: Unsafe · Capabilities: PromptExternalPurchase* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `user` | `Player` | | | | `subscriptionId` | `string` | | | **Returns:** `()` ### Method: MarketplaceService:PromptGamePassPurchase **Signature:** `MarketplaceService:PromptGamePassPurchase(player: Instance, gamePassId: int64): ()` Prompts a user to purchase a [pass](/docs/en-us/production/monetization/passes.md) with the given `gamePassId`. *Security: None · Thread Safety: Unsafe · Capabilities: PromptExternalPurchase* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Instance` | | | | `gamePassId` | `int64` | | | **Returns:** `()` ### Method: MarketplaceService:PromptProductPurchase **Signature:** `MarketplaceService:PromptProductPurchase(player: Instance, productId: int64, equipIfPurchased?: boolean, currencyType?: CurrencyType): ()` Prompts a user to purchase a [developer product](/docs/en-us/production/monetization/developer-products.md) with the given `productId`. *Security: None · Thread Safety: Unsafe · Capabilities: PromptExternalPurchase* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Instance` | | | | `productId` | `int64` | | | | `equipIfPurchased` | `boolean` | `true` | | | `currencyType` | `CurrencyType` | `Default` | | **Returns:** `()` **MarketplaceService:PromptProductPurchase** The following example illustrates how to prompt purchase of a [developer product](/docs/en-us/production/monetization/developer-products.md) with the [PromptProductPurchase()](/docs/reference/engine/classes/MarketplaceService.md) method. Depending on the needs of your experience, you can call the `promptPurchase()` function in situations such as when the player presses a [button](/docs/en-us/ui/buttons.md) or when their character talks to a vendor NPC. ```lua local MarketplaceService = game:GetService("MarketplaceService") local Players = game:GetService("Players") local player = Players.LocalPlayer local productId = 0000000 -- Change this to your developer product ID -- Function to prompt purchase of the developer product local function promptPurchase() MarketplaceService:PromptProductPurchase(player, productId) end promptPurchase() ``` ### Method: MarketplaceService:PromptPurchase **Signature:** `MarketplaceService:PromptPurchase(player: Instance, assetId: int64, equipIfPurchased?: boolean, currencyType?: CurrencyType): ()` Prompts a user to purchase an item with the given `assetId`. - This does not work for [USD Creator Store](/docs/en-us/production/creator-store.md) purchases. - If the item has the [Sale Location](/docs/en-us/marketplace/publish-to-marketplace.md#sale-location) set as `Experience By Place ID (API Only)`, you must call [MarketplaceService:PromptPurchase](/docs/reference/engine/classes/MarketplaceService.md) from a server script. - If prompting a purchase of a [limited](/docs/en-us/marketplace/marketplace-fees-and-commissions.md#limiteds) item: - (Recommended) Server requests prompt original copies until they run out, regardless of the price. Once original copies run out, resale copies are prompted. - Client requests prompt from the lowest resale price even if original copies are available. *Security: None · Thread Safety: Unsafe · Capabilities: PromptExternalPurchase* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Instance` | | | | `assetId` | `int64` | | | | `equipIfPurchased` | `boolean` | `true` | | | `currencyType` | `CurrencyType` | `Default` | Ignored. | **Returns:** `()` **LocalScript (Client)** The below example would prompt all new players to buy Beautiful Hair for Beautiful People when they click on a part. Place this LocalScript somewhere on the Client such as within Players -> StarterPlayerScripts so that it will be able to fire a [RemoteEvent:FireServer()](/docs/reference/engine/classes/RemoteEvent.md) signal from a Client to the Server. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local promptPurchaseEvent = ReplicatedStorage:WaitForChild("PromptPurchaseEvent") local part = Instance.new("Part") part.Parent = workspace local clickDetector = Instance.new("ClickDetector") clickDetector.Parent = part clickDetector.MouseClick:Connect(function() promptPurchaseEvent:FireServer(16630147) end) ``` **Script (Server)** Since the below code is listening for a [RemoteEvent.OnServerEvent](/docs/reference/engine/classes/RemoteEvent.md) to fire to the Server from a Client. Place this script somewhere on the Server such as within ServerStorage. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local MarketplaceService = game:GetService("MarketplaceService") local promptPurchaseEvent = Instance.new("RemoteEvent") promptPurchaseEvent.Name = "PromptPurchaseEvent" promptPurchaseEvent.Parent = ReplicatedStorage -- Listen for the RemoteEvent to fire from a Client and then trigger the purchase prompt promptPurchaseEvent.OnServerEvent:Connect(function(player, id) MarketplaceService:PromptPurchase(player, id) end) ``` ### Method: MarketplaceService:PromptRobloxSubscriptionPurchase **Signature:** `MarketplaceService:PromptRobloxSubscriptionPurchase(user: Player): ()` Prompts a user to purchase a Roblox Plus subscription. When the user successfully subscribes, any experience-defined rewards for the upsell are granted automatically through the engine API. #### See Also - [MarketplaceService.PromptRobloxSubscriptionPurchaseFinished](/docs/reference/engine/classes/MarketplaceService.md) which fires when the Roblox Plus purchase UI closes. - [Player.HasRobloxSubscription](/docs/reference/engine/classes/Player.md) which can be observed via [Instance:GetPropertyChangedSignal()](/docs/reference/engine/classes/Instance.md) to detect when a user's subscription status changes. *Security: None · Thread Safety: Unsafe · Capabilities: PromptExternalPurchase* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `user` | `Player` | | The [Player](/docs/reference/engine/classes/Player.md) to be prompted to purchase Roblox Plus. | **Returns:** `()` **Prompt Roblox Plus Subscription Purchase** The following code prompts users to subscribe to Roblox Plus when their character touches the part that the [Script](/docs/reference/engine/classes/Script.md) is attached to. Users who already have an active subscription are immediately granted the reward and teleported to an exclusive area. ```lua local MarketplaceService = game:GetService("MarketplaceService") local Players = game:GetService("Players") local teleporter = script.Parent local showModal = true local EXCLUSIVE_AREA_POSITION = Vector3.new(1200, 200, 60) -- Grant the reward and teleport the subscribing player to the exclusive area local function grantRewardAndTeleport(player) player:RequestStreamAroundAsync(EXCLUSIVE_AREA_POSITION) local character = player.Character if character and character.Parent then local currentPivot = character:GetPivot() character:PivotTo(currentPivot * CFrame.new(EXCLUSIVE_AREA_POSITION)) end end -- Detect character parts touching the teleporter teleporter.Touched:Connect(function(otherPart) local player = Players:GetPlayerFromCharacter(otherPart.Parent) if not player then return end if not player:GetAttribute("CharacterPartsTouching") then player:SetAttribute("CharacterPartsTouching", 0) end player:SetAttribute("CharacterPartsTouching", player:GetAttribute("CharacterPartsTouching") + 1) if player.HasRobloxSubscription then -- Player already has Roblox Plus; grant reward immediately grantRewardAndTeleport(player) else -- Prompt Roblox Plus subscription, debounced to once every few seconds if not showModal then return end showModal = false task.delay(5, function() showModal = true end) MarketplaceService:PromptRobloxSubscriptionPurchase(player) end end) -- Detect character parts exiting the teleporter teleporter.TouchEnded:Connect(function(otherPart) local player = Players:GetPlayerFromCharacter(otherPart.Parent) if player and player:GetAttribute("CharacterPartsTouching") then player:SetAttribute("CharacterPartsTouching", player:GetAttribute("CharacterPartsTouching") - 1) end end) -- Grant reward when the server confirms a subscription change -- Connect HasRobloxSubscription change for each player Players.PlayerAdded:Connect(function(player) player:GetPropertyChangedSignal("HasRobloxSubscription"):Connect(function() if player.HasRobloxSubscription and player:GetAttribute("CharacterPartsTouching") and player:GetAttribute("CharacterPartsTouching") > 0 then grantRewardAndTeleport(player) end end) end) ``` ### Method: MarketplaceService:PromptRobuxTransferAsync **Signature:** `MarketplaceService:PromptRobuxTransferAsync(sender: Player, receiverUserId: int64, amount: int64): string` `PromptRobuxTransferAsync` initiates a Robux transfer from the `sender` to the user specified by `receiverUserId`. This is a server-only method and must be called from a [Script](/docs/reference/engine/classes/Script.md) with `RunContext` set to `Server`. After a successful transfer, both the sender and receiver will have receipts delivered to their respective [BindReceiptHandler](/docs/reference/engine/classes/MarketplaceService.md) callbacks: - The sender's receipt has [ReceiptType.RobuxTransferSender](/docs/reference/engine/enums/ReceiptType.md). - The receiver's receipt has [ReceiptType.RobuxTransferReceiver](/docs/reference/engine/enums/ReceiptType.md). Both receipts include a `TransferRequestId` field matching the `transferRequestId` returned by this method, which you can use for logging or to correlate sender and receiver receipts. #### Receipt Timing Both receipts are delivered to whichever server the corresponding user is currently in once the transfer settles (neither side needs to rejoin). If a transfer is gated on receiver approval, for example parental consent for the receiver's account, it does not settle until approval lands; receipts are delivered after that point. - **Receiver receipt** is delivered to the server the receiver is currently in once the transfer settles, or on their next session join if they are offline. - **Sender receipt** is delivered immediately if the transfer settles synchronously. If approval is required, it is delivered to the server the sender is currently in once the receiver accepts, or on the sender's next session join if they are offline. #### Errors This method throws an error if: - The `sender` is not a valid [Player](/docs/reference/engine/classes/Player.md) instance. - `receiverUserId` is not a positive integer. - `amount` is not a positive integer. - The method is called from the client instead of the server. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: PromptExternalPurchase* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `sender` | `Player` | | The [Player](/docs/reference/engine/classes/Player.md) initiating the transfer. Must be a valid player currently in the server. | | `receiverUserId` | `int64` | | The [UserId](/docs/reference/engine/classes/Player.md) of the user who will receive the Robux. | | `amount` | `int64` | | The amount of Robux to transfer. Must be a positive integer. | **Returns:** `string` — A string `transferRequestId` that uniquely identifies this transfer request. Use this ID to correlate with receipts delivered through [BindReceiptHandler](/docs/reference/engine/classes/MarketplaceService.md). **MarketplaceService:PromptRobuxTransferAsync** The following server-side [Script](/docs/reference/engine/classes/Script.md) example shows how to use [MarketplaceService:PromptRobuxTransferAsync()](/docs/reference/engine/classes/MarketplaceService.md) to initiate a Robux transfer between two players. The method returns a `transferRequestId` that uniquely identifies the transfer and can be used to correlate with receipts. ```lua local MarketplaceService = game:GetService("MarketplaceService") local Players = game:GetService("Players") -- Example: transfer Robux when a player triggers a RemoteEvent local transferEvent = game.ReplicatedStorage:WaitForChild("RequestTransfer") transferEvent.OnServerEvent:Connect(function(sender, receiverUserId, amount) -- Validate inputs if not sender or not sender:IsA("Player") then return end if typeof(receiverUserId) ~= "number" or receiverUserId <= 0 then warn("Invalid receiverUserId") return end if typeof(amount) ~= "number" or amount <= 0 then warn("Invalid amount") return end local success, result = pcall(function() return MarketplaceService:PromptRobuxTransferAsync(sender, receiverUserId, amount) end) if success then print(`Transfer initiated with TransferRequestId: {result}`) else warn(`Transfer failed: {result}`) end end) ``` ### Method: MarketplaceService:PromptSubscriptionPurchase **Signature:** `MarketplaceService:PromptSubscriptionPurchase(user: Player, subscriptionId: string): ()` Prompts a user to purchase a subscription for the given `subscriptionId`. *Security: None · Thread Safety: Unsafe · Capabilities: PromptExternalPurchase* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `user` | `Player` | | The [Player](/docs/reference/engine/classes/Player.md) object to be prompted to subscribe. | | `subscriptionId` | `string` | | The ID of the subscription to subscribe to. | **Returns:** `()` ### Method: MarketplaceService:RankProductsAsync **Signature:** `MarketplaceService:RankProductsAsync(productIdentifiers: Array): List` Takes a list of product IDs and returns a personalized ordered list of those products. This API has a client-side throttling limit of 10 requests per minute. If you exceed this limit, wait 60 seconds and make the request again. *Yields · Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `productIdentifiers` | `Array` | | An array of objects identifying the products you want to rank. This array can include up to 50 items. Each `ProductIdentifier` has: - [InfoType](/docs/reference/engine/enums/InfoType.md): Enum.InfoType - Must be either [InfoType.GamePass](/docs/reference/engine/enums/InfoType.md) or [InfoType.Product](/docs/reference/engine/enums/InfoType.md). - `Id`: number - The ID of the game pass or developer product. ```lua local ProductIdentifier = { InfoType = Enum.InfoType.GamePass, Id = 123456 } ``` | **Returns:** `List` — The array of ranked items in a personalized order for the current user. Each array has: - `ProductIdentifier`: The corresponding ID from the input array. - `ProductInfo`: The standard product info dictionary returned by [GetProductInfoAsync](/docs/reference/engine/classes/MarketplaceService.md). **Get a list of ranked products based on the provided product IDs** This sample takes a list of product IDs and returns a ranked list of those products. ```lua -- Get the MarketplaceService local MarketplaceService = game:GetService("MarketplaceService") -- Create the array of products you want to rank local productIdentifiers = { {InfoType = Enum.InfoType.GamePass, Id = 123}, {InfoType = Enum.InfoType.Product, Id = 456}, {InfoType = Enum.InfoType.Product, Id = 789} } -- Call in a protected call to handle errors gracefully local success, rankedProducts = pcall(function() return MarketplaceService:RankProductsAsync(productIdentifiers) end) if not success then error("Failed to rank products") end -- Load the returned items into the store. for i, rankedItem in ipairs(rankedProducts) do local productIdentifier = rankedItem.ProductIdentifier local productInfo = rankedItem.ProductInfo -- ... -- Logic to add products into store end ``` ### Method: MarketplaceService:RecommendTopProductsAsync **Signature:** `MarketplaceService:RecommendTopProductsAsync(infoTypes: Array): List` Takes an array of [InfoType](/docs/reference/engine/enums/InfoType.md) and returns up to 50 items representing the products a user is most likely to engage with and purchase. If no recommendations can be determined, the method returns an empty list. This API has a client-side throttling limit of 5 requests per minute. If you exceed this limit, wait 60 seconds and make the request again. *Yields · Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `infoTypes` | `Array` | | An array of [InfoType](/docs/reference/engine/enums/InfoType.md) values specifying the types of product to retrieve recommendations for. Supported `InfoTypes`: [InfoType.GamePass](/docs/reference/engine/enums/InfoType.md), [InfoType.Product](/docs/reference/engine/enums/InfoType.md). ```lua local infoTypes = { Enum.InfoType.GamePass, Enum.InfoType.Product } ``` | **Returns:** `List` — A ranked list of up to 50 items the user is most likely to engage with, based on the provided `InfoTypes`. If no recommendations can be determined, the method returns an empty list. **Get a ranked list of the top products for in your in-experience store** This sample returns a ranked list of the top products for a user in your in-experience store. ```lua -- Get the MarketplaceService local MarketplaceService = game:GetService("MarketplaceService") -- Create an array of product types to include. In this case both game passes and developer products local productTypes = {Enum.InfoType.GamePass, Enum.InfoType.Product} -- Call in a protected call to handle errors gracefully local success, topRankedItems = pcall(function() return MarketplaceService:RecommendTopProductsAsync(productTypes) end) if not success then error("Failed to rank products") end -- Load the returned items into the store. Make sure to filter out any ineligible items from topRankedItems such as developer products the user can no longer purchase for i, rankedItem in ipairs(topRankedItems) do local productIdentifier = rankedItem.ProductIdentifier local productInfo = rankedItem.ProductInfo -- ... -- Logic to add products into store end ``` ### Method: MarketplaceService:UserOwnsGamePassAsync **Signature:** `MarketplaceService:UserOwnsGamePassAsync(userId: User, gamePassId: int64): boolean` Returns true if the user with the given [UserId](/docs/reference/engine/classes/Player.md) owns the [pass](/docs/en-us/production/monetization/passes.md) with the given `gamePassId` (not to be confused with an asset ID). You can use this method on both the client and the server. #### Caching Behavior The results of this function are cached so that repeated calls are returned faster. When the [PromptGamePassPurchaseFinished](/docs/reference/engine/classes/MarketplaceService.md) event fires, the cache gets updated to reflect the latest ownership state of the associated game pass. If the user purchases a game pass outside of the experience while remaining in the same session, the cache is eventually updated, but this process might take several minutes to propagate. When a user first enters a server after purchasing a game pass, this functions always returns true. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetRead* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `userId` | `User` | | The [UserId](/docs/reference/engine/classes/Player.md) of the [Player](/docs/reference/engine/classes/Player.md) whose inventory you're checking. | | `gamePassId` | `int64` | | The pass ID you want to check for. Not to be confused with an asset ID. | **Returns:** `boolean` ### Method: MarketplaceService:GetProductInfo **Signature:** `MarketplaceService:GetProductInfo(assetId: int64, infoType?: InfoType): Dictionary` > **Deprecated:** This method has been superseded by [GetProductInfoAsync()](/docs/reference/engine/classes/MarketplaceService.md). Returns the product information of an asset using its asset ID. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetRead* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `assetId` | `int64` | | The asset ID of the specified product. | | `infoType` | `InfoType` | `Asset` | An [InfoType](/docs/reference/engine/enums/InfoType.md) enum value specifying the type of information being retrieved. | **Returns:** `Dictionary` — A dictionary containing information about the queried item, described in the previous tables. **Getting Product Info** The below example will print the name and description of the asset with an ID of 125378389. In this case it will print: _"Mr. Fancy Top Hat :: So fancy that even his top hat's top hat has a top hat."_ ```lua local MarketplaceService = game:GetService("MarketplaceService") local ASSET_ID = 125378389 local asset = MarketplaceService:GetProductInfoAsync(ASSET_ID) print(asset.Name .. " :: " .. asset.Description) ``` ### Method: MarketplaceService:PlayerOwnsAsset **Signature:** `MarketplaceService:PlayerOwnsAsset(player: Instance, assetId: int64): boolean` > **Deprecated:** This method has been superseded by [PlayerOwnsAssetAsync()](/docs/reference/engine/classes/MarketplaceService.md). Returns whether the given user has the given asset. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetRead* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Instance` | | The [Player](/docs/reference/engine/classes/Player.md) whose inventory is tested for ownership of the given asset. | | `assetId` | `int64` | | The asset ID for which the given player's inventory is tested. | **Returns:** `boolean` — Indicates whether the given player's inventory contains the given asset. ### Method: MarketplaceService:PlayerOwnsBundle **Signature:** `MarketplaceService:PlayerOwnsBundle(player: Player, bundleId: int64): boolean` > **Deprecated:** This method has been superseded by [PlayerOwnsBundleAsync()](/docs/reference/engine/classes/MarketplaceService.md). Returns whether the given player owns the given bundle. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AssetRead* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Player` | | The [Player](/docs/reference/engine/classes/Player.md) whose inventory is tested for ownership of the given bundle. | | `bundleId` | `int64` | | The bundle ID for which the given player's inventory is tested. | **Returns:** `boolean` — Indicates whether the given player's inventory contains the given bundle. ### Method: MarketplaceService:PromptPremiumPurchase **Signature:** `MarketplaceService:PromptPremiumPurchase(player: Instance): ()` > **Deprecated:** This method has been superseded by [PromptRobloxSubscriptionPurchase()](/docs/reference/engine/classes/MarketplaceService.md). Prompts a user to purchase [Roblox Premium](https://www.roblox.com/premium/membership). To learn more about Premium and about incorporating Premium incentives into your experience, see [Engagement-based payouts](/docs/en-us/production/monetization/engagement-based-payouts.md). #### See Also - [MarketplaceService.PromptPremiumPurchaseFinished](/docs/reference/engine/classes/MarketplaceService.md) which fires when the Premium purchase UI closes. - [Players.PlayerMembershipChanged](/docs/reference/engine/classes/Players.md) which fires when the server recognizes that a user's membership has changed. *Security: None · Thread Safety: Unsafe · Capabilities: PromptExternalPurchase* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Instance` | | The user being prompted to purchase Premium. | **Returns:** `()` **Prompt Premium Purchase** The following code prompts users to purchase Premium when their character touches the part that its containing [Script](/docs/reference/engine/classes/Script.md) is attached to, such as a teleporter that allows access to an exclusive area. ```lua local MarketplaceService = game:GetService("MarketplaceService") local Players = game:GetService("Players") local teleporter = script.Parent local showModal = true local TELEPORT_POSITION = Vector3.new(1200, 200, 60) -- Teleport character to exclusive area local function teleportPlayer(player) -- Request streaming around target location player:RequestStreamAroundAsync(TELEPORT_POSITION) -- Teleport character local character = player.Character if character and character.Parent then local currentPivot = character:GetPivot() character:PivotTo(currentPivot * CFrame.new(TELEPORT_POSITION)) end end -- Detect character parts touching teleporter teleporter.Touched:Connect(function(otherPart) local player = Players:GetPlayerFromCharacter(otherPart.Parent) if not player then return end if not player:GetAttribute("CharacterPartsTouching") then player:SetAttribute("CharacterPartsTouching", 0) end player:SetAttribute("CharacterPartsTouching", player:GetAttribute("CharacterPartsTouching") + 1) if player.MembershipType == Enum.MembershipType.Premium then -- User has Premium; teleport character to exclusive area within experience teleportPlayer(player) else -- Show purchase modal, using debounce to show once every few seconds at most if not showModal then return end showModal = false task.delay(5, function() showModal = true end) MarketplaceService:PromptPremiumPurchase(player) end end) -- Detect character parts exiting teleporter teleporter.TouchEnded:Connect(function(otherPart) local player = Players:GetPlayerFromCharacter(otherPart.Parent) if player and player:GetAttribute("CharacterPartsTouching") then player:SetAttribute("CharacterPartsTouching", player:GetAttribute("CharacterPartsTouching") - 1) end end) -- Handle membership changed event Players.PlayerMembershipChanged:Connect(function(player) warn("User membership changed; new membership is " .. tostring(player.MembershipType)) -- Teleport character if membership type is Premium and character is on teleporter if player.MembershipType == Enum.MembershipType.Premium and player:GetAttribute("CharacterPartsTouching") > 0 then teleportPlayer(player) end end) ``` ## Events ### Event: MarketplaceService.PromptBulkPurchaseFinished **Signature:** `MarketplaceService.PromptBulkPurchaseFinished(player: Instance, status: MarketplaceBulkPurchasePromptStatus, results: Dictionary)` This event fires when a purchase prompt for a bulk avatar items closes. For example, when a user receives the purchase prompt and clicks **Cancel**, or when they receive a success or error message and click **OK**. Note: This is not a trusted event from the client. To check if the user owns the items purchased, use [MarketplaceService.PlayerOwnsAssetAsync](/docs/reference/engine/classes/MarketplaceService.md) or [MarketplaceService.PlayerOwnsBundleAsync](/docs/reference/engine/classes/MarketplaceService.md). *Security: None · Capabilities: PromptExternalPurchase* **Parameters:** | Name | Type | Description | |------|------|-------------| | `player` | `Instance` | The [Player](/docs/reference/engine/classes/Player.md) who received the prompt. | | `status` | `MarketplaceBulkPurchasePromptStatus` | The status of the bulk purchase. | | `results` | `Dictionary` | The table type containing the line items and their status in the following format: ```lua { RobuxSpent: number Items: { { type: MarketplaceProductType, id: string, status: MarketplaceItemPurchaseStatus }, ... } } ``` Each line item contains the following pairs: - `type`: The corresponding [MarketplaceProductType](/docs/reference/engine/enums/MarketplaceProductType.md) (Enum). - `id`: The ID of the asset or bundle (string). - `status`: The [MarketplaceItemPurchaseStatus](/docs/reference/engine/enums/MarketplaceItemPurchaseStatus.md) of the purchase (Enum) | ### Event: MarketplaceService.PromptBundlePurchaseFinished **Signature:** `MarketplaceService.PromptBundlePurchaseFinished(player: Instance, bundleId: int64, wasPurchased: boolean)` *Security: None · Capabilities: PromptExternalPurchase* **Parameters:** | Name | Type | Description | |------|------|-------------| | `player` | `Instance` | | | `bundleId` | `int64` | | | `wasPurchased` | `boolean` | | ### Event: MarketplaceService.PromptGamePassPurchaseFinished **Signature:** `MarketplaceService.PromptGamePassPurchaseFinished(player: Instance, gamePassId: int64, wasPurchased: boolean)` This event fires when a purchase prompt for a [pass](/docs/en-us/production/monetization/passes.md) closes. For example, when a user receives the purchase prompt and clicks **Cancel**, or when they receive a success or error message and click **OK**. #### See Also - For repeatable **developer product** purchase prompts, use [PromptProductPurchaseFinished](/docs/reference/engine/classes/MarketplaceService.md). - For **affiliate gear sales** or other assets, use [PromptPurchaseFinished](/docs/reference/engine/classes/MarketplaceService.md). - For more information on saving and replicating user data like purchases and progress, see [Implementing player data and purchases](https://devforum.roblox.com/t/implementing-player-data-and-purchasing-systems/2839941). *Security: None · Capabilities: PromptExternalPurchase* **Parameters:** | Name | Type | Description | |------|------|-------------| | `player` | `Instance` | The [Player](/docs/reference/engine/classes/Player.md) who received the prompt. | | `gamePassId` | `int64` | The ID number of the pass shown in the prompt. Not to be confused with an asset ID. | | `wasPurchased` | `boolean` | Indicates if the user pressed **OK** (true), **Cancel** (false) on the purchase prompt, or if the purchase prompt errored (false). When `PromptGamePassPurchaseFinished` fires, it updates the cache used by [UserOwnsGamePassAsync()](/docs/reference/engine/classes/MarketplaceService.md) to reflect the current ownership state. `PromptGamePassPurchaseFinished` should only be listened to in a server script. When used on the server, values such as `wasPurchased` reflect the final outcome of the purchase attempt. When used in a local script, these values should not be relied on for validation or game logic. | **Handling Gamepass Purchase Finished** ```lua local MarketplaceService = game:GetService("MarketplaceService") local function gamepassPurchaseFinished(...) -- Print all the details of the prompt, for example: -- PromptGamePassPurchaseFinished PlayerName 123456 false print("PromptGamePassPurchaseFinished", ...) end MarketplaceService.PromptGamePassPurchaseFinished:Connect(gamepassPurchaseFinished) ``` ### Event: MarketplaceService.PromptPremiumPurchaseFinished **Signature:** `MarketplaceService.PromptPremiumPurchaseFinished()` This event fires when a purchase prompt for [Roblox Premium](https://www.roblox.com/premium/membership) closes. For example, when a user receives the purchase prompt and clicks **Cancel**, or when they receive a success or error message and click **OK**. #### See Also - [PromptPremiumPurchase](/docs/reference/engine/classes/MarketplaceService.md) to prompt a user to purchase Premium. - [PlayerMembershipChanged](/docs/reference/engine/classes/Players.md), which fires when the server recognizes that a user's membership has changed. *Security: None · Capabilities: PromptExternalPurchase* ### Event: MarketplaceService.PromptProductPurchaseFinished **Signature:** `MarketplaceService.PromptProductPurchaseFinished(userId: int64, productId: int64, isPurchased: boolean)` **IMPORTANT:** Do **not** use the `PromptProductPurchaseFinished` event to process purchases; instead, use the [ProcessReceipt](/docs/reference/engine/classes/MarketplaceService.md) callback. The firing of `PromptProductPurchaseFinished` does **not** mean that a user has successfully purchased an item. This event fires when a purchase prompt for a [developer product](/docs/en-us/production/monetization/developer-products.md) closes. For example, when a user receives the purchase prompt and clicks **Cancel**, or when they receive a success or error message and click **OK**. The firing of this event does **not** mean that a user has successfully purchased an item. While you can use the `PromptProductPurchaseFinished` event to detect when a user closes a purchase prompt, you should **not** use it to process purchases because those purchases might still fail in the backend for several reasons. For example, if a Roblox system is offline, or if the product price has changed and the user now doesn't have enough Robux to make the purchase. To process purchases, you must use [ProcessReceipt](/docs/reference/engine/classes/MarketplaceService.md). Using `ProcessReceipt` allows you to confirm that the purchase has succeeded before you grant the user the item they have purchased. The `PromptProductPurchaseFinished` event fires with a `Player.UserId` instead of a reference to the `Player` object. #### See Also - [PromptGamePassPurchaseFinished](/docs/reference/engine/classes/MarketplaceService.md) to prompt a user to purchase a pass. - [PromptPurchaseFinished](/docs/reference/engine/classes/MarketplaceService.md) to prompt a user to purchase affiliate gear or other assets. - For more information on saving and replicating user data like purchases and progress, see [Implementing player data and purchases](https://devforum.roblox.com/t/implementing-player-data-and-purchasing-systems/2839941). *Security: None · Capabilities: PromptExternalPurchase* **Parameters:** | Name | Type | Description | |------|------|-------------| | `userId` | `int64` | The [UserId](/docs/reference/engine/classes/Player.md) of the user who received the developer product prompt. | | `productId` | `int64` | The ID number of the developer product shown in the prompt. Not to be confused with an asset ID. | | `isPurchased` | `boolean` | Indicates if the user pressed **OK** (true), **Cancel** (false) on the purchase prompt, or if the purchase prompt errored (false). Do not use this parameter to process developer product purchases. | ### Event: MarketplaceService.PromptPurchaseFinished **Signature:** `MarketplaceService.PromptPurchaseFinished(player: Instance, assetId: int64, isPurchased: boolean)` This event fires when a purchase prompt for an affiliate gear sale or other asset closes. For example, when a user receives the purchase prompt and clicks **Cancel**, or when they receive a success or error message and click **OK**. This event does not fire for [developer product](/docs/en-us/production/monetization/developer-products.md) or [pass](/docs/en-us/production/monetization/passes.md) prompts. #### See Also - [PromptGamePassPurchaseFinished](/docs/reference/engine/classes/MarketplaceService.md) to prompt a user to purchase a pass. - [PromptProductPurchaseFinished](/docs/reference/engine/classes/MarketplaceService.md) to prompt a user to purchase a developer product. - For more information on saving and replicating user data like purchases and progress, see [Implementing player data and purchases](https://devforum.roblox.com/t/implementing-player-data-and-purchasing-systems/2839941). *Security: None · Capabilities: PromptExternalPurchase* **Parameters:** | Name | Type | Description | |------|------|-------------| | `player` | `Instance` | The [Player](/docs/reference/engine/classes/Player.md) who received the prompt. | | `assetId` | `int64` | The asset ID of the item shown in the prompt. | | `isPurchased` | `boolean` | Indicates if the user pressed **OK** (true), **Cancel** (false) on the purchase prompt, or if the purchase prompt errored (false). This might not accurately reflect if the purchase itself has been successfully processed. | **Handling PromptPurchaseFinished Event** The below example would print 'Telamon bought an item with AssetID: 1111' to the output, if they were to complete a transaction in (your) game with an item that had an AssetID of 1111. Alternatively, it would print 'Telamon didn't buy an item with AssetID: 1111' if the opposite was true. ```lua local MarketplaceService = game:GetService("MarketplaceService") local function onPromptPurchaseFinished(player, assetId, isPurchased) if isPurchased then print(player.Name, "bought an item with AssetID:", assetId) else print(player.Name, "didn't buy an item with AssetID:", assetId) end end MarketplaceService.PromptPurchaseFinished:Connect(onPromptPurchaseFinished) ``` ### Event: MarketplaceService.PromptRobloxSubscriptionPurchaseFinished **Signature:** `MarketplaceService.PromptRobloxSubscriptionPurchaseFinished(user: Player, didTryPurchasing: boolean)` This event fires when a purchase prompt for Roblox Plus closes. For example, when a user receives the purchase prompt and clicks **Cancel**, or when they receive a success or error message and click **OK**. Note that this event firing does **not** guarantee the subscription was successfully processed. Listen to [Player.HasRobloxSubscription](/docs/reference/engine/classes/Player.md) via [Instance:GetPropertyChangedSignal()](/docs/reference/engine/classes/Instance.md) on the server to confirm a subscription change before granting rewards. #### See Also - [PromptRobloxSubscriptionPurchase](/docs/reference/engine/classes/MarketplaceService.md) to prompt a user to purchase Roblox Plus. - [Player.HasRobloxSubscription](/docs/reference/engine/classes/Player.md), which can be observed via [Instance:GetPropertyChangedSignal()](/docs/reference/engine/classes/Instance.md) to detect when a user's subscription status changes. *Security: None · Capabilities: PromptExternalPurchase* **Parameters:** | Name | Type | Description | |------|------|-------------| | `user` | `Player` | The [Player](/docs/reference/engine/classes/Player.md) who received the prompt. | | `didTryPurchasing` | `boolean` | Whether the user attempted to purchase Roblox Plus. | **Handle PromptRobloxSubscriptionPurchaseFinished Event** The following code listens for the [MarketplaceService.PromptRobloxSubscriptionPurchaseFinished](/docs/reference/engine/classes/MarketplaceService.md) event to detect when a player closes the Roblox Plus purchase UI. This event does not guarantee the subscription was successfully processed. To confirm a subscription change before granting rewards, listen to [Player.HasRobloxSubscription](/docs/reference/engine/classes/Player.md) via [Instance:GetPropertyChangedSignal()](/docs/reference/engine/classes/Instance.md) on the server. ```lua local MarketplaceService = game:GetService("MarketplaceService") local function onPromptRobloxSubscriptionPurchaseFinished(player, didTryPurchasing) if didTryPurchasing then -- Player attempted to subscribe; wait for HasRobloxSubscription change to confirm print(player.Name, "attempted to subscribe to Roblox Plus") else print(player.Name, "closed the Roblox Plus subscription prompt without purchasing") end end MarketplaceService.PromptRobloxSubscriptionPurchaseFinished:Connect(onPromptRobloxSubscriptionPurchaseFinished) ``` ### Event: MarketplaceService.PromptSubscriptionPurchaseFinished **Signature:** `MarketplaceService.PromptSubscriptionPurchaseFinished(user: Player, subscriptionId: string, didTryPurchasing: boolean)` This event fires when a purchase prompt for an affiliate gear sale or other asset closes. For example, when a user receives the purchase prompt and clicks **Cancel**, or when they receive a success or error message and click **OK**. #### See Also - [PromptSubscriptionPurchase](/docs/reference/engine/classes/MarketplaceService.md) to prompt a user to purchase a subscription. - [UserSubscriptionStatusChanged](/docs/reference/engine/classes/Players.md), which fires when the server recognizes that a user's membership has changed. *Security: None · Capabilities: PromptExternalPurchase* **Parameters:** | Name | Type | Description | |------|------|-------------| | `user` | `Player` | The [Player](/docs/reference/engine/classes/Player.md) who received the prompt. | | `subscriptionId` | `string` | The ID of the subscription with a status change. | | `didTryPurchasing` | `boolean` | Whether the user attempted to purchase the subscription. | ## Callbacks ### Callback: MarketplaceService.ProcessReceipt **Signature:** `MarketplaceService.ProcessReceipt(receiptInfo: Dictionary): ProductPurchaseDecision` `ProcessReceipt` is a callback to process receipts from [developer product purchases](/docs/en-us/production/monetization/developer-products.md). You can sell developer products inside an experience using `MarketplaceService` functions, or outside an experience on the Store tab of your experience details page. You should only set the `ProcessReceipt` callback one time in a single server-side [Script](/docs/reference/engine/classes/Script.md). This callback must handle the receipts for all developer products you have for sale. **IMPORTANT:** It's **highly recommended** that you properly implement the `ProcessReceipt` callback in order to sell your developer products. You should use `ProcessReceipt` to grant users their purchased product over any other granting method. If your `ProcessReceipt` implementation isn't correct, you will **not** be able to grant users the products they have purchased on the Store tab of your experience details page. #### Guarantees The `ProcessReceipt` callback is called for all unresolved developer product purchases when: - A user successfully completes the purchase of a developer product. - A successful developer product purchase prompt appears to the user. - A user joins the server. A purchase is considered **successfully initiated** when: - The purchase is processed on Roblox's backend. - The funds are placed in escrow. A purchase is considered **resolved** when: - The `ProcessReceipt` callback returns a [ProductPurchaseDecision](/docs/reference/engine/enums/ProductPurchaseDecision.md) enum of [PurchaseGranted](/docs/reference/engine/enums/ProductPurchaseDecision.md). - The purchase is successfully recorded on Roblox's backend. #### Unresolved Developer Product Purchases An **unresolved developer product purchase** takes place when a user's purchase of a developer product has not yet been acknowledged by the server through the `ProcessReceipt` function. Unresolved developer product purchases are not removed or refunded after the escrow period expires. #### Retries and Timeouts `ProcessReceipt` has no time-based retry mechanism. If a user makes a purchase that returns a [ProductPurchaseDecision](/docs/reference/engine/enums/ProductPurchaseDecision.md) enum of [NotProcessedYet](/docs/reference/engine/enums/ProductPurchaseDecision.md), the `ProcessReceipt` callback is only called again on the same server if: - The user successfully initiates another developer product purchase. - The user re-joins any server under the same experience. `ProcessReceipt` also has no timeout for yielded callbacks. A `ProcessReceipt` callback can yield for as long as the server is running, and the callback result is still accepted when the result returns. #### Limitations - If you don't implement a `ProcessReceipt` callback, your receipts will be auto-acknowledged. You can't get a receipt back after it has been acknowledged. - When there are multiple purchases pending for a user, `ProcessReceipt` callbacks are called in a non-deterministic order. - The user must be on the server for the `ProcessReceipt` callback to be invoked. - The user does not have to be on the server for the result of the `ProcessReceipt` callback to be recorded on the backend. - The `ProcessReceipt` callback for a specific purchase might run on two different servers at the same time if the user joins the second server before the callback returns on the first server. - The `ProcessReceipt` callback might still fail to be recorded on the backend, even if it returns a [ProductPurchaseDecision](/docs/reference/engine/enums/ProductPurchaseDecision.md) enum of [PurchaseGranted](/docs/reference/engine/enums/ProductPurchaseDecision.md). When this happens, the purchase remains unresolved. *Security: None · Thread Safety: Unsafe · Capabilities: Monetization* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `receiptInfo` | `Dictionary` | | The `receiptInfo` table passed to this callback contains the following data: - `PurchaseId` — A unique identifier for the specific purchase. - `PlayerId` — The user ID of the user who made the purchase. - `ProductId` — The ID of the purchased product. - `PlaceIdWherePurchased` — The place ID in which the purchase was made. Depending on where the user is during gameplay, the purchase place's ID can be the same as or different from the current place's ID. - `CurrencySpent` — The amount of currency spent in the transaction. - `CurrencyType` — The type of currency spent in the purchase; always [CurrencyType.Robux](/docs/reference/engine/enums/CurrencyType.md). - `ProductPurchaseChannel` — How the user acquired the developer product. One of [ProductPurchaseChannel](/docs/reference/engine/enums/ProductPurchaseChannel.md). | **Returns:** `ProductPurchaseDecision` — An enum that represents how the developer product receipt was processed. - `PurchaseGranted`: - Indicates that the experience successfully granted the player the developer product. - Indicates to Roblox that the developer product sale was successful. - `NotProcessedYet`: - Indicates that the experience failed to grant the player the developer product. **ProcessReceipt Callback** The following code sample: - Sets up the `ProcessReceipt` callback function to handle the purchase of two developer products for an experience. - Checks for and records purchases using a `GlobalDataStore` called `PurchaseHistory`. - Properly returns [PurchaseGranted](/docs/reference/engine/enums/ProductPurchaseDecision.md) if the transaction completes successfully, or if the function detects that the purchase has already been granted using the `PurchaseHistory` data store. After the receipt processing routine, it's possible that the purchase was granted but **recording** it as granted failed due to a data store error. This is one unavoidable scenario that leads to duplicate granting of the purchase, because `processReceipt()` will be called for the purchase again. You can mitigate this by keeping another in-memory record of purchases so that the same server will not grant the same purchase twice, but you'll need a [session locking](/docs/en-us/cloud-services/data-stores/player-data-purchasing.md) implementation around your data store to avoid the potential of duplicate grants across servers. ```lua local MarketplaceService = game:GetService("MarketplaceService") local DataStoreService = game:GetService("DataStoreService") local Players = game:GetService("Players") -- Data store setup for tracking purchases that were successfully processed local purchaseHistoryStore = DataStoreService:GetDataStore("PurchaseHistory") local productIdByName = { fullHeal = 123123, gold100 = 456456, } -- A dictionary to look up the handler function to grant a purchase corresponding to a product ID -- These functions return true if the purchase was granted successfully -- These functions must never yield since they're called later within an UpdateAsync() callback local grantPurchaseHandlerByProductId = { [productIdByName.fullHeal] = function(_receipt, player) local character = player.Character local humanoid = character and character:FindFirstChild("Humanoid") -- Ensure the player has a humanoid to heal if not humanoid then return false end -- Heal the player to full Health humanoid.Health = humanoid.MaxHealth -- Indicate a successful grant return true end, [productIdByName.gold100] = function(_receipt, player) local leaderstats = player:FindFirstChild("leaderstats") local goldStat = leaderstats and leaderstats:FindFirstChild("Gold") if not goldStat then return false end -- Add 100 gold to the player's gold stat goldStat.Value += 100 -- Indicate a successful grant return true end, } -- The core ProcessReceipt callback function -- This implementation handles most failure scenarios but does not completely mitigate cross-server data failure scenarios local function processReceipt(receiptInfo) local success, result = pcall( purchaseHistoryStore.UpdateAsync, purchaseHistoryStore, receiptInfo.PurchaseId, function(isPurchased) if isPurchased then -- This purchase was already recorded as granted, so it must have previously been handled -- Avoid calling the grant purchase handler here to prevent granting the purchase twice -- While the value in the data store is already true, true is returned again so that the pcall result variable is also true -- This will later be used to return PurchaseGranted from the receipt processor return true end local player = Players:GetPlayerByUserId(receiptInfo.PlayerId) if not player then -- Avoids granting the purchase if the player is not in the server -- When they rejoin, this receipt processor will be called again return nil end local grantPurchaseHandler = grantPurchaseHandlerByProductId[receiptInfo.ProductId] if not grantPurchaseHandler then -- If there's no handler defined for this product ID, the purchase cannot be processed -- This will never happen as long as a handler is set for every product ID sold in the experience warn(`No purchase handler defined for product ID '{receiptInfo.ProductId}'`) return nil end local handlerSucceeded, handlerResult = pcall(grantPurchaseHandler, receiptInfo, player) if not handlerSucceeded then local errorMessage = handlerResult warn( `Grant purchase handler errored while processing purchase from '{player.Name}' of product ID '{receiptInfo.ProductId}': {errorMessage}` ) return nil end local didHandlerGrantPurchase = handlerResult == true if not didHandlerGrantPurchase then -- The handler did not grant the purchase, so record it as not granted return nil end -- The purchase is now granted to the player, so record it as granted -- This will later be used to return PurchaseGranted from the receipt processor return true end ) if not success then local errorMessage = result warn(`Failed to process receipt due to data store error: {errorMessage}`) return Enum.ProductPurchaseDecision.NotProcessedYet end local didGrantPurchase = result == true return if didGrantPurchase then Enum.ProductPurchaseDecision.PurchaseGranted else Enum.ProductPurchaseDecision.NotProcessedYet end -- Set the callback; this can only be done once by one script on the server MarketplaceService.ProcessReceipt = processReceipt ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: MatchmakingService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated summary: "The service responsible for managing custom matchmaking data." --- # Class: MatchmakingService > The service responsible for managing custom matchmaking data. ## Description [MatchmakingService](/docs/reference/engine/classes/MatchmakingService.md) is responsible for managing custom matchmaking attributes. Use it to read and write matchmaking data. ## Methods ### Method: MatchmakingService:GetServerAttribute **Signature:** `MatchmakingService:GetServerAttribute(name: string): Tuple` Retrieves the value of a specific server attribute. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name` | `string` | | The name of the server attribute. Limited to a maximum of 50 characters. | **Returns:** `Tuple` — Returns the server attribute value if the attribute is found and if the error is `nil`. Otherwise, returns `nil` for the attribute value and an error message. ### Method: MatchmakingService:InitializeServerAttributesForStudio **Signature:** `MatchmakingService:InitializeServerAttributesForStudio(serverAttributes: Dictionary): Tuple` Initiates the server attribute schema and its values to test in Studio. This method is optional and has no effect when running outside of Studio. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `serverAttributes` | `Dictionary` | | An array of attribute name-value pairs. | **Returns:** `Tuple` — Returns `true` if the call succeeded. Otherwise, returns `false` and an error message. ### Method: MatchmakingService:SetServerAttribute **Signature:** `MatchmakingService:SetServerAttribute(name: string, value: Variant): Tuple` Assigns a value to a specific server attribute. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name` | `string` | | The name of the server attribute. Limited to a maximum of 50 characters. | | `value` | `Variant` | | The value of the server attribute. Limited to a maximum of 50 characters. | **Returns:** `Tuple` — Returns `true` if the call succeeded. Otherwise, returns `false` and an error message. **MatchmakingService sample** The following code sample: - Tests the hard-coded `Level`, `Elo`, and `TrainingMode` attributes in Studio with [InitializeServerAttributesForStudio](/docs/reference/engine/classes/MatchmakingService.md). - Gets the `Level` attribute with [GetServerAttribute](/docs/reference/engine/classes/MatchmakingService.md). - Updates the player's `Level` attribute with [SetServerAttribute](/docs/reference/engine/classes/MatchmakingService.md). ```lua local MatchmakingService = game:GetService("MatchmakingService") local RunService = game:GetService("RunService") if RunService:IsStudio() then -- Sets up initial attributes and schema for testing MatchmakingService:InitializeServerAttributesForStudio({ Level = "Advanced", Elo = 123.456, TrainingMode = true }) end -- Retrieves the Level attribute local currentLevel, errorMessage = MatchmakingService:GetServerAttribute("Level") if errorMessage then warn(errorMessage) else print("Current level: " .. currentLevel) end -- Updates the Level attribute value to Advanced local success, errorMessage = MatchmakingService:SetServerAttribute("Level", "Advanced") if not success then warn("Failed to update server attribute [Level] to [Advanced] due to error: " .. errorMessage) else print("Successfully set [Level] to [Advanced]") end ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: MaterialService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service summary: "The game service responsible for managing materials." --- # Class: MaterialService > The game service responsible for managing materials. ## Description MaterialService is the game service responsible for managing materials. It is the container for global [MaterialVariant](/docs/reference/engine/classes/MaterialVariant.md) instances. [MaterialVariant](/docs/reference/engine/classes/MaterialVariant.md) can be child or descendant of MaterialService. For each base Material type, MaterialService internally keeps a set of MaterialVariant references. [MaterialVariant.Name](/docs/reference/engine/classes/MaterialVariant.md) is the key to access it. The [MaterialVariant.Name](/docs/reference/engine/classes/MaterialVariant.md) and [MaterialVariant.BaseMaterial](/docs/reference/engine/classes/MaterialVariant.md) are combined to work as an identifier. If more than one MaterialVariant object has the same name and BaseMaterial under MaterialService, only one of them can be used. MaterialService has some (Material)Name properties. Assigning a MaterialVariant Name replaces the built-in material with the specified MaterialVariant. If the MaterialService can't find a matching MaterialVariant, it falls back to built-in material. Note BaseMaterial should also match, for example, a MaterialVariant with BaseMaterial Grass can only be assigned to MaterialService.GrassName, not AsphaltName or any other names. These properties are not scriptable but can read and write using [MaterialService:GetBaseMaterialOverride()](/docs/reference/engine/classes/MaterialService.md) and [MaterialService:SetBaseMaterialOverride()](/docs/reference/engine/classes/MaterialService.md) function. MaterialService has a [MaterialService.Use2022Materials](/docs/reference/engine/classes/MaterialService.md) property that switches between legacy materials and new materials introduced in year 2022. Because legacy and user-generated (new) terrain materials use different encoding, using legacy terrain materials and MaterialVariant at the same time has a performance penalty. If your game is using pre-2022 terrain materials, avoid overriding any built-in materials. Migrate to 2022 materials if possible. ## Properties ### Property: MaterialService.AsphaltName ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Material Overrides", "capabilities": [ "Material" ] } ``` Specify [MaterialVariant](/docs/reference/engine/classes/MaterialVariant.md) name to override built-in Asphalt. The Specified MaterialVariant must have [MaterialVariant.BaseMaterial](/docs/reference/engine/classes/MaterialVariant.md) set to Asphalt. ### Property: MaterialService.BasaltName ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Material Overrides", "capabilities": [ "Material" ] } ``` Specify [MaterialVariant](/docs/reference/engine/classes/MaterialVariant.md) name to override built-in Basalt. The Specified MaterialVariant must have [MaterialVariant.BaseMaterial](/docs/reference/engine/classes/MaterialVariant.md) set to Basalt. ### Property: MaterialService.BrickName ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Material Overrides", "capabilities": [ "Material" ] } ``` Specify [MaterialVariant](/docs/reference/engine/classes/MaterialVariant.md) name to override built-in Brick. The Specified MaterialVariant must have [MaterialVariant.BaseMaterial](/docs/reference/engine/classes/MaterialVariant.md) set to Brick. ### Property: MaterialService.CardboardName ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Material Overrides", "capabilities": [ "Material" ] } ``` ### Property: MaterialService.CarpetName ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Material Overrides", "capabilities": [ "Material" ] } ``` ### Property: MaterialService.CeramicTilesName ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Material Overrides", "capabilities": [ "Material" ] } ``` ### Property: MaterialService.ClayRoofTilesName ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Material Overrides", "capabilities": [ "Material" ] } ``` ### Property: MaterialService.CobblestoneName ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Material Overrides", "capabilities": [ "Material" ] } ``` Specify [MaterialVariant](/docs/reference/engine/classes/MaterialVariant.md) name to override built-in Cobblestone. The Specified MaterialVariant must have [MaterialVariant.BaseMaterial](/docs/reference/engine/classes/MaterialVariant.md) set to Cobblestone. ### Property: MaterialService.ConcreteName ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Material Overrides", "capabilities": [ "Material" ] } ``` Specify [MaterialVariant](/docs/reference/engine/classes/MaterialVariant.md) name to override built-in Concrete. The Specified MaterialVariant must have [MaterialVariant.BaseMaterial](/docs/reference/engine/classes/MaterialVariant.md) set to Concrete. ### Property: MaterialService.CorrodedMetalName ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Material Overrides", "capabilities": [ "Material" ] } ``` Specify [MaterialVariant](/docs/reference/engine/classes/MaterialVariant.md) name to override built-in CorrodedMetal. The Specified MaterialVariant must have [MaterialVariant.BaseMaterial](/docs/reference/engine/classes/MaterialVariant.md) set to CorrodedMetal. ### Property: MaterialService.CrackedLavaName ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Material Overrides", "capabilities": [ "Material" ] } ``` Specify [MaterialVariant](/docs/reference/engine/classes/MaterialVariant.md) name to override built-in CrackedLava. The Specified MaterialVariant must have [MaterialVariant.BaseMaterial](/docs/reference/engine/classes/MaterialVariant.md) set to CrackedLava. ### Property: MaterialService.DiamondPlateName ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Material Overrides", "capabilities": [ "Material" ] } ``` Specify [MaterialVariant](/docs/reference/engine/classes/MaterialVariant.md) name to override built-in DiamondPlate. The Specified MaterialVariant must have [MaterialVariant.BaseMaterial](/docs/reference/engine/classes/MaterialVariant.md) set to DiamondPlate. ### Property: MaterialService.FabricName ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Material Overrides", "capabilities": [ "Material" ] } ``` Specify [MaterialVariant](/docs/reference/engine/classes/MaterialVariant.md) name to override built-in Fabric. The Specified MaterialVariant must have [MaterialVariant.BaseMaterial](/docs/reference/engine/classes/MaterialVariant.md) set to Fabric. ### Property: MaterialService.FoilName ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Material Overrides", "capabilities": [ "Material" ] } ``` Specify [MaterialVariant](/docs/reference/engine/classes/MaterialVariant.md) name to override built-in Foil. The Specified MaterialVariant must have [MaterialVariant.BaseMaterial](/docs/reference/engine/classes/MaterialVariant.md) set to Foil. ### Property: MaterialService.GlacierName ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Material Overrides", "capabilities": [ "Material" ] } ``` Specify [MaterialVariant](/docs/reference/engine/classes/MaterialVariant.md) name to override built-in Glacier. The Specified MaterialVariant must have [MaterialVariant.BaseMaterial](/docs/reference/engine/classes/MaterialVariant.md) set to Glacier. ### Property: MaterialService.GraniteName ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Material Overrides", "capabilities": [ "Material" ] } ``` Specify [MaterialVariant](/docs/reference/engine/classes/MaterialVariant.md) name to override built-in Granite. The Specified MaterialVariant must have [MaterialVariant.BaseMaterial](/docs/reference/engine/classes/MaterialVariant.md) set to Granite. ### Property: MaterialService.GrassName ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Material Overrides", "capabilities": [ "Material" ] } ``` Specify [MaterialVariant](/docs/reference/engine/classes/MaterialVariant.md) name to override built-in Grass. The Specified MaterialVariant must have [MaterialVariant.BaseMaterial](/docs/reference/engine/classes/MaterialVariant.md) set to Grass. ### Property: MaterialService.GroundName ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Material Overrides", "capabilities": [ "Material" ] } ``` Specify [MaterialVariant](/docs/reference/engine/classes/MaterialVariant.md) name to override built-in Ground. The Specified MaterialVariant must have [MaterialVariant.BaseMaterial](/docs/reference/engine/classes/MaterialVariant.md) set to Ground. ### Property: MaterialService.IceName ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Material Overrides", "capabilities": [ "Material" ] } ``` Specify [MaterialVariant](/docs/reference/engine/classes/MaterialVariant.md) name to override built-in Ice. The Specified MaterialVariant must have [MaterialVariant.BaseMaterial](/docs/reference/engine/classes/MaterialVariant.md) set to Ice. ### Property: MaterialService.LeafyGrassName ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Material Overrides", "capabilities": [ "Material" ] } ``` Specify [MaterialVariant](/docs/reference/engine/classes/MaterialVariant.md) name to override built-in LeafyGrass. The Specified MaterialVariant must have [MaterialVariant.BaseMaterial](/docs/reference/engine/classes/MaterialVariant.md) set to LeafyGrass. ### Property: MaterialService.LeatherName ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Material Overrides", "capabilities": [ "Material" ] } ``` ### Property: MaterialService.LimestoneName ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Material Overrides", "capabilities": [ "Material" ] } ``` Specify [MaterialVariant](/docs/reference/engine/classes/MaterialVariant.md) name to override built-in Limestone. The Specified MaterialVariant must have [MaterialVariant.BaseMaterial](/docs/reference/engine/classes/MaterialVariant.md) set to Limestone. ### Property: MaterialService.MarbleName ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Material Overrides", "capabilities": [ "Material" ] } ``` Specify [MaterialVariant](/docs/reference/engine/classes/MaterialVariant.md) name to override built-in Marble. The Specified MaterialVariant must have [MaterialVariant.BaseMaterial](/docs/reference/engine/classes/MaterialVariant.md) set to Marble. ### Property: MaterialService.MetalName ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Material Overrides", "capabilities": [ "Material" ] } ``` Specify [MaterialVariant](/docs/reference/engine/classes/MaterialVariant.md) name to override built-in Metal. The Specified MaterialVariant must have [MaterialVariant.BaseMaterial](/docs/reference/engine/classes/MaterialVariant.md) set to Metal. ### Property: MaterialService.MudName ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Material Overrides", "capabilities": [ "Material" ] } ``` Specify [MaterialVariant](/docs/reference/engine/classes/MaterialVariant.md) name to override built-in Mud. The Specified MaterialVariant must have [MaterialVariant.BaseMaterial](/docs/reference/engine/classes/MaterialVariant.md) set to Mud. ### Property: MaterialService.PavementName ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Material Overrides", "capabilities": [ "Material" ] } ``` Specify [MaterialVariant](/docs/reference/engine/classes/MaterialVariant.md) name to override built-in Pavement. The Specified MaterialVariant must have [MaterialVariant.BaseMaterial](/docs/reference/engine/classes/MaterialVariant.md) set to Pavement. ### Property: MaterialService.PebbleName ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Material Overrides", "capabilities": [ "Material" ] } ``` Specify [MaterialVariant](/docs/reference/engine/classes/MaterialVariant.md) name to override built-in Pebble. The Specified MaterialVariant must have [MaterialVariant.BaseMaterial](/docs/reference/engine/classes/MaterialVariant.md) set to Pebble. ### Property: MaterialService.PlasterName ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Material Overrides", "capabilities": [ "Material" ] } ``` ### Property: MaterialService.PlasticName ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Material Overrides", "capabilities": [ "Material" ] } ``` Specify [MaterialVariant](/docs/reference/engine/classes/MaterialVariant.md) name to override built-in Plastic. The Specified MaterialVariant must have [MaterialVariant.BaseMaterial](/docs/reference/engine/classes/MaterialVariant.md) set to Plastic. ### Property: MaterialService.RockName ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Material Overrides", "capabilities": [ "Material" ] } ``` Specify [MaterialVariant](/docs/reference/engine/classes/MaterialVariant.md) name to override built-in Rock. The Specified MaterialVariant must have [MaterialVariant.BaseMaterial](/docs/reference/engine/classes/MaterialVariant.md) set to Rock. ### Property: MaterialService.RoofShinglesName ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Material Overrides", "capabilities": [ "Material" ] } ``` ### Property: MaterialService.RubberName ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Material Overrides", "capabilities": [ "Material" ] } ``` ### Property: MaterialService.SaltName ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Material Overrides", "capabilities": [ "Material" ] } ``` Specify [MaterialVariant](/docs/reference/engine/classes/MaterialVariant.md) name to override built-in Salt. The Specified MaterialVariant must have [MaterialVariant.BaseMaterial](/docs/reference/engine/classes/MaterialVariant.md) set to Salt. ### Property: MaterialService.SandName ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Material Overrides", "capabilities": [ "Material" ] } ``` Specify [MaterialVariant](/docs/reference/engine/classes/MaterialVariant.md) name to override built-in Sand. The Specified MaterialVariant must have [MaterialVariant.BaseMaterial](/docs/reference/engine/classes/MaterialVariant.md) set to Sand. ### Property: MaterialService.SandstoneName ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Material Overrides", "capabilities": [ "Material" ] } ``` Specify [MaterialVariant](/docs/reference/engine/classes/MaterialVariant.md) name to override built-in Sandstone. The Specified MaterialVariant must have [MaterialVariant.BaseMaterial](/docs/reference/engine/classes/MaterialVariant.md) set to Sandstone. ### Property: MaterialService.SlateName ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Material Overrides", "capabilities": [ "Material" ] } ``` Specify [MaterialVariant](/docs/reference/engine/classes/MaterialVariant.md) name to override built-in Slate. The Specified MaterialVariant must have [MaterialVariant.BaseMaterial](/docs/reference/engine/classes/MaterialVariant.md) set to Slate. ### Property: MaterialService.SmoothPlasticName ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Material Overrides", "capabilities": [ "Material" ] } ``` Specify [MaterialVariant](/docs/reference/engine/classes/MaterialVariant.md) name to override built-in SmoothPlastic. The Specified MaterialVariant must have [MaterialVariant.BaseMaterial](/docs/reference/engine/classes/MaterialVariant.md) set to SmoothPlastic. ### Property: MaterialService.SnowName ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Material Overrides", "capabilities": [ "Material" ] } ``` Specify [MaterialVariant](/docs/reference/engine/classes/MaterialVariant.md) name to override built-in Snow. The Specified MaterialVariant must have [MaterialVariant.BaseMaterial](/docs/reference/engine/classes/MaterialVariant.md) set to Snow. ### Property: MaterialService.Use2022Materials ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Material Pack", "capabilities": [ "Material" ] } ``` When it's false, built-in materials use the material pack before 2022. When it's true, built-in materials use the material pack released in 2022. ### Property: MaterialService.WoodName ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Material Overrides", "capabilities": [ "Material" ] } ``` Specify [MaterialVariant](/docs/reference/engine/classes/MaterialVariant.md) name to override built-in Wood. The Specified MaterialVariant must have [MaterialVariant.BaseMaterial](/docs/reference/engine/classes/MaterialVariant.md) set to Wood. ### Property: MaterialService.WoodPlanksName ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Material Overrides", "capabilities": [ "Material" ] } ``` Specify [MaterialVariant](/docs/reference/engine/classes/MaterialVariant.md) name to override built-in WoodPlanks. The Specified MaterialVariant must have [MaterialVariant.BaseMaterial](/docs/reference/engine/classes/MaterialVariant.md) set to WoodPlanks. ## Methods ### Method: MaterialService:GetBaseMaterialOverride **Signature:** `MaterialService:GetBaseMaterialOverride(material: Material): string` Get the override [MaterialVariant](/docs/reference/engine/classes/MaterialVariant.md) name of specified Material type. *Security: None · Thread Safety: Unsafe · Capabilities: Material* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `material` | `Material` | | Material type to be fetched. | **Returns:** `string` — MaterialVariant name currently set as override. ### Method: MaterialService:GetMaterialVariant **Signature:** `MaterialService:GetMaterialVariant(material: Material, name: string): MaterialVariant` Get the effective MaterialVariant reference given a MaterialVariant name and BaseMaterial. This MaterialVariant must be a descendant of MaterialService. Returns `nil` if no matching instance exists. *Security: None · Thread Safety: Unsafe · Capabilities: Material* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `material` | `Material` | | BaseMaterial of MaterialVariant. | | `name` | `string` | | Name of MaterialVariant. | **Returns:** `MaterialVariant` — A MaterialVariant instance that matches parameters. ### Method: MaterialService:SetBaseMaterialOverride **Signature:** `MaterialService:SetBaseMaterialOverride(material: Material, name: string): ()` Set a [MaterialVariant](/docs/reference/engine/classes/MaterialVariant.md) name that overrides a built-in material. *Security: None · Thread Safety: Unsafe · Capabilities: Material* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `material` | `Material` | | The Material type to be changed. | | `name` | `string` | | Name of the MaterialVariant object. | **Returns:** `()` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: MaterialVariant last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances summary: "Represent a variant of a Material." --- # Class: MaterialVariant > Represent a variant of a Material. ## Description Using MaterialVariant objects can expand the variety of materials in an experience. MaterialVariant has properties that can define the appearance of a material. Its name can be set in MaterialService to globally override a built-in material, or set in the [BasePart.MaterialVariant](/docs/reference/engine/classes/BasePart.md) property to change certain Parts. MaterialVariant objects only work as descendants of MaterialService. ## Properties ### Property: MaterialVariant.AlphaMode ```json { "type": "AlphaMode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Material" ] } ``` This property determines how the alpha channel of the [MaterialVariant.ColorMap](/docs/reference/engine/classes/MaterialVariant.md) is used. When set to [Transparency](/docs/reference/engine/enums/AlphaMode.md) and the [MeshPart.Transparency](/docs/reference/engine/classes/MeshPart.md) is set to `0`, opaque pixels in the [ColorMap](/docs/reference/engine/classes/MaterialVariant.md) renders as completely opaque in the 3D scene. This combination works better with depth-based effects and occlusion. When set to [Transparency](/docs/reference/engine/enums/AlphaMode.md) and the [MeshPart.Transparency](/docs/reference/engine/classes/MeshPart.md) is set to at least `0.02`, a different blending method is used that can better represent smooth transparency gradients and soft edges. This combination does not support all effects and occlusion may not be perfect. For more information, see [Alpha modes](/docs/en-us/art/modeling/surface-appearance.md#alpha-modes). ### Property: MaterialVariant.BaseMaterial ```json { "type": "Material", "access": "ReadOnly", "security": { "read": "None", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Material" ] } ``` Category Material this variant belongs to. ### Property: MaterialVariant.ColorMap ```json { "type": "ContentId", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Material" ] } ``` This property determines the color of the surface. This texture is sometimes called the albedo texture. The alpha channel is not used. ### Property: MaterialVariant.ColorMapContent ```json { "type": "Content", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Material" ] } ``` This property determines the color of the surface. This texture is sometimes called the albedo texture. The alpha channel is not used. ### Property: MaterialVariant.CustomPhysicalProperties ```json { "type": "PhysicalProperties", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Material", "capabilities": [ "Material" ] } ``` ### Property: MaterialVariant.EmissiveMaskContent ```json { "type": "Content", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Material" ] } ``` This property determines the apparent emissivity across the surface. An emissive mask is a grayscale image where black pixels correspond to no emissivity, and white pixels correspond to full emissivity. The emissive contribution gets added to total lighting contribution across the surface, and the sum gets multiplied by the albedo texture, which would be sampled from [ColorMap](/docs/reference/engine/classes/MaterialVariant.md) if provided. ### Property: MaterialVariant.EmissiveStrength ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Material" ] } ``` This value is a multiplier to control how strong the emissive contribution is across the surface. If the final contribution of emissives goes beyond supported dynamic range, it will get clamped; this could result in emissive contribution appearing all white when using large emissive strength values. Since this value is a multiplier, the maximum value to prevent clamping depends on all of the other contributing values, including [EmissiveTint](/docs/reference/engine/classes/MaterialVariant.md), [ColorMap](/docs/reference/engine/classes/MaterialVariant.md) and combined lighting contribution. ### Property: MaterialVariant.EmissiveTint ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Material" ] } ``` This value is an RGB color for tinting emissive contribution. ### Property: MaterialVariant.MaterialPattern ```json { "type": "MaterialPattern", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Material" ] } ``` Determines texture tiling method. ### Property: MaterialVariant.MetalnessMap ```json { "type": "ContentId", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Material" ] } ``` This property determines which parts of the surface are metal and are non-metal. A metalness map is a grayscale image where black pixels correspond to non-metals and white pixels correspond to metals. Metals only reflect light the same color as the metal, and they reflect much more light than non-metals. Most materials in the real world can be categorized either metals or non-metals. For this reason, most pixels in a metalness map will be either pure black or pure white. Values in between are typically used to simulate dirt or grunge on top of an underlying metal area. When [Lighting.EnvironmentSpecularScale](/docs/reference/engine/classes/Lighting.md) is 0, metalness has no effect. For the most realistic reflections, setting EnvironmentSpecularScale and [Lighting.EnvironmentDiffuseScale](/docs/reference/engine/classes/Lighting.md) to 1, and [Lighting.Ambient](/docs/reference/engine/classes/Lighting.md) and [Lighting.OutdoorAmbient](/docs/reference/engine/classes/Lighting.md) to (0,0,0) is recommended. ### Property: MaterialVariant.MetalnessMapContent ```json { "type": "Content", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Material" ] } ``` This property determines which parts of the surface are metal and are non-metal. A metalness map is a grayscale image where black pixels correspond to non-metals and white pixels correspond to metals. Metals only reflect light the same color as the metal, and they reflect much more light than non-metals. Most materials in the real world can be categorized either metals or non-metals. For this reason, most pixels in a metalness map will be either pure black or pure white. Values in between are typically used to simulate dirt or grunge on top of an underlying metal area. When [Lighting.EnvironmentSpecularScale](/docs/reference/engine/classes/Lighting.md) is 0, metalness has no effect. For the most realistic reflections, setting EnvironmentSpecularScale and [Lighting.EnvironmentDiffuseScale](/docs/reference/engine/classes/Lighting.md) to 1, and [Lighting.Ambient](/docs/reference/engine/classes/Lighting.md) and [Lighting.OutdoorAmbient](/docs/reference/engine/classes/Lighting.md) to (0,0,0) is recommended. ### Property: MaterialVariant.NormalMap ```json { "type": "ContentId", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Material" ] } ``` This property modifies the lighting of the surface by adding bumps, dents, cracks, and curves without adding more polygons. Normal maps are RGB images that modify the surface's normal vector used for lighting calculations. The R, G, and B channels of the NormalMap correspond to the X, Y, and Z components of the local surface vector respectively, and byte values of 0 and 255 for each channel correspond linearly to normal vector components of -1 and 1.016 respectively. This range is stretched slightly from -1 to 1 so that a byte value of 127 maps to exactly 0. The normal vector's Z axis is always defined as the direction of the underlying mesh's normal. A uniform (127,127,255) image translates to a completely flat normal map where the normal is everywhere perpendicular to the mesh surface. This format is called "tangent space" normal maps. Roblox does not support world space or object space normal maps. Incorrectly flipped normal components can make bumps appear like indents. If you import a normal map and notice the lighting looks off, you may need to invert the G channel of the image. The X and Y axes of the tangent space frame correspond to the X and Y directions in the image after it's transformed by the mesh UVs. If you view your normal map in an image editor as if it were displayed on a surface, normals pointing towards the right side of the screen should appear more red, and normals pointing towards the top side of your screen should appear more green. The terms "DirectX format" and "OpenGL format" are sometimes used to describe whether the G channel of the normal map is inverted or not. Roblox expects the OpenGL format. Roblox expects imported meshes to include tangents. Modeling software may also refer to this as "tangent space" information. If you apply a normal map and it does not seem to make any visual difference, you may need to re-export your mesh along with its tangent information from modeling software. ### Property: MaterialVariant.NormalMapContent ```json { "type": "Content", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Material" ] } ``` This property modifies the lighting of the surface by adding bumps, dents, cracks, and curves without adding more polygons. Normal maps are RGB images that modify the surface's normal vector used for lighting calculations. The R, G, and B channels of the NormalMap correspond to the X, Y, and Z components of the local surface vector respectively, and byte values of 0 and 255 for each channel correspond linearly to normal vector components of -1 and 1.016 respectively. This range is stretched slightly from -1 to 1 so that a byte value of 127 maps to exactly 0. The normal vector's Z axis is always defined as the direction of the underlying mesh's normal. A uniform (127,127,255) image translates to a completely flat normal map where the normal is everywhere perpendicular to the mesh surface. This format is called "tangent space" normal maps. Roblox does not support world space or object space normal maps. Incorrectly flipped normal components can make bumps appear like indents. If you import a normal map and notice the lighting looks off, you may need to invert the G channel of the image. The X and Y axes of the tangent space frame correspond to the X and Y directions in the image after it's transformed by the mesh UVs. If you view your normal map in an image editor as if it were displayed on a surface, normals pointing towards the right side of the screen should appear more red, and normals pointing towards the top side of your screen should appear more green. The terms "DirectX format" and "OpenGL format" are sometimes used to describe whether the G channel of the normal map is inverted or not. Roblox expects the OpenGL format. Roblox expects imported meshes to include tangents. Modeling software may also refer to this as "tangent space" information. If you apply a normal map and it does not seem to make any visual difference, you may need to re-export your mesh along with its tangent information from modeling software. ### Property: MaterialVariant.RoughnessMap ```json { "type": "ContentId", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Material" ] } ``` This property determines the apparent roughness across the surface. A roughness map is a grayscale image where black pixels correspond to a maximally smooth surface, and white pixels correspond to a maximally rough surface. Roughness refers to how much variation the surface has on a very small scale. Reflections on smooth surfaces are sharp and concentrated. Reflections on rough surfaces are more blurry and dispersed. ### Property: MaterialVariant.RoughnessMapContent ```json { "type": "Content", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Material" ] } ``` This property determines the apparent roughness across the surface. A roughness map is a grayscale image where black pixels correspond to a maximally smooth surface, and white pixels correspond to a maximally rough surface. Roughness refers to how much variation the surface has on a very small scale. Reflections on smooth surfaces are sharp and concentrated. Reflections on rough surfaces are more blurry and dispersed. ### Property: MaterialVariant.StudsPerTile ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Material" ] } ``` Determines the scale of textures. Larger values for this property will lead to the textures appearing larger, and repeating less frequently. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: MemStorageConnection last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated --- # Class: MemStorageConnection ## Methods ### Method: MemStorageConnection:Disconnect **Signature:** `MemStorageConnection:Disconnect(): ()` *Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `()` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: MemStorageService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated --- # Class: MemStorageService ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: MemoryStoreHashMap last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated summary: "Provides access to a hash map within MemoryStoreService." --- # Class: MemoryStoreHashMap > Provides access to a hash map within [MemoryStoreService](/docs/reference/engine/classes/MemoryStoreService.md). ## Description Provides access to a hash map within [MemoryStoreService](/docs/reference/engine/classes/MemoryStoreService.md). A hash map is a collection of items where string keys are associated with arbitrary values (up to the maximum allowed size -- see [Memory Stores](/docs/en-us/cloud-services/memory-stores/hash-map.md)). The keys have no ordering guarantees. ## Methods ### Method: MemoryStoreHashMap:GetAsync **Signature:** `MemoryStoreHashMap:GetAsync(key: string): Variant` Retrieves the value of a key in the hash map. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: DataStore* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | The key whose value you want to retrieve. | **Returns:** `Variant` — The value, or `nil` if the key doesn't exist. **Getting data from a MemoryStore Hash Map** ```lua local MemoryStoreService = game:GetService("MemoryStoreService") local hashMap = MemoryStoreService:GetHashMap("HashMap1") local key = "User_1234" local value = 1000 local expiration = 30 local setSuccess, _ = pcall(function() return hashMap:SetAsync(key, value, expiration) end) if setSuccess then print("Set succeeded!") end local item local getSuccess, getError = pcall(function() item = hashMap:GetAsync(key) end) if getSuccess then print(item) else warn(getError) end ``` ### Method: MemoryStoreHashMap:ListItemsAsync **Signature:** `MemoryStoreHashMap:ListItemsAsync(count: int): MemoryStoreHashMapPages` Returns a [MemoryStoreHashMapPages](/docs/reference/engine/classes/MemoryStoreHashMapPages.md) object for enumerating through items in the hash map. The valid range is 1 to 200 inclusive. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: DataStore* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `count` | `int` | | Maximum possible number of items that can be returned. | **Returns:** `MemoryStoreHashMapPages` — A [MemoryStoreHashMapPages](/docs/reference/engine/classes/MemoryStoreHashMapPages.md) instance that enumerates the items as [MemoryStoreHashMapPages](/docs/reference/engine/classes/MemoryStoreHashMapPages.md) instances. **Listing items in a MemoryStore Hash Map** The code below lists all the items in a Memory Store Hash Map. ```lua local MemoryStoreService = game:GetService("MemoryStoreService") local testHashMap = MemoryStoreService:GetHashMap("HashMap1") local EXPIRATION = 600 local NUM_TEST_ITEMS = 32 local function populateHashMap(hashMap: MemoryStoreHashMap, numItems: number): { [string]: any } print("Setting HashMap data...") local createdItems = {} for index = 1, numItems do local key = tostring(index) -- HashMap keys must be strings local value = `{key}_test_value` local success, result = pcall(hashMap.SetAsync, hashMap, key, value, EXPIRATION) if success then createdItems[key] = value else warn(`Error setting key {key}: {result}`) end end print("Done setting HashMap data.") return createdItems end local function getItemsFromAllPages(pages: MemoryStoreHashMapPages): { [string]: any } -- Purely for logging purposes, we track what page number we're on local currentPageNumber = 1 local retrievedItems = {} while not pages.IsFinished do print(`Getting items on page {currentPageNumber}...`) local items = pages:GetCurrentPage() for _, entry in pairs(items) do print(` {entry.key}: {entry.value}`) retrievedItems[entry.key] = entry.value end -- Advance pages if there are more pages to read if not pages.IsFinished then pages:AdvanceToNextPageAsync() currentPageNumber += 1 end end print("Finished reading all pages") return retrievedItems end local function compareAllItems(retrievedItems: { [string]: any }, expectedItems: { [string]: any }): number print("Comparing retrieved items to expected items...") local numMatchingItems = 0 for key, expectedValue in pairs(expectedItems) do if retrievedItems[key] == expectedValue then numMatchingItems += 1 else warn(`Mismatched retrieved value for key {key}: expected {expectedValue}, retrieved {retrievedItems[key]}`) end end print("Comparison complete!") return numMatchingItems end -- Keys added to the hashmap are also added to this expectedItems table. -- Later, the retrieved hashmap items will be compared against this table of expected items. local expectedItems = populateHashMap(testHashMap, NUM_TEST_ITEMS) -- Getting pages can error. In this case, we will let it error and stop program execution, -- but you may want to pcall it and handle it differently. print(`Getting HashMap pages with ListItemsAsync...`) local pages = testHashMap:ListItemsAsync(NUM_TEST_ITEMS) local retrievedItems = getItemsFromAllPages(pages) local numMatchingItems = compareAllItems(retrievedItems, expectedItems) -- If there were no errors setting or getting items, all items should match. print(`Program complete. {numMatchingItems}/{NUM_TEST_ITEMS} retrieved items matched the expected values.`) ``` ### Method: MemoryStoreHashMap:RemoveAsync **Signature:** `MemoryStoreHashMap:RemoveAsync(key: string): ()` Removes an item from the hash map. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: DataStore* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | The key to remove. | **Returns:** `()` **Removing data from a MemoryStore Hash Map** ```lua local MemoryStoreService = game:GetService("MemoryStoreService") local hashMap = MemoryStoreService:GetHashMap("HashMap1") local key = "User_1234" local value = 1000 local expiration = 30 local setSuccess, setError = pcall(function() return hashMap:SetAsync(key, value, expiration) end) if not setSuccess then warn(setError) end local removeSuccess, removeError = pcall(function() hashMap:RemoveAsync(key) end) if removeSuccess then print("Remove succeeded!") else warn(removeError) end ``` ### Method: MemoryStoreHashMap:SetAsync **Signature:** `MemoryStoreHashMap:SetAsync(key: string, value: Variant, expiration: int64): boolean` Sets the value of a key in the hash map, overwriting any existing value. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: DataStore* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | The key whose value to set. | | `value` | `Variant` | | The value to set. | | `expiration` | `int64` | | Item expiration in seconds, after which the item is automatically removed from the hash map. The maximum expiration time is 45 days (3,888,000 seconds). | **Returns:** `boolean` **Adding data to a MemoryStore Hash Map** ```lua local MemoryStoreService = game:GetService("MemoryStoreService") local hashMap = MemoryStoreService:GetHashMap("HashMap1") local key = "User_1234" local value = 1000 local expiration = 30 local setSuccess, setError = pcall(function() return hashMap:SetAsync(key, value, expiration) end) if setSuccess then print("Set succeeded!") else warn(setError) end ``` ### Method: MemoryStoreHashMap:UpdateAsync **Signature:** `MemoryStoreHashMap:UpdateAsync(key: string, transformFunction: Function, expiration: int64): Variant` Retrieves the value of a key from a hash map and lets you update it to a new value. This method accepts a callback function that retrieves the existing key value and passes it to a transform function, which returns the new value for the item, with these exceptions: - If the key does not exist, the old value passed to the function is `nil`. - If the function returns `nil`, the update is canceled. The new value is saved only if the key was not updated (for example, by a different game server) since the moment it was read. If the value changed in that time, the transform function is called again with the most recent item value. This cycle repeats until the value is saved successfully or the transform function returns `nil` to abort the operation. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: DataStore* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | The key whose value you want to update. | | `transformFunction` | `Function` | | The transform function, which you provide. This function takes the old value as an input and returns the new value. | | `expiration` | `int64` | | Item expiration in seconds, after which the item is automatically removed from the hash map. The maximum expiration time is 45 days (3,888,000 seconds). | **Returns:** `Variant` — The last value returned by the transform function. **Updating a Memory Store Hash Map** This sample updates the count of some resource in a shared inventory. The use of `MemoryStoreHashMap:UpdateAsync()` ensures that all player contributions make their way into this shared inventory, even if the contributions occur simultaneously. The function enforces a max resource count of 500. ```lua local MemoryStoreService = game:GetService("MemoryStoreService") local hashMap = MemoryStoreService:GetHashMap("ResourceInventory") local function contributeResources(itemResource, addedCount) local success, newResourceCount = pcall(function() return hashMap:UpdateAsync(itemResource, function(resource) resource = resource or { count = 0 } resource.count = resource.count + addedCount -- ensure we don't exceed the maximum resource count if resource.count > 500 then resource.count = 500 end return resource end, 1200) end) if success then print(newResourceCount) end end contributeResources("myResource", 50) ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: MemoryStoreHashMapPages last_updated: 2026-06-29T19:34:07Z inherits: - Pages - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated summary: "A special type of Pages object whose pages contain key-value pairs from a MemoryStoreHashMap." --- # Class: MemoryStoreHashMapPages > A special type of [Pages](/docs/reference/engine/classes/Pages.md) object whose pages contain key-value pairs > from a [MemoryStoreHashMap](/docs/reference/engine/classes/MemoryStoreHashMap.md). ## Description A special type of [Pages](/docs/reference/engine/classes/Pages.md) object whose pages contain key-value pairs from a [MemoryStoreHashMap](/docs/reference/engine/classes/MemoryStoreHashMap.md). [Pages:GetCurrentPage()](/docs/reference/engine/classes/Pages.md) can be used to retrieve an array of tables, each containing a key and value; these reflect the key-value pair data. ## Code Samples **Listing items in a MemoryStore Hash Map** The code below lists all the items in a Memory Store Hash Map. ```lua local MemoryStoreService = game:GetService("MemoryStoreService") local testHashMap = MemoryStoreService:GetHashMap("HashMap1") local EXPIRATION = 600 local NUM_TEST_ITEMS = 32 local function populateHashMap(hashMap: MemoryStoreHashMap, numItems: number): { [string]: any } print("Setting HashMap data...") local createdItems = {} for index = 1, numItems do local key = tostring(index) -- HashMap keys must be strings local value = `{key}_test_value` local success, result = pcall(hashMap.SetAsync, hashMap, key, value, EXPIRATION) if success then createdItems[key] = value else warn(`Error setting key {key}: {result}`) end end print("Done setting HashMap data.") return createdItems end local function getItemsFromAllPages(pages: MemoryStoreHashMapPages): { [string]: any } -- Purely for logging purposes, we track what page number we're on local currentPageNumber = 1 local retrievedItems = {} while not pages.IsFinished do print(`Getting items on page {currentPageNumber}...`) local items = pages:GetCurrentPage() for _, entry in pairs(items) do print(` {entry.key}: {entry.value}`) retrievedItems[entry.key] = entry.value end -- Advance pages if there are more pages to read if not pages.IsFinished then pages:AdvanceToNextPageAsync() currentPageNumber += 1 end end print("Finished reading all pages") return retrievedItems end local function compareAllItems(retrievedItems: { [string]: any }, expectedItems: { [string]: any }): number print("Comparing retrieved items to expected items...") local numMatchingItems = 0 for key, expectedValue in pairs(expectedItems) do if retrievedItems[key] == expectedValue then numMatchingItems += 1 else warn(`Mismatched retrieved value for key {key}: expected {expectedValue}, retrieved {retrievedItems[key]}`) end end print("Comparison complete!") return numMatchingItems end -- Keys added to the hashmap are also added to this expectedItems table. -- Later, the retrieved hashmap items will be compared against this table of expected items. local expectedItems = populateHashMap(testHashMap, NUM_TEST_ITEMS) -- Getting pages can error. In this case, we will let it error and stop program execution, -- but you may want to pcall it and handle it differently. print(`Getting HashMap pages with ListItemsAsync...`) local pages = testHashMap:ListItemsAsync(NUM_TEST_ITEMS) local retrievedItems = getItemsFromAllPages(pages) local numMatchingItems = compareAllItems(retrievedItems, expectedItems) -- If there were no errors setting or getting items, all items should match. print(`Program complete. {numMatchingItems}/{NUM_TEST_ITEMS} retrieved items matched the expected values.`) ``` ## Inherited Members ### From [Pages](/docs/reference/engine/classes/Pages.md) - **Property `IsFinished`** (`boolean`): Whether or not the current page is the last page available. - **Method `AdvanceToNextPageAsync(): ()`**: Iterates to the next page in the pages object, if possible. - **Method `GetCurrentPage(): Array`**: Returns the items on the current page. The keys in the item are determined ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: MemoryStoreQueue last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated summary: "Provides access to a queue within MemoryStore." --- # Class: MemoryStoreQueue > Provides access to a queue within MemoryStore. ## Description Provides access to a queue within MemoryStore. A queue is a data structure that provides temporary storage for arbitrary items (up to the maximum item size -- see [MemoryStore Limits](/docs/en-us/cloud-services/memory-stores.md#limits-and-quotas)). Each queue item has a numeric priority: MemoryStore retrieves items with higher priority from the queue first, and it retrieves Items with the same priority in order of addition. Items in the queue can optionally be set to expire after a certain amount of time. Expired items simply disappear from the queue as if they were never added. ## Methods ### Method: MemoryStoreQueue:AddAsync **Signature:** `MemoryStoreQueue:AddAsync(value: Variant, expiration: int64, priority?: double): ()` Adds an item to the queue. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: DataStore* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `value` | `Variant` | | The value of the item to add to the queue. | | `expiration` | `int64` | | Item expiration time, in seconds, after which the item will be automatically removed from the queue. | | `priority` | `double` | `0` | Item priority. Items with higher priority are retrieved from the queue before items with lower priority. | **Returns:** `()` ### Method: MemoryStoreQueue:GetSizeAsync **Signature:** `MemoryStoreQueue:GetSizeAsync(excludeInvisible?: boolean): int` Gets the size of the queue. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: DataStore* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `excludeInvisible` | `boolean` | `false` | Determines whether to exclude invisible items from the size count. | **Returns:** `int` ### Method: MemoryStoreQueue:ReadAsync **Signature:** `MemoryStoreQueue:ReadAsync(count: int, allOrNothing?: boolean, waitTimeout?: double): Tuple` Reads one or more items from the queue as a single atomic operation. This method does not automatically delete the returned items from the queue but makes them invisible to other ReadAsync calls for the period of the invisibility timeout. The items must be explicitly removed from the queue with [MemoryStoreQueue:RemoveAsync()](/docs/reference/engine/classes/MemoryStoreQueue.md) before the invisibility timeout expires. The invisibility timeout defaults to 30 seconds unless a different value was provided in [MemoryStoreService:GetQueue()](/docs/reference/engine/classes/MemoryStoreService.md). *Yields · Security: None · Thread Safety: Unsafe · Capabilities: DataStore* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `count` | `int` | | Number of items to read. The maximum allowed value of this parameter is 100. | | `allOrNothing` | `boolean` | `false` | Controls the behavior of the method in the case the queue has fewer than `count` items: if set to false the method returns all available items; if set to true, it returns no items. The default value is false. | | `waitTimeout` | `double` | `-1` | The duration, in seconds, for which the method will wait if the required number of items is not immediately available in the queue. Reads are attempted every two seconds during this period. This parameter can be set to zero to indicate no wait. If this parameter is not provided or set to -1, the method will wait indefinitely. | **Returns:** `Tuple` — A tuple of two elements. The first element is an array of item values read from the queue. The second element is a string identifier that should be passed to [MemoryStoreQueue:RemoveAsync()](/docs/reference/engine/classes/MemoryStoreQueue.md) to permanently remove these items from the queue. **Using a MemoryStoreQueue** The following code sample demonstrates using [MemoryStoreQueue:ReadAsync()](/docs/reference/engine/classes/MemoryStoreQueue.md) and [MemoryStoreQueue:RemoveAsync()](/docs/reference/engine/classes/MemoryStoreQueue.md) to reliably read, process, and remove items from a queue. Though this process can be as complicated as necessary, this example simply sets a flag in the corresponding data store item, which guarantees that every item will eventually be processed even if some of the calls encounter errors or the server crashes: - If [MemoryStoreQueue:ReadAsync()](/docs/reference/engine/classes/MemoryStoreQueue.md) fails, no items are retrieved from the queue. An item will be picked up for processing during the next iteration of the loop. - If [MemoryStoreQueue:ReadAsync()](/docs/reference/engine/classes/MemoryStoreQueue.md) succeeds but [DataStoreService:UpdateAsync()](/docs/reference/engine/classes/DataStoreService.md) fails, the item stays invisible until the queue's invisibility timeout expires, causing the item becoming visible again to be returned in a future loop iteration. - Similarly, if [MemoryStoreQueue:RemoveAsync()](/docs/reference/engine/classes/MemoryStoreQueue.md) fails, the item will become visible again in the future and will be processed again. Depending on where the failure happens, it's possible that an item will be processed more than once. You should account for that like the following code sample, in which the end result is the same even if [DataStoreService:UpdateAsync()](/docs/reference/engine/classes/DataStoreService.md) is invoked multiple times. ```lua local MemoryStoreService = game:GetService("MemoryStoreService") local DataStoreService = game:GetService("DataStoreService") local queue = MemoryStoreService:GetQueue("PlayerQueue") local dataStore = DataStoreService:GetDataStore("PlayerStore") while true do pcall(function() -- wait for an item to process local items, id = queue:ReadAsync(1, false, 30) -- check if an item was retrieved if #items > 0 then -- mark the item as processed dataStore:UpdateAsync(items[0], function(data) data = data or {} data.processed = 1 return data end) -- remove the item from the queue queue:RemoveAsync(id) end end) end ``` ### Method: MemoryStoreQueue:RemoveAsync **Signature:** `MemoryStoreQueue:RemoveAsync(id: string): ()` Removes an item or items previously read from the queue. This method uses the identifier returned by [MemoryStoreQueue:ReadAsync()](/docs/reference/engine/classes/MemoryStoreQueue.md) to identify the items to remove. If called after the invisibility timeout has expired, the call has no effect. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: DataStore* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `id` | `string` | | Identifies the items to delete. Use the value returned by [MemoryStoreQueue:ReadAsync()](/docs/reference/engine/classes/MemoryStoreQueue.md). | **Returns:** `()` **Using a MemoryStoreQueue** The following code sample demonstrates using [MemoryStoreQueue:ReadAsync()](/docs/reference/engine/classes/MemoryStoreQueue.md) and [MemoryStoreQueue:RemoveAsync()](/docs/reference/engine/classes/MemoryStoreQueue.md) to reliably read, process, and remove items from a queue. Though this process can be as complicated as necessary, this example simply sets a flag in the corresponding data store item, which guarantees that every item will eventually be processed even if some of the calls encounter errors or the server crashes: - If [MemoryStoreQueue:ReadAsync()](/docs/reference/engine/classes/MemoryStoreQueue.md) fails, no items are retrieved from the queue. An item will be picked up for processing during the next iteration of the loop. - If [MemoryStoreQueue:ReadAsync()](/docs/reference/engine/classes/MemoryStoreQueue.md) succeeds but [DataStoreService:UpdateAsync()](/docs/reference/engine/classes/DataStoreService.md) fails, the item stays invisible until the queue's invisibility timeout expires, causing the item becoming visible again to be returned in a future loop iteration. - Similarly, if [MemoryStoreQueue:RemoveAsync()](/docs/reference/engine/classes/MemoryStoreQueue.md) fails, the item will become visible again in the future and will be processed again. Depending on where the failure happens, it's possible that an item will be processed more than once. You should account for that like the following code sample, in which the end result is the same even if [DataStoreService:UpdateAsync()](/docs/reference/engine/classes/DataStoreService.md) is invoked multiple times. ```lua local MemoryStoreService = game:GetService("MemoryStoreService") local DataStoreService = game:GetService("DataStoreService") local queue = MemoryStoreService:GetQueue("PlayerQueue") local dataStore = DataStoreService:GetDataStore("PlayerStore") while true do pcall(function() -- wait for an item to process local items, id = queue:ReadAsync(1, false, 30) -- check if an item was retrieved if #items > 0 then -- mark the item as processed dataStore:UpdateAsync(items[0], function(data) data = data or {} data.processed = 1 return data end) -- remove the item from the queue queue:RemoveAsync(id) end end) end ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: MemoryStoreService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - Service summary: "Exposes methods to access specific primitives within MemoryStore." --- # Class: MemoryStoreService > Exposes methods to access specific primitives within MemoryStore. ## Description A top-level singleton class which exposes methods to access specific primitives within the MemoryStoreService. Use it for any data that rapidly changes that other servers can restore, such as global leaderboards, matchmaking queues, and auction houses. For a more in-depth look, see [Memory Stores](/docs/en-us/cloud-services/memory-stores.md). For the limits and quotas of the service, see [Limits and Quotas](/docs/en-us/cloud-services/memory-stores.md#limits-and-quotas). ## Methods ### Method: MemoryStoreService:GetHashMap **Signature:** `MemoryStoreService:GetHashMap(name: string): MemoryStoreHashMap` Returns a [MemoryStoreHashMap](/docs/reference/engine/classes/MemoryStoreHashMap.md) instance for the provided name. The name is global within the game, so any place that uses the same name accesses the same hash map. *Security: None · Thread Safety: Unsafe · Capabilities: DataStore* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name` | `string` | | The name of the hash map. | **Returns:** `MemoryStoreHashMap` — A [MemoryStoreHashMap](/docs/reference/engine/classes/MemoryStoreHashMap.md) instance for the provided name. ### Method: MemoryStoreService:GetQueue **Signature:** `MemoryStoreService:GetQueue(name: string, invisibilityTimeout?: int): MemoryStoreQueue` Returns a [MemoryStoreQueue](/docs/reference/engine/classes/MemoryStoreQueue.md) instance for the provided name. The name is global within the game, thus any place that uses the same name accesses the same queue. *Security: None · Thread Safety: Unsafe · Capabilities: DataStore* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name` | `string` | | Name of the queue. | | `invisibilityTimeout` | `int` | `30` | **(Optional)** Invisibility timeout, in seconds, for read operations through this queue instance. If not provided, defaults to 30 seconds. | **Returns:** `MemoryStoreQueue` — A [MemoryStoreQueue](/docs/reference/engine/classes/MemoryStoreQueue.md) instance for the provided name. ### Method: MemoryStoreService:GetSortedMap **Signature:** `MemoryStoreService:GetSortedMap(name: string): MemoryStoreSortedMap` Returns a [MemoryStoreSortedMap](/docs/reference/engine/classes/MemoryStoreSortedMap.md) instance for the provided name. The name is global within the game, so any place that uses the same name accesses the same sorted map. *Security: None · Thread Safety: Unsafe · Capabilities: DataStore* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name` | `string` | | Name of the sorted map. | **Returns:** `MemoryStoreSortedMap` — A [MemoryStoreSortedMap](/docs/reference/engine/classes/MemoryStoreSortedMap.md) instance for the provided name. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: MemoryStoreSortedMap last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated summary: "Provides access to a sorted map within MemoryStoreService." --- # Class: MemoryStoreSortedMap > Provides access to a sorted map within [MemoryStoreService](/docs/reference/engine/classes/MemoryStoreService.md). ## Description Provides access to a sorted map within [MemoryStoreService](/docs/reference/engine/classes/MemoryStoreService.md). A sorted map is a collection of items where string keys are associated with arbitrary values (up to the maximum allowed size -- see [Memory Stores](/docs/en-us/cloud-services/memory-stores/sorted-map.md)). Each item can also have an optional sort key, which can be a number or a string. In the ordering of items, the sort key, if provided, takes precedence over the key. Items with numeric sort keys are sorted before items with string sort keys, which are sorted before items with no sort key. Items with the same sort key and items with no sort key are arranged in alphabetical order by key. ## Methods ### Method: MemoryStoreSortedMap:GetAsync **Signature:** `MemoryStoreSortedMap:GetAsync(key: string): Tuple` Retrieves the value and sort key of a key in the sorted map. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: DataStore* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | Key whose value and sort key to retrieve. | **Returns:** `Tuple` — A tuple of two values: - Key value, or `nil` if there's no item with the specified key. - Sort key, or `nil` if there's no sort key associated with the specified key. ### Method: MemoryStoreSortedMap:GetRangeAsync **Signature:** `MemoryStoreSortedMap:GetRangeAsync(direction: SortDirection, count: int, exclusiveLowerBound: Variant, exclusiveUpperBound: Variant): Array` Gets items within a sorted range of keys and sort keys. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: DataStore* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `direction` | `SortDirection` | | Sort direction, ascending or descending. | | `count` | `int` | | The number of items to retrieve; the maximum allowed value for this parameter is 200. | | `exclusiveLowerBound` | `Variant` | | **(Optional)** Lower bound, exclusive, for the returned keys. This is provided as a table where one or both of key and sort key can be specified: { key: string, sortKey: Variant } . | | `exclusiveUpperBound` | `Variant` | | **(Optional)** Upper bound, exclusive, for the returned keys. This is provided as a table where one or both of key and sort key can be specified: { key: string, sortKey: Variant } . | **Returns:** `Array` — Item keys, values and sort keys in the requested range. **Retrieving MemoryStore Keys** The code below prints all keys in a sorted map in alphabetical order with the help of the [MemoryStoreSortedMap:GetRangeAsync()](/docs/reference/engine/classes/MemoryStoreSortedMap.md) method. ```lua local MemoryStoreService = game:GetService("MemoryStoreService") local myMap = MemoryStoreService:GetSortedMap("MySortedMap") function printAllKeys(map) -- the initial lower bound is nil which means to start from the very first item local exclusiveLowerBound = nil -- this loop continues until the end of the map is reached while true do -- get up to a hundred items starting from the current lower bound local items = map:GetRangeAsync(Enum.SortDirection.Ascending, 100, exclusiveLowerBound) for _, item in ipairs(items) do print(item.key) print(item.sortKey) end -- if the call returned less than a hundred items it means we've reached the end of the map if #items < 100 then break end -- the last retrieved key is the exclusive lower bound for the next iteration exclusiveLowerBound = {} exclusiveLowerBound["key"] = items[#items].key exclusiveLowerBound["sortKey"] = items[#items].sortKey end end printAllKeys(myMap) ``` ### Method: MemoryStoreSortedMap:GetSizeAsync **Signature:** `MemoryStoreSortedMap:GetSizeAsync(): int` Gets the size of the sorted map. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: DataStore* **Returns:** `int` ### Method: MemoryStoreSortedMap:RemoveAsync **Signature:** `MemoryStoreSortedMap:RemoveAsync(key: string): ()` Removes the provided key from the sorted map. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: DataStore* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | Key to remove. | **Returns:** `()` ### Method: MemoryStoreSortedMap:SetAsync **Signature:** `MemoryStoreSortedMap:SetAsync(key: string, value: Variant, expiration: int64, sortKey: Variant): boolean` Sets the value and sort key of the key overwriting any existing key value and sort key. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: DataStore* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | Key whose value to set. | | `value` | `Variant` | | Key value to set. | | `expiration` | `int64` | | Item expiration, in seconds. The item is automatically removed from the sorted map once the expiration duration is reached. The maximum expiration time is 45 days (3,888,000 seconds). | | `sortKey` | `Variant` | | **(Optional)** Sort key to set for this key. Accepted types are a number (integer or decimal) or a string. | **Returns:** `boolean` ### Method: MemoryStoreSortedMap:UpdateAsync **Signature:** `MemoryStoreSortedMap:UpdateAsync(key: string, transformFunction: Function, expiration: int64): Tuple` Retrieves the value and sort key of a key from a sorted map and lets you update it to a new value and sort key via a callback function. This method accepts a callback function that transforms the old value and old sort key into the updated value and updated sort key as required. The method retrieves the existing key value and sort key and passes it to the transform function which returns the new value and sort key for the item, with these exceptions: - If the key does not exist, the old value and old sort key passed to the function will be `nil`. - If the function returns `nil`, the update is canceled. The new value and new sort key is saved only if the key was not updated (e.g. by a different game server) since the moment it was read. If the value or sort key did change, the transform function is invoked again with the most recent item value and sort key. This cycle repeats until the value and sort key are saved successfully or the transform function returns `nil` to abort the operation. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: DataStore* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | Key whose value to update. | | `transformFunction` | `Function` | | A function which you need to provide. The function takes the key's old value and old sort key as input and returns the new value and new sort key. | | `expiration` | `int64` | | Item expiration time, in seconds, after which the item will be automatically removed from the sorted map. The maximum expiration time is 45 days (3,888,000 seconds). | **Returns:** `Tuple` — The return value is a tuple of the last value and sort key returned by the transform function. **Updating a MemoryStore Sorted Map** The code below updates the score in a leaderboard for a player in a game. The score is calculated as kills / deaths. The use of [MemoryStoreSortedMap:UpdateAsync()](/docs/reference/engine/classes/MemoryStoreSortedMap.md) ensures that the kills and deaths are updated for the most recent values even if multiple game servers update the same item simultaneously. A player's kills and deaths are monotonically increasing values and can hence only increase in value in a session. ```lua local MemoryStoreService = game:GetService("MemoryStoreService") local map = MemoryStoreService:GetSortedMap("Leaderboard") local Players = game:GetService("Players") function updateLeaderboard(itemKey, killsToAdd, deathsToAdd) local success, newStats, newScore = pcall(function() return map:UpdateAsync(itemKey, function(playerStats, playerScore) playerStats = playerStats or { kills = 0, deaths = 0 } playerStats.kills += killsToAdd playerStats.deaths += deathsToAdd if playerStats then -- `playerScore` is the sortKey being used to sort items in the map playerScore = playerStats.kills / math.max(playerStats.deaths, 1) return playerStats, playerScore end return nil end, 30) end) end -- Add one kill to all players for i, player in pairs(Players:GetPlayers()) do updateLeaderboard(player.UserId, 1, 0) end -- Add 5 kills and 1 death to player with userId 12345 updateLeaderboard(12345, 5, 1) ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: MeshContentProvider last_updated: 2026-06-29T19:34:07Z inherits: - CacheableContentProvider - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated summary: "An internal Roblox service that is responsible for fetching, parsing and caching meshes. This service cannot be used by developers." --- # Class: MeshContentProvider > An internal Roblox service that is responsible for fetching, parsing and > caching meshes. This service cannot be used by developers. ## Description A service that is internally responsible for fetching, parsing, and caching meshes. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: MeshPart last_updated: 2026-06-29T19:34:07Z inherits: - TriangleMeshPart - BasePart - PVInstance - Instance - Object type: class memory_category: BaseParts summary: "A form of BasePart that includes a physically simulated custom mesh." --- # Class: MeshPart > A form of [BasePart](/docs/reference/engine/classes/BasePart.md) that includes a physically simulated custom mesh. ## Description [MeshPart](/docs/reference/engine/classes/MeshPart.md) is a form of [BasePart](/docs/reference/engine/classes/BasePart.md) that includes a physically simulated custom mesh. Unlike with other mesh classes, such as [SpecialMesh](/docs/reference/engine/classes/SpecialMesh.md) and [BlockMesh](/docs/reference/engine/classes/BlockMesh.md), they are not parented to a [BasePart](/docs/reference/engine/classes/BasePart.md) but rather behave as a [BasePart](/docs/reference/engine/classes/BasePart.md) in their own right. The mesh and texture of a [MeshPart](/docs/reference/engine/classes/MeshPart.md) are determined by the [MeshId](/docs/reference/engine/classes/MeshPart.md) and [TextureID](/docs/reference/engine/classes/MeshPart.md) properties. For more information, see [Meshes](/docs/en-us/parts/meshes.md). ## Properties ### Property: MeshPart.DoubleSided ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` This property determines whether to render both faces of polygons in the mesh. It is only changeable in Studio. This is useful for meshes that are typically modeled as "cards" such as a leaf, hair, or cloth. ### Property: MeshPart.MeshContent ```json { "type": "Content", "access": "ReadOnly", "security": { "read": "None", "write": "NotAccessibleSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` The mesh that is displayed on the [MeshPart](/docs/reference/engine/classes/MeshPart.md). Supports [asset URIs](/docs/en-us/projects/assets.md#asset-uris) and [EditableMesh](/docs/reference/engine/classes/EditableMesh.md) objects. Note that this property cannot be changed directly by scripts, as the collision geometry of the mesh cannot be recomputed in real time. See [AssetService:CreateMeshPartAsync()](/docs/reference/engine/classes/AssetService.md) as a method to create a new [MeshPart](/docs/reference/engine/classes/MeshPart.md) from a given [Content](/docs/reference/engine/datatypes/Content.md) with a specified [CollisionFidelity](/docs/reference/engine/classes/MeshPart.md). [MeshPart:ApplyMesh()](/docs/reference/engine/classes/MeshPart.md) can be used to overwrite the [MeshContent](/docs/reference/engine/classes/MeshPart.md), [TextureContent](/docs/reference/engine/classes/MeshPart.md), and collision geometry of an existing [MeshPart](/docs/reference/engine/classes/MeshPart.md). ### Property: MeshPart.MeshId ```json { "type": "ContentId", "access": "ReadOnly", "security": { "read": "None", "write": "NotAccessibleSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` The [asset URIs](/docs/en-us/projects/assets.md#asset-uris) of the mesh that is displayed on the [MeshPart](/docs/reference/engine/classes/MeshPart.md). Reads and writes to [MeshContent](/docs/reference/engine/classes/MeshPart.md). Note that this property cannot be changed directly by scripts, as the collision geometry of the mesh cannot be recomputed in real time. See [AssetService:CreateMeshPartAsync()](/docs/reference/engine/classes/AssetService.md) as a method to create a new [MeshPart](/docs/reference/engine/classes/MeshPart.md) from a given [Content](/docs/reference/engine/datatypes/Content.md) with a specified [CollisionFidelity](/docs/reference/engine/classes/MeshPart.md). [MeshPart:ApplyMesh()](/docs/reference/engine/classes/MeshPart.md) can be used to overwrite the [MeshContent](/docs/reference/engine/classes/MeshPart.md), [TextureContent](/docs/reference/engine/classes/MeshPart.md), and collision geometry of an existing [MeshPart](/docs/reference/engine/classes/MeshPart.md). ### Property: MeshPart.RenderFidelity ```json { "type": "RenderFidelity", "access": "ReadOnly", "security": { "read": "None", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` This property determines the level of detail that the [MeshPart](/docs/reference/engine/classes/MeshPart.md) will be shown in. It can be set to the possible values of the [RenderFidelity](/docs/reference/engine/enums/RenderFidelity.md) enum. The default value is [Automatic](/docs/reference/engine/enums/RenderFidelity.md), meaning the mesh's detail is based on its distance from the camera as outlined in the following table. | Distance From Camera | Render Fidelity | Example | | --- | --- | --- | | Less than 250 studs | Highest | | | 250-500 studs | Medium | | | 500 or more studs | Lowest | | ### Property: MeshPart.TextureContent ```json { "type": "Content", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` The texture applied to the [MeshPart](/docs/reference/engine/classes/MeshPart.md). Supports [asset URIs](/docs/en-us/projects/assets.md#asset-uris) and [EditableImage](/docs/reference/engine/classes/EditableImage.md) objects. When this property is set to [Content.none](/docs/reference/engine/datatypes/Content.md), no texture will be applied to the mesh. ``` local Workspace = game:GetService("Workspace") local meshPart = Workspace.MeshPart meshPart.TextureContent = Content.none -- No texture ``` Note that the [MeshContent](/docs/reference/engine/classes/MeshPart.md) property cannot be directly changed during runtime but the texture can. ##### Changing a Mesh Texture Using the [TextureContent](/docs/reference/engine/classes/MeshPart.md) property, the texture of a mesh can be changed without having to re-upload the mesh. To do this, a new image can be uploaded to Roblox with the desired texture. The original texture image file can be obtained by exporting the mesh using the **Export Selection** option in Studio. The image file will be saved alongside the exported `.obj` file. The new texture can then be uploaded to Roblox as a decal and its [asset URI](/docs/en-us/projects/assets.md#asset-uris) can be applied to the mesh using the [TextureContent](/docs/reference/engine/classes/MeshPart.md) or [TextureID](/docs/reference/engine/classes/MeshPart.md) property. [TextureContent](/docs/reference/engine/classes/MeshPart.md) can also be set to reference an [EditableImage](/docs/reference/engine/classes/EditableImage.md) that has not been published yet. ``` local AssetService = game:GetService("AssetService") local Workspace = game:GetService("Workspace") local meshPart = Workspace.MeshPart local editableImage = AssetService:CreateEditableImageAsync(meshPart.TextureContent) meshPart.TextureContent = Content.fromObject(editableImage) -- Live updates ``` When [TextureContent](/docs/reference/engine/classes/MeshPart.md) references an [EditableImage](/docs/reference/engine/classes/EditableImage.md), the texture will live update with any edits to the [EditableImage](/docs/reference/engine/classes/EditableImage.md) object. ##### Making a Textured Mesh A mesh can only be textured if the mesh has been UV mapped, referring to the practice of projecting a texture map onto a mesh. This cannot be done using Roblox Studio and must be done using an external 3D modeling application such as [Blender](https://www.blender.org/). ### Property: MeshPart.TextureID ```json { "type": "ContentId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` The texture applied to the [MeshPart](/docs/reference/engine/classes/MeshPart.md). Reads and writes to [TextureContent](/docs/reference/engine/classes/MeshPart.md). When this property is set to an empty string, no texture will be applied to the mesh. ``` local Workspace = game:GetService("Workspace") local meshPart = Workspace.MeshPart meshPart.TextureID = "" -- No texture ``` Note that the [MeshPart.MeshId](/docs/reference/engine/classes/MeshPart.md) property cannot be changed during runtime but the texture can. See [TextureContent](/docs/reference/engine/classes/MeshPart.md) for details. ### Property: MeshPart.HasJointOffset *(hidden)* ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "None", "write": "NotAccessibleSecurity" }, "serialization": { "can_load": true, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` ### Property: MeshPart.HasSkinnedMesh *(hidden)* ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "None", "write": "NotAccessibleSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` ### Property: MeshPart.JointOffset *(hidden)* ```json { "type": "Vector3", "access": "ReadOnly", "security": { "read": "None", "write": "NotAccessibleSecurity" }, "serialization": { "can_load": true, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` ## Methods ### Method: MeshPart:ApplyMesh **Signature:** `MeshPart:ApplyMesh(meshPart: Instance): ()` Overwrites the [MeshContent](/docs/reference/engine/classes/MeshPart.md), [TextureContent](/docs/reference/engine/classes/MeshPart.md), and collision geometry properties of this [MeshPart](/docs/reference/engine/classes/MeshPart.md) from the given source `meshPart`. Most of these properties are read-only and cannot be changed during runtime on their own directly. To keep [MeshContent](/docs/reference/engine/classes/MeshPart.md) and physics data in sync, they must be updated together. Copies the following properties: - [MeshPart.MeshContent](/docs/reference/engine/classes/MeshPart.md) (implicitly updates [MeshId](/docs/reference/engine/classes/MeshPart.md)) - [MeshPart.TextureContent](/docs/reference/engine/classes/MeshPart.md) (implicitly updates [TextureID](/docs/reference/engine/classes/MeshPart.md)) - [MeshPart.RenderFidelity](/docs/reference/engine/classes/MeshPart.md) - [MeshPart.CollisionFidelity](/docs/reference/engine/classes/MeshPart.md) (with any internal collision geometry) - [MeshPart.FluidFidelity](/docs/reference/engine/classes/MeshPart.md) (with any internal aero geometry) - [MeshPart.MeshSize](/docs/reference/engine/classes/MeshPart.md) *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `meshPart` | `Instance` | | | **Returns:** `()` ## Inherited Members ### From [TriangleMeshPart](/docs/reference/engine/classes/TriangleMeshPart.md) - **Property `CollisionFidelity`** (`CollisionFidelity`): Determines the level of detail the part's physics will adhere to its mesh. - **Property `FluidFidelity`** (`FluidFidelity`): Determines the geometric representation used to compute aerodynamic forces - **Property `MeshSize`** (`Vector3`): ### From [BasePart](/docs/reference/engine/classes/BasePart.md) - **Property `Anchored`** (`boolean`): Determines whether a part is immovable by physics. - **Property `AssemblyAngularVelocity`** (`Vector3`): The angular velocity of the part's assembly. - **Property `AssemblyCenterOfMass`** (`Vector3`): The center of mass of the part's assembly in world space. - **Property `AssemblyLinearVelocity`** (`Vector3`): The linear velocity of the part's assembly. - **Property `AssemblyMass`** (`float`): The total mass of the part's assembly. - **Property `AssemblyRootPart`** (`BasePart`): A reference to the root part of the assembly. - **Property `AudioCanCollide`** (`boolean`): Determines whether the part will physically interact with audio - **Property `BackParamA`** (`float`): Determines the first parameter for the SurfaceType on the Back face of a *(deprecated, hidden)* - **Property `BackParamB`** (`float`): Determines the second parameter for the SurfaceType on the Back face of a *(deprecated, hidden)* - **Property `BackSurface`** (`SurfaceType`): Determines the type of surface for the back face of a part. - **Property `BackSurfaceInput`** (`InputType`): Determines the kind of input for the Back face of a part. *(deprecated, hidden)* - **Property `BottomParamA`** (`float`): Determines the first parameter for the SurfaceType on the Bottom face of a *(deprecated, hidden)* - **Property `BottomParamB`** (`float`): Determines the second parameter for the SurfaceType on the Bottom face of *(deprecated, hidden)* - **Property `BottomSurface`** (`SurfaceType`): Determines the type of surface for the bottom face of a part. - **Property `BottomSurfaceInput`** (`InputType`): Determines the kind of input for the Bottom face of a part. *(deprecated, hidden)* - **Property `BrickColor`** (`BrickColor`): Determines the color of a part. - **Property `brickColor`** (`BrickColor`): *(deprecated)* - **Property `CanCollide`** (`boolean`): Determines whether a part may collide with other parts. - **Property `CanQuery`** (`boolean`): Determines whether the part is considered during spatial query operations. - **Property `CanTouch`** (`boolean`): Determines if Touched and - **Property `CastShadow`** (`boolean`): Determines whether or not a part casts a shadow. - **Property `CenterOfMass`** (`Vector3`): Describes the world position in which a part's center of mass is located. - **Property `CFrame`** (`CFrame`): Determines the position and orientation of the BasePart in the - **Property `CollisionGroup`** (`string`): Describes the name of a part's collision group. - **Property `CollisionGroupId`** (`int`): Describes the automatically set ID number of a part's collision group. *(deprecated)* - **Property `Color`** (`Color3`): Determines the color of a part. - **Property `CurrentPhysicalProperties`** (`PhysicalProperties`): Indicates the current physical properties of the part. - **Property `CustomPhysicalProperties`** (`PhysicalProperties`): Determines several physical properties of a part. - **Property `Elasticity`** (`float`): Used to control the Elasticity of the part, but it no longer does *(deprecated, hidden)* - **Property `EnableFluidForces`** (`boolean`): Used to enable or disable aerodynamic forces on parts and assemblies. - **Property `ExtentsCFrame`** (`CFrame`): The CFrame of the physical extents of the BasePart. - **Property `ExtentsSize`** (`Vector3`): The actual physical size of the BasePart as regarded by the - **Property `Friction`** (`float`): Used to control the Friction of the part, but now it no longer does *(deprecated, hidden)* - **Property `FrontParamA`** (`float`): Determines the first parameter for the SurfaceType on the Front face of a *(deprecated, hidden)* - **Property `FrontParamB`** (`float`): Determines the second parameter for the SurfaceType on the Front face of a *(deprecated, hidden)* - **Property `FrontSurface`** (`SurfaceType`): Determines the type of surface for the front face of a part. - **Property `FrontSurfaceInput`** (`InputType`): Determines the kind of input for the Front face of a part (-Z direction). *(deprecated, hidden)* - **Property `LeftParamA`** (`float`): Determines the first parameter for the SurfaceType on the Left face of a *(deprecated, hidden)* - **Property `LeftParamB`** (`float`): Determines the second parameter for the SurfaceType on the Left face of a *(deprecated, hidden)* - **Property `LeftSurface`** (`SurfaceType`): Determines the type of surface for the left face of a part. - **Property `LeftSurfaceInput`** (`InputType`): Determines the kind of input for the Left face of a part. *(deprecated, hidden)* - **Property `LocalTransparencyModifier`** (`float`): Determines a multiplier for BasePart.Transparency that is only *(hidden)* - **Property `Locked`** (`boolean`): Determines whether a part is selectable in Studio. - **Property `Mass`** (`float`): Describes the mass of the part, the product of its density and volume. - **Property `Massless`** (`boolean`): Determines whether the part contributes to the total mass or inertia of - **Property `Material`** (`Material`): Determines the texture and default physical properties of a part. - **Property `MaterialVariant`** (`string`): The name of MaterialVariant. - **Property `Orientation`** (`Vector3`): Describes the rotation of the part in the world. *(hidden)* - **Property `PivotOffset`** (`CFrame`): Specifies the offset of the part's pivot from its CFrame. - **Property `Position`** (`Vector3`): Describes the position of the part in the world. *(hidden)* - **Property `ReceiveAge`** (`float`): Time since last recorded physics update. *(hidden)* - **Property `Reflectance`** (`float`): Determines how much a part reflects the skybox. - **Property `ResizeableFaces`** (`Faces`): Describes the faces on which a part may be resized. - **Property `ResizeIncrement`** (`int`): Describes the smallest change in size allowable by the - **Property `RightParamA`** (`float`): Determines the first parameter for the SurfaceType on the Right face of a *(deprecated, hidden)* - **Property `RightParamB`** (`float`): Determines the second parameter for the SurfaceType on the Right face of a *(deprecated, hidden)* - **Property `RightSurface`** (`SurfaceType`): Determines the type of surface for the right face of a part. - **Property `RightSurfaceInput`** (`InputType`): Determines the kind of input for the Right face of a part (-X direction). *(deprecated, hidden)* - **Property `RootPriority`** (`int`): The main rule in determining the root part of an assembly. - **Property `Rotation`** (`Vector3`): The rotation of the part in degrees for the three axes. - **Property `RotVelocity`** (`Vector3`): Determines a part's change in orientation over time. *(deprecated, hidden)* - **Property `Size`** (`Vector3`): Determines the dimensions of a part (length, width, height). - **Property `SpecificGravity`** (`float`): The ratio of the part's density to the density of water determined by the *(deprecated)* - **Property `TopParamA`** (`float`): Determines the first parameter for the SurfaceType on the Top face of a *(deprecated, hidden)* - **Property `TopParamB`** (`float`): Determines the second parameter for the SurfaceType on the Top face of a *(deprecated, hidden)* - **Property `TopSurface`** (`SurfaceType`): Determines the type of surface for the top face of a part. - **Property `TopSurfaceInput`** (`InputType`): Determines the kind of input for the Top face of a part (+Y direction). *(deprecated, hidden)* - **Property `Transparency`** (`float`): Determines how much a part can be seen through (the inverse of part - **Property `Velocity`** (`Vector3`): Determines a part's change in position over time. *(deprecated, hidden)* - **Method `AngularAccelerationToTorque(angAcceleration: Vector3, angVelocity?: Vector3): Vector3`**: Returns the torque needed to achieve a given angular acceleration on this - **Method `ApplyAngularImpulse(impulse: Vector3): ()`**: Apply an angular impulse to the assembly. - **Method `ApplyImpulse(impulse: Vector3): ()`**: Apply an impulse to the assembly at the assembly's - **Method `ApplyImpulseAtPosition(impulse: Vector3, position: Vector3): ()`**: Apply an impulse to the assembly at specified position. - **Method `BreakJoints(): ()`**: Breaks any surface connection with any adjacent part, including *(deprecated)* - **Method `breakJoints(): ()`**: *(deprecated)* - **Method `CanCollideWith(part: BasePart): boolean`**: Returns whether the parts can collide with each other. - **Method `CanSetNetworkOwnership(): Tuple`**: Checks whether you can set a part's network ownership. - **Method `GetClosestPointOnSurface(position: Vector3): Vector3`**: Returns the closest point on the part's surface to the given point. - **Method `GetConnectedParts(recursive?: boolean): List`**: Returns a table of parts connected to the object by any kind of rigid - **Method `GetJoints(): Instances`**: Return all Joints or Constraints that is connected to this Part. - **Method `GetMass(): float`**: Returns the value of the Mass property. - **Method `getMass(): float`**: *(deprecated)* - **Method `GetNetworkOwner(): Instance`**: Returns the current player who is the network owner of this part, or `nil` - **Method `GetNetworkOwnershipAuto(): boolean`**: Returns true if the game engine automatically decides the network owner - **Method `GetNoCollisionConstraints(): Instances`**: - **Method `GetRenderCFrame(): CFrame`**: OBSOLETE. Returns a CFrame describing where the part is being rendered at. *(deprecated)* - **Method `GetRootPart(): Instance`**: Returns the base part of an assembly of parts. *(deprecated)* - **Method `GetTouchingParts(): Instances`**: Returns a table of all BasePart.CanCollide true parts that - **Method `GetVelocityAtPosition(position: Vector3): Vector3`**: Returns the linear velocity of the part's assembly at the given position - **Method `IntersectAsync(parts: Instances, collisionfidelity?: CollisionFidelity, renderFidelity?: RenderFidelity): Instance`**: Note: It is highly recommended to use the newer - **Method `IsGrounded(): boolean`**: Returns true if the object is connected to a part that will hold it in - **Method `MakeJoints(): ()`**: Creates a joint on any side of the object that has a surface ID that can *(deprecated)* - **Method `makeJoints(): ()`**: *(deprecated)* - **Method `Resize(normalId: NormalId, deltaAmount: int): boolean`**: Changes the size of an object just like using the Studio resize tool. - **Method `resize(normalId: NormalId, deltaAmount: int): boolean`**: *(deprecated)* - **Method `SetNetworkOwner(playerInstance?: Player): ()`**: Sets the given player as network owner for this and all connected parts. - **Method `SetNetworkOwnershipAuto(): ()`**: Lets the game engine dynamically decide who will handle the part's physics - **Method `SubtractAsync(parts: Instances, collisionfidelity?: CollisionFidelity, renderFidelity?: RenderFidelity): Instance`**: Note: It is highly recommended to use the newer - **Method `TorqueToAngularAcceleration(torque: Vector3, angVelocity?: Vector3): Vector3`**: Returns the angular acceleration that would result from applying a given - **Method `UnionAsync(parts: Instances, collisionfidelity?: CollisionFidelity, renderFidelity?: RenderFidelity): Instance`**: Note: It is highly recommended to use the newer - **Event `LocalSimulationTouched`**: *(deprecated)* - **Event `OutfitChanged`**: *(deprecated)* - **Event `StoppedTouching`**: *(deprecated)* - **Event `Touched`**: Fires when a part touches another part as a result of physical movement. - **Event `TouchEnded`**: Fires when a part stops touching another part as a result of physical ### From [PVInstance](/docs/reference/engine/classes/PVInstance.md) - **Property `Origin`** (`CFrame`): - **Property `Pivot Offset`** (`CFrame`): - **Method `GetPivot(): CFrame`**: Gets the pivot of a PVInstance. - **Method `PivotTo(targetCFrame: CFrame): ()`**: Transforms the PVInstance along with all of its descendant ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Message last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - Deprecated summary: "Fills the entire screen with a semi-transparent grey background, with centered text in the middle of the screen." --- # Class: Message > Fills the entire screen with a semi-transparent grey background, with centered > text in the middle of the screen. ## Description Fills the entire screen with a semi-transparent grey background, with centered text in the middle of the screen. Messages will not display if the [Message.Text](/docs/reference/engine/classes/Message.md) field is empty. ## Properties ### Property: Message.Text ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "UI" ] } ``` Sets the text of a [Message](/docs/reference/engine/classes/Message.md) or [Hint](/docs/reference/engine/classes/Hint.md). A [Message](/docs/reference/engine/classes/Message.md) will be invisible if its [Message.Text](/docs/reference/engine/classes/Message.md) property is blank, however the black bar of a [Hint](/docs/reference/engine/classes/Hint.md) will remain visible. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: MessagingService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated summary: "Allows servers of the same experience to communicate with each other." --- # Class: MessagingService > Allows servers of the same experience to communicate with each other. ## Description **MessagingService** allows servers of the same experience to communicate with each other in real time, typically within 1-2 seconds, using topics. Topics are developer‑defined strings (1–80 characters) that servers use to send and receive messages. Delivery is best effort and not guaranteed. Make sure to architect your experience so delivery failures are not critical. [Cross-Server Messaging](/docs/en-us/cloud-services/cross-server-messaging.md) explores how to communicate between servers in greater detail. If you want to publish ad-hoc messages to live game servers, or publish across experiences, you can use the [Open Cloud APIs](/docs/en-us/cloud/guides/usage-messaging.md). #### Limitations Note that these limits are subject to change. | Limit | Maximum | | --- | --- | | **Size of message** | 1kB | | **Messages sent per game server** | 600 + 240 * (number of players in this game server) per minute | | **Messages received per topic** | (40 + 80 * number of servers) per minute | | **Messages received for entire game** | (400 + 200 * number of servers) per minute | | **Subscriptions allowed per game server** | 20 + 8 * (number of players in this game server) | | **Subscribe requests per game server** | 240 requests per minute | ## Methods ### Method: MessagingService:PublishAsync **Signature:** `MessagingService:PublishAsync(topic: string, message: Variant): ()` This function sends the provided message to all subscribers to the topic, triggering their registered callbacks to be invoked. Yields until the message is received by the backend. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: ServerCommunication* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `topic` | `string` | | Determines where the message is sent. | | `message` | `Variant` | | The data to include in the message. | **Returns:** `()` ### Method: MessagingService:SubscribeAsync **Signature:** `MessagingService:SubscribeAsync(topic: string, callback: Function): RBXScriptConnection` This function registers a callback to begin listening to the given topic. The callback is invoked when a topic receives a message. It can be called multiple times for the same topic. #### Callback The callback is invoked with a single argument, a table with the following entries: | Field | Summary | | --- | --- | | **Data** | Developer supplied payload | | **Sent** | Unix time in seconds at which the message was sent | It yields until the subscription is properly registered and returns a connection object. To unsubscribe, call [Disconnect()](/docs/reference/engine/datatypes/RBXScriptConnection.md) on the returned object. Once called, the callback should never be invoked. Killing the script containing the connections also causes the underlying connect to be unsubscribed. See also [MessagingService:PublishAsync()](/docs/reference/engine/classes/MessagingService.md) which sends the provided message to all subscribers to the topic, triggering their registered callbacks to be invoked. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: ServerCommunication* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `topic` | `string` | | Determines where to listen for messages. | | `callback` | `Function` | | Function to be invoked whenever a message is received. | **Returns:** `RBXScriptConnection` — Connection that can be used to unsubscribe from the topic. **Subscribing to Cross Server Messages** This example demonstrates how to use [MessagingService:SubscribeAsync()](/docs/reference/engine/classes/MessagingService.md) to listen to a topic for cross-server chat within a game universe. When a player joins, the example subscribes to the topic _player-_. When a message is sent with this topic, the connected callback executes and prints _Received message for _. It also disconnects the connection when the player's [ancestry changes](/docs/reference/engine/classes/Player.md). In order for this to work as expected it must be placed in a server `Script`. ```lua local MessagingService = game:GetService("MessagingService") local Players = game:GetService("Players") local function onPlayerAdded(player) --subscribe to the topic local topic = "player-" .. player.UserId local connection = MessagingService:SubscribeAsync(topic, function(message) print("Received message for", player.Name, message.Data) end) player.AncestryChanged:Connect(function() -- unsubscribe from the topic connection:Disconnect() end) end Players.PlayerAdded:Connect(onPlayerAdded) ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: MicroProfilerService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated --- # Class: MicroProfilerService ## Properties ### Property: MicroProfilerService.ContextLabel ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Model last_updated: 2026-06-29T19:34:07Z inherits: - PVInstance - Instance - Object type: class memory_category: BaseParts summary: "Models are container objects, meaning they group objects together. They are best used to hold collections of BaseParts and have a number of functions that extend their functionality." --- # Class: Model > Models are container objects, meaning they group objects together. They are > best used to hold collections of [BaseParts](/docs/reference/engine/classes/BasePart.md) and have a number > of functions that extend their functionality. ## Description Models are container objects, meaning they group objects together. They are best used to hold collections of [BaseParts](/docs/reference/engine/classes/BasePart.md) and have a number of functions that extend their functionality. Models are intended to represent **geometric** groupings. If your grouping has no geometric interpretation, for instance a collection of [Scripts](/docs/reference/engine/classes/Script.md), use a [Folder](/docs/reference/engine/classes/Folder.md) instead. Models whose constituent parts are joined together with joints (so that they can move around or be destroyed via physics simulation) usually have a [PrimaryPart](/docs/reference/engine/classes/Model.md) set, as it specifies which part within the model the pivot and bounding box will "follow" as the model moves. Static models which stay in one place do not benefit from having a primary part set. Models have a wide range of applications, including Roblox player characters. They also have a number of unique behaviors that are important to keep in mind: - When a [Humanoid](/docs/reference/engine/classes/Humanoid.md) and a [Part](/docs/reference/engine/classes/Part.md) named **Head** are parented under a model, a name/health GUI will appear over the model; see [Character Name/Health Display](/docs/en-us/characters/name-health-display.md) for details. - If a part's position on the **Y** axis hits the [Workspace.FallenPartsDestroyHeight](/docs/reference/engine/classes/Workspace.md) value, and it was the last object inside of a [Model](/docs/reference/engine/classes/Model.md), the model will be destroyed as well. - When used in a place with [Workspace.StreamingEnabled](/docs/reference/engine/classes/Workspace.md) set to true, the value of [ModelStreamingMode](/docs/reference/engine/classes/Model.md) controls various behaviors around how the model and any descendants are replicated and/or removed from clients. In addition, the value of [LevelOfDetail](/docs/reference/engine/classes/Model.md) impacts rendering of the model. As with all [Instance](/docs/reference/engine/classes/Instance.md) types, the fact that a parent [Model](/docs/reference/engine/classes/Model.md) is replicated to a client does not guarantee that all its children are replicated. This is particularly important if these instances are being accessed by code running on the client, such as in a [LocalScript](/docs/reference/engine/classes/LocalScript.md). Using [ModelStreamingMode](/docs/reference/engine/classes/Model.md) with values such as [Atomic](/docs/reference/engine/enums/ModelStreamingMode.md) can ensure that the entire model and all of its descendants are present if the parent model exists on the client, or you can use [WaitForChild()](/docs/reference/engine/classes/Instance.md) when atomicity is not desired. ## Code Samples **Basic Model Instantiation** The following sample includes a basic function that takes a table of objects and parents them into a new Model, returning that Model. ```lua local function groupObjects(objectTable) local model = Instance.new("Model") for _, object in pairs(objectTable) do object.Parent = model end return model end local objects = { Instance.new("Part"), Instance.new("Part"), } groupObjects(objects) ``` ## Properties ### Property: Model.LevelOfDetail ```json { "type": "ModelLevelOfDetail", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance" } ``` Sets the level of detail on the model for experiences with instance [streaming](/docs/en-us/workspace/streaming.md) enabled. Composite or imposter meshes do not support physics, collision detection, or raycasting. When set to [StreamingMesh](/docs/reference/engine/enums/ModelLevelOfDetail.md), a lower resolution "imposter" mesh (colored, coarse mesh that wraps around all child parts of the model) renders outside the streaming radius. Does not support textures. When set to [SLIM](/docs/reference/engine/enums/ModelLevelOfDetail.md), a **SLIM** model (Scalable Lightweight Interactive Model) renders a composite of all child parts at progressively lower resolutions at distances based on the streaming radius. This greatly improves visual quality over [StreamingMesh](/docs/reference/engine/enums/ModelLevelOfDetail.md). When set to [Disabled](/docs/reference/engine/enums/ModelLevelOfDetail.md) or [Automatic](/docs/reference/engine/enums/ModelLevelOfDetail.md), lower resolution meshes will not be displayed. ### Property: Model.ModelStreamingMode ```json { "type": "ModelStreamingMode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior" } ``` Controls how [Models](/docs/reference/engine/classes/Model.md) are streamed in and out when instance [streaming](/docs/en-us/workspace/streaming.md) is enabled. Behavior depends on the selected enum. Has no effect when streaming is not enabled. This property should only be changed in Studio via the [Properties](/docs/en-us/studio/properties.md) window when streaming is enabled, or in [Scripts](/docs/reference/engine/classes/Script.md), but never in [LocalScripts](/docs/reference/engine/classes/LocalScript.md) (doing so can result in undefined behavior). ### Property: Model.PrimaryPart ```json { "type": "BasePart", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Pivot" } ``` Points to the primary part of the [Model](/docs/reference/engine/classes/Model.md). The primary part is the [BasePart](/docs/reference/engine/classes/BasePart.md) that acts as the physical reference for the pivot of the model. That is, when parts within the model are moved due to physical simulation or other means, the pivot will move in sync with the primary part. Note that [Models](/docs/reference/engine/classes/Model.md) do not have `PrimaryPart` set by default. If you are creating a model that needs to be acted upon by physics, you should manually set this property in Studio or within a script. If the primary part is **not** set, the pivot will remain at the same location in world space, even if parts within the model are moved. Also note that when setting this property, it must be a [BasePart](/docs/reference/engine/classes/BasePart.md) that is a descendant of the model. If you try to set [Model.PrimaryPart](/docs/reference/engine/classes/Model.md) to a [BasePart](/docs/reference/engine/classes/BasePart.md) that is **not** a descendant of the model, it will be set to that part but reset to `nil` during the next simulation step — this is legacy behavior to support scripts which assume they can temporarily set the primary part to a [BasePart](/docs/reference/engine/classes/BasePart.md) which isn't a descendant of the model. The general rule for models is that: - Models whose parts are joined together via physical joints such as [WeldConstraints](/docs/reference/engine/classes/WeldConstraint.md) or [Motor6Ds](/docs/reference/engine/classes/Motor6D.md) should have a primary part assigned. For example, Roblox character models have their [Model.PrimaryPart](/docs/reference/engine/classes/Model.md) set to the **HumanoidRootPart** by default. - Static (usually [Anchored](/docs/reference/engine/classes/BasePart.md)) models which stay in one place unless a script explicitly moves them don't require a [Model.PrimaryPart](/docs/reference/engine/classes/Model.md) and tend not to benefit from having one set. **Throwing Dice** This code sample creates and throws a dice model, then outputs whether it landed with the blue side facing up. If [Model.PrimaryPart](/docs/reference/engine/classes/Model.md) was not set, the pivot / bounding box of the dice would rotate, as the parts inside of it are physically simulated during the roll. ```lua -- Create a dice model with two halves and attach them together local diceModel = Instance.new("Model") diceModel.Name = "ChanceCube" local diceTop = Instance.new("Part") diceTop.Size = Vector3.new(4, 2, 4) diceTop.Position = Vector3.new(0, 1, 0) diceTop.Color = Color3.new(0, 0, 1) diceTop.Parent = diceModel local diceBottom = diceTop:Clone() diceBottom.Position = Vector3.new(0, -1, 0) diceBottom.Color = Color3.new(1, 0, 0) diceBottom.Parent = diceModel local weld = Instance.new("WeldConstraint") weld.Part0 = diceTop weld.Part1 = diceBottom weld.Parent = diceModel -- Put the dice up in the air above the workspace origin (does not require a primary part) diceModel.Parent = workspace diceModel:PivotTo(CFrame.new(0, 10, 0)) -- Assign the primary part before physical simulation -- Without this line, the script will always output the same thing and the bounding box of the model will not change orientation diceModel.PrimaryPart = diceTop -- Wait a bit before rolling the dice (let it settle onto the floor) for i = 5, 1, -1 do print("Rolling dice in...", i) task.wait(1) end diceTop:ApplyAngularImpulse(Vector3.new(15000, 1000, 5000)) diceTop:ApplyImpulse(Vector3.new(0, 3000, 0)) task.wait(1) -- Wait for the roll to complete while diceTop.AssemblyLinearVelocity.Magnitude > 0.1 or diceTop.AssemblyAngularVelocity.Magnitude > 0.1 do task.wait() end -- Get the dice orientation, impacted by the primary part local orientation = diceModel:GetBoundingBox() if orientation.YVector.Y > 0.5 then print("It's the boy!") else print("It's his mother!") end ``` ### Property: Model.Scale ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Pivot" } ``` Setting this property in the Properties window will scale the model as though [Model:ScaleTo()](/docs/reference/engine/classes/Model.md) was called on it, scaling all descendant Instances in the model, such as materials, images, and the 3D geometry of parts, so that the model has the specified scale factor relative to its original size. This property is only available in Studio and will throw an error if used in a [Script](/docs/reference/engine/classes/Script.md) or [LocalScript](/docs/reference/engine/classes/LocalScript.md). [Model:ScaleTo()](/docs/reference/engine/classes/Model.md) and [Model:GetScale()](/docs/reference/engine/classes/Model.md) should be used from scripts. ### Property: Model.WorldPivot ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Pivot" } ``` This property determines where the pivot of a [Model](/docs/reference/engine/classes/Model.md) which does **not** have a set [Model.PrimaryPart](/docs/reference/engine/classes/Model.md) is located. If the [Model](/docs/reference/engine/classes/Model.md) **does** have a [PrimaryPart](/docs/reference/engine/classes/Model.md), the pivot of the [Model](/docs/reference/engine/classes/Model.md) is equal to the pivot of that primary part instead, and this [WorldPivot](/docs/reference/engine/classes/Model.md) property is ignored. For a newly created [Model](/docs/reference/engine/classes/Model.md), its pivot will be treated as the center of the bounding box of its contents until the **first time** its [Model.WorldPivot](/docs/reference/engine/classes/Model.md) property is set. Once the world pivot is set for the first time, it is impossible to restore this initial behavior. Most commonly, moving the model with the Studio tools, or with model movement functions such as [PVInstance:PivotTo()](/docs/reference/engine/classes/PVInstance.md) and [Model:MoveTo()](/docs/reference/engine/classes/Model.md), will set the world pivot and thus end this new model behavior. The purpose of this behavior is to allow Luau code to get a sensible pivot simply by creating a new model and parenting objects to it, avoiding the need to explicitly set [Model.WorldPivot](/docs/reference/engine/classes/Model.md) every time you create a model in code. ``` local Workspace = game:GetService("Workspace") local model = Instance.new("Model") Workspace.BluePart.Parent = model Workspace.RedPart.Parent = model model.Parent = Workspace print(model:GetPivot()) -- Currently equal to the center of the bounding box containing "BluePart" and "RedPart" model:PivotTo(CFrame.new(0, 10, 0)) -- This works without needing to explicitly set "model.WorldPivot" ``` **Reset Pivot** This code sample shows a custom function for resetting the pivot of a model back to the center of that model's bounding box. ```lua local function resetPivot(model) local boundsCFrame = model:GetBoundingBox() if model.PrimaryPart then model.PrimaryPart.PivotOffset = model.PrimaryPart.CFrame:ToObjectSpace(boundsCFrame) else model.WorldPivot = boundsCFrame end end resetPivot(script.Parent) ``` ## Methods ### Method: Model:AddPersistentPlayer **Signature:** `Model:AddPersistentPlayer(playerInstance?: Player): ()` Sets this model to be persistent for the specified player. Persistent models stay present for the player regardless of streaming settings or conditions. [ModelStreamingMode](/docs/reference/engine/classes/Model.md) must be set to [PersistentPerPlayer](/docs/reference/engine/enums/ModelStreamingMode.md) for behavior to be changed as a result of addition. *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `playerInstance` | `Player` | `nil` | The [Player](/docs/reference/engine/classes/Player.md) to make this model persistent for. | **Returns:** `()` ### Method: Model:GetBoundingBox **Signature:** `Model:GetBoundingBox(): Tuple` This function returns a description of a volume that contains all [BasePart](/docs/reference/engine/classes/BasePart.md) children within a [Model](/docs/reference/engine/classes/Model.md). The volume's orientation is based on the orientation of the [PrimaryPart](/docs/reference/engine/classes/Model.md), and matches the selection box rendered in Studio when the model is selected. Mirroring the behavior of [Terrain:FillBlock()](/docs/reference/engine/classes/Terrain.md), it returns a [CFrame](/docs/reference/engine/datatypes/CFrame.md) representing the center of that bounding box and a [Vector3](/docs/reference/engine/datatypes/Vector3.md) representing its size. The size may be inaccurate at runtime if physics constraints are acting upon the parts within the [Model](/docs/reference/engine/classes/Model.md). The orientation of the bounding box matches the orientation of the [Pivot](/docs/reference/engine/classes/PVInstance.md) - either the pivot of the [PrimaryPart](/docs/reference/engine/classes/Model.md) (if present) or the [WorldPivot](/docs/reference/engine/classes/Model.md) of the model. ```lua local Workspace = game:GetService("Workspace") local model = Workspace.Model local part = Workspace.Part local orientation, size = model:GetBoundingBox() -- Resize and position part equal to bounding box of model part.Size = size part.CFrame = orientation ``` *Security: None · Thread Safety: Unsafe* **Returns:** `Tuple` — A [CFrame](/docs/reference/engine/datatypes/CFrame.md) representing the orientation of the volume followed by a [Vector3](/docs/reference/engine/datatypes/Vector3.md) representing the size of the volume. ### Method: Model:GetExtentsSize **Signature:** `Model:GetExtentsSize(): Vector3` Returns the size of the smallest bounding box that contains all of the [BaseParts](/docs/reference/engine/classes/BasePart.md) in the [Model](/docs/reference/engine/classes/Model.md). The orientation matches the orientation of the [Pivot](/docs/reference/engine/classes/PVInstance.md) - either the pivot of the [PrimaryPart](/docs/reference/engine/classes/Model.md) (if present) or the [WorldPivot](/docs/reference/engine/classes/Model.md) of the model. Note this function only returns the size of the smallest bounding box, and the developer must employ their own method to obtain the position of the bounding box. *Security: None · Thread Safety: Unsafe* **Returns:** `Vector3` — The [Vector3](/docs/reference/engine/datatypes/Vector3.md) extents size of the [Model](/docs/reference/engine/classes/Model.md). **Model GetExtentsSize** The code sample below demonstrates how Model.GetExtentsSize can be used to get the size of the bounding box containing the parts. ```lua local model = Instance.new("Model") model.Parent = workspace local RNG = Random.new() for _ = 1, 5 do local part = Instance.new("Part") part.Anchored = true part.Size = Vector3.new(RNG:NextNumber(0.05, 5), RNG:NextNumber(0.05, 5), RNG:NextNumber(0.05, 5)) part.Parent = model end print(model:GetExtentsSize()) ``` ### Method: Model:GetPersistentPlayers **Signature:** `Model:GetPersistentPlayers(): List` When this method is called from a [Script](/docs/reference/engine/classes/Script.md), it returns all the [Player](/docs/reference/engine/classes/Player.md) objects that this model is persistent for. When called from a [LocalScript](/docs/reference/engine/classes/LocalScript.md), this method only checks if this model is persistent for the [LocalPlayer](/docs/reference/engine/classes/Players.md). *Security: None · Thread Safety: Unsafe* **Returns:** `List` — A table with all the [Player](/docs/reference/engine/classes/Player.md) objects that this model object is persistent for. ### Method: Model:GetScale **Signature:** `Model:GetScale(): float` Models contain a persistent canonical scale factor, which starts out at 1 for newly created models and changes as the model is scaled by calling [Model:ScaleTo()](/docs/reference/engine/classes/Model.md). This function returns the current canonical scale factor of the model. The current scale factor does not _directly_ impact the size of Instances under the model. It is used for content authoring and scripting purposes to remember how the model has been scaled relative to its original size. Within a given session, the model will cache the precise original size information of the descendant Instances after the first [Model:ScaleTo()](/docs/reference/engine/classes/Model.md) call. This means that calling [ScaleTo(x)](/docs/reference/engine/classes/Model.md) followed by [ScaleTo(1)](/docs/reference/engine/classes/Model.md) will get you back _exactly_ the original configuration of the model with no floating point drift. Avoiding floating point drift is the motivation for having a Scale**To** function instead of a Scale**By** function. The scale factor does impact engine behavior in one way: The scale factor of a model will be applied to joint offsets of [Animations](/docs/reference/engine/classes/Animation.md) played on an [AnimationController](/docs/reference/engine/classes/AnimationController.md) under that model, so that animated rigs will correctly play back animations even when scaled. *Security: None · Thread Safety: Unsafe · Simulation Access: true* **Returns:** `float` — The current canonical scale factor of the model. **Substituting in a replacement model using PivotTo and ScaleTo** This code sample demonstrates substituting in a replacement for all the copies of a tree model using [Model:PivotTo()](/docs/reference/engine/classes/Model.md) and [Model:ScaleTo()](/docs/reference/engine/classes/Model.md). The pivot and scale of the models are used as a reference ensuring that the relative sizes and locations of the replacement models match those of the originals. ```lua local CollectionService = game:GetService("CollectionService") local ReplicatedStorage = game:GetService("ReplicatedStorage") -- Find all the models with the tag we want to replace local items = CollectionService:GetTagged("Tree") local newModel = ReplicatedStorage.FancyTreeReplacementModel for _, item in items do -- Make the new item and scale / position it where the old one was local newItem = newModel:Clone() newItem:ScaleTo(item:GetScale()) newItem:PivotTo(item:GetPivot()) -- Add the same tag to the replacement CollectionService:AddTag(newItem, "Tree") -- Delete the old item and parent the new one newItem.Parent = item.Parent item:Destroy() end ``` ### Method: Model:MoveTo **Signature:** `Model:MoveTo(position: Vector3): ()` Moves the [PrimaryPart](/docs/reference/engine/classes/Model.md) to the given position. If a primary part has not been specified, the root part of the model will be used, but the root part is not deterministic and it is recommended that you always set a primary part when using [MoveTo()](/docs/reference/engine/classes/Model.md). If there are any obstructions where the model is to be moved, such as [Terrain](/docs/reference/engine/classes/Terrain.md) or other [BaseParts](/docs/reference/engine/classes/BasePart.md), the model will be moved vertically upward until there is nothing in the way. If this behavior is not desired, [PVInstance:PivotTo()](/docs/reference/engine/classes/PVInstance.md) should be used instead. Note that rotation is not preserved when moving a model with [MoveTo()](/docs/reference/engine/classes/Model.md). It is recommended to use either [TranslateBy()](/docs/reference/engine/classes/Model.md) or [PVInstance:PivotTo()](/docs/reference/engine/classes/PVInstance.md) if the current rotation of the model needs to be preserved. *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `position` | `Vector3` | | The [Vector3](/docs/reference/engine/datatypes/Vector3.md) the [Model](/docs/reference/engine/classes/Model.md) is moved to. | **Returns:** `()` **Model MoveTo** This sample demonstrates how [Model:MoveTo()](/docs/reference/engine/classes/Model.md) avoids collisions. A simple two part [Model](/docs/reference/engine/classes/Model.md) is created, and its [Model.PrimaryPart](/docs/reference/engine/classes/Model.md) is set. An large obstruction part is placed next to it. After 5 seconds [Model:MoveTo()](/docs/reference/engine/classes/Model.md) is used to direct the model to move inside the obstruction part. However, as MoveTo will not move a model inside of an obstruction the [Model](/docs/reference/engine/classes/Model.md) is moved up on the Y axis and placed above the obstruction. ```lua local START_POSITION = Vector3.new(-20, 10, 0) local END_POSITION = Vector3.new(0, 10, 0) local model = Instance.new("Model") model.Parent = workspace local part1 = Instance.new("Part") part1.Size = Vector3.new(4, 4, 4) part1.Position = START_POSITION part1.Anchored = true part1.BrickColor = BrickColor.new("Bright yellow") part1.Parent = model local part2 = Instance.new("Part") part2.Size = Vector3.new(2, 2, 2) part2.Position = START_POSITION + Vector3.new(0, 3, 0) part2.Anchored = true part2.BrickColor = BrickColor.new("Bright blue") part2.Parent = model model.PrimaryPart = part1 model.Parent = workspace local obstruction = Instance.new("Part") obstruction.Name = "Obstruction" obstruction.Size = Vector3.new(10, 10, 10) obstruction.Position = Vector3.new(0, 10, 0) obstruction.Anchored = true obstruction.BrickColor = BrickColor.new("Bright green") obstruction.Parent = workspace task.wait(3) model:MoveTo(END_POSITION) ``` ### Method: Model:RemovePersistentPlayer **Signature:** `Model:RemovePersistentPlayer(playerInstance?: Player): ()` Makes this model no longer persistent for the specified player. This does not guarantee the model will immediately be removed for the player; after calling this method, the model will be treated as [Atomic](/docs/reference/engine/enums/ModelStreamingMode.md) for that player and will remain present as long as it is within the target streaming radius. [ModelStreamingMode](/docs/reference/engine/classes/Model.md) must be set to [PersistentPerPlayer](/docs/reference/engine/enums/ModelStreamingMode.md) for behavior to be changed as a result of removal. *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `playerInstance` | `Player` | `nil` | The [Player](/docs/reference/engine/classes/Player.md) to make this model no longer persistent for. | **Returns:** `()` ### Method: Model:ScaleTo **Signature:** `Model:ScaleTo(newScaleFactor: float): ()` Models contain a persistent canonical scale factor, which starts out at 1 for newly created models. This function scales the model, around the pivot location, relative to how it would look at a scale factor of 1. To accomplish this it does two things: - Sets the current scale factor of the model to the specified value - Resizes and repositions all descendant Instances accordingly The scaling of locations is done around the pivot location. All "geometric" properties of descendant Instances will be scaled. That obviously includes the sizes of parts, but here are some other examples of properties which are scaled: - The length of joints like [WeldConstraints](/docs/reference/engine/classes/WeldConstraint.md), and [RopeConstraint](/docs/reference/engine/classes/RopeConstraint.md) - Physical velocities and forces like [HingeConstraint](/docs/reference/engine/classes/HingeConstraint.md) - Visual properties like sizes of particle emitters - Other length properties like [Sound.RollOffMinDistance](/docs/reference/engine/classes/Sound.md) *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `newScaleFactor` | `float` | | | **Returns:** `()` ### Method: Model:TranslateBy **Signature:** `Model:TranslateBy(delta: Vector3): ()` Shifts a [Model](/docs/reference/engine/classes/Model.md) by the given [Vector3](/docs/reference/engine/datatypes/Vector3.md) offset, preserving the model's orientation. If another [BasePart](/docs/reference/engine/classes/BasePart.md) or [Terrain](/docs/reference/engine/classes/Terrain.md) already exists at the new position then the [Model](/docs/reference/engine/classes/Model.md) will overlap said object. The translation is applied in world space rather than object space, meaning even if the model's parts are orientated differently it will still move along the standard axis. *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `delta` | `Vector3` | | The [Vector3](/docs/reference/engine/datatypes/Vector3.md) to translate the [Model](/docs/reference/engine/classes/Model.md) by. | **Returns:** `()` **Model TranslateBy** This sample demonstrates how [Model:TranslateBy()](/docs/reference/engine/classes/Model.md) ignores collisions and respects the orientation of the model. A simple two part [Model](/docs/reference/engine/classes/Model.md) is created, rotated 45 degrees on the Y axis, and its [Model.PrimaryPart](/docs/reference/engine/classes/Model.md) is set. An large obstruction part is placed next to it. After 5 seconds [Model:TranslateBy()](/docs/reference/engine/classes/Model.md) is used to direct the model to move inside the obstruction part. The model will move inside of the obstruction and maintain it's current orientation. ```lua local START_POSITION = Vector3.new(-20, 10, 0) local END_POSITION = Vector3.new(0, 10, 0) local model = Instance.new("Model") local part1 = Instance.new("Part") part1.Size = Vector3.new(4, 4, 4) part1.CFrame = CFrame.new(START_POSITION) * CFrame.Angles(0, math.rad(45), 0) part1.Anchored = true part1.BrickColor = BrickColor.new("Bright yellow") part1.Parent = model local part2 = Instance.new("Part") part2.Size = Vector3.new(2, 2, 2) part2.CFrame = part1.CFrame * CFrame.new(0, 3, 0) part2.Anchored = true part2.BrickColor = BrickColor.new("Bright blue") part2.Parent = model model.PrimaryPart = part1 model.Parent = workspace local obstruction = Instance.new("Part") obstruction.Name = "Obstruction" obstruction.Size = Vector3.new(10, 10, 10) obstruction.Position = Vector3.new(0, 10, 0) obstruction.Transparency = 0.5 obstruction.Anchored = true obstruction.BrickColor = BrickColor.new("Bright green") obstruction.Parent = workspace task.wait(3) -- use TranslateBy to shift the model into the obstruction model:TranslateBy(END_POSITION - START_POSITION) ``` ### Method: Model:BreakJoints **Signature:** `Model:BreakJoints(): ()` Breaks connections between `BaseParts`, including surface connections with any adjacent parts, [WeldConstraints](/docs/reference/engine/classes/WeldConstraint.md), and all [Welds](/docs/reference/engine/classes/Weld.md) and other [JointInstances](/docs/reference/engine/classes/JointInstance.md). When BreakJoints is used on a Player character [Model](/docs/reference/engine/classes/Model.md), the character's [Humanoid](/docs/reference/engine/classes/Humanoid.md) will die as it relies on the Neck joint. Note that although joints produced by surface connections with adjacent Parts can technically be recreated using [Model:MakeJoints()](/docs/reference/engine/classes/Model.md), this will only recreate joints produced by surfaces. Developers should not rely on this as following the joints being broken parts may no longer be in contact with each other. *Security: None · Thread Safety: Unsafe* **Returns:** `()` **Break Character Joints** In this sample the joints in every Player character Model added will be broken 3 seconds after spawning. Breaking these joints will cause the Humanoid to die. ```lua local Players = game:GetService("Players") local function onCharacterAdded(character) task.wait(3) character:BreakJoints() end local function onPlayerAdded(player) player.CharacterAdded:Connect(onCharacterAdded) end Players.PlayerAdded:Connect(onPlayerAdded) ``` **Manual Joint Creation** This code sample demonstrates manual creation of joints on two parts that are siblings of the script (PartA and PartB). It creates a joints on two touching parts with compatible surface types (Studs and Inlet). ```lua local JointsService = game:GetService("JointsService") local partA = script.Parent.PartA local partB = script.Parent.PartB local function join(part0, part1, jointClass, parent) local newJoint = Instance.new(jointClass or "ManualWeld") newJoint.Part0 = part0 newJoint.Part1 = part1 newJoint.C0 = CFrame.new() newJoint.C1 = part1.CFrame:toObjectSpace(part0.CFrame) newJoint.Parent = parent or part0 return newJoint end -- Create some joints and break them join(partA, partB) partA:BreakJoints() -- Glue joints are wobbly join(partA, partB, "Glue") partA:BreakJoints() -- Most of the time, joints ought to be put in JointsService join(partA, partB, "Weld", JointsService) ``` ### Method: Model:breakJoints **Signature:** `Model:breakJoints(): ()` > **Deprecated:** This deprecated function is a variant of [Model:BreakJoints()](/docs/reference/engine/classes/Model.md) which should be used instead. *Security: None · Thread Safety: Unsafe* **Returns:** `()` ### Method: Model:GetModelCFrame **Signature:** `Model:GetModelCFrame(): CFrame` > **Deprecated:** This function has been deprecated as it did not provide reliable results. You can instead use [Model:GetPrimaryPartCFrame()](/docs/reference/engine/classes/Model.md) to retrieve the [CFrame](/docs/reference/engine/datatypes/CFrame.md) of the model's primary part. This value historically returned the CFrame of a central position in the model. *Security: None · Thread Safety: Unsafe* **Returns:** `CFrame` ### Method: Model:GetModelSize **Signature:** `Model:GetModelSize(): Vector3` > **Deprecated:** This item is deprecated. Do not use it for new work. Developers can instead use [Model.GetExtentsSize](/docs/reference/engine/classes/Model.md). The GetModelSize function returns the [Vector3](/docs/reference/engine/datatypes/Vector3.md) size of the [Model](/docs/reference/engine/classes/Model.md). *Security: None · Thread Safety: Unsafe* **Returns:** `Vector3` **Model:GetModelSize** This code would create a model, put some parts in it, position them randomly, and then print its size: ```lua local model = Instance.new("Model") model.Parent = workspace local RNG = Random.new() for _ = 1, 5 do local part = Instance.new("Part") part.Parent = model part.Anchored = true part.Size = Vector3.new(RNG:NextNumber(0.05, 5), RNG:NextNumber(0.05, 5), RNG:NextNumber(0.05, 5)) end print(model:GetModelSize()) ``` ### Method: Model:GetPrimaryPartCFrame **Signature:** `Model:GetPrimaryPartCFrame(): CFrame` This function has been superseded by [PVInstance:GetPivot()](/docs/reference/engine/classes/PVInstance.md) which acts as a replacement and does not change your code's behavior. Use [PVInstance:GetPivot()](/docs/reference/engine/classes/PVInstance.md) for new work and migrate your existing [Model:GetPrimaryPartCFrame()](/docs/reference/engine/classes/Model.md) calls when convenient. Returns the [CFrame](/docs/reference/engine/datatypes/CFrame.md) of the model's [Model.PrimaryPart](/docs/reference/engine/classes/Model.md). This function is equivalent to the following. Model.PrimaryPart.CFrame Note this function will throw an error if no primary part exists for the [Model](/docs/reference/engine/classes/Model.md). If this behavior is not desired developers can do the following, which will be equal to `nil` if there is no primary part. local cFrame = Model.PrimaryPart and Model.PrimaryPart.CFrame *Security: None · Thread Safety: Unsafe* **Returns:** `CFrame` **Rotating a Model** The following code demonstrates how GetPrimaryPartCFrame and SetPrimaryPartCFrame can be used to rotate a model. A simple model is created in the Workspace and a loop is started that will rotate the model 10 degrees around the Y axis every 0.2 seconds. ```lua local START_POSITION = Vector3.new(0, 10, 0) local model = Instance.new("Model") local part1 = Instance.new("Part") part1.Size = Vector3.new(4, 4, 4) part1.CFrame = CFrame.new(START_POSITION) part1.Anchored = true part1.BrickColor = BrickColor.new("Bright yellow") part1.Parent = model local part2 = Instance.new("Part") part2.Size = Vector3.new(2, 2, 2) part2.CFrame = part1.CFrame * CFrame.new(0, 3, 0) part2.Anchored = true part2.BrickColor = BrickColor.new("Bright blue") part2.Parent = model -- set the primary part model.PrimaryPart = part1 model.Parent = workspace while true do local primaryPartCFrame = model:GetPrimaryPartCFrame() local newCFrame = primaryPartCFrame * CFrame.Angles(0, math.rad(1), 0) model:SetPrimaryPartCFrame(newCFrame) task.wait() end ``` ### Method: Model:MakeJoints **Signature:** `Model:MakeJoints(): ()` > **Deprecated:** This joint type has been deprecated. Don't use it for new work. Use [WeldConstraints](/docs/reference/engine/classes/WeldConstraint.md) and [HingeConstraints](/docs/reference/engine/classes/HingeConstraint.md) instead. SurfaceType based joining is deprecated. Don't use MakeJoints for new projects. Use [WeldConstraints](/docs/reference/engine/classes/WeldConstraint.md) and [HingeConstraints](/docs/reference/engine/classes/HingeConstraint.md) instead. Goes through all [Parts](/docs/reference/engine/classes/BasePart.md) in the [Model](/docs/reference/engine/classes/Model.md) and creates joints between the specified Parts and any planar touching surfaces, depending on the parts' surfaces. - Smooth surfaces will not create joints - Glue surfaces will create a [Glue](/docs/reference/engine/classes/Glue.md) joint - Weld will create a [Weld](/docs/reference/engine/classes/Weld.md) joint with any surface except for Unjoinable - Studs, Inlet, or Universal will each create a [Snap](/docs/reference/engine/classes/Snap.md) joint with either of other the other two surfaces (e.g. Studs with Inlet and Universal) - Hinge and Motor surfaces create [Rotate](/docs/reference/engine/classes/Rotate.md) and [RotateV](/docs/reference/engine/classes/RotateV.md) joint instances This function doesn't work if the Part is not a descendant of [Workspace](/docs/reference/engine/classes/Workspace.md). Therefore, you must first ensure the Model is parented to Workspace before using MakeJoints. *Security: None · Thread Safety: Unsafe* **Returns:** `()` **Model MakeJoints** This code sample demonstrates how joints can be made using the Model:MakeJoints function. A model is instanced, with two parts on top of each other. The top part is anchored and the bottom part is not. Normally, when parented to the Workspace the bottom part would fall to the Baseplate. However, as TopSurface property of the bottom part is set to Enum.SurfaceType.Weld, this means that when Model:MakeJoints is ran a connection is made between them. Therefore the bottom part does not drop until the joints in the model are broken. This is often done using Model:BreakJoints, but in this example the connection is broken using an explosion. ```lua local model = Instance.new("Model") -- create one part on top of another local topPart = Instance.new("Part") topPart.Size = Vector3.new(5, 1, 5) topPart.Position = Vector3.new(0, 10, 0) topPart.BrickColor = BrickColor.new("Bright green") topPart.Anchored = true -- anchor the top part topPart.Parent = model local bottomPart = Instance.new("Part") bottomPart.Size = Vector3.new(8, 1, 8) bottomPart.Position = Vector3.new(0, 9, 0) bottomPart.BrickColor = BrickColor.new("Bright green") bottomPart.Anchored = false -- leave bottom unanchored bottomPart.Parent = model topPart.BottomSurface = Enum.SurfaceType.Smooth topPart.TopSurface = Enum.SurfaceType.Smooth bottomPart.BottomSurface = Enum.SurfaceType.Smooth bottomPart.TopSurface = Enum.SurfaceType.Weld -- 'Weld' to create a joint model.Parent = workspace model:MakeJoints() task.wait(2) -- unanchor the bottom part - part does not fall print("Unanchored!") bottomPart.BrickColor = BrickColor.new("Bright red") bottomPart.Anchored = false task.wait(2) -- break the joints using an explosion - part falls local explosion = Instance.new("Explosion") explosion.Position = bottomPart.Position explosion.Parent = workspace ``` **Simple Joint Creation** This code sample demonstrates creation of joints on two parts that are siblings of the script (PartA and PartB). It uses MakeJoints on two touching parts with compatible surface types (Studs and Inlet). ```lua local partA = script.Parent.PartA local partB = script.Parent.PartB -- Move PartB on top of PartA partB.CFrame = partA.CFrame * CFrame.new(0, partB.Size.Y / 2 + partA.Size.Y / 2, 0) -- Studs and Inlet will make joints partA.TopSurface = Enum.SurfaceType.Studs partB.BottomSurface = Enum.SurfaceType.Inlet -- Automatically create a joint between PartA and PartB partA:MakeJoints() ``` ### Method: Model:makeJoints **Signature:** `Model:makeJoints(): ()` > **Deprecated:** This deprecated function is a variant of [Model:MakeJoints()](/docs/reference/engine/classes/Model.md) which should be used instead. *Security: None · Thread Safety: Unsafe* **Returns:** `()` ### Method: Model:move **Signature:** `Model:move(location: Vector3): ()` > **Deprecated:** This item has been superseded by [Model:MoveTo()](/docs/reference/engine/classes/Model.md) which should be used in all new work *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `location` | `Vector3` | | | **Returns:** `()` ### Method: Model:moveTo **Signature:** `Model:moveTo(location: Vector3): ()` > **Deprecated:** This deprecated function is a variant of [Model:MoveTo()](/docs/reference/engine/classes/Model.md) which should be used instead. *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `location` | `Vector3` | | | **Returns:** `()` ### Method: Model:ResetOrientationToIdentity **Signature:** `Model:ResetOrientationToIdentity(): ()` > **Deprecated:** This function has been deprecated; it remains to prevent legacy scripts from throwing errors, but it does nothing when called. Resets the rotation of the model's parts to the previously set identity rotation, which is done through the [Model:SetIdentityOrientation()](/docs/reference/engine/classes/Model.md) method. *Security: None · Thread Safety: Unsafe* **Returns:** `()` ### Method: Model:SetIdentityOrientation **Signature:** `Model:SetIdentityOrientation(): ()` > **Deprecated:** This function has been deprecated; it remains to prevent legacy scripts from throwing errors, but it does nothing when called. Sets the identity rotation of the given model, allowing you to reset the rotation of the entire model later, through the use of the `ResetOrientationToIdentity` method. *Security: None · Thread Safety: Unsafe* **Returns:** `()` ### Method: Model:SetPrimaryPartCFrame **Signature:** `Model:SetPrimaryPartCFrame(cframe: CFrame): ()` This function has been superseded by [PVInstance:PivotTo()](/docs/reference/engine/classes/PVInstance.md) which acts as a more performant replacement and does not change your code's behavior. Use [PVInstance:PivotTo()](/docs/reference/engine/classes/PVInstance.md) for new work and migrate your existing [Model:SetPrimaryPartCFrame()](/docs/reference/engine/classes/Model.md) calls when convenient. Sets the [BasePart.CFrame](/docs/reference/engine/classes/BasePart.md) of the model's [Model.PrimaryPart](/docs/reference/engine/classes/Model.md). All other parts in the model will also be moved and will maintain their orientation and offset respective to the [Model.PrimaryPart](/docs/reference/engine/classes/Model.md). Note, this function will throw an error if no [Model.PrimaryPart](/docs/reference/engine/classes/Model.md) exists for the model. This can cause issues if, for example, the primary part was never set or has been destroyed. *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `cframe` | `CFrame` | | The [CFrame](/docs/reference/engine/datatypes/CFrame.md) to be set. | **Returns:** `()` ## Inherited Members ### From [PVInstance](/docs/reference/engine/classes/PVInstance.md) - **Property `Origin`** (`CFrame`): - **Property `Pivot Offset`** (`CFrame`): - **Method `GetPivot(): CFrame`**: Gets the pivot of a PVInstance. - **Method `PivotTo(targetCFrame: CFrame): ()`**: Transforms the PVInstance along with all of its descendant ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ModerationService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated --- # Class: ModerationService ## Methods ### Method: ModerationService:BindReviewableContentEventProcessor **Signature:** `ModerationService:BindReviewableContentEventProcessor(priority: int, callback: Function): RBXScriptConnection` *Security: None · Thread Safety: Unsafe · Capabilities: Consequences* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `priority` | `int` | | | | `callback` | `Function` | | | **Returns:** `RBXScriptConnection` ### Method: ModerationService:CreateReviewableContentAsync **Signature:** `ModerationService:CreateReviewableContentAsync(config: Dictionary): string` *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Consequences* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `config` | `Dictionary` | | | **Returns:** `string` ### Method: ModerationService:CreateReviewableContentKey **Signature:** `ModerationService:CreateReviewableContentKey(content: Content): string` *Security: None · Thread Safety: Unsafe · Capabilities: Consequences* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `content` | `Content` | | | **Returns:** `string` ### Method: ModerationService:InternalRequestReviewableContentReviewAsync **Signature:** `ModerationService:InternalRequestReviewableContentReviewAsync(config: Dictionary): ()` *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Consequences, InternalTest* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `config` | `Dictionary` | | | **Returns:** `()` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ModuleScript last_updated: 2026-06-29T19:34:07Z inherits: - LuaSourceContainer - Instance - Object type: class memory_category: Script summary: "A script type that runs once when LuaGlobals.require() is called with it. Returns exactly one value, usually a table of functions, to used by other scripts. Useful for compartmentalizing code." --- # Class: ModuleScript > A script type that runs once when [LuaGlobals.require()](/docs/reference/engine/globals/LuaGlobals.md) is called with > it. Returns exactly one value, usually a table of functions, to used by other > scripts. Useful for compartmentalizing code. ## Description A [ModuleScript](/docs/reference/engine/classes/ModuleScript.md) is a script type that returns exactly one value by a call to [LuaGlobals.require()](/docs/reference/engine/globals/LuaGlobals.md). [ModuleScripts](/docs/reference/engine/classes/ModuleScript.md) run once and only once per Luau environment and return the exact same value for subsequent calls to [LuaGlobals.require()](/docs/reference/engine/globals/LuaGlobals.md). [ModuleScripts](/docs/reference/engine/classes/ModuleScript.md) are essential objects for adhering to the "Don't Repeat Yourself" (DRY) principle, allowing you to write a function only once and use it everywhere. Having multiple copies of a function is problematic when you need to change their behavior, so you should define functions or groups of functions in [ModuleScripts](/docs/reference/engine/classes/ModuleScript.md) and have your [Scripts](/docs/reference/engine/classes/Script.md) and [LocalScripts](/docs/reference/engine/classes/LocalScript.md) call [LuaGlobals.require()](/docs/reference/engine/globals/LuaGlobals.md) on those modules. It's important to know that return values from [ModuleScripts](/docs/reference/engine/classes/ModuleScript.md) are independent with regards to [Scripts](/docs/reference/engine/classes/Script.md) and [LocalScripts](/docs/reference/engine/classes/LocalScript.md), and other environments like the [Command Bar](/docs/en-us/studio/ui-overview.md#command-bar). Using [LuaGlobals.require()](/docs/reference/engine/globals/LuaGlobals.md) on a [ModuleScript](/docs/reference/engine/classes/ModuleScript.md) in a [LocalScript](/docs/reference/engine/classes/LocalScript.md) will run the code on the client, even if a [Script](/docs/reference/engine/classes/Script.md) did so already on the server. Therefore, be careful if you're using a [ModuleScript](/docs/reference/engine/classes/ModuleScript.md) on the client and server at the same time, or debugging it within Studio. Note that the first call to [LuaGlobals.require()](/docs/reference/engine/globals/LuaGlobals.md) will not yield (halt) unless the [ModuleScript](/docs/reference/engine/classes/ModuleScript.md) yields (calls [task.wait()](/docs/reference/engine/globals/task.md) for example), in which case the current thread that called [LuaGlobals.require()](/docs/reference/engine/globals/LuaGlobals.md) will yield until the [ModuleScript](/docs/reference/engine/classes/ModuleScript.md) returns a value. If a [ModuleScript](/docs/reference/engine/classes/ModuleScript.md) is attempting to [LuaGlobals.require()](/docs/reference/engine/globals/LuaGlobals.md) another [ModuleScript](/docs/reference/engine/classes/ModuleScript.md) that in turn tries to [LuaGlobals.require()](/docs/reference/engine/globals/LuaGlobals.md) it, the thread will **hang and never halt** (cyclic [LuaGlobals.require()](/docs/reference/engine/globals/LuaGlobals.md) calls do not generate errors). Be mindful of your module dependencies in large projects! If a [ModuleScript](/docs/reference/engine/classes/ModuleScript.md) is uploaded to Roblox and the root module has the name set to `MainModule`, it can be uploaded as a model and required using [LuaGlobals.require()](/docs/reference/engine/globals/LuaGlobals.md) with the model's asset ID. Then it can be loaded into your experience, although this logic only works on the server and will error on the client. If other users want to use the module, it must be public. ## Code Samples **Simple ModuleScript Example** The code sample starts by creating a local variable holding an empty table. It then fills the table with two placeholder functions, and then finally returns the table. Using this code in a ModuleScript would make the `my_functions` table (and thus any functions, tables or other values within it) available to any Script, LocalScript or other ModuleScript. ```lua -- Tables store multiple values in one variable local MyFunctions = {} -- Add a few functions to the table function MyFunctions.foo() print("Foo!") end function MyFunctions.bar() print("Bar!") end -- ModuleScripts must return exactly one value return MyFunctions ``` **Simple ModuleScript Usage** This code sample shows how to use the require function on a ModuleScript, then use the value that it returned. See the "Simple ModuleScript Example" for the code to go with this sample. ```lua -- The require function is provided a ModuleScript, then runs -- the code, waiting until it returns a singular value. local MyFunctions = require(script.Parent.MyFunctions) -- These are some dummy functions defined in another code sample MyFunctions.foo() MyFunctions.bar() ``` ## Properties ### Property: ModuleScript.Source ```json { "type": "ProtectedString", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "PluginOrOpenCloud" ] } ``` The code to be executed. If you want to read or modify a script that the user has open, consider using the [ScriptEditorService](/docs/reference/engine/classes/ScriptEditorService.md) to interact with the Script Editor instead. ### Property: ModuleScript.LinkedSource ```json { "type": "ContentId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "LoadUnownedAsset" ] } ``` > **Deprecated:** This property is now replaced by [packages](/docs/en-us/projects/assets/packages.md) which has greater functionality. Used to store a URL that points to an online script source. Binds the online code to the script's [Script.Source](/docs/reference/engine/classes/Script.md). ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Motor last_updated: 2026-06-29T19:34:07Z inherits: - JointInstance - Instance - Object type: class memory_category: BaseParts summary: "Makes a movable JointInstance between two parts." --- # Class: Motor > Makes a movable [JointInstance](/docs/reference/engine/classes/JointInstance.md) between two parts. ## Description An object used to make movable [JointInstance](/docs/reference/engine/classes/JointInstance.md) between two Parts. ## Properties ### Property: Motor.CurrentAngle ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Physics" ] } ``` Displays the current rotation of the motor in radians. ### Property: Motor.DesiredAngle ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Physics" ] } ``` The desired angle to turn the motor to in radians. The motor will attempt to reach this angle (provided [Motor.MaxVelocity](/docs/reference/engine/classes/Motor.md) is greater than 0. ### Property: Motor.MaxVelocity ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Physics" ] } ``` The maximum velocity the motor can use to reach [Motor.DesiredAngle](/docs/reference/engine/classes/Motor.md) measured in radians per physics frame (1/60th of a second). ## Methods ### Method: Motor:SetDesiredAngle **Signature:** `Motor:SetDesiredAngle(value: float): ()` Sets [Motor.DesiredAngle](/docs/reference/engine/classes/Motor.md) of the motor. *Security: None · Thread Safety: Unsafe · Capabilities: Physics* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `value` | `float` | | | **Returns:** `()` ## Inherited Members ### From [JointInstance](/docs/reference/engine/classes/JointInstance.md) - **Property `Active`** (`boolean`): Determines if the joint is currently active in the world. - **Property `C0`** (`CFrame`): Determines how the offset point is attached to - **Property `C1`** (`CFrame`): Subtracted from the C0 property to create an - **Property `Enabled`** (`boolean`): Sets whether the joint is active or not. - **Property `Part0`** (`BasePart`): The first BasePart that the joint connects. - **Property `Part1`** (`BasePart`): The second BasePart that the joint connects. - **Property `part1`** (`BasePart`): *(deprecated, hidden)* ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Motor6D last_updated: 2026-06-29T19:34:07Z inherits: - Motor - JointInstance - Instance - Object type: class memory_category: BaseParts summary: "Creates an animatable joint between two BaseParts. Superseded by AnimationConstraint for avatar/character rigs. Motor6D is no longer used by default for player characters when AvatarJointUpgrade is enabled." --- # Class: Motor6D > Creates an animatable joint between two [BaseParts](/docs/reference/engine/classes/BasePart.md). Superseded > by [AnimationConstraint](/docs/reference/engine/classes/AnimationConstraint.md) for avatar/character rigs. Motor6D is no longer > used by default for player characters when > [AvatarJointUpgrade](/docs/reference/engine/classes/StarterPlayer.md) is enabled. ## Description #### Description **Motor6D** joins two [BaseParts](/docs/reference/engine/classes/BasePart.md) ([Part0](/docs/reference/engine/classes/JointInstance.md) and [Part1](/docs/reference/engine/classes/JointInstance.md)) together in an animatable way. The [Transform](/docs/reference/engine/classes/Motor6D.md) property determines the offset between these parts. This can be set manually using [RunService.PreSimulation](/docs/reference/engine/classes/RunService.md) or through an [Animator](/docs/reference/engine/classes/Animator.md). Models whose parts are joined by [Motor6D](/docs/reference/engine/classes/Motor6D.md) are usually referred to as **rigs**, typically for [Humanoids](/docs/reference/engine/classes/Humanoid.md). Motor6D remains appropriate for non-avatar mechanical rigs (doors, turrets, vehicles) where physical simulation is not needed. #### Transitioning to AnimationConstraint for Avatar rigs As of the [Avatar Joint Upgrade](https://devforum.roblox.com/t/avatar-joint-upgrade-for-physically-simulated-character-movement-is-now-live/4298561), R15 player characters spawn with [AnimationConstraints](/docs/reference/engine/classes/AnimationConstraint.md) instead of Motor6Ds when [AvatarJointUpgrade](/docs/reference/engine/classes/StarterPlayer.md) is enabled (the default for new experiences). Code that assumes character joints are Motor6Ds — such as `character:FindFirstChildOfClass("Motor6D")` or `joint:IsA("Motor6D")` — will not find joints on upgraded characters. Use [AnimationConstraint](/docs/reference/engine/classes/AnimationConstraint.md) for new avatar rig code. See the migration notes on the [AnimationConstraint](/docs/reference/engine/classes/AnimationConstraint.md) page. ## Properties ### Property: Motor6D.ChildName ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Animatable", "capabilities": [ "Physics" ] } ``` ### Property: Motor6D.ParentName ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Animatable", "capabilities": [ "Physics" ] } ``` ### Property: Motor6D.Transform *(hidden)* ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The internal [CFrame](/docs/reference/engine/datatypes/CFrame.md) that is manipulated when a [Motor6D](/docs/reference/engine/classes/Motor6D.md) is being animated. It is recommended to use this property for custom animations rather than [JointInstance.C0](/docs/reference/engine/classes/JointInstance.md) and [JointInstance.C1](/docs/reference/engine/classes/JointInstance.md). ##### Timing [Motor6D](/docs/reference/engine/classes/Motor6D.md) transforms are not applied immediately, unlike updating [C0](/docs/reference/engine/classes/JointInstance.md) and [C1](/docs/reference/engine/classes/JointInstance.md), but rather as a batch in a parallel job after [RunService.PreSimulation](/docs/reference/engine/classes/RunService.md), immediately before physics steps. The deferred batch update is much more efficient than many immediate updates. If the [Motor6D](/docs/reference/engine/classes/Motor6D.md) is part of an animated model with an [Animator](/docs/reference/engine/classes/Animator.md), then [Motor6D.Transform](/docs/reference/engine/classes/Motor6D.md) will usually be overwritten every frame by the [Animator](/docs/reference/engine/classes/Animator.md) after [RunService.PreAnimation](/docs/reference/engine/classes/RunService.md) and before [RunService.PreSimulation](/docs/reference/engine/classes/RunService.md). ## Inherited Members ### From [Motor](/docs/reference/engine/classes/Motor.md) - **Property `CurrentAngle`** (`float`): Displays the current rotation of the motor in radians. - **Property `DesiredAngle`** (`float`): The desired angle to turn the motor to in radians. - **Property `MaxVelocity`** (`float`): The maximum velocity the motor can use to reach Motor.DesiredAngle - **Method `SetDesiredAngle(value: float): ()`**: Sets Motor.DesiredAngle of the motor. ### From [JointInstance](/docs/reference/engine/classes/JointInstance.md) - **Property `Active`** (`boolean`): Determines if the joint is currently active in the world. - **Property `C0`** (`CFrame`): Determines how the offset point is attached to - **Property `C1`** (`CFrame`): Subtracted from the C0 property to create an - **Property `Enabled`** (`boolean`): Sets whether the joint is active or not. - **Property `Part0`** (`BasePart`): The first BasePart that the joint connects. - **Property `Part1`** (`BasePart`): The second BasePart that the joint connects. - **Property `part1`** (`BasePart`): *(deprecated, hidden)* ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: MotorFeature last_updated: 2026-06-29T19:34:07Z inherits: - Feature - Instance - Object type: class memory_category: Instances tags: - Deprecated --- # Class: MotorFeature ## Description A MotorFeature is an unused type of surface joint. It can be connected to a [Hole](/docs/reference/engine/classes/Hole.md) object by using a [VelocityMotor](/docs/reference/engine/classes/VelocityMotor.md). ## Inherited Members ### From [Feature](/docs/reference/engine/classes/Feature.md) - **Property `FaceId`** (`NormalId`): Sets what side of the Parent the object is on. - **Property `InOut`** (`InOut`): Controls how the Feature is positioned on it's parent's surface, in - **Property `LeftRight`** (`LeftRight`): Controls whether the feature is shifted to the left, center, or right on - **Property `TopBottom`** (`TopBottom`): Controls whether the feature is shifted to the top, center, or bottom on ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Mouse last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable summary: "Legacy object that contains members useful for pointer input." --- # Class: Mouse > Legacy object that contains members useful for pointer input. ## Description **Mouse** has been superseded by [UserInputService](/docs/reference/engine/classes/UserInputService.md) and [ContextActionService](/docs/reference/engine/classes/ContextActionService.md), which cover a broader scope, are more feature rich, and support **cross-platform** patterns better. It remains supported because of its widespread use, but you should strongly consider using these alternatives. The **Mouse** object houses various API for pointers, primarily for buttons and raycasting. It can be accessed through [Player:GetMouse()](/docs/reference/engine/classes/Player.md) called on the [Players.LocalPlayer](/docs/reference/engine/classes/Players.md) in a [LocalScript](/docs/reference/engine/classes/LocalScript.md). It is also passed by the [Tool.Equipped](/docs/reference/engine/classes/Tool.md) event. - It is most notable for the [Icon](/docs/reference/engine/classes/Mouse.md) property, which changes the cursor's appearance. - It continually raycasts the screen mouse position into the 3D world using the [TargetFilter](/docs/reference/engine/classes/Mouse.md) property, storing the results of the raycast in the [Hit](/docs/reference/engine/classes/Mouse.md), [Target](/docs/reference/engine/classes/Mouse.md), and [TargetSurface](/docs/reference/engine/classes/Mouse.md) properties. These can be useful for simple cases, but [WorldRoot:Raycast()](/docs/reference/engine/classes/WorldRoot.md) should be used in more complicated [raycasting](/docs/en-us/workspace/raycasting.md) scenarios. - [Plugins](/docs/reference/engine/classes/Plugin.md) can use [Plugin:GetMouse()](/docs/reference/engine/classes/Plugin.md) to get a [PluginMouse](/docs/reference/engine/classes/PluginMouse.md), which behaves similarly. ```lua -- From a LocalScript: local Players = game:GetService("Players") local player = Players.LocalPlayer local mouse = player:GetMouse() -- Setting the mouse icon mouse.Icon = "rbxasset://SystemCursors/Wait" ``` Note: - This object does not control/restrict pointer movement. For this, see [UserInputService.MouseBehavior](/docs/reference/engine/classes/UserInputService.md) and [UserInputService.MouseDeltaSensitivity](/docs/reference/engine/classes/UserInputService.md). - If two functions are connected to same input event, such as [Button1Down](/docs/reference/engine/classes/Mouse.md), **both** functions will run when the event fires. There is no concept of sinking/passing input, as events don't support this behavior. However, [ContextActionService](/docs/reference/engine/classes/ContextActionService.md) does have this behavior through [BindAction](/docs/reference/engine/classes/ContextActionService.md). - While a mouse may not be available on all platforms, Mouse will still function on mobile (touch) and console (gamepad), which don't typically have mice or pointer hardware. For explicit cross-platform behaviors, use [UserInputService](/docs/reference/engine/classes/UserInputService.md) and [ContextActionService](/docs/reference/engine/classes/ContextActionService.md). See [Input and Camera](/docs/en-us/input.md) for more information on customizing inputs in your experience. ## Properties ### Property: Mouse.Hit ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Input" ] } ``` This property indicates [CFrame](/docs/reference/engine/datatypes/CFrame.md) of the mouse's position in 3D space. Note that [Mouse.TargetFilter](/docs/reference/engine/classes/Mouse.md) and its descendants will be ignored. Developers can get obtain the position of Hit like so: ``` local position = mouse.Hit.Position ``` Hit is often used by [Tools](/docs/reference/engine/classes/Tool.md) to fire a weapon towards the mouse in third person. Developers looking for the [BasePart](/docs/reference/engine/classes/BasePart.md) the mouse is pointing at should use [Mouse.Target](/docs/reference/engine/classes/Mouse.md). #### How is Mouse.Hit calculated? The position of the Hit CFrame is calculated as the point of intersection between the mouse's internal [Ray](/docs/reference/engine/datatypes/Ray.md) (an extended version of [Mouse.UnitRay](/docs/reference/engine/classes/Mouse.md)) and an object in 3D space (such as a part). The orientation of the Hit CFrame corresponds with the direction of the [Mouse.UnitRay](/docs/reference/engine/classes/Mouse.md). ``` local unitRayDirection = mouse.UnitRay.Direction local mouseHitDirection = mouse.Hit.lookVector -- unitRayDirection ≈ mouseHitDirection -- the vectors are approximately equal ``` Note, the roll of the [Workspace.CurrentCamera](/docs/reference/engine/classes/Workspace.md) is not used when calculating the orientation of the Hit [CFrame](/docs/reference/engine/datatypes/CFrame.md). The mouse's internal ray extends for 1,000 studs. If the mouse is not pointing at an object in 3D space (for example when pointing at the sky), this property will be 1,000 studs away from the [Workspace.CurrentCamera](/docs/reference/engine/classes/Workspace.md). **Mouse.Hit Laser Beam** The code in this sample, when placed inside a `LocalScript` within `StarterPlayerScripts` will draw a red laser beam between the character's head and [Mouse.Hit](/docs/reference/engine/classes/Mouse.md) at all times. Note, this beam will pass directly through obstructions in third person as the `Mouse`'s raycasting is done from the [Workspace.CurrentCamera](/docs/reference/engine/classes/Workspace.md) not the head. ```lua local Players = game:GetService("Players") local RunService = game:GetService("RunService") local player = Players.LocalPlayer local mouse = player:GetMouse() local beam = Instance.new("Beam") beam.Segments = 1 beam.Width0 = 0.2 beam.Width1 = 0.2 beam.Color = ColorSequence.new(Color3.new(1, 0, 0)) beam.FaceCamera = true local attachment0 = Instance.new("Attachment") local attachment1 = Instance.new("Attachment") beam.Attachment0 = attachment0 beam.Attachment1 = attachment1 beam.Parent = workspace.Terrain attachment0.Parent = workspace.Terrain attachment1.Parent = workspace.Terrain local function onRenderStep() local character = player.Character if not character then beam.Enabled = false return end local head = character:FindFirstChild("Head") if not head then beam.Enabled = false return end beam.Enabled = true local origin = head.Position local finish = mouse.Hit.Position attachment0.Position = origin attachment1.Position = finish end RunService.RenderStepped:Connect(onRenderStep) ``` **Mouse Origin vs Mouse Hit vs CurrentCamera Position** The code below visualizes the difference between [Mouse.Hit](/docs/reference/engine/classes/Mouse.md) and [Mouse.Origin](/docs/reference/engine/classes/Mouse.md). In order to do this, the code uses the [Vector3](/docs/reference/engine/datatypes/Vector3.md) positions of the hit and origin [CFrame](/docs/reference/engine/datatypes/CFrame.md) values using `.p`. The difference is that the origin is "where the mouse came from" (its origin) and the hit is the position where the mouse hits (is when the player presses their mouse). This example also visualizes that the mouse origin is very similar to the position of the `CurrentCamera` by printing the magnitude (distance) between the two positions. ```lua local Players = game:GetService("Players") local Workspace = game:GetService("Workspace") local player = Players.LocalPlayer local camera = Workspace.CurrentCamera local mouse = player:GetMouse() local camPos = camera.CFrame.Position local function onButton1Down() print("Mouse.Hit:", mouse.Hit.Position) print("camPos:", camPos) print("Mouse.Origin:", mouse.Origin.Position) print("Magnitude:", (mouse.Origin.Position - camPos).Magnitude) end mouse.Button1Down:Connect(onButton1Down) ``` ### Property: Mouse.Icon ```json { "type": "ContentId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Input" ] } ``` **Icon** is a property that determines the image used as the pointer. If blank, a default arrow is used. While the cursor hovers over a [GuiButton](/docs/reference/engine/classes/GuiButton.md), this property is temporarily ignored. To hide the cursor entirely, **do not** use a transparent image – instead, set [UserInputService.MouseIconEnabled](/docs/reference/engine/classes/UserInputService.md) to false. For getting/setting the user mouse icon in experiences, you should use [UserInputService.MouseIcon](/docs/reference/engine/classes/UserInputService.md). [Mouse.Icon](/docs/reference/engine/classes/Mouse.md) will be deprecated after the new API for plugins to set the mouse cursor is released. ##### Designing a Cursor The following guidelines may prove useful when creating your own mouse cursors: - The dimensions of the image used determines the size of the cursor. - The dimensions of the image should not exceed 256 pixels on any axis. - The **center** of the image is where mouse inputs are issued. - The default mouse image is 64x64 pixels, with the mouse taking up 17x24 pixels of space. ##### System Cursors When using a [PluginMouse](/docs/reference/engine/classes/PluginMouse.md) retrieved from [Plugin:GetMouse()](/docs/reference/engine/classes/Plugin.md), you can use the following icons similar to your system's default cursors, such as hands, arrows, I-beams, etc. You can use these with GUI events like [MouseEnter](/docs/reference/engine/classes/GuiObject.md), [MouseLeave](/docs/reference/engine/classes/GuiObject.md), and [MouseButton1Down](/docs/reference/engine/classes/GuiButton.md) to provide a consistent Studio experience when interacting with certain kinds of GUI components. Note that these only work for Studio plugins; they will not work for other [Mouse](/docs/reference/engine/classes/Mouse.md) objects. | Look* | Asset | Suggested Use | | --- | --- | --- | | | `rbxasset://SystemCursors/Arrow` | Default clicking and selection. | | | `rbxasset://SystemCursors/PointingHand` | Hovering over an active link/button. | | | `rbxasset://SystemCursors/OpenHand` | Hovering over a draggable item. | | | `rbxasset://SystemCursors/ClosedHand` | Dragging an item. | | | `rbxasset://SystemCursors/IBeam` | Hovering in a text field. | | | `rbxasset://SystemCursors/SizeNS` | Hovering over a vertical resize handle. | | | `rbxasset://SystemCursors/SizeEW` | Hovering over a horizontal resize handle. | | | `rbxasset://SystemCursors/SizeNESW` | Hovering over a corner resize handle. | | | `rbxasset://SystemCursors/SizeNWSE` | Hovering over a corner resize handle. | | | `rbxasset://SystemCursors/SizeAll` | Hovering over a multi-direction resize handle. | | | `rbxasset://SystemCursors/SplitNS` | Hovering over a vertical "split" handle. | | | `rbxasset://SystemCursors/SplitEW` | Hovering over a horizontal "split" handle. | | | `rbxasset://SystemCursors/Forbidden` | Hovering over a locked/forbidden item. | | | `rbxasset://SystemCursors/Wait` | Indicating an action is in progress. | | | `rbxasset://SystemCursors/Busy` | Indicating the system is busy. | | | `rbxasset://SystemCursors/Cross` | Hovering over a pinpoint selection area. |
* These appearances are approximations — the actual look is dependent on your operating system.
**Dragon Mouse Icon** This example changes the [Players.LocalPlayer](/docs/reference/engine/classes/Players.md) mouse icon to look like a dragon image. ```lua local Players = game:GetService("Players") local mouse = Players.LocalPlayer:GetMouse() mouse.Icon = "http://www.roblox.com/asset?id=163023520" ``` ### Property: Mouse.IconContent ```json { "type": "Content", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Input" ] } ``` **Icon** is a property that determines the image used as the pointer. If blank, a default arrow is used. While the cursor hovers over a [GuiButton](/docs/reference/engine/classes/GuiButton.md), this property is temporarily ignored. This property only supports asset URIs. To hide the cursor entirely, **do not** use a transparent image – instead, set [UserInputService.MouseIconEnabled](/docs/reference/engine/classes/UserInputService.md) to false. For getting/setting the user mouse icon in experiences, you should use [UserInputService.MouseIcon](/docs/reference/engine/classes/UserInputService.md). [Mouse.Icon](/docs/reference/engine/classes/Mouse.md) will be deprecated after the new API for plugins to set the mouse cursor is released. ##### Designing a Cursor The following guidelines may prove useful when creating your own mouse cursors: - The dimensions of the image used determines the size of the cursor. - The dimensions of the image should not exceed 256 pixels on any axis. - The **center** of the image is where mouse inputs are issued. - The default mouse image is 64x64 pixels, with the mouse taking up 17x24 pixels of space. ##### System Cursors When using a [PluginMouse](/docs/reference/engine/classes/PluginMouse.md) retrieved from [Plugin:GetMouse()](/docs/reference/engine/classes/Plugin.md), you can use the following icons similar to your system's default cursors, such as hands, arrows, I-beams, etc. You can use these with GUI events like [MouseEnter](/docs/reference/engine/classes/GuiObject.md), [MouseLeave](/docs/reference/engine/classes/GuiObject.md), and [MouseButton1Down](/docs/reference/engine/classes/GuiButton.md) to provide a consistent Studio experience when interacting with certain kinds of GUI components. Note that these only work for Studio plugins; they will not work for other [Mouse](/docs/reference/engine/classes/Mouse.md) objects. | Look* | Asset | Suggested Use | | --- | --- | --- | | | `rbxasset://SystemCursors/Arrow` | Default clicking and selection. | | | `rbxasset://SystemCursors/PointingHand` | Hovering over an active link/button. | | | `rbxasset://SystemCursors/OpenHand` | Hovering over a draggable item. | | | `rbxasset://SystemCursors/ClosedHand` | Dragging an item. | | | `rbxasset://SystemCursors/IBeam` | Hovering in a text field. | | | `rbxasset://SystemCursors/SizeNS` | Hovering over a vertical resize handle. | | | `rbxasset://SystemCursors/SizeEW` | Hovering over a horizontal resize handle. | | | `rbxasset://SystemCursors/SizeNESW` | Hovering over a corner resize handle. | | | `rbxasset://SystemCursors/SizeNWSE` | Hovering over a corner resize handle. | | | `rbxasset://SystemCursors/SizeAll` | Hovering over a multi-direction resize handle. | | | `rbxasset://SystemCursors/SplitNS` | Hovering over a vertical "split" handle. | | | `rbxasset://SystemCursors/SplitEW` | Hovering over a horizontal "split" handle. | | | `rbxasset://SystemCursors/Forbidden` | Hovering over a locked/forbidden item. | | | `rbxasset://SystemCursors/Wait` | Indicating an action is in progress. | | | `rbxasset://SystemCursors/Busy` | Indicating the system is busy. | | | `rbxasset://SystemCursors/Cross` | Hovering over a pinpoint selection area. |
* These appearances are approximations — the actual look is dependent on your operating system.
**Dragon Mouse Icon** This example changes the [Players.LocalPlayer](/docs/reference/engine/classes/Players.md) mouse icon to look like a dragon image. ```lua local Players = game:GetService("Players") local mouse = Players.LocalPlayer:GetMouse() mouse.Icon = "http://www.roblox.com/asset?id=163023520" ``` ### Property: Mouse.Origin ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Input" ] } ``` The origin [Mouse](/docs/reference/engine/classes/Mouse.md) property is a [CFrame](/docs/reference/engine/datatypes/CFrame.md) indicating where the mouse originated from. It is positioned at the [Workspace.CurrentCamera](/docs/reference/engine/classes/Workspace.md) and oriented toward the mouse's 3D position. [Mouse.UnitRay](/docs/reference/engine/classes/Mouse.md) starts at the same position as Origin, and extends for a stud in the same direction. ```lua local unitRay = mouse.UnitRay local origin = mouse.Origin -- unitRay.Direction = origin.p -- unitRay.Direction ≈ origin.lookVector ``` For the position of the [Mouse](/docs/reference/engine/classes/Mouse.md) in 3D space, see [Mouse.Hit](/docs/reference/engine/classes/Mouse.md). **Mouse Origin vs Mouse Hit vs CurrentCamera Position** The code below visualizes the difference between [Mouse.Hit](/docs/reference/engine/classes/Mouse.md) and [Mouse.Origin](/docs/reference/engine/classes/Mouse.md). In order to do this, the code uses the [Vector3](/docs/reference/engine/datatypes/Vector3.md) positions of the hit and origin [CFrame](/docs/reference/engine/datatypes/CFrame.md) values using `.p`. The difference is that the origin is "where the mouse came from" (its origin) and the hit is the position where the mouse hits (is when the player presses their mouse). This example also visualizes that the mouse origin is very similar to the position of the `CurrentCamera` by printing the magnitude (distance) between the two positions. ```lua local Players = game:GetService("Players") local Workspace = game:GetService("Workspace") local player = Players.LocalPlayer local camera = Workspace.CurrentCamera local mouse = player:GetMouse() local camPos = camera.CFrame.Position local function onButton1Down() print("Mouse.Hit:", mouse.Hit.Position) print("camPos:", camPos) print("Mouse.Origin:", mouse.Origin.Position) print("Magnitude:", (mouse.Origin.Position - camPos).Magnitude) end mouse.Button1Down:Connect(onButton1Down) ``` ### Property: Mouse.Target ```json { "type": "BasePart", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Input" ] } ``` The object in 3D space the [mouse](/docs/reference/engine/classes/Mouse.md) is pointing to. Note: - If [Mouse.TargetFilter](/docs/reference/engine/classes/Mouse.md) has been set, the target filter and its descendants will be ignored. - When the mouse is not pointing at a [BasePart](/docs/reference/engine/classes/BasePart.md), for example when it is pointing at the sky, Target will be `nil`. - Developers looking for the position of the mouse in 3D space should use [Mouse.Hit](/docs/reference/engine/classes/Mouse.md). **Color Randomizer Tool** When placed in a `LocalScript` in `StarterPlayerScripts`, the following code changes the [BasePart.BrickColor](/docs/reference/engine/classes/BasePart.md) of every `BasePart` the player clicks on, such as the spawn location or baseplate. ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local mouse = player:GetMouse() mouse.Button1Down:Connect(function() local target = mouse.Target if target then print("Clicked:", target:GetFullName()) if target:IsA("BasePart") then target.BrickColor = BrickColor.random() end end end) ``` ### Property: Mouse.TargetFilter ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Input" ] } ``` This property determines an object to be ignored by the mouse when calculating [Mouse.Hit](/docs/reference/engine/classes/Mouse.md) and [Mouse.Target](/docs/reference/engine/classes/Mouse.md). The descendants of the object are also ignored, so it is possible to ignore multiple objects so long as they are a descendant of the object to which this property is set. This property is useful when filtering models containing special effects or decorations that should not affect [Mouse.Hit](/docs/reference/engine/classes/Mouse.md) or [Mouse.Target](/docs/reference/engine/classes/Mouse.md). This property can be set to any [Instance](/docs/reference/engine/classes/Instance.md) or `nil`, for example: ```lua local Players = game:GetService("Players") local Workspace = game:GetService("Workspace") local player = Players.LocalPlayer local mouse = player:GetMouse() mouse.TargetFilter = Workspace.Model ``` Note that the [Character](/docs/reference/engine/classes/Player.md) of the [Players.LocalPlayer](/docs/reference/engine/classes/Players.md) is ignored by the mouse automatically. ### Property: Mouse.TargetSurface ```json { "type": "NormalId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Input" ] } ``` This property indicates the [NormalId](/docs/reference/engine/enums/NormalId.md) of the [BasePart](/docs/reference/engine/classes/BasePart.md) surface at which the mouse is pointing. This property is derived from the world position of mouse ([Mouse.Hit](/docs/reference/engine/classes/Mouse.md)) and the part toward which the mouse is pointing ([Mouse.Target](/docs/reference/engine/classes/Mouse.md)). This property isn't meaningful when the mouse is not pointing at a part, for example when the mouse is pointing at the sky. At the moment, this property is set to 'Right' under these circumstances. Before using this property, check that the mouse's target is not `nil`. ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local mouse = player:GetMouse() -- Check that there exists a part at which the mouse is pointing if mouse.Target then print("The mouse is pointing to the " .. mouse.TargetSurface.Name .. " side of " .. mouse.Target.Name) else print("The mouse is not pointing at anything.") end ``` **Surface Randomizer** The code in this sample, when placed in a `LocalScript` inside `StarterPlayerScripts` will set the surface of any `BasePart` clicked on to a random surface. ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local mouse = player:GetMouse() local surfaceTypes = { Enum.SurfaceType.Smooth, Enum.SurfaceType.Glue, Enum.SurfaceType.Weld, Enum.SurfaceType.Studs, Enum.SurfaceType.Inlet, Enum.SurfaceType.Universal, Enum.SurfaceType.Hinge, Enum.SurfaceType.Motor, } local function onMouseClick() -- make sure the mouse is pointing at a part local target = mouse.Target if not target then return end local surfaceType = surfaceTypes[math.random(1, #surfaceTypes)] local surface = mouse.TargetSurface local propertyName = surface.Name .. "Surface" mouse.Target[propertyName] = surfaceType end mouse.Button1Down:Connect(onMouseClick) ``` ### Property: Mouse.UnitRay ```json { "type": "Ray", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Input" ] } ``` The UnitRay property is a [Ray](/docs/reference/engine/datatypes/Ray.md) directed toward the mouse's position in 3D space (described by [Mouse.Hit](/docs/reference/engine/classes/Mouse.md)). It originates from the [CFrame](/docs/reference/engine/classes/Camera.md) of the [Workspace.CurrentCamera](/docs/reference/engine/classes/Workspace.md). Like all unit rays, it has a distance of 1. ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local mouse = player:GetMouse() print(mouse.UnitRay.Direction.Magnitude) -- Always 1 ``` ### Property: Mouse.ViewSizeX ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Input" ] } ``` The ViewSizeX property describes the horizontal component of the game window's size in pixels. **Normalized Mouse Position** This code sample shows how you can create a [Vector2](/docs/reference/engine/datatypes/Vector2.md) representing the `Mouse` object's position on screen ([X()](/docs/reference/engine/classes/Mouse.md) and [Y()](/docs/reference/engine/classes/Mouse.md)) and the size of the screen itself ([ViewSizeX()](/docs/reference/engine/classes/Mouse.md) and [ViewSizeY()](/docs/reference/engine/classes/Mouse.md)). Using these, you can normalize the position of the mouse on-screen such that the top-left just under the topbar maps to (0, 0) and the bottom-right maps to (1, 1). This normalized position is calculated and printed as the mouse moves using the [Move()](/docs/reference/engine/classes/Mouse.md) event. ```lua local Players = game:GetService("Players") -- Note: You should use ContextActionService or UserInputService instead of -- the Mouse object for accomplishing this task. local player = Players.LocalPlayer local mouse = player:GetMouse() local function onMouseMove() -- Construct Vector2 objects for the mouse's position and screen size local position = Vector2.new(mouse.X, mouse.Y) local size = Vector2.new(mouse.ViewSizeX, mouse.ViewSizeY) -- A normalized position will map the top left (just under the topbar) -- to (0, 0) the bottom right to (1, 1), and the center to (0.5, 0.5). -- This is calculated by dividing the position by the total size. local normalizedPosition = position / size print(normalizedPosition) end mouse.Move:Connect(onMouseMove) ``` **Expected output:** When run, moving your mouse will print its normalized position on-screen. Move your mouse toward the top left and you'll notice it gets closer to (0, 0). Move to the bottom right will get closer to (1, 1). ### Property: Mouse.ViewSizeY ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Input" ] } ``` The ViewSizeY property describes the vertical component of the game window's size in pixels. This length includes the space used by the topbar. **Normalized Mouse Position** This code sample shows how you can create a [Vector2](/docs/reference/engine/datatypes/Vector2.md) representing the `Mouse` object's position on screen ([X()](/docs/reference/engine/classes/Mouse.md) and [Y()](/docs/reference/engine/classes/Mouse.md)) and the size of the screen itself ([ViewSizeX()](/docs/reference/engine/classes/Mouse.md) and [ViewSizeY()](/docs/reference/engine/classes/Mouse.md)). Using these, you can normalize the position of the mouse on-screen such that the top-left just under the topbar maps to (0, 0) and the bottom-right maps to (1, 1). This normalized position is calculated and printed as the mouse moves using the [Move()](/docs/reference/engine/classes/Mouse.md) event. ```lua local Players = game:GetService("Players") -- Note: You should use ContextActionService or UserInputService instead of -- the Mouse object for accomplishing this task. local player = Players.LocalPlayer local mouse = player:GetMouse() local function onMouseMove() -- Construct Vector2 objects for the mouse's position and screen size local position = Vector2.new(mouse.X, mouse.Y) local size = Vector2.new(mouse.ViewSizeX, mouse.ViewSizeY) -- A normalized position will map the top left (just under the topbar) -- to (0, 0) the bottom right to (1, 1), and the center to (0.5, 0.5). -- This is calculated by dividing the position by the total size. local normalizedPosition = position / size print(normalizedPosition) end mouse.Move:Connect(onMouseMove) ``` **Expected output:** When run, moving your mouse will print its normalized position on-screen. Move your mouse toward the top left and you'll notice it gets closer to (0, 0). Move to the bottom right will get closer to (1, 1). ### Property: Mouse.X ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Input" ] } ``` When detecting changes in the mouse's position on-screen, it is recommended that you use [ContextActionService:BindAction()](/docs/reference/engine/classes/ContextActionService.md) with [UserInputType.MouseMovement](/docs/reference/engine/enums/UserInputType.md) or [UserInputService.InputChanged](/docs/reference/engine/classes/UserInputService.md), which both describe the position of the mouse using the [Position](/docs/reference/engine/classes/InputObject.md) (a [Vector3](/docs/reference/engine/datatypes/Vector3.md)) of an [InputObject](/docs/reference/engine/classes/InputObject.md), instead of using this and related properties. The X property describes the horizontal component of the mouse's position on the screen. The position is measured in pixels relative to the top left corner, under the topbar. This property can be used in conjunction with [Mouse.Y](/docs/reference/engine/classes/Mouse.md) to produce a [Vector2](/docs/reference/engine/datatypes/Vector2.md) representing the mouse's position: ```lua local position = Vector2.new(mouse.X, mouse.Y) ``` This property does not fire [Changed](/docs/reference/engine/classes/Object.md) or the signal returned from [GetPropertyChangedSignal](/docs/reference/engine/classes/Object.md). Use the [Mouse.Move](/docs/reference/engine/classes/Mouse.md) event instead. **Normalized Mouse Position** This code sample shows how you can create a [Vector2](/docs/reference/engine/datatypes/Vector2.md) representing the `Mouse` object's position on screen ([X()](/docs/reference/engine/classes/Mouse.md) and [Y()](/docs/reference/engine/classes/Mouse.md)) and the size of the screen itself ([ViewSizeX()](/docs/reference/engine/classes/Mouse.md) and [ViewSizeY()](/docs/reference/engine/classes/Mouse.md)). Using these, you can normalize the position of the mouse on-screen such that the top-left just under the topbar maps to (0, 0) and the bottom-right maps to (1, 1). This normalized position is calculated and printed as the mouse moves using the [Move()](/docs/reference/engine/classes/Mouse.md) event. ```lua local Players = game:GetService("Players") -- Note: You should use ContextActionService or UserInputService instead of -- the Mouse object for accomplishing this task. local player = Players.LocalPlayer local mouse = player:GetMouse() local function onMouseMove() -- Construct Vector2 objects for the mouse's position and screen size local position = Vector2.new(mouse.X, mouse.Y) local size = Vector2.new(mouse.ViewSizeX, mouse.ViewSizeY) -- A normalized position will map the top left (just under the topbar) -- to (0, 0) the bottom right to (1, 1), and the center to (0.5, 0.5). -- This is calculated by dividing the position by the total size. local normalizedPosition = position / size print(normalizedPosition) end mouse.Move:Connect(onMouseMove) ``` **Expected output:** When run, moving your mouse will print its normalized position on-screen. Move your mouse toward the top left and you'll notice it gets closer to (0, 0). Move to the bottom right will get closer to (1, 1). ### Property: Mouse.Y ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Input" ] } ``` When detecting changes in the mouse's position on-screen, it is recommended that you use [ContextActionService:BindAction()](/docs/reference/engine/classes/ContextActionService.md) with [UserInputType.MouseMovement](/docs/reference/engine/enums/UserInputType.md) or [UserInputService.InputChanged](/docs/reference/engine/classes/UserInputService.md), which both describe the position of the mouse using the [Position](/docs/reference/engine/classes/InputObject.md) (a [Vector3](/docs/reference/engine/datatypes/Vector3.md)) of an [InputObject](/docs/reference/engine/classes/InputObject.md), instead of using this and related properties. The Y property describes the vertical component of the mouse's position on the screen. The position is measured in pixels relative to the top left corner, under the topbar. This property can be used in conjunction with [Mouse.X](/docs/reference/engine/classes/Mouse.md) to produce a [Vector2](/docs/reference/engine/datatypes/Vector2.md) representing the mouse's position: ```lua local position = Vector2.new(mouse.X, mouse.Y) ``` This property does not fire [Changed](/docs/reference/engine/classes/Object.md) or the signal returned from [GetPropertyChangedSignal](/docs/reference/engine/classes/Object.md). Use the [Mouse.Move](/docs/reference/engine/classes/Mouse.md) event instead. **Normalized Mouse Position** This code sample shows how you can create a [Vector2](/docs/reference/engine/datatypes/Vector2.md) representing the `Mouse` object's position on screen ([X()](/docs/reference/engine/classes/Mouse.md) and [Y()](/docs/reference/engine/classes/Mouse.md)) and the size of the screen itself ([ViewSizeX()](/docs/reference/engine/classes/Mouse.md) and [ViewSizeY()](/docs/reference/engine/classes/Mouse.md)). Using these, you can normalize the position of the mouse on-screen such that the top-left just under the topbar maps to (0, 0) and the bottom-right maps to (1, 1). This normalized position is calculated and printed as the mouse moves using the [Move()](/docs/reference/engine/classes/Mouse.md) event. ```lua local Players = game:GetService("Players") -- Note: You should use ContextActionService or UserInputService instead of -- the Mouse object for accomplishing this task. local player = Players.LocalPlayer local mouse = player:GetMouse() local function onMouseMove() -- Construct Vector2 objects for the mouse's position and screen size local position = Vector2.new(mouse.X, mouse.Y) local size = Vector2.new(mouse.ViewSizeX, mouse.ViewSizeY) -- A normalized position will map the top left (just under the topbar) -- to (0, 0) the bottom right to (1, 1), and the center to (0.5, 0.5). -- This is calculated by dividing the position by the total size. local normalizedPosition = position / size print(normalizedPosition) end mouse.Move:Connect(onMouseMove) ``` **Expected output:** When run, moving your mouse will print its normalized position on-screen. Move your mouse toward the top left and you'll notice it gets closer to (0, 0). Move to the bottom right will get closer to (1, 1). ### Property: Mouse.target ```json { "type": "BasePart", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Input" ] } ``` > **Deprecated:** This property is a deprecated variant of [Mouse.Target](/docs/reference/engine/classes/Mouse.md) which should be used instead. ### Property: Mouse.hit *(hidden)* ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Input" ] } ``` > **Deprecated:** This property is a deprecated variant of [Mouse.Hit](/docs/reference/engine/classes/Mouse.md) which should be used instead. ## Events ### Event: Mouse.Button1Down **Signature:** `Mouse.Button1Down()` This event fires when the player presses their left mouse button. Note that this can be accessed from a [Tool](/docs/reference/engine/classes/Tool.md); for example, when placed in a [LocalScript](/docs/reference/engine/classes/LocalScript.md), the code below prints `Button1Down` whenever the left mouse button is pressed. ```lua local tool = script.Parent -- Make sure this is a Tool object tool.Equipped:Connect(function(mouse) mouse.Button1Down:Connect(function() print("Button1Down") end) end) ``` You can find out the position of the mouse in world space and if it's pointing at any [BasePart](/docs/reference/engine/classes/BasePart.md) using the [Hit](/docs/reference/engine/classes/Mouse.md) and [Target](/docs/reference/engine/classes/Mouse.md) properties. *Security: None · Capabilities: Input* ### Event: Mouse.Button1Up **Signature:** `Mouse.Button1Up()` This event fires when the player releases their left mouse button. Note that this can be accessed from a [Tool](/docs/reference/engine/classes/Tool.md); for example, when placed in a [LocalScript](/docs/reference/engine/classes/LocalScript.md), the code below prints `Button1Up` whenever the left mouse button is released. ```lua local tool = script.Parent -- Make sure this is a Tool object tool.Equipped:Connect(function(mouse) mouse.Button1Up:Connect(function() print("Button1Up") end) end) ``` You can find out the position of the mouse in world space and if it's pointing at any [BasePart](/docs/reference/engine/classes/BasePart.md) using the [Hit](/docs/reference/engine/classes/Mouse.md) and [Target](/docs/reference/engine/classes/Mouse.md) properties. *Security: None · Capabilities: Input* ### Event: Mouse.Button2Down **Signature:** `Mouse.Button2Down()` This event fires when the player presses their right mouse button. Note that this can be accessed from a [Tool](/docs/reference/engine/classes/Tool.md); for example, when placed in a [LocalScript](/docs/reference/engine/classes/LocalScript.md), the code below prints `Button2Down` whenever the right mouse button is pressed. ```lua local tool = script.Parent -- Make sure this is a Tool object tool.Equipped:Connect(function(mouse) mouse.Button2Down:Connect(function() print("Button2Down") end) end) ``` You can find out the position of the mouse in world space and if it's pointing at any [BasePart](/docs/reference/engine/classes/BasePart.md) using the [Hit](/docs/reference/engine/classes/Mouse.md) and [Target](/docs/reference/engine/classes/Mouse.md) properties. *Security: None · Capabilities: Input* ### Event: Mouse.Button2Up **Signature:** `Mouse.Button2Up()` This event fires when the player releases their right mouse button. Note that this can be accessed from a [Tool](/docs/reference/engine/classes/Tool.md); for example, when placed in a [LocalScript](/docs/reference/engine/classes/LocalScript.md), the code below prints `Button2Up` whenever the right mouse button is released. ```lua local tool = script.Parent -- Make sure this is a Tool object tool.Equipped:Connect(function(mouse) mouse.Button2Up:Connect(function() print("Button2Up") end) end) ``` You can find out the position of the mouse in world space and if it's pointing at any [BasePart](/docs/reference/engine/classes/BasePart.md) using the [Hit](/docs/reference/engine/classes/Mouse.md) and [Target](/docs/reference/engine/classes/Mouse.md) properties. *Security: None · Capabilities: Input* ### Event: Mouse.Idle **Signature:** `Mouse.Idle()` Fired during every heartbeat that the mouse isn't being passed to another mouse event. Note, this event should not be used to determine when the mouse is still. As it fires every heartbeat it will fire between [Mouse.Move](/docs/reference/engine/classes/Mouse.md) events. For information on how to obtain the [Mouse](/docs/reference/engine/classes/Mouse.md) object, please see the [Mouse](/docs/reference/engine/classes/Mouse.md) page. Developers can find out the position of the mouse in world-space, and if it is pointing at any [BasePart](/docs/reference/engine/classes/BasePart.md) using the [Mouse.Hit](/docs/reference/engine/classes/Mouse.md) and [Mouse.Target](/docs/reference/engine/classes/Mouse.md) properties. Note, developers are recommended to use [UserInputService](/docs/reference/engine/classes/UserInputService.md) instead of the [Mouse](/docs/reference/engine/classes/Mouse.md) object in new work. *Security: None · Capabilities: Input* **Mouse.Idle** This example demonstrates how mouse events are passed during each frame ```lua local Players = game:GetService("Players") local RunService = game:GetService("RunService") local player = Players.LocalPlayer local mouse = player:GetMouse() local events = { "Button1Down", "Button1Up", "Button2Down", "Button2Up", "Idle", "Move", "WheelBackward", "WheelForward", "KeyDown", "KeyUp", } local currentEvent local frame = 0 local function processInput() frame = frame + 1 print("Frame", frame, "- mouse event was passed to", currentEvent) end for _, event in pairs(events) do mouse[event]:Connect(function() currentEvent = event end) end RunService:BindToRenderStep("ProcessInput", Enum.RenderPriority.Input.Value, processInput) ``` ### Event: Mouse.Move **Signature:** `Mouse.Move()` Fired when the mouse is moved. Note, this event is fired when the mouse's position is updated, therefore it will fire repeatedly while being moved. For information on how to obtain the [Mouse](/docs/reference/engine/classes/Mouse.md) object, please see the [Mouse](/docs/reference/engine/classes/Mouse.md) page. Developers can find out the position of the mouse in world-space, and if it is pointing at any [BasePart](/docs/reference/engine/classes/BasePart.md) using the [Mouse.Hit](/docs/reference/engine/classes/Mouse.md) and [Mouse.Target](/docs/reference/engine/classes/Mouse.md) properties. ``` mouse.Move:Connect(function() local position = mouse.Hit.p local target = mouse.Target print(target, position) end) ``` Note, developers are recommended to use [UserInputService](/docs/reference/engine/classes/UserInputService.md) instead of the [Mouse](/docs/reference/engine/classes/Mouse.md) object in new work. *Security: None · Capabilities: Input* **Move Parts with the Mouse** The example below allows the [local player](/docs/reference/engine/classes/Players.md) to move parts with their mouse. When the player presses their left mouse button over a part, that part is the [mouse's target](/docs/reference/engine/classes/Mouse.md) and becomes the _point_. Until the player releases their left mouse button, that part will move to the mouse's world position when the player moves their mouse. Note that the [Mouse.TargetFilter](/docs/reference/engine/classes/Mouse.md) property allows the code to ignore the part being moved when determining the mouse's world position. The code should work as expected when placed in a `LocalScript`. ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local mouse = player:GetMouse() local point local down local function selectPart() if mouse.Target and not mouse.Target.Locked then point = mouse.Target mouse.TargetFilter = point down = true end end local function movePart() if down and point then local posX, posY, posZ = mouse.Hit.X, mouse.Hit.Y, mouse.Hit.Z point.Position = Vector3.new(posX, posY, posZ) end end local function deselectPart() down = false point = nil mouse.TargetFilter = nil end mouse.Button1Down:Connect(selectPart) mouse.Button1Up:Connect(deselectPart) mouse.Move:Connect(movePart) ``` ### Event: Mouse.WheelBackward **Signature:** `Mouse.WheelBackward()` The WheelBackward event fires when the mouse wheel is scrolled backwards. Possible uses for this event include toggling a gun's scope in a first person shooter (FPS) or zooming the player's camera. This can be used alongside the scrolling forward event, [Mouse.WheelForward](/docs/reference/engine/classes/Mouse.md). For information on how to obtain the [Mouse](/docs/reference/engine/classes/Mouse.md) object, please see the [Mouse](/docs/reference/engine/classes/Mouse.md) page. Note, developers are recommended to use [UserInputService](/docs/reference/engine/classes/UserInputService.md) instead of the [Mouse](/docs/reference/engine/classes/Mouse.md) object in new work. *Security: None · Capabilities: Input* **Mouse.WheelBackward** The below example assumes that you have already got the player's mouse (and set it as a variable named 'mouse'), whether by use of a [Tool](/docs/reference/engine/classes/Tool.md), [HopperBin](/docs/reference/engine/classes/HopperBin.md) or the [Player:GetMouse()](/docs/reference/engine/classes/Player.md) method. It will print "Wheel went backwards!" when the player scrolls backwards. ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local mouse = player:GetMouse() local function onWheelBackward() print("Wheel went backwards!") end mouse.WheelBackward:Connect(onWheelBackward) ``` ### Event: Mouse.WheelForward **Signature:** `Mouse.WheelForward()` The WheelForward event fires when the mouse wheel is scrolled forwards. Possible uses for this event include toggling a gun's scope in a first person shooter (FPS) or zooming the player's camera. This can be used alongside the scrolling backward event, [Mouse.WheelBackward](/docs/reference/engine/classes/Mouse.md). For information on how to obtain the [Mouse](/docs/reference/engine/classes/Mouse.md) object, please see the [Mouse](/docs/reference/engine/classes/Mouse.md) page. Note, developers are recommended to use [UserInputService](/docs/reference/engine/classes/UserInputService.md) instead of the [Mouse](/docs/reference/engine/classes/Mouse.md) object in new work. *Security: None · Capabilities: Input* **Mouse.WheelForward** The below example assumes that you have already got the player's mouse (and set it as a variable named 'mouse'), whether by use of a `Tool`, `HopperBin` or the [Player:GetMouse()](/docs/reference/engine/classes/Player.md) method. It will print "Wheel went forward!" when the player scrolls forwards. ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local mouse = player:GetMouse() local function onWheelForward() print("Wheel went forward!") end mouse.WheelForward:Connect(onWheelForward) ``` ### Event: Mouse.KeyDown **Signature:** `Mouse.KeyDown(key: string)` > **Deprecated:** Mouse events have been superseded by [UserInputService](/docs/reference/engine/classes/UserInputService.md) which should be used in all new work. This event fires when a Key is pressed, with the passed argument being the key that was pressed. Note: - Not all keys generate this event. However, you can get around this with a few of the keys, "/" for example, by using the [Mouse.KeyUp](/docs/reference/engine/classes/Mouse.md) event. - Some keys generate the same string as other keys. - It's possible for the string to be empty (possibly due to "\0" key code). *Security: None · Capabilities: Input* **Parameters:** | Name | Type | Description | |------|------|-------------| | `key` | `string` | | **Mouse.KeyDown** The below example, assuming that 'mouse' was defined, would print the key that was pressed (e.g. "q") along with the ascii value for that key (e.g. 113). ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local mouse = player:GetMouse() local function onKeyDown(key) print("Key:", key, "Code:", string.byte(key)) end mouse.KeyDown:Connect(onKeyDown) ``` ### Event: Mouse.keyDown **Signature:** `Mouse.keyDown(key: string)` > **Deprecated:** This event is a deprecated variant of [Mouse.KeyDown](/docs/reference/engine/classes/Mouse.md) which has also been deprecated. Neither event should be used in new work. *Security: None · Capabilities: Input* **Parameters:** | Name | Type | Description | |------|------|-------------| | `key` | `string` | | ### Event: Mouse.KeyUp **Signature:** `Mouse.KeyUp(key: string)` > **Deprecated:** Mouse events have been superseded by [UserInputService](/docs/reference/engine/classes/UserInputService.md) which should be used in all new work. This event is fired when a Key is released, with the passed argument being the key that was released. Note: - Not all keys generate this event. However, you can get around this with a few of the keys, "/" for example, by using the [[API:Class/Mouse/KeyUp|KeyUp]] event. - Some keys generate the same string as other keys. - It's possible for the string to be empty (possibly due to "\0" key code). *Security: None · Capabilities: Input* **Parameters:** | Name | Type | Description | |------|------|-------------| | `key` | `string` | | **Mouse KeyUp** The below example, assuming that 'mouse' was defined, would print the key that was released (e.g. "q") along with the ascii value for that key (e.g. 113). ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local mouse = player:GetMouse() local function onKeyUp(key) print("Key:", key, "Code:", string.byte(key)) end mouse.KeyUp:Connect(onKeyUp) ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some
--- name: MouseService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated --- # Class: MouseService ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: MultipleDocumentInterfaceInstance last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated --- # Class: MultipleDocumentInterfaceInstance ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: NegateOperation last_updated: 2026-06-29T19:34:07Z inherits: - PartOperation - TriangleMeshPart - BasePart - PVInstance - Instance - Object type: class memory_category: Instances summary: "Result of a part that has been negated for use in solid modeling." --- # Class: NegateOperation > Result of a part that has been negated for use in solid modeling. ## Description A **NegateOperation** is the result of a part that has been negated through Studio's solid modeling **Negate** tool. A negated part turns pink and translucent as an indicator of its state. If the negated part is then unioned with a normal part using the **Union** tool, sections where the negated part overlaps the normal part will be cut out. Note that you can undo part negation by selecting the negated part and clicking **Negate** again. See [Solid Modeling](/docs/en-us/parts/solid-modeling.md) to learn more about Studio's solid modeling tools and methods. ## Inherited Members ### From [PartOperation](/docs/reference/engine/classes/PartOperation.md) - **Property `RenderFidelity`** (`RenderFidelity`): The level of detail used to render the solid modeled part. - **Property `SmoothingAngle`** (`float`): An angle in degrees which affects the smooth shading of a solid modeled - **Property `TriangleCount`** (`int`): The number of polygons in this solid model. - **Property `UsePartColor`** (`boolean`): Sets whether the PartOperation can be recolored using inherited - **Method `SubstituteGeometry(source: Instance): ()`**: Substitutes the geometry of this PartOperation with the geometry ### From [TriangleMeshPart](/docs/reference/engine/classes/TriangleMeshPart.md) - **Property `CollisionFidelity`** (`CollisionFidelity`): Determines the level of detail the part's physics will adhere to its mesh. - **Property `FluidFidelity`** (`FluidFidelity`): Determines the geometric representation used to compute aerodynamic forces - **Property `MeshSize`** (`Vector3`): ### From [BasePart](/docs/reference/engine/classes/BasePart.md) - **Property `Anchored`** (`boolean`): Determines whether a part is immovable by physics. - **Property `AssemblyAngularVelocity`** (`Vector3`): The angular velocity of the part's assembly. - **Property `AssemblyCenterOfMass`** (`Vector3`): The center of mass of the part's assembly in world space. - **Property `AssemblyLinearVelocity`** (`Vector3`): The linear velocity of the part's assembly. - **Property `AssemblyMass`** (`float`): The total mass of the part's assembly. - **Property `AssemblyRootPart`** (`BasePart`): A reference to the root part of the assembly. - **Property `AudioCanCollide`** (`boolean`): Determines whether the part will physically interact with audio - **Property `BackParamA`** (`float`): Determines the first parameter for the SurfaceType on the Back face of a *(deprecated, hidden)* - **Property `BackParamB`** (`float`): Determines the second parameter for the SurfaceType on the Back face of a *(deprecated, hidden)* - **Property `BackSurface`** (`SurfaceType`): Determines the type of surface for the back face of a part. - **Property `BackSurfaceInput`** (`InputType`): Determines the kind of input for the Back face of a part. *(deprecated, hidden)* - **Property `BottomParamA`** (`float`): Determines the first parameter for the SurfaceType on the Bottom face of a *(deprecated, hidden)* - **Property `BottomParamB`** (`float`): Determines the second parameter for the SurfaceType on the Bottom face of *(deprecated, hidden)* - **Property `BottomSurface`** (`SurfaceType`): Determines the type of surface for the bottom face of a part. - **Property `BottomSurfaceInput`** (`InputType`): Determines the kind of input for the Bottom face of a part. *(deprecated, hidden)* - **Property `BrickColor`** (`BrickColor`): Determines the color of a part. - **Property `brickColor`** (`BrickColor`): *(deprecated)* - **Property `CanCollide`** (`boolean`): Determines whether a part may collide with other parts. - **Property `CanQuery`** (`boolean`): Determines whether the part is considered during spatial query operations. - **Property `CanTouch`** (`boolean`): Determines if Touched and - **Property `CastShadow`** (`boolean`): Determines whether or not a part casts a shadow. - **Property `CenterOfMass`** (`Vector3`): Describes the world position in which a part's center of mass is located. - **Property `CFrame`** (`CFrame`): Determines the position and orientation of the BasePart in the - **Property `CollisionGroup`** (`string`): Describes the name of a part's collision group. - **Property `CollisionGroupId`** (`int`): Describes the automatically set ID number of a part's collision group. *(deprecated)* - **Property `Color`** (`Color3`): Determines the color of a part. - **Property `CurrentPhysicalProperties`** (`PhysicalProperties`): Indicates the current physical properties of the part. - **Property `CustomPhysicalProperties`** (`PhysicalProperties`): Determines several physical properties of a part. - **Property `Elasticity`** (`float`): Used to control the Elasticity of the part, but it no longer does *(deprecated, hidden)* - **Property `EnableFluidForces`** (`boolean`): Used to enable or disable aerodynamic forces on parts and assemblies. - **Property `ExtentsCFrame`** (`CFrame`): The CFrame of the physical extents of the BasePart. - **Property `ExtentsSize`** (`Vector3`): The actual physical size of the BasePart as regarded by the - **Property `Friction`** (`float`): Used to control the Friction of the part, but now it no longer does *(deprecated, hidden)* - **Property `FrontParamA`** (`float`): Determines the first parameter for the SurfaceType on the Front face of a *(deprecated, hidden)* - **Property `FrontParamB`** (`float`): Determines the second parameter for the SurfaceType on the Front face of a *(deprecated, hidden)* - **Property `FrontSurface`** (`SurfaceType`): Determines the type of surface for the front face of a part. - **Property `FrontSurfaceInput`** (`InputType`): Determines the kind of input for the Front face of a part (-Z direction). *(deprecated, hidden)* - **Property `LeftParamA`** (`float`): Determines the first parameter for the SurfaceType on the Left face of a *(deprecated, hidden)* - **Property `LeftParamB`** (`float`): Determines the second parameter for the SurfaceType on the Left face of a *(deprecated, hidden)* - **Property `LeftSurface`** (`SurfaceType`): Determines the type of surface for the left face of a part. - **Property `LeftSurfaceInput`** (`InputType`): Determines the kind of input for the Left face of a part. *(deprecated, hidden)* - **Property `LocalTransparencyModifier`** (`float`): Determines a multiplier for BasePart.Transparency that is only *(hidden)* - **Property `Locked`** (`boolean`): Determines whether a part is selectable in Studio. - **Property `Mass`** (`float`): Describes the mass of the part, the product of its density and volume. - **Property `Massless`** (`boolean`): Determines whether the part contributes to the total mass or inertia of - **Property `Material`** (`Material`): Determines the texture and default physical properties of a part. - **Property `MaterialVariant`** (`string`): The name of MaterialVariant. - **Property `Orientation`** (`Vector3`): Describes the rotation of the part in the world. *(hidden)* - **Property `PivotOffset`** (`CFrame`): Specifies the offset of the part's pivot from its CFrame. - **Property `Position`** (`Vector3`): Describes the position of the part in the world. *(hidden)* - **Property `ReceiveAge`** (`float`): Time since last recorded physics update. *(hidden)* - **Property `Reflectance`** (`float`): Determines how much a part reflects the skybox. - **Property `ResizeableFaces`** (`Faces`): Describes the faces on which a part may be resized. - **Property `ResizeIncrement`** (`int`): Describes the smallest change in size allowable by the - **Property `RightParamA`** (`float`): Determines the first parameter for the SurfaceType on the Right face of a *(deprecated, hidden)* - **Property `RightParamB`** (`float`): Determines the second parameter for the SurfaceType on the Right face of a *(deprecated, hidden)* - **Property `RightSurface`** (`SurfaceType`): Determines the type of surface for the right face of a part. - **Property `RightSurfaceInput`** (`InputType`): Determines the kind of input for the Right face of a part (-X direction). *(deprecated, hidden)* - **Property `RootPriority`** (`int`): The main rule in determining the root part of an assembly. - **Property `Rotation`** (`Vector3`): The rotation of the part in degrees for the three axes. - **Property `RotVelocity`** (`Vector3`): Determines a part's change in orientation over time. *(deprecated, hidden)* - **Property `Size`** (`Vector3`): Determines the dimensions of a part (length, width, height). - **Property `SpecificGravity`** (`float`): The ratio of the part's density to the density of water determined by the *(deprecated)* - **Property `TopParamA`** (`float`): Determines the first parameter for the SurfaceType on the Top face of a *(deprecated, hidden)* - **Property `TopParamB`** (`float`): Determines the second parameter for the SurfaceType on the Top face of a *(deprecated, hidden)* - **Property `TopSurface`** (`SurfaceType`): Determines the type of surface for the top face of a part. - **Property `TopSurfaceInput`** (`InputType`): Determines the kind of input for the Top face of a part (+Y direction). *(deprecated, hidden)* - **Property `Transparency`** (`float`): Determines how much a part can be seen through (the inverse of part - **Property `Velocity`** (`Vector3`): Determines a part's change in position over time. *(deprecated, hidden)* - **Method `AngularAccelerationToTorque(angAcceleration: Vector3, angVelocity?: Vector3): Vector3`**: Returns the torque needed to achieve a given angular acceleration on this - **Method `ApplyAngularImpulse(impulse: Vector3): ()`**: Apply an angular impulse to the assembly. - **Method `ApplyImpulse(impulse: Vector3): ()`**: Apply an impulse to the assembly at the assembly's - **Method `ApplyImpulseAtPosition(impulse: Vector3, position: Vector3): ()`**: Apply an impulse to the assembly at specified position. - **Method `BreakJoints(): ()`**: Breaks any surface connection with any adjacent part, including *(deprecated)* - **Method `breakJoints(): ()`**: *(deprecated)* - **Method `CanCollideWith(part: BasePart): boolean`**: Returns whether the parts can collide with each other. - **Method `CanSetNetworkOwnership(): Tuple`**: Checks whether you can set a part's network ownership. - **Method `GetClosestPointOnSurface(position: Vector3): Vector3`**: Returns the closest point on the part's surface to the given point. - **Method `GetConnectedParts(recursive?: boolean): List`**: Returns a table of parts connected to the object by any kind of rigid - **Method `GetJoints(): Instances`**: Return all Joints or Constraints that is connected to this Part. - **Method `GetMass(): float`**: Returns the value of the Mass property. - **Method `getMass(): float`**: *(deprecated)* - **Method `GetNetworkOwner(): Instance`**: Returns the current player who is the network owner of this part, or `nil` - **Method `GetNetworkOwnershipAuto(): boolean`**: Returns true if the game engine automatically decides the network owner - **Method `GetNoCollisionConstraints(): Instances`**: - **Method `GetRenderCFrame(): CFrame`**: OBSOLETE. Returns a CFrame describing where the part is being rendered at. *(deprecated)* - **Method `GetRootPart(): Instance`**: Returns the base part of an assembly of parts. *(deprecated)* - **Method `GetTouchingParts(): Instances`**: Returns a table of all BasePart.CanCollide true parts that - **Method `GetVelocityAtPosition(position: Vector3): Vector3`**: Returns the linear velocity of the part's assembly at the given position - **Method `IntersectAsync(parts: Instances, collisionfidelity?: CollisionFidelity, renderFidelity?: RenderFidelity): Instance`**: Note: It is highly recommended to use the newer - **Method `IsGrounded(): boolean`**: Returns true if the object is connected to a part that will hold it in - **Method `MakeJoints(): ()`**: Creates a joint on any side of the object that has a surface ID that can *(deprecated)* - **Method `makeJoints(): ()`**: *(deprecated)* - **Method `Resize(normalId: NormalId, deltaAmount: int): boolean`**: Changes the size of an object just like using the Studio resize tool. - **Method `resize(normalId: NormalId, deltaAmount: int): boolean`**: *(deprecated)* - **Method `SetNetworkOwner(playerInstance?: Player): ()`**: Sets the given player as network owner for this and all connected parts. - **Method `SetNetworkOwnershipAuto(): ()`**: Lets the game engine dynamically decide who will handle the part's physics - **Method `SubtractAsync(parts: Instances, collisionfidelity?: CollisionFidelity, renderFidelity?: RenderFidelity): Instance`**: Note: It is highly recommended to use the newer - **Method `TorqueToAngularAcceleration(torque: Vector3, angVelocity?: Vector3): Vector3`**: Returns the angular acceleration that would result from applying a given - **Method `UnionAsync(parts: Instances, collisionfidelity?: CollisionFidelity, renderFidelity?: RenderFidelity): Instance`**: Note: It is highly recommended to use the newer - **Event `LocalSimulationTouched`**: *(deprecated)* - **Event `OutfitChanged`**: *(deprecated)* - **Event `StoppedTouching`**: *(deprecated)* - **Event `Touched`**: Fires when a part touches another part as a result of physical movement. - **Event `TouchEnded`**: Fires when a part stops touching another part as a result of physical ### From [PVInstance](/docs/reference/engine/classes/PVInstance.md) - **Property `Origin`** (`CFrame`): - **Property `Pivot Offset`** (`CFrame`): - **Method `GetPivot(): CFrame`**: Gets the pivot of a PVInstance. - **Method `PivotTo(targetCFrame: CFrame): ()`**: Transforms the PVInstance along with all of its descendant ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: NetworkClient last_updated: 2026-06-29T19:34:07Z inherits: - NetworkPeer - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated --- # Class: NetworkClient ## Description This service is responsible for connecting a client to a server. ## Events ### Event: NetworkClient.ConnectionAccepted **Signature:** `NetworkClient.ConnectionAccepted(peer: string, replicator: Instance)` Fired when the client successfully connects to a server. Returns a string showing the server's IP and Port, and the client's [ClientReplicator](/docs/reference/engine/classes/ClientReplicator.md). *Security: None · Capabilities: Network* **Parameters:** | Name | Type | Description | |------|------|-------------| | `peer` | `string` | The server's IP and port. | | `replicator` | `Instance` | The client's [ClientReplicator](/docs/reference/engine/classes/ClientReplicator.md). | ### Event: NetworkClient.ConnectionFailed **Signature:** `NetworkClient.ConnectionFailed(peer: string, code: int)` Fired if the client fails to connect to the server. *Security: None · Capabilities: Network* **Parameters:** | Name | Type | Description | |------|------|-------------| | `peer` | `string` | The server's IP and port. | | `code` | `int` | The error code representing why the client failed to connect. | ## Inherited Members ### From [NetworkPeer](/docs/reference/engine/classes/NetworkPeer.md) - **Method `SetOutgoingKBPSLimit(limit: int): ()`**: Sets the maximum outgoing bandwidth that Roblox can use. ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: NetworkMarker last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotBrowsable summary: "The NetworkMarker is used to tell the client when the server has finished loading the world for the client." --- # Class: NetworkMarker > The NetworkMarker is used to tell the client when the server has finished > loading the world for the client. ## Events ### Event: NetworkMarker.Received **Signature:** `NetworkMarker.Received()` Fired when the server has finished replicating the world to the client. *Security: None · Capabilities: Network* ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: NetworkPeer last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotBrowsable summary: "The `NetworkPeer` object is the most basic class of the network objects." --- # Class: NetworkPeer > The `NetworkPeer` object is the most basic class of the network objects. ## Methods ### Method: NetworkPeer:SetOutgoingKBPSLimit **Signature:** `NetworkPeer:SetOutgoingKBPSLimit(limit: int): ()` Sets the maximum outgoing bandwidth (in 1,000 bits/second) that Roblox can use. *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `limit` | `int` | | | **Returns:** `()` **NetworkPeer:SetOutgoingKBPSLimit** The following code, when ran from the command bar or a plugin, would limit bandwidth to 1KBPS. ```lua local NetworkClient = game:GetService("NetworkClient") NetworkClient:SetOutgoingKBPSLimit(1) ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: NetworkReplicator last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated summary: "An object which handles the replication of other objects either from the server to the client, or from the client to the server." --- # Class: NetworkReplicator > An object which handles the replication of other objects either from the > server to the client, or from the client to the server. ## Methods ### Method: NetworkReplicator:GetPlayer **Signature:** `NetworkReplicator:GetPlayer(): Instance` Returns the player that is connected to the NetworkReplicator. *Security: None · Thread Safety: Unsafe · Capabilities: Network, Players* **Returns:** `Instance` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: NetworkServer last_updated: 2026-06-29T19:34:07Z inherits: - NetworkPeer - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated --- # Class: NetworkServer ## Description The [NetworkServer](/docs/reference/engine/classes/NetworkServer.md) stores all the [NetworkReplicator](/docs/reference/engine/classes/NetworkReplicator.md) in the game and handles all connections. [NetworkPeer:SetOutgoingKBPSLimit()](/docs/reference/engine/classes/NetworkPeer.md) can be used to imitate latency while using Start Server. ## Methods ### Method: NetworkServer:EncryptStringForPlayerId **Signature:** `NetworkServer:EncryptStringForPlayerId(toEncrypt: string, playerId: int64): string` *Security: None · Thread Safety: Unsafe · Capabilities: Network* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `toEncrypt` | `string` | | | | `playerId` | `int64` | | | **Returns:** `string` ## Inherited Members ### From [NetworkPeer](/docs/reference/engine/classes/NetworkPeer.md) - **Method `SetOutgoingKBPSLimit(limit: int): ()`**: Sets the maximum outgoing bandwidth that Roblox can use. ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: NetworkSettings last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated - NotBrowsable summary: "Settings related to networked engine behaviors." --- # Class: NetworkSettings > Settings related to networked engine behaviors. ## Description `NetworkSettings` is a class that allows you to debug several features with Roblox's server/client networking. It can be found in Roblox Studio's settings under the **Network** tab. ## Properties ### Property: NetworkSettings.HttpProxyEnabled ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "None", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Proxy Settings", "capabilities": [ "Network" ] } ``` ### Property: NetworkSettings.HttpProxyURL ```json { "type": "string", "access": "ReadOnly", "security": { "read": "None", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Proxy Settings", "capabilities": [ "Network" ] } ``` ### Property: NetworkSettings.InboundNetworkJitterMs ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Network" ] } ``` On [playtest](/docs/en-us/studio/testing-modes.md#playtesting) connections, this property adds variability (jitter) in milliseconds to the latency added to packets sent from server to client. This variable latency is in addition to any latency introduced by [InboundNetworkMinDelayMs](/docs/reference/engine/classes/NetworkSettings.md). Per-packet delay sampled from delay and jitter is rounded to 1 millisecond. See [network simulation](/docs/en-us/studio/testing-modes.md#network-simulation) for additional details. ### Property: NetworkSettings.InboundNetworkLossPercent ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Network" ] } ``` On [playtest](/docs/en-us/studio/testing-modes.md#playtesting) connections, this property sets a percent probability that packets sent from server to client are dropped. To maintain a stable test environment, the value is capped at 0.5%. See [network simulation](/docs/en-us/studio/testing-modes.md#network-simulation) for additional details. ### Property: NetworkSettings.InboundNetworkMinDelayMs ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Network" ] } ``` On [playtest](/docs/en-us/studio/testing-modes.md#playtesting) connections, this property adds a minimum latency in milliseconds for packets sent from server to client. Per-packet delay sampled from delay and jitter is rounded to 1 millisecond. See [network simulation](/docs/en-us/studio/testing-modes.md#network-simulation) for additional details. ### Property: NetworkSettings.IncomingReplicationLag ```json { "type": "double", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Diagnostics", "capabilities": [ "Network" ] } ``` This property instructs the engine to simulate additional replication lag by delaying all incoming messages. Units are seconds. ### Property: NetworkSettings.OutboundNetworkJitterMs ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Network" ] } ``` On [playtest](/docs/en-us/studio/testing-modes.md#playtesting) connections, this property adds variability (jitter) in milliseconds to the latency added to packets sent from client to server. This variable latency is in addition to any latency introduced by [OutboundNetworkMinDelayMs](/docs/reference/engine/classes/NetworkSettings.md). Per-packet delay sampled from delay and jitter is rounded to 1 millisecond. See [network simulation](/docs/en-us/studio/testing-modes.md#network-simulation) for additional details. ### Property: NetworkSettings.OutboundNetworkLossPercent ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Network" ] } ``` On [playtest](/docs/en-us/studio/testing-modes.md#playtesting) connections, this property sets a percent probability that packets sent from client to server are dropped. To maintain a stable test environment, the value is capped at 0.5%. See [network simulation](/docs/en-us/studio/testing-modes.md#network-simulation) for additional details. ### Property: NetworkSettings.OutboundNetworkMinDelayMs ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Network" ] } ``` On [playtest](/docs/en-us/studio/testing-modes.md#playtesting) connections, this property adds a minimum latency in milliseconds for packets sent from client to server. Per-packet delay sampled from delay and jitter is rounded to 1 millisecond. See [network simulation](/docs/en-us/studio/testing-modes.md#network-simulation) for additional details. ### Property: NetworkSettings.PrintJoinSizeBreakdown ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Diagnostics", "capabilities": [ "Network" ] } ``` Prints diagnostic information to the **Output** window after connecting. The data will indicate the largest individual [Instances](/docs/reference/engine/classes/Instance.md) sent, as well as aggregate data about data sent by [Instance](/docs/reference/engine/classes/Instance.md) type. The data sent for initial loading is compressed so the contributions are approximate. ### Property: NetworkSettings.PrintPhysicsErrors ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Diagnostics", "capabilities": [ "Network" ] } ``` When set to `true`, debug messages will be printed to the **Output** window pertaining to physics replication errors. The following are debug outputs that are made available: - `Physics-in old packet` prints for a part that has been updated ahead of the packet's submission time. This happens if the packet is received late, and a newer packet has already been processed. - `Physics-in of unidentified {GUID}` prints for an unfound part that is trying to be updated because the provided [Instance](/docs/reference/engine/classes/Instance.md) identifier was invalid, where `{GUID}` is the unknown [Instance:GetDebugId()](/docs/reference/engine/classes/Instance.md) identifier that is supposed to be targeting the part. This typically happens if a part is removed before the physics update packet is received. - `Physics-in of part not in workspace {GUID}` prints for a request to update the physics of a part that is not a descendant of the [Workspace](/docs/reference/engine/classes/Workspace.md), where `{GUID}` is the [Instance:GetDebugId()](/docs/reference/engine/classes/Instance.md) identifier of the target part. This happens if the part was just moved out of the [Workspace](/docs/reference/engine/classes/Workspace.md), and was previously being simulated. ### Property: NetworkSettings.PrintStreamInstanceQuota ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Diagnostics", "capabilities": [ "Network" ] } ``` When set to `true`, debug information is printed to the **Output** window regarding the replication of instances when [Workspace.StreamingEnabled](/docs/reference/engine/classes/Workspace.md) is set to `true`. Note that this documentation may become outdated in the future, as Roblox's network code is always changing behind the scenes. #### Streaming Capacity Update When the client's streaming capacity is updated, the following debug message will be printed: `clientInstanceQuota {1}, packet in queue {2}, predictedTotalInstanceProcessTime {3}, avgStreamDataReadTime {4}, avgInstancesPerStreamData {5}` - `{1}` — The ID of the client instance quota. - `{2}` — The current number of incoming packets that have been queued. - `{3}` — A prediction for how long it will take to update the quota. - `{4}` — The current average time it takes to read the stream data. - `{5}` — The average number of instances in the stream data. #### Instance Quota Update When the client receives an instance quota update, the following debug message will be printed: `Received new client instance quota: {1}, max region radius: {2}` - `{1}` — The ID of the client instance quota. - `{2}` — The maximum radius of space around the client's [Player.ReplicationFocus](/docs/reference/engine/classes/Player.md) that can have physical instances streamed in. ### Property: NetworkSettings.RandomizeJoinInstanceOrder ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Advanced", "capabilities": [ "Network" ] } ``` Emulates the behavior of a server that has been online a long time by randomizing the order that instances initially arrive on clients. It is recommended to keep this setting enabled to help discover potential bugs while testing in Studio. ### Property: NetworkSettings.RenderStreamedRegions ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Network" ] } ``` When set to `true`, regions of space that are being streamed to the client will be outlined in red. This will only be shown if [Workspace.StreamingEnabled](/docs/reference/engine/classes/Workspace.md) is set to `true`. ### Property: NetworkSettings.ShowActiveAnimationAsset ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Network", "Animation" ] } ``` When set to `true`, a label will be shown above each player character's head, showing the current animation (if any) being played by the character's [Humanoid](/docs/reference/engine/classes/Humanoid.md). ### Property: NetworkSettings.EmulatedTotalMemoryInMB *(hidden)* ```json { "type": "int", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Network" ] } ``` ### Property: NetworkSettings.FreeMemoryMBytes *(hidden)* ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Network" ] } ``` `FreeMemoryMBytes` is a read-only property that describes how much free memory is available, in MiBs. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: NoCollisionConstraint last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: BaseParts summary: "An instance used to prevent collisions between two specific parts." --- # Class: NoCollisionConstraint > An instance used to prevent collisions between two specific parts. ## Description The **NoCollisionConstraint** prevents collisions between two specific parts, but those parts may still register collisions with the rest of the world. Compared to [collision groups](/docs/en-us/workspace/collisions.md#collision-filtering), it provides a direct way to disable specific collisions, such as the wheel of a car scraping against the car's body. The most common way to create this constraint is by selecting **No Collision** through Studio's **Create** menu in the toolbar's **Model** tab. Unlike most constraints, [NoCollisionConstraint](/docs/reference/engine/classes/NoCollisionConstraint.md) does not utilize any [Attachments](/docs/reference/engine/classes/Attachment.md). Note that the tool acts differently based on how many parts are selected when the tool is activated: | Option | Tool Behavior | | --- | --- | | No parts selected | The next two parts that are clicked will be linked together. If the same part is clicked twice, no link will be created. | | One part selected | The next part that is clicked will be linked to the selected part. | | Two parts selected | Both selected parts will be linked together. | ## Properties ### Property: NoCollisionConstraint.Enabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Physics" ], "simulationAccess": true } ``` This property determines whether the two linked parts, [Part0](/docs/reference/engine/classes/NoCollisionConstraint.md) and [Part1](/docs/reference/engine/classes/NoCollisionConstraint.md), will collide with each other. ### Property: NoCollisionConstraint.Part0 ```json { "type": "BasePart", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Parts", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The second [BasePart](/docs/reference/engine/classes/BasePart.md) that the constraint connects. ### Property: NoCollisionConstraint.Part1 ```json { "type": "BasePart", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Parts", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The first [BasePart](/docs/reference/engine/classes/BasePart.md) that the constraint connects. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: NotificationService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service summary: "An internal service which cannot be used by developers." --- # Class: NotificationService > An internal service which cannot be used by developers. ## Description An unfinished service that would allow you to schedule notifications. Currently goes unimplemented, and cannot be enabled. ## Properties ### Property: NotificationService.IsLuaGameDetailsEnabled ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Players" ] } ``` ### Property: NotificationService.SelectedTheme ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Players" ] } ``` ## Events ### Event: NotificationService.Roblox17sConnectionChanged **Signature:** `NotificationService.Roblox17sConnectionChanged(connectionName: string, connectionState: ConnectionState, namespaceSequenceNumbers: string)` *Security: None · Capabilities: Players* **Parameters:** | Name | Type | Description | |------|------|-------------| | `connectionName` | `string` | | | `connectionState` | `ConnectionState` | | | `namespaceSequenceNumbers` | `string` | | ### Event: NotificationService.Roblox17sEventReceived **Signature:** `NotificationService.Roblox17sEventReceived(eventData: Map)` *Security: None · Capabilities: Players* **Parameters:** | Name | Type | Description | |------|------|-------------| | `eventData` | `Map` | | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: NumberPose last_updated: 2026-06-29T19:34:07Z inherits: - PoseBase - Instance - Object type: class memory_category: Animation summary: "Holds the value applied to a specific FACS control." --- # Class: NumberPose > Holds the value applied to a specific FACS control. ## Description A **NumberPose** holds the value applied to a specific FACS control. The control which is affected depends on its name. **NumberPose** instances are the building blocks of facial animation and, with [Keyframes](/docs/reference/engine/classes/Keyframe.md), make up [KeyframeSequences](/docs/reference/engine/classes/KeyframeSequence.md). #### NumberPoses and Facial Animation Although a **NumberPose** is assigned to a single FACS control by name, that control can in turn affect multiple bones of the face rig. FACS controls act as an abstraction layer between facial muscle movements and how they are defined in the rig. #### NumberPose Hierarchy Contrary to [Pose](/docs/reference/engine/classes/Pose.md) Instances, **NumberPose** instances cannot be parented together. However, they all must be stored in a [Folder](/docs/reference/engine/classes/Folder.md) named **FaceControls** that is a child of the **Head** [Pose](/docs/reference/engine/classes/Pose.md). #### NumberPose Value The Roblox animation system applies **NumberPose** values to the corresponding FACS controls. Because those controls only respond to values between 0 and 1, the values calculated by the animation system are always clamped to that range. ## Properties ### Property: NumberPose.Value ```json { "type": "double", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Animation" ] } ``` The value that will be applied to the FACS control corresponding with the [NumberPose](/docs/reference/engine/classes/NumberPose.md). ## Inherited Members ### From [PoseBase](/docs/reference/engine/classes/PoseBase.md) - **Property `EasingDirection`** (`PoseEasingDirection`): The easing direction to use to reach the next Pose's value. - **Property `EasingStyle`** (`PoseEasingStyle`): The easing style to use to reach the next Pose's value. - **Property `Weight`** (`float`): ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: NumberValue last_updated: 2026-06-29T19:34:07Z inherits: - ValueBase - Instance - Object type: class memory_category: Instances summary: "A container object for a single double-precision floating point number." --- # Class: NumberValue > A container object for a single double-precision floating point number. ## Description Store a single [Luau number](https://luau.org/syntax#number-literals), defined to be [double-precision floating point number](https://en.wikipedia.org/wiki/Double-precision_floating-point_format), or more commonly known as a **double**. This stores a number in 64 bits (8 bytes) using the IEEE 754 representation (1 sign bit, 11 exponent bits and 52 fractional bits). Maximum and minimum values are ±1.7976931348623157e+308, with a range of ±2^53 for exact integer representations. Like all [ValueBase](/docs/reference/engine/classes/ValueBase.md) objects, this single value is stored in the [Value](/docs/reference/engine/classes/NumberValue.md) property. The `Changed` event fires with the new value being stored in the object, instead of a string representing the property being changed. ## Code Samples **Changed Event** This sample demonstrates the subtleties of the `Changed` event on normal objects and `Value` objects. ```lua -- Demonstrate the Changed event by creating a Part local part = Instance.new("Part") part.Changed:Connect(print) -- This fires Changed with "Transparency" part.Transparency = 0.5 -- Similarly, this fires Changed with "Number" part.Name = "SomePart" -- Since changing BrickColor will also change other -- properties at the same time, this line fires Changed -- with "BrickColor", "Color3" and "Color3uint16". part.BrickColor = BrickColor.Red() -- A NumberValue holds a double-precision floating-point number local vNumber = Instance.new("NumberValue") vNumber.Changed:Connect(print) -- This fires Changed with 123.456 (not "Value") vNumber.Value = 123.456 -- This does not fire Changed vNumber.Name = "SomeNumber" -- A StringValue stores one string local vString = Instance.new("StringValue") vString.Changed:Connect(print) -- This fires Changed with "Hello" (not "Value") vString.Value = "Hello" ``` ## Properties ### Property: NumberValue.Value ```json { "type": "double", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "simulationAccess": true } ``` Used to hold a double value. ## Events ### Event: NumberValue.Changed **Signature:** `NumberValue.Changed(value: double)` Fires whenever the [NumberValue.Value](/docs/reference/engine/classes/NumberValue.md) changes. It runs with the new value being stored in the argument object, instead of a string representing the property being changed. Listening for the `Changed` signal can be useful in games that use `NumberValues` to track values like leaderboard statistics or item prices. *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `value` | `double` | The new value after the change. | **NumberValue Changed** This example prints the new value of the `NumberValue` each time it changes. Here it prints 20. ```lua local numberValue = script.Parent.NumberValue local function printValue(value) print(value) end numberValue.Changed:Connect(printValue) numberValue.Value = 20 ``` ### Event: NumberValue.changed **Signature:** `NumberValue.changed(value: double)` > **Deprecated:** This event is a deprecated variant of [NumberValue.Changed](/docs/reference/engine/classes/NumberValue.md) which should be used instead. *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `value` | `double` | | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Object last_updated: 2026-06-29T19:34:07Z type: class memory_category: Instances tags: - NotCreatable - NotReplicated summary: "`Object` is the base class for all classes in the Roblox class hierarchy." --- # Class: Object > `Object` is the base class for all classes in the Roblox class hierarchy. ## Description `Object` is the base class for all classes in the Roblox class hierarchy. Every other class that the Roblox Engine defines inherits all of the members of `Object`. It is not possible to directly create an `Object`. ## Properties ### Property: Object.ClassName ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` A read-only string representing the class this [Object](/docs/reference/engine/classes/Object.md) belongs to. This property can be used with various other functions that are used to identify objects by type, such as [Object:IsA()](/docs/reference/engine/classes/Object.md) or [Instance:FindFirstChildOfClass()](/docs/reference/engine/classes/Instance.md). Note this property is read only and cannot be altered by scripts. Developers wishing to change an object's class will instead have to create a new [Object](/docs/reference/engine/classes/Object.md). Unlike [Object:IsA()](/docs/reference/engine/classes/Object.md), ClassName can be used to check if an object belongs to a specific class ignoring class inheritance. For example: ``` for _, child in workspace:GetChildren() do if child.ClassName == "Part" then print("Found a Part") -- will find Parts in model, but NOT TrussParts, WedgeParts, etc end end ``` ### Property: Object.className ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data" } ``` > **Deprecated:** This deprecated property is a variant of [Object.ClassName](/docs/reference/engine/classes/Object.md) which should be used instead. ## Methods ### Method: Object:GetPropertyChangedSignal **Signature:** `Object:GetPropertyChangedSignal(property: string): RBXScriptSignal` This method returns an event that behaves exactly like the [Changed](/docs/reference/engine/classes/Object.md) event, except that it only fires when the given property changes. It's generally a good idea to use this method instead of a connection to [Changed](/docs/reference/engine/classes/Object.md) with a function that checks the property name. Subsequent calls to this method on the same object with the same property name return the same event. [ValueBase](/docs/reference/engine/classes/ValueBase.md) objects, such as [IntValue](/docs/reference/engine/classes/IntValue.md) and [StringValue](/docs/reference/engine/classes/StringValue.md), use a modified [Changed](/docs/reference/engine/classes/Object.md) event that fires with the contents of their `Value` property. As such, this method provides a way to detect changes in other properties of those objects. Note that this event will not pass any arguments to a connected function, so the value of the changed property must be read directly within a script. #### Limitations The event returned by this method does **not** fire for physics-related changes, such as when the [CFrame](/docs/reference/engine/classes/BasePart.md), [AssemblyLinearVelocity](/docs/reference/engine/classes/BasePart.md), [AssemblyAngularVelocity](/docs/reference/engine/classes/BasePart.md), [Position](/docs/reference/engine/classes/BasePart.md), or [Orientation](/docs/reference/engine/classes/BasePart.md) properties of a [BasePart](/docs/reference/engine/classes/BasePart.md) change due to gravity. To detect changes in these properties, consider using a physics-based event like [RunService.PreSimulation](/docs/reference/engine/classes/RunService.md). Additionally, the returned event may not fire on every modification of properties that change very frequently, and/or it may not fire for such properties at all. It's recommended that you carefully test for property changes that impact game logic. *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `property` | `string` | | The property to connect to. | **Returns:** `RBXScriptSignal` — A signal that fires whenever the property changes. **Old-to-New Values with Changed** This code sample demonstrates how to save a value before a changed event fires on it in order to get more information about a change. ```lua local part = Instance.new("Part") local currentColor = part.BrickColor local function onBrickColorChanged() local newColor = part.BrickColor print("Color changed from", currentColor.Name, "to", newColor.Name) currentColor = newColor end part:GetPropertyChangedSignal("BrickColor"):Connect(onBrickColorChanged) part.BrickColor = BrickColor.new("Really red") part.BrickColor = BrickColor.new("Really blue") ``` **Changed and GetPropertyChangedSignal** This code sample demonstrates the equivalence of the `Changed` event and event returned by `GetPropertyChangedSignal`. ```lua local part = Instance.new("Part") local function onBrickColorChanged() print("My color is now " .. part.BrickColor.Name) end local function onChanged(property) if property == "BrickColor" then onBrickColorChanged() end end part:GetPropertyChangedSignal("BrickColor"):Connect(onBrickColorChanged) part.Changed:Connect(onChanged) -- Trigger some changes (because we connected twice, -- both of these will cause two calls to onBrickColorChanged) part.BrickColor = BrickColor.new("Really red") part.BrickColor = BrickColor.new("Institutional white") ``` ### Method: Object:IsA **Signature:** `Object:IsA(className: string): boolean` IsA returns true if the object's class is **equivalent to** or a **subclass** of a given class. This function is similar to the **instanceof** operators in other languages, and is a form of [type introspection](https://en.wikipedia.org/wiki/Type_introspection). To ignore class inheritance, test the [ClassName](/docs/reference/engine/classes/Object.md) property directly instead. For checking native Luau data types (number, string, etc) use the functions `type` and `typeof`. Most commonly, this function is used to test if an object is some kind of part, such as [Part](/docs/reference/engine/classes/Part.md) or [WedgePart](/docs/reference/engine/classes/WedgePart.md), which inherits from [BasePart](/docs/reference/engine/classes/BasePart.md) (an abstract class). For example, if your goal is to change all of a character's limbs to the same color, you might use [GetChildren](/docs/reference/engine/classes/Instance.md) to iterate over the children, then use IsA to filter non-[BasePart](/docs/reference/engine/classes/BasePart.md) objects which lack the [BrickColor](/docs/reference/engine/datatypes/BrickColor.md) property: ```lua local Players = game:GetService("Players") local function paintFigure(character, color) -- Iterate over the child objects of the character for _, child in character:GetChildren() do -- Filter out non-part objects, such as Shirt, Pants and Humanoid -- R15 use MeshPart and R6 use Part, so we use BasePart here to detect both: if child:IsA("BasePart") then child.BrickColor = color end end end paintFigure(Players.Player.Character, BrickColor.new("Bright blue")) ``` Since all classes inherit from [Object](/docs/reference/engine/classes/Object.md), calling `object:IsA("Object")` will always return true. *Security: None · Thread Safety: Safe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `className` | `string` | | The class against which the Object's class will be checked. Case-sensitive. | **Returns:** `boolean` — Describes whether the Object's class matched or is a subclass of the given class. **Instance:IsA()** Demonstrates determining the class of Workspace using [Instance:IsA()](/docs/reference/engine/classes/Instance.md) Workspace is of class 'Workspace', so the first statement is true. Workspace is not of class 'BasePart', so the second statement is false. Workspace inherits from Instance and therefore is of class 'Instance', so the third statement is true. ```lua local Workspace = game:GetService("Workspace") print(Workspace:IsA("Workspace")) -- true print(Workspace:IsA("BasePart")) -- false print(Workspace:IsA("Instance")) -- true ``` ### Method: Object:isA **Signature:** `Object:isA(className: string): boolean` > **Deprecated:** This deprecated function is a variant of [Object:IsA()](/docs/reference/engine/classes/Object.md) which should be used instead. *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `className` | `string` | | | **Returns:** `boolean` ## Events ### Event: Object.Changed **Signature:** `Object.Changed(property: string)` This event fires immediately after an object property is changed, with some exceptions (see below). The new value of a changed property is **not** passed as a parameter. Instead, you can access it using `object[property]`: ``` object.Changed:Connect(function(property) print("The new property's value is", object[property]) end) ``` - If you are only interested in listening for changes to one specific property, consider using the [GetPropertyChangedSignal()](/docs/reference/engine/classes/Object.md) method instead. - To listen for changes in attributes, see [Instance:GetAttributeChangedSignal()](/docs/reference/engine/classes/Instance.md) and [Instance.AttributeChanged](/docs/reference/engine/classes/Instance.md). - To detect when children are added or removed, see [Instance.ChildAdded](/docs/reference/engine/classes/Instance.md) and [Instance.ChildRemoved](/docs/reference/engine/classes/Instance.md). For [ValueBase](/docs/reference/engine/classes/ValueBase.md) objects such as [IntValue](/docs/reference/engine/classes/IntValue.md) and [StringValue](/docs/reference/engine/classes/StringValue.md), this event only fires when the object's `Value` property changes. To detect other changes in [ValueBase](/docs/reference/engine/classes/ValueBase.md) objects, use [GetPropertyChangedSignal()](/docs/reference/engine/classes/Object.md). #### Limitations This event does **not** fire for physics-related changes, such as when the [CFrame](/docs/reference/engine/classes/BasePart.md), [AssemblyLinearVelocity](/docs/reference/engine/classes/BasePart.md), [AssemblyAngularVelocity](/docs/reference/engine/classes/BasePart.md), [Position](/docs/reference/engine/classes/BasePart.md), or [Orientation](/docs/reference/engine/classes/BasePart.md) properties of a [BasePart](/docs/reference/engine/classes/BasePart.md) change due to gravity. To detect changes in these properties, consider using a physics-based event like [RunService.PreSimulation](/docs/reference/engine/classes/RunService.md). Similarly, the `Changed` event does not fire when [AssemblyMass](/docs/reference/engine/classes/BasePart.md) updates due to a change in material, size, or density for a welded part (but the event does fire for [Mass](/docs/reference/engine/classes/BasePart.md) if you modify a part's size). For properties that change frequently, the `Changed` event might not fire for every modification, or it might not fire at all. **If a property change impacts your game logic, test carefully** to ensure the event is firing as you expect. *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `property` | `string` | The name of the property that changed. | **Changed Event** This sample demonstrates the subtleties of the `Changed` event on normal objects and `Value` objects. ```lua -- Demonstrate the Changed event by creating a Part local part = Instance.new("Part") part.Changed:Connect(print) -- This fires Changed with "Transparency" part.Transparency = 0.5 -- Similarly, this fires Changed with "Number" part.Name = "SomePart" -- Since changing BrickColor will also change other -- properties at the same time, this line fires Changed -- with "BrickColor", "Color3" and "Color3uint16". part.BrickColor = BrickColor.Red() -- A NumberValue holds a double-precision floating-point number local vNumber = Instance.new("NumberValue") vNumber.Changed:Connect(print) -- This fires Changed with 123.456 (not "Value") vNumber.Value = 123.456 -- This does not fire Changed vNumber.Name = "SomeNumber" -- A StringValue stores one string local vString = Instance.new("StringValue") vString.Changed:Connect(print) -- This fires Changed with "Hello" (not "Value") vString.Value = "Hello" ``` **Change Detector** This code sample demonstrates the Changed event firing within a parent object. ```lua local object = script.Parent local function onChanged(property) -- Get the current value of the property local value = object[property] -- Print a message saying what changed print(object:GetFullName() .. "." .. property .. " (" .. typeof(value) .. ") changed to " .. tostring(value)) end object.Changed:Connect(onChanged) -- Trigger a simple change in the object (add an underscore to the name) object.Name = "_" .. object.Name ``` --- name: ObjectValue last_updated: 2026-06-29T19:34:07Z inherits: - ValueBase - Instance - Object type: class memory_category: Instances summary: "A container object for a reference to another instance." --- # Class: ObjectValue > A container object for a reference to another instance. ## Description Stores a single reference to another object. If this object is duplicated within Studio and the value refers to an object also being copied, then the new `ObjectValue` points to the copied object instead of the original. Otherwise, the same value is kept. Copying and pasting this object clears the value field. Like all [ValueBase](/docs/reference/engine/classes/ValueBase.md) objects, this single value is stored in the [Value](/docs/reference/engine/classes/ObjectValue.md) property. The `Changed` event fires with the new value being stored in the object, instead of a string representing the property being changed. If [streaming](/docs/en-us/workspace/streaming.md) is enabled, the `Value` property is nil until the referenced object streams in, at which point the `Changed` event fires. ## Code Samples **ObjectValue Example** This code sample creates an ObjectValue in the Workspace which holds a reference to an object in the workspace named "Baseplate". ```lua local objectValue = Instance.new("ObjectValue") objectValue.Name = "MyBaseplateReference" objectValue.Value = workspace:FindFirstChild("Baseplate") objectValue.Parent = workspace ``` ## Properties ### Property: ObjectValue.Value ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "simulationAccess": true } ``` Holds a reference to an instance. ## Events ### Event: ObjectValue.Changed **Signature:** `ObjectValue.Changed(value: Instance)` Fires whenever the [ObjectValue.Value](/docs/reference/engine/classes/ObjectValue.md) changes. It runs with the new value being stored in the argument object, instead of a string representing the property being changed. Listening for the `Changed` signal can be useful in games that use `ObjectValues` to track game state, such as an RPG targeting system. *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `value` | `Instance` | The new value after the change. | **ObjectValue Changed** This example prints the path to the newly reference instance when the [Value](/docs/reference/engine/classes/ObjectValue.md) property is changed. ```lua local objectValue = script.Parent.ObjectValue local part = script.Parent.Part local function printObject(object) print(object:GetFullName()) end objectValue.Changed:Connect(printObject) objectValue.Value = part ``` ### Event: ObjectValue.changed **Signature:** `ObjectValue.changed(value: Instance)` > **Deprecated:** This event is a deprecated variant of [ObjectValue.Changed](/docs/reference/engine/classes/ObjectValue.md) which should be used instead. *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `value` | `Instance` | | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: OpenCloudApiV1 last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated - Deprecated --- # Class: OpenCloudApiV1 ## Methods ### Method: OpenCloudApiV1:CreateModel **Signature:** `OpenCloudApiV1:CreateModel(name: string): OpenCloudModel` *Security: None · Thread Safety: Unsafe · Capabilities: Network* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name` | `string` | | | **Returns:** `OpenCloudModel` ### Method: OpenCloudApiV1:CreateUserNotificationAsync **Signature:** `OpenCloudApiV1:CreateUserNotificationAsync(user: string, userNotification: OpenCloudModel): OpenCloudModel` *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Network* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `user` | `string` | | | | `userNotification` | `OpenCloudModel` | | | **Returns:** `OpenCloudModel` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: OpenCloudService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated - Deprecated --- # Class: OpenCloudService ## Methods ### Method: OpenCloudService:GetApiV1 **Signature:** `OpenCloudService:GetApiV1(): OpenCloudApiV1` *Security: None · Thread Safety: Unsafe · Capabilities: Network* **Returns:** `OpenCloudApiV1` ### Method: OpenCloudService:InvokeAsync **Signature:** `OpenCloudService:InvokeAsync(version: string, methodName: string, arguments: Dictionary, headers?: Dictionary): Dictionary` *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Network* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `version` | `string` | | | | `methodName` | `string` | | | | `arguments` | `Dictionary` | | | | `headers` | `Dictionary` | `nil` | | **Returns:** `Dictionary` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: OrderedDataStore last_updated: 2026-06-29T19:34:07Z inherits: - GlobalDataStore - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated summary: "A GlobalDataStore that also allows for ordered data store entries." --- # Class: OrderedDataStore > A GlobalDataStore that also allows for ordered data store entries. ## Description A **OrderedDataStore** is essentially a [GlobalDataStore](/docs/reference/engine/classes/GlobalDataStore.md) with the exception that stored values must be **integers**. It exposes a method [GetSortedAsync()](/docs/reference/engine/classes/OrderedDataStore.md) which allows inspection of the entries in sorted order using a [DataStorePages](/docs/reference/engine/classes/DataStorePages.md) object. Ordered data stores do not support versioning and metadata, so [DataStoreKeyInfo](/docs/reference/engine/classes/DataStoreKeyInfo.md) is always `nil` for keys in an [OrderedDataStore](/docs/reference/engine/classes/OrderedDataStore.md). If you need versioning and metadata support, use a [DataStore](/docs/reference/engine/classes/DataStore.md). Ordered data stores do not support the optional `userIds` parameter for [SetAsync()](/docs/reference/engine/classes/OrderedDataStore.md) or [IncrementAsync()](/docs/reference/engine/classes/OrderedDataStore.md). See [Data stores](/docs/en-us/cloud-services/data-stores.md) for an overview on how to use ordered data stores. ## Code Samples **OrderedDataStore Basics** This code sample demonstrates usage of an `OrderedDataStore` and pages. ```lua local DataStoreService = game:GetService("DataStoreService") local pointsStore = DataStoreService:GetOrderedDataStore("Points") local function printTopTenPlayers() local isAscending = false local pageSize = 10 local pages = pointsStore:GetSortedAsync(isAscending, pageSize) local topTen = pages:GetCurrentPage() -- The data in 'topTen' is stored with the index being the index on the page -- For each item, 'data.key' is the key in the OrderedDataStore and 'data.value' is the value for rank, data in ipairs(topTen) do local name = data.key local points = data.value print(name .. " is ranked #" .. rank .. " with " .. points .. "points") end -- Potentially load the next page... --pages:AdvanceToNextPageAsync() end -- Create some data pointsStore:SetAsync("Alex", 55) pointsStore:SetAsync("Charley", 32) pointsStore:SetAsync("Sydney", 68) -- Display the top ten players printTopTenPlayers() ``` ## Methods ### Method: OrderedDataStore:GetSortedAsync **Signature:** `OrderedDataStore:GetSortedAsync(ascending: boolean, pagesize: int, minValue: Variant, maxValue: Variant): DataStorePages` Returns a [DataStorePages](/docs/reference/engine/classes/DataStorePages.md) object. The sort order is determined by **ascending**, the length of each page by **pageSize**, and **minValue**/**maxValue** are optional parameters which filter the results. See [Error codes and limits](/docs/en-us/cloud-services/data-stores/error-codes-and-limits.md) for request limits and descriptions of the error codes. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: DataStore* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `ascending` | `boolean` | | A boolean indicating whether the returned data pages are in ascending order. | | `pagesize` | `int` | | The length of each page. By default is 50. The max allowed value is 100. | | `minValue` | `Variant` | | Optional parameter. If set, data pages with a value less than **minValue** will be excluded. | | `maxValue` | `Variant` | | Optional parameter. If set, data pages with a value greater than **maxValue** will be excluded. | **Returns:** `DataStorePages` — A sorted [DataStorePages](/docs/reference/engine/classes/DataStorePages.md) object based on the provided arguments. ## Inherited Members ### From [GlobalDataStore](/docs/reference/engine/classes/GlobalDataStore.md) - **Method `GetAsync(key: string, options?: DataStoreGetOptions): Tuple`**: Returns the value of a key in a specified data store and a - **Method `IncrementAsync(key: string, delta?: int, userIds?: Array, options?: DataStoreIncrementOptions): Variant`**: Increments the value of a key by the provided amount (both must be - **Method `OnUpdate(key: string, callback: Function): RBXScriptConnection`**: Sets a callback function to be executed any time the value associated with *(deprecated)* - **Method `RemoveAsync(key: string): Tuple`**: Removes the specified key while also retaining an accessible version. - **Method `SetAsync(key: string, value: Variant, userIds?: Array, options?: DataStoreSetOptions): Variant`**: Sets the value of the data store for the given key. - **Method `UpdateAsync(key: string, transformFunction: Function): Tuple`**: Updates a key's value with a new value from the specified callback ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: OutfitPages last_updated: 2026-06-29T19:34:07Z inherits: - Pages - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated summary: "The `OutfitPages` class is used in the case of iterating over a user's outfits." --- # Class: OutfitPages > The `OutfitPages` class is used in the case of iterating over a user's > outfits. ## Description The `OutfitPages` class is used in the case of iterating over a user's outfits. It's a special version of the [Pages](/docs/reference/engine/classes/Pages.md) class returned by [AvatarEditorService:GetOutfitsAsync()](/docs/reference/engine/classes/AvatarEditorService.md). ## Inherited Members ### From [Pages](/docs/reference/engine/classes/Pages.md) - **Property `IsFinished`** (`boolean`): Whether or not the current page is the last page available. - **Method `AdvanceToNextPageAsync(): ()`**: Iterates to the next page in the pages object, if possible. - **Method `GetCurrentPage(): Array`**: Returns the items on the current page. The keys in the item are determined ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: PVAdornment last_updated: 2026-06-29T19:34:07Z inherits: - GuiBase3d - GuiBase - Instance - Object type: class memory_category: Instances tags: - NotCreatable summary: "An abstract class of which the inheritors can be adorned to objects of the PVInstance class." --- # Class: PVAdornment > An abstract class of which the inheritors can be adorned to objects of the > [PVInstance](/docs/reference/engine/classes/PVInstance.md) class. ## Description The [PVAdornment](/docs/reference/engine/classes/PVAdornment.md) class is an abstract class of which the inheritors can be adorned to objects of the [PVInstance](/docs/reference/engine/classes/PVInstance.md) class. ## Properties ### Property: PVAdornment.Adornee ```json { "type": "PVInstance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` The [PVInstance](/docs/reference/engine/classes/PVInstance.md) which this [PVAdornment](/docs/reference/engine/classes/PVAdornment.md) is attached to. An adornment will stay positioned and rotated relative to its adornee, even if the adornee moves. ## Inherited Members ### From [GuiBase3d](/docs/reference/engine/classes/GuiBase3d.md) - **Property `Color`** (`BrickColor`): Sets the color of a GUI object. *(deprecated, hidden)* - **Property `Color3`** (`Color3`): Sets the color of this GuiBase3d object. - **Property `Transparency`** (`float`): Sets the transparency of this GuiBase3d object. - **Property `Visible`** (`boolean`): Determines whether this GuiBase3d object and its descendants will ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: PVInstance last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotBrowsable summary: "Abstract class for all objects that have a physical location in the world." --- # Class: PVInstance > Abstract class for all objects that have a physical location in the world. ## Description A [PVInstance](/docs/reference/engine/classes/PVInstance.md) is an abstract class that cannot be created. It is the base for all objects that have a physical location in the world. ## Properties ### Property: PVInstance.Origin ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Transform" } ``` ### Property: PVInstance.Pivot Offset ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Pivot" } ``` ## Methods ### Method: PVInstance:GetPivot **Signature:** `PVInstance:GetPivot(): CFrame` This function gets the pivot of a [PVInstance](/docs/reference/engine/classes/PVInstance.md). This is often used with [PVInstance:PivotTo()](/docs/reference/engine/classes/PVInstance.md) to move a model. [Models](/docs/reference/engine/classes/Model.md) and [BaseParts](/docs/reference/engine/classes/BasePart.md) are both [PVInstances](/docs/reference/engine/classes/PVInstance.md) ("Position Velocity Instances") and so both have this function. *Security: None · Thread Safety: Safe · Simulation Access: true* **Returns:** `CFrame` **Simple Character Teleportation** This code sample is a simple teleport script that moves your character 10 studs forwards in the direction you're currently facing when you press the F key. It does so by getting the current pivot with [PVInstance:GetPivot()](/docs/reference/engine/classes/PVInstance.md) and calling `PVInstance|PivotTo` to move the character forwards. ```lua -- This code should be placed in a LocalScript under StarterPlayerScripts local Players = game:GetService("Players") local ContextActionService = game:GetService("ContextActionService") local player = Players.LocalPlayer local function doTeleport(_actionName, inputState, _inputObject) local character = player.Character if character and character.Parent and inputState == Enum.UserInputState.Begin then -- Move the character 10 studs forwards in the direction they're facing local currentPivot = character:GetPivot() character:PivotTo(currentPivot * CFrame.new(0, 0, -10)) end end ContextActionService:BindAction("Teleport", doTeleport, true, Enum.KeyCode.F) ``` ### Method: PVInstance:PivotTo **Signature:** `PVInstance:PivotTo(targetCFrame: CFrame): ()` Transforms the [PVInstance](/docs/reference/engine/classes/PVInstance.md) along with all of its descendant [PVInstances](/docs/reference/engine/classes/PVInstance.md) such that the pivot is now located at the specified [CFrame](/docs/reference/engine/datatypes/CFrame.md). This is the primary function that should be used to move [Models](/docs/reference/engine/classes/Model.md) via scripting. [BaseParts](/docs/reference/engine/classes/BasePart.md) are moved in this way by having their [CFrame](/docs/reference/engine/datatypes/CFrame.md) transformed by the necessary offset. [Models](/docs/reference/engine/classes/Model.md) are moved in this way by having their [Model.WorldPivot](/docs/reference/engine/classes/Model.md) transformed by the necessary offset. Note that for efficiency purposes, [Object.Changed](/docs/reference/engine/classes/Object.md) events are not fired for [Position](/docs/reference/engine/classes/BasePart.md) and [Orientation](/docs/reference/engine/classes/BasePart.md) of [BaseParts](/docs/reference/engine/classes/BasePart.md) moved in this way; they are only fired for [CFrame](/docs/reference/engine/datatypes/CFrame.md). When calling [PivotTo](/docs/reference/engine/classes/PVInstance.md) on [Models](/docs/reference/engine/classes/Model.md), the offsets of the descendant parts and models are cached, such that subsequent calls to [PivotTo](/docs/reference/engine/classes/PVInstance.md) on the same model do not accumulate floating point drift between the parts making up the model. [Models](/docs/reference/engine/classes/Model.md) and [BaseParts](/docs/reference/engine/classes/BasePart.md) are both [PVInstances](/docs/reference/engine/classes/PVInstance.md) ("Position Velocity Instances") and so both have this function. *Security: None · Thread Safety: Unsafe · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `targetCFrame` | `CFrame` | | The [CFrame](/docs/reference/engine/datatypes/CFrame.md) that the [PVInstance](/docs/reference/engine/classes/PVInstance.md) pivot should equal after moving it. | **Returns:** `()` **Simple Character Teleportation** This code sample is a simple teleport script that moves your character 10 studs forwards in the direction you're currently facing when you press the F key. It does so by getting the current pivot with [PVInstance:GetPivot()](/docs/reference/engine/classes/PVInstance.md) and calling `PVInstance|PivotTo` to move the character forwards. ```lua -- This code should be placed in a LocalScript under StarterPlayerScripts local Players = game:GetService("Players") local ContextActionService = game:GetService("ContextActionService") local player = Players.LocalPlayer local function doTeleport(_actionName, inputState, _inputObject) local character = player.Character if character and character.Parent and inputState == Enum.UserInputState.Begin then -- Move the character 10 studs forwards in the direction they're facing local currentPivot = character:GetPivot() character:PivotTo(currentPivot * CFrame.new(0, 0, -10)) end end ContextActionService:BindAction("Teleport", doTeleport, true, Enum.KeyCode.F) ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: PackageLink last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotBrowsable summary: "Links a DataModel instance to a corresponding asset in the cloud." --- # Class: PackageLink > Links a [DataModel](/docs/reference/engine/classes/DataModel.md) instance to a corresponding asset in the cloud. ## Description The purpose of the [PackageLink](/docs/reference/engine/classes/PackageLink.md) object is to link a [DataModel](/docs/reference/engine/classes/DataModel.md) instance to a corresponding asset in the cloud. This improves flows for collaboration, version control, and sharing for models. The [PackageLink](/docs/reference/engine/classes/PackageLink.md) instance will be a child of the root of the entire package hierarchy. [PackageLinks](/docs/reference/engine/classes/PackageLink.md) are not creatable through [Scripts](/docs/reference/engine/classes/Script.md). They can only be added through interaction with Studio and can only be parented to [Instances](/docs/reference/engine/classes/Instance.md) that can be published independently of [DataModel](/docs/reference/engine/classes/DataModel.md) publish. The [PackageLink](/docs/reference/engine/classes/PackageLink.md) instance will always be the first child shown in the tree view, regardless of sorting. ## Properties ### Property: PackageLink.AutoUpdate ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Link", "capabilities": [ "AssetRead" ] } ``` When this property is set to true, the package associated with the given [PackageLink](/docs/reference/engine/classes/PackageLink.md) will be automatically updated to the latest version. By default, this property is false upon creation of a package. This property lets you decide if a given package should be automatically updated to the latest version when entering a given place. The game periodically checks for new updates while a place is open. ### Property: PackageLink.Creator ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AssetRead" ] } ``` The creator of the package asset. ### Property: PackageLink.DefaultName ```json { "type": "string", "access": "ReadOnly", "security": { "read": "None", "write": "NotAccessibleSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Link", "capabilities": [ "AssetRead" ] } ``` ### Property: PackageLink.PackageAssetName ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AssetRead" ] } ``` The asset name of the package. ### Property: PackageLink.PackageId ```json { "type": "ContentId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Link", "capabilities": [ "AssetRead" ] } ``` The ID of the asset this package corresponds to. ### Property: PackageLink.PermissionLevel ```json { "type": "PackagePermission", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "AssetRead" ] } ``` The package permission for the current Studio user. ### Property: PackageLink.SerializedDefaultAttributes ```json { "type": "BinaryString", "access": "ReadOnly", "security": { "read": "None", "write": "NotAccessibleSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Link", "capabilities": [ "AssetRead" ] } ``` ### Property: PackageLink.Status ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Link", "capabilities": [ "AssetRead" ] } ``` The status of the package. It can be one of the following statuses: Up To Date, Changed, New Version Available, Changed + New Version Available. ### Property: PackageLink.VersionNumber ```json { "type": "int64", "access": "ReadOnly", "security": { "read": "None", "write": "NotAccessibleSecurity" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Link", "capabilities": [ "AssetRead" ] } ``` This property refers to a revision of a specific package ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: PackageService last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated --- # Class: PackageService ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Pages last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated summary: "An abstract class for pages objects." --- # Class: Pages > An abstract class for pages objects. ## Description An object which is essentially a table of pages, each of which is a sorted list of the key/value pairs. ## Code Samples **Pages Iterator** When each page in a [Pages](/docs/reference/engine/classes/Pages.md) object contains a list of multiple items, this iterator function and its helper may be useful. ```lua -- Reformat pages as tables local function pagesToTable(pages) local items = {} while true do table.insert(items, pages:GetCurrentPage()) if pages.IsFinished then break end pages:AdvanceToNextPageAsync() end return items end local function iterPageItems(pages) local contents = pagesToTable(pages) -- Track the current page number starting at 1 local pageNum = 1 -- Get last page number so we don't iterate over it local lastPageNum = #contents -- for resumes this coroutine until there's nothing to go through return coroutine.wrap(function() -- Loop until page number is greater than last page number while pageNum <= lastPageNum do -- Go through all the entries of the current page for _, item in ipairs(contents[pageNum]) do -- Pause loop to let developer handle entry and page number coroutine.yield(item, pageNum) end pageNum += 1 end end) end -- Using the iterPageItems function to iterate through the pages of a catalog search local AvatarEditorService = game:GetService("AvatarEditorService") local parameters = CatalogSearchParams.new() parameters.SearchKeyword = "Hair" local catalogPages = AvatarEditorService:SearchCatalogAsync(parameters) for item, pageNumber in iterPageItems(catalogPages) do print(item, pageNumber) end ``` ## Properties ### Property: Pages.IsFinished ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` Whether or not the current page is the last page available. ## Methods ### Method: Pages:AdvanceToNextPageAsync **Signature:** `Pages:AdvanceToNextPageAsync(): ()` Iterates to the next page in the pages object, if possible. The request limit is the same of the endpoint originally called. *Yields · Security: None · Thread Safety: Unsafe* **Returns:** `()` ### Method: Pages:GetCurrentPage **Signature:** `Pages:GetCurrentPage(): Array` Returns the items on the current page. The keys in the item are determined by the source of this object. *Security: None · Thread Safety: Unsafe* **Returns:** `Array` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Pants last_updated: 2026-06-29T19:34:07Z inherits: - Clothing - CharacterAppearance - Instance - Object type: class memory_category: Instances summary: "Displays a Pants texture from the Roblox website to display on a Humanoid rig." --- # Class: Pants > Displays a Pants texture from the Roblox website to display on a > [Humanoid](/docs/reference/engine/classes/Humanoid.md) rig. ## Description The `Pants` object displays a pants texture from Roblox on a [Humanoid](/docs/reference/engine/classes/Humanoid.md) rig. Pants cover the torso and legs, and will be covered by a [Shirt](/docs/reference/engine/classes/Shirt.md) on the torso. To be visible, a `Pants` must be a sibling of a [Humanoid](/docs/reference/engine/classes/Humanoid.md) and have its [PantsTemplate](/docs/reference/engine/classes/Pants.md) property set to an appropriate texture such as `rbxassetid://86896501`. The pants texture may be colorized using the [Clothing.Color3](/docs/reference/engine/classes/Clothing.md) property. Pants are automatically loaded on [Player](/docs/reference/engine/classes/Player.md) characters if their avatar is wearing one. ## Code Samples **Change Shirt / Pants** This sample includes a simple function to change the texture of the `Shirt` and `Pants` worn by a player's character. If shirt and pants don't exist then they are created. Note, this should be run every time the character spawns. If a developer is looking to permanently change a character's appearance to a preset it is recommended they use [Player.CharacterAppearance](/docs/reference/engine/classes/Player.md). ```lua local Players = game:GetService("Players") local function replaceClothes(player) local character = player.Character if character then -- look for shirts / pants local shirt = character:FindFirstChildOfClass("Shirt") local pants = character:FindFirstChildOfClass("Pants") -- create shirts / pants if they don't exist if not shirt then shirt = Instance.new("Shirt") shirt.Parent = character end if not pants then pants = Instance.new("Pants") pants.Parent = character end -- reset shirt / pants content ids shirt.ShirtTemplate = "http://www.roblox.com/asset/?id=83326831" pants.PantsTemplate = "http://www.roblox.com/asset/?id=10045638" end end for _index, player in ipairs(Players:GetPlayers()) do replaceClothes(player) end ``` ## Properties ### Property: Pants.PantsTemplate ```json { "type": "ContentId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "AvatarAppearance" ] } ``` The content ID link pointing to the pants template hosted on Roblox. This content ID is different than the website URL of the pants. It can be found by pasting the website URL of the pants into the **PantsTemplate** property in Studio. Alternatively, [InsertService:LoadAsset()](/docs/reference/engine/classes/InsertService.md) can be used to insert the pants into the workspace, for example: ``` local InsertService = game:GetService("InsertService") local Workspace = game:GetService("Workspace") local webURL = "https://www.roblox.com/catalog/1804739/Jeans" local assetId = tonumber(string.match(webURL, "%d+") or 0) -- Extract the number local success, model = pcall(function() return InsertService:LoadAsset(assetId) end) if success then model.Parent = Workspace end ``` For a [Shirt](/docs/reference/engine/classes/Shirt.md) object's template, see [Shirt.ShirtTemplate](/docs/reference/engine/classes/Shirt.md). **Change Shirt / Pants** This sample includes a simple function to change the texture of the `Shirt` and `Pants` worn by a player's character. If shirt and pants don't exist then they are created. Note, this should be run every time the character spawns. If a developer is looking to permanently change a character's appearance to a preset it is recommended they use [Player.CharacterAppearance](/docs/reference/engine/classes/Player.md). ```lua local Players = game:GetService("Players") local function replaceClothes(player) local character = player.Character if character then -- look for shirts / pants local shirt = character:FindFirstChildOfClass("Shirt") local pants = character:FindFirstChildOfClass("Pants") -- create shirts / pants if they don't exist if not shirt then shirt = Instance.new("Shirt") shirt.Parent = character end if not pants then pants = Instance.new("Pants") pants.Parent = character end -- reset shirt / pants content ids shirt.ShirtTemplate = "http://www.roblox.com/asset/?id=83326831" pants.PantsTemplate = "http://www.roblox.com/asset/?id=10045638" end end for _index, player in ipairs(Players:GetPlayers()) do replaceClothes(player) end ``` ## Inherited Members ### From [Clothing](/docs/reference/engine/classes/Clothing.md) - **Property `Color3`** (`Color3`): Determines the colorization to be applied to the Clothing texture. ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Part last_updated: 2026-06-29T19:34:07Z inherits: - FormFactorPart - BasePart - PVInstance - Instance - Object type: class memory_category: BaseParts summary: "A common type of BasePart that comes in different primitive shapes." --- # Class: Part > A common type of [BasePart](/docs/reference/engine/classes/BasePart.md) that comes in different primitive shapes. ## Description The Part object is a type of [BasePart](/docs/reference/engine/classes/BasePart.md). It comes in five different primitive shapes: Ball, Block, Cylinder, Wedge, and CornerWedge. ## Code Samples **Create a Part in a Script** The script below spawns a new `Part` instance and sets several of the part's properties. Most notably, the script sets the [Part.Shape](/docs/reference/engine/classes/Part.md) property to [PartType.Ball](/docs/reference/engine/enums/PartType.md). It also names the part _JurassicPart_, anchors it, makes it a child of _Workspace_, and sets its color to white. ```lua local part = Instance.new("Part") part.Name = "JurassicPart" part.Anchored = true part.Shape = Enum.PartType.Ball part.Color = Color3.new(1, 1, 1) part.Parent = workspace -- Put the part into the Workspace ``` ## Properties ### Property: Part.Shape ```json { "type": "PartType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Part", "capabilities": [ "Basic" ] } ``` The Shape property sets the overall shape of the object to one of a predetermined list of built-in shapes. The [PartType](/docs/reference/engine/enums/PartType.md) enum controls the shape value, and has five possible shapes: | Shape/Value | Description | | ----------- | --------------------------------------- | | Ball | A spherical shape. | | Block | A block shape. | | Cylinder | A cylinder shape. | | Wedge | A wedge shape with a slope on one side. | | CornerWedge | A wedge shape with slopes on two sides. | [MeshPart](/docs/reference/engine/classes/MeshPart.md) and [solid modeling](/docs/en-us/parts/solid-modeling.md) can be used to obtain completely custom part shapes. Collisions between balls, blocks, and wedges, and corner wedges are exact, whereas collisions between terrain, cylinders, TriangleMeshes, and other geometry types are approximations. This means that the ball shape can be useful to create stable colliders for car wheels. **Create a Part in a Script** The script below spawns a new `Part` instance and sets several of the part's properties. Most notably, the script sets the [Part.Shape](/docs/reference/engine/classes/Part.md) property to [PartType.Ball](/docs/reference/engine/enums/PartType.md). It also names the part _JurassicPart_, anchors it, makes it a child of _Workspace_, and sets its color to white. ```lua local part = Instance.new("Part") part.Name = "JurassicPart" part.Anchored = true part.Shape = Enum.PartType.Ball part.Color = Color3.new(1, 1, 1) part.Parent = workspace -- Put the part into the Workspace ``` ## Inherited Members ### From [FormFactorPart](/docs/reference/engine/classes/FormFactorPart.md) - **Property `FormFactor`** (`FormFactor`): *(deprecated)* - **Property `formFactor`** (`FormFactor`): *(deprecated, hidden)* ### From [BasePart](/docs/reference/engine/classes/BasePart.md) - **Property `Anchored`** (`boolean`): Determines whether a part is immovable by physics. - **Property `AssemblyAngularVelocity`** (`Vector3`): The angular velocity of the part's assembly. - **Property `AssemblyCenterOfMass`** (`Vector3`): The center of mass of the part's assembly in world space. - **Property `AssemblyLinearVelocity`** (`Vector3`): The linear velocity of the part's assembly. - **Property `AssemblyMass`** (`float`): The total mass of the part's assembly. - **Property `AssemblyRootPart`** (`BasePart`): A reference to the root part of the assembly. - **Property `AudioCanCollide`** (`boolean`): Determines whether the part will physically interact with audio - **Property `BackParamA`** (`float`): Determines the first parameter for the SurfaceType on the Back face of a *(deprecated, hidden)* - **Property `BackParamB`** (`float`): Determines the second parameter for the SurfaceType on the Back face of a *(deprecated, hidden)* - **Property `BackSurface`** (`SurfaceType`): Determines the type of surface for the back face of a part. - **Property `BackSurfaceInput`** (`InputType`): Determines the kind of input for the Back face of a part. *(deprecated, hidden)* - **Property `BottomParamA`** (`float`): Determines the first parameter for the SurfaceType on the Bottom face of a *(deprecated, hidden)* - **Property `BottomParamB`** (`float`): Determines the second parameter for the SurfaceType on the Bottom face of *(deprecated, hidden)* - **Property `BottomSurface`** (`SurfaceType`): Determines the type of surface for the bottom face of a part. - **Property `BottomSurfaceInput`** (`InputType`): Determines the kind of input for the Bottom face of a part. *(deprecated, hidden)* - **Property `BrickColor`** (`BrickColor`): Determines the color of a part. - **Property `brickColor`** (`BrickColor`): *(deprecated)* - **Property `CanCollide`** (`boolean`): Determines whether a part may collide with other parts. - **Property `CanQuery`** (`boolean`): Determines whether the part is considered during spatial query operations. - **Property `CanTouch`** (`boolean`): Determines if Touched and - **Property `CastShadow`** (`boolean`): Determines whether or not a part casts a shadow. - **Property `CenterOfMass`** (`Vector3`): Describes the world position in which a part's center of mass is located. - **Property `CFrame`** (`CFrame`): Determines the position and orientation of the BasePart in the - **Property `CollisionGroup`** (`string`): Describes the name of a part's collision group. - **Property `CollisionGroupId`** (`int`): Describes the automatically set ID number of a part's collision group. *(deprecated)* - **Property `Color`** (`Color3`): Determines the color of a part. - **Property `CurrentPhysicalProperties`** (`PhysicalProperties`): Indicates the current physical properties of the part. - **Property `CustomPhysicalProperties`** (`PhysicalProperties`): Determines several physical properties of a part. - **Property `Elasticity`** (`float`): Used to control the Elasticity of the part, but it no longer does *(deprecated, hidden)* - **Property `EnableFluidForces`** (`boolean`): Used to enable or disable aerodynamic forces on parts and assemblies. - **Property `ExtentsCFrame`** (`CFrame`): The CFrame of the physical extents of the BasePart. - **Property `ExtentsSize`** (`Vector3`): The actual physical size of the BasePart as regarded by the - **Property `Friction`** (`float`): Used to control the Friction of the part, but now it no longer does *(deprecated, hidden)* - **Property `FrontParamA`** (`float`): Determines the first parameter for the SurfaceType on the Front face of a *(deprecated, hidden)* - **Property `FrontParamB`** (`float`): Determines the second parameter for the SurfaceType on the Front face of a *(deprecated, hidden)* - **Property `FrontSurface`** (`SurfaceType`): Determines the type of surface for the front face of a part. - **Property `FrontSurfaceInput`** (`InputType`): Determines the kind of input for the Front face of a part (-Z direction). *(deprecated, hidden)* - **Property `LeftParamA`** (`float`): Determines the first parameter for the SurfaceType on the Left face of a *(deprecated, hidden)* - **Property `LeftParamB`** (`float`): Determines the second parameter for the SurfaceType on the Left face of a *(deprecated, hidden)* - **Property `LeftSurface`** (`SurfaceType`): Determines the type of surface for the left face of a part. - **Property `LeftSurfaceInput`** (`InputType`): Determines the kind of input for the Left face of a part. *(deprecated, hidden)* - **Property `LocalTransparencyModifier`** (`float`): Determines a multiplier for BasePart.Transparency that is only *(hidden)* - **Property `Locked`** (`boolean`): Determines whether a part is selectable in Studio. - **Property `Mass`** (`float`): Describes the mass of the part, the product of its density and volume. - **Property `Massless`** (`boolean`): Determines whether the part contributes to the total mass or inertia of - **Property `Material`** (`Material`): Determines the texture and default physical properties of a part. - **Property `MaterialVariant`** (`string`): The name of MaterialVariant. - **Property `Orientation`** (`Vector3`): Describes the rotation of the part in the world. *(hidden)* - **Property `PivotOffset`** (`CFrame`): Specifies the offset of the part's pivot from its CFrame. - **Property `Position`** (`Vector3`): Describes the position of the part in the world. *(hidden)* - **Property `ReceiveAge`** (`float`): Time since last recorded physics update. *(hidden)* - **Property `Reflectance`** (`float`): Determines how much a part reflects the skybox. - **Property `ResizeableFaces`** (`Faces`): Describes the faces on which a part may be resized. - **Property `ResizeIncrement`** (`int`): Describes the smallest change in size allowable by the - **Property `RightParamA`** (`float`): Determines the first parameter for the SurfaceType on the Right face of a *(deprecated, hidden)* - **Property `RightParamB`** (`float`): Determines the second parameter for the SurfaceType on the Right face of a *(deprecated, hidden)* - **Property `RightSurface`** (`SurfaceType`): Determines the type of surface for the right face of a part. - **Property `RightSurfaceInput`** (`InputType`): Determines the kind of input for the Right face of a part (-X direction). *(deprecated, hidden)* - **Property `RootPriority`** (`int`): The main rule in determining the root part of an assembly. - **Property `Rotation`** (`Vector3`): The rotation of the part in degrees for the three axes. - **Property `RotVelocity`** (`Vector3`): Determines a part's change in orientation over time. *(deprecated, hidden)* - **Property `Size`** (`Vector3`): Determines the dimensions of a part (length, width, height). - **Property `SpecificGravity`** (`float`): The ratio of the part's density to the density of water determined by the *(deprecated)* - **Property `TopParamA`** (`float`): Determines the first parameter for the SurfaceType on the Top face of a *(deprecated, hidden)* - **Property `TopParamB`** (`float`): Determines the second parameter for the SurfaceType on the Top face of a *(deprecated, hidden)* - **Property `TopSurface`** (`SurfaceType`): Determines the type of surface for the top face of a part. - **Property `TopSurfaceInput`** (`InputType`): Determines the kind of input for the Top face of a part (+Y direction). *(deprecated, hidden)* - **Property `Transparency`** (`float`): Determines how much a part can be seen through (the inverse of part - **Property `Velocity`** (`Vector3`): Determines a part's change in position over time. *(deprecated, hidden)* - **Method `AngularAccelerationToTorque(angAcceleration: Vector3, angVelocity?: Vector3): Vector3`**: Returns the torque needed to achieve a given angular acceleration on this - **Method `ApplyAngularImpulse(impulse: Vector3): ()`**: Apply an angular impulse to the assembly. - **Method `ApplyImpulse(impulse: Vector3): ()`**: Apply an impulse to the assembly at the assembly's - **Method `ApplyImpulseAtPosition(impulse: Vector3, position: Vector3): ()`**: Apply an impulse to the assembly at specified position. - **Method `BreakJoints(): ()`**: Breaks any surface connection with any adjacent part, including *(deprecated)* - **Method `breakJoints(): ()`**: *(deprecated)* - **Method `CanCollideWith(part: BasePart): boolean`**: Returns whether the parts can collide with each other. - **Method `CanSetNetworkOwnership(): Tuple`**: Checks whether you can set a part's network ownership. - **Method `GetClosestPointOnSurface(position: Vector3): Vector3`**: Returns the closest point on the part's surface to the given point. - **Method `GetConnectedParts(recursive?: boolean): List`**: Returns a table of parts connected to the object by any kind of rigid - **Method `GetJoints(): Instances`**: Return all Joints or Constraints that is connected to this Part. - **Method `GetMass(): float`**: Returns the value of the Mass property. - **Method `getMass(): float`**: *(deprecated)* - **Method `GetNetworkOwner(): Instance`**: Returns the current player who is the network owner of this part, or `nil` - **Method `GetNetworkOwnershipAuto(): boolean`**: Returns true if the game engine automatically decides the network owner - **Method `GetNoCollisionConstraints(): Instances`**: - **Method `GetRenderCFrame(): CFrame`**: OBSOLETE. Returns a CFrame describing where the part is being rendered at. *(deprecated)* - **Method `GetRootPart(): Instance`**: Returns the base part of an assembly of parts. *(deprecated)* - **Method `GetTouchingParts(): Instances`**: Returns a table of all BasePart.CanCollide true parts that - **Method `GetVelocityAtPosition(position: Vector3): Vector3`**: Returns the linear velocity of the part's assembly at the given position - **Method `IntersectAsync(parts: Instances, collisionfidelity?: CollisionFidelity, renderFidelity?: RenderFidelity): Instance`**: Note: It is highly recommended to use the newer - **Method `IsGrounded(): boolean`**: Returns true if the object is connected to a part that will hold it in - **Method `MakeJoints(): ()`**: Creates a joint on any side of the object that has a surface ID that can *(deprecated)* - **Method `makeJoints(): ()`**: *(deprecated)* - **Method `Resize(normalId: NormalId, deltaAmount: int): boolean`**: Changes the size of an object just like using the Studio resize tool. - **Method `resize(normalId: NormalId, deltaAmount: int): boolean`**: *(deprecated)* - **Method `SetNetworkOwner(playerInstance?: Player): ()`**: Sets the given player as network owner for this and all connected parts. - **Method `SetNetworkOwnershipAuto(): ()`**: Lets the game engine dynamically decide who will handle the part's physics - **Method `SubtractAsync(parts: Instances, collisionfidelity?: CollisionFidelity, renderFidelity?: RenderFidelity): Instance`**: Note: It is highly recommended to use the newer - **Method `TorqueToAngularAcceleration(torque: Vector3, angVelocity?: Vector3): Vector3`**: Returns the angular acceleration that would result from applying a given - **Method `UnionAsync(parts: Instances, collisionfidelity?: CollisionFidelity, renderFidelity?: RenderFidelity): Instance`**: Note: It is highly recommended to use the newer - **Event `LocalSimulationTouched`**: *(deprecated)* - **Event `OutfitChanged`**: *(deprecated)* - **Event `StoppedTouching`**: *(deprecated)* - **Event `Touched`**: Fires when a part touches another part as a result of physical movement. - **Event `TouchEnded`**: Fires when a part stops touching another part as a result of physical ### From [PVInstance](/docs/reference/engine/classes/PVInstance.md) - **Property `Origin`** (`CFrame`): - **Property `Pivot Offset`** (`CFrame`): - **Method `GetPivot(): CFrame`**: Gets the pivot of a PVInstance. - **Method `PivotTo(targetCFrame: CFrame): ()`**: Transforms the PVInstance along with all of its descendant ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: PartAdornment last_updated: 2026-06-29T19:34:07Z inherits: - GuiBase3d - GuiBase - Instance - Object type: class memory_category: Instances tags: - NotCreatable summary: "An abstract class for GUI elements that are adorned to (displayed as attached to) objects deriving from BasePart." --- # Class: PartAdornment > An abstract class for GUI elements that are adorned to (displayed as attached > to) objects deriving from [BasePart](/docs/reference/engine/classes/BasePart.md). ## Properties ### Property: PartAdornment.Adornee ```json { "type": "BasePart", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic", "UI" ] } ``` Sets the object to adorn to. ## Inherited Members ### From [GuiBase3d](/docs/reference/engine/classes/GuiBase3d.md) - **Property `Color`** (`BrickColor`): Sets the color of a GUI object. *(deprecated, hidden)* - **Property `Color3`** (`Color3`): Sets the color of this GuiBase3d object. - **Property `Transparency`** (`float`): Sets the transparency of this GuiBase3d object. - **Property `Visible`** (`boolean`): Determines whether this GuiBase3d object and its descendants will ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: PartOperation last_updated: 2026-06-29T19:34:07Z inherits: - TriangleMeshPart - BasePart - PVInstance - Instance - Object type: class memory_category: Instances summary: "An abstract class that all parts based on solid modeling inherit from." --- # Class: PartOperation > An abstract class that all parts based on solid modeling inherit from. ## Description An abstract class that all parts based on [solid modeling](/docs/en-us/parts/solid-modeling.md) inherit from. ## Properties ### Property: PartOperation.RenderFidelity ```json { "type": "RenderFidelity", "access": "ReadOnly", "security": { "read": "None", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic", "CSG" ] } ``` This property determines the level of detail that the solid modeled part will be shown in. It can be set to the possible values of the [RenderFidelity](/docs/reference/engine/enums/RenderFidelity.md) enum except for [Performance](/docs/reference/engine/enums/RenderFidelity.md). The default value is [Automatic](/docs/reference/engine/enums/RenderFidelity.md), meaning the part's detail is based on its distance from the camera as outlined in the following table. | Distance From Camera | Render Fidelity | | --- | --- | | Less than 250 studs | Highest | | 250-500 studs | Medium | | 500 or more studs | Lowest | ### Property: PartOperation.SmoothingAngle ```json { "type": "float", "access": "ReadOnly", "security": { "read": "None", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic", "CSG" ] } ``` This property represents an angle in degrees for a threshold value between face normals on a [solid modeled](/docs/en-us/parts/solid-modeling.md) part. If the normal difference is less than the value, normals will be adjusted to smooth the difference. While a value between 30 and 70 degrees usually produces a good result, values between 90 and 180 are not recommended as they may cause a "shadowing" effect on unions with sharp edges. Note that smoothing does not affect the normals between different materials or different colors. ![Solid modeled part with SmoothingAngle of 0](../../../assets/modeling/solid-modeling/CSG-SmoothingAngle-0.jpg)_[SmoothingAngle](/docs/reference/engine/classes/PartOperation.md) = 0_ ![Solid modeled part with SmoothingAngle of 50](../../../assets/modeling/solid-modeling/CSG-SmoothingAngle-50.jpg)_[SmoothingAngle](/docs/reference/engine/classes/PartOperation.md) = 50_ ### Property: PartOperation.TriangleCount ```json { "type": "int", "access": "ReadOnly", "security": { "read": "None", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic", "CSG" ] } ``` The number of polygons in this solid model. ### Property: PartOperation.UsePartColor ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic", "CSG" ] } ``` Sets whether the [PartOperation](/docs/reference/engine/classes/PartOperation.md) can be recolored using the [BasePart.Color](/docs/reference/engine/classes/BasePart.md) or [BasePart.BrickColor](/docs/reference/engine/classes/BasePart.md) properties. When true, the entire union will be colored as per [Color](/docs/reference/engine/classes/BasePart.md) or [BrickColor](/docs/reference/engine/classes/BasePart.md). When false, the parts in the union will maintain their original colors from before the onion operation was performed. ## Methods ### Method: PartOperation:SubstituteGeometry **Signature:** `PartOperation:SubstituteGeometry(source: Instance): ()` Substitutes the geometry of this [PartOperation](/docs/reference/engine/classes/PartOperation.md) with the geometry of another [PartOperation](/docs/reference/engine/classes/PartOperation.md). This makes it easier to utilize the geometry of a solid modeling operation like [UnionAsync()](/docs/reference/engine/classes/GeometryService.md), [SubtractAsync()](/docs/reference/engine/classes/GeometryService.md), or [IntersectAsync()](/docs/reference/engine/classes/GeometryService.md) but maintain properties, attributes, tags, and children of the main part such as [Attachments](/docs/reference/engine/classes/Attachment.md), [Constraints](/docs/reference/engine/classes/Constraint.md), [ParticleEmitters](/docs/reference/engine/classes/ParticleEmitter.md), light objects, decals, and more. This approach also circumvents the potential "flicker" of completely replacing the original [PartOperation](/docs/reference/engine/classes/PartOperation.md) with another. Note that if you're calling this method on a [PartOperation](/docs/reference/engine/classes/PartOperation.md) with child [Attachments](/docs/reference/engine/classes/Attachment.md) or [Constraints](/docs/reference/engine/classes/Constraint.md), you should calculate the affected instances with [CalculateConstraintsToPreserve()](/docs/reference/engine/classes/GeometryService.md), then drop those whose recommended parent is `nil`. *Security: None · Thread Safety: Unsafe · Capabilities: Basic, CSG* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `source` | `Instance` | | The [PartOperation](/docs/reference/engine/classes/PartOperation.md) whose geometry will substitute the geometry of this [PartOperation](/docs/reference/engine/classes/PartOperation.md). | **Returns:** `()` **Substitute Geometry and Drop Constraints** The following example substitutes the geometry of one [PartOperation](/docs/reference/engine/classes/PartOperation.md) with the geometry of another [PartOperation](/docs/reference/engine/classes/PartOperation.md), then drops constraints/attachments that should not be preserved after substitution. ```lua local GeometryService = game:GetService("GeometryService") local mainPart = workspace.PurpleBlock local otherParts = { workspace.BlueBlock } local options = { CollisionFidelity = Enum.CollisionFidelity.Default, RenderFidelity = Enum.RenderFidelity.Automatic, SplitApart = false, } local constraintOptions = { tolerance = 0.1, weldConstraintPreserve = Enum.WeldConstraintPreserve.All, } -- Perform union operation in pcall() since it's asyncronous local success, newParts = pcall(function() return GeometryService:UnionAsync(mainPart, otherParts, options) end) if success and #newParts > 0 and mainPart:IsA("PartOperation") then -- Set first part in resulting operation as part to use for substitution -- First part is simply an option; this can be any PartOperation local substitutePart = newParts[1] -- Reposition part to the position of main part substitutePart.CFrame = mainPart.CFrame -- Calculate constraints/attachments to either preserve or drop local recommendedTable = GeometryService:CalculateConstraintsToPreserve(mainPart, newParts, constraintOptions) -- Substitute main part's geometry with substitution geometry mainPart:SubstituteGeometry(substitutePart) -- Drop constraints/attachments that are not automatically preserved with substitution for _, item in pairs(recommendedTable) do if item.Attachment then if item.ConstraintParent == nil then item.Constraint.Parent = nil end if item.AttachmentParent == nil then item.Attachment.Parent = nil end elseif item.WeldConstraint then if item.Parent == nil then item.WeldConstraint.Parent = nil end end end -- Destroy other parts for _, otherPart in pairs(otherParts) do otherPart.Parent = nil otherPart:Destroy() end end ``` ## Inherited Members ### From [TriangleMeshPart](/docs/reference/engine/classes/TriangleMeshPart.md) - **Property `CollisionFidelity`** (`CollisionFidelity`): Determines the level of detail the part's physics will adhere to its mesh. - **Property `FluidFidelity`** (`FluidFidelity`): Determines the geometric representation used to compute aerodynamic forces - **Property `MeshSize`** (`Vector3`): ### From [BasePart](/docs/reference/engine/classes/BasePart.md) - **Property `Anchored`** (`boolean`): Determines whether a part is immovable by physics. - **Property `AssemblyAngularVelocity`** (`Vector3`): The angular velocity of the part's assembly. - **Property `AssemblyCenterOfMass`** (`Vector3`): The center of mass of the part's assembly in world space. - **Property `AssemblyLinearVelocity`** (`Vector3`): The linear velocity of the part's assembly. - **Property `AssemblyMass`** (`float`): The total mass of the part's assembly. - **Property `AssemblyRootPart`** (`BasePart`): A reference to the root part of the assembly. - **Property `AudioCanCollide`** (`boolean`): Determines whether the part will physically interact with audio - **Property `BackParamA`** (`float`): Determines the first parameter for the SurfaceType on the Back face of a *(deprecated, hidden)* - **Property `BackParamB`** (`float`): Determines the second parameter for the SurfaceType on the Back face of a *(deprecated, hidden)* - **Property `BackSurface`** (`SurfaceType`): Determines the type of surface for the back face of a part. - **Property `BackSurfaceInput`** (`InputType`): Determines the kind of input for the Back face of a part. *(deprecated, hidden)* - **Property `BottomParamA`** (`float`): Determines the first parameter for the SurfaceType on the Bottom face of a *(deprecated, hidden)* - **Property `BottomParamB`** (`float`): Determines the second parameter for the SurfaceType on the Bottom face of *(deprecated, hidden)* - **Property `BottomSurface`** (`SurfaceType`): Determines the type of surface for the bottom face of a part. - **Property `BottomSurfaceInput`** (`InputType`): Determines the kind of input for the Bottom face of a part. *(deprecated, hidden)* - **Property `BrickColor`** (`BrickColor`): Determines the color of a part. - **Property `brickColor`** (`BrickColor`): *(deprecated)* - **Property `CanCollide`** (`boolean`): Determines whether a part may collide with other parts. - **Property `CanQuery`** (`boolean`): Determines whether the part is considered during spatial query operations. - **Property `CanTouch`** (`boolean`): Determines if Touched and - **Property `CastShadow`** (`boolean`): Determines whether or not a part casts a shadow. - **Property `CenterOfMass`** (`Vector3`): Describes the world position in which a part's center of mass is located. - **Property `CFrame`** (`CFrame`): Determines the position and orientation of the BasePart in the - **Property `CollisionGroup`** (`string`): Describes the name of a part's collision group. - **Property `CollisionGroupId`** (`int`): Describes the automatically set ID number of a part's collision group. *(deprecated)* - **Property `Color`** (`Color3`): Determines the color of a part. - **Property `CurrentPhysicalProperties`** (`PhysicalProperties`): Indicates the current physical properties of the part. - **Property `CustomPhysicalProperties`** (`PhysicalProperties`): Determines several physical properties of a part. - **Property `Elasticity`** (`float`): Used to control the Elasticity of the part, but it no longer does *(deprecated, hidden)* - **Property `EnableFluidForces`** (`boolean`): Used to enable or disable aerodynamic forces on parts and assemblies. - **Property `ExtentsCFrame`** (`CFrame`): The CFrame of the physical extents of the BasePart. - **Property `ExtentsSize`** (`Vector3`): The actual physical size of the BasePart as regarded by the - **Property `Friction`** (`float`): Used to control the Friction of the part, but now it no longer does *(deprecated, hidden)* - **Property `FrontParamA`** (`float`): Determines the first parameter for the SurfaceType on the Front face of a *(deprecated, hidden)* - **Property `FrontParamB`** (`float`): Determines the second parameter for the SurfaceType on the Front face of a *(deprecated, hidden)* - **Property `FrontSurface`** (`SurfaceType`): Determines the type of surface for the front face of a part. - **Property `FrontSurfaceInput`** (`InputType`): Determines the kind of input for the Front face of a part (-Z direction). *(deprecated, hidden)* - **Property `LeftParamA`** (`float`): Determines the first parameter for the SurfaceType on the Left face of a *(deprecated, hidden)* - **Property `LeftParamB`** (`float`): Determines the second parameter for the SurfaceType on the Left face of a *(deprecated, hidden)* - **Property `LeftSurface`** (`SurfaceType`): Determines the type of surface for the left face of a part. - **Property `LeftSurfaceInput`** (`InputType`): Determines the kind of input for the Left face of a part. *(deprecated, hidden)* - **Property `LocalTransparencyModifier`** (`float`): Determines a multiplier for BasePart.Transparency that is only *(hidden)* - **Property `Locked`** (`boolean`): Determines whether a part is selectable in Studio. - **Property `Mass`** (`float`): Describes the mass of the part, the product of its density and volume. - **Property `Massless`** (`boolean`): Determines whether the part contributes to the total mass or inertia of - **Property `Material`** (`Material`): Determines the texture and default physical properties of a part. - **Property `MaterialVariant`** (`string`): The name of MaterialVariant. - **Property `Orientation`** (`Vector3`): Describes the rotation of the part in the world. *(hidden)* - **Property `PivotOffset`** (`CFrame`): Specifies the offset of the part's pivot from its CFrame. - **Property `Position`** (`Vector3`): Describes the position of the part in the world. *(hidden)* - **Property `ReceiveAge`** (`float`): Time since last recorded physics update. *(hidden)* - **Property `Reflectance`** (`float`): Determines how much a part reflects the skybox. - **Property `ResizeableFaces`** (`Faces`): Describes the faces on which a part may be resized. - **Property `ResizeIncrement`** (`int`): Describes the smallest change in size allowable by the - **Property `RightParamA`** (`float`): Determines the first parameter for the SurfaceType on the Right face of a *(deprecated, hidden)* - **Property `RightParamB`** (`float`): Determines the second parameter for the SurfaceType on the Right face of a *(deprecated, hidden)* - **Property `RightSurface`** (`SurfaceType`): Determines the type of surface for the right face of a part. - **Property `RightSurfaceInput`** (`InputType`): Determines the kind of input for the Right face of a part (-X direction). *(deprecated, hidden)* - **Property `RootPriority`** (`int`): The main rule in determining the root part of an assembly. - **Property `Rotation`** (`Vector3`): The rotation of the part in degrees for the three axes. - **Property `RotVelocity`** (`Vector3`): Determines a part's change in orientation over time. *(deprecated, hidden)* - **Property `Size`** (`Vector3`): Determines the dimensions of a part (length, width, height). - **Property `SpecificGravity`** (`float`): The ratio of the part's density to the density of water determined by the *(deprecated)* - **Property `TopParamA`** (`float`): Determines the first parameter for the SurfaceType on the Top face of a *(deprecated, hidden)* - **Property `TopParamB`** (`float`): Determines the second parameter for the SurfaceType on the Top face of a *(deprecated, hidden)* - **Property `TopSurface`** (`SurfaceType`): Determines the type of surface for the top face of a part. - **Property `TopSurfaceInput`** (`InputType`): Determines the kind of input for the Top face of a part (+Y direction). *(deprecated, hidden)* - **Property `Transparency`** (`float`): Determines how much a part can be seen through (the inverse of part - **Property `Velocity`** (`Vector3`): Determines a part's change in position over time. *(deprecated, hidden)* - **Method `AngularAccelerationToTorque(angAcceleration: Vector3, angVelocity?: Vector3): Vector3`**: Returns the torque needed to achieve a given angular acceleration on this - **Method `ApplyAngularImpulse(impulse: Vector3): ()`**: Apply an angular impulse to the assembly. - **Method `ApplyImpulse(impulse: Vector3): ()`**: Apply an impulse to the assembly at the assembly's - **Method `ApplyImpulseAtPosition(impulse: Vector3, position: Vector3): ()`**: Apply an impulse to the assembly at specified position. - **Method `BreakJoints(): ()`**: Breaks any surface connection with any adjacent part, including *(deprecated)* - **Method `breakJoints(): ()`**: *(deprecated)* - **Method `CanCollideWith(part: BasePart): boolean`**: Returns whether the parts can collide with each other. - **Method `CanSetNetworkOwnership(): Tuple`**: Checks whether you can set a part's network ownership. - **Method `GetClosestPointOnSurface(position: Vector3): Vector3`**: Returns the closest point on the part's surface to the given point. - **Method `GetConnectedParts(recursive?: boolean): List`**: Returns a table of parts connected to the object by any kind of rigid - **Method `GetJoints(): Instances`**: Return all Joints or Constraints that is connected to this Part. - **Method `GetMass(): float`**: Returns the value of the Mass property. - **Method `getMass(): float`**: *(deprecated)* - **Method `GetNetworkOwner(): Instance`**: Returns the current player who is the network owner of this part, or `nil` - **Method `GetNetworkOwnershipAuto(): boolean`**: Returns true if the game engine automatically decides the network owner - **Method `GetNoCollisionConstraints(): Instances`**: - **Method `GetRenderCFrame(): CFrame`**: OBSOLETE. Returns a CFrame describing where the part is being rendered at. *(deprecated)* - **Method `GetRootPart(): Instance`**: Returns the base part of an assembly of parts. *(deprecated)* - **Method `GetTouchingParts(): Instances`**: Returns a table of all BasePart.CanCollide true parts that - **Method `GetVelocityAtPosition(position: Vector3): Vector3`**: Returns the linear velocity of the part's assembly at the given position - **Method `IntersectAsync(parts: Instances, collisionfidelity?: CollisionFidelity, renderFidelity?: RenderFidelity): Instance`**: Note: It is highly recommended to use the newer - **Method `IsGrounded(): boolean`**: Returns true if the object is connected to a part that will hold it in - **Method `MakeJoints(): ()`**: Creates a joint on any side of the object that has a surface ID that can *(deprecated)* - **Method `makeJoints(): ()`**: *(deprecated)* - **Method `Resize(normalId: NormalId, deltaAmount: int): boolean`**: Changes the size of an object just like using the Studio resize tool. - **Method `resize(normalId: NormalId, deltaAmount: int): boolean`**: *(deprecated)* - **Method `SetNetworkOwner(playerInstance?: Player): ()`**: Sets the given player as network owner for this and all connected parts. - **Method `SetNetworkOwnershipAuto(): ()`**: Lets the game engine dynamically decide who will handle the part's physics - **Method `SubtractAsync(parts: Instances, collisionfidelity?: CollisionFidelity, renderFidelity?: RenderFidelity): Instance`**: Note: It is highly recommended to use the newer - **Method `TorqueToAngularAcceleration(torque: Vector3, angVelocity?: Vector3): Vector3`**: Returns the angular acceleration that would result from applying a given - **Method `UnionAsync(parts: Instances, collisionfidelity?: CollisionFidelity, renderFidelity?: RenderFidelity): Instance`**: Note: It is highly recommended to use the newer - **Event `LocalSimulationTouched`**: *(deprecated)* - **Event `OutfitChanged`**: *(deprecated)* - **Event `StoppedTouching`**: *(deprecated)* - **Event `Touched`**: Fires when a part touches another part as a result of physical movement. - **Event `TouchEnded`**: Fires when a part stops touching another part as a result of physical ### From [PVInstance](/docs/reference/engine/classes/PVInstance.md) - **Property `Origin`** (`CFrame`): - **Property `Pivot Offset`** (`CFrame`): - **Method `GetPivot(): CFrame`**: Gets the pivot of a PVInstance. - **Method `PivotTo(targetCFrame: CFrame): ()`**: Transforms the PVInstance along with all of its descendant ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: PartOperationAsset last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances summary: "An internal service which cannot be used by developers." --- # Class: PartOperationAsset > An internal service which cannot be used by developers. ## Description An internal instance used to save/load solid models onto the Roblox cloud. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ParticleEmitter last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances summary: "A special object that emits customizable 2D particles into the world." --- # Class: ParticleEmitter > A special object that emits customizable 2D particles into the world. ## Description A **ParticleEmitter** is a special object that emits customizable 2D particles into the world. To emit and render particles, it must be parented to a [BasePart](/docs/reference/engine/classes/BasePart.md) or an [Attachment](/docs/reference/engine/classes/Attachment.md) within such a part. When parented to a [BasePart](/docs/reference/engine/classes/BasePart.md), particles spawn randomly within the part's bounding box or [shape](/docs/en-us/effects/particle-emitters.md#shape); when parented to an [Attachment](/docs/reference/engine/classes/Attachment.md), particles spawn from the attachment's position. Particles emit automatically when the emitter is [Enabled](/docs/reference/engine/classes/ParticleEmitter.md) with a non-zero [Rate](/docs/reference/engine/classes/ParticleEmitter.md), or manually when the [Emit](/docs/reference/engine/classes/ParticleEmitter.md) method is called. With a non-zero [Speed](/docs/reference/engine/classes/ParticleEmitter.md), particles are set in motion outwards and/or inwards, depending on the [ShapeInOut](/docs/reference/engine/classes/ParticleEmitter.md) property. By default, particles face the camera, but the [Orientation](/docs/reference/engine/classes/ParticleEmitter.md) can be modified to respect the particle velocity instead. During the [Lifetime](/docs/reference/engine/classes/ParticleEmitter.md) of the particles, they can change appearance according to the [Color](/docs/reference/engine/classes/ParticleEmitter.md) and [Size](/docs/reference/engine/classes/ParticleEmitter.md). Their motion can change over time according to the [Drag](/docs/reference/engine/classes/ParticleEmitter.md) and [Acceleration](/docs/reference/engine/classes/ParticleEmitter.md) properties, and they can also move as their parent moves when they are [LockedToPart](/docs/reference/engine/classes/ParticleEmitter.md) or have a non-zero [VelocityInheritance](/docs/reference/engine/classes/ParticleEmitter.md). To learn more about creating and customizing particle emitters, see [Particle Emitters](/docs/en-us/effects/particle-emitters.md). ## Code Samples **Creating a Particle Emitter from Scratch** This rather lengthy code sample shows how every property of a `ParticleEmitter` can be set, including [NumberRange](/docs/reference/engine/datatypes/NumberRange.md), [NumberSequence](/docs/reference/engine/datatypes/NumberSequence.md) and [ColorSequence](/docs/reference/engine/datatypes/ColorSequence.md) properties. Below is how the ParticleEmitter should look after every property is set. Try playing around with the different properties to customize how the effect looks! ```lua local emitter = Instance.new("ParticleEmitter") -- Number of particles = Rate * Lifetime emitter.Rate = 5 -- Particles per second emitter.Lifetime = NumberRange.new(1, 1) -- How long the particles should be alive (min, max) emitter.Enabled = true -- Visual properties emitter.Texture = "rbxassetid://1266170131" -- A transparent image of a white ring -- For Color, build a ColorSequence using ColorSequenceKeypoint local colorKeypoints = { -- API: ColorSequenceKeypoint.new(time, color) ColorSequenceKeypoint.new(0, Color3.new(1, 1, 1)), -- At t=0, White ColorSequenceKeypoint.new(0.5, Color3.new(1, 0.5, 0)), -- At t=.5, Orange ColorSequenceKeypoint.new(1, Color3.new(1, 0, 0)), -- At t=1, Red } emitter.Color = ColorSequence.new(colorKeypoints) local numberKeypoints = { -- API: NumberSequenceKeypoint.new(time, size, envelop) NumberSequenceKeypoint.new(0, 1), -- At t=0, fully transparent NumberSequenceKeypoint.new(0.1, 0), -- At t=.1, fully opaque NumberSequenceKeypoint.new(0.5, 0.25), -- At t=.5, mostly opaque NumberSequenceKeypoint.new(1, 1), -- At t=1, fully transparent } emitter.Transparency = NumberSequence.new(numberKeypoints) emitter.LightEmission = 1 -- When particles overlap, multiply their color to be brighter emitter.LightInfluence = 0 -- Don't be affected by world lighting -- Speed properties emitter.EmissionDirection = Enum.NormalId.Front -- Emit forwards emitter.Speed = NumberRange.new(0, 0) -- Speed of zero emitter.Drag = 0 -- Apply no drag to particle motion emitter.VelocitySpread = NumberRange.new(0, 0) emitter.VelocityInheritance = 0 -- Don't inherit parent velocity emitter.Acceleration = Vector3.new(0, 0, 0) emitter.LockedToPart = false -- Don't lock the particles to the parent emitter.SpreadAngle = Vector2.new(0, 0) -- No spread angle on either axis -- Simulation properties local numberKeypoints2 = { NumberSequenceKeypoint.new(0, 0), -- At t=0, size of 0 NumberSequenceKeypoint.new(1, 10), -- At t=1, size of 10 } emitter.Size = NumberSequence.new(numberKeypoints2) emitter.ZOffset = -1 -- Render slightly behind the actual position emitter.Rotation = NumberRange.new(0, 360) -- Start at random rotation emitter.RotSpeed = NumberRange.new(0) -- Do not rotate during simulation -- Create an attachment so particles emit from the exact same spot (concentric rings) local attachment = Instance.new("Attachment") attachment.Position = Vector3.new(0, 5, 0) -- Move the attachment upwards a little attachment.Parent = script.Parent emitter.Parent = attachment ``` **Expected output:** When you run the code sample, you should see particles that look like the provided animation. ## Properties ### Property: ParticleEmitter.Acceleration ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Motion", "capabilities": [ "Basic" ] } ``` The **Acceleration** property determines how the [Speed](/docs/reference/engine/classes/ParticleEmitter.md) of particles changes over their lifetime. It is defined using a [Vector3](/docs/reference/engine/datatypes/Vector3.md) to determine the acceleration on the global **X**/**Y**/**Z** axes and is measured in studs per second squared. When changed, this property affects all particles emitted by the emitter, both current and future. Acceleration will slow particles down if the vector points in the opposite [EmissionDirection](/docs/reference/engine/classes/ParticleEmitter.md) in which they are emitted. Otherwise, it will speed them up. ### Property: ParticleEmitter.Brightness ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` Scales the light emitted from the emitter when [ParticleEmitter.LightInfluence](/docs/reference/engine/classes/ParticleEmitter.md) is 0. ### Property: ParticleEmitter.Color ```json { "type": "ColorSequence", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` The **Color** property determines the color of all active particles over their individual lifetimes. The color applies to the [Texture](/docs/reference/engine/classes/ParticleEmitter.md) when rendering and it uses the texture alpha along with the emitter's [Transparency](/docs/reference/engine/classes/ParticleEmitter.md). If an emitter has a [LightEmission](/docs/reference/engine/classes/ParticleEmitter.md) value that's greater than 0, darker colors make particles appear more transparent. Changing this property affects all particles emitted by the emitter, both current and future. When this property uses a gradient [ColorSequence](/docs/reference/engine/datatypes/ColorSequence.md), a particle's present color is determined by linearly interpolating on the sequence using the particle's age and its total lifetime. For example, if a particle spawned 2 seconds ago and has a 4 second lifetime, the color will be whatever is 50% of the way through the [ColorSequence](/docs/reference/engine/datatypes/ColorSequence.md). ### Property: ParticleEmitter.Drag ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Particles", "capabilities": [ "Basic" ] } ``` The **Drag** property determines the rate in seconds at which individual particles will lose half their speed via exponential decay. Drag is applied by scaling the expected velocity from [Speed](/docs/reference/engine/classes/ParticleEmitter.md) and any velocity inherited from the parent from [VelocityInheritance](/docs/reference/engine/classes/ParticleEmitter.md). Setting this property to a negative value will cause particles' velocities to grow exponentially. ### Property: ParticleEmitter.EmissionDirection ```json { "type": "NormalId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Emission", "capabilities": [ "Basic" ] } ``` The **EmissionDirection** property determines the face ([NormalId](/docs/reference/engine/enums/NormalId.md)) of the parent object that particles emit from. A negative [Speed](/docs/reference/engine/classes/ParticleEmitter.md) means particles emit in the opposite direction. [SpreadAngle](/docs/reference/engine/classes/ParticleEmitter.md) further varies the emission direction. If you add a [ParticleEmitter](/docs/reference/engine/classes/ParticleEmitter.md) to an [Attachment](/docs/reference/engine/classes/Attachment.md), which has a direction, you can rotate the attachment itself ([Attachment.Orientation](/docs/reference/engine/classes/Attachment.md)) instead of using this property. ### Property: ParticleEmitter.Enabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Emission", "capabilities": [ "Basic" ] } ``` The **Enabled** property determines if particles emit from the emitter. Setting this to `false` stops further particles from spawning, but any existing particles remain active until they expire. This property is useful when you have a pre-made particle effect that you want to remain disabled until you need it to emit particles. If you want to clear all particles from a disabled emitter, call [Clear()](/docs/reference/engine/classes/ParticleEmitter.md). Then, if desired, call [Emit()](/docs/reference/engine/classes/ParticleEmitter.md) on the emitter to emit and render particles. ### Property: ParticleEmitter.FlipbookBlendFrames ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Flipbook", "capabilities": [ "Basic" ] } ``` The **FlipbookBlendFrames** property determines whether the frames in the flipbook texture are blended between using a linear crossfade, or swapped instantly. ### Property: ParticleEmitter.FlipbookFramerate ```json { "type": "NumberRange", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Flipbook", "capabilities": [ "Basic" ] } ``` The **FlipbookFramerate** property determines how fast the flipbook texture animates in frames per second. Like [Lifetime](/docs/reference/engine/classes/ParticleEmitter.md), you can set a minimum and maximum range to randomize the framerate of the flipbook, with a maximum of 30 frames per second. ### Property: ParticleEmitter.FlipbookIncompatible ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Flipbook", "capabilities": [ "Basic" ] } ``` The error message to display if the [Texture](/docs/reference/engine/classes/ParticleEmitter.md) is incompatible for a flipbook. The flipbook texture size must be an exact multiple of the flipbook layout size. ### Property: ParticleEmitter.FlipbookLayout ```json { "type": "ParticleFlipbookLayout", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Flipbook", "capabilities": [ "Basic" ] } ``` The **FlipbookLayout** property determines the layout of the texture. It can be any value of the [ParticleFlipbookLayout](/docs/reference/engine/enums/ParticleFlipbookLayout.md) enum: - **None** – Disable flipbook features and use the texture as a single static texture over the particle's lifetime. - **Grid2x2** – 2×2 frames for a 4-frame animation. - **Grid4x4** – 4×4 frames for a 16-frame animation. - **Grid8x8** – 8×8 frames for a 64-frame animation. - **Custom** – A custom grid size defined by [FlipbookSizeX](/docs/reference/engine/classes/ParticleEmitter.md) and [FlipbookSizeY](/docs/reference/engine/classes/ParticleEmitter.md). ### Property: ParticleEmitter.FlipbookMode ```json { "type": "ParticleFlipbookMode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Flipbook", "capabilities": [ "Basic" ] } ``` The **FlipbookMode** property determines the type of the flipbook animation. It can be any value of the [ParticleFlipbookMode](/docs/reference/engine/enums/ParticleFlipbookMode.md) enum: - **Loop** – Continuously play through all frames, starting back at the first frame after playing the last. - **OneShot** – Play through the animation only once across the particle's lifetime. With this setting, the [FlipbookFramerate](/docs/reference/engine/classes/ParticleEmitter.md) property doesn't apply; instead, the framerate is determined by the particle's [Lifetime](/docs/reference/engine/classes/ParticleEmitter.md) divided evenly by the number of frames in the animation. **OneShot** animations are useful for clear non-repeating animations, such as an explosion that creates a puff of smoke and then fades out. - **PingPong** – Play from the first to the last frame, then in reverse from the last to the first, repeating throughout the [Lifetime](/docs/reference/engine/classes/ParticleEmitter.md) of the particle. - **Random** – Play the frames in a random order, blending/crossfading from one frame to the next. This can be useful for organic particle textures at low framerates, such as stars slowly twinkling between subtly different shapes. ### Property: ParticleEmitter.FlipbookSizeX ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Flipbook", "capabilities": [ "Basic" ] } ``` The **FlipbookSizeX** property defines the number of horizontal frames (columns) in a custom flipbook layout. This property only applies when [FlipbookLayout](/docs/reference/engine/classes/ParticleEmitter.md) is set to [Custom](/docs/reference/engine/enums/ParticleFlipbookLayout.md). ### Property: ParticleEmitter.FlipbookSizeY ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Flipbook", "capabilities": [ "Basic" ] } ``` The **FlipbookSizeY** property defines the number of vertical frames (rows) in a custom flipbook layout. This property only applies when [FlipbookLayout](/docs/reference/engine/classes/ParticleEmitter.md) is set to [Custom](/docs/reference/engine/enums/ParticleFlipbookLayout.md). ### Property: ParticleEmitter.FlipbookStartRandom ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Flipbook", "capabilities": [ "Basic" ] } ``` The **FlipbookStartRandom** property determines whether each particle begins at a random frame of the animation instead of always starting at the first frame. One use case is to enable this property and also set [FlipbookFramerate](/docs/reference/engine/classes/ParticleEmitter.md) to zero, causing each emitted particle to be a static frame chosen randomly from the flipbook texture. ### Property: ParticleEmitter.Lifetime ```json { "type": "NumberRange", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Emission", "capabilities": [ "Basic" ] } ``` The **Lifetime** property defines the maximum and minimum ages for newly emitted particles. Lifetimes are stored on a per-particle basis, so if this value is changed, existing particles will stay active until their randomly chosen lifetime ends. A lifetime of 0 will prevent particles from emitting at all. ### Property: ParticleEmitter.LightEmission ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` The **LightEmission** property determines the blending of the [Texture](/docs/reference/engine/classes/ParticleEmitter.md) colors with the colors behind them. A value of 0 uses normal blending mode while a value of 1 uses additive blending. When changed, this property instantly affects all particles owned by the emitter, both current and future. This property should not be confused with [LightInfluence](/docs/reference/engine/classes/ParticleEmitter.md) which determines how particles are affected by environmental light. This property does **not** cause particles to light the environment around them. To accomplish that, consider using a [PointLight](/docs/reference/engine/classes/PointLight.md). ### Property: ParticleEmitter.LightInfluence ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` The **LightInfluence** property determines how much environmental light affects the color of individual particles when they render. It must be in the range of 0–1; behavior of values outside of this range are not defined. At 0, particles are not influenced by light at all (they retain full brightness); at 1, particles are fully influenced by light (in complete darkness, particles will be black). By default, this value is 1 if inserted with Studio tools. If inserted using [Instance.new()](/docs/reference/engine/datatypes/Instance.md), it is 0. ### Property: ParticleEmitter.LockedToPart ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Particles", "capabilities": [ "Basic" ] } ``` The **LockedToPart** property determines if particles "stick" to the emission source (the [Attachment](/docs/reference/engine/classes/Attachment.md) or [BasePart](/docs/reference/engine/classes/BasePart.md) to which the [ParticleEmitter](/docs/reference/engine/classes/ParticleEmitter.md) is parented). If `true`, active particles will move in lock step if the parent object moves. Alternatively, consider using the [VelocityInheritance](/docs/reference/engine/classes/ParticleEmitter.md) property with a value of 1 which may be more appropriate for some effects. ### Property: ParticleEmitter.Orientation ```json { "type": "ParticleOrientation", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` The **Orientation** property determines which orientation mode to use for an emitter's particle geometry. | Orientation | Particle Behavior | | --- | --- | | **FacingCamera** | Standard camera-facing billboard quad; default behavior. | | **FacingCameraWorldUp** | Facing the camera, but rotating only on the vertical upward world **Y** axis. | | **VelocityParallel** | Aligned parallel to their direction of movement. | | **VelocityPerpendicular** | Aligned perpendicular to their direction movement. | ### Property: ParticleEmitter.Rate ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Emission", "capabilities": [ "Basic" ] } ``` The **Rate** property determines how many particles are emitted per second while the emitter is [Enabled](/docs/reference/engine/classes/ParticleEmitter.md). It is the inverse of frequency, meaning that a rate of 5 emits a particle every 0.2 seconds. When changed, this property has no affect on any active particles. ### Property: ParticleEmitter.Rotation ```json { "type": "NumberRange", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Emission", "capabilities": [ "Basic" ] } ``` The **Rotation** property determines the range of rotations in degrees for newly emitted particles, measured in degrees. Positive values are in the clockwise direction. This property is commonly set to `[0, 360]` to provide a completely random rotation to new particles. [RotSpeed](/docs/reference/engine/classes/ParticleEmitter.md) also influences the rotation of a particle over its lifetime. Changes to this value only affect new particles; existing particles maintain the rotation at which they were originally emitted. ### Property: ParticleEmitter.RotSpeed ```json { "type": "NumberRange", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Emission", "capabilities": [ "Basic" ] } ``` The **RotSpeed** property determines a random range of angular speeds for newly emitted particles, measured in degrees per second. A random angular speed is chosen upon emission, so changing this property doesn't affect active particles. This property, along with [Rotation](/docs/reference/engine/classes/ParticleEmitter.md), affects the angle of the rendered particle image. Particles with very high angular speeds can appear to rotate slower or not at all, since the angle of rotation is synchronized with the software render speed. For example, if particles rotate at exactly 360 degrees every frame, there will be no apparent change in rotation. ### Property: ParticleEmitter.Shape ```json { "type": "ParticleEmitterShape", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "EmitterShape", "capabilities": [ "Basic" ] } ``` The **Shape** property sets the shape of the emitter to either a box, sphere, cylinder, or disc. After you make a selection, you can adjust the [ShapeStyle](/docs/reference/engine/classes/ParticleEmitter.md), [ShapeInOut](/docs/reference/engine/classes/ParticleEmitter.md), and [ShapePartial](/docs/reference/engine/classes/ParticleEmitter.md) properties to further customize particle emission. For visual examples, see [here](/docs/en-us/effects/particle-emitters.md#shape). ### Property: ParticleEmitter.ShapeInOut ```json { "type": "ParticleEmitterShapeInOut", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "EmitterShape", "capabilities": [ "Basic" ] } ``` Sets whether particles emit outward only, inward only, or in both directions. For visual examples, see [here](/docs/en-us/effects/particle-emitters.md#shape). ### Property: ParticleEmitter.ShapePartial ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "EmitterShape", "capabilities": [ "Basic" ] } ``` Depending on the [Shape](/docs/reference/engine/classes/ParticleEmitter.md) value, this property performs a different action: - For cylinders, it specifies the top radius proportion. A value of 0 means the top of the cylinder has zero radius, making it a cone. A value of 1 means the cylinder has no deformation. - For discs, it specifies the inner radius proportion. A value of 0 means the disc is fully closed (circle/ellipse), while a value of 1 means emission only occurs on the outermost rim of the disc. Values between 0 and 1 emit from an annulus with a certain thickness. - For spheres, it specifies the hemispherical angle over which particles emit. A value of 1 means particles emit from the entire sphere; a value of 0.5 means particles emit from a half-dome; a value of 0 means particles only emit from a single point at the north pole. For visual examples, see [here](/docs/en-us/effects/particle-emitters.md#shape). ### Property: ParticleEmitter.ShapeStyle ```json { "type": "ParticleEmitterShapeStyle", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "EmitterShape", "capabilities": [ "Basic" ] } ``` Sets particle emission to either volumetric or surface-only emission. For visual examples, see [here](/docs/en-us/effects/particle-emitters.md#shape). ### Property: ParticleEmitter.Size ```json { "type": "NumberSequence", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` The **Size** property determines the world size of all active particles over their individual lifetimes. This property represents the dimensions of the square [Texture](/docs/reference/engine/classes/ParticleEmitter.md) for each particle. It is a [NumberSequence](/docs/reference/engine/datatypes/NumberSequence.md) that works similarly to [Transparency](/docs/reference/engine/classes/ParticleEmitter.md). A particle's present size is determined by linearly interpolating on this sequence using the particle's age and its total lifetime. For example, if a particle spawned 2 seconds ago and has a 4 second lifetime, the size will be whatever is 50% of the way through the [NumberSequence](/docs/reference/engine/datatypes/NumberSequence.md). For any [NumberSequenceKeypoint](/docs/reference/engine/datatypes/NumberSequenceKeypoint.md) with a non-zero envelope value, a random value in the envelope range is chosen for each keypoint for each particle when it emits. ### Property: ParticleEmitter.Speed ```json { "type": "NumberRange", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Emission", "capabilities": [ "Basic" ] } ``` The **Speed** property determines a random range of velocities (minimum to maximum) at which new particles will emit, measured in studs per second. Each particle's velocity is chosen upon emission and it applies in the [EmissionDirection](/docs/reference/engine/classes/ParticleEmitter.md). Negative values cause particles to travel in reverse. Note that changing [Speed](/docs/reference/engine/classes/ParticleEmitter.md) does not affect active particles and they retain whatever speed they already have. However, [Acceleration](/docs/reference/engine/classes/ParticleEmitter.md), [Drag](/docs/reference/engine/classes/ParticleEmitter.md), and [VelocityInheritance](/docs/reference/engine/classes/ParticleEmitter.md) can be used to affect the speed of active particles over their lifetime. ### Property: ParticleEmitter.SpreadAngle ```json { "type": "Vector2", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Emission", "capabilities": [ "Basic" ] } ``` The **SpreadAngle** property determines the random angles at which a particle may be emitted. For example, if the [EmissionDirection](/docs/reference/engine/classes/ParticleEmitter.md) is **Top** (+**Y**), this [Vector2](/docs/reference/engine/datatypes/Vector2.md) describes the size of the random angle spread on the **X**/**Z** axes, in degrees. Setting one axis to 360 will cause particles to emit in all direction in a **circle**. Setting both to 360 will cause particles to emit in all directions in a **sphere**. ### Property: ParticleEmitter.Squash ```json { "type": "NumberSequence", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` Allows for non-uniform scaling of particles, curve-controlled over their lifetime. Values greater than 0 cause particles to both shrink horizontally and grow vertically, while values less than 0 cause particles to both grow horizontally and shrink vertically. ### Property: ParticleEmitter.Texture ```json { "type": "ContentId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` The **Texture** property determines the image rendered on particles. This image is influenced by [Color](/docs/reference/engine/classes/ParticleEmitter.md), [Transparency](/docs/reference/engine/classes/ParticleEmitter.md), [LightInfluence](/docs/reference/engine/classes/ParticleEmitter.md), and [LightEmission](/docs/reference/engine/classes/ParticleEmitter.md). Textures with transparent backgrounds work best for particles. ### Property: ParticleEmitter.TimeScale ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Particles", "capabilities": [ "Basic" ] } ``` A value between 0 and 1 that controls the speed of the particle effect. At 1, it runs at normal speed; at 0.5 it runs at half speed; at 0 it freezes in time. ### Property: ParticleEmitter.Transparency ```json { "type": "NumberSequence", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` The **Transparency** property determines the transparency of all active particles over their individual lifetimes. It works similar to [Size](/docs/reference/engine/classes/ParticleEmitter.md) in how it affects particles over time. In terms of rendering, a value of 0 is completely visible (opaque) and a value of 1 is completely invisible (not rendered at all). A particle's present transparency is determined by linearly interpolating on this sequence using the particle's age and its total lifetime. For example, if a particle spawned 2 seconds ago and has a 4 second lifetime, the transparency will be whatever is 50% of the way through the [NumberSequence](/docs/reference/engine/datatypes/NumberSequence.md). For any [NumberSequenceKeypoint](/docs/reference/engine/datatypes/NumberSequenceKeypoint.md) with a non-zero envelope value, a random value in the envelope range is chosen for each keypoint for each particle when it emits. ### Property: ParticleEmitter.VelocityInheritance ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Particles", "capabilities": [ "Basic" ] } ``` The **VelocityInheritance** property determines how much of the parent part's [Velocity](/docs/reference/engine/classes/BasePart.md) is inherited by particles when they are emitted. A value of 0 means that no velocity is inherited, while a value of 1 means the particle will have the exact same speed as the parent [BasePart](/docs/reference/engine/classes/BasePart.md). When used in conjunction with [Drag](/docs/reference/engine/classes/ParticleEmitter.md), a particle emitter can appear to "shed" particles from a moving part. ### Property: ParticleEmitter.WindAffectsDrag ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Particles", "capabilities": [ "Basic" ] } ``` If true, emitted particles follow the [Workspace.GlobalWind](/docs/reference/engine/classes/Workspace.md) vector. Only applies if the [Drag](/docs/reference/engine/classes/ParticleEmitter.md) property is greater than 0. ### Property: ParticleEmitter.ZOffset ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` The **ZOffset** property determines the forward-backward render position of particles, in studs, without changing their size on the screen. When changed, this property affects both current and future particles. Note that this property accepts fractional values; it is not like [GuiObject.ZIndex](/docs/reference/engine/classes/GuiObject.md) (an integer). Positive values move particles closer to the camera and negative values move particles away. Sufficiently negative values can cause particles to render inside or behind the parent part. ### Property: ParticleEmitter.VelocitySpread ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Emission", "capabilities": [ "Basic" ] } ``` > **Deprecated:** This property has been superseded by [ParticleEmitter.SpreadAngle](/docs/reference/engine/classes/ParticleEmitter.md) which should be used in all new work. This property determines how offset a particle can be fired from the local emitter direction of its parent. When a particle is created its offset is picked randomly between 0 and VelocitySpread. This value is measured in degrees. ### Property: ParticleEmitter.LocalTransparencyModifier *(hidden)* ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` ## Methods ### Method: ParticleEmitter:Clear **Signature:** `ParticleEmitter:Clear(): ()` The **Clear** method instantly clears all existing particles that have been emitted by the [ParticleEmitter](/docs/reference/engine/classes/ParticleEmitter.md) through its natural emission (non-zero [Rate](/docs/reference/engine/classes/ParticleEmitter.md) on an [Enabled](/docs/reference/engine/classes/ParticleEmitter.md) emitter) or via [Emit()](/docs/reference/engine/classes/ParticleEmitter.md). *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Returns:** `()` **ParticleEmitter Burst** This code sample causes a `ParticleEmitter` to [ParticleEmitter:Emit()](/docs/reference/engine/classes/ParticleEmitter.md) particles in bursts of 10 every 2 seconds. It [ParticleEmitter:Clear()](/docs/reference/engine/classes/ParticleEmitter.md)s any existing particles before doing so. ```lua local emitter = script.Parent while true do emitter:Clear() emitter:Emit(10) task.wait(2) end ``` **Expected output:** When run, the script should emit a burst of 10 particles every 10 seconds, clearing before each new emission. ### Method: ParticleEmitter:Emit **Signature:** `ParticleEmitter:Emit(particleCount?: int): ()` The **Emit** method will cause the [ParticleEmitter](/docs/reference/engine/classes/ParticleEmitter.md) to instantly emit the given number of particles. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `particleCount` | `int` | `16` | The number of particles to emit. | **Returns:** `()` **Emit Particles Over Distance** This code sample causes a parent `ParticleEmitter` to [ParticleEmitter:Emit()](/docs/reference/engine/classes/ParticleEmitter.md) particles based on how far the parent `BasePart` moves. ```lua local RunService = game:GetService("RunService") local emitter = script.Parent local part = emitter.Parent local PARTICLES_PER_STUD = 3 local lastPosition = part.Position local distance = 0 local function onStep() local displacement = part.Position - lastPosition distance = distance + displacement.magnitude local n = math.floor(distance * PARTICLES_PER_STUD) emitter:Emit(n) distance = distance - n / PARTICLES_PER_STUD lastPosition = part.Position end RunService.Stepped:Connect(onStep) emitter.Enabled = false ``` **Expected output:** While the script is running, moving the part should cause 3 particles to be emit per stud that the part moves. **ParticleEmitter Burst** This code sample causes a `ParticleEmitter` to [ParticleEmitter:Emit()](/docs/reference/engine/classes/ParticleEmitter.md) particles in bursts of 10 every 2 seconds. It [ParticleEmitter:Clear()](/docs/reference/engine/classes/ParticleEmitter.md)s any existing particles before doing so. ```lua local emitter = script.Parent while true do emitter:Clear() emitter:Emit(10) task.wait(2) end ``` **Expected output:** When run, the script should emit a burst of 10 particles every 10 seconds, clearing before each new emission. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: PatchBundlerFileWatch last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated --- # Class: PatchBundlerFileWatch ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: PatchMapping last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated --- # Class: PatchMapping ## Properties ### Property: PatchMapping.FlattenTree ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: PatchMapping.PatchId ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: PatchMapping.TargetPath ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Path last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated summary: "Stores the result of paths created by PathfindingService:CreatePath()." --- # Class: Path > Stores the result of paths created by [PathfindingService:CreatePath()](/docs/reference/engine/classes/PathfindingService.md). ## Description `Path` objects store the result of paths created by [PathfindingService:CreatePath()](/docs/reference/engine/classes/PathfindingService.md). Once a `Path` object is created, you can call [ComputeAsync()](/docs/reference/engine/classes/Path.md) with a starting point and ending point. This will attempt to compute a valid path for a character to move along, based on default or custom parameters passed to [CreatePath()](/docs/reference/engine/classes/PathfindingService.md). If [ComputeAsync()](/docs/reference/engine/classes/Path.md) successfully finds a path, the `Path` object will have a [Status](/docs/reference/engine/classes/Path.md) value of [PathStatus.Success](/docs/reference/engine/enums/PathStatus.md). Otherwise the status will be [PathStatus.NoPath](/docs/reference/engine/enums/PathStatus.md) which can occur if there are obstacles between the two points (and no way around) or if the points are inside of solid objects. In addition to [ComputeAsync()](/docs/reference/engine/classes/Path.md), `Path` objects have the [GetWaypoints()](/docs/reference/engine/classes/Path.md) method which returns a list of waypoints representing the points a character should follow in sequence to get from the beginning to the end of the path. Finally, `Path` objects can be **connected** to the [Blocked](/docs/reference/engine/classes/Path.md) event which will fire if, at any time during the path's existence, the path is blocked. Note that this can occur **behind** a character moving along the path, not just in front of it. See [Pathfinding](/docs/en-us/characters/pathfinding.md) for details on implementing character pathfinding. ## Properties ### Property: Path.Status ```json { "type": "PathStatus", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` The [PathStatus](/docs/reference/engine/enums/PathStatus.md) of the generated `Path`. ## Methods ### Method: Path:CheckOcclusionAsync **Signature:** `Path:CheckOcclusionAsync(start: int): int` This function checks if a path is blocked starting at the waypoint indicated by **start**. It returns the first waypoint of occlusion if blocked, -1 if not. it returns an error if **start** is less than 0 or greater than the number of waypoints in the `Path`. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `start` | `int` | | | **Returns:** `int` ### Method: Path:ComputeAsync **Signature:** `Path:ComputeAsync(start: Vector3, finish: Vector3): ()` This function computes a `Path` from a start position to an end position. This function is not automatically called when a path is created and must be invoked each time the path needs to be updated. Once the path is computed, it will have a series of waypoints that, when followed, can lead a character along the path. These points are gathered with the [GetWaypoints()](/docs/reference/engine/classes/Path.md) function. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `start` | `Vector3` | | The world position where the computed path begins. | | `finish` | `Vector3` | | The world position where the computed path finishes. | **Returns:** `()` ### Method: Path:GetWaypoints **Signature:** `Path:GetWaypoints(): Array` This method returns an array of all the [PathWaypoints](/docs/reference/engine/datatypes/PathWaypoint.md) in a `Path` as computed by [ComputeAsync()](/docs/reference/engine/classes/Path.md). Each waypoint in the array specifies a [Vector3](/docs/reference/engine/datatypes/Vector3.md) position and [PathWaypointAction](/docs/reference/engine/enums/PathWaypointAction.md) to take when the waypoint is reached. The array is arranged in the order of waypoints from the path start to path end. If a path could not be computed, this function will return an empty array. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Returns:** `Array` — An array of [PathWaypoints](/docs/reference/engine/datatypes/PathWaypoint.md) ordered from path start to path end. ### Method: Path:GetPointCoordinates **Signature:** `Path:GetPointCoordinates(): Array` > **Deprecated:** This item has been superseded by [GetWaypoints()](/docs/reference/engine/classes/Path.md) which should be used in all new work instead. This function returns a table of `Path` instances. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Returns:** `Array` ## Events ### Event: Path.Blocked **Signature:** `Path.Blocked(blockedWaypointIdx: int)` Fires when the computed path becomes blocked. Note that paths may become blocked somewhere **behind** the agent, such as a pile of rubble falling on a path as the agent runs away. See [blocked paths](/docs/en-us/characters/pathfinding.md#blocked-paths) for details on checking the forward waypoint progress of an agent along a path. *Security: None · Capabilities: Basic* **Parameters:** | Name | Type | Description | |------|------|-------------| | `blockedWaypointIdx` | `int` | | ### Event: Path.Unblocked **Signature:** `Path.Unblocked(unblockedWaypointIdx: int)` Fires when a computed path that was blocked becomes unblocked. Note that a blocked path may become unblocked somewhere **behind** the agent, effectively making reaction to this event unnecessary. See [blocked paths](/docs/en-us/characters/pathfinding.md#blocked-paths) for details on checking the forward waypoint progress of an agent along a path. *Security: None · Capabilities: Basic* **Parameters:** | Name | Type | Description | |------|------|-------------| | `unblockedWaypointIdx` | `int` | | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Path2D last_updated: 2026-06-29T19:34:07Z inherits: - GuiBase - Instance - Object type: class memory_category: Instances --- # Class: Path2D ## Description `Path2D` is a UI instance that represents a 2D spline. It can be placed under any [GuiObject](/docs/reference/engine/classes/GuiObject.md) and be edited directly in the viewport using built‑in tooling. `Path2D` stores control points and exposes methods to sample positions along the curve, enabling curved UI layouts, path‑based animations, and interactive tools like graph editors and visual effects. See the [2D paths](/docs/en-us/ui/2D-paths.md) guide for more information and usage examples. ## Properties ### Property: Path2D.Closed ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` Connects the first and last control points when enabled. ### Property: Path2D.Color3 ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` Determines the color of the [Path2D](/docs/reference/engine/classes/Path2D.md). ### Property: Path2D.SelectedControlPoint ```json { "type": "int", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Control Points", "capabilities": [ "UI" ] } ``` Index of the currently selected [Path2DControlPoint](/docs/reference/engine/datatypes/Path2DControlPoint.md). ### Property: Path2D.SelectedControlPointData ```json { "type": "Path2DControlPoint", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Control Points", "capabilities": [ "UI" ] } ``` [Path2DControlPoint](/docs/reference/engine/datatypes/Path2DControlPoint.md) data type representing the currently selected control point, if any. ### Property: Path2D.Thickness ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` Determines how thick the [Path2D](/docs/reference/engine/classes/Path2D.md) path is. ### Property: Path2D.Visible ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` Determines if the [Path2D](/docs/reference/engine/classes/Path2D.md) path is rendered or not. When `false`, the path will not render. However, any modifications to the control points will update correctly, ensuring that querying data will have the correct info. ### Property: Path2D.ZIndex ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` Determines the order in which a [Path2D](/docs/reference/engine/classes/Path2D.md) path renders relative to other GUIs. Works the same as [GuiObject.ZIndex](/docs/reference/engine/classes/GuiObject.md) but does not interact with layout order in any way. ## Methods ### Method: Path2D:GetBoundingRect **Signature:** `Path2D:GetBoundingRect(): Rect` Returns the [Rect](/docs/reference/engine/datatypes/Rect.md) bounding size for the [Path2D](/docs/reference/engine/classes/Path2D.md). This is computed based on the control points and is not modifiable outside of changing the control point data. *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Returns:** `Rect` ### Method: Path2D:GetControlPoint **Signature:** `Path2D:GetControlPoint(index: int): Path2DControlPoint` Returns the [Path2DControlPoint](/docs/reference/engine/datatypes/Path2DControlPoint.md) for a given index. If the index is out of bounds, this method will throw an error. *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `index` | `int` | | | **Returns:** `Path2DControlPoint` — The control point at the given index. ### Method: Path2D:GetControlPoints **Signature:** `Path2D:GetControlPoints(): Array` Returns a table of all the [Path2DControlPoints](/docs/reference/engine/datatypes/Path2DControlPoint.md) for the [Path2D](/docs/reference/engine/classes/Path2D.md). *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Returns:** `Array` — Table of all the [Path2DControlPoints](/docs/reference/engine/datatypes/Path2DControlPoint.md). ### Method: Path2D:GetLength **Signature:** `Path2D:GetLength(): float` Returns the length of the [Path2D](/docs/reference/engine/classes/Path2D.md). This function can be expensive if called too frequently. *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Returns:** `float` ### Method: Path2D:GetMaxControlPoints **Signature:** `Path2D:GetMaxControlPoints(): int` Returns the maximum allowed number of control points. *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Returns:** `int` ### Method: Path2D:GetPositionOnCurve **Signature:** `Path2D:GetPositionOnCurve(t: float): UDim2` Returns the 2D [UDim2](/docs/reference/engine/datatypes/UDim2.md) position at a given `t` value between `0` and `1` (inclusive), representing the parameter space result of querying the spline. The values will be more tightly packed near bends and wider apart in straighter segments; see [GetPositionOnCurveArcLength()](/docs/reference/engine/classes/Path2D.md) for even spacing results. Throws an error if the [Path2D](/docs/reference/engine/classes/Path2D.md) has less than two control points. *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `t` | `float` | | The value to query the [Path2D](/docs/reference/engine/classes/Path2D.md) at. | **Returns:** `UDim2` — The position in parameter space. ### Method: Path2D:GetPositionOnCurveArcLength **Signature:** `Path2D:GetPositionOnCurveArcLength(t: float): UDim2` Returns the 2D [UDim2](/docs/reference/engine/datatypes/UDim2.md) position at a given `t` value between `0` and `1` (inclusive), representing the arc length space result of querying the spline. The values will be evenly spaced along the spline; see [GetPositionOnCurve()](/docs/reference/engine/classes/Path2D.md) for parameter spacing results. Throws an error if the [Path2D](/docs/reference/engine/classes/Path2D.md) has less than two control points. *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `t` | `float` | | The value to query the Path2D at. | **Returns:** `UDim2` — The position in arc length space. ### Method: Path2D:GetTangentOnCurve **Signature:** `Path2D:GetTangentOnCurve(t: float): Vector2` Returns the tangent at a given `t` value in parameter space where `t` is a value between `0` and `1` (inclusive). Throws an error if the [Path2D](/docs/reference/engine/classes/Path2D.md) has less than two control points. *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `t` | `float` | | The value to query the [Path2D](/docs/reference/engine/classes/Path2D.md) at. | **Returns:** `Vector2` ### Method: Path2D:GetTangentOnCurveArcLength **Signature:** `Path2D:GetTangentOnCurveArcLength(t: float): Vector2` Returns the tangent at a given `t` value in arc length space where `t` is a value between `0` and `1` (inclusive). Throws an error if the [Path2D](/docs/reference/engine/classes/Path2D.md) has less than two control points. *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `t` | `float` | | The value to query the [Path2D](/docs/reference/engine/classes/Path2D.md) at. | **Returns:** `Vector2` — The tangent in arc length space. ### Method: Path2D:InsertControlPoint **Signature:** `Path2D:InsertControlPoint(index: int, point: Path2DControlPoint): ()` Inserts a new [Path2DControlPoint](/docs/reference/engine/datatypes/Path2DControlPoint.md) at a given index. Throws a warning if the index is out of bounds or if you're trying to add control points past the limit of 50. *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `index` | `int` | | The index to insert at. | | `point` | `Path2DControlPoint` | | The control point to insert. | **Returns:** `()` ### Method: Path2D:RemoveControlPoint **Signature:** `Path2D:RemoveControlPoint(index: int): ()` Removes a control point at the given index. Throws a warning if the index is out of bounds. *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `index` | `int` | | The index to remove at. | **Returns:** `()` ### Method: Path2D:SetControlPoints **Signature:** `Path2D:SetControlPoints(controlPoints: Array): ()` Sets all the control points to the specified array, replacing all existing points with new ones. Throws a warning if there are more than 50 points in the `controlPoints` array. *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `controlPoints` | `Array` | | The new list of control points to set. | **Returns:** `()` ### Method: Path2D:UpdateControlPoint **Signature:** `Path2D:UpdateControlPoint(index: int, point: Path2DControlPoint): ()` Updates the control point at the given index. Throws a warning if the index is out of range. *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `index` | `int` | | The control point index to update. | | `point` | `Path2DControlPoint` | | | **Returns:** `()` ## Events ### Event: Path2D.ControlPointChanged **Signature:** `Path2D.ControlPointChanged()` Fires any time control points change. *Security: None · Capabilities: UI* ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: PathfindingLink last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances summary: "Connects two locations which otherwise by default are unconnected." --- # Class: PathfindingLink > Connects two locations which otherwise by default are unconnected. ## Description `PathfindingLink` connects two locations which otherwise by default are unconnected. See [Pathfinding](/docs/en-us/characters/pathfinding.md#pathfinding-links) for details. ## Properties ### Property: PathfindingLink.Attachment0 ```json { "type": "Attachment", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` The originating attachment of the link. ### Property: PathfindingLink.Attachment1 ```json { "type": "Attachment", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` The landing attachment of the link. ### Property: PathfindingLink.IsBidirectional ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` Enables a path to traverse a link in both directions. The default value is `true`. ### Property: PathfindingLink.Label ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` A classifying string to add additional information about the link. This label is included in the waypoint generated by this link. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: PathfindingModifier last_updated: 2026-06-29T19:34:07Z inherits: - Instance - Object type: class memory_category: Instances summary: "Modifiers used to represent space that has a higher or lower cost to be traversed when creating paths using PathfindingService." --- # Class: PathfindingModifier > Modifiers used to represent space that has a higher or lower cost to be > traversed when creating paths using [PathfindingService](/docs/reference/engine/classes/PathfindingService.md). ## Description Pathfinding modifiers can be used to represent space that has a higher or lower cost to be traversed. You can include pathfinding modifiers in the [PathfindingService:CreatePath()](/docs/reference/engine/classes/PathfindingService.md) parameters and compute smarter paths across various materials or around defined regions. See [Pathfinding](/docs/en-us/characters/pathfinding.md#pathfinding-modifiers) for details. ## Properties ### Property: PathfindingModifier.Label ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` The name of the navigation area inside or on top of the parts enclosed by the modifier. ### Property: PathfindingModifier.PassThrough ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` Determines if the parts enclosed by the modifier are traversable, even if they would normally be collided with. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: PathfindingService last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated summary: "Used to find logical paths between two points." --- # Class: PathfindingService > Used to find logical paths between two points. ## Description `PathfindingService` is used to find logical paths between two points, ensuring that characters can move between the points without running into walls or other obstacles. By default, the shortest path is calculated, but you can implement pathfinding modifiers to compute smarter paths across various materials, around defined regions, or through obstacles. See [Pathfinding](/docs/en-us/characters/pathfinding.md) for details on implementing character pathfinding. ## Properties ### Property: PathfindingService.EmptyCutoff ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` > **Deprecated:** This property is deprecated, since the legacy pathfinding system using it has since been removed. Do not use it for new work. This function worked with the legacy pathfinding system. The pathfinding system currently uses a navigation grid and the EmptyCutoff is unused. When the [PathfindingService](/docs/reference/engine/classes/PathfindingService.md) computes a path using [PathfindingService:ComputeRawPathAsync()](/docs/reference/engine/classes/PathfindingService.md) or [PathfindingService:ComputeRawPathAsync()](/docs/reference/engine/classes/PathfindingService.md) it uses the voxel representation of the world. A voxel is one cube in a grid overlaid on the world. In this case the voxels being used are 4x4x4. This property sets the percent of a voxel has to be occupied to be considered empty. Defaults to 0.16. ## Methods ### Method: PathfindingService:CreatePath **Signature:** `PathfindingService:CreatePath(agentParameters?: Dictionary): Path` Creates a [Path](/docs/reference/engine/classes/Path.md) object based on various agent parameters. Valid keys and values in the `agentParameters` table are as follows: | Key | Type | Default | Description | | --- | --- | --- | --- | | `AgentRadius` | integer | `2` | Determines the minimum amount of horizontal space required for empty space to be considered traversable. | | `AgentHeight` | integer | `5` | Determines the minimum amount of vertical space required for empty space to be considered traversable. | | `AgentCanJump` | boolean | `true` | Determines whether jumping during pathfinding is allowed. | | `AgentCanClimb` | boolean | `false` | Determines whether climbing [TrussParts](/docs/reference/engine/classes/TrussPart\.md) during pathfinding is allowed. | | `WaypointSpacing` | number | 4 | Determines the spacing between intermediate waypoints in path. | | `Costs` | table | `{}` | Table of materials or defined [PathfindingModifiers](/docs/reference/engine/classes/PathfindingModifier\.md) and their "cost" for traversal. Useful for making the agent prefer certain materials/regions over others. | *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `agentParameters` | `Dictionary` | `nil` | Lua table which lets you fine-tune the path based on various agent parameters. | **Returns:** `Path` — A [Path](/docs/reference/engine/classes/Path.md) object. ### Method: PathfindingService:FindPathAsync **Signature:** `PathfindingService:FindPathAsync(start: Vector3, finish: Vector3): Path` This function is used to find a [Path](/docs/reference/engine/classes/Path.md) between two provided points. This path uses the navigation grid created by [PathfindingService](/docs/reference/engine/classes/PathfindingService.md) and makes sure that the path can be followed by a regular-sized Roblox character. This function returns a [Path](/docs/reference/engine/classes/Path.md) object which contains the coordinates of the path. If no path is found between the two points, this function will still return a [Path](/docs/reference/engine/classes/Path.md) object, but that object's [Path.Status](/docs/reference/engine/classes/Path.md) will be [PathStatus.NoPath](/docs/reference/engine/enums/PathStatus.md). To get the waypoints of a [Path](/docs/reference/engine/classes/Path.md) object, you can use the [Path:GetWaypoints()](/docs/reference/engine/classes/Path.md) function. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `start` | `Vector3` | | Path start coordinates. | | `finish` | `Vector3` | | Path finish coordinates. | **Returns:** `Path` — A [Path](/docs/reference/engine/classes/Path.md) object. ### Method: PathfindingService:ComputeRawPathAsync **Signature:** `PathfindingService:ComputeRawPathAsync(start: Vector3, finish: Vector3, maxDistance: float): Path` > **Deprecated:** This item has been superseded by [PathfindingService:FindPathAsync()](/docs/reference/engine/classes/PathfindingService.md) which should be used in all new work instead. This function computes and returns a [Path](/docs/reference/engine/classes/Path.md) between two [Vector3s](/docs/reference/engine/datatypes/Vector3.md). If the given MaxDistance is greater than 512, an error will be thrown. (MaxDistance is too large). *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `start` | `Vector3` | | | | `finish` | `Vector3` | | | | `maxDistance` | `float` | | | **Returns:** `Path` — A [Path](/docs/reference/engine/classes/Path.md) object. ### Method: PathfindingService:ComputeSmoothPathAsync **Signature:** `PathfindingService:ComputeSmoothPathAsync(start: Vector3, finish: Vector3, maxDistance: float): Path` > **Deprecated:** This item has been superseded by [PathfindingService:FindPathAsync()](/docs/reference/engine/classes/PathfindingService.md) which should be used in all new work instead. This function computes and returns a smooth [Path](/docs/reference/engine/classes/Path.md) between two [Vector3s](/docs/reference/engine/datatypes/Vector3.md). This function fulfills the same purpose as [PathfindingService:ComputeRawPathAsync()](/docs/reference/engine/classes/PathfindingService.md), but creates a much smoother path for an NPC to follow in comparison. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `start` | `Vector3` | | | | `finish` | `Vector3` | | | | `maxDistance` | `float` | | | **Returns:** `Path` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: PermissionsService last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service --- # Class: PermissionsService ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: PhysicsService last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service --- # Class: PhysicsService ## Description [PhysicsService](/docs/reference/engine/classes/PhysicsService.md) primarily contains methods for working with **collision groups** which define whether a set of parts may or may not collide with parts in other collision groups. You can register a collision group through [RegisterCollisionGroup()](/docs/reference/engine/classes/PhysicsService.md) and assign parts to it by setting those parts' [CollisionGroup](/docs/reference/engine/classes/BasePart.md) property to the **name** of the collision group. Creating, deleting, and modifying collision relationships between collision groups is limited to server-side [Scripts](/docs/reference/engine/classes/Script.md). See [Collision Filtering](/docs/en-us/workspace/collisions.md#collision-filtering) for usage details in Studio and within scripts. ## Methods ### Method: PhysicsService:CollisionGroupsAreCollidable **Signature:** `PhysicsService:CollisionGroupsAreCollidable(name1: string, name2: string): boolean` Returns whether the two specified collision groups will collide. This method will also return true if either of the groups are unregistered, as the default collision mask collides with all groups. *Security: None · Thread Safety: Unsafe · Capabilities: Physics · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name1` | `string` | | | | `name2` | `string` | | | **Returns:** `boolean` ### Method: PhysicsService:CollisionGroupSetCollidable **Signature:** `PhysicsService:CollisionGroupSetCollidable(name1: string, name2: string, collidable: boolean): ()` Sets the collision status between two groups. This method will throw an error if either of the groups is unregistered, so it's recommended that you confirm each group's registration through [PhysicsService:IsCollisionGroupRegistered()](/docs/reference/engine/classes/PhysicsService.md) before making this call. *Security: None · Thread Safety: Unsafe · Capabilities: Physics · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name1` | `string` | | | | `name2` | `string` | | | | `collidable` | `boolean` | | | **Returns:** `()` ### Method: PhysicsService:GetMaxCollisionGroups **Signature:** `PhysicsService:GetMaxCollisionGroups(): int` Returns the maximum number of collision groups the engine supports. This value is currently 32. *Security: None · Thread Safety: Unsafe · Capabilities: Physics · Simulation Access: true* **Returns:** `int` ### Method: PhysicsService:GetRegisteredCollisionGroups **Signature:** `PhysicsService:GetRegisteredCollisionGroups(): Array` Returns a table with info on all of the place's collision groups. Each value in the returned table is itself a table and containing two members: | Member | Type | Description | | --- | --- | --- | | mask | integer | The collision group's mask; only for internal use. | | name | string | Name of the collision group. | *Security: None · Thread Safety: Unsafe · Capabilities: Physics · Simulation Access: true* **Returns:** `Array` ### Method: PhysicsService:IsCollisionGroupRegistered **Signature:** `PhysicsService:IsCollisionGroupRegistered(name: string): boolean` Checks if a collision group is registered. It's recommended that you call this method before calling methods that throw errors for unregistered collision groups, such as [PhysicsService:CollisionGroupSetCollidable()](/docs/reference/engine/classes/PhysicsService.md). *Security: None · Thread Safety: Unsafe · Capabilities: Physics · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name` | `string` | | | **Returns:** `boolean` ### Method: PhysicsService:RegisterCollisionGroup **Signature:** `PhysicsService:RegisterCollisionGroup(name: string): ()` Registers a new collision group with the given name. The name cannot be `"Default"`. Note that this method has a slight performance overhead based on the number of [BaseParts](/docs/reference/engine/classes/BasePart.md) in the workspace, so it's recommended that you register all collision groups at edit time through the Studio [editor](/docs/en-us/workspace/collisions.md#collision-groups) and call [UnregisterCollisionGroup()](/docs/reference/engine/classes/PhysicsService.md) and [RenameCollisionGroup()](/docs/reference/engine/classes/PhysicsService.md) as infrequently as possible. *Security: None · Thread Safety: Unsafe · Capabilities: Physics · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name` | `string` | | | **Returns:** `()` **PhysicsService:RegisterCollisionGroup** This example demonstrates one basic use of collision groups. It assigns **BallPart** to `"CollisionGroupBall"` and **DoorPart** to `"CollisionGroupDoor"`, then makes the two groups non-collidable using [PhysicsService:CollisionGroupSetCollidable()](/docs/reference/engine/classes/PhysicsService.md). ```lua local PhysicsService = game:GetService("PhysicsService") local collisionGroupBall = "CollisionGroupBall" local collisionGroupDoor = "CollisionGroupDoor" -- Register collision groups PhysicsService:RegisterCollisionGroup(collisionGroupBall) PhysicsService:RegisterCollisionGroup(collisionGroupDoor) -- Assign parts to collision groups script.Parent.BallPart.CollisionGroup = collisionGroupBall script.Parent.DoorPart.CollisionGroup = collisionGroupDoor -- Set groups as non-collidable with each other and check the result PhysicsService:CollisionGroupSetCollidable(collisionGroupBall, collisionGroupDoor, false) print(PhysicsService:CollisionGroupsAreCollidable(collisionGroupBall, collisionGroupDoor)) --> false ``` ### Method: PhysicsService:RenameCollisionGroup **Signature:** `PhysicsService:RenameCollisionGroup(from: string, to: string): ()` Renames the specified registered collision group, but does **not** rename the [CollisionGroup](/docs/reference/engine/classes/BasePart.md) property of parts that utilize the group. The first argument of this method is the name of the group to rename, the second argument is the new name for the group. If the specified group does not exist, this method will not do anything. The naming conventions for the new name follow the same rules as if the group was being created with [RegisterCollisionGroup()](/docs/reference/engine/classes/PhysicsService.md). This method will throw a runtime error in the following circumstances: - Invalid or empty name provided for either argument. - The method is called from a client. Note that this method has a slight performance overhead based on the number of [BaseParts](/docs/reference/engine/classes/BasePart.md) in the workspace, so it's recommended that you register all collision groups at edit time through the Studio [editor](/docs/en-us/workspace/collisions.md#collision-groups) and rename them as infrequently as possible. *Security: None · Thread Safety: Unsafe · Capabilities: Physics · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `from` | `string` | | | | `to` | `string` | | | **Returns:** `()` ### Method: PhysicsService:UnregisterCollisionGroup **Signature:** `PhysicsService:UnregisterCollisionGroup(name: string): ()` Unregisters the collision group for the given name, with the following behaviors: - If an invalid name is provided, the method will not do anything. - If the reserved name `"Default"` is provided **or** if the method is called from a client, it will throw an error. - If there are any parts in the collision group when it is removed, those parts will still maintain the same collision group name. The physical behavior of parts in a removed group is undefined, so it's recommended to move any parts in a removed group to another group, such as the `"Default"` group. Note that this method has a slight performance overhead based on the number of [BaseParts](/docs/reference/engine/classes/BasePart.md) in the workspace, so it's recommended that you register all collision groups at edit time through the Studio [editor](/docs/en-us/workspace/collisions.md#collision-groups) and call this method as infrequently as possible. *Security: None · Thread Safety: Unsafe · Capabilities: Physics · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name` | `string` | | | **Returns:** `()` ### Method: PhysicsService:CollisionGroupContainsPart **Signature:** `PhysicsService:CollisionGroupContainsPart(name: string, part: BasePart): boolean` > **Deprecated:** This method has been deprecated. It's recommended that you query a part's collision group through its [CollisionGroup](/docs/reference/engine/classes/BasePart.md) property. Returns whether the specified part is in the specified collision group. This method will throw a runtime error in the following circumstances: - The specified group does not exist. - The specified part is not a [BasePart](/docs/reference/engine/classes/BasePart.md). *Security: None · Thread Safety: Unsafe · Capabilities: Physics* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name` | `string` | | | | `part` | `BasePart` | | | **Returns:** `boolean` ### Method: PhysicsService:CreateCollisionGroup **Signature:** `PhysicsService:CreateCollisionGroup(name: string): int` > **Deprecated:** This method has been superseded by [RegisterCollisionGroup()](/docs/reference/engine/classes/PhysicsService.md) which should be used for all new work. Creates a new collision group with the given name, and returns the ID of the created group. *Security: None · Thread Safety: Unsafe · Capabilities: Physics* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name` | `string` | | | **Returns:** `int` ### Method: PhysicsService:GetCollisionGroupId **Signature:** `PhysicsService:GetCollisionGroupId(name: string): int` > **Deprecated:** This method has been deprecated. It's recommended that you query collision groups by **name** through a part's [CollisionGroup](/docs/reference/engine/classes/BasePart.md) property. This method returns the ID of the collision group with the specified name. It will throw an error if no group with the given name exists. *Security: None · Thread Safety: Unsafe · Capabilities: Physics* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name` | `string` | | The name of the collision group being retrieved. | **Returns:** `int` — The ID of the retrieved collision group, or `nil` if no such group exists. ### Method: PhysicsService:GetCollisionGroupName **Signature:** `PhysicsService:GetCollisionGroupName(name: int): string` > **Deprecated:** This method has been deprecated. It's recommended that you query collision groups by **name** through a part's [CollisionGroup](/docs/reference/engine/classes/BasePart.md) property. Returns the name of the collision group with the corresponding ID. This method will return `nil` if the group with the corresponding ID has not been named. *Security: None · Thread Safety: Unsafe · Capabilities: Physics* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name` | `int` | | | **Returns:** `string` ### Method: PhysicsService:GetCollisionGroups **Signature:** `PhysicsService:GetCollisionGroups(): Array` > **Deprecated:** This method has been superseded by [GetRegisteredCollisionGroups()](/docs/reference/engine/classes/PhysicsService.md) which should be used for all new work. Returns a table with info on all of the place's collision groups. Each value in the returned table is itself a table containing three members: | Member | Type | Description | | --- | --- | --- | | id | integer | The ID of the group. | | mask | integer | The collision group's mask; only for internal use. | | name | string | The name of the collision group. | *Security: None · Thread Safety: Unsafe · Capabilities: Physics* **Returns:** `Array` ### Method: PhysicsService:RemoveCollisionGroup **Signature:** `PhysicsService:RemoveCollisionGroup(name: string): ()` > **Deprecated:** This method has been superseded by [UnregisterCollisionGroup()](/docs/reference/engine/classes/PhysicsService.md) which should be used for all new work. Removes the collision group with the given name. If an invalid name is provided, this method will not do anything, although if the reserved name `"Default"` is provided, it will throw an error. If there are any parts in the collision group when it is removed, these parts will still maintain the same collision group ID. The physical behavior of parts in a removed group is undefined, so it is recommended to move any parts in a removed group to another group. This method will throw a runtime error in the following circumstances: - The name `"Default"` is provided. - The method is called from a client. *Security: None · Thread Safety: Unsafe · Capabilities: Physics* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name` | `string` | | | **Returns:** `()` ### Method: PhysicsService:SetPartCollisionGroup **Signature:** `PhysicsService:SetPartCollisionGroup(part: BasePart, name: string): ()` > **Deprecated:** This method has been deprecated. It's recommended that you set a part's collision group by **name** through its [CollisionGroup](/docs/reference/engine/classes/BasePart.md) property. This method sets the collision group of the specified part to the group with the specified name. It is equivalent to setting the [BasePart.CollisionGroupId](/docs/reference/engine/classes/BasePart.md), although calling this method is the recommended approach. This method will throw a runtime error in the following circumstances: - The part parameter is not a [BasePart](/docs/reference/engine/classes/BasePart.md) instance. - The specified group does not exist. *Security: None · Thread Safety: Unsafe · Capabilities: Physics* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `part` | `BasePart` | | The part being set. | | `name` | `string` | | The name of collision group that the part's collision group is being set to. | **Returns:** `()` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: PhysicsSettings last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated --- # Class: PhysicsSettings ## Description The PhysicsSettings is a singleton class, which lets you view debugging features in Roblox's physics engine. You can find it under the Physics tab in Studio's settings menu. ## Properties ### Property: PhysicsSettings.AllowSleep ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Performance" } ``` When set to true, physically simulated objects will stop being simulated if they have little to no motion for a set period of time. ### Property: PhysicsSettings.AreAnchorsShown ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Display" } ``` When set to true, parts that are [BasePart.Anchored](/docs/reference/engine/classes/BasePart.md) will show a gray surface outline on the surface of the part's bounding box that is currently facing the ground. ### Property: PhysicsSettings.AreAssembliesShown ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Display" } ``` When set to true, each physics assembly is assigned a unique color and the [Part](/docs/reference/engine/classes/Part.md) associated with the assembly are outlined with the color. Parts that are attached together by [JointInstance](/docs/reference/engine/classes/JointInstance.md) will share the same color. ### Property: PhysicsSettings.AreAssemblyCentersOfMassShown ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Display" } ``` ### Property: PhysicsSettings.AreAwakePartsHighlighted ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Display" } ``` When set to true, parts that are actively being physically simulated will have a red outline. ### Property: PhysicsSettings.AreBodyTypesShown ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Display" } ``` When set to true, [Part](/docs/reference/engine/classes/Part.md) will be outlined with a specific color, depending on the state of its root simulation body. #### Body Types | Color | Body Type | Description | | --- | --- | --- | | | Real-Time Body | Physics Body that is always simulated in real time, and is never throttled. Used for Humanoids. | | | Free-Fall Body | Physics Body that is freely moving with no physical contact. | | | Joint Body | Physics Body that is being influenced by a physically simulated joint, such as a Motor or a Hinge. | | | Contact Body | Physics Body that is in contact with another physics body. | | | Symmetric Contact Body | Physics Body that is experiencing a torquing force, while in contact with another body. | | | Vertical Contact Body | Physics Body that is moving very little along the Y plane, while in contact with another body. | ### Property: PhysicsSettings.AreCollisionCostsShown ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Display" } ``` ### Property: PhysicsSettings.AreConstraintForcesShownForSelectedOrHoveredInstances ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Display" } ``` ### Property: PhysicsSettings.AreConstraintTorquesShownForSelectedOrHoveredInstances ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Display" } ``` ### Property: PhysicsSettings.AreContactForcesShownForSelectedOrHoveredAssemblies ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Display" } ``` ### Property: PhysicsSettings.AreContactIslandsShown ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Display" } ``` When set to true, each contact island will render [SelectionBox](/docs/reference/engine/classes/SelectionBox.md) adorns on the parts in contact islands, where each contact island is assigned a random color. ### Property: PhysicsSettings.AreContactPointsShown ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Display" } ``` When set to true, sphere adorns will be drawn at the contact points of each part where physics interactions are occurring. Each sphere also has an arrow drawn in 3D, facing the surface that the contact point is detecting. #### Solver Variations The behavior of this property varies depending on whether Roblox's physics engine is using the `PGS Physics Solver`, or the `Spring Physics Solver`. This is controlled by the [Workspace.PGSPhysicsSolverEnabled](/docs/reference/engine/classes/Workspace.md) property. ##### Spring Physics Solver When [Workspace.PGSPhysicsSolverEnabled](/docs/reference/engine/classes/Workspace.md) is set to false, the contact points are color coded as listed below. The length of the arrow extruding from the sphere depends on how much force the contact point is exerting, and what the contact type is. | Color | Contact Type | Description | | --- | --- | --- | | | Normal Contact | Contact point with no special conditions. | | | Resting Contact | Contact point that has been active for at least 4 frames. | | | Second Pass Contact | Contact point that was made by a kernel joint going through a second pass. Rarely seen. | | | Real-Time Contact | Contact point that was made with a real-time physics body. This applies to tripped [Humanoid](/docs/reference/engine/classes/Humanoid.md). | | | Joint Contact | Contact point that was made under the context of a physically simulated joint. This applies to Motors and Hinges. | PGS Physics Solver When [Workspace.PGSPhysicsSolverEnabled](/docs/reference/engine/classes/Workspace.md) is set to true, the contact points are always colored **RED**, and the length of the arrow will always be 1 stud. There are no special conditions tracked, because the PGS solver does not keep specific lookup tables for the states listed in the Spring Solver. | Color | Contact Type | Description | | --- | --- | --- | | | Normal Contact | Contact point with no special conditions. | ### Property: PhysicsSettings.AreGravityForcesShownForSelectedOrHoveredAssemblies ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Display" } ``` ### Property: PhysicsSettings.AreJointCoordinatesShown ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Display" } ``` When set to true, XYZ axes are rendered at the [BasePart.CFrame](/docs/reference/engine/classes/BasePart.md) of every part. ### Property: PhysicsSettings.AreMagnitudesShownForDrawnForcesAndTorques ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Display" } ``` ### Property: PhysicsSettings.AreMechanismsShown ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Display" } ``` When set to true, every individual mechanism of parts is given a unique color. ### Property: PhysicsSettings.AreModelCoordsShown ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Display" } ``` An ancient property that hasn't work correctly since late 2007. It's supposed to render an XYZ axis on the root part of a [Model](/docs/reference/engine/classes/Model.md), but the axis rendering component doesn't work correctly. ### Property: PhysicsSettings.AreNonAnchorsShown ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Display" } ``` ### Property: PhysicsSettings.AreOwnersShown ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Display" } ``` When set to true, each player's character is outlined with a unique color, and each part that the player has network ownership over is outlined with the same color. ### Property: PhysicsSettings.ArePartCoordsShown ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Display" } ``` An ancient property that hasn't worked correctly since late 2007. It's supposed to render a large XYZ axis in the center of each [BasePart](/docs/reference/engine/classes/BasePart.md), but the axis rendering component doesn't work correctly. ### Property: PhysicsSettings.AreRegionsShown ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Display" } ``` When set to true, a cylinder is drawn around each player's character, representing their [Player.SimulationRadius](/docs/reference/engine/classes/Player.md). Each physically simulated object will check to see which player is closest to that object, and if they are within the player's simulation radius. If both conditions are met, that player will becomes the network owner of that object. ### Property: PhysicsSettings.AreSolverIslandsShown ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Display" } ``` ### Property: PhysicsSettings.AreTerrainReplicationRegionsShown ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Display" } ``` ### Property: PhysicsSettings.AreTimestepsShown ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Display" } ``` ### Property: PhysicsSettings.AreUnalignedPartsShown ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Display" } ``` When set to true, parts that aren't aligned on the 1x1x1 grid will be outlined yellow. ### Property: PhysicsSettings.AreWorldCoordsShown ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Display" } ``` An ancient property that hasn't worked correctly since late 2007. It's supposed to render a large XYZ axis in the center of the world, but the axis rendering component doesn't work correctly. ### Property: PhysicsSettings.DisableCSGv2 ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Display" } ``` When set to true, Roblox will fall back to using its legacy CSG solver when performing solid model operations. ### Property: PhysicsSettings.DisableCSGv3ForPlugins ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Display" } ``` ### Property: PhysicsSettings.DrawConstraintsNetForce ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Display" } ``` ### Property: PhysicsSettings.DrawContactsNetForce ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Display" } ``` ### Property: PhysicsSettings.DrawTotalNetForce ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Display" } ``` ### Property: PhysicsSettings.EnableForceVisualizationSmoothing ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Display" } ``` ### Property: PhysicsSettings.FluidForceDrawScale ```json { "type": "float", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Display" } ``` Sets the scale of arrows drawn for aerodynamic force visualization. The default value is 1.0; smaller values draw smaller arrows and vice versa. The default value is a good starting point for a wide range of aerodynamic mechanisms. ### Property: PhysicsSettings.ForceDrawScale ```json { "type": "float", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Display" } ``` ### Property: PhysicsSettings.ForceVisualizationSmoothingSteps ```json { "type": "int", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Display" } ``` ### Property: PhysicsSettings.IsInterpolationThrottleShown ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Display" } ``` ### Property: PhysicsSettings.IsReceiveAgeShown ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Display" } ``` This property is supposed to show the [BasePart.ReceiveAge](/docs/reference/engine/classes/BasePart.md) of a part, but it does not work correctly. ### Property: PhysicsSettings.IsTreeShown ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Display" } ``` When set to true, the joint connections of each part, and the states of their underlying primitive components are visualized as a spanning tree. ##### Spanning Tree Table There are several visualizations made available when this property is set to true: | Color | Adorn Type | Description | | --- | --- | --- | | | Box | Root Primitive of a Mechanism that is currently anchored, or connected to an anchored primitive. See [BasePart:IsGrounded()](/docs/reference/engine/classes/BasePart.md). | | | Box | Root Primitive of a Mechanism that is free to be physically simulated. | | | Box | Root Primitive of a Mechanism that has moving components. | | | Sphere | Root Primitive of an Assembly. | | | Line | Connection between two Primitives that share the same Assembly. | | | Line | Connection between two Primitives. | ### Property: PhysicsSettings.PhysicsEnvironmentalThrottle ```json { "type": "EnviromentalPhysicsThrottle", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Performance" } ``` Controls the throttle rate of Roblox's physics engine. By default, the physics engine will adjust the physics environment throttle depending on how much work the physics engine is doing, and the current framerate. See the enum page for [EnviromentalPhysicsThrottle](/docs/reference/engine/enums/EnviromentalPhysicsThrottle.md) for more information. ### Property: PhysicsSettings.ShowDecompositionGeometry ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Display" } ``` When set to true, the underlying collision geometry for [PartOperation](/docs/reference/engine/classes/PartOperation.md) and [MeshPart](/docs/reference/engine/classes/MeshPart.md) is rendered. ### Property: PhysicsSettings.ShowFluidForcesForSelectedOrHoveredMechanisms ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Display" } ``` When set to true, enables aerodynamic visualization for selected or hovered mechanisms in Studio's play and run modes. This visualization shows aerodynamic force, torque, and center of pressure for the hovered or selected mechanisms. ### Property: PhysicsSettings.ShowInstanceNamesForDrawnForcesAndTorques ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Display" } ``` ### Property: PhysicsSettings.SolverConvergenceMetricType ```json { "type": "SolverConvergenceMetricType", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Display" } ``` ### Property: PhysicsSettings.SolverConvergenceVisualizationMode ```json { "type": "SolverConvergenceVisualizationMode", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Display" } ``` ### Property: PhysicsSettings.ThrottleAdjustTime ```json { "type": "double", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Performance" } ``` If the [PhysicsSettings.PhysicsEnvironmentalThrottle](/docs/reference/engine/classes/PhysicsSettings.md) is set to `DefaultAuto`, this specifies the maximum time that the physics environmental throttle has to wait before it is allowed to automatically change. ### Property: PhysicsSettings.TorqueDrawScale ```json { "type": "float", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Display" } ``` ### Property: PhysicsSettings.UseCSGv2 ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Display" } ``` If set to true, version 2 of Roblox's CSG solver will be used instead of version 1. ### Property: PhysicsSettings.ForceCSGv2 *(hidden)* ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Display" } ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: PitchShiftSoundEffect last_updated: 2026-06-29T19:34:08Z inherits: - SoundEffect - Instance - Object type: class memory_category: Instances summary: "Adjusts the pitch of a sound without changing its playback speed." --- # Class: PitchShiftSoundEffect > Adjusts the pitch of a sound without changing its playback speed. ## Description The PitchShiftSoundEffect raises or lowers the pitch of the associated Sound or SoundGroup without changing the playback speed of the audio. This effect can be computationally expensive. ## Properties ### Property: PitchShiftSoundEffect.Octave ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "LegacySound" ] } ``` Range: 0.5 to 2 (default 1.25) The percentage to shift the original pitch. Setting this to its minimum (0.5) lowers the octave by 1, setting this to its maximum (2) increases the octave by 1. ## Inherited Members ### From [SoundEffect](/docs/reference/engine/classes/SoundEffect.md) - **Property `Enabled`** (`boolean`): Toggles the effect on and off. - **Property `Priority`** (`int`): Determines the order the effect will be applied in relation to other ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: PlacesService last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated --- # Class: PlacesService ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Plane last_updated: 2026-06-29T19:34:08Z inherits: - PlaneConstraint - Constraint - Instance - Object type: class memory_category: BaseParts tags: - Deprecated summary: "Constrains Attachment0 and Attachment1 such that both points lie in a plane with origin at Attachment0's position and unit normal vector equal to Attachment0's primary axis." --- # Class: Plane > Constrains Attachment0 and Attachment1 such that both points lie in a plane > with origin at Attachment0's position and unit normal vector equal to > Attachment0's primary axis. ## Description Constrains Attachment0 and Attachment1 such that both points lie in a plane defined by Attachment0. The plane origin is at Attachment0 and the plane unit normal is the primary axis of Attachment0. This means that Attachment0 and Attachment1 will move to a position/orientation such that the distance between Attachment1 and Attachment0, projected onto the Plane unit normal, is zero. ## Inherited Members ### From [Constraint](/docs/reference/engine/classes/Constraint.md) - **Property `Active`** (`boolean`): Indicates if the constraint is currently active in the world. - **Property `Attachment0`** (`Attachment`): The Attachment that is connected to - **Property `Attachment1`** (`Attachment`): The Attachment that is connected to - **Property `Color`** (`BrickColor`): The color of the constraint. - **Property `Enabled`** (`boolean`): Toggles whether or not the constraint is enabled. - **Property `Visible`** (`boolean`): Toggles the constraint's visibility. - **Method `GetDebugAppliedForce(bodyId: int): Vector3`**: *(deprecated)* - **Method `GetDebugAppliedTorque(bodyId: int): Vector3`**: *(deprecated)* ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: PlaneConstraint last_updated: 2026-06-29T19:34:08Z inherits: - Constraint - Instance - Object type: class memory_category: BaseParts summary: "Moves its attachments into a position/orientation along a plane whose normal vector is the primary axis of Attachment0." --- # Class: PlaneConstraint > Moves its attachments into a position/orientation along a plane whose normal > vector is the primary axis of [Attachment0](/docs/reference/engine/classes/Constraint.md). ## Description A **PlaneConstraint** moves its [Attachment0](/docs/reference/engine/classes/Constraint.md) and [Attachment1](/docs/reference/engine/classes/Constraint.md) into a position/orientation along a plane whose normal vector is the primary axis of [Attachment0](/docs/reference/engine/classes/Constraint.md). Both parent assemblies remain free to translate and rotate unless otherwise constrained. ## Inherited Members ### From [Constraint](/docs/reference/engine/classes/Constraint.md) - **Property `Active`** (`boolean`): Indicates if the constraint is currently active in the world. - **Property `Attachment0`** (`Attachment`): The Attachment that is connected to - **Property `Attachment1`** (`Attachment`): The Attachment that is connected to - **Property `Color`** (`BrickColor`): The color of the constraint. - **Property `Enabled`** (`boolean`): Toggles whether or not the constraint is enabled. - **Property `Visible`** (`boolean`): Toggles the constraint's visibility. - **Method `GetDebugAppliedForce(bodyId: int): Vector3`**: *(deprecated)* - **Method `GetDebugAppliedTorque(bodyId: int): Vector3`**: *(deprecated)* ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Platform last_updated: 2026-06-29T19:34:08Z inherits: - Part - FormFactorPart - BasePart - PVInstance - Instance - Object type: class memory_category: BaseParts tags: - NotCreatable summary: "Historically a form of Seat that wouldn't place the player in a sitting pose. This object is no longer create-able and cannot be used by developers." --- # Class: Platform > Historically a form of [Seat](/docs/reference/engine/classes/Seat.md) that wouldn't place the player in a > sitting pose. This object is no longer create-able and cannot be used by > developers. ## Description The Platform object creates a brick that when touched by a [Player](/docs/reference/engine/classes/Player.md) will anchor their torso to the brick. This allows for the creation of vehicles that players can stand in and not be flung about the cabin/deck of the vehicle. The Platform is almost identical to the [Seat](/docs/reference/engine/classes/Seat.md) object, except that instead of sitting down the player will be standing while locked in place. Good for ships. The Platform object is very useful for making people's characters staying in one spot while they move around, such as a ship or truck. When a player touches the Platform a [Weld](/docs/reference/engine/classes/Weld.md) constraint is created, so they are 'attached' to the Platform and can't move until that weld is broken. It can be removed by hitting the spacebar, when the player jumps to exit the Platform. ## Inherited Members ### From [Part](/docs/reference/engine/classes/Part.md) - **Property `Shape`** (`PartType`): Sets the overall shape of the object. ### From [FormFactorPart](/docs/reference/engine/classes/FormFactorPart.md) - **Property `FormFactor`** (`FormFactor`): *(deprecated)* - **Property `formFactor`** (`FormFactor`): *(deprecated, hidden)* ### From [BasePart](/docs/reference/engine/classes/BasePart.md) - **Property `Anchored`** (`boolean`): Determines whether a part is immovable by physics. - **Property `AssemblyAngularVelocity`** (`Vector3`): The angular velocity of the part's assembly. - **Property `AssemblyCenterOfMass`** (`Vector3`): The center of mass of the part's assembly in world space. - **Property `AssemblyLinearVelocity`** (`Vector3`): The linear velocity of the part's assembly. - **Property `AssemblyMass`** (`float`): The total mass of the part's assembly. - **Property `AssemblyRootPart`** (`BasePart`): A reference to the root part of the assembly. - **Property `AudioCanCollide`** (`boolean`): Determines whether the part will physically interact with audio - **Property `BackParamA`** (`float`): Determines the first parameter for the SurfaceType on the Back face of a *(deprecated, hidden)* - **Property `BackParamB`** (`float`): Determines the second parameter for the SurfaceType on the Back face of a *(deprecated, hidden)* - **Property `BackSurface`** (`SurfaceType`): Determines the type of surface for the back face of a part. - **Property `BackSurfaceInput`** (`InputType`): Determines the kind of input for the Back face of a part. *(deprecated, hidden)* - **Property `BottomParamA`** (`float`): Determines the first parameter for the SurfaceType on the Bottom face of a *(deprecated, hidden)* - **Property `BottomParamB`** (`float`): Determines the second parameter for the SurfaceType on the Bottom face of *(deprecated, hidden)* - **Property `BottomSurface`** (`SurfaceType`): Determines the type of surface for the bottom face of a part. - **Property `BottomSurfaceInput`** (`InputType`): Determines the kind of input for the Bottom face of a part. *(deprecated, hidden)* - **Property `BrickColor`** (`BrickColor`): Determines the color of a part. - **Property `brickColor`** (`BrickColor`): *(deprecated)* - **Property `CanCollide`** (`boolean`): Determines whether a part may collide with other parts. - **Property `CanQuery`** (`boolean`): Determines whether the part is considered during spatial query operations. - **Property `CanTouch`** (`boolean`): Determines if Touched and - **Property `CastShadow`** (`boolean`): Determines whether or not a part casts a shadow. - **Property `CenterOfMass`** (`Vector3`): Describes the world position in which a part's center of mass is located. - **Property `CFrame`** (`CFrame`): Determines the position and orientation of the BasePart in the - **Property `CollisionGroup`** (`string`): Describes the name of a part's collision group. - **Property `CollisionGroupId`** (`int`): Describes the automatically set ID number of a part's collision group. *(deprecated)* - **Property `Color`** (`Color3`): Determines the color of a part. - **Property `CurrentPhysicalProperties`** (`PhysicalProperties`): Indicates the current physical properties of the part. - **Property `CustomPhysicalProperties`** (`PhysicalProperties`): Determines several physical properties of a part. - **Property `Elasticity`** (`float`): Used to control the Elasticity of the part, but it no longer does *(deprecated, hidden)* - **Property `EnableFluidForces`** (`boolean`): Used to enable or disable aerodynamic forces on parts and assemblies. - **Property `ExtentsCFrame`** (`CFrame`): The CFrame of the physical extents of the BasePart. - **Property `ExtentsSize`** (`Vector3`): The actual physical size of the BasePart as regarded by the - **Property `Friction`** (`float`): Used to control the Friction of the part, but now it no longer does *(deprecated, hidden)* - **Property `FrontParamA`** (`float`): Determines the first parameter for the SurfaceType on the Front face of a *(deprecated, hidden)* - **Property `FrontParamB`** (`float`): Determines the second parameter for the SurfaceType on the Front face of a *(deprecated, hidden)* - **Property `FrontSurface`** (`SurfaceType`): Determines the type of surface for the front face of a part. - **Property `FrontSurfaceInput`** (`InputType`): Determines the kind of input for the Front face of a part (-Z direction). *(deprecated, hidden)* - **Property `LeftParamA`** (`float`): Determines the first parameter for the SurfaceType on the Left face of a *(deprecated, hidden)* - **Property `LeftParamB`** (`float`): Determines the second parameter for the SurfaceType on the Left face of a *(deprecated, hidden)* - **Property `LeftSurface`** (`SurfaceType`): Determines the type of surface for the left face of a part. - **Property `LeftSurfaceInput`** (`InputType`): Determines the kind of input for the Left face of a part. *(deprecated, hidden)* - **Property `LocalTransparencyModifier`** (`float`): Determines a multiplier for BasePart.Transparency that is only *(hidden)* - **Property `Locked`** (`boolean`): Determines whether a part is selectable in Studio. - **Property `Mass`** (`float`): Describes the mass of the part, the product of its density and volume. - **Property `Massless`** (`boolean`): Determines whether the part contributes to the total mass or inertia of - **Property `Material`** (`Material`): Determines the texture and default physical properties of a part. - **Property `MaterialVariant`** (`string`): The name of MaterialVariant. - **Property `Orientation`** (`Vector3`): Describes the rotation of the part in the world. *(hidden)* - **Property `PivotOffset`** (`CFrame`): Specifies the offset of the part's pivot from its CFrame. - **Property `Position`** (`Vector3`): Describes the position of the part in the world. *(hidden)* - **Property `ReceiveAge`** (`float`): Time since last recorded physics update. *(hidden)* - **Property `Reflectance`** (`float`): Determines how much a part reflects the skybox. - **Property `ResizeableFaces`** (`Faces`): Describes the faces on which a part may be resized. - **Property `ResizeIncrement`** (`int`): Describes the smallest change in size allowable by the - **Property `RightParamA`** (`float`): Determines the first parameter for the SurfaceType on the Right face of a *(deprecated, hidden)* - **Property `RightParamB`** (`float`): Determines the second parameter for the SurfaceType on the Right face of a *(deprecated, hidden)* - **Property `RightSurface`** (`SurfaceType`): Determines the type of surface for the right face of a part. - **Property `RightSurfaceInput`** (`InputType`): Determines the kind of input for the Right face of a part (-X direction). *(deprecated, hidden)* - **Property `RootPriority`** (`int`): The main rule in determining the root part of an assembly. - **Property `Rotation`** (`Vector3`): The rotation of the part in degrees for the three axes. - **Property `RotVelocity`** (`Vector3`): Determines a part's change in orientation over time. *(deprecated, hidden)* - **Property `Size`** (`Vector3`): Determines the dimensions of a part (length, width, height). - **Property `SpecificGravity`** (`float`): The ratio of the part's density to the density of water determined by the *(deprecated)* - **Property `TopParamA`** (`float`): Determines the first parameter for the SurfaceType on the Top face of a *(deprecated, hidden)* - **Property `TopParamB`** (`float`): Determines the second parameter for the SurfaceType on the Top face of a *(deprecated, hidden)* - **Property `TopSurface`** (`SurfaceType`): Determines the type of surface for the top face of a part. - **Property `TopSurfaceInput`** (`InputType`): Determines the kind of input for the Top face of a part (+Y direction). *(deprecated, hidden)* - **Property `Transparency`** (`float`): Determines how much a part can be seen through (the inverse of part - **Property `Velocity`** (`Vector3`): Determines a part's change in position over time. *(deprecated, hidden)* - **Method `AngularAccelerationToTorque(angAcceleration: Vector3, angVelocity?: Vector3): Vector3`**: Returns the torque needed to achieve a given angular acceleration on this - **Method `ApplyAngularImpulse(impulse: Vector3): ()`**: Apply an angular impulse to the assembly. - **Method `ApplyImpulse(impulse: Vector3): ()`**: Apply an impulse to the assembly at the assembly's - **Method `ApplyImpulseAtPosition(impulse: Vector3, position: Vector3): ()`**: Apply an impulse to the assembly at specified position. - **Method `BreakJoints(): ()`**: Breaks any surface connection with any adjacent part, including *(deprecated)* - **Method `breakJoints(): ()`**: *(deprecated)* - **Method `CanCollideWith(part: BasePart): boolean`**: Returns whether the parts can collide with each other. - **Method `CanSetNetworkOwnership(): Tuple`**: Checks whether you can set a part's network ownership. - **Method `GetClosestPointOnSurface(position: Vector3): Vector3`**: Returns the closest point on the part's surface to the given point. - **Method `GetConnectedParts(recursive?: boolean): List`**: Returns a table of parts connected to the object by any kind of rigid - **Method `GetJoints(): Instances`**: Return all Joints or Constraints that is connected to this Part. - **Method `GetMass(): float`**: Returns the value of the Mass property. - **Method `getMass(): float`**: *(deprecated)* - **Method `GetNetworkOwner(): Instance`**: Returns the current player who is the network owner of this part, or `nil` - **Method `GetNetworkOwnershipAuto(): boolean`**: Returns true if the game engine automatically decides the network owner - **Method `GetNoCollisionConstraints(): Instances`**: - **Method `GetRenderCFrame(): CFrame`**: OBSOLETE. Returns a CFrame describing where the part is being rendered at. *(deprecated)* - **Method `GetRootPart(): Instance`**: Returns the base part of an assembly of parts. *(deprecated)* - **Method `GetTouchingParts(): Instances`**: Returns a table of all BasePart.CanCollide true parts that - **Method `GetVelocityAtPosition(position: Vector3): Vector3`**: Returns the linear velocity of the part's assembly at the given position - **Method `IntersectAsync(parts: Instances, collisionfidelity?: CollisionFidelity, renderFidelity?: RenderFidelity): Instance`**: Note: It is highly recommended to use the newer - **Method `IsGrounded(): boolean`**: Returns true if the object is connected to a part that will hold it in - **Method `MakeJoints(): ()`**: Creates a joint on any side of the object that has a surface ID that can *(deprecated)* - **Method `makeJoints(): ()`**: *(deprecated)* - **Method `Resize(normalId: NormalId, deltaAmount: int): boolean`**: Changes the size of an object just like using the Studio resize tool. - **Method `resize(normalId: NormalId, deltaAmount: int): boolean`**: *(deprecated)* - **Method `SetNetworkOwner(playerInstance?: Player): ()`**: Sets the given player as network owner for this and all connected parts. - **Method `SetNetworkOwnershipAuto(): ()`**: Lets the game engine dynamically decide who will handle the part's physics - **Method `SubtractAsync(parts: Instances, collisionfidelity?: CollisionFidelity, renderFidelity?: RenderFidelity): Instance`**: Note: It is highly recommended to use the newer - **Method `TorqueToAngularAcceleration(torque: Vector3, angVelocity?: Vector3): Vector3`**: Returns the angular acceleration that would result from applying a given - **Method `UnionAsync(parts: Instances, collisionfidelity?: CollisionFidelity, renderFidelity?: RenderFidelity): Instance`**: Note: It is highly recommended to use the newer - **Event `LocalSimulationTouched`**: *(deprecated)* - **Event `OutfitChanged`**: *(deprecated)* - **Event `StoppedTouching`**: *(deprecated)* - **Event `Touched`**: Fires when a part touches another part as a result of physical movement. - **Event `TouchEnded`**: Fires when a part stops touching another part as a result of physical ### From [PVInstance](/docs/reference/engine/classes/PVInstance.md) - **Property `Origin`** (`CFrame`): - **Property `Pivot Offset`** (`CFrame`): - **Method `GetPivot(): CFrame`**: Gets the pivot of a PVInstance. - **Method `PivotTo(targetCFrame: CFrame): ()`**: Transforms the PVInstance along with all of its descendant ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Player last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances summary: "An object that represents a presently connected client to the experience." --- # Class: Player > An object that represents a presently connected client to the experience. ## Description A `Player` object is a client that is currently connected. These objects are added to the [Players](/docs/reference/engine/classes/Players.md) service when a new player connects, then removed when they eventually disconnect from the server. The [Instance.Name](/docs/reference/engine/classes/Instance.md) property reflects the player's username. When saving information about a player, you should use their [UserId](/docs/reference/engine/classes/Player.md) since it is possible that a player can change their username. There are several similar methods in the [Players](/docs/reference/engine/classes/Players.md) service for working with Player objects. Use these over their respective [Instance](/docs/reference/engine/classes/Instance.md) methods: - You can get a table of current `Player` objects using [Players:GetPlayers()](/docs/reference/engine/classes/Players.md); again, use this instead of [Instance:GetChildren()](/docs/reference/engine/classes/Instance.md). - To detect the addition of `Player` objects, it is recommended to use the [Players.PlayerAdded](/docs/reference/engine/classes/Players.md) event (instead of [Instance.ChildAdded](/docs/reference/engine/classes/Instance.md) on the [Players](/docs/reference/engine/classes/Players.md) service). - Similarly, you can detect the removal of `Player` objects using [Players.PlayerRemoving](/docs/reference/engine/classes/Players.md), which fires just **before** the `Player` is removed (instead of [Instance.ChildRemoved](/docs/reference/engine/classes/Instance.md) which fires after). This is important if you are saving information about the player that might be removed or cleaned up on removal. ## Properties ### Property: Player.AccountAge ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Players" ] } ``` This property describes how long ago a player's account was registered in days. It is set using the [SetAccountAge()](/docs/reference/engine/classes/Player.md) method, which cannot be accessed by scripts. ### Property: Player.AutoJumpEnabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Players" ] } ``` This property determines whether the [Character](/docs/reference/engine/classes/Player.md) of a [Player](/docs/reference/engine/classes/Player.md) using a mobile device will automatically jump when they hit an obstacle. This can make levels more navigable while on a mobile device. When the player joins the experience, the [StarterPlayer.AutoJumpEnabled](/docs/reference/engine/classes/StarterPlayer.md) value determines the initial state of this property. Then, this property determines the value of the [Humanoid.AutoJumpEnabled](/docs/reference/engine/classes/Humanoid.md) property of the [Character](/docs/reference/engine/classes/Player.md) on spawn. In other words, it is possible to set the auto-jump behavior on a per-character, per-player, and per-experience basis using these three properties. **Auto-Jump Toggle** This code sample is meant for a TextButton. It allows the player to toggle the auto-jumping behavior while on a mobile device. ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local button = script.Parent local function update() -- Update button text if player.AutoJumpEnabled then button.Text = "Auto-Jump is ON" else button.Text = "Auto-Jump is OFF" end -- Reflect the property in the player's character, if they have one if player.Character then local human = player.Character:FindFirstChild("Humanoid") if human then human.AutoJumpEnabled = player.AutoJumpEnabled end end end local function onActivated() -- Toggle auto-jump player.AutoJumpEnabled = not player.AutoJumpEnabled -- Update everything else update() end button.Activated:Connect(onActivated) update() ``` ### Property: Player.CameraMaxZoomDistance ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Camera", "capabilities": [ "Players" ] } ``` This property sets the maximum distance the player's camera is allowed to zoom out, in studs. The default value of this property is set by [StarterPlayer.CameraMaxZoomDistance](/docs/reference/engine/classes/StarterPlayer.md). If this value is set to a lower value than [CameraMinZoomDistance](/docs/reference/engine/classes/Player.md), it will be increased to [CameraMinZoomDistance](/docs/reference/engine/classes/Player.md). ### Property: Player.CameraMinZoomDistance ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Camera", "capabilities": [ "Players" ] } ``` This property sets the minimum distance the player's camera is allowed to zoom in, in studs. The default value of this property is set by [StarterPlayer.CameraMinZoomDistance](/docs/reference/engine/classes/StarterPlayer.md). If this value is set to a higher value than [CameraMaxZoomDistance](/docs/reference/engine/classes/Player.md), it will be decreased to [CameraMaxZoomDistance](/docs/reference/engine/classes/Player.md). ### Property: Player.CameraMode ```json { "type": "CameraMode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Camera", "capabilities": [ "Players" ] } ``` This property sets the player's camera mode, defaulting to third person. #### Third Person In the default third person mode ([CameraMode.Classic](/docs/reference/engine/enums/CameraMode.md)), the character can be seen in the camera. While in this mode, the default behavior is: - Players can right-click and drag (mouse), tap and drag (mobile), use the secondary thumbstick (gamepad), or press the left/right arrows (keyboard) to rotate the camera around their character. - When a player moves their character, it faces in the corresponding movement direction. - Players can zoom in and out freely, even to first person on full zoom in. #### First Person In first person mode ([CameraMode.LockFirstPerson](/docs/reference/engine/enums/CameraMode.md)), the player's camera is zoomed all the way in. Unless there is a visible GUI present with the [GuiButton.Modal](/docs/reference/engine/classes/GuiButton.md) property set to `true`, moving the mouse, tap-dragging on mobile, or using the secondary thumbstick on a gamepad will rotate the camera around the character. **Playing in First Person** This example demonstrates how to change the character's CameraMode to first person using the LockFirstPerson value of the [CameraMode](/docs/reference/engine/enums/CameraMode.md) enum. ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer player.CameraMode = Enum.CameraMode.LockFirstPerson ``` ### Property: Player.CanLoadCharacterAppearance ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Players" ] } ``` This property determines whether the character's appearance will be loaded when the player spawns. The default value of this property is set by [StarterPlayer.LoadPlayerAppearance](/docs/reference/engine/classes/StarterPlayer.md). - If `true`, the character will load the appearance of the player corresponding to the player's [CharacterAppearanceId](/docs/reference/engine/classes/Player.md). - If `false`, the player will spawn with a default appearance. Attempting to set the property after the character has spawned will not change the character; you must call [LoadCharacterAsync()](/docs/reference/engine/classes/Player.md) to load the new appearance. ### Property: Player.Character ```json { "type": "Model", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Players" ] } ``` This property contains a reference to a [Model](/docs/reference/engine/classes/Model.md) containing a [Humanoid](/docs/reference/engine/classes/Humanoid.md), body parts, scripts, and other objects required for simulating the player's avatar in-experience. The model is parented to the [Workspace](/docs/reference/engine/classes/Workspace.md) but it may be moved. It is automatically loaded when [Players.CharacterAutoLoads](/docs/reference/engine/classes/Players.md) is `true` and it can be manually loaded otherwise using [LoadCharacterAsync()](/docs/reference/engine/classes/Player.md). Initially this property is `nil` and it is set when the player's character first spawns. Use the [CharacterAdded](/docs/reference/engine/classes/Player.md) event to detect when a player's character properly loads, and the [CharacterRemoving](/docs/reference/engine/classes/Player.md) event to detect when the character is about to despawn. Avoid using [Object:GetPropertyChangedSignal()](/docs/reference/engine/classes/Object.md) on this property. Note that [LocalScripts](/docs/reference/engine/classes/LocalScript.md) that are cloned from [StarterGui](/docs/reference/engine/classes/StarterGui.md) or [StarterPack](/docs/reference/engine/classes/StarterPack.md) into a player's [PlayerGui](/docs/reference/engine/classes/PlayerGui.md) or [Backpack](/docs/reference/engine/classes/Backpack.md) respectively are often run before the old character model is replaced, so [Player.Character](/docs/reference/engine/classes/Player.md) may refer to the old model whose [Parent](/docs/reference/engine/classes/Instance.md) property is `nil`. Therefore, in a [LocalScript](/docs/reference/engine/classes/LocalScript.md) under [StarterGui](/docs/reference/engine/classes/StarterGui.md) or [StarterPack](/docs/reference/engine/classes/StarterPack.md), it is advisable to make sure the parent of `Character` is not `nil` before using it, for example: ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local character = player.Character if not character or character.Parent == nil then character = player.CharacterAdded:Wait() end ``` ### Property: Player.CharacterAppearanceId ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Players" ] } ``` This property determines the user ID of the account whose character appearance is used for a player's [Character](/docs/reference/engine/classes/Player.md). By default, this property is the [UserId](/docs/reference/engine/classes/Player.md), which uses the player's avatar as they have created it on Roblox. Changing this property to the user ID of another account will cause the player to spawn with that account's appearance. You can also toggle whether or not a player's character appearance is loaded in experience by changing the [StarterPlayer.LoadCharacterAppearance](/docs/reference/engine/classes/StarterPlayer.md) property. ### Property: Player.DevCameraOcclusionMode ```json { "type": "DevCameraOcclusionMode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Camera", "capabilities": [ "Players" ] } ``` Defines how the default camera scripts handle objects between the camera and the camera subject. Set by [StarterPlayer.DevCameraOcclusionMode](/docs/reference/engine/classes/StarterPlayer.md) and can't be changed for individual players. The default value is [Zoom](/docs/reference/engine/enums/DevCameraOcclusionMode.md). See [DevCameraOcclusionMode](/docs/reference/engine/enums/DevCameraOcclusionMode.md) for a list of available modes. ### Property: Player.DevComputerCameraMode ```json { "type": "DevComputerCameraMovementMode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Camera", "capabilities": [ "Players" ] } ``` This property determines the manner in which a player moves their camera when using a device with a mouse and keyboard. This property cannot be set using a [LocalScript](/docs/reference/engine/classes/LocalScript.md) (it must be set on the server using a [Script](/docs/reference/engine/classes/Script.md)). The default value of this property is determined by [StarterPlayer.DevComputerCameraMovementMode](/docs/reference/engine/classes/StarterPlayer.md). This property doesn't affect players using a [TouchEnabled](/docs/reference/engine/classes/UserInputService.md) device. See [DevTouchCameraMode](/docs/reference/engine/classes/Player.md) instead. ### Property: Player.DevComputerMovementMode ```json { "type": "DevComputerMovementMode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Control", "capabilities": [ "Players" ] } ``` This property determines the manner in which a player moves their character when using a device with a mouse and keyboard. This property cannot be set using a [LocalScript](/docs/reference/engine/classes/LocalScript.md) (it must be set on the server using a [Script](/docs/reference/engine/classes/Script.md)). The default value of this property is determined by [StarterPlayer.DevComputerMovementMode](/docs/reference/engine/classes/StarterPlayer.md). This property doesn't affect players using a [TouchEnabled](/docs/reference/engine/classes/UserInputService.md) device. See [DevTouchMovementMode](/docs/reference/engine/classes/Player.md) instead. ### Property: Player.DevEnableMouseLock ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Camera", "capabilities": [ "Players" ] } ``` This property determines if a player is able to toggle mouse lock by pressing Shift. A player can disable the mouse lock switch in the experience's settings during play. By default, this property is set to the value of [StarterPlayer.EnableMouseLockOption](/docs/reference/engine/classes/StarterPlayer.md). This can be set server-side during runtime by using a [Script](/docs/reference/engine/classes/Script.md). It can not be set client-side. When mouse lock is enabled, the player's cursor is locked to the center of the screen. Moving the mouse will orbit the camera around the player's [Character](/docs/reference/engine/classes/Player.md), and the character will face the same direction as the [Camera](/docs/reference/engine/classes/Camera.md). It also offsets the camera view just over the right shoulder of the player's character. ### Property: Player.DevTouchCameraMode ```json { "type": "DevTouchCameraMovementMode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Camera", "capabilities": [ "Players" ] } ``` This property determines the manner in which a player moves their camera when using a [TouchEnabled](/docs/reference/engine/classes/UserInputService.md) device. This property cannot be set using a [LocalScript](/docs/reference/engine/classes/LocalScript.md) (it must be set on the server using a [Script](/docs/reference/engine/classes/Script.md)). The default value of this property is determined by [StarterPlayer.DevTouchCameraMovementMode](/docs/reference/engine/classes/StarterPlayer.md). This property doesn't affect players who aren't using a [TouchEnabled](/docs/reference/engine/classes/UserInputService.md) device. See [DevComputerCameraMode](/docs/reference/engine/classes/Player.md) instead. ### Property: Player.DevTouchMovementMode ```json { "type": "DevTouchMovementMode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Control", "capabilities": [ "Players" ] } ``` This property determines the manner in which a player moves their character when using a [TouchEnabled](/docs/reference/engine/classes/UserInputService.md) device. This property cannot be set using a [LocalScript](/docs/reference/engine/classes/LocalScript.md) (it must be set on the server using a [Script](/docs/reference/engine/classes/Script.md)). The default value of this property is determined by [StarterPlayer.DevTouchMovementMode](/docs/reference/engine/classes/StarterPlayer.md). This property doesn't affect players who aren't using a [TouchEnabled](/docs/reference/engine/classes/UserInputService.md) device. See [DevComputerMovementMode](/docs/reference/engine/classes/Player.md) instead. ### Property: Player.DisplayName ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Players" ] } ``` This property contains the display name of the authenticated user associated with the [Player](/docs/reference/engine/classes/Player.md) object. Unlike [UserId](/docs/reference/engine/classes/Player.md), display names are non-unique names a player displays to others. #### Usage Notes - Since display names are non-unique, it's possible for two players in a single instance to have identical names. If you need a globally unique identifier for a player, use [UserId](/docs/reference/engine/classes/Player.md) instead. - Characters generated with [LoadCharacterAsync()](/docs/reference/engine/classes/Player.md) or by the Roblox engine will have their [Humanoid.DisplayName](/docs/reference/engine/classes/Humanoid.md) property assigned to the [Player.DisplayName](/docs/reference/engine/classes/Player.md) property. - Display names may have unicode characters in the string. See [UTF-8](/docs/reference/engine/globals/utf8.md) for more information on how to work with strings with unicode characters. ### Property: Player.FollowUserId ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Players" ] } ``` This property contains the [UserId](/docs/reference/engine/classes/Player.md) of the user that a player followed into the experience, or `0` if the player did not follow anyone in. This property is useful for alerting players who have been followed by another player into the experience. You can get the name of the player followed using this user ID and the [Players:GetNameFromUserIdAsync()](/docs/reference/engine/classes/Players.md) method. **Followed Alert** This code sample alerts players if a new player follows the local player into the game. Place this in a [LocalScript](/docs/reference/engine/classes/LocalScript.md) in [StarterPlayerScripts](/docs/reference/engine/classes/StarterPlayerScripts.md). ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local screenGui = Instance.new("ScreenGui") screenGui.Parent = player:WaitForChild("PlayerGui") local function onPlayerAdded(newPlayer) if newPlayer.FollowUserId == player.UserId then local textLabel = Instance.new("TextLabel") textLabel.Parent = screenGui textLabel.Text = "You were followed to this game by " .. newPlayer.Name .. "!" task.delay(3, function() if textLabel then textLabel:Destroy() end end) end end Players.PlayerAdded:Connect(onPlayerAdded) ``` ### Property: Player.HasRobloxSubscription ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "None", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Players" ] } ``` This read-only property is `true` when the player has an active Roblox subscription (the flagship Roblox membership), and `false` otherwise. It is set by the server and cannot be changed by scripts. Use this property instead of [Player.MembershipType](/docs/reference/engine/classes/Player.md) to check for the Roblox subscription. **Check Roblox Subscription Status** The following example checks whether a player has an active [Roblox subscription](https://create.roblox.com/docs/production/monetization/roblox-plus) and grants them a benefit if so. ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer if player.HasRobloxSubscription then -- Grant subscriber-exclusive content or perks end ``` ### Property: Player.HasVerifiedBadge ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Players" ] } ``` This property indicates if the player has a **Verified** badge. ### Property: Player.HealthDisplayDistance ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Camera", "capabilities": [ "Players" ] } ``` This property sets the distance in studs at which this player will see other [Humanoid](/docs/reference/engine/classes/Humanoid.md) health bars. If set to `0`, the health bars will not be displayed. This property is set to [StarterPlayer.HealthDisplayDistance](/docs/reference/engine/classes/StarterPlayer.md) by default. If a humanoid's health bar is visible, you can set the display type using [Humanoid.DisplayDistanceType](/docs/reference/engine/classes/Humanoid.md). ### Property: Player.InputLatency ```json { "type": "int", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Players" ] } ``` ### Property: Player.MembershipType ```json { "type": "MembershipType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Players" ] } ``` This property can only be read from to determine membership (it cannot be set to another membership type). It holds a [MembershipType](/docs/reference/engine/enums/MembershipType.md) enum of the account's membership type. **Check Player Membership Status** The following example checks whether a player has [Premium](https://www.roblox.com/premium/membership) membership. ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer if player.MembershipType == Enum.MembershipType.Premium then -- Take some action specifically for Premium members end ``` ### Property: Player.NameDisplayDistance ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Camera", "capabilities": [ "Players" ] } ``` This property sets the distance in studs at which this player will see other [Humanoid](/docs/reference/engine/classes/Humanoid.md) names. If the property is set to `0`, names are hidden. This property is set to [StarterPlayer.NameDisplayDistance](/docs/reference/engine/classes/StarterPlayer.md) by default. If a humanoid's name is visible, you can set the display type using [Humanoid.DisplayDistanceType](/docs/reference/engine/classes/Humanoid.md). ### Property: Player.Neutral ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Team", "capabilities": [ "Players" ] } ``` This property determines whether the player is on a specific team. - When `true`, the player is not on a specific team. This also means that the [Team](/docs/reference/engine/classes/Player.md) property will be `nil` and the [TeamColor](/docs/reference/engine/classes/Player.md) will be white. - When `false`, the player is on a specific team. The [Team](/docs/reference/engine/classes/Player.md) property will correspond to the [Team](/docs/reference/engine/classes/Team.md) that the player is on, as will the [TeamColor](/docs/reference/engine/classes/Player.md). ### Property: Player.ReplicationFocus ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Players" ] } ``` This property sets the part to focus replication around a player. Different Roblox systems that communicate over the network (such as physics, streaming, etc.) replicate at different rates depending on how close objects are to the replication focus. When this property is `nil`, it reverts to its default behavior which is to treat the local player's character's [PrimaryPart](/docs/reference/engine/classes/Model.md) as the replication focus. This property should only be set on the server with a [Script](/docs/reference/engine/classes/Script.md), not a [LocalScript](/docs/reference/engine/classes/LocalScript.md). Note that this property does not change or update network ownership of parts. ### Property: Player.RespawnLocation ```json { "type": "SpawnLocation", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Players" ] } ``` If set, the player will respawn at the given [SpawnLocation](/docs/reference/engine/classes/SpawnLocation.md) which must meet the following criteria: - Descendant of [Workspace](/docs/reference/engine/classes/Workspace.md). - The [SpawnLocation.TeamColor](/docs/reference/engine/classes/SpawnLocation.md) property is set to the player's [TeamColor](/docs/reference/engine/classes/Player.md) or the [SpawnLocation.Neutral](/docs/reference/engine/classes/SpawnLocation.md) property is set to `true`. #### Alternatives - A [Player](/docs/reference/engine/classes/Player.md) will spawn from [SpawnLocations](/docs/reference/engine/classes/SpawnLocation.md) belonging to their team. In some cases it may be simpler to change the player's [Team](/docs/reference/engine/classes/Player.md) instead. - Implement your own custom spawn logic using [PVInstance:PivotTo()](/docs/reference/engine/classes/PVInstance.md) to manually move the [Character](/docs/reference/engine/classes/Player.md). **Change Spawn on Touch** This code sample will set the player to always respawn from the last SpawnLocation they touched. New players will respawn from the SpawnLocation named 'FirstSpawn' until they touch a different SpawnLocation. This is an alternative to using the AllowTeamChangeOnTouch property to switch SpawnLocations and does not require Teams. ```lua local Players = game:GetService("Players") local function addSpawn(spawnLocation) -- listen for the spawn being touched spawnLocation.Touched:Connect(function(hit) local character = hit:FindFirstAncestorOfClass("Model") if character then local player = Players:GetPlayerFromCharacter(character) if player and player.RespawnLocation ~= spawnLocation then local humanoid = character:FindFirstChildOfClass("Humanoid") -- make sure the character isn't dead if humanoid and humanoid:GetState() ~= Enum.HumanoidStateType.Dead then print("spawn set") player.RespawnLocation = spawnLocation end end end end) end local firstSpawn -- look through the workspace for spawns for _, descendant in pairs(workspace:GetDescendants()) do if descendant:IsA("SpawnLocation") then if descendant.Name == "FirstSpawn" then firstSpawn = descendant end addSpawn(descendant) end end local function playerAdded(player) player.RespawnLocation = firstSpawn end -- listen for new players Players.PlayerAdded:Connect(playerAdded) -- go through existing players for _, player in pairs(Players:GetPlayers()) do playerAdded(player) end ``` ### Property: Player.StepIdOffset ```json { "type": "int", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Players" ] } ``` ### Property: Player.Team ```json { "type": "Team", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Team", "capabilities": [ "Players" ] } ``` This property is a reference to a [Team](/docs/reference/engine/classes/Team.md) object within the [Teams](/docs/reference/engine/classes/Teams.md) service. If the player isn't on a team or has an invalid [TeamColor](/docs/reference/engine/classes/Player.md), this property is `nil`. When this property is set, the player has joined the [Team](/docs/reference/engine/classes/Team.md) and the [Team.PlayerAdded](/docs/reference/engine/classes/Team.md) event fires on the associated team. Similarly, [Team.PlayerRemoved](/docs/reference/engine/classes/Team.md) fires when the property is unset from a certain [Team](/docs/reference/engine/classes/Team.md). ### Property: Player.TeamColor ```json { "type": "BrickColor", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Team", "capabilities": [ "Players" ] } ``` This property determines which [Team](/docs/reference/engine/classes/Team.md) a player is associated with according to that team's [Team.TeamColor](/docs/reference/engine/classes/Team.md). If no [Team](/docs/reference/engine/classes/Team.md) object has the associated [BrickColor](/docs/reference/engine/datatypes/BrickColor.md), the player will not be associated with a team. It's often a better idea to set [Player.Team](/docs/reference/engine/classes/Player.md) to the respective [Team](/docs/reference/engine/classes/Team.md) instead of using this property. Setting this property often leads to repetition of the same [BrickColor](/docs/reference/engine/datatypes/BrickColor.md) value for a certain team across many scripts. ### Property: Player.ThirdPartyTextChatRestrictionStatus ```json { "type": "ChatRestrictionStatus", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Players" ] } ``` ### Property: Player.User ```json { "type": "User", "access": "ReadOnly", "security": { "read": "None", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Players" ] } ``` A read-only [User](/docs/reference/engine/datatypes/User.md) value that represents this player's domain-scoped identity within the current experience. The [User](/docs/reference/engine/datatypes/User.md) encapsulates the player's domain user ID alongside the domain type and domain ID, providing an unambiguous identifier that carries its context. Use this property as the standard way to identify users in new code. Engine APIs that accept user ID parameters also accept [User](/docs/reference/engine/datatypes/User.md) values directly. ```lua local Players = game:GetService("Players") Players.PlayerAdded:Connect(function(player) local user = player.User print("Domain user ID:", user.Id) print("Domain type:", user.DomainType) print("Domain ID:", user.DomainId) end) ``` ### Property: Player.UserId ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Players" ] } ``` This property contains a read-only integer that **uniquely and consistently** identifies the user's account on Roblox. Unlike the player's [DisplayName](/docs/reference/engine/classes/Player.md) which may change, this value will never change for the same account. This property is essential when saving/loading player data using [GlobalDataStores](/docs/reference/engine/classes/GlobalDataStore.md). **Player.UserId** The below example would print the UserId of every user who entered a game. ```lua local Players = game:GetService("Players") local function onPlayerAdded(player) print(player.UserId) end Players.PlayerAdded:Connect(onPlayerAdded) ``` **Data Store to Leaderboard** This code sample retrieves a player's saved gold from a data store and puts the returned value onto the leaderboard. Note that this sample does not save players' gold — it only loads it. ```lua local Players = game:GetService("Players") local DataStoreService = game:GetService("DataStoreService") local goldDataStore = DataStoreService:GetDataStore("Gold") local STARTING_GOLD = 100 local function onPlayerAdded(player) local playerKey = "Player_" .. player.UserId local leaderstats = Instance.new("IntValue") leaderstats.Name = "leaderstats" local gold = Instance.new("IntValue") gold.Name = "Gold" gold.Parent = leaderstats local success, result = pcall(function() return goldDataStore:GetAsync(playerKey) or STARTING_GOLD end) if success then gold.Value = result else -- Failed to retrieve data warn(result) end leaderstats.Parent = player end Players.PlayerAdded:Connect(onPlayerAdded) ``` ### Property: Player.CharacterAppearance ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Players" ] } ``` > **Deprecated:** This item is deprecated. Do not use it for new work. This property indicates the URL of the asset containing the character's appearance, clothing, and gear. It is automatically set by Roblox to load your avatar's appearance when you join an experience. Attempting to set the property after the character has spawned will not change the character, you must call [LoadCharacterAsync()](/docs/reference/engine/classes/Player.md) to load the new appearance. ### Property: Player.userId ```json { "type": "int64", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Players" ] } ``` > **Deprecated:** This property is a deprecated variant of [Player.UserId](/docs/reference/engine/classes/Player.md) which should be used instead. ### Property: Player.DataComplexity *(hidden)* ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Players" ] } ``` > **Deprecated:** This item is deprecated, as it may have been used for a now obsolete data persistence method. Please save and load player data using [DataStoreService](/docs/reference/engine/classes/DataStoreService.md) for new work. This property was once used by an ancient data persistence method to indicate the total amount of data currently being stored in the player's cache on the current place. #### Notes - Booleans and numbers cost 1 data complexity unit. - Strings cost their length divided by 100 in data complexity units. - Instances cost their DataCost in data complexity units. - Saving the default value (0 for numbers, false for booleans, "" for strings and `nil` for Instances) removes the key from the DataComplexity count. - If, when using the SaveBoolean, SaveString, SaveNumber or SaveInstance functions, the DataComplexity for the player goes over the limit (currently 45000 units, defined by DataComplexityLimit), the function throws an error, the value is not saved, and any previous value of the key that was being saved to is deleted. ### Property: Player.DataReady *(hidden)* ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Players" ] } ``` > **Deprecated:** This item is deprecated, as it may have been used for a now obsolete data persistence method. Please save and load player data using [DataStoreService](/docs/reference/engine/classes/DataStoreService.md) for new work. This property was once used by an ancient data persistence method to indicate when the player's data is available to load. Becomes true when data is available. ### Property: Player.GameplayPaused *(hidden)* ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "None", "write": "NotAccessibleSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Players" ] } ``` This property indicates if the player is currently in a pause state in a place with [StreamingEnabled](/docs/reference/engine/classes/Workspace.md) activated. It is set on the client but replicated to the server. #### See Also - [Workspace.StreamingEnabled](/docs/reference/engine/classes/Workspace.md) which controls whether content streaming is enabled - [Workspace.StreamingIntegrityMode](/docs/reference/engine/classes/Workspace.md) and [StreamingIntegrityMode](/docs/reference/engine/enums/StreamingIntegrityMode.md) for more details on when gameplay is paused. ### Property: Player.LocaleId *(hidden)* ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Players" ] } ``` This property shows the locale ID that the local player has set for their Roblox account. It holds a string with the two letter code, for example `en-us`. See also [LocalizationService.RobloxLocaleId](/docs/reference/engine/classes/LocalizationService.md), the locale ID used for localizing internal content. This will be a different value when Roblox does not yet internally support the local player's set locale. ### Property: Player.PartyId *(hidden)* ```json { "type": "string", "access": "ReadOnly", "security": { "read": "None", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Players" ] } ``` A read-only string identifying the party the player currently belongs to within the experience. If the player is not in a party, this value is an empty string. This property is essential for integrating with the Roblox Party feature. Use it in combination with [SocialService:GetPlayersByPartyId()](/docs/reference/engine/classes/SocialService.md) and [SocialService:GetPartyAsync()](/docs/reference/engine/classes/SocialService.md) to access information about a player's party and its members. To test this service in your experience, use the [Party Simulator](/docs/en-us/studio/testing-modes.md#party-simulation) in Roblox Studio or publish the experience and play it in the Roblox application. **Player.PartyId** This example checks the [Player.PartyId](/docs/reference/engine/classes/Player.md) of a [Player](/docs/reference/engine/classes/Player.md) when they join the experience. If the player is in a party, it outputs the [Player.PartyId](/docs/reference/engine/classes/Player.md); otherwise, it outputs that the player is not in a party. ```lua local Players = game:GetService("Players") Players.PlayerAdded:Connect(function(player) local partyId = player.PartyId if partyId ~= "" then print("Player is in a party with ID: " .. partyId) else print("Player is not in a party") end player:GetPropertyChangedSignal("PartyId"):Connect(function() if player.PartyId ~= "" then print("Player joined party with ID: " .. player.PartyId) else print("Player left party") end end) end) ``` ## Methods ### Method: Player:AddReplicationFocus **Signature:** `Player:AddReplicationFocus(part: BasePart): ()` This method adds an additional replication focus for the player in order to trigger streaming around the location of the specified `part`. In this manner, streaming can occur around multiple locations, not just the location of [Player.ReplicationFocus](/docs/reference/engine/classes/Player.md). This has no effect in experiences that are not streaming enabled. Additional foci will use the same values of [Workspace.StreamingMinRadius](/docs/reference/engine/classes/Workspace.md) and [Workspace.StreamingTargetRadius](/docs/reference/engine/classes/Workspace.md) as are used by the primary focus. This method should only be called on the server. It has no effect when called from a [LocalScript](/docs/reference/engine/classes/LocalScript.md). *Security: None · Thread Safety: Unsafe · Capabilities: Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `part` | `BasePart` | | The [BasePart](/docs/reference/engine/classes/BasePart.md) to use as a new replication focus. | **Returns:** `()` ### Method: Player:ClearCharacterAppearance **Signature:** `Player:ClearCharacterAppearance(): ()` This method removes all [Accessory](/docs/reference/engine/classes/Accessory.md), [Shirt](/docs/reference/engine/classes/Shirt.md), [Pants](/docs/reference/engine/classes/Pants.md), [CharacterMesh](/docs/reference/engine/classes/CharacterMesh.md), and [BodyColors](/docs/reference/engine/classes/BodyColors.md) from the given player's [Character](/docs/reference/engine/classes/Player.md). In addition, it also removes the T-Shirt [Decal](/docs/reference/engine/classes/Decal.md) on the player's torso. The character's body part colors and face will remain unchanged. This method does nothing if the player does not have a [Character](/docs/reference/engine/classes/Player.md). *Security: None · Thread Safety: Unsafe · Capabilities: Players* **Returns:** `()` **How to Clear a Character's Appearance** ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local character = player.Character or player.CharacterAdded:Wait() local function onChildRemoved(child) print(child.ClassName, "removed from character") end character.ChildRemoved:Connect(onChildRemoved) player:ClearCharacterAppearance() --> BodyColors removed from character --> ShirtGraphic removed from character --> Shirt removed from character --> Pants removed from character --> CharacterMesh removed from character --> Hat removed from character --> Shirt removed from character ``` ### Method: Player:ClearCachedAvatarAppearance **Signature:** `Player:ClearCachedAvatarAppearance(): ()` This method clears the cached avatar appearance for the player. The next time [LoadCharacterAsync()](/docs/reference/engine/classes/Player.md) is called with the player's default platform appearance, a fresh version will be fetched from the backend instead of using the cached version. This is useful when you want to allow a player to reset their character after they have updated their appearance outside of the experience. For example, you can use this to implement a custom reset button that mirrors the cache-busting behavior of the default in-experience menu. *Security: None · Thread Safety: Unsafe · Capabilities: AvatarAppearance, Players* **Returns:** `()` ### Method: Player:DistanceFromCharacter **Signature:** `Player:DistanceFromCharacter(point: Vector3): float` This method returns the distance between the character's head and the given [Vector3](/docs/reference/engine/datatypes/Vector3.md) point, or `0` if the player has no [Character](/docs/reference/engine/classes/Player.md). This is useful when determining the distance between a player and another object or location in experience. If you would like to determine the distance between two non-player instances or positions, you can use the following: ```lua local distance = (position1 - position2).Magnitude ``` *Security: None · Thread Safety: Unsafe · Capabilities: Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `point` | `Vector3` | | The location from which player's distance to is being measured. | **Returns:** `float` — The distance in studs between the player and the location. **Measuring the Distance Between a Player and a Position** This example demonstrates how to measure the distance between a player's [Player.Character](/docs/reference/engine/classes/Player.md) and another location. This code will print the distance of each player's character from the origin (0, 0, 0): ```lua local Players = game:GetService("Players") for _, player in pairs(Players:GetPlayers()) do print(player:DistanceFromCharacter(Vector3.new(0, 0, 0))) end ``` ### Method: Player:GetFriendsOnlineAsync **Signature:** `Player:GetFriendsOnlineAsync(maxFriends?: int): Array` This function returns a dictionary array of online friends, using a 30 second cache. In the returned array, some fields are only present for certain location types; for example, `PlaceId` won't be present when `LocationType` is `0` (mobile website). | Name | Type | Description | | --- | --- | --- | | `VisitorId` | number | The [UserId](/docs/reference/engine/classes/Player.md) of the friend. | | `UserName` | string | The username of the friend. | | `DisplayName` | string | The [DisplayName](/docs/reference/engine/classes/Player.md) of the friend. | | `LastOnline` | string | When the friend was last online. | | `IsOnline` | boolean | If the friend is currently online. | | `LastLocation` | string | The name of the friend's current location. | | `PlaceId` | number | The place ID of the friend's last location. | | `GameId` | string | The [DataModel.JobId](/docs/reference/engine/classes/DataModel.md) of the friend's last location. | | `LocationType` | number | The location type of the friend's last location. | *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Players, Social* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `maxFriends` | `int` | `200` | The maximum number of online friends to return. | **Returns:** `Array` — A dictionary of online friends (see the table above). **Get a List of Online Connections** This example demonstrates how to get a dictionary of a player's online friends. It returns the maximum number of friends specified by the argument, or 200 if an argument is not provided. ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local success, result = pcall(player.GetFriendsOnlineAsync, player, 10) if success then for _, friend in pairs(result) do print(friend.UserName) end else warn("Failed to get online players: " .. result) end ``` ### Method: Player:GetFriendsWhoPlayedAsync **Signature:** `Player:GetFriendsWhoPlayedAsync(): Array` Returns an array of user IDs of any friends who have previously played this game. For example, if a player has had two friends play the game, the method might return `{1111111111, 2222222222}`. If no friends have played this game, the method returns an empty array (`{}`). *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Players, Social* **Returns:** `Array` — An array of user IDs. ### Method: Player:GetJoinData **Signature:** `Player:GetJoinData(): Dictionary` Returns a dictionary containing information describing how the player joins the experience. The dictionary contains any of the following fields: | Key | Value Type | Description | | --- | --- | --- | | `SourceGameId` | number | The [DataModel.GameId](/docs/reference/engine/classes/DataModel.md) of the experience the `Player` teleported from. Only present if the player teleports to the current experience and if a server calls the teleport function. | | `SourcePlaceId` | number | The [DataModel.PlaceId](/docs/reference/engine/classes/DataModel.md) of the place the `Player` teleported from. Only present if the player teleports to the current place and a server calls the teleport function. | | `ReferredByPlayerId` | number | The [UserId](/docs/reference/engine/classes/Player.md) of the player who invited the current player to the experience. Use this data to identify the referrer and trigger reward logic. | | `Members` | array | An array containing the [UserId](/docs/reference/engine/classes/Player.md) numbers of the users teleported alongside the player. Only present if the player teleported as part of a group. | | `TeleportData` | variant | Reflects the `teleportData` specified in the original teleport. Useful for sharing information between servers the player teleports to. Only present if `teleportData` was specified and a server calls the teleport function. | | `LaunchData` | string | A plain or JSON encoded string that contains launch data specified in a [share link](/docs/en-us/production/promotion/share-links.md) or [ExperienceInviteOptions.LaunchData](/docs/reference/engine/classes/ExperienceInviteOptions.md). | | `GameJoinContext` | dictionary | A dictionary that includes relevant information based on the context of the join. It contains the following keys: | If a server initiates the player's teleport, the dictionary that this method returns includes the player's teleport data. The [GetJoinData()](/docs/reference/engine/classes/Player.md) method can only be used to fetch teleport data on the server. To fetch the data on the client, use [TeleportService:GetLocalPlayerTeleportData()](/docs/reference/engine/classes/TeleportService.md). Unlike [TeleportService:GetLocalPlayerTeleportData()](/docs/reference/engine/classes/TeleportService.md), [GetJoinData()](/docs/reference/engine/classes/Player.md) only provides teleport data that meets the following security criteria: - It's guaranteed to have been sent by a Roblox server in the past 48 hours. - It's guaranteed to have been sent with this [Player](/docs/reference/engine/classes/Player.md). - The `SourcePlaceId` and `SourceGameId` are guaranteed to be the place and universe the data was sent from. This means you can verify the teleport data came from an approved place. As this data is transmitted by the client, it can still potentially be abused by an exploiter. Sensitive data such as player currency should be transmitted via a secure solution like [Memory Stores](/docs/en-us/cloud-services/memory-stores.md). *Security: None · Thread Safety: Unsafe · Capabilities: Players* **Returns:** `Dictionary` — A dictionary containing PlaceId and UserId values (see table in description). **Tracking Traffic Sources** The following example tracks sources of traffic for analytics. By creating URLs with unique launch data for each social platform, you can determine the most popular traffic sources. The sample checks the source against a list of possible samples and discards any invalid sources because users can modify the launch data. ```lua local DataStoreService = game:GetService("DataStoreService") local Players = game:GetService("Players") local analyticsStore = DataStoreService:GetDataStore("Analytics") local ALLOWED_SOURCES = { "twitter", "youtube", "discord", } local function onPlayerAdded(player) local source = player:GetJoinData().LaunchData -- check if the provided source is valid if source and table.find(ALLOWED_SOURCES, source) then -- update the data store to track the source popularity local success, result = pcall(analyticsStore.IncrementAsync, analyticsStore, source) if success then print(player.Name, "joined from", source, "- total:", result) else warn("Failed to record join source: " .. result) end end end Players.PlayerAdded:Connect(onPlayerAdded) ``` **Referral URL Generator** The following example generates a URL with the user's ID used as launch data. It then displays the URL in a read-only text box that makes it easy for the user to copy and share the link with their friends. When a user joins the game using a referral link, you can use the launch data to reward the referrer. ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local DIRECT_JOIN_URL = "https://www.roblox.com/games/start?placeId=%d&launchData=%s" local textBox = script.Parent local function generateReferralURL(player) return DIRECT_JOIN_URL:format(game.PlaceId, player.UserId) end local function highlightAll() if -- avoid recursive property updates textBox:IsFocused() and not (textBox.SelectionStart == 1 and textBox.CursorPosition == #textBox.Text + 1) then textBox.SelectionStart = 1 textBox.CursorPosition = #textBox.Text + 1 end end textBox.Focused:Connect(highlightAll) textBox:GetPropertyChangedSignal("SelectionStart"):Connect(highlightAll) textBox:GetPropertyChangedSignal("CursorPosition"):Connect(highlightAll) textBox.TextEditable = false textBox.ClearTextOnFocus = false textBox.Text = generateReferralURL(player) ``` **Using a Table as Launch Data** The following example is a function that converts a table into a string you can use as launch data. The provided data is JSON encoded, checked for valid character length, and escaped with percent signs. ```lua local HttpService = game:GetService("HttpService") local DATA_CHARACTER_LIMIT = 200 local function encodeTableAsLaunchData(data) -- convert the table to a string local jsonEncodedData = HttpService:JSONEncode(data) if #jsonEncodedData <= DATA_CHARACTER_LIMIT then -- escape potentially invalid characters, such as spaces local urlEncodedData = HttpService:UrlEncode(jsonEncodedData) return true, urlEncodedData else -- report character limit error return false, ("Encoded table exceeds %d character limit"):format(DATA_CHARACTER_LIMIT) end end local sampleData = { joinMessage = "Hello!", urlCreationDate = os.time(), magicNumbers = { 534, 1337, 746733573, }, } local success, encodedData = encodeTableAsLaunchData(sampleData) if success then print(encodedData) else warn("failed to encode launch data: " .. encodedData) end ``` **Decoding JSON Launch Data** The following example attempts to decode launch data, using `pcall` to prevent an error in case the data is corrupt. ```lua local HttpService = game:GetService("HttpService") local Players = game:GetService("Players") local function onPlayerAdded(player) local launchData = player:GetJoinData().LaunchData if launchData then -- attempt to decode the data local success, result = pcall(HttpService.JSONDecode, HttpService, launchData) if success then print(player.Name, "joined with data:", result) else -- this is probably due to the user messing with the URL warn("Failed to parse launch data:" .. result) end end end Players.PlayerAdded:Connect(onPlayerAdded) ``` **Server TeleportData Example** The following code sample is an example of how teleport data can be retrieved on the server using [Player:GetJoinData()](/docs/reference/engine/classes/Player.md). This code, when ran in a `Script` in `ServerScriptService`, will listen for new `Player|Players` joining the game. When they join it will retrieve their teleport data (verifying it came from a valid place) to find their current level. ```lua local Players = game:GetService("Players") local approvedPlaceIds = { 1 } -- insert approved PlaceIds here local function isPlaceIdApproved(placeId) for _, id in pairs(approvedPlaceIds) do if id == placeId then return true end end return false end local function onPlayerAdded(player) local joinData = player:GetJoinData() -- verify this data was sent by an approved place if isPlaceIdApproved(joinData.SourcePlaceId) then local teleportData = joinData.TeleportData if teleportData then local currentLevel = teleportData.currentLevel print(player.Name .. " is on level " .. currentLevel) end end end Players.PlayerAdded:Connect(onPlayerAdded) ``` ### Method: Player:GetMouse **Signature:** `Player:GetMouse(): Mouse` This method returns the [Mouse](/docs/reference/engine/classes/Mouse.md) being used by the client. The player's mouse instance can be used to track user mouse input including left and right mouse button clicks and movement and location. Note that [UserInputService](/docs/reference/engine/classes/UserInputService.md) provides additional methods, properties, and events to track user input, especially for devices that do not use a mouse. *Security: None · Thread Safety: Unsafe · Capabilities: Input, Players* **Returns:** `Mouse` ### Method: Player:GetNetworkPing **Signature:** `Player:GetNetworkPing(): float` Returns the round-trip, isolated network latency of the player in seconds. "Ping" is a measurement of the time taken for data to be sent from the client to the server, then back again. It doesn't involve data deserialization or processing. For client-side [LocalScripts](/docs/reference/engine/classes/LocalScript.md), this function can only be called on the [Players.LocalPlayer](/docs/reference/engine/classes/Players.md). This function is useful in identifying and debugging issues that occur in high network latency scenarios. It's also useful for masking latency, such as adjusting the speed of throwing animations for projectiles. *Security: None · Thread Safety: Safe · Capabilities: Players* **Returns:** `float` ### Method: Player:HasAppearanceLoaded **Signature:** `Player:HasAppearanceLoaded(): boolean` This method returns whether or not the appearance of the player's [Character](/docs/reference/engine/classes/Player.md) has loaded. Appearance includes items such as the player's [Shirt](/docs/reference/engine/classes/Shirt.md), [Pants](/docs/reference/engine/classes/Pants.md), and [Accessories](/docs/reference/engine/classes/Accessory.md). This is useful when determining whether a player's appearance has loaded after they first join the experience, which can be tracked using the [Players.PlayerAdded](/docs/reference/engine/classes/Players.md) event. *Security: None · Thread Safety: Unsafe · Capabilities: Players* **Returns:** `boolean` — A boolean indicating whether or not the appearance of the player's character has loaded. **Check if a Player's Appearance Has Loaded** This example prints the result of [Player:HasAppearanceLoaded()](/docs/reference/engine/classes/Player.md) after a player joins the game until the player's appearance has loaded. ```lua local Players = game:GetService("Players") local function onPlayerAdded(player) local loaded = player:HasAppearanceLoaded() print(loaded) while not loaded do loaded = player:HasAppearanceLoaded() print(loaded) task.wait() end end Players.PlayerAdded:Connect(onPlayerAdded) ``` ### Method: Player:IsFriendsWithAsync **Signature:** `Player:IsFriendsWithAsync(userId: User): boolean` This method sends a request to Roblox asking whether a player is a friend of another user, given the [UserId](/docs/reference/engine/classes/Player.md) of that user. This method caches results so multiple calls on the same player with the same `userId` may not yield the most up-to-date result. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Players, Social* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `userId` | `User` | | The [Player.UserId](/docs/reference/engine/classes/Player.md) of the specified player. | **Returns:** `boolean` — A boolean indicating whether a player is a friend of the specified user. ### Method: Player:IsInGroupAsync **Signature:** `Player:IsInGroupAsync(groupId: int64): boolean` This method sends a request to Roblox asking whether a player is a member of a group, given the ID of that group. This call may not yield the most up-to-date information. If a player leaves a group while they are in the experience, `IsInGroupAsync()` will still think they're in that group until they leave. However, this does not happen when used with a [LocalScript](/docs/reference/engine/classes/LocalScript.md) because the method caches results, so multiple calls of `IsInGroupAsync()` on the same player with the same group ID will yield the same result as when the method was first called with the given group ID. The caching behavior is on a per-peer basis: a server does not share the same cache as a client. When a player joins a group in-experience due to a call to [GroupService:PromptJoinAsync()](/docs/reference/engine/classes/GroupService.md), any cached value for that player will be cleared on the client where the prompt was shown. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Players, Groups* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `groupId` | `int64` | | The group ID of the specified group. | **Returns:** `boolean` — A boolean indicating whether the player is in the specified group. ### Method: Player:IsVerified **Signature:** `Player:IsVerified(): boolean` Returns a boolean value indicating that player's verification status. When `true`, the player is verified. Verification includes, but isn't limited to, non-VOIP phone number or government ID verification. When implementing `IsVerified`, exercise caution to ensure that the implementation does not inadvertently block all unverified users. Note that the method can only be called on the backend server. Calling it client-side results in an error. Additionally, this method will always return `false` in Studio. *Security: None · Thread Safety: Unsafe · Capabilities: Players* **Returns:** `boolean` — A boolean indicating whether the player is verified. **Using IsVerified** The following example prints "true" if the player is verified. ```lua local Players = game:GetService("Players") local function onPlayerAdded(player) print(player:IsVerified()) end for _, player in pairs(Players:GetPlayers()) do onPlayerAdded(player) end Players.PlayerAdded:Connect(onPlayerAdded) ``` ### Method: Player:Kick **Signature:** `Player:Kick(message: string): ()` This method allows an experience to gracefully disconnect a client and optionally provide a message to the disconnected user. This is useful for moderating abusive users. You should only allow specific users whom you trust to trigger this method on other users. Calling this method on a [Player](/docs/reference/engine/classes/Player.md) with no arguments disconnects the user from the server and provides a default notice message. Calling this method on a [Player](/docs/reference/engine/classes/Player.md) along with a string as the first argument replaces the default message with the provided string. When using this method from a [LocalScript](/docs/reference/engine/classes/LocalScript.md), only the local user's client can be kicked. *Security: None · Thread Safety: Unsafe · Capabilities: Players, Consequences* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `message` | `string` | | The message to show the user upon kicking. | **Returns:** `()` ### Method: Player:LoadCharacterAsync **Signature:** `Player:LoadCharacterAsync(): ()` This method creates a new character for the player, removing the old one. It also clears the player's [Backpack](/docs/reference/engine/classes/Backpack.md) and [PlayerGui](/docs/reference/engine/classes/PlayerGui.md). This is useful in cases where you want to reload the character without killing the player, such as when you want to load a new character appearance after changing the player's [CharacterAppearance](/docs/reference/engine/classes/Player.md). When reloading a [Player](/docs/reference/engine/classes/Player.md) with their default platform appearance applied, a cached version of their appearance will be loaded. The cached version is cleared whenever the player's appearance is updated using [AvatarEditorService](/docs/reference/engine/classes/AvatarEditorService.md) or when the player manually resets via the in-experience menu. To programmatically clear this cache, call [ClearCachedAvatarAppearance()](/docs/reference/engine/classes/Player.md) before calling `LoadCharacterAsync()`. After calling `LoadCharacterAsync()` for an individual player, it is not recommended to call it again for the same player until after that player's [CharacterAppearanceLoaded](/docs/reference/engine/classes/Player.md) event has fired. #### Character Loading Event Order Calling the `LoadCharacterAsync()` method on any `Player` fires events in the following order: 1. [Player.Character](/docs/reference/engine/classes/Player.md) sets, automatically removing old character. 2. [Player.CharacterAdded](/docs/reference/engine/classes/Player.md) fires. 3. [Object.Changed](/docs/reference/engine/classes/Object.md) fires on the [Player](/docs/reference/engine/classes/Player.md) with a value of `Character`. 4. The character appearance initializes. 5. [Player.CharacterAppearanceLoaded](/docs/reference/engine/classes/Player.md) fires. 6. The character's [Parent](/docs/reference/engine/classes/Instance.md) sets to the [DataModel](/docs/reference/engine/classes/DataModel.md). 7. The character rig builds and scales. 8. The character moves to the spawn location. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Players* **Returns:** `()` **Turn Off Auto-Loading and Simulate Character Respawn** This script turns off auto-loading and simulates character respawning. ```lua local Players = game:GetService("Players") local RESPAWN_DELAY = 5 Players.CharacterAutoLoads = false local function onPlayerAdded(player) local function onCharacterAdded(character) local humanoid = character:WaitForChild("Humanoid") local function onDied() task.wait(RESPAWN_DELAY) player:LoadCharacterAsync() end humanoid.Died:Connect(onDied) end player.CharacterAdded:Connect(onCharacterAdded) player:LoadCharacterAsync() end Players.PlayerAdded:Connect(onPlayerAdded) ``` ### Method: Player:LoadCharacterWithHumanoidDescriptionAsync **Signature:** `Player:LoadCharacterWithHumanoidDescriptionAsync(humanoidDescription: HumanoidDescription, assetTypeVerification?: AssetTypeVerification): ()` This method spawns a player character with everything equipped in the passed in [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). After calling this method for an individual player, it is not recommended to call it again for the same player until after that player's [CharacterAppearanceLoaded](/docs/reference/engine/classes/Player.md) event has fired. See also [HumanoidDescription System](/docs/en-us/characters/appearance.md#humanoiddescription), an article which explains the humanoid description system in greater detail and provides several scripting examples. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `humanoidDescription` | `HumanoidDescription` | | A [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) containing traits like body parts/colors, body scaling, accessories, clothing, and animations that will be equipped to the loaded character. | | `assetTypeVerification` | `AssetTypeVerification` | `Default` | The asset type verification mode. | **Returns:** `()` **Spawn Characters With HumanoidDescription** To create a `HumanoidDescription` and then spawn a character with that description applied, add a `Script` (not a `LocalScript`) to the workspace and add this code to it. ```lua local Players = game:GetService("Players") Players.CharacterAutoLoads = false local function onPlayerAdded(player) local humanoidDescription = Instance.new("HumanoidDescription") humanoidDescription.HatAccessory = "2551510151,2535600138" humanoidDescription.BodyTypeScale = 0.1 humanoidDescription.ClimbAnimation = 619521311 humanoidDescription.Face = 86487700 humanoidDescription.GraphicTShirt = 1711661 humanoidDescription.HeadColor = Color3.new(0, 1, 0) player:LoadCharacterWithHumanoidDescriptionAsync(humanoidDescription) end Players.PlayerAdded:Connect(onPlayerAdded) ``` ### Method: Player:Move **Signature:** `Player:Move(walkDirection: Vector3, relativeToCamera?: boolean): ()` This method causes the player's character to walk in the given direction until stopped, or interrupted by the player (by using their controls). This is useful when scripting NPC [Humanoids](/docs/reference/engine/classes/Humanoid.md) that move around a map but are not controlled by an actual player's input. Note that the function's second argument indicates whether the provided [Vector3](/docs/reference/engine/datatypes/Vector3.md) should move the player relative to world coordinates (`false`) or the player's [Camera](/docs/reference/engine/classes/Camera.md) (`true`). *Security: None · Thread Safety: Unsafe · Capabilities: Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `walkDirection` | `Vector3` | | The Vector3 direction that the player should move. | | `relativeToCamera` | `boolean` | `false` | A boolean indicating whether the player should move relative to the player's camera. | **Returns:** `()` **Moving the Player relative to their Camera** Demonstrates moving a player relative to their camera's position using [Player:Move()](/docs/reference/engine/classes/Player.md). The script first waits for the player's Character and Humanoid to load, as both are required before calling [Player:Move()](/docs/reference/engine/classes/Player.md). Otherwise a warning will display in the Output. ```lua local Players = game:GetService("Players") local localPlayer = Players.LocalPlayer -- Wait for the player's character and humanoid, which must exist before calling :Move() local character = localPlayer.Character or localPlayer.CharacterAdded:Wait() character:WaitForChild("Humanoid") -- The player will move until they are 50 studs away from the camera's position at the time of running localPlayer:Move(Vector3.new(0, 0, -50), true) ``` ### Method: Player:RemoveReplicationFocus **Signature:** `Player:RemoveReplicationFocus(part: BasePart): ()` This method removes a replication focus previously added by [AddReplicationFocus()](/docs/reference/engine/classes/Player.md). Has no effect in experiences that are not streaming enabled. This method should only be called on the server. It has no effect when called from a [LocalScript](/docs/reference/engine/classes/LocalScript.md). *Security: None · Thread Safety: Unsafe · Capabilities: Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `part` | `BasePart` | | The [BasePart](/docs/reference/engine/classes/BasePart.md) to remove as a replication focus. | **Returns:** `()` ### Method: Player:RequestStreamAroundAsync **Signature:** `Player:RequestStreamAroundAsync(position: Vector3, timeOut?: double): ()` For experiences where [instance streaming](/docs/en-us/workspace/streaming.md) is enabled, requests that the server stream to the player regions (parts and terrain) around the specified **X**, **Y**, **Z** location in the 3D world. It is useful if the experience knows that the player's [CFrame](/docs/reference/engine/datatypes/CFrame.md) will be set to the specified location in the near future. Without providing the location with this call, the player may not have streamed in content for the destination, resulting in a streaming pause or other undesirable behavior. The effect of this call will be temporary and there are no guarantees of what will be streamed in around the specified location. Client memory limits and network conditions may impact what will be available on the client. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `position` | `Vector3` | | World location where streaming is requested. | | `timeOut` | `double` | `0` | Optional timeout for the request, the maximum duration that the engine attempts to stream regions around the `position` parameter before abandoning the request. If you don't specify a value, the timeout is effectively infinite. However, if the client is low on memory, the engine abandons all streaming requests, even those that are still within the timeout duration. | **Returns:** `()` ### Method: Player:SetAccountAge **Signature:** `Player:SetAccountAge(accountAge: int): ()` This method sets the [AccountAge](/docs/reference/engine/classes/Player.md) of the player in days, meaning the age of the account itself relative to when it was first created. *Security: PluginSecurity · Thread Safety: Unsafe · Capabilities: Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `accountAge` | `int` | | The age of the account in days. | **Returns:** `()` ### Method: Player:SetSuperSafeChat **Signature:** `Player:SetSuperSafeChat(value: boolean): ()` This method sets whether or not the player sees chat filtered by [TextService:FilterStringAsync()](/docs/reference/engine/classes/TextService.md) rather than normal chats. ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer player:SetSuperSafeChat(true) ``` Regardless of whether a player has filtered chat enabled, all chat should be filtered by [TextService](/docs/reference/engine/classes/TextService.md) when broadcast to other players or on the player's own screen. [TextService:FilterStringAsync()](/docs/reference/engine/classes/TextService.md) returns a [TextFilterResult](/docs/reference/engine/classes/TextFilterResult.md) object that can be filtered differently according to the message's intended use. *Security: PluginSecurity · Thread Safety: Unsafe · Capabilities: Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `value` | `boolean` | | A boolean indicating whether or not the player sees filtered chat. | **Returns:** `()` ### Method: Player:GetFriendsOnline **Signature:** `Player:GetFriendsOnline(maxFriends?: int): Array` > **Deprecated:** This method has been superseded by [GetFriendsOnlineAsync()](/docs/reference/engine/classes/Player.md). Returns a dictionary of online friends. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Players, Social* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `maxFriends` | `int` | `200` | The maximum number of online friends to return. | **Returns:** `Array` — A dictionary of online friends (see the table above). **Get a List of Online Connections** This example demonstrates how to get a dictionary of a player's online friends. It returns the maximum number of friends specified by the argument, or 200 if an argument is not provided. ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local success, result = pcall(player.GetFriendsOnlineAsync, player, 10) if success then for _, friend in pairs(result) do print(friend.UserName) end else warn("Failed to get online players: " .. result) end ``` ### Method: Player:GetRankInGroup **Signature:** `Player:GetRankInGroup(groupId: int64): int` > **Deprecated:** This method has been superseded by [GetRankInGroupAsync()](/docs/reference/engine/classes/Player.md). Only public roles are considered. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Players, Groups* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `groupId` | `int64` | | The `groupId` of the specified group. | **Returns:** `int` — The player's rank in the group. **How to Check a Player's Rank in a Group** The code below will check if a player that has entered the game has a rank equal to 255, in a group with an ID of 2. If they are, it will print "Player is the owner of the group, 'LOL'!", otherwise "Player is NOT the owner of the group, 'LOL'!" will be printed to the output. ```lua local Players = game:GetService("Players") local function onPlayerAdded(player) if player:GetRankInGroupAsync(2) == 255 then print("Player is the owner of the group, 'LOL'!") else print("Player is NOT the owner of the group, 'LOL'!") end end Players.PlayerAdded:Connect(onPlayerAdded) ``` ### Method: Player:GetRankInGroupAsync **Signature:** `Player:GetRankInGroupAsync(groupId: int64): int` > **Deprecated:** This method only returns a single role rank and may produce arbitrary results when a user holds multiple roles. Use [GroupService:GetRolesInGroupAsync()](/docs/reference/engine/classes/GroupService.md) instead, which returns all roles. This method returns the player's rank in the group as an integer between `0` and `255`, where `0` is a non-member and `255` is the group's owner. Only public roles are considered. This call may not yield the most up-to-date information. If a player leaves a group while they are in the experience, `GetRankInGroupAsync()` will still think they're in that group until they leave. However, this does not happen when used with a [LocalScript](/docs/reference/engine/classes/LocalScript.md) because the method caches results, so multiple calls of `GetRankInGroupAsync()` on the same player with the same group ID will yield the same result as when the method was first called with the given group ID. The caching behavior is on a per-peer basis: a server does not share the same cache as a client. When a player joins a group in-experience due to a call to [GroupService:PromptJoinAsync()](/docs/reference/engine/classes/GroupService.md), any cached value for that player will be cleared on the client where the prompt was shown. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Players, Groups* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `groupId` | `int64` | | The `groupId` of the specified group. | **Returns:** `int` — The player's rank in the group. **How to Check a Player's Rank in a Group** The code below will check if a player that has entered the game has a rank equal to 255, in a group with an ID of 2. If they are, it will print "Player is the owner of the group, 'LOL'!", otherwise "Player is NOT the owner of the group, 'LOL'!" will be printed to the output. ```lua local Players = game:GetService("Players") local function onPlayerAdded(player) if player:GetRankInGroupAsync(2) == 255 then print("Player is the owner of the group, 'LOL'!") else print("Player is NOT the owner of the group, 'LOL'!") end end Players.PlayerAdded:Connect(onPlayerAdded) ``` ### Method: Player:GetRoleInGroup **Signature:** `Player:GetRoleInGroup(groupId: int64): string` > **Deprecated:** This method has been superseded by [GetRoleInGroup()](/docs/reference/engine/classes/Player.md). Only public roles are considered. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Players, Groups* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `groupId` | `int64` | | The group ID of the specified group. | **Returns:** `string` — The player's role in the specified group, or `Guest` if the player is not a member. **How to Check a Player's Role in a Group** The code below will print the name of the rank that the player is currently a part of, in a specific group. In this instance we're checking what rank the player is within a group which has a group ID of 2. ```lua local Players = game:GetService("Players") local function onPlayerAdded(player) print("Player is ranked as '", player:GetRoleInGroupAsync(2), "' in group, 'LOL'!") end Players.PlayerAdded:Connect(onPlayerAdded) ``` ### Method: Player:GetRoleInGroupAsync **Signature:** `Player:GetRoleInGroupAsync(groupId: int64): string` > **Deprecated:** This method only returns a single role name and may produce arbitrary results when a user holds multiple roles. Use [GroupService:GetRolesInGroupAsync()](/docs/reference/engine/classes/GroupService.md) instead, which returns all roles. This method returns the player's role in the group as a string, or `Guest` if the player isn't part of the group. Only public roles are considered. This call may not yield the most up-to-date information. If a player leaves a group while they are in the experience, `GetRoleInGroupAsync()` will still think they're in that group until they leave. However, this does not happen when used with a [LocalScript](/docs/reference/engine/classes/LocalScript.md) because the method caches results, so multiple calls of `GetRoleInGroupAsync()` on the same player with the same group ID will yield the same result as when the method was first called with the given group ID. The caching behavior is on a per-peer basis: a server does not share the same cache as a client. When a player joins a group in-experience due to a call to [GroupService:PromptJoinAsync()](/docs/reference/engine/classes/GroupService.md), any cached value for that player will be cleared on the client where the prompt was shown. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Players, Groups* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `groupId` | `int64` | | The group ID of the specified group. | **Returns:** `string` — The player's role in the specified group, or `Guest` if the player is not a member. **How to Check a Player's Role in a Group** The code below will print the name of the rank that the player is currently a part of, in a specific group. In this instance we're checking what rank the player is within a group which has a group ID of 2. ```lua local Players = game:GetService("Players") local function onPlayerAdded(player) print("Player is ranked as '", player:GetRoleInGroupAsync(2), "' in group, 'LOL'!") end Players.PlayerAdded:Connect(onPlayerAdded) ``` ### Method: Player:IsBestFriendsWith **Signature:** `Player:IsBestFriendsWith(userId: User): boolean` > **Deprecated:** This function is obsolete because the "best friends" feature was removed. Use [Player:IsFriendsWithAsync()](/docs/reference/engine/classes/Player.md) instead. This function was once used to return whether a player is best friends with the specified user, but the feature has since been removed. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `userId` | `User` | | | **Returns:** `boolean` ### Method: Player:IsFriendsWith **Signature:** `Player:IsFriendsWith(userId: User): boolean` > **Deprecated:** This method has been superseded by the [Player:IsFriendsWithAsync()](/docs/reference/engine/classes/Player.md) method which should be used for new work. Checks whether a player is a friend of the user with the given [Player.UserId](/docs/reference/engine/classes/Player.md). *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Players, Social* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `userId` | `User` | | The [Player.UserId](/docs/reference/engine/classes/Player.md) of the specified player. | **Returns:** `boolean` — A boolean indicating whether a player is a friend of the specified user. ### Method: Player:isFriendsWith **Signature:** `Player:isFriendsWith(userId: User): boolean` > **Deprecated:** This method has been superseded by the [Player:IsFriendsWithAsync()](/docs/reference/engine/classes/Player.md) method which should be used for new work. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `userId` | `User` | | | **Returns:** `boolean` ### Method: Player:IsInGroup **Signature:** `Player:IsInGroup(groupId: int64): boolean` > **Deprecated:** This method has been superseded by [IsInGroupAsync()](/docs/reference/engine/classes/Player.md). Checks whether a player is a member of a group with the given ID. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Players, Groups* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `groupId` | `int64` | | The group ID of the specified group. | **Returns:** `boolean` — A boolean indicating whether the player is in the specified group. ### Method: Player:LoadBoolean **Signature:** `Player:LoadBoolean(key: string): boolean` > **Deprecated:** This item is deprecated, as it may have been used for a now obsolete data persistence method. Please save and load player data using [DataStoreService](/docs/reference/engine/classes/DataStoreService.md) for new work. This function returns a boolean value that was previously saved to the player with [Player:SaveBoolean()](/docs/reference/engine/classes/Player.md) with the same key. Returns false if the key doesn't exist, not `nil`. *Security: None · Thread Safety: Unsafe · Capabilities: Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | | **Returns:** `boolean` ### Method: Player:loadBoolean **Signature:** `Player:loadBoolean(key: string): boolean` > **Deprecated:** This deprecated function is a variant of [Player:LoadBoolean()](/docs/reference/engine/classes/Player.md) which has also been deprecated. Neither function should be used in new work. *Security: None · Thread Safety: Unsafe · Capabilities: Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | | **Returns:** `boolean` ### Method: Player:LoadCharacter **Signature:** `Player:LoadCharacter(): ()` > **Deprecated:** This method has been superseded by [LoadCharacterAsync()](/docs/reference/engine/classes/Player.md). Creates a new character for the player, removing the old one. Also clears the player's [Backpack](/docs/reference/engine/classes/Backpack.md) and [PlayerGui](/docs/reference/engine/classes/PlayerGui.md). *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Players* **Returns:** `()` **Turn Off Auto-Loading and Simulate Character Respawn** This script turns off auto-loading and simulates character respawning. ```lua local Players = game:GetService("Players") local RESPAWN_DELAY = 5 Players.CharacterAutoLoads = false local function onPlayerAdded(player) local function onCharacterAdded(character) local humanoid = character:WaitForChild("Humanoid") local function onDied() task.wait(RESPAWN_DELAY) player:LoadCharacterAsync() end humanoid.Died:Connect(onDied) end player.CharacterAdded:Connect(onCharacterAdded) player:LoadCharacterAsync() end Players.PlayerAdded:Connect(onPlayerAdded) ``` ### Method: Player:LoadCharacterAppearance **Signature:** `Player:LoadCharacterAppearance(assetInstance: Instance): ()` The LoadCharacterAppearance [Player](/docs/reference/engine/classes/Player.md) function places the given instance either in the player's [Player.Character](/docs/reference/engine/classes/Player.md), head, or [StarterGear](/docs/reference/engine/classes/StarterGear.md) based on the instance's class. This is useful when giving a player's character an asset from the Roblox catalog, such as a hat or piece of gear. It is similar to [Player:LoadCharacterAsync()](/docs/reference/engine/classes/Player.md), except it does not reload the entire character instance, StarterGear, or [PlayerGui](/docs/reference/engine/classes/PlayerGui.md). Note: - [Accessory](/docs/reference/engine/classes/Accessory.md), [Shirt](/docs/reference/engine/classes/Shirt.md), [ShirtGraphic](/docs/reference/engine/classes/ShirtGraphic.md), [CharacterMesh](/docs/reference/engine/classes/CharacterMesh.md), [BodyColors](/docs/reference/engine/classes/BodyColors.md), and [Accoutrement](/docs/reference/engine/classes/Accoutrement.md) are parented to the player's character. - [Decal](/docs/reference/engine/classes/Decal.md), [FileMesh](/docs/reference/engine/classes/FileMesh.md), [SpecialMesh](/docs/reference/engine/classes/SpecialMesh.md), [BlockMesh](/docs/reference/engine/classes/BlockMesh.md), [CylinderMesh](/docs/reference/engine/classes/CylinderMesh.md), and [Texture](/docs/reference/engine/classes/Texture.md) are parented to the character's head. - [Tool](/docs/reference/engine/classes/Tool.md) is parented to the player's [StarterGear](/docs/reference/engine/classes/StarterGear.md). - All other classes are ignored. *Security: None · Thread Safety: Unsafe · Capabilities: AvatarAppearance, Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `assetInstance` | `Instance` | | An instance of the asset being loaded, which can be obtained using the [InsertService:LoadAsset()](/docs/reference/engine/classes/InsertService.md) function. | **Returns:** `()` **Player:LoadCharacterAppearance** This script gives any Hat/Gear/Face/Body Part to a player once they chat the URL of the asset. ```lua local Players = game:GetService("Players") local InsertService = game:GetService("InsertService") local function onPlayerAdded(player) local function onChatted(message) local assetId = message:match("id=(%d+)") if assetId then local model = InsertService:LoadAsset(assetId) for _, child in pairs(model:GetChildren()) do player:LoadCharacterAppearance(child) end end end player.Chatted:Connect(onChatted) end Players.PlayerAdded:Connect(onPlayerAdded) ``` ### Method: Player:LoadCharacterWithHumanoidDescription **Signature:** `Player:LoadCharacterWithHumanoidDescription(humanoidDescription: HumanoidDescription, assetTypeVerification?: AssetTypeVerification): ()` > **Deprecated:** This method has been superseded by [LoadCharacterWithHumanoidDescriptionAsync()](/docs/reference/engine/classes/Player.md). Spawns a player character with everything equipped in the passed in [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `humanoidDescription` | `HumanoidDescription` | | A [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md) containing traits like body parts/colors, body scaling, accessories, clothing, and animations that will be equipped to the loaded character. | | `assetTypeVerification` | `AssetTypeVerification` | `Default` | The asset type verification mode. | **Returns:** `()` **Spawn Characters With HumanoidDescription** To create a `HumanoidDescription` and then spawn a character with that description applied, add a `Script` (not a `LocalScript`) to the workspace and add this code to it. ```lua local Players = game:GetService("Players") Players.CharacterAutoLoads = false local function onPlayerAdded(player) local humanoidDescription = Instance.new("HumanoidDescription") humanoidDescription.HatAccessory = "2551510151,2535600138" humanoidDescription.BodyTypeScale = 0.1 humanoidDescription.ClimbAnimation = 619521311 humanoidDescription.Face = 86487700 humanoidDescription.GraphicTShirt = 1711661 humanoidDescription.HeadColor = Color3.new(0, 1, 0) player:LoadCharacterWithHumanoidDescriptionAsync(humanoidDescription) end Players.PlayerAdded:Connect(onPlayerAdded) ``` ### Method: Player:LoadInstance **Signature:** `Player:LoadInstance(key: string): Instance` > **Deprecated:** This item is deprecated, as it may have been used for a now obsolete data persistence method. Please save and load player data using [DataStoreService](/docs/reference/engine/classes/DataStoreService.md) for new work. This function returns an instance that was previously saved to the player with [Player:SaveInstance()](/docs/reference/engine/classes/Player.md) with the same key. Returns `nil` if the key doesn't exist. *Security: None · Thread Safety: Unsafe · Capabilities: Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | | **Returns:** `Instance` **Player:LoadInstance** The below example would reparent the saved instance to Workspace. ```lua local Players = game:GetService("Players") local function onPlayerAdded(player) if player:WaitForDataReady() then local model = player:LoadInstance("Model") model.Parent = workspace end end Players.PlayerAdded:Connect(onPlayerAdded) ``` ### Method: Player:loadInstance **Signature:** `Player:loadInstance(key: string): Instance` > **Deprecated:** This deprecated function is a variant of [Player:LoadInstance()](/docs/reference/engine/classes/Player.md) which has also been deprecated. Neither function should be used in new work. *Security: None · Thread Safety: Unsafe · Capabilities: Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | | **Returns:** `Instance` ### Method: Player:LoadNumber **Signature:** `Player:LoadNumber(key: string): double` > **Deprecated:** This item is deprecated, as it may have been used for a now obsolete data persistence method. Please save and load player data using [DataStoreService](/docs/reference/engine/classes/DataStoreService.md) for new work. This function was once used by an ancient data persistence method to return a number value that was previously saved to the player with [Player:SaveNumber()](/docs/reference/engine/classes/Player.md) with the same key. Returns 0 if the key doesn't exist, not `nil`. *Security: None · Thread Safety: Unsafe · Capabilities: Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | | **Returns:** `double` **Player:LoadNumber** The below would print the value of the player's TotalPlays. ```lua local Players = game:GetService("Players") local function onPlayerAdded(player) if player:WaitForDataReady() then print(player:LoadNumber("TotalPlays")) end end Players.PlayerAdded:Connect(onPlayerAdded) ``` ### Method: Player:loadNumber **Signature:** `Player:loadNumber(key: string): double` > **Deprecated:** This deprecated function is a variant of [Player:LoadNumber()](/docs/reference/engine/classes/Player.md) which has also been deprecated. Neither function should be used in new work. *Security: None · Thread Safety: Unsafe · Capabilities: Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | | **Returns:** `double` ### Method: Player:LoadString **Signature:** `Player:LoadString(key: string): string` > **Deprecated:** This item is deprecated, as it may have been used for a now obsolete data persistence method. Please save and load player data using [DataStoreService](/docs/reference/engine/classes/DataStoreService.md) for new work. This function returns a string value that was previously saved to the player with [Player:SaveString()](/docs/reference/engine/classes/Player.md) with the same key. Returns an empty string ("") if the key doesn't exist, not `nil`. *Security: None · Thread Safety: Unsafe · Capabilities: Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | | **Returns:** `string` **Player:LoadString** The below example would print the value of the previously saved key "TheirName". ```lua game.Players.PlayerAdded:Connect(function(Player) if Player:WaitForDataReady() then print(Player:LoadString("TheirName")) end end) ``` ### Method: Player:loadString **Signature:** `Player:loadString(key: string): string` > **Deprecated:** This function is a deprecated variant of [Player:LoadString()](/docs/reference/engine/classes/Player.md) which has also been deprecated. Neither function should be used in new work. *Security: None · Thread Safety: Unsafe · Capabilities: Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | | **Returns:** `string` ### Method: Player:SaveBoolean **Signature:** `Player:SaveBoolean(key: string, value: boolean): ()` > **Deprecated:** This item is deprecated, as it may have been used for a now obsolete data persistence method. Please save and load player data using [DataStoreService](/docs/reference/engine/classes/DataStoreService.md) for new work. This function is used to save a boolean value that can be loaded again at a later time using [Player:LoadBoolean()](/docs/reference/engine/classes/Player.md). *Security: None · Thread Safety: Unsafe · Capabilities: Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | | | `value` | `boolean` | | | **Returns:** `()` **Player:SaveBoolean** The below example would save a boolean to the player with a key of "HasPlayed" and a value of true. ```lua game.Players.PlayerAdded:Connect(function(Player) if Player:WaitForDataReady() then Player:SaveBoolean("HasPlayed", true) end end) ``` ### Method: Player:saveBoolean **Signature:** `Player:saveBoolean(key: string, value: boolean): ()` > **Deprecated:** This function is a deprecated variant of [Player:SaveBoolean()](/docs/reference/engine/classes/Player.md) which has also been deprecated. Neither function should be used in new work. *Security: None · Thread Safety: Unsafe · Capabilities: Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | | | `value` | `boolean` | | | **Returns:** `()` ### Method: Player:SaveInstance **Signature:** `Player:SaveInstance(key: string, value: Instance): ()` > **Deprecated:** This item is deprecated, as it may have been used for a now obsolete data persistence method. Please save and load player data using [DataStoreService](/docs/reference/engine/classes/DataStoreService.md) for new work. This function was once used by an ancient data persistence method to save an instance which can be loaded again at a later time using [Player:LoadInstance()](/docs/reference/engine/classes/Player.md).. *Security: None · Thread Safety: Unsafe · Capabilities: Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | | | `value` | `Instance` | | | **Returns:** `()` **Player:SaveInstance** The below code will save 'Model', a descendant of Workspace, to the player each time they join the game. To access this instance again, we would need to index it by it's key, 'Model', as defined in the first argument of this method. ```lua game.Players.PlayerAdded:Connect(function(Player) Player:WaitForDataReady() Player:SaveInstance("Model", game.Workspace.Model) end) ``` ### Method: Player:saveInstance **Signature:** `Player:saveInstance(key: string, value: Instance): ()` > **Deprecated:** This function is a deprecated variant of [Player:SaveInstance()](/docs/reference/engine/classes/Player.md) which has also been deprecated. Neither function should be used in new work. *Security: None · Thread Safety: Unsafe · Capabilities: Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | | | `value` | `Instance` | | | **Returns:** `()` ### Method: Player:SaveNumber **Signature:** `Player:SaveNumber(key: string, value: double): ()` > **Deprecated:** This item is deprecated, as it may have been used for a now obsolete data persistence method. Please save and load player data using [DataStoreService](/docs/reference/engine/classes/DataStoreService.md) for new work. This function was once used by an ancient data persistence method to save a number value that can be loaded again at a later time using [Player:LoadNumber()](/docs/reference/engine/classes/Player.md). *Security: None · Thread Safety: Unsafe · Capabilities: Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | | | `value` | `double` | | | **Returns:** `()` **Player:SaveNumber** The below example would save a number to the player with a key of "TotalPlays" and add one to the value each time. ```lua game.Players.PlayerAdded:Connect(function(Player) if Player:WaitForDataReady() then local OldValue = Player:SaveNumber("TotalPlays") Player:SaveNumber("TotalPlays", OldValue + 1) end end) ``` ### Method: Player:saveNumber **Signature:** `Player:saveNumber(key: string, value: double): ()` > **Deprecated:** This function is a deprecated variant of [Player:SaveNumber()](/docs/reference/engine/classes/Player.md) which has also been deprecated. Neither function should be used in new work. *Security: None · Thread Safety: Unsafe · Capabilities: Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | | | `value` | `double` | | | **Returns:** `()` ### Method: Player:SaveString **Signature:** `Player:SaveString(key: string, value: string): ()` > **Deprecated:** This item is deprecated, as it may have been used for a now obsolete data persistence method. Please save and load player data using [DataStoreService](/docs/reference/engine/classes/DataStoreService.md) for new work. This function was once used by an ancient data persistence method to save a string value that can be loaded again at a later time using [Player:LoadString()](/docs/reference/engine/classes/Player.md). *Security: None · Thread Safety: Unsafe · Capabilities: Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | | | `value` | `string` | | | **Returns:** `()` **Player:SaveString** The below example would save a string to the player with a key of "TheirName" and a value of their current name. If you printed it, it would print their name. ```lua local Players = game:GetService("Players") local function onPlayerAdded(player) if player:WaitForDataReady() then player:SaveString("TheirName", player.Name) end end Players.PlayerAdded:Connect(onPlayerAdded) ``` ### Method: Player:saveString **Signature:** `Player:saveString(key: string, value: string): ()` > **Deprecated:** This function is a deprecated variant of [Player:SaveString()](/docs/reference/engine/classes/Player.md) which has also been deprecated. Neither function should be used in new work. *Security: None · Thread Safety: Unsafe · Capabilities: Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | | | `value` | `string` | | | **Returns:** `()` ### Method: Player:WaitForDataReady **Signature:** `Player:WaitForDataReady(): boolean` > **Deprecated:** This item is deprecated, as it may have been used for a now obsolete data persistence method. Please save and load player data using [DataStoreService](/docs/reference/engine/classes/DataStoreService.md) for new work. This function is used to pause the script until the player's data is available to manipulate, or until a certain amount of time has elapsed without fetching the player's data *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Players* **Returns:** `boolean` ### Method: Player:waitForDataReady **Signature:** `Player:waitForDataReady(): boolean` > **Deprecated:** This function is a deprecated variant of [Player:WaitForDataReady()](/docs/reference/engine/classes/Player.md) which has also been deprecated. Neither function should be used in new work. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Players* **Returns:** `boolean` ## Events ### Event: Player.CharacterAdded **Signature:** `Player.CharacterAdded(character: Model)` This event fires when a player's character spawns or respawns. It fires soon after setting [Character](/docs/reference/engine/classes/Player.md) to a non-`nil` value or calling [LoadCharacterAsync()](/docs/reference/engine/classes/Player.md), which is before the character is parented to the [Workspace](/docs/reference/engine/classes/Workspace.md). This can be used alongside the [CharacterRemoving](/docs/reference/engine/classes/Player.md) event which fires right before a player's character is about to be removed, typically after death. As such, both of these events can potentially fire many times as players die then respawn in a place. Note that the [Humanoid](/docs/reference/engine/classes/Humanoid.md) and its default body parts (head, torso, and limbs) will exist on the server when this event fires, but clothing items like [Hats](/docs/reference/engine/classes/Hat.md), [Shirts](/docs/reference/engine/classes/Shirt.md), and [Pants](/docs/reference/engine/classes/Pants.md) might take a few seconds to be added to the character. The parts will also take time to replicate to clients. Connect [Instance.ChildAdded](/docs/reference/engine/classes/Instance.md) on the added character to detect these, or wait for the [CharacterAppearanceLoaded](/docs/reference/engine/classes/Player.md) event to be sure the character has everything equipped. If you instead need to track when a player joins/leaves the experience, use the events [Players.PlayerAdded](/docs/reference/engine/classes/Players.md) and [Players.PlayerRemoving](/docs/reference/engine/classes/Players.md). *Security: None · Capabilities: Players* **Parameters:** | Name | Type | Description | |------|------|-------------| | `character` | `Model` | An instance of the character that spawned/respawned. | **Detecting Player Spawns and Despawns** This code sample demonstrates the usage of [Players.PlayerAdded](/docs/reference/engine/classes/Players.md), [Player.CharacterAdded](/docs/reference/engine/classes/Player.md) and [Player.CharacterRemoving](/docs/reference/engine/classes/Player.md) in order to detect the spawning and despawning of players' characters. You can use this as a boilerplate script to make changes to players' characters as they spawn, such as changing [Humanoid.WalkSpeed](/docs/reference/engine/classes/Humanoid.md). ```lua local Players = game:GetService("Players") local function onCharacterAdded(character) print(character.Name .. " has spawned") end local function onCharacterRemoving(character) print(character.Name .. " is despawning") end local function onPlayerAdded(player) player.CharacterAdded:Connect(onCharacterAdded) player.CharacterRemoving:Connect(onCharacterRemoving) end Players.PlayerAdded:Connect(onPlayerAdded) ``` **Expected output:** Player1 has spawned Player1 is despawning **Accessory Remover** This code sample automatically removes `Accessory` objects like hats from the `Player`'s character when they respawn. Warning: this includes hair, so this script may cause acute baldness. When the [Character()](/docs/reference/engine/classes/Player.md) is [added](/docs/reference/engine/classes/Player.md), we wait for [RunService.Stepped](/docs/reference/engine/classes/RunService.md) to fire once (using the `wait` function of events). This is so the accessory removal logic runs one frame after the character spawns. A warning can appear if you delete accessories too quickly after the player spawns, so waiting one frame will avoid that. ```lua local Players = game:GetService("Players") local RunService = game:GetService("RunService") local function destroyAccessory(object) if object:IsA("Hat") or object:IsA("Accessory") then object:Destroy() end end local function onCharacterAdded(character) -- Wait a brief moment before removing accessories to avoid the -- "Something unexpectedly set ___ parent to NULL" warning RunService.Stepped:Wait() -- Check for any existing accessories in the player's character for _, child in pairs(character:GetChildren()) do destroyAccessory(child) end -- Hats may be added to the character a moment after -- CharacterAdded fires, so we listen for those using ChildAdded character.ChildAdded:Connect(destroyAccessory) end local function onPlayerAdded(player) player.CharacterAdded:Connect(onCharacterAdded) end Players.PlayerAdded:Connect(onPlayerAdded) ``` ### Event: Player.CharacterAppearanceLoaded **Signature:** `Player.CharacterAppearanceLoaded(character: Model)` This event fires when the full appearance of a [Character](/docs/reference/engine/classes/Player.md) has been inserted. It only fires on the server. A [Character](/docs/reference/engine/classes/Player.md) generally has a range of objects modifying its appearance, including [Accoutrements](/docs/reference/engine/classes/Accoutrement.md), [Shirts](/docs/reference/engine/classes/Shirt.md), [Pants](/docs/reference/engine/classes/Pants.md) and [CharacterMeshes](/docs/reference/engine/classes/CharacterMesh.md). This event will fire when all such objects have been inserted into the character. For custom character implementations, such as using a character model named `StarterCharacter` inside [StarterPlayer](/docs/reference/engine/classes/StarterPlayer.md), use [CharacterAdded](/docs/reference/engine/classes/Player.md) and handle your own accessories. One use for this event is to ensure all accessories have loaded before destroying them. See below for an example of this. *Security: None · Capabilities: Players* **Parameters:** | Name | Type | Description | |------|------|-------------| | `character` | `Model` | The [Player.Character](/docs/reference/engine/classes/Player.md) [Model](/docs/reference/engine/classes/Model.md). | **Remove Accessories After Loading** This code sample will wait for accessories to fully load, print out how many there are, and then destroy them all. ```lua local Players = game:GetService("Players") local function onPlayerAddedAsync(player) local connection = player.CharacterAppearanceLoaded:Connect(function(character) -- All accessories have loaded at this point local humanoid = character:FindFirstChildOfClass("Humanoid") local numAccessories = #humanoid:GetAccessories() print(("Destroying %d accessories for %s"):format(numAccessories, player.Name)) humanoid:RemoveAccessories() end) -- Make sure we disconnect our connection to the player after they leave -- to allow the player to get garbage collected player.AncestryChanged:Wait() connection:Disconnect() end for _, player in Players:GetPlayers() do task.spawn(onPlayerAddedAsync, player) end Players.PlayerAdded:Connect(onPlayerAddedAsync) ``` ### Event: Player.CharacterRemoving **Signature:** `Player.CharacterRemoving(character: Model)` This event fires right before a player's [Character](/docs/reference/engine/classes/Player.md) is removed, such as when the player is respawning. This can be used alongside the [CharacterAdded](/docs/reference/engine/classes/Player.md) event which fires when a player's character spawns or respawns. If you instead need to track when a player joins/leaves the experience, use the events [Players.PlayerAdded](/docs/reference/engine/classes/Players.md) and [Players.PlayerRemoving](/docs/reference/engine/classes/Players.md). *Security: None · Capabilities: Players* **Parameters:** | Name | Type | Description | |------|------|-------------| | `character` | `Model` | An instance of the character that is being removed. | **Detecting Player Spawns and Despawns** This code sample demonstrates the usage of [Players.PlayerAdded](/docs/reference/engine/classes/Players.md), [Player.CharacterAdded](/docs/reference/engine/classes/Player.md) and [Player.CharacterRemoving](/docs/reference/engine/classes/Player.md) in order to detect the spawning and despawning of players' characters. You can use this as a boilerplate script to make changes to players' characters as they spawn, such as changing [Humanoid.WalkSpeed](/docs/reference/engine/classes/Humanoid.md). ```lua local Players = game:GetService("Players") local function onCharacterAdded(character) print(character.Name .. " has spawned") end local function onCharacterRemoving(character) print(character.Name .. " is despawning") end local function onPlayerAdded(player) player.CharacterAdded:Connect(onCharacterAdded) player.CharacterRemoving:Connect(onCharacterRemoving) end Players.PlayerAdded:Connect(onPlayerAdded) ``` **Expected output:** Player1 has spawned Player1 is despawning ### Event: Player.Chatted **Signature:** `Player.Chatted(message: string, recipient: Player)` This event fires when a [Player](/docs/reference/engine/classes/Player.md) types a message and presses Enter in Roblox's provided chat bar. This is done using some Luau bindings by the default chat script. You can prevent players from chatting by using [StarterGui:SetCoreGuiEnabled()](/docs/reference/engine/classes/StarterGui.md) and setting [CoreGuiType.Chat](/docs/reference/engine/enums/CoreGuiType.md) to `false`. *Security: None · Capabilities: Chat, Players* **Parameters:** | Name | Type | Description | |------|------|-------------| | `message` | `string` | The content of the message the player typed in chat. | | `recipient` | `Player` | **Deprecated.** For whisper messages, this was the Player who was the intended target of the chat message. | ### Event: Player.Idled **Signature:** `Player.Idled(time: double)` This event fires approximately two minutes after the engine classifies the player as idle. Time is the number of seconds that have elapsed since that point. The event continues to fire every 30 seconds for as long as the player remains idle. This event only fires in client scripts, not server scripts; use a [RemoteEvent](/docs/reference/engine/classes/RemoteEvent.md) to notify the server of idle players. Roblox automatically disconnects players that have been idle for at least 20 minutes, so this event is useful for warning players that they will be disconnected soon, disconnecting players prior to those 20 minutes, or other away from keyboard (AFK) features. To track how often automatic disconnects occur, try correlating this event with occurrences of [Players.PlayerRemoving](/docs/reference/engine/classes/Players.md). *Security: None · Capabilities: Players* **Parameters:** | Name | Type | Description | |------|------|-------------| | `time` | `double` | The time in seconds the player has been idle. | ### Event: Player.OnTeleport **Signature:** `Player.OnTeleport(teleportState: TeleportState, placeId: int64, spawnName: string)` This event fires when the [TeleportState](/docs/reference/engine/enums/TeleportState.md) of a player changes. This event is useful for detecting whether a teleportation was successful. *Security: None · Capabilities: Players, Teleport* **Parameters:** | Name | Type | Description | |------|------|-------------| | `teleportState` | `TeleportState` | The new [TeleportState](/docs/reference/engine/enums/TeleportState.md) of the [Player](/docs/reference/engine/classes/Player.md). | | `placeId` | `int64` | The ID of the place the [Player](/docs/reference/engine/classes/Player.md) is being teleported to. | | `spawnName` | `string` | The name of the spawn to teleport to, if [TeleportService:TeleportToSpawnByName()](/docs/reference/engine/classes/TeleportService.md) has been used. | **Player.OnTeleport** This example prints which stage of a teleport a player is at, as well as printing if the teleport was a failure. ```lua local Players = game:GetService("Players") Players.PlayerAdded:Connect(function(player) local playerOnTeleport = player player.OnTeleport:Connect(function(teleportState, _placeId, _spawnName) if teleportState == Enum.TeleportState.Started then print("Teleport started (" .. playerOnTeleport.Name .. ")") elseif teleportState == Enum.TeleportState.WaitingForServer then print("Teleport waiting for server (" .. playerOnTeleport.Name .. ")") elseif teleportState == Enum.TeleportState.InProgress then print("Teleport in progress (" .. playerOnTeleport.Name .. ")") elseif teleportState == Enum.TeleportState.Failed then print("Teleport failed! (" .. playerOnTeleport.Name .. ")") end end) end) ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: PlayerGui last_updated: 2026-06-29T19:34:08Z inherits: - BasePlayerGui - Instance - Object type: class memory_category: Instances tags: - NotCreatable - PlayerReplicated summary: "A container that holds a player's UI." --- # Class: PlayerGui > A container that holds a player's UI. ## Description The `PlayerGui` container stores objects that create the player's GUI. If a [ScreenGui](/docs/reference/engine/classes/ScreenGui.md) is a descendant of `PlayerGui`, any [GuiObject](/docs/reference/engine/classes/GuiObject.md) inside that [ScreenGui](/docs/reference/engine/classes/ScreenGui.md) displays on the player's screen. Any [LocalScript](/docs/reference/engine/classes/LocalScript.md) will also run if it is inserted into a `PlayerGui`. When a player first joins the experience, their `PlayerGui` is automatically inserted into their [Player](/docs/reference/engine/classes/Player.md) object. When the player's [Player.Character](/docs/reference/engine/classes/Player.md) spawns for the first time, all of the contents of [StarterGui](/docs/reference/engine/classes/StarterGui.md) are automatically copied into the player's `PlayerGui`. If [StarterGui.ResetPlayerGuiOnSpawn](/docs/reference/engine/classes/StarterGui.md) is set to `true`, all the contents of a player's `PlayerGui` are cleared and replaced with the contents of [StarterGui](/docs/reference/engine/classes/StarterGui.md) every time the player's character respawns. Note that if [Players.CharacterAutoLoads](/docs/reference/engine/classes/Players.md) is set to `false`, the character won't spawn and [StarterGui](/docs/reference/engine/classes/StarterGui.md) contents won't copy over until [Player:LoadCharacterAsync()](/docs/reference/engine/classes/Player.md) is called. If you need to control a player's UI container during playtime, for example to show/hide a specific [ScreenGui](/docs/reference/engine/classes/ScreenGui.md) or any of its children, access it as follows from a [LocalScript](/docs/reference/engine/classes/LocalScript.md): ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local playerGui = player.PlayerGui ``` ## Properties ### Property: PlayerGui.CurrentScreenOrientation ```json { "type": "ScreenOrientation", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` Describes the player's current screen orientation. ### Property: PlayerGui.ScreenOrientation ```json { "type": "ScreenOrientation", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` Sets the preferred screen orientation mode for this player, if on a mobile device. ### Property: PlayerGui.SelectionImageObject ```json { "type": "GuiObject", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "UI" ] } ``` Overrides the default selection adornment used for gamepads. For best results, this should point to a [GuiObject](/docs/reference/engine/classes/GuiObject.md). ## Methods ### Method: PlayerGui:GetTopbarTransparency **Signature:** `PlayerGui:GetTopbarTransparency(): float` Returns the transparency of the Topbar. *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Returns:** `float` **PlayerGui:GetTopbarTransparency** The following line of code prints the transparency of the topbar. ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local playerGui = player:WaitForChild("PlayerGui") print(playerGui:GetTopbarTransparency()) ``` ### Method: PlayerGui:SetTopbarTransparency **Signature:** `PlayerGui:SetTopbarTransparency(transparency: float): ()` This method sets the transparency of the top bar [CoreGui](/docs/reference/engine/classes/CoreGui.md). A value of `0` is completely opaque and a value of `1` is completely transparent. Values outside of the range `[0, 1]` are clamped. The default transparency of the topbar is `0.5`. Using the [StarterGui:SetCore()](/docs/reference/engine/classes/StarterGui.md) method with the `"TopbarEnabled"` option allows you to enable/disable the entire topbar and all of its features (player list, health, etc). By contrast, this method only affects how the top bar is displayed. *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `transparency` | `float` | | | **Returns:** `()` **PlayerGui:SetTopbarTransparency** The following line of code makes the Topbar appear fully opaque. ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local playerGui = player:WaitForChild("PlayerGui") playerGui:WaitForChild("PlayerGui"):SetTopbarTransparency(0) ``` **Custom Topbar Style** This code sample demonstrates how you can create a custom-styled topbar. Paste it into a [LocalScript](/docs/reference/engine/classes/LocalScript.md) placed within [StarterPlayerScripts](/docs/reference/engine/classes/StarterPlayerScripts.md). ```lua local Players = game:GetService("Players") local TOPBAR_COLOR = Color3.fromRGB(0, 0, 127) local TOPBAR_TRANSPARENCY = 0 local playerGui = Players.LocalPlayer:WaitForChild("PlayerGui") -- Hide the topbar playerGui:SetTopbarTransparency(1) -- Create a "Fake" replacement topbar with a ScreenGui and Frame local screenGui = Instance.new("ScreenGui") local frame = Instance.new("Frame") -- Move (0, 0) to the actual top left corner of the screen, instead of under the topbar screenGui.IgnoreGuiInset = true -- The topbar is 36 pixels tall, and spans the entire width of the screen frame.Size = UDim2.new(1, 0, 0, 36) frame.BackgroundColor3 = TOPBAR_COLOR frame.BackgroundTransparency = TOPBAR_TRANSPARENCY frame.BorderSizePixel = 0 frame.Parent = screenGui screenGui.Parent = playerGui ``` ## Events ### Event: PlayerGui.TopbarTransparencyChangedSignal **Signature:** `PlayerGui.TopbarTransparencyChangedSignal(transparency: float)` Fires when the transparency of the Topbar CoreGui changes. *Security: None · Capabilities: UI* **Parameters:** | Name | Type | Description | |------|------|-------------| | `transparency` | `float` | | **PlayerGui.TopbarTransparencyChangedSignal1** The below example prints out the value of the topbar transparency when it changes. ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local playerGui = player:WaitForChild("PlayerGui") local function onTransparencyChanged(topbarTransparency) print("The topbar transparency is now:", topbarTransparency) end playerGui.TopbarTransparencyChangedSignal:Connect(onTransparencyChanged) ``` ## Inherited Members ### From [BasePlayerGui](/docs/reference/engine/classes/BasePlayerGui.md) - **Method `GetGuiObjectsAtPosition(x: int, y: int): Instances`**: Returns a list of all GuiObject instances occupying the given ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: PlayerListConfiguration last_updated: 2026-06-29T19:34:08Z inherits: - BaseCoreGuiConfiguration - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated --- # Class: PlayerListConfiguration ## Properties ### Property: PlayerListConfiguration.Open ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "UI" ] } ``` ## Inherited Members ### From [BaseCoreGuiConfiguration](/docs/reference/engine/classes/BaseCoreGuiConfiguration.md) - **Property `Enabled`** (`boolean`): ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: PlayerMouse last_updated: 2026-06-29T19:34:08Z inherits: - Mouse - Instance - Object type: class memory_category: Instances tags: - NotCreatable summary: "The PlayerMouse behaves identically to the Mouse object that is obtained using Tool.Equipped. Both PlayerMouse and Mouse are legacy APIs, superseded by UserInputService." --- # Class: PlayerMouse > The PlayerMouse behaves identically to the [Mouse](/docs/reference/engine/classes/Mouse.md) object that is > obtained using [Tool.Equipped](/docs/reference/engine/classes/Tool.md). Both PlayerMouse and [Mouse](/docs/reference/engine/classes/Mouse.md) are > legacy APIs, superseded by [UserInputService](/docs/reference/engine/classes/UserInputService.md). ## Description The PlayerMouse behaves identically to the [Mouse](/docs/reference/engine/classes/Mouse.md) object that is obtained using [Tool.Equipped](/docs/reference/engine/classes/Tool.md). It can be accessed from [LocalScripts](/docs/reference/engine/classes/LocalScript.md) using the local player's [Player:GetMouse()](/docs/reference/engine/classes/Player.md) method. Both PlayerMouse and [Mouse](/docs/reference/engine/classes/Mouse.md) are legacy APIs, superseded by [UserInputService](/docs/reference/engine/classes/UserInputService.md). The only difference between the PlayerMouse and the [Mouse](/docs/reference/engine/classes/Mouse.md) object is the PlayerMouse can be obtained using the [Player:GetMouse()](/docs/reference/engine/classes/Player.md) method. In most cases developers are advised to use the new [UserInputService](/docs/reference/engine/classes/UserInputService.md). However the PlayerMouse and Mouse objects remain supported for a number of reasons. See [Input and Camera](/docs/en-us/input.md) for more information on customizing inputs in your experience. ## Code Samples **PlayerMouse** This code sample includes a simple example of how the local player's `PlayerMouse` can be retrieved using the [Player:GetMouse()](/docs/reference/engine/classes/Player.md) function in a `LocalScript`. This code should be placed in a `LocalScript` in `StarterPlayerScripts`. ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local mouse = player:GetMouse() local function onMouseMove() print("mouse screen position: ", mouse.X, mouse.Y) end mouse.Move:Connect(onMouseMove) ``` ## Inherited Members ### From [Mouse](/docs/reference/engine/classes/Mouse.md) - **Property `Hit`** (`CFrame`): The CFrame of the mouse's position in 3D space. - **Property `hit`** (`CFrame`): *(deprecated, hidden)* - **Property `Icon`** (`ContentId`): The content ID of the image used as the Mouse icon. - **Property `IconContent`** (`Content`): The content of the image used as the Mouse icon. Only supports - **Property `Origin`** (`CFrame`): A CFrame positioned at the Workspace.CurrentCamera and - **Property `Target`** (`BasePart`): The object in 3D space the mouse is pointing to. - **Property `target`** (`BasePart`): *(deprecated)* - **Property `TargetFilter`** (`Instance`): Determines an object (and its descendants) to be ignored when determining - **Property `TargetSurface`** (`NormalId`): Indicates the NormalId of the BasePart surface at which the - **Property `UnitRay`** (`Ray`): A Ray directed towards the mouse's world position, originating - **Property `ViewSizeX`** (`int`): Describes the width of the game window in pixels. - **Property `ViewSizeY`** (`int`): Describes the height of the game window in pixels. - **Property `X`** (`int`): Describes the X (horizontal) component of the mouse's position on the - **Property `Y`** (`int`): Describes the Y (vertical) component of the mouse's screen position. - **Event `Button1Down`**: Fires when the left mouse button is pressed. - **Event `Button1Up`**: Fires when the left mouse button is released. - **Event `Button2Down`**: Fires when the right mouse button is pressed. - **Event `Button2Up`**: Fired when the right mouse button is released. - **Event `Idle`**: Fired during every heartbeat that the mouse isn't being passed to another - **Event `KeyDown`**: Fires when a Key is pressed. *(deprecated)* - **Event `keyDown`**: *(deprecated)* - **Event `KeyUp`**: Fires when a Key is released. *(deprecated)* - **Event `Move`**: Fired when the mouse is moved. - **Event `WheelBackward`**: Fires when the mouse wheel is scrolled backwards. - **Event `WheelForward`**: Fires when the mouse wheel is scrolled forwards. ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: PlayerScripts last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated summary: "A container for client-side scripts to be run inside Player objects within the Players service." --- # Class: PlayerScripts > A container for client-side scripts to be run inside [Player](/docs/reference/engine/classes/Player.md) objects > within the [Players](/docs/reference/engine/classes/Players.md) service. ## Description `PlayerScripts` is a container object located inside [Player](/docs/reference/engine/classes/Player.md) objects within the [Players](/docs/reference/engine/classes/Players.md) service. It is created automatically when a player joins the game and its main purpose is to contain client scripts copied from the [StarterPlayerScripts](/docs/reference/engine/classes/StarterPlayerScripts.md) container within the [StarterPlayer](/docs/reference/engine/classes/StarterPlayer.md) service. Unlike the [Backpack](/docs/reference/engine/classes/Backpack.md) and [PlayerGui](/docs/reference/engine/classes/PlayerGui.md) containers, the `PlayerScripts` container is not accessible to the server and server‑side [Script](/docs/reference/engine/classes/Script.md) objects will not run when parented to `PlayerScripts`. ## Methods ### Method: PlayerScripts:ClearComputerCameraMovementModes **Signature:** `PlayerScripts:ClearComputerCameraMovementModes(): ()` Unregisters all `ComputerCameraMovementMode` enums from the game's settings menu. *Security: None · Thread Safety: Unsafe · Capabilities: Input, Players* **Returns:** `()` ### Method: PlayerScripts:ClearComputerMovementModes **Signature:** `PlayerScripts:ClearComputerMovementModes(): ()` Unregisters all `ComputerMovementMode` enums from the game's settings menu. *Security: None · Thread Safety: Unsafe · Capabilities: Input, Players* **Returns:** `()` ### Method: PlayerScripts:ClearTouchCameraMovementModes **Signature:** `PlayerScripts:ClearTouchCameraMovementModes(): ()` Unregisters all `TouchCameraMovementMode` enums from the game's settings menu. *Security: None · Thread Safety: Unsafe · Capabilities: Input, Players* **Returns:** `()` ### Method: PlayerScripts:ClearTouchMovementModes **Signature:** `PlayerScripts:ClearTouchMovementModes(): ()` Unregisters all `TouchMovementMode` enums from the game's settings menu. *Security: None · Thread Safety: Unsafe · Capabilities: Input, Players* **Returns:** `()` ### Method: PlayerScripts:RegisterComputerCameraMovementMode **Signature:** `PlayerScripts:RegisterComputerCameraMovementMode(cameraMovementMode: ComputerCameraMovementMode): ()` Registers that a computer camera movement mode is available to be selected from the game menu. *Security: None · Thread Safety: Unsafe · Capabilities: Input, Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `cameraMovementMode` | `ComputerCameraMovementMode` | | | **Returns:** `()` ### Method: PlayerScripts:RegisterComputerMovementMode **Signature:** `PlayerScripts:RegisterComputerMovementMode(movementMode: ComputerMovementMode): ()` Registers that a computer movement mode is available to be selected from the game menu. *Security: None · Thread Safety: Unsafe · Capabilities: Input, Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `movementMode` | `ComputerMovementMode` | | | **Returns:** `()` ### Method: PlayerScripts:RegisterTouchCameraMovementMode **Signature:** `PlayerScripts:RegisterTouchCameraMovementMode(cameraMovementMode: TouchCameraMovementMode): ()` Registers that a touch camera movement mode is available to be selected from the game menu. *Security: None · Thread Safety: Unsafe · Capabilities: Input, Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `cameraMovementMode` | `TouchCameraMovementMode` | | | **Returns:** `()` ### Method: PlayerScripts:RegisterTouchMovementMode **Signature:** `PlayerScripts:RegisterTouchMovementMode(movementMode: TouchMovementMode): ()` Registers that a touch movement mode is available to be selected from the game menu. *Security: None · Thread Safety: Unsafe · Capabilities: Input, Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `movementMode` | `TouchMovementMode` | | | **Returns:** `()` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: PlayerViewService last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated summary: "Provides a way to get additional information about a player's view." --- # Class: PlayerViewService > Provides a way to get additional information about a player's view. ## Description [PlayerViewService](/docs/reference/engine/classes/PlayerViewService.md) provides a way to get additional information about a player's view. ## Methods ### Method: PlayerViewService:GetDeviceCameraCFrame **Signature:** `PlayerViewService:GetDeviceCameraCFrame(player?: Player): CFrame` Returns a world space [CFrame](/docs/reference/engine/datatypes/CFrame.md) looking at the player's character, such that setting the current camera's [CFrame](/docs/reference/engine/datatypes/CFrame.md) will view that character from the perspective of their device. This method leverages the device's camera and it only functions on mobile devices. If no information is available, for example the user is not on a mobile device or they don't have their camera turned on, this method returns a [CFrame.identity](/docs/reference/engine/datatypes/CFrame.md). *Security: None · Thread Safety: Unsafe · Capabilities: SensitiveInput* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Player` | `nil` | The player for which to get the device camera [CFrame](/docs/reference/engine/datatypes/CFrame.md). | **Returns:** `CFrame` — The world space [CFrame](/docs/reference/engine/datatypes/CFrame.md) looking at the player's character, or a [CFrame.identity](/docs/reference/engine/datatypes/CFrame.md) (see description). **PlayerViewService:GetDeviceCameraCFrame()** Updates the local player's camera by using [PlayerViewService:GetDeviceCameraCFrame()](/docs/reference/engine/classes/PlayerViewService.md). This method returns a world space [CFrame](/docs/reference/engine/datatypes/CFrame.md) looking at the player's character, such that setting the current camera's [CFrame](/docs/reference/engine/datatypes/CFrame.md) will view that character from the perspective of their device. ```lua local PlayerViewService = game:GetService("PlayerViewService") local RunService = game:GetService("RunService") local Workspace = game:GetService("Workspace") local Players = game:GetService("Players") local player = Players.LocalPlayer local camera = Workspace.CurrentCamera local function updatePictureInPictureCamera() camera.CFrame = PlayerViewService:GetDeviceCameraCFrame(player) end RunService:BindToRenderStep( "PictureInPictureCamera", Enum.RenderPriority.Camera.Value + 1, updatePictureInPictureCamera ) ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Players last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service summary: "A service that contains presently connected Player objects." --- # Class: Players > A service that contains presently connected [Player](/docs/reference/engine/classes/Player.md) objects. ## Description The [Players](/docs/reference/engine/classes/Players.md) service contains [Player](/docs/reference/engine/classes/Player.md) objects for presently connected clients to a Roblox server. It also contains information about a place's configuration. It can fetch information about players not connected to the server, such as character appearances, friends, and avatar thumbnail. ## Properties ### Property: Players.BanningEnabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Players" ] } ``` Enables or disables the three [Players](/docs/reference/engine/classes/Players.md) methods ([BanAsync()](/docs/reference/engine/classes/Players.md), [UnbanAsync()](/docs/reference/engine/classes/Players.md), and [GetBanHistoryAsync()](/docs/reference/engine/classes/Players.md)) that constitute the ban API. This property is not scriptable and can only be modified in Studio. ### Property: Players.BubbleChat ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Chat", "Players" ] } ``` This property indicates whether or not bubble chat is enabled. It is set with the [Players:SetChatStyle()](/docs/reference/engine/classes/Players.md) method using the [ChatStyle](/docs/reference/engine/enums/ChatStyle.md) enum. When this chat mode is enabled, the experience displays chats in the chat user interface at the top-left corner of the screen. There are two other chat modes, [Players.ClassicChat](/docs/reference/engine/classes/Players.md) and a chat mode where both classic and bubble chat are enabled. ### Property: Players.CharacterAutoLoads ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Players" ] } ``` This property indicates whether [characters](/docs/reference/engine/classes/Player.md) will respawn automatically. The default value is true. If this property is disabled (false), player [characters](/docs/reference/engine/classes/Player.md) will not spawn until the [Player:LoadCharacterAsync()](/docs/reference/engine/classes/Player.md) function is called for each [Player](/docs/reference/engine/classes/Player.md), including when players join the experience. This can be useful in experiences where players have finite lives, such as competitive experiences in which players do not respawn until a round ends. **Player Respawn Timer** This example demonstrates one possible usage of the [Players.CharacterAutoLoads](/docs/reference/engine/classes/Players.md) property. The example below respawns all players in the game, if dead, once every 10 seconds. This means that players who die 1 second after all players respawn must wait 9 seconds until the script loads all [Player.Character](/docs/reference/engine/classes/Player.md) again. First, this script removes a player's character when they die and the [Humanoid.Died](/docs/reference/engine/classes/Humanoid.md) function fires. This is done so that the respawn loop that executes every 10 seconds reloads that player when it does not find the player's character in the Workspace. To work as expected, this example should be run within a `Script`. ```lua local Players = game:GetService("Players") -- Set CharacterAutoLoads to false Players.CharacterAutoLoads = false -- Remove player's character from workspace on death Players.PlayerAdded:Connect(function(player) while true do local char = player.CharacterAdded:Wait() char.Humanoid.Died:Connect(function() char:Destroy() end) end end) -- Respawn all dead players once every 10 seconds while true do local players = Players:GetChildren() -- Check if each player is dead by checking if they have no character, if dead load that player's character for _, player in pairs(players) do if not workspace:FindFirstChild(player.Name) then player:LoadCharacterAsync() end end -- Wait 10 seconds until next respawn check task.wait(10) end ``` ### Property: Players.ClassicChat ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Chat", "Players" ] } ``` Indicates whether or not classic chat is enabled. This property is set by the [Players:SetChatStyle()](/docs/reference/engine/classes/Players.md) method using the [ChatStyle](/docs/reference/engine/enums/ChatStyle.md) enum. When this chat mode is enabled, the experience displays chats in a bubble above the sender's head. There are two other chat modes, [Players.BubbleChat](/docs/reference/engine/classes/Players.md) and a chat mode where both classic and bubble chat are enabled. ### Property: Players.LocalPlayer ```json { "type": "Player", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Players" ] } ``` This read-only property refers to the [Player](/docs/reference/engine/classes/Player.md) whose client is running the experience. This property is only defined for [LocalScripts](/docs/reference/engine/classes/LocalScript.md) and [ModuleScripts](/docs/reference/engine/classes/ModuleScript.md) required by them, since they run on the client. For the server, on which [Script](/docs/reference/engine/classes/Script.md) objects run their code, this property is `nil`. ### Property: Players.MaxPlayers ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Players" ] } ``` This property determines the maximum number of players that can be in a server. This property can only be set through a specific place's settings on the [Creator Dashboard](https://create.roblox.com/dashboard/creations) or through [Experience Settings](/docs/en-us/studio/experience-settings.md). ### Property: Players.PreferredPlayers ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Players" ] } ``` This property indicates the number of players to which Roblox's matchmaker will fill servers. This number will be less than the maximum number of players ([Players.MaxPlayers](/docs/reference/engine/classes/Players.md)) supported by the experience. ### Property: Players.RespawnTime ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Players" ] } ``` This property controls the time, in seconds, it takes for a player to respawn when [Players.CharacterAutoLoads](/docs/reference/engine/classes/Players.md) is true. It defaults to 5.0 seconds. This is useful when you want to change how long it takes to respawn based on the type of your experience but don't want to handle spawning players individually. Although this property can be set from within a [Script](/docs/reference/engine/classes/Script.md), you can more easily set it directly on the [Players](/docs/reference/engine/classes/Players.md) object in Studio's [Explorer](/docs/en-us/studio/explorer.md) window. ### Property: Players.UseStrafingAnimations ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Players" ] } ``` ### Property: Players.NumPlayers ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Players" ] } ``` > **Deprecated:** This item is deprecated. Instead, of using this item, you should count the number of players returned by [Players:GetPlayers()](/docs/reference/engine/classes/Players.md). This property indicates the number of people in the server at the current time. It is read only. Meaning it cannot be written to, only read. ### Property: Players.localPlayer *(hidden)* ```json { "type": "Player", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Players" ] } ``` > **Deprecated:** This property is a deprecated variant of [Players.LocalPlayer](/docs/reference/engine/classes/Players.md) which should be used instead. ### Property: Players.numPlayers *(hidden)* ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Players" ] } ``` > **Deprecated:** This property is a deprecated variant of [Players.NumPlayers](/docs/reference/engine/classes/Players.md) which has also been deprecated. Neither property should be used in new work. Instead, you should count the number of players returned by [Players:GetPlayers()](/docs/reference/engine/classes/Players.md). This property indicates the number of people in the server at the current time. It is read only. Meaning it cannot be written to, only read. ## Methods ### Method: Players:BanAsync **Signature:** `Players:BanAsync(config: Dictionary): ()` The [Players:BanAsync()](/docs/reference/engine/classes/Players.md) method allows you to easily ban users who violate your experience's guidelines. You can specify the ban duration, enable the ban to propagate to suspected alternate accounts, enable the ban to temporarily block the banned user's device from rejoining the experience, and provide a message to the banned user in accordance with the [Usage Guidelines](/docs/en-us/players.md#ban-users). You should also post your experience rules somewhere accessible to all users and provide a way for them to appeal. This method is enabled and disabled by the [Players.BanningEnabled](/docs/reference/engine/classes/Players.md) property, which you can toggle in Studio. #### Banning and Messaging Banned users will be immediately evicted and prevented from rejoining your experiences. They will be presented with an error modal displaying the time left on their ban and your `DisplayReason`. Roblox's backend systems will evict players across all servers from the place(s) that you specify. `DisplayReason` can have a maximum length of 400 characters and is subject to a text filter. For more information on acceptable modal text, see [ban messaging](/docs/en-us/players.md#message-guidelines). #### Places and Universe By default, bans extend to any place within that universe. To limit the ban to only the place from which this API is called, configure `ApplyToUniverse` to `false`. However, if a user is banned in the start place of the universe, it effectively results in the user being excluded from the entirety of the universe, irrespective of whether a universal ban is in place or not. #### Alternative Accounts Users often play under multiple different accounts, known as alternate accounts, which are sometimes used to circumvent account bans. To help you keep banned users out, the default behavior of this API will propagate all bans from the source account you banned to any of their suspected alternate accounts. You can turn off ban propagations to alternate accounts by configuring `ExcludeAltAccounts` to `true`. #### Device Blocks To help address disruptive users who circumvent alternate account detection, set `ApplyDeviceBlock` to `true`. This blocks the banned user's device from rejoining the experience for 24 hours after the ban is applied. #### Ban Duration Not all transgressions are the same, so not all bans should be the same length. This API lets you configure the duration of the ban, in seconds, with the `Duration` field. To specify a permanent ban, set the field to `-1`. You may also want to dynamically configure the ban duration based on the user's ban history, which you can query for using [Players:GetBanHistoryAsync()](/docs/reference/engine/classes/Players.md). For example, you may want to consider the number of bans, the duration of previous bans, or build logic off of the notes you save under `PrivateReason` which can be up to 1000 characters and are not text filtered. `PrivateReason` notes are never shared with the client and can be considered safe from attackers. #### Errors and Throttling This method invokes an HTTP call to backend services which are subject to throttling and may fail. If you're calling this API with more than one [UserId](/docs/reference/engine/classes/Player.md), this method will attempt to make the HTTP call for each ID. It will then aggregate any error messages and join them as a comma separated list. For example, if this method is invoked for five users and requests for those with [UserIds](/docs/reference/engine/classes/Player.md) 2 and 4 fail, the following error message appears: `HTTP failure for UserId 2: Timedout, HTTP 504 (Service unavailable) failure for UserId 4: Service exception` The message will always include `failure for UserId {}` if it is an HTTP error. #### Client-Side Requirement Because of the risks associated with banning users, this method may only be called on the backend experience server (client-side calls will result in an error). You may test this API in Studio, during [collaborative](/docs/en-us/projects/collaboration.md) creation, or in a [team test](/docs/en-us/studio/testing-modes.md#collaborative-testing), but the bans will not apply to production. This API uses the [User Restrictions Open Cloud API](/docs/en-us/cloud/reference/UserRestriction.md). You will be able to utilize these APIs to manage your bans in third party applications. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Players, Consequences* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `config` | `Dictionary` | | - `UserIds` (required; array) — Array of [UserIds](/docs/reference/engine/classes/Player.md) of players to be banned. Max size is `50`. - `ApplyToUniverse` (optional; boolean) — Whether ban propagates to all places within the experience universe. Default is `true`. - `Duration` (required; integer) — Duration of the ban, in seconds. Permanent bans should have a value of `-1`. `0` and all other negative values are invalid. - `DisplayReason` (required; string) — The message that will be displayed to users when they attempt to and fail to join an experience. Maximum string length is `400`. - `PrivateReason` (required; string) — Internal messaging that will be returned when querying the user's ban history. Maximum string length is `1000`. - `ExcludeAltAccounts` (optional; boolean) — When `true`, Roblox does not attempt to ban alternate accounts. Default is `false`. - `ApplyDeviceBlock` (optional; boolean) — When `true`, Roblox will block banned users' devices from rejoining the experience for 24 hours after the ban is applied. Default is `false`. The block can be overridden by unbanning a user via a call to [Players:UnbanAsync()](/docs/reference/engine/classes/Players.md). Note that unbanning a user through any other method will not lift the device block. | **Returns:** `()` **Banning Users** The following example bans a user with a duration calculated from their ban history, scoped to the entire universe and all of the user's alternate accounts. ```lua local Players = game:GetService("Players") if shouldBeBanned(player) then local banHistoryPages = Players:GetBanHistoryAsync(player.UserId) local duration = 86400 -- 24 hours in seconds. Recommendation: use your own logic based off of user's BanHistory local config: BanConfigType = { UserIds = { player.UserId }, Duration = duration, DisplayReason = "You violated community guideline #5", PrivateReason = "Put anything here that the user should not know but is helpful for your records", ExcludeAltAccounts = false, ApplyToUniverse = true, } local success, err = pcall(function() return Players:BanAsync(config) end) print(success, err) end ``` ### Method: Players:Chat **Signature:** `Players:Chat(message: string): ()` This function makes the local player chat the given message. Since this item is protected, attempting to use it in a [Script](/docs/reference/engine/classes/Script.md) or [LocalScript](/docs/reference/engine/classes/LocalScript.md) will cause an error. Instead, when creating a custom chat system, or a system that needs access to the chat, you can use the [Chat](/docs/reference/engine/classes/Chat.md) service's [Chat:Chat()](/docs/reference/engine/classes/Chat.md) function instead. *Security: PluginSecurity · Thread Safety: Unsafe · Capabilities: Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `message` | `string` | | The message chatted. | **Returns:** `()` **Players:Chat** This example demonstrates that the [Players:Chat()](/docs/reference/engine/classes/Players.md) function executes without error if using the Command Bar or a Plugin (assuming the local player can chat freely) and errors if executed in a `Script`. ```lua -- Command bar game:GetService("Players"):Chat("Hello, world!") --Results in 'Hello, world!' appearing in the Chat log under your Player's name. -- Script local Players = game:GetService("Players") Players:Chat("Hello, world!") --Errors ``` ### Method: Players:CreateHumanoidModelFromDescriptionAsync **Signature:** `Players:CreateHumanoidModelFromDescriptionAsync(description: HumanoidDescription, rigType: HumanoidRigType, assetTypeVerification?: AssetTypeVerification): Model` Returns a character [Model](/docs/reference/engine/classes/Model.md) equipped with everything specified in the passed in [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md), and is R6 or R15 as specified by `rigType`. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AvatarAppearance, Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `description` | `HumanoidDescription` | | Specifies the appearance of the returned character. | | `rigType` | `HumanoidRigType` | | Specifies whether the returned character will be R6 or R15. | | `assetTypeVerification` | `AssetTypeVerification` | `Default` | The asset type verification mode. | **Returns:** `Model` — A [Humanoid](/docs/reference/engine/classes/Humanoid.md) character [Model](/docs/reference/engine/classes/Model.md). **Create Humanoid Model From Description** This code sample creates a Humanoid Model from the passed in HumanoidDescription and parents the Model to the Workspace. ```lua game.Players:CreateHumanoidModelFromDescriptionAsync(Instance.new("HumanoidDescription"), Enum.HumanoidRigType.R15).Parent = game.Workspace ``` **Expected output:** When run, a Model is created and parented to the Workspace. ### Method: Players:CreateHumanoidModelFromUserIdAsync **Signature:** `Players:CreateHumanoidModelFromUserIdAsync(userId: User): Model` Returns a character Model set-up with everything equipped to match the avatar of the user specified by the passed in userId. This includes whether that character is currently R6 or R15. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `userId` | `User` | | The userId for a Roblox user. (The UserId is the number in the profile of the user e.g www.roblox.com/users/1/profile). | **Returns:** `Model` — A Humanoid character Model. **Create Humanoid Model From A User ID** This code sample creates a Humanoid Model to match the avatar of the passed in User ID, and parents the Model to the Workspace. ```lua game.Players:CreateHumanoidModelFromUserIdAsync(1).Parent = game.Workspace ``` **Expected output:** When run, a Model is created and parented to the Workspace. ### Method: Players:GetBanHistoryAsync **Signature:** `Players:GetBanHistoryAsync(userId: User): BanHistoryPages` Retrieves the ban and unban history of any user within the experience's universe. This method returns a [BanHistoryPages](/docs/reference/engine/classes/BanHistoryPages.md) instance that inherits from [Pages](/docs/reference/engine/classes/Pages.md). This method is enabled and disabled by the [Players.BanningEnabled](/docs/reference/engine/classes/Players.md) property, which you can toggle in Studio. This function call will only succeed on production servers and not on client devices or in Studio. This API uses the [User Restrictions Open Cloud API](/docs/en-us/cloud/reference/UserRestriction.md). You will be able to utilize these APIs to manage your bans in third party applications. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Players, Consequences* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `userId` | `User` | | | **Returns:** `BanHistoryPages` — See [BanHistoryPages](/docs/reference/engine/classes/BanHistoryPages.md) for return reference. ### Method: Players:GetCharacterAppearanceInfoAsync **Signature:** `Players:GetCharacterAppearanceInfoAsync(userId: User): Dictionary` This function returns information about a player's avatar on the Roblox website in the form of a dictionary. It is not to be confused with [GetCharacterAppearanceAsync](/docs/reference/engine/classes/Players.md), which actually loads the assets described by this method. You can use [InsertService:LoadAsset()](/docs/reference/engine/classes/InsertService.md) to load the assets that are used in the player's avatar. The structure of the returned dictionary is as follows: | Name | Type | Description | | --- | --- | --- | | `assets` | table (see below) | Describes the equipped assets (hats, body parts, etc) | | `bodyColors` | table (see below) | Describes the BrickColor values for each limb | | `bodyColor3s` | table (see below) | Describes the Color3 instance for each limb which may not match perfectly with bodyColors | | `defaultPantsApplied` | bool | Describes whether default pants are applied | | `defaultShirtApplied` | bool | Describes whether default shirt is applied | | `emotes` | table (see below) | Describes the equipped emote animations | | `playerAvatarType` | string | Either "R15" or "R6" | | `scales` | table (see below) | Describes various body scaling factors | #### Assets Sub-Table The `assets` table is an array of tables containing the following keys that describe the assets currently equipped by the player: | Name | Type | Description | | --- | --- | --- | | `id` | number | The asset ID of the equipped asset | | `assetType` | table | A table with `name` and `id` fields, each describing the kind of asset equipped ("Hat", "Face", etc.) | | `name` | string | The name of the equipped asset | #### Scales Sub-Table The `scales` table has the following keys, each a number corresponding to one [Humanoid](/docs/reference/engine/classes/Humanoid.md) scaling property: `bodyType`, `head`, `height`, `proportion`, `depth`, `width`. #### Body Colors Sub-Table The `bodyColors` table has the following keys, each a number corresponding to a [BrickColor](/docs/reference/engine/datatypes/BrickColor.md) ID number which can be used with [BrickColor.new(id)](/docs/reference/engine/datatypes/BrickColor.md): `leftArmColorId`, `torsoColorId`, `rightArmColorId`, `headColorId`, `leftLegColorId`, `rightLegColorId`. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `userId` | `User` | | The \*_userId_ of the specified player. | **Returns:** `Dictionary` — A dictionary containing information about the character appearance of a given user. **Example Return Character Appearance Dictionary** Sometimes it is best to see an example of the returned dictionary structure in pure Lua. Here is one such example of a player whose avatar uses a package and wears several hats. Can you guess who it is? ```lua local result = { playerAvatarType = "R15", defaultPantsApplied = false, defaultShirtApplied = false, scales = { bodyType = 0, head = 1, height = 1.05, proportion = 0, depth = 0.92, width = 0.85, }, bodyColors = { leftArmColorId = 1030, torsoColorId = 1001, rightArmColorId = 1030, headColorId = 1030, leftLegColorId = 1001, rightLegColorId = 1001, }, assets = { { id = 1031492, assetType = { name = "Hat", id = 8, }, name = "Striped Hat", }, { id = 13062491, assetType = { name = "Face Accessory", id = 42, }, name = "Vision Française ", }, { id = 16598440, assetType = { name = "Neck Accessory", id = 43, }, name = "Red Bow Tie", }, { id = 28999228, assetType = { name = "Face", id = 18, }, name = "Joyous Surprise", }, { id = 86896488, assetType = { name = "Shirt", id = 11, }, name = "Expensive Red Tuxedo Jacket", }, { id = 86896502, assetType = { name = "Pants", id = 12, }, name = "Expensive Red Tuxedo Pants", }, { id = 376530220, assetType = { name = "Left Arm", id = 29, }, name = "ROBLOX Boy Left Arm", }, { id = 376531012, assetType = { name = "Right Arm", id = 28, }, name = "ROBLOX Boy Right Arm", }, { id = 376531300, assetType = { name = "Left Leg", id = 30, }, name = "ROBLOX Boy Left Leg", }, { id = 376531703, assetType = { name = "Right Leg", id = 31, }, name = "ROBLOX Boy Right Leg", }, { id = 376532000, assetType = { name = "Torso", id = 27, }, name = "ROBLOX Boy Torso", }, }, } print(result) ``` ### Method: Players:GetFriendsAsync **Signature:** `Players:GetFriendsAsync(userId: User): FriendPages` The GetFriends [Players](/docs/reference/engine/classes/Players.md) function returns a [FriendPages](/docs/reference/engine/classes/FriendPages.md) object which contains information for all of the given user's friends. The items within the [FriendPages](/docs/reference/engine/classes/FriendPages.md) object are tables with the following fields: | Name | Type | Description | | --- | --- | --- | | Id | int64 | The friend's UserId | | Username | string | The friend's username | | DisplayName | string | The [display name](/docs/reference/engine/classes/Player.md) of the friend. | See the code samples for an easy way to iterate over all a player's friends. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Players, Social* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `userId` | `User` | | The user ID of the player being specified. | **Returns:** `FriendPages` **Print Roblox Friends** This code sample loads the [Player.UserId](/docs/reference/engine/classes/Player.md) of the player whose username is provided at the top of the script by using [Players:GetUserIdFromNameAsync()](/docs/reference/engine/classes/Players.md). Then, it gets a `FriendPages` object by calling [Players:GetFriendsAsync()](/docs/reference/engine/classes/Players.md) and iterates over each entry using the `iterPageItems` function. The username of each friend is stored in a table, then printed at the end. ```lua local Players = game:GetService("Players") local USERNAME = "Cozecant" local function iterPageItems(pages) return coroutine.wrap(function() local pagenum = 1 while true do for _, item in ipairs(pages:GetCurrentPage()) do coroutine.yield(item, pagenum) end if pages.IsFinished then break end pages:AdvanceToNextPageAsync() pagenum = pagenum + 1 end end) end -- First, get the user ID of the player local userId = Players:GetUserIdFromNameAsync(USERNAME) -- Then, get a FriendPages object for their friends local friendPages = Players:GetFriendsAsync(userId) -- Iterate over the items in the pages. For FriendPages, these -- are tables of information about the friend, including Username. -- Collect each username in a table local usernames = {} for item, _pageNo in iterPageItems(friendPages) do table.insert(usernames, item.Username) end print("Connections of " .. USERNAME .. ": " .. table.concat(usernames, ", ")) ``` **Expected output:** When run, this code sample will load the friends of the player whose username is provided at the top of the script. ### Method: Players:GetHumanoidDescriptionFromOutfitIdAsync **Signature:** `Players:GetHumanoidDescriptionFromOutfitIdAsync(outfitId: int64): HumanoidDescription` Returns the HumanoidDescription for a specified outfitId, which will be set with the parts/colors/Animations etc of the outfit. An outfit can be one created by a user, or it can be the outfit for a bundle created by Roblox. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AvatarAppearance, Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `outfitId` | `int64` | | The ID of the outfit for which the HumanoidDescription is sought. | **Returns:** `HumanoidDescription` — HumanoidDescription initialized with the specification for the passed in outfitId. **Get HumanoidDescription From Outfit ID** Shows how to get the HumanoidDescription for bundle 799 (Fishman). ```lua local Players = game:GetService("Players") local Workspace = game:GetService("Workspace") local function getOutfitId(bundleId) if bundleId <= 0 then return end local info = game.AssetService:GetBundleDetailsAsync(bundleId) if not info then return end for _, item in pairs(info.Items) do if item.Type == "UserOutfit" then return item.Id end end return nil end local function getHumanoidDescriptionBundle(bundleId) local itemId = getOutfitId(bundleId) if itemId and itemId > 0 then return Players:GetHumanoidDescriptionFromOutfitIdAsync(itemId) end return nil end local humanoidDescription = getHumanoidDescriptionBundle(799) local humanoidModel = Players:CreateHumanoidModelFromDescriptionAsync(humanoidDescription, Enum.HumanoidRigType.R15) humanoidModel.Parent = Workspace ``` **Expected output:** When run, a HumanoidDescription is placed in the Workspace. ### Method: Players:GetHumanoidDescriptionFromUserIdAsync **Signature:** `Players:GetHumanoidDescriptionFromUserIdAsync(userId: User): HumanoidDescription` Returns a HumanoidDescription which specifies everything equipped for the avatar of the user specified by the passed in userId. Also includes scales and body colors. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AvatarAppearance, Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `userId` | `User` | | The userId for a Roblox user. (The UserId is the number in the profile of the user e.g www.roblox.com/users/1/profile). | **Returns:** `HumanoidDescription` — HumanoidDescription initialized with the passed in user's avatar specification. **Get HumanoidDescription From User ID** This code sample shows how to use GetHumanoidDescriptionFromUserIdAsync() to create a Humanoid Model. ```lua game.Players:CreateHumanoidModelFromDescriptionAsync( game.Players:GetHumanoidDescriptionFromUserIdAsync(1), Enum.HumanoidRigType.R15 ).Parent = game.Workspace ``` **Expected output:** When run, a Humanoid Model is placed in the Workspace. ### Method: Players:GetNameFromUserIdAsync **Signature:** `Players:GetNameFromUserIdAsync(userId: User): string` The GetNameFromUserIdAsync [Players](/docs/reference/engine/classes/Players.md) function will send a query to the Roblox website asking what the username is of the account with the given [UserId](/docs/reference/engine/classes/Player.md). This method errors if no account exists with the given UserId. If you aren't certain such an account exists, it's recommended to wrap calls to this function with [LuaGlobals.pcall()](/docs/reference/engine/globals/LuaGlobals.md). In addition, you can manually cache results to make future calls with the same UserId fast. See the code samples to learn more. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `userId` | `User` | | The [Player.UserId](/docs/reference/engine/classes/Player.md) of the player being specified. | **Returns:** `string` — The name of a user with the specified [Player.UserId](/docs/reference/engine/classes/Player.md). **Get Name from UserId** This code sample demonstrates using the [Players:GetNameFromUserIdAsync()](/docs/reference/engine/classes/Players.md) method to get a user's [Player.Name](/docs/reference/engine/classes/Player.md) from their [Player.UserId](/docs/reference/engine/classes/Player.md). ```lua local Players = game:GetService("Players") -- Example Data: -- UserId: 118271 Name: "RobloxRulez" -- UserId: 131963979 Name: "docsRule" local nameOne = Players:GetNameFromUserIdAsync(118271) local nameTwo = Players:GetNameFromUserIdAsync(131963979) print(nameOne, nameTwo) -- prints: "RobloxRulez docsRule" ``` **Expected output:** RobloxRulez docsRule **Get Name from UserId using a cache** This code sample demonstrates using the [Players:GetNameFromUserIdAsync()](/docs/reference/engine/classes/Players.md) method to get a user's [Player.Name](/docs/reference/engine/classes/Player.md) from their [Player.UserId](/docs/reference/engine/classes/Player.md). Because [GetNameFromUserIdAsync()](/docs/reference/engine/classes/Players.md) yields, you can avoid calling it for the same [Name](/docs/reference/engine/classes/Player.md) using a table to store each `UserId`:`Name` pair found, called a `cache`. [LuaGlobals.pcall()](/docs/reference/engine/globals/LuaGlobals.md) is used to catch the failure in case the `Name` doesn't exist. ```lua local Players = game:GetService("Players") -- Create a table called 'cache' to store each 'Name' as they are found. -- If we lookup a 'Name' using the same 'UserId', the 'Name' will come -- from cache (fast) instead of GetNameFromUserIdAsync() (yields). local cache = {} function getNameFromUserId(userId) -- First, check if the cache contains 'userId' local nameFromCache = cache[userId] if nameFromCache then -- if a value was stored in the cache at key 'userId', then this 'nameFromCache' -- is the correct Name and we can return it. return nameFromCache end -- If here, 'userId' was not previously looked up and does not exist in the -- cache. Now we need to use GetNameFromUserIdAsync() to look up the name local name local success, _ = pcall(function() name = Players:GetNameFromUserIdAsync(userId) end) if success then -- if 'success' is true, GetNameFromUserIdAsync() successfully found the -- name. Store this name in the cache using 'userId' as the key so we -- never have to look this name up in the future. Then return name. cache[userId] = name return name end -- If here, 'success' was false, meaning GetNameFromUserIdAsync() -- was unable to find the 'name' for the 'userId' provided. Warn the user -- this happened and then return nothing, or nil. warn("Unable to find Name for UserId:", userId) return nil end -- Example Data: -- UserId: 118271 Name: "RobloxRulez" -- UserId: 131963979 Name: "docsRule" -- The first time a UserId is used, GetNameFromUserIdAsync() will be called local nameOne = getNameFromUserId(118271) local nameTwo = getNameFromUserId(131963979) -- Because 118271 was previously used, get its Name from the cache local nameOneQuick = getNameFromUserId(118271) print(nameOne, nameTwo, nameOneQuick) -- prints: "RobloxRulez docsRule RobloxRulez" ``` **Expected output:** RobloxRulez docsRule RobloxRulez ### Method: Players:GetPlayerByUserId **Signature:** `Players:GetPlayerByUserId(userId: User): Player` This function searches each [Player](/docs/reference/engine/classes/Player.md) in [Players](/docs/reference/engine/classes/Players.md) for one whose [Player.UserId](/docs/reference/engine/classes/Player.md) matches the given `userId`. If such a player does not exist, it returns `nil`. This method is useful in finding the purchaser of a developer product using [MarketplaceService.ProcessReceipt](/docs/reference/engine/classes/MarketplaceService.md) which provides a table that includes the purchaser's [UserId](/docs/reference/engine/classes/Player.md) and not a reference to the [Player](/docs/reference/engine/classes/Player.md) object itself. Most experiences will require a reference to the player in order to grant products. *Security: None · Thread Safety: Safe · Capabilities: Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `userId` | `User` | | The [Player.UserId](/docs/reference/engine/classes/Player.md) of the player being specified. | **Returns:** `Player` **Players:GetPlayerByUserId** ```lua local Players = game:GetService("Players") local player = Players:GetPlayerByUserId(1) if player then print("Player with userId 1 is in this server! Their name is: " .. player.Name) else print("Player with userId 1 is not in this server!") end ``` ### Method: Players:GetPlayerFromCharacter **Signature:** `Players:GetPlayerFromCharacter(character: Model): Player` This function returns the [Player](/docs/reference/engine/classes/Player.md) associated with the given [Player.Character](/docs/reference/engine/classes/Player.md), or `nil` if one cannot be found. It is equivalent to the following function: ```lua local function getPlayerFromCharacter(character) for _, player in game:GetService("Players"):GetPlayers() do if player.Character == character then return player end end end ``` This method is often used when some event in player's character fires (such as their [Humanoid](/docs/reference/engine/classes/Humanoid.md) [dying](/docs/reference/engine/classes/Humanoid.md)). Such an event might not directly reference the Player object, but this method provides easy access. The inverse of this function can be described as getting the Character of a Player. To do this, simply access the Character property. *Security: None · Thread Safety: Unsafe · Capabilities: Players · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `character` | `Model` | | A character instance that you want to get the player from. | **Returns:** `Player` **Players:GetPlayerFromCharacter** Players:GetPlayerFromCharacter ```lua local Players = game:GetService("Players") local Workspace = game:GetService("Workspace") local PLAYER_NAME = "Nightriff" local character = Workspace:FindFirstChild(PLAYER_NAME) local player = Players:GetPlayerFromCharacter(character) if player then print(`Player {player.Name} ({player.UserId}) is in the game`) else print(`Player {PLAYER_NAME} is not in the game!`) end ``` ### Method: Players:GetPlayers **Signature:** `Players:GetPlayers(): List` This method returns a table of all presently connected [Player](/docs/reference/engine/classes/Player.md) objects. It functions the same way [Instance:GetChildren()](/docs/reference/engine/classes/Instance.md) would except that it only returns [Player](/docs/reference/engine/classes/Player.md) objects found under [Players](/docs/reference/engine/classes/Players.md). When used with a `for` loop, it is useful for iterating over all players in an experience. ```lua local Players = game:GetService("Players") for _, player in Players:GetPlayers() do print(player.Name) end ``` Scripts that connect to [Players.PlayerAdded](/docs/reference/engine/classes/Players.md) are often trying to process every Player that connects to the experience. This method is useful for iterating over already-connected players that wouldn't fire [PlayerAdded](/docs/reference/engine/classes/Players.md). Using this method ensures that no player is missed! ```lua local Players = game:GetService("Players") local function onPlayerAdded(player) print("Player: " .. player.Name) end for _, player in Players:GetPlayers() do onPlayerAdded(player) end Players.PlayerAdded:Connect(onPlayerAdded) ``` *Security: None · Thread Safety: Safe · Capabilities: Players* **Returns:** `List` — A table containing all the players in the server. **Give Sparkles to Everyone** This code sample listens for players spawning and gives them Sparkles in their head. It does this by defining two functions, `onPlayerSpawned` and `onPlayerAdded`. ```lua local Players = game:GetService("Players") local function onCharacterAdded(character) -- Give them sparkles on their head if they don't have them yet if not character:FindFirstChild("Sparkles") then local sparkles = Instance.new("Sparkles") sparkles.Parent = character:WaitForChild("Head") end end local function onPlayerAdded(player) -- Check if they already spawned in if player.Character then onCharacterAdded(player.Character) end -- Listen for the player (re)spawning player.CharacterAdded:Connect(onCharacterAdded) end Players.PlayerAdded:Connect(onPlayerAdded) ``` ### Method: Players:GetUserIdFromNameAsync **Signature:** `Players:GetUserIdFromNameAsync(userName: string): int64` This function will send a query to the Roblox website asking what the [Player.UserId](/docs/reference/engine/classes/Player.md) is of the account with the given [Player](/docs/reference/engine/classes/Player.md) name. This method errors if no account exists with the given username. If you aren't certain such an account exists, it's recommended to wrap calls to this function with [LuaGlobals.pcall()](/docs/reference/engine/globals/LuaGlobals.md). In addition, you can manually cache results to quickly make future calls with the same username. See the code samples to learn more. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `userName` | `string` | | The username of the player being specified. | **Returns:** `int64` — The [Player.UserId](/docs/reference/engine/classes/Player.md) of a user whose name is specified. **Get UserId from Name** This code sample demonstrates using the [Players:GetUserIdFromNameAsync()](/docs/reference/engine/classes/Players.md) method to get a user's [Player.UserId](/docs/reference/engine/classes/Player.md) from their [Player.Name](/docs/reference/engine/classes/Player.md). ```lua local Players = game:GetService("Players") -- Example Data: -- UserId: 118271 Name: "RobloxRulez" -- UserId: 131963979 Name: "docsRule" local userIdOne = Players:GetUserIdFromNameAsync("RobloxRulez") local userIdTwo = Players:GetUserIdFromNameAsync("docsRule") print(userIdOne, userIdTwo) -- prints: "118271 131963979" ``` **Expected output:** 118271 131963979 **Get UserId from Name using a cache** This code sample demonstrates using the [Players:GetUserIdFromNameAsync()](/docs/reference/engine/classes/Players.md) method to get a user's [Player.UserId](/docs/reference/engine/classes/Player.md) from their [Player.Name](/docs/reference/engine/classes/Player.md). Because [GetUserIdFromNameAsync()](/docs/reference/engine/classes/Players.md) yields, you can avoid calling it for the same [UserId](/docs/reference/engine/classes/Player.md) using a table to store each `Name`:`UserId` pair found, called a `cache`. [LuaGlobals.pcall()](/docs/reference/engine/globals/LuaGlobals.md) is used to catch the failure in case the `UserId` doesn't exist. ```lua local Players = game:GetService("Players") -- Create a table called 'cache' to store each 'UserId' as they are found. -- If we lookup a 'UserId' using the same 'Name', the 'UserId' will come -- from cache (fast) instead of GetUserIdFromNameAsync() (yields). local cache = {} function getUserIdFromName(name) -- First, check if the cache contains 'name' local userIdFromCache = cache[name] if userIdFromCache then -- if a value was stored in the cache at key 'name', then this 'userIdFromCache' -- is the correct UserId and we can return it. return userIdFromCache end -- If here, 'name' was not previously looked up and does not exist in the -- cache. Now we need to use GetUserIdFromNameAsync() to look up the userId local userId local success, _ = pcall(function() userId = Players:GetUserIdFromNameAsync(name) end) if success then -- if 'success' is true, GetUserIdFromNameAsync() successfully found the -- userId. Store this userId in the cache using 'name' as the key so we -- never have to look this userId up in the future. Then return userId. cache[name] = userId return userId end -- If here, 'success' was false, meaning GetUserIdFromNameAsync() -- was unable to find the 'userId' for the 'name' provided. We can warn the -- user this happened and then return nothing, or nil. warn("Unable to find UserId for Name:", name) return nil end -- Example Data: -- UserId: 118271 Name: "RobloxRulez" -- UserId: 131963979 Name: "docsRule" -- The first time a Name is used, GetUserIdFromNameAsync() will be called local userIdOne = getUserIdFromName("RobloxRulez") local userIdTwo = getUserIdFromName("docsRule") -- Because "RobloxRulez" was previously used, get its UserId from the cache local userIdOneQuick = getUserIdFromName("RobloxRulez") print(userIdOne, userIdTwo, userIdOneQuick) -- prints: "118271 131963979 118271" ``` **Expected output:** 118271 131963979 118271 ### Method: Players:GetUserThumbnailAsync **Signature:** `Players:GetUserThumbnailAsync(userId: User, thumbnailType: ThumbnailType, thumbnailSize: ThumbnailSize): Tuple` This function returns the content URL of an image of a player's avatar given their [UserId](/docs/reference/engine/classes/Player.md), the desired image size as a [ThumbnailSize](/docs/reference/engine/enums/ThumbnailSize.md) enum, and the desired type as a [ThumbnailType](/docs/reference/engine/enums/ThumbnailType.md) enum. It also returns a boolean describing if the image is ready to use. Most often, this method is used with [ImageLabel.Image](/docs/reference/engine/classes/ImageLabel.md) or [Decal.Texture](/docs/reference/engine/classes/Decal.md) to display user avatar pictures in an experience. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `userId` | `User` | | The [Player.UserId](/docs/reference/engine/classes/Player.md) of the player being specified. | | `thumbnailType` | `ThumbnailType` | | A [ThumbnailType](/docs/reference/engine/enums/ThumbnailType.md) describing the type of thumbnail. | | `thumbnailSize` | `ThumbnailSize` | | A [ThumbnailSize](/docs/reference/engine/enums/ThumbnailSize.md) specifying the size of the thumbnail. | **Returns:** `Tuple` — A tuple containing the content URL of a user thumbnail based on the specified parameters, and a bool describing if the image is ready to be used or not. **Display Player Thumbnail** This code sample displays the current player's thumbnail in a parent `ImageLabel` by using [Players:GetUserThumbnailAsync()](/docs/reference/engine/classes/Players.md) and setting the [Image()](/docs/reference/engine/classes/ImageLabel.md) property as well as its [Size()](/docs/reference/engine/classes/GuiObject.md). ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local PLACEHOLDER_IMAGE = "rbxassetid://0" -- replace with placeholder image -- fetch the thumbnail local userId = player.UserId local thumbType = Enum.ThumbnailType.HeadShot local thumbSize = Enum.ThumbnailSize.Size420x420 local content, isReady = Players:GetUserThumbnailAsync(userId, thumbType, thumbSize) -- set the ImageLabel's content to the user thumbnail local imageLabel = script.Parent imageLabel.Image = (isReady and content) or PLACEHOLDER_IMAGE imageLabel.Size = UDim2.new(0, 420, 0, 420) ``` **Expected output:** When run, the parent `ImageLabel` should have its `Class.ImageLabel.Image|Image()` and `Class.GuiObject.Size|Size()` set accordingly. ### Method: Players:SetChatStyle **Signature:** `Players:SetChatStyle(style?: ChatStyle): ()` This function sets whether BubbleChat and ClassicChat are being used, and tells TeamChat and Chat what to do using the [ChatStyle](/docs/reference/engine/enums/ChatStyle.md) enum. Since this item is protected, attempting to use it in a [Script](/docs/reference/engine/classes/Script.md) or [LocalScript](/docs/reference/engine/classes/LocalScript.md) will cause an error. This function is used internally when the chat mode is set by the experience. *Security: PluginSecurity · Thread Safety: Unsafe · Capabilities: Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `style` | `ChatStyle` | `Classic` | The specified chat style being set. | **Returns:** `()` **Setting a Player's Chat Style** This example demonstrates that the [Players:SetChatStyle()](/docs/reference/engine/classes/Players.md) function executes without error if using the Command Bar or a Plugin and errors if executed in a `LocalScript`. When executed in the Command Bar, this code sets the chat style to Classic using the [ChatStyle](/docs/reference/engine/enums/ChatStyle.md) enum. ```lua -- Command bar game.Players:SetChatStyle(Enum.ChatStyle.Classic) -- Set's chat style to Classic -- LocalScript local Players = game:GetService("Players") Players:SetChatStyle(Enum.ChatStyle.Classic) -- Errors ``` ### Method: Players:TeamChat **Signature:** `Players:TeamChat(message: string): ()` This function makes the [Players.LocalPlayer](/docs/reference/engine/classes/Players.md) chat the given message, which will only be viewable by users on the same team. Since this item is protected, attempting to use it in a [Script](/docs/reference/engine/classes/Script.md) or [LocalScript](/docs/reference/engine/classes/LocalScript.md) will cause an error. This function is used internally when the [Players.LocalPlayer](/docs/reference/engine/classes/Players.md) sends a message to their team. *Security: PluginSecurity · Thread Safety: Unsafe · Capabilities: Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `message` | `string` | | The message being chatted. | **Returns:** `()` **Sending Team Chat** This example demonstrates that the [Players:TeamChat()](/docs/reference/engine/classes/Players.md) function executes without error if using the Command Bar or a Plugin and errors if executed in a `LocalScript`. When executed in the Command Bar, the function sends the specified message to all players on the same `Team` as the [Players.LocalPlayer](/docs/reference/engine/classes/Players.md). ```lua -- Command bar game.Players:TeamChat("Hello World") -- Sends a "Hello World" message to all players on the local player's team -- LocalScript local Players = game:GetService("Players") Players:TeamChat("Hello World") -- Errors ``` ### Method: Players:UnbanAsync **Signature:** `Players:UnbanAsync(config: Dictionary): ()` Unbans players banned from [Players:BanAsync()](/docs/reference/engine/classes/Players.md) or the [User Restrictions Open Cloud API](/docs/en-us/cloud/reference/UserRestriction.md). This method is enabled and disabled by the [Players.BanningEnabled](/docs/reference/engine/classes/Players.md) property, which you can toggle in Studio. Like [Players:BanAsync()](/docs/reference/engine/classes/Players.md), this method takes in a `config` dictionary that will let you bulk unban users. This configures the users that are unbanned and the scope from which they are unbanned from. Unbans will only take effect on bans with the same `ApplyToUniverse` scope. For example, an unban with `ApplyToUniverse` set to `true` will not invalidate a previous ban with `ApplyToUniverse` set to `false`. In other words, a universe level unban will not invalidate a place level ban. The opposite also holds true. For the case where a user's device may be blocked by a ban where `ApplyDeviceBlock` was set to `true`, an unban applied by this method will override the device block for the unbanned player. Please note that unbanning a user through any other method, such as the Open Cloud API or the Creator Hub, will not lift the device block. This method invokes a HTTP call to backend services, which are throttled and may fail. If you are calling this API with multiple UserIds, this method will attempt to make this HTTP call for each UserId. It will then aggregate any error messages and join them as a comma separated list. For example, if this method is invoked for five `UserIds`: `{1, 2, 3, 4, 5}` and requests for users 2 and 4 fail then the following error message appears: `HTTP failure for UserId 2: Timedout, HTTP 504 (Service unavailable) failure for UserId 4: Service exception.` The message will always include `failure for UserId {}` if it is an HTTP error. It is undefined behavior if you pass in both valid and invalid UserIds, i.e. a `UserId` that is not a positive number, as some network requests may succeed before all input is validated. Because of the risks associated with banning users, this method may only be called on the backend server. Client side calls will result in an error. You may test this API in Studio, Team Create, and Team Test, but the bans will not apply to production. This function call will only attempt ban requests on production servers and not in Studio testing. However, all input validation steps will still work in Studio. This API uses the [User Restrictions Open Cloud API](/docs/en-us/cloud/reference/UserRestriction.md). You will be able to utilize these APIs to manage your bans in third party applications. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Players, Consequences* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `config` | `Dictionary` | | \| Name \| Type \| Description \| \| --- \| --- \| --- \| \| `UserIds` \| array \| UserIDs to be force allowed into the experience(s). Max size is `50`. \| \| `ApplyToUniverse` \| boolean \| Propagates the unban to all places within this universe. \| | **Returns:** `()` **Unbanning Users** The following un-bans a user, as well as another unrelated account with UserId 789. ```lua local Players = game:GetService("Players") if shouldBeUnbanned(player) then local config: UnbanConfigType = { UserIds = { player.UserId, 789 }, ApplyToUniverse = false, } local success, err = pcall(function() return Players:UnbanAsync(config) end) print(success, err) end ``` ### Method: Players:CreateHumanoidModelFromDescription **Signature:** `Players:CreateHumanoidModelFromDescription(description: HumanoidDescription, rigType: HumanoidRigType, assetTypeVerification?: AssetTypeVerification): Model` > **Deprecated:** This method has been superseded by [CreateHumanoidModelFromDescriptionAsync()](/docs/reference/engine/classes/Players.md). Returns a character [Model](/docs/reference/engine/classes/Model.md) equipped with everything specified in the passed in [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AvatarAppearance, Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `description` | `HumanoidDescription` | | Specifies the appearance of the returned character. | | `rigType` | `HumanoidRigType` | | Specifies whether the returned character will be R6 or R15. | | `assetTypeVerification` | `AssetTypeVerification` | `Default` | The asset type verification mode. | **Returns:** `Model` — A [Humanoid](/docs/reference/engine/classes/Humanoid.md) character [Model](/docs/reference/engine/classes/Model.md). ### Method: Players:CreateHumanoidModelFromUserId **Signature:** `Players:CreateHumanoidModelFromUserId(userId: User): Model` > **Deprecated:** This method has been superseded by [CreateHumanoidModelFromUserIdAsync()](/docs/reference/engine/classes/Players.md). Returns a character Model set-up with everything equipped to match the avatar of the user specified by the passed in userId. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `userId` | `User` | | The userId for a Roblox user. (The UserId is the number in the profile of the user e.g www.roblox.com/users/1/profile). | **Returns:** `Model` — A Humanoid character Model. **Create Humanoid Model From A User ID** This code sample creates a Humanoid Model to match the avatar of the passed in User ID, and parents the Model to the Workspace. ```lua game.Players:CreateHumanoidModelFromUserIdAsync(1).Parent = game.Workspace ``` **Expected output:** When run, a Model is created and parented to the Workspace. ### Method: Players:GetCharacterAppearanceAsync **Signature:** `Players:GetCharacterAppearanceAsync(userId: User): Model` This function returns a [Model](/docs/reference/engine/classes/Model.md) containing the assets which the player is wearing, excluding gear. If you prefer a Luau table of information about these assets instead of a model, use [Players:GetCharacterAppearanceInfoAsync()](/docs/reference/engine/classes/Players.md). This method behaves similar to [InsertService:LoadAsset()](/docs/reference/engine/classes/InsertService.md), and is like using [LoadAsset](/docs/reference/engine/classes/InsertService.md) on the asset information returned by [Players:GetCharacterAppearanceInfoAsync()](/docs/reference/engine/classes/Players.md) except faster. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `userId` | `User` | | The [Player.UserId](/docs/reference/engine/classes/Player.md) of the specified player. | **Returns:** `Model` **How To Get A Character's Appearance** This example demonstrates how to retrieve a character's appearance. The returned `Model` is not a child of Workspace by default. If you want the model to be visible, its [parent](/docs/reference/engine/classes/Instance.md) property must be set. ```lua local Players = game:GetService("Players") local USER_ID = 18697683 local appearanceModel = Players:GetCharacterAppearanceAsync(USER_ID) appearanceModel.Parent = workspace ``` ### Method: Players:GetHumanoidDescriptionFromOutfitId **Signature:** `Players:GetHumanoidDescriptionFromOutfitId(outfitId: int64): HumanoidDescription` > **Deprecated:** This method has been superseded by [GetHumanoidDescriptionFromOutfitIdAsync()](/docs/reference/engine/classes/Players.md). Returns the HumanoidDescription for a specified outfit, which will be set with the parts/colors/Animations etc of the outfit. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AvatarAppearance, Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `outfitId` | `int64` | | The ID of the outfit for which the HumanoidDescription is sought. | **Returns:** `HumanoidDescription` — HumanoidDescription initialized with the specification for the passed in outfitId. **Get HumanoidDescription From Outfit ID** Shows how to get the HumanoidDescription for bundle 799 (Fishman). ```lua local Players = game:GetService("Players") local Workspace = game:GetService("Workspace") local function getOutfitId(bundleId) if bundleId <= 0 then return end local info = game.AssetService:GetBundleDetailsAsync(bundleId) if not info then return end for _, item in pairs(info.Items) do if item.Type == "UserOutfit" then return item.Id end end return nil end local function getHumanoidDescriptionBundle(bundleId) local itemId = getOutfitId(bundleId) if itemId and itemId > 0 then return Players:GetHumanoidDescriptionFromOutfitIdAsync(itemId) end return nil end local humanoidDescription = getHumanoidDescriptionBundle(799) local humanoidModel = Players:CreateHumanoidModelFromDescriptionAsync(humanoidDescription, Enum.HumanoidRigType.R15) humanoidModel.Parent = Workspace ``` **Expected output:** When run, a HumanoidDescription is placed in the Workspace. ### Method: Players:GetHumanoidDescriptionFromUserId **Signature:** `Players:GetHumanoidDescriptionFromUserId(userId: User): HumanoidDescription` > **Deprecated:** This method has been superseded by [GetHumanoidDescriptionFromUserIdAsync()](/docs/reference/engine/classes/Players.md). Returns a HumanoidDescription which specifies everything equipped for the avatar of the user specified by the passed in userId. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: AvatarAppearance, Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `userId` | `User` | | The userId for a Roblox user. (The UserId is the number in the profile of the user e.g www.roblox.com/users/1/profile). | **Returns:** `HumanoidDescription` — HumanoidDescription initialized with the passed in user's avatar specification. **Get HumanoidDescription From User ID** This code sample shows how to use GetHumanoidDescriptionFromUserIdAsync() to create a Humanoid Model. ```lua game.Players:CreateHumanoidModelFromDescriptionAsync( game.Players:GetHumanoidDescriptionFromUserIdAsync(1), Enum.HumanoidRigType.R15 ).Parent = game.Workspace ``` **Expected output:** When run, a Humanoid Model is placed in the Workspace. ### Method: Players:getPlayers **Signature:** `Players:getPlayers(): List` > **Deprecated:** This function is a deprecated variant of [Players:GetPlayers()](/docs/reference/engine/classes/Players.md) which should be used instead. *Security: None · Thread Safety: Unsafe · Capabilities: Players* **Returns:** `List` ### Method: Players:playerFromCharacter **Signature:** `Players:playerFromCharacter(character: Model): Player` > **Deprecated:** This function is a deprecated variant of [Players:GetPlayerFromCharacter()](/docs/reference/engine/classes/Players.md) which should be used in new work. *Security: None · Thread Safety: Unsafe · Capabilities: Players* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `character` | `Model` | | | **Returns:** `Player` ### Method: Players:players **Signature:** `Players:players(): List` > **Deprecated:** This item has been superseded by [Players:GetPlayers()](/docs/reference/engine/classes/Players.md) which should be used in all new work. This function was once used to return a list of players in an experience, but has since been deprecated in favor of [Players:GetPlayers()](/docs/reference/engine/classes/Players.md) *Security: None · Thread Safety: Unsafe · Capabilities: Players* **Returns:** `List` ## Events ### Event: Players.PlayerAdded **Signature:** `Players.PlayerAdded(player: Player)` This event fires when a player enters the experience, such as loading the player's saved [GlobalDataStore](/docs/reference/engine/classes/GlobalDataStore.md) data. This can be used alongside the [Players.PlayerRemoving](/docs/reference/engine/classes/Players.md) event, which fires when a player is about to leave the experience. For instance, if you would like print a message every time a new player joins or leaves: ```lua local Players = game:GetService("Players") Players.PlayerAdded:Connect(function(player) print(player.Name .. " joined the experience!") end) Players.PlayerRemoving:Connect(function(player, reason) print(player.Name .. " left the experience! Reason: " .. tostring(exitReason)) end) ``` If you want to track when a player's character is added or removed from the experience, such as when a player respawns or dies, you can use the [Player.CharacterAdded](/docs/reference/engine/classes/Player.md) and [Player.CharacterRemoving](/docs/reference/engine/classes/Player.md) functions. Note that this event does not work as expected in a solo playtest mode because the player is created before scripts run that connect to [PlayerAdded](/docs/reference/engine/classes/Players.md). To handle this case, as well as cases in which the script is added into the experience after a player enters, create an `onPlayerAdded()` function that you can call to handle a player's entrance. *Security: None · Capabilities: Players* **Parameters:** | Name | Type | Description | |------|------|-------------| | `player` | `Player` | An instance of the player that joined the experience. | **Players.PlayerAdded** This example will print "A player has entered: " followed by the name of the player that enters/joins a game every time a player joins. ```lua local Players = game:GetService("Players") local function onPlayerAdded(player) print("A player has entered: " .. player.Name) end Players.PlayerAdded:Connect(onPlayerAdded) ``` ### Event: Players.PlayerMembershipChanged **Signature:** `Players.PlayerMembershipChanged(player: Player)` This event fires when the experience server recognizes that a player's membership has changed. Note, however, that the server will only attempt to check and update the membership **after** the Premium modal has been closed. Thus, to account for cases where the user purchases Premium **outside** of the experience while playing, you must still prompt them to purchase Premium; this will then show a message telling them they're already upgraded and, once they close the modal, the server will update their membership and trigger this event. To learn more about and incorporating Premium into your experience and monetizing with the engagement-based payouts system, see [Engagement-Based Payouts](/docs/en-us/production/monetization/engagement-based-payouts.md). See also: - [MarketplaceService:PromptPremiumPurchase()](/docs/reference/engine/classes/MarketplaceService.md), used to prompt a user to purchase Premium - [MarketplaceService.PromptPremiumPurchaseFinished](/docs/reference/engine/classes/MarketplaceService.md), fires when the Premium purchase UI closes *Security: None · Capabilities: Players* **Parameters:** | Name | Type | Description | |------|------|-------------| | `player` | `Player` | | **Handling Premium Membership Changes** The function in the code sample runs after the game server confirms a player's membership has changed. It demonstrates how you can grant players access to Premium benefits (or revoke them) when their membership status changes. ```lua local Players = game:GetService("Players") local function grantPremiumBenefits(player) -- Grant the player access to Premium-only areas, items, or anything you can imagine! print("Giving", player, "premium benefits!") end local function playerAdded(player) if player.MembershipType == Enum.MembershipType.Premium then grantPremiumBenefits(player) end end local function playerMembershipChanged(player) print("Received event PlayerMembershipChanged. New membership = " .. tostring(player.MembershipType)) if player.MembershipType == Enum.MembershipType.Premium then grantPremiumBenefits(player) end end Players.PlayerAdded:Connect(playerAdded) Players.PlayerMembershipChanged:Connect(playerMembershipChanged) ``` ### Event: Players.PlayerRemoving **Signature:** `Players.PlayerRemoving(player: Player, reason: PlayerExitReason)` This event fires right before a [Player](/docs/reference/engine/classes/Player.md) leaves the experience, before [ChildRemoved](/docs/reference/engine/classes/Instance.md) fires on [Players](/docs/reference/engine/classes/Players.md), and behaves somewhat similarly to [Instance.DescendantRemoving](/docs/reference/engine/classes/Instance.md). Since it fires before the actual removal of a [Player](/docs/reference/engine/classes/Player.md), this event is useful for storing player data using a [GlobalDataStore](/docs/reference/engine/classes/GlobalDataStore.md). This can be used alongside the [Player.PlayerAdded](/docs/reference/engine/classes/Player.md) event, which fires when a player joins the experience. For instance, to print a message every time a new player joins or leaves: ```lua local Players = game:GetService("Players") Players.PlayerAdded:Connect(function(player) print(player.Name .. " joined the experience!") end) Players.PlayerRemoving:Connect(function(player, exitReason) print(player.Name .. " left the experience! - Reason: " .. tostring(exitReason)) end) ``` If you want to track when a player's character is added or removed from the experience, such as when a player respawns or dies, you can use the [Player.CharacterAdded](/docs/reference/engine/classes/Player.md) and [Player.CharacterRemoving](/docs/reference/engine/classes/Player.md) functions. *Security: None · Capabilities: Players* **Parameters:** | Name | Type | Description | |------|------|-------------| | `player` | `Player` | An instance of the player that is leaving. | | `reason` | `PlayerExitReason` | Enum.PlayerExitReason in attempt to inform why. | **Players.PlayerRemoving** This code will print "A player has left: ", followed by the player's name, every time a player leaves: ```lua local Players = game:GetService("Players") local function onPlayerRemoving(player) print("A player has left: " .. player.Name) end Players.PlayerRemoving:Connect(onPlayerRemoving) ``` ### Event: Players.UserSubscriptionStatusChanged **Signature:** `Players.UserSubscriptionStatusChanged(user: Player, subscriptionId: string)` This event fires when the experience server recognizes that the user's status for a certain subscription has changed. Note that the server only attempts to check and update the status **after** the Subscription Purchase modal has been closed. To account for cases in which the user purchases the subscription **outside** of the experience while playing, you must still prompt them to purchase the subscription; the prompt shows a message telling the user they're already subscribed, and after they close the modal, the server updates their subscription status and triggers this event. Note that only server scripts receive this event. *Security: None · Capabilities: Players, Monetization* **Parameters:** | Name | Type | Description | |------|------|-------------| | `user` | `Player` | User whose subscription status has changed. | | `subscriptionId` | `string` | The ID of the subscription with a status change. | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Plugin last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable --- # Class: Plugin ## Description `Plugin` is the main object responsible for creating custom Studio widgets, plugin toolbars, plugin buttons, and more. The `Plugin` object can be accessed through the [RobloxGlobals.plugin](/docs/reference/engine/globals/RobloxGlobals.md) global reference in a [Script](/docs/reference/engine/classes/Script.md) that is executed as a plugin. ## Code Samples **Script - Pass the Plugin Global to a ModuleScript** The [RobloxGlobals.plugin](/docs/reference/engine/globals/RobloxGlobals.md) global reference is not passed to [ModuleScripts](/docs/reference/engine/classes/ModuleScript.md) within the plugin. In order to use it in a [ModuleScript](/docs/reference/engine/classes/ModuleScript.md), you must explicitly pass it as seen in the example below. ```lua assert(plugin, "This script must be run as a plugin!") -- Code beyond this point will execute only if the script is run as a plugin -- Load the module and pass the plugin reference local pluginModule = require(script.Parent.PluginModule) pluginModule:Initialize(plugin) -- Verify if the plugin reference was initialized pluginModule:CheckForPluginGlobal() ``` **ModuleScript - Receive and Store the Plugin Global** ```lua local pluginModule = {} local plugin -- Local plugin reference -- Initialize the plugin reference if not already set function pluginModule:Initialize(pluginReference: Plugin) if plugin ~= pluginReference then plugin = pluginReference else error("Plugin is already initialized") end end -- Check if the plugin reference is set and print out appropriate info function pluginModule:CheckForPluginGlobal() if plugin ~= nil then print("Plugin reference is set!") else warn("Plugin reference is missing!") end end return pluginModule ``` ## Properties ### Property: Plugin.CollisionEnabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` Returns whether the user has enabled **Collisions** in Studio's toolbar. ### Property: Plugin.DisableUIDragDetectorDrags ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` If `true`, toolbars and buttons for the plugin will ignore dragging related to a [UIDragDetector](/docs/reference/engine/classes/UIDragDetector.md) instance. ### Property: Plugin.GridSize ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` Returns the grid snapping size the user has set in Studio's toolbar. Note that this property may have slight rounding errors; for example it may be `0.0099999997764826` for a user setting of `0.01`, or `0.4000000059604645` for a user setting of `0.4`. ### Property: Plugin.IsDebuggable ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ## Methods ### Method: Plugin:Activate **Signature:** `Plugin:Activate(exclusiveMouse: boolean): ()` This method sets the state of the calling plugin to activated. Activating the plugin allows mouse control through the [GetMouse()](/docs/reference/engine/classes/Plugin.md) method. At any given time, there are either 0 or 1 activated plugins. Activating a plugin will deactivate all other plugins and they will receive a [Deactivation](/docs/reference/engine/classes/Plugin.md) event. #### See Also - [IsActivatedWithExclusiveMouse()](/docs/reference/engine/classes/Plugin.md) which returns `true` if the plugin is currently active with an exclusive mouse, after having been activated via this method. - [Unloading](/docs/reference/engine/classes/Plugin.md) which fires immediately before the plugin is unloaded or reloaded via uninstallation, deactivation, or updating. *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `exclusiveMouse` | `boolean` | | A boolean specifying whether to activate the plugin with exclusive mouse. If `true`, a [PluginMouse](/docs/reference/engine/classes/PluginMouse.md) can be retrieved via [GetMouse()](/docs/reference/engine/classes/Plugin.md). | **Returns:** `()` ### Method: Plugin:CreateDockWidgetPluginGuiAsync **Signature:** `Plugin:CreateDockWidgetPluginGuiAsync(pluginGuiId: string, dockWidgetPluginGuiInfo: DockWidgetPluginGuiInfo): DockWidgetPluginGui` This method creates a new [DockWidgetPluginGui](/docs/reference/engine/classes/DockWidgetPluginGui.md) from the given [DockWidgetPluginGuiInfo](/docs/reference/engine/datatypes/DockWidgetPluginGuiInfo.md). The first parameter, `pluginGuiId`, should be a unique and consistent string. It is used to save the state of the widget's dock state and other internal details. *Yields · Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `pluginGuiId` | `string` | | A unique and consistent identifier used to storing the widget's dock state and other internal details. | | `dockWidgetPluginGuiInfo` | `DockWidgetPluginGuiInfo` | | Describes the [DockWidgetPluginGui](/docs/reference/engine/classes/DockWidgetPluginGui.md) to create (initial state, size, etc). | **Returns:** `DockWidgetPluginGui` **Widget GUI Text Button** This code, when ran inside a `Plugin`, creates a `DockWidgetPluginGui` with a simple `TextButton`. ```lua -- Create new 'DockWidgetPluginGuiInfo' object local widgetInfo = DockWidgetPluginGuiInfo.new( Enum.InitialDockState.Float, -- Widget will be initialized in floating panel true, -- Widget will be initially enabled false, -- Don't override the previous enabled state 200, -- Default width of the floating window 300, -- Default height of the floating window 150, -- Minimum width of the floating window (optional) 150 -- Minimum height of the floating window (optional) ) -- Create new widget GUI local testWidget = plugin:CreateDockWidgetPluginGuiAsync("TestWidget", widgetInfo) local testButton = Instance.new("TextButton") testButton.BorderSizePixel = 0 testButton.TextSize = 20 testButton.TextColor3 = Color3.new(1, 0.2, 0.4) testButton.AnchorPoint = Vector2.new(0.5, 0.5) testButton.Size = UDim2.new(1, 0, 1, 0) testButton.Position = UDim2.new(0.5, 0, 0.5, 0) testButton.SizeConstraint = Enum.SizeConstraint.RelativeYY testButton.Text = "Click Me" testButton.Parent = testWidget ``` ### Method: Plugin:CreatePluginAction **Signature:** `Plugin:CreatePluginAction(actionId: string, text: string, statusTip: string, iconName: string, allowBinding?: boolean): PluginAction` This method creates a [PluginAction](/docs/reference/engine/classes/PluginAction.md) which represents a generic performable action in Studio with no directly‑associated [PluginToolbarButton](/docs/reference/engine/classes/PluginToolbarButton.md). See also [CreatePluginMenu()](/docs/reference/engine/classes/Plugin.md) which creates a Studio [PluginMenu](/docs/reference/engine/classes/PluginMenu.md) that can display a list of [PluginActions](/docs/reference/engine/classes/PluginAction.md). *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `actionId` | `string` | | Must be a unique string that identifies this PluginAction from others. | | `text` | `string` | | The displayed name of the action. | | `statusTip` | `string` | | The displayed description of the action. | | `iconName` | `string` | | The name of the icon used to display the plugin. | | `allowBinding` | `boolean` | `true` | Whether the [PluginAction](/docs/reference/engine/classes/PluginAction.md) will be hidden from Studio's shortcuts view. Useful for contextual actions. Defaults to true. | **Returns:** `PluginAction` **Creating a PluginAction** This code sample visualizes how to create a [PluginAction](/docs/reference/engine/classes/PluginAction.md). These must be created using the [Plugin:CreatePluginAction()](/docs/reference/engine/classes/Plugin.md) method in order to work. In order to work as expected, the code block must but pasted into the Command Bar, but only once. Consecutive attempts at executing the code in the Command Bar will result in an error because a plugin cannot create more than one [PluginMenu](/docs/reference/engine/classes/PluginMenu.md) with the same ID. When the created action is bound and [Triggered](/docs/reference/engine/classes/PluginAction.md), it outputs `Hello world!`. ```lua local pluginAction = plugin:CreatePluginAction( "HelloWorldAction", "Hello World", "Prints a 'Hello world!'", "rbxasset://textures/sparkle.png", true ) pluginAction.Name = "Test Action" local function actionTriggered() print("Hello world!") end pluginAction.Triggered:Connect(actionTriggered) ``` ### Method: Plugin:CreatePluginMenu **Signature:** `Plugin:CreatePluginMenu(id: string, title: string, icon: string): PluginMenu` This function creates a new [PluginMenu](/docs/reference/engine/classes/PluginMenu.md) which is a context menu that can be shown in Studio that displays a list of [PluginActions](/docs/reference/engine/classes/PluginAction.md) and also supports submenus. *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `id` | `string` | | Unique ID for the menu. | | `title` | `string` | | The text to be displayed when used as a submenu. | | `icon` | `string` | | The icon to be displayed when used as a submenu. | **Returns:** `PluginMenu` **Creating a PluginMenu and PluginMenuAction** This code sample visualizes how [PluginMenus](/docs/reference/engine/classes/PluginMenu.md) and [PluginActions](/docs/reference/engine/classes/PluginAction.md) behave when created for a [Plugin](/docs/reference/engine/classes/Plugin.md). Outside of this example, you should not parent the plugin or its functional components to the experience's workspace. After executing the code, changing the created [BoolValue](/docs/reference/engine/classes/BoolValue.md) in the experience's workspace opens the plugin's menus. Selecting an action from the menus the function connected to the trigger signal. ```lua -- This code can be pasted into the Command Bar, but only once local pluginMenu = plugin:CreatePluginMenu(math.random(), "Test Menu") pluginMenu.Name = "Test Menu" pluginMenu:AddNewAction("ActionA", "A", "rbxasset://textures/loading/robloxTiltRed.png") pluginMenu:AddNewAction("ActionB", "B", "rbxasset://textures/loading/robloxTilt.png") local subMenu = plugin:CreatePluginMenu(math.random(), "C", "rbxasset://textures/explosion.png") subMenu.Name = "Sub Menu" subMenu:AddNewAction("ActionD", "D", "rbxasset://textures/whiteCircle.png") subMenu:AddNewAction("ActionE", "E", "rbxasset://textures/icon_ROBUX.png") pluginMenu:AddMenu(subMenu) pluginMenu:AddSeparator() pluginMenu:AddNewAction("ActionF", "F", "rbxasset://textures/sparkle.png") local toggle = Instance.new("BoolValue") toggle.Name = "TogglePluginMenu" toggle.Parent = workspace local function onToggled() if toggle.Value then toggle.Value = false local selectedAction = pluginMenu:ShowAsync() if selectedAction then print("Selected Action:", selectedAction.Text, "with ActionId:", selectedAction.ActionId) else print("User did not select an action!") end end end toggle.Changed:Connect(onToggled) ``` ### Method: Plugin:CreateToolbar **Signature:** `Plugin:CreateToolbar(name: string): PluginToolbar` This method creates a new [PluginToolbar](/docs/reference/engine/classes/PluginToolbar.md) with the given name. The toolbar can then be used to create plugin buttons. *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name` | `string` | | The visible text on the toolbar, labeling the group of buttons contained within. | **Returns:** `PluginToolbar` **Plugin:CreateToolbar** This code creates a toolbar with the name "ExampleToolbar". ```lua plugin:CreateToolbar("ExampleToolbar") ``` ### Method: Plugin:Deactivate **Signature:** `Plugin:Deactivate(): ()` Deactivates the plugin. This will disengage the associated [PluginMouse](/docs/reference/engine/classes/PluginMouse.md) if it has been activated. #### See Also - [Activate()](/docs/reference/engine/classes/Plugin.md) which sets the state of the calling plugin to activated. - [Deactivation](/docs/reference/engine/classes/Plugin.md) which fires when the plugin is deactivated. - [Unloading](/docs/reference/engine/classes/Plugin.md) which fires immediately before the plugin is unloaded or reloaded via uninstallation, deactivation, or updating. *Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `()` ### Method: Plugin:GetJoinMode **Signature:** `Plugin:GetJoinMode(): JointCreationMode` Returns the [JointCreationMode](/docs/reference/engine/enums/JointCreationMode.md) the user has set in Studio's toolbar. *Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `JointCreationMode` ### Method: Plugin:GetMouse **Signature:** `Plugin:GetMouse(): PluginMouse` This method returns a [PluginMouse](/docs/reference/engine/classes/PluginMouse.md) that can be used while the plugin is active through [Activate()](/docs/reference/engine/classes/Plugin.md). *Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `PluginMouse` **Plugin:GetMouse** This code would print Button 1 pressed from PluginMouse each time the mouse's left button was clicked. ```lua local mouse = plugin:GetMouse() local function button1Down() print("Button 1 pressed from PluginMouse") end mouse.Button1Down:Connect(button1Down) ``` ### Method: Plugin:GetSelectedRibbonTool **Signature:** `Plugin:GetSelectedRibbonTool(): RibbonTool` This method returns the currently selected [RibbonTool](/docs/reference/engine/enums/RibbonTool.md). *Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `RibbonTool` **Plugin:GetSelectedRibbonTool** This code must be run from a plugin. This code selects the move tool, checks which tool is selected, then prints out to the console which tool is selected. SelectRibbonTool will not return the value until the next frame. ```lua plugin:SelectRibbonTool(Enum.RibbonTool.Move, UDim2.new()) task.wait() -- wait for next frame local selectedRibbonTool = plugin:GetSelectedRibbonTool() print("The selected RibbonTool is", selectedRibbonTool) ``` ### Method: Plugin:GetSetting **Signature:** `Plugin:GetSetting(key: string): Variant` Retrieves a previously stored value with the given key, or `nil` if the given key doesn't exist. Because multiple instances of the same plugin can run simultaneously (for example, if multiple Studio windows are open), you shouldn't depend on this value staying the same over time. The other plugin instances can update the setting at any time. This call can silently fail and return `nil` if multiple instances of the same plugin are actively reading and writing data. If your plugin expects to write to settings frequently, you should double-check the returned value from this call after a short while to distinguish between a setting being temporarily unavailable and a setting not existing. *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | | **Returns:** `Variant` **Plugin:GetSetting** The below example would print the value saved to the key FirstTime. If the key doesn't exist, it would print nil. ```lua local RAN_BEFORE_KEY = "RanBefore" local didRunBefore = plugin:GetSetting(RAN_BEFORE_KEY) if didRunBefore then print("Welcome back!") else plugin:SetSetting(RAN_BEFORE_KEY, true) print("Welcome! Thanks for installing this plugin!") end ``` ### Method: Plugin:ImportFbxAnimationAsync **Signature:** `Plugin:ImportFbxAnimationAsync(rigModel: Instance, isR15?: boolean): Instance` This function prompts the user to open a `.fbx` animation file that can be loaded onto the `rigModel`, then proceeds to insert the animation as a [KeyframeSequence](/docs/reference/engine/classes/KeyframeSequence.md) in the [Workspace](/docs/reference/engine/classes/Workspace.md). *Yields · Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `rigModel` | `Instance` | | | | `isR15` | `boolean` | `true` | | **Returns:** `Instance` ### Method: Plugin:ImportFbxRigAsync **Signature:** `Plugin:ImportFbxRigAsync(isR15?: boolean): Instance` Prompts the user to open a `.fbx` file, uploads the individual components of the model as meshes, and generates a character rig for use in animation, which is loaded into the [Workspace](/docs/reference/engine/classes/Workspace.md). *Yields · Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `isR15` | `boolean` | `true` | | **Returns:** `Instance` ### Method: Plugin:Intersect **Signature:** `Plugin:Intersect(objects: Instances): Instance` Intersects the given parts and returns the resulting [IntersectOperation](/docs/reference/engine/classes/IntersectOperation.md). *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `objects` | `Instances` | | | **Returns:** `Instance` ### Method: Plugin:IsActivated **Signature:** `Plugin:IsActivated(): boolean` This function returns true if this plugin is currently active, after having been activated via the [Activate()](/docs/reference/engine/classes/Plugin.md) function. *Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `boolean` — A boolean indicating whether the plugin is currently active. ### Method: Plugin:IsActivatedWithExclusiveMouse **Signature:** `Plugin:IsActivatedWithExclusiveMouse(): boolean` This function returns `true` if this plugin is currently active with an exclusive mouse, after having been activated via the [Activate()](/docs/reference/engine/classes/Plugin.md) function. If this returns `true`, a [PluginMouse](/docs/reference/engine/classes/PluginMouse.md) can be retrieved via [GetMouse()](/docs/reference/engine/classes/Plugin.md). #### See Also - [Deactivation](/docs/reference/engine/classes/Plugin.md) which fires when the plugin is deactivated. - [Unloading](/docs/reference/engine/classes/Plugin.md) which fires immediately before the plugin is unloaded or reloaded via uninstallation, deactivation, or updating. *Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `boolean` — Whether this plugin is currently active with an exclusive mouse. ### Method: Plugin:Negate **Signature:** `Plugin:Negate(objects: Instances): Instances` Negates the given parts and returns the resulting [NegateOperations](/docs/reference/engine/classes/NegateOperation.md). *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `objects` | `Instances` | | | **Returns:** `Instances` ### Method: Plugin:OpenWikiPage **Signature:** `Plugin:OpenWikiPage(url: string): ()` Opens the context help window to the wiki page that `url` links to. *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `url` | `string` | | | **Returns:** `()` **Plugin:OpenWikiPage** The following, when executed in a plugin, will open the [BasePart API page](/docs/reference/engine/classes/BasePart.md) in the context help. ```lua plugin:OpenWikiPage("API:Class/BasePart") ``` ### Method: Plugin:PromptForExistingAssetId **Signature:** `Plugin:PromptForExistingAssetId(assetType: string): int64` Opens a window in Roblox Studio which prompts the user to select an asset based on the `assetType` specified. *Yields · Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `assetType` | `string` | | | **Returns:** `int64` ### Method: Plugin:PromptForExistingAssetIdAsync **Signature:** `Plugin:PromptForExistingAssetIdAsync(assetType: string): int64` Opens a window in Roblox Studio which prompts the user to select an asset based on the `assetType` specified. Returns what asset ID was selected, or `-1` if the window was closed. *Yields · Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `assetType` | `string` | | | **Returns:** `int64` ### Method: Plugin:PromptSaveSelectionAsync **Signature:** `Plugin:PromptSaveSelectionAsync(suggestedFileName: string): boolean` Prompts the user to save their current selection with the specified file name. Returns `true` if the user did save the file. *Yields · Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `suggestedFileName` | `string` | | | **Returns:** `boolean` ### Method: Plugin:SaveSelectedToRoblox **Signature:** `Plugin:SaveSelectedToRoblox(): ()` Opens an upload window for the user's current selection. *Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `()` ### Method: Plugin:SelectRibbonTool **Signature:** `Plugin:SelectRibbonTool(tool: RibbonTool, position: UDim2): ()` Activates the specified Roblox Studio tool. If the tool opens a window, the position parameter specifies where it should be shown on the screen. An object must be selected in order for this to work correctly. Also note that altering the scale fields of the `position` property will not affect the dialog popups. *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `tool` | `RibbonTool` | | | | `position` | `UDim2` | | | **Returns:** `()` ### Method: Plugin:Separate **Signature:** `Plugin:Separate(objects: Instances): Instances` Separates the given [UnionOperations](/docs/reference/engine/classes/UnionOperation.md) and returns the resulting parts. *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `objects` | `Instances` | | | **Returns:** `Instances` ### Method: Plugin:SetSetting **Signature:** `Plugin:SetSetting(key: string, value: Variant): ()` Stores a given value for later use under the given key. The value will persist even after Roblox Studio is closed. These settings are saved in `.json` format as a map with string keys. Arrays are automatically converted to maps by converting the numeric keys to strings first. Note that the `.json` format imposes additional restrictions, including the following characters which can corrupt the settings file: - Backslashes (`\`) in keys or values, in particular escaped quotes (`\"`). - Newlines (`\n`) in keys. - Quotes (`"`) in keys. - Periods (`.`) in keys. This call can silently fail if multiple instances of the same plugin are actively reading and writing data. If your plugin expects to write to settings frequently, you may check that the data has been properly written by calling [GetSetting()](/docs/reference/engine/classes/Plugin.md). *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `string` | | | | `value` | `Variant` | | | **Returns:** `()` **Plugin:GetSetting and Plugin:SetSetting** This code sample demonstrates use of the [Plugin:GetSetting()](/docs/reference/engine/classes/Plugin.md) and [Plugin:SetSetting()](/docs/reference/engine/classes/Plugin.md) functions. They detect if a plugin is running for the first time by setting a key. If the key is `true`, it prints a "welcome back" message; otherwise it prints a "first run" message and sets the key to `true`. ```lua local RAN_BEFORE_KEY = "RunBefore" local hasRunBefore = plugin:GetSetting(RAN_BEFORE_KEY) if hasRunBefore then print("Welcome back!") else print("Thanks for installing this plugin!") plugin:SetSetting(RAN_BEFORE_KEY, true) end ``` ### Method: Plugin:StartDrag **Signature:** `Plugin:StartDrag(dragData: Dictionary): ()` This method initiates a drag action using a dictionary of parameters. The parameters are as follows: | Name | Type | Default | Description | | --- | --- | --- | --- | | `Sender` | string | `""` | Identifies the source of the drag action to the drop target | | `MimeType` | string | `""` | The MIME type of `Data`. | | `Data` | string | `""` | Information about the drag action, for example what is being dragged. Should be used by the drop target. | | `MouseIcon` | [Content](/docs/reference/engine/datatypes/Content.md) | `""` | The icon to use for the mouse cursor during the drag. If empty, uses the default cursor. | | `DragIcon` | [Content](/docs/reference/engine/datatypes/Content.md) | `""` | An image to render under the mouse cursor during the drag. This should represent the item being dragged. | | `HotSpot` | [Vector2](/docs/reference/engine/datatypes/Vector2.md) | [Vector2.new(0, 0)](/docs/reference/engine/datatypes/Vector2.md) | The pixel offset from the top-left where the cursor should "hold" the `DragIcon`. | See also [PluginGui.PluginDragEntered](/docs/reference/engine/classes/PluginGui.md), [PluginGui.PluginDragMoved](/docs/reference/engine/classes/PluginGui.md), [PluginGui.PluginDragDropped](/docs/reference/engine/classes/PluginGui.md), and [PluginGui.PluginDragLeft](/docs/reference/engine/classes/PluginGui.md). *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `dragData` | `Dictionary` | | | **Returns:** `()` **Plugin Drag and Drop** This code sample creates two plugin widget windows: a drag source and a drop target. In the source window, the script creates a [TextBox](/docs/reference/engine/classes/TextBox.md) and [TextButton](/docs/reference/engine/classes/TextButton.md) to allow the user to begin a plugin drag action. The drop target window will display the MIME type of whatever is dragged into it. If the MIME type is `text/plain`, it will display the plain text data instead. To run this code sample as a plugin, paste it into a [Script](/docs/reference/engine/classes/Script.md). Then, right-click the script in the **Explorer** window and choose **Save as Local Plugin**. ```lua assert(plugin, "This script must be run as a Studio plugin") local widgetInfo = DockWidgetPluginGuiInfo.new(Enum.InitialDockState.Float, true, true, 300, 200) local dragSourceWidget = plugin:CreateDockWidgetPluginGuiAsync("Drag Source", widgetInfo) dragSourceWidget.Title = "Drag Source" local textBox = Instance.new("TextBox") textBox.Parent = dragSourceWidget textBox.Size = UDim2.new(1, 0, 0, 32) textBox.Text = "Hello, plugin drags" local dragButton = Instance.new("TextButton") dragButton.Size = UDim2.new(1, 0, 1, -32) dragButton.Position = UDim2.new(0, 0, 0, 32) dragButton.Text = "Edit the text above, then start drag here" dragButton.Parent = dragSourceWidget function onMouseButton1Down() local dragData = { Sender = "SomeDragSource", MimeType = "text/plain", Data = textBox.Text, MouseIcon = "", DragIcon = "", HotSpot = Vector2.new(0, 0), } plugin:StartDrag(dragData) end dragButton.MouseButton1Down:Connect(onMouseButton1Down) -- This widget will receive drops local dragTargetWidget = plugin:CreateDockWidgetPluginGuiAsync("Drop Target", widgetInfo) dragTargetWidget.Title = "Drop Target" -- This TextLabel will display what was dropped local textLabel = Instance.new("TextLabel") textLabel.Size = UDim2.new(1, 0, 1, 0) textLabel.Text = "Drop here..." textLabel.Parent = dragTargetWidget local function onDragDrop(dragData) if dragData.MimeType == "text/plain" then textLabel.Text = dragData.Data else textLabel.Text = dragData.MimeType end end dragTargetWidget.PluginDragDropped:Connect(onDragDrop) dragTargetWidget.PluginDragEntered:Connect(function(_dragData) print("PluginDragEntered") end) dragTargetWidget.PluginDragLeft:Connect(function(_dragData) print("PluginDragLeft") end) dragTargetWidget.PluginDragMoved:Connect(function(_dragData) print("PluginDragMoved") end) ``` ### Method: Plugin:Union **Signature:** `Plugin:Union(objects: Instances): Instance` Unions the given parts and returns the resulting [UnionOperation](/docs/reference/engine/classes/UnionOperation.md). *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `objects` | `Instances` | | | **Returns:** `Instance` ### Method: Plugin:CreateDockWidgetPluginGui **Signature:** `Plugin:CreateDockWidgetPluginGui(pluginGuiId: string, dockWidgetPluginGuiInfo: DockWidgetPluginGuiInfo): DockWidgetPluginGui` > **Deprecated:** This method has been superseded by [CreateDockWidgetPluginGuiAsync()](/docs/reference/engine/classes/Plugin.md). Creates a [DockWidgetPluginGui](/docs/reference/engine/classes/DockWidgetPluginGui.md) given a [DockWidgetPluginGuiInfo](/docs/reference/engine/datatypes/DockWidgetPluginGuiInfo.md). *Yields · Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `pluginGuiId` | `string` | | A unique and consistent identifier used to storing the widget's dock state and other internal details. | | `dockWidgetPluginGuiInfo` | `DockWidgetPluginGuiInfo` | | Describes the [DockWidgetPluginGui](/docs/reference/engine/classes/DockWidgetPluginGui.md) to create (initial state, size, etc). | **Returns:** `DockWidgetPluginGui` ### Method: Plugin:GetStudioUserId **Signature:** `Plugin:GetStudioUserId(): int64` Returns the Studio user's userId if they're logged in, otherwise returns 0. *Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `int64` ### Method: Plugin:ImportFbxAnimation **Signature:** `Plugin:ImportFbxAnimation(rigModel: Instance, isR15?: boolean): Instance` Prompts the user to open a `.fbx` animation file that can be loaded onto the `rigModel`, then proceeds to insert the animation as a [KeyframeSequence](/docs/reference/engine/classes/KeyframeSequence.md) in the [Workspace](/docs/reference/engine/classes/Workspace.md). *Yields · Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `rigModel` | `Instance` | | | | `isR15` | `boolean` | `true` | | **Returns:** `Instance` ### Method: Plugin:ImportFbxRig **Signature:** `Plugin:ImportFbxRig(isR15?: boolean): Instance` > **Deprecated:** This method has been superseded by [ImportFbxRigAsync()](/docs/reference/engine/classes/Plugin.md). Prompts the user to open a `.fbx` file, uploads the individual components of the model as meshes, and generates a character rig for use in animation, which is loaded into the [Workspace](/docs/reference/engine/classes/Workspace.md). *Yields · Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `isR15` | `boolean` | `true` | | **Returns:** `Instance` ### Method: Plugin:OpenScript **Signature:** `Plugin:OpenScript(script: LuaSourceContainer, lineNumber?: int): ()` Used to open the given script instance in an editor window, in Roblox studio, at the given line. If no line is given as an argument it will default to 1. *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `script` | `LuaSourceContainer` | | | | `lineNumber` | `int` | `1` | | **Returns:** `()` **Plugin:OpenScript** The following creates a new [Script](/docs/reference/engine/classes/Script.md) in the Workspace and opens it. ```lua local newScript = Instance.new("Script") newScript.Parent = workspace plugin:OpenScript(newScript) ``` ### Method: Plugin:PromptSaveSelection **Signature:** `Plugin:PromptSaveSelection(suggestedFileName: string): boolean` > **Deprecated:** This method has been superseded by [PromptSaveSelectionAsync()](/docs/reference/engine/classes/Plugin.md). Prompts the user to save their current selection with the specified file name. *Yields · Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `suggestedFileName` | `string` | | | **Returns:** `boolean` ## Events ### Event: Plugin.Deactivation **Signature:** `Plugin.Deactivation()` Fired when the [Plugin](/docs/reference/engine/classes/Plugin.md) is deactivated. This occurs when either the plugin code calls [Deactivate()](/docs/reference/engine/classes/Plugin.md), or because some other plugin called [Activate()](/docs/reference/engine/classes/Plugin.md), which forces all other plugins to lose their active state. See also [Unloading](/docs/reference/engine/classes/Plugin.md) which fires immediately before the plugin is unloaded or reloaded via uninstallation, deactivation, or updating. *Security: PluginSecurity* ### Event: Plugin.Unloading **Signature:** `Plugin.Unloading()` This event fires immediately before the [Plugin](/docs/reference/engine/classes/Plugin.md) stops running. Plugins are unloaded when disabled, uninstalled, about to be updated, or when the place is closing. It enables a plugin to clean up after itself before its scripts stop running, e.g. to remove unnecessary instances from the [DataModel](/docs/reference/engine/classes/DataModel.md). If a plugin does not clean up properly, the old copies will remain. When this occurs, users may be forced to close and reopen the place which is a bad user experience. Plugin-related instances such as [PluginToolbarButtons](/docs/reference/engine/classes/PluginToolbarButton.md), [DockWidgetPluginGuis](/docs/reference/engine/classes/DockWidgetPluginGui.md), and [PluginGuis](/docs/reference/engine/classes/PluginGui.md) are automatically cleaned up when the plugin is unloaded so there is no need to remove them. *Security: PluginSecurity* ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: PluginAction last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotReplicated --- # Class: PluginAction ## Description `PluginAction` represents a generic performable action in Studio with no directly‑associated [PluginToolbarButton](/docs/reference/engine/classes/PluginToolbarButton.md). If [AllowBinding](/docs/reference/engine/classes/PluginAction.md) is `true`, the action can be assigned a keyboard shortcut through Studio's **File** ⟩ **Customize Shortcuts** window. A `PluginAction` must be created using the [Plugin:CreatePluginAction()](/docs/reference/engine/classes/Plugin.md) method in order to work as expected. ## Properties ### Property: PluginAction.ActionId ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` A string that uniquely identifies this action. This string is the key used when saving and loading the action's state in Roblox Studio. ### Property: PluginAction.AllowBinding ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` This property determines whether the action can be assigned a keyboard shortcut through Studio's **File** ⟩ **Customize Shortcuts** window. Default is `true`. ### Property: PluginAction.StatusTip ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` The description of the action when viewing it from the keyboard shortcuts window in Roblox Studio. ### Property: PluginAction.Text ```json { "type": "string", "access": "ReadOnly", "security": { "read": "None", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` The text that is displayed when viewing this action in Roblox Studio. ## Events ### Event: PluginAction.Triggered **Signature:** `PluginAction.Triggered()` Fires when the action is triggered, typically through the keyboard shortcut that it was bound to. *Security: PluginSecurity* ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: PluginCapabilities last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances --- # Class: PluginCapabilities ## Properties ### Property: PluginCapabilities.Manifest ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: PluginConnection last_updated: 2026-06-29T19:34:08Z inherits: - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated summary: "Encapsulates a connection between the current data model and another for plugin communication." --- # Class: PluginConnection > Encapsulates a connection between the current data model and another for > plugin communication. ## Description Studio plugins run in each Studio data model that the user opens, including both edit and playtest data models. `PluginConnection` objects allow these instances of the same plugin to communicate across data model boundaries. Messages sent by [PluginConnection:SendMessage()](/docs/reference/engine/classes/PluginConnection.md) will be received in the remote data model via the callback the same plugin registers with [PluginConnection:BindToMessage()](/docs/reference/engine/classes/PluginConnection.md). Each `PluginConnection` object can both send and receive messages while connected, and messages will be received reliably and in order unless the connection drops (e.g. due to the remote data model shutting down) before their receipt. Note that although multiple plugins may share the same `PluginConnection` object, they each have their own internal connection; each plugin will receive only its own messages in its callback to `BindToMessage()`, even if other plugins call `SendMessage()`. ## Properties ### Property: PluginConnection.Connected ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` If true, then this `PluginConnection` remains connected to the target data model, and it is still possible to send messages to that data model and receive them from it. If false, then the connection has been lost. Once disconnected, `PluginConnection` objects cannot reconnect, so a value of false here indicates it is now safe to discard the `PluginConnection` object. ### Property: PluginConnection.TargetId ```json { "type": "string", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` This string contains a globally unique identifier for the target data model, which will be the same for any two plugin connections that target the same data model (even if those connections originate from different data models). This property is immutable. ### Property: PluginConnection.Type ```json { "type": "PluginConnectionTargetType", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` This [PluginConnectionTargetType](/docs/reference/engine/enums/PluginConnectionTargetType.md) describes the relationship of the target data model to the current data model. For example, a value of [PluginConnectionTargetType.Test](/docs/reference/engine/enums/PluginConnectionTargetType.md) means that the target data model is a playtest data model spawned from this data model. Alternatively, a value of [PluginConnectionTargetType.Edit](/docs/reference/engine/enums/PluginConnectionTargetType.md) means the target data model is the edit session which spawned this test data model. This property is immutable. ## Methods ### Method: PluginConnection:BindToMessage **Signature:** `PluginConnection:BindToMessage(callback: Function): RBXScriptConnection` This function binds a callback to this connection, which will be called when the connection receives a message from the current plugin running in the remote data model. The message passed to the callback will be the same as the one the plugin passed to `SendMessage()`. The `PluginConnection` will buffer any messages received before the plugin calls `BindToMessage()` and deliver them immediately when the plugin does call `BindToMessage()`. Each plugin may only bind one callback at a time. *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `callback` | `Function` | | A function accepting a string or a buffer which will be called when this `PluginConnection` receives a message. | **Returns:** `RBXScriptConnection` — A [RBXScriptConnection](/docs/reference/engine/datatypes/RBXScriptConnection.md) representing the binding of this callback to the `PluginConnection`. Disconnect it to disconnect this callback from the connection. ### Method: PluginConnection:SendMessage **Signature:** `PluginConnection:SendMessage(message: Variant): ()` This function transmits a string or buffer to the remote data model. The callback registered in `BindToMessage()` by the instance of the current plugin running in the remote data model will be called asynchronously with the message as its argument. *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `message` | `Variant` | | The string or buffer to be sent. | **Returns:** `()` ## Inherited Members ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: PluginConnectionService last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated summary: "This service is used by plugins to communicate with other instances of themselves running in other data models." --- # Class: PluginConnectionService > This service is used by plugins to communicate with other instances of > themselves running in other data models. ## Description This service is used by plugins to communicate with other instances of themselves running in other data models (e.g. a plugin in the Studio edit data model can communicate with the instance of that same plugin in the Studio playtest Server data model). Plugins can retrieve [PluginConnection](/docs/reference/engine/classes/PluginConnection.md) objects from this service, which provide an API for sending messages across the data model boundary. All access to this class is restricted to Plugin security. The command bar is not currently supported. ## Code Samples **PluginConnection echo Plugin** This plugin demonstrates using PluginConnectionService end-to-end On the edit data model, it listens for incoming connections from test data models. On the test data model, it looks for its connection back to the edit data model. In either case: When a connection is found, the plugin binds to it to receive messages from the other data model and makes arrangements to clean up when the connection disconencts or plugin unloads. After connecting, the test data models send a greeting, and both sides of each connection send messages back and forth for the duration of the test session. ```lua local Service = game:GetService("PluginConnectionService") local TestType = Enum.PluginConnectionTargetType.Test local EditType = Enum.PluginConnectionTargetType.Edit local function MakeConnection(Connection: PluginConnection, Side: string) assert(Connection.Connected) local Count = 0 local Binding = Connection:BindToMessage(function(Msg: string) print(Msg) task.wait(1) if Connection.Connected then Connection:SendMessage(`Hello from {Side} iteration {Count}`) end Count += 1 end) plugin.Unloading:Connect(function() Binding:Disconnect() end) Connection:GetPropertyChangedSignal("Connected"):Once(function() assert(not Connection.Connected) print("Disconnected", Connection.TargetId) end) end if Service:CanHaveConnectionType(TestType) then for _, ExistingConnection in Service:GetPluginConnectionsOfType(TestType) do MakeConnection(ExistingConnection, "Edit") end Service.Connected:Connect(function(NewConnection) if NewConnection.Type == TestType then MakeConnection(NewConnection, "Edit") end end) elseif Service:CanHaveConnectionType(EditType) then local Connections = Service:GetPluginConnectionsOfType(EditType) assert(#Connections <= 1) local Connection: PluginConnection? = nil if #Connections == 1 then Connection = Connections[1] else while true do local NewConnection = Service.Connected:Wait() if NewConnection.Type == EditType then Connection = NewConnection break end end end assert(Connection) assert(Connection.Connected) MakeConnection(Connection, if game:GetService("RunService"):IsServer() then "Server" else "Client") Connection:SendMessage("Hello Edit!") end ``` ## Methods ### Method: PluginConnectionService:CanHaveConnectionType **Signature:** `PluginConnectionService:CanHaveConnectionType(type: PluginConnectionTargetType): boolean` Checks if the current data model can potentially have connections of a given [PluginConnectionTargetType](/docs/reference/engine/enums/PluginConnectionTargetType.md). Note that it is possible for this function to return true even if no such connections currently exist. *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `type` | `PluginConnectionTargetType` | | The connection type to check. | **Returns:** `boolean` ### Method: PluginConnectionService:GetPluginConnectionsOfType **Signature:** `PluginConnectionService:GetPluginConnectionsOfType(type: PluginConnectionTargetType): List` Returns a list of currently connected [PluginConnection](/docs/reference/engine/classes/PluginConnection.md) objects with the given connection type. Each `PluginConnection` from this list will initially have their `Connected` property set to true and correspond to a connected data model related to the current data model according to their [PluginConnectionTargetType](/docs/reference/engine/enums/PluginConnectionTargetType.md). For example, an edit data model may have [PluginConnectionTargetType.Test](/docs/reference/engine/enums/PluginConnectionTargetType.md) connections to communicate with its ongoing playtest data models, and each test data model will have an [PluginConnectionTargetType.Edit](/docs/reference/engine/enums/PluginConnectionTargetType.md) connection back to its edit data model. *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `type` | `PluginConnectionTargetType` | | The connection type to check. | **Returns:** `List` — An array of the currently connected [PluginConnection](/docs/reference/engine/classes/PluginConnection.md) objects with that target type. ## Events ### Event: PluginConnectionService.Connected **Signature:** `PluginConnectionService.Connected(conn: PluginConnection)` Fires just after a new [PluginConnection](/docs/reference/engine/classes/PluginConnection.md) successfully connects. Plugins may listen for this signal to learn when a new data model that they can communicate with becomes available. Note: On startup, it is possible some [PluginConnections](/docs/reference/engine/classes/PluginConnection.md) already exist. Plugins should query [PluginConnectionService:GetPluginConnectionsOfType()](/docs/reference/engine/classes/PluginConnectionService.md) for each type they are interested in after connecting to this function. *Security: PluginSecurity* **Parameters:** | Name | Type | Description | |------|------|-------------| | `conn` | `PluginConnection` | The newly connected [PluginConnection](/docs/reference/engine/classes/PluginConnection.md). | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: PluginDebugService last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated --- # Class: PluginDebugService ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: PluginDragEvent last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated --- # Class: PluginDragEvent ## Properties ### Property: PluginDragEvent.Data ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: PluginDragEvent.MimeType ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: PluginDragEvent.Position ```json { "type": "Vector2", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: PluginDragEvent.Sender ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: PluginGui last_updated: 2026-06-29T19:34:08Z inherits: - LayerCollector - GuiBase2d - GuiBase - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated --- # Class: PluginGui ## Description PluginGui is an abstract class for GUIs that allow the display of [GuiObjects](/docs/reference/engine/classes/GuiObject.md) in various Roblox Studio widgets. As of right now, the only available PluginGui type is [DockWidgetPluginGui](/docs/reference/engine/classes/DockWidgetPluginGui.md), but there may be more in the future! ## Properties ### Property: PluginGui.Title ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` The title that is displayed above the contents of the [PluginGui](/docs/reference/engine/classes/PluginGui.md). Defaults to empty string. ## Methods ### Method: PluginGui:BindToClose **Signature:** `PluginGui:BindToClose(function?: Function): ()` This function binds a function to the [PluginGui](/docs/reference/engine/classes/PluginGui.md) close button, overriding the default behavior. By default, when the user clicks the 'x' button in the top right corner of the [PluginGui](/docs/reference/engine/classes/PluginGui.md) the [Enabled](/docs/reference/engine/classes/LayerCollector.md) property is set to _false_, closing the window. When a custom function is bound using BindToClose this behavior is overwritten, allowing you to check if the user really wants to close the window or give them an opportunity to save their work. As the default closing behavior is overwritten by this function, you'll need to configure the [PluginGui](/docs/reference/engine/classes/PluginGui.md) to close manually by setting [PluginGui.Enabled](/docs/reference/engine/classes/LayerCollector.md) to _false_. For example, in the below snippet users are required to click a confirm button to close the GUI: ```lua local closing = false pluginGui:BindToClose(function() -- make sure we haven't already made a button if closing then return end closing = true -- create confirm button local confirmButton = Instance.new("TextButton") confirmButton.AnchorPoint = Vector2.new(0.5, 0.5) confirmButton.Size = UDim2.new(0.5, 0, 0.5, 0) confirmButton.Position = UDim2.new(0.5, 0, 0.5, 0) confirmButton.BackgroundColor3 = Color3.new(1, 0, 0) confirmButton.Text = "Close?" confirmButton.Parent = pluginGui -- listen for click confirmButton.Activated:Connect(function() -- close the gui pluginGui.Enabled = false -- remove confirm button confirmButton:Destroy() end) end) ``` You can call BindToClose with no argument to 'unbind' and revert to the default behavior described above. For example: ```lua pluginGui:BindToClose() ``` See also: - [Plugin:CreateDockWidgetPluginGuiAsync()](/docs/reference/engine/classes/Plugin.md) to create a [PluginGui](/docs/reference/engine/classes/PluginGui.md) - [DataModel:BindToClose()](/docs/reference/engine/classes/DataModel.md), which can be used to bind a function to the game ending and should not be confused with this function *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `function` | `Function` | `nil` | The function to bind the close button to. If no function is specified then any previously specified function will be unbound. | **Returns:** `()` ### Method: PluginGui:GetRelativeMousePosition **Signature:** `PluginGui:GetRelativeMousePosition(): Vector2` GetRelativeMousePosition returns the position of the mouse relative to the top-left corner of the [PluginGui](/docs/reference/engine/classes/PluginGui.md). The returned value changes only if a mouse input began on the PluginGui, or if the mouse is presently hovering over the window. *Security: PluginSecurity · Thread Safety: Unsafe · Capabilities: UI* **Returns:** `Vector2` — The screen position of the mouse relative to the PluginGui in pixels. **PluginGui:GetRelativeMousePosition** ```lua local RunService = game:GetService("RunService") local widgetInfo = DockWidgetPluginGuiInfo.new( Enum.InitialDockState.Float, true, false, -- Enabled state, override 200, 300, -- Size 150, 150 -- Minimum size ) local testWidget = plugin:CreateDockWidgetPluginGuiAsync("TestWidget", widgetInfo) function update() local v2 = testWidget:GetRelativeMousePosition() testWidget.Title = ("(%d, %d)"):format(v2.x, v2.y) end RunService.Stepped:Connect(update) update() ``` ## Events ### Event: PluginGui.PluginDragDropped **Signature:** `PluginGui.PluginDragDropped(dragData: Dictionary)` **PluginDragDropped** fires when the user releases their mouse over a [PluginGui](/docs/reference/engine/classes/PluginGui.md) during a drag operation started by [Plugin:StartDrag()](/docs/reference/engine/classes/Plugin.md). See also: - [PluginGui.PluginDragEntered](/docs/reference/engine/classes/PluginGui.md) - [PluginGui.PluginDragLeft](/docs/reference/engine/classes/PluginGui.md) - [PluginGui.PluginDragMoved](/docs/reference/engine/classes/PluginGui.md) *Security: PluginSecurity · Capabilities: UI* **Parameters:** | Name | Type | Description | |------|------|-------------| | `dragData` | `Dictionary` | | **Plugin Drag and Drop** This code sample creates two plugin widget windows: a drag source and a drop target. In the source window, the script creates a [TextBox](/docs/reference/engine/classes/TextBox.md) and [TextButton](/docs/reference/engine/classes/TextButton.md) to allow the user to begin a plugin drag action. The drop target window will display the MIME type of whatever is dragged into it. If the MIME type is `text/plain`, it will display the plain text data instead. To run this code sample as a plugin, paste it into a [Script](/docs/reference/engine/classes/Script.md). Then, right-click the script in the **Explorer** window and choose **Save as Local Plugin**. ```lua assert(plugin, "This script must be run as a Studio plugin") local widgetInfo = DockWidgetPluginGuiInfo.new(Enum.InitialDockState.Float, true, true, 300, 200) local dragSourceWidget = plugin:CreateDockWidgetPluginGuiAsync("Drag Source", widgetInfo) dragSourceWidget.Title = "Drag Source" local textBox = Instance.new("TextBox") textBox.Parent = dragSourceWidget textBox.Size = UDim2.new(1, 0, 0, 32) textBox.Text = "Hello, plugin drags" local dragButton = Instance.new("TextButton") dragButton.Size = UDim2.new(1, 0, 1, -32) dragButton.Position = UDim2.new(0, 0, 0, 32) dragButton.Text = "Edit the text above, then start drag here" dragButton.Parent = dragSourceWidget function onMouseButton1Down() local dragData = { Sender = "SomeDragSource", MimeType = "text/plain", Data = textBox.Text, MouseIcon = "", DragIcon = "", HotSpot = Vector2.new(0, 0), } plugin:StartDrag(dragData) end dragButton.MouseButton1Down:Connect(onMouseButton1Down) -- This widget will receive drops local dragTargetWidget = plugin:CreateDockWidgetPluginGuiAsync("Drop Target", widgetInfo) dragTargetWidget.Title = "Drop Target" -- This TextLabel will display what was dropped local textLabel = Instance.new("TextLabel") textLabel.Size = UDim2.new(1, 0, 1, 0) textLabel.Text = "Drop here..." textLabel.Parent = dragTargetWidget local function onDragDrop(dragData) if dragData.MimeType == "text/plain" then textLabel.Text = dragData.Data else textLabel.Text = dragData.MimeType end end dragTargetWidget.PluginDragDropped:Connect(onDragDrop) dragTargetWidget.PluginDragEntered:Connect(function(_dragData) print("PluginDragEntered") end) dragTargetWidget.PluginDragLeft:Connect(function(_dragData) print("PluginDragLeft") end) dragTargetWidget.PluginDragMoved:Connect(function(_dragData) print("PluginDragMoved") end) ``` ### Event: PluginGui.PluginDragEntered **Signature:** `PluginGui.PluginDragEntered(dragData: Dictionary)` **PluginDragEntered** fires when the user's mouse enters the [PluginGui](/docs/reference/engine/classes/PluginGui.md) during a drag operation started by [Plugin:StartDrag()](/docs/reference/engine/classes/Plugin.md). This event is useful for displaying a "Drop Here" UI on PluginGuis where a drag operation can be dropped. Such a UI should be hidden when either [PluginDragLeft](/docs/reference/engine/classes/PluginGui.md) or [PluginDragDropped](/docs/reference/engine/classes/PluginGui.md) fire. See also: - [PluginGui.PluginDragLeft](/docs/reference/engine/classes/PluginGui.md) - [PluginGui.PluginDragMoved](/docs/reference/engine/classes/PluginGui.md) - [PluginGui.PluginDragDropped](/docs/reference/engine/classes/PluginGui.md) *Security: PluginSecurity · Capabilities: UI* **Parameters:** | Name | Type | Description | |------|------|-------------| | `dragData` | `Dictionary` | A copy of the data originally passed to [Plugin:StartDrag()](/docs/reference/engine/classes/Plugin.md). | **Plugin Drag and Drop** This code sample creates two plugin widget windows: a drag source and a drop target. In the source window, the script creates a [TextBox](/docs/reference/engine/classes/TextBox.md) and [TextButton](/docs/reference/engine/classes/TextButton.md) to allow the user to begin a plugin drag action. The drop target window will display the MIME type of whatever is dragged into it. If the MIME type is `text/plain`, it will display the plain text data instead. To run this code sample as a plugin, paste it into a [Script](/docs/reference/engine/classes/Script.md). Then, right-click the script in the **Explorer** window and choose **Save as Local Plugin**. ```lua assert(plugin, "This script must be run as a Studio plugin") local widgetInfo = DockWidgetPluginGuiInfo.new(Enum.InitialDockState.Float, true, true, 300, 200) local dragSourceWidget = plugin:CreateDockWidgetPluginGuiAsync("Drag Source", widgetInfo) dragSourceWidget.Title = "Drag Source" local textBox = Instance.new("TextBox") textBox.Parent = dragSourceWidget textBox.Size = UDim2.new(1, 0, 0, 32) textBox.Text = "Hello, plugin drags" local dragButton = Instance.new("TextButton") dragButton.Size = UDim2.new(1, 0, 1, -32) dragButton.Position = UDim2.new(0, 0, 0, 32) dragButton.Text = "Edit the text above, then start drag here" dragButton.Parent = dragSourceWidget function onMouseButton1Down() local dragData = { Sender = "SomeDragSource", MimeType = "text/plain", Data = textBox.Text, MouseIcon = "", DragIcon = "", HotSpot = Vector2.new(0, 0), } plugin:StartDrag(dragData) end dragButton.MouseButton1Down:Connect(onMouseButton1Down) -- This widget will receive drops local dragTargetWidget = plugin:CreateDockWidgetPluginGuiAsync("Drop Target", widgetInfo) dragTargetWidget.Title = "Drop Target" -- This TextLabel will display what was dropped local textLabel = Instance.new("TextLabel") textLabel.Size = UDim2.new(1, 0, 1, 0) textLabel.Text = "Drop here..." textLabel.Parent = dragTargetWidget local function onDragDrop(dragData) if dragData.MimeType == "text/plain" then textLabel.Text = dragData.Data else textLabel.Text = dragData.MimeType end end dragTargetWidget.PluginDragDropped:Connect(onDragDrop) dragTargetWidget.PluginDragEntered:Connect(function(_dragData) print("PluginDragEntered") end) dragTargetWidget.PluginDragLeft:Connect(function(_dragData) print("PluginDragLeft") end) dragTargetWidget.PluginDragMoved:Connect(function(_dragData) print("PluginDragMoved") end) ``` ### Event: PluginGui.PluginDragLeft **Signature:** `PluginGui.PluginDragLeft(dragData: Dictionary)` **PluginDragLeft** fires when the user's mouse leaves a [PluginGui](/docs/reference/engine/classes/PluginGui.md) during a drag operation started by [Plugin:StartDrag()](/docs/reference/engine/classes/Plugin.md). This event and [PluginDragDropped](/docs/reference/engine/classes/PluginGui.md) are useful for hiding a "Drop Here" UI on PluginGuis where a drag operation can be dropped. Such a UI should be shown when either [PluginDragEntered](/docs/reference/engine/classes/PluginGui.md) fires. See also: - [PluginGui.PluginDragEntered](/docs/reference/engine/classes/PluginGui.md) - [PluginGui.PluginDragMoved](/docs/reference/engine/classes/PluginGui.md) - [PluginGui.PluginDragDropped](/docs/reference/engine/classes/PluginGui.md) *Security: PluginSecurity · Capabilities: UI* **Parameters:** | Name | Type | Description | |------|------|-------------| | `dragData` | `Dictionary` | A copy of the data originally passed to [Plugin:StartDrag()](/docs/reference/engine/classes/Plugin.md). | **Plugin Drag and Drop** This code sample creates two plugin widget windows: a drag source and a drop target. In the source window, the script creates a [TextBox](/docs/reference/engine/classes/TextBox.md) and [TextButton](/docs/reference/engine/classes/TextButton.md) to allow the user to begin a plugin drag action. The drop target window will display the MIME type of whatever is dragged into it. If the MIME type is `text/plain`, it will display the plain text data instead. To run this code sample as a plugin, paste it into a [Script](/docs/reference/engine/classes/Script.md). Then, right-click the script in the **Explorer** window and choose **Save as Local Plugin**. ```lua assert(plugin, "This script must be run as a Studio plugin") local widgetInfo = DockWidgetPluginGuiInfo.new(Enum.InitialDockState.Float, true, true, 300, 200) local dragSourceWidget = plugin:CreateDockWidgetPluginGuiAsync("Drag Source", widgetInfo) dragSourceWidget.Title = "Drag Source" local textBox = Instance.new("TextBox") textBox.Parent = dragSourceWidget textBox.Size = UDim2.new(1, 0, 0, 32) textBox.Text = "Hello, plugin drags" local dragButton = Instance.new("TextButton") dragButton.Size = UDim2.new(1, 0, 1, -32) dragButton.Position = UDim2.new(0, 0, 0, 32) dragButton.Text = "Edit the text above, then start drag here" dragButton.Parent = dragSourceWidget function onMouseButton1Down() local dragData = { Sender = "SomeDragSource", MimeType = "text/plain", Data = textBox.Text, MouseIcon = "", DragIcon = "", HotSpot = Vector2.new(0, 0), } plugin:StartDrag(dragData) end dragButton.MouseButton1Down:Connect(onMouseButton1Down) -- This widget will receive drops local dragTargetWidget = plugin:CreateDockWidgetPluginGuiAsync("Drop Target", widgetInfo) dragTargetWidget.Title = "Drop Target" -- This TextLabel will display what was dropped local textLabel = Instance.new("TextLabel") textLabel.Size = UDim2.new(1, 0, 1, 0) textLabel.Text = "Drop here..." textLabel.Parent = dragTargetWidget local function onDragDrop(dragData) if dragData.MimeType == "text/plain" then textLabel.Text = dragData.Data else textLabel.Text = dragData.MimeType end end dragTargetWidget.PluginDragDropped:Connect(onDragDrop) dragTargetWidget.PluginDragEntered:Connect(function(_dragData) print("PluginDragEntered") end) dragTargetWidget.PluginDragLeft:Connect(function(_dragData) print("PluginDragLeft") end) dragTargetWidget.PluginDragMoved:Connect(function(_dragData) print("PluginDragMoved") end) ``` ### Event: PluginGui.PluginDragMoved **Signature:** `PluginGui.PluginDragMoved(dragData: Dictionary)` **PluginDragMoved** fires when the user's mouse moves within a [PluginGui](/docs/reference/engine/classes/PluginGui.md) during a drag operation started by [Plugin:StartDrag()](/docs/reference/engine/classes/Plugin.md). See also: - [PluginGui.PluginDragEntered](/docs/reference/engine/classes/PluginGui.md) - [PluginGui.PluginDragLeft](/docs/reference/engine/classes/PluginGui.md) - [PluginGui.PluginDragDropped](/docs/reference/engine/classes/PluginGui.md) *Security: PluginSecurity · Capabilities: UI* **Parameters:** | Name | Type | Description | |------|------|-------------| | `dragData` | `Dictionary` | | ### Event: PluginGui.WindowFocused **Signature:** `PluginGui.WindowFocused()` **WindowFocused** fires immediately when the user starts interacting with the PluginGui's window, usually by clicking on it. This functions works similarly to the similarly-named [UserInputService.WindowFocused](/docs/reference/engine/classes/UserInputService.md) event. It fires before any [GuiObject.InputBegan](/docs/reference/engine/classes/GuiObject.md) events related to mouse buttons. If another [PluginGui](/docs/reference/engine/classes/PluginGui.md) is in focus and the user focuses this PluginGui, then this event fires after the other's [WindowFocusReleased](/docs/reference/engine/classes/PluginGui.md) event. However, if the main game window was in focus, this event fires **after** [UserInputService.WindowFocusReleased](/docs/reference/engine/classes/UserInputService.md). *Security: PluginSecurity · Capabilities: UI* **Detecting PluginGui Focus State** This code sample demonstrates how the focus state of a `PluginGui` can be tracked using the [WindowFocused()](/docs/reference/engine/classes/PluginGui.md) and [WindowFocusReleased()](/docs/reference/engine/classes/PluginGui.md) events. It changes the [Title()](/docs/reference/engine/classes/PluginGui.md) as the focus state changes. ```lua local info = DockWidgetPluginGuiInfo.new(Enum.InitialDockState.Float, true, true, 200, 50, 1, 1) local widget = plugin:CreateDockWidgetPluginGuiAsync("TestWidget", info) local function onFocusReleased() widget.Title = "I'm not in focus :(" end local function onFocused() widget.Title = "I'm in focus :D" end widget.WindowFocusReleased:Connect(onFocusReleased) widget.WindowFocused:Connect(onFocused) ``` ### Event: PluginGui.WindowFocusReleased **Signature:** `PluginGui.WindowFocusReleased()` **WindowFocusReleased** fires immediately when the user stops interacting with the PluginGui's window, usually by clicking on something not in the window. This functions works similarly to the similarly-named [UserInputService.WindowFocusReleased](/docs/reference/engine/classes/UserInputService.md) event. If focus is moving to another [PluginGui](/docs/reference/engine/classes/PluginGui.md) while the user had this PluginGui in focus, then this event fires before the other's [WindowFocused](/docs/reference/engine/classes/PluginGui.md) event. However, if the main game window is being put in focus, this event fires **after** [UserInputService.WindowFocused](/docs/reference/engine/classes/UserInputService.md). *Security: PluginSecurity · Capabilities: UI* **Detecting PluginGui Focus State** This code sample demonstrates how the focus state of a `PluginGui` can be tracked using the [WindowFocused()](/docs/reference/engine/classes/PluginGui.md) and [WindowFocusReleased()](/docs/reference/engine/classes/PluginGui.md) events. It changes the [Title()](/docs/reference/engine/classes/PluginGui.md) as the focus state changes. ```lua local info = DockWidgetPluginGuiInfo.new(Enum.InitialDockState.Float, true, true, 200, 50, 1, 1) local widget = plugin:CreateDockWidgetPluginGuiAsync("TestWidget", info) local function onFocusReleased() widget.Title = "I'm not in focus :(" end local function onFocused() widget.Title = "I'm in focus :D" end widget.WindowFocusReleased:Connect(onFocusReleased) widget.WindowFocused:Connect(onFocused) ``` ## Inherited Members ### From [LayerCollector](/docs/reference/engine/classes/LayerCollector.md) - **Property `Enabled`** (`boolean`): Toggles the visibility of this LayerCollector. - **Property `ResetOnSpawn`** (`boolean`): Determines if the LayerCollector resets (deletes itself and - **Property `ZIndexBehavior`** (`ZIndexBehavior`): Controls how GuiObject.ZIndex behaves on all descendants of this - **Method `GetLayoutNodeTree(): Dictionary`**: *(deprecated)* ### From [GuiBase2d](/docs/reference/engine/classes/GuiBase2d.md) - **Property `AbsolutePosition`** (`Vector2`): Describes the actual screen position of a GuiBase2d element, in - **Property `AbsoluteRotation`** (`float`): Describes the actual screen rotation of a GuiBase2d element, in - **Property `AbsoluteSize`** (`Vector2`): Describes the actual screen size of a GuiBase2d element, in - **Property `AutoLocalize`** (`boolean`): When set to `true`, localization will be applied to this GuiBase2d - **Property `Localize`** (`boolean`): Automatically set to true when a localization table's *(deprecated, hidden)* - **Property `RootLocalizationTable`** (`LocalizationTable`): A reference to a LocalizationTable to be used to apply automated - **Property `SelectionBehaviorDown`** (`SelectionBehavior`): Customizes gamepad selection behavior in the down direction. - **Property `SelectionBehaviorLeft`** (`SelectionBehavior`): Customizes gamepad selection behavior in the left direction. - **Property `SelectionBehaviorRight`** (`SelectionBehavior`): Customizes gamepad selection behavior in the right direction. - **Property `SelectionBehaviorUp`** (`SelectionBehavior`): Customizes gamepad selection behavior in the up direction. - **Property `SelectionGroup`** (`boolean`): Allows customization of gamepad selection movement. - **Event `SelectionChanged`**: Fires when the gamepad selection moves to, leaves, or changes within the ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: PluginGuiService last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated summary: "A service that stores PluginGui objects to be displayed in Roblox Studio." --- # Class: PluginGuiService > A service that stores [PluginGui](/docs/reference/engine/classes/PluginGui.md) objects to be displayed in Roblox > Studio. ## Description PluginGuiService is a service that stores [PluginGui](/docs/reference/engine/classes/PluginGui.md) objects to be displayed in Roblox Studio. It only allows PluginGuis to be direct children of the service, and PluginGuis are not allowed to be parented anywhere besides the service. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: PluginManagementService last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated --- # Class: PluginManagementService ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: PluginManager last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable --- # Class: PluginManager ## Description `PluginManager` is a deprecated singleton that was previously required to create plugins. It still has some applicable uses, such as if you need to create a [Plugin](/docs/reference/engine/classes/Plugin.md) object from Studio's [Command Bar](/docs/en-us/studio/ui-overview.md#command-bar). ## Methods ### Method: PluginManager:ExportPlace **Signature:** `PluginManager:ExportPlace(filePath: string): ()` This method exports all geometry in the place to an `.obj` file. The file is saved to the path chosen by the user in a file save dialogue (the `filePath` argument is ignored). *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `filePath` | `string` | | | **Returns:** `()` ### Method: PluginManager:ExportSelection **Signature:** `PluginManager:ExportSelection(filePath: string): ()` This method exports all geometry in the current [Selection](/docs/reference/engine/classes/Selection.md) to an `.obj` file. The file is saved to the path chosen by the user in a file save dialogue (the `filePath` argument is ignored). *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `filePath` | `string` | | | **Returns:** `()` ### Method: PluginManager:CreatePlugin **Signature:** `PluginManager:CreatePlugin(): Instance` > **Deprecated:** The steps to create a plugin have changed. To learn more, see [Plugin](/docs/reference/engine/classes/Plugin.md). *Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `Instance` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: PluginManagerInterface last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated --- # Class: PluginManagerInterface ## Methods ### Method: PluginManagerInterface:ExportPlace **Signature:** `PluginManagerInterface:ExportPlace(filePath: string): ()` *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `filePath` | `string` | | | **Returns:** `()` ### Method: PluginManagerInterface:ExportSelection **Signature:** `PluginManagerInterface:ExportSelection(filePath: string): ()` *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `filePath` | `string` | | | **Returns:** `()` ### Method: PluginManagerInterface:CreatePlugin **Signature:** `PluginManagerInterface:CreatePlugin(): Instance` > **Deprecated:** The steps to create a plugin have changed. To learn more, see [Plugin](/docs/reference/engine/classes/Plugin.md). *Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `Instance` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: PluginMenu last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated summary: "A context menu that can be shown in Studio. Displays a list of PluginActions and supports submenus." --- # Class: PluginMenu > A context menu that can be shown in Studio. Displays a list of > [PluginActions](/docs/reference/engine/classes/PluginAction.md) and supports submenus. ## Description `PluginMenu` represents a context menu which can be shown in Studio to display a list of [PluginActions](/docs/reference/engine/classes/PluginAction.md) and which also supports submenus. A `PluginMenu` must be created using the [Plugin:CreatePluginMenu()](/docs/reference/engine/classes/Plugin.md) method in order to work as expected. ## Code Samples **Creating a PluginMenu and PluginMenuAction** This code sample visualizes how [PluginMenus](/docs/reference/engine/classes/PluginMenu.md) and [PluginActions](/docs/reference/engine/classes/PluginAction.md) behave when created for a [Plugin](/docs/reference/engine/classes/Plugin.md). Outside of this example, you should not parent the plugin or its functional components to the experience's workspace. After executing the code, changing the created [BoolValue](/docs/reference/engine/classes/BoolValue.md) in the experience's workspace opens the plugin's menus. Selecting an action from the menus the function connected to the trigger signal. ```lua -- This code can be pasted into the Command Bar, but only once local pluginMenu = plugin:CreatePluginMenu(math.random(), "Test Menu") pluginMenu.Name = "Test Menu" pluginMenu:AddNewAction("ActionA", "A", "rbxasset://textures/loading/robloxTiltRed.png") pluginMenu:AddNewAction("ActionB", "B", "rbxasset://textures/loading/robloxTilt.png") local subMenu = plugin:CreatePluginMenu(math.random(), "C", "rbxasset://textures/explosion.png") subMenu.Name = "Sub Menu" subMenu:AddNewAction("ActionD", "D", "rbxasset://textures/whiteCircle.png") subMenu:AddNewAction("ActionE", "E", "rbxasset://textures/icon_ROBUX.png") pluginMenu:AddMenu(subMenu) pluginMenu:AddSeparator() pluginMenu:AddNewAction("ActionF", "F", "rbxasset://textures/sparkle.png") local toggle = Instance.new("BoolValue") toggle.Name = "TogglePluginMenu" toggle.Parent = workspace local function onToggled() if toggle.Value then toggle.Value = false local selectedAction = pluginMenu:ShowAsync() if selectedAction then print("Selected Action:", selectedAction.Text, "with ActionId:", selectedAction.ActionId) else print("User did not select an action!") end end end toggle.Changed:Connect(onToggled) ``` ## Properties ### Property: PluginMenu.Icon ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Appearance" } ``` This property determines the icon to be displayed when used as a submenu. It defaults to an empty string `""`. ### Property: PluginMenu.Title ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Appearance" } ``` This property determines the text to be displayed when a [PluginMenu](/docs/reference/engine/classes/PluginMenu.md) is used as a submenu. It defaults to an empty string `""`. ### Property: PluginMenu.Visible ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ## Methods ### Method: PluginMenu:AddAction **Signature:** `PluginMenu:AddAction(action: Instance): ()` Adds the given action to the menu. *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `action` | `Instance` | | The action to add. | **Returns:** `()` ### Method: PluginMenu:AddMenu **Signature:** `PluginMenu:AddMenu(menu: Instance): ()` Adds the given menu as a separator. *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `menu` | `Instance` | | The menu to add as a submenu. Uses its [Title](/docs/reference/engine/classes/PluginMenu.md) and [Icon](/docs/reference/engine/classes/PluginMenu.md) to display. | **Returns:** `()` ### Method: PluginMenu:AddNewAction **Signature:** `PluginMenu:AddNewAction(actionId: string, text: string, icon: string): Instance` Creates a temporary action that is hidden from Studio's **File** ⟩ **Customize Shortcuts** window. *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `actionId` | `string` | | Must be a unique string that identifies this [PluginAction](/docs/reference/engine/classes/PluginAction.md) from others. | | `text` | `string` | | The text to be displayed. | | `icon` | `string` | | The icon to be displayed. | **Returns:** `Instance` — The created [PluginAction](/docs/reference/engine/classes/PluginAction.md). ### Method: PluginMenu:AddSeparator **Signature:** `PluginMenu:AddSeparator(): ()` Adds a separator between items in the menu. *Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `()` ### Method: PluginMenu:Clear **Signature:** `PluginMenu:Clear(): ()` Clears the menu. *Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `()` ### Method: PluginMenu:ShowAsync **Signature:** `PluginMenu:ShowAsync(): Instance` Shows the menu at the mouse cursor. It yields until either an item is selected or the menu is closed. The selected action fires its [PluginAction.Triggered](/docs/reference/engine/classes/PluginAction.md) event. *Yields · Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `Instance` — The [PluginAction](/docs/reference/engine/classes/PluginAction.md) item that was selected or `nil`. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: PluginMouse last_updated: 2026-06-29T19:34:08Z inherits: - Mouse - Instance - Object type: class memory_category: Instances tags: - NotCreatable summary: "The PluginMouse object gives Plugins access to the mouse. It works like the Mouse object and can be obtained using the plugin Plugin:GetMouse() method." --- # Class: PluginMouse > The PluginMouse object gives [Plugins](/docs/reference/engine/classes/Plugin.md) access to the mouse. It > works like the [Mouse](/docs/reference/engine/classes/Mouse.md) object and can be obtained using the plugin > [Plugin:GetMouse()](/docs/reference/engine/classes/Plugin.md) method. ## Description The PluginMouse object gives [Plugins](/docs/reference/engine/classes/Plugin.md) access to the mouse. It works like the [Mouse](/docs/reference/engine/classes/Mouse.md) object and can be obtained using the plugin [Plugin:GetMouse()](/docs/reference/engine/classes/Plugin.md) method. Note the PluginMouse can only be used when the plugin has been activated using [Plugin:Activate()](/docs/reference/engine/classes/Plugin.md). In addition to the functions from the [Mouse](/docs/reference/engine/classes/Mouse.md) object, the PluginMouse includes the [PluginMouse.DragEnter](/docs/reference/engine/classes/PluginMouse.md) function which keeps track of items being selected while the mouse is dragging. For more information on how to use mouse objects, see the [Mouse](/docs/reference/engine/classes/Mouse.md) page. ## Code Samples **PluginMouse Get** The code below demonstrates how the PluginMouse object can be obtained and used in a plugin. To use this code, paste it into a `Script` save that script to the local Plugins Folder using right click, save to file. The plugins folder can be found in the Plugins tab in the Roblox Studio toolbar. ```lua plugin:Activate(false) -- gain non exclusive access to the mouse local mouse = plugin:GetMouse() local function button1Down() print("Left mouse click") end mouse.Button1Down:Connect(button1Down) ``` ## Events ### Event: PluginMouse.DragEnter **Signature:** `PluginMouse.DragEnter(instances: Instances)` Fired when Instances are being selected while the mouse is dragging. *Security: PluginSecurity · Capabilities: Input* **Parameters:** | Name | Type | Description | |------|------|-------------| | `instances` | `Instances` | | ## Inherited Members ### From [Mouse](/docs/reference/engine/classes/Mouse.md) - **Property `Hit`** (`CFrame`): The CFrame of the mouse's position in 3D space. - **Property `hit`** (`CFrame`): *(deprecated, hidden)* - **Property `Icon`** (`ContentId`): The content ID of the image used as the Mouse icon. - **Property `IconContent`** (`Content`): The content of the image used as the Mouse icon. Only supports - **Property `Origin`** (`CFrame`): A CFrame positioned at the Workspace.CurrentCamera and - **Property `Target`** (`BasePart`): The object in 3D space the mouse is pointing to. - **Property `target`** (`BasePart`): *(deprecated)* - **Property `TargetFilter`** (`Instance`): Determines an object (and its descendants) to be ignored when determining - **Property `TargetSurface`** (`NormalId`): Indicates the NormalId of the BasePart surface at which the - **Property `UnitRay`** (`Ray`): A Ray directed towards the mouse's world position, originating - **Property `ViewSizeX`** (`int`): Describes the width of the game window in pixels. - **Property `ViewSizeY`** (`int`): Describes the height of the game window in pixels. - **Property `X`** (`int`): Describes the X (horizontal) component of the mouse's position on the - **Property `Y`** (`int`): Describes the Y (vertical) component of the mouse's screen position. - **Event `Button1Down`**: Fires when the left mouse button is pressed. - **Event `Button1Up`**: Fires when the left mouse button is released. - **Event `Button2Down`**: Fires when the right mouse button is pressed. - **Event `Button2Up`**: Fired when the right mouse button is released. - **Event `Idle`**: Fired during every heartbeat that the mouse isn't being passed to another - **Event `KeyDown`**: Fires when a Key is pressed. *(deprecated)* - **Event `keyDown`**: *(deprecated)* - **Event `KeyUp`**: Fires when a Key is released. *(deprecated)* - **Event `Move`**: Fired when the mouse is moved. - **Event `WheelBackward`**: Fires when the mouse wheel is scrolled backwards. - **Event `WheelForward`**: Fires when the mouse wheel is scrolled forwards. ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: PluginToolbar last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable --- # Class: PluginToolbar ## Description A `PluginToolbar` object is created by calling the [Plugin:CreateToolbar()](/docs/reference/engine/classes/Plugin.md) method and is used to contain [PluginToolbarButtons](/docs/reference/engine/classes/PluginToolbarButton.md). ## Methods ### Method: PluginToolbar:CreateButton **Signature:** `PluginToolbar:CreateButton(buttonId: string, tooltip: string, iconname: string, text: string): PluginToolbarButton` Creates a [PluginToolbarButton](/docs/reference/engine/classes/PluginToolbarButton.md) that allows the user to initiate a single, one-off action in Studio through its [Click](/docs/reference/engine/classes/PluginToolbarButton.md) event. *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `buttonId` | `string` | | A unique button ID. | | `tooltip` | `string` | | The text displayed in the tooltip shown when a user hovers over the button. | | `iconname` | `string` | | The asset ID of the icon displayed in the button. | | `text` | `string` | | Optional text displayed under the button icon. | **Returns:** `PluginToolbarButton` — The created [PluginToolbarButton](/docs/reference/engine/classes/PluginToolbarButton.md) instance. **Adding a Plugin Toolbar Button** Creates a custom toolbar and **Plugins** menu folder containing a button that, when clicked, inserts an empty script in [ServerScriptService](/docs/reference/engine/classes/ServerScriptService.md). ```lua local ServerScriptService = game:GetService("ServerScriptService") -- Create a new toolbar section and Plugins menu folder titled "Custom" local toolbar = plugin:CreateToolbar("Custom") -- Add a toolbar button labeled "Empty Script" local newScriptButton = toolbar:CreateButton("Empty Script", "Create an empty script", "rbxassetid://14978048121") -- Make button clickable even if 3D viewport is hidden newScriptButton.ClickableWhenViewportHidden = true local function onPluginButtonClicked() local newScript = Instance.new("Script") newScript.Source = "" newScript.Parent = ServerScriptService end newScriptButton.Click:Connect(onPluginButtonClicked) ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: PluginToolbarButton last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable --- # Class: PluginToolbarButton ## Description A `PluginToolbarButton` object is created through the [PluginToolbar:CreateButton()](/docs/reference/engine/classes/PluginToolbar.md) function. It allows the user to initiate a single, one-off action in Roblox Studio through the [Click](/docs/reference/engine/classes/PluginToolbarButton.md) event. Toolbar buttons can also be assigned a keyboard shortcut through Studio's **File** ⟩ **Customize Shortcuts** window. When pressed, the [Click](/docs/reference/engine/classes/PluginToolbarButton.md) event fires. A button will also remain in the pressed state, which may be set manually using [SetActive()](/docs/reference/engine/classes/PluginToolbarButton.md). Upon plugin activation ([Plugin:Activate()](/docs/reference/engine/classes/Plugin.md)), buttons in all other [PluginToolbars](/docs/reference/engine/classes/PluginToolbar.md) will be toggled off. If all buttons in a toolbar are off, the toolbar's plugin is deactivated ([Plugin:Deactivate()](/docs/reference/engine/classes/Plugin.md)). When the game viewport is not visible, buttons will be disabled as if their [Enabled](/docs/reference/engine/classes/PluginToolbarButton.md) property were false. Disabled buttons are desaturated and do not respond to user clicks. By setting [ClickableWhenViewportHidden](/docs/reference/engine/classes/PluginToolbarButton.md) to true, you can allow plugin buttons to remain clickable, such as during script editing. ## Properties ### Property: PluginToolbarButton.ClickableWhenViewportHidden ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Appearance" } ``` This property determines whether a [PluginToolbarButton](/docs/reference/engine/classes/PluginToolbarButton.md) may be clicked while the 3D viewport is hidden, such as when a [Script](/docs/reference/engine/classes/Script.md) is being edited in another tab. Typically, this property should be enabled if an action triggered by a plugin button's [Click](/docs/reference/engine/classes/PluginToolbarButton.md) event doesn't occur in the 3D world ([Workspace](/docs/reference/engine/classes/Workspace.md)). For example, a button that opens a widget should have this property as `true`, since showing a widget is visible to the user even if the 3D view isn't visible. ### Property: PluginToolbarButton.Enabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Appearance" } ``` This property determines whether a button is clickable in general. When `false`, the button will be greyed out and unclickable, preventing the user from firing the [Click](/docs/reference/engine/classes/PluginToolbarButton.md) event. Buttons are enabled by default. When re-enabling this property, the plugin button's state won't be remembered from the previous state in which the user left the button in. Instead, it will default to the last state set by [SetActive()](/docs/reference/engine/classes/PluginToolbarButton.md) or to the inactive state if [SetActive()](/docs/reference/engine/classes/PluginToolbarButton.md) was never used. Plugins should disable their buttons when the button action isn't relevant in the current context. For example, a plugin button that assigns random colors to selected should not be enabled when the selection contains no parts. See also [ClickableWhenViewportHidden](/docs/reference/engine/classes/PluginToolbarButton.md) which determines whether a button is clickable when the game view is hidden (and not just in general). **BrickColor Randomizer Plugin** This code sample is for a studio `Plugin`. The plugin creates a `PluginToolbarButton` which randomizes the [BrickColor()](/docs/reference/engine/classes/BasePart.md) of each selected part using `BrickColor.random()`. Furthermore, the button is only enabled if at least one part is selected. It does this by detecting changes in the `Selection` using [Selection.SelectionChanged](/docs/reference/engine/classes/Selection.md). ```lua assert(plugin, "This script must be run as a plugin") local Selection = game:GetService("Selection") local toolbar = plugin:CreateToolbar("Parts") local pluginToolbarButton = toolbar:CreateButton( "Randomize Colors", "Click this button to assign random colors to selected parts", "rbxassetid://5325741572" -- A rainbow ) local function onClick() local selection = Selection:Get() for _, object in pairs(selection) do if object:IsA("BasePart") then object.BrickColor = BrickColor.random() end end end pluginToolbarButton.Click:Connect(onClick) local function doesSelectionContainAPart() local selection = Selection:Get() for _, object in pairs(selection) do if object:IsA("BasePart") then return true end end return false end local function onSelectionChanged() pluginToolbarButton.Enabled = doesSelectionContainAPart() end Selection.SelectionChanged:Connect(onSelectionChanged) onSelectionChanged() ``` ### Property: PluginToolbarButton.Icon ```json { "type": "ContentId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Appearance" } ``` This property determines which [Content](/docs/reference/engine/datatypes/Content.md) should be shown for the button's icon in the toolbar. When this property is not set, the button will instead use the button's text given by [PluginToolbar:CreateButton()](/docs/reference/engine/classes/PluginToolbar.md). ## Methods ### Method: PluginToolbarButton:SetActive **Signature:** `PluginToolbarButton:SetActive(active: boolean): ()` This method can be used to manually set the active state of the plugin button. When the [Enabled](/docs/reference/engine/classes/PluginToolbarButton.md) property is toggled back on, the button will either revert to the last state set by this method or default to inactive if this method hasn't been used previously. *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `active` | `boolean` | | | **Returns:** `()` ## Events ### Event: PluginToolbarButton.Click **Signature:** `PluginToolbarButton.Click()` This event fires when the [PluginToolbarButton](/docs/reference/engine/classes/PluginToolbarButton.md) is pressed and released by the user. See also [SetActive()](/docs/reference/engine/classes/PluginToolbarButton.md) to manually set the state of the button. *Security: PluginSecurity* **PluginToolbarButton.Click** This code sample demonstrates creating a `PluginToolbar` and a `PluginToolbarButton` on it, then connecting a function `onClick` to the [PluginToolbarButton.Click](/docs/reference/engine/classes/PluginToolbarButton.md) event. When pressed, the button will print "Hello, world" to the output. ```lua assert(plugin, "This script must be run as a plugin") local toolbar = plugin:CreateToolbar("Hello World Plugin Toolbar") local pluginToolbarButton = toolbar:CreateButton("Print Hello World", "Click this button to print Hello World!", "rbxassetid://133293265") local function onClick() print("Hello, world") end pluginToolbarButton.Click:Connect(onClick) ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: PointLight last_updated: 2026-06-29T19:34:08Z inherits: - Light - Instance - Object type: class memory_category: Instances summary: "A light source that emits illumination from a single point." --- # Class: PointLight > A light source that emits illumination from a single point. ## Description A PointLight is a light source that emits illumination from a single point. Light is emitted spherically based on the [PointLight.Range](/docs/reference/engine/classes/PointLight.md) of the PointLight. In order for a PointLight to provide illumination, it must be the direct child of a [BasePart](/docs/reference/engine/classes/BasePart.md) or [Attachment](/docs/reference/engine/classes/Attachment.md) (the part or attachment itself must be a descendant of the [Workspace](/docs/reference/engine/classes/Workspace.md)). If a PointLight is parented to a part, then the light will emanate from the part's [BasePart.Position](/docs/reference/engine/classes/BasePart.md). If a PointLight is parented to an attachment, then the light will emanate from the attachment's [Attachment.WorldPosition](/docs/reference/engine/classes/Attachment.md). For more light types, see the **see also** section. ## See Also - [SurfaceLight](/docs/reference/engine/classes/SurfaceLight.md) - [SpotLight](/docs/reference/engine/classes/SpotLight.md) ## Code Samples **Creating a New Point Light** This example creates a new anchored `BasePart` named _Part_ at the position `{0, 0, 0}`. It then creates a new point light with `brightness` of 1, `Color3` `color` of `{255/255, 255/255, 255/255}` (white) and `range` of 16 studs. The point light's parent is set to the BasePart we created. To view the light, navigate to the part at `{0, 0, 0}` or move the _Part_ created to a location visible to the player. Please note that the properties of the created point light can easily be changed by modifying the property values in the code sample below. Additionally, if you have an existing point light, you can also create a similar script that modifies that light instead of creating a new BasePart and light. ```lua local part = Instance.new("Part") part.Anchored = true part.Position = Vector3.new(0, 0, 0) part.Parent = workspace local light = Instance.new("PointLight") light.Color = Color3.new(1, 1, 1) light.Brightness = 1 light.Range = 16 light.Parent = part ``` ## Properties ### Property: PointLight.Range ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` The size of the area that the PointLight will illuminate. ## Inherited Members ### From [Light](/docs/reference/engine/classes/Light.md) - **Property `Brightness`** (`float`): Sets how bright the emitted light is, defaults to 1. - **Property `Color`** (`Color3`): The color of the emitted light. - **Property `Enabled`** (`boolean`): If set to true, light will be emitted from the source object. - **Property `Shadows`** (`boolean`): If set to true, will project shadows if light is blocked by an obstacle. ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: PointsService last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - Deprecated summary: "This service controls the points award system used to showcase a player's achievements and participation throughout Roblox." --- # Class: PointsService > This service controls the points award system used to showcase a player's > achievements and participation throughout Roblox. ## Description The PointsService class controls points. Points are an award system used to showcase a player's achievements and participation throughout Roblox. How points are awarded through this service is at the discretion of the game's developer. ## Methods ### Method: PointsService:AwardPoints **Signature:** `PointsService:AwardPoints(userId: int64, amount: int): Tuple` > **Deprecated:** This function was once part of the `PointService` class used to control an ancient achievement system since removed and deprecated. It should not be used in new work. This function attempts to award the user with the specified [Player.UserId](/docs/reference/engine/classes/Player.md) the specified number of points. If successful, this function will return the UserId of the user the points were awarded to, the number of points awarded, the new total number of points the user has in the game and another value which appears to always be 0. This function will cause an error if the specified UserId is not positive or if the number of points specified is 0. The function can be used to award a negative number of points to a user however. ```lua local userId, amount, total = game:GetService("PointsService"):AwardPoints(1, 5) print("The user was awarded " ..amount.. " points. They have now have a total of " ..total.. " points in this game.") ``` *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `userId` | `int64` | | | | `amount` | `int` | | | **Returns:** `Tuple` **PointsService:AwardPoints** The below example would attempt to award 5 points to the Roblox account. If the account had been awarded 10 points before, it would print "The user was awarded 5 points. They have now a total of 15 points in this game.". ```lua local PointsService = game:GetService("PointsService") local userId, amount, total = PointsService:AwardPoints(1, 5) print("User", userId, "was awarded", amount, "points. They have now have a total of", total, "points in this game.") ``` ### Method: PointsService:GetAwardablePoints **Signature:** `PointsService:GetAwardablePoints(): int` > **Deprecated:** This function was once part of the `PointService` class used to control an ancient achievement system since removed and deprecated. It should not be used in new work. This function returns the number of points the current game has available to award to players. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Returns:** `int` ### Method: PointsService:GetGamePointBalance **Signature:** `PointsService:GetGamePointBalance(userId: int64): int` > **Deprecated:** This function was once part of the `PointService` class used to control an ancient achievement system since removed and deprecated. It should not be used in new work. This function returns the total number of points a player has in the current game. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `userId` | `int64` | | | **Returns:** `int` ### Method: PointsService:GetPointBalance **Signature:** `PointsService:GetPointBalance(userId: int64): int` > **Deprecated:** This function was once part of the `PointService` class used to control an ancient achievement system since removed and deprecated. It should not be used in new work. This function returns the total number of points the given player has across **_all_** games. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `userId` | `int64` | | | **Returns:** `int` ## Events ### Event: PointsService.PointsAwarded **Signature:** `PointsService.PointsAwarded(userId: int64, pointsAwarded: int, userBalanceInGame: int, userTotalBalance: int)` This event fires when points have been successfully awarded to a player, while also passing along the updated balance of points the player has in the current game and all games. When a player is awarded points successfully the below example would print the userId and their new point balance. If, for example, the Roblox account was awarded thirty points (and had none to begin with) > User: 1 has now earned 30 (+30) points in the current game, now making > their total balance would be printed. ```lua local function pointsAwarded(userId, pointsAwarded, userBalanceInGame, userTotalBalance) print("User: " .. userId .. " has now earned " .. userBalanceInGame .. " (+" .. pointsAwarded ..") points in the current game, now making their total balance " .. userTotalBalance) end game:GetService("PointsService").PointsAwarded:Connect(pointsAwarded) ``` *Security: None · Capabilities: Basic* **Parameters:** | Name | Type | Description | |------|------|-------------| | `userId` | `int64` | | | `pointsAwarded` | `int` | | | `userBalanceInGame` | `int` | | | `userTotalBalance` | `int` | | **PointsService.PointsAwarded** When a player is awarded points successfully the below example would print the userId and their new point balance. If, for example, the Roblox account was awarded thirty points (and had none to begin with) "User: 1 has now earned 30 (+30) points in the current game, now making their total balance 30" would be printed. ```lua local PointsService = game:GetService("PointsService") local function onPointsAwarded(userId, pointsAwarded, userBalanceInGame, userTotalBalance) print( "User:", userId, "has now earned", userBalanceInGame, "(+", pointsAwarded, ") points in the current game, now making their total balance", userTotalBalance ) end PointsService.PointsAwarded:Connect(onPointsAwarded) ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: PolicyService last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated summary: "Helps you query information regarding policy compliance for players around the world based on age range, location, and platform type." --- # Class: PolicyService > Helps you query information regarding policy compliance for players around the > world based on age range, location, and platform type. ## Description `PolicyService` helps you query information regarding policy compliance for players around the world based on age range, location, and platform type. ## Methods ### Method: PolicyService:CanViewBrandProjectAsync **Signature:** `PolicyService:CanViewBrandProjectAsync(player: Player, brandProjectId: string): boolean` Determines if a user can see brand project assets inside your experience. This method lets you work with brands to only show commercial assets to brand-compliant audiences. To use `CanViewBrandProjectAsync`, you must use a brand project ID provided by Roblox. To request a brand project ID, contact us. You must call this method on a server-side Script and wrap it in a [LuaGlobals.pcall()](/docs/reference/engine/globals/LuaGlobals.md). *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Player` | | The `Player` object you're trying to show the brand project to. | | `brandProjectId` | `string` | | The brand project ID provided by Roblox. Represents all assets associated with a brand project. | **Returns:** `boolean` — Whether or not the brand project can be shown to the specific user. **Server-side code sample** You must call `CanViewBrandProjectAsync` from the server-side and then fire an event on the client. Calling this method from the client-side returns an error. ```lua -- In ServerScriptService local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") local PolicyService = game:GetService("PolicyService") -- Pre-created RemoteEvent in ReplicatedStorage local RemoteEvent = ReplicatedStorage:WaitForChild("RemoteEvent") local brandedAsset = ReplicatedStorage:WaitForChild("BrandedAsset") local defaultAsset = Instance.new("Part") Players.PlayerAdded:Connect(function(player) -- PolicyService:CanViewBrandProjectAsync can only be called from the Server local success, canView = pcall(function() return PolicyService:CanViewBrandProjectAsync(player, "BRP-0123456789") end) if success and canView then RemoteEvent:FireClient(player, brandedAsset) else RemoteEvent:FireClient(player, defaultAsset) end end) ``` **Client-side code sample** ```lua -- In StarterPlayerScripts local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") -- Pre-created RemoteEvent in ReplicatedStorage local RemoteEvent = ReplicatedStorage:WaitForChild("RemoteEvent") RemoteEvent.OnClientEvent:Connect(function(partToLoad) local clonedPart = partToLoad:Clone() clonedPart.Parent = workspace end) ``` ### Method: PolicyService:GetPolicyInfoForPlayerAsync **Signature:** `PolicyService:GetPolicyInfoForPlayerAsync(player: Instance): Dictionary` Returns policy information about a player based on geolocation, age group, and platform. The structure of the returned dictionary is as follows: | Name | Type | Required for | Description | | --- | --- | --- | --- | | `AreAdsAllowed` | Boolean | Any experience that includes [immersive ads](/docs/en-us/production/monetization/immersive-ads.md). | When `true`, the player might see immersive ads within an experience. | | `ArePaidRandomItemsRestricted` | Boolean | Any experience that has paid random items. | When `true`, the player can **not** interact with paid random item generators, either via in‑experience currency bought with Robux or Robux directly. | | `IsContentSharingAllowed` | Boolean | Any feature in the experience that enables users to create content (e.g. text, images, video, audio) that other users can see in the experience or allows user to share content within experiences. | When `true`, the player can use features related to sharing UGC within an experience, such as uploading a screen capture, sharing a video, or posting an image to a content feed that other users can see. | | `IsEligibleToPurchaseCommerceProduct` | Boolean | Any experience that wants to sell [commerce products](/docs/en-us/production/monetization/commerce-products.md). | When `true`, the player is eligible to purchase commerce products within an experience. | | `IsEligibleToPurchaseSubscription` | Boolean | Any experience that wants to sell subscriptions. | When `true`, the player is eligible to purchase subscriptions within an experience. | | `IsPaidItemTradingAllowed` | Boolean | Any experience that allows users to purchase virtual items that they can trade with other players. | When `true`, the player can trade virtual items that they purchased with in-experience currency or Robux. | | `IsPhotoToAvatarAllowed` | Boolean | Any experience that uses the Photo-to-Avatar APIs. | When `true`, the Photo-to-Avatar API [AvatarCreationService: PromptSelectAvatarGenerationImageAsync()](/docs/reference/engine/classes/AvatarCreationService.md) is available to the player. | | `IsSubjectToChinaPolicies` | Boolean | Any experience that is available in China. | When `true`, an experience should enforce compliance changes. See [this forum post](https://devforum.roblox.com/t/new-programs-available-roblox-china-licensed-to-operate/1023361) for more information. | | `AllowedExternalLinkReferences` | Array | Not required. | This is a legacy field. It always returns an empty array. | | `IsEndlessContentLoadAllowed` | Boolean | Any experience that includes a feature where content loads automatically and endlessly as the user scrolls, such as a feed, providing an endless flow of media content without requiring the user to engage in any specific interaction such as manual "load more" actions or separate pagination. | When `true`, the player can use features with endless content loading within an experience. | | `IsEndlessContentAutoplayAllowed` | Boolean | Any experience that contains media content (video or audio) that automatically and endlessly plays without requiring user interaction or initiation, for example clicking a play button. | When `true`, the player can use features within an experience that automatically and endlessly play media content. | ##### Exceptions Like any async call, this method needs to be wrapped in [LuaGlobals.pcall()](/docs/reference/engine/globals/LuaGlobals.md) and error-handled properly. A full list of possible error messages and their reasons is: | Message | Reason | | --- | --- | | Instance was not a player | The `player` parameter is not a [Player](/docs/reference/engine/classes/Player.md) instance. | | Players not found | Internal error that the [Players](/docs/reference/engine/classes/Players.md) object is missing. | | This method cannot be called on the client for a non-local player | This method cannot be called on the client for a non-local [Player](/docs/reference/engine/classes/Player.md). | | GetPolicyInfoForPlayerAsync is called too many times | Internal error that `GetPolicyInfoForPlayerAsync()` is called more than 100 (current setting) times before an HTTP response comes back. | See also [LocalizationService:GetCountryRegionForPlayerAsync()](/docs/reference/engine/classes/LocalizationService.md) which returns a country/region code string according to the player's client IP geolocation. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Instance` | | The [Player](/docs/reference/engine/classes/Player.md) to get policy information for. | **Returns:** `Dictionary` — A dictionary containing information about the policy information of the requested player; see above for the dictionary structure. **Getting Policy Information for a Player** This code sample gets policy information for the local player and warns if they cannot interact with paid random item generators. ```lua local PolicyService = game:GetService("PolicyService") local Players = game:GetService("Players") local player = Players.LocalPlayer local success, result = pcall(function() return PolicyService:GetPolicyInfoForPlayerAsync(player) end) if not success then warn("PolicyService error: " .. result) elseif result.ArePaidRandomItemsRestricted then warn("Player cannot interact with paid random item generators") end ``` **Getting Policy Information for a Player** This code sample gets policy information for the local player and warns if they cannot purchase a real-world commerce product. ```lua local PolicyService = game:GetService("PolicyService") local Players = game:GetService("Players") local player = Players.LocalPlayer local success, result = pcall(function() return PolicyService:GetPolicyInfoForPlayerAsync(player) end) if not success then warn("PolicyService error: " .. result) elseif not result.IsEligibleToPurchaseCommerceProduct then warn("Player is not eligible to purchase commerce products") end ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Pose last_updated: 2026-06-29T19:34:08Z inherits: - PoseBase - Instance - Object type: class memory_category: Animation summary: "Holds the CFrame applied to the Motor6D connected to its associated BasePart. The part which is controlled depends on the name of the Pose." --- # Class: Pose > Holds the [CFrame](/docs/reference/engine/datatypes/CFrame.md) applied to the [Motor6D](/docs/reference/engine/classes/Motor6D.md) connected to its > associated [BasePart](/docs/reference/engine/classes/BasePart.md). The part which is controlled depends on the name > of the Pose. ## Description A Pose holds the [CFrame](/docs/reference/engine/datatypes/CFrame.md) applied to the [Motor6D](/docs/reference/engine/classes/Motor6D.md) connected to its associated [BasePart](/docs/reference/engine/classes/BasePart.md). The part which is controlled depends on the name of the Pose. Poses are the fundamental building blocks of animations and, with `Keyframes`, make up `KeyframeSequences`. ## Poses, joints and hierarchy Although a Pose is assigned to a [BasePart](/docs/reference/engine/classes/BasePart.md) by name, the object manipulated during animation playback is actually the [Motor6D](/docs/reference/engine/classes/Motor6D.md) connected to this part. Animation rigs branch out from the model's root part through such joints. In a R15 character rig, the root part is the HumanoidRootPart. The LowerTorso is connected to the HumanoidRootPart by the a motor named 'Root'. Therefore, the [CFrame](/docs/reference/engine/datatypes/CFrame.md) of a Pose named 'LowerTorso' in a [Keyframe](/docs/reference/engine/classes/Keyframe.md) would be applied to the motor named 'Root', and not the LowerTorso itself. Poses are arranged in a [Keyframe](/docs/reference/engine/classes/Keyframe.md) based on joint hierarchy. This means, the Pose's [CFrame](/docs/reference/engine/datatypes/CFrame.md) is applied to the motor connecting the part associated with the pose to the part associated with the pose's parent. See below for a visual example of the structure of Poses on a R15 character. ## Pose CFrame The Roblox animation system applies [Pose.CFrame](/docs/reference/engine/classes/Pose.md) to the corresponding [Motor6D](/docs/reference/engine/classes/Motor6D.md) by manipulating the relative transformation of the motor, the [Motor6D.Transform](/docs/reference/engine/classes/Motor6D.md) property. The original [C0](/docs/reference/engine/classes/JointInstance.md) and [C1](/docs/reference/engine/classes/JointInstance.md) values are not changed. ## Code Samples **Keyframe Generate Poses** This sample includes a function that will generate a 'blank' keyframe containing blank poses for all of the model's connected parts in the correct hierarchical order. ```lua local function generateKeyframe(model) if not model.PrimaryPart then warn("No primary part set") return end local rootPart = model.PrimaryPart:GetRootPart() if not rootPart then warn("Root part not found") return end local partsAdded = {} partsAdded[rootPart] = true local function addPoses(part, parentPose) -- get all of the joints attached to the part for _, joint in pairs(part:GetJoints()) do -- we're only interested in Motor6Ds if joint:IsA("Motor6D") then -- find the connected part local connectedPart = nil if joint.Part0 == part then connectedPart = joint.Part1 elseif joint.Part1 == part then connectedPart = joint.Part0 end if connectedPart then -- make sure we haven't already added this part if not partsAdded[connectedPart] then partsAdded[connectedPart] = true -- create a pose local pose = Instance.new("Pose") pose.Name = connectedPart.Name parentPose:AddSubPose(pose) -- recurse addPoses(connectedPart, pose) end end end end end local keyframe = Instance.new("Keyframe") -- populate the keyframe local rootPose = Instance.new("Pose") rootPose.Name = rootPart.Name addPoses(rootPart, rootPose) keyframe:AddPose(rootPose) return keyframe end local character = script.Parent local keyframe = generateKeyframe(character) print(keyframe) ``` **Keyframe Reset Poses** This code sample includes a function to reset the CFrame of the Poses in a Keyframe. ```lua local function resetPoses(parent) -- both functions are equivalent to GetChildren local poses = parent:IsA("Keyframe") and parent:GetPoses() or parent:IsA("Pose") and parent:GetSubPoses() for _, pose in pairs(poses) do if pose:IsA("Pose") then pose.CFrame = CFrame.new() -- recurse resetPoses(pose) end end end ``` ## Properties ### Property: Pose.CFrame ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Animation" ] } ``` This [CFrame](/docs/reference/engine/datatypes/CFrame.md) applies to the [Motor6D](/docs/reference/engine/classes/Motor6D.md) corresponding with the [Pose](/docs/reference/engine/classes/Pose.md) when the [Motor6D.Transform](/docs/reference/engine/classes/Motor6D.md) is changed. The original [Motor6D.C0](/docs/reference/engine/classes/Motor6D.md) and [Motor6D.C1](/docs/reference/engine/classes/Motor6D.md) values are not changed. [Pose](/docs/reference/engine/classes/Pose.md) objects are arranged in a [Keyframe](/docs/reference/engine/classes/Keyframe.md) based on joint hierarchy. This means, that the [Pose.CFrame](/docs/reference/engine/classes/Pose.md) is applied to the motor connecting the part associated with the pose to the part associated with the pose's parent. ### Property: Pose.MaskWeight ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Animation" ] } ``` > **Deprecated:** This property is deprecated. Use the [AnimationTrack:AdjustWeight()](/docs/reference/engine/classes/AnimationTrack.md) function when blending multiple animations. ## Methods ### Method: Pose:AddSubPose **Signature:** `Pose:AddSubPose(pose: Instance): ()` Adds a sub [Pose](/docs/reference/engine/classes/Pose.md) to the [Pose](/docs/reference/engine/classes/Pose.md) by parenting it to it. It is functionally identical to setting the new pose's [Instance.Parent](/docs/reference/engine/classes/Instance.md) to the pose. Note, this function will not error when an instance other than a [Pose](/docs/reference/engine/classes/Pose.md) is given as the pose parameter and will parent it successfully. *Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `pose` | `Instance` | | The [Pose](/docs/reference/engine/classes/Pose.md) to be added. | **Returns:** `()` **Keyframe Add/Remove Pose** This sample demonstrates quickly the Keyframe.AddPose, Keyframe.RemovePose and Pose.AddSubPose and Pose.RemoveSubPose functions. Note these are functionally equivalent to parenting and un-parenting the poses. ```lua local keyframe = Instance.new("Keyframe") keyframe.Parent = workspace local pose = Instance.new("Pose") pose.EasingStyle = Enum.PoseEasingStyle.Cubic pose.EasingDirection = Enum.PoseEasingDirection.Out local pose2 = Instance.new("Pose") pose2.EasingStyle = Enum.PoseEasingStyle.Cubic pose2.EasingDirection = Enum.PoseEasingDirection.Out keyframe:AddPose(pose) -- pose.Parent = keyframe task.wait(2) keyframe:RemovePose(pose) -- pose.Parent = nil task.wait(2) keyframe:AddPose(pose) -- pose.Parent = keyframe task.wait(2) pose:AddSubPose(pose2) -- pose2.Parent = pose task.wait(2) pose:RemoveSubPose(pose2) -- pose2.Parent = nil ``` ### Method: Pose:GetSubPoses **Signature:** `Pose:GetSubPoses(): Instances` Returns an array containing all sub [Poses](/docs/reference/engine/classes/Pose.md) that have been added to a [Pose](/docs/reference/engine/classes/Pose.md). This is functionally the same as using the [Instance:GetChildren()](/docs/reference/engine/classes/Instance.md) function on the [Pose](/docs/reference/engine/classes/Pose.md). Note: this function returns all children of the [Pose](/docs/reference/engine/classes/Pose.md), including non [Pose](/docs/reference/engine/classes/Pose.md) [Instances](/docs/reference/engine/classes/Instance.md) if any are present. *Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Returns:** `Instances` — An array of sub [Poses](/docs/reference/engine/classes/Pose.md). **Keyframe Reset Poses** This code sample includes a function to reset the CFrame of the Poses in a Keyframe. ```lua local function resetPoses(parent) -- both functions are equivalent to GetChildren local poses = parent:IsA("Keyframe") and parent:GetPoses() or parent:IsA("Pose") and parent:GetSubPoses() for _, pose in pairs(poses) do if pose:IsA("Pose") then pose.CFrame = CFrame.new() -- recurse resetPoses(pose) end end end ``` ### Method: Pose:RemoveSubPose **Signature:** `Pose:RemoveSubPose(pose: Instance): ()` Removes a sub [Pose](/docs/reference/engine/classes/Pose.md) from the [Pose](/docs/reference/engine/classes/Pose.md) by parenting it to `nil`. This is functionally identical to setting the new pose's [Instance.Parent](/docs/reference/engine/classes/Instance.md) to `nil`. Note: If an [Instance](/docs/reference/engine/classes/Instance.md) other than [Pose](/docs/reference/engine/classes/Pose.md) is used as a [Pose](/docs/reference/engine/classes/Pose.md) parameter, this function removes that [Instance](/docs/reference/engine/classes/Instance.md) and does not provide an error. *Security: None · Thread Safety: Unsafe · Capabilities: Animation* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `pose` | `Instance` | | The [Pose](/docs/reference/engine/classes/Pose.md) to be removed. | **Returns:** `()` **Keyframe Add/Remove Pose** This sample demonstrates quickly the Keyframe.AddPose, Keyframe.RemovePose and Pose.AddSubPose and Pose.RemoveSubPose functions. Note these are functionally equivalent to parenting and un-parenting the poses. ```lua local keyframe = Instance.new("Keyframe") keyframe.Parent = workspace local pose = Instance.new("Pose") pose.EasingStyle = Enum.PoseEasingStyle.Cubic pose.EasingDirection = Enum.PoseEasingDirection.Out local pose2 = Instance.new("Pose") pose2.EasingStyle = Enum.PoseEasingStyle.Cubic pose2.EasingDirection = Enum.PoseEasingDirection.Out keyframe:AddPose(pose) -- pose.Parent = keyframe task.wait(2) keyframe:RemovePose(pose) -- pose.Parent = nil task.wait(2) keyframe:AddPose(pose) -- pose.Parent = keyframe task.wait(2) pose:AddSubPose(pose2) -- pose2.Parent = pose task.wait(2) pose:RemoveSubPose(pose2) -- pose2.Parent = nil ``` ## Inherited Members ### From [PoseBase](/docs/reference/engine/classes/PoseBase.md) - **Property `EasingDirection`** (`PoseEasingDirection`): The easing direction to use to reach the next Pose's value. - **Property `EasingStyle`** (`PoseEasingStyle`): The easing style to use to reach the next Pose's value. - **Property `Weight`** (`float`): ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: PoseBase last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable summary: "Base class of all 'Pose Instance' objects." --- # Class: PoseBase > Base class of all 'Pose Instance' objects. ## Properties ### Property: PoseBase.EasingDirection ```json { "type": "PoseEasingDirection", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Animation" ] } ``` The easing direction to use to reach the next Pose's value. ### Property: PoseBase.EasingStyle ```json { "type": "PoseEasingStyle", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Animation" ] } ``` The easing style to use to reach the next Pose's value. ### Property: PoseBase.Weight ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Animation" ] } ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: PostEffect last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable summary: "Abstract base class for post-processing effects." --- # Class: PostEffect > Abstract base class for post-processing effects. ## Description PostEffect is an abstract base class for post-processing effects, such as [BloomEffect](/docs/reference/engine/classes/BloomEffect.md) and [ColorCorrectionEffect](/docs/reference/engine/classes/ColorCorrectionEffect.md). They change how the world looks **after** it has been rendered. They do not affect [GuiObjects](/docs/reference/engine/classes/GuiObject.md). Objects of this kind should be parented to the [Lighting](/docs/reference/engine/classes/Lighting.md) or the [Workspace.CurrentCamera](/docs/reference/engine/classes/Workspace.md) in order to work. It should also be noted that some post-processing effects will work differently or **not at all** when Roblox is set to a low [QualityLevel](/docs/reference/engine/classes/RenderSettings.md) (or [EditQualityLevel](/docs/reference/engine/classes/RenderSettings.md) in Studio). On some low-end devices, faster rendering algorithms may be used. By default, these quality settings are set to Automatic, so if you aren't seeing post-processing effects you should check Roblox's settings under the "Rendering" section. It may be necessary to override the automatic behavior temporarily in order to preview post-processing effects. ## Properties ### Property: PostEffect.Enabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Environment" ] } ``` Toggles whether or not the PostEffect is enabled. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: PrismaticConstraint last_updated: 2026-06-29T19:34:08Z inherits: - SlidingBallConstraint - Constraint - Instance - Object type: class memory_category: BaseParts summary: "Constraint which creates a rigid joint between two Attachments, allowing them to slide along one axis but not rotate." --- # Class: PrismaticConstraint > Constraint which creates a rigid joint between two > [Attachments](/docs/reference/engine/classes/Attachment.md), allowing them to slide along one axis but not > rotate. ## Description A **PrismaticConstraint** creates a rigid joint between two [Attachments](/docs/reference/engine/classes/Attachment.md), allowing them to slide along one axis but not rotate. This constrains the attachments so that their **X** axes are collinear but pointing in opposite directions. It also constrains the attachments so that their **Y** axes are parallel. This constraint inherits many properties from [SlidingBallConstraint](/docs/reference/engine/classes/SlidingBallConstraint.md) including [ActuatorType](/docs/reference/engine/classes/SlidingBallConstraint.md), [LimitsEnabled](/docs/reference/engine/classes/SlidingBallConstraint.md), [Velocity](/docs/reference/engine/classes/SlidingBallConstraint.md), and more. Please refer to [SlidingBallConstraint](/docs/reference/engine/classes/SlidingBallConstraint.md) for details on configuring a [PrismaticConstraint](/docs/reference/engine/classes/PrismaticConstraint.md). When configuring this constraint, it may be helpful to study [Roblox Units](/docs/en-us/physics/units.md) to understand how Roblox units compare to metric units. ## Inherited Members ### From [SlidingBallConstraint](/docs/reference/engine/classes/SlidingBallConstraint.md) - **Property `ActuatorType`** (`ActuatorType`): Sets whether the translation of the SlidingBallConstraint is - **Property `CurrentPosition`** (`float`): The current offset between the constraint's - **Property `LimitsEnabled`** (`boolean`): Sets whether the SlidingBallConstraint will limit the range of - **Property `LinearResponsiveness`** (`float`): Specifies the "sharpness" of the linear servo motor in reaching the - **Property `LowerLimit`** (`float`): The lower positional limit along the **X** axis of - **Property `MotorMaxAcceleration`** (`float`): The constraint's maximum acceleration when - **Property `MotorMaxForce`** (`float`): The constraint's maximum force when - **Property `Restitution`** (`float`): The elasticity of the constraint's Attachments when - **Property `ServoMaxForce`** (`float`): The constraint's maximum force when - **Property `Size`** (`float`): The constraint's visualized size. - **Property `SoftlockServoUponReachingTarget`** (`boolean`): *(deprecated)* - **Property `Speed`** (`float`): The constraint's desired speed when - **Property `TargetPosition`** (`float`): The constraint's attempted target position when - **Property `UpperLimit`** (`float`): The upper positional limit along the **X** axis of - **Property `Velocity`** (`float`): The constraint's attempted velocity when ### From [Constraint](/docs/reference/engine/classes/Constraint.md) - **Property `Active`** (`boolean`): Indicates if the constraint is currently active in the world. - **Property `Attachment0`** (`Attachment`): The Attachment that is connected to - **Property `Attachment1`** (`Attachment`): The Attachment that is connected to - **Property `Color`** (`BrickColor`): The color of the constraint. - **Property `Enabled`** (`boolean`): Toggles whether or not the constraint is enabled. - **Property `Visible`** (`boolean`): Toggles the constraint's visibility. - **Method `GetDebugAppliedForce(bodyId: int): Vector3`**: *(deprecated)* - **Method `GetDebugAppliedTorque(bodyId: int): Vector3`**: *(deprecated)* ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ProceduralModel last_updated: 2026-06-29T19:34:08Z inherits: - Model - PVInstance - Instance - Object type: class memory_category: BaseParts summary: "Procedural models support edit-time procedural generation. Instead of manually constructing model content, a procedural model generates its contents automatically in response to parameter changes." --- # Class: ProceduralModel > Procedural models support edit-time procedural generation. Instead of manually > constructing model content, a procedural model generates its contents > automatically in response to parameter changes. ## Description `ProceduralModel` inherits from [Model](/docs/reference/engine/classes/Model.md) and supports edit-time procedural generation of its contents. Instead of manually constructing model content, a procedural model generates its contents automatically in response to parameter changes. Generation is defined by a **generator module**, a [ModuleScript](/docs/reference/engine/classes/ModuleScript.md) referenced by the [Generator](/docs/reference/engine/classes/ProceduralModel.md) property. The engine invokes the module's `OnGenerate` function to produce the model's contents. A procedural model regenerates when any of the following inputs change: - The [Size](/docs/reference/engine/classes/ProceduralModel.md) property of the procedural model, which defines a bounding box for the model to generate within. - Any attributes on the procedural mode, as defined by the generator module. - The generator's module sandboxing configuration (`Sandboxed` and `Capabilities`). - The source code of the generator module. When any of these inputs change, the engine schedules regeneration. This scheduled regeneration is performed by calling `OnGenerate` and applying its results once it returns. In most cases, generation happens within the same frame, but can be deferred to maintain performance. ## Properties ### Property: ProceduralModel.GenerationError ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Basic" ] } ``` If the generator module encounters an error during generation, the contents of the `ProceduralModel` are replaced with an error state placeholder. The error is stored in the `GenerationError` property to give you visibility into why the generation failed. ### Property: ProceduralModel.Generator ```json { "type": "ModuleScript", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Basic" ] } ``` A reference to a [ModuleScript](/docs/reference/engine/classes/ModuleScript.md) containing code that defines how the `ProceduralModel` generates its contents in response to parameter changes. Setting `Generator` triggers generation using the new generator module. The `Attributes` table exported by the module is automatically applied as default attribute values on the `ProceduralModel`. Clearing `Generator` preserves the current contents of the `ProceduralModel`, and prevents further generation until a new generator is assigned. Guidelines for writing the generator module's `OnGenerate` function: - Only write results into the provided `targetContainer`. `OnGenerate` should not modify the [DataModel](/docs/reference/engine/classes/DataModel.md) directly; writes elsewhere may not interact correctly with engine systems. - `OnGenerate` is allowed to yield and call yielding methods (for example, [GeometryService](/docs/reference/engine/classes/GeometryService.md) CSG APIs). Generation is considered complete whenever `OnGenerate` returns. - Calling `parameters:Pause()` inside long-running loops lets the engine break up work. It only yields when needed to avoid dropping frames, so calls are low-cost when the frame budget is not exceeded. **Default Generator Module** The default generator module that Studio inserts when a new [ProceduralModel](/docs/reference/engine/classes/ProceduralModel.md) is created. It illustrates the required shape of a generator module: an `Attributes` table of default attribute values, and an `OnGenerate` function that populates the `targetContainer` based on the passed `parameters`. Returning the module makes it assignable to [ProceduralModel.Generator](/docs/reference/engine/classes/ProceduralModel.md). ```lua -- A generator module is a ModuleScript that defines how a ProceduralModel builds its contents. -- See https://create.roblox.com/docs/reference/engine/classes/ProceduralModel for details. -- -- To be used as a generator, the ModuleScript must return a table with two fields: -- * `Attributes` - a table of default values. Each key becomes an attribute on every -- ProceduralModel that uses this generator. Users can then tweak these -- attributes in the Properties panel to re-run generation with new inputs. -- * `OnGenerate` - a function the engine calls to (re)build the model's contents. -- -- Nothing about this script or its placement is special. It can live anywhere in the DataModel -- and the `Generator` property on a ProceduralModel can be reassigned to point at it. -- The type annotations below are optional but documented here so the required shape is explicit. -- `GenerationFunctionParams` is passed to `OnGenerate` every time generation runs. It exposes: -- * `Attributes` - the current attribute values on the ProceduralModel. -- * `Size` - the bounding volume to generate within (from `ProceduralModel.Size`). -- * `Pause` - a cooperative yield. Call `parameters:Pause()` inside long loops so large -- generations don't stall the calling thread; the engine may resume you later. type GenerationFunctionParams = { Attributes: Attributes, Size: Vector3, Pause: (self: GenerationFunctionParams) -> (), } -- `GeneratorModuleDefinition` is the table the ModuleScript must return. -- `OnGenerate` must not return a value; parent generated instances into `targetContainer` instead. -- Anything parented to `targetContainer` (or its descendants) becomes the output of generation. type GeneratorModuleDefinition = { Attributes: Attributes, OnGenerate: ( parameters: GenerationFunctionParams, targetContainer: GeneratedFolder ) -> (), } -- Default attributes. Each entry here shows up as an editable attribute on the ProceduralModel. -- A fixed `RandomSeed` keeps generation deterministic: the same inputs always produce the same -- output, which is important because generation may run many times as the user edits properties. local defaultAttributes = { BorderThickness = 1, RandomSeed = 142376088, } local Generator: GeneratorModuleDefinition = { Attributes = defaultAttributes, OnGenerate = function(parameters, targetContainer) -- Inputs. `parameters.Attributes` reflects whatever the user has set on the -- ProceduralModel; `parameters.Size` is the bounding box to build into. local borderThickness = parameters.Attributes.BorderThickness local random = Random.new(parameters.Attributes.RandomSeed) local size = parameters.Size -- Derived values. Seeded `random` above makes these reproducible per RandomSeed. local minSize = math.min(size.X, size.Y, size.Z) local baseColor = Color3.fromHSV(random:NextNumber(0.5, 0.8), 0.7, 0.8) local outlineColor = Color3.fromHSV(random:NextNumber(), 0.4, 0.25) -- Small helpers to make the construction below read top-to-bottom. You don't need -- helpers like these to write a generator; they just cut down on repetition. local function assignProperties(instance: Instance, properties: { [string]: unknown }) -- Set Parent last so other properties are in place before the instance enters -- the DataModel and fires Changed events. for name, value in properties do if name ~= "Parent" then (instance :: any)[name] = value end end if properties.Parent ~= nil then instance.Parent = properties.Parent :: Instance end return instance end local function createInstance(className: string, properties: { [string]: unknown }) return assignProperties(Instance.new(className), properties) end local function createPartWithDefaults(properties: { [string]: unknown }) -- Generated parts are typically anchored and non-colliding; override per-part as needed. local part = createInstance("Part", { Anchored = true, CanCollide = false, Color = baseColor, Material = Enum.Material.SmoothPlastic, BottomSurface = Enum.SurfaceType.Smooth, TopSurface = Enum.SurfaceType.Smooth, }) return assignProperties(part, properties) end -- Everything below parents into `targetContainer`, which is how instances become -- part of the generated output. Instances not parented here are discarded. -- Invisible bounding part used as the billboard adornee. local bounds = createPartWithDefaults({ Name = "Bounds", Parent = targetContainer, Transparency = 1, Size = size, }) -- A recessed platform with a border outline underneath it. local base = createPartWithDefaults({ Name = "PlatformBase", Parent = targetContainer, Color = baseColor, CFrame = CFrame.new(0, (-size.Y + 1) / 2, 0), Size = Vector3.new( size.X - 2 * borderThickness, 1.01, size.Z - 2 * borderThickness ), }) local outline = createPartWithDefaults({ Name = "PlatformOutline", Parent = targetContainer, Color = outlineColor, Position = Vector3.yAxis * (-size.Y + 1) / 2, Size = Vector3.new(size.X, 1, size.Z), }) -- "Hello World!" billboard. Generators can create any instance type, not just parts. local billboard = createInstance("BillboardGui", { Name = "Text", Parent = targetContainer, Adornee = bounds, LightInfluence = 0, Size = UDim2.fromScale(minSize, minSize), }) local textLabel = createInstance("TextLabel", { Name = "HelloWorld", Parent = billboard, AnchorPoint = Vector2.new(0.5, 0.5), BackgroundTransparency = 1, Position = UDim2.fromScale(0.5, 0.5), Size = UDim2.fromScale(1, 1), Text = "Hello World!", TextColor3 = baseColor, TextScaled = true, TextStrokeTransparency = 0, }) local textStroke = createInstance("UIStroke", { Parent = textLabel, Color = outlineColor, Thickness = borderThickness * 3, }) end, } -- Returning the module definition is what makes this ModuleScript usable as a `Generator`. return Generator ``` **Classic Ladder Generator Module** A minimal generator module that builds a ladder from rungs and two side posts, sized to fit the [Size](/docs/reference/engine/classes/ProceduralModel.md) of the ProceduralModel that references it. Demonstrates the required shape of a generator (an `Attributes` table plus an `OnGenerate` function) and shows how to use `parameters:Pause()` to cooperatively yield during large generations. ```lua -- A generator module is a ModuleScript that returns a table with `Attributes` and `OnGenerate`. -- Assigning this ModuleScript to a ProceduralModel's `Generator` property runs `OnGenerate` -- whenever the model's Size, attributes, or the module source changes. -- See https://create.roblox.com/docs/reference/engine/classes/ProceduralModel. local ClassicRobloxLadderGenerator = {} -- Default attribute values. Each key becomes an editable attribute on any ProceduralModel that -- uses this generator, and shows up in the Studio Properties panel under Attributes. Editing any -- of them re-runs `OnGenerate` with the new values. ClassicRobloxLadderGenerator.Attributes = { RungSpacing = 2, RungThickness = 1, PostSize = 1, Color = Color3.new(0.5, 0.3, 0), } -- `OnGenerate` is called by the engine to build the model's contents. -- Contract: -- * `parameters.Size` - the bounding box (from `ProceduralModel.Size`) to build within. -- * `parameters.Attributes` - current attribute values on the ProceduralModel. -- * `parameters:Pause()` - cooperative yield; call inside long loops so large generations -- don't stall the thread. -- * `targetContainer` - anything parented here (or to its descendants) becomes the output. -- * Do not return a value from `OnGenerate`; outputs are communicated via `targetContainer`. ClassicRobloxLadderGenerator.OnGenerate = function(parameters, targetContainer) local size = parameters.Size local rungThickness = parameters.Attributes.RungThickness local postSize = parameters.Attributes.PostSize local count = (size.Y - rungThickness) // parameters.Attributes.RungSpacing + 1 local function createPart(position, size) local part = Instance.new("Part") part.Size = size part.Position = position part.Anchored = true part.Color = parameters.Attributes.Color part.TopSurface = Enum.SurfaceType.Smooth part.BottomSurface = Enum.SurfaceType.Smooth -- Parenting into `targetContainer` is what adds the part to the generated output. part.Parent = targetContainer end -- Rungs, stacked from the bottom of the bounding box upward. local position = Vector3.new(0, -size.Y / 2 + rungThickness / 2, 0) for i = 1, count do -- Yield between rungs so a ladder with many rungs doesn't block the thread. parameters:Pause() createPart(position, Vector3.new(size.X - 2 * postSize, rungThickness, size.Z)) position += Vector3.new(0, parameters.Attributes.RungSpacing, 0) end -- Left and right side posts spanning the full height. createPart(Vector3.new(-0.5 * size.X + postSize / 2, 0, 0), Vector3.new(postSize, size.Y, size.Z)) createPart(Vector3.new(0.5 * size.X - postSize / 2, 0, 0), Vector3.new(postSize, size.Y, size.Z)) end -- Returning the module definition is what makes this ModuleScript usable as a `Generator`. return ClassicRobloxLadderGenerator ``` ### Property: ProceduralModel.Size ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Transform", "capabilities": [ "Basic" ] } ``` The `Size` of a `ProceduralModel` defines the bounding volume used for generation. In a standard model, size is a derived output value based on its contents and can be queried using [GetBoundingBox()](/docs/reference/engine/classes/Model.md). A procedural model inverts this relationship: `Size` is an **input** that determines how generation occurs, and is explicitly set as a property. The `Size` property represents the **physical** dimensions of the `ProceduralModel` and is affected by [ScaleTo](/docs/reference/engine/classes/Model.md). The `OnGenerate` function in a generator module doesn't need to account for position or scaling: it produces a result of the specified `Size` around the origin at a scale of 1, and the procedural model system automatically uses [PivotTo()](/docs/reference/engine/classes/PVInstance.md) and [ScaleTo()](/docs/reference/engine/classes/Model.md) to place and scale the output in the `ProceduralModel`. ## Methods ### Method: ProceduralModel:ForceGeneration **Signature:** `ProceduralModel:ForceGeneration(): boolean` *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Returns:** `boolean` ### Method: ProceduralModel:WaitForGenerationAsync **Signature:** `ProceduralModel:WaitForGenerationAsync(): boolean` Generation is scheduled to run as soon as possible after a parameter changes, but not immediately. Use `WaitForGenerationAsync()` to wait for generation to complete before parameters are changed. This method errors if called on a [ProceduralModel](/docs/reference/engine/classes/ProceduralModel.md) that is not in the [DataModel](/docs/reference/engine/classes/DataModel.md) because generation isn't performed for instances without a parent. This method returns `false` if the [ProceduralModel](/docs/reference/engine/classes/ProceduralModel.md) is removed from the [DataModel](/docs/reference/engine/classes/DataModel.md) while generation is in progress, since this cancels the operation. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Returns:** `boolean` — Was the generation successful? **Failed generation** This code snippet simulates a failed generation. ```lua local proceduralModel = ... proceduralModel:SetAttribute("ThrowAnError", true) --> Deliberately fail print(proceduralModel:WaitForGenerationAsync()) --> false ``` ## Inherited Members ### From [Model](/docs/reference/engine/classes/Model.md) - **Property `LevelOfDetail`** (`ModelLevelOfDetail`): Sets the level of detail on the model for experiences with instance - **Property `ModelStreamingMode`** (`ModelStreamingMode`): Controls the model streaming behavior on Models when - **Property `PrimaryPart`** (`BasePart`): The primary part of the Model, or `nil` if not explicitly set. - **Property `Scale`** (`float`): Editor-only property used to scale the model around its pivot. Setting - **Property `WorldPivot`** (`CFrame`): Determines where the pivot of a Model which does **not** have a - **Method `AddPersistentPlayer(playerInstance?: Player): ()`**: Sets this model to be persistent for the specified player. - **Method `BreakJoints(): ()`**: Breaks connections between `BaseParts`, including surface connections with *(deprecated)* - **Method `breakJoints(): ()`**: *(deprecated)* - **Method `GetBoundingBox(): Tuple`**: Returns a description of a volume that contains all parts of a Model. - **Method `GetExtentsSize(): Vector3`**: Returns the size of the smallest bounding box that contains all of the - **Method `GetModelCFrame(): CFrame`**: This value historically returned the CFrame of a central position in the *(deprecated)* - **Method `GetModelSize(): Vector3`**: Returns the Vector3 size of the Model. *(deprecated)* - **Method `GetPersistentPlayers(): List`**: Returns all the Player objects that this model object is - **Method `GetPrimaryPartCFrame(): CFrame`**: Returns the CFrame of the model's Model.PrimaryPart. *(deprecated)* - **Method `GetScale(): float`**: Returns the canonical scale of the model, which defaults to 1 for newly - **Method `MakeJoints(): ()`**: Goes through all BaseParts in the Model. If any *(deprecated)* - **Method `makeJoints(): ()`**: *(deprecated)* - **Method `move(location: Vector3): ()`**: *(deprecated)* - **Method `MoveTo(position: Vector3): ()`**: Moves the PrimaryPart to the given position. If - **Method `moveTo(location: Vector3): ()`**: *(deprecated)* - **Method `RemovePersistentPlayer(playerInstance?: Player): ()`**: Makes this model no longer persistent for the specified player. - **Method `ResetOrientationToIdentity(): ()`**: Resets the rotation of the model's parts to the previously set identity *(deprecated)* - **Method `ScaleTo(newScaleFactor: float): ()`**: Sets the scale factor of the model, adjusting the sizing and location of - **Method `SetIdentityOrientation(): ()`**: Sets the identity rotation of the given model, allowing you to reset the *(deprecated)* - **Method `SetPrimaryPartCFrame(cframe: CFrame): ()`**: Sets the BasePart.CFrame of the model's Model.PrimaryPart. *(deprecated)* - **Method `TranslateBy(delta: Vector3): ()`**: Shifts a Model by the given Vector3 offset, preserving ### From [PVInstance](/docs/reference/engine/classes/PVInstance.md) - **Property `Origin`** (`CFrame`): - **Property `Pivot Offset`** (`CFrame`): - **Method `GetPivot(): CFrame`**: Gets the pivot of a PVInstance. - **Method `PivotTo(targetCFrame: CFrame): ()`**: Transforms the PVInstance along with all of its descendant ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ProcessInstancePhysicsService last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service --- # Class: ProcessInstancePhysicsService ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ProximityPrompt last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances summary: "An object that lets you prompt players to interact with an object in the 3D world." --- # Class: ProximityPrompt > An object that lets you prompt players to interact with an object in the 3D > world. ## Description The **ProximityPrompt** instance lets you prompt players to interact with an object in the 3D world, such as opening a door or picking up an item. A [ProximityPrompt](/docs/reference/engine/classes/ProximityPrompt.md) object works when parented to a [BasePart](/docs/reference/engine/classes/BasePart.md), [Attachment](/docs/reference/engine/classes/Attachment.md), or [Model](/docs/reference/engine/classes/Model.md) (with [PrimaryPart](/docs/reference/engine/classes/Model.md) set) in the workspace. When the player's character approaches, a UI appears to prompt them for input. Prompts consist of three primary elements, each of which can be controlled by the noted properties. The default UI can be swapped out for your own custom appearance as outlined in [Style](/docs/reference/engine/classes/ProximityPrompt.md). | Property | Description | Default | | --- | --- | --- | | [ObjectText](/docs/reference/engine/classes/ProximityPrompt.md) | An optional name for the object being interacted with. | | | [ActionText](/docs/reference/engine/classes/ProximityPrompt.md) | An optional action name shown to the player. | Interact | | [KeyboardKeyCode](/docs/reference/engine/classes/ProximityPrompt.md) | The keyboard key which will trigger the prompt. | E | | [GamepadKeyCode](/docs/reference/engine/classes/ProximityPrompt.md) | The gamepad button which will trigger the prompt. | ButtonX | You can connect to proximity prompt events either on the [ProximityPrompt](/docs/reference/engine/classes/ProximityPrompt.md) object itself or globally through [ProximityPromptService](/docs/reference/engine/classes/ProximityPromptService.md). The [ProximityPromptService](/docs/reference/engine/classes/ProximityPromptService.md) allows you to manage all proximity prompt behavior from one location, preventing any need for duplicate code in your experience. For more information regarding proximity prompts, see the [Proximity Prompts](/docs/en-us/ui/proximity-prompts.md) guide. ## Code Samples **Using a Proximity Prompt with a Seat** In the example below, a user must interact with a chair to sit in it. Paste this into a `Script` that is a child of a `ProximityPrompt`, which is itself a sibling of a `Seat` object named _Seat_. ```lua local proximityPrompt = script.Parent local seat = proximityPrompt.Parent.Seat seat:GetPropertyChangedSignal("Occupant"):Connect(function() if seat.Occupant then proximityPrompt.Enabled = false else proximityPrompt.Enabled = true end end) proximityPrompt.Triggered:Connect(function(player) seat:Sit(player.Character.Humanoid) end) ``` **Generating a Custom Proximity Prompt** The example below generates a custom `ProximityPrompt` UI similar to the default style, which you may use as a starting point. It should be used in a `LocalScript`. ```lua local UserInputService = game:GetService("UserInputService") local ProximityPromptService = game:GetService("ProximityPromptService") local TweenService = game:GetService("TweenService") local RunService = game:GetService("RunService") local Players = game:GetService("Players") local LocalPlayer = Players.LocalPlayer while LocalPlayer == nil do Players.ChildAdded:wait() LocalPlayer = Players.LocalPlayer end local PlayerGui = LocalPlayer:WaitForChild("PlayerGui") local KeyboardButtonImage = { [Enum.KeyCode.Backspace] = "rbxasset://textures/ui/Controls/backspace.png", [Enum.KeyCode.Return] = "rbxasset://textures/ui/Controls/return.png", [Enum.KeyCode.LeftShift] = "rbxasset://textures/ui/Controls/shift.png", [Enum.KeyCode.RightShift] = "rbxasset://textures/ui/Controls/shift.png", [Enum.KeyCode.Tab] = "rbxasset://textures/ui/Controls/tab.png", } local KeyboardButtonIconMapping = { ["'"] = "rbxasset://textures/ui/Controls/apostrophe.png", [","] = "rbxasset://textures/ui/Controls/comma.png", ["`"] = "rbxasset://textures/ui/Controls/graveaccent.png", ["."] = "rbxasset://textures/ui/Controls/period.png", [" "] = "rbxasset://textures/ui/Controls/spacebar.png", } local KeyCodeToTextMapping = { [Enum.KeyCode.LeftControl] = "Ctrl", [Enum.KeyCode.RightControl] = "Ctrl", [Enum.KeyCode.LeftAlt] = "Alt", [Enum.KeyCode.RightAlt] = "Alt", [Enum.KeyCode.F1] = "F1", [Enum.KeyCode.F2] = "F2", [Enum.KeyCode.F3] = "F3", [Enum.KeyCode.F4] = "F4", [Enum.KeyCode.F5] = "F5", [Enum.KeyCode.F6] = "F6", [Enum.KeyCode.F7] = "F7", [Enum.KeyCode.F8] = "F8", [Enum.KeyCode.F9] = "F9", [Enum.KeyCode.F10] = "F10", [Enum.KeyCode.F11] = "F11", [Enum.KeyCode.F12] = "F12", } KeyCodeToTextMapping[Enum.KeyCode.PageUp] = "PgUp" KeyCodeToTextMapping[Enum.KeyCode.PageDown] = "PgDn" KeyCodeToTextMapping[Enum.KeyCode.Home] = "Home" KeyCodeToTextMapping[Enum.KeyCode.End] = "End" KeyCodeToTextMapping[Enum.KeyCode.Insert] = "Ins" KeyCodeToTextMapping[Enum.KeyCode.Delete] = "Del" local KeyCodeToFontSize = { [Enum.KeyCode.LeftControl] = 12, [Enum.KeyCode.RightControl] = 12, [Enum.KeyCode.LeftAlt] = 12, [Enum.KeyCode.RightAlt] = 12, [Enum.KeyCode.F10] = 12, [Enum.KeyCode.F11] = 12, [Enum.KeyCode.F12] = 12, [Enum.KeyCode.PageUp] = 8, [Enum.KeyCode.PageDown] = 8, [Enum.KeyCode.Home] = 8, [Enum.KeyCode.End] = 10, [Enum.KeyCode.Insert] = 10, [Enum.KeyCode.Delete] = 10, } -- Colors local BackgroundColor = Color3.new(0.07, 0.07, 0.07) local ContentColor = Color3.new(1, 1, 1) local SecondaryContentColor = Color3.new(0.7, 0.7, 0.7) -- TweenInfo presets local tweenInfoQuick = TweenInfo.new(0.06, Enum.EasingStyle.Linear, Enum.EasingDirection.Out) local tweenInfoOutHalfSecond = TweenInfo.new(0.5, Enum.EasingStyle.Quad, Enum.EasingDirection.Out) local tweenInfoFast = TweenInfo.new(0.2, Enum.EasingStyle.Quad, Enum.EasingDirection.Out) local function getScreenGui() local screenGui = PlayerGui:FindFirstChild("ProximityPrompts") if screenGui == nil then screenGui = Instance.new("ScreenGui") screenGui.Name = "ProximityPrompts" screenGui.ResetOnSpawn = false screenGui.Parent = PlayerGui screenGui.IgnoreGuiInset = true end return screenGui end local progressGradientFuzz = 0.01 local function createProgressBarGradient(parent, leftSide, tweensForFadeOut, tweensForFadeIn) local frame = Instance.new("Frame") frame.Size = UDim2.fromScale(0.5, 1) frame.Position = UDim2.fromScale(leftSide and 0 or 0.5, 0) frame.BackgroundTransparency = 1 frame.ClipsDescendants = true frame.Visible = false frame.Parent = parent local image = Instance.new("ImageLabel") image.BackgroundTransparency = 1 image.Size = UDim2.fromScale(2, 1) image.Position = UDim2.fromScale(leftSide and 0 or -1, 0) image.Image = "rbxasset://textures/ui/Controls/RadialFill.png" image.Parent = frame local gradient = Instance.new("UIGradient") gradient.Transparency = NumberSequence.new { NumberSequenceKeypoint.new(0, 0), NumberSequenceKeypoint.new(0.5, 0), NumberSequenceKeypoint.new(0.5 + progressGradientFuzz, 1), NumberSequenceKeypoint.new(1, 1) } gradient.Rotation = leftSide and 180 or 0 gradient.Parent = image table.insert(tweensForFadeOut, TweenService:Create(image, tweenInfoQuick, { ImageTransparency = 1 })) table.insert(tweensForFadeIn, TweenService:Create(image, tweenInfoQuick, { ImageTransparency = 0 })) return gradient, frame end local function createCircularProgressBar(tweensForFadeOut, tweensForFadeIn) local bar = Instance.new("Frame") bar.Name = "CircularProgressBar" bar.Size = UDim2.fromOffset(58, 58) bar.AnchorPoint = Vector2.new(0.5, 0.5) bar.Position = UDim2.fromScale(0.5, 0.5) bar.BackgroundTransparency = 1 local leftGradient, leftFrame = createProgressBarGradient(bar, true, tweensForFadeOut, tweensForFadeIn) local rightGradient, rightFrame = createProgressBarGradient(bar, false, tweensForFadeOut, tweensForFadeIn) local progress = Instance.new("NumberValue") progress.Name = "Progress" progress.Parent = bar progress.Changed:Connect(function(value) local angle = math.clamp(value * 360, 0, 360) leftFrame.Visible = progress.Value > 0.5 leftGradient.Rotation = math.clamp(angle, 180, 360) rightFrame.Visible = progress.Value > progressGradientFuzz rightGradient.Rotation = math.clamp(angle, 0, 180) end) return bar end local function createPrompt(prompt, inputType, gui) local tweensForButtonHoldBegin = {} local tweensForButtonHoldEnd = {} local tweensForFadeOut = {} local tweensForFadeIn = {} local tweenInfoInFullDuration = TweenInfo.new(prompt.HoldDuration, Enum.EasingStyle.Linear, Enum.EasingDirection.Out) local promptUI = Instance.new("BillboardGui") promptUI.Name = "Prompt" promptUI.AlwaysOnTop = true local frame = Instance.new("Frame") frame.Size = UDim2.fromScale(0.5, 1) frame.BackgroundTransparency = 1 frame.BackgroundColor3 = BackgroundColor frame.AnchorPoint = Vector2.new(0.5, 0) frame.Position = UDim2.fromScale(0.5, 0) frame.Size = UDim2.fromScale(0, 1) frame.AutomaticSize = Enum.AutomaticSize.X frame.Parent = promptUI -- Add right padding local padding = Instance.new("UIPadding") padding.Parent = frame local roundedCorner = Instance.new("UICorner") roundedCorner.Parent = frame local inputFrame = Instance.new("Frame") inputFrame.Name = "InputFrame" inputFrame.Size = UDim2.fromScale(1, 1) inputFrame.BackgroundTransparency = 1 inputFrame.SizeConstraint = Enum.SizeConstraint.RelativeYY inputFrame.Parent = frame local resizeableInputFrame = Instance.new("Frame") resizeableInputFrame.Size = UDim2.fromScale(1, 1) resizeableInputFrame.Position = UDim2.fromScale(0.5, 0.5) resizeableInputFrame.AnchorPoint = Vector2.new(0.5, 0.5) resizeableInputFrame.BackgroundTransparency = 1 resizeableInputFrame.Parent = inputFrame local inputFrameScaler = Instance.new("UIScale") inputFrameScaler.Parent = resizeableInputFrame local inputFrameScaleFactor = inputType == Enum.ProximityPromptInputType.Touch and 1.6 or 1.33 table.insert(tweensForButtonHoldBegin, TweenService:Create(inputFrameScaler, tweenInfoFast, { Scale = inputFrameScaleFactor })) table.insert(tweensForButtonHoldEnd, TweenService:Create(inputFrameScaler, tweenInfoFast, { Scale = 1 })) local actionText = Instance.new("TextLabel") actionText.Name = "ActionText" actionText.Font = Enum.Font.GothamMedium actionText.TextSize = 19 actionText.BackgroundTransparency = 1 actionText.TextTransparency = 1 actionText.TextColor3 = ContentColor actionText.TextXAlignment = Enum.TextXAlignment.Left table.insert(tweensForButtonHoldBegin, TweenService:Create(actionText, tweenInfoFast, { TextTransparency = 1 })) table.insert(tweensForButtonHoldEnd, TweenService:Create(actionText, tweenInfoFast, { TextTransparency = 0 })) table.insert(tweensForFadeOut, TweenService:Create(actionText, tweenInfoFast, { TextTransparency = 1 })) table.insert(tweensForFadeIn, TweenService:Create(actionText, tweenInfoFast, { TextTransparency = 0 })) local objectText = Instance.new("TextLabel") objectText.Name = "ObjectText" objectText.Font = Enum.Font.GothamMedium objectText.TextSize = 14 objectText.BackgroundTransparency = 1 objectText.TextTransparency = 1 objectText.TextColor3 = SecondaryContentColor objectText.TextXAlignment = Enum.TextXAlignment.Left -- Add list layout, as well a tween out to mimic old shrinking local listLayout = Instance.new("UIListLayout") listLayout.FillDirection = Enum.FillDirection.Horizontal listLayout.Padding = UDim.new(-0.25, 0) listLayout.Parent = frame table.insert(tweensForButtonHoldBegin, TweenService:Create(listLayout, tweenInfoFast, { Padding = UDim.new(-0.25, 0) })) table.insert(tweensForButtonHoldEnd, TweenService:Create(listLayout, tweenInfoFast, { Padding = UDim.new(0, 0) })) table.insert(tweensForFadeOut, TweenService:Create(listLayout, tweenInfoFast, { Padding = UDim.new(-0.25, 0) })) table.insert(tweensForFadeIn, TweenService:Create(listLayout, tweenInfoFast, { Padding = UDim.new(0, 0) })) -- Add container for text labels local textFrame = Instance.new("Frame") textFrame.Name = "TextFrame" textFrame.Size = UDim2.fromScale(0, 1) textFrame.AutomaticSize = Enum.AutomaticSize.X textFrame.BackgroundTransparency = 1 textFrame.Parent = frame -- ActionText and ObjectText automatic sizing, parent to textFrame actionText.Size = UDim2.fromScale(0, 1) actionText.AutomaticSize = Enum.AutomaticSize.X actionText.Parent = textFrame objectText.Size = UDim2.fromScale(0, 1) objectText.AutomaticSize = Enum.AutomaticSize.X objectText.Parent = textFrame table.insert(tweensForButtonHoldBegin, TweenService:Create(objectText, tweenInfoFast, { TextTransparency = 1 })) table.insert(tweensForButtonHoldEnd, TweenService:Create(objectText, tweenInfoFast, { TextTransparency = 0 })) table.insert(tweensForFadeOut, TweenService:Create(objectText, tweenInfoFast, { TextTransparency = 1 })) table.insert(tweensForFadeIn, TweenService:Create(objectText, tweenInfoFast, { TextTransparency = 0 })) table.insert(tweensForButtonHoldBegin, TweenService:Create(frame, tweenInfoFast, { BackgroundTransparency = 1 })) table.insert(tweensForButtonHoldEnd, TweenService:Create(frame, tweenInfoFast, { BackgroundTransparency = 0.2 })) table.insert(tweensForFadeOut, TweenService:Create(frame, tweenInfoFast, { BackgroundTransparency = 1 })) table.insert(tweensForFadeIn, TweenService:Create(frame, tweenInfoFast, { BackgroundTransparency = 0.2 })) local roundFrame = Instance.new("Frame") roundFrame.Name = "RoundFrame" roundFrame.Size = UDim2.fromOffset(48, 48) roundFrame.AnchorPoint = Vector2.new(0.5, 0.5) roundFrame.Position = UDim2.fromScale(0.5, 0.5) roundFrame.BackgroundTransparency = 1 roundFrame.Parent = resizeableInputFrame local roundedFrameCorner = Instance.new("UICorner") roundedFrameCorner.CornerRadius = UDim.new(0.5, 0) roundedFrameCorner.Parent = roundFrame table.insert(tweensForFadeOut, TweenService:Create(roundFrame, tweenInfoQuick, { BackgroundTransparency = 1 })) table.insert(tweensForFadeIn, TweenService:Create(roundFrame, tweenInfoQuick, { BackgroundTransparency = 0.5 })) if inputType == Enum.ProximityPromptInputType.Gamepad then local mappedIconImage = UserInputService:GetImageForKeyCode(prompt.GamepadKeyCode) if mappedIconImage then local icon = Instance.new("ImageLabel") icon.Name = "ButtonImage" icon.AnchorPoint = Vector2.new(0.5, 0.5) icon.Size = UDim2.fromOffset(24, 24) icon.Position = UDim2.fromScale(0.5, 0.5) icon.BackgroundTransparency = 1 icon.ImageTransparency = 1 icon.Image = mappedIconImage icon.Parent = resizeableInputFrame table.insert(tweensForFadeOut, TweenService:Create(icon, tweenInfoQuick, { ImageTransparency = 1 })) table.insert(tweensForFadeIn, TweenService:Create(icon, tweenInfoQuick, { ImageTransparency = 0 })) end elseif inputType == Enum.ProximityPromptInputType.Touch then local buttonImage = Instance.new("ImageLabel") buttonImage.Name = "ButtonImage" buttonImage.BackgroundTransparency = 1 buttonImage.ImageTransparency = 1 buttonImage.Size = UDim2.fromOffset(25, 31) buttonImage.AnchorPoint = Vector2.new(0.5, 0.5) buttonImage.Position = UDim2.fromScale(0.5, 0.5) buttonImage.Image = "rbxasset://textures/ui/Controls/TouchTapIcon.png" buttonImage.Parent = resizeableInputFrame table.insert(tweensForFadeOut, TweenService:Create(buttonImage, tweenInfoQuick, { ImageTransparency = 1 })) table.insert(tweensForFadeIn, TweenService:Create(buttonImage, tweenInfoQuick, { ImageTransparency = 0 })) else local buttonImage = Instance.new("ImageLabel") buttonImage.Name = "ButtonImage" buttonImage.BackgroundTransparency = 1 buttonImage.ImageTransparency = 1 buttonImage.Size = UDim2.fromOffset(28, 30) buttonImage.AnchorPoint = Vector2.new(0.5, 0.5) buttonImage.Position = UDim2.fromScale(0.5, 0.5) buttonImage.Image = "rbxasset://textures/ui/Controls/key_single.png" buttonImage.Parent = resizeableInputFrame table.insert(tweensForFadeOut, TweenService:Create(buttonImage, tweenInfoQuick, { ImageTransparency = 1 })) table.insert(tweensForFadeIn, TweenService:Create(buttonImage, tweenInfoQuick, { ImageTransparency = 0 })) local buttonTextString = UserInputService:GetStringForKeyCode(prompt.KeyboardKeyCode) local buttonTextImage = KeyboardButtonImage[prompt.KeyboardKeyCode] if buttonTextImage == nil then buttonTextImage = KeyboardButtonIconMapping[buttonTextString] end if buttonTextImage == nil then local keyCodeMappedText = KeyCodeToTextMapping[prompt.KeyboardKeyCode] if keyCodeMappedText then buttonTextString = keyCodeMappedText end end if buttonTextImage then local icon = Instance.new("ImageLabel") icon.Name = "ButtonImage" icon.AnchorPoint = Vector2.new(0.5, 0.5) icon.Size = UDim2.fromOffset(36, 36) icon.Position = UDim2.fromScale(0.5, 0.5) icon.BackgroundTransparency = 1 icon.ImageTransparency = 1 icon.Image = buttonTextImage icon.Parent = resizeableInputFrame table.insert(tweensForFadeOut, TweenService:Create(icon, tweenInfoQuick, { ImageTransparency = 1 })) table.insert(tweensForFadeIn, TweenService:Create(icon, tweenInfoQuick, { ImageTransparency = 0 })) elseif buttonTextString ~= nil and buttonTextString ~= '' then local buttonText = Instance.new("TextLabel") buttonText.Name = "ButtonText" buttonText.AutoLocalize = false buttonText.Position = UDim2.fromOffset(0, -1) buttonText.Size = UDim2.fromScale(1, 1) buttonText.Font = Enum.Font.GothamMedium local buttonTextSize = KeyCodeToFontSize[prompt.KeyboardKeyCode] if buttonTextSize == nil then buttonTextSize = 14 end buttonText.TextSize = buttonTextSize buttonText.BackgroundTransparency = 1 buttonText.TextTransparency = 1 buttonText.TextColor3 = ContentColor buttonText.TextXAlignment = Enum.TextXAlignment.Center buttonText.Text = buttonTextString buttonText.Parent = resizeableInputFrame table.insert(tweensForFadeOut, TweenService:Create(buttonText, tweenInfoQuick, { TextTransparency = 1 })) table.insert(tweensForFadeIn, TweenService:Create(buttonText, tweenInfoQuick, { TextTransparency = 0 })) else error("ProximityPrompt '" .. prompt.Name .. "' has an unsupported keycode for rendering UI: " .. tostring(prompt.KeyboardKeyCode)) end end if inputType == Enum.ProximityPromptInputType.Touch or prompt.ClickablePrompt then local button = Instance.new("TextButton") button.BackgroundTransparency = 1 button.TextTransparency = 1 button.Size = UDim2.fromScale(1, 1) button.Parent = promptUI button.Selectable = false local buttonDown = false button.InputBegan:Connect(function(input) if (input.UserInputType == Enum.UserInputType.Touch or input.UserInputType == Enum.UserInputType.MouseButton1) and input.UserInputState ~= Enum.UserInputState.Change then prompt:InputHoldBegin() buttonDown = true end end) button.InputEnded:Connect(function(input) if input.UserInputType == Enum.UserInputType.Touch or input.UserInputType == Enum.UserInputType.MouseButton1 then if buttonDown then buttonDown = false prompt:InputHoldEnd() end end end) promptUI.Active = true end if prompt.HoldDuration > 0 then local circleBar = createCircularProgressBar(tweensForFadeOut, tweensForFadeIn) circleBar.Parent = resizeableInputFrame table.insert(tweensForButtonHoldBegin, TweenService:Create(circleBar.Progress, tweenInfoInFullDuration, { Value = 1 })) table.insert(tweensForButtonHoldEnd, TweenService:Create(circleBar.Progress, tweenInfoOutHalfSecond, { Value = 0 })) end local holdBeganConnection local holdEndedConnection local triggeredConnection local triggerEndedConnection if prompt.HoldDuration > 0 then holdBeganConnection = prompt.PromptButtonHoldBegan:Connect(function() for _, tween in ipairs(tweensForButtonHoldBegin) do tween:Play() end end) holdEndedConnection = prompt.PromptButtonHoldEnded:Connect(function() for _, tween in ipairs(tweensForButtonHoldEnd) do tween:Play() end end) end triggeredConnection = prompt.Triggered:Connect(function() for _, tween in ipairs(tweensForFadeOut) do tween:Play() end end) triggerEndedConnection = prompt.TriggerEnded:Connect(function() for _, tween in ipairs(tweensForFadeIn) do tween:Play() end end) local function updateUIFromPrompt() local promptHeight = 72 local promptWidth = 72 local textPaddingRight = 24 local actionTextYOffset = 0 if prompt.ObjectText ~= nil and prompt.ObjectText ~= '' then actionTextYOffset = 9 end actionText.Text = prompt.ActionText objectText.Text = prompt.ObjectText actionText.AutoLocalize = prompt.AutoLocalize actionText.RootLocalizationTable = prompt.RootLocalizationTable objectText.AutoLocalize = prompt.AutoLocalize objectText.RootLocalizationTable = prompt.RootLocalizationTable textPaddingRight = textPaddingRight - actionTextYOffset if (prompt.ActionText ~= nil and prompt.ActionText ~= '') or (prompt.ObjectText ~= nil and prompt.ObjectText ~= '') then padding.PaddingRight = UDim.new(0, textPaddingRight) else padding.PaddingRight = UDim.new(0, 0) end actionText.Position = UDim2.new(0, 0, 0, actionTextYOffset) objectText.Position = UDim2.new(0, 0, 0, -10) promptUI.Size = UDim2.fromOffset(promptWidth, promptHeight) promptUI.SizeOffset = Vector2.new(prompt.UIOffset.X / promptUI.Size.Width.Offset, prompt.UIOffset.Y / promptUI.Size.Height.Offset) -- BillboardGuis can't be automatically sized, so we need to calculate -- the size based on the automatically sized prompt frame. task.defer(function () -- Automatic sizing takes approximately 2 render cycles to be calculated RunService.RenderStepped:Wait() RunService.RenderStepped:Wait() promptWidth = frame.AbsoluteSize.X promptUI.Size = UDim2.fromOffset(promptWidth, promptHeight) promptUI.SizeOffset = Vector2.new(prompt.UIOffset.X / promptUI.Size.Width.Offset, prompt.UIOffset.Y / promptUI.Size.Height.Offset) end) end local changedConnection = prompt.Changed:Connect(updateUIFromPrompt) updateUIFromPrompt() promptUI.Adornee = prompt.Parent promptUI.Parent = gui local function updateUIAncestry() promptUI.Adornee = prompt.Parent end local ancestryConnection = prompt.AncestryChanged:Connect(updateUIAncestry) for _, tween in ipairs(tweensForFadeIn) do tween:Play() end local function cleanup() if holdBeganConnection then holdBeganConnection:Disconnect() end if holdEndedConnection then holdEndedConnection:Disconnect() end triggeredConnection:Disconnect() triggerEndedConnection:Disconnect() changedConnection:Disconnect() ancestryConnection:Disconnect() for _, tween in ipairs(tweensForFadeOut) do tween:Play() end wait(0.2) promptUI.Parent = nil end return cleanup end local function createIndicator(prompt, gui) local tweensForFadeOut = {} local tweensForFadeIn = {} local indicatorUI = Instance.new("BillboardGui") indicatorUI.Name = "Indicator" indicatorUI.Size = UDim2.fromOffset(15, 15) indicatorUI.AlwaysOnTop = true local frame = Instance.new("Frame") frame.Size = UDim2.fromScale(1, 1) frame.BackgroundTransparency = 1 frame.BackgroundColor3 = BackgroundColor frame.AnchorPoint = Vector2.new(0.5, 0) frame.Position = UDim2.fromScale(0.5, 0) frame.Parent = indicatorUI local corner = Instance.new("UICorner") corner.CornerRadius = UDim.new(1, 0) corner.Parent = frame local stroke = Instance.new("UIStroke") stroke.Color = ContentColor stroke.Thickness = 1.5 stroke.Transparency = 1 stroke.Parent = frame table.insert(tweensForFadeOut, TweenService:Create(frame, tweenInfoQuick, { BackgroundTransparency = 1 })) table.insert(tweensForFadeOut, TweenService:Create(stroke, tweenInfoQuick, { Transparency = 1 })) table.insert(tweensForFadeIn, TweenService:Create(frame, tweenInfoQuick, { BackgroundTransparency = 0.2 })) table.insert(tweensForFadeIn, TweenService:Create(stroke, tweenInfoQuick, { Transparency = 0.2 })) indicatorUI.Adornee = prompt.Parent indicatorUI.Parent = gui local function updateUIAncestry() indicatorUI.Adornee = prompt.Parent end local ancestryConnection = prompt.AncestryChanged:Connect(updateUIAncestry) for _, tween in ipairs(tweensForFadeIn) do tween:Play() end local function cleanup() ancestryConnection:Disconnect() for _, tween in ipairs(tweensForFadeOut) do tween:Play() end wait(0.2) indicatorUI.Parent = nil end return cleanup end local function onLoad() ProximityPromptService.PromptShown:Connect(function(prompt, inputType) if prompt.Style ~= Enum.ProximityPromptStyle.Custom then return end local gui = getScreenGui() local cleanupFunction = createPrompt(prompt, inputType, gui) -- Wait for either the prompt being hidden or destroyed local yield = Instance.new("BindableEvent") local con = prompt.PromptHidden:Connect(function() yield:Fire() end) local con2 = prompt.Destroying:Connect(function() yield:Fire() end) yield.Event:Wait() con:Disconnect() con2:Disconnect() cleanupFunction() end) ProximityPromptService.IndicatorShown:Connect(function(prompt) if prompt.Style ~= Enum.ProximityPromptStyle.Custom then return end local gui = getScreenGui() local cleanupFunction = createIndicator(prompt, gui) -- Wait for either the indicator being hidden or the prompt is destroyed local yield = Instance.new("BindableEvent") local con = prompt.IndicatorHidden:Connect(function() yield:Fire() end) local con2 = prompt.Destroying:Connect(function() yield:Fire() end) yield.Event:Wait() con:Disconnect() con2:Disconnect() cleanupFunction() end) end onLoad() ``` **Healing Proximity Prompt** The example below illustrates a prompt that requires the user to hold the button down in order to heal their character. This should be used in a `Script` that is a child of a `ProximityPrompt`. ```lua local RunService = game:GetService("RunService") local prompt = script.Parent local playersHealing = {} RunService.Stepped:Connect(function(_currentTime, deltaTime) for player, _value in pairs(playersHealing) do local humanoid = player.Character:FindFirstChildWhichIsA("Humanoid") if humanoid then humanoid.Health = humanoid.Health + 30 * deltaTime end end end) prompt.Triggered:Connect(function(player) playersHealing[player] = true end) prompt.TriggerEnded:Connect(function(player) playersHealing[player] = nil end) ``` ## Properties ### Property: ProximityPrompt.ActionText ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI", "Input" ] } ``` This property determines the action text shown to the user. ### Property: ProximityPrompt.AutoLocalize ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Localization", "capabilities": [ "UI", "Input" ] } ``` This property determines whether the prompt's [ProximityPrompt.ActionText](/docs/reference/engine/classes/ProximityPrompt.md) and [ProximityPrompt.ObjectText](/docs/reference/engine/classes/ProximityPrompt.md) will be localized according to the [ProximityPrompt.RootLocalizationTable](/docs/reference/engine/classes/ProximityPrompt.md). When set to true, localization will be applied. ### Property: ProximityPrompt.ClickablePrompt ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI", "Input" ] } ``` This property determines whether the prompt can be activated by clicking/tapping on the prompt's UI. When set to false, the prompt cannot be activated by click/tap except on mobile. ### Property: ProximityPrompt.Enabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI", "Input" ] } ``` This property indicates whether or this [ProximityPrompt](/docs/reference/engine/classes/ProximityPrompt.md) should be shown. ### Property: ProximityPrompt.Exclusivity ```json { "type": "ProximityPromptExclusivity", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI", "Input" ] } ``` This property is used to customize which prompts can be shown at the same time. ### Property: ProximityPrompt.GamepadKeyCode ```json { "type": "KeyCode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI", "Input" ] } ``` This property determines the gamepad button the player should press to trigger the [ProximityPrompt](/docs/reference/engine/classes/ProximityPrompt.md). Default is **ButtonX**. ### Property: ProximityPrompt.HoldDuration ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI", "Input" ] } ``` This property indicates the duration, in seconds, that the player must hold the button/key down to trigger the prompt. ### Property: ProximityPrompt.KeyboardKeyCode ```json { "type": "KeyCode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI", "Input" ] } ``` This property determines the key the player should press to trigger the [ProximityPrompt](/docs/reference/engine/classes/ProximityPrompt.md). Default is E. ### Property: ProximityPrompt.MaxActivationDistance ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI", "Input" ] } ``` This property determines the maximum distance a Player's [character](/docs/reference/engine/classes/Player.md) can be from the [ProximityPrompt](/docs/reference/engine/classes/ProximityPrompt.md) for the prompt to appear. ### Property: ProximityPrompt.MaxIndicatorDistance ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI", "Input" ] } ``` ### Property: ProximityPrompt.ObjectText ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI", "Input" ] } ``` This optional property determines the optional object name text shown to the user. ### Property: ProximityPrompt.RequiresLineOfSight ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI", "Input" ] } ``` This property indicates whether the prompt is hidden if the path between the player's [Camera](/docs/reference/engine/classes/Camera.md) and object parented to the [ProximityPrompt](/docs/reference/engine/classes/ProximityPrompt.md) is obstructed. If true, this prompt will only be shown if there is a clear path from the camera to the object. The parent [Part](/docs/reference/engine/classes/Part.md) or [Model](/docs/reference/engine/classes/Model.md) of the prompt will be excluded from this check. ### Property: ProximityPrompt.RootLocalizationTable ```json { "type": "LocalizationTable", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Localization", "capabilities": [ "UI", "Input" ] } ``` This property serves as a reference to the [LocalizationTable](/docs/reference/engine/classes/LocalizationTable.md) used to apply automated localization to the prompt's [ProximityPrompt.ActionText](/docs/reference/engine/classes/ProximityPrompt.md) and [ProximityPrompt.ObjectText](/docs/reference/engine/classes/ProximityPrompt.md). In order for this to apply, [ProximityPrompt.AutoLocalize](/docs/reference/engine/classes/ProximityPrompt.md) must be set. Developers can set this to reference a LocalizationTable anywhere in the [DataModel](/docs/reference/engine/classes/DataModel.md). It is not required to be a child of [LocalizationService](/docs/reference/engine/classes/LocalizationService.md). If there is no translation available in the referenced table it will look for a translation in the parent of that table, if it is also a LocalizationTable, and so on. ### Property: ProximityPrompt.Style ```json { "type": "ProximityPromptStyle", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI", "Input" ] } ``` This property indicates the prompt's style. When set to Custom, no default UI will be provided. The provided UI can be swapped out for a custom UI. In order to do this, set Style to Custom. Then, listen to the [ProximityPrompt.PromptShown](/docs/reference/engine/classes/ProximityPrompt.md) and [ProximityPrompt.PromptHidden](/docs/reference/engine/classes/ProximityPrompt.md) events in a [LocalScript](/docs/reference/engine/classes/LocalScript.md), where developers should create and tear down the UI. Developers may also use [ProximityPrompt.PromptButtonHoldBegan](/docs/reference/engine/classes/ProximityPrompt.md) and [ProximityPrompt.PromptButtonHoldEnded](/docs/reference/engine/classes/ProximityPrompt.md) in order to utilize the [ProximityPrompt.HoldDuration](/docs/reference/engine/classes/ProximityPrompt.md) progress animation feature. ### Property: ProximityPrompt.UIOffset ```json { "type": "Vector2", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI", "Input" ] } ``` This property indicates the pixel offset applied to the prompt's UI. ## Methods ### Method: ProximityPrompt:InputHoldBegin **Signature:** `ProximityPrompt:InputHoldBegin(): ()` This function triggers a signal indicating that the user began pressing the [ProximityPrompt](/docs/reference/engine/classes/ProximityPrompt.md) prompt button. It should be used by developers who wish to customize the prompt and trigger it from a prompt GUI button press. *Security: None · Thread Safety: Unsafe · Capabilities: UI, Input* **Returns:** `()` ### Method: ProximityPrompt:InputHoldEnd **Signature:** `ProximityPrompt:InputHoldEnd(): ()` A counterpoint to [ProximityPrompt:InputHoldBegin()](/docs/reference/engine/classes/ProximityPrompt.md), this signals that the user ended pressing the prompt GUI button. *Security: None · Thread Safety: Unsafe · Capabilities: UI, Input* **Returns:** `()` ## Events ### Event: ProximityPrompt.IndicatorHidden **Signature:** `ProximityPrompt.IndicatorHidden()` *Security: None · Capabilities: UI, Input* ### Event: ProximityPrompt.IndicatorShown **Signature:** `ProximityPrompt.IndicatorShown()` *Security: None · Capabilities: UI, Input* ### Event: ProximityPrompt.PromptButtonHoldBegan **Signature:** `ProximityPrompt.PromptButtonHoldBegan(playerWhoTriggered: Player)` This event triggers when a player begins holding down the [key](/docs/reference/engine/classes/ProximityPrompt.md)/button on a prompt with a non-zero [ProximityPrompt.HoldDuration](/docs/reference/engine/classes/ProximityPrompt.md). One possible usage includes to animate a hold progress bar. *Security: None · Capabilities: UI, Input* **Parameters:** | Name | Type | Description | |------|------|-------------| | `playerWhoTriggered` | `Player` | The [Player](/docs/reference/engine/classes/Player.md) who begins holding down the prompt button. | ### Event: ProximityPrompt.PromptButtonHoldEnded **Signature:** `ProximityPrompt.PromptButtonHoldEnded(playerWhoTriggered: Player)` This event triggers when the player ends holding down the button on a prompt with a non-zero [ProximityPrompt.HoldDuration](/docs/reference/engine/classes/ProximityPrompt.md). One possible usage includes to animate a hold progress bar. *Security: None · Capabilities: UI, Input* **Parameters:** | Name | Type | Description | |------|------|-------------| | `playerWhoTriggered` | `Player` | The player who ended the input hold. | ### Event: ProximityPrompt.PromptHidden **Signature:** `ProximityPrompt.PromptHidden()` This event triggers when the [prompt](/docs/reference/engine/classes/ProximityPrompt.md) becomes hidden. This event is triggered client-side for `LocalScripts`. *Security: None · Capabilities: UI, Input* ### Event: ProximityPrompt.PromptShown **Signature:** `ProximityPrompt.PromptShown(inputType: ProximityPromptInputType)` This event triggers when the [prompt](/docs/reference/engine/classes/ProximityPrompt.md) becomes visible. This event is triggered client-side for `LocalScripts`. *Security: None · Capabilities: UI, Input* **Parameters:** | Name | Type | Description | |------|------|-------------| | `inputType` | `ProximityPromptInputType` | The input that triggers the prompt. | ### Event: ProximityPrompt.Triggered **Signature:** `ProximityPrompt.Triggered(playerWhoTriggered: Player)` This event is triggered when the prompt [key](/docs/reference/engine/classes/ProximityPrompt.md)/button is pressed, or after a specified amount of time holding the button, if [ProximityPrompt.HoldDuration](/docs/reference/engine/classes/ProximityPrompt.md) is used. *Security: None · Capabilities: UI, Input* **Parameters:** | Name | Type | Description | |------|------|-------------| | `playerWhoTriggered` | `Player` | The [Player](/docs/reference/engine/classes/Player.md) who triggered the prompt. | ### Event: ProximityPrompt.TriggerEnded **Signature:** `ProximityPrompt.TriggerEnded(playerWhoTriggered: Player)` This event is triggered when the [key](/docs/reference/engine/classes/ProximityPrompt.md)/button is released, for longer events where the user is required to hold down the button (e.g. heal another player over time.) *Security: None · Capabilities: UI, Input* **Parameters:** | Name | Type | Description | |------|------|-------------| | `playerWhoTriggered` | `Player` | The [Player](/docs/reference/engine/classes/Player.md) who released the key/button, ending the trigger event. | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ProximityPromptService last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - Service - NotBrowsable summary: "Allows developers to interact with ProximityPrompt objects in a global way." --- # Class: ProximityPromptService > Allows developers to interact with [ProximityPrompt](/docs/reference/engine/classes/ProximityPrompt.md) objects in a global > way. ## Description **ProximityPromptService** allows developers to interact with [ProximityPrompt](/docs/reference/engine/classes/ProximityPrompt.md) objects in a global way. It may be more convenient to listen to events through this service rather than on individual [ProximityPrompt](/docs/reference/engine/classes/ProximityPrompt.md) objects. ## Properties ### Property: ProximityPromptService.Enabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Input" ] } ``` This property determines whether [ProximityPrompts](/docs/reference/engine/classes/ProximityPrompt.md) are enabled, and therefore shown, in-experience. When false, no prompts will be shown. For example, in a round-based system, you can disable prompts at certain points in the experience to disable proximity-based interactions: ```lua local ProximityPromptService = game:GetService("ProximityPromptService") local ReplicatedStorage = game:GetService("ReplicatedStorage") local enablePrompts = ReplicatedStorage:FindFirstChild("EnablePrompts") -- BindableEvent -- Connect to the BindableEvent and fire from another script controlling experience logic enablePrompts.OnServerEvent:Connect(function(enabled) ProximityPromptService.Enabled = enabled end) ``` ### Property: ProximityPromptService.MaxIndicatorsVisible ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Input" ] } ``` ### Property: ProximityPromptService.MaxPromptsVisible ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Input" ] } ``` This property indicates the maximum number of [ProximityPrompts](/docs/reference/engine/classes/ProximityPrompt.md) that will be shown to the player. ## Events ### Event: ProximityPromptService.IndicatorHidden **Signature:** `ProximityPromptService.IndicatorHidden(prompt: ProximityPrompt)` *Security: None · Capabilities: Input* **Parameters:** | Name | Type | Description | |------|------|-------------| | `prompt` | `ProximityPrompt` | | ### Event: ProximityPromptService.IndicatorShown **Signature:** `ProximityPromptService.IndicatorShown(prompt: ProximityPrompt)` *Security: None · Capabilities: Input* **Parameters:** | Name | Type | Description | |------|------|-------------| | `prompt` | `ProximityPrompt` | | ### Event: ProximityPromptService.PromptButtonHoldBegan **Signature:** `ProximityPromptService.PromptButtonHoldBegan(prompt: ProximityPrompt, playerWhoTriggered: Player)` This event triggers when the player begins holding down the [KeyboardKeyCode](/docs/reference/engine/classes/ProximityPrompt.md) key/button on a prompt with a non-zero [HoldDuration](/docs/reference/engine/classes/ProximityPrompt.md). *Security: None · Capabilities: Input* **Parameters:** | Name | Type | Description | |------|------|-------------| | `prompt` | `ProximityPrompt` | The prompt that the player begins interacting with. | | `playerWhoTriggered` | `Player` | The player who holds the key/button. | ### Event: ProximityPromptService.PromptButtonHoldEnded **Signature:** `ProximityPromptService.PromptButtonHoldEnded(prompt: ProximityPrompt, playerWhoTriggered: Player)` This event triggers when the player stops holding down the [KeyboardKeyCode](/docs/reference/engine/classes/ProximityPrompt.md) key/button on a prompt with a non-zero [HoldDuration](/docs/reference/engine/classes/ProximityPrompt.md). *Security: None · Capabilities: Input* **Parameters:** | Name | Type | Description | |------|------|-------------| | `prompt` | `ProximityPrompt` | The prompt that the player stops interacting with. | | `playerWhoTriggered` | `Player` | The player who releases the held key/button. | ### Event: ProximityPromptService.PromptHidden **Signature:** `ProximityPromptService.PromptHidden(prompt: ProximityPrompt)` This event triggers client-side in connected local scripts when a prompt becomes hidden. *Security: None · Capabilities: Input* **Parameters:** | Name | Type | Description | |------|------|-------------| | `prompt` | `ProximityPrompt` | The prompt instance that becomes hidden. | ### Event: ProximityPromptService.PromptShown **Signature:** `ProximityPromptService.PromptShown(prompt: ProximityPrompt, inputType: ProximityPromptInputType)` This event triggers client-side in connected local scripts when a prompt becomes visible. *Security: None · Capabilities: Input* **Parameters:** | Name | Type | Description | |------|------|-------------| | `prompt` | `ProximityPrompt` | The prompt instance that becomes visible. | | `inputType` | `ProximityPromptInputType` | The input that triggered the event. | ### Event: ProximityPromptService.PromptTriggered **Signature:** `ProximityPromptService.PromptTriggered(prompt: ProximityPrompt, playerWhoTriggered: Player)` This event triggers when the player completes interaction with a prompt, either when the [KeyboardKeyCode](/docs/reference/engine/classes/ProximityPrompt.md) key/button is pressed, or after a specified amount of time holding the key/button if the prompt's [HoldDuration](/docs/reference/engine/classes/ProximityPrompt.md) is non-zero. *Security: None · Capabilities: Input* **Parameters:** | Name | Type | Description | |------|------|-------------| | `prompt` | `ProximityPrompt` | The prompt that the player interacts with. | | `playerWhoTriggered` | `Player` | The interacting player. | ### Event: ProximityPromptService.PromptTriggerEnded **Signature:** `ProximityPromptService.PromptTriggerEnded(prompt: ProximityPrompt, playerWhoTriggered: Player)` This event triggers when the player stops holding down the [KeyboardKeyCode](/docs/reference/engine/classes/ProximityPrompt.md) key/button while triggering a prompt. This is intended to allow interactions which require the player to hold a key/button while something happens in-experience. *Security: None · Capabilities: Input* **Parameters:** | Name | Type | Description | |------|------|-------------| | `prompt` | `ProximityPrompt` | The prompt that the player stops interacting with. | | `playerWhoTriggered` | `Player` | The player that releases the key/button. | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: PublishService last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated --- # Class: PublishService ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: PyramidHandleAdornment last_updated: 2026-06-29T19:34:08Z inherits: - HandleAdornment - PVAdornment - GuiBase3d - GuiBase - Instance - Object type: class memory_category: Instances summary: "A pyramid-shaped handle that can be adorned to a BasePart." --- # Class: PyramidHandleAdornment > A pyramid-shaped handle that can be adorned to a [BasePart](/docs/reference/engine/classes/BasePart.md). ## Description `PyramidHandleAdornment` is a pyramid-shaped handle that can be adorned to a [BasePart](/docs/reference/engine/classes/BasePart.md). If parented to a player's [PlayerGui](/docs/reference/engine/classes/PlayerGui.md) or the [CoreGui](/docs/reference/engine/classes/CoreGui.md), handles can listen to input events for purposes such as making dragger tools. ## Properties ### Property: PyramidHandleAdornment.Height ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic", "UI" ] } ``` The height of the pyramid adornment in studs. ### Property: PyramidHandleAdornment.Shading ```json { "type": "AdornShading", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic", "UI" ] } ``` ### Property: PyramidHandleAdornment.Sides ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic", "UI" ] } ``` The number of sides for the pyramid adornment. Default is `4`, minimum is `3`, and maximum is `32`. ### Property: PyramidHandleAdornment.Size ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic", "UI" ] } ``` The size of the pyramid adornment's base in studs. ## Inherited Members ### From [HandleAdornment](/docs/reference/engine/classes/HandleAdornment.md) - **Property `AdornCullingMode`** (`AdornCullingMode`): Determines whether to automatically cull the adornment. - **Property `AlwaysOnTop`** (`boolean`): Forces this adornment to render on top of all 3D objects in the workspace. - **Property `CFrame`** (`CFrame`): The position and rotation of the object relative to its - **Property `SizeRelativeOffset`** (`Vector3`): The positional offset of the adornment based on the adornee's - **Property `ZIndex`** (`int`): Determines the draw order of this HandleAdornment when - **Event `MouseButton1Down`**: Fires when a player presses down on their left mouse button while hovering - **Event `MouseButton1Up`**: Fires when a player releases their left mouse button while hovering over - **Event `MouseEnter`**: Fires when a player moves their mouse over the adornment. - **Event `MouseLeave`**: Fires when a player moves their mouse out of the adornment. ### From [PVAdornment](/docs/reference/engine/classes/PVAdornment.md) - **Property `Adornee`** (`PVInstance`): The PVInstance which this PVAdornment is attached to. ### From [GuiBase3d](/docs/reference/engine/classes/GuiBase3d.md) - **Property `Color`** (`BrickColor`): Sets the color of a GUI object. *(deprecated, hidden)* - **Property `Color3`** (`Color3`): Sets the color of this GuiBase3d object. - **Property `Transparency`** (`float`): Sets the transparency of this GuiBase3d object. - **Property `Visible`** (`boolean`): Determines whether this GuiBase3d object and its descendants will ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: QWidgetPluginGui last_updated: 2026-06-29T19:34:08Z inherits: - PluginGui - LayerCollector - GuiBase2d - GuiBase - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated --- # Class: QWidgetPluginGui ## Inherited Members ### From [PluginGui](/docs/reference/engine/classes/PluginGui.md) - **Property `Title`** (`string`): The title that is displayed above the contents of the PluginGui. - **Method `BindToClose(function?: Function): ()`**: Binds a function to the PluginGui close button, overriding the - **Method `GetRelativeMousePosition(): Vector2`**: Returns the position of the mouse relative to the PluginGui. - **Event `PluginDragDropped`**: Fires when the user releases their mouse when hovering over a PluginGui - **Event `PluginDragEntered`**: Fires when the user's mouse enters a PluginGui during a drag operation - **Event `PluginDragLeft`**: Fires when the user's mouse leaves a PluginGui during a drag operation - **Event `PluginDragMoved`**: Fires when the user's mouse moves within a PluginGui during a drag - **Event `WindowFocused`**: Fires when the user begins interacting with the window of the PluginGui. - **Event `WindowFocusReleased`**: Fires when the user stops interacting with the window of the PluginGui. ### From [LayerCollector](/docs/reference/engine/classes/LayerCollector.md) - **Property `Enabled`** (`boolean`): Toggles the visibility of this LayerCollector. - **Property `ResetOnSpawn`** (`boolean`): Determines if the LayerCollector resets (deletes itself and - **Property `ZIndexBehavior`** (`ZIndexBehavior`): Controls how GuiObject.ZIndex behaves on all descendants of this - **Method `GetLayoutNodeTree(): Dictionary`**: *(deprecated)* ### From [GuiBase2d](/docs/reference/engine/classes/GuiBase2d.md) - **Property `AbsolutePosition`** (`Vector2`): Describes the actual screen position of a GuiBase2d element, in - **Property `AbsoluteRotation`** (`float`): Describes the actual screen rotation of a GuiBase2d element, in - **Property `AbsoluteSize`** (`Vector2`): Describes the actual screen size of a GuiBase2d element, in - **Property `AutoLocalize`** (`boolean`): When set to `true`, localization will be applied to this GuiBase2d - **Property `Localize`** (`boolean`): Automatically set to true when a localization table's *(deprecated, hidden)* - **Property `RootLocalizationTable`** (`LocalizationTable`): A reference to a LocalizationTable to be used to apply automated - **Property `SelectionBehaviorDown`** (`SelectionBehavior`): Customizes gamepad selection behavior in the down direction. - **Property `SelectionBehaviorLeft`** (`SelectionBehavior`): Customizes gamepad selection behavior in the left direction. - **Property `SelectionBehaviorRight`** (`SelectionBehavior`): Customizes gamepad selection behavior in the right direction. - **Property `SelectionBehaviorUp`** (`SelectionBehavior`): Customizes gamepad selection behavior in the up direction. - **Property `SelectionGroup`** (`boolean`): Allows customization of gamepad selection movement. - **Event `SelectionChanged`**: Fires when the gamepad selection moves to, leaves, or changes within the ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: RayValue last_updated: 2026-06-29T19:34:08Z inherits: - ValueBase - Instance - Object type: class memory_category: Instances summary: "A container object for a single Ray." --- # Class: RayValue > A container object for a single Ray. ## Description A RayValue is an object whose purpose is to store a single Ray. Similar to CFrameValue, a RayValue's stored ray cannot be viewed or edited within the Properties window within studio. Instead, use the Command bar to get and set the value of these objects. For example, you can use a line like the one below to create a new RayValue named "Value" within the [Workspace](/docs/reference/engine/classes/Workspace.md). It creates a ray at (0, 50, 0) and it faces in the positive-X direction. `Instance.new("RayValue").Value = Ray.new(Vector3.new(0, 50, 0), Vector3.new(10, 0, 0))` Since there is no trivial way to edit rays within Studio, sometimes it is better to use a CFrameValue instead (which can be changed through a part or the camera). You can reconstruct a ray from a CFrame using `Ray.new(cf.p, cf.lookVector * dist)`, where `cf` is a given CFrame and `dist` is the length of the Ray you want to construct. Like all "-Value" objects, this single value is stored in the Value property. The Changed event for this (and other objects like it) will fire with the new value being stored in the object, instead of a string representing the property being changed. ## Code Samples **Rays, RayValue and Raycasting** This code sample demonstrates constructing a Ray, storing the Ray within a RayValue and Raycasting to find other parts between two parts named "PartA" and "PartB". ```lua local partA = workspace.PartA local partB = workspace.PartB local origin = partA.Position local direction = partB.Position - partA.Position local ray = Ray.new(origin, direction) local rayValue = Instance.new("RayValue") rayValue.Value = ray rayValue.Name = "PartA-to-PartB Ray" rayValue.Parent = workspace -- Raycast to find any parts in between PartA and PartB local part = workspace:FindPartOnRay(ray) if part then print("Hit part: " .. part:GetFullName()) else -- This ought to never happen, as our ray starts at PartA -- and points towards PartB, so we should always hit PartB -- or some other part. print("No part hit!") end ``` ## Properties ### Property: RayValue.Value ```json { "type": "Ray", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "simulationAccess": true } ``` The stored Ray. ## Events ### Event: RayValue.Changed **Signature:** `RayValue.Changed(value: Ray)` This event fires whenever the [RayValue.Value](/docs/reference/engine/classes/RayValue.md) property is changed. It will run with the new value being stored in the argument object, instead of a string representing the property being changed. This event, like other changed events, can be used to track when an RayValue changes and to track the different values that it may change to. Equivalent changed events exist for similar objects, such as [NumberValue](/docs/reference/engine/classes/NumberValue.md) and [StringValue](/docs/reference/engine/classes/StringValue.md), depending on what object type best suits the need. *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `value` | `Ray` | The value after the change. | **How to Use RayValue.Changed** The below example, assuming all referenced objects existed, would print the RayValue's new value each time it changed. In the example below it would print _"{0, 0, 0}, {0.577350199, 0.577350199, 0.577350199}"_. ```lua local value = Instance.new("RayValue") value.Parent = workspace value.Changed:Connect(function(NewValue) print(NewValue) end) local start = Vector3.new(0, 0, 0) local lookAt = Vector3.new(10, 10, 10) local ray = Ray.new(start, (lookAt - start).Unit) value.Value = ray ``` **Expected output:** "{0, 0, 0}, {0.577350199, 0.577350199, 0.577350199}" ### Event: RayValue.changed **Signature:** `RayValue.changed(value: Ray)` > **Deprecated:** This event is a deprecated variant of [RayValue.Changed](/docs/reference/engine/classes/RayValue.md) which should be used instead. *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `value` | `Ray` | | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: RecommendationPages last_updated: 2026-06-29T19:34:08Z inherits: - Pages - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated summary: "A special version of the Pages class returned by GenerateItemListAsync." --- # Class: RecommendationPages > A special version of the [Pages](/docs/reference/engine/classes/Pages.md) class returned by > [GenerateItemListAsync](/docs/reference/engine/classes/RecommendationService.md). ## Description A special version of the [Pages](/docs/reference/engine/classes/Pages.md) class returned by [GenerateItemListAsync](/docs/reference/engine/classes/RecommendationService.md). See the following code sample for reference on the returned data format. ## Code Samples **Generate a list of recommended items** This code snippet demonstrates how to generate a list of recommended items and print their details using the RecommendationService. ```lua local RecommendationService = game:GetService("RecommendationService") -- Define the request for generating a recommendation list local request: GenerateRecommendationItemListRequest = { ConfigName = "MaximizeEngagement", LocationId = "Lobby", PageSize = 10 -- NOTE: Uncomment and set CustomContexts.UserId for Server script. -- No need to set for Local script. -- CustomContexts = { -- ["UserId"] = tostring(player.UserId), -- } } -- Call GenerateItemListAsync to get the recommendation pages local success, recommendationPages = pcall(function() return RecommendationService:GenerateItemListAsync(request) end) if success then -- Get the current page of recommended items local currentPage = recommendationPages:GetCurrentPage() for _, item in ipairs(currentPage) do print("ItemId: " .. item.ItemId) print(" ReferenceId: " .. item.ReferenceId) print(" TracingId: " .. item.TracingId) end else warn("RecommendationService error: " .. tostring(recommendationPages)) end --[[ Returned data format for each item in the page { "ItemId": string, "ReferenceId": string, "TracingId": string, "Creator": { "CreatorId": number, "CreatorType": Enum.CreatorType, }?, "Attributes": { { "AssetId": number?, "Text": string?, "Description": string?, "SeekStartTime": number?, "SeekEndTime": number?, "TrimStartTime": number?, "TrimEndTime": number? } }?, "CustomTags": { string }? } --]] ``` ## Inherited Members ### From [Pages](/docs/reference/engine/classes/Pages.md) - **Property `IsFinished`** (`boolean`): Whether or not the current page is the last page available. - **Method `AdvanceToNextPageAsync(): ()`**: Iterates to the next page in the pages object, if possible. - **Method `GetCurrentPage(): Array`**: Returns the items on the current page. The keys in the item are determined ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: RecommendationService last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service summary: "A service that provides an interface for you to manage and display personalized content recommendations." --- # Class: RecommendationService > A service that provides an interface for you to manage and display > personalized content recommendations. ## Description `RecommendationService` provides an interface for you to manage and display personalized content recommendations. It supports creating, retrieving, updating, and deleting recommendation items, as well as generating lists of recommended content for users. Additionally, it includes functionality for logging user interactions, such as views and actions, to help refine and improve recommendation quality. Once set up, you can monitor analytics in the Creator Dashboard under the **Engagement** section for an experience. ## Methods ### Method: RecommendationService:GenerateItemListAsync **Signature:** `RecommendationService:GenerateItemListAsync(generateRecommendationItemListRequest: Dictionary): RecommendationPages` This function returns a paginated list of recommended items based on a given request. The request can specify criteria such as configuration name, location, and page size to tailor the recommendations. It returns a [RecommendationPages](/docs/reference/engine/classes/RecommendationPages.md) object that can be used to iterate through the list of items. The `ConfigName` parameter determines how the recommendation engine ranks and returns items. For example, you can have configurations that optimize for views, likes, or purchases. For the ranking to be effective, you must ensure that you are logging the corresponding user interactions using [LogImpressionEvent](/docs/reference/engine/classes/RecommendationService.md) and [LogActionEvent](/docs/reference/engine/classes/RecommendationService.md). Supported `ConfigName` are: - `MaximizeTimespent`: Optimizes recommendations to prioritize content that engages users for longer periods. - `MaximizeReactions`: Optimizes recommendations to highlight content that generates the most user reactions, such as likes and favorites. - `MaximizePlays`: Optimizes recommendations to prioritize content that generates the most `Play` actions. To use this configuration effectively, you must log both impressions and `Play` actions. It's recommended to filter for "quality" plays (for example play duration greater than 60 seconds) before logging to reduce noise and improve the system's learning accuracy. - `MaximizeEngagement`: Optimizes recommendations through a balanced approach that considers multiple engagement signals, including quality views and reactions. This config is designed to highlight content that performs well across different engagement areas. - `PlayerSpecific`: Returns public items created by the player specified in the request and sorted by the most recent creation time. To use this config, you must pass the `UserId` in the `CustomContexts`. - `RecentlyAdded`: Returns items sorted by how recent they are, and displays the most recently added public content first. - `MaximizeJoins`: (Coming soon) Optimizes recommendations to prioritize game joins. This configuration is currently exclusive to Roblox Moments and focuses on maximizing interactions leading to a teleport. In contrast, `MaximizePlays` is purely optimizing `Play` actions. - `PlayerOwnedItems`: Displays the user's own creations sorted by creation time. This config requires user authentication because it displays private items. This config is only available on the **client**. This function can be called from both the **server** and the **client**. When called from the server, you must pass the `UserId` in the `CustomContexts`. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `generateRecommendationItemListRequest` | `Dictionary` | | A dictionary containing the following fields: - `ConfigName` — A unique ID for the specific configuration. This determines how the candidates are ranked. - `LocationId` — A developer-defined string that specifies the location where the recommendation is used, such as `"For_you"` or `"Lobby"`. This parameter will not affect the items returned, and it can help you track the performance of multiple recommendation features within your experience. Recommendation metrics for each individual location will be displayed in the Creator Hub. `LocationId` **must** be a string and cannot be `"Other"` or `"other"` as these values are reserved by the Creator Hub. - `PageSize` — The number of items returned for each page. - `BoostCustomTags` — A list of string tags. Any item with this tag will be boosted in ranking. Note: only supports boosting one tag now. - `CustomContexts` — A table of key-value pairs used to pass in additional context data for ranking. For example, `UserId` for a Server script. | **Returns:** `RecommendationPages` **Generate a list of recommended items** This code snippet demonstrates how to generate a list of recommended items using RecommendationService. ```lua local RecommendationService = game:GetService("RecommendationService") -- Define the request for generating a recommendation list local request: GenerateRecommendationItemListRequest = { ConfigName = "MaximizeEngagement", LocationId = "Lobby", PageSize = 10 -- NOTE: Uncomment and set CustomContexts.UserId for Server script. -- No need to set for Local script. -- CustomContexts = { -- ["UserId"] = tostring(player.UserId), -- } } -- Call GenerateItemListAsync to get the recommendation pages local success, recommendationPages = pcall(function() return RecommendationService:GenerateItemListAsync(request) end) ``` ### Method: RecommendationService:GetRecommendationItemAsync **Signature:** `RecommendationService:GetRecommendationItemAsync(itemId: string): Dictionary` This function returns a single recommendation item by its `ItemId`. This is useful for getting the details of a specific item without having to fetch a whole list. This function can be called only from the **server**. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `itemId` | `string` | | The ID of the item to retrieve. | **Returns:** `Dictionary` — A dictionary representing the recommendation item with the following fields: - `ItemId` — The unique ID for the item. - `ReferenceId` — The developer-provided ID for the item. - `TracingId` — An ID for tracking recommendation sessions. This will be empty when fetching a single item directly. - `Creator` — A table containing the `CreatorId` and `CreatorType`. - `Attributes` — A list of content attributes associated with the item. - `CustomTags` — A list of custom string tags. - `Visibility` — An enum of type [RecommendationItemVisibility](/docs/reference/engine/enums/RecommendationItemVisibility.md). **Get a single recommendation item** This code snippet demonstrates how to retrieve a single recommendation item by its ID. ```lua local RecommendationService = game:GetService("RecommendationService") local function getItem(itemId) local success, item = pcall(function() return RecommendationService:GetRecommendationItemAsync(itemId) end) if success and item then print("Successfully retrieved item: " .. item.ItemId) print(" ReferenceId: " .. item.ReferenceId) -- The TracingId will be empty when fetching a single item directly if item.Creator then print(" CreatorId: " .. tostring(item.Creator.CreatorId)) end if item.CustomTags then print(" CustomTags: ") for _, customTag in ipairs(item.CustomTags) do print(" " .. customTag) end end else warn("Failed to retrieve item: " .. itemId .. ". Error: " .. tostring(item)) end end -- Example usage (requires a valid itemId) -- getItem("some-item-id") ``` ### Method: RecommendationService:LogActionEvent **Signature:** `RecommendationService:LogActionEvent(actionType: RecommendationActionType, itemId: string, tracingId: string, actionEventDetails?: Dictionary): ()` This function logs a user action on a recommended item, such as a "like," "share," or "purchase." It requires the `itemId` of the item and a `tracingId` from the GenerateItemListAsync response to link the action to a specific recommendation context. Additional details about the action can be provided in the `actionEventDetails` dictionary. Logging actions is essential for recommendation configurations that rank items based on user engagement such as number of likes or purchases. This function can only be called from the **client**. **Note:** Only `LogActionEvent` calls in production actually log actions. Calling this function in Studio doesn't have any effect; you can call it as many times as you want when testing. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `actionType` | `RecommendationActionType` | | The enum for the type of action. | | `itemId` | `string` | | The item ID returned from registration and [GenerateItemListAsync](/docs/reference/engine/classes/RecommendationService.md). | | `tracingId` | `string` | | The tracing ID returned from the [GenerateItemListAsync](/docs/reference/engine/classes/RecommendationService.md) response. Each item has a `TracingId`. | | `actionEventDetails` | `Dictionary` | `nil` | A dictionary containing the following fields: - `Weight` — A number representing the weight of the action. Default is 1. - `DestinationPlaceId` — The ID of the place the user was sent to after the action. Used for [Play](/docs/reference/engine/enums/RecommendationActionType.md). - `CommentText` — Any text associated with the action, such as a comment. Used for [Comment](/docs/reference/engine/enums/RecommendationActionType.md). - `ReactionType` — A string describing the type of reaction, for example, "like" or "dislike". Used for [AddReaction](/docs/reference/engine/enums/RecommendationActionType.md) or [RemoveReaction](/docs/reference/engine/enums/RecommendationActionType.md). | **Returns:** `()` **Logging a user action** This code snippet demonstrates how to log a user action on a recommended item. ```lua local RecommendationService = game:GetService("RecommendationService") local function logLike(itemId, tracingId) local detail: RecommendationActionEventDetails = { ReactionType = "Like", Weight = 1.0 } RecommendationService:LogActionEvent(Enum.RecommendationActionType.AddReaction, itemId, tracingId, detail) print("Logged 'Like' action for item: " .. itemId) end -- Example usage (requires a valid itemId and tracingId from GenerateItemListAsync) -- logLike("item-id", "tracing-id") ``` ### Method: RecommendationService:LogImpressionEvent **Signature:** `RecommendationService:LogImpressionEvent(impressionType: RecommendationImpressionType, itemId: string, tracingId: string, impressionEventDetails?: Dictionary): ()` This function logs an impression event, such as a user viewing a recommended item. It requires the `itemId` and a `tracingId` to associate the impression with the recommendation context. Details like view duration and position can be passed in the `impressionEventDetails` dictionary to provide more context for the recommendation engine. Logging impressions, especially `Duration`, is critical for recommendation configurations that rank items based on view time. This function can only be called from the **client**. **Note:** Only `LogImpressionEvent` calls in production actually log impressions. Calling this function in Studio doesn't have any effect; you can call it as many times as you want when testing. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `impressionType` | `RecommendationImpressionType` | | The enum for the type of the impression. | | `itemId` | `string` | | The item ID returned from registration and [GenerateItemListAsync](/docs/reference/engine/classes/RecommendationService.md). | | `tracingId` | `string` | | The tracing ID returned from the [GenerateItemListAsync](/docs/reference/engine/classes/RecommendationService.md) response. Each item has a `TracingId`. | | `impressionEventDetails` | `Dictionary` | `nil` | A dictionary containing the following fields: - `Duration` — The duration of the impression in seconds. - `Weight` — A number representing the weight of the impression. Default is 1. - `ItemPosition` — The position of the item in the recommendation list. - `DepartureIntent` — The [RecommendationDepartureIntent](/docs/reference/engine/enums/RecommendationDepartureIntent.md) indicating the user's intent when leaving a view. For example, `Positive` if the view is considered good. | **Returns:** `()` **Log a user impression** This code snippet demonstrates how to log a user impression, such as viewing an item. ```lua local RecommendationService = game:GetService("RecommendationService") local function logView(itemId, tracingId) local detail: RecommendationImpressionEventDetails = { Duration = 5, -- User viewed for 5 seconds ItemPosition = 1, DepartureIntent = Enum.RecommendationDepartureIntent.Neutral } RecommendationService:LogImpressionEvent(Enum.RecommendationImpressionType.View, itemId, tracingId, detail) print("Logged 'View' impression for item: " .. itemId) end -- Example usage (requires a valid itemId and tracingId from GenerateItemListAsync) -- logView("some-item-id", "some-tracing-id") ``` ### Method: RecommendationService:LogPreferenceEvent **Signature:** `RecommendationService:LogPreferenceEvent(preferenceType: RecommendationPreferenceType, targetType: RecommendationPreferenceTargetType, targetId: string, tracingId: string, itemId: string): ()` This function logs a user preference signal, such as follow, unfollow, mute, or unmute, directed at another user, a universe, or a custom content tag. It requires a `preferenceType`, a `targetType`, and a `targetId` whose format depends on the target type: the user key for [User](/docs/reference/engine/enums/RecommendationPreferenceTargetType.md), the universe ID as a string for [Universe](/docs/reference/engine/enums/RecommendationPreferenceTargetType.md), or the tag string for [CustomTag](/docs/reference/engine/enums/RecommendationPreferenceTargetType.md). When the preference originates from a recommendation card served by [GenerateItemListAsync](/docs/reference/engine/classes/RecommendationService.md), pass the card's `tracingId` and `itemId` to correlate the event with the recommendation context; otherwise pass empty strings for both. Logging preferences helps the recommendation engine personalize future results across every recommendation surface, not just item feeds. This function can only be called from the **client**. **Note:** Only `LogPreferenceEvent` calls in production actually log preferences. Calling this function in Studio doesn't have any effect; you can call it as many times as you want when testing. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `preferenceType` | `RecommendationPreferenceType` | | The enum for the type of preference. | | `targetType` | `RecommendationPreferenceTargetType` | | The enum for the type of target. | | `targetId` | `string` | | The identifier of the preference target. The format depends on `targetType`. | | `tracingId` | `string` | | The tracing ID returned from the [GenerateItemListAsync](/docs/reference/engine/classes/RecommendationService.md) response. Pass an empty string if the preference originates outside a recommendation feed. | | `itemId` | `string` | | The item ID returned from the [GenerateItemListAsync](/docs/reference/engine/classes/RecommendationService.md) response. Pass an empty string if the preference originates outside a recommendation feed. | **Returns:** `()` ### Method: RecommendationService:RegisterItemAsync **Signature:** `RecommendationService:RegisterItemAsync(player: Player, registerRecommendationItemsRequest: Dictionary): Dictionary` This function registers a new item to be included in recommendations. It requires a `player` object and a `registerRecommendationItemsRequest` dictionary containing details about the item, such as its content type, reference ID, and custom tags. It returns a dictionary with the `ItemId` and the `ReferenceId` of the newly registered item. When selecting a `ContentType`, choose the type that best represents your item. Note that different content types are not ranked against each other directly. Instead, they are mixed into the final recommendation list based on a configured ratio. For example, a configuration might display one `Static` item for every ten items. If your experience only features a single type of content, ensure that all registered items share the same `ContentType`. The `ItemId` is a unique ID returned by the `RecommendationService`. All functions in the `RecommendationService` use the `ItemId` as input and output. The `ReferenceId` is a developer-provided identifier for an item. To make sure that this reference ID is unique, we recommend that you use a UUID. You can use this reference ID as a key to store rendering-specific metadata in a data store. When you register an item, you should only provide information that is relevant for ranking and recommendations. All other data needed for rendering should be stored separately in, for example, a data store. This approach decouples the recommendation logic from the rendering process, and results in a system that is more modular and easier to maintain. `Attributes` is a list of content attributes associated with the item. Each attribute in the list can contain the following fields: - `AssetId` — The ID of an asset, such as an image or video. - `Text` — A short text string, like a title. - `Description` — A longer text description for the attribute. - `SeekStartTime` / `SeekEndTime` — The start and end times for seeking within video content. - `TrimStartTime` / `TrimEndTime` — The start and end times for trimming video content. Common Error Codes: - **HTTP 403 (Forbidden)** — This error can occur when you call `RegisterItemAsync` from Studio. Even if you run it as a server script in Studio, the request is not issued from a Roblox server. To resolve this, publish your experience and run it in the Roblox client. - **HTTP 400 (Bad Request)** — This error indicates that a parameter is malformed. Common causes include a custom tag containing a comma or the `Attributes` table exceeding its size limit. If you encounter a 400 error and have a large `Attributes` table, try reducing its size. Remember to only include attributes that are relevant for ranking. This function can only be called from the **server**. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Player` | | The player who created the item. | | `registerRecommendationItemsRequest` | `Dictionary` | | A dictionary containing the following fields: - `ContentType` — The [RecommendationItemContentType](/docs/reference/engine/enums/RecommendationItemContentType.md) specifying the type of content. Type is defined in a generic way. For example, you can use `Static` for images, `Dynamic` for videos, and `Interactive` for 3D models that support interaction. - `ReferenceId` — The developer-defined string that uniquely identifies the item. - `Duration` — The duration of the content in seconds. - `Attributes` — The table of attributes for the item, such as `AssetId` or `Description`. - `CustomTags` — The list of string tags for filtering and boosting. Individual custom tags can't contain commas. - `Visibility` — The [RecommendationItemVisibility](/docs/reference/engine/enums/RecommendationItemVisibility.md) enum that controls the item's visibility, such as `Public` or `Private`. | **Returns:** `Dictionary` — A table with only two fields: `ItemId` and `ReferenceId`. **Register a new item** This code snippet demonstrates how to register a new item for recommendations. ```lua local RecommendationService = game:GetService("RecommendationService") local storage = game:GetService("ReplicatedStorage") -- Assume a RemoteFunction named 'RegisterItemRemote' exists in ReplicatedStorage local registerItemRemote = storage.RegisterItemRemote registerItemRemote.OnServerInvoke = function(player, refId) local request: RegisterRecommendationItemRequest = { ContentType = Enum.RecommendationItemContentType.Dynamic, ReferenceId = refId, Duration = 60, Visibility = Enum.RecommendationItemVisibility.Public, CustomTags = {"locale:en-us", "seasonal:summer"}, Attributes = { { AssetId = 123, Description = "test video", TrimStartTime = 0, TrimEndTime = 10.5 } } } local success, response = pcall(function() return RecommendationService:RegisterItemAsync(player, request) end) if success and response then print("Successfully registered item. ItemId: " .. response.ItemId) return response.ItemId else warn("Failed to register item. Error: " .. tostring(response)) return nil end end ``` ### Method: RecommendationService:RemoveItemAsync **Signature:** `RecommendationService:RemoveItemAsync(itemId: string): ()` This function removes an item from the recommendation system. It takes the `itemId` of the item to be deleted as a parameter. This function can be called from both the **server** and the **client**, with the following limitations: - When called from the server, it can only remove items registered under the same `universeId`. - When called from the client, it can only remove items registered by the current `player`. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `itemId` | `string` | | The itemId to remove. | **Returns:** `()` — No return value. **Remove an item** This code snippet demonstrates how to remove an item from the recommendation system. ```lua local RecommendationService = game:GetService("RecommendationService") local function removeItem(itemId) local success, err = pcall(function() return RecommendationService:RemoveItemAsync(itemId) end) if success then print("Successfully removed item: " .. itemId) else warn("Failed to remove item: " .. itemId .. ". Error: " .. tostring(err)) end end ``` ### Method: RecommendationService:UpdateItemAsync **Signature:** `RecommendationService:UpdateItemAsync(updateRecommendationItemRequest: Dictionary): ()` This function updates the attributes of an existing recommendation item. It takes an `updateRecommendationItemRequest` dictionary containing the `itemId` and the fields to be updated. Any fields not included in the request will remain unchanged. Items with their [RecommendationItemVisibility](/docs/reference/engine/enums/RecommendationItemVisibility.md) set to `Private` are not recommended to other users, but they can still be returned when a user requests their own creations by using the `ConfigName` of `PlayerOwnedItems`. In Roblox Moments, moderated items are set to `Private`. While this prevents other users from seeing them, the item creator can still see these moderated items and check their moderation status in their **My Moments** tab. This function can only be called from the **server**. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `updateRecommendationItemRequest` | `Dictionary` | | A dictionary containing the following fields: - `ItemId` — The ID of the item to update. - `ReferenceId` — The new developer-defined string to identify the item. - `Creator` — The new creator for the item. Creator is a table containing two fields: `CreatorId: number` and `CreatorType: Enum.CreatorType`. - `Duration` — The new duration for the content in seconds. - `Visibility` — The new visibility setting for the item. - `Attributes` — The new table of attributes for the item. - `CustomTags` — The new list of string tags. Individual custom tags can't contain commas. | **Returns:** `()` — No return value. **Update an item** This code snippet demonstrates how to update an existing recommendation item. ```lua local RecommendationService = game:GetService("RecommendationService") local function updateItem(itemId) -- Only include the fields you want to update local request: UpdateRecommendationItemRequest = { ItemId = itemId, Visibility = Enum.RecommendationItemVisibility.Private, -- NOTE: the whole CustomTags list will be replaced with this new list CustomTags = {"updated-tag"} } local success, err = pcall(function() return RecommendationService:UpdateItemAsync(request) end) if success then print("Successfully updated item: " .. itemId) else warn("Failed to update item: " .. itemId .. ". Error: " .. tostring(err)) end end -- Example usage -- updateItem("some-item-id-to-update") ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ReflectionMetadata last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances summary: "A currently unused object whose functionality was split into ReflectionMetadataClasses and ReflectionMetadataEnums." --- # Class: ReflectionMetadata > A currently unused object whose functionality was split into > [ReflectionMetadataClasses](/docs/reference/engine/classes/ReflectionMetadataClasses.md) and [ReflectionMetadataEnums](/docs/reference/engine/classes/ReflectionMetadataEnums.md). ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ReflectionMetadataCallbacks last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances summary: "Acts as a Folder containing information about callbacks for a ReflectionMetadataClass, and should be parented to it. ReflectionMetadataMembers that are parented to this object will be perceived as callbacks under the class this is parented to." --- # Class: ReflectionMetadataCallbacks > Acts as a [Folder](/docs/reference/engine/classes/Folder.md) containing information about callbacks for a > [ReflectionMetadataClass](/docs/reference/engine/classes/ReflectionMetadataClass.md), and should be parented to it. > [ReflectionMetadataMembers](/docs/reference/engine/classes/ReflectionMetadataMember.md) that are parented > to this object will be perceived as callbacks under the class this is parented > to. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ReflectionMetadataClass last_updated: 2026-06-29T19:34:08Z inherits: - ReflectionMetadataItem - Instance - Object type: class memory_category: Instances summary: "Registers information about a class, and its members. Should be parented to ReflectionMetadataClasses." --- # Class: ReflectionMetadataClass > Registers information about a class, and its members. Should be parented to > [ReflectionMetadataClasses](/docs/reference/engine/classes/ReflectionMetadataClasses.md). ## Description Registers information about a class, and its members. Should be parented to [ReflectionMetadataClasses](/docs/reference/engine/classes/ReflectionMetadataClasses.md) ## Properties ### Property: ReflectionMetadataClass.ExplorerImageIndex ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Reflection" } ``` A number index, which refers to a specific class icon. ### Property: ReflectionMetadataClass.ExplorerOrder ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Reflection" } ``` Determines how this class is sorted in the Explorer compared to other classes. ### Property: ReflectionMetadataClass.Insertable ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Reflection" } ``` Toggles whether or not this object can be inserted through the Advanced Objects menu. ### Property: ReflectionMetadataClass.PreferredParent ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Reflection" } ``` Sets the class that this class would prefer parenting to. ## Inherited Members ### From [ReflectionMetadataItem](/docs/reference/engine/classes/ReflectionMetadataItem.md) - **Property `Browsable`** (`boolean`): Whether or not this can be seen in studio. - **Property `ClassCategory`** (`string`): Describes the category of this class. - **Property `ClientOnly`** (`boolean`): - **Property `Constraint`** (`string`): Describes a constraint for a single-argument function whose argument type - **Property `Deprecated`** (`boolean`): Whether or not this item is deprecated. - **Property `EditingDisabled`** (`boolean`): Toggles whether this property can be edited from the Properties window. - **Property `EditorType`** (`string`): - **Property `FFlag`** (`string`): - **Property `IsBackend`** (`boolean`): Vague value for showing if this depends on backend stuff. - **Property `PropertyOrder`** (`int`): - **Property `ScriptContext`** (`string`): Describes the context where this member can be used. If set to "Server", - **Property `ServerOnly`** (`boolean`): - **Property `SliderScaling`** (`string`): - **Property `UIMaximum`** (`double`): The maximum value of this property. Used with - **Property `UIMinimum`** (`double`): The minimum value of this property. Used with - **Property `UINumTicks`** (`double`): The number of potential values the property's slider bar can be set to, ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ReflectionMetadataClasses last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances summary: "An internal object which cannot be used by developers." --- # Class: ReflectionMetadataClasses > An internal object which cannot be used by developers. ## Description Acts as a [Folder](/docs/reference/engine/classes/Folder.md) for [ReflectionMetadataClass](/docs/reference/engine/classes/ReflectionMetadataClass.md) objects. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ReflectionMetadataEnum last_updated: 2026-06-29T19:34:08Z inherits: - ReflectionMetadataItem - Instance - Object type: class memory_category: Instances summary: "Registers information about an Enum and its EnumItems." --- # Class: ReflectionMetadataEnum > Registers information about an [Enum](/docs/reference/engine/datatypes/Enum.md) and its EnumItems. ## Description The ReflectionMetadataEnum is the class that registers information about an Enum, and its EnumItems. It should be parented to [ReflectionMetadataEnums](/docs/reference/engine/classes/ReflectionMetadataEnums.md). Since it is an internal object, it cannot be used by developers. ## Inherited Members ### From [ReflectionMetadataItem](/docs/reference/engine/classes/ReflectionMetadataItem.md) - **Property `Browsable`** (`boolean`): Whether or not this can be seen in studio. - **Property `ClassCategory`** (`string`): Describes the category of this class. - **Property `ClientOnly`** (`boolean`): - **Property `Constraint`** (`string`): Describes a constraint for a single-argument function whose argument type - **Property `Deprecated`** (`boolean`): Whether or not this item is deprecated. - **Property `EditingDisabled`** (`boolean`): Toggles whether this property can be edited from the Properties window. - **Property `EditorType`** (`string`): - **Property `FFlag`** (`string`): - **Property `IsBackend`** (`boolean`): Vague value for showing if this depends on backend stuff. - **Property `PropertyOrder`** (`int`): - **Property `ScriptContext`** (`string`): Describes the context where this member can be used. If set to "Server", - **Property `ServerOnly`** (`boolean`): - **Property `SliderScaling`** (`string`): - **Property `UIMaximum`** (`double`): The maximum value of this property. Used with - **Property `UIMinimum`** (`double`): The minimum value of this property. Used with - **Property `UINumTicks`** (`double`): The number of potential values the property's slider bar can be set to, ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ReflectionMetadataEnumItem last_updated: 2026-06-29T19:34:08Z inherits: - ReflectionMetadataItem - Instance - Object type: class memory_category: Instances summary: "Contains information about a specific EnumItem embedded within an Enum." --- # Class: ReflectionMetadataEnumItem > Contains information about a specific EnumItem embedded within an Enum. ## Description The ReflectionMetadataEnumItem is the class containing information about a specific EnumItem embedded within an Enum and should be parented to a [ReflectionMetadataEnum](/docs/reference/engine/classes/ReflectionMetadataEnum.md). Since it is an internal object, it cannot be used by developers. ## Inherited Members ### From [ReflectionMetadataItem](/docs/reference/engine/classes/ReflectionMetadataItem.md) - **Property `Browsable`** (`boolean`): Whether or not this can be seen in studio. - **Property `ClassCategory`** (`string`): Describes the category of this class. - **Property `ClientOnly`** (`boolean`): - **Property `Constraint`** (`string`): Describes a constraint for a single-argument function whose argument type - **Property `Deprecated`** (`boolean`): Whether or not this item is deprecated. - **Property `EditingDisabled`** (`boolean`): Toggles whether this property can be edited from the Properties window. - **Property `EditorType`** (`string`): - **Property `FFlag`** (`string`): - **Property `IsBackend`** (`boolean`): Vague value for showing if this depends on backend stuff. - **Property `PropertyOrder`** (`int`): - **Property `ScriptContext`** (`string`): Describes the context where this member can be used. If set to "Server", - **Property `ServerOnly`** (`boolean`): - **Property `SliderScaling`** (`string`): - **Property `UIMaximum`** (`double`): The maximum value of this property. Used with - **Property `UIMinimum`** (`double`): The minimum value of this property. Used with - **Property `UINumTicks`** (`double`): The number of potential values the property's slider bar can be set to, ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ReflectionMetadataEnums last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances summary: "Acts as a Folder for ReflectionMetadataEnum objects." --- # Class: ReflectionMetadataEnums > Acts as a [Folder](/docs/reference/engine/classes/Folder.md) for [ReflectionMetadataEnum](/docs/reference/engine/classes/ReflectionMetadataEnum.md) objects. ## Description The ReflectionMetadataEnums is the class that acts as a [Folder](/docs/reference/engine/classes/Folder.md) for [ReflectionMetadataEnum](/docs/reference/engine/classes/ReflectionMetadataEnum.md) objects. Since it is an internal object, it cannot be used by developers. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ReflectionMetadataEvents last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances summary: "Acts as a Folder containing information about events for a ReflectionMetadataClass, and should be parented to it. ReflectionMetadataMembers that are parented to this object will be perceived as events under the class this is parented to." --- # Class: ReflectionMetadataEvents > Acts as a [Folder](/docs/reference/engine/classes/Folder.md) containing information about events for a > [ReflectionMetadataClass](/docs/reference/engine/classes/ReflectionMetadataClass.md), and should be parented to it. > [ReflectionMetadataMembers](/docs/reference/engine/classes/ReflectionMetadataMember.md) that are parented > to this object will be perceived as events under the class this is parented > to. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ReflectionMetadataFunctions last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances summary: "Acts as a Folder containing information about functions for a ReflectionMetadataClass, and should be parented to it. ReflectionMetadataMembers that are parented to this object will be perceived as functions under the class this is parented to." --- # Class: ReflectionMetadataFunctions > Acts as a [Folder](/docs/reference/engine/classes/Folder.md) containing information about functions for a > [ReflectionMetadataClass](/docs/reference/engine/classes/ReflectionMetadataClass.md), and should be parented to it. > [ReflectionMetadataMembers](/docs/reference/engine/classes/ReflectionMetadataMember.md) that are parented > to this object will be perceived as functions under the class this is parented > to. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ReflectionMetadataItem last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable summary: "Acts as abstract properties for generic information about Classes, Members, Enums, and EnumItems." --- # Class: ReflectionMetadataItem > Acts as abstract properties for generic information about Classes, Members, > Enums, and EnumItems. ## Properties ### Property: ReflectionMetadataItem.Browsable ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Reflection" } ``` When this value is true, it means that this property/class can be seen in Studio, e.g. in the explorer. ### Property: ReflectionMetadataItem.ClassCategory ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Reflection" } ``` Describes the category of this class. ### Property: ReflectionMetadataItem.ClientOnly ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Reflection" } ``` ### Property: ReflectionMetadataItem.Constraint ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Reflection" } ``` Describes a constraint for a single-argument function whose argument type is a [Object.ClassName](/docs/reference/engine/classes/Object.md). Currently there are two constraints available: | Constraint | Description | | --- | --- | | isScriptCreatable | The specified class must be creatable with Instance.new | | isService | The specified class must be a service. | ### Property: ReflectionMetadataItem.Deprecated ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Reflection" } ``` When an object is deprecated, it should not be used anymore for new scripts. ### Property: ReflectionMetadataItem.EditingDisabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Reflection" } ``` Toggles whether this property can be edited from the Properties window. ### Property: ReflectionMetadataItem.EditorType ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Reflection" } ``` ### Property: ReflectionMetadataItem.FFlag ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Reflection" } ``` ### Property: ReflectionMetadataItem.IsBackend ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Reflection" } ``` This should determine if a method needs to use stuff on the Roblox main servers. This property isn't applied where it should be, though. ### Property: ReflectionMetadataItem.PropertyOrder ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Reflection" } ``` ### Property: ReflectionMetadataItem.ScriptContext ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Reflection" } ``` Describes the context where this member can be used. If set to "Server", this member will not be available to auto fill when editing a [LocalScript](/docs/reference/engine/classes/LocalScript.md). If set to "Client", this member will not be available to auto fill when editing a [Script](/docs/reference/engine/classes/Script.md). ### Property: ReflectionMetadataItem.ServerOnly ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Reflection" } ``` ### Property: ReflectionMetadataItem.SliderScaling ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Reflection" } ``` ### Property: ReflectionMetadataItem.UIMaximum ```json { "type": "double", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Reflection" } ``` The maximum value of this property. Used with [ReflectionMetadataItem.UIMinimum](/docs/reference/engine/classes/ReflectionMetadataItem.md) to control the slider bar of this property in the `Properties` window. ### Property: ReflectionMetadataItem.UIMinimum ```json { "type": "double", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Reflection" } ``` The minimum value of this property. Used with [ReflectionMetadataItem.UIMaximum](/docs/reference/engine/classes/ReflectionMetadataItem.md) to control the slider bar of this property in the `Properties` window. ### Property: ReflectionMetadataItem.UINumTicks ```json { "type": "double", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Reflection" } ``` The number of potential values the property's slider bar can be set to, [ReflectionMetadataItem.UIMinimum](/docs/reference/engine/classes/ReflectionMetadataItem.md) and [ReflectionMetadataItem.UIMaximum](/docs/reference/engine/classes/ReflectionMetadataItem.md). ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ReflectionMetadataMember last_updated: 2026-06-29T19:34:08Z inherits: - ReflectionMetadataItem - Instance - Object type: class memory_category: Instances summary: "An internal object which cannot be used by developers." --- # Class: ReflectionMetadataMember > An internal object which cannot be used by developers. ## Description ReflectionMetadataMember represents either a `Function`, `YieldFunction`, or `Property` in Roblox's ReflectionMetadata. ## Inherited Members ### From [ReflectionMetadataItem](/docs/reference/engine/classes/ReflectionMetadataItem.md) - **Property `Browsable`** (`boolean`): Whether or not this can be seen in studio. - **Property `ClassCategory`** (`string`): Describes the category of this class. - **Property `ClientOnly`** (`boolean`): - **Property `Constraint`** (`string`): Describes a constraint for a single-argument function whose argument type - **Property `Deprecated`** (`boolean`): Whether or not this item is deprecated. - **Property `EditingDisabled`** (`boolean`): Toggles whether this property can be edited from the Properties window. - **Property `EditorType`** (`string`): - **Property `FFlag`** (`string`): - **Property `IsBackend`** (`boolean`): Vague value for showing if this depends on backend stuff. - **Property `PropertyOrder`** (`int`): - **Property `ScriptContext`** (`string`): Describes the context where this member can be used. If set to "Server", - **Property `ServerOnly`** (`boolean`): - **Property `SliderScaling`** (`string`): - **Property `UIMaximum`** (`double`): The maximum value of this property. Used with - **Property `UIMinimum`** (`double`): The minimum value of this property. Used with - **Property `UINumTicks`** (`double`): The number of potential values the property's slider bar can be set to, ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ReflectionMetadataProperties last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances summary: "Acts as a Folder containing information about properties for a ReflectionMetadataClass, and should be parented to it. ReflectionMetadataMembers that are parented to this object will be perceived as properties under the class this is parented to." --- # Class: ReflectionMetadataProperties > Acts as a [Folder](/docs/reference/engine/classes/Folder.md) containing information about properties for a > [ReflectionMetadataClass](/docs/reference/engine/classes/ReflectionMetadataClass.md), and should be parented to it. > [ReflectionMetadataMembers](/docs/reference/engine/classes/ReflectionMetadataMember.md) that are parented > to this object will be perceived as properties under the class this is > parented to. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ReflectionMetadataYieldFunctions last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances summary: "Acts as a Folder containing information about yielding functions for a ReflectionMetadataClass, and should be parented to it. ReflectionMetadataMembers that are parented to this object will be perceived as yielding functions under the class this is parented to." --- # Class: ReflectionMetadataYieldFunctions > Acts as a [Folder](/docs/reference/engine/classes/Folder.md) containing information about yielding functions for a > [ReflectionMetadataClass](/docs/reference/engine/classes/ReflectionMetadataClass.md), and should be parented to it. > [ReflectionMetadataMembers](/docs/reference/engine/classes/ReflectionMetadataMember.md) that are parented > to this object will be perceived as yielding functions under the class this is > parented to. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ReflectionService last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated --- # Class: ReflectionService ## Description `ReflectionService` allows scripts to query the engine for details about its API, including required security permissions, functionality, and inheritance structure. You can use this service to dynamically inspect classes and their properties, methods, and events, which can be useful for debugging, tooling, or creating dynamic behaviors based on the engine's capabilities. The following is an example script that inspects classes and their properties: ```lua local ReflectionService = game:GetService("ReflectionService") -- Create a SecurityCapabilities object with all capabilities local allCapabilities = SecurityCapabilities.new(table.unpack(Enum.SecurityCapability:GetEnumItems())) -- Get all classes accessible with all capabilities local allClasses = ReflectionService:GetClasses({ Security = allCapabilities }) print("Total classes:", #allClasses) -- Get a specific class local partClass = ReflectionService:GetClass("Part") if partClass then print("\nPart class:") print(" Name:", partClass.Name) print(" Superclass:", partClass.Superclass and tostring(partClass.Superclass) or "none") end -- Get the first 5 properties of a class local properties = ReflectionService:GetPropertiesOfClass("Part", { Security = allCapabilities }) print("\nPart properties:", #properties) for i = 1, math.min(5, #properties) do print(" -", properties[i].Name) end ``` ## Methods ### Method: ReflectionService:GetClass **Signature:** `ReflectionService:GetClass(className: string, filter?: Dictionary): ReflectedClass?` Given a class name in the Roblox API, returns a `ReflectedClass` dictionary with information about the associated class's reflection state. If no such class name exists, returns `nil`. If you pass in a `ReflectionClassFilter` dictionary, you can adjust the set of eligible classes and adjust the output behavior: ```lua { -- The security context to use; can be more expansive than the current script's security Security: SecurityCapabilities?, -- default: SecurityCapabilities.fromCurrent() -- Require classes to derive from the passed-in class name IsA: string?, -- default: nil -- Whether to exclude Studio display information about the class ExcludeDisplay: boolean?, -- default: false } ``` If the class name is valid, you'll get the following `ReflectedClass` dictionary as output: ```lua { -- The name of the class Name: string, -- Whether the class is serialized (able to be saved to disk) Serialized: boolean, -- The class's parent in the instance hierarchy (unless it's the root) Superclass: string?, -- The names of the class's direct children in the instance hierarchy Subclasses: {string}, -- Studio display information Display: { -- Always "General" for classes Category: string, -- A message indicating that the class is deprecated, if applicable DeprecationMessage: string?, }?, -- The security permissions required to access this class Permits: { -- If the class is a Service, the security capabilities required to obtain access. If not applicable, this field isn't present GetService: SecurityCapabilities?, -- The security capabilities required to create an instance of this class. If not possible, this field isn't present New: SecurityCapabilities?, } } ``` *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `className` | `string` | | The name of the class for which you wish to retrieve information. | | `filter` | `Dictionary` | `nil` | An optional filter to restrict or expand the set of classes that this method can return and change the method's behavior. | **Returns:** `ReflectedClass?` — A `ReflectedClass` dictionary with reflection information if the class exists; otherwise, `nil`. ### Method: ReflectionService:GetClasses **Signature:** `ReflectionService:GetClasses(filter?: Dictionary): List` Returns a list of `ReflectedClass` dictionaries for all classes in the Roblox API that match the provided filter criteria. If you pass in a `ReflectionClassFilter` dictionary, you can adjust the set of eligible classes and adjust the output behavior: ```lua { -- The security context to use; can be more expansive than the current script's security Security: SecurityCapabilities?, -- default: SecurityCapabilities.fromCurrent() -- Require classes to derive from the passed-in class name IsA: string?, -- default: nil -- Whether to exclude Studio display information about the classes ExcludeDisplay: boolean?, -- default: false } ``` You'll get a list of `ReflectedClass` dictionaries as output, each with the same structure as described in the `GetClass` method. If there are no classes that match the filter criteria, you'll receive an empty list. *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `filter` | `Dictionary` | `nil` | An optional filter to restrict or expand the set of classes that this method can return and change the method's behavior. | **Returns:** `List` — A list of `ReflectedClass` dictionaries with reflection information for each class that matches the filter criteria. ### Method: ReflectionService:GetEventsOfClass **Signature:** `ReflectionService:GetEventsOfClass(className: string, filter?: Dictionary): List` Given a class name in the Roblox API, returns a list of `ReflectedEvent` dictionaries for all events of that class that match the provided filter criteria. If no such class name exists, returns `nil`. If you pass in a `ReflectionMemberFilter` dictionary, you can adjust the set of eligible events and adjust the output behavior: ```lua { -- The security context to use; can be more expansive than the current script's security Security: SecurityCapabilities?, -- default: SecurityCapabilities.fromCurrent() -- Whether to exclude inherited events from superclasses ExcludeInherited: boolean?, -- default: false -- Whether to exclude Studio display information about the events ExcludeDisplay: boolean?, -- default: false } ``` If the class name is valid and that class has events, you'll get a list of `ReflectedEvent` dictionaries as output, each with the following structure: ```lua { -- The name of the event Name: string, -- The superclass from which the property is inherited, or the class name if not inherited Owner: string, -- A list of all parameters the event passes to connected functions Parameters: { { -- The name of the parameter Name: string, -- The type of the parameter Type: ReflectionType, } }, -- Studio display information Display: { -- A message indicating that the event is deprecated, if applicable DeprecationMessage: string?, }?, -- The security permissions required to access this event Permits: { -- The security permissions required to listen for the event in any context Listen: SecurityCapabilities?, }, } ``` *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `className` | `string` | | The name of the class for which you wish to retrieve events. | | `filter` | `Dictionary` | `nil` | An optional filter to restrict or expand the set of events that this method can return and change the method's behavior. | **Returns:** `List` — A list of `ReflectedEvent` dictionaries with reflection information for each event of the class that matches the filter criteria. ### Method: ReflectionService:GetMethodsOfClass **Signature:** `ReflectionService:GetMethodsOfClass(className: string, filter?: Dictionary): List` Given a class name in the Roblox API, returns a list of `ReflectedMethod` dictionaries for all methods of that class that match the provided filter criteria. If no such class name exists, returns `nil`. If you pass in a `ReflectionMemberFilter` dictionary, you can adjust the set of eligible methods and adjust the output behavior: ```lua { -- The security context to use; can be more expansive than the current script's security Security: SecurityCapabilities?, -- default: SecurityCapabilities.fromCurrent() -- Whether to exclude inherited methods from superclasses ExcludeInherited: boolean?, -- default: false -- Whether to exclude Studio display information about the methods ExcludeDisplay: boolean?, -- default: false } ``` If the class name is valid and that class has methods, you'll get a list of `ReflectedMethod` dictionaries as output, each with the following structure: ```lua { -- The name of the method Name: string, -- The superclass from which the property is inherited, or the class name if not inherited Owner: string, -- A list of all parameters the method takes Parameters: { { -- The name of the parameter Name: string, -- The type of the parameter Type: ReflectionType, -- The default value of the parameter, if it has one DefaultValue: any?, } }, -- The return type of the method; "void" if none ReturnType: ReflectionType, -- Whether the method is yielding CanYield: boolean, -- Studio display information Display: { -- A message indicating that the method is deprecated, if applicable DeprecationMessage: string?, }?, -- The security permissions required to access this method Permits: { -- The security permissions required to call the method in a single-threaded context Call: SecurityCapabilities?, -- The security permissions required to call the method in a parallel context CallParallel: SecurityCapabilities?, }, } ``` *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `className` | `string` | | The name of the class for which you wish to retrieve methods. | | `filter` | `Dictionary` | `nil` | An optional filter to restrict or expand the set of methods that this method can return and change the method's behavior. | **Returns:** `List` — A list of `ReflectedMethod` dictionaries with reflection information for each method of the class that matches the filter criteria. ### Method: ReflectionService:GetPropertiesOfClass **Signature:** `ReflectionService:GetPropertiesOfClass(className: string, filter?: Dictionary): List` Given a class name in the Roblox API, returns a list of `ReflectedProperty` dictionaries for all properties of that class that match the provided filter criteria. If no such class name exists, returns `nil`. If you pass in a `ReflectionMemberFilter` dictionary, you can adjust the set of eligible properties and adjust the output behavior: ```lua { -- The security context to use; can be more expansive than the current script's security Security: SecurityCapabilities?, -- default: SecurityCapabilities.fromCurrent() -- Whether to exclude inherited properties from superclasses ExcludeInherited: boolean?, -- default: false -- Whether to exclude Studio display information about the properties ExcludeDisplay: boolean?, -- default: false } ``` If the class name is valid and that class has properties, you'll get a list of `ReflectedProperty` dictionaries as output, each with the following structure: ```lua { -- The name of the property Name: string, -- The superclass from which the property is inherited, or the class name if not inherited Owner: string, -- Whether the property is serialized (able to be saved to disk or sent over the network) Serialized: boolean, -- The type of the property Type: ReflectionType, -- The content type of the property, if applicable ContentType: Enum.AssetType?, -- Studio display information Display: { -- The category under which the property is listed in Studio Category: string, -- A message indicating that the property is deprecated, if applicable DeprecationMessage: string?, }?, -- The security permissions required to access this property Permits: { -- The security context required to read this property Read: SecurityCapabilities?, -- The security context required to read this property while thread-safe ReadParallel: SecurityCapabilities?, -- The security context required to write to this property Write: SecurityCapabilities?, -- The security context required to write to this property while thread-safe WriteParallel: SecurityCapabilities?, }, } ``` *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `className` | `string` | | The name of the class for which you wish to retrieve properties. | | `filter` | `Dictionary` | `nil` | An optional filter to restrict or expand the set of properties that this method can return and change the method's behavior. | **Returns:** `List` — A list of `ReflectedProperty` dictionaries with reflection information for each property of the class that matches the filter criteria. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: RemoteCommandService last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service --- # Class: RemoteCommandService ## Methods ### Method: RemoteCommandService:GetExecutingPlayer **Signature:** `RemoteCommandService:GetExecutingPlayer(): Player` *Security: None · Thread Safety: Unsafe · Capabilities: Unknown* **Returns:** `Player` ### Method: RemoteCommandService:GetReceivedUpdateSignal **Signature:** `RemoteCommandService:GetReceivedUpdateSignal(): RBXScriptSignal` *Security: None · Thread Safety: Unsafe · Capabilities: Unknown* **Returns:** `RBXScriptSignal` ### Method: RemoteCommandService:GetStoppingSignal **Signature:** `RemoteCommandService:GetStoppingSignal(): RBXScriptSignal` *Security: None · Thread Safety: Unsafe · Capabilities: Unknown* **Returns:** `RBXScriptSignal` ### Method: RemoteCommandService:SendUpdate **Signature:** `RemoteCommandService:SendUpdate(args: Tuple): ()` *Security: None · Thread Safety: Unsafe · Capabilities: Unknown* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `args` | `Tuple` | | | **Returns:** `()` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: RemoteDebuggerServer last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated --- # Class: RemoteDebuggerServer ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: RemoteEvent last_updated: 2026-06-29T19:34:08Z inherits: - BaseRemoteEvent - Instance - Object type: class memory_category: Instances summary: "An object which facilitates asynchronous, one-way communication across the client-server boundary. Scripts firing a RemoteEvent do not yield." --- # Class: RemoteEvent > An object which facilitates asynchronous, one-way communication across the > client-server boundary. Scripts firing a [RemoteEvent](/docs/reference/engine/classes/RemoteEvent.md) do not yield. ## Description The **RemoteEvent** object facilitates asynchronous, one-way communication across the [client-server](/docs/en-us/projects/client-server.md) boundary without yielding for a response. This communication can be directed from one client to the server, from the server to a specific client, or from the server to all clients. In order for both the server and clients to access a [RemoteEvent](/docs/reference/engine/classes/RemoteEvent.md) instance, it must be in a place where both sides can see it, such as [ReplicatedStorage](/docs/reference/engine/classes/ReplicatedStorage.md), although in some cases it's appropriate to store it in [Workspace](/docs/reference/engine/classes/Workspace.md) or inside a [Tool](/docs/reference/engine/classes/Tool.md). If no connected listener exists to handle an event, you might see a `Remote event invocation discarded` error in the log to indicate that the event was discarded and that you need to implement either `OnClientEvent` or `OnServerEvent`. Unlike [UnreliableRemoteEvents](/docs/reference/engine/classes/UnreliableRemoteEvent.md), [RemoteEvents](/docs/reference/engine/classes/RemoteEvent.md) buffer a large number of events before throwing this error. If you need the result of the call, you should use a [RemoteFunction](/docs/reference/engine/classes/RemoteFunction.md) instead. Otherwise a remote event is recommended since it will minimize network traffic/latency and won't yield the script to wait for a response. See [Remote events and callbacks](/docs/en-us/scripting/events/remote.md) for code samples and further details. #### Throttling Remote events are subject to rate limits when sent from the client to the server with the [FireServer()](/docs/reference/engine/classes/RemoteEvent.md) method. [RemoteEvents](/docs/reference/engine/classes/RemoteEvent.md) and [UnreliableRemoteEvents](/docs/reference/engine/classes/UnreliableRemoteEvent.md) both have a limit of approximately 500 requests per second, per client. This limit is **shared among all remote events of the same type**. To avoid throttling and latency issues, limit recurring remote events whenever possible. #### Parameter limitations Any type of Roblox object ([Enum](/docs/reference/engine/datatypes/Enum.md), [Instance](/docs/reference/engine/classes/Instance.md), etc.) can be passed as a parameter when a [RemoteEvent](/docs/reference/engine/classes/RemoteEvent.md) is fired, as well as Luau types such as numbers, strings, and booleans, although you should carefully explore the [limitations](/docs/en-us/scripting/events/remote.md#argument-limitations). ## Methods ### Method: RemoteEvent:FireAllClients **Signature:** `RemoteEvent:FireAllClients(arguments: Tuple): ()` Fires the [OnClientEvent](/docs/reference/engine/classes/RemoteEvent.md) event for each connected client. Unlike [FireClient()](/docs/reference/engine/classes/RemoteEvent.md), this event does not take a target [Player](/docs/reference/engine/classes/Player.md) as the first argument, since it fires to multiple clients. Since this method is used to communicate from the server to clients, it only works when used in a [Script](/docs/reference/engine/classes/Script.md). *Security: None · Thread Safety: Unsafe · Capabilities: RemoteEvent* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `arguments` | `Tuple` | | Values to pass to all [OnClientEvent](/docs/reference/engine/classes/RemoteEvent.md) events connected to the same [RemoteEvent](/docs/reference/engine/classes/RemoteEvent.md). | **Returns:** `()` ### Method: RemoteEvent:FireClient **Signature:** `RemoteEvent:FireClient(player: Player, arguments: Tuple): ()` Fires the [OnClientEvent](/docs/reference/engine/classes/RemoteEvent.md) event for the specific client in the required [Player](/docs/reference/engine/classes/Player.md) argument. Since this method is used to communicate from the server to a client, it only works when used in a [Script](/docs/reference/engine/classes/Script.md). *Security: None · Thread Safety: Unsafe · Capabilities: RemoteEvent* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Player` | | The client of the [Player](/docs/reference/engine/classes/Player.md) to fire the event to. | | `arguments` | `Tuple` | | Values to pass to [OnClientEvent](/docs/reference/engine/classes/RemoteEvent.md) events connected to the same [RemoteEvent](/docs/reference/engine/classes/RemoteEvent.md). | **Returns:** `()` ### Method: RemoteEvent:FireServer **Signature:** `RemoteEvent:FireServer(arguments: Tuple): ()` Fires the [OnServerEvent](/docs/reference/engine/classes/RemoteEvent.md) event on the server from one client. Connected events receive the [Player](/docs/reference/engine/classes/Player.md) argument of the firing client. Since this method is used to communicate from a client to the server, it only works when used in a client script. *Security: None · Thread Safety: Unsafe · Capabilities: RemoteEvent* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `arguments` | `Tuple` | | Values to pass to [OnServerEvent](/docs/reference/engine/classes/RemoteEvent.md) events connected to the same [RemoteEvent](/docs/reference/engine/classes/RemoteEvent.md). | **Returns:** `()` ## Events ### Event: RemoteEvent.OnClientEvent **Signature:** `RemoteEvent.OnClientEvent(arguments: Tuple)` Fires from a [LocalScript](/docs/reference/engine/classes/LocalScript.md) when either [FireClient()](/docs/reference/engine/classes/RemoteEvent.md) or [FireAllClients()](/docs/reference/engine/classes/RemoteEvent.md) is called on the same [RemoteEvent](/docs/reference/engine/classes/RemoteEvent.md) instance from a [Script](/docs/reference/engine/classes/Script.md). See [Remote Events and Callbacks](/docs/en-us/scripting/events/remote.md) for code samples and further details on [OnClientEvent](/docs/reference/engine/classes/RemoteEvent.md). *Security: None · Capabilities: RemoteEvent* **Parameters:** | Name | Type | Description | |------|------|-------------| | `arguments` | `Tuple` | The parameters sent through [FireClient()](/docs/reference/engine/classes/RemoteEvent.md) or [FireAllClients()](/docs/reference/engine/classes/RemoteEvent.md). | ### Event: RemoteEvent.OnServerEvent **Signature:** `RemoteEvent.OnServerEvent(player: Player, arguments: Tuple)` Fires from a [Script](/docs/reference/engine/classes/Script.md) when [FireServer()](/docs/reference/engine/classes/RemoteEvent.md) is called on the same [RemoteEvent](/docs/reference/engine/classes/RemoteEvent.md) instance from a [LocalScript](/docs/reference/engine/classes/LocalScript.md). See [Remote Events and Callbacks](/docs/en-us/scripting/events/remote.md) for code samples and further details on [OnServerEvent](/docs/reference/engine/classes/RemoteEvent.md). *Security: None · Capabilities: RemoteEvent* **Parameters:** | Name | Type | Description | |------|------|-------------| | `player` | `Player` | The [Player](/docs/reference/engine/classes/Player.md) associated with the client that the [FireServer()](/docs/reference/engine/classes/RemoteEvent.md) call originates from. | | `arguments` | `Tuple` | The parameters sent through [FireServer()](/docs/reference/engine/classes/RemoteEvent.md). | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: RemoteFunction last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances summary: "An object which facilitates synchronous, two-way communication across the client-server boundary. Scripts invoking a RemoteFunction yield until they receive a response from the recipient." --- # Class: RemoteFunction > An object which facilitates synchronous, two-way communication across the > client-server boundary. Scripts invoking a [RemoteFunction](/docs/reference/engine/classes/RemoteFunction.md) yield until > they receive a response from the recipient. ## Description The **RemoteFunction** object facilitates synchronous, two-way communication across the [client-server](/docs/en-us/projects/client-server.md) boundary. You can use it to define a custom callback function and invoke it manually by calling [RemoteFunction:InvokeClient()](/docs/reference/engine/classes/RemoteFunction.md) or [RemoteFunction:InvokeServer()](/docs/reference/engine/classes/RemoteFunction.md). The code invoking the function **yields** until it receives a response from the recipient. In order for both the server and clients to access a [RemoteFunction](/docs/reference/engine/classes/RemoteFunction.md) instance, it must be in a place where both sides can see it, such as [ReplicatedStorage](/docs/reference/engine/classes/ReplicatedStorage.md), although in some cases it's appropriate to store it in [Workspace](/docs/reference/engine/classes/Workspace.md) or inside a [Tool](/docs/reference/engine/classes/Tool.md). If the result is **not** needed, it is recommended that you use a [RemoteEvent](/docs/reference/engine/classes/RemoteEvent.md) instead, since its call is asynchronous and doesn't need to wait for a response to continue execution. See [Remote Events and Callbacks](/docs/en-us/scripting/events/remote.md) for code samples and further details on [RemoteFunction](/docs/reference/engine/classes/RemoteFunction.md). #### Streaming Precautions Note that if an invoked [RemoteFunction](/docs/reference/engine/classes/RemoteFunction.md) creates an instance on the server, there is no guarantee that it exists on the client when the function returns. This is particularly important in places where instance [streaming](/docs/en-us/workspace/streaming.md) is enabled and when the created instances are [BaseParts](/docs/reference/engine/classes/BasePart.md) or [Models](/docs/reference/engine/classes/Model.md), since parts that are far away from the player's character may not be streamed to the client, and models that are [Atomic](/docs/reference/engine/enums/ModelStreamingMode.md) depend on whether their parts are streamed. Even if a model is [Persistent](/docs/reference/engine/enums/ModelStreamingMode.md), there may be some delay between the creation of the model and when it is replicated to the client. #### Parameter Limitations Any type of Roblox object such as an [Enum](/docs/reference/engine/datatypes/Enum.md), [Instance](/docs/reference/engine/classes/Instance.md), or others can be passed as a parameter when a [RemoteFunction](/docs/reference/engine/classes/RemoteFunction.md) is invoked, as well as Luau types such as numbers, strings, and booleans, although you should carefully explore the [limitations](/docs/en-us/scripting/events/remote.md#argument-limitations). ## Methods ### Method: RemoteFunction:InvokeClient **Signature:** `RemoteFunction:InvokeClient(player: Player, arguments: Tuple): Tuple` Invokes the [RemoteFunction](/docs/reference/engine/classes/RemoteFunction.md) which in turn calls the [OnClientInvoke](/docs/reference/engine/classes/RemoteFunction.md) callback. Since this method is used to communicate from the server to a client, it will only work when used in a [Script](/docs/reference/engine/classes/Script.md). Any type of Roblox object such as an [Enum](/docs/reference/engine/datatypes/Enum.md), [Instance](/docs/reference/engine/classes/Instance.md), or others can be passed as a parameter to [InvokeClient()](/docs/reference/engine/classes/RemoteFunction.md), as well as Luau types such as numbers, strings, and booleans, although you should carefully explore the [limitations](/docs/en-us/scripting/events/remote.md#argument-limitations). See [Remote Events and Callbacks](/docs/en-us/scripting/events/remote.md) for code samples and further details on [RemoteFunction](/docs/reference/engine/classes/RemoteFunction.md). #### Warning In practice, the server does not often invoke the client, as clients typically do not have information that the server doesn't have, and actions that only a client can take, such as displaying a GUI, typically do not require a callback. As such, [RemoteEvent:FireClient()](/docs/reference/engine/classes/RemoteEvent.md) is recommended as an asynchronous method that doesn't need to wait for a response to continue execution. If you legitimately need to invoke a client from the server, note the following risks: - If the client throws an error, the server throws the error too. - If the client disconnects while it's being invoked, [InvokeClient()](/docs/reference/engine/classes/RemoteFunction.md) throws an error. - If the client doesn't return a value, the server yields forever. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: RemoteEvent* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Player` | | The [Player](/docs/reference/engine/classes/Player.md) associated with the client to invoke. | | `arguments` | `Tuple` | | Values to pass to the [OnClientInvoke](/docs/reference/engine/classes/RemoteFunction.md) callback. | **Returns:** `Tuple` — Values returned from the [OnClientInvoke](/docs/reference/engine/classes/RemoteFunction.md) callback. ### Method: RemoteFunction:InvokeServer **Signature:** `RemoteFunction:InvokeServer(arguments: Tuple): Tuple` Invokes the [RemoteFunction](/docs/reference/engine/classes/RemoteFunction.md) which in turn calls the [OnServerInvoke](/docs/reference/engine/classes/RemoteFunction.md) callback. Since this method is used to communicate from a client to the server, it will only work when used in a [LocalScript](/docs/reference/engine/classes/LocalScript.md). If a returned result is not needed, it's recommended to use [RemoteEvent:FireServer()](/docs/reference/engine/classes/RemoteEvent.md) instead, as its call is asynchronous and doesn't need to wait for a response to continue execution. Any type of Roblox object such as an [Enum](/docs/reference/engine/datatypes/Enum.md), [Instance](/docs/reference/engine/classes/Instance.md), or others can be passed as a parameter to [InvokeServer()](/docs/reference/engine/classes/RemoteFunction.md), as well as Luau types such as numbers, strings, and booleans, although you should carefully explore the [limitations](/docs/en-us/scripting/events/remote.md#argument-limitations). See [Remote Events and Callbacks](/docs/en-us/scripting/events/remote.md) for code samples and further details on [RemoteFunction](/docs/reference/engine/classes/RemoteFunction.md). *Yields · Security: None · Thread Safety: Unsafe · Capabilities: RemoteEvent* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `arguments` | `Tuple` | | Values to pass to the [OnServerInvoke](/docs/reference/engine/classes/RemoteFunction.md) callback. | **Returns:** `Tuple` — Values returned from the [OnServerInvoke](/docs/reference/engine/classes/RemoteFunction.md) callback. ## Callbacks ### Callback: RemoteFunction.OnClientInvoke **Signature:** `RemoteFunction.OnClientInvoke(arguments: Tuple): Tuple` This callback is called when the [RemoteFunction](/docs/reference/engine/classes/RemoteFunction.md) is invoked with [InvokeClient()](/docs/reference/engine/classes/RemoteFunction.md). When the bound function returns, the returned values are sent back to the calling server. See [Remote Events and Callbacks](/docs/en-us/scripting/events/remote.md) for code samples and further details on [OnClientInvoke](/docs/reference/engine/classes/RemoteFunction.md). *Security: None · Thread Safety: Unsafe · Capabilities: RemoteEvent* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `arguments` | `Tuple` | | The parameters sent through [InvokeClient()](/docs/reference/engine/classes/RemoteFunction.md). | **Returns:** `Tuple` — Values returned by the callback function. ### Callback: RemoteFunction.OnServerInvoke **Signature:** `RemoteFunction.OnServerInvoke(player: Player, arguments: Tuple): Tuple` This callback is called when the [RemoteFunction](/docs/reference/engine/classes/RemoteFunction.md) is invoked with [InvokeServer()](/docs/reference/engine/classes/RemoteFunction.md). When the bound function returns, the returned values are sent back to the calling client. See [Remote Events and Callbacks](/docs/en-us/scripting/events/remote.md) for code samples and further details on [OnServerInvoke](/docs/reference/engine/classes/RemoteFunction.md). *Security: None · Thread Safety: Unsafe · Capabilities: RemoteEvent* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Player` | | The [Player](/docs/reference/engine/classes/Player.md) associated with the client that the [InvokeServer()](/docs/reference/engine/classes/RemoteFunction.md) call originates from. | | `arguments` | `Tuple` | | The parameters sent through [InvokeServer()](/docs/reference/engine/classes/RemoteFunction.md). | **Returns:** `Tuple` — Values returned by the callback function. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: RenderSettings last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated - NotBrowsable --- # Class: RenderSettings ## Description `RenderSettings` is a singleton class which lets developers debug components of Roblox's graphics engine. It can be found in Roblox Studio's settings under the **Rendering** tab. ## Properties ### Property: RenderSettings.AutoFRMLevel ```json { "type": "int", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Debug" } ``` Sets the starting quality level of the framerate manager when [EnableFRM](/docs/reference/engine/classes/RenderSettings.md) is set to `true`. ### Property: RenderSettings.EagerBulkExecution ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Performance" } ``` When set to `true`, all scene updates will be given an unlimited budget, regardless of how computationally expensive it may be. This ensures each frame will look as it should, at the cost of a more unstable frame rate. ### Property: RenderSettings.EditQualityLevel ```json { "type": "QualityLevel", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Performance" } ``` Sets the graphics quality level in Roblox Studio when [EnableFRM](/docs/reference/engine/classes/RenderSettings.md) is set to `false`. ### Property: RenderSettings.Enable VR Mode ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "General" } ``` ### Property: RenderSettings.ExportMergeByMaterial ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "General" } ``` Sets whether materials should be generated per part, or per unique appearance in Roblox's exporter. ### Property: RenderSettings.FrameRateManager ```json { "type": "FramerateManagerMode", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "General" } ``` Specifies the behavior of the framerate manager. ### Property: RenderSettings.GraphicsMode ```json { "type": "GraphicsMode", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "General" } ``` The graphics API that Roblox will use on startup. ### Property: RenderSettings.MeshCacheSize ```json { "type": "int", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Cache" } ``` The size in bytes of the mesh cache. Defaults to 32 MiB. ### Property: RenderSettings.MeshPartDetailLevel ```json { "type": "MeshPartDetailLevel", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Debug" } ``` Determines the mode for the selection of detail levels for mesh parts. For a good balance between performance and fidelity, this should be set to [MeshPartDetailLevel.DistanceBased](/docs/reference/engine/enums/MeshPartDetailLevel.md) (default), which is what the client uses. Note that the [MeshPart.RenderFidelity](/docs/reference/engine/classes/MeshPart.md) needs to be set to [RenderFidelity.Automatic](/docs/reference/engine/enums/RenderFidelity.md) for this to work. If you set it to [RenderFidelity.Precise](/docs/reference/engine/enums/RenderFidelity.md), you will always see the higher resolution version and the [MeshPartDetailLevel](/docs/reference/engine/enums/MeshPartDetailLevel.md) value will be ignored for that [MeshPart](/docs/reference/engine/classes/MeshPart.md). ### Property: RenderSettings.QualityLevel ```json { "type": "QualityLevel", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Performance" } ``` If [EnableFRM](/docs/reference/engine/classes/RenderSettings.md) is set to `true`, this property controls the quality level in Roblox Studio. ### Property: RenderSettings.ReloadAssets ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Debug" } ``` When set to `true`, Roblox Studio will automatically reload changes that are made to files in Roblox's `content` folder. ### Property: RenderSettings.RenderCSGTrianglesDebug ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Debug" } ``` When set to `true`, a wireframe of polygons will be shown on all [PartOperation](/docs/reference/engine/classes/PartOperation.md) objects. ### Property: RenderSettings.ShowBoundingBoxes ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Debug" } ``` If set to `true`, renders bounding boxes around each individual rendered entity in the scene. ### Property: RenderSettings.ViewMode ```json { "type": "ViewMode", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Debug" } ``` ### Property: RenderSettings.EnableFRM *(hidden)* ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Debug" } ``` Toggles the enabled state of the framerate manager. ## Methods ### Method: RenderSettings:GetMaxQualityLevel **Signature:** `RenderSettings:GetMaxQualityLevel(): int` Returns the maximum quality level. *Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `int` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: RenderingTest last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances summary: "An internal testing utility for the rendering pipeline." --- # Class: RenderingTest > An internal testing utility for the rendering pipeline. ## Description **RenderingTest** is an internal testing utility for the rendering pipeline. It is not intended for external use. ## Properties ### Property: RenderingTest.CFrame ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "RenderingTest", "capabilities": [ "Basic" ] } ``` ### Property: RenderingTest.ComparisonDiffThreshold ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "RenderingTest", "capabilities": [ "Basic" ] } ``` ### Property: RenderingTest.ComparisonMethod ```json { "type": "RenderingTestComparisonMethod", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "RenderingTest", "capabilities": [ "Basic" ] } ``` ### Property: RenderingTest.ComparisonPsnrThreshold ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "RenderingTest", "capabilities": [ "Basic" ] } ``` ### Property: RenderingTest.Description ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "RenderingTest", "capabilities": [ "Basic" ] } ``` ### Property: RenderingTest.FieldOfView ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "RenderingTest", "capabilities": [ "Basic" ] } ``` ### Property: RenderingTest.PerfTest ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "RenderingTest", "capabilities": [ "Basic" ] } ``` ### Property: RenderingTest.QualityAuto ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "RenderingTest", "capabilities": [ "Basic" ] } ``` ### Property: RenderingTest.QualityLevel ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "RenderingTest", "capabilities": [ "Basic" ] } ``` ### Property: RenderingTest.RenderingTestFrameCount ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "RenderingTest", "capabilities": [ "Basic" ] } ``` ### Property: RenderingTest.ShouldSkip ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "RenderingTest", "capabilities": [ "Basic" ] } ``` ### Property: RenderingTest.Ticket ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "RenderingTest", "capabilities": [ "Basic" ] } ``` ### Property: RenderingTest.Timeout ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "RenderingTest", "capabilities": [ "Basic" ] } ``` ### Property: RenderingTest.Orientation *(hidden)* ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "RenderingTest", "capabilities": [ "Basic" ] } ``` ### Property: RenderingTest.Position *(hidden)* ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "RenderingTest", "capabilities": [ "Basic" ] } ``` ## Methods ### Method: RenderingTest:RenderdocTriggerCapture **Signature:** `RenderingTest:RenderdocTriggerCapture(): ()` An internal service which cannot be used by developers. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Returns:** `()` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ReplicatedFirst last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service summary: "A container whose contents are replicated to all clients (but not back to the server) first before anything else." --- # Class: ReplicatedFirst > A container whose contents are replicated to all clients (but not back to the > server) first before anything else. ## Description `ReplicatedFirst` is a container whose contents are replicated to all clients (but not back to the server) before anything else. It's most commonly used to store [LocalScripts](/docs/reference/engine/classes/LocalScript.md) and other elements that are essential for the experience's start such as [loading screens](/docs/en-us/players/loading-screens.md). For objects that do **not** need to be replicated before anything else, use the [ReplicatedStorage](/docs/reference/engine/classes/ReplicatedStorage.md) container instead. There are some key considerations for running [LocalScripts](/docs/reference/engine/classes/LocalScript.md) in `ReplicatedFirst`: - Since its contents replicate before anything else in the experience, [LocalScripts](/docs/reference/engine/classes/LocalScript.md) running in `ReplicatedFirst` will need to wait for any objects they require to replicate using [Instance:WaitForChild()](/docs/reference/engine/classes/Instance.md) - Any objects that are to be used by a [LocalScript](/docs/reference/engine/classes/LocalScript.md) in `ReplicatedFirst` should also be parented to `ReplicatedFirst`. Otherwise, they may replicate to the client late, yielding the script and negating the benefit of initial replication. ## Methods ### Method: ReplicatedFirst:RemoveDefaultLoadingScreen **Signature:** `ReplicatedFirst:RemoveDefaultLoadingScreen(): ()` Immediately removes the default Roblox loading screen. Note that if any object has been placed in `ReplicatedFirst`, the default loading screen will be removed after a few seconds regardless if this method has been called or not. You should **not** remove the default loading screen unless you want to display your own. If you remove the default screen without a replacement, players will be able to see geometry loading in the background. *Security: None · Thread Safety: Unsafe* **Returns:** `()` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ReplicatedStorage last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service summary: "A container service for objects that are replicated to all clients." --- # Class: ReplicatedStorage > A container service for objects that are replicated to all clients. ## Description [ReplicatedStorage](/docs/reference/engine/classes/ReplicatedStorage.md) is a general container service for objects that are available to both the server and connected clients. It is ideal for [ModuleScript](/docs/reference/engine/classes/ModuleScript.md), [RemoteFunction](/docs/reference/engine/classes/RemoteFunction.md), [RemoteEvent](/docs/reference/engine/classes/RemoteEvent.md), and other objects that are useful to both server-side [Scripts](/docs/reference/engine/classes/Script.md) and client-side [LocalScripts](/docs/reference/engine/classes/LocalScript.md). Objects parented to this service are fully replicated to clients, and normal replication rules apply. Any changes that are made on the client persist but aren't replicated to the server. Client changes may be overwritten if the server does something that overwrites those changes. Certain changes on the client, such as moving an object from the [Workspace](/docs/reference/engine/classes/Workspace.md) to [ReplicatedStorage](/docs/reference/engine/classes/ReplicatedStorage.md), can lead to desynchronization issues (for example, physics updates not being replicated to the object). [LocalScripts](/docs/reference/engine/classes/LocalScript.md) do not run when parented to this service, even if they are [Enabled](/docs/reference/engine/classes/BaseScript.md); [LocalScripts](/docs/reference/engine/classes/LocalScript.md) have various other locations where they eventually run on a [Player](/docs/reference/engine/classes/Player.md) client such as [StarterPlayerScripts](/docs/reference/engine/classes/StarterPlayerScripts.md), [StarterCharacterScripts](/docs/reference/engine/classes/StarterCharacterScripts.md), or [StarterGui](/docs/reference/engine/classes/StarterGui.md). Similarly, [Scripts](/docs/reference/engine/classes/Script.md) do not run when parented to this service unless you change their [RunContext](/docs/reference/engine/enums/RunContext.md) property from the default value of [Legacy](/docs/reference/engine/enums/RunContext.md). Server [Scripts](/docs/reference/engine/classes/Script.md) that run on their own should be parented to [ServerScriptService](/docs/reference/engine/classes/ServerScriptService.md) instead. If a [ModuleScript](/docs/reference/engine/classes/ModuleScript.md) within this service is required by any other script, it runs as normal. Such modules typically house code that is shared by the server and client. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ReverbSoundEffect last_updated: 2026-06-29T19:34:08Z inherits: - SoundEffect - Instance - Object type: class memory_category: Instances summary: "Reverberates audio, simulating the effect of bouncing off walls in a room." --- # Class: ReverbSoundEffect > Reverberates audio, simulating the effect of bouncing off walls in a room. ## Description The **ReverbSoundEffect** simulates the effect of sounds bouncing off of several surfaces (such as walls in a room), which causes several overlapping echoes that arrive at the listener at slightly offset times. Like all other [SoundEffects](/docs/reference/engine/classes/SoundEffect.md), a [ReverbSoundEffect](/docs/reference/engine/classes/ReverbSoundEffect.md) can be applied either to a [Sound](/docs/reference/engine/classes/Sound.md) or [SoundGroup](/docs/reference/engine/classes/SoundGroup.md) by being parented to either. ## Properties ### Property: ReverbSoundEffect.DecayTime ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "LegacySound" ] } ``` Range: 0.1 to 20 (default 1.5) Sets how long it takes for the reverberating echoes to fade out completely. Larger decay times simulate larger (empty) spaces ### Property: ReverbSoundEffect.Density ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "LegacySound" ] } ``` Range: 0 to 1 (default 1) Controls how many reflections are generated. ### Property: ReverbSoundEffect.Diffusion ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "LegacySound" ] } ``` Range: 0 to 1 (default 1) Controls how smooth and reflective the simulated surfaces are. The lower this value, the more discrete the echoes are. At higher levels the echos will blend more. ### Property: ReverbSoundEffect.DryLevel ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "LegacySound" ] } ``` Range: -80 to 10 (default -6) The output volume of the original sound. ### Property: ReverbSoundEffect.WetLevel ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "LegacySound" ] } ``` Range: -80 to 10 (default 0) The output volume of the echoed effect. ## Inherited Members ### From [SoundEffect](/docs/reference/engine/classes/SoundEffect.md) - **Property `Enabled`** (`boolean`): Toggles the effect on and off. - **Property `Priority`** (`int`): Determines the order the effect will be applied in relation to other ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: RigidConstraint last_updated: 2026-06-29T19:34:08Z inherits: - Constraint - Instance - Object type: class memory_category: BaseParts summary: "Creates a rigid connection between two Attachments or Bones." --- # Class: RigidConstraint > Creates a rigid connection between two [Attachments](/docs/reference/engine/classes/Attachment.md) or > [Bones](/docs/reference/engine/classes/Bone.md). ## Description **RigidConstraint** connects two [Attachments](/docs/reference/engine/classes/Attachment.md) or [Bones](/docs/reference/engine/classes/Bone.md) and ensures they stay in the same relative position/orientation to each other. This flexibility gives it additional functionality beyond [WeldConstraint](/docs/reference/engine/classes/WeldConstraint.md), such as attaching accessories to [Attachments](/docs/reference/engine/classes/Attachment.md) on a character rig. The fastest way to create this constraint is by selecting **Rigid Constraint** through Studio's **Create** menu in the toolbar's **Model** tab. Note that this tool behaves differently depending on whether you click on existing [BaseParts](/docs/reference/engine/classes/BasePart.md), [Attachments](/docs/reference/engine/classes/Attachment.md), or [Bones](/docs/reference/engine/classes/Bone.md) after the tool is activated: - Clicking on an existing [BasePart](/docs/reference/engine/classes/BasePart.md) creates a new [Attachment](/docs/reference/engine/classes/Attachment.md) upon it as the intended [RigidConstraint.Attachment0](/docs/reference/engine/classes/RigidConstraint.md) or [RigidConstraint.Attachment1](/docs/reference/engine/classes/RigidConstraint.md). - Clicking on an existing [Attachment](/docs/reference/engine/classes/Attachment.md) or [Bone](/docs/reference/engine/classes/Bone.md) uses that instance as the intended [RigidConstraint.Attachment0](/docs/reference/engine/classes/RigidConstraint.md) or [RigidConstraint.Attachment1](/docs/reference/engine/classes/RigidConstraint.md). Following the second valid click, a new [RigidConstraint](/docs/reference/engine/classes/RigidConstraint.md) is created to connect the two new attachments. If either the first or second click is **not** made on a [BasePart](/docs/reference/engine/classes/BasePart.md), [Attachment](/docs/reference/engine/classes/Attachment.md), or [Bone](/docs/reference/engine/classes/Bone.md), the workflow is canceled and no constraint is created. ## Inherited Members ### From [Constraint](/docs/reference/engine/classes/Constraint.md) - **Property `Active`** (`boolean`): Indicates if the constraint is currently active in the world. - **Property `Attachment0`** (`Attachment`): The Attachment that is connected to - **Property `Attachment1`** (`Attachment`): The Attachment that is connected to - **Property `Color`** (`BrickColor`): The color of the constraint. - **Property `Enabled`** (`boolean`): Toggles whether or not the constraint is enabled. - **Property `Visible`** (`boolean`): Toggles the constraint's visibility. - **Method `GetDebugAppliedForce(bodyId: int): Vector3`**: *(deprecated)* - **Method `GetDebugAppliedTorque(bodyId: int): Vector3`**: *(deprecated)* ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: RocketPropulsion last_updated: 2026-06-29T19:34:08Z inherits: - BodyMover - Instance - Object type: class memory_category: Instances tags: - Deprecated summary: "Applies a force so that an assembly follows and faces a target part." --- # Class: RocketPropulsion > Applies a force so that an assembly follows and faces a target part. ## Description The `RocketPropulsion` object applies force on an assembly so that it both **follows** and **faces** a target. It acts like a hybrid of [BodyPosition](/docs/reference/engine/classes/BodyPosition.md) and [BodyGyro](/docs/reference/engine/classes/BodyGyro.md). Unlike other [BodyMovers](/docs/reference/engine/classes/BodyMover.md), `RocketPropulsion` must be instructed to begin applying or stopping force via [Fire()](/docs/reference/engine/classes/RocketPropulsion.md) or [Abort()](/docs/reference/engine/classes/RocketPropulsion.md) respectively. You can detect when the assembly reaches its target using the [ReachedTarget](/docs/reference/engine/classes/RocketPropulsion.md) event which fires once the assembly is within the [TargetRadius](/docs/reference/engine/classes/RocketPropulsion.md) of the [Target](/docs/reference/engine/classes/RocketPropulsion.md) part. ## Properties ### Property: RocketPropulsion.CartoonFactor ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Goals", "capabilities": [ "Physics" ] } ``` This property determines the tendency of the assembly to face the [Target](/docs/reference/engine/classes/RocketPropulsion.md). By default, this property is set to `0.7`. If set to `0`, the assembly will make no effort to face the target. ### Property: RocketPropulsion.MaxSpeed ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Thrust", "capabilities": [ "Physics" ] } ``` This property determines the upper limit of the velocity at which the assembly will move toward the [Target](/docs/reference/engine/classes/RocketPropulsion.md). ### Property: RocketPropulsion.MaxThrust ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Thrust", "capabilities": [ "Physics" ] } ``` This property determines the upper limit of the thrust that may be exerted to move the assembly. Assemblies that have high mass will require more thrust in order to remain airborne and thus track the [Target](/docs/reference/engine/classes/RocketPropulsion.md). ### Property: RocketPropulsion.MaxTorque ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Turn", "capabilities": [ "Physics" ] } ``` This property determines the upper limit on the amount of torque that may be exerted in order to rotate the assembly towards the [Target](/docs/reference/engine/classes/RocketPropulsion.md). It functions similarly to [BodyGyro.MaxTorque](/docs/reference/engine/classes/BodyGyro.md). ### Property: RocketPropulsion.Target ```json { "type": "BasePart", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Goals", "capabilities": [ "Physics" ] } ``` This property determines the object towards which the `RocketPropulsion` will exert force/torque. If set to `nil`, the [TargetOffset](/docs/reference/engine/classes/RocketPropulsion.md) will be used instead. ### Property: RocketPropulsion.TargetOffset ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Goals", "capabilities": [ "Physics" ] } ``` This property determines the world offset from the [Target](/docs/reference/engine/classes/RocketPropulsion.md). It is especially useful when [Target](/docs/reference/engine/classes/RocketPropulsion.md) is set to `nil`, since this property then acts as the target position. ### Property: RocketPropulsion.TargetRadius ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Goals", "capabilities": [ "Physics" ] } ``` This property determines the maximum distance from the [Target](/docs/reference/engine/classes/RocketPropulsion.md) at which the assembly must be in order for [ReachedTarget](/docs/reference/engine/classes/RocketPropulsion.md) to be fired. It does not affect the exerted forces in any way. ### Property: RocketPropulsion.ThrustD ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Thrust", "capabilities": [ "Physics" ] } ``` This property is used to dampen the velocity of the assembly in order to prevent it from overshooting the [Target](/docs/reference/engine/classes/RocketPropulsion.md) and causing a "rubber‑banding" effect. It behaves similarly to [BodyPosition.D](/docs/reference/engine/classes/BodyPosition.md). ### Property: RocketPropulsion.ThrustP ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Thrust", "capabilities": [ "Physics" ] } ``` This property determines how much power is used while applying force in order to reach the [Target](/docs/reference/engine/classes/RocketPropulsion.md) position. The higher this value, the more power will be used and the faster it will be used. This property works similarly to [BodyPosition.P](/docs/reference/engine/classes/BodyPosition.md). ### Property: RocketPropulsion.TurnD ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Turn", "capabilities": [ "Physics" ] } ``` This property specifies how much dampening will be applied to the torque used to face the [Target](/docs/reference/engine/classes/RocketPropulsion.md). When the assembly approaches the goal orientation, it needs to decelerate, otherwise it will rotate past the goal and have to stop and re-accelerate back toward the goal. This often creates an undesirable "rubber‑banding" effect, avoided by applying dampening. The higher this value is set, the greater the dampening curve becomes, or the slower the part will approach the goal orientation. ### Property: RocketPropulsion.TurnP ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Turn", "capabilities": [ "Physics" ] } ``` This property determines how much power is used while applying torque in order to face the [Target](/docs/reference/engine/classes/RocketPropulsion.md). The higher this value, the more power will be used and the faster it will be used. ## Methods ### Method: RocketPropulsion:Abort **Signature:** `RocketPropulsion:Abort(): ()` Causes the assembly to stop moving toward its [Target](/docs/reference/engine/classes/RocketPropulsion.md). *Security: None · Thread Safety: Unsafe · Capabilities: Physics* **Returns:** `()` ### Method: RocketPropulsion:Fire **Signature:** `RocketPropulsion:Fire(): ()` Causes the assembly to start moving toward its [Target](/docs/reference/engine/classes/RocketPropulsion.md). *Security: None · Thread Safety: Unsafe · Capabilities: Physics* **Returns:** `()` ### Method: RocketPropulsion:fire **Signature:** `RocketPropulsion:fire(): ()` > **Deprecated:** This function is a deprecated variant of [RocketPropulsion:Fire()](/docs/reference/engine/classes/RocketPropulsion.md) which should be used instead. *Security: None · Thread Safety: Unsafe · Capabilities: Physics* **Returns:** `()` ## Events ### Event: RocketPropulsion.ReachedTarget **Signature:** `RocketPropulsion.ReachedTarget()` Fires when the assembly comes within [TargetRadius](/docs/reference/engine/classes/RocketPropulsion.md) of the [Target](/docs/reference/engine/classes/RocketPropulsion.md). *Security: None · Capabilities: Physics* ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: RodConstraint last_updated: 2026-06-29T19:34:08Z inherits: - Constraint - Instance - Object type: class memory_category: BaseParts summary: "Keeps two attachments separated by its defined Length." --- # Class: RodConstraint > Keeps two attachments separated by its defined > [Length](/docs/reference/engine/classes/RodConstraint.md). ## Description A **RodConstraint** keeps two attachments separated by its defined [Length](/docs/reference/engine/classes/RodConstraint.md). By default, both attachments can rotate freely, although you can enable limits to restrict rotational tilt. Note that if this constraint attaches one part (**A**) to another part (**B**) that is anchored or connected to an anchored part (**Z**), part **A** will not be locally simulated when interacting with a player. #### Limits You can limit rotation of the attachments within a cone, independently of each other, by enabling the [LimitsEnabled](/docs/reference/engine/classes/RodConstraint.md) property and setting [LimitAngle0](/docs/reference/engine/classes/RodConstraint.md) and [LimitAngle1](/docs/reference/engine/classes/RodConstraint.md) respectively. ## Properties ### Property: RodConstraint.CurrentDistance ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Derived", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The current distance between the constraint's [Attachments](/docs/reference/engine/classes/Attachment.md). ### Property: RodConstraint.Length ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Rod", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The distance apart at which the constraint attempts to keep its [Attachments](/docs/reference/engine/classes/Attachment.md). Measured in studs. ### Property: RodConstraint.LimitAngle0 ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Limits", "capabilities": [ "Physics" ], "simulationAccess": true } ``` This property determines the maximum angle between the rod and [Attachment0](/docs/reference/engine/classes/Constraint.md) when [LimitsEnabled](/docs/reference/engine/classes/RodConstraint.md) is true. ### Property: RodConstraint.LimitAngle1 ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Limits", "capabilities": [ "Physics" ], "simulationAccess": true } ``` This property determines the maximum angle between the rod and [Attachment1](/docs/reference/engine/classes/Constraint.md) when [LimitsEnabled](/docs/reference/engine/classes/RodConstraint.md) is true. ### Property: RodConstraint.LimitsEnabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Rod", "capabilities": [ "Physics" ], "simulationAccess": true } ``` This property determines whether the [LimitAngle0](/docs/reference/engine/classes/RodConstraint.md) and [LimitAngle1](/docs/reference/engine/classes/RodConstraint.md) properties control the angles between the rod and the respective attachments. ### Property: RodConstraint.Thickness ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The visualized thickness of the [RodConstraint](/docs/reference/engine/classes/RodConstraint.md). ## Inherited Members ### From [Constraint](/docs/reference/engine/classes/Constraint.md) - **Property `Active`** (`boolean`): Indicates if the constraint is currently active in the world. - **Property `Attachment0`** (`Attachment`): The Attachment that is connected to - **Property `Attachment1`** (`Attachment`): The Attachment that is connected to - **Property `Color`** (`BrickColor`): The color of the constraint. - **Property `Enabled`** (`boolean`): Toggles whether or not the constraint is enabled. - **Property `Visible`** (`boolean`): Toggles the constraint's visibility. - **Method `GetDebugAppliedForce(bodyId: int): Vector3`**: *(deprecated)* - **Method `GetDebugAppliedTorque(bodyId: int): Vector3`**: *(deprecated)* ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: RopeConstraint last_updated: 2026-06-29T19:34:08Z inherits: - Constraint - Instance - Object type: class memory_category: BaseParts summary: "Simulates rope dynamics, preventing two attachments from separating further than a defined length." --- # Class: RopeConstraint > Simulates rope dynamics, preventing two attachments from separating further > than a defined length. ## Description A **RopeConstraint** prevents two attachments from separating further than a defined [Length](/docs/reference/engine/classes/RopeConstraint.md). The attachments can move closer together than this length and both can freely rotate. This constraint can also be powered to behave as a motorized winch. When configuring this constraint, it may be helpful to study [Roblox Units](/docs/en-us/physics/units.md) to understand how Roblox units compare to metric units. #### Winch If a rope's [WinchEnabled](/docs/reference/engine/classes/RopeConstraint.md) property is enabled, it attempts to translate the attachments to a set separation specified by [WinchTarget](/docs/reference/engine/classes/RopeConstraint.md), effectively the target length of the rope in studs. This translation is controlled by [WinchSpeed](/docs/reference/engine/classes/RopeConstraint.md), [WinchResponsiveness](/docs/reference/engine/classes/RopeConstraint.md), and [WinchForce](/docs/reference/engine/classes/RopeConstraint.md). Note that [WinchSpeed](/docs/reference/engine/classes/RopeConstraint.md) must be a **positive** value, used to either contract or extend the rope length to [WinchTarget](/docs/reference/engine/classes/RopeConstraint.md). Setting a negative speed will revert to 0, not reverse the winch servo direction. ## Properties ### Property: RopeConstraint.CurrentDistance ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Derived", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The current distance between the constraint's [Attachments](/docs/reference/engine/classes/Attachment.md). ### Property: RopeConstraint.Length ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Rope", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The maximum distance apart the two [Attachments](/docs/reference/engine/classes/Attachment.md) can be. Measured in studs. ### Property: RopeConstraint.Restitution ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Rope", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Elasticity of the [Attachments](/docs/reference/engine/classes/Attachment.md) connected by the constraint when reaching the maximum defined [Length](/docs/reference/engine/classes/RopeConstraint.md). Constrained between 0 and 1. ### Property: RopeConstraint.Thickness ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The visualized thickness of the [RopeConstraint](/docs/reference/engine/classes/RopeConstraint.md). ### Property: RopeConstraint.WinchEnabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Rope", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Enables the winch motor which has the effect or reeling in/out the length of the rope to [WinchTarget](/docs/reference/engine/classes/RopeConstraint.md). ### Property: RopeConstraint.WinchForce ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Winch", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The maximum force that the winch motor can apply. ### Property: RopeConstraint.WinchResponsiveness ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Winch", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The sharpness of the winch motor in reaching the [WinchTarget](/docs/reference/engine/classes/RopeConstraint.md). ### Property: RopeConstraint.WinchSpeed ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Winch", "capabilities": [ "Physics" ], "simulationAccess": true } ``` A positive desired velocity at which the winch motor changes the rope length. ### Property: RopeConstraint.WinchTarget ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Winch", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The target length for the winch motor. ## Inherited Members ### From [Constraint](/docs/reference/engine/classes/Constraint.md) - **Property `Active`** (`boolean`): Indicates if the constraint is currently active in the world. - **Property `Attachment0`** (`Attachment`): The Attachment that is connected to - **Property `Attachment1`** (`Attachment`): The Attachment that is connected to - **Property `Color`** (`BrickColor`): The color of the constraint. - **Property `Enabled`** (`boolean`): Toggles whether or not the constraint is enabled. - **Property `Visible`** (`boolean`): Toggles the constraint's visibility. - **Method `GetDebugAppliedForce(bodyId: int): Vector3`**: *(deprecated)* - **Method `GetDebugAppliedTorque(bodyId: int): Vector3`**: *(deprecated)* ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Rotate last_updated: 2026-06-29T19:34:08Z inherits: - JointInstance - Instance - Object type: class memory_category: BaseParts tags: - Deprecated --- # Class: Rotate ## Description The **Rotate** object is used to allow rotation between two parts. Most commonly created through the [SurfaceType.Hinge](/docs/reference/engine/enums/SurfaceType.md) on a [BasePart](/docs/reference/engine/classes/BasePart.md). If created like this, the rotation will be about the normal vector from the face of the part the hinge is placed on. If created through a script, the axis and point of rotation can be defined arbitrarily. ## Inherited Members ### From [JointInstance](/docs/reference/engine/classes/JointInstance.md) - **Property `Active`** (`boolean`): Determines if the joint is currently active in the world. - **Property `C0`** (`CFrame`): Determines how the offset point is attached to - **Property `C1`** (`CFrame`): Subtracted from the C0 property to create an - **Property `Enabled`** (`boolean`): Sets whether the joint is active or not. - **Property `Part0`** (`BasePart`): The first BasePart that the joint connects. - **Property `Part1`** (`BasePart`): The second BasePart that the joint connects. - **Property `part1`** (`BasePart`): *(deprecated, hidden)* ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: RotateP last_updated: 2026-06-29T19:34:08Z inherits: - DynamicRotate - JointInstance - Instance - Object type: class memory_category: BaseParts tags: - Deprecated --- # Class: RotateP ## Description A **RotateP** object joins two parts together and allows rotation about a set axis. The joint will attempt to rotate the two parts until a desired rotational position is reached. This object is most commonly created by the [SurfaceType.SteppingMotor](/docs/reference/engine/enums/SurfaceType.md) on a [BasePart](/docs/reference/engine/classes/BasePart.md). If created through a script, behavior is still governed by the surface input of [JointInstance.Part0](/docs/reference/engine/classes/JointInstance.md). The three inputs of note are as follows: - `NoInput` — The joint will not rotate under its own power. It can still be rotated by external forces (such as from a character pushing one of the parts). - `Constant` — The joint will rotate based on the `ParamB` property of [JointInstance.Part0](/docs/reference/engine/classes/JointInstance.md). This rotation is measured in radians per physics frame (which is approximately 1/60th of a second). - `Sin` — The joint will rotate based on the `ParamA` and `ParamB` properties of [JointInstance.Part0](/docs/reference/engine/classes/JointInstance.md). ## Inherited Members ### From [DynamicRotate](/docs/reference/engine/classes/DynamicRotate.md) - **Property `BaseAngle`** (`float`): The base angle of the DynamicRotate object, in radians. ### From [JointInstance](/docs/reference/engine/classes/JointInstance.md) - **Property `Active`** (`boolean`): Determines if the joint is currently active in the world. - **Property `C0`** (`CFrame`): Determines how the offset point is attached to - **Property `C1`** (`CFrame`): Subtracted from the C0 property to create an - **Property `Enabled`** (`boolean`): Sets whether the joint is active or not. - **Property `Part0`** (`BasePart`): The first BasePart that the joint connects. - **Property `Part1`** (`BasePart`): The second BasePart that the joint connects. - **Property `part1`** (`BasePart`): *(deprecated, hidden)* ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: RotateV last_updated: 2026-06-29T19:34:08Z inherits: - DynamicRotate - JointInstance - Instance - Object type: class memory_category: BaseParts tags: - Deprecated --- # Class: RotateV ## Description A **RotateV** object joins two parts together and allows rotation about a set axis. This object is most commonly created by the [SurfaceType.Motor](/docs/reference/engine/enums/SurfaceType.md) on a [BasePart](/docs/reference/engine/classes/BasePart.md). If created through a script, behavior is still governed by the surface input of [JointInstance.Part0](/docs/reference/engine/classes/JointInstance.md). The three inputs of note are as follows: - `NoInput` — The joint will not rotate under its own power. It can still be rotated by external forces (such as from a character pushing one of the parts). - `Constant` — The joint will rotate based on the ParamB property of [JointInstance.Part0](/docs/reference/engine/classes/JointInstance.md). This rotation is measured in radians per physics frame (which is approximately 1/60th of a second). - `Sin` — The joint will rotate based on the `ParamA` and `ParamB` properties of [JointInstance.Part0](/docs/reference/engine/classes/JointInstance.md). ## Inherited Members ### From [DynamicRotate](/docs/reference/engine/classes/DynamicRotate.md) - **Property `BaseAngle`** (`float`): The base angle of the DynamicRotate object, in radians. ### From [JointInstance](/docs/reference/engine/classes/JointInstance.md) - **Property `Active`** (`boolean`): Determines if the joint is currently active in the world. - **Property `C0`** (`CFrame`): Determines how the offset point is attached to - **Property `C1`** (`CFrame`): Subtracted from the C0 property to create an - **Property `Enabled`** (`boolean`): Sets whether the joint is active or not. - **Property `Part0`** (`BasePart`): The first BasePart that the joint connects. - **Property `Part1`** (`BasePart`): The second BasePart that the joint connects. - **Property `part1`** (`BasePart`): *(deprecated, hidden)* ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: RotationCurve last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances summary: "Represents a sequence of rotations and the interpolation curve between them." --- # Class: RotationCurve > Represents a sequence of rotations and the interpolation curve between them. ## Description This class holds a sorted list of [RotationCurveKeys](/docs/reference/engine/datatypes/RotationCurveKey.md) that represent a sequence of rotations. The shape of the interpolation curve between two keys is determined by the [RotationCurveKey.Interpolation](/docs/reference/engine/datatypes/RotationCurveKey.md) type. ## Properties ### Property: RotationCurve.Length ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` Number of rotation keys in this curve. ## Methods ### Method: RotationCurve:GetKeyAtIndex **Signature:** `RotationCurve:GetKeyAtIndex(index: int): RotationCurveKey` Returns a copy of a key at a given index. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `index` | `int` | | | **Returns:** `RotationCurveKey` ### Method: RotationCurve:GetKeyIndicesAtTime **Signature:** `RotationCurve:GetKeyIndicesAtTime(time: float): Array` The first item in the returned array is the index of the last key with time less than or equal to `time` (or the lesser of either 1 or the curve length if no key was found). The second item in the returned array is the index of the first key with time greater than or equal to `time` (or the curve length if no key was found satisfying the inequality). *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `time` | `float` | | | **Returns:** `Array` ### Method: RotationCurve:GetKeys **Signature:** `RotationCurve:GetKeys(): Array` Returns a copy of all the keys in the rotation curve as a Luau array of [RotationCurveKeys](/docs/reference/engine/datatypes/RotationCurveKey.md). *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Returns:** `Array` — Array of [RotationCurveKeys](/docs/reference/engine/datatypes/RotationCurveKey.md). ### Method: RotationCurve:GetValueAtTime **Signature:** `RotationCurve:GetValueAtTime(time: float): CFrame?` Samples the rotation curve at a given time and returns the corresponding rotation as a [CFrame](/docs/reference/engine/datatypes/CFrame.md). Empty rotation curves are evaluated as [CFrame.identity](/docs/reference/engine/datatypes/CFrame.md). *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `time` | `float` | | Time at which to sample the curve. | **Returns:** `CFrame?` — Value of the curve at the requested `time`. ### Method: RotationCurve:InsertKey **Signature:** `RotationCurve:InsertKey(key: RotationCurveKey): Array` Adds the key passed as an argument to this curve. If a key at the same time is found, it will be replaced. In the returned array, the first value is `true` if a key was added or `false` if a previous key was replaced; the second value is the index at which the marker was added. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `key` | `RotationCurveKey` | | [RotationCurveKey](/docs/reference/engine/datatypes/RotationCurveKey.md) to insert. | **Returns:** `Array` — (see description) . ### Method: RotationCurve:RemoveKeyAtIndex **Signature:** `RotationCurve:RemoveKeyAtIndex(startingIndex: int, count?: int): int` Removes a given number (`count`) of keys starting from the `startingIndex` index and returns the number of keys that were removed. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `startingIndex` | `int` | | Starting index from which to remove keys. | | `count` | `int` | `1` | Number of keys to remove. | **Returns:** `int` — Number of keys removed. ### Method: RotationCurve:SetKeys **Signature:** `RotationCurve:SetKeys(keys: Array): int` Resets this curve's keys using the [RotationCurveKey](/docs/reference/engine/datatypes/RotationCurveKey.md) array passed as an argument. Keys in the `keys` array are sorted in ascending time order before insertion, and keys at duplicated times are removed in a stable manner. Returns the number of keys actually inserted. Keys previously stored in this curve are removed before the keys passed as arguments are added. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `keys` | `Array` | | Array of [RotationCurveKeys](/docs/reference/engine/datatypes/RotationCurveKey.md). | **Returns:** `int` — Number of keys inserted. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: RunService last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated summary: "Service responsible for all runtime activity and progression of time." --- # Class: RunService > Service responsible for all runtime activity and progression of time. ## Description `RunService` contains methods and events for time management as well as for managing the context in which an experience or script is running. Methods like [IsClient()](/docs/reference/engine/classes/RunService.md), [IsServer()](/docs/reference/engine/classes/RunService.md), and [IsStudio()](/docs/reference/engine/classes/RunService.md) can help you determine under what context code is running. These methods are useful for [ModuleScripts](/docs/reference/engine/classes/ModuleScript.md) that may be required by both client and server scripts. Furthermore, [IsStudio()](/docs/reference/engine/classes/RunService.md) can be used to add special behaviors for in‑Studio testing. `RunService` also houses events that allow your code to adhere to the engine's frame‑by‑frame loop, such as [PreRender](/docs/reference/engine/classes/RunService.md), [PreAnimation](/docs/reference/engine/classes/RunService.md), [PreSimulation](/docs/reference/engine/classes/RunService.md), [PostSimulation](/docs/reference/engine/classes/RunService.md), and [Heartbeat](/docs/reference/engine/classes/RunService.md). Selecting the proper event to use for any case is important, so you should read [Task Scheduler](/docs/en-us/performance-optimization/microprofiler/task-scheduler.md) to make an informed decision. ##### Context Test Results | Environment | [IsStudio](/docs/reference/engine/classes/RunService.md) | [IsClient](/docs/reference/engine/classes/RunService.md) | [IsServer](/docs/reference/engine/classes/RunService.md) | [IsEdit](/docs/reference/engine/classes/RunService.md) | [IsRunning](/docs/reference/engine/classes/RunService.md) | [IsRunMode](/docs/reference/engine/classes/RunService.md) | | --- | --- | --- | --- | --- | --- | --- | | Live (Client) | `false` | `true` | `false` | | `true` | `false` | | Live (Server) | `false` | `false` | `true` | | `true` | `false` | | Edit | `true` | `true` | `true` | `true` | `false` | `false` | | Collaborative Edit | `true` | `true` | `false` | `true` | `false` | `false` | | Run Mode | `true` | `false` | `true` | | `true` | `true` | | Play Mode (Client) | `true` | `true` | `false` | | `true` | `false` | | Play Mode (Server) | `true` | `false` | `true` | | `true` | `false` | | Team Test (Client) | `true` | `true` | `false` | | `true` | `false` | | Team Test (Server) | `true` | `false` | `true` | | `true` | `false` | | Luau Execution | `false` | `false` | `true` | | `false` | `false` | ## Properties ### Property: RunService.ClientGitHash ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` ### Property: RunService.RunState ```json { "type": "RunState", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` ## Methods ### Method: RunService:BindToRenderStep **Signature:** `RunService:BindToRenderStep(name: string, priority: int, function: Function): ()` The `BindToRenderStep()` function binds a custom function to be called at a specific time during the render step. There are three main arguments: `name`, `priority`, and what `function` to call. As it is linked to the client's rendering process, `BindToRenderStep()` can only be called on the client. ##### Name The `name` parameter is a label for the binding and can be used with [RunService:UnbindFromRenderStep()](/docs/reference/engine/classes/RunService.md) if the binding is no longer needed. ```lua local RunService = game:GetService("RunService") local function functionToBind() end -- Bind the function above to the binding named "tempBinding" RunService:BindToRenderStep("tempBinding", 1, functionToBind) -- Unbind the function bound to "tempBinding" RunService:UnbindFromRenderStep("tempBinding") ``` ##### Priority The `priority` of the binding is an integer; it determines when during the render step to call the custom function. The lower this number, the sooner the custom function will be called. If two bindings have the same priority, the engine will randomly pick one to run first. The default control scripts run with these specific priorities: - Player Input: `100` - Camera Controls: `200` For convenience; the [RenderPriority](/docs/reference/engine/enums/RenderPriority.md) enum can be used to determine the integer value to set a binding. For example, to make a binding right before the default camera update, simply subtract `1` from the camera priority level. When using [RenderPriority](/docs/reference/engine/enums/RenderPriority.md), remember to use `.Value` at the end of the desired enum. [RunService:BindToRenderStep()](/docs/reference/engine/classes/RunService.md) will not work if just the enum is used on its own. ```lua local RunService = game:GetService("RunService") local function beforeCamera(delta) -- Code in here will run before the default camera script end RunService:BindToRenderStep("Before camera", Enum.RenderPriority.Camera.Value - 1, beforeCamera) ``` ##### Custom Function and Delta Time The last argument (`function`) is the custom function to call. This function will be passed one parameter called `deltaTime` which shows how much time passed between the beginning of the previous render step and the beginning of the current render step. All rendering updates will wait until the code in the render step finishes. Make sure that any code called by `BindToRenderStep()` runs quickly and efficiently; if code takes too long, the experience visuals will be choppy. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name` | `string` | | Label for the binding which can be used with [Unbind](/docs/reference/engine/classes/RunService.md) if the binding is no longer needed. | | `priority` | `int` | | Priority of the binding as an integer; it determines when during the render step to call the custom function. The lower this number, the sooner the custom function will be called. If two bindings have the same priority, the engine will randomly pick one to run first. | | `function` | `Function` | | The custom function being bound. | **Returns:** `()` **Frame Moving in Circle** This code sample moves a GuiObject in a circle within its parent object using RunService's BindToRenderStep. It defines a parametric equation in a function to help with positioning the GuiObject. To try this code out, put a ScreenGui in the StarterGui. Inside the ScreenGui, insert a Frame with a LocalScript. Paste this code into the LocalScript, then play the game. Watch the Frame travel counterclockwise within. ```lua local RunService = game:GetService("RunService") -- How fast the frame ought to move local SPEED = 2 local frame = script.Parent frame.AnchorPoint = Vector2.new(0.5, 0.5) -- A simple parametric equation of a circle -- centered at (0.5, 0.5) with radius (0.5) local function circle(t) return 0.5 + math.cos(t) * 0.5, 0.5 + math.sin(t) * 0.5 end -- Keep track of the current time local currentTime = 0 local function onRenderStep(deltaTime) -- Update the current time currentTime = currentTime + deltaTime * SPEED -- ...and the frame's position local x, y = circle(currentTime) frame.Position = UDim2.new(x, 0, y, 0) end -- This is just a visual effect, so use the "Last" priority RunService:BindToRenderStep("FrameCircle", Enum.RenderPriority.Last.Value, onRenderStep) --RunService.RenderStepped:Connect(onRenderStep) -- Also works, but not recommended ``` **RunService Custom Function** This example shows how to bind a simple function to the render step. All this function does is print how much time passed between the last render step and the current one. Note that this code will need to be in a `LocalScript` to run. ```lua local RunService = game:GetService("RunService") local function checkDelta(deltaTime) print("Time since last render step:", deltaTime) end RunService:BindToRenderStep("Check delta", Enum.RenderPriority.First.Value, checkDelta) ``` **Bind and Unbind a Function** This example uses the `RunService` to bind and unbind a function named `printHello`. First, we bind the function to the RenderStep so that fires every _step_. Then, after we wait 5 seconds (`wait(5)`), we unbind the function. ```lua local RunService = game:GetService("RunService") -- Step 1: Declare the function and a name local NAME = "Print Hello" local function printHello() print("Hello") end -- Step 2: Bind the function RunService:BindToRenderStep(NAME, Enum.RenderPriority.First.Value, printHello) -- Step 3: Unbind the function RunService:UnbindFromRenderStep(NAME) ``` ### Method: RunService:BindToSimulation **Signature:** `RunService:BindToSimulation(function: Function, frequency?: StepFrequency, priority?: int): RBXScriptConnection` `BindToSimulation()` binds a custom function to be called at a fixed frequency which is independent of the frame rate. In the server authority model, bound functions will also get called when the client needs to resimulate. This method is only available when [Workspace.UseFixedSimulation](/docs/reference/engine/classes/Workspace.md) is enabled. Note that since the bound function is meant for physics controllers and prediction, an error will trigger if you read or write from unsynchronized properties or call an unsynchronized method. This error is meant to help guide you in writing a pure, synchronized simulation. To synchronize inaccessible properties, you should store the relevant data in [attributes](/docs/en-us/studio/properties.md#instance-attributes) and update the relevant property in [PostSimulation](/docs/reference/engine/classes/RunService.md) or [RenderStepped](/docs/reference/engine/classes/RunService.md) by reading from attributes. This will leverage attribute synchronization to keep the property fully synchronized through the rollback mechanism. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `function` | `Function` | | The function to call. This function will be passed one parameter called `deltaTime` which shows how much time passed between the beginning of the previous simulation step and the beginning of the current simulation step. | | `frequency` | `StepFrequency` | `Hz30` | Optional [StepFrequency](/docs/reference/engine/enums/StepFrequency.md) value indicating the frequency at which to call the bound function. If not provided, the default frequency will be used. | | `priority` | `int` | `2000` | Optional priority of the binding as an integer; it determines the order in which bound functions are called within a simulation step. Lower numbers are called first. If two bindings have the same priority, the order between them is unspecified. Defaults to 2000. | **Returns:** `RBXScriptConnection` **Synchronizing a Part's Color Property** ```lua local RunService = game:GetService("RunService") local Workspace = game:GetService("Workspace") local somePart:BasePart = Workspace:WaitForChild("Part") RunService:BindToSimulation(function(deltaTime: number) local syncdColor = Color3.fromHSV(math.fmod(time(), 1.0), 1.0, 1.0) somePart:SetAttribute("SyncdColor", syncdColor) end) RunService.RenderStepped:Connect(function(deltaTime: number) local syncdColor = somePart:GetAttribute("SyncdColor") somePart.Color = syncdColor end) ``` ### Method: RunService:GetPredictionStatus **Signature:** `RunService:GetPredictionStatus(context: Instance): PredictionStatus` Clients can use this method to check the [PredictionStatus](/docs/reference/engine/enums/PredictionStatus.md) of a specific context instance, useful for debugging scripts affecting multiple instances (vehicle controllers, custom physics, etc.) where some might be predicted and others might not. This method is also useful for debugging and observing the effects of [PredictionMode.Automatic](/docs/reference/engine/enums/PredictionMode.md) prediction mode. *Security: None · Thread Safety: Unsafe · Capabilities: Basic · Simulation Access: true* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `context` | `Instance` | | The [Instance](/docs/reference/engine/classes/Instance.md) for which to check prediction status. | **Returns:** `PredictionStatus` ### Method: RunService:IsClient **Signature:** `RunService:IsClient(): boolean` If the code that invoked this method is running in a client context (in a [LocalScript](/docs/reference/engine/classes/LocalScript.md), in a [ModuleScript](/docs/reference/engine/classes/ModuleScript.md) required by a [LocalScript](/docs/reference/engine/classes/LocalScript.md), or in a [Script](/docs/reference/engine/classes/Script.md) with [RunContext](/docs/reference/engine/classes/BaseScript.md) set to [RunContext.Client](/docs/reference/engine/enums/RunContext.md)), this method will return `true`. In all other cases, this method will return `false`. If this method returns `true`, the current environment can access client‑only features like [RunService.PreRender](/docs/reference/engine/classes/RunService.md) or [Players.LocalPlayer](/docs/reference/engine/classes/Players.md). *Security: None · Thread Safety: Safe · Capabilities: Basic* **Returns:** `boolean` — Whether the current environment is running the client. ### Method: RunService:IsEdit **Signature:** `RunService:IsEdit(): boolean` This method returns whether the current environment is in "edit" mode, for example in Studio when the experience is not running. `IsEdit()` will return the inverse of [IsRunning()](/docs/reference/engine/classes/RunService.md), except when the simulation has been paused, in which case both methods will return `false`. *Security: PluginSecurity · Thread Safety: Safe · Capabilities: Basic* **Returns:** `boolean` — Whether the current environment is in "edit" mode. ### Method: RunService:IsRunMode **Signature:** `RunService:IsRunMode(): boolean` This method returns whether a **Run** playtest has been initiated in Studio. It will continue to return `true` if the simulation has been paused using the **Pause** button; however, once it has been stopped using the **Stop** button, it will revert to returning `false`. Note that Studio only enters "run" mode when a **Run** playtest (without the user's player character) is initiated. Also note that this method will return `false` if the simulation was started using [RunService:Run()](/docs/reference/engine/classes/RunService.md). *Security: None · Thread Safety: Safe · Capabilities: Basic* **Returns:** `boolean` — Whether a **Run** playtest has been initiated in Studio. ### Method: RunService:IsRunning **Signature:** `RunService:IsRunning(): boolean` Returns whether the experience is currently running. `IsRunning()` will always return the inverse of [IsEdit()](/docs/reference/engine/classes/RunService.md) except when the simulation has been paused, in which case both methods will return `false`. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Returns:** `boolean` — Whether the experience is currently running. ### Method: RunService:IsServer **Signature:** `RunService:IsServer(): boolean` This method returns whether the current environment is running on the server. If the code that invoked this method is running in a server context (in a [Script](/docs/reference/engine/classes/Script.md) with [RunContext](/docs/reference/engine/classes/BaseScript.md) set to [RunContext.Server](/docs/reference/engine/enums/RunContext.md) or [RunContext.Legacy](/docs/reference/engine/enums/RunContext.md), or in a [ModuleScript](/docs/reference/engine/classes/ModuleScript.md) required by a [Script](/docs/reference/engine/classes/Script.md)), this method will return `true`. In all other cases, this method will return `false`. If this function returns true, then the current environment can access server‑only features like [ServerStorage](/docs/reference/engine/classes/ServerStorage.md) or [ServerScriptService](/docs/reference/engine/classes/ServerScriptService.md). *Security: None · Thread Safety: Safe · Capabilities: Basic* **Returns:** `boolean` — Whether the current environment is running on the server. ### Method: RunService:IsStudio **Signature:** `RunService:IsStudio(): boolean` This method returns whether the current environment is running in Studio. It can be used to wrap code that should only execute when testing in Studio. *Security: None · Thread Safety: Safe · Capabilities: Basic* **Returns:** `boolean` — Whether the current environment is running in Studio. ### Method: RunService:Pause **Signature:** `RunService:Pause(): ()` This method pauses the experience's simulation if it is running, suspending physics and scripts. When the simulation is paused, [IsRunning()](/docs/reference/engine/classes/RunService.md) will return `false`. *Security: PluginSecurity · Thread Safety: Unsafe · Capabilities: Basic* **Returns:** `()` ### Method: RunService:Run **Signature:** `RunService:Run(): ()` This method runs the experience's simulation (physics and scripts). When the simulation is running, [IsRunning()](/docs/reference/engine/classes/RunService.md) will return `true`. However, [IsRunMode()](/docs/reference/engine/classes/RunService.md) will only return `true` if the simulation was started using the **Run** button in Studio. *Security: PluginSecurity · Thread Safety: Unsafe · Capabilities: Basic* **Returns:** `()` ### Method: RunService:SetPredictionMode **Signature:** `RunService:SetPredictionMode(context: Instance, mode: PredictionMode): ()` Sets the prediction mode for an [Instance](/docs/reference/engine/classes/Instance.md) (`context`) to an [PredictionMode](/docs/reference/engine/enums/PredictionMode.md) value. Mismatches between the physics properties or attributes for predicted instances will cause a rollback and resimulation on the client. Can only be called on the client. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `context` | `Instance` | | The [Instance](/docs/reference/engine/classes/Instance.md) for which to set the prediction mode. | | `mode` | `PredictionMode` | | The [PredictionMode](/docs/reference/engine/enums/PredictionMode.md) to set for the context instance. | **Returns:** `()` ### Method: RunService:Stop **Signature:** `RunService:Stop(): ()` This method stops the experience's simulation if it is running. When the simulation is stopped, [IsRunning()](/docs/reference/engine/classes/RunService.md) will return `false` and [IsEdit()](/docs/reference/engine/classes/RunService.md) will return `true`. In contrast to the **Stop** button in Studio, calling this method will not restore the experience to the state it was in prior to the simulation being run. This means any changes made to the experience by the physics simulation and scripts will persist after the simulation has ended. *Security: PluginSecurity · Thread Safety: Unsafe · Capabilities: Basic* **Returns:** `()` ### Method: RunService:UnbindFromRenderStep **Signature:** `RunService:UnbindFromRenderStep(name: string): ()` Given a name of a function sent to [BindToRenderStep()](/docs/reference/engine/classes/RunService.md), this method will unbind the function from being called during [PreRender](/docs/reference/engine/classes/RunService.md). This is used to unbind bound functions once they are no longer needed, or when they no longer need to fire every step. If there is no bound function by the given name, this method takes no action and continues without raising an error. *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name` | `string` | | The name of the function being unbound. | **Returns:** `()` ### Method: RunService:Reset **Signature:** `RunService:Reset(): ()` > **Deprecated:** This item is deprecated and should not be used in new work. The Reset function resets the current game to a waypoint set when Run was called. This method should only be used after Run was called. *Security: PluginSecurity · Thread Safety: Unsafe · Capabilities: Basic* **Returns:** `()` ## Events ### Event: RunService.Heartbeat **Signature:** `RunService.Heartbeat(deltaTime: double)` The `Heartbeat` event fires every frame, after the physics simulation has completed. The `deltaTime` argument indicates the time that has elapsed since the previous frame. This event is when most scripts run. It occurs at the end of each frame and it's also when any waiting scripts are executed, such as those scheduled with the [task](/docs/reference/engine/globals/task.md) library. `Heartbeat` is commonly used for periodic tasks, such as updating core game systems like health regeneration. Following this step, the engine sends property updates and events to the server or clients which are later received as part of the [replication](/docs/en-us/projects/client-server.md#replication) receive step. *Security: None · Capabilities: Basic* **Parameters:** | Name | Type | Description | |------|------|-------------| | `deltaTime` | `double` | The time (in seconds) that has elapsed since the previous frame. | ### Event: RunService.Misprediction **Signature:** `RunService.Misprediction(time: double, instances: Array, stats: Dictionary)` With server authority enabled, the Misprediction event fires when a received server state differs from the predicted state. Use this event to build debugging tools that inspect misprediction data programmatically. For example, a plugin could listen for this event and track which instances are frequently mispredicting, what properties are diverging, and by how much. ##### Instances Array Each entry in the `instances` array is a table with the following structure: | Field | Type | Description | | --- | --- | --- | | `Instance` | `Instance` | The instance that was mispredicted. | | `Properties` | `dictionary?` | Properties that were mispredicted. Each key is a property name mapping to a table with the client's `Predicted` and owner's/server's `Authoritative` values as captured on the step when the client first mispredicted the owner's value. Only present if there are property mismatches. | | `Attributes` | `dictionary?` | Attributes that were mispredicted. Each key is a property name mapping to a table with the client's `Predicted` and owner's/server's `Authoritative` values as captured on the step when the client first mispredicted the owner's value. Only present if there are attribute mismatches. | ##### Stats Dictionary The `stats` dictionary contains the following fields: | Field | Type | Description | | --- | --- | --- | | `ResimulationTime` | number | The time spent during resimulation (or the amount of time rolled back). | ##### Code Sample ```lua local RunService = game:GetService("RunService") RunService.Misprediction:Connect(function(time, instances, stats) print(`Misprediction at t={time}, resim={stats.ResimulationTime}s`) for _, entry in instances do print(` Instance: {entry.Instance:GetFullName()}`) if entry.Properties then for name, values in entry.Properties do print(` Property '{name}': predicted={values.Predicted}, authoritative={values.Authoritative}`) end end if entry.Attributes then for name, values in entry.Attributes do print(` Attribute '{name}': predicted={values.Predicted}, authoritative={values.Authoritative}`) end end end end) ``` *Security: None · Capabilities: Basic, PluginOrOpenCloud* **Parameters:** | Name | Type | Description | |------|------|-------------| | `time` | `double` | The time (in seconds) from the start of simulation at which the client's predicted state first diverged from the server's authoritative state. | | `instances` | `Array` | An array of tables, each describing an [Instance](/docs/reference/engine/classes/Instance.md) that was mispredicted. See below for the structure of each entry. | | `stats` | `Dictionary` | A dictionary of statistics about the misprediction. See below for the structure. | ### Event: RunService.PostSimulation **Signature:** `RunService.PostSimulation(deltaTimeSim: double)` The `PostSimulation` event fires every frame, after the physics simulation has completed. The `deltaTimeSim` argument indicates the time that the current frame has stepped the physics simulation, not accounting for physics throttling. This may deviate from the actual time between frames in Studio edit mode or when framerate is very low. This event is useful for making final adjustments to the outcome of the simulation. Following this phase, the engine triggers the [Heartbeat](/docs/reference/engine/classes/RunService.md) event. *Security: None · Capabilities: Basic* **Parameters:** | Name | Type | Description | |------|------|-------------| | `deltaTimeSim` | `double` | The time (in seconds) that the current frame has stepped the physics simulation, not accounting for physics throttling. | ### Event: RunService.PreAnimation **Signature:** `RunService.PreAnimation(deltaTimeSim: double)` The `PreAnimation` event fires every frame, prior to the physics simulation but after rendering. The `deltaTimeSim` argument indicates the time that the current frame has stepped animations. This event is useful for modifying animation objects, such as adjusting their speed or priority. Once the `PreAnimation` event is complete, the engine proceeds to run these animations, updating the joint transforms which will later be used to update objects during the physics simulation. After animations are stepped, the engine triggers the [PreSimulation](/docs/reference/engine/classes/RunService.md) event. *Security: None · Capabilities: Basic* **Parameters:** | Name | Type | Description | |------|------|-------------| | `deltaTimeSim` | `double` | The time (in seconds) that the current frame has stepped animations. | ### Event: RunService.PreRender **Signature:** `RunService.PreRender(deltaTimeRender: double)` The `PreRender` event (replacement for [RenderStepped](/docs/reference/engine/classes/RunService.md)) fires every frame, prior to the frame being rendered. The `deltaTimeRender` argument indicates the time that has elapsed since the previous frame. This event allows you to run code and update the world before it's drawn on a player's screen. This is useful for last‑minute adjustments such as changing object positions, updating animations, or preparing visual effects, but it should be used sparingly as the engine cannot start to render the frame until code running in this event has finished executing. As `PreRender` is client-side, it can only be used in a [LocalScript](/docs/reference/engine/classes/LocalScript.md), in a [ModuleScript](/docs/reference/engine/classes/ModuleScript.md) required by a [LocalScript](/docs/reference/engine/classes/LocalScript.md), or in a [Script](/docs/reference/engine/classes/Script.md) with [RunContext](/docs/reference/engine/classes/BaseScript.md) set to [RunContext.Client](/docs/reference/engine/enums/RunContext.md). *Security: None · Capabilities: Basic* **Parameters:** | Name | Type | Description | |------|------|-------------| | `deltaTimeRender` | `double` | The time (in seconds) that has elapsed since the previous frame. | ### Event: RunService.PreSimulation **Signature:** `RunService.PreSimulation(deltaTimeSim: double)` The `PreSimulation` event (replacement for [Stepped](/docs/reference/engine/classes/RunService.md)) fires every frame, prior to the physics simulation. The `deltaTimeSim` argument indicates the time that the current frame will step the physics simulation, not accounting for physics throttling. This may deviate from the actual time between frames in Studio edit mode or when framerate is very low. This event is useful for adjusting properties like velocity or forces just before they're applied as part of the simulation. The simulation then runs, potentially multiple times, as the physics solver runs at a higher frequency than other engine systems. Once this is complete, the [PostSimulation](/docs/reference/engine/classes/RunService.md) event is fired. *Security: None · Capabilities: Basic* **Parameters:** | Name | Type | Description | |------|------|-------------| | `deltaTimeSim` | `double` | The time (in seconds) that the current frame will step the physics simulation, not accounting for physics throttling. | ### Event: RunService.RenderStepped **Signature:** `RunService.RenderStepped(deltaTime: double)` Fires every frame, prior to the frame being rendered. ##### Migration Note This event has been superseded by [PreRender](/docs/reference/engine/classes/RunService.md) which should be used for new work. *Security: None · Capabilities: Basic* **Parameters:** | Name | Type | Description | |------|------|-------------| | `deltaTime` | `double` | The time (in seconds) that has elapsed since the previous frame. | ### Event: RunService.Rollback **Signature:** `RunService.Rollback(time: double)` When server authority is enabled, the `Rollback` event fires after the engine detects a misprediction and rolls back all eligible state (properties, attributes, and related physics and animation state), but before the engine resimulates the rolled-back steps. Use this event to update state in your place that wouldn't otherwise be rolled back after a misprediction. For example, if you maintain gameplay state that can't be stored in a property or attribute with simulation access, you can listen for this event, compare the rolled-back-to step against your farthest-reached step, and rewind your custom state accordingly. ```lua local RunService = game:GetService("RunService") local stateHistory = {} -- your custom state tracked per fixed step RunService.Rollback:Connect(function(time) -- Discard any custom state newer than the rolled-back-to step -- and restore the state at that step before resimulation begins for step, _ in stateHistory do if step > time then stateHistory[step] = nil end end end) ``` *Security: None · Capabilities: Basic* **Parameters:** | Name | Type | Description | |------|------|-------------| | `time` | `double` | The time (in seconds) that we are rolling back to. | ### Event: RunService.Stepped **Signature:** `RunService.Stepped(time: double, deltaTime: double)` Fires every frame, prior to the physics simulation. ##### Migration Note This event has been superseded by [PreSimulation](/docs/reference/engine/classes/RunService.md) which should be used for new work. *Security: None · Capabilities: Basic* **Parameters:** | Name | Type | Description | |------|------|-------------| | `time` | `double` | The duration (in seconds) that [RunService](/docs/reference/engine/classes/RunService.md) has been running for. | | `deltaTime` | `double` | The time (in seconds) that has elapsed since the previous frame. | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: RunningAverageItemDouble last_updated: 2026-06-29T19:34:08Z inherits: - StatsItem - Instance - Object type: class memory_category: Instances tags: - NotCreatable summary: "Measures the runtime average of a **double** value." --- # Class: RunningAverageItemDouble > Measures the runtime average of a **double** value. ## Description A special type of [StatsItem](/docs/reference/engine/classes/StatsItem.md) which measures the runtime average of an internal **double** value. ## Inherited Members ### From [StatsItem](/docs/reference/engine/classes/StatsItem.md) - **Property `DisplayName`** (`string`): *(hidden)* - **Method `GetValue(): double`**: Returns the StatsItem's value. - **Method `GetValueString(): string`**: Returns the StatsItem's value as a formatted string. ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: RunningAverageItemInt last_updated: 2026-06-29T19:34:08Z inherits: - StatsItem - Instance - Object type: class memory_category: Instances tags: - NotCreatable summary: "Measures the runtime average of an **integer** value." --- # Class: RunningAverageItemInt > Measures the runtime average of an **integer** value. ## Description A special type of [StatsItem](/docs/reference/engine/classes/StatsItem.md) which measures the runtime average of an internal integer value. As of right now, this StatsItem goes unused. ## Inherited Members ### From [StatsItem](/docs/reference/engine/classes/StatsItem.md) - **Property `DisplayName`** (`string`): *(hidden)* - **Method `GetValue(): double`**: Returns the StatsItem's value. - **Method `GetValueString(): string`**: Returns the StatsItem's value as a formatted string. ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: RunningAverageTimeIntervalItem last_updated: 2026-06-29T19:34:08Z inherits: - StatsItem - Instance - Object type: class memory_category: Instances tags: - NotCreatable summary: "Measures the runtime average of a time interval value." --- # Class: RunningAverageTimeIntervalItem > Measures the runtime average of a time interval value. ## Description A special type of [StatsItem](/docs/reference/engine/classes/StatsItem.md) which measures a runtime average time interval. As of right now, this StatsItem goes unused. ## Inherited Members ### From [StatsItem](/docs/reference/engine/classes/StatsItem.md) - **Property `DisplayName`** (`string`): *(hidden)* - **Method `GetValue(): double`**: Returns the StatsItem's value. - **Method `GetValueString(): string`**: Returns the StatsItem's value as a formatted string. ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: SceneAnalysisService last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated --- # Class: SceneAnalysisService ## Description Provides programmatic access to the [Scene Analysis](/docs/en-us/performance-optimization/scene-analysis.md) tool's underlying performance data. ## Methods ### Method: SceneAnalysisService:GetAnimationMemoryAsync **Signature:** `SceneAnalysisService:GetAnimationMemoryAsync(): AnimationMemoryResult` Returns loaded animation clip memory. The same asset shared by multiple Animators appears only once, with all owners listed. #### Root dictionary | Key | Type | Description | | ---------- | ------ | ---------------------------------------------------------------- | | `Name` | string | Always `"AnimationMemory"`. | | `Size` | number | Total loaded clip memory in bytes. | | `Children` | array | Array of clip entries and optionally an out-of-data model group. | #### Clip entry Each child in `Children` is either a clip entry or the "not in data model" group (see below). | Key | Type | Description | | --------- | ------ | ----------------------------------------------------------- | | `Name` | string | The asset ID (e.g. `"rbxassetid://111"`). | | `Size` | number | Clip memory in bytes. | | `AssetId` | string | The asset ID string (same value as `Name`). | | `Owners` | array | Array of owner entries for all Animators playing this clip. | #### Owner entry | Key | Type | Description | | ----------- | ------ | ---------------------------------------------------------------------------- | | `Name` | string | Full path to the owning Animator (e.g. `"Workspace.NPC.Humanoid.Animator"`). | | `ClassName` | string | Class name of the owner (e.g. `"Animator"`). | #### "Not in data model" group Clips from Animators held only by Lua (not parented in the data model) are collected into a single group entry. | Key | Type | Description | | ---------- | ------ | --------------------------------------------- | | `Name` | string | Always `"Not In DataModel"`. | | `Size` | number | Sum of clip bytes in this group. | | `Children` | array | Array of clip entries (same schema as above). | #### Example ```lua { Name = "AnimationMemory", Size = 1048576, Children = { { Name = "rbxassetid://111", Size = 32768, AssetId = "rbxassetid://111", Owners = { { Name = "Workspace.Player.Humanoid.Animator", ClassName = "Animator" }, }}, { Name = "rbxassetid://444", Size = 65536, AssetId = "rbxassetid://444", Owners = { { Name = "Workspace.NPC1.Humanoid.Animator", ClassName = "Animator" }, { Name = "Workspace.NPC2.Humanoid.Animator", ClassName = "Animator" }, }}, { Name = "Not In DataModel", Size = 32768, Children = { { Name = "rbxassetid://555", Size = 32768, AssetId = "rbxassetid://555", Owners = { { Name = "Animator", ClassName = "Animator" }, }}, }}, } } ``` *Yields · Security: None · Thread Safety: Unsafe* **Returns:** `AnimationMemoryResult` — A dictionary tree of loaded animation clip memory, deduplicated by clip. ### Method: SceneAnalysisService:GetAudioMemoryAsync **Signature:** `SceneAnalysisService:GetAudioMemoryAsync(): AudioMemoryResult` Returns loaded audio asset memory, deduplicated by asset ID. Assets whose owners share the same parent are organized under that parent path — flattened when there is one child, nested when there are several. Assets whose owners span different parents appear as shared entries with an `Owners` array. #### Root dictionary | Key | Type | Description | | ---------- | ------ | ------------------------------------------------------- | | `Name` | string | Always `"AudioMemory"`. | | `Size` | number | Total loaded audio memory in bytes. | | `Children` | array | Mix of flat entries, parent groups, and shared entries. | #### Flat asset entry Emitted when a parent has exactly one audio child. | Key | Type | Description | | --------- | ------ | ---------------------------------------------------- | | `Name` | string | Full instance path (e.g. `"Workspace.Map.BGMusic"`). | | `Size` | number | Asset memory in bytes. | | `AssetId` | string | The audio asset ID. | #### Parent group Emitted when a parent has multiple audio children. | Key | Type | Description | | ---------- | ------ | ------------------------------------------------------------------ | | `Name` | string | Parent path (e.g. `"Workspace.Map.SFX"`). | | `Size` | number | Sum of child asset bytes. | | `Children` | array | Array of flat asset entries (each with `Name`, `Size`, `AssetId`). | #### Shared asset entry Emitted when an asset's owners are under different parents. | Key | Type | Description | | --------- | ------ | ------------------------------------------- | | `Name` | string | The asset ID. | | `Size` | number | Asset memory in bytes. | | `AssetId` | string | The asset ID string (same value as `Name`). | | `Owners` | array | Array of owner entries. | #### Owner entry | Key | Type | Description | | ----------- | ------ | ------------------------------------------------------ | | `Name` | string | Full instance path (e.g. `"Workspace.Zone1.Ambient"`). | | `ClassName` | string | `"Sound"` or `"AudioPlayer"`. | #### Example ```lua { Name = "AudioMemory", Size = 5242880, Children = { -- Single audio under unique parent (flat) { Name = "Workspace.Map.BGMusic", Size = 2097152, AssetId = "rbxassetid://111" }, -- Multiple audio under same parent (grouped) { Name = "Workspace.Map.SFX", Size = 1048576, Children = { { Name = "Workspace.Map.SFX.Explosion", Size = 524288, AssetId = "rbxassetid://222" }, { Name = "Workspace.Map.SFX.Gunshot", Size = 524288, AssetId = "rbxassetid://333" }, }}, -- Shared asset (owners in different locations) { Name = "rbxassetid://444", Size = 2097152, AssetId = "rbxassetid://444", Owners = { { Name = "Workspace.Zone1.Ambient", ClassName = "Sound" }, { Name = "Workspace.Zone2.Ambient", ClassName = "Sound" }, }}, } } ``` *Yields · Security: None · Thread Safety: Unsafe* **Returns:** `AudioMemoryResult` — A dictionary tree of loaded audio asset memory, deduplicated by asset ID. ### Method: SceneAnalysisService:GetInstanceCompositionAsync **Signature:** `SceneAnalysisService:GetInstanceCompositionAsync(): InstanceCompositionResult` Returns instance counts by category and class from the data model tree. Scans descendants of Workspace, Players, Lighting, MaterialService, ReplicatedFirst, ReplicatedStorage, ServerScriptService, ServerStorage, StarterGui, StarterPack, StarterPlayer, Teams, SoundService, and TextChatService. Each instance is classified into one of the categories below based on its class hierarchy. #### Root dictionary | Key | Type | Description | | ---------- | ------ | ------------------------------------------------- | | `Name` | string | Always `"InstanceComposition"`. | | `Size` | number | Total instance count across all scanned services. | | `Children` | array | Array of category entries. | #### Category entry | Key | Type | Description | | ---------- | ------ | --------------------------------- | | `Name` | string | Category name (see table below). | | `Size` | number | Total instances in this category. | | `Children` | array | Array of class entries. | #### Class entry (leaf) | Key | Type | Description | | ------ | ------ | ------------------------------------------------------ | | `Name` | string | The class name (e.g. `"Part"`, `"Script"`, `"Frame"`). | | `Size` | number | Number of instances of this class. | #### Categories | Category | Representative Types | | ------------------ | ------------------------------------------------------------------------------------ | | 3D Objects | BasePart, Model, Camera | | Physics | Attachment, Constraint, JointInstance, WeldConstraint, ControllerManager, SensorBase | | UI | GuiBase, BasePlayerGui, UIBase, ProximityPrompt, ClickDetector, TextChannel | | Lights | Light | | PostProcessing | PostEffect, Highlight, Atmosphere, Sky, Clouds | | Scripts | LuaSourceContainer, BindableEvent, RemoteEvent, RemoteFunction | | Audio | Sound, SoundEffect, AudioPlayer, Wire, SoundGroup, AudioEmitter | | Animation | Animator, AnimationController, Animation, KeyframeSequence, IKControl | | Values | ValueBase | | Character | CharacterAppearance, Humanoid, HumanoidDescription, BaseWrap, FaceControls | | Textures | FaceInstance, SurfaceAppearance, MaterialVariant | | Meshes | DataModelMesh | | Particles | ParticleEmitter, Fire, Trail, Beam, Explosion | | Services & Storage | Player, Folder, Configuration | | Misc | Tool, Backpack, Team, ForceField, PathfindingModifier, PackageLink, HapticEffect | | Unclassified | Anything not matching the above | #### Example ```lua { Name = "InstanceComposition", Size = 12450, Children = { { Name = "3D Objects", Size = 5200, Children = { { Name = "Part", Size = 3100 }, { Name = "MeshPart", Size = 1800 }, { Name = "Model", Size = 300 }, }}, { Name = "UI", Size = 2100, Children = { { Name = "Frame", Size = 900 }, { Name = "TextLabel", Size = 700 }, { Name = "UIListLayout", Size = 500 }, }}, } } ``` *Yields · Security: None · Thread Safety: Unsafe* **Returns:** `InstanceCompositionResult` — A dictionary tree of instance counts grouped by category and class name. ### Method: SceneAnalysisService:GetScriptMemoryAsync **Signature:** `SceneAnalysisService:GetScriptMemoryAsync(): ScriptMemoryResult` Returns per-script Luau VM heap memory usage in bytes. Scripts are grouped first by their containing service (derived from the first segment of the dotted path), then split into `"ModuleScripts"` and `"Scripts"` subgroups based on the instance's class. #### Root dictionary | Key | Type | Description | | ---------- | ------ | ---------------------------------- | | `Name` | string | Always `"ScriptMemory"`. | | `Size` | number | Total Luau VM memory in bytes. | | `Children` | array | Array of service category entries. | #### Service category entry | Key | Type | Description | | ---------- | ------ | ---------------------------------------------------------------------------- | | `Name` | string | Service name (e.g. `"ServerScriptService"`, `"ReplicatedStorage"`). | | `Size` | number | Sum of all script memory in this service, in bytes. | | `Children` | array | One or two script-type group entries (`"ModuleScripts"` and/or `"Scripts"`). | #### Script-type group entry | Key | Type | Description | | ---------- | ------ | -------------------------------------------------- | | `Name` | string | Either `"ModuleScripts"` or `"Scripts"`. | | `Size` | number | Sum of memory for scripts in this group, in bytes. | | `Children` | array | Array of individual script entries. | #### Script entry (leaf) | Key | Type | Description | | ------ | ------ | ---------------------------------------------------------------------- | | `Name` | string | Dotted path to the script (e.g. `"ServerScriptService.Shared.Utils"`). | | `Size` | number | Heap memory used by this script, in bytes. | #### Example ```lua { Name = "ScriptMemory", Size = 8388608, Children = { { Name = "ServerScriptService", Size = 524288, Children = { { Name = "ModuleScripts", Size = 409600, Children = { { Name = "ServerScriptService.Shared.Utils", Size = 204800 }, { Name = "ServerScriptService.Shared.Config", Size = 204800 }, }}, { Name = "Scripts", Size = 114688, Children = { { Name = "ServerScriptService.MainScript", Size = 114688 }, }}, }}, { Name = "ReplicatedStorage", Size = 262144, Children = { { Name = "ModuleScripts", Size = 262144, Children = { { Name = "ReplicatedStorage.SharedModule", Size = 262144 }, }}, }}, } } ``` *Yields · Security: None · Thread Safety: Unsafe* **Returns:** `ScriptMemoryResult` — A dictionary tree of per-script Luau VM heap memory grouped by service and script type. ### Method: SceneAnalysisService:GetTriangleCompositionAsync **Signature:** `SceneAnalysisService:GetTriangleCompositionAsync(): TriangleCompositionResult` Returns triangle and draw call counts by render pass, sourced from `StatsService` performance data. Unlike other queries, this uses a `Sizes` dictionary (with `Triangles` and `Drawcalls` keys) instead of a single `Size` field. Render pass entries with zero triangles and zero draw calls are omitted. #### Root dictionary | Key | Type | Description | | ---------- | ---------- | ---------------------------------------------------------------------- | | `Name` | string | Always `"TriangleComposition"`. | | `Sizes` | dictionary | `{ Triangles: number, Drawcalls: number }` — totals across all passes. | | `Children` | array | Array of render pass entries. | #### Render pass entry (leaf) | Key | Type | Description | | ------- | ---------- | --------------------------------------------------------- | | `Name` | string | Render pass type (see table below). | | `Sizes` | dictionary | `{ Triangles: number, Drawcalls: number }` for this pass. | #### Render pass types | Name | Description | | ------------------ | ------------------------------ | | Opaque | Opaque geometry | | Transparent | Transparent geometry | | Terrain | Voxel/smooth terrain | | Grass | Terrain grass decoration | | UI | Screen-space UI | | Decal | Decal rendering | | Cloud | Volumetric clouds | | GenericPostProcess | Generic post-processing | | SSAO | Screen-space ambient occlusion | | DOF | Depth of field | | Particles | Particle systems | | Sky | Skybox | | Shadows | Shadow map rendering | | Undefined | Uncategorized | #### Example ```lua { Name = "TriangleComposition", Sizes = { Triangles = 245000, Drawcalls = 1200 }, Children = { { Name = "Opaque", Sizes = { Triangles = 180000, Drawcalls = 800 } }, { Name = "Transparent", Sizes = { Triangles = 25000, Drawcalls = 150 } }, { Name = "Shadows", Sizes = { Triangles = 30000, Drawcalls = 200 } }, { Name = "UI", Sizes = { Triangles = 10000, Drawcalls = 50 } }, } } ``` *Yields · Security: None · Thread Safety: Unsafe* **Returns:** `TriangleCompositionResult` — A dictionary tree of triangle and draw call counts broken down by render pass type. ### Method: SceneAnalysisService:GetUnparentedInstancesAsync **Signature:** `SceneAnalysisService:GetUnparentedInstancesAsync(): UnparentedInstancesResult` Returns instances held by Luau but no longer in the data model, grouped by host script. Each unparented instance is traced back to the script that holds the reference. Unparented instances that are flagged for garbage collection but not yet deleted are temporarily shown with a parent of `Unknown`. This is a side effect of how garbage collection is intended to work and not indicative of any problems. #### Root dictionary | Key | Type | Description | | ---------- | ------ | ------------------------------------ | | `Name` | string | Always `"UnparentedInstances"`. | | `Size` | number | Total count of unparented instances. | | `Children` | array | Array of host script entries. | #### Host script entry | Key | Type | Description | | ---------- | ------ | ------------------------------------------------------------------------------------------- | | `Name` | string | Script path (e.g. `"ServerScriptService.Gameplay.Combat"`) or `"(unknown)"` if untraceable. | | `Size` | number | Count of unparented instances held by this script. | | `Children` | array | Array of class entries. | #### Class entry (leaf) | Key | Type | Description | | ------ | ------ | ---------------------------------------------------------------------------- | | `Name` | string | Class name of the unparented instance (e.g. `"Part"`, `"Frame"`, `"Sound"`). | | `Size` | number | Count of unparented instances of this class from this host script. | #### Example ```lua { Name = "UnparentedInstances", Size = 47, Children = { { Name = "ServerScriptService.Gameplay.Combat", Size = 30, Children = { { Name = "Part", Size = 22 }, { Name = "Attachment", Size = 8 }, }}, { Name = "(unknown)", Size = 17, Children = { { Name = "Frame", Size = 12 }, { Name = "Sound", Size = 5 }, }}, } } ``` *Yields · Security: None · Thread Safety: Unsafe* **Returns:** `UnparentedInstancesResult` — A dictionary tree of unparented instances grouped by the host script holding the reference. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ScreenGui last_updated: 2026-06-29T19:34:08Z inherits: - LayerCollector - GuiBase2d - GuiBase - Instance - Object type: class memory_category: Instances summary: "Primary container of on-screen 2D user interface elements." --- # Class: ScreenGui > Primary container of on-screen 2D user interface elements. ## Description [ScreenGui](/docs/reference/engine/classes/ScreenGui.md) is a storage container for 2D [GuiObjects](/docs/reference/engine/classes/GuiObject.md) displayed on the user's screen. A [ScreenGui](/docs/reference/engine/classes/ScreenGui.md) only shows if parented to a player's [PlayerGui](/docs/reference/engine/classes/PlayerGui.md); parenting a [ScreenGui](/docs/reference/engine/classes/ScreenGui.md) to [StarterGui](/docs/reference/engine/classes/StarterGui.md) ensures it clones into each player's [PlayerGui](/docs/reference/engine/classes/PlayerGui.md) when they join the experience and their character first spawns. See [On‑Screen UI Containers](/docs/en-us/ui/on-screen-containers.md) for further details. ![Example ScreenGui with various GuiObject children, including a Frame, TextLabel, TextBox, and ImageButton.](../../../assets/ui/ui-objects/ScreenGui-Example.jpg) For performance improvements, the appearance of a [ScreenGui](/docs/reference/engine/classes/ScreenGui.md) is cached until one of the following events occurs: - A descendant is added to or removed from it. - A property of a descendant changes. - A property of the [ScreenGui](/docs/reference/engine/classes/ScreenGui.md) itself changes. If any of these events occur, the [ScreenGui](/docs/reference/engine/classes/ScreenGui.md) appearance is recomputed on the next frame it gets rendered. ## Properties ### Property: ScreenGui.ClipToDeviceSafeArea ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "UI" ] } ``` If this property is `true`, all [GuiObject](/docs/reference/engine/classes/GuiObject.md) descendants of the [ScreenGui](/docs/reference/engine/classes/ScreenGui.md) will be clipped to the device's safe area (see [ScreenInsets](/docs/reference/engine/enums/ScreenInsets.md)). The default is `true` to maintain backwards compatibility of UI that is intentionally hidden offscreen, such as objects that slide into view from a screen edge when they're needed. ![Mobile device showing UI button clipped by device safe area](../../../assets/engine-api/classes/ScreenGui/ClipToDeviceSafeArea-True.png) If this property is `false`, [GuiObject](/docs/reference/engine/classes/GuiObject.md) descendants will **not** be clipped to the device's safe area and may be obscured by the camera notch or other screen cutouts. ![Mobile device showing UI button overflowing device safe area, obscured by screen camera notch](../../../assets/engine-api/classes/ScreenGui/ClipToDeviceSafeArea-False.png) Note that this property will be ignored if you set [ScreenInsets](/docs/reference/engine/classes/ScreenGui.md) to [None](/docs/reference/engine/enums/ScreenInsets.md), as doing so implies that you intentionally want to disregard the device's safe insets. ### Property: ScreenGui.DisplayOrder ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` This property controls the Z-index order in which multiple [ScreenGui](/docs/reference/engine/classes/ScreenGui.md) containers are drawn. Those with a higher [DisplayOrder](/docs/reference/engine/classes/ScreenGui.md) will be drawn on top of those with a lower value. ### Property: ScreenGui.IgnoreGuiInset ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` If this property is `false` (default), [ScreenInsets](/docs/reference/engine/classes/ScreenGui.md) is set to [CoreUISafeInsets](/docs/reference/engine/enums/ScreenInsets.md), effectively keeping its bounds below the Roblox top bar core UI. If this property is changed to `true` and [ScreenInsets](/docs/reference/engine/classes/ScreenGui.md) is currently set to [CoreUISafeInsets](/docs/reference/engine/enums/ScreenInsets.md), [ScreenInsets](/docs/reference/engine/classes/ScreenGui.md) will be set to [DeviceSafeInsets](/docs/reference/engine/enums/ScreenInsets.md). See [ScreenInsets](/docs/reference/engine/classes/ScreenGui.md) for details on how screen insets affect the contents of a [ScreenGui](/docs/reference/engine/classes/ScreenGui.md). ### Property: ScreenGui.SafeAreaCompatibility ```json { "type": "SafeAreaCompatibility", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "UI" ] } ``` This property specifies whether automatic UI compatibility transformations are applied to descendant "fullscreen" [GuiObjects](/docs/reference/engine/classes/GuiObject.md) of the [ScreenGui](/docs/reference/engine/classes/ScreenGui.md) on displays with screen cutouts. Eligibility occurs if the total area of the descendant [GuiObject](/docs/reference/engine/classes/GuiObject.md) (including any applied border or [UIStroke](/docs/reference/engine/classes/UIStroke.md)) covers the device's safe area both horizontally and vertically. See the [SafeAreaCompatibility](/docs/reference/engine/enums/SafeAreaCompatibility.md) enum reference for details. The default value is [FullscreenExtension](/docs/reference/engine/enums/SafeAreaCompatibility.md) in order to automatically improve the appearance of UI that was authored for screens without any cutouts. However, it's recommended that you avoid fullscreen extensions for new work; instead, use the [ScreenInsets](/docs/reference/engine/classes/ScreenGui.md) property to specify which insets should be respected for different [ScreenGui](/docs/reference/engine/classes/ScreenGui.md) containers. Note that descendant UI objects will continue to be clipped by the device's safe area if [ClipToDeviceSafeArea](/docs/reference/engine/classes/ScreenGui.md) is set to `true`. ### Property: ScreenGui.ScreenInsets ```json { "type": "ScreenInsets", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "UI" ] } ``` This property controls the safe area insets that are applied to the contents of the [ScreenGui](/docs/reference/engine/classes/ScreenGui.md). The default of [CoreUISafeInsets](/docs/reference/engine/enums/ScreenInsets.md) keeps all descendant [GuiObjects](/docs/reference/engine/classes/GuiObject.md) inside the core UI safe area, clear of the Roblox top bar buttons and other screen cutouts like the device's camera notch. ![Mobile device showing UI buttons inside core UI safe area](../../../assets/engine-api/enums/ScreenInsets/CoreUISafeInsets.png) If you set this property to [None](/docs/reference/engine/enums/ScreenInsets.md), UI objects may be obscured behind core UI objects or device cutouts like the camera notch. As a result, you should only use [None](/docs/reference/engine/enums/ScreenInsets.md) for a [ScreenGui](/docs/reference/engine/classes/ScreenGui.md) that contains noninteractive content like background images. See [On-Screen UI Containers](/docs/en-us/ui/on-screen-containers.md#screen-insets) for alternative examples. ## Inherited Members ### From [LayerCollector](/docs/reference/engine/classes/LayerCollector.md) - **Property `Enabled`** (`boolean`): Toggles the visibility of this LayerCollector. - **Property `ResetOnSpawn`** (`boolean`): Determines if the LayerCollector resets (deletes itself and - **Property `ZIndexBehavior`** (`ZIndexBehavior`): Controls how GuiObject.ZIndex behaves on all descendants of this - **Method `GetLayoutNodeTree(): Dictionary`**: *(deprecated)* ### From [GuiBase2d](/docs/reference/engine/classes/GuiBase2d.md) - **Property `AbsolutePosition`** (`Vector2`): Describes the actual screen position of a GuiBase2d element, in - **Property `AbsoluteRotation`** (`float`): Describes the actual screen rotation of a GuiBase2d element, in - **Property `AbsoluteSize`** (`Vector2`): Describes the actual screen size of a GuiBase2d element, in - **Property `AutoLocalize`** (`boolean`): When set to `true`, localization will be applied to this GuiBase2d - **Property `Localize`** (`boolean`): Automatically set to true when a localization table's *(deprecated, hidden)* - **Property `RootLocalizationTable`** (`LocalizationTable`): A reference to a LocalizationTable to be used to apply automated - **Property `SelectionBehaviorDown`** (`SelectionBehavior`): Customizes gamepad selection behavior in the down direction. - **Property `SelectionBehaviorLeft`** (`SelectionBehavior`): Customizes gamepad selection behavior in the left direction. - **Property `SelectionBehaviorRight`** (`SelectionBehavior`): Customizes gamepad selection behavior in the right direction. - **Property `SelectionBehaviorUp`** (`SelectionBehavior`): Customizes gamepad selection behavior in the up direction. - **Property `SelectionGroup`** (`boolean`): Allows customization of gamepad selection movement. - **Event `SelectionChanged`**: Fires when the gamepad selection moves to, leaves, or changes within the ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ScreenshotCapture last_updated: 2026-06-29T19:34:08Z inherits: - Capture - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated summary: "A child class of Capture for screenshots." --- # Class: ScreenshotCapture > A child class of [Capture](/docs/reference/engine/classes/Capture.md) for screenshots. ## Inherited Members ### From [Capture](/docs/reference/engine/classes/Capture.md) - **Property `CaptureTime`** (`DateTime`): - **Property `CaptureType`** (`CaptureType`): - **Property `FilePathString`** (`string`): - **Property `LocalId`** (`string`): - **Property `SourcePlaceId`** (`int64`): - **Property `SourceUniverseId`** (`int64`): ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ScreenshotHud last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated summary: "A 2D user interface that allows users to capture and save screenshots to their local device." --- # Class: ScreenshotHud > A 2D user interface that allows users to capture and save screenshots to their > local device. ## Description The [ScreenshotHud](/docs/reference/engine/classes/ScreenshotHud.md) is a 2D user interface that allows users to capture and save screenshots to their local device. It consists of the following UI elements: - An overlay containing the experience name and Roblox branding. These remain on screen when a screenshot is taken, although the experience name can be disabled through the [ScreenshotHud.ExperienceNameOverlayEnabled](/docs/reference/engine/classes/ScreenshotHud.md) property. - A camera button that hides all UI except for the overlay and takes a screenshot. - A close button that closes the [ScreenshotHud](/docs/reference/engine/classes/ScreenshotHud.md). ## Code Samples **LocalScript** ```lua local GuiService = game:GetService("GuiService") local screenshotHud = GuiService:WaitForChild("ScreenshotHud") screenshotHud.ExperienceNameOverlayEnabled = true screenshotHud.OverlayFont = Enum.Font.GothamMedium screenshotHud.Visible = true ``` ## Properties ### Property: ScreenshotHud.CameraButtonIcon ```json { "type": "ContentId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "UI" ] } ``` Asset ID of the icon used for the camera button. ### Property: ScreenshotHud.CameraButtonPosition ```json { "type": "UDim2", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "UI" ] } ``` Screen location of the camera button. ### Property: ScreenshotHud.CloseButtonPosition ```json { "type": "UDim2", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "UI" ] } ``` Screen location of the close button. ### Property: ScreenshotHud.CloseWhenScreenshotTaken ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` Determines whether the [ScreenshotHud](/docs/reference/engine/classes/ScreenshotHud.md) closes automatically when a screenshot is taken. ### Property: ScreenshotHud.HideCoreGuiForCaptures ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` ### Property: ScreenshotHud.HidePlayerGuiForCaptures ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` ### Property: ScreenshotHud.Visible ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "UI" ] } ``` Determines whether the [ScreenshotHud](/docs/reference/engine/classes/ScreenshotHud.md) is visible. ### Property: ScreenshotHud.ExperienceNameOverlayEnabled *(hidden)* ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "UI" ] } ``` Determines whether the experience name in the overlay is enabled. ### Property: ScreenshotHud.OverlayFont *(hidden)* ```json { "type": "Enum.Font", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "UI" ] } ``` The [Font](/docs/reference/engine/datatypes/Font.md) used for the experience name in the overlay. ### Property: ScreenshotHud.UsernameOverlayEnabled *(hidden)* ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "UI" ] } ``` Currently disabled with no effect. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Script last_updated: 2026-06-29T19:34:08Z inherits: - BaseScript - LuaSourceContainer - Instance - Object type: class memory_category: Script summary: "An object that contains and runs Luau code on the server." --- # Class: Script > An object that contains and runs Luau code on the server. ## Description A [Script](/docs/reference/engine/classes/Script.md) is a Luau source container that can access server-side objects, properties, and events, such as to award badges to players using [BadgeService](/docs/reference/engine/classes/BadgeService.md), while [LocalScripts](/docs/reference/engine/classes/LocalScript.md) on the client cannot. The instant that the following conditions are met, a script's code is run in a new thread: - Its [Enabled](/docs/reference/engine/classes/Script.md) property is `true`. - The [Script](/docs/reference/engine/classes/Script.md) object is a descendant of the [Workspace](/docs/reference/engine/classes/Workspace.md) or [ServerScriptService](/docs/reference/engine/classes/ServerScriptService.md). The script will continue to run until the above conditions are **not** met, it terminates, or it raises an error (unless that error is raised by a function connected to some event that is firing). Additionally, the thread will be stopped if the script or one of its ancestors is destroyed. A script will continue to run even if the [Parent](/docs/reference/engine/classes/Instance.md) property is set to `nil` and the [Script](/docs/reference/engine/classes/Script.md) is not destroyed. ## Properties ### Property: Script.Source ```json { "type": "ProtectedString", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "PluginOrOpenCloud" ] } ``` Represents the code to be executed. It's protected and discouraged for editing directly. Attempting to access this property in a [Script](/docs/reference/engine/classes/Script.md) or [LocalScript](/docs/reference/engine/classes/LocalScript.md) causes errors. If you want to read or modify the source of a script that a user has open, use [ScriptEditorService](/docs/reference/engine/classes/ScriptEditorService.md) to interact with the [Script Editor](/docs/en-us/studio/script-editor.md) rather than directly modifying this property. Both [ScriptEditorService:UpdateSourceAsync()](/docs/reference/engine/classes/ScriptEditorService.md) and [ScriptEditorService:GetEditorSource()](/docs/reference/engine/classes/ScriptEditorService.md) can read or modify script content from the script editor if the script is opened. You can also read the source from the [command line](/docs/en-us/studio/ui-overview.md#command-bar). ## Inherited Members ### From [BaseScript](/docs/reference/engine/classes/BaseScript.md) - **Property `Disabled`** (`boolean`): Determines whether a BaseScript will run or not. - **Property `Enabled`** (`boolean`): Determines whether a BaseScript will run or not. - **Property `LinkedSource`** (`ContentId`): The content ID of an uploaded script. When set binds the uploaded code to *(deprecated)* - **Property `RunContext`** (`RunContext`): Determines the context under which the script will run. ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ScriptBuilder last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated --- # Class: ScriptBuilder ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ScriptContext last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated --- # Class: ScriptContext ## Description This service controls all [BaseScript](/docs/reference/engine/classes/BaseScript.md) objects. Most of the properties and methods of this service are locked for internal use. ## Methods ### Method: ScriptContext:EnableCoverage **Signature:** `ScriptContext:EnableCoverage(instance: Instance): ()` *Security: None · Thread Safety: Unsafe · Capabilities: PluginOrOpenCloud* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `instance` | `Instance` | | | **Returns:** `()` ### Method: ScriptContext:GetCoverageStats **Signature:** `ScriptContext:GetCoverageStats(): Array` *Security: None · Thread Safety: Unsafe · Capabilities: PluginOrOpenCloud* **Returns:** `Array` ### Method: ScriptContext:SetTimeout **Signature:** `ScriptContext:SetTimeout(seconds: double): ()` Limits how long a script is allowed to run without yielding. *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `seconds` | `double` | | | **Returns:** `()` ## Events ### Event: ScriptContext.Error **Signature:** `ScriptContext.Error(message: string, stackTrace: string, script: Instance)` Fired when an error occurs. *Security: None · Capabilities: Logging* **Parameters:** | Name | Type | Description | |------|------|-------------| | `message` | `string` | | | `stackTrace` | `string` | | | `script` | `Instance` | | **ScriptContext.Error** When an error occurs, this example prints the name of the script that errored, the reason it errored, and a trace of the error. ```lua local ScriptContext = game:GetService("ScriptContext") local function onError(message, trace, script) print(script:GetFullName(), "errored!") print("Reason:", message) print("Trace:", trace) end ScriptContext.Error:Connect(onError) -- Somewhere, in another script error("Error occurred!") ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ScriptDebuggerService last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated summary: "Provides programmatic breakpoint management, execution control, and runtime inspection of Luau scripts during a playtest." --- # Class: ScriptDebuggerService > Provides programmatic breakpoint management, execution control, and runtime > inspection of Luau scripts during a playtest. ## Description `ScriptDebuggerService` exposes the Roblox Studio Luau debugger for programmatic use. It provides functionality for breakpoint management, execution control, and runtime state inspection. **APIs in this class are currently in beta and are subject to breaking changes.** #### Breakpoint Propagation Breakpoints set in the **edit** [DataModel](/docs/reference/engine/classes/DataModel.md) are set on the specific script instance and do not propagate to clones, but they propagate to corresponding scripts in play data models at the start of a playtest. Breakpoints set in a **play** [DataModel](/docs/reference/engine/classes/DataModel.md) propagate to script clones in the same data model and to corresponding scripts in other data models. #### Parallel Threads The behavior of this API with parallel Luau is undefined. ## Methods ### Method: ScriptDebuggerService:AddBreakpoint **Signature:** `ScriptDebuggerService:AddBreakpoint(scriptInstance: LuaSourceContainer, breakpoint: Dictionary): ScriptBreakpointResult` Adds a breakpoint to the specified script. If a breakpoint already exists on the same script and line, its data is replaced with the new configuration. Errors if the script instance or breakpoint argument is invalid. *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `scriptInstance` | `LuaSourceContainer` | | The [LuaSourceContainer](/docs/reference/engine/classes/LuaSourceContainer.md) to place the breakpoint on. | | `breakpoint` | `Dictionary` | | Dictionary describing the breakpoint configuration through the following key-value pairs: - `Line` — Required 1-based line number. - `Enabled` — Optional boolean whether the breakpoint is active. Default is `true`. - `Condition` — Optional string indicating the Luau expression which must be truthy to pause, for example `"health < 10"`. - `LogMessage` — Optional string message logged when the breakpoint is hit. This string is parsed as a comma-separated list of Luau expressions, evaluated in the breakpoint's scope, and concatenated [LuaGlobals.print()](/docs/reference/engine/globals/LuaGlobals.md)‑style with spaces between segments. String literals are quoted; bare identifiers reference live values. For example, `"'count is', count"` produces output like `count is 7`. - `ContinueExecution` — If `true`, the [DataModel](/docs/reference/engine/classes/DataModel.md) does not pause when the breakpoint is hit. Default is `false`. | **Returns:** `ScriptBreakpointResult` — Dictionary indicating whether the breakpoint was placed successfully and on which line. Includes the following key-value pairs: - `Verified` — Boolean value indicating whether the breakpoint was placed successfully. - `Line` — The line number the breakpoint was placed on. - `Message` — Optional explanation if `Verified` is `false`. ### Method: ScriptDebuggerService:RemoveBreakpoint **Signature:** `ScriptDebuggerService:RemoveBreakpoint(scriptInstance: LuaSourceContainer, line: int): boolean` Removes the breakpoint at the specified line in the given script. Returns `false` if no breakpoint exists on the line (no-op). Errors if the script instance or line number is invalid. *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `scriptInstance` | `LuaSourceContainer` | | The [LuaSourceContainer](/docs/reference/engine/classes/LuaSourceContainer.md) containing the breakpoint. | | `line` | `int` | | The 1-based line number of the breakpoint to remove. | **Returns:** `boolean` — `true` if a breakpoint was removed, `false` if no breakpoint existed on the line. ### Method: ScriptDebuggerService:ClearBreakpoints **Signature:** `ScriptDebuggerService:ClearBreakpoints(): ()` Removes all breakpoints across all scripts. *Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `()` ### Method: ScriptDebuggerService:SetExceptionBreakMode **Signature:** `ScriptDebuggerService:SetExceptionBreakMode(breakMode: DebugBreakModeType): ()` Sets the exception break mode on **all** [DataModels](/docs/reference/engine/classes/DataModel.md). Use [DebugBreakModeType](/docs/reference/engine/enums/DebugBreakModeType.md) to specify when the debugger should pause on exceptions. *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `breakMode` | `DebugBreakModeType` | | The [DebugBreakModeType](/docs/reference/engine/enums/DebugBreakModeType.md) to set. | **Returns:** `()` ### Method: ScriptDebuggerService:Pause **Signature:** `ScriptDebuggerService:Pause(): ()` Requests the debugger to pause at the next safe point. This method is asynchronous and returns immediately. When the thread pauses, [OnStopped](/docs/reference/engine/classes/ScriptDebuggerService.md) fires with reason [ScriptStoppedReason.Pause](/docs/reference/engine/enums/ScriptStoppedReason.md). Has no effect if already stopped. Only meaningful when the [DataModel](/docs/reference/engine/classes/DataModel.md) is running during a playtest. Calling `Pause()` while already stopped at a breakpoint has no effect. *Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `()` ### Method: ScriptDebuggerService:GetThreads **Signature:** `ScriptDebuggerService:GetThreads(): List` Returns all paused Luau threads. Should be called when the [DataModel](/docs/reference/engine/classes/DataModel.md) is stopped (typically inside [OnStopped](/docs/reference/engine/classes/ScriptDebuggerService.md)). Returns empty results when the [DataModel](/docs/reference/engine/classes/DataModel.md) is not stopped at a breakpoint or exception, or when stopped via [Pause()](/docs/reference/engine/classes/ScriptDebuggerService.md). *Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `List` — An array of script debug thread dictionaries, each containing the following key-value pairs: - `Id` — Numerical thread identifier; use with [GetStackTrace()](/docs/reference/engine/classes/ScriptDebuggerService.md) and stepping. - `Name` — Human-readable name of the script. ### Method: ScriptDebuggerService:GetStackTrace **Signature:** `ScriptDebuggerService:GetStackTrace(threadId: int, startFrame?: int?): DebugStackTraceResult` Returns the call stack for a paused thread, ordered innermost (current execution point) to outermost. Use `startFrame` (1‑based) for paginated retrieval of large stacks. Errors if `threadId` or `startFrame` is invalid. *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `threadId` | `int` | | The thread identifier from a script debug thread `Id` field (see [GetThreads()](/docs/reference/engine/classes/ScriptDebuggerService.md)). | | `startFrame` | `int?` | `nil` | Optional 1-based frame index for paginated retrieval. | **Returns:** `DebugStackTraceResult` — Dictionary containing the frames ordered innermost (current) to outermost. Contains the following key-value pairs: - `Frames` — Array of debug stack frame dictionaries. Each dictionary item contains the following key-value pairs: - `Id` — Numerical frame identifier; use with [GetRootVariables()](/docs/reference/engine/classes/ScriptDebuggerService.md) and [Evaluate()](/docs/reference/engine/classes/ScriptDebuggerService.md). - `Name` — Human-readable name of the function at this frame. - `ScriptPath` — Full instance path of the script, for example `"ServerScriptService.MainScript"`. - `Line` — 1-based line number where execution is paused at this frame. - `TotalFrames` — Total frame count, provided when paginating. ### Method: ScriptDebuggerService:GetRootVariables **Signature:** `ScriptDebuggerService:GetRootVariables(frameId: int): List` Returns the root variables (locals, upvalues, globals) for the specified stack frame. Each variable includes a `VariablesReference` field; if greater than `0`, pass it to [GetVariables()](/docs/reference/engine/classes/ScriptDebuggerService.md) to drill into children. Errors if `frameId` is invalid. Returns empty if the [DataModel](/docs/reference/engine/classes/DataModel.md) is not stopped at a breakpoint or exception, or when stopped via [Pause()](/docs/reference/engine/classes/ScriptDebuggerService.md). *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `frameId` | `int` | | The frame identifier from a debug stack frame `Id` field (see [GetStackTrace()](/docs/reference/engine/classes/ScriptDebuggerService.md)). | **Returns:** `List` — An array of script variable dictionaries, each containing the following key-value pairs: - `Name` — String value indicating the variable name or table key. - `Value` — String representation of the value. - `Type` — String indicating the Luau type (`"number"`, `"string"`, `"table"`, `"Instance"`, etc.). - `Scope` — [ScriptVariableScope](/docs/reference/engine/enums/ScriptVariableScope.md) value (children inherit parent's scope). - `VariablesReference` — If greater than `0`, call [GetVariables()](/docs/reference/engine/classes/ScriptDebuggerService.md) with this to get children. ### Method: ScriptDebuggerService:GetVariables **Signature:** `ScriptDebuggerService:GetVariables(variablesReference: int): List` Drills into structured variables such as tables and [Instances](/docs/reference/engine/classes/Instance.md). Pass a `VariablesReference` obtained from a script variable returned by [GetRootVariables()](/docs/reference/engine/classes/ScriptDebuggerService.md) or a previous call to this method. *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `variablesReference` | `int` | | A reference from a previous script variable's `VariablesReference` field (see [GetRootVariables()](/docs/reference/engine/classes/ScriptDebuggerService.md)). | **Returns:** `List` — An array of script variable dictionaries representing the children in the same format as variable dictionaries from [GetRootVariables()](/docs/reference/engine/classes/ScriptDebuggerService.md). Returns empty if the [DataModel](/docs/reference/engine/classes/DataModel.md) is not stopped at a breakpoint or exception, or when stopped via [Pause()](/docs/reference/engine/classes/ScriptDebuggerService.md). ### Method: ScriptDebuggerService:Evaluate **Signature:** `ScriptDebuggerService:Evaluate(expression: string, frameId?: int?): ScriptEvaluateResult` Evaluates a Luau expression in the context of the specified stack frame, or globally if no `frameId` is provided. Errors if the expression has a syntax error or `frameId` is invalid. *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `expression` | `string` | | The Luau expression to evaluate. | | `frameId` | `int?` | `nil` | Optional frame identifier. If omitted, evaluates globally. | **Returns:** `ScriptEvaluateResult` — Dictionary with the following key-value pairs: - `Result` — String representation of the evaluated result. - `Type` — String indicating the Luau type of the result (`"number"`, `"string"`, `"table"`, `"Instance"`, etc.). - `VariablesReference` — If greater than `0`, drill into with [GetVariables()](/docs/reference/engine/classes/ScriptDebuggerService.md). ## Events ### Event: ScriptDebuggerService.Resumed **Signature:** `ScriptDebuggerService.Resumed(threadIds: Array)` Fires when a previously paused thread resumes. After this event, all `frameId` values, `VariablesReference` values, and script variable objects from that thread are invalidated. Re-fetch them the next time the [DataModel](/docs/reference/engine/classes/DataModel.md) stops if needed. Avoid modifying the [DataModel](/docs/reference/engine/classes/DataModel.md), throwing unhandled errors, or calling async yielding functions inside this event handler. *Security: PluginSecurity* **Parameters:** | Name | Type | Description | |------|------|-------------| | `threadIds` | `Array` | An array of thread identifiers that resumed. | ## Callbacks ### Callback: ScriptDebuggerService.OnStopped **Signature:** `ScriptDebuggerService.OnStopped(stopped: Dictionary): ScriptResumeAction` The primary mechanism for reacting to debugger pauses. Set this to a function that receives a `stopped` payload and returns a script resume action dictionary indicating how execution should continue. Only one `OnStopped` per [DataModel](/docs/reference/engine/classes/DataModel.md) is allowed; it is not inherited from the edit [DataModel](/docs/reference/engine/classes/DataModel.md). If the callback returns nothing or throws, [DebuggerResumeType.Resume](/docs/reference/engine/enums/DebuggerResumeType.md) is assumed. Avoid modifying the [DataModel](/docs/reference/engine/classes/DataModel.md), throwing unhandled errors, or calling async yielding functions inside this callback. #### Late-Set Behavior If `OnStopped` is set while the [DataModel](/docs/reference/engine/classes/DataModel.md) is already stopped at a breakpoint and the previous value was `nil`, the callback runs immediately. *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `stopped` | `Dictionary` | | Dictionary describing why the debugger paused. The following key-value pairs are valid: - `Reason` — [ScriptStoppedReason](/docs/reference/engine/enums/ScriptStoppedReason.md) why the debugger paused. - `ThreadIds` — Array of thread identifiers that stopped. - `ExceptionText` — Error message present when `Reason` is [ScriptStoppedReason.Exception](/docs/reference/engine/enums/ScriptStoppedReason.md). | **Returns:** `ScriptResumeAction` — Dictionary specifying how to resume execution. Contains the following key-value pairs: - `steppedType` — [DebuggerResumeType](/docs/reference/engine/enums/DebuggerResumeType.md) describing how to resume. - `threadId` — Number indicating which thread to step. Required for step actions. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ScriptDocument last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated --- # Class: ScriptDocument ## Description A [ScriptDocument](/docs/reference/engine/classes/ScriptDocument.md) instance is a proxy of the document of a Studio Script Editor. It's different from the [LuaSourceContainer](/docs/reference/engine/classes/LuaSourceContainer.md) open in the editor in that it represents the ephemeral state of an open document, and its representation is in a format that's more suited for reading and editing code than executing it. In particular, [ScriptDocument](/docs/reference/engine/classes/ScriptDocument.md) reflects any changes that have been made to the open script in Drafts Mode, which the source property doesn't. The Script Editor itself exists and changes on a different thread than any [DataModel](/docs/reference/engine/classes/DataModel.md), so the [ScriptDocument](/docs/reference/engine/classes/ScriptDocument.md) replicates the open Script Editor, but it isn't the open editor. Because of the replication, there's sometimes a slight delay between changing the text in the editor and updating the [ScriptDocument](/docs/reference/engine/classes/ScriptDocument.md). The delay usually occurs because the [DataModel](/docs/reference/engine/classes/DataModel.md) is busy, and it's almost always extremely small, but it still exists. The existence of a [ScriptDocument](/docs/reference/engine/classes/ScriptDocument.md) indicates that a document is open in the Script Editor. All [ScriptDocument](/docs/reference/engine/classes/ScriptDocument.md) instances have [ScriptEditorService](/docs/reference/engine/classes/ScriptEditorService.md) as its parent. Each instance adheres to the following encoding conventions: - All text in [ScriptDocument](/docs/reference/engine/classes/ScriptDocument.md) is UTF-8 encoded. - All line indices are 1-indexed. - All character indices are 1-indexed and count UTF-8 bytes, not graphemes, so the same warning from [TextBox.CursorPosition](/docs/reference/engine/classes/TextBox.md) applies: many Unicode characters take more than one byte. - All ranges are inclusive of their start position and exclusive of their end position, so start == end implies an empty range. All APIs for [ScriptDocument](/docs/reference/engine/classes/ScriptDocument.md) are at **Plugin** level security. ## Methods ### Method: ScriptDocument:CloseAsync **Signature:** `ScriptDocument:CloseAsync(): Tuple` Requests that the editor associated with this document close. Yields the current thread until the editor responds to the request. If the function succeeds, it returns (true, nil). If the function fails, it returns (false, string) as a description of the problem. This function can't close the command bar. *Yields · Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `Tuple` **ScriptDocument:CloseAsync** ScriptDocument:CloseAsync ```lua --!nocheck -- Run the following code in the Command Bar local ScriptEditorService = game:GetService("ScriptEditorService") local documents = ScriptEditorService:GetScriptDocuments() local scriptDocument -- Find the first open script document for _, document in documents do -- The Command Bar can't be closed, so don't select it if not document:IsCommandBar() then scriptDocument = document break end end if scriptDocument then local success, err = scriptDocument:CloseAsync() if success then print(`Closed {scriptDocument.Name}`) else warn(`Failed to close {scriptDocument.Name} because: {err}`) end else print("No open scripts") end ``` ### Method: ScriptDocument:EditTextAsync **Signature:** `ScriptDocument:EditTextAsync(newText: string, startLine: int, startCharacter: int, endLine: int, endCharacter: int): Tuple` Replaces the text in the specified range from (`startLine`, `startColumn`) to (`endLine`, `endColumn`) with `newText`. If the range is empty, then the function inserts the text at (`startLine`, `startColumn`). If the text cursor is within the specified range, the cursor moves to the end position of the edit. Otherwise, the text cursor doesn't move. This function yields the current thread until it receives a reply from the editor about the edit. If the function succeeds, it returns (`true`, `nil`). The function throws an error if: - The range is invalid. - The range would slice a unicode character, for example replace only some of the bytes of the unicode character. - The `newText` itself contains invalid UTF-8. If the function fails, it returns (false, string). The string is a description of the problem. The most common failure type is a version mismatch. This occurs when you try to call `EditTextAsync` during the time when the [ScriptDocument](/docs/reference/engine/classes/ScriptDocument.md) is out of sync with the contents of the editor. If this happens, you can retry the edit. *Yields · Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `newText` | `string` | | | | `startLine` | `int` | | | | `startCharacter` | `int` | | | | `endLine` | `int` | | | | `endCharacter` | `int` | | | **Returns:** `Tuple` ### Method: ScriptDocument:ForceSetSelectionAsync **Signature:** `ScriptDocument:ForceSetSelectionAsync(cursorLine: int, cursorCharacter: int, anchorLine?: int?, anchorCharacter?: int?): Tuple` Asks the editor to set its cursor selection to the argument values. Both anchor arguments must be passed, or neither. If neither is passed, then they each default to being the same as the corresponding cursor argument. The editor might decline to update its cursor if the text content of the document has changed. Unlike [ScriptDocument:RequestSetSelectionAsync()](/docs/reference/engine/classes/ScriptDocument.md), the editor will not decline to move its cursor if the cursor has moved since the request was made. Returns (true, nil) if the cursor was updated, and (false, string) with an explanation string if it was not. Yields the current thread until the editor replies. *Yields · Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `cursorLine` | `int` | | | | `cursorCharacter` | `int` | | | | `anchorLine` | `int?` | `nil` | | | `anchorCharacter` | `int?` | `nil` | | **Returns:** `Tuple` **ScriptDocument:ForceSetSelectionAsync()** ScriptDocument:ForceSetSelectionAsync() ```lua --!nocheck -- Run the following code in the Command Bar while a script is open local ScriptEditorService = game:GetService("ScriptEditorService") local function getFirstOpenDocument() local documents = ScriptEditorService:GetScriptDocuments() for _, document in documents do if not document:IsCommandBar() then return document end end return nil end local scriptDocument = getFirstOpenDocument() if scriptDocument then -- Get the text on the cursor's current line local cursorLine = scriptDocument:GetSelection() local lineText = scriptDocument:GetLine(cursorLine) -- Force select the entire line of text local success, err = scriptDocument:ForceSetSelectionAsync(cursorLine, 1, cursorLine, #lineText + 1) if success then print("Set selection!") else print(`Failed to set selection because: {err}`) end else print("No scripts open") end ``` ### Method: ScriptDocument:GetLine **Signature:** `ScriptDocument:GetLine(lineIndex?: int?): string` Returns the text of the specified line. When no argument is provided, returns the line of the current cursor position. *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `lineIndex` | `int?` | `nil` | | **Returns:** `string` **ScriptDocument.SelectionChanged and ScriptDocument:GetLine()** ScriptDocument.SelectionChanged and ScriptDocument:GetLine() ```lua --!nocheck -- Run the following code in the Command Bar while a script is open local ScriptEditorService = game:GetService("ScriptEditorService") local function getFirstOpenDocument() local documents = ScriptEditorService:GetScriptDocuments() for _, document in documents do if not document:IsCommandBar() then return document end end return nil end local scriptDocument = getFirstOpenDocument() if scriptDocument then scriptDocument.SelectionChanged:Connect(function(positionLine, positionCharacter, anchorLine, anchorCharacter) print(`Selected: Line {positionLine}, Char {positionCharacter}`) print(`Anchor: Line {anchorLine}, Char {anchorCharacter}`) local lineText = scriptDocument:GetLine(positionLine) print(`Selected line text: {lineText}`) end) else print("No scripts open") end ``` ### Method: ScriptDocument:GetLineCount **Signature:** `ScriptDocument:GetLineCount(): int` Returns the number of lines in the active document. *Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `int` **ScriptDocument:GetLineCount()** ScriptDocument:GetLineCount() ```lua --!nocheck -- Run the following code in the Command Bar while a script is open local ScriptEditorService = game:GetService("ScriptEditorService") local function getFirstOpenDocument() local documents = ScriptEditorService:GetScriptDocuments() for _, document in documents do if not document:IsCommandBar() then return document end end return nil end local scriptDocument = getFirstOpenDocument() if scriptDocument then local lineCount = scriptDocument:GetLineCount() print(`The script has {lineCount} lines!`) else print("No scripts open") end ``` ### Method: ScriptDocument:GetScript **Signature:** `ScriptDocument:GetScript(): LuaSourceContainer` Returns the underlying [LuaSourceContainer](/docs/reference/engine/classes/LuaSourceContainer.md) instance, if one exists, otherwise `nil`. *Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `LuaSourceContainer` **ScriptDocument:GetScript()** ScriptDocument:GetScript() ```lua --!nocheck -- Run the following code in the Command Bar while a script is open local ScriptEditorService = game:GetService("ScriptEditorService") local function getFirstOpenDocument() local documents = ScriptEditorService:GetScriptDocuments() for _, document in documents do if not document:IsCommandBar() then return document end end return nil end local scriptDocument = getFirstOpenDocument() if scriptDocument then local openScript = scriptDocument:GetScript() print(`Currently open script: {openScript:GetFullName()}`) else print("No scripts open") end ``` ### Method: ScriptDocument:GetSelectedText **Signature:** `ScriptDocument:GetSelectedText(): string` Gets the text selected in the editor, or an empty string if there is no selection. *Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `string` **ScriptDocument:HasSelectedText() and :GetSelectedText()** ScriptDocument:HasSelectedText() and :GetSelectedText() ```lua --!nocheck -- Run the following code in the Command Bar while a script is open local ScriptEditorService = game:GetService("ScriptEditorService") local function getFirstOpenDocument() local documents = ScriptEditorService:GetScriptDocuments() for _, document in documents do if not document:IsCommandBar() then return document end end return nil end local scriptDocument = getFirstOpenDocument() if scriptDocument then scriptDocument.SelectionChanged:Connect(function() if scriptDocument:HasSelectedText() then local selectedText = scriptDocument:GetSelectedText() print(`Currently selected text: {selectedText}`) else print("No text currently selected") end end) else print("No scripts open") end ``` ### Method: ScriptDocument:GetSelection **Signature:** `ScriptDocument:GetSelection(): Tuple` Returns the last known selection of the Script Editor in the format: `CursorLine, CursorChar, AnchorLine, AnchorChar`. If the Script Editor has no selection, `CursorLine == AnchorLine` and `CursorChar == AnchorChar`. *Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `Tuple` — CursorLine, CursorChar, AnchorLine, AnchorChar. ### Method: ScriptDocument:GetSelectionEnd **Signature:** `ScriptDocument:GetSelectionEnd(): Tuple` Gets the larger of the cursor position and anchor. If the editor has no selection, they are the same value. *Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `Tuple` **ScriptDocument:GetSelectionStart() and :GetSelectionEnd()** ScriptDocument:GetSelectionStart() and :GetSelectionEnd() ```lua --!nocheck -- Run the following code in the Command Bar while a script is open local ScriptEditorService = game:GetService("ScriptEditorService") local function getFirstOpenDocument() local documents = ScriptEditorService:GetScriptDocuments() for _, document in documents do if not document:IsCommandBar() then return document end end return nil end local scriptDocument = getFirstOpenDocument() if scriptDocument then local startLine, startCharacter = scriptDocument:GetSelectionStart() local endLine, endCharacter = scriptDocument:GetSelectionEnd() print(`Selection start: Line {startLine}, Char {startCharacter}`) print(`Selection end: Line {endLine}, Char {endCharacter}`) else print("No scripts open") end ``` ### Method: ScriptDocument:GetSelectionStart **Signature:** `ScriptDocument:GetSelectionStart(): Tuple` Gets the smaller of the cursor position and anchor. If the editor has no selection, they are the same value. *Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `Tuple` **ScriptDocument:GetSelectionStart() and :GetSelectionEnd()** ScriptDocument:GetSelectionStart() and :GetSelectionEnd() ```lua --!nocheck -- Run the following code in the Command Bar while a script is open local ScriptEditorService = game:GetService("ScriptEditorService") local function getFirstOpenDocument() local documents = ScriptEditorService:GetScriptDocuments() for _, document in documents do if not document:IsCommandBar() then return document end end return nil end local scriptDocument = getFirstOpenDocument() if scriptDocument then local startLine, startCharacter = scriptDocument:GetSelectionStart() local endLine, endCharacter = scriptDocument:GetSelectionEnd() print(`Selection start: Line {startLine}, Char {startCharacter}`) print(`Selection end: Line {endLine}, Char {endCharacter}`) else print("No scripts open") end ``` ### Method: ScriptDocument:GetText **Signature:** `ScriptDocument:GetText(startLine?: int?, startCharacter?: int?, endLine?: int?, endCharacter?: int?): string` Returns text from the open editor. Must be called with 0, 2 or 4 arguments: - If called with 0 arguments, gets the entire contents of the open editor. - If called with 2 arguments, gets the text of the document starting at (`startLine`, `startColumn`). - If called with 4 arguments, gets the text of the document starting at (`startLine`, `startColumn`) and ending at (`endLine`, `endColumn`). *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `startLine` | `int?` | `nil` | | | `startCharacter` | `int?` | `nil` | | | `endLine` | `int?` | `nil` | | | `endCharacter` | `int?` | `nil` | | **Returns:** `string` **ScriptDocument:GetText()** ScriptDocument:GetText() ```lua --!nocheck -- Run the following code in the Command Bar while a script is open local ScriptEditorService = game:GetService("ScriptEditorService") local function getFirstOpenDocument() local documents = ScriptEditorService:GetScriptDocuments() for _, document in documents do if not document:IsCommandBar() then return document end end return nil end local scriptDocument = getFirstOpenDocument() if scriptDocument then local text = scriptDocument:GetText() print(`Script contents: {text}`) else print("No scripts open") end ``` ### Method: ScriptDocument:GetViewport **Signature:** `ScriptDocument:GetViewport(): Tuple` Returns the currently displayed line numbers in the editor change. The editor displays the lines between startLine and endLine, inclusive. The first and last line might only display partially. For example, only the topmost pixel of the last line might be on screen. Furthermore, code folding might hide lines between startLine and endLine. *Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `Tuple` **ScriptDocument:GetViewport** ScriptDocument:GetViewport ```lua --!nocheck -- Run the following code in the Command Bar while a script is open local ScriptEditorService = game:GetService("ScriptEditorService") local function getFirstOpenDocument() local documents = ScriptEditorService:GetScriptDocuments() for _, document in documents do if not document:IsCommandBar() then return document end end return nil end local scriptDocument = getFirstOpenDocument() if scriptDocument then local firstLine, lastLine = scriptDocument:GetViewport() print(`Currently viewing lines {firstLine} to {lastLine}`) else print("No scripts open") end ``` ### Method: ScriptDocument:HasSelectedText **Signature:** `ScriptDocument:HasSelectedText(): boolean` Returns whether or not the editor has any text selected. *Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `boolean` **ScriptDocument:HasSelectedText() and :GetSelectedText()** ScriptDocument:HasSelectedText() and :GetSelectedText() ```lua --!nocheck -- Run the following code in the Command Bar while a script is open local ScriptEditorService = game:GetService("ScriptEditorService") local function getFirstOpenDocument() local documents = ScriptEditorService:GetScriptDocuments() for _, document in documents do if not document:IsCommandBar() then return document end end return nil end local scriptDocument = getFirstOpenDocument() if scriptDocument then scriptDocument.SelectionChanged:Connect(function() if scriptDocument:HasSelectedText() then local selectedText = scriptDocument:GetSelectedText() print(`Currently selected text: {selectedText}`) else print("No text currently selected") end end) else print("No scripts open") end ``` ### Method: ScriptDocument:IsCommandBar **Signature:** `ScriptDocument:IsCommandBar(): boolean` Returns true if the [ScriptDocument](/docs/reference/engine/classes/ScriptDocument.md) represents the Command bar. The command bar has special rules and limitations in this API: - Studio creates the Command bar before running plugins, so it doesn't always fire the opened event, although it does close and reopen as Studio transitions between DataModels. - You can't edit the Command bar with `EditTextAsync` for security reasons. *Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `boolean` **ScriptDocument:IsCommandBar()** ScriptDocument:IsCommandBar() ```lua --!nocheck -- Run the following code in the Command Bar local ScriptEditorService = game:GetService("ScriptEditorService") local documents = ScriptEditorService:GetScriptDocuments() for _, document in documents do if document:IsCommandBar() then print("Command bar document:", document) end end ``` ### Method: ScriptDocument:MultiEditTextAsync **Signature:** `ScriptDocument:MultiEditTextAsync(edits: Array): Tuple` *Yields · Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `edits` | `Array` | | | **Returns:** `Tuple` ### Method: ScriptDocument:RequestSetSelectionAsync **Signature:** `ScriptDocument:RequestSetSelectionAsync(cursorLine: int, cursorCharacter: int, anchorLine?: int?, anchorCharacter?: int?): Tuple` Asks the editor to set its cursor selection to the argument values. Both anchor arguments must be passed, or neither. If neither is passed, then they each default to being the same as the corresponding cursor argument. The editor might decline to update its cursor if the text content of the document has changed, or the cursor has moved since the request was made. Returns (true, nil) if the cursor was updated, and (false, string) with an explanation string if it was not. Yields the current thread until the editor replies. *Yields · Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `cursorLine` | `int` | | | | `cursorCharacter` | `int` | | | | `anchorLine` | `int?` | `nil` | | | `anchorCharacter` | `int?` | `nil` | | **Returns:** `Tuple` **ScriptDocument:RequestSetSelectionAsync()** ScriptDocument:RequestSetSelectionAsync() ```lua --!nocheck -- Run the following code in the Command Bar while a script is open local ScriptEditorService = game:GetService("ScriptEditorService") local function getFirstOpenDocument() local documents = ScriptEditorService:GetScriptDocuments() for _, document in documents do if not document:IsCommandBar() then return document end end return nil end local scriptDocument = getFirstOpenDocument() if scriptDocument then -- Get the text on the cursor's current line local cursorLine = scriptDocument:GetSelection() local lineText = scriptDocument:GetLine(cursorLine) -- Force select the entire line of text local success, err = scriptDocument:RequestSetSelectionAsync(cursorLine, 1, cursorLine, #lineText + 1) if success then print("Set selection!") else print(`Failed to set selection because: {err}`) end else print("No scripts open") end ``` ## Events ### Event: ScriptDocument.SelectionChanged **Signature:** `ScriptDocument.SelectionChanged(positionLine: int64, positionCharacter: int64, anchorLine: int64, anchorCharacter: int64)` Fires when the ScriptDocument changes, including immediately after a text change. *Security: PluginSecurity* **Parameters:** | Name | Type | Description | |------|------|-------------| | `positionLine` | `int64` | | | `positionCharacter` | `int64` | | | `anchorLine` | `int64` | | | `anchorCharacter` | `int64` | | **ScriptDocument.SelectionChanged and ScriptDocument:GetLine()** ScriptDocument.SelectionChanged and ScriptDocument:GetLine() ```lua --!nocheck -- Run the following code in the Command Bar while a script is open local ScriptEditorService = game:GetService("ScriptEditorService") local function getFirstOpenDocument() local documents = ScriptEditorService:GetScriptDocuments() for _, document in documents do if not document:IsCommandBar() then return document end end return nil end local scriptDocument = getFirstOpenDocument() if scriptDocument then scriptDocument.SelectionChanged:Connect(function(positionLine, positionCharacter, anchorLine, anchorCharacter) print(`Selected: Line {positionLine}, Char {positionCharacter}`) print(`Anchor: Line {anchorLine}, Char {anchorCharacter}`) local lineText = scriptDocument:GetLine(positionLine) print(`Selected line text: {lineText}`) end) else print("No scripts open") end ``` ### Event: ScriptDocument.ViewportChanged **Signature:** `ScriptDocument.ViewportChanged(startLine: int64, endLine: int64)` Fires when the displayed line numbers in the editor change. See [ScriptDocument.GetViewport](/docs/reference/engine/classes/ScriptDocument.md) for details. *Security: PluginSecurity* **Parameters:** | Name | Type | Description | |------|------|-------------| | `startLine` | `int64` | | | `endLine` | `int64` | | **Connecting to ScriptDocument.ViewportChanged** Demonstrates using [ScriptDocument.ViewportChanged](/docs/reference/engine/classes/ScriptDocument.md) to print the start and end line of the script's viewport when it changes. To run: 1. Ensure Output view is open 2. Run the below code in the Command Bar 3. Scroll up and down in the opened Script window ```lua --!nocheck --[[ To run: 1. Ensure Output view is open 2. Run the below code in the Command Bar 3. Scroll up and down in the opened Script window Print statements from the ViewportChanged event will appear in the Output ]] local Workspace = game:GetService("Workspace") local ScriptEditorService = game:GetService("ScriptEditorService") -- Create text that spans many lines local dummyText = string.rep("-- Dummy Text\n", 60) -- Create a script containing the dummy text and open it local otherScript = Instance.new("Script") otherScript.Source = dummyText otherScript.Parent = Workspace local success, err = ScriptEditorService:OpenScriptDocumentAsync(otherScript) if not success then warn(`Failed to open script because: {err}`) return end -- Get a reference to the opened script local scriptDocument = ScriptEditorService:FindScriptDocument(otherScript) local function onViewportChanged(startLine: number, endLine: number) print(`Script Viewport Changed - startLine: {startLine}, endLine: {endLine}`) end -- Connect the ViewportChanged event to the function above that prints the start and end line of the updated viewport scriptDocument.ViewportChanged:Connect(onViewportChanged) ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ScriptEditorService last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated summary: "This service is used for interacting with ScriptDocument instances." --- # Class: ScriptEditorService > This service is used for interacting with [ScriptDocument](/docs/reference/engine/classes/ScriptDocument.md) instances. ## Methods ### Method: ScriptEditorService:DeregisterAutocompleteCallback **Signature:** `ScriptEditorService:DeregisterAutocompleteCallback(name: string): ()` Removes a previously registered callback with the name `name`. *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name` | `string` | | | **Returns:** `()` **DeregisterAutocompleteCallback()** ```lua local ScriptEditorService = game:GetService("ScriptEditorService") ScriptEditorService:DeregisterAutocompleteCallback("foo") ``` ### Method: ScriptEditorService:DeregisterScriptAnalysisCallback **Signature:** `ScriptEditorService:DeregisterScriptAnalysisCallback(name: string): ()` Removes a previously registered callback with the name `name`. *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name` | `string` | | | **Returns:** `()` **DeregisterScriptAnalysisCallback()** ```lua local ScriptEditorService = game:GetService("ScriptEditorService") ScriptEditorService:DeregisterScriptAnalysisCallback("foo") ``` ### Method: ScriptEditorService:FindScriptDocument **Signature:** `ScriptEditorService:FindScriptDocument(script: LuaSourceContainer): ScriptDocument` Returns the open [ScriptDocument](/docs/reference/engine/classes/ScriptDocument.md) corresponding to the given [LuaSourceContainer](/docs/reference/engine/classes/LuaSourceContainer.md), or `nil` if the given script is not open. *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `script` | `LuaSourceContainer` | | | **Returns:** `ScriptDocument` **ScriptDocument:CloseAsync** ScriptDocument:CloseAsync ```lua --!nocheck -- Run the following code in the Command Bar local ScriptEditorService = game:GetService("ScriptEditorService") local documents = ScriptEditorService:GetScriptDocuments() local scriptDocument -- Find the first open script document for _, document in documents do -- The Command Bar can't be closed, so don't select it if not document:IsCommandBar() then scriptDocument = document break end end if scriptDocument then local success, err = scriptDocument:CloseAsync() if success then print(`Closed {scriptDocument.Name}`) else warn(`Failed to close {scriptDocument.Name} because: {err}`) end else print("No open scripts") end ``` **Connecting to ScriptDocument.ViewportChanged** Demonstrates using [ScriptDocument.ViewportChanged](/docs/reference/engine/classes/ScriptDocument.md) to print the start and end line of the script's viewport when it changes. To run: 1. Ensure Output view is open 2. Run the below code in the Command Bar 3. Scroll up and down in the opened Script window ```lua --!nocheck --[[ To run: 1. Ensure Output view is open 2. Run the below code in the Command Bar 3. Scroll up and down in the opened Script window Print statements from the ViewportChanged event will appear in the Output ]] local Workspace = game:GetService("Workspace") local ScriptEditorService = game:GetService("ScriptEditorService") -- Create text that spans many lines local dummyText = string.rep("-- Dummy Text\n", 60) -- Create a script containing the dummy text and open it local otherScript = Instance.new("Script") otherScript.Source = dummyText otherScript.Parent = Workspace local success, err = ScriptEditorService:OpenScriptDocumentAsync(otherScript) if not success then warn(`Failed to open script because: {err}`) return end -- Get a reference to the opened script local scriptDocument = ScriptEditorService:FindScriptDocument(otherScript) local function onViewportChanged(startLine: number, endLine: number) print(`Script Viewport Changed - startLine: {startLine}, endLine: {endLine}`) end -- Connect the ViewportChanged event to the function above that prints the start and end line of the updated viewport scriptDocument.ViewportChanged:Connect(onViewportChanged) ``` ### Method: ScriptEditorService:GetEditorSource **Signature:** `ScriptEditorService:GetEditorSource(script: LuaSourceContainer): string` Returns the edit-time source for the given script. If the script is open in the [Script Editor](/docs/en-us/studio/script-editor.md), this method returns the text currently being displayed in the editor. If the script is not open in the editor, the method returns the text that the editor would display if it's opened. The edit-time source is not always be consistent with the [Script.Source](/docs/reference/engine/classes/Script.md) property. *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `script` | `LuaSourceContainer` | | | **Returns:** `string` ### Method: ScriptEditorService:GetScriptDocuments **Signature:** `ScriptEditorService:GetScriptDocuments(): List` Returns an array of the currently open script documents, including the command bar. *Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `List` **Print the name of every script** Gets all script documents in the place with [ScriptEditorService:GetScriptDocuments()](/docs/reference/engine/classes/ScriptEditorService.md) and prints their names. ```lua --!nocheck -- Run the following code in the Command Bar local ScriptEditorService = game:GetService("ScriptEditorService") local scriptDocuments = ScriptEditorService:GetScriptDocuments() for _, scriptDocument in scriptDocuments do -- Prints the name of each script if not scriptDocument:IsCommandBar() then print(scriptDocument.Name) end end ``` ### Method: ScriptEditorService:OpenScriptDocumentAsync **Signature:** `ScriptEditorService:OpenScriptDocumentAsync(script: LuaSourceContainer, options?: Dictionary): Tuple` Requests that a Script Editor open the specified script. Returns (true, nil) if the request succeeds. Returns (false, string) if the request fails, with a string that describes the problem. If the script is already open, this function succeeds and switches tabs to the associated editor. *Yields · Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `script` | `LuaSourceContainer` | | | | `options` | `Dictionary` | `nil` | A dictionary that supports the following options: - `Temporary` — Boolean. Whether to open the script in a preview tab. Default is false. - `HighlightRange` — A nested dictionary containing a line and character range to highlight in the editor. Example: `HighlightRange = { Start = { Line = 10, Character = 1 }, End = { Line = 15, Character = 20 }}`. | **Returns:** `Tuple` **OpenScriptDocumentAsync()** ```lua --!nocheck -- Run the following code in the Command Bar local ScriptEditorService = game:GetService("ScriptEditorService") local Workspace = game:GetService("Workspace") local newScript = Instance.new("Script") newScript.Parent = Workspace local success, err = ScriptEditorService:OpenScriptDocumentAsync(newScript) if success then print("Opened script document") else print(`Failed to open script document: {err}`) end ``` ### Method: ScriptEditorService:RegisterAutocompleteCallback **Signature:** `ScriptEditorService:RegisterAutocompleteCallback(name: string, priority: int, callbackFunction: Function): ()` Registers an autocomplete callback `callbackFunction` named `name` with priority `priority`. When the Script Editor invokes autocomplete, all registered autocomplete callbacks call in order of ascending priority with the autocomplete request and response. Multiple callbacks may share a priority, but then their calling order is unpredictable. Each callback is intended to return a response table with the same format as the response input table. Callbacks shouldn't yield. The first callback invoked receives the internal autocomplete's response as its response table, and subsequent callbacks receive the previous callback's output as their response table. Callbacks may either modify the passed table or return a new table of the same format. The `callbackFunction` must have the following type: `(Request: table, Response: table) -> table` The Request table has the following format: ```lua type Request = { position: { line: number, character: number }, textDocument: { document: ScriptDocument?, script: LuaSourceContainer? } } ``` - `position` is the one-indexed cursor position where you are autocompleting. - `textDocument.document` is the open [ScriptDocument](/docs/reference/engine/classes/ScriptDocument.md) you are completing in, if it exists. - `textDocument.script` is the [LuaSourceContainer](/docs/reference/engine/classes/LuaSourceContainer.md) you are completing in, if it exists. If both `textDocument.document` and `textDocument.script` are present, then they correspond to each other: `req.textDocument.document:GetScript() == req.textDocument.script` The Response table has the following format: ``` type Response = { items: { { label: string, -- The label kind: Enum.CompletionItemKind?, tags: {Enum.CompletionItemTag}?, detail: string?, documentation: { value: string, }?, overloads: number?, learnMoreLink: string?, codeSample: string?, preselect: boolean?, textEdit: { newText: string, insert: { start: { line: number, character: number }, ["end"]: { line: number, character: number } }, replace: { start: { line: number, character: number }, ["end"]: { line: number, character: number } }, }? } } } ``` - `Response.items` is an array of the completion items. The order of this array is insignificant, and it resorts in the editor as the user types. - `Response.items[n].label` is the label of the item which display in the autocomplete menu. - `Response.items[n].kind` specifies what type of autocomplete item this is. Primarily this controls the icon given to the item in the editor. Not all kinds have a unique icon. If not specified, the editor uses the "Text" icon. Unsupported kinds default to displaying the "Property" icon. - `Response.items[n].tags` specifies an array of tags describing this completion item. See the [CompletionItemTag](/docs/reference/engine/enums/CompletionItemTag.md) for details on their function. - `Response.items[n].details` specifies a string describing details about the completion item. For default items, this is a string representation of their type. Note that, in order for the documentation widget to display, `documentation` must be present, but `documentation.value` may be empty. - `Response.items[n].documentation` specifies the main body of the documentation in its `value` field. `documentation` is present, even if value is empty, so the documentation window displays if either details or overloads are specified. - `Response.items[n].overloads` specifies the number of overloads of a function autocompletion. - `Response.items[n].learnMoreLink` links to a relevant page on the creator docs. This URL must be a `https` request to create.roblox.com; no other URLs display in the editor. - `Response.items[n].codeSample` specifies a sample use of the completion item. `documentation` must be non-empty to display this field. - `Response.items[n].preselect` If true, the editor sorts this completion item ahead of all others and selects it for the user by default. No effect if false or missing. - `Response.items[n].textEdit` If present, accepting the completion applies this text edit - inserting or replacing the span between the positions start and end with newText. If a callback returns a malformed result or encounters an error, the editor discards the modified Response table and uses the built-in autocomplete result list. *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name` | `string` | | | | `priority` | `int` | | | | `callbackFunction` | `Function` | | | **Returns:** `()` **RegisterAutocompleteCallback() and DeregisterAutocompleteCallback()** ```lua --!nocheck -- Run the following code in the Command Bar local ScriptEditorService = game:GetService("ScriptEditorService") type Request = { position: { line: number, character: number, }, textDocument: { document: ScriptDocument?, script: LuaSourceContainer?, }, } type Response = { items: { { label: string, kind: Enum.CompletionItemKind?, tags: { Enum.CompletionItemTag }?, detail: string?, documentation: { value: string, }?, overloads: number?, learnMoreLink: string?, codeSample: string?, preselect: boolean?, textEdit: { newText: string, replace: { start: { line: number, character: number }, ["end"]: { line: number, character: number }, }, }?, } }, } local autocompleteCallback = function(request: Request, response: Response): Response local item = { label = "foo", preselect = true, } table.insert(response.items, item) return response end ScriptEditorService:RegisterAutocompleteCallback("foo", 1, autocompleteCallback) -- To deregister the callback, run the following code in the Command Bar ScriptEditorService:DeregisterAutocompleteCallback("foo") ``` ### Method: ScriptEditorService:RegisterScriptAnalysisCallback **Signature:** `ScriptEditorService:RegisterScriptAnalysisCallback(name: string, priority: int, callbackFunction: Function): ()` Registers a Script Analysis callback `callbackFunction` named `name` with `priority`. When Script Analysis in Studio runs, all registered callbacks call in order of ascending priority. Each callback is intended to return a response table matching the format specified below. Callbacks should not yield. The request table has the following format, where `script` is the `LuaSourceContainer` that is going to be analyzed. ``` type Request = { script: LuaSourceContainer? } ``` The response table has the following format, where `diagnostics` is an array of diagnostic tables. Each diagnostic table has the entries listed below. ``` type Response = { diagnostics: { { range: { start: { line: number, character: number, }, ["end"]: { line: number, character: number, } }, code: string?, message: string, severity: Enum.Severity?, codeDescription: { href: string }? } } } ``` - `range` represents a text range that should be highlighted by the linter, providing what line/character to start highlighting and what line/character to stop highlighting. - `code` is a label for the message. - `message` is a warning message to be displayed for the line. This will also appear on a tooltip when the user hovers their cursor over the line in the Script Editor. - `severity` is a [Severity](/docs/reference/engine/enums/Severity.md) value for the diagnostics. This determines how the diagnostic is categorized in the Script Analysis tool in Studio, as well as how text is highlighted in the Script Editor. - `codeDescription` links to a relevant page on the creator docs. This URL must be an `https` request to `create.roblox.com`; no other URLs display in the editor. *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name` | `string` | | | | `priority` | `int` | | | | `callbackFunction` | `Function` | | | **Returns:** `()` **RegisterScriptAnalysisCallback()** ```lua type Request = { ["script"]: LuaSourceContainer, } type Response = { diagnostics: { { range: { start: { line: number, character: number, }, ["end"]: { line: number, character: number, }, }, code: string?, message: string, severity: Enum.Severity?, codeDescription: { href: string }?, } }, } local ScriptEditorService = game:GetService("ScriptEditorService") ScriptEditorService:RegisterScriptAnalysisCallback("foo", 1, function(Req: Request): Response local response = { diagnostics = {}, } local lineNo = 1 -- Iterate line by line for text, newline in Req.script.Source:gmatch("([^\r\n]*)([\r\n]*)") do local startIndex, endIndex = string.find(text, "Foo") if startIndex and endIndex then table.insert(response.diagnostics, { range = { ["start"] = { line = lineNo, character = startIndex, }, ["end"] = { line = lineNo, character = endIndex, }, }, code = "FooFinder", message = "Foo found here!", severity = Enum.Severity.Warning, }) end lineNo = lineNo + #newline:gsub("\n+", "\0%0\0"):gsub(".%z.", "."):gsub("%z", "") end return response end) ``` ### Method: ScriptEditorService:UpdateSourceAsync **Signature:** `ScriptEditorService:UpdateSourceAsync(script: LuaSourceContainer, callback: Function): ()` Returns the edit-time [Script.Source](/docs/reference/engine/classes/Script.md) for the given script. This function calls the passed callback using the old contents of the script to calculate the new contents of the script. If the script is open in the [Script Editor](/docs/en-us/studio/script-editor.md), then it issues a request to the editor to update its source. The editor may reject this update if the [Script.Source](/docs/reference/engine/classes/Script.md) property was out of date with the user's version of the script when this function was called, in which case the callback will be re-invoked and the attempt will be repeated. The callback may not yield. If the callback returns `nil`, the operation is cancelled. This function yields until the operation is cancelled or succeeds. If the script is not open in the editor, the new content updates to the script source, which is the text the editor would display if it is opened. ```lua local ScriptEditorService = game:GetService("ScriptEditorService") ScriptEditorService:UpdateSourceAsync(workspace.Script, function(oldContent) return oldContent .. " World!" end) ``` *Yields · Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `script` | `LuaSourceContainer` | | Script instance to be updated. | | `callback` | `Function` | | The function to return new script content. | **Returns:** `()` ## Events ### Event: ScriptEditorService.TextDocumentDidChange **Signature:** `ScriptEditorService.TextDocumentDidChange(document: ScriptDocument, changesArray: Variant)` Fires just after a [ScriptDocument](/docs/reference/engine/classes/ScriptDocument.md) changes. The `textChanged` is an array of change structures of the format: `{ range : { start : { line : number, character : number }, end : { line : number, character : number } }, text: string }` *Security: PluginSecurity* **Parameters:** | Name | Type | Description | |------|------|-------------| | `document` | `ScriptDocument` | | | `changesArray` | `Variant` | | **ScriptEditorService.TextDocumentDidChange** ```lua --!nocheck -- Run the following code in the Command Bar local ScriptEditorService = game:GetService("ScriptEditorService") ScriptEditorService.TextDocumentDidChange:Connect(function(scriptDocument, changes) print("Changed", scriptDocument, changes) end) ``` ### Event: ScriptEditorService.TextDocumentDidClose **Signature:** `ScriptEditorService.TextDocumentDidClose(oldDocument: ScriptDocument)` Fires just before a [ScriptDocument](/docs/reference/engine/classes/ScriptDocument.md) object is destroyed, which happens right after the script editor closes. After this event fires, the [ScriptDocument](/docs/reference/engine/classes/ScriptDocument.md) enters a "Closed" state, and trying to call its methods throws an error. [ScriptDocument](/docs/reference/engine/classes/ScriptDocument.md) objects aren't reusable, even if the script editor reopens the same script. *Security: PluginSecurity* **Parameters:** | Name | Type | Description | |------|------|-------------| | `oldDocument` | `ScriptDocument` | | **ScriptEditorService.TextDocumentDidClose** ```lua --!nocheck -- Run the following code in the Command Bar local ScriptEditorService = game:GetService("ScriptEditorService") ScriptEditorService.TextDocumentDidClose:Connect(function(scriptDocument) print("Closed", scriptDocument) end) ``` ### Event: ScriptEditorService.TextDocumentDidOpen **Signature:** `ScriptEditorService.TextDocumentDidOpen(newDocument: ScriptDocument)` Fires just after a [ScriptDocument](/docs/reference/engine/classes/ScriptDocument.md) object is created and parented to the service, which happens right after the script editor opens. *Security: PluginSecurity* **Parameters:** | Name | Type | Description | |------|------|-------------| | `newDocument` | `ScriptDocument` | | **ScriptEditorService.TextDocumentDidOpen** ```lua --!nocheck -- Run the following code in the Command Bar local ScriptEditorService = game:GetService("ScriptEditorService") ScriptEditorService.TextDocumentDidOpen:Connect(function(scriptDocument) print("Opened", scriptDocument) end) ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ScriptProfilerService last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service --- # Class: ScriptProfilerService ## Methods ### Method: ScriptProfilerService:ClientRequestData **Signature:** `ScriptProfilerService:ClientRequestData(player: Player): ()` *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Player` | | | **Returns:** `()` ### Method: ScriptProfilerService:ClientStart **Signature:** `ScriptProfilerService:ClientStart(player: Player, frequency: int?): ()` *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Player` | | | | `frequency` | `int?` | | | **Returns:** `()` ### Method: ScriptProfilerService:ClientStop **Signature:** `ScriptProfilerService:ClientStop(player: Player): ()` *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Player` | | | **Returns:** `()` ### Method: ScriptProfilerService:DeserializeJSON **Signature:** `ScriptProfilerService:DeserializeJSON(jsonString: string?): Dictionary` *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `jsonString` | `string?` | | | **Returns:** `Dictionary` ### Method: ScriptProfilerService:ServerRequestData **Signature:** `ScriptProfilerService:ServerRequestData(): ()` *Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `()` ### Method: ScriptProfilerService:ServerStart **Signature:** `ScriptProfilerService:ServerStart(frequency: int?): ()` *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `frequency` | `int?` | | | **Returns:** `()` ### Method: ScriptProfilerService:ServerStop **Signature:** `ScriptProfilerService:ServerStop(): ()` *Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `()` ## Events ### Event: ScriptProfilerService.OnNewData **Signature:** `ScriptProfilerService.OnNewData(player: Player, jsonString: string)` *Security: PluginSecurity* **Parameters:** | Name | Type | Description | |------|------|-------------| | `player` | `Player` | | | `jsonString` | `string` | | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ScriptService last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service summary: "An unimplemented service that has zero functionality, in a similar manner to the `LocalWorkspace` service." --- # Class: ScriptService > An unimplemented service that has zero functionality, in a similar manner to > the `LocalWorkspace` service. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ScrollingFrame last_updated: 2026-06-29T19:34:08Z inherits: - GuiObject - GuiBase2d - GuiBase - Instance - Object type: class memory_category: Gui summary: "`ScrollingFrame` is a special Frame type with built-in scrolling interactivity and different ways to customize how the scrolling works." --- # Class: ScrollingFrame > `ScrollingFrame` is a special [Frame](/docs/reference/engine/classes/Frame.md) type with built-in scrolling > interactivity and different ways to customize how the scrolling works. ## Description `ScrollingFrame` is a special [Frame](/docs/reference/engine/classes/Frame.md) type with built-in scrolling interactivity and different ways to customize how the scrolling works. ![Example ScrollingFrame on the screen containing a tabbed category bar and a list of magical items for the player to consider purchasing.](/assets/ui/ui-objects/ScrollingFrame-Example.jpg) ## Properties ### Property: ScrollingFrame.AbsoluteCanvasSize ```json { "type": "Vector2", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "Unsafe", "category": "Scrolling", "capabilities": [ "UI" ] } ``` The size of the area that is scrollable, in offsets. This property is set to the maximum of the [CanvasSize](/docs/reference/engine/classes/ScrollingFrame.md) property and the size of the children if [AutomaticCanvasSize](/docs/reference/engine/classes/ScrollingFrame.md) is set to something other than [AutomaticSize.None](/docs/reference/engine/enums/AutomaticSize.md). ### Property: ScrollingFrame.AbsoluteWindowSize ```json { "type": "Vector2", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "Unsafe", "category": "Scrolling", "capabilities": [ "UI" ] } ``` The size of the frame, in offsets, without the scroll bars. ### Property: ScrollingFrame.AutomaticCanvasSize ```json { "type": "AutomaticSize", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Scrolling", "capabilities": [ "UI" ] } ``` This property is used to automatically size parent UI objects based on the size of its descendants. You can use this property to dynamically add text and other content to a `ScrollingFrame` at edit or run time and the size will adjust to fit that content. When this property is set to an [AutomaticSize](/docs/reference/engine/enums/AutomaticSize.md) value other than [None](/docs/reference/engine/enums/AutomaticSize.md), [AbsoluteCanvasSize](/docs/reference/engine/classes/ScrollingFrame.md) may resize depending on its child content. ### Property: ScrollingFrame.BottomImage ```json { "type": "ContentId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Scrolling", "capabilities": [ "UI" ] } ``` Image that displays on the bottom of a vertical scroll bar, or the right of a horizontal scroll bar (rotated 90° counterclockwise for a horizontal scroll bar). ![Diagram showing the three image asset elements which construct a scrolling frame's scroll bar.](/assets/ui/ui-objects/ScrollingFrame-Scroll-Bar-Elements.png) ### Property: ScrollingFrame.BottomImageContent ```json { "type": "Content", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Scrolling", "capabilities": [ "UI" ] } ``` Image that displays on the bottom of a vertical scroll bar, or the right of a horizontal scroll bar (rotated 90° counterclockwise for a horizontal scroll bar). ![Diagram showing the three image asset elements which construct a scrolling frame's scroll bar.](/assets/ui/ui-objects/ScrollingFrame-Scroll-Bar-Elements.png) ### Property: ScrollingFrame.CanvasPosition ```json { "type": "Vector2", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Scrolling", "capabilities": [ "UI" ] } ``` Reflects the **current** positional offset of the canvas within the frame, in pixels, and sets the position of scroll bars accordingly. Note that this property doesn't do anything if scroll bars aren't visible. ### Property: ScrollingFrame.CanvasSize ```json { "type": "UDim2", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Scrolling", "capabilities": [ "UI" ] } ``` Determines the size of the scrollable area. For an adaptive alternative based on the overall size of children within the `ScrollingFrame`, consider using [AutomaticCanvasSize](/docs/reference/engine/classes/ScrollingFrame.md). ### Property: ScrollingFrame.ElasticBehavior ```json { "type": "ElasticBehavior", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Scrolling", "capabilities": [ "UI" ] } ``` This property determines if and when elastic scrolling is allowed on touch‑enabled devices. Defaults to [WhenScrollable](/docs/reference/engine/enums/ElasticBehavior.md). ### Property: ScrollingFrame.HorizontalScrollBarInset ```json { "type": "ScrollBarInset", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Scrolling", "capabilities": [ "UI" ] } ``` Indicates whether [CanvasSize](/docs/reference/engine/classes/ScrollingFrame.md) is inset by [ScrollBarThickness](/docs/reference/engine/classes/ScrollingFrame.md) on the horizontal axis. ### Property: ScrollingFrame.MidImage ```json { "type": "ContentId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Scrolling", "capabilities": [ "UI" ] } ``` Image which spans the area between [TopImage](/docs/reference/engine/classes/ScrollingFrame.md) and [BottomImage](/docs/reference/engine/classes/ScrollingFrame.md) (rotated 90° counterclockwise for a horizontal scroll bar). This image automatically scales to fill the space between the cap segments. ![Diagram showing the three image asset elements which construct a scrolling frame's scroll bar.](/assets/ui/ui-objects/ScrollingFrame-Scroll-Bar-Elements.png) ### Property: ScrollingFrame.MidImageContent ```json { "type": "Content", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Scrolling", "capabilities": [ "UI" ] } ``` Image which spans the area between [TopImage](/docs/reference/engine/classes/ScrollingFrame.md) and [BottomImage](/docs/reference/engine/classes/ScrollingFrame.md) (rotated 90° counterclockwise for a horizontal scroll bar). This image automatically scales to fill the space between the cap segments. ![Diagram showing the three image asset elements which construct a scrolling frame's scroll bar.](/assets/ui/ui-objects/ScrollingFrame-Scroll-Bar-Elements.png) ### Property: ScrollingFrame.ScrollBarImageColor3 ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Scrolling", "capabilities": [ "UI" ] } ``` Determines how the scroll bar images ([TopImage](/docs/reference/engine/classes/ScrollingFrame.md), [MidImage](/docs/reference/engine/classes/ScrollingFrame.md), [BottomImage](/docs/reference/engine/classes/ScrollingFrame.md)) are colorized. When set to white, no colorization occurs. This property is useful for reusing image assets; if the source images are completely white with transparency, you can set the color of the entire scroll bar at once. ### Property: ScrollingFrame.ScrollBarImageTransparency ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Scrolling", "capabilities": [ "UI" ] } ``` Determines the opacity of the scroll bar images ([TopImage](/docs/reference/engine/classes/ScrollingFrame.md), [MidImage](/docs/reference/engine/classes/ScrollingFrame.md), [BottomImage](/docs/reference/engine/classes/ScrollingFrame.md)). A value of `0` is completely opaque and a value of `1` is completely transparent (invisible). ### Property: ScrollingFrame.ScrollBarThickness ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Scrolling", "capabilities": [ "UI" ] } ``` Thickness of the scroll bar in pixels; applies to both horizontal and vertical scroll bars. If set to `0`, no scroll bars are rendered. ### Property: ScrollingFrame.ScrollingDirection ```json { "type": "ScrollingDirection", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Scrolling", "capabilities": [ "UI" ] } ``` This property determines the direction(s) in which scrolling is allowed. If scrolling is disallowed in a direction, the associated scroll bar will not appear. Defaults to [ScrollingDirection.XY](/docs/reference/engine/enums/ScrollingDirection.md). ### Property: ScrollingFrame.ScrollingEnabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Scrolling", "capabilities": [ "UI" ] } ``` Determines whether scrolling is allowed on the frame. If `false`, no scroll bars will be rendered. ### Property: ScrollingFrame.TopImage ```json { "type": "ContentId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Scrolling", "capabilities": [ "UI" ] } ``` Image which displays on the top of a vertical scroll bar, or the left of a horizontal scroll bar (rotated 90° counterclockwise for a horizontal scroll bar). ![Diagram showing the three image asset elements which construct a scrolling frame's scroll bar.](/assets/ui/ui-objects/ScrollingFrame-Scroll-Bar-Elements.png) ### Property: ScrollingFrame.TopImageContent ```json { "type": "Content", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Scrolling", "capabilities": [ "UI" ] } ``` ### Property: ScrollingFrame.VerticalScrollBarInset ```json { "type": "ScrollBarInset", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Scrolling", "capabilities": [ "UI" ] } ``` Indicates whether [CanvasSize](/docs/reference/engine/classes/ScrollingFrame.md) is inset by [ScrollBarThickness](/docs/reference/engine/classes/ScrollingFrame.md) on the vertical axis. ### Property: ScrollingFrame.VerticalScrollBarPosition ```json { "type": "VerticalScrollBarPosition", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Scrolling", "capabilities": [ "UI" ] } ``` Indicates whether the vertical scroll bar is positioned to the left or right of the canvas. Defaults to [VerticalScrollBarPosition.Right](/docs/reference/engine/enums/VerticalScrollBarPosition.md). ## Methods ### Method: ScrollingFrame:GetScrollVelocity **Signature:** `ScrollingFrame:GetScrollVelocity(): Vector2` Returns a [Vector2](/docs/reference/engine/datatypes/Vector2.md) representing the current inertial scroll velocity after the user stops their input, for example lifts their finger off the `ScrollingFrame` on a touch-enabled device. Note that this method may only produce noticable data on an actual device or in an emulated scenario from Studio's **Device Emulator**. *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Returns:** `Vector2` ### Method: ScrollingFrame:ResetScrollVelocity **Signature:** `ScrollingFrame:ResetScrollVelocity(): ()` Resets the inertial scroll velocity of the `ScrollingFrame` to `0` on both axes. *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Returns:** `()` ## Inherited Members ### From [GuiObject](/docs/reference/engine/classes/GuiObject.md) - **Property `Active`** (`boolean`): Determines whether this UI element sinks input. - **Property `AnchorPoint`** (`Vector2`): Determines the origin point of a GuiObject, relative to its - **Property `AutomaticSize`** (`AutomaticSize`): Determines whether resizing occurs based on child content. - **Property `BackgroundColor`** (`BrickColor`): Determines the color of the GuiObject background. *(deprecated, hidden)* - **Property `BackgroundColor3`** (`Color3`): Determines the GuiObject background color. - **Property `BackgroundTransparency`** (`float`): Determines the transparency of the GuiObject background and - **Property `BorderColor`** (`BrickColor`): Determines the color of the GuiObject border. *(deprecated, hidden)* - **Property `BorderColor3`** (`Color3`): Determines the color of the GuiObject border. - **Property `BorderMode`** (`BorderMode`): Determines in what manner the GuiObject border is laid out - **Property `BorderSizePixel`** (`int`): Determines the pixel width of the GuiObject border. - **Property `ClipsDescendants`** (`boolean`): Determines if descendant GuiObjects outside of the - **Property `Draggable`** (`boolean`): Determines whether a GuiObject (and its descendants) can be *(deprecated)* - **Property `GuiState`** (`GuiState`): Determines whether the player's mouse is being actively pressed on the - **Property `InputSink`** (`InputSink`): - **Property `Interactable`** (`boolean`): Determines whether the GuiButton can be interacted with or not, or - **Property `LayoutOrder`** (`int`): Controls the sort order of the GuiObject when used with a - **Property `NextSelectionDown`** (`GuiObject`): Sets the GuiObject which will be selected when the gamepad - **Property `NextSelectionLeft`** (`GuiObject`): Sets the GuiObject which will be selected when the gamepad - **Property `NextSelectionRight`** (`GuiObject`): Sets the GuiObject which will be selected when the gamepad - **Property `NextSelectionUp`** (`GuiObject`): Sets the GuiObject which will be selected when the gamepad - **Property `Position`** (`UDim2`): Determines the pixel and scalar position of the GuiObject. - **Property `Rotation`** (`float`): Determines the number of degrees by which the GuiObject is - **Property `Selectable`** (`boolean`): Determine whether the GuiObject can be selected by a gamepad. - **Property `SelectionImageObject`** (`GuiObject`): Overrides the default selection adornment used for gamepads. - **Property `SelectionOrder`** (`int`): The order of GuiObjects selected by the gamepad UI - **Property `Size`** (`UDim2`): Determines the pixel and scalar size of the GuiObject. - **Property `SizeConstraint`** (`SizeConstraint`): Sets the Size axes that the GuiObject will - **Property `Transparency`** (`float`): A mixed property of *(hidden)* - **Property `Visible`** (`boolean`): Determines whether the GuiObject and its descendants will be - **Property `ZIndex`** (`int`): Determines the order in which a GuiObject renders relative to - **Method `TweenPosition(endPosition: UDim2, easingDirection?: EasingDirection, easingStyle?: EasingStyle, time?: float, override?: boolean, callback?: Function): boolean`**: Smoothly moves a GUI to a new UDim2. - **Method `TweenSize(endSize: UDim2, easingDirection?: EasingDirection, easingStyle?: EasingStyle, time?: float, override?: boolean, callback?: Function): boolean`**: Smoothly resizes a GuiObject to a new UDim2. - **Method `TweenSizeAndPosition(endSize: UDim2, endPosition: UDim2, easingDirection?: EasingDirection, easingStyle?: EasingStyle, time?: float, override?: boolean, callback?: Function): boolean`**: Smoothly moves a GUI to a new size and position. - **Event `DragBegin`**: Fired when a player begins dragging the object. *(deprecated)* - **Event `DragStopped`**: Fired when a player stops dragging the object. *(deprecated)* - **Event `InputBegan`**: Fired when a user begins interacting via a Human-Computer Interface device - **Event `InputChanged`**: Fired when a user changes how they're interacting via a Human-Computer - **Event `InputEnded`**: Fired when a user stops interacting via a Human-Computer Interface device - **Event `MouseEnter`**: Fires when a user moves their mouse into a GUI element. - **Event `MouseLeave`**: Fires when a user moves their mouse out of a GUI element. - **Event `MouseMoved`**: Fires whenever a user moves their mouse while it is inside a GUI element. - **Event `MouseWheelBackward`**: Fires when a user scrolls their mouse wheel back when the mouse is over a - **Event `MouseWheelForward`**: Fires when a user scrolls their mouse wheel forward when the mouse is over - **Event `SelectionGained`**: Fired when the GuiObject is being focused on with the Gamepad selector. - **Event `SelectionLost`**: Fired when the Gamepad selector stops focusing on the GuiObject. - **Event `TouchLongPress`**: Fires when the player starts, continues and stops long-pressing the UI - **Event `TouchPan`**: Fires when the player moves their finger on the UI element. - **Event `TouchPinch`**: Fires when the player performs a pinch or pull gesture using two fingers - **Event `TouchRotate`**: Fires when the player performs a rotation gesture using two fingers on the - **Event `TouchSwipe`**: Fires when the player performs a swipe gesture on the UI element. - **Event `TouchTap`**: Fires when the player performs a tap gesture on the UI element. ### From [GuiBase2d](/docs/reference/engine/classes/GuiBase2d.md) - **Property `AbsolutePosition`** (`Vector2`): Describes the actual screen position of a GuiBase2d element, in - **Property `AbsoluteRotation`** (`float`): Describes the actual screen rotation of a GuiBase2d element, in - **Property `AbsoluteSize`** (`Vector2`): Describes the actual screen size of a GuiBase2d element, in - **Property `AutoLocalize`** (`boolean`): When set to `true`, localization will be applied to this GuiBase2d - **Property `Localize`** (`boolean`): Automatically set to true when a localization table's *(deprecated, hidden)* - **Property `RootLocalizationTable`** (`LocalizationTable`): A reference to a LocalizationTable to be used to apply automated - **Property `SelectionBehaviorDown`** (`SelectionBehavior`): Customizes gamepad selection behavior in the down direction. - **Property `SelectionBehaviorLeft`** (`SelectionBehavior`): Customizes gamepad selection behavior in the left direction. - **Property `SelectionBehaviorRight`** (`SelectionBehavior`): Customizes gamepad selection behavior in the right direction. - **Property `SelectionBehaviorUp`** (`SelectionBehavior`): Customizes gamepad selection behavior in the up direction. - **Property `SelectionGroup`** (`boolean`): Allows customization of gamepad selection movement. - **Event `SelectionChanged`**: Fires when the gamepad selection moves to, leaves, or changes within the ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Seat last_updated: 2026-06-29T19:34:08Z inherits: - Part - FormFactorPart - BasePart - PVInstance - Instance - Object type: class memory_category: BaseParts summary: "A type of BasePart that characters can 'sit' in. When a character touches an enabled Seat object, it will be attached to the part by a Weld and the default character scripts will play a sitting animation." --- # Class: Seat > A type of [BasePart](/docs/reference/engine/classes/BasePart.md) that characters can 'sit' in. When a character > touches an enabled Seat object, it will be attached to the part by a > [Weld](/docs/reference/engine/classes/Weld.md) and the default character scripts will play a sitting animation. ## Description A type of [BasePart](/docs/reference/engine/classes/BasePart.md) that a player character can 'sit' in. When a character touches an enabled Seat object, it will be attached to the part by a [Weld](/docs/reference/engine/classes/Weld.md) and the default character scripts will play a sitting animation. ## How do Seats work? When a model containing a [Humanoid](/docs/reference/engine/classes/Humanoid.md) and a [BasePart](/docs/reference/engine/classes/BasePart.md) called 'HumanoidRootPart' (generally a player character) touches a seat, a [Weld](/docs/reference/engine/classes/Weld.md) is created between the seat and the part. The [C0](/docs/reference/engine/classes/JointInstance.md) and [C1](/docs/reference/engine/classes/JointInstance.md) properties are configured so that the character is welded 2 studs above the seat. This weld is named 'SeatWeld' and parented to the seat. When sitting the [Seat.Occupant](/docs/reference/engine/classes/Seat.md) property is set to the [Humanoid](/docs/reference/engine/classes/Humanoid.md) that is 'sitting' in the seat. Furthermore the [Humanoid.SeatPart](/docs/reference/engine/classes/Humanoid.md) property of the humanoid is set to the seat. A character can also be forced to sit in a seat using the [Seat:Sit()](/docs/reference/engine/classes/Seat.md) function. There are two ways for a character to get out of a seat. When a player jumps, they are removed from the seat. However this can also be done manually by destroying the seat weld, for example: seat:FindFirstChild("SeatWeld"):Destroy() Note seats have a cooldown (currently 3 seconds) that is on a per-character per-seat basis. This means once a character has gotten out of a seat they cannot sit back on the same seat for 3 seconds. This cooldown behavior may change and should not be relied upon by developers. ## What can Seats be used for? Seats have a diverse range of uses, ranging from the obvious to the more unconventional. - Creating chairs or benches without the need for any programming - Allowing characters to 'sit' in moving objects such as vehicles without getting flung around - Creating interfaces that are controlled by the character in the seat using the [Seat.Occupant](/docs/reference/engine/classes/Seat.md) property ## Code Samples **Detecting Seat Occupant** This code sample includes a demonstration of how the [Seat.Occupant](/docs/reference/engine/classes/Seat.md) property can be used to track which player is sitting in a seat and when they sit down or sit up. ```lua local Players = game:GetService("Players") local seat = Instance.new("Seat") seat.Anchored = true seat.Position = Vector3.new(0, 1, 0) seat.Parent = workspace local currentPlayer = nil local function onOccupantChanged() local humanoid = seat.Occupant if humanoid then local character = humanoid.Parent local player = Players:GetPlayerFromCharacter(character) if player then print(player.Name .. " has sat down") currentPlayer = player return end end if currentPlayer then print(currentPlayer.Name .. " has got up") currentPlayer = nil end end seat:GetPropertyChangedSignal("Occupant"):Connect(onOccupantChanged) ``` ## Properties ### Property: Seat.Disabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Control", "capabilities": [ "Basic", "AvatarBehavior" ], "simulationAccess": true } ``` Whether or not the seat is usable. If set to true, the seat will act as a normal part. ### Property: Seat.Occupant ```json { "type": "Humanoid", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Control", "capabilities": [ "Basic", "AvatarBehavior" ], "simulationAccess": true } ``` The humanoid that is sitting in the seat ## Methods ### Method: Seat:Sit **Signature:** `Seat:Sit(humanoid: Instance): ()` Forces the character with the specified [Humanoid](/docs/reference/engine/classes/Humanoid.md) to sit in the Seat. *Security: None · Thread Safety: Unsafe · Capabilities: Basic, AvatarBehavior* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `humanoid` | `Instance` | | | **Returns:** `()` ## Inherited Members ### From [Part](/docs/reference/engine/classes/Part.md) - **Property `Shape`** (`PartType`): Sets the overall shape of the object. ### From [FormFactorPart](/docs/reference/engine/classes/FormFactorPart.md) - **Property `FormFactor`** (`FormFactor`): *(deprecated)* - **Property `formFactor`** (`FormFactor`): *(deprecated, hidden)* ### From [BasePart](/docs/reference/engine/classes/BasePart.md) - **Property `Anchored`** (`boolean`): Determines whether a part is immovable by physics. - **Property `AssemblyAngularVelocity`** (`Vector3`): The angular velocity of the part's assembly. - **Property `AssemblyCenterOfMass`** (`Vector3`): The center of mass of the part's assembly in world space. - **Property `AssemblyLinearVelocity`** (`Vector3`): The linear velocity of the part's assembly. - **Property `AssemblyMass`** (`float`): The total mass of the part's assembly. - **Property `AssemblyRootPart`** (`BasePart`): A reference to the root part of the assembly. - **Property `AudioCanCollide`** (`boolean`): Determines whether the part will physically interact with audio - **Property `BackParamA`** (`float`): Determines the first parameter for the SurfaceType on the Back face of a *(deprecated, hidden)* - **Property `BackParamB`** (`float`): Determines the second parameter for the SurfaceType on the Back face of a *(deprecated, hidden)* - **Property `BackSurface`** (`SurfaceType`): Determines the type of surface for the back face of a part. - **Property `BackSurfaceInput`** (`InputType`): Determines the kind of input for the Back face of a part. *(deprecated, hidden)* - **Property `BottomParamA`** (`float`): Determines the first parameter for the SurfaceType on the Bottom face of a *(deprecated, hidden)* - **Property `BottomParamB`** (`float`): Determines the second parameter for the SurfaceType on the Bottom face of *(deprecated, hidden)* - **Property `BottomSurface`** (`SurfaceType`): Determines the type of surface for the bottom face of a part. - **Property `BottomSurfaceInput`** (`InputType`): Determines the kind of input for the Bottom face of a part. *(deprecated, hidden)* - **Property `BrickColor`** (`BrickColor`): Determines the color of a part. - **Property `brickColor`** (`BrickColor`): *(deprecated)* - **Property `CanCollide`** (`boolean`): Determines whether a part may collide with other parts. - **Property `CanQuery`** (`boolean`): Determines whether the part is considered during spatial query operations. - **Property `CanTouch`** (`boolean`): Determines if Touched and - **Property `CastShadow`** (`boolean`): Determines whether or not a part casts a shadow. - **Property `CenterOfMass`** (`Vector3`): Describes the world position in which a part's center of mass is located. - **Property `CFrame`** (`CFrame`): Determines the position and orientation of the BasePart in the - **Property `CollisionGroup`** (`string`): Describes the name of a part's collision group. - **Property `CollisionGroupId`** (`int`): Describes the automatically set ID number of a part's collision group. *(deprecated)* - **Property `Color`** (`Color3`): Determines the color of a part. - **Property `CurrentPhysicalProperties`** (`PhysicalProperties`): Indicates the current physical properties of the part. - **Property `CustomPhysicalProperties`** (`PhysicalProperties`): Determines several physical properties of a part. - **Property `Elasticity`** (`float`): Used to control the Elasticity of the part, but it no longer does *(deprecated, hidden)* - **Property `EnableFluidForces`** (`boolean`): Used to enable or disable aerodynamic forces on parts and assemblies. - **Property `ExtentsCFrame`** (`CFrame`): The CFrame of the physical extents of the BasePart. - **Property `ExtentsSize`** (`Vector3`): The actual physical size of the BasePart as regarded by the - **Property `Friction`** (`float`): Used to control the Friction of the part, but now it no longer does *(deprecated, hidden)* - **Property `FrontParamA`** (`float`): Determines the first parameter for the SurfaceType on the Front face of a *(deprecated, hidden)* - **Property `FrontParamB`** (`float`): Determines the second parameter for the SurfaceType on the Front face of a *(deprecated, hidden)* - **Property `FrontSurface`** (`SurfaceType`): Determines the type of surface for the front face of a part. - **Property `FrontSurfaceInput`** (`InputType`): Determines the kind of input for the Front face of a part (-Z direction). *(deprecated, hidden)* - **Property `LeftParamA`** (`float`): Determines the first parameter for the SurfaceType on the Left face of a *(deprecated, hidden)* - **Property `LeftParamB`** (`float`): Determines the second parameter for the SurfaceType on the Left face of a *(deprecated, hidden)* - **Property `LeftSurface`** (`SurfaceType`): Determines the type of surface for the left face of a part. - **Property `LeftSurfaceInput`** (`InputType`): Determines the kind of input for the Left face of a part. *(deprecated, hidden)* - **Property `LocalTransparencyModifier`** (`float`): Determines a multiplier for BasePart.Transparency that is only *(hidden)* - **Property `Locked`** (`boolean`): Determines whether a part is selectable in Studio. - **Property `Mass`** (`float`): Describes the mass of the part, the product of its density and volume. - **Property `Massless`** (`boolean`): Determines whether the part contributes to the total mass or inertia of - **Property `Material`** (`Material`): Determines the texture and default physical properties of a part. - **Property `MaterialVariant`** (`string`): The name of MaterialVariant. - **Property `Orientation`** (`Vector3`): Describes the rotation of the part in the world. *(hidden)* - **Property `PivotOffset`** (`CFrame`): Specifies the offset of the part's pivot from its CFrame. - **Property `Position`** (`Vector3`): Describes the position of the part in the world. *(hidden)* - **Property `ReceiveAge`** (`float`): Time since last recorded physics update. *(hidden)* - **Property `Reflectance`** (`float`): Determines how much a part reflects the skybox. - **Property `ResizeableFaces`** (`Faces`): Describes the faces on which a part may be resized. - **Property `ResizeIncrement`** (`int`): Describes the smallest change in size allowable by the - **Property `RightParamA`** (`float`): Determines the first parameter for the SurfaceType on the Right face of a *(deprecated, hidden)* - **Property `RightParamB`** (`float`): Determines the second parameter for the SurfaceType on the Right face of a *(deprecated, hidden)* - **Property `RightSurface`** (`SurfaceType`): Determines the type of surface for the right face of a part. - **Property `RightSurfaceInput`** (`InputType`): Determines the kind of input for the Right face of a part (-X direction). *(deprecated, hidden)* - **Property `RootPriority`** (`int`): The main rule in determining the root part of an assembly. - **Property `Rotation`** (`Vector3`): The rotation of the part in degrees for the three axes. - **Property `RotVelocity`** (`Vector3`): Determines a part's change in orientation over time. *(deprecated, hidden)* - **Property `Size`** (`Vector3`): Determines the dimensions of a part (length, width, height). - **Property `SpecificGravity`** (`float`): The ratio of the part's density to the density of water determined by the *(deprecated)* - **Property `TopParamA`** (`float`): Determines the first parameter for the SurfaceType on the Top face of a *(deprecated, hidden)* - **Property `TopParamB`** (`float`): Determines the second parameter for the SurfaceType on the Top face of a *(deprecated, hidden)* - **Property `TopSurface`** (`SurfaceType`): Determines the type of surface for the top face of a part. - **Property `TopSurfaceInput`** (`InputType`): Determines the kind of input for the Top face of a part (+Y direction). *(deprecated, hidden)* - **Property `Transparency`** (`float`): Determines how much a part can be seen through (the inverse of part - **Property `Velocity`** (`Vector3`): Determines a part's change in position over time. *(deprecated, hidden)* - **Method `AngularAccelerationToTorque(angAcceleration: Vector3, angVelocity?: Vector3): Vector3`**: Returns the torque needed to achieve a given angular acceleration on this - **Method `ApplyAngularImpulse(impulse: Vector3): ()`**: Apply an angular impulse to the assembly. - **Method `ApplyImpulse(impulse: Vector3): ()`**: Apply an impulse to the assembly at the assembly's - **Method `ApplyImpulseAtPosition(impulse: Vector3, position: Vector3): ()`**: Apply an impulse to the assembly at specified position. - **Method `BreakJoints(): ()`**: Breaks any surface connection with any adjacent part, including *(deprecated)* - **Method `breakJoints(): ()`**: *(deprecated)* - **Method `CanCollideWith(part: BasePart): boolean`**: Returns whether the parts can collide with each other. - **Method `CanSetNetworkOwnership(): Tuple`**: Checks whether you can set a part's network ownership. - **Method `GetClosestPointOnSurface(position: Vector3): Vector3`**: Returns the closest point on the part's surface to the given point. - **Method `GetConnectedParts(recursive?: boolean): List`**: Returns a table of parts connected to the object by any kind of rigid - **Method `GetJoints(): Instances`**: Return all Joints or Constraints that is connected to this Part. - **Method `GetMass(): float`**: Returns the value of the Mass property. - **Method `getMass(): float`**: *(deprecated)* - **Method `GetNetworkOwner(): Instance`**: Returns the current player who is the network owner of this part, or `nil` - **Method `GetNetworkOwnershipAuto(): boolean`**: Returns true if the game engine automatically decides the network owner - **Method `GetNoCollisionConstraints(): Instances`**: - **Method `GetRenderCFrame(): CFrame`**: OBSOLETE. Returns a CFrame describing where the part is being rendered at. *(deprecated)* - **Method `GetRootPart(): Instance`**: Returns the base part of an assembly of parts. *(deprecated)* - **Method `GetTouchingParts(): Instances`**: Returns a table of all BasePart.CanCollide true parts that - **Method `GetVelocityAtPosition(position: Vector3): Vector3`**: Returns the linear velocity of the part's assembly at the given position - **Method `IntersectAsync(parts: Instances, collisionfidelity?: CollisionFidelity, renderFidelity?: RenderFidelity): Instance`**: Note: It is highly recommended to use the newer - **Method `IsGrounded(): boolean`**: Returns true if the object is connected to a part that will hold it in - **Method `MakeJoints(): ()`**: Creates a joint on any side of the object that has a surface ID that can *(deprecated)* - **Method `makeJoints(): ()`**: *(deprecated)* - **Method `Resize(normalId: NormalId, deltaAmount: int): boolean`**: Changes the size of an object just like using the Studio resize tool. - **Method `resize(normalId: NormalId, deltaAmount: int): boolean`**: *(deprecated)* - **Method `SetNetworkOwner(playerInstance?: Player): ()`**: Sets the given player as network owner for this and all connected parts. - **Method `SetNetworkOwnershipAuto(): ()`**: Lets the game engine dynamically decide who will handle the part's physics - **Method `SubtractAsync(parts: Instances, collisionfidelity?: CollisionFidelity, renderFidelity?: RenderFidelity): Instance`**: Note: It is highly recommended to use the newer - **Method `TorqueToAngularAcceleration(torque: Vector3, angVelocity?: Vector3): Vector3`**: Returns the angular acceleration that would result from applying a given - **Method `UnionAsync(parts: Instances, collisionfidelity?: CollisionFidelity, renderFidelity?: RenderFidelity): Instance`**: Note: It is highly recommended to use the newer - **Event `LocalSimulationTouched`**: *(deprecated)* - **Event `OutfitChanged`**: *(deprecated)* - **Event `StoppedTouching`**: *(deprecated)* - **Event `Touched`**: Fires when a part touches another part as a result of physical movement. - **Event `TouchEnded`**: Fires when a part stops touching another part as a result of physical ### From [PVInstance](/docs/reference/engine/classes/PVInstance.md) - **Property `Origin`** (`CFrame`): - **Property `Pivot Offset`** (`CFrame`): - **Method `GetPivot(): CFrame`**: Gets the pivot of a PVInstance. - **Method `PivotTo(targetCFrame: CFrame): ()`**: Transforms the PVInstance along with all of its descendant ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Selection last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service summary: "The Selection service controls the Instances that are selected in Roblox Studio." --- # Class: Selection > The Selection service controls the [Instances](/docs/reference/engine/classes/Instance.md) that are > selected in Roblox Studio. ## Description The Selection service controls the [Instances](/docs/reference/engine/classes/Instance.md) that are selected in Roblox Studio. This service is particularly useful when developing [Plugins](/docs/reference/engine/classes/Plugin.md), as it allows the developer to access and manipulate the current selection. Currently selected [Instances](/docs/reference/engine/classes/Instance.md) can be obtained and set using the [Selection:Get()](/docs/reference/engine/classes/Selection.md) and [Selection:Set()](/docs/reference/engine/classes/Selection.md) functions. The [Selection.SelectionChanged](/docs/reference/engine/classes/Selection.md) event fires whenever the current selection changes. For more information on using [Selection](/docs/reference/engine/classes/Selection.md) and [Plugins](/docs/reference/engine/classes/Plugin.md), see [Plugin](/docs/reference/engine/classes/Plugin.md). Selection is also often used in the command bar, to set hidden properties or run functions for selected [Instances](/docs/reference/engine/classes/Instance.md). Note this class only applies to Roblox Studio and has no applicability to games. ## Code Samples **Selection** The following code sample, when used in a plugin or the command bar, will rotate currently selected `BasePart`s. ```lua local Selection = game:GetService("Selection") for _, object in pairs(Selection:Get()) do if object:IsA("BasePart") then object.CFrame = object.CFrame * CFrame.Angles(0, math.pi / 2, 0) end end ``` ## Properties ### Property: Selection.SelectionThickness ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` The thickness of the selection highlight outline drawn around selected [Instances](/docs/reference/engine/classes/Instance.md) in Roblox Studio's viewport. This read-only property returns the current outline thickness as a float. The default value is `0.02`. The value is controlled by Roblox Studio and cannot be changed through the scripting API. This property can be read in [Plugins](/docs/reference/engine/classes/Plugin.md) to query the current selection visualization setting, such as when drawing custom overlays that need to match the built-in selection appearance. ## Methods ### Method: Selection:Add **Signature:** `Selection:Add(instancesToAdd: Instances): ()` Adds the [Instances](/docs/reference/engine/classes/Instance.md) in the given array to the current selection. [Instances](/docs/reference/engine/classes/Instance.md) that are already selected remain selected. Calling this function fires the [Selection.SelectionChanged](/docs/reference/engine/classes/Selection.md) event if any new [Instances](/docs/reference/engine/classes/Instance.md) are added to the selection. Note this function can only be used in [Plugins](/docs/reference/engine/classes/Plugin.md) or the command bar. For setting the selection to an entirely new set of instances, see [Selection:Set()](/docs/reference/engine/classes/Selection.md). For removing instances from the current selection, see [Selection:Remove()](/docs/reference/engine/classes/Selection.md). *Security: PluginSecurity · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `instancesToAdd` | `Instances` | | An array of [Instances](/docs/reference/engine/classes/Instance.md) to add to the current selection. | **Returns:** `()` ### Method: Selection:Get **Signature:** `Selection:Get(): Instances` Returns an array of currently selected [Instances](/docs/reference/engine/classes/Instance.md) in Roblox Studio. If no [Instances](/docs/reference/engine/classes/Instance.md) are selected, the array returned will be empty. This function can be used in conjunction with the [Selection.SelectionChanged](/docs/reference/engine/classes/Selection.md) event to get the selection whenever it changes. Note, this function can only be used in [Plugins](/docs/reference/engine/classes/Plugin.md) or the command line. For changing the current selection, please see [Selection:Set()](/docs/reference/engine/classes/Selection.md). *Security: PluginSecurity · Thread Safety: Unsafe · Capabilities: Basic* **Returns:** `Instances` — An array of currently selected [Instances](/docs/reference/engine/classes/Instance.md). **Selection** The following code sample, when used in a plugin or the command bar, will rotate currently selected `BasePart`s. ```lua local Selection = game:GetService("Selection") for _, object in pairs(Selection:Get()) do if object:IsA("BasePart") then object.CFrame = object.CFrame * CFrame.Angles(0, math.pi / 2, 0) end end ``` **Selection.SelectionChanged** This example prints the number of selected items whenever SelectionChanged is fired: ```lua local selection = game:GetService("Selection") selection.SelectionChanged:Connect(function() print("Selection contains " .. #selection:Get() .. " items.") end) ``` ### Method: Selection:Remove **Signature:** `Selection:Remove(instancesToRemove: Instances): ()` Removes the [Instances](/docs/reference/engine/classes/Instance.md) in the given array from the current selection. [Instances](/docs/reference/engine/classes/Instance.md) that are not currently selected are ignored. Calling this function fires the [Selection.SelectionChanged](/docs/reference/engine/classes/Selection.md) event if any [Instances](/docs/reference/engine/classes/Instance.md) are removed from the selection. Note this function can only be used in [Plugins](/docs/reference/engine/classes/Plugin.md) or the command bar. For adding instances to the current selection, see [Selection:Add()](/docs/reference/engine/classes/Selection.md). For replacing the selection entirely, see [Selection:Set()](/docs/reference/engine/classes/Selection.md). *Security: PluginSecurity · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `instancesToRemove` | `Instances` | | An array of [Instances](/docs/reference/engine/classes/Instance.md) to remove from the current selection. | **Returns:** `()` ### Method: Selection:Set **Signature:** `Selection:Set(selection: Instances): ()` Sets the currently selected objects in Roblox Studio to [Instances](/docs/reference/engine/classes/Instance.md) in the given array. Calling this function will cause the [Selection.SelectionChanged](/docs/reference/engine/classes/Selection.md) event to fire, unless the new selection set is identical to the previous selection. Note this function overwrites the existing selection. However, using [Selection:Get()](/docs/reference/engine/classes/Selection.md) an [Instance](/docs/reference/engine/classes/Instance.md) can be added to the existing selection like so: ``` local selected = Selection:Get() table.insert(selected, object) Selection:Set(selected) ``` *Security: PluginSecurity · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `selection` | `Instances` | | An array of [Instances](/docs/reference/engine/classes/Instance.md) to set the current selection to. | **Returns:** `()` **Selection Set** This code sample will select every `BasePart` in the workspace that is Bright red. ```lua local Selection = game:GetService("Selection") local newSelection = {} for _, object in pairs(workspace:GetDescendants()) do if object:IsA("BasePart") and object.BrickColor == BrickColor.new("Bright red") then table.insert(newSelection, object) end end Selection:Set(newSelection) ``` ## Events ### Event: Selection.SelectionChanged **Signature:** `Selection.SelectionChanged()` Fires when the [Instances](/docs/reference/engine/classes/Instance.md) selected in Roblox Studio changes. Note this event does not give the new selection. Developers will need to use the [Selection:Get()](/docs/reference/engine/classes/Selection.md) function to obtain the current selection. Although this event can be used outside of plugins and the command bar, it only applies to the selection in Roblox Studio and therefore has no functionality outside of Studio. To change the selection use the [Selection:Set()](/docs/reference/engine/classes/Selection.md) function. *Security: None · Capabilities: Basic* **Selection.SelectionChanged** This example prints the number of selected items whenever SelectionChanged is fired: ```lua local selection = game:GetService("Selection") selection.SelectionChanged:Connect(function() print("Selection contains " .. #selection:Get() .. " items.") end) ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: SelectionBox last_updated: 2026-06-29T19:34:08Z inherits: - InstanceAdornment - GuiBase3d - GuiBase - Instance - Object type: class memory_category: Instances summary: "Renders a 3D box around its Adornee." --- # Class: SelectionBox > Renders a 3D box around its [Adornee](/docs/reference/engine/classes/PVAdornment.md). ## Description `SelectionBox` renders a 3D box around its [Adornee](/docs/reference/engine/classes/PVAdornment.md) when it is a descendant of the [Workspace](/docs/reference/engine/classes/Workspace.md) or anywhere where GUI objects are rendered. The box's geometry consists of rectangular prisms forming an outline/wireframe in addition to a surface for each of its faces. `SelectionBox` does not capture any form of input; it is solely a visual effect. For an outline/fill overlay effect which covers non‑primitive geometry such as that of a [MeshPart](/docs/reference/engine/classes/MeshPart.md), see [Highlight](/docs/reference/engine/classes/Highlight.md). ## Properties ### Property: SelectionBox.LineThickness ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic", "UI" ] } ``` `LineThickness` determines the thickness of the box's outlines, measured in studs. If set to `0`, the outline will not be visible at all. ### Property: SelectionBox.SurfaceColor3 ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic", "UI" ] } ``` `SurfaceColor3` determines the color of the box's surfaces. See also [Color3](/docs/reference/engine/classes/SelectionBox.md), a property of the superclass [GuiBase3d](/docs/reference/engine/classes/GuiBase3d.md) which controls the color of the outline rather than the surface faces. ### Property: SelectionBox.SurfaceTransparency ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic", "UI" ] } ``` `SurfaceTransparency` determines the transparency of the box's surfaces, similar to the way [BasePart.Transparency](/docs/reference/engine/classes/BasePart.md) works. By default, this property is `1` which causes the surfaces to be invisible. See also [Transparency](/docs/reference/engine/classes/SelectionBox.md), a property of the superclass [GuiBase3d](/docs/reference/engine/classes/GuiBase3d.md) which controls the transparency of the outline rather than the surfaces. ### Property: SelectionBox.SurfaceColor *(hidden)* ```json { "type": "BrickColor", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic", "UI" ] } ``` > **Deprecated:** This property is deprecated in favor of [SurfaceColor3](/docs/reference/engine/classes/SelectionBox.md) which uses the more precise [Color3](/docs/reference/engine/datatypes/Color3.md) data type instead of a [BrickColor](/docs/reference/engine/datatypes/BrickColor.md) used by this property. A [BrickColor](/docs/reference/engine/datatypes/BrickColor.md) version of [SurfaceColor3](/docs/reference/engine/classes/SelectionBox.md). ## Inherited Members ### From [InstanceAdornment](/docs/reference/engine/classes/InstanceAdornment.md) - **Property `Adornee`** (`Instance`): Which Instance to adorn. ### From [GuiBase3d](/docs/reference/engine/classes/GuiBase3d.md) - **Property `Color`** (`BrickColor`): Sets the color of a GUI object. *(deprecated, hidden)* - **Property `Color3`** (`Color3`): Sets the color of this GuiBase3d object. - **Property `Transparency`** (`float`): Sets the transparency of this GuiBase3d object. - **Property `Visible`** (`boolean`): Determines whether this GuiBase3d object and its descendants will ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: SelectionHighlightManager last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service --- # Class: SelectionHighlightManager ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: SelectionLasso last_updated: 2026-06-29T19:34:08Z inherits: - GuiBase3d - GuiBase - Instance - Object type: class memory_category: Instances tags: - NotCreatable summary: "Abstract class for SelectionPartLasso and SelectionPartLasso." --- # Class: SelectionLasso > Abstract class for [SelectionPartLasso](/docs/reference/engine/classes/SelectionPartLasso.md) and [SelectionPartLasso](/docs/reference/engine/classes/SelectionPartLasso.md). ## Description The SelectionLasso class is an abstract class of which the inheritors are able to be attached to an object of the [Humanoid](/docs/reference/engine/classes/Humanoid.md) class or the [BasePart](/docs/reference/engine/classes/BasePart.md) class. They can also be attached to a point in the tridimensional space indicated by a Vector3 value. ## Properties ### Property: SelectionLasso.Humanoid ```json { "type": "Humanoid", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` The Humanoid that the Lasso belongs to, and will come from. ## Inherited Members ### From [GuiBase3d](/docs/reference/engine/classes/GuiBase3d.md) - **Property `Color`** (`BrickColor`): Sets the color of a GUI object. *(deprecated, hidden)* - **Property `Color3`** (`Color3`): Sets the color of this GuiBase3d object. - **Property `Transparency`** (`float`): Sets the transparency of this GuiBase3d object. - **Property `Visible`** (`boolean`): Determines whether this GuiBase3d object and its descendants will ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: SelectionPartLasso last_updated: 2026-06-29T19:34:08Z inherits: - SelectionLasso - GuiBase3d - GuiBase - Instance - Object type: class memory_category: Instances tags: - Deprecated summary: "An instance used to display a \"lasso\" between a Humanoid Torso and a BasePart." --- # Class: SelectionPartLasso > An instance used to display a "lasso" between a [Humanoid](/docs/reference/engine/classes/Humanoid.md) Torso and a > [BasePart](/docs/reference/engine/classes/BasePart.md). ## Description An instance used to display a "lasso" between a [Humanoid](/docs/reference/engine/classes/Humanoid.md) Torso and a [BasePart](/docs/reference/engine/classes/BasePart.md). It should be noted that the [GuiBase3d.Transparency](/docs/reference/engine/classes/GuiBase3d.md) property doesn't currently work. ## Properties ### Property: SelectionPartLasso.Part ```json { "type": "BasePart", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic", "UI" ] } ``` Sets the target of the lasso object. ## Inherited Members ### From [SelectionLasso](/docs/reference/engine/classes/SelectionLasso.md) - **Property `Humanoid`** (`Humanoid`): The Humanoid that the Lasso belongs to, and will come from. ### From [GuiBase3d](/docs/reference/engine/classes/GuiBase3d.md) - **Property `Color`** (`BrickColor`): Sets the color of a GUI object. *(deprecated, hidden)* - **Property `Color3`** (`Color3`): Sets the color of this GuiBase3d object. - **Property `Transparency`** (`float`): Sets the transparency of this GuiBase3d object. - **Property `Visible`** (`boolean`): Determines whether this GuiBase3d object and its descendants will ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: SelectionPointLasso last_updated: 2026-06-29T19:34:08Z inherits: - SelectionLasso - GuiBase3d - GuiBase - Instance - Object type: class memory_category: Instances tags: - Deprecated --- # Class: SelectionPointLasso ## Description A 3D GUI object which displays a lasso between the defined Humanoid and a given Vector3 point. ## Properties ### Property: SelectionPointLasso.Point ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic", "UI" ] } ``` Sets the Vector3 target of the lasso object. ## Inherited Members ### From [SelectionLasso](/docs/reference/engine/classes/SelectionLasso.md) - **Property `Humanoid`** (`Humanoid`): The Humanoid that the Lasso belongs to, and will come from. ### From [GuiBase3d](/docs/reference/engine/classes/GuiBase3d.md) - **Property `Color`** (`BrickColor`): Sets the color of a GUI object. *(deprecated, hidden)* - **Property `Color3`** (`Color3`): Sets the color of this GuiBase3d object. - **Property `Transparency`** (`float`): Sets the transparency of this GuiBase3d object. - **Property `Visible`** (`boolean`): Determines whether this GuiBase3d object and its descendants will ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: SelectionSphere last_updated: 2026-06-29T19:34:08Z inherits: - PVAdornment - GuiBase3d - GuiBase - Instance - Object type: class memory_category: Instances summary: "Renders a 3D sphere around its Adornee." --- # Class: SelectionSphere > Renders a 3D sphere around its [Adornee](/docs/reference/engine/classes/PVAdornment.md). ## Description `SelectionSphere` renders a 3D sphere around its [Adornee](/docs/reference/engine/classes/PVAdornment.md) when it is a descendant of the [Workspace](/docs/reference/engine/classes/Workspace.md) or anywhere where GUI objects are rendered. The sphere's geometry consists of a ring/outline in addition to a surface. `SelectionSphere` does not capture any form of input; it is solely a visual effect. For an outline/fill overlay effect which covers non‑primitive geometry such as that of a [MeshPart](/docs/reference/engine/classes/MeshPart.md), see [Highlight](/docs/reference/engine/classes/Highlight.md). ## Properties ### Property: SelectionSphere.SurfaceColor3 ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic", "UI" ] } ``` `SurfaceColor3` determines the color of the sphere's surface. See also [Color3](/docs/reference/engine/classes/SelectionSphere.md), a property of the superclass [GuiBase3d](/docs/reference/engine/classes/GuiBase3d.md) which controls the color of the outline rather than the surface faces. ### Property: SelectionSphere.SurfaceTransparency ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic", "UI" ] } ``` `SurfaceTransparency` determines the transparency of the sphere's surface, similar to the way [BasePart.Transparency](/docs/reference/engine/classes/BasePart.md) works. By default, this property is `1` which causes the surfaces to be invisible. See also [Transparency](/docs/reference/engine/classes/SelectionSphere.md), a property of the superclass [GuiBase3d](/docs/reference/engine/classes/GuiBase3d.md) which controls the transparency of the outline rather than the surface. ### Property: SelectionSphere.SurfaceColor *(hidden)* ```json { "type": "BrickColor", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic", "UI" ] } ``` > **Deprecated:** This property is deprecated in favor of [SurfaceColor3](/docs/reference/engine/classes/SelectionSphere.md) which uses the more precise [Color3](/docs/reference/engine/datatypes/Color3.md) data type instead of a [BrickColor](/docs/reference/engine/datatypes/BrickColor.md) used by this property. A [BrickColor](/docs/reference/engine/datatypes/BrickColor.md) version of [SurfaceColor3](/docs/reference/engine/classes/SelectionBox.md). ## Inherited Members ### From [PVAdornment](/docs/reference/engine/classes/PVAdornment.md) - **Property `Adornee`** (`PVInstance`): The PVInstance which this PVAdornment is attached to. ### From [GuiBase3d](/docs/reference/engine/classes/GuiBase3d.md) - **Property `Color`** (`BrickColor`): Sets the color of a GUI object. *(deprecated, hidden)* - **Property `Color3`** (`Color3`): Sets the color of this GuiBase3d object. - **Property `Transparency`** (`float`): Sets the transparency of this GuiBase3d object. - **Property `Visible`** (`boolean`): Determines whether this GuiBase3d object and its descendants will ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: SelfViewConfiguration last_updated: 2026-06-29T19:34:08Z inherits: - BaseCoreGuiConfiguration - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated --- # Class: SelfViewConfiguration ## Properties ### Property: SelfViewConfiguration.Open ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "UI" ] } ``` ## Inherited Members ### From [BaseCoreGuiConfiguration](/docs/reference/engine/classes/BaseCoreGuiConfiguration.md) - **Property `Enabled`** (`boolean`): ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: SensorBase last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable summary: "An abstract class for various sensor instance types." --- # Class: SensorBase > An abstract class for various sensor instance types. ## Description A [SensorBase](/docs/reference/engine/classes/SensorBase.md) is an instance that, when parented to a [BasePart](/docs/reference/engine/classes/BasePart.md), outputs additional data about the world around that part. This data is presented in the sensor's "output" property category. Often, other systems will consume the sensor's output data. ## Properties ### Property: SensorBase.UpdateType ```json { "type": "SensorUpdateType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Settings", "simulationAccess": true } ``` Determines how the sensor will update its output data. With [SensorUpdateType.OnRead](/docs/reference/engine/enums/SensorUpdateType.md), internal sensor logic is run so that the output properties are always up to date. In this mode, the sensor will only run if you read the properties and they are currently outdated. With [SensorUpdateType.Manual](/docs/reference/engine/enums/SensorUpdateType.md), the output properties will never change. Instead, you can write your own scripts to set the output properties as you like. ## Methods ### Method: SensorBase:Sense **Signature:** `SensorBase:Sense(): ()` > **Deprecated:** This method should not be used in new work. *Security: None · Thread Safety: Unsafe* **Returns:** `()` ## Events ### Event: SensorBase.OnSensorOutputChanged **Signature:** `SensorBase.OnSensorOutputChanged()` *Security: None* ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: SerializationService last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service --- # Class: SerializationService ## Methods ### Method: SerializationService:DeserializeInstancesAsync **Signature:** `SerializationService:DeserializeInstancesAsync(buffer: buffer): List` Deserializes a [buffer](/docs/reference/engine/globals/buffer.md) containing `.rbxm` content, returning a list of [instances](/docs/reference/engine/classes/Instance.md). This API can only be called from Studio plugins or Open Cloud Luau Execution Sessions. If non-creatable instances or services are included in the content, an error is thrown. No stability contract is offered for the `.rbxm` format and the composition of this data may change at any time. This API will faithfully parse `.rbxm` content that was serialized by the Roblox engine, including `.rbxm` files generated by Studio or via [SerializeInstancesAsync()](/docs/reference/engine/classes/SerializationService.md). Although it is possible to use this API to deserialize `.rbxm` content generated by any other means, this is done at the user's own risk. The engine may fail to deserialize such content faithfully, or at all. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: PluginOrOpenCloud* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `buffer` | `buffer` | | | **Returns:** `List` **SerializationService:SerializeInstancesAsync** ```lua local SerializationService = game:GetService("SerializationService") local part = Instance.new("Part") part.Name = "MyPart" local contentBuffer: buffer = SerializationService:SerializeInstancesAsync({ part }) local instances: { Instance } = SerializationService:DeserializeInstancesAsync(contentBuffer) print(instances) -- {MyPart} ``` **Expected output:** {MyPart} ### Method: SerializationService:SerializeInstancesAsync **Signature:** `SerializationService:SerializeInstancesAsync(inputInstances: List): buffer` Serializes a list of [instances](/docs/reference/engine/classes/Instance.md) to the `.rbxm` format, returning a [buffer](/docs/reference/engine/globals/buffer.md) or `.rbxm` content. This API can only be called from Studio plugins or Open Cloud Luau Execution Sessions. If non-creatable instances or services are included in the list of instances, an error is thrown. No stability contract is offered for the `.rbxm` format and the composition of this data may change at any time. This API will serialize content according to the serialization behavior of the current engine version. Although it is possible for other tools to parse `.rbxm` content, this is done at the user's own risk. The engine may still generate `.rbxm` content that 3rd-party tools fail to parse faithfully. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: PluginOrOpenCloud* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `inputInstances` | `List` | | | **Returns:** `buffer` **SerializationService:SerializeInstancesAsync** ```lua local SerializationService = game:GetService("SerializationService") local part = Instance.new("Part") part.Name = "MyPart" local contentBuffer: buffer = SerializationService:SerializeInstancesAsync({ part }) local instances: { Instance } = SerializationService:DeserializeInstancesAsync(contentBuffer) print(instances) -- {MyPart} ``` **Expected output:** {MyPart} ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ServerReplicator last_updated: 2026-06-29T19:34:08Z inherits: - NetworkReplicator - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated --- # Class: ServerReplicator ## Description The ServerReplicator's job is to replicate changes from other clients and the server over to a certain client. ## Inherited Members ### From [NetworkReplicator](/docs/reference/engine/classes/NetworkReplicator.md) - **Method `GetPlayer(): Instance`**: Returns the player that is connected to the NetworkReplicator. ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ServerScriptService last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated summary: "A container service for server-only Script objects." --- # Class: ServerScriptService > A container service for server-only [Script](/docs/reference/engine/classes/Script.md) objects. ## Description **ServerScriptService** is a container service for [Script](/docs/reference/engine/classes/Script.md), [ModuleScript](/docs/reference/engine/classes/ModuleScript.md) and other scripting-related assets that are only meant for server use. The contents are never replicated to player clients at all, which allows for a secure storage of important game logic. Script objects will run if they are within this service and not [Disabled](/docs/reference/engine/classes/BaseScript.md). This service houses just one property, [LoadStringEnabled](/docs/reference/engine/classes/ServerScriptService.md), which determines whether the `loadstring` function in Luau is enabled. It's recommended to keep this disabled for security reasons, as misusing this function can lead to remote code execution vulnerabilities. Scripts running in ServerScriptService may need access to various other assets which are not scripting-related, such as prefabricated models to be [cloned](/docs/reference/engine/classes/Instance.md). Such assets should go in [ServerStorage](/docs/reference/engine/classes/ServerStorage.md), which behaves similarly to this service except that [Script](/docs/reference/engine/classes/Script.md) objects will not run even if they are not [Disabled](/docs/reference/engine/classes/BaseScript.md). Assets and [ModuleScript](/docs/reference/engine/classes/ModuleScript.md) that are useful to both the server and clients should go in [ReplicatedStorage](/docs/reference/engine/classes/ReplicatedStorage.md) instead. Finally, you can further organize objects within this service through the use of [Folders](/docs/reference/engine/classes/Folder.md) without affecting the way it behaves. ## Properties ### Property: ServerScriptService.LoadStringEnabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Basic" ] } ``` Toggles whether or not the `loadstring` function can be used by server scripts. Defaults to false. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ServerStorage last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated summary: "A container whose contents are only accessible on the server. Objects descending from ServerStorage will not replicate to the client and will not be accessible from LocalScripts." --- # Class: ServerStorage > A container whose contents are only accessible on the server. Objects > descending from ServerStorage will not replicate to the client and will not be > accessible from [LocalScripts](/docs/reference/engine/classes/LocalScript.md). ## Description A container whose contents are only accessible on the server. Objects descending from ServerStorage will not replicate to the client and will not be accessible from [LocalScripts](/docs/reference/engine/classes/LocalScript.md). As ServerStorage is a service it can only be accessed using the [DataModel.GetService](/docs/reference/engine/classes/DataModel.md) method. By storing large objects such as maps in ServerStorage until they are needed, network traffic will not be used up transmitting these objects to the client when they join the game. [Scripts](/docs/reference/engine/classes/Script.md) will not run when they are parented to ServerStorage, although [ModuleScripts](/docs/reference/engine/classes/ModuleScript.md) contained within can be accessed and ran. It is recommended developers use [ServerScriptService](/docs/reference/engine/classes/ServerScriptService.md) to hold [Scripts](/docs/reference/engine/classes/Script.md) they wish the server to execute. Note that as the contents of ServerStorage can only be accessed by the server, its contents will need to be parented elsewhere (such as [Workspace](/docs/reference/engine/classes/Workspace.md)) before clients can access them. Developers who require a container that is accessible by both the server and client are advised to use [ReplicatedStorage](/docs/reference/engine/classes/ReplicatedStorage.md) instead. ## Code Samples **ServerStorage Map Rotation** This is a very simple example of how a multiple map system can be made using ServerStorage. It creates three dummy models (to take the place of maps), that are parented to ServerStorage. Then, it will load a random map (different to the previous map) and wait a specified duration on a loop. Developers wishing to integrate something similar into their games should consider measures to ensure players respawn correctly, or go to a lobby during intermission periods. ```lua local ServerStorage = game:GetService("ServerStorage") local ROUND_TIME = 5 local map1 = Instance.new("Model") map1.Name = "Map1" map1.Parent = ServerStorage local map2 = Instance.new("Model") map2.Name = "Map2" map2.Parent = ServerStorage local map3 = Instance.new("Model") map3.Name = "Map3" map3.Parent = ServerStorage local maps = { map1, map2, map3 } local RNG = Random.new() local currentMap = nil local lastPick = nil while true do print("New map!") -- remove current map if currentMap then currentMap:Destroy() end -- pick a map local randomPick = nil if #maps > 1 then repeat randomPick = RNG:NextInteger(1, #maps) until randomPick ~= lastPick lastPick = randomPick end -- fetch new map local map = maps[randomPick] currentMap = map:Clone() currentMap.Parent = workspace task.wait(ROUND_TIME) end ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ServiceProvider last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotBrowsable summary: "A ServiceProvider is an abstract class, which stores, and provides certain singleton classes, depending on what inherited class you are using its members with." --- # Class: ServiceProvider > A ServiceProvider is an abstract class, which stores, and provides certain > singleton classes, depending on what inherited class you are using its members > with. ## Methods ### Method: ServiceProvider:FindService **Signature:** `ServiceProvider:FindService(className: string): Instance` Returns the service specified by the given className if it's already created, errors for an invalid name. *Security: None · Thread Safety: Safe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `className` | `string` | | | **Returns:** `Instance` **ServiceProvider:FindService** ```lua print(game:FindService("Part")) --> nil print(game:FindService("Workspace")) --> Workspace ``` ### Method: ServiceProvider:GetService **Signature:** `ServiceProvider:GetService(className: string): Instance` Returns a service with the class name requested. When called with the name of a service (such as [Debris](/docs/reference/engine/classes/Debris.md)) it will return the instance of that service. If the service does not yet exist it will be created and the new service is returned. This is the only way to create some services, and can also be used for services that have unusual names, e.g. RunService's name is "Run Service". Note: - This function will return `nil` if the className parameter is an existing class, but the class is not a service. - If you attempt to fetch a service that is present under another Object, an error will be thrown stating that the "singleton serviceName already exists". *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `className` | `string` | | The class name of the requested service. | **Returns:** `Instance` — An instance of the requested service. **ServiceProvider:GetService** ```lua local BadgeService = game:GetService("BadgeService") local GameSettings = UserSettings():GetService("UserGameSettings") print(BadgeService) print(GameSettings) ``` ### Method: ServiceProvider:getService **Signature:** `ServiceProvider:getService(className: string): Instance` > **Deprecated:** This deprecated function is a variant of [ServiceProvider:GetService()](/docs/reference/engine/classes/ServiceProvider.md) which should be used instead. *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `className` | `string` | | | **Returns:** `Instance` ### Method: ServiceProvider:service **Signature:** `ServiceProvider:service(className: string): Instance` > **Deprecated:** This item has been superseded by [ServiceProvider:GetService()](/docs/reference/engine/classes/ServiceProvider.md) which should be used in all new work. *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `className` | `string` | | | **Returns:** `Instance` ## Events ### Event: ServiceProvider.Close **Signature:** `ServiceProvider.Close()` Fires when the current place is exited. *Security: None* **ServiceProvider.Close** This example prints "The place is closing" when the game.Close event fires. ```lua local function onClose() print("The place is closing") end game.Close:Connect(onClose) ``` ### Event: ServiceProvider.ServiceAdded **Signature:** `ServiceProvider.ServiceAdded(service: Instance)` Fired when a service is created. *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `service` | `Instance` | | ### Event: ServiceProvider.ServiceRemoving **Signature:** `ServiceProvider.ServiceRemoving(service: Instance)` Fired when a service is about to be removed. *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `service` | `Instance` | | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ServiceVisibilityService last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service --- # Class: ServiceVisibilityService ## Properties ### Property: ServiceVisibilityService.HiddenServices ```json { "type": "BinaryString", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior" } ``` ### Property: ServiceVisibilityService.VisibleServices ```json { "type": "BinaryString", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior" } ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: SessionCheckService last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service --- # Class: SessionCheckService ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: SharedTableRegistry last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated summary: "Provides a global registry of named SharedTable objects." --- # Class: SharedTableRegistry > Provides a global registry of named [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) objects. ## Description Provides a global registry of named [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) objects. It can be used to store [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) objects that are used by multiple scripts. ## Methods ### Method: SharedTableRegistry:GetSharedTable **Signature:** `SharedTableRegistry:GetSharedTable(name: string): SharedTable` Gets the registered [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) with the specified name. If no [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) with that name exists, a new [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) is created with that name and is returned. ```lua local SharedTableRegistry = game:GetService("SharedTableRegistry") local st = SharedTableRegistry:GetSharedTable("MyData") ``` *Security: None · Thread Safety: Safe · Capabilities: ScriptGlobals* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name` | `string` | | The name of the registered [SharedTable](/docs/reference/engine/datatypes/SharedTable.md). | **Returns:** `SharedTable` ### Method: SharedTableRegistry:SetSharedTable **Signature:** `SharedTableRegistry:SetSharedTable(name: string, st: SharedTable?): ()` Registers the provided [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) with the specified name. If the provided [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) is `nil`, any existing [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) with the specified name is unregistered. ```lua local SharedTableRegistry = game:GetService("SharedTableRegistry") -- Register a SharedTable with the name "MyData": local st = SharedTable.new({1, 2, 3}) SharedTableRegistry:SetSharedTable("MyData", st) -- Unregister the SharedTable with the name "MyData": SharedTableRegistry:SetSharedTable("MyData", nil) ``` *Security: None · Thread Safety: Safe · Capabilities: ScriptGlobals* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name` | `string` | | The name of the registered [SharedTable](/docs/reference/engine/datatypes/SharedTable.md). | | `st` | `SharedTable?` | | The [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) object to register, or `nil` to unregister any previously registered [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) object. | **Returns:** `()` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Shirt last_updated: 2026-06-29T19:34:08Z inherits: - Clothing - CharacterAppearance - Instance - Object type: class memory_category: Instances summary: "Displays a shirt texture on a Humanoid rig." --- # Class: Shirt > Displays a shirt texture on a [Humanoid](/docs/reference/engine/classes/Humanoid.md) rig. ## Description The `Shirt` object displays a shirt texture from Roblox on a [Humanoid](/docs/reference/engine/classes/Humanoid.md) rig. Shirts cover the torso and arms, and will take priority over a [Pants](/docs/reference/engine/classes/Pants.md) on the torso. To be visible, a `Shirt` must be a sibling of a [Humanoid](/docs/reference/engine/classes/Humanoid.md) and have its [ShirtTemplate](/docs/reference/engine/classes/Shirt.md) property set to an appropriate texture such as `rbxassetid://86896487`. The shirt texture may be colorized using the [Clothing.Color3](/docs/reference/engine/classes/Clothing.md) property. Shirts are automatically loaded on [Player](/docs/reference/engine/classes/Player.md) characters if their avatar is wearing one. ## Code Samples **Change Shirt / Pants** This sample includes a simple function to change the texture of the `Shirt` and `Pants` worn by a player's character. If shirt and pants don't exist then they are created. Note, this should be run every time the character spawns. If a developer is looking to permanently change a character's appearance to a preset it is recommended they use [Player.CharacterAppearance](/docs/reference/engine/classes/Player.md). ```lua local Players = game:GetService("Players") local function replaceClothes(player) local character = player.Character if character then -- look for shirts / pants local shirt = character:FindFirstChildOfClass("Shirt") local pants = character:FindFirstChildOfClass("Pants") -- create shirts / pants if they don't exist if not shirt then shirt = Instance.new("Shirt") shirt.Parent = character end if not pants then pants = Instance.new("Pants") pants.Parent = character end -- reset shirt / pants content ids shirt.ShirtTemplate = "http://www.roblox.com/asset/?id=83326831" pants.PantsTemplate = "http://www.roblox.com/asset/?id=10045638" end end for _index, player in ipairs(Players:GetPlayers()) do replaceClothes(player) end ``` ## Properties ### Property: Shirt.ShirtTemplate ```json { "type": "ContentId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "AvatarAppearance" ] } ``` The content ID link pointing to the shirt template hosted on Roblox. This content ID is different than the website URL of the shirt. It can be found by pasting the website URL of the shirt into the **ShirtTemplate** property in Studio. Alternatively, [InsertService:LoadAsset()](/docs/reference/engine/classes/InsertService.md) can be used to insert the shirt into the workspace, for example: ``` local InsertService = game:GetService("InsertService") local Workspace = game:GetService("Workspace") local webURL = "https://www.roblox.com/catalog/1804747/White-Shirt" local assetId = tonumber(string.match(webURL, "%d+") or 0) -- Extract the number local success, model = pcall(function() return InsertService:LoadAsset(assetId) end) if success then model.Parent = Workspace end ``` See also [ShirtGraphic.Graphic](/docs/reference/engine/classes/ShirtGraphic.md) for the image applied to T-shirts. **Change Shirt / Pants** This sample includes a simple function to change the texture of the `Shirt` and `Pants` worn by a player's character. If shirt and pants don't exist then they are created. Note, this should be run every time the character spawns. If a developer is looking to permanently change a character's appearance to a preset it is recommended they use [Player.CharacterAppearance](/docs/reference/engine/classes/Player.md). ```lua local Players = game:GetService("Players") local function replaceClothes(player) local character = player.Character if character then -- look for shirts / pants local shirt = character:FindFirstChildOfClass("Shirt") local pants = character:FindFirstChildOfClass("Pants") -- create shirts / pants if they don't exist if not shirt then shirt = Instance.new("Shirt") shirt.Parent = character end if not pants then pants = Instance.new("Pants") pants.Parent = character end -- reset shirt / pants content ids shirt.ShirtTemplate = "http://www.roblox.com/asset/?id=83326831" pants.PantsTemplate = "http://www.roblox.com/asset/?id=10045638" end end for _index, player in ipairs(Players:GetPlayers()) do replaceClothes(player) end ``` ## Inherited Members ### From [Clothing](/docs/reference/engine/classes/Clothing.md) - **Property `Color3`** (`Color3`): Determines the colorization to be applied to the Clothing texture. ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: ShirtGraphic last_updated: 2026-06-29T19:34:08Z inherits: - CharacterAppearance - Instance - Object type: class memory_category: Instances summary: "Applies a texture to the front surface of a character's torso, used to display t-shirts." --- # Class: ShirtGraphic > Applies a texture to the front surface of a character's torso, used to display > t-shirts. ## Description The **ShirtGraphic** object applies a texture to the front surface of a character's torso. It is used to display t-shirts. ## Properties ### Property: ShirtGraphic.Color3 ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "AvatarAppearance" ] } ``` This property determines the colorization to be applied to the [ShirtGraphic](/docs/reference/engine/classes/ShirtGraphic.md) texture. It functions similarly to [ImageLabel.ImageColor3](/docs/reference/engine/classes/ImageLabel.md) except that it is applied to [ShirtGraphic](/docs/reference/engine/classes/ShirtGraphic.md) textures. This is useful for creating shirt graphics that have many different color variations but share the same basic look. See also: - [Clothing.Color3](/docs/reference/engine/classes/Clothing.md) which determines the colorization to be applied to the [Clothing](/docs/reference/engine/classes/Clothing.md) texture ### Property: ShirtGraphic.Graphic ```json { "type": "ContentId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "AvatarAppearance" ] } ``` The content ID link pointing to the [ShirtGraphic](/docs/reference/engine/classes/ShirtGraphic.md) texture hosted on the Roblox website. This property sets the texture associated with a t-shirt. #### Finding the ID This content ID is different than the website URL of the t-shirt. It can be found by pasting the website URL of the t-shirt into the **Graphic** property of the [ShirtGraphic](/docs/reference/engine/classes/ShirtGraphic.md) in Roblox Studio, as Studio will correct it. Alternatively [InsertService:LoadAsset()](/docs/reference/engine/classes/InsertService.md) can be used to insert the t-shirt into the workspace, for example: ``` local InsertService = game:GetService("InsertService") local Workspace = game:GetService("Workspace") local webURL = "https://www.roblox.com/catalog/2591161/Sword-Fight-on-the-Heights-Ring-of-Fire-T-Shirt" local assetId = tonumber(string.match(webURL, "%d+") or 0) -- Extract the number local success, model = pcall(function() return InsertService:LoadAsset(assetId) end) if success then model.Parent = Workspace end ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: SkateboardController last_updated: 2026-06-29T19:34:08Z inherits: - Controller - Instance - Object type: class memory_category: Instances --- # Class: SkateboardController ## Description A SkateboardController is an object responsible for translating PlayerActions to movements with a [SkateboardPlatform](/docs/reference/engine/classes/SkateboardPlatform.md). ## Properties ### Property: SkateboardController.Steer ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Axes", "capabilities": [ "Basic" ] } ``` The direction of movement, tied to the keys A and D. Must be 1 (right), 0 (straight), or -1 (left). Will refresh back to 0 unless constantly set. ### Property: SkateboardController.Throttle ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Axes", "capabilities": [ "Basic" ] } ``` The direction of movement, tied to the keys W and S. Must be an integer 1 (forward), 0 (null), or -1 (reverse). Will refresh back to 0 unless constantly set. ## Events ### Event: SkateboardController.AxisChanged **Signature:** `SkateboardController.AxisChanged(axis: string)` Fired when any input state of the skateboard controller is updated. The `axis` is fired with either `"Throttle"` if the throttle state of the skateboard was updated or `"Steer"` if the steering state of the skateboard was updated. *Security: None · Capabilities: Basic* **Parameters:** | Name | Type | Description | |------|------|-------------| | `axis` | `string` | | ## Inherited Members ### From [Controller](/docs/reference/engine/classes/Controller.md) - **Method `BindButton(button: Button, caption: string): ()`**: Activates an overriding bind on the specified button. - **Method `bindButton(button: Button, caption: string): ()`**: *(deprecated)* - **Method `GetButton(button: Button): boolean`**: Returns whether or not Button is being pressed. - **Method `getButton(button: Button): boolean`**: *(deprecated)* - **Method `UnbindButton(button: Button): ()`**: Removes the bind on button. - **Event `ButtonChanged`**: Fired when the pressed state of a bound button is changed. This event can ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: SkateboardPlatform last_updated: 2026-06-29T19:34:08Z inherits: - Part - FormFactorPart - BasePart - PVInstance - Instance - Object type: class memory_category: BaseParts tags: - Deprecated summary: "A SkateboardPlatform can be used to create a skateboard. When characters get on a skateboard, they are stuck to it until they press the escape key. Until then, the character uses skateboard animations and travels faster than a walking character." --- # Class: SkateboardPlatform > A SkateboardPlatform can be used to create a skateboard. When characters get > on a skateboard, they are stuck to it until they press the escape key. Until > then, the character uses skateboard animations and travels faster than a > walking character. ## Properties ### Property: SkateboardPlatform.Controller ```json { "type": "SkateboardController", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Control", "capabilities": [ "Basic" ] } ``` The SkateboardPlatform's active SkateboardController. ### Property: SkateboardPlatform.ControllingHumanoid ```json { "type": "Humanoid", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Control", "capabilities": [ "Basic" ] } ``` The [Humanoid](/docs/reference/engine/classes/Humanoid.md) that is controlling the SkateboardPlatform. ### Property: SkateboardPlatform.Steer ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Control", "capabilities": [ "Basic" ] } ``` The direction of movement, tied to the keys A and D. Must be 1 (right), 0 (straight), or -1 (left). Will refresh back to 0 unless constantly set. ### Property: SkateboardPlatform.StickyWheels ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Control", "capabilities": [ "Basic" ] } ``` If true, wheels won't roll without user input. ### Property: SkateboardPlatform.Throttle ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Control", "capabilities": [ "Basic" ] } ``` The direction of movement, tied to the keys W and S. Must be an integer 1 (forward), 0 (null), or -1 (reverse). Will refresh back to 0 unless constantly set. ## Methods ### Method: SkateboardPlatform:ApplySpecificImpulse **Signature:** `SkateboardPlatform:ApplySpecificImpulse(impulseWorld: Vector3): ()` Adds ''impulseWorld'' to the SkateboardPlatform's [BasePart.Velocity](/docs/reference/engine/classes/BasePart.md). *Security: None · Thread Safety: Unsafe · Capabilities: Basic* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `impulseWorld` | `Vector3` | | | **Returns:** `()` ## Events ### Event: SkateboardPlatform.Equipped **Signature:** `SkateboardPlatform.Equipped(humanoid: Instance, skateboardController: Instance)` Fired when the skateboard is equipped. *Security: None · Capabilities: Basic* **Parameters:** | Name | Type | Description | |------|------|-------------| | `humanoid` | `Instance` | The [Humanoid](/docs/reference/engine/classes/Humanoid.md) associated with the character equipping the skateboard. | | `skateboardController` | `Instance` | The [SkateboardController](/docs/reference/engine/classes/SkateboardController.md). | ### Event: SkateboardPlatform.MoveStateChanged **Signature:** `SkateboardPlatform.MoveStateChanged(newState: MoveState, oldState: MoveState)` Fired when the SkateboardPlatform's [SkateboardPlatform.ControllingHumanoid](/docs/reference/engine/classes/SkateboardPlatform.md) changes the force being used on the SkateboardPlatform. *Security: None · Capabilities: Basic* **Parameters:** | Name | Type | Description | |------|------|-------------| | `newState` | `MoveState` | The new [MoveState](/docs/reference/engine/enums/MoveState.md). | | `oldState` | `MoveState` | The old [MoveState](/docs/reference/engine/enums/MoveState.md). | ### Event: SkateboardPlatform.Unequipped **Signature:** `SkateboardPlatform.Unequipped(humanoid: Instance)` Fired whenever the skateboard is unequipped. *Security: None · Capabilities: Basic* **Parameters:** | Name | Type | Description | |------|------|-------------| | `humanoid` | `Instance` | The [Humanoid](/docs/reference/engine/classes/Humanoid.md) associated with the character that unequipped the skateboard. | ### Event: SkateboardPlatform.equipped **Signature:** `SkateboardPlatform.equipped(humanoid: Instance, skateboardController: Instance)` > **Deprecated:** This deprecated event is a variant of [SkateboardPlatform.Equipped](/docs/reference/engine/classes/SkateboardPlatform.md) which has also been deprecated. Neither event should be used in new work. *Security: None · Capabilities: Basic* **Parameters:** | Name | Type | Description | |------|------|-------------| | `humanoid` | `Instance` | | | `skateboardController` | `Instance` | | ### Event: SkateboardPlatform.unequipped **Signature:** `SkateboardPlatform.unequipped(humanoid: Instance)` > **Deprecated:** This deprecated event is a variant of [SkateboardPlatform.Unequipped](/docs/reference/engine/classes/SkateboardPlatform.md) which has also been deprecated. Neither event should be used in new work. *Security: None · Capabilities: Basic* **Parameters:** | Name | Type | Description | |------|------|-------------| | `humanoid` | `Instance` | | ## Inherited Members ### From [Part](/docs/reference/engine/classes/Part.md) - **Property `Shape`** (`PartType`): Sets the overall shape of the object. ### From [FormFactorPart](/docs/reference/engine/classes/FormFactorPart.md) - **Property `FormFactor`** (`FormFactor`): *(deprecated)* - **Property `formFactor`** (`FormFactor`): *(deprecated, hidden)* ### From [BasePart](/docs/reference/engine/classes/BasePart.md) - **Property `Anchored`** (`boolean`): Determines whether a part is immovable by physics. - **Property `AssemblyAngularVelocity`** (`Vector3`): The angular velocity of the part's assembly. - **Property `AssemblyCenterOfMass`** (`Vector3`): The center of mass of the part's assembly in world space. - **Property `AssemblyLinearVelocity`** (`Vector3`): The linear velocity of the part's assembly. - **Property `AssemblyMass`** (`float`): The total mass of the part's assembly. - **Property `AssemblyRootPart`** (`BasePart`): A reference to the root part of the assembly. - **Property `AudioCanCollide`** (`boolean`): Determines whether the part will physically interact with audio - **Property `BackParamA`** (`float`): Determines the first parameter for the SurfaceType on the Back face of a *(deprecated, hidden)* - **Property `BackParamB`** (`float`): Determines the second parameter for the SurfaceType on the Back face of a *(deprecated, hidden)* - **Property `BackSurface`** (`SurfaceType`): Determines the type of surface for the back face of a part. - **Property `BackSurfaceInput`** (`InputType`): Determines the kind of input for the Back face of a part. *(deprecated, hidden)* - **Property `BottomParamA`** (`float`): Determines the first parameter for the SurfaceType on the Bottom face of a *(deprecated, hidden)* - **Property `BottomParamB`** (`float`): Determines the second parameter for the SurfaceType on the Bottom face of *(deprecated, hidden)* - **Property `BottomSurface`** (`SurfaceType`): Determines the type of surface for the bottom face of a part. - **Property `BottomSurfaceInput`** (`InputType`): Determines the kind of input for the Bottom face of a part. *(deprecated, hidden)* - **Property `BrickColor`** (`BrickColor`): Determines the color of a part. - **Property `brickColor`** (`BrickColor`): *(deprecated)* - **Property `CanCollide`** (`boolean`): Determines whether a part may collide with other parts. - **Property `CanQuery`** (`boolean`): Determines whether the part is considered during spatial query operations. - **Property `CanTouch`** (`boolean`): Determines if Touched and - **Property `CastShadow`** (`boolean`): Determines whether or not a part casts a shadow. - **Property `CenterOfMass`** (`Vector3`): Describes the world position in which a part's center of mass is located. - **Property `CFrame`** (`CFrame`): Determines the position and orientation of the BasePart in the - **Property `CollisionGroup`** (`string`): Describes the name of a part's collision group. - **Property `CollisionGroupId`** (`int`): Describes the automatically set ID number of a part's collision group. *(deprecated)* - **Property `Color`** (`Color3`): Determines the color of a part. - **Property `CurrentPhysicalProperties`** (`PhysicalProperties`): Indicates the current physical properties of the part. - **Property `CustomPhysicalProperties`** (`PhysicalProperties`): Determines several physical properties of a part. - **Property `Elasticity`** (`float`): Used to control the Elasticity of the part, but it no longer does *(deprecated, hidden)* - **Property `EnableFluidForces`** (`boolean`): Used to enable or disable aerodynamic forces on parts and assemblies. - **Property `ExtentsCFrame`** (`CFrame`): The CFrame of the physical extents of the BasePart. - **Property `ExtentsSize`** (`Vector3`): The actual physical size of the BasePart as regarded by the - **Property `Friction`** (`float`): Used to control the Friction of the part, but now it no longer does *(deprecated, hidden)* - **Property `FrontParamA`** (`float`): Determines the first parameter for the SurfaceType on the Front face of a *(deprecated, hidden)* - **Property `FrontParamB`** (`float`): Determines the second parameter for the SurfaceType on the Front face of a *(deprecated, hidden)* - **Property `FrontSurface`** (`SurfaceType`): Determines the type of surface for the front face of a part. - **Property `FrontSurfaceInput`** (`InputType`): Determines the kind of input for the Front face of a part (-Z direction). *(deprecated, hidden)* - **Property `LeftParamA`** (`float`): Determines the first parameter for the SurfaceType on the Left face of a *(deprecated, hidden)* - **Property `LeftParamB`** (`float`): Determines the second parameter for the SurfaceType on the Left face of a *(deprecated, hidden)* - **Property `LeftSurface`** (`SurfaceType`): Determines the type of surface for the left face of a part. - **Property `LeftSurfaceInput`** (`InputType`): Determines the kind of input for the Left face of a part. *(deprecated, hidden)* - **Property `LocalTransparencyModifier`** (`float`): Determines a multiplier for BasePart.Transparency that is only *(hidden)* - **Property `Locked`** (`boolean`): Determines whether a part is selectable in Studio. - **Property `Mass`** (`float`): Describes the mass of the part, the product of its density and volume. - **Property `Massless`** (`boolean`): Determines whether the part contributes to the total mass or inertia of - **Property `Material`** (`Material`): Determines the texture and default physical properties of a part. - **Property `MaterialVariant`** (`string`): The name of MaterialVariant. - **Property `Orientation`** (`Vector3`): Describes the rotation of the part in the world. *(hidden)* - **Property `PivotOffset`** (`CFrame`): Specifies the offset of the part's pivot from its CFrame. - **Property `Position`** (`Vector3`): Describes the position of the part in the world. *(hidden)* - **Property `ReceiveAge`** (`float`): Time since last recorded physics update. *(hidden)* - **Property `Reflectance`** (`float`): Determines how much a part reflects the skybox. - **Property `ResizeableFaces`** (`Faces`): Describes the faces on which a part may be resized. - **Property `ResizeIncrement`** (`int`): Describes the smallest change in size allowable by the - **Property `RightParamA`** (`float`): Determines the first parameter for the SurfaceType on the Right face of a *(deprecated, hidden)* - **Property `RightParamB`** (`float`): Determines the second parameter for the SurfaceType on the Right face of a *(deprecated, hidden)* - **Property `RightSurface`** (`SurfaceType`): Determines the type of surface for the right face of a part. - **Property `RightSurfaceInput`** (`InputType`): Determines the kind of input for the Right face of a part (-X direction). *(deprecated, hidden)* - **Property `RootPriority`** (`int`): The main rule in determining the root part of an assembly. - **Property `Rotation`** (`Vector3`): The rotation of the part in degrees for the three axes. - **Property `RotVelocity`** (`Vector3`): Determines a part's change in orientation over time. *(deprecated, hidden)* - **Property `Size`** (`Vector3`): Determines the dimensions of a part (length, width, height). - **Property `SpecificGravity`** (`float`): The ratio of the part's density to the density of water determined by the *(deprecated)* - **Property `TopParamA`** (`float`): Determines the first parameter for the SurfaceType on the Top face of a *(deprecated, hidden)* - **Property `TopParamB`** (`float`): Determines the second parameter for the SurfaceType on the Top face of a *(deprecated, hidden)* - **Property `TopSurface`** (`SurfaceType`): Determines the type of surface for the top face of a part. - **Property `TopSurfaceInput`** (`InputType`): Determines the kind of input for the Top face of a part (+Y direction). *(deprecated, hidden)* - **Property `Transparency`** (`float`): Determines how much a part can be seen through (the inverse of part - **Property `Velocity`** (`Vector3`): Determines a part's change in position over time. *(deprecated, hidden)* - **Method `AngularAccelerationToTorque(angAcceleration: Vector3, angVelocity?: Vector3): Vector3`**: Returns the torque needed to achieve a given angular acceleration on this - **Method `ApplyAngularImpulse(impulse: Vector3): ()`**: Apply an angular impulse to the assembly. - **Method `ApplyImpulse(impulse: Vector3): ()`**: Apply an impulse to the assembly at the assembly's - **Method `ApplyImpulseAtPosition(impulse: Vector3, position: Vector3): ()`**: Apply an impulse to the assembly at specified position. - **Method `BreakJoints(): ()`**: Breaks any surface connection with any adjacent part, including *(deprecated)* - **Method `breakJoints(): ()`**: *(deprecated)* - **Method `CanCollideWith(part: BasePart): boolean`**: Returns whether the parts can collide with each other. - **Method `CanSetNetworkOwnership(): Tuple`**: Checks whether you can set a part's network ownership. - **Method `GetClosestPointOnSurface(position: Vector3): Vector3`**: Returns the closest point on the part's surface to the given point. - **Method `GetConnectedParts(recursive?: boolean): List`**: Returns a table of parts connected to the object by any kind of rigid - **Method `GetJoints(): Instances`**: Return all Joints or Constraints that is connected to this Part. - **Method `GetMass(): float`**: Returns the value of the Mass property. - **Method `getMass(): float`**: *(deprecated)* - **Method `GetNetworkOwner(): Instance`**: Returns the current player who is the network owner of this part, or `nil` - **Method `GetNetworkOwnershipAuto(): boolean`**: Returns true if the game engine automatically decides the network owner - **Method `GetNoCollisionConstraints(): Instances`**: - **Method `GetRenderCFrame(): CFrame`**: OBSOLETE. Returns a CFrame describing where the part is being rendered at. *(deprecated)* - **Method `GetRootPart(): Instance`**: Returns the base part of an assembly of parts. *(deprecated)* - **Method `GetTouchingParts(): Instances`**: Returns a table of all BasePart.CanCollide true parts that - **Method `GetVelocityAtPosition(position: Vector3): Vector3`**: Returns the linear velocity of the part's assembly at the given position - **Method `IntersectAsync(parts: Instances, collisionfidelity?: CollisionFidelity, renderFidelity?: RenderFidelity): Instance`**: Note: It is highly recommended to use the newer - **Method `IsGrounded(): boolean`**: Returns true if the object is connected to a part that will hold it in - **Method `MakeJoints(): ()`**: Creates a joint on any side of the object that has a surface ID that can *(deprecated)* - **Method `makeJoints(): ()`**: *(deprecated)* - **Method `Resize(normalId: NormalId, deltaAmount: int): boolean`**: Changes the size of an object just like using the Studio resize tool. - **Method `resize(normalId: NormalId, deltaAmount: int): boolean`**: *(deprecated)* - **Method `SetNetworkOwner(playerInstance?: Player): ()`**: Sets the given player as network owner for this and all connected parts. - **Method `SetNetworkOwnershipAuto(): ()`**: Lets the game engine dynamically decide who will handle the part's physics - **Method `SubtractAsync(parts: Instances, collisionfidelity?: CollisionFidelity, renderFidelity?: RenderFidelity): Instance`**: Note: It is highly recommended to use the newer - **Method `TorqueToAngularAcceleration(torque: Vector3, angVelocity?: Vector3): Vector3`**: Returns the angular acceleration that would result from applying a given - **Method `UnionAsync(parts: Instances, collisionfidelity?: CollisionFidelity, renderFidelity?: RenderFidelity): Instance`**: Note: It is highly recommended to use the newer - **Event `LocalSimulationTouched`**: *(deprecated)* - **Event `OutfitChanged`**: *(deprecated)* - **Event `StoppedTouching`**: *(deprecated)* - **Event `Touched`**: Fires when a part touches another part as a result of physical movement. - **Event `TouchEnded`**: Fires when a part stops touching another part as a result of physical ### From [PVInstance](/docs/reference/engine/classes/PVInstance.md) - **Property `Origin`** (`CFrame`): - **Property `Pivot Offset`** (`CFrame`): - **Method `GetPivot(): CFrame`**: Gets the pivot of a PVInstance. - **Method `PivotTo(targetCFrame: CFrame): ()`**: Transforms the PVInstance along with all of its descendant ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Skin last_updated: 2026-06-29T19:34:08Z inherits: - CharacterAppearance - Instance - Object type: class memory_category: Instances tags: - Deprecated summary: "Historically changed the colors of body parts to match the Skin.SkinColor property. Superseded by the BodyColors class." --- # Class: Skin > Historically changed the colors of body parts to match the > [Skin.SkinColor](/docs/reference/engine/classes/Skin.md) property. Superseded by the [BodyColors](/docs/reference/engine/classes/BodyColors.md) class. ## Description The Skin object, when placed into a humanoid model, will change the colors all body parts of the torso, head, etc, to value of the [Skin.SkinColor](/docs/reference/engine/classes/Skin.md) property. Superseded by the [BodyColors](/docs/reference/engine/classes/BodyColors.md) class. ## Properties ### Property: Skin.SkinColor ```json { "type": "BrickColor", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "AvatarAppearance" ] } ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Sky last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances summary: "Changes the default appearance of the experience's sky." --- # Class: Sky > Changes the default appearance of the experience's sky. ## Description The `Sky` object, when placed inside [Lighting](/docs/reference/engine/classes/Lighting.md), changes the default appearance of the experience's sky. This object's [skybox](/docs/en-us/environment/skybox.md) is composed of six sides, like a cube. Rotation of the skybox can be changed through [SkyboxOrientation](/docs/reference/engine/classes/Sky.md). The skybox sun, moon, and other celestial objects remain visible unless you turn off the [CelestialBodiesShown](/docs/reference/engine/classes/Sky.md) property. By adjusting the [StarCount](/docs/reference/engine/classes/Sky.md) property, you can change how many stars appear in the sky at night. This object can also be used as a cubemap for reflections in [ViewportFrames](/docs/reference/engine/classes/ViewportFrame.md), in which case only the [Sky](/docs/reference/engine/classes/Sky.md) object's six‑side `Skybox[…]` properties are used. For details, see [viewport frames](/docs/en-us/ui/viewport-frames.md). ## Properties ### Property: Sky.CelestialBodiesShown ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` Sets whether the sun, moon, and stars will show. ### Property: Sky.MoonAngularSize ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` The perceived angular size of the moon while using this skybox, in degrees. ### Property: Sky.MoonTextureContent ```json { "type": "Content", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` ### Property: Sky.MoonTextureId ```json { "type": "ContentId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` The texture of the moon while using this skybox. ### Property: Sky.SkyboxBackContent ```json { "type": "Content", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` ### Property: Sky.SkyboxBk ```json { "type": "ContentId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` The URL link to a picture for the back surface of the sky. ### Property: Sky.SkyboxDn ```json { "type": "ContentId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` Asset ID for the bottom surface of the skybox. ### Property: Sky.SkyboxDownContent ```json { "type": "Content", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` ### Property: Sky.SkyboxFrontContent ```json { "type": "Content", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` ### Property: Sky.SkyboxFt ```json { "type": "ContentId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` Asset ID for the front surface of the skybox. ### Property: Sky.SkyboxLeftContent ```json { "type": "Content", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` ### Property: Sky.SkyboxLf ```json { "type": "ContentId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` Asset ID for the left surface of the skybox. ### Property: Sky.SkyboxOrientation ```json { "type": "Vector3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` Changes the orientation of the skybox surfaces. This property takes a [Vector3](/docs/reference/engine/datatypes/Vector3.md) of degree values in the typical **XYZ** order, but applies rotation **first** around the **Y** axis. After applying the Y axis rotation, the orientation is applied to the **X** and **Z** axes to allow for predictable control over complex movements. Your camera and object orientation can affect how this rotation appears to be applied. An easy way to script an orientation animation is to spin around the **Y** axis (keeping the horizon level), then tilt this axis by setting **X** and **Z** to a fixed value: ```lua local Lighting = game:GetService("Lighting") local RunService = game:GetService("RunService") local sky = Lighting:FindFirstChild("Sky") local ROTATION_SPEED = 5 -- In degrees per second RunService.Heartbeat:Connect(function(deltaTime) sky.SkyboxOrientation = Vector3.new( 30, (sky.SkyboxOrientation.Y + ROTATION_SPEED * deltaTime) % 360, 0 ) end) ``` See [here](/docs/en-us/environment/skybox.md#orientation) for further info and limitations. **Skybox Orientation with Tween** This script uses [TweenService](/docs/reference/engine/classes/TweenService.md) to create an oscillating tween on the **X** axis and [RunService](/docs/reference/engine/classes/RunService.md) to apply the tween's motion plus rotation around the **Y** axis. ```lua local Lighting = game:GetService("Lighting") local RunService = game:GetService("RunService") local TweenService = game:GetService("TweenService") local sky = Lighting:FindFirstChild("Sky") local ROTATION_SPEED = 4 -- In degrees per second local MAX_TILT = 2 -- In degrees local TILT_SPEED = 4 local currentTilt = Instance.new("NumberValue") currentTilt.Value = -MAX_TILT local tweenGoal = { Value = MAX_TILT } local tweenInfo = TweenInfo.new(TILT_SPEED, Enum.EasingStyle.Cubic, Enum.EasingDirection.InOut, -1, true) local tween = TweenService:Create(currentTilt, tweenInfo, tweenGoal) tween:Play() RunService.Heartbeat:Connect(function(deltaTime) sky.SkyboxOrientation = Vector3.new(currentTilt.Value, (sky.SkyboxOrientation.Y + ROTATION_SPEED * deltaTime) % 360, 0) end) ``` ### Property: Sky.SkyboxRightContent ```json { "type": "Content", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` ### Property: Sky.SkyboxRt ```json { "type": "ContentId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` Asset ID for the right surface of the skybox. ### Property: Sky.SkyboxUp ```json { "type": "ContentId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` Asset ID for the top surface of the skybox. ### Property: Sky.SkyboxUpContent ```json { "type": "Content", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` ### Property: Sky.StarCount ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` How many stars are shown in the skybox. Only works if [CelestialBodiesShown](/docs/reference/engine/classes/Sky.md) is `true`. ### Property: Sky.SunAngularSize ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` The perceived angular size of the sun while using this skybox, in degrees. ### Property: Sky.SunTextureContent ```json { "type": "Content", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` ### Property: Sky.SunTextureId ```json { "type": "ContentId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` The texture of the sun while using this skybox. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: SlidingBallConstraint last_updated: 2026-06-29T19:34:08Z inherits: - Constraint - Instance - Object type: class memory_category: BaseParts tags: - NotCreatable summary: "The base class for constraints that allow their attachments to slide along an axis but not rotate." --- # Class: SlidingBallConstraint > The base class for constraints that allow their attachments to slide along an > axis but not rotate. ## Description **SlidingBallConstraint** is the base class for constraints that allow their attachments to slide along an axis but not rotate, including [PrismaticConstraint](/docs/reference/engine/classes/PrismaticConstraint.md) and [CylindricalConstraint](/docs/reference/engine/classes/CylindricalConstraint.md). This constrains the attachments so that their **X** axes are collinear but pointing in opposite directions. It also constrains the attachments so that their **Y** axes are parallel. When configuring this constraint, it may be helpful to study [Roblox Units](/docs/en-us/physics/units.md) to understand how Roblox units compare to metric units. #### Linear Power If this constraint's [ActuatorType](/docs/reference/engine/classes/SlidingBallConstraint.md) is set to [Motor](/docs/reference/engine/enums/ActuatorType.md), it attempts to translate the attachments with the goal of reaching [Velocity](/docs/reference/engine/classes/SlidingBallConstraint.md). You can further control this translation through both [MotorMaxAcceleration](/docs/reference/engine/classes/SlidingBallConstraint.md) and [MotorMaxForce](/docs/reference/engine/classes/SlidingBallConstraint.md). If this constraint's [ActuatorType](/docs/reference/engine/classes/SlidingBallConstraint.md) is set to [Servo](/docs/reference/engine/enums/ActuatorType.md), it attempts to translate the attachments to a set separation specified by [TargetPosition](/docs/reference/engine/classes/SlidingBallConstraint.md). This translation is controlled by [Speed](/docs/reference/engine/classes/SlidingBallConstraint.md), [LinearResponsiveness](/docs/reference/engine/classes/SlidingBallConstraint.md), and [ServoMaxForce](/docs/reference/engine/classes/SlidingBallConstraint.md). #### Limits You can set **limits** to restrict this constraint's sliding range. Enabling the [LimitsEnabled](/docs/reference/engine/classes/SlidingBallConstraint.md) property exposes the [LowerLimit](/docs/reference/engine/classes/SlidingBallConstraint.md) and [UpperLimit](/docs/reference/engine/classes/SlidingBallConstraint.md) values, as well as [Restitution](/docs/reference/engine/classes/SlidingBallConstraint.md) which defines the elasticity of the attachments when they reach either limit. ## Properties ### Property: SlidingBallConstraint.ActuatorType ```json { "type": "ActuatorType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Slider", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Sets whether the translation of the [SlidingBallConstraint](/docs/reference/engine/classes/SlidingBallConstraint.md) is actuated and, if so, what kind of actuation. - If [ActuatorType](/docs/reference/engine/classes/SlidingBallConstraint.md) is set to [None](/docs/reference/engine/enums/ActuatorType.md), the joint can slide freely. - If [ActuatorType](/docs/reference/engine/classes/SlidingBallConstraint.md) is set to [Motor](/docs/reference/engine/enums/ActuatorType.md), it attempts to translate the attachments with the goal of reaching [Velocity](/docs/reference/engine/classes/SlidingBallConstraint.md). You can further control this translation through both [MotorMaxAcceleration](/docs/reference/engine/classes/SlidingBallConstraint.md) and [MotorMaxForce](/docs/reference/engine/classes/SlidingBallConstraint.md). - If this constraint's [ActuatorType](/docs/reference/engine/classes/SlidingBallConstraint.md) is set to [Servo](/docs/reference/engine/enums/ActuatorType.md), it attempts to translate the attachments to a set separation specified by [TargetPosition](/docs/reference/engine/classes/SlidingBallConstraint.md). This translation is controlled by [Speed](/docs/reference/engine/classes/SlidingBallConstraint.md), [LinearResponsiveness](/docs/reference/engine/classes/SlidingBallConstraint.md), and [ServoMaxForce](/docs/reference/engine/classes/SlidingBallConstraint.md). ### Property: SlidingBallConstraint.CurrentPosition ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Derived", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The current offset between the constraint's [Attachments](/docs/reference/engine/classes/Attachment.md). ### Property: SlidingBallConstraint.LimitsEnabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Slider", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Sets whether the [SlidingBallConstraint](/docs/reference/engine/classes/SlidingBallConstraint.md) will limit the range of translation. If true, this property exposes the [LowerLimit](/docs/reference/engine/classes/SlidingBallConstraint.md) and [UpperLimit](/docs/reference/engine/classes/SlidingBallConstraint.md) values, as well as [Restitution](/docs/reference/engine/classes/SlidingBallConstraint.md) which defines the elasticity of the attachments when they reach either limit. ### Property: SlidingBallConstraint.LinearResponsiveness ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Servo", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Specifies the "sharpness" of the linear servo motor in reaching the [TargetPosition](/docs/reference/engine/classes/SlidingBallConstraint.md) when the derived classes' actuator type is set to [Servo](/docs/reference/engine/enums/ActuatorType.md). Larger values correspond to faster a response and smaller values results in more damping and a slower response. ### Property: SlidingBallConstraint.LowerLimit ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Limits", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The lower positional limit along the **X** axis of [Attachment0](/docs/reference/engine/classes/Constraint.md) if [LimitsEnabled](/docs/reference/engine/classes/SlidingBallConstraint.md) is true. ### Property: SlidingBallConstraint.MotorMaxAcceleration ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Motor", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The constraint's maximum acceleration when [ActuatorType](/docs/reference/engine/classes/SlidingBallConstraint.md) is set to [Motor](/docs/reference/engine/enums/ActuatorType.md), as the constraint attempts to reach its desired [Velocity](/docs/reference/engine/classes/SlidingBallConstraint.md). ### Property: SlidingBallConstraint.MotorMaxForce ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Motor", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The constraint's maximum force when [ActuatorType](/docs/reference/engine/classes/SlidingBallConstraint.md) is set to [Motor](/docs/reference/engine/enums/ActuatorType.md), as the constraint attempts to reach its desired [Velocity](/docs/reference/engine/classes/SlidingBallConstraint.md). ### Property: SlidingBallConstraint.Restitution ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Limits", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The elasticity of the constraint's [Attachments](/docs/reference/engine/classes/Attachment.md) when they reach the end of the range specified by [UpperLimit](/docs/reference/engine/classes/SlidingBallConstraint.md) and [LowerLimit](/docs/reference/engine/classes/SlidingBallConstraint.md), assuming [LimitsEnabled](/docs/reference/engine/classes/SlidingBallConstraint.md) is set to true. The valid range is between 0–1. ### Property: SlidingBallConstraint.ServoMaxForce ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Servo", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The constraint's maximum force when [ActuatorType](/docs/reference/engine/classes/SlidingBallConstraint.md) is set to [Servo](/docs/reference/engine/enums/ActuatorType.md), as the constraint attempts to reach its desired [Speed](/docs/reference/engine/classes/SlidingBallConstraint.md). ### Property: SlidingBallConstraint.Size ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The constraint's visualized size. ### Property: SlidingBallConstraint.Speed ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Servo", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The constraint's desired speed when [ActuatorType](/docs/reference/engine/classes/SlidingBallConstraint.md) is set to [Servo](/docs/reference/engine/enums/ActuatorType.md), as the constraint translates towards its [TargetPosition](/docs/reference/engine/classes/SlidingBallConstraint.md). Measured in studs per second. ### Property: SlidingBallConstraint.TargetPosition ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Servo", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The constraint's attempted target position when [ActuatorType](/docs/reference/engine/classes/SlidingBallConstraint.md) is set to [Servo](/docs/reference/engine/enums/ActuatorType.md) Measured in studs. ### Property: SlidingBallConstraint.UpperLimit ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Limits", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The upper positional limit along the **X** axis of [Attachment0](/docs/reference/engine/classes/Constraint.md) if [LimitsEnabled](/docs/reference/engine/classes/SlidingBallConstraint.md) is true. ### Property: SlidingBallConstraint.Velocity ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Motor", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The constraint's attempted velocity when [ActuatorType](/docs/reference/engine/classes/SlidingBallConstraint.md) is set to [Motor](/docs/reference/engine/enums/ActuatorType.md). Measured in studs per second. ### Property: SlidingBallConstraint.SoftlockServoUponReachingTarget ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Servo", "capabilities": [ "Physics" ], "simulationAccess": true } ``` > **Deprecated:** This property should not be used in new work. ## Inherited Members ### From [Constraint](/docs/reference/engine/classes/Constraint.md) - **Property `Active`** (`boolean`): Indicates if the constraint is currently active in the world. - **Property `Attachment0`** (`Attachment`): The Attachment that is connected to - **Property `Attachment1`** (`Attachment`): The Attachment that is connected to - **Property `Color`** (`BrickColor`): The color of the constraint. - **Property `Enabled`** (`boolean`): Toggles whether or not the constraint is enabled. - **Property `Visible`** (`boolean`): Toggles the constraint's visibility. - **Method `GetDebugAppliedForce(bodyId: int): Vector3`**: *(deprecated)* - **Method `GetDebugAppliedTorque(bodyId: int): Vector3`**: *(deprecated)* ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: SlimContentProvider last_updated: 2026-06-29T19:34:08Z inherits: - CacheableContentProvider - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service --- # Class: SlimContentProvider ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Smoke last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances summary: "A particle emitter with the visual aesthetic of smoke." --- # Class: Smoke > A particle emitter with the visual aesthetic of smoke. ## Description Smoke is one of several particle-emitting classes. Like other particle emitters of its kind, Smoke objects emit particles when parented to a [BasePart](/docs/reference/engine/classes/BasePart.md) (such as a [Part](/docs/reference/engine/classes/Part.md)) or an [Attachment](/docs/reference/engine/classes/Attachment.md) within such a [BasePart](/docs/reference/engine/classes/BasePart.md). Compared to the [ParticleEmitter](/docs/reference/engine/classes/ParticleEmitter.md) class, Smoke lacks many different customization properties and special methods, such as [ParticleEmitter.Lifetime](/docs/reference/engine/classes/ParticleEmitter.md) or [ParticleEmitter:Emit()](/docs/reference/engine/classes/ParticleEmitter.md). It is useful to create a quick special effect in a pinch; for more detailed work it is preferable to use a [ParticleEmitter](/docs/reference/engine/classes/ParticleEmitter.md) instead. When [Smoke.Enabled](/docs/reference/engine/classes/Smoke.md) is toggled off, particles emit by this object will continue to render until their lifetime expires. When a Smoke object's [Instance.Parent](/docs/reference/engine/classes/Instance.md) is set to `nil` (and/or [Instance:Destroy()](/docs/reference/engine/classes/Instance.md)ed), all particles will instantly disappear. If this effect is not desired, try hiding the parent object at a far away position, then removing the Smoke after a few seconds using [Debris](/docs/reference/engine/classes/Debris.md) to give the last particles a chance to expire. This object does not have a [ParticleEmitter:Clear()](/docs/reference/engine/classes/ParticleEmitter.md) method, but it is possible to set the [Instance.Parent](/docs/reference/engine/classes/Instance.md) to `nil` and back to the exact same object for the same effect. Smoke particles are only emitted from the center of [BasePart](/docs/reference/engine/classes/BasePart.md) to which they are parented. Parenting a Smoke object to an [Attachment](/docs/reference/engine/classes/Attachment.md) instead allows customization of the particles' start position. ## Code Samples **Add Smoke to All Fire** This code sample adds a `Smoke` object to every `Fire` object in the `Workspace`. It does this by using a recursive search. ```lua local function recurseForFire(object) -- Check if we found a Fire object that has no Smoke if object:IsA("Fire") and not object.Parent:FindFirstChildOfClass("Smoke") then -- Create a smoke effect for this fire local smoke = Instance.new("Smoke") smoke.Color = Color3.new(0, 0, 0) smoke.Opacity = 0.15 smoke.RiseVelocity = 4 smoke.Size = object.Size / 4 smoke.Parent = object.Parent end -- Continue search for Fire objects for _, child in pairs(object:GetChildren()) do recurseForFire(child) end end recurseForFire(workspace) ``` **Expected output:** When run, you should see every Fire object in the workspace have a Smoke sibling object (if it did not have one before). ## Properties ### Property: Smoke.Color ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` The Color property determines the color of all the particles emit by a [Smoke](/docs/reference/engine/classes/Smoke.md) object (both existing and future particles). It behaves similarly to [ParticleEmitter.Color](/docs/reference/engine/classes/ParticleEmitter.md), except that it is only one color and not a [ColorSequence](/docs/reference/engine/datatypes/ColorSequence.md). A color of white with some [Smoke.Opacity](/docs/reference/engine/classes/Smoke.md) makes for a nice fog effect, and a very opaque black color can compliment a [Fire](/docs/reference/engine/classes/Fire.md) object nicely. **Add Smoke to All Fire** This code sample adds a `Smoke` object to every `Fire` object in the `Workspace`. It does this by using a recursive search. ```lua local function recurseForFire(object) -- Check if we found a Fire object that has no Smoke if object:IsA("Fire") and not object.Parent:FindFirstChildOfClass("Smoke") then -- Create a smoke effect for this fire local smoke = Instance.new("Smoke") smoke.Color = Color3.new(0, 0, 0) smoke.Opacity = 0.15 smoke.RiseVelocity = 4 smoke.Size = object.Size / 4 smoke.Parent = object.Parent end -- Continue search for Fire objects for _, child in pairs(object:GetChildren()) do recurseForFire(child) end end recurseForFire(workspace) ``` **Expected output:** When run, you should see every Fire object in the workspace have a Smoke sibling object (if it did not have one before). ### Property: Smoke.Enabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` The Enabled property, much like [ParticleEmitter.Enabled](/docs/reference/engine/classes/ParticleEmitter.md), determines whether smoke particles are emitted. Any particles already emit will continue to render until their lifetime expires. This property is useful for keeping pre-made smoke effects off until they are needed later. Since smoke particles are destroyed when the [Smoke](/docs/reference/engine/classes/Smoke.md) object's [Instance.Parent](/docs/reference/engine/classes/Instance.md) is set to `nil`, this property is useful in allowing existing particles the opportunity to expire before destroying the Fire object altogether. See the function below. ``` local Debris = game:GetService("Debris") local part = script.Parent function stopSmoke(smoke) smoke.Enabled = false -- No more new particles Debris:AddItem(smoke, 10) -- Remove the object after a delay (after existing particles have expired) end stopSmoke(part.Smoke) ``` **Add Smoke to All Fire** This code sample adds a `Smoke` object to every `Fire` object in the `Workspace`. It does this by using a recursive search. ```lua local function recurseForFire(object) -- Check if we found a Fire object that has no Smoke if object:IsA("Fire") and not object.Parent:FindFirstChildOfClass("Smoke") then -- Create a smoke effect for this fire local smoke = Instance.new("Smoke") smoke.Color = Color3.new(0, 0, 0) smoke.Opacity = 0.15 smoke.RiseVelocity = 4 smoke.Size = object.Size / 4 smoke.Parent = object.Parent end -- Continue search for Fire objects for _, child in pairs(object:GetChildren()) do recurseForFire(child) end end recurseForFire(workspace) ``` **Expected output:** When run, you should see every Fire object in the workspace have a Smoke sibling object (if it did not have one before). ### Property: Smoke.Opacity ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` Opacity determines the opaqueness of the smoke particles. It must be in the range [0, 1]. This property works **inversely** in comparison to a part's [BasePart.Transparency](/docs/reference/engine/classes/BasePart.md) or ParticleEmitter's [ParticleEmitter.Transparency](/docs/reference/engine/classes/ParticleEmitter.md): a value of 0 is completely invisible, 1 is completely visible. The texture that Roblox uses for [Smoke](/docs/reference/engine/classes/Smoke.md) particles is partially transparent, so setting this property to 1 still yields transparency in rendered smoke. **Add Smoke to All Fire** This code sample adds a `Smoke` object to every `Fire` object in the `Workspace`. It does this by using a recursive search. ```lua local function recurseForFire(object) -- Check if we found a Fire object that has no Smoke if object:IsA("Fire") and not object.Parent:FindFirstChildOfClass("Smoke") then -- Create a smoke effect for this fire local smoke = Instance.new("Smoke") smoke.Color = Color3.new(0, 0, 0) smoke.Opacity = 0.15 smoke.RiseVelocity = 4 smoke.Size = object.Size / 4 smoke.Parent = object.Parent end -- Continue search for Fire objects for _, child in pairs(object:GetChildren()) do recurseForFire(child) end end recurseForFire(workspace) ``` **Expected output:** When run, you should see every Fire object in the workspace have a Smoke sibling object (if it did not have one before). ### Property: Smoke.RiseVelocity ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` RiseVelocity behaves similarly to [ParticleEmitter.Speed](/docs/reference/engine/classes/ParticleEmitter.md) and [Fire.Heat](/docs/reference/engine/classes/Fire.md): it determines how fast the smoke particles move during their lifetime. It must be in the range [-25, 25]. Negative values will cause particles to emit in the bottom (-Y) direction of the parent [BasePart](/docs/reference/engine/classes/BasePart.md). When using a [Smoke](/docs/reference/engine/classes/Smoke.md) effect to create fog, set this property to 0. For large smoke effects, make the rise subtle (2 to 8). For chimneys and smokestacks, higher values are appropriate. **Add Smoke to All Fire** This code sample adds a `Smoke` object to every `Fire` object in the `Workspace`. It does this by using a recursive search. ```lua local function recurseForFire(object) -- Check if we found a Fire object that has no Smoke if object:IsA("Fire") and not object.Parent:FindFirstChildOfClass("Smoke") then -- Create a smoke effect for this fire local smoke = Instance.new("Smoke") smoke.Color = Color3.new(0, 0, 0) smoke.Opacity = 0.15 smoke.RiseVelocity = 4 smoke.Size = object.Size / 4 smoke.Parent = object.Parent end -- Continue search for Fire objects for _, child in pairs(object:GetChildren()) do recurseForFire(child) end end recurseForFire(workspace) ``` **Expected output:** When run, you should see every Fire object in the workspace have a Smoke sibling object (if it did not have one before). ### Property: Smoke.Size ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` The Size property of [Smoke](/docs/reference/engine/classes/Smoke.md) determines the size of the newly emit smoke particles. Unlike [Smoke.Color](/docs/reference/engine/classes/Smoke.md), this property will not change the size of existing particles. It must be in the range [0.1, 100]. Unlike [ParticleEmitter.Size](/docs/reference/engine/classes/ParticleEmitter.md), this property is only a number (not a [NumberSequence](/docs/reference/engine/datatypes/NumberSequence.md)). Also note also that the size of the particles is not 1-to-1 with studs; in fact, the size of the smoke particle is more than twice as large. At the largest size, smoke particles can render larger than 200 studs wide! **Add Smoke to All Fire** This code sample adds a `Smoke` object to every `Fire` object in the `Workspace`. It does this by using a recursive search. ```lua local function recurseForFire(object) -- Check if we found a Fire object that has no Smoke if object:IsA("Fire") and not object.Parent:FindFirstChildOfClass("Smoke") then -- Create a smoke effect for this fire local smoke = Instance.new("Smoke") smoke.Color = Color3.new(0, 0, 0) smoke.Opacity = 0.15 smoke.RiseVelocity = 4 smoke.Size = object.Size / 4 smoke.Parent = object.Parent end -- Continue search for Fire objects for _, child in pairs(object:GetChildren()) do recurseForFire(child) end end recurseForFire(workspace) ``` **Expected output:** When run, you should see every Fire object in the workspace have a Smoke sibling object (if it did not have one before). ### Property: Smoke.TimeScale ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` A value between 0-1 than controls the speed of the particle effect. At 1 it runs at normal speed, at 0.5 it runs at half speed, and at 0 it freezes time. ### Property: Smoke.LocalTransparencyModifier *(hidden)* ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: SmoothVoxelsUpgraderService last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated --- # Class: SmoothVoxelsUpgraderService ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Snap last_updated: 2026-06-29T19:34:08Z inherits: - JointInstance - Instance - Object type: class memory_category: BaseParts tags: - Deprecated summary: "Holds two parts together and functions identically to Weld." --- # Class: Snap > Holds two parts together and functions identically to [Weld](/docs/reference/engine/classes/Weld.md). ## Description An object that holds two objects rigidly together. Most commonly created when [BasePart:MakeJoints()](/docs/reference/engine/classes/BasePart.md) is called on parts where Inlet and Stud [SurfaceType](/docs/reference/engine/enums/SurfaceType.md) are touching. Functionally identical to [Weld](/docs/reference/engine/classes/Weld.md). See also [WeldConstraint](/docs/reference/engine/classes/WeldConstraint.md) for a newer alternative using the [constraints](/docs/en-us/physics/mechanical-constraints.md) system that does not require [C0](/docs/reference/engine/classes/JointInstance.md) or [C1](/docs/reference/engine/classes/JointInstance.md) properties to be manually set. ## Root part Every Assembly has a root part, see [BasePart:GetRootPart()](/docs/reference/engine/classes/BasePart.md). When a Snap's [C0](/docs/reference/engine/classes/JointInstance.md)/[C1](/docs/reference/engine/classes/JointInstance.md) is modified the root part will stay where it was. ## Directionality Snaps do not have any directionality. [Part0](/docs/reference/engine/classes/JointInstance.md) or [Part1](/docs/reference/engine/classes/JointInstance.md), doesn't matter. You can imagine rigid joints forming a tree branching down from the root part. All the parts down the tree from root will move, and their welded "children" in this tree will move with them. ## Inherited Members ### From [JointInstance](/docs/reference/engine/classes/JointInstance.md) - **Property `Active`** (`boolean`): Determines if the joint is currently active in the world. - **Property `C0`** (`CFrame`): Determines how the offset point is attached to - **Property `C1`** (`CFrame`): Subtracted from the C0 property to create an - **Property `Enabled`** (`boolean`): Sets whether the joint is active or not. - **Property `Part0`** (`BasePart`): The first BasePart that the joint connects. - **Property `Part1`** (`BasePart`): The second BasePart that the joint connects. - **Property `part1`** (`BasePart`): *(deprecated, hidden)* ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: SocialService last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated summary: "Facilitates social functions that impact relationships made on the Roblox platform." --- # Class: SocialService > Facilitates social functions that impact relationships made on the Roblox > platform. ## Description **SocialService** facilitates social functions that impact relationships made on the Roblox platform. Its primary usage is to show [invite prompts](/docs/en-us/production/promotion/invite-prompts.md) and the phone book to players, allowing them to send invitation requests to their friends through [PromptGameInvite()](/docs/reference/engine/classes/SocialService.md) and [PromptPhoneBook()](/docs/reference/engine/classes/SocialService.md) respectively. You may leverage signals when such requests are made. ## Methods ### Method: SocialService:CanSendCallInviteAsync **Signature:** `SocialService:CanSendCallInviteAsync(player: Instance): boolean` Returns `true` if the given [Player](/docs/reference/engine/classes/Player.md) can send a call invite to a friend. You should always use the result of this method before calling [PromptPhoneBook()](/docs/reference/engine/classes/SocialService.md) since the ability to open the phone book may vary depending on the player. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Social* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Instance` | | The [Player](/docs/reference/engine/classes/Player.md) instance of the player potentially sending a call invite. | **Returns:** `boolean` — Whether the specified player can send a call invite. **SocialService:PromptPhoneBook()** The following code sample, placed within a child [LocalScript](/docs/reference/engine/classes/LocalScript.md) of a [GuiButton](/docs/reference/engine/classes/GuiButton.md), uses [CanSendCallInviteAsync()](/docs/reference/engine/classes/SocialService.md) to confirm that the player can make a call. If so, it connects [PromptPhoneBook()](/docs/reference/engine/classes/SocialService.md) to the button's [Activated](/docs/reference/engine/classes/GuiButton.md) event. ```lua local Players = game:GetService("Players") local SocialService = game:GetService("SocialService") local player = Players.LocalPlayer local button = script.Parent button.Visible = false -- Function to check whether the player can send a call invite local function canSendCallingInvite(sendingPlayer) local success, canSend = pcall(function() return SocialService:CanSendCallInviteAsync(sendingPlayer) end) return success and canSend end local canCall = canSendCallingInvite(player) if canCall then button.Visible = true button.Activated:Connect(function() SocialService:PromptPhoneBook(player, "") end) end ``` ### Method: SocialService:CanSendGameInviteAsync **Signature:** `SocialService:CanSendGameInviteAsync(player: Instance, recipientId?: User): boolean` [CanSendGameInviteAsync()](/docs/reference/engine/classes/SocialService.md) returns `true` if the given [Player](/docs/reference/engine/classes/Player.md) can invite other players to the current experience. You should always use the result of this method before calling [PromptGameInvite()](/docs/reference/engine/classes/SocialService.md) since the ability to invite players may vary depending on the platform or player. See [Player Invite Prompts](/docs/en-us/production/promotion/invite-prompts.md) for more details on implementing player invite prompts, customizing prompts and notifications, and using launch data. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Social* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Instance` | | The [Player](/docs/reference/engine/classes/Player.md) instance of the player potentially sending an invite. | | `recipientId` | `User` | `U1.AQAAAAAAAAAAAAAAAAAAAAA` | Optional [Player.UserId](/docs/reference/engine/classes/Player.md) of the potential **recipient**, used to check whether the sender can invite that specific recipient. | **Returns:** `boolean` — Whether the specified player can send an invite. **Sending an Invite** The following code sample uses [CanSendGameInviteAsync()](/docs/reference/engine/classes/SocialService.md) to confirm whether the local [Player](/docs/reference/engine/classes/Player.md) can send an invite. If `true`, it then prompts the invite using [PromptGameInvite()](/docs/reference/engine/classes/SocialService.md). ```lua local SocialService = game:GetService("SocialService") local Players = game:GetService("Players") local player = Players.LocalPlayer -- Function to check whether the player can send an invite local function canSendGameInvite(sendingPlayer) local success, canSend = pcall(function() return SocialService:CanSendGameInviteAsync(sendingPlayer) end) return success and canSend end local canInvite = canSendGameInvite(player) if canInvite then SocialService:PromptGameInvite(player) end ``` ### Method: SocialService:GetEventRsvpStatusAsync **Signature:** `SocialService:GetEventRsvpStatusAsync(eventId: string): RsvpStatus` Returns the local player's RSVP status for the given event. Events must be in the current experience and must not have already started. If the event has already started, this method will return an error. Note that you can use [PromptRsvpToEventAsync()](/docs/reference/engine/classes/SocialService.md) to prompt the player to change their RSVP status for the event. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Social* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `eventId` | `string` | | The event ID of the event to prompt the player to change their RSVP status for. This must be a valid event ID that exists in the current experience, represented as a string (not a number). | **Returns:** `RsvpStatus` — Returns an [RsvpStatus](/docs/reference/engine/enums/RsvpStatus.md) indicating the player's current RSVP status for the event. If the player has not RSVP'd to the event, this will return [RsvpStatus.None](/docs/reference/engine/enums/RsvpStatus.md). **SocialService:PromptRsvpToEventAsync()** The following example checks if a player is RSVP'd to an event; if not, it gives them the option to RSVP. If the player is already RSVP'd, it gives them the option to cancel their RSVP. The following [Script](/docs/reference/engine/classes/Script.md) assumes that [RunContext](/docs/reference/engine/classes/BaseScript.md) is set to [RunContext.Client](/docs/reference/engine/enums/RunContext.md) and that two sibling [ProximityPrompts](/docs/reference/engine/classes/ProximityPrompt.md) are in place: one for following the event and one for unfollowing the event. The prompts are enabled or disabled based on the player's RSVP status. ```lua local SocialService = game:GetService("SocialService") local EVENT_ID = "YOUR_EVENT_ID" local followPrompt: ProximityPrompt = script.Parent.FollowProximityPrompt local unFollowPrompt: ProximityPrompt = script.Parent.UnfollowProximityPrompt local function updatePrompt(rsvpStatus: Enum.RsvpStatus) if rsvpStatus == Enum.RsvpStatus.Going then unFollowPrompt.Enabled = true followPrompt.Enabled = false else unFollowPrompt.Enabled = false followPrompt.Enabled = true end end local success, currentRsvpStatus = pcall(function() return SocialService:GetEventRsvpStatusAsync(EVENT_ID) end) if not success then -- Could not retrieve RSVP status; don't enable either proximity prompt warn("Failed to get RSVP status:", currentRsvpStatus) return end print("CurrentRsvpStatus:", currentRsvpStatus) updatePrompt(currentRsvpStatus) unFollowPrompt.Triggered:Connect(function(player) local rsvpStatus = SocialService:PromptRsvpToEventAsync(EVENT_ID) updatePrompt(rsvpStatus) end) followPrompt.Triggered:Connect(function(player) local rsvpStatus = SocialService:PromptRsvpToEventAsync(EVENT_ID) updatePrompt(rsvpStatus) end) ``` ### Method: SocialService:GetExperienceEventAsync **Signature:** `SocialService:GetExperienceEventAsync(eventId: string): ExperienceEvent?` `GetExperienceEventAsync()` yields until the request completes and then returns a dictionary describing the requested event. The dictionary contains the following fields: - `Id` (string) - `Title` (string) - `Subtitle` (string) - `Description` (string) - `DisplayTitle` (string) - `DisplaySubtitle` (string) - `DisplayDescription` (string) - `ThumbnailIds` (array of numbers): Asset IDs for the event thumbnails. The first ID is the primary thumbnail. - `StartTime` (dictionary) - `Day` (number) - `Year` (number) - `Month` (number) - `Minute` (number) - `Millisecond` (number) - `Hour` (number) - `Second` (number) - `EndTime` (dictionary) - `Day` (number) - `Year` (number) - `Month` (number) - `Minute` (number) - `Millisecond` (number) - `Hour` (number) - `Second` (number) - `HasStarted` (boolean) - `HasEnded` (boolean) - `Status` ([ExperienceEventStatus](/docs/reference/engine/enums/ExperienceEventStatus.md)) - `UserRsvpStatus` ([RsvpStatus](/docs/reference/engine/enums/RsvpStatus.md) or `nil`) If the event cannot be found, belongs to a different experience, or the response is malformed, this method returns `nil`. Requests may also fail with a `HttpError ` message if the service returns an unexpected error code. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Social* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `eventId` | `string` | | The string identifier of the event to retrieve. Must correspond to an event in the current experience. | **Returns:** `ExperienceEvent?` — A dictionary describing the event, or `nil` if the event does not exist, belongs to another experience, or is otherwise unavailable. **SocialService:GetUpcomingExperienceEventsAsync()** Finds the next event that has not started and prompts the player to RSVP. This avoids hardcoded IDs by discovering events at runtime. ```lua local SocialService = game:GetService("SocialService") -- Discover the next upcoming/active event and prompt the player to RSVP local function promptNextUpcomingEvent() local success, eventsOrError = pcall(function() return SocialService:GetUpcomingExperienceEventsAsync() end) if not success then warn("Failed to load upcoming events:", eventsOrError) return end local upcomingEvents = eventsOrError local selectedEvent for _, event in ipairs(upcomingEvents) do if not event.HasStarted and not event.HasEnded then selectedEvent = event break end end if selectedEvent then local success, result = pcall(function() return SocialService:PromptRsvpToEventAsync(selectedEvent.Id) end) if not success then warn("RSVP prompt failed:", result) end else warn("No future events available to prompt.") return end end promptNextUpcomingEvent() ``` ### Method: SocialService:GetPartyAsync **Signature:** `SocialService:GetPartyAsync(partyId: string): Array` Returns an array of dictionaries containing data for all members associated with the given `partyId`. The returned array reflects the current state of the party across all active server instances within the experience and it is ordered by the time each party member accepted the party invite. This means the first element in the array is the earliest to accept and the last is the most recent. This method is useful for retrieving up-to-date information about all party members currently in the experience and across different servers, enabling coordinated group behavior such as teleportation, matchmaking, or party-based gameplay logic. Each dictionary in the returned array contains the following fields: | Key | Value Type | Description | | --- | --- | --- | | `UserId` | number | The player's [Player.UserId](/docs/reference/engine/classes/Player.md) property. | | `PlaceId` | number | The [DataModel.PlaceId](/docs/reference/engine/classes/DataModel.md) of the place the party member is currently in. | | `JobId` | string | The [DataModel.JobId](/docs/reference/engine/classes/DataModel.md) of the server instance the user currently resides in. | | `PrivateServerId` | string | If applicable, the [DataModel.PrivateServerId](/docs/reference/engine/classes/DataModel.md) when the party member is in a private or reserved server. | | `ReservedServerAccessCode` | string | If applicable, the access code for the reserved server that the user currently resides in. Useful for teleporting party members to each other using [TeleportService:TeleportAsync()](/docs/reference/engine/classes/TeleportService.md). | To test this service in your experience, use the [Party Simulator](/docs/en-us/studio/testing-modes.md#party-simulation) in Roblox Studio or publish the experience and play it in the Roblox application. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Social* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `partyId` | `string` | | | **Returns:** `Array` — An array of dictionaries representing the members of the specified party who are currently in the experience. **SocialService:GetPartyAsync()** The following example checks if a player is in a party when they join. If so, it retrieves the latest party data using [SocialService:GetPartyAsync()](/docs/reference/engine/classes/SocialService.md) and outputs the details of each party member. ```lua local SocialService = game:GetService("SocialService") local Players = game:GetService("Players") Players.PlayerAdded:Connect(function(player) local partyId = player.PartyId if partyId == "" then warn("Player is not in a party") return end local success, partyData = pcall(function() return SocialService:GetPartyAsync(partyId) end) if success and partyData then for _, partyMemberData in partyData do print( partyMemberData.UserId, partyMemberData.PlaceId, partyMemberData.JobId, partyMemberData.PrivateServerId, partyMemberData.ReservedServerAccessCode ) end else warn("Failed to retrieve party data") end end) ``` ### Method: SocialService:GetPlayersByPartyId **Signature:** `SocialService:GetPlayersByPartyId(partyId: string): List` Returns a table of all presently connected [Player](/docs/reference/engine/classes/Player.md) objects whose [Player.PartyId](/docs/reference/engine/classes/Player.md) property matches the provided `partyId`. This method behaves similarly to [Players:GetPlayers()](/docs/reference/engine/classes/Players.md) but filters the results to include only those players belonging to the specified party. To test this service in your experience, use the [Party Simulator](/docs/en-us/studio/testing-modes.md#party-simulation) in Roblox Studio or publish the experience and play it in the Roblox application. *Security: None · Thread Safety: Unsafe · Capabilities: Social* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `partyId` | `string` | | | **Returns:** `List` — A table of [Player](/docs/reference/engine/classes/Player.md) objects whose [Player.PartyId](/docs/reference/engine/classes/Player.md) property matches the passed `partyId`. **SocialService:GetPlayersByPartyId()** The following example listens for when a player joins the experience. If the player is part of a party, it uses [SocialService:GetPlayersByPartyId()](/docs/reference/engine/classes/SocialService.md) to retrieve all players in the same party who are currently connected to the server. It then outputs the name of each party member. ```lua local SocialService = game:GetService("SocialService") local Players = game:GetService("Players") Players.PlayerAdded:Connect(function(player) local partyId = player.PartyId if partyId ~= "" then local partyPlayers: { Player } = SocialService:GetPlayersByPartyId(partyId) for _, partyMember in partyPlayers do print("Party Member: " .. partyMember.Name) end else warn("Player is not in a party") end end) ``` **Team Rebalance with Party Integration** This code sample demonstrates how to assign players to [Teams](/docs/reference/engine/classes/Team.md) based on their [Player.PartyId](/docs/reference/engine/classes/Player.md) using the [SocialService:GetPlayersByPartyId()](/docs/reference/engine/classes/SocialService.md) method. When a player joins the experience, the script checks whether they're part of a party. If so, it attempts to assign them to the same team as other party members. If no match is found or the player isn't in a party, they're placed on the team with fewer members to maintain balance. ```lua local Teams = game:GetService("Teams") local Players = game:GetService("Players") local SocialService = game:GetService("SocialService") local redTeam = Instance.new("Team") redTeam.Name = "Red Team" redTeam.TeamColor = BrickColor.Red() redTeam.AutoAssignable = false redTeam.Parent = Teams local blueTeam = Instance.new("Team") blueTeam.Name = "Blue Team" blueTeam.TeamColor = BrickColor.Blue() blueTeam.AutoAssignable = false blueTeam.Parent = Teams local function assignPlayerToTeam(player) local partyId = player.PartyId if partyId == "" then assignBalancedTeam(player) return end local teams = { redTeam, blueTeam } local matchingTeam = nil local partyPlayers = SocialService:GetPlayersByPartyId(partyId) for _, partyPlayer in partyPlayers do if partyPlayer ~= player and table.find(teams, partyPlayer.Team) then matchingTeam = partyPlayer.Team break end end if matchingTeam then player.Team = matchingTeam player.TeamColor = matchingTeam.TeamColor else assignBalancedTeam(player) end end function assignBalancedTeam(player) local redCount = #redTeam:GetPlayers() local blueCount = #blueTeam:GetPlayers() local chosenTeam = redCount <= blueCount and redTeam or blueTeam player.Team = chosenTeam player.TeamColor = chosenTeam.TeamColor end local function groupPlayersByParty() local seenPartyIds = {} local groupedPlayers = {} for _, player in Players:GetPlayers() do if player.PartyId == "" then table.insert(groupedPlayers, { player }) elseif not table.find(seenPartyIds, player.PartyId) then table.insert(seenPartyIds, player.PartyId) local partyPlayers = SocialService:GetPlayersByPartyId(player.PartyId) table.insert(groupedPlayers, partyPlayers) end end return groupedPlayers end -- Call this function to rebalance teams local function rebalanceTeams() local groups = groupPlayersByParty() table.sort(groups, function(a, b) return #a > #b end) for _, player in Players:GetPlayers() do player.Team = nil end for _, group in groups do local redCount = #redTeam:GetPlayers() local blueCount = #blueTeam:GetPlayers() local bestTeam if redCount <= blueCount then bestTeam = redTeam else bestTeam = blueTeam end for _, player in group do player.Team = bestTeam player.TeamColor = bestTeam.TeamColor end end for _, player in Players:GetPlayers() do player:LoadCharacterAsync() end end Players.PlayerAdded:Connect(function(player) assignPlayerToTeam(player) player:LoadCharacterAsync() player:GetPropertyChangedSignal("PartyId"):Connect(function() if player.PartyId ~= "" then assignPlayerToTeam(player) end end) end) ``` ### Method: SocialService:GetUpcomingExperienceEventsAsync **Signature:** `SocialService:GetUpcomingExperienceEventsAsync(): List` `GetUpcomingExperienceEventsAsync()` yields while the client requests data for the current experience and then returns an array of dictionaries, each with the same structure described in [GetExperienceEventAsync()](/docs/reference/engine/classes/SocialService.md). The array includes only events that are currently active or have not yet ended, excluding cancelled, moderated, or unpublished events. Results are sorted so the soonest start time appears first. If the service indicates there are no matching events, this method returns an empty array. Requests may raise a `HttpError ` message if the service returns an unexpected error code. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Social* **Returns:** `List` — An array of dictionaries describing each active or upcoming event in the current experience, ordered by soonest start time first. **SocialService:GetUpcomingExperienceEventsAsync()** Finds the next event that has not started and prompts the player to RSVP. This avoids hardcoded IDs by discovering events at runtime. ```lua local SocialService = game:GetService("SocialService") -- Discover the next upcoming/active event and prompt the player to RSVP local function promptNextUpcomingEvent() local success, eventsOrError = pcall(function() return SocialService:GetUpcomingExperienceEventsAsync() end) if not success then warn("Failed to load upcoming events:", eventsOrError) return end local upcomingEvents = eventsOrError local selectedEvent for _, event in ipairs(upcomingEvents) do if not event.HasStarted and not event.HasEnded then selectedEvent = event break end end if selectedEvent then local success, result = pcall(function() return SocialService:PromptRsvpToEventAsync(selectedEvent.Id) end) if not success then warn("RSVP prompt failed:", result) end else warn("No future events available to prompt.") return end end promptNextUpcomingEvent() ``` ### Method: SocialService:HideSelfView **Signature:** `SocialService:HideSelfView(): ()` Hides the calling player's self view. If this method is called while the self view is already hidden, it does nothing. *Security: None · Thread Safety: Unsafe · Capabilities: Social* **Returns:** `()` ### Method: SocialService:PromptFeedbackSubmissionAsync **Signature:** `SocialService:PromptFeedbackSubmissionAsync(options: Dictionary?): ()` Displays a feedback submission dialog to the player, allowing them to provide feedback about their experience. The submitted feedback becomes available to developers in the Creator Dashboard under the **Audience** > **Feedback** section. Players can submit feedback once per day per experience. Experience owners cannot submit feedback for their own experiences. If the player has already submitted feedback for the day, owns the experience, or is otherwise ineligible, the prompt will display a message indicating that feedback is unavailable. This method yields until the player either submits feedback or dismisses the dialog. As an asynchronous operation, it should be called using `task.spawn()` or within a coroutine. Note that this service does not work during playtesting in Roblox Studio. To test the feedback prompt, you must publish the experience and play it in the Roblox application. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Social* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `options` | `Dictionary?` | | Optional `Dictionary` configuring the prompt. Supported keys: - `FeedbackType` ([FeedbackType](/docs/reference/engine/enums/FeedbackType.md)). Selects which feedback flow to display. Defaults to [FeedbackType.Feedback](/docs/reference/engine/enums/FeedbackType.md). | **Returns:** `()` **SocialService:PromptFeedbackSubmissionAsync()** The following example shows how to prompt players for feedback at appropriate moments in your experience, such as after reaching a milestone or completing a level. ```lua local SocialService = game:GetService("SocialService") -- Function to prompt for feedback local function promptForFeedback() local success, errorMessage = pcall(function() SocialService:PromptFeedbackSubmissionAsync() end) if success then print("Feedback prompt completed") else warn("Failed to prompt for feedback:", errorMessage) end end -- Prompt for feedback when player reaches a milestone local function onMilestoneReached() promptForFeedback() end -- Example usage: Connect to a game event game.ReplicatedStorage.MilestoneReached.OnClientEvent:Connect(onMilestoneReached) ``` ### Method: SocialService:PromptGameInvite **Signature:** `SocialService:PromptGameInvite(player: Instance, experienceInviteOptions?: Instance): ()` [PromptGameInvite()](/docs/reference/engine/classes/SocialService.md) displays an invite prompt to the local player through which they may invite their friends to the current experience. Before calling this method, you should use [CanSendGameInviteAsync()](/docs/reference/engine/classes/SocialService.md) to determine whether the player can send an invite, as this ability may vary depending on the platform or player. See [Player Invite Prompts](/docs/en-us/production/promotion/invite-prompts.md) for more details on implementing invite prompts, customizing prompts and notifications, and using launch data. *Security: None · Thread Safety: Unsafe · Capabilities: Social* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Instance` | | The [Player](/docs/reference/engine/classes/Player.md) to prompt with the invite popup. | | `experienceInviteOptions` | `Instance` | `nil` | Optional [ExperienceInviteOptions](/docs/reference/engine/classes/ExperienceInviteOptions.md) object for customizing the prompt. | **Returns:** `()` **Sending an Invite** The following code sample uses [CanSendGameInviteAsync()](/docs/reference/engine/classes/SocialService.md) to confirm whether the local [Player](/docs/reference/engine/classes/Player.md) can send an invite. If `true`, it then prompts the invite using [PromptGameInvite()](/docs/reference/engine/classes/SocialService.md). ```lua local SocialService = game:GetService("SocialService") local Players = game:GetService("Players") local player = Players.LocalPlayer -- Function to check whether the player can send an invite local function canSendGameInvite(sendingPlayer) local success, canSend = pcall(function() return SocialService:CanSendGameInviteAsync(sendingPlayer) end) return success and canSend end local canInvite = canSendGameInvite(player) if canInvite then SocialService:PromptGameInvite(player) end ``` ### Method: SocialService:PromptLinkSharingAsync **Signature:** `SocialService:PromptLinkSharingAsync(player: Player, options?: Dictionary): Tuple` This call is restricted to server scripts. After a link is successfully generated, the target player will either be prompted to open a share sheet with the generated link, or the link will be copied directly to the clipboard if share sheet functionality is not available to the player. ```lua local HttpService = game:GetService("HttpService") local SocialService = game:GetService("SocialService") local success, result = pcall(function() local data = { promoCode = "j7du67" } local launchData = HttpService:JSONEncode(data) local LinkSharingOptions = { ExpirationSeconds = 20000, PreviewTitle = "Awesome Game", PreviewDescription = "Lets play together", LaunchData = launchData, } return SocialService:PromptLinkSharingAsync(player, LinkSharingOptions) end) if not success or result ~= Enum.PromptLinkSharingResult.Success then warn("PromptLinkSharingAsync failed:", tostring(result)) end ``` *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Social* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Player` | | Prompts the given [Player](/docs/reference/engine/classes/Player.md) with a Roblox platform-level share sheet with a generated share link. | | `options` | `Dictionary` | `nil` | `Dictionary` that specifies the configuration for the generated link. It includes the following optional key-value pairs: - `FallbackLinkId` (string). Determines how this share link will direct player to once expired. Default to experience join link. - `ExpirationSeconds` (number). Determines time to expiration once the share link is created. Truncates to nearest integer. Defaults to 86,400. - `PreviewTitle` (string) Preview title of the share link. - `PreviewDescription` (string) Preview description of the share link. - `PreviewAssetId` (number) Image asset ID used for share link preview. - `LaunchData` (string). Used to set a parameter in [Player:GetJoinData()](/docs/reference/engine/classes/Player.md) when a player joined using the generated share link. | **Returns:** `Tuple` — A tuple containing an [PromptLinkSharingResult](/docs/reference/engine/enums/PromptLinkSharingResult.md) indicating the result of the link sharing prompt. ### Method: SocialService:PromptPhoneBook **Signature:** `SocialService:PromptPhoneBook(player: Instance, tag: string): ()` Prompts the given [Player](/docs/reference/engine/classes/Player.md) with the phone book. If the player chooses to call someone, the [CallInviteStateChanged](/docs/reference/engine/classes/SocialService.md) event fires. You should use [CanSendCallInviteAsync()](/docs/reference/engine/classes/SocialService.md) prior to calling [PromptPhoneBook()](/docs/reference/engine/classes/SocialService.md) since the ability to see the phone book may vary depending on the player. If a player is not eligible to open the phone book, an error dialog is shown. *Security: None · Thread Safety: Unsafe · Capabilities: Social* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Instance` | | The player to prompt with the phone book. | | `tag` | `string` | | String to help differentiate between various phone book "entry points" or similar. For example, you can pass a string defining what region of an experience the calling player's character is currently in. | **Returns:** `()` **SocialService:PromptPhoneBook()** The following code sample, placed within a child [LocalScript](/docs/reference/engine/classes/LocalScript.md) of a [GuiButton](/docs/reference/engine/classes/GuiButton.md), uses [CanSendCallInviteAsync()](/docs/reference/engine/classes/SocialService.md) to confirm that the player can make a call. If so, it connects [PromptPhoneBook()](/docs/reference/engine/classes/SocialService.md) to the button's [Activated](/docs/reference/engine/classes/GuiButton.md) event. ```lua local Players = game:GetService("Players") local SocialService = game:GetService("SocialService") local player = Players.LocalPlayer local button = script.Parent button.Visible = false -- Function to check whether the player can send a call invite local function canSendCallingInvite(sendingPlayer) local success, canSend = pcall(function() return SocialService:CanSendCallInviteAsync(sendingPlayer) end) return success and canSend end local canCall = canSendCallingInvite(player) if canCall then button.Visible = true button.Activated:Connect(function() SocialService:PromptPhoneBook(player, "") end) end ``` ### Method: SocialService:PromptRsvpToEventAsync **Signature:** `SocialService:PromptRsvpToEventAsync(eventId: string): RsvpStatus` `PromptRsvpToEventAsync()` displays a prompt to the local player through which they may change their RSVP status to the given event. Events must be in the current experience and must not have already started. If the event has already started, this method will return an error. Note that you can use [GetEventRsvpStatusAsync()](/docs/reference/engine/classes/SocialService.md) to check the player's current RSVP status before calling this method. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Social* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `eventId` | `string` | | The event ID of the event to prompt the player to change their RSVP status for. This must be a valid event ID that exists in the current experience, represented as a string (not a number). | **Returns:** `RsvpStatus` — Returns a [RsvpStatus](/docs/reference/engine/enums/RsvpStatus.md) indicating the player's new RSVP status after the prompt is closed. If the player closes the prompt without changing their RSVP status, this will return [RsvpStatus.None](/docs/reference/engine/enums/RsvpStatus.md) or their old [RsvpStatus](/docs/reference/engine/enums/RsvpStatus.md) if they had already selected a status. **SocialService:PromptRsvpToEventAsync()** The following example checks if a player is RSVP'd to an event; if not, it gives them the option to RSVP. If the player is already RSVP'd, it gives them the option to cancel their RSVP. The following [Script](/docs/reference/engine/classes/Script.md) assumes that [RunContext](/docs/reference/engine/classes/BaseScript.md) is set to [RunContext.Client](/docs/reference/engine/enums/RunContext.md) and that two sibling [ProximityPrompts](/docs/reference/engine/classes/ProximityPrompt.md) are in place: one for following the event and one for unfollowing the event. The prompts are enabled or disabled based on the player's RSVP status. ```lua local SocialService = game:GetService("SocialService") local EVENT_ID = "YOUR_EVENT_ID" local followPrompt: ProximityPrompt = script.Parent.FollowProximityPrompt local unFollowPrompt: ProximityPrompt = script.Parent.UnfollowProximityPrompt local function updatePrompt(rsvpStatus: Enum.RsvpStatus) if rsvpStatus == Enum.RsvpStatus.Going then unFollowPrompt.Enabled = true followPrompt.Enabled = false else unFollowPrompt.Enabled = false followPrompt.Enabled = true end end local success, currentRsvpStatus = pcall(function() return SocialService:GetEventRsvpStatusAsync(EVENT_ID) end) if not success then -- Could not retrieve RSVP status; don't enable either proximity prompt warn("Failed to get RSVP status:", currentRsvpStatus) return end print("CurrentRsvpStatus:", currentRsvpStatus) updatePrompt(currentRsvpStatus) unFollowPrompt.Triggered:Connect(function(player) local rsvpStatus = SocialService:PromptRsvpToEventAsync(EVENT_ID) updatePrompt(rsvpStatus) end) followPrompt.Triggered:Connect(function(player) local rsvpStatus = SocialService:PromptRsvpToEventAsync(EVENT_ID) updatePrompt(rsvpStatus) end) ``` ### Method: SocialService:ShowSelfView **Signature:** `SocialService:ShowSelfView(selfViewPosition?: SelfViewPosition): ()` Shows the calling player's self view. If this method is called while the self view is already visible, it does nothing. *Security: None · Thread Safety: Unsafe · Capabilities: Social* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `selfViewPosition` | `SelfViewPosition` | `LastPosition` | The position to place the self view . | **Returns:** `()` ### Method: SocialService:PromptLinkSharing **Signature:** `SocialService:PromptLinkSharing(player: Player, options?: Dictionary): Tuple` *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Social* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Player` | | Prompts the given [Player](/docs/reference/engine/classes/Player.md) with a Roblox platform-level share sheet with a generated share link. | | `options` | `Dictionary` | `nil` | `Dictionary` that specifies the configuration for the generated link. It includes the following optional key-value pairs: - `FallbackLinkId` (string). Determines how this share link will direct player to once expired. Default to experience join link. - `ExpirationSeconds` (number). Determines time to expiration once the share link is created. Truncates to nearest integer. Defaults to 86,400. - `PreviewTitle` (string) Preview title of the share link. - `PreviewDescription` (string) Preview description of the share link. - `PreviewAssetId` (number) Image asset ID used for share link preview. - `LaunchData` (string). Used to set a parameter in [Player:GetJoinData()](/docs/reference/engine/classes/Player.md) when a player joined using the generated share link. | **Returns:** `Tuple` — A tuple containing an [PromptLinkSharingResult](/docs/reference/engine/enums/PromptLinkSharingResult.md) indicating the result of the link sharing prompt. ## Events ### Event: SocialService.CallInviteStateChanged **Signature:** `SocialService.CallInviteStateChanged(player: Instance, inviteState: InviteState)` This event fires when a player's call invite state changes. *Security: None · Capabilities: Social* **Parameters:** | Name | Type | Description | |------|------|-------------| | `player` | `Instance` | The [Player](/docs/reference/engine/classes/Player.md) instance of the player who had a call invite state change. | | `inviteState` | `InviteState` | The new call invite state. | **SocialService.CallInviteStateChanged** ```lua local SocialService = game:GetService("SocialService") local button = script.Parent local isPhonebookOpen = false SocialService.CallInviteStateChanged:Connect(function(_, inviteState) local isCalling = inviteState == Enum.InviteState.Placed if isCalling or isPhonebookOpen then button.Visible = false else button.Visible = true end end) ``` ### Event: SocialService.GameInvitePromptClosed **Signature:** `SocialService.GameInvitePromptClosed(player: Instance, recipientIds: Array)` This event fires when a player closes an invite prompt. *Security: None · Capabilities: Social* **Parameters:** | Name | Type | Description | |------|------|-------------| | `player` | `Instance` | The [Player](/docs/reference/engine/classes/Player.md) instance of the player who closed the prompt. | | `recipientIds` | `Array` | No longer populated; an empty array. | ### Event: SocialService.PhoneBookPromptClosed **Signature:** `SocialService.PhoneBookPromptClosed(player: Instance)` Fires when a player closes the phone book prompt. *Security: None · Capabilities: Social* **Parameters:** | Name | Type | Description | |------|------|-------------| | `player` | `Instance` | The [Player](/docs/reference/engine/classes/Player.md) instance of the player who closed the phone book. | ### Event: SocialService.ShareSheetClosed **Signature:** `SocialService.ShareSheetClosed(player: Player)` *Security: None · Capabilities: Social* **Parameters:** | Name | Type | Description | |------|------|-------------| | `player` | `Player` | | ## Callbacks ### Callback: SocialService.OnCallInviteInvoked **Signature:** `SocialService.OnCallInviteInvoked(tag: string, callParticipantIds: Array): Instance` A callback to process when a call is placed from the phone book. The `tag` parameter can be used to differentiate between different "entry points" or similar, as described in [PromptPhoneBook()](/docs/reference/engine/classes/SocialService.md). Only one callback can be set. *Security: None · Thread Safety: Unsafe · Capabilities: Social* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `tag` | `string` | | String to help differentiate between various phone book entry points. | | `callParticipantIds` | `Array` | | Array containing all of the players involved in the call. The caller will always be the first player in the array. | **Returns:** `Instance` — Table including the `PlaceId` and `ReservedServerAccessCode` keys whose values are the [DataModel.PlaceId](/docs/reference/engine/classes/DataModel.md) and the server access code returned by [TeleportService:ReserveServerAsync()](/docs/reference/engine/classes/TeleportService.md), respectively. **SocialService.OnCallInviteInvoked** ```lua local SocialService = game:GetService("SocialService") local TeleportService = game:GetService("TeleportService") SocialService.OnCallInviteInvoked = function() local placeId = 0123456789 -- This is the place ID of the desired place to drop call participants into local accessCode = TeleportService:ReserveServerAsync(placeId) return { ReservedServerAccessCode = accessCode, PlaceId = placeId } end ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: SolidModelContentProvider last_updated: 2026-06-29T19:34:08Z inherits: - CacheableContentProvider - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated summary: "An internal service which serves no functionality to developers." --- # Class: SolidModelContentProvider > An internal service which serves no functionality to developers. ## Description An internal [ContentProvider](/docs/reference/engine/classes/ContentProvider.md) for preloading solid models. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Sound last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Internal summary: "An object that emits sound. This object can be placed within a BasePart or Attachment to emit a sound from a particular position within a place or world, or it can be attached elsewhere to play the sound at a constant volume throughout the entire place." --- # Class: Sound > An object that emits sound. This object can be placed within a > [BasePart](/docs/reference/engine/classes/BasePart.md) or [Attachment](/docs/reference/engine/classes/Attachment.md) to emit a sound from a particular > position within a place or world, or it can be attached elsewhere to play the > sound at a constant volume throughout the entire place. ## Description [Sound](/docs/reference/engine/classes/Sound.md) is an object that emits sound. When placed in a [BasePart](/docs/reference/engine/classes/BasePart.md) or an [Attachment](/docs/reference/engine/classes/Attachment.md), this object will emit its sound from that part's [BasePart.Position](/docs/reference/engine/classes/BasePart.md) or the attachment's [Attachment.WorldPosition](/docs/reference/engine/classes/Attachment.md). In this placement, a [Sound](/docs/reference/engine/classes/Sound.md) exhibits the Doppler effect, meaning its frequency and pitch varies with the relative motion of whatever attachment or part it is attached to. Additionally, its volume will be determined by the distance between the client's sound listener (by default the [Camera](/docs/reference/engine/classes/Camera.md) position) and the position of the sound's parent. For more information, see [RollOffMode](/docs/reference/engine/classes/Sound.md). A sound is considered "global" if it is **not** parented to a [BasePart](/docs/reference/engine/classes/BasePart.md) or an [Attachment](/docs/reference/engine/classes/Attachment.md). In this case, the sound will play at the same volume throughout the entire place. ## Code Samples **Music Playing Part** The code in this sample demonstrates how a sound parented to a Part or Attachment will play locally and experience volume drop off the further the player's camera is away from the part. A part is instanced, and a sound is instanced and parented to the part. A click detector is set up on the part that will check if the sound is playing, using [Sound.IsPlaying](/docs/reference/engine/classes/Sound.md) and play or stop the sound depending. ```lua local part = Instance.new("Part") part.Anchored = true part.Position = Vector3.new(0, 3, 0) part.BrickColor = BrickColor.new("Bright red") part.Name = "MusicBox" part.Parent = workspace -- create a sound local sound = Instance.new("Sound", part) sound.SoundId = "rbxassetid://9120386436" sound.EmitterSize = 5 -- decrease the emitter size (for earlier volume drop off) sound.Looped = true sound.Parent = part local clickDetector = Instance.new("ClickDetector") clickDetector.Parent = part -- toggle the sound playing / not playing clickDetector.MouseClick:Connect(function() if not sound.IsPlaying then part.BrickColor = BrickColor.new("Bright green") sound:Play() else part.BrickColor = BrickColor.new("Bright red") sound:Stop() end end) ``` ## Properties ### Property: Sound.AcousticSimulationEnabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Emitter", "capabilities": [ "LegacySound" ] } ``` ### Property: Sound.IsLoaded ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Asset", "capabilities": [ "LegacySound" ] } ``` This property is `true` when the [Sound](/docs/reference/engine/classes/Sound.md) has loaded from Roblox servers and is ready to play. You can use this property and the [Loaded](/docs/reference/engine/classes/Sound.md) event to verify a sound has loaded before playing it. **Load Sound** This simple function will verify a Sound has loaded by checking the Sound.IsLoaded property. If Sound.IsLoaded is false it will wait for the Loaded event before resuming. It is important to check Sound.IsLoaded before connecting to the Sound.Loaded event, as if the sound has already loaded the Sound.Loaded event will not fire and the function will yield indefinitely. ```lua local sound = script.Parent.Sound if not sound.IsLoaded then sound.Loaded:Wait() end print("The sound has loaded!") ``` ### Property: Sound.Looped ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Playback", "capabilities": [ "LegacySound" ] } ``` This sets whether or not the [Sound](/docs/reference/engine/classes/Sound.md) repeats once it has finished playing. Looped sounds are suitable for a range of applications including music and background ambient sounds. The [DidLoop](/docs/reference/engine/classes/Sound.md) event can be used to track the number of times as sound has looped. **Loop a Number of Times** This code sample includes a function that will play a sound and allow it to loop for a given number of times before stopping it. ```lua local function loopNTimes(sound, numberOfLoops) if not sound.IsPlaying then sound.Looped = true local connection = nil connection = sound.DidLoop:Connect(function(_soundId, numOfTimesLooped) print(numOfTimesLooped) if numOfTimesLooped >= numberOfLoops then -- disconnect the connection connection:Disconnect() -- stop the sound sound:Stop() end end) sound:Play() end end local sound = Instance.new("Sound") sound.SoundId = "rbxassetid://0" loopNTimes(sound, 5) ``` ### Property: Sound.LoopRegion ```json { "type": "NumberRange", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Regions", "capabilities": [ "LegacySound" ] } ``` A range denoting a desired loop start and loop end within the [PlaybackRegion](/docs/reference/engine/classes/Sound.md), in seconds. - If [LoopRegion.Min](/docs/reference/engine/classes/Sound.md) `>` [PlaybackRegion.Min](/docs/reference/engine/classes/Sound.md), the loop starts from [LoopRegion.Min](/docs/reference/engine/classes/Sound.md). - If [LoopRegion.Min](/docs/reference/engine/classes/Sound.md) `<` [PlaybackRegion.Min](/docs/reference/engine/classes/Sound.md), the loop starts from [PlaybackRegion.Min](/docs/reference/engine/classes/Sound.md). - If [LoopRegion.Max](/docs/reference/engine/classes/Sound.md) `>` [PlaybackRegion.Max](/docs/reference/engine/classes/Sound.md), the loop starts at [PlaybackRegion.Max](/docs/reference/engine/classes/Sound.md). - If [LoopRegion.Max](/docs/reference/engine/classes/Sound.md) `<` [PlaybackRegion.Max](/docs/reference/engine/classes/Sound.md), the loop starts at **exactly** that time. - If [LoopRegion.Min](/docs/reference/engine/classes/Sound.md) `==` [LoopRegion.Max](/docs/reference/engine/classes/Sound.md), the [Sound](/docs/reference/engine/classes/Sound.md) uses the [PlaybackRegion](/docs/reference/engine/classes/Sound.md) property instead. ### Property: Sound.PlaybackLoudness ```json { "type": "double", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Playback", "capabilities": [ "LegacySound" ] } ``` A number between `0` and `1000` indicating how loud the [Sound](/docs/reference/engine/classes/Sound.md) is currently playing back. This property reflects the amplitude of the sound's playback in the instance of time it is read. **Volume Amplitude Bar** In this sample Sound.PlaybackLoudness is used to create an amplitude bar that shows the amplitude of a sound playing. This code sample should be placed in StarterPlayerScripts. A simple GUI is created, a frame holding that bar and a frame containing the bar. A Sound is then played and the size of the bar is set to reflect the Sound.PlaybackLoudness on a loop. ```lua -- to be placed in StarterPlayer > StarterPlayerScripts local Players = game:GetService("Players") -- wait for local player PlayerGui local LocalPlayer = Players.LocalPlayer local playerGui = LocalPlayer:WaitForChild("PlayerGui") -- create a ScreenGui local screenGui = Instance.new("ScreenGui") screenGui.Parent = playerGui -- create a holder for our bar local frame = Instance.new("Frame") frame.AnchorPoint = Vector2.new(0.5, 0.5) frame.Position = UDim2.new(0.5, 0, 0.5, 0) frame.Size = UDim2.new(0.3, 0, 0.05, 0) frame.Parent = screenGui -- create a bar local bar = Instance.new("Frame") bar.Position = UDim2.new(0, 0, 0, 0) bar.Size = UDim2.new(1, 0, 1, 0) bar.BackgroundColor3 = Color3.new(0, 1, 0) bar.Parent = frame -- create a sound local sound = Instance.new("Sound") sound.SoundId = "rbxassetid://9120386436" sound.Looped = true sound.Parent = screenGui sound:Play() -- define a max loudness local maxLoudness = 30 -- animate the amplitude bar while true do local amplitude = math.clamp(sound.PlaybackLoudness / maxLoudness, 0, 1) bar.Size = UDim2.new(amplitude, 0, 1, 0) wait(0.05) end ``` ### Property: Sound.PlaybackRegion ```json { "type": "NumberRange", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Regions", "capabilities": [ "LegacySound" ] } ``` A range denoting a desired start and stop time within the [TimeLength](/docs/reference/engine/classes/Sound.md), in seconds. - If [PlaybackRegion.Min](/docs/reference/engine/classes/Sound.md) `>` `0`, the sound begins to play from the [PlaybackRegion.Min](/docs/reference/engine/classes/Sound.md) time. - If [PlaybackRegion.Min](/docs/reference/engine/classes/Sound.md) `<` `0`, the sound begins to play from `0`. - If [PlaybackRegion.Max](/docs/reference/engine/classes/Sound.md) `>` [Sound.TimeLength](/docs/reference/engine/classes/Sound.md), the sound stops at [Sound.TimeLength](/docs/reference/engine/classes/Sound.md). - If [PlaybackRegion.Max](/docs/reference/engine/classes/Sound.md) `<` [Sound.TimeLength](/docs/reference/engine/classes/Sound.md), the sound stops at **exactly** that time. - If [PlaybackRegion.Min](/docs/reference/engine/classes/Sound.md) `==` [PlaybackRegion.Max](/docs/reference/engine/classes/Sound.md), this property is inactive. ### Property: Sound.PlaybackRegionsEnabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Playback", "capabilities": [ "LegacySound" ] } ``` If `true`, this property gives your [Sound](/docs/reference/engine/classes/Sound.md) access to the [PlaybackRegion](/docs/reference/engine/classes/Sound.md) and [LoopRegion](/docs/reference/engine/classes/Sound.md) properties which can more-accurately control its playback. ### Property: Sound.PlaybackSpeed ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Playback", "capabilities": [ "LegacySound" ] } ``` Determines the speed at which a [Sound](/docs/reference/engine/classes/Sound.md) will play, with higher values causing the sound to play faster and at a higher pitch. **Sound PlaybackSpeed** In this example a Sound is played in the Workspace. The PlaybackSpeed property is increased and decreased at intervals to demonstrate its impact on the playback of the Sound. ```lua local sound = Instance.new("Sound") sound.SoundId = "rbxassetid://9120386436" sound.Looped = true sound.Parent = workspace sound:Play() task.wait(10) sound.PlaybackSpeed = 3 -- 3x faster task.wait(5) sound.PlaybackSpeed = 0.5 -- 2x slower task.wait(5) sound.PlaybackSpeed = 1 -- default ``` ### Property: Sound.Playing ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Playback", "capabilities": [ "LegacySound" ] } ``` Indicates whether the [Sound](/docs/reference/engine/classes/Sound.md) is currently playing. This can be toggled, and this property will always replicate. In Studio's [Properties](/docs/en-us/studio/properties.md) window, while in **Edit** mode, toggling [Playing](/docs/reference/engine/classes/Sound.md) to `true` does not begin playing the sound, but the sound will begin playing during runtime. This property should not be confused with [IsPlaying](/docs/reference/engine/classes/Sound.md) which is a read-only property. Note that when [Playing](/docs/reference/engine/classes/Sound.md) is set to `false`, the [TimePosition](/docs/reference/engine/classes/Sound.md) property of the sound will not reset, meaning that when [Playing](/docs/reference/engine/classes/Sound.md) is set to `true` again, the audio will continue from the time position it was at when it was stopped. However, if the [Play()](/docs/reference/engine/classes/Sound.md) function is used to resume the sound, the time position will reset to `0`. **Sound Playing** This sample demonstrates how the Sound.Playing property can be used to start and stop playback of a sound. A sound is instanced in the Workspace and playback is started by setting Sound.Playing to true. After ten seconds the playback is stopped by setting Sound.Playing to false. When the playback is again resumed using Sound.Playing it resumes at the previous Sound.TimePosition it was stopped at. This is demonstrated by printing the TimePosition property immediately after resuming the sound. ```lua local sound = Instance.new("Sound") sound.SoundId = "rbxassetid://9120386436" sound.Looped = true sound.Parent = workspace print("playing sound") sound.Playing = true task.wait(10) print("stopping sound") sound.Playing = false task.wait(5) sound.Playing = true local timePosition = sound.TimePosition print("resumed at time position: " .. tostring(timePosition)) -- c. 10 seconds ``` ### Property: Sound.PlayOnRemove ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "LegacySound" ] } ``` When `true`, the [Sound](/docs/reference/engine/classes/Sound.md) will play when it is removed from the experience by parenting the [Sound](/docs/reference/engine/classes/Sound.md) or one if its ancestors to `nil`. This means all of the following will cause the sound to play when [PlayOnRemove](/docs/reference/engine/classes/Sound.md) is `true`: - `sound:Destroy()` - `sound.Parent = nil` - `sound.Parent.Parent = nil` ### Property: Sound.RollOffMaxDistance ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Emitter", "capabilities": [ "LegacySound" ] } ``` The maximum distance, in studs, a client's listener can be from the sound's origin and still hear it. Only applies to [Sounds](/docs/reference/engine/classes/Sound.md) parented to a [BasePart](/docs/reference/engine/classes/BasePart.md) or [Attachment](/docs/reference/engine/classes/Attachment.md). How [RollOffMaxDistance](/docs/reference/engine/classes/Sound.md) impacts the attenuation of a sound (manner in which it fades out) is dependent on the [RollOffMode](/docs/reference/engine/classes/Sound.md) property. ### Property: Sound.RollOffMinDistance ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Emitter", "capabilities": [ "LegacySound" ] } ``` The minimum distance, in studs, at which a [Sound](/docs/reference/engine/classes/Sound.md) which is parented to a [BasePart](/docs/reference/engine/classes/BasePart.md) or [Attachment](/docs/reference/engine/classes/Attachment.md) will begin to attenuate (decrease in volume). How [RollOffMinDistance](/docs/reference/engine/classes/Sound.md) impacts the attenuation of a sound (manner in which it fades out) is dependent on the [RollOffMode](/docs/reference/engine/classes/Sound.md) property. ### Property: Sound.RollOffMode ```json { "type": "RollOffMode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Emitter", "capabilities": [ "LegacySound" ] } ``` This property controls how the volume of a [Sound](/docs/reference/engine/classes/Sound.md) which is parented to a [BasePart](/docs/reference/engine/classes/BasePart.md) or [Attachment](/docs/reference/engine/classes/Attachment.md) attenuates (fades out) as the distance between the listener and parent changes. For details on the different modes, see [RollOffMode](/docs/reference/engine/enums/RollOffMode.md). ### Property: Sound.SoundGroup ```json { "type": "SoundGroup", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Routing", "capabilities": [ "LegacySound" ] } ``` The [SoundGroup](/docs/reference/engine/classes/SoundGroup.md) that is linked to this [Sound](/docs/reference/engine/classes/Sound.md). ### Property: Sound.SoundId ```json { "type": "ContentId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Asset", "capabilities": [ "LegacySound" ] } ``` This property is the content ID of the sound file to associate with the [Sound](/docs/reference/engine/classes/Sound.md). See [Audio Assets](/docs/en-us/audio/assets.md) for more information. ### Property: Sound.TimeLength ```json { "type": "double", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Asset", "capabilities": [ "LegacySound" ] } ``` The length of the [Sound](/docs/reference/engine/classes/Sound.md) in seconds. If the [Sound](/docs/reference/engine/classes/Sound.md) is not loaded, this value will be `0`. This property is often used in conjunction with [PlaybackSpeed](/docs/reference/engine/classes/Sound.md) to adjust the speed of a sound so that it lasts for a specific duration. **Play a Sound for a Specific Duration** This code sample includes a simple function that uses Sound.TimeLength and Sound.PlaybackSpeed to play a sound that'll take the given duration to complete. It achieves this by setting the PlaybackSpeed of the sound to be equal to the TimeLength of the sound divided by the desired duration. Note that as TimeLength is equal to 0 when the sound has not loaded, the function will yield while it loads the sound. ```lua local function playForDuration(sound, duration) if not sound.IsLoaded then sound.Loaded:wait() end local speed = sound.TimeLength / duration sound.PlaybackSpeed = speed sound:Play() end local sound = script.Parent playForDuration(sound, 5) ``` ### Property: Sound.TimePosition ```json { "type": "double", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Playback", "capabilities": [ "LegacySound" ] } ``` This property reflects the progress of the [Sound](/docs/reference/engine/classes/Sound.md) in seconds. It can be changed to move the playback position of the sound both before and during playback. As a [Sound](/docs/reference/engine/classes/Sound.md) is played, [TimePosition](/docs/reference/engine/classes/Sound.md) increases at a rate of [PlaybackSpeed](/docs/reference/engine/classes/Sound.md) per second. Once [TimePosition](/docs/reference/engine/classes/Sound.md) reaches [TimeLength](/docs/reference/engine/classes/Sound.md), the sound will stop unless it is [Looped](/docs/reference/engine/classes/Sound.md). Note that setting [TimePosition](/docs/reference/engine/classes/Sound.md) to a value greater than the length in a looped track will not cause it to wrap around. If that behavior is desired, consider the following code snippet: ``` local newPosition = 1.5 if newPosition >= sound.TimeLength then newPosition = newPosition - sound.TimeLength end sound.TimePosition = newPosition ``` **Sound TimePosition** This sample demonstrates how Sound.TimePosition can be used to jump to particular points in an audio file both before and during Sound playback. A Sound is created in the Workspace and set to start at 30 seconds in. During playback, it jumps forwards to 100 seconds and then back to the start (0 seconds). ```lua local sound = Instance.new("Sound") sound.SoundId = "rbxassetid://9120386436" sound.Parent = workspace sound.TimePosition = 30 sound:Play() task.wait(5) sound.TimePosition = 100 task.wait(5) sound.TimePosition = 0 ``` ### Property: Sound.Volume ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Playback", "capabilities": [ "LegacySound" ] } ``` The volume of the [Sound](/docs/reference/engine/classes/Sound.md). Can be set between `0` and `10` and defaults to `0.5`. Note that if the [Sound](/docs/reference/engine/classes/Sound.md) is a member of a [SoundGroup](/docs/reference/engine/classes/SoundGroup.md), its playback volume (but not its [Volume](/docs/reference/engine/classes/Sound.md) property) will be influenced by the group's [SoundGroup.Volume](/docs/reference/engine/classes/SoundGroup.md) property. ### Property: Sound.EmitterSize ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "LegacySound" ] } ``` > **Deprecated:** This property has deprecated in favor of [Sound.RollOffMinDistance](/docs/reference/engine/classes/Sound.md) and [Sound.RollOffMaxDistance](/docs/reference/engine/classes/Sound.md) which should be used instead in new work. The minimum distance, in studs, at which a 3D [Sound](/docs/reference/engine/classes/Sound.md) (direct child of a [BasePart](/docs/reference/engine/classes/BasePart.md) or [Attachment](/docs/reference/engine/classes/Attachment.md)) will begin to attenuate (decrease in volume). Sounds parented to a [BasePart](/docs/reference/engine/classes/BasePart.md) or [Attachment](/docs/reference/engine/classes/Attachment.md) that are descendants of the [Workspace](/docs/reference/engine/classes/Workspace.md) are considered 3D sounds and their volume while playing is dependent on the distance between the client's sound listener ([Camera](/docs/reference/engine/classes/Camera.md) position by default) and the Sound's parent. Two properties influence this behavior EmitterSize and [Sound.RollOffMode](/docs/reference/engine/classes/Sound.md). The way the [Sound](/docs/reference/engine/classes/Sound.md) attenuates (fades out) after the distance between the listener and the sound exceeds the EmitterSize is determined by RollOffMode. **Music Playing Part** The code in this sample demonstrates how a sound parented to a Part or Attachment will play locally and experience volume drop off the further the player's camera is away from the part. A part is instanced, and a sound is instanced and parented to the part. A click detector is set up on the part that will check if the sound is playing, using [Sound.IsPlaying](/docs/reference/engine/classes/Sound.md) and play or stop the sound depending. ```lua local part = Instance.new("Part") part.Anchored = true part.Position = Vector3.new(0, 3, 0) part.BrickColor = BrickColor.new("Bright red") part.Name = "MusicBox" part.Parent = workspace -- create a sound local sound = Instance.new("Sound", part) sound.SoundId = "rbxassetid://9120386436" sound.EmitterSize = 5 -- decrease the emitter size (for earlier volume drop off) sound.Looped = true sound.Parent = part local clickDetector = Instance.new("ClickDetector") clickDetector.Parent = part -- toggle the sound playing / not playing clickDetector.MouseClick:Connect(function() if not sound.IsPlaying then part.BrickColor = BrickColor.new("Bright green") sound:Play() else part.BrickColor = BrickColor.new("Bright red") sound:Stop() end end) ``` ### Property: Sound.isPlaying ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "LegacySound" ] } ``` > **Deprecated:** This deprecated property is a variant of [Sound.IsPlaying](/docs/reference/engine/classes/Sound.md) which should be used instead. ### Property: Sound.MaxDistance ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "LegacySound" ] } ``` > **Deprecated:** This property has deprecated in favor of [Sound.RollOffMinDistance](/docs/reference/engine/classes/Sound.md) and [Sound.RollOffMaxDistance](/docs/reference/engine/classes/Sound.md) which should be used instead in new work. The maximum distance, in studs, a client's listener can be from the [Sound](/docs/reference/engine/classes/Sound.md) origin and still hear it. Only applies to Sounds parented to a [Part](/docs/reference/engine/classes/Part.md) or [Attachment](/docs/reference/engine/classes/Attachment.md) (3D sounds). How MaxDistance impacts the attenuation of a sound (manner in which it fades out) is dependent on the [Sound.RollOffMode](/docs/reference/engine/classes/Sound.md) property. When RollOffMode is set to use an inverse type distance model (Inverse or InverseTapered) the MaxDistance will not effect the attenuation of the sound. This means that low values for MaxDistance will cause the sound to abruptly cut off when the listener reaches the MaxDistance. In most cases this is not desirable and developers are advised not to use low MaxDistance values. When RollOffMode is set to a linear type distance model (Linear or LinearSquared) the sound will attenuate between [Sound.EmitterSize](/docs/reference/engine/classes/Sound.md) and MaxDistance (with playback volume reaching zero at MaxDistance). This is less realistic, but in some cases allows attenuation to be handled in a more intuitive way. **Sound MaxDistance** This sample includes a brief demonstration of how Sound.MaxDistance works. A Part is instanced in the Workspace with a looped Sound playing within it. A function is made to listen to the Sound's MaxDistance and change the size of a sphere whenever it changes. The sphere's radius will always be equal to the Sound's MaxDistance. This example helps demonstrate how the sound is only audible when the client's listener (by default the Camera's position) is within the sphere. ```lua local part = Instance.new("Part") part.Position = Vector3.new(0, 3, 0) part.Size = Vector3.new(1, 1, 1) part.Anchored = true part.Parent = workspace local sphere = Instance.new("Part") sphere.Name = "MaxDistanceSphere" sphere.CFrame = part.CFrame sphere.Anchored = true sphere.CanCollide = false sphere.Size = Vector3.new(1, 1, 1) sphere.Shape = Enum.PartType.Ball sphere.Transparency = 0.8 sphere.BrickColor = BrickColor.new("Lime green") sphere.Parent = workspace local sound = Instance.new("Sound") sound.Name = "TestSound" sound.MaxDistance = 20 sound.SoundId = "rbxasset://sounds/uuhhh.mp3" -- oof sound.Looped = true sound.Parent = part -- a function that changes the sphere size to reflect the sound's MaxDistance local function updateSize() local distance = sound.MaxDistance sphere.Size = Vector3.new(distance * 2, distance * 2, distance * 2) end -- update the sphere size updateSize() sound:GetPropertyChangedSignal("MaxDistance"):Connect(updateSize) sound:Play() ``` ### Property: Sound.MinDistance ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "LegacySound" ] } ``` > **Deprecated:** MinDistance has been superseded by [Sound.EmitterSize](/docs/reference/engine/classes/Sound.md), whose name better describes this properties behavior. The minimum distance at which a 3D [Sound](/docs/reference/engine/classes/Sound.md) (direct child of a [BasePart](/docs/reference/engine/classes/BasePart.md) or [Attachment](/docs/reference/engine/classes/Attachment.md)) will begin to attenuate. Effectively, the emitter size. ### Property: Sound.Pitch ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "LegacySound" ] } ``` > **Deprecated:** This property has been deprecated in favor of [Sound.PlaybackSpeed](/docs/reference/engine/classes/Sound.md) whose name suits the behavior better. Sets how high pitched and fast a [Sound](/docs/reference/engine/classes/Sound.md) is when it is played. The greater the integer, the higher and faster the sound is. ### Property: Sound.AudioContent *(hidden)* ```json { "type": "Content", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Asset", "capabilities": [ "LegacySound" ] } ``` This property is a reference to an audio asset. ### Property: Sound.IsPaused *(hidden)* ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Playback", "capabilities": [ "LegacySound" ] } ``` This read-only property returns `true` when the [Sound](/docs/reference/engine/classes/Sound.md) is not playing. Note that it can return `true` if a sound has been paused using [Pause()](/docs/reference/engine/classes/Sound.md), if it has been stopped using [Stop()](/docs/reference/engine/classes/Sound.md), or the sound has never been played. As [IsPaused](/docs/reference/engine/classes/Sound.md) is read-only, it cannot be used to stop the sound; [Stop()](/docs/reference/engine/classes/Sound.md) or [Pause()](/docs/reference/engine/classes/Sound.md) should be used instead. **Sound IsPlaying and SoundIsPaused** This code sample contains demonstrates when the Sound.IsPlaying and Sound.IsPaused properties will be true or false. A sound is instanced in the Workspace and the Sound.IsLoaded property is checked to ensure it has loaded, if it has not the Sound.Loaded event is used to yield the script until the sound has. As the sound is played, paused and stopped the Sound.IsPlaying and Sound.IsPaused properties are printed to demonstrate how they respond to each of these functions. Note Sound.IsPaused will always be true if even if the sound has been stopped rather than paused. ```lua local sound = Instance.new("Sound") sound.SoundId = "rbxassetid://9120386436" sound.Looped = true sound.Parent = workspace if not sound.isLoaded then sound.Loaded:Wait() end sound:Play() print(sound.IsPlaying, sound.IsPaused) -- true, false task.wait(2) sound:Pause() print(sound.IsPlaying, sound.IsPaused) -- false, true task.wait(2) sound:Play() print(sound.IsPlaying, sound.IsPaused) -- true, false task.wait(2) sound:Stop() print(sound.IsPlaying, sound.IsPaused) -- false, true ``` ### Property: Sound.IsPlaying *(hidden)* ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Playback", "capabilities": [ "LegacySound" ] } ``` This read-only property returns true when the [Sound](/docs/reference/engine/classes/Sound.md) is playing. As [IsPlaying](/docs/reference/engine/classes/Sound.md) is read-only, it cannot be used to play the sound; [Play()](/docs/reference/engine/classes/Sound.md) should be used instead. **Sound IsPlaying and SoundIsPaused** This code sample contains demonstrates when the Sound.IsPlaying and Sound.IsPaused properties will be true or false. A sound is instanced in the Workspace and the Sound.IsLoaded property is checked to ensure it has loaded, if it has not the Sound.Loaded event is used to yield the script until the sound has. As the sound is played, paused and stopped the Sound.IsPlaying and Sound.IsPaused properties are printed to demonstrate how they respond to each of these functions. Note Sound.IsPaused will always be true if even if the sound has been stopped rather than paused. ```lua local sound = Instance.new("Sound") sound.SoundId = "rbxassetid://9120386436" sound.Looped = true sound.Parent = workspace if not sound.isLoaded then sound.Loaded:Wait() end sound:Play() print(sound.IsPlaying, sound.IsPaused) -- true, false task.wait(2) sound:Pause() print(sound.IsPlaying, sound.IsPaused) -- false, true task.wait(2) sound:Play() print(sound.IsPlaying, sound.IsPaused) -- true, false task.wait(2) sound:Stop() print(sound.IsPlaying, sound.IsPaused) -- false, true ``` ## Methods ### Method: Sound:Pause **Signature:** `Sound:Pause(): ()` This method pauses playback of the [Sound](/docs/reference/engine/classes/Sound.md) if it is playing, setting [Playing](/docs/reference/engine/classes/Sound.md) to `false`. Unlike [Stop()](/docs/reference/engine/classes/Sound.md), it does not reset [TimePosition](/docs/reference/engine/classes/Sound.md), meaning the sound can be resumed using [Resume()](/docs/reference/engine/classes/Sound.md). *Security: None · Thread Safety: Unsafe · Capabilities: LegacySound* **Returns:** `()` **Sound Functions** This sample gives a simple demonstration of what each of the Sound functions (Sound.Play, Sound.Stop, Sound.Pause and Sound.Resume) do to Sound.Playing and Sound.TimePosition. ```lua -- create a sound local sound = Instance.new("Sound", game.Workspace) sound.SoundId = "rbxassetid://9120386436" if not sound.IsLoaded then sound.Loaded:Wait() end -- listen for events sound.Played:Connect(function(_soundId) print("Sound.Played event") end) sound.Paused:Connect(function(_soundId) print("Sound.Paused event") end) sound.Resumed:Connect(function(_soundId) print("Sound.Resumed event") end) sound.Stopped:Connect(function(_soundId) print("Sound.Stopped event") end) sound:Play() print("play", sound.Playing, sound.TimePosition) -- play true 0 task.wait(10) sound:Pause() print("pause", sound.Playing, sound.TimePosition) -- pause false 10 task.wait(3) sound:Resume() print("play", sound.Playing, sound.TimePosition) -- play true 10 task.wait(3) sound:Stop() print("stop", sound.Playing, sound.TimePosition) -- stop false 0 task.wait(3) sound:Play() print("play", sound.Playing, sound.TimePosition) -- play true 0 ``` ### Method: Sound:Play **Signature:** `Sound:Play(): ()` This method plays the [Sound](/docs/reference/engine/classes/Sound.md) and sets [TimePosition](/docs/reference/engine/classes/Sound.md) to the last value set by a script (or `0` if it has not been set), then sets [Playing](/docs/reference/engine/classes/Sound.md) to `true`. *Security: None · Thread Safety: Unsafe · Capabilities: LegacySound* **Returns:** `()` **Sound Functions** This sample gives a simple demonstration of what each of the Sound functions (Sound.Play, Sound.Stop, Sound.Pause and Sound.Resume) do to Sound.Playing and Sound.TimePosition. ```lua -- create a sound local sound = Instance.new("Sound", game.Workspace) sound.SoundId = "rbxassetid://9120386436" if not sound.IsLoaded then sound.Loaded:Wait() end -- listen for events sound.Played:Connect(function(_soundId) print("Sound.Played event") end) sound.Paused:Connect(function(_soundId) print("Sound.Paused event") end) sound.Resumed:Connect(function(_soundId) print("Sound.Resumed event") end) sound.Stopped:Connect(function(_soundId) print("Sound.Stopped event") end) sound:Play() print("play", sound.Playing, sound.TimePosition) -- play true 0 task.wait(10) sound:Pause() print("pause", sound.Playing, sound.TimePosition) -- pause false 10 task.wait(3) sound:Resume() print("play", sound.Playing, sound.TimePosition) -- play true 10 task.wait(3) sound:Stop() print("stop", sound.Playing, sound.TimePosition) -- stop false 0 task.wait(3) sound:Play() print("play", sound.Playing, sound.TimePosition) -- play true 0 ``` ### Method: Sound:Resume **Signature:** `Sound:Resume(): ()` This method resumes the [Sound](/docs/reference/engine/classes/Sound.md) and sets [Playing](/docs/reference/engine/classes/Sound.md) to `true`. Does not alter [TimePosition](/docs/reference/engine/classes/Sound.md) and thus can be used to resume playback of a sound paused through [Pause()](/docs/reference/engine/classes/Sound.md). *Security: None · Thread Safety: Unsafe · Capabilities: LegacySound* **Returns:** `()` **Sound Functions** This sample gives a simple demonstration of what each of the Sound functions (Sound.Play, Sound.Stop, Sound.Pause and Sound.Resume) do to Sound.Playing and Sound.TimePosition. ```lua -- create a sound local sound = Instance.new("Sound", game.Workspace) sound.SoundId = "rbxassetid://9120386436" if not sound.IsLoaded then sound.Loaded:Wait() end -- listen for events sound.Played:Connect(function(_soundId) print("Sound.Played event") end) sound.Paused:Connect(function(_soundId) print("Sound.Paused event") end) sound.Resumed:Connect(function(_soundId) print("Sound.Resumed event") end) sound.Stopped:Connect(function(_soundId) print("Sound.Stopped event") end) sound:Play() print("play", sound.Playing, sound.TimePosition) -- play true 0 task.wait(10) sound:Pause() print("pause", sound.Playing, sound.TimePosition) -- pause false 10 task.wait(3) sound:Resume() print("play", sound.Playing, sound.TimePosition) -- play true 10 task.wait(3) sound:Stop() print("stop", sound.Playing, sound.TimePosition) -- stop false 0 task.wait(3) sound:Play() print("play", sound.Playing, sound.TimePosition) -- play true 0 ``` ### Method: Sound:Stop **Signature:** `Sound:Stop(): ()` This method stops the [Sound](/docs/reference/engine/classes/Sound.md) and sets [Playing](/docs/reference/engine/classes/Sound.md) to `false`, then sets [TimePosition](/docs/reference/engine/classes/Sound.md) to `0`. *Security: None · Thread Safety: Unsafe · Capabilities: LegacySound* **Returns:** `()` **Sound Functions** This sample gives a simple demonstration of what each of the Sound functions (Sound.Play, Sound.Stop, Sound.Pause and Sound.Resume) do to Sound.Playing and Sound.TimePosition. ```lua -- create a sound local sound = Instance.new("Sound", game.Workspace) sound.SoundId = "rbxassetid://9120386436" if not sound.IsLoaded then sound.Loaded:Wait() end -- listen for events sound.Played:Connect(function(_soundId) print("Sound.Played event") end) sound.Paused:Connect(function(_soundId) print("Sound.Paused event") end) sound.Resumed:Connect(function(_soundId) print("Sound.Resumed event") end) sound.Stopped:Connect(function(_soundId) print("Sound.Stopped event") end) sound:Play() print("play", sound.Playing, sound.TimePosition) -- play true 0 task.wait(10) sound:Pause() print("pause", sound.Playing, sound.TimePosition) -- pause false 10 task.wait(3) sound:Resume() print("play", sound.Playing, sound.TimePosition) -- play true 10 task.wait(3) sound:Stop() print("stop", sound.Playing, sound.TimePosition) -- stop false 0 task.wait(3) sound:Play() print("play", sound.Playing, sound.TimePosition) -- play true 0 ``` ### Method: Sound:pause **Signature:** `Sound:pause(): ()` > **Deprecated:** This deprecated function is a variant of [Sound:Pause()](/docs/reference/engine/classes/Sound.md) which should be used instead. *Security: None · Thread Safety: Unsafe · Capabilities: LegacySound* **Returns:** `()` ### Method: Sound:play **Signature:** `Sound:play(): ()` > **Deprecated:** This deprecated function is a variant of [Sound:Play()](/docs/reference/engine/classes/Sound.md) which should be used instead. *Security: None · Thread Safety: Unsafe · Capabilities: LegacySound* **Returns:** `()` ### Method: Sound:stop **Signature:** `Sound:stop(): ()` > **Deprecated:** This deprecated function is a variant of [Sound:Stop()](/docs/reference/engine/classes/Sound.md) which should be used instead. *Security: None · Thread Safety: Unsafe · Capabilities: LegacySound* **Returns:** `()` ## Events ### Event: Sound.DidLoop **Signature:** `Sound.DidLoop(soundId: string, numOfTimesLooped: int)` Fires whenever the [Sound](/docs/reference/engine/classes/Sound.md) loops. Returns `soundId` and `numOfTimesLooped`, giving the content ID of the sound and the number of times looped respectively. When the [Sound](/docs/reference/engine/classes/Sound.md) is stopped through [Stop()](/docs/reference/engine/classes/Sound.md), the looped counter resets meaning the next [DidLoop](/docs/reference/engine/classes/Sound.md) event will return `1` for `numOfTimesLooped`. *Security: None · Capabilities: LegacySound* **Parameters:** | Name | Type | Description | |------|------|-------------| | `soundId` | `string` | The [SoundId](/docs/reference/engine/classes/Sound.md) of the [Sound](/docs/reference/engine/classes/Sound.md) that looped. | | `numOfTimesLooped` | `int` | The number of times the [Sound](/docs/reference/engine/classes/Sound.md) has looped. | **Loop a Number of Times** This code sample includes a function that will play a sound and allow it to loop for a given number of times before stopping it. ```lua local function loopNTimes(sound, numberOfLoops) if not sound.IsPlaying then sound.Looped = true local connection = nil connection = sound.DidLoop:Connect(function(_soundId, numOfTimesLooped) print(numOfTimesLooped) if numOfTimesLooped >= numberOfLoops then -- disconnect the connection connection:Disconnect() -- stop the sound sound:Stop() end end) sound:Play() end end local sound = Instance.new("Sound") sound.SoundId = "rbxassetid://0" loopNTimes(sound, 5) ``` ### Event: Sound.Ended **Signature:** `Sound.Ended(soundId: string)` Fires when the [Sound](/docs/reference/engine/classes/Sound.md) has completed playback and stopped. This event is often used to destroy a sound when it has completed playback: ``` sound:Play() sound.Ended:Wait() sound:Destroy() ``` Note that this event will **not** fire for sounds with [Looped](/docs/reference/engine/classes/Sound.md) set to `true`, as they continue playing upon reaching their end. This event will also **not** fire when the sound is stopped before playback has completed; for this use the [Stopped](/docs/reference/engine/classes/Sound.md) event. *Security: None · Capabilities: LegacySound* **Parameters:** | Name | Type | Description | |------|------|-------------| | `soundId` | `string` | The [SoundId](/docs/reference/engine/classes/Sound.md) of the [Sound](/docs/reference/engine/classes/Sound.md) that has ended. | ### Event: Sound.Loaded **Signature:** `Sound.Loaded(soundId: string)` Fires when the [Sound](/docs/reference/engine/classes/Sound.md) is loaded. As this event only fires at the time the sound is loaded, it's recommended to check the sound's [IsLoaded](/docs/reference/engine/classes/Sound.md) property prior to connecting to this event. *Security: None · Capabilities: LegacySound* **Parameters:** | Name | Type | Description | |------|------|-------------| | `soundId` | `string` | The [SoundId](/docs/reference/engine/classes/Sound.md) of the [Sound](/docs/reference/engine/classes/Sound.md) that loaded. | **Load Sound** This simple function will verify a Sound has loaded by checking the Sound.IsLoaded property. If Sound.IsLoaded is false it will wait for the Loaded event before resuming. It is important to check Sound.IsLoaded before connecting to the Sound.Loaded event, as if the sound has already loaded the Sound.Loaded event will not fire and the function will yield indefinitely. ```lua local sound = script.Parent.Sound if not sound.IsLoaded then sound.Loaded:Wait() end print("The sound has loaded!") ``` ### Event: Sound.Paused **Signature:** `Sound.Paused(soundId: string)` Fires whenever the [Sound](/docs/reference/engine/classes/Sound.md) is paused using [Pause()](/docs/reference/engine/classes/Sound.md). *Security: None · Capabilities: LegacySound* **Parameters:** | Name | Type | Description | |------|------|-------------| | `soundId` | `string` | The [SoundId](/docs/reference/engine/classes/Sound.md) of the [Sound](/docs/reference/engine/classes/Sound.md) that was paused. | **Sound Functions** This sample gives a simple demonstration of what each of the Sound functions (Sound.Play, Sound.Stop, Sound.Pause and Sound.Resume) do to Sound.Playing and Sound.TimePosition. ```lua -- create a sound local sound = Instance.new("Sound", game.Workspace) sound.SoundId = "rbxassetid://9120386436" if not sound.IsLoaded then sound.Loaded:Wait() end -- listen for events sound.Played:Connect(function(_soundId) print("Sound.Played event") end) sound.Paused:Connect(function(_soundId) print("Sound.Paused event") end) sound.Resumed:Connect(function(_soundId) print("Sound.Resumed event") end) sound.Stopped:Connect(function(_soundId) print("Sound.Stopped event") end) sound:Play() print("play", sound.Playing, sound.TimePosition) -- play true 0 task.wait(10) sound:Pause() print("pause", sound.Playing, sound.TimePosition) -- pause false 10 task.wait(3) sound:Resume() print("play", sound.Playing, sound.TimePosition) -- play true 10 task.wait(3) sound:Stop() print("stop", sound.Playing, sound.TimePosition) -- stop false 0 task.wait(3) sound:Play() print("play", sound.Playing, sound.TimePosition) -- play true 0 ``` ### Event: Sound.Played **Signature:** `Sound.Played(soundId: string)` Fires whenever the [Sound](/docs/reference/engine/classes/Sound.md) is played using [Play()](/docs/reference/engine/classes/Sound.md). This event will **not** fire if the [Sound](/docs/reference/engine/classes/Sound.md) is played due to [PlayOnRemove](/docs/reference/engine/classes/Sound.md) being set to `true` and the sound being destroyed. *Security: None · Capabilities: LegacySound* **Parameters:** | Name | Type | Description | |------|------|-------------| | `soundId` | `string` | The [SoundId](/docs/reference/engine/classes/Sound.md) of the [Sound](/docs/reference/engine/classes/Sound.md) that was played. | **Sound Functions** This sample gives a simple demonstration of what each of the Sound functions (Sound.Play, Sound.Stop, Sound.Pause and Sound.Resume) do to Sound.Playing and Sound.TimePosition. ```lua -- create a sound local sound = Instance.new("Sound", game.Workspace) sound.SoundId = "rbxassetid://9120386436" if not sound.IsLoaded then sound.Loaded:Wait() end -- listen for events sound.Played:Connect(function(_soundId) print("Sound.Played event") end) sound.Paused:Connect(function(_soundId) print("Sound.Paused event") end) sound.Resumed:Connect(function(_soundId) print("Sound.Resumed event") end) sound.Stopped:Connect(function(_soundId) print("Sound.Stopped event") end) sound:Play() print("play", sound.Playing, sound.TimePosition) -- play true 0 task.wait(10) sound:Pause() print("pause", sound.Playing, sound.TimePosition) -- pause false 10 task.wait(3) sound:Resume() print("play", sound.Playing, sound.TimePosition) -- play true 10 task.wait(3) sound:Stop() print("stop", sound.Playing, sound.TimePosition) -- stop false 0 task.wait(3) sound:Play() print("play", sound.Playing, sound.TimePosition) -- play true 0 ``` ### Event: Sound.Resumed **Signature:** `Sound.Resumed(soundId: string)` Fires when the [Sound](/docs/reference/engine/classes/Sound.md) is resumed using [Resume()](/docs/reference/engine/classes/Sound.md). *Security: None · Capabilities: LegacySound* **Parameters:** | Name | Type | Description | |------|------|-------------| | `soundId` | `string` | The [SoundId](/docs/reference/engine/classes/Sound.md) of the [Sound](/docs/reference/engine/classes/Sound.md) being resumed. | **Sound Functions** This sample gives a simple demonstration of what each of the Sound functions (Sound.Play, Sound.Stop, Sound.Pause and Sound.Resume) do to Sound.Playing and Sound.TimePosition. ```lua -- create a sound local sound = Instance.new("Sound", game.Workspace) sound.SoundId = "rbxassetid://9120386436" if not sound.IsLoaded then sound.Loaded:Wait() end -- listen for events sound.Played:Connect(function(_soundId) print("Sound.Played event") end) sound.Paused:Connect(function(_soundId) print("Sound.Paused event") end) sound.Resumed:Connect(function(_soundId) print("Sound.Resumed event") end) sound.Stopped:Connect(function(_soundId) print("Sound.Stopped event") end) sound:Play() print("play", sound.Playing, sound.TimePosition) -- play true 0 task.wait(10) sound:Pause() print("pause", sound.Playing, sound.TimePosition) -- pause false 10 task.wait(3) sound:Resume() print("play", sound.Playing, sound.TimePosition) -- play true 10 task.wait(3) sound:Stop() print("stop", sound.Playing, sound.TimePosition) -- stop false 0 task.wait(3) sound:Play() print("play", sound.Playing, sound.TimePosition) -- play true 0 ``` ### Event: Sound.Stopped **Signature:** `Sound.Stopped(soundId: string)` Fires when the [Sound](/docs/reference/engine/classes/Sound.md) is stopped through using [Stop()](/docs/reference/engine/classes/Sound.md). Destroying a sound while it is playing will not cause this event to fire. *Security: None · Capabilities: LegacySound* **Parameters:** | Name | Type | Description | |------|------|-------------| | `soundId` | `string` | The [SoundId](/docs/reference/engine/classes/Sound.md) of the [Sound](/docs/reference/engine/classes/Sound.md) that stopped. | **Sound Functions** This sample gives a simple demonstration of what each of the Sound functions (Sound.Play, Sound.Stop, Sound.Pause and Sound.Resume) do to Sound.Playing and Sound.TimePosition. ```lua -- create a sound local sound = Instance.new("Sound", game.Workspace) sound.SoundId = "rbxassetid://9120386436" if not sound.IsLoaded then sound.Loaded:Wait() end -- listen for events sound.Played:Connect(function(_soundId) print("Sound.Played event") end) sound.Paused:Connect(function(_soundId) print("Sound.Paused event") end) sound.Resumed:Connect(function(_soundId) print("Sound.Resumed event") end) sound.Stopped:Connect(function(_soundId) print("Sound.Stopped event") end) sound:Play() print("play", sound.Playing, sound.TimePosition) -- play true 0 task.wait(10) sound:Pause() print("pause", sound.Playing, sound.TimePosition) -- pause false 10 task.wait(3) sound:Resume() print("play", sound.Playing, sound.TimePosition) -- play true 10 task.wait(3) sound:Stop() print("stop", sound.Playing, sound.TimePosition) -- stop false 0 task.wait(3) sound:Play() print("play", sound.Playing, sound.TimePosition) -- play true 0 ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: SoundEffect last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable summary: "SoundEffect is the base class that all other sound effects derive from. A SoundEffect can be applied to either a Sound or SoundGroup by being parented to either." --- # Class: SoundEffect > SoundEffect is the base class that all other sound effects derive from. A > SoundEffect can be applied to either a [Sound](/docs/reference/engine/classes/Sound.md) or [SoundGroup](/docs/reference/engine/classes/SoundGroup.md) by > being parented to either. ## Description SoundEffect is the base class that all other sound effects derive from. A SoundEffect can be applied to either a [Sound](/docs/reference/engine/classes/Sound.md) or [SoundGroup](/docs/reference/engine/classes/SoundGroup.md) by being parented to either. Multiple effects can be applied to the same Sound or SoundGroup. The order the effects will be applied in is determined by that effect's Priority. ## Properties ### Property: SoundEffect.Enabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "LegacySound" ] } ``` Toggles the effect on and off. True by default. ### Property: SoundEffect.Priority ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "LegacySound" ] } ``` Determines the order the effect will be applied in relation to other effects. Higher priority effects will be applied earlier. The exception is when Priority equals 0 (which is the default). In this case, the base priority for the effect will be used. If the priority of two effects are equal, then the order is undetermined. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: SoundGroup last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Internal summary: "A SoundGroup is used to manage the volume and sound effects on multiple Sounds at once. Sounds in the SoundGroup will have their volume and effects adjusted by the SoundGroup." --- # Class: SoundGroup > A [SoundGroup](/docs/reference/engine/classes/SoundGroup.md) is used to manage the volume and sound effects on > multiple [Sounds](/docs/reference/engine/classes/Sound.md) at once. [Sounds](/docs/reference/engine/classes/Sound.md) in the SoundGroup > will have their volume and effects adjusted by the SoundGroup. ## Description A [SoundGroup](/docs/reference/engine/classes/SoundGroup.md) is used to manage the volume and effects on multiple [Sounds](/docs/reference/engine/classes/Sound.md) at once. Every sound in the sound group will have its volume adjusted by the group's [Volume](/docs/reference/engine/classes/SoundGroup.md) property which acts as a multiplier, meaning a [Sound](/docs/reference/engine/classes/Sound.md) with volume `0.5` assigned to a [SoundGroup](/docs/reference/engine/classes/SoundGroup.md) with a volume of `0.5` will have an effective volume of `0.25`. If the [SoundGroup](/docs/reference/engine/classes/SoundGroup.md) has any [SoundEffects](/docs/reference/engine/classes/SoundEffect.md) as children, those effects will be applied to all of the [Sounds](/docs/reference/engine/classes/Sound.md) in the group. Note that a [Sound](/docs/reference/engine/classes/Sound.md) must be added to a [SoundGroup](/docs/reference/engine/classes/SoundGroup.md) by setting its [SoundGroup](/docs/reference/engine/classes/Sound.md) property, not by simply parenting the [Sound](/docs/reference/engine/classes/Sound.md) to the [SoundGroup](/docs/reference/engine/classes/SoundGroup.md). A [Sound](/docs/reference/engine/classes/Sound.md) can only belong to one [SoundGroup](/docs/reference/engine/classes/SoundGroup.md) at a time, although you can nest groups as outlined [here](/docs/en-us/sound/groups.md#nesting-soundgroups). See [Sound Groups](/docs/en-us/sound/groups.md) for further details on working with the [SoundGroup](/docs/reference/engine/classes/SoundGroup.md) class. ## Properties ### Property: SoundGroup.Volume ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "LegacySound" ] } ``` The volume multiplier applied to [Sounds](/docs/reference/engine/classes/Sound.md) which belong to the [SoundGroup](/docs/reference/engine/classes/SoundGroup.md). Value can range from `0` to `10`. **SoundGroups** This sample demonstrates how a SoundGroup can be used to change the volume of its associated Sounds and apply SoundEffects. In this example a Sound is instanced in the Workspace and assigned to a new SoundGroup. The Sound is played and during playback the volume is changed via the SoundGroup and a SoundEffect is added. ```lua local SoundService = game:GetService("SoundService") -- create a sound group local soundGroup = Instance.new("SoundGroup") soundGroup.Parent = SoundService -- create a sound local sound = Instance.new("Sound") sound.SoundId = "rbxassetid://9120386436" sound.Looped = true sound.PlaybackSpeed = 2 sound.SoundGroup = soundGroup sound.Parent = workspace -- play the sound sound:Play() task.wait(10) -- change the volume soundGroup.Volume = 0.1 task.wait(3) -- return the volume soundGroup.Volume = 0.5 task.wait(4) -- add a sound effect local reverb = Instance.new("ReverbSoundEffect") reverb.Parent = soundGroup ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: SoundService last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service summary: "A service that determines various aspects of how the audio engine works. Most of its properties affect how Sounds play in the experience." --- # Class: SoundService > A service that determines various aspects of how the audio engine works. Most > of its properties affect how [Sounds](/docs/reference/engine/classes/Sound.md) play in the experience. ## Description A service that determines various aspects of how the audio engine works. Most of its properties affect how [Sounds](/docs/reference/engine/classes/Sound.md) play in the experience, while others affect the behavior of instances in the advanced audio system such as [AudioPlayers](/docs/reference/engine/classes/AudioPlayer.md) and [AudioEmitters](/docs/reference/engine/classes/AudioEmitter.md). [SoundService](/docs/reference/engine/classes/SoundService.md) is also often used to store [SoundGroups](/docs/reference/engine/classes/SoundGroup.md), although this is not mandatory for groups to work. ## Code Samples **Dynamic Reverb System** The code in this sample, when ran from a `LocalScript`, will change the [SoundService.AmbientReverb](/docs/reference/engine/classes/SoundService.md) property of `SoundService` when the player is inside a `BasePart` tagged using `CollectionService`. To add or remove tags and reverb types, change the entries in the 'reverbTags' table. ```lua local Players = game:GetService("Players") local CollectionService = game:GetService("CollectionService") local SoundService = game:GetService("SoundService") local localPlayer = Players.LocalPlayer local reverbTags = { ["reverb_Cave"] = Enum.ReverbType.Cave, } -- collect parts and group them by tag local parts = {} for reverbTag, reverbType in pairs(reverbTags) do for _, part in pairs(CollectionService:GetTagged(reverbTag)) do parts[part] = reverbType end end -- function to check if a position is within a part's extents local function positionInPart(part, position) local extents = part.Size / 2 local offset = part.CFrame:PointToObjectSpace(position) return offset.x < extents.x and offset.y < extents.y and offset.z < extents.z end local reverbType = SoundService.AmbientReverb while true do task.wait() if not localPlayer then return end local character = localPlayer.Character -- default to no reverb local newReverbType = Enum.ReverbType.NoReverb if character and character.PrimaryPart then local position = character.PrimaryPart.Position -- go through all the indexed parts for part, type in pairs(parts) do -- see if the character is within them if positionInPart(part, position) then -- if so, pick that reverb type newReverbType = type break end end end -- set the reverb type if it has changed if newReverbType ~= reverbType then SoundService.AmbientReverb = newReverbType reverbType = newReverbType end end ``` ## Properties ### Property: SoundService.AcousticSimulationEnabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Audio" ] } ``` Determines at a global level whether sound from [AudioEmitters](/docs/reference/engine/classes/AudioEmitter.md) and [AudioListeners](/docs/reference/engine/classes/AudioListener.md) should automatically implement features of acoustic simulation, such as occlusion (being muffled through walls), diffraction (bending around corners), and reverberation (echoing off of walls). If set to `false`, these instances will not simulate these features, regardless of their individual [AcousticSimulationEnabled](/docs/reference/engine/classes/AudioEmitter.md) settings. ### Property: SoundService.AmbientReverb ```json { "type": "ReverbType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Audio" ] } ``` A reverb preset that should be applied to all [Sounds](/docs/reference/engine/classes/Sound.md) in the experience. Each [ReverbType](/docs/reference/engine/enums/ReverbType.md) option for this property corresponds to a preset available in the FMOD sound engine. For example, when [AmbientReverb](/docs/reference/engine/classes/SoundService.md) is set to [ReverbType.Hangar](/docs/reference/engine/enums/ReverbType.md), [Sounds](/docs/reference/engine/classes/Sound.md) will have reverb applied to simulate being in a large enclosed space. Note that this only affects [Sounds](/docs/reference/engine/classes/Sound.md) and **not** instances in the advanced audio system such as [AudioPlayers](/docs/reference/engine/classes/AudioPlayer.md) and [AudioEmitters](/docs/reference/engine/classes/AudioEmitter.md). See [AudioReverb](/docs/reference/engine/classes/AudioReverb.md) for a way to apply reverb in that system. ### Property: SoundService.CharacterSoundsUseNewApi ```json { "type": "RolloutState", "access": "ReadOnly", "security": { "read": "None", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Audio" ] } ``` Determines which set of instances core scripts will use to create default character sounds. If set to [RolloutState.Enabled](/docs/reference/engine/enums/RolloutState.md), it will use instances in the advanced audio system such as [AudioPlayers](/docs/reference/engine/classes/AudioPlayer.md) and [AudioEmitters](/docs/reference/engine/classes/AudioEmitter.md). If set to [RolloutState.Disabled](/docs/reference/engine/enums/RolloutState.md), it will use instances in the legacy sound system such as [Sounds](/docs/reference/engine/classes/Sound.md). ### Property: SoundService.DefaultListenerLocation ```json { "type": "ListenerLocation", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Listening", "capabilities": [ "Audio" ] } ``` Determines where to place an [AudioListener](/docs/reference/engine/classes/AudioListener.md) by default. The [AudioListener](/docs/reference/engine/classes/AudioListener.md) will automatically be wired to a [AudioDeviceOutput](/docs/reference/engine/classes/AudioDeviceOutput.md) and will have an empty [AudioListener.InteractionGroup](/docs/reference/engine/classes/AudioListener.md) set. See [ListenerLocation](/docs/reference/engine/enums/ListenerLocation.md) for detailed descriptions of each option. ### Property: SoundService.DistanceFactor ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Audio" ] } ``` The number of studs to be considered a meter by [SoundService](/docs/reference/engine/classes/SoundService.md) when simulating the Doppler effect for [Sounds](/docs/reference/engine/classes/Sound.md). This impacts any [Sound](/docs/reference/engine/classes/Sound.md) parented to a [BasePart](/docs/reference/engine/classes/BasePart.md) or [Attachment](/docs/reference/engine/classes/Attachment.md). By default, this property is `3.33`, meaning that a meter is considered 3.33 studs for the purposes of simulating the Doppler effect. The greater the [DistanceFactor](/docs/reference/engine/classes/SoundService.md), the faster the listener has to travel relative to [Sounds](/docs/reference/engine/classes/Sound.md) in order to experience the same Doppler shift. It's recommended that you only change this property if the objects in your experience are scaled differently from what they represent. For example, if your character is meant to be very small (but is normal-sized in the engine), you may want to increase [SoundService.DistanceFactor](/docs/reference/engine/classes/SoundService.md). Note that this does not impact the behavior of instances in the advanced audio system, such as [AudioPlayer](/docs/reference/engine/classes/AudioPlayer.md) or [AudioEmitter](/docs/reference/engine/classes/AudioEmitter.md). ### Property: SoundService.DopplerScale ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Audio" ] } ``` This property determines the degree to which the pitch of a [Sound](/docs/reference/engine/classes/Sound.md) varies due to the Doppler effect. This impacts any [Sound](/docs/reference/engine/classes/Sound.md) parented to a [BasePart](/docs/reference/engine/classes/BasePart.md) or [Attachment](/docs/reference/engine/classes/Attachment.md). The Doppler effect is a phenomenon whereby the pitch of a sound changes as the source and observer of the sound move further away or closer together, which is stronger the more quickly they are moving. Increasing [SoundService.DopplerScale](/docs/reference/engine/classes/SoundService.md) exaggerates the impact of this effect, whereas decreasing it minimizes it. By default, the value of this property is `1`. Note that this does not impact the behavior of instances in the advanced audio system, such as [AudioPlayer](/docs/reference/engine/classes/AudioPlayer.md) or [AudioEmitter](/docs/reference/engine/classes/AudioEmitter.md). ### Property: SoundService.ListenerCFrame ```json { "type": "CFrame", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Listening", "capabilities": [ "Audio" ] } ``` This property is only functional when [ListenerType](/docs/reference/engine/classes/SoundService.md) is set to [ListenerType.CFrame](/docs/reference/engine/enums/ListenerType.md). In that scenario, this property determines the world space position from which 3D [Sounds](/docs/reference/engine/classes/Sound.md) are heard by the player. ### Property: SoundService.ListenerObject ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Listening", "capabilities": [ "Audio" ] } ``` This property is only functional when [ListenerType](/docs/reference/engine/classes/SoundService.md) is set to [ListenerType.ObjectPosition](/docs/reference/engine/enums/ListenerType.md) or [ListenerType.ObjectCFrame](/docs/reference/engine/enums/ListenerType.md). In that scenario, this property determines which [Instance](/docs/reference/engine/classes/Instance.md) will be used to determine the position from which 3D [Sounds](/docs/reference/engine/classes/Sound.md) are heard by the player. This property can only be set to [Instances](/docs/reference/engine/classes/Instance.md) with a position, such as a [BasePart](/docs/reference/engine/classes/BasePart.md) or an [Attachment](/docs/reference/engine/classes/Attachment.md). If it is set to `nil`, 3D [Sounds](/docs/reference/engine/classes/Sound.md) will not be heard by the player. ### Property: SoundService.ListenerType ```json { "type": "ListenerType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Listening", "capabilities": [ "Audio" ] } ``` The listener type for any 3D [Sounds](/docs/reference/engine/classes/Sound.md) in the experience, effectively the point from which 3D [Sound](/docs/reference/engine/classes/Sound.md) audio in the experience is heard by the player. For [Sounds](/docs/reference/engine/classes/Sound.md) parented to a [BasePart](/docs/reference/engine/classes/BasePart.md) or [Attachment](/docs/reference/engine/classes/Attachment.md), the listener influences the volume and left/right balance of a playing sound. By default, this listener is set to [Workspace.CurrentCamera](/docs/reference/engine/classes/Workspace.md). Note that this does not affect the listener location when using the advanced audio system; see [AudioListener](/docs/reference/engine/classes/AudioListener.md) for a way to set the listener location within that system. This property may not yet be enabled for all creators; until full rollout, use [SoundService:GetListener()](/docs/reference/engine/classes/SoundService.md) and [SoundService:SetListener()](/docs/reference/engine/classes/SoundService.md) instead. ### Property: SoundService.RespectFilteringEnabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Audio" ] } ``` This property determines whether [Sound](/docs/reference/engine/classes/Sound.md) playback is replicated from the client to the server, and therefore from the server. In other words, when a [LocalScript](/docs/reference/engine/classes/LocalScript.md) calls [Play()](/docs/reference/engine/classes/Sound.md) and this property is `true`, the sound will only play on the respective client. If this property is `false`, other clients will also hear the sound. Default is `true`, meaning filtering is enabled. ### Property: SoundService.RolloffScale ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Audio" ] } ``` Determines how fast the volume of a spatialized [Sound](/docs/reference/engine/classes/Sound.md) attenuates. This impacts any [Sound](/docs/reference/engine/classes/Sound.md) parented to a [BasePart](/docs/reference/engine/classes/BasePart.md) or [Attachment](/docs/reference/engine/classes/Attachment.md). A higher [RolloffScale](/docs/reference/engine/classes/SoundService.md) means the volume of a [Sound](/docs/reference/engine/classes/Sound.md) will attenuate more rapidly as the distance between the listener and the [Sound](/docs/reference/engine/classes/Sound.md) grows. More precisely, the volume of the [Sound](/docs/reference/engine/classes/Sound.md) will still start attenuating at a distance equal to [Sound.RollOffMinDistance](/docs/reference/engine/classes/Sound.md), but the attenuation curve will be steeper or more gradual based on the value of [RolloffScale](/docs/reference/engine/classes/SoundService.md). Note that the [Sound](/docs/reference/engine/classes/Sound.md) will still be inaudible past its the [Sound.RollOffMaxDistance](/docs/reference/engine/classes/Sound.md) regardless of the value of [SoundService.RolloffScale](/docs/reference/engine/classes/SoundService.md). Note that this property does not affect the behavior of instances in the advanced audio system, such as [AudioEmitter](/docs/reference/engine/classes/AudioEmitter.md). See [AudioEmitter:SetDistanceAttenuation](/docs/reference/engine/classes/AudioEmitter.md) for a way to apply custom attenuation in that system. ### Property: SoundService.VolumetricAudio ```json { "type": "VolumetricAudio", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Audio" ] } ``` Determines whether any [Sounds](/docs/reference/engine/classes/Sound.md) parented to a [Part](/docs/reference/engine/classes/Part.md) emit volumetrically. If set to [VolumetricAudio.Enabled](/docs/reference/engine/enums/VolumetricAudio.md), the [Sound](/docs/reference/engine/classes/Sound.md) will simulate being emitted from every point in the interior of the [Part](/docs/reference/engine/classes/Part.md). If set to [VolumetricAudio.Disabled](/docs/reference/engine/enums/VolumetricAudio.md), the [Sound](/docs/reference/engine/classes/Sound.md) will only emit from a single point in the center of the [Part](/docs/reference/engine/classes/Part.md). Note that this does not impact [Sounds](/docs/reference/engine/classes/Sound.md) parented to other objects, such as [Attachments](/docs/reference/engine/classes/Attachment.md) or [MeshParts](/docs/reference/engine/classes/MeshPart.md). This also does not impact the behavior of instances in the advanced audio system such as [AudioEmitter](/docs/reference/engine/classes/AudioEmitter.md). ## Methods ### Method: SoundService:GetListener **Signature:** `SoundService:GetListener(): Tuple` Returns the current listener type used by [Sounds](/docs/reference/engine/classes/Sound.md) and what object or position that listener is currently set to. This is the point from which [Sound](/docs/reference/engine/classes/Sound.md) audio in the experience is heard by the player. By default, the listener is set to [Workspace.CurrentCamera](/docs/reference/engine/classes/Workspace.md). The listener can be changed using [SetListener()](/docs/reference/engine/classes/SoundService.md). Note that this does not affect the listener location when using the advanced audio system. See [AudioListener](/docs/reference/engine/classes/AudioListener.md) for a way to set the listener location in that system. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `Tuple` — A table containing two results. The first result is the listener's [ListenerType](/docs/reference/engine/enums/ListenerType.md) and the second result is dependent on that type: | Listener Type | Description | | --- | --- | | [ListenerType.Camera](/docs/reference/engine/enums/ListenerType.md) | Does not return a listener object as [CurrentCamera](/docs/reference/engine/classes/Workspace.md) is always used. | | [ListenerType.CFrame](/docs/reference/engine/enums/ListenerType.md) | Returns the [CFrame](/docs/reference/engine/datatypes/CFrame.md) used in [SetListener()](/docs/reference/engine/classes/SoundService.md). | | [ListenerType.ObjectPosition](/docs/reference/engine/enums/ListenerType.md) | Returns the [BasePart](/docs/reference/engine/classes/BasePart.md) used in [SetListener()](/docs/reference/engine/classes/SoundService.md). | | [ListenerType.ObjectCFrame](/docs/reference/engine/enums/ListenerType.md) | Returns the [BasePart](/docs/reference/engine/classes/BasePart.md) used in [SetListener()](/docs/reference/engine/classes/SoundService.md). | ### Method: SoundService:GetMixerTime **Signature:** `SoundService:GetMixerTime(): double` Returns the number of seconds since the audio engine began mixing. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Returns:** `double` — The number of seconds since the audio engine began mixing. This value is stable, sample-accurate, and monotonically-increasing – intended to be used for scheduling audible changes at precise times. ### Method: SoundService:OpenAttenuationCurveEditor **Signature:** `SoundService:OpenAttenuationCurveEditor(selectedCurveObjects: Instances): ()` Opens the attenuation curve editor in Studio for the provided [AudioEmitter](/docs/reference/engine/classes/AudioEmitter.md) or [AudioListener](/docs/reference/engine/classes/AudioListener.md) instances. *Security: PluginSecurity · Thread Safety: Unsafe · Capabilities: Audio* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `selectedCurveObjects` | `Instances` | | A list of [AudioEmitters](/docs/reference/engine/classes/AudioEmitter.md) or [AudioListeners](/docs/reference/engine/classes/AudioListener.md). | **Returns:** `()` ### Method: SoundService:OpenDirectionalCurveEditor **Signature:** `SoundService:OpenDirectionalCurveEditor(selectedCurveObjects: Instances): ()` Opens the directional curve editor in Studio for the provided [AudioEmitter](/docs/reference/engine/classes/AudioEmitter.md) or [AudioListener](/docs/reference/engine/classes/AudioListener.md) instances. *Security: PluginSecurity · Thread Safety: Unsafe · Capabilities: Audio* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `selectedCurveObjects` | `Instances` | | A list of [AudioEmitters](/docs/reference/engine/classes/AudioEmitter.md) or [AudioListeners](/docs/reference/engine/classes/AudioListener.md). | **Returns:** `()` ### Method: SoundService:PlayLocalSound **Signature:** `SoundService:PlayLocalSound(sound: Instance): ()` Plays a copy of a [Sound](/docs/reference/engine/classes/Sound.md) locally. The [Sound](/docs/reference/engine/classes/Sound.md) will only be heard by the client calling this method, regardless of where it's parented to. Some properties of the [Sound](/docs/reference/engine/classes/Sound.md) will be carried over into the copy. These include its [Sound.Volume](/docs/reference/engine/classes/Sound.md), [Sound.TimePosition](/docs/reference/engine/classes/Sound.md), [Sound.PlaybackSpeed](/docs/reference/engine/classes/Sound.md), and any spatialization and effects that are applied to it, including through [SoundGroups](/docs/reference/engine/classes/Sound.md). Properties that do not affect the copy include [Sound.Looped](/docs/reference/engine/classes/Sound.md) and [SoundService.AmbientReverb](/docs/reference/engine/classes/SoundService.md). *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `sound` | `Instance` | | The [Sound](/docs/reference/engine/classes/Sound.md) to be played. | **Returns:** `()` ### Method: SoundService:SetListener **Signature:** `SoundService:SetListener(listenerType: ListenerType, listener: Tuple): ()` Sets the listener type for any [Sounds](/docs/reference/engine/classes/Sound.md) in the experience, which defines the point from which [Sound](/docs/reference/engine/classes/Sound.md) audio in the experience is heard by the player. For [Sounds](/docs/reference/engine/classes/Sound.md) parented to a [BasePart](/docs/reference/engine/classes/BasePart.md) or [Attachment](/docs/reference/engine/classes/Attachment.md), the listener influences the volume and left/right balance of a playing sound. By default, this listener is set to [Workspace.CurrentCamera](/docs/reference/engine/classes/Workspace.md). Note that this does not affect the listener location when using the advanced audio system. See [AudioListener](/docs/reference/engine/classes/AudioListener.md) for a way to set the listener location in that system. *Security: None · Thread Safety: Unsafe · Capabilities: Audio* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `listenerType` | `ListenerType` | | The [ListenerType](/docs/reference/engine/enums/ListenerType.md) of the listener. | | `listener` | `Tuple` | | Dependent on the [ListenerType](/docs/reference/engine/enums/ListenerType.md). Use a [BasePart](/docs/reference/engine/classes/BasePart.md) for [ListenerType.ObjectPosition](/docs/reference/engine/enums/ListenerType.md) or [ListenerType.ObjectCFrame](/docs/reference/engine/enums/ListenerType.md), a [CFrame](/docs/reference/engine/datatypes/CFrame.md) for [ListenerType.CFrame](/docs/reference/engine/enums/ListenerType.md), or `nil` for [ListenerType.Camera](/docs/reference/engine/enums/ListenerType.md). | **Returns:** `()` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Sparkles last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances summary: "A particle emitter with the visual aesthetic of sparkles." --- # Class: Sparkles > A particle emitter with the visual aesthetic of sparkles. ## Description Sparkles is one of several particle-emitting classes. Like other particle emitters of its kind, Sparkles objects emit particles when parented to a [BasePart](/docs/reference/engine/classes/BasePart.md) (such as a [Part](/docs/reference/engine/classes/Part.md)) or an [Attachment](/docs/reference/engine/classes/Attachment.md) within such a [BasePart](/docs/reference/engine/classes/BasePart.md). Compared to the [ParticleEmitter](/docs/reference/engine/classes/ParticleEmitter.md) class, Sparkles lacks many different customization properties and special methods, such as [ParticleEmitter.Lifetime](/docs/reference/engine/classes/ParticleEmitter.md) or [ParticleEmitter:Emit()](/docs/reference/engine/classes/ParticleEmitter.md). It is useful to create a quick special effect in a pinch; for more detailed work it is preferable to use a [ParticleEmitter](/docs/reference/engine/classes/ParticleEmitter.md) instead. When [Sparkles.Enabled](/docs/reference/engine/classes/Sparkles.md) is toggled off, particles emit by this object will continue to render until their lifetime expires. When a Sparkles object's [Instance.Parent](/docs/reference/engine/classes/Instance.md) is set to `nil` (and/or [Instance:Destroy()](/docs/reference/engine/classes/Instance.md)ed), all particles will instantly disappear. If this effect is not desired, try hiding the parent object at a far away position, then removing the Sparkles after a few seconds using [Debris](/docs/reference/engine/classes/Debris.md) to give the last particles a chance to expire. This object does not have a [ParticleEmitter:Clear()](/docs/reference/engine/classes/ParticleEmitter.md) method, but it is possible to set the [Instance.Parent](/docs/reference/engine/classes/Instance.md) to `nil` and back to the exact same object for the same effect. Sparkles particles are only emitted from the center of [BasePart](/docs/reference/engine/classes/BasePart.md) to which they are parented. Parenting a Sparkles object to an [Attachment](/docs/reference/engine/classes/Attachment.md) instead allows customization of the particles' start position. ## Code Samples **Give Sparkles** The code sample below gives any new players sparkles that are colored the same as their torso color. ```lua local Players = game:GetService("Players") local function onCharacterSpawned(character) local hrp = character:WaitForChild("HumanoidRootPart") -- Add sparkles that are colored to the player's torso color local sparkles = Instance.new("Sparkles") sparkles.Parent = hrp sparkles.SparkleColor = character:WaitForChild("Body Colors").TorsoColor.Color sparkles.Enabled = true end local function onPlayerAdded(player) player.CharacterAdded:Connect(onCharacterSpawned) end Players.PlayerAdded:Connect(onPlayerAdded) ``` **Expected output:** When your character spawns while this script is running, they should have a very sparkly torso, customized for their character. ## Properties ### Property: Sparkles.Enabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` The Enabled property, much like [ParticleEmitter.Enabled](/docs/reference/engine/classes/ParticleEmitter.md), determines whether sparkle particles are emit. Any particles already emit will continue to render until their lifetime expires. This property is useful for keeping pre-made sparkle effects off until they are needed later. Since sparkle particles are destroyed when the `Sparkle` object's [Instance.Parent](/docs/reference/engine/classes/Instance.md) is set to `nil`, this property is useful in allowing existing particles the opportunity to expire before destroying the Fire object altogether. See the function below. ``` local Debris = game:GetService("Debris") local part = script.Parent function stopSparkling(sparkles) sparkles.Enabled = false -- No more new particles Debris:AddItem(sparkles, 4) -- Remove the object after a delay (after existing particles have expired) end stopSparkling(part.Sparkles) ``` **Give Sparkles** The code sample below gives any new players sparkles that are colored the same as their torso color. ```lua local Players = game:GetService("Players") local function onCharacterSpawned(character) local hrp = character:WaitForChild("HumanoidRootPart") -- Add sparkles that are colored to the player's torso color local sparkles = Instance.new("Sparkles") sparkles.Parent = hrp sparkles.SparkleColor = character:WaitForChild("Body Colors").TorsoColor.Color sparkles.Enabled = true end local function onPlayerAdded(player) player.CharacterAdded:Connect(onCharacterSpawned) end Players.PlayerAdded:Connect(onPlayerAdded) ``` **Expected output:** When your character spawns while this script is running, they should have a very sparkly torso, customized for their character. ### Property: Sparkles.SparkleColor ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` This property functions identically to [Sparkles.Color](/docs/reference/engine/classes/Sparkles.md). The SparkleColor property determines the color of all the particles emit by a [Sparkles](/docs/reference/engine/classes/Sparkles.md) object (both existing and future particles). It behaves similarly to [ParticleEmitter.Color](/docs/reference/engine/classes/ParticleEmitter.md), except that it is only one color and not a [ColorSequence](/docs/reference/engine/datatypes/ColorSequence.md). Sparkles have a natural color sequence applied which is most apparent when this property is set to white; sparkles very faintly animate between a subtle green and red. It should be noted that sparkles have a partial [ParticleEmitter.LightEmission](/docs/reference/engine/classes/ParticleEmitter.md) effect, so dark colors tend to render more transparent and white colors look very bright. **Give Sparkles** The code sample below gives any new players sparkles that are colored the same as their torso color. ```lua local Players = game:GetService("Players") local function onCharacterSpawned(character) local hrp = character:WaitForChild("HumanoidRootPart") -- Add sparkles that are colored to the player's torso color local sparkles = Instance.new("Sparkles") sparkles.Parent = hrp sparkles.SparkleColor = character:WaitForChild("Body Colors").TorsoColor.Color sparkles.Enabled = true end local function onPlayerAdded(player) player.CharacterAdded:Connect(onCharacterSpawned) end Players.PlayerAdded:Connect(onPlayerAdded) ``` **Expected output:** When your character spawns while this script is running, they should have a very sparkly torso, customized for their character. ### Property: Sparkles.TimeScale ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` ### Property: Sparkles.Color *(hidden)* ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` The Color property determines the color of all the particles emit by a [Sparkles](/docs/reference/engine/classes/Sparkles.md) object (both existing and future particles). It behaves similarly to [ParticleEmitter.Color](/docs/reference/engine/classes/ParticleEmitter.md), except that it is only one color and not a [ColorSequence](/docs/reference/engine/datatypes/ColorSequence.md). Sparkles have a natural color sequence applied which is most apparent when this property is set to white; sparkles very faintly animate between a subtle green and red. It should be noted that sparkles have a partial [ParticleEmitter.LightEmission](/docs/reference/engine/classes/ParticleEmitter.md) effect, so dark colors tend to render more transparent and white colors look very bright. **Give Sparkles** The code sample below gives any new players sparkles that are colored the same as their torso color. ```lua local Players = game:GetService("Players") local function onCharacterSpawned(character) local hrp = character:WaitForChild("HumanoidRootPart") -- Add sparkles that are colored to the player's torso color local sparkles = Instance.new("Sparkles") sparkles.Parent = hrp sparkles.SparkleColor = character:WaitForChild("Body Colors").TorsoColor.Color sparkles.Enabled = true end local function onPlayerAdded(player) player.CharacterAdded:Connect(onCharacterSpawned) end Players.PlayerAdded:Connect(onPlayerAdded) ``` **Expected output:** When your character spawns while this script is running, they should have a very sparkly torso, customized for their character. ### Property: Sparkles.LocalTransparencyModifier *(hidden)* ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: SpawnLocation last_updated: 2026-06-29T19:34:08Z inherits: - Part - FormFactorPart - BasePart - PVInstance - Instance - Object type: class memory_category: BaseParts summary: "SpawnLocations, or \"spawns\" determine where a Player respawns when they die. They can be configured to allow only certain players to use each spawn, using Teams. They also control how ForceFields are set up for newly-spawned players." --- # Class: SpawnLocation > [SpawnLocations](/docs/reference/engine/classes/SpawnLocation.md), or "spawns" determine where a > [Player](/docs/reference/engine/classes/Player.md) respawns when they die. They can be configured to allow only > certain players to use each spawn, using [Teams](/docs/reference/engine/classes/Team.md). They also control > how [ForceFields](/docs/reference/engine/classes/ForceField.md) are set up for newly-spawned players. ## Description SpawnLocations, or "spawns" determine where a [Player](/docs/reference/engine/classes/Player.md) respawns when they die. They can be configured to allow only certain players to use each spawn, using [Teams](/docs/reference/engine/classes/Team.md). They also control how [ForceFields](/docs/reference/engine/classes/ForceField.md) are set up for newly-spawned players. SpawnLocations can be used as checkpoints, such as in an obstacle course, using the [SpawnLocation.AllowTeamChangeOnTouch](/docs/reference/engine/classes/SpawnLocation.md) property, so that when a player touches it, they will change teams to the SpawnLocation's team. In this case, only the first [Team](/docs/reference/engine/classes/Team.md) should have [Team.AutoAssignable](/docs/reference/engine/classes/Team.md) set to true, else players will not start at the first checkpoint. Note if a SpawnLocation is added to the [Workspace](/docs/reference/engine/classes/Workspace.md) in Studio with [SpawnLocation.Neutral](/docs/reference/engine/classes/SpawnLocation.md) set to false a Team will be created corresponding to [SpawnLocation.TeamColor](/docs/reference/engine/classes/SpawnLocation.md) if it does not already exist. This behavior does not occur when spawns are created in-game using a [Script](/docs/reference/engine/classes/Script.md) or if the properties of the SpawnLocation are changed after already being added. It is recommended that developers always set up their teams manually and not rely on this behavior. ## Spawning Rules There are several rules that come into play for a given SpawnLocation when a player respawns: - When [SpawnLocation.Neutral](/docs/reference/engine/classes/SpawnLocation.md) is set to false only [Players](/docs/reference/engine/classes/Player.md) with [Player.TeamColor](/docs/reference/engine/classes/Player.md) matching [SpawnLocation.TeamColor](/docs/reference/engine/classes/SpawnLocation.md) will respawn above it - When [SpawnLocation.Neutral](/docs/reference/engine/classes/SpawnLocation.md) is set to true any Player can spawn above it regardless of [SpawnLocation.TeamColor](/docs/reference/engine/classes/SpawnLocation.md) - If multiple eligible spawns are available to a [Player](/docs/reference/engine/classes/Player.md), a random one will be chosen - Players will spawn at different points on top of a SpawnLocation, but currently, they may still spawn on top of each other if they spawn right after one and other See also: - If you'd like to configure how long it takes for a player to respawn, take a look at the [RespawnTime](/docs/reference/engine/classes/Players.md) property ## Code Samples **SpawnLocation Checkpoints** This sample demonstrates how SpawnLocations can be used to make a checkpoint system. Typically this would be done Studio and not in Lua, but this example serves as a comprehensive example of what Team and SpawnLocation properties need to be used to achieve this setup. ```lua local Teams = game:GetService("Teams") -- create start team (AutoAssignable = true) local startTeam = Instance.new("Team") startTeam.Name = "Start" startTeam.AutoAssignable = true startTeam.TeamColor = BrickColor.new("White") startTeam.Parent = Teams -- create checkpoint teams (Autoassignable = false), ensuring all TeamColors are unique local team1 = Instance.new("Team") team1.Name = "Checkpoint 1" team1.AutoAssignable = false team1.TeamColor = BrickColor.new("Bright blue") team1.Parent = Teams local team2 = Instance.new("Team") team2.Name = "Checkpoint 2" team2.AutoAssignable = false team2.TeamColor = BrickColor.new("Bright green") team2.Parent = Teams local team3 = Instance.new("Team") team3.Name = "Checkpoint 2" team3.AutoAssignable = false team3.TeamColor = BrickColor.new("Bright red") team3.Parent = Teams -- create spawns local startSpawn = Instance.new("SpawnLocation") startSpawn.Anchored = true startSpawn.Size = Vector3.new(5, 1, 5) startSpawn.Neutral = false startSpawn.AllowTeamChangeOnTouch = false startSpawn.TeamColor = startTeam.TeamColor startSpawn.BrickColor = startTeam.TeamColor startSpawn.Parent = game.Workspace local team1Spawn = Instance.new("SpawnLocation") team1Spawn.Anchored = true team1Spawn.Size = Vector3.new(5, 1, 5) team1Spawn.Neutral = false team1Spawn.AllowTeamChangeOnTouch = true team1Spawn.TeamColor = team1.TeamColor team1Spawn.BrickColor = team1.TeamColor team1Spawn.Parent = game.Workspace local team2Spawn = Instance.new("SpawnLocation") team2Spawn.Anchored = true team2Spawn.Size = Vector3.new(5, 1, 5) team2Spawn.Neutral = false team2Spawn.AllowTeamChangeOnTouch = true team2Spawn.TeamColor = team2.TeamColor team2Spawn.BrickColor = team2.TeamColor team2Spawn.Parent = game.Workspace local team3Spawn = Instance.new("SpawnLocation") team3Spawn.Anchored = true team3Spawn.Size = Vector3.new(5, 1, 5) team3Spawn.Neutral = false team3Spawn.AllowTeamChangeOnTouch = true team3Spawn.TeamColor = team3.TeamColor team3Spawn.BrickColor = team3.TeamColor team3Spawn.Parent = game.Workspace -- position spawns startSpawn.CFrame = CFrame.new(0, 0.5, 0) team1Spawn.CFrame = CFrame.new(10, 0.5, 0) team2Spawn.CFrame = CFrame.new(20, 0.5, 0) team3Spawn.CFrame = CFrame.new(30, 0.5, 0) ``` ## Properties ### Property: SpawnLocation.AllowTeamChangeOnTouch ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Teams", "capabilities": [ "Basic", "Players" ] } ``` Allows a [Player](/docs/reference/engine/classes/Player.md) to join the team by touching the [SpawnLocation](/docs/reference/engine/classes/SpawnLocation.md). When set to true, if a [Player](/docs/reference/engine/classes/Player.md) character comes into contact with the [SpawnLocation](/docs/reference/engine/classes/SpawnLocation.md), the player's [Player.TeamColor](/docs/reference/engine/classes/Player.md) will be set to [SpawnLocation.TeamColor](/docs/reference/engine/classes/SpawnLocation.md). [Player.Neutral](/docs/reference/engine/classes/Player.md) will also be set to [SpawnLocation.Neutral](/docs/reference/engine/classes/SpawnLocation.md) upon contact, meaning a player can also become neutral by touching a spawn location. This will not function when [SpawnLocation.Enabled](/docs/reference/engine/classes/SpawnLocation.md) is set to false. #### Making Checkpoints This feature is often used to make checkpoints in obstacle courses or similar games. **SpawnLocation Checkpoints** This sample demonstrates how SpawnLocations can be used to make a checkpoint system. Typically this would be done Studio and not in Lua, but this example serves as a comprehensive example of what Team and SpawnLocation properties need to be used to achieve this setup. ```lua local Teams = game:GetService("Teams") -- create start team (AutoAssignable = true) local startTeam = Instance.new("Team") startTeam.Name = "Start" startTeam.AutoAssignable = true startTeam.TeamColor = BrickColor.new("White") startTeam.Parent = Teams -- create checkpoint teams (Autoassignable = false), ensuring all TeamColors are unique local team1 = Instance.new("Team") team1.Name = "Checkpoint 1" team1.AutoAssignable = false team1.TeamColor = BrickColor.new("Bright blue") team1.Parent = Teams local team2 = Instance.new("Team") team2.Name = "Checkpoint 2" team2.AutoAssignable = false team2.TeamColor = BrickColor.new("Bright green") team2.Parent = Teams local team3 = Instance.new("Team") team3.Name = "Checkpoint 2" team3.AutoAssignable = false team3.TeamColor = BrickColor.new("Bright red") team3.Parent = Teams -- create spawns local startSpawn = Instance.new("SpawnLocation") startSpawn.Anchored = true startSpawn.Size = Vector3.new(5, 1, 5) startSpawn.Neutral = false startSpawn.AllowTeamChangeOnTouch = false startSpawn.TeamColor = startTeam.TeamColor startSpawn.BrickColor = startTeam.TeamColor startSpawn.Parent = game.Workspace local team1Spawn = Instance.new("SpawnLocation") team1Spawn.Anchored = true team1Spawn.Size = Vector3.new(5, 1, 5) team1Spawn.Neutral = false team1Spawn.AllowTeamChangeOnTouch = true team1Spawn.TeamColor = team1.TeamColor team1Spawn.BrickColor = team1.TeamColor team1Spawn.Parent = game.Workspace local team2Spawn = Instance.new("SpawnLocation") team2Spawn.Anchored = true team2Spawn.Size = Vector3.new(5, 1, 5) team2Spawn.Neutral = false team2Spawn.AllowTeamChangeOnTouch = true team2Spawn.TeamColor = team2.TeamColor team2Spawn.BrickColor = team2.TeamColor team2Spawn.Parent = game.Workspace local team3Spawn = Instance.new("SpawnLocation") team3Spawn.Anchored = true team3Spawn.Size = Vector3.new(5, 1, 5) team3Spawn.Neutral = false team3Spawn.AllowTeamChangeOnTouch = true team3Spawn.TeamColor = team3.TeamColor team3Spawn.BrickColor = team3.TeamColor team3Spawn.Parent = game.Workspace -- position spawns startSpawn.CFrame = CFrame.new(0, 0.5, 0) team1Spawn.CFrame = CFrame.new(10, 0.5, 0) team2Spawn.CFrame = CFrame.new(20, 0.5, 0) team3Spawn.CFrame = CFrame.new(30, 0.5, 0) ``` ### Property: SpawnLocation.Duration ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Forcefield", "capabilities": [ "Basic", "Players" ] } ``` The length of time, in seconds, that a [ForceField](/docs/reference/engine/classes/ForceField.md) will be applied to a [Player](/docs/reference/engine/classes/Player.md) character spawning at this [SpawnLocation](/docs/reference/engine/classes/SpawnLocation.md). If Duration is zero, the [ForceField](/docs/reference/engine/classes/ForceField.md) is never created, and it will not trigger the [Instance.DescendantAdded](/docs/reference/engine/classes/Instance.md) or [Instance.ChildAdded](/docs/reference/engine/classes/Instance.md) events. This default value of this property is 10 seconds. The duration feature allows developers to easily give [Players](/docs/reference/engine/classes/Player.md) protection from 'spawn killing' which can be a frustrating experience for players. Note, [ForceFields](/docs/reference/engine/classes/ForceField.md) will only protect users from [Explosions](/docs/reference/engine/classes/Explosion.md) and weapons that use [Humanoid:TakeDamage()](/docs/reference/engine/classes/Humanoid.md) to deal damage or otherwise check for a [ForceField](/docs/reference/engine/classes/ForceField.md). **SpawnLocation ForceField** This sample will create a neutral SpawnLocation in the Workspace that'll give players spawning a ForceField for 20 seconds. ```lua local spawnLocation = Instance.new("SpawnLocation") spawnLocation.Anchored = true spawnLocation.Size = Vector3.new(5, 1, 5) spawnLocation.Neutral = true -- anyone can spawn here spawnLocation.Duration = 20 spawnLocation.Parent = workspace ``` ### Property: SpawnLocation.Enabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "Basic", "Players" ] } ``` Sets whether or not the [SpawnLocation](/docs/reference/engine/classes/SpawnLocation.md) is enabled. When disabled players cannot spawn at the [SpawnLocation](/docs/reference/engine/classes/SpawnLocation.md) and the [SpawnLocation.AllowTeamChangeOnTouch](/docs/reference/engine/classes/SpawnLocation.md) functionality is disabled. This property provides the most convenient way of preventing [Players](/docs/reference/engine/classes/Player.md) from spawning at a spawn. Note, although team changing on touch using [SpawnLocation.AllowTeamChangeOnTouch](/docs/reference/engine/classes/SpawnLocation.md) is disabled when Enabled is set to false, other touched events using [BasePart.Touched](/docs/reference/engine/classes/BasePart.md) will still fire. **SpawnLocation Enabled** The following sample will create a SpawnLocation in the Workspace that will become semi-transparent when it is disabled. ```lua local spawnLocation = Instance.new("SpawnLocation") spawnLocation.Anchored = true spawnLocation.Size = Vector3.new(5, 1, 5) spawnLocation.Neutral = true -- anyone can spawn here spawnLocation.Enabled = true spawnLocation.Parent = workspace local function onEnabledChanged() spawnLocation.Transparency = spawnLocation.Enabled and 0 or 0.5 end spawnLocation:GetPropertyChangedSignal("Enabled"):Connect(onEnabledChanged) task.wait(5) spawnLocation.Enabled = false -- transparency = 0.5 ``` ### Property: SpawnLocation.Neutral ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Teams", "capabilities": [ "Basic", "Players" ] } ``` Whether or not a spawn is affiliated with a specific team. This means that any [Player](/docs/reference/engine/classes/Player.md), of any [Team](/docs/reference/engine/classes/Team.md), can spawn on it if this property is set to true. If Neutral is set to false, only players whose [Player.TeamColor](/docs/reference/engine/classes/Player.md) is equal to [SpawnLocation.TeamColor](/docs/reference/engine/classes/SpawnLocation.md) can use the [SpawnLocation](/docs/reference/engine/classes/SpawnLocation.md). If [SpawnLocation.AllowTeamChangeOnTouch](/docs/reference/engine/classes/SpawnLocation.md) is true [Player.Neutral](/docs/reference/engine/classes/Player.md) will be set to this property upon contact with the spawn. **SpawnLocation Checkpoints** This sample demonstrates how SpawnLocations can be used to make a checkpoint system. Typically this would be done Studio and not in Lua, but this example serves as a comprehensive example of what Team and SpawnLocation properties need to be used to achieve this setup. ```lua local Teams = game:GetService("Teams") -- create start team (AutoAssignable = true) local startTeam = Instance.new("Team") startTeam.Name = "Start" startTeam.AutoAssignable = true startTeam.TeamColor = BrickColor.new("White") startTeam.Parent = Teams -- create checkpoint teams (Autoassignable = false), ensuring all TeamColors are unique local team1 = Instance.new("Team") team1.Name = "Checkpoint 1" team1.AutoAssignable = false team1.TeamColor = BrickColor.new("Bright blue") team1.Parent = Teams local team2 = Instance.new("Team") team2.Name = "Checkpoint 2" team2.AutoAssignable = false team2.TeamColor = BrickColor.new("Bright green") team2.Parent = Teams local team3 = Instance.new("Team") team3.Name = "Checkpoint 2" team3.AutoAssignable = false team3.TeamColor = BrickColor.new("Bright red") team3.Parent = Teams -- create spawns local startSpawn = Instance.new("SpawnLocation") startSpawn.Anchored = true startSpawn.Size = Vector3.new(5, 1, 5) startSpawn.Neutral = false startSpawn.AllowTeamChangeOnTouch = false startSpawn.TeamColor = startTeam.TeamColor startSpawn.BrickColor = startTeam.TeamColor startSpawn.Parent = game.Workspace local team1Spawn = Instance.new("SpawnLocation") team1Spawn.Anchored = true team1Spawn.Size = Vector3.new(5, 1, 5) team1Spawn.Neutral = false team1Spawn.AllowTeamChangeOnTouch = true team1Spawn.TeamColor = team1.TeamColor team1Spawn.BrickColor = team1.TeamColor team1Spawn.Parent = game.Workspace local team2Spawn = Instance.new("SpawnLocation") team2Spawn.Anchored = true team2Spawn.Size = Vector3.new(5, 1, 5) team2Spawn.Neutral = false team2Spawn.AllowTeamChangeOnTouch = true team2Spawn.TeamColor = team2.TeamColor team2Spawn.BrickColor = team2.TeamColor team2Spawn.Parent = game.Workspace local team3Spawn = Instance.new("SpawnLocation") team3Spawn.Anchored = true team3Spawn.Size = Vector3.new(5, 1, 5) team3Spawn.Neutral = false team3Spawn.AllowTeamChangeOnTouch = true team3Spawn.TeamColor = team3.TeamColor team3Spawn.BrickColor = team3.TeamColor team3Spawn.Parent = game.Workspace -- position spawns startSpawn.CFrame = CFrame.new(0, 0.5, 0) team1Spawn.CFrame = CFrame.new(10, 0.5, 0) team2Spawn.CFrame = CFrame.new(20, 0.5, 0) team3Spawn.CFrame = CFrame.new(30, 0.5, 0) ``` ### Property: SpawnLocation.TeamColor ```json { "type": "BrickColor", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Teams", "capabilities": [ "Basic", "Players" ] } ``` The TeamColor property sets what team the [SpawnLocation](/docs/reference/engine/classes/SpawnLocation.md) is affiliated to. If [SpawnLocation.Neutral](/docs/reference/engine/classes/SpawnLocation.md) property is false, only [Players](/docs/reference/engine/classes/Player.md) with the same [Player.TeamColor](/docs/reference/engine/classes/Player.md) as the spawn's TeamColor will be able to spawn there. If [SpawnLocation.AllowTeamChangeOnTouch](/docs/reference/engine/classes/SpawnLocation.md) is true [Player.Neutral](/docs/reference/engine/classes/Player.md) will be set to this property upon contact with the spawn. **SpawnLocation Checkpoints** This sample demonstrates how SpawnLocations can be used to make a checkpoint system. Typically this would be done Studio and not in Lua, but this example serves as a comprehensive example of what Team and SpawnLocation properties need to be used to achieve this setup. ```lua local Teams = game:GetService("Teams") -- create start team (AutoAssignable = true) local startTeam = Instance.new("Team") startTeam.Name = "Start" startTeam.AutoAssignable = true startTeam.TeamColor = BrickColor.new("White") startTeam.Parent = Teams -- create checkpoint teams (Autoassignable = false), ensuring all TeamColors are unique local team1 = Instance.new("Team") team1.Name = "Checkpoint 1" team1.AutoAssignable = false team1.TeamColor = BrickColor.new("Bright blue") team1.Parent = Teams local team2 = Instance.new("Team") team2.Name = "Checkpoint 2" team2.AutoAssignable = false team2.TeamColor = BrickColor.new("Bright green") team2.Parent = Teams local team3 = Instance.new("Team") team3.Name = "Checkpoint 2" team3.AutoAssignable = false team3.TeamColor = BrickColor.new("Bright red") team3.Parent = Teams -- create spawns local startSpawn = Instance.new("SpawnLocation") startSpawn.Anchored = true startSpawn.Size = Vector3.new(5, 1, 5) startSpawn.Neutral = false startSpawn.AllowTeamChangeOnTouch = false startSpawn.TeamColor = startTeam.TeamColor startSpawn.BrickColor = startTeam.TeamColor startSpawn.Parent = game.Workspace local team1Spawn = Instance.new("SpawnLocation") team1Spawn.Anchored = true team1Spawn.Size = Vector3.new(5, 1, 5) team1Spawn.Neutral = false team1Spawn.AllowTeamChangeOnTouch = true team1Spawn.TeamColor = team1.TeamColor team1Spawn.BrickColor = team1.TeamColor team1Spawn.Parent = game.Workspace local team2Spawn = Instance.new("SpawnLocation") team2Spawn.Anchored = true team2Spawn.Size = Vector3.new(5, 1, 5) team2Spawn.Neutral = false team2Spawn.AllowTeamChangeOnTouch = true team2Spawn.TeamColor = team2.TeamColor team2Spawn.BrickColor = team2.TeamColor team2Spawn.Parent = game.Workspace local team3Spawn = Instance.new("SpawnLocation") team3Spawn.Anchored = true team3Spawn.Size = Vector3.new(5, 1, 5) team3Spawn.Neutral = false team3Spawn.AllowTeamChangeOnTouch = true team3Spawn.TeamColor = team3.TeamColor team3Spawn.BrickColor = team3.TeamColor team3Spawn.Parent = game.Workspace -- position spawns startSpawn.CFrame = CFrame.new(0, 0.5, 0) team1Spawn.CFrame = CFrame.new(10, 0.5, 0) team2Spawn.CFrame = CFrame.new(20, 0.5, 0) team3Spawn.CFrame = CFrame.new(30, 0.5, 0) ``` ## Inherited Members ### From [Part](/docs/reference/engine/classes/Part.md) - **Property `Shape`** (`PartType`): Sets the overall shape of the object. ### From [FormFactorPart](/docs/reference/engine/classes/FormFactorPart.md) - **Property `FormFactor`** (`FormFactor`): *(deprecated)* - **Property `formFactor`** (`FormFactor`): *(deprecated, hidden)* ### From [BasePart](/docs/reference/engine/classes/BasePart.md) - **Property `Anchored`** (`boolean`): Determines whether a part is immovable by physics. - **Property `AssemblyAngularVelocity`** (`Vector3`): The angular velocity of the part's assembly. - **Property `AssemblyCenterOfMass`** (`Vector3`): The center of mass of the part's assembly in world space. - **Property `AssemblyLinearVelocity`** (`Vector3`): The linear velocity of the part's assembly. - **Property `AssemblyMass`** (`float`): The total mass of the part's assembly. - **Property `AssemblyRootPart`** (`BasePart`): A reference to the root part of the assembly. - **Property `AudioCanCollide`** (`boolean`): Determines whether the part will physically interact with audio - **Property `BackParamA`** (`float`): Determines the first parameter for the SurfaceType on the Back face of a *(deprecated, hidden)* - **Property `BackParamB`** (`float`): Determines the second parameter for the SurfaceType on the Back face of a *(deprecated, hidden)* - **Property `BackSurface`** (`SurfaceType`): Determines the type of surface for the back face of a part. - **Property `BackSurfaceInput`** (`InputType`): Determines the kind of input for the Back face of a part. *(deprecated, hidden)* - **Property `BottomParamA`** (`float`): Determines the first parameter for the SurfaceType on the Bottom face of a *(deprecated, hidden)* - **Property `BottomParamB`** (`float`): Determines the second parameter for the SurfaceType on the Bottom face of *(deprecated, hidden)* - **Property `BottomSurface`** (`SurfaceType`): Determines the type of surface for the bottom face of a part. - **Property `BottomSurfaceInput`** (`InputType`): Determines the kind of input for the Bottom face of a part. *(deprecated, hidden)* - **Property `BrickColor`** (`BrickColor`): Determines the color of a part. - **Property `brickColor`** (`BrickColor`): *(deprecated)* - **Property `CanCollide`** (`boolean`): Determines whether a part may collide with other parts. - **Property `CanQuery`** (`boolean`): Determines whether the part is considered during spatial query operations. - **Property `CanTouch`** (`boolean`): Determines if Touched and - **Property `CastShadow`** (`boolean`): Determines whether or not a part casts a shadow. - **Property `CenterOfMass`** (`Vector3`): Describes the world position in which a part's center of mass is located. - **Property `CFrame`** (`CFrame`): Determines the position and orientation of the BasePart in the - **Property `CollisionGroup`** (`string`): Describes the name of a part's collision group. - **Property `CollisionGroupId`** (`int`): Describes the automatically set ID number of a part's collision group. *(deprecated)* - **Property `Color`** (`Color3`): Determines the color of a part. - **Property `CurrentPhysicalProperties`** (`PhysicalProperties`): Indicates the current physical properties of the part. - **Property `CustomPhysicalProperties`** (`PhysicalProperties`): Determines several physical properties of a part. - **Property `Elasticity`** (`float`): Used to control the Elasticity of the part, but it no longer does *(deprecated, hidden)* - **Property `EnableFluidForces`** (`boolean`): Used to enable or disable aerodynamic forces on parts and assemblies. - **Property `ExtentsCFrame`** (`CFrame`): The CFrame of the physical extents of the BasePart. - **Property `ExtentsSize`** (`Vector3`): The actual physical size of the BasePart as regarded by the - **Property `Friction`** (`float`): Used to control the Friction of the part, but now it no longer does *(deprecated, hidden)* - **Property `FrontParamA`** (`float`): Determines the first parameter for the SurfaceType on the Front face of a *(deprecated, hidden)* - **Property `FrontParamB`** (`float`): Determines the second parameter for the SurfaceType on the Front face of a *(deprecated, hidden)* - **Property `FrontSurface`** (`SurfaceType`): Determines the type of surface for the front face of a part. - **Property `FrontSurfaceInput`** (`InputType`): Determines the kind of input for the Front face of a part (-Z direction). *(deprecated, hidden)* - **Property `LeftParamA`** (`float`): Determines the first parameter for the SurfaceType on the Left face of a *(deprecated, hidden)* - **Property `LeftParamB`** (`float`): Determines the second parameter for the SurfaceType on the Left face of a *(deprecated, hidden)* - **Property `LeftSurface`** (`SurfaceType`): Determines the type of surface for the left face of a part. - **Property `LeftSurfaceInput`** (`InputType`): Determines the kind of input for the Left face of a part. *(deprecated, hidden)* - **Property `LocalTransparencyModifier`** (`float`): Determines a multiplier for BasePart.Transparency that is only *(hidden)* - **Property `Locked`** (`boolean`): Determines whether a part is selectable in Studio. - **Property `Mass`** (`float`): Describes the mass of the part, the product of its density and volume. - **Property `Massless`** (`boolean`): Determines whether the part contributes to the total mass or inertia of - **Property `Material`** (`Material`): Determines the texture and default physical properties of a part. - **Property `MaterialVariant`** (`string`): The name of MaterialVariant. - **Property `Orientation`** (`Vector3`): Describes the rotation of the part in the world. *(hidden)* - **Property `PivotOffset`** (`CFrame`): Specifies the offset of the part's pivot from its CFrame. - **Property `Position`** (`Vector3`): Describes the position of the part in the world. *(hidden)* - **Property `ReceiveAge`** (`float`): Time since last recorded physics update. *(hidden)* - **Property `Reflectance`** (`float`): Determines how much a part reflects the skybox. - **Property `ResizeableFaces`** (`Faces`): Describes the faces on which a part may be resized. - **Property `ResizeIncrement`** (`int`): Describes the smallest change in size allowable by the - **Property `RightParamA`** (`float`): Determines the first parameter for the SurfaceType on the Right face of a *(deprecated, hidden)* - **Property `RightParamB`** (`float`): Determines the second parameter for the SurfaceType on the Right face of a *(deprecated, hidden)* - **Property `RightSurface`** (`SurfaceType`): Determines the type of surface for the right face of a part. - **Property `RightSurfaceInput`** (`InputType`): Determines the kind of input for the Right face of a part (-X direction). *(deprecated, hidden)* - **Property `RootPriority`** (`int`): The main rule in determining the root part of an assembly. - **Property `Rotation`** (`Vector3`): The rotation of the part in degrees for the three axes. - **Property `RotVelocity`** (`Vector3`): Determines a part's change in orientation over time. *(deprecated, hidden)* - **Property `Size`** (`Vector3`): Determines the dimensions of a part (length, width, height). - **Property `SpecificGravity`** (`float`): The ratio of the part's density to the density of water determined by the *(deprecated)* - **Property `TopParamA`** (`float`): Determines the first parameter for the SurfaceType on the Top face of a *(deprecated, hidden)* - **Property `TopParamB`** (`float`): Determines the second parameter for the SurfaceType on the Top face of a *(deprecated, hidden)* - **Property `TopSurface`** (`SurfaceType`): Determines the type of surface for the top face of a part. - **Property `TopSurfaceInput`** (`InputType`): Determines the kind of input for the Top face of a part (+Y direction). *(deprecated, hidden)* - **Property `Transparency`** (`float`): Determines how much a part can be seen through (the inverse of part - **Property `Velocity`** (`Vector3`): Determines a part's change in position over time. *(deprecated, hidden)* - **Method `AngularAccelerationToTorque(angAcceleration: Vector3, angVelocity?: Vector3): Vector3`**: Returns the torque needed to achieve a given angular acceleration on this - **Method `ApplyAngularImpulse(impulse: Vector3): ()`**: Apply an angular impulse to the assembly. - **Method `ApplyImpulse(impulse: Vector3): ()`**: Apply an impulse to the assembly at the assembly's - **Method `ApplyImpulseAtPosition(impulse: Vector3, position: Vector3): ()`**: Apply an impulse to the assembly at specified position. - **Method `BreakJoints(): ()`**: Breaks any surface connection with any adjacent part, including *(deprecated)* - **Method `breakJoints(): ()`**: *(deprecated)* - **Method `CanCollideWith(part: BasePart): boolean`**: Returns whether the parts can collide with each other. - **Method `CanSetNetworkOwnership(): Tuple`**: Checks whether you can set a part's network ownership. - **Method `GetClosestPointOnSurface(position: Vector3): Vector3`**: Returns the closest point on the part's surface to the given point. - **Method `GetConnectedParts(recursive?: boolean): List`**: Returns a table of parts connected to the object by any kind of rigid - **Method `GetJoints(): Instances`**: Return all Joints or Constraints that is connected to this Part. - **Method `GetMass(): float`**: Returns the value of the Mass property. - **Method `getMass(): float`**: *(deprecated)* - **Method `GetNetworkOwner(): Instance`**: Returns the current player who is the network owner of this part, or `nil` - **Method `GetNetworkOwnershipAuto(): boolean`**: Returns true if the game engine automatically decides the network owner - **Method `GetNoCollisionConstraints(): Instances`**: - **Method `GetRenderCFrame(): CFrame`**: OBSOLETE. Returns a CFrame describing where the part is being rendered at. *(deprecated)* - **Method `GetRootPart(): Instance`**: Returns the base part of an assembly of parts. *(deprecated)* - **Method `GetTouchingParts(): Instances`**: Returns a table of all BasePart.CanCollide true parts that - **Method `GetVelocityAtPosition(position: Vector3): Vector3`**: Returns the linear velocity of the part's assembly at the given position - **Method `IntersectAsync(parts: Instances, collisionfidelity?: CollisionFidelity, renderFidelity?: RenderFidelity): Instance`**: Note: It is highly recommended to use the newer - **Method `IsGrounded(): boolean`**: Returns true if the object is connected to a part that will hold it in - **Method `MakeJoints(): ()`**: Creates a joint on any side of the object that has a surface ID that can *(deprecated)* - **Method `makeJoints(): ()`**: *(deprecated)* - **Method `Resize(normalId: NormalId, deltaAmount: int): boolean`**: Changes the size of an object just like using the Studio resize tool. - **Method `resize(normalId: NormalId, deltaAmount: int): boolean`**: *(deprecated)* - **Method `SetNetworkOwner(playerInstance?: Player): ()`**: Sets the given player as network owner for this and all connected parts. - **Method `SetNetworkOwnershipAuto(): ()`**: Lets the game engine dynamically decide who will handle the part's physics - **Method `SubtractAsync(parts: Instances, collisionfidelity?: CollisionFidelity, renderFidelity?: RenderFidelity): Instance`**: Note: It is highly recommended to use the newer - **Method `TorqueToAngularAcceleration(torque: Vector3, angVelocity?: Vector3): Vector3`**: Returns the angular acceleration that would result from applying a given - **Method `UnionAsync(parts: Instances, collisionfidelity?: CollisionFidelity, renderFidelity?: RenderFidelity): Instance`**: Note: It is highly recommended to use the newer - **Event `LocalSimulationTouched`**: *(deprecated)* - **Event `OutfitChanged`**: *(deprecated)* - **Event `StoppedTouching`**: *(deprecated)* - **Event `Touched`**: Fires when a part touches another part as a result of physical movement. - **Event `TouchEnded`**: Fires when a part stops touching another part as a result of physical ### From [PVInstance](/docs/reference/engine/classes/PVInstance.md) - **Property `Origin`** (`CFrame`): - **Property `Pivot Offset`** (`CFrame`): - **Method `GetPivot(): CFrame`**: Gets the pivot of a PVInstance. - **Method `PivotTo(targetCFrame: CFrame): ()`**: Transforms the PVInstance along with all of its descendant ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: SpawnerService last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service summary: "An internal service that is responsible for the behavior of SpawnLocations. Its functionality is not accessible to developers." --- # Class: SpawnerService > An internal service that is responsible for the behavior of > [SpawnLocations](/docs/reference/engine/classes/SpawnLocation.md). Its functionality is not accessible to > developers. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: SpecialMesh last_updated: 2026-06-29T19:34:08Z inherits: - FileMesh - DataModelMesh - Instance - Object type: class memory_category: BaseParts summary: "The SpecialMesh object applies a mesh to a BasePart depending on the MeshType property." --- # Class: SpecialMesh > The [SpecialMesh](/docs/reference/engine/classes/SpecialMesh.md) object applies a mesh to a [BasePart](/docs/reference/engine/classes/BasePart.md) depending > on the [MeshType](/docs/reference/engine/classes/SpecialMesh.md) property. ## Description The [SpecialMesh](/docs/reference/engine/classes/SpecialMesh.md) object applies a mesh to a [BasePart](/docs/reference/engine/classes/BasePart.md) depending on the [MeshType](/docs/reference/engine/classes/SpecialMesh.md) property. A number of options are available. - **Brick** - A block shape, equivalent to a [BlockMesh](/docs/reference/engine/classes/BlockMesh.md) - **Cylinder** - A cylinder, identical to a [Part](/docs/reference/engine/classes/Part.md) with a [Part.Shape](/docs/reference/engine/classes/Part.md) of 'Cylinder' - **FileMesh** - A user uploaded Mesh, equivalent to [FileMesh](/docs/reference/engine/classes/FileMesh.md) that a texture can be applied to using the [FileMesh.TextureId](/docs/reference/engine/classes/FileMesh.md) property - **Head** - A character head shape - **Sphere** - A sphere shape, similar to a [Part](/docs/reference/engine/classes/Part.md) with a [Part.Shape](/docs/reference/engine/classes/Part.md) of 'Ball' but can be freely resized on all axis - **Wedge** - A wedge shape, identical to a [WedgePart](/docs/reference/engine/classes/WedgePart.md) - **Torso** - A block with sloped sides, due to be deprecated Note, each [SpecialMesh.MeshType](/docs/reference/engine/classes/SpecialMesh.md) will scale differently when using [DataModelMesh.Scale](/docs/reference/engine/classes/DataModelMesh.md), for more information on this please see the page on [DataModelMesh.Scale](/docs/reference/engine/classes/DataModelMesh.md). The SpecialMesh object also exposes the [DataModelMesh.Offset](/docs/reference/engine/classes/DataModelMesh.md) property. It is important to remember that when using a SpecialMesh, only the appearance of a part changes. The collision model of the part remains the same. For example, a character will not be able to walk correctly over a mesh as the mesh geometry is not taken into account. #### SpecialMesh vs MeshPart There are currently two ways of using a developer created mesh. They are using a SpecialMesh with the [SpecialMesh.FileType](/docs/reference/engine/classes/SpecialMesh.md) set to 'FileMesh', or by using a [MeshPart](/docs/reference/engine/classes/MeshPart.md). Although, on the whole, the [MeshPart](/docs/reference/engine/classes/MeshPart.md) object has superseded the SpecialMesh there are some differences developers should be aware of. - [BasePart.Material](/docs/reference/engine/classes/BasePart.md) displays correctly on the mesh when using a [MeshPart](/docs/reference/engine/classes/MeshPart.md) and not when using a SpecialMesh - [MeshParts](/docs/reference/engine/classes/MeshPart.md) include the [MeshPart.CollisionFidelity](/docs/reference/engine/classes/MeshPart.md) property, meaning the collision model of a [MeshPart](/docs/reference/engine/classes/MeshPart.md) can be set to resemble the geometry of the mesh. The SpecialMesh object by contrast, uses the parent [BaseParts](/docs/reference/engine/classes/BasePart.md) collision model - The mesh of a [MeshPart](/docs/reference/engine/classes/MeshPart.md) scales on all axis depending on the [Size](/docs/reference/engine/classes/BasePart.md) property of the [MeshPart](/docs/reference/engine/classes/MeshPart.md), the mesh of a SpecialMesh does not - The SpecialMesh object includes the [Offset](/docs/reference/engine/classes/DataModelMesh.md) and [Scale](/docs/reference/engine/classes/DataModelMesh.md) properties whereas [MeshParts](/docs/reference/engine/classes/MeshPart.md) do not - The [MeshId](/docs/reference/engine/classes/FileMesh.md) property of a [SpecialMesh](/docs/reference/engine/classes/SpecialMesh.md) can be changed by a [Script](/docs/reference/engine/classes/Script.md) or [LocalScript](/docs/reference/engine/classes/LocalScript.md) during runtime. The [MeshId](/docs/reference/engine/classes/MeshPart.md) property of a [MeshPart](/docs/reference/engine/classes/MeshPart.md) can not. ## Code Samples **Mesh Offset and Scale** In this code sample a `BasePart` is instanced with a `SpecialMesh`. The [DataModelMesh.Scale](/docs/reference/engine/classes/DataModelMesh.md) and [DataModelMesh.Offset](/docs/reference/engine/classes/DataModelMesh.md) properties of the `SpecialMesh` are then animated using `TweenService`. ```lua local TweenService = game:GetService("TweenService") -- instance a part and a mesh local part = Instance.new("Part") part.Size = Vector3.new(4, 8, 4) part.Position = Vector3.new(0, 4, 0) part.Anchored = true part.CanCollide = false local mesh = Instance.new("SpecialMesh") mesh.MeshType = Enum.MeshType.FileMesh mesh.MeshId = "rbxassetid://1086413449" mesh.TextureId = "rbxassetid://1461576423" mesh.Offset = Vector3.new(0, 0, 0) mesh.Scale = Vector3.new(4, 4, 4) mesh.Parent = part -- selection box to show part extents local box = Instance.new("SelectionBox") box.Adornee = part box.Parent = part -- parent part to workspace part.Parent = workspace -- animate offset and scale with a tween local tween = TweenService:Create( mesh, TweenInfo.new(1, Enum.EasingStyle.Linear, Enum.EasingDirection.Out, -1, true, 0), { Scale = Vector3.new(1, 1, 1), Offset = Vector3.new(0, 3, 0) } ) tween:Play() ``` **Mesh VertexColor** In this code sample a `BasePart` is instanced with a `SpecialMesh`. The [DataModelMesh.VertexColor](/docs/reference/engine/classes/DataModelMesh.md) property of the `SpecialMesh` is then animated using `TweenService`. ```lua local TweenService = game:GetService("TweenService") -- instance a part and a mesh local part = Instance.new("Part") part.Size = Vector3.new(4, 8, 4) part.Position = Vector3.new(0, 4, 0) part.Anchored = true part.CanCollide = false local mesh = Instance.new("SpecialMesh") mesh.MeshType = Enum.MeshType.FileMesh mesh.MeshId = "rbxassetid://1086413449" mesh.TextureId = "rbxassetid://1461576423" mesh.Offset = Vector3.new(0, 0, 0) mesh.Scale = Vector3.new(4, 4, 4) mesh.VertexColor = Vector3.new(1, 1, 1) mesh.Parent = part -- parent part to workspace part.Parent = workspace -- create tweens local tweenInfo = TweenInfo.new(1, Enum.EasingStyle.Linear, Enum.EasingDirection.Out) local blackTween = TweenService:Create(mesh, tweenInfo, { VertexColor = Vector3.new(0, 0, 0) }) local redTween = TweenService:Create(mesh, tweenInfo, { VertexColor = Vector3.new(1, 0, 0) }) local greenTween = TweenService:Create(mesh, tweenInfo, { VertexColor = Vector3.new(0, 1, 0) }) local blueTween = TweenService:Create(mesh, tweenInfo, { VertexColor = Vector3.new(0, 0, 1) }) local resetTween = TweenService:Create(mesh, tweenInfo, { VertexColor = Vector3.new(1, 1, 1) }) -- animate while true do blackTween:Play() blackTween.Completed:Wait() redTween:Play() redTween.Completed:Wait() greenTween:Play() greenTween.Completed:Wait() blueTween:Play() blueTween.Completed:Wait() resetTween:Play() resetTween.Completed:Wait() task.wait() end ``` ## Properties ### Property: SpecialMesh.MeshType ```json { "type": "MeshType", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic" ] } ``` The mesh that the [SpecialMesh](/docs/reference/engine/classes/SpecialMesh.md)object applies to the [BasePart](/docs/reference/engine/classes/BasePart.md) depends on the MeshType property. A number of options are available. - **Brick** - A block shape, equivalent to a [BlockMesh](/docs/reference/engine/classes/BlockMesh.md) - **Cylinder** - A cylinder, identical to a [Part](/docs/reference/engine/classes/Part.md) with a [Part.Shape](/docs/reference/engine/classes/Part.md) of 'Cylinder' - **FileMesh** - A user uploaded Mesh, equivalent to [FileMesh](/docs/reference/engine/classes/FileMesh.md) that a texture can be applied to using the [FileMesh.TextureId](/docs/reference/engine/classes/FileMesh.md) property - **Head** - A character head shape - **Sphere** - A sphere shape, similar to a [Part](/docs/reference/engine/classes/Part.md) with a [Part.Shape](/docs/reference/engine/classes/Part.md) of 'Ball' but can be freely resized on all axis - **Wedge** - A wedge shape, identical to a [WedgePart](/docs/reference/engine/classes/WedgePart.md) - **Torso** - A block with sloped sides, due to be deprecated Note, each MeshType will scale differently when using [DataModelMesh.Scale](/docs/reference/engine/classes/DataModelMesh.md), for more information on this please see the page on [DataModelMesh.Scale](/docs/reference/engine/classes/DataModelMesh.md). **Mesh VertexColor** In this code sample a `BasePart` is instanced with a `SpecialMesh`. The [DataModelMesh.VertexColor](/docs/reference/engine/classes/DataModelMesh.md) property of the `SpecialMesh` is then animated using `TweenService`. ```lua local TweenService = game:GetService("TweenService") -- instance a part and a mesh local part = Instance.new("Part") part.Size = Vector3.new(4, 8, 4) part.Position = Vector3.new(0, 4, 0) part.Anchored = true part.CanCollide = false local mesh = Instance.new("SpecialMesh") mesh.MeshType = Enum.MeshType.FileMesh mesh.MeshId = "rbxassetid://1086413449" mesh.TextureId = "rbxassetid://1461576423" mesh.Offset = Vector3.new(0, 0, 0) mesh.Scale = Vector3.new(4, 4, 4) mesh.VertexColor = Vector3.new(1, 1, 1) mesh.Parent = part -- parent part to workspace part.Parent = workspace -- create tweens local tweenInfo = TweenInfo.new(1, Enum.EasingStyle.Linear, Enum.EasingDirection.Out) local blackTween = TweenService:Create(mesh, tweenInfo, { VertexColor = Vector3.new(0, 0, 0) }) local redTween = TweenService:Create(mesh, tweenInfo, { VertexColor = Vector3.new(1, 0, 0) }) local greenTween = TweenService:Create(mesh, tweenInfo, { VertexColor = Vector3.new(0, 1, 0) }) local blueTween = TweenService:Create(mesh, tweenInfo, { VertexColor = Vector3.new(0, 0, 1) }) local resetTween = TweenService:Create(mesh, tweenInfo, { VertexColor = Vector3.new(1, 1, 1) }) -- animate while true do blackTween:Play() blackTween.Completed:Wait() redTween:Play() redTween.Completed:Wait() greenTween:Play() greenTween.Completed:Wait() blueTween:Play() blueTween.Completed:Wait() resetTween:Play() resetTween.Completed:Wait() task.wait() end ``` ## Inherited Members ### From [FileMesh](/docs/reference/engine/classes/FileMesh.md) - **Property `MeshId`** (`ContentId`): The MeshId is the content ID of the mesh that is to be displayed. - **Property `TextureId`** (`ContentId`): The TextureId is the content ID of the texture that is to be applied to ### From [DataModelMesh](/docs/reference/engine/classes/DataModelMesh.md) - **Property `Offset`** (`Vector3`): The Offset of a mesh determines the relative position from the - **Property `Scale`** (`Vector3`): The Scale of a mesh determines the size of the mesh relative to its - **Property `VertexColor`** (`Vector3`): Changes the hue of a mesh's texture, used with FileMesh.TextureId. ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: SphereHandleAdornment last_updated: 2026-06-29T19:34:08Z inherits: - HandleAdornment - PVAdornment - GuiBase3d - GuiBase - Instance - Object type: class memory_category: Instances summary: "A sphere-shaped handle that can be adorned to a BasePart." --- # Class: SphereHandleAdornment > A sphere-shaped handle that can be adorned to a [BasePart](/docs/reference/engine/classes/BasePart.md). ## Description `SphereHandleAdornment` is a sphere-shaped handle that can be adorned to a [BasePart](/docs/reference/engine/classes/BasePart.md). If parented to a player's [PlayerGui](/docs/reference/engine/classes/PlayerGui.md) or the [CoreGui](/docs/reference/engine/classes/CoreGui.md), handles can listen to input events for purposes such as making dragger tools. ## Properties ### Property: SphereHandleAdornment.Radius ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic", "UI" ] } ``` The radius of the sphere adornment in studs. ### Property: SphereHandleAdornment.Shading ```json { "type": "AdornShading", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic", "UI" ] } ``` ## Inherited Members ### From [HandleAdornment](/docs/reference/engine/classes/HandleAdornment.md) - **Property `AdornCullingMode`** (`AdornCullingMode`): Determines whether to automatically cull the adornment. - **Property `AlwaysOnTop`** (`boolean`): Forces this adornment to render on top of all 3D objects in the workspace. - **Property `CFrame`** (`CFrame`): The position and rotation of the object relative to its - **Property `SizeRelativeOffset`** (`Vector3`): The positional offset of the adornment based on the adornee's - **Property `ZIndex`** (`int`): Determines the draw order of this HandleAdornment when - **Event `MouseButton1Down`**: Fires when a player presses down on their left mouse button while hovering - **Event `MouseButton1Up`**: Fires when a player releases their left mouse button while hovering over - **Event `MouseEnter`**: Fires when a player moves their mouse over the adornment. - **Event `MouseLeave`**: Fires when a player moves their mouse out of the adornment. ### From [PVAdornment](/docs/reference/engine/classes/PVAdornment.md) - **Property `Adornee`** (`PVInstance`): The PVInstance which this PVAdornment is attached to. ### From [GuiBase3d](/docs/reference/engine/classes/GuiBase3d.md) - **Property `Color`** (`BrickColor`): Sets the color of a GUI object. *(deprecated, hidden)* - **Property `Color3`** (`Color3`): Sets the color of this GuiBase3d object. - **Property `Transparency`** (`float`): Sets the transparency of this GuiBase3d object. - **Property `Visible`** (`boolean`): Determines whether this GuiBase3d object and its descendants will ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: SpotLight last_updated: 2026-06-29T19:34:08Z inherits: - Light - Instance - Object type: class memory_category: Instances summary: "A light source that emits light directionally in the shape of a cone with a spherical base." --- # Class: SpotLight > A light source that emits light directionally in the shape of a cone with a > spherical base. ## Description A **Spotlight** emits light of a specified [Color](/docs/reference/engine/classes/Light.md) and [Brightness](/docs/reference/engine/classes/Light.md) in the shape of a cone with a spherical base. This object is ideal for **directional** light sources like flashlights and headlights. [Range](/docs/reference/engine/classes/SpotLight.md) controls the distance of illumination and [Angle](/docs/reference/engine/classes/SpotLight.md) defines the angle of light emission from the cone's apex as illustrated below. A spotlight must be a direct child of a [BasePart](/docs/reference/engine/classes/BasePart.md) or [Attachment](/docs/reference/engine/classes/Attachment.md) and will exhibit the following behavior: - When the spotlight is parented to a [BasePart](/docs/reference/engine/classes/BasePart.md), the [Face](/docs/reference/engine/classes/SpotLight.md) property determines the face of the part from which light emanates. - Although [Attachments](/docs/reference/engine/classes/Attachment.md) don't have faces, the [Face](/docs/reference/engine/classes/SpotLight.md) property determines the axis of the attachment from which light emanates; **-Z** is front, **+X** is right, **+Y** is top, etc. See also [SurfaceLight](/docs/reference/engine/classes/SurfaceLight.md) and [PointLight](/docs/reference/engine/classes/PointLight.md). ## Properties ### Property: SpotLight.Angle ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` The angle of which the light is shone from the SpotLight. ### Property: SpotLight.Face ```json { "type": "NormalId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` Sets the side of the parent that the SpotLight comes from. ### Property: SpotLight.Range ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` The size of the area that the SpotLight will illuminate. ## Inherited Members ### From [Light](/docs/reference/engine/classes/Light.md) - **Property `Brightness`** (`float`): Sets how bright the emitted light is, defaults to 1. - **Property `Color`** (`Color3`): The color of the emitted light. - **Property `Enabled`** (`boolean`): If set to true, light will be emitted from the source object. - **Property `Shadows`** (`boolean`): If set to true, will project shadows if light is blocked by an obstacle. ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: SpringConstraint last_updated: 2026-06-29T19:34:08Z inherits: - Constraint - Instance - Object type: class memory_category: BaseParts summary: "Simulates spring and damper behavior between two attachments." --- # Class: SpringConstraint > Simulates spring and damper behavior between two attachments. ## Description A **SpringConstraint** applies a force to its [Attachments](/docs/reference/engine/classes/Attachment.md) based on spring and damper behavior. This constraint, along with a [CylindricalConstraint](/docs/reference/engine/classes/CylindricalConstraint.md), is ideal for building vehicle suspension. Note that if this constraint attaches one part (**A**) to another part (**B**) that is anchored or connected to an anchored part (**Z**), part **A** will not be locally simulated when interacting with a player. When configuring this constraint, it may be helpful to study [Roblox Units](/docs/en-us/physics/units.md) to understand how Roblox units compare to metric units. #### Free Length [FreeLength](/docs/reference/engine/classes/SpringConstraint.md) defines the natural resting length of the spring. If the attachments are further apart than the free length, they are forced together; if the attachments are closer together than the free length, they are forced apart. #### Damping The [Damping](/docs/reference/engine/classes/SpringConstraint.md) value controls how fast the spring's oscillation dies down. A value of 0 allows the spring to oscillate endlessly, while higher values bring the spring to a rest more quickly. #### Stiffness [Stiffness](/docs/reference/engine/classes/SpringConstraint.md) sets the strength of the spring. Higher values create a spring that responds with more force when its attachments are closer together or further apart than [FreeLength](/docs/reference/engine/classes/SpringConstraint.md). #### Limits Enabling the [LimitsEnabled](/docs/reference/engine/classes/SpringConstraint.md) property exposes the [MinLength](/docs/reference/engine/classes/SpringConstraint.md) and [MaxLength](/docs/reference/engine/classes/SpringConstraint.md) values for setting the minimum and maximum length of the spring. If the spring's attachments reach these limits, they stop moving apart from one another without restitution. ## Properties ### Property: SpringConstraint.Coils ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The number of coils visualized on the [SpringConstraint](/docs/reference/engine/classes/SpringConstraint.md). This can only be set between 0 and 8. ### Property: SpringConstraint.CurrentLength ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Derived", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The current distance between the constraint's [Attachments](/docs/reference/engine/classes/Attachment.md). ### Property: SpringConstraint.Damping ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Spring", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Damping constant for the [SpringConstraint](/docs/reference/engine/classes/SpringConstraint.md). Multiplied to the velocity of the constraint's [Attachments](/docs/reference/engine/classes/Attachment.md) to reduce the spring force applied. ### Property: SpringConstraint.FreeLength ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Spring", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Natural resting length of the spring. ### Property: SpringConstraint.LimitsEnabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Spring", "capabilities": [ "Physics" ], "simulationAccess": true } ``` Sets whether the [SpringConstraint](/docs/reference/engine/classes/SpringConstraint.md) enforces a minimum and maximum length. If the constraint's [Attachments](/docs/reference/engine/classes/Attachment.md) reach these limits, they will simply stop moving apart from one another without restitution. If you need restitution or elasticity at the ends of the range of motion, you can combine a [SpringConstraint](/docs/reference/engine/classes/SpringConstraint.md) with another constraint that allows restitution at the end of its range, such as a [PrismaticConstraint](/docs/reference/engine/classes/PrismaticConstraint.md) or [RopeConstraint](/docs/reference/engine/classes/RopeConstraint.md). ### Property: SpringConstraint.MaxForce ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Spring", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The maximum force the [SpringConstraint](/docs/reference/engine/classes/SpringConstraint.md) can apply on its [Attachments](/docs/reference/engine/classes/Attachment.md). Some spring systems can give rise to forces that grow fast leading to instability. In such cases it is recommended to set MaxForce to a reasonable value. ### Property: SpringConstraint.MaxLength ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Limits", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The maximum separation the [SpringConstraint](/docs/reference/engine/classes/SpringConstraint.md) will allow if [LimitsEnabled](/docs/reference/engine/classes/SpringConstraint.md) is true. ### Property: SpringConstraint.MinLength ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Limits", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The minimum separation the [SpringConstraint](/docs/reference/engine/classes/SpringConstraint.md) will allow if [LimitsEnabled](/docs/reference/engine/classes/SpringConstraint.md) is true. ### Property: SpringConstraint.Radius ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The visualized radius of the spring's coils. ### Property: SpringConstraint.Stiffness ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Spring", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The strength of the spring. The higher this value the more force will be applied when the attachments are separated a different length than the [FreeLength](/docs/reference/engine/classes/SpringConstraint.md). ### Property: SpringConstraint.Thickness ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Physics" ], "simulationAccess": true } ``` The visualized thickness of the spring's coils. ## Inherited Members ### From [Constraint](/docs/reference/engine/classes/Constraint.md) - **Property `Active`** (`boolean`): Indicates if the constraint is currently active in the world. - **Property `Attachment0`** (`Attachment`): The Attachment that is connected to - **Property `Attachment1`** (`Attachment`): The Attachment that is connected to - **Property `Color`** (`BrickColor`): The color of the constraint. - **Property `Enabled`** (`boolean`): Toggles whether or not the constraint is enabled. - **Property `Visible`** (`boolean`): Toggles the constraint's visibility. - **Method `GetDebugAppliedForce(bodyId: int): Vector3`**: *(deprecated)* - **Method `GetDebugAppliedTorque(bodyId: int): Vector3`**: *(deprecated)* ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: StandalonePluginScripts last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances --- # Class: StandalonePluginScripts ## Description **StandalonePluginScripts** is a container of [Scripts](/docs/reference/engine/classes/Script.md) to be run in a standalone plugin [DataModel](/docs/reference/engine/classes/DataModel.md) when Roblox Studio starts (it is not useful outside the context of plugins). When a place to be edited is loaded in Roblox Studio, scripts inside this container are not run. #### Limited Functionality and Availability Functionality of this class may be enabled or disabled without notice. Before using it, verify that it works as expected before continuing with further work. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: StandardPages last_updated: 2026-06-29T19:34:08Z inherits: - Pages - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated --- # Class: StandardPages ## Description A generic version of the Pages class, which may contain variable data, depending on what method it was returned from. See the [Pages](/docs/reference/engine/classes/Pages.md) class for more information. ## Inherited Members ### From [Pages](/docs/reference/engine/classes/Pages.md) - **Property `IsFinished`** (`boolean`): Whether or not the current page is the last page available. - **Method `AdvanceToNextPageAsync(): ()`**: Iterates to the next page in the pages object, if possible. - **Method `GetCurrentPage(): Array`**: Returns the items on the current page. The keys in the item are determined ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: StarterCharacterScripts last_updated: 2026-06-29T19:34:08Z inherits: - StarterPlayerScripts - Instance - Object type: class memory_category: Instances tags: - NotCreatable summary: "Stores instances to be parented to a player's character when it spawns." --- # Class: StarterCharacterScripts > Stores instances to be parented to a player's character when it spawns. ## Description The [StarterCharacterScripts](/docs/reference/engine/classes/StarterCharacterScripts.md) container stores scripts to be parented to a player's [Player.Character](/docs/reference/engine/classes/Player.md) when it spawns. Unlike scripts stored in the [StarterPlayerScripts](/docs/reference/engine/classes/StarterPlayerScripts.md) folder, these scripts will not persist when the character respawns. If a [LocalScript](/docs/reference/engine/classes/LocalScript.md) named `Animate`, `Sound`, or `Health` is placed in this container, it will replace the default script that manages character animations, character sounds, and character health regeneration respectively. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: StarterGear last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances summary: "If the game allows gear, `StarterGear` is a container automatically inserted into each Player object when the player joins the game. Whenever the player's character spawns, the contents of that player's `StarterGear` are copied into the player's Backpack." --- # Class: StarterGear > If the game allows gear, `StarterGear` is a container automatically inserted > into each [Player](/docs/reference/engine/classes/Player.md) object when the player joins the game. Whenever the > player's character spawns, the contents of that player's `StarterGear` are > copied into the player's [Backpack](/docs/reference/engine/classes/Backpack.md). ## Description When a player spawns, the contents of that player's `StarterGear` is copied into the player's [Backpack](/docs/reference/engine/classes/Backpack.md). Additionally, if the game allows gear, all of the appropriate gear [Tools](/docs/reference/engine/classes/Tool.md) that the player owns are inserted into that player's `StarterGear`. Unlike [StarterPack](/docs/reference/engine/classes/StarterPack.md), `StarterGear` isn't a service but rather a child of each [Player](/docs/reference/engine/classes/Player.md) object, so its contents are player‑specific. Whether or not gear can be permitted is determined in a place's **Permissions** settings. You can enable gear by its genre or choose specific types to allow. To disable gear, ensure all the gear types are unselected. Listening to the [Players.PlayerAdded](/docs/reference/engine/classes/Players.md) event from a server [Script](/docs/reference/engine/classes/Script.md) is useful for accessing `StarterGear`: ```lua local Players = game:GetService("Players") local toolPrefab = Instance.new("Tool") toolPrefab.Name = "Example Tool" local function onPlayerAdded(player) -- Wait for StarterGear to be added local starterGear = player:WaitForChild("StarterGear") -- Clone prefab tool to StarterGear local toolClone = toolPrefab:Clone() toolClone.Parent = starterGear end Players.PlayerAdded:Connect(onPlayerAdded) ``` #### Gameplay Considerations Always test experiences after adding gear to them to check that users can't easily abuse them there. Gear may include [Script](/docs/reference/engine/classes/Script.md) objects which allow the player to perform actions that you might not consider; for example, navigational gear might allow the player to access a part of the map that you don't want them to, and weapon gear may allow the holder to damage other players, possibly without retribution or retaliation. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: StarterGui last_updated: 2026-06-29T19:34:08Z inherits: - BasePlayerGui - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service summary: "A container for LayerCollector objects to be copied into the PlayerGui of Players. Also provides a range of functions for interacting with the CoreGui." --- # Class: StarterGui > A container for [LayerCollector](/docs/reference/engine/classes/LayerCollector.md) objects to be copied into the > [PlayerGui](/docs/reference/engine/classes/PlayerGui.md) of [Players](/docs/reference/engine/classes/Player.md). Also provides a range of > functions for interacting with the [CoreGui](/docs/reference/engine/classes/CoreGui.md). ## Description [StarterGui](/docs/reference/engine/classes/StarterGui.md) is a container object designed to hold [LayerCollector](/docs/reference/engine/classes/LayerCollector.md) objects such as [ScreenGuis](/docs/reference/engine/classes/ScreenGui.md). When a [Player.Character](/docs/reference/engine/classes/Player.md) spawns, the contents of their [PlayerGui](/docs/reference/engine/classes/PlayerGui.md) (if any) are emptied. Children of the [StarterGui](/docs/reference/engine/classes/StarterGui.md) are then copied along with their descendants into the [PlayerGui](/docs/reference/engine/classes/PlayerGui.md). Note, however, that [LayerCollector](/docs/reference/engine/classes/LayerCollector.md) objects such as [ScreenGuis](/docs/reference/engine/classes/ScreenGui.md) with their [ResetOnSpawn](/docs/reference/engine/classes/LayerCollector.md) property set to `false` will only be placed into each player's [PlayerGui](/docs/reference/engine/classes/PlayerGui.md) once and will not be deleted when the [Player](/docs/reference/engine/classes/Player.md) respawns. [StarterGui](/docs/reference/engine/classes/StarterGui.md) also includes a range of functions allowing you to interact with the [CoreGui](/docs/reference/engine/classes/CoreGui.md). For example [StarterGui:SetCoreGuiEnabled()](/docs/reference/engine/classes/StarterGui.md) can be used to disable elements of the [CoreGui](/docs/reference/engine/classes/CoreGui.md), and [StarterGui:SetCore()](/docs/reference/engine/classes/StarterGui.md) can perform a range of functions including creating notifications and system messages. ## Properties ### Property: StarterGui.RtlTextSupport ```json { "type": "RtlTextSupport", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "UI" ] } ``` ### Property: StarterGui.ScreenOrientation ```json { "type": "ScreenOrientation", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` This property sets the preferred screen orientation mode for users with mobile devices. For the different modes available, see [ScreenOrientation](/docs/reference/engine/enums/ScreenOrientation.md). By default, this property is set to [Sensor](/docs/reference/engine/enums/ScreenOrientation.md), meaning the experience is displayed depending on the best match to the device's current orientation, either landscape (left/right) or portrait. When a [Player](/docs/reference/engine/classes/Player.md) joins the experience on a mobile device, this property determines the device's starting orientation and sets that player's [PlayerGui.ScreenOrientation](/docs/reference/engine/classes/PlayerGui.md) accordingly. You can also get the player's current screen orientation through [PlayerGui.CurrentScreenOrientation](/docs/reference/engine/classes/PlayerGui.md), useful when using one of the "sensor" [ScreenOrientation](/docs/reference/engine/enums/ScreenOrientation.md) settings. Note that changing this property will not change the screen orientation for [Players](/docs/reference/engine/classes/Player.md) already in the experience. To change the orientation for an existing player, use their [PlayerGui.ScreenOrientation](/docs/reference/engine/classes/PlayerGui.md) property. ### Property: StarterGui.ShowDevelopmentGui ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` This property determines whether the contents of [StarterGui](/docs/reference/engine/classes/StarterGui.md) is visible in Studio. ### Property: StarterGui.VirtualCursorMode ```json { "type": "VirtualCursorMode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "UI" ] } ``` ### Property: StarterGui.ResetPlayerGuiOnSpawn ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` > **Deprecated:** This property is deprecated. Use [LayerCollector.ResetOnSpawn](/docs/reference/engine/classes/LayerCollector.md) to control the resetting behavior for individual [LayerCollector](/docs/reference/engine/classes/LayerCollector.md) objects. If set to true, each child parented to the [StarterGui](/docs/reference/engine/classes/StarterGui.md) will be cloned into a player's [PlayerGui](/docs/reference/engine/classes/PlayerGui.md) when that player's character is respawned. If one of the children is a PlayerGui and it has its PlayerGui property set to false, it will not be cloned. ### Property: StarterGui.ProcessUserInput *(hidden)* ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "UI" ] } ``` Allows [StarterGui](/docs/reference/engine/classes/StarterGui.md) to process input like [PlayerGui](/docs/reference/engine/classes/PlayerGui.md) and [CoreGui](/docs/reference/engine/classes/CoreGui.md) do. The default value is `false`. ## Methods ### Method: StarterGui:GetCore **Signature:** `StarterGui:GetCore(parameterName: string): Variant` This method returns data set or made available by Roblox's core scripts. The first and only parameter is a string that selects the information to be fetched. The following sections describe the strings and the data they return by this function. Calling this method may yield. Many of these also register an equivalent [SetCore()](/docs/reference/engine/classes/StarterGui.md) function (these are marked with an asterisk). ##### PointsNotificationsActive \* Returns `true` if player point notifications are enabled. ##### BadgesNotificationsActive \* Returns `true` if badge notifications are enabled. ##### AvatarContextMenuEnabled \* Returns `true` if the [Avatar Context Menu](/docs/en-us/players/avatar-context-menu.md) is enabled. ##### ChatActive \* Returns whether the chat is active or not. This is indicated by the selection state of the top bar's chat icon. ##### ChatWindowSize \* Returns the size of the chat window as a [UDim2](/docs/reference/engine/datatypes/UDim2.md). ##### ChatWindowPosition \* Returns the size of the chat window as a [UDim2](/docs/reference/engine/datatypes/UDim2.md). ##### ChatBarDisabled \* Returns `true` if the chat bar is disabled. ##### GetBlockedUserIds Returns a list of [UserIds](/docs/reference/engine/classes/Player.md) associated with users that have been blocked by the local player. ##### PlayerBlockedEvent Returns a [BindableEvent](/docs/reference/engine/classes/BindableEvent.md) that is fired whenever a player is blocked by the local player. ##### PlayerUnblockedEvent Returns a [BindableEvent](/docs/reference/engine/classes/BindableEvent.md) that is fired whenever a player is unblocked by the local player. ##### PlayerMutedEvent Returns a [BindableEvent](/docs/reference/engine/classes/BindableEvent.md) that is fired whenever a player is muted by the local player. ##### PlayerUnmutedEvent Returns a [BindableEvent](/docs/reference/engine/classes/BindableEvent.md) that is fired whenever a player is unmuted by the local player. ##### PlayerFriendedEvent Returns a [BindableEvent](/docs/reference/engine/classes/BindableEvent.md) that is fired whenever a player is connected by the local player. ##### PlayerUnfriendedEvent Returns a [BindableEvent](/docs/reference/engine/classes/BindableEvent.md) that is fired whenever a player is unconnected by the local player. ##### DevConsoleVisible \* Returns `true` if the [Developer Console](/docs/en-us/studio/developer-console.md) is visible. ##### VRRotationIntensity Returns a string describing the camera rotation sensitivity in VR: `Low`, `High` and `Smooth`. This will not be available unless [VRService.VREnabled](/docs/reference/engine/classes/VRService.md) is `true`. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: UI* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `parameterName` | `string` | | | **Returns:** `Variant` ### Method: StarterGui:GetCoreGuiEnabled **Signature:** `StarterGui:GetCoreGuiEnabled(coreGuiType: CoreGuiType): boolean` This function returns whether the given [CoreGuiType](/docs/reference/engine/enums/CoreGuiType.md)is enabled, or if it has been disabled using [StarterGui:SetCoreGuiEnabled()](/docs/reference/engine/classes/StarterGui.md). This function should be called on the client. Note that setting `"TopbarEnabled"` to `false` using [SetCore()](/docs/reference/engine/classes/StarterGui.md) hides all [CoreGuiTypes](/docs/reference/engine/enums/CoreGuiType.md) but does not affect the result of this function. *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `coreGuiType` | `CoreGuiType` | | The given [CoreGuiType](/docs/reference/engine/enums/CoreGuiType.md). | **Returns:** `boolean` — Whether the given [CoreGuiType](/docs/reference/engine/enums/CoreGuiType.md) is enabled. **Checking if a Core GUI is Enabled** The below example would print whether or not the player list is visible to the LocalPlayer. ```lua local StarterGui = game:GetService("StarterGui") print(StarterGui:GetCoreGuiEnabled("PlayerList")) ``` ### Method: StarterGui:SetCore **Signature:** `StarterGui:SetCore(parameterName: string, value: Variant): ()` This method (not to be confused with [SetCoreGuiEnabled()](/docs/reference/engine/classes/StarterGui.md)) exposes a variety of functionality defined by Roblox's core scripts, such as sending notifications, toggling notifications for badges/points, defining a callback for the reset button, or toggling the topbar. The first parameter is a string that selects the functionality with which the call will interact. It may be necessary to call this method multiple times using [LuaGlobals.pcall()](/docs/reference/engine/globals/LuaGlobals.md) in case the respective core script has not yet loaded (or if it has been disabled entirely). The following table describes the strings that may be accepted as the first parameter. The parameters that should follow are dependent on the functionality that will be used and are described in sub-tables. ##### ChatActive Controls whether the chat is active. | Name | Type | Default | Description | | --- | --- | --- | --- | | `active` | boolean | (required) | Determines whether the chat should be made active. | ##### PointsNotificationsActive Controls whether notifications for earned player points will appear. | Name | Type | Default | Description | | --- | --- | --- | --- | | `active` | boolean | (required) | Determines whether notifications for earned player points will appear. | ##### BadgesNotificationsActive Controls whether notifications for earned badges will appear. | Name | Type | Default | Description | | --- | --- | --- | --- | | `active` | boolean | (required) | Determines whether notifications for earned badges will appear. | ##### ResetButtonCallback Determines the behavior, if any, of the reset button given a boolean or a [BindableEvent](/docs/reference/engine/classes/BindableEvent.md) to be fired when a player requests to reset. | Name | Type | Default | Description | | --- | --- | --- | --- | | `enabled` | boolean | (required) | Determines whether the reset button retains its default behavior. | | **OR** | | `callback` | [BindableEvent](/docs/reference/engine/classes/BindableEvent.md) | (required) | A [BindableEvent](/docs/reference/engine/classes/BindableEvent.md) to be fired when the player confirms they want to reset. | ##### ChatMakeSystemMessage Display a formatted message in the chat. Using this method requires the experience's [TextChatService.ChatVersion](/docs/reference/engine/classes/TextChatService.md) to be set to [LegacyChatService](/docs/reference/engine/enums/ChatVersion.md), although legacy chat is **deprecated** and usage is discouraged. For experiences using the current [TextChatService](/docs/reference/engine/classes/TextChatService.md), refer to [TextChannel:DisplaySystemMessage()](/docs/reference/engine/classes/TextChannel.md). | Name | Type | Default | Description | | --- | --- | --- | --- | | `configTable` | dictionary | (required) | A dictionary of information describing the message (see below). | | Name | Type | Default | Description | | --- | --- | --- | --- | | `Text` | string | (required) | The message to display. | | `Color` | [Color3](/docs/reference/engine/datatypes/Color3.md) | [Color3.fromRGB(255, 255, 243)](/docs/reference/engine/datatypes/Color3.md) | Text color of the message. | | `Font` | [Font](/docs/reference/engine/enums/Font.md) | `SourceSansBold` | Font of the message. | | `TextSize` | integer | `18` | Text size of the message. | ##### SendNotification Causes a non-intrusive notification to appear at the bottom right of the screen. The notification may have up to two buttons. | Name | Type | Default | Description | | --- | --- | --- | --- | | `configTable` | dictionary | (required) | A dictionary of information describing the notification (see below). | | Name | Type | Default | Description | | --- | --- | --- | --- | | `Title` | string | (required) | The title of the notification. | | `Text` | string | (required) | The main text of the notification. | | `Icon` | string | | The image to display with the notification. | | `Duration` | number | `5` | Duration (in seconds) the notification should stay visible. | | `Callback` | [BindableFunction](/docs/reference/engine/classes/BindableFunction.md) | | A [BindableFunction](/docs/reference/engine/classes/BindableFunction.md) that should be invoked with the text of the button pressed by the player. | | `Button1` | string | | The text to display on the first button. | | `Button2` | string | | The text to display on the second button. | ##### TopbarEnabled Determines whether the topbar is displayed. Disabling the topbar will also disable all [CoreGuis](/docs/reference/engine/classes/CoreGui.md) such as the chat, inventory, and player list (for example, those set with [SetCoreGuiEnabled](/docs/reference/engine/classes/StarterGui.md)). When disabled, the region the topbar once occupied will still capture mouse events; however, buttons placed there will not respond to clicks. The origin of GUI space will still be offset 36 pixels from the top of the screen. | Name | Type | Default | Description | | --- | --- | --- | --- | | `enabled` | boolean | (required) | Determines whether the topbar should be visible. | ##### DevConsoleVisible Determines whether the [Developer Console](/docs/en-us/studio/developer-console.md) is visible. | Name | Type | Default | Description | | --- | --- | --- | --- | | `visibility` | boolean | (required) | Determines whether the console is visible. | ##### PromptSendFriendRequest Prompts the current player to send a friend request to the given [Player](/docs/reference/engine/classes/Player.md). | Name | Type | Default | Description | | --- | --- | --- | --- | | `player` | [Player](/docs/reference/engine/classes/Player.md) | (required) | The player to which the friend request should be sent. | ##### PromptUnfriend Prompts the current player to remove a given [Player](/docs/reference/engine/classes/Player.md) from their friends list. | Name | Type | Default | Description | | --- | --- | --- | --- | | `player` | [Player](/docs/reference/engine/classes/Player.md) | (required) | The player who should be unconnected. | ##### PromptBlockPlayer Prompts the current player to block the given [Player](/docs/reference/engine/classes/Player.md). | Name | Type | Default | Description | | --- | --- | --- | --- | | `player` | [Player](/docs/reference/engine/classes/Player.md) | (required) | The player who should be blocked. | ##### PromptUnblockPlayer Prompts the current player to unblock the given [Player](/docs/reference/engine/classes/Player.md). | Name | Type | Default | Description | | --- | --- | --- | --- | | `player` | [Player](/docs/reference/engine/classes/Player.md) | (required) | The player who should be unblocked. | ##### AvatarContextMenuEnabled Determines whether the [Avatar Context Menu](/docs/en-us/players/avatar-context-menu.md) is enabled. | Name | Type | Default | Description | | --- | --- | --- | --- | | `enabled` | boolean | (required) | Determines whether the context menu is enabled. | ##### AvatarContextMenuTarget Forcibly opens the [Avatar Context Menu](/docs/en-us/players/avatar-context-menu.md). | Name | Type | Default | Description | | --- | --- | --- | --- | | `player` | [Player](/docs/reference/engine/classes/Player.md) | (required) | The player on whom the context menu will be opened. | ##### AddAvatarContextMenuOption Adds an option to the [Avatar Context Menu](/docs/en-us/players/avatar-context-menu.md). | Name | Type | Default | Description | | --- | --- | --- | --- | | `option` | [AvatarContextMenuOption](/docs/reference/engine/enums/AvatarContextMenuOption.md) | (required) | Option to add. | | **OR** | | `option` | table | (required) | A two-element table, where the first is the name of the custom action, and the second is a [BindableEvent](/docs/reference/engine/classes/BindableEvent.md) which will be fired with a player was selected when the option was activated. | ##### RemoveAvatarContextMenuOption Removes an option to the [Avatar Context Menu](/docs/en-us/players/avatar-context-menu.md). The `option` argument must be the same as what was used with `"AddAvatarContextMenuOption"` (see above). | Name | Type | Default | Description | | --- | --- | --- | --- | | `option` | Variant | (required) | The same value provided to **AddAvatarContextMenuOption**. | ##### AvatarContextMenuTheme Configures the customizable [Avatar Context Menu](/docs/en-us/players/avatar-context-menu.md) which is an opt-in feature that allows easy player-to-player social interaction via custom actions, such as initiating trades, battles, and more. For more info on how to customize its theme, see the [Avatar Context Menu](/docs/en-us/players/avatar-context-menu.md) article. ##### CoreGuiChatConnections Sets up a bindable gateway connection between the [CoreGui](/docs/reference/engine/classes/CoreGui.md) topbar's chat button and the legacy chat system. The second parameter must be a table of [BindableEvents](/docs/reference/engine/classes/BindableEvent.md) and [BindableFunctions](/docs/reference/engine/classes/BindableFunction.md). *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `parameterName` | `string` | | Selects the functionality with which the call will interact. | | `value` | `Variant` | | A table of [BindableEvents](/docs/reference/engine/classes/BindableEvent.md) and [BindableFunctions](/docs/reference/engine/classes/BindableFunction.md). | **Returns:** `()` **StarterGui Setting Core GUI** ```lua local StarterGui = game:GetService("StarterGui") StarterGui:SetCore("AvatarContextMenuTheme", { BackgroundImage = "", BackgroundTransparency = 0.5, BackgroundColor = Color3.fromRGB(111, 145, 242), NameTagColor = Color3.fromRGB(0, 0, 200), NameUnderlineColor = Color3.fromRGB(213, 233, 255), ButtonFrameColor = Color3.fromRGB(15, 24, 65), ButtonFrameTransparency = 0.2, ButtonUnderlineColor = Color3.fromRGB(213, 233, 255), Font = Enum.Font.SciFi, }) ``` ### Method: StarterGui:SetCoreGuiEnabled **Signature:** `StarterGui:SetCoreGuiEnabled(coreGuiType: CoreGuiType, enabled: boolean): ()` This function sets whether the [CoreGui](/docs/reference/engine/classes/CoreGui.md) element associated with the given [CoreGuiType](/docs/reference/engine/enums/CoreGuiType.md) is enabled or disabled. The top bar cannot be disabled using this function. To disable it, set `"TopbarEnabled"` to `false` using [StarterGui:SetCore()](/docs/reference/engine/classes/StarterGui.md). *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `coreGuiType` | `CoreGuiType` | | The given [CoreGuiType](/docs/reference/engine/enums/CoreGuiType.md). | | `enabled` | `boolean` | | Whether to enable or disable the given [CoreGuiType](/docs/reference/engine/enums/CoreGuiType.md). | **Returns:** `()` ## Inherited Members ### From [BasePlayerGui](/docs/reference/engine/classes/BasePlayerGui.md) - **Method `GetGuiObjectsAtPosition(x: int, y: int): Instances`**: Returns a list of all GuiObject instances occupying the given ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: StarterPack last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service summary: "A container whose contents are copied into each player's Backpack when their player character spawns. It is generally used to hold Tools." --- # Class: StarterPack > A container whose contents are copied into each player's [Backpack](/docs/reference/engine/classes/Backpack.md) when > their player character spawns. It is generally used to hold > [Tools](/docs/reference/engine/classes/Tool.md). ## Description `StarterPack` is a service-level container whose contents are copied into each player's [Backpack](/docs/reference/engine/classes/Backpack.md) when their player character spawns. It is generally used to hold [Tools](/docs/reference/engine/classes/Tool.md) but it can also hold [LocalScripts](/docs/reference/engine/classes/LocalScript.md) to ensure that each player gets a copy. To ensure that certain [Tools](/docs/reference/engine/classes/Tool.md) are available to specific players instead of **all** players (`StarterPack`), use player‑specific [StarterGear](/docs/reference/engine/classes/StarterGear.md) containers or parent those [Tools](/docs/reference/engine/classes/Tool.md) directly to player [Backpacks](/docs/reference/engine/classes/Backpack.md). ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: StarterPlayer last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service summary: "A service which allows the defaults of properties in the Player object to be set." --- # Class: StarterPlayer > A service which allows the defaults of properties in the [Player](/docs/reference/engine/classes/Player.md) object > to be set. ## Description A service which allows the defaults of properties in the [Player](/docs/reference/engine/classes/Player.md) object to be set. When a player enters the server, each property of the player object is set to the current value of the corresponding property in [StarterPlayer](/docs/reference/engine/classes/StarterPlayer.md). Additionally, you may add four objects to this service: - A [StarterPlayerScripts](/docs/reference/engine/classes/StarterPlayerScripts.md) instance with scripts that run once for each player. - A [StarterCharacterScripts](/docs/reference/engine/classes/StarterCharacterScripts.md) instance with scripts to add to each player's character every time they spawn. - A [Humanoid](/docs/reference/engine/classes/Humanoid.md) instance named `StarterHumanoid` which will be used as the default humanoid for each player's character. - A [Model](/docs/reference/engine/classes/Model.md) instance named `StarterCharacter` which will be used as the character model for all players. ## Properties ### Property: StarterPlayer.AutoJumpEnabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Mobile", "capabilities": [ "Players" ] } ``` This property sets whether the character will automatically jump when hitting an obstacle on a mobile device. This property is copied from the [StarterPlayer](/docs/reference/engine/classes/StarterPlayer.md) to a [Player](/docs/reference/engine/classes/Player.md) when they join the game. Following that. the value of this property is copied to [Humanoid.AutoJumpEnabled](/docs/reference/engine/classes/Humanoid.md) property of the character's [Humanoid](/docs/reference/engine/classes/Humanoid.md) on spawn. In other words, it is possible to set the auto-jump behavior on a per-character, per-player and per-game basis using these three properties. **Auto-Jump Toggle** This code sample is meant for a TextButton. It allows the player to toggle the auto-jumping behavior while on a mobile device. ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local button = script.Parent local function update() -- Update button text if player.AutoJumpEnabled then button.Text = "Auto-Jump is ON" else button.Text = "Auto-Jump is OFF" end -- Reflect the property in the player's character, if they have one if player.Character then local human = player.Character:FindFirstChild("Humanoid") if human then human.AutoJumpEnabled = player.AutoJumpEnabled end end end local function onActivated() -- Toggle auto-jump player.AutoJumpEnabled = not player.AutoJumpEnabled -- Update everything else update() end button.Activated:Connect(onActivated) update() ``` ### Property: StarterPlayer.AvatarJointUpgrade ```json { "type": "RolloutState", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Character", "capabilities": [ "Players" ] } ``` This property controls the rollout state of new [AnimationConstraint](/docs/reference/engine/classes/AnimationConstraint.md) joints in avatars. When disabled, avatars spawn with legacy [Motor6Ds](/docs/reference/engine/classes/Motor6D.md) connecting their limbs. When enabled, avatars spawn with [AnimationConstraints](/docs/reference/engine/classes/AnimationConstraint.md) and [BallSocketConstraints](/docs/reference/engine/classes/BallSocketConstraint.md) connecting their limbs. This makes it easier to write scripts that enable physical simulation, like arm strength and ragdoll falling down. ### Property: StarterPlayer.CameraMaxZoomDistance ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Camera", "capabilities": [ "Players" ] } ``` This property sets the maximum distance in studs the camera can be from the character with the default cameras. This property sets the default value of [Player.CameraMaxZoomDistance](/docs/reference/engine/classes/Player.md) for each player who joins the game. If this value is set to a lower value than [StarterPlayer.CameraMinZoomDistance](/docs/reference/engine/classes/StarterPlayer.md) it will be increased to CameraMinZoomDistance. **Setting Camera Zoom Distance** The example demonstrates how to set a player's camera minimum and maximum zoom distance. In this example, we set the [Player.CameraMinZoomDistance](/docs/reference/engine/classes/Player.md) and [Player.CameraMaxZoomDistance](/docs/reference/engine/classes/Player.md) to set the min and max distance in studs a player's camera can be from their character. Note that since the example attempts to set the CameraMinZoomDistance to be greater than the CameraMaxZoomDistance, the CameraMinZoomDistance value will be decreased and set to the value of the max zoom distance. To change the default min and max zoom distance values for a player when they first enter the game, you can change the `StarterClass.Player.CameraMinZoomDistance` and `StarterClass.Player.CameraMaxZoomDistance` properties. ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer player.CameraMaxZoomDistance = 50 player.CameraMinZoomDistance = 75 ``` ### Property: StarterPlayer.CameraMinZoomDistance ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Camera", "capabilities": [ "Players" ] } ``` This property sets the minimum distance in studs the camera can be from the character with the default cameras. This property sets the default value of [Player.CameraMinZoomDistance](/docs/reference/engine/classes/Player.md) for each player who joins the game. If this value is set to a higher value than [StarterPlayer.CameraMaxZoomDistance](/docs/reference/engine/classes/StarterPlayer.md) it will be decreased to CameraMaxZoomDistance. **Setting Camera Zoom Distance** The example demonstrates how to set a player's camera minimum and maximum zoom distance. In this example, we set the [Player.CameraMinZoomDistance](/docs/reference/engine/classes/Player.md) and [Player.CameraMaxZoomDistance](/docs/reference/engine/classes/Player.md) to set the min and max distance in studs a player's camera can be from their character. Note that since the example attempts to set the CameraMinZoomDistance to be greater than the CameraMaxZoomDistance, the CameraMinZoomDistance value will be decreased and set to the value of the max zoom distance. To change the default min and max zoom distance values for a player when they first enter the game, you can change the `StarterClass.Player.CameraMinZoomDistance` and `StarterClass.Player.CameraMaxZoomDistance` properties. ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer player.CameraMaxZoomDistance = 50 player.CameraMinZoomDistance = 75 ``` ### Property: StarterPlayer.CameraMode ```json { "type": "CameraMode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Camera", "capabilities": [ "Players" ] } ``` Sets the default value for [Player.CameraMode](/docs/reference/engine/classes/Player.md) for each player in the game. The camera has two modes: #### First Person In first person mode, the player's camera is zoomed all the way in. Unless there is a visible GUI present with the [GuiButton.Modal](/docs/reference/engine/classes/GuiButton.md) property set to `true`, the mouse will be locked and the user's camera will turn as the mouse moves. #### Third Person In third person mode (default), the character can be seen in the camera. While in third person mode on Roblox: - You may right-click and drag to rotate your camera, or use the arrow keys at the bottom right-hand corner of the screen. - When you move your mouse, your camera does not change (unless you move the mouse to the end of the screen). - When you press any of the arrow keys, the user's character will face in the corresponding arrow key's direction. - You can zoom in and out freely. **Playing in First Person** This example demonstrates how to change the character's CameraMode to first person using the LockFirstPerson value of the [CameraMode](/docs/reference/engine/enums/CameraMode.md) enum. ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer player.CameraMode = Enum.CameraMode.LockFirstPerson ``` ### Property: StarterPlayer.CharacterBreakJointsOnDeath ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Character", "capabilities": [ "Players" ] } ``` This property determines the starting value of [Humanoid.BreakJointsOnDeath](/docs/reference/engine/classes/Humanoid.md) for a player's [Player.Character](/docs/reference/engine/classes/Player.md). Note that [AvatarJointUpgrade](/docs/reference/engine/classes/StarterPlayer.md) must be enabled for this property to take effect. ### Property: StarterPlayer.CharacterJumpHeight ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Character Jump Settings", "capabilities": [ "Players" ] } ``` This property determines the starting value of [Humanoid.JumpHeight](/docs/reference/engine/classes/Humanoid.md) for a player's [Player.Character](/docs/reference/engine/classes/Player.md) in studs, with a default of `7.2`. This property is only visible in the **Properties** window If [CharacterUseJumpPower](/docs/reference/engine/classes/StarterPlayer.md) is set to `false`, as it would not be relevant otherwise. Since this property is only relevant for characters being spawned in the future, changing it will not change any existing player characters. Changes to this property will only take effect when a player respawns. ### Property: StarterPlayer.CharacterJumpPower ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Character Jump Settings", "capabilities": [ "Players" ] } ``` This property determines the starting value of [Humanoid.JumpPower](/docs/reference/engine/classes/Humanoid.md) for a player's [Player.Character](/docs/reference/engine/classes/Player.md), with a default of `50`, minimum of `0`, and maximum of `1000`. This property is only visible in the **Properties** window If [CharacterUseJumpPower](/docs/reference/engine/classes/StarterPlayer.md) is set to `true`, as it would not be relevant otherwise. Since this property is only relevant for characters being spawned in the future, changing it will not change any existing player characters. Changes to this property will only take effect when a player respawns. ### Property: StarterPlayer.CharacterMaxSlopeAngle ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Character", "capabilities": [ "Players" ] } ``` This property determines the starting value of [Humanoid.MaxSlopeAngle](/docs/reference/engine/classes/Humanoid.md) for a player's [Player.Character](/docs/reference/engine/classes/Player.md) in degrees. It defaults to `89`, so humanoids can climb pretty much any slope they want by default. Since this property is only relevant for characters being spawned in the future, changing it will not change any existing player characters. Changes to this property will only take effect when a player respawns. ### Property: StarterPlayer.CharacterUseJumpPower ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Character Jump Settings", "capabilities": [ "Players" ] } ``` This property determines the starting value of [Humanoid.UseJumpPower](/docs/reference/engine/classes/Humanoid.md) for a player's [Player.Character](/docs/reference/engine/classes/Player.md). Toggling it will change which property is visible in the **Properties** window ([CharacterJumpHeight](/docs/reference/engine/classes/StarterPlayer.md) if `false` or [CharacterJumpPower](/docs/reference/engine/classes/StarterPlayer.md) if `true`). Defaults to `true`. Since this property is only relevant for characters being spawned in the future, changing it will not change any existing player characters. Changes to this property will only take effect when a player respawns. ### Property: StarterPlayer.CharacterWalkSpeed ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Character", "capabilities": [ "Players" ] } ``` This property determines the starting value of [Humanoid.WalkSpeed](/docs/reference/engine/classes/Humanoid.md) for a player's [Player.Character](/docs/reference/engine/classes/Player.md) with a default of `16`. Since this property is only relevant for characters being spawned in the future, changing it will not change any existing player characters. Changes to this property will only take effect when a player respawns. ### Property: StarterPlayer.ClassicDeath ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Character", "capabilities": [ "Players" ] } ``` When enabled, the character uses the classic death behavior (joints break apart and parts scatter). When disabled, characters spawn with [Humanoid.BreakJointsOnDeath](/docs/reference/engine/classes/Humanoid.md) = `false`, and will ragdoll when they die. ### Property: StarterPlayer.CreateDefaultPlayerModule ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Controls", "capabilities": [ "Players" ] } ``` This property is only visible when [Workspace.PlayerScriptsUseInputActionSystem](/docs/reference/engine/classes/Workspace.md) is enabled. When set to `true` (default), the `PlayerModule` injection point occurs at [StarterPlayer](/docs/reference/engine/classes/StarterPlayer.md). When set to `false`, the default camera and control scripts will not be added to the place. ### Property: StarterPlayer.DevCameraOcclusionMode ```json { "type": "DevCameraOcclusionMode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Camera", "capabilities": [ "Players" ] } ``` Defines how the default camera scripts handle objects between the camera and the camera subject. Applies to all players as they join the experience and can't be changed for individual players. The default value is [Zoom](/docs/reference/engine/enums/DevCameraOcclusionMode.md) (0). See [DevCameraOcclusionMode](/docs/reference/engine/enums/DevCameraOcclusionMode.md) for a list of available modes. ### Property: StarterPlayer.DevComputerCameraMovementMode ```json { "type": "DevComputerCameraMovementMode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Camera", "capabilities": [ "Players" ] } ``` This property lets you overwrite the player's camera mode on a computer. If set to [UserChoice](/docs/reference/engine/enums/DevComputerCameraMovementMode.md), the player's camera movement mode will be determined by whatever they set in the experience's settings. Otherwise, the mode will be set based on this property. This property does not affect players who are not on a computer. ### Property: StarterPlayer.DevComputerMovementMode ```json { "type": "DevComputerMovementMode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Controls", "capabilities": [ "Players" ] } ``` This property lets you overwrite the player's movement mode on a computer. If set to [UserChoice](/docs/reference/engine/enums/DevComputerMovementMode.md), the player's movement mode will be determined by whatever they set in the experience's settings. Otherwise, the mode will be set based on this property. This property does not affect players who are not on a computer. ### Property: StarterPlayer.DevTouchCameraMovementMode ```json { "type": "DevTouchCameraMovementMode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Camera", "capabilities": [ "Players" ] } ``` This property lets you overwrite the player's camera mode on a touch-enabled device. If set to [UserChoice](/docs/reference/engine/enums/DevTouchCameraMovementMode.md), the player's camera movement mode will be determined by whatever they set in the experience's settings. Otherwise, the mode will be set based on this property. This property does not affect players who are not on a touch-enabled device. ### Property: StarterPlayer.DevTouchMovementMode ```json { "type": "DevTouchMovementMode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Controls", "capabilities": [ "Players" ] } ``` Lets you overwrite the player's movement mode on a touch-enabled device. If set to [UserChoice](/docs/reference/engine/enums/DevTouchMovementMode.md), the player's movement mode will be determined by whatever they set in the experience's settings. Otherwise, the mode will be set based on this property. This property does not affect players who are not on a touch-enabled device. ### Property: StarterPlayer.EnableDynamicHeads ```json { "type": "LoadDynamicHeads", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Character", "capabilities": [ "Players" ] } ``` Sets the use of dynamic heads. When true, enables the use of avatar heads with facial animation data. ### Property: StarterPlayer.EnableMouseLockOption ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Controls", "capabilities": [ "Players" ] } ``` This property determines if a player can toggle mouse lock by default. Mouselock will lock the player's cursor to the center of the screen. Moving the mouse will rotate the [Camera](/docs/reference/engine/classes/Camera.md) and [Player](/docs/reference/engine/classes/Player.md) will move relative to the current rotation of the camera. This property sets the value of [Player.DevEnableMouseLock](/docs/reference/engine/classes/Player.md). Note that shift-lock related APIs are in the process of being deprecated, so it's recommended to use [UserInputService.MouseBehavior](/docs/reference/engine/classes/UserInputService.md) instead to lock the mouse. **Enabling a Player's Mouse Lock** The example demonstrates how to enable and disabled whether a player can lock their mouse. In this example, we set the use a while true loop to toggle the state of the DevEnabledMouseLock property between _true_ and _false_ every 5 seconds. While this example has little practical use, it demos how to change the property via a `LocalScript`. ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer while true do player.DevEnableMouseLock = not player.DevEnableMouseLock task.wait(5) end ``` ### Property: StarterPlayer.HealthDisplayDistance ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Players" ] } ``` This property sets the distance in studs at which this player will see other [Humanoid](/docs/reference/engine/classes/Humanoid.md) health bars. If set to 0, the health bars will not be displayed. This property is set to 100 studs by default. To change the display distance for a player once they join the game, you can set the [Player.HealthDisplayDistance](/docs/reference/engine/classes/Player.md) property. If a [Humanoid](/docs/reference/engine/classes/Humanoid.md) health bar is visible, you can set the display type using [Humanoid.DisplayDistanceType](/docs/reference/engine/classes/Humanoid.md). **Hiding Player Health and Names** This example demonstrates how to hide other `Humanoid`'s (`Player` and NPC) health bars and names. This is done by setting the player's [Player.HealthDisplayDistance](/docs/reference/engine/classes/Player.md) and [Player.NameDisplayDistance](/docs/reference/engine/classes/Player.md) properties to 0. If you would like to display health bars and names, you set the properties to a value greater than 0. For instance, setting the properties to 100 means that the player will see other player's health and names up to 100 studs away. To modify the default values for players, you can change the values of the `StarterClass.Player.HealthDisplayDistance` and `StarterClass.Player.NameDisplayDistance` properties. ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer player.HealthDisplayDistance = 0 player.NameDisplayDistance = 0 ``` ### Property: StarterPlayer.LoadCharacterAppearance ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Character", "capabilities": [ "Players" ] } ``` This property sets whether or not the appearance of a player's character should be loaded. Setting this to `false` results in the player having no clothes (including hats), body colors, body packages or anything else related to the appearance of the player's avatar. By default, this property is set to `true`. Setting this to `true` results in the player loading the appearance corresponding to the player's [Player.CharacterAppearanceId](/docs/reference/engine/classes/Player.md). If [Player:LoadCharacterWithHumanoidDescriptionAsync()](/docs/reference/engine/classes/Player.md) is used, it can be advantageous to set [StarterPlayer.LoadCharacterAppearance](/docs/reference/engine/classes/StarterPlayer.md) to false as the player's avatar is not required as all asset IDs to equip on the character will come from the passed in [HumanoidDescription](/docs/reference/engine/classes/HumanoidDescription.md). **Disabling a Player's Appearance** This example demonstrates how to disable loading a player's character appearance. Instead, the player loads as a grey model without any hats, shirts, pants, etc. This is useful for games using custom clothing and accessories. Note that if the character has already spawned, this change will not take affect until the player respawns or the [Player:LoadCharacterAsync()](/docs/reference/engine/classes/Player.md) function is called. ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer player.CanLoadCharacterAppearance = false ``` ### Property: StarterPlayer.LoadCharacterLayeredClothing ```json { "type": "LoadCharacterLayeredClothing", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Character", "capabilities": [ "Players" ] } ``` Indicates whether characters spawning into an experience will have layered clothing accessories equipped on them (Although [Workspace.MeshPartHeadsAndAccessories](/docs/reference/engine/classes/Workspace.md) also need to be enabled in the [Workspace](/docs/reference/engine/classes/Workspace.md)). ### Property: StarterPlayer.LuaCharacterController ```json { "type": "CharacterControlMode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Controls", "capabilities": [ "Players" ] } ``` Determines which character controller implementation drives the player's character movement. Accepts a value from [CharacterControlMode](/docs/reference/engine/enums/CharacterControlMode.md): `Default`, `Legacy`, `NoCharacterController`, or `LuaCharacterController`. ### Property: StarterPlayer.NameDisplayDistance ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Players" ] } ``` Sets the distance at which this player will see other [Humanoid](/docs/reference/engine/classes/Humanoid.md) names. If set to `0`, names are hidden. This property is set to `100` studs by default. To change the display distance for a player once they join the game, you can set the [Player.NameDisplayDistance](/docs/reference/engine/classes/Player.md) property. If a [Humanoid](/docs/reference/engine/classes/Humanoid.md) name is visible, you can set the display type using [Humanoid.DisplayDistanceType](/docs/reference/engine/classes/Humanoid.md). **Hiding Player Health and Names** This example demonstrates how to hide other `Humanoid`'s (`Player` and NPC) health bars and names. This is done by setting the player's [Player.HealthDisplayDistance](/docs/reference/engine/classes/Player.md) and [Player.NameDisplayDistance](/docs/reference/engine/classes/Player.md) properties to 0. If you would like to display health bars and names, you set the properties to a value greater than 0. For instance, setting the properties to 100 means that the player will see other player's health and names up to 100 studs away. To modify the default values for players, you can change the values of the `StarterClass.Player.HealthDisplayDistance` and `StarterClass.Player.NameDisplayDistance` properties. ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer player.HealthDisplayDistance = 0 player.NameDisplayDistance = 0 ``` ### Property: StarterPlayer.UserEmotesEnabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Character", "capabilities": [ "Players" ] } ``` This property determines if user-owned emotes are loaded when loading avatars. Setting this property to `false` disables loading. Developers can set the property in Studio directly. When emote loading is disabled, the emotes UI will still work as long as developers choose to use the emotes feature by adding emotes within their game. See also [Avatar Emotes](/docs/en-us/characters/emotes.md), an article detailing how to control, customize, and play avatar emotes. ### Property: StarterPlayer.AllowCustomAnimations *(hidden)* ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "None", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Character", "capabilities": [ "Players" ] } ``` This property describes the current game's permission levels regarding custom avatar [Animations](/docs/reference/engine/classes/Animation.md) from the website. As such, this value cannot be changed from within the game. It can only be changed by changing the game's permission levels within the game's setting's page on the website. This property is not intended for use in the game. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: StarterPlayerScripts last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable summary: "A container for objects to be copied to a Player's PlayerScripts when they join a game." --- # Class: StarterPlayerScripts > A container for objects to be copied to a Player's PlayerScripts when they > join a game. ## Description [StarterPlayerScripts](/docs/reference/engine/classes/StarterPlayerScripts.md) is a container object located within the [StarterPlayer](/docs/reference/engine/classes/StarterPlayer.md) service. It can contain [LocalScripts](/docs/reference/engine/classes/LocalScript.md) and other objects to be copied to the [PlayerScripts](/docs/reference/engine/classes/PlayerScripts.md) container once when a [Player](/docs/reference/engine/classes/Player.md) joins the game. For example, if you want to create special effects on the client when certain conditions are met, you can place a [LocalScript](/docs/reference/engine/classes/LocalScript.md) within this container to do that. When an experience is run, this object will also house the default multi-platform Roblox control scripts for the camera and character. If [LocalScripts](/docs/reference/engine/classes/LocalScript.md) named `CameraScript` or `ControlScript` are placed within this container, they will **replace** the Roblox defaults for those scripts respectively. If desired, you can add empty [LocalScripts](/docs/reference/engine/classes/LocalScript.md) for each of these to disable them altogether; this is useful for experiences that do not follow the typical control paradigms of a Roblox experience. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: StartupMessageService last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated --- # Class: StartupMessageService ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Stats last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service summary: "Performance metrics for a game." --- # Class: Stats > Performance metrics for a game. ## Description [Stats](/docs/reference/engine/classes/Stats.md) is a service that provides real-time performance information about the current running game instance. Its primary purpose is to provide an end point to measure where resources are being consumed, as well as how much memory is being consumed overall. The service also stores a tree of [StatsItem](/docs/reference/engine/classes/StatsItem.md) objects which can have their values read by plugins. ## Properties ### Property: Stats.ContactsCount ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` This property describes how many parts are currently in contact with each other, such that one of the two parts are being physically simulated, and thus can be recognized by the [BasePart:GetTouchingParts()](/docs/reference/engine/classes/BasePart.md) method. ### Property: Stats.DataReceiveKbps ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` In a networked game, this property describes roughly how many kilobytes of data are being received by the current instance, per second. If from the server's perspective, this represents the total amount of data being received from the clients connected to the server. If from a client's perspective, this represents the total amount of data being received from the server. ### Property: Stats.DataSendKbps ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` In a networked game, this property describes roughly how many kilobytes of data are being sent by the current instance, per second. If from the server's perspective, this represents the total amount of data being sent to the clients connected to the server. If from a client's perspective, this represents the total amount of data being sent to the server. ### Property: Stats.FrameTime ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` This property is only available in client scripts and is a measurement of how long it took to render the most-recent frame in seconds. Divide `1` by this value to calculate an FPS value for the frame time. High frame times are indicative of performance problems on the device. Consider using the [MicroProfiler](/docs/en-us/studio/microprofiler.md) to troubleshoot. ### Property: Stats.HeartbeatTime ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` This property is a measurement of the total amount of time it takes for the server to update its task scheduler jobs in seconds. If this value is high, examine [server compute](/docs/en-us/performance-optimization/identifying.md#server-compute). ### Property: Stats.InstanceCount ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` `InstanceCount` is a read-only measurement of how many [Instance](/docs/reference/engine/classes/Instance.md) are currently in memory. This includes the [DataModel](/docs/reference/engine/classes/DataModel.md), its descendants, as well as any object created with [Instance.new()](/docs/reference/engine/datatypes/Instance.md) which is still present in memory. ### Property: Stats.MemoryTrackingEnabled ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` If `MemoryTrackingEnabled` returns `false`, any API that returns category-based memory usage such as [Stats:GetMemoryUsageMbForTag()](/docs/reference/engine/classes/Stats.md) or [Stats:GetMemoryUsageMbAllCategories()](/docs/reference/engine/classes/Stats.md) will return `0` and emit a warning. Therefore, usage of category-based memory usage API should be conditional on `MemoryTrackingEnabled` returning `true`. The value of `MemoryTrackingEnabled` will not change from one experience to the next; it will only potentially change after restarting the Roblox client application. ### Property: Stats.MovingPrimitivesCount ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` A measurement of how many physically simulated components are currently moving in the game world. ### Property: Stats.PhysicsReceiveKbps ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` PhysicsReceiveKbps is a measurement of roughly how many kilobytes of physics data are being received by the current instance, per second.If from the server's perspective, this represents the total amount of physics data being received from the clients connected to the server.If from a client's perspective, this represents the total amount of physics data being received from the server. ### Property: Stats.PhysicsSendKbps ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` `PhysicsSendKbps` describes roughly how many kilobytes of physics data are being sent by the current instance, per second. If from the server's perspective, this represents the total amount of physics data being sent to the clients connected to the server. If from a client's perspective, this represents the total amount of physics data being sent to the server. ### Property: Stats.PhysicsStepTime ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` This property is a measurement of how long it takes for the physics engine to update its current state. If this value is high, it means the game instance is under stress from the physics simulations taking place. ### Property: Stats.PrimitivesCount ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` A measurement of how many physically simulated components currently exist in the game world. ### Property: Stats.RenderCPUFrameTime ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` This property is a measurement of how long it takes for the CPU to process all of its rendering tasks for a frame. ### Property: Stats.RenderGPUFrameTime ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` This property is a measurement of how long it takes for the GPU to process all of its tasks required to render a frame. ### Property: Stats.SceneDrawcallCount ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` This property is a measurement of the number of draw calls made by the game's current scene. A draw call is a single rendering operation, such as drawing a mesh. A high draw call count could mean a scene is too complex or unoptimized, which can lead to performance issues. ### Property: Stats.SceneTriangleCount ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` This property is a measurement of the number of triangles rendered by the game's current scene. A count of triangles rendered is useful when trying to estimate the complexity and performance of a scene. ### Property: Stats.ShadowsDrawcallCount ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` This property is a measurement of the number of draw calls being made for shadows by the game's current scene. A high count means more shadows are being created by the amount of rendered objects in a scene. ### Property: Stats.ShadowsTriangleCount ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` This property is a measurement of the number of triangles rendered as shadows in the game's current scene. A high count means there are a lot of triangles used to cast shadows, which can hinder performance. ### Property: Stats.UI2DDrawcallCount ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` This property is a measurement of the number of 2D draw calls made for UI elements in the game's current scene. A high count can mean there are a lot of 2D UI elements being used. ### Property: Stats.UI2DTriangleCount ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` This property is a measurement of the number of triangles that are being rendered for 2D UI elements in the game's current scene. A high count can mean there are many or complex 2D UI elements used, which can contribute to performance loss in regards to rendering. ### Property: Stats.UI3DDrawcallCount ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` This property is a measurement of the number of 3D draw calls made for UI elements in the game's current scene. A high count could indicate a high amount of 3D objects being used within UI, potentially hurting performance; however, it is very unlikely you would see a significant count since UI elements are typically 2D. ### Property: Stats.UI3DTriangleCount ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` This property is a measurement of the number of triangles being rendered for 3D UI elements in the game's current scene; however, it is very unlikely you would see a significant count since UI elements are typically 2D. ### Property: Stats.HeartbeatTimeMs ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data" } ``` The `HeartbeatTimeMs` property is a measurement of the total amount of time it takes long it takes for Roblox to update all of its task scheduler jobs, in milliseconds. If this value is high, then it means one of the tasks are hogging up a lot of resources. ### Property: Stats.PhysicsStepTimeMs ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data" } ``` A measurement of how long it takes for the physics engine to update its current state, in milliseconds. If this value is high, then it means the game instance is under stress from the physics simulations taking place. ## Methods ### Method: Stats:GetHarmonyQualityLevel **Signature:** `Stats:GetHarmonyQualityLevel(): int` *Security: None · Thread Safety: Unsafe · Capabilities: InternalTest* **Returns:** `int` ### Method: Stats:GetMemoryCategoryNames **Signature:** `Stats:GetMemoryCategoryNames(): Array` *Security: None · Thread Safety: Unsafe · Capabilities: InternalTest* **Returns:** `Array` ### Method: Stats:GetMemoryUsageMbAllCategories **Signature:** `Stats:GetMemoryUsageMbAllCategories(): Array` Returns the number of megabytes that are being consumed by all available categories. If [MemoryTrackingEnabled](/docs/reference/engine/classes/Stats.md) is `false`, calling `GetMemoryUsageMbAllCategories()` will return an empty array and emit a warning to the console. *Security: None · Thread Safety: Unsafe · Capabilities: InternalTest* **Returns:** `Array` ### Method: Stats:GetMemoryUsageMbForTag **Signature:** `Stats:GetMemoryUsageMbForTag(tag: DeveloperMemoryTag): float` Returns the number of megabytes that are being consumed in the specified [DeveloperMemoryTag](/docs/reference/engine/enums/DeveloperMemoryTag.md) category. If [MemoryTrackingEnabled](/docs/reference/engine/classes/Stats.md) is `false`, calling `GetMemoryUsageMbForTag()` will return `0` and emit a warning to the console. *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `tag` | `DeveloperMemoryTag` | | | **Returns:** `float` ### Method: Stats:GetTotalMemoryUsageMb **Signature:** `Stats:GetTotalMemoryUsageMb(): float` Returns the total amount of memory being consumed by the current game session, in megabytes. This method gets memory usage from the operating system, which may exclude memory that has been paged out to disk. As such, the return value tends to differ significantly from the sum of usage for all [DeveloperMemoryTags](/docs/reference/engine/enums/DeveloperMemoryTag.md). The return value should be very similar to memory usage for Roblox in the Windows Task Manager or macOS Activity Monitor. *Security: None · Thread Safety: Unsafe* **Returns:** `float` ### Method: Stats:ResetHarmonyMemoryTarget **Signature:** `Stats:ResetHarmonyMemoryTarget(): ()` *Security: None · Thread Safety: Unsafe · Capabilities: InternalTest* **Returns:** `()` ### Method: Stats:SetHarmonyMemoryTarget **Signature:** `Stats:SetHarmonyMemoryTarget(targetMB: int): ()` *Security: None · Thread Safety: Unsafe · Capabilities: InternalTest* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `targetMB` | `int` | | | **Returns:** `()` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: StatsItem last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable summary: "A single performance metric." --- # Class: StatsItem > A single performance metric. ## Description A StatsItem is an internal measurement item that is created by the engine to benchmark many of the backend components of Roblox. It cannot be created using [Instance.new()](/docs/reference/engine/datatypes/Instance.md), but its value can be read by plugins. They can be found stored inside of the [Stats](/docs/reference/engine/classes/Stats.md) service. ## Properties ### Property: StatsItem.DisplayName *(hidden)* ```json { "type": "string", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` ## Methods ### Method: StatsItem:GetValue **Signature:** `StatsItem:GetValue(): double` Returns the StatsItem's value. *Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `double` ### Method: StatsItem:GetValueString **Signature:** `StatsItem:GetValueString(): string` Returns the StatsItem's value as a formatted string. *Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `string` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Status last_updated: 2026-06-29T19:34:08Z inherits: - Model - PVInstance - Instance - Object type: class memory_category: BaseParts tags: - NotCreatable - Deprecated summary: "An unfinished object which offers no functionality to developers." --- # Class: Status > An unfinished object which offers no functionality to developers. ## Description Used as a storage for custom Humanoid statuses. This object would've been officially used in the RbxStatus library, but remains unfinished. ## Inherited Members ### From [Model](/docs/reference/engine/classes/Model.md) - **Property `LevelOfDetail`** (`ModelLevelOfDetail`): Sets the level of detail on the model for experiences with instance - **Property `ModelStreamingMode`** (`ModelStreamingMode`): Controls the model streaming behavior on Models when - **Property `PrimaryPart`** (`BasePart`): The primary part of the Model, or `nil` if not explicitly set. - **Property `Scale`** (`float`): Editor-only property used to scale the model around its pivot. Setting - **Property `WorldPivot`** (`CFrame`): Determines where the pivot of a Model which does **not** have a - **Method `AddPersistentPlayer(playerInstance?: Player): ()`**: Sets this model to be persistent for the specified player. - **Method `BreakJoints(): ()`**: Breaks connections between `BaseParts`, including surface connections with *(deprecated)* - **Method `breakJoints(): ()`**: *(deprecated)* - **Method `GetBoundingBox(): Tuple`**: Returns a description of a volume that contains all parts of a Model. - **Method `GetExtentsSize(): Vector3`**: Returns the size of the smallest bounding box that contains all of the - **Method `GetModelCFrame(): CFrame`**: This value historically returned the CFrame of a central position in the *(deprecated)* - **Method `GetModelSize(): Vector3`**: Returns the Vector3 size of the Model. *(deprecated)* - **Method `GetPersistentPlayers(): List`**: Returns all the Player objects that this model object is - **Method `GetPrimaryPartCFrame(): CFrame`**: Returns the CFrame of the model's Model.PrimaryPart. *(deprecated)* - **Method `GetScale(): float`**: Returns the canonical scale of the model, which defaults to 1 for newly - **Method `MakeJoints(): ()`**: Goes through all BaseParts in the Model. If any *(deprecated)* - **Method `makeJoints(): ()`**: *(deprecated)* - **Method `move(location: Vector3): ()`**: *(deprecated)* - **Method `MoveTo(position: Vector3): ()`**: Moves the PrimaryPart to the given position. If - **Method `moveTo(location: Vector3): ()`**: *(deprecated)* - **Method `RemovePersistentPlayer(playerInstance?: Player): ()`**: Makes this model no longer persistent for the specified player. - **Method `ResetOrientationToIdentity(): ()`**: Resets the rotation of the model's parts to the previously set identity *(deprecated)* - **Method `ScaleTo(newScaleFactor: float): ()`**: Sets the scale factor of the model, adjusting the sizing and location of - **Method `SetIdentityOrientation(): ()`**: Sets the identity rotation of the given model, allowing you to reset the *(deprecated)* - **Method `SetPrimaryPartCFrame(cframe: CFrame): ()`**: Sets the BasePart.CFrame of the model's Model.PrimaryPart. *(deprecated)* - **Method `TranslateBy(delta: Vector3): ()`**: Shifts a Model by the given Vector3 offset, preserving ### From [PVInstance](/docs/reference/engine/classes/PVInstance.md) - **Property `Origin`** (`CFrame`): - **Property `Pivot Offset`** (`CFrame`): - **Method `GetPivot(): CFrame`**: Gets the pivot of a PVInstance. - **Method `PivotTo(targetCFrame: CFrame): ()`**: Transforms the PVInstance along with all of its descendant ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: StopWatchReporter last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated --- # Class: StopWatchReporter ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: StringValue last_updated: 2026-06-29T19:34:08Z inherits: - ValueBase - Instance - Object type: class memory_category: Instances summary: "A container object for a single string." --- # Class: StringValue > A container object for a single string. ## Description Stores a single [Luau string](https://luau.org/syntax#string-literals). The length of the string can't be more than 200,000 characters; anything longer causes a `String too long` error. If the string is too long to be displayed in the **Value** field within the **Properties** window, it shows partial string contents and an ellipsis (...). Like all [ValueBase](/docs/reference/engine/classes/ValueBase.md) objects, this single value is stored in the [Value](/docs/reference/engine/classes/StringValue.md) property. The `Changed` event fires with the new value being stored in the object, instead of a string representing the property being changed. ## Code Samples **Changed Event** This sample demonstrates the subtleties of the `Changed` event on normal objects and `Value` objects. ```lua -- Demonstrate the Changed event by creating a Part local part = Instance.new("Part") part.Changed:Connect(print) -- This fires Changed with "Transparency" part.Transparency = 0.5 -- Similarly, this fires Changed with "Number" part.Name = "SomePart" -- Since changing BrickColor will also change other -- properties at the same time, this line fires Changed -- with "BrickColor", "Color3" and "Color3uint16". part.BrickColor = BrickColor.Red() -- A NumberValue holds a double-precision floating-point number local vNumber = Instance.new("NumberValue") vNumber.Changed:Connect(print) -- This fires Changed with 123.456 (not "Value") vNumber.Value = 123.456 -- This does not fire Changed vNumber.Name = "SomeNumber" -- A StringValue stores one string local vString = Instance.new("StringValue") vString.Changed:Connect(print) -- This fires Changed with "Hello" (not "Value") vString.Value = "Hello" ``` ## Properties ### Property: StringValue.Value ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "simulationAccess": true } ``` The stored string. ## Events ### Event: StringValue.Changed **Signature:** `StringValue.Changed(value: string)` Fires whenever the [IntValue.Value](/docs/reference/engine/classes/IntValue.md) changes. It runs with the new value being stored in the argument object, instead of a string representing the property being changed. Listening for the `Changed` signal can be useful in games that use `StringValues` to track values like NPC names or item descriptions. *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `value` | `string` | The new value after the change. | **How to Use StringValue.Changed** The below example, assuming all referenced objects existed, would print the StringValue's new value each time it changed. In the example below it would print _"Hello world!"_. ```lua local value = Instance.new("StringValue") value.Parent = workspace value.Changed:Connect(function(NewValue) print(NewValue) end) value.Value = "Hello world!" ``` **Expected output:** "Hello world!" ### Event: StringValue.changed **Signature:** `StringValue.changed(value: string)` > **Deprecated:** This deprecated event is a variant of [StringValue.Changed](/docs/reference/engine/classes/StringValue.md) which should be used instead. *Security: None* **Parameters:** | Name | Type | Description | |------|------|-------------| | `value` | `string` | | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Studio last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated --- # Class: Studio ## Description The Studio object is a settings object that is exclusive to Roblox Studio. It can be found in Roblox Studio's settings under the Studio tab. ## Properties ### Property: Studio."function" Color ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Script Editor Colors" } ``` ### Property: Studio."local" Color ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Script Editor Colors" } ``` ### Property: Studio."nil" Color ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Script Editor Colors" } ``` ### Property: Studio."self" Color ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Script Editor Colors" } ``` ### Property: Studio."TODO" Color ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Script Editor Colors" } ``` ### Property: Studio.ActionOnAutoResumeSync ```json { "type": "ActionOnAutoResumeSync", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Directories" } ``` ### Property: Studio.ActionOnStopSync ```json { "type": "ActionOnStopSync", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Directories" } ``` ### Property: Studio.Active Color ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tools" } ``` ### Property: Studio.Active Hover Over Color ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tools" } ``` ### Property: Studio.Always Save Script Changes ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "General" } ``` If set to true, Roblox Studio will attempt to transfer script changes that were made during a Play Solo session to the opened place. ### Property: Studio.Animate Hover Over ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tools" } ``` If set to true, the hover selection box that is shown when mousing over selectable objects in the [Workspace](/docs/reference/engine/classes/Workspace.md) will flash between `Hover Over Color` and `Select Color` based on the `Hover Animate Speed`. ### Property: Studio.Auto Clean Empty Line ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Script Editor" } ``` ### Property: Studio.Auto Closing Brackets ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Script Editor" } ``` ### Property: Studio.Auto Closing Quotes ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Script Editor" } ``` ### Property: Studio.Auto Delete Closing Brackets and Quotes ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Script Editor" } ``` ### Property: Studio.Auto Indent Rule ```json { "type": "AutoIndentRule", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Script Editor" } ``` ### Property: Studio.Auto-Recovery Enabled ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Auto-Recovery" } ``` ### Property: Studio.Auto-Recovery Interval (Minutes) ```json { "type": "int", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Auto-Recovery" } ``` ### Property: Studio.AutocompleteAcceptanceBehavior ```json { "type": "CompletionAcceptanceBehavior", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Script Editor" } ``` ### Property: Studio.Automatically trigger AI Code Completion ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Script Editor" } ``` ### Property: Studio.AutoResumeSyncOnPlaceOpen ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Directories" } ``` ### Property: Studio.AutoUpdateEnabled ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Updates" } ``` ### Property: Studio.Background Color ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Script Editor Colors" } ``` Specifies the background color of Roblox Studio's script editor. ### Property: Studio.Basic Objects Display Mode ```json { "type": "ListDisplayMode", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "General" } ``` Sets the scrolling mode of the `Advanced Objects` tab in Roblox Studio. ### Property: Studio.Bool Color ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Script Editor Colors" } ``` ### Property: Studio.Bracket Color ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Script Editor Colors" } ``` ### Property: Studio.Built-in Function Color ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Script Editor Colors" } ``` Sets the color of built-in functions and keywords in the script editor. ### Property: Studio.Camera Mouse Wheel Speed ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Camera" } ``` Sets how many studs the camera will move forward or backwards when using the mouse wheel. ### Property: Studio.Camera Pan Speed ```json { "type": "float", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Camera" } ``` ### Property: Studio.Camera Shift Speed ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Camera" } ``` Sets the speed in studs/sec that the camera moves while holding down Shift with the movement keys. ### Property: Studio.Camera Speed ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Camera" } ``` Sets the speed in studs/sec that the camera moves when movement keys are pressed. ### Property: Studio.Camera Speed Adjust Binding ```json { "type": "CameraSpeedAdjustBinding", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Camera" } ``` ### Property: Studio.Camera Zoom to Mouse Position ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Camera" } ``` ### Property: Studio.CameraAdaptiveSpeed ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Camera" } ``` ### Property: Studio.CameraNavigationModel ```json { "type": "CameraNavigationModel", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Camera" } ``` ### Property: Studio.CameraOrbitSensitivity ```json { "type": "float", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Camera" } ``` ### Property: Studio.CameraPanSensitivity ```json { "type": "float", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Camera" } ``` ### Property: Studio.CameraShiftFactor ```json { "type": "float", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Camera" } ``` ### Property: Studio.CameraTweenFocus ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Camera" } ``` ### Property: Studio.CameraZoomSpeed ```json { "type": "float", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Camera" } ``` ### Property: Studio.Clear Output On Start ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Output" } ``` If set to true, the output will be automatically cleared when game sessions are switched. ### Property: Studio.CommandBarEnterExec ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Script Editor" } ``` ### Property: Studio.CommandBarFont ```json { "type": "QFont", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Script Editor" } ``` ### Property: Studio.CommandBarLocalState ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Lua Debugger" } ``` ### Property: Studio.Comment Color ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Script Editor Colors" } ``` Specifies the color of comments in Roblox Studio's script editor. ### Property: Studio.Current Line Highlight Color ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Script Editor Colors" } ``` ### Property: Studio.Debugger Current Line Color ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Script Editor Colors" } ``` ### Property: Studio.Debugger Error Line Color ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Script Editor Colors" } ``` ### Property: Studio.DefaultInstancesDir ```json { "type": "QDir", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Directories" } ``` ### Property: Studio.DefaultScriptSyncFileType ```json { "type": "DefaultScriptSyncFileType", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Directories" } ``` ### Property: Studio.DeprecatedObjectsShown ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Browsing" } ``` If set to true, deprecated objects will be shown in the Advanced Objects window, as well as the Object Browser. ### Property: Studio.DisplayLanguage ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "General" } ``` ### Property: Studio.DraggerActiveColor ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: Studio.DraggerLengthFactor ```json { "type": "float", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: Studio.DraggerMajorGridIncrement ```json { "type": "int", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: Studio.DraggerMaxSoftSnaps ```json { "type": "int", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: Studio.DraggerPassiveColor ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: Studio.DraggerScaleFactor ```json { "type": "float", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: Studio.DraggerShowAxisTicks ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: Studio.DraggerShowHoverRuler ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: Studio.DraggerShowMeasurement ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: Studio.DraggerShowNegativeAxes ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: Studio.DraggerShowPlanes ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: Studio.DraggerShowTargetSnap ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: Studio.DraggerShowTrackball ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: Studio.DraggerShowWhileDragging ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: Studio.DraggerSoftSnapMarginFactor ```json { "type": "float", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: Studio.DraggerSummonMarginFactor ```json { "type": "float", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: Studio.DraggerTiltRotateDuration ```json { "type": "float", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: Studio.Enable Autocomplete ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Script Editor" } ``` When set to true, the script editor and command bar will show an autocomplete menu while writing. ### Property: Studio.Enable Autocomplete Doc View ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Script Editor" } ``` ### Property: Studio.Enable CoreScript Debugger ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Advanced" } ``` ### Property: Studio.Enable Http Sandboxing ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Advanced" } ``` ### Property: Studio.Enable Internal Beta Features ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Advanced" } ``` ### Property: Studio.Enable Internal Features ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Advanced" } ``` ### Property: Studio.Enable Script Analysis ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Script Editor" } ``` ### Property: Studio.Enable Scrollbar Markers ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Script Editor" } ``` ### Property: Studio.Enable Signature Help ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Script Editor" } ``` ### Property: Studio.Enable Signature Help Doc View ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Script Editor" } ``` ### Property: Studio.Enable Temporary Tabs ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Script Editor" } ``` ### Property: Studio.Enable Temporary Tabs In Explorer ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Script Editor" } ``` ### Property: Studio.Enable Type Hover ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Script Editor" } ``` ### Property: Studio.EnableCodeAssist ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Script Editor" } ``` ### Property: Studio.EnableFindOnType ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: Studio.EnableIndentationRulers ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Script Editor" } ``` ### Property: Studio.EnableSelectionTooltips ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Script Editor" } ``` ### Property: Studio.EnableStudioStreaming ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Streaming" } ``` ### Property: Studio.Error Color ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Script Editor Colors" } ``` Specifies the color of the wavy underline shown when malformed code is detected in the script editor. ### Property: Studio.ExternalEditorMode ```json { "type": "ExternalEditorMode", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Directories" } ``` ### Property: Studio.ExternalEditorSelection ```json { "type": "QDir", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Directories" } ``` ### Property: Studio.Find Selection Background Color ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Script Editor Colors" } ``` Sets the highlight color of matches in the script editor's Find Selection operation (Ctrl+F). ### Property: Studio.Font ```json { "type": "QFont", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Script Editor" } ``` Specifies the font used in the script editor. ### Property: Studio.Format On Paste ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Script Editor" } ``` ### Property: Studio.Format On Type ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Script Editor" } ``` ### Property: Studio.Function Name Color ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Script Editor Colors" } ``` ### Property: Studio.Highlight Current Line ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Script Editor" } ``` ### Property: Studio.Highlight Occurances ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Script Editor" } ``` ### Property: Studio.HintColor ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Script Editor Colors" } ``` ### Property: Studio.Hover Animate Speed ```json { "type": "HoverAnimateSpeed", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tools" } ``` Specifies how frequently the hover animation flashes when the mouse is hovering over a selectable object in the [Workspace](/docs/reference/engine/classes/Workspace.md). ### Property: Studio.Hover Box Thickness ```json { "type": "float", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tools" } ``` ### Property: Studio.Hover Line Thickness ```json { "type": "int", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tools" } ``` ### Property: Studio.Hover Over Color ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tools" } ``` Specifies the color that the hover selection box uses. ### Property: Studio.IconOverrideDir ```json { "type": "QDir", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Directories" } ``` ### Property: Studio.Indent Using Spaces ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Script Editor" } ``` ### Property: Studio.IndentationRulerColor ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Script Editor Colors" } ``` ### Property: Studio.InformationColor ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Script Editor Colors" } ``` ### Property: Studio.Keyword Color ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Script Editor Colors" } ``` Sets the text color of built-in Luau keywords. ### Property: Studio.LargeFileLineCountThreshold ```json { "type": "int", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Script Editor" } ``` ### Property: Studio.LargeFileThreshold ```json { "type": "int", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Script Editor" } ``` ### Property: Studio.Line Thickness ```json { "type": "float", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Primary Part" } ``` Sets the thickness of the [Model.PrimaryPart](/docs/reference/engine/classes/Model.md) selection adornee. This value is constrained between 0 and 0.05. ### Property: Studio.LoadAllBuiltinPluginsInRunModes ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: Studio.LoadInternalPlugins ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: Studio.LoadUserPluginsInRunModes ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: Studio.LocalAssetsFolder ```json { "type": "QDir", "access": "ReadOnly", "security": { "read": "None", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Directories" } ``` ### Property: Studio.LuaDebuggerEnabled ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Lua Debugger" } ``` Specifies whether or not the Lua Debugger feature is enabled. ### Property: Studio.Luau Keyword Color ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Script Editor Colors" } ``` ### Property: Studio.Main Volume ```json { "type": "float", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Audio" } ``` ### Property: Studio.Matching Word Background Color ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Script Editor Colors" } ``` Sets the highlight color of double-clicked variables in the script editor. ### Property: Studio.MaxFindReplaceAllResults ```json { "type": "int", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: Studio.Maximum Output Lines ```json { "type": "int", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Output" } ``` The maximum number of lines that can be displayed in the output. ### Property: Studio.Menu Item Background Color ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Script Editor Colors" } ``` ### Property: Studio.Method Color ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Script Editor Colors" } ``` ### Property: Studio.Number Color ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Script Editor Colors" } ``` Specifies the color of numbers in Roblox Studio's script editor. ### Property: Studio.Only Play Audio from Window in Focus ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Audio" } ``` If set to true, audio being played will only be heard if the game window is being focused on. ### Property: Studio.Operator Color ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Script Editor Colors" } ``` Sets the text color of operator characters in the script editor. ### Property: Studio.Output Font ```json { "type": "QFont", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Output" } ``` Specifies the font used by the output. ### Property: Studio.Output Layout Mode ```json { "type": "OutputLayoutMode", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Output" } ``` Sets the layout mode of the output. ### Property: Studio.PermissionLevelShown ```json { "type": "PermissionLevelShown", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Browsing" } ``` Sets the highest permission level that APIs have to have in order to be shown in the Object Browser. See [PermissionLevelShown](/docs/reference/engine/enums/PermissionLevelShown.md) for more info. ### Property: Studio.Physical Draggers Select Scope By Default ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tools" } ``` ### Property: Studio.Pivot Snap To Geometry Color ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tools" } ``` ### Property: Studio.PluginDebuggingEnabled ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Lua Debugger" } ``` ### Property: Studio.PluginsDir ```json { "type": "QDir", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Directories" } ``` The directory where local plugins are stored. ### Property: Studio.PreferredTextSize ```json { "type": "PreferredTextSize", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "General" } ``` ### Property: Studio.Primary Text Color ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Script Editor Colors" } ``` ### Property: Studio.Property Color ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Script Editor Colors" } ``` ### Property: Studio.ReloadBuiltinPluginsOnChange ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Directories" } ``` ### Property: Studio.ReloadLocalPluginsOnChange ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Directories" } ``` ### Property: Studio.Respect Studio shortcuts when game has focus ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Advanced" } ``` When set to true, Roblox Studio shortcuts will take priority over inputs being captured in the game window. ### Property: Studio.Ruler Color ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Script Editor Colors" } ``` ### Property: Studio.Rulers ```json { "type": "string", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Script Editor" } ``` ### Property: Studio.RuntimeUndoBehavior ```json { "type": "RuntimeUndoBehavior", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Undo" } ``` ### Property: Studio.Script Editor Color Preset ```json { "type": "StudioScriptEditorColorPresets", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Script Editor Colors" } ``` ### Property: Studio.Script Editor Scrollbar Background Color ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Script Editor Colors" } ``` ### Property: Studio.Script Editor Scrollbar Handle Color ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Script Editor Colors" } ``` ### Property: Studio.ScriptTimeoutLength ```json { "type": "int", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Lua Debugger" } ``` The time (in seconds) a script can wait to be resumed before timing out. ### Property: Studio.Scroll Past Last Line ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Script Editor" } ``` ### Property: Studio.Secondary Text Color ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Script Editor Colors" } ``` ### Property: Studio.Select Color ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tools" } ``` The color of the selection box used with object selections in the [Workspace](/docs/reference/engine/classes/Workspace.md) ### Property: Studio.Select/Hover Color ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Primary Part" } ``` Sets the color of the [Model.PrimaryPart](/docs/reference/engine/classes/Model.md) selection box. ### Property: Studio.Selected Menu Item Background Color ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Script Editor Colors" } ``` ### Property: Studio.Selected Text Color ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Script Editor Colors" } ``` ### Property: Studio.Selection Background Color ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Script Editor Colors" } ``` Sets the background color of selected text in the script editor. ### Property: Studio.Selection Box Thickness ```json { "type": "float", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tools" } ``` ### Property: Studio.Selection Color ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Script Editor Colors" } ``` Sets the text color of selected text in the script editor. ### Property: Studio.Selection Line Thickness ```json { "type": "int", "access": "ReadOnly", "security": { "read": "RobloxEngineSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tools" } ``` ### Property: Studio.Set Pivot of Imported Parts ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Advanced" } ``` ### Property: Studio.Show Core GUI in Explorer while Playing ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Explorer" } ``` If set to true, the [CoreGui](/docs/reference/engine/classes/CoreGui.md) will be visible in the Explorer while the game is running. ### Property: Studio.Show Diagnostics Bar ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Advanced" } ``` If set to true, basic diagnostic information is shown in the bottom right. ### Property: Studio.Show FileSyncService ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Explorer" } ``` ### Property: Studio.Show Hidden Objects in Explorer ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Explorer" } ``` ### Property: Studio.Show Hover Over ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tools" } ``` If set to true, hovering over an object in the [Workspace](/docs/reference/engine/classes/Workspace.md) will show a selection box. ### Property: Studio.Show Light Guides ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Visualization" } ``` ### Property: Studio.Show Navigation Labels ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Visualization" } ``` ### Property: Studio.Show Navigation Mesh ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Visualization" } ``` When set to true, the navigation mesh used by the [PathfindingService](/docs/reference/engine/classes/PathfindingService.md) will be visualized. ### Property: Studio.Show Pathfinding Links ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Visualization" } ``` ### Property: Studio.Show Plugin GUI Service in Explorer ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Explorer" } ``` When set to true, the [PluginGuiService](/docs/reference/engine/classes/PluginGuiService.md) will be shown in Roblox Studio's explorer. ### Property: Studio.Show plus button on hover in Explorer ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Explorer" } ``` ### Property: Studio.Show Singly Selected Attachment Parent Frame ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Visualization" } ``` ### Property: Studio.Show Whitespace ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Script Editor" } ``` ### Property: Studio.ShowCorePackagesInExplorer ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Explorer" } ``` ### Property: Studio.Skip Closing Brackets and Quotes ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Script Editor" } ``` ### Property: Studio.String Color ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Script Editor Colors" } ``` Specifies the color of strings in the script editor. ### Property: Studio.Tab Width ```json { "type": "int", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Script Editor" } ``` Specifies how many spaces are used to represent a tab in the script editor. ### Property: Studio.Text Color ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Script Editor Colors" } ``` Specifies the color of normal text in the script editor. ### Property: Studio.Text Wrapping ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Script Editor" } ``` If set to true, text in the script editor will be wrapped. ### Property: Studio.Theme ```json { "type": "Instance", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "General" } ``` The Theme property is used to get/set the current [StudioTheme](/docs/reference/engine/classes/StudioTheme.md) used by [Studio](/docs/reference/engine/classes/Studio.md). This is intended for use within [Plugins](/docs/reference/engine/classes/Plugin.md), but will also execute in the Command Line. You can access the function via: ```lua settings().Studio.Theme ``` For instance, if you would like to print the current Studio theme: ```lua print("The current Studio theme is:", settings().Studio.Theme) ``` ### Property: Studio.TypeColor ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Script Editor Colors" } ``` ### Property: Studio.Use Bounding Box Move Handles ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Tools" } ``` ### Property: Studio.UseDefaultExternalEditor ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Directories" } ``` ### Property: Studio.VAxisColor ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: Studio.Warning Color ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Script Editor Colors" } ``` Specifies the color of the wavy underline shown when the script analyzer picks up a problem that should be addressed in the script editor. ### Property: Studio.Whitespace Color ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Script Editor Colors" } ``` ### Property: Studio.XAxisColor ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: Studio.YAxisColor ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: Studio.ZAxisColor ```json { "type": "Color3", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: Studio.LuaDebuggerEnabledAtStartup *(hidden)* ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Lua Debugger" } ``` ### Property: Studio.UI Theme *(hidden)* ```json { "type": "UITheme", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "General" } ``` Specifies the color scheme of Roblox Studio. ## Methods ### Method: Studio:GetAvailableThemes **Signature:** `Studio:GetAvailableThemes(): Array` The **GetAvailableThemes()** function returns a list of [StudioThemes](/docs/reference/engine/classes/StudioTheme.md) available in [Studio](/docs/reference/engine/classes/Studio.md). You can access the function via: ``` settings().Studio:GetAvailableThemes() ``` *Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `Array` — A list of themes available in Studio. **Output Studio Themes** The code below retrieves an array of all available themes and then iterates through them. ```lua local themes = settings().Studio:GetAvailableThemes() for _, theme in pairs(themes) do print(theme) end ``` ## Events ### Event: Studio.ThemeChanged **Signature:** `Studio.ThemeChanged()` The **ThemeChanged** event fires when Studio's [Theme](/docs/reference/engine/classes/Studio.md) changes. The best use of this event is to get the colors from the theme that changed and update your plugin's UI accordingly. *Security: PluginSecurity* ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: StudioCaptureService last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated --- # Class: StudioCaptureService ## Methods ### Method: StudioCaptureService:CanCaptureScreenshot **Signature:** `StudioCaptureService:CanCaptureScreenshot(): boolean` *Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `boolean` ### Method: StudioCaptureService:CaptureScreenshot **Signature:** `StudioCaptureService:CaptureScreenshot(screenshotOptions: Dictionary): StudioScreenshotCapture` *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `screenshotOptions` | `Dictionary` | | | **Returns:** `StudioScreenshotCapture` ### Method: StudioCaptureService:RequestScreenshotPermissionAsync **Signature:** `StudioCaptureService:RequestScreenshotPermissionAsync(): boolean` *Yields · Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `boolean` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: StudioDeviceSimulatorService last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated summary: "Service allowing you to control Studio's Device Simulator." --- # Class: StudioDeviceSimulatorService > Service allowing you to control Studio's Device Simulator. ## Description Provides programmatic control over Studio's Device Simulator. Use this service to switch between device presets, override resolution and pixel density, control orientation and scaling, and manage custom device profiles from a plugin or from an external tool connected through the MCP server. All methods are asynchronous and yield the calling coroutine. The service is available in **Edit mode** and **Play Client**. Methods that modify the active simulation state are blocked in **PlayServer** mode. ##### Limitations - All methods are asynchronous and yield the calling coroutine. - Methods that modify the active simulation state (`SetDeviceAsync`, `StopSimulationAsync`, `SetOrientationAsync`, `SetResolutionAsync`, `SetPixelDensityAsync`, `SetScalingModeAsync`) error in PlayServer mode. They work in Edit mode and Play Client. - `UpdateDeviceAsync` and `RemoveDeviceAsync` error when called on built-in presets. - Resolution, DPI, and scaling mode methods require an active device and error when `GetDeviceAsync()` returns "default". - Resolution and DPI overrides are session-level. They are not persisted and are cleared on `SetDeviceAsync`. - Built-in presets are immutable. ## Methods ### Method: StudioDeviceSimulatorService:CreateDeviceAsync **Signature:** `StudioDeviceSimulatorService:CreateDeviceAsync(config: Dictionary): string` Creates a custom device and returns its newly assigned ID. Custom devices are persisted to disk and appear in the Device Simulator UI. Errors if any required field is missing or out of range. *Yields · Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `config` | `Dictionary` | | | **Returns:** `string` — string **Create Device example** Example of CreateDeviceAsync method ```lua local id = Simulator:CreateDeviceAsync({ Name = "My Custom Tablet", Width = 2560, Height = 1600, PixelDensity = 300, DeviceForm = Enum.DeviceForm.Tablet, }) ``` ### Method: StudioDeviceSimulatorService:GetDeviceAsync **Signature:** `StudioDeviceSimulatorService:GetDeviceAsync(): string` Returns the ID of the currently active device, or `"default"` if no device is active. *Yields · Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `string` — string ### Method: StudioDeviceSimulatorService:GetDeviceInfoAsync **Signature:** `StudioDeviceSimulatorService:GetDeviceInfoAsync(deviceId: string): Dictionary` Returns a `DeviceConfiguration` for the specified device without activating it. Useful for inspecting presets before choosing one to switch to. The returned dictionary contains the following fields: | Field | Type | Create/Update | Description | | --- | --- | --- | --- | | `DeviceId` | string | Read-only | Unique identifier assigned by the service. | | `Name` | string | Required | Display name. 1 to 200 characters. Cannot be `"default"`. | | `Width` | number | Required | Screen width in pixels. Range: 1 to 7680. | | `Height` | number | Required | Screen height in pixels. Range: 1 to 4320. | | `PixelDensity` | number | Required | Pixel density in DPI. Range: 72 to 10000. | | `DeviceForm` | [DeviceForm](/docs/reference/engine/enums/DeviceForm.md) | Optional (default: Phone) | One of Phone, Tablet, Desktop, Console, VR. | | `IsCustom` | boolean | Read-only | `true` if the device was user-created. | | `ResolutionScale` | number | Optional (default: 1.0) | Resolution scaling factor. Must be greater than 0, max 10.0. | | `PortraitKeyboardHeight` | number | Optional (default: 0) | Virtual keyboard height in portrait mode. | | `LandscapeKeyboardHeight` | number | Optional (default: 0) | Virtual keyboard height in landscape mode. | *Yields · Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `deviceId` | `string` | | | **Returns:** `Dictionary` — A dictionary containing the device configuration and passed to `CreateDeviceAsync` and `UpdateDeviceAsync`. ### Method: StudioDeviceSimulatorService:GetDeviceListAsync **Signature:** `StudioDeviceSimulatorService:GetDeviceListAsync(): Array` Returns an array of device IDs for all available presets, including built-in and custom devices. *Yields · Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `Array` — An array of device IDs. **Get device list example** Example of GetDeviceListAsync method ```lua local Simulator = game:GetService("StudioDeviceSimulatorService") local devices = Simulator:GetDeviceListAsync() for _, deviceId in devices do print(deviceId) end ``` ### Method: StudioDeviceSimulatorService:GetOrientationAsync **Signature:** `StudioDeviceSimulatorService:GetOrientationAsync(): ScreenOrientation` Returns the current simulated orientation. *Yields · Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `ScreenOrientation` — ScreenOrientation ### Method: StudioDeviceSimulatorService:GetPixelDensityAsync **Signature:** `StudioDeviceSimulatorService:GetPixelDensityAsync(): float` Returns the current simulated pixel density. *Yields · Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `float` — number ### Method: StudioDeviceSimulatorService:GetResolutionAsync **Signature:** `StudioDeviceSimulatorService:GetResolutionAsync(): Vector2` Returns the current simulated resolution. *Yields · Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `Vector2` — Vector2 ### Method: StudioDeviceSimulatorService:GetScalingModeAsync **Signature:** `StudioDeviceSimulatorService:GetScalingModeAsync(): DeviceSimulatorScalingMode` Returns the current scaling mode. *Yields · Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `DeviceSimulatorScalingMode` — DeviceSimulatorScalingMode ### Method: StudioDeviceSimulatorService:RemoveDeviceAsync **Signature:** `StudioDeviceSimulatorService:RemoveDeviceAsync(deviceId: string): ()` Removes a custom device. Errors if the target is a built-in preset. *Yields · Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `deviceId` | `string` | | | **Returns:** `()` ### Method: StudioDeviceSimulatorService:SetDeviceAsync **Signature:** `StudioDeviceSimulatorService:SetDeviceAsync(deviceId: string): ()` Activates the specified device. Passing `"default"` stops simulation and is equivalent to calling `StopSimulationAsync`. Errors in PlayServer mode. Errors if the device ID does not exist. *Yields · Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `deviceId` | `string` | | | **Returns:** `()` ### Method: StudioDeviceSimulatorService:SetOrientationAsync **Signature:** `StudioDeviceSimulatorService:SetOrientationAsync(orientation: ScreenOrientation): ()` Sets or gets the simulated screen orientation. Accepts Portrait, LandscapeLeft, or LandscapeRight. Other values will error. Errors in PlayServer mode. *Yields · Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `orientation` | `ScreenOrientation` | | | **Returns:** `()` ### Method: StudioDeviceSimulatorService:SetPixelDensityAsync **Signature:** `StudioDeviceSimulatorService:SetPixelDensityAsync(density: float): ()` Overrides or returns the simulated pixel density in DPI. Session-level; cleared on device switch. This method requires an active device. `SetPixelDensityAsync` also errors in PlayServer mode. *Yields · Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `density` | `float` | | | **Returns:** `()` ### Method: StudioDeviceSimulatorService:SetResolutionAsync **Signature:** `StudioDeviceSimulatorService:SetResolutionAsync(width: int, height: int): ()` Overrides the simulated viewport resolution or returns the current resolution as a Vector2. Overrides are session-level and cleared when you switch devices. Coordinates are in landscape space: the first parameter maps to the horizontal axis in landscape, the second to the vertical axis. In portrait, axes are swapped automatically. Both methods require an active device. They error if `GetDeviceAsync()` returns `"default"`. `SetResolutionAsync` also errors in PlayServer mode. *Yields · Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `width` | `int` | | | | `height` | `int` | | | **Returns:** `()` ### Method: StudioDeviceSimulatorService:SetScalingModeAsync **Signature:** `StudioDeviceSimulatorService:SetScalingModeAsync(mode: DeviceSimulatorScalingMode): ()` Controls how the simulated resolution maps to the Studio viewport. - `ScaleToPhysicalSize` scales the viewport to approximate physical device size. - `ActualResolution` renders at exact pixel resolution. - `FitToWindow` scales to fill the viewport. Both methods require an active device. `SetScalingModeAsync` also errors in PlayServer mode. *Yields · Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `mode` | `DeviceSimulatorScalingMode` | | | **Returns:** `()` ### Method: StudioDeviceSimulatorService:StopSimulationAsync **Signature:** `StudioDeviceSimulatorService:StopSimulationAsync(): ()` Stops the active simulation. Prefer this over `SetDeviceAsync("default")` for clarity. Errors in PlayServer mode. *Yields · Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `()` ### Method: StudioDeviceSimulatorService:UpdateDeviceAsync **Signature:** `StudioDeviceSimulatorService:UpdateDeviceAsync(deviceId: string, config: Dictionary): ()` Updates a custom device's configuration. Errors if the target is a built-in preset. *Yields · Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `deviceId` | `string` | | | | `config` | `Dictionary` | | | **Returns:** `()` **Update Device example** Example of UpdateDeviceAsync method ```lua Simulator:UpdateDeviceAsync(id, { Name = "My Custom Tablet", Width = 2732, Height = 2048, PixelDensity = 264, DeviceForm = Enum.DeviceForm.Tablet, }) ``` ## Events ### Event: StudioDeviceSimulatorService.ConfigurationChanged **Signature:** `StudioDeviceSimulatorService.ConfigurationChanged()` Fires when the active simulation state changes: device switch, orientation, resolution, pixel density, scaling mode, or user interaction with the Device Simulator UI. Does not fire for catalog-only operations such as `CreateDeviceAsync`, `UpdateDeviceAsync` on a non-active device, `RemoveDeviceAsync` on a non-active device, or `GetDeviceInfoAsync`. Getter calls inside the handler are async and will yield. *Security: PluginSecurity* **Configuration changed example** Example of ConfigurationChanged event ```lua Simulator.ConfigurationChanged:Connect(function() local deviceId = Simulator:GetDeviceAsync() if deviceId ~= "default" then local res = Simulator:GetResolutionAsync() print(string.format("Active: %s @ %dx%d", deviceId, res.X, res.Y)) end end) ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: StudioScreenshotCapture last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated --- # Class: StudioScreenshotCapture ## Properties ### Property: StudioScreenshotCapture.BufferFormat ```json { "type": "StudioCaptureScreenshotFormat", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: StudioScreenshotCapture.BufferStatus ```json { "type": "StudioCaptureBufferStatus", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: StudioScreenshotCapture.OriginalSize ```json { "type": "Vector2", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: StudioScreenshotCapture.Position ```json { "type": "Vector2", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: StudioScreenshotCapture.Resolution ```json { "type": "Vector2", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: StudioScreenshotCapture.UICaptureMode ```json { "type": "UICaptureMode", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ## Methods ### Method: StudioScreenshotCapture:GetBuffer **Signature:** `StudioScreenshotCapture:GetBuffer(): buffer` *Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `buffer` ### Method: StudioScreenshotCapture:GetErrors **Signature:** `StudioScreenshotCapture:GetErrors(): Array` *Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `Array` ### Method: StudioScreenshotCapture:ScaleAsync **Signature:** `StudioScreenshotCapture:ScaleAsync(strategy: ResamplerMode, newSize: Vector2): StudioScreenshotCapture` *Yields · Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `strategy` | `ResamplerMode` | | | | `newSize` | `Vector2` | | | **Returns:** `StudioScreenshotCapture` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: StudioService last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated summary: "Provides access to configuration of Roblox Studio and allows importing files from the user's file system." --- # Class: StudioService > Provides access to configuration of Roblox Studio and allows importing files > from the user's file system. ## Description **StudioService** provides access to configuration of Roblox Studio, allows importing files from the user's file system, and other miscellaneous information. It is intended to be used by [Plugins](/docs/reference/engine/classes/Plugin.md) in order to provide a consistent user experience. - Plugins that allow the user to move objects may find [GridSize](/docs/reference/engine/classes/StudioService.md), [RotateIncrement](/docs/reference/engine/classes/StudioService.md) and [UseLocalSpace](/docs/reference/engine/classes/StudioService.md) useful. - Plugins that require the user to import files should use [PromptImportFileAsync](/docs/reference/engine/classes/StudioService.md) or [PromptImportFilesAsync](/docs/reference/engine/classes/StudioService.md) in order to receive [File](/docs/reference/engine/classes/File.md) objects. - Plugins that display icons of Instance classes can use [GetClassIcon](/docs/reference/engine/classes/StudioService.md). - Plugins that care about which script is currently being edited (if any) can read this from [ActiveScript](/docs/reference/engine/classes/StudioService.md). ## Properties ### Property: StudioService.ActiveScript ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "State" } ``` **ActiveScript** refers to the [LuaSourceContainer](/docs/reference/engine/classes/LuaSourceContainer.md) currently being edited by the user. If the user is not editing a script, this will be `nil`. Below is an example that shows how you can use this property to measure for how long a script was active. ```lua local StudioService = game:GetService("StudioService") local startTime = os.time() local activeScript local function onActiveScriptChanged() local newActiveScript = StudioService.ActiveScript if activeScript and newActiveScript ~= activeScript then local deltaTime = os.time() - startTime print(("You edited %s for %d:%2.d"):format(activeScript.Name, deltaTime // 60, deltaTime % 60)) end startTime = os.time() activeScript = newActiveScript end StudioService:GetPropertyChangedSignal("ActiveScript"):Connect(onActiveScriptChanged) ``` ### Property: StudioService.DraggerSolveConstraints ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: StudioService.GridSize ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` **GridSize** determines the distance in studs by which Studio's drag and move tools move objects each tick. This is set in the user's toolbar's **Model** tab. ### Property: StudioService.RotateIncrement ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` **RotateIncrement** determines the angle in degrees by which Studio's rotation tool will rotate selected objects each tick. This is set in the user's toolbar's **Model** tab. ### Property: StudioService.Secrets ```json { "type": "string", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: StudioService.ShowConstraintDetails ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: StudioService.ShowWeldDetails ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxScriptSecurity" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: StudioService.StudioLocaleId ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "State" } ``` The **StudioLocaleId** property contains the locale currently in-use by Studio, e.g. `en_US`. It is useful when localizing plugins. Below is a trivial example of localization based on the value returned by this function. ```lua local locale = game:GetService("StudioService").StudioLocaleId if locale == "en_US" then print("Howdy, ya'll") elseif locale == "en_GB" then print("'Ello, gov'na") elseif locale:sub(1, 2) == "en" then print("Hello") elseif locale == "fr_FR" then print("Bonjour") end ``` ### Property: StudioService.UseLocalSpace ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data" } ``` **UseLocalSpace** determines whether the Studio movement/rotation tools will manipulate a part's [CFrame](/docs/reference/engine/classes/BasePart.md) using the local space of an object or global space. By default, this setting is toggled with CtrlL or L. Plugins can read from this property if they implement their own object movement tools. ### Property: StudioService.DrawConstraintsOnTop ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data" } ``` > **Deprecated:** This property is deprecated; constraints can no longer be drawn "on top" of other objects. ## Methods ### Method: StudioService:GetClassIcon **Signature:** `StudioService:GetClassIcon(className: string): Dictionary` **GetClassIcon** provides a dictionary that allows the display of a class' Explorer window icon, e.g. calling this function with "Part" returns property values that display the part icon from the Explorer window. Below is a literal table representation of the value returned when this function is called with `"Part"`. ``` { Image = "rbxasset://textures/ClassImages.png", ImageRectOffset = Vector2.new(16, 0), ImageRectSize = Vector2.new(16, 16) } ``` The utility function below may prove useful when displaying class icons: ```lua local StudioService = game:GetService("StudioService") local imageLabel = script.Parent local function displayClassIcon(image, className) for k, v in StudioService:GetClassIcon(className) do image[k] = v -- Set property end end displayClassIcon(imageLabel, "Part") ``` *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `className` | `string` | | | **Returns:** `Dictionary` ### Method: StudioService:GetUserId **Signature:** `StudioService:GetUserId(): int64` Returns the Studio user's userId if they're logged in, otherwise returns 0. *Security: PluginSecurity · Thread Safety: Unsafe* **Returns:** `int64` **StudioService:GetUserId** The example prints the currently logged in user's ID. ```lua -- Can only be used in a plugin local StudioService = game:GetService("StudioService") local Players = game:GetService("Players") local loggedInUserId = StudioService:GetUserId() local loggedInUserName = Players:GetNameFromUserIdAsync(loggedInUserId) print("Hello,", loggedInUserName) ``` ### Method: StudioService:PromptImportFileAsync **Signature:** `StudioService:PromptImportFileAsync(fileTypeFilter?: Array): Instance` This function prompts the current Studio user to select one file, which will then be loaded as a [File](/docs/reference/engine/classes/File.md). See also: - [StudioService:PromptImportFilesAsync()](/docs/reference/engine/classes/StudioService.md), the same function but for loading a list of files instead of a single file *Yields · Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `fileTypeFilter` | `Array` | `{}` | A list of file types that the user is allowed to select. File types are formatted without a period. For example, {"jpg", "png"} would allow only a JPG or PNG file to be selected. If no filter is provided, the filter is `nil` and allows the user to select any file type. | **Returns:** `Instance` — The imported [File](/docs/reference/engine/classes/File.md). Returns `nil` if no files were selected, or if the selected file was too large (FileSize greater than 100 megabytes). ### Method: StudioService:PromptImportFilesAsync **Signature:** `StudioService:PromptImportFilesAsync(fileTypeFilter?: Array): Instances` This function prompts the current Studio user to select one or more files, which will then be loaded as [Files](/docs/reference/engine/classes/File.md). Throws an error if the fileTypeFilter was an empty list. See also: - [StudioService:PromptImportFileAsync()](/docs/reference/engine/classes/StudioService.md), the same function but for loading a single file instead of a list of files *Yields · Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `fileTypeFilter` | `Array` | `{}` | A list of file types that the user is allowed to select. File types are formatted without a period. For example, {"jpg", "png"} would allow only JPG and PNG files to be selected. If no filter is provided, the filter is `nil` and allows the user to select any file type. | **Returns:** `Instances` — The imported [Files](/docs/reference/engine/classes/File.md). Returns an empty list if no files were selected. Returns `nil` if the user selected one or more files that were too large (FileSize greater than 100 megabytes). ### Method: StudioService:PromptImportFile **Signature:** `StudioService:PromptImportFile(fileTypeFilter?: Array): Instance` Prompts the current Studio user to select one file to add as a [File](/docs/reference/engine/classes/File.md). *Yields · Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `fileTypeFilter` | `Array` | `{}` | A list of file types that the user is allowed to select. File types are formatted without a period. For example, {"jpg", "png"} would allow only a JPG or PNG file to be selected. If no filter is provided, the filter is `nil` and allows the user to select any file type. | **Returns:** `Instance` — The imported [File](/docs/reference/engine/classes/File.md). Returns `nil` if no files were selected, or if the selected file was too large (FileSize greater than 100 megabytes). ### Method: StudioService:PromptImportFiles **Signature:** `StudioService:PromptImportFiles(fileTypeFilter?: Array): Instances` Prompts the current Studio user to select files to add as [Files](/docs/reference/engine/classes/File.md). *Yields · Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `fileTypeFilter` | `Array` | `{}` | A list of file types that the user is allowed to select. File types are formatted without a period. For example, {"jpg", "png"} would allow only JPG and PNG files to be selected. If no filter is provided, the filter is `nil` and allows the user to select any file type. | **Returns:** `Instances` — The imported [Files](/docs/reference/engine/classes/File.md). Returns an empty list if no files were selected. Returns `nil` if the user selected one or more files that were too large (FileSize greater than 100 megabytes). ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: StudioTestService last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated summary: "Service allowing plugins to automate and customize Test and Run mode testing." --- # Class: StudioTestService > Service allowing plugins to automate and customize Test and Run mode testing. ## Description `StudioTestService` allows plugins to automate and customize Test and Run mode testing. With `StudioTestService`, your plugins can launch tests that jump straight to a specific part of your game, or run complex code tests automatically. You can use `StudioTestService` to start a server with multiple simulated clients, add players mid-session, pass arguments into running tests, end tests from the server, and trigger client disconnections, all without manually clicking through the Test tab in Studio. These methods complement the existing playtest automation available through Studio's built-in MCP server. Where the MCP server's `start_stop_play` tool starts a single Play Client session, the `StudioTestService` methods are designed for scripted multi-client scenarios such as lobby, matchmaking, and join/leave flow testing. A typical multiplayer test splits across three script contexts: the plugin that launches it, the server that drives it, and the clients that participate: Plugin script: ```lua local StudioTestService = game:GetService("StudioTestService") local toolbar = plugin:CreateToolbar("STS Tests") local button = toolbar:CreateButton("MyTest", "", "") button.Click:Connect(function() button.Enabled = false local result = StudioTestService:ExecuteMultiplayerTestAsync(1, "MyArguments") assert(result == "Success!") print("Test finished successfully!") button.Enabled = true end) ``` Server script: ```lua local Players = game:GetService("Players") local StudioTestService = game:GetService("StudioTestService") assert(StudioTestService:GetTestArgs() == "MyArguments") Players.PlayerRemoving:Connect(function(player) print(player.Name .. " left the experience") task.wait(3) StudioTestService:EndTest("Success!") end) ``` Client script: ```lua if not game:IsLoaded() then game.Loaded:Wait() end local StudioTestService = game:GetService("StudioTestService") task.wait(3) StudioTestService:LeaveTest() ``` ##### Limitations - `ExecuteMultiplayerTestAsync` can only be called from plugin scripts. It yields until the test ends, so wrap it in a coroutine if the calling plugin needs to remain responsive. - `ExecuteMultiplayerTestAsync` cannot be called from inside a running test. - `ExecuteMultiplayerTestAsync` supports up to 8 simulated clients per test session. - Only one multiplayer test session can run at a time per Studio instance. - `AddPlayers` and `EndTest` must be called from the server DataModel of a running test. - `CanLeaveTest` and `LeaveTest` must be called from a client DataModel. - `GetTestArgs` currently has a known issue when called from a client LocalScript. ## Properties ### Property: StudioTestService.EditModeActive ```json { "type": "boolean", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ## Methods ### Method: StudioTestService:AddPlayers **Signature:** `StudioTestService:AddPlayers(numPlayers: int): ()` Adds `numPlayers` additional client DataModels to the running test session. Useful for simulating staggered joins, testing lobby-to-game transitions under load, or reproducing race conditions that only appear when clients connect at different times. Must be called from the server DataModel of an active multiplayer test. Errors if called outside of an active test, or if called from a client DataModel. *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `numPlayers` | `int` | | | **Returns:** `()` **Add Players example** Example of AddPlayers method ```lua local StudioTestService = game:GetService("StudioTestService") task.wait(5) StudioTestService:AddPlayers(3) ``` ### Method: StudioTestService:CanLeaveTest **Signature:** `StudioTestService:CanLeaveTest(): boolean` Returns `true` if the calling client DataModel is allowed to disconnect from the test, and `false` otherwise. Check this before calling `LeaveTest` to avoid errors during session teardown or during states where disconnection is blocked. Call from a client DataModel. *Security: None · Thread Safety: Unsafe* **Returns:** `boolean` **Can Leave Test example** Example of CanLeaveTest method ```lua local StudioTestService = game:GetService("StudioTestService") if StudioTestService:CanLeaveTest() then StudioTestService:LeaveTest() end ``` ### Method: StudioTestService:EndTest **Signature:** `StudioTestService:EndTest(value: Variant): ()` Ends the current Studio test session if called from the server [DataModel](/docs/reference/engine/classes/DataModel.md), even if the test was not started by this service. If the test was started by [StudioTestService:ExecutePlayModeAsync()](/docs/reference/engine/classes/StudioTestService.md) or [StudioTestService:ExecuteRunModeAsync()](/docs/reference/engine/classes/StudioTestService.md), the value passed here is returned by that method. This method returns immediately and ends the test session asynchronously. It errors if called from any [DataModel](/docs/reference/engine/classes/DataModel.md) other than that of the server during a running Studio test session. *Security: None · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `value` | `Variant` | | Value returned to the calling [StudioTestService:ExecutePlayModeAsync()](/docs/reference/engine/classes/StudioTestService.md) or [StudioTestService:ExecuteRunModeAsync()](/docs/reference/engine/classes/StudioTestService.md) method. | **Returns:** `()` **End Test example** Example of EndTest method ```lua local StudioTestService = game:GetService("StudioTestService") -- Server-side logic runs here, waits for players, runs assertions, etc. StudioTestService:EndTest("Success!") ``` ### Method: StudioTestService:ExecuteMultiplayerTestAsync **Signature:** `StudioTestService:ExecuteMultiplayerTestAsync(numPlayers: int, args: Variant): Variant` Launches a multiplayer test session with one server and `numPlayers` client DataModels. Yields until the test completes and returns the value passed to `EndTest` on the server. Supports up to 8 simulated clients per test session. The optional `args` value is forwarded to scripts running inside the test session and can be read via `GetTestArgs`. It can be any Roblox-serializable value, for example a string, a Color3, or a table. Common uses include passing a test name, a timeout, or configuration flags. Errors if a test is already running. Errors if `numPlayers` is less than 1 or exceeds 8. *Yields · Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `numPlayers` | `int` | | | | `args` | `Variant` | | | **Returns:** `Variant` **Execute Multiplayer Test example** Example of ExecuteMultiplayerTestAsync method ```lua local StudioTestService = game:GetService("StudioTestService") local result = StudioTestService:ExecuteMultiplayerTestAsync(4, "LobbySpawnTest") assert(result == "Success!") print("Test finished:", result) ``` ### Method: StudioTestService:ExecutePlayModeAsync **Signature:** `StudioTestService:ExecutePlayModeAsync(args: Variant): Variant` Starts a solo **Test** session and yields until that session ends. The `args` parameter can be retrieved using [StudioTestService:GetTestArgs()](/docs/reference/engine/classes/StudioTestService.md). Returns the value passed to [StudioTestService:EndTest()](/docs/reference/engine/classes/StudioTestService.md), or `nil` if the test ended by other means. This method errors if a test session is already running. *Yields · Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `args` | `Variant` | | Argument passed to the test session, or `nil`. | **Returns:** `Variant` — Value passed from [StudioTestService:EndTest()](/docs/reference/engine/classes/StudioTestService.md), or `nil`. ### Method: StudioTestService:ExecuteRunModeAsync **Signature:** `StudioTestService:ExecuteRunModeAsync(args: Variant): Variant` Starts a **Run** test session and yields until that session ends. The `args` parameter can be retrieved using [StudioTestService:GetTestArgs()](/docs/reference/engine/classes/StudioTestService.md). Returns the value passed to [StudioTestService:EndTest()](/docs/reference/engine/classes/StudioTestService.md), or `nil` if the test ended by other means. This method errors if a test session is already running. *Yields · Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `args` | `Variant` | | Argument passed to the test session, or `nil`. | **Returns:** `Variant` — Value passed from [StudioTestService:EndTest()](/docs/reference/engine/classes/StudioTestService.md), or `nil`. ### Method: StudioTestService:GetTestArgs **Signature:** `StudioTestService:GetTestArgs(): Variant` Returns the argument passed to [StudioTestService:ExecutePlayModeAsync()](/docs/reference/engine/classes/StudioTestService.md) or [StudioTestService:ExecuteRunModeAsync()](/docs/reference/engine/classes/StudioTestService.md) for the current test session, or `nil` if the session was not started by those methods. `GetTestArgs` returns the value as-is, preserving its type. For example, if the plugin passed a Color3, `GetTestArgs` returns a Color3. Note that if you call `GetTestArgs` a client `LocalScript`, it may fail. Server-side calls work as expected. *Security: None · Thread Safety: Unsafe* **Returns:** `Variant` — Arguments passed to the test, or `nil`. **Get Test Args example** Example of GetTestArgs method ```lua local StudioTestService = game:GetService("StudioTestService") local args = StudioTestService:GetTestArgs() print("Received args:", args) ``` ### Method: StudioTestService:LeaveTest **Signature:** `StudioTestService:LeaveTest(): ()` Disconnects the calling client from the active multiplayer test. Useful for testing player leave and reconnection logic, cleanup-on-disconnect behavior, and leader reassignment in lobby systems. Call from a client DataModel. Make sure to check `CanLeaveTest()` first. Errors if the client cannot currently leave or if called from a server DataModel. *Security: None · Thread Safety: Unsafe* **Returns:** `()` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: StudioTheme last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated --- # Class: StudioTheme ## Methods ### Method: StudioTheme:GetColor **Signature:** `StudioTheme:GetColor(styleguideitem: StudioStyleGuideColor, modifier?: StudioStyleGuideModifier): Color3` The **GetColor()** function returns the [Color3](/docs/reference/engine/datatypes/Color3.md) corresponding to the arguments provided. For instance, if you would like to get the [Color3](/docs/reference/engine/datatypes/Color3.md) of the Studio "MainButton" when it's **disabled**, you can use the following code: ```lua settings().Studio.Theme:GetColor(Enum.StudioStyleGuideColor.MainButton, Enum.StudioStyleGuideModifier.Disabled) ``` See the [StudioStyleGuideColor](/docs/reference/engine/enums/StudioStyleGuideColor.md) reference for a list of Studio elements and [StudioStyleGuideModifier](/docs/reference/engine/enums/StudioStyleGuideModifier.md) for a list of modifiers. *Security: PluginSecurity · Thread Safety: Unsafe* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `styleguideitem` | `StudioStyleGuideColor` | | The element you want to get the theme color for. | | `modifier` | `StudioStyleGuideModifier` | `Default` | The modifier you want to place on the `StyleGuideColor` element. | **Returns:** `Color3` — The corresponding Color3 theme value. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: StyleBase last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Gui tags: - NotCreatable summary: "The base class for StyleSheet and StyleRule." --- # Class: StyleBase > The base class for [StyleSheet](/docs/reference/engine/classes/StyleSheet.md) and [StyleRule](/docs/reference/engine/classes/StyleRule.md). ## Description The base class for [StyleSheet](/docs/reference/engine/classes/StyleSheet.md) and [StyleRule](/docs/reference/engine/classes/StyleRule.md). Holds a list of child [StyleRules](/docs/reference/engine/classes/StyleRule.md), as well as token definitions which are stored as [attributes](/docs/en-us/studio/properties.md#instance-attributes). ## Methods ### Method: StyleBase:GetStyleRules **Signature:** `StyleBase:GetStyleRules(): List` Returns an array of associated [StyleRules](/docs/reference/engine/classes/StyleRule.md). *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Returns:** `List` — Array of [StyleRules](/docs/reference/engine/classes/StyleRule.md). ### Method: StyleBase:InsertStyleRule **Signature:** `StyleBase:InsertStyleRule(rule: StyleRule, priority: int?): ()` Inserts a new [StyleRule](/docs/reference/engine/classes/StyleRule.md) into the array of rules so that its [Priority](/docs/reference/engine/classes/StyleRule.md) is greater than all previous [StyleRules](/docs/reference/engine/classes/StyleRule.md). If `priority` is specified, sets the new rule's [Priority](/docs/reference/engine/classes/StyleRule.md) to the specified value. *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `rule` | `StyleRule` | | The [StyleRule](/docs/reference/engine/classes/StyleRule.md) to insert. | | `priority` | `int?` | | The number to set the rule's [Priority](/docs/reference/engine/classes/StyleRule.md) to. | **Returns:** `()` ### Method: StyleBase:SetStyleRules **Signature:** `StyleBase:SetStyleRules(rules: Instances): ()` Similar to [InsertStyleRule()](/docs/reference/engine/classes/StyleBase.md) but lets you declare and set multiple [StyleRules](/docs/reference/engine/classes/StyleRule.md) at once. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local coreSheet = Instance.new("StyleSheet") coreSheet.Name = "CoreSheet" coreSheet.Parent = ReplicatedStorage local styleRuleA = Instance.new("StyleRule") styleRuleA.Selector = "Frame" styleRuleA:SetProperty("BackgroundColor3", Color3.new(1, 0, 0)) local styleRuleB = Instance.new("StyleRule") styleRuleB.Selector = "Frame" styleRuleB:SetProperty("BackgroundColor3", Color3.new(0, 1, 0)) coreSheet:SetStyleRules({ styleRuleA, styleRuleB }) ``` *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `rules` | `Instances` | | Array of [StyleRules](/docs/reference/engine/classes/StyleRule.md) to set. | **Returns:** `()` ## Events ### Event: StyleBase.StyleRulesChanged **Signature:** `StyleBase.StyleRulesChanged()` Fires when one or more [StyleRules](/docs/reference/engine/classes/StyleRule.md) is explicitly changed on the connected [StyleSheet](/docs/reference/engine/classes/StyleSheet.md) or [StyleRule](/docs/reference/engine/classes/StyleRule.md), for example when [InsertStyleRule()](/docs/reference/engine/classes/StyleBase.md) is called. *Security: None · Capabilities: UI* ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: StyleDerive last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Gui summary: "When parented to a StyleSheet, references another StyleSheet from which the parent inherits StyleRules and token definitions." --- # Class: StyleDerive > When parented to a [StyleSheet](/docs/reference/engine/classes/StyleSheet.md), references another [StyleSheet](/docs/reference/engine/classes/StyleSheet.md) > from which the parent inherits [StyleRules](/docs/reference/engine/classes/StyleRule.md) and token > definitions. ## Description When parented to a [StyleSheet](/docs/reference/engine/classes/StyleSheet.md), references another [StyleSheet](/docs/reference/engine/classes/StyleSheet.md) from which the parent inherits [StyleRules](/docs/reference/engine/classes/StyleRule.md) and token definitions. Multiple `StyleDerive` instances can exist under a [StyleSheet](/docs/reference/engine/classes/StyleSheet.md) and you can order them by [Priority](/docs/reference/engine/classes/StyleDerive.md). ## Properties ### Property: StyleDerive.Priority ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` A number that determines how style properties inherited through this `StyleDerive` apply relative to the same properties inherited through other `StyleDerives`. Higher priority takes precedence over lower; for example, if a `StyleDerive` with a priority of `10` points to a sheet `StyleSheetA`, its [StyleRules](/docs/reference/engine/classes/StyleRule.md) and tokens will take precedence over the same [StyleRules](/docs/reference/engine/classes/StyleRule.md) and tokens inherited through lower-priority `StyleDerives`. ### Property: StyleDerive.StyleSheet ```json { "type": "StyleSheet", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` The [StyleSheet](/docs/reference/engine/classes/StyleSheet.md) from which the parent [StyleSheet](/docs/reference/engine/classes/StyleSheet.md) inherits [StyleRules](/docs/reference/engine/classes/StyleRule.md) and token definitions. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: StyleLink last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Gui summary: "Links a StyleSheet to the instance tree whose root is the parent of the `StyleLink`." --- # Class: StyleLink > Links a [StyleSheet](/docs/reference/engine/classes/StyleSheet.md) to the instance tree whose root is the parent of > the `StyleLink`. ## Description Links a [StyleSheet](/docs/reference/engine/classes/StyleSheet.md) to the instance tree whose root is the parent of the `StyleLink`. For example, placing a `StyleLink` under a [ScreenGui](/docs/reference/engine/classes/ScreenGui.md) applies the referenced [StyleSheet](/docs/reference/engine/classes/StyleSheet.md) to both the [ScreenGui](/docs/reference/engine/classes/ScreenGui.md) and all of the [GuiObjects](/docs/reference/engine/classes/GuiObject.md) within it. Only one [StyleSheet](/docs/reference/engine/classes/StyleSheet.md) can apply to a given tree. ## Properties ### Property: StyleLink.StyleSheet ```json { "type": "StyleSheet", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` The [StyleSheet](/docs/reference/engine/classes/StyleSheet.md) to link to the parent such that the parent's descendants are styled accordingly. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: StyleQuery last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances summary: "Instance used to set conditions such as `\"MaxSize\"` and `\"PreferredInput\"` for a StyleRule." --- # Class: StyleQuery > Instance used to set conditions such as `"MaxSize"` and `"PreferredInput"` for > a [StyleRule](/docs/reference/engine/classes/StyleRule.md). ## Description `StyleQuery` is an instance used to set conditions like `"MaxSize"` and `"PreferredInput"` for a [StyleRule](/docs/reference/engine/classes/StyleRule.md). If the conditions are `true`, the rule's `@` selector is enabled. This is helpful to author styling with dynamic container sizes and cross-platform UI. ## Properties ### Property: StyleQuery.IsActive ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "UI" ] } ``` A boolean that determines whether a [StyleRule.Selector](/docs/reference/engine/classes/StyleRule.md) of `@` will match the `StyleQuery` name. For example, if a `StyleQuery` named `SmGamepad` has conditions set for [UserInputService.PreferredInput](/docs/reference/engine/classes/UserInputService.md) of [Gamepad](/docs/reference/engine/enums/PreferredInput.md) and [GuiService.ViewportDisplaySize](/docs/reference/engine/classes/GuiService.md) of [Small](/docs/reference/engine/enums/DisplaySize.md) and both of these conditions are `true` (player is playing on a small display size and using a gamepad), the parent of the `StyleQuery` will match any [StyleRule.Selector](/docs/reference/engine/classes/StyleRule.md) `@SmGamepad`. #### Conditions | Condition | Type | Description | | --- | --- | --- | | `AspectRatioRange` | [NumberRange](/docs/reference/engine/datatypes/NumberRange.md) | Evaluates to `true` if the parent width-to-height ratio of [GuiBase2d.AbsoluteSize](/docs/reference/engine/classes/GuiBase2d.md) is greater than or equal to `AspectRatioRange.Min` and less than `AspectRatioRange.Max`. | | `MaxSize` | [Vector2](/docs/reference/engine/datatypes/Vector2.md) | Evaluates to `true` if the parent [GuiBase2d.AbsoluteSize](/docs/reference/engine/classes/GuiBase2d.md) is less than `MaxSize`. | | `MinSize` | [Vector2](/docs/reference/engine/datatypes/Vector2.md) | Evaluates to `true` if the parent [GuiBase2d.AbsoluteSize](/docs/reference/engine/classes/GuiBase2d.md) is greater than or equal to `MinSize`. | | `PreferredInput` | [PreferredInput](/docs/reference/engine/enums/PreferredInput.md) | Evaluates to `true` if `PreferredInput` matches [UserInputService.PreferredInput](/docs/reference/engine/classes/UserInputService.md). | | `PreferredTextSize` | [PreferredTextSize](/docs/reference/engine/enums/PreferredTextSize.md) | Evaluates to `true` if `PreferredTextSize` matches [GuiService.PreferredTextSize](/docs/reference/engine/classes/GuiService.md). | | `ReducedMotionEnabled` | boolean | Evaluates to `true` if `ReducedMotionEnabled` matches [GuiService.ReducedMotionEnabled](/docs/reference/engine/classes/GuiService.md). | | `ViewportDisplaySize` | [DisplaySize](/docs/reference/engine/enums/DisplaySize.md) | Evaluates to `true` if `ViewportDisplaySize` matches [GuiService.ViewportDisplaySize](/docs/reference/engine/classes/GuiService.md). | ## Methods ### Method: StyleQuery:GetCondition **Signature:** `StyleQuery:GetCondition(name: string): Variant` Returns the value of a specific condition in the `StyleQuery`. *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name` | `string` | | String name of the condition, for example `"MaxSize"` or `"ViewportDisplaySize"`. | **Returns:** `Variant` — Value of the condition. ### Method: StyleQuery:GetConditions **Signature:** `StyleQuery:GetConditions(): Dictionary` Returns a dictionary of key-value pairs describing the conditoins set on the `StyleQuery`. *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Returns:** `Dictionary` — Dictionary of key-value pairs describing the conditions set on the `StyleQuery`. ### Method: StyleQuery:SetCondition **Signature:** `StyleQuery:SetCondition(name: string, value: Variant): ()` Sets a new condition (or modifies an existing condition) for the `StyleQuery`. The `name` parameter should be a valid condition of `StyleQuery`, and the assigned value should match the condition's value type, for example [Vector2](/docs/reference/engine/datatypes/Vector2.md) for `MaxSize` or [PreferredInput](/docs/reference/engine/enums/PreferredInput.md) for `PreferredInput`. Attempts to assign invalid condition names such as `"Size"` or `"Input"` will silently fail. Type mismatches such as [UDim2](/docs/reference/engine/datatypes/UDim2.md) for `MaxSize` or `string` for `ReducedMotionEnabled` will also fail. To set multiple conditions for a `StyleQuery` at once, see [SetConditions()](/docs/reference/engine/classes/StyleQuery.md). ```lua local styleQuery = Instance.new("StyleQuery") styleQuery:SetCondition("MaxSize", Vector2.new(200, math.huge)) ``` *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name` | `string` | | Condition name to set, for example `"MinSize"`. | | `value` | `Variant` | | Condition value to set, for example [Vector2.new(100, 0)](/docs/reference/engine/datatypes/Vector2.md). | **Returns:** `()` ### Method: StyleQuery:SetConditions **Signature:** `StyleQuery:SetConditions(conditions: Dictionary): ()` Similar to [SetCondition()](/docs/reference/engine/classes/StyleQuery.md) but lets you declare and set multiple conditions of the `StyleQuery` at once. Each `name` parameter should be a valid condition of `StyleQuery`, and the assigned value should match the condition's value type, for example [Vector2](/docs/reference/engine/datatypes/Vector2.md) for `MaxSize` or [PreferredInput](/docs/reference/engine/enums/PreferredInput.md) for `PreferredInput`. Attempts to assign invalid condition names such as `"Size"` or `"Input"` will silently fail. Type mismatches such as [UDim2](/docs/reference/engine/datatypes/UDim2.md) for `MaxSize` or `string` for `ReducedMotionEnabled` will also fail. To set/update just one condition of a `StyleQuery`, see [SetCondition()](/docs/reference/engine/classes/StyleQuery.md). ```lua local styleQuery = Instance.new("StyleQuery") styleQuery:SetConditions({ ["AspectRatioRange"] = NumberRange.new(0, 1), ["MaxSize"] = Vector2.new(200, math.huge), }) ``` *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `conditions` | `Dictionary` | | Dictionary of key-value pairs defining the conditions to set. | **Returns:** `()` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: StyleRule last_updated: 2026-06-29T19:34:08Z inherits: - StyleBase - Instance - Object type: class memory_category: Gui summary: "Defines style properties which override properties on the instances affected by the Selector property." --- # Class: StyleRule > Defines style properties which override properties on the instances affected > by the [Selector](/docs/reference/engine/classes/StyleRule.md) property. ## Properties ### Property: StyleRule.Priority ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` A number that determines how properties of the `StyleRule` apply relative to the same properties in other [StyleRules](/docs/reference/engine/classes/StyleRule.md). Higher priority values take precedence over lower. For example, if a `StyleRule` with a priority of `10` has an `AnchorPoint` property of `1, 0`, it will take precedence over lower-priority [StyleRules](/docs/reference/engine/classes/StyleRule.md) with `AnchorPoint` properties. ### Property: StyleRule.Selector ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` A string specifying which instances the `StyleRule` should affect. This can be a mix of selectors and combinators to match characteristics such as the class name, instance name, and hierarchy relationships. For example, `".Container > ImageLabel.BlueOnHover:Hover"` effectively means the style rule overrides every [ImageLabel](/docs/reference/engine/classes/ImageLabel.md) that's a child of an instance tagged with `Container` (`.Container > ImageLabel`) **and** is tagged with `BlueOnHover` (`.BlueOnHover`) **and** is in the [GuiState.Hover](/docs/reference/engine/enums/GuiState.md) state (`:Hover`). #### Selectors | Selector | Description | Examples | | --- | --- | --- | | `[class]` | Matches instances of a [GuiObject](/docs/reference/engine/classes/GuiObject.md) or [UIComponent](/docs/reference/engine/classes/UIComponent.md) class. | `"Frame"` `"ImageButton"` `"UICorner"` | | `.[tag]` | Matches instances [tagged](/docs/en-us/studio/properties.md#instance-tags) with a [CollectionService](/docs/reference/engine/classes/CollectionService.md) tag. | `".Container"` `".BlueOnHover"` | | `#[name]` | Matches instances of a specific [Instance.Name](/docs/reference/engine/classes/Instance.md). | `"#ModalFrame"` `"#CloseButton"` | | `:[state]` | Matches instances currently in a [GuiState](/docs/reference/engine/enums/GuiState.md). | `":Hover"` | | `@[StyleQuery name]` | Matches instances with child [StyleQuery](/docs/reference/engine/classes/StyleQuery.md) that has [IsActive](/docs/reference/engine/classes/StyleQuery.md) set to `true`. There are also built-in selectors for `"@ReducedMotionEnabledFalse"`, `"@ReducedMotionEnabledTrue"`, `"@PreferredInputGamepad"`, `"@PreferredInputKeyboardAndMouse"`, `"@PreferredInputTouch"`, `"@PreferredTextSizeMedium"`, `"@PreferredTextSizeLarge"`, `"@PreferredTextSizeLarger"`, `"@PreferredTextSizeLargest"`, `"@ViewportDisplaySizeSmall"`, `"@ViewportDisplaySizeMedium"`, and `"@ViewportDisplaySizeLarge"` that can match an instance without a [StyleQuery](/docs/reference/engine/classes/StyleQuery.md) instance. | `"@StyleQuerySmall"` | #### Combinators | Combinator | Description | Examples | | --- | --- | --- | | `>` | Matches instances that are **direct children** of the previous filter matches. | `"Frame > .Inventory"` | | `>>` | Matches instances that are **descendants** of the previous filter matches. | `"ImageButton >> .BlueOnHover"` | | `,` | Specifies a list of multiple independent selectors for the style rule. | `"Frame.TagA, TextLabel.TagA"` | | `::` | Creates a phantom [UIComponent](/docs/reference/engine/classes/UIComponent.md) instance under the previous filter matches and applies the style rule's properties to it. Multiple instances can be created for [StyleQuery](/docs/reference/engine/classes/StyleQuery.md) and [UIStroke](/docs/reference/engine/classes/UIStroke.md) by aliasing the instance with a name selector. Some nested instances are supported, including [UIGradient](/docs/reference/engine/classes/UIGradient.md) under [UIStroke](/docs/reference/engine/classes/UIStroke.md) or [UIConstraint](/docs/reference/engine/classes/UIConstraint.md) under a [UIGridLayout](/docs/reference/engine/classes/UIGridLayout.md). | `"Frame::UICorner" "::UIStroke #Outer"` | **UI Class Selector** The following example shows how to define a **class** selector which targets all [TextButton](/docs/reference/engine/classes/TextButton.md) instances and styles them with blue background and light grey text. To test, paste the code into a [LocalScript](/docs/reference/engine/classes/LocalScript.md) which is a child of a [ScreenGui](/docs/reference/engine/classes/ScreenGui.md) located inside the [StarterGui](/docs/reference/engine/classes/StarterGui.md) container. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local screenGui = script.Parent local coreSheet = Instance.new("StyleSheet") coreSheet.Parent = ReplicatedStorage local styleLink = Instance.new("StyleLink") styleLink.StyleSheet = coreSheet styleLink.Parent = screenGui local rule = Instance.new("StyleRule") rule.Parent = coreSheet -- Class selector rule.Selector = "TextButton" -- Set rule properties rule:SetProperties({ ["BackgroundColor3"] = Color3.fromHex("335FFF"), ["TextColor3"] = Color3.fromHex("E1E1E1"), ["Size"] = UDim2.new(0.15, 0, 0, 40), ["BorderSizePixel"] = 0, }) local button = Instance.new("TextButton") button.Text = "Main Menu" button.Parent = screenGui ``` **UI Tag Selector** The following example shows how to define a **tag** selector which utilizes [tags](/docs/en-us/studio/properties.md#instance-tags) applied through [CollectionService](/docs/reference/engine/classes/CollectionService.md) to target a [TextButton](/docs/reference/engine/classes/TextButton.md) tagged with `ButtonPrimary`. To test, paste the code into a [LocalScript](/docs/reference/engine/classes/LocalScript.md) which is a child of a [ScreenGui](/docs/reference/engine/classes/ScreenGui.md) located inside the [StarterGui](/docs/reference/engine/classes/StarterGui.md) container. ```lua local CollectionService = game:GetService("CollectionService") local ReplicatedStorage = game:GetService("ReplicatedStorage") local screenGui = script.Parent local coreSheet = Instance.new("StyleSheet") coreSheet.Parent = ReplicatedStorage local styleLink = Instance.new("StyleLink") styleLink.StyleSheet = coreSheet styleLink.Parent = screenGui local rule = Instance.new("StyleRule") rule.Parent = coreSheet -- Tag selector rule.Selector = ".ButtonPrimary" -- Set rule properties rule:SetProperties({ ["BackgroundColor3"] = Color3.fromHex("FF0099"), ["TextColor3"] = Color3.fromHex("E1E1E1"), ["Size"] = UDim2.new(0.15, 0, 0, 40), ["BorderSizePixel"] = 0, }) local button = Instance.new("TextButton") button.Text = "Main Menu" button.Parent = screenGui -- Apply tag to button CollectionService:AddTag(button, "ButtonPrimary") ``` **UI Modifier Selector** The following example shows how to define a UI **modifier** selector which applies a phantom [UICorner](/docs/reference/engine/classes/UICorner.md) instance to a [Frame](/docs/reference/engine/classes/Frame.md) tagged with `RoundedCorner20`. To test, paste the code into a [LocalScript](/docs/reference/engine/classes/LocalScript.md) which is a child of a [ScreenGui](/docs/reference/engine/classes/ScreenGui.md) located inside the [StarterGui](/docs/reference/engine/classes/StarterGui.md) container. ```lua local CollectionService = game:GetService("CollectionService") local ReplicatedStorage = game:GetService("ReplicatedStorage") local screenGui = script.Parent local coreSheet = Instance.new("StyleSheet") coreSheet.Parent = ReplicatedStorage local styleLink = Instance.new("StyleLink") styleLink.StyleSheet = coreSheet styleLink.Parent = screenGui local rule = Instance.new("StyleRule") rule.Parent = coreSheet -- UI component selector rule.Selector = "Frame.RoundedCorner20::UICorner" -- Set rule property rule:SetProperty("CornerRadius", UDim.new(0, 20)) -- Create frame local frame = Instance.new("Frame") frame.Size = UDim2.new(0.4, 0, 0.2, 0) frame.Parent = screenGui -- Apply tag to frame CollectionService:AddTag(frame, "RoundedCorner20") ``` **UI Style Query Selector** The following example shows how to define a **query** selector which utilizes [StyleQuery](/docs/reference/engine/classes/StyleQuery.md) to target a [TextButton](/docs/reference/engine/classes/TextButton.md) at different widths. To test, paste the code into a [LocalScript](/docs/reference/engine/classes/LocalScript.md) which is a child of a [ScreenGui](/docs/reference/engine/classes/ScreenGui.md) located inside the [StarterGui](/docs/reference/engine/classes/StarterGui.md) container, and resize the viewport width. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local screenGui = script.Parent local coreSheet = Instance.new("StyleSheet") coreSheet.Parent = ReplicatedStorage local styleLink = Instance.new("StyleLink") styleLink.StyleSheet = coreSheet styleLink.Parent = screenGui local smallRule = Instance.new("StyleRule") smallRule.Parent = coreSheet local mediumRule = Instance.new("StyleRule") mediumRule.Parent = coreSheet -- Declare style query selectors smallRule.Selector = "@Small" mediumRule.Selector = "@Medium" -- Set rule properties smallRule:SetProperties({ ["BackgroundColor3"] = Color3.fromHex("FF0099"), ["Text"] = "Small Container", ["TextColor3"] = Color3.fromHex("E1E1E1"), ["TextSize"] = 12, }) mediumRule:SetProperties({ ["BackgroundColor3"] = Color3.fromHex("F0F0F0"), ["Text"] = "Medium Container", ["TextColor3"] = Color3.fromHex("0022FC"), ["TextSize"] = 24, }) local button = Instance.new("TextButton") button.Text = "Main Menu" button.Parent = screenGui button.Size = UDim2.fromScale(0.5, 0.2) -- Create style queries with names matching style rule selectors local mediumSQ = Instance.new("StyleQuery") mediumSQ.Parent = button mediumSQ.Name = "Medium" mediumSQ:SetConditions({ ["MinSize"] = Vector2.new(200, 0), }) local smallSQ = Instance.new("StyleQuery") smallSQ.Parent = button smallSQ.Name = "Small" smallSQ:SetConditions({ ["MaxSize"] = Vector2.new(200, math.huge), ["MinSize"] = Vector2.new(0, 0), }) ``` ### Property: StyleRule.SelectorError ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` A read-only string that displays errors from the [Selector](/docs/reference/engine/classes/StyleRule.md) property such as syntax errors, unsupported class types, etc. ## Methods ### Method: StyleRule:GetDefaultPropertyTransition **Signature:** `StyleRule:GetDefaultPropertyTransition(): Variant` Returns the default transition applied to all properties of the `StyleRule` that don't have an explicit transition set. This is equivalent to calling [GetPropertyTransitions()](/docs/reference/engine/classes/StyleRule.md) and reading the value at key `"*"`. *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Returns:** `Variant` — The default transition as a [TweenInfo](/docs/reference/engine/datatypes/TweenInfo.md) or string token, or `nil` if no default transition is set. ### Method: StyleRule:GetProperties **Signature:** `StyleRule:GetProperties(): Dictionary` Returns a dictionary of key-value pairs describing the properties of the `StyleRule`, for example: ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local coreSheet = ReplicatedStorage:FindFirstChild("CoreSheet") -- Get reference to style rule local frameRule = coreSheet.Frame local props = frameRule:GetProperties() print(props) --[[ { ["AnchorPoint"] = 0.5, 0, ["BackgroundColor3"] = 1, 0, 0.25 } ]] ``` *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Returns:** `Dictionary` — Dictionary of key-value pairs describing the properties of the `StyleRule`. ### Method: StyleRule:GetProperty **Signature:** `StyleRule:GetProperty(name: string): Variant` Returns the value of a specific property in the `StyleRule`. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local coreSheet = ReplicatedStorage:FindFirstChild("CoreSheet") -- Get reference to style rule local frameRule = coreSheet.Frame local prop = frameRule:GetProperty("AnchorPoint") print(prop) --> 0.5, 0 ``` *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name` | `string` | | String name of the property, for example `"AnchorPoint"` or `"BackgroundColor3"`. | **Returns:** `Variant` — Value of the property. ### Method: StyleRule:GetPropertyTransitions **Signature:** `StyleRule:GetPropertyTransitions(): Dictionary` Returns a dictionary of all property transitions set on the `StyleRule`. Each key is a property name (string) and each value is a [TweenInfo](/docs/reference/engine/datatypes/TweenInfo.md) or a string token that defines the transition's timing for that property. *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Returns:** `Dictionary` — Dictionary of key-value pairs mapping property names to their transition parameters. **GetPropertyTransitions()** This example shows how to retrieve a table of property transitions. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local coreSheet = ReplicatedStorage:FindFirstChild("CoreSheet") -- Get reference to style rule local frameRule = coreSheet.Frame -- Set some transitions frameRule:SetPropertyTransition("BackgroundColor3", TweenInfo.new(1, Enum.EasingStyle.Linear)) frameRule:SetPropertyTransition("BackgroundTransparency", "$DefaultTransition") local transitions = frameRule:GetPropertyTransitions() print(transitions) ``` ### Method: StyleRule:SetDefaultPropertyTransition **Signature:** `StyleRule:SetDefaultPropertyTransition(transitionParams: Variant): ()` Sets or clears a default transition that applies to all properties of the `StyleRule` that don't have an explicit transition set. This is equivalent to calling [SetPropertyTransition()](/docs/reference/engine/classes/StyleRule.md) with `"*"` as the property name. The `transitionParams` parameter accepts a [TweenInfo](/docs/reference/engine/datatypes/TweenInfo.md) that configures the transition's timing. The following [TweenInfo](/docs/reference/engine/datatypes/TweenInfo.md) properties are used: - [Time](/docs/reference/engine/datatypes/TweenInfo.md) — Duration of the transition in seconds. - [EasingStyle](/docs/reference/engine/datatypes/TweenInfo.md) — The easing function used for interpolation. - [EasingDirection](/docs/reference/engine/datatypes/TweenInfo.md) — The direction of the easing function. - [DelayTime](/docs/reference/engine/datatypes/TweenInfo.md) — Delay in seconds before the transition starts. Note that the [RepeatCount](/docs/reference/engine/datatypes/TweenInfo.md) and [Reverses](/docs/reference/engine/datatypes/TweenInfo.md) fields of [TweenInfo](/docs/reference/engine/datatypes/TweenInfo.md) are ignored, as style transitions cannot repeat or reverse automatically. A per-property transition set via [SetPropertyTransition()](/docs/reference/engine/classes/StyleRule.md) takes precedence over the default transition for that property. *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `transitionParams` | `Variant` | | Either a [TweenInfo](/docs/reference/engine/datatypes/TweenInfo.md), a token string defining the default transition timing, or `nil` to remove the default transition. | **Returns:** `()` ### Method: StyleRule:SetProperties **Signature:** `StyleRule:SetProperties(styleProperties: Dictionary): ()` Similar to [SetProperty()](/docs/reference/engine/classes/StyleRule.md) but lets you declare and set multiple properties of the `StyleRule` at once. Each assignment should be a valid property of the affected [GuiObject](/docs/reference/engine/classes/GuiObject.md) or [UIComponent](/docs/reference/engine/classes/UIComponent.md) ([UICorner](/docs/reference/engine/classes/UICorner.md), [UIGradient](/docs/reference/engine/classes/UIGradient.md), etc.), and each assigned value should match its property's value type, for example [Vector2](/docs/reference/engine/datatypes/Vector2.md) for [AnchorPoint](/docs/reference/engine/classes/GuiObject.md) or [Color3](/docs/reference/engine/datatypes/Color3.md) for [BackgroundColor3](/docs/reference/engine/classes/GuiObject.md). Attempts to assign invalid property names such as `"AnchorPt"` or `"BkColor"` will silently fail. Type mismatches such as [CFrame](/docs/reference/engine/datatypes/CFrame.md) for [AnchorPoint](/docs/reference/engine/classes/GuiObject.md) or [UDim2](/docs/reference/engine/datatypes/UDim2.md) for [BackgroundColor3](/docs/reference/engine/classes/GuiObject.md) will also fail and an error will appear in the [Output](/docs/en-us/studio/output.md) window. To set/update just one property of a `StyleRule`, see [SetProperty()](/docs/reference/engine/classes/StyleRule.md). ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local coreSheet = ReplicatedStorage:FindFirstChild("CoreSheet") -- Get reference to style rule local frameRule = coreSheet.Frame -- Set rule properties frameRule:SetProperties({ ["AnchorPoint"] = Vector2.new(0.5, 0), ["BackgroundColor3"] = Color3.new(1, 0, 0.25) }) ``` Note that you can assign **tokens** as property values through the `$` prefix: ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local coreSheet = ReplicatedStorage:FindFirstChild("CoreSheet") local tokensSheet = ReplicatedStorage:FindFirstChild("Tokens") -- Set tokens (attributes) on tokens sheet tokensSheet:SetAttribute("TopCenterAnchor", Vector2.new(0.5, 0)) tokensSheet:SetAttribute("MainBackgroundColor", Color3.new(0.2, 0.2, 0.3)) -- Get reference to style rule local frameRule = coreSheet.Frame -- Set rule properties frameRule:SetProperties({ ["AnchorPoint"] = "$TopCenterAnchor", ["BackgroundColor3"] = "$MainBackgroundColor" }) ``` *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `styleProperties` | `Dictionary` | | Dictionary of key-value pairs defining the properties to set. | **Returns:** `()` ### Method: StyleRule:SetProperty **Signature:** `StyleRule:SetProperty(name: string, value: Variant): ()` Sets a new property (or modifies an existing property) for the `StyleRule`. The `name` parameter should be a valid property of the affected [GuiObject](/docs/reference/engine/classes/GuiObject.md) or [UIComponent](/docs/reference/engine/classes/UIComponent.md) ([UICorner](/docs/reference/engine/classes/UICorner.md), [UIGradient](/docs/reference/engine/classes/UIGradient.md), etc.), and the assigned value should match the property's value type, for example [Vector2](/docs/reference/engine/datatypes/Vector2.md) for [AnchorPoint](/docs/reference/engine/classes/GuiObject.md) or [Color3](/docs/reference/engine/datatypes/Color3.md) for [BackgroundColor3](/docs/reference/engine/classes/GuiObject.md). Attempts to assign invalid property names such as `"AnchorPt"` or `"BkColor"` will silently fail. Type mismatches such as [CFrame](/docs/reference/engine/datatypes/CFrame.md) for [AnchorPoint](/docs/reference/engine/classes/GuiObject.md) or [UDim2](/docs/reference/engine/datatypes/UDim2.md) for [BackgroundColor3](/docs/reference/engine/classes/GuiObject.md) will also fail and an error will appear in the [Output](/docs/en-us/studio/output.md) window. To set multiple properties for a `StyleRule` at once, see [SetProperties()](/docs/reference/engine/classes/StyleRule.md). ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local coreSheet = ReplicatedStorage:FindFirstChild("CoreSheet") -- Get reference to style rule local frameRule = coreSheet.Frame -- Set rule property frameRule:SetProperty("BackgroundColor3", Color3.new(1, 0, 0.25)) ``` Note that you can assign **tokens** as property values through the `$` prefix: ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local coreSheet = ReplicatedStorage:FindFirstChild("CoreSheet") local tokensSheet = ReplicatedStorage:FindFirstChild("Tokens") -- Set new token (attribute) on tokens sheet tokensSheet:SetAttribute("MainBackgroundColor", Color3.new(0.2, 0.2, 0.3)) -- Get reference to style rule local frameRule = coreSheet.Frame -- Set rule property to use the token as its value frameRule:SetProperty("BackgroundColor3, "$MainBackgroundColor") ``` *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name` | `string` | | Property name to set, for example `"BackgroundColor3"`. | | `value` | `Variant` | | Property value to set, for example [Color3.new(1, 0, 0.25)](/docs/reference/engine/datatypes/Color3.md). | **Returns:** `()` ### Method: StyleRule:SetPropertyTransition **Signature:** `StyleRule:SetPropertyTransition(property: string, transitionParams: Variant): ()` Sets or clears the transition for a single property on the `StyleRule`. When a transition is set for a property, any change to that property's styled value will be animated over time instead of applied immediately. The `transitionParams` parameter accepts a [TweenInfo](/docs/reference/engine/datatypes/TweenInfo.md) that configures the transition's timing. The following [TweenInfo](/docs/reference/engine/datatypes/TweenInfo.md) properties are used: - [Time](/docs/reference/engine/datatypes/TweenInfo.md) — Duration of the transition in seconds. - [EasingStyle](/docs/reference/engine/datatypes/TweenInfo.md) — The easing function used for interpolation. - [EasingDirection](/docs/reference/engine/datatypes/TweenInfo.md) — The direction of the easing function. - [DelayTime](/docs/reference/engine/datatypes/TweenInfo.md) — Delay in seconds before the transition starts. Note that the [RepeatCount](/docs/reference/engine/datatypes/TweenInfo.md) and [Reverses](/docs/reference/engine/datatypes/TweenInfo.md) fields of [TweenInfo](/docs/reference/engine/datatypes/TweenInfo.md) are ignored, as style transitions cannot repeat or reverse automatically. To set transitions for multiple properties at once, see [SetPropertyTransitions()](/docs/reference/engine/classes/StyleRule.md). *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `property` | `string` | | String name of the property to set a transition for, for example `"BackgroundColor3"` or `"Size"`. | | `transitionParams` | `Variant` | | A [TweenInfo](/docs/reference/engine/datatypes/TweenInfo.md) or token string defining the transition timing, or `nil` to remove the transition for this property. | **Returns:** `()` **SetPropertyTransition()** ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local coreSheet = ReplicatedStorage:FindFirstChild("CoreSheet") -- Get reference to style rule local frameRule = coreSheet.Frame -- Set a 1-second linear transition on BackgroundColor3 frameRule:SetPropertyTransition("BackgroundColor3", TweenInfo.new( 1, -- Time Enum.EasingStyle.Linear -- EasingStyle )) -- Remove the transition frameRule:SetPropertyTransition("BackgroundColor3", nil) ``` **SetPropertyTransition() Using Tokens** This example shows how you can assign **tokens** as transition values using a string with the `$` prefix. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local coreSheet = ReplicatedStorage:FindFirstChild("CoreSheet") local tokensSheet = ReplicatedStorage:FindFirstChild("Tokens") -- Set tokens (attributes) on tokens sheet tokensSheet:SetAttribute("LinearTransition", TweenInfo.new(1, Enum.EasingStyle.Linear)) -- Get reference to style rule local frameRule = coreSheet.Frame frameRule:SetPropertyTransition("BackgroundColor3", "$LinearTransition") ``` ### Method: StyleRule:SetPropertyTransitions **Signature:** `StyleRule:SetPropertyTransitions(properties: Dictionary): ()` Similar to [SetPropertyTransition()](/docs/reference/engine/classes/StyleRule.md) but lets you declare and set transitions for multiple properties of the `StyleRule` at once. Calling this method **replaces** all existing transitions on the `StyleRule` with the provided set. Each key should be a property name (string) and each value should be a [TweenInfo](/docs/reference/engine/datatypes/TweenInfo.md) or string token defining the transition timing for that property. Passing `nil` as a value for a key excludes that property from the resulting transitions. The following [TweenInfo](/docs/reference/engine/datatypes/TweenInfo.md) properties are used: - [Time](/docs/reference/engine/datatypes/TweenInfo.md) — Duration of the transition in seconds. - [EasingStyle](/docs/reference/engine/datatypes/TweenInfo.md) — The easing function used for interpolation. - [EasingDirection](/docs/reference/engine/datatypes/TweenInfo.md) — The direction of the easing function. - [DelayTime](/docs/reference/engine/datatypes/TweenInfo.md) — Delay in seconds before the transition starts. Note that the [RepeatCount](/docs/reference/engine/datatypes/TweenInfo.md) and [Reverses](/docs/reference/engine/datatypes/TweenInfo.md) fields of [TweenInfo](/docs/reference/engine/datatypes/TweenInfo.md) are ignored, as style transitions cannot repeat or reverse automatically. To set/update just one property transition on a `StyleRule`, see [SetPropertyTransition()](/docs/reference/engine/classes/StyleRule.md). *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `properties` | `Dictionary` | | Dictionary of property names to transition parameters. | **Returns:** `()` **SetPropertyTransitions()** ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local coreSheet = ReplicatedStorage:FindFirstChild("CoreSheet") -- Get reference to style rule local frameRule = coreSheet.Frame -- Sets transitions for two properties at once frameRule:SetPropertyTransitions({ ["BackgroundColor3"] = TweenInfo.new(1, Enum.EasingStyle.Linear), ["BackgroundTransparency"] = TweenInfo.new(0.25, Enum.EasingStyle.Quad, Enum.EasingDirection.Out), }) -- Remove all transitions frameRule:SetPropertyTransitions({}) ``` **SetPropertyTransitions() Using Tokens** This example shows how you can assign **tokens** as transition values using a string with the `$` prefix. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local coreSheet = ReplicatedStorage:FindFirstChild("CoreSheet") local tokenSheet = ReplicatedStorage:FindFirstChild("Tokens") -- Set tokens (attributes) on token sheet tokenSheet:SetAttribute("LinearTransition", TweenInfo.new(1, Enum.EasingStyle.Linear)) tokenSheet:SetAttribute("QuadTransition", TweenInfo.new(0.25, Enum.EasingStyle.Quad)) -- Get reference to style rule local frameRule = coreSheet.Frame -- Set transitions for multiple properties at once frameRule:SetPropertyTransitions({ ["BackgroundColor3"] = "$LinearTransition", ["BackgroundTransparency"] = "$QuadTransition", }) ``` ## Inherited Members ### From [StyleBase](/docs/reference/engine/classes/StyleBase.md) - **Method `GetStyleRules(): List`**: Returns an array of associated StyleRules. - **Method `InsertStyleRule(rule: StyleRule, priority: int?): ()`**: Inserts a new StyleRule into the array of rules. - **Method `SetStyleRules(rules: Instances): ()`**: Similar to InsertStyleRule() but lets - **Event `StyleRulesChanged`**: Fires when one or more StyleRules is explicitly changed ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: StyleSheet last_updated: 2026-06-29T19:34:08Z inherits: - StyleBase - Instance - Object type: class memory_category: Gui summary: "Aggregates StyleRules and can be linked to DataModel trees to apply style properties to instances." --- # Class: StyleSheet > Aggregates [StyleRules](/docs/reference/engine/classes/StyleRule.md) and can be linked to [DataModel](/docs/reference/engine/classes/DataModel.md) > trees to apply style properties to instances. ## Description Aggregates [StyleRules](/docs/reference/engine/classes/StyleRule.md) and can be linked to [DataModel](/docs/reference/engine/classes/DataModel.md) trees to apply style properties to instances. Note that a `StyleSheet` may exist outside the [DataModel](/docs/reference/engine/classes/DataModel.md), but it cannot be derived or linked to a [DataModel](/docs/reference/engine/classes/DataModel.md) tree in such a case. ## Methods ### Method: StyleSheet:GetDerives **Signature:** `StyleSheet:GetDerives(): List` Returns an array of other `StyleSheets` from which the `StyleSheet` is deriving [StyleRules](/docs/reference/engine/classes/StyleRule.md) and token definitions. *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Returns:** `List` — Array of other `StyleSheets`. ### Method: StyleSheet:SetDerives **Signature:** `StyleSheet:SetDerives(derives: Instances): ()` Sets the `StyleSheet` to derive [StyleRules](/docs/reference/engine/classes/StyleRule.md) and token definitions from one or more other `StyleSheets` in the order they are listed. This method spawns the appropriate [StyleDerive](/docs/reference/engine/classes/StyleDerive.md) instances and sets their priorities to establish the specified derivations. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local coreSheet = ReplicatedStorage:FindFirstChild("CoreSheet") -- Create a tokens style sheet local tokensSheet = Instance.new("StyleSheet") tokensSheet.Name = "Tokens" tokensSheet.Parent = ReplicatedStorage -- Set tokens (attributes) on tokens sheet tokensSheet:SetAttribute("LightGray", Color3.new(0.9, 0.9, 0.9)) tokensSheet:SetAttribute("DarkGray", Color3.new(0.2, 0.2, 0.2)) -- Create theme style sheets local lightThemeSheet = Instance.new("StyleSheet") lightThemeSheet.Name = "LightTheme" lightThemeSheet:SetAttribute("Background", "$LightGray") lightThemeSheet.Parent = ReplicatedStorage local darkThemeSheet = Instance.new("StyleSheet") darkThemeSheet.Name = "DarkTheme" darkThemeSheet:SetAttribute("Background", "$DarkGray") darkThemeSheet.Parent = ReplicatedStorage -- Set theme sheets to derive from tokens sheet lightThemeSheet:SetDerives({ tokensSheet }) darkThemeSheet:SetDerives({ tokensSheet }) local themeDerive = Instance.new("StyleDerive") themeDerive.Parent = coreSheet themeDerive.StyleSheet = lightThemeSheet -- Function to dynamically change the derived theme for the core sheet local function changeTheme() if themeDerive.StyleSheet == lightThemeSheet then themeDerive.StyleSheet = darkThemeSheet elseif themeDerive.StyleSheet == darkThemeSheet then themeDerive.StyleSheet = lightThemeSheet end end ``` Note that if you've created a design using the [Style Editor](/docs/en-us/ui/styling/editor.md), the **StyleSheet** sheet in the **Design** folder of [ReplicatedStorage](/docs/reference/engine/classes/ReplicatedStorage.md) will contain a [StyleDerive](/docs/reference/engine/classes/StyleDerive.md) to the **BaseStyleSheet** also in the **Design** folder. When setting derives with `SetDerives()`, be sure to include the base style sheet in the spot of least priority in relation to other `StyleSheets` in the `derives` array. *Security: None · Thread Safety: Unsafe · Capabilities: UI* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `derives` | `Instances` | | Array of other `StyleSheets` to derive [StyleRules](/docs/reference/engine/classes/StyleRule.md) and token definitions from. | **Returns:** `()` ## Inherited Members ### From [StyleBase](/docs/reference/engine/classes/StyleBase.md) - **Method `GetStyleRules(): List`**: Returns an array of associated StyleRules. - **Method `InsertStyleRule(rule: StyleRule, priority: int?): ()`**: Inserts a new StyleRule into the array of rules. - **Method `SetStyleRules(rules: Instances): ()`**: Similar to InsertStyleRule() but lets - **Event `StyleRulesChanged`**: Fires when one or more StyleRules is explicitly changed ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: SunRaysEffect last_updated: 2026-06-29T19:34:08Z inherits: - PostEffect - Instance - Object type: class memory_category: Instances summary: "Renders dynamic rays from the sun." --- # Class: SunRaysEffect > Renders dynamic rays from the sun. ## Description The **SunRaysEffect** renders a halo of light around sun. The halo is shaped/blocked by world objects between the [Workspace.CurrentCamera](/docs/reference/engine/classes/Workspace.md) and the sun. Like other post-processing effects, **SunRaysEffect** will only work while [Enabled](/docs/reference/engine/classes/PostEffect.md) and when parented to [Lighting](/docs/reference/engine/classes/Lighting.md) or [Workspace.CurrentCamera](/docs/reference/engine/classes/Workspace.md). Also, it may not render on low-end devices, and it may render differently depending on your Studio settings (see the **Quality Level** settings in **Rendering** → **Performance**). For more details on this effect and others, see [Post-Processing Effects](/docs/en-us/environment/post-processing-effects.md). ## Properties ### Property: SunRaysEffect.Intensity ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Environment" ] } ``` Intensity determines the opacity of the sun rays. Values closer to 0 are less visible, while values closer to 1 become more visible. ### Property: SunRaysEffect.Spread ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "State", "capabilities": [ "Environment" ] } ``` Spread determines how wide the sun rays will spread across the sky. Its value should be set between 0 and 1 as values outside that range have undefined behavior. ## Inherited Members ### From [PostEffect](/docs/reference/engine/classes/PostEffect.md) - **Property `Enabled`** (`boolean`): Toggles whether or not the PostEffect is enabled. ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: SurfaceAppearance last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances summary: "An object that allows developers to override the appearance of a MeshPart with advanced graphics options." --- # Class: SurfaceAppearance > An object that allows developers to override the appearance of a MeshPart with > advanced graphics options. ## Description **SurfaceAppearance** objects let you override the appearance of a [MeshPart](/docs/reference/engine/classes/MeshPart.md) with advanced graphics options. Most notably, it can apply a set of **Physically‑Based Rendering** (PBR) texture images, or maps, on a single object. Combining multiple texture maps can more accurately simulate color, roughness, and reflectivity in any lighting environment and can enhance the visual elements of your assets and environment; see [PBR Textures](/docs/en-us/art/modeling/surface-appearance.md) for more details. ![A realistic leafy bush](/assets/modeling/surface-appearance/SurfaceAppearance-Example-1.jpg)![A realistic mossy rock](/assets/modeling/surface-appearance/SurfaceAppearance-Example-3.jpg) Appearance of this object on a [MeshPart](/docs/reference/engine/classes/MeshPart.md) depends on the user's device and graphics quality level. For best results, you may want to preview your content with different quality level settings. Note that most [SurfaceAppearance](/docs/reference/engine/classes/SurfaceAppearance.md) properties cannot be modified by scripts, as the necessary pre-processing is usually too expensive during runtime. ## Properties ### Property: SurfaceAppearance.AlphaMode ```json { "type": "AlphaMode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` This property determines how the alpha channel of the [SurfaceAppearance.ColorMap](/docs/reference/engine/classes/SurfaceAppearance.md) is used. When set to [Transparency](/docs/reference/engine/enums/AlphaMode.md) and the [MeshPart.Transparency](/docs/reference/engine/classes/MeshPart.md) is set to `0`, opaque pixels in the [ColorMap](/docs/reference/engine/classes/SurfaceAppearance.md) will render as completely opaque in the 3D scene. This combination works better with depth-based effects and occlusion. When set to [Transparency](/docs/reference/engine/enums/AlphaMode.md) and the [MeshPart.Transparency](/docs/reference/engine/classes/MeshPart.md) is set to at least `0.02`, a different blending method is used that can better represent smooth transparency gradients and soft edges. This combination does not support all effects and occlusion may not be perfect. See [here](/docs/en-us/art/modeling/surface-appearance.md#alpha-modes) for more information. ### Property: SurfaceAppearance.Color ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` Applies a tint to your existing colormap. Set directly with color picker or programmatically with [Color3](/docs/reference/engine/datatypes/Color3.md). ### Property: SurfaceAppearance.ColorMap ```json { "type": "ContentId", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` This property determines the color and opacity of the surface. This texture is sometimes called the **albedo** texture. The alpha channel of this texture controls its opacity, which behaves differently based on the [SurfaceAppearance.AlphaMode](/docs/reference/engine/classes/SurfaceAppearance.md) setting. See [here](/docs/en-us/art/modeling/surface-appearance.md#color-albedo) for more information. ### Property: SurfaceAppearance.EmissiveMaskContent ```json { "type": "Content", "access": "ReadOnly", "security": { "read": "None", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` This property determines the apparent emissivity across the surface. An emissive mask is a grayscale image where black pixels correspond to no emissivity, and white pixels correspond to full emissivity. The emissive contribution gets added to total lighting contribution across the surface, and the sum gets multiplied by the albedo texture, which would be sampled from [ColorMap](/docs/reference/engine/classes/SurfaceAppearance.md) if provided. ### Property: SurfaceAppearance.EmissiveStrength ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` This value is a multiplier to control how strong the emissive contribution is across the surface. If the final contribution of emissives goes beyond supported dynamic range, it will get clamped; this could result in emissive contribution appearing all white when using large emissive strength values. Since this value is a multiplier, the maximum value to prevent clamping depends on all of the other contributing values, including [EmissiveTint](/docs/reference/engine/classes/SurfaceAppearance.md), [ColorMap](/docs/reference/engine/classes/SurfaceAppearance.md) and combined lighting contribution. ### Property: SurfaceAppearance.EmissiveTint ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` This value is an RGB color for tinting emissive contribution. ### Property: SurfaceAppearance.MetalnessMap ```json { "type": "ContentId", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` This property determines which parts of the surface are metal or non-metal. The metalness map is a grayscale image where black pixels correspond to non-metals and white pixels correspond to metals. Metals only reflect light the same color as the metal, and they reflect much more light than non-metals. Most pixels in a metalness map will be either pure black or pure white, while values in between can be used to simulate dirt or grunge on top of an underlying metal area. When [Lighting.EnvironmentSpecularScale](/docs/reference/engine/classes/Lighting.md) is 0, metalness has no effect. For the most realistic reflections, it's recommended to set [Lighting.EnvironmentSpecularScale](/docs/reference/engine/classes/Lighting.md) and [Lighting.EnvironmentDiffuseScale](/docs/reference/engine/classes/Lighting.md) to 1, as well as [Lighting.Ambient](/docs/reference/engine/classes/Lighting.md) and [Lighting.OutdoorAmbient](/docs/reference/engine/classes/Lighting.md) to `(0, 0, 0)`. See [here](/docs/en-us/art/modeling/surface-appearance.md#metalness) for more information. ### Property: SurfaceAppearance.NormalMap ```json { "type": "ContentId", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` This property modifies the lighting of the surface by adding bumps, dents, cracks, and curves, without adding more polygons. The normal map is an RGB image that modifies the surface's normal vector used for lighting calculations. Its **R**, **G**, and **B** channels correspond to the **X**, **Y**, and **Z** components of the local surface vector respectively, and byte values of 0 and 255 for each channel correspond linearly to normal vector components of -1 and 1.016 respectively. This range is stretched slightly from -1 to 1 so that a byte value of 127 maps to exactly 0. The normal vector's **Z** axis is always defined as the direction of the underlying mesh's normal. A uniform `(127, 127, 255)` image translates to a completely flat normal map where the normal is everywhere perpendicular to the mesh surface; this format is referred to as a "tangent space" normal map. Note that Roblox does not support world space or object space normal maps. Incorrectly flipped normal components can make bumps appear like indents. If you import a normal map and notice the lighting looks off, you may need to invert the **G** channel of the image. The **X** and **Y** axes of the tangent space frame correspond to the **X** and **Y** directions in the image after it's transformed by the mesh UVs. If you view your normal map in an image editor as if it were displayed on a surface, normals pointing towards the right side of the screen should appear more red, and normals pointing towards the top side of your screen should appear more green. The terms "DirectX format" and "OpenGL format" are sometimes used to describe whether the **G** channel of the normal map is inverted or not (Roblox expects the OpenGL format). Roblox also expects imported meshes to include tangents. Modeling software may also refer to this as "tangent space" information. If you apply a normal map and it does not seem to make any visual difference, you may need to re-export your mesh along with its tangent information from the modeling software. See [here](/docs/en-us/art/modeling/surface-appearance.md#normal) for more information. ### Property: SurfaceAppearance.ResampleMode ```json { "type": "ResamplerMode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` ### Property: SurfaceAppearance.RoughnessMap ```json { "type": "ContentId", "access": "ReadOnly", "security": { "read": "PluginSecurity", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` This property determines the apparent roughness across the surface. The roughness map is a grayscale image where black pixels correspond to a maximally smooth surface, and white pixels correspond to a maximally rough surface. Note that surface roughness works on a very small scale. Reflections on smooth surfaces are sharp and concentrated, while reflections on rough surfaces are more blurry and dispersed. See [here](/docs/en-us/art/modeling/surface-appearance.md#roughness) for more information. ### Property: SurfaceAppearance.TexturePack ```json { "type": "ContentId", "access": "ReadOnly", "security": { "read": "RobloxScriptSecurity", "write": "RobloxEngineSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` ### Property: SurfaceAppearance.ColorMapContent *(hidden)* ```json { "type": "Content", "access": "ReadOnly", "security": { "read": "None", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` ### Property: SurfaceAppearance.MetalnessMapContent *(hidden)* ```json { "type": "Content", "access": "ReadOnly", "security": { "read": "None", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` ### Property: SurfaceAppearance.NormalMapContent *(hidden)* ```json { "type": "Content", "access": "ReadOnly", "security": { "read": "None", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` ### Property: SurfaceAppearance.RoughnessMapContent *(hidden)* ```json { "type": "Content", "access": "ReadOnly", "security": { "read": "None", "write": "PluginSecurity" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: SurfaceGui last_updated: 2026-06-29T19:34:08Z inherits: - SurfaceGuiBase - LayerCollector - GuiBase2d - GuiBase - Instance - Object type: class memory_category: Instances summary: "Container for GuiObjects that are rendered on the surface of a part." --- # Class: SurfaceGui > Container for GuiObjects that are rendered on the surface of a part. ## Description [SurfaceGui](/docs/reference/engine/classes/SurfaceGui.md) allows for the rendering of UI objects onto a part's surface in the 3D world while also allowing for basic user interaction to occur. Similar to [Decals](/docs/reference/engine/classes/Decal.md) and [Textures](/docs/reference/engine/classes/Texture.md), UI objects such as [TextLabels](/docs/reference/engine/classes/TextLabel.md) and [ImageLabels](/docs/reference/engine/classes/ImageLabel.md) parented to a [SurfaceGui](/docs/reference/engine/classes/SurfaceGui.md) face the same direction as the surface they're on, editable through the [Face](/docs/reference/engine/classes/SurfaceGui.md) property. ![SurfaceGui on a 3D part in the place with an ImageLabel child to depict a screen console.](/assets/ui/in-experience/SurfaceGui-Diagram.jpg) Note that interactive UI elements like [ImageButtons](/docs/reference/engine/classes/ImageButton.md) and [TextButtons](/docs/reference/engine/classes/TextButton.md) inside a [SurfaceGui](/docs/reference/engine/classes/SurfaceGui.md) will only receive user input if they are parented to the [PlayerGui](/docs/reference/engine/classes/PlayerGui.md), typically via placing the [SurfaceGui](/docs/reference/engine/classes/SurfaceGui.md) inside [StarterGui](/docs/reference/engine/classes/StarterGui.md) (the [Adornee](/docs/reference/engine/classes/SurfaceGui.md) property can be used to target a part in the 3D world while the [SurfaceGui](/docs/reference/engine/classes/SurfaceGui.md) itself remains in the [PlayerGui](/docs/reference/engine/classes/PlayerGui.md)). Additionally, the part's [CanQuery](/docs/reference/engine/classes/BasePart.md) property must be `true` for the interactive UI element to receive input. See [In-Experience UI](/docs/en-us/ui/in-experience-containers.md#surface-ui) for a guide on working with [SurfaceGui](/docs/reference/engine/classes/SurfaceGui.md) containers. ##### Caching Behavior To help improve performance, the appearance of a [SurfaceGui](/docs/reference/engine/classes/SurfaceGui.md) is cached until one of the following occurs, after which its appearance will be recomputed on the next rendering frame. - A descendant is added to or removed from the [SurfaceGui](/docs/reference/engine/classes/SurfaceGui.md). - A property of a descendant of the [SurfaceGui](/docs/reference/engine/classes/SurfaceGui.md) changes. - A property of the [SurfaceGui](/docs/reference/engine/classes/SurfaceGui.md) itself changes. ## Properties ### Property: SurfaceGui.AlwaysOnTop ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` This property determines whether the [SurfaceGui](/docs/reference/engine/classes/SurfaceGui.md) will always render on top of other 3D objects. When set to `false` (default), the [SurfaceGui](/docs/reference/engine/classes/SurfaceGui.md) renders like other 3D content and is occluded by other 3D objects. When set to `true`, the [SurfaceGui](/docs/reference/engine/classes/SurfaceGui.md) always renders on top of 3D content and the appearance changes significantly: - Colors match how they appear inside a [ScreenGui](/docs/reference/engine/classes/ScreenGui.md). - Text may appear sharper on high DPI devices. - [LightInfluence](/docs/reference/engine/classes/SurfaceGui.md) is treated as though it's `0`. - [Brightness](/docs/reference/engine/classes/SurfaceGui.md) has no effect. ### Property: SurfaceGui.Brightness ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` This property determines the factor by which the [SurfaceGui](/docs/reference/engine/classes/SurfaceGui.md) container's light is scaled when [LightInfluence](/docs/reference/engine/classes/SurfaceGui.md) is `0`. By default, this property is `1` and can be set to any number between `0` and `1000`. By modifying this property, the apparent brightness of a [SurfaceGui](/docs/reference/engine/classes/SurfaceGui.md) can be better matched to its environment. For instance, a video billboard can be brightened inside a dark room by increasing [Brightness](/docs/reference/engine/classes/SurfaceGui.md) to `10`. Note that [Brightness](/docs/reference/engine/classes/SurfaceGui.md) is inaccessible in Studio and has no effect when either [LightInfluence](/docs/reference/engine/classes/SurfaceGui.md) is `1` or [AlwaysOnTop](/docs/reference/engine/classes/SurfaceGui.md) is `true`. ### Property: SurfaceGui.CanvasSize ```json { "type": "Vector2", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Sizing", "capabilities": [ "UI" ] } ``` The size of a "virtual screen" in "virtual pixels" which makes [SurfaceGuis](/docs/reference/engine/classes/SurfaceGui.md) pixel-to-pixel compatible with [ScreenGuis](/docs/reference/engine/classes/ScreenGui.md). ### Property: SurfaceGui.ClipsDescendants ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Behavior", "capabilities": [ "UI" ] } ``` When set to `true` (default), portions of [GuiObjects](/docs/reference/engine/classes/GuiObject.md) that fall outside of the [SurfaceGui](/docs/reference/engine/classes/SurfaceGui.md) canvas borders will not be drawn. Even when this property is `false`, [GuiObjects](/docs/reference/engine/classes/GuiObject.md) that are **completely** outside of the canvas will not render. ### Property: SurfaceGui.LightInfluence ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` Controls how much the [SurfaceGui](/docs/reference/engine/classes/SurfaceGui.md) is influenced by environmental lighting, in a range from `0` to `1`. Setting this to `1` means that surrounding lighting has complete control over the appearance, while setting it to `0` means that the lighting has no effect. ### Property: SurfaceGui.MaxDistance ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` This property controls how far from the camera the [SurfaceGui](/docs/reference/engine/classes/SurfaceGui.md) will be displayed before it stops rendering. A value of `0` means there is no limit and it will render infinitely far away. The default value of `1000` works fine for most cases. For [SurfaceGuis](/docs/reference/engine/classes/SurfaceGui.md) that appear outdoors, it's recommended that [MaxDistance](/docs/reference/engine/classes/SurfaceGui.md) is high enough to ensure that the container's UI is sufficiently small on the screen when it appears or disappears, minimizing the sudden pop‑in/out effect. ### Property: SurfaceGui.PixelsPerStud ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Sizing", "capabilities": [ "UI" ] } ``` This property determines the density of pixels used for each world-space stud to render the contents of the [SurfaceGui](/docs/reference/engine/classes/SurfaceGui.md). Higher values will cause the various [GuiObjects](/docs/reference/engine/classes/GuiObject.md) within to appear smaller if they are kept the same size. Conversely, lower values will cause objects to appear larger. However, if the [GuiObjects](/docs/reference/engine/classes/GuiObject.md) are scaled proportionally through use of [UIScale](/docs/reference/engine/classes/UIScale.md), [GuiObject.Size](/docs/reference/engine/classes/GuiObject.md), [TextLabel.TextSize](/docs/reference/engine/classes/TextLabel.md), or similar, this property allows for higher definition to be used. It's important to select a value based on how far away you expect a player to view the [SurfaceGui](/docs/reference/engine/classes/SurfaceGui.md). Also be mindful that a large pixel density could negatively affect performance if the face of the adorned part is large enough. ### Property: SurfaceGui.SizingMode ```json { "type": "SurfaceGuiSizingMode", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Sizing", "capabilities": [ "UI" ] } ``` When set to [SurfaceGuiSizingMode.PixelsPerStud](/docs/reference/engine/enums/SurfaceGuiSizingMode.md) (default), the [SurfaceGui](/docs/reference/engine/classes/SurfaceGui.md) renders with a variable size based on [PixelsPerStud](/docs/reference/engine/classes/SurfaceGui.md) and the surface's size in studs. When set to [SurfaceGuiSizingMode.FixedSize](/docs/reference/engine/enums/SurfaceGuiSizingMode.md), the [SurfaceGui](/docs/reference/engine/classes/SurfaceGui.md) renders with a fixed size set through [CanvasSize](/docs/reference/engine/classes/SurfaceGui.md). ### Property: SurfaceGui.ToolPunchThroughDistance ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` Sets the distance in which left clicking starts acting on the [SurfaceGui](/docs/reference/engine/classes/SurfaceGui.md) instead of for the held [Tool](/docs/reference/engine/classes/Tool.md). If a character is within this distance of the [SurfaceGui](/docs/reference/engine/classes/SurfaceGui.md), the [Tool](/docs/reference/engine/classes/Tool.md) will not activate on click. ### Property: SurfaceGui.ZOffset ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "UI" ] } ``` Layers this [SurfaceGui](/docs/reference/engine/classes/SurfaceGui.md) in relation to others on the same face (changing this does not visually "lift" or "sink" a [SurfaceGui](/docs/reference/engine/classes/SurfaceGui.md) from the surface). ## Inherited Members ### From [SurfaceGuiBase](/docs/reference/engine/classes/SurfaceGuiBase.md) - **Property `Active`** (`boolean`): - **Property `Adornee`** (`Instance`): BasePart on which to apply the SurfaceGui, overriding the - **Property `Face`** (`NormalId`): NormalId face upon which to apply the SurfaceGui. ### From [LayerCollector](/docs/reference/engine/classes/LayerCollector.md) - **Property `Enabled`** (`boolean`): Toggles the visibility of this LayerCollector. - **Property `ResetOnSpawn`** (`boolean`): Determines if the LayerCollector resets (deletes itself and - **Property `ZIndexBehavior`** (`ZIndexBehavior`): Controls how GuiObject.ZIndex behaves on all descendants of this - **Method `GetLayoutNodeTree(): Dictionary`**: *(deprecated)* ### From [GuiBase2d](/docs/reference/engine/classes/GuiBase2d.md) - **Property `AbsolutePosition`** (`Vector2`): Describes the actual screen position of a GuiBase2d element, in - **Property `AbsoluteRotation`** (`float`): Describes the actual screen rotation of a GuiBase2d element, in - **Property `AbsoluteSize`** (`Vector2`): Describes the actual screen size of a GuiBase2d element, in - **Property `AutoLocalize`** (`boolean`): When set to `true`, localization will be applied to this GuiBase2d - **Property `Localize`** (`boolean`): Automatically set to true when a localization table's *(deprecated, hidden)* - **Property `RootLocalizationTable`** (`LocalizationTable`): A reference to a LocalizationTable to be used to apply automated - **Property `SelectionBehaviorDown`** (`SelectionBehavior`): Customizes gamepad selection behavior in the down direction. - **Property `SelectionBehaviorLeft`** (`SelectionBehavior`): Customizes gamepad selection behavior in the left direction. - **Property `SelectionBehaviorRight`** (`SelectionBehavior`): Customizes gamepad selection behavior in the right direction. - **Property `SelectionBehaviorUp`** (`SelectionBehavior`): Customizes gamepad selection behavior in the up direction. - **Property `SelectionGroup`** (`boolean`): Allows customization of gamepad selection movement. - **Event `SelectionChanged`**: Fires when the gamepad selection moves to, leaves, or changes within the ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: SurfaceGuiBase last_updated: 2026-06-29T19:34:08Z inherits: - LayerCollector - GuiBase2d - GuiBase - Instance - Object type: class memory_category: Instances tags: - NotCreatable --- # Class: SurfaceGuiBase ## Properties ### Property: SurfaceGuiBase.Active ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` This property determines whether the [SurfaceGui](/docs/reference/engine/classes/SurfaceGui.md) will sink input, for example if the parent or [Adornee](/docs/reference/engine/classes/SurfaceGuiBase.md) part contains a [ClickDetector](/docs/reference/engine/classes/ClickDetector.md) class like [DragDetector](/docs/reference/engine/classes/DragDetector.md). Note that interactive UI elements like [ImageButtons](/docs/reference/engine/classes/ImageButton.md) and [TextButtons](/docs/reference/engine/classes/TextButton.md) inside a [SurfaceGui](/docs/reference/engine/classes/SurfaceGui.md) will only receive user input if they are parented to the [PlayerGui](/docs/reference/engine/classes/PlayerGui.md) (the [SurfaceGui.Adorneee](/docs/reference/engine/classes/SurfaceGui.md) property can be used to target a part in the 3D world while the [SurfaceGui](/docs/reference/engine/classes/SurfaceGui.md) itself remains in the [PlayerGui](/docs/reference/engine/classes/PlayerGui.md)). Additionally, the part's [CanQuery](/docs/reference/engine/classes/BasePart.md) property must be `true` for the interactive UI element to receive input. ### Property: SurfaceGuiBase.Adornee ```json { "type": "Instance", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` [BasePart](/docs/reference/engine/classes/BasePart.md) on which to apply the [SurfaceGui](/docs/reference/engine/classes/SurfaceGui.md), overriding the default parent association. ### Property: SurfaceGuiBase.Face ```json { "type": "NormalId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "UI" ] } ``` [NormalId](/docs/reference/engine/enums/NormalId.md) face of the [SurfaceGui](/docs/reference/engine/classes/SurfaceGui.md) parent (or its [Adornee](/docs/reference/engine/classes/SurfaceGuiBase.md)) upon which to apply the [SurfaceGui](/docs/reference/engine/classes/SurfaceGui.md). ## Inherited Members ### From [LayerCollector](/docs/reference/engine/classes/LayerCollector.md) - **Property `Enabled`** (`boolean`): Toggles the visibility of this LayerCollector. - **Property `ResetOnSpawn`** (`boolean`): Determines if the LayerCollector resets (deletes itself and - **Property `ZIndexBehavior`** (`ZIndexBehavior`): Controls how GuiObject.ZIndex behaves on all descendants of this - **Method `GetLayoutNodeTree(): Dictionary`**: *(deprecated)* ### From [GuiBase2d](/docs/reference/engine/classes/GuiBase2d.md) - **Property `AbsolutePosition`** (`Vector2`): Describes the actual screen position of a GuiBase2d element, in - **Property `AbsoluteRotation`** (`float`): Describes the actual screen rotation of a GuiBase2d element, in - **Property `AbsoluteSize`** (`Vector2`): Describes the actual screen size of a GuiBase2d element, in - **Property `AutoLocalize`** (`boolean`): When set to `true`, localization will be applied to this GuiBase2d - **Property `Localize`** (`boolean`): Automatically set to true when a localization table's *(deprecated, hidden)* - **Property `RootLocalizationTable`** (`LocalizationTable`): A reference to a LocalizationTable to be used to apply automated - **Property `SelectionBehaviorDown`** (`SelectionBehavior`): Customizes gamepad selection behavior in the down direction. - **Property `SelectionBehaviorLeft`** (`SelectionBehavior`): Customizes gamepad selection behavior in the left direction. - **Property `SelectionBehaviorRight`** (`SelectionBehavior`): Customizes gamepad selection behavior in the right direction. - **Property `SelectionBehaviorUp`** (`SelectionBehavior`): Customizes gamepad selection behavior in the up direction. - **Property `SelectionGroup`** (`boolean`): Allows customization of gamepad selection movement. - **Event `SelectionChanged`**: Fires when the gamepad selection moves to, leaves, or changes within the ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: SurfaceLight last_updated: 2026-06-29T19:34:08Z inherits: - Light - Instance - Object type: class memory_category: Instances summary: "A light source that emits illumination of a specified color and brightness from a face for a specified range." --- # Class: SurfaceLight > A light source that emits illumination of a specified color and brightness > from a face for a specified range. ## Description A SurfaceLight is a light source that emits illumination of a specified [Light.Color](/docs/reference/engine/classes/Light.md) and [Light.Brightness](/docs/reference/engine/classes/Light.md) from a [SurfaceLight.Face](/docs/reference/engine/classes/SurfaceLight.md) for a specified [SurfaceLight.Range](/docs/reference/engine/classes/SurfaceLight.md). In order for a SurfaceLight to provide illumination, it must be the direct child of a [BasePart](/docs/reference/engine/classes/BasePart.md) or [Attachment](/docs/reference/engine/classes/Attachment.md) (the part or attachment itself must be a descendant of the Workspace). If a SurfaceLight is parented to a part, then the light will emanate from the part's selected face(s). If parented to an attachment SurfaceLight is equivalent to a [SpotLight](/docs/reference/engine/classes/SpotLight.md). For more light types, please see the **see also** section. ## See Also - [PointLight](/docs/reference/engine/classes/PointLight.md) - [SpotLight](/docs/reference/engine/classes/SpotLight.md) ## Code Samples **Creating a New Surface Light** This example creates a new anchored `BasePart` named _Part_ at the position `{0, 0, 0}`. It then creates a new surface light with `brightness` of 1, `Color3` `color` of `{255/255, 255/255, 255/255}` (white) and `range` of 16 studs. The surface light's parent is set to the BasePart we created. To view the light, navigate to the part at `{0, 0, 0}` or move the _Part_ created to a location visible to the player. Please note that the properties of the created surface light can easily be changed by modifying the property values in the code sample below. Additionally, if you have an existing surface light, you can also create a similar script that modifies that surface light instead of creating a new BasePart and light. ```lua local part = Instance.new("Part") part.Anchored = true part.Position = Vector3.new(0, 0, 0) part.Parent = workspace local light = Instance.new("SurfaceLight") light.Color = Color3.fromRGB(255, 255, 255) light.Brightness = 1 light.Range = 16 light.Parent = part ``` ## Properties ### Property: SurfaceLight.Angle ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` The angle of which the light is shone from the SurfaceLight. ### Property: SurfaceLight.Face ```json { "type": "NormalId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` Sets the side of the parent that the SurfaceLight comes from. ### Property: SurfaceLight.Range ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Basic" ] } ``` The distance from the SurfaceLight's face that will illuminate. ## Inherited Members ### From [Light](/docs/reference/engine/classes/Light.md) - **Property `Brightness`** (`float`): Sets how bright the emitted light is, defaults to 1. - **Property `Color`** (`Color3`): The color of the emitted light. - **Property `Enabled`** (`boolean`): If set to true, light will be emitted from the source object. - **Property `Shadows`** (`boolean`): If set to true, will project shadows if light is blocked by an obstacle. ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: SurfaceSelection last_updated: 2026-06-29T19:34:08Z inherits: - PartAdornment - GuiBase3d - GuiBase - Instance - Object type: class memory_category: Instances summary: "Highlights a face of a surface in a configurable color." --- # Class: SurfaceSelection > Highlights a face of a surface in a configurable color. ## Description A `SurfaceSelection` highlights a particular face ([TargetSurface](/docs/reference/engine/classes/SurfaceSelection.md)) of its [Adornee](/docs/reference/engine/classes/PartAdornment.md). The highlight's color is configurable using the [Color3](/docs/reference/engine/classes/GuiBase3d.md) property. The `SurfaceSelection` object is typically used by [Plugin](/docs/reference/engine/classes/Plugin.md) when the user is selecting the face of a [BasePart](/docs/reference/engine/classes/BasePart.md). ## Properties ### Property: SurfaceSelection.TargetSurface ```json { "type": "NormalId", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Basic", "UI" ] } ``` Sets which side the SurfaceSelection will appear on, on the adorned [BasePart](/docs/reference/engine/classes/BasePart.md). ## Inherited Members ### From [PartAdornment](/docs/reference/engine/classes/PartAdornment.md) - **Property `Adornee`** (`BasePart`): Sets the object to adorn to. ### From [GuiBase3d](/docs/reference/engine/classes/GuiBase3d.md) - **Property `Color`** (`BrickColor`): Sets the color of a GUI object. *(deprecated, hidden)* - **Property `Color3`** (`Color3`): Sets the color of this GuiBase3d object. - **Property `Transparency`** (`float`): Sets the transparency of this GuiBase3d object. - **Property `Visible`** (`boolean`): Determines whether this GuiBase3d object and its descendants will ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: SwimController last_updated: 2026-06-29T19:34:08Z inherits: - ControllerBase - Instance - Object type: class memory_category: Instances --- # Class: SwimController ## Properties ### Property: SwimController.PitchMaxTorque ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Balance", "capabilities": [ "Basic" ], "simulationAccess": true } ``` The maximum torque used to rotate on the local **X** axis to the desired pitch orientation. Has no effect if [ControllerBase.RigidityEnabled](/docs/reference/engine/classes/ControllerBase.md) is true. ### Property: SwimController.PitchSpeedFactor ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Balance", "capabilities": [ "Basic" ], "simulationAccess": true } ``` Multiplied by [ControllerManager.BaseTurnSpeed](/docs/reference/engine/classes/ControllerManager.md) to determine the maximum angular velocity rotating on the local **X** axis. Has no effect if [ControllerBase.RigidityEnabled](/docs/reference/engine/classes/ControllerBase.md) is true. ### Property: SwimController.RollMaxTorque ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Balance", "capabilities": [ "Basic" ], "simulationAccess": true } ``` The maximum torque applied to rotate on the local **Z** axis to the desired roll orientation. Has no effect if [ControllerBase.RigidityEnabled](/docs/reference/engine/classes/ControllerBase.md) is true. ### Property: SwimController.RollSpeedFactor ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Balance", "capabilities": [ "Basic" ], "simulationAccess": true } ``` Multiplied by [ControllerManager.BaseTurnSpeed](/docs/reference/engine/classes/ControllerManager.md) to determine the maximum angular velocity rotating on the local **Z** axis. Has no effect if [ControllerBase.RigidityEnabled](/docs/reference/engine/classes/ControllerBase.md) is true. ## Inherited Members ### From [ControllerBase](/docs/reference/engine/classes/ControllerBase.md) - **Property `Active`** (`boolean`): - **Property `BalanceRigidityEnabled`** (`boolean`): - **Property `MoveSpeedFactor`** (`float`): The value multiplied by the ControllerManager.BaseMoveSpeed. ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: SyncScriptBuilder last_updated: 2026-06-29T19:34:08Z inherits: - ScriptBuilder - Instance - Object type: class memory_category: Instances tags: - NotCreatable - NotReplicated --- # Class: SyncScriptBuilder ## Properties ### Property: SyncScriptBuilder.CompileTarget ```json { "type": "CompileTarget", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: SyncScriptBuilder.CoverageInfo ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: SyncScriptBuilder.DebugInfo ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: SyncScriptBuilder.PackAsSource ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data" } ``` ### Property: SyncScriptBuilder.RawBytecode ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data" } ``` > **Deprecated:** This property should not be used for new work. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: TaskScheduler last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated summary: "Collection of settings for the _Task Scheduler_ feature." --- # Class: TaskScheduler > Collection of settings for the _Task Scheduler_ feature. ## Description TaskScheduler is a read-only settings class responsible for the Task Scheduler feature. Can be found in Roblox Studio's settings with the name _Task Scheduler_. ## Properties ### Property: TaskScheduler.SchedulerDutyCycle ```json { "type": "double", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Diagnostics", "capabilities": [ "Basic" ] } ``` The average time divided by the average interval of the duty cycle. ### Property: TaskScheduler.SchedulerRate ```json { "type": "double", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Diagnostics", "capabilities": [ "Basic" ] } ``` The current average rate of the task scheduler. ### Property: TaskScheduler.ThreadPoolConfig ```json { "type": "ThreadPoolConfig", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Configuration", "capabilities": [ "Basic" ] } ``` The specified thread pooling configuration for the task scheduler. ### Property: TaskScheduler.ThreadPoolSize ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Diagnostics", "capabilities": [ "Basic" ] } ``` The current size of the thread pool. ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Team last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances summary: "The Team class represents a faction in a Roblox place. The only valid parent for a Team is in the Teams service." --- # Class: Team > The [Team](/docs/reference/engine/classes/Team.md) class represents a faction in a Roblox place. The only valid > parent for a Team is in the [Teams](/docs/reference/engine/classes/Teams.md) service. ## Description The [Team](/docs/reference/engine/classes/Team.md) class represents a faction in a Roblox place. The only valid parent for a Team is in the [Teams](/docs/reference/engine/classes/Teams.md) service. Teams offer a range of features that are useful to developers that can be divided into two rough groups: - Features that work 'out of the box' - Features developers can program into their game. **Built-in Team Behavior** The following functionality of Teams exists by default and does not require the developer to program any custom behavior. - When part of a Team, the name above a player's character [Model](/docs/reference/engine/classes/Model.md) will be colored to the [Team.TeamColor](/docs/reference/engine/classes/Team.md) - Changing [Player.TeamColor](/docs/reference/engine/classes/Player.md) will cause [Player.Team](/docs/reference/engine/classes/Player.md) to switch to the Team with the corresponding [Team.TeamColor](/docs/reference/engine/classes/Team.md) - When using the default player list users will be grouped and displayed together as a team - Setting [Player.Neutral](/docs/reference/engine/classes/Player.md) to true will cause the [Player](/docs/reference/engine/classes/Player.md) to be disassociated with the team, but it will not change [Player.Team](/docs/reference/engine/classes/Player.md) or [Player.TeamColor](/docs/reference/engine/classes/Player.md) - When a [Player](/docs/reference/engine/classes/Player.md) joins a game, they will be allocated to the team with [Team.AutoAssignable](/docs/reference/engine/classes/Team.md) set to true that has the fewest players. If no auto assignable team is available, [Player.Neutral](/docs/reference/engine/classes/Player.md) will be set to true - When [SpawnLocation.Neutral](/docs/reference/engine/classes/SpawnLocation.md) is set to false, only players whose [Player.TeamColor](/docs/reference/engine/classes/Player.md) matches [SpawnLocation.TeamColor](/docs/reference/engine/classes/SpawnLocation.md) can spawn on that [SpawnLocation](/docs/reference/engine/classes/SpawnLocation.md) - When [SpawnLocation.AllowTeamChangeOnTouch](/docs/reference/engine/classes/SpawnLocation.md) is set to true, a player's [Player.TeamColor](/docs/reference/engine/classes/Player.md) will change to [SpawnLocation.TeamColor](/docs/reference/engine/classes/SpawnLocation.md) when their character touches the [SpawnLocation](/docs/reference/engine/classes/SpawnLocation.md) **Optional Extended Team Behaviors** Many developers chose to add the following features to teams in their own code. - Implement checks in weapon code to prevent friendly fire. - Implement checks in doors or other features that allow only certain teams to use them - Periodically reassign teams to maintain team balance ## Code Samples **Simple Team Rebalance** This code sample includes a simple example of how to re-balance teams. When Team.AutoAssignable is set to true players will be added to Teams in a balanced fashion. However as Players leave the game this can lead to unbalanced teams as players are not reallocated. This code keeps track of the number of players in each team and, when players leave will check to see if the teams need re-balancing. ```lua local Teams = game:GetService("Teams") -- create two teams local redTeam = Instance.new("Team") redTeam.TeamColor = BrickColor.new("Bright red") redTeam.AutoAssignable = true redTeam.Name = "Red Team" redTeam.Parent = Teams local blueTeam = Instance.new("Team") blueTeam.TeamColor = BrickColor.new("Bright blue") blueTeam.AutoAssignable = true blueTeam.Name = "Blue Team" blueTeam.Parent = Teams -- start counting the number of players on each team local numberRed, numberBlue = 0, 0 local function playerAdded(team) -- increase the team's count by 1 if team == redTeam then numberRed = numberRed + 1 elseif team == blueTeam then numberBlue = numberBlue + 1 end end local function playerRemoved(team) -- decrease the team's count by 1 if team == redTeam then numberRed = numberRed - 1 elseif team == blueTeam then numberBlue = numberBlue - 1 end -- check if the teams are unbalanced local bigTeam, smallTeam = nil, nil if (numberRed - numberBlue) > 2 then bigTeam = redTeam smallTeam = blueTeam elseif (numberBlue - numberRed) > 2 then bigTeam = blueTeam smallTeam = redTeam end if bigTeam then -- pick a random player local playerList = bigTeam:GetPlayers() local player = playerList[math.random(1, #playerList)] -- check the player exists if player then -- change the player's team player.TeamColor = smallTeam.TeamColor -- respawn the player player:LoadCharacterAsync() end end end -- listen for players being added / removed blueTeam.PlayerAdded:Connect(function(_player) playerAdded(blueTeam) end) blueTeam.PlayerRemoved:Connect(function(_player) playerRemoved(blueTeam) end) redTeam.PlayerAdded:Connect(function(_player) playerAdded(redTeam) end) redTeam.PlayerRemoved:Connect(function(_player) playerRemoved(redTeam) end) ``` **Team Kill Check** This code sample includes a quick function that can be added to weapons in a place to prevent them from team killing. It will return false when the two players are on different teams or if either of them is neutral. ```lua local Players = game:GetService("Players") function checkTeamKill(playerAttack, playerVictim) if playerAttack.Team ~= playerVictim.Team or playerAttack.Neutral or playerVictim.Neutral then return false end return true end local players = Players:GetPlayers() checkTeamKill(players[1], players[2]) ``` **Team Only Door** The following code sample will create a door in the Workspace that can only be walked through by Players on the Bright red team. ```lua local Players = game:GetService("Players") local door = Instance.new("Part") door.Anchored = true door.Size = Vector3.new(7, 10, 1) door.Position = Vector3.new(0, 5, 0) door.Parent = workspace local debounce = false door.Touched:Connect(function(hit) if not debounce then debounce = true if hit then local player = Players:GetPlayerFromCharacter(hit.Parent) if player and player.TeamColor == BrickColor.new("Bright red") then door.Transparency = 0.5 door.CanCollide = false task.wait(3) door.Transparency = 0 door.CanCollide = true end end task.wait(0.5) debounce = false end end) ``` **Team Rebalance with Party Integration** This code sample demonstrates how to assign players to [Teams](/docs/reference/engine/classes/Team.md) based on their [Player.PartyId](/docs/reference/engine/classes/Player.md) using the [SocialService:GetPlayersByPartyId()](/docs/reference/engine/classes/SocialService.md) method. When a player joins the experience, the script checks whether they're part of a party. If so, it attempts to assign them to the same team as other party members. If no match is found or the player isn't in a party, they're placed on the team with fewer members to maintain balance. ```lua local Teams = game:GetService("Teams") local Players = game:GetService("Players") local SocialService = game:GetService("SocialService") local redTeam = Instance.new("Team") redTeam.Name = "Red Team" redTeam.TeamColor = BrickColor.Red() redTeam.AutoAssignable = false redTeam.Parent = Teams local blueTeam = Instance.new("Team") blueTeam.Name = "Blue Team" blueTeam.TeamColor = BrickColor.Blue() blueTeam.AutoAssignable = false blueTeam.Parent = Teams local function assignPlayerToTeam(player) local partyId = player.PartyId if partyId == "" then assignBalancedTeam(player) return end local teams = { redTeam, blueTeam } local matchingTeam = nil local partyPlayers = SocialService:GetPlayersByPartyId(partyId) for _, partyPlayer in partyPlayers do if partyPlayer ~= player and table.find(teams, partyPlayer.Team) then matchingTeam = partyPlayer.Team break end end if matchingTeam then player.Team = matchingTeam player.TeamColor = matchingTeam.TeamColor else assignBalancedTeam(player) end end function assignBalancedTeam(player) local redCount = #redTeam:GetPlayers() local blueCount = #blueTeam:GetPlayers() local chosenTeam = redCount <= blueCount and redTeam or blueTeam player.Team = chosenTeam player.TeamColor = chosenTeam.TeamColor end local function groupPlayersByParty() local seenPartyIds = {} local groupedPlayers = {} for _, player in Players:GetPlayers() do if player.PartyId == "" then table.insert(groupedPlayers, { player }) elseif not table.find(seenPartyIds, player.PartyId) then table.insert(seenPartyIds, player.PartyId) local partyPlayers = SocialService:GetPlayersByPartyId(player.PartyId) table.insert(groupedPlayers, partyPlayers) end end return groupedPlayers end -- Call this function to rebalance teams local function rebalanceTeams() local groups = groupPlayersByParty() table.sort(groups, function(a, b) return #a > #b end) for _, player in Players:GetPlayers() do player.Team = nil end for _, group in groups do local redCount = #redTeam:GetPlayers() local blueCount = #blueTeam:GetPlayers() local bestTeam if redCount <= blueCount then bestTeam = redTeam else bestTeam = blueTeam end for _, player in group do player.Team = bestTeam player.TeamColor = bestTeam.TeamColor end end for _, player in Players:GetPlayers() do player:LoadCharacterAsync() end end Players.PlayerAdded:Connect(function(player) assignPlayerToTeam(player) player:LoadCharacterAsync() player:GetPropertyChangedSignal("PartyId"):Connect(function() if player.PartyId ~= "" then assignPlayerToTeam(player) end end) end) ``` ## Properties ### Property: Team.AutoAssignable ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Players" ] } ``` This property determines whether [Players](/docs/reference/engine/classes/Player.md) will be automatically placed onto the [Team](/docs/reference/engine/classes/Team.md) when joining. If multiple teams have this property set to true, Roblox will attempt to even the teams out when [Players](/docs/reference/engine/classes/Player.md) are added. When a [Player](/docs/reference/engine/classes/Player.md) joins a game they will be assigned to the [Team](/docs/reference/engine/classes/Team.md) with [Team.AutoAssignable](/docs/reference/engine/classes/Team.md) set to true that has the fewest players. If no such team is available, [Player.Neutral](/docs/reference/engine/classes/Player.md) will be set to true. Note while using this property will help even out teams when players are added, it will not do anything when players are removed. For this reason developers may wish to implement their own team balancing system. **Simple Team Rebalance** This code sample includes a simple example of how to re-balance teams. When Team.AutoAssignable is set to true players will be added to Teams in a balanced fashion. However as Players leave the game this can lead to unbalanced teams as players are not reallocated. This code keeps track of the number of players in each team and, when players leave will check to see if the teams need re-balancing. ```lua local Teams = game:GetService("Teams") -- create two teams local redTeam = Instance.new("Team") redTeam.TeamColor = BrickColor.new("Bright red") redTeam.AutoAssignable = true redTeam.Name = "Red Team" redTeam.Parent = Teams local blueTeam = Instance.new("Team") blueTeam.TeamColor = BrickColor.new("Bright blue") blueTeam.AutoAssignable = true blueTeam.Name = "Blue Team" blueTeam.Parent = Teams -- start counting the number of players on each team local numberRed, numberBlue = 0, 0 local function playerAdded(team) -- increase the team's count by 1 if team == redTeam then numberRed = numberRed + 1 elseif team == blueTeam then numberBlue = numberBlue + 1 end end local function playerRemoved(team) -- decrease the team's count by 1 if team == redTeam then numberRed = numberRed - 1 elseif team == blueTeam then numberBlue = numberBlue - 1 end -- check if the teams are unbalanced local bigTeam, smallTeam = nil, nil if (numberRed - numberBlue) > 2 then bigTeam = redTeam smallTeam = blueTeam elseif (numberBlue - numberRed) > 2 then bigTeam = blueTeam smallTeam = redTeam end if bigTeam then -- pick a random player local playerList = bigTeam:GetPlayers() local player = playerList[math.random(1, #playerList)] -- check the player exists if player then -- change the player's team player.TeamColor = smallTeam.TeamColor -- respawn the player player:LoadCharacterAsync() end end end -- listen for players being added / removed blueTeam.PlayerAdded:Connect(function(_player) playerAdded(blueTeam) end) blueTeam.PlayerRemoved:Connect(function(_player) playerRemoved(blueTeam) end) redTeam.PlayerAdded:Connect(function(_player) playerAdded(redTeam) end) redTeam.PlayerRemoved:Connect(function(_player) playerRemoved(redTeam) end) ``` ### Property: Team.TeamColor ```json { "type": "BrickColor", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Players" ] } ``` This property sets the color of the [Team](/docs/reference/engine/classes/Team.md). Determines the [Player.TeamColor](/docs/reference/engine/classes/Player.md) property of players who are a member of the team. A lot of Roblox's default team functionality is based on the team color, rather than the name or object. For example, [SpawnLocations](/docs/reference/engine/classes/SpawnLocation.md) can be assigned to a team via [SpawnLocation.TeamColor](/docs/reference/engine/classes/SpawnLocation.md). For this reason it is recommended that developers ensure each [Team](/docs/reference/engine/classes/Team.md) has a unique TeamColor. Any player which is a part of a team will have their name color changed to the team's TeamColor property. They will also be put underneath the team heading on the player list. **Team Only Door** The following code sample will create a door in the Workspace that can only be walked through by Players on the Bright red team. ```lua local Players = game:GetService("Players") local door = Instance.new("Part") door.Anchored = true door.Size = Vector3.new(7, 10, 1) door.Position = Vector3.new(0, 5, 0) door.Parent = workspace local debounce = false door.Touched:Connect(function(hit) if not debounce then debounce = true if hit then local player = Players:GetPlayerFromCharacter(hit.Parent) if player and player.TeamColor == BrickColor.new("Bright red") then door.Transparency = 0.5 door.CanCollide = false task.wait(3) door.Transparency = 0 door.CanCollide = true end end task.wait(0.5) debounce = false end end) ``` ### Property: Team.AutoColorCharacters ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Players" ] } ``` > **Deprecated:** This property is deprecated and no longer functions, it should not be used for new work. Historically set whether or not [Player](/docs/reference/engine/classes/Player.md) character models on a team would be colored to [Team.TeamColor](/docs/reference/engine/classes/Team.md). Developers are advised not to use this property as the script which changed the team colors has since been removed from the default character. This property is deprecated and should not be used for new work. ### Property: Team.Score ```json { "type": "int", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Players" ] } ``` > **Deprecated:** This property is deprecated and should not be used in new work. For more information on how to handle leaderboards and scoring please see [this tutorial](/docs/en-us/players/leaderboards.md). This property can be used to store an integer value associated with the team. This property offers no additional functionality and is not used by any game services. This property is deprecated and should not be used by developers for new work. ## Methods ### Method: Team:GetPlayers **Signature:** `Team:GetPlayers(): List` Returns a list of [Players](/docs/reference/engine/classes/Player.md) who are assigned to the [Team](/docs/reference/engine/classes/Team.md). A [Player](/docs/reference/engine/classes/Player.md) is considered assigned if their [Player.Team](/docs/reference/engine/classes/Player.md) property is equal to the [Team](/docs/reference/engine/classes/Team.md) and [Player.Neutral](/docs/reference/engine/classes/Player.md) is false. This function has a number of potential uses, including counting the number of players on a [Team](/docs/reference/engine/classes/Team.md) or giving every [Player](/docs/reference/engine/classes/Player.md) on a [Team](/docs/reference/engine/classes/Team.md) a [Tool](/docs/reference/engine/classes/Tool.md). *Security: None · Thread Safety: Safe · Capabilities: Players* **Returns:** `List` — An array of [Players](/docs/reference/engine/classes/Player.md) in the [Team](/docs/reference/engine/classes/Team.md). **Teams GetTeams** The example below prints the number of players on each Team. ```lua local Teams = game:GetService("Teams") local teams = Teams:GetTeams() for _, team in pairs(teams) do local players = team:GetPlayers() print("Team", team.Name, "has", #players, "players") end ``` ## Events ### Event: Team.PlayerAdded **Signature:** `Team.PlayerAdded(player: Player)` Fires whenever a [Player](/docs/reference/engine/classes/Player.md) is assigned to the [Team](/docs/reference/engine/classes/Team.md). A player is considered assigned if their [Player.Team](/docs/reference/engine/classes/Player.md) property is equal to the [Team](/docs/reference/engine/classes/Team.md) and [Player.Neutral](/docs/reference/engine/classes/Player.md) is false. This event is team specific and will only fire when a [Player](/docs/reference/engine/classes/Player.md) joints the specific [Team](/docs/reference/engine/classes/Team.md). Any function connected to this event will be passed the [Player](/docs/reference/engine/classes/Player.md) object of the player who joined the team. For example: Team.PlayerAdded:Connect(function(player) print(player.Name.." has joined the team") end) *Security: None · Capabilities: Players* **Parameters:** | Name | Type | Description | |------|------|-------------| | `player` | `Player` | The [Player](/docs/reference/engine/classes/Player.md) that was added. | **Simple Team Rebalance** This code sample includes a simple example of how to re-balance teams. When Team.AutoAssignable is set to true players will be added to Teams in a balanced fashion. However as Players leave the game this can lead to unbalanced teams as players are not reallocated. This code keeps track of the number of players in each team and, when players leave will check to see if the teams need re-balancing. ```lua local Teams = game:GetService("Teams") -- create two teams local redTeam = Instance.new("Team") redTeam.TeamColor = BrickColor.new("Bright red") redTeam.AutoAssignable = true redTeam.Name = "Red Team" redTeam.Parent = Teams local blueTeam = Instance.new("Team") blueTeam.TeamColor = BrickColor.new("Bright blue") blueTeam.AutoAssignable = true blueTeam.Name = "Blue Team" blueTeam.Parent = Teams -- start counting the number of players on each team local numberRed, numberBlue = 0, 0 local function playerAdded(team) -- increase the team's count by 1 if team == redTeam then numberRed = numberRed + 1 elseif team == blueTeam then numberBlue = numberBlue + 1 end end local function playerRemoved(team) -- decrease the team's count by 1 if team == redTeam then numberRed = numberRed - 1 elseif team == blueTeam then numberBlue = numberBlue - 1 end -- check if the teams are unbalanced local bigTeam, smallTeam = nil, nil if (numberRed - numberBlue) > 2 then bigTeam = redTeam smallTeam = blueTeam elseif (numberBlue - numberRed) > 2 then bigTeam = blueTeam smallTeam = redTeam end if bigTeam then -- pick a random player local playerList = bigTeam:GetPlayers() local player = playerList[math.random(1, #playerList)] -- check the player exists if player then -- change the player's team player.TeamColor = smallTeam.TeamColor -- respawn the player player:LoadCharacterAsync() end end end -- listen for players being added / removed blueTeam.PlayerAdded:Connect(function(_player) playerAdded(blueTeam) end) blueTeam.PlayerRemoved:Connect(function(_player) playerRemoved(blueTeam) end) redTeam.PlayerAdded:Connect(function(_player) playerAdded(redTeam) end) redTeam.PlayerRemoved:Connect(function(_player) playerRemoved(redTeam) end) ``` ### Event: Team.PlayerRemoved **Signature:** `Team.PlayerRemoved(player: Player)` Fires whenever a [Player](/docs/reference/engine/classes/Player.md) is removed from a [Team](/docs/reference/engine/classes/Team.md). This can be due to the [Player](/docs/reference/engine/classes/Player.md) leaving the game, [Player.Neutral](/docs/reference/engine/classes/Player.md) being set to true or the [Player](/docs/reference/engine/classes/Player.md) joining a different team. This event is team specific and will only fire when a [Player](/docs/reference/engine/classes/Player.md) leaves the specific [Team](/docs/reference/engine/classes/Team.md). Any function connected to this event will be passed the [Player](/docs/reference/engine/classes/Player.md) object of the player who left the team. For example: Team.PlayerRemoved:Connect(function(player) print(player.Name.." has left the team") end) *Security: None · Capabilities: Players* **Parameters:** | Name | Type | Description | |------|------|-------------| | `player` | `Player` | The [Player](/docs/reference/engine/classes/Player.md) removed. | **Simple Team Rebalance** This code sample includes a simple example of how to re-balance teams. When Team.AutoAssignable is set to true players will be added to Teams in a balanced fashion. However as Players leave the game this can lead to unbalanced teams as players are not reallocated. This code keeps track of the number of players in each team and, when players leave will check to see if the teams need re-balancing. ```lua local Teams = game:GetService("Teams") -- create two teams local redTeam = Instance.new("Team") redTeam.TeamColor = BrickColor.new("Bright red") redTeam.AutoAssignable = true redTeam.Name = "Red Team" redTeam.Parent = Teams local blueTeam = Instance.new("Team") blueTeam.TeamColor = BrickColor.new("Bright blue") blueTeam.AutoAssignable = true blueTeam.Name = "Blue Team" blueTeam.Parent = Teams -- start counting the number of players on each team local numberRed, numberBlue = 0, 0 local function playerAdded(team) -- increase the team's count by 1 if team == redTeam then numberRed = numberRed + 1 elseif team == blueTeam then numberBlue = numberBlue + 1 end end local function playerRemoved(team) -- decrease the team's count by 1 if team == redTeam then numberRed = numberRed - 1 elseif team == blueTeam then numberBlue = numberBlue - 1 end -- check if the teams are unbalanced local bigTeam, smallTeam = nil, nil if (numberRed - numberBlue) > 2 then bigTeam = redTeam smallTeam = blueTeam elseif (numberBlue - numberRed) > 2 then bigTeam = blueTeam smallTeam = redTeam end if bigTeam then -- pick a random player local playerList = bigTeam:GetPlayers() local player = playerList[math.random(1, #playerList)] -- check the player exists if player then -- change the player's team player.TeamColor = smallTeam.TeamColor -- respawn the player player:LoadCharacterAsync() end end end -- listen for players being added / removed blueTeam.PlayerAdded:Connect(function(_player) playerAdded(blueTeam) end) blueTeam.PlayerRemoved:Connect(function(_player) playerRemoved(blueTeam) end) redTeam.PlayerAdded:Connect(function(_player) playerAdded(redTeam) end) redTeam.PlayerRemoved:Connect(function(_player) playerRemoved(redTeam) end) ``` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: TeamCreateData last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service --- # Class: TeamCreateData ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: TeamCreateService last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service - NotReplicated --- # Class: TeamCreateService ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Teams last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service summary: "The Teams service holds a game's Team objects. Team objects must be parented to the Teams service." --- # Class: Teams > The Teams service holds a game's [Team](/docs/reference/engine/classes/Team.md) objects. [Team](/docs/reference/engine/classes/Team.md) objects > must be parented to the Teams service. ## Description The Teams service holds a game's [Team](/docs/reference/engine/classes/Team.md) objects. [Team](/docs/reference/engine/classes/Team.md) objects must be parented to the Teams service. Teams offer a range of features that are useful to developers. These can broadly be divided into features that work out-of-the-box and features developers can program into their game. **Built-in team behavior** The following functionality of Teams exists by default and does not require the developer to program any custom behavior. - When part of a Team, the name above a player's character [Model](/docs/reference/engine/classes/Model.md) will be colored to the [Team.TeamColor](/docs/reference/engine/classes/Team.md) - Changing [Player.TeamColor](/docs/reference/engine/classes/Player.md) will cause [Player.Team](/docs/reference/engine/classes/Player.md) switch to the Team with the corresponding [Team.TeamColor](/docs/reference/engine/classes/Team.md) - When using the default player list users will be grouped and displayed by team - Setting [Player.Neutral](/docs/reference/engine/classes/Player.md) to true will cause the [Player](/docs/reference/engine/classes/Player.md) to be dis-associated with the team, but will not change [Player.Team](/docs/reference/engine/classes/Player.md) or [Player.TeamColor](/docs/reference/engine/classes/Player.md) - When a [Player](/docs/reference/engine/classes/Player.md) joins a game, they will be allocated to the team with [Team.AutoAssignable](/docs/reference/engine/classes/Team.md) set to true that has the fewest players. If no auto assignable team is available, [Player.Neutral](/docs/reference/engine/classes/Player.md) will be set to true - When [SpawnLocation.Neutral](/docs/reference/engine/classes/SpawnLocation.md) is set to false, only players whose [Player.TeamColor](/docs/reference/engine/classes/Player.md) matches [SpawnLocation.TeamColor](/docs/reference/engine/classes/SpawnLocation.md) can spawn on that [SpawnLocation](/docs/reference/engine/classes/SpawnLocation.md) - When [SpawnLocation.AllowTeamChangeOnTouch](/docs/reference/engine/classes/SpawnLocation.md) is set to true, a player's [Player.TeamColor](/docs/reference/engine/classes/Player.md) will change to [SpawnLocation.TeamColor](/docs/reference/engine/classes/SpawnLocation.md) when their character touches the [SpawnLocation](/docs/reference/engine/classes/SpawnLocation.md) **Optional extended team behavior** Many developers chose to add the following features to teams in their own code. - Implement checks for team in weapon code to prevent team killing - Implement doors or other features that only certain teams can use - Periodically reassign teams to maintain team balance ## Code Samples **Simple Team Rebalance** This code sample includes a simple example of how to re-balance teams. When Team.AutoAssignable is set to true players will be added to Teams in a balanced fashion. However as Players leave the game this can lead to unbalanced teams as players are not reallocated. This code keeps track of the number of players in each team and, when players leave will check to see if the teams need re-balancing. ```lua local Teams = game:GetService("Teams") -- create two teams local redTeam = Instance.new("Team") redTeam.TeamColor = BrickColor.new("Bright red") redTeam.AutoAssignable = true redTeam.Name = "Red Team" redTeam.Parent = Teams local blueTeam = Instance.new("Team") blueTeam.TeamColor = BrickColor.new("Bright blue") blueTeam.AutoAssignable = true blueTeam.Name = "Blue Team" blueTeam.Parent = Teams -- start counting the number of players on each team local numberRed, numberBlue = 0, 0 local function playerAdded(team) -- increase the team's count by 1 if team == redTeam then numberRed = numberRed + 1 elseif team == blueTeam then numberBlue = numberBlue + 1 end end local function playerRemoved(team) -- decrease the team's count by 1 if team == redTeam then numberRed = numberRed - 1 elseif team == blueTeam then numberBlue = numberBlue - 1 end -- check if the teams are unbalanced local bigTeam, smallTeam = nil, nil if (numberRed - numberBlue) > 2 then bigTeam = redTeam smallTeam = blueTeam elseif (numberBlue - numberRed) > 2 then bigTeam = blueTeam smallTeam = redTeam end if bigTeam then -- pick a random player local playerList = bigTeam:GetPlayers() local player = playerList[math.random(1, #playerList)] -- check the player exists if player then -- change the player's team player.TeamColor = smallTeam.TeamColor -- respawn the player player:LoadCharacterAsync() end end end -- listen for players being added / removed blueTeam.PlayerAdded:Connect(function(_player) playerAdded(blueTeam) end) blueTeam.PlayerRemoved:Connect(function(_player) playerRemoved(blueTeam) end) redTeam.PlayerAdded:Connect(function(_player) playerAdded(redTeam) end) redTeam.PlayerRemoved:Connect(function(_player) playerRemoved(redTeam) end) ``` **Team Only Door** The following code sample will create a door in the Workspace that can only be walked through by Players on the Bright red team. ```lua local Players = game:GetService("Players") local door = Instance.new("Part") door.Anchored = true door.Size = Vector3.new(7, 10, 1) door.Position = Vector3.new(0, 5, 0) door.Parent = workspace local debounce = false door.Touched:Connect(function(hit) if not debounce then debounce = true if hit then local player = Players:GetPlayerFromCharacter(hit.Parent) if player and player.TeamColor == BrickColor.new("Bright red") then door.Transparency = 0.5 door.CanCollide = false task.wait(3) door.Transparency = 0 door.CanCollide = true end end task.wait(0.5) debounce = false end end) ``` **Team Kill Check** This code sample includes a quick function that can be added to weapons in a place to prevent them from team killing. It will return false when the two players are on different teams or if either of them is neutral. ```lua local Players = game:GetService("Players") function checkTeamKill(playerAttack, playerVictim) if playerAttack.Team ~= playerVictim.Team or playerAttack.Neutral or playerVictim.Neutral then return false end return true end local players = Players:GetPlayers() checkTeamKill(players[1], players[2]) ``` ## Methods ### Method: Teams:GetTeams **Signature:** `Teams:GetTeams(): List` The GetTeam function returns a table containing the game's [Team](/docs/reference/engine/classes/Team.md) objects. Note this will only return Team objects that are directly parented to the [Teams](/docs/reference/engine/classes/Teams.md) service. For this reason it is recommended developers only parent [Team](/docs/reference/engine/classes/Team.md) objects to the [Teams](/docs/reference/engine/classes/Teams.md) service and not to other [Instances](/docs/reference/engine/classes/Instance.md) (or to each other). *Security: None · Thread Safety: Unsafe · Capabilities: Players* **Returns:** `List` — An array of [Teams](/docs/reference/engine/classes/Team.md) in the game. **Teams GetTeams** The example below prints the number of players on each Team. ```lua local Teams = game:GetService("Teams") local teams = Teams:GetTeams() for _, team in pairs(teams) do local players = team:GetPlayers() print("Team", team.Name, "has", #players, "players") end ``` ### Method: Teams:RebalanceTeams **Signature:** `Teams:RebalanceTeams(): ()` > **Deprecated:** This function has been deprecated and no longer functions correctly. It should not be used. Developers should instead implement their own team sorting systems. Evens the number of people on each team. This function does not work correctly and should not be used. *Security: None · Thread Safety: Unsafe · Capabilities: Players* **Returns:** `()` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: TeleportAsyncResult last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable summary: "The return structure of the `TeleportAsync` function call." --- # Class: TeleportAsyncResult > The return structure of the `TeleportAsync` function call. ## Description This class is an instance that is returned by the `TeleportAsync` function with information about the teleport. ## Properties ### Property: TeleportAsyncResult.PrivateServerId ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Teleport" ] } ``` The private server ID of the reserved server that the players are being teleported to. This field is populated only if the teleport is to a reserved server. This field is not the same as the private server ID of the server that initiated the teleport, please see: [DataModel.PrivateServerId](/docs/reference/engine/classes/DataModel.md) See also: For more information on how to teleport players between servers, see [Teleporting Between Places](/docs/en-us/projects/teleport.md). ### Property: TeleportAsyncResult.ReservedServerAccessCode ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Teleport" ] } ``` The access code of the reserved server that the players are being teleported to. This field is populated only if the teleport is to a reserved server. This allows developers to perform subsequent teleports to this same reserved server. For more information on how to teleport players between servers, see [Teleporting Between Places](/docs/en-us/projects/teleport.md). ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: TeleportOptions last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances summary: "Optional input arguments to the TeleportService:TeleportAsync() function." --- # Class: TeleportOptions > Optional input arguments to the [TeleportService:TeleportAsync()](/docs/reference/engine/classes/TeleportService.md) > function. ## Description This class is an optional parameter to the [TeleportService:TeleportAsync()](/docs/reference/engine/classes/TeleportService.md) function that allows developers to provide arguments for the teleport call. Certain arguments in this class are not compatible with each other and cause an error when passed to [TeleportService:TeleportAsync()](/docs/reference/engine/classes/TeleportService.md): - ReservedServerAccessCode + ServerInstanceId - ShouldReserveServer + ServerInstanceId - ShouldReserveServer + ReservedServerAccessCode For more information on how to teleport players between servers, see [Teleporting Between Places](/docs/en-us/projects/teleport.md). ## Properties ### Property: TeleportOptions.ReservedServerAccessCode ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Teleport" ] } ``` This property indicates the reserved server access code for the reserved server that the user(s) should be teleported to. For more information on how to teleport players between servers, see [Teleporting Between Places](/docs/en-us/projects/teleport.md). ### Property: TeleportOptions.ServerInstanceId ```json { "type": "string", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Teleport" ] } ``` This property indicates the [DataModel.JobId](/docs/reference/engine/classes/DataModel.md) of the server instance the user(s) should be teleported to. For more information on how to teleport players between servers, see [Teleporting Between Places](/docs/en-us/projects/teleport.md). ### Property: TeleportOptions.ShouldReserveServer ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Teleport" ] } ``` This property indicates whether the teleport call should create a new reserved server. When set to true, a reserved server will be created and the player(s) will be teleported to the new server. If set to false, the player(s) will be teleported to the public server with the specified [TeleportOptions.ServerInstanceId](/docs/reference/engine/classes/TeleportOptions.md) if provided. When [TeleportOptions.ServerInstanceId](/docs/reference/engine/classes/TeleportOptions.md) is blank or no matching server is found, a new public server will be created to teleport the player(s) to. For more information on how to teleport players between servers, see [Teleporting Between Places](/docs/en-us/projects/teleport.md). ## Methods ### Method: TeleportOptions:GetTeleportData **Signature:** `TeleportOptions:GetTeleportData(): Variant` This function returns the teleport data stored in the [TeleportOptions](/docs/reference/engine/classes/TeleportOptions.md) instance by [TeleportOptions:SetTeleportData()](/docs/reference/engine/classes/TeleportOptions.md). Once a player has teleported, teleport data can be retrieved using the [Player:GetJoinData()](/docs/reference/engine/classes/Player.md) and [TeleportService:GetLocalPlayerTeleportData()](/docs/reference/engine/classes/TeleportService.md) functions. For more information on how to teleport players between servers, see [Teleporting Between Places](/docs/en-us/projects/teleport.md). *Security: None · Thread Safety: Unsafe · Capabilities: Teleport* **Returns:** `Variant` ### Method: TeleportOptions:SetTeleportData **Signature:** `TeleportOptions:SetTeleportData(teleportData: Variant): ()` This is a setter function for data to be passed to the destination place. On the destination place, this data can be retrieved using [Player:GetJoinData()](/docs/reference/engine/classes/Player.md) or [TeleportService:GetLocalPlayerTeleportData()](/docs/reference/engine/classes/TeleportService.md). For example, the following snippet would send the [DataModel.PlaceId](/docs/reference/engine/classes/DataModel.md) and [DataModel.JobId](/docs/reference/engine/classes/DataModel.md) in a dictionary passing the teleport data in a [TeleportOptions](/docs/reference/engine/classes/TeleportOptions.md) instance using [TeleportOptions:SetTeleportData()](/docs/reference/engine/classes/TeleportOptions.md): ```lua -- Server local teleportOptions = Instance.new("TeleportOptions") local teleportData = { placeId = game.PlaceId, jobId = game.JobId } teleportOptions:SetTeleportData(teleportData) TeleportService:TeleportAsync(game.PlaceId, {player}, teleportOptions) ``` This data could then be retrieved upon arrival using the GetLocalPlayerTeleportData() function as follows: ```lua -- Client local TeleportService = game:GetService("TeleportService") local teleportData = TeleportService:GetLocalPlayerTeleportData() if teleportData then local placeId = teleportData.placeId local jobId = teleportData.JobId end ``` If no `teleportData` was set in the teleportation function this GetLocalPlayerTeleportData() will return `nil`. For more information on how to send and receive user data along with teleports, see, see [Teleporting Between Places](/docs/en-us/projects/teleport.md#sending-user-data-along-with-teleports). *Security: None · Thread Safety: Unsafe · Capabilities: Teleport* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `teleportData` | `Variant` | | Data to be passed to the destination place. | **Returns:** `()` ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: TeleportService last_updated: 2026-06-29T19:34:08Z inherits: - Instance - Object type: class memory_category: Instances tags: - NotCreatable - Service summary: "Enables transporting Players between places and servers. For more information on how to teleport players between servers, see [Teleport between places](/docs/en-us/projects/teleport.md)." --- # Class: TeleportService > Enables transporting [Players](/docs/reference/engine/classes/Player.md) between places and servers. For > more information on how to teleport players between servers, see > [Teleport between places](/docs/en-us/projects/teleport.md). ## Description **TeleportService** is responsible for transporting [Players](/docs/reference/engine/classes/Player.md) between different places and servers. For more information on how to teleport players between servers, see [Teleport between places](/docs/en-us/projects/teleport.md). ## Properties ### Property: TeleportService.CustomizedTeleportUI ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Teleport" ] } ``` > **Deprecated:** This item is deprecated since the default message it controls has been removed. Do not use it for new work. This property used to control whether or not a [Message](/docs/reference/engine/classes/Message.md) would be shown by default. The default message has been removed, so this no longer does anything. ## Methods ### Method: TeleportService:GetArrivingTeleportGui **Signature:** `TeleportService:GetArrivingTeleportGui(): Instance` This function returns the _customLoadingScreen_ the [LocalPlayer](/docs/reference/engine/classes/Players.md) arrived into the place with. Note, the _customLoadingScreen_ will not be used if the destination place is in a different game. #### Loading Screen During a teleport, while the destination place is loading, the _customLoadingScreen_ is parented to the [CoreGui](/docs/reference/engine/classes/CoreGui.md). Once the place has loaded the [loading screen](/docs/reference/engine/classes/ScreenGui.md) is [parented](/docs/reference/engine/classes/Instance.md) to _nil_. If you wish to preserve the _customLoadingScreen_ and perform your own transitions, you will need to parent it to the local player's [PlayerGui](/docs/reference/engine/classes/PlayerGui.md). For an example of this, see the code sample below. #### Studio Limitation Note that this service does not work during playtesting in Roblox Studio; to test aspects of your experience using it, you must publish the experience and play it in the Roblox application. *Security: None · Thread Safety: Unsafe · Capabilities: UI, Teleport* **Returns:** `Instance` — The _customLoadingScreen_ the [LocalPlayer](/docs/reference/engine/classes/Players.md) arrived into the place with. **Handling a Teleport Loading GUI** The following code, when placed inside a `LocalScript` in `ReplicatedFirst` will preserve a custom teleport loading screen for five seconds before destroying it. ```lua local TeleportService = game:GetService("TeleportService") local Players = game:GetService("Players") local ReplicatedFirst = game:GetService("ReplicatedFirst") local customLoadingScreen = TeleportService:GetArrivingTeleportGui() if customLoadingScreen then local playerGui = Players.LocalPlayer:WaitForChild("PlayerGui") ReplicatedFirst:RemoveDefaultLoadingScreen() customLoadingScreen.Parent = playerGui task.wait(5) customLoadingScreen:Destroy() end ``` ### Method: TeleportService:GetLocalPlayerTeleportData **Signature:** `TeleportService:GetLocalPlayerTeleportData(): Variant` This function returns the teleport data the [Players.LocalPlayer](/docs/reference/engine/classes/Players.md) arrived with. It can only be called from the client. Exploiters can spoof teleport data. Send secure data such as player currency through a server-side service such as [DataStoreService](/docs/reference/engine/classes/DataStoreService.md) to prevent tampering. *Security: None · Thread Safety: Unsafe · Capabilities: Teleport* **Returns:** `Variant` — The teleport data the [Players.LocalPlayer](/docs/reference/engine/classes/Players.md) arrived into the place with. **Getting LocalPlayer Teleport Data** When put into StarterPlayerScripts, this example runs when the player joins the game, and prints any teleport data provided by the player's previous server. ```lua local TeleportService = game:GetService("TeleportService") local teleportData = TeleportService:GetLocalPlayerTeleportData() print("Local player arrived with this data:", teleportData) ``` **Expected output:** Local player arrived with this data: nil ### Method: TeleportService:GetPlayerPlaceInstanceAsync **Signature:** `TeleportService:GetPlayerPlaceInstanceAsync(userId: User): Tuple` This function returns the [PlaceId](/docs/reference/engine/classes/DataModel.md) and [JobId](/docs/reference/engine/classes/DataModel.md) of the server the user with the given [UserId](/docs/reference/engine/classes/Player.md) is in, provided it is in the same game as the current place. Then, [TeleportService:TeleportToPlaceInstance()](/docs/reference/engine/classes/TeleportService.md) can be called with this information to allow a user to join the target user's server. Upon a successful lookup, the function returns the following values: | # | Name | Type | Description | | --- | --- | --- | --- | | **1** | currentInstance | bool | A bool indicating if the user was found in the current instance | | **2** | error | string | An error message in the event of the lookup failing | | **3** | placeId | int64 | The PlaceId of the server the user is in | | **4** | instanceId | string | The JobId of the server the user is in | If there is a problem during lookup, such as the user being offline, an error is thrown. It is recommended that you wrap calls to this function in `pcall`. #### Limitations You should be aware of the following limitations when using this function: - This function can only be called by the server. - This function may fail to return the correct information if the user is teleporting. - It is possible for this function to throw an error, hence developers should wrap it in a [LuaGlobals.pcall()](/docs/reference/engine/globals/LuaGlobals.md) (see example below) - As this function returns the JobId of the server and not the access code returned by [TeleportService:ReserveServerAsync()](/docs/reference/engine/classes/TeleportService.md), the ID returned is not appropriate for use with reserved servers. #### Studio Limitation Note that this service does not work during playtesting in Roblox Studio; to test aspects of your experience using it, you must publish the experience and play it in the Roblox application. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Teleport* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `userId` | `User` | | The [Player.UserId](/docs/reference/engine/classes/Player.md) of the [Player](/docs/reference/engine/classes/Player.md). | **Returns:** `Tuple` — See the table above. **Following Another Player** The code sample below, when placed inside a `Script` within `ServerScriptService`, will teleport a player who's following another player to the associated place/server. Note that this will not work if the player being followed is in a reserved server. ```lua local TeleportService = game:GetService("TeleportService") local Players = game:GetService("Players") Players.PlayerAdded:Connect(function(player) local followId = player.FollowUserId if followId and followId ~= 0 then -- Is this player following anyone? If so, find out where they are local success, currentInstance, error, placeId, jobIdOrErr = pcall(function() return TeleportService:GetPlayerPlaceInstanceAsync(followId) end) -- followId is the user ID of the player that you want to retrieve the place and job ID for if success then -- Teleports player on success based off jobId TeleportService:TeleportToPlaceInstance(placeId, jobIdOrErr, player) else -- Posts a warning based off returning error warn(jobIdOrErr) end else warn(("Player %d is not following another player!"):format(player.UserId)) end end) ``` ### Method: TeleportService:GetTeleportSetting **Signature:** `TeleportService:GetTeleportSetting(setting: string): Variant` This function retrieves a teleport setting saved using [TeleportService:SetTeleportSetting()](/docs/reference/engine/classes/TeleportService.md) using the given key. This method is intended for use on the client only and should not be used on the server. Teleport settings are preserved across teleportations within the same game. This means data can be saved using [TeleportService:SetTeleportSetting()](/docs/reference/engine/classes/TeleportService.md) in one place and retrieved using GetTeleportSetting in another place the user has been teleported to. For example, in a game that allowed crouching you could save whether the user is currently crouching prior to teleporting as a teleport setting. This could then be retrieved in the destination place after the teleportation: ```lua local TeleportService = game:GetService("TeleportService") local isCrouching = TeleportService:GetTeleportSetting("isCrouching") ``` If no teleport setting exists under the given key, this function will return _nil_. #### Differences from GlobalDataStores Although they share some similarities, there are some key differences between teleport settings and datastores: - [GlobalDataStore:SetAsync()](/docs/reference/engine/classes/GlobalDataStore.md) stores the data on Roblox servers whereas SetTeleportSetting stores the data locally - Data stored in a [GlobalDataStore](/docs/reference/engine/classes/GlobalDataStore.md) is preserved after the user leaves the game universe whereas teleport settings are not - [GlobalDataStores](/docs/reference/engine/classes/GlobalDataStore.md) can only be accessed on the server, whereas teleport settings can only be accessed on the client - [GlobalDataStores](/docs/reference/engine/classes/GlobalDataStore.md) have usage limits, whereas teleport settings do not In general teleport settings should be used to preserve client side information within a single play session across different places in a game. [GlobalDataStores](/docs/reference/engine/classes/GlobalDataStore.md) should be used to save important player data that needs to be accessed across player sessions. #### Teleport settings and security As teleport settings are stored locally, it is possible they can be manipulated by malicious users. This risk can be mitigated by employing server side validation. #### Studio Limitation Note that this service does not work during playtesting in Roblox Studio; to test aspects of your experience using it, you must publish the experience and play it in the Roblox application. *Security: None · Thread Safety: Unsafe · Capabilities: Teleport* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `setting` | `string` | | The key the value was stored under using [TeleportService:SetTeleportSetting()](/docs/reference/engine/classes/TeleportService.md). | **Returns:** `Variant` — The value stored under the given key. ### Method: TeleportService:PromptExperienceDetailsAsync **Signature:** `TeleportService:PromptExperienceDetailsAsync(player: Player, universeId: int64): PromptExperienceDetailsResult` Prompts the specified [Player](/docs/reference/engine/classes/Player.md) with information of the specified experience. The prompt includes the experience name, creator name, maturity rating, etc. The prompt also includes a **Join** button which the player can use to be teleported to the target experience. If the player is ineligible to join the target experience, the button will be disabled. Any teleport failures after the player clicks the **Join** button will also fire [TeleportService.TeleportInitFailed](/docs/reference/engine/classes/TeleportService.md) providing a reason for the failure. #### Limitations - For security purposes, teleporting a user from your experience to another experience owned by others fails by default. See [here](/docs/en-us/projects/teleport.md#enable-cross-experience-teleportation) for steps to enable cross-experience teleportation. - This function currently can only be called from the client with [Players.LocalPlayer](/docs/reference/engine/classes/Players.md) as the `player` parameter. - The join button will always be disabled during Studio playtesting; to test aspects of your experience using it, you must publish the experience and play it in the Roblox application. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Teleport* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `player` | `Player` | | The [Player](/docs/reference/engine/classes/Player.md) to be presented the prompt. | | `universeId` | `int64` | | [DataModel.UniverseId](/docs/reference/engine/classes/DataModel.md) of the experience to be presented to the [Player](/docs/reference/engine/classes/Player.md). | **Returns:** `PromptExperienceDetailsResult` — [PromptExperienceDetailsResult](/docs/reference/engine/enums/PromptExperienceDetailsResult.md) **Show Experience Details to Player** The code sample below, when placed inside a `Script` with Client RunContext, will display a system dialog to the player showing details about a specified experience. Player can choose to teleport to the experience or close the dialog. ```lua local TeleportService = game:GetService("TeleportService") local Players = game:GetService("Players") local player = Players.LocalPlayer local success, result = pcall(function() TeleportService:PromptExperienceDetailsAsync(player, 8357232245) end) if not success then warn("Error prompting experience details: " .. tostring(result)) end if result == Enum.PromptExperienceDetailsResult.PromptClosed then print("Player closed the experience details prompt") elseif result == Enum.PromptExperienceDetailsResult.TeleportAttempted then print("Player chose to teleport to the experience") end ``` ### Method: TeleportService:ReserveServerAsync **Signature:** `TeleportService:ReserveServerAsync(placeId: int64): Tuple` This function returns an access code that can be used to teleport players to a reserved server, along with the server's [DataModel.PrivateServerId](/docs/reference/engine/classes/DataModel.md). It can only be called on the server. #### Reserved Servers You can access reserved servers using: - [TeleportService:TeleportAsync()](/docs/reference/engine/classes/TeleportService.md) with the [TeleportOptions.ReservedServerAccessCode](/docs/reference/engine/classes/TeleportOptions.md) parameter. - [TeleportService:TeleportToPrivateServer()](/docs/reference/engine/classes/TeleportService.md), with the access code `ReserveServerAsync` returns. - A server is started when the access code is first used. - Access codes remain valid indefinitely, meaning reserved servers can still be joined if no game server is running (in this case a new server will be started). You can see if the current server is a reserved server by using the following code: ```lua local isReserved = game.PrivateServerId ~= "" and game.PrivateServerOwnerId == 0 ``` The [DataModel.PrivateServerId](/docs/reference/engine/classes/DataModel.md) is constant across all server instances associated with the server access code, the [DataModel.JobId](/docs/reference/engine/classes/DataModel.md) is not. #### Studio Limitation Note that this service does not work during playtesting in Roblox Studio; to test aspects of your experience using it, you must publish the experience and play it in the Roblox application. #### Cross-Platform Play Players on Xbox and PlayStation with cross‑play disabled will arrive in a different server than players with cross‑play enabled. This can cause multiple game servers with the same [PrivateServerId](/docs/reference/engine/classes/DataModel.md) to exist. You can use [DataModel.MatchmakingType](/docs/reference/engine/classes/DataModel.md) to differentiate these game servers. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Teleport* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `placeId` | `int64` | | The [DataModel.PlaceId](/docs/reference/engine/classes/DataModel.md) of the place the reserved server is being created for. | **Returns:** `Tuple` — The server access code required by [TeleportService:TeleportToPrivateServer()](/docs/reference/engine/classes/TeleportService.md) and the [DataModel.PrivateServerId](/docs/reference/engine/classes/DataModel.md) for the reserved server. **TeleportService: Teleport to a Reserved Server** The following code would send everyone in the current game to a reserved server. Since reserved servers can only be joined by using [TeleportService:TeleportToPrivateServer()](/docs/reference/engine/classes/TeleportService.md), nobody will join the reserved server afterwards. Mind that, since everyone will be teleported, the current server will (probably) shutdown as nobody would be left in it. ```lua local TeleportService = game:GetService("TeleportService") local Players = game:GetService("Players") local code = TeleportService:ReserveServerAsync(game.PlaceId) local players = Players:GetPlayers() TeleportService:TeleportToPrivateServer(game.PlaceId, code, players) -- You could add extra arguments to this function: spawnName, teleportData and customLoadingScreen ``` **TeleportService: Teleport to a Reserved Server via Chat** The following code would reserve one server, if it hasn't be reserved before. Whenever someone says "reserved", they will be teleported to the private server. ```lua local TeleportService = game:GetService("TeleportService") local Players = game:GetService("Players") local DataStoreService = game:GetService("DataStoreService") local dataStore = DataStoreService:GetGlobalDataStore() -- Get the saved code local code = dataStore:GetAsync("ReservedServer") if typeof(code) ~= "string" then -- None saved, create one code = TeleportService:ReserveServerAsync(game.PlaceId) dataStore:SetAsync("ReservedServer", code) end local function joined(player) player.Chatted:Connect(function(message) if message == "reserved" then TeleportService:TeleportToPrivateServer(game.PlaceId, code, { player }) end end) end Players.PlayerAdded:Connect(joined) ``` ### Method: TeleportService:SetTeleportGui **Signature:** `TeleportService:SetTeleportGui(gui: Instance): ()` This function sets the custom [teleport GUI](/docs/reference/engine/classes/ScreenGui.md) that will be shown to the local user during teleportation, prior to the teleport being invoked. Note, the [teleport GUI](/docs/reference/engine/classes/ScreenGui.md) will not be used if the destination place is in a different game. It will also not persist across multiple teleports and will need to be set prior to each one. This function should only be used on the client. If the teleportation function is called from the server (as is the case with [TeleportService:TeleportAsync()](/docs/reference/engine/classes/TeleportService.md)) then this function should be called on the client prior to this. One way of doing this is listening to a [RemoteEvent](/docs/reference/engine/classes/RemoteEvent.md) that fires several seconds before teleportation. #### Loading screen During a teleport, while the destination place is loading, the _customLoadingScreen_ is parented to the [CoreGui](/docs/reference/engine/classes/CoreGui.md). Once the place has loaded the [loading screen](/docs/reference/engine/classes/ScreenGui.md) is [parented](/docs/reference/engine/classes/Instance.md) to _nil_. This [ScreenGui](/docs/reference/engine/classes/ScreenGui.md) can be fetched at the destination place using [TeleportService:GetArrivingTeleportGui()](/docs/reference/engine/classes/TeleportService.md), allowing you to parent it to the [PlayerGui](/docs/reference/engine/classes/PlayerGui.md) and perform your own transitions. You are advised to also [parent](/docs/reference/engine/classes/Instance.md) the [ScreenGui](/docs/reference/engine/classes/ScreenGui.md) to the [PlayerGui](/docs/reference/engine/classes/PlayerGui.md) in the start place while the teleport is initiating. #### Studio Limitation Note that this service does not work during playtesting in Roblox Studio; to test aspects of your experience using it, you must publish the experience and play it in the Roblox application. *Security: None · Thread Safety: Unsafe · Capabilities: UI, Teleport* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `gui` | `Instance` | | The loading [ScreenGui](/docs/reference/engine/classes/ScreenGui.md) that is to be displayed during teleportation. | **Returns:** `()` **Teleporting the local player** This snippet demonstrates how `TeleportService` can be used to teleport the [LocalPlayer()](/docs/reference/engine/classes/Players.md) from the client. It also shows how [TeleportService:SetTeleportGui()](/docs/reference/engine/classes/TeleportService.md) can be used to define a custom loading GUI. Note, this `ScreenGui` will need to be retrieved at the destination place using [TeleportService:GetArrivingTeleportGui()](/docs/reference/engine/classes/TeleportService.md) and be parented to the `PlayerGui`. ```lua local TeleportService = game:GetService("TeleportService") local ReplicatedStorage = game:GetService("ReplicatedStorage") local Players = game:GetService("Players") local playerGui = Players.LocalPlayer:WaitForChild("PlayerGui") local PLACE_ID = 0 -- replace here local loadingGui = ReplicatedStorage:FindFirstChild("LoadingGui") -- parent the loading gui for this place loadingGui.Parent = playerGui -- set the loading gui for the destination place TeleportService:SetTeleportGui(loadingGui) TeleportService:Teleport(PLACE_ID) ``` ### Method: TeleportService:SetTeleportSetting **Signature:** `TeleportService:SetTeleportSetting(setting: string, value: Variant): ()` This function stores a value under a given key that persists across all teleportations in the same game. This method is intended for use on the client only and should not be used on the server. The stored value can later be retrieved using [TeleportService:GetTeleportSetting()](/docs/reference/engine/classes/TeleportService.md). This will work in the current place and any subsequent places the [Players.LocalPlayer](/docs/reference/engine/classes/Players.md) teleports to, provided they are in the same game. For example, in a game that allowed crouching you could save whether the user is currently crouching prior to teleporting as a teleport setting: ```lua local TeleportService = game:GetService("TeleportService") local isCrouching = false TeleportService:SetTeleportSetting("isCrouching", isCrouching) ``` The stored value can take one of the following forms: - A table without mixed keys (all strings or all integers) - A string - A number - A bool If data is already stored under the given key, the previous value will be overwritten by the new value. #### Differences from GlobalDataStores Although they share some similarities, there are some key differences between teleport settings and datastores: - [GlobalDataStore:SetAsync()](/docs/reference/engine/classes/GlobalDataStore.md) stores the data on Roblox servers whereas SetTeleportSetting stores the data locally - Data stored in a [GlobalDataStore](/docs/reference/engine/classes/GlobalDataStore.md) is preserved after the user leaves the game universe whereas teleport settings are not - [GlobalDataStores](/docs/reference/engine/classes/GlobalDataStore.md) can only be accessed on the server, whereas teleport settings can only be accessed on the client - [GlobalDataStores](/docs/reference/engine/classes/GlobalDataStore.md) have usage limits, whereas teleport settings do not In general teleport settings should be used to preserve client side information within a single play session across different places in a game. [GlobalDataStores](/docs/reference/engine/classes/GlobalDataStore.md) should be used to save important player data that needs to be accessed across player sessions. #### Teleport settings and security As teleport settings are stored locally, it is possible they can be manipulated by malicious users. This risk can be mitigated by employing server side validation. #### Studio Limitation Note that this service does not work during playtesting in Roblox Studio; to test aspects of your experience using it, you must publish the experience and play it in the Roblox application. *Security: None · Thread Safety: Unsafe · Capabilities: Teleport* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `setting` | `string` | | The key to store the _value_ under. This key can be used to retrieve the value using [TeleportService:GetTeleportSetting()](/docs/reference/engine/classes/TeleportService.md). | | `value` | `Variant` | | The value to store. | **Returns:** `()` ### Method: TeleportService:Teleport **Signature:** `TeleportService:Teleport(placeId: int64, player?: Instance, teleportData: Variant, customLoadingScreen?: Instance): ()` This method should not be used for new work; the numerous teleport functions have been combined into a single method, [TeleportAsync()](/docs/reference/engine/classes/TeleportService.md), which should be used instead. *Security: None · Thread Safety: Unsafe · Capabilities: UI, Teleport* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `placeId` | `int64` | | The ID of the place to teleport to. | | `player` | `Instance` | `nil` | The [Player](/docs/reference/engine/classes/Player.md) to teleport, if this function is being called from the client this defaults to the [Players.LocalPlayer](/docs/reference/engine/classes/Players.md). | | `teleportData` | `Variant` | | Optional data to be passed to the destination place. Can be retrieved using [TeleportService:GetLocalPlayerTeleportData()](/docs/reference/engine/classes/TeleportService.md). | | `customLoadingScreen` | `Instance` | `nil` | Optional custom loading screen to be placed in the [CoreGui](/docs/reference/engine/classes/CoreGui.md) at the destination place. Can be retrieved using [TeleportService:GetArrivingTeleportGui()](/docs/reference/engine/classes/TeleportService.md). | **Returns:** `()` **Teleporting the local player** This snippet demonstrates how `TeleportService` can be used to teleport the [LocalPlayer()](/docs/reference/engine/classes/Players.md) from the client. It also shows how [TeleportService:SetTeleportGui()](/docs/reference/engine/classes/TeleportService.md) can be used to define a custom loading GUI. Note, this `ScreenGui` will need to be retrieved at the destination place using [TeleportService:GetArrivingTeleportGui()](/docs/reference/engine/classes/TeleportService.md) and be parented to the `PlayerGui`. ```lua local TeleportService = game:GetService("TeleportService") local ReplicatedStorage = game:GetService("ReplicatedStorage") local Players = game:GetService("Players") local playerGui = Players.LocalPlayer:WaitForChild("PlayerGui") local PLACE_ID = 0 -- replace here local loadingGui = ReplicatedStorage:FindFirstChild("LoadingGui") -- parent the loading gui for this place loadingGui.Parent = playerGui -- set the loading gui for the destination place TeleportService:SetTeleportGui(loadingGui) TeleportService:Teleport(PLACE_ID) ``` **Teleporting from the server** This snippet demonstrates how `TeleportService` can be used to teleport a `Player` from the server. ```lua local Players = game:GetService("Players") local TeleportService = game:GetService("TeleportService") local PLACE_ID = 0 -- replace here local USER_ID = 1 -- replace with player's UserId local player = Players:GetPlayerByUserId(USER_ID) TeleportService:Teleport(PLACE_ID, player) ``` ### Method: TeleportService:TeleportAsync **Signature:** `TeleportService:TeleportAsync(placeId: int64, players: Instances, teleportOptions?: Instance): Instance` This function serves as the all-encompassing method to teleport a player or group of players from one server to another. It can be used to: - Teleport players to a different place. - Teleport players to a specific server. - Teleport players to a reserved server. #### Group Teleport Limitations - Groups of players can only be teleported within a single experience. - No more than 50 players can be teleported with a single [TeleportService:TeleportAsync()](/docs/reference/engine/classes/TeleportService.md) call. #### Potential Errors This is a list of potential reasons a teleport may fail, ranging from invalid teleports to network issues. | Error | Description | | --- | --- | | Invalid placeId | The provided place ID is below 0. | | Players empty | The provided list of players to teleport is empty. | | List of players instances is incorrect | Any of the provided players is not a Player object. | | TeleportOptions not of correct type | The provided teleportOption is not a TeleportOptions object. | | TeleportAsync called from Client | The client called TeleportAsync, which can only be called from the server. | | Incompatible Parameters | Conflicting teleport options were used and TeleportService doesn't know where to send the player. Conflicting TeleportOption parameters: * ReservedServerAccessCode and ServerInstanceId * ShouldReserveServer and ServerInstanceId * ShouldReserveServer and ReservedServerAccessCode | For more information on how to teleport players between servers and receive user data from a teleport, see [Teleport between places](/docs/en-us/projects/teleport.md#sending-user-data-along-with-teleports). *Yields · Security: None · Thread Safety: Unsafe · Capabilities: UI, Teleport* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `placeId` | `int64` | | The place ID the player(s) should be teleported to. | | `players` | `Instances` | | An array of the player(s) to teleport. | | `teleportOptions` | `Instance` | `nil` | An optional [TeleportOptions](/docs/reference/engine/classes/TeleportOptions.md) object containing additional arguments to the [TeleportService:TeleportAsync()](/docs/reference/engine/classes/TeleportService.md) call. If this is not passed, no result will be returned. | **Returns:** `Instance` — If a [TeleportOptions](/docs/reference/engine/classes/TeleportOptions.md) parameter is passed, this will be a [TeleportAsyncResult](/docs/reference/engine/classes/TeleportAsyncResult.md) object that provides information about the final teleport destination. ### Method: TeleportService:TeleportPartyAsync **Signature:** `TeleportService:TeleportPartyAsync(placeId: int64, players: Instances, teleportData: Variant, customLoadingScreen?: Instance): string` This method should not be used for new work; the numerous teleport functions have been combined into a single method, [TeleportAsync()](/docs/reference/engine/classes/TeleportService.md), which should be used instead. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: UI, Teleport* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `placeId` | `int64` | | The ID of the place to teleport to. | | `players` | `Instances` | | An array containing the [Players](/docs/reference/engine/classes/Player.md) to teleport. | | `teleportData` | `Variant` | | Optional data to be passed to the destination place. Can be retrieved using [TeleportService:GetLocalPlayerTeleportData()](/docs/reference/engine/classes/TeleportService.md). | | `customLoadingScreen` | `Instance` | `nil` | Optional custom loading screen to be placed in the [CoreGui](/docs/reference/engine/classes/CoreGui.md) at the destination place. Can be retrieved using [TeleportService:GetArrivingTeleportGui()](/docs/reference/engine/classes/TeleportService.md). | **Returns:** `string` — The [DataModel.JobId](/docs/reference/engine/classes/DataModel.md) of the server instance the [Players](/docs/reference/engine/classes/Player.md) were teleported to. **Teleport all players in the server** This code sample is an example of how [TeleportService:TeleportPartyAsync()](/docs/reference/engine/classes/TeleportService.md) can be used to teleport a group of `Player|Players`. In this case, all `Player|Players` will be teleported to the specified _placeId_ as a party. The [DataModel.JobId](/docs/reference/engine/classes/DataModel.md) of the destination server is then printed. ```lua local Players = game:GetService("Players") local TeleportService = game:GetService("TeleportService") local PLACE_ID = 0 -- replace local playerList = Players:GetPlayers() local success, result = pcall(function() return TeleportService:TeleportPartyAsync(PLACE_ID, playerList) end) if success then local jobId = result print("Players teleported to", jobId) else warn(result) end ``` ### Method: TeleportService:TeleportToPlaceInstance **Signature:** `TeleportService:TeleportToPlaceInstance(placeId: int64, instanceId: string, player?: Instance, spawnName: string, teleportData: Variant, customLoadingScreen?: Instance): ()` This method should not be used for new work; the numerous teleport functions have been combined into a single method, [TeleportAsync()](/docs/reference/engine/classes/TeleportService.md), which should be used instead. *Security: None · Thread Safety: Unsafe · Capabilities: UI, Teleport* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `placeId` | `int64` | | The ID of the place to teleport to. | | `instanceId` | `string` | | The [DataModel.JobId](/docs/reference/engine/classes/DataModel.md) of the server instance to teleport to. | | `player` | `Instance` | `nil` | The [Player](/docs/reference/engine/classes/Player.md) to teleport, if this function is being called from the client this defaults to the [Players.LocalPlayer](/docs/reference/engine/classes/Players.md). | | `spawnName` | `string` | | Optional name of the [SpawnLocation](/docs/reference/engine/classes/SpawnLocation.md) to spawn at. | | `teleportData` | `Variant` | | Optional data to be passed to the destination place. Can be retrieved using [TeleportService:GetLocalPlayerTeleportData()](/docs/reference/engine/classes/TeleportService.md). | | `customLoadingScreen` | `Instance` | `nil` | Optional custom loading screen to be placed in the [CoreGui](/docs/reference/engine/classes/CoreGui.md) at the destination place. Can be retrieved using [TeleportService:GetArrivingTeleportGui()](/docs/reference/engine/classes/TeleportService.md). | **Returns:** `()` **Following Another Player** The code sample below, when placed inside a `Script` within `ServerScriptService`, will teleport a player who's following another player to the associated place/server. Note that this will not work if the player being followed is in a reserved server. ```lua local TeleportService = game:GetService("TeleportService") local Players = game:GetService("Players") Players.PlayerAdded:Connect(function(player) local followId = player.FollowUserId if followId and followId ~= 0 then -- Is this player following anyone? If so, find out where they are local success, currentInstance, error, placeId, jobIdOrErr = pcall(function() return TeleportService:GetPlayerPlaceInstanceAsync(followId) end) -- followId is the user ID of the player that you want to retrieve the place and job ID for if success then -- Teleports player on success based off jobId TeleportService:TeleportToPlaceInstance(placeId, jobIdOrErr, player) else -- Posts a warning based off returning error warn(jobIdOrErr) end else warn(("Player %d is not following another player!"):format(player.UserId)) end end) ``` ### Method: TeleportService:TeleportToPrivateServer **Signature:** `TeleportService:TeleportToPrivateServer(placeId: int64, reservedServerAccessCode: string, players: Instances, spawnName: string, teleportData: Variant, customLoadingScreen?: Instance): ()` This method should not be used for new work; the numerous teleport functions have been combined into a single method, [TeleportAsync()](/docs/reference/engine/classes/TeleportService.md), which should be used instead. *Security: None · Thread Safety: Unsafe · Capabilities: UI, Teleport* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `placeId` | `int64` | | The ID of the place to teleport to. | | `reservedServerAccessCode` | `string` | | The reserved server access code returned by [TeleportService:ReserveServerAsync()](/docs/reference/engine/classes/TeleportService.md). | | `players` | `Instances` | | An array of [Players](/docs/reference/engine/classes/Player.md) to teleport. | | `spawnName` | `string` | | Optional name of the [SpawnLocation](/docs/reference/engine/classes/SpawnLocation.md) to spawn at. | | `teleportData` | `Variant` | | Optional data to be passed to the destination place. Can be retrieved using [TeleportService:GetLocalPlayerTeleportData()](/docs/reference/engine/classes/TeleportService.md). | | `customLoadingScreen` | `Instance` | `nil` | Optional custom loading screen to be placed in the [CoreGui](/docs/reference/engine/classes/CoreGui.md) at the destination place. Can be retrieved using [TeleportService:GetArrivingTeleportGui()](/docs/reference/engine/classes/TeleportService.md). | **Returns:** `()` **TeleportService: Teleport to a Reserved Server via Chat** The following code would reserve one server, if it hasn't be reserved before. Whenever someone says "reserved", they will be teleported to the private server. ```lua local TeleportService = game:GetService("TeleportService") local Players = game:GetService("Players") local DataStoreService = game:GetService("DataStoreService") local dataStore = DataStoreService:GetGlobalDataStore() -- Get the saved code local code = dataStore:GetAsync("ReservedServer") if typeof(code) ~= "string" then -- None saved, create one code = TeleportService:ReserveServerAsync(game.PlaceId) dataStore:SetAsync("ReservedServer", code) end local function joined(player) player.Chatted:Connect(function(message) if message == "reserved" then TeleportService:TeleportToPrivateServer(game.PlaceId, code, { player }) end end) end Players.PlayerAdded:Connect(joined) ``` **TeleportService: Teleport to a Reserved Server** The following code would send everyone in the current game to a reserved server. Since reserved servers can only be joined by using [TeleportService:TeleportToPrivateServer()](/docs/reference/engine/classes/TeleportService.md), nobody will join the reserved server afterwards. Mind that, since everyone will be teleported, the current server will (probably) shutdown as nobody would be left in it. ```lua local TeleportService = game:GetService("TeleportService") local Players = game:GetService("Players") local code = TeleportService:ReserveServerAsync(game.PlaceId) local players = Players:GetPlayers() TeleportService:TeleportToPrivateServer(game.PlaceId, code, players) -- You could add extra arguments to this function: spawnName, teleportData and customLoadingScreen ``` ### Method: TeleportService:TeleportToSpawnByName **Signature:** `TeleportService:TeleportToSpawnByName(placeId: int64, spawnName: string, player?: Instance, teleportData: Variant, customLoadingScreen?: Instance): ()` This method should not be used for new work; the numerous teleport functions have been combined into a single method, [TeleportAsync()](/docs/reference/engine/classes/TeleportService.md), which should be used instead. *Security: None · Thread Safety: Unsafe · Capabilities: UI, Teleport* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `placeId` | `int64` | | The ID of the place to teleport to. | | `spawnName` | `string` | | The name of the [SpawnLocation](/docs/reference/engine/classes/SpawnLocation.md) to spawn at. | | `player` | `Instance` | `nil` | The [Player](/docs/reference/engine/classes/Player.md) to teleport, if this function is being called from the client this defaults to the [Players.LocalPlayer](/docs/reference/engine/classes/Players.md). | | `teleportData` | `Variant` | | Optional data to be passed to the destination place. Can be retrieved using [TeleportService:GetLocalPlayerTeleportData()](/docs/reference/engine/classes/TeleportService.md). | | `customLoadingScreen` | `Instance` | `nil` | Optional custom loading screen to be placed in the [CoreGui](/docs/reference/engine/classes/CoreGui.md) at the destination place. Can be retrieved using [TeleportService:GetArrivingTeleportGui()](/docs/reference/engine/classes/TeleportService.md). | **Returns:** `()` **TeleportService:TeleportToSpawnByName** This code will teleport a player to Crossroads, and if there is a spawn named "TeleportSpawn" then the player would spawn on it. This assumes it's being used in a [LocalScript](/docs/reference/engine/classes/LocalScript.md). ```lua local TeleportService = game:GetService("TeleportService") TeleportService:TeleportToSpawnByName(1818, "TeleportSpawn") ``` ### Method: TeleportService:ReserveServer **Signature:** `TeleportService:ReserveServer(placeId: int64): Tuple` Returns an access code that can be used to teleport players to a reserved server, along with the [DataModel.PrivateServerId](/docs/reference/engine/classes/DataModel.md) for it. *Yields · Security: None · Thread Safety: Unsafe · Capabilities: Teleport* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `placeId` | `int64` | | The [DataModel.PlaceId](/docs/reference/engine/classes/DataModel.md) of the place the reserved server is being created for. | **Returns:** `Tuple` — The server access code required by [TeleportService:TeleportToPrivateServer()](/docs/reference/engine/classes/TeleportService.md) and the [DataModel.PrivateServerId](/docs/reference/engine/classes/DataModel.md) for the reserved server. **TeleportService: Teleport to a Reserved Server** The following code would send everyone in the current game to a reserved server. Since reserved servers can only be joined by using [TeleportService:TeleportToPrivateServer()](/docs/reference/engine/classes/TeleportService.md), nobody will join the reserved server afterwards. Mind that, since everyone will be teleported, the current server will (probably) shutdown as nobody would be left in it. ```lua local TeleportService = game:GetService("TeleportService") local Players = game:GetService("Players") local code = TeleportService:ReserveServerAsync(game.PlaceId) local players = Players:GetPlayers() TeleportService:TeleportToPrivateServer(game.PlaceId, code, players) -- You could add extra arguments to this function: spawnName, teleportData and customLoadingScreen ``` **TeleportService: Teleport to a Reserved Server via Chat** The following code would reserve one server, if it hasn't be reserved before. Whenever someone says "reserved", they will be teleported to the private server. ```lua local TeleportService = game:GetService("TeleportService") local Players = game:GetService("Players") local DataStoreService = game:GetService("DataStoreService") local dataStore = DataStoreService:GetGlobalDataStore() -- Get the saved code local code = dataStore:GetAsync("ReservedServer") if typeof(code) ~= "string" then -- None saved, create one code = TeleportService:ReserveServerAsync(game.PlaceId) dataStore:SetAsync("ReservedServer", code) end local function joined(player) player.Chatted:Connect(function(message) if message == "reserved" then TeleportService:TeleportToPrivateServer(game.PlaceId, code, { player }) end end) end Players.PlayerAdded:Connect(joined) ``` ## Events ### Event: TeleportService.LocalPlayerArrivedFromTeleport **Signature:** `TeleportService.LocalPlayerArrivedFromTeleport(loadingGui: Instance, dataTable: Variant)` This function fires when the [Players.LocalPlayer](/docs/reference/engine/classes/Players.md) enters the place following a teleport. The `teleportData` and `customLoadingScreen` are provided as arguments. When fetching _teleportData_ and the _customLoadingScreen_ you are advised to use [TeleportService:GetLocalPlayerTeleportData()](/docs/reference/engine/classes/TeleportService.md) and [TeleportService:GetArrivingTeleportGui()](/docs/reference/engine/classes/TeleportService.md) instead. This is because these functions can be called immediately without having to wait for this event to fire. This event should be connected immediately in a [LocalScript](/docs/reference/engine/classes/LocalScript.md) parented to [ReplicatedFirst](/docs/reference/engine/classes/ReplicatedFirst.md). Otherwise, when the connection is made the event may have already fired. #### Loading Screen During a teleport, while the destination place is loading, the _customLoadingScreen_ is parented to the [CoreGui](/docs/reference/engine/classes/CoreGui.md). Once the place has loaded the [loading screen](/docs/reference/engine/classes/ScreenGui.md) is [parented](/docs/reference/engine/classes/Instance.md) to _nil_. If you wish to preserve the _customLoadingScreen_ and perform your own transitions, you will need to parent it to the local player's [PlayerGui](/docs/reference/engine/classes/PlayerGui.md). For example, using the following code inside a [LocalScript](/docs/reference/engine/classes/LocalScript.md) in [ReplicatedFirst](/docs/reference/engine/classes/ReplicatedFirst.md): ```lua local TeleportService = game:GetService("TeleportService") local Players = game:GetService("Players") local ReplicatedFirst = game:GetService("ReplicatedFirst") TeleportService.LocalPlayerArrivedFromTeleport:Connect(function(customLoadingScreen, teleportData) local playerGui = Players.LocalPlayer:WaitForChild("PlayerGui") ReplicatedFirst:RemoveDefaultLoadingScreen() customLoadingScreen.Parent = playerGui -- animate screen here wait(5) -- destroy screen customLoadingScreen:Destroy() end) ``` The _customLoadingScreen_ will not be used if the destination place is in a different game. #### Studio Limitation Note that this service does not work during playtesting in Roblox Studio; to test aspects of your experience using it, you must publish the experience and play it in the Roblox application. *Security: None · Capabilities: Teleport* **Parameters:** | Name | Type | Description | |------|------|-------------| | `loadingGui` | `Instance` | The _customLoadingScreen_ the [LocalPlayer](/docs/reference/engine/classes/Players.md) arrived into the place with. | | `dataTable` | `Variant` | The _teleportData_ the [LocalPlayer](/docs/reference/engine/classes/Players.md) arrived into the place with. | ### Event: TeleportService.TeleportInitFailed **Signature:** `TeleportService.TeleportInitFailed(player: Instance, teleportResult: TeleportResult, errorMessage: string, placeId: int64, teleportOptions: Instance)` This event fires on both the client and the server when a request to teleport from a function such as [TeleportService:TeleportAsync()](/docs/reference/engine/classes/TeleportService.md) fails and the player does not leave the current server. It provides a reason for the failure, as well as all of the information necessary to retry the teleport. If a group teleport fails, the event will fire once per player. #### TeleportOptions The [TeleportOptions](/docs/reference/engine/classes/TeleportOptions.md) object provided by this event is not identical to the one passed to the original [TeleportService:TeleportAsync()](/docs/reference/engine/classes/TeleportService.md) call. It is a new object populated with the necessary parameters to retry the teleport and send the player to the exact same destination. This is especially important for facilitating group teleports when they fail. | Original Teleport Type | Teleport Data | ReservedServerAccessCode | ServerInstanceId | ShouldReserveServer | | --- | --- | --- | --- | --- | | Individual player to place | Original value | None | None | false | | Player(s) to reserved server | Original value | Original value, or the code generated if ShouldReserveServer was originally true | None | false | | Player(s) to specific server | Original value | None | Original value | false | | Players to place | Original value | None | Same destination ID as the other players in the original teleport | false | For more information on how to teleport players between servers, see [Teleport between places](/docs/en-us/projects/teleport.md). *Security: None · Capabilities: Teleport* **Parameters:** | Name | Type | Description | |------|------|-------------| | `player` | `Instance` | The [Player](/docs/reference/engine/classes/Player.md) instance that failed to teleport. | | `teleportResult` | `TeleportResult` | The reason for the teleport failure. | | `errorMessage` | `string` | The message provided to the player explaining the teleport failure. | | `placeId` | `int64` | The original target place ID of the teleport. | | `teleportOptions` | `Instance` | A [TeleportOptions](/docs/reference/engine/classes/TeleportOptions.md) object that can be passed back to [TeleportService:TeleportAsync()](/docs/reference/engine/classes/TeleportService.md) to retry the failed teleport. | ## Inherited Members ### From [Instance](/docs/reference/engine/classes/Instance.md) - **Property `Archivable`** (`boolean`): Determines if an Instance and its descendants can be cloned using - **Property `archivable`** (`boolean`): *(deprecated, hidden)* - **Property `Capabilities`** (`SecurityCapabilities`): The set of capabilities allowed to be used for scripts inside this - **Property `Name`** (`string`): A non-unique identifier of the Instance. - **Property `Parent`** (`Instance`): Determines the hierarchical parent of the Instance. - **Property `PredictionMode`** (`PredictionMode`): - **Property `RobloxLocked`** (`boolean`): A deprecated property that used to protect CoreGui objects. *(hidden)* - **Property `Sandboxed`** (`boolean`): When enabled, the instance can only access abilities in its `Capabilities` - **Property `UniqueId`** (`UniqueId`): A unique identifier for the instance. - **Method `AddTag(tag: string): ()`**: Applies a tag to the instance. - **Method `children(): Instances`**: Returns an array of the object's children. *(deprecated)* - **Method `ClearAllChildren(): ()`**: This method destroys all of an instance's children. - **Method `Clone(): Instance`**: Create a copy of an instance and all its descendants, ignoring instances - **Method `clone(): Instance`**: *(deprecated)* - **Method `Destroy(): ()`**: Sets the Instance.Parent property to `nil`, locks the - **Method `destroy(): ()`**: *(deprecated)* - **Method `FindFirstAncestor(name: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorOfClass(className: string): Instance?`**: Returns the first ancestor of the Instance whose - **Method `FindFirstAncestorWhichIsA(className: string): Instance?`**: Returns the first ancestor of the Instance for whom - **Method `FindFirstChild(name: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance found with the given name. - **Method `findFirstChild(name: string, recursive?: boolean): Instance`**: *(deprecated)* - **Method `FindFirstChildOfClass(className: string): Instance?`**: Returns the first child of the Instance whose - **Method `FindFirstChildWhichIsA(className: string, recursive?: boolean): Instance?`**: Returns the first child of the Instance for whom - **Method `FindFirstDescendant(name: string): Instance?`**: Returns the first descendant found with the given Instance.Name. - **Method `GetActor(): Actor?`**: Returns the Actor associated with the Instance, if any. - **Method `GetAttribute(attribute: string): Variant`**: Returns the value which has been assigned to the given attribute name. - **Method `GetAttributeChangedSignal(attribute: string): RBXScriptSignal`**: Returns an event that fires when the given attribute changes. - **Method `GetAttributes(): Dictionary`**: Returns a dictionary of the instance's attributes. - **Method `GetChildren(): Instances`**: Returns an array containing all of the instance's children. - **Method `getChildren(): Instances`**: *(deprecated)* - **Method `GetDebugId(scopeLength?: int): string`**: Returns a coded string of the debug ID used internally by Roblox. - **Method `GetDescendants(): Instances`**: Returns an array containing all of the descendants of the instance. - **Method `GetFullName(): string`**: Returns a string describing the instance's ancestry. - **Method `GetStyled(name: string, selector: string?): Variant`**: Returns the styled or explicitly modified value of the specified property, - **Method `GetStyledPropertyChangedSignal(property: string): RBXScriptSignal`**: - **Method `GetTags(): Array`**: Gets an array of all tags applied to the instance. - **Method `HasTag(tag: string): boolean`**: Check whether the instance has a given tag. - **Method `IsAncestorOf(descendant: Instance): boolean`**: Returns true if an Instance is an ancestor of the given - **Method `IsDescendantOf(ancestor: Instance): boolean`**: Returns `true` if an Instance is a descendant of the given - **Method `isDescendantOf(ancestor: Instance): boolean`**: *(deprecated)* - **Method `IsPropertyModified(property: string): boolean`**: Returns `true` if the value stored in the specified property is not equal - **Method `QueryDescendants(selector: string): Instances`**: Returns an array containing all descendants of the instance that match the - **Method `Remove(): ()`**: Sets the object's `Parent` to `nil`, and does the same for all its *(deprecated)* - **Method `remove(): ()`**: *(deprecated)* - **Method `RemoveTag(tag: string): ()`**: Removes a tag from the instance. - **Method `ResetPropertyToDefault(property: string): ()`**: Resets a property to its default value. - **Method `SetAttribute(attribute: string, value: Variant): ()`**: Sets the attribute with the given name to the given value. - **Method `WaitForChild(childName: string, timeOut: double): Instance`**: Returns the child of the Instance with the given name. If the - **Event `AncestryChanged`**: Fires when the Instance.Parent property of this object or one of - **Event `AttributeChanged`**: Fires whenever an attribute is changed on the Instance. - **Event `ChildAdded`**: Fires after an object is parented to this Instance. - **Event `childAdded`**: *(deprecated)* - **Event `ChildRemoved`**: Fires after a child is removed from this Instance. - **Event `DescendantAdded`**: Fires after a descendant is added to the Instance. - **Event `DescendantRemoving`**: Fires immediately before a descendant of the Instance is removed. - **Event `Destroying`**: Fires immediately before (or is deferred until after) the instance is - **Event `StyledPropertiesChanged`**: Fires whenever any style property is changed on the instance, including ### From [Object](/docs/reference/engine/classes/Object.md) - **Property `ClassName`** (`string`): A read-only string representing the class this Object belongs to. - **Property `className`** (`string`): *(deprecated)* - **Method `GetPropertyChangedSignal(property: string): RBXScriptSignal`**: Get an event that fires when a given property of the object changes. - **Method `IsA(className: string): boolean`**: Returns true if an object's class matches or inherits from a given class. - **Method `isA(className: string): boolean`**: *(deprecated)* - **Event `Changed`**: Fires immediately after a property of the object changes, with some --- name: Terrain last_updated: 2026-06-29T19:34:08Z inherits: - BasePart - PVInstance - Instance - Object type: class memory_category: Instances tags: - NotCreatable summary: "`Terrain` lets you to create dynamically morphable environments." --- # Class: Terrain > `Terrain` lets you to create dynamically morphable environments. ## Description The `Terrain` class lets you create dynamically morphable environments. It is based on a 4×4×4 grid of cells, where each cell has a number between `0` and `1` representing how much the geometry should occupy the cell, and the material of the cell. The occupancy determines how the cell will morph together with surrounding cells, and the result is the illusion of having no grid constraint. For more information, see [Terrain](/docs/en-us/parts/terrain.md). ## Properties ### Property: Terrain.Decoration ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` Currently enables or disables animated grass on the [Grass](/docs/reference/engine/enums/Material.md) terrain material, although future modifications of this property may control additional decorative features. ### Property: Terrain.GrassLength ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` Specifies the length of animated grass on the [Grass](/docs/reference/engine/enums/Material.md) terrain material, assuming [Decoration](/docs/reference/engine/classes/Terrain.md) is enabled. Valid values are between `0.1` and `1`. ### Property: Terrain.MaterialColors ```json { "type": "BinaryString", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` `MaterialColors` represents the editor for the **Material Color** feature and cannot be edited by scripts. To get the color of a material, use [Terrain:GetMaterialColor()](/docs/reference/engine/classes/Terrain.md). To set the color of a material, use [Terrain:SetMaterialColor()](/docs/reference/engine/classes/Terrain.md). ### Property: Terrain.MaxExtents ```json { "type": "Region3int16", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Environment" ] } ``` Displays the boundaries of the largest possible editable region. ### Property: Terrain.WaterColor ```json { "type": "Color3", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` The tint of `Terrain` water. ### Property: Terrain.WaterReflectance ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` Controls how opaque `Terrain` water reflections are. ### Property: Terrain.WaterTransparency ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` The transparency of `Terrain` water. ### Property: Terrain.WaterWaveSize ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` Sets the maximum height of `Terrain` water waves in studs. This is currently constrained to between `0` and `1`. ### Property: Terrain.WaterWaveSpeed ```json { "type": "float", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": true, "can_save": true }, "thread_safety": "ReadSafe", "category": "Appearance", "capabilities": [ "Environment" ] } ``` Sets how many times `Terrain` water waves will move up and down per minute. This is currently constrained to between `0` and `100`. ### Property: Terrain.IsSmooth ```json { "type": "boolean", "access": "ReadWrite", "security": { "read": "None", "write": "None" }, "serialization": { "can_load": false, "can_save": false }, "deprecated": true, "thread_safety": "ReadSafe", "category": "Data", "capabilities": [ "Environment" ] } ``` > **Deprecated:** The legacy terrain engine has been removed, so this property will always be `true`. Returns `true` if the game is using the smooth terrain system. ## Methods ### Method: Terrain:CellCenterToWorld **Signature:** `Terrain:CellCenterToWorld(x: int, y: int, z: int): Vector3` Returns the world position of the center of the terrain cell. *Security: None · Thread Safety: Unsafe · Capabilities: Environment* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `int` | | | | `y` | `int` | | | | `z` | `int` | | | **Returns:** `Vector3` ### Method: Terrain:CellCornerToWorld **Signature:** `Terrain:CellCornerToWorld(x: int, y: int, z: int): Vector3` Returns the position of the lower-left-forward corner of the grid cell. *Security: None · Thread Safety: Unsafe · Capabilities: Environment* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `int` | | | | `y` | `int` | | | | `z` | `int` | | | **Returns:** `Vector3` ### Method: Terrain:Clear **Signature:** `Terrain:Clear(): ()` Clears all terrain. *Security: None · Thread Safety: Unsafe · Capabilities: Environment* **Returns:** `()` ### Method: Terrain:CopyRegion **Signature:** `Terrain:CopyRegion(region: Region3int16): TerrainRegion` Stores a chunk of terrain into a [TerrainRegion](/docs/reference/engine/classes/TerrainRegion.md) object so it can be loaded back later. Note that [TerrainRegion](/docs/reference/engine/classes/TerrainRegion.md) data does not replicate between server and client. *Security: None · Thread Safety: Unsafe · Capabilities: Environment* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `region` | `Region3int16` | | | **Returns:** `TerrainRegion` **Terrain:CopyRegion()** The following code will copy all terrain and clear it. After 5 seconds it will paste the terrain back. ```lua local terrainRegion = workspace.Terrain:CopyRegion(workspace.Terrain.MaxExtents) workspace.Terrain:Clear() task.wait(5) workspace.Terrain:PasteRegion(terrainRegion, workspace.Terrain.MaxExtents.Min, true) ``` ### Method: Terrain:CountCells **Signature:** `Terrain:CountCells(): int` Returns the number of non-empty cells in the terrain. *Security: None · Thread Safety: Unsafe · Capabilities: Environment* **Returns:** `int` ### Method: Terrain:FillBall **Signature:** `Terrain:FillBall(center: Vector3, radius: float, material: Material): ()` Fills a ball of smooth terrain in a given space. *Security: None · Thread Safety: Unsafe · Capabilities: Environment* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `center` | `Vector3` | | The position of the center of the terrain ball. | | `radius` | `float` | | The radius in studs of the terrain ball. | | `material` | `Material` | | The [Material](/docs/reference/engine/enums/Material.md) of the terrain ball. | **Returns:** `()` ### Method: Terrain:FillBlock **Signature:** `Terrain:FillBlock(cframe: CFrame, size: Vector3, material: Material): ()` Fills a block of smooth terrain with a given location, rotation, size, and material. *Security: None · Thread Safety: Unsafe · Capabilities: Environment* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `cframe` | `CFrame` | | The position and orientation of the terrain block. | | `size` | `Vector3` | | The size in studs of the square block (both the height and width). | | `material` | `Material` | | The [Material](/docs/reference/engine/enums/Material.md) of the terrain block. | **Returns:** `()` ### Method: Terrain:FillCylinder **Signature:** `Terrain:FillCylinder(cframe: CFrame, height: float, radius: float, material: Material): ()` Fills a cylinder of smooth terrain in a given space. The space is defined using a [CFrame](/docs/reference/engine/datatypes/CFrame.md), height, and radius. *Security: None · Thread Safety: Unsafe · Capabilities: Environment* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `cframe` | `CFrame` | | The position and orientation of the terrain cylinder. | | `height` | `float` | | The height in studs of the terrain cylinder. | | `radius` | `float` | | The radius in studs of the terrain cylinder. | | `material` | `Material` | | The [Material](/docs/reference/engine/enums/Material.md) of the terrain cylinder. | **Returns:** `()` ### Method: Terrain:FillRegion **Signature:** `Terrain:FillRegion(region: Region3, resolution: float, material: Material): ()` Fills a [Region3](/docs/reference/engine/datatypes/Region3.md) space with smooth terrain. *Security: None · Thread Safety: Unsafe · Capabilities: Environment* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `region` | `Region3` | | | | `resolution` | `float` | | | | `material` | `Material` | | | **Returns:** `()` ### Method: Terrain:FillWedge **Signature:** `Terrain:FillWedge(cframe: CFrame, size: Vector3, material: Material): ()` This method fills a wedge-shaped volume of [Terrain](/docs/reference/engine/classes/Terrain.md) with the given [Material](/docs/reference/engine/enums/Material.md) and the area's [CFrame](/docs/reference/engine/datatypes/CFrame.md) and size. The orientation of the wedge is the same as an equivalent [WedgePart](/docs/reference/engine/classes/WedgePart.md). *Security: None · Thread Safety: Unsafe · Capabilities: Environment* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `cframe` | `CFrame` | | The position and orientation of the wedge to fill. | | `size` | `Vector3` | | The size of the wedge to fill. | | `material` | `Material` | | The material with which the wedge will be filled. | **Returns:** `()` ### Method: Terrain:GetMaterialColor **Signature:** `Terrain:GetMaterialColor(material: Material): Color3` Returns the current terrain material color for the specified terrain material. *Security: None · Thread Safety: Safe · Capabilities: Environment* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `material` | `Material` | | | **Returns:** `Color3` ### Method: Terrain:PasteRegion **Signature:** `Terrain:PasteRegion(region: TerrainRegion, corner: Vector3int16, pasteEmptyCells: boolean): ()` Applies a chunk of terrain to the `Terrain` object. Note that [TerrainRegion](/docs/reference/engine/classes/TerrainRegion.md) data does not replicate between server and client. *Security: None · Thread Safety: Unsafe · Capabilities: Environment* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `region` | `TerrainRegion` | | | | `corner` | `Vector3int16` | | | | `pasteEmptyCells` | `boolean` | | | **Returns:** `()` **Create, Copy and Paste Terrain** Creates some terrain, copies it, then pastes it. ```lua local Terrain = workspace.Terrain -- Create a simple terrain region local initialRegion = Region3.new(Vector3.zero, Vector3.one * 10) Terrain:FillRegion(initialRegion, 4, Enum.Material.Grass) -- Copy the region local copyRegion = Region3int16.new(Vector3int16.new(0, 0, 0), Vector3int16.new(10, 10, 10)) local copiedRegion = Terrain:CopyRegion(copyRegion) -- Define where to paste the region local newRegionCorner = Vector3int16.new(5, 0, 0) -- Paste the region Terrain:PasteRegion(copiedRegion, newRegionCorner, true) ``` ### Method: Terrain:ReadVoxelChannels **Signature:** `Terrain:ReadVoxelChannels(region: Region3, resolution: float, channelIds: Array): Dictionary` Returns a region of terrain voxel data in table format based on the channel names. *Security: None · Thread Safety: Safe · Capabilities: Environment* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `region` | `Region3` | | Target region to read from. Must be aligned to the voxel grid. Will throw an error if region is too large; limit is currently 4194304 voxels³. | | `resolution` | `float` | | Voxel resolution. Must be 4. | | `channelIds` | `Array` | | Array of channel IDs (strings) that need to be accessed from the voxel data. Each channel ID represents a type of data that's stored in voxel. Current supported IDs are `{"SolidMaterial", "SolidOccupancy", "LiquidOccupancy"}`. | **Returns:** `Dictionary` — Returns voxel data as a dictionary based on the `channelIds` input. Keys represent each channel ID with their respective value as an array of 3D data. - `SolidMaterial` — The [Material](/docs/reference/engine/enums/Material.md) material of the voxel. Note that [Water](/docs/reference/engine/enums/Material.md) is not supported anymore; instead, a voxel that contains water will have a value of `LiquidOccupancy`. - `SolidOccupancy` — The occupancy of the voxel's material as specified in the `SolidMaterial` channel. This is a value between 0 (empty) and 1 (full). - `LiquidOccupancy` — Specifies the occupancy of the [Water](/docs/reference/engine/enums/Material.md) material in a voxel as a value between 0 (no water) and 1 (full of water). If the `SolidOccupancy` is 1 and the `SolidMaterial` is not [Air](/docs/reference/engine/enums/Material.md), this will be 0. The dictionary also contains a `Size` key with a value representing the 3D array size of each channel data. **Terrain:ReadVoxelChannels()** ```lua local REGION_START = Vector3.new(-20, -20, -20) local REGION_END = Vector3.new(20, 20, 20) local function printRegion(terrain, region) local channelOutput = terrain:ReadVoxelChannels(region, 4, { "SolidOccupancy", "SolidMaterial", "LiquidOccupancy" }) local size = channelOutput.Size for x = 1, size.X do for y = 1, size.Y do for z = 1, size.Z do print( ("(%2i, %2i, %2i): %.2f %s %.2f"):format( x, y, z, channelOutput.SolidOccupancy[x][y][z], channelOutput.SolidMaterial[x][y][z].Name, channelOutput.LiquidOccupancy[x][y][z] ) ) end end end end local region = Region3.new(REGION_START, REGION_END) printRegion(workspace.Terrain, region) ``` ### Method: Terrain:ReadVoxels **Signature:** `Terrain:ReadVoxels(region: Region3, resolution: float): Tuple` Returns a certain region of smooth terrain in table format. *Security: None · Thread Safety: Safe · Capabilities: Environment* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `region` | `Region3` | | Target region to read from. Must be aligned to the voxel grid. Will throw an error if region is too large. The limit is currently 4194304 voxels³. | | `resolution` | `float` | | Voxel resolution. Must be `4`. | **Returns:** `Tuple` — Returns raw voxel data as two 3D arrays. - `materials` - 3D array of [Material](/docs/reference/engine/enums/Material.md) from the target area. Also contains a Size field, equal to the dimensions of the nested arrays. - `occupancies` - 3D array of occupancy values from the target area. Also contains a Size field, equal to the dimensions of the nested arrays. **Terrain:ReadVoxels()** ```lua local REGION_START = Vector3.new(-20, -20, -20) local REGION_END = Vector3.new(20, 20, 20) local function printRegion(terrain, region) local materials, occupancies = terrain:ReadVoxels(region, 4) local size = materials.Size -- Same as occupancies.Size for x = 1, size.X, 1 do for y = 1, size.Y, 1 do for z = 1, size.Z, 1 do print(("(%2i, %2i, %2i): %.2f %s"):format(x, y, z, occupancies[x][y][z], materials[x][y][z].Name)) end end end end local region = Region3.new(REGION_START, REGION_END) printRegion(workspace.Terrain, region) ``` ### Method: Terrain:ReplaceMaterial **Signature:** `Terrain:ReplaceMaterial(region: Region3, resolution: float, sourceMaterial: Material, targetMaterial: Material): ()` ReplaceMaterial replaces terrain of a certain [Material](/docs/reference/engine/enums/Material.md) within a [Region3](/docs/reference/engine/datatypes/Region3.md) with another material. Essentially, it is a find-and-replace operation on [Terrain](/docs/reference/engine/classes/Terrain.md) materials. When calling this method, the `resolution` parameter must be exactly `4`. Additionally, `region` must be aligned to the terrain materials grid, such that the components of the region's minimum and maximum points must be divisible by `4`. Use [Region3:ExpandToGrid()](/docs/reference/engine/datatypes/Region3.md) to make a region compatible with this function. *Security: None · Thread Safety: Unsafe · Capabilities: Environment* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `region` | `Region3` | | The region in which the replacement operation will occur. | | `resolution` | `float` | | The resolution at which the replacement operation will take place; at the moment this must be exactly `4`. | | `sourceMaterial` | `Material` | | The old material that shall be replaced. | | `targetMaterial` | `Material` | | The new material. | **Returns:** `()` **Terrain:ReplaceMaterial** This code sample demonstrates the usage of [Terrain:ReplaceMaterial()](/docs/reference/engine/classes/Terrain.md) by replacing grass near the game origin with asphalt. ```lua local terrain = workspace.Terrain local region = Region3.new(Vector3.new(-20, -20, -20), Vector3.new(20, 20, 20)) local resolution = 4 local materialToReplace = Enum.Material.Grass local replacementMaterial = Enum.Material.Asphalt terrain:ReplaceMaterial(region, resolution, materialToReplace, replacementMaterial) ``` ### Method: Terrain:SetMaterialColor **Signature:** `Terrain:SetMaterialColor(material: Material, value: Color3): ()` Sets current terrain material color for specified terrain material. Terrain material will shift its base color toward specified color. *Security: None · Thread Safety: Unsafe · Capabilities: Environment* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `material` | `Material` | | | | `value` | `Color3` | | | **Returns:** `()` ### Method: Terrain:WorldToCell **Signature:** `Terrain:WorldToCell(position: Vector3): Vector3` Returns the grid cell location that contains the point `position`. *Security: None · Thread Safety: Unsafe · Capabilities: Environment* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `position` | `Vector3` | | | **Returns:** `Vector3` ### Method: Terrain:WorldToCellPreferEmpty **Signature:** `Terrain:WorldToCellPreferEmpty(position: Vector3): Vector3` Returns the grid cell location that contains the point `position`, preferring empty grid cells when position is on a grid edge. *Security: None · Thread Safety: Unsafe · Capabilities: Environment* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `position` | `Vector3` | | | **Returns:** `Vector3` ### Method: Terrain:WorldToCellPreferSolid **Signature:** `Terrain:WorldToCellPreferSolid(position: Vector3): Vector3` Returns the grid cell location that contains the point position, preferring non-empty grid cells when position is on a grid edge. *Security: None · Thread Safety: Unsafe · Capabilities: Environment* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `position` | `Vector3` | | | **Returns:** `Vector3` ### Method: Terrain:WriteVoxelChannels **Signature:** `Terrain:WriteVoxelChannels(region: Region3, resolution: float, channels: Dictionary): ()` Sets a region of terrain using a dictionary of voxel channel data. *Security: None · Thread Safety: Unsafe · Capabilities: Environment* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `region` | `Region3` | | Target region to write to. Must be aligned to the voxel grid. Will throw an error if region is too large; limit is currently 4194304 voxels³. | | `resolution` | `float` | | Voxel resolution. Must be `4`. | | `channels` | `Dictionary` | | Dictionary of voxel data similar to the return value of [ReadVoxelChannels()](/docs/reference/engine/classes/Terrain.md). Keys represent each channel ID with their respective value as an array of 3D data. The dictionary can support single or multiple channel inputs. - `SolidMaterial` — The [Material](/docs/reference/engine/enums/Material.md) material of the voxel. Note that [Water](/docs/reference/engine/enums/Material.md) is not supported anymore; instead, a voxel that contains only water should be entered as `SolidMaterial = Enum.Material.Air, LiquidOccupancy = x`, where `x` is a number between `0` (exclusive) and `1` (inclusive). - `SolidOccupancy` — The occupancy of the voxel's material as specified in the `SolidMaterial` channel. This should be a value between `0` (empty) and `1` (full). - `LiquidOccupancy` — Specifies the occupancy of the [Water](/docs/reference/engine/enums/Material.md) material in a voxel as a value between `0` (no water) and `1` (full of water). If the `SolidOccupancy` is 1 and the `SolidMaterial` is not [Air](/docs/reference/engine/enums/Material.md), this will be `0`. | **Returns:** `()` **Terrain:WriteVoxelChannels()** ```lua local region = Region3.new(Vector3.new(0, 0, 0), Vector3.new(64, 32, 64)) local RESOLUTION = 4 local OCC_EPSILON = 1 / 256 local function generateRandomTerrainInRegion(regionInput) local region = regionInput:ExpandToGrid(4) local size = region.Size / 4 local solidMaterials = {} local solidOccupancies = {} local waterOcc = {} for x = 1, size.X do table.insert(solidMaterials, {}) table.insert(solidOccupancies, {}) table.insert(waterOcc, {}) for y = 1, size.Y do table.insert(solidMaterials[x], {}) table.insert(solidOccupancies[x], {}) table.insert(waterOcc[x], {}) for z = 1, size.Z do local mat = if math.random() < 0.5 then Enum.Material.Air else Enum.Material.Sand local occ = 0 local water = math.random() if mat == Enum.Material.Sand then occ = math.random() / 2 + 0.5 if occ > 1 - OCC_EPSILON then water = 0 -- Solids cannot contain water end else occ = 0 end table.insert(solidMaterials[x][y], mat) table.insert(solidOccupancies[x][y], occ) table.insert(waterOcc[x][y], water) end end end return { SolidMaterial = solidMaterials, SolidOccupancy = solidOccupancies, LiquidOccupancy = waterOcc } end local regionContent = generateRandomTerrainInRegion(region) workspace.Terrain:WriteVoxelChannels(region, 4, regionContent) ``` ### Method: Terrain:WriteVoxels **Signature:** `Terrain:WriteVoxels(region: Region3, resolution: float, materials: Array, occupancy: Array): ()` Sets a certain region of smooth terrain using table format. *Security: None · Thread Safety: Unsafe · Capabilities: Environment* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `region` | `Region3` | | Target region to write to. Must be aligned to the voxel grid. Will throw an error if region is too large. | | `resolution` | `float` | | Voxel resolution. Must be `4`. | | `materials` | `Array` | | 3D array of [Material](/docs/reference/engine/enums/Material.md). Dimensions must exactly match the size of the target region in voxels. | | `occupancy` | `Array` | | 3D array of voxel occupancies (number between `0` and `1`). Dimensions must exactly match the size of the target region in voxels. | **Returns:** `()` **Terrain:WriteVoxels()** ```lua local terrain = workspace.Terrain local resolution = 4 local region = Region3.new(Vector3.new(0, 0, 0), Vector3.new(16, 28, 20)):ExpandToGrid(resolution) local materials = { { { Enum.Material.CrackedLava, Enum.Material.CrackedLava, Enum.Material.CrackedLava, Enum.Material.CrackedLava, Enum.Material.CrackedLava, }, { Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock }, { Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock }, { Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand }, { Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand }, { Enum.Material.Mud, Enum.Material.Mud, Enum.Material.Mud, Enum.Material.Mud, Enum.Material.Mud }, { Enum.Material.Air, Enum.Material.Air, Enum.Material.Air, Enum.Material.Air, Enum.Material.Air }, }, { { Enum.Material.CrackedLava, Enum.Material.CrackedLava, Enum.Material.CrackedLava, Enum.Material.CrackedLava, Enum.Material.CrackedLava, }, { Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock }, { Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock }, { Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand }, { Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand }, { Enum.Material.Mud, Enum.Material.Snow, Enum.Material.Snow, Enum.Material.Snow, Enum.Material.Mud }, { Enum.Material.Air, Enum.Material.Snow, Enum.Material.Snow, Enum.Material.Snow, Enum.Material.Air }, }, { { Enum.Material.CrackedLava, Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand, Enum.Material.CrackedLava, }, { Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock }, { Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock }, { Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand }, { Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand }, { Enum.Material.Mud, Enum.Material.Snow, Enum.Material.Snow, Enum.Material.Snow, Enum.Material.Mud }, { Enum.Material.Air, Enum.Material.Snow, Enum.Material.Snow, Enum.Material.Snow, Enum.Material.Air }, }, { { Enum.Material.CrackedLava, Enum.Material.CrackedLava, Enum.Material.CrackedLava, Enum.Material.CrackedLava, Enum.Material.CrackedLava, }, { Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock }, { Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock }, { Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand }, { Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand }, { Enum.Material.Mud, Enum.Material.Mud, Enum.Material.Mud, Enum.Material.Mud, Enum.Material.Mud }, { Enum.Material.Air, Enum.Material.Air, Enum.Material.Air, Enum.Material.Air, Enum.Material.Air }, }, } local occupancies = { { { 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1 }, { 0.5, 0.5, 0.5, 0.5, 0.5 }, { 0, 0, 0, 0, 0 }, }, { { 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1 }, { 0.5, 1, 1, 1, 0.5 }, { 0, 1, 1, 1, 0 }, }, { { 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1 }, { 0.5, 1, 1, 1, 0.5 }, { 0, 1, 1, 1, 0 }, }, { { 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1 }, { 0.5, 0.5, 0.5, 0.5, 0.5 }, { 0, 0, 0, 0, 0 }, }, } terrain:WriteVoxels(region, resolution, materials, occupancies) ``` ### Method: Terrain:AutowedgeCell **Signature:** `Terrain:AutowedgeCell(x: int, y: int, z: int): boolean` > **Deprecated:** This item is a deprecated function of a legacy [Terrain](/docs/reference/engine/classes/Terrain.md) engine that has been removed. Do not use it for new work. Obsolete function which no longer does anything. *Security: None · Thread Safety: Unsafe · Capabilities: Environment* **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `int` | | | | `y` | `int` | | | | `z` | `int` | | | **Returns:** `boolean` ### Method: Terrain:AutowedgeCells **Signature:** `Terrain:AutowedgeCells(region: Region3int16): ()` > **Deprecated:** This item is a deprecated function of a legacy [Terrain](/docs/reference/engine/classes/Terrain.md) engine that has been removed. Do not use it for new work. (-0, -0, -1) print(-cf.ZVector) --> (-0, -0, -1) print(-R02, -R12, -R22) --> (-0 -0 -1) ``` Adding a [CFrame](/docs/reference/engine/datatypes/CFrame.md) object's [LookVector](/docs/reference/engine/datatypes/CFrame.md) to itself produces a [CFrame](/docs/reference/engine/datatypes/CFrame.md) moved forward in whichever direction it's facing by 1 unit. ### CFrame.RightVector **Type:** `Vector3` The right-direction component of the [CFrame](/docs/reference/engine/datatypes/CFrame.md) object's orientation. Equivalent to [XVector](/docs/reference/engine/datatypes/CFrame.md) or the first column of the rotation matrix. ```lua local cf = CFrame.new(0, 0, 0) local x, y, z, R00, R01, R02, R10, R11, R12, R20, R21, R22 = cf:GetComponents() print(cf.RightVector) --> (1, 0, 0) print(cf.XVector) --> (1, 0, 0) print(R00, R10, R20) --> (1 0 0) ``` ### CFrame.UpVector **Type:** `Vector3` The up-direction component of the [CFrame](/docs/reference/engine/datatypes/CFrame.md) object's orientation. Equivalent to [YVector](/docs/reference/engine/datatypes/CFrame.md) or the second column of the rotation matrix. ```lua local cf = CFrame.new(0, 0, 0) local x, y, z, R00, R01, R02, R10, R11, R12, R20, R21, R22 = cf:GetComponents() print(cf.UpVector) --> (0, 1, 0) print(cf.YVector) --> (0, 1, 0) print(R01, R11, R21) --> (0 1 0) ``` ### CFrame.XVector **Type:** `Vector3` The X component of the [CFrame](/docs/reference/engine/datatypes/CFrame.md) object's orientation. Equivalent to [RightVector](/docs/reference/engine/datatypes/CFrame.md) or the first column of the rotation matrix. ```lua local cf = CFrame.new(0, 0, 0) local x, y, z, R00, R01, R02, R10, R11, R12, R20, R21, R22 = cf:GetComponents() print(cf.XVector) --> (1, 0, 0) print(cf.RightVector) --> (1, 0, 0) print(R00, R10, R20) --> (1 0 0) ``` ### CFrame.YVector **Type:** `Vector3` The Y component of the [CFrame](/docs/reference/engine/datatypes/CFrame.md) object's orientation. Equivalent to [UpVector](/docs/reference/engine/datatypes/CFrame.md) or the second column of the rotation matrix. ```lua local cf = CFrame.new(0, 0, 0) local x, y, z, R00, R01, R02, R10, R11, R12, R20, R21, R22 = cf:GetComponents() print(cf.YVector) --> (0, 1, 0) print(cf.UpVector) --> (0, 1, 0) print(R01, R11, R21) --> (0 1 0) ``` ### CFrame.ZVector **Type:** `Vector3` The Z component of the [CFrame](/docs/reference/engine/datatypes/CFrame.md) object's orientation. Equivalent to the negated [LookVector](/docs/reference/engine/datatypes/CFrame.md) or the third column of the rotation matrix. ```lua local cf = CFrame.new(0, 0, 0) local x, y, z, R00, R01, R02, R10, R11, R12, R20, R21, R22 = cf:GetComponents() print(cf.ZVector) --> (0, 0, 1) print(-cf.LookVector) --> (0, 0, 1) print(R02, R12, R22) --> (0 0 1) ``` ## Methods ### CFrame:Inverse **Signature:** `CFrame:Inverse(): CFrame` Returns the inverse of the [CFrame](/docs/reference/engine/datatypes/CFrame.md). **Returns:** `CFrame` ### CFrame:Lerp **Signature:** `CFrame:Lerp(goal: CFrame, alpha: number): CFrame` Returns a [CFrame](/docs/reference/engine/datatypes/CFrame.md) interpolated between itself and `goal` by the fraction `alpha`. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `goal` | `CFrame` | | | | `alpha` | `number` | | | **Returns:** `CFrame` ### CFrame:Orthonormalize **Signature:** `CFrame:Orthonormalize(): CFrame` Returns an orthonormalized copy of the [CFrame](/docs/reference/engine/datatypes/CFrame.md). The [BasePart.CFrame](/docs/reference/engine/classes/BasePart.md) property automatically applies orthonormalization, but other APIs which take [CFrames](/docs/reference/engine/datatypes/CFrame.md) do not, so this method is occasionally necessary when incrementally updating a [CFrame](/docs/reference/engine/datatypes/CFrame.md) and using it with them. **Returns:** `CFrame` ### CFrame:ToWorldSpace **Signature:** `CFrame:ToWorldSpace(...: Tuple): Tuple` Receives one or more [CFrame](/docs/reference/engine/datatypes/CFrame.md) objects and returns them transformed from object to world space. Equivalent to: `CFrame * cf` ```lua local cf = CFrame.new(5, 10, 0) local offset = CFrame.new(0, 2, 0) -- Output world space of offset CFrame relative to calling CFrame print(cf:ToWorldSpace(offset)) --> 5, 12, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1 ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `...` | `Tuple` | | | **Returns:** `Tuple` ### CFrame:ToObjectSpace **Signature:** `CFrame:ToObjectSpace(...: Tuple): Tuple` Receives one or more [CFrame](/docs/reference/engine/datatypes/CFrame.md) objects and returns them transformed from world to object space. Equivalent to: `CFrame:Inverse() * cf` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `...` | `Tuple` | | | **Returns:** `Tuple` ### CFrame:PointToWorldSpace **Signature:** `CFrame:PointToWorldSpace(...: Tuple): Tuple` Receives one or more [Vector3](/docs/reference/engine/datatypes/Vector3.md) objects and returns them transformed from object to world space. Equivalent to: `CFrame * v3` ```lua local cf = CFrame.new(5, 10, 0) -- Output core world space of the CFrame print(cf:PointToWorldSpace()) --> 5, 10, 0 -- Output world space of a Vector3 (0, 4, 0) relative to the CFrame (object space) print(cf:PointToWorldSpace(Vector3.new(0, 4, 0))) --> 5, 14, 0 ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `...` | `Tuple` | | | **Returns:** `Tuple` ### CFrame:PointToObjectSpace **Signature:** `CFrame:PointToObjectSpace(...: Tuple): Tuple` Receives one or more [Vector3](/docs/reference/engine/datatypes/Vector3.md) objects and returns them transformed from world to object space. Equivalent to: `CFrame:Inverse() * v3` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `...` | `Tuple` | | | **Returns:** `Tuple` ### CFrame:VectorToWorldSpace **Signature:** `CFrame:VectorToWorldSpace(...: Tuple): Tuple` Receives one or more [Vector3](/docs/reference/engine/datatypes/Vector3.md) objects and returns them rotated from object to world space. Equivalent to: `(CFrame - CFrame.Position) * v3` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `...` | `Tuple` | | | **Returns:** `Tuple` ### CFrame:VectorToObjectSpace **Signature:** `CFrame:VectorToObjectSpace(...: Tuple): Tuple` Receives one or more [Vector3](/docs/reference/engine/datatypes/Vector3.md) objects and returns them rotated from world to object space. Equivalent to: `(CFrame:Inverse() - CFrame:Inverse().Position) * v3` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `...` | `Tuple` | | | **Returns:** `Tuple` ### CFrame:GetComponents **Signature:** `CFrame:GetComponents(): Tuple` Returns the values `x`, `y`, `z`, `R00`, `R01`, `R02`, `R10`, `R11`, `R12`, `R20`, `R21`, and `R22`, where `x` `y` `z` represent the position of the [CFrame](/docs/reference/engine/datatypes/CFrame.md) and `R00`‑`R22` represent its 3×3 rotation matrix. **Returns:** `Tuple` ### CFrame:ToEulerAngles **Signature:** `CFrame:ToEulerAngles(order?: RotationOrder): number, number, number` Returns approximate angles that could be used to generate the [CFrame](/docs/reference/engine/datatypes/CFrame.md) using the optional [RotationOrder](/docs/reference/engine/enums/RotationOrder.md). If you don't provide `order`, the method uses [RotationOrder.XYZ](/docs/reference/engine/enums/RotationOrder.md). **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `order` | `RotationOrder` | `Enum.RotationOrder.XYZ` | | **Returns:** `number`, `number`, `number` ### CFrame:ToEulerAnglesXYZ **Signature:** `CFrame:ToEulerAnglesXYZ(): number, number, number` Returns the approximate angles, in radians, that could be used to generate the [CFrame](/docs/reference/engine/datatypes/CFrame.md) using [RotationOrder.XYZ](/docs/reference/engine/enums/RotationOrder.md). **Returns:** `number`, `number`, `number` — The X-axis rotation angle in radians. The Y-axis rotation angle in radians. The Z-axis rotation angle in radians. ### CFrame:ToEulerAnglesYXZ **Signature:** `CFrame:ToEulerAnglesYXZ(): number, number, number` Returns the approximate angles, in radians, that could be used to generate the [CFrame](/docs/reference/engine/datatypes/CFrame.md) using [RotationOrder.YXZ](/docs/reference/engine/enums/RotationOrder.md). **Returns:** `number`, `number`, `number` — The X-axis rotation angle in radians. The Y-axis rotation angle in radians. The Z-axis rotation angle in radians. ### CFrame:ToOrientation **Signature:** `CFrame:ToOrientation(): number, number, number` Returns the approximate angles, in radians, that could be used to generate the [CFrame](/docs/reference/engine/datatypes/CFrame.md) using [RotationOrder.YXZ](/docs/reference/engine/enums/RotationOrder.md). Equivalent to [CFrame:ToEulerAnglesYXZ()](/docs/reference/engine/datatypes/CFrame.md). **Returns:** `number`, `number`, `number` — The X-axis rotation angle in radians. The Y-axis rotation angle in radians. The Z-axis rotation angle in radians. ### CFrame:ToAxisAngle **Signature:** `CFrame:ToAxisAngle(): Vector3, number` Returns a tuple containing a unit [Vector3](/docs/reference/engine/datatypes/Vector3.md) axis and a rotation angle in radians. **Returns:** `Vector3`, `number` ### CFrame:components **Signature:** `CFrame:components(): Tuple` Equivalent to [CFrame:GetComponents()](/docs/reference/engine/datatypes/CFrame.md). **Returns:** `Tuple` ### CFrame:FuzzyEq **Signature:** `CFrame:FuzzyEq(other: CFrame, epsilon?: number): bool` Returns `true` if the other [CFrame](/docs/reference/engine/datatypes/CFrame.md) is sufficiently close to this [CFrame](/docs/reference/engine/datatypes/CFrame.md) in both position and rotation. The `epsilon` value is used to control the tolerance for this similarity; this value is optional and should be a small positive value if provided. The similarity for position is component-wise while rotation uses a fast approximation of the angle difference. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `other` | `CFrame` | | | | `epsilon` | `number` | `0.00001 (1e-5)` | | **Returns:** `bool` ### CFrame:AngleBetween **Signature:** `CFrame:AngleBetween(other: CFrame): number` Returns the angle, in radians, between the orientation of one [CFrame](/docs/reference/engine/datatypes/CFrame.md) and another. This function does not take the position of either [CFrame](/docs/reference/engine/datatypes/CFrame.md) into account and only looks at the relative orientation. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `other` | `CFrame` | | | **Returns:** `number` ## Math Operations | Operation | Type A | Type B | Returns | Description | |-----------|--------|--------|---------|-------------| | `*` | `CFrame` | `CFrame` | `CFrame` | Produces a new [CFrame](/docs/reference/engine/datatypes/CFrame.md) representing the composition of the two [CFrames](/docs/reference/engine/datatypes/CFrame.md). | | `*` | `CFrame` | `Vector3` | `Vector3` | Produces a [Vector3](/docs/reference/engine/datatypes/Vector3.md) transformed from object to world coordinates. | | `+` | `CFrame` | `Vector3` | `CFrame` | Produces a [CFrame](/docs/reference/engine/datatypes/CFrame.md) translated in world space by the [Vector3](/docs/reference/engine/datatypes/Vector3.md). | | `-` | `CFrame` | `Vector3` | `CFrame` | Produces a [CFrame](/docs/reference/engine/datatypes/CFrame.md) translated in world space by the negative [Vector3](/docs/reference/engine/datatypes/Vector3.md). | --- name: CatalogSearchParams last_updated: 2026-06-29T19:34:08Z type: datatype summary: "Stores parameters used in catalog searches via AvatarEditorService:SearchCatalogAsync()." --- # CatalogSearchParams Stores parameters used in catalog searches via [AvatarEditorService:SearchCatalogAsync()](/docs/reference/engine/classes/AvatarEditorService.md). **Type:** datatype ## Description The [CatalogSearchParams](/docs/reference/engine/datatypes/CatalogSearchParams.md) data type stores the parameters of a catalog search via [AvatarEditorService:SearchCatalogAsync()](/docs/reference/engine/classes/AvatarEditorService.md). When accessing the value of the [CatalogSearchParams.BundleTypes](/docs/reference/engine/datatypes/CatalogSearchParams.md) or [CatalogSearchParams.AssetTypes](/docs/reference/engine/datatypes/CatalogSearchParams.md) property the returned table will be read-only to avoid confusion when not directly accessing the [CatalogSearchParams](/docs/reference/engine/datatypes/CatalogSearchParams.md) instance. For example, you can use these properties as follows: ```lua local params = CatalogSearchParams.new() params.SearchKeyword = "Test" params.MinPrice = 5000 params.MaxPrice = 10000 params.BundleTypes = {Enum.BundleType.Animations, Enum.BundleType.BodyParts} local types = params.BundleTypes for _, val in types do print(val) end -- table.insert(types, Enum.BundleType.Animations) -- This would not work because the table is read only ``` ## Properties ### CatalogSearchParams.SearchKeyword **Type:** `string` The keyword to search for catalog results with. ### CatalogSearchParams.MinPrice **Type:** `number` The minimum item price to search for. *Default: 0* ### CatalogSearchParams.MaxPrice **Type:** `number` The maximum item price to search for. *Default: 2147483647* ### CatalogSearchParams.SortType **Type:** `CatalogSortType` The order in which to sort the results, represented by a [CatalogSortType](/docs/reference/engine/enums/CatalogSortType.md). *Default: Enum.CatalogSortType.Relevance* ### CatalogSearchParams.SortAggregation **Type:** `Enum.CatalogSortAggregation` The time period to use to aggregate the sort results by, represented by a [CatalogSortAggregation](/docs/reference/engine/enums/CatalogSortAggregation.md). This only applies when the sort type is [CatalogSortType.MostFavorited](/docs/reference/engine/enums/CatalogSortType.md) or [CatalogSortType.BestSelling](/docs/reference/engine/enums/CatalogSortType.md). It does not apply for other sort types. *Default: Enum.CatalogSortAggregation.AllTime* ### CatalogSearchParams.CategoryFilter **Type:** `CatalogCategoryFilter` The category to filter the search by, represented by a [CatalogCategoryFilter](/docs/reference/engine/enums/CatalogCategoryFilter.md). *Default: Enum.CatalogCategoryFilter.None* ### CatalogSearchParams.SalesTypeFilter **Type:** `Enum.SalesTypeFilter` The sales type the search by, represented by a [SalesTypeFilter](/docs/reference/engine/enums/SalesTypeFilter.md). *Default: Enum.SalesTypeFilter.All* ### CatalogSearchParams.BundleTypes **Type:** `Array` An array containing [BundleType](/docs/reference/engine/enums/BundleType.md) values to filter the search by. *Default: {}* ### CatalogSearchParams.AssetTypes **Type:** `Array` An array containing [AvatarAssetType](/docs/reference/engine/enums/AvatarAssetType.md) values to filter the search by. ### CatalogSearchParams.IncludeOffSale **Type:** `bool` Whether off sale items should be included in the results. *Default: false* ### CatalogSearchParams.CreatorName **Type:** `string` Search for items with the given creator name. Specify whether to search users, groups, or both with [CatalogSearchParams.CreatorType](/docs/reference/engine/datatypes/CatalogSearchParams.md). ### CatalogSearchParams.CreatorType **Type:** `Enum.CreatorTypeFilter` Search for items created by the given creator type. When unspecified, it defaults to returning creations from both [CreatorTypeFilter.User](/docs/reference/engine/enums/CreatorTypeFilter.md) and [CreatorTypeFilter.Group](/docs/reference/engine/enums/CreatorTypeFilter.md). Searching by [CatalogSearchParams.CreatorId](/docs/reference/engine/datatypes/CatalogSearchParams.md) with [CreatorTypeFilter.All](/docs/reference/engine/enums/CreatorTypeFilter.md) results in a HTTP 400 Bad Request error. *Default: Enum.CreatorTypeFilter.All* ### CatalogSearchParams.CreatorId **Type:** `number` Search for items created by the single creator ID. Specify user or group with [CatalogSearchParams.CreatorType](/docs/reference/engine/datatypes/CatalogSearchParams.md). Searching by creator ID **and** creator name is not supported; specify one, not both. *Default: 0* ### CatalogSearchParams.Limit **Type:** `number` Specifies the number of items to return. Accepts `10`, `28`, `30`, `60`, and `120`. Defaults to `30`. --- name: Color3 last_updated: 2026-06-29T19:34:08Z type: datatype summary: "A color value comprised of red, green, and blue components." --- # Color3 A color value comprised of red, green, and blue components. **Type:** datatype ## Description The [Color3](/docs/reference/engine/datatypes/Color3.md) data type describes a color using red, green, and blue components in the range of 0 to 1. Unlike the [BrickColor](/docs/reference/engine/datatypes/BrickColor.md) data type which describes named colors, [Color3](/docs/reference/engine/datatypes/Color3.md) is used for precise coloring of objects on screen through properties like [BasePart.Color](/docs/reference/engine/classes/BasePart.md) and [GuiObject.BackgroundColor3](/docs/reference/engine/classes/GuiObject.md). ## Constructors ### Color3.new **Signature:** `Color3.new(red?: number, green?: number, blue?: number)` Returns a [Color3](/docs/reference/engine/datatypes/Color3.md) with the given red, green, and blue values. The parameters should be within the range of 0 to 1. ```lua local red = Color3.new(1, 0, 0) local green = Color3.new(0, 1, 0) local blue = Color3.new(0, 0, 1) ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `red` | `number` | `0` | | | `green` | `number` | `0` | | | `blue` | `number` | `0` | | ### Color3.fromRGB **Signature:** `Color3.fromRGB(red?: number, green?: number, blue?: number)` Creates a [Color3](/docs/reference/engine/datatypes/Color3.md) with the given red, green, and blue components. Unlike most other [Color3](/docs/reference/engine/datatypes/Color3.md) functions, the parameters for this function should be within the range of 0 to 255. ```lua local red = Color3.fromRGB(255, 0, 0) local green = Color3.fromRGB(0, 255, 0) local blue = Color3.fromRGB(0, 0, 255) ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `red` | `number` | `0` | | | `green` | `number` | `0` | | | `blue` | `number` | `0` | | ### Color3.fromHSV **Signature:** `Color3.fromHSV(hue: number, saturation: number, value: number)` Creates a [Color3](/docs/reference/engine/datatypes/Color3.md) with the given hue, saturation, and value. The parameters should be within the range of 0 to 1. ```lua local red = Color3.fromHSV(1, 1, 1) local green = Color3.fromHSV(0.3333333, 1, 1) local white = Color3.fromHSV(0, 0, 1) ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `hue` | `number` | | | | `saturation` | `number` | | | | `value` | `number` | | | ### Color3.fromHex **Signature:** `Color3.fromHex(hex: string)` Returns a new [Color3](/docs/reference/engine/datatypes/Color3.md) from a six- or three-character hexadecimal format, case insensitive. A preceding hashtag (`#`) is ignored, if present. This function interprets the given string as a typical web hex color in the format `RRGGBB` or `RGB` (shorthand for `RRGGBB`). For example, `#FFAA00` produces an orange color and is the same as `#FA0`. ```lua local red = Color3.fromHex("FF0000") local magenta = Color3.fromHex("ec008c") local black = Color3.fromHex("000") local white = Color3.fromHex("#FFF") ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `hex` | `string` | | | ## Properties ### Color3.R **Type:** `number` The red value of the color. ### Color3.G **Type:** `number` The green value of the color. ### Color3.B **Type:** `number` The blue value of the color. ## Methods ### Color3:Lerp **Signature:** `Color3:Lerp(color: Color3, alpha: number): Color3` Returns a [Color3](/docs/reference/engine/datatypes/Color3.md) interpolated between two colors. The `alpha` value should be within the range of 0 to 1. ```lua local white = Color3.new(1, 1, 1) local black = Color3.new(0, 0, 0) local gray10 = white:Lerp(black, 0.1) print(gray10) --> 0.9, 0.9, 0.9 local gray50 = white:Lerp(black, 0.5) print(gray50) --> 0.5, 0.5, 0.5 local gray85 = white:Lerp(black, 0.85) print(gray85) --> 0.15, 0.15, 0.15 ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `color` | `Color3` | | | | `alpha` | `number` | | | **Returns:** `Color3` ### Color3:ToHSV **Signature:** `Color3:ToHSV(): number, number, number` Returns the hue, saturation, and value of a [Color3](/docs/reference/engine/datatypes/Color3.md). This function is the inverse operation of the [Color3.fromHSV()](/docs/reference/engine/datatypes/Color3.md) constructor. ```lua local red = Color3.fromRGB(255, 0, 0) local green = Color3.fromRGB(0, 255, 0) local redH, redS, redV = red:ToHSV() print(redH, redS, redV) --> 1 1 1 local greenH, greenS, greenV = green:ToHSV() print(greenH, greenS, greenV) --> 0.3333333 1 1 ``` **Returns:** `number`, `number`, `number` ### Color3:ToHex **Signature:** `Color3:ToHex(): string` Converts the color to a six-character hexadecimal string representing the color in the format `RRGGBB`. It is not prefixed with an octothorpe (`#`). The returned string can be provided to [Color3.fromHex()](/docs/reference/engine/datatypes/Color3.md) to produce the original color. ```lua local red = Color3.fromRGB(255, 0, 0) local magenta = Color3.fromRGB(236, 0, 140) local redHex = red:ToHex() print(redHex) --> ff0000 local magentaHex = magenta:ToHex() print(magentaHex) --> ec008c ``` **Returns:** `string` ## Functions ### Color3.toHSV **Signature:** `Color3.toHSV(color: Color3): number, number, number` Returns the hue, saturation, and value of a [Color3](/docs/reference/engine/datatypes/Color3.md). This function is the inverse operation of the [Color3.fromHSV()](/docs/reference/engine/datatypes/Color3.md) constructor. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `color` | `Color3` | | | **Returns:** `number`, `number`, `number` --- name: ColorSequence last_updated: 2026-06-29T19:34:08Z type: datatype summary: "A gradient of color values comprised of ColorSequenceKeypoints." --- # ColorSequence A gradient of color values comprised of [ColorSequenceKeypoints](/docs/reference/engine/datatypes/ColorSequenceKeypoint.md). **Type:** datatype ## Description The [ColorSequence](/docs/reference/engine/datatypes/ColorSequence.md) data type represents a gradient of color values from `0` to `1`. The color values are expressed using the [ColorSequenceKeypoint](/docs/reference/engine/datatypes/ColorSequenceKeypoint.md) type. This type is used in various properties of [ParticleEmitter](/docs/reference/engine/classes/ParticleEmitter.md), [Trail](/docs/reference/engine/classes/Trail.md), [Beam](/docs/reference/engine/classes/Beam.md), and other objects that use color gradients. #### Equality Two [ColorSequence](/docs/reference/engine/datatypes/ColorSequence.md) objects are equivalent only if the values of their [ColorSequenceKeypoint](/docs/reference/engine/datatypes/ColorSequenceKeypoint.md) are equivalent, even if both would result in similar gradients. #### Evaluation The [ColorSequence](/docs/reference/engine/datatypes/ColorSequence.md) type does not have a built-in method for getting the value at a certain time/point. However, you can use the following function to evaluate at a specific time. ```lua local function evalColorSequence(sequence: ColorSequence, time: number) -- If time is 0 or 1, return the first or last value respectively if time == 0 then return sequence.Keypoints[1].Value elseif time == 1 then return sequence.Keypoints[#sequence.Keypoints].Value end -- Otherwise, step through each sequential pair of keypoints for i = 1, #sequence.Keypoints - 1 do local thisKeypoint = sequence.Keypoints[i] local nextKeypoint = sequence.Keypoints[i + 1] if time >= thisKeypoint.Time and time < nextKeypoint.Time then -- Calculate how far alpha lies between the points local alpha = (time - thisKeypoint.Time) / (nextKeypoint.Time - thisKeypoint.Time) -- Evaluate the real value between the points using alpha return Color3.new( (nextKeypoint.Value.R - thisKeypoint.Value.R) * alpha + thisKeypoint.Value.R, (nextKeypoint.Value.G - thisKeypoint.Value.G) * alpha + thisKeypoint.Value.G, (nextKeypoint.Value.B - thisKeypoint.Value.B) * alpha + thisKeypoint.Value.B ) end end end local colorSequence = ColorSequence.new{ ColorSequenceKeypoint.new(0, Color3.fromRGB(255, 0, 0)), ColorSequenceKeypoint.new(0.5, Color3.fromRGB(0, 190, 200)), ColorSequenceKeypoint.new(1, Color3.fromRGB(190, 0, 255)) } print(evalColorSequence(colorSequence, 0.75)) --> 0.372549, 0.372549, 0.892157 ``` ## Constructors ### ColorSequence.new **Signature:** `ColorSequence.new(c: Color3)` Returns a sequence of two keypoints with `c` for both the start and end values. ```lua local colorSequence = ColorSequence.new(c) -- Equivalent to local colorSequence = ColorSequence.new{ ColorSequenceKeypoint.new(0, c), ColorSequenceKeypoint.new(1, c) } ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `c` | `Color3` | | | ### ColorSequence.new **Signature:** `ColorSequence.new(c0: Color3, c1: Color3)` Returns a new [ColorSequence](/docs/reference/engine/datatypes/ColorSequence.md) with `c0` as the start value and `c1` as the end value. ```lua local colorSequence = ColorSequence.new(c0, c1) -- Equivalent to local colorSequence = ColorSequence.new{ ColorSequenceKeypoint.new(0, c0), ColorSequenceKeypoint.new(1, c1) } ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `c0` | `Color3` | | | | `c1` | `Color3` | | | ### ColorSequence.new **Signature:** `ColorSequence.new(keypoints: Array)` Returns a new [ColorSequence](/docs/reference/engine/datatypes/ColorSequence.md) from an array of [ColorSequenceKeypoints](/docs/reference/engine/datatypes/ColorSequenceKeypoint.md). The keypoints must be in a non-descending time value order. At least two keypoints must be provided, and they must have a time value of `0` (first) and `1` (last). ```lua local red = Color3.fromRGB(255, 0, 0) local cyan = Color3.fromRGB(0, 190, 200) local purple = Color3.fromRGB(190, 0, 255) local colorSequence = ColorSequence.new{ ColorSequenceKeypoint.new(0, red), ColorSequenceKeypoint.new(0.5, cyan), ColorSequenceKeypoint.new(1, purple) } ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `keypoints` | `Array` | | | ## Properties ### ColorSequence.Keypoints **Type:** `Array` An array containing [ColorSequenceKeypoint](/docs/reference/engine/datatypes/ColorSequenceKeypoint.md) values for the [ColorSequence](/docs/reference/engine/datatypes/ColorSequence.md). --- name: ColorSequenceKeypoint last_updated: 2026-06-29T19:34:08Z type: datatype summary: "A color and time value that represents a keypoint in a ColorSequence." --- # ColorSequenceKeypoint A color and time value that represents a keypoint in a [ColorSequence](/docs/reference/engine/datatypes/ColorSequence.md). **Type:** datatype ## Constructors ### ColorSequenceKeypoint.new **Signature:** `ColorSequenceKeypoint.new(time: number, color: Color3)` Creates a keypoint with a specified time and color. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `time` | `number` | | | | `color` | `Color3` | | | ## Properties ### ColorSequenceKeypoint.Time **Type:** `number` The relative time at which the keypoint is located. ### ColorSequenceKeypoint.Value **Type:** `Color3` The Color3 value of the keypoint. --- name: Content last_updated: 2026-06-29T19:34:08Z type: datatype summary: "Represents a reference to asset content stored externally or as an object within the place." --- # Content Represents a reference to asset content stored externally or as an object within the place. **Type:** datatype ## Description The `Content` data type represents a reference to asset content stored externally or as an object within the place, wrapping a single value of one of the supported [ContentSourceType](/docs/reference/engine/enums/ContentSourceType.md) values. ##### Warning Replication is not yet supported for [Object](/docs/reference/engine/datatypes/Content.md) values. When an [Instance](/docs/reference/engine/classes/Instance.md) with a [Content](/docs/reference/engine/datatypes/Content.md) property containing a [Object](/docs/reference/engine/datatypes/Content.md) value is replicated, an unusable placeholder [Object](/docs/reference/engine/classes/Object.md) of the same type will be used instead of the [Object](/docs/reference/engine/classes/Object.md) itself, and any attempt to read or write the contents of that placeholder object will throw. These placeholder objects will render as a cyan and magenta checkerboard pattern. This will be replaced with standard replication behavior in the future. For now, do not use [EditableImage](/docs/reference/engine/classes/EditableImage.md) or [EditableMesh](/docs/reference/engine/classes/EditableMesh.md) as [Content](/docs/reference/engine/datatypes/Content.md) on the server on an [Instance](/docs/reference/engine/classes/Instance.md) that can replicate to clients. ## Constructors ### Content.fromUri **Signature:** `Content.fromUri(uri: string)` Returns a new `Content` with an [asset URI](/docs/en-us/projects/assets.md#asset-uris) `string` value referencing content external to the place. [Content.SourceType](/docs/reference/engine/datatypes/Content.md) will be [Uri](/docs/reference/engine/enums/ContentSourceType.md), and [Content.Uri](/docs/reference/engine/datatypes/Content.md) will contain a non‑`nil` `string` value. If `uri` is empty, [Content.none](/docs/reference/engine/datatypes/Content.md) will be returned instead. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `uri` | `string` | | The [asset URI](/docs/en-us/projects/assets.md#asset-uris) string. | ### Content.fromAssetId **Signature:** `Content.fromAssetId(assetId: number)` Returns a new `Content` from a numeric asset ID. This is a convenience constructor equivalent to calling `Content.fromUri("rbxassetid://" .. tostring(assetId))`. [Content.SourceType](/docs/reference/engine/datatypes/Content.md) will be [Uri](/docs/reference/engine/enums/ContentSourceType.md), and [Content.Uri](/docs/reference/engine/datatypes/Content.md) will contain the formatted `rbxassetid://` URI string. If `assetId` is `0`, [Content.none](/docs/reference/engine/datatypes/Content.md) will be returned instead. Throws if `assetId` is non‑finite, for example [math.huge()](/docs/reference/engine/globals/math.md) or `0/0`. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `assetId` | `number` | | The numeric asset ID. | ### Content.fromObject **Signature:** `Content.fromObject(object: Object)` Returns a new `Content` with a strong reference to an [Object](/docs/reference/engine/classes/Object.md). [Content.SourceType](/docs/reference/engine/datatypes/Content.md) will be [Object](/docs/reference/engine/enums/ContentSourceType.md), and [Content.Object](/docs/reference/engine/datatypes/Content.md) will contain a non‑`nil` [Object](/docs/reference/engine/classes/Object.md) reference. [Content.Object](/docs/reference/engine/datatypes/Content.md) references are **strong** references that hold **shared ownership** of the [Object](/docs/reference/engine/classes/Object.md). Any [Content.Object](/docs/reference/engine/datatypes/Content.md) reference will extend the lifetime of that [Object](/docs/reference/engine/classes/Object.md) and prevent it from being garbage collected. Throws if `object` is `nil`. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `object` | `Object` | | The [Object](/docs/reference/engine/classes/Object.md) to reference. | ## Constants | Name | Type | Description | |------|------|-------------| | `Content.none` | `Content` | An empty `Content` value with [Content.SourceType](/docs/reference/engine/datatypes/Content.md) of [None](/docs/reference/engine/enums/ContentSourceType.md). | ## Properties ### Content.SourceType **Type:** `Enum.ContentSourceType` The source type of the contained value. Indicates which property contains a non‑`nil` value. ### Content.Uri **Type:** `string?` A URI `string` if [Content.SourceType](/docs/reference/engine/datatypes/Content.md) is [Uri](/docs/reference/engine/enums/ContentSourceType.md), otherwise `nil`. ### Content.Object **Type:** `Object?` A reference to a non-`nil` [Object](/docs/reference/engine/classes/Object.md) if [Content.SourceType](/docs/reference/engine/datatypes/Content.md) is [Object](/docs/reference/engine/enums/ContentSourceType.md), otherwise `nil`. ### Content.Opaque **Type:** `Opaque?` A reference to a non-`nil` `Opaque` content if [Content.SourceType](/docs/reference/engine/datatypes/Content.md) is [Opaque](/docs/reference/engine/enums/ContentSourceType.md), otherwise `nil`. --- name: DateTime last_updated: 2026-06-29T19:34:08Z type: datatype summary: "A data type that represents a moment in time." --- # DateTime A data type that represents a moment in time. **Type:** datatype ## Description The **DateTime** data type represents a moment in time using a Unix timestamp. It can be used to easily format dates and times in specific locales. When converted to a string, a string conversion of the stored timestamp integer is returned. They don't store timezone values. Instead, timezones are considered when constructing and using **DateTime** objects. **DateTime** objects are equal if and only if their [UnixTimestampMillis](/docs/reference/engine/datatypes/DateTime.md) properties are equal. #### Time Value Table The [ToUniversalTime()](/docs/reference/engine/datatypes/DateTime.md) and [ToLocalTime()](/docs/reference/engine/datatypes/DateTime.md) methods return a table of time-related values, such as `Year`, `Month`, and `Day`. The format of the table returned by these functions is described below, with each integer element in descending size order. | Name | Range | Notes | | --- | --- | --- | | `Year` | 1400–9999 | — | | `Month` | 1–12 | — | | `Day` | 1–31 | — | | `Hour` | 0–23 | — | | `Minute` | 0–59 | — | | `Second` | 0–60 | Usually 0–59, sometimes 60 to accommodate leap seconds in certain systems. | | `Millisecond` | 0–999 | — | #### String Format The [DateTime](/docs/reference/engine/datatypes/DateTime.md) object supports date/time conversion into a string through the [FormatUniversalTime()](/docs/reference/engine/datatypes/DateTime.md) and [FormatLocalTime()](/docs/reference/engine/datatypes/DateTime.md) methods. They both work the same except for which timezone the [DateTime](/docs/reference/engine/datatypes/DateTime.md) should be interpreted in. The first argument passed to these methods must be a string representing one of the tokens shown below. The tokens are then replaced with a specific value depending on the provided locale. ```lua local dt = DateTime.now() -- The "dddd" token is replaced with the full day of the week print("Today is " .. dt:FormatLocalTime("dddd", "en-us")) -- For the "en-us" locale, the "LL" token equals "MMMM D, YYYY" print("The date is " .. dt:FormatLocalTime("LL", "en-us")) ``` ##### Composite Tokens Depending on locale, these tokens replace to **specific combinations** of the **elemental tokens** described in the next section. This produces a correct date/time string depending on the locale. For example, in English, the date has the "[month] [day]" as in "June 11". In French, the date is in a "[day] [month]" format as in "11 juin". These special rules are handled for you automatically through the following composite tokens, so use these if you're displaying simple time and/or date information. | Token(s) | Locale Examples (Format) | | --- | --- | | Time | | `LT` | `en-us` : `8:30 PM` (`h:mm A`)`zh-cn` : `20:30` (`HH:mm`) | | Time with Seconds | | `LTS` | `en-us` : `8:30:25 PM` (`h:mm:ss A`)`zh-cn` : `20:30:25` (`HH:mm:ss`) | | Month (Number), Day, Year | | `L` | `en-us` : `09/04/1986` (`MM/DD/YYYY`)`zh-cn` : `1986/09/04` (`YYYY/MM/DD`) | | `l` | `en-us` : `9/4/1986` (`M/D/YYYY`)`zh-cn` : `1986/9/4` (`YYYY/M/D`) | | Month (Name), Day, Year | | `LL` | `en-us` : `September 4, 1986` (`MMMM D, YYYY`)`zh-cn` : `1986年9月4日` (`YYYY年M月D日`) | | `ll` | `en-us` : `Sep 4, 1986` (`MMM D, YYYY`)`zh-cn` : `1986年9月4日` (`YYYY年M月D日`) | | Month (Name), Day, Year, Time | | `LLL` | `en-us` : `September 4, 1986 8:30 PM` (`MMMM D, YYYY h:mm A`)`zh-cn` : `1986年9月4日晚上8点30分` (`YYYY年M月D日Ah点mm分`) | | `lll` | `en-us` : `Sep 4, 1986 8:30 PM` (`MMM D, YYYY h:mm A`)`zh-cn` : `1986年9月4日 20:30` (`YYYY年M月D日 HH:mm`) | | Day of Week, Month (Name), Day, Year, Time | | `LLLL` | `en-us` : `Thursday, September 4, 1986 8:30 PM` (`dddd, MMMM D, YYYY h:mm A`)`zh-cn` : `1986年9月4日星期四晚上8点30分` (`YYYY年M月D日ddddAh点mm分`) | | `llll` | `en-us` : `Thu, Sep 4, 1986 8:30 PM` (`ddd, MMM D, YYYY h:mm A`)`zh-cn` : `1986年9月4日星期四 20:30` (`YYYY年M月D日dddd HH:mm`) | ##### Elemental Tokens Each of these tokens replace to a **single value** and can be used as elements of a larger time string. Avoid using these if you only need simple date and time information, as the **composite tokens** are more appropriate for those purposes. | Token(s) | Examples | | --- | --- | | Year | | `YY` | `70`, `71`, …, `29`, `30` | | `YYYY` | `1970`, `1971`, …, `2029`, `2030` | | Month | | `M` | `1`, `2`, …, `11`, `12` | | `MM` | `01`, `02`, …, `11`, `12` | | `MMM` | `Jan`, `Feb`, …, `Nov`, `Dec` | | `MMMM` | `January`, `February`, …, `November`, `December` | | Day of Month | | `D` | `1`, `2`, …, `30`, `31` | | `DD` | `01`, `02`, …, `30`, `31` | | Day of Year | | `DDD` | `1`, `2`, …, `364`, `365` | | `DDDD` | `001`, `002`, …, `364`, `365` | | Day of Week | | `d` | `0`, `1`, …, `5`, `6` | | `dd` | `Su`, `Mo`, …, `Fr`, `Sa` | | `ddd` | `Sun`, `Mon`, …, `Fri`, `Sat` | | `dddd` | `Sunday`, `Monday`, …, `Friday`, `Saturday` | | Hour | | `H` | `0`, `1`, …, `22`, `23` | | `HH` | `00`, `01`, …, `22`, `23` | | `h` | `1`, `2`, …, `11`, `12` | | `hh` | `01`, `02`, …, `11`, `12` | | Minute | | `m` | `0`, `1`, …, `58`, `59` | | `mm` | `00`, `01`, …, `58`, `59` | | Second | | `s` | `0`, `1`, …, `58`, `59` | | `ss` | `00`, `01`, …, `58`, `59` | | Fractional Second | | `S` | `0`, `1`, …, `8`, `9` | | `SS` | `00`, `01`, …, `98`, `99` | | `SSS` | `000`, `001`, …, `998`, `999` | | AM/PM | | `A` | `en-us` : `AM`, `PM``zh-cn` : `凌晨\|早上\|上午\|中午\|下午\|晚上` | | `a` | `en-us` : `am`, `pm``zh-cn` : `凌晨\|早上\|上午\|中午\|下午\|晚上` | ## Constructors ### DateTime.now **Signature:** `DateTime.now()` Returns a new [DateTime](/docs/reference/engine/datatypes/DateTime.md) representing the current moment in time, using the device's local clock. Most operating systems automatically sync their local time against online time servers, so this should be within a few hundred milliseconds. However, users can easily disable sync behavior and set the system time to anything they want; for synchronized time between client and server, use [Workspace:GetServerTimeNow()](/docs/reference/engine/classes/Workspace.md) instead. When you need to precisely measure the time elapsed between two points in time, like when testing performance, use [os.clock()](/docs/reference/engine/globals/os.md) instead. ### DateTime.fromUnixTimestamp **Signature:** `DateTime.fromUnixTimestamp(unixTimestamp: number)` Returns a new [DateTime](/docs/reference/engine/datatypes/DateTime.md) object from the given Unix timestamp, or the number of **seconds** since January 1st, 1970 at 00:00 (UTC). This function only has granularity to 1 second. To represent sub-second timestamps, use [DateTime.fromUnixTimestampMillis()](/docs/reference/engine/datatypes/DateTime.md) instead. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `unixTimestamp` | `number` | | | ### DateTime.fromUnixTimestampMillis **Signature:** `DateTime.fromUnixTimestampMillis(unixTimestampMillis: number)` Returns a new [DateTime](/docs/reference/engine/datatypes/DateTime.md) object from the given Unix timestamp, or the number of **milliseconds** since January 1st, 1970 at 00:00 (UTC). **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `unixTimestampMillis` | `number` | | | ### DateTime.fromUniversalTime **Signature:** `DateTime.fromUniversalTime(year?: number, month?: number, day?: number, hour?: number, minute?: number, second?: number, millisecond?: number)` Returns a new [DateTime](/docs/reference/engine/datatypes/DateTime.md) using the given units from a UTC time. The values accepted are similar to those found in the time value table returned by [ToUniversalTime()](/docs/reference/engine/datatypes/DateTime.md). - Date units (year, month, day) that produce an invalid date will raise an error. For example, January 32nd or February 29th on a non-leap year. - Time units (hour, minute, second, millisecond) that are outside their normal range are valid. For example, 90 minutes will cause the hour to roll over by 1; -10 seconds will cause the minute value to roll back by 1. - Non-integer values are rounded down. For example, providing 2.5 hours will be equivalent to providing 2 hours, not 2 hours 30 minutes. - Omitted values are assumed to be their lowest value in their normal range, except for year which defaults to 1970. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `year` | `number` | `1970` | | | `month` | `number` | `1` | | | `day` | `number` | `1` | | | `hour` | `number` | `0` | | | `minute` | `number` | `0` | | | `second` | `number` | `0` | | | `millisecond` | `number` | `0` | | ### DateTime.fromLocalTime **Signature:** `DateTime.fromLocalTime(year?: number, month?: number, day?: number, hour?: number, minute?: number, second?: number, millisecond?: number)` Returns a new [DateTime](/docs/reference/engine/datatypes/DateTime.md) using the given units from a local time. The values accepted are similar to those found in the time value table returned by [ToLocalTime()](/docs/reference/engine/datatypes/DateTime.md). - Date units (year, month, day) that produce an invalid date will raise an error. For example, January 32nd or February 29th on a non-leap year. - Time units (hour, minute, second, millisecond) that are outside their normal range are valid. For example, 90 minutes will cause the hour to roll over by 1; -10 seconds will cause the minute value to roll back by 1. - Non-integer values are rounded down. For example, providing 2.5 hours will be equivalent to providing 2 hours, not 2 hours 30 minutes. - Omitted values are assumed to be their lowest value in their normal range, except for year which defaults to 1970. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `year` | `number` | `1970` | | | `month` | `number` | `1` | | | `day` | `number` | `1` | | | `hour` | `number` | `0` | | | `minute` | `number` | `0` | | | `second` | `number` | `0` | | | `millisecond` | `number` | `0` | | ### DateTime.fromIsoDate **Signature:** `DateTime.fromIsoDate(isoDate: string)` Returns a [DateTime](/docs/reference/engine/datatypes/DateTime.md) from an ISO 8601 date-time string in UTC time, such as those returned by [ToIsoDate()](/docs/reference/engine/datatypes/DateTime.md). If the string parsing fails, the function returns `nil`. An example ISO 8601 date-time string would be `2020-01-02T10:30:45Z`, which represents January 2nd 2020 at 10:30 AM, 45 seconds. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `isoDate` | `string` | | | ## Properties ### DateTime.UnixTimestamp **Type:** `number` The number of **seconds** since January 1st, 1970 at 00:00 UTC (the Unix epoch). Range is -17,987,443,200 to 253,402,300,799, approximately years 1400–9999. ### DateTime.UnixTimestampMillis **Type:** `number` The number of **milliseconds** since January 1st, 1970 at 00:00 UTC (the Unix epoch). Range is -17,987,443,200,000 to 253,402,300,799,999, approximately years 1400–9999. ## Methods ### DateTime:ToUniversalTime **Signature:** `DateTime:ToUniversalTime(): Dictionary` Converts the value of this [DateTime](/docs/reference/engine/datatypes/DateTime.md) object to Universal Coordinated Time (UTC). The returned table contains the following keys: `Year`, `Month`, `Day`, `Hour`, `Minute`, `Second`, `Millisecond`. For more details, see the time value table in this data type's description. The values within this table could be passed to [fromUniversalTime()](/docs/reference/engine/datatypes/DateTime.md) to produce the original [DateTime](/docs/reference/engine/datatypes/DateTime.md) object. **Returns:** `Dictionary` ### DateTime:ToLocalTime **Signature:** `DateTime:ToLocalTime(): Dictionary` Converts the value of this [DateTime](/docs/reference/engine/datatypes/DateTime.md) object to local time. The returned table contains the following keys: `Year`, `Month`, `Day`, `Hour`, `Minute`, `Second`, `Millisecond`. For more details, see the time value table in this data type's description. The values within this table could be passed to [fromLocalTime()](/docs/reference/engine/datatypes/DateTime.md) to produce the original [DateTime](/docs/reference/engine/datatypes/DateTime.md) object. **Returns:** `Dictionary` ### DateTime:ToIsoDate **Signature:** `DateTime:ToIsoDate(): string` Formats a date as a ISO 8601 date-time string. The value returned by this function could be passed to [fromIsoDate()](/docs/reference/engine/datatypes/DateTime.md) to produce the original [DateTime](/docs/reference/engine/datatypes/DateTime.md) object. An example ISO 8601 date-time string would be `2020-01-02T10:30:45Z`, which represents January 2nd 2020 at 10:30 AM, 45 seconds. **Returns:** `string` ### DateTime:FormatUniversalTime **Signature:** `DateTime:FormatUniversalTime(format: string, locale: string): string` Generates a string from the [DateTime](/docs/reference/engine/datatypes/DateTime.md) value interpreted as Universal Coordinated Time (UTC) and a format string. The format string should contain tokens, which will replace to certain date/time values described by the [DateTime](/docs/reference/engine/datatypes/DateTime.md) object. For more details, see the **String Format** section in this data type's description. ```lua local dt = DateTime.now() -- For en-us, the "LL" token equals "MMM D, YYYY", which gives "June 11, 2020" print("The date is " .. dt:FormatUniversalTime("LL", "en-us")) --> "The date is June 11, 2020" ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `format` | `string` | | | | `locale` | `string` | | | **Returns:** `string` ### DateTime:FormatLocalTime **Signature:** `DateTime:FormatLocalTime(format: string, locale: string): string` Generates a string from the [DateTime](/docs/reference/engine/datatypes/DateTime.md) value interpreted as **local time** and a format string. The format string should contain tokens, which will replace to certain date/time values described by the [DateTime](/docs/reference/engine/datatypes/DateTime.md) object. For more details, see the **String Format** section in this data type's description. ```lua local dt = DateTime.now() -- For en-us, the "LL" token equals "MMM D, YYYY", which gives "June 11, 2020" print("The date is " .. dt:FormatLocalTime("LL", "en-us")) --> "The date is June 11, 2020" ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `format` | `string` | | | | `locale` | `string` | | | **Returns:** `string` --- name: DockWidgetPluginGuiInfo last_updated: 2026-06-29T19:34:08Z type: datatype summary: "Describes details for a DockWidgetPluginGui." --- # DockWidgetPluginGuiInfo Describes details for a [DockWidgetPluginGui](/docs/reference/engine/classes/DockWidgetPluginGui.md). **Type:** datatype ## Description The [DockWidgetPluginGuiInfo](/docs/reference/engine/datatypes/DockWidgetPluginGuiInfo.md) data type describes details for a [DockWidgetPluginGui](/docs/reference/engine/classes/DockWidgetPluginGui.md). This data type is used when constructing a [PluginGui](/docs/reference/engine/classes/PluginGui.md) via the plugin's [Plugin:CreateDockWidgetPluginGuiAsync()](/docs/reference/engine/classes/Plugin.md) method. ## Constructors ### DockWidgetPluginGuiInfo.new **Signature:** `DockWidgetPluginGuiInfo.new(InitialDockState?: InitialDockState, InitialEnabled?: bool, InitialEnabledShouldOverrideRestore?: bool, FloatingXSize?: number, FloatingYSize?: number, MinWidth?: number, MinHeight?: number)` The main constructor function for the [DockWidgetPluginGuiInfo](/docs/reference/engine/datatypes/DockWidgetPluginGuiInfo.md). **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `InitialDockState` | `InitialDockState` | `Enum.InitialDockState.Right` | | | `InitialEnabled` | `bool` | `false` | | | `InitialEnabledShouldOverrideRestore` | `bool` | `false` | | | `FloatingXSize` | `number` | `0` | | | `FloatingYSize` | `number` | `0` | | | `MinWidth` | `number` | `0` | | | `MinHeight` | `number` | `0` | | ## Properties ### DockWidgetPluginGuiInfo.InitialEnabled **Type:** `bool` The initial enabled state of a [PluginGui](/docs/reference/engine/classes/PluginGui.md) created using this [DockWidgetPluginGuiInfo](/docs/reference/engine/datatypes/DockWidgetPluginGuiInfo.md). If a [PluginGui](/docs/reference/engine/classes/PluginGui.md) with the same ''pluginGuiId'' has previously been created in an earlier session of Roblox Studio, then it will reload that saved enabled state (unless `InitialEnabledShouldOverrideRestore` is true). ### DockWidgetPluginGuiInfo.InitialEnabledShouldOverrideRestore **Type:** `bool` If true, the value of `InitialEnabled` will override the previously saved enabled state of a [PluginGui](/docs/reference/engine/classes/PluginGui.md) being created with this [DockWidgetPluginGuiInfo](/docs/reference/engine/datatypes/DockWidgetPluginGuiInfo.md). The previously saved enabled state is loaded based on the `pluginGuiId` argument of [Plugin:CreateDockWidgetPluginGuiAsync()](/docs/reference/engine/classes/Plugin.md). ### DockWidgetPluginGuiInfo.FloatingXSize **Type:** `number` The initial pixel width of a [PluginGui](/docs/reference/engine/classes/PluginGui.md) created using this [DockWidgetPluginGuiInfo](/docs/reference/engine/datatypes/DockWidgetPluginGuiInfo.md), when the [InitialDockState](/docs/reference/engine/enums/InitialDockState.md) is set to `Float`. ### DockWidgetPluginGuiInfo.FloatingYSize **Type:** `number` The initial pixel height of a [PluginGui](/docs/reference/engine/classes/PluginGui.md) created using this [DockWidgetPluginGuiInfo](/docs/reference/engine/datatypes/DockWidgetPluginGuiInfo.md), when the [InitialDockState](/docs/reference/engine/enums/InitialDockState.md) is set to `Float`. ### DockWidgetPluginGuiInfo.MinWidth **Type:** `number` The minimum width of a [PluginGui](/docs/reference/engine/classes/PluginGui.md) created using this [DockWidgetPluginGuiInfo](/docs/reference/engine/datatypes/DockWidgetPluginGuiInfo.md), in pixels. Each platform has its own absolute minimum that Roblox will enforce. These variations exist to account for the contents of the title bar (which varies by platform) when the widget is floating. For example, on a Mac, the width can never be less than ~80 pixels to accommodate the close/minimize/maximize buttons. ### DockWidgetPluginGuiInfo.MinHeight **Type:** `number` The minimum height of a [PluginGui](/docs/reference/engine/classes/PluginGui.md) created using this [DockWidgetPluginGuiInfo](/docs/reference/engine/datatypes/DockWidgetPluginGuiInfo.md), in pixels. --- name: Enum last_updated: 2026-06-29T19:34:08Z type: datatype summary: "A data type that represents an individual enum." --- # Enum A data type that represents an individual enum. **Type:** datatype ## Description The [Enum](/docs/reference/engine/datatypes/Enum.md) data type represents an individual enum in Roblox. An individual enum can be indexed through the [Enums](/docs/reference/engine/datatypes/Enums.md) type, via the name of the enum itself. ## Methods ### Enum:GetEnumItems **Signature:** `Enum:GetEnumItems(): Array` Returns an array of all the [EnumItem](/docs/reference/engine/datatypes/EnumItem.md) options available for this enum. **Returns:** `Array` ### Enum:FromName **Signature:** `Enum:FromName(name: string): Enum | nil` Returns either the converted [Enum](/docs/reference/engine/datatypes/Enum.md) or `nil`. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name` | `string` | | | **Returns:** `Enum | nil` ### Enum:FromValue **Signature:** `Enum:FromValue(value: number): Enum | nil` Returns either the converted [Enum](/docs/reference/engine/datatypes/Enum.md) or `nil`. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `value` | `number` | | | **Returns:** `Enum | nil` --- name: EnumItem last_updated: 2026-06-29T19:34:08Z type: datatype summary: "An individual item in an enum." --- # EnumItem An individual item in an enum. **Type:** datatype ## Description The [EnumItem](/docs/reference/engine/datatypes/EnumItem.md) data type represents an individual item in a Roblox enum. ## Properties ### EnumItem.Name **Type:** `string` The name of the EnumItem. ### EnumItem.Value **Type:** `number` The integral value assigned to the EnumItem. ### EnumItem.EnumType **Type:** `Enum` A reference to the parent [Enum](/docs/reference/engine/datatypes/Enum.md) of the EnumItem. --- name: Enums last_updated: 2026-06-29T19:34:08Z type: datatype summary: "A root access point of all Enums." --- # Enums A root access point of all [Enums](/docs/reference/engine/datatypes/Enum.md). **Type:** datatype ## Description The [Enums](/docs/reference/engine/datatypes/Enums.md) data type, more commonly known as [Enum](/docs/reference/engine/datatypes/Enum.md) by its global variable in Luau), acts as the root access point for all available enums on Roblox. All enums on Roblox are indexed via their name through this data type, and developers can also utilize the `GetEnums()` method to get a list of all enums on Roblox. ## Methods ### Enums:GetEnums **Signature:** `Enums:GetEnums(): Array` Returns an array containing all available [Enums](/docs/reference/engine/datatypes/Enum.md) on Roblox. **Returns:** `Array` --- name: Faces last_updated: 2026-06-29T19:34:08Z type: datatype summary: "A data type containing six booleans, each representing a face of a BasePart." --- # Faces A data type containing six booleans, each representing a face of a [BasePart](/docs/reference/engine/classes/BasePart.md). **Type:** datatype ## Description The [Faces](/docs/reference/engine/datatypes/Faces.md) data type contains six booleans representing whether a feature is enabled for each face ([NormalId](/docs/reference/engine/enums/NormalId.md)) of a Part. In other words, this contains a boolean for each axes (X/Y/Z) in both directions (positive/negative). The [Handles](/docs/reference/engine/classes/Handles.md) object uses this data type to enable whether a direction has a visible handle on a Part's face. ```lua local handles = Instance.new("Handles") handles.Faces = Faces.new(Enum.NormalId.Front, Enum.NormalId.Left) ``` Like most data types on Roblox, the Faces data type is immutable: you cannot assign to its properties once created. ## Constructors ### Faces.new **Signature:** `Faces.new(normalIds...: Tuple)` Creates a new Faces given some number of [NormalId](/docs/reference/engine/enums/NormalId.md) as arguments. Each NormalId provided indicates the property of the same name in the new Faces will be true. - The [table.unpack()](/docs/reference/engine/globals/table.md) function can be used to unpack a table of NormalId to be included. - Passing values that are not a [NormalId](/docs/reference/engine/enums/NormalId.md) will do nothing; they are ignored silently. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `normalIds...` | `Tuple` | | | ## Properties ### Faces.Top **Type:** `bool` Whether the top face is included. ### Faces.Bottom **Type:** `bool` Whether the bottom face is included. ### Faces.Left **Type:** `bool` Whether the left face is included. ### Faces.Right **Type:** `bool` Whether the right face is included. ### Faces.Back **Type:** `bool` Whether the back face is included. ### Faces.Front **Type:** `bool` Whether the front face is included. --- name: FloatCurveKey last_updated: 2026-06-29T19:34:08Z type: datatype summary: "A time-value pair used with FloatCurve instances." --- # FloatCurveKey A time-value pair used with [FloatCurve](/docs/reference/engine/classes/FloatCurve.md) instances. **Type:** datatype ## Description A time-value pair used with [FloatCurve](/docs/reference/engine/classes/FloatCurve.md) instances. The [Interpolation](/docs/reference/engine/datatypes/FloatCurveKey.md) property dictates the interpolation mode for the segment started by this key and ended by the next key on the curve. Each segment may use a different interpolation mode. The [LeftTangent](/docs/reference/engine/datatypes/FloatCurveKey.md) and [RightTangent](/docs/reference/engine/datatypes/FloatCurveKey.md) properties apply to the cubic interpolation mode and define the desired tangent (slope) at the key. Different left and right values can be used to encode discontinuities in slope at the key. Attempting to set a [RightTangent](/docs/reference/engine/datatypes/FloatCurveKey.md) value on a key that doesn't use the cubic interpolation mode will result in a runtime error. It is possible to set the [LeftTangent](/docs/reference/engine/datatypes/FloatCurveKey.md) property on any key, as it will be used should the preceding segment use cubic interpolation. ## Constructors ### FloatCurveKey.new **Signature:** `FloatCurveKey.new(time: number, value: number, Interpolation: KeyInterpolationMode)` Creates a new [FloatCurveKey](/docs/reference/engine/datatypes/FloatCurveKey.md) at a given time and value. [LeftTangent](/docs/reference/engine/datatypes/FloatCurveKey.md) and [RightTangent](/docs/reference/engine/datatypes/FloatCurveKey.md) are left uninitialized and, if not initialized, tangent values of 0 will be used when evaluating the curve. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `time` | `number` | | Time at which to create the new [FloatCurveKey](/docs/reference/engine/datatypes/FloatCurveKey.md). | | `value` | `number` | | Value of the new [FloatCurveKey](/docs/reference/engine/datatypes/FloatCurveKey.md). | | `Interpolation` | `KeyInterpolationMode` | | | ## Properties ### FloatCurveKey.Interpolation **Type:** `KeyInterpolationMode` Defines this key interpolation mode for the segment started by this [FloatCurveKey](/docs/reference/engine/datatypes/FloatCurveKey.md). ### FloatCurveKey.Time **Type:** `number` The time position of this [FloatCurveKey](/docs/reference/engine/datatypes/FloatCurveKey.md). ### FloatCurveKey.Value **Type:** `number` The value of this [FloatCurveKey](/docs/reference/engine/datatypes/FloatCurveKey.md). ### FloatCurveKey.RightTangent **Type:** `number` The tangent to the right of this [FloatCurveKey](/docs/reference/engine/datatypes/FloatCurveKey.md). ### FloatCurveKey.LeftTangent **Type:** `number` The tangent to the left of this [FloatCurveKey](/docs/reference/engine/datatypes/FloatCurveKey.md). --- name: Font last_updated: 2026-06-29T19:34:08Z type: datatype summary: "Describes the font used to render text." --- # Font Describes the font used to render text. **Type:** datatype ## Description Describes the font used to render text. Every font consists of a **font family**, a **weight** like [FontWeight.Bold](/docs/reference/engine/enums/FontWeight.md), and a **style** like [FontStyle.Italic](/docs/reference/engine/enums/FontStyle.md). Font families are a type of asset, like images or meshes. Each font family contains a number of font faces, and each face has a different weight and style. [Font](/docs/reference/engine/datatypes/Font.md) is used by the [TextLabel.FontFace](/docs/reference/engine/classes/TextLabel.md), [TextButton.FontFace](/docs/reference/engine/classes/TextButton.md), [TextBox.FontFace](/docs/reference/engine/classes/TextBox.md), and similar properties. See also [Font](/docs/reference/engine/enums/Font.md) as an older alternative to this datatype that is required by some methods and properties. | Name | Asset ID / Weights / Appearance | | --- | --- | | Accanthis ADF Std | `rbxasset://fonts/families/AccanthisADFStd.json` | | Amatic SC | `rbxasset://fonts/families/AmaticSC.json` | | Arimo | `rbxasset://fonts/families/Arimo.json` | | Balthazar | `rbxasset://fonts/families/Balthazar.json` | | Bangers | `rbxasset://fonts/families/Bangers.json` | | Builder Extended | `rbxasset://fonts/families/BuilderExtended.json` | | Builder Mono | `rbxasset://fonts/families/BuilderMono.json` | | Builder Sans | `rbxasset://fonts/families/BuilderSans.json` | | Comic Neue Angular | `rbxasset://fonts/families/ComicNeueAngular.json` | | Creepster | `rbxasset://fonts/families/Creepster.json` | | Denk One | `rbxasset://fonts/families/DenkOne.json` | | Fondamento | `rbxasset://fonts/families/Fondamento.json` | | Fredoka One | `rbxasset://fonts/families/FredokaOne.json` | | Grenze Gotisch | `rbxasset://fonts/families/GrenzeGotisch.json` | | Guru | `rbxasset://fonts/families/Guru.json` | | Highway Gothic | `rbxasset://fonts/families/HighwayGothic.json` | | Inconsolata | `rbxasset://fonts/families/Inconsolata.json` | | Indie Flower | `rbxasset://fonts/families/IndieFlower.json` | | Josefin Sans | `rbxasset://fonts/families/JosefinSans.json` | | Jura | `rbxasset://fonts/families/Jura.json` | | Kalam | `rbxasset://fonts/families/Kalam.json` | | Luckiest Guy | `rbxasset://fonts/families/LuckiestGuy.json` | | Merriweather | `rbxasset://fonts/families/Merriweather.json` | | Michroma | `rbxasset://fonts/families/Michroma.json` | | Montserrat | `rbxasset://fonts/families/Montserrat.json` | | Nunito | `rbxasset://fonts/families/Nunito.json` | | Oswald | `rbxasset://fonts/families/Oswald.json` | | Patrick Hand | `rbxasset://fonts/families/PatrickHand.json` | | Permanent Marker | `rbxasset://fonts/families/PermanentMarker.json` | | Press Start 2P | `rbxasset://fonts/families/PressStart2P.json` | | Roboto | `rbxasset://fonts/families/Roboto.json` | | Roboto Condensed | `rbxasset://fonts/families/RobotoCondensed.json` | | Roboto Mono | `rbxasset://fonts/families/RobotoMono.json` | | Roman Antique | `rbxasset://fonts/families/RomanAntique.json` | | Sarpanch | `rbxasset://fonts/families/Sarpanch.json` | | Source Sans Pro | `rbxasset://fonts/families/SourceSansPro.json` | | Special Elite | `rbxasset://fonts/families/SpecialElite.json` | | Titillium Web | `rbxasset://fonts/families/TitilliumWeb.json` | | Ubuntu | `rbxasset://fonts/families/Ubuntu.json` | | Zekton | `rbxasset://fonts/families/Zekton.json` | ## Constructors ### Font.new **Signature:** `Font.new(family: Content, weight?: FontWeight, style?: FontStyle)` Creates a new [Font](/docs/reference/engine/datatypes/Font.md). **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `family` | `Content` | | The asset ID for the font family, starting with `rbxasset://` or `rbxassetid://`. | | `weight` | `FontWeight` | `Enum.FontWeight.Regular` | How thick the text is. | | `style` | `FontStyle` | `Enum.FontStyle.Normal` | Whether the text is normal or italic. | **Font.new()** Shows how to use [Font.new()](/docs/reference/engine/datatypes/Font.md). ```lua local label = script.Parent.TextLabel label.FontFace = Font.new("rbxasset://fonts/families/Roboto.json", Enum.FontWeight.Light) ``` ### Font.fromEnum **Signature:** `Font.fromEnum(font: Font)` Creates a [Font](/docs/reference/engine/datatypes/Font.md) from an [Font](/docs/reference/engine/enums/Font.md) value. Throws an error when called with [Font.Unknown](/docs/reference/engine/enums/Font.md). The following table indicates how each [Font](/docs/reference/engine/enums/Font.md) value maps to a font [Family](/docs/reference/engine/datatypes/Font.md). | Font Enums | Equivalent Family | | --- | --- | | [AmaticSC](/docs/reference/engine/enums/Font.md) | `rbxasset://fonts/families/AmaticSC.json` | | [Antique](/docs/reference/engine/enums/Font.md) | `rbxasset://fonts/families/RomanAntique.json` | | [Arcade](/docs/reference/engine/enums/Font.md) | `rbxasset://fonts/families/PressStart2P.json` | | [Arimo](/docs/reference/engine/enums/Font.md), [ArimoBold](/docs/reference/engine/enums/Font.md) | `rbxasset://fonts/families/Arimo.json` | | [Bangers](/docs/reference/engine/enums/Font.md) | `rbxasset://fonts/families/Bangers.json` | | [Bodoni](/docs/reference/engine/enums/Font.md) | `rbxasset://fonts/families/AccanthisADFStd.json` | | [BuilderSans](/docs/reference/engine/enums/Font.md), [BuilderSansMedium](/docs/reference/engine/enums/Font.md), [BuilderSansBold](/docs/reference/engine/enums/Font.md), [BuilderSansExtraBold](/docs/reference/engine/enums/Font.md) | `rbxasset://fonts/families/BuilderSans.json` | | [Cartoon](/docs/reference/engine/enums/Font.md) | `rbxasset://fonts/families/ComicNeueAngular.json` | | [Code](/docs/reference/engine/enums/Font.md) | `rbxasset://fonts/families/Inconsolata.json` | | [Creepster](/docs/reference/engine/enums/Font.md) | `rbxasset://fonts/families/Creepster.json` | | [DenkOne](/docs/reference/engine/enums/Font.md) | `rbxasset://fonts/families/DenkOne.json` | | [Fantasy](/docs/reference/engine/enums/Font.md) | `rbxasset://fonts/families/Balthazar.json` | | [Fondamento](/docs/reference/engine/enums/Font.md) | `rbxasset://fonts/families/Fondamento.json` | | [FredokaOne](/docs/reference/engine/enums/Font.md) | `rbxasset://fonts/families/FredokaOne.json` | | [Garamond](/docs/reference/engine/enums/Font.md) | `rbxasset://fonts/families/Guru.json` | | [GrenzeGotisch](/docs/reference/engine/enums/Font.md) | `rbxasset://fonts/families/GrenzeGotisch.json` | | [Highway](/docs/reference/engine/enums/Font.md) | `rbxasset://fonts/families/HighwayGothic.json` | | [IndieFlower](/docs/reference/engine/enums/Font.md) | `rbxasset://fonts/families/IndieFlower.json` | | [JosefinSans](/docs/reference/engine/enums/Font.md) | `rbxasset://fonts/families/JosefinSans.json` | | [Jura](/docs/reference/engine/enums/Font.md) | `rbxasset://fonts/families/Jura.json` | | [Kalam](/docs/reference/engine/enums/Font.md) | `rbxasset://fonts/families/Kalam.json` | | [Legacy](/docs/reference/engine/enums/Font.md) | `rbxasset://fonts/families/LegacyArial.json` | | [LuckiestGuy](/docs/reference/engine/enums/Font.md) | `rbxasset://fonts/families/LuckiestGuy.json` | | [Merriweather](/docs/reference/engine/enums/Font.md) | `rbxasset://fonts/families/Merriweather.json` | | [Michroma](/docs/reference/engine/enums/Font.md) | `rbxasset://fonts/families/Michroma.json` | | [Nunito](/docs/reference/engine/enums/Font.md) | `rbxasset://fonts/families/Nunito.json` | | [Oswald](/docs/reference/engine/enums/Font.md) | `rbxasset://fonts/families/Oswald.json` | | [PatrickHand](/docs/reference/engine/enums/Font.md) | `rbxasset://fonts/families/PatrickHand.json` | | [PermanentMarker](/docs/reference/engine/enums/Font.md) | `rbxasset://fonts/families/PermanentMarker.json` | | [Roboto](/docs/reference/engine/enums/Font.md) | `rbxasset://fonts/families/Roboto.json` | | [RobotoCondensed](/docs/reference/engine/enums/Font.md) | `rbxasset://fonts/families/RobotoCondensed.json` | | [RobotoMono](/docs/reference/engine/enums/Font.md) | `rbxasset://fonts/families/RobotoMono.json` | | [Sarpanch](/docs/reference/engine/enums/Font.md) | `rbxasset://fonts/families/Sarpanch.json` | | [SciFi](/docs/reference/engine/enums/Font.md) | `rbxasset://fonts/families/Zekton.json` | | [SourceSans](/docs/reference/engine/enums/Font.md), [SourceSansBold](/docs/reference/engine/enums/Font.md), [SourceSansItalic](/docs/reference/engine/enums/Font.md), [SourceSansLight](/docs/reference/engine/enums/Font.md), [SourceSansSemibold](/docs/reference/engine/enums/Font.md) | `rbxasset://fonts/families/SourceSansPro.json` | | [SpecialElite](/docs/reference/engine/enums/Font.md) | `rbxasset://fonts/families/SpecialElite.json` | | [TitilliumWeb](/docs/reference/engine/enums/Font.md) | `rbxasset://fonts/families/TitilliumWeb.json` | | [Ubuntu](/docs/reference/engine/enums/Font.md) | `rbxasset://fonts/families/Ubuntu.json` | **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `font` | `Font` | | The enum value of the font to use. | **Font.fromEnum()** The following snippet shows how to use [Font.fromEnum()](/docs/reference/engine/datatypes/Font.md). In this example, [Font.SciFi](/docs/reference/engine/enums/Font.md) maps to `rbxasset://fonts/families/Zekton.json` with regular [Weight](/docs/reference/engine/datatypes/Font.md) and normal [Style](/docs/reference/engine/datatypes/Font.md). ```lua local label = script.Parent.TextLabel label.FontFace = Font.fromEnum(Enum.Font.SciFi) print(label.FontFace) --> Font { Family = rbxasset://fonts/families/Zekton.json, Weight = Regular, Style = Normal } ``` **Expected output:** Font { Family = rbxasset://fonts/families/Zekton.json, Weight = Regular, Style = Normal } ### Font.fromName **Signature:** `Font.fromName(name: string, weight?: FontWeight, style?: FontStyle)` This is a convenience method for creating fonts from the content folder. The name you pass in will be converted into an asset ID like `rbxasset://fonts/families/BuilderSans.json`. The name can only contain alphabetical characters, digits, `_` (underscore), and `-` (hyphen). It can't contain any spaces. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `name` | `string` | | The name of the font. | | `weight` | `FontWeight` | `Enum.FontWeight.Regular` | How thick the text is. | | `style` | `FontStyle` | `Enum.FontStyle.Normal` | Whether the text is normal or italic. | **Font.fromName()** Shows how to use [Font.fromName()](/docs/reference/engine/datatypes/Font.md). ```lua local label = script.Parent.TextLabel label.FontFace = Font.fromName("FredokaOne") print(label.FontFace) --> Font { Family = rbxasset://fonts/families/FredokaOne.json, Weight = Regular, Style = Normal } ``` **Expected output:** Font { Family = rbxasset://fonts/families/FredokaOne.json, Weight = Regular, Style = Normal } ### Font.fromId **Signature:** `Font.fromId(id: number, weight?: FontWeight, style?: FontStyle)` This is a convenience method for creating fonts from an asset ID number. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `id` | `number` | | The asset ID of the font as a number. | | `weight` | `FontWeight` | `Enum.FontWeight.Regular` | How thick the text is. | | `style` | `FontStyle` | `Enum.FontStyle.Normal` | Whether the text is normal or italic. | **Font.fromId()** Shows how to use [Font.fromId()](/docs/reference/engine/datatypes/Font.md). ```lua local label = script.Parent.TextLabel label.FontFace = Font.fromId(8836875837) print(label.FontFace) --> Font { Family = rbxassetid://8836875837, Weight = Regular, Style = Normal } ``` **Expected output:** Font { Family = rbxassetid://8836875837, Weight = Regular, Style = Normal } ## Properties ### Font.Family **Type:** `Content` The asset ID for the font family. These start with either `rbxasset://` or `rbxassetid://`. ### Font.Weight **Type:** `FontWeight` How thick the text is. The default value is [FontWeight.Regular](/docs/reference/engine/enums/FontWeight.md). When set, [Font.Bold](/docs/reference/engine/datatypes/Font.md) is updated and becomes `true` if the weight is [FontWeight.SemiBold](/docs/reference/engine/enums/FontWeight.md) or thicker. ### Font.Style **Type:** `FontStyle` Whether the font is italic. The default value is [FontStyle.Normal](/docs/reference/engine/enums/FontStyle.md). ### Font.Bold **Type:** `bool` Whether the font is bold. Sets [Font.Weight](/docs/reference/engine/datatypes/Font.md) to [FontWeight.Bold](/docs/reference/engine/enums/FontWeight.md) when `true`, or to [FontWeight.Regular](/docs/reference/engine/enums/FontWeight.md) otherwise. --- name: Instance last_updated: 2026-06-29T19:34:08Z type: datatype summary: "Holds the constructor for Instances." --- # Instance Holds the constructor for Instances. **Type:** datatype ## Description The [Instance](/docs/reference/engine/classes/Instance.md) data type holds constructors for [Instance](/docs/reference/engine/classes/Instance.md) objects. ## Constructors ### Instance.new **Signature:** `Instance.new(className: string, parent: Instance?)` Creates a new [Instance](/docs/reference/engine/classes/Instance.md) of type `className`. Abstract classes and services cannot be created with this constructor. Note that when the [Parent](/docs/reference/engine/classes/Instance.md) of an object is set, Luau listens to a variety of different property changes for replication, rendering, and physics. For performance reasons, it's recommended to set the instance's [Parent](/docs/reference/engine/classes/Instance.md) property **last** when creating new objects, instead of specifying the second argument (`parent`) of this constructor. ```lua local Workspace = game:GetService("Workspace") -- Set new instance's parent last (recommended) local part = Instance.new("Part") part.Position = Vector3.new(0, 10, 0) part.Parent = Workspace -- Set new instance's parent in constructor (discouraged) local part = Instance.new("Part", Workspace) part.Position = Vector3.new(0, 10, 0) ```` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `className` | `string` | | Class name of the new instance to create. | | `parent` | `Instance?` | | Optional object to parent the new instance to. Not recommended for performance reasons (see description above). | ### Instance.fromExisting **Signature:** `Instance.fromExisting(existingInstance: Instance)` Creates a new object with the same type and property values as an existing object. In most cases using [Instance:Clone()](/docs/reference/engine/classes/Instance.md) is more appropriate, but this constructor is useful when implementing low‑level libraries or systems. There are two behavioral differences between this constructor and the [Instance:Clone()](/docs/reference/engine/classes/Instance.md) method: - This constructor will not copy any of the descendant [Instances](/docs/reference/engine/classes/Instance.md) parented to the existing object. - This constructor will return a new object even if the existing object had [Instance.Archivable](/docs/reference/engine/classes/Instance.md) set to `false`. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `existingInstance` | `Instance` | | The existing [Instance](/docs/reference/engine/classes/Instance.md) to copy property values from. | --- name: NumberRange last_updated: 2026-06-29T19:34:08Z type: datatype summary: "Represents a range of numbers." --- # NumberRange Represents a range of numbers. **Type:** datatype ## Description The [NumberRange](/docs/reference/engine/datatypes/NumberRange.md) represents a range of numbers. `NumberRange` stores its [NumberRange.Min](/docs/reference/engine/datatypes/NumberRange.md) and [NumberRange.Max](/docs/reference/engine/datatypes/NumberRange.md) values as 32-bit floating-point numbers. Very large numbers or numbers requiring high decimal precision may lose accuracy. ## Constructors ### NumberRange.new **Signature:** `NumberRange.new(value: number)` Returns a new [NumberRange](/docs/reference/engine/datatypes/NumberRange.md) with the minimum and maximum set to the `value`. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `value` | `number` | | | ### NumberRange.new **Signature:** `NumberRange.new(minimum: number, maximum: number)` Returns a new [NumberRange](/docs/reference/engine/datatypes/NumberRange.md) with the provided `minimum` and `maximum`. The `minimum` must be less than or equal to `maximum`. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `minimum` | `number` | | | | `maximum` | `number` | | | ## Properties ### NumberRange.Min **Type:** `number` The minimum value of the [NumberRange](/docs/reference/engine/datatypes/NumberRange.md), always less than or equal to the maximum. ### NumberRange.Max **Type:** `number` The maximum value of the [NumberRange](/docs/reference/engine/datatypes/NumberRange.md), always greater than or equal to the minimum. --- name: NumberSequence last_updated: 2026-06-29T19:34:08Z type: datatype summary: "A series of floats across a period of time." --- # NumberSequence A series of floats across a period of time. **Type:** datatype ## Description The [NumberSequence](/docs/reference/engine/datatypes/NumberSequence.md) data type represents a series of number values from `0` to `1`. The number values are expressed using the [NumberSequenceKeypoint](/docs/reference/engine/datatypes/NumberSequenceKeypoint.md) type. This type is used in properties such as [ParticleEmitter.Size](/docs/reference/engine/classes/ParticleEmitter.md) and [Beam.Transparency](/docs/reference/engine/classes/Beam.md) to define a numerical change over time. #### Equality Two [NumberSequence](/docs/reference/engine/datatypes/NumberSequence.md) objects are equivalent only if the values of their [NumberSequenceKeypoint](/docs/reference/engine/datatypes/NumberSequenceKeypoint.md) are equivalent, even if both would result in similar graphs. #### Evaluation The [NumberSequence](/docs/reference/engine/datatypes/NumberSequence.md) type does not have a built-in method for getting the value at a certain time/point because keypoints can have random envelopes. However, assuming the envelope values of your keypoints are all `0`, you can use the following function to evaluate at a specific time. ```lua local function evalNumberSequence(sequence: NumberSequence, time: number) -- If time is 0 or 1, return the first or last value respectively if time == 0 then return sequence.Keypoints[1].Value elseif time == 1 then return sequence.Keypoints[#sequence.Keypoints].Value end -- Otherwise, step through each sequential pair of keypoints for i = 1, #sequence.Keypoints - 1 do local currKeypoint = sequence.Keypoints[i] local nextKeypoint = sequence.Keypoints[i + 1] if time >= currKeypoint.Time and time < nextKeypoint.Time then -- Calculate how far alpha lies between the points local alpha = (time - currKeypoint.Time) / (nextKeypoint.Time - currKeypoint.Time) -- Return the value between the points using alpha return currKeypoint.Value + (nextKeypoint.Value - currKeypoint.Value) * alpha end end end local numberSequence = NumberSequence.new{ NumberSequenceKeypoint.new(0, 0), NumberSequenceKeypoint.new(0.5, 1), NumberSequenceKeypoint.new(1, 0), } print(evalNumberSequence(numberSequence, 0.65)) --> 0.7 ``` ## Constructors ### NumberSequence.new **Signature:** `NumberSequence.new(n: number)` Returns a [NumberSequence](/docs/reference/engine/datatypes/NumberSequence.md) with the start and end values set to the provided `n`. ```lua local numberSequence = NumberSequence.new(n) -- Equivalent to local numberSequence = NumberSequence.new{ NumberSequenceKeypoint.new(0, n), NumberSequenceKeypoint.new(1, n) } ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `n` | `number` | | | ### NumberSequence.new **Signature:** `NumberSequence.new(n0: number, n1: number)` Returns a [NumberSequence](/docs/reference/engine/datatypes/NumberSequence.md) of two keypoints with `n0` as the start value and `n1` as the end value. ```lua local numberSequence = NumberSequence.new(n0, n1) -- Equivalent to local numberSequence = NumberSequence.new{ NumberSequenceKeypoint.new(0, n0), NumberSequenceKeypoint.new(1, n1) } ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `n0` | `number` | | | | `n1` | `number` | | | ### NumberSequence.new **Signature:** `NumberSequence.new(Keypoints: Array)` Returns a [NumberSequence](/docs/reference/engine/datatypes/NumberSequence.md) from an array of [NumberSequenceKeypoints](/docs/reference/engine/datatypes/NumberSequenceKeypoint.md). The keypoints must be provided in a non-descending time value order. At least two keypoints must be provided, and they must have a time value of `0` (first) and `1` (last). ```lua local numberSequence = NumberSequence.new{ NumberSequenceKeypoint.new(0, 0), NumberSequenceKeypoint.new(0.5, 0.5, 0.25), NumberSequenceKeypoint.new(1, 1) } ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `Keypoints` | `Array` | | | ## Properties ### NumberSequence.Keypoints **Type:** `Array` An array containing [NumberSequenceKeypoint](/docs/reference/engine/datatypes/NumberSequenceKeypoint.md) values for the [NumberSequence](/docs/reference/engine/datatypes/NumberSequence.md). --- name: NumberSequenceKeypoint last_updated: 2026-06-29T19:34:08Z type: datatype summary: "Represents keypoint within a NumberSequence with a particular time, value, and envelope size." --- # NumberSequenceKeypoint Represents keypoint within a [NumberSequence](/docs/reference/engine/datatypes/NumberSequence.md) with a particular time, value, and envelope size. **Type:** datatype ## Description The [NumberSequenceKeypoint](/docs/reference/engine/datatypes/NumberSequenceKeypoint.md) data type represents keypoints within a NumberSequence with a particular time, value, and envelope size. ## Constructors ### NumberSequenceKeypoint.new **Signature:** `NumberSequenceKeypoint.new(time: number, value: number)` Returns a keypoint with a specified time and value. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `time` | `number` | | | | `value` | `number` | | | ### NumberSequenceKeypoint.new **Signature:** `NumberSequenceKeypoint.new(time: number, value: number, envelope: number)` Returns a keypoint with a specified time, value, and envelope. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `time` | `number` | | | | `value` | `number` | | | | `envelope` | `number` | | | ## Properties ### NumberSequenceKeypoint.Envelope **Type:** `number` The amount of variance allowed from the value. For example, a value of 0.5 with an envelope of 0.1 could compute to between 0.4 and 0.6 at runtime. The most common use case for this property is to randomize the size of particles (within the envelope) from a [ParticleEmitter](/docs/reference/engine/classes/ParticleEmitter.md). ### NumberSequenceKeypoint.Time **Type:** `number` The relative time at which the keypoint is positioned. ### NumberSequenceKeypoint.Value **Type:** `number` The base value of this keypoint. --- name: OverlapParams last_updated: 2026-06-29T19:34:08Z type: datatype summary: "Stores parameters used in boundary-querying functions." --- # OverlapParams Stores parameters used in boundary-querying functions. **Type:** datatype ## Description The [OverlapParams](/docs/reference/engine/datatypes/OverlapParams.md) data type stores parameters for use with [WorldRoot](/docs/reference/engine/classes/WorldRoot.md) boundary-querying functions, in particular [WorldRoot:GetPartBoundsInBox()](/docs/reference/engine/classes/WorldRoot.md), [WorldRoot:GetPartBoundsInRadius()](/docs/reference/engine/classes/WorldRoot.md) and [WorldRoot:GetPartsInPart()](/docs/reference/engine/classes/WorldRoot.md). The [ExcludeInstances](/docs/reference/engine/datatypes/OverlapParams.md) and [IncludeInstances](/docs/reference/engine/datatypes/OverlapParams.md) properties store a set of objects and their descendants that will be excluded and included from the query. The [OverlapParams.CollisionGroup](/docs/reference/engine/datatypes/OverlapParams.md) property can specify a collision group for the boundary query operation. Unlike most data types in Luau, you can change all of the members of [OverlapParams](/docs/reference/engine/datatypes/OverlapParams.md) without creating a new object, allowing you to reuse the same object repeatedly. ## Constructors ### OverlapParams.new **Signature:** `OverlapParams.new()` Returns a blank [OverlapParams](/docs/reference/engine/datatypes/OverlapParams.md) object. Unlike other data type constructors, this constructor does not have any parameters, so you should set its properties appropriately. ## Properties ### OverlapParams.ExcludeInstances **Type:** `{Instance}?` An optional array of instances whose descendants will be excluded from the query. This property can be used simultaneously with [IncludeInstances](/docs/reference/engine/datatypes/OverlapParams.md) to allow for mixed filtering, for example including a large folder but excluding specific child instances. If an instance matches both an exclude and include filter, exclusions take priority over inclusions. ### OverlapParams.IncludeInstances **Type:** `{Instance}?` An optional array of instances whose descendants will be included in the query. This property can be used simultaneously with [ExcludeInstances](/docs/reference/engine/datatypes/OverlapParams.md) to allow for mixed filtering. If an instance matches both an exclude and include filter, exclusions take priority over inclusions. Setting `IncludeInstances` to `nil` versus an empty array (`{}`) has opposite effects. `IncludeInstances = nil` is the most permissive filter (includes everything), whereas `IncludeInstances = {}` is the most restrictive filter (includes nothing). ### OverlapParams.MaxParts **Type:** `number` The maximum amount of parts to be returned by the query. The default value of zero (`0`) represents no limit. ### OverlapParams.CollisionGroup **Type:** `string` Specifies a collision group for the operation. Parts in collision groups that are set to **not** collide with this group are ignored. If this property is omitted, the operation assumes the **Default** collision group. ### OverlapParams.Tolerance **Type:** `number` Slightly increases the volume of the boundary-querying operation, clamped between `0` and `0.05` studs. ### OverlapParams.RespectCanCollide **Type:** `bool` This property, if `true`, makes the boundary-querying operation use an intersected part's [BasePart.CanCollide](/docs/reference/engine/classes/BasePart.md) value in favor of its [BasePart.CanQuery](/docs/reference/engine/classes/BasePart.md) value when determining whether that part is included in the array of spatial query results. ### OverlapParams.BruteForceAllSlow **Type:** `bool` When enabled, the query will ignore all part collision properties and perform a brute-force check on every part. This will negatively impact performance and should not be used in live experiences. ### OverlapParams.FilterDescendantsInstances **Type:** `Array` An array of objects whose descendants are used in filtering candidates. Note that this property has been superseded by [ExcludeInstances](/docs/reference/engine/datatypes/OverlapParams.md) and [IncludeInstances](/docs/reference/engine/datatypes/OverlapParams.md) which should be used for new work. ### OverlapParams.FilterType **Type:** `RaycastFilterType` Determines whether the [FilterDescendantsInstances](/docs/reference/engine/datatypes/OverlapParams.md) array is used as an exclude or include list. Note that this property has been superseded by [ExcludeInstances](/docs/reference/engine/datatypes/OverlapParams.md) and [IncludeInstances](/docs/reference/engine/datatypes/OverlapParams.md) which should be used for new work. ## Methods ### OverlapParams:AddToFilter **Signature:** `OverlapParams:AddToFilter(instances: Instance | Array): ()` Adds the instances provided to [FilterDescendantsInstances](/docs/reference/engine/datatypes/OverlapParams.md). Note that this property has been superseded by [ExcludeInstances](/docs/reference/engine/datatypes/OverlapParams.md) and [IncludeInstances](/docs/reference/engine/datatypes/OverlapParams.md) which should be used for new work. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `instances` | `Instance | Array` | | An instance or an array containing instances to add. | **Returns:** `()` --- name: Path2DControlPoint last_updated: 2026-06-29T19:34:08Z type: datatype summary: "Stores the info for a single control point used with the Path2D instance." --- # Path2DControlPoint Stores the info for a single control point used with the [Path2D](/docs/reference/engine/classes/Path2D.md) instance. **Type:** datatype ## Description The `Path2DControlPoint` data type represents a single control point used with the [Path2D](/docs/reference/engine/classes/Path2D.md) instance. ## Constructors ### Path2DControlPoint.new **Signature:** `Path2DControlPoint.new()` Returns an empty [Path2DControlPoint](/docs/reference/engine/datatypes/Path2DControlPoint.md). ### Path2DControlPoint.new **Signature:** `Path2DControlPoint.new(Position: UDim2)` Returns a [Path2DControlPoint](/docs/reference/engine/datatypes/Path2DControlPoint.md) with the position set. This constructor will set the tangents to default [UDim2](/docs/reference/engine/datatypes/UDim2.md) values. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `Position` | `UDim2` | | The [UDim2](/docs/reference/engine/datatypes/UDim2.md) position of the control point. | ### Path2DControlPoint.new **Signature:** `Path2DControlPoint.new(Position: UDim2, Left Tangent: UDim2, Right Tangent: UDim2)` Returns a [Path2DControlPoint](/docs/reference/engine/datatypes/Path2DControlPoint.md) with the position, left tangent, and right tangent set. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `Position` | `UDim2` | | The [UDim2](/docs/reference/engine/datatypes/UDim2.md) position of the control point. | | `Left Tangent` | `UDim2` | | The [UDim2](/docs/reference/engine/datatypes/UDim2.md) left tangent of the control point. | | `Right Tangent` | `UDim2` | | The [UDim2](/docs/reference/engine/datatypes/UDim2.md) right tangent of the control point. | ## Properties ### Path2DControlPoint.Position **Type:** `UDim2` The position of the [Path2DControlPoint](/docs/reference/engine/datatypes/Path2DControlPoint.md) relative to the path's container. ### Path2DControlPoint.LeftTangent **Type:** `UDim2` The left tangent of the [Path2DControlPoint](/docs/reference/engine/datatypes/Path2DControlPoint.md). ### Path2DControlPoint.RightTangent **Type:** `UDim2` The right tangent of the [Path2DControlPoint](/docs/reference/engine/datatypes/Path2DControlPoint.md). --- name: PathWaypoint last_updated: 2026-06-29T19:34:08Z type: datatype summary: "A description of the steps required to reach the next waypoint in a path." --- # PathWaypoint A description of the steps required to reach the next waypoint in a path. **Type:** datatype ## Description The `PathWaypoint` data type is constructed by a [PathWaypointAction](/docs/reference/engine/enums/PathWaypointAction.md) action, [Vector3](/docs/reference/engine/datatypes/Vector3.md) position, and [string](/docs/reference/engine/globals/string.md) label which is used by the [PathfindingService](/docs/reference/engine/classes/PathfindingService.md) to create points along a generated path. ## Constructors ### PathWaypoint.new **Signature:** `PathWaypoint.new(position: Vector3, action: PathWaypointAction, label: string)` Returns a [PathWaypoint](/docs/reference/engine/datatypes/PathWaypoint.md) object from the given [Vector3](/docs/reference/engine/datatypes/Vector3.md) position, [PathWaypointAction](/docs/reference/engine/enums/PathWaypointAction.md) action, and optional string label. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `position` | `Vector3` | | The 3D position of the waypoint. | | `action` | `PathWaypointAction` | | The action to perform at the waypoint. | | `label` | `string` | | The name of the navigation area that generates the waypoint. | ## Properties ### PathWaypoint.Action **Type:** `PathWaypointAction` The action to perform at this waypoint as an [PathWaypointAction](/docs/reference/engine/enums/PathWaypointAction.md). ### PathWaypoint.Position **Type:** `Vector3` The 3D position of this waypoint. ### PathWaypoint.Label **Type:** `string` The name of the navigation area that generates this waypoint. Automatic jump links have `Jump` as their label. --- name: PhysicalProperties last_updated: 2026-06-29T19:34:08Z type: datatype summary: "Describes properties that affect the physical behavior of a BasePart." --- # PhysicalProperties Describes properties that affect the physical behavior of a [BasePart](/docs/reference/engine/classes/BasePart.md). **Type:** datatype ## Description The [PhysicalProperties](/docs/reference/engine/datatypes/PhysicalProperties.md) data type describes several physical properties of a [BasePart](/docs/reference/engine/classes/BasePart.md): [Density](/docs/reference/engine/datatypes/PhysicalProperties.md), [Elasticity](/docs/reference/engine/datatypes/PhysicalProperties.md), and [Friction](/docs/reference/engine/datatypes/PhysicalProperties.md). It is used in the similarly-named [BasePart.CustomPhysicalProperties](/docs/reference/engine/classes/BasePart.md) property. #### Weighting Behavior [PhysicalProperties](/docs/reference/engine/datatypes/PhysicalProperties.md) also provides weightings properties, [ElasticityWeight](/docs/reference/engine/datatypes/PhysicalProperties.md) and [FrictionWeight](/docs/reference/engine/datatypes/PhysicalProperties.md). When two parts interact, the friction and elasticity between them are determined in the same way by the following pairwise weighted average function: ```lua local function getActualFriction(partA, partB) return (partA.Friction * partA.FrictionWeight + partB.Friction * partB.FrictionWeight) / (partA.FrictionWeight + partB.FrictionWeight) end ``` Although the formula above refers to the [Friction](/docs/reference/engine/datatypes/PhysicalProperties.md) and [FrictionWeight](/docs/reference/engine/datatypes/PhysicalProperties.md) of two parts, **A** and **B**, the formula is used in the same manner when determining [Elasticity](/docs/reference/engine/datatypes/PhysicalProperties.md). Generally, when the weight of **A** is much greater than that of **B**, the actual value will be closer to **A**. If the weights are similar, the actual value will be close to the midpoint between their individual values. ## Constructors ### PhysicalProperties.new **Signature:** `PhysicalProperties.new(material: Material)` Returns a [PhysicalProperties](/docs/reference/engine/datatypes/PhysicalProperties.md) container, with the density, friction, and elasticity specified for this material. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `material` | `Material` | | | ### PhysicalProperties.new **Signature:** `PhysicalProperties.new(density: number, friction: number, elasticity: number)` Returns a [PhysicalProperties](/docs/reference/engine/datatypes/PhysicalProperties.md) container, with the specified density, friction, and elasticity. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `density` | `number` | | | | `friction` | `number` | | | | `elasticity` | `number` | | | ### PhysicalProperties.new **Signature:** `PhysicalProperties.new(density: number, friction: number, elasticity: number, frictionWeight: number, elasticityWeight: number)` Creates a [PhysicalProperties](/docs/reference/engine/datatypes/PhysicalProperties.md) container with the specified density, friction, elasticity, weight of friction, and weight of elasticity. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `density` | `number` | | | | `friction` | `number` | | | | `elasticity` | `number` | | | | `frictionWeight` | `number` | | | | `elasticityWeight` | `number` | | | ### PhysicalProperties.new **Signature:** `PhysicalProperties.new(density: number, friction: number, elasticity: number, frictionWeight: number, elasticityWeight: number, acousticAbsorption: number)` Creates a [PhysicalProperties](/docs/reference/engine/datatypes/PhysicalProperties.md) container with the specified density, friction, elasticity, weight of friction, weight of elasticity, and acoustic absorption. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `density` | `number` | | | | `friction` | `number` | | | | `elasticity` | `number` | | | | `frictionWeight` | `number` | | | | `elasticityWeight` | `number` | | | | `acousticAbsorption` | `number` | | | ## Properties ### PhysicalProperties.AcousticAbsorption **Type:** `number` A value between `0` and `1` denoting how absorbent the material is to [AudioEmitters](/docs/reference/engine/classes/AudioEmitter.md). When using acoustic simulation, surfaces with higher absorption will result in less reverb than surfaces with lower absorption. Note that this does not affect the degree to which audio transmits **through** surfaces; for that, see [Density](/docs/reference/engine/datatypes/PhysicalProperties.md). ### PhysicalProperties.Density **Type:** `number` Density is defined as the amount of mass per unit volume. The more dense a part is, the more force it takes to accelerate it. Acceptable range is `0.0001` to `100.0` and values outside this range will be clamped. When using acoustic simulation, parts with higher density will muffle occluded [AudioEmitters](/docs/reference/engine/classes/AudioEmitter.md) more. ### PhysicalProperties.Friction **Type:** `number` Friction is defined as the force that opposes the relative lateral motion of two solid surfaces in contact. The greater the friction on a part, the quicker it will decelerate when it rubs against another part with friction. Acceptable range is `0.0` to `2.0` and values outside this range will be clamped. ### PhysicalProperties.Elasticity **Type:** `number` Elasticity refers to a part's tendency to retain energy when colliding with another part. An [Elasticity](/docs/reference/engine/datatypes/PhysicalProperties.md) of 1 indicates that the part bounces with the same energy it had before a collision. Acceptable range is `0.0` to `1.0` and values outside this range will be clamped. ### PhysicalProperties.FrictionWeight **Type:** `number` The friction weight of two parts rubbing together creates a ratio used to calculate the actual friction between the two parts. The higher a part's [FrictionWeight](/docs/reference/engine/datatypes/PhysicalProperties.md), the more its [Friction](/docs/reference/engine/datatypes/PhysicalProperties.md) is used. Acceptable range is `0.0` to `100.0` and values outside this range will be clamped. ### PhysicalProperties.ElasticityWeight **Type:** `number` The elasticity weight of two parts colliding creates a ratio used to calculate the actual elasticity between the two parts. The higher a part's [ElasticityWeight](/docs/reference/engine/datatypes/PhysicalProperties.md), the more its [Elasticity](/docs/reference/engine/datatypes/PhysicalProperties.md) is used. Acceptable range is `0.0` to `100.0` and values outside this range will be clamped. --- name: RBXScriptConnection last_updated: 2026-06-29T19:34:08Z type: datatype summary: "A connection between an RBXScriptSignal and a function." --- # RBXScriptConnection A connection between an [RBXScriptSignal](/docs/reference/engine/datatypes/RBXScriptSignal.md) and a function. **Type:** datatype ## Description The `RBXScriptConnection` data type is a special object returned by the [Connect()](/docs/reference/engine/datatypes/RBXScriptSignal.md) method of an event ([RBXScriptSignal](/docs/reference/engine/datatypes/RBXScriptSignal.md)). This is used primarily to disconnect a listener from an [RBXScriptSignal](/docs/reference/engine/datatypes/RBXScriptSignal.md). ```lua local part : BasePart = script.Parent -- Store reference to the RBXScriptConnection so it can be disconnected later local connection : RBXScriptConnection = part.Touched:Connect(function(otherPart) print("Hello world!") end) -- Wait 15 seconds, then disconnect task.wait(15) connection:Disconnect() ``` ## Properties ### RBXScriptConnection.Connected **Type:** `bool` Describes whether or not the connection is still alive. This becomes `false` if [Disconnect()](/docs/reference/engine/datatypes/RBXScriptConnection.md) is called. ## Methods ### RBXScriptConnection:Disconnect **Signature:** `RBXScriptConnection:Disconnect(): ()` Disconnects the connection from the event. **Returns:** `()` --- name: RBXScriptSignal last_updated: 2026-06-29T19:34:08Z type: datatype summary: "An object that runs connected functions upon a specific occurrence." --- # RBXScriptSignal An object that runs connected functions upon a specific occurrence. **Type:** datatype ## Description The [RBXScriptSignal](/docs/reference/engine/datatypes/RBXScriptSignal.md) data type, more commonly known as an **Event**, provides a way for user-defined functions, called **listeners**, to call when something happens in the game. When an event happens, the [RBXScriptSignal](/docs/reference/engine/datatypes/RBXScriptSignal.md) fires and calls any listeners that are connected to it. An [RBXScriptSignal](/docs/reference/engine/datatypes/RBXScriptSignal.md) may also pass arguments to each listener to provide extra information about the event. ## Methods ### RBXScriptSignal:Connect **Signature:** `RBXScriptSignal:Connect(func: function): RBXScriptConnection` Establishes a function to be called when the event fires. Returns an [RBXScriptConnection](/docs/reference/engine/datatypes/RBXScriptConnection.md) object associated with the connection. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `func` | `function` | | | **Returns:** `RBXScriptConnection` ### RBXScriptSignal:ConnectParallel **Signature:** `RBXScriptSignal:ConnectParallel(func: function): RBXScriptConnection` Establishes a function to be called when the event fires. Returns an [RBXScriptConnection](/docs/reference/engine/datatypes/RBXScriptConnection.md) object associated with the connection. When the event fires, the signal callback is executed in a desynchronized state. Using `ConnectParallel` is similar to, but more efficient than, using `Connect` followed by a call to [task.desynchronize()](/docs/reference/engine/globals/task.md) in the signal handler. Note: Scripts that connect in parallel must be rooted under an Actor. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `func` | `function` | | | **Returns:** `RBXScriptConnection` ### RBXScriptSignal:Once **Signature:** `RBXScriptSignal:Once(func: function): RBXScriptConnection` Establishes a function to be called when the event fires. Returns an [RBXScriptConnection](/docs/reference/engine/datatypes/RBXScriptConnection.md) object associated with the connection. The behavior of `Once` is similar to `Connect`. However, instead of allowing multiple events to be received by the specified function, only the first event will be delivered. Using `Once` also ensures that the connection to the function will be automatically disconnected prior the function being called. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `func` | `function` | | | **Returns:** `RBXScriptConnection` ### RBXScriptSignal:Wait **Signature:** `RBXScriptSignal:Wait(): Variant` Yields the current thread until the signal fires and returns the arguments provided by the signal. **Returns:** `Variant` --- name: Random last_updated: 2026-06-29T19:34:08Z type: datatype summary: "Generates pseudorandom numbers and directions." --- # Random Generates pseudorandom numbers and directions. **Type:** datatype ## Description The [Random](/docs/reference/engine/datatypes/Random.md) data type generates pseudorandom numbers and directions. ## Constructors ### Random.new **Signature:** `Random.new(seed: number)` Returns a new [Random](/docs/reference/engine/datatypes/Random.md) object. If you don't provide the seed parameter, [Random](/docs/reference/engine/datatypes/Random.md) uses a seed from an internal entropy source. If you provide a seed, it should be within the range [-9007199254740991, 9007199254740991], and [Random](/docs/reference/engine/datatypes/Random.md) will round it down to the nearest integer. So seeds of 0, 0.99, and [math.random()](/docs/reference/engine/globals/math.md) all produce identical generators. If you need to generate a seed and store it for later retrieval, use [math.random(max)](/docs/reference/engine/globals/math.md). **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `seed` | `number` | | | **Datatype.Random** Generates a pseudorandom seed and uses it to create a new [Random](/docs/reference/engine/datatypes/Random.md) generator. ```lua local max = 2147483647 -- use a large integer local seed = math.random(max) local generator = Random.new(seed) ``` ## Methods ### Random:NextInteger **Signature:** `Random:NextInteger(min: number, max: number): number` Returns a pseudorandom integer uniformly distributed over `[min, max]`. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `min` | `number` | | | | `max` | `number` | | | **Returns:** `number` ### Random:NextNumber **Signature:** `Random:NextNumber(): number` Returns a uniform pseudorandom real number in the range of 0 to 1, inclusive. **Returns:** `number` ### Random:NextNumber **Signature:** `Random:NextNumber(min: number, max: number): number` Returns a uniform pseudorandom real number in the range of `min` to `max`, inclusive. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `min` | `number` | | | | `max` | `number` | | | **Returns:** `number` ### Random:Shuffle **Signature:** `Random:Shuffle(tb: table): ()` Uniformly shuffles the array part of `tb` in-place using `NextInteger` to pick indices. If there are any `nil` "holes" in the array part of the table, `Shuffle` throws an error, since shuffling could change the length. The hash part of `tb` is ignored. No metamethods of `tb` are invoked. The shuffle is defined to be a Fisher-Yates shuffle so the number of `NextInteger` calls is guaranteed to be consistent between engine versions for a given size of table. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `tb` | `table` | | | **Returns:** `()` ### Random:NextUnitVector **Signature:** `Random:NextUnitVector(): Vector3` Returns a unit vector with a pseudorandom direction. Vectors returned from this function are uniformly distributed over the unit sphere. **Returns:** `Vector3` — A unit vector with a pseudorandom direction. ### Random:Clone **Signature:** `Random:Clone(): Random` Returns a new Random object with the same state as the original. **Returns:** `Random` --- name: Ray last_updated: 2026-06-29T19:34:08Z type: datatype summary: "Represents a line with a starting point that casts infinitely in a specific direction." --- # Ray Represents a line with a starting point that casts infinitely in a specific direction. **Type:** datatype ## Description The [Ray](/docs/reference/engine/datatypes/Ray.md) data type represents a half-line, finite in one direction but infinite in the other. It can be defined by a 3D point, where the line originates from, and a direction vector, which is the direction it goes in. ## Constructors ### Ray.new **Signature:** `Ray.new(Origin: Vector3, Direction: Vector3)` Returns a new [Ray](/docs/reference/engine/datatypes/Ray.md) with given `Origin` and `Direction`. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `Origin` | `Vector3` | | | | `Direction` | `Vector3` | | | ## Properties ### Ray.Unit **Type:** `Ray` The [Ray](/docs/reference/engine/datatypes/Ray.md) with a normalized direction (the direction has a magnitude of `1`). ### Ray.Origin **Type:** `Vector3` The position of the origin. ### Ray.Direction **Type:** `Vector3` The direction vector of the [Ray](/docs/reference/engine/datatypes/Ray.md). ## Methods ### Ray:ClosestPoint **Signature:** `Ray:ClosestPoint(point: Vector3): Vector3` Returns a [Vector3](/docs/reference/engine/datatypes/Vector3.md) projected onto the ray so that it is within the [Ray](/docs/reference/engine/datatypes/Ray.md) line of sight. **Note:** the [Ray](/docs/reference/engine/datatypes/Ray.md) **must** be a unit ray for this method to behave as expected! **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `point` | `Vector3` | | | **Returns:** `Vector3` ### Ray:Distance **Signature:** `Ray:Distance(point: Vector3): number` Returns the distance between the given point and the point on the ray nearest to the given point ([Ray.ClosestPoint(point)](/docs/reference/engine/datatypes/Ray.md)). **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `point` | `Vector3` | | | **Returns:** `number` --- name: RaycastParams last_updated: 2026-06-29T19:34:08Z type: datatype summary: "A container for parameters used in raycasting operations." --- # RaycastParams A container for parameters used in raycasting operations. **Type:** datatype ## Description The [RaycastParams](/docs/reference/engine/datatypes/RaycastParams.md) data type stores parameters for [WorldRoot:Raycast()](/docs/reference/engine/classes/WorldRoot.md) operations. The [ExcludeInstances](/docs/reference/engine/datatypes/RaycastParams.md) and [IncludeInstances](/docs/reference/engine/datatypes/RaycastParams.md) properties store a set of objects and their descendants that will be excluded and included from the query. The [RaycastParams.IgnoreWater](/docs/reference/engine/datatypes/RaycastParams.md) property can be used to ignore [Terrain](/docs/reference/engine/classes/Terrain.md) water, and the [RaycastParams.CollisionGroup](/docs/reference/engine/datatypes/RaycastParams.md) property can specify a collision group for the raycasting operation. This object is different from the similarly named [RaycastResult](/docs/reference/engine/datatypes/RaycastResult.md) which provides the results of a raycast. Unlike most data types in Luau, you can change all of the members of [RaycastParams](/docs/reference/engine/datatypes/RaycastParams.md) without creating a new object, allowing you to reuse the same object repeatedly. ## Constructors ### RaycastParams.new **Signature:** `RaycastParams.new()` Returns a blank [RaycastParams](/docs/reference/engine/datatypes/RaycastParams.md) object. Unlike other data type constructors, this constructor does not have any parameters, so you should set its properties appropriately. ## Properties ### RaycastParams.ExcludeInstances **Type:** `{Instance}?` An optional array of instances whose descendants will be excluded from the query. This property can be used simultaneously with [IncludeInstances](/docs/reference/engine/datatypes/RaycastParams.md) to allow for mixed filtering, for example including a large folder but excluding specific child instances. If an instance matches both an exclude and include filter, exclusions take priority over inclusions. ### RaycastParams.IncludeInstances **Type:** `{Instance}?` An optional array of instances whose descendants will be included in the query. This property can be used simultaneously with [ExcludeInstances](/docs/reference/engine/datatypes/RaycastParams.md) to allow for mixed filtering. If an instance matches both an exclude and include filter, exclusions take priority over inclusions. Setting `IncludeInstances` to `nil` versus an empty array (`{}`) has opposite effects. `IncludeInstances = nil` is the most permissive filter (includes everything), whereas `IncludeInstances = {}` is the most restrictive filter (includes nothing). ### RaycastParams.IgnoreWater **Type:** `bool` Determines whether the water material is considered when raycasting against [Terrain](/docs/reference/engine/classes/Terrain.md). ### RaycastParams.CollisionGroup **Type:** `string` Specifies a collision group for the raycasting operation. Parts in collision groups that are set to **not** collide with this group are ignored. If this property is omitted, the raycast assumes the **Default** collision group. ### RaycastParams.RespectCanCollide **Type:** `bool` This property, if `true`, makes the raycast operation use an intersected part's [CanCollide](/docs/reference/engine/classes/BasePart.md) value in favor of its [CanQuery](/docs/reference/engine/classes/BasePart.md) value when determining whether that part is included in the [RaycastResult](/docs/reference/engine/datatypes/RaycastResult.md). ### RaycastParams.BruteForceAllSlow **Type:** `bool` When enabled, the query will ignore all part collision properties and perform a brute-force check on every part. This will negatively impact performance and should not be used in live experiences. ### RaycastParams.FilterDescendantsInstances **Type:** `Array` An array of objects whose descendants are used to filter raycasting candidates. Note that this property has been superseded by [ExcludeInstances](/docs/reference/engine/datatypes/RaycastParams.md) and [IncludeInstances](/docs/reference/engine/datatypes/RaycastParams.md) which should be used for new work. ### RaycastParams.FilterType **Type:** `RaycastFilterType` Determines whether the [FilterDescendantsInstances](/docs/reference/engine/datatypes/RaycastParams.md) array is used as an exclude or include list. Note that this property has been superseded by [ExcludeInstances](/docs/reference/engine/datatypes/RaycastParams.md) and [IncludeInstances](/docs/reference/engine/datatypes/RaycastParams.md) which should be used for new work. ## Methods ### RaycastParams:AddToFilter **Signature:** `RaycastParams:AddToFilter(instances: Instance | Array): ()` Adds the instances provided to [FilterDescendantsInstances](/docs/reference/engine/datatypes/RaycastParams.md). Note that this property has been superseded by [ExcludeInstances](/docs/reference/engine/datatypes/RaycastParams.md) and [IncludeInstances](/docs/reference/engine/datatypes/RaycastParams.md) which should be used for new work. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `instances` | `Instance | Array` | | An instance or an array containing instances to add. | **Returns:** `()` --- name: RaycastResult last_updated: 2026-06-29T19:34:08Z type: datatype summary: "Stores results from a raycast operation." --- # RaycastResult Stores results from a raycast operation. **Type:** datatype ## Description The [RaycastResult](/docs/reference/engine/datatypes/RaycastResult.md) data type stores the result of a successful raycasting operation performed by [WorldRoot:Raycast()](/docs/reference/engine/classes/WorldRoot.md). It contains the properties listed below. This object should not be confused with the similarly-named [RaycastParams](/docs/reference/engine/datatypes/RaycastParams.md) which is used to perform a raycast. ## Properties ### RaycastResult.Distance **Type:** `number` The distance between the ray origin and the intersection point. ### RaycastResult.Instance **Type:** `BasePart` The [BasePart](/docs/reference/engine/classes/BasePart.md) or [Terrain](/docs/reference/engine/classes/Terrain.md) cell that the ray intersected. ### RaycastResult.Material **Type:** `Material` The [Material](/docs/reference/engine/enums/Material.md) at the intersection point. For normal parts this is the [BasePart.Material](/docs/reference/engine/classes/BasePart.md); for [Terrain](/docs/reference/engine/classes/Terrain.md) this can vary depending on terrain data. ### RaycastResult.Position **Type:** `Vector3` The world space point at which the intersection occurred, usually a point directly on the surface of the [BasePart](/docs/reference/engine/classes/BasePart.md) or [Terrain](/docs/reference/engine/classes/Terrain.md) cell. ### RaycastResult.Normal **Type:** `Vector3` The normal vector of the intersected face. --- name: Rect last_updated: 2026-06-29T19:34:08Z type: datatype summary: "A value that represents a two-dimensional rectangle." --- # Rect A value that represents a two-dimensional rectangle. **Type:** datatype ## Description **Rect** describes a rectangle on a 2D plane. It is constructed using two corners, either two [Vector2](/docs/reference/engine/datatypes/Vector2.md) positions or four numbers: ```lua local rect1 = Rect.new(Vector2.new(10, 10), Vector2.new(80, 80)) local rect2 = Rect.new(10, 10, 80, 80) ``` This data type is used in the [ImageLabel.SliceCenter](/docs/reference/engine/classes/ImageLabel.md) property which determines the center area of a scaled image. ## Constructors ### Rect.new **Signature:** `Rect.new()` Constructs a new **Rect** with two zero [Vector2](/docs/reference/engine/datatypes/Vector2.md) positions. ### Rect.new **Signature:** `Rect.new(min: Vector2, max: Vector2)` Constructs a new **Rect** given two [Vector2](/docs/reference/engine/datatypes/Vector2.md) positions: `min` as the top-left corner and `max` as the bottom-right corner. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `min` | `Vector2` | | | | `max` | `Vector2` | | | ### Rect.new **Signature:** `Rect.new(minX: number, minY: number, maxX: number, maxY: number)` Constructs a new **Rect** using `minX` and `minY` as coordinates for the top-left corner, and `maxX` and `maxY` as coordinates for the bottom-right corner. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `minX` | `number` | | | | `minY` | `number` | | | | `maxX` | `number` | | | | `maxY` | `number` | | | ## Properties ### Rect.Width **Type:** `number` The width of the **Rect** in pixels. ### Rect.Height **Type:** `number` The height of the **Rect** in pixels. ### Rect.Min **Type:** `Vector2` The top-left corner. ### Rect.Max **Type:** `Vector2` The bottom-right corner. --- name: Region3 last_updated: 2026-06-29T19:34:08Z type: datatype summary: "Describes a rectangular volume in 3D space." --- # Region3 Describes a rectangular volume in 3D space. **Type:** datatype ## Description The [Region3](/docs/reference/engine/datatypes/Region3.md) data type describes a volume in 3D space similar to an **axis-aligned rectangular prism**. It is commonly used with [Terrain](/docs/reference/engine/classes/Terrain.md) functions and functions that detect parts within a volume, such as [WorldRoot:FindPartsInRegion3()](/docs/reference/engine/classes/WorldRoot.md). The prism's center is accessible using the [Region3.CFrame](/docs/reference/engine/datatypes/Region3.md) property and the prism's size is accessible through the [Region3.Size](/docs/reference/engine/datatypes/Region3.md) property. Note that the components of this property may be **negative**. The [Region3:ExpandToGrid()](/docs/reference/engine/datatypes/Region3.md) method returns a new [Region3](/docs/reference/engine/datatypes/Region3.md) whose bounds comply with a provided resolution value. The resulting volume may be equal to or greater than the original volume, but never smaller. See also: - [Region3int16](/docs/reference/engine/datatypes/Region3int16.md) ## Constructors ### Region3.new **Signature:** `Region3.new(min: Vector3, max: Vector3)` Returns a new [Region3](/docs/reference/engine/datatypes/Region3.md) given the [Vector3](/docs/reference/engine/datatypes/Vector3.md) bounds of the rectangular prism volume. Note that the order of the provided bounds matters: by switching them, the polarity of the size components will switch. It is possible to create a [Region3](/docs/reference/engine/datatypes/Region3.md) with a **negative volume**. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `min` | `Vector3` | | | | `max` | `Vector3` | | | ## Properties ### Region3.CFrame **Type:** `CFrame` The center location and rotation of the [Region3](/docs/reference/engine/datatypes/Region3.md). ### Region3.Size **Type:** `Vector3` The 3D size of the [Region3](/docs/reference/engine/datatypes/Region3.md). ## Methods ### Region3:ExpandToGrid **Signature:** `Region3:ExpandToGrid(resolution: number): Region3` Expands the [Region3](/docs/reference/engine/datatypes/Region3.md) based on the provided resolution and returns the expanded [Region3](/docs/reference/engine/datatypes/Region3.md) aligned to the [Terrain](/docs/reference/engine/classes/Terrain.md) voxel grid. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `resolution` | `number` | | | **Returns:** `Region3` --- name: Region3int16 last_updated: 2026-06-29T19:34:08Z type: datatype summary: "Represents a Region3 stored as two boundaries as opposed to position and size components." --- # Region3int16 Represents a Region3 stored as two boundaries as opposed to position and size components. **Type:** datatype ## Description Not to be confused with [Region3](/docs/reference/engine/datatypes/Region3.md), a separate class that fulfills a different purpose. The [Region3int16](/docs/reference/engine/datatypes/Region3int16.md) data type represents a volume in 3D space similar to an **axis-aligned rectangular prism**. It uses two [Vector3int16](/docs/reference/engine/datatypes/Vector3int16.md) to store the volume's bounds in the `Min` and `Max` properties. It is constructed using `Region3int16.new(Min, Max)`, given the two [Vector3int16](/docs/reference/engine/datatypes/Vector3int16.md) bounds. This data type features no functions or operations. ## Calculating Center and Size This data type differs from [Region3](/docs/reference/engine/datatypes/Region3.md) in that it stores its bounds directly, rather than through a center and size combination. Nonetheless, it is possible to calculate these dimensions using `Min` and `Max`: ```lua local region = Region3int16.new(Vector3int16.new(0, 0, -3), Vector3int16.new(4, 4, 4)) local size = region.Max - region.Min local center = (region.Max + region.Min) / 2 ``` ## Conversion to Region3 The following function can be used to convert a Region3int16 into a similar [Region3](/docs/reference/engine/datatypes/Region3.md). It does this by converting the `Min` and `Max` properties, which are Vector3int16, into [Vector3](/docs/reference/engine/datatypes/Vector3.md) used with [Region3.new()](/docs/reference/engine/datatypes/Region3.md). ```lua local function Region3int16toRegion3(region16) return Region3.new( Vector3.new(region16.Min.X, region16.Min.Y, region16.Min.Z), Vector3.new(region16.Max.X, region16.Max.Y, region16.Max.Z) ) end ``` See also: - [Region3](/docs/reference/engine/datatypes/Region3.md), a similar data type ## Constructors ### Region3int16.new **Signature:** `Region3int16.new(min: Vector3int16, max: Vector3int16)` Returns a new Region3int16 from the provided boundaries. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `min` | `Vector3int16` | | | | `max` | `Vector3int16` | | | ## Properties ### Region3int16.Min **Type:** `Vector3int16` The lower bound of the [Region3int16](/docs/reference/engine/datatypes/Region3int16.md), as passed to [Region3int16.new()](/docs/reference/engine/datatypes/Region3int16.md). ### Region3int16.Max **Type:** `Vector3int16` The upper bound of the [Region3int16](/docs/reference/engine/datatypes/Region3int16.md), as passed to [Region3int16.new()](/docs/reference/engine/datatypes/Region3int16.md). --- name: RotationCurveKey last_updated: 2026-06-29T19:34:08Z type: datatype summary: "A time-value pair used with RotationCurve instances." --- # RotationCurveKey A time-value pair used with [RotationCurve](/docs/reference/engine/classes/RotationCurve.md) instances. **Type:** datatype ## Description A time-value pair used with [RotationCurve](/docs/reference/engine/classes/RotationCurve.md) instances. The [Interpolation](/docs/reference/engine/datatypes/RotationCurveKey.md) property dictates the interpolation mode for the segment started by this key and ended by the next key on the curve. Each segment may use a different interpolation mode. The [LeftTangent](/docs/reference/engine/datatypes/RotationCurveKey.md) and [RightTangent](/docs/reference/engine/datatypes/RotationCurveKey.md) properties apply to the cubic interpolation mode and define the desired tangent (slope) at the key. Different left and right values can be used to encode discontinuities in slope at the key. Attempting to set a [RightTangent](/docs/reference/engine/datatypes/RotationCurveKey.md) value on a key that doesn't use the cubic interpolation mode will result in a runtime error. It is possible to set the [LeftTangent](/docs/reference/engine/datatypes/RotationCurveKey.md) property on any key, as it will be used should the preceding segment use cubic interpolation. ## Constructors ### RotationCurveKey.new **Signature:** `RotationCurveKey.new(time: number, cframe: CFrame, Interpolation: KeyInterpolationMode)` Creates a new [RotationCurveKey](/docs/reference/engine/datatypes/RotationCurveKey.md) at a given time with a given [CFrame](/docs/reference/engine/datatypes/CFrame.md). [LeftTangent](/docs/reference/engine/datatypes/RotationCurveKey.md) and [RightTangent](/docs/reference/engine/datatypes/RotationCurveKey.md) are left uninitialized and, if not initialized, tangent values of 0 will be used when evaluating the curve. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `time` | `number` | | Time at which to create the new [RotationCurveKey](/docs/reference/engine/datatypes/RotationCurveKey.md). | | `cframe` | `CFrame` | | [CFrame](/docs/reference/engine/datatypes/CFrame.md) of the new [RotationCurveKey](/docs/reference/engine/datatypes/RotationCurveKey.md). | | `Interpolation` | `KeyInterpolationMode` | | | ## Properties ### RotationCurveKey.Interpolation **Type:** `KeyInterpolationMode` Defines the key interpolation mode for the segment started by this [RotationCurveKey](/docs/reference/engine/datatypes/RotationCurveKey.md). ### RotationCurveKey.Time **Type:** `number` The time position of this [RotationCurveKey](/docs/reference/engine/datatypes/RotationCurveKey.md). ### RotationCurveKey.Value **Type:** `CFrame` The [CFrame](/docs/reference/engine/datatypes/CFrame.md) value of this [RotationCurveKey](/docs/reference/engine/datatypes/RotationCurveKey.md). ### RotationCurveKey.RightTangent **Type:** `number` The tangent to the right of this [RotationCurveKey](/docs/reference/engine/datatypes/RotationCurveKey.md). ### RotationCurveKey.LeftTangent **Type:** `number` The tangent to the left of this [RotationCurveKey](/docs/reference/engine/datatypes/RotationCurveKey.md). --- name: Secret last_updated: 2026-06-29T19:34:08Z type: datatype summary: "Stores secret non-printable content." --- # Secret Stores secret non-printable content. **Type:** datatype ## Description The [Secret](/docs/reference/engine/datatypes/Secret.md) data type stores the secret content returned by [HttpService:GetSecret()](/docs/reference/engine/classes/HttpService.md). It cannot be printed or logged, but can be modified using built-in functions: ```lua local HttpService = game:GetService("HttpService") local mySiteApiKey = HttpService:GetSecret("my_site") local url = "https://apis.mysite.com?apiKey=" local urlWithKey = mySiteApiKey:AddPrefix(url) local params = "&request=join&user=myname" local resultingUrl = urlWithKey:AddSuffix(params) ``` ## Methods ### Secret:AddPrefix **Signature:** `Secret:AddPrefix(prefix: string): Secret` Returns a secret formed by concatenating the supplied string to the secret content, for example prepending `"Bearer"` to the API key. ```lua local HttpService = game:GetService("HttpService") local secret = HttpService:GetSecret("yelp") local authHeader = secret:AddPrefix("Bearer ") ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `prefix` | `string` | | | **Returns:** `Secret` ### Secret:AddSuffix **Signature:** `Secret:AddSuffix(suffix: string): Secret` Returns a secret formed by concatenating the original secret and the supplied string parameter. Useful when creating a URL containing a key and query parameters. ```lua local HttpService = game:GetService("HttpService") local googleMapsApiKey = HttpService:GetSecret("google_map") local baseUrl = "https://maps.googleapis.com/maps/api/distancematrix/json?key=" local queryParams = "&destinations=" .. destination .. "&origins=" .. origin .. "&departure_time=now&units=imperial" local authedUrl = googleMapsApiKey:AddPrefix(baseUrl) local queryUrl = authedUrl:AddSuffix(queryParams) ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `suffix` | `string` | | | **Returns:** `Secret` --- name: SecurityCapabilities last_updated: 2026-06-29T19:34:08Z type: datatype summary: "A set of SecurityCapability items. See [script capabilities](/docs/en-us/scripting/capabilities.md)." --- # SecurityCapabilities A set of [SecurityCapability](/docs/reference/engine/enums/SecurityCapability.md) items. See [script capabilities](/docs/en-us/scripting/capabilities.md). **Type:** datatype ## Constructors ### SecurityCapabilities.new **Signature:** `SecurityCapabilities.new(...: SecurityCapability)` Returns a new set of capabilities from zero or more [SecurityCapability](/docs/reference/engine/enums/SecurityCapability.md) items. ```lua local capabilities = SecurityCapabilities.new(Enum.SecurityCapability.Players, Enum.SecurityCapability.Animation, Enum.SecurityCapability.Basic) local newScript = Instance.new("Script") newScript.Capabilities = capabilities ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `...` | `SecurityCapability` | | | ### SecurityCapabilities.fromCurrent **Signature:** `SecurityCapabilities.fromCurrent()` Returns a new set of capabilities from the capabilities of the calling function. ## Methods ### SecurityCapabilities:Add **Signature:** `SecurityCapabilities:Add(...: SecurityCapability): SecurityCapabilities` Returns a new set of capabilities with zero or more additions. If you add [SecurityCapability](/docs/reference/engine/enums/SecurityCapability.md) items that are already present, they are not duplicated. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `...` | `SecurityCapability` | | | **Returns:** `SecurityCapabilities` ### SecurityCapabilities:Add **Signature:** `SecurityCapabilities:Add(capabilities: SecurityCapabilities): SecurityCapabilities` Returns a new set of capabilities with the provided set added. If you add [SecurityCapability](/docs/reference/engine/enums/SecurityCapability.md) items that are already present, they are not duplicated. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `capabilities` | `SecurityCapabilities` | | | **Returns:** `SecurityCapabilities` ### SecurityCapabilities:Remove **Signature:** `SecurityCapabilities:Remove(...: SecurityCapability): SecurityCapabilities` Returns a new set of capabilities with zero or more deletions. [SecurityCapability](/docs/reference/engine/enums/SecurityCapability.md) items not present in the original set are ignored. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `...` | `SecurityCapability` | | | **Returns:** `SecurityCapabilities` ### SecurityCapabilities:Remove **Signature:** `SecurityCapabilities:Remove(capabilities: SecurityCapabilities): SecurityCapabilities` Returns a new set of capabilities with the provided set deleted. [SecurityCapability](/docs/reference/engine/enums/SecurityCapability.md) items not present in the original set are ignored. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `capabilities` | `SecurityCapabilities` | | | **Returns:** `SecurityCapabilities` ### SecurityCapabilities:Contains **Signature:** `SecurityCapabilities:Contains(...: SecurityCapability): boolean` Returns whether a set of capabilities contains the specified [SecurityCapability](/docs/reference/engine/enums/SecurityCapability.md) items. The set must contain **all** provided items to return true. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `...` | `SecurityCapability` | | | **Returns:** `boolean` ### SecurityCapabilities:Contains **Signature:** `SecurityCapabilities:Contains(capabilities: SecurityCapabilities): boolean` Returns whether a set of capabilities contains another set of capabilities. The set must contain **all** items in the provided set to return true. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `capabilities` | `SecurityCapabilities` | | | **Returns:** `boolean` --- name: SharedTable last_updated: 2026-06-29T19:34:08Z type: datatype summary: "Provides sharable, table-like storage for key/value pairs." --- # SharedTable Provides sharable, table-like storage for key/value pairs. **Type:** datatype ## Description Represents a table-like data structure that can be shared across execution contexts. While it can be used for various sorts of general data storage, it is designed especially for use with [Parallel Luau](/docs/en-us/scripting/multithreading.md), where it can be used to share state across scripts parented under different [Actor](/docs/reference/engine/classes/Actor.md) instances. There are a couple idiomatic ways to communicate shared tables between scripts. One method is to store and retrieve [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) objects in the [SharedTableRegistry](/docs/reference/engine/classes/SharedTableRegistry.md). The registry lets any script in the same data model get or set a [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) by name. Another method is to use [Actor:SendMessage()](/docs/reference/engine/classes/Actor.md) to send a shared table to another [Actor](/docs/reference/engine/classes/Actor.md) inside a message. Like a Luau table, a [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) object stores a set of key-value element pairs. Unlike a Luau table, only selected types of objects may be stored in a SharedTable, similar to other restrictions you'll find elsewhere in the Roblox Engine. Keys must either be (1) a string or (2) a nonnegative integer number less than 232. Other kinds of keys are not supported. Values must have one of the following types: Boolean, Number, Vector, String, [SharedTable](/docs/reference/engine/datatypes/SharedTable.md), or a serializable data type. The ability to store a [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) as a value in another [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) permits the construction of nested and recursive data structures. [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) objects are distinct and different [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) objects never compare equal, even if they have contents that would compare equal. Like a Luau table, a [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) object may be frozen, in which case it is read-only. An attempt to modify a frozen [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) will raise an error. A frozen [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) can be created by first creating a (non-frozen, modifiable) [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) with the desired contents, and then calling [SharedTable.cloneAndFreeze()](/docs/reference/engine/datatypes/SharedTable.md) to create a frozen clone of it. ## Code Samples **Element Access** Elements in a [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) are accessed in the same way as elements of Luau tables, using the `st[k]` syntax or, for string keys, `st.k`. ```lua local st = SharedTable.new() st[1] = "a" st["x"] = true st.y = 5 assert(st[1] == "a") assert(st["x"] == true) assert(st.x == true) assert(st["y"] == 5) assert(st.y == 5) -- true is not a valid SharedTable key, so attempting to set that key -- fails: assert(not pcall(function() st[true] = 100 end)) -- A function is not a valid SharedTable value, so attempting to set a -- value to a function fails: assert(not pcall(function() st["f"] = function() end end)) ``` **Element Iteration** The `for` loop can be used to iterate over the elements of a [SharedTable](/docs/reference/engine/datatypes/SharedTable.md). The elements of the [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) are not iterated directly. Instead, a shallow clone is made of the [SharedTable](/docs/reference/engine/datatypes/SharedTable.md), and its elements are iterated. This is done to ensure that a consistent view of the [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) is maintained throughout the iteration. Thus, both of the following for loops have the same behavior. Note that this means that if the [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) is accessed directly from within the body of the iteration loop, its state may not be consistent with the state observed through the iteration. The iteration order is partially specified. Elements with numeric keys are iterated before elements with string keys. Elements with numeric keys are iterated in ascending numeric order. Elements with string keys are iterated in an unspecified order. ```lua local st = SharedTable.new({"a", "b", "c"}) for k, v in SharedTable.clone(st, false) do print(k, ": ", v) end for k, v in st do print(k, ": ", v) end ``` ## Constructors ### SharedTable.new **Signature:** `SharedTable.new()` Returns a new, empty [SharedTable](/docs/reference/engine/datatypes/SharedTable.md). ```lua local st = SharedTable.new() ``` ### SharedTable.new **Signature:** `SharedTable.new(t: table)` Returns a new [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) containing elements equivalent to those in the provided Luau table. If the provided Luau table contains any keys or values that cannot be stored in a [SharedTable](/docs/reference/engine/datatypes/SharedTable.md), construction of the [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) fails. See the summary at the top of this page for a list of types of objects that can be stored in a [SharedTable](/docs/reference/engine/datatypes/SharedTable.md). If the Luau table contains any table as a value, that table is converted into a new [SharedTable](/docs/reference/engine/datatypes/SharedTable.md). ```lua local t = {} t.x = 1 t.y = 2 t.z = {"a", "b", "c"} local st = SharedTable.new(t) assert(st.x == 1) assert(st.y == 2) assert(st.z[1] == "a") assert(st.z[2] == "b") assert(st.z[3] == "c") ``` Note that in some cases it may be desirable to store a [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) in the [SharedTableRegistry](/docs/reference/engine/classes/SharedTableRegistry.md). The [ShareTableRegistry:GetSharedTable()](/docs/reference/engine/classes/ShareTableRegistry.md) method provides a convenient way to accomplish this. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `t` | `table` | | The Luau table whose elements are to be stored in the new [SharedTable](/docs/reference/engine/datatypes/SharedTable.md). | ## Functions ### SharedTable.clear **Signature:** `SharedTable.clear(st: SharedTable): ()` Atomically removes all of the elements from a [SharedTable](/docs/reference/engine/datatypes/SharedTable.md). If the [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) is frozen, the operation fails and an error will be raised. ```lua local st = SharedTable.new({"a", "b", "c"}) assert(SharedTable.size(st) == 3) SharedTable.clear(st) assert(SharedTable.size(st) == 0) ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `st` | `SharedTable` | | The [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) to clear. | **Returns:** `()` ### SharedTable.clone **Signature:** `SharedTable.clone(st: SharedTable, deep?: boolean?): SharedTable` Creates a clone of a [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) and returns the clone. If the optional `deep` argument is not present, or if it is present and its value is `false`, then a shallow clone is created. A shallow clone copies only the top-level [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) object. If any value in the [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) itself is a [SharedTable](/docs/reference/engine/datatypes/SharedTable.md), then both the original [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) and the clone [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) will refer to the same [SharedTable](/docs/reference/engine/datatypes/SharedTable.md). The shallow clone operation is atomic, so the clone [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) will contain a consistent snapshot of the state in the original [SharedTable](/docs/reference/engine/datatypes/SharedTable.md), even if it is being modified concurrently from other scripts. If the optional `deep` argument is present and its value is `true`, then a deep clone is created. A deep clone recursively copies a structure of [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) objects, such that there is no state shared between the original [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) and the clone. The clone of each [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) within the graph of [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) objects is atomic, but the deep clone as a whole is not atomic. Thus, the clone of each [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) within the graph will contain a consistent snapshot of the state of the original [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) object from which it was cloned, but the states of different [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) objects may be inconsistent if the graph is being modified concurrently from other scripts. The [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) object(s) being cloned may be frozen (read-only) or not. Regardless, the newly created clones are _not_ frozen (and are thus modifiable). To create frozen clones, use the [SharedTable.cloneAndFreeze](/docs/reference/engine/datatypes/SharedTable.md) function. To illustrate the difference between a shallow clone and a deep clone, consider the following samples. This first sample creates a shallow clone and the second creates a deep clone. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `st` | `SharedTable` | | The [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) object to clone. | | `deep` | `boolean?` | `false` | Whether to create a deep clone (`true`) or a shallow clone (`false`). | **Returns:** `SharedTable` **Shallow Clone** This code sample creates a shallow clone of a [SharedTable](/docs/reference/engine/datatypes/SharedTable.md). ```lua local original = SharedTable.new() original["a"] = "original a" original["b"] = "original b" original["c"] = SharedTable.new() original["c"]["d"] = "original d" local clone = SharedTable.clone(original, false) clone["a"] = "new a" clone["b"] = "new b" clone["c"]["d"] = "new d" assert(original["a"] == "original a") assert(original["b"] == "original b") -- Because this was a shallow clone, original["c"] and clone["c"] are -- the same SharedTable object. assert(original["c"] == clone["c"]) assert(original["c"]["d"] == "new d") ``` **Deep Clone** This code sample creates a deep clone of a [SharedTable](/docs/reference/engine/datatypes/SharedTable.md). ```lua local original = SharedTable.new() original["a"] = "original a" original["b"] = "original b" original["c"] = SharedTable.new() original["c"]["d"] = "original d" local clone = SharedTable.clone(original, true) clone["a"] = "new a" clone["b"] = "new b" clone["c"]["d"] = "new d" assert(original["a"] == "original a") assert(original["b"] == "original b") -- Because this was a deep clone, clone["c"] is a clone of original["c"]; -- they are distinct SharedTable objects. assert(original["c"] ~= clone["c"]) assert(original["c"]["d"] == "original d") ``` ### SharedTable.cloneAndFreeze **Signature:** `SharedTable.cloneAndFreeze(st: SharedTable, deep?: boolean?): SharedTable` Creates a frozen (read-only) clone of a [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) and returns the clone. The behavior of this function is the same as the behavior of clone, except that the clone is frozen. If a deep clone is requested, then all of the cloned [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) objects are frozen. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `st` | `SharedTable` | | The SharedTable object to clone. | | `deep` | `boolean?` | `false` | Whether to create a deep clone (`true`) or a shallow clone (`false`). | **Returns:** `SharedTable` ### SharedTable.increment **Signature:** `SharedTable.increment(st: SharedTable, key: string | number, delta: number): number` Atomically increments the value of an element. An element with the specified key must exist in the [SharedTable](/docs/reference/engine/datatypes/SharedTable.md), and it must be of type `number`. The specified `delta` is added to the value, and the original value is returned. The `SharedTable.update` function can also be used for this purpose; this `increment` function exists for convenience and performance (in general, `increment` is much faster than `update`, so it should be preferred where possible). The following two function calls have the same effect: ```lua local st = SharedTable.new() st["x"] = 1 local oldValue = SharedTable.increment(st, "x", 1) SharedTable.update(st, "x", function(v) oldValue = v return v + 1 end) ``` If the [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) is frozen, the operation fails and an error will be raised. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `st` | `SharedTable` | | The [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) object to be updated. | | `key` | `string | number` | | The key of the element in the [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) object to be updated. | | `delta` | `number` | | The value to be added to the element in the [SharedTable](/docs/reference/engine/datatypes/SharedTable.md). | **Returns:** `number` — The original value of the element, before `delta` was added to it. **Increment** This code sample demonstrates usage of [SharedTable.increment()](/docs/reference/engine/datatypes/SharedTable.md). ```lua local st = SharedTable.new() st["x"] = 1 local oldValue = SharedTable.increment(st, "x", 1) assert(oldValue == 1) assert(st["x"] == 2) -- The value of the specified key must be a number. If it is not a -- number, the call will fail: st["y"] = "test" assert(not pcall(function() SharedTable.increment(st, "y", 1) end)) ``` ### SharedTable.isFrozen **Signature:** `SharedTable.isFrozen(st: SharedTable): boolean` Returns `true` if the [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) is frozen (read-only). ```lua local st1 = SharedTable.new({"a", "b", "c"}) assert(not SharedTable.isFrozen(st1)) local st2 = SharedTable.cloneAndFreeze(st1) assert(SharedTable.isFrozen(st2)) ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `st` | `SharedTable` | | The [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) object whose frozen state is to be queried. | **Returns:** `boolean` ### SharedTable.size **Signature:** `SharedTable.size(st: SharedTable): number` Returns the number of elements stored in the SharedTable. Note that if other scripts are concurrently modifying the SharedTable, the returned size may no longer be correct after it is returned, since other scripts may have added or removed elements from the SharedTable. ```lua local st = SharedTable.new({"a", "b", "c"}) assert(SharedTable.size(st) == 3) st[2] = nil assert(SharedTable.size(st) == 2) ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `st` | `SharedTable` | | The [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) object whose size is to be queried. | **Returns:** `number` ### SharedTable.update **Signature:** `SharedTable.update(st: SharedTable, key: string | number, f: function): ()` Atomically updates the value of an element. When a [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) is accessed concurrently from scripts running in different execution contexts, it is possible for their accesses to interleave unpredictably. Because of this, code like the following is generally incorrect, because the value may have changed between the read on the first line and the update on the second line: ```lua local oldValue = st["x"] st["x"] = oldValue .. ",x" ``` The update function makes it possible to perform an atomic update to an element. It takes a function that it will call with the current value of the element. The function can then compute and return the new value. Note that the function may be called multiple times if the [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) is being concurrently modified from other scripts. If the [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) is frozen, the operation fails and an error will be raised. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `st` | `SharedTable` | | The [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) object to be updated. | | `key` | `string | number` | | The key of the element in the [SharedTable](/docs/reference/engine/datatypes/SharedTable.md) object to be updated. | | `f` | `function` | | The function that will be called to compute the new value for the element. | **Returns:** `()` **Update** This code sample demonstrates usage of [SharedTable.update()](/docs/reference/engine/datatypes/SharedTable.md). ```lua local st = SharedTable.new() st["x"] = "abcd" SharedTable.update(st, "x", function(v) assert(v == "abcd") return v .. "e" end) assert(st["x"] == "abcde") ``` --- name: TweenInfo last_updated: 2026-06-29T19:34:08Z type: datatype summary: "Stores parameters for Tweens." --- # TweenInfo Stores parameters for [Tweens](/docs/reference/engine/classes/Tween.md). **Type:** datatype ## Description The [TweenInfo](/docs/reference/engine/datatypes/TweenInfo.md) data type stores parameters for [TweenService:Create()](/docs/reference/engine/classes/TweenService.md) to specify the behavior of the tween. The properties of a [TweenInfo](/docs/reference/engine/datatypes/TweenInfo.md) cannot be written to after its creation. ## Constructors ### TweenInfo.new **Signature:** `TweenInfo.new(time?: number, easingStyle?: EasingStyle, easingDirection?: EasingDirection, repeatCount?: number, reverses?: bool, delayTime?: number)` Creates a new [TweenInfo](/docs/reference/engine/datatypes/TweenInfo.md) from the provided parameters. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `time` | `number` | `1` | Duration for the tween, in seconds. | | `easingStyle` | `EasingStyle` | `Enum.EasingStyle.Quad` | Easing style for the tween. | | `easingDirection` | `EasingDirection` | `Enum.EasingDirection.Out` | The direction in which the tween executes. | | `repeatCount` | `number` | `0` | Number of times the tween should repeat. `-1` repeats indefinitely. | | `reverses` | `bool` | `false` | Whether the tween should reverse to the starting values once it reaches its targets. | | `delayTime` | `number` | `0` | Time of delay until the tween begins, in seconds. | ## Properties ### TweenInfo.EasingDirection **Type:** `EasingDirection` The direction in which the tween executes. ### TweenInfo.Time **Type:** `number` Duration of the tween, in seconds. ### TweenInfo.DelayTime **Type:** `number` Time of delay until the tween begins, in seconds. ### TweenInfo.RepeatCount **Type:** `number` Number of times the tween repeats. `-1` indicates indefinite repetition. ### TweenInfo.EasingStyle **Type:** `EasingStyle` The style in which the tween executes. ### TweenInfo.Reverses **Type:** `bool` Whether or not the tween interpolates in reverse tween once the initial tween completes. --- name: UDim last_updated: 2026-06-29T19:34:08Z type: datatype summary: "Represents a one-dimensional value with two components, a relative scale and an absolute offset." --- # UDim Represents a one-dimensional value with two components, a relative scale and an absolute offset. **Type:** datatype ## Description The [UDim](/docs/reference/engine/datatypes/UDim.md) data type represents a one-dimensional value with two components, a relative scale and an absolute offset in pixels. ## Constructors ### UDim.new **Signature:** `UDim.new(Scale: number, Offset: number)` Returns a [UDim](/docs/reference/engine/datatypes/UDim.md) from the given components. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `Scale` | `number` | | | | `Offset` | `number` | | | ## Properties ### UDim.Scale **Type:** `number` The relative scale component of the [UDim](/docs/reference/engine/datatypes/UDim.md). ### UDim.Offset **Type:** `number` The absolute offset component of the [UDim](/docs/reference/engine/datatypes/UDim.md) in pixels. ## Math Operations | Operation | Type A | Type B | Returns | Description | |-----------|--------|--------|---------|-------------| | `+` | `UDim` | `UDim` | `UDim` | Produces a [UDim](/docs/reference/engine/datatypes/UDim.md) representing the sum of the two [UDim](/docs/reference/engine/datatypes/UDim.md) values. | | `-` | `UDim` | `UDim` | `UDim` | Produces a [UDim](/docs/reference/engine/datatypes/UDim.md) representing the difference between the two [UDim](/docs/reference/engine/datatypes/UDim.md) values. | --- name: UDim2 last_updated: 2026-06-29T19:34:08Z type: datatype summary: "Represents a two-dimensional value where each dimension is composed of a relative scale and an absolute offset." --- # UDim2 Represents a two-dimensional value where each dimension is composed of a relative scale and an absolute offset. **Type:** datatype ## Description The [UDim2](/docs/reference/engine/datatypes/UDim2.md) data type represents a two-dimensional value where each dimension is composed of a relative scale and an absolute offset in pixels. It is a combination of two [UDim](/docs/reference/engine/datatypes/UDim.md) data types representing the **X** and **Y** dimensions. The most common usages for [UDim2](/docs/reference/engine/datatypes/UDim2.md) are setting the [Size](/docs/reference/engine/classes/GuiObject.md) and [Position](/docs/reference/engine/classes/GuiObject.md) of [GuiObjects](/docs/reference/engine/classes/GuiObject.md). ```lua local guiObject = script.Parent guiObject.Size = UDim2.new(0, 300, 1, 0) -- 300 pixels wide; full height of parent guiObject.Position = UDim2.new(0, 50, 0, 0) -- 50 pixels from the left ``` ## Constructors ### UDim2.new **Signature:** `UDim2.new()` Returns a new [UDim2](/docs/reference/engine/datatypes/UDim2.md) with the coordinates of two zero [UDim](/docs/reference/engine/datatypes/UDim.md) components representing each axis. ### UDim2.new **Signature:** `UDim2.new(xScale?: number, xOffset?: number, yScale?: number, yOffset?: number)` Returns a new [UDim2](/docs/reference/engine/datatypes/UDim2.md) given the coordinates of the two [UDim](/docs/reference/engine/datatypes/UDim.md) components representing each axis. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `xScale` | `number` | `0` | The **X** dimension scale. | | `xOffset` | `number` | `0` | The **X** dimension offset. | | `yScale` | `number` | `0` | The **Y** dimension scale. | | `yOffset` | `number` | `0` | The **Y** dimension offset. | ### UDim2.new **Signature:** `UDim2.new(x: UDim, y: UDim)` Returns a new [UDim2](/docs/reference/engine/datatypes/UDim2.md) from the given [UDim](/docs/reference/engine/datatypes/UDim.md) objects representing the **X** and **Y** dimensions, respectively. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `UDim` | | | | `y` | `UDim` | | | ### UDim2.fromScale **Signature:** `UDim2.fromScale(xScale?: number, yScale?: number)` Returns a new [UDim2](/docs/reference/engine/datatypes/UDim2.md) with the given scalar coordinates and no offsets. Equivalent to: ```lua UDim2.fromScale(xScale, yScale) == UDim2.new(xScale, 0, yScale, 0) ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `xScale` | `number` | `0` | | | `yScale` | `number` | `0` | | ### UDim2.fromOffset **Signature:** `UDim2.fromOffset(xOffset?: number, yOffset?: number)` Returns a new [UDim2](/docs/reference/engine/datatypes/UDim2.md) with the given offset coordinates and no scaling. Equivalent to: ```lua UDim2.fromOffset(xOffset, yOffset) == UDim2.new(0, xOffset, 0, yOffset) ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `xOffset` | `number` | `0` | | | `yOffset` | `number` | `0` | | ## Properties ### UDim2.X **Type:** `UDim` The **X** dimension scale and offset of the [UDim2](/docs/reference/engine/datatypes/UDim2.md). ### UDim2.Y **Type:** `UDim` The **Y** dimension scale and offset of the [UDim2](/docs/reference/engine/datatypes/UDim2.md). ### UDim2.Width **Type:** `UDim` The **X** dimension scale and offset of the [UDim2](/docs/reference/engine/datatypes/UDim2.md). ### UDim2.Height **Type:** `UDim` The **Y** dimension scale and offset of the [UDim2](/docs/reference/engine/datatypes/UDim2.md). ## Methods ### UDim2:Lerp **Signature:** `UDim2:Lerp(goal: UDim2, alpha: number): UDim2` Returns a [UDim2](/docs/reference/engine/datatypes/UDim2.md) interpolated linearly between this [UDim2](/docs/reference/engine/datatypes/UDim2.md) and the given `goal`. The `alpha` value should be a number between `0` and `1`. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `goal` | `UDim2` | | | | `alpha` | `number` | | | **Returns:** `UDim2` ## Math Operations | Operation | Type A | Type B | Returns | Description | |-----------|--------|--------|---------|-------------| | `+` | `UDim2` | `UDim2` | `UDim2` | Produces a [UDim2](/docs/reference/engine/datatypes/UDim2.md) with components that are the sum of the respective components of the two [UDim2](/docs/reference/engine/datatypes/UDim2.md) objects. | | `-` | `UDim2` | `UDim2` | `UDim2` | Produces a [UDim2](/docs/reference/engine/datatypes/UDim2.md) with components that are the difference of the respective components of the two [UDim2](/docs/reference/engine/datatypes/UDim2.md) objects. | --- name: User last_updated: 2026-06-29T19:34:08Z type: datatype summary: "Represents a user's domain-scoped identity within a specific domain." --- # User Represents a user's domain-scoped identity within a specific domain. **Type:** datatype ## Description The **User** data type represents a user's scoped identity within a specific domain, such as an experience or OAuth application. It encapsulates a domain user ID alongside the domain metadata that uniquely identifies that user within that domain. Unlike a numeric user ID alone, a [User](/docs/reference/engine/datatypes/User.md) value carries its domain context, making it unambiguous across experiences and apps. It is the standard identifier for referencing users in new experience code and is available from [Player.User](/docs/reference/engine/classes/Player.md). #### Domain User IDs User IDs on Roblox are **domain-scoped**: a single account has a unique domain user ID for each experience or app it interacts with. This ensures that user activity in one experience cannot be correlated with activity in another by a third party using the user ID alone. A domain user ID is an integer that is unique within a given domain (identified by [DomainType](/docs/reference/engine/enums/DomainType.md) and domain ID). Domain user IDs are guaranteed not to collide with existing global user IDs. #### Serialization [User](/docs/reference/engine/datatypes/User.md) supports round-trip string serialization via [ToString()](/docs/reference/engine/datatypes/User.md) and [fromString()](/docs/reference/engine/datatypes/User.md). A [User](/docs/reference/engine/datatypes/User.md) serialized with `ToString()` and deserialized with `fromString()` produces an identical value. The encoded string is stable, URL-safe, and compact. Use it when persisting or transmitting user identity across systems. ```lua -- Serialize local encoded = player.User:ToString() -- Deserialize local user = User.fromString(encoded) ``` #### Users vs. Players A [User](/docs/reference/engine/datatypes/User.md) represents persistent identity, not live server presence. It can reference any account in the domain, whether or not that user is currently connected. Use [Player](/docs/reference/engine/classes/Player.md) when you need the in-server instance. ## Constructors ### User.fromId **Signature:** `User.fromId(domainUserId: int64)` Creates a new [User](/docs/reference/engine/datatypes/User.md) from the given domain user ID, using the current experience as the domain. The ID must be a valid domain user ID (above the global user ID range). ```lua local user = User.fromId(domainUserId) print(user.Id) -- domainUserId print(user.DomainType) -- Enum.DomainType.EXPERIENCE ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `domainUserId` | `int64` | | | ### User.fromString **Signature:** `User.fromString(userString: string)` Deserializes a [User](/docs/reference/engine/datatypes/User.md) from a string previously produced by [ToString()](/docs/reference/engine/datatypes/User.md). ```lua local encoded = player.User:ToString() local restored = User.fromString(encoded) print(restored.Id == player.User.Id) -- true ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `userString` | `string` | | | ## Properties ### User.Id **Type:** `int64` The domain user ID for this user within the associated domain. This is an integer that uniquely identifies the user within the specific domain type and domain ID combination. ### User.DomainType **Type:** `DomainType` The [DomainType](/docs/reference/engine/enums/DomainType.md) that identifies the kind of domain this domain user ID belongs to. Currently supported values are [DomainType.EXPERIENCE](/docs/reference/engine/enums/DomainType.md) and [DomainType.OAUTH](/docs/reference/engine/enums/DomainType.md). ### User.DomainId **Type:** `int64` The identifier of the specific domain instance that this user ID is scoped to. For [DomainType.EXPERIENCE](/docs/reference/engine/enums/DomainType.md), this is the universe ID. For [DomainType.OAUTH](/docs/reference/engine/enums/DomainType.md), this is the application ID. ## Methods ### User:ToString **Signature:** `User:ToString(): string` Serializes this [User](/docs/reference/engine/datatypes/User.md) into a string that encodes the domain type, domain ID, and domain user ID. The result is stable, URL-safe, and compact. Use this when storing or transmitting user identity. The string can be converted back to a [User](/docs/reference/engine/datatypes/User.md) via [fromString()](/docs/reference/engine/datatypes/User.md). ```lua local encoded = player.User:ToString() -- Store in a data store, send over a remote, etc. DataStore:SetAsync("last_visitor", encoded) ``` **Returns:** `string` --- name: ValueCurveKey last_updated: 2026-06-29T19:34:08Z type: datatype summary: "A time-value pair used with ValueCurve instances." --- # ValueCurveKey A time-value pair used with [ValueCurve](/docs/reference/engine/classes/ValueCurve.md) instances. **Type:** datatype ## Description A time-value pair used with [ValueCurve](/docs/reference/engine/classes/ValueCurve.md) instances. The [Interpolation](/docs/reference/engine/datatypes/ValueCurveKey.md) property dictates the interpolation mode for the segment started by this key and ended by the next key on the curve. Each segment may use a different interpolation mode. The [LeftTangent](/docs/reference/engine/datatypes/ValueCurveKey.md) and [RightTangent](/docs/reference/engine/datatypes/ValueCurveKey.md) properties apply to the cubic interpolation mode and define the desired tangent (slope) at the key. Different left and right values can be used to encode discontinuities in slope at the key. Attempting to set a [RightTangent](/docs/reference/engine/datatypes/ValueCurveKey.md) value on a key that doesn't use the cubic interpolation mode will result in a runtime error. It is possible to set the [LeftTangent](/docs/reference/engine/datatypes/ValueCurveKey.md) property on any key, as it will be used should the preceding segment use cubic interpolation. ## Constructors ### ValueCurveKey.new **Signature:** `ValueCurveKey.new(time: number, value: any, Interpolation: KeyInterpolationMode)` Creates a new [ValueCurveKey](/docs/reference/engine/datatypes/ValueCurveKey.md) at a given time and value. [LeftTangent](/docs/reference/engine/datatypes/ValueCurveKey.md) and [RightTangent](/docs/reference/engine/datatypes/ValueCurveKey.md) are left uninitialized and, if not initialized, tangent values of '0' will be used when evaluating the curve. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `time` | `number` | | Time at which to create the new [ValueCurveKey](/docs/reference/engine/datatypes/ValueCurveKey.md). | | `value` | `any` | | Value of the new [ValueCurveKey](/docs/reference/engine/datatypes/ValueCurveKey.md). | | `Interpolation` | `KeyInterpolationMode` | | | ## Properties ### ValueCurveKey.Interpolation **Type:** `KeyInterpolationMode` Defines this key's interpolation mode for the segment started by this [ValueCurveKey](/docs/reference/engine/datatypes/ValueCurveKey.md). ### ValueCurveKey.Time **Type:** `number` The time position of this [ValueCurveKey](/docs/reference/engine/datatypes/ValueCurveKey.md). ### ValueCurveKey.Value **Type:** `any` The value of this [ValueCurveKey](/docs/reference/engine/datatypes/ValueCurveKey.md). ### ValueCurveKey.RightTangent **Type:** `number` The tangent to the right of this [ValueCurveKey](/docs/reference/engine/datatypes/ValueCurveKey.md). ### ValueCurveKey.LeftTangent **Type:** `number` The tangent to the left of this [ValueCurveKey](/docs/reference/engine/datatypes/ValueCurveKey.md). --- name: Vector2 last_updated: 2026-06-29T19:34:08Z type: datatype summary: "Represents a 2D value with direction and magnitude." --- # Vector2 Represents a 2D value with direction and magnitude. **Type:** datatype ## Description The [Vector2](/docs/reference/engine/datatypes/Vector2.md) data type represents a 2D value with direction and magnitude. Some applications include GUI elements and 2D mouse positions. #### Math Operations The following math operations are valid for the [Vector2](/docs/reference/engine/datatypes/Vector2.md) data type: | Operation | Description | | --- | --- | | [Vector2](/docs/reference/engine/datatypes/Vector2.md) `+` [Vector2](/docs/reference/engine/datatypes/Vector2.md) | Produces a [Vector2](/docs/reference/engine/datatypes/Vector2.md) with each component of the second added to the corresponding component of the first. | | [Vector2](/docs/reference/engine/datatypes/Vector2.md) `-` [Vector2](/docs/reference/engine/datatypes/Vector2.md) | Produces a [Vector2](/docs/reference/engine/datatypes/Vector2.md) with each component of the second subtracted from the corresponding component of the first. | | [Vector2](/docs/reference/engine/datatypes/Vector2.md) `*` [Vector2](/docs/reference/engine/datatypes/Vector2.md) | Produces a [Vector2](/docs/reference/engine/datatypes/Vector2.md) with each component of the second multiplied by the corresponding component of the first. | | [Vector2](/docs/reference/engine/datatypes/Vector2.md) `/` [Vector2](/docs/reference/engine/datatypes/Vector2.md) | Produces a [Vector2](/docs/reference/engine/datatypes/Vector2.md) with each component of the first divided by the corresponding component of the second. | | [Vector2](/docs/reference/engine/datatypes/Vector2.md) `*` `number` | Produces a [Vector2](/docs/reference/engine/datatypes/Vector2.md) with each component multiplied by the number. | | [Vector2](/docs/reference/engine/datatypes/Vector2.md) `/` `number` | Produces a [Vector2](/docs/reference/engine/datatypes/Vector2.md) with each component divided by the number. | ## Constructors ### Vector2.new **Signature:** `Vector2.new(x: number, y: number)` Returns a [Vector2](/docs/reference/engine/datatypes/Vector2.md) from the given x and y components. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `number` | | | | `y` | `number` | | | ## Constants | Name | Type | Description | |------|------|-------------| | `Vector2.zero` | `Vector2` | A [Vector2](/docs/reference/engine/datatypes/Vector2.md) with a magnitude of zero. | | `Vector2.one` | `Vector2` | A [Vector2](/docs/reference/engine/datatypes/Vector2.md) with a value of 1 on every axis. | | `Vector2.xAxis` | `Vector2` | A [Vector2](/docs/reference/engine/datatypes/Vector2.md) with a value of 1 on the X axis. | | `Vector2.yAxis` | `Vector2` | A [Vector2](/docs/reference/engine/datatypes/Vector2.md) with a value of 1 on the Y axis. | ## Properties ### Vector2.X **Type:** `number` The x-coordinate of the [Vector2](/docs/reference/engine/datatypes/Vector2.md). ### Vector2.Y **Type:** `number` The y-coordinate of the [Vector2](/docs/reference/engine/datatypes/Vector2.md). ### Vector2.Magnitude **Type:** `number` The length of the [Vector2](/docs/reference/engine/datatypes/Vector2.md). ### Vector2.Unit **Type:** `Vector2` A normalized copy of the [Vector2](/docs/reference/engine/datatypes/Vector2.md). ## Methods ### Vector2:Cross **Signature:** `Vector2:Cross(other: Vector2): number` Returns the cross product of the two vectors. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `other` | `Vector2` | | | **Returns:** `number` ### Vector2:Abs **Signature:** `Vector2:Abs(): Vector2` Returns a new vector from the absolute values of the original's components. For example, a vector of `(-2, 4)` returns a vector of `(2, 4)`. **Returns:** `Vector2` ### Vector2:Ceil **Signature:** `Vector2:Ceil(): Vector2` Returns a new vector from the ceiling of the original's components. For example, a vector of `(-2.6, 5.1)` returns a vector of `(-2, 6)`. **Returns:** `Vector2` ### Vector2:Floor **Signature:** `Vector2:Floor(): Vector2` Returns a new vector from the floor of the original's components. For example, a vector of `(-2.6, 5.1)` returns a vector of `(-3, 5)`. **Returns:** `Vector2` ### Vector2:Sign **Signature:** `Vector2:Sign(): Vector2` Returns a new vector from the sign (-1, 0, or 1) of the original's components. For example, a vector of `(-2.6, 5.1)` returns a vector of `(-1, 1)`. **Returns:** `Vector2` ### Vector2:Angle **Signature:** `Vector2:Angle(other: Vector2, isSigned?: boolean): number` Returns the angle in radians between the two vectors. Specify `true` for the optional `isSigned` boolean if you want a signed angle. By default, the method returns the absolute value. Signed angles are a negative when going clockwise. Values are in the range `[0, pi]` for absolute angles and `[-pi, pi]` for signed angles. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `other` | `Vector2` | | | | `isSigned` | `boolean` | `false` | | **Returns:** `number` ### Vector2:Dot **Signature:** `Vector2:Dot(v: Vector2): number` Returns a scalar dot product of the two vectors. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `v` | `Vector2` | | | **Returns:** `number` ### Vector2:Lerp **Signature:** `Vector2:Lerp(v: Vector2, alpha: number): Vector2` Returns a [Vector2](/docs/reference/engine/datatypes/Vector2.md) linearly interpolated between this [Vector2](/docs/reference/engine/datatypes/Vector2.md) and the given goal by the given alpha. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `v` | `Vector2` | | | | `alpha` | `number` | | | **Returns:** `Vector2` ### Vector2:Max **Signature:** `Vector2:Max(others...: Tuple): Vector2` Returns a [Vector2](/docs/reference/engine/datatypes/Vector2.md) with each component as the highest among the respective components of the provided [Vector2](/docs/reference/engine/datatypes/Vector2.md) objects. ```lua local a = Vector2.new(1, 2) local b = Vector2.new(2, 1) print(a:Max(b)) -- Vector2.new(2, 2) ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `others...` | `Tuple` | | | **Returns:** `Vector2` ### Vector2:Min **Signature:** `Vector2:Min(others...: Tuple): Vector2` Returns a [Vector2](/docs/reference/engine/datatypes/Vector2.md) with each component as the lowest among the respective components of the provided [Vector2](/docs/reference/engine/datatypes/Vector2.md) objects. ```lua local a = Vector2.new(1, 2) local b = Vector2.new(2, 1) print(a:Min(b)) -- Vector2.new(1, 1) ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `others...` | `Tuple` | | | **Returns:** `Vector2` ### Vector2:FuzzyEq **Signature:** `Vector2:FuzzyEq(other: Vector2, epsilon?: number): bool` Returns `true` if the X and Y components of the other [Vector2](/docs/reference/engine/datatypes/Vector2.md) are within epsilon units of each corresponding component of this [Vector2](/docs/reference/engine/datatypes/Vector2.md). **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `other` | `Vector2` | | | | `epsilon` | `number` | `0.00001 (1e-5)` | | **Returns:** `bool` ## Math Operations | Operation | Type A | Type B | Returns | Description | |-----------|--------|--------|---------|-------------| | `+` | `Vector2` | `Vector2` | `Vector2` | Produces a [Vector2](/docs/reference/engine/datatypes/Vector2.md) with each component of the second added to the corresponding component of the first. | | `-` | `Vector2` | `Vector2` | `Vector2` | Produces a [Vector2](/docs/reference/engine/datatypes/Vector2.md) with each component of the second subtracted from the corresponding component of the first. | | `*` | `Vector2` | `Vector2` | `Vector2` | Produces a [Vector2](/docs/reference/engine/datatypes/Vector2.md) with each component of the second multiplied by the corresponding component of the first. | | `/` | `Vector2` | `Vector2` | `Vector2` | Produces a [Vector2](/docs/reference/engine/datatypes/Vector2.md) with each component of the first divided by the corresponding component of the second. | | `*` | `Vector2` | `number` | `Vector2` | Produces a [Vector2](/docs/reference/engine/datatypes/Vector2.md) with each component multiplied by the number. | | `/` | `Vector2` | `number` | `Vector2` | Produces a [Vector2](/docs/reference/engine/datatypes/Vector2.md) with each component divided by the number. | --- name: Vector2int16 last_updated: 2026-06-29T19:34:08Z type: datatype summary: "Represents a Vector2 with signed 16-bit integers for components." --- # Vector2int16 Represents a Vector2 with signed 16-bit integers for components. **Type:** datatype ## Description The [Vector2int16](/docs/reference/engine/datatypes/Vector2int16.md) data type represents a vector in 2D space with a **signed 16-bit integer** for its components. It is similar to [Vector2](/docs/reference/engine/datatypes/Vector2.md) in that it allows for the same arithmetic operations, but it lacks commonly used vector functions. [Vector2int16](/docs/reference/engine/datatypes/Vector2int16.md) should **not** be confused with: - [Vector2](/docs/reference/engine/datatypes/Vector2.md), a **more precise** and complete implementation for 2D vectors. - [Vector3int16](/docs/reference/engine/datatypes/Vector3int16.md), a similar implementation for 3D vectors. For each component: - The **lower** bound is -215, or **-32,768**. - The **upper** bound is 215 − 1, or **32,767**. #### Converting to Vector2 To convert a [Vector2int16](/docs/reference/engine/datatypes/Vector2int16.md) to a [Vector2](/docs/reference/engine/datatypes/Vector2.md), construct a [Vector2](/docs/reference/engine/datatypes/Vector2.md) by passing each **component** of the [Vector2int16](/docs/reference/engine/datatypes/Vector2int16.md) to [Vector2.new()](/docs/reference/engine/datatypes/Vector2.md): ```lua local vector2int16 = Vector2int16.new(1, 2) local vector2 = Vector2.new(vector2int16.X, vector2int16.Y) print(vector2) --> 1, 2 ``` Do **not** pass an entire [Vector2int16](/docs/reference/engine/datatypes/Vector2int16.md) to [Vector2.new()](/docs/reference/engine/datatypes/Vector2.md), as the constructor interprets a [Vector2int16](/docs/reference/engine/datatypes/Vector2int16.md) as a `0` within its parameters **without producing an error**. This can lead to silent logic errors if you do something like: ```lua local vector2int16 = Vector2int16.new(1, 2) local vector2 = Vector2.new(vector2int16) print(vector2) --> 0, 0 ``` #### Math Operations The following math operations are valid for the [Vector2int16](/docs/reference/engine/datatypes/Vector2int16.md) data type. For all operations, be mindful of the bounds associated with signed 16-bit integers, described earlier. | Operation | Description | | --- | --- | | [Vector2int16](/docs/reference/engine/datatypes/Vector2int16.md) `+` [Vector2int16](/docs/reference/engine/datatypes/Vector2int16.md) | Produces a [Vector2int16](/docs/reference/engine/datatypes/Vector2int16.md) whose components are the sum of the operands' respective components. | | [Vector2int16](/docs/reference/engine/datatypes/Vector2int16.md) `-` [Vector2int16](/docs/reference/engine/datatypes/Vector2int16.md) | Produces a [Vector2int16](/docs/reference/engine/datatypes/Vector2int16.md) whose components are the difference of the operands' respective components. | | [Vector2int16](/docs/reference/engine/datatypes/Vector2int16.md) `*` [Vector2int16](/docs/reference/engine/datatypes/Vector2int16.md) | Produces a [Vector2int16](/docs/reference/engine/datatypes/Vector2int16.md) whose components are the product of the operands' respective components. | | [Vector2int16](/docs/reference/engine/datatypes/Vector2int16.md) `/` [Vector2int16](/docs/reference/engine/datatypes/Vector2int16.md) | Produces a [Vector2int16](/docs/reference/engine/datatypes/Vector2int16.md) whose components are the quotient of the operands' respective components. The results of the division are rounded down. | | [Vector2int16](/docs/reference/engine/datatypes/Vector2int16.md) `*` `number` | Produces a [Vector2int16](/docs/reference/engine/datatypes/Vector2int16.md) whose components are the product of the respective [Vector2int16](/docs/reference/engine/datatypes/Vector2int16.md) components and the number (factor). This operation is commutative. | | [Vector2int16](/docs/reference/engine/datatypes/Vector2int16.md) `/` `number` | Produces a [Vector2int16](/docs/reference/engine/datatypes/Vector2int16.md) whose components are the quotient of the respective [Vector2int16](/docs/reference/engine/datatypes/Vector2int16.md) components and the number (divisor). The results of the division are rounded toward zero. | ## Constructors ### Vector2int16.new **Signature:** `Vector2int16.new(x: number, y: number)` Returns a new [Vector2int16](/docs/reference/engine/datatypes/Vector2int16.md) given the x and y components. Non-integer components are rounded down. The components must fall within the range [-215, 215). If outside this range, integer overflow may occur. For example, providing 32,768 (equal to 215) as a component overflows the 16-bit integer, and so the component is -32,768 (equal to -215) instead. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `number` | | | | `y` | `number` | | | ## Properties ### Vector2int16.X **Type:** `number` The x-coordinate of the [Vector2int16](/docs/reference/engine/datatypes/Vector2int16.md), also accessible in its lower-case variant. ### Vector2int16.Y **Type:** `number` The y-coordinate of the [Vector2int16](/docs/reference/engine/datatypes/Vector2int16.md), also accessible in its lower-case variant. ## Math Operations | Operation | Type A | Type B | Returns | Description | |-----------|--------|--------|---------|-------------| | `+` | `Vector2int16` | `Vector2int16` | `Vector2int16` | Produces a [Vector2int16](/docs/reference/engine/datatypes/Vector2int16.md) whose components are the sum of the operands' respective components. | | `-` | `Vector2int16` | `Vector2int16` | `Vector2int16` | Produces a [Vector2int16](/docs/reference/engine/datatypes/Vector2int16.md) whose components are the difference of the operands' respective components. | | `*` | `Vector2int16` | `Vector2int16` | `Vector2int16` | Produces a [Vector2int16](/docs/reference/engine/datatypes/Vector2int16.md) whose components are the product of the operands' respective components. | | `/` | `Vector2int16` | `Vector2int16` | `Vector2int16` | Produces a [Vector2int16](/docs/reference/engine/datatypes/Vector2int16.md) whose components are the quotient of the operands' respective components. | | `*` | `Vector2int16` | `number` | `Vector2int16` | Produces a [Vector2int16](/docs/reference/engine/datatypes/Vector2int16.md) whose components are the product of the respective [Vector2int16](/docs/reference/engine/datatypes/Vector2int16.md) components and the number (factor). | | `/` | `Vector2int16` | `number` | `Vector2int16` | Produces a [Vector2int16](/docs/reference/engine/datatypes/Vector2int16.md) whose components are the quotient of the respective [Vector2int16](/docs/reference/engine/datatypes/Vector2int16.md) components and the number (divisor). | --- name: Vector3 last_updated: 2026-06-29T19:34:08Z type: datatype summary: "Represents a 3D value with a direction and magnitude." --- # Vector3 Represents a 3D value with a direction and magnitude. **Type:** datatype ## Description The [Vector3](/docs/reference/engine/datatypes/Vector3.md) data type represents a vector in 3D space, typically used as a point in 3D space or the dimensions of a rectangular prism. [Vector3](/docs/reference/engine/datatypes/Vector3.md) supports basic component-based arithmetic operations (sum, difference, product, and quotient) and these operations can be applied on the left or right hand side to either another [Vector3](/docs/reference/engine/datatypes/Vector3.md) or a number. It also features methods for common vector operations, such as [Cross()](/docs/reference/engine/datatypes/Vector3.md) and [Dot()](/docs/reference/engine/datatypes/Vector3.md). Alternatively to [Vector3](/docs/reference/engine/datatypes/Vector3.md), consider using the methods and properties of the [vector](/docs/reference/engine/globals/vector.md) library. Some example usages of [Vector3](/docs/reference/engine/datatypes/Vector3.md) are the [Position](/docs/reference/engine/classes/BasePart.md), [Rotation](/docs/reference/engine/classes/BasePart.md), and [Size](/docs/reference/engine/classes/BasePart.md) of parts, for example: ```lua local Workspace = game:GetService("Workspace") local part = Workspace.Part part.Position = part.Position + Vector3.new(5, 2, 10) -- Move part by (5, 2, 10) ``` [Vector3](/docs/reference/engine/datatypes/Vector3.md) is also commonly used when constructing more complex 3D data types such as [CFrame](/docs/reference/engine/datatypes/CFrame.md). Many of these data types' methods will use a [Vector3](/docs/reference/engine/datatypes/Vector3.md) within their parameters, such as [CFrame:PointToObjectSpace()](/docs/reference/engine/datatypes/CFrame.md). ## Constructors ### Vector3.new **Signature:** `Vector3.new(x?: number, y?: number, z?: number)` Returns a new [Vector3](/docs/reference/engine/datatypes/Vector3.md) using the given `x`, `y`, and `z` components. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `number` | `0` | | | `y` | `number` | `0` | | | `z` | `number` | `0` | | ### Vector3.FromNormalId **Signature:** `Vector3.FromNormalId(normal: NormalId)` Returns a new [Vector3](/docs/reference/engine/datatypes/Vector3.md) in the given direction. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `normal` | `NormalId` | | | ### Vector3.FromAxis **Signature:** `Vector3.FromAxis(axis: Axis)` Returns a new [Vector3](/docs/reference/engine/datatypes/Vector3.md) for the given axis. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `axis` | `Axis` | | | ## Constants | Name | Type | Description | |------|------|-------------| | `Vector3.zero` | `Vector3` | A [Vector3](/docs/reference/engine/datatypes/Vector3.md) with a magnitude of `0`. | | `Vector3.one` | `Vector3` | A [Vector3](/docs/reference/engine/datatypes/Vector3.md) with a value of `1` on every axis. | | `Vector3.xAxis` | `Vector3` | A [Vector3](/docs/reference/engine/datatypes/Vector3.md) with a value of `1` on the **X** axis. | | `Vector3.yAxis` | `Vector3` | A [Vector3](/docs/reference/engine/datatypes/Vector3.md) with a value of `1` on the **Y** axis. | | `Vector3.zAxis` | `Vector3` | A [Vector3](/docs/reference/engine/datatypes/Vector3.md) with a value of `1` on the **Z** axis. | ## Properties ### Vector3.X **Type:** `number` The **X** coordinate of the [Vector3](/docs/reference/engine/datatypes/Vector3.md). ### Vector3.Y **Type:** `number` The **Y** coordinate of the [Vector3](/docs/reference/engine/datatypes/Vector3.md). ### Vector3.Z **Type:** `number` The **Z** coordinate of the [Vector3](/docs/reference/engine/datatypes/Vector3.md). ### Vector3.Magnitude **Type:** `number` The length of the [Vector3](/docs/reference/engine/datatypes/Vector3.md). ### Vector3.Unit **Type:** `Vector3` A normalized copy of the [Vector3](/docs/reference/engine/datatypes/Vector3.md) - one that has the same direction as the original but a magnitude of `1`. ## Methods ### Vector3:Abs **Signature:** `Vector3:Abs(): Vector3` Returns a new vector from the absolute values of the original's components. For example, a vector of `(-2, 4, -6)` returns a vector of `(2, 4, 6)`. **Returns:** `Vector3` ### Vector3:Ceil **Signature:** `Vector3:Ceil(): Vector3` Returns a new vector from the ceiling of the original's components. For example, a vector of `(-2.6, 5.1, 8.8)` returns a vector of `(-2, 6, 9)`. **Returns:** `Vector3` ### Vector3:Floor **Signature:** `Vector3:Floor(): Vector3` Returns a new vector from the floor of the original's components. For example, a vector of `(-2.6, 5.1, 8.8)` returns a vector of `(-3, 5, 8)`. **Returns:** `Vector3` ### Vector3:Sign **Signature:** `Vector3:Sign(): Vector3` Returns a new vector from the sign (-1, 0, or 1) of the original's components. For example, a vector of `(-2.6, 5.1, 0)` returns a vector of `(-1, 1, 0)`. **Returns:** `Vector3` ### Vector3:Cross **Signature:** `Vector3:Cross(other: Vector3): Vector3` Returns the cross product of the two vectors. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `other` | `Vector3` | | | **Returns:** `Vector3` ### Vector3:Angle **Signature:** `Vector3:Angle(other: Vector3, axis?: Vector3): number` Returns the angle in radians between the two vectors. If you provide an axis, it determines the sign of the angle. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `other` | `Vector3` | | | | `axis` | `Vector3` | `nil` | | **Returns:** `number` ### Vector3:Dot **Signature:** `Vector3:Dot(other: Vector3): number` Returns a scalar dot product of the two vectors. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `other` | `Vector3` | | | **Returns:** `number` ### Vector3:FuzzyEq **Signature:** `Vector3:FuzzyEq(other: Vector3, epsilon?: number): bool` Returns `true` if the difference between the squared magnitude of the two vectors is within `epsilon`. `epsilon` is scaled relative to the magnitude, rather than an absolute epsilon. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `other` | `Vector3` | | | | `epsilon` | `number` | `0.00001 aka 1e-5` | | **Returns:** `bool` ### Vector3:Lerp **Signature:** `Vector3:Lerp(goal: Vector3, alpha: number): Vector3` Returns a [Vector3](/docs/reference/engine/datatypes/Vector3.md) linearly interpolated between this [Vector3](/docs/reference/engine/datatypes/Vector3.md) and the given `goal` [Vector3](/docs/reference/engine/datatypes/Vector3.md) by the fraction `alpha`. Note that `alpha` is **not** limited to the range `[0, 1]`. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `goal` | `Vector3` | | | | `alpha` | `number` | | | **Returns:** `Vector3` ### Vector3:Max **Signature:** `Vector3:Max(vector: Vector3): Vector3` Returns a [Vector3](/docs/reference/engine/datatypes/Vector3.md) with each component as the highest among the respective components of both provided [Vector3](/docs/reference/engine/datatypes/Vector3.md) objects. ```lua local a = Vector3.new(1, 2, 1) local b = Vector3.new(2, 1, 2) print(a:Max(b)) --> Vector3.new(2, 2, 2) ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `vector` | `Vector3` | | | **Returns:** `Vector3` ### Vector3:Min **Signature:** `Vector3:Min(vector: Vector3): Vector3` Returns a [Vector3](/docs/reference/engine/datatypes/Vector3.md) with each component as the lowest among the respective components of both provided [Vector3](/docs/reference/engine/datatypes/Vector3.md) objects. ```lua local a = Vector3.new(1, 2, 1) local b = Vector3.new(2, 1, 2) print(a:Min(b)) --> Vector3.new(1, 1, 1) ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `vector` | `Vector3` | | | **Returns:** `Vector3` ## Math Operations | Operation | Type A | Type B | Returns | Description | |-----------|--------|--------|---------|-------------| | `+` | `Vector3` | `Vector3` | `Vector3` | Produces a [Vector3](/docs/reference/engine/datatypes/Vector3.md) by adding each component of the first vector to the corresponding component of the second. | | `-` | `Vector3` | `Vector3` | `Vector3` | Produces a [Vector3](/docs/reference/engine/datatypes/Vector3.md) by subtracting each component of the second vector from the corresponding component of the first. | | `*` | `Vector3` | `Vector3` | `Vector3` | Produces a [Vector3](/docs/reference/engine/datatypes/Vector3.md) by multiplying each component of the first vector by the corresponding component of the second. | | `/` | `Vector3` | `Vector3` | `Vector3` | Produces a [Vector3](/docs/reference/engine/datatypes/Vector3.md) by dividing each component of the first vector by the corresponding component of the second. | | `//` | `Vector3` | `Vector3` | `Vector3` | Produces a [Vector3](/docs/reference/engine/datatypes/Vector3.md) by **floor dividing** each component of the first vector by the corresponding component of the second. | | `*` | `Vector3` | `number` | `Vector3` | Produces a [Vector3](/docs/reference/engine/datatypes/Vector3.md) by multiplying each component of the provided vector by the number. | | `/` | `Vector3` | `number` | `Vector3` | Produces a [Vector3](/docs/reference/engine/datatypes/Vector3.md) by dividing each component of the provided vector by the number. | | `//` | `Vector3` | `number` | `Vector3` | Produces a [Vector3](/docs/reference/engine/datatypes/Vector3.md) by **floor dividing** each component of the provided vector by the number. | --- name: Vector3int16 last_updated: 2026-06-29T19:34:08Z type: datatype summary: "A Vector3 with signed 16-bit integers for components." --- # Vector3int16 A Vector3 with signed 16-bit integers for components. **Type:** datatype ## Description The [Vector3int16](/docs/reference/engine/datatypes/Vector3int16.md) data type represents a vector in 3D space with a **signed 16-bit integer** for its components. It is similar to [Vector3](/docs/reference/engine/datatypes/Vector3.md) in that it allows for the same arithmetic operations, but it lacks commonly used vector functions. [Vector3int16](/docs/reference/engine/datatypes/Vector3int16.md) should **not** be confused with: - [Vector3](/docs/reference/engine/datatypes/Vector3.md), a **more precise** and complete implementation for 3D vectors. - [Vector2int16](/docs/reference/engine/datatypes/Vector2int16.md), a similar implementation for 2D vectors. For each component: - The **lower** bound is -215, or **-32,768**. - The **upper** bound is 215 − 1, or **32,767**. #### Converting to Vector3 To convert a [Vector3int16](/docs/reference/engine/datatypes/Vector3int16.md) to a [Vector3](/docs/reference/engine/datatypes/Vector3.md), construct a [Vector3](/docs/reference/engine/datatypes/Vector3.md) by passing each **component** of the [Vector3int16](/docs/reference/engine/datatypes/Vector3int16.md) to [Vector3.new()](/docs/reference/engine/datatypes/Vector3.md): ```lua local vector3int16 = Vector3int16.new(1, 2, 3) local vector3 = Vector3.new(vector3int16.X, vector3int16.Y, vector3int16.Z) print(vector3) --> 1, 2, 3 ``` Do **not** pass an entire [Vector3int16](/docs/reference/engine/datatypes/Vector3int16.md) to [Vector3.new()](/docs/reference/engine/datatypes/Vector3.md), as the constructor interprets a [Vector3int16](/docs/reference/engine/datatypes/Vector3int16.md) as a `0` within its parameters **without producing an error**. This can lead to silent logic errors if you do something like: ```lua local vector3int16 = Vector3int16.new(1, 2, 3) local vector3 = Vector3.new(vector3int16) print(vector3) --> 0, 0, 0 ``` #### Math Operations The following math operations are valid for the [Vector3int16](/docs/reference/engine/datatypes/Vector3int16.md) data type. For all operations, be mindful of the bounds associated with signed 16-bit integers, described earlier. | Operation | Description | | --- | --- | | [Vector3int16](/docs/reference/engine/datatypes/Vector3int16.md) `+` [Vector3int16](/docs/reference/engine/datatypes/Vector3int16.md) | Produces a [Vector3int16](/docs/reference/engine/datatypes/Vector3int16.md) whose components are the sum of the operands' respective components. | | [Vector3int16](/docs/reference/engine/datatypes/Vector3int16.md) `-` [Vector3int16](/docs/reference/engine/datatypes/Vector3int16.md) | Produces a [Vector3int16](/docs/reference/engine/datatypes/Vector3int16.md) whose components are the difference of the operands' respective components. | | [Vector3int16](/docs/reference/engine/datatypes/Vector3int16.md) `*` [Vector3int16](/docs/reference/engine/datatypes/Vector3int16.md) | Produces a [Vector3int16](/docs/reference/engine/datatypes/Vector3int16.md) whose components are the product of the operands' respective components. | | [Vector3int16](/docs/reference/engine/datatypes/Vector3int16.md) `/` [Vector3int16](/docs/reference/engine/datatypes/Vector3int16.md) | Produces a [Vector3int16](/docs/reference/engine/datatypes/Vector3int16.md) whose components are the quotient of the operands' respective components. The results of the division are rounded down. | | [Vector3int16](/docs/reference/engine/datatypes/Vector3int16.md) `*` `number` | Produces a [Vector3int16](/docs/reference/engine/datatypes/Vector3int16.md) whose components are the product of the respective [Vector3int16](/docs/reference/engine/datatypes/Vector3int16.md) components and the number (factor). This operation is commutative. | | [Vector3int16](/docs/reference/engine/datatypes/Vector3int16.md) `/` `number` | Produces a [Vector3int16](/docs/reference/engine/datatypes/Vector3int16.md) whose components are the quotient of the respective [Vector3int16](/docs/reference/engine/datatypes/Vector3int16.md) components and the number (divisor). The results of the division are rounded toward zero. | ## Constructors ### Vector3int16.new **Signature:** `Vector3int16.new(x: number, y: number, z: number)` Returns a new [Vector3int16](/docs/reference/engine/datatypes/Vector3int16.md) from the given x, y and z components. Non-integer components are rounded down. The components must fall within the range [-215, 215). If outside this range, integer overflow may occur. For example, providing 32,768 (equal to 215) as a component overflows the 16-bit integer, and so the component will be -32,768 (equal to -215) instead. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `number` | | | | `y` | `number` | | | | `z` | `number` | | | ## Properties ### Vector3int16.X **Type:** `number` The x-coordinate of the [Vector3int16](/docs/reference/engine/datatypes/Vector3int16.md), also accessible in its lower-case variant. ### Vector3int16.Y **Type:** `number` The y-coordinate of the [Vector3int16](/docs/reference/engine/datatypes/Vector3int16.md), also accessible in its lower-case variant. ### Vector3int16.Z **Type:** `number` The z-coordinate of the [Vector3int16](/docs/reference/engine/datatypes/Vector3int16.md), also accessible in its lower-case variant. ## Math Operations | Operation | Type A | Type B | Returns | Description | |-----------|--------|--------|---------|-------------| | `+` | `Vector3int16` | `Vector3int16` | `Vector3int16` | Produces a [Vector3int16](/docs/reference/engine/datatypes/Vector3int16.md) whose components are the sum of the operands' respective components. | | `-` | `Vector3int16` | `Vector3int16` | `Vector3int16` | Produces a [Vector3int16](/docs/reference/engine/datatypes/Vector3int16.md) whose components are the difference of the operands' respective components. | | `*` | `Vector3int16` | `Vector3int16` | `Vector3int16` | Produces a [Vector3int16](/docs/reference/engine/datatypes/Vector3int16.md) whose components are the product of the operands' respective components. | | `/` | `Vector3int16` | `Vector3int16` | `Vector3int16` | Produces a [Vector3int16](/docs/reference/engine/datatypes/Vector3int16.md) whose components are the quotient of the operands' respective components. The results of the division are rounded down. | | `*` | `Vector3int16` | `number` | `Vector3int16` | Produces a [Vector3int16](/docs/reference/engine/datatypes/Vector3int16.md) whose components are the product of the respective [Vector3int16](/docs/reference/engine/datatypes/Vector3int16.md) components and the number (factor). This operation is commutative. | | `/` | `Vector3int16` | `number` | `Vector3int16` | Produces a [Vector3int16](/docs/reference/engine/datatypes/Vector3int16.md) whose components are the quotient of the respective [Vector3int16](/docs/reference/engine/datatypes/Vector3int16.md) components and the number (divisor). The results of the division are rounded toward zero. | --- title: "Roblox Engine data types" url: /docs/en-us/reference/engine/datatypes last_updated: 2026-06-29T19:34:09Z description: "Data types are types unique to the Roblox Engine. They can contain constructors, properties, and methods." --- # Roblox Engine data types --- name: AccessModifierType last_updated: 2026-06-29T19:34:08Z type: enum --- # AccessModifierType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Allow` | 0 | | | `Deny` | 1 | | --- name: AccessoryType last_updated: 2026-06-29T19:34:08Z type: enum summary: "A subset of AssetTypes which are relevant to only accessories." --- # AccessoryType A subset of AssetTypes which are relevant to only accessories. **Type:** enum ## Description AccessoryType is a subset of AssetTypes which are relevant to only accessories. To get the AccessoryType of an accessory, see the Type on the accessory's web page. For example, see [Yi the Art Teacher - Hair](https://www.roblox.com/catalog/5911063681/Yi-the-Art-Teacher-Hair). AccessoryTypes are mainly used in the AccessoryBlob of HumanoidDescription, and to specify the type in Accessory. ## Items | Name | Value | Description | |------|-------|-------------| | `Unknown` | 0 | If the accessory type is not known. | | `Hat` | 1 | A hat accessory (may be a rigid or layered accessory). | | `Hair` | 2 | A hair accessory (may be a rigid or layered accessory). | | `Face` | 3 | A face accessory (may be a rigid or layered accessory). | | `Neck` | 4 | A neck Accessory (may be a rigid or layered accessory). | | `Shoulder` | 5 | A shoulder accessory (may be a rigid or layered accessory). | | `Front` | 6 | A front accessory (may be a rigid or layered accessory). | | `Back` | 7 | A back accessory (may be a rigid or layered accessory). | | `Waist` | 8 | A waist accessory (may be a rigid or layered accessory). | | `TShirt` | 9 | A layered clothing T-Shirt accessory. | | `Shirt` | 10 | A layered clothing shirt accessory. | | `Pants` | 11 | A layered clothing pants accessory. | | `Jacket` | 12 | A layered clothing jacket accessory. | | `Sweater` | 13 | A layered clothing sweater accessory. | | `Shorts` | 14 | A layered clothing shorts accessory. | | `LeftShoe` | 15 | A layered clothing left shoe accessory. | | `RightShoe` | 16 | A layered clothing right shoe accessory. | | `DressSkirt` | 17 | A layered clothing dress or skirt accessory. | | `Eyebrow` | 18 | | | `Eyelash` | 19 | | --- name: ActionOnAutoResumeSync last_updated: 2026-06-29T19:34:08Z type: enum --- # ActionOnAutoResumeSync **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `DontResume` | 0 | | | `KeepStudio` | 1 | | | `KeepLocal` | 2 | | --- name: ActionOnStopSync last_updated: 2026-06-29T19:34:08Z type: enum --- # ActionOnStopSync **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `AlwaysAsk` | 0 | | | `KeepLocalFiles` | 1 | | | `DeleteLocalFiles` | 2 | | --- name: ActionType last_updated: 2026-06-29T19:34:08Z type: enum --- # ActionType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Nothing` | 0 | | | `Pause` | 1 | | | `Lose` | 2 | | | `Draw` | 3 | | | `Win` | 4 | | --- name: ActivePayerStatus last_updated: 2026-06-29T19:34:08Z type: enum summary: "Describes a player's payer status bucket for the current experience." --- # ActivePayerStatus Describes a player's payer status bucket for the current experience. **Type:** enum ## Description The `ActivePayerStatus` enum describes a player's payer status bucket for the current experience. ## Items | Name | Value | Description | |------|-------|-------------| | `Unknown` | 0 | Segment data is unavailable. | | `Never` | 1 | The player has not made a qualifying purchase in the experience. | | `Lapsed` | 2 | The player has previously spent in the experience but is not currently an active payer. | | `Casual50Percent` | 3 | The player is in the casual payer bucket for the experience. | | `Intermediate35Percent` | 4 | The player is in the intermediate payer bucket for the experience. | | `Top15Percent` | 5 | The player is in the top payer bucket for the experience. | --- name: ActuatorRelativeTo last_updated: 2026-06-29T19:34:08Z type: enum summary: "The CFrame value in which the body mover constraint is expressed." --- # ActuatorRelativeTo The CFrame value in which the body mover constraint is expressed. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Attachment0` | 0 | The constraint is expressed relative to Attachment0. | | `Attachment1` | 1 | The constraint is expressed relative to Attachment1. | | `World` | 2 | The constraint is expressed relative to the game world. | --- name: ActuatorType last_updated: 2026-06-29T19:34:08Z type: enum summary: "The active physics component of the constraint." --- # ActuatorType The active physics component of the constraint. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `None` | 0 | The motor and servo components are both disabled. | | `Motor` | 1 | The motor component is enabled. Motor properties are active and the actuator will apply continuous force. | | `Servo` | 2 | The servo component is enabled. Servo properties are active and the actuator will apply force to approach the servo's target. | --- name: AdAvailabilityResult last_updated: 2026-06-29T19:34:08Z type: enum --- # AdAvailabilityResult **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `IsAvailable` | 1 | An ad is available to show the user. | | `DeviceIneligible` | 2 | The device is ineligible to receive ads. | | `ExperienceIneligible` | 3 | The experience is ineligible to receive ads. Check if its eligibility has been revoked. | | `InternalError` | 4 | An unspecified internal error occurred. Try fetching the ad again. | | `NoFill` | 5 | There are no ads available to fill your ad request. You might have hit the ad frequency limit. | | `PlayerIneligible` | 6 | The user is ineligible to receive ads during this session. The user might be under 13 or located in a blocked region. | | `PublisherIneligible` | 7 | The publisher is ineligible to receive ads because they have not met the publisher eligibility requirements. | --- name: AdEventType last_updated: 2026-06-29T19:34:08Z type: enum --- # AdEventType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `VideoLoaded` | 0 | | | `VideoRemoved` | 1 | | | `UserCompletedVideo` | 2 | | | `RewardedAdLoaded` | 3 | The event is fired when a click-to-play video ad is being served. This can be used to communicate and promote the reward to users through the UI or signage. | | `RewardedAdGrant` | 4 | The event is fired when a user has watched the click-to-play video ad for a certain time. This can be used to grant the player a reward such as an in-game item or in-game currency. The `RewardedAdGrant` enum will only be triggered once per ad rotation. | | `RewardedAdUnloaded` | 5 | The event is fired when a click-to-play video ad is rotated out. This can be used to remove any UI or signage that is promoting the reward. | --- name: AdFormat last_updated: 2026-06-29T19:34:08Z type: enum --- # AdFormat **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `RewardedVideo` | 0 | The format of the rewarded video ad. | --- name: AdShape last_updated: 2026-06-29T19:34:08Z type: enum summary: "Describes the form factor of an ad in an AdGui. There is currently only one available size." --- # AdShape Describes the form factor of an ad in an [AdGui](/docs/reference/engine/classes/AdGui.md). There is currently only one available size. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `HorizontalRectangle` | 1 | The ad has an aspect ratio of 16:9 and a minimum size of 8 by 4.5 studs. | --- name: AdTeleportMethod last_updated: 2026-06-29T19:34:08Z type: enum --- # AdTeleportMethod **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Undefined` | 0 | | | `PortalForward` | 1 | | | `InGameMenuBackButton` | 2 | | | `UIBackButton` | 3 | | --- name: AdUIEventType last_updated: 2026-06-29T19:34:08Z type: enum --- # AdUIEventType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `AdLabelClicked` | 0 | | | `VolumeButtonClicked` | 1 | | | `FullscreenButtonClicked` | 2 | | | `PlayButtonClicked` | 3 | | | `PauseButtonClicked` | 4 | | | `CloseButtonClicked` | 5 | | | `WhyThisAdClicked` | 6 | | | `PlayEventTriggered` | 7 | | | `PauseEventTriggered` | 8 | | --- name: AdUIType last_updated: 2026-06-29T19:34:08Z type: enum --- # AdUIType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `None` | 0 | | | `Image` | 1 | | | `Video` | 2 | | --- name: AdUnitStatus last_updated: 2026-06-29T19:34:08Z type: enum summary: "Exposes the status of an immersive ad." --- # AdUnitStatus Exposes the status of an immersive ad. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Inactive` | 0 | The ad unit isn't currently serving an ad and it may be configured incorrectly. An [AdGui](/docs/reference/engine/classes/AdGui.md) with this status will display its fallback image. | | `Active` | 1 | The ad unit is currently serving an ad. Players will observe sponsored content in an [AdGui](/docs/reference/engine/classes/AdGui.md) and an [AdPortal](/docs/reference/engine/classes/AdPortal.md) will teleport users that walk through. | --- name: AdornCullingMode last_updated: 2026-06-29T19:34:08Z type: enum summary: "Culling method used for adornments." --- # AdornCullingMode Culling method used for adornments. **Type:** enum ## Description Used by [HandleAdornment](/docs/reference/engine/classes/HandleAdornment.md) to determine how it is culled. ## Items | Name | Value | Description | |------|-------|-------------| | `Automatic` | 0 | Automatically culls adornment based on distance. | | `Never` | 1 | Never culls adornment. Adornment is always visible. | --- name: AdornShading last_updated: 2026-06-29T19:34:08Z type: enum --- # AdornShading **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | | | `Shaded` | 1 | | | `XRay` | 2 | | | `XRayShaded` | 3 | | | `AlwaysOnTop` | 4 | | --- name: AlignType last_updated: 2026-06-29T19:34:08Z type: enum summary: "An enum that specifies how the constraint will attempt to align the body associated with the constraint." --- # AlignType An enum that specifies how the constraint will attempt to align the body associated with the constraint. **Type:** enum ## Description An enum that specifies how the constraint will attempt to align the body associated with the constraint and attachment. Depending on the configuration, it can be used to align specific axes to be parallel, perpendicular, or to look at a specific point. ## Items | Name | Value | Description | |------|-------|-------------| | `Parallel` | 0 | | | `Perpendicular` | 1 | | | `PrimaryAxisParallel` | 2 | Aligns the primary axis to be parallel to the axis given by [Constraint.Attachment1](/docs/reference/engine/classes/Constraint.md). | | `PrimaryAxisPerpendicular` | 3 | Aligns the primary axis to be perpendicular to the axis given by [Constraint.Attachment1](/docs/reference/engine/classes/Constraint.md). | | `PrimaryAxisLookAt` | 4 | Aligns the primary axis to look at the point given by [Constraint.Attachment1](/docs/reference/engine/classes/Constraint.md) or the [AlignOrientation.LookAtPosition](/docs/reference/engine/classes/AlignOrientation.md). | | `AllAxes` | 5 | Aligns all of the axes of [Constraint.Attachment0](/docs/reference/engine/classes/Constraint.md) to the axes given by [Constraint.Attachment1](/docs/reference/engine/classes/Constraint.md) or to the target orientation provided by [AlignOrientation.PrimaryAxis](/docs/reference/engine/classes/AlignOrientation.md) and [AlignOrientation.SecondaryAxis](/docs/reference/engine/classes/AlignOrientation.md). | --- name: AlphaMode last_updated: 2026-06-29T19:34:08Z type: enum summary: "Used by SurfaceAppearance.AlphaMode to determine how the alpha channel of the SurfaceAppearance.ColorMap of a SurfaceAppearance is used." --- # AlphaMode Used by [SurfaceAppearance.AlphaMode](/docs/reference/engine/classes/SurfaceAppearance.md) to determine how the alpha channel of the [SurfaceAppearance.ColorMap](/docs/reference/engine/classes/SurfaceAppearance.md) of a [SurfaceAppearance](/docs/reference/engine/classes/SurfaceAppearance.md) is used. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Overlay` | 0 | Overlays the [ColorMap](/docs/reference/engine/classes/SurfaceAppearance.md) on top of the underlying part color based on the map's alpha channel. | | `Transparency` | 1 | Uses the [ColorMap](/docs/reference/engine/classes/SurfaceAppearance.md) alpha channel to control the transparency of the surface. | | `TintMask` | 2 | Uses the [ColorMap](/docs/reference/engine/classes/SurfaceAppearance.md) alpha channel to control the amount of [SurfaceAppearance.Color](/docs/reference/engine/classes/SurfaceAppearance.md) tinting. | | `Opaque` | 3 | Ignores the [ColorMap](/docs/reference/engine/classes/SurfaceAppearance.md) alpha channel and assumes an opacity value of 1 everywhere. | --- name: AnalyticsCustomFieldKeys last_updated: 2026-06-29T19:34:08Z type: enum summary: "Used to form a dictionary of custom fields to provide breakdowns in Roblox-provided charts." --- # AnalyticsCustomFieldKeys Used to form a dictionary of custom fields to provide breakdowns in Roblox-provided charts. **Type:** enum ## Description Used to form a dictionary of custom fields to provide breakdowns in Roblox-provided charts. For example: ```lua local customFields = { [Enum.AnalyticsCustomFieldKeys.CustomField01.Name] = "value1", [Enum.AnalyticsCustomFieldKeys.CustomField02.Name] = "value2", [Enum.AnalyticsCustomFieldKeys.CustomField03.Name] = "value3", } ``` ## Items | Name | Value | Description | |------|-------|-------------| | `CustomField01` | 0 | | | `CustomField02` | 1 | | | `CustomField03` | 2 | | --- name: AnalyticsEconomyAction last_updated: 2026-06-29T19:34:08Z type: enum --- # AnalyticsEconomyAction **Type:** enum ## Description This enum is used as an argument in [AnalyticsService:FireInGameEconomyEvent()](/docs/reference/engine/classes/AnalyticsService.md). ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | Default action. Used for an undefined Action. | | `Acquire` | 1 | Indicates the acquisition of an in game resource. For example, a player acquires virtual currency by completing a specific in game task. | | `Spend` | 2 | Indicates the spending or loss of an in game resource. For example, a player spends virtual currency to acquire a new item. | --- name: AnalyticsEconomyFlowType last_updated: 2026-06-29T19:34:08Z type: enum --- # AnalyticsEconomyFlowType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Sink` | 0 | Currency is being removed from the player's account. | | `Source` | 1 | Currency is being added to the player's account. | --- name: AnalyticsEconomyTransactionType last_updated: 2026-06-29T19:34:08Z type: enum --- # AnalyticsEconomyTransactionType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `IAP` | 0 | Represents a source of currency purchased via Robux, for example in-experience "gold" purchased for Robux. | | `Shop` | 1 | Purchases within a shop, for example in-experience "coins" used to buy an item within the experience. | | `Gameplay` | 2 | Currency earned or paid through gameplay, for example in-experience "gold" earned from winning a match. | | `ContextualPurchase` | 3 | Purchase with direct context to a reward, for example in-experience "gold" used to purchase an extra life. | | `TimedReward` | 4 | Time-based reward, for example in-experience "gold" rewarded for logging in daily. | | `Onboarding` | 5 | Transaction related to onboarding, for example in-experience "gold" rewarded for completing the tutorial. | --- name: AnalyticsLogLevel last_updated: 2026-06-29T19:34:08Z type: enum --- # AnalyticsLogLevel **Type:** enum ## Description This enum is used as an argument in [AnalyticsService.LogEvent](/docs/reference/engine/classes/AnalyticsService.md) to describe the error severity level. ## Items | Name | Value | Description | |------|-------|-------------| | `Trace` | 0 | Trace is the noisiest level, rarely (if ever) enabled for a production app. | | `Debug` | 1 | Used for debugging. | | `Information` | 2 | | | `Warning` | 3 | Used for warning. | | `Error` | 4 | When functionality is unavailable or expectations broken. | | `Fatal` | 5 | The most critical level, Fatal events demand immediate attention. | --- name: AnalyticsProgressionStatus last_updated: 2026-06-29T19:34:08Z type: enum summary: "This enum is used as an argument in AnalyticsService:FirePlayerProgressionEvent() to describe the status of progression." --- # AnalyticsProgressionStatus This enum is used as an argument in [AnalyticsService:FirePlayerProgressionEvent()](/docs/reference/engine/classes/AnalyticsService.md) to describe the status of progression. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | Default status. Used for an undefined status. | | `Begin` | 1 | Indicates the beginning of progression. | | `Complete` | 2 | Indicates the progression completed. | | `Abandon` | 3 | Indicates the progression abandoned. | | `Fail` | 4 | Indicates the progression failed. | --- name: AnalyticsProgressionType last_updated: 2026-06-29T19:34:08Z type: enum --- # AnalyticsProgressionType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Custom` | 0 | | | `Start` | 1 | | | `Fail` | 2 | | | `Complete` | 3 | | --- name: AnimationClipFromVideoStatus last_updated: 2026-06-29T19:34:08Z type: enum --- # AnimationClipFromVideoStatus **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Initializing` | 0 | | | `Pending` | 1 | | | `Processing` | 2 | | | `ErrorGeneric` | 4 | | | `Success` | 6 | | | `ErrorVideoTooLong` | 7 | | | `ErrorNoPersonDetected` | 8 | | | `ErrorVideoUnstable` | 9 | | | `Timeout` | 10 | | | `Cancelled` | 11 | | | `ErrorMultiplePeople` | 12 | | | `ErrorUploadingVideo` | 2001 | | --- name: AnimationNodeType last_updated: 2026-06-29T19:34:08Z type: enum --- # AnimationNodeType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `InvalidNode` | 0 | | | `AddNode` | 1 | | | `OverNode` | 2 | | | `Blend1DNode` | 3 | | | `Blend2DNode` | 4 | | | `ClipNode` | 5 | | | `GraphOutput` | 6 | | | `MaskNode` | 7 | | | `PrioritySelectNode` | 8 | | | `RandomSequenceNode` | 9 | | | `SelectNode` | 10 | | | `SequenceNode` | 11 | | | `SpeedNode` | 12 | | | `SubtractNode` | 13 | | --- name: AnimationPriority last_updated: 2026-06-29T19:34:08Z type: enum summary: "The AnimationPriority Enum determines how concurrently-playing AnimationTracks contribute to the final animation." --- # AnimationPriority The AnimationPriority Enum determines how concurrently-playing AnimationTracks contribute to the final animation. **Type:** enum ## Description When multiple [AnimationTracks](/docs/reference/engine/classes/AnimationTrack.md) are played concurrently by the same [Animator](/docs/reference/engine/classes/Animator.md) and affect the same animated joints, the tracks are evaluated in order from high to low priority, per joint, while the total track weight sum remains less than 1.0. When the track weight sum reaches or exceeds 1.0 for a joint, evaluation stops and no lower-priority tracks will be evaluated for that joint. [AnimationTracks](/docs/reference/engine/classes/AnimationTrack.md) with the same priority that are playing concurrently will blend proportional to their track weights (normalized if the sum exceeds 1). Higher priority animation will override lower priority ones per joint. The priority order is: 1. **Action4** 2. **Action3** 3. **Action2** 4. **Action** 5. **Movement** 6. **Idle** 7. **Core** Roblox's default character animations, including catalog animation bundles, play at **Core** priority. **Idle** through **Action4** priority are for developer use. ## Items | Name | Value | Description | |------|-------|-------------| | `Idle` | 0 | (6) - Recommended priority for character idle animations. | | `Movement` | 1 | (5) - Recommended priority for walk, run, swim, climb, and other locomotion animations. | | `Action` | 2 | (4) - Recommended priority for character actions that must override idle and locomotion animations. | | `Action2` | 3 | (3) - Action2 will override Action. | | `Action3` | 4 | (2) - Action3 will override Action2. | | `Action4` | 5 | (1) - Action4 is the highest priority available, overriding all other priority values. | | `Core` | 1000 | (7) - Lowest priority, intended for use by Roblox default animations and catalog animation bundles. | --- name: AnimatorRetargetingMode last_updated: 2026-06-29T19:34:08Z type: enum summary: "Animation Retargeting setting." --- # AnimatorRetargetingMode Animation Retargeting setting. **Type:** enum ## Description The AnimatorRetargetingMode Enum describes the setting for Animation Retargeting. Retargeting adapts animations to various character rigs based on their proportions. ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | Retargeting in default state, opt-in initially but subject to change. | | `Disabled` | 1 | Retargeting disabled. | | `Enabled` | 2 | Retargeting enabled. | --- name: AnnotationEditingMode last_updated: 2026-06-29T19:34:08Z type: enum --- # AnnotationEditingMode **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `None` | 0 | | | `PlacingNew` | 1 | | | `WritingNew` | 2 | | --- name: AnnotationRequestStatus last_updated: 2026-06-29T19:34:08Z type: enum --- # AnnotationRequestStatus **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Success` | 0 | | | `Loading` | 1 | | | `ErrorInternalFailure` | 2 | | | `ErrorNotFound` | 3 | | | `ErrorModerated` | 4 | | --- name: AnnotationRequestType last_updated: 2026-06-29T19:34:08Z type: enum --- # AnnotationRequestType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Unknown` | 0 | | | `Create` | 1 | | | `Resolve` | 2 | | | `Delete` | 3 | | | `Edit` | 4 | | --- name: AntiAliasing last_updated: 2026-06-29T19:34:08Z type: enum summary: "Determines whether anti-aliasing is applied when drawing on the instance." --- # AntiAliasing Determines whether anti-aliasing is applied when drawing on the instance. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Disabled` | 0 | Disables anti-aliasing, resulting in hard edges. | | `Enabled` | 1 | Enables anti-aliasing, resulting in soft edges. | --- name: AppLifecycleManagerState last_updated: 2026-06-29T19:34:08Z type: enum --- # AppLifecycleManagerState **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Detached` | 0 | | | `Active` | 1 | | | `Inactive` | 2 | | | `Hidden` | 3 | | --- name: AppShellActionType last_updated: 2026-06-29T19:34:08Z type: enum --- # AppShellActionType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `None` | 0 | | | `OpenApp` | 1 | | | `TapChatTab` | 2 | | | `TapConversationEntry` | 3 | | | `TapAvatarTab` | 4 | | | `ReadConversation` | 5 | | | `TapGamePageTab` | 6 | | | `TapHomePageTab` | 7 | | | `GamePageLoaded` | 8 | | | `HomePageLoaded` | 9 | | | `AvatarEditorPageLoaded` | 10 | | | `HomePageInteractive` | 11 | | --- name: AppShellFeature last_updated: 2026-06-29T19:34:08Z type: enum --- # AppShellFeature **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `None` | 0 | | | `Chat` | 1 | | | `AvatarEditor` | 2 | | | `GamePage` | 3 | | | `HomePage` | 4 | | | `More` | 5 | | | `Landing` | 6 | | --- name: AppUpdateStatus last_updated: 2026-06-29T19:34:08Z type: enum --- # AppUpdateStatus **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Unknown` | 0 | | | `NotSupported` | 1 | | | `Failed` | 2 | | | `NotAvailable` | 3 | | | `Available` | 4 | | | `AvailableBoundChannel` | 5 | | | `AvailableBetaProgram` | 6 | | --- name: ApplyStrokeMode last_updated: 2026-06-29T19:34:08Z type: enum summary: "Used by UIStroke.ApplyStrokeMode to determine where to apply the stroke." --- # ApplyStrokeMode Used by [UIStroke.ApplyStrokeMode](/docs/reference/engine/classes/UIStroke.md) to determine where to apply the stroke. **Type:** enum ## Description This enum is used by [UIStroke.ApplyStrokeMode](/docs/reference/engine/classes/UIStroke.md) to determine whether to apply the stroke to the object's border or of the text itself. For a more detailed walkthrough of the UIStroke object, see Applying Strokes. ## Items | Name | Value | Description | |------|-------|-------------| | `Contextual` | 0 | The stroke is applied to the parent UI element's text. | | `Border` | 1 | The stroke is applied to the parent UI element's border. | --- name: AspectType last_updated: 2026-06-29T19:34:08Z type: enum summary: "Used by UIAspectRatioConstraint.AspectType to control sizing behavior." --- # AspectType Used by [UIAspectRatioConstraint.AspectType](/docs/reference/engine/classes/UIAspectRatioConstraint.md) to control sizing behavior. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `FitWithinMaxSize` | 0 | The object will be the maximum size possible within its own [AbsoluteSize](/docs/reference/engine/classes/GuiBase2d.md). | | `ScaleWithParentSize` | 1 | The object's maximum size will be the size of the parent while still maintaining the aspect ratio. | --- name: AssetCreatorType last_updated: 2026-06-29T19:34:08Z type: enum --- # AssetCreatorType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `User` | 0 | | | `Group` | 1 | | --- name: AssetFetchStatus last_updated: 2026-06-29T19:34:08Z type: enum --- # AssetFetchStatus **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Success` | 0 | The asset loaded successfully. | | `Failure` | 1 | The asset failed to load successfully. Subsequent attempts are likely to fail; there may be something wrong with the [Content](/docs/reference/engine/datatypes/Content.md) string. | | `None` | 2 | The engine has no information about this asset. The engine never tried to load it. | | `Loading` | 3 | The engine is in the middle of trying to load this asset. | | `TimedOut` | 4 | The engine tried to load this asset but timed out. Future attempts to load may succeed. | --- name: AssetType last_updated: 2026-06-29T19:34:08Z type: enum summary: "This Enum can be used to match the AssetTypeId from MarketplaceService:GetProductInfoAsync() to an asset type." --- # AssetType This Enum can be used to match the AssetTypeId from [MarketplaceService:GetProductInfoAsync()](/docs/reference/engine/classes/MarketplaceService.md) to an asset type. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Image` | 1 | The asset is an image. | | `TShirt` | 2 | | | `Audio` | 3 | The asset is an audio clip. | | `Mesh` | 4 | The asset is a mesh. | | `Lua` | 5 | The asset is Luau code, for example a [ModuleScript](/docs/reference/engine/classes/ModuleScript.md). | | `Hat` | 8 | The asset is a hat. | | `Place` | 9 | The asset is a place. | | `Model` | 10 | The asset is a model. | | `Shirt` | 11 | The asset is a shirt. | | `Pants` | 12 | The asset is a pants item. | | `Decal` | 13 | The asset is a decal (image). | | `Head` | 17 | The asset is a head. | | `Face` | 18 | The asset is a face. | | `Gear` | 19 | The asset is a gear item. | | `Badge` | 21 | The asset is a badge. | | `Animation` | 24 | The asset is an animation; also see the more specific animation type enums below. | | `Torso` | 27 | The asset is a torso. | | `RightArm` | 28 | The asset is a right arm. | | `LeftArm` | 29 | The asset is a left arm. | | `LeftLeg` | 30 | The asset is a left leg. | | `RightLeg` | 31 | The asset is a right leg. | | `Package` | 32 | The asset is a pack, for example an animation package. | | `GamePass` | 34 | The asset is a GamePass. | | `Plugin` | 38 | The asset is a plugin. | | `MeshPart` | 40 | The asset is a mesh part. | | `HairAccessory` | 41 | The asset is a hair accessory. | | `FaceAccessory` | 42 | The asset is a face accessory. | | `NeckAccessory` | 43 | The asset is a neck accessory. | | `ShoulderAccessory` | 44 | The asset is a shoulder accessory. | | `FrontAccessory` | 45 | The asset is a front accessory. | | `BackAccessory` | 46 | The asset is a back accessory. | | `WaistAccessory` | 47 | The asset is a waist accessory. | | `ClimbAnimation` | 48 | The asset is a climb animation. | | `DeathAnimation` | 49 | The asset is a death animation. | | `FallAnimation` | 50 | The asset is a fall animation. | | `IdleAnimation` | 51 | The asset is an idle animation. | | `JumpAnimation` | 52 | The asset is a jump animation. | | `RunAnimation` | 53 | The asset is a run animation. | | `SwimAnimation` | 54 | The asset is a swim animation. | | `WalkAnimation` | 55 | The asset is a walk animation. | | `PoseAnimation` | 56 | The asset is a pose animation. | | `EarAccessory` | 57 | | | `EyeAccessory` | 58 | | | `EmoteAnimation` | 61 | The asset is an emote animation. | | `Video` | 62 | The asset is a video. | | `TShirtAccessory` | 64 | | | `ShirtAccessory` | 65 | | | `PantsAccessory` | 66 | | | `JacketAccessory` | 67 | | | `SweaterAccessory` | 68 | | | `ShortsAccessory` | 69 | | | `LeftShoeAccessory` | 70 | | | `RightShoeAccessory` | 71 | | | `DressSkirtAccessory` | 72 | | | `FontFamily` | 73 | | | `EyebrowAccessory` | 76 | | | `EyelashAccessory` | 77 | | | `MoodAnimation` | 78 | | | `DynamicHead` | 79 | | | `FaceMakeup` | 88 | | | `LipMakeup` | 89 | | | `EyeMakeup` | 90 | | | `VoxelFragment` | 91 | | --- name: AssetTypeVerification last_updated: 2026-06-29T19:34:08Z type: enum summary: "Determines the asset type verification mode." --- # AssetTypeVerification Determines the asset type verification mode. **Type:** enum ## Description Determines the asset type verification mode used when calling [Humanoid:ApplyDescriptionAsync()](/docs/reference/engine/classes/Humanoid.md) or [Players:CreateHumanoidModelFromDescriptionAsync()](/docs/reference/engine/classes/Players.md). This verification mode determines if the method will load models or not. Loading models can be insecure if it's possible for malicious users to trick your game into loading unexpected models that you own which may include malicious scripts. You should set this to `Always` unless you want to load non-catalog assets. ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 1 | Use the default behavior for asset type verification. | | `ClientOnly` | 2 | Only verify asset types on the client. Asset type verification can not be turned off on the client. | | `Always` | 3 | Always verify the asset types to be loaded and disallow loading models. | --- name: AudioApiRollout last_updated: 2026-06-29T19:34:08Z type: enum summary: "Used to determine whether voice chat is represented and controlled by AudioDeviceInput objects." --- # AudioApiRollout Used to determine whether voice chat is represented and controlled by [AudioDeviceInput](/docs/reference/engine/classes/AudioDeviceInput.md) objects. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Disabled` | 0 | Voice chat will use an internal voice chat implementation that is automatic and hidden. | | `Automatic` | 1 | Currently means the same thing as `Disabled`, but will be updated to mean `Enabled` in the future. | | `Enabled` | 2 | Voice chat can be customized or controlled via [AudioDeviceInputs](/docs/reference/engine/classes/AudioDeviceInput.md). | --- name: AudioCaptureMode last_updated: 2026-06-29T19:34:08Z type: enum --- # AudioCaptureMode **Type:** enum --- name: AudioChannelLayout last_updated: 2026-06-29T19:34:08Z type: enum summary: "Describes the channel layout of an audio stream." --- # AudioChannelLayout Describes the channel layout of an audio stream. **Type:** enum ## Description The `AudioChannelLayout` enum describes the channel layout of an audio stream. Audio streams contain one or more channels that are intended to be rendered simultaneously with a particular arrangement of speakers. In general, more channels impact performance but provide higher spatial quality. ## Items | Name | Value | Description | |------|-------|-------------| | `Mono` | 0 | Monaural audio streams contain only one **Center** channel. ![Diagram showing position of channels for Mono layout.](/assets/engine-api/enums/AudioChannelLayout/Mono.jpg) | | `Stereo` | 1 | Stereophonic audio streams consist of two channels: **Left** and **Right**. ![Diagram showing position of channels for Stereo layout.](/assets/engine-api/enums/AudioChannelLayout/Stereo.jpg) | | `Quad` | 2 | Quadrophonic audio streams consist of four channels: **Left**, **Right**, **BackLeft**, and **BackRight**. Quadrophonic streams can encode forward/backward spatial information that stereo streams might struggle with. ![Diagram showing position of channels for Quad layout.](/assets/engine-api/enums/AudioChannelLayout/Quad.jpg) | | `Surround_5` | 3 | Surround sound audio streams consist of five channels: **Left**, **Right**, **Center**, **BackLeft**, and **BackRight**. Surround sound streams encode spatial information with better resolution front and center. ![Diagram showing position of channels for Surround 5 layout.](/assets/engine-api/enums/AudioChannelLayout/Surround_5.jpg) | | `Surround_5_1` | 4 | 5.1 surround sound consists of six channels: **Left**, **Right**, **Center**, **BackLeft**, **BackRight**, and a **Sub** (subsonic) low‑frequency channel. 5.1 surround sound benefits from low frequencies being less directional in order to encode higher resolution spatial information versus simple 5‑channel surround sound. ![Diagram showing position of channels for Surround 5.1 layout.](/assets/engine-api/enums/AudioChannelLayout/Surround_5_1.jpg) | | `Surround_7_1` | 5 | 7.1 surround sound consists of eight channels: **Left**, **Right**, **Center**, **SurroundLeft**, **SurroundRight**, **BackLeft**, **BackRight**, and a **Sub** (subsonic) low‑frequency channel. 7.1 surround sound is an improvement over 5.1 surround sound, offering better spatial resolution directly to the left and right as well. ![Diagram showing position of channels for Surround 7.1 layout.](/assets/engine-api/enums/AudioChannelLayout/Surround_7_1.jpg) | | `Surround_7_1_4` | 6 | 7.1.4 surround sound consists of twelve channels: **Left**, **Right**, **Center**, **SurroundLeft**, **SurroundRight**, **BackLeft**, **BackRight**, **Sub**, **TopLeft**, **TopRight**, **TopBackLeft**, and **TopBackRight**. 7.1.4 is the only currently supported channel layout that encodes height information. ![Diagram showing position of channels for Surround 7.1.4 layout.](/assets/engine-api/enums/AudioChannelLayout/Surround_7_1_4.jpg) | --- name: AudioFilterType last_updated: 2026-06-29T19:34:08Z type: enum summary: "Filter types used for AudioFilter instances." --- # AudioFilterType Filter types used for [AudioFilter](/docs/reference/engine/classes/AudioFilter.md) instances. **Type:** enum ## Description Families of curves that [AudioFilter](/docs/reference/engine/classes/AudioFilter.md) can use to process incoming audio. Each filter type is applied at a specified [Frequency](/docs/reference/engine/classes/AudioFilter.md) and may be shaped by additional [Gain](/docs/reference/engine/classes/AudioFilter.md) or [Q](/docs/reference/engine/classes/AudioFilter.md) parameters. ## Items | Name | Value | Description | |------|-------|-------------| | `Peak` | 0 | Filter that boosts or reduces sound near a specified [Frequency](/docs/reference/engine/classes/AudioFilter.md). | | `LowShelf` | 1 | Filter that boosts or reduces sound below a specified [Frequency](/docs/reference/engine/classes/AudioFilter.md). | | `HighShelf` | 2 | Filter that boosts or reduces sound above a specified [Frequency](/docs/reference/engine/classes/AudioFilter.md). | | `Lowpass12dB` | 3 | Filter that cuts sound above a specified [Frequency](/docs/reference/engine/classes/AudioFilter.md), at a slope of -12dB/octave. | | `Lowpass24dB` | 4 | Filter that cuts sound above a specified [Frequency](/docs/reference/engine/classes/AudioFilter.md), at a slope of -24dB/octave. | | `Lowpass48dB` | 5 | Filter that cuts sound above a specified [Frequency](/docs/reference/engine/classes/AudioFilter.md), at a slope of -48dB/octave. | | `Highpass12dB` | 6 | Filter that cuts sound below a specified [Frequency](/docs/reference/engine/classes/AudioFilter.md), at a slope of -12dB/octave. | | `Highpass24dB` | 7 | Filter that cuts sound below a specified [Frequency](/docs/reference/engine/classes/AudioFilter.md), at a slope of -24dB/octave. | | `Highpass48dB` | 8 | Filter that cuts sound below a specified [Frequency](/docs/reference/engine/classes/AudioFilter.md), at a slope of -48dB/octave. | | `Bandpass` | 9 | Filter that only allows sound near a specified [Frequency](/docs/reference/engine/classes/AudioFilter.md) to be heard. | | `Notch` | 10 | Filter that cuts sound near a specified [Frequency](/docs/reference/engine/classes/AudioFilter.md). | | `Lowpass6dB` | 11 | Filter that cuts sound above a specified [Frequency](/docs/reference/engine/classes/AudioFilter.md), at a slope of -6dB/octave. | --- name: AudioSimulationFidelity last_updated: 2026-06-29T19:34:08Z type: enum summary: "Enum which determines how detailed audio simulation should be for AudioEmitters and AudioListeners." --- # AudioSimulationFidelity Enum which determines how detailed audio simulation should be for [AudioEmitters](/docs/reference/engine/classes/AudioEmitter.md) and [AudioListeners](/docs/reference/engine/classes/AudioListener.md). **Type:** enum ## Description This enum, used with [AudioEmitter.SimulationFidelity](/docs/reference/engine/classes/AudioEmitter.md) and [AudioListener.SimulationFidelity](/docs/reference/engine/classes/AudioListener.md), determines how detailed audio simulation should be. ## Items | Name | Value | Description | |------|-------|-------------| | `None` | 0 | No acoustic simulation occurs; only direction and distance are taken into account. | | `Automatic` | 1 | The audio engine simulates transmission, diffraction, and reflections at a level of detail suitable to most devices. | --- name: AudioSubType last_updated: 2026-06-29T19:34:08Z type: enum summary: "The categorization of an audio asset." --- # AudioSubType The categorization of an audio asset. **Type:** enum ## Description The categorization of an audio asset between music and sound effects. This categorization is used for filtering audio in [AudioSearchParams](/docs/reference/engine/classes/AudioSearchParams.md). ## Items | Name | Value | Description | |------|-------|-------------| | `Music` | 1 | Represents an asset which contains music. | | `SoundEffect` | 2 | Represents an asset which is a sound effect. | --- name: AudioWindowSize last_updated: 2026-06-29T19:34:08Z type: enum summary: "For audio effects that use some internal buffering, determines the window-size used." --- # AudioWindowSize For audio effects that use some internal buffering, determines the window-size used. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Small` | 0 | | | `Medium` | 1 | | | `Large` | 2 | | --- name: AuthorityMode last_updated: 2026-06-29T19:34:08Z type: enum summary: "Enum used with Workspace.AuthorityMode." --- # AuthorityMode Enum used with [Workspace.AuthorityMode](/docs/reference/engine/classes/Workspace.md). **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Server` | 0 | Server authority with client side prediction and rollback enabled. | | `Automatic` | 1 | Traditional distributed authority model where, for each instance, the engine decides whether the server or a client should be the authority; [BasePart:SetNetworkOwner()](/docs/reference/engine/classes/BasePart.md) can be used to influence the decision. | --- name: AutoIndentRule last_updated: 2026-06-29T19:34:08Z type: enum --- # AutoIndentRule **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Off` | 0 | | | `Absolute` | 1 | | | `Relative` | 2 | | --- name: AutomaticSize last_updated: 2026-06-29T19:34:08Z type: enum summary: "UI objects with AutomaticSize enabled will increase in size up to maximum size allowed by the parent (if there is one) and no smaller than the Size property bounds." --- # AutomaticSize UI objects with [AutomaticSize](/docs/reference/engine/classes/GuiObject.md) enabled will increase in size up to maximum size allowed by the parent (if there is one) and no smaller than the [Size](/docs/reference/engine/classes/GuiObject.md) property bounds. **Type:** enum ## Description UI objects with [AutomaticSize](/docs/reference/engine/classes/GuiObject.md) enabled will increase in size up to maximum size allowed by the parent (if there is one) and no smaller than the [Size](/docs/reference/engine/classes/GuiObject.md) property bounds. To enable [AutomaticSize](/docs/reference/engine/enums/AutomaticSize.md), set the value to an enum value other than `None`. This enum is used by [GuiObject.AutomaticSize](/docs/reference/engine/classes/GuiObject.md) and [ScrollingFrame.AutomaticCanvasSize](/docs/reference/engine/classes/ScrollingFrame.md) to determine whether and how resizing occurs based on child content. For more information, see [Automatic Sizing](/docs/en-us/ui/size-modifiers.md#automatic-sizing). ## Items | Name | Value | Description | |------|-------|-------------| | `None` | 0 | Default sizing behavior. | | `X` | 1 | Automatically resize element along the **X** axis to fit child contents. | | `Y` | 2 | Automatically resize element along the **Y** axis to fit child contents. Text objects will only resize along the **Y** axis if their `TextWrapped` property is enabled. | | `XY` | 3 | Automatically resize element along the **X** and **Y** axes to fit child contents. | --- name: AvatarAssetType last_updated: 2026-06-29T19:34:08Z type: enum summary: "Describes the asset types that an avatar can equip." --- # AvatarAssetType Describes the asset types that an avatar can equip. **Type:** enum ## Description Describes the asset types that an avatar can equip. See [AssetType](/docs/reference/engine/enums/AssetType.md) for all asset types. ## Items | Name | Value | Description | |------|-------|-------------| | `TShirt` | 2 | A classic 2D TShirt. | | `Hat` | 8 | A hat. | | `Shirt` | 11 | A classic 2D shirt. | | `Pants` | 12 | A classic 2D pants. | | `Head` | 17 | A head. | | `Face` | 18 | A face. | | `Gear` | 19 | A gear. | | `Torso` | 27 | A torso. | | `RightArm` | 28 | A right arm. | | `LeftArm` | 29 | A left arm. | | `LeftLeg` | 30 | A left leg. | | `RightLeg` | 31 | A right leg. | | `HairAccessory` | 41 | A hair accessory. | | `FaceAccessory` | 42 | A face accessory. | | `NeckAccessory` | 43 | A neck accessory. | | `ShoulderAccessory` | 44 | A shoulder accessory. | | `FrontAccessory` | 45 | A front accessory. | | `BackAccessory` | 46 | A back accessory. | | `WaistAccessory` | 47 | A waist accessory. | | `ClimbAnimation` | 48 | A climb animation. | | `FallAnimation` | 50 | A fall animation. | | `IdleAnimation` | 51 | An idle animation. | | `JumpAnimation` | 52 | A jump animation. | | `RunAnimation` | 53 | A run animation. | | `SwimAnimation` | 54 | A swim animation. | | `WalkAnimation` | 55 | A walk animation. | | `EmoteAnimation` | 61 | An emote animation. | | `TShirtAccessory` | 64 | A layered clothing TShirt. | | `ShirtAccessory` | 65 | A layered clothing shirt. | | `PantsAccessory` | 66 | A layered clothing pants. | | `JacketAccessory` | 67 | A layered clothing jacket. | | `SweaterAccessory` | 68 | A layered clothing sweater. | | `ShortsAccessory` | 69 | A layered clothing shorts. | | `LeftShoeAccessory` | 70 | A layered clothing left shoe. | | `RightShoeAccessory` | 71 | A layered clothing right shoe. | | `DressSkirtAccessory` | 72 | A layered clothing dress or skirt. | | `EyebrowAccessory` | 76 | | | `EyelashAccessory` | 77 | | | `MoodAnimation` | 78 | | | `DynamicHead` | 79 | | | `FaceMakeup` | 88 | | | `LipMakeup` | 89 | | | `EyeMakeup` | 90 | | --- name: AvatarChatServiceFeature last_updated: 2026-06-29T19:34:08Z type: enum --- # AvatarChatServiceFeature **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `None` | 0 | | | `UniverseAudio` | 1 | | | `UniverseVideo` | 2 | | | `PlaceAudio` | 4 | | | `PlaceVideo` | 8 | | | `UserAudioEligible` | 16 | | | `UserAudio` | 32 | | | `UserVideoEligible` | 64 | | | `UserVideo` | 128 | | | `UserBanned` | 256 | | | `UserVerifiedForVoice` | 512 | | --- name: AvatarContextMenuOption last_updated: 2026-06-29T19:34:08Z type: enum --- # AvatarContextMenuOption **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Friend` | 0 | | | `Chat` | 1 | | | `Emote` | 2 | | | `InspectMenu` | 3 | | --- name: AvatarItemType last_updated: 2026-06-29T19:34:08Z type: enum summary: "The type (Asset or Bundle) of avatar items." --- # AvatarItemType The type (Asset or Bundle) of avatar items. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Asset` | 1 | Avatar assets. | | `Bundle` | 2 | Avatar bundles. | --- name: AvatarPromptResult last_updated: 2026-06-29T19:34:08Z type: enum summary: "The result of prompt operations of AvatarEditorService." --- # AvatarPromptResult The result of prompt operations of [AvatarEditorService](/docs/reference/engine/classes/AvatarEditorService.md). **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Success` | 1 | The prompted action was completed. | | `PermissionDenied` | 2 | Denied permission to take the prompted action. | | `Failed` | 3 | Failure while trying to take the prompted action. | --- name: AvatarSettingsAccessoryLimitMethod last_updated: 2026-06-29T19:34:08Z type: enum --- # AvatarSettingsAccessoryLimitMethod **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Scale` | 0 | | | `Remove` | 1 | | | `PreviewScale` | 2 | | | `PreviewRemove` | 3 | | --- name: AvatarSettingsAccessoryMode last_updated: 2026-06-29T19:34:08Z type: enum --- # AvatarSettingsAccessoryMode **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `PlayerChoice` | 0 | | | `CustomLimit` | 1 | | --- name: AvatarSettingsAnimationClipsMode last_updated: 2026-06-29T19:34:08Z type: enum --- # AvatarSettingsAnimationClipsMode **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `PlayerChoice` | 0 | | | `CustomClips` | 1 | | --- name: AvatarSettingsAnimationPacksMode last_updated: 2026-06-29T19:34:08Z type: enum --- # AvatarSettingsAnimationPacksMode **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `PlayerChoice` | 0 | | | `StandardR15` | 1 | | | `StandardR6` | 2 | | --- name: AvatarSettingsAppearanceMode last_updated: 2026-06-29T19:34:08Z type: enum --- # AvatarSettingsAppearanceMode **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `PlayerChoice` | 0 | | | `CustomParts` | 1 | | | `CustomBody` | 2 | | --- name: AvatarSettingsBuildMode last_updated: 2026-06-29T19:34:08Z type: enum --- # AvatarSettingsBuildMode **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `PlayerChoice` | 0 | | | `CustomBuild` | 1 | | --- name: AvatarSettingsClothingMode last_updated: 2026-06-29T19:34:08Z type: enum --- # AvatarSettingsClothingMode **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `PlayerChoice` | 0 | | | `CustomLimit` | 1 | | --- name: AvatarSettingsCollisionMode last_updated: 2026-06-29T19:34:08Z type: enum --- # AvatarSettingsCollisionMode **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | | | `SingleCollider` | 1 | | | `Legacy` | 2 | | --- name: AvatarSettingsCustomAccessoryMode last_updated: 2026-06-29T19:34:08Z type: enum --- # AvatarSettingsCustomAccessoryMode **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `PlayerChoice` | 0 | | | `CustomAccessories` | 1 | | --- name: AvatarSettingsCustomBodyType last_updated: 2026-06-29T19:34:08Z type: enum --- # AvatarSettingsCustomBodyType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `AvatarReference` | 0 | | | `BundleId` | 1 | | --- name: AvatarSettingsCustomClothingMode last_updated: 2026-06-29T19:34:08Z type: enum --- # AvatarSettingsCustomClothingMode **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `PlayerChoice` | 0 | | | `CustomClothing` | 1 | | --- name: AvatarSettingsHitAndTouchDetectionMode last_updated: 2026-06-29T19:34:08Z type: enum --- # AvatarSettingsHitAndTouchDetectionMode **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `UseParts` | 0 | | | `UseCollider` | 1 | | --- name: AvatarSettingsJumpMode last_updated: 2026-06-29T19:34:08Z type: enum --- # AvatarSettingsJumpMode **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `JumpHeight` | 0 | | | `JumpPower` | 1 | | --- name: AvatarSettingsLegacyCollisionMode last_updated: 2026-06-29T19:34:08Z type: enum --- # AvatarSettingsLegacyCollisionMode **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `R6Colliders` | 0 | | | `InnerBoxColliders` | 1 | | --- name: AvatarSettingsScaleMode last_updated: 2026-06-29T19:34:08Z type: enum --- # AvatarSettingsScaleMode **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `PlayerChoice` | 0 | | | `CustomScale` | 1 | | --- name: AvatarThumbnailCustomizationType last_updated: 2026-06-29T19:34:08Z type: enum --- # AvatarThumbnailCustomizationType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Closeup` | 1 | | | `FullBody` | 2 | | --- name: AvatarUnificationMode last_updated: 2026-06-29T19:34:08Z type: enum summary: "Controls whether the R6 to R15 adapter is active, allowing R15 avatars to join R6 experiences." --- # AvatarUnificationMode Controls whether the R6 to R15 adapter is active, allowing R15 avatars to join R6 experiences. **Type:** enum ## Description When enabled, R15 avatars joining an R6 experience receive adapter parts, invisible [MeshParts](/docs/reference/engine/classes/MeshPart.md) with R6-compatible names welded to their corresponding R15 parts. The adapter preserves R6-like scale and movement while enabling R15 features such as layered clothing and animatable heads. Used by [Workspace.AvatarUnificationMode](/docs/reference/engine/classes/Workspace.md). ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | Uses the engine-default avatar unification state. | | `Disabled` | 1 | Avatar unification is disabled. | | `Enabled` | 2 | Avatar unification is enabled. | --- name: Axis last_updated: 2026-06-29T19:34:08Z type: enum summary: "One of the 3D axes." --- # Axis One of the 3D axes. **Type:** enum ## Description The Axis Enum represents the X, Y and Z axes. Primarily this Enum is used with [ArcHandles](/docs/reference/engine/classes/ArcHandles.md) and [Handles](/docs/reference/engine/classes/Handles.md). ## Items | Name | Value | Description | |------|-------|-------------| | `X` | 0 | The X axis. | | `Y` | 1 | The Y axis. | | `Z` | 2 | The Z axis. | --- name: BenefitType last_updated: 2026-06-29T19:34:08Z type: enum --- # BenefitType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `DeveloperProduct` | 0 | | | `AvatarAsset` | 1 | | | `AvatarBundle` | 2 | | --- name: BinType last_updated: 2026-06-29T19:34:08Z type: enum --- # BinType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Script` | 0 | | | `GameTool` | 1 | | | `Grab` | 2 | | | `Clone` | 3 | | | `Hammer` | 4 | | --- name: BodyPart last_updated: 2026-06-29T19:34:08Z type: enum summary: "The BodyPart Enum determines which BodyPart a CharacterMesh affects." --- # BodyPart The BodyPart Enum determines which BodyPart a [CharacterMesh](/docs/reference/engine/classes/CharacterMesh.md) affects. **Type:** enum ## Description The BodyPart Enum determines which BodyPart a [CharacterMesh](/docs/reference/engine/classes/CharacterMesh.md) affects. (the [CharacterMesh](/docs/reference/engine/classes/CharacterMesh.md) is used with R6 characters) ## Items | Name | Value | Description | |------|-------|-------------| | `Head` | 0 | The head BodyPart. | | `Torso` | 1 | The torso BodyPart. | | `LeftArm` | 2 | The left arm BodyPart. | | `RightArm` | 3 | The right arm BodyPart. | | `LeftLeg` | 4 | The left leg BodyPart. | | `RightLeg` | 5 | The right leg BodyPart. | --- name: BodyPartR15 last_updated: 2026-06-29T19:34:08Z type: enum summary: "BodyPartR15 is an enum with entries for each of the 15 body parts of an R15 character (plus an entry for the HumanoidRootPart)." --- # BodyPartR15 BodyPartR15 is an enum with entries for each of the 15 body parts of an R15 character (plus an entry for the HumanoidRootPart). **Type:** enum ## Description BodyPartR15 is an Enum used with both [Humanoid:GetBodyPartR15()](/docs/reference/engine/classes/Humanoid.md) and [Humanoid:ReplaceBodyPartR15()](/docs/reference/engine/classes/Humanoid.md) to find and replace specific body parts of R15 characters. ## Items | Name | Value | Description | |------|-------|-------------| | `Head` | 0 | Reference to a Humanoid's Head. | | `UpperTorso` | 1 | Reference to a Humanoid's UpperTorso. | | `LowerTorso` | 2 | Reference to a Humanoid's LowerTorso. | | `LeftFoot` | 3 | Reference to a Humanoid's LeftFoot. | | `LeftLowerLeg` | 4 | Reference to a Humanoid's LeftLowerLeg. | | `LeftUpperLeg` | 5 | Reference to a Humanoid's LeftUpperLeg. | | `RightFoot` | 6 | Reference to a Humanoid's RightFoot. | | `RightLowerLeg` | 7 | Reference to a Humanoid's RightLowerLeg. | | `RightUpperLeg` | 8 | Reference to a Humanoid's RightUpperLeg. | | `LeftHand` | 9 | Reference to a Humanoid's LeftHand. | | `LeftLowerArm` | 10 | Reference to a Humanoid's LeftLowerArm. | | `LeftUpperArm` | 11 | Reference to a Humanoid's LeftUpperArm. | | `RightHand` | 12 | Reference to a Humanoid's RightHand. | | `RightLowerArm` | 13 | Reference to a Humanoid's RightLowerArm. | | `RightUpperArm` | 14 | Reference to a Humanoid's RightUpperArm. | | `RootPart` | 15 | Reference to a Humanoid's HumanoidRootPart. | | `Unknown` | 17 | Unknown R15 Humanoid body part. | --- name: BorderMode last_updated: 2026-06-29T19:34:08Z type: enum summary: "Used by GuiObject.BorderMode to determine where borders are placed." --- # BorderMode Used by [GuiObject.BorderMode](/docs/reference/engine/classes/GuiObject.md) to determine where borders are placed. **Type:** enum ## Description Determines in what matter the border of a [GuiObject](/docs/reference/engine/classes/GuiObject.md) is laid out relative to its actual dimensions. It is used by the property of the same name, [GuiObject.BorderMode](/docs/reference/engine/classes/GuiObject.md). ## Items | Name | Value | Description | |------|-------|-------------| | `Outline` | 0 | As [GuiObject.BorderSizePixel](/docs/reference/engine/classes/GuiObject.md) increases, the border grows outward. The dimensions of the GuiObject's contents do not change. | | `Middle` | 1 | As [GuiObject.BorderSizePixel](/docs/reference/engine/classes/GuiObject.md) increases, the border grows evenly inward and outward. The dimensions of the GuiObject's contents are reduced at a 1:1 ratio. | | `Inset` | 2 | As [GuiObject.BorderSizePixel](/docs/reference/engine/classes/GuiObject.md) increases, the border grows evenly inward only. The dimensions of the GuiObject's contents are reduced at a 1:2 ratio. | --- name: BorderStrokePosition last_updated: 2026-06-29T19:34:08Z type: enum summary: "Used by UIStroke.BorderStrokePosition to determine the stroke's position on its parent's border." --- # BorderStrokePosition Used by [UIStroke.BorderStrokePosition](/docs/reference/engine/classes/UIStroke.md) to determine the stroke's position on its parent's border. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Outer` | 0 | The stroke renders on the outer border of the parent UI element. | | `Center` | 1 | The stroke renders on the center border of the parent UI element. | | `Inner` | 2 | The stroke renders on the inner border of the parent UI element. | --- name: BreakReason last_updated: 2026-06-29T19:34:08Z type: enum summary: "Reason for the breakpoint hit." --- # BreakReason Reason for the breakpoint hit. **Type:** enum ## Description Value for the reason the [ScriptDebugger.EncounteredBreak](/docs/reference/engine/classes/ScriptDebugger.md) signal fired. ## Items | Name | Value | Description | |------|-------|-------------| | `Other` | 0 | Pausing for a reason not covered by other values, for example if the user hit "Pause" button. | | `Error` | 1 | Pausing on error hit in the code. | | `SpecialBreakpoint` | 2 | Pausing on an internal breakpoint set by debugger command: e.g. step over, step into, step out of. | | `UserBreakpoint` | 3 | Pausing on a user breakpoint. | --- name: BreakpointRemoveReason last_updated: 2026-06-29T19:34:08Z type: enum summary: "Reason that a breakpoint was removed." --- # BreakpointRemoveReason Reason that a breakpoint was removed. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Requested` | 0 | Breakpoint removal was requested by user or a command. | | `ScriptChanged` | 1 | Script that contained the breakpoint has changed. | | `ScriptRemoved` | 2 | Script that contained the breakpoint was removed. | --- name: BulkMoveMode last_updated: 2026-06-29T19:34:08Z type: enum --- # BulkMoveMode **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `FireAllEvents` | 0 | | | `FireCFrameChanged` | 1 | | --- name: BundleType last_updated: 2026-06-29T19:34:08Z type: enum summary: "The type of a bundle of assets." --- # BundleType The type of a bundle of assets. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `BodyParts` | 1 | A bundle of body parts and accessories. | | `Animations` | 2 | A bundle of just animations. | | `Shoes` | 3 | A bundle of left shoe and right shoe. | | `DynamicHead` | 4 | A bundle consisting of dynamicHead and moodAnimation assets, optionally with eyebrowAccessory and eyelashAccessory. | | `DynamicHeadAvatar` | 5 | | --- name: Button last_updated: 2026-06-29T19:34:08Z type: enum summary: "One of the buttons used by Controller." --- # Button One of the buttons used by [Controller](/docs/reference/engine/classes/Controller.md). **Type:** enum ## Description Used for accessing buttons for use by the [Controller](/docs/reference/engine/classes/Controller.md). ## Items | Name | Value | Description | |------|-------|-------------| | `Dismount` | 8 | The Dismount button. | | `Jump` | 32 | The Jump button. | --- name: ButtonStyle last_updated: 2026-06-29T19:34:08Z type: enum summary: "Used by GuiButton.Style to set a special hardcoded appearance." --- # ButtonStyle Used by [GuiButton.Style](/docs/reference/engine/classes/GuiButton.md) to set a special hardcoded appearance. **Type:** enum ## Description Used by [GuiButton.Style](/docs/reference/engine/classes/GuiButton.md) to set a special hardcoded appearance. These should generally be avoided in favor of setting other appearance properties, but they can be useful for wireframing. Despite the Roblox naming, these are not used by any official Roblox UI. ## Items | Name | Value | Description | |------|-------|-------------| | `Custom` | 0 | The button will respect the [GuiObject.BorderColor3](/docs/reference/engine/classes/GuiObject.md) and [GuiObject.BackgroundColor3](/docs/reference/engine/classes/GuiObject.md) properties. | | `RobloxButtonDefault` | 1 | A black background with a red border, and rounded corners. | | `RobloxButton` | 2 | A black background with a grey border, and rounded corners. | | `RobloxRoundButton` | 3 | A grey background with rounded corners, and a slight drop shadow. | | `RobloxRoundDefaultButton` | 4 | A blue background with rounded corners, and a slight drop shadow. | | `RobloxRoundDropdownButton` | 5 | A white background with a thin grey border, rounded corners, and a slight drop shadow. | --- name: CageType last_updated: 2026-06-29T19:34:08Z type: enum --- # CageType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Inner` | 0 | | | `Outer` | 1 | | --- name: CameraMode last_updated: 2026-06-29T19:34:08Z type: enum summary: "Used to set Player.CameraMode to determine when first person and third person cameras should be used." --- # CameraMode Used to set [Player.CameraMode](/docs/reference/engine/classes/Player.md) to determine when first person and third person cameras should be used. **Type:** enum ## Description The **CameraMode** enum is used to set [Player.CameraMode](/docs/reference/engine/classes/Player.md) to determine when first person and third person cameras should be used. #### Third Person In the default third person mode ([Classic](/docs/reference/engine/enums/CameraMode.md)), the character can be seen in the camera. While in this mode, the default behavior is: - Players can right-click and drag (mouse), tap and drag (mobile), use the secondary thumbstick (gamepad), or press the left/right arrows (keyboard) to rotate the camera around their character. - When a player moves their character, it faces in the corresponding movement direction. - Players can zoom in and out freely, even to first person on full zoom in. #### First Person In first person mode ([LockFirstPerson](/docs/reference/engine/enums/CameraMode.md)), the player's camera is zoomed all the way in. Unless there is a visible GUI present with the [GuiButton.Modal](/docs/reference/engine/classes/GuiButton.md) property set to `true`, moving the mouse, tap-dragging on mobile, or using the secondary thumbstick on a gamepad will rotate the camera around the character. ## Items | Name | Value | Description | |------|-------|-------------| | `Classic` | 0 | Third person camera which can be zoomed into first person. | | `LockFirstPerson` | 1 | Exclusively first person camera. | --- name: CameraNavigationModel last_updated: 2026-06-29T19:34:08Z type: enum --- # CameraNavigationModel **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Roblox` | 0 | | | `IndustryCompatible` | 1 | | --- name: CameraPanMode last_updated: 2026-06-29T19:34:08Z type: enum summary: "The CameraPanMode Enum represents the available pan modes for Camera:SetCameraPanMode()." --- # CameraPanMode The CameraPanMode Enum represents the available pan modes for [Camera:SetCameraPanMode()](/docs/reference/engine/classes/Camera.md). **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Classic` | 0 | Enables swipe and pan on mobile devices, and disables edge bump camera controls. Default. | | `EdgeBump` | 1 | Disables swipe to pan on mobile devices, and enables edge bump camera controls. It does not affect Windows or Mac users. | --- name: CameraSpeedAdjustBinding last_updated: 2026-06-29T19:34:08Z type: enum --- # CameraSpeedAdjustBinding **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `None` | 0 | | | `RmbScroll` | 1 | | | `AltScroll` | 2 | | --- name: CameraType last_updated: 2026-06-29T19:34:08Z type: enum summary: "Describes the camera behavior mode if using the default PlayerScripts." --- # CameraType Describes the camera behavior mode if using the default PlayerScripts. **Type:** enum ## Description The CameraType Enum is used in [Camera.CameraType](/docs/reference/engine/classes/Camera.md) to set the behavior of the Camera object. Attach, Watch, Track, and Follow all require a valid [Camera.CameraSubject](/docs/reference/engine/classes/Camera.md) to work properly. ## Items | Name | Value | Description | |------|-------|-------------| | `Fixed` | 0 | Camera is stationary. | | `Attach` | 1 | Camera moves with the subject at a fixed offset and will rotate as the subject rotates. | | `Watch` | 2 | Camera is stationary but will rotate to keep the subject in the center of the screen. | | `Track` | 3 | Camera moves with the subject but does not rotate automatically. | | `Follow` | 4 | Camera moves with the subject and rotates to keep the subject in the center. | | `Custom` | 5 | Default mode used by Roblox core scripts. | | `Scriptable` | 6 | No default behavior. Used when developers need to script custom behavior. | | `Orbital` | 7 | The camera has a fixed Y position, but can be rotated around the player. | --- name: CaptureGalleryPermission last_updated: 2026-06-29T19:34:08Z type: enum --- # CaptureGalleryPermission **Type:** enum ## Description Used to specify the scope of permissions for accessing the user's capture gallery. ## Items | Name | Value | Description | |------|-------|-------------| | `ReadAndUpload` | 0 | | --- name: CaptureType last_updated: 2026-06-29T19:34:08Z type: enum --- # CaptureType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Screenshot` | 1 | | | `Video` | 2 | | --- name: CatalogCategoryFilter last_updated: 2026-06-29T19:34:08Z type: enum --- # CatalogCategoryFilter **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `None` | 1 | | | `Featured` | 2 | | | `Collectibles` | 3 | | | `CommunityCreations` | 4 | | | `Premium` | 5 | | | `Recommended` | 6 | | --- name: CatalogSortAggregation last_updated: 2026-06-29T19:34:08Z type: enum --- # CatalogSortAggregation **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Past12Hours` | 1 | | | `PastDay` | 2 | | | `Past3Days` | 3 | | | `PastWeek` | 4 | | | `PastMonth` | 5 | | | `AllTime` | 6 | | --- name: CatalogSortType last_updated: 2026-06-29T19:34:08Z type: enum --- # CatalogSortType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Relevance` | 1 | | | `PriceHighToLow` | 2 | | | `PriceLowToHigh` | 3 | | | `MostFavorited` | 5 | | | `RecentlyCreated` | 6 | | | `Bestselling` | 7 | | --- name: CellBlock last_updated: 2026-06-29T19:34:08Z type: enum --- # CellBlock **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Solid` | 0 | | | `VerticalWedge` | 1 | | | `CornerWedge` | 2 | | | `InverseCornerWedge` | 3 | | | `HorizontalWedge` | 4 | | --- name: CellMaterial last_updated: 2026-06-29T19:34:08Z type: enum --- # CellMaterial **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Empty` | 0 | | | `Grass` | 1 | | | `Sand` | 2 | | | `Brick` | 3 | | | `Granite` | 4 | | | `Asphalt` | 5 | | | `Iron` | 6 | | | `Aluminum` | 7 | | | `Gold` | 8 | | | `WoodPlank` | 9 | | | `WoodLog` | 10 | | | `Gravel` | 11 | | | `CinderBlock` | 12 | | | `MossyStone` | 13 | | | `Cement` | 14 | | | `RedPlastic` | 15 | | | `BluePlastic` | 16 | | | `Water` | 17 | | --- name: CellOrientation last_updated: 2026-06-29T19:34:08Z type: enum --- # CellOrientation **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `NegZ` | 0 | | | `X` | 1 | | | `Z` | 2 | | | `NegX` | 3 | | --- name: CenterDialogType last_updated: 2026-06-29T19:34:08Z type: enum --- # CenterDialogType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `UnsolicitedDialog` | 1 | | | `PlayerInitiatedDialog` | 2 | | | `ModalDialog` | 3 | | | `QuitDialog` | 4 | | --- name: CharacterControlMode last_updated: 2026-06-29T19:34:08Z type: enum --- # CharacterControlMode **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | | | `Legacy` | 1 | | | `NoCharacterController` | 2 | | | `LuaCharacterController` | 3 | | --- name: ChatCallbackType last_updated: 2026-06-29T19:34:08Z type: enum --- # ChatCallbackType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `OnCreatingChatWindow` | 1 | | | `OnClientSendingMessage` | 2 | | | `OnClientFormattingMessage` | 3 | | | `OnServerReceivingMessage` | 17 | | --- name: ChatColor last_updated: 2026-06-29T19:34:08Z type: enum summary: "Describes the chat color." --- # ChatColor Describes the chat color. **Type:** enum ## Description The **ChatColor** enum is used to change the color of a chat using [Chat](/docs/reference/engine/classes/Chat.md). ## Items | Name | Value | Description | |------|-------|-------------| | `Blue` | 0 | Blue chat color. | | `Green` | 1 | Green chat color. | | `Red` | 2 | Red chat color. | | `White` | 3 | White chat color. | --- name: ChatMode last_updated: 2026-06-29T19:34:08Z type: enum --- # ChatMode **Type:** enum ## Description Controls the methods of communication available to a player. ## Items | Name | Value | Description | |------|-------|-------------| | `Menu` | 0 | The player is restricted to chatting via a menu of chat messages. | | `TextAndMenu` | 1 | A player can chat via custom text messages and a menu of chat messages. | --- name: ChatPrivacyMode last_updated: 2026-06-29T19:34:08Z type: enum --- # ChatPrivacyMode **Type:** enum ## Description Internally controls the user's chat privacy settings. ## Items | Name | Value | Description | |------|-------|-------------| | `AllUsers` | 0 | A player can chat with all users in a game. | | `NoOne` | 1 | A player cannot chat with any other users in a game. | | `Friends` | 2 | A player can only chat with users in a game that are on their friends list. | --- name: ChatRestrictionStatus last_updated: 2026-06-29T19:34:08Z type: enum --- # ChatRestrictionStatus **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Unknown` | 0 | | | `NotRestricted` | 1 | | | `Restricted` | 2 | | --- name: ChatStyle last_updated: 2026-06-29T19:34:08Z type: enum summary: "The ChatStyle Enum is used to set the style of Chat used in a game via the Players:SetChatStyle() method." --- # ChatStyle The ChatStyle Enum is used to set the style of [Chat](/docs/reference/engine/classes/Chat.md) used in a game via the [Players:SetChatStyle()](/docs/reference/engine/classes/Players.md) method. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Classic` | 0 | Displays chats in the chat user interface at the top-left corner of the screen. | | `Bubble` | 1 | Displays chats in a bubble above the sender's head. | | `ClassicAndBubble` | 2 | Has the effects of Classic and Bubble combined. | --- name: ChatVersion last_updated: 2026-06-29T19:34:08Z type: enum summary: "Determines whether TextChatService should be fully enabled or to allow the legacy system." --- # ChatVersion Determines whether [TextChatService](/docs/reference/engine/classes/TextChatService.md) should be fully enabled or to allow the legacy system. **Type:** enum ## Description Determines whether [TextChatService](/docs/reference/engine/classes/TextChatService.md) should be fully enabled or to allow the legacy system. [TextChatService.ChatVersion](/docs/reference/engine/classes/TextChatService.md) is not scriptable and must be set in Studio. ## Items | Name | Value | Description | |------|-------|-------------| | `LegacyChatService` | 0 | Enables the legacy chat system. | | `TextChatService` | 1 | Enables [TextChatService](/docs/reference/engine/classes/TextChatService.md) chat and prevents legacy chat system behavior. | --- name: ClientAnimatorThrottlingMode last_updated: 2026-06-29T19:34:08Z type: enum summary: "An Enum describing various animation throttling modes on clients." --- # ClientAnimatorThrottlingMode An Enum describing various animation throttling modes on clients. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | The default animation throttling mode defined by Roblox. | | `Disabled` | 1 | Disables animation throttling. | | `Enabled` | 2 | Enables animation throttling. | --- name: CloseReason last_updated: 2026-06-29T19:34:08Z type: enum summary: "Specifies the reason for the experience server shutdown." --- # CloseReason Specifies the reason for the experience server shutdown. **Type:** enum ## Description Specifies the reason for the experience server shutdown. This enum value is the first parameter passed to functions bound by [BindToClose()](/docs/reference/engine/classes/DataModel.md). The **DeveloperShutdown** value must always be passed to functions bound by [BindToClose()](/docs/reference/engine/classes/DataModel.md) when they're called in Studio. ## Items | Name | Value | Description | |------|-------|-------------| | `Unknown` | 0 | The server shut down for an unknown reason. | | `RobloxMaintenance` | 1 | The server shut down for maintenance. | | `DeveloperShutdown` | 2 | The experience developer has shut down the server, or functions bound by [BindToClose()](/docs/reference/engine/classes/DataModel.md) have been called inside Studio. | | `DeveloperUpdate` | 3 | The experience developer has migrated the server to a new place version. | | `ServerEmpty` | 4 | The last player has left and the experience is empty. | | `OutOfMemory` | 5 | The experience has hit the memory limit for the game server. | --- name: CollaboratorStatus last_updated: 2026-06-29T19:34:08Z type: enum --- # CollaboratorStatus **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `None` | 0 | | | `Editing3D` | 1 | | | `Scripting` | 2 | | | `PrivateScripting` | 3 | | --- name: CollisionFidelity last_updated: 2026-06-29T19:34:08Z type: enum summary: "Determines behavior of the collision hitbox for MeshPart and PartOperation instances." --- # CollisionFidelity Determines behavior of the collision hitbox for [MeshPart](/docs/reference/engine/classes/MeshPart.md) and [PartOperation](/docs/reference/engine/classes/PartOperation.md) instances. **Type:** enum ## Description Determines behavior of the collision hitbox for [MeshPart](/docs/reference/engine/classes/MeshPart.md) and [PartOperation](/docs/reference/engine/classes/PartOperation.md) instances. See [here](/docs/en-us/workspace/collisions.md#collision-fidelity) for a visual representation of the various options. ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | Collision model uses a voxel-based convex-hull decomposition that is relatively fast but may not be highly accurate, especially for holes, doorways, and cavities in general. | | `Hull` | 1 | Collision model uses the convex hull of the visual mesh. | | `Box` | 2 | Collision model uses a bounding box that encompasses the visual mesh. | | `PreciseConvexDecomposition` | 3 | Collison model uses a convex-hull decomposition of the visual mesh which is computed by a variation of an algorithm called HACD. This option is the most accurate representation of collision geometry for complex objects with built-in tolerances to reduce the total number of convex-hulls by compromising on accuracy when justified. | --- name: CommandPermission last_updated: 2026-06-29T19:34:08Z type: enum --- # CommandPermission **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Plugin` | 0 | | | `LocalUser` | 1 | | --- name: CompileTarget last_updated: 2026-06-29T19:34:08Z type: enum --- # CompileTarget **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Client` | 0 | | | `CoreScript` | 1 | | | `Studio` | 2 | | | `CoreScriptRaw` | 3 | | --- name: CompletionAcceptanceBehavior last_updated: 2026-06-29T19:34:08Z type: enum --- # CompletionAcceptanceBehavior **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Insert` | 0 | | | `Replace` | 1 | | | `ReplaceOnEnterInsertOnTab` | 2 | | | `InsertOnEnterReplaceOnTab` | 3 | | --- name: CompletionItemKind last_updated: 2026-06-29T19:34:08Z type: enum --- # CompletionItemKind **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Text` | 1 | | | `Method` | 2 | | | `Function` | 3 | | | `Constructor` | 4 | | | `Field` | 5 | | | `Variable` | 6 | | | `Class` | 7 | | | `Interface` | 8 | | | `Module` | 9 | | | `Property` | 10 | | | `Unit` | 11 | | | `Value` | 12 | | | `Enum` | 13 | | | `Keyword` | 14 | | | `Snippet` | 15 | | | `Color` | 16 | | | `File` | 17 | | | `Reference` | 18 | | | `Folder` | 19 | | | `EnumMember` | 20 | | | `Constant` | 21 | | | `Struct` | 22 | | | `Event` | 23 | | | `Operator` | 24 | | | `TypeParameter` | 25 | | --- name: CompletionItemTag last_updated: 2026-06-29T19:34:08Z type: enum summary: "Determines the tags for completion items in ScriptEditorService:RegisterAutocompleteCallback()." --- # CompletionItemTag Determines the tags for completion items in [ScriptEditorService:RegisterAutocompleteCallback()](/docs/reference/engine/classes/ScriptEditorService.md). **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Deprecated` | 1 | Indicates that the completion is deprecated, applying a penalty towards its score and crossing it out in the completion list. | | `IncorrectIndexType` | 2 | Indicates that the completion is a member and being accessed with the wrong index type (e.g. using `:` to access a property), hiding it from the completion list. | | `PluginPermissions` | 3 | Indicates that the completion requires Plugin security level to access, hiding it if the client lacks this level. | | `CommandLinePermissions` | 4 | Indicates that the completion requires Command line security level to access, hiding it if the client lacks this level. | | `RobloxPermissions` | 5 | Indicates that the completion requires core script security level to access, hiding it if the client lacks this level. | | `AddParens` | 6 | Instructs the editor to add parentheses before the cursor after inserting the given completion, if accepted. | | `PutCursorInParens` | 7 | Instructs the editor to add parentheses before the cursor and move the cursor into the parentheses after inserting the given completion, if accepted. | | `TypeCorrect` | 8 | Indicates that the completion is type correct in the current context, granting a bonus to its score. | | `ClientServerBoundaryViolation` | 9 | Indicates that the completion is being accessed from the wrong side of the client/server boundary (e.g. Players.LocalPlayer in a server script), hiding it from the completion list. | | `Invalidated` | 10 | | | `PutCursorBeforeEnd` | 11 | | --- name: CompletionTriggerKind last_updated: 2026-06-29T19:34:08Z type: enum --- # CompletionTriggerKind **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Invoked` | 1 | | | `TriggerCharacter` | 2 | | | `TriggerForIncompleteCompletions` | 3 | | --- name: CompositeValueCurveType last_updated: 2026-06-29T19:34:08Z type: enum summary: "Describes the type of value animated by a CompositeValueCurve." --- # CompositeValueCurveType Describes the type of value animated by a [CompositeValueCurve](/docs/reference/engine/classes/CompositeValueCurve.md). **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `ColorRGB` | 0 | The [CompositeValueCurve](/docs/reference/engine/classes/CompositeValueCurve.md) will animate children of type [FloatCurve](/docs/reference/engine/classes/FloatCurve.md) named `"R"`, `"G"`, and `"B"` to animate the corresponding components of the [Color3](/docs/reference/engine/datatypes/Color3.md) value returned by the method [CompositeValueCurve:GetValueAtTime()](/docs/reference/engine/classes/CompositeValueCurve.md). | | `ColorHSV` | 1 | The [CompositeValueCurve](/docs/reference/engine/classes/CompositeValueCurve.md) will animate children of type [FloatCurve](/docs/reference/engine/classes/FloatCurve.md) named `"H"`, `"S"`, and `"V"` to animate hue, saturation, and value of a color that will be converted to RGB before returning a [Color3](/docs/reference/engine/datatypes/Color3.md) value from the method [CompositeValueCurve:GetValueAtTime()](/docs/reference/engine/classes/CompositeValueCurve.md). | | `NumberRange` | 2 | The [CompositeValueCurve](/docs/reference/engine/classes/CompositeValueCurve.md) will animate children of type [FloatCurve](/docs/reference/engine/classes/FloatCurve.md) named `"Min"` and `"Max"` to animate the corresponding components of the [NumberRange](/docs/reference/engine/datatypes/NumberRange.md) value returned by the method [CompositeValueCurve:GetValueAtTime()](/docs/reference/engine/classes/CompositeValueCurve.md). | | `Rect` | 3 | The [CompositeValueCurve](/docs/reference/engine/classes/CompositeValueCurve.md) will animate children of type [FloatCurve](/docs/reference/engine/classes/FloatCurve.md) named `"MinX"`, `"MaxX"`, `"MinY"`, and `"MaxY"` to animate the corresponding components of the [Rect](/docs/reference/engine/datatypes/Rect.md) value returned by the method [CompositeValueCurve:GetValueAtTime()](/docs/reference/engine/classes/CompositeValueCurve.md). | | `UDim` | 4 | The [CompositeValueCurve](/docs/reference/engine/classes/CompositeValueCurve.md) will animate children of type [FloatCurve](/docs/reference/engine/classes/FloatCurve.md) named `"Scale"` and `"Offset"` to animate the corresponding components of the [UDim](/docs/reference/engine/datatypes/UDim.md) value returned by the method [CompositeValueCurve:GetValueAtTime()](/docs/reference/engine/classes/CompositeValueCurve.md). | | `UDim2` | 5 | The [CompositeValueCurve](/docs/reference/engine/classes/CompositeValueCurve.md) will animate children of type [FloatCurve](/docs/reference/engine/classes/FloatCurve.md) named `"ScaleX"`, `"OffsetX"`, `"ScaleY"`, and `"OffsetY"` to animate the corresponding components of the [UDim2](/docs/reference/engine/datatypes/UDim2.md) value returned by the method [CompositeValueCurve:GetValueAtTime()](/docs/reference/engine/classes/CompositeValueCurve.md). | | `Vector2` | 6 | The [CompositeValueCurve](/docs/reference/engine/classes/CompositeValueCurve.md) will animate children of type [FloatCurve](/docs/reference/engine/classes/FloatCurve.md) named `"X"` and `"Y"` to animate the corresponding components of the [Vector2](/docs/reference/engine/datatypes/Vector2.md) value returned by the method [CompositeValueCurve:GetValueAtTime()](/docs/reference/engine/classes/CompositeValueCurve.md). | | `Vector3` | 7 | The [CompositeValueCurve](/docs/reference/engine/classes/CompositeValueCurve.md) will animate children of type [FloatCurve](/docs/reference/engine/classes/FloatCurve.md) named `"X"`, `"Y"`, and `"Z"` to animate the corresponding components of the [Vector3](/docs/reference/engine/datatypes/Vector3.md) value returned by the method [CompositeValueCurve:GetValueAtTime()](/docs/reference/engine/classes/CompositeValueCurve.md). | --- name: CompressionAlgorithm last_updated: 2026-06-29T19:34:08Z type: enum summary: "A compression algorithm to use in EncodingService methods." --- # CompressionAlgorithm A compression algorithm to use in [EncodingService](/docs/reference/engine/classes/EncodingService.md) methods. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Zstd` | 0 | Used to perform compression using [Zstandard](https://en.wikipedia.org/wiki/Zstd) compression, also known as zstd. | --- name: ComputerCameraMovementMode last_updated: 2026-06-29T19:34:08Z type: enum summary: "The camera movement mode currently in-use by the client." --- # ComputerCameraMovementMode The camera movement mode currently in-use by the client. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | The default camera movement mode is classic. | | `Classic` | 1 | Camera tracks the player but will not automatically rotate if the player walks left or right. | | `Follow` | 2 | Camera tracks the player and automatically rotates if the player walks left or right. | | `Orbital` | 3 | The camera has a fixed Y position, but can be rotated around the player. | | `CameraToggle` | 4 | The camera toggles between locked and free rotation with the right mouse button. | --- name: ComputerMovementMode last_updated: 2026-06-29T19:34:08Z type: enum summary: "The computer movement type in-use by the client." --- # ComputerMovementMode The computer movement type in-use by the client. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | The default is KeyboardMouse. | | `KeyboardMouse` | 1 | The player's character is controlled using the keyboard and mouse. | | `ClickToMove` | 2 | The player can right-click in the game world and their character will move there. In addition, the player can also control their character with their mouse and keyboard. | --- name: ConfigSnapshotErrorState last_updated: 2026-06-29T19:34:08Z type: enum --- # ConfigSnapshotErrorState **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `None` | 0 | | | `LoadFailed` | 1 | | --- name: ConnectionError last_updated: 2026-06-29T19:34:08Z type: enum --- # ConnectionError **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `OK` | 0 | | | `Unknown` | 1 | | | `ConnectErrors` | 2 | | | `AlreadyConnected` | 3 | | | `NoFreeIncomingConnections` | 4 | | | `ConnectionBanned` | 5 | | | `InvalidPassword` | 6 | | | `IncompatibleProtocolVersion` | 7 | | | `IPRecentlyConnected` | 8 | | | `OurSystemRequiresSecurity` | 9 | | | `SecurityKeyMismatch` | 10 | | | `DisconnectErrors` | 256 | | | `DisconnectBadhash` | 257 | | | `DisconnectSecurityKeyMismatch` | 258 | | | `DisconnectProtocolMismatch` | 259 | | | `DisconnectReceivePacketError` | 260 | | | `DisconnectReceivePacketStreamError` | 261 | | | `DisconnectSendPacketError` | 262 | | | `DisconnectIllegalTeleport` | 263 | | | `DisconnectDuplicatePlayer` | 264 | | | `DisconnectDuplicateTicket` | 265 | | | `DisconnectTimeout` | 266 | | | `DisconnectLuaKick` | 267 | | | `DisconnectOnRemoteSysStats` | 268 | | | `DisconnectHashTimeout` | 269 | | | `DisconnectCloudEditKick` | 270 | | | `DisconnectPlayerless` | 271 | | | `DisconnectNewSecurityKeyMismatch` | 272 | | | `DisconnectEvicted` | 273 | | | `DisconnectDevMaintenance` | 274 | | | `DisconnectRobloxMaintenance` | 275 | | | `DisconnectRejoin` | 276 | | | `DisconnectConnectionLost` | 277 | | | `DisconnectIdle` | 278 | | | `DisconnectRaknetErrors` | 279 | | | `DisconnectWrongVersion` | 280 | | | `DisconnectBySecurityPolicy` | 281 | | | `DisconnectBlockedIP` | 282 | | | `DisconnectClientFailure` | 284 | | | `DisconnectClientRequest` | 285 | | | `DisconnectPrivateServerKickout` | 286 | | | `DisconnectModeratedGame` | 287 | | | `ServerShutdown` | 288 | | | `ReplicatorTimeout` | 290 | | | `PlayerRemoved` | 291 | | | `DisconnectOutOfMemoryKeepPlayingLeave` | 292 | | | `DisconnectRomarkEndOfTest` | 293 | | | `DisconnectCollaboratorPermissionRevoked` | 294 | | | `DisconnectCollaboratorUnderage` | 295 | | | `NetworkInternal` | 296 | | | `NetworkSend` | 297 | | | `NetworkTimeout` | 298 | | | `NetworkMisbehavior` | 299 | | | `NetworkSecurity` | 300 | | | `ReplacementReady` | 301 | | | `ServerEmpty` | 302 | | | `PhantomFreeze` | 303 | | | `AndroidAnticheatKick` | 304 | | | `AndroidEmulatorKick` | 305 | | | `AndroidRootedKick` | 306 | | | `ScreentimeLockoutKick` | 307 | | | `DisconnectionNotification` | 308 | | | `DisconnectVerboselyModeratedGame` | 309 | | | `DisconnectCollaboratorNotAgeVerified` | 310 | | | `DisconnectCollaboratorTrustedConnectionsRequired` | 311 | | | `DisconnectCollaboratorOwnerActionRequired` | 312 | | | `DisconnectCollaboratorTooManyCollaborators` | 313 | | | `DisconnectCollaboratorUnknownError` | 314 | | | `PlacelaunchErrors` | 512 | | | `PlacelaunchDisabled` | 515 | | | `PlacelaunchError` | 516 | | | `PlacelaunchGameEnded` | 517 | | | `PlacelaunchGameFull` | 518 | | | `PlacelaunchUserLeft` | 522 | | | `PlacelaunchRestricted` | 523 | | | `PlacelaunchUnauthorized` | 524 | | | `PlacelaunchFlooded` | 525 | | | `PlacelaunchHashExpired` | 526 | | | `PlacelaunchHashException` | 527 | | | `PlacelaunchPartyCannotFit` | 528 | | | `PlacelaunchHttpError` | 529 | | | `PlacelaunchUserPrivacyUnauthorized` | 533 | | | `PlacelaunchVipOwnerNotPresent` | 541 | | | `PlacelaunchAgeVerificationRequired` | 542 | | | `PlacelaunchParentalApprovalRequired` | 543 | | | `PlacelaunchCoreGated` | 544 | | | `PlacelaunchCreatorBan` | 600 | | | `PlacelaunchDeviceBlock` | 601 | | | `PlacelaunchCustomMessage` | 610 | | | `PlacelaunchOtherError` | 611 | | | `TeleportErrors` | 768 | | | `TeleportFailure` | 769 | | | `TeleportGameNotFound` | 770 | | | `TeleportGameEnded` | 771 | | | `TeleportGameFull` | 772 | | | `TeleportUnauthorized` | 773 | | | `TeleportFlooded` | 774 | | | `TeleportIsTeleporting` | 775 | | --- name: ConnectionState last_updated: 2026-06-29T19:34:08Z type: enum summary: "Used to determine the connection state of the client to the game server." --- # ConnectionState Used to determine the connection state of the client to the game server. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Connected` | 0 | The client is connected. | | `Disconnected` | 1 | The client is disconnected. | --- name: ContentSourceType last_updated: 2026-06-29T19:34:08Z type: enum summary: "The source type of a Content value." --- # ContentSourceType The source type of a [Content](/docs/reference/engine/datatypes/Content.md) value. **Type:** enum ## Description The source type of [Content](/docs/reference/engine/datatypes/Content.md) value. Indicates which properties of a [Content](/docs/reference/engine/datatypes/Content.md) contain the referenced content. ## Items | Name | Value | Description | |------|-------|-------------| | `None` | 0 | Empty value with no source type. Does not reference any content or hold any non‑`nil` value. | | `Uri` | 1 | An [asset URI](/docs/en-us/projects/assets.md#asset-uris) `string` value contained in [Content.Uri](/docs/reference/engine/datatypes/Content.md). | | `Object` | 2 | A non-`nil` [Object](/docs/reference/engine/classes/Object.md) reference contained in [Content.Object](/docs/reference/engine/datatypes/Content.md). | | `Opaque` | 3 | A non-`nil` `Opaque` reference contained in [Content.Opaque](/docs/reference/engine/datatypes/Content.md). | --- name: ContextActionPriority last_updated: 2026-06-29T19:34:08Z type: enum summary: "Describes the priority of a context action." --- # ContextActionPriority Describes the priority of a context action. **Type:** enum ## Description The ContextActionPriority is used to set the priority of a context action. This priority is used to determine context action order. ## Items | Name | Value | Description | |------|-------|-------------| | `Low` | 1000 | Low priority. | | `Medium` | 2000 | Medium priority. | | `High` | 3000 | High priority. | --- name: ContextActionResult last_updated: 2026-06-29T19:34:08Z type: enum summary: "Describes whether a contextual action should sink or pass input events." --- # ContextActionResult Describes whether a contextual action should sink or pass input events. **Type:** enum ## Description `ContextActionResult` controls the behavior of multiple bound actions. It gives the option of controlling whether or not a bound action should sink or pass the input event, meaning other things (including other bound actions) can process it. ## Items | Name | Value | Description | |------|-------|-------------| | `Sink` | 0 | If `functionToBind` from [ContextActionService:BindAction()](/docs/reference/engine/classes/ContextActionService.md) returns [ContextActionResult.Sink](/docs/reference/engine/enums/ContextActionResult.md), the input event will stop at that function and no other bound actions under it will be processed. This is the default behavior if `functionToBind` does not return anything or yields in any way. | | `Pass` | 1 | If `functionToBind` from [ContextActionService:BindAction()](/docs/reference/engine/classes/ContextActionService.md) returns [ContextActionResult.Pass](/docs/reference/engine/enums/ContextActionResult.md), the input event is considered to have not been handled by `functionToBind` and will continue being passed to actions bound to the same input type. | --- name: ControlMode last_updated: 2026-06-29T19:34:08Z type: enum summary: "The ControlMode Enum sets how the player is controlled." --- # ControlMode The ControlMode Enum sets how the player is controlled. **Type:** enum ## Description The ControlMode Enum sets how the player is controlled. Note that shift-lock related APIs are in the process of being deprecated, so it's recommended to use [UserInputService.MouseBehavior](/docs/reference/engine/classes/UserInputService.md) instead to lock the mouse. ## Items | Name | Value | Description | |------|-------|-------------| | `Classic` | 0 | Allows the camera to be moved by clicking and dragging with the right mouse button. | | `MouseLockSwitch` | 1 | Similar to classic, but allows the player to toggle mouse locking, causing the camera to rotate as the player moves the mouse (without holding down a button). | --- name: CoreGuiType last_updated: 2026-06-29T19:34:08Z type: enum summary: "Represents all available CoreGui \"types\" such as the Backpack and text chat window." --- # CoreGuiType Represents all available [CoreGui](/docs/reference/engine/classes/CoreGui.md) "types" such as the [Backpack](/docs/reference/engine/classes/Backpack.md) and text chat window. **Type:** enum ## Description This enum represents all available [CoreGui](/docs/reference/engine/classes/CoreGui.md) "types" such as the [Backpack](/docs/reference/engine/classes/Backpack.md) and text chat window. ## Items | Name | Value | Description | |------|-------|-------------| | `PlayerList` | 0 | Dynamically updated [Players](/docs/reference/engine/classes/Players.md) list, commonly used as a [leaderboard](/docs/en-us/players/leaderboards.md). | | `Health` | 1 | The character's [Health](/docs/reference/engine/classes/Humanoid.md) bar. | | `Backpack` | 2 | The character's [Backpack](/docs/reference/engine/classes/Backpack.md) which contains [in‑experience tools](/docs/en-us/players/tools.md). | | `Chat` | 3 | The [text chat](/docs/en-us/chat/in-experience-text-chat.md) window. | | `All` | 4 | Refers to all available core UI. | | `EmotesMenu` | 5 | Popup menu of character [emotes](/docs/en-us/characters/emotes.md). | | `SelfView` | 6 | A player's perspective or view of their own character. | | `Captures` | 7 | A "capture" button along the right side of the screen. | | `AvatarSwitcher` | 8 | The **Avatar Switcher** allows users to change their platform avatar. | | `ExperienceShop` | 9 | | --- name: CreateAssetResult last_updated: 2026-06-29T19:34:08Z type: enum --- # CreateAssetResult **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Success` | 1 | | | `PermissionDenied` | 2 | | | `UploadFailed` | 3 | | | `Unknown` | 4 | | --- name: CreateContentResult last_updated: 2026-06-29T19:34:08Z type: enum --- # CreateContentResult **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Success` | 1 | Success. | | `PermissionDenied` | 2 | The user does not have the required permissions to perform this operation. | | `UploadFailed` | 3 | Asset failed to upload. | | `StorageLimitExceeded` | 4 | The storage budget limit for [DataModel](/docs/reference/engine/classes/DataModel.md)-scoped content was exceeded. | | `Unknown` | 5 | An unknown error occurred. | --- name: CreateOutfitFailure last_updated: 2026-06-29T19:34:08Z type: enum --- # CreateOutfitFailure **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `InvalidName` | 1 | | | `OutfitLimitReached` | 2 | | | `Other` | 3 | | --- name: CreatorType last_updated: 2026-06-29T19:34:08Z type: enum --- # CreatorType **Type:** enum ## Description The ownership type of the open place. ## Items | Name | Value | Description | |------|-------|-------------| | `User` | 0 | The place is owned by a single user. | | `Group` | 1 | The place is owned by a group. | --- name: CreatorTypeFilter last_updated: 2026-06-29T19:34:08Z type: enum --- # CreatorTypeFilter **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `User` | 0 | | | `Group` | 1 | | | `All` | 2 | | --- name: CurrencyType last_updated: 2026-06-29T19:34:08Z type: enum summary: "The CurrencyType Enum is used with MarketplaceService to set the currency used." --- # CurrencyType The CurrencyType Enum is used with [MarketplaceService](/docs/reference/engine/classes/MarketplaceService.md) to set the currency used. **Type:** enum ## Description The CurrencyType Enum is used with [MarketplaceService](/docs/reference/engine/classes/MarketplaceService.md) to set the currency used. As tickets no longer exist on the Roblox platform, this enum is **ignored** in most API that previously used it, such as [PromptPurchase](/docs/reference/engine/classes/MarketplaceService.md). ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | | | `Robux` | 1 | Use Robux. | | `Tix` | 2 | Use Tickets. | --- name: CustomCameraMode last_updated: 2026-06-29T19:34:08Z type: enum summary: "Changes the player's camera mode." --- # CustomCameraMode Changes the player's camera mode. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | The default camera mode is classic. | | `Classic` | 1 | Camera tracks the player but will not automatically rotate if the player walks left or right. | | `Follow` | 2 | Camera tracks the player and automatically rotates if the player walks left or right. | --- name: DataStoreRequestType last_updated: 2026-06-29T19:34:08Z type: enum summary: "Indicates the type of data store request being made." --- # DataStoreRequestType Indicates the type of data store request being made. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `GetAsync` | 0 | Refers to [GetAsync()](/docs/reference/engine/classes/GlobalDataStore.md) and the read of [UpdateAsync()](/docs/reference/engine/classes/GlobalDataStore.md). | | `SetIncrementAsync` | 1 | Refers to [SetAsync()](/docs/reference/engine/classes/DataStore.md), [IncrementAsync()](/docs/reference/engine/classes/DataStore.md), [RemoveAsync()](/docs/reference/engine/classes/DataStore.md), and the write of [UpdateAsync()](/docs/reference/engine/classes/DataStore.md) when it returns a non-`nil` value. | | `UpdateAsync` | 2 | Refers to [UpdateAsync()](/docs/reference/engine/classes/GlobalDataStore.md). | | `GetSortedAsync` | 3 | Refers to [GetSortedAsync()](/docs/reference/engine/classes/OrderedDataStore.md). | | `SetIncrementSortedAsync` | 4 | Refers to [SetAsync()](/docs/reference/engine/classes/OrderedDataStore.md) [IncrementAsync()](/docs/reference/engine/classes/OrderedDataStore.md), [RemoveAsync()](/docs/reference/engine/classes/OrderedDataStore.md), and the write of [UpdateAsync()](/docs/reference/engine/classes/OrderedDataStore.md) while using an [OrderedDataStore](/docs/reference/engine/classes/OrderedDataStore.md). | | `OnUpdate` | 5 | Refers to [OnUpdate()](/docs/reference/engine/classes/GlobalDataStore.md). | | `ListAsync` | 6 | Refers to [ListKeysAsync()](/docs/reference/engine/classes/DataStore.md) and [ListVersionsAsync()](/docs/reference/engine/classes/DataStore.md). | | `GetVersionAsync` | 7 | Refers to [GetVersionAsync()](/docs/reference/engine/classes/DataStore.md). | | `RemoveVersionAsync` | 8 | Refers to [RemoveVersionAsync()](/docs/reference/engine/classes/DataStore.md). | | `StandardRead` | 9 | Refers to [GetAsync()](/docs/reference/engine/classes/DataStore.md), [GetVersionAsync()](/docs/reference/engine/classes/DataStore.md), [GetVersionAtTimeAsync()](/docs/reference/engine/classes/DataStore.md), and the read of [UpdateAsync()](/docs/reference/engine/classes/DataStore.md) for [DataStore](/docs/reference/engine/classes/DataStore.md). | | `StandardWrite` | 10 | Refers to [SetAsync()](/docs/reference/engine/classes/DataStore.md), [IncrementAsync()](/docs/reference/engine/classes/DataStore.md), and the write of [UpdateAsync()](/docs/reference/engine/classes/DataStore.md) for [DataStore](/docs/reference/engine/classes/DataStore.md). | | `StandardList` | 11 | Refers to [ListDataStoresAsync()](/docs/reference/engine/classes/DataStoreService.md), and [ListKeysAsync()](/docs/reference/engine/classes/DataStore.md) and [ListVersionsAsync()](/docs/reference/engine/classes/DataStore.md) for [DataStore](/docs/reference/engine/classes/DataStore.md). | | `StandardRemove` | 12 | Refers to [RemoveAsync()](/docs/reference/engine/classes/DataStore.md) for [DataStore](/docs/reference/engine/classes/DataStore.md). | | `OrderedRead` | 13 | Refers to [GetAsync()](/docs/reference/engine/classes/OrderedDataStore.md) and the read of [UpdateAsync()](/docs/reference/engine/classes/OrderedDataStore.md) for [OrderedDataStore](/docs/reference/engine/classes/OrderedDataStore.md). | | `OrderedWrite` | 14 | Refers to [SetAsync()](/docs/reference/engine/classes/OrderedDataStore.md), [IncrementAsync()](/docs/reference/engine/classes/OrderedDataStore.md), and the write of [UpdateAsync()](/docs/reference/engine/classes/OrderedDataStore.md) for [OrderedDataStore](/docs/reference/engine/classes/OrderedDataStore.md). | | `OrderedList` | 15 | Refers to [GetSortedAsync()](/docs/reference/engine/classes/OrderedDataStore.md) for [OrderedDataStore](/docs/reference/engine/classes/OrderedDataStore.md). GetRequestBudgetForRequestType()`with this enum will return`0`. | | `OrderedRemove` | 16 | Refers to [RemoveAsync()](/docs/reference/engine/classes/OrderedDataStore.md) for [OrderedDataStore](/docs/reference/engine/classes/OrderedDataStore.md). | --- name: DebugBreakModeType last_updated: 2026-06-29T19:34:08Z type: enum summary: "Controls when the debugger pauses on exceptions." --- # DebugBreakModeType Controls when the debugger pauses on exceptions. **Type:** enum ## Description Used with [ScriptDebuggerService:SetExceptionBreakMode()](/docs/reference/engine/classes/ScriptDebuggerService.md) to control when the debugger pauses on exceptions. The mode applies to all [DataModels](/docs/reference/engine/classes/DataModel.md). ## Items | Name | Value | Description | |------|-------|-------------| | `Never` | 0 | Never break on exceptions. | | `Always` | 1 | Break on all exceptions. | | `Unhandled` | 2 | Break only on unhandled exceptions. | --- name: DebuggerEndReason last_updated: 2026-06-29T19:34:08Z type: enum summary: "Reason for the end of the debugger session." --- # DebuggerEndReason Reason for the end of the debugger session. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `ClientRequest` | 0 | Client requested connection termination. | | `Timeout` | 1 | Connection timed out. | | `InvalidHost` | 2 | Invalid host:port combination. | | `Disconnected` | 3 | Connection was lost. | | `ServerShutdown` | 4 | Server terminated the connection because it shut down. | | `ServerProtocolMismatch` | 5 | Server has wrong version of protocol. | | `ConfigurationFailed` | 6 | Got a failure response when trying to configure the server. | | `RpcError` | 7 | An error occurred in the RPC layer. | --- name: DebuggerExceptionBreakMode last_updated: 2026-06-29T19:34:08Z type: enum --- # DebuggerExceptionBreakMode **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Never` | 0 | | | `Always` | 1 | | | `Unhandled` | 2 | | --- name: DebuggerFrameType last_updated: 2026-06-29T19:34:08Z type: enum summary: "Call stack frame type." --- # DebuggerFrameType Call stack frame type. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `C` | 0 | Call stack frame belongs to a C (native) function. | | `Lua` | 1 | Call stack frame belongs to a Luau function. | --- name: DebuggerPauseReason last_updated: 2026-06-29T19:34:08Z type: enum summary: "Reason that the DataModel was paused." --- # DebuggerPauseReason Reason that the DataModel was paused. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Unknown` | 0 | Pausing for a reason not covered by other values. | | `Requested` | 1 | Pause was requested by user. | | `Breakpoint` | 2 | Pausing on a user breakpoint. | | `Exception` | 3 | Pausing on error hit in the code. | | `SingleStep` | 4 | Pausing on an internal breakpoint set by debugger command: e.g. step over, step into, step out of. | | `Entrypoint` | 5 | Pausing at the entry on the script. | --- name: DebuggerResumeType last_updated: 2026-06-29T19:34:08Z type: enum summary: "Specifies how the debugger should resume execution after a pause." --- # DebuggerResumeType Specifies how the debugger should resume execution after a pause. **Type:** enum ## Description Returned from the [ScriptDebuggerService.OnStopped](/docs/reference/engine/classes/ScriptDebuggerService.md) callback (via [ScriptResumeAction](/docs/reference/engine/datatypes/ScriptResumeAction.md)) to control how execution resumes after a debugger pause. ## Items | Name | Value | Description | |------|-------|-------------| | `StepInto` | 0 | Step into the next function call on the specified thread. | | `StepOut` | 1 | Step out of the current function on the specified thread. | | `StepOver` | 2 | Step over the current line on the specified thread. | | `Resume` | 3 | Resume execution normally without stepping. | --- name: DebuggerStatus last_updated: 2026-06-29T19:34:08Z type: enum summary: "Result of a debugger request." --- # DebuggerStatus Result of a debugger request. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Success` | 0 | Request has completed successfully. | | `Timeout` | 1 | Timed out while waiting for response. | | `ConnectionLost` | 2 | Connection to the debugger was lost. | | `InvalidResponse` | 3 | Failed to parse response. | | `InternalError` | 4 | Exception encountered while processing response. | | `InvalidState` | 5 | The request was not appropriate for the current debugger state. | | `RpcError` | 6 | Response was an error. | | `InvalidArgument` | 7 | One of the request arguments was invalid. | | `ConnectionClosed` | 8 | Connection closed while waiting for response. | --- name: DefaultScriptSyncFileType last_updated: 2026-06-29T19:34:08Z type: enum --- # DefaultScriptSyncFileType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Lua` | 0 | | | `Luau` | 1 | | --- name: DevCameraOcclusionMode last_updated: 2026-06-29T19:34:08Z type: enum summary: "Determines how the default camera handles objects that are in-between the camera and the camera subject." --- # DevCameraOcclusionMode Determines how the default camera handles objects that are in-between the camera and the camera subject. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Zoom` | 0 | The camera zooms in until there is nothing between it and its subject. | | `Invisicam` | 1 | Any objects between the camera and its subject become translucent locally. | --- name: DevComputerCameraMovementMode last_updated: 2026-06-29T19:34:08Z type: enum summary: "Overwrites the player's camera movement mode setting on a computer." --- # DevComputerCameraMovementMode Overwrites the player's camera movement mode setting on a computer. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `UserChoice` | 0 | The camera will move based on the player's settings. | | `Classic` | 1 | Camera tracks the player but will not automatically rotate if the player walks left or right. | | `Follow` | 2 | Camera tracks the player and automatically rotates if the player walks left or right. | | `Orbital` | 3 | The camera has a fixed Y position, but can be rotated around the player. | | `CameraToggle` | 4 | The camera toggles between locked and free rotation with the right mouse button. | --- name: DevComputerMovementMode last_updated: 2026-06-29T19:34:08Z type: enum summary: "Sets the movement mode of players who are playing on computers." --- # DevComputerMovementMode Sets the movement mode of players who are playing on computers. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `UserChoice` | 0 | Allows players to choose their desired control scheme from the in-experience menu. This is the default movement mode. | | `KeyboardMouse` | 1 | The player's character will be controlled using the keyboard and mouse. | | `ClickToMove` | 2 | Players can only move through the experience by clicking a target location. The player's character will automatically jump when reaching a surmountable obstacle/gap while moving to the click destination. | | `Scriptable` | 3 | Disables all default controls and allows you to script your own control scheme. | --- name: DevTouchCameraMovementMode last_updated: 2026-06-29T19:34:08Z type: enum summary: "Overwrites the camera mode if the player is on a touch device." --- # DevTouchCameraMovementMode Overwrites the camera mode if the player is on a touch device. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `UserChoice` | 0 | The camera will move based on the player's settings. | | `Classic` | 1 | Camera tracks the player but will not automatically rotate if the player walks left or right. | | `Follow` | 2 | Camera tracks the player and automatically rotates if the player walks left or right. | | `Orbital` | 3 | The camera has a fixed Y position, but can be rotated around the player. | --- name: DevTouchMovementMode last_updated: 2026-06-29T19:34:08Z type: enum summary: "Overrides the movement mode of players who are playing on touch-enabled devices." --- # DevTouchMovementMode Overrides the movement mode of players who are playing on touch-enabled devices. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `UserChoice` | 0 | Allows players to choose their desired control scheme from the in-experience menu. This is the default movement mode. | | `Thumbstick` | 1 | | | `DPad` | 2 | | | `Thumbpad` | 3 | | | `ClickToMove` | 4 | The player's character will attempt to move to a location in the world when the player taps a location. | | `Scriptable` | 5 | The player's character will not respond to default controls; any character movement must be defined in custom scripts. | | `DynamicThumbstick` | 6 | The player's character is controlled with a virtual thumbstick that appears when they touch the lower portion of the screen. Jumping is controlled with a separate button. | --- name: DeveloperMemoryTag last_updated: 2026-06-29T19:34:08Z type: enum summary: "A memory tracking category." --- # DeveloperMemoryTag A memory tracking category. **Type:** enum ## Description A list of memory categories, and a description of what they are allocated to. ## Items | Name | Value | Description | |------|-------|-------------| | `Internal` | 0 | General data that doesn't have any categorization. This could be due to either internal reasons, or because it simply isn't being tracked categorically. | | `HttpCache` | 1 | A cache of HTTP responses. | | `Instances` | 2 | All the Instances present in memory. | | `Signals` | 3 | Events, signals, connections, etc. | | `LuaHeap` | 4 | All of the data in Luau, including everything happening in core scripts, built-in data types, etc. | | `Script` | 5 | All memory being manipulated and referenced by scripts. | | `PhysicsCollision` | 6 | Collision detection in the [Workspace](/docs/reference/engine/classes/Workspace.md). | | `BaseParts` | 7 | 3D parts used for simulation. | | `GraphicsSolidModels` | 8 | Rendering solid models (stuff made with Union, Negate, etc.). | | `GraphicsMeshParts` | 10 | Rendering of mesh parts. | | `GraphicsParticles` | 11 | Rendering of particles from ParticleEmitters. | | `GraphicsParts` | 12 | Rendering of regular parts. | | `GraphicsSpatialHash` | 13 | Spatial hash lookup tables of the game world that are used for rendering. | | `GraphicsTerrain` | 14 | Rendering of terrain geometry. | | `GraphicsTexture` | 15 | Rendering of textures in the game world. | | `GraphicsTextureCharacter` | 16 | Rendering of texture composition maps that are generated for Humanoids. | | `Sounds` | 17 | Data of sounds in-game. | | `StreamingSounds` | 18 | Playback of sounds in-game. | | `TerrainVoxels` | 19 | Occupancy/Material data of the Terrain. | | `Gui` | 21 | Gui element data and rendering. | | `Animation` | 22 | Playback of Animations on Humanoids and AnimationControllers. | | `Navigation` | 23 | Pathfinding for Humanoids via the PathfindingService. | | `GeometryCSG` | 24 | | | `GraphicsSlimModels` | 25 | | --- name: DeviceFeatureType last_updated: 2026-06-29T19:34:08Z type: enum --- # DeviceFeatureType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `DeviceCapture` | 0 | | | `InExperienceFAE` | 1 | | --- name: DeviceForm last_updated: 2026-06-29T19:34:08Z type: enum --- # DeviceForm **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Console` | 0 | | | `Phone` | 1 | | | `Tablet` | 2 | | | `Desktop` | 3 | | | `VR` | 4 | | --- name: DeviceLevel last_updated: 2026-06-29T19:34:08Z type: enum --- # DeviceLevel **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Low` | 0 | | | `Medium` | 1 | | | `High` | 2 | | --- name: DeviceSimulatorScalingMode last_updated: 2026-06-29T19:34:08Z type: enum --- # DeviceSimulatorScalingMode **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `ScaleToPhysicalSize` | 0 | | | `ActualResolution` | 1 | | | `FitToWindow` | 2 | | --- name: DeviceType last_updated: 2026-06-29T19:34:08Z type: enum summary: "Device category of the client." --- # DeviceType Device category of the client. **Type:** enum ## Description The **DeviceType** enum is used to determine the category of devices that a client belongs to. ## Items | Name | Value | Description | |------|-------|-------------| | `Unknown` | 0 | The client is an unknown device. | | `Desktop` | 1 | The client is a desktop device. | | `Tablet` | 2 | The client is a tablet device. | | `Phone` | 3 | The client is a phone device. | | `TV` | 4 | | --- name: DialogBehaviorType last_updated: 2026-06-29T19:34:08Z type: enum summary: "Controls whether multiple people can use this dialog, or only one person at a time can use it." --- # DialogBehaviorType Controls whether multiple people can use this dialog, or only one person at a time can use it. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `SinglePlayer` | 0 | Only one player can interact with this dialog at a time. The conversation between the player and dialog can be seen by all players. | | `MultiplePlayers` | 1 | Multiple players can simultaneously interact with this dialog. The conversation between the player and the dialog cannot be seen by other players. | --- name: DialogPurpose last_updated: 2026-06-29T19:34:08Z type: enum summary: "The DialogPurpose enum is used to set the icon of a Dialog." --- # DialogPurpose The DialogPurpose enum is used to set the icon of a [Dialog](/docs/reference/engine/classes/Dialog.md). **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Quest` | 0 | Shows an exclamation point ('''!'''). | | `Help` | 1 | Shows a question mark ('''?'''). | | `Shop` | 2 | Shows a dollar sign ('''$'''). | --- name: DialogTone last_updated: 2026-06-29T19:34:08Z type: enum summary: "Sets the Dialog.Tone of a Dialog object, which influences how the dialog appears." --- # DialogTone Sets the [Dialog.Tone](/docs/reference/engine/classes/Dialog.md) of a [Dialog](/docs/reference/engine/classes/Dialog.md) object, which influences how the dialog appears. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Neutral` | 0 | Causes the dialog to have a blue bar on its left-hand side. | | `Friendly` | 1 | Causes the dialog to have a green bar on its left-hand side. | | `Enemy` | 2 | Causes the dialog to have a red bar on its left-hand side. | --- name: DigitsRigDescriptionSide last_updated: 2026-06-29T19:34:08Z type: enum --- # DigitsRigDescriptionSide **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `None` | 0 | | | `Left` | 1 | | | `Right` | 2 | | --- name: DisplaySize last_updated: 2026-06-29T19:34:08Z type: enum summary: "This enum is used with GuiService.ViewportDisplaySize to indicate the internally-categorized rendering size of the viewport." --- # DisplaySize This enum is used with [GuiService.ViewportDisplaySize](/docs/reference/engine/classes/GuiService.md) to indicate the internally-categorized rendering size of the viewport. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Small` | 0 | Viewport is categorized as small. Applies to most tablet/mobile/handheld devices. | | `Medium` | 1 | Viewport is categorized as medium. Applies to most laptops and monitors. | | `Large` | 2 | Viewport is categorized as large. Applies to most TVs or larger. | --- name: DomainType last_updated: 2026-06-29T19:34:08Z type: enum summary: "Specifies the type of domain that a User is scoped to." --- # DomainType Specifies the type of domain that a [User](/docs/reference/engine/datatypes/User.md) is scoped to. **Type:** enum ## Description The **DomainType** enum describes the kind of domain in which a domain user ID is unique. It is used by the [User](/docs/reference/engine/datatypes/User.md) data type to identify whether the user's ID belongs to an experience or an OAuth application. ## Items | Name | Value | Description | |------|-------|-------------| | `EXPERIENCE` | 1 | The user ID is scoped to a specific experience (universe). | | `OAUTH` | 3 | The user ID is scoped to a specific OAuth application. | --- name: DominantAxis last_updated: 2026-06-29T19:34:08Z type: enum summary: "Used by UIAspectRatioConstraint.DominantAxis for resizing the object to maintain the aspect ratio." --- # DominantAxis Used by [UIAspectRatioConstraint.DominantAxis](/docs/reference/engine/classes/UIAspectRatioConstraint.md) for resizing the object to maintain the aspect ratio. **Type:** enum ## Description Used by [UIAspectRatioConstraint.DominantAxis](/docs/reference/engine/classes/UIAspectRatioConstraint.md) to specify which axis is used when setting the new size of the GUI element. ## Items | Name | Value | Description | |------|-------|-------------| | `Width` | 0 | The X axis. | | `Height` | 1 | The Y axis. | --- name: DraftStatusCode last_updated: 2026-06-29T19:34:08Z type: enum --- # DraftStatusCode **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `OK` | 0 | | | `DraftOutdated` | 1 | | | `ScriptRemoved` | 2 | | | `DraftCommitted` | 3 | | --- name: DragDetectorDragStyle last_updated: 2026-06-29T19:34:08Z type: enum summary: "Used with DragDetector as the paradigm to generate proposed motion, given a stream of cursor rays." --- # DragDetectorDragStyle Used with [DragDetector](/docs/reference/engine/classes/DragDetector.md) as the paradigm to generate proposed motion, given a stream of cursor rays. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `TranslateLine` | 0 | 1D motion along the detector's [Axis](/docs/reference/engine/classes/DragDetector.md), by default the world **Y** axis. | | `TranslatePlane` | 1 | 2D motion in the plane **perpendicular** to the detector's [Axis](/docs/reference/engine/classes/DragDetector.md), by default the world **XZ** plane. | | `TranslatePlaneOrLine` | 2 | 2D motion in the plane **perpendicular** to the detector's [Axis](/docs/reference/engine/classes/DragDetector.md) and, when the modifier is active, 1D motion along the detector's [Axis](/docs/reference/engine/classes/DragDetector.md). | | `TranslateLineOrPlane` | 3 | 1D motion along the detector's [Axis](/docs/reference/engine/classes/DragDetector.md) and, when the modifier is active, 2D motion in the plane **perpendicular** to the detector's [Axis](/docs/reference/engine/classes/DragDetector.md). | | `TranslateViewPlane` | 4 | 2D motion in the plane perpendicular to the camera's view. In this mode, the plane is constantly updated, even while dragging, and will always face the camera's current view. | | `RotateAxis` | 5 | Rotation about the detector's [Axis](/docs/reference/engine/classes/DragDetector.md), by default the world **Y** axis. | | `RotateTrackball` | 6 | Trackball rotation, further customized through the [TrackballRadialPullFactor](/docs/reference/engine/classes/DragDetector.md) and [TrackballRollFactor](/docs/reference/engine/classes/DragDetector.md) properties. | | `Scriptable` | 7 | Calculates desired motion via a custom function provided through [SetDragStyleFunction()](/docs/reference/engine/classes/DragDetector.md). | | `BestForDevice` | 8 | **TranslatePlaneOrLine** for mouse and gamepad; **TranslatePlane** for touch; **6DOF** for VR. | --- name: DragDetectorPermissionPolicy last_updated: 2026-06-29T19:34:08Z type: enum summary: "Used to control the permission level for which players can interact with a DragDetector." --- # DragDetectorPermissionPolicy Used to control the permission level for which players can interact with a [DragDetector](/docs/reference/engine/classes/DragDetector.md). **Type:** enum ## Description Used to control the permission level for which players can interact with a [DragDetector](/docs/reference/engine/classes/DragDetector.md) through its [PermissionPolicy](/docs/reference/engine/classes/DragDetector.md) property. ## Items | Name | Value | Description | |------|-------|-------------| | `Nobody` | 0 | No players can interact with the [DragDetector](/docs/reference/engine/classes/DragDetector.md). | | `Everybody` | 1 | All players can interact with the [DragDetector](/docs/reference/engine/classes/DragDetector.md). | | `Scriptable` | 2 | Invokes the function registered via [DragDetector:SetPermissionPolicyFunction()](/docs/reference/engine/classes/DragDetector.md), enabling/disabling the detector based on whether the function returns `true` or `false`. | --- name: DragDetectorResponseStyle last_updated: 2026-06-29T19:34:08Z type: enum summary: "Describes how the clicked object will be treated once the desired motion has been calculated." --- # DragDetectorResponseStyle Describes how the clicked object will be treated once the desired motion has been calculated. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Geometric` | 0 | For an [Anchored](/docs/reference/engine/classes/BasePart.md) object both inside the running experience and in Studio edit mode, the position/orientation of the object will be updated to exactly reflect the proposed motion. For an unanchored object, behavior is the same as for an anchored object; however, in a running experience, the object will be anchored at the start of the drag and restored to unanchored upon drag release. | | `Physical` | 1 | An [Anchored](/docs/reference/engine/classes/BasePart.md) object will default to **Geometric** behavior as it is not affected by forces. An unanchored object will be moved by constraint forces that attempt to bring it to the desired position and/or orientation given by the proposed motion. | | `Custom` | 2 | The object will not move at all, but [DragFrame](/docs/reference/engine/classes/DragDetector.md) will still be updated and you can respond to drag manipulation however you'd like. | --- name: DraggerCoordinateSpace last_updated: 2026-06-29T19:34:08Z type: enum --- # DraggerCoordinateSpace **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Object` | 0 | | | `World` | 1 | | --- name: DraggerMovementMode last_updated: 2026-06-29T19:34:08Z type: enum --- # DraggerMovementMode **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Geometric` | 0 | | | `Physical` | 1 | | --- name: DraggingScrollBar last_updated: 2026-06-29T19:34:08Z type: enum --- # DraggingScrollBar **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `None` | 0 | | | `Horizontal` | 1 | | | `Vertical` | 2 | | --- name: EasingDirection last_updated: 2026-06-29T19:34:08Z type: enum summary: "Enum used with TweenInfo.new() to control the direction Tweens play in." --- # EasingDirection Enum used with [TweenInfo.new()](/docs/reference/engine/datatypes/TweenInfo.md) to control the direction [Tweens](/docs/reference/engine/classes/Tween.md) play in. **Type:** enum ## Description These enum values are passed to [TweenInfo.new()](/docs/reference/engine/datatypes/TweenInfo.md) to control the direction [Tweens](/docs/reference/engine/classes/Tween.md) play in. ## Items | Name | Value | Description | |------|-------|-------------| | `In` | 0 | The easing style is applied in a forward direction. | | `Out` | 1 | The easing style is applied in a reverse direction. | | `InOut` | 2 | The easing style is applied forward for the first half and in reverse for the second half. | --- name: EasingStyle last_updated: 2026-06-29T19:34:08Z type: enum summary: "Enum used with TweenInfo.new to control the motion of a Tween." --- # EasingStyle Enum used with [TweenInfo.new](/docs/reference/engine/datatypes/TweenInfo.md) to control the motion of a [Tween](/docs/reference/engine/classes/Tween.md). **Type:** enum ## Description These enum values are passed to [TweenInfo.new()](/docs/reference/engine/datatypes/TweenInfo.md) to control the motion of a [Tween](/docs/reference/engine/classes/Tween.md). The following graphs reflect easing styles for [Enum.EasingDirection.In](/docs/reference/engine/enums/EasingDirection.md). For graphs reflecting [Enum.EasingDirection.Out](/docs/reference/engine/enums/EasingDirection.md) and [Enum.EasingDirection.InOut](/docs/reference/engine/enums/EasingDirection.md), see [UI Animations](/docs/en-us/ui/animation.md#style). ![Graphs of EasingStyle variations with an 'In' EasingDirection.](/assets/engine-api/enums/EasingStyle/Easing-Styles-In.png) ## Items | Name | Value | Description | |------|-------|-------------| | `Linear` | 0 | Moves at a constant speed. | | `Sine` | 1 | Speed is determined by a sine wave for a gentle easing motion. | | `Back` | 2 | Slightly overshoots the target, then backs into place. | | `Quad` | 3 | Similar to `Sine` but with a slightly sharper curve based on quadratic interpolation. | | `Quart` | 4 | Similar to `Cubic` but with an even sharper curve based on quartic interpolation. | | `Quint` | 5 | Similar to `Quart` but with an even sharper curve based on quintic interpolation. | | `Bounce` | 6 | Bounces backwards multiple times after reaching the target, before eventually settling. | | `Elastic` | 7 | Moves as if attached to a rubber band, overshooting the target several times. | | `Exponential` | 8 | The sharpest curve based on exponential interpolation. | | `Circular` | 9 | Follows a circular arc, such that acceleration is more sudden and deceleration more gradual versus `Quint` or `Exponential`. | | `Cubic` | 10 | Similar to `Quad` but with a slightly sharper curve based on cubic interpolation. | --- name: ElasticBehavior last_updated: 2026-06-29T19:34:08Z type: enum summary: "This enum is used by ScrollingFrame.ElasticBehavior to control when elastic scrolling is active on touch‑enabled devices." --- # ElasticBehavior This enum is used by [ScrollingFrame.ElasticBehavior](/docs/reference/engine/classes/ScrollingFrame.md) to control when elastic scrolling is active on touch‑enabled devices. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `WhenScrollable` | 0 | Scrolling is elastic when there is content to be scrolled to. | | `Always` | 1 | Scrolling is always elastic, even when there isn't content available to scroll to. | | `Never` | 2 | Scrolling is never elastic and the canvas will never scroll beyond its bounds. | --- name: EmitterPositionType last_updated: 2026-06-29T19:34:08Z type: enum summary: "Defines where and how an AudioEmitter is positioned when sending out spatial audio." --- # EmitterPositionType Defines where and how an [AudioEmitter](/docs/reference/engine/classes/AudioEmitter.md) is positioned when sending out spatial audio. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Parent` | 0 | Uses the parent [Instance](/docs/reference/engine/classes/Instance.md) of the [AudioEmitter](/docs/reference/engine/classes/AudioEmitter.md). | | `Instance` | 1 | Uses a specified [Instance](/docs/reference/engine/classes/Instance.md). | --- name: EnviromentalPhysicsThrottle last_updated: 2026-06-29T19:34:08Z type: enum summary: "Used to control the throttle rate of Roblox's physics engine." --- # EnviromentalPhysicsThrottle Used to control the throttle rate of Roblox's physics engine. **Type:** enum ## Description The `EnviromentalPhysicsThrottle` enum is used with [PhysicsSettings.PhysicsEnvironmentalThrottle](/docs/reference/engine/classes/PhysicsSettings.md), controlling how aggressively the engine skips physics simulation steps to reduce CPU load. ## Items | Name | Value | Description | |------|-------|-------------| | `DefaultAuto` | 0 | Automatically adjusts throttle level based on performance. | | `Disabled` | 1 | No throttling; every physics step runs. | | `Always` | 2 | Maximum throttling; physics is effectively frozen. | | `Skip2` | 3 | Runs 1 out of every 2 steps (50% reduction). If [Workspace.PhysicsSteppingMethod](/docs/reference/engine/classes/Workspace.md) is set to [Adaptive](/docs/reference/engine/enums/PhysicsSteppingMethod.md), skipping is based on groups of 4 steps instead of individual steps. | | `Skip4` | 4 | Runs 1 out of every 4 steps (75% reduction). If [Workspace.PhysicsSteppingMethod](/docs/reference/engine/classes/Workspace.md) is set to [Adaptive](/docs/reference/engine/enums/PhysicsSteppingMethod.md), skipping is based on groups of 4 steps instead of individual steps. | | `Skip8` | 5 | Runs 1 out of every 8 steps (87.5% reduction). If [Workspace.PhysicsSteppingMethod](/docs/reference/engine/classes/Workspace.md) is set to [Adaptive](/docs/reference/engine/enums/PhysicsSteppingMethod.md), skipping is based on groups of 4 steps instead of individual steps. | | `Skip16` | 6 | Runs 1 out of every 16 steps (93.75% reduction). If [Workspace.PhysicsSteppingMethod](/docs/reference/engine/classes/Workspace.md) is set to [Adaptive](/docs/reference/engine/enums/PhysicsSteppingMethod.md), skipping is based on groups of 4 steps instead of individual steps. | --- name: ExperienceAuthScope last_updated: 2026-06-29T19:34:08Z type: enum --- # ExperienceAuthScope **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `DefaultScope` | 0 | | | `CreatorAssetsCreate` | 1 | | --- name: ExperienceEventStatus last_updated: 2026-06-29T19:34:08Z type: enum --- # ExperienceEventStatus **Type:** enum ## Description Represents the lifecycle state of an experience event returned by [SocialService](/docs/reference/engine/classes/SocialService.md). ## Items | Name | Value | Description | |------|-------|-------------| | `Active` | 0 | The event is currently published and available. | | `Cancelled` | 1 | The event has been cancelled and will not occur. | | `Moderated` | 2 | The event has been removed by moderation. | | `Unpublished` | 3 | The event has been unpublished by the creator. | | `Unknown` | 4 | The event status could not be determined. | --- name: ExplosionType last_updated: 2026-06-29T19:34:08Z type: enum --- # ExplosionType **Type:** enum ## Description The ExplosionType Enum is used to set the [Explosion.ExplosionType](/docs/reference/engine/classes/Explosion.md) property. ## Items | Name | Value | Description | |------|-------|-------------| | `NoCraters` | 0 | Explosion does not generate craters. | | `Craters` | 1 | Explosion generates craters. | --- name: ExternalEditorMode last_updated: 2026-06-29T19:34:08Z type: enum --- # ExternalEditorMode **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `SystemDefault` | 0 | | | `UserSelectedEditor` | 1 | | --- name: FACSDataLod last_updated: 2026-06-29T19:34:08Z type: enum --- # FACSDataLod **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `LOD0` | 0 | | | `LOD1` | 1 | | | `LODCount` | 2 | | --- name: FacialAgeEstimationResultType last_updated: 2026-06-29T19:34:08Z type: enum --- # FacialAgeEstimationResultType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Complete` | 0 | | | `Cancel` | 1 | | | `Error` | 2 | | --- name: FacialAnimationStreamingState last_updated: 2026-06-29T19:34:08Z type: enum --- # FacialAnimationStreamingState **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `None` | 0 | | | `Audio` | 1 | | | `Video` | 2 | | | `Place` | 4 | | | `Server` | 8 | | --- name: FacsActionUnit last_updated: 2026-06-29T19:34:08Z type: enum --- # FacsActionUnit **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `ChinRaiserUpperLip` | 0 | | | `ChinRaiser` | 1 | | | `FlatPucker` | 2 | | | `Funneler` | 3 | | | `LowerLipSuck` | 4 | | | `LipPresser` | 5 | | | `LipsTogether` | 6 | | | `MouthLeft` | 7 | | | `MouthRight` | 8 | | | `Pucker` | 9 | | | `UpperLipSuck` | 10 | | | `LeftCheekPuff` | 11 | | | `LeftDimpler` | 12 | | | `LeftLipCornerDown` | 13 | | | `LeftLowerLipDepressor` | 14 | | | `LeftLipCornerPuller` | 15 | | | `LeftLipStretcher` | 16 | | | `LeftUpperLipRaiser` | 17 | | | `RightCheekPuff` | 18 | | | `RightDimpler` | 19 | | | `RightLipCornerDown` | 20 | | | `RightLowerLipDepressor` | 21 | | | `RightLipCornerPuller` | 22 | | | `RightLipStretcher` | 23 | | | `RightUpperLipRaiser` | 24 | | | `JawDrop` | 25 | | | `JawLeft` | 26 | | | `JawRight` | 27 | | | `Corrugator` | 28 | | | `LeftBrowLowerer` | 29 | | | `LeftOuterBrowRaiser` | 30 | | | `LeftNoseWrinkler` | 31 | | | `LeftInnerBrowRaiser` | 32 | | | `RightBrowLowerer` | 33 | | | `RightOuterBrowRaiser` | 34 | | | `RightInnerBrowRaiser` | 35 | | | `RightNoseWrinkler` | 36 | | | `EyesLookDown` | 37 | | | `EyesLookLeft` | 38 | | | `EyesLookUp` | 39 | | | `EyesLookRight` | 40 | | | `LeftCheekRaiser` | 41 | | | `LeftEyeUpperLidRaiser` | 42 | | | `LeftEyeClosed` | 43 | | | `RightCheekRaiser` | 44 | | | `RightEyeUpperLidRaiser` | 45 | | | `RightEyeClosed` | 46 | | | `TongueDown` | 47 | | | `TongueOut` | 48 | | | `TongueUp` | 49 | | --- name: FieldOfViewMode last_updated: 2026-06-29T19:34:08Z type: enum --- # FieldOfViewMode **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Vertical` | 0 | | | `Diagonal` | 1 | | | `MaxAxis` | 2 | | --- name: FileMode last_updated: 2026-06-29T19:34:08Z type: enum --- # FileMode **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Binary` | 0 | | | `Text` | 1 | | --- name: FileSystemWalkMode last_updated: 2026-06-29T19:34:08Z type: enum --- # FileSystemWalkMode **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `NonRecursive` | 0 | | | `Recursive` | 1 | | --- name: FillDirection last_updated: 2026-06-29T19:34:08Z type: enum summary: "Used by UIGridStyleLayout.FillDirection to control which direction elements are placed." --- # FillDirection Used by [UIGridStyleLayout.FillDirection](/docs/reference/engine/classes/UIGridStyleLayout.md) to control which direction elements are placed. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Horizontal` | 0 | Fill in the left-to-right direction. | | `Vertical` | 1 | Fill in the top-to-bottom direction. | --- name: FilterErrorType last_updated: 2026-06-29T19:34:08Z type: enum --- # FilterErrorType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `BackslashNotEscapingAnything` | 0 | | | `BadBespokeFilter` | 1 | | | `BadName` | 2 | | | `IncompleteOr` | 3 | | | `IncompleteParenthesis` | 4 | | | `InvalidDoubleStar` | 5 | | | `InvalidTilde` | 6 | | | `PropertyBadOperator` | 7 | | | `PropertyDoesNotExist` | 8 | | | `PropertyInvalidField` | 9 | | | `PropertyInvalidValue` | 10 | | | `PropertyUnsupportedFields` | 11 | | | `PropertyUnsupportedProperty` | 12 | | | `UnexpectedNameIndex` | 13 | | | `UnexpectedToken` | 14 | | | `UnfinishedBinaryOperator` | 15 | | | `UnfinishedQuote` | 16 | | | `UnknownBespokeFilter` | 17 | | | `WildcardInProperty` | 18 | | --- name: FilterResult last_updated: 2026-06-29T19:34:08Z type: enum summary: "Used to determine the result of a text filter request." --- # FilterResult Used to determine the result of a text filter request. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Accepted` | 0 | Text accepted by filter. | | `Rejected` | 1 | Text rejected by filter. | --- name: FinishRecordingOperation last_updated: 2026-06-29T19:34:08Z type: enum --- # FinishRecordingOperation **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Cancel` | 0 | Cancels the recording, automatically undoing any work in progress. | | `Commit` | 1 | Commits the recorded work. | | `Append` | 2 | Commits the recorded work and merges with the previous recording if possible. | --- name: FluidFidelity last_updated: 2026-06-29T19:34:08Z type: enum summary: "Determines the geometric representation used to compute aerodynamic forces." --- # FluidFidelity Determines the geometric representation used to compute aerodynamic forces. **Type:** enum ## Description Determines the geometric representation used to compute aerodynamic forces and torques for [MeshPart](/docs/reference/engine/classes/MeshPart.md) and [PartOperation](/docs/reference/engine/classes/PartOperation.md) instances. ## Items | Name | Value | Description | |------|-------|-------------| | `Automatic` | 0 | Let the physics engine select the geometric representation for aerodynamic force and torque calculations. | | `UseCollisionGeometry` | 1 | Use the current collision geometry specified by [TriangleMeshPart.CollisionFidelity](/docs/reference/engine/classes/TriangleMeshPart.md) for aerodynamic force and torque calculations . | | `UsePreciseGeometry` | 2 | Force the engine to compute aerodynamic forces and torques using a more precise geometry representation based on the original mesh. This option may increase join and replication time if used on a large scale. | --- name: FluidForces last_updated: 2026-06-29T19:34:08Z type: enum summary: "Controls the enablement of aerodynamic forces on parts and assemblies in the workspace." --- # FluidForces Controls the enablement of aerodynamic forces on parts and assemblies in the workspace. **Type:** enum ## Description Used as an enum value for [Workspace.FluidForces](/docs/reference/engine/classes/Workspace.md) to control the enablement of aerodynamic forces on parts and assemblies in the workspace. Note that this enum is not applicable in scripting and [Workspace.FluidForces](/docs/reference/engine/classes/Workspace.md) must be toggled in Studio. ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | Aerodynamic forces will not be calculated on any [BaseParts](/docs/reference/engine/classes/BasePart.md). | | `Experimental` | 1 | Aerodynamic forces will be calculated on [BaseParts](/docs/reference/engine/classes/BasePart.md) with [EnableFluidForces](/docs/reference/engine/classes/BasePart.md) set to true. During the beta phase, the behavior of the aerodynamic force model may change as the model improves. | --- name: Font last_updated: 2026-06-29T19:34:08Z type: enum --- # Font **Type:** enum ## Description The `Font` enum is used in [TextBox](/docs/reference/engine/classes/TextBox.md), [TextLabel](/docs/reference/engine/classes/TextLabel.md), [TextButton](/docs/reference/engine/classes/TextButton.md), and similar GUI objects to determine how the element will render its text. See also [Font](/docs/reference/engine/datatypes/Font.md) as a newer alternative to this enum which encapsulates fonts as a datatype and provides access to more fonts than this enum allows. ## Items | Name | Value | Description | |------|-------|-------------| | `Legacy` | 0 | | | `Arial` | 1 | `Arial` has been removed. Using it will map to the `Arimo` font family of the [Font](/docs/reference/engine/datatypes/Font.md) data type. | | `ArialBold` | 2 | `ArialBold` has been removed. Using it will map to the `Arimo` font family of the [Font](/docs/reference/engine/datatypes/Font.md) data type. | | `SourceSans` | 3 | | | `SourceSansBold` | 4 | | | `SourceSansLight` | 5 | | | `SourceSansItalic` | 6 | | | `Bodoni` | 7 | | | `Garamond` | 8 | | | `Cartoon` | 9 | | | `Code` | 10 | | | `Highway` | 11 | | | `SciFi` | 12 | | | `Arcade` | 13 | | | `Fantasy` | 14 | | | `Antique` | 15 | | | `SourceSansSemibold` | 16 | | | `Gotham` | 17 | `Gotham` has been removed. Using it will map to the `Montserrat` font family of the [Font](/docs/reference/engine/datatypes/Font.md) data type. | | `GothamMedium` | 18 | `GothamMedium` has been removed. Using it will map to the `Montserrat` font family of the [Font](/docs/reference/engine/datatypes/Font.md) data type. | | `GothamBold` | 19 | `GothamBold` has been removed. Using it will map to the `Montserrat` font family of the [Font](/docs/reference/engine/datatypes/Font.md) data type. | | `GothamBlack` | 20 | `GothamBlack` has been removed. Using it will map to the `Montserrat` font family of the [Font](/docs/reference/engine/datatypes/Font.md) data type. | | `AmaticSC` | 21 | | | `Bangers` | 22 | | | `Creepster` | 23 | | | `DenkOne` | 24 | | | `Fondamento` | 25 | | | `FredokaOne` | 26 | | | `GrenzeGotisch` | 27 | | | `IndieFlower` | 28 | | | `JosefinSans` | 29 | | | `Jura` | 30 | | | `Kalam` | 31 | | | `LuckiestGuy` | 32 | | | `Merriweather` | 33 | | | `Michroma` | 34 | | | `Nunito` | 35 | | | `Oswald` | 36 | | | `PatrickHand` | 37 | | | `PermanentMarker` | 38 | | | `Roboto` | 39 | | | `RobotoCondensed` | 40 | | | `RobotoMono` | 41 | | | `Sarpanch` | 42 | | | `SpecialElite` | 43 | | | `TitilliumWeb` | 44 | | | `Ubuntu` | 45 | | | `BuilderSans` | 46 | See [Builder Font License](/docs/en-us/resources/builder-font-license.md). | | `BuilderSansMedium` | 47 | See [Builder Font License](/docs/en-us/resources/builder-font-license.md). | | `BuilderSansBold` | 48 | See [Builder Font License](/docs/en-us/resources/builder-font-license.md). | | `BuilderSansExtraBold` | 49 | See [Builder Font License](/docs/en-us/resources/builder-font-license.md). | | `Arimo` | 50 | | | `ArimoBold` | 51 | | | `Unknown` | 100 | Set when the GUI object's font face is set to a [Font](/docs/reference/engine/datatypes/Font.md) that has no corresponding enum value. | --- name: FontSize last_updated: 2026-06-29T19:34:08Z type: enum --- # FontSize **Type:** enum ## Description The `FontSize` enum controls the font size of text in [TextBox](/docs/reference/engine/classes/TextBox.md), [TextLabel](/docs/reference/engine/classes/TextLabel.md), [TextButton](/docs/reference/engine/classes/TextButton.md), and similar objects. ## Items | Name | Value | Description | |------|-------|-------------| | `Size8` | 0 | Font size `8`. | | `Size9` | 1 | Font size `9`. | | `Size10` | 2 | Font size `10`. | | `Size11` | 3 | Font size `11`. | | `Size12` | 4 | Font size `12`. | | `Size14` | 5 | Font size `14`. | | `Size18` | 6 | Font size `18`. | | `Size24` | 7 | Font size `24`. | | `Size36` | 8 | Font size `36`. | | `Size48` | 9 | Font size `48`. | | `Size28` | 10 | Font size `28`. | | `Size32` | 11 | Font size `32`. | | `Size42` | 12 | Font size `42`. | | `Size60` | 13 | Font size `60`. | | `Size96` | 14 | Font size `96`. | --- name: FontStyle last_updated: 2026-06-29T19:34:08Z type: enum summary: "Describes whether a Font style is normal or italic." --- # FontStyle Describes whether a [Font](/docs/reference/engine/datatypes/Font.md) style is normal or italic. **Type:** enum ## Description Describes whether a [Font](/docs/reference/engine/datatypes/Font.md) style is normal or italic. Used by [Font.Style](/docs/reference/engine/datatypes/Font.md). ## Items | Name | Value | Description | |------|-------|-------------| | `Normal` | 0 | The text renders normally with this font. | | `Italic` | 1 | The text renders italic with this font. | --- name: FontWeight last_updated: 2026-06-29T19:34:08Z type: enum summary: "Describes how thick a Font is." --- # FontWeight Describes how thick a [Font](/docs/reference/engine/datatypes/Font.md) is. **Type:** enum ## Description Describes how thick a [Font](/docs/reference/engine/datatypes/Font.md) is. Used by [Font.Weight](/docs/reference/engine/datatypes/Font.md). ## Items | Name | Value | Description | |------|-------|-------------| | `Thin` | 100 | | | `ExtraLight` | 200 | | | `Light` | 300 | | | `Regular` | 400 | The default thickness for text. | | `Medium` | 500 | | | `SemiBold` | 600 | | | `Bold` | 700 | | | `ExtraBold` | 800 | | | `Heavy` | 900 | | --- name: ForceLimitMode last_updated: 2026-06-29T19:34:08Z type: enum summary: "The **ForceLimitMode** enum determines how the maximum force for a constraint is specified and how that limit is enforced by the constraint." --- # ForceLimitMode The **ForceLimitMode** enum determines how the maximum force for a constraint is specified and how that limit is enforced by the constraint. **Type:** enum ## Description This enum is used to determine how the maximum force for a constraint is specified and how that limit will be enforced by the constraint. ## Items | Name | Value | Description | |------|-------|-------------| | `Magnitude` | 0 | A single number is used to specify the magnitude of the maximum constraint force. The constraint will ensure that the force it applies will have a magnitude that is less than this limit. | | `PerAxis` | 1 | A vector is used to specify the maximum force value along each axis of a given reference frame. The constraint will ensure that each component of the force will have an absolute value that's less than the corresponding vector entry. | --- name: FormFactor last_updated: 2026-06-29T19:34:08Z type: enum --- # FormFactor **Type:** enum ## Description The FormFactor Enum for [FormFactorPart.FormFactor](/docs/reference/engine/classes/FormFactorPart.md). Minimum size along a given axis is 1 \* RateOfIncrease for that axis, except in the case of the "Custom" FormFactor, which has a minimum size of 0.2 along all axes. ## Items | Name | Value | Description | |------|-------|-------------| | `Symmetric` | 0 | Increases by a rate of 1 along all axes. | | `Brick` | 1 | Increases by a rate of 1 along the x- and z- axes, 1.2 along the y-axis. | | `Plate` | 2 | Increases by a rate of 1 along the x- and z- axes, 0.4 along the y-axis. | | `Custom` | 3 | Increases by a rate as low as .001 along all axes. | --- name: FrameStyle last_updated: 2026-06-29T19:34:08Z type: enum summary: "Used to set the style of a Frame." --- # FrameStyle Used to set the style of a [Frame](/docs/reference/engine/classes/Frame.md). **Type:** enum ## Description The **FrameStyle** enum is used to set the style of a [Frame](/docs/reference/engine/classes/Frame.md). ## Items | Name | Value | Description | |------|-------|-------------| | `Custom` | 0 | Uses the frame's [GuiObject.BackgroundColor3](/docs/reference/engine/classes/GuiObject.md), [GuiObject.BorderColor3](/docs/reference/engine/classes/GuiObject.md), and [GuiObject.BackgroundTransparency](/docs/reference/engine/classes/GuiObject.md) properties to determine the frame's appearance. It has no padding; elements with the position [UDim2.new(0, 0, 0, 0)](/docs/reference/engine/datatypes/UDim2.md) will appear at the frame's top-left corner. | | `ChatBlue` | 1 | Causes the frame to appear similar to a [Dialog](/docs/reference/engine/classes/Dialog.md) with its [Tone](/docs/reference/engine/classes/Dialog.md) property set to [DialogTone.Neutral](/docs/reference/engine/enums/DialogTone.md). Like **ChatGreen** and **ChatRed**, this has a padding of fifteen pixels on all sides. | | `RobloxSquare` | 2 | Causes the frame to appear as a translucent dark gray rectangle with a padding of five pixels on all sides. | | `RobloxRound` | 3 | Causes the frame to appear as a translucent dark gray rectangle with rounded edges. Like **RobloxSquare**, this has a padding of five pixels on all sides. | | `ChatGreen` | 4 | Causes the frame to appear similar to a [Dialog](/docs/reference/engine/classes/Dialog.md) with its [Tone](/docs/reference/engine/classes/Dialog.md) property set to [DialogTone.Friendly](/docs/reference/engine/enums/DialogTone.md). Like **ChatBlue** and **ChatRed**, this has a padding of fifteen pixels on all sides. | | `ChatRed` | 5 | Causes the frame to appear similar to a [Dialog](/docs/reference/engine/classes/Dialog.md) with its [Tone](/docs/reference/engine/classes/Dialog.md) property set to [DialogTone.Enemy](/docs/reference/engine/enums/DialogTone.md). Like **ChatBlue** and **ChatGreen**, this has a padding of fifteen pixels on all sides. | | `DropShadow` | 6 | Causes the frame to appear as a translucent gray rectangle with blurred sides. The blur is more apparent on the bottom edge. It has a padding of eight pixels on all sides. | --- name: FramerateManagerMode last_updated: 2026-06-29T19:34:08Z type: enum --- # FramerateManagerMode **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Automatic` | 0 | | | `On` | 1 | | | `Off` | 2 | | --- name: FriendRequestEvent last_updated: 2026-06-29T19:34:08Z type: enum --- # FriendRequestEvent **Type:** enum ## Description Describes the event when a player sends or responds to a friend request. This is used by the in-game friend request system. ## Items | Name | Value | Description | |------|-------|-------------| | `Issue` | 0 | A player invokes (sends) a friend request. | | `Revoke` | 1 | A player revokes (cancels) a previously invoked (sent) friend request. | | `Accept` | 2 | A player accepts a friend request. | | `Deny` | 3 | A player denies a friend request. | --- name: FriendStatus last_updated: 2026-06-29T19:34:08Z type: enum --- # FriendStatus **Type:** enum ## Description Determines the friend status between two players. This is used by the in-game friend request system. ## Items | Name | Value | Description | |------|-------|-------------| | `Unknown` | 0 | The friend status of two players is unknown. | | `NotFriend` | 1 | Two players are not friends. | | `Friend` | 2 | Two players are friends. | | `FriendRequestSent` | 3 | At least one of two players has sent a friend request to the other player. | | `FriendRequestReceived` | 4 | At least one of two players has received a friend request sent by the other player. | --- name: FunctionalTestResult last_updated: 2026-06-29T19:34:08Z type: enum summary: "Status of a single functional test run." --- # FunctionalTestResult Status of a single functional test run. **Type:** enum ## Description Represents the outcome of a single functional test run. ## Items | Name | Value | Description | |------|-------|-------------| | `Passed` | 0 | The test run was successful. | | `Warning` | 1 | The test run is potentially unstable. | | `Error` | 2 | The test run failed. | --- name: GameAvatarType last_updated: 2026-06-29T19:34:08Z type: enum summary: "Value indicating which type of avatar an experience uses." --- # GameAvatarType Value indicating which type of avatar an experience uses. **Type:** enum ## Description The value of this Enum indicates which type of avatar an experience uses. Some experiences may specify explicitly R6/R15, while some will allow the player's choice of avatar to be used. ## Items | Name | Value | Description | |------|-------|-------------| | `R6` | 0 | The R6 character rig. | | `R15` | 1 | The R15 character rig. | | `PlayerChoice` | 2 | The rig (either R6 or R15) determined by a player's character settings set on the website in the 'Edit Avatar' page. | --- name: GamepadType last_updated: 2026-06-29T19:34:08Z type: enum --- # GamepadType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Unknown` | 0 | | | `PS4` | 1 | | | `PS5` | 2 | | | `XboxOne` | 3 | | --- name: GearGenreSetting last_updated: 2026-06-29T19:34:08Z type: enum tags: - Deprecated --- # GearGenreSetting **Type:** enum ## Description The GearGenreSetting Enum sets what types of gear are allowed in a place. ## Items | Name | Value | Description | |------|-------|-------------| | `AllGenres` | 0 | All gear genres are allowed. | | `MatchingGenreOnly` | 1 | Only gear genres matching the allowed genres specified in a place's configuration settings. | --- name: GearType last_updated: 2026-06-29T19:34:08Z type: enum tags: - Deprecated --- # GearType **Type:** enum ## Description The GearType Enum is used as an argument in [DataModel:IsGearTypeAllowed()](/docs/reference/engine/classes/DataModel.md). Gear may have more than one type/attribute. ## Items | Name | Value | Description | |------|-------|-------------| | `MeleeWeapons` | 0 | Gear with the 'Melee' attribute. | | `RangedWeapons` | 1 | Gear with the 'Ranged' attribute. | | `Explosives` | 2 | Gear with the 'Explosive' attribute. | | `PowerUps` | 3 | Gear with the 'Power Up' attribute. | | `NavigationEnhancers` | 4 | Gear with the 'Navigation' attribute. | | `MusicalInstruments` | 5 | Gear with the 'Musical' attribute. | | `SocialItems` | 6 | Gear with the 'Social' attribute. | | `BuildingTools` | 7 | Gear with the 'Building' attribute. | | `Transport` | 8 | Gear with the 'Transport' attribute. | --- name: Genre last_updated: 2026-06-29T19:34:08Z type: enum tags: - Deprecated summary: "Used to represent the type of game." --- # Genre Used to represent the type of game. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `All` | 0 | The game's genre is set to All. The game will be listed as all possible genres. | | `TownAndCity` | 1 | The game's genre is set to Town and City. | | `Fantasy` | 2 | The game's genre is set to Fantasy. | | `SciFi` | 3 | The game's genre is set to SciFi (Science Fiction). | | `Ninja` | 4 | The game's genre is set to Ninja. | | `Scary` | 5 | The game's genre is set to Scary. | | `Pirate` | 6 | The game's genre is set to Pirate. | | `Adventure` | 7 | The game's genre is set to Adventure. | | `Sports` | 8 | The game's genre is set to Sports. | | `Funny` | 9 | The game's genre is set to Funny. | | `WildWest` | 10 | The game's genre is set to Wild West. | | `War` | 11 | The game's genre is set to War. | | `SkatePark` | 12 | The game's genre is set to Skate Park. | | `Tutorial` | 13 | The game's genre is set to Tutorial. | --- name: GraphicsMode last_updated: 2026-06-29T19:34:08Z type: enum summary: "Used to set the graphics API that Roblox uses to render the game." --- # GraphicsMode Used to set the graphics API that Roblox uses to render the game. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Automatic` | 1 | Roblox will automatically decide what graphics mode to use, depending on what best suits your device's specifications. | | `Direct3D11` | 2 | Direct3D Version 11 (Microsoft). | | `OpenGL` | 4 | OpenGL (Khronos Group). | | `Metal` | 5 | Metal (Apple). | | `Vulkan` | 6 | Vulkan (Khronos Group). | | `NoGraphics` | 9 | Game will not be rendered, and instead a _Diagnostics_ window is rendered on screen. | --- name: GraphicsOptimizationMode last_updated: 2026-06-29T19:34:08Z type: enum --- # GraphicsOptimizationMode **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Performance` | 0 | | | `Balanced` | 1 | | | `Quality` | 2 | | --- name: GroupMembershipStatus last_updated: 2026-06-29T19:34:08Z type: enum summary: "Defines the possible outcomes of the GroupService:PromptJoinAsync() method." --- # GroupMembershipStatus Defines the possible outcomes of the [GroupService:PromptJoinAsync()](/docs/reference/engine/classes/GroupService.md) method. **Type:** enum ## Description `GroupMembershipStatus` is an enumeration that represents the result of a player's interaction with the [GroupService:PromptJoinAsync()](/docs/reference/engine/classes/GroupService.md) method. It conveys the player's group membership status after the prompt. ## Items | Name | Value | Description | |------|-------|-------------| | `None` | 0 | The player chose not to join, cancelled the prompt, or was not eligible to join the group. | | `Joined` | 1 | The player successfully joined the group during the prompt. | | `JoinRequestPending` | 2 | The player submitted a request to join the group, or already had a pending join request prior to prompting. | | `AlreadyMember` | 3 | The player was already a member of the group. | --- name: GuiState last_updated: 2026-06-29T19:34:08Z type: enum --- # GuiState **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Idle` | 0 | The GUI currently has no state. | | `Hover` | 1 | The GUI is being hovered over by the user. | | `Press` | 2 | The GUI is being pressed/held down by the user. | | `NonInteractable` | 3 | The GUI cannot be interacted with and therefore has no states. | --- name: GuiType last_updated: 2026-06-29T19:34:08Z type: enum --- # GuiType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Core` | 0 | | | `Custom` | 1 | | | `PlayerNameplates` | 2 | | | `CustomBillboards` | 3 | | | `CoreBillboards` | 4 | | --- name: HandlesStyle last_updated: 2026-06-29T19:34:08Z type: enum --- # HandlesStyle **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Resize` | 0 | | | `Movement` | 1 | | --- name: HapticEffectType last_updated: 2026-06-29T19:34:08Z type: enum summary: "Enum used alongside HapticEffect.Type." --- # HapticEffectType Enum used alongside [HapticEffect.Type](/docs/reference/engine/classes/HapticEffect.md). **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Custom` | 0 | Allows for application of a custom haptic waveform through the [HapticEffect:SetWaveformKeys()](/docs/reference/engine/classes/HapticEffect.md) method. | | `UIHover` | 1 | Useful for when a player browses over an object (often a UI object) without the intention of triggering its action; it can also alert the player that they have browsed over an interactable object. This effect type is subtle and does not disrupt the gameplay experience. | | `UIClick` | 2 | Useful for when a player has selected an object (often a UI object) with the intention of triggering its action. This effect type is crisp and it provides immediate feedback without being overwhelming. | | `UINotification` | 3 | Useful for when there is an inbound message that should draw the player's attention away from their current gameplay and prompt them that the notification requires immediate attention or action. | | `GameplayExplosion` | 4 | Useful to signify a large-scale physics event that triggers impact across a large portion of a given scene. This effect is high intensity in order to represent the magnitude of the impact and it lingers for a longer period of time than `GameplayCollision`. | | `GameplayCollision` | 5 | This effect is a large immediate rumble that dies down quickly, useful to signify a clear and purposeful impact between objects. | --- name: HashAlgorithm last_updated: 2026-06-29T19:34:08Z type: enum summary: "A Cryptographic hash function to use in EncodingService methods." --- # HashAlgorithm A Cryptographic hash function to use in [EncodingService](/docs/reference/engine/classes/EncodingService.md) methods. **Type:** enum ## Description A [Cryptographic hash function](https://en.wikipedia.org/wiki/Cryptographic_hash_function) to use in [EncodingService](/docs/reference/engine/classes/EncodingService.md) methods. ## Items | Name | Value | Description | |------|-------|-------------| | `Blake2b` | 0 | Used to compute a 256-bit (32-byte) hash digest using the [BLAKE2b](https://en.wikipedia.org/wiki/BLAKE2) algorithm. | | `Blake3` | 1 | Used to compute a 256-bit (32-byte) hash digest using the [BLAKE3](https://en.wikipedia.org/wiki/BLAKE3) algorithm. | | `Md5` | 2 | Used to compute a 128-bit (16-byte) hash digest using the [MD5](https://en.wikipedia.org/wiki/MD5) algorithm. | | `Sha1` | 3 | Used to compute a 160-bit (20-byte) hash digest using the [SHA-1](https://en.wikipedia.org/wiki/SHA-1) algorithm. | | `Sha256` | 4 | Used to compute a 256-bit (32-byte) hash digest using the [SHA256](https://en.wikipedia.org/wiki/SHA-2) algorithm. | --- name: HighlightDepthMode last_updated: 2026-06-29T19:34:08Z type: enum summary: "Controls how the Highlight effect displays with respect to other objects in the world." --- # HighlightDepthMode Controls how the [Highlight](/docs/reference/engine/classes/Highlight.md) effect displays with respect to other objects in the world. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `AlwaysOnTop` | 0 | Allows the [Highlight](/docs/reference/engine/classes/Highlight.md) to display regardless if there are objects between the camera and the highlighted object. This means the viewer is always able to see the highlight regardless of what is between the highlighted object and the camera. | | `Occluded` | 1 | Hides the [Highlight](/docs/reference/engine/classes/Highlight.md) if there are objects between the camera and the highlighted object. This means the viewer is only able to see the object if there are no obstructing objects between the highlighted object and the camera's view. | --- name: HorizontalAlignment last_updated: 2026-06-29T19:34:08Z type: enum summary: "Used by UIGridStyleLayout.HorizontalAlignment to align the layout horizontally within its parent." --- # HorizontalAlignment Used by [UIGridStyleLayout.HorizontalAlignment](/docs/reference/engine/classes/UIGridStyleLayout.md) to align the layout horizontally within its parent. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Center` | 0 | Grid is aligned to the center of its parent along the **X** axis. | | `Left` | 1 | Grid is aligned to the left edge of its parent. | | `Right` | 2 | Grid is aligned to the right edge of its parent. | --- name: HoverAnimateSpeed last_updated: 2026-06-29T19:34:08Z type: enum --- # HoverAnimateSpeed **Type:** enum ## Description Specifies how frequently the hover animation flashes. ## Items | Name | Value | Description | |------|-------|-------------| | `VerySlow` | 0 | 2 seconds per cycle. | | `Slow` | 1 | 1 second per cycle. | | `Medium` | 2 | 0.5 seconds per cycle. | | `Fast` | 3 | 0.25 seconds per cycle. | | `VeryFast` | 4 | 0.1 seconds per cycle. | --- name: HttpCachePolicy last_updated: 2026-06-29T19:34:08Z type: enum --- # HttpCachePolicy **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `None` | 0 | | | `Full` | 1 | | | `DataOnly` | 2 | | | `Default` | 3 | | | `InternalRedirectRefresh` | 4 | | --- name: HttpCompression last_updated: 2026-06-29T19:34:08Z type: enum --- # HttpCompression **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `None` | 0 | | | `Gzip` | 1 | | --- name: HttpContentType last_updated: 2026-06-29T19:34:08Z type: enum summary: "Specifies the value of the HTTP Content-Type header which describes the HTTP request data type." --- # HttpContentType Specifies the value of the HTTP Content-Type header which describes the HTTP request data type. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `ApplicationJson` | 0 | Represents the "application/json" Content-Type. | | `ApplicationXml` | 1 | Represents the "application/xml" Content-Type. | | `ApplicationUrlEncoded` | 2 | Represents the "application/x-www-form-urlencoded" Content-Type. | | `TextPlain` | 3 | Represents the "text/plain" Content-Type. | | `TextXml` | 4 | Represents the "text/xml" Content-Type. | --- name: HttpError last_updated: 2026-06-29T19:34:08Z type: enum --- # HttpError **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `OK` | 0 | | | `InvalidUrl` | 1 | | | `DnsResolve` | 2 | | | `ConnectFail` | 3 | | | `OutOfMemory` | 4 | | | `TimedOut` | 5 | | | `TooManyRedirects` | 6 | | | `InvalidRedirect` | 7 | | | `NetFail` | 8 | | | `Aborted` | 9 | | | `SslConnectFail` | 10 | | | `SslVerificationFail` | 11 | | | `Unknown` | 12 | | | `ConnectionClosed` | 13 | | | `ServerProtocolError` | 14 | | | `CreatorEnvironmentsNotSupportedByService` | 15 | | | `InactivityTimeout` | 16 | | | `TooManyOutstandingRequests` | 17 | | --- name: HttpRequestType last_updated: 2026-06-29T19:34:08Z type: enum --- # HttpRequestType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | | | `MarketplaceService` | 2 | | | `Players` | 7 | | | `Chat` | 15 | | | `Avatar` | 16 | | | `Analytics` | 23 | | | `Localization` | 25 | | --- name: HumanoidCollisionType last_updated: 2026-06-29T19:34:08Z type: enum summary: "The collision Humanoid uses." --- # HumanoidCollisionType The collision [Humanoid](/docs/reference/engine/classes/Humanoid.md) uses. **Type:** enum ## Description Enum to select the [Humanoid.CollisionType](/docs/reference/engine/classes/Humanoid.md). ## Items | Name | Value | Description | |------|-------|-------------| | `OuterBox` | 0 | Dynamically sized collision boxes based on mesh sizes. | | `InnerBox` | 1 | Fixed size collision boxes, similar to the classic avatar collision. | --- name: HumanoidDisplayDistanceType last_updated: 2026-06-29T19:34:08Z type: enum summary: "HumanoidDisplayDistanceType determines how Humanoid.HealthDisplayDistance, and Humanoid.NameDisplayDistance are used in determining whether a players's name and health are visible to other players." --- # HumanoidDisplayDistanceType HumanoidDisplayDistanceType determines how [Humanoid.HealthDisplayDistance](/docs/reference/engine/classes/Humanoid.md), and [Humanoid.NameDisplayDistance](/docs/reference/engine/classes/Humanoid.md) are used in determining whether a players's name and health are visible to other players. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Viewer` | 0 | A player selecting `Viewer` on their [Humanoid](/docs/reference/engine/classes/Humanoid.md) will see the Name and healthbar of other players if those other players are within the distance settings set in the [Humanoid](/docs/reference/engine/classes/Humanoid.md) selecting `Viewer` (this can be thought of as the lowest priority, and will not be taken into account if other players are selecting 'Subject' or 'None'). | | `Subject` | 1 | A player selecting `Subject` on their [Humanoid](/docs/reference/engine/classes/Humanoid.md) will allow other players to see their Name and healthbar if those other players are within the distance settings set in the [Humanoid](/docs/reference/engine/classes/Humanoid.md) selecting `Subject` (this can be thought of as the second highest priority). | | `None` | 2 | A player selecting `None` on their [Humanoid](/docs/reference/engine/classes/Humanoid.md) will not have their Name and healthbar displayed to other players under any circumstance (this can be thought of as the highest priority). | --- name: HumanoidHealthDisplayType last_updated: 2026-06-29T19:34:08Z type: enum summary: "Controls under what circumstances the Humanoid health bar is displayed." --- # HumanoidHealthDisplayType Controls under what circumstances the [Humanoid](/docs/reference/engine/classes/Humanoid.md) health bar is displayed. **Type:** enum ## Description Controls when the [Humanoid](/docs/reference/engine/classes/Humanoid.md) health bar is displayed. This works in conjunction with the [Humanoid.MaxHealth](/docs/reference/engine/classes/Humanoid.md) property which must have a value higher than zero or the health bar doesn't display. ## Items | Name | Value | Description | |------|-------|-------------| | `DisplayWhenDamaged` | 0 | The humanoid's health bar is only visible when the humanoid is not at full health (assuming `MaxHealth` is greater than zero). | | `AlwaysOn` | 1 | The humanoid's health bar is always visible (assuming `MaxHealth` is greater than zero). | | `AlwaysOff` | 2 | The humanoid's health bar is never visible. | --- name: HumanoidRigType last_updated: 2026-06-29T19:34:08Z type: enum summary: "Describes whether or not a character is using the new R15 rig, or the legacy R6 rig." --- # HumanoidRigType Describes whether or not a character is using the new R15 rig, or the legacy R6 rig. **Type:** enum ## Description Describes whether or not a character is using the new R15 rig, or the legacy R6 rig. This is the type returned by Humanoid's RigType property, which indicates whether the Humanoid is R6 or R15. ## Items | Name | Value | Description | |------|-------|-------------| | `R6` | 0 | Indicates that a character is using the legacy R6 rig. | | `R15` | 1 | Indicates that a character is using the new R15 rig. | --- name: HumanoidStateType last_updated: 2026-06-29T19:34:08Z type: enum summary: "Describes the physics control states within the Humanoid." --- # HumanoidStateType Describes the physics control states within the Humanoid. **Type:** enum ## Description Identifies, reads and sets the physics control state of a [Humanoid](/docs/reference/engine/classes/Humanoid.md). [Humanoid:GetState()](/docs/reference/engine/classes/Humanoid.md) and [Humanoid:ChangeState()](/docs/reference/engine/classes/Humanoid.md) methods, as well as the [Humanoid.StateChanged](/docs/reference/engine/classes/Humanoid.md) event currently use this Enum. Some states only allow manual setting, and allow a developer to make the Humanoid relinquish control of its character. When altering the Humanoid of a player, this should be done from a [LocalScript](/docs/reference/engine/classes/LocalScript.md) ran by that player on their local client. Certain states only work when set by the owner process (client or server). (Dead for example) ## Items | Name | Value | Description | |------|-------|-------------| | `FallingDown` | 0 | The Humanoid has been tripped, and will attempt to get up in a few moments. | | `Ragdoll` | 1 | (Deprecated) The Humanoid has been hit by a fast moving object (uncontrolled falling). _The Humanoid can recover from this._ This state has to be set and unset manually using [Humanoid:ChangeState()](/docs/reference/engine/classes/Humanoid.md). | | `GettingUp` | 2 | The Humanoid is getting back on their feet after FallingDown or Ragdoll. | | `Jumping` | 3 | The Humanoid just jumped. (Check [Humanoid.Jump](/docs/reference/engine/classes/Humanoid.md)). This state lasts only briefly. This state normally transitions into either Landed, if on the ground, or Freefall, if still in the air. | | `Swimming` | 4 | The Humanoid is currently swimming in [Terrain](/docs/reference/engine/classes/Terrain.md) water. | | `Freefall` | 5 | The Humanoid is currently freefalling (jumped from a height or fell off a ledge). | | `Flying` | 6 | When set, the Humanoid won't be animated, as with the [Humanoid.PlatformStand](/docs/reference/engine/classes/Humanoid.md) property. This state lasts as long as the player flies. | | `Landed` | 7 | The Humanoid touched the ground after a Freefall. This state lasts only briefly. | | `Running` | 8 | Currently running while on the ground. | | `RunningNoPhysics` | 10 | (Deprecated) Currently running and not near other physical objects. | | `StrafingNoPhysics` | 11 | Not currently used with default Humanoid. Cannot be set with [Humanoid:ChangeState()](/docs/reference/engine/classes/Humanoid.md). | | `Climbing` | 12 | The Humanoid is climbing (e.g. up a [TrussPart](/docs/reference/engine/classes/TrussPart.md) or ladder). | | `Seated` | 13 | The Humanoid is currently sitting in a Seat or VehicleSeat. Check the [Humanoid.Sit](/docs/reference/engine/classes/Humanoid.md) property. | | `PlatformStanding` | 14 | The Humanoid is platformstanding. Check the [Humanoid.PlatformStand](/docs/reference/engine/classes/Humanoid.md) property. | | `Dead` | 15 | The Humanoid died. Changing a Humanoid's state to this state will kill it. | | `Physics` | 16 | The Humanoid doesn't apply any force on its own and will not automatically transition to any other state. This state has to be set and unset manually using [Humanoid:ChangeState()](/docs/reference/engine/classes/Humanoid.md). | | `None` | 18 | Unusable placeholder in case an unknown state gets triggered internally. | --- name: IKCollisionsMode last_updated: 2026-06-29T19:34:08Z type: enum --- # IKCollisionsMode **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `NoCollisions` | 0 | Only the part and any parts directly joined to it via joints/[constraints](/docs/reference/engine/classes/Constraint.md) be involved in the resolution, everything else in the workspace will be treated as though it doesn't exist. | | `OtherMechanismsAnchored` | 1 | Only the part and any parts directly jointed to it via joints/[constraints](/docs/reference/engine/classes/Constraint.md) will be moved during resolution, but they will collide with other objects in the workspace. | | `IncludeContactedMechanisms` | 2 | The part, any parts directly joined to it via joints/[constraints](/docs/reference/engine/classes/Constraint.md), and any parts which it comes into contact with during the solve will be moved during the resolution. That is, the moved parts will be allowed to "push" other unanchored parts in the workspace out of the way in order to get to the target position. | --- name: IKControlConstraintSupport last_updated: 2026-06-29T19:34:08Z type: enum summary: "Values for Workspace.IKControlConstraintSupport. Sets the support for constraints for IKControls in your experience." --- # IKControlConstraintSupport Values for [Workspace.IKControlConstraintSupport](/docs/reference/engine/classes/Workspace.md). Sets the support for constraints for [IKControls](/docs/reference/engine/classes/IKControl.md) in your experience. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | The default option for [IKControls](/docs/reference/engine/classes/IKControl.md). The default is currently set to `Enabled`. | | `Disabled` | 1 | Disables support for constraints for [IKControls](/docs/reference/engine/classes/IKControl.md). When disabled, [IKControls](/docs/reference/engine/classes/IKControl.md) still work, but ignore physics constraints. | | `Enabled` | 2 | Enables support for constraints for [IKControls](/docs/reference/engine/classes/IKControl.md). | --- name: IKControlType last_updated: 2026-06-29T19:34:08Z type: enum summary: "Used on IKControl to specify their Type, to change their behavior." --- # IKControlType Used on [IKControl](/docs/reference/engine/classes/IKControl.md) to specify their [Type](/docs/reference/engine/classes/IKControl.md), to change their behavior. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Transform` | 0 | It is a full 6-DoF constraint. Aligns the [EndEffector](/docs/reference/engine/classes/IKControl.md) [CFrame](/docs/reference/engine/datatypes/CFrame.md) to that of the [Target](/docs/reference/engine/classes/IKControl.md). It is the default value. | | `Position` | 1 | Aligns the [EndEffector](/docs/reference/engine/classes/IKControl.md) position to that of the [Target](/docs/reference/engine/classes/IKControl.md) . | | `Rotation` | 2 | Aligns the [EndEffector](/docs/reference/engine/classes/IKControl.md) rotation to that of the [Target](/docs/reference/engine/classes/IKControl.md). | | `LookAt` | 3 | Moves and orients the whole chain to make the forward axis on the [EndEffector](/docs/reference/engine/classes/IKControl.md) point at a position in the world specified by [Target](/docs/reference/engine/classes/IKControl.md). | --- name: IXPLoadingStatus last_updated: 2026-06-29T19:34:08Z type: enum --- # IXPLoadingStatus **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `None` | 0 | | | `Pending` | 1 | | | `Initialized` | 2 | | | `ErrorInvalidUser` | 3 | | | `ErrorConnection` | 4 | | | `ErrorJsonParse` | 5 | | | `ErrorTimedOut` | 6 | | --- name: ImageAlphaType last_updated: 2026-06-29T19:34:08Z type: enum summary: "Enum for determining if the alpha or color is locked while combining two images." --- # ImageAlphaType Enum for determining if the alpha or color is locked while combining two images. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 1 | The default behavior in which neither color nor alpha values are locked. | | `LockCanvasAlpha` | 2 | The alpha values of the image will not be modified. | | `LockCanvasColor` | 3 | The color values of the image will not be modified. | --- name: ImageCombineType last_updated: 2026-06-29T19:34:08Z type: enum summary: "Enum for determining how two images are combined together." --- # ImageCombineType Enum for determining how two images are combined together. **Type:** enum ## Description Enum for determining how two images are combined together. In all combine operations, **source** refers to the new pixels being drawn and **destination** refers to the existing pixels in the image being drawn onto. ## Items | Name | Value | Description | |------|-------|-------------| | `BlendSourceOver` | 1 | Blends pixels from the source with pixels from the destination using source over alpha blending. | | `Overwrite` | 2 | Overwrites all pixels in the destination image with pixels from the source image. | | `Add` | 3 | Adds pixels from the source and pixels from the destination together. | | `Multiply` | 4 | Multiplies pixels from the source and pixels from the destination together. RGBA values are multiplied as values between `0` and `1`. Values lower than `1` have a darkening effect on the image. | | `AlphaBlend` | 5 | Blends pixels from the source with pixels from the destination based on the alpha of the source pixels. Unlike **BlendSourceOver**, the destination color in **AlphaBlend** affects the resulting color of the image, regardless of the destination color's alpha. | | `NormalMapBlend` | 6 | | --- name: InOut last_updated: 2026-06-29T19:34:08Z type: enum summary: "The InOut Enum is used to set where the object is on the side of its parent." --- # InOut The InOut Enum is used to set where the object is on the side of its parent. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Edge` | 0 | The object is located along an edge of its parent. | | `Inset` | 1 | The object's located is inset from its parent's location. | | `Center` | 2 | The object's location is centered on it's parent. | --- name: InfoType last_updated: 2026-06-29T19:34:08Z type: enum --- # InfoType **Type:** enum ## Description Used to represent the possible information formats returnable by [MarketplaceService:GetProductInfoAsync()](/docs/reference/engine/classes/MarketplaceService.md). ## Items | Name | Value | Description | |------|-------|-------------| | `Asset` | 0 | The product is an asset. | | `Product` | 1 | Product such as a [Developer Product](/docs/en-us/production/monetization/developer-products.md). | | `GamePass` | 2 | The product is a game pass. | | `Subscription` | 3 | | | `Bundle` | 4 | | --- name: InitialDockState last_updated: 2026-06-29T19:34:08Z type: enum summary: "Describes the initial docking state of a DockWidgetPluginGui." --- # InitialDockState Describes the initial docking state of a [DockWidgetPluginGui](/docs/reference/engine/classes/DockWidgetPluginGui.md). **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Top` | 0 | The widget will appear docked above the game screen. | | `Bottom` | 1 | The widget will appear docked below the game screen. | | `Left` | 2 | The widget will appear docked to the left of the game screen. | | `Right` | 3 | The widget will appear docked to the right of the game screen. | | `Float` | 4 | The widget will not be docked to any side of the game screen, and instead can be freely dragged around. | --- name: InputActionType last_updated: 2026-06-29T19:34:08Z type: enum summary: "This enum is used by InputAction.Type to determine which input data type the InputAction will receive." --- # InputActionType This enum is used by [InputAction.Type](/docs/reference/engine/classes/InputAction.md) to determine which input data type the [InputAction](/docs/reference/engine/classes/InputAction.md) will receive. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Bool` | 0 | The [InputAction](/docs/reference/engine/classes/InputAction.md) will receive boolean values from button inputs, for example `true`/`false` on press/release from inputs such as [KeyCode.ButtonA](/docs/reference/engine/enums/KeyCode.md) or [KeyCode.E](/docs/reference/engine/enums/KeyCode.md). This setting also exposes the [UIButton](/docs/reference/engine/classes/InputBinding.md) property on child [InputBindings](/docs/reference/engine/classes/InputBinding.md), allowing you to easily hook up press or release of a [GuiButton](/docs/reference/engine/classes/GuiButton.md) for the action. | | `Direction1D` | 1 | The [InputAction](/docs/reference/engine/classes/InputAction.md) will receive numerical values, generally from analog gamepad triggers such as [KeyCode.ButtonL2](/docs/reference/engine/enums/KeyCode.md) or [KeyCode.ButtonR2](/docs/reference/engine/enums/KeyCode.md). This setting also exposes the [Up](/docs/reference/engine/classes/InputBinding.md) and [Down](/docs/reference/engine/classes/InputBinding.md) properties on child [InputBindings](/docs/reference/engine/classes/InputBinding.md), allowing for boolean inputs or "1D" inputs as composite directions for the action. | | `Direction2D` | 2 | The [InputAction](/docs/reference/engine/classes/InputAction.md) will receive [Vector2](/docs/reference/engine/datatypes/Vector2.md) values, generally from thumbstick inputs such as [KeyCode.Thumbstick1](/docs/reference/engine/enums/KeyCode.md) and [KeyCode.Thumbstick2](/docs/reference/engine/enums/KeyCode.md). This setting also exposes the [Up](/docs/reference/engine/classes/InputBinding.md), [Down](/docs/reference/engine/classes/InputBinding.md), [Left](/docs/reference/engine/classes/InputBinding.md), and [Right](/docs/reference/engine/classes/InputBinding.md) properties on child [InputBindings](/docs/reference/engine/classes/InputBinding.md), allowing for "2D" inputs as composite directions for the action. | | `Direction3D` | 3 | The [InputAction](/docs/reference/engine/classes/InputAction.md) will receive [Vector3](/docs/reference/engine/datatypes/Vector3.md) values from inputs assigned to the [Up](/docs/reference/engine/classes/InputBinding.md), [Down](/docs/reference/engine/classes/InputBinding.md), [Left](/docs/reference/engine/classes/InputBinding.md), [Right](/docs/reference/engine/classes/InputBinding.md), [Forward](/docs/reference/engine/classes/InputBinding.md), and/or [Backward](/docs/reference/engine/classes/InputBinding.md) properties on child [InputBindings](/docs/reference/engine/classes/InputBinding.md), allowing for "3D" inputs as composite directions for the action. | | `ViewportPosition` | 4 | The [InputAction](/docs/reference/engine/classes/InputAction.md) will receive [Vector2](/docs/reference/engine/datatypes/Vector2.md) values representing the absolute pixel (**X**, **Y**) coordinates of a pointer input in the viewport. | --- name: InputBindingType last_updated: 2026-06-29T19:34:08Z type: enum summary: "This enum is used by InputBinding.Type to determine whether the binding responds to hardware input or is driven programmatically." --- # InputBindingType This enum is used by [InputBinding.Type](/docs/reference/engine/classes/InputBinding.md) to determine whether the binding responds to hardware input or is driven programmatically. **Type:** enum ## Description This enum is used by [InputBinding.Type](/docs/reference/engine/classes/InputBinding.md) to determine whether the [InputBinding](/docs/reference/engine/classes/InputBinding.md) responds to hardware input devices or is driven programmatically via [InputBinding:Fire()](/docs/reference/engine/classes/InputBinding.md). ## Items | Name | Value | Description | |------|-------|-------------| | `Automatic` | 0 | The [InputBinding](/docs/reference/engine/classes/InputBinding.md) responds to physical input devices (keyboard, mouse, gamepad, touch) based on its [KeyCode](/docs/reference/engine/classes/InputBinding.md) or composite direction properties. This is the default. | | `Scriptable` | 1 | The [InputBinding](/docs/reference/engine/classes/InputBinding.md) ignores hardware input and instead receives state updates exclusively through the [InputBinding:Fire()](/docs/reference/engine/classes/InputBinding.md) method. | --- name: InputSink last_updated: 2026-06-29T19:34:08Z type: enum --- # InputSink **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `None` | 0 | | | `Activate` | 1 | | | `All` | 100 | | --- name: InputType last_updated: 2026-06-29T19:34:08Z type: enum summary: "The InputType Enum controls the SurfaceInputs of Part." --- # InputType The InputType Enum controls the SurfaceInputs of [Part](/docs/reference/engine/classes/Part.md). **Type:** enum ## Description The InputType Enum controls the SurfaceInputs of [Part](/docs/reference/engine/classes/Part.md). Several parameters here are left-overs from 2005, before Roblox was a multiplayer game, and are used by [Part.BackSurfaceInput](/docs/reference/engine/classes/Part.md), [Part.BottomSurfaceInput](/docs/reference/engine/classes/Part.md), [Part.FrontSurfaceInput](/docs/reference/engine/classes/Part.md), [Part.LeftSurfaceInput](/docs/reference/engine/classes/Part.md), [Part.RightSurfaceInput](/docs/reference/engine/classes/Part.md), [Part.TopSurfaceInput](/docs/reference/engine/classes/Part.md). ## Items | Name | Value | Description | |------|-------|-------------| | `NoInput` | 0 | Behaves like a weld and does nothing. | | `Constant` | 12 | Rotate at a constant velocity of [BasePart](/docs/reference/engine/classes/BasePart.md) `ParamB`. | | `Sin` | 13 | Rotate at a velocity of: `ParamA * math.sin(workspace.DistributedGameTime * ParamB)`, where [BasePart](/docs/reference/engine/classes/BasePart.md) `ParamA` determines the maximum speed at which the part spins, and [BasePart](/docs/reference/engine/classes/BasePart.md) `ParamB` determines how frequently it changes direction. | --- name: InstanceFileSyncStatus last_updated: 2026-06-29T19:34:08Z type: enum summary: "Describes the file sync status of an Instance." --- # InstanceFileSyncStatus Describes the file sync status of an Instance. **Type:** enum ## Description Describes the file sync status of an [Instance](/docs/reference/engine/classes/Instance.md). ## Items | Name | Value | Description | |------|-------|-------------| | `NotSynced` | 0 | The [Instance](/docs/reference/engine/classes/Instance.md) is not being synced or errored. | | `Errored` | 1 | The [Instance](/docs/reference/engine/classes/Instance.md) is a sync root that has errored and stopped syncing. | | `SyncedAsRoot` | 2 | The [Instance](/docs/reference/engine/classes/Instance.md) is being synced as the root of a sync tree. | | `SyncedAsDescendant` | 3 | The [Instance](/docs/reference/engine/classes/Instance.md) is being synced because it is a descendant of a sync root. | | `AncestorErrored` | 4 | The [Instance](/docs/reference/engine/classes/Instance.md) is not currently being synced because the root of its sync tree is `Errored`. | --- name: IntermediateMeshGenerationResult last_updated: 2026-06-29T19:34:08Z type: enum --- # IntermediateMeshGenerationResult **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `HighQualityMesh` | 0 | | --- name: InterpolationThrottlingMode last_updated: 2026-06-29T19:34:08Z type: enum --- # InterpolationThrottlingMode **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | | | `Disabled` | 1 | | | `Enabled` | 2 | | --- name: InviteState last_updated: 2026-06-29T19:34:08Z type: enum summary: "Describes a player's call invite state." --- # InviteState Describes a player's call invite state. **Type:** enum ## Description This enum describes a player's call invite state. See [SocialService.CallInviteStateChanged](/docs/reference/engine/classes/SocialService.md) for details. ## Items | Name | Value | Description | |------|-------|-------------| | `Placed` | 0 | Call invite was placed. | | `Accepted` | 1 | Call invite was accepted. | | `Declined` | 2 | Call invite was declined. | | `Missed` | 3 | Call invite was missed. | --- name: ItemLineAlignment last_updated: 2026-06-29T19:34:08Z type: enum summary: "Used for UIListLayout.ItemLineAlignment and UIFlexItem.ItemLineAlignment in a flex layout to define the cross-directional alignment of siblings or the parent within a line." --- # ItemLineAlignment Used for [UIListLayout.ItemLineAlignment](/docs/reference/engine/classes/UIListLayout.md) and [UIFlexItem.ItemLineAlignment](/docs/reference/engine/classes/UIFlexItem.md) in a flex layout to define the cross-directional alignment of siblings or the parent within a line. **Type:** enum ## Description In a [flex layout](/docs/en-us/ui/list-flex-layouts.md#flex-layouts), this enum is used for the [ItemLineAlignment](/docs/reference/engine/classes/UIListLayout.md) property of the [UIListLayout](/docs/reference/engine/classes/UIListLayout.md) to define the **cross-directional** alignment of siblings within a line. It is also used for the [ItemLineAlignment](/docs/reference/engine/classes/UIFlexItem.md) property of a specific [UIFlexItem](/docs/reference/engine/classes/UIFlexItem.md) to define the cross-directional alignment of the parent [GuiObject](/docs/reference/engine/classes/GuiObject.md) within the line. ![Examples of options for ItemLineAlignment in a horizontal fill direction.](../../../assets/engine-api/classes/UIListLayout/ItemLineAlignment.png) ## Items | Name | Value | Description | |------|-------|-------------| | `Automatic` | 0 | Aligns the siblings of the [UIListLayout](/docs/reference/engine/classes/UIListLayout.md) or the specific [UIFlexItem](/docs/reference/engine/classes/UIFlexItem.md) parent to the layout's [HorizontalAlignment](/docs/reference/engine/classes/UIListLayout.md) or [VerticalAlignment](/docs/reference/engine/classes/UIListLayout.md), depending on its [FillDirection](/docs/reference/engine/classes/UIListLayout.md). If [HorizontalFlex](/docs/reference/engine/classes/UIListLayout.md) or [VerticalFlex](/docs/reference/engine/classes/UIListLayout.md) is enabled for the [UIListLayout](/docs/reference/engine/classes/UIListLayout.md) cross‑direction, `Stretch` will be used for that direction. | | `Start` | 1 | Aligns the siblings of the [UIListLayout](/docs/reference/engine/classes/UIListLayout.md) or the specific [UIFlexItem](/docs/reference/engine/classes/UIFlexItem.md) parent to the line's **top** in a horizontal fill or the line's **left** in a vertical fill. | | `Center` | 2 | Aligns the siblings of the [UIListLayout](/docs/reference/engine/classes/UIListLayout.md) or the specific [UIFlexItem](/docs/reference/engine/classes/UIFlexItem.md) parent to the line's **center** in either a horizontal or vertical fill. | | `End` | 3 | Aligns the siblings of the [UIListLayout](/docs/reference/engine/classes/UIListLayout.md) or the specific [UIFlexItem](/docs/reference/engine/classes/UIFlexItem.md) parent to the line's **bottom** in a horizontal fill or the line's **right** in a vertical fill. | | `Stretch` | 4 | Stretches the siblings of the [UIListLayout](/docs/reference/engine/classes/UIListLayout.md) or the specific [UIFlexItem](/docs/reference/engine/classes/UIFlexItem.md) parent to fill the entire cross‑direction of the line in either a horizontal or vertical fill. | --- name: JoinSource last_updated: 2026-06-29T19:34:08Z type: enum --- # JoinSource **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `CreatedItemAttribution` | 1 | | --- name: JointCreationMode last_updated: 2026-06-29T19:34:08Z type: enum summary: "The way joints are created between two surfaces." --- # JointCreationMode The way joints are created between two surfaces. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `All` | 0 | Joints are created between any kind of surface. Functions identically to Surface. | | `Surface` | 1 | Joints are created between any kind of surface. Functions identically to All. | | `None` | 2 | Joints are never created, no matter what. | --- name: KeyCode last_updated: 2026-06-29T19:34:08Z type: enum summary: "Byte keycodes that represent the key or button involved in user input." --- # KeyCode Byte keycodes that represent the key or button involved in user input. **Type:** enum ## Description The `KeyCode` enum contains a list of byte keycodes that represent the key or button involved in user input. This enum also includes buttons and axes present on gamepads, and 96 `World[]` values for non-standard buttons. Note that keyboard values refer to the physical position of buttons on a standard QWERTY keyboard. Provided the user's system is configured correctly, the location of keys (such as WASD) will remain the same on other keyboard types such as Dvorak keyboards where it would map to ,AOE. See also [InputObject](/docs/reference/engine/classes/InputObject.md), used for keyboard and gamepad input. ## Items | Name | Value | Description | |------|-------|-------------| | `Unknown` | 0 | Blank value that represents no key being pressed. | | `Backspace` | 8 | The `Backspace` key. | | `Tab` | 9 | The `Tab` key. | | `Clear` | 12 | Only present on certain keyboards. | | `Return` | 13 | Frequently known as **Enter**. | | `Pause` | 19 | Only present on certain keyboards. | | `Escape` | 27 | The `Escape` key. | | `Space` | 32 | The `Space` key. | | `QuotedDouble` | 34 | The `"` key. | | `Hash` | 35 | The `#` key. | | `Dollar` | 36 | The `$` key. | | `Percent` | 37 | The `%` key. | | `Ampersand` | 38 | The `&` key. | | `Quote` | 39 | The `'` key. | | `LeftParenthesis` | 40 | The `(` key. | | `RightParenthesis` | 41 | The `)` key. | | `Asterisk` | 42 | The `*` key. | | `Plus` | 43 | The `+` key. | | `Comma` | 44 | The `,` key. | | `Minus` | 45 | The `-` key. | | `Period` | 46 | The `.` key. | | `Slash` | 47 | The `/` key. | | `Zero` | 48 | The `0` key. | | `One` | 49 | The `1` key. | | `Two` | 50 | The `2` key. | | `Three` | 51 | The `3` key. | | `Four` | 52 | The `4` key. | | `Five` | 53 | The `5` key. | | `Six` | 54 | The `6` key. | | `Seven` | 55 | The `7` key. | | `Eight` | 56 | The `8` key. | | `Nine` | 57 | The `9` key. | | `Colon` | 58 | The `:` key. | | `Semicolon` | 59 | The `;` key. | | `LessThan` | 60 | The `<` key. | | `Equals` | 61 | The `=` key. | | `GreaterThan` | 62 | The `>` key. | | `Question` | 63 | The `?` key. | | `At` | 64 | The `@` key. | | `LeftBracket` | 91 | The `[` key. | | `BackSlash` | 92 | The `\` key. | | `RightBracket` | 93 | The `]` key. | | `Caret` | 94 | The `^` key. | | `Underscore` | 95 | The `_` key. | | `Backquote` | 96 | The `` ` `` key. | | `A` | 97 | The `A` key. | | `B` | 98 | The `B` key. | | `C` | 99 | The `C` key. | | `D` | 100 | The `D` key. | | `E` | 101 | The `E` key. | | `F` | 102 | The `F` key. | | `G` | 103 | The `G` key. | | `H` | 104 | The `H` key. | | `I` | 105 | The `I` key. | | `J` | 106 | The `J` key. | | `K` | 107 | The `K` key. | | `L` | 108 | The `L` key. | | `M` | 109 | The `M` key. | | `N` | 110 | The `N` key. | | `O` | 111 | The `O` key. | | `P` | 112 | The `P` key. | | `Q` | 113 | The `Q` key. | | `R` | 114 | The `R` key. | | `S` | 115 | The `S` key. | | `T` | 116 | The `T` key. | | `U` | 117 | The `U` key. | | `V` | 118 | The `V` key. | | `W` | 119 | The `W` key. | | `X` | 120 | The `X` key. | | `Y` | 121 | The `Y` key. | | `Z` | 122 | The `Z` key. | | `LeftCurly` | 123 | The `{` key. | | `Pipe` | 124 | The `\|` key. | | `RightCurly` | 125 | The `}` key. | | `Tilde` | 126 | The `~` key. | | `Delete` | 127 | The `Del` key. | | `World0` | 160 | | | `World1` | 161 | | | `World2` | 162 | | | `World3` | 163 | | | `World4` | 164 | | | `World5` | 165 | | | `World6` | 166 | | | `World7` | 167 | | | `World8` | 168 | | | `World9` | 169 | | | `World10` | 170 | | | `World11` | 171 | | | `World12` | 172 | | | `World13` | 173 | | | `World14` | 174 | | | `World15` | 175 | | | `World16` | 176 | | | `World17` | 177 | | | `World18` | 178 | | | `World19` | 179 | | | `World20` | 180 | | | `World21` | 181 | | | `World22` | 182 | | | `World23` | 183 | | | `World24` | 184 | | | `World25` | 185 | | | `World26` | 186 | | | `World27` | 187 | | | `World28` | 188 | | | `World29` | 189 | | | `World30` | 190 | | | `World31` | 191 | | | `World32` | 192 | | | `World33` | 193 | | | `World34` | 194 | | | `World35` | 195 | | | `World36` | 196 | | | `World37` | 197 | | | `World38` | 198 | | | `World39` | 199 | | | `World40` | 200 | | | `World41` | 201 | | | `World42` | 202 | | | `World43` | 203 | | | `World44` | 204 | | | `World45` | 205 | | | `World46` | 206 | | | `World47` | 207 | | | `World48` | 208 | | | `World49` | 209 | | | `World50` | 210 | | | `World51` | 211 | | | `World52` | 212 | | | `World53` | 213 | | | `World54` | 214 | | | `World55` | 215 | | | `World56` | 216 | | | `World57` | 217 | | | `World58` | 218 | | | `World59` | 219 | | | `World60` | 220 | | | `World61` | 221 | | | `World62` | 222 | | | `World63` | 223 | | | `World64` | 224 | | | `World65` | 225 | | | `World66` | 226 | | | `World67` | 227 | | | `World68` | 228 | | | `World69` | 229 | | | `World70` | 230 | | | `World71` | 231 | | | `World72` | 232 | | | `World73` | 233 | | | `World74` | 234 | | | `World75` | 235 | | | `World76` | 236 | | | `World77` | 237 | | | `World78` | 238 | | | `World79` | 239 | | | `World80` | 240 | | | `World81` | 241 | | | `World82` | 242 | | | `World83` | 243 | | | `World84` | 244 | | | `World85` | 245 | | | `World86` | 246 | | | `World87` | 247 | | | `World88` | 248 | | | `World89` | 249 | | | `World90` | 250 | | | `World91` | 251 | | | `World92` | 252 | | | `World93` | 253 | | | `World94` | 254 | | | `World95` | 255 | | | `KeypadZero` | 256 | The `0` key on the keypad cluster. | | `KeypadOne` | 257 | The `1` key on the keypad cluster. | | `KeypadTwo` | 258 | The `2` key on the keypad cluster. | | `KeypadThree` | 259 | The `3` key on the keypad cluster. | | `KeypadFour` | 260 | The `4` key on the keypad cluster. | | `KeypadFive` | 261 | The `5` key on the keypad cluster. | | `KeypadSix` | 262 | The `6` key on the keypad cluster. | | `KeypadSeven` | 263 | The `7` key on the keypad cluster. | | `KeypadEight` | 264 | The `8` key on the keypad cluster. | | `KeypadNine` | 265 | The `9` key on the keypad cluster. | | `KeypadPeriod` | 266 | The `.` key on the keypad cluster. | | `KeypadDivide` | 267 | The `/` key on the keypad cluster. | | `KeypadMultiply` | 268 | The `*` key on the keypad cluster. | | `KeypadMinus` | 269 | The `-` key on the keypad cluster. | | `KeypadPlus` | 270 | The `+` key on the keypad cluster. | | `KeypadEnter` | 271 | The `Enter` key on the keypad cluster. | | `KeypadEquals` | 272 | The `=` key on the keypad cluster. | | `Up` | 273 | The `↑` arrow key. | | `Down` | 274 | The `↓` arrow key. | | `Right` | 275 | The `→` arrow key. | | `Left` | 276 | The `←` arrow key. | | `Insert` | 277 | The `Insert` key. | | `Home` | 278 | The `Home` key. | | `End` | 279 | The `End` key. | | `PageUp` | 280 | The `PgUp` key. | | `PageDown` | 281 | The `PgDown` key. | | `F1` | 282 | The `F1` key. | | `F2` | 283 | The `F2` key. | | `F3` | 284 | The `F3` key. | | `F4` | 285 | The `F4` key. | | `F5` | 286 | The `F5` key. | | `F6` | 287 | The `F6` key. | | `F7` | 288 | The `F7` key. | | `F8` | 289 | The `F8` key. | | `F9` | 290 | The `F9` key. | | `F10` | 291 | The `F10` key. | | `F11` | 292 | The `F11` key. | | `F12` | 293 | The `F12` key. | | `F13` | 294 | The `F13` key. Only present on certain keyboards. | | `F14` | 295 | The `F14` key. Only present on certain keyboards. | | `F15` | 296 | The `F15` key. Only present on certain keyboards. | | `NumLock` | 300 | The `Num Lock` key on the keypad cluster. | | `CapsLock` | 301 | The `Caps Lock` key. | | `ScrollLock` | 302 | The `Scr Lock` key. | | `RightShift` | 303 | The right side `Shift` key. | | `LeftShift` | 304 | The left side `Shift` key. | | `RightControl` | 305 | The right side `Ctrl` key. | | `LeftControl` | 306 | The left side `Ctrl` key. | | `RightAlt` | 307 | The right side `Alt` key. | | `LeftAlt` | 308 | The left side `Alt` key. | | `RightMeta` | 309 | The right side `Meta` key. | | `LeftMeta` | 310 | The left side `Meta` key. | | `LeftSuper` | 311 | The left side `Super` key. Better known as the Windows key or Cmd key. | | `RightSuper` | 312 | The right side `Super` key. Better known as the Windows key or Cmd key. | | `Mode` | 313 | Only present on certain keyboards. | | `Compose` | 314 | Only present on certain keyboards. | | `Help` | 315 | Only present on certain keyboards. | | `Print` | 316 | Only present on certain keyboards. | | `SysReq` | 317 | Only present on certain keyboards. | | `Break` | 318 | Only present on certain keyboards. | | `Menu` | 319 | The `Menu` key. | | `Power` | 320 | Only present on certain keyboards. | | `Euro` | 321 | The `€` key. Only present on certain keyboards. | | `Undo` | 322 | Only present on certain keyboards. | | `ButtonX` | 1000 | Gamepad `X` button. | | `ButtonY` | 1001 | Gamepad `Y` button. | | `ButtonA` | 1002 | Gamepad `A` button. | | `ButtonB` | 1003 | Gamepad `B` button. | | `ButtonR1` | 1004 | Gamepad `R1` button. | | `ButtonL1` | 1005 | Gamepad `L1` button. | | `ButtonR2` | 1006 | Gamepad `R2` button. | | `ButtonL2` | 1007 | Gamepad `L2` button. | | `ButtonR3` | 1008 | Gamepad `R3` button. | | `ButtonL3` | 1009 | Gamepad `L3` button. | | `ButtonStart` | 1010 | Gamepad `Start` button. | | `ButtonSelect` | 1011 | Gamepad `Select` button. | | `DPadLeft` | 1012 | Left arrow on a gamepad D-pad. | | `DPadRight` | 1013 | Right arrow on a gamepad D-pad. | | `DPadUp` | 1014 | Up arrow on a gamepad D-pad. | | `DPadDown` | 1015 | Down arrow on a gamepad D-pad. | | `Thumbstick1` | 1016 | Gamepad primary thumbstick. | | `Thumbstick2` | 1017 | Gamepad secondary thumbstick. | | `Thumbstick1Up` | 1018 | Up vector on the gamepad primary thumbstick. Primarily used in the [Input Action System](/docs/en-us/input/input-action-system.md). | | `Thumbstick1Down` | 1019 | Down vector on the gamepad primary thumbstick. Primarily used in the [Input Action System](/docs/en-us/input/input-action-system.md). | | `Thumbstick1Left` | 1020 | Left vector on the gamepad primary thumbstick. Primarily used in the [Input Action System](/docs/en-us/input/input-action-system.md). | | `Thumbstick1Right` | 1021 | Right vector on the gamepad primary thumbstick. Primarily used in the [Input Action System](/docs/en-us/input/input-action-system.md). | | `Thumbstick2Up` | 1022 | Up vector on the gamepad secondary thumbstick. Primarily used in the [Input Action System](/docs/en-us/input/input-action-system.md). | | `Thumbstick2Down` | 1023 | Down vector on the gamepad secondary thumbstick. Primarily used in the [Input Action System](/docs/en-us/input/input-action-system.md). | | `Thumbstick2Left` | 1024 | Left vector on the gamepad secondary thumbstick. Primarily used in the [Input Action System](/docs/en-us/input/input-action-system.md). | | `Thumbstick2Right` | 1025 | Right vector on the gamepad secondary thumbstick. Primarily used in the [Input Action System](/docs/en-us/input/input-action-system.md). | | `MouseLeftButton` | 1026 | The left mouse button, also commonly referred to as "button 1" on a generic mouse. Primarily used in the [Input Action System](/docs/en-us/input/input-action-system.md). | | `MouseRightButton` | 1027 | The right mouse button, also commonly referred to as "button 2" on a generic mouse. Primarily used in the [Input Action System](/docs/en-us/input/input-action-system.md). | | `MouseMiddleButton` | 1028 | The middle mouse button, also commonly referred to as "button 3" on a generic mouse. Primarily used in the [Input Action System](/docs/en-us/input/input-action-system.md). | | `MouseBackButton` | 1029 | | | `MouseNoButton` | 1030 | | | `MouseX` | 1031 | | | `MouseY` | 1032 | | | `MousePosition` | 1033 | The position of a mouse in the viewport. Primarily used in the [Input Action System](/docs/en-us/input/input-action-system.md). | | `TouchPosition` | 1034 | The position of a touch in the viewport. Primarily used in the [Input Action System](/docs/en-us/input/input-action-system.md). | | `MouseWheel` | 1035 | The scroll delta (change) of a mouse wheel. Primarily used in the [Input Action System](/docs/en-us/input/input-action-system.md). | | `TrackpadPan` | 1040 | The pan delta (change) from a trackpad two-finger pan gesture. Primarily used in the [Input Action System](/docs/en-us/input/input-action-system.md). | | `TrackpadPinch` | 1045 | The scale delta (change) from a trackpad two-finger pinch gesture. Primarily used in the [Input Action System](/docs/en-us/input/input-action-system.md). | | `MouseDelta` | 1048 | The movement delta (change) of the mouse cursor. Primarily used in the [Input Action System](/docs/en-us/input/input-action-system.md). | | `TouchDelta` | 1049 | The movement delta (change) of a touch input. Primarily used in the [Input Action System](/docs/en-us/input/input-action-system.md). | | `TouchPinch` | 1050 | The scale delta (change) of a two-finger touch pinch gesture. Primarily used in the [Input Action System](/docs/en-us/input/input-action-system.md). | --- name: KeyCodeStringFormat last_updated: 2026-06-29T19:34:08Z type: enum summary: "Determines the string format returned by UserInputService:GetStringForKeyCode()." --- # KeyCodeStringFormat Determines the string format returned by [UserInputService:GetStringForKeyCode()](/docs/reference/engine/classes/UserInputService.md). **Type:** enum ## Description This enum is used with [UserInputService:GetStringForKeyCode()](/docs/reference/engine/classes/UserInputService.md) to control how the returned key string is formatted. The `Abbreviated` option provides shortened labels suitable for compact UI elements such as keybind hints, tooltips, or on-screen button prompts where space is limited. ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | Default behavior which returns the full key string, for example `"LeftControl"`, `"Backspace"`, or `"Escape"`. | | `Abbreviated` | 1 | Returns a shortened key string when available, for example `"LCtrl"`, `"Bksp"`, or `"Esc"`. | --- name: KeyInterpolationMode last_updated: 2026-06-29T19:34:08Z type: enum summary: "Describes the interpolation method between two keys." --- # KeyInterpolationMode Describes the interpolation method between two keys. **Type:** enum ## Description Describes the interpolation method for a [FloatCurve](/docs/reference/engine/classes/FloatCurve.md) or [RotationCurve](/docs/reference/engine/classes/RotationCurve.md) segment between the key for which this mode is set and the next key in the curve. ## Items | Name | Value | Description | |------|-------|-------------| | `Constant` | 0 | The segment starting at this key will constantly evaluate to the value set at this key. | | `Linear` | 1 | The segment starting at this key will evaluate using a linear interpolation at this key and the value at the next key. | | `Cubic` | 2 | The segment starting at this key will evaluate using cubic interpolation of this key value using its right tangent and the next key value and its left tangent. | --- name: KeywordFilterType last_updated: 2026-06-29T19:34:08Z type: enum summary: "Determines if a filter is 'inclusive' or 'exclusive'." --- # KeywordFilterType Determines if a filter is 'inclusive' or 'exclusive'. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Include` | 0 | Include the matched content. | | `Exclude` | 1 | Exclude the matched content. | --- name: Language last_updated: 2026-06-29T19:34:08Z type: enum --- # Language **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | | --- name: LeftRight last_updated: 2026-06-29T19:34:08Z type: enum summary: "The LeftRight Enum is used to set where the object is on the side of its parent." --- # LeftRight The LeftRight Enum is used to set where the object is on the side of its parent. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Left` | 0 | The object is on the left side of its parent (**X** axis). | | `Center` | 1 | The object is centered on the center of its parent (**X** axis), expanding equally to the left and right of the parent's center. | | `Right` | 2 | The object is on the right side of its parent (**X** axis). | --- name: LexemeType last_updated: 2026-06-29T19:34:08Z type: enum --- # LexemeType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Eof` | 0 | | | `Name` | 1 | | | `QuotedString` | 2 | | | `Number` | 3 | | | `And` | 4 | | | `Or` | 5 | | | `Equal` | 6 | | | `TildeEqual` | 7 | | | `GreaterThan` | 8 | | | `GreaterThanEqual` | 9 | | | `LessThan` | 10 | | | `LessThanEqual` | 11 | | | `Colon` | 12 | | | `Dot` | 13 | | | `LeftParenthesis` | 14 | | | `RightParenthesis` | 15 | | | `Star` | 16 | | | `DoubleStar` | 17 | | | `ReservedSpecial` | 18 | | --- name: LightingStyle last_updated: 2026-06-29T19:34:08Z type: enum summary: "Enum used by Lighting.LightingStyle to indicate the artistic intent behind lighting in the experience." --- # LightingStyle Enum used by [Lighting.LightingStyle](/docs/reference/engine/classes/Lighting.md) to indicate the artistic intent behind lighting in the experience. **Type:** enum ## Description This enum indicates the artistic intent behind lighting in the experience. It is used by the [Lighting.LightingStyle](/docs/reference/engine/classes/Lighting.md) property. ## Items | Name | Value | Description | |------|-------|-------------| | `Realistic` | 0 | The most advanced and realistic lighting and shadows Roblox can deliver. | | `Soft` | 1 | A flat, retro-Roblox look with softer lights and shadows. | --- name: Limb last_updated: 2026-06-29T19:34:08Z type: enum summary: "Describes which limb a particular Instance belongs to (assuming the Instance is part of a Humanoid)." --- # Limb Describes which limb a particular Instance belongs to (assuming the Instance is part of a Humanoid). **Type:** enum ## Description Describes which limb a particular Instance belongs to (assuming the Instance is part of a Humanoid). Passing an Instance to the Humanoid:GetLimb() function will return the Limb for the Instance. ## Items | Name | Value | Description | |------|-------|-------------| | `Head` | 0 | If the limb is a part of the Humanoid's Head. | | `Torso` | 1 | If the limb is a part of the Humanoid's Torso. This includes UpperTorso and LowerTorso for R15 rigs. | | `LeftArm` | 2 | If the limb is a part of the Humanoid's Left Arm. This includes UpperLeftArm, LowerLeftArm, and LeftHand for R15 rigs. | | `RightArm` | 3 | If the limb is a part of the Humanoid's Right Arm. This includes UpperRightArm, LowerRightArm and RightHand for R15 rigs. | | `LeftLeg` | 4 | If the limb is a part of the Humanoid's Left Leg. This includes UpperLeftLeg, LowerLeftLeg and LeftFoot for R15 rigs. | | `RightLeg` | 5 | If the limb is a part of the Humanoid's Right Leg. This includes UpperRightLeg, LowerRightLeg, and RightFoot for R15 rigs. | | `Unknown` | 6 | If a part is not a limb (e.g. running the [Humanoid:GetLimb()](/docs/reference/engine/classes/Humanoid.md) function and passing it an accessory which is a sibling of the Humanoid will return this value). | --- name: LineJoinMode last_updated: 2026-06-29T19:34:08Z type: enum summary: "This enum is used by UIStroke.LineJoinMode to determine how corners are interpreted." --- # LineJoinMode This enum is used by [UIStroke.LineJoinMode](/docs/reference/engine/classes/UIStroke.md) to determine how corners are interpreted. **Type:** enum ## Description This enum is used by [UIStroke.LineJoinMode](/docs/reference/engine/classes/UIStroke.md) to determine how corners are interpreted. For a more detailed walkthrough of the [UIStroke](/docs/reference/engine/classes/UIStroke.md) object, see [here](/docs/en-us/ui/appearance-modifiers.md#stroke). ## Items | Name | Value | Description | |------|-------|-------------| | `Round` | 0 | The corners are rounded. | | `Bevel` | 1 | The corners are beveled. | | `Miter` | 2 | The corners are mitered. | --- name: ListDisplayMode last_updated: 2026-06-29T19:34:08Z type: enum summary: "Used to set the scrolling mode of the Advanced Objects tab." --- # ListDisplayMode Used to set the scrolling mode of the Advanced Objects tab. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Horizontal` | 0 | Scrolling along the X-axis (fixed Y-Axis). | | `Vertical` | 1 | Scrolling along the Y-axis (fixed X-Axis). | --- name: ListenerLocation last_updated: 2026-06-29T19:34:08Z type: enum summary: "Enum used with SoundService.DefaultListenerLocation to determine where an AudioListener is placed by default." --- # ListenerLocation Enum used with [SoundService.DefaultListenerLocation](/docs/reference/engine/classes/SoundService.md) to determine where an [AudioListener](/docs/reference/engine/classes/AudioListener.md) is placed by default. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | Behavior depends on the value of [VoiceChatService.EnableDefaultVoice](/docs/reference/engine/classes/VoiceChatService.md) and [VoiceChatService.UseAudioApi](/docs/reference/engine/classes/VoiceChatService.md). When using the default voice setup with the audio API, this behaves similarly to `Camera`. | | `None` | 1 | No [AudioListener](/docs/reference/engine/classes/AudioListener.md) will be created by default, but they can be created separately by scripts. | | `Character` | 2 | All of the following, resulting in the world being heard from the position of your character while matching the orientation of your camera. - An [Attachment](/docs/reference/engine/classes/Attachment.md) will be created and parented to the [PrimaryPart](/docs/reference/engine/classes/Model.md) of the local player's [Character](/docs/reference/engine/classes/Player.md) model. - An [AudioListener](/docs/reference/engine/classes/AudioListener.md) will be created and parented to the [Attachment](/docs/reference/engine/classes/Attachment.md). - An [AudioDeviceOutput](/docs/reference/engine/classes/AudioDeviceOutput.md) will be created and parented to [SoundService](/docs/reference/engine/classes/SoundService.md). - A [Wire](/docs/reference/engine/classes/Wire.md) will be created and parented to the previously-created [AudioListener](/docs/reference/engine/classes/AudioListener.md). The wire's [SourceInstance](/docs/reference/engine/classes/Wire.md) will be set to the previously-created [AudioListener](/docs/reference/engine/classes/AudioListener.md) and its [TargetInstance](/docs/reference/engine/classes/Wire.md) will be set to the previously-created [AudioDeviceOutput](/docs/reference/engine/classes/AudioDeviceOutput.md). - The previously-created [Attachment](/docs/reference/engine/classes/Attachment.md) will be updated once per frame to face the same direction as [Workspace.CurrentCamera](/docs/reference/engine/classes/Workspace.md). | | `Camera` | 3 | All of the following, resulting in the world being heard from the perspective (postition and orientation) of the camera. - An [AudioListener](/docs/reference/engine/classes/AudioListener.md) will be created and parented to [Workspace.CurrentCamera](/docs/reference/engine/classes/Workspace.md). - An [AudioDeviceOutput](/docs/reference/engine/classes/AudioDeviceOutput.md) will be created and parented to [SoundService](/docs/reference/engine/classes/SoundService.md). - A [Wire](/docs/reference/engine/classes/Wire.md) will be created and parented to the previously-created [AudioListener](/docs/reference/engine/classes/AudioListener.md). The wire's [SourceInstance](/docs/reference/engine/classes/Wire.md) will be set to the previously-created [AudioListener](/docs/reference/engine/classes/AudioListener.md) and its [TargetInstance](/docs/reference/engine/classes/Wire.md) will be set to the previously-created [AudioDeviceOutput](/docs/reference/engine/classes/AudioDeviceOutput.md). | --- name: ListenerPositionType last_updated: 2026-06-29T19:34:08Z type: enum summary: "Defines where and how an AudioListener is positioned when receiving spatial audio." --- # ListenerPositionType Defines where and how an [AudioListener](/docs/reference/engine/classes/AudioListener.md) is positioned when receiving spatial audio. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Parent` | 0 | Uses the parent [Instance](/docs/reference/engine/classes/Instance.md) of the [AudioEmitter](/docs/reference/engine/classes/AudioEmitter.md). | | `Instance` | 1 | Uses a specified [Instance](/docs/reference/engine/classes/Instance.md). | --- name: ListenerType last_updated: 2026-06-29T19:34:08Z type: enum summary: "Defines where and how the listener is positioned when picking up spatial audio." --- # ListenerType Defines where and how the listener is positioned when picking up spatial audio. **Type:** enum ## Description Defines where and how the listener is positioned when picking up spatial audio. Used in [SoundService.SetListener](/docs/reference/engine/classes/SoundService.md) and [SoundService.GetListener](/docs/reference/engine/classes/SoundService.md). ## Items | Name | Value | Description | |------|-------|-------------| | `Camera` | 0 | Uses the world CFrame of `workspace.CurrentCamera`. | | `CFrame` | 1 | Uses a specified world CFrame. | | `ObjectPosition` | 2 | Uses the world position of an instance and the rotation of `workspace.CurrentCamera`. | | `ObjectCFrame` | 3 | Uses the world CFrame from an instance. | --- name: LiveEditingAtomicUpdateResponse last_updated: 2026-06-29T19:34:08Z type: enum --- # LiveEditingAtomicUpdateResponse **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Success` | 0 | | | `FailureGuidNotFound` | 1 | | | `FailureHashMismatch` | 2 | | | `FailureOperationIllegal` | 3 | | --- name: LiveEditingBroadcastMessageType last_updated: 2026-06-29T19:34:08Z type: enum --- # LiveEditingBroadcastMessageType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Normal` | 0 | | | `Warning` | 1 | | | `Error` | 2 | | --- name: LoadCharacterLayeredClothing last_updated: 2026-06-29T19:34:08Z type: enum summary: "Indicates whether characters spawning into an experience will have layered clothing accessories equipped on them." --- # LoadCharacterLayeredClothing Indicates whether characters spawning into an experience will have layered clothing accessories equipped on them. **Type:** enum ## Description Indicates whether characters spawning into an experience will have layered clothing accessories equipped on them (Although [MeshPartHeadsAndAccessories](/docs/reference/engine/enums/MeshPartHeadsAndAccessories.md) also need to be enabled). This is set in [StarterPlayer.LoadCharacterLayeredClothing](/docs/reference/engine/classes/StarterPlayer.md). ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | The behavior of the default setting is not constant. It can mean Disabled or Enabled depending on what the global back-end flag settings are currently. | | `Disabled` | 1 | This means layered clothing will not be equipped on characters in the experience. | | `Enabled` | 2 | This means layered clothing will be equipped on characters in the experience (Although [MeshPartHeadsAndAccessories](/docs/reference/engine/enums/MeshPartHeadsAndAccessories.md) also need to be enabled for the experience). | --- name: LoadDynamicHeads last_updated: 2026-06-29T19:34:08Z type: enum --- # LoadDynamicHeads **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | | | `Disabled` | 1 | | | `Enabled` | 2 | | --- name: LocationType last_updated: 2026-06-29T19:34:08Z type: enum --- # LocationType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Character` | 0 | | | `Camera` | 1 | | | `ObjectPosition` | 2 | | --- name: LuauTypeCheckMode last_updated: 2026-06-29T19:34:08Z type: enum summary: "The Luau type checking mode for scripts in the experience." --- # LuauTypeCheckMode The Luau type checking mode for scripts in the experience. **Type:** enum ## Description Determines how strictly the Luau type checker analyzes scripts in the experience. This enum is used by [Workspace.LuauTypeCheckMode](/docs/reference/engine/classes/Workspace.md). ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | Uses the engine-default type checking mode. | | `NoCheck` | 1 | Scripts are not type-checked. | | `Nonstrict` | 2 | Scripts are type-checked in nonstrict mode. | | `Strict` | 3 | Scripts are type-checked in strict mode. | --- name: MakeupType last_updated: 2026-06-29T19:34:08Z type: enum --- # MakeupType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Face` | 0 | | | `Lip` | 1 | | | `Eye` | 2 | | --- name: MarketplaceBulkPurchasePromptStatus last_updated: 2026-06-29T19:34:08Z type: enum summary: "The status of the BulkPurchasePrompt after player interaction." --- # MarketplaceBulkPurchasePromptStatus The status of the BulkPurchasePrompt after player interaction. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Completed` | 1 | User confirmed the purchase and the transaction has been processed. | | `Aborted` | 2 | User closed the prompt. | | `Error` | 3 | User confirmed the purchase, but the transaction cannot be sent to the backend for processing. | --- name: MarketplaceItemPurchaseStatus last_updated: 2026-06-29T19:34:08Z type: enum summary: "The status of the item purchase through MarketplaceService." --- # MarketplaceItemPurchaseStatus The status of the item purchase through MarketplaceService. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Success` | 1 | The item is purchased successfully. | | `SystemError` | 2 | Unable to purchase item due to Roblox system error. | | `AlreadyOwned` | 3 | Unable to purchase item because user already owns the item. Users can only own one copy of non-limited item. | | `InsufficientRobux` | 4 | Unable to purchase item because of insufficient Robux. | | `QuantityLimitExceeded` | 5 | User has reached the max quantity allowed per user set by the creator for limited items. | | `QuotaExceeded` | 6 | The user has exceeded our [purchase request limit](/docs/en-us/marketplace/marketplace-fees-and-commissions.md#rate-limits). | | `NotForSale` | 7 | Item is not for sale. | | `NotAvailableForPurchaser` | 8 | This item is restricted to a group of users and the purchaser is not in this group. | | `PriceMismatch` | 9 | The provided price does not match the item price. Most likely the price changed since displayed to the user. | | `SoldOut` | 10 | The item is sold out. | | `PurchaserIsSeller` | 11 | The purchaser is the same user as the seller, for a resale purchase. | | `InsufficientMembership` | 12 | The user does not have sufficient premium membership to purchase this item. | | `PlaceInvalid` | 13 | The item is not allowed to be sold in the place. | --- name: MarketplaceProductType last_updated: 2026-06-29T19:34:08Z type: enum summary: "Indicates if an avatar item is an asset or bundle. Used with MarketplaceService and similar services." --- # MarketplaceProductType Indicates if an avatar item is an asset or bundle. Used with MarketplaceService and similar services. **Type:** enum ## Description Indicates if an avatar item is an asset or bundle. Used with [MarketplaceService](/docs/reference/engine/classes/MarketplaceService.md) and similar services. ## Items | Name | Value | Description | |------|-------|-------------| | `AvatarAsset` | 1 | Avatar asset | | `AvatarBundle` | 2 | Avatar bundle | --- name: MarkupKind last_updated: 2026-06-29T19:34:08Z type: enum --- # MarkupKind **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `PlainText` | 0 | | | `Markdown` | 1 | | --- name: MatchmakingType last_updated: 2026-06-29T19:34:08Z type: enum summary: "Used with DataModel.MatchmakingType to represent how players in the server are handled by matchmaking." --- # MatchmakingType Used with [DataModel.MatchmakingType](/docs/reference/engine/classes/DataModel.md) to represent how players in the server are handled by matchmaking. **Type:** enum ## Description This enum is used with [DataModel.MatchmakingType](/docs/reference/engine/classes/DataModel.md) to represent how players in the server are handled by matchmaking. ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 1 | Includes desktop, mobile, cross-play enabled players, etc. | | `XboxOnly` | 2 | Xbox players with cross-play disabled. | | `PlayStationOnly` | 3 | PlayStation players with cross-play disabled. | --- name: Material last_updated: 2026-06-29T19:34:08Z type: enum summary: "Materials used for parts and/or terrain." --- # Material Materials used for parts and/or terrain. **Type:** enum ## Description The following base materials are used for [parts](/docs/en-us/parts.md) and/or [terrain](/docs/en-us/parts/terrain.md). See the [Materials](/docs/en-us/parts/materials.md) guide for more details on using and modifying materials. ## Items | Name | Value | Description | |------|-------|-------------| | `Plastic` | 256 | Applies to [BasePart](/docs/reference/engine/classes/BasePart.md) only. | | `SmoothPlastic` | 272 | Applies to [BasePart](/docs/reference/engine/classes/BasePart.md) only. | | `Neon` | 288 | Applies to [BasePart](/docs/reference/engine/classes/BasePart.md) only. | | `Wood` | 512 | Applies to [BasePart](/docs/reference/engine/classes/BasePart.md) only. | | `WoodPlanks` | 528 | Applies to [BasePart](/docs/reference/engine/classes/BasePart.md) and [Terrain](/docs/reference/engine/classes/Terrain.md). | | `Marble` | 784 | Applies to [BasePart](/docs/reference/engine/classes/BasePart.md) only. | | `Basalt` | 788 | Applies to [BasePart](/docs/reference/engine/classes/BasePart.md) and [Terrain](/docs/reference/engine/classes/Terrain.md). | | `Slate` | 800 | Applies to [BasePart](/docs/reference/engine/classes/BasePart.md) and [Terrain](/docs/reference/engine/classes/Terrain.md). | | `CrackedLava` | 804 | Applies to [BasePart](/docs/reference/engine/classes/BasePart.md) and [Terrain](/docs/reference/engine/classes/Terrain.md). | | `Concrete` | 816 | Applies to [BasePart](/docs/reference/engine/classes/BasePart.md) and [Terrain](/docs/reference/engine/classes/Terrain.md). | | `Limestone` | 820 | Applies to [BasePart](/docs/reference/engine/classes/BasePart.md) and [Terrain](/docs/reference/engine/classes/Terrain.md). | | `Granite` | 832 | Applies to [BasePart](/docs/reference/engine/classes/BasePart.md) only. | | `Pavement` | 836 | Applies to [BasePart](/docs/reference/engine/classes/BasePart.md) and [Terrain](/docs/reference/engine/classes/Terrain.md). | | `Brick` | 848 | Applies to [BasePart](/docs/reference/engine/classes/BasePart.md) and [Terrain](/docs/reference/engine/classes/Terrain.md). | | `Pebble` | 864 | Applies to [BasePart](/docs/reference/engine/classes/BasePart.md) only. | | `Cobblestone` | 880 | Applies to [BasePart](/docs/reference/engine/classes/BasePart.md) and [Terrain](/docs/reference/engine/classes/Terrain.md). | | `Rock` | 896 | Applies to [BasePart](/docs/reference/engine/classes/BasePart.md) and [Terrain](/docs/reference/engine/classes/Terrain.md). | | `Sandstone` | 912 | Applies to [BasePart](/docs/reference/engine/classes/BasePart.md) and [Terrain](/docs/reference/engine/classes/Terrain.md). | | `CorrodedMetal` | 1040 | Applies to [BasePart](/docs/reference/engine/classes/BasePart.md) only. | | `DiamondPlate` | 1056 | Applies to [BasePart](/docs/reference/engine/classes/BasePart.md) only. | | `Foil` | 1072 | Applies to [BasePart](/docs/reference/engine/classes/BasePart.md) only. | | `Metal` | 1088 | Applies to [BasePart](/docs/reference/engine/classes/BasePart.md) only. | | `Grass` | 1280 | Applies to [BasePart](/docs/reference/engine/classes/BasePart.md) and [Terrain](/docs/reference/engine/classes/Terrain.md). | | `LeafyGrass` | 1284 | Applies to [BasePart](/docs/reference/engine/classes/BasePart.md) and [Terrain](/docs/reference/engine/classes/Terrain.md). | | `Sand` | 1296 | Applies to [BasePart](/docs/reference/engine/classes/BasePart.md) and [Terrain](/docs/reference/engine/classes/Terrain.md). | | `Fabric` | 1312 | Applies to [BasePart](/docs/reference/engine/classes/BasePart.md) only. | | `Snow` | 1328 | Applies to [BasePart](/docs/reference/engine/classes/BasePart.md) and [Terrain](/docs/reference/engine/classes/Terrain.md). | | `Mud` | 1344 | Applies to [BasePart](/docs/reference/engine/classes/BasePart.md) and [Terrain](/docs/reference/engine/classes/Terrain.md). | | `Ground` | 1360 | Applies to [BasePart](/docs/reference/engine/classes/BasePart.md) and [Terrain](/docs/reference/engine/classes/Terrain.md). | | `Asphalt` | 1376 | Applies to [BasePart](/docs/reference/engine/classes/BasePart.md) and [Terrain](/docs/reference/engine/classes/Terrain.md). | | `Salt` | 1392 | Applies to [BasePart](/docs/reference/engine/classes/BasePart.md) and [Terrain](/docs/reference/engine/classes/Terrain.md). | | `Ice` | 1536 | Applies to [BasePart](/docs/reference/engine/classes/BasePart.md) and [Terrain](/docs/reference/engine/classes/Terrain.md). | | `Glacier` | 1552 | Applies to [BasePart](/docs/reference/engine/classes/BasePart.md) and [Terrain](/docs/reference/engine/classes/Terrain.md). | | `Glass` | 1568 | Applies to [BasePart](/docs/reference/engine/classes/BasePart.md) only. Note that refraction of light through this material is not supported on mobile devices due to computational limitations. | | `ForceField` | 1584 | Must be used on a [MeshPart](/docs/reference/engine/classes/MeshPart.md) with UVs laid out to cover much of the 0:1 UV space or more (larger is better). The [MeshPart](/docs/reference/engine/classes/MeshPart.md) must also have a texture applied to its [TextureID](/docs/reference/engine/classes/MeshPart.md), and that texture image must have a wide value range since the material displays the range from dark/black to light/white values. Obtain [this model](https://create.roblox.com/store/asset/15507769146/ForceFieldMesh) for a functional example. | | `Air` | 1792 | Applies to [Terrain](/docs/reference/engine/classes/Terrain.md) only. | | `Water` | 2048 | Applies to [Terrain](/docs/reference/engine/classes/Terrain.md) only. | | `Cardboard` | 2304 | Applies to [BasePart](/docs/reference/engine/classes/BasePart.md) only. | | `Carpet` | 2305 | Applies to [BasePart](/docs/reference/engine/classes/BasePart.md) only. | | `CeramicTiles` | 2306 | Applies to [BasePart](/docs/reference/engine/classes/BasePart.md) only. | | `ClayRoofTiles` | 2307 | Applies to [BasePart](/docs/reference/engine/classes/BasePart.md) only. | | `RoofShingles` | 2308 | Applies to [BasePart](/docs/reference/engine/classes/BasePart.md) only. | | `Leather` | 2309 | Applies to [BasePart](/docs/reference/engine/classes/BasePart.md) only. | | `Plaster` | 2310 | Applies to [BasePart](/docs/reference/engine/classes/BasePart.md) only. | | `Rubber` | 2311 | Applies to [BasePart](/docs/reference/engine/classes/BasePart.md) only. | --- name: MaterialPattern last_updated: 2026-06-29T19:34:08Z type: enum summary: "Describes the pattern of material. Affects texture tiling method." --- # MaterialPattern Describes the pattern of material. Affects texture tiling method. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Regular` | 0 | Material has ordered pattern, usually man-made, like brick or wood planks. | | `Organic` | 1 | Material pattern has less repetition, like stone or dirt. | --- name: MembershipType last_updated: 2026-06-29T19:34:08Z type: enum summary: "The membership type of a Player." --- # MembershipType The membership type of a Player. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `None` | 0 | The player does not have an active paid membership. | | `BuildersClub` | 1 | (no longer available). | | `TurboBuildersClub` | 2 | (no longer available). | | `OutrageousBuildersClub` | 3 | (no longer available). | | `Premium` | 4 | The player has an active Premium membership. | --- name: MeshPartDetailLevel last_updated: 2026-06-29T19:34:08Z type: enum summary: "The level of detail of `MeshParts` displayed in Studio." --- # MeshPartDetailLevel The level of detail of `MeshParts` displayed in Studio. **Type:** enum ## Description The level of detail of `MeshParts` displayed in Studio. Used to visually verify the quality of MeshParts at lower level of detail at close range. Level01 being the highest detail and Level04 being the lowest detail. ## Items | Name | Value | Description | |------|-------|-------------| | `DistanceBased` | 0 | Select level of detail based on distance (default behavior as in game). | | `Level00` | 1 | | | `Level01` | 2 | Highest level of detail. | | `Level02` | 3 | Second highest level of detail. | | `Level03` | 4 | Third highest level of detail. | | `Level04` | 5 | Lowest level of detail. | | `Level05` | 6 | | | `Level06` | 7 | | | `Level07` | 8 | | | `Level08` | 9 | | | `Level09` | 10 | | --- name: MeshPartHeadsAndAccessories last_updated: 2026-06-29T19:34:08Z type: enum summary: "Controls the Workspace.MeshPartHeadsAndAccessories feature." --- # MeshPartHeadsAndAccessories Controls the [Workspace.MeshPartHeadsAndAccessories](/docs/reference/engine/classes/Workspace.md) feature. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | The default option to control the [Workspace.MeshPartHeadsAndAccessories](/docs/reference/engine/classes/Workspace.md) feature. This currently has the same meaning as "Enabled". | | `Disabled` | 1 | Disables the [Workspace.MeshPartHeadsAndAccessories](/docs/reference/engine/classes/Workspace.md) feature. | | `Enabled` | 2 | Enables the [Workspace.MeshPartHeadsAndAccessories](/docs/reference/engine/classes/Workspace.md) feature. | --- name: MeshScaleUnit last_updated: 2026-06-29T19:34:08Z type: enum --- # MeshScaleUnit **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Stud` | 0 | | | `Meter` | 1 | | | `CM` | 2 | | | `MM` | 3 | | | `Foot` | 4 | | | `Inch` | 5 | | --- name: MeshType last_updated: 2026-06-29T19:34:08Z type: enum summary: "The MeshType Enum is used to set what type of mesh the SpecialMesh is." --- # MeshType The MeshType Enum is used to set what type of mesh the [SpecialMesh](/docs/reference/engine/classes/SpecialMesh.md) is. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Head` | 0 | The mesh is the default [Humanoid](/docs/reference/engine/classes/Humanoid.md) head mesh. | | `Torso` | 1 | The mesh is the default Humanoid torso mesh. | | `Wedge` | 2 | The mesh is a wedge. | | `Sphere` | 3 | The mesh is a sphere. | | `Cylinder` | 4 | The mesh is a cylinder. | | `FileMesh` | 5 | The mesh is determined by the Roblox mesh asset ID provided. | | `Brick` | 6 | The mesh is a brick (just like the shape of a default [BasePart](/docs/reference/engine/classes/BasePart.md)). | | `Prism` | 7 | The mesh is a prism. | | `Pyramid` | 8 | The mesh is a pyramid. | | `ParallelRamp` | 9 | The mesh is a parallel ramp. | | `RightAngleRamp` | 10 | The mesh is a right angle ramp. | | `CornerWedge` | 11 | The mesh is a corner wedge. | --- name: MessageType last_updated: 2026-06-29T19:34:08Z type: enum summary: "Message category and severity level." --- # MessageType Message category and severity level. **Type:** enum ## Description A message can be a simple diagnostic or a sign of a system instability or failure. The channel that the message appears in indicates its severity. The MessageType indicates which channel the message displays in. ## Items | Name | Value | Description | |------|-------|-------------| | `MessageOutput` | 0 | The message is from the standard output channel. | | `MessageInfo` | 1 | The message is from the information channel. | | `MessageWarning` | 2 | The message is from the warning channel. | | `MessageError` | 3 | The message is from the error channel. | --- name: ModelLevelOfDetail last_updated: 2026-06-29T19:34:08Z type: enum summary: "Controls the level of detail for Models in experiences with instance streaming enabled." --- # ModelLevelOfDetail Controls the level of detail for [Models](/docs/reference/engine/classes/Model.md) in experiences with instance streaming enabled. **Type:** enum ## Description Controls the level of detail for [Models](/docs/reference/engine/classes/Model.md) in experiences with instance [streaming](/docs/en-us/workspace/streaming.md) enabled. Composite or imposter meshes do not support physics, collision detection, or raycasting. | _Original model_ | _Lightweight SLIM mesh_ | _Low-res "imposter" mesh_ | | --- | --- | --- | ## Items | Name | Value | Description | |------|-------|-------------| | `Automatic` | 0 | Default behavior, currently equivalent to `Disabled`. | | `StreamingMesh` | 1 | A lower resolution "imposter" mesh (colored, coarse mesh that wraps around all child parts of the model) renders outside the streaming radius. Does not support textures. | | `Disabled` | 2 | A lower resolution mesh will not be displayed. | | `SLIM` | 4 | A **SLIM** model (Scalable Lightweight Interactive Model) renders a composite of all child parts at progressively lower resolutions at distances based on the streaming radius. Greatly improves visual quality over `StreamingMesh`. | --- name: ModelStreamingBehavior last_updated: 2026-06-29T19:34:08Z type: enum summary: "Controls how Models are sent to clients in experiences with instance streaming enabled." --- # ModelStreamingBehavior Controls how [Models](/docs/reference/engine/classes/Model.md) are sent to clients in experiences with instance streaming enabled. **Type:** enum ## Description Controls how [Models](/docs/reference/engine/classes/Model.md) are replicated in experiences when instance [streaming](/docs/en-us/workspace/streaming.md) is enabled. Only affects models with [ModelStreamingMode](/docs/reference/engine/classes/Model.md) set to [Default](/docs/reference/engine/enums/ModelStreamingMode.md)/[Nonatomic](/docs/reference/engine/enums/ModelStreamingMode.md). ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | Default behavior (subject to change). | | `Legacy` | 1 | Models are sent when their parent is sent, typically during player join. Models are never streamed out unless an ancestor of the model is streamed out. | | `Improved` | 2 | Models are never sent during player join. See [model streaming controls](/docs/en-us/workspace/streaming.md#model-streaming-controls) for detailed behavioral notes. | --- name: ModelStreamingMode last_updated: 2026-06-29T19:34:08Z type: enum summary: "Controls stream in and out behavior of a model." --- # ModelStreamingMode Controls stream in and out behavior of a model. **Type:** enum ## Description Controls how model is streamed in and out when used in an experience that is streaming enabled. Has no effect when experience is not streaming enabled. ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | Default behavior (subject to change). | | `Atomic` | 1 | The [Model](/docs/reference/engine/classes/Model.md) and all of its descendants are streamed in/out together. For streaming in, this applies when **any** descendant [BasePart](/docs/reference/engine/classes/BasePart.md) is eligible for streaming in. For streaming out, this applies when **all** descendant [BaseParts](/docs/reference/engine/classes/BasePart.md) are eligible for streaming out. | | `Persistent` | 2 | Persistent models are sent as a complete atomic unit soon after the player joins and before the [Workspace.PersistentLoaded](/docs/reference/engine/classes/Workspace.md) event fires. Persistent models and their descendants are never streamed out. | | `PersistentPerPlayer` | 3 | Behaves as a persistent model for players that have been added using [Model:AddPersistentPlayer()](/docs/reference/engine/classes/Model.md). For other players, behavior is the same as `Atomic`. You can revert a model from player persistence via [Model:RemovePersistentPlayer()](/docs/reference/engine/classes/Model.md). | | `Nonatomic` | 4 | When a nonatomic model is streamed, descendants are also sent, except for part descendants. Nonatomic models that are not descendants of parts are sent during experience loading. | --- name: ModerationStatus last_updated: 2026-06-29T19:34:08Z type: enum --- # ModerationStatus **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `ReviewedApproved` | 1 | | | `ReviewedRejected` | 2 | | | `NotReviewed` | 3 | | | `NotApplicable` | 4 | | | `Invalid` | 5 | | --- name: ModifierKey last_updated: 2026-06-29T19:34:08Z type: enum --- # ModifierKey **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Shift` | 0 | | | `Ctrl` | 1 | | | `Alt` | 2 | | | `Meta` | 3 | | --- name: MouseBehavior last_updated: 2026-06-29T19:34:08Z type: enum summary: "Sets the user's mouse behavior." --- # MouseBehavior Sets the user's mouse behavior. **Type:** enum ## Description Used with the [UserInputService.MouseBehavior](/docs/reference/engine/classes/UserInputService.md) property of [UserInputService](/docs/reference/engine/classes/UserInputService.md) to set how the user's mouse behaves. ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | The mouse moves freely around the user's screen. | | `LockCenter` | 1 | The mouse is locked and cannot move from the center of the user's screen. | | `LockCurrentPosition` | 2 | The mouse is locked and cannot move from its current position on the user's screen at the time of locking. | --- name: MoveState last_updated: 2026-06-29T19:34:08Z type: enum summary: "Represents the state of a SkateboardPlatform." --- # MoveState Represents the state of a [SkateboardPlatform](/docs/reference/engine/classes/SkateboardPlatform.md). **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Stopped` | 0 | Skateboard is stopped (not moving). | | `Coasting` | 1 | Skateboard is coasting (not actively speeding up / being pushed). | | `Pushing` | 2 | Skateboard is being pushed (speeding up). | | `Stopping` | 3 | Skateboard is stopping, but still moving. | | `AirFree` | 4 | Skateboard is in the air. | --- name: MuteState last_updated: 2026-06-29T19:34:08Z type: enum --- # MuteState **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Unmuted` | 0 | | | `Muted` | 1 | | --- name: NameOcclusion last_updated: 2026-06-29T19:34:08Z type: enum summary: "The NameOcclusion Enum is used to set the Humanoid.NameOcclusion property." --- # NameOcclusion The NameOcclusion Enum is used to set the [Humanoid.NameOcclusion](/docs/reference/engine/classes/Humanoid.md) property. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `NoOcclusion` | 0 | Do not occlude (hide) any humanoid names. | | `EnemyOcclusion` | 1 | Occlude (hide) all enemy humanoid names. | | `OccludeAll` | 2 | Occlude (hide) all humanoid names. | --- name: NegateOperationHiddenHistory last_updated: 2026-06-29T19:34:08Z type: enum --- # NegateOperationHiddenHistory **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `None` | 0 | | | `NegatedUnion` | 1 | | | `NegatedIntersection` | 2 | | --- name: NetworkOwnership last_updated: 2026-06-29T19:34:08Z type: enum summary: "Defines how simulation authority is determined for the Network Ownership Unit/Mechanism this part is attached to." --- # NetworkOwnership Defines how simulation authority is determined for the Network Ownership Unit/Mechanism this part is attached to. **Type:** enum ## Description Defines how to determine which client has ownership of a part for a server (network). ## Items | Name | Value | Description | |------|-------|-------------| | `Automatic` | 0 | Network ownership is determined automatically by the server. | | `Manual` | 1 | Network ownership is set manually by the developer. | | `OnContact` | 2 | The first player to touch a part is given ownership of that part for the server (network). Ownership will not change if another player touches that part unless network ownership has been released by the owner. | --- name: NetworkStatus last_updated: 2026-06-29T19:34:08Z type: enum --- # NetworkStatus **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Unknown` | 0 | | | `Connected` | 1 | | | `Disconnected` | 2 | | --- name: NoiseType last_updated: 2026-06-29T19:34:08Z type: enum --- # NoiseType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `SimplexGabor` | 0 | | --- name: NormalId last_updated: 2026-06-29T19:34:08Z type: enum summary: "The NormalId Enum sets which side/face of a Part is used." --- # NormalId The NormalId Enum sets which side/face of a Part is used. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Right` | 0 | The right face of a Part. | | `Top` | 1 | The top face of a Part. | | `Back` | 2 | The back face of a Part. | | `Left` | 3 | The left face of a Part. | | `Bottom` | 4 | The bottom face of a Part. | | `Front` | 5 | The front face of a Part. | --- name: NotificationButtonType last_updated: 2026-06-29T19:34:08Z type: enum --- # NotificationButtonType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Primary` | 0 | | | `Secondary` | 1 | | --- name: OperationType last_updated: 2026-06-29T19:34:08Z type: enum --- # OperationType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Null` | 0 | | | `Union` | 1 | | | `Subtraction` | 2 | | | `Intersection` | 3 | | | `Primitive` | 4 | | --- name: OrientationAlignmentMode last_updated: 2026-06-29T19:34:08Z type: enum summary: "The OrientationAlignmentMode Enum is used to select the number of Attachments used in an alignment." --- # OrientationAlignmentMode The OrientationAlignmentMode Enum is used to select the number of [Attachments](/docs/reference/engine/classes/Attachment.md) used in an alignment. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `OneAttachment` | 0 | Use one attachment. | | `TwoAttachment` | 1 | Use two attachments. | --- name: OutfitSource last_updated: 2026-06-29T19:34:08Z type: enum --- # OutfitSource **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `All` | 1 | | | `Created` | 2 | | | `Purchased` | 3 | | --- name: OutfitType last_updated: 2026-06-29T19:34:08Z type: enum --- # OutfitType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `All` | 1 | Returns outfits of every type, with no filtering applied. | | `Avatar` | 2 | A full-body avatar outfit. | | `DynamicHead` | 3 | An outfit containing a dynamic head. | | `Shoes` | 4 | An outfit consisting of a pair of shoes. | | `Makeup` | 5 | An outfit consisting of makeup items applied to the avatar's face. | --- name: OutputLayoutMode last_updated: 2026-06-29T19:34:08Z type: enum summary: "Sets the layout mode of the output." --- # OutputLayoutMode Sets the layout mode of the output. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Horizontal` | 0 | The output layout is horizontal (x-axis). | | `Vertical` | 1 | The output layout is vertical (y-axis). | --- name: OverrideMouseIconBehavior last_updated: 2026-06-29T19:34:08Z type: enum summary: "Overrides the behavior of the mouse icon to either force that it is always shown or force that it is always hidden." --- # OverrideMouseIconBehavior Overrides the behavior of the mouse icon to either force that it is always shown or force that it is always hidden. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `None` | 0 | The mouse icon behavior is not overridden - default behavior is used. | | `ForceShow` | 1 | The mouse icon is forced to always remain shown/visible. | | `ForceHide` | 2 | The mouse icon is forced to always remain hidden/invisible. | --- name: PackagePermission last_updated: 2026-06-29T19:34:08Z type: enum summary: "Indicates the current user's or group roleset's permission to the package." --- # PackagePermission Indicates the current user's or group roleset's permission to the package. **Type:** enum ## Description The [PackagePermission](/docs/reference/engine/enums/PackagePermission.md) indicates the current user's or group roleset's permission to the package. ## Items | Name | Value | Description | |------|-------|-------------| | `None` | 0 | Permission data is not available for the current user or group roleset. | | `NoAccess` | 1 | The current user or group roleset doesn't have access. | | `Revoked` | 2 | The current user's or group roleset's permission is revoked. | | `UseView` | 3 | The current user or group roleset can download a copy of the package from Roblox. | | `Edit` | 4 | The current user or group roleset can download a copy of the package from Roblox and publish package changes to Roblox. | | `Own` | 5 | The current user or group roleset can download a copy of the page from Roblox, publish package changes to Roblox, and manage who has access to the package. | --- name: PartType last_updated: 2026-06-29T19:34:08Z type: enum summary: "Controls the Part.Shape of an object." --- # PartType Controls the [Part.Shape](/docs/reference/engine/classes/Part.md) of an object. **Type:** enum ## Description The **PartType** enum controls the [Shape](/docs/reference/engine/classes/Part.md) of a [Part](/docs/reference/engine/classes/Part.md). ## Items | Name | Value | Description | |------|-------|-------------| | `Ball` | 0 | A spherical shape. | | `Block` | 1 | A block shape. | | `Cylinder` | 2 | A cylinder shape oriented along the **X** axis. | | `Wedge` | 3 | A wedge shape with a slope on one side. | | `CornerWedge` | 4 | A wedge shape with slopes on two sides. | --- name: ParticleEmitterShape last_updated: 2026-06-29T19:34:08Z type: enum --- # ParticleEmitterShape **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Box` | 0 | | | `Sphere` | 1 | | | `Cylinder` | 2 | | | `Disc` | 3 | | --- name: ParticleEmitterShapeInOut last_updated: 2026-06-29T19:34:08Z type: enum --- # ParticleEmitterShapeInOut **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Outward` | 0 | | | `Inward` | 1 | | | `InAndOut` | 2 | | --- name: ParticleEmitterShapeStyle last_updated: 2026-06-29T19:34:08Z type: enum --- # ParticleEmitterShapeStyle **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Volume` | 0 | | | `Surface` | 1 | | --- name: ParticleFlipbookLayout last_updated: 2026-06-29T19:34:08Z type: enum summary: "Determines the layout of the flipbook texture." --- # ParticleFlipbookLayout Determines the layout of the flipbook texture. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `None` | 0 | Disable flipbook features and use the texture as a single static texture over the particle's lifetime. | | `Grid2x2` | 1 | 2×2 frames for a 4-frame animation. | | `Grid4x4` | 2 | 4×4 frames for a 16-frame animation. | | `Grid8x8` | 3 | 8×8 frames for a 64-frame animation. | | `Custom` | 4 | A custom grid size defined by [FlipbookSizeX](/docs/reference/engine/classes/ParticleEmitter.md) and [FlipbookSizeY](/docs/reference/engine/classes/ParticleEmitter.md). | --- name: ParticleFlipbookMode last_updated: 2026-06-29T19:34:08Z type: enum summary: "Determines the type of the flipbook animation." --- # ParticleFlipbookMode Determines the type of the flipbook animation. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Loop` | 0 | Continuously play through all frames, starting back at the first frame after playing the last. | | `OneShot` | 1 | Play through the animation only once across the particle's lifetime. With this setting, the [ParticleEmitter.FlipbookFramerate](/docs/reference/engine/classes/ParticleEmitter.md) property doesn't apply; instead, the framerate is determined by the particle's [ParticleEmitter.Lifetime](/docs/reference/engine/classes/ParticleEmitter.md) divided evenly by the number of frames in the animation. **OneShot** animations are useful for clear non-repeating animations, such as an explosion that creates a puff of smoke and then fades out. | | `PingPong` | 2 | Play from the first to the last frame, then in reverse from the last to the first, repeating throughout the [ParticleEmitter.Lifetime](/docs/reference/engine/classes/ParticleEmitter.md) of the particle. | | `Random` | 3 | Play the frames in a random order, blending/crossfading from one frame to the next. This can be useful for organic particle textures at low framerates, such as stars slowly twinkling between subtly different shapes. | --- name: ParticleFlipbookTextureCompatible last_updated: 2026-06-29T19:34:08Z type: enum --- # ParticleFlipbookTextureCompatible **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `NotCompatible` | 0 | | | `Compatible` | 1 | | | `Unknown` | 2 | | --- name: ParticleOrientation last_updated: 2026-06-29T19:34:08Z type: enum --- # ParticleOrientation **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `FacingCamera` | 0 | | | `FacingCameraWorldUp` | 1 | | | `VelocityParallel` | 2 | | | `VelocityPerpendicular` | 3 | | --- name: PathStatus last_updated: 2026-06-29T19:34:08Z type: enum summary: "The success of a Path generated by PathfindingService." --- # PathStatus The success of a [Path](/docs/reference/engine/classes/Path.md) generated by [PathfindingService](/docs/reference/engine/classes/PathfindingService.md). **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Success` | 0 | Path found successfully. | | `ClosestNoPath` | 1 | Path doesn't exist, returns path to closest point. | | `ClosestOutOfRange` | 2 | Goal is out of MaxDistance range, returns path to closest point you can reach within MaxDistance. | | `FailStartNotEmpty` | 3 | Failed to compute path; the starting point is not empty. | | `FailFinishNotEmpty` | 4 | Failed to compute path; the finish point is not empty. | | `NoPath` | 5 | Path doesn't exist. | --- name: PathWaypointAction last_updated: 2026-06-29T19:34:08Z type: enum summary: "Describes the action to take when a PathWaypoint is reached." --- # PathWaypointAction Describes the action to take when a [PathWaypoint](/docs/reference/engine/datatypes/PathWaypoint.md) is reached. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Walk` | 0 | Walk action needed to reach the next waypoint. | | `Jump` | 1 | Jump action needed to reach the next waypoint. | | `Custom` | 2 | | --- name: PathfindingUseImprovedSearch last_updated: 2026-06-29T19:34:08Z type: enum summary: "Controls whether PathfindingService uses an improved search algorithm." --- # PathfindingUseImprovedSearch Controls whether [PathfindingService](/docs/reference/engine/classes/PathfindingService.md) uses an improved search algorithm. **Type:** enum ## Description Determines whether [PathfindingService](/docs/reference/engine/classes/PathfindingService.md) uses an improved pathfinding search algorithm. This enum is used by [Workspace.PathfindingUseImprovedSearch](/docs/reference/engine/classes/Workspace.md). ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | Uses the engine-default pathfinding behavior. | | `Disabled` | 1 | Uses the legacy pathfinding search algorithm. | | `Enabled` | 2 | Uses the improved pathfinding search algorithm. | --- name: PeoplePageLayout last_updated: 2026-06-29T19:34:08Z type: enum --- # PeoplePageLayout **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Card` | 0 | | | `List` | 1 | | --- name: PermissionLevelShown last_updated: 2026-06-29T19:34:08Z type: enum summary: "Used to set the highest permission level that APIs have to have in order to be shown in the Object Browser." --- # PermissionLevelShown Used to set the highest permission level that APIs have to have in order to be shown in the Object Browser. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Game` | 0 | Member must have no security permissions in order to be shown. | | `RobloxGame` | 1 | Member must have security permissions less than or equal to **RobloxPlaceSecurity** to be shown. | | `RobloxScript` | 2 | Member must have security permissions less than or equal to **RobloxScriptSecurity** to be shown. | | `Studio` | 3 | Member must have security permissions less than or equal to **LocalUserSecurity** to be shown. | | `Roblox` | 4 | Member is shown no matter what security it has. | --- name: PhysicalConstraintType last_updated: 2026-06-29T19:34:08Z type: enum --- # PhysicalConstraintType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `AnimationConstraint` | 0 | | | `Motor6D` | 1 | | --- name: PhysicsSimulationRate last_updated: 2026-06-29T19:34:08Z type: enum --- # PhysicsSimulationRate **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Fixed240Hz` | 0 | | | `Fixed120Hz` | 1 | | | `Fixed60Hz` | 2 | | --- name: PhysicsSteppingMethod last_updated: 2026-06-29T19:34:08Z type: enum --- # PhysicsSteppingMethod **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | The current default is `Adaptive`. | | `Fixed` | 1 | All simulated assemblies inside the workspace will advance forward at 240 Hz. This option is best for optimal stability and simulation accuracy. | | `Adaptive` | 2 | The engine attempts to assign optimal simulation rates for individual assemblies of either 240 Hz, 120 Hz, or 60 Hz. This setting is optimized for performance. | --- name: Platform last_updated: 2026-06-29T19:34:08Z type: enum summary: "Host operating system of the client." --- # Platform Host operating system of the client. **Type:** enum ## Description The **Platform** enum is used to determine the operating system of the client. ## Items | Name | Value | Description | |------|-------|-------------| | `Windows` | 0 | The client is running on a Windows Operating System. | | `OSX` | 1 | The client is running on OSX (Apple desktop). | | `IOS` | 2 | The client is running on IOS (Apple mobile). | | `Android` | 3 | The client is running on Android (Google mobile). | | `XBoxOne` | 4 | The client is running on an XBox One (console). | | `PS4` | 5 | The client is running on a PS4 (console). | | `PS3` | 6 | The client is running on a PS3 (console). | | `XBox360` | 7 | The client is running on an XBox 360 (console). | | `WiiU` | 8 | The client is running on a Wii U (console). | | `NX` | 9 | The client is running on an NX Operating System (Cisco Nexus). | | `Ouya` | 10 | The client is running on an Ouya Operating System (Android-Based). | | `AndroidTV` | 11 | The client is running on an Android TV. | | `Chromecast` | 12 | The client is running on Chromecast. | | `Linux` | 13 | The client is running on a Linux Operating System (desktop). | | `SteamOS` | 14 | The client is running on Steam. | | `WebOS` | 15 | The client is running on WebOS. | | `DOS` | 16 | The client is running on DOS. | | `BeOS` | 17 | The client is running on BeOS. | | `UWP` | 18 | The client is running on UWP. | | `PS5` | 19 | | | `MetaOS` | 20 | | | `Web` | 21 | | | `None` | 22 | The client's operating system is unknown. | --- name: PlaybackState last_updated: 2026-06-29T19:34:08Z type: enum summary: "Describes the current state of a Tween in its Tween.PlaybackState property." --- # PlaybackState Describes the current state of a [Tween](/docs/reference/engine/classes/Tween.md) in its [Tween.PlaybackState](/docs/reference/engine/classes/Tween.md) property. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Begin` | 0 | The tween has been created, but has yet to be played. After exiting this state, the tween never enters it again. | | `Delayed` | 1 | The tween is waiting for the duration specified in its [TweenInfo.DelayTime](/docs/reference/engine/datatypes/TweenInfo.md). After the delay elapses, the tween plays. | | `Playing` | 2 | The tween is currently in progress. | | `Paused` | 3 | The tween is paused in the middle of playing. | | `Completed` | 4 | The tween completed successfully. | | `Cancelled` | 5 | The tween was cancelled before completion. | --- name: PlayerActions last_updated: 2026-06-29T19:34:08Z type: enum summary: "References a movement action taken by a player." --- # PlayerActions References a movement action taken by a player. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `CharacterForward` | 0 | The player moved forward. | | `CharacterBackward` | 1 | The player moved backward. | | `CharacterLeft` | 2 | The player moved left. | | `CharacterRight` | 3 | The player moved right. | | `CharacterJump` | 4 | The player jumped. | --- name: PlayerCharacterDestroyBehavior last_updated: 2026-06-29T19:34:08Z type: enum summary: "Controls destruction behavior when a player character is removed." --- # PlayerCharacterDestroyBehavior Controls destruction behavior when a player character is removed. **Type:** enum ## Description Controls whether the engine automatically calls `Destroy()` on a player's old character when it is replaced and on the `Player` object when the player leaves. This enum is used by [Workspace.PlayerCharacterDestroyBehavior](/docs/reference/engine/classes/Workspace.md). ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | Uses the engine-default behavior. | | `Disabled` | 1 | Automatic `Destroy()` calls on character replacement and player removal are disabled. | | `Enabled` | 2 | The engine automatically calls `Destroy()` on old characters when they are replaced (for example, on respawn) and on `Player` objects when players leave. | --- name: PlayerChatType last_updated: 2026-06-29T19:34:08Z type: enum summary: "Determines the style of a chat message." --- # PlayerChatType Determines the style of a chat message. **Type:** enum ## Description Used by Roblox's Chat to determine the style of a chat message. If you want to check for this kind of stuff, see [Player.Chatted](/docs/reference/engine/classes/Player.md). ## Items | Name | Value | Description | |------|-------|-------------| | `All` | 0 | A global message that everyone can receive. | | `Team` | 1 | A team message only players in the same team can receive. | | `Whisper` | 2 | A whispered message only the person it's whispered to can receive. | --- name: PlayerDataErrorState last_updated: 2026-06-29T19:34:08Z type: enum --- # PlayerDataErrorState **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `LoadFailed` | 0 | | | `FlushFailed` | 1 | | | `ReleaseFailed` | 2 | | | `None` | 3 | | --- name: PlayerDataLoadFailureBehavior last_updated: 2026-06-29T19:34:08Z type: enum --- # PlayerDataLoadFailureBehavior **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Failure` | 0 | | | `FallbackToDefault` | 1 | | | `Kick` | 2 | | --- name: PlayerExitReason last_updated: 2026-06-29T19:34:08Z type: enum summary: "An enum that specifies the reason for **Players.PlayerRemoving** signal." --- # PlayerExitReason An enum that specifies the reason for **Players.PlayerRemoving** signal. **Type:** enum ## Description Second argument in [Players.PlayerRemoving](/docs/reference/engine/classes/Players.md). Helps identify whether a kick was caused by the Roblox platform or the Creator. ## Items | Name | Value | Description | |------|-------|-------------| | `Unknown` | 0 | Catch-all for all other disconnect reasons. | | `PlatformKick` | 1 | User was kicked by Roblox systems, such as being blocked while in a Private Server. | | `CreatorKick` | 2 | Creator called **Player:Kick()** | --- name: PlayerPlatformSpenderStatus last_updated: 2026-06-29T19:34:08Z type: enum summary: "Describes a player's platform-wide spender status bucket." --- # PlayerPlatformSpenderStatus Describes a player's platform-wide spender status bucket. **Type:** enum ## Description The `PlayerPlatformSpenderStatus` enum describes a player's platform-wide spender status bucket. ## Items | Name | Value | Description | |------|-------|-------------| | `Unknown` | 0 | Segment data is unavailable. | | `Active` | 1 | The player is an active platform spender. | | `OtherPayer` | 2 | The player is not an active platform spender, including players who have never spent. | --- name: PluginConnectionTargetType last_updated: 2026-06-29T19:34:08Z type: enum --- # PluginConnectionTargetType **Type:** enum ## Description An enum describing the possible relationships a target data model of a [PluginConnection](/docs/reference/engine/classes/PluginConnection.md) can have to the current data model. ## Items | Name | Value | Description | |------|-------|-------------| | `Edit` | 0 | | | `Test` | 1 | | --- name: PoseEasingDirection last_updated: 2026-06-29T19:34:08Z type: enum summary: "Used exclusively by Pose.EasingDirection to specify direction of the EasingStyle curve." --- # PoseEasingDirection Used exclusively by Pose.EasingDirection to specify direction of the EasingStyle curve. **Type:** enum ## Description When a Pose has an EasingStyle which is not symmetric, the EasingDirection determines if the curve is applied from previous keyframe to next, next keyframe to previous, or in both directions by using two copies of the curve back to back. For the symmetric Linear and Constant EasingStyles, this property has no effect. For legacy compatibility reasons, the use of In and Out are backwards from how these terms are used by most other animation tools and by TweenService. ## Items | Name | Value | Description | |------|-------|-------------| | `In` | 0 | The EasingStyle curve is reversed, with the easing becoming linear as it approaches the next keyframe. | | `Out` | 1 | EasingStyle curves are applied in the forward direction, from the current keyframe to the next. | | `InOut` | 2 | Two easing curves are applied back-to-back with the linear ends of the curves meeting in the middle. | --- name: PoseEasingStyle last_updated: 2026-06-29T19:34:08Z type: enum --- # PoseEasingStyle **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Linear` | 0 | Poses interpolate linearly between key frames. | | `Constant` | 1 | Poses do not interpolate but snap to the key frame indicated by the [PoseEasingDirection](/docs/reference/engine/enums/PoseEasingDirection.md). | | `Elastic` | 2 | Pose interpolation will overshoot like it is elastic. | | `Cubic` | 3 | Deprecated - Use [PoseEasingStyle.CubicV2](/docs/reference/engine/enums/PoseEasingStyle.md). Pose interpolation is a cubic curve between keyframes based on the [PoseEasingDirection](/docs/reference/engine/enums/PoseEasingDirection.md). | | `Bounce` | 4 | Pose interpolation produces a bounce like effect between key frames. | | `CubicV2` | 5 | Pose interpolation is a cubic curve between keyframes based on [PoseEasingDirection](/docs/reference/engine/enums/PoseEasingDirection.md). | --- name: PositionAlignmentMode last_updated: 2026-06-29T19:34:08Z type: enum summary: "The PositionAlignmentMode Enum is used to select the number of Attachments used in an alignment." --- # PositionAlignmentMode The PositionAlignmentMode Enum is used to select the number of [Attachments](/docs/reference/engine/classes/Attachment.md) used in an alignment. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `OneAttachment` | 0 | Use one attachment. | | `TwoAttachment` | 1 | Use two attachments. | --- name: PredictionMode last_updated: 2026-06-29T19:34:08Z type: enum summary: "Enum used with RunService:SetPredictionMode() to define the prediction mode for the instance." --- # PredictionMode Enum used with [RunService:SetPredictionMode()](/docs/reference/engine/classes/RunService.md) to define the prediction mode for the instance. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Automatic` | 0 | The engine will automatically decide if this instance should be predicted or not. Currently, only [BaseParts](/docs/reference/engine/classes/BasePart.md) near the local player character's [Humanoid](/docs/reference/engine/classes/Humanoid.md) will be predicted within a dynamic radius that grows and shrinks based on the device's capability to handle the simulation load. | | `On` | 1 | The instance will be predicted. Mismatches in the attributes of this instance between the client and server will cause a rollback and resimulation. If the instance is a [BasePart](/docs/reference/engine/classes/BasePart.md), its physics properties will be predicted ahead of the replicated authoritative server state. | | `Off` | 2 | The instance will **not** be predicted. Regular network ownership semantics apply. | --- name: PredictionStatus last_updated: 2026-06-29T19:34:08Z type: enum summary: "Enum used with RunService:GetPredictionStatus() to check the status of a specific instance." --- # PredictionStatus Enum used with [RunService:GetPredictionStatus()](/docs/reference/engine/classes/RunService.md) to check the status of a specific instance. **Type:** enum ## Description Enum used with [RunService:GetPredictionStatus()](/docs/reference/engine/classes/RunService.md) to check the status of a specific instance. This prediction status can be used to determine whether any associated scripts should run. If the status is `Authoritative` or `Predicted`, such scripts should run, since the local node is either the authority for the instance or is predicting it. If the status is `None`, such scripts do not need to run. ## Items | Name | Value | Description | |------|-------|-------------| | `Authoritative` | 0 | This instance is authoritative. | | `Predicted` | 1 | This instance is being predicted. | | `None` | 2 | The instance will not be resimulated; classic simulation rules apply instead. | --- name: PreferredInput last_updated: 2026-06-29T19:34:08Z type: enum summary: "This enum is used with UserInputService.PreferredInput to indicate the primary input type a player is likely using." --- # PreferredInput This enum is used with [UserInputService.PreferredInput](/docs/reference/engine/classes/UserInputService.md) to indicate the primary input type a player is likely using. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `KeyboardAndMouse` | 0 | The player has connected or most recently interacted with a keyboard or mouse. | | `Gamepad` | 1 | The player has connected or most recently interacted with a gamepad. | | `Touch` | 2 | The player's device has touch capability and no other input method is available or was recently interacted with. | --- name: PreferredTextSize last_updated: 2026-06-29T19:34:08Z type: enum summary: "This enum is used with GuiService.PreferredTextSize to indicate the player's preferred text size." --- # PreferredTextSize This enum is used with [GuiService.PreferredTextSize](/docs/reference/engine/classes/GuiService.md) to indicate the player's preferred text size. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Medium` | 1 | The player prefers default text size. | | `Large` | 2 | The player prefers text size slightly increased beyond default. | | `Larger` | 3 | The player prefers text size moderately increased beyond default. | | `Largest` | 4 | The player prefers text size considerably increased beyond default. | --- name: PrimalPhysicsSolver last_updated: 2026-06-29T19:34:08Z type: enum summary: "Controls whether the experimental Primal Physics Solver is enabled." --- # PrimalPhysicsSolver Controls whether the experimental Primal Physics Solver is enabled. **Type:** enum ## Description An experimental constraint solver that can improve accuracy in complex setups. This enum is used by [Workspace.PrimalPhysicsSolver](/docs/reference/engine/classes/Workspace.md). ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | Uses the engine-default physics solver state. | | `Experimental` | 1 | The experimental Primal Physics Solver is enabled. | | `Disabled` | 2 | The Primal Physics Solver is disabled. | --- name: PrimitiveType last_updated: 2026-06-29T19:34:08Z type: enum --- # PrimitiveType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Null` | 0 | | | `Ball` | 1 | | | `Cylinder` | 2 | | | `Block` | 3 | | | `Wedge` | 4 | | | `CornerWedge` | 5 | | --- name: PrivilegeType last_updated: 2026-06-29T19:34:08Z type: enum tags: - Deprecated --- # PrivilegeType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Banned` | 0 | | | `Visitor` | 10 | | | `Member` | 128 | | | `Admin` | 240 | | | `Owner` | 255 | | --- name: ProductLocationRestriction last_updated: 2026-06-29T19:34:08Z type: enum --- # ProductLocationRestriction **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `AvatarShop` | 0 | Item is purchasable only in the Marketplace. | | `AllowedGames` | 1 | Item is purchasable in the Marketplace and experiences owned by the item creator. | | `AllGames` | 2 | (Default) Item is purchasable in the Marketplace and all experiences. | --- name: ProductPurchaseChannel last_updated: 2026-06-29T19:34:08Z type: enum summary: "Enum which works with MarketplaceService to represent how the user acquired the developer product." --- # ProductPurchaseChannel Enum which works with [MarketplaceService](/docs/reference/engine/classes/MarketplaceService.md) to represent how the user acquired the developer product. **Type:** enum ## Description The `ProductPurchaseChannel` enum works with [MarketplaceService](/docs/reference/engine/classes/MarketplaceService.md) and represents how the user acquired the developer product. ## Items | Name | Value | Description | |------|-------|-------------| | `InExperience` | 1 | The purchase was made inside of an experience. | | `ExperienceDetailsPage` | 2 | The purchase was made outside of an experience, on the **Store** tab of the experience details page. | | `AdReward` | 3 | The product was rewarded to the user when they watched a video ad to completion. | | `CommerceProduct` | 4 | The user acquired the product as a benefit of purchasing commerce merchandise from the developer of the experience. | --- name: ProductPurchaseDecision last_updated: 2026-06-29T19:34:08Z type: enum summary: "The `ProductPurchaseDecisionEnum` works with MarketplaceService to indicate the status of the sale of developer products." --- # ProductPurchaseDecision The `ProductPurchaseDecisionEnum` works with [MarketplaceService](/docs/reference/engine/classes/MarketplaceService.md) to indicate the status of the sale of developer products. **Type:** enum ## Description The `ProductPurchaseDecisionEnum` is used to work with [MarketplaceService](/docs/reference/engine/classes/MarketplaceService.md), and the sale of developer products. ## Items | Name | Value | Description | |------|-------|-------------| | `NotProcessedYet` | 0 | The purchase request has been sent but not yet processed. | | `PurchaseGranted` | 1 | The purchase has been processed and granted to the user who initiated the purchase request. | --- name: PromptCreateAssetResult last_updated: 2026-06-29T19:34:08Z type: enum --- # PromptCreateAssetResult **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Success` | 1 | | | `PermissionDenied` | 2 | | | `Timeout` | 3 | | | `UploadFailed` | 4 | | | `NoUserInput` | 5 | | | `UnknownFailure` | 6 | | | `UGCValidationFailed` | 7 | | | `ModeratedName` | 8 | | | `PurchaseFailure` | 9 | | | `TokenInvalid` | 10 | | --- name: PromptCreateAvatarResult last_updated: 2026-06-29T19:34:08Z type: enum --- # PromptCreateAvatarResult **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Success` | 1 | | | `PermissionDenied` | 2 | | | `Timeout` | 3 | | | `UploadFailed` | 4 | | | `NoUserInput` | 5 | | | `InvalidHumanoidDescription` | 6 | | | `UGCValidationFailed` | 7 | | | `ModeratedName` | 8 | | | `MaxOutfits` | 9 | | | `PurchaseFailure` | 10 | | | `UnknownFailure` | 11 | | | `TokenInvalid` | 12 | | --- name: PromptExperienceDetailsResult last_updated: 2026-06-29T19:34:08Z type: enum --- # PromptExperienceDetailsResult **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `PromptClosed` | 0 | | | `TeleportAttempted` | 1 | | --- name: PromptPublishAssetResult last_updated: 2026-06-29T19:34:08Z type: enum --- # PromptPublishAssetResult **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Success` | 1 | | | `PermissionDenied` | 2 | | | `Timeout` | 3 | | | `UploadFailed` | 4 | | | `NoUserInput` | 5 | | | `UnknownFailure` | 6 | | --- name: PropertyStatus last_updated: 2026-06-29T19:34:08Z type: enum --- # PropertyStatus **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Ok` | 0 | | | `Warning` | 1 | | | `Error` | 2 | | --- name: ProximityPromptExclusivity last_updated: 2026-06-29T19:34:08Z type: enum --- # ProximityPromptExclusivity **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `OnePerButton` | 0 | One prompt will be shown per input [KeyCode](/docs/reference/engine/enums/KeyCode.md). | | `OneGlobally` | 1 | Only one prompt will be shown with this setting. | | `AlwaysShow` | 2 | This prompt will always show when in range and visible. | --- name: ProximityPromptInputType last_updated: 2026-06-29T19:34:08Z type: enum --- # ProximityPromptInputType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Keyboard` | 0 | | | `Gamepad` | 1 | | | `Touch` | 2 | | --- name: ProximityPromptStyle last_updated: 2026-06-29T19:34:08Z type: enum --- # ProximityPromptStyle **Type:** enum ## Code Samples **Generating a Custom Proximity Prompt** The example below generates a custom `ProximityPrompt` UI similar to the default style, which you may use as a starting point. It should be used in a `LocalScript`. ```lua local UserInputService = game:GetService("UserInputService") local ProximityPromptService = game:GetService("ProximityPromptService") local TweenService = game:GetService("TweenService") local RunService = game:GetService("RunService") local Players = game:GetService("Players") local LocalPlayer = Players.LocalPlayer while LocalPlayer == nil do Players.ChildAdded:wait() LocalPlayer = Players.LocalPlayer end local PlayerGui = LocalPlayer:WaitForChild("PlayerGui") local KeyboardButtonImage = { [Enum.KeyCode.Backspace] = "rbxasset://textures/ui/Controls/backspace.png", [Enum.KeyCode.Return] = "rbxasset://textures/ui/Controls/return.png", [Enum.KeyCode.LeftShift] = "rbxasset://textures/ui/Controls/shift.png", [Enum.KeyCode.RightShift] = "rbxasset://textures/ui/Controls/shift.png", [Enum.KeyCode.Tab] = "rbxasset://textures/ui/Controls/tab.png", } local KeyboardButtonIconMapping = { ["'"] = "rbxasset://textures/ui/Controls/apostrophe.png", [","] = "rbxasset://textures/ui/Controls/comma.png", ["`"] = "rbxasset://textures/ui/Controls/graveaccent.png", ["."] = "rbxasset://textures/ui/Controls/period.png", [" "] = "rbxasset://textures/ui/Controls/spacebar.png", } local KeyCodeToTextMapping = { [Enum.KeyCode.LeftControl] = "Ctrl", [Enum.KeyCode.RightControl] = "Ctrl", [Enum.KeyCode.LeftAlt] = "Alt", [Enum.KeyCode.RightAlt] = "Alt", [Enum.KeyCode.F1] = "F1", [Enum.KeyCode.F2] = "F2", [Enum.KeyCode.F3] = "F3", [Enum.KeyCode.F4] = "F4", [Enum.KeyCode.F5] = "F5", [Enum.KeyCode.F6] = "F6", [Enum.KeyCode.F7] = "F7", [Enum.KeyCode.F8] = "F8", [Enum.KeyCode.F9] = "F9", [Enum.KeyCode.F10] = "F10", [Enum.KeyCode.F11] = "F11", [Enum.KeyCode.F12] = "F12", } KeyCodeToTextMapping[Enum.KeyCode.PageUp] = "PgUp" KeyCodeToTextMapping[Enum.KeyCode.PageDown] = "PgDn" KeyCodeToTextMapping[Enum.KeyCode.Home] = "Home" KeyCodeToTextMapping[Enum.KeyCode.End] = "End" KeyCodeToTextMapping[Enum.KeyCode.Insert] = "Ins" KeyCodeToTextMapping[Enum.KeyCode.Delete] = "Del" local KeyCodeToFontSize = { [Enum.KeyCode.LeftControl] = 12, [Enum.KeyCode.RightControl] = 12, [Enum.KeyCode.LeftAlt] = 12, [Enum.KeyCode.RightAlt] = 12, [Enum.KeyCode.F10] = 12, [Enum.KeyCode.F11] = 12, [Enum.KeyCode.F12] = 12, [Enum.KeyCode.PageUp] = 8, [Enum.KeyCode.PageDown] = 8, [Enum.KeyCode.Home] = 8, [Enum.KeyCode.End] = 10, [Enum.KeyCode.Insert] = 10, [Enum.KeyCode.Delete] = 10, } -- Colors local BackgroundColor = Color3.new(0.07, 0.07, 0.07) local ContentColor = Color3.new(1, 1, 1) local SecondaryContentColor = Color3.new(0.7, 0.7, 0.7) -- TweenInfo presets local tweenInfoQuick = TweenInfo.new(0.06, Enum.EasingStyle.Linear, Enum.EasingDirection.Out) local tweenInfoOutHalfSecond = TweenInfo.new(0.5, Enum.EasingStyle.Quad, Enum.EasingDirection.Out) local tweenInfoFast = TweenInfo.new(0.2, Enum.EasingStyle.Quad, Enum.EasingDirection.Out) local function getScreenGui() local screenGui = PlayerGui:FindFirstChild("ProximityPrompts") if screenGui == nil then screenGui = Instance.new("ScreenGui") screenGui.Name = "ProximityPrompts" screenGui.ResetOnSpawn = false screenGui.Parent = PlayerGui screenGui.IgnoreGuiInset = true end return screenGui end local progressGradientFuzz = 0.01 local function createProgressBarGradient(parent, leftSide, tweensForFadeOut, tweensForFadeIn) local frame = Instance.new("Frame") frame.Size = UDim2.fromScale(0.5, 1) frame.Position = UDim2.fromScale(leftSide and 0 or 0.5, 0) frame.BackgroundTransparency = 1 frame.ClipsDescendants = true frame.Visible = false frame.Parent = parent local image = Instance.new("ImageLabel") image.BackgroundTransparency = 1 image.Size = UDim2.fromScale(2, 1) image.Position = UDim2.fromScale(leftSide and 0 or -1, 0) image.Image = "rbxasset://textures/ui/Controls/RadialFill.png" image.Parent = frame local gradient = Instance.new("UIGradient") gradient.Transparency = NumberSequence.new { NumberSequenceKeypoint.new(0, 0), NumberSequenceKeypoint.new(0.5, 0), NumberSequenceKeypoint.new(0.5 + progressGradientFuzz, 1), NumberSequenceKeypoint.new(1, 1) } gradient.Rotation = leftSide and 180 or 0 gradient.Parent = image table.insert(tweensForFadeOut, TweenService:Create(image, tweenInfoQuick, { ImageTransparency = 1 })) table.insert(tweensForFadeIn, TweenService:Create(image, tweenInfoQuick, { ImageTransparency = 0 })) return gradient, frame end local function createCircularProgressBar(tweensForFadeOut, tweensForFadeIn) local bar = Instance.new("Frame") bar.Name = "CircularProgressBar" bar.Size = UDim2.fromOffset(58, 58) bar.AnchorPoint = Vector2.new(0.5, 0.5) bar.Position = UDim2.fromScale(0.5, 0.5) bar.BackgroundTransparency = 1 local leftGradient, leftFrame = createProgressBarGradient(bar, true, tweensForFadeOut, tweensForFadeIn) local rightGradient, rightFrame = createProgressBarGradient(bar, false, tweensForFadeOut, tweensForFadeIn) local progress = Instance.new("NumberValue") progress.Name = "Progress" progress.Parent = bar progress.Changed:Connect(function(value) local angle = math.clamp(value * 360, 0, 360) leftFrame.Visible = progress.Value > 0.5 leftGradient.Rotation = math.clamp(angle, 180, 360) rightFrame.Visible = progress.Value > progressGradientFuzz rightGradient.Rotation = math.clamp(angle, 0, 180) end) return bar end local function createPrompt(prompt, inputType, gui) local tweensForButtonHoldBegin = {} local tweensForButtonHoldEnd = {} local tweensForFadeOut = {} local tweensForFadeIn = {} local tweenInfoInFullDuration = TweenInfo.new(prompt.HoldDuration, Enum.EasingStyle.Linear, Enum.EasingDirection.Out) local promptUI = Instance.new("BillboardGui") promptUI.Name = "Prompt" promptUI.AlwaysOnTop = true local frame = Instance.new("Frame") frame.Size = UDim2.fromScale(0.5, 1) frame.BackgroundTransparency = 1 frame.BackgroundColor3 = BackgroundColor frame.AnchorPoint = Vector2.new(0.5, 0) frame.Position = UDim2.fromScale(0.5, 0) frame.Size = UDim2.fromScale(0, 1) frame.AutomaticSize = Enum.AutomaticSize.X frame.Parent = promptUI -- Add right padding local padding = Instance.new("UIPadding") padding.Parent = frame local roundedCorner = Instance.new("UICorner") roundedCorner.Parent = frame local inputFrame = Instance.new("Frame") inputFrame.Name = "InputFrame" inputFrame.Size = UDim2.fromScale(1, 1) inputFrame.BackgroundTransparency = 1 inputFrame.SizeConstraint = Enum.SizeConstraint.RelativeYY inputFrame.Parent = frame local resizeableInputFrame = Instance.new("Frame") resizeableInputFrame.Size = UDim2.fromScale(1, 1) resizeableInputFrame.Position = UDim2.fromScale(0.5, 0.5) resizeableInputFrame.AnchorPoint = Vector2.new(0.5, 0.5) resizeableInputFrame.BackgroundTransparency = 1 resizeableInputFrame.Parent = inputFrame local inputFrameScaler = Instance.new("UIScale") inputFrameScaler.Parent = resizeableInputFrame local inputFrameScaleFactor = inputType == Enum.ProximityPromptInputType.Touch and 1.6 or 1.33 table.insert(tweensForButtonHoldBegin, TweenService:Create(inputFrameScaler, tweenInfoFast, { Scale = inputFrameScaleFactor })) table.insert(tweensForButtonHoldEnd, TweenService:Create(inputFrameScaler, tweenInfoFast, { Scale = 1 })) local actionText = Instance.new("TextLabel") actionText.Name = "ActionText" actionText.Font = Enum.Font.GothamMedium actionText.TextSize = 19 actionText.BackgroundTransparency = 1 actionText.TextTransparency = 1 actionText.TextColor3 = ContentColor actionText.TextXAlignment = Enum.TextXAlignment.Left table.insert(tweensForButtonHoldBegin, TweenService:Create(actionText, tweenInfoFast, { TextTransparency = 1 })) table.insert(tweensForButtonHoldEnd, TweenService:Create(actionText, tweenInfoFast, { TextTransparency = 0 })) table.insert(tweensForFadeOut, TweenService:Create(actionText, tweenInfoFast, { TextTransparency = 1 })) table.insert(tweensForFadeIn, TweenService:Create(actionText, tweenInfoFast, { TextTransparency = 0 })) local objectText = Instance.new("TextLabel") objectText.Name = "ObjectText" objectText.Font = Enum.Font.GothamMedium objectText.TextSize = 14 objectText.BackgroundTransparency = 1 objectText.TextTransparency = 1 objectText.TextColor3 = SecondaryContentColor objectText.TextXAlignment = Enum.TextXAlignment.Left -- Add list layout, as well a tween out to mimic old shrinking local listLayout = Instance.new("UIListLayout") listLayout.FillDirection = Enum.FillDirection.Horizontal listLayout.Padding = UDim.new(-0.25, 0) listLayout.Parent = frame table.insert(tweensForButtonHoldBegin, TweenService:Create(listLayout, tweenInfoFast, { Padding = UDim.new(-0.25, 0) })) table.insert(tweensForButtonHoldEnd, TweenService:Create(listLayout, tweenInfoFast, { Padding = UDim.new(0, 0) })) table.insert(tweensForFadeOut, TweenService:Create(listLayout, tweenInfoFast, { Padding = UDim.new(-0.25, 0) })) table.insert(tweensForFadeIn, TweenService:Create(listLayout, tweenInfoFast, { Padding = UDim.new(0, 0) })) -- Add container for text labels local textFrame = Instance.new("Frame") textFrame.Name = "TextFrame" textFrame.Size = UDim2.fromScale(0, 1) textFrame.AutomaticSize = Enum.AutomaticSize.X textFrame.BackgroundTransparency = 1 textFrame.Parent = frame -- ActionText and ObjectText automatic sizing, parent to textFrame actionText.Size = UDim2.fromScale(0, 1) actionText.AutomaticSize = Enum.AutomaticSize.X actionText.Parent = textFrame objectText.Size = UDim2.fromScale(0, 1) objectText.AutomaticSize = Enum.AutomaticSize.X objectText.Parent = textFrame table.insert(tweensForButtonHoldBegin, TweenService:Create(objectText, tweenInfoFast, { TextTransparency = 1 })) table.insert(tweensForButtonHoldEnd, TweenService:Create(objectText, tweenInfoFast, { TextTransparency = 0 })) table.insert(tweensForFadeOut, TweenService:Create(objectText, tweenInfoFast, { TextTransparency = 1 })) table.insert(tweensForFadeIn, TweenService:Create(objectText, tweenInfoFast, { TextTransparency = 0 })) table.insert(tweensForButtonHoldBegin, TweenService:Create(frame, tweenInfoFast, { BackgroundTransparency = 1 })) table.insert(tweensForButtonHoldEnd, TweenService:Create(frame, tweenInfoFast, { BackgroundTransparency = 0.2 })) table.insert(tweensForFadeOut, TweenService:Create(frame, tweenInfoFast, { BackgroundTransparency = 1 })) table.insert(tweensForFadeIn, TweenService:Create(frame, tweenInfoFast, { BackgroundTransparency = 0.2 })) local roundFrame = Instance.new("Frame") roundFrame.Name = "RoundFrame" roundFrame.Size = UDim2.fromOffset(48, 48) roundFrame.AnchorPoint = Vector2.new(0.5, 0.5) roundFrame.Position = UDim2.fromScale(0.5, 0.5) roundFrame.BackgroundTransparency = 1 roundFrame.Parent = resizeableInputFrame local roundedFrameCorner = Instance.new("UICorner") roundedFrameCorner.CornerRadius = UDim.new(0.5, 0) roundedFrameCorner.Parent = roundFrame table.insert(tweensForFadeOut, TweenService:Create(roundFrame, tweenInfoQuick, { BackgroundTransparency = 1 })) table.insert(tweensForFadeIn, TweenService:Create(roundFrame, tweenInfoQuick, { BackgroundTransparency = 0.5 })) if inputType == Enum.ProximityPromptInputType.Gamepad then local mappedIconImage = UserInputService:GetImageForKeyCode(prompt.GamepadKeyCode) if mappedIconImage then local icon = Instance.new("ImageLabel") icon.Name = "ButtonImage" icon.AnchorPoint = Vector2.new(0.5, 0.5) icon.Size = UDim2.fromOffset(24, 24) icon.Position = UDim2.fromScale(0.5, 0.5) icon.BackgroundTransparency = 1 icon.ImageTransparency = 1 icon.Image = mappedIconImage icon.Parent = resizeableInputFrame table.insert(tweensForFadeOut, TweenService:Create(icon, tweenInfoQuick, { ImageTransparency = 1 })) table.insert(tweensForFadeIn, TweenService:Create(icon, tweenInfoQuick, { ImageTransparency = 0 })) end elseif inputType == Enum.ProximityPromptInputType.Touch then local buttonImage = Instance.new("ImageLabel") buttonImage.Name = "ButtonImage" buttonImage.BackgroundTransparency = 1 buttonImage.ImageTransparency = 1 buttonImage.Size = UDim2.fromOffset(25, 31) buttonImage.AnchorPoint = Vector2.new(0.5, 0.5) buttonImage.Position = UDim2.fromScale(0.5, 0.5) buttonImage.Image = "rbxasset://textures/ui/Controls/TouchTapIcon.png" buttonImage.Parent = resizeableInputFrame table.insert(tweensForFadeOut, TweenService:Create(buttonImage, tweenInfoQuick, { ImageTransparency = 1 })) table.insert(tweensForFadeIn, TweenService:Create(buttonImage, tweenInfoQuick, { ImageTransparency = 0 })) else local buttonImage = Instance.new("ImageLabel") buttonImage.Name = "ButtonImage" buttonImage.BackgroundTransparency = 1 buttonImage.ImageTransparency = 1 buttonImage.Size = UDim2.fromOffset(28, 30) buttonImage.AnchorPoint = Vector2.new(0.5, 0.5) buttonImage.Position = UDim2.fromScale(0.5, 0.5) buttonImage.Image = "rbxasset://textures/ui/Controls/key_single.png" buttonImage.Parent = resizeableInputFrame table.insert(tweensForFadeOut, TweenService:Create(buttonImage, tweenInfoQuick, { ImageTransparency = 1 })) table.insert(tweensForFadeIn, TweenService:Create(buttonImage, tweenInfoQuick, { ImageTransparency = 0 })) local buttonTextString = UserInputService:GetStringForKeyCode(prompt.KeyboardKeyCode) local buttonTextImage = KeyboardButtonImage[prompt.KeyboardKeyCode] if buttonTextImage == nil then buttonTextImage = KeyboardButtonIconMapping[buttonTextString] end if buttonTextImage == nil then local keyCodeMappedText = KeyCodeToTextMapping[prompt.KeyboardKeyCode] if keyCodeMappedText then buttonTextString = keyCodeMappedText end end if buttonTextImage then local icon = Instance.new("ImageLabel") icon.Name = "ButtonImage" icon.AnchorPoint = Vector2.new(0.5, 0.5) icon.Size = UDim2.fromOffset(36, 36) icon.Position = UDim2.fromScale(0.5, 0.5) icon.BackgroundTransparency = 1 icon.ImageTransparency = 1 icon.Image = buttonTextImage icon.Parent = resizeableInputFrame table.insert(tweensForFadeOut, TweenService:Create(icon, tweenInfoQuick, { ImageTransparency = 1 })) table.insert(tweensForFadeIn, TweenService:Create(icon, tweenInfoQuick, { ImageTransparency = 0 })) elseif buttonTextString ~= nil and buttonTextString ~= '' then local buttonText = Instance.new("TextLabel") buttonText.Name = "ButtonText" buttonText.AutoLocalize = false buttonText.Position = UDim2.fromOffset(0, -1) buttonText.Size = UDim2.fromScale(1, 1) buttonText.Font = Enum.Font.GothamMedium local buttonTextSize = KeyCodeToFontSize[prompt.KeyboardKeyCode] if buttonTextSize == nil then buttonTextSize = 14 end buttonText.TextSize = buttonTextSize buttonText.BackgroundTransparency = 1 buttonText.TextTransparency = 1 buttonText.TextColor3 = ContentColor buttonText.TextXAlignment = Enum.TextXAlignment.Center buttonText.Text = buttonTextString buttonText.Parent = resizeableInputFrame table.insert(tweensForFadeOut, TweenService:Create(buttonText, tweenInfoQuick, { TextTransparency = 1 })) table.insert(tweensForFadeIn, TweenService:Create(buttonText, tweenInfoQuick, { TextTransparency = 0 })) else error("ProximityPrompt '" .. prompt.Name .. "' has an unsupported keycode for rendering UI: " .. tostring(prompt.KeyboardKeyCode)) end end if inputType == Enum.ProximityPromptInputType.Touch or prompt.ClickablePrompt then local button = Instance.new("TextButton") button.BackgroundTransparency = 1 button.TextTransparency = 1 button.Size = UDim2.fromScale(1, 1) button.Parent = promptUI button.Selectable = false local buttonDown = false button.InputBegan:Connect(function(input) if (input.UserInputType == Enum.UserInputType.Touch or input.UserInputType == Enum.UserInputType.MouseButton1) and input.UserInputState ~= Enum.UserInputState.Change then prompt:InputHoldBegin() buttonDown = true end end) button.InputEnded:Connect(function(input) if input.UserInputType == Enum.UserInputType.Touch or input.UserInputType == Enum.UserInputType.MouseButton1 then if buttonDown then buttonDown = false prompt:InputHoldEnd() end end end) promptUI.Active = true end if prompt.HoldDuration > 0 then local circleBar = createCircularProgressBar(tweensForFadeOut, tweensForFadeIn) circleBar.Parent = resizeableInputFrame table.insert(tweensForButtonHoldBegin, TweenService:Create(circleBar.Progress, tweenInfoInFullDuration, { Value = 1 })) table.insert(tweensForButtonHoldEnd, TweenService:Create(circleBar.Progress, tweenInfoOutHalfSecond, { Value = 0 })) end local holdBeganConnection local holdEndedConnection local triggeredConnection local triggerEndedConnection if prompt.HoldDuration > 0 then holdBeganConnection = prompt.PromptButtonHoldBegan:Connect(function() for _, tween in ipairs(tweensForButtonHoldBegin) do tween:Play() end end) holdEndedConnection = prompt.PromptButtonHoldEnded:Connect(function() for _, tween in ipairs(tweensForButtonHoldEnd) do tween:Play() end end) end triggeredConnection = prompt.Triggered:Connect(function() for _, tween in ipairs(tweensForFadeOut) do tween:Play() end end) triggerEndedConnection = prompt.TriggerEnded:Connect(function() for _, tween in ipairs(tweensForFadeIn) do tween:Play() end end) local function updateUIFromPrompt() local promptHeight = 72 local promptWidth = 72 local textPaddingRight = 24 local actionTextYOffset = 0 if prompt.ObjectText ~= nil and prompt.ObjectText ~= '' then actionTextYOffset = 9 end actionText.Text = prompt.ActionText objectText.Text = prompt.ObjectText actionText.AutoLocalize = prompt.AutoLocalize actionText.RootLocalizationTable = prompt.RootLocalizationTable objectText.AutoLocalize = prompt.AutoLocalize objectText.RootLocalizationTable = prompt.RootLocalizationTable textPaddingRight = textPaddingRight - actionTextYOffset if (prompt.ActionText ~= nil and prompt.ActionText ~= '') or (prompt.ObjectText ~= nil and prompt.ObjectText ~= '') then padding.PaddingRight = UDim.new(0, textPaddingRight) else padding.PaddingRight = UDim.new(0, 0) end actionText.Position = UDim2.new(0, 0, 0, actionTextYOffset) objectText.Position = UDim2.new(0, 0, 0, -10) promptUI.Size = UDim2.fromOffset(promptWidth, promptHeight) promptUI.SizeOffset = Vector2.new(prompt.UIOffset.X / promptUI.Size.Width.Offset, prompt.UIOffset.Y / promptUI.Size.Height.Offset) -- BillboardGuis can't be automatically sized, so we need to calculate -- the size based on the automatically sized prompt frame. task.defer(function () -- Automatic sizing takes approximately 2 render cycles to be calculated RunService.RenderStepped:Wait() RunService.RenderStepped:Wait() promptWidth = frame.AbsoluteSize.X promptUI.Size = UDim2.fromOffset(promptWidth, promptHeight) promptUI.SizeOffset = Vector2.new(prompt.UIOffset.X / promptUI.Size.Width.Offset, prompt.UIOffset.Y / promptUI.Size.Height.Offset) end) end local changedConnection = prompt.Changed:Connect(updateUIFromPrompt) updateUIFromPrompt() promptUI.Adornee = prompt.Parent promptUI.Parent = gui local function updateUIAncestry() promptUI.Adornee = prompt.Parent end local ancestryConnection = prompt.AncestryChanged:Connect(updateUIAncestry) for _, tween in ipairs(tweensForFadeIn) do tween:Play() end local function cleanup() if holdBeganConnection then holdBeganConnection:Disconnect() end if holdEndedConnection then holdEndedConnection:Disconnect() end triggeredConnection:Disconnect() triggerEndedConnection:Disconnect() changedConnection:Disconnect() ancestryConnection:Disconnect() for _, tween in ipairs(tweensForFadeOut) do tween:Play() end wait(0.2) promptUI.Parent = nil end return cleanup end local function createIndicator(prompt, gui) local tweensForFadeOut = {} local tweensForFadeIn = {} local indicatorUI = Instance.new("BillboardGui") indicatorUI.Name = "Indicator" indicatorUI.Size = UDim2.fromOffset(15, 15) indicatorUI.AlwaysOnTop = true local frame = Instance.new("Frame") frame.Size = UDim2.fromScale(1, 1) frame.BackgroundTransparency = 1 frame.BackgroundColor3 = BackgroundColor frame.AnchorPoint = Vector2.new(0.5, 0) frame.Position = UDim2.fromScale(0.5, 0) frame.Parent = indicatorUI local corner = Instance.new("UICorner") corner.CornerRadius = UDim.new(1, 0) corner.Parent = frame local stroke = Instance.new("UIStroke") stroke.Color = ContentColor stroke.Thickness = 1.5 stroke.Transparency = 1 stroke.Parent = frame table.insert(tweensForFadeOut, TweenService:Create(frame, tweenInfoQuick, { BackgroundTransparency = 1 })) table.insert(tweensForFadeOut, TweenService:Create(stroke, tweenInfoQuick, { Transparency = 1 })) table.insert(tweensForFadeIn, TweenService:Create(frame, tweenInfoQuick, { BackgroundTransparency = 0.2 })) table.insert(tweensForFadeIn, TweenService:Create(stroke, tweenInfoQuick, { Transparency = 0.2 })) indicatorUI.Adornee = prompt.Parent indicatorUI.Parent = gui local function updateUIAncestry() indicatorUI.Adornee = prompt.Parent end local ancestryConnection = prompt.AncestryChanged:Connect(updateUIAncestry) for _, tween in ipairs(tweensForFadeIn) do tween:Play() end local function cleanup() ancestryConnection:Disconnect() for _, tween in ipairs(tweensForFadeOut) do tween:Play() end wait(0.2) indicatorUI.Parent = nil end return cleanup end local function onLoad() ProximityPromptService.PromptShown:Connect(function(prompt, inputType) if prompt.Style ~= Enum.ProximityPromptStyle.Custom then return end local gui = getScreenGui() local cleanupFunction = createPrompt(prompt, inputType, gui) -- Wait for either the prompt being hidden or destroyed local yield = Instance.new("BindableEvent") local con = prompt.PromptHidden:Connect(function() yield:Fire() end) local con2 = prompt.Destroying:Connect(function() yield:Fire() end) yield.Event:Wait() con:Disconnect() con2:Disconnect() cleanupFunction() end) ProximityPromptService.IndicatorShown:Connect(function(prompt) if prompt.Style ~= Enum.ProximityPromptStyle.Custom then return end local gui = getScreenGui() local cleanupFunction = createIndicator(prompt, gui) -- Wait for either the indicator being hidden or the prompt is destroyed local yield = Instance.new("BindableEvent") local con = prompt.IndicatorHidden:Connect(function() yield:Fire() end) local con2 = prompt.Destroying:Connect(function() yield:Fire() end) yield.Event:Wait() con:Disconnect() con2:Disconnect() cleanupFunction() end) end onLoad() ``` ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | The default prompt UI style. | | `Custom` | 1 | Custom prompt UI style as set by the developer. For more information, see [ProximityPrompt.Style](/docs/reference/engine/classes/ProximityPrompt.md). | --- name: QualityLevel last_updated: 2026-06-29T19:34:08Z type: enum summary: "Controls the rendering quality of the game." --- # QualityLevel Controls the rendering quality of the game. **Type:** enum ## Description Controls the rendering quality of the game. Higher levels correspond to higher quality graphics. As QualityLevel increases, Roblox rendering engine increases the rendering distance, shading quality, apparent geometry and texture resolution, limits on particles that are being rendered, fidelity of trail effects, post-effect quality and more. On low quality levels some post effects may be completely disabled and objects that are far away will not be rendered. The specific effects of quality level on the individual elements of the scene or individual rendering features are platform- and device- specific and can not be relied upon. ## Items | Name | Value | Description | |------|-------|-------------| | `Automatic` | 0 | The quality level of graphics is determined automatically depending on the client's setting for either game performance or graphic quality. | | `Level01` | 1 | Graphic quality level 1 - the lowest quality level. | | `Level02` | 2 | Graphic quality level 2. | | `Level03` | 3 | Graphic quality level 3. | | `Level04` | 4 | Graphic quality level 4. | | `Level05` | 5 | Graphic quality level 5. | | `Level06` | 6 | Graphic quality level 6. | | `Level07` | 7 | Graphic quality level 7. | | `Level08` | 8 | Graphic quality level 8. | | `Level09` | 9 | Graphic quality level 9. | | `Level10` | 10 | Graphic quality level 10. | | `Level11` | 11 | Graphic quality level 11. | | `Level12` | 12 | Graphic quality level 12. | | `Level13` | 13 | Graphic quality level 13. | | `Level14` | 14 | Graphic quality level 14. | | `Level15` | 15 | Graphic quality level 15. | | `Level16` | 16 | Graphic quality level 16. | | `Level17` | 17 | Graphic quality level 17. | | `Level18` | 18 | Graphic quality level 18. | | `Level19` | 19 | Graphic quality level 19. | | `Level20` | 20 | Graphic quality level 20. | | `Level21` | 21 | Graphic quality level 21, the highest quality level. | --- name: R15CollisionType last_updated: 2026-06-29T19:34:08Z type: enum summary: "Collision behavior type for a R15 character." --- # R15CollisionType Collision behavior type for a R15 character. **Type:** enum ## Description Internal enum used to save the collision behavior setting for R15 characters. ## Items | Name | Value | Description | |------|-------|-------------| | `OuterBox` | 0 | Dynamically sized collision boxes based on mesh sizes. | | `InnerBox` | 1 | Fixed size collision boxes, similar to the classic avatar collision. | --- name: RaycastFilterType last_updated: 2026-06-29T19:34:08Z type: enum summary: "Used in a RaycastParams object to determine how its FilterDescendantsInstances list will be used." --- # RaycastFilterType Used in a [RaycastParams](/docs/reference/engine/datatypes/RaycastParams.md) object to determine how its [FilterDescendantsInstances](/docs/reference/engine/datatypes/RaycastParams.md) list will be used. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Exclude` | 0 | Every [BasePart](/docs/reference/engine/classes/BasePart.md) in the raycast operation will be considered **except** those that are descendants of objects in the filter list. | | `Include` | 1 | Only [BaseParts](/docs/reference/engine/classes/BasePart.md) which are descendants of objects in the filter list will be considered in the raycast operation. | --- name: ReadCapturesFromGalleryResult last_updated: 2026-06-29T19:34:08Z type: enum --- # ReadCapturesFromGalleryResult **Type:** enum ## Description Represents the result of reading captures from a user's gallery. ## Items | Name | Value | Description | |------|-------|-------------| | `Success` | 0 | | | `NeedPermission` | 1 | | --- name: ReceiptDecision last_updated: 2026-06-29T19:34:08Z type: enum summary: "`ReceiptDecision` works with MarketplaceService to indicate the acknowledgement of receipts." --- # ReceiptDecision `ReceiptDecision` works with [MarketplaceService](/docs/reference/engine/classes/MarketplaceService.md) to indicate the acknowledgement of receipts. **Type:** enum ## Description `ReceiptDecision` is the required return type for callbacks registered with [MarketplaceService:BindReceiptHandler()](/docs/reference/engine/classes/MarketplaceService.md). The value you return determines whether the receipt is marked as complete or remains unresolved for future processing. ## Items | Name | Value | Description | |------|-------|-------------| | `NotProcessedYet` | 0 | The receipt has not been processed yet. Returning this value keeps the receipt unresolved so it will be redelivered to the user's handler later, either during their existing session or the next time they join a server if they have disconnected. | | `Processed` | 1 | The receipt has been processed and all benefits have been granted. The receipt is marked as complete and will not be delivered again. | --- name: ReceiptType last_updated: 2026-06-29T19:34:08Z type: enum summary: "`ReceiptType` is used to work with server-sided receipt processing." --- # ReceiptType `ReceiptType` is used to work with server-sided receipt processing. **Type:** enum ## Description `ReceiptType` works with [MarketplaceService:BindReceiptHandler()](/docs/reference/engine/classes/MarketplaceService.md) to indicate the different type of receipts a developer can bind to for processing. When you call [BindReceiptHandler](/docs/reference/engine/classes/MarketplaceService.md), you pass a `ReceiptType` to specify which kind of receipt your handler should process. For Robux transfers initiated with [PromptRobuxTransferAsync](/docs/reference/engine/classes/MarketplaceService.md), two receipts are generated: one for the sender (`RobuxTransferSender`) and one for the receiver (`RobuxTransferReceiver`). You should register handlers for both types to fully process a transfer. Transfer receipts are delivered to whichever server the user is currently in once the transfer settles (the user does not need to rejoin). If the user is offline when the transfer settles, the receipt is delivered the next time they join a server. See [BindReceiptHandler](/docs/reference/engine/classes/MarketplaceService.md) for details. ## Items | Name | Value | Description | |------|-------|-------------| | `DeveloperProduct` | 0 | | | `RobuxTransferSender` | 1 | Used for processing receipts for the user who initiated a Robux transfer. The receipt's `PlayerId` is the sender's user ID and includes a `TransferRequestId` field. Delivered immediately if the transfer settles synchronously. If receiver approval is required, delivered to the server the sender is currently in once the receiver accepts, or on the sender's next session join if they are offline. | | `RobuxTransferReceiver` | 2 | Used for processing receipts for the user who received Robux via a transfer. The receipt's `PlayerId` is the receiver's user ID and includes a `TransferRequestId` field. Delivered to the server the receiver is currently in once the transfer settles, or on their next session join if they are offline. | --- name: RecommendationActionType last_updated: 2026-06-29T19:34:08Z type: enum --- # RecommendationActionType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `AddReaction` | 0 | The user added a reaction to the item, such as a "like". | | `RemoveReaction` | 1 | The user removed a reaction from the item. | | `Share` | 2 | The user shared the item. | | `Report` | 3 | The user created an abuse report of the item. | | `Comment` | 4 | The user commented on the item. | | `Play` | 5 | The user joined an experience through the item, or played a mini-game associated with the item. | | `Purchase` | 6 | The user made a purchase from the item. | --- name: RecommendationDepartureIntent last_updated: 2026-06-29T19:34:08Z type: enum --- # RecommendationDepartureIntent **Type:** enum ## Description This enum captures the user's intent when leaving a view—whether the experience was neutral, positive, or negative. While longer views can sometimes suggest higher engagement, duration alone isn't a reliable signal. For example, a user might quickly see what they need and immediately teleport through the item. Even though the view time is short, the swift, purposeful action indicates a positive departure intent. ## Items | Name | Value | Description | |------|-------|-------------| | `Neutral` | 0 | | | `Positive` | 1 | | | `Negative` | 2 | | --- name: RecommendationImpressionType last_updated: 2026-06-29T19:34:08Z type: enum --- # RecommendationImpressionType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `View` | 0 | A complete view of the item. | | `NotViewable` | 1 | The item has failed to render. | --- name: RecommendationItemContentType last_updated: 2026-06-29T19:34:08Z type: enum --- # RecommendationItemContentType **Type:** enum ## Description This enum describes the content type of the recommendation item. It is designed to be generic. ## Items | Name | Value | Description | |------|-------|-------------| | `Static` | 0 | The item is static, such as an image. | | `Dynamic` | 1 | The item is dynamic; for example, it is a video. | | `Interactive` | 2 | The item is interactive, such as a 3D model or a mini-game. | --- name: RecommendationItemVisibility last_updated: 2026-06-29T19:34:08Z type: enum --- # RecommendationItemVisibility **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Private` | 0 | | | `Public` | 1 | | --- name: RecommendationPreferenceTargetType last_updated: 2026-06-29T19:34:08Z type: enum --- # RecommendationPreferenceTargetType **Type:** enum ## Description The kind of entity a preference signal targets when logged via [LogPreferenceEvent](/docs/reference/engine/classes/RecommendationService.md). Determines how the `targetId` parameter is interpreted. ## Items | Name | Value | Description | |------|-------|-------------| | `User` | 0 | The target is another Roblox user. The `targetId` is the user key. | | `Universe` | 1 | The target is a universe. The `targetId` is the universe ID as a string. | | `CustomTag` | 2 | The target is a custom content tag. The `targetId` is the tag string. | --- name: RecommendationPreferenceType last_updated: 2026-06-29T19:34:08Z type: enum --- # RecommendationPreferenceType **Type:** enum ## Description The type of preference signal logged via [LogPreferenceEvent](/docs/reference/engine/classes/RecommendationService.md). Preference signals capture follow, unfollow, mute, and unmute actions toward another user, a universe, or a custom content tag, and are used by the recommendation engine to personalize future results. ## Items | Name | Value | Description | |------|-------|-------------| | `AddFollow` | 0 | The user started following the target. | | `RemoveFollow` | 1 | The user stopped following the target. | | `AddMute` | 2 | The user muted the target. | | `RemoveMute` | 3 | The user unmuted a previously muted target. | --- name: RejectCharacterDeletions last_updated: 2026-06-29T19:34:08Z type: enum summary: "Controls whether the server rejects client attempts to delete player characters." --- # RejectCharacterDeletions Controls whether the server rejects client attempts to delete player characters. **Type:** enum ## Description Determines whether the server rejects client replication requests that would delete player character models from the workspace. This enum is used by [Workspace.RejectCharacterDeletions](/docs/reference/engine/classes/Workspace.md). ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | Uses the engine-default behavior (currently equivalent to `Enabled`). | | `Disabled` | 1 | The server does not reject character deletion requests from clients. | | `Enabled` | 2 | The server rejects client attempts to delete player characters. | --- name: RenderFidelity last_updated: 2026-06-29T19:34:08Z type: enum summary: "Determines the level of detail that solid modeled and mesh parts will be shown in." --- # RenderFidelity Determines the level of detail that solid modeled and mesh parts will be shown in. **Type:** enum ## Description This enum determines the level of detail that [meshes](/docs/en-us/parts/meshes.md) and [solid modeled](/docs/en-us/parts/solid-modeling.md) parts will be shown in. The default is **Automatic**, meaning the object's detail is based on its distance from the camera as outlined in the following table. | Distance From Camera | Render Fidelity | | --- | --- | | Less than 250 studs | Highest | | 250 to 500 studs | Medium | | 500 or more studs | Lowest | ## Items | Name | Value | Description | |------|-------|-------------| | `Automatic` | 0 | Object's level of detail is dynamically controlled by its distance from the camera (see table above). | | `Precise` | 1 | Object is rendered in the highest fidelity regardless of its distance from the camera. | | `Performance` | 2 | Push performance as much as possible, trying to maintain quality if possible, and discarding appearance if that's necessary to reach performance. This means that the performance will always be excellent, but mesh visuals may be affected negatively. | --- name: RenderPriority last_updated: 2026-06-29T19:34:08Z type: enum summary: "A list of standard reserved values in BindToRenderStep." --- # RenderPriority A list of standard reserved values in BindToRenderStep. **Type:** enum ## Description A list of standard reserved values in BindToRenderStep. See [RunService:BindToRenderStep()](/docs/reference/engine/classes/RunService.md) for the proper usage, as the enum itself isn't used. ## Items | Name | Value | Description | |------|-------|-------------| | `First` | 0 | This should run first. | | `Input` | 100 | This should run as second. | | `Camera` | 200 | This should run after Input. | | `Character` | 300 | This should run after Camera. | | `Last` | 2000 | This should run as last, after Character. | --- name: RenderingCacheOptimizationMode last_updated: 2026-06-29T19:34:08Z type: enum summary: "Controls rendering cache optimization behavior." --- # RenderingCacheOptimizationMode Controls rendering cache optimization behavior. **Type:** enum ## Description Determines whether the renderer caches per-object rendering state to avoid redundant per-frame work. This enum is used by [Workspace.RenderingCacheOptimizations](/docs/reference/engine/classes/Workspace.md). ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | Uses the engine-default rendering cache state. | | `Disabled` | 1 | Rendering cache optimizations are disabled. | | `Enabled` | 2 | Rendering cache optimizations are enabled. | --- name: RenderingTestComparisonMethod last_updated: 2026-06-29T19:34:08Z type: enum --- # RenderingTestComparisonMethod **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `psnr` | 0 | | | `diff` | 1 | | --- name: ReplicateInstanceDestroySetting last_updated: 2026-06-29T19:34:08Z type: enum summary: "Controls how `Instance:Destroy()` calls are replicated from server to clients." --- # ReplicateInstanceDestroySetting Controls how `Instance:Destroy()` calls are replicated from server to clients. **Type:** enum ## Description Determines the replication behavior the server uses when `Instance:Destroy()` is called. This enum is used by [Workspace.ReplicateInstanceDestroySetting](/docs/reference/engine/classes/Workspace.md). ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | Uses the engine-default instance destroy replication behavior. | | `Disabled` | 1 | Uses legacy instance destroy replication behavior. | | `Enabled` | 2 | Uses improved instance destroy replication behavior. | --- name: ResamplerMode last_updated: 2026-06-29T19:34:08Z type: enum summary: "Determines the image filtering used." --- # ResamplerMode Determines the image filtering used. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | Bilinear filtering of the four near pixels in the image. | | `Pixelated` | 1 | Nearest neighbor filtering of the closest pixel in the image. | --- name: ReservedHighlightId last_updated: 2026-06-29T19:34:08Z type: enum --- # ReservedHighlightId **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Standard` | 0 | | | `Active` | 131072 | | | `Hover` | 262144 | | | `Selection` | 524288 | | | `NegatedPart` | 1048576 | | --- name: RestPose last_updated: 2026-06-29T19:34:08Z type: enum --- # RestPose **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | | | `RotationsReset` | 1 | | | `Custom` | 2 | | --- name: ReturnKeyType last_updated: 2026-06-29T19:34:08Z type: enum --- # ReturnKeyType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | | | `Done` | 1 | | | `Go` | 2 | | | `Next` | 3 | | | `Search` | 4 | | | `Send` | 5 | | --- name: ReverbType last_updated: 2026-06-29T19:34:08Z type: enum summary: "The ReverbType Enum allows you to make audio in your game sound different, depending on what \"area\" the sounds are in." --- # ReverbType The ReverbType Enum allows you to make audio in your game sound different, depending on what "area" the sounds are in. **Type:** enum ## Description The ReverbType Enum allows you to make audio in your game sound different, depending on what "area" the sounds are in. Note that some of these do not appear to have any effect on the sound. ## Items | Name | Value | Description | |------|-------|-------------| | `NoReverb` | 0 | No sound reverb. Audio is not changed from default. | | `GenericReverb` | 1 | Sound reverb is changed to a generic reverb effect. | | `PaddedCell` | 2 | Sound reverb is changed to sound like the player is in a padded cell. | | `Room` | 3 | Sound reverb is changed to sound like the player is in a room. | | `Bathroom` | 4 | Sound reverb is changed to sound like the player is in a bathroom. | | `LivingRoom` | 5 | Sound reverb is changed to sound like the player is in a living room. | | `StoneRoom` | 6 | Sound reverb is changed to sound like the player is in a stone room. | | `Auditorium` | 7 | Sound reverb is changed to sound like the player is in an auditorium. | | `ConcertHall` | 8 | Sound reverb is changed to sound like the player is in a concert hall. | | `Cave` | 9 | Sound reverb is changed to sound like the player is in a cave. | | `Arena` | 10 | Sound reverb is changed to sound like the player is in an arena. | | `Hangar` | 11 | Sound reverb is changed to sound like the player is in a hangar. | | `CarpettedHallway` | 12 | Sound reverb is changed to sound like the player is in a carpeted hallway. | | `Hallway` | 13 | Sound reverb is changed to sound like the player is in a hallway. | | `StoneCorridor` | 14 | Sound reverb is changed to sound like the player is in a stone corridor. | | `Alley` | 15 | Sound reverb is changed to sound like the player is in an alley. | | `Forest` | 16 | Sound reverb is changed to sound like the player is in a forest. | | `City` | 17 | Sound reverb is changed to sound like the player is in a city. | | `Mountains` | 18 | Sound reverb is changed to sound like the player is in the mountains. | | `Quarry` | 19 | Sound reverb is changed to sound like the player is in a quarry. | | `Plain` | 20 | Sound reverb is changed to sound like the player is an open plain. | | `ParkingLot` | 21 | Sound reverb is changed to sound like the player is in a parking lot. | | `SewerPipe` | 22 | Sound reverb is changed to sound like the player is in a sewer pipe. | | `UnderWater` | 23 | Sound reverb is changed to sound like the player is underwater. | --- name: RibbonTool last_updated: 2026-06-29T19:34:09Z type: enum --- # RibbonTool **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Select` | 0 | | | `Scale` | 1 | | | `Rotate` | 2 | | | `Move` | 3 | | | `Transform` | 4 | | | `ColorPicker` | 5 | | | `MaterialPicker` | 6 | Deprecated in favor of Material Manager. Don't use. | | `Group` | 7 | | | `Ungroup` | 8 | | | `None` | 9 | | | `PivotEditor` | 10 | | --- name: RigScale last_updated: 2026-06-29T19:34:09Z type: enum --- # RigScale **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | | | `Rthro` | 1 | | | `RthroNarrow` | 2 | | --- name: RigType last_updated: 2026-06-29T19:34:09Z type: enum summary: "The type of rig being imported with the 3D Importer." --- # RigType The type of rig being imported with the 3D Importer. **Type:** enum ## Description The type of rig being imported with the 3D Importer ## Items | Name | Value | Description | |------|-------|-------------| | `R15` | 0 | A rig type of R15. | | `CustomHumanoid` | 1 | | | `Custom` | 2 | A rig type of Custom. | | `None` | 3 | A rig type of None. | --- name: RollOffMode last_updated: 2026-06-29T19:34:09Z type: enum summary: "How Sounds parented to a BasePart or Attachment attenuate (fade out) as the distance between the listener and the parent increases." --- # RollOffMode How [Sounds](/docs/reference/engine/classes/Sound.md) parented to a [BasePart](/docs/reference/engine/classes/BasePart.md) or [Attachment](/docs/reference/engine/classes/Attachment.md) attenuate (fade out) as the distance between the listener and the parent increases. **Type:** enum ## Description Enum which determines how [Sounds](/docs/reference/engine/classes/Sound.md) parented to a [BasePart](/docs/reference/engine/classes/BasePart.md) or [Attachment](/docs/reference/engine/classes/Attachment.md) attenuate (fade out) as the distance between the listener and the parent increases. ## Items | Name | Value | Description | |------|-------|-------------| | `Inverse` | 0 | Volume attenuates from [Sound.RollOffMinDistance](/docs/reference/engine/classes/Sound.md) in an inverse manner, mirroring how sounds attenuate in the real world. This is done through [Sound.RollOffMinDistance](/docs/reference/engine/classes/Sound.md)/`distance`, where `distance` is the [Vector3.Magnitude](/docs/reference/engine/datatypes/Vector3.md) between the audio source and the audio listener. | | `Linear` | 1 | Volume attenuates between [Sound.RollOffMinDistance](/docs/reference/engine/classes/Sound.md) and [Sound.RollOffMaxDistance](/docs/reference/engine/classes/Sound.md) with a linear relationship. This is done through ([Sound.RollOffMaxDistance](/docs/reference/engine/classes/Sound.md)/`distance`)/([Sound.RollOffMaxDistance](/docs/reference/engine/classes/Sound.md)-[Sound.RollOffMinDistance](/docs/reference/engine/classes/Sound.md)), where `distance` is the [Vector3.Magnitude](/docs/reference/engine/datatypes/Vector3.md) between the audio source and the audio listener. | | `LinearSquare` | 2 | Volume attenuates between [Sound.RollOffMinDistance](/docs/reference/engine/classes/Sound.md) and [Sound.RollOffMaxDistance](/docs/reference/engine/classes/Sound.md) with a linear squared relationship. This is done through squaring `Linear`. | | `InverseTapered` | 3 | A hybrid model which follows the `Inverse` model when close to [Sound.RollOffMinDistance](/docs/reference/engine/classes/Sound.md) and the `LinearSquare` model when close to [Sound.RollOffMaxDistance](/docs/reference/engine/classes/Sound.md). This is done by taking the lesser of `Inverse` and `LinearSquare`. | --- name: RolloutState last_updated: 2026-06-29T19:34:09Z type: enum summary: "A three-phase rollout state used to opt in or out of engine features." --- # RolloutState A three-phase rollout state used to opt in or out of engine features. **Type:** enum ## Description Several [Workspace](/docs/reference/engine/classes/Workspace.md) properties use `RolloutState` to support a three-phase rollout pattern, where a feature is initially opt-in (`Default` equals disabled), then opt-out (`Default` equals enabled), and finally always on. `Default` always tracks the current engine-wide phase. ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | Uses the engine-wide rollout default, which changes as the feature progresses through its rollout phases. | | `Disabled` | 1 | Opts out of the feature regardless of the engine-wide rollout phase. | | `Enabled` | 2 | Opts in to the feature regardless of the engine-wide rollout phase. | --- name: RotationOrder last_updated: 2026-06-29T19:34:09Z type: enum summary: "The order of rotation axes used for Euler angles encoding of rotations." --- # RotationOrder The order of rotation axes used for Euler angles encoding of rotations. **Type:** enum ## Description Euler angles encode a rotation in 3D space via a sequence of three rotations along the X, Y, and Z axes. The `RotationOrder` enum specifies the order in which the engine performs these rotations. To help visualize the many rotation orders, you can test them manually in Studio with the [Rotate](/docs/en-us/parts.md#rotate) tool or by inserting [task.wait()](/docs/reference/engine/globals/task.md) statements between individual rotations of a cube with a unique face: ```lua local Workspace = game:GetService("Workspace") local cube = Workspace.Cube local rx, ry, rz = math.rad(90), math.rad(90), math.rad(90) task.wait(5) cube.CFrame *= CFrame.fromEulerAngles(rx, 0, 0) -- X task.wait(5) cube.CFrame *= CFrame.fromEulerAngles(0, ry, 0) -- Y task.wait(5) cube.CFrame *= CFrame.fromEulerAngles(0, 0, rz) -- Z ``` An equivalent operation is: ```lua local Workspace = game:GetService("Workspace") local cube = Workspace.Cube local rx, ry, rz = math.rad(90), math.rad(90), math.rad(90) cube.CFrame = CFrame.fromEulerAngles(rx, ry, rz, Enum.RotationOrder.XYZ) ``` ## Items | Name | Value | Description | |------|-------|-------------| | `XYZ` | 0 | X, Y, Z order. | | `XZY` | 1 | X, Z, Y order. | | `YZX` | 2 | Y, Z, X order. | | `YXZ` | 3 | Y, X, Z order. | | `ZXY` | 4 | Z, X, Y order. | | `ZYX` | 5 | Z, Y, X order. | --- name: RotationType last_updated: 2026-06-29T19:34:09Z type: enum --- # RotationType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `MovementRelative` | 0 | | | `CameraRelative` | 1 | | --- name: RsvpStatus last_updated: 2026-06-29T19:34:09Z type: enum --- # RsvpStatus **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `None` | 0 | | | `Going` | 1 | | | `NotGoing` | 2 | | --- name: RtlTextSupport last_updated: 2026-06-29T19:34:09Z type: enum --- # RtlTextSupport **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | | | `Disabled` | 1 | | | `Enabled` | 2 | | --- name: RunContext last_updated: 2026-06-29T19:34:09Z type: enum --- # RunContext **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Legacy` | 0 | Runs in legacy script containers dependent on the type of script uses such as [LocalScript](/docs/reference/engine/classes/LocalScript.md) or [Script](/docs/reference/engine/classes/Script.md). | | `Server` | 1 | Runs on the server. | | `Client` | 2 | Runs on the client. | | `Plugin` | 3 | Runs as a descendant of [Plugin](/docs/reference/engine/classes/Plugin.md) instances. | --- name: RunState last_updated: 2026-06-29T19:34:09Z type: enum --- # RunState **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Stopped` | 0 | | | `Running` | 1 | | | `Paused` | 2 | | --- name: RuntimeUndoBehavior last_updated: 2026-06-29T19:34:09Z type: enum --- # RuntimeUndoBehavior **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Aggregate` | 0 | | | `Snapshot` | 1 | | | `Hybrid` | 2 | | --- name: SafeAreaCompatibility last_updated: 2026-06-29T19:34:09Z type: enum summary: "Describes how descendants of a ScreenGui adapt to screens with cutouts." --- # SafeAreaCompatibility Describes how descendants of a [ScreenGui](/docs/reference/engine/classes/ScreenGui.md) adapt to screens with cutouts. **Type:** enum ## Description This enum describes the automatic compatibility transformations that apply to descendant "fullscreen" [GuiObjects](/docs/reference/engine/classes/GuiObject.md) of a [ScreenGui](/docs/reference/engine/classes/ScreenGui.md) on displays with screen cutouts. ## Items | Name | Value | Description | |------|-------|-------------| | `None` | 0 | Do not apply compatibility transformations to any descendants of the [ScreenGui](/docs/reference/engine/classes/ScreenGui.md). | | `FullscreenExtension` | 1 | If the total area of any descendant [GuiObject](/docs/reference/engine/classes/GuiObject.md) within the [ScreenGui](/docs/reference/engine/classes/ScreenGui.md) (including any applied border or [UIStroke](/docs/reference/engine/classes/UIStroke.md)) covers the device's safe area both horizontally and vertically, its background area will be expanded to cover the fullscreen area. This expansion does **not** affect the size or position of the descendant's **content**, except in the case of [ImageLabel](/docs/reference/engine/classes/ImageLabel.md), [ImageButton](/docs/reference/engine/classes/ImageButton.md), or [VideoFrame](/docs/reference/engine/classes/VideoFrame.md) where the image/video is considered part of the background and will be expanded to fullscreen. Note that this option is intended to automatically improve the appearance of UI that was authored for screens without any cutouts. However, it's recommended that you avoid fullscreen extensions for new work; instead, use the [ScreenInsets](/docs/reference/engine/classes/ScreenGui.md) property to specify which insets should be respected for different [ScreenGuis](/docs/reference/engine/classes/ScreenGui.md). | --- name: SalesTypeFilter last_updated: 2026-06-29T19:34:09Z type: enum --- # SalesTypeFilter **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `All` | 1 | | | `Collectibles` | 2 | | | `Premium` | 3 | | | `TimedOptions` | 4 | | --- name: SandboxedInstanceMode last_updated: 2026-06-29T19:34:09Z type: enum summary: "Controls whether sandboxed instance mode (script capabilities) is enabled." --- # SandboxedInstanceMode Controls whether sandboxed instance mode (script capabilities) is enabled. **Type:** enum ## Description Determines whether sandboxed instance mode, which lets you use script capabilities, is active. This enum is used by [Workspace.SandboxedInstanceMode](/docs/reference/engine/classes/Workspace.md). ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | Uses the engine-default sandboxed instance state. | | `Experimental` | 1 | Sandboxed instance mode is enabled. | --- name: SaveAvatarThumbnailCustomizationFailure last_updated: 2026-06-29T19:34:09Z type: enum --- # SaveAvatarThumbnailCustomizationFailure **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `BadThumbnailType` | 1 | | | `BadYRotDeg` | 2 | | | `BadFieldOfViewDeg` | 3 | | | `BadDistanceScale` | 4 | | | `Other` | 5 | | | `Throttled` | 6 | | --- name: SaveFilter last_updated: 2026-06-29T19:34:09Z type: enum tags: - Deprecated summary: "Used by DataModel.SavePlace to determine the type of save operation This enum determines which aspects of the current place are saved, based on the following options." --- # SaveFilter Used by [DataModel.SavePlace](/docs/reference/engine/classes/DataModel.md) to determine the type of save operation This enum determines which aspects of the current place are saved, based on the following options. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `SaveWorld` | 0 | Saves only the world-related data. | | `SaveGame` | 1 | Saves only the game-related data. | | `SaveAll` | 2 | Saves all data, including both world and game-related information. | --- name: SavedQualitySetting last_updated: 2026-06-29T19:34:09Z type: enum --- # SavedQualitySetting **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Automatic` | 0 | | | `QualityLevel1` | 1 | | | `QualityLevel2` | 2 | | | `QualityLevel3` | 3 | | | `QualityLevel4` | 4 | | | `QualityLevel5` | 5 | | | `QualityLevel6` | 6 | | | `QualityLevel7` | 7 | | | `QualityLevel8` | 8 | | | `QualityLevel9` | 9 | | | `QualityLevel10` | 10 | | --- name: ScaleType last_updated: 2026-06-29T19:34:09Z type: enum summary: "Determines how an image (of a ImageLabel or ImageButton) is scaled." --- # ScaleType Determines how an image (of a [ImageLabel](/docs/reference/engine/classes/ImageLabel.md) or [ImageButton](/docs/reference/engine/classes/ImageButton.md)) is scaled. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Stretch` | 0 | The image is stretched to fit within the element. | | `Slice` | 1 | 9-Slice scaling: slice the image into 9 regions and apply different scaling rules to each region. The slice boundaries are determined by [ImageLabel.SliceCenter](/docs/reference/engine/classes/ImageLabel.md) or [ImageButton.SliceCenter](/docs/reference/engine/classes/ImageButton.md). See [UI 9-Slice Design](/docs/en-us/ui/9-slice.md) for more information. . | | `Tile` | 2 | The image is tiled to fit within the element. For example, if the element is twice the X dimension of the image, the image will appear twice (2 tiles). | | `Fit` | 3 | The image is scaled fit within the element X or Y dimension (whichever fits first). | | `Crop` | 4 | The image is cropped to fit within the element. | --- name: ScopeCheckResult last_updated: 2026-06-29T19:34:09Z type: enum --- # ScopeCheckResult **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `ConsentAccepted` | 0 | | | `InvalidScopes` | 1 | | | `Timeout` | 2 | | | `NoUserInput` | 3 | | | `BackendError` | 4 | | | `UnexpectedError` | 5 | | | `InvalidArgument` | 6 | | | `ConsentDenied` | 7 | | --- name: ScreenInsets last_updated: 2026-06-29T19:34:09Z type: enum summary: "Insets associated with various screen safe areas." --- # ScreenInsets Insets associated with various screen safe areas. **Type:** enum ## Description This enum specifies screen edge insets that are associated with a particular screen safe area. The insets are relative to the **fullscreen area**, meaning the rectangular region that encompasses all visible pixels on the screen. ![Mobile device screen showing inset regions.](../../../assets/engine-api/enums/ScreenInsets/Inset-Regions-All.png) ## Items | Name | Value | Description | |------|-------|-------------| | `None` | 0 | No insets are added to the fullscreen area. This mode may result in UI that is obscured or completely hidden by device notches and cutouts, so you should only use it for a [ScreenGui](/docs/reference/engine/classes/ScreenGui.md) that contains non‑interactive content like background images. | | `DeviceSafeInsets` | 1 | Device safe area insets are added to the fullscreen area. The resulting area is guaranteed to not be occluded by any **device** screen cutouts such as the camera notch, although no inset is added for Roblox core UI elements like the top bar buttons. As a result, it's recommended that you use `CoreUISafeInsets` for a [ScreenGui](/docs/reference/engine/classes/ScreenGui.md) whose contents should remain unobscured by both device cutouts **and** core screen elements. | | `CoreUISafeInsets` | 2 | Core UI insets are added to the `DeviceSafeInsets` area, resulting in an area guaranteed to be unobscured by both device screen cutouts and Roblox core UI elements like the top bar buttons. This mode is recommended for any [ScreenGui](/docs/reference/engine/classes/ScreenGui.md) that contains interactive and/or important UI elements such as buttons and status messages. | | `TopbarSafeInsets` | 3 | Top bar safe area insets are added to the `DeviceSafeInsets` area, resulting in an area guaranteed to be unobscured by both device screen cutouts and Roblox core UI elements like the experience controls. Unlike `CoreUISafeInsets`, this area's position and size is dynamic and is limited to the space available within the top bar area itself, outside of `CoreUISafeInsets`. See the [GuiService.TopbarInset](/docs/reference/engine/classes/GuiService.md) property which represents the absolute size and position of the unobstructed area within the top bar space. | --- name: ScreenOrientation last_updated: 2026-06-29T19:34:09Z type: enum summary: "The preference of in-game screen orientation on hand-held devices." --- # ScreenOrientation The preference of in-game screen orientation on hand-held devices. **Type:** enum ## Description Indicates what orientation the game prefers to be played on mobile devices. ## Items | Name | Value | Description | |------|-------|-------------| | `LandscapeLeft` | 0 | A preference where the game is displayed landscape (horizontally) with the bottom of the device on the left side of the screen. | | `LandscapeRight` | 1 | A preference where the game is displayed landscape (horizontally) with the bottom of the device on the right side of the screen. | | `LandscapeSensor` | 2 | A preference where the game is displayed landscape (horizontally) with the bottom of the device on the left of right side of the screen depending on the device's orientation (the sensor determines which side allows for the game to be displayed right-side-up). | | `Portrait` | 3 | A preference where the game is displayed portrait (vertically) with the bottom of the device on the bottom of the screen. | | `Sensor` | 4 | A preference where the game is displayed depending on the best match to the device's current orientation - either landscape (left/right) or portrait. | --- name: ScriptStoppedReason last_updated: 2026-06-29T19:34:09Z type: enum summary: "Describes why the debugger paused execution." --- # ScriptStoppedReason Describes why the debugger paused execution. **Type:** enum ## Description Passed in the [ScriptDebugStopped](/docs/reference/engine/datatypes/ScriptDebugStopped.md) payload to the [ScriptDebuggerService.OnStopped](/docs/reference/engine/classes/ScriptDebuggerService.md) callback, indicating what triggered the pause. ## Items | Name | Value | Description | |------|-------|-------------| | `Breakpoint` | 0 | Execution stopped because a user-defined breakpoint was hit. | | `Exception` | 1 | Execution stopped because an exception was thrown (governed by the current [DebugBreakModeType](/docs/reference/engine/enums/DebugBreakModeType.md)). | | `Pause` | 2 | Execution stopped because [ScriptDebuggerService:Pause()](/docs/reference/engine/classes/ScriptDebuggerService.md) was called. | | `Step` | 3 | Execution stopped after completing a step operation. | | `Entry` | 4 | Execution stopped at a script's entry point. | --- name: ScriptVariableScope last_updated: 2026-06-29T19:34:09Z type: enum summary: "Indicates the scope category of a variable returned by the debugger." --- # ScriptVariableScope Indicates the scope category of a variable returned by the debugger. **Type:** enum ## Description Used in [ScriptVariable](/docs/reference/engine/datatypes/ScriptVariable.md) to classify the scope of a variable returned by [ScriptDebuggerService:GetRootVariables()](/docs/reference/engine/classes/ScriptDebuggerService.md) or [ScriptDebuggerService:GetVariables()](/docs/reference/engine/classes/ScriptDebuggerService.md). Children of a variable inherit the parent's scope. ## Items | Name | Value | Description | |------|-------|-------------| | `Local` | 0 | A local variable declared in the current function scope. | | `Upvalue` | 1 | A variable captured from an enclosing function scope. | | `Global` | 2 | A global variable. | --- name: ScrollBarInset last_updated: 2026-06-29T19:34:09Z type: enum summary: "This enum is used with ScrollingFrame.HorizontalScrollBarInset and ScrollingFrame.VerticalScrollBarInset to indicate whether the canvas should be inset by ScrollBarThickness for the respective scroll bar." --- # ScrollBarInset This enum is used with [ScrollingFrame.HorizontalScrollBarInset](/docs/reference/engine/classes/ScrollingFrame.md) and [ScrollingFrame.VerticalScrollBarInset](/docs/reference/engine/classes/ScrollingFrame.md) to indicate whether the canvas should be inset by [ScrollBarThickness](/docs/reference/engine/classes/ScrollingFrame.md) for the respective scroll bar. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `None` | 0 | The canvas will never be inset for the respective scroll bar. | | `ScrollBar` | 1 | The canvas will only be inset if the respective scroll bar is showing. | | `Always` | 2 | The canvas will always be inset for the respective scroll bar, regardless of whether that scroll bar is showing. | --- name: ScrollingDirection last_updated: 2026-06-29T19:34:09Z type: enum summary: "This enum is used by ScrollingFrame.ScrollingDirection to specify the direction(s) in which scrolling is allowed." --- # ScrollingDirection This enum is used by [ScrollingFrame.ScrollingDirection](/docs/reference/engine/classes/ScrollingFrame.md) to specify the direction(s) in which scrolling is allowed. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `X` | 1 | The canvas can only be scrolled along the **X** axis. | | `Y` | 2 | The canvas can only be scrolled along the **Y** axis. | | `XY` | 4 | The canvas can be scrolled along both the **X** and **Y** axes. | --- name: SecurityCapability last_updated: 2026-06-29T19:34:09Z type: enum --- # SecurityCapability **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `RunClientScript` | 0 | | | `RunServerScript` | 1 | | | `AccessOutsideWrite` | 2 | | | `AssetRequire` | 3 | | | `LoadString` | 4 | | | `ScriptGlobals` | 5 | | | `CreateInstances` | 6 | | | `Basic` | 7 | | | `Audio` | 8 | | | `DataStore` | 9 | | | `Network` | 10 | | | `Physics` | 11 | | | `UI` | 12 | | | `CSG` | 13 | | | `Chat` | 14 | | | `Animation` | 15 | | | `Avatar` | 16 | | | `Input` | 17 | | | `Environment` | 18 | | | `RemoteEvent` | 19 | | | `LegacySound` | 20 | | | `Players` | 21 | | | `CapabilityControl` | 22 | | | `Plugin` | 23 | | | `LocalUser` | 24 | | | `WritePlayer` | 25 | | | `RobloxScript` | 26 | | | `RobloxEngine` | 27 | | | `Unassigned` | 28 | | | `InternalTest` | 29 | | | `PluginOrOpenCloud` | 30 | | | `Assistant` | 31 | | | `RemoteCommand` | 32 | | | `AssetRead` | 33 | | | `AssetManagement` | 34 | | | `DynamicGeneration` | 35 | | | `PlatformAvatarEditing` | 36 | | | `AssetCreateUpdate` | 37 | | | `Capture` | 38 | | | `SensitiveInput` | 39 | | | `Monetization` | 40 | | | `LoadOwnedAsset` | 41 | | | `Social` | 42 | | | `ServerCommunication` | 43 | | | `Logging` | 44 | | | `PromptExternalPurchase` | 45 | | | `Groups` | 46 | | | `Teleport` | 47 | | | `Consequences` | 48 | | | `Material` | 49 | | | `AvatarBehavior` | 50 | | | `AvatarAppearance` | 51 | | | `LoadUnownedAsset` | 52 | | --- name: SelectionBehavior last_updated: 2026-06-29T19:34:09Z type: enum summary: "Customization options for gamepad selection when GuiBase2d.SelectionGroup is true." --- # SelectionBehavior Customization options for gamepad selection when [GuiBase2d.SelectionGroup](/docs/reference/engine/classes/GuiBase2d.md) is true. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Escape` | 0 | Prioritizes GuiObjects within the same SelectionGroup but allows the selection to move to other outside GuiObjects if no suitable button is found. | | `Stop` | 1 | Constrains gamepad selection to the SelectionGroup. | --- name: SelectionRenderMode last_updated: 2026-06-29T19:34:09Z type: enum --- # SelectionRenderMode **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Outlines` | 0 | | | `BoundingBoxes` | 1 | | | `Both` | 2 | | --- name: SelfViewPosition last_updated: 2026-06-29T19:34:09Z type: enum summary: "Defines the screen location of the current player's self view." --- # SelfViewPosition Defines the screen location of the current player's self view. **Type:** enum ## Description This enum defines the screen location of the current player's self view, used in conjunction with [SocialService:ShowSelfView()](/docs/reference/engine/classes/SocialService.md) for example. ## Items | Name | Value | Description | |------|-------|-------------| | `LastPosition` | 0 | The position of the self view when it was last closed. | | `TopLeft` | 1 | Top-left corner of the screen, with slight padding from the top and left edges. | | `TopRight` | 2 | Top-right corner of the screen, with slight padding from the top and right edges. | | `BottomLeft` | 3 | Bottom-left corner of the screen, with slight padding from the bottom and left edges. | | `BottomRight` | 4 | Bottom-right corner of the screen, with slight padding from the bottom and right edges. | --- name: SensorMode last_updated: 2026-06-29T19:34:09Z type: enum --- # SensorMode **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Floor` | 0 | | | `Ladder` | 1 | | | `ClassicFloor` | 2 | | | `ClassicLadder` | 3 | | --- name: SensorUpdateType last_updated: 2026-06-29T19:34:09Z type: enum --- # SensorUpdateType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `OnRead` | 0 | | | `Manual` | 1 | | --- name: ServerLiveEditingMode last_updated: 2026-06-29T19:34:09Z type: enum --- # ServerLiveEditingMode **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Uninitialized` | 0 | | | `Enabled` | 1 | | | `Disabled` | 2 | | --- name: ServiceVisibility last_updated: 2026-06-29T19:34:09Z type: enum --- # ServiceVisibility **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Always` | 0 | | | `Off` | 1 | | | `WithChildren` | 2 | | --- name: Severity last_updated: 2026-06-29T19:34:09Z type: enum --- # Severity **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Error` | 1 | | | `Warning` | 2 | | | `Information` | 3 | | | `Hint` | 4 | | --- name: ShowAdResult last_updated: 2026-06-29T19:34:09Z type: enum --- # ShowAdResult **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `ShowCompleted` | 1 | The ad was successfully shown to completion. | | `AdNotReady` | 2 | You are trying to show a full-screen ad before the ad has fully loaded. You might have failed to properly fetch the ad, or the ad might have expired. | | `AdAlreadyShowing` | 3 | You are trying to show a full-screen ad while another full-screen ad is already playing. | | `InternalError` | 4 | An unspecified internal error occurred. Try showing the ad again. | | `ShowInterrupted` | 5 | The ad view of the user has been interrupted. The user might have closed the ad view or left the experience, and should not receive a reward. | | `InsufficientMemory` | 6 | | --- name: SignalBehavior last_updated: 2026-06-29T19:34:09Z type: enum summary: "Determines when the engine resumes event handlers." --- # SignalBehavior Determines when the engine resumes event handlers. **Type:** enum ## Description Determines when the engine resumes event handlers. At some future point, the default mode will be `Deferred` but opt-out will still be possible through use of `Immediate`. For more information, see [Deferred Events](/docs/en-us/scripting/events/deferred.md). ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | The default behavior; currently equivalent to `Immediate` but this will eventually change to `Deferred`. | | `Immediate` | 1 | Event handlers are resumed immediately when the event occurs. | | `Deferred` | 2 | All events are deferred and their handlers resumed at specific resumptions points each frame. | | `AncestryDeferred` | 3 | Equivalent to `Deferred` but only for events triggered by changes in ancestry. | --- name: SizeConstraint last_updated: 2026-06-29T19:34:09Z type: enum summary: "Used to constrain the scale of a GUI object." --- # SizeConstraint Used to constrain the scale of a GUI object. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `RelativeXY` | 0 | The scale of X and Y are independent. | | `RelativeXX` | 1 | The scale of X is shared with Y. | | `RelativeYY` | 2 | The scale of Y is shared with X. | --- name: SolverConvergenceMetricType last_updated: 2026-06-29T19:34:09Z type: enum --- # SolverConvergenceMetricType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `IterationBased` | 0 | | | `AlgorithmAgnostic` | 1 | | --- name: SolverConvergenceVisualizationMode last_updated: 2026-06-29T19:34:09Z type: enum --- # SolverConvergenceVisualizationMode **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Disabled` | 0 | | | `PerIsland` | 1 | | | `PerEdge` | 2 | | --- name: SortDirection last_updated: 2026-06-29T19:34:09Z type: enum --- # SortDirection **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Ascending` | 0 | | | `Descending` | 1 | | --- name: SortOrder last_updated: 2026-06-29T19:34:09Z type: enum summary: "Used by UIGridStyleLayout.SortOrder to order the elements in the layout." --- # SortOrder Used by [UIGridStyleLayout.SortOrder](/docs/reference/engine/classes/UIGridStyleLayout.md) to order the elements in the layout. **Type:** enum ## Description Used by [UIGridStyleLayout.SortOrder](/docs/reference/engine/classes/UIGridStyleLayout.md) to order the elements in the layout. ## Items | Name | Value | Description | |------|-------|-------------| | `Name` | 0 | Elements are ordered by their [Instance.Name](/docs/reference/engine/classes/Instance.md) in alphanumeric order. | | `Custom` | 1 | Elements are ordered by the function passed to [UIGridStyleLayout:SetCustomSortFunction()](/docs/reference/engine/classes/UIGridStyleLayout.md). | | `LayoutOrder` | 2 | Elements are ordered by [GuiObject.LayoutOrder](/docs/reference/engine/classes/GuiObject.md) in ascending order; for example `0` will be placed before `1`. | --- name: SpecialKey last_updated: 2026-06-29T19:34:09Z type: enum --- # SpecialKey **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Insert` | 0 | | | `Home` | 1 | | | `End` | 2 | | | `PageUp` | 3 | | | `PageDown` | 4 | | | `ChatHotkey` | 5 | | --- name: StartCorner last_updated: 2026-06-29T19:34:09Z type: enum summary: "Used by UIGridLayout.StartCorner to decide where the first element is placed." --- # StartCorner Used by [UIGridLayout.StartCorner](/docs/reference/engine/classes/UIGridLayout.md) to decide where the first element is placed. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `TopLeft` | 0 | Grid starts in top left corner. | | `TopRight` | 1 | Grid starts in top right corner. | | `BottomLeft` | 2 | Grid starts in bottom left corner. | | `BottomRight` | 3 | Grid starts in bottom right corner. | --- name: StateObjectFieldType last_updated: 2026-06-29T19:34:09Z type: enum --- # StateObjectFieldType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Boolean` | 0 | | | `CFrame` | 1 | | | `Color3` | 2 | | | `Float` | 3 | | | `Instance` | 4 | | | `Random` | 5 | | | `Vector2` | 6 | | | `Vector3` | 7 | | | `INVALID` | 8 | | --- name: Status last_updated: 2026-06-29T19:34:09Z type: enum tags: - Deprecated summary: "This enum was used with the unfinished Status library." --- # Status This enum was used with the unfinished [Status](/docs/reference/engine/classes/Status.md) library. **Type:** enum ## Description This enum was used with the unfinished [Status](/docs/reference/engine/classes/Status.md) library which would have allowed you to add conditions to a [Humanoid](/docs/reference/engine/classes/Humanoid.md). ## Items | Name | Value | Description | |------|-------|-------------| | `Poison` | 0 | | | `Confusion` | 1 | | --- name: StepFrequency last_updated: 2026-06-29T19:34:09Z type: enum summary: "Enum used with RunService:BindToSimulation() to indicate the frequency at which the bound function is called." --- # StepFrequency Enum used with [RunService:BindToSimulation()](/docs/reference/engine/classes/RunService.md) to indicate the frequency at which the bound function is called. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Hz60` | 0 | The bound function is called at 60 Hz. | | `Hz30` | 1 | The bound function is called at 30 Hz. | | `Hz15` | 2 | The bound function is called at 15 Hz. | | `Hz10` | 3 | The bound function is called at 10 Hz. | | `Hz5` | 4 | The bound function is called at 5 Hz. | | `Hz1` | 5 | The bound function is called at 1 Hz. | --- name: StreamOutBehavior last_updated: 2026-06-29T19:34:09Z type: enum summary: "Determines how content is streamed out from Player clients." --- # StreamOutBehavior Determines how content is streamed out from [Player](/docs/reference/engine/classes/Player.md) clients. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | Default behavior (subject to change). | | `LowMemory` | 1 | Only stream out when memory is low. | | `Opportunistic` | 2 | Stream out content that is significantly outside of the current [Workspace.StreamingTargetRadius](/docs/reference/engine/classes/Workspace.md). | --- name: StreamingIntegrityMode last_updated: 2026-06-29T19:34:09Z type: enum summary: "Determines how a user's client should handle not having enough content streamed in." --- # StreamingIntegrityMode Determines how a user's client should handle not having enough content streamed in. **Type:** enum ## Description This enum is used to control [Workspace.StreamingIntegrityMode](/docs/reference/engine/classes/Workspace.md) behavior. For all modes, the replication focus defaults to be the local character model unless explicitly set via [Player.ReplicationFocus](/docs/reference/engine/classes/Player.md). Note that `MinimumRadiusPause` and `PauseOutsideLoadedArea` both set the [Player.GameplayPaused](/docs/reference/engine/classes/Player.md) property when their respective pause logic is triggered, and a default message is displayed on the client. ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | Default behavior (subject to change). | | `Disabled` | 1 | Simulation of the replication focus is never paused, regardless of the amount of content streamed in on the client. | | `MinimumRadiusPause` | 2 | All client-side simulation is paused when content is not streamed in up to the minimum radius. | | `PauseOutsideLoadedArea` | 3 | Simulation of the replication focus is paused when any part of its bounding box is not in a streamed-in area. | --- name: StreamingPauseMode last_updated: 2026-06-29T19:34:09Z type: enum summary: "Determines how a client should handle not having enough content streamed in to continue playing properly." --- # StreamingPauseMode Determines how a client should handle not having enough content streamed in to continue playing properly. **Type:** enum ## Description This enum is used to control [Workspace.StreamingPauseMode](/docs/reference/engine/classes/Workspace.md) behavior. The `Disabled` mode indicates that gameplay continues unchanged even if player does not have the minimum streaming radius available. In `ClientPhysicsPause` mode, client-side physics is paused when the player doesn't have the minimum radius present and resumed when the minimum radius is available. ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | Default behavior (subject to change). | | `Disabled` | 1 | No change to gameplay due to streaming region availability. | | `ClientPhysicsPause` | 2 | Client owned physics is paused. | --- name: StrokeSizingMode last_updated: 2026-06-29T19:34:09Z type: enum summary: "Used by UIStroke.StrokeSizingMode to determine whether the stroke's Thickness will be measured in pixels or be relative to the parent." --- # StrokeSizingMode Used by [UIStroke.StrokeSizingMode](/docs/reference/engine/classes/UIStroke.md) to determine whether the stroke's [Thickness](/docs/reference/engine/classes/UIStroke.md) will be measured in pixels or be relative to the parent. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `FixedSize` | 0 | [Thickness](/docs/reference/engine/classes/UIStroke.md) is measured in pixels. | | `ScaledSize` | 1 | [Thickness](/docs/reference/engine/classes/UIStroke.md) is relative to minimum parent width or height. If stroke is on text, thickness is relative to font size. | --- name: StudioCaptureBufferStatus last_updated: 2026-06-29T19:34:09Z type: enum --- # StudioCaptureBufferStatus **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `NotStarted` | 0 | | | `Pending` | 1 | | | `Ready` | 2 | | | `Error` | 3 | | --- name: StudioCaptureScreenshotFormat last_updated: 2026-06-29T19:34:09Z type: enum --- # StudioCaptureScreenshotFormat **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `RGBA8` | 0 | | | `PNG` | 1 | | --- name: StudioCloseMode last_updated: 2026-06-29T19:34:09Z type: enum --- # StudioCloseMode **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `None` | 0 | | | `CloseStudio` | 1 | | | `CloseDoc` | 2 | | | `LogOut` | 3 | | --- name: StudioDataModelType last_updated: 2026-06-29T19:34:09Z type: enum --- # StudioDataModelType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Edit` | 0 | | | `PlayClient` | 1 | | | `PlayServer` | 2 | | | `Standalone` | 3 | | | `None` | 4 | | --- name: StudioPlaceUpdateFailureReason last_updated: 2026-06-29T19:34:09Z type: enum --- # StudioPlaceUpdateFailureReason **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Other` | 0 | | | `TeamCreateConflict` | 1 | | --- name: StudioScriptEditorColorCategories last_updated: 2026-06-29T19:34:09Z type: enum --- # StudioScriptEditorColorCategories **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | | | `Operator` | 1 | | | `Number` | 2 | | | `String` | 3 | | | `Comment` | 4 | | | `Keyword` | 5 | | | `Builtin` | 6 | | | `Method` | 7 | | | `Property` | 8 | | | `Nil` | 9 | | | `Bool` | 10 | | | `Function` | 11 | | | `Local` | 12 | | | `Self` | 13 | | | `LuauKeyword` | 14 | | | `FunctionName` | 15 | | | `TODO` | 16 | | | `Background` | 17 | | | `SelectionText` | 18 | | | `SelectionBackground` | 19 | | | `FindSelectionBackground` | 20 | | | `MatchingWordBackground` | 21 | | | `Warning` | 22 | | | `Error` | 23 | | | `Info` | 24 | | | `Hint` | 25 | | | `Whitespace` | 26 | | | `ActiveLine` | 27 | | | `DebuggerCurrentLine` | 28 | | | `DebuggerErrorLine` | 29 | | | `Ruler` | 30 | | | `Bracket` | 31 | | | `Type` | 32 | | | `MenuPrimaryText` | 33 | | | `MenuSecondaryText` | 34 | | | `MenuSelectedText` | 35 | | | `MenuBackground` | 36 | | | `MenuSelectedBackground` | 37 | | | `MenuScrollbarBackground` | 38 | | | `MenuScrollbarHandle` | 39 | | | `MenuBorder` | 40 | | | `DocViewCodeBackground` | 41 | | | `AICOOverlayText` | 42 | | | `AICOOverlayButtonBackground` | 43 | | | `AICOOverlayButtonBackgroundHover` | 44 | | | `AICOOverlayButtonBackgroundPressed` | 45 | | | `IndentationRuler` | 46 | | --- name: StudioScriptEditorColorPresets last_updated: 2026-06-29T19:34:09Z type: enum --- # StudioScriptEditorColorPresets **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `RobloxDefault` | 0 | | | `Extra1` | 1 | | | `Extra2` | 2 | | | `Custom` | 3 | | --- name: StudioStyleGuideColor last_updated: 2026-06-29T19:34:09Z type: enum --- # StudioStyleGuideColor **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `MainBackground` | 0 | The primary background color used by the toolbar and windows. | | `Titlebar` | 1 | The color of windows' title bars. | | `Dropdown` | 2 | The color of dropdown menus, like those found in the Properties window. | | `Tooltip` | 3 | The background color of tooltips, like those seen when hovering over buttons on the toolbar or properties in the Properties window. | | `Notification` | 4 | | | `ScrollBar` | 5 | The color of scrollbar handles, like those seen in the Explorer window. | | `ScrollBarBackground` | 6 | The background color of scrollbars, like those seen in the Explorer window. | | `TabBar` | 7 | The background color of the tab bar, like the one where the script editor is opened. | | `Tab` | 8 | The color of tabs on the tab bar, like the ones opened when the script editor is opened. | | `FilterButtonDefault` | 9 | | | `FilterButtonHover` | 10 | | | `FilterButtonChecked` | 11 | | | `FilterButtonAccent` | 12 | | | `FilterButtonBorder` | 13 | | | `FilterButtonBorderAlt` | 14 | | | `RibbonTab` | 15 | The color of a tab on the toolbar menu. | | `RibbonTabTopBar` | 16 | The background color of the toolbar. | | `Button` | 17 | The background color of clickable buttons. | | `MainButton` | 18 | The background color of clickable buttons that are the primary action. | | `RibbonButton` | 19 | The background color of buttons on the toolbar. | | `ViewPortBackground` | 20 | The background color of the game viewport. | | `InputFieldBackground` | 21 | The background color of input fields, like those found in the Explorer window. | | `Item` | 22 | | | `TableItem` | 23 | | | `CategoryItem` | 24 | | | `GameSettingsTableItem` | 25 | The color of items within tables in the settings window. | | `GameSettingsTooltip` | 26 | The color of tooltips in the settings window. | | `EmulatorBar` | 27 | The color of the bar shown when the mobile emulator is active. | | `EmulatorDropDown` | 28 | The color of the device and physical size dropdowns in the emulator bar. | | `ColorPickerFrame` | 29 | The background color of the color picker. | | `CurrentMarker` | 30 | | | `Border` | 31 | The color of borders, like those found in the Properties window. | | `DropShadow` | 32 | | | `Shadow` | 33 | The color of shadows rendered under floating items like dropdowns. | | `Light` | 34 | | | `Dark` | 35 | | | `Mid` | 36 | | | `MainText` | 37 | | | `SubText` | 38 | | | `TitlebarText` | 39 | | | `BrightText` | 40 | | | `DimmedText` | 41 | | | `LinkText` | 42 | | | `WarningText` | 43 | | | `ErrorText` | 44 | | | `InfoText` | 45 | | | `SensitiveText` | 46 | | | `ScriptSideWidget` | 47 | | | `ScriptBackground` | 48 | | | `ScriptText` | 49 | | | `ScriptSelectionText` | 50 | | | `ScriptSelectionBackground` | 51 | | | `ScriptFindSelectionBackground` | 52 | | | `ScriptMatchingWordSelectionBackground` | 53 | | | `ScriptOperator` | 54 | | | `ScriptNumber` | 55 | | | `ScriptString` | 56 | | | `ScriptComment` | 57 | | | `ScriptKeyword` | 58 | | | `ScriptBuiltInFunction` | 59 | | | `ScriptWarning` | 60 | | | `ScriptError` | 61 | | | `ScriptInformation` | 62 | | | `ScriptHint` | 63 | | | `ScriptWhitespace` | 64 | | | `ScriptRuler` | 65 | | | `DocViewCodeBackground` | 66 | | | `DebuggerCurrentLine` | 67 | | | `DebuggerErrorLine` | 68 | | | `DiffFilePathText` | 69 | | | `DiffTextHunkInfo` | 70 | | | `DiffTextNoChange` | 71 | | | `DiffTextAddition` | 72 | | | `DiffTextDeletion` | 73 | | | `DiffTextSeparatorBackground` | 74 | | | `DiffTextNoChangeBackground` | 75 | | | `DiffTextAdditionBackground` | 76 | | | `DiffTextDeletionBackground` | 77 | | | `DiffLineNum` | 78 | | | `DiffLineNumSeparatorBackground` | 79 | | | `DiffLineNumNoChangeBackground` | 80 | | | `DiffLineNumAdditionBackground` | 81 | | | `DiffLineNumDeletionBackground` | 82 | | | `DiffFilePathBackground` | 83 | | | `DiffFilePathBorder` | 84 | | | `ChatIncomingBgColor` | 85 | | | `ChatIncomingTextColor` | 86 | | | `ChatOutgoingBgColor` | 87 | | | `ChatOutgoingTextColor` | 88 | | | `ChatModeratedMessageColor` | 89 | | | `Separator` | 90 | | | `ButtonBorder` | 91 | | | `ButtonText` | 92 | | | `InputFieldBorder` | 93 | | | `CheckedFieldBackground` | 94 | | | `CheckedFieldBorder` | 95 | | | `CheckedFieldIndicator` | 96 | | | `HeaderSection` | 97 | | | `Midlight` | 98 | | | `StatusBar` | 99 | The color of the status bar shown at the bottom of Studio, if it is visible. | | `DialogButton` | 100 | | | `DialogButtonText` | 101 | | | `DialogButtonBorder` | 102 | | | `DialogMainButton` | 103 | | | `DialogMainButtonText` | 104 | | | `InfoBarWarningBackground` | 105 | | | `InfoBarWarningText` | 106 | | | `ScriptEditorCurrentLine` | 107 | | | `ScriptMethod` | 108 | | | `ScriptProperty` | 109 | | | `ScriptNil` | 110 | | | `ScriptBool` | 111 | | | `ScriptFunction` | 112 | | | `ScriptLocal` | 113 | | | `ScriptSelf` | 114 | | | `ScriptLuauKeyword` | 115 | | | `ScriptFunctionName` | 116 | | | `ScriptTodo` | 117 | | | `ScriptBracket` | 118 | | | `AttributeCog` | 119 | | | `AICOOverlayText` | 128 | | | `AICOOverlayButtonBackground` | 129 | | | `AICOOverlayButtonBackgroundHover` | 130 | | | `AICOOverlayButtonBackgroundPressed` | 131 | | | `OnboardingCover` | 132 | | | `OnboardingHighlight` | 133 | | | `OnboardingShadow` | 134 | | | `BreakpointMarker` | 136 | | | `DiffLineNumHover` | 137 | | | `DiffLineNumSeparatorBackgroundHover` | 138 | | --- name: StudioStyleGuideModifier last_updated: 2026-06-29T19:34:09Z type: enum --- # StudioStyleGuideModifier **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | | | `Selected` | 1 | | | `Pressed` | 2 | | | `Disabled` | 3 | | | `Hover` | 4 | | --- name: Style last_updated: 2026-06-29T19:34:09Z type: enum summary: "The Style Enum is used to set what style of supports the TrussPart has." --- # Style The Style Enum is used to set what style of supports the [TrussPart](/docs/reference/engine/classes/TrussPart.md) has. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `AlternatingSupports` | 0 | Truss beams contain alternating supports. | | `BridgeStyleSupports` | 1 | Truss beams contain bridge style supports. | | `NoSupports` | 2 | Truss beams do not contain supports. | --- name: SubscriptionExpirationReason last_updated: 2026-06-29T19:34:09Z type: enum --- # SubscriptionExpirationReason **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `ProductInactive` | 0 | Subscription product is inactive. | | `ProductDeleted` | 1 | Subscription product has been deleted. | | `SubscriberCancelled` | 2 | Subscriber has cancelled the subscription. | | `SubscriberRefunded` | 3 | Subscriber has been refunded. | | `Lapsed` | 4 | Subscription has lapsed. | --- name: SubscriptionPaymentStatus last_updated: 2026-06-29T19:34:09Z type: enum --- # SubscriptionPaymentStatus **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Paid` | 0 | Payment for the subscription has been received. | | `Refunded` | 1 | Payment for the subscription has been refunded. | --- name: SubscriptionPeriod last_updated: 2026-06-29T19:34:09Z type: enum --- # SubscriptionPeriod **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Month` | 0 | | --- name: SubscriptionState last_updated: 2026-06-29T19:34:09Z type: enum --- # SubscriptionState **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `NeverSubscribed` | 0 | User has never subscribed to the product. | | `SubscribedWillRenew` | 1 | Subscription is active and will renew. | | `SubscribedWillNotRenew` | 2 | Subscription is active and will not renew. | | `SubscribedRenewalPaymentPending` | 3 | Subscription is active and renewal payment is pending. | | `Expired` | 4 | Subscription has expired. | --- name: SurfaceConstraint last_updated: 2026-06-29T19:34:09Z type: enum summary: "This item is deprecated and is replaced by the SurfaceType enum." --- # SurfaceConstraint This item is deprecated and is replaced by the [SurfaceType](/docs/reference/engine/enums/SurfaceType.md) enum. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `None` | 0 | No surface constraint. | | `Hinge` | 1 | Makes the side appear with a yellow hinge. Any part connected to this hinge will stick to the side and rotate using physics. | | `SteppingMotor` | 2 | Functions identically to a motor. It may have functioned differently in the past, but that functionality no longer seems to exist. | | `Motor` | 3 | Acts the same as a Hinge, but has a grey ring around it and automatically rotates any part connected to it. | --- name: SurfaceGuiShape last_updated: 2026-06-29T19:34:09Z type: enum --- # SurfaceGuiShape **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Flat` | 0 | | | `CurvedHorizontally` | 1 | | --- name: SurfaceGuiSizingMode last_updated: 2026-06-29T19:34:09Z type: enum summary: "Used by SurfaceGui.SizingMode to control the sizing behavior of a SurfaceGui." --- # SurfaceGuiSizingMode Used by [SurfaceGui.SizingMode](/docs/reference/engine/classes/SurfaceGui.md) to control the sizing behavior of a [SurfaceGui](/docs/reference/engine/classes/SurfaceGui.md). **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `FixedSize` | 0 | Renders with a fixed size set using [SurfaceGui.CanvasSize](/docs/reference/engine/classes/SurfaceGui.md). | | `PixelsPerStud` | 1 | Renders with a variable size based on [SurfaceGui.PixelsPerStud](/docs/reference/engine/classes/SurfaceGui.md) and the SurfaceGui's size in studs. | --- name: SurfaceType last_updated: 2026-06-29T19:34:09Z type: enum summary: "Used to determine how a surface should be displayed on a part and how automatic surface joints should behave." --- # SurfaceType Used to determine how a surface should be displayed on a part and how automatic surface joints should behave. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Smooth` | 0 | Adds no details on the surface. | | `Glue` | 1 | Adds an "X" pattern across the surface. | | `Weld` | 2 | Adds an "X" pattern across the surface. | | `Studs` | 3 | Adds square studs across the surface. | | `Inlet` | 4 | Adds square holes across the surface where studs would be. | | `Universal` | 5 | Adds a checker pattern to the surface using studs and inlets. | | `Hinge` | 6 | Adds a yellow hinge to the surface. Parts touching it stick to the surface, allowing for rotations using physics. This should not be used for future work and existing instances should be replaced with a [HingeConstraint](/docs/reference/engine/classes/HingeConstraint.md). | | `Motor` | 7 | Functioned identically to a hinge with the addition of a grey ring. | | `SteppingMotor` | 8 | Functioned identically to a motor. It may have functioned differently in the past, but that functionality is non-existent. | | `SmoothNoOutlines` | 10 | Previously similar to **Smooth** with outlines but no longer relevant since outlines have been removed. | --- name: SwipeDirection last_updated: 2026-06-29T19:34:09Z type: enum summary: "The direction that a user is swiping on their touch screen." --- # SwipeDirection The direction that a user is swiping on their touch screen. **Type:** enum ## Description The SwipeDirection Enum represents the direction in which a user swipes on a [UserInputService.TouchEnabled](/docs/reference/engine/classes/UserInputService.md) device. The [GuiObject.TouchSwipe](/docs/reference/engine/classes/GuiObject.md) and [UserInputService.TouchSwipe](/docs/reference/engine/classes/UserInputService.md) events pass this enum as an argument. ## Items | Name | Value | Description | |------|-------|-------------| | `Right` | 0 | The user swiped right. | | `Left` | 1 | The user swiped left. | | `Up` | 2 | The user swiped up. | | `Down` | 3 | The user swiped down. | | `None` | 4 | The user did not swipe in a certain direction. | --- name: SystemThemeValue last_updated: 2026-06-29T19:34:09Z type: enum --- # SystemThemeValue **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `error` | 0 | | | `light` | 1 | | | `dark` | 2 | | | `systemLight` | 3 | | | `systemDark` | 4 | | --- name: TableMajorAxis last_updated: 2026-06-29T19:34:09Z type: enum summary: "Used by UITableLayout.MajorAxis to decide whether direct siblings are rows or columns." --- # TableMajorAxis Used by [UITableLayout.MajorAxis](/docs/reference/engine/classes/UITableLayout.md) to decide whether direct siblings are rows or columns. **Type:** enum ## Description Used to indicate whether the direct siblings of a [UITableLayout](/docs/reference/engine/classes/UITableLayout.md) are considered the rows or the columns. The children of the direct siblings are the columns or rows, respectively. ## Items | Name | Value | Description | |------|-------|-------------| | `RowMajor` | 0 | The direct siblings are considered rows. | | `ColumnMajor` | 1 | The direct siblings are considered columns. | --- name: TeamCreateErrorState last_updated: 2026-06-29T19:34:09Z type: enum --- # TeamCreateErrorState **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `PlaceSizeTooLarge` | 0 | | | `PlaceSizeApproachingLimit` | 1 | | | `PlaceUploadFailing` | 2 | | | `NoError` | 3 | | --- name: Technology last_updated: 2026-06-29T19:34:09Z type: enum summary: "Enum used by Lighting.Technology to represent the different lighting systems available for rendering the 3D world." --- # Technology Enum used by [Lighting.Technology](/docs/reference/engine/classes/Lighting.md) to represent the different lighting systems available for rendering the 3D world. **Type:** enum ## Description This enum represents the different lighting systems available for rendering the 3D world. It is used by the [Lighting.Technology](/docs/reference/engine/classes/Lighting.md) property. Note that [Lighting.Technology](/docs/reference/engine/classes/Lighting.md) has been superseded by [LightingStyle](/docs/reference/engine/classes/Lighting.md) which determines the artistic intent behind lighting, and [PrioritizeLightingQuality](/docs/reference/engine/classes/Lighting.md) which indicates whether you prefer lighting/shading quality or view distance to scale down first. ## Items | Name | Value | Description | |------|-------|-------------| | `Legacy` | 0 | | | `Voxel` | 1 | Uses a 4×4×4 voxel map for light and shadow calculation. | | `Compatibility` | 2 | Simulates the removed legacy technology and is now deprecated. To achieve a similar look, use `Voxel` Lighting and add a [ColorGradingEffect](/docs/reference/engine/classes/ColorGradingEffect.md) post‑processing effect set to the [Retro](/docs/reference/engine/enums/TonemapperPreset.md) preset. | | `ShadowMap` | 3 | Features shadow mapping that produces more realistic and crisp shadows from sunlight or directional light sources. | | `Future` | 4 | Features the most advanced technology for high-fidelity lighting and shadows. | | `Unified` | 5 | | --- name: TeleportMethod last_updated: 2026-06-29T19:34:09Z type: enum --- # TeleportMethod **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `TeleportToSpawnByName` | 0 | | | `TeleportToPlaceInstance` | 1 | | | `TeleportToPrivateServer` | 2 | | | `TeleportPartyAsync` | 3 | | | `TeleportToVIPServer` | 4 | | | `TeleportToInstanceBack` | 5 | | | `TeleportUnknown` | 6 | | --- name: TeleportResult last_updated: 2026-06-29T19:34:09Z type: enum summary: "Describes the result of a teleport." --- # TeleportResult Describes the result of a teleport. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Success` | 0 | The teleport was successful. | | `Failure` | 1 | The teleport failed for an unknown reason. | | `GameNotFound` | 2 | The game that this player attempted to teleport to could not be found. | | `GameEnded` | 3 | The game that this player attempted to teleport to has ended. | | `GameFull` | 4 | The game that this player attempted to teleport to is full. | | `Unauthorized` | 5 | The player is not authorized to complete this teleport. | | `Flooded` | 6 | Too many teleport requests have been made recently. | | `IsTeleporting` | 7 | The player is currently being teleported. | --- name: TeleportState last_updated: 2026-06-29T19:34:09Z type: enum summary: "Determines the current teleportation state of a player." --- # TeleportState Determines the current teleportation state of a player. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `RequestedFromServer` | 0 | The server has requested that the client teleport. | | `Started` | 1 | The client has started attempting to teleport. | | `WaitingForServer` | 2 | The client is waiting for the server to respond to the teleport request. | | `Failed` | 3 | The teleport failed. | | `InProgress` | 4 | The teleport is currently in progress. The player usually disconnects and teleports to the destination after this. | --- name: TeleportType last_updated: 2026-06-29T19:34:09Z type: enum summary: "Determines the type of teleport destination for a TeleportService teleport call." --- # TeleportType Determines the type of teleport destination for a [TeleportService](/docs/reference/engine/classes/TeleportService.md) teleport call. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `ToPlace` | 0 | The teleport destination is a place (no specific instance). | | `ToInstance` | 1 | The teleport destination is an instance of a place. | | `ToReservedServer` | 2 | The destination of the teleport is a reserved server (a place instance that is reserved for a select group of players). | | `ToVIPServer` | 3 | | | `ToInstanceBack` | 4 | | --- name: TerrainAcquisitionMethod last_updated: 2026-06-29T19:34:09Z type: enum --- # TerrainAcquisitionMethod **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `None` | 0 | | | `Legacy` | 1 | | | `Template` | 2 | | | `Generate` | 3 | | | `Import` | 4 | | | `Convert` | 5 | | | `EditAddTool` | 6 | | | `EditSeaLevelTool` | 7 | | | `EditReplaceTool` | 8 | | | `RegionFillTool` | 9 | | | `RegionPasteTool` | 10 | | | `Other` | 11 | | --- name: TerrainFace last_updated: 2026-06-29T19:34:09Z type: enum --- # TerrainFace **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Top` | 0 | | | `Side` | 1 | | | `Bottom` | 2 | | --- name: TextChatMessageStatus last_updated: 2026-06-29T19:34:09Z type: enum summary: "Indicates the status of a TextChatMessage." --- # TextChatMessageStatus Indicates the status of a [TextChatMessage](/docs/reference/engine/classes/TextChatMessage.md). **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Unknown` | 1 | Generic failed status for any other [TextChannel:SendAsync()](/docs/reference/engine/classes/TextChannel.md) failures. | | `Success` | 2 | Message has no issues. | | `Sending` | 3 | Message is sending. | | `TextFilterFailed` | 4 | Text filter failed to process the message. | | `Floodchecked` | 5 | Message is from a user sending messages too frequently. | | `InvalidPrivacySettings` | 6 | Message can't be sent because of the user's chat privacy settings. | | `InvalidTextChannelPermissions` | 7 | Message's [TextSource](/docs/reference/engine/classes/TextSource.md) is either not in the intended [TextChannel](/docs/reference/engine/classes/TextChannel.md) or [TextSource.CanSend](/docs/reference/engine/classes/TextSource.md) is `false`. | | `MessageTooLong` | 8 | Message is too long. | | `ModerationTimeout` | 9 | | --- name: TextDirection last_updated: 2026-06-29T19:34:09Z type: enum --- # TextDirection **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Auto` | 0 | | | `LeftToRight` | 1 | | | `RightToLeft` | 2 | | --- name: TextFilterContext last_updated: 2026-06-29T19:34:09Z type: enum --- # TextFilterContext **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `PublicChat` | 1 | | | `PrivateChat` | 2 | | --- name: TextInputType last_updated: 2026-06-29T19:34:09Z type: enum --- # TextInputType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | | | `NoSuggestions` | 1 | | | `Number` | 2 | | | `Email` | 3 | | | `Phone` | 4 | | | `Password` | 5 | | | `PasswordShown` | 6 | | | `Username` | 7 | | | `OneTimePassword` | 8 | | | `NewPassword` | 9 | | | `NewPasswordShown` | 10 | | --- name: TextTruncate last_updated: 2026-06-29T19:34:09Z type: enum summary: "Controls the truncation of text when using the `TextTruncate` property." --- # TextTruncate Controls the truncation of text when using the `TextTruncate` property. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `None` | 0 | Text is not truncated. | | `AtEnd` | 1 | Text is truncated at the end of the text; extra graphemes that cannot fit into the space are replaced with `...`. Word boundaries are respected if possible. For example, if the control is between -> and <- like so: `->Long text is truncated at the en<-d of the last complete word` The text will be: `Long text is truncated at the...` | | `SplitWord` | 2 | If the end of the line occurs in the middle of a word, the text is truncated inside of that word. Extra graphemes that cannot fit into the space are replaced with `...`. For example, if the control is between -> and <- like so: `->Long text is truncated at the en<-d of the last complete word` The text will be: `Long text is truncated at the en...` | --- name: TextXAlignment last_updated: 2026-06-29T19:34:09Z type: enum summary: "Determines horizontal alignment of text." --- # TextXAlignment Determines horizontal alignment of text. **Type:** enum ## Description Determines horizontal alignment of text. Used by [TextLabel](/docs/reference/engine/classes/TextLabel.md), [TextButton](/docs/reference/engine/classes/TextButton.md), and [TextBox](/docs/reference/engine/classes/TextBox.md). ## Items | Name | Value | Description | |------|-------|-------------| | `Left` | 0 | Alignment starts at the left side of the GUI object. | | `Right` | 1 | Alignment starts at the right side of the GUI object. | | `Center` | 2 | Aligned starts at the center of the GUI object and spreads equally left and right. | --- name: TextYAlignment last_updated: 2026-06-29T19:34:09Z type: enum summary: "Determines vertical alignment of text." --- # TextYAlignment Determines vertical alignment of text. **Type:** enum ## Description Determines vertical alignment of text. Used by [TextLabel](/docs/reference/engine/classes/TextLabel.md), [TextButton](/docs/reference/engine/classes/TextButton.md), and [TextBox](/docs/reference/engine/classes/TextBox.md). ## Items | Name | Value | Description | |------|-------|-------------| | `Top` | 0 | Alignment starts at the top of the GUI object. | | `Center` | 1 | Aligned starts at the center of the GUI object and spreads equally toward the top and bottom. | | `Bottom` | 2 | Alignment starts at the bottom of the GUI object. | --- name: TextureMode last_updated: 2026-06-29T19:34:09Z type: enum summary: "Describes how the texture of a Trail or Beam behaves." --- # TextureMode Describes how the texture of a [Trail](/docs/reference/engine/classes/Trail.md) or [Beam](/docs/reference/engine/classes/Beam.md) behaves. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Stretch` | 0 | For a [Trail](/docs/reference/engine/classes/Trail.md), the texture will stretch out based on the lifetime of the trail, and shrink inwards if the trail's attachments stop moving. For a [Beam](/docs/reference/engine/classes/Beam.md), the texture will repeat [TextureLength](/docs/reference/engine/classes/Beam.md) times across the beam's overall length. See [here](/docs/en-us/effects/beams.md#texture-lengthmode) for details. | | `Wrap` | 1 | For a [Trail](/docs/reference/engine/classes/Trail.md), the texture will be tiled as the length of the trail changes, but the textures will remain stationary relative to their attachments. For a [Beam](/docs/reference/engine/classes/Beam.md), the texture repetitions will equal the beam's overall length (in studs) divided by its [TextureLength](/docs/reference/engine/classes/Beam.md). See [here](/docs/en-us/effects/beams.md#texture-lengthmode) for details. | | `Static` | 2 | For a [Trail](/docs/reference/engine/classes/Trail.md), the texture will be rolled out as the attachments move, and they will remain in place until their lifetime is met. This setting is ideal for trail textures that should appear "stamped" where rendered, such as paw prints or tire tracks. This value is not supported for [Beam](/docs/reference/engine/classes/Beam.md) and therefore behaves identically to **Wrap**. | --- name: TextureQueryType last_updated: 2026-06-29T19:34:09Z type: enum summary: "Used to get memory information about textures." --- # TextureQueryType Used to get memory information about textures. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `NonHumanoid` | 0 | Texture memory for objects other than [Humanoid](/docs/reference/engine/classes/Humanoid.md). | | `NonHumanoidOrphaned` | 1 | Unreferenced texture memory for objects other than [Humanoid](/docs/reference/engine/classes/Humanoid.md). | | `Humanoid` | 2 | Texture memory for [Humanoid](/docs/reference/engine/classes/Humanoid.md). | | `HumanoidOrphaned` | 3 | Unreferenced texture memory for [Humanoid](/docs/reference/engine/classes/Humanoid.md). | --- name: ThreadPoolConfig last_updated: 2026-06-29T19:34:09Z type: enum summary: "Thread pooling scheme for the task scheduler." --- # ThreadPoolConfig Thread pooling scheme for the task scheduler. **Type:** enum ## Description Controls the thread pooling scheme of the underlying 'TaskScheduler'. See TaskScheduler for details. ## Items | Name | Value | Description | |------|-------|-------------| | `Auto` | 0 | Let task scheduler make a decision internally. | | `Threads1` | 1 | Utilize 1 worker thread, ignore the physical CPU core count. | | `Threads2` | 2 | Utilize 2 worker threads, ignore the physical CPU core count. | | `Threads3` | 3 | Utilize 3 worker threads, ignore the physical CPU core count. | | `Threads4` | 4 | Utilize 4 worker threads, ignore the physical CPU core count. | | `Threads8` | 8 | Utilize 8 worker threads, ignore the physical CPU core count. | | `Threads16` | 16 | Utilize 16 worker threads, ignore the physical CPU core count. | | `PerCore1` | 101 | Utilize 1 worker thread per available physical CPU core. | | `PerCore2` | 102 | Utilize 2 worker threads per available physical CPU core. | | `PerCore3` | 103 | Utilize 3 worker threads per available physical CPU core. | | `PerCore4` | 104 | Utilize 4 worker threads per available physical CPU core. | --- name: ThrottlingPriority last_updated: 2026-06-29T19:34:09Z type: enum summary: "Amount of throttling to apply." --- # ThrottlingPriority Amount of throttling to apply. **Type:** enum ## Description Controls the amount of throttling under the [HttpRbxApiService](/docs/reference/engine/classes/HttpRbxApiService.md). ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | Standard throttling. | | `ElevatedOnServer` | 1 | Less throttling if on the server. | | `Extreme` | 2 | No throttling, absolutely no exceptions. | --- name: ThumbnailSize last_updated: 2026-06-29T19:34:09Z type: enum --- # ThumbnailSize **Type:** enum ## Description Describes the resolution of a user thumbnail being returned by GetUserThumbnailAsync. ## Items | Name | Value | Description | |------|-------|-------------| | `Size48x48` | 0 | Thumbnail resolution is 48 pixels x 48 pixels. | | `Size180x180` | 1 | Thumbnail resolution is 180 pixels x 180 pixels. | | `Size420x420` | 2 | Thumbnail resolution is 420 pixels x 420 pixels. | | `Size60x60` | 3 | Thumbnail resolution is 60 pixels x 60 pixels. | | `Size100x100` | 4 | Thumbnail resolution is 100 pixels x 100 pixels. | | `Size150x150` | 5 | Thumbnail resolution is 150 pixels x 150 pixels. | | `Size352x352` | 6 | Thumbnail resolution is 352 pixels x 352 pixels. | --- name: ThumbnailType last_updated: 2026-06-29T19:34:09Z type: enum --- # ThumbnailType **Type:** enum ## Description Describes the type of user thumbnail that should be returned by GetUserThumbnailAsync. ## Items | Name | Value | Description | |------|-------|-------------| | `HeadShot` | 0 | The returned thumbnail shows just the headshot (head and up) of the user's avatar. | | `AvatarBust` | 1 | The returned thumbnail shows the bust (chest up) of the user's avatar. | | `AvatarThumbnail` | 2 | The returned thumbnail shows the entire body of the user's avatar. | --- name: TickCountSampleMethod last_updated: 2026-06-29T19:34:09Z type: enum summary: "Controls the precision of a timer." --- # TickCountSampleMethod Controls the precision of a timer. **Type:** enum ## Description Compute time using a faster, but less precise method. ## Items | Name | Value | Description | |------|-------|-------------| | `Fast` | 0 | Compute time using a faster, but less precise method. | | `Benchmark` | 1 | Dynamically decide between using ''Fast'' and ''Precise'' depending on performance. | | `Precise` | 2 | Compute time using a precise method. | --- name: TonemapperPreset last_updated: 2026-06-29T19:34:09Z type: enum --- # TonemapperPreset **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | Sets the tone mapper to use the post‑2019 Roblox appearance which provides vivid colors and high contrasts. | | `Retro` | 1 | Sets the tone mapper to imitate the pre‑2019 Roblox appearance. Colors look less saturated and there is less contrast between them. | --- name: TopBottom last_updated: 2026-06-29T19:34:09Z type: enum summary: "Sets where the object is on the side of its parent." --- # TopBottom Sets where the object is on the side of its parent. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Top` | 0 | The object is above its parent. | | `Center` | 1 | The object is centered around the center of its parent. | | `Bottom` | 2 | The object is below its parent. | --- name: TouchCameraMovementMode last_updated: 2026-06-29T19:34:09Z type: enum summary: "Changes the touch camera movement mode currently in-use by the client using a TouchEnabled device." --- # TouchCameraMovementMode Changes the touch camera movement mode currently in-use by the client using a TouchEnabled device. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | Default mode used by Roblox core scripts. | | `Classic` | 1 | Default mode used by Roblox core scripts. | | `Follow` | 2 | Camera moves with the subject and rotates to keep the subject in the center. | | `Orbital` | 3 | The camera locks at a certain angle. Once it's set, players can rotate the camera, but it will always maintain a consistent angle to the ground. | --- name: TouchMovementMode last_updated: 2026-06-29T19:34:09Z type: enum summary: "The movement mode used by a client with a TouchEnabled device." --- # TouchMovementMode The movement mode used by a client with a TouchEnabled device. **Type:** enum ## Description The TouchMovementMode Enum is used to change the movement controller on Roblox Mobile. ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | The default movement controller. | | `Thumbstick` | 1 | Change the movement controller to the classic thumbstick. | | `DPad` | 2 | Change the movement controller to the DPad. | | `Thumbpad` | 3 | Change the movement controller to the thumbpad. | | `ClickToMove` | 4 | Change the movement controller to click to move. | | `DynamicThumbstick` | 5 | Change the movement controller to the dynamic thumbstick. | --- name: TrackerError last_updated: 2026-06-29T19:34:09Z type: enum --- # TrackerError **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Ok` | 0 | | | `NoService` | 1 | | | `InitFailed` | 2 | | | `NoVideo` | 3 | | | `VideoError` | 4 | | | `VideoNoPermission` | 5 | | | `VideoUnsupported` | 6 | | | `NoAudio` | 7 | | | `AudioError` | 8 | | | `AudioNoPermission` | 9 | | | `UnsupportedDevice` | 10 | | --- name: TrackerExtrapolationFlagMode last_updated: 2026-06-29T19:34:09Z type: enum --- # TrackerExtrapolationFlagMode **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `ForceDisabled` | 0 | | | `ExtrapolateFacsAndPose` | 1 | | | `ExtrapolateFacsOnly` | 2 | | | `Auto` | 3 | | --- name: TrackerFaceTrackingStatus last_updated: 2026-06-29T19:34:09Z type: enum --- # TrackerFaceTrackingStatus **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `FaceTrackingSuccess` | 0 | | | `FaceTrackingNoFaceFound` | 1 | | | `FaceTrackingUnknown` | 2 | | | `FaceTrackingLost` | 3 | | | `FaceTrackingHasTrackingError` | 4 | | | `FaceTrackingIsOccluded` | 5 | | | `FaceTrackingUninitialized` | 6 | | --- name: TrackerLodFlagMode last_updated: 2026-06-29T19:34:09Z type: enum --- # TrackerLodFlagMode **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `ForceFalse` | 0 | | | `ForceTrue` | 1 | | | `Auto` | 2 | | --- name: TrackerLodValueMode last_updated: 2026-06-29T19:34:09Z type: enum --- # TrackerLodValueMode **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Force0` | 0 | | | `Force1` | 1 | | | `Auto` | 2 | | --- name: TrackerMode last_updated: 2026-06-29T19:34:09Z type: enum --- # TrackerMode **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `None` | 0 | | | `Audio` | 1 | | | `Video` | 2 | | | `AudioVideo` | 3 | | --- name: TrackerPromptEvent last_updated: 2026-06-29T19:34:09Z type: enum --- # TrackerPromptEvent **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `LODCameraRecommendDisable` | 0 | | --- name: TrackerType last_updated: 2026-06-29T19:34:09Z type: enum --- # TrackerType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `None` | 0 | | | `Face` | 1 | | | `UpperBody` | 2 | | --- name: TriStateBoolean last_updated: 2026-06-29T19:34:09Z type: enum --- # TriStateBoolean **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Unknown` | 0 | | | `True` | 1 | | | `False` | 2 | | --- name: TweenStatus last_updated: 2026-06-29T19:34:09Z type: enum summary: "The completion status of a GuiObject tween function." --- # TweenStatus The completion status of a [GuiObject](/docs/reference/engine/classes/GuiObject.md) tween function. **Type:** enum ## Description Describes the completion status of a [GuiObject](/docs/reference/engine/classes/GuiObject.md) tween function. Passed as an argument to the callback function provided to [GuiObject:TweenPosition()](/docs/reference/engine/classes/GuiObject.md), [GuiObject:TweenSize()](/docs/reference/engine/classes/GuiObject.md), and [GuiObject:TweenSizeAndPosition()](/docs/reference/engine/classes/GuiObject.md). Not to be confused for [PlaybackState](/docs/reference/engine/enums/PlaybackState.md) which is used with [TweenService](/docs/reference/engine/classes/TweenService.md). ## Items | Name | Value | Description | |------|-------|-------------| | `Canceled` | 0 | The tween was cancelled before completion. | | `Completed` | 1 | The Tween has successfully completed. | --- name: UICaptureMode last_updated: 2026-06-29T19:34:09Z type: enum summary: "Used to determine what UI elements should be captured with CaptureService." --- # UICaptureMode Used to determine what UI elements should be captured with [CaptureService](/docs/reference/engine/classes/CaptureService.md). **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `All` | 0 | Capture all UI elements. | | `None` | 1 | Capture no UI elements. | --- name: UIDragDetectorBoundingBehavior last_updated: 2026-06-29T19:34:09Z type: enum summary: "Used with UIDragDetector to determine bounding behavior of the dragged UI object when UIDragDetector.BoundingUI is set." --- # UIDragDetectorBoundingBehavior Used with [UIDragDetector](/docs/reference/engine/classes/UIDragDetector.md) to determine bounding behavior of the dragged UI object when [UIDragDetector.BoundingUI](/docs/reference/engine/classes/UIDragDetector.md) is set. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Automatic` | 0 | Mimics **EntireObject** behavior for a UI object that's entirely contained by the [BoundingUI](/docs/reference/engine/classes/UIDragDetector.md), or else **HitPoint** for a UI object that's partially outside the [BoundingUI](/docs/reference/engine/classes/UIDragDetector.md). | | `EntireObject` | 1 | Bounds the entire dragged UI object within the [BoundingUI](/docs/reference/engine/classes/UIDragDetector.md). | | `HitPoint` | 2 | Bounds the dragged UI only by the exact hit/grab point and its respective position after translation/rotation. | --- name: UIDragDetectorDragRelativity last_updated: 2026-06-29T19:34:09Z type: enum summary: "Used with DragDetector to set the paradigm which defines the relativity of inputs/outputs from a custom drag function." --- # UIDragDetectorDragRelativity Used with [DragDetector](/docs/reference/engine/classes/DragDetector.md) to set the paradigm which defines the relativity of inputs/outputs from a custom drag function. **Type:** enum ## Description Used with [DragDetector](/docs/reference/engine/classes/DragDetector.md) to set the paradigm which defines the relativity of inputs/outputs from a custom drag function registered through [SetDragStyleFunction()](/docs/reference/engine/classes/UIDragDetector.md) or [AddConstraintFunction()](/docs/reference/engine/classes/UIDragDetector.md). ## Items | Name | Value | Description | |------|-------|-------------| | `Absolute` | 0 | Designates the input and return values as the absolute target position/rotation in the space defined by [DragSpace](/docs/reference/engine/classes/UIDragDetector.md). | | `Relative` | 1 | Designates the input and return values as the change from the current position/rotation in the space defined by [DragSpace](/docs/reference/engine/classes/UIDragDetector.md). | --- name: UIDragDetectorDragSpace last_updated: 2026-06-29T19:34:09Z type: enum summary: "Used with DragDetector to set the paradigm which defines the space of inputs/outputs from a custom drag function." --- # UIDragDetectorDragSpace Used with [DragDetector](/docs/reference/engine/classes/DragDetector.md) to set the paradigm which defines the space of inputs/outputs from a custom drag function. **Type:** enum ## Description Used with [DragDetector](/docs/reference/engine/classes/DragDetector.md) to set the paradigm which defines the space of inputs/outputs from a custom drag function registered through [SetDragStyleFunction()](/docs/reference/engine/classes/UIDragDetector.md) or [AddConstraintFunction()](/docs/reference/engine/classes/UIDragDetector.md). ## Items | Name | Value | Description | |------|-------|-------------| | `Parent` | 0 | Designates the input and return values' space as the local space of the detector's parent [GuiObject](/docs/reference/engine/classes/GuiObject.md). | | `LayerCollector` | 1 | Designates the input and return values' space as that of the [LayerCollector](/docs/reference/engine/classes/LayerCollector.md). | | `Reference` | 2 | Designates the input and return values' space as that of the [ReferenceUIInstance](/docs/reference/engine/classes/UIDragDetector.md). For [DragRelativity](/docs/reference/engine/classes/UIDragDetector.md) and [DragUDim2](/docs/reference/engine/classes/UIDragDetector.md) purposes, the `(0, 0)` origin is the absolute center position of the [ReferenceUIInstance](/docs/reference/engine/classes/UIDragDetector.md). If [ReferenceUIInstance](/docs/reference/engine/classes/UIDragDetector.md) is not `nil`, this will behave the same as **Parent**. | --- name: UIDragDetectorDragStyle last_updated: 2026-06-29T19:34:09Z type: enum summary: "Used with UIDragDetector as the paradigm to generate proposed motion, given a stream of input position vectors." --- # UIDragDetectorDragStyle Used with [UIDragDetector](/docs/reference/engine/classes/UIDragDetector.md) as the paradigm to generate proposed motion, given a stream of input position vectors. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `TranslatePlane` | 0 | 2D motion in the plane of the [LayerCollector](/docs/reference/engine/classes/LayerCollector.md). | | `TranslateLine` | 1 | 1D motion along the detector's [DragAxis](/docs/reference/engine/classes/UIDragDetector.md). | | `Rotate` | 2 | By default, rotation about the absolute center position of the detector's parent [GuiObject](/docs/reference/engine/classes/GuiObject.md). If [ReferenceUIInstance](/docs/reference/engine/classes/UIDragDetector.md) is set, rotation happens about that instance's absolute center position. | | `Scriptable` | 3 | Calculates desired motion via a custom function provided through [SetDragStyleFunction()](/docs/reference/engine/classes/UIDragDetector.md). | --- name: UIDragDetectorResponseStyle last_updated: 2026-06-29T19:34:09Z type: enum summary: "Describes how the clicked GuiObject will be treated once the desired motion has been calculated." --- # UIDragDetectorResponseStyle Describes how the clicked [GuiObject](/docs/reference/engine/classes/GuiObject.md) will be treated once the desired motion has been calculated. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Offset` | 0 | Move by the [Offset](/docs/reference/engine/datatypes/UDim.md) values of the detector's parent's [GuiObject.Position](/docs/reference/engine/classes/GuiObject.md) value. | | `Scale` | 1 | Move by the [Scale](/docs/reference/engine/datatypes/UDim.md) values of the detector's parent's [GuiObject.Position](/docs/reference/engine/classes/GuiObject.md) value. | | `CustomOffset` | 2 | The UI element will not move at all, but the [Offset](/docs/reference/engine/datatypes/UDim.md) values of the detector's [DragUDim2](/docs/reference/engine/classes/UIDragDetector.md) will still be updated and the detector's events will still fire, allowing you to respond to drag manipulation however you'd like. | | `CustomScale` | 3 | The UI element will not move at all, but the [Scale](/docs/reference/engine/datatypes/UDim.md) values of the detector's [DragUDim2](/docs/reference/engine/classes/UIDragDetector.md) will still be updated and the detector's events will still fire, allowing you to respond to drag manipulation however you'd like. | --- name: UIDragSpeedAxisMapping last_updated: 2026-06-29T19:34:09Z type: enum summary: "Used with UIDragDetector.UIDragSpeedAxisMapping to determine the **X**/**Y** dimension dragging speeds." --- # UIDragSpeedAxisMapping Used with [UIDragDetector.UIDragSpeedAxisMapping](/docs/reference/engine/classes/UIDragDetector.md) to determine the **X**/**Y** dimension dragging speeds. **Type:** enum ## Description Used with [UIDragDetector.UIDragSpeedAxisMapping](/docs/reference/engine/classes/UIDragDetector.md) to determine the **X**/**Y** dimension dragging speeds, based on the detector's [SelectionModeDragSpeed](/docs/reference/engine/classes/UIDragDetector.md). ## Items | Name | Value | Description | |------|-------|-------------| | `XY` | 0 | Default setting for a detector's [UIDragSpeedAxisMapping](/docs/reference/engine/classes/UIDragDetector.md) where the **X** and **Y** axis speeds are based off the **X** and **Y** [Scale](/docs/reference/engine/datatypes/UDim.md)/[Offset](/docs/reference/engine/datatypes/UDim.md) values respectively. | | `XX` | 1 | Both the **X** and **Y** axis speeds are based off the **X** axis for [Scale](/docs/reference/engine/datatypes/UDim.md), while the [Offset](/docs/reference/engine/datatypes/UDim.md) values still apply to their respective axis. | | `YY` | 2 | Both the **X** and **Y** axis speeds are based off the **Y** axis for [Scale](/docs/reference/engine/datatypes/UDim.md), while the [Offset](/docs/reference/engine/datatypes/UDim.md) values still apply to their respective axis. | --- name: UIFlexAlignment last_updated: 2026-06-29T19:34:09Z type: enum summary: "In a UIListLayout flex layout, specifies how to distribute extra space in the parent container." --- # UIFlexAlignment In a [UIListLayout](/docs/reference/engine/classes/UIListLayout.md) flex layout, specifies how to distribute extra space in the parent container. **Type:** enum ## Description In a [UIListLayout](/docs/reference/engine/classes/UIListLayout.md) flex layout, specifies how to distribute extra horizontal space in a [HorizontalFlex](/docs/reference/engine/classes/UIListLayout.md) layout or vertical space in a [VerticalFlex](/docs/reference/engine/classes/UIListLayout.md) layout. ## Items | Name | Value | Description | |------|-------|-------------| | `None` | 0 | No flex behavior; siblings maintain their defined width or height. | | `Fill` | 1 | Siblings resize to fill the entire parent container, overriding their defined width or height. | | `SpaceAround` | 2 | Siblings maintain their defined width or height. Equal spacing is added on both sides of each sibling. | | `SpaceBetween` | 3 | Siblings maintain their defined width or height. Equal spacing is added **between** siblings, but no additional space is added **around** siblings. | | `SpaceEvenly` | 4 | Siblings maintain their defined width or height. Equal spacing is added both **between** and **around** siblings. | --- name: UIFlexMode last_updated: 2026-06-29T19:34:09Z type: enum summary: "Used with UIFlexItem.FlexMode to define how the parent GuiObject grows or shrinks." --- # UIFlexMode Used with [UIFlexItem.FlexMode](/docs/reference/engine/classes/UIFlexItem.md) to define how the parent [GuiObject](/docs/reference/engine/classes/GuiObject.md) grows or shrinks. **Type:** enum ## Description Used with [UIFlexItem.FlexMode](/docs/reference/engine/classes/UIFlexItem.md) to define how the parent [GuiObject](/docs/reference/engine/classes/GuiObject.md) grows or shrinks with available space in the container. ## Items | Name | Value | Description | |------|-------|-------------| | `None` | 0 | The parent [GuiObject](/docs/reference/engine/classes/GuiObject.md) is unaffected and neither shrinks nor grows. | | `Grow` | 1 | Sets an effective `1:0` grow‑shrink ratio on the parent [GuiObject](/docs/reference/engine/classes/GuiObject.md). Objects set to `Grow` never shrink below their basis size, so overflow may occur if the container becomes smaller than the flex line's combined basis size. | | `Shrink` | 2 | Sets an effective `0:1` grow‑shrink ratio on the parent [GuiObject](/docs/reference/engine/classes/GuiObject.md). Objects set to `Shrink` never grow above their basis size, so underflow may occur if the container becomes larger than the flex line's combined basis size. | | `Fill` | 3 | Sets an effective `1:1` grow‑shrink ratio on the parent [GuiObject](/docs/reference/engine/classes/GuiObject.md). This setting ensures the flex line always fills the container, even if the container size changes. | | `Custom` | 4 | Enables the [GrowRatio](/docs/reference/engine/classes/UIFlexItem.md) and [ShrinkRatio](/docs/reference/engine/classes/UIFlexItem.md) properties for the [UIFlexItem](/docs/reference/engine/classes/UIFlexItem.md), allowing for relative growth or shrinking of the parent [GuiObject](/docs/reference/engine/classes/GuiObject.md) in a ratio compared to other flex objects also under control of a [UIFlexItem](/docs/reference/engine/classes/UIFlexItem.md). | --- name: UITheme last_updated: 2026-06-29T19:34:09Z type: enum --- # UITheme **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Light` | 0 | | | `Dark` | 1 | | --- name: UiMessageType last_updated: 2026-06-29T19:34:09Z type: enum summary: "Controls the UI message under the GuiService." --- # UiMessageType Controls the UI message under the [GuiService](/docs/reference/engine/classes/GuiService.md). **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `UiMessageError` | 0 | The message error. | | `UiMessageInfo` | 1 | The message info. | --- name: UploadCaptureResult last_updated: 2026-06-29T19:34:09Z type: enum --- # UploadCaptureResult **Type:** enum ## Description Represents the result of uploading a capture to the asset system. ## Items | Name | Value | Description | |------|-------|-------------| | `Success` | 0 | | | `NeedPermission` | 1 | | | `CaptureModerated` | 2 | | | `CaptureNotInGallery` | 3 | | | `IneligibleCapture` | 4 | | | `UploadQuotaReached` | 5 | | | `UploadPending` | 6 | | | `UploadFailed` | 7 | | --- name: UsageContext last_updated: 2026-06-29T19:34:09Z type: enum --- # UsageContext **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | | | `Preview` | 1 | | --- name: UserCFrame last_updated: 2026-06-29T19:34:09Z type: enum summary: "Determines what body part is being tracked by a VR device, and what its CFrame actually is." --- # UserCFrame Determines what body part is being tracked by a VR device, and what its CFrame actually is. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Head` | 0 | The CFrame of the user's head. | | `LeftHand` | 1 | The CFrame of the user's left hand. | | `RightHand` | 2 | The CFrame of the user's right hand. | | `Floor` | 3 | | --- name: UserInputState last_updated: 2026-06-29T19:34:09Z type: enum summary: "This enum describes the state of an input that is currently or was recently performed." --- # UserInputState This enum describes the state of an input that is currently or was recently performed. **Type:** enum ## Description The `UserInputState` enum describes the state of an input that is currently or was recently performed. It is used by the [InputObject.UserInputState](/docs/reference/engine/classes/InputObject.md) property of the same name, as well as various [UserInputService](/docs/reference/engine/classes/UserInputService.md) and [GuiObject](/docs/reference/engine/classes/GuiObject.md) events. Depending on the [UserInputType](/docs/reference/engine/enums/UserInputType.md), input may follow states differently: - Simple button and key presses generally follow a simple `Begin` to `End` flow. Analog gamepad trigger buttons are similar to button presses but will use `Change` as the trigger pressure changes. - Mouse movement generally follows `Begin` (mouse-over) to `Change` (mouse movement) to `End` (mouse-leave). - Touch input behaves somewhat similarly to mouse movement. `Begin` and `End` occur when the user starts or ends touching the screen, respectively. The same [InputObject](/docs/reference/engine/classes/InputObject.md) is used for the same touch point. - Gamepad thumbstick controls will cause `Change` to occur each frame the position changes. ## Items | Name | Value | Description | |------|-------|-------------| | `Begin` | 0 | Occurs when an [InputObject](/docs/reference/engine/classes/InputObject.md) starts to interact with the experience. For example, a mouse button down, a key down, or when the player begins touching the screen. | | `Change` | 1 | Occurs each frame an [InputObject](/docs/reference/engine/classes/InputObject.md) has already begun interacting with the experience and part of its state is changing. For example, movement of the mouse position, a gamepad thumbstick movement, an analog gamepad trigger button change, or screen touch point change. | | `End` | 2 | Occurs when an [InputObject](/docs/reference/engine/classes/InputObject.md) finishes interacting with the experience. For example, a mouse button up, a key up, or when the player stops touching the screen. | | `Cancel` | 3 | A special circumstance state that indicates this input is no longer relevant, particularly with [ContextActionService](/docs/reference/engine/classes/ContextActionService.md). For example, binding two action-handling functions will cause the first to `Cancel` if an input was already in-progress when the second was bound. | | `None` | 4 | A state that should never be seen in an experience; essentially just marks the end of the enum. | --- name: UserInputType last_updated: 2026-06-29T19:34:09Z type: enum summary: "Describes the type of a user input event." --- # UserInputType Describes the type of a user input event. **Type:** enum ## Description The **UserInputType** enum describes the kind of input being performed (mouse, keyboard, gamepad, touch, etc). This enum is used by the [InputObject.UserInputType](/docs/reference/engine/classes/InputObject.md) property of the same name, as well as various [UserInputService](/docs/reference/engine/classes/UserInputService.md) and [GuiObject](/docs/reference/engine/classes/GuiObject.md) events. ## Items | Name | Value | Description | |------|-------|-------------| | `MouseButton1` | 0 | The left mouse button. | | `MouseButton2` | 1 | The right mouse button. | | `MouseButton3` | 2 | The middle mouse button. | | `MouseWheel` | 3 | The mouse wheel. | | `MouseMovement` | 4 | Movement of the mouse. Fires changed events each time the player's cursor position changes and when the move enters/leaves the game window. | | `Touch` | 7 | A tap on the screen from a mobile device. | | `Keyboard` | 8 | Key press on a keyboard. | | `Focus` | 9 | The client regaining focus of the Roblox window. | | `Accelerometer` | 10 | The accelerometer of a mobile device. | | `Gyro` | 11 | The Gyroscope of a mobile device. | | `Gamepad1` | 12 | Input from the 1st plugged in Gamepad. | | `Gamepad2` | 13 | Input from the 2nd plugged in Gamepad. | | `Gamepad3` | 14 | Input from the 3rd plugged in Gamepad. | | `Gamepad4` | 15 | Input from the 4th plugged in Gamepad. | | `Gamepad5` | 16 | Input from the 5th plugged in Gamepad. | | `Gamepad6` | 17 | Input from the 6th plugged in Gamepad. | | `Gamepad7` | 18 | Input from the 7th plugged in Gamepad. | | `Gamepad8` | 19 | Input from the 8th plugged in Gamepad. | | `TextInput` | 20 | Input of Text into a text-based [GuiObject](/docs/reference/engine/classes/GuiObject.md). Normally this is only a [TextBox](/docs/reference/engine/classes/TextBox.md). | | `InputMethod` | 21 | Text input from an input method editor (IME). [InputObjects](/docs/reference/engine/classes/InputObject.md) with this type aren't currently fired. | | `None` | 22 | Unknown UserInputType. | --- name: VRComfortSetting last_updated: 2026-06-29T19:34:09Z type: enum --- # VRComfortSetting **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Comfort` | 0 | | | `Normal` | 1 | | | `Expert` | 2 | | | `Custom` | 3 | | --- name: VRControllerModelMode last_updated: 2026-06-29T19:34:09Z type: enum --- # VRControllerModelMode **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Disabled` | 0 | | | `Transparent` | 1 | | --- name: VRDeviceType last_updated: 2026-06-29T19:34:09Z type: enum --- # VRDeviceType **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Unknown` | 0 | | | `OculusRift` | 1 | | | `HTCVive` | 2 | | | `ValveIndex` | 3 | | | `OculusQuest` | 4 | | --- name: VRLaserPointerMode last_updated: 2026-06-29T19:34:09Z type: enum --- # VRLaserPointerMode **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Disabled` | 0 | | | `Pointer` | 1 | | | `DualPointer` | 2 | | --- name: VRSafetyBubbleMode last_updated: 2026-06-29T19:34:09Z type: enum --- # VRSafetyBubbleMode **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `NoOne` | 0 | | | `OnlyFriends` | 1 | | | `Anyone` | 2 | | --- name: VRScaling last_updated: 2026-06-29T19:34:09Z type: enum --- # VRScaling **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `World` | 0 | Adjusts `Class.Camera.HeadScale' so that the world in VR is seen from the avatar's perspective. | | `Off` | 1 | Disable VR AutomaticScaling . | --- name: VRSessionState last_updated: 2026-06-29T19:34:09Z type: enum --- # VRSessionState **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Undefined` | 0 | | | `Idle` | 1 | | | `Visible` | 2 | | | `Focused` | 3 | | | `Stopping` | 4 | | --- name: VRTouchpad last_updated: 2026-06-29T19:34:09Z type: enum summary: "Used to universally identify a VR touchpad that is used by either the left, or right hand." --- # VRTouchpad Used to universally identify a VR touchpad that is used by either the left, or right hand. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Left` | 0 | Left hand touchpad. | | `Right` | 1 | Right hand touchpad. | --- name: VRTouchpadMode last_updated: 2026-06-29T19:34:09Z type: enum summary: "Used to identify the behavior of a specified VR touchpad." --- # VRTouchpadMode Used to identify the behavior of a specified VR touchpad. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Touch` | 0 | The touchpad is treated as ButtonB if it is the left touchpad, or ButtonA if it is the right touchpad. | | `VirtualThumbstick` | 1 | The touchpad will function as a gamepad joystick. | | `ABXY` | 2 | The touchpad will function as a means of inputting the standard ABXY gamepad input, dividing it up into 4 diagonal pie slices. | --- name: VelocityConstraintMode last_updated: 2026-06-29T19:34:09Z type: enum summary: "The velocity constraint mode property controls how the linear velocity of the attachment(s) is constrained." --- # VelocityConstraintMode The velocity constraint mode property controls how the linear velocity of the attachment(s) is constrained. **Type:** enum ## Description The velocity constraint mode sets how the attachment velocity is constrained. The velocity can be constrained to a line, a plane or a vector. See each mode for more details. ## Items | Name | Value | Description | |------|-------|-------------| | `Line` | 0 | The velocity component in the direction of the line is constrained to the specified value. The line direction is based on the [RelativeTo](/docs/reference/engine/classes/LinearVelocity.md) property: - [Attachment0](/docs/reference/engine/enums/ActuatorRelativeTo.md) - The line direction is the primary axis of [Attachment0](/docs/reference/engine/classes/Constraint.md). - [Attachment1](/docs/reference/engine/enums/ActuatorRelativeTo.md) - The line direction is the primary axis of [Attachment1](/docs/reference/engine/classes/Constraint.md). - [World](/docs/reference/engine/enums/ActuatorRelativeTo.md) - The line direction must be specified. | | `Plane` | 1 | The velocity components in the plane are constrained to the specified values. The plane tangents are based on the [RelativeTo](/docs/reference/engine/classes/LinearVelocity.md) property: - [Attachment0](/docs/reference/engine/enums/ActuatorRelativeTo.md) - The plane tangents are the two axes of [Attachment0](/docs/reference/engine/classes/Constraint.md). - [Attachment1](/docs/reference/engine/enums/ActuatorRelativeTo.md) - The plane tangents are the two axes of [Attachment1](/docs/reference/engine/classes/Constraint.md). - [World](/docs/reference/engine/enums/ActuatorRelativeTo.md) - The two plane tangents must be specified | | `Vector` | 2 | The velocity components must be equal to the vector components specified. The coordinate system of the vector is based on the [RelativeTo](/docs/reference/engine/classes/LinearVelocity.md) property: - [Attachment0](/docs/reference/engine/enums/ActuatorRelativeTo.md) - The vector components are in the coordinate system defined by the axes of [Attachment0](/docs/reference/engine/classes/Constraint.md). - [Attachment1](/docs/reference/engine/enums/ActuatorRelativeTo.md) - The vector components are in the coordinate system defined by the axes of [Attachment1](/docs/reference/engine/classes/Constraint.md). - [World](/docs/reference/engine/enums/ActuatorRelativeTo.md) - The coordinate system is in the world and the vector components must be specified. | --- name: VerticalAlignment last_updated: 2026-06-29T19:34:09Z type: enum summary: "Used by UIGridStyleLayout.VerticalAlignment to align the layout vertically within its parent." --- # VerticalAlignment Used by [UIGridStyleLayout.VerticalAlignment](/docs/reference/engine/classes/UIGridStyleLayout.md) to align the layout vertically within its parent. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Center` | 0 | Grid is aligned to the center of its parent along the **Y** axis. | | `Top` | 1 | Grid is aligned to the top edge of its parent. | | `Bottom` | 2 | Grid is aligned to the bottom edge of its parent. | --- name: VerticalScrollBarPosition last_updated: 2026-06-29T19:34:09Z type: enum summary: "This enum is used for ScrollingFrame.VerticalScrollBarPosition to indicate vertical scroll bar positioning." --- # VerticalScrollBarPosition This enum is used for [ScrollingFrame.VerticalScrollBarPosition](/docs/reference/engine/classes/ScrollingFrame.md) to indicate vertical scroll bar positioning. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Right` | 0 | Right side of the [ScrollingFrame](/docs/reference/engine/classes/ScrollingFrame.md). | | `Left` | 1 | Left side of the [ScrollingFrame](/docs/reference/engine/classes/ScrollingFrame.md). | --- name: VibrationMotor last_updated: 2026-06-29T19:34:09Z type: enum summary: "Describes various types of haptic motor sizes and locations." --- # VibrationMotor Describes various types of haptic motor sizes and locations. **Type:** enum ## Description Describes various types of haptic motor sizes and locations. Most devices have two motors, one that's small and another that's large. For devices with a single motor, such as mobile devices, only the large motor is implemented. ## Items | Name | Value | Description | |------|-------|-------------| | `Large` | 0 | Large motor. | | `Small` | 1 | Small motor. | | `LeftTrigger` | 2 | Left trigger motor. | | `RightTrigger` | 3 | Right trigger motor. | | `LeftHand` | 4 | Left hand motor for VR devices. | | `RightHand` | 5 | Right hand motor for VR devices. | --- name: VideoCaptureResult last_updated: 2026-06-29T19:34:09Z type: enum --- # VideoCaptureResult **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Success` | 0 | | | `OtherError` | 1 | | | `ScreenSizeChanged` | 2 | | | `TimeLimitReached` | 3 | | --- name: VideoCaptureStartedResult last_updated: 2026-06-29T19:34:09Z type: enum --- # VideoCaptureStartedResult **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Success` | 0 | | | `OtherError` | 1 | | | `CapturingAlready` | 2 | | | `NoDeviceSupport` | 3 | | | `NoSpaceOnDevice` | 4 | | --- name: VideoDeviceCaptureQuality last_updated: 2026-06-29T19:34:09Z type: enum --- # VideoDeviceCaptureQuality **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | | | `Low` | 1 | | | `Medium` | 2 | | | `High` | 3 | | --- name: VideoError last_updated: 2026-06-29T19:34:09Z type: enum --- # VideoError **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Ok` | 0 | | | `Eof` | 1 | | | `EAgain` | 2 | | | `BadParameter` | 3 | | | `AllocFailed` | 4 | | | `CodecInitFailed` | 5 | | | `CodecCloseFailed` | 6 | | | `DecodeFailed` | 7 | | | `ParsingFailed` | 8 | | | `Unsupported` | 9 | | | `Generic` | 10 | | | `DownloadFailed` | 11 | | | `StreamNotFound` | 12 | | | `EncodeFailed` | 13 | | | `CreateFailed` | 14 | | | `NoPermission` | 15 | | | `NoService` | 16 | | | `ReleaseFailed` | 17 | | | `Unknown` | 18 | | --- name: VideoSampleSize last_updated: 2026-06-29T19:34:09Z type: enum summary: "The size of textures produced by VideoSampler." --- # VideoSampleSize The size of textures produced by [VideoSampler](/docs/reference/engine/classes/VideoSampler.md). **Type:** enum ## Description Controls the resolution of textures produced by [VideoSampler](/docs/reference/engine/classes/VideoSampler.md). Use the smallest resolution that meets your needs to minimize memory usage and improve sampling performance. ## Items | Name | Value | Description | |------|-------|-------------| | `Small` | 0 | | | `Medium` | 1 | | | `Large` | 2 | | | `Full` | 3 | | --- name: ViewMode last_updated: 2026-06-29T19:34:09Z type: enum --- # ViewMode **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `None` | 0 | | | `GeometryComplexity` | 1 | | | `Transparent` | 2 | | | `Decal` | 3 | | --- name: VirtualCursorMode last_updated: 2026-06-29T19:34:09Z type: enum summary: "Enables Virtual Cursor mode within an experience." --- # VirtualCursorMode Enables Virtual Cursor mode within an experience. **Type:** enum ## Description Enables Virtual Cursor mode within an experience. By default, this property is disabled. ## Items | Name | Value | Description | |------|-------|-------------| | `Default` | 0 | The Virtual Cursor is in the default state. | | `Disabled` | 1 | The Virtual Cursor is disabled. | | `Enabled` | 2 | The Virtual Cursor is enabled. | --- name: VirtualInputMode last_updated: 2026-06-29T19:34:09Z type: enum --- # VirtualInputMode **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `None` | 0 | | | `Recording` | 1 | | | `Playing` | 2 | | --- name: VoiceChatDistanceAttenuationType last_updated: 2026-06-29T19:34:09Z type: enum summary: "Enum used for preset distance attenuation curve options in the default voice chat setup." --- # VoiceChatDistanceAttenuationType Enum used for preset distance attenuation curve options in the default voice chat setup. **Type:** enum ## Description Each value in this enum represents a distance attenuation curve that can be used by [VoiceChatService](/docs/reference/engine/classes/VoiceChatService.md) when creating the default voice chat setup using [AudioDeviceInput](/docs/reference/engine/classes/AudioDeviceInput.md) and [AudioEmitter](/docs/reference/engine/classes/AudioEmitter.md) objects. ## Items | Name | Value | Description | |------|-------|-------------| | `Inverse` | 0 | Represents a distance attenuation curve that follows the inverse-squared law. This is identical to the default distance attenuation of an [AudioEmitter](/docs/reference/engine/classes/AudioEmitter.md) instance. | | `Legacy` | 1 | Represents a linear-squared distance attenuation curve with a minimum distance of `7` and a maximum distance of `80`. This is identical to the distance attenuation used in the internal-only default voice setup that does not use [AudioDeviceInput](/docs/reference/engine/classes/AudioDeviceInput.md) and [AudioEmitter](/docs/reference/engine/classes/AudioEmitter.md) objects. | --- name: VoiceChatState last_updated: 2026-06-29T19:34:09Z type: enum --- # VoiceChatState **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Idle` | 0 | | | `Joining` | 1 | | | `JoiningRetry` | 2 | | | `Joined` | 3 | | | `Leaving` | 4 | | | `Ended` | 5 | | | `Failed` | 6 | | --- name: VoiceControlPath last_updated: 2026-06-29T19:34:09Z type: enum --- # VoiceControlPath **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Publish` | 0 | | | `Subscribe` | 1 | | | `Join` | 2 | | --- name: VolumetricAudio last_updated: 2026-06-29T19:34:09Z type: enum summary: "Controls how the engine renders volumetric audio effects." --- # VolumetricAudio Controls how the engine renders volumetric audio effects. **Type:** enum ## Description Controls how the engine renders volumetric audio effects, allowing sound to emanate from the surface of a [Part](/docs/reference/engine/classes/Part.md) rather than a single point in space. ## Items | Name | Value | Description | |------|-------|-------------| | `Disabled` | 0 | Volumetric audio is disabled; sound emanates from a single point. | | `Automatic` | 1 | Currently equivalent to [VolumetricAudio.Disabled](/docs/reference/engine/enums/VolumetricAudio.md). | | `Enabled` | 2 | Volumetric audio is enabled; sound emanates from the object's volume. | --- name: WaterDirection last_updated: 2026-06-29T19:34:09Z type: enum summary: "This Enum was once used to set the direction of Terrain water." --- # WaterDirection This Enum was once used to set the direction of [Terrain](/docs/reference/engine/classes/Terrain.md) water. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `NegX` | 0 | | | `X` | 1 | | | `NegY` | 2 | | | `Y` | 3 | | | `NegZ` | 4 | | | `Z` | 5 | | --- name: WaterForce last_updated: 2026-06-29T19:34:09Z type: enum summary: "The WaterForce Enum is used to work with Terrain water cells." --- # WaterForce The WaterForce Enum is used to work with [Terrain](/docs/reference/engine/classes/Terrain.md) water cells. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `None` | 0 | | | `Small` | 1 | | | `Medium` | 2 | | | `Strong` | 3 | | | `Max` | 4 | | --- name: WebSocketState last_updated: 2026-06-29T19:34:09Z type: enum --- # WebSocketState **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Connecting` | 0 | | | `Open` | 1 | | | `Closing` | 2 | | | `Closed` | 3 | | --- name: WebStreamClientState last_updated: 2026-06-29T19:34:09Z type: enum summary: "WebStreamClientState indicates the current state of a WebStreamClient object." --- # WebStreamClientState WebStreamClientState indicates the current state of a WebStreamClient object. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Connecting` | 0 | The client has sent a request to connect with the server and is waiting for a response. | | `Open` | 1 | The client is connected to the server, allowing for data to be streamed between the server and client. | | `Error` | 2 | An unrecoverable error has occured while setting up the connection orduring the connection lifetime, cutting off the stream. | | `Closed` | 3 | The connection has run to completion without issues, either closed naturally by the server or manually by the user. | --- name: WebStreamClientType last_updated: 2026-06-29T19:34:09Z type: enum summary: "Specifies what type of streaming to use when creating a WebStreamClient." --- # WebStreamClientType Specifies what type of streaming to use when creating a [WebStreamClient](/docs/reference/engine/classes/WebStreamClient.md). **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `SSE` | 0 | Traditional Server-Sent Events (SSE) client. Requires `text/event-stream` to be returned in the `Content-Type` header. Messages in the stream will follow the [event stream format](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#event_stream_format). | | `RawStream` | 1 | General purpose HTTP streaming client. It can connect to any server that provides streaming data transfer (e.g. [chunked encoding](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Transfer-Encoding)). It provides no guarantees about the stream format. | | `WebSocket` | 2 | [WebSocket](https://en.wikipedia.org/wiki/WebSocket) client that provides a bidirectional communication channel over a TCP connection. | --- name: WeldConstraintPreserve last_updated: 2026-06-29T19:34:09Z type: enum --- # WeldConstraintPreserve **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `All` | 0 | All [WeldConstraints](/docs/reference/engine/classes/WeldConstraint.md) will be returned in the recommended table. | | `None` | 1 | [WeldConstraints](/docs/reference/engine/classes/WeldConstraint.md) will be ignored and not returned in the recommended table. | | `Touching` | 2 | Only [WeldConstraints](/docs/reference/engine/classes/WeldConstraint.md) that connect touching parts in the performed operation will be returned in the recommended table. | --- name: WhenUserFirstPlayed last_updated: 2026-06-29T19:34:09Z type: enum summary: "Describes when a player first played the current experience, represented as a bucket." --- # WhenUserFirstPlayed Describes when a player first played the current experience, represented as a bucket. **Type:** enum ## Description The `WhenUserFirstPlayed` enum describes when a player first played the current experience, represented as a bucket. ## Items | Name | Value | Description | |------|-------|-------------| | `Unknown` | 0 | Segment data is unavailable. | | `Days0To30` | 1 | The player first played the experience within the last 30 days. | | `Days31To90` | 2 | The player first played the experience between 31 and 90 days ago. | | `Days91To180` | 3 | The player first played the experience between 91 and 180 days ago. | | `Days181To365` | 4 | The player first played the experience between 181 and 365 days ago. | | `Days366Plus` | 5 | The player first played the experience more than 365 days ago. | --- name: WhisperChatPrivacyMode last_updated: 2026-06-29T19:34:09Z type: enum --- # WhisperChatPrivacyMode **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `AllUsers` | 0 | | | `NoOne` | 1 | | --- name: WrapLayerAutoSkin last_updated: 2026-06-29T19:34:09Z type: enum --- # WrapLayerAutoSkin **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Disabled` | 0 | | | `EnabledPreserve` | 1 | | | `EnabledOverride` | 2 | | --- name: WrapLayerDebugMode last_updated: 2026-06-29T19:34:09Z type: enum summary: "The Studio-only property for quickly visualizing and debugging meshes with inner cage and outer cages." --- # WrapLayerDebugMode The Studio-only property for quickly visualizing and debugging meshes with inner cage and outer cages. **Type:** enum ## Description The Studio-only property for quickly visualizing and debugging meshes with inner and outer cages. This debug visualization only works when the WrapLayer is active and does not work if WrapLayer is not active or incorrectly configured. ## Items | Name | Value | Description | |------|-------|-------------| | `None` | 0 | This debug rendering mode does nothing. This is the default value. | | `BoundCage` | 1 | This debug mode visualizes corresponding cage mesh vertices bound to an underlying [WrapTarget](/docs/reference/engine/classes/WrapTarget.md). In order to find corresponding vertices between different cages, the Wrap Deformer algorithm uses UV matching. As long as your [WrapLayer](/docs/reference/engine/classes/WrapLayer.md) cage mesh share the same UV layout as [WrapTarget](/docs/reference/engine/classes/WrapTarget.md), its vertices will be marked as "bound". | | `LayerCage` | 2 | This debug mode shows the resulting layer mesh as a whole. The deformer will use vertex locations from the previous layer for all unbound vertices. | | `BoundCageAndLinks` | 3 | The same as BoundCage but also visualizes how bound vertices were moved. You can use this mode to identify where they belong on a previous layer and where they are located on the current layer. | | `Reference` | 4 | This debug mode shows the original inner cage mesh as it was created. | | `Rbf` | 5 | This debug mode visualizes the internal RBF solver state. You can estimate the wrap deformer's expected behavior by looking at this state. All renderable mesh vertices move from a "big sphere" to a "small sphere" along a corresponding line connecting two spheres. | | `OuterCage` | 6 | This debug mode shows the original outer cage mesh as it was created. | | `ReferenceMeshAfterMorph` | 7 | | | `HSROuterDetail` | 8 | | | `HSROuter` | 9 | | | `HSRInner` | 10 | | | `HSRInnerReverse` | 11 | | | `LayerCageFittedToBase` | 12 | | | `LayerCageFittedToPrev` | 13 | | | `PreWrapDeformerOuterCage` | 14 | | | `SkinningTransfer` | 15 | | --- name: WrapTargetDebugMode last_updated: 2026-06-29T19:34:09Z type: enum summary: "The Studio-only property for quickly visualizing and debugging meshes with only outer cages." --- # WrapTargetDebugMode The Studio-only property for quickly visualizing and debugging meshes with only outer cages. **Type:** enum ## Description The Studio-only property for quickly visualizing and debugging meshes with only outer cages. This debug visualization only works when the WrapTarget is active and does not work if WrapTarget is not active or incorrectly configured. ## Items | Name | Value | Description | |------|-------|-------------| | `None` | 0 | This debug rendering mode does nothing, and this is the default value. | | `TargetCageOriginal` | 1 | This debug mode shows the original cage mesh as it was created. | | `TargetCageCompressed` | 2 | This debug mode shows corresponding cage mesh compressed by clothing layers above it. This debug mode is intended to validate that the compression algorithm and corresponding cages work as intended. | | `TargetCageInterface` | 3 | This debug mode shows the resulting cage mesh for the corresponding Wrap Instance. This debug mode is intended to validate that the final deformed cage looks as intended. | | `TargetLayerCageOriginal` | 4 | The same as TargetCageOriginal but affects all WrapTargets that belong to the Wrap Deformer simultaneously. It doesn't matter which particular WrapTarget you enable this debug visualization option. | | `TargetLayerCageCompressed` | 5 | The same as TargetCageCompressed but affects all WrapTargets that belong to the Wrap Deformer simultaneously. It doesn't matter which particular WrapTarget you enable this debug visualization option. | | `TargetLayerInterface` | 6 | The same as TargetCageInterface but affects all WrapTargets that belong to the Wrap Deformer simultaneously. It doesn't matter which particular WrapTarget you enable this debug visualization option. | | `Rbf` | 7 | This debug mode visualizes the internal RBF solver state. You can estimate the wrap deformer expected behavior by looking at this state. All renderable mesh vertices move from a "big sphere" to a "small sphere" along a corresponding line connecting two spheres. | | `OuterCageDetail` | 8 | | | `PreWrapDeformerCage` | 9 | | --- name: ZIndexBehavior last_updated: 2026-06-29T19:34:09Z type: enum summary: "Used by LayerCollector.ZIndexBehavior to control how the GuiObject.ZIndex property behaves for descendants." --- # ZIndexBehavior Used by [LayerCollector.ZIndexBehavior](/docs/reference/engine/classes/LayerCollector.md) to control how the [GuiObject.ZIndex](/docs/reference/engine/classes/GuiObject.md) property behaves for descendants. **Type:** enum ## Items | Name | Value | Description | |------|-------|-------------| | `Global` | 0 | The ZIndex property overrides the default value computed from the depth in the hierarchy. | | `Sibling` | 1 | The ZIndex property controls the order in which the GuiObject renders relative to its siblings. | --- title: "Roblox Engine enums" url: /docs/en-us/reference/engine/enums last_updated: 2026-06-29T19:34:09Z description: "Enums are groups of constants that define preset values that you can use with other APIs." --- # Roblox Engine enums --- name: Luau globals last_updated: 2026-06-29T19:34:09Z type: global summary: "A list of functions and variables that are native to Luau." --- # Luau globals A list of functions and variables that are native to Luau. **Type:** global ## Description The following is a list of functions and variables that are native to Luau. These functions can be used in a standard installation of both [Luau](https://luau.org) and [Lua 5.1.4](https://www.lua.org/manual/5.1/), though there are some differences in how some of these work on Roblox. ## Properties ### _G **Type:** `Array` A table that is shared between all scripts of the same context level. ### _VERSION **Type:** `string` A global variable (not a function) that holds a string containing the current interpreter version. ## Functions ### assert **Signature:** `assert(value: Variant, errorMessage?: string): Variant` Throws an error if the provided `value` is `false` or `nil`. If the assertion passes, it returns all values passed to it. ```lua local product = 90 * 4 assert(product == 360, "Oh dear, multiplication is broken") -- The line above does nothing, because 90 times 4 is 360 ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `value` | `Variant` | | The value that will be asserted against. | | `errorMessage` | `string` | `assertion failed!` | The text that will be shown in the error if the assertion fails. | **Returns:** `Variant` ### error **Signature:** `error(message: Variant, level?: int): ()` Terminates the last protected function called and outputs `message` as an error message. If the function containing the error is not called in a protected function such as `pcall()`, then the script which called the function will terminate. The error function itself never returns and acts like a script error. The `level` argument specifies how to get the error position. With level `1` (the default), the error position is where the error function was called. Level 2 points the error to where the function that called error was called; and so on. Passing a level `0` avoids the addition of error position information to the message. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `message` | `Variant` | | The error message to display. | | `level` | `int` | `1` | The level of information that should be printed. Defaults to 1. | **Returns:** `()` ### gcinfo **Signature:** `gcinfo(): number` Returns the total memory heap size in kilobytes. The number reflects the current heap consumption from the operating system perspective, which fluctuates over time as garbage collector frees objects. **Returns:** `number` ### getmetatable **Signature:** `getmetatable(t: Variant): Variant` Returns the metatable of the given table `t` if it has one, otherwise returns `nil`. If `t` does have a metatable, and the `__metatable` metamethod is set, it returns that value instead. ```lua -- Demonstrate getmetatable: local meta = {} local t = setmetatable({}, meta) print(getmetatable(t) == meta) --> true -- Make the original metatable unrecoverable by setting the __metatable metamethod: meta.__metatable = "protected" print(getmetatable(t)) --> protected ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `t` | `Variant` | | The object to fetch the metatable of. | **Returns:** `Variant` ### ipairs **Signature:** `ipairs(t: Array): function, Array, int` Returns three values: an iterator function, the table `t` and the number `0`. Each time the iterator function is called, it returns the next numerical index-value pair in the table. When used in a generic for-loop, the return values can be used to iterate over each numerical index in the table: ```lua local fruits = {"apples", "oranges", "kiwi"} for index, fruit in ipairs(fruits) do print(index, fruit) --> 1 apples, 2 oranges, 3 kiwi, etc... end ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `t` | `Array` | | A table whose elements are to be iterated over. | **Returns:** `function`, `Array`, `int` ### loadstring **Signature:** `loadstring(contents: string, chunkname: string): Variant` Loads Luau code from a string and returns it as a function. Unlike standard Lua 5.1, Roblox's Luau cannot load the compiled bytecode using `loadstring()`. `loadstring()` is disabled by default. For guidance around enabling it, see [ServerScriptService](/docs/reference/engine/classes/ServerScriptService.md). **WARNING:** This method disables certain Luau optimizations on the returned function. Extreme caution should be taken when using [LuaGlobals.loadstring()](/docs/reference/engine/globals/LuaGlobals.md); if your intention is to allow users to run code in your experience, make sure to protect the returned function's environment by using [LuaGlobals.getfenv()](/docs/reference/engine/globals/LuaGlobals.md) and [LuaGlobals.setfenv()](/docs/reference/engine/globals/LuaGlobals.md). **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `contents` | `string` | | The specified string to be loaded as Luau code. | | `chunkname` | `string` | | An optional chunk name for error messages and debug information. If unspecified, Luau uses the `contents` string. | **Returns:** `Variant` ### newproxy **Signature:** `newproxy(addMetatable?: bool): userdata` Creates a blank `userdata`, with the option for it to have a metatable. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `addMetatable` | `bool` | `false` | | **Returns:** `userdata` ### next **Signature:** `next(t: table, lastKey?: Variant): Variant, Variant` Returns the first key/value pair in the array. If a `lastKey` argument was specified then returns the next element in the array based on the key that provided. The order in which the indices are enumerated is not specified, even for numeric indices. To traverse a table in numeric order, use a numerical for loop or `ipairs`. The behavior of next is undefined if, during the traversal, you assign any value to a non-existent field in the table. You may, however, modify existing fields. In particular, you may clear existing fields. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `t` | `table` | | The array to be traversed. | | `lastKey` | `Variant` | `nil` | The last key that was previously retrieved from a call to next. | **Returns:** `Variant`, `Variant` ### pairs **Signature:** `pairs(t: table): function, table` Returns an iterator function, the passed table `t`, and `nil`, so that the construction will iterate over all key/value pairs of that table when used in a generic `for` loop: ```lua local scores = { ["John"] = 5, ["Sally"] = 10 } for name, score in pairs(scores) do print(name .. " has score: " .. score) end ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `t` | `table` | | An array or dictionary table to iterate over. | **Returns:** `function`, `table` ### pcall **Signature:** `pcall(func: function, args: Tuple): bool, Variant` Calls the function `func` with the given arguments in protected mode. This means that any error inside `func` is not propagated; instead, `pcall()` catches the error and returns a status code. Its first result is the status code (a boolean), which is true if the call succeeds without errors. In such case, `pcall()` also returns all results from the call, after this first result. In case of any error, `pcall()` returns false plus the error message. ```lua local function divideByFive(n: number): number return n / 5 end local success, errorMessage = pcall(divideByFive, "notANumber") -- Results in error... if success then -- Handle successful response... else warn("Error message:", errorMessage) end ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `func` | `function` | | The function to be called in protected mode. | | `args` | `Tuple` | | The arguments to send to `func` when executing. | **Returns:** `bool`, `Variant` ### print **Signature:** `print(params: Tuple): ()` Receives any number of arguments, and prints their values to the output. `print` is not intended for formatted output, but only as a quick way to show a value, typically for debugging. For a formatted output, use [string.format()](/docs/reference/engine/globals/string.md). On Roblox, `print` does not call `tostring`, but the `__tostring` metamethod still fires if the table has one. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `params` | `Tuple` | | Any number of arguments to be outputted. | **Returns:** `()` ### rawequal **Signature:** `rawequal(v1: Variant, v2: Variant): bool` Checks whether `v1` is equal to `v2`, without invoking any metamethods. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `v1` | `Variant` | | The first variable to compare. | | `v2` | `Variant` | | The second variable to compare. | **Returns:** `bool` ### rawget **Signature:** `rawget(t: table, index: Variant): Variant` Gets the real value of `table[index]`, without invoking any metamethods. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `t` | `table` | | The table to be referenced. | | `index` | `Variant` | | The index to get from `t`. | **Returns:** `Variant` ### rawlen **Signature:** `rawlen(t: table): table` Returns the length of the string or table, without invoking any metamethods. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `t` | `table` | | The table to be referenced. | **Returns:** `table` ### rawset **Signature:** `rawset(t: table, index: Variant, value: Variant): table` Sets the real value of `table[index]` to a given `value`, without invoking any metamethod. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `t` | `table` | | The table to be referenced. | | `index` | `Variant` | | The index to set in `t` to a specified `value`. Must be different from `nil`. | | `value` | `Variant` | | The value to be set to a specified `index` in table `t`. | **Returns:** `table` ### require **Signature:** `require(module: ModuleScript | string | number): Variant` Runs the supplied [ModuleScript](/docs/reference/engine/classes/ModuleScript.md) and returns what the [ModuleScript](/docs/reference/engine/classes/ModuleScript.md) returned (usually a table or a function). If the [ModuleScript](/docs/reference/engine/classes/ModuleScript.md) has not been run yet, it will be executed. If a string path is provided instead, it is first resolved to a [ModuleScript](/docs/reference/engine/classes/ModuleScript.md) relative to the script that called [LuaGlobals.require()](/docs/reference/engine/globals/LuaGlobals.md), mimicking the Unix-like semantics of Luau's `require()` expression. Specifically, require-by-string's resolution semantics are as follows: - Paths with the `./` prefix begin resolution at `script.Parent`. - Paths with the `../` prefix begin resolution at `script.Parent.Parent`. - Paths with the `@self/` prefix begin resolution at `script`. - Paths with the `@game/` prefix begin resolution at `game`. - Each non-prefix component in a given path corresponds to a child instance of the previous component. The exception to this is the `..` component, which corresponds to the parent of the previous component. - If the desired [ModuleScript](/docs/reference/engine/classes/ModuleScript.md) is not present at the time that [LuaGlobals.require()](/docs/reference/engine/globals/LuaGlobals.md) is called, the call will fail and throw an error. In other words, require-by-string is non-blocking: it does not implicitly wait for a [ModuleScript](/docs/reference/engine/classes/ModuleScript.md) to be created. To illustrate this, each pair of [LuaGlobals.require()](/docs/reference/engine/globals/LuaGlobals.md) expressions in the example below contains two functionally equivalent calls. Redundant parentheses have been added to clarify exactly how each path component maps onto an instance. ```lua -- "./" prefix is equivalent to script.Parent require("./MySibling") require((script.Parent).(MySibling)) -- "../" prefix is equivalent to script.Parent.Parent require("../SiblingOfMyParent") require((script.Parent.Parent).(SiblingOfMyParent)) -- "../" can be chained to go up multiple levels require("../../SiblingOfMyGrandparent") require((script.Parent.Parent).(Parent).(SiblingOfMyGrandparent)) -- "@self" prefix corresponds to the script itself require("@self/MyChild") require((script).(MyChild)) -- "@game" prefix corresponds to the DataModel root require("@game/ReplicatedStorage/Shared/MyModule") require((game).(ReplicatedStorage).(Shared).(MyModule)) ``` Once the return object is created by an **initial** [LuaGlobals.require()](/docs/reference/engine/globals/LuaGlobals.md) call of a [ModuleScript](/docs/reference/engine/classes/ModuleScript.md), future [LuaGlobals.require()](/docs/reference/engine/globals/LuaGlobals.md) calls for the same [ModuleScript](/docs/reference/engine/classes/ModuleScript.md) (on the same side of the client-server boundary) will not run the code again. Instead, a reference to the **same** return object created by the initial [LuaGlobals.require()](/docs/reference/engine/globals/LuaGlobals.md) call will be supplied. This behavior allows for the sharing of values across different scripts, as multiple [LuaGlobals.require()](/docs/reference/engine/globals/LuaGlobals.md) calls from different scripts will reference the same returned object. If the returned object is a table, any values stored within the table are shared and accessible by any script requiring that [ModuleScript](/docs/reference/engine/classes/ModuleScript.md). As noted above, the "object sharing" behavior does not cross the client-server boundary. This means that if a [ModuleScript](/docs/reference/engine/classes/ModuleScript.md) is accessible to both the client **and** server (such as by being placed in [ReplicatedStorage](/docs/reference/engine/classes/ReplicatedStorage.md)) and [LuaGlobals.require()](/docs/reference/engine/globals/LuaGlobals.md) is called from both a [LocalScript](/docs/reference/engine/classes/LocalScript.md) as well as a [Script](/docs/reference/engine/classes/Script.md), the code in the [ModuleScript](/docs/reference/engine/classes/ModuleScript.md) will be run twice, and the [LocalScript](/docs/reference/engine/classes/LocalScript.md) will receive a distinct return object from the one received by the [Script](/docs/reference/engine/classes/Script.md). Also note that if the [ModuleScript](/docs/reference/engine/classes/ModuleScript.md) the user wants to run has been uploaded to Roblox (with the instance's name being `MainModule`), it can be loaded by using the [LuaGlobals.require()](/docs/reference/engine/globals/LuaGlobals.md) function on the asset ID of the [ModuleScript](/docs/reference/engine/classes/ModuleScript.md), though only on the server. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `module` | `ModuleScript | string | number` | | The [ModuleScript](/docs/reference/engine/classes/ModuleScript.md) that will be executed to retrieve the return value it provides, or a reference to one (a string path or asset ID). | **Returns:** `Variant` — What the [ModuleScript](/docs/reference/engine/classes/ModuleScript.md) returned (usually a table or a function). ### select **Signature:** `select(index: Variant, args: Tuple): Tuple` Returns all arguments after argument number `index`. If negative, it will return from the end of the argument list. ```lua print(select(2, "A", "B", "C")) --> B C print(select(-1, "A", "B", "C")) --> C ``` If the `index` argument is set to `"#"`, the number of arguments that were passed after it is returned. ```lua print(select("#", "A", "B", "C")) --> 3 ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `index` | `Variant` | | The index of the argument to return all arguments after in `args`. If it's set to `"#"`, the number of arguments that were passed after it is returned. | | `args` | `Tuple` | | A tuple of arguments. | **Returns:** `Tuple` ### setmetatable **Signature:** `setmetatable(t: table, newMeta: Variant): table` Sets the metatable for the given table `t` to `newMeta`. If `newMeta` is `nil`, the metatable of `t` is removed. Finally, this function returns the table `t` which was passed to it. If `t` already has a metatable whose `__metatable` metamethod is set, calling this on `t` raises an error. ```lua local meta = {__metatable = "protected"} local t = {} setmetatable(t, meta) -- This sets the metatable of t -- We now have a table, t, with a metatable. If we try to change it... setmetatable(t, {}) --> Error: cannot change a protected metatable ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `t` | `table` | | The table to set the metatable of. | | `newMeta` | `Variant` | | If `nil`, the metatable of the given table `t` is removed. Otherwise, the metatable to set for the given table `t`. | **Returns:** `table` ### tonumber **Signature:** `tonumber(arg: Variant, base?: int): Variant` Attempts to convert the arg into a number with a specified base to interpret the value in. If it cannot be converted, this function returns `nil`. The base may be any integer between 2 and 36, inclusive. In bases above 10, the letter 'A' (in either upper or lower case) represents 10, 'B' represents 11, and so forth, with 'Z' representing 35. In base 10 (the default), the number may have a decimal part, as well as an optional exponent part. In other bases, only unsigned integers are accepted. If a string begins with `0x` and a base is not provided, the `0x` is trimmed and the base is assumed to be 16, or hexadecimal. ```lua print(tonumber("1337")) --> 1337 (assumes base 10, decimal) print(tonumber("1.25")) --> 1.25 (base 10 may have decimal portions) print(tonumber("3e2")) --> 300 (base 10 may have exponent portion, 3 × 10 ^ 2) print(tonumber("25", 8)) --> 21 (base 8, octal) print(tonumber("0x100")) --> 256 (assumes base 16, hexadecimal) print(tonumber("roblox")) --> nil (does not raise an error) -- Tip: use with assert if you would like unconvertable numbers to raise an error print(assert(tonumber("roblox"))) --> Error: assertion failed ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `arg` | `Variant` | | The object to be converted into a number. | | `base` | `int` | `10` | The numerical base to convert `arg` into. | **Returns:** `Variant` ### tostring **Signature:** `tostring(e: Variant): string` Receives an argument of any type and converts it to a string in a reasonable format. For complete control of how numbers are converted, use string.format. If the metatable of `e` has a `__tostring` metamethod, then it will be called with `e` as the only argument and will return the result. ```lua local isRobloxCool = true -- Convert the boolean to a string then concatenate: print("Roblox is cool: " .. tostring(isRobloxCool)) --> Roblox is cool: true ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `e` | `Variant` | | The object to be converted into a string. | **Returns:** `string` ### type **Signature:** `type(v: Variant): string` Returns the type of its only argument, coded as a string. The possible results of this function are `"nil"` (a string, not the value nil), `"number"`, `"string"`, `"boolean"`, `"table"`, `"vector"`, `"function"`, `"thread"`, `"userdata"`, and `"buffer"`. The `buffer` and `vector` primitives are additions from Luau, not from Lua. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `v` | `Variant` | | The object to return the type of. | **Returns:** `string` ### unpack **Signature:** `unpack(list: table, i?: int, j?: int): Variant` Returns the elements from the given table. By default, `i` is 1 and `j` is the length of `list`, as defined by the length operator. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `list` | `table` | | The list of elements to be unpacked. | | `i` | `int` | `1` | The index of the first element to unpack. | | `j` | `int` | `#list` | The index of the last element to unpack. | **Returns:** `Variant` ### xpcall **Signature:** `xpcall(f: function, err: function, args: Tuple): bool, Variant` This function is similar to [LuaGlobals.pcall()](/docs/reference/engine/globals/LuaGlobals.md), except that you can set a new error handler. `xpcall()` calls function `f` in protected mode, using `err` as the error handler, and passes a list of arguments. Any error inside `f` is not propagated; instead, `xpcall()` catches the error, calls the `err` function with the original error object, and returns a status code. Its first result is the status code (a boolean), which is true if the call succeeds without errors. In this case, `xpcall()` also returns all results from the call, after this first result. In case of any error, `xpcall()` returns false plus the result from `err`. Unlike [LuaGlobals.pcall()](/docs/reference/engine/globals/LuaGlobals.md), the `err` function preserves the stack trace of function `f`, which can be inspected using [debug.info()](/docs/reference/engine/globals/debug.md) or [debug.traceback()](/docs/reference/engine/globals/debug.md). **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `f` | `function` | | The function to be called in protected mode. | | `err` | `function` | | The function to be used as an error handle if xpcall catches an error. | | `args` | `Tuple` | | | **Returns:** `bool`, `Variant` ### collectgarbage *(deprecated)* **Signature:** `collectgarbage(operation: string): Variant` Performs the specified operation of the garbage collector. Note that Roblox's Luau sandbox only allows the `count` option to be used (the total memory in use by Luau, in kilobytes), as other options can interfere with existing processes. Effectively, this makes [LuaGlobals.gcinfo()](/docs/reference/engine/globals/LuaGlobals.md) a superior alternative that should be used instead. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `operation` | `string` | | The name of the operation that should be performed by the garbage collector. | **Returns:** `Variant` ### getfenv *(deprecated)* **Signature:** `getfenv(stack?: Variant): table` > **Deprecated:** This function allows uncontrolled change of the global/function environment and disables script optimizations. Changes to the environment are not tracked by the script analysis tooling and may result in missing or incorrect warnings. As a replacement, consider using [debug.info()](/docs/reference/engine/globals/debug.md) instead. Returns the current environment in use by the caller, as a dictionary. - If provided with a function, the environment of the function will be returned. - If provided with an integer, [LuaGlobals.getfenv()](/docs/reference/engine/globals/LuaGlobals.md) will provide the environment of the function at the provided stack level: Level 1 is the function calling [LuaGlobals.getfenv()](/docs/reference/engine/globals/LuaGlobals.md). If `stack` is `0`, [LuaGlobals.getfenv()](/docs/reference/engine/globals/LuaGlobals.md) returns the global environment of the current script. When using [LuaGlobals.getfenv()](/docs/reference/engine/globals/LuaGlobals.md) to get the current environment of a script, it will return the same table every time within the specific thread. **WARNING:** This function allows uncontrolled change of the global/function environment and disables script optimizations. Changes to the environment are not tracked by the script analysis tooling and may result in missing or incorrect warnings. As a replacement, consider using [debug.info()](/docs/reference/engine/globals/debug.md) instead. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `stack` | `Variant` | `1` | The stack level (int) of the environment to be returned; or the function whose environment will be returned. | **Returns:** `table` ### setfenv *(deprecated)* **Signature:** `setfenv(f: Variant, fenv: table): Variant` > **Deprecated:** This function allows uncontrolled change of the global/function environment and disables script optimizations. Changes to the environment are not tracked by the script analysis tooling and may result in missing or incorrect warnings. Sets the environment to be used by the given function. `f` can be a function or a number that specifies the function at that stack level: Level 1 is the function calling `setfenv()`. `setfenv()` returns the given function. If `f` is `0`, then `setfenv()` changes the environment of the running thread and returns no values. **WARNING:** This function allows uncontrolled change of the global/function environment and disables script optimizations. Changes to the environment are not tracked by the script analysis tooling and may result in missing or incorrect warnings. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `f` | `Variant` | | Either a function or a number that specifies the function at that stack level. | | `fenv` | `table` | | The function environment table to set for the specified function. | **Returns:** `Variant` --- name: Roblox globals last_updated: 2026-06-29T19:34:09Z type: global summary: "Built-in functions and constants unique to Roblox." --- # Roblox globals Built-in functions and constants unique to Roblox. **Type:** global ## Description Roblox provides several unique built-in functions and variables in its embedding of Luau. These are only found on Roblox and are not packaged by default with Luau or Lua. ## Properties ### Enum **Type:** `Enums` A reference to the Enums data type, which stores all of the available enums that can be used on Roblox. ### game **Type:** `DataModel` A reference to the [DataModel](/docs/reference/engine/classes/DataModel.md), which is the root Instance of Roblox's parent/child hierarchy. ### plugin **Type:** `Plugin` A reference to the [Plugin](/docs/reference/engine/classes/Plugin.md) object that represents the plugin being run from this [Script](/docs/reference/engine/classes/Script.md). This reference exists only in the context where a script is executed as a plugin and is not passed to [ModuleScripts](/docs/reference/engine/classes/ModuleScript.md) within the plugin. To use this reference in a [ModuleScript](/docs/reference/engine/classes/ModuleScript.md), you must explicitly pass it. ```lua assert(plugin, "This script must be run as a plugin!") -- Code beyond this point will execute only if the script is run as a plugin ``` ### shared **Type:** `Array` A table that is shared across all scripts that share the same execution context level. This serves the exact same purpose as `_G`. ### script **Type:** `LuaSourceContainer` A reference to the script object that is executing the code you are writing. It can be either a [Script](/docs/reference/engine/classes/Script.md), a [LocalScript](/docs/reference/engine/classes/LocalScript.md), or a [ModuleScript](/docs/reference/engine/classes/ModuleScript.md). This variable is not available when executing code from Roblox Studio's command bar. ### workspace **Type:** `Workspace` A reference to the [Workspace](/docs/reference/engine/classes/Workspace.md) service, which contains all of the physical components of a Roblox world. ## Functions ### PluginManager **Signature:** `PluginManager(): PluginManager` Returns the [PluginManager](/docs/reference/engine/classes/PluginManager.md) which is a deprecated singleton that was previously required to create plugins. It still has some applicable uses, such as if you need to create a [Plugin](/docs/reference/engine/classes/Plugin.md) object from Studio's [Command Bar](/docs/en-us/studio/ui-overview.md#command-bar). **Returns:** `PluginManager` ### settings **Signature:** `settings(): GlobalSettings` Returns the [GlobalSettings](/docs/reference/engine/classes/GlobalSettings.md) object, which can be used to access the settings objects that are used in Roblox Studio's settings menu. **Returns:** `GlobalSettings` ### tick **Signature:** `tick(): number` Returns how much time has elapsed, in seconds, since the Unix epoch, on the current local session's computer. The Unix epoch is represented by 00:00:00 on 1 January 1970. `tick()` isn't officially deprecated, but has a variety of issues. It can be off by up to one second and returns inconsistent results across time zones and operating systems. Use [os.time()](/docs/reference/engine/globals/os.md), [os.clock()](/docs/reference/engine/globals/os.md), or [RobloxGlobals.time()](/docs/reference/engine/globals/RobloxGlobals.md) instead. Also consider [DateTime.UnixTimestamp](/docs/reference/engine/datatypes/DateTime.md) and [DateTime.UnixTimestampMillis](/docs/reference/engine/datatypes/DateTime.md). **Returns:** `number` ### time **Signature:** `time(): number` Returns the amount of time, in seconds, that has elapsed since the current game instance started running. If the current game instance is not running, this will be `0`. If [Workspace.AuthorityMode](/docs/reference/engine/classes/Workspace.md) is [AuthorityMode.Server](/docs/reference/engine/enums/AuthorityMode.md), this value is synchronized between client and server. **Returns:** `number` ### typeof **Signature:** `typeof(object: Variant): string` Returns the type of the object specified, as a string. This function is more accurate than Luau's native `type` function, as it does not denote Roblox-specific types as `userdata`. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `object` | `Variant` | | The Luau type that will have its type checked. | **Returns:** `string` ### UserSettings **Signature:** `UserSettings(): UserSettings` Returns the [UserSettings](/docs/reference/engine/classes/UserSettings.md) object, which is used to read information from the current user's game menu settings. **Returns:** `UserSettings` ### warn **Signature:** `warn(params: Tuple): ()` Behaves identically to Luau's print function, except the output is styled as a warning, with yellow text and a timestamp. This function accepts any number of arguments, and will attempt to convert them into strings which will then be joined together with spaces between them. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `params` | `Tuple` | | This function accepts any number of arguments, and will attempt to convert them into strings which will then be joined together with spaces between them. | **Returns:** `()` ### delay *(deprecated)* **Signature:** `delay(delayTime: number, callback: function): ()` > **Deprecated:** This method has been superseded by [task.delay()](/docs/reference/engine/globals/task.md) and should not be used for future work. Schedules a function to be executed after `delayTime` seconds have passed, without yielding the current thread. This function allows multiple Luau threads to be executed in parallel from the same stack. The delay will have a minimum duration of 29 milliseconds, but this minimum may be higher depending on the target framerate and various throttling conditions. If the `delayTime` parameter is not specified, the minimum duration will be used. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `delayTime` | `number` | | The amount of time that this function will be queued before being executed. | | `callback` | `function` | | The function that will be executed once `delayTime` seconds have passed. | **Returns:** `()` ### DebuggerManager *(deprecated)* **Signature:** `DebuggerManager(): DebuggerManager` > **Deprecated:** The `DebuggerManager` is obsolete and serves little to no use case for developers. Returns the legacy `DebuggerManager` class which acts as an interface for Roblox's Luau debugger feature. This function is not recognized by Luau's analysis tool and will raise an undefined global warning. **Returns:** `DebuggerManager` ### elapsedTime *(deprecated)* **Signature:** `elapsedTime(): number` Returns how much time has elapsed since the current instance of Roblox was started. In Roblox Studio, this begins counting up from the moment Roblox Studio starts running, not just when opening a place. **Returns:** `number` ### printidentity *(deprecated)* **Signature:** `printidentity(prefix?: string): ()` Prints `Current identity is [ID]` to the output, where [ID] corresponds to the current thread's security context level. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `prefix` | `string` | `Current identity is` | | **Returns:** `()` ### spawn *(deprecated)* **Signature:** `spawn(callback: function): ()` > **Deprecated:** This method has been superseded by [task.spawn()](/docs/reference/engine/globals/task.md) and should not be used for future work. Runs the specified callback function in a separate thread, without yielding the current thread. The function will be executed the next time Roblox's Task Scheduler runs an update cycle. This delay will take at least 29 milliseconds but can arbitrarily take longer, depending on the target framerate and various throttling conditions. The callback function is invoked with two arguments: 1. The first being the amount of time which elapsed from when spawn was called to when the function was invoked. 2. The second being equivalent to elapsedTime() or roughly how long the engine has been running. ```lua spawn(print) -- 0.0079617658390703 451.55683163643 ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `callback` | `function` | | The function that will be executed. | **Returns:** `()` ### stats *(deprecated)* **Signature:** `stats(): Stats` Returns the [Stats](/docs/reference/engine/classes/Stats.md) service. It is preferred that developers use [ServiceProvider:GetService()](/docs/reference/engine/classes/ServiceProvider.md) to retrieve it instead. **Returns:** `Stats` ### version *(deprecated)* **Signature:** `version(): string` Returns the current version of Roblox as a string. The integers in the version string are separated by periods, and each integers represent the following, in order: - Generation - The current generation of the application shell that is hosting the client. - Version - The current release version of Roblox. - Patch - The current patch number for this version of Roblox. - Commit - The ID of the last internal commit that was accepted into this version of the client. **Returns:** `string` ### wait *(deprecated)* **Signature:** `wait(seconds?: number): number, number` > **Deprecated:** This method has been superseded by [task.wait()](/docs/reference/engine/globals/task.md) and should not be used for future work. Yields the current thread until the specified amount of seconds have elapsed. The delay will have a minimum duration of 29 milliseconds, but this minimum may be higher depending on the target framerate and various throttling conditions. If the `seconds` parameter is not specified, the minimum duration will be used. This function returns: - Actual time yielded (in seconds). - Total time since the software was initialized (in seconds). **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `seconds` | `number` | `0.03` | Specifies how long the thread should yield for. | **Returns:** `number`, `number` ### ypcall *(deprecated)* **Signature:** `ypcall(f: function, args: Tuple): bool, Variant` Legacy function to work around an old task scheduling limitation of [LuaGlobals.pcall()](/docs/reference/engine/globals/LuaGlobals.md); should not be used for new work (use [LuaGlobals.pcall()](/docs/reference/engine/globals/LuaGlobals.md) instead). **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `f` | `function` | | The function to be called in protected mode. | | `args` | `Tuple` | | | **Returns:** `bool`, `Variant` --- title: "Roblox Engine globals" url: /docs/en-us/reference/engine/globals last_updated: 2026-06-29T19:34:09Z description: "Globals are functions or properties that you can use in any script. Luau globals are native to Luau while Roblox globals are found only on Roblox." --- # Roblox Engine globals --- name: bit32 last_updated: 2026-06-29T19:34:09Z type: library summary: "A library of functions to perform bitwise operations." --- # bit32 A library of functions to perform bitwise operations. **Type:** library ## Description This library provides functions to perform bitwise operations. #### Number Limitations This library treats numbers as unsigned 32-bit integers; numbers will be converted to this before being used (see image below). Numbers with decimal numbers are rounded to the nearest whole number. ![32-bit integer conversion (in hexadecimal)](../../../assets/engine-api/libraries/bit32/32-Bit-Restriction.png) ## Functions ### bit32.arshift **Signature:** `bit32.arshift(x: number, disp: number): number` Returns the number `x` shifted `disp` bits to the right. The number `disp` may be any representable integer. Negative displacements shift to the left. This shift operation is what is called arithmetic shift. Vacant bits on the left are filled with copies of the higher bit of `x`; vacant bits on the right are filled with zeros. In particular, displacements with absolute values higher than 31 result in zero or 0xFFFFFFFF (all original bits are shifted out). **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `number` | | The number whose bits shall be shifted. | | `disp` | `number` | | The integer number of bits to shift by. | **Returns:** `number` ### bit32.band **Signature:** `bit32.band(numbers: Tuple): number` Returns the bitwise AND of all provided numbers. Each bit is tested against the following truth table: | A | B | Output | | --- | --- | --- | | 0 | 0 | 0 | | 1 | 0 | 0 | | 0 | 1 | 0 | | 1 | 1 | 1 | ![Bitwise AND of 3 numbers](../../../assets/engine-api/libraries/bit32/AND.png) **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `numbers` | `Tuple` | | | **Returns:** `number` ### bit32.bnot **Signature:** `bit32.bnot(x: number): number` Returns the bitwise negation of `x`. ![Negation of a provided number](../../../assets/engine-api/libraries/bit32/NOT.png) For any integer `x`, the following identity holds: ```lua assert(bit32.bnot(x) == (-1 - x) % 2^32) ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `number` | | | **Returns:** `number` ### bit32.bor **Signature:** `bit32.bor(numbers: Tuple): number` Returns the bitwise OR of all provided numbers. Each bit is tested against the following truth table: | A | B | Output | | --- | --- | --- | | 0 | 0 | 0 | | 1 | 0 | 1 | | 0 | 1 | 1 | | 1 | 1 | 1 | ![Bitwise OR of 3 numbers](../../../assets/engine-api/libraries/bit32/OR.png) **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `numbers` | `Tuple` | | | **Returns:** `number` ### bit32.btest **Signature:** `bit32.btest(numbers: Tuple): bool` Returns a boolean signalling whether the bitwise _and_ of its operands is different from zero. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `numbers` | `Tuple` | | | **Returns:** `bool` ### bit32.bxor **Signature:** `bit32.bxor(numbers: Tuple): number` Returns the bitwise XOR of all provided numbers. Each bit is tested against the following truth table: | A | B | Output | | --- | --- | --- | | 0 | 0 | 0 | | 1 | 0 | 1 | | 0 | 1 | 1 | | 1 | 1 | 0 | ![Bitwise XOR of 3 numbers](../../../assets/engine-api/libraries/bit32/XOR.png) **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `numbers` | `Tuple` | | | **Returns:** `number` ### bit32.byteswap **Signature:** `bit32.byteswap(x: number): number` Returns the given number with the order of the bytes swapped. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `number` | | | **Returns:** `number` ### bit32.countlz **Signature:** `bit32.countlz(n: number): number` Returns the number of consecutive zero bits in the 32-bit representation of the provided number starting from the left-most (most significant) bit. Returns 32 if the provided number is zero. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `n` | `number` | | | **Returns:** `number` ### bit32.countrz **Signature:** `bit32.countrz(n: number): number` Returns the number of consecutive zero bits in the 32-bit representation of the provided number starting from the right-most (least significant) bit. Returns 32 if the provided number is zero. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `n` | `number` | | | **Returns:** `number` ### bit32.extract **Signature:** `bit32.extract(n: number, field: number, width?: number): number` Returns the unsigned number formed by the bits `field` to `field + width - 1` from `n`. Bits are numbered from 0 (least significant) to 31 (most significant). All accessed bits must be in the range [0, 31]. The default for `width` is 1. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `n` | `number` | | | | `field` | `number` | | | | `width` | `number` | `1` | | **Returns:** `number` ### bit32.replace **Signature:** `bit32.replace(n: number, v: number, field: number, width?: number): number` Returns a copy of `n` with the bits `field` to `field + width - 1` replaced by the value `v`. See [bit32.extract()](/docs/reference/engine/globals/bit32.md) for details about `field` and `width`. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `n` | `number` | | | | `v` | `number` | | | | `field` | `number` | | | | `width` | `number` | `1` | | **Returns:** `number` ### bit32.lrotate **Signature:** `bit32.lrotate(x: number, disp: number): number` Returns the number `x` rotated `disp` bits to the left. The number `disp` may be any representable integer. For any valid displacement, the following identity holds: ```lua assert(bit32.lrotate(x, disp) == bit32.lrotate(x, disp % 32)) ``` In particular, negative displacements rotate to the right. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `number` | | | | `disp` | `number` | | | **Returns:** `number` ### bit32.lshift **Signature:** `bit32.lshift(x: number, disp: number): number` Returns the number `x` shifted `disp` bits to the left. The number `disp` may be any representable integer. Negative displacements shift to the right. In any direction, vacant bits are filled with zeros. In particular, displacements with absolute values higher than 31 result in zero (all bits are shifted out). ![Number shifted 3 to the left](../../../assets/engine-api/libraries/bit32/LSHIFT.png) For positive displacements, the following equality holds: ```lua assert(bit32.lshift(b, disp) == (b * 2^disp) % 2^32) ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `number` | | | | `disp` | `number` | | | **Returns:** `number` ### bit32.rrotate **Signature:** `bit32.rrotate(x: number, disp: number): number` Returns the number `x` rotated `disp` bits to the right. The number `disp` may be any representable integer. For any valid displacement, the following identity holds: ```lua assert(bit32.rrotate(x, disp) == bit32.rrotate(x , disp % 32)) ``` In particular, negative displacements rotate to the left. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `number` | | | | `disp` | `number` | | | **Returns:** `number` ### bit32.rshift **Signature:** `bit32.rshift(x: number, disp: number): number` Returns the number `x` shifted `disp` bits to the right. The number `disp` may be any representable integer. Negative displacements shift to the left. In any direction, vacant bits are filled with zeros. In particular, displacements with absolute values higher than 31 result in zero (all bits are shifted out). ![Number shifted 3 to the right](../../../assets/engine-api/libraries/bit32/RSHIFT.png) For positive displacements, the following equality holds: ```lua assert(bit32.rshift(b, disp) == (b % 2^32 / 2^disp) // 1) ``` This shift operation is what is called logical shift. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `number` | | | | `disp` | `number` | | | **Returns:** `number` --- name: buffer last_updated: 2026-06-29T19:34:09Z type: library summary: "A library of buffer functions." --- # buffer A library of buffer functions. **Type:** library ## Description A buffer is an object that represents a fixed-size mutable block of memory. The buffer library provides functions for creation and manipulation of buffer objects, providing all its functions inside the global [buffer](/docs/reference/engine/globals/buffer.md) variable. Buffer is intended to be used a low-level binary data storage structure, replacing the uses of [string.pack()](/docs/reference/engine/globals/string.md) and [string.unpack()](/docs/reference/engine/globals/string.md). Use cases include reading and writing existing binary formats, working with data in a more compact form, serialization to custom binary formats, and general work with native memory types like fixed-length integers and floats. When passed through Roblox APIs, including sending a buffer through custom events, the identity of the buffer object is not preserved and the target will receive a copy. Similar to other limitations, the same buffer object cannot be used from multiple [Actor](/docs/reference/engine/classes/Actor.md) scripts (Parallel Luau). Many of the functions accept an offset in bytes from the start of the buffer. Offset of `0` from the start of the buffer memory block accesses the first byte. All offsets, counts and sizes should be non-negative integer numbers. If the bytes that are accessed by any read or write operation are outside the buffer memory, an error is thrown. The `read` and `write` methods that work with integers and floats use [little-endian](https://en.wikipedia.org/wiki/Endianness) encoding. ## Functions ### buffer.create **Signature:** `buffer.create(size: number): buffer` Creates a buffer of the requested size with all bytes initialized to `0`. Size limit is 1 GiB, or 1,073,741,824 bytes. Keep in mind that larger buffers might fail to allocate if device is running low on memory. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `size` | `number` | | Size of the buffer in bytes. Must be a positive integer. | **Returns:** `buffer` ### buffer.fromstring **Signature:** `buffer.fromstring(str: string): buffer` Creates a buffer initialized to the contents of the string. The size of the buffer equals the length of the string. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `str` | `string` | | | **Returns:** `buffer` ### buffer.tostring **Signature:** `buffer.tostring(b: buffer): string` Returns the buffer data as a string. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `b` | `buffer` | | | **Returns:** `string` ### buffer.len **Signature:** `buffer.len(b: buffer): number` Returns the size of the buffer in bytes. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `b` | `buffer` | | | **Returns:** `number` ### buffer.readbits **Signature:** `buffer.readbits(b: buffer, bitOffset: number, bitCount: number): number` Reads a range of bits into an unsigned integer from the buffer based on a specific `bitCount` integer from `0` to `32`, inclusive. For example: - `buffer.readbits(b, 0, 8)` is equivalent to [buffer.readu8(b, 0)](/docs/reference/engine/globals/buffer.md). - `buffer.readbits(b, 0, 16)` is equivalent to [buffer.readu16(b, 0)](/docs/reference/engine/globals/buffer.md). - `buffer.readbits(b, 0, 32)` is equivalent to [buffer.readu32(b, 0)](/docs/reference/engine/globals/buffer.md). - `buffer.readbits(b, 0, 24)` reads 24 bits from the buffer. Note that `0` bit width is supported only to not error in generalized cases where bit count is dynamic, and reading 0 bits returns `0`. Also note that, since the max size of the buffer is 1 GB, `bitOffset` cannot be handled as a 32‑bit integer number like byte offset in other buffer functions. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `b` | `buffer` | | | | `bitOffset` | `number` | | Offset from the beginning of the buffer memory, starting from `0`. | | `bitCount` | `number` | | Integer bit count to read. Error is thrown if this value is not in range of `0` to `32`, inclusive. | **Returns:** `number` ### buffer.readi8 **Signature:** `buffer.readi8(b: buffer, offset: number): number` Reads the data from the buffer by reinterpreting bytes at the offset as an 8-bit signed integer and converting it into a number. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `b` | `buffer` | | | | `offset` | `number` | | Offset from the beginning of the buffer memory, starting from `0`. | **Returns:** `number` ### buffer.readu8 **Signature:** `buffer.readu8(b: buffer, offset: number): number` Reads the data from the buffer by reinterpreting bytes at the offset as an 8-bit unsigned integer and converting it into a number. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `b` | `buffer` | | | | `offset` | `number` | | Offset from the beginning of the buffer memory, starting from `0`. | **Returns:** `number` ### buffer.readi16 **Signature:** `buffer.readi16(b: buffer, offset: number): number` Reads the data from the buffer by reinterpreting bytes at the offset as a 16-bit signed integer and converting it into a number. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `b` | `buffer` | | | | `offset` | `number` | | Offset from the beginning of the buffer memory, starting from `0`. | **Returns:** `number` ### buffer.readu16 **Signature:** `buffer.readu16(b: buffer, offset: number): number` Reads the data from the buffer by reinterpreting bytes at the offset as a 16-bit unsigned integer and converting it into a number. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `b` | `buffer` | | | | `offset` | `number` | | Offset from the beginning of the buffer memory, starting from `0`. | **Returns:** `number` ### buffer.readi32 **Signature:** `buffer.readi32(b: buffer, offset: number): number` Reads the data from the buffer by reinterpreting bytes at the offset as a 32-bit signed integer and converting it into a number. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `b` | `buffer` | | | | `offset` | `number` | | Offset from the beginning of the buffer memory, starting from `0`. | **Returns:** `number` ### buffer.readu32 **Signature:** `buffer.readu32(b: buffer, offset: number): number` Reads the data from the buffer by reinterpreting bytes at the offset as a 32-bit unsigned integer and converting it into a number. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `b` | `buffer` | | | | `offset` | `number` | | Offset from the beginning of the buffer memory, starting from `0`. | **Returns:** `number` ### buffer.readf32 **Signature:** `buffer.readf32(b: buffer, offset: number): number` Reads the data from the buffer by reinterpreting bytes at the offset as a 32-bit floating-point value and converting it into a number. If the floating-point value matches any bit patterns that represent `NaN` (not a number), the returned value may be converted to a different quiet `NaN` representation. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `b` | `buffer` | | | | `offset` | `number` | | Offset from the beginning of the buffer memory, starting from `0`. | **Returns:** `number` ### buffer.readf64 **Signature:** `buffer.readf64(b: buffer, offset: number): number` Reads the data from the buffer by reinterpreting bytes at the offset as a 64-bit floating-point value and converting it into a number. If the floating-point value matches any bit patterns that represent `NaN` (not a number), the returned value may be converted to a different quiet `NaN` representation. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `b` | `buffer` | | | | `offset` | `number` | | Offset from the beginning of the buffer memory, starting from `0`. | **Returns:** `number` ### buffer.writebits **Signature:** `buffer.writebits(b: buffer, bitOffset: number, bitCount: number, value: number): ()` Writes data to the buffer based on a specific `bitCount` integer from `0` to `32`, inclusive. `value` is treated as an unsigned 32‑bit number and only `bitCount` least significant bits are written. Note that `0` bit width is supported only to not error in generalized cases where bit count is dynamic, and writing 0 bits has no effect. Also note that, since the max size of the buffer is 1 GB, `bitOffset` cannot be handled as a 32‑bit integer number like byte offset in other buffer functions. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `b` | `buffer` | | | | `bitOffset` | `number` | | Offset from the beginning of the buffer memory, starting from `0`. | | `bitCount` | `number` | | Integer bit count to write. Error is thrown if this value is not in range of `0` to `32`, inclusive. | | `value` | `number` | | Unsigned 32‑bit number. Only `bitCount` least significant bits are written. | **Returns:** `()` ### buffer.writei8 **Signature:** `buffer.writei8(b: buffer, offset: number, value: number): ()` Writes data to the buffer by converting the number into an 8-bit signed integer and writing a single byte. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `b` | `buffer` | | | | `offset` | `number` | | Offset from the beginning of the buffer memory, starting from `0`. | | `value` | `number` | | An integer number in range [-128, 127]. | **Returns:** `()` ### buffer.writeu8 **Signature:** `buffer.writeu8(b: buffer, offset: number, value: number): ()` Writes data to the buffer by converting the number into an 8-bit unsigned integer and writing a single byte. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `b` | `buffer` | | | | `offset` | `number` | | Offset from the beginning of the buffer memory, starting from `0`. | | `value` | `number` | | An integer number in range [0, 255]. | **Returns:** `()` ### buffer.writei16 **Signature:** `buffer.writei16(b: buffer, offset: number, value: number): ()` Writes data to the buffer by converting the number into a 16-bit signed integer and reinterpreting it as individual bytes. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `b` | `buffer` | | | | `offset` | `number` | | Offset from the beginning of the buffer memory, starting from `0`. | | `value` | `number` | | An integer number in range [-32,768, 32,767]. | **Returns:** `()` ### buffer.writeu16 **Signature:** `buffer.writeu16(b: buffer, offset: number, value: number): ()` Writes data to the buffer by converting the number into a 16-bit unsigned integer and reinterpreting it as individual bytes. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `b` | `buffer` | | | | `offset` | `number` | | Offset from the beginning of the buffer memory, starting from `0`. | | `value` | `number` | | An integer number in range [0, 65,535]. | **Returns:** `()` ### buffer.writei32 **Signature:** `buffer.writei32(b: buffer, offset: number, value: number): ()` Writes data to the buffer by converting the number into a 32-bit signed integer and reinterpreting it as individual bytes. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `b` | `buffer` | | | | `offset` | `number` | | Offset from the beginning of the buffer memory, starting from `0`. | | `value` | `number` | | An integer number in range [-2,147,483,648, 2,147,483,647]. | **Returns:** `()` ### buffer.writeu32 **Signature:** `buffer.writeu32(b: buffer, offset: number, value: number): ()` Writes data to the buffer by converting the number into a 32-bit unsigned integer and reinterpreting it as individual bytes. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `b` | `buffer` | | | | `offset` | `number` | | Offset from the beginning of the buffer memory, starting from `0`. | | `value` | `number` | | An integer number in range [0, 4,294,967,295]. | **Returns:** `()` ### buffer.writef32 **Signature:** `buffer.writef32(b: buffer, offset: number, value: number): ()` Writes data to the buffer by converting the number into a 32-bit floating-point value and reinterpreting it as individual bytes. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `b` | `buffer` | | | | `offset` | `number` | | Offset from the beginning of the buffer memory, starting from `0`. | | `value` | `number` | | A single-precision floating-point number. | **Returns:** `()` ### buffer.writef64 **Signature:** `buffer.writef64(b: buffer, offset: number, value: number): ()` Writes data to the buffer by converting the number into a 64-bit floating-point value and reinterpreting it as individual bytes. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `b` | `buffer` | | | | `offset` | `number` | | Offset from the beginning of the buffer memory, starting from `0`. | | `value` | `number` | | A double-precision floating-point number. | **Returns:** `()` ### buffer.readstring **Signature:** `buffer.readstring(b: buffer, offset: number, count: number): string` Reads a string of length `count` from the buffer at the specified `offset`. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `b` | `buffer` | | | | `offset` | `number` | | Offset from the beginning of the buffer memory, starting from `0`. | | `count` | `number` | | Length to read. | **Returns:** `string` ### buffer.writestring **Signature:** `buffer.writestring(b: buffer, offset: number, value: string, count: number?): ()` Writes data from a string into the buffer at the specified `offset`. If an optional `count` is specified, only `count` bytes are taken from the string. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `b` | `buffer` | | | | `offset` | `number` | | Offset from the beginning of the buffer memory, starting from `0`. | | `value` | `string` | | Data to write. | | `count` | `number?` | | Number of bytes to take from the string. This value cannot be larger than the string length. | **Returns:** `()` ### buffer.copy **Signature:** `buffer.copy(target: buffer, targetOffset: number, source: buffer, sourceOffset?: number?, count: number?): ()` Copies `count` bytes from `source` starting at offset `sourceOffset` into the `target` at `targetOffset`. It's possible for `source` and `target` to be the same. Copying an overlapping region inside the same buffer acts as if the source region is copied into a temporary buffer and then that buffer is copied over to the target. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `target` | `buffer` | | Buffer to copy data into. | | `targetOffset` | `number` | | Offset from the beginning of the buffer memory, starting from `0`. | | `source` | `buffer` | | Buffer to take the data from. | | `sourceOffset` | `number?` | `0` | Offset from the beginning of the buffer memory, starting from `0`. | | `count` | `number?` | | Number of bytes to copy. If omitted, the whole `source` data starting from `sourceOffset` is taken. | **Returns:** `()` ### buffer.fill **Signature:** `buffer.fill(b: buffer, offset: number, value: number, count: number?): ()` Sets `count` bytes in the buffer starting at the specified `offset` to `value`. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `b` | `buffer` | | Buffer to write the data into. | | `offset` | `number` | | Offset from the beginning of the buffer memory, starting from `0`. | | `value` | `number` | | An integer number in range [0, 255]. | | `count` | `number?` | | Number of bytes to write. If omitted, all bytes after the specified offset are set. | **Returns:** `()` --- name: coroutine last_updated: 2026-06-29T19:34:09Z type: library summary: "A function that executes alongside the main thread." --- # coroutine A function that executes alongside the main thread. **Type:** library ## Description A **coroutine** is used to perform multiple tasks at the same time from within the same script. Such tasks might include producing values from inputs or performing work on a subroutine when solving a larger problem. A task doesn't even need to have a defined ending point, but it does need to define particular times at which it **yields** (pause) to let other things be worked on. ## Using Coroutines A new coroutine can be created by providing a function to [coroutine.create()](/docs/reference/engine/globals/coroutine.md). Once created, a coroutine doesn't begin running until the first call to [coroutine.resume()](/docs/reference/engine/globals/coroutine.md) which passes the arguments to the function. This call returns when the function either halts or calls [coroutine.yield()](/docs/reference/engine/globals/coroutine.md) and, when this happens, [coroutine.resume()](/docs/reference/engine/globals/coroutine.md) returns either the values returned by the function, the values sent to [coroutine.yield()](/docs/reference/engine/globals/coroutine.md), or an error message. If it does error, the second return value is the thrown error. ```lua local function task(...) -- This function might do some work for a bit then yield some value coroutine.yield("first") -- To be returned by coroutine.resume() -- The function continues once it is resumed again return "second" end local taskCoro = coroutine.create(task) -- Call resume for the first time, which runs the function from the beginning local success, result = coroutine.resume(taskCoro, ...) print(success, result) --> true, first (task called coroutine.yield()) -- Continue running the function until it yields or halts success, result = coroutine.resume(taskCoro) print(success, result) --> true, second (task halted because it returned "second") ``` During the lifetime of the coroutine, you can call [coroutine.status()](/docs/reference/engine/globals/coroutine.md) to inspect its status: | Status | Meaning | | --- | --- | | **suspended** | The coroutine is waiting to be resumed. Coroutines begin in this state and enter it when their function calls `coroutine.yield()`. | | **running** | The coroutine is running right now. | | **normal** | The coroutine is awaiting the yield of another coroutine; in other words, it has resumed another coroutine. | | **dead** | The function has halted (returned or thrown an error). The coroutine cannot be used further. | ### Wrapping Coroutines When working with coroutines, you can also forgo the use of the coroutine object and instead use a wrapper function. Such a wrapper function will resume a particular coroutine when it is called and will return only the yielded values. You can do this using [coroutine.wrap()](/docs/reference/engine/globals/coroutine.md): ```lua -- Create coroutine and return a wrapper function that resumes it local f = coroutine.wrap(task) -- Resume the coroutine as if we called coroutine.resume() local result = f() -- If an error occurs it will be raised here! -- This differs from coroutine.resume() which acts similar to pcall() ``` The first value returned from [coroutine.resume()](/docs/reference/engine/globals/coroutine.md) describes whether a coroutine ran without errors. However, functions returned by [coroutine.wrap()](/docs/reference/engine/globals/coroutine.md) will not do this: instead they directly return the values returned or passed to [coroutine.yield()](/docs/reference/engine/globals/coroutine.md), if any. Should an error have occurred while running the coroutine function, the error is raised on the call of the returned function. ### Producer Pattern Example Imagine a task that produces repetitions of a word: each time it produces a repetition, the next one will produce one more. For example, providing `Hello` will produce `Hello`, `HelloHello`, `HelloHelloHello`, etc. To do this, you can define `repeatThis()`: ```lua -- This function repeats a word every time its coroutine is resumed local function repeatThis(word) local repetition = "" while true do -- Do one repetition then yield the result repetition = repetition .. word coroutine.yield(repetition) end end ``` To run this function as a coroutine, you can use [coroutine.create()](/docs/reference/engine/globals/coroutine.md) followed by multiple calls to [coroutine.resume()](/docs/reference/engine/globals/coroutine.md): ```lua local repetitionCoro = coroutine.create(repeatThis) print(coroutine.resume(repetitionCoro, "Hello")) -- true, Hello print(coroutine.resume(repetitionCoro)) -- true, HelloHello print(coroutine.resume(repetitionCoro)) -- true, HelloHelloHello ``` For this producer function, you can also use [coroutine.wrap()](/docs/reference/engine/globals/coroutine.md) to get a function that produces values: ```lua local f = coroutine.wrap(repeatThis) print(f("Hello")) -- Hello print(f()) -- HelloHello print(f()) -- HelloHelloHello ``` ## Functions ### coroutine.close **Signature:** `coroutine.close(co: thread): bool, Variant` Closes and puts the provided coroutine in a dead state. This function returns `true` unless the coroutine is in an error state, in which case it returns `false` and the error message. A coroutine that is currently running cannot be closed. A coroutine cannot be resumed after it is closed. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `co` | `thread` | | | **Returns:** `bool`, `Variant` — `true` unless the coroutine being closed is in an error state. The error message, if any. ### coroutine.create **Signature:** `coroutine.create(f: function): thread` Creates a new coroutine, with body f. f must be a Luau function. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `f` | `function` | | | **Returns:** `thread` ### coroutine.isyieldable **Signature:** `coroutine.isyieldable(): bool` Returns `true` if the coroutine this function is called within can safely yield. Yielding a coroutine inside metamethods or C functions is prohibited, with the exception of `pcall` and `xpcall`. **Returns:** `bool` — Whether or not the coroutine can safely yield at this point. ### coroutine.resume **Signature:** `coroutine.resume(co: thread, ...: Variant): bool, Variant` Starts or continues the execution of coroutine `co`. The first time you resume a coroutine, it starts running its body. The values `...` are passed as the arguments to the body function. If the coroutine has yielded, resume restarts it; the values `...` are passed as the results from the yield. If the coroutine runs without any errors, resume returns true plus any values passed to yield (if the coroutine yields) or any values returned by the body function (if the coroutine terminates). If there is any error, resume returns false plus the error message. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `co` | `thread` | | | | `...` | `Variant` | | | **Returns:** `bool`, `Variant` ### coroutine.running **Signature:** `coroutine.running(): thread` Returns the running coroutine. **Returns:** `thread` ### coroutine.status **Signature:** `coroutine.status(co: thread): string` Returns the status of coroutine co, as a string: 'running', if the coroutine is running (that is, it called status); 'suspended', if the coroutine is suspended in a call to yield, or if it has not started running yet; 'normal' if the coroutine is active but not running (that is, it has resumed another coroutine); and 'dead' if the coroutine has finished its body function, or if it has stopped with an error. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `co` | `thread` | | | **Returns:** `string` ### coroutine.wrap **Signature:** `coroutine.wrap(f: function): function` Creates a new coroutine, with body f. f must be a Luau function. Returns a function that resumes the coroutine each time it is called. Any arguments passed to the function behave as the extra arguments to resume. Returns the same values returned by resume, except the first boolean. In case of error, propagates the error. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `f` | `function` | | | **Returns:** `function` ### coroutine.yield **Signature:** `coroutine.yield(...: Tuple): Tuple` Suspends the execution of the calling coroutine. Any arguments to yield are passed as extra results to resume. Yielding a coroutine inside metamethods or C functions is prohibited, with the exception of `pcall` and `xpcall`. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `...` | `Tuple` | | | **Returns:** `Tuple` --- name: debug last_updated: 2026-06-29T19:34:09Z type: library summary: "This library provides functions useful for debugging and profiling code." --- # debug This library provides functions useful for debugging and profiling code. **Type:** library ## Description Provides a few basic functions for debugging code in Roblox. Unlike the [debug](/docs/reference/engine/globals/debug.md) library found in Lua natively, this version has been heavily sandboxed. ## Functions ### debug.traceback **Signature:** `debug.traceback(message: string, level?: number): string` Returns a traceback of the current function call stack as a string; in other words, a description of the functions that have been called up to this point. During debugging, this behaves like an error stack trace but does not stop execution of the script. The `level` parameter specifies what level of the call stack to consider, with `1` being the call of [debug.traceback()](/docs/reference/engine/globals/debug.md) itself, `2` being the call of the function calling [debug.traceback()](/docs/reference/engine/globals/debug.md), and so on. See the code sample below for an example of sequential function calls. Note that this function will often return inaccurate results (compared to the original source code) and that the format of the returned traceback may change at any time. You should **not** parse the return value for specific information such as script names or line numbers. The following example includes sequential function calls; `fnB()` is called, and it calls `fnA()` which then calls [debug.traceback()](/docs/reference/engine/globals/debug.md). ```lua local function fnA() print(debug.traceback("Specific moment during fnA()")) end local function fnB() fnA() end -- Call function fnB() to begin traceback fnB() ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `message` | `string` | | The first line of the returned string. | | `level` | `number` | `1` | The number of calls "up" the call stack to return. | **Returns:** `string` — Traceback of the current function call stack. ### debug.traceback **Signature:** `debug.traceback(thread: thread, message: string, level?: number): string` Returns a traceback of the current function call stack as a string; in other words, a description of the functions that have been called up to this point. During debugging, this behaves like an error stack trace but does not stop execution of the script. The `level` parameter specifies what level of the call stack to consider, with `1` being the call of [debug.traceback()](/docs/reference/engine/globals/debug.md) itself, `2` being the call of the function calling [debug.traceback()](/docs/reference/engine/globals/debug.md), and so on. See the code sample below for an example of sequential function calls. Note that this function will often return inaccurate results (compared to the original source code) and that the format of the returned traceback may change at any time. You should **not** parse the return value for specific information such as script names or line numbers. The following example includes sequential function calls; `fnB()` is called, and it calls `fnA()` which then calls [debug.traceback()](/docs/reference/engine/globals/debug.md). ```lua local function fnA() print(debug.traceback("Specific moment during fnA()")) end local function fnB() fnA() end -- Call function fnB() to begin traceback fnB() ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `thread` | `thread` | | A thread as returned by [coroutine.create()](/docs/reference/engine/globals/coroutine.md). | | `message` | `string` | | The first line of the returned string. | | `level` | `number` | `1` | The number of calls "up" the call stack to return. | **Returns:** `string` — Traceback of the current function call stack. ### debug.info **Signature:** `debug.info(level: number, options: string): Tuple` Allows programmatic inspection of the call stack. This function differs from [debug.traceback()](/docs/reference/engine/globals/debug.md) in that it guarantees the format of the data it returns. This is useful for general logging and filtering purposes as well as for sending the data to systems expecting structured input, such as crash aggregation. ```lua local function fnA() -- Output source identifier ("s") and line ("l") at levels 1 and 2 print(debug.info(1, "sl")) --> fnA() 3 print(debug.info(2, "sl")) --> fnA() 7 end fnA() ``` Note that this function is similar to [debug.getinfo](https://www.lua.org/pil/23.1.html), an unavailable part of the standard Lua library which serves a similar purpose. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `level` | `number` | | Determines at what level of the call stack the information returned should describe. A value of `1` represents the function which is calling [debug.info()](/docs/reference/engine/globals/debug.md), a value of `2` represents the function that called that function, and so on. | | `options` | `string` | | A string that describes what the returned information should represent. It must only contain 0 or 1 instances of the characters `slnaf`, each representing a piece of information: - `s` ([string](/docs/en-us/luau/strings.md)) — The function source identifier, equal to the full name of the script the function is defined in. - `l` ([number](/docs/en-us/luau/numbers.md)) — The line number of the function call represented by `level`. - `n` ([string](/docs/en-us/luau/strings.md)) — The name of the function; may be `nil` for anonymous functions and C functions without an assigned debug name. - `a` ([number](/docs/en-us/luau/numbers.md), [boolean](/docs/en-us/luau/booleans.md)) — Arity of the function, which refers to the parameter count and whether the function is variadic. - `f` ([function](/docs/en-us/luau/functions.md)) — The function which was inspected. | **Returns:** `Tuple` ### debug.info **Signature:** `debug.info(function: function, options: string): Tuple` Allows programmatic inspection of the call stack. This function differs from [debug.traceback()](/docs/reference/engine/globals/debug.md) in that it guarantees the format of the data it returns. This is useful for general logging and filtering purposes as well as for sending the data to systems expecting structured input, such as crash aggregation. ```lua local function fnA() end local function fnB() end -- Output line ("l"), name ("n"), and identifier ("f") for both fnA() and fnB() print(debug.info(fnA, "lnf")) --> 1 fnA function: 0x75e3d3c398a81252 print(debug.info(fnB, "lnf")) --> 5 fnB function: 0x6022a6dc5ccf4ab2 ``` Note that this function is similar to [debug.getinfo](https://www.lua.org/pil/23.1.html), an unavailable part of the standard Lua library which serves a similar purpose. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `function` | `function` | | The function of the call stack which the information returned should describe. | | `options` | `string` | | A string that describes what the returned information should represent. It must only contain 0 or 1 instances of the characters `slnaf`, each representing a piece of information: - `s` ([string](/docs/en-us/luau/strings.md)) — The function source identifier, equal to the full name of the script the function is defined in. - `l` ([number](/docs/en-us/luau/numbers.md)) — The line that `function` is defined on. - `n` ([string](/docs/en-us/luau/strings.md)) — The name of the function; may be `nil` for anonymous functions and C functions without an assigned debug name. - `a` ([number](/docs/en-us/luau/numbers.md), [boolean](/docs/en-us/luau/booleans.md)) — Arity of the function, which refers to the parameter count and whether the function is variadic. - `f` ([function](/docs/en-us/luau/functions.md)) — The function which was inspected. | **Returns:** `Tuple` ### debug.info **Signature:** `debug.info(thread: thread, level: number, options: string): Tuple` Allows programmatic inspection of the call stack. This function differs from [debug.traceback()](/docs/reference/engine/globals/debug.md) in that it guarantees the format of the data it returns. This is useful for general logging and filtering purposes as well as for sending the data to systems expecting structured input, such as crash aggregation. ```lua local function fnA() -- Output source identifier ("s") and line ("l") at levels 1 and 2 print(debug.info(1, "sl")) --> fnA() 3 print(debug.info(2, "sl")) --> fnA() 7 end fnA() ``` Note that this function is similar to [debug.getinfo](https://www.lua.org/pil/23.1.html), an unavailable part of the standard Lua library which serves a similar purpose. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `thread` | `thread` | | A thread as returned by [coroutine.create()](/docs/reference/engine/globals/coroutine.md). | | `level` | `number` | | Determines at what level of the call stack the information returned should describe. A value of `1` represents the function which is calling [debug.info()](/docs/reference/engine/globals/debug.md), a value of `2` represents the function that called that function, and so on. | | `options` | `string` | | A string that describes what the returned information should represent. It must only contain 0 or 1 instances of the characters `slnaf`, each representing a piece of information: - `s` ([string](/docs/en-us/luau/strings.md)) — The function source identifier, equal to the full name of the script the function is defined in. - `l` ([number](/docs/en-us/luau/numbers.md)) — The line number of the function call represented by `level`. - `n` ([string](/docs/en-us/luau/strings.md)) — The name of the function; may be `nil` for anonymous functions and C functions without an assigned debug name. - `a` ([number](/docs/en-us/luau/numbers.md), [boolean](/docs/en-us/luau/booleans.md)) — Arity of the function, which refers to the parameter count and whether the function is variadic. - `f` ([function](/docs/en-us/luau/functions.md)) — The function which was inspected. | **Returns:** `Tuple` ### debug.profilebegin **Signature:** `debug.profilebegin(label: string): ()` Starts profiling for a [MicroProfiler](/docs/en-us/studio/microprofiler.md) label. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `label` | `string` | | The text that this [MicroProfiler](/docs/en-us/studio/microprofiler.md) label displays. | **Returns:** `()` ### debug.profileend **Signature:** `debug.profileend(): ()` Stops profiling for the most recent [MicroProfiler](/docs/en-us/studio/microprofiler.md) label that [debug.profilebegin()](/docs/reference/engine/globals/debug.md) opened. **Returns:** `()` ### debug.getmemorycategory **Signature:** `debug.getmemorycategory(): string` Returns the name of the current thread's active memory category. **Returns:** `string` — The current thread's active memory category. ### debug.setmemorycategory **Signature:** `debug.setmemorycategory(tag: string): string` Assigns a custom tag name to the current thread's memory category in the [Developer Console](/docs/en-us/studio/developer-console.md). Useful for analyzing memory usage of multiple threads in the same script which would otherwise be grouped together under the same tag/name. Returns the name of the current thread's previous memory category. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `tag` | `string` | | | **Returns:** `string` — The current thread's previous memory category. ### debug.resetmemorycategory **Signature:** `debug.resetmemorycategory(): ()` Resets the tag assigned by [debug.setmemorycategory()](/docs/reference/engine/globals/debug.md) to the automatically assigned value (typically, the script name). **Returns:** `()` ### debug.dumpcodesize **Signature:** `debug.dumpcodesize(): ()` Displays a table of native code size of individual functions and scripts. This function is only available in the Command Bar in Studio. More details can be found on the [Native Code Generation](/docs/en-us/luau/native-code-gen.md) page. **Returns:** `()` --- name: math last_updated: 2026-06-29T19:34:09Z type: library summary: "A library of math functions." --- # math A library of math functions. **Type:** library ## Description This library is an interface to the standard C math library, providing all of its functions inside the [math](/docs/reference/engine/globals/math.md) table. ## Properties ### math.e **Type:** `number` The value of Euler's number, e. ### math.huge **Type:** `number` Returns a value larger than or equal to any other numerical value (about 21024). Dividing a positive number by zero yields this same value. ### math.nan **Type:** `number` A NaN value, as defined by the IEEE 754 standard. Comparing directly to `math.nan` will always return false; use [math.isnan()](/docs/reference/engine/globals/math.md) instead. ### math.phi **Type:** `number` The value of the golden ratio. ### math.pi **Type:** `number` The value of pi. ### math.sqrt2 **Type:** `number` The value of the square root of 2. ### math.tau **Type:** `number` The value of tau, which is defined as `2 * math.pi`. ## Functions ### math.abs **Signature:** `math.abs(x: number): number` Returns the absolute value of `x`. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `number` | | | **Returns:** `number` ### math.acos **Signature:** `math.acos(x: number): number` Returns the arc cosine of `x`. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `number` | | | **Returns:** `number` ### math.asin **Signature:** `math.asin(x: number): number` Returns the arc sine of `x`. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `number` | | | **Returns:** `number` ### math.atan **Signature:** `math.atan(x: number): number` Returns the arc tangent of `x` in radians. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `number` | | | **Returns:** `number` ### math.atan2 **Signature:** `math.atan2(y: number, x: number): number` Returns the arc tangent of `y`/`x` (in radians) while using the signs of both parameters to find the quadrant of the result. It also handles correctly the case of `x` being zero. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `y` | `number` | | | | `x` | `number` | | | **Returns:** `number` ### math.ceil **Signature:** `math.ceil(x: number): int` Returns the smallest integer larger than or equal to `x`. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `number` | | | **Returns:** `int` ### math.clamp **Signature:** `math.clamp(x: number, min: number, max: number): number` Returns a number between `min` and `max`, inclusive. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `number` | | | | `min` | `number` | | | | `max` | `number` | | | **Returns:** `number` ### math.cos **Signature:** `math.cos(x: number): number` Returns the cosine of `x`, assumed to be in radians. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `number` | | | **Returns:** `number` ### math.cosh **Signature:** `math.cosh(x: number): number` Returns the hyperbolic cosine of `x`. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `number` | | | **Returns:** `number` ### math.deg **Signature:** `math.deg(x: number): number` Returns the angle `x` (given in radians) in degrees. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `number` | | | **Returns:** `number` ### math.exp **Signature:** `math.exp(x: number): number` Returns the value `e`^`x`. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `number` | | | **Returns:** `number` ### math.floor **Signature:** `math.floor(x: number): int` Returns the largest integer smaller than or equal to `x`. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `number` | | | **Returns:** `int` ### math.fmod **Signature:** `math.fmod(x: number, y: number): number` Returns the remainder of the division of `x` by `y` that rounds the quotient towards zero. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `number` | | | | `y` | `number` | | | **Returns:** `number` ### math.frexp **Signature:** `math.frexp(x: number): number, int` Returns `m` and `e` such that `x` = `m`\*`2`^`e`. `e` is an integer and the absolute value of `m` is in the range of `0.5` to `1` (inclusive of `0.5` but exclusive of `1`), or zero when `x` is zero. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `number` | | | **Returns:** `number`, `int` ### math.isfinite **Signature:** `math.isfinite(x: number): boolean` Returns true if `x` is a finite number, meaning it is neither NaN nor positive or negative infinity (±[math.huge](/docs/reference/engine/globals/math.md)). **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `number` | | | **Returns:** `boolean` ### math.isinf **Signature:** `math.isinf(x: number): boolean` Returns true if `x` is positive or negative infinity (±[math.huge](/docs/reference/engine/globals/math.md)) and false otherwise. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `number` | | | **Returns:** `boolean` ### math.isnan **Signature:** `math.isnan(x: number): boolean` Returns true if `x` is not a number (NaN) and false otherwise. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `number` | | | **Returns:** `boolean` ### math.ldexp **Signature:** `math.ldexp(x: number, e: int): number` Returns `x`\*`2`^`e` (`e` should be an integer). **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `number` | | | | `e` | `int` | | | **Returns:** `number` ### math.lerp **Signature:** `math.lerp(a: number, b: number, t: number): number` Returns the linear interpolation between `a` and `b` based on the factor `t`. This function uses the formula `a`+`(b-a)`\*`t`. `t` is typically between `0` and `1` but values outside this range are acceptable. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `a` | `number` | | The starting value. | | `b` | `number` | | The ending value. | | `t` | `number` | | The interpolation factor, typically between `0` and `1`. | **Returns:** `number` — The interpolated value between `a` and `b`. ### math.log **Signature:** `math.log(x: number, base?: number): number` Returns the logarithm of `x` using the given base, or the mathematical constant `e` if no base is provided (natural logarithm). **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `number` | | | | `base` | `number` | `2.7182818` | The base of the logarithm, the constant `e` by default. | **Returns:** `number` ### math.log10 **Signature:** `math.log10(x: number): number` Returns the base-10 logarithm of `x`. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `number` | | | **Returns:** `number` ### math.map **Signature:** `math.map(x: number, inmin: number, inmax: number, outmin: number, outmax: number): number` Returns a value that represents `x` mapped linearly from the input range (`inmin` to `inmax`) to the output range (`outmin` to `outmax`). This is achieved by determining the relative position of `x` within the input range and applying that ratio to the output range. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `number` | | The number to be mapped. | | `inmin` | `number` | | The lower bound of the input range. | | `inmax` | `number` | | The upper bound of the input range. | | `outmin` | `number` | | The lower bound of the output range. | | `outmax` | `number` | | The upper bound of the output range. | **Returns:** `number` — The value of `x` mapped to the output range. ### math.max **Signature:** `math.max(x: number, ...: number): number` Returns the maximum value among the numbers passed to the function. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `number` | | | | `...` | `number` | | | **Returns:** `number` ### math.min **Signature:** `math.min(x: number, ...: number): number` Returns the minimum value among the numbers passed to the function. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `number` | | | | `...` | `number` | | | **Returns:** `number` ### math.modf **Signature:** `math.modf(x: number): number, number` Returns two numbers: the integral part of `x` and the fractional part of `x`. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `number` | | | **Returns:** `number`, `number` ### math.noise **Signature:** `math.noise(x: number, y?: number, z?: number): number` Returns a Perlin noise value. The returned value is most often between the range of `-1` to `1` (inclusive) but sometimes may be outside that range; if the interval is critical to you, use [math.clamp(noise, -1, 1)](/docs/reference/engine/globals/math.md) on the output. If you leave arguments out, they will be interpreted as zero, so [math.noise(1.158)](/docs/reference/engine/globals/math.md) is equivalent to [math.noise(1.158, 0, 0)](/docs/reference/engine/globals/math.md) and [math.noise(1.158, 5.723)](/docs/reference/engine/globals/math.md) is equivalent to [math.noise(1.158, 5.723, 0)](/docs/reference/engine/globals/math.md). Note that this function uses a Perlin noise algorithm to assign fixed values to coordinates. For example, [math.noise(1.158, 5.723)](/docs/reference/engine/globals/math.md) will always return `0.48397532105446` and [math.noise(1.158, 6)](/docs/reference/engine/globals/math.md) will always return `0.15315161645412`. If `x`, `y`, and `z` are all integers, the return value will be `0`. For fractional values of `x`, `y`, and `z`, the return value will gradually fluctuate between `-0.5` and `0.5`. For coordinates that are close to each other, the return values will also be close to each other. The noise repeats with a period of `256` on each axis, so [math.noise(x, y, z)](/docs/reference/engine/globals/math.md) and [math.noise(x + 256, y, z)](/docs/reference/engine/globals/math.md) return identical values. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `number` | | | | `y` | `number` | `0` | | | `z` | `number` | `0` | | **Returns:** `number` ### math.pow **Signature:** `math.pow(x: number, y: number): number` Returns `x`^`y` (you can also use the expression `x`^`y` to compute this value). **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `number` | | | | `y` | `number` | | | **Returns:** `number` ### math.rad **Signature:** `math.rad(x: number): number` Returns the angle `x` (given in degrees) in radians. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `number` | | | **Returns:** `number` ### math.random **Signature:** `math.random(m?: number, n?: number): number` When called without arguments, returns a uniform pseudo-random real number in the range of `0` to `1` (inclusive of `0` but exclusive of `1`). When called with an integer number `m`, returns a uniform pseudo-random integer in the range of `1` to `m`, inclusive. When called with two integer numbers `m` and `n`, returns a uniform pseudo-random integer in the range of `m` to `n`, inclusive. Internally, this uses a 32-bit PCG (Permuted Congruential Generator) which achieves excellent statistical performance and makes its output hard to predict. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `m` | `number` | `0` | | | `n` | `number` | `1` | | **Returns:** `number` ### math.randomseed **Signature:** `math.randomseed(x: number): ()` Sets `x` as the seed for the pseudo-random generator: equal seeds produce equal sequences of numbers. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `number` | | | **Returns:** `()` ### math.round **Signature:** `math.round(x: number): number` Returns the integer with the smallest difference between it and the given number. For example, the value `5.8` returns `6`. For values like `0.5` that are equidistant to two integers, the value with the greater difference between it and zero is chosen. In other words, the function "rounds away from zero" such that `0.5` rounds to `1` and `-0.5` rounds to `-1`. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `number` | | The value to be rounded. | **Returns:** `number` ### math.sign **Signature:** `math.sign(x: number): int` Returns `-1` if `x` is less than `0`, `0` if `x` equals `0`, or `1` if `x` is greater than `0`. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `number` | | | **Returns:** `int` ### math.sin **Signature:** `math.sin(x: number): number` Returns the sine of `x`, assumed to be in radians. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `number` | | | **Returns:** `number` ### math.sinh **Signature:** `math.sinh(x: number): number` Returns the hyperbolic sine of `x`. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `number` | | | **Returns:** `number` ### math.sqrt **Signature:** `math.sqrt(x: number): number` Returns the square root of `x`. You can also use the expression `x`^`0.5` to compute this value. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `number` | | | **Returns:** `number` ### math.tan **Signature:** `math.tan(x: number): number` Returns the tangent of `x`, assumed to be in radians. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `number` | | | **Returns:** `number` ### math.tanh **Signature:** `math.tanh(x: number): number` Returns the hyperbolic tangent of `x`. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `number` | | | **Returns:** `number` --- name: os last_updated: 2026-06-29T19:34:09Z type: library summary: "This library provides functions related to time and date." --- # os This library provides functions related to time and date. **Type:** library ## Description This library currently serves the purpose of providing information about the system time under the UTC format. It has been heavily sandboxed from the standard Lua [os](/docs/reference/engine/globals/os.md) library and does not allow you to perform any system-altering operations. ## Functions ### os.clock **Signature:** `os.clock(): double` Returns elapsed time in seconds since an arbitrary baseline with sub-microsecond precision. This function is useful for comparing durations between two events that occur on the same computer, and is the best option for benchmarking. Unlike with functions such as [os.time()](/docs/reference/engine/globals/os.md) or [DateTime.now()](/docs/reference/engine/datatypes/DateTime.md), adjustments to the system clock (such as by the user or [NTP](https://en.wikipedia.org/wiki/Network_Time_Protocol)) do not cause time to jump forwards or backwards. ```lua -- Record the initial time: local startTime = os.clock() -- Do something you want to measure the performance of: local a, b = 0, 1 for _ = 1, 5000000 do a, b = b, a end -- Measure amount of time this took: local deltaTime = os.clock() - startTime print("Elapsed time: " .. deltaTime) --> Elapsed time: 0.044425600033719 (actual number may vary) ``` **Returns:** `double` ### os.date **Signature:** `os.date(formatString: string, time: int): Dictionary` Formats the given `formatString` with date/time information based on the given time, or if not provided, the value returned by [os.time()](/docs/reference/engine/globals/os.md). This function should be avoided in new work. Instead, use the [DateTime](/docs/reference/engine/datatypes/DateTime.md) API, which supports localized formatting. The following specifiers (based on the C function strftime) are supported: | Specifier | Meaning | Example | | --- | --- | --- | | `%a` | Abbreviated weekday name * | Mon | | `%A` | Full weekday name * | Monday | | `%b` | Abbreviated month name * | Feb | | `%B` | Full month name * | February | | `%c` | Date and time * | Mon Feb 12 14:14:35 2024 | | `%d` | Day of the month | 12 | | `%H` | Hour, using 24-hour clock | 14 | | `%I` | Hour, using 12-hour clock | 02 | | `%j` | Day of year | 043 | | `%m` | Month | 02 | | `%M` | Minute | 14 | | `%p` | Either "AM" or "PM" | PM | | `%S` | Second | 35 | | `%U` | Week number (first Sunday as the first day of week one) | 06 | | `%w` | Weekday | 1 | | `%W` | Week number (first Monday as the first day of week one) | 07 | | `%x` | Date * | 02/12/24 | | `%X` | Time * | 14:14:35 | | `%y` | Two-digit year | 24 | | `%Y` | Full year | 2024 | | `%z` | ISO 8601 offset from UTC in timezone (1 minute = 1, 1 hour = 100) | -0800 | | `%Z` | Timezone name or abbreviation * | PST | | `%%` | The % character | % | \* This value can vary depending on the current locale. † The example provided is for February 12th, 2024 (Monday) at 2:14:35 PM (14:14:35), run using locale "en-us" in Pacific Standard Time (PST). If the provided `formatString` is exactly `"*t"` (local time) or `"!*t"` (UTC time), this function instead returns a dictionary containing the following components, which are normally available in the specifiers above. | Field | Type | Description | | --- | --- | --- | | year | int | An integer that describes the current year of the Current Era (ex. 2017) | | month | int | An integer between 1 and 12 (starting from January) that describes the current month. | | wday | int | An integer between 1 and 7 (starting from Sunday) that describes the current week day. | | yday | int | An integer between 1 and 366 describing how many days we are into the year. There can be 366 days if it is a leap year. | | day | int | An integer between 1 and 31 describing the current day of the month. | | hour | int | An integer between 1 and 24 describing the current hour of the day. | | min | int | An integer between 0 and 59 describing the current minute of the hour. | | sec | int | An integer between 0 and 60 describing the current second of the hour. (60 because the function is described to indicate leap seconds, but in practice it probably doesn't). | | isdst | bool | A boolean describing if daylight savings time is currently active. | **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `formatString` | `string` | | Must be either `"*t"` or `"!*t"`. | | `time` | `int` | | The time value to format. | **Returns:** `Dictionary` ### os.difftime **Signature:** `os.difftime(t2: int, t1: int): int` Returns the number of seconds from `t1` to `t2`. The difference is computed assuming that `t1` and `t2` are correctly casted to the [time_t](http://en.cppreference.com/w/cpp/chrono/c/time_t) format. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `t2` | `int` | | | | `t1` | `int` | | | **Returns:** `int` ### os.time **Signature:** `os.time(time?: table): int` Returns how many seconds have passed since the Unix epoch (1 January 1970, 00:00:00), under current UTC time. If provided a table formatted similarly to that returned by [os.date()](/docs/reference/engine/globals/os.md), it returns the number of seconds from the Unix epoch to that time instead. Note that the returned time uses the device's local clock. Most operating systems automatically sync their local time against online time servers, so this should be within a few hundred milliseconds. However, users can easily disable sync behavior and set the system time to anything they want; for synchronized time between client and server, use [Workspace:GetServerTimeNow()](/docs/reference/engine/classes/Workspace.md) instead. This function should be avoided in new work. Instead, use the [DateTime](/docs/reference/engine/datatypes/DateTime.md) API, which supports localized formatting. When you need to precisely measure the time elapsed between two points in time, like when testing performance, use [os.clock()](/docs/reference/engine/globals/os.md) instead. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `time` | `table` | `UTC time` | A dictionary table describing a specific time, similar to that returned by [os.date()](/docs/reference/engine/globals/os.md). If not provided, uses the current UTC time. | **Returns:** `int` --- name: string last_updated: 2026-06-29T19:34:09Z type: library summary: "Provides generic functions to manipulate strings." --- # string Provides generic functions to manipulate strings. **Type:** library ## Description The string library provides generic functions to manipulate strings, such as to extract substrings or match patterns. You can access the string library by the global [string](/docs/reference/engine/globals/string.md) library. See [String pattern reference](/docs/en-us/luau/strings.md#string-pattern-reference) for details on using [string.match()](/docs/reference/engine/globals/string.md), [string.gmatch()](/docs/reference/engine/globals/string.md), and [string.gsub()](/docs/reference/engine/globals/string.md) to find (and replace) substrings. ## Functions ### string.byte **Signature:** `string.byte(s: string, i?: number, j?: number): int` Returns the internal numerical codes of the characters `s[i], s[i+1], ..., s[j]`. The default value for `i` is 1; the default value for `j` is `i`. These indices are corrected following the same rules of function [string.sub()](/docs/reference/engine/globals/string.md). **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `s` | `string` | | | | `i` | `number` | `1` | | | `j` | `number` | `i` | | **Returns:** `int` ### string.char **Signature:** `string.char(...: int): string` Receives zero or more integers and returns a string with length equal to the number of arguments, in which each character has the internal numerical code equal to its corresponding argument. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `...` | `int` | | | **Returns:** `string` ### string.find **Signature:** `string.find(s: string, pattern: string, init?: number, plain?: bool): number, number` Searches for the first occurrence of a pattern in a string and returns the start and end indices of the match. If no match is found, it returns `nil.` You can specify where to start the search using the optional `init` parameter which defaults to 1 and can be negative. An optional `plain` parameter turns off pattern matching, so the function performs a plain substring search; note that if you use `plain`, you must also provide `init.` ```lua -- Example 1: Basic usage local s = "Hello, world!" local pattern = "world" local start_index, end_index = string.find(s, pattern) print(start_index, end_index) -- Output: 8 12 ``` ```lua -- Example 2: Using init parameter local s = "Hello, world! Hello, Roblox!" local pattern = "Hello" local start_index, end_index = string.find(s, pattern, 10) print(start_index, end_index) -- Output: 15 19 ``` ```lua -- Example 3: Using plain parameter local s = "Hello, world! (Hello)" local pattern = "(Hello)" local start_index, end_index = string.find(s, pattern, 1, true) print(start_index, end_index) -- Output: 14 20 ``` ```lua -- Example 4: No Pattern found local s = "Hello, world!" local pattern = "Roblox" local start_index, end_index = string.find(s, pattern) print(start_index, end_index) -- Output: nil ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `s` | `string` | | The string to search within. | | `pattern` | `string` | | The pattern to search for in given string. | | `init` | `number` | `1` | The starting index for the search. | | `plain` | `bool` | `false` | | **Returns:** `number`, `number` — The starting index of the match. The ending index of the match. ### string.format **Signature:** `string.format(formatstring: string, ...: string): string` Returns a formatted version of its variable number of arguments following the description given in its first argument, which must be a string. You can convert variables into user-friendly strings of text using the [string.format()](/docs/reference/engine/globals/string.md) function. The function requires the following format: `%[flags][width].[precision][specifier]`. #### Specifiers The most important part of string formatting is the **specifiers**. | Specifier | Accepts | Outputs | Example Output | | --- | --- | --- | --- | | `c` | integer | | 3 | | `d` or `i` | integer | Decimal representation. | 321 | | `e` or `E` | float | Scientific notation using `e` or `E`. | 3.296e2 3.296E2 | | `f` | float | | 3231.1231 | | `g` or `G` | float | The shorter of `e`/`E` and `f`. | 3E14 3e14 | | `o` | integer | Octal representation. | 610 | | `q` | string | String in a form suitable to be safely read back by the Luau interpreter. The string is written between double quotes and all double quotes, new lines, embedded zeros, and backslashes are correctly escaped. | "print(\"Hi\")" | | `s` | string | | Hello world! | | `u` | integer | Decimal representation. | 3131 | | `x` or `X` | integer | Hexadecimal representation. | 7fa 7FA | | `*` | any | Equivalent to `s` but accepts any variable by converting it to a string using [LuaGlobals.tostring()](/docs/reference/engine/globals/LuaGlobals.md). | table: 0x0123456789abcdef | | `%` | | `%` followed by another `%` will return the `%` sign itself. | % | ``` local str = "The magic word is %s" print(string.format(str, "Roblox")) -- The magic word is Roblox local str = "The magic word is %q" print(string.format(str, "Roblox")) -- The magic word is "Roblox" local str = "Skip to \na new line and \nanother new line!" print(string.format(str, "%q")) --[[ Output: Skip to a new line and another new line! ]] ``` #### Flags | Flag | Description | | --- | --- | | `-` | Left-justify the given field width (see `Width` below). Right justification is the default. | | `+` | Forces a "+" sign to precede a number. Has no effect on negative numbers. | | (space) | One blank space is inserted before a positive number, while negative numbers are unaffected. This is useful for making positive and negative numbers vertically align in a visual stacked list. | | `#` | When used with `o` and `x`/`X`, writes a 0 (octal) or 0x/0X (hex) before values other than zero. When used with `e`/`E` and `f`, forces the output to contain a decimal point, even if no digits would follow (by default, no decimal point is written if no digits follow). When used with `g` or `G`, the result is the same as with `e` or `E` but trailing zeros are not removed. | | `0` | Left-pads the number with zeros instead of empty spaces (see `Width` below). | ```lua local str = "%-10d" print(string.format(str, 300) .. "]") -- 300 ] -- There are 7 spaces between '300' and ']' local str = "%+i versus %+i" print(string.format(str, 300, -300)) -- +300 versus -300 local str = "There is a% i%% chance of rain in Seattle today." print(string.format(str, 100)) -- There is a 100% chance of rain in Seattle today. ``` #### Width | Width | Description | | --- | --- | | (number) | Minimum number of characters to return. If the number of characters to be formatted is less than this number, the result is padded with blank spaces. | ```lua local str = "%012i" print("Score: " .. string.format(str, 15000)) -- Output: Score: 000000015000 -- The output has 12 digits total, left-padded with zeros ``` #### Precision The default precision is 1. If you give a period without a value, the default is 0. | Precision | Description | | --- | --- | | `.`(number) | For integer specifiers (`d`, `i`, `o`, `u`, `x`/`X`), precision specifies the minimum number of digits to be returned. If the value to be formatted is shorter than this number, the result is padded with leading zeros. A precision of 0 means that no character is written for the value 0. For `e`/`E` and `f` specifiers, this is the number of digits to be printed after the decimal point. For `g`/`G` specifiers, this is the maximum number of digits (before the `e`/`E`, if present). For `s`, this is the maximum number of characters to be returned. For `c` and `q`, this has no effect. | ```lua -- Add decimal with precision of 2 for a currency output local str = "$%.2f" print(string.format(str, 300)) -- Output: $300.00 -- Return first 6 letters of a string local str = "%.6s" print(string.format(str, "Robloxian")) -- Output: Roblox local str = "Once upon a time, there was a dragon named %s and it had %.8f horns." print(string.format(str, "Pi", math.pi)) -- Output: Once upon a time, there was a dragon named Pi and it had 3.14159265 horns. ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `formatstring` | `string` | | | | `...` | `string` | | | **Returns:** `string` ### string.gmatch **Signature:** `string.gmatch(s: string, pattern: string): function` Returns an iterator function that returns the next captures from pattern over the string `s` each time it's called. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `s` | `string` | | | | `pattern` | `string` | | | **Returns:** `function` ### string.gsub **Signature:** `string.gsub(s: string, pattern: string, replacement: Variant, replacements: number): string, number` Short for global substitution. Returns a copy of `s` in which all (or the first n, if given) occurrences of the pattern are substituted (replaced) with the given `replacement`. The second value returned is the total number of substitutions made. The `replacement` can be one of several types, each used differently to determine the actual string: - string: The pattern is replaced with the string directly - table: The string that matched the pattern is looked up in the table as a key, and the value (string) is what replaces it, if it exists. - function: Called with the string that matched the pattern, should return the string to replace the matched pattern. An optional final argument can be provided which specifies the maximum number of substitutions to make (for example, stop after 2 replacements) #### Various Examples ```lua -- Basic replacement string.gsub("I love tacos!", "tacos", "Roblox") --> I love Roblox! 1 -- Replacement with a pattern string.gsub("I like red!", "%w+", "word") --> word word word! 3 -- Replacement table string.gsub("I play Roblox.", "%w+", {I="Je", play="joue à"}) --> Je joue à Roblox. 3 -- Replacement function string.gsub("I have 2 cats.", "%d+", function(n) return tonumber(n) * 12 end) --> I have 24 cats. 1 -- Replace only twice string.gsub("aaa", "a", "b", 2) --> bba 2 -- Replacement with capture groups (maximum of nine) string.gsub("love2play Roblox", "(%w+)(%d+)(%w+)%s+(%w+)", "I %1 %2 %3 %4!") --> I love 2 play Roblox! 1 ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `s` | `string` | | The string whose occurrences of the given pattern shall be replaced. | | `pattern` | `string` | | The pattern to be matched and replaced. | | `replacement` | `Variant` | | Determines what should replace the occurrence(s) of the given pattern. | | `replacements` | `number` | | The maximum number of substitutions to make. | **Returns:** `string`, `number` ### string.len **Signature:** `string.len(s: string): int` Returns the length of a string. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `s` | `string` | | | **Returns:** `int` ### string.lower **Signature:** `string.lower(s: string): string` Returns a copy of a string with all uppercase letters changed to lowercase. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `s` | `string` | | | **Returns:** `string` ### string.match **Signature:** `string.match(s: string, pattern: string, init?: number): string` Looks for the first match of pattern in the string `s`. If a match is found, it is returned; otherwise, it returns `nil`. A third, optional numerical argument, init, specifies where to start the search; its default value is 1 and can be negative. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `s` | `string` | | | | `pattern` | `string` | | | | `init` | `number` | `1` | | **Returns:** `string` ### string.pack **Signature:** `string.pack(format: string, ...: Variant): string` Returns a binary string containing the provided arguments. The first argument, `format`, determines the way the remaining arguments are packed; see [here](https://www.lua.org/manual/5.3/manual.html#6.4.2) for options. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `format` | `string` | | | | `...` | `Variant` | | | **Returns:** `string` ### string.packsize **Signature:** `string.packsize(format: string): number` Returns the size in bytes of any string packed with a given description. The sole argument, `format`, determines the way the remaining arguments are packed, but you cannot use `s` and `z` because they have variable lengths. See [here](https://www.lua.org/manual/5.3/manual.html#6.4.2) for options. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `format` | `string` | | | **Returns:** `number` ### string.rep **Signature:** `string.rep(s: string, n: int): string` Returns a string that is the concatenation of `n` copies of the string `s`. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `s` | `string` | | | | `n` | `int` | | | **Returns:** `string` ### string.reverse **Signature:** `string.reverse(s: string): string` Returns a string that is the string `s` reversed. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `s` | `string` | | | **Returns:** `string` ### string.split **Signature:** `string.split(s: string, separator?: string): table` Splits a string into parts based on the defined separator character(s), returning a table of ordered results. If an empty "slice" is located, that part will be returned as an empty string. For instance `string.split("abc||def", "|")` will return a table with three strings: `"abc"`, `""`, and `"def"`. ```lua local values = input:split(",") print(values[1], values[2], values[3]) ``` Also note that whitespace from the original string will be preserved, for example `string.split("abc _ def", "_")` will honor the whitespace on both sides of the `_` separator. By default, the separator character is `,` but you can specify an alternative character or series of characters. **Corner Cases** #### Empty String ```lua "" --> "" ``` #### Empty Slices ```lua "foo,,bar" --> "foo", "", "bar" ",foo" --> "", "foo" "foo," --> "foo", "" "," --> "", "" ",," --> "", "", "" ``` #### Whitespace Preserved ```lua " whitespace " --> " whitespace " "foo , bar" --> "foo ", " bar" ``` #### Invalid UTF-8 ```lua "\xFF" --> "\xFF" "\xFD,\xFE" --> "\xFD", "\xFE" ``` #### Unicode ```lua "," --> U+FF0C FULLWIDTH COMMA "我很高兴,你呢?" --> "我很高兴", "你呢?" "•" --> U+2022 BULLET "hello•world" --> "hello", "world" ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `s` | `string` | | The string to split. | | `separator` | `string` | `,` | The separator character(s) to be used for splitting the string. | **Returns:** `table` ### string.sub **Signature:** `string.sub(s: string, i?: int, j?: int): string` Returns the substring of `s` that starts at `i` and continues until and including `j`. `i` and `j` can be negative. `i` defaults to 1 and `j` defaults to `-1`. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `s` | `string` | | | | `i` | `int` | `1` | | | `j` | `int` | `-1` | | **Returns:** `string` ### string.unpack **Signature:** `string.unpack(format: string, data: string, readStart?: string): Tuple` Extracts the values packed in the provided binary string based on the first argument, `format`, which should match the one originally used to [pack()](/docs/reference/engine/globals/string.md) the string; see [here](https://www.lua.org/manual/5.3/manual.html#6.4.2) for options. The optional third parameter determines the byte at which the reading starts. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `format` | `string` | | | | `data` | `string` | | | | `readStart` | `string` | `1` | | **Returns:** `Tuple` — The values packed into the provided binary string, plus the index of the first unread byte. ### string.upper **Signature:** `string.upper(s: string): string` Returns a copy of a string with all lowercase letters changed to uppercase. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `s` | `string` | | | **Returns:** `string` --- name: table last_updated: 2026-06-29T19:34:09Z type: library summary: "A library of table functions." --- # table A library of table functions. **Type:** library ## Description This library provides generic functions for table/array manipulation, providing all its functions inside the global [table](/docs/reference/engine/globals/table.md) variable. Most functions in the [table](/docs/reference/engine/globals/table.md) library assume that the table represents an array or a list. For these functions, the "length" of a table means the result of the length operator. ## Functions ### table.clear **Signature:** `table.clear(table: table): ()` Sets the value for all keys within the given table to `nil`. This causes the `#` operator to return `0` for the given table. The allocated capacity of the table's array portion is maintained, which allows for efficient re-use of the space. ```lua local grades = {95, 82, 71, 92, 100, 60} print(grades[4], #grades) --> 92, 6 table.clear(grades) print(grades[4], #grades) --> nil, 0 -- If grades is filled again with the same number of entries, -- no potentially expensive array resizing will occur -- because the capacity was maintained by table.clear. ``` This function does not delete/destroy the table provided to it. This function is meant to be used specifically for tables that are to be re-used. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `table` | `table` | | The table whose keys will be cleared. | **Returns:** `()` ### table.clone **Signature:** `table.clone(t: table): table` Returns an unfrozen shallow copy of the provided table. ```lua local original = { key = "value", engine = "Roblox", playerID = 505306092 } local clone = table.clone(original) ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `t` | `table` | | The table to be cloned. | **Returns:** `table` — The clone of the provided table. ### table.concat **Signature:** `table.concat(t: Array, sep: string, i?: int, j: int): string` Given an array where all elements are strings or numbers, returns the string `t[i] ... sep ... t[i+1] ... sep ... t[j]`. The default value for sep is an empty string, the default for `i` is 1, and the default for `j` is #t. If i is greater than `j`, returns the empty string. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `t` | `Array` | | The table that will be converted into a string. | | `sep` | `string` | | The string that will be concatenated between each entry in the table. | | `i` | `int` | `1` | The starting index of the table concatenation. | | `j` | `int` | | The ending index of the table concatenation. | **Returns:** `string` ### table.create **Signature:** `table.create(count: number, value: Variant): table` Creates a table with the array portion allocated to the given `number` of elements, optionally filled with the given `value`. ```lua local t = table.create(3, "Roblox") print(table.concat(t)) --> RobloxRobloxRoblox ``` If you are inserting into large array-like tables and are certain of a reasonable upper limit to the number of elements, it's recommended to use this function to initialize the table. This ensures the table's array portion of its memory is sufficiently sized, as resizing it can be expensive. For small quantities this is typically not noticeable. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `count` | `number` | | | | `value` | `Variant` | | | **Returns:** `table` ### table.find **Signature:** `table.find(haystack: table, needle: Variant, init: number): Variant` Within the given array-like table `haystack`, find the first occurrence of value `needle`, starting from index `init` or the beginning if not provided. If the value is not found, `nil` is returned. A [linear search](https://en.wikipedia.org/wiki/Linear_search) algorithm is performed. ```lua local t = {"a", "b", "c", "d", "e"} print(table.find(t, "d")) --> 4 print(table.find(t, "z")) --> nil, because z is not in the table print(table.find(t, "b", 3)) --> nil, because b appears before index 3 ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `haystack` | `table` | | | | `needle` | `Variant` | | | | `init` | `number` | | | **Returns:** `Variant` ### table.freeze **Signature:** `table.freeze(t: table): table` This function makes the given table read-only, effectively "freezing" it in its current state. Attempting to modify a frozen table throws an error. This freezing effect is shallow, which means that you can write to a table within a frozen table. To deep freeze a table, call this function recursively on all of the descending tables. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `t` | `table` | | The table to be frozen. | **Returns:** `table` — The frozen table. ### table.insert **Signature:** `table.insert(t: Array, pos: number, value: Variant): ()` Inserts the provided value to the target position of the array. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `t` | `Array` | | The table that is being appended to. | | `pos` | `number` | | The position at which the value will be inserted. | | `value` | `Variant` | | The value that will be appended to the table. | **Returns:** `()` ### table.insert **Signature:** `table.insert(t: Array, value: Variant): ()` Appends the provided value to the end of the array. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `t` | `Array` | | The table that is being appended to. | | `value` | `Variant` | | The value that will be appended to the table. | **Returns:** `()` ### table.isfrozen **Signature:** `table.isfrozen(t: table): bool` This function returns `true` if the given table is frozen and `false` if it isn't frozen. You can freeze tables using [table.freeze()](/docs/reference/engine/globals/table.md). **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `t` | `table` | | The table to check. | **Returns:** `bool` — Whether the table is frozen from [table.freeze()](/docs/reference/engine/globals/table.md). ### table.maxn **Signature:** `table.maxn(t: table): number` Returns the maximum numeric key of the provided table, or zero if the table has no numeric keys. Gaps in the table are ignored. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `t` | `table` | | | **Returns:** `number` ### table.move **Signature:** `table.move(src: table, a: number, b: number, t: number, dst?: table): table` Copies elements in table `src` from `src[a]` up to `src[b]` into table `dst` starting at index `t`. Equivalent to the assignment statement `dst[t], ..., dst[t + (b - a)] = src[a], ..., src[b]`. The default for `dst` is `src`. The destination range may overlap with the source range. Returns `dst` for convenience. ```lua local sourceTable = {4, 5} -- Table of data to copy from local destTable = {1, 2, 3} -- Table to add copied data to table.move( sourceTable, -- Source table 1, -- Index to start from in source table #sourceTable, -- Index up to (and including) from source table #destTable + 1, -- Index within destination table to move data into destTable -- Destination table ) print(destTable) --> {1, 2, 3, 4, 5} ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `src` | `table` | | Source table. | | `a` | `number` | | Start copying at `src[a]`. | | `b` | `number` | | Copy up to and including `src[b]`. | | `t` | `number` | | Copy into `dst[t], ...`. | | `dst` | `table` | `src` | Destination table. | **Returns:** `table` — `dst` for convenience. ### table.pack **Signature:** `table.pack(values...: Variant): Variant` Returns a new table with all arguments stored into keys 1, 2, etc. and with a field "n" with the total number of arguments. Note that the resulting table may not be a sequence. ```lua local t = table.pack(1, 2, 3) print(table.concat(t, ", ")) --> 1, 2, 3 ``` **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `values...` | `Variant` | | | **Returns:** `Variant` ### table.remove **Signature:** `table.remove(t: Array, pos: number): Variant` Removes from array t the element at position pos, returning the value of the removed element. When pos is an integer between `1` and `#t`, it shifts down the elements `t[pos+1], t[pos+2], ..., t[#t]` and erases element `t[#t]`. If the pos parameter is not provided, pos defaults to the length of the table removing the last element. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `t` | `Array` | | The table that is having an element removed. | | `pos` | `number` | | The index of the element being removed. | **Returns:** `Variant` ### table.sort **Signature:** `table.sort(t: Array, comp?: function): ()` Sorts elements of array t in a given order, from `t[1]` to `t[#t]`. If `comp` is given, then it must be a function that receives two elements and returns true when the first element must come before the second in the final order. The error `invalid order function for sorting` is thrown if both `comp(a, b)` and `comp(b, a)` return `true`. If `comp` is not given, then the standard Luau operator `<` is used instead. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `t` | `Array` | | | | `comp` | `function` | `nil` | An optional comparison function to be used when comparing elements in the table. This function receives two elements, and should return true if the first element should be sorted before the second in the final order. | **Returns:** `()` ### table.unpack **Signature:** `table.unpack(list: table, i?: number, j?: number): Tuple` Returns the elements from the given list. By default, `i` is 1 and `j` is the length of `list`. Note that this same functionality is also provided by the global [LuaGlobals.unpack()](/docs/reference/engine/globals/LuaGlobals.md) function. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `list` | `table` | | The list of elements to be unpacked. | | `i` | `number` | `1` | The index of the first element to unpack. | | `j` | `number` | `#list` | The index of the last element to unpack. | **Returns:** `Tuple` ### table.foreach *(deprecated)* **Signature:** `table.foreach(t: table, f: function): ()` Iterates over the provided table, passing the key and value of each iteration over to the provided function. This function has been deprecated and is not recommended for use in new code; use a `for` loop instead. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `t` | `table` | | The table to be iterated over. | | `f` | `function` | | The function that will be used for the iteration. This function will receive 2 arguments for each iteration, where the 1st argument is the key, and the 2nd argument is the value. | **Returns:** `()` ### table.foreachi *(deprecated)* **Signature:** `table.foreachi(t: Array, f: function): ()` This is similar to [table.foreach()](/docs/reference/engine/globals/table.md) except that index-value pairs are passed, not key-value pairs. This function has been deprecated and is not recommended for use in new code; use a `for` loop instead. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `t` | `Array` | | The table to be iterated over. | | `f` | `function` | | The function that will be used for the iteration. This function will receive 2 arguments for each iteration, where the 1st argument is the index, and the 2nd argument is the value. | **Returns:** `()` ### table.getn *(deprecated)* **Signature:** `table.getn(t: Array): number` Returns the number of elements in the table passed. This function has been deprecated and is not recommended for use in new code; use `#t` instead. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `t` | `Array` | | The table whose size is being measured. | **Returns:** `number` --- name: task last_updated: 2026-06-29T19:34:09Z type: library summary: "Allows for functions and threads to be coordinated with the engine's scheduler." --- # task Allows for functions and threads to be coordinated with the engine's scheduler. **Type:** library ## Description The **task** library allows for functions and threads to be scheduled with the engine's scheduler. The functions available in this library generally support functions and threads. In most cases using a function is sufficient, but for more advanced cases it's recommended you familiarize yourself with the [coroutine](/docs/reference/engine/globals/coroutine.md) library. ## Functions ### task.spawn **Signature:** `task.spawn(functionOrThread: function | thread, ...: Variant): thread` Accepts a function or a thread (as returned by [coroutine.create()](/docs/reference/engine/globals/coroutine.md)) and calls/resumes it immediately through the engine's scheduler. Arguments after the first are sent to the function/thread. If the calling script is currently running in a serial execution phase, then the spawned function or thread is resumed in the current serial execution phase. If the calling script is currently running in a parallel execution phase, then the spawned function or thread is resumed in the current parallel execution phase. For more information, see [Parallel Luau](/docs/en-us/scripting/multithreading.md). **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `functionOrThread` | `function | thread` | | A function or a thread returned by [coroutine.create()](/docs/reference/engine/globals/coroutine.md). | | `...` | `Variant` | | Arguments to send to the function or thread. | **Returns:** `thread` — The scheduled thread. ### task.defer **Signature:** `task.defer(functionOrThread: function | thread, ...: Variant): thread` Accepts a function or a thread (as returned by [coroutine.create()](/docs/reference/engine/globals/coroutine.md)) and defers it until the end of the current resume point within the current frame. This function should be used when a similar behavior to [task.spawn()](/docs/reference/engine/globals/task.md) is desirable, but the thread does not need to run immediately. If the calling script is currently running in a serial execution phase, then the deferred function or thread is resumed in a serial execution phase. If the calling script is currently running in a parallel execution phase, then the deferred function or thread is resumed in a parallel execution phase. For more information, see [Parallel Luau](/docs/en-us/scripting/multithreading.md). **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `functionOrThread` | `function | thread` | | A function or a thread returned by coroutine.create. | | `...` | `Variant` | | Arguments to send to the function or thread. | **Returns:** `thread` — The scheduled thread. ### task.delay **Signature:** `task.delay(duration: number, functionOrThread: function | thread, ...: Variant): thread` Accepts a function or a thread (as returned by [coroutine.create()](/docs/reference/engine/globals/coroutine.md)) and schedules it to be called/resumed on the next [Heartbeat](/docs/reference/engine/classes/RunService.md) after the given amount of time in seconds has elapsed. Arguments after the second are sent to the function/thread. This function differs from the deprecated global `delay()` function in that **no throttling occurs**: on the very same [Heartbeat](/docs/reference/engine/classes/RunService.md) step in which enough time has passed, the function is guaranteed to be called/resumed. Providing a duration of zero (`0`) will guarantee that the function is called on the very next [Heartbeat](/docs/reference/engine/classes/RunService.md). You can calculate the actual time passed by calling [os.clock()](/docs/reference/engine/globals/os.md) upon scheduling and in the scheduled function. If the calling script is currently running in a serial execution phase, then the delayed function or thread is resumed in a serial execution phase. If the calling script is currently running in a parallel execution phase, then the delayed function or thread is resumed in a parallel execution phase. For more information, see [Parallel Luau](/docs/en-us/scripting/multithreading.md). **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `duration` | `number` | | The minimum number of seconds that must pass before the function/thread is called/resumed. | | `functionOrThread` | `function | thread` | | | | `...` | `Variant` | | Arguments to be passed to the function/thread when it is due to be called/resumed. | **Returns:** `thread` — The scheduled thread. ### task.desynchronize **Signature:** `task.desynchronize(): ()` If the calling script is currently running in a serial execution phase, `desynchronize()` suspends the script and the script will be resumed in the next parallel execution phase. If the calling script is currently running in a parallel execution phase, `desynchronize()` returns immediately and has no effect. Only scripts which are descendants of an [Actor](/docs/reference/engine/classes/Actor.md) may call this method. If a script outside of an [Actor](/docs/reference/engine/classes/Actor.md) calls this method, an error will be raised. [ModuleScripts](/docs/reference/engine/classes/ModuleScript.md) may also call `desynchronize()` as long as the instantiation of the module calling it was required by a script that is a descendant of an [Actor](/docs/reference/engine/classes/Actor.md). For more information, see [Parallel Luau](/docs/en-us/scripting/multithreading.md). **Returns:** `()` ### task.synchronize **Signature:** `task.synchronize(): ()` If the calling script is currently running in a parallel execution phase, `synchronize()` suspends the script and the script will be resumed in the next serial execution phase. If the calling script is currently running in a serial execution phase, `synchronize()` returns immediately and has no effect. Only scripts which are descendants of an [Actor](/docs/reference/engine/classes/Actor.md) may call this method. If a script outside of an [Actor](/docs/reference/engine/classes/Actor.md) calls this method, an error will be raised. [ModuleScripts](/docs/reference/engine/classes/ModuleScript.md) may also call `synchronize()` as long as the instantiation of the module calling it was required by a script that is a descendant of an [Actor](/docs/reference/engine/classes/Actor.md). For more information, see [Parallel Luau](/docs/en-us/scripting/multithreading.md). **Returns:** `()` ### task.wait **Signature:** `task.wait(duration?: number): number` Yields the current thread until the given duration (in seconds) has elapsed, then resumes the thread on the next [Heartbeat](/docs/reference/engine/classes/RunService.md) step. The actual amount of time elapsed is returned. If no duration is given, it will default to zero (`0`). This means the thread resumes on the very next step, which is equivalent in behavior to [RunService.Heartbeat:Wait()](/docs/reference/engine/classes/RunService.md) Unlike the deprecated global `wait()`, this function **does not throttle** and guarantees the resumption of the thread on the first Heartbeat that occurs when it is due. This function also only returns the elapsed time and nothing else. If the calling script is currently running in a serial execution phase, then the script is resumed in a serial execution phase. If the calling script is currently running in a parallel execution phase, then the script is resumed in a parallel execution phase. For more information, see [Parallel Luau](/docs/en-us/scripting/multithreading.md). **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `duration` | `number` | `0` | The amount of time in seconds that should elapse before the current thread is resumed. | **Returns:** `number` ### task.cancel **Signature:** `task.cancel(thread: thread): ()` Cancels a thread and closes it, preventing it from being resumed manually or by the engine's scheduler. This function can be used with other members of the **task** library that return a thread to cancel them before they are resumed. For example: ```lua local thread = task.delay(5, function() print("Hello world!") end) task.cancel(thread) ``` Note that threads may be in a state where it is impossible to cancel them. For example, the currently executing thread and threads that have resumed another coroutine may not be cancelled. If this is the case, an error will be generated. However, code should not rely on specific thread states or conditions causing [task.cancel()](/docs/reference/engine/globals/task.md) to fail. It is possible that future updates will eliminate these constraints and allow threads in these states to be successfully cancelled. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `thread` | `thread` | | The thread that will be cancelled. | **Returns:** `()` --- name: utf8 last_updated: 2026-06-29T19:34:09Z type: library summary: "This library provides basic support for `UTF-8` encoding." --- # utf8 This library provides basic support for `UTF-8` encoding. **Type:** library ## Description This library provides basic support for `UTF-8` encoding. This library does not provide any support for Unicode other than the handling of the encoding. Any operation that needs the meaning of a character, such as character classification, is outside its scope. Unless stated otherwise, all functions that expect a byte position as a parameter assume that the given position is either the start of a byte sequence or one plus the length of the subject string. As in the string library, negative indices count from the end of the string. You can find a large catalog of usable `UTF-8` characters [here](https://www.w3schools.com/charsets/ref_html_utf8.asp). ## Properties ### utf8.charpattern **Type:** `string` The pattern `"[%z\x01-\x7F\xC2-\xF4][\x80-\xBF]*"`, which matches exactly zero or more UTF-8 byte sequence, assuming that the subject is a valid UTF-8 string. ## Functions ### utf8.char **Signature:** `utf8.char(codepoints: Tuple): string` Receives zero or more codepoints as integers, converts each one to its corresponding UTF-8 byte sequence and returns a string with the concatenation of all these sequences. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `codepoints` | `Tuple` | | | **Returns:** `string` ### utf8.codes **Signature:** `utf8.codes(str: string): function, string, int` Returns an iterator function so that the construction: ```lua for position, codepoint in utf8.codes(str) do -- body end ``` will iterate over all codepoints in string `str`. It raises an error if it meets any invalid byte sequence. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `str` | `string` | | The string to iterate over. | **Returns:** `function`, `string`, `int` ### utf8.codepoint **Signature:** `utf8.codepoint(str: string, i?: int, j?: int): Tuple` Returns the codepoints (as integers) from all codepoints in the provided string (str) that start between byte positions `i` and `j` (both included). The default for `i` is `1` and for `j` is `i`. It raises an error if it meets any invalid byte sequence. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `str` | `string` | | | | `i` | `int` | `1` | The index of the codepoint that should be fetched from this string. | | `j` | `int` | `i` | The index of the last codepoint between `i` and `j` that will be returned. If excluded, this will default to the value of `i`. | **Returns:** `Tuple` ### utf8.len **Signature:** `utf8.len(s: string, i?: int, j?: int): int` Returns the number of UTF-8 codepoints in the string _str_ that start between positions `i` and `j` (both inclusive). The default for `i` is `1` and for `j` is `-1`. If it finds any invalid byte sequence, returns a nil value plus the position of the first invalid byte. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `s` | `string` | | | | `i` | `int` | `1` | The starting position. | | `j` | `int` | `-1` | The ending position. | **Returns:** `int` ### utf8.offset **Signature:** `utf8.offset(s: string, n: int, i?: int): int?` Returns the position (in bytes) where the encoding of the `n`‑th codepoint of `s` (counting from byte position `i`) starts. A negative `n` gets characters before position `i`. The default for `i` is `1` when `n` is non-negative and `#s + 1` otherwise, so that `utf8.offset(s, -n)` gets the offset of the `n`‑th character from the end of the string. If the specified character is neither in the subject nor right after its end, the function returns `nil`. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `s` | `string` | | | | `n` | `int` | | | | `i` | `int` | `1` | | **Returns:** `int?` ### utf8.graphemes **Signature:** `utf8.graphemes(str: string, i: number, j: number): function` Returns an iterator function so that ```lua for first, last in utf8.graphemes(str) do local grapheme = s:sub(first, last) -- body end ``` will iterate the grapheme clusters of the string. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `str` | `string` | | | | `i` | `number` | | | | `j` | `number` | | | **Returns:** `function` ### utf8.nfcnormalize **Signature:** `utf8.nfcnormalize(str: string): string` Converts the input string to Normal Form C, which tries to convert decomposed characters into composed characters. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `str` | `string` | | | **Returns:** `string` ### utf8.nfdnormalize **Signature:** `utf8.nfdnormalize(str: string): string` Converts the input string to Normal Form D, which tries to break up composed characters into decomposed characters. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `str` | `string` | | The string to convert. | **Returns:** `string` --- name: vector last_updated: 2026-06-29T19:34:09Z type: library summary: "A library of vector functions." --- # vector A library of vector functions. **Type:** library ## Description This library implements functionality for the vector type in addition to the built-in primitive operator support. It uses vectors with three components (`x`, `y`, and `z`). Individual vector components can be accessed using the fields `x` or `X`, `y` or `Y`, `z` or `Z`. Since vector values are immutable, writing to individual components is not supported. ## Properties ### vector.zero **Type:** `vector` Constant vector with all components set to `0`. ### vector.one **Type:** `vector` Constant vector with all components set to `1`. ## Functions ### vector.create **Signature:** `vector.create(x: number, y: number, z: number): vector` Creates a new vector with the given component values. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `x` | `number` | | | | `y` | `number` | | | | `z` | `number` | | | **Returns:** `vector` ### vector.magnitude **Signature:** `vector.magnitude(vec: vector): number` Calculates the magnitude of a given vector. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `vec` | `vector` | | | **Returns:** `number` ### vector.normalize **Signature:** `vector.normalize(vec: vector): vector` Computes the normalized version (unit vector) of a given vector. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `vec` | `vector` | | | **Returns:** `vector` ### vector.cross **Signature:** `vector.cross(vec1: vector, vec2: vector): vector` Computes the cross product of two vectors. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `vec1` | `vector` | | | | `vec2` | `vector` | | | **Returns:** `vector` ### vector.dot **Signature:** `vector.dot(vec1: vector, vec2: vector): number` Computes the dot product of two vectors. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `vec1` | `vector` | | | | `vec2` | `vector` | | | **Returns:** `number` ### vector.angle **Signature:** `vector.angle(vec1: vector, vec2: vector, axis: vector?): number` Computes the angle between two vectors in radians. The axis, if specified, is used to determine the sign of the angle. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `vec1` | `vector` | | | | `vec2` | `vector` | | | | `axis` | `vector?` | | | **Returns:** `number` ### vector.floor **Signature:** `vector.floor(vec: vector): vector` Applies [math.floor()](/docs/reference/engine/globals/math.md) to every component of the input vector. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `vec` | `vector` | | | **Returns:** `vector` ### vector.ceil **Signature:** `vector.ceil(vec: vector): vector` Applies [math.ceil()](/docs/reference/engine/globals/math.md) to every component of the input vector. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `vec` | `vector` | | | **Returns:** `vector` ### vector.abs **Signature:** `vector.abs(vec: vector): vector` Applies [math.abs()](/docs/reference/engine/globals/math.md) to every component of the input vector. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `vec` | `vector` | | | **Returns:** `vector` ### vector.sign **Signature:** `vector.sign(vec: vector): vector` Applies [math.sign()](/docs/reference/engine/globals/math.md) to every component of the input vector. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `vec` | `vector` | | | **Returns:** `vector` ### vector.clamp **Signature:** `vector.clamp(vec: vector, min: vector, max: vector): vector` Applies [math.clamp()](/docs/reference/engine/globals/math.md) to every component of the input vector. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `vec` | `vector` | | | | `min` | `vector` | | | | `max` | `vector` | | | **Returns:** `vector` ### vector.lerp **Signature:** `vector.lerp(vec1: vector, vec2: vector, alpha: number): vector` Returns a vector linearly interpolated between two vectors (`vec1`, `vec2`) by the fraction `alpha`. Note that `alpha` is **not** limited to the range `[0, 1]`. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `vec1` | `vector` | | | | `vec2` | `vector` | | | | `alpha` | `number` | | | **Returns:** `vector` ### vector.max **Signature:** `vector.max(...: vector): vector` Applies [math.max()](/docs/reference/engine/globals/math.md) to the corresponding components of the input vectors. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `...` | `vector` | | | **Returns:** `vector` ### vector.min **Signature:** `vector.min(...: vector): vector` Applies [math.min()](/docs/reference/engine/globals/math.md) to the corresponding components of the input vectors. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `...` | `vector` | | | **Returns:** `vector` --- title: "Roblox Engine libraries" url: /docs/en-us/reference/engine/libraries last_updated: 2026-06-29T19:34:09Z description: "Libraries are groups of functions that you can use to manipulate built-in data types." --- # Roblox Engine libraries --- title: "Roblox Engine API reference" url: /docs/en-us/reference/engine/overview last_updated: 2026-06-29T19:34:09Z description: "This API reference documents all of Roblox's classes, data types, enumerations, functions, events, callbacks, and properties." --- # Roblox Engine API reference --- title: "Building system" url: /docs/en-us/resources/battle-royale/building-system last_updated: 2026-06-29T19:34:09Z description: "Explains the process of creating a building system in the Battle Royale kit." --- # Building system Roblox Battle Royale features a building system that lets users build their own structures to reach higher ground for a better shooting angle, or to provide cover during battle. Using a menu, the user can select from a number of different tile types and place them in the world. ![Building Example](../../assets/resources/battle-royale/building-system/Battle-Royale-Building-View.jpeg) To maintain an orderly and structured system: - Each tile must connect to another existing tile or to the ground. - Place all building tiles within a grid coordinated with the 3D world so that tiles connect properly and do not overlap. ## Tile composition In order for users to build new tiles quickly and easily, the building system needs to understand which tiles are connected to each other and where new tiles can be placed on the grid. Tiles use 3 components: the [visual asset](#asset) that is used as the tile object, information about the [space the tile occupies](#occupancy) in reference to other world objects, and the [connection points](#connectivity) of a tile which allow additional tiles to connect to available edges. ### Asset An asset is a `Class.BasePart` (`Class.Part`, `Class.MeshPart`, etc.) placed in `ReplicatedStorage/BuildingSystem/Assets` representing what the tile looks like in the 3D world. This `Class.BasePart` must have two children: - **ObjectType** — A `Class.StringValue` with a `Value` of the tile type name. - **StartingRotation** — A `Class.NumberValue` with a `Value` of the tile's starting rotation when a user places it in the world. ### Occupancy Ensuring that a tile occupies the correct location in a grid system is important and can be done using a bitmask. A bitmask represents the physical space the tile type takes up within a basic section of world space, or cell. Each building system tile/object can occupy any number of faces of a grid cell (front, left, back, right, top, and bottom) and any number of octants in a grid cell. A user can only place a building system object in a cell if all the faces and octants that the object occupies are available in that cell. Occupancy for a building system object is defined as follows: | Bit index | Meaning | | --- | --- | | 0 | Bottom face of cell | | 1 | Top face of cell | | 2 | Right face of cell | | 3 | Back face of cell | | 4 | Left face of cell | | 5 | Front face of cell | | 6 | **+X, –Y, +Z** octant of cell with origin at center of cell | | 7 | **+X, +Y, +Z** octant of cell with origin at center of cell | | 8 | **–X, –Y, +Z** octant of cell with origin at center of cell | | 9 | **–X, +Y, +Z** octant of cell with origin at center of cell | | 10 | **–X, –Y, –Z** octant of cell with origin at center of cell | | 11 | **–X, +Y, –Z** octant of cell with origin at center of cell | | 12 | **+X, –Y, –Z** octant of cell with origin at center of cell | | 13 | **+X, +Y, –Z** octant of cell with origin at center of cell | ### Connectivity Tiles need to connect to each other to create a structure. You can use a bitmask which represents what portions of the cell's faces/edges the tile type touches. A building system tile/object's connectivity is represented by a series of connection points, bits, on the surface of a cell. They arrange in the following way on the faces of the cell: ![Octant Cube Diagram](../../assets/resources/battle-royale/building-system/Octant-Cube.png) A building system object is considered connected to another building system object in the grid if they share at least 2 connectivity points on the surface of a cell. ## Create new tile types To create a new tile type: 1. Place the asset in `ReplicatedStorage/BuildingSystem/Assets`. Remember that it must contain `ObjectType` and `StartingRotation` children as defined above.![Battle Royale Weapon Example](../../assets/resources/battle-royale/building-system/Battle-Royale-New-Tile-1.png) 2. Define the [Occupancy](#occupancy) and [Connectivity](#connectivity) for your new tile type in `ReplicatedStorage/BuildingSystem/Libraries/Grid/ObjectTypeConfigurations`:![Battle Royale Weapon Example](../../assets/resources/battle-royale/building-system/Battle-Royale-New-Tile-2.png) For example, the **Wall** tile type is expressed as follows: ```lua ObjectTypeConfigurations.Wall = { ASSET_OFFSET_FROM_CENTER = Vector3.new(0, 0, -CELL_DIMENSIONS.Z / 2), CONNECTIVITY = 0xFF8000, -- 0b 111 111 111 000 000 000 000 000 OCCUPANCY = 0x20, -- 0b 00 00 00 00 1 0 0 0 0 0 } ``` ## Work with terrain If you would like to create a map with free-form [terrain](/docs/en-us/parts/terrain.md) that lines up perfectly with the building system, we recommend that you use the **heightmap import** system as outlined in [Environmental Terrain](/docs/en-us/parts/terrain.md) and that you observe the following details: - Terrain in Roblox is represented by a three dimensional array of voxels, each of which occupies a cube of 4×4×4 studs in the world. Because of this, Roblox Battle Royale uses a grid cell size of 20×16×20 studs, with wall tiles that are 20×16×1 studs and floor/ceiling tiles that are 20×1×20 studs in size. This allows building tiles and terrain to line up properly. - Each pixel in your heightmap image should represent one terrain voxel in the **X/Z** plane, so a 5×5 area in pixels represents one building system grid cell that occupies a 20×20 stud area. - In your heightmap image, make sure the red channel or grey channel value for each pixel is divisible by the height of your building system grid cell so that the floor tiles sit nicely on top of the terrain. - When you import your heightmap, set:```lua Position (X, Y, Z) = 0, 0, 0 Size (X, Y, Z) = heightmap_width × 4, 256, heightmap_length × 4 ``` --- title: "Core scripts" url: /docs/en-us/resources/battle-royale/core-scripts last_updated: 2026-06-29T19:34:09Z description: "Explains the two scripts responsible for the Battle Royale gameplay loop." --- # Core scripts The core game loop in Roblox Battle Royale is generally handled by two scripts, specifically `ServerScriptService/Server` on the server and `StarterPlayer/StarterPlayerScripts/Client` on the client. ## Initial setup The `ReplicatedFirst/InitialSetup` script configures a few engine-level systems — built-in UI, chat, etc. — and ensures that the client loads the assets referenced in `ReplicatedFirst/Configurations/AssetPreloads` before proceeding. ## Game stages The initialization and updating of specific systems is done differently depending on the current stage of the experience. Most of these cases are handled by `ServerScriptService/Core/GameStageHandler` on the server and `ReplicatedStorage/Core/StageManager` on the client. When a stage is requested, a module of the same name is required and various setup functions called on it. If there is already a module handling the current stage, shutdown functions are called on it prior to the new stage handler being initialized. ## Places/server roles Within `ReplicatedFirst/Configurations/MainConfiguration`, different gameplay modes are organized into lists of stages to be executed. ```lua local _roleStages = { Lobby = {"Lobby"}, Queue = {"Queue"}, Gameplay = {"Waiting", "Gameplay", "EndGame"}, } ``` There are 3 different roles that a place can have: - **Lobby** — Initial place where options like game mode are selected. - **Queue** — Where a cohort of players gather for a particular game mode, building and fighting while waiting for the configured number of minimum players. Once the minimum numbers of players have arrived, a countdown will start, currently configured for 3:00 minutes, at the end of which gameplay will be initiated. - **Gameplay** — The place where skydiving takes place and the game is played until completion. --- title: "Battle Royale" url: /docs/en-us/resources/battle-royale last_updated: 2026-06-29T19:34:09Z description: "Battle Royale is an example game-kit with instructions on specific gameplay implementations." --- # Battle Royale > **Warning:** Processes and features may have changed since the writing of this documentation. Refer to the appropriate feature documentation for up-to-date information on any features and workflows. > > The content of this project and documentation can be used under Roblox's [Limited Use License](/docs/en-us/resources/limited-use-license.md). **Roblox Battle Royale** is a game kit built by internal Roblox developers consisting of multiplayer battles on a large island with destructible buildings, a variety of vehicles and weapons, and interesting areas to explore. The default game loop is simple: join a match, skydive onto the island, stay out of the storm, and survive until you are the last person standing. Players can also build their own structures by laying down building tiles. This project is [available to join](https://www.roblox.com/games/5158731546/Roblox-Battle-Royale) as a public experience. ![Drop onto a region in a classic battle royale experience.](../../assets/resources/battle-royale/introduction/Battle-Royale-Slide-B.jpeg) ## Features At a high level, Roblox Battle Royale contains the following: - Code for 3rd-person cameras. - A robust and customizable ranged [weapon system](/docs/en-us/resources/weapons-kit.md), including 9 different weapons preconfigured. - A building system. - A destruction system. All of these systems are supported on a wide range of hardware, screen sizes, and game controllers, from mobile phones to Xbox to high-end desktop PCs. The code is designed to be performant and easy to read so anyone can learn how to build a game like this. ## Play modes There are 4 play modes included in this project: - **Last One Standing** — Traditional battle royale; players who are eliminated are removed from the match and the last person to survive wins. - **Solo Deathmatch** — Players will respawn after dying; the player with the highest kill count wins. - **Team Deathmatch** — Two teams of players attempt to eliminate the other team; the team with players still alive at the end wins. - **Free Play** — Players can enter the game and play around with the building system, weapons, and vehicles. --- title: "Installation and setup" url: /docs/en-us/resources/battle-royale/installation-and-setup last_updated: 2026-06-29T19:34:09Z description: "Explains the installation and setup details for the Battle Royale game kit." --- # Installation and setup To setup the Battle Royale experience, you must download [Studio](/docs/en-us/studio/setup.md) and the project [reference files](#reference-files). Additional configuration of the [place IDs](#copy-and-paste-place-ids), [server](#adjust-server-fill), and [publishing](#publish-additional-places) settings are also required before continuing on to [running the game](/docs/en-us/resources/battle-royale/run-the-game.md). ## Reference files [RobloxBattleRoyale.zip](../../assets/resources/battle-royale/installation-and-setup/RobloxBattleRoyale.zip) consists of easily accessible `.rbxl` files which you can open in Roblox Studio and experiment with immediately. | Name | Description | | --- | --- | | Lobby.rbxl | Entrance lobby where players choose the game mode. | | Gameplay.rbxl | Where the battle match takes place. | | Queue.rbxl | Queue place where players gather before being teleported to battle map. | ## Create a new game Roblox Battle Royale must be structured as a game with six unique places. To begin: 1. Open `Lobby.rbxl` in Roblox Studio.![Lobby View.](../../assets/resources/battle-royale/installation-and-setup/Battle-Royale-Lobby-View.jpeg) 2. Select **File** → **Publish As…** to open the publishing window. 3. Near the bottom of the window, click **Create new experience**. 4. Type in **Lobby** for the place name.![Set Lobby Name](../../assets/resources/battle-royale/installation-and-setup/Battle-Royale-Lobby-Set-Name.png) 5. For the **Creator** field, select "Me" to publish the place to your personal account, or select a group. 6. When ready, click the **Create** button. ## Add additional places Once the lobby place is published, you'll need to add **five additional places** to the game: 1. If it's not already visible, open the **Asset Manager** window. 2. Double-click the **Places** folder.![Select Places](../../assets/resources/battle-royale/installation-and-setup/Battle-Royale-AM-Select-Places-1.png) 3. Right-click in any empty region of the window (not over a place name/tile) and select **Add New Place**. Repeat this a total of five times so that you have six places. 4. Right-click each of the **new** places, select **Rename**, and name them as follows:![Battle royale Place Names](../../assets/resources/battle-royale/installation-and-setup/Battle-Royale-Place-Names.png) 5. Publish the game again (**File** → **Publish to Roblox**). ## Copy and paste place IDs Each place must be cross-associated so that players can teleport from the lobby to various play mode queues and vice-versa. To achieve this, you'll need to gather the place IDs of the places you created above. 1. In the **Asset Manager** window, right-click **Lobby** and select **Copy ID to Clipboard**.![Copy Lobby ID](../../assets/resources/battle-royale/installation-and-setup/Battle-Royale-Lobby-Copy-ID.png) 2. If it's not already visible, open the **Explorer** window. 3. Open the **MainConfiguration** script within **ReplicatedFirst** → **Configurations**.![MainConfiguration Script](../../assets/resources/battle-royale/installation-and-setup/Battle-Royale-Place-MainConfiguration.png) 4. Locate the `_places` table and paste the copied ID from step #1 as the value of the `lobby` key.```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local RunService = game:GetService("RunService") local Players = game:GetService("Players") local isServer = RunService:IsServer() local ConfigEvent = nil local _placeOverrides = {} local _overrides = {} --- -- List of named places in the game local _places = { lobby = 0123456789, gameplay_development = 0, queue_default = 0, queue_deathmatch = 0, queue_teamDeathmatch = 0, queue_freePlay = 0 } --- ``` 5. Repeat the **Copy ID to Clipboard** process for the other five places and paste them into the associated `_places` table key value. | Place | Table key | | --- | --- | | Lobby | lobby | | Gameplay | gameplay_development | | Queue (Default) | queue_default | | Queue (Deathmatch) | queue_deathmatch | | Queue (Team Deathmatch) | queue_teamDeathmatch | | Queue (Free Play) | queue_freePlay |```lua -- List of named places in the game local _places = { lobby = 0123456789, gameplay_development = 0987654321, queue_default = 0123459876, queue_deathmatch = 0987651234, queue_teamDeathmatch = 0132457689, queue_freePlay = 0678912345 } ``` 6. Publish the game again (**File** → **Publish to Roblox**). ## Adjust server fill By default, Roblox balances players/servers for an optimal social gameplay experience, but a battle royale should allow for bigger and more intense battles. To achieve this: 1. Open Studio's **File** ⟩ **Experience Settings** window. 2. Select the **Places** tab. 3. For each of the six places, click the button and select **Edit**.![Edit Place Settings](../../assets/resources/battle-royale/installation-and-setup/Battle-Royale-Edit-Place.png) 4. For **Server Fill**, select **Maximum**.![Set Server Fill to Maximum.](../../assets/resources/battle-royale/installation-and-setup/Place-Server-Fill-Maximum.png) 5. Click **Save** at the bottom of the window. ## Publish additional places Now you'll need to open the remaining `.rbxl` files from the downloaded bundle, modify their `_places` tables, and publish them. ### Copy places table 1. Refer to the `_places` table in the lobby's **MainConfiguration** script: ```lua -------------------------------------- -- List of named places in the game local _places = { lobby = 0123456789, gameplay_development = 0987654321, queue_default = 0123459876, queue_deathmatch = 0987651234, queue_teamDeathmatch = 0132457689, queue_freePlay = 0678912345 } -------------------------------------- ``` 1. Select the **entire table** and copy it to the clipboard with `Ctrl``C` (`⌘``C` on Mac). 2. Close the lobby place by clicking the X in its tab.![Close Lobby Tab.](../../assets/resources/battle-royale/installation-and-setup/Battle-Royale-Lobby-Close.png) ### Replace tables 1. Open the `Gameplay.rbxl` file. 2. Open its **MainConfiguration** script within **ReplicatedFirst** → **Configurations**.![MainConfiguration Script](../../assets/resources/battle-royale/installation-and-setup//Battle-Royale-Place-MainConfiguration.png) 3. Paste the `_places` table you copied above over the existing `_places` table (`Ctrl``V`; `⌘``V`) so that each place's tables are identical.```lua -------------------------------------- -- List of named places in the game local _places = { lobby = 0123456789, gameplay_development = 0987654321, queue_default = 0123459876, queue_deathmatch = 0987651234, queue_teamDeathmatch = 0132457689, queue_freePlay = 0678912345 } -------------------------------------- ``` 4. Select **File** → **Publish As…** to open the publishing window. 5. Near the bottom of the window, click **Update existing experience…**. 6. Locate and click the **Lobby** place that you published earlier. 7. On the next screen, you should see a list of the places you added earlier. From the list, select the **Gameplay** place and click the **Overwrite** button.![Overwrite Existing Place](../../assets/resources/battle-royale/installation-and-setup/Battle-Royale-Place-Overwrite.png) 8. Once the place is published, close it by clicking the X in its tab.![Close the place tab.](../../assets/resources/battle-royale/installation-and-setup/Battle-Royale-Gameplay-Close.png) 9. Open the `Queue.rbxl` file and repeat this process, using **File** → **Publish As…** to publish it to all four queue places. Essentially, `Queue.rbxl` should be published to the **Queue (Default)**, **Queue (Deathmatch)**, **Queue (Team Deathmatch)**, and **Queue (Free Play)** slots. | File | Publish Slot | | --- | --- | | Lobby.rbxl | Lobby | | Gameplay.rbxl | Gameplay | | Queue.rbxl | Queue (Default) | | Queue.rbxl | Queue (Deathmatch) | | Queue.rbxl | Queue (Team Deathmatch) | | Queue.rbxl | Queue (Free Play) | --- title: "Minimap system" url: /docs/en-us/resources/battle-royale/minimap-system last_updated: 2026-06-29T19:34:09Z description: "Explains the minimap implementation details for the Battle Royale game kit." --- # Minimap system The minimap shows a subset of the world map at the top-right corner of the player's screen. It also displays an indicator to show where you are, where the delivery vehicle is, and where your teammates are when playing in team mode. Lastly, it shows the current state of the [storm](/docs/en-us/resources/battle-royale/the-storm.md) and where the storm will be next so players know how to avoid it. ![Minimap example](../../assets/resources/battle-royale/minimap-system/Battle-Royale-Minimap.jpeg) Players can toggle between the minimap and world map by pressing `M` on a keyboard, pressing up on a gamepad's DPad, or by touching the minimap on touchscreens. ## Structure The minimap is simply an `Class.ImageLabel` that displays a previously-generated image and uses `Class.CollectionService` to show the position of objects with specific tags. All of the UI objects used for the minimap are inside of the minimap screen GUI located in `ReplicatedStorage/Assets/GuiObjects`. ![Minimap Objects](../../assets/resources/battle-royale/minimap-system/Battle-Royale-Minimap-Objects.png) ## Add indicators To add new indicators that show up on the minimap, complete these steps: 1. Through the [Tags](/docs/en-us/studio/properties.md#instance-tags) section of its properties, apply a custom tag to the workspace object that you want to show up on the minimap. 2. Add the `Class.ImageLabel` you want to represent the object as a child of `ReplicatedStorage/Assets/GuiObjects/minimap/mapframe` and give it a unique name. 3. Inside of `ReplicatedStorage/Libraries/Guis/MinimapGui`, locate the `MinimapGui.start()` function. Inside it, register the tag as shown below, where tag is the tag you registered in step #1 and `indicatorLabel` is the name of the `Class.ImageLabel` you added in step #2.```lua function MinimapGui.start(teamDividingAngle) while not _setupFinished do task.wait() end MinimapGui.addMapTag("DeliveryVehicle", "Bus") MinimapGui.addMapTag(Util._clientFocusTag, "LocalPlayerLocation") MinimapGui.addMapTag("Player") MinimapGui.addMapTag("Vehicle", "VehicleLocation") MinimapGui.addMapTag(tag, indicatorLabel) ``` ## Customize the minimap To customize the minimap or use the minimap system with your own map and minimap image, you can change these values in `ReplicatedFirst/Configurations/MainConfiguration`: | Variable | Description | | --- | --- | | `map_size` | The size of one edge of your map in studs. Note the minimap assumes your map is square and that the map center is located at this world point: `Vector3.new(map_size, 0, map_size`). | | `minimap_width` | Width in `Datatype.UDim` scale of the minimap on the player's screen. | | `minimap_height` | Height in `Datatype.UDim` scale of the minimap on the player's screen. | | `minimap_zoom` | Amount the minimap is zoomed in on the world map. | | `worldmap_width` | Width in `Datatype.UDim` scale of the world map on the player's screen. | | `worldmap_height` | Width in `Datatype.UDim` scale of the world map on the player's screen. | | `worldmap_zoom` | Amount the world map is zoomed in on the worldmap. | --- title: "Pickup system" url: /docs/en-us/resources/battle-royale/pickup-system last_updated: 2026-06-29T19:34:09Z description: "Explains the pickup system for the Battle Royale game kit." --- # Pickup system The Roblox Battle Royale **pickup system** lets players pick up different kinds of objects, although it's currently only used for weapon pickups. In game, weapons are spawned around the game map and — when players get close enough — an on-screen key/action/button prompt appears along with the weapon name and description. ![Battle Royale Weapon Example](../../assets/resources/battle-royale/pickup-system/Battle-Royale-Weapon-Pickup.jpg) > **Info:** Roblox Battle Royale uses the [Weapons Kit](/docs/en-us/resources/weapons-kit.md), so please consult its documentation for details and customization options. ## Structure There are several important folders related to the pickup system. Make sure that these folders are set up correctly in your project: - `Workspace/PickupSpawners` — Contains pickup spawner `Class.Part|Parts` which tell the system where to place visual pickup `Class.Model|Models` (see the next point). Note that these spawners are **not required** to be in this folder since the system looks for parts tagged with the **PickupSpawner** tag instead of the folder path.![Pickup Spawners](../../assets/resources/battle-royale/pickup-system/Battle-Royale-PickupSpawners.png) > **Info:** Pickup spawners can be placed at any physical location in the game, but they will choose a weapon **randomly** from `ReplicatedStorage/Assets/Weapons` and use the name-matched pickup model as a visual representation. - `ReplicatedStorage/Assets/Weapons` — Contains the weapons (functional `Class.Tool|Tools`) that the pickup system grants when a weapon pickup is activated.![Battle Royale Items](../../assets/resources/battle-royale/pickup-system/Battle-Royale-Weapons.png) - `ReplicatedStorage/Assets/Pickups` — Contains the pickup `Class.Model|Models` that the system will place at pickup spawners in the game world. **These should be visual models only**, not functional weapon Tools.![Battle Royale Pickups](../../assets/resources/battle-royale/pickup-system/Battle-Royale-Pickups.png) ## Add new pickups As noted above, pickups require both a functional `Class.Tool` and a visual `Class.Model` that will be spawned in the game world. ### Tool 1. Create a `Class.Tool` and give it a unique name. You can create new weapons based upon those in the [Weapons Kit](/docs/en-us/resources/weapons-kit.md) or take tools from the [Toolbox](/docs/en-us/projects/assets/toolbox.md). 2. Place the `Class.Tool` in `ReplicatedStorage/Assets/Weapons`.![Battle Royale New Weapon](../../assets/resources/battle-royale/pickup-system/Battle-Royale-New-Weapon.png) ### Model 1. Create a `Class.Model` for the visual pickup and give it the **same name** as you gave the `Class.Tool`. 2. Through the [Tags](/docs/en-us/studio/properties.md#instance-tags) section of its properties, apply the following tags to the model: - **Action** - **Pickup** - **WeaponPickup** - **WeaponSystemIgnore** - One of the rarity tags as outlined in [Rarity](#rarity). 3. Place the model in `ReplicatedStorage/Assets/Pickups`.![Battle Royale New Pickup](../../assets/resources/battle-royale/pickup-system/Battle-Royale-New-Pickup.png) ### Rarity Pickup rarity is not defined by any mathematical formula, but you can associate an on-screen GUI like those pictured below to suggest an item's rarity. 1. Open the `ReplicatedFirst/Configurations/RarityConfiguration` script. This script contains tables for each rarity category, each of which includes a color value (`Color`) for the pickup's particle effect and an asset ID (`Image`) for the on-screen GUI background. For each GUI: - The item name will appear as the model/weapon name. - The description will appear as the rarity name (such as **Epic**) plus **Item**. The default rarities are as follows, but feel free to define your own. | Rarity | GUI | | --- | --- | | Common | | | Uncommon | | | Rare | | | Epic | | | Legendary | | | Special | | 2. For the pickup `Class.Model` you created previously (located in `ReplicatedStorage/Assets/Pickups`), assign one of the tags you've defined in the `RarityConfiguration` script. --- title: "Run the game" url: /docs/en-us/resources/battle-royale/run-the-game last_updated: 2026-06-29T19:34:09Z description: "Explains the playtest instructions for the Battle Royale game kit." --- # Run the game Once all six places are published, you're ready to playtest. 1. If you have any places currently open in Studio, close them. 2. Make sure the Recent button is selected in the left column.![Lobby View.](../../assets/resources/battle-royale/running-the-game/Studio-Recent-Games.png) 3. Hover your mouse over the Lobby icon, click the button, and select Open place page from the context menu. 4. From the target page, click the large play button to test your experience. ## Game controls The following are the various keyboard and mouse, mobile, and gamepad controls set for this project. See [Inputs](/docs/en-us/input.md) for more information on customizing your own controls. ### General controls | Action | Keyboard and mouse controls | Mobile controls | Controller controls | | --- | --- | --- | --- | | Move forward | `W` or `↑` | Move thumbstick forward | Move left analog stick forward | | Move backward | `S` or `↓` | Move thumbstick back | Move left analog stick back | | Move (strafe) left | `A` or `←` | Move thumbstick left | Move left analog stick left | | Move (strafe) right | `D` or `→` | Move thumbstick right | Move left analog stick right | | Look up | Move mouse forward | Drag up on main screen area | Move right analog stick forward | | Look down | Move mouse backward | Drag down on main screen area | Move right analog stick back | | Turn left | Move mouse left | Drag left on main screen area | Move right analog stick left | | Turn right | Move mouse right | Drag right on main screen area | Move right analog stick right | | Jump | `Spacebar` | Tap on-screen jump button | `A` | | Run | `Shift` | Push thumbstick fully in desired direction | Push left analog stick fully in desired direction | | Shoot | Click left mouse button | Tap on-screen fire button or main screen area | Right trigger | | Shoot full auto | Hold left mouse button | Double-tap main screen area | Hold right trigger | | Aim | Hold right mouse button | Tap on-screen aim button | Left trigger | | Select weapon | `1`–`9` | Tap on-screen weapon button | Left or right bumper | | Skydive | `Spacebar` | Tap main screen area | `A` | | Take item | `E` | Tap on item | `X` | | Open door | `E` | Tap on-screen door button | `X` | | Toggle minimap | `M` | Tap on-screen minimap display | Press up on DPad | | Show/hide leaderboard | `Tab` | Tap on-screen player count display | Press down on DPad | ### Building controls | Action | Keyboard and mouse controls | Mobile controls | Controller controls | | --- | --- | --- | --- | | Toggle build mode | `Q` | Tap on-screen toggle button | `B` | | Select building tile | `1`–`9` | Tap on-screen tile button | | | Place building yile | Click left mouse button | Tap main screen area | Right trigger | | Rotate building tile | `R` | Tap on-screen rotate button | Click right analog stick | | Previous/next building tile | Click right mouse button | Tap on-screen previous/next arrow buttons | Left or right bumper | | Wall tile | `Z` | Tap on-screen tile button | | | Floor tile | `X` | Tap on-screen tile button | | | Ramp tile | `C` | Tap on-screen tile button | | ### Ground vehicle controls | Action | Keyboard and mouse controls | Mobile controls | Controller controls | | --- | --- | --- | --- | | Accelerate / forward | `W` or `↑` | Jump button (changes visually) | Right trigger | | Brake / reverse | `S` or `↓` | Smaller button to left of accelerate button | Left trigger | | Turn left | `A` or `←` | Move thumbstick | Move left analog stick left | | Turn right | `D` or `→` | Occurs at maximum turn | Move left analog stick right | | E-Brake / drift | `Spacebar` | Button prompt above vehicle | `X` | | Enter / exit / flip | `E` | Smaller button above accelerate button | `Y` | --- title: "The storm" url: /docs/en-us/resources/battle-royale/the-storm last_updated: 2026-06-29T19:34:09Z description: "Explains the implementation details for the storm feature in the Battle Royale game kit." --- # The storm The **storm** is essentially a large cylindrical barrier that starts big and slowly gets smaller as the match goes on. Players will take damage when outside of the barrier's boundary — this forces them to get closer and closer to each other and helps matches end in a reasonable time. ![The Storm Example](../../assets/resources/battle-royale/the-storm/Battle-Royale-Storm.jpeg) ## Structure Due to the part size limit, it's not possible to make the storm one huge cylinder. Instead, the storm consists of many thin rectangular parts arranged around the barrier's edge, making it appear cylindrical. These parts dynamically resize based on the proximity of players, splitting or combining with neighboring parts to maintain a smooth appearance. This behavior is handled by: - `ServerScriptService/Core/ShrinkingBarrier` - `ReplicatedStorage/Core/ShrinkingBarrierVisualization` ## Customize the storm Within `ReplicatedFirst/Configurations/MainConfiguration`, the following variables can be adjusted to customize the storm: ### Map offset The default center of the storm is the center of the map, but you can change the `map_offset` value to force players toward another point as the storm boundary shrinks. ```lua map_size = 2450 * 4, map_offset = Vector3.new(4900, 0, 4900), ``` ### Storm options Lower down, the `storm` table contains configuration values which determine how the storm behaves during matches, such as its radius, timing, and how much it shrinks. ```lua storm = { radius = 6000, time_before_start = 120, debug_time_scale = 1, number_of_stages = 10, -- stage 0 (show starting circle with no transition) { transition_length = 0, wait_length = 150, damage = 1, move_scale = 0, shrinkage_factor = 0 }, ``` | Variable | Description | | --- | --- | | `radius` | Starting radius of the storm. | | `time_before_start` | Time before the storm first appears. | | `debug_time_scale` | Debugging time scale, helpful for tuning the stages. Change this to something higher than 1 to carry out the storm's stages faster. | | `number_of_stages` | Number of stages the storm has. | | (stage tables) | Series of tables (one for each stage as defined by number_of_stages) containing these variables:

- `transition_length` — Time in seconds for the storm to transition from its previous stage to this stage.

- `wait_length` — Time in seconds for the storm to wait before transitioning to the next stage.

- `damage` — Damage per second that players will take when they are outside of the storm barrier during this stage.

- `move_scale` — Multiplier that allows the storm center to move more when transitioning to this stage. A value of 0 means the storm center will only move such that the entire storm in this stage will stay within the bounds of the storm of the previous stage.

- `shrinkage_factor` — Fraction of the previous radius that is subtracted from the radius. For example, the radius at the end of stage 2 is 1000 and `shrinkage_factor` for stage 3 is 0.25, so the radius at the end of stage 3 will be 750 (`1000-(0.25*1000)`). |
--- title: "Building architecture" url: /docs/en-us/resources/beyond-the-dark/building-architecture last_updated: 2026-06-29T19:34:09Z description: "Explains the design concepts for architecture in Beyond The Dark." --- # Building architecture Building the station in a modular way allowed us to update and propagate content updates smoothly and it was key to making sure we didn't spend hours updating individual meshes, textures, parts, and more. Roblox packages let us achieve this modular design by letting us package together a palette of `Class.MeshPart|Meshparts` that easily snapped together, trim sheet sets, and `Class.SurfaceAppearance` nodes that the `Class.MeshPart|MeshParts` could share. This let us adapt to changes in layout and size during early playtests and made the process of filling out the station much faster. ![Architecture Overview](../../assets/resources/beyond-the-dark/building-architecture/Architecture-Overview.jpeg) ## Flexible designs to final art When we first started, we didn't have complete understanding of what we wanted to be in the station, or how big it needed to be for 50 players. To figure that out, we started first building out the environment using `Class.Part|Parts` and bringing in some meshes like the rotunda shape from our external DCC application, such as Maya or Blender. With these simple shapes, we adjusted the scale of the map, arrangement of the rooms, and pathways to find the most fun arrangements. Whether for a game or a social experience, this early stage tends to be the most dynamic. When building the layout of the station, the most important thing to drive exploration was to give the user a goal. In this case, the most obvious goal is to get a closer look at the black hole. Next, we knew giving them a straight path to the black hole would be boring, because a player can just walk from spawn to the end of the map. We broke the path so the player couldn't get to the black hole without first going around and seeing other things that might draw them in too. The modular design allowed us to make such changes to the environment without catastrophic reworking of the overall design. We could move entire sections of the station to play with different gameplay ideas. Ultimately, these same modular pieces could then be updated using packages so we didn't have to rebuild multiple versions of the same asset. ![Architecture Example 1](../../assets/resources/beyond-the-dark/building-architecture/Architecture_Example_1.jpeg)![Architecture Example 2](../../assets/resources/beyond-the-dark/building-architecture/Architecture_Example_2.jpeg)![Architecture Example 3](../../assets/resources/beyond-the-dark/building-architecture/Architecture_Example_3.jpeg)![Architecture Example 4](../../assets/resources/beyond-the-dark/building-architecture/Architecture_Example_4.jpeg) ## Modular design of Kerr-Newman DSR-14 The space station uses just a handful of modular palette pieces. Modular palette pieces are reusable assets, meaning we don't have to create each wall uniquely but instead can put together several smaller walls. This allows flexibility in layout as we're building. Here are some tips that helped us create our modular design: - **Settle on a uniform grid size** — For the station, we settled on a 16 stud grid size to create most of the modular set. The grid size you use is arbitrary, but should be consistent throughout the project and across all artists working on that project. Settling on a grid size at the beginning of your project ensures each piece fits nicely with the piece next to it. It also allows you to swap pieces easily, for example swapping a wall section for a door section. - **All units are imported as studs** — When creating meshes in other DCC applications, such as Maya or Blender, Studio doesn't convert meters, centimeters, feet, or inches — it simply replaces the incoming units with the same units in studs. For example, if you set your units to meters, Studio converts 2 meters to 2 studs. It's best to treat your unit scales in your application of choice as stud scales in Roblox. The closest real-world scale for a single stud in Roblox is 28 centimeters. - **Keep pieces simple** — Don't try to build out every piece that you think you'll ever need. The idea is to have a few very versatile pieces and not a lot of one-offs. The more pieces you have, the more time it takes to create each piece, and the more it affects your overall memory and performance budget. You can always add another piece to your kit if it's absolutely needed. - **Use packages** — Convert every modular piece to a package and also put materials in packages before you start populating them in your experience. This lets you easily push updates to all instances. We'll dive into packages a bit later on. The following screenshots show you various ways we used modular design to construct portions of the station. ![Architecture Example 5](../../assets/resources/beyond-the-dark/building-architecture/Architecture_Example_5.png)![Architecture Example 6](../../assets/resources/beyond-the-dark/building-architecture/Architecture_Example_6.png)![Architecture Example 7](../../assets/resources/beyond-the-dark/building-architecture/Architecture_Example_7.png)![Architecture Example 8](../../assets/resources/beyond-the-dark/building-architecture/Architecture_Example_8.png) ## Create trim sheets 1:1 textures are the way of the future, but they become costly in regards to memory budgets and time to create. For the space station, 90% of the architectural elements used a handful of swappable trim sheet sets, which are a set of texture maps that are laid out in either a vertical or horizontal fashion. Each row or column of the sheet has a unique look to it, giving you a bunch of different surface treatments to choose from when unwrapping a model, all while using one image sheet. Here's some tips for making trim sheets: - **Start with an inventory of the type of objects and materials you need** — Trim sheets can include a large variation in material types, such as wood, metal, rubber, and more. Before you start to model your first object, create a trim sheet template. This is the starting point for all your other trim sheet sets and it gives you a quick starting point for unwrapping your models. These trim sheets live within the `Class.SurfaceAppearance` node, which we'll cover later. - **Include a variety of surface treatments in one sheet** — This lets you keep more of the mesh together, so you can apply new treatments without having to break your meshes into multiple meshes. This improves runtime performance by decreasing object and material draw calls. - **Make a trim sheet template** — A template ensures that you can swap out any map set for another. For example, you can swap a clean metal set with a rusted metal set. Here's how we created trim sheets for the station: We created a rough outline of what the sheet should look like: ![Trim Sheet Example](../../assets/resources/beyond-the-dark/building-architecture/Trim_Sheet_Example.png) In Photoshop, we created a mockup to get a quick idea of what each trim section of the sheet would look like. ![Trim Section Example](../../assets/resources/beyond-the-dark/building-architecture/Trim_Sheet_Example_2.png) We used Substance, but you can use whatever application suits you to create the final maps. This is the final set of trim sheet maps that were generated in Substance. All four maps work together within a SurfaceAppearance node to produce a Physically Based Rendered (PBR) material. Here are some final renders of the trim sheet inside of Substance Designer: And here are examples of the same trim sheets being used on a large range of objects: ![Modular Trim Sheets](../../assets/resources/beyond-the-dark/building-architecture/Modular_Trim_Sheet.png) If you want to learn more about Trim Sheet UV Mapping, just search the topic on the web; there are many great tutorials out there. ## Apply surface appearances `Class.SurfaceAppearance` lets you specify new texture inputs to our Physically Based Rendering system for `Class.MeshPart|MeshParts` and lets you override the appearance of a mesh with advanced graphics options, including ColorMap, MetalnessMap, RoughnessMap, and NormalMap. It also makes use of the new dynamic cubemap system that Roblox uses for reflections in materials, such as chrome. For example, here's a side-by-side image of a mesh with SurfaceAppearance applied next to a mesh without it: ![Surface Appearance Example](../../assets/resources/beyond-the-dark/building-architecture/Surface_Appearance_Example_1.png) When using surface appearance, keep the following tips in mind: - If your trim maps contain alpha information for transparency, change the AlphaMode from Overlay to Transparency. - Surface appearance doesn't work with Parts because they don't have the UV information required to apply the maps with. Parts use world-aligned projections in place of UV information. MeshParts are normally created in another app such as Maya, 3ds Max, or Blender and contain UV unwrap information. To apply `Class.SurfaceAppearance`: 1. Click on the at the end of a `Class.MeshPart` or folder name in the **Explorer** and search for `Class.SurfaceAppearance`. Adding one will override any BrickColor, Color, Material, and TextureID you may have already applied. 2. Rename the `Class.SurfaceAppearance` node to something unique and add a postfix of `_SA` to make them easy to search for. In this example, we used `Metal_Trim_A_SA`. 3. Import the trim sheet maps, copy the IDs from the **Asset Manager** window, and paste them into the appropriate slots in the **Properties** window. Here's how the `Metal_Trim_A_SA` node looks with all the map IDs inserted into and applied to the door. ![Surface Appearance Example 2](../../assets/resources/beyond-the-dark/building-architecture/Surface_Appearance_Example_2.png) > **Info:** For more information on Physically Based Rendering (PBR), see Adobe's Substance Designer documentation [Part 1](https://substance3d.adobe.com/tutorials/courses/the-pbr-guide-part-1) and [Part 2](https://substance3d.adobe.com/tutorials/courses/the-pbr-guide-part-2). ## Increase efficiency with packages Packages let you create object instances throughout your Roblox experiences, so that updates you make to the package are published to all instances of that package. We used packages for almost every object we put into the station, so we didn't have to hunt down each placement if we needed to make tweaks. For example, we used over 1,000 wall panel instances of a package and were able to publish updates to them instantly by modifying the package. Follow these tips when using packages: Turn on the auto-update feature in `Class.PackageLink`. All modifications are applied to all uses of the package when this is enabled, saving you the step of having to use the "update all" feature. ![Packages Example](../../assets/resources/beyond-the-dark/building-architecture/Packages_Example_1.png) You can use packages for `Class.SurfaceAppearance` objects to create a "material library." This lets you modify bitmaps in a `Class.SurfaceAppearance` object and publish them out to every object that uses it. The following screenshot shows master materials used throughout the demo. If we needed to modify the maps, we just had to update the master material and republish the package. ![Packages Example 2](../../assets/resources/beyond-the-dark/building-architecture/Packages_Example_2.png) --- title: "Custom characters" url: /docs/en-us/resources/beyond-the-dark/custom-characters last_updated: 2026-06-29T19:34:09Z description: "Explains the design concepts and systems for characters in Beyond The Dark." --- # Custom characters You can import custom meshes to create everything from NPCs to an animated cloth of a sail boat. We used the custom setting of the [Importer](/docs/en-us/studio/importer.md) to bring in more exotic custom characters, like the mysterious black hole creatures and the friendly, if unaware, service droids. The following sections go over how we used [rigging and skinning](/docs/en-us/art/modeling/rigging.md), [PBR (surface appearance)](/docs/en-us/art/modeling/surface-appearance.md), and VFX to build one of our more complicated characters that we named the "Creature." We wanted it to glow, emit some light, have trails of particle smoke, and a fluid motion that involved skinning a rig with enough joints to create the convincing waves of its tentacles. ![Creature Banner](../../assets/resources/beyond-the-dark/custom-characters/Creature_Banner.png) ## Rig When we were rigging the Creature, we found it best to model the character in a neutral pose, because that pose is best suited to bending in multiple directions. If we modeled the Creature with its tentacles already curled, it would have led to stretching if we animated the tentacles to bend in the opposite direction. The following screenshots show the Creature in its natural state: From a neutral pose, we added joints economically, focusing on the areas that needed the most movement. The fewer joints the better, because you'll have to manage them when skinning your character, and you'll have less to control when you animate them. In the previous screenshots, the Creature looks like it has a lot of joints in the center, but the main body only has one joint. Most of the other joints are towards the tentacles and mandibles. With the tentacles, we wanted a lot of secondary motion, or motion that is layered, to create a convincing effect that they're all moving on their own. However, the center mass of tentacles were so close together that it felt wasteful to make joints for all of them, both on performance and effort to animate. So instead, we treated the center mass as a single large tentacle with smaller "tails" where the tips left the center mass. ![Creature Front, Maya](../../assets/resources/beyond-the-dark/custom-characters/Creature_Front.png) > **Info:** It sometimes helps to make a "control rig" that saves you from having to animate every joint by hand. You can search for excellent tutorials of control rigs appropriate for your character and corresponding DCC application. We found the following guidelines useful, so that the character mesh imports correctly into Studio: - Individual joints and bones must have unique names. - Meshes must not have the same name as joints and bones. - Meshes should have no transforms prior to skinning/binding; in other words, transforms should be 0 and scales should be 1. - Mesh normals should face outward (the model shouldn't look inside-out). - Skeletons shouldn't have any scale factor; all joints should be [1, 1, 1]. ## Skin When we finished the Creature's skeleton, the next step was to skin the mesh. Skinning can be an arduous task, so to make matters easier, it's best to be familiar with the different initial skinning settings of your DCC application to find the one you like. Since this is an organic character, we skinned it with plenty of falloff on each joint and overlap between them. This way, the bending feels smooth and not sharp. The following screenshots show bad skinning and smooth skinning respectively: _Bad Skinning_ _Smooth Skinning_ We found the following guidelines produced the best outcomes for skinning: - Skinning influences (meaning they affect a part of the model when moved) should be a maximum of 4 influences per vertex. - Joint and mesh names need to be unique, both within each other and between each other. - Any joint you want to import into Studio must have some influence on the model's skinning, otherwise the engine doesn't import it. Whenever possible, skin your model in its original or "bind" pose. ## Import the mesh to Studio Importing your custom characters into Studio is one of the more exciting parts of the process, because you get to see your creations in the experience you're building! To import the mesh into Studio: 1. Export the character from the DCC application and ensure the following: - All normals, scales, and names are correct. - The character has all joint and bone hierarchy and all meshes. - Meshes are all under 10,000 triangles for each part of the mesh. - Mesh total size isn't over 2000 units in any axis. - See [Mesh Requirements](/docs/en-us/avatar/character-bodies/specifications.md) for a complete list of model specifications.![Creature in DCC Tool](../../assets/resources/beyond-the-dark/custom-characters/Creature-In-Maya.png) 2. In the [Importer](/docs/en-us/studio/importer.md), import the custom `.fbx` or `.obj` file.![Creature Imported Into Studio](../../assets/resources/beyond-the-dark/custom-characters/Custom-Import-2.png) ## Make the creature glow Once the Creature's model was stable and didn't require any more immediate import into Studio, we started putting together the SurfaceAppearance objects, lights, and visual effects. We did this to ensure that the quality of the model was good enough before proceeding to place and edit any one aspect of it. ![Glowing Creature Example](../../assets/resources/beyond-the-dark/custom-characters/Glowing-Creature.png) We knew we wanted the Creature to be dark, and the focal points to be the eyes and its "grabby" tentacles. High points of contrast tend to attract attention, so having a few strong ones ensure the viewer knows what to focus on. Studio supports neon materials that self-illuminate, so early on we separated the eyes out so they could be their own material from the rest of the character. We did something similar for the tentacles, so they would glow on their tips only. ![Creature Eyes in Studio](../../assets/resources/beyond-the-dark/custom-characters/Creature-Eyes.jpeg) Neon material doesn't emit actual light, so after some testing, we added separate parts to control placement and direction of light emission. This ensured the lighting was directed in a manner to enhance the glow of the eyes and also project its own source of light. ![Point Light in Studio](../../assets/resources/beyond-the-dark/custom-characters/Light-Emission.jpeg) Notice that the `SpotLights` add visual flare to the Creature close to other surfaces or a player. ![Creature Light Example](../../assets/resources/beyond-the-dark/custom-characters/Spotlights.png) In addition, we wanted the Creature's tentacles to emit some particles, so it would leave behind a smoke trail when it moved. Because the tentacles are very long, adding the `Class.ParticleEmitter` to the entire tentacle would make the particles emit from the entire tentacle instead of the tip. To combat this, we used a small part positioned near the end of the tentacle, so we could control the emission size, placement, and direction of the particles. ![Particle Example](../../assets/resources/beyond-the-dark/custom-characters/Particle-Emitter.jpeg) ## Make the VFX follow the character The skinned character's mesh positions aren't updated when the Creature animates, so we needed a method to make sure the VFX, SFX, and lights all followed the Creature properly. To accomplish this, we created a VFX controller script and used CollectionService to inform the parts that contained the VFX where the creature's bones were and to follow them. 1. We placed the following `Class.LocalScript` in **StarterPlayer** → **StarterPlayerScripts**. This essentially runs the VFX update function.```lua -- Add this snippet to an existing local script that makes PreSimulation -- connections local RunService = game:GetService("RunService") local vfx = require(workspace.VfxUpdateModule) RunService.PreSimulation:Connect(vfx.updateVfx) ``````lua -- This module attached parts to animations so they are updated as the -- animation plays. It is a workaround for the current limitations -- with Joints and Bones and will not always be necessary. -- -- Prereqs: -- To be included, models need the "AnimatedVfxModel" tag, and a folder -- of all parts that you want to sync with animation. Each part needs -- an attribute called "AttachedBoneName" that refers to the name of the -- bone you want to attach to. Parts should also already be in their -- correct positions relative to the desired bone. -- -- To Use: -- A LocalScript should require this module, then connect -- VfxUpdateModule.updateVfx to the RunService.PreSimulation event. local VfxUpdateModule = {} local CollectionService = game:GetService("CollectionService") -- SETUP - this should run once on every client. -- Collect all models with the tag local vfxModels = CollectionService:GetTagged("AnimatedVfxModel") local vfxTable = {} -- where we will store all the parts and offsets -- Assign a table to each model that will hold all vfx parts and offset for _, model in vfxModels do vfxTable[model] = {} local vfxParts = model:FindFirstChild("VFX"):GetChildren() -- Find theVFX folder -- Find the bone via attribute and calculate the offset for each part. for _,part in vfxParts do local name = part:GetAttribute("AttachedBoneName") local bone = model:FindFirstChild(name, true) if bone then local offset = (bone.TransformedWorldCFrame:inverse() * part.CFrame) vfxTable[model][part] = {bone, offset} else warn("Vfx part refers to bone that could not be found.") end end end print(vfxTable) -- UPDATE - This should be linked to every client's RunService.PreSimulation -- Go through all models, then update all parts on the model to match the bonecframe. function VfxUpdateModule.updateVfx() for model, vfxParts in vfxTable do for part, bone in vfxParts do part.CFrame = bone[1].TransformedWorldCFrame * bone[2] end end end return VfxUpdateModule ``` 2. We created a **VFXUpdateModule** `Class.ModuleScript` to tell any objects tagged appropriately with **AnimatedVfxModel** to update on a play event. 3. We tagged the necessary model groups with **AnimatedVfxModel** using the [Tags](/docs/en-us/studio/properties.md#instance-tags) section of its properties. Using tags allows the **VFXUpdateModule** to know which object to look for as the first VFX child and to apply the update.![VFX In Studio Example](../../assets/resources/beyond-the-dark/custom-characters/In-Studio-VFX.jpeg) 4. Finally, we added an **AttachedBoneName** custom attribute to the part we wanted to animate and added the precise name of the joint we wanted it to follow. ## Texture the creature Next, we set up the PBR (Physically Based Rendered) texture maps. These powerful bitmaps give the creature the varied sheen and surface variations to make it look like it has a lot of small bumps and imperfections. This visual effect helps sell the appearance of the Creature when it's closer to the player. ![Creature Texturing Comparison](../../assets/resources/beyond-the-dark/custom-characters/Surface-Appearance.png) Here's how we created the surface appearance texture maps: 1. The texture maps for this character were all on one "sheet" per map. This made the creature more efficient and meant we had to deal with fewer texture maps or `Class.SurfaceAppearance` objects. 2. For areas that needed to glow or be self-illuminated, such as the "grabby tentacles," we also used transparency on the `Class.SurfaceAppearance` to blend with those parts.![Illumination Example](../../assets/resources/beyond-the-dark/custom-characters/Surface-Appearance-Transparency.jpeg) We found it helpful to follow these guidelines when creating surface appearance texture maps: - Make sure your maps are no bigger than 1024×1024. - Your green channel may need to be flipped depending on the application you worked in. ## Animate the creature Animating is very subjective and has a personal style. Options include motion capture, hand "key frame" animating in your DCC application, or using Studio's powerful **Animation Editor**. As we mentioned earlier, we wanted to make sure we had enough joints for fluid motion as well as enough limbs, so the creature animation felt natural and "layered." Layering, also known as secondary motion, is something you see in everyday life — when you throw your arm out, every joint is reacting to the initial impulse from your upper arm, and every joint in your body doesn't move or rest at the same time. We used this technique to animate the Creature to feel like the limbs were reacting to the movement driven by its body as shown here: ![Creature Animation Example](../../assets/resources/beyond-the-dark/custom-characters/Animation-Example.gif) If using an external DCC application for animation, we found the following guidelines work best: - Set frame rates to at least 30 FPS. - Create your animations with the same character you're applying it to. - Export your animation data as a separate `.fbx` file. Because we animated the character outside of Studio, we needed to use the **Animation Editor** to import the animation `.fbx` file. The editor lets you select any avatar with joints or motors and drive them via points in the timeline. To import an animation: 1. In the toolbar's **Avatar** tab, click **Clip Editor**. 2. Select the rigged character that you want to animate in Roblox. The character should be the same one you are rigging in your external DCC application. 3. Click the **⋯** button in the upper-left section of the editor window, select **Import From FBX Animation**, and locate your exported `.fbx` animation file.![Animation Import Example 2](../../assets/resources/beyond-the-dark/custom-characters/Animation-Import-2.png) > **Info:** If you animated at a different frame rate than 30 FPS, you may get a warning about alignment issues. This is because the key frames at different frame rates will not align to the frame count in Studio. You can generate keys at those points in time, or ignore it. Either way you'll be able to export, but aligning allows you better control over editing the frames in Studio. 4. When you're happy with your animation, you can export it to discover the animation ID, which you can then use in Roblox scripts to run them. For example, you can add a `Class.Script` to the model group of the imported character and use the following code to run the animation:![Animation Script Example](../../assets/resources/beyond-the-dark/custom-characters/Script-Example.jpeg)```lua local animationId = "YOUR_ANIMATION_ID" local char = script.Parent local animController = char:FindFirstChildWhichIsA("Humanoid") or char:FindFirstChildOfClass("AnimationController") local animation = Instance.new("Animation") animation.AnimationId = "rbxassetid://" .. tostring(animationId) local animTrack = animController:LoadAnimation(animation) animTrack:Play(0, 1, 1) ``` > **Warning:** Check the character after you've closed the **Animation Editor** to ensure that the **AnimSaves** folder was deleted, as its files are only useful during editing. ## Final results After a few finishing tweaks to colors, light brightnesses, and some more particle effects to give it a stronger halo effect in front of windows, here's the final result in the space station! ![Final Creature Rendering](../../assets/resources/beyond-the-dark/custom-characters/Final-Result.png) --- title: "Beyond the Dark" url: /docs/en-us/resources/beyond-the-dark last_updated: 2026-06-29T19:34:09Z description: "Beyond the Dark is a showcase demo with accompanying documentation on the concepts and processes used during development." --- # Beyond the Dark > **Warning:** The following project notes were written and published at the release of the Beyond The Dark showcase. Processes and features may have changed since the time this was written. Refer to the appropriate feature documentation for up-to-date information on any features and workflows. ![Beyond The Dark Banner](../../assets/resources/beyond-the-dark/btd-banner.png) [Beyond the Dark](https://www.roblox.com/games/7208091524/Beyond-the-Dark-Vistech-Showcase) is an official Roblox experience used to showcase Studio's various features and tools to create a high-fidelity environment. Explore the Kerr-Newman DSR-14 space station in this official demo, non-copylocked and editable in Studio. This document goes over how we put the most important parts of the Kerr-Newman DSR-14 space station together, providing best practices along the way as well as describing the systems used to create the sights and sounds of the station. You can follow the articles sequentially or jump between the following sections: - [Building architecture](/docs/en-us/resources/beyond-the-dark/building-architecture.md) explains how to build content in a modular way. This allows you to go from early layouts to a polished experience in the fastest way possible. - [Custom characters](/docs/en-us/resources/beyond-the-dark/custom-characters.md) explains how we built the creepy creatures in the station and best practices for building your skinned custom avatars to import into Studio. It also covers some tips for adding visual effects, sound effects, and lights to the character. - [Layered clothing](/docs/en-us/resources/beyond-the-dark/layered-clothing.md) shows how we used this new system to leverage the new cage deformer feature to fit suits to these characters. - [Sound design](/docs/en-us/resources/beyond-the-dark/sound-design.md) details how we set up Roblox's SoundService and place sounds for maximum immersion. - [User interface](/docs/en-us/resources/beyond-the-dark/user-interface.md) describes how to add a literal layer to the experience and digs into how we built the parallax effect on the map as well as the local holo-screens. --- title: "Layered clothing example" url: /docs/en-us/resources/beyond-the-dark/layered-clothing last_updated: 2026-06-29T19:34:09Z description: "Explains the concepts for applying layered clothing in Beyond The Dark." --- # Layered clothing example Layered clothing lets you create 3D clothes and accessories that nearly any avatar can wear with any combination of other layered clothes. Layered clothing naturally stretches over an avatar body and other layered clothing items without clipping or breaking. We recommend building a base body character upon which you can fit the layered clothing you're creating. We'll go over how to do this in an external DCC application and combine it all in Studio to create the final character from the space station. > **Info:** For additional layered clothing resources, such as guides, example projects, and reference models, see [Layered accessories](/docs/en-us/avatar/layered-accessories.md). ## Build the custom character We wanted the users in our experience to embody someone sent to the station to investigate some disturbing events. We weren't sure early on what our actual character would be or if there would be multiple types of characters, but we wanted them to wear space suits. Because of this, we decided to make the clothing a separate component which would allow flexibility in our design. We ultimately settled on a single character design that we named "The Visitor," but layered clothing allowed us to create a main character while also releasing the clothing to the Marketplace as a modular component. As a creator, you'll be able to do the same soon, so follow along to see how we did this. ![Custom Character Example](../../assets/resources/beyond-the-dark/layered-clothing/Custom-Character-Example.png) ### Make the base body The base body is essentially the lowest layer of the layered clothing system. This can be anything from our character's body in underwear, to a fishman or even a blocky avatar. Layered Clothing Checklist: - The body geo, cage parts, and skeleton must conform to the R15 schema. - The body cage must use the cage supplied by Roblox. - The cage should be in the same number of parts as the body geo, with the suffix _OuterCage. - LeftUpperArm_Geo (mesh) - LeftUpperArm_OuterCage (cage) - The poly budget for each part cannot exceed 10,000 triangles. - Character size cannot exceed 2000×2000×2000 units. ### Make the model Layered Clothing lets clothes fit on almost any kind of body, whether it be a fishman, a rock golem, or a human space explorer. The only requirements are that they be bipedal humanoids. You should model the character in a neutral and natural pose. This allows a more full range of motion when animating, and also gives you implied directionality to movement. Implied directionality means that when you're building your character's skeleton, the joints have a small bend in them to know which way they should naturally bend. The following examples show a very small bend in the arms and legs to allow for this. We also posed the hands in a natural posture that could still hold onto something if we wanted items or gear in our experience. ![Unique Character Examples](../../assets/resources/beyond-the-dark/layered-clothing/Unique-Characters.png) The silhouette, or the shape of your character, should also be distinct and discernable to another player at a distance, which is important for multiplayer experiences. This cuts down confusion and helps users know what is safe or dangerous when a lot of things are going on. An important note about the base character: Since this is a player avatar, and we wanted to eventually release the body and clothing on the Marketplace, it still needs to follow the R15 schema. This includes the body being separated into 15 parts. To make sure the parts still appear contiguous, it's recommended you cap your mesh components and then use your application to "average" the vertex normals between body part "seams." This will treat the two vertices on either side of the body part as one, with regards to the surface normals. ![Influences Between Seams Example 1](../../assets/resources/beyond-the-dark/layered-clothing/Seams-Example-1.png)![Influences Between Seams Example 2](../../assets/resources/beyond-the-dark/layered-clothing/Seams-Example-2.png) ### Texture Texturing your characters lets you bring them to life based on how you want them to look. For the space station, we wanted a character that was a similar level of realism as its surroundings and settled on the following character: ![Texturing Example](../../assets/resources/beyond-the-dark/layered-clothing/Texturing-Showcase.png) Now we'll go over how to get textures into Studio and make sure the character looks great and fits into the experience. 1. Before texturing, we did a range of motion tests by moving the character joints and posing it to make sure the model deforms well. It's not always necessary to do a range of motion tests before texturing, but it'll help you catch surprises in your model before you put hours into the textures. After that, we separated the rig from the mesh (unbound it) to focus on texturing. It's not advisable to do texture work, or anything beyond animating, once a character is bound to its rig.![Motion Test Example](../../assets/resources/beyond-the-dark/layered-clothing/Motion-Test.png) 2. To allow for skin-tone controls for the Marketplace, we made areas like the shirt, shorts, gloves, and boots fully opaque. However, for the areas with exposed skin, we used partial transparency to allow for details to be added to the skin tone, but not completely override it. This allowed us to enhance features of the anatomy beyond what Studio's current lighting can do.![Texturing and UV Diagram](../../assets/resources/beyond-the-dark/layered-clothing/UV-Diagram.png) The following images show color (albedo), metalness, roughness, and normal maps. And the following images show skin tone transparency in action in Studio, and show the same color map with transparency and a skin tone color applied. ![Various Texture Comparisons](../../assets/resources/beyond-the-dark/layered-clothing/Texture-Comparisons.png) ### Cage Once we established the base body, we needed to cage it so that layered clothing could fit on it. Cages are the way that the clothing and body interact. Both the body and the clothing have the same cage, deformed by the user to define the clothing's fit. The body cage tells the clothing the shape of the character without all the fine details of the mesh itself. The clothing's inner cage defines how tight the clothing's fit is. The first pass of fitting the cage, for now, needs to be done within an external DCC application. You can use the cage supplied by Roblox to start. The cage for the body is largely the same as that of the clothing, except the final result is a cage that has 15 parts (each corresponding to a body part mesh). Below is the "editable" cage which is a single mesh, the cage parts which are deformed by editing the "editable" cage, and the mesh itself. ![Individual Body Part Cages](../../assets/resources/beyond-the-dark/layered-clothing/Body-Cage-Example-1.png) When you first start with the cage, it's very likely it won't match your character, so you'll need to adjust the "editable" cage, or the parts directly, to fit to the outer bounds of the body mesh. As with clothing, the outer cage should always fit outside the body mesh, so that clothing knows the shape of the body itself. It uses a "twin" cage in the clothing to essentially snap clothing to this outer cage (we'll go over this in more detail below). ![Cage Adjustment Example](../../assets/resources/beyond-the-dark/layered-clothing/Body-Cage-Example-2.png) We decided that we wanted the outer cage to cover the skin, but not the gloves and boots. Other than those areas, you'll see that the cage fits as best as it can outside the body, including the face cage matching the distance and shape of facial features. ![Outer Cage Example 3](../../assets/resources/beyond-the-dark/layered-clothing/Body-Cage-Example-5.png)![Outer Cage Example 4](../../assets/resources/beyond-the-dark/layered-clothing/Body-Cage-Example-6.png) ### Skin Skinning the character, similar to Custom Characters, is what gives the organic bending and twisting of your character when moving through the environment. We found the following guidelines useful, so that the character mesh imports correctly into Studio: - Individual joints and bones must have unique names. - Meshes should have no transforms prior to skinning/binding; in other words, transforms should be 0 and scales should be 1. - Mesh normals should face outward (the model shouldn't look inside-out). - Skeletons shouldn't have any scale factor; all joints should be [1, 1, 1]. - Skinning this character was a bit more difficult given the limitations of joint count, but here are some notes from the work we did: - When skinning a human with clear anatomy, it's best to paint your weights so that bending looks natural, for example the shoulder muscle stretching or compressing when you move the arm up and down. - Another common issue with skinning humans is that skinning defaults will often times be "too smooth," meaning areas like the elbows and knees will have influences distributed too much. It's best to paint them tighter so your legs/arms don't compress when they bend. - Lastly, because your model is split into 15 parts, you'll need to make sure seams between the parts are 1:1 in terms of weighting. Most applications will allow you to copy influences from one vertex to another so they have the exact same weighting. ### Gloves and boots As you'll notice from the base body, we decided to include gloves and boots. You might want to do this temporarily as well, for the following reasons: 1. We wanted gloves to be part of the Layered Clothing suit, but gloves aren't currently supported in the Layered Clothing feature, so we replaced the hands of our initial model with one that had gloves. 2. We wanted the metal ring of the shoe to be rigid and not deform to fit the foot and leg, so we replaced the feet of our initial model with one that had boots.![Comparison After Replacing Feet and Hands](../../assets/resources/beyond-the-dark/layered-clothing/Replacing-Hands-And-Feet.png) 3. To make sure Layered Clothing didn't try to fit around the boots or gloves, we intentionally kept the cages inside them. This enables the top and bottom of the space suit to deform to the hands and feet.![Caging with Gloves](../../assets/resources/beyond-the-dark/layered-clothing/Caging-With-Gloves.png) ## Build the suit Early on in the process, we decided that the suit should fit onto multiple characters, and that we wanted to release the assets to the Marketplace so others could enjoy the clothing. In addition to Layered Clothing, our character needed "hardpoint" items like a helmet and backpack as finishing touches. The following image shows how the space suit looks on a variety of characters. ![Layered Clothing Suit Comparison](../../assets/resources/beyond-the-dark/layered-clothing/Layerable-Suit-Comparison.png) We found the following guidelines useful when building Layered Clothing: - The exported `.fbx` file from your DCC application must contain the **clothing mesh**, **inner cage**, **outer cage**, and **skeleton**. - The way you name your clothes is arbitrary, but both cages need to have this naming format: - [clothing name]**_InnerCage** - [clothing name]**_OuterCage** - You must skin clothing to the R15 skeleton with a maximum of **4** influences. - Your R15 skeleton must follow this naming schema. With those guidelines in mind, here are the steps we followed to build the space suit: 1. Use the Roblox Layered Clothing cage template, a base frame that you can use to fit clothing to. Depending on the distance of the clothing to the frame, you can have clothing that is tight or loose fitting. 2. Model the clothing to the frame and sculpt a higher-poly model in an external DCC application. 3. Rebuild the lower-poly, game-ready version of the higher-poly model. If you're interested in more information about this process, there are many tutorials online for high-poly to game-ready models. Note that clothing does not need to be split up into parts corresponding to the body parts they're covering; it can be a single mesh.![Clothing Design Example 1](../../assets/resources/beyond-the-dark/layered-clothing/Clothing-Design-Example-1.png)![Clothing Design Example 2](../../assets/resources/beyond-the-dark/layered-clothing/Clothing-Design-Example-2.png) 4. Test the movement and fitness on other characters prior to finalizing any textures. 5. Define the "outer cage" or the outer bounds of the clothing item. This tells any additional layers how tightly to fit to the astronaut suit top. > **Warning:** It's very important for clothing outer cages to follow the clothing mesh closely, but always be outside the clothing. This allows Studio to also employ another powerful feature, Hidden Surface Removal. This feature removes geometry of a layer of clothing or even the body, if it's covered by another layer. This is immensely useful, because you won't have to worry about any meshes interpenetrating when the character runs around.![Layered Clothing Outer Cage Example](../../assets/resources/beyond-the-dark/layered-clothing/Clothing-Outer-Cage.png) 6. After caging, it may help to temporarily skin the clothing to the skeleton in the template file, just to do a range of motion test. By posing the rig in different positions, you can confirm it deforms well before finalizing texturing. If you do this, be sure to unbind before moving to texturing!![Layered Clothing Rig Binding](../../assets/resources/beyond-the-dark/layered-clothing/Clothing-Posing.png) ### Texture After we skinned the clothing, and it moved the way we intended, we moved to texturing. 1. Because we wanted to aim for realism, we used surface appearance for the beautiful PBR materials. We brought this into a texturing application to make the albedo, metalness, roughness, and normal maps. This allowed the outfit to have parts that looked metallic and others to look more like rough canvas.![Surface Appearance Lighting Test 1](../../assets/resources/beyond-the-dark/layered-clothing/Suit-Lighting-Test.gif) 2. Before going much further, we brought the model and textures into Studio to test the materials with the proper lighting in the space station. This led to some adjustments to the metalness of the arm and torso bands.![Surface Appearance Lighting Test 2](../../assets/resources/beyond-the-dark/layered-clothing/Suit-Lighting-Test-2.png) When texturing your clothing, keep the following points in mind: - For surface appearance, make sure to include color (albedo), metalness, roughness, and normal maps. - Without metalness, the surface appearance defaults to 0 metalness, or a plastic surface. - Without roughness, the surface appearance defaults to 0 roughness, or a smooth surface. ### Skin Skinning clothing is required to have it bend properly with the base body. By skinning it, you're also telling the animations how the clothing should bend, including which parts are rigid or move with the character. ![Skinned Model Animation](../../assets/resources/beyond-the-dark/layered-clothing/Skinning-Movement.gif) Luckily, unlike the base body, you don't need to split up your clothing, so the suit top can be a single mesh and skinned as such. To save yourself time, most applications will allow you to only skin your mesh to certain joints. The joints we wanted to affect the suit top were only upper/lower torso, upper/lower arms, and the head. Not binding to the whole body saved time removing unnecessary weighting. ![Maya Character Vertex Influences Example 1](../../assets/resources/beyond-the-dark/layered-clothing/Vertex-Influence-Example-1.png) Much like the body, though, you'll want to skin this so it bends naturally given its material. This meant for the middle plastic section, we had a solid weighting to the upper torso. Since the gloves were going to be rigid, and the suit top stopped well above the wrist, we didn't feel it was necessary for it to bend with the hands. ![Maya Character Vertex Influences Example 2](../../assets/resources/beyond-the-dark/layered-clothing/Vertex-Influence-Example-2.png) Once skinning on the suit top was done, we simply repeated the same procedure on the pants to get them ready for Studio and bringing into the engine! ### Import to Studio When the clothing looked good in our DCC application and in Studio, we were ready to import it into Studio and use the Accessory Tool, an upcoming release which will help you refine the clothing's cages to get the best fit possible. The tool can also export clothing as an accessory, which lets you add your clothing to a character. In Studio, we named our Layered Clothing items with an LC_ prefix, so we could distinguish between them and legacy style accessories (the helmet and backpack parts of the suit). ![Accessories in the Studio Explorer](../../assets/resources/beyond-the-dark/layered-clothing/Accessories-In-Explorer.png) ## Build the helmet The helmet is an important element of the suit. We wanted users to see the character's face and also capture the sheen of the helmet's glass surface. ![Helmet Glass Surface Render](../../assets/resources/beyond-the-dark/layered-clothing/Glass-Surface-Example.png) Accessories where we don't want deformation to fit the head, like this helmet, are best built as "hardpoint" accessories, which are the types of accessories that you currently find in the Marketplace. The main difference with the helmet and what you find in the Marketplace is that it uses `Class.SurfaceAppearance`, which creates convincing plastic and metal looks and gives the helmet visor some transparency. ### Make the model To build a helmet model: 1. We established a good silhouette when we placed the helmet on the character's head.![Helmet Silhouette](../../assets/resources/beyond-the-dark/layered-clothing/Helmet-Silhouette.png) 2. We reused the trim maps from the space station to keep the amount of textures to a minimum. We modeled the helmet with enough separate sections so we could place the UVs to best utilize the texture maps.![Helmet Texture Maps](../../assets/resources/beyond-the-dark/layered-clothing/Helmet-Texture-Map.png) 3. We then used the same trim maps we used in many areas of the station. ### Import to Studio When we had the helmet roughly fitted and placed in our DCC application, we could then import it into Studio with all the effects we wanted. 1. We initially imported the helmet using the **Asset Manager**, but because it's a single mesh, you can also drop a `Class.MeshPart` into your place and specify the MeshId using the **Properties** window.![Importing Helmet Mesh](../../assets/resources/beyond-the-dark/layered-clothing/Importing-Helmet-Mesh.jpeg) 2. We then added the texture maps via a `Class.SurfaceAppearance` object on the mesh part. This process took some tuning to get the ideal alpha on the color (albedo) map, so you could see through the helmet without it being fully transparent.![Texturing Helmet Mesh](../../assets/resources/beyond-the-dark/layered-clothing/Adding-Surface-Appearance.jpeg) 3. Because we wanted some suits to be clean and some to be dirty without having to use multiple textures, we added decals to the same object to create this effect. Decals work by planar projecting onto each surface.![Adding Additional Textures Helmet Mesh](../../assets/resources/beyond-the-dark/layered-clothing/Adding-Surface-Appearance-2.jpeg) 4. We wanted a light beam and light to indicate which way the user was facing in dark rooms and to illuminate the areas that they're walking through. To do this, we used attachments, because you can add an arbitrary amount of them, and because they're not parts, they're valid in accessories. You can use attachments for particle effects, beams, or lights as in the following example.![Adding Lighting to Helmet Mesh](../../assets/resources/beyond-the-dark/layered-clothing/Helmet-Attachments.jpeg) ### Introduce the accessory tool > **Info:** See [Accessory Fitting Tool](/docs/en-us/avatar/accessory-fitting-tool.md) for more information on converting a mesh or model to an `Class.Accessory`. With so many components being built in parallel, it can be a little hard to know if your assets fit exactly and look the way you want. However, with the upcoming Accessory Tool, fitting and refining clothing is much easier. We used this tool to set up the character's top and bottom, as well as the hardpoint helmet and backpack accessories. ![Character and Suit Overview](../../assets/resources/beyond-the-dark/layered-clothing/Character-And-Suit-Overview.png) Here's a summary of using the Accessory Tool for layered clothing: 1. We first fitted the suit's top to the character. Luckily, the fit was great and our inner cage conformed to the body well. We also tested the outer cage with other clothing items. When we were happy with the fit, we used the tool to generate the appropriate accessory for the Marketplace.![Using Accessory Fitting Tool Mannequin](../../assets/resources/beyond-the-dark/layered-clothing/Accessory-Tool-Mannequin.png) 2. With the suit's bottom, we wanted to adjust the fit within Studio because they were initially too bulky. Using the point editor, we nudged things to get them into position. Like the suit top, we made it into an accessory.![Adjusting Layered Clothing with Accessory Fitting Tool](../../assets/resources/beyond-the-dark/layered-clothing/Accessory-Fitting-Tool-Adjustments.png) 3. For the helmet and backpack, we adjusted these by just scaling, rotating, and moving them where we wanted, because they don't have cages.![Adjusting Layered Clothing Helmet with Accessory Fitting Tool](../../assets/resources/beyond-the-dark/layered-clothing/Accessory-Fitting-Tool-Adjustments-2.png) 4. Seeing how the helmet fits without the rest of the suit is tricky, so we used the Accessory Tool to visualize the top and bottom of the suit. This allowed us to ensure the fit would align with the suit's top.![Adjusting Layered Clothing Helmet with Accessory Fitting Tool](../../assets/resources/beyond-the-dark/layered-clothing/Accessory-Fitting-Tool-Adjustments-3.png) 5. With the helmet, backpack, top, and bottom complete, we were ready to see how this character looked in our space station. We copied and pasted the accessories onto the character and were able to see how they fit. We then placed the character in our place file and confirmed all the materials, lighting, and other effects were exactly the way we wanted.![Layered Clothing Accessories Final Render](../../assets/resources/beyond-the-dark/layered-clothing/Helmet-Equipped-Final.png) --- title: "Sound design" url: /docs/en-us/resources/beyond-the-dark/sound-design last_updated: 2026-06-29T19:34:09Z description: "Explains the design concepts for sound in Beyond The Dark." --- # Sound design ![Sound Design Banner](../../assets/resources/beyond-the-dark/sound-design/Sound-Banner.jpeg) The approach we used to fill the space station with audio was to implement a 2D omnipresent soundbed as our base sound and use 3D sounds attached to objects (spot ambiences) to pull the user's ear in different directions as they explore the ship. We focused a lot on immersion and making the ship feel as real as possible when creating and placing these sounds. The result is a vibrant environment that's full of sonic diversity with pockets of fun audio discovery. The DSR-14 spaceship contains the following audio that were sent through a custom mix hierarchy that lowers and raises the volume level of each sound group dynamically: - One 2D soundbed - Two dialogue tracks - Three custom music tracks - 10 scripted audio events - 50 3D sounds ## Design sound assets and effects The space station's fish tank emits some of our favorite sounds in the experience. It's a perfect encapsulation of the design philosophy that we employed, using spot ambiences to make the space feel dynamic and fresh every time you enter or exit a distinct area. Other places we did this included neon light buzzes, fans, and the black hole rumble. As seen in the following screenshot, the looping fish tank sound is a child of a part inside the tank to give it a location in the scene to play from. ![Sound Design In-Studio Location Example](../../assets/resources/beyond-the-dark/sound-design/In-Studio-Sound.jpeg) The fish tank sound uses two main layers: a saw-wave synthesizer loop and a recording of a river in the Catskill Mountains in New York. This created the feeling of a high-tech motor pushing water through the tank. We applied an `Class.EqualizerSoundEffect` to the river sound to eliminate high frequencies and give it a more "underwater" feeling. ![Sound Design In-Studio Location Example](../../assets/resources/beyond-the-dark/sound-design/In-Explorer-Sound.png) We applied a `Class.ChorusSoundEffect` on the synthesizer loop to make it sound more other-worldly. This chorus adds a warbly de-tuned effect to whatever content you pass through it. It is a great way to make something sound strange or unnatural, and it's also a common effect used in music. Here, we've picked a rate of 16 cycles per second, and a fairly mild chorus depth of 0.21. With these settings, some of the original character of the sound still comes through. ![Sound Design In-Studio Location Example](../../assets/resources/beyond-the-dark/sound-design/Sound-Properties.png) When we layer these sounds together, it fits the object and space very well. However, you might be wondering why we didn't export a looping sound that sounds like this from the beginning and implement it as one object. There are two main benefits to layering the sound like this: - We get more uses from the same content by reusing the river sound in the rainforest room. - We get real-time randomness that keeps the sound varied and interesting over long stretches of time. Two layered sound loops become as long as their least common multiple. In other words, a six second loop and a five second loop remains unique for 30 seconds, increasing audio variety for free. Other sound assets were created for multiple scripted sequences on the ship, for example the black hole triggering the anti-gravity effect. These assets were largely made from recordings of a toaster oven door, pitched down and run through various reverb and EQ effects. Pitching this source material down implies the large size of the ship, and the reverb does a great job of continuing to reinforce that effect, by making the whole thing feel like a cave. Some synthesized laser elements were also generated and added for additional accent and impact on the events. ## The mix The mix uses a nested bus structure with sound groups, and two sidechain compressors. The main goal of the mix was to create clarity and listener interest. ![Sound Design In-Studio Location Example](../../assets/resources/beyond-the-dark/sound-design/Sound-Structure.png) When creating a mix for an environment with as much excitement and ambient activity as this ship, it's important not to let things get out of hand sonically. This means creating priority through sidechains. Let's take a look at one of the sidechain compressors on the generic ambience `Class.SoundGroup`. ![Sound Design In-Studio Location Example](../../assets/resources/beyond-the-dark/sound-design/Sound-Structure-Properties.png) A sidechain compressor allows one audio source to duck the volume of another based on its current volume. This is an effect commonly used in podcasts, music, games, and other audio media. As you can see in the above image, the Ambience Generic bed is receiving compression from Ambience Local. This means when the player walks up to a local ambience like the waterfall, the generic spaceship soundbed is ducked. This creates a feeling of audio "freshness" around the ship. All of the sounds and interesting areas are creating room for each other to breathe and be the focus of attention. > **Info:** The actual settings on this compressor in the image shown, while instructive, are highly specific to these sounds and will be different depending on how loud your source audio and target both are. They cannot be copied and pasted and used with different content to deliver the same effect. Everything is relative to the volume of your content! A general overview of the three most important settings in the compressor, and how they behave in the sidechain: 1. **Attack / Release** — This is how quickly in seconds that the compressor begins ducking, and how slowly it returns to normal volume after being ducked. 2. **Threshold** — This value determines the minimum volume level at which point the compressor will start to work. 3. **Ratio** — This refers to how intensely the sidechained material will be ducked; a higher ratio indicates more severe ducking. --- title: "User interface" url: /docs/en-us/resources/beyond-the-dark/user-interface last_updated: 2026-06-29T19:34:09Z description: "Explains the design concepts for user-interface effects in Beyond The Dark." --- # User interface We wanted to add an interactive map UI to let users consume information in the space station that looked and felt like it lived in this world. We decided to build the map inside the 3D space instead of on a screen that overlays the experience. This type of diegetic visualization allows for more immersion with the world as opposed to feeling like it is a completely separate experience. ## Design the map To design the map: 1. We mocked the UI in an external application and came up with a rough idea of how we wanted it to look.![UI Mock](../../assets/resources/beyond-the-dark/user-interface/UI-Mock.png) 2. We exported the individual pieces of the map as `.png` and imported them into Studio.![UI Elements Exported](../../assets/resources/beyond-the-dark/user-interface/UI-Export.png) ## Build the map Building the map inside Studio involved using `Class.Part|Parts` and `Class.SurfaceGui|SurfaceGuis`. 1. For non-interactive elements, all we needed to do is add a `Class.SurfaceGui` object to the part. 2. For interactive elements, the `Class.SurfaceGui` also needs to be inside the `Class.StarterGui` container, with the `Adornee` property linked to the appropriate part in the 3D workspace. Doing so allows you to add button events. 3. To achieve a parallax effect, we used three separate `Class.ScreenGui` instances assigned to three unique `Parts` with different **X** values.![Parallax Example](../../assets/resources/beyond-the-dark/user-interface/Parallax-Example.png) 4. We then added a glow effect with the `Class.SurfaceGui.LightInfluence` property. If you set the property value to anything less than 1, it enables the `Class.SurfaceGui.Brightness` property. By adjusting the brightness, you can increase the glow emitting from the image. 5. To let users toggle the display of the map, we used a `Class.ProximityPrompt` that we attached to a 3D model. This is an easy way to allow user interaction with world elements.![Proximity Prompt in Explorer](../../assets/resources/beyond-the-dark/user-interface/Proximity-Prompt.png) 6. Finally, using a UITweenModule ModuleScript inside ReplicatedStorage, we animated hiding and showing the UI with TweenService and a bit of logic for determining the state. By tracking what the user clicked, we could hide and show elements by tweening various properties like alpha, position, and size.![PUI Tween Module in Explorer](../../assets/resources/beyond-the-dark/user-interface/UITweenModule.png)```lua local TweenService = game:GetService("TweenService") local UITween = {} -- for fading images function UITween.fadePart(object, amount, time, delay) local tweenAlpha = TweenInfo.new( time, --Time Enum.EasingStyle.Quad, --EasingStyle Enum.EasingDirection.Out, --EasingDirection 0, --Repeat count false, --Reverses if true delay --Delay time ) local tween = TweenService:Create(object, tweenAlpha, {Transparency = amount}) tween:Play() end function UITween.fade(object, amount, time, delay) local tweenAlpha = TweenInfo.new( time, --Time Enum.EasingStyle.Quad, --EasingStyle Enum.EasingDirection.Out, --EasingDirection 0, --Repeat count false, --Reverses if true delay --Delay time ) local tween = TweenService:Create(object, tweenAlpha, {ImageTransparency = amount}) tween:Play() end -- for fading images function UITween.fadeBackground(object, amount, time, delay) local tweenAlpha = TweenInfo.new( time, --Time Enum.EasingStyle.Quad, --EasingStyle Enum.EasingDirection.Out, --EasingDirection 0, --Repeat count false, --Reverses if true delay --Delay time ) local tween = TweenService:Create(object, tweenAlpha, {BackgroundTransparency = amount}) tween:Play() end -- for fading text function UITween.fadeText(object, amount, time, delay) local tweenAlpha = TweenInfo.new( time, --Time Enum.EasingStyle.Quad, --EasingStyle Enum.EasingDirection.Out, --EasingDirection 0, --Repeat count false, --Reverses if true delay --Delay time ) local tween1 = TweenService:Create(object, tweenAlpha, {TextTransparency = amount}) tween1:Play() end -- for moving text and images function UITween.move(object, position, time, delay) task.wait(delay) object:TweenPosition(position, Enum.EasingDirection.Out, Enum.EasingStyle.Quint, time) end -- for changing size function UITween.size(object, size, time, delay, override, callback) local tweenSize = TweenInfo.new( time, --Time Enum.EasingStyle.Quint, --EasingStyle Enum.EasingDirection.Out, --EasingDirection 0, --Repeat count false, --Reverses if true delay, --Delay time override, callback ) local tween = TweenService:Create(object, tweenSize, {Size = size}) tween:Play() end function UITween.rotate(object, rotation, time, delay, override, callback) local tweenSize = TweenInfo.new( time, --Time Enum.EasingStyle.Quint, --EasingStyle Enum.EasingDirection.Out, --EasingDirection 0, --Repeat count false, --Reverses if true delay, --Delay time override, callback ) local tween = TweenService:Create(object, tweenSize, {Rotation = rotation}) tween:Play() end -- for blurring the game camera function UITween.blur(object, amount, time) local tweenInfo = TweenInfo.new(time, Enum.EasingStyle.Linear, Enum.EasingDirection.Out, 0, false, 0) local tween = TweenService:Create(object, tweenInfo, {Size = amount}) tween:Play() end -- for blurring the game camera function UITween.turnOn(object, amount, time) local tweenInfo = TweenInfo.new(time, Enum.EasingStyle.Linear, Enum.EasingDirection.Out, 0, false, 0) local tween = TweenService:Create(object, tweenInfo, {Brightness = amount}) tween:Play() end return UITween ``````lua local ReplicatedStorage = game:GetService("ReplicatedStorage") -- Add UITween Module local UITween = require(ReplicatedStorage.UITweenModule) -- Find player Guis and UI objects local playerGui = game:GetService('Players').LocalPlayer:WaitForChild('PlayerGui') local screenGuiMapUIFrame = playerGui:WaitForChild("ScreenGuiMapUIFrame").SurfaceGui local mapUIFrameStroke = screenGuiMapUIFrame.FrameStroke local mapUIFrameFill = screenGuiMapUIFrame.FrameFill -- Sizes used for tweening local frameSizeStart = UDim2.new(0, 0, 0, 0) local frameSizeMid = UDim2.new(1, 0, 0.05, 0) local frameSizeEnd = UDim2.new(1, 0, 1, 0) -- Example Tweening UITween.fade(mapUIFrameStroke, 0, 2, 0) UITween.size(mapUIFrameStroke, frameSizeMid, 0.4, 0) UITween.fade(mapUIFrameFill, 0, 2, 0.5) UITween.size(mapUIFrameFill, frameSizeEnd, 0.4, 0.25) task.wait(0.25) UITween.size(mapUIFrameStroke, frameSizeMid, 0.4, 0) UITween.size(mapUIFrameFill, frameSizeMid, 0.4, 0.25) task.wait(0.25) UITween.size(mapUIFrameStroke, frameSizeEnd, 0.4, 0) UITween.size(mapUIFrameFill, frameSizeEnd, 0.4, 0.25) ``` Here's the final result of the interactive map: --- title: "Builder font license" url: /docs/en-us/resources/builder-font-license last_updated: 2026-06-29T19:34:09Z description: "License for using Roblox's Builder font." --- # Builder font license Builder font family copyright (c) 2024, Roblox Corporation License: Permission is hereby granted, free of charge, to any person obtaining a copy of the Builder font family including Builder Sans, Builder Extended, and Builder Mono (collectively "Builder"), to use, study, copy, merge, and embed the Builder font, subject to the following conditions: 1. By using the Builder font you agree to use the font solely for the purpose of creating, developing, modifying, uploading, and publishing UGC (as defined in the Roblox Terms of Use) on the Roblox platform and for digital promotions incorporating such UGC off of the Roblox platform. 2. You agree to not use or distribute the font for any other purpose whatsoever unless permitted by the Roblox Corporation in writing. 3. **You understand and agree that Builder font is licensed "AS IS," and Roblox Corporation disclaims any and all warranties, whether express or implied including, without limitation, any implied warranties of merchantability, fitness for a particular purpose, or non-infringement with respect to the Builder font.** 4. Your use of the Builder font is governed by the [Roblox Terms of Use](https://en.help.roblox.com/hc/en-us/articles/115004647846-Roblox-Terms-of-Use) to the extent applicable. Builder is a trademark of the Roblox Corporation. --- title: "Bundles package" url: /docs/en-us/resources/feature-packages/bundles last_updated: 2026-06-29T19:34:09Z description: "Learn about the bundles feature package." --- # Bundles package The **Bundles** feature package offers out-of-the-box functionality to sell collections of items to players at a discount. You can choose whether to allow players to purchase bundles using a custom in-experience currency or Robux, which bundle type you want to use, what set of items you want to sell, and how you want to prompt players during their gameplay. Using the package's customization options, you can tailor your bundles to meet the design and monetization goals of your experiences, such as: - Targetting a low [conversion rate](/docs/en-us/production/game-design/analytics-essentials.md#monetization-metrics) metric by offering discounted starter packs that provide value to new players and encourage early spend. - Increasing [spend depth](/docs/en-us/production/game-design/analytics-essentials.md#monetization-metrics) by bundling items at various price points to appeal to a range of players. - Monetizing live operations (LiveOps) [events](/docs/en-us/production/game-design/liveops-essentials.md#content-cadence) by offering limited-time bundles of exclusive items. ## Get package The **Creator Store** is a tab of the **Toolbox** that you can use to find all assets that are made by Roblox and the Roblox community for use within your projects, including model, image, mesh, audio, plugin, video, and font assets. You can use the Creator Store to add one or more assets directly into an open experience, including feature packages! Every feature package requires the **Core** feature package to function properly. Once the **Core** and **Bundles** feature package assets are within your inventory, you can reuse them in any project on the platform. To get the packages from your inventory into your experience: 1. Add the **Core** and **Bundles** feature package to your inventory within Studio by clicking the **Add to Inventory** link in the following set of components. #### Core Feature Package The Core feature package offers shared data store logic for all feature packages. #### Bundles Feature Package The Bundles Feature Package offers functionality to sell collections of items to players at a discount. 2. From Studio's **Window** menu or **Home** tab toolbar, open the [Toolbox](/docs/en-us/projects/assets/toolbox.md). 3. In the **Toolbox** window, click the **Inventory** tab. The **My Models** sort displays.![Studio's Toolbox window with the Inventory tab highlighted.](../../assets/studio/toolbox/Inventory-Tab.png) 4. Click the **Feature Package Core** tile, then the **Bundle Feature Package** tile. Both package folders display in the **Explorer** window. 5. Drag the package folders into `Class.ReplicatedStorage`. 6. Allow data store calls to track player purchases with the packages. 1. Open Studio's **File** ⟩ **Experience Settings** window. 2. Navigate to the **Security** tab, then enable **Enable Studio Access to API Services**. ## Define currencies > **Info:** If you do not plan on charging in-experience currencies for any bundles, you can skip this section and configure bundles through `devProductId`. If your experience has its own currency system, you can register those with the **Core** feature package by defining them in `ReplicatedStorage.FeaturePackagesCore.Configs.Currencies`. There is a commented out example of a Gems currency already in this file; replace it with your own. ```lua Gems = { displayName = "Gems", symbol = "💎", icon = nil, }, ``` The `Currencies` script tells the **Core** feature package some metadata about your currency: - (required) `displayName` - The name of your currency. If you do not specify a symbol or icon, this name is used in purchase buttons (i.e. "100 Gems"). - (optional) `symbol` - If you have a text character to use as the icon for your currency, this is used instead of the `displayName` in purchase buttons (i.e. "💎100"). - (optional) `icon` - If you have an `AssetId` image icon for your currency, this is used instead of the `displayName` in purchase buttons (i.e. image will be placed to the left of the price "🖼️100") > **Info:** If you specify both a symbol and an icon for your currency, the package uses the icon instead of the symbol. Once your currency is set up, you need to manually specify the bundle's price, currency, and icon for the heads up display instead of that information being fetched from the bundle's associated developer product. ```lua -- If you want to use a dev product, you must provide a unique devProductId, only used by one bundle. -- We will fetch bundle price and icon from the developer product pricing = { priceType = CurrencyTypes.PriceType.Marketplace, devProductId = 1795621566, }, -- Otherwise, if you want to use in-experience currency instead of a dev product, you can use the following instead: -- Price here is in the in-experience currency, not Robux pricing = { priceType = CurrencyTypes.PriceType.InExperience, price = 79, currencyId = "Gems", icon = 18712203759, }, ``` You also need to reference the `BundlesExample` script to call `setInExperiencePurchaseHandler`. ```lua local function awardInExperiencePurchase( _player: Player, _bundleId: Types.BundleId, _currencyId: CurrencyTypes.CurrencyId, _price: number ) -- Check if the player has enough currency to purchase the bundle -- Update player data, give items, etc. -- Deduct the currency from the player task.wait(2) return true end local function initializePurchaseHandlers() local bundles = Bundles.getBundles() for bundleId, bundle in bundles do -- Bundle is not associated with a developer product if it does not have marketplace price type if not bundle or bundle.pricing.priceType ~= "Marketplace" then continue end Bundles.setPurchaseHandler(bundleId, awardMarketplacePurchase) receiptHandlers[bundle.pricing.devProductId] = receiptHandler end -- If you have any in-experience currencies that you are using for bundles, set the handler here for currencyId, _ in Currencies do Bundles.setInExperiencePurchaseHandler(currencyId, awardInExperiencePurchase) end end ``` Specifically, you need to fill out `awardInExperiencePurchase`, which is called by a loop through `Currencies` inside of the example `initializePurchaseHandlers` (i.e. each currencyId is connected to the handler through `Bundles.setInExperiencePurchaseHandler(currencyId, awardInExperiencePurchase)`). ## Define bundles All bundles offerable in your experience can be defined within `ReplicatedStorage.Bundles.Configs.Bundles`, with types exported from the `Types` script in the same folder. If you're using a `devProductId`, you need to update the bundle's main `devProductId` to match the one in your experience. This is what will be prompted through `Class.MarketplaceService` to purchase the bundle itself. **It's strongly recommended to use a new developer product for the bundle to make it easier to track separate sales.** If you want a bundle with multiple items, and if these are already represented by developer products in your experience, you don't need to explicitly set item price/assetId/name, which will be fetched via product info: ```lua { itemType = ItemTypes.ItemType.DevProduct, devProductId = , metadata = { caption = { text = "x1", color = Color3.fromRGB(236, 201, 74), } -- Caption is optional! You can also omit this field } }, ``` Otherwise, you can manually configure those item details: ```lua { itemType = ItemTypes.ItemType.Robux, priceInRobux = 49, icon = , metadata = { caption = { text = "x1", color = Color3.fromRGB(236, 201, 74), } -- Caption is optional! You can also leave omit this field } }, ``` For example, your entire bundle will likely look like this: ```lua local starterBundle: Types.RelativeTimeBundle = { bundleType = Types.BundleType.RelativeTime, -- If you want to use a dev product, you must provide a unique devProductId, only used by one bundle. -- We will fetch bundle price and icon from the developer product pricing = { priceType = CurrencyTypes.PriceType.Marketplace, devProductId = , }, -- Otherwise, if you want to use in-experience currency instead of a dev product, you can use the following instead: -- Price here is in the in-experience currency, not Robux -- pricing = { -- priceType = CurrencyTypes.PriceType.InExperience, -- price = 79, -- currencyId = , -- icon = , -- }, includedItems = { [1] = { -- The item itself is not sold via a developer product, so indicate how much it is worth in Robux and give an icon -- The priceInRobux helps Bundles show relative value of the bundle price vs. the sum of its contents itemType = ItemTypes.ItemType.Robux, priceInRobux = 49, icon = , -- Alternatively, if this has a dev product leave off price and icon above and just set the devProductId -- The price and icon will be fetched from the developer product -- devProductId = -- There are more optional metadata fields that are UI-specific if needed metadata = { caption = { text = "x1", color = Color3.fromRGB(236, 201, 74), }, }, }, [2] = { itemType = ItemTypes.ItemType.Robux, priceInRobux = 99, icon = , metadata = { caption = { text = "x1", color = Color3.fromRGB(236, 201, 74), }, }, }, [3] = { itemType = ItemTypes.ItemType.Robux, priceInRobux = 149, icon = , metadata = { caption = { text = "x1", color = Color3.fromRGB(236, 201, 74), }, }, }, }, singleUse = true, -- Once purchased or expired, no longer valid even if your experience tries to prompt (onPlayerAdded). You can make this false while testing in studio. durationInSeconds = 900, -- 15 minutes includesOfflineTime = false, -- Only count time elapsed in the experience metadata = { displayName = "STARTER BUNDLE", description = "Save 75% and get a head start!", }, } ``` ## Integrate server logic Take a look at `ReplicatedStorage.Bundles.Server.Examples.BundlesExample`, which shows how your server will interact with the **Bundles** feature package and the above methods on the `Class.ModuleScript`. The snippets below are from that script. You mainly need to hook up four things once dragging the **Bundles** feature package into your experience: 1. Connect purchase handlers through `Bundles.setPurchaseHandler` to specify the functions to call to award items when a purchase is being processed.```lua local function awardMarketplacePurchase(_player: Player, _bundleId: Types.BundleId, _receiptInfo: { [string]: any }) -- Update player data, give items, etc. -- ... AND record receiptInfo.PurchaseId so we can check if user already has this bundle task.wait(2) return Enum.ProductPurchaseDecision.PurchaseGranted end local function awardInExperiencePurchase( _player: Player, _bundleId: Types.BundleId, _currencyId: CurrencyTypes.CurrencyId, _price: number ) -- Check if the player has enough currency to purchase the bundle -- Update player data, give items, etc. -- Deduct the currency from the player task.wait(2) return true end local function initializePurchaseHandlers() local bundles = Bundles.getBundles() for bundleId, bundle in bundles do -- Bundle is not associated with a developer product if it does not have marketplace price type if not bundle or bundle.pricing.priceType ~= "Marketplace" then continue end Bundles.setPurchaseHandler(bundleId, awardMarketplacePurchase) receiptHandlers[bundle.pricing.devProductId] = receiptHandler end -- If you have any in-experience currencies that you are using for bundles, set the handler here for currencyId, _ in Currencies do Bundles.setInExperiencePurchaseHandler(currencyId, awardInExperiencePurchase) end end ``` 2. Connect your logic for `Class.MarketplaceService.ProcessReceipt`, but this might be done elsewhere if your experience already has developer products for sale. Essentially, when a developer product receipt is being processed, they will now call `Bundles.getBundleByDevProduct` to check if the product belongs to a bundle. If it does, the script then calls `Bundles.processReceipt`.```lua -- Process receipt from marketplace to determine if player needs to be charged or not local function processReceipt(receiptInfo): Enum.ProductPurchaseDecision local userId, productId = receiptInfo.PlayerId, receiptInfo.ProductId local player = Players:GetPlayerByUserId(userId) if not player then return Enum.ProductPurchaseDecision.NotProcessedYet end local handler = receiptHandlers[productId] -- Get the handler for the product local success, result = pcall(handler, receiptInfo, player) -- Call the handler to check if purchase logic is successful if not success or not result then warn("Failed to process receipt:", receiptInfo, result) return Enum.ProductPurchaseDecision.NotProcessedYet end return Enum.ProductPurchaseDecision.PurchaseGranted end local function receiptHandler(receiptInfo: { [string]: any }, player: Player) local bundleId, _bundle = Bundles.getBundleByProductId(receiptInfo.ProductId) if bundleId then -- This purchase belongs to a bundle, let Bundles handle it local purchaseDecision = Bundles.processReceiptAsync(player, bundleId, receiptInfo) return purchaseDecision == Enum.ProductPurchaseDecision.PurchaseGranted end -- This purchase does not belong to a bundle, -- ... Handle all your existing logic here if you have any return false end ``` 3. Connect `Players.PlayerAdded:Connect(Bundles.OnPlayerAdded)` so that the **Bundles** feature package re-prompts any active bundles that have not yet expired for a player.```lua local function onPlayerAdded(player: Player) -- Tell Bundles when player joins so it can reload their data Bundles.onPlayerAdded(player) -- If you had some starter bundle that you wanted to offer to all new users, you could prompt that here -- ... Bundles will handle if player already has purchased it or if it's expired since it's not repeatable -- Bundles.promptIfValidAsync(player, "StarterBundle") -- Calling this here just for example, you can call this whenever or wherever you want onPromptBundleXYZEvent(player) end ``` 4. Prompt bundles. While this depends on gameplay, the example prompt players with a StarterBundle `onPlayerAdded`. - The **Bundles** feature package logic ensures each player doesn't get a repeat offer if they have already purchased the bundle, or if they let the offer already expire (based on bundle config). - Whenever you want to prompt a bundle to a player, call `Bundles.promptIfValidAsync(player, bundleId)`.```lua local function onPromptBundleXYZEvent(player: Player) -- Connect whatever experience event you want to use to determine when a player gets prompted the bundle -- ... This will be whenever you've met your elligibility criteria to prompt a player the bundle -- ... For example, if you want to prompt a bundle when a player joins, or when a player levels up task.spawn(Bundles.promptIfValidAsync, player, ) -- ... If creating multiple bundles, using task.spawn() to wrap the above function call will minimize discrepancies between countdowns end ``` > **Warning:** When awarding purchases, ensure that you also check and record `receiptInfo.PurchaseId` in your data stores. Consider the following best practice guidance on redundant recordings of ReceiptIds: - While the **Bundles** feature package does record ReceiptIds to avoid processing the same receipt twice, you should also be recording ReceiptIds inside of your tables so that if the purchase flow fails after their purchase handler has already finished, you know on subsequent retry not to award items again. - The **Bundles** feature package will not record the ReceiptId if the purchase fails at any step, so you should ensure that you are recording the ReceiptId in your tables before processing the receipt as part of your purchaseHandler. - This redundancy helps ensure that all purchase logic has been appropriately handled and that your data store and the **Bundles** feature package's data store reach eventual consistency, with your data store being the source of truth. ## Configure constants Constants for the **Core** feature package live in two spots: - Shared constants live in `ReplicatedStorage.FeaturePackagesCore.Configs.SharedConstants`. - Package-specific constants, in this case the **Bundles** feature package, live in `ReplicatedStorage.Bundles.Configs.Constants`. The main things you might want to adjust to meet the design requirements of your experience: - Sound asset IDs - Purchase effect duration and particle colors - Heads up display collapsibility Additionally, you can find strings for translation broken out into one location: `ReplicatedStorage.FeaturePackagesCore.Configs.TranslationStrings`. ## Customize UI components By modifying the package objects, such as colors, font, and transparency, you can adjust the visual presentation of your bundle prompts. However, keep in mind that if you move any of the objects around hierarchically, the code will not be able to find them, and you'll need to make adjustments to your code. A prompt is made up of two high-level components: - `PromptItem` – The individual component repeated out for each item within a bundle (item image, caption, name, price). - `Prompt` – The prompt window itself. The heads up display is also made up of two components: - `HudItem` – An individual component that represents each menu option in the heads up display. - `Hud` – To be filled with programmatically with `HudItems`. If you want to have greater control over the heads up display, instead of just using the existing HUD UI within `ReplicatedStorage.Bundles.Objects.BundlesGui`, you can move things around to meet your own design requirements. Just be sure to update client script behavior in the `ReplicatedStorage.Bundles.Client.UIController` script. ## API reference ### Types #### RelativeTime Once the `RelativeTime` bundle is offered to a player, it remains available until the time duration runs out. This type displays on the player's heads up display, and automatically prompts on future sessions until the bundle expires or the player purchases it. A common example of this bundle type is a single-use starter pack offer that displays to all new players for 24 hours. | Name | Type | Description | | --- | --- | --- | | `includeOfflineTime` | `bool` | **(Optional)** If not set, only time spent in experience will count towards the remaining offer duration. | | `singleUse` | `bool` | **(Optional)** If not set, the purchase can be reactivated after it's purchased or expired.

If set, once purchased or expired the first time, it will not be promptable ever again, even if you call `Bundles.promptIfValidAsync` with the bundleId. | #### FixedTime Once the `FixedTime` bundle is offered to a player, it remains available until the end of the set coordinated universal time (UTC). This type displays on the player's heads up display, and automatically prompts on future sessions until the bundle expires or the player purchases it. A common example of this bundle type is a holiday offer that's only available for a given month. #### OneTime A `OneTime` bundle is only available in the moment that it's offered to a player. It does not display on the player's heads up display, and once a player closes the prompt, it cannot be reopened until it's prompted by the server again. A common example of this bundle type is an offer to purchase more in-experience currency the moment a player runs out.
--- title: "Changelog" url: /docs/en-us/resources/feature-packages/changelog last_updated: 2026-06-29T19:34:09Z description: "Learn about updates to all feature packages." --- # Changelog ## Core ### Version 4 -> Version 5 - **ModalManager** - **[BREAKING CHANGE]** HUD buttons for all packages are now stored in a centralized location, in a `ScreenGui` under `FeaturePackagesCore`. This `ScreenGui` can be fetched with `ModalManager.getHudGui()`. It contains two frames, which are used to store HUD buttons in different locations on the screen. All packages need to be updated to use the new centralized HUD button locations. - Added `ModalManager.toggleOpen` method to toggle the visibility of a given modal. - **UITimer, SharedConstants** - **[BREAKING CHANGE]** Renamed `CircularIndicator` to `RoundProgressBar` to be more consistent with the name of the linear `ProgressBar`. This includes the CollectionServiceTag and instances of the same name. - **playPurchaseEffect** - Fixed animated ImageLabels not getting cleaned up after the collection animation completes - Fixed animation loop exiting early after the first item when overrideTransparency is true - Animated ImageLabels now always have `ImageTransparency` of `0` to allow for better visibility when animating icons that are not opaque - Reduced `Hover` animation duration from `0.5` to `0.3` seconds for a slightly snappier feel - Adjusted `UIHover` animations to clarify logic and avoid resizing TextButtons and TextLabels, opting to adjust the `TextTransparency` and `BackgroundTransparency` instead. Other instance types still get resized. - **UITween** - Made `playTween` function public as `UITween.play` to allow for more custom tweening of multiple properties - **UITimer, Attributes** - Time is now based on `Workspace:GetServerTimeNow()` instead of `DateTime.now()` to allow for better synchronization between the client and server timers - Fixed `FeaturePackagesTimerExpired` attribute not getting set when the timer expires - **TranslationStrings** - Added a couple strings used in the new Engagement Rewards package ## Bundles ### Version 8 -> Version 9 - **UIController** - **[BREAKING CHANGE]** Refactored HUD button creation to go through the new centralized `FeaturePackagesCore.ModalManager` location - **[BREAKING CHANGE]** Updated the styling of the HUD buttons to achieve a more consistent look across all packages - **[BREAKING CHANGE]** Major refactor of logic controlling the HUD button collapsible behavior to be more intuitive with smoother animations ## Missions ### Version 9 -> Version 10 - **UIController** - **[BREAKING CHANGE]** Refactored HUD button creation to go through the new centralized `FeaturePackagesCore.ModalManager` location - **[BREAKING CHANGE]** Updated the styling of the HUD buttons to achieve a more consistent look across all packages - **MissionsUI** - Added a HUD button getter `getMissionsHudButton` to decouple the HUD button hierarchy from the Season Passes package - **Server.Missions** - Fixed a potential infinite loop when initializing data for a new player - **MissionsExample** - Fixed walking detection not working very well when the player walks at a consistent speed - Removed jump detection to simplify the example, since listening for jumps on the server is inconsistent - **Configs.Missions** - Fixed a typo `startImmeadiately` -> `startImmediately`. The package still checks for the typo'd value, but new work should use the corrected name. Also fixed various typos in internal code across various scripts. - Renamed example missions requirement from `Walks` to `Steps` for better grammar - Updated example missions to use `Walking` instead of `Jumping` for all metrics - Updated example numeric values to allow for slightly more time to test in studio - Updated example mission rewards to add XP instead of coins to better integrate the Season Passes example - UI button instances: - Added padding, corner radius, UIHover tag, and adjusted text positioning to slightly improve styling ## Season Passes ### Version 5 -> Version 6 - **UIController** - **[BREAKING CHANGE]** Updated the reference to Missions HUD button to use the new `MissionsUI.getMissionsHudButton` method - **Configs.Season** - Updated the example season `startUtc` and `endUtc` to be relative to the current time in order to allow testing in studio to work regardless of when the package is being tested. For most purposes, you would still want to use an absolute time in your own season configuration. - **UI button instances** - Added padding, corner radius, and UIHover tag to match the slightly improved styling of the Missions panel --- title: "Engagement rewards" url: /docs/en-us/resources/feature-packages/engagement-rewards last_updated: 2026-06-29T19:34:09Z description: "Learn about the engagement rewards feature package." --- # Engagement rewards The **Engagement Rewards** feature package is a customizable framework for offering in-game rewards for certain player activities, including daily login streaks and play session time. In addition to client and server logic, the package includes a default UI for viewing reward progress, reward status, and claiming rewards. ## Get the package 1. Add the **Core** and **Engagement Rewards** packages to your inventory within Studio by clicking the **Add to Inventory** links here: #### Core The Core feature package offers shared data store logic for all feature packages. #### Engagement Rewards The Engagement Rewards feature package helps you offer rewards for daily logins, login streaks, and time played. 2. From Studio's **Window** menu or **Home** tab toolbar, open the [Toolbox](/docs/en-us/projects/assets/toolbox.md). 3. In the **Toolbox** window, click the **Inventory** tab. The **My Models** sort displays.![Studio's Toolbox window with the Inventory tab highlighted.](../../assets/studio/toolbox/Inventory-Tab.png) 4. Click the **Feature Package Core** tile, then the **Engagement Rewards Feature Package** tile. Both package folders display in the **Explorer** window. 5. Drag the folders into `Class.ReplicatedStorage`. ## Initialize the package Moving the packages to `Class.ReplicatedStorage` and testing your experience runs the `EngagementRewardsExample` script in `ReplicatedStorage.EngagementRewards.Server.Examples`. This script shows how to initialize the package for use in your experience, which involves requiring several module scripts and defining a `rewardClaimedHandlerFunction()` function that ultimately gives the reward(s) to the player. This function must return a boolean. In the example script, you can see that it doesn't give any rewards to the player; it just prints the player, reward, and quantity being claimed. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local EngagementRewardsConfig = require(ReplicatedStorage.EngagementRewards.Configs.EngagementRewardsConfig) local EngagementRewards = require(ReplicatedStorage.EngagementRewards.Server.EngagementRewards) local EngagementRewardsUtils = require(ReplicatedStorage.EngagementRewards.Utils.EngagementRewardsUtils) local Types = require(ReplicatedStorage.EngagementRewards.Configs.Types) -- Replace this handler function with your own reward claimed handler function -- This function should handle the reward claimed event for the rewardId passed in local function rewardClaimedHandlerFunction(player: Player, rewardId: RewardId, quantity: number): (boolean, string?) print(\`Reward {rewardId} claimed by {player} with quantity {quantity}\`) return true end -- more ``` You can either modify this script directly or move it to `Class.ServerScriptService` if that's your preferred location for server code. As-is, the script is only useful for testing purposes. Giving the reward to the player varies by experience. In some experiences, you might confer an experience boost or just increment the player's gold count. In other experiences, you might have a custom inventory system, whereas others might place an item into player backpacks. In all cases, however, you must replace `rewardClaimedHandlerFunction()` with your own function. ## Add rewards and criteria Most rewards customization occurs within `ReplicatedStorage.EngagementRewards.Configs.EngagementRewardsConfig`. This module script defines the reward and the requirements for unlocking it. You can see the full type declarations (or add new ones) in `ReplicatedStorage.EngagementRewards.Configs.Types`, but you probably only need to work with `EngagementRewardsConfig`. The feature package includes two reward types: `Time` and `Daily`. ```lua local engagementRewardsConfig: Types.EngagementRewardsConfig = { [Types.RewardType.Time] = { tabDisplayName = "Time", tabOrder = 1, description = "Keep playing to unlock rewards!", rewards = { MinutesPlayed1 = { icon = 116913478160966, displayName = "Ice Lance", requiredSecondsInGame = 1 * 5, }, -- more [Types.RewardType.Daily] = { tabDisplayName = "Daily", tabOrder = 2, description = "Play daily to unlock rewards!", rewards = { DailyStreak1 = { icon = 116913478160966, displayName = "Ice Lance", requiredDaysVisitedStreak = 1, effect = Types.RewardEffect.Valuable, }, -- more ``` Both reward types require an icon, which is a Roblox asset ID for an image (**not** a decal). You likely also want a display name for use in the UI. Specify `quantity` for values other than 1. You can also optionally designate a reward as valuable (`effect = Types.RewardEffect.Valuable`) to give it a different background frame in the UI. | Variable | Type | Description | Default | Required | | --- | --- | --- | --- | --- | | icon | Number | A Roblox asset ID for the UI icon. | N/A | Yes | | displayName | String | A name for the rewards for use in the UI. | The `RewardId` | No | | quantity | Number | The number of items to reward. | 1 | No | | effect | RewardEffect | The visual effect to use in the UI. See [Customize the UI](#customize-the-ui). | `Types.RewardEffect.Default` | No | ### Time rewards `Time` rewards unlock after a certain number of seconds in-game. Customize these numbers to meet your needs. You might use small numbers for easy testing, and in the published experience, confer a reward after 10 minutes, another after 30 minutes, another after an hour, and so on. > **Info:** Time tracking starts at 0 for each session, so players lose all progress towards `Time` rewards when they leave the experience. Multiplication makes numbers in seconds easier for humans to work with, so for two hours, you might prefer to specify `2 * 60 * 60` rather than `7200`. | Variable | Type | Description | Default | Required | | --- | --- | --- | --- | --- | | requiredSecondsInGame | Number | The number of seconds (a positive integer) the player must spend in-game to earn the reward. | N/A | Yes | ### Daily rewards `Daily` rewards unlock after a certain number of consecutive daily logins. For example, the first day that a player logs in, you might give them three potions or a cinnamon roll. Subsequent days, you might reward more valuable consumables, followed by a durable item, like a new fishing rod, after seven straight days. | Variable | Type | Description | Default | Required | | --- | --- | --- | --- | --- | | requiredDaysVisitedStreak | Number | The number of consecutive days the player must connect to the experience to earn the reward. | N/A | Yes | Two additional daily reward configuration options are available in the `DailyRewardTabConfig`. ```lua [Types.RewardType.Daily] = { tabDisplayName = "Daily", tabOrder = 2, description = "Play daily to unlock rewards!", isHiddenOnJoin = true, isAlignedToStreakResetTime = true, rewards = { DailyStreak1 = { icon = 116913478160966, displayName = "Ice Lance", requiredDaysVisitedStreak = 1, }, -- more ``` | Variable | Type | Description | Default | Required | | --- | --- | --- | --- | --- | | isHiddenOnJoin | Boolean | When true, rewards don't pop up automatically when joining the experience. When false, rewards automatically pop up when joining the game if the player has a new daily reward to claim. | False | No | | isAlignedToStreakResetTime | Boolean | When true, rewards for day 2 can be claimed 24 hours after the initial day 1 claimable time. When false, day 2 rewards can be claimed at the first midnight after the day 1 claimable time. For example, if a player logs in at 11:00 PM (23:00) and claims their day 1 reward, true means they can't claim the day 2 reward until 11:00 PM the next day. False means they can claim the day 2 reward at 12:00 AM (00:00), one hour later. Rewards for day 3 and beyond are always available 24 hours after the previous day's claimable time. | False | No | ## Customize the UI In the previous section, you might have noticed the `tabDisplayName`, `tabOrder`, and `description` fields, which allow for basic customization of the user interface. You can also find some UI strings in `ReplicatedStorage.FeaturePackagesCore.Configs.TranslationStrings`. - To specify a new reward background using the `effect` variable, add a frame to `ReplicatedStorage.EngagementRewards.Objects.RewardItemFrames`. Then add the name of the frame to the `Types.RewardEffect` table in `ReplicatedStorage.EngagementRewards.Configs.Types`. - For more comprehensive customization, modify the objects in `ReplicatedStorage.EngagementRewards.Objects`. For example, you might modify the `Class.Frame.BackgroundColor3|BackgroundColor3` of the `RewardsHudButton.Background` frame or the `Class.UIGradient.Color|Color` of `FooterContentFrame.ClaimableUIGradient`. > **Info:** Be careful if you modify `ReplicatedStorage.FeaturePackagesCore.Objects`; changes to **Core** affect all feature packages. - The entry point for the user interface is the `ReplicatedStorage.EngagementRewards.Client.UIController` script, which gets the necessary objects and initializes the UI. If you add or rename top-level objects (as opposed to only adding children or modifying properties), you likely need to update the code in this folder to handle them. --- title: "Feature packages" url: /docs/en-us/resources/feature-packages last_updated: 2026-06-29T19:34:09Z description: "Feature Packages let you drag and drop fully functional, customizable features directly into your experiences." --- # Feature packages Feature packages are fully functional, customizable features that you can drag and drop directly into your experience. Each feature package comes with: - Functional back-end code for minimal engineering work - Default UI components that you can customize to fit your unique aesthetics - Built-in game design and UI/UX best practices - Funnel analytics and insights You can use feature packages to add new functionality and gameplay features to your experiences, improve your experience's key metrics, and contribute to sustainable [live operations](/docs/en-us/production/game-design/liveops-essentials.md) (LiveOps). ## Bundles The **Bundles** feature package offers out-of-the-box functionality to sell collections of items to players at a discount. 1. ##### Define currencies 2. ##### Choose items 3. ##### Customize prompts ## Missions The **Missions** feature package offers out-of-the-box functionality to create missions that players can complete to achieve rewards and progress in your experience. 1. ##### Define tasks 2. ##### Track progress 3. ##### Customize menus ## Season passes The **Season Passes** feature package offers out-of-the-box functionality to create quests that players can complete to earn rewards during a pre-defined period of time. 1. ##### Define quests 2. ##### Track progress 3. ##### Customize rewards ## Engagement rewards The **Engagement Rewards** feature package is a customizable framework for offering in-game rewards for certain player activities, such as daily logins, login streaks, and time played. 1. ##### Initialize the package 2. ##### Add rewards 3. ##### Customize the UI --- title: "Missions package" url: /docs/en-us/resources/feature-packages/missions last_updated: 2026-06-29T19:34:09Z description: "Learn about the missions feature package." --- # Missions package > **Warning:** This feature package is in beta. The **Missions** feature package offers out-of-the-box functionality to create missions that players can complete to achieve rewards and progress in your experience. All missions must have an ID, category, and a list of tasks that players must complete to finish the mission. However, the tasks list can be empty, allowing the mission's rewards to be claimed immediately. Using the package's customization options, you can personalize all missions to meet your unique gameplay requirements, such as: - Creating missions that [onboard](/docs/en-us/production/game-design/onboarding.md) new players into your experience and improve [D1 retention](/docs/en-us/production/game-design/analytics-essentials.md#retention-metrics). - Targetting [D7 and D30 retention](/docs/en-us/production/game-design/analytics-essentials.md#retention-metrics) by adding mid- to long-term goals and a sense of progression. - Increasing [engagement](/docs/en-us/production/game-design/analytics-essentials.md#engagement-metrics) by surfacing the various systems in your game and rewarding players for interacting with them. > **Info:** For information on how to provide players with fresh goals and incentives so that they're motivated to play longer and increase their engagement, see [Introduction to Quest Design](/docs/en-us/production/game-design/introduction-to-quest-design.md). ## Get package The **Creator Store** is a tab of the **Toolbox** that you can use to find all assets that are made by Roblox and the Roblox community for use within your projects, including model, image, mesh, audio, plugin, video, and font assets. You can use the Creator Store to add one or more assets directly into an open experience, including feature packages! Every feature package requires the **Core** feature package to function properly. Once the **Core** and **Missions** feature package assets are within your inventory, you can reuse them in any project on the platform. To get the packages from your inventory into your experience: 1. Add the **Core** and **Missions** to your inventory within Studio by clicking the **Add to Inventory** link in the following set of components. #### Core Feature Package The Core feature package offers shared data store logic for all feature packages. #### Missions Feature Package The Missions Feature Package offers functionality to create missions that players can complete to achieve rewards and progress in your experience. 2. From Studio's **Window** menu or **Home** tab toolbar, open the [Toolbox](/docs/en-us/projects/assets/toolbox.md). 3. In the **Toolbox** window, click the **Inventory** tab. The **My Models** sort displays.![Studio's Toolbox window with the Inventory tab highlighted.](../../assets/studio/toolbox/Inventory-Tab.png) 4. Click the **Feature Package Core** tile, then the **Missions Feature Package** tile. Both package folders display in the **Explorer** window. 5. Drag the package folders into `Class.ReplicatedStorage`. ## Define missions Each completeable mission includes a set of tasks that must be finished to complete the mission, configuration options, and optional display metadata, all of which can be defined within `ReplicatedStorage.Missions.Configs.Missions`, with types exported from the `Types` script in the same folder. ### Required fields The following fields are required for each mission. | Name | Type | Description | | --- | --- | --- | | `missionId` | `string` | The main Missions table key. All missions are identified by their own unique string. | | `categoryId` | `string` | Missions must belong to a category, and are grouped by category in the UI. | | `tasks` | `table` | A list of tasks that the player must finish to complete the mission. | ### Unlock conditions By default, missions are unlocked for players automatically and can be completed exactly once. However, you can use the following **optional** configuration options to alter this behavior. | Name | Type | Description | | --- | --- | --- | | `prerequisites` | `table` | A list of other missionIds that must be completed before the mission can be unlocked. | | `manualOnly` | `bool` | Disables the automatic unlocking of the mission. Instead, a function must be called to unlock the mission. Any other unlock conditions must still be met as well. | | `availableAfterUtc` | `bool` | The mission cannot be unlocked before the specified time in UTC. | | `availableBeforeUtc` | `bool` | The mission cannot be unlocked after the specified time in UTC. If it is unlocked but not completed before this time, the mission will be failed. | | `repeatable` | `bool` | The mission becomes unlocked again after it is completed. | | `repeatLimit` | `number` | If the mission is repeatable, it cannot be repeated more than this many times. | | `repeatCooldownSeconds` | `number` | If the mission is repeatable, there is a delay before it becomes unlocked. | | `expireSeconds` | `number` | If the mission is unlocked and not completed within the specified number of seconds, then it will fail instead. | | `expireCountOffline` | `bool` | If the mission has `expireSeconds` and `expireCountOffline` is set to true, time while the player is not actually in the experience will count toward mission expiration. | ### Metadata Missions have metadata that specifies how they will be displayed in the Missions user interface. You can use the following **optional** fields to customize its metadata. | Name | Type | Description | | --- | --- | --- | | `displayName` | `string` | A name to display for the mission in the user interface instead of the `missionId`. | | `description` | `string` | A longer block of text that provides additional information or context about the mission. | | `visibleAfterComplete` | `bool` | If set to true, the mission will show up in the list of missions even after it is completed but marked as completed. | | `visibleAfterFailed` | `bool` | If set to true, the mission will show up in the list of missions even after it is failed but marked as failed. | | `visibleBeforeUnlocked` | `bool` | If set to true, the mission will be visible but locked in the list of missions before being available for completion. | | `invisibleWhileActive` | `bool` | If set to true, the mission will be invisible even while actively in progress. | | `rewards` | `table` | A list of reward display information.
| ## Define tasks Each mission can have zero or more tasks. If a mission has zero tasks, it is claimable immediately after it's unlocked; if a mission has one or more tasks, once the tasks are complete, the player can collect any rewards associated with the mission. Each task has a `taskId`, which is the key associated with the task for a given mission. Tasks come in two types: - **Timed Tasks** - Lets you start and stop the task at different points in time. A certain amount of time must pass while the task's timer is running, then the task is complete. - **Count Tasks** - Lets you add to or set the task's progress. When the progress reaches a set value, the task is complete. Both task types share the following fields: | Name | Type | Description | | --- | --- | --- | | `taskType` | `string` | Specifies if the task type is count or timed. | | `counter` | `object` | (Optional) The counter this task tracks. Counters are player-specific persistent storage for a number or a timer. Multiple tasks can track a single counter; for example, if more than one task tracks how many coins a player has collected, then they all can share the same “coins” counter. These tasks can independently track new coins collected, starting from zero, or continue counting from the counter value (all coins already collected).
| | `metadata` | `object` | (Optional) Information about how the task displays in the UI.
| | `callToAction` | `object` | (Optional) A button that triggers a callback function.
| ### Count tasks fields Count tasks have a required value. When the task progress meets this amount, the task is complete. | Name | Type | Description | | --- | --- | --- | | `goalCount` | `number` | (Optional) The progress required to complete the task. | ### Time tasks fields Time tasks have a target amount of time spent, and are started and stopped. When the target amount of time is met, the task is complete. | Name | Type | Description | | --- | --- | --- | | `goalSeconds` | `number` | The number of seconds that must pass to complete the task. | | `startImmediately` | `bool` | If the task should start counting time as soon as it is unlocked, rather than only after it is started. | | `includesOfflineTime` | `bool` | If the task should include time spent while the player is not actively in the experience. | ## Configure categories Categories do not have to be explicitly defined to be used, as the category for a mission has default values that will be used. However, you can configure these values in `ReplicatedStorage.Missions.Configs.Categories` to add additional effects to the category. Categories are identified by unique `CategoryIds`, the same ones referenced in the missions config. | Name | Type | Description | | --- | --- | --- | | `repeatDelaySeconds` | `number` | (Optional) If set, all missions in the category are reset every time the specified amount of time passes, and may be unlocked, completed, and their rewards claimed again. This is separate from repeatable missions, which are repeatable within a given category repeat. | ## Integrate server logic Take a look at `ReplicatedStorage.Missions.Server.Examples.MissionsExample`, which shows how your server will interact with the **Missions** feature package. You mainly need to hook up four things once dragging the **Missions** feature package into your experience: 1. Define missions in your missions config. 2. Add logic to your experience to update task progress or the counters the progress is tied to.```lua -- Increases progress on a mission Jumping with a Jumps task Missions.addProgressToTask(player, "Jumping", "Jumps", 1) -- Starts the timer on a mission BattlingTime with a TimeInBattle task Missions.startTimedTask(player, "BattlingTime", "TimeInBattle") -- Stops the timer on a mission BattlingTime with a TimeInBattle task Missions.stopTimedTask(player, "BattlingTime", "TimeInBattle") -- Increases the progress on all tasks tied to the Jumps counter CounterSystem.addCounter(player, "Jumps", 1) -- Starts the timer on all timed tasks tied to the TimeInBattle timer counter CounterSystem.startTimer(player, "TimeInBattle") -- Stops the timer on all timed tasks tied to the TimeInBattle timer counter CounterSystem.stopTimer(player, "TimeInBattle") ``` 3. Set mission completion handlers, and optionally unlock or fail handlers. Use the completion handler to award the rewards from the mission in your experience.```lua local function completeHandler(player: Player, missionId: Types.MissionId) print(\`{player} completed mission {missionId}\`) -- Award player their rewards. end Missions.setCompletionHandler(missionId, completeHandler) ``` 4. Unlock missions that are not unlocked automatically. **Missions** feature package logic ensures that all mission requirements are met before the mission is complete, and its rewards are collectible.```lua Missions.unlockMission(player, "Manual") ``` ## Configure constants Constants for the **Core** feature package live in two spots: - Shared constants live in `ReplicatedStorage.FeaturePackagesCore.Configs.SharedConstants`. - Package-specific constants, in this case the **Missions** feature package, live in `ReplicatedStorage.Missions.Configs.Constants`. Additionally, you can find strings for translation broken out into one location: `ReplicatedStorage.FeaturePackagesCore.Configs.TranslationStrings`. ## Customize UI components By modifying the package objects, such as colors, font, and transparency, you can adjust the visual presentation of your missions UI. For example, in `ReplicatedStorage.Missions.Configs.Constants`, you can enable `SingleTaskMode` to display a progress bar for a task directly on the mission itself for missions that only have a single task. > **Warning:** If you move any of the objects around hierarchically, the code will not be able to find them, and you'll need to make adjustments in **MissionsUI**. In addition, if your experience already has an existing UI that you would like to integrate with the **Missions** feature package, the client `Class.ModuleScript` `MissionsClient` contains all the functions necessary to get the information about a player's missions sent from the server.
--- title: "Season passes package" url: /docs/en-us/resources/feature-packages/season-passes last_updated: 2026-06-29T19:34:09Z description: "Learn about the season passes feature package." --- # Season passes package > **Warning:** This feature package is in beta and requires custom configuration for the unique needs of your experiences. Because of this, make sure to test the balance and functionality of your season pass before publishing to your audience. The **Season Passes** feature package offers out-of-the-box functionality to create limited-time, quest-based progression systems in which players can complete quest objectives to earn rewards that are only available during a pre-defined period of time. Using the package's customization options, you can personalize all season passes to deliver new content to your audience, promote player retention, and generate revenue. ## Get package The **Creator Store** is a tab of the **Toolbox** that you can use to find all assets that are made by Roblox and the Roblox community for use within your projects, including model, image, mesh, audio, plugin, video, and font assets. You can use the Creator Store to add one or more assets directly into an open experience, including feature packages! Every feature package requires the **Core** feature package to function properly, and the **Season Passes** package requires the **Missions** feature package to define a season's missions. Once all three package assets are within your inventory, you can reuse them in any project on the platform. To get the packages from your inventory into your experience: 1. Add the **Core**, **Missions**, and **Season Passes** packages to your inventory within Studio by clicking the **Add to Inventory** link in the following set of components. #### Core Feature Package The Core Feature Package offers shared data store table logic for all feature packages. #### Missions Feature Package The Missions Feature Package offers functionality to create missions that players can complete to achieve rewards and progress in your experience. #### Season Passes Feature Package The Season Passes Feature Package offers functionality to create a limited-time, quest-based progression system that players can complete to earn rewards. 2. From Studio's **Window** menu or **Home** tab toolbar, open the [Toolbox](/docs/en-us/projects/assets/toolbox.md). 3. In the **Toolbox** window, click the **Inventory** tab. The **My Models** sort displays.![Studio's Toolbox window with the Inventory tab highlighted.](../../assets/studio/toolbox/Inventory-Tab.png) 4. Click the **Feature Package Core** tile, **Missions Feature Package** tile, then the **Season Passes Feature Package** tile. All three package folders display in the **Explorer** window. 5. Drag the package folders into `Class.ReplicatedStorage`. 6. Allow data store calls to track player purchases with the packages. 1. Open Studio's **File** ⟩ **Experience Settings** window. 2. Navigate to the **Security** tab, then enable **Enable Studio Access to API Services**. ## Configure mission dependencies For seasons to function properly, you must add logic so that the **Season Passes** feature package can track player progress and update its UI components. For simplicity, the example below uses the **Missions** feature package and calls `SeasonPasses.addProgressToSeason()` after players complete missions, but you can track player progress however you'd like. Whether you define missions or not, though, you must still add the **Missions** feature package to your experience for the **Season Passes** code to function. Missions each have a set of tasks that must be finished to complete the mission, configuration options, and optional display metadata. For an in-depth guide on how to configure missions and their tasks, see [Define missions](/docs/en-us/resources/feature-packages/missions.md#define-missions). ### Add XP from missions to seasons This example uses the **Missions** feature package to define missions that players can complete to gain experience (XP) and progress through the season. To award players XP when they complete a defined mission, you must add logic to the `ReplicatedStorage.Missions.Server.Examples.MissionsExample` server script so that it can import season pass data and reward players according to their progress. ```lua local SeasonPasses = require(ReplicatedStorage.SeasonPasses.Server.SeasonPasses) local function completeHandler(player: Player, missionId: Types.MissionId) print(\`{player} completed mission {missionId}\`) if missionId == "Walking" then SeasonPasses.addProgressToSeason(player, 100) end if missionId == "Walking2" then SeasonPasses.addProgressToSeason(player, 100) end if missionId == "Walking3" then SeasonPasses.addProgressToSeason(player, 100) end if missionId == "Availability" then SeasonPasses.addProgressToSeason(player, 100) end if missionId == "Manual" then SeasonPasses.addProgressToSeason(player, 100) end if missionId == "Minutely" then SeasonPasses.addProgressToSeason(player, 100) end end ``` ### Add season tier reward icons In addition to granting players XP when they complete missions, the **Season Passes** feature package uses the **Missions** feature package to define unique reward tiers within a season. If you want to add an on-screen icon for a tier reward that players can earn if they enough XP, add an asset ID in the `Missions` config for each season tier reward. ```lua Tier3Mission = { categoryId = "General", metadata = { displayName = "Tier 3 Mission", description = "Jump 10 times", visibleAfterComplete = true, visibleBeforeUnlocked = true, rewards = { { icon = 16826775737, displayName = "Item", }, { icon = 106912146245070, displayName = "Season XP", }, }, }, } ``` ## Configure seasons After you have set up every mission you want to include in a season, it's time to configure the season pass itself. In `ReplicatedStorage.SeasonPasses.Configs.Season`, there is an example of a season configuration for your reference, and the following code samples are from that file. ### Define passes [Passes](/docs/en-us/production/monetization/passes.md) allow you to charge players a one-time Robux fee in order to access a season pass's missions and potential rewards. After you create a pass with a unique asset ID, you can set the details of the pass in the `premiumTrack` field of your season. For example, the following code sample defines a pass with an example asset ID of `928192647` to represent a season pass, then it gives the season pass a track name of "Premium Track". ```lua premiumTrack = { pricing = { gamePassId = 928192647, }, trackMetadata = { trackName = "Premium Track", }, }, ``` > **Info:** To test the purchasing of the premium pass, as well as how the purchasing effects the season, you need to publish the experience to Roblox and test in the client. Studio does not mock pass purchasing. ### Configure tiers and rewards Season tiers represent the milestones that players must meet in order to earn rewards throughout a season. To climb the tiers, players complete missions in order to earn XP, and when they earn enough XP, they graduate to the next tier and receive rewards for the tier they just completed. Tiers have an `upperBoundXP` property that sets the threshold of XP necessary to claim the rewards for a tier. Each tier can offer two types of rewards: - **Free** - A reward that players can earn without purchasing the season pass. - **Premium** - A reward that players can only earn by purchasing the season pass. To demonstrate, review the following code sample for a season with one tier that requires 100 XP to earn a free ice lance reward and a premium blast shield reward. The `icon` is the asset ID of the UI image to display for a reward, and the `displayName` displays a name for the reward. Note that the `displayName` displays in the UI, but the `rewardId` is hidden from the player and is mainly used for [data store](/docs/en-us/cloud-services/data-stores.md) purposes. ```lua tiers = { [1] = { upperBoundXP = 100, freeReward = { icon = 128181721602852, displayName = "Ice Lance", rewardId = "iceLance", }, premiumReward = { icon = 95882371958115, displayName = "Blast Shield", rewardId = "blastShield", }, }, } ``` While this script is for illustration purposes on how to configure one tier at a time, most seasons include more than one tier. As you define multiple tiers and rewards for a season, make sure each `rewardId` is numeric and in increasing order so that your tiers display in the order that you define them in the tiers list. ### Define season details Now that you have a season pass with tiers and rewards, it's time to define your season's details: - `title` - The name of your season. - `description` - The description of your season. - `startUtc` - The beginning of the season in coordinated universal time. - `endUtc` - The end of the season in coordinated universal time. - `seasonId` - Lets you track the XP a player gains throughout the given season. The package uses `startUtc` and `endUtc` to display the countdown of the season. ```lua startUtc = DateTime.fromUniversalTime(2024, 10, 01, 17, 0, 0), endUtc = DateTime.fromUniversalTime(2025, 2, 28, 17, 0, 0), title = "Season 1", description = "Into Space City", seasonId = "season1", ``` ## Integrate server logic Take a look at `ReplicatedStorage.SeasonPasses.Server.Examples.SeasonPassesExample`, which shows how your server will interact with the **Season Passes Feature Package**. The snippet below is from that script. You mainly need to create a handler function for awarding claimed rewards to your players. For example, the following code sample creates a `rewardClaimedHandlerFunction()` function that tracks when players progress to the next tier, then rewards them either just the free reward, or both the free and premium rewards if they purchased the season pass. For your purposes, replace the `Library.task.wait()` call with logic that allows players to get the rewards that they have claimed in the season. This function **must** return a boolean: true if the server granted the reward, false if it did not. ```lua local function rewardClaimedHandlerFunction(_player: Player, _rewardId: Types.RewardId): boolean task.wait(2) return true end local function initializeRewardClaimedHandlers() for tierId, tier in pairs(Season.tiers) do if tier then if tier.freeReward then SeasonPasses.setRewardClaimedHandler(tier.freeReward.rewardId, rewardClaimedHandlerFunction) end if tier.premiumReward then SeasonPasses.setRewardClaimedHandler(tier.premiumReward.rewardId, rewardClaimedHandlerFunction) end end end end ``` ## Configure constants Constants for the **Core** feature package live in two spots: - Shared constants live in `ReplicatedStorage.FeaturePackagesCore.Configs.SharedConstants`. - Package-specific constants, in this case the **Season Passes** feature package, live in `ReplicatedStorage.SeasonPasses.Configs.Constants`. The main things you might want to adjust to meet the design requirements of your experience: - Sound asset IDs - Purchase effect duration and particle colors Additionally, you can find strings for translation broken out into one location: `ReplicatedStorage.FeaturePackagesCore.Configs.TranslationStrings`. ## Customize UI components By modifying the package objects, such as colors, font, and transparency, you can adjust the visual presentation of your season pass prompts. However, keep in mind that if you move any of the objects around hierarchically, the code will not be able to find them, and you'll need to make adjustments to your code. A season is made up of the following high-level components that are programatically added to the **Missions Feature Package** UI components: - `SeasonBottomFrame` – The frame that includes information about each track, a scrolling frame of the rewards for tiers, and tier data. - `SeasonDescriptionFrame` - The frame in the header that displays the season title, description and countdown. - `TierRewardFrame` - The frame that displays a specific reward for a tier. - `TierProgressFrame` - The frame that displays the progress the player has made toward the current tier they are working on. - `PurchaseFrame` - The frame that prompts the player to purchase a premium pass. - `SeasonProgressFrame` - The frame that displays the player's progress bar throughout the entire season. - `RewardsButtonFrame` - The frame that adds a button that allows the season's information to be shown through the `SeasonBottomFrame`. --- title: "Feedback guidelines" url: /docs/en-us/resources/feedback-guidelines last_updated: 2026-06-29T19:34:09Z description: "Provides guidelines for feedback on the documentation." --- # Feedback guidelines If you want to provide feedback on the documentation, first off, thank you! We rely on contributions and bug reports to help improve our content, and we of course love hearing about what we're doing well. Following our submission guidelines can help us quickly reproduce any issues you might have encountered. ## Include key details - If some element of the site isn't working properly, list the steps that cause the problem and any relevant browser extensions. If the behavior occurs inconsistently, note how often it occurs and the situations in which it seems to occur most often. - If the content is inaccurate or misleading, include the problematic text or section header. Feel free to suggest an improved wording. ### Example feedback > "The **Clothing** and **Environment** tabs on the PBR Textures page aren't working on Firefox for Windows." > > "The Importer page says that it allows you to import `.fbx` files, but it also supports `.obj` files." > > "This page does an incredible job explaining the purpose of module scripts, but it was hard to find. Have you considered linking to it from the reference documentation? ## Don't include personal information We don't need or want your personal information! Please only include the information necessary to reproduce and troubleshoot your issue. --- title: "Limited use license" url: /docs/en-us/resources/limited-use-license last_updated: 2026-06-29T19:34:09Z description: "Roblox's Limited Use License." --- # Limited use license ROBLOX CORPORATION LIMITED USE LICENSE Roblox, Corporation ("Roblox") is making certain software available for your access and use pursuant to this Limited Use License (the "License"). By downloading, accessing, or using the software and materials (which may include associated data and data sets, models, and documentation) accompanying this License (the "Licensed Software"), you agree to be bound by the terms and conditions of this License. As used herein, "you" means any individual or entity receiving a copy of the Licensed Software, whether directly from Roblox or indirectly through another source. Access and use of the Licensed Software may require applying for and maintaining an active registration and account with Roblox, the use of which account and associated services (collectively, the "Service") is governed by separate terms (the "Terms of Service"). - **License**. Subject to your complete and ongoing compliance with this License (including any applicable Terms of Service), Roblox grants you a limited, nonexclusive, non-transferable, non-sublicensable, revocable permission to access and use the Licensed Software during the License Term (defined below) in accordance with applicable documentation, solely (a) for the purpose of internally incorporating the Licensed Software into your larger applications for deployment through the Roblox platform, and (b) in connection with the Roblox products and services for which the Licensed Software was designed (the "Purpose"). You may, in the exercise of the rights under this License, collaborate with other developers that have agreed to be bound by and comply with this License, including by distributing copies of the Licensed Software to such developers. - **Source Code**. The License granted in Section 1 also includes the right to modify any portion(s) of the Licensed Software provided by Roblox in source code form (the "Licensed Source") and to use such modified versions for the Purpose. - **Usage Guidelines**. You must not use the Licensed Software to transmit unlawful or otherwise objectionable material, transmit viruses or other harmful computer code, interfere with the performance of the Service or the data contained therein, attempt to gain unauthorized access to the Service or networks related to the Service, or interfere with another's use of the Service. You may not access or use the Licensed Software or the Service in connection with any alternative or competitive service, or to otherwise reproduce, emulate, or replace features of the Service. You may not remove any attribution information or proprietary notices of any kind in the Licensed Software. The Licensed Software may use, reference, or rely on materials separately available from Roblox or other sources, and additional terms and conditions may apply to the access and use of any such dependencies. - **Feedback**. From time to time you may, but are not required to, provide Roblox with feedback regarding the Licensed Software, including by reporting any errors you encounter, providing suggestions for modifications and enhancements, submitting bug fixes, contributions, or other associated code, or other similar submission ("Feedback"). You retain ownership of your rights in Feedback, and in the event that you choose to submit or make available Feedback to Roblox, you hereby irrevocably grant Roblox and its successors an unrestricted, unlimited, worldwide, nonexclusive, transferable, sublicensable, fully paid up, royalty free, sublicensable (through multiple tiers) license to reproduce, distribute, publicly perform or display, and prepare derivatives of the Feedback, and to make, have made, use, sell, offer for sale, and import any product or service, and to practice any method disclosed in the Feedback, in any form or manner and without limitation, attribution, or compensation. Reservation of Rights. Roblox retains all its right, title, and interest, including all intellectual property rights, in and to the Licensed Software and derivatives thereof, and nothing herein will be deemed to grant or confer on you any ownership of any such rights, whether expressly, by implication, estoppel, or otherwise. - **DISCLAIMER; LIMITATION OF LIABILITY**. THE LICENSED SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, ROBLOX HEREBY DISCLAIMS ALL WARRANTIES, WHETHER EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, NONINFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE, OR THAT IT SHALL BE ERROR-FREE OR UNINTERRUPTED. IN NO EVENT SHALL ROBLOX HAVE ANY DIRECT OR INDIRECT LIABILITY TO YOU FOR ANY REASON UNDER THIS LICENSE, INCLUDING ANY LOST PROFITS, LOSS OF DATA, LOSS OF USE, COSTS OF PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, OR FOR ANY SPECIAL, PUNITIVE, OR EXEMPLARY DAMAGES HOWEVER CAUSED AND, WHETHER IN CONTRACT, TORT OR UNDER ANY OTHER THEORY OF LIABILITY, WHETHER OR NOT THE PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. In addition, you agree and understand that the Licensed Software may be provided on an alpha, beta, or other pre-release basis, and Roblox is under no obligation to make any further releases, including any production release, and that any subsequent release may differ from the Licensed Software in any manner Roblox sees fit, including addition, modification, or removal of features and functionality present in the Licensed Software. You acknowledge that the limitations of liability and the disclaimers of warranties and damages set forth herein form an essential basis of the bargain with Roblox and that they will survive and apply even if found to have failed of their essential purpose, to the fullest extent permissible pursuant to applicable law. - **Term and Termination**. This Agreement commences on the date you accept this License and, unless terminated earlier as described below or extended by mutual written agreement, shall terminate upon the later of (a) the License Term specified at the time you access the Licensed Software, or (b) the public release of the production version of the Licensed Software (the "License Term"). Roblox may terminate the License (and your access to the Licensed Software and any associated materials) at any time for any reason or no reason. Any provisions that by their nature or express terms survive expiration or termination of this License will survive any expiration or termination of this License. Your obligations with respect to Confidential Information and Feedback will survive indefinitely, notwithstanding any eventual publication of a commercial or production version of the Licensed Software. Upon expiration or termination of this License for any reason, you must cease using the Licensed Software, and return or destroy copies of any Licensed Software or Licensed Source (and derivatives thereof) or other Confidential Information in your possession. --- title: "Emote Bar" url: /docs/en-us/resources/modules/emote-bar last_updated: 2026-06-29T19:34:09Z description: "The Emote Bar module aims to provide players an accessible, customizable way to socially interact." --- # Emote Bar Emotes are a core component of any social experience. The **EmoteBar** [developer module](/docs/en-us/resources/modules.md) aims to provide players an accessible, customizable way to facilitate meaningful social interaction. ## Module usage ### Installation To use the **EmoteBar** module in an experience: 1. From Studio's **Window** menu or **Home** tab toolbar, open the [Toolbox](/docs/en-us/projects/assets/toolbox.md) and select the **Creator Store** tab. 2. Make sure the **Models** sorting is selected, then click the **See All** button for **Categories**. 3. Locate and click the **Packages** tile. 4. Locate the **Emote Bar** module and click it, or drag-and-drop it into the 3D view. 5. In the [Explorer](/docs/en-us/studio/explorer.md) window, move the entire **EmoteBar** model into `Class.ReplicatedStorage`. Upon running the experience the module will begin running. ### Configuration The module is preconfigured with 7 emotes and it can be easily customized with your own emotes and display options. Additionally, if the player owns any emotes from previous Roblox events such as Lil Nas X, Royal Blood, or Twenty One Pilots, those emotes will be automatically added to the list of available emotes. 1. In **ServerScriptService**, create a new `Class.Script` and rename it **ConfigureEmotes**. 2. Paste the following code into the new **ConfigureEmotes** script. The `useDefaultEmotes` setting of `false` overrides the default emotes and lets you define custom emotes via the [setEmotes](#setemotes) function.```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local EmoteBar = require(ReplicatedStorage.EmoteBar) EmoteBar.configureServer({ useDefaultEmotes = false, }) EmoteBar.setEmotes({ { name = "Hello", animation = "rbxassetid://3344650532", image = "rbxassetid://7719817462", defaultTempo = 1, }, { name = "Applaud", animation = "rbxassetid://5915693819", image = "rbxassetid://7720292217", defaultTempo = 2, }, }) ``` > **Info:** If you want to disable loading of user-owned emotes into the emote bar or wheel, make sure the **UserEmotesEnabled** property is disabled for the **StarterPlayer** object. Note that this property can only be set in Studio and cannot be set by scripts at runtime. > > Also note that this developer module will modify the local user's emotes via `Class.HumanoidDescription:SetEmotes()`, meaning that calls to `Class.HumanoidDescription:GetEmotes()` will return values modified by this module. ### Mega emotes A **mega emote** is formed when multiple players in the same area perform the same emote at the same time. As more and more players join in, the mega emote grows larger. As players stop performing the emote, the mega emote grows smaller until eventually it disappears. ### Tempo An emote's **tempo** is the speed at which it plays when its button is tapped once. The default speed of an emote is determined by its `defaultTempo`. An emote's speed can be increased or decreased by tapping its button faster or slower. ## API reference ### Types #### Emote Each emote is represented by a dictionary with the following key-value pairs: | Key | Type | Description | | --- | --- | --- | | `name` | string | Emote name, for example `"Shrug"`. | | `animation` | string | Asset ID for the emote's animation. | | `image` | string | Asset ID for the emote's image in the GUI. | | `defaultTempo` | number | Default speed factor at which to play the emote animation. For example, a tempo of 2 will play the animation at twice its normal speed. Must be greater than 0. | | `isLocked` | bool | Whether the emote is "locked" from being activated. | ### Enums #### EmoteBar.GuiType | Name | Summary | | --- | --- | | `EmoteBar` | Default form where emotes are displayed in a bar along the bottom of the screen, separated into individual "pages." | | `EmoteWheel` | Variant where emotes are displayed in a ring when a player clicks or taps on their player character. | ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local EmoteBar = require(ReplicatedStorage.EmoteBar) EmoteBar.configureClient({ guiType = EmoteBar.GuiType.EmoteWheel, }) ``` ### Functions #### configureServer _ configureServer(config: `Library.table`)_ Overrides default server-side configuration options through the following keys/values in the `config` table. This function can only be called from a `Class.Script` and changes will automatically replicate to all clients. | Key | Description | Default | | --- | --- | --- | | `useDefaultEmotes` | Whether the provided default emotes are included or not. | true | | `useMegaEmotes` | Enables or disables the [mega emotes](#mega-emotes) feature. | true | | `emoteMinPlayers` | Minimum number of players performing the same emote to contribute to a mega emote. | 3 | | `emoteMaxPlayers` | Maximum number of players performing the same emote to contribute to a mega emote. | 50 | | `playParticles` | Enables or disables the emotes players are playing as floating particles above their heads. | true | | `sendContributingEmotes` | Enables or disables sending a small emote icon to contribute to the mega emote. | true | ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local EmoteBar = require(ReplicatedStorage.EmoteBar) EmoteBar.configureServer({ emoteMinPlayers = 2, playParticles = false, }) ``` #### configureClient _ configureClient(config: `Library.table`)_ Overrides default client-side configuration options through the following keys/values in the `config` table. This function can only be called from a `Class.LocalScript`. Depending on the value of `guiType`, options in the noted tabs also apply. #### General | Key | Description | Default | | --- | --- | --- | | `guiType` | Controls which form the GUI will take for displaying emotes ([EmoteBar.GuiType](#emotebarguitype)). | [EmoteBar](#emotebarguitype) | | `useTempo` | Enables or disables the [tempo](#tempo) feature where users are able to control how fast or slow their emotes are played by repeatedly activating the same emote rhythmically. | true | | `tempoActivationWindow` | Amount of time, in seconds, the user has between sequential activations of an emote for it to count as part of the tempo. | 3 | | `lockedImage` | Image to display overtop locked emotes. | "rbxassetid://6905802778" | #### Bar If the value of `guiType` is [EmoteBar](#emotebarguitype) (default), the following options apply: | Key | Description | Default | | --- | --- | --- | | `maxEmotesPerPage` | Maximum number of emotes that are displayed at a time. Smaller screens will automatically show fewer emotes. | 4 | | `emoteBarPosLandscape` | Position of the emote bar in landscape mode (`Datatype.UDim2`). | (0.5, 0, 1, -16) | | `emoteBarPosPortrait` | Position of the emote bar in portrait mode (`Datatype.UDim2`). | (0.5, 0, 1, -100) | | `useEmoteHotkeys` | Whether emote hotkeys are used. If `true`, this binds 1, 2, 3, 4, etc. as hotkeys for the emote bar. Only numeric keys 1–9 are supported. | true | | `usePageHotkeys` | Whether page hotkeys are used. If `true`, `nextPageKey` and `prevPageKey` are used to cycle between pages. | true | | `prevPageKey` | Key used to cycle to the previous page of emotes (`Enum.KeyCode`). | `Enum.KeyCode\|Q` | | `nextPageKey` | Key used to cycle to the next page of emotes (`Enum.KeyCode`). | `Enum.KeyCode\|E` | | `leftArrowImage` | Image for the left arrow (previous page). | "rbxassetid://6998633654" | | `rightArrowImage` | Image for the right arrow (next page). | "rbxassetid://6998635824" | #### Wheel If the value of `guiType` is [EmoteWheel](#emotebarguitype), the following options apply: | Key | Description | Default | | --- | --- | --- | | `closeImage` | Image for the close button on the emote wheel, placed overtop the `closeBackgroundImage` image. | "rbxassetid://7027440823" | | `closeBackgroundImage` | Background image for the close button on the emote wheel. | "rbxassetid://7027440823" | | `emoteHoverImage` | Image for hover-over indication of the selected emote in the wheel. | "rbxassetid://7344843157" | ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local EmoteBar = require(ReplicatedStorage.EmoteBar) EmoteBar.configureClient({ guiType = EmoteBar.GuiType.EmoteBar, maxEmotesPerPage = 6, nextPageKey = Enum.KeyCode.Z, prevPageKey = Enum.KeyCode.C, }) ``` ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local EmoteBar = require(ReplicatedStorage.EmoteBar) EmoteBar.configureClient({ guiType = EmoteBar.GuiType.EmoteWheel, }) ``` #### setEmotes _ setEmotes(emotes: `Library.table`)_ Sets the custom emotes to use. These will be added to the defaults if `useDefaultEmotes` is `true`, or replace the defaults if `useDefaultEmotes` is `false`. This function can only be called from a `Class.Script` and changes will automatically replicate to all clients. See [Emote](#emote) for the structure of each emote passed to this function. > **Info:** The order of custom emotes in the array determines how each emote is ordered in the UI. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local EmoteBar = require(ReplicatedStorage.EmoteBar) EmoteBar.configureServer({ useDefaultEmotes = false, }) EmoteBar.setEmotes({ { name = "Hello", animation = "rbxassetid://3344650532", image = "rbxassetid://7719817462", defaultTempo = 1, }, { name = "Applaud", animation = "rbxassetid://5915693819", image = "rbxassetid://7720292217", defaultTempo = 2, }, }) ``` #### setGuiVisibility _ setGuiVisibility(visible: `boolean`)_ Shows or hides the emotes GUI. This function can only be called from a `Class.LocalScript` on a specific client. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local EmoteBar = require(ReplicatedStorage.EmoteBar) EmoteBar.setGuiVisibility(false) ``` #### getEmote _ getEmote(emoteName: `Library.string`): `Library.table`_ Gets an [Emote](#emote) by name. Returns `nil` if the emote cannot be found. This function can only be called from a `Class.LocalScript` on a specific client. > **Info:** Emotes registered on the server with [setEmotes](#setemotes) can be retrieved client-side using this function. This can also be used to retrieve any of the default emotes by name: `"Hello"`, `"Applaud"`, `"Point"`, `"Stadium"`, `"Tilt"`, `"Shrug"`, or `"Salute"`. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local EmoteBar = require(ReplicatedStorage.EmoteBar) local shrug = EmoteBar.getEmote("Shrug") ``` #### playEmote _ playEmote(emote: [Emote](#emote))_ Plays the given [Emote](#emote) and fires the [emotePlayed](#emoteplayed) event on the server, if connected. This function can only be called from a `Class.LocalScript` on a specific client. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local EmoteBar = require(ReplicatedStorage.EmoteBar) local shrug = EmoteBar.getEmote("Shrug") EmoteBar.playEmote(shrug) ``` #### lockEmote _ lockEmote(emoteName: `Library.string`)_ Locks the [Emote](#emote) with the given name. This function can only be called from a `Class.LocalScript` on the client. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local EmoteBar = require(ReplicatedStorage.EmoteBar) EmoteBar.lockEmote("Applaud") ``` #### unlockEmote _ unlockEmote(emoteName: `Library.string`)_ Unlocks the [Emote](#emote) with the given name. This function can only be called from a `Class.LocalScript` on the client. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local EmoteBar = require(ReplicatedStorage.EmoteBar) EmoteBar.unlockEmote("Applaud") ``` ### Events #### emotePlayed Fires when any client plays an emote. This event can only be connected in a `Class.LocalScript`. | Parameters | | --- | | player: `Class.Player` | Player who acted out the emote. | | emote: [Emote](#emote) | Emote which was played. | ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local EmoteBar = require(ReplicatedStorage.EmoteBar) EmoteBar.emotePlayed:Connect(function(player, emote) print(player.Name, "played", emote.name) end) ``` #### lockedEmoteActivated Fires when a client clicks a locked emote. This event can only be connected in a `Class.LocalScript`. | Parameters | | --- | | emote: [Emote](#emote) | Locked emote which was activated. | ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local Players = game:GetService("Players") local EmoteBar = require(ReplicatedStorage.EmoteBar) EmoteBar.lockedEmoteActivated:Connect(function(emote) print(Players.LocalPlayer, "clicked", emote.name) end) ``` --- title: "Event Sequencer" url: /docs/en-us/resources/modules/event-sequencer last_updated: 2026-06-29T19:34:10Z description: "Event Sequencer is a powerful framework for building live, cross-server events and cutscenes." --- # Event Sequencer **EventSequencer** is a powerful framework that enables you to build live, cross-server events and cutscenes on a structured sequence of actions and triggers. More specifically, this module helps you: - Build an event or cutscene on a structured framework through scheduled configurations of audio, animations, and tweens. - Transition between multiple scenes across multiple servers, synchronizing complex animations and visuals to a timeline. - Seek through an event and preview the experience for testing and development purposes. This framework has been battle tested in Roblox events like the [Twenty One Pilots](https://www.youtube.com/watch?v=0fAhhoXK12o) and [24kGoldn](https://www.youtube.com/watch?v=UfzqGkfRKr4) concerts, as well as many highly visited experiences. To see **EventSequencer** in action within an editable place, check out the [Concert](https://www.roblox.com/games/10275826693/Concert) template in Roblox Studio. This template is a comprehensive starting point for developers to create events/concerts and familiarize themselves with the various features and components involved. ## Module usage ### Installation To use the **EventSequencer** framework in an experience: 1. From Studio's **Window** menu or **Home** tab toolbar, open the [Toolbox](/docs/en-us/projects/assets/toolbox.md) and select the **Creator Store** tab. 2. Make sure the **Models** sorting is selected, then click the **See All** button for **Categories**. 3. Locate and click the **Packages** tile. 4. Locate the **Event Sequencer** module and click it, or drag-and-drop it into the 3D view. 5. In the [Explorer](/docs/en-us/studio/explorer.md) window, move the entire **EventSequencer** model into `Class.ServerScriptService`. Upon running the experience the module will begin running. ### Framework modes #### Replace mode The default framework mode is **replace mode** in which you design unique [scenes](#create-scenes) by placing [3D objects](/docs/en-us/parts.md), [terrain](/docs/en-us/parts/terrain.md), [lighting properties](/docs/en-us/environment/lighting.md), [environmental effects](/docs/en-us/environment.md#environment), and user interface objects into that scene's **Environment** folder. When a scene loads, those objects and properties get distributed into `Class.Workspace`, `Class.Terrain`, and `Class.Lighting`, **replacing** existing objects/properties to form a cloned space. > **Success:** This mode is best for **new** event experiences consisting of multiple sequential scenes, such as a pre-show venue to the main concert to a post-show party. #### Inline mode An alternate framework mode is **inline mode** in which you similarly design unique [scenes](#create-scenes) with scripting logic for their flow/events, but the framework will **not** destroy existing [3D objects](/docs/en-us/parts.md), [terrain](/docs/en-us/parts/terrain.md), [lighting properties](/docs/en-us/environment/lighting.md), [environmental effects](/docs/en-us/environment.md#environment), and user interface objects in order to clone assets/properties from a scene's **Environment** folder upon loading. > **Success:** This mode is best for existing experiences, as it preserves the design and layout of your place while letting you orchestrate events such as cutscenes. To enable inline mode: 1. Inside the **EventSequencer** model that you placed in **ServerScriptService**, drill down and select the **Inline** value inside the **ReplicatedStorage** folder. 2. In the [Properties](/docs/en-us/studio/properties.md) window, toggle on its **Value** checkbox. ### Create scenes A **scene** is essentially part of an overall event or a cutscene wrapped up in a series of folders. Each scene contains scripting logic that defines its flow/events, and a scene can store its own [3D objects](/docs/en-us/parts.md), [terrain](/docs/en-us/parts/terrain.md), [lighting properties](/docs/en-us/environment/lighting.md), [environmental effects](/docs/en-us/environment.md#environment), and user interface objects. To get started quickly, you can find an empty scene inside the module's main folder: 1. Expand the **EventSequencer** folder and locate the **BlankScene** folder. 2. Move or copy the entire **BlankScene** folder into **ReplicatedStorage**. > **Warning:** While developing an event, you can alternatively place scenes elsewhere in the [Explorer](/docs/en-us/studio/explorer.md) hierarchy and tag them with **SequencerScene** using the [Tags](/docs/en-us/studio/properties.md#instance-tags) section of their properties (**BlankScene** is already tagged as such). However, you'll need to move all event-ready scenes to **ReplicatedStorage** in order for them to work within the overall event flow. #### Time length Each scene should have a **time length**, in seconds, defining its duration — just like a movie or concert has a set duration. Time length is defined as a numeric [attribute](/docs/en-us/studio/properties.md#instance-attributes) on the scene's folder named **TimeLength** which you can set directly in Studio or programmatically through `Class.Instance:SetAttribute()`. #### Environment A scene's **Environment** folder contains everything that users see and hear, including [3D objects](/docs/en-us/parts.md), [terrain](/docs/en-us/parts/terrain.md), [lighting properties](/docs/en-us/environment/lighting.md) and [environmental effects](/docs/en-us/environment.md#environment), and user interface objects. When a scene loads, those objects and properties get distributed into `Class.Workspace`, `Class.Terrain`, and `Class.Lighting`, replacing existing objects/properties to form a cloned space. > **Warning:** This folder is not intended for usage with [Inline Mode](#inline-mode), as that mode expects you to use existing assets and global property settings for the place, not load in scene-specific assets/properties. The **Environment** folder contains the following containers: | Container | Description | | --- | --- | | **Client** | Contains all assets to load when any user (client) joins the event, such as user interface objects or an animation rig. | | **PlayerSpawns** | Contains parts where users spawn upon joining. Any part in this folder behaves similar to a `Class.SpawnLocation`. | | **Server** | Contains all assets to load when a scene is first created on a server. It's recommended that most visual assets go here. | | **Terrain** | Contains scene terrain. | | **Lighting** | Contains global [lighting properties](/docs/en-us/environment/lighting.md) as attributes, as well as modifiers like [atmospheric effects](/docs/en-us/environment/atmosphere.md) and [post-processing](/docs/en-us/environment/post-processing-effects.md) effects. | #### Events A scene's **Events** folder is purely a placeholder for `Class.RemoteEvent|RemoteEvents` that communicate between the [Client](#client) and [Server](#server) modules. It's not a requirement to place anything in this folder. #### Client This script executes [schema](#scene-schemas) logic on the client. #### Server This script executes [schema](#scene-schemas) logic on the server. ### Scene schemas A scene's **schema** defines what happens at what point in the scene's timeline. You should define a scene's schema in both its [Client](#client) and [Server](#server) modules and include **lifecycle hooks** to manage when **configurations** occur. #### Lifecycle hooks Schema [lifecycle hooks](#schema-lifecycle-hooks) let you manage when scene operations occur. A scene in production will typically run in the most simple flow: - [OnSetup](#onsetup) → [OnRun](#onrun) → [OnEndScene](#onendscene) [OnRun](#onrun) may be interrupted when seeking: - [OnSetup](#onsetup) → [OnRun](#onrun)… **seek** → [OnRun](#onrun)… **seek** → [OnRun](#onrun) → [OnEndScene](#onendscene) All three hooks can also repeat if the scene is replayed: - [OnSetup](#onsetup) → [OnRun](#onrun) → [OnEndScene](#onendscene)… **replay** → [OnSetup](#onsetup) → [OnRun](#onrun) → [OnEndScene](#onendscene) #### Configurations Schema [configurations](#schema-configurations) define the core operations of a scene, for example [playing audio](#audio) at 00:32, queuing an [animation](#animate) to sync with that audio, [scheduling a scene event](#schedule) like a fireworks show, and more. Every configuration supports certain callback functions where the first parameter (`self`) is the configuration instance. > **Info:** You can use configurations in both the [Client](#client) and [Server](#server) module schemas, but it's recommended that you perform operations like [audio](#audio) and [animations](#animate) on the client side, just like in a typical experience. ### Seek scenes A unique feature of **EventSequencer** is the ability to "seek" around scenes as you might seek through a video. In [Replace Mode](#replace-mode), you can also switch between scenes to preview an entire multi-scene event before deploying it to production. **Scene seeking is not accessible to everybody** since users simply enjoying the event should not have the ability to control its time flow. Instead, you must grant seeking permission based on the event's `Class.DataModel.PlaceId|PlaceId` as well as specific `Class.Player.UserId|UserIds` and/or [groups](/docs/en-us/projects/groups.md) and roles within them. 1. Create a new `Class.Script` within `Class.ServerScriptService`. 2. Paste the following code into the new script.```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local EventSequencer = require(ReplicatedStorage:WaitForChild("EventSequencer")) EventSequencer.setSeekingPermissions({ placeIDs = {}, userIDs = {}, groups = { {GroupID = , MinimumRankID = }, } }) ``` 3. Fill in the following tables within the [setSeekingPermissions](#setseekingpermissions) call as follows: | `placeIDs` | Comma-delimited list of `Class.DataModel.PlaceId\|PlaceId` values to support seeking within. | | --- | --- | | `userIDs` | Comma-delimited list of `Class.Player.UserId\|UserIds` for those who can seek within the supported places. | | `groups` | Comma-delimited list of tables, each containing a [group](/docs/en-us/projects/groups.md) ID and the minimum rank of that group's members who can seek within the supported places. | ## Scene manager plugin The [Scene Manager](https://www.roblox.com/library/9995053698/Scene-Manager) plugin is a useful tool for loading and unloading scenes, [lighting](#save-and-load-lighting), and [terrain](#save-and-load-terrain). Unless you're using [Inline Mode](#inline-mode), it's highly recommended that you use this plugin instead of manually placing/editing scene objects and properties. > **Warning:** This plugin is not intended for usage with [Inline Mode](#inline-mode), as that mode expects you to use existing assets and global property settings for the place, not load in scene-specific assets/properties from the scene's [Environment](#environment) folder. To install the plugin: 1. From Studio's **Window** menu or **Home** tab toolbar, open the [Toolbox](/docs/en-us/projects/assets/toolbox.md). 2. With the **Creator Store** tab selected, select **Plugins** from the dropdown menu. 3. In the search field, type **Scene Manager** and press `Enter` to locate the plugin. 4. Click the plugin's icon to view its details and then click the **Install** button. 5. Once the plugin is installed, it appears in Studio's **Plugins** tab. ### Load and unload scenes As outlined in [creating scenes](#create-scenes), a scene's **Environment** folder contains everything that users see and hear, including [3D objects](/docs/en-us/parts.md). The plugin helps you quickly load a scene's assets into or out of organized folders within the workspace. > **Error:** Scenes must be tagged with **SequencerScene** for the plugin to recognize them (**BlankScene** is already tagged as such). If you've created a scene but it doesn't appear in the plugin, make sure that it's tagged, then save and reopen the place. | Plugin action | Description | | --- | --- | | **Load Client** | If the scene's client content is unloaded, moves its **Environment**/**Client** folder into the **Workspace**/**ScenesClient** folder. | | **Load Server** | If the scene's server content is unloaded, moves its **Environment**/**Server** folder into the **Workspace**/**ScenesServer** folder. | | **Unload Client** | If the scene's client content is loaded, moves its **Client** folder from **Workspace**/**ScenesClient** back to the **[Scene]**/**Environment** folder. | | **Unload Server** | If the scene's server content is loaded, moves its **Server** folder from **Workspace**/**ScenesServer** back to the **[Scene]**/**Environment** folder. | | **Unload All Scenes** | Moves every loaded scene's **Client** and **Server** folder back to its **Environment** folder. | ### Save and load lighting The top-level `Class.Lighting` service stores all of a place's lighting properties and visual effects. Since it's a top-level service, you cannot manually move it to a particular scene's **Environment**/**Server** or **Environment**/**Client** folder. Instead, you can utilize the plugin to copy its properties and children to the scene's **Environment**/**Lighting** folder. 1. Configure the scene's [lighting properties](/docs/en-us/environment/lighting.md), [post-processing](/docs/en-us/environment/post-processing-effects.md) effects, [atmospheric effects](/docs/en-us/environment/atmosphere.md), and [skyboxes](/docs/en-us/environment/skybox.md) through the top-level `Class.Lighting` service. 2. In the **Scene Manager** plugin window, click **Save Lighting** for the desired scene. 3. Select and expand that scene's **Environment**/**Lighting** configuration and you'll see the same lighting properties as [attributes](/docs/en-us/studio/properties.md#instance-attributes) of the folder, as well as cloned children of the top-level `Class.Lighting` service._Cloned instances__Saved attributes_ Once lighting properties and children are saved for a scene, you can quickly load them back into the top-level `Class.Lighting` service by clicking **Load Lighting** from the plugin window. > **Info:** If you want an environmental lighting effect like `Class.Atmosphere` to persist between scenes of a multi-scene event, place it in the top-level `Class.Lighting` service and assign it a boolean [attribute](/docs/en-us/studio/properties.md#instance-attributes) of **SceneFrameworkIgnore** set to **true**. > > The framework also applies a **UseCurrentLighting** attribute to the top-level `Class.Lighting` service with a default of **false**. If set to **true**, the service's lighting overrides all scene-specific lighting during playtesting — however, scene lighting always takes precedence in a published event. As a real world analogy, this lets you stage a concert in full light before "turning down the lights" as the show begins. ### Save and load terrain Since `Class.Terrain` is a top-level class within `Class.Workspace`, you cannot manually move generated or sculpted terrain to a particular scene's **Environment**/**Server** or **Environment**/**Client** folder. Instead, you can utilize the plugin to copy it to the scene's **Environment**/**Terrain** folder. 1. Configure the scene's terrain through the top-level **Terrain** service. 2. In the **Scene Manager** plugin window, click **Save Terrain** for the desired scene. 3. Select and expand that scene's **Environment**/**Terrain** folder and you'll see a **TerrainRegion** object which represents the saved terrain. Once terrain is saved for a scene, you can quickly load it back into the top-level `Class.Terrain` service by clicking **Load Terrain** from the plugin window. > **Warning:** If a scene contains absolutely no terrain, you should still click **Save Terrain** for it. This saves an "empty" terrain region for the scene and effectively clears all terrain from any scene shown before it. ## API reference ### Schema lifecycle hooks #### OnSetup The **OnSetup** lifecycle hook is intended for initializing assets and variables that will be referenced in [OnRun](#onrun) or [OnEndScene](#onendscene), setting up `Datatype.RBXScriptSignal:Connect()|connections` that are intended to last for the duration of the scene, etc. This hook receives the `timePositionObject` parameter which lets you read the current time at setup. > **Warning:** It's recommended that you do **not** call `Global.RobloxGlobals.wait()` or `Library.task.wait()` within **OnSetup**, as it will delay the [OnRun](#onrun) start while the scene's timer continues to count up. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local EventSequencer = require(ReplicatedStorage:WaitForChild("EventSequencer")) local Schema = EventSequencer.createSchema() local clientEnvironment local serverEnvironment local dummy Schema.OnSetup = function(timePositionObject) print("OnSetup (Client)") -- Access scene environments; does not apply to Inline Mode clientEnvironment = EventSequencer.getCurrentSceneEnvironment() serverEnvironment = EventSequencer.getCurrentServerEnvironmentFromClient() -- Wait for assets dummy = clientEnvironment:WaitForChild("Dummy") print("Current time is:", timePositionObject.Value) end ``` ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local EventSequencer = require(ReplicatedStorage:WaitForChild("EventSequencer")) local Schema = EventSequencer.createSchema() local serverEnvironment local partColorConnection local changePartColorEvent = script.Parent.Events.ChangePartColor Schema.OnSetup = function(timePositionObject) print("OnSetup (Server)") -- Access scene environment; does not apply to Inline Mode serverEnvironment = EventSequencer.getCurrentSceneEnvironment() partColorConnection = changePartColorEvent.OnServerEvent:Connect(function(player, changedPart, newColor) serverEnvironment.changedPart.Color = newColor end) print("Current time is:", timePositionObject.Value) end ``` #### OnRun **OnRun** is the main operational lifecycle hook within a [schema](#scene-schemas). It should contain all timed [configurations](#schema-configurations) for the scene, from playing [audio](#audio) or an [animation](#animate) to [scheduling an event](#schedule) like a fireworks display. > **Warning:** It's recommended that you do **not** call `Global.RobloxGlobals.wait()` or `Library.task.wait()` within **OnRun**, as it will delay the scene's orchestration while the scene's timer continues to count up. ```lua Schema.OnRun = function() print("OnRun (Client)") local MainAudio = Schema:audio({ StartTime = 1, SoundId = "rbxassetid://1838673350", OnStart = function(self) print("Audio playing") end, OnEnd = function(self) print("Audio ended") end }) end ``` #### OnEndScene The **OnEndScene** lifecycle hook is useful for cleaning up anything outstanding in the scene, such as disconnecting connections created in [OnSetup](#onsetup) or [OnRun](#onrun) that remain for the duration of the scene. ```lua Schema.OnEndScene = function() print("OnEndScene (Server)") if partColorConnection then partColorConnection:Disconnect() partColorConnection = nil end end ``` > **Info:** In cases like a post-show venue which may be considered "endless," omit the **OnEndScene** lifecycle event and make the [TimeLength](#time-length) value as long as needed to accommodate all of your schema [configurations](#configurations). Also, do not [inform](#inform) anything to the schema, as those things will be cleaned up when the scene reaches its time length. ### Schema configurations #### audio Creates a `Class.Sound` object in the workspace which plays at a certain time. The sound is then deleted after the scene is over or after the `Class.Sound` object finishes playing. | Configuration Key | Description | | --- | --- | | `StartTime` | When to play the audio in relation to the scene duration, in seconds. | | `SoundId` | Asset ID of the audio to play. | | `OnStart` | Custom function to fire when the audio begins playing. | | `OnEnd` | Custom function to fire when the audio finishes playing. | | `Volume` | Volume of the `Class.Sound` object; default is 0.5. | ```lua Schema.OnRun = function() print("OnRun (Client)") local MainAudio = Schema:audio({ StartTime = 1, SoundId = "rbxassetid://1838673350", OnStart = function(self) print("Audio playing") end, OnEnd = function(self) print("Audio ended") end }) end ``` > **Info:** You can access `[audio].CurrentSoundIntensityRatio` as the sound intensity ratio of an audio configuration for use in your intervals. This acts like a pseudo pulse/tempo of a song based on how loud it has been so far. #### animate Creates an `Class.Animation` which plays at a certain time. | Configuration Key | Description | | --- | --- | | `StartTime` | When to play the animation in relation to the scene duration, in seconds. | | `EndTime` | Optional time when to end the animation in relation to the scene duration, in seconds. | | `Rig` | The animation rig to play the animation on. | | `AnimationId` | Asset ID of the animation to play. | | `Speed` | Playback speed of the animation; default is 1. | | `FadeInTime` | Amount of time to fade in the animation, in seconds; default is 0.2 (seconds). | | `FadeOutTime` | Amount of time to fade out the animation, in seconds; default is 0.2 (seconds). | | `OnStart` | Custom function to fire when the animation begins playing. | | `OnEnd` | Custom function to fire when the animation finishes playing. | | `Looped` | Whether to loop the animation; default is `false`. | | `SyncToAudio` | Table defining whether to sync the animation to an [audio](#audio) configuration. Accepts the following keys: | ```lua Schema.OnRun = function() print("OnRun (Client)") local MainAudio = Schema:audio({ StartTime = 1, SoundId = "rbxassetid://1838673350", }) local DanceAnimation = Schema:animate({ AnimationId = "rbxassetid://3695333486", Rig = Dummy, Speed = 1, FadeInTime = 0.1, FadeOutTime = 0.3, SyncToAudio = { Audio = MainAudio, StartAtAudioTime = 5, }, OnStart = function(self) print("Animation playing") end, OnEnd = function(self) print("Animation stopped") end }) end ``` #### tween Creates a configurable `Class.Tween` which is preserved in seeking and in dynamic join, meaning you can chain tweens at separate points in time and everything should play and sync as expected. | Configuration Key | Description | | --- | --- | | `StartTimes` | Table of start times in relation to the scene duration, in seconds. | | `Class.Tween` | Table defining the object and properties to tween. Accepts the following keys: | | `OnStart` | Custom function to fire when the tween begins playing. | | `OnHeartbeat` | Custom function to fire on every `Class.RunService.Heartbeat\|Heartbeat`; receives the tween alpha as its second parameter. | | `OnEnd` | Custom function to fire when the tween finishes playing. | | `SyncToAudio` | Table defining whether to sync the tween to an [audio](#audio) configuration. Accepts the following keys: | ```lua Schema.OnRun = function() print("OnRun (Client)") local MainAudio = Schema:audio({ StartTime = 1, SoundId = "rbxassetid://1838673350", }) local LightFadeOut = Schema:tween({ StartTimes = {29.884}, Tween = { Object = game:GetService("Lighting"), Info = TweenInfo.new(2, Enum.EasingStyle.Sine, Enum.EasingDirection.Out), Properties = { Brightness = 0, } }, SyncToAudio = { Audio = MainAudio, StartAtAudioTimes = {5, 7.2, 9.4, 11.6}, }, OnStart = function(self) print("Tween playing") end, OnHeartbeat = function(self, alpha) print("Tween alpha", alpha) end, OnEnd = function(self) print("Tween completed") end, }) end ``` #### interval Executes a custom callback function over a specified duration on a specified frequency, in seconds. Useful for repeating events like flashing lights, pulsing an audio's intensity, etc. The lowest possible frequency is 0 seconds, but technically the minimum frequency is always clamped to `Class.RunService.Heartbeat|Heartbeat`. | Configuration Key | Description | | --- | --- | | `StartTime` | Beginning of the interval duration in relation to the scene duration, in seconds. | | `EndTime` | End of the interval duration in relation to the scene duration, in seconds. | | `Frequency` | How often the `OnInterval` function should fire, in seconds, with the first execution being at `StartTime`. | | `OnStart` | Custom function to fire when the series of intervals begins. | | `OnInterval` | Custom function to fire at every interval within the specified duration (`StartTime` to `EndTime`). | | `OnEnd` | Custom function to fire when the series of intervals ends. | | `SyncToAudio` | Table defining whether to sync the interval duration to an [audio](#audio) configuration. Accepts the following keys: | ```lua Schema.OnRun = function() print("OnRun (Client)") local MainAudio = Schema:audio({ StartTime = 1, SoundId = "rbxassetid://1838673350", }) local ClientTimerUpdate = Schema:interval({ Frequency = 1, SyncToAudio = { StartAtAudioTime = 2.5, EndAtAudioTime = 10, Audio = MainAudio }, OnInterval = function(self) print(MainAudio.Sound.TimePosition, MainAudio.CurrentSoundIntensityRatio) end, }) end ``` #### schedule Similar to [interval](#interval) except that you can define multiple specific starting times for the same event, such as scheduling a fireworks display twice in a scene. | Configuration Key | Description | | --- | --- | | `StartTimes` | Table of start times in relation to the scene duration, in seconds. | | `OnStart` | Custom function to fire at every specified time in the `StartTimes` table. | | `Skippable` | Boolean defining whether the scheduled event can be skipped for users who join late, or for when seeking ahead of a scheduled start time. If set to `false`, all event start times scheduled before the join/seek time will occur at that join/seek time. If set to `true`, only the start times scheduled **after** join/seek will occur. Default is `false`. | | `SyncToAudio` | Table defining whether to sync the schedule to an [audio](#audio) configuration. Accepts the following keys: | ```lua Schema.OnRun = function() print("OnRun (Client)") Schema:schedule({ StartTimes = {5, 27.25}, OnStart = function(self) -- Initialize temporary heartbeat connection local tempConnection = RunService.Heartbeat:Connect(function() end) -- Inform framework of connection Schema:inform(tempConnection) end }) end ``` #### inform Informs the framework of any modules, UI objects, connections, etc. which are created in the [OnRun](#onrun) lifecycle hook, ensuring they are properly cleaned up when [seeking](#seek-scenes). Use cases include: - Informing the framework of a temporary ad-hoc connection such as `Class.RunService.Heartbeat` so that the connection is cleaned up when seeking to an earlier point in the scene's duration.```lua Schema.OnRun = function() print("OnRun (Server)") Schema:schedule({ StartTimes = {5}, OnStart = function(self) -- Initialize temporary heartbeat connection local tempConnection = RunService.Heartbeat:Connect(function() end) -- Inform framework of connection Schema:inform(tempConnection) end }) end ``` - Calling a custom "cleanup" function in a `Class.ModuleScript` that initializes a connection or other reference during the [OnRun](#onrun) lifecycle hook.```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local RunService = game:GetService("RunService") local customModule = require(ReplicatedStorage:WaitForChild("CustomModule")) local EventSequencer = require(ReplicatedStorage:WaitForChild("EventSequencer")) local Schema = EventSequencer.createSchema() Schema.OnRun = function() print("OnRun (Server)") Schema:schedule({ StartTimes = {5}, OnStart = function(self) -- Call "init" function in custom module customModule.init() -- Call "clean" function in custom module on scene cleanup Schema:inform(customModule, customModule.clean) end, }) end ``````lua local RunService = game:GetService("RunService") local CustomModule = {} CustomModule.init = function() -- Initialize heartbeat connection CustomModule.connection = RunService.Heartbeat:Connect(function() end) end CustomModule.clean = function() -- Disconnect and clear heartbeat connection if CustomModule.connection then CustomModule.connection:Disconnect() CustomModule.connection = nil end end return CustomModule ``` > **Warning:** While **inform** is critical for [seeking](#seek-scenes) support, you should only use it to inform the framework of any module, UI object, connection, etc. that is created/initialized during the [OnRun](#onrun) lifecycle hook. If you want to reference something throughout the entirety of the scene, simply initialize it in [OnSetup](#onsetup) and clean it up in [OnEndScene](#onendscene). ### Functions #### loadScene _ loadScene(sceneName: `Library.string`, startTime: `number` ?)_ Programmatically loads a scene by `sceneName` and starts it at `startTime` from its beginning. There will be a 5 second "grace period" for the scene to load from the server before the seek occurs and the scene starts playing. This means that if you call `loadScene("[SceneName]", 20)` at exactly 4:15:00 PM, the framework will wait 5 seconds in addition to the requested 20, kicking off the scene at 4:15:25 PM. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local EventSequencer = require(ReplicatedStorage:WaitForChild("EventSequencer")) -- Figure out next scene to load when current scene finishes EventSequencer.onOrchestrationFinished:Connect(function(endedSceneName) if endedSceneName == "PreShow" then -- "PreShow" ended; load the first scene in the concert EventSequencer.loadScene("Track1") elseif endedSceneName == "Track1" then -- "Track1" ended; load the second scene in the concert EventSequencer.loadScene("Track2") else -- Loop back to the pre-show scene EventSequencer.loadScene("PreShow") end end) ``` > **Warning:** Do not call `loadScene` from within a schema [lifecycle hook](#schema-lifecycle-hooks), as **EventSequencer** expects that scene transitions and the overall sequence of multi-scene events is handled outside of individual scenes. #### createSchema _ createSchema(): `Library.table`_ Returns an instance of the scene [schema](#scene-schemas) to create logic for the scene. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local EventSequencer = require(ReplicatedStorage:WaitForChild("EventSequencer")) local Schema = EventSequencer.createSchema() Schema.OnSetup = function(timePositionObject) print("OnSetup (Client)") end ``` #### seek _ seek(time: `number`)_ Seeks to the `time` value, in seconds, from the currently loaded scene's beginning. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local EventSequencer = require(ReplicatedStorage:WaitForChild("EventSequencer")) EventSequencer.seek(95.58) ``` > **Warning:** Do not call `seek` from within a schema [lifecycle hook](#schema-lifecycle-hooks), as **EventSequencer** expects that scene seeking and [loading](#loadscene) is handled outside of individual scenes. #### setSceneWarningTime _ setSceneWarningTime(endSceneTimeWindow: `number`)_ Sets the amount of time from the **end** of all scenes at which a warning is dispatched. You can detect the warning either client-side through [onSceneEndingWarningForClient](#onsceneendingwarningforclient) or server-side through [onSceneEndingWarningForServer](#onsceneendingwarningforserver). ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local EventSequencer = require(ReplicatedStorage:WaitForChild("EventSequencer")) -- Load scene EventSequencer.loadScene("BeautifulScene") -- Set warning time to 5 seconds before the scene ends EventSequencer.setSceneWarningTime(5) -- Detect when scene is about to end EventSequencer.onSceneEndingWarningForServer:Connect(function() warn("Scene is about to end!") end) ``` #### setSeekingPermissions _ setSeekingPermissions(permissions: `Library.table`)_ Grants seeking permission based on the event's `Class.DataModel.PlaceId|PlaceId` as well as specific `Class.Player.UserId|UserIds` and/or [groups](/docs/en-us/projects/groups.md) and roles within them. See [seek and switch scenes](#seek-scenes) for more information. #### getCurrentSceneEnvironment _ getCurrentSceneEnvironment(): `Class.Folder` **(YIELDS)**_ Returns the current scene's client-side or server-side [Environment](#environment) folder, depending on whether it's called from the [Client](#client) schema script or [Server](#server) schema script respectively. > **Warning:** Do not use this function with [Inline Mode](#inline-mode), as that mode expects you to use existing assets and global property settings for the place, not load in scene-specific assets/properties from the scene's [Environment](#environment) folder. This function will **infinitely yield** if the folder doesn't exist. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local EventSequencer = require(ReplicatedStorage:WaitForChild("EventSequencer")) local Schema = EventSequencer.createSchema() local clientEnvironment local serverEnvironment Schema.OnSetup = function(timePositionObject) print("OnSetup (Client)") -- Access scene environments; does not apply to Inline Mode clientEnvironment = EventSequencer.getCurrentSceneEnvironment() serverEnvironment = EventSequencer.getCurrentServerEnvironmentFromClient() print("Current time is:", timePositionObject.Value) end ``` ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local EventSequencer = require(ReplicatedStorage:WaitForChild("EventSequencer")) local Schema = EventSequencer.createSchema() local serverEnvironment local partColorConnection local changePartColorEvent = script.Parent.Events.ChangePartColor Schema.OnSetup = function(timePositionObject) print("OnSetup (Server)") serverEnvironment = EventSequencer.getCurrentSceneEnvironment() partColorConnection = changePartColorEvent.OnServerEvent:Connect(function(player, changedPart, newColor) serverEnvironment.changedPart.Color = newColor end) end ``` #### getCurrentServerEnvironmentFromClient _ getCurrentServerEnvironmentFromClient(): `Class.Folder` **(YIELDS)**_ Returns the current scene's **server-side** [Environment](#environment) folder. Unlike [getCurrentSceneEnvironment](#getcurrentsceneenvironment), you can call this from the [Client](#client) schema script. > **Warning:** Do not use this function with [Inline Mode](#inline-mode), as that mode expects you to use existing assets and global property settings for the place, not load in scene-specific assets/properties from the scene's [Environment](#environment) folder. This function will **infinitely yield** if the folder doesn't exist. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local EventSequencer = require(ReplicatedStorage:WaitForChild("EventSequencer")) local Schema = EventSequencer.createSchema() local clientEnvironment local serverEnvironment Schema.OnSetup = function(timePositionObject) print("OnSetup (Client)") -- Access scene environments; does not apply to Inline Mode clientEnvironment = EventSequencer.getCurrentSceneEnvironment() serverEnvironment = EventSequencer.getCurrentServerEnvironmentFromClient() print("Current time is:", timePositionObject.Value) end ``` #### isLoadingScene _ isLoadingScene(): `boolean`_ Called from the server to know if a scene is currently loading. > **Warning:** Do not use this function with [Inline Mode](#inline-mode), as that mode expects you to use existing assets and global property settings for the place, not load in scene-specific assets/properties from the scene's [Environment](#environment) folder. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local EventSequencer = require(ReplicatedStorage:WaitForChild("EventSequencer")) print(EventSequencer.isLoadingScene()) while EventSequencer.isLoadingScene() do task.wait() end print("Scene loaded") ``` ### Events #### onSceneEndingWarningForClient Fires on the client before the scene is about to end. The default time is 3 seconds, but you can configure it through [setSceneWarningTime](#setscenewarningtime). This event can only be connected in a `Class.LocalScript`. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local EventSequencer = require(ReplicatedStorage:WaitForChild("EventSequencer")) -- Detect when scene is about to end (client-side) EventSequencer.onSceneEndingWarningForClient:Connect(function() warn("Scene is about to end!") end) ``` #### onSceneEndingWarningForServer Fires on the server before the scene is about to end. The default time is 3 seconds, but you can configure it through [setSceneWarningTime](#setscenewarningtime). This event can only be connected in a `Class.Script`. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local EventSequencer = require(ReplicatedStorage:WaitForChild("EventSequencer")) -- Detect when scene is about to end (server-side) EventSequencer.onSceneEndingWarningForServer:Connect(function() warn("Scene is about to end!") end) ``` #### onSceneLoadedForClient Fires on the client when the scene is starting. This event can only be connected in a `Class.LocalScript`. > **Warning:** Don't use this event for [Inline Mode](#inline-mode) because that mode expects you to use existing assets and global property settings for the place, not load in scene-specific assets/properties from the scene's [Environment](#environment) folder. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local EventSequencer = require(ReplicatedStorage:WaitForChild("EventSequencer")) -- Detect when scene is starting (client-side) EventSequencer.onSceneLoadedForClient:Connect(function() warn("Scene is starting!") end) ``` #### onOrchestrationFinished Fires on the server when a scene has reached its [time length](#time-length) and has effectively ended. This event receives an `endedSceneName` string name argument for the scene that just finished and you can chain off this event to conditionally [load another scene](#loadscene). Can only be connected in a `Class.Script`. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local EventSequencer = require(ReplicatedStorage:WaitForChild("EventSequencer")) -- Figure out next scene to load when current scene finishes EventSequencer.onOrchestrationFinished:Connect(function(endedSceneName) if endedSceneName == "PreShow" then -- "PreShow" ended; load the first scene in the concert EventSequencer.loadScene("Track1") elseif endedSceneName == "Track1" then -- "Track1" ended; load the second scene in the concert EventSequencer.loadScene("Track2") else -- Loop back to the pre-show scene EventSequencer.loadScene("PreShow") end end) ``` --- title: "Friends Locator" url: /docs/en-us/resources/modules/friends-locator last_updated: 2026-06-29T19:34:10Z description: "The Friends Locator module lets players easily find and teleport to their friends inside a place." --- # Friends Locator It can be challenging to locate friends in-experience. The **FriendsLocator** [developer module](/docs/en-us/resources/modules.md) lets players easily find and teleport to their friends inside a place. ## Module usage ### Installation To use the **FriendsLocator** module in an experience: 1. From Studio's **Window** menu or **Home** tab toolbar, open the [Toolbox](/docs/en-us/projects/assets/toolbox.md) and select the **Creator Store** tab. 2. Make sure the **Models** sorting is selected, then click the **See All** button for **Categories**. 3. Locate and click the **Packages** tile. 4. Locate the **Friends Locator** module and click it, or drag-and-drop it into the 3D view. 5. In the [Explorer](/docs/en-us/studio/explorer.md) window, move the entire **FriendsLocator** model into `Class.ReplicatedStorage`. Upon running the experience the module will begin running. ### Test in Studio To test the module in Studio, the **FriendsLocator** module must be run in a multi-client simulation, since no friends will be present in a solo playtest. 1. In **StarterPlayerScripts**, create a new `Class.LocalScript` and rename it **ConfigureFriendsLocator**. 2. Paste the following code into the new **ConfigureFriendsLocator** script. The `showAllPlayers` setting within the [configure](#configure) function ensures that locators are shown for all users while testing in Studio, but not in a published place.```lua local RunService = game:GetService("RunService") local ReplicatedStorage = game:GetService("ReplicatedStorage") local FriendsLocator = require(ReplicatedStorage.FriendsLocator) FriendsLocator.configure({ showAllPlayers = RunService:IsStudio(), -- Allows for debugging in Studio }) ``` 3. From Studio's mezzanine, start a [multi-client simulation](/docs/en-us/studio/testing-modes.md#multi-client-simulation) with 2 clients. Three new instances of Studio will open; one simulated server and two simulated clients.![Server & Clients option in the testing modes dropdown of Studio's mezzanine.](../../assets/studio/general/Mezzanine-Testing-Mode-Server-Clients.png) 4. Go into either of the **client** Studio instances, move a distance of 100 studs away from the other character, and you should see a **locator icon** appear over its head. > **Info:** Due to the way user IDs are handled in Studio, avatar images will not appear on the locator icons during testing. > **Warning:** By default, clicking/tapping a friends's icon will teleport your character to that character's location. If you keep this default behavior and you find that streaming pause is occurring under the [instance streaming](/docs/en-us/workspace/streaming.md) architecture, you may want to [request area streaming](/docs/en-us/workspace/streaming/techniques.md#proactive-streaming) around the teleport location as shown in the [clicked](#clicked) event code sample. ### Connect to events The **FriendsLocator** module exposes [events](#events) so that you can introduce custom behaviors when users interact with a locator icon. 1. Make sure that you've created the **ConfigureFriendsLocator** script outlined in [testing in Studio](#test-in-studio). 2. Add lines 8 and 11-13 to the script:```lua local RunService = game:GetService("RunService") local ReplicatedStorage = game:GetService("ReplicatedStorage") local FriendsLocator = require(ReplicatedStorage.FriendsLocator) FriendsLocator.configure({ showAllPlayers = RunService:IsStudio(), -- Allows for debugging in Studio teleportToFriend = false, -- Prevent teleport on icon click/tap }) FriendsLocator.clicked:Connect(function(player, playerCFrame) print("You clicked on locator icon for", player.DisplayName) end) ``` 3. Conduct a [multi-client test](#test-in-studio) and click on another character's locator icon. Notice that your character does not teleport to that location, and the event triggers to allow for custom handling of icon clicks. ### Custom locator UI If the default style does not fit your experience, you can replace the default avatar portrait UI with your own UI. To replace the default UI: 1. Create a new `Class.ScreenGui` instance inside the **StarterGui** container. 2. Create a `Class.Frame` instance named **FriendLocator** as a child of the new `Class.ScreenGui`, then add elements like `Class.ImageLabel|ImageLabels`, `Class.TextLabel|TextLabels` to design your custom UI. 3. When finished, **disable** the parent `Class.ScreenGui` so that the module doesn't show the custom locator UI until needed. 4. **(Optional)** If you want the friend's avatar portrait and `Class.Player.DisplayName|DisplayName` to show up somewhere in the custom UI, you can place the following instances inside the **FriendLocator** frame. - An `Class.ImageLabel` of the name **Portrait**. - A `Class.TextLabel` of the name **DisplayName**. The module will look for these items and display the friend's avatar portrait and/or display name respectively. ## API reference ### Functions #### configure _ configure(config: `Library.table`)_ Overrides default configuration options through the following keys/values in the `config` table. | Key | Description | Default | | --- | --- | --- | | `alwaysOnTop` | If `true`, shows locator icons on top of everything, preventing them from being blocked by 3D world objects. | true | | `showAllPlayers` | If `true`, shows locations for all players, not just friends; this can help verify the module's functionality in Studio. | false | | `teleportToFriend` | Teleports player character to the friend's location when their locator icon is clicked or tapped. | true | | `thresholdDistance` | Camera distance threshold at which locator icons appear; friends closer than this distance will not display icons. | 100 | | `maxLocators` | Maximum number of locator icons shown at any given time. | 10 | ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local FriendsLocator = require(ReplicatedStorage.FriendsLocator) FriendsLocator.configure({ alwaysOnTop = true, showAllPlayers = false, teleportToFriend = true, thresholdDistance = 100, maxLocators = 10 }) ``` ### Events #### clicked Fires when a locator icon is clicked/activated by the local player. This event can only be connected in a `Class.LocalScript`. | Parameters | | --- | | player: `Class.Player` | Player that the locator icon belongs to. | | playerCFrame: `Datatype.CFrame` | `Datatype.CFrame` of the player's `Class.Humanoid.RootPart` that the locator icon belongs to. | ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local Players = game:GetService("Players") local Workspace = game:GetService("Workspace") local FriendsLocator = require(ReplicatedStorage.FriendsLocator) local localPlayer = Players.LocalPlayer FriendsLocator.clicked:Connect(function(player, playerCFrame) -- Request streaming around target location if Workspace.StreamingEnabled then local success, errorMessage = pcall(function() localPlayer:RequestStreamAroundAsync(playerCFrame.Position) end) if not success then print(errorMessage) end end print("You clicked on locator icon for", player.DisplayName, "at position", playerCFrame.Position) end) ``` #### visibilityChanged Fires when a locator icon is shown/hidden on the local player's screen. This event can only be connected in a `Class.LocalScript`. | Parameters | | --- | | player: `Class.Player` | `Class.Player` object that the locator icon belongs to. | | playerCFrame: `Datatype.CFrame` | `Datatype.CFrame` of the player's `Class.Humanoid.RootPart` that the locator icon belongs to. | | isVisible: `boolean` | Whether the locator icon is currently visible on the local player's screen. Note that this will still be `true` if `alwaysOnTop` is `false` and the locator renders behind an object in the 3D world. | ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local FriendsLocator = require(ReplicatedStorage.FriendsLocator) FriendsLocator.visibilityChanged:Connect(function(player, playerCFrame, isVisible) print("Visibility of locator icon for", player.DisplayName, ":", isVisible) end) ``` --- title: "Developer modules" url: /docs/en-us/resources/modules last_updated: 2026-06-29T19:34:10Z description: "Developer modules let you easily add powerful social features to your experiences." --- # Developer modules **Developer modules** let you easily add powerful social features to your experiences. Each module is **designed to work out of the box**, so the moment you press play it will simply work. If this isn't your style, you can configure each module to work in different ways. [Selfie Mode](/docs/en-us/resources/modules/selfie-mode.md)_Pose and capture memorable moments_ [Merch Booth](/docs/en-us/resources/modules/merch-booth.md)_Sell avatar assets and more in an experience_ [Friends Locator](/docs/en-us/resources/modules/friends-locator.md)_Easily spot your friends and teleport to them_ [Spawn With Friends](/docs/en-us/resources/modules/spawn-with-friends.md)_Join an experience right next to your friends_ [Emote Bar](/docs/en-us/resources/modules/emote-bar.md)_Express yourself and have dance parties_ [Profile Card](/docs/en-us/resources/modules/profile-card.md)_Craft your identity within an experience_ [Photo Booth](/docs/en-us/resources/modules/photo-booth.md)_Strike a pose with a unique background_ [Surface Art](/docs/en-us/resources/modules/surface-art.md)_Literally leave your mark in an experience_ [Scavenger Hunt](/docs/en-us/resources/modules/scavenger-hunt.md)_Encourage players to explore your space_ [Social Interactions](/docs/en-us/resources/modules/social-interactions.md)_Add a touch of realism to your avatars_ --- title: "Merch Booth" url: /docs/en-us/resources/modules/merch-booth last_updated: 2026-06-29T19:34:10Z description: "The Merch Booth module lets you sell avatar assets, passes, and products directly in an experience." --- # Merch Booth The **MerchBooth** [developer module](/docs/en-us/resources/modules.md) lets you offer [avatar assets](/docs/en-us/avatar/rigid-accessories.md), [passes](/docs/en-us/production/monetization/passes.md), and [developer products](/docs/en-us/production/monetization/developer-products.md) for sale directly within your experience. Players can browse items, preview assets on their own avatar, purchase items, and instantly use or equip them — all without leaving your experience. This can help you [monetize](/docs/en-us/production/monetization.md) your experience and gain revenue through the 40% affiliate fee associated with selling other creators' items. > **Warning:** To offer assets created by third parties in the merch booth, make sure **Allow Third Party Sales** is enabled from the **Security** section of Studio's **File** ⟩ **Experience Settings** window. If this setting is disabled, you will not be able to sell UGC assets created by other users. ## Module usage ### Installation To use the **MerchBooth** module in an experience: 1. From Studio's **Window** menu or **Home** tab toolbar, open the [Toolbox](/docs/en-us/projects/assets/toolbox.md) and select the **Creator Store** tab. 2. Make sure the **Models** sorting is selected, then click the **See All** button for **Categories**. 3. Locate and click the **Packages** tile. 4. Locate the **Merch Booth** module and click it, or drag-and-drop it into the 3D view. 5. In the [Explorer](/docs/en-us/studio/explorer.md) window, move the entire **MerchBooth** model into `Class.ReplicatedStorage`. Upon running the experience the module will begin running. ### Configuration The module is preconfigured to work for most use cases, but it can be easily customized through the [configure](#configure) function. For example, to create a lighter theme and disable the default **Filter** button in the upper-left area of the catalog view: 1. In **StarterPlayerScripts**, create a new `Class.LocalScript` and rename it to **ConfigureMerchBooth**. 2. Paste the following code into the new script.```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local MerchBooth = require(ReplicatedStorage.MerchBooth) MerchBooth.configure({ backgroundColor = Color3.fromRGB(220, 210, 200), textSize = 17, textFont = Enum.Font.Fondamento, textColor = Color3.fromRGB(20, 20, 20), useFilters = false }) ``` ### Add items What's a merch booth without merch? The following sections outline how to add [avatar assets](#avatar-assets), [passes](#passes), and [developer products](#developer-products) to your merch booth. #### Avatar assets Items such as [clothing and accessories](/docs/en-us/avatar/rigid-accessories.md) must be added through their **asset ID** located on the item's detail page in the [Avatar Shop](https://www.roblox.com/catalog). 1. Create a `Class.Script` within **ServerScriptService** and paste in the following code.```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local MerchBooth = require(ReplicatedStorage.MerchBooth) local items = { } for _, assetId in items do local success, errorMessage = pcall(function() MerchBooth.addItemAsync(assetId) end) if not success then warn(errorMessage) end end ``` 2. Copy item asset IDs from their [Avatar Shop](https://www.roblox.com/catalog) website URL. For example, the ID of [Roblox Baseball Cap](https://www.roblox.com/catalog/607702162/Roblox-Baseball-Cap) is **607702162**. 3. Paste each copied ID into a comma-delimited list within the `items` table. By default, items appear in the catalog view in alphabetical order, but you can customize sorting using [setCatalogSort](#setcatalogsort).```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local MerchBooth = require(ReplicatedStorage.MerchBooth) local items = { 607702162, -- Roblox Baseball Cap 4819740796, -- Robox 1374269, -- Kitty Ears 11884330, -- Nerd Glasses 10476359, -- Paper Hat } for _, assetId in items do local success, errorMessage = pcall(function() MerchBooth.addItemAsync(assetId) end) if not success then warn(errorMessage) end end ``` > **Info:** This example uses assets owned by the Roblox account, but a thriving marketplace of incredible community-made assets is available [here](https://www.roblox.com/catalog?Category=13&Subcategory=40). #### Passes Adding [passes](/docs/en-us/production/monetization/passes.md) requires pass IDs which can be located in the [Creator Dashboard](https://create.roblox.com/dashboard/creations). 1. Create a `Class.Script` within **ServerScriptService** and paste in the following code.```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local MerchBooth = require(ReplicatedStorage.MerchBooth) local items = { } for _, assetId in items do local success, errorMessage = pcall(function() MerchBooth.addItemAsync(assetId) end) if not success then warn(errorMessage) end end ``` 2. Navigate to the [Creator Dashboard](https://create.roblox.com/dashboard/creations) and select the experience. 3. In the left column, under **Monetization**, select **Passes**. 4. Hover over a pass' thumbnail, click the **⋯** button, and select **Copy Asset ID** from the context menu. 5. Paste each copied ID into a comma-delimited list within the `items` table **and** include `Enum.InfoType.GamePass` as the second parameter for [addItemAsync](#additemasync) to indicate that the items are passes. By default, items will appear in the catalog view in alphabetical order, but sorting can be customized via [setCatalogSort](#setcatalogsort).```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local MerchBooth = require(ReplicatedStorage.MerchBooth) local items = { 4343758, -- ColdFyre Armor 28521575, -- Slime Shield } for _, assetId in items do local success, errorMessage = pcall(function() MerchBooth.addItemAsync(assetId, Enum.InfoType.GamePass) end) if not success then warn(errorMessage) end end ``` #### Developer Products Adding [developer products](/docs/en-us/production/monetization/developer-products.md) requires product IDs which can be located in the [Creator Dashboard](https://create.roblox.com/dashboard/creations). 1. Create a `Class.Script` within **ServerScriptService** and paste in the following code.```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local MerchBooth = require(ReplicatedStorage.MerchBooth) local items = { } for _, assetId in items do local success, errorMessage = pcall(function() MerchBooth.addItemAsync(assetId) end) if not success then warn(errorMessage) end end ``` 2. Navigate to the [Creator Dashboard](https://create.roblox.com/dashboard/creations) and select the experience. 3. In the left column, under **Monetization**, select **Developer Products**. 4. Hover over a product's thumbnail, click the **⋯** button, and select **Copy Asset ID** from the context menu. 5. Paste each copied ID into a comma-delimited list within the `items` table **and** include `Enum.InfoType.Product` as the second parameter for [addItemAsync](#additemasync) to indicate that the items are developer products. By default, items appear in the catalog view in alphabetical order, but you can customize sorting using [setCatalogSort](#setcatalogsort).```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local MerchBooth = require(ReplicatedStorage.MerchBooth) local items = { 1236602053, -- Mana Refill 1257880672, -- Healing Potion } for _, assetId in items do local success, errorMessage = pcall(function() MerchBooth.addItemAsync(assetId, Enum.InfoType.Product) end) if not success then warn(errorMessage) end end ``` ### Custom catalog button By default, a right-side **catalog button** lets players open the booth at any time. In some cases, it may be useful to [remove](#togglecatalogbutton) this button and connect your own: 1. Create a new button as outlined in [Buttons](/docs/en-us/ui/buttons.md). 2. Create a `Class.LocalScript` as a child of the button object. 3. Paste the following code into the new script.```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local MerchBooth = require(ReplicatedStorage.MerchBooth) -- Remove the default catalog button MerchBooth.toggleCatalogButton(false) -- Connect the custom button script.Parent.Activated:Connect(function() MerchBooth.openMerchBooth() end) ``` ### Shoppable regions A helpful way to drive purchases in your experience is to automatically show the merch booth when a player enters an area. To create a shoppable region: 1. Create an `Class.BasePart.Anchored|Anchored` block that encompasses the detection region. Make sure the block is tall enough to collide with the `Class.Model.PrimaryPart|PrimaryPart` of character models (**HumanoidRootPart** by default)._Block to detect when players approach the front of the shop counter_ 2. Using the [Tags](/docs/en-us/studio/properties.md#instance-tags) section of the block's properties, apply the tag `ShopRegion` to the block so that `Class.CollectionService` detects it. 3. Set the part's `Class.BasePart.Transparency|Transparency` to the maximum to hide it from players in the experience. Also disable its `Class.BasePart.CanCollide|CanCollide` and `Class.BasePart.CanQuery|CanQuery` properties so that objects do not physically collide with it and raycasts do not detect it. 4. Insert a new `Class.LocalScript` under **StarterPlayerScripts**. 5. In the new script, paste the following code which uses the `Class.BasePart.Touched|Touched` and `Class.BasePart.TouchEnded|TouchEnded` events to detect when characters enter/leave the region and calls [openMerchBooth](#openmerchbooth) and [closeMerchBooth](#closemerchbooth) to open/close the booth GUI.```lua local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") local CollectionService = game:GetService("CollectionService") local MerchBooth = require(ReplicatedStorage.MerchBooth) -- Remove the default catalog button MerchBooth.toggleCatalogButton(false) local function setupRegion(region: BasePart) region.Touched:Connect(function(otherPart) local character = Players.LocalPlayer.Character if character and otherPart == character.PrimaryPart then MerchBooth.openMerchBooth() end end) region.TouchEnded:Connect(function(otherPart) local character = Players.LocalPlayer.Character if character and otherPart == character.PrimaryPart then MerchBooth.closeMerchBooth() end end) end -- Iterate through existing tagged shop regions for _, region in CollectionService:GetTagged("ShopRegion") do setupRegion(region) end -- Detect when non-streamed shop regions stream in CollectionService:GetInstanceAddedSignal("ShopRegion"):Connect(setupRegion) ``` ### Proximity prompts As an alternative to the 2D catalog view, you can add **proximity prompts** over in-experience objects. This encourages players to discover items in the 3D environment, preview them on their own avatar, purchase them, and instantly equip them. See [addProximityButton](#addproximitybutton) for details. > **Info:** If a player has opened an item view through a proximity prompt, it automatically closes when the player moves further away from the prompt object than its activation distance. If you want to keep the booth open regardless of the player's distance from the prompt, set `closeWhenFarFromPrompt` to `false` in a [configure](#configure) call. ### Change the equip effect By default, the merch booth shows a generic sparkle effect when a player equips an item from it. To change the effect, set `particleEmitterTemplate` to your own instance of a `Class.ParticleEmitter` in a [configure](#configure) call. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local MerchBooth = require(ReplicatedStorage.MerchBooth) local myParticleEmitter = Instance.new("ParticleEmitter") myParticleEmitter.SpreadAngle = Vector2.new(22, 22) myParticleEmitter.Lifetime = NumberRange.new(0.5, 1.5) myParticleEmitter.Shape = Enum.ParticleEmitterShape.Sphere myParticleEmitter.Transparency = NumberSequence.new(0, 1) myParticleEmitter.RotSpeed = NumberRange.new(200, 200) MerchBooth.configure({ particleEmitterTemplate = myParticleEmitter }) ``` ### GUI visibility By default, the merch booth hides all `Class.ScreenGui|ScreenGuis` and `Class.CoreGui|CoreGuis` when its UI appears, including the chat, leaderboard, and others included by Roblox. If you want to disable this behavior, set `hideOtherUis` to `false` in a [configure](#configure) call. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local MerchBooth = require(ReplicatedStorage.MerchBooth) MerchBooth.configure({ hideOtherUis = false }) ``` ### Character movement It can be advantageous to prevent a character from moving while they are in the merch booth. This can be done by setting `disableCharacterMovement` to `true` in a [configure](#configure) call. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local MerchBooth = require(ReplicatedStorage.MerchBooth) MerchBooth.configure({ disableCharacterMovement = true }) ``` ## API reference ### Types #### Item Items in the merch booth are represented by a dictionary with the following key-value pairs. Items can be gathered through the [getItems](#getitems) function or the [itemAdded](#itemadded) event. | Key | Type | Description | | --- | --- | --- | | `assetId` | number | Catalog ID of the item, as passed to [addItemAsync](#additemasync). | | `title` | string | Item title as it appears in the catalog. | | `price` | number | Item price in Robux. | | `description` | string | Item description as it appears in the catalog. | | `assetType` | string | String representing the item's accessory type. | | `isOwned` | bool | Whether the current player owns the item. | | `creatorName` | string | Item creator as shown in the catalog. | | `creatorType` | `Enum.CreatorType` | Creator type for the item. | ### Enums #### MerchBooth.Controls Used along with [setControlKeyCodes](#setcontrolkeycodes) to customize the keys and gamepad buttons for interacting with the merch booth. | Name | Summary | | --- | --- | | `ProximityPrompts` | Key and/or gamepad button to open the item view when [proximity prompts](#proximity-prompts) are configured. | | `OpenMerchBooth` | Key and/or gamepad button to open the merch booth. | | `CloseMerchBooth` | Key and/or gamepad button to close the merch booth. | | `Filter` | Key and/or gamepad button to use the default **Filter** pulldown in the upper-left area of the catalog view. | | `ViewItem` | Key and/or gamepad button to open a specific merch booth item view. | ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local MerchBooth = require(ReplicatedStorage.MerchBooth) MerchBooth.setControlKeyCodes(MerchBooth.Controls.ProximityPrompts, { keyboard = Enum.KeyCode.Q, gamepad = Enum.KeyCode.ButtonL1 }) ``` ### Functions #### configure _ configure(config: `Library.table`)_ Overrides default client-side configuration options through the following keys/values in the `config` table. This function can only be called from a `Class.LocalScript`. #### Appearance | Key | Description | Default | | --- | --- | --- | | `backgroundColor` | Main background color of the window (`Datatype.Color3`). | [0, 0, 0] | | `cornerRadius` | Corner radius for the main window (`Datatype.UDim`). | (0, 16) | | `cornerRadiusSmall` | Corner radius for elements inside the window (`Datatype.UDim`). | (0, 8) | | `textFont` | Font of "main text" such as prices, descriptions, and other general info (`Enum.Font`). | `Enum.Font\|Gotham` | | `textSize` | Size of the main text. | 14 | | `textColor` | Color of the main text (`Datatype.Color3`). | [255, 255, 255] | | `secondaryTextColor` | Color used for some variations of the main text (`Datatype.Color3`). | [153, 153, 158] | | `headerFont` | Font of the header text used for the window title (`Enum.Font`). | `Enum.Font\|GothamMedium` | | `headerTextSize` | Size of the header text used for the window title. | 18 | | `titleFont` | Font of the title text used for item names on the item detail page (`Enum.Font`). | `Enum.Font\|GothamBold` | | `titleTextSize` | Size of the title text used for item names on the item detail page. | 28 | | `buttonColor` | Background color for larger buttons in a clickable state, such as the main purchase button in item view (`Datatype.Color3`). | [255, 255, 255] | | `buttonTextColor` | Text color for larger buttons in a clickable state, such as the main purchase button in item view (`Datatype.Color3`). | [0, 0, 0] | | `secondaryButtonColor` | Background color for smaller buttons such as the price buttons in catalog view or the **Try On** button (`Datatype.Color3`). | [34, 34, 34] | | `secondaryButtonTextColor` | Text color for smaller buttons such as the price buttons in catalog view or the **Try On** button (`Datatype.Color3`). | [255, 255, 255] | | `inactiveButtonColor` | Background color for all buttons in an un-clickable state (`Datatype.Color3`). | [153, 153, 158] | | `inactiveButtonTextColor` | Text color for all buttons in an un-clickable state (`Datatype.Color3`). | [255, 255, 255] | | `particleEmitterTemplate` | Optional custom `Class.ParticleEmitter` instance that appears and plays on equip. | | #### Proximity Prompts | Key | Description | Default | | --- | --- | --- | | `proximityButtonActivationDistance` | Maximum distance a player's character can be from the [prompt](#proximity-prompts) adornee for the prompt to appear. | 10 | | `proximityButtonExclusivity` | `Enum.ProximityPromptExclusivity` specifying which prompts can be shown at the same time. | `Enum.ProximityPromptExclusivity\|OnePerButton` | | `proximityButtonOffset` | Pixel offset applied to the prompt's UI (`Datatype.Vector2`). | (0, 0) | | `proximityButtonPulseCount` | How many "pulses" occur around proximity buttons before stopping. | 3 | #### Behavior | Key | Description | Default | | --- | --- | --- | | `useFilters` | Toggles on/off the **Filter** button shown in the catalog. | true | | `disableCharacterMovement` | If `true`, prevents character from moving while the merch booth is open. | false | | `hideOtherUis` | If `true`, the merch booth hides all `Class.ScreenGui\|ScreenGuis` and `Class.CoreGui\|CoreGuis` when its UI appears. | true | | `closeWhenFarFromPrompt` | If `true` **and** if the player has opened an item view through a proximity prompt, the merch booth will automatically close when the player moves further away from the prompt object than its activation distance. | true | ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local MerchBooth = require(ReplicatedStorage.MerchBooth) MerchBooth.configure({ backgroundColor = Color3.fromRGB(255, 255, 255), textSize = 16, textFont = Enum.Font.Roboto, textColor = Color3.fromRGB(20, 20, 20), hideOtherUis = false, }) ``` #### addItemAsync _ addItemAsync(assetId: `number`, productType: `Enum.InfoType`, hideFromCatalog: `boolean`)_ Asynchronously adds an item to the merch booth so that it's eligible for purchase in the experience. `assetId` is the item's asset ID, `productType` is the item's `Enum.InfoType` enum, and `hideFromCatalog` can be used to hide the item in the catalog view. See [Adding Items](#add-items) for details, as usage varies slightly for **assets** versus **game passes** or **developer products**. > **Warning:** This function can only be called from a `Class.Script` and it performs an asynchronous network call that may occasionally fail. As shown below, it's recommended that it be wrapped in `Global.LuaGlobals.pcall()` to catch and handle errors. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local MerchBooth = require(ReplicatedStorage.MerchBooth) local items = { 607702162, -- Roblox Baseball Cap 4819740796, -- Robox 1374269, -- Kitty Ears 11884330, -- Nerd Glasses 10476359, -- Paper Hat } for _, assetId in items do local success, errorMessage = pcall(function() MerchBooth.addItemAsync(assetId) end) if not success then warn(errorMessage) end end ``` ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local MerchBooth = require(ReplicatedStorage.MerchBooth) local items = { 4343758, -- ColdFyre Armor 28521575, -- Slime Shield } for _, assetId in items do local success, errorMessage = pcall(function() MerchBooth.addItemAsync(assetId, Enum.InfoType.GamePass) end) if not success then warn(errorMessage) end end ``` ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local MerchBooth = require(ReplicatedStorage.MerchBooth) local items = { 1236602053, -- Mana Refill 1257880672, -- Healing Potion } for _, assetId in items do local success, errorMessage = pcall(function() MerchBooth.addItemAsync(assetId, Enum.InfoType.Product) end) if not success then warn(errorMessage) end end ``` #### getItems _ getItems(): `Library.table`_ Returns a dictionary representing all of the currently registered items. Each key is an item's asset ID as a string, and each key's value is an [Item](#item). This function can only be called from a `Class.Script`. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local MerchBooth = require(ReplicatedStorage.MerchBooth) local success, errorMessage = pcall(function() MerchBooth.addItemAsync(4819740796) end) if success then local items = MerchBooth.getItems() print(items) end ``` #### removeItem _ removeItem(assetId: `number`)_ Unregisters an item previously added with [addItemAsync](#additemasync), removing its tile in the catalog view and any [proximity prompts](#proximity-prompts) assigned to it. This function can only be called from a `Class.Script`. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local MerchBooth = require(ReplicatedStorage.MerchBooth) local success, errorMessage = pcall(function() MerchBooth.addItemAsync(4819740796) end) if success then -- After some time, remove the item task.wait(5) MerchBooth.removeItem(4819740796) end ``` #### addProximityButton _ addProximityButton(adornee: `Class.BasePart`|`Class.Model`|`Class.Attachment|Attachment`, assetId: `number`)_ Adds a [proximity prompt](#proximity-prompts) over the given `adornee` that will trigger the display of an item's purchase view, given its asset ID. This can be used as an alternative to the 2D catalog view, encouraging players to discover items in the 3D environment. Note that an item must be added via [addItemAsync](#additemasync) before a proximity button can be assigned to it. See also [removeProximityButton](#removeproximitybutton) to remove the proximity prompt from an object. > **Info:** When implementing proximity prompts, you may want to [remove the catalog button](#togglecatalogbutton) to eliminate its usage in opening the booth. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local Workspace = game:GetService("Workspace") local MerchBooth = require(ReplicatedStorage.MerchBooth) local success, errorMessage = pcall(function() MerchBooth.addItemAsync(4819740796) end) if success then local item = Workspace:FindFirstChild("Robox") if item then MerchBooth.addProximityButton(item, 4819740796) end end ``` #### removeProximityButton _ removeProximityButton(adornee: `Class.BasePart`|`Class.Model`|`Class.Attachment|Attachment`)_ Removes a [proximity prompt](#proximity-prompts) generated through [addProximityButton](#addproximitybutton). This function can only be called from a `Class.Script`. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local Workspace = game:GetService("Workspace") local MerchBooth = require(ReplicatedStorage.MerchBooth) local success, errorMessage = pcall(function() MerchBooth.addItemAsync(4819740796) end) if success then local item = Workspace:FindFirstChild("Robox") if item then MerchBooth.addProximityButton(item, 4819740796) end -- After some time, remove the prompt task.wait(5) MerchBooth.removeProximityButton(item) end ``` #### setCatalogSort _ setCatalogSort(sortFunction: `function`): `boolean`_ Sets the sorting function `sortFunction` to be used in the catalog view. The provided sorting function can use logic based on [Item](#item) info such as `price` or `title`. This function can only be called from a `Class.LocalScript`. Here are some examples for sorting the catalog: ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local MerchBooth = require(ReplicatedStorage.MerchBooth) MerchBooth.setCatalogSort(function(a, b) return a.price < b.price end) ``` ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local MerchBooth = require(ReplicatedStorage.MerchBooth) MerchBooth.setCatalogSort(function(a, b) return a.price > b.price end) ``` ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local MerchBooth = require(ReplicatedStorage.MerchBooth) MerchBooth.setCatalogSort(function(a, b) return if a.price == b.price then a.title < b.title else a.price < b.price end) ``` #### setControlKeyCodes _ setControlKeyCodes(control: [MerchBooth.Controls](#merchboothcontrols), keyCodes: `Library.table`)_ Configures the key and button values for interactions with the merch booth. The first parameter must be a [MerchBooth.Controls](#merchboothcontrols) enum and the second parameter a table containing the keys `keyboard` and/or `gamepad` with corresponding `Enum.KeyCode` enums. | Enum (`control`) | Default `keyCodes` Keys/Values | | --- | --- | | `MerchBooth.Controls.ProximityPrompts` | `keyboard = Enum.KeyCode.E`
`gamepad = Enum.KeyCode.ButtonY` | | `MerchBooth.Controls.OpenMerchBooth` | `gamepad = Enum.KeyCode.ButtonY` | | `MerchBooth.Controls.CloseMerchBooth` | `gamepad = Enum.KeyCode.ButtonB` | | `MerchBooth.Controls.Filter` | `gamepad = Enum.KeyCode.ButtonX` | | `MerchBooth.Controls.ViewItem` | `gamepad = Enum.KeyCode.ButtonA` | ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local MerchBooth = require(ReplicatedStorage.MerchBooth) MerchBooth.setControlKeyCodes(MerchBooth.Controls.ProximityPrompts, { keyboard = Enum.KeyCode.Q, gamepad = Enum.KeyCode.ButtonL1, }) ``` #### openMerchBooth _ openMerchBooth()_ Opens the merch booth window (if closed) and navigates to the catalog view. This function can only be called from a `Class.LocalScript`. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local MerchBooth = require(ReplicatedStorage.MerchBooth) local success, errorMessage = pcall(function() MerchBooth.addItemAsync(assetId) end) if not success then warn(errorMessage) end MerchBooth.openMerchBooth() ``` #### openItemView _ openItemView(itemId: `number`)_ Navigates to the single item view of the given `itemId`, opening the merch booth window if it is currently closed. This function can only be called from a `Class.LocalScript`. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local MerchBooth = require(ReplicatedStorage.MerchBooth) local success, errorMessage = pcall(function() MerchBooth.addItemAsync(4819740796) end) if success then MerchBooth.openItemView(4819740796) end ``` #### toggleCatalogButton _ toggleCatalogButton(enabled: `boolean`)_ Toggles on/off the catalog button on the right side of the screen. This is useful when implementing a [custom button](#custom-catalog-button) or limiting the merch booth's appearance to [regions](#shoppable-regions) or [proximity prompts](#proximity-prompts). Can only be called from a `Class.LocalScript`. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local MerchBooth = require(ReplicatedStorage.MerchBooth) MerchBooth.toggleCatalogButton(false) ``` #### isMerchBoothOpen _ isMerchBoothOpen(): `Tuple`_ Returns `true` if either the catalog or the item view is open. If the item view is open, the item's asset ID is returned as the second value. This function can only be called from a `Class.LocalScript`. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local MerchBooth = require(ReplicatedStorage.MerchBooth) local success, errorMessage = pcall(function() MerchBooth.addItemAsync(4819740796) end) if success then MerchBooth.openItemView(4819740796) local isOpen, itemId = MerchBooth.isMerchBoothOpen() print(isOpen, itemId) end ``` #### closeMerchBooth _ closeMerchBooth()_ Closes the merch booth window. This function can only be called from a `Class.LocalScript`. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local MerchBooth = require(ReplicatedStorage.MerchBooth) MerchBooth.closeMerchBooth() ``` #### isMerchBoothEnabled _ isMerchBoothEnabled(): `boolean`_ This function may be used in tandem with [setEnabled](#setenabled) to check whether the merch booth is currently enabled or not. Can only be called from a `Class.LocalScript`. #### setEnabled _ setEnabled(enabled: `boolean`)_ Sets whether the entire merch booth is enabled or not. When disabled, this function removes the entire UI, including [proximity prompts](#proximity-prompts), and disconnects all [events](#events). This function can only be called from a `Class.LocalScript`. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local MerchBooth = require(ReplicatedStorage.MerchBooth) local isEnabled = MerchBooth.isMerchBoothEnabled() if isEnabled then MerchBooth.setEnabled(false) end ``` ### Events #### itemAdded Fires when an item is added through [addItemAsync](#additemasync). This event can only be connected in a `Class.Script`. | Parameters | | --- | | assetId: `number` | Item asset ID. | | itemInfo: `Library.table` | Dictionary of [Item](#item) info such as `price` or `title`. | ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local MerchBooth = require(ReplicatedStorage.MerchBooth) MerchBooth.itemAdded:Connect(function(assetId, itemInfo) print("Item added with asset ID of", assetId) print(itemInfo) end) ``` #### itemRemoved Fires when an item is removed through [removeItem](#removeitem). This event can only be connected in a `Class.Script`. | Parameters | | --- | | assetId: `number` | Item asset ID. | ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local MerchBooth = require(ReplicatedStorage.MerchBooth) MerchBooth.itemRemoved:Connect(function(assetId) print("Item removed with asset ID of", assetId) end) ``` #### merchBoothOpened Fires when either the catalog **or** item detail view are opened. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local MerchBooth = require(ReplicatedStorage.MerchBooth) MerchBooth.merchBoothOpened:Connect(function() print("Booth view opened") end) ``` #### merchBoothClosed Fires when either the catalog **or** item detail view are closed. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local MerchBooth = require(ReplicatedStorage.MerchBooth) MerchBooth.merchBoothClosed:Connect(function() print("Booth view closed") end) ``` #### catalogViewOpened Fires when the catalog view is opened. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local MerchBooth = require(ReplicatedStorage.MerchBooth) MerchBooth.catalogViewOpened:Connect(function() print("Catalog view opened") end) ``` #### catalogViewClosed Fires when the catalog view is closed. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local MerchBooth = require(ReplicatedStorage.MerchBooth) MerchBooth.catalogViewClosed:Connect(function() print("Catalog view closed") end) ``` #### itemViewOpened Fires when the item detail view is opened. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local MerchBooth = require(ReplicatedStorage.MerchBooth) MerchBooth.itemViewOpened:Connect(function() print("Item view opened") end) ``` #### itemViewClosed Fires when the item detail view is closed. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local MerchBooth = require(ReplicatedStorage.MerchBooth) MerchBooth.itemViewClosed:Connect(function() print("Item view closed") end) ```
--- title: "Photo Booth" url: /docs/en-us/resources/modules/photo-booth last_updated: 2026-06-29T19:34:10Z description: "The Photo Booth module lets players strike a unique pose with a background." --- # Photo Booth Taking a photo is a perfect way to commemorate a great experience. The **PhotoBooth** [developer module](/docs/en-us/resources/modules.md) is an easy-to-use photo staging tool which lets the players strike a unique pose with a background that represents their experience. ## Module usage ### Installation To use the **PhotoBooth** module in an experience: 1. From Studio's **Window** menu or **Home** tab toolbar, open the [Toolbox](/docs/en-us/projects/assets/toolbox.md) and select the **Creator Store** tab. 2. Make sure the **Models** sorting is selected, then click the **See All** button for **Categories**. 3. Locate and click the **Packages** tile. 4. Locate the **Photo Booth** module and click it, or drag-and-drop it into the 3D view. 5. In the [Explorer](/docs/en-us/studio/explorer.md) window, move the entire **PhotoBooth** model into `Class.ReplicatedStorage`. Upon running the experience the module will begin running. ### Position the booth The module comes with one **PhotoBooth** model that you can position in the 3D world. This model is what players will interact with to set up a photo. 1. Locate the **PhotoBooth** mesh inside the **Workspace** folder of the module's main folder. 2. Move it into the top-level **Workspace** hierarchy and position it where desired. > **Info:** It is not required that you use the packaged mesh for triggering photo printouts. The module uses `Class.CollectionService` to locate parts tagged as `PhotoBooth` and make them operate as photo booths. If desired, the `Class.CollectionService` tag name to search for can be changed by setting a different value for `photoboothTag` in a [configure](#configure) call. ### Configuration The module is preconfigured to work for most use cases, but it can be easily customized through the [configure](#configure) function. For example, to change the default message at the bottom of the photo: 1. In **StarterPlayerScripts**, create a new `Class.LocalScript` and rename it to **ConfigurePhotoBooth**. 2. Paste the following code into the new script.```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local PhotoBooth = require(ReplicatedStorage.PhotoBooth) PhotoBooth.configure({ frameMessage = "First Photo Booth Capture!", }) ``` ### Connect to events Every time the photo booth displays a new screen to a local client, a corresponding event is fired. These events can be connected in a `Class.LocalScript` so that you can respond with your own custom logic. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local PhotoBooth = require(ReplicatedStorage.PhotoBooth) PhotoBooth.countdownStarted:Connect(function() print("The countdown has started") end) PhotoBooth.printoutShown:Connect(function() print("The printout is showing") end) PhotoBooth.promptShown:Connect(function() print("The camera prompt is showing") end) ``` ### GUI visibility By default, the photo booth hides all `Class.ScreenGui|ScreenGuis` and `Class.CoreGui|CoreGuis` when a photo is staged. If you want to override this auto-hiding behavior and programmatically decide which GUIs should remain visible, include the [hideOtherGuis](#hideotherguis) and [showOtherGuis](#showotherguis) callbacks and respond with your own custom logic. ```lua local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") local StarterGui = game:GetService("StarterGui") local PhotoBooth = require(ReplicatedStorage.PhotoBooth) local player = Players.LocalPlayer local playerGui = player:WaitForChild("PlayerGui") local hiddenInstances = {} -- Create a screen GUI that will not be hidden local specialGuiInstance = Instance.new("ScreenGui") -- Draw the screen GUI above the photo booth GUI specialGuiInstance.DisplayOrder = 1 specialGuiInstance.Parent = playerGui -- Set attribute on screen GUI to prevent hiding specialGuiInstance:SetAttribute("ShowInPhotoBooth", true) -- Add text label to the GUI local specialLabel = Instance.new("TextLabel") specialLabel.Size = UDim2.fromScale(1, 0.1) specialLabel.Text = "Remains visible when taking a photo" specialLabel.Font = Enum.Font.GothamMedium specialLabel.TextSize = 24 specialLabel.Parent = specialGuiInstance PhotoBooth.hideOtherGuis(function() -- Hide all developer-defined screen GUIs except those marked with attribute local instances = playerGui:GetChildren() for _, instance in instances do if instance:IsA("ScreenGui") and not instance:GetAttribute("ShowInPhotoBooth") and instance.Enabled then instance.Enabled = false table.insert(hiddenInstances, instance) end end -- Hide specific core GUIs StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType.PlayerList, false) end) PhotoBooth.showOtherGuis(function() -- Show all developer-defined screen GUIs that were hidden for _, instance in hiddenInstances do instance.Enabled = true end hiddenInstances = {} -- Show specific core GUIs that were hidden StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType.PlayerList, true) end) ``` ## API reference ### Functions #### configure _ configure(config: `Library.table`)_ Overrides default configuration options through the following keys/values in the `config` table. This function can only be called from a `Class.LocalScript`. #### Appearance | Key | Description | Default | | --- | --- | --- | | `frameMessage` | Message that is shown at the bottom of the photo. Its duration can be controlled via the `fadeUiDelay` property. | "Use your device to take a screenshot and share!" | | `fadeUiDelay` | Time to show the frame message before it fades out, in seconds. Set to a negative number to never fade. | 3 | | `closeButtonImage` | Image to use for the close photo button, placed overtop the `closeButtonBackgroundImage` image. | "rbxassetid://7027440823" | | `closeButtonBackgroundImage` | Background image to use for the close photo button. | "rbxassetid://7027440891" | | `cameraLandscapePosition` | Distance of the photo booth's camera, in front and upward from the character, when taking a photo in landscape mode (`Datatype.Vector2`). | (5, 2) | | `cameraPortraitPosition` | Distance of the photo booth's camera, in front and upward from the character, when taking a photo in portrait mode (`Datatype.Vector2`). | (10, 1) | | `countdownFont` | Font to use for the numbers in the countdown (`Enum.Font`). | `Enum.Font\|GothamBlack` | | `countdownTextColor` | Color of the numbers in the countdown (`Datatype.Color3`). | [255, 255, 255] | | `printoutCharacterPosition` | Position of the character on the screen when the printout is showing (`Datatype.UDim2`). | (0.5, 0, 0.5, 0) | | `printoutCharacterSize` | Amount of screen space the character takes up in the printout (`Datatype.UDim2`). | (1, 0, 1, 0) | | `characterAnimation` | Asset ID of the animation the character takes in the photo, paused at its starting frame. | "rbxassetid://6899663224" | | `filterImage` | Image to apply over the photo as a filter. If `nil`, a default filter that darkens the image edges will be used. | `nil` | #### Behavior | Key | Description | Default | | --- | --- | --- | | `maxActivationDistance` | Maximum distance, in studs, a player's character can be from the photo booth for the prompt to appear. | 10 | | `countdownBeepSound` | Asset ID of the `Class.Sound` to play for each number shown in the countdown. | "rbxassetid://7743999789" | | `countdownFlashSound` | Asset ID for the `Class.Sound` to play when the flash effect is shown. | "rbxassetid://7744000850" | | `countdownSeconds` | Number of seconds to count down for. | 3 | | `photoboothTag` | Tag used by `Class.CollectionService` to find all "booths" in the place. | "PhotoBooth" | ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local PhotoBooth = require(ReplicatedStorage.PhotoBooth) PhotoBooth.configure({ frameMessage = "What a cool pose!", fadeUiDelay = 5, maxActivationDistance = 5, printoutCharacterSize = UDim2.fromScale(1.5, 1.5), }) ``` #### setBackgrounds _ setBackgrounds(backgrounds: `Library.table`)_ Overrides the default backgrounds provided by the photo booth. Background images should be at 16:9 aspect ratio (1024×576) for an optimal experience and their asset IDs should be included in the backgrounds array. 1–4 (inclusive) backgrounds can be provided. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local PhotoBooth = require(ReplicatedStorage.PhotoBooth) PhotoBooth.setBackgrounds({ "rbxassetid://7018713114", "rbxassetid://950538356", }) ``` ### Events #### countdownStarted Fires when the countdown starts. This event can only be connected in a `Class.LocalScript`. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local PhotoBooth = require(ReplicatedStorage.PhotoBooth) PhotoBooth.countdownStarted:Connect(function() print("The countdown has started") end) ``` #### printoutShown Fires when the printout is shown to the user. This event can only be connected in a `Class.LocalScript`. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local PhotoBooth = require(ReplicatedStorage.PhotoBooth) PhotoBooth.printoutShown:Connect(function() print("The printout is showing") end) ``` #### promptShown Fires when the printout is closed and the camera button is showing again. This event can only be connected in a `Class.LocalScript`. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local PhotoBooth = require(ReplicatedStorage.PhotoBooth) PhotoBooth.promptShown:Connect(function() print("The camera prompt is showing") end) ``` ### Callbacks #### hideOtherGuis _ hideOtherGuis(callback: `function`)_ This callback runs immediately before the printout is displayed, letting you disable entire `Class.ScreenGui|ScreenGuis` or elements within them before the printout is shown. GUIs used by the photo booth have the attribute **ShowInPhotoBooth** set to **true**. See [GUI Visibility](#gui-visibility) for details and sample code. #### showOtherGuis _ showOtherGuis(callback: `function`)_ This callback runs after the printout has been closed, letting you reenable entire `Class.ScreenGui|ScreenGuis` or elements within them. GUIs used by the photo booth have the attribute **ShowInPhotoBooth** set to **true**. See [GUI Visibility](#gui-visibility) for details and sample code. --- title: "Profile Card" url: /docs/en-us/resources/modules/profile-card last_updated: 2026-06-29T19:34:10Z description: "The Profile Card module lets players see more information about other players in an experience." --- # Profile Card It can be interesting to learn about other players. The **ProfileCard** [developer module](/docs/en-us/resources/modules.md) is a great way to see more information about others within an experience, from badges achieved to the player's favorite experiences. ## Module usage ### Installation To use the **ProfileCard** module in an experience: 1. From Studio's **Window** menu or **Home** tab toolbar, open the [Toolbox](/docs/en-us/projects/assets/toolbox.md) and select the **Creator Store** tab. 2. Make sure the **Models** sorting is selected, then click the **See All** button for **Categories**. 3. Locate and click the **Packages** tile. 4. Locate the **Profile Card** module and click it, or drag-and-drop it into the 3D view. 5. In the [Explorer](/docs/en-us/studio/explorer.md) window, move the entire **ProfileCard** model into `Class.ReplicatedStorage`. Upon running the experience the module will begin running. ### Views Profile cards have different views depending on whether you're viewing your own card or another player's card. #### Your Card When you first spawn into the experience, an icon appears over your character. Clicking the icon opens the profile card view. Once the card is closed, the icon disappears, but you can reopen the card at any time by clicking your character. When viewing your own card, it appears as it would to another player, except placeholder text will appear for any blank inputs. Additionally, the status string is subject to **text filtering**, as any free text input should be. _Card without custom status_ _Card with custom status_ _Entering a custom status_ _Attempt to enter invalid status_ #### Other Player's Card Icons do not appear over other characters, but clicking on a character will open their profile card. The button in the upper-right corner of the card lets you quickly request that player as a friend. Note that players under the age of 13 will only see the status message if it's appropriate for their age group. _Card with custom status_ _Card without custom status_ ## API reference ### Functions #### configure _ configure(config: `Library.table`)_ Overrides default configuration options through the following keys/values in the `config` table. This function should be called from a `Class.LocalScript` within **StarterPlayerScripts**. | Key | Description | Default | | --- | --- | --- | | `alwaysOnTop` | If `true`, shows locator icons on top of everything, preventing them from being blocked by 3D world objects. | true | | `showPersonalIndicator` | If `true`, shows the personal indicator when a player first joins the experience. | true | | `showBlur` | If `true`, shows the blurry screen background when a player enters edit mode. | true | | `maxClickDistance` | Maximum distance from the camera viewpoint a card will appear when a character is clicked, measured in studs. | 100 | | `backgroundColor` | Background color for the card (`Datatype.Color3`). | [228, 255, 255] | | `backgroundTransparency` | Transparency of the card's `backgroundColor`. | 0.2 | | `isScaled` | If `true`, automatically scales the size of the text to fill the height of the space. | false | | `isTruncated` | If `true`, automatically hides the ends of strings that would otherwise be too long for proper viewing. | true | | `hasRoundedCorners` | If `true`, the card's corners will be rounded. | false | | `cornerRadiusValue` | Value of the `Class.UICorner` corner radius, if `hasRoundedCorners` is `true`. | 20 | | `hasBorder` | If `true`, shows a border for the card. | false | | `borderColor` | Color of the card's border (`Datatype.Color3`). Only applies if `hasBorder` is `true`. | [228, 255, 255] | | `borderThickness` | Thickness of the card's border, if `hasBorder` is `true`. | 3 | | `borderTransparency` | Transparency of the card's border, if `hasBorder` is `true`. | 0 | | `borderLineJoinMode` | Corner style of the card's border (`Enum.LineJoinMode`). Only applies if `hasBorder` is `true`. | `Enum.LineJoinMode\|Round` | | `headerFontSize` | Font size for the card's header. | 18 | | `headerFontType` | Font type for the card's header (`Enum.Font`). | `Enum.Font\|GothamBlack` | | `textFontSize` | Font size for the card's body text. | 15 | | `textFontType` | Font type for the card's body text (`Enum.Font`). | `Enum.Font\|GothamMedium` | ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local ProfileCard = require(ReplicatedStorage.ProfileCard) ProfileCard.configure({ alwaysOnTop = true, maxClickDistance = 50, backgroundColor = Color3.fromRGB(0, 0, 0), backgroundTransparency = 0.4 }) ``` --- title: "Scavenger Hunt" url: /docs/en-us/resources/modules/scavenger-hunt last_updated: 2026-06-29T19:34:10Z description: "The Scavenger Hunt module gives players an inherently gamified way to explore an experience." --- # Scavenger Hunt The **ScavengerHunt** [developer module](/docs/en-us/resources/modules.md) gives players an inherently gamified way to explore your experience, organically introducing them to the entire place. Player progress is persistent, so scavenger hunts can continue across sessions. > **Warning:** This module utilizes [data stores](/docs/en-us/cloud-services/data-stores.md). To test it in Studio, make sure **Enable Studio Access to API Services** is enabled from the **Security** section of Studio's **File** ⟩ **Experience Settings** window. ## Module usage ### Installation To use the **ScavengerHunt** module in an experience: 1. From Studio's **Window** menu or **Home** tab toolbar, open the [Toolbox](/docs/en-us/projects/assets/toolbox.md) and select the **Creator Store** tab. 2. Make sure the **Models** sorting is selected, then click the **See All** button for **Categories**. 3. Locate and click the **Packages** tile. 4. Locate the **Scavenger Hunt** module and click it, or drag-and-drop it into the 3D view. 5. In the [Explorer](/docs/en-us/studio/explorer.md) window, move the entire **ScavengerHunt** model into `Class.ReplicatedStorage`. Upon running the experience the module will begin running. ### Use tokens The scavenger hunt module uses **tokens** as the items which players search for and collect. The module comes with one token model that you can position in the 3D world. 1. Locate the **Token1** mesh inside the **Workspace** folder of the module's main folder. 2. Move **Token1** into the top-level **Workspace** hierarchy and position it where desired. 3. Give the token a **unique name**; this name is how the module tracks which tokens each player has collected. 4. To add more tokens, duplicate an existing token and give it a unique name. If you don't want to use the bundled mesh tokens, any `Class.Model` or `Class.BasePart` will work, as long as it meets the following criteria: - Object has a `Class.CollectionService` tag of `ScavengerHuntPart`. If desired, the `Class.CollectionService` tag name which the module utilizes can be changed by setting a different value for `tokenTag` in a [configureServer](#configureserver) call. - Object contains a child `Class.StringValue` instance set to the "flavor text" to display when the token is collected._Model__MeshPart_ > **Error:** Remember that each token must have a unique name as a means of tracking player progress. > **Info:** The module will automatically disable the `Class.BasePart.CanCollide|CanCollide` property of tokens at runtime so that players do not physically collide with them. As such, all tokens should be **anchored** so they do not fall through the world geometry. ### Use regions Regions differ slightly from tokens, as large areas that are marked as "collected" once the player enters them. Additionally, when a player leaves the region, the flavor text modal automatically dismisses and the region itself is removed from the workspace. 1. Create an anchored part around the region, such as a block or sphere. The module will automatically disable the `Class.BasePart.CanCollide|CanCollide` property on runtime so players do not physically collide with the region. 2. Give it a **unique name**. This name is how the module tracks which regions each player has entered. 3. Using the [Tags](/docs/en-us/studio/properties.md#instance-tags) section of the part's properties, apply the tag `ScavengerHuntPart` to the part so that `Class.CollectionService` detects it. If desired, the tag name which the module utilizes can be changed by setting a different value for `tokenTag` in a [configureServer](#configureserver) call. 4. Include a child `Class.StringValue` instance set to the "flavor text" to display when the region is entered. > **Error:** Remember that each region must have a unique name as a means of tracking player progress. ### Configuration The module is preconfigured to work for most use cases, but it can be easily customized. For example, to change the token rotation speed and customize the modal info message: 1. In **StarterPlayerScripts**, create a new `Class.LocalScript` and rename it to **ConfigureScavengerHunt**. 2. Paste the following code into the new script.```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local ScavengerHunt = require(ReplicatedStorage.ScavengerHunt) ScavengerHunt.configureClient({ infoModalText = "Welcome to my Scavenger Hunt!", completeModalText = "Thanks for playing my Scavenger Hunt!", tokenRotationSpeed = 60, }) ``` ### Collection events Every time a player collects a token or enters a region, the [collected](#collected) event fires. You can listen to this event from a server-side `Class.Script` and respond accordingly. The connected function receives the `Class.Player` that collided with the token or entered the region and that token or region's name. Similarly, when a player collects **all** tokens or enters **all** tagged regions, the [allCollected](#allcollected) event fires and the connected function receives the associated `Class.Player`. This function is only fired once per player and it can be used to reward that player with a [badge](/docs/en-us/production/publishing/badges.md), access to a new area, [in-experience currency](/docs/en-us/production/monetization/developer-products.md), etc. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local ScavengerHunt = require(ReplicatedStorage.ScavengerHunt) ScavengerHunt.collected:Connect(function(player, itemName) print(player.DisplayName, itemName) end) ScavengerHunt.allCollected:Connect(function(player) print(player.DisplayName .. " completed the hunt!") end) ``` ### Custom GUI This module exposes several options to customize its default GUI, but you can opt to display custom GUI elements instead. When `useCustomModals` is set to `true` in the [configureClient](#configureclient) function, the [showInfoModal](#showinfomodal) event fires every time the player activates the token tracker. Similarly, the [showCompleteModal](#showcompletemodal) event fires when the player has collected everything in the scavenger hunt. Both of these events can be listened to in a `Class.LocalScript`. ```lua local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") local ScavengerHunt = require(ReplicatedStorage.ScavengerHunt) ScavengerHunt.showInfoModal:Connect(function() -- Show a custom info modal local infoModal = Players.LocalPlayer.PlayerGui.ScavengerInfoModal infoModal.Enabled = true end) ScavengerHunt.showCompleteModal:Connect(function() -- Show a custom complete modal local completeModal = Players.LocalPlayer.PlayerGui.ScavengerCompleteModal completeModal.Enabled = true end) ``` > **Warning:** When using custom modals, be sure to provide a way for players to close/hide them, or an automatic dismissal after a delay. ### GUI visibility By default, the scavenger hunt hides all `Class.ScreenGui|ScreenGuis` and `Class.CoreGui|CoreGuis` (except for the player list) when the info modal or completion modal appears. If you want to override this auto-hiding behavior and programmatically decide which GUIs should remain visible, include the [hideOtherGuis](#hideotherguis) and [showOtherGuis](#showotherguis) callbacks and respond with your own custom logic. ```lua local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") local StarterGui = game:GetService("StarterGui") local ScavengerHunt = require(ReplicatedStorage.ScavengerHunt) local player = Players.LocalPlayer local playerGui = player:WaitForChild("PlayerGui") local hiddenInstances = {} -- Create a screen GUI that will not be hidden local specialGuiInstance = Instance.new("ScreenGui") -- Draw the screen GUI above the scavenger hunt GUI specialGuiInstance.DisplayOrder = 1 specialGuiInstance.Parent = playerGui -- Add text label to the GUI local specialLabel = Instance.new("TextLabel") specialLabel.Size = UDim2.fromScale(1, 0.1) specialLabel.Text = "Remains visible when displaying modals" specialLabel.Font = Enum.Font.GothamMedium specialLabel.TextSize = 24 specialLabel.Parent = specialGuiInstance ScavengerHunt.hideOtherGuis(function() -- Hide all developer-defined screen GUIs local instances = playerGui:GetChildren() for _, instance in instances do if instance:IsA("ScreenGui") and not instance.Name == "ScavengerHunt" and instance.Enabled then instance.Enabled = false table.insert(hiddenInstances, instance) end end -- Hide specific core GUIs StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType.PlayerList, false) end) ScavengerHunt.showOtherGuis(function() -- Show all developer-defined screen GUIs that were hidden for _, instance in hiddenInstances do instance.Enabled = true end hiddenInstances = {} -- Show specific core GUIs that were hidden StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType.PlayerList, true) end) ``` ## API reference ### Functions #### configureClient _ configureClient(config: `Library.table`)_ Overrides default client-side configuration options through the following keys/values in the `config` table. This function can only be called from a `Class.LocalScript`. #### General | Key | Description | Default | | --- | --- | --- | | `autoDismissTime` | Time in seconds before the modal automatically dismisses itself or navigates to the next page if there is one. Set to 0 to disable. | 20 | | `closeModalGamepad` | Gamepad button used to close modals (`Enum.KeyCode`). | `Enum.KeyCode\|ButtonA` | | `closeModalKeyboard` | Keyboard key used to close modals (`Enum.KeyCode`). | `Enum.KeyCode\|E` | | `completeModalText` | Text to show on the modal that appears after clicking the token tracker when the scavenger hunt is complete. | "Thanks for participating!" | | `infoModalText` | Text to show on the modal that appears after clicking the token tracker. | "Find all the tokens to complete the hunt" | | `tokenRotationSpeed` | Speed at which the tokens rotate, in degrees per second. Set to 0 to prevent rotation. | 20 | | `nextArrowImage` | Image used to indicate there are more modal pages to show after the current modal page. | "rbxassetid://8167172095" | | `openTokenTrackerGamepad` | Gamepad button used to show the modals that appear after activating the token tracker (`Enum.KeyCode`). | `Enum.KeyCode\|ButtonY` | | `openTokenTrackerKeyboard` | Keyboard key used to show the modals that appear after activating the token tracker (`Enum.KeyCode`). | `Enum.KeyCode\|Y` | | `openTokenTrackerGamepadButtonImage` | Image for the gamepad button that is used to activate the token tracker. | "rbxassetid://8025860488" | | `regionIcon` | Icon to display next to the token tracker when entering regions. | "rbxassetid://8073794624" | | `tokenIcon` | Icon to display next to the token tracker when collecting tokens. | "rbxassetid://8073794477" | | `tokenTrackerPositionSmallDevice` | Position of the token tracker UI on small devices such as phones (`Datatype.UDim2`). | (1, 0, 0, 84) | | `tokenTrackerPositionLargeDevice` | Position of the token tracker UI on larger devices like tablets and PC (`Datatype.UDim2`). | (1, 0, 1, -16) | | `useRegions` | Instead of [tokens](#use-tokens), use [regions](#use-regions). | false | #### Modals | Key | Description | Default | | --- | --- | --- | | `modal.backgroundColor` | Background color of the modals (`Datatype.Color3`). | [0, 0, 0] | | `modal.font` | Font of the text that appears in a modal (`Enum.Font`). | `Enum.Font\|GothamMedium` | | `modal.textColor` | Color of the text that appears in a modal (`Datatype.Color3`). | [255, 255, 255] | | `modal.textSize` | Size of the text that appears in a modal. | 16 | | `useCustomModals` | If true, default modals are not displayed. This lets you show custom modals as outlined in [Custom GUI](#custom-gui). | false | | `useCustomTokenTracker` | If true, the default token tracker is not displayed. This lets you show a custom token tracker GUI instead. | false | #### Navigation Beam | Key | Description | Default | | --- | --- | --- | | `showNavigationBeam` | If true, a `Class.Beam` from the player to the nearest token will be shown. | true | | `navigationBeam.color` | `Datatype.ColorSequence` defining the beam's color across its segments. See `Class.Beam.Color` for details. | [255, 255, 255] → [255, 255, 255] | | `navigationBeam.curveSize0` | Position of the first control point in the beam's Bézier curve. See `Class.Beam.CurveSize0` for more info. | 0 | | `navigationBeam.curveSize1` | Position of the second control point in the beam's Bézier curve. See `Class.Beam.CurveSize1` for more info. | 0 | | `navigationBeam.faceCamera` | Whether the beam's segments will always face the camera regardless of its orientation. See `Class.Beam.FaceCamera` for details. | true | | `navigationBeam.lightEmission` | Degree to which the colors of the beam are blended with the colors behind it. See `Class.Beam.LightEmission` for details. | 0 | | `navigationBeam.lightInfluence` | Degree to which the beam is influenced by the environment's lighting. See `Class.Beam.LightInfluence` for details. | 0 | | `navigationBeam.segments` | How many straight segments the beam is made up of. | 10 | | `navigationBeam.texture` | Asset ID of the texture to be displayed on the beam. | "rbxassetid://8081777495" | | `navigationBeam.textureLength` | Length of the beam's texture, depending on the setting for `navigationBeam.textureMode`. See `Class.Beam.TextureLength` for details. | 1 | | `navigationBeam.textureMode` | Manner in which the beam texture scales and repeats (`Enum.TextureMode`). | `Enum.TextureMode\|Wrap` | | `navigationBeam.textureSpeed` | Speed at which the texture image moves along the beam. | 1 | | `navigationBeam.transparency` | `Datatype.NumberSequence` defining the beam's transparency across its segments. See `Class.Beam.Transparency` for details. | (0, 0) → (0.15, 1) → (1, 1) | | `navigationBeam.width0` | Width of the beam at its base, in studs. | 1 | | `navigationBeam.width1` | Width of the beam at its end, in studs. | 1 | | `navigationBeam.zOffset` | Distance, in studs, by which the beam's display is offset, relative to the camera. | 0 | ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local ScavengerHunt = require(ReplicatedStorage.ScavengerHunt) ScavengerHunt.configureClient({ infoModalText = "Welcome to my Scavenger Hunt!", completeModalText = "Thanks for playing my Scavenger Hunt!", tokenRotationSpeed = 60, navigationBeam = { lightEmission = 1 }, modal = { textSize = 14 }, }) ``` #### configureServer _ configureServer(config: `Library.table`)_ Overrides default server-side configuration options through the following keys/values in the `config` table. This function can only be called from a `Class.Script`. | Key | Description | Default | | --- | --- | --- | | `tokenTag` | Tag used by `Class.CollectionService` to find all the tokens or regions used in the scavenger hunt. | "ScavengerHuntPart" | | `datastoreName` | Name of the `Class.DataStore` used by the scavenger hunt to store each player's collection progress. | "ScavengerHuntTokens" | | `resetOnPlayerRemoving` | If true, resets the user's progress when they leave the experience; convenient for not saving progress while testing the scavenger hunt. | false | ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local ScavengerHunt = require(ReplicatedStorage.ScavengerHunt) ScavengerHunt.configureServer({ tokenTag = "GreenGem", }) ``` #### disable _ disable()_ Hides all UI for the scavenger hunt, disconnects all input event listeners, and prevents players from collecting tokens or interacting with regions. This function can only be called from a `Class.Script`. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local ScavengerHunt = require(ReplicatedStorage.ScavengerHunt) ScavengerHunt.disable() ``` #### enable _ enable()_ Shows all UI for the scavenger hunt, connects all input event listeners, and allows players to collect tokens and interact with regions. This function can only be called from a `Class.Script`. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local ScavengerHunt = require(ReplicatedStorage.ScavengerHunt) ScavengerHunt.enable() ``` ### Events #### collected Fires when a player collides with a token or enters a region. The connected function will receive the `Class.Player` that collided with the token or entered the region and the name of the token that was collided into or the region that was entered. This event can only be connected in a `Class.Script`. | Parameters | | --- | | player: `Class.Player` | User who collided with a token or entered a region. | | itemName: `Library.string` | Name of the token that was collided into or the region that was entered. | | totalCollected: `number` | Total number of tokens collected by the user represented by `player`. | ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local ScavengerHunt = require(ReplicatedStorage.ScavengerHunt) ScavengerHunt.collected:Connect(function(player, itemName, totalCollected) print(player.DisplayName, itemName, totalCollected) end) ``` #### allCollected Fires when a player collects all tokens or enters all regions in the scavenger hunt. The connected function will receive the `Class.Player` that collected all tokens, and it is only ever fired once per player. This event can only be connected in a `Class.Script`. | Parameters | | --- | | player: `Class.Player` | Player who collected all tokens or entered all regions. | ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local ScavengerHunt = require(ReplicatedStorage.ScavengerHunt) ScavengerHunt.allCollected:Connect(function(player) print(player.DisplayName .. " completed the hunt!") end) ``` #### showInfoModal Fires when the player clicks on the token tracker when the `useCustomModals` [configuration](#configureclient) option is set to true. This event can only be connected in a `Class.LocalScript`. ```lua local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") local ScavengerHunt = require(ReplicatedStorage.ScavengerHunt) ScavengerHunt.showInfoModal:Connect(function() local infoModal = Players.LocalPlayer.PlayerGui.InfoModal infoModal.Enabled = true end) ``` #### showCompleteModal Fires when the player clicks on the token tracker when the `useCustomModals` [configuration](#configureclient) option is set to `true` and the player has collected all tokens in the scavenger hunt. This event can only be connected in a `Class.LocalScript`. ```lua local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") local ScavengerHunt = require(ReplicatedStorage.ScavengerHunt) ScavengerHunt.showCompleteModal:Connect(function() local completeModal = Players.LocalPlayer.PlayerGui.CompleteModal completeModal.Enabled = true end) ``` ### Callbacks #### hideOtherGuis _ hideOtherGuis(callback: `function`)_ This callback runs immediately before a modal is displayed, letting you disable entire `Class.ScreenGui|ScreenGuis` or elements within them before the modal is shown. See [GUI Visibility](#gui-visibility) for details and sample code. #### showOtherGuis _ showOtherGuis(callback: `function`)_ This callback runs immediately after a modal has been dismissed, letting you enable entire `Class.ScreenGui|ScreenGuis` or elements within them. See [GUI Visibility](#gui-visibility) for details and sample code. --- title: "Selfie Mode" url: /docs/en-us/resources/modules/selfie-mode last_updated: 2026-06-29T19:34:10Z description: "The Selfie Mode module lets players capture screenshots to commemorate fun moments." --- # Selfie Mode Players already take screenshots to commemorate fun moments in experiences. The **SelfieMode** [developer module](/docs/en-us/resources/modules.md) lets players capture a cleaner memory of that moment without the chat window or player list, while also supporting filter effects, hiding of other characters, and posing. ## Module Usage ### Installation To use the **SelfieMode** module in an experience: 1. From Studio's **Window** menu or **Home** tab toolbar, open the [Toolbox](/docs/en-us/projects/assets/toolbox.md) and select the **Creator Store** tab. 2. Make sure the **Models** sorting is selected, then click the **See All** button for **Categories**. 3. Locate and click the **Packages** tile. 4. Locate the **Selfie Mode** module and click it, or drag-and-drop it into the 3D view. 5. In the [Explorer](/docs/en-us/studio/explorer.md) window, move the entire **SelfieMode** package into `Class.ReplicatedStorage`. Upon running the experience the module will begin running. ### Configuration The module is preconfigured to work for most use cases, but you can easily customize it through the [configure](#configure) function. 1. In **StarterPlayerScripts**, create a new `Class.LocalScript` and rename it to **ConfigureSelfieMode**. 2. Paste the following code into the new script.```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local SelfieMode = require(ReplicatedStorage.SelfieMode) SelfieMode.configure({ disableCharacterMovement = true }) ``` ### Character Movement It may be advantageous to prevent the player's character from moving while in selfie mode. You can achieve this by setting `disableCharacterMovement` to true in a [configure](#configure) call. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local SelfieMode = require(ReplicatedStorage.SelfieMode) SelfieMode.configure({ disableCharacterMovement = true }) ``` ### Selfie Mode Actions **SelfieMode** comes with the following [actions](#action), each of which you can use with the [activateAction](#activateaction), [deactivateAction](#deactivateaction), and [toggleAction](#toggleaction) functions, or detect through the [actionActivated](#actionactivated) and [actionDeactivated](#actiondeactivated) events. #### Depth-of-Field By default, **SelfieMode** shows a generic **depth‑of‑field** effect (subtle blur of the background) when a player toggles the action. #### Off #### On > **Warning:** This effect does not render on mobile devices such as phones and tablets. As such, it will not be offered as an action on those devices. > > On non-mobile devices, the player's graphics quality must be eight or higher to render depth‑of‑field. If their graphics quality is less than eight, they'll be prompted to increase it. To change the default depth‑of‑field effect, set `depthOfFieldEffect` to your own `Class.DepthOfFieldEffect` instance in a [configure](#configure) call. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local SelfieMode = require(ReplicatedStorage.SelfieMode) local customDepthOfField = Instance.new("DepthOfFieldEffect") customDepthOfField.NearIntensity = 0 customDepthOfField.FarIntensity = 1 customDepthOfField.FocusDistance = 5 customDepthOfField.InFocusRadius = 5 SelfieMode.configure({ depthOfFieldEffect = customDepthOfField }) ``` #### Lock Gaze The **Lock Gaze** toggle causes the player's character to look at the camera while setting up the selfie pose, within a realistic range of how far their neck can turn. #### Hide Others By default, other characters are visible alongside the player's character. Players can obtain a perfect solo shot by clicking the **Hide Others** button. When toggled on, other characters fade out of view and remain invisible until the action is toggled off. #### Filter The **Filter** action lets the player apply a preset filter from the options **Pop**, **Soft**, **Antique**, **Cute**, **Dramatic**, and **Monochrome**. #### Soft #### Dramatic #### Monochrome #### Pose The **Pose** action lets the player select a preset pose from the options **Cheer**, **Clapping**, **Dolphin**, **Flossing**, **Guitar**, **Jump Wave**, **Louder**, **Top Rock**, **Twirl**, and **Wave**. #### Flossing #### Louder #### Twirl ## API Reference ### Types #### Action Each action is represented by a dictionary with the following key-value pairs: | Key | Type | Description | | --- | --- | --- | | `name` | string | Name of the action, shown first in tooltips. | | `description` | string | Description of the action, shown after **name** in tooltips. | | `icon` | string | Asset ID for the action's icon. | | `activeIcon` | string | Asset ID for the action's icon in "active" state. Can only be used on parent actions, not sub-actions. | | `actions` | table | Optional list of sub-actions. This allows you to create submenus of various other actions. | | `parent` | [Action](#action) | The parent of the action; this only applies to a sub-action and points to the action that contains it. | | `onActivated` | function | Optional callback function that runs when a player activates an action or sub-action. Typically, if an action contains sub-actions, only the sub-actions will need a callback defined as a means of knowing that the player activated the sub-action and did not simply "expand" the parent action. | ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local SelfieMode = require(ReplicatedStorage.SelfieMode) SelfieMode.actionActivated:Connect(function(action) print(action.name, "activated") end) SelfieMode.actionDeactivated:Connect(function(action) print(action.name, "deactivated") end) ``` ### Enums #### SelfieMode.Action **SelfieMode** comes with several [actions](#action). You can use this enum with the [activateAction](#activateaction), [deactivateAction](#deactivateaction), and [toggleAction](#toggleaction) functions. | Name | Summary | | --- | --- | | `DepthOfField` | Reference to the [Depth‑of‑Field](#depth-of-field) action. | | `LockGaze` | Reference to the [Lock Gaze](#lock-gaze) action. | | `HideOthers` | Reference to the [Hide Others](#hide-others) action. | | `Filter` | Reference to the [Filter](#filter) action. | | `Pose` | Reference to the [Pose](#pose) action. | ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local SelfieMode = require(ReplicatedStorage.SelfieMode) -- Activate "Filter" action SelfieMode.activateAction(SelfieMode.Action.Filter) ``` ### Functions #### configure _ configure(config: `Library.table`)_ Overrides default configuration options through the following keys/values in the `config` table. This function can only be called from a `Class.LocalScript`. | Key | Description | Default | | --- | --- | --- | | `disableCharacterMovement` | If true, prevents character from moving while selfie mode is open. | false | | `depthOfFieldEffect` | Optional custom `Class.DepthOfFieldEffect` instance that appears when the player toggles the [Depth of Field](#depth-of-field) action. | | ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local SelfieMode = require(ReplicatedStorage.SelfieMode) SelfieMode.configure({ disableCharacterMovement = true }) ``` #### openSelfieMode _ openSelfieMode()_ A player will typically open selfie mode with the "camera" button on the right side of the screen, but this function lets you open it through code. When implementing a custom button as shown below, you should disable the default button through [setHudButtonEnabled](#sethudbuttonenabled). This function can only be called from a `Class.LocalScript`. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local SelfieMode = require(ReplicatedStorage.SelfieMode) local button = script.Parent -- Remove the default button SelfieMode.setHudButtonEnabled(false) -- Connect the custom button button.Activated:Connect(function() SelfieMode.openSelfieMode() end) ``` #### closeSelfieMode _ closeSelfieMode()_ A player will typically close selfie mode with the **⊗** button at the bottom of the screen, but this function lets you close it through code. Can only be called from a `Class.LocalScript`. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local SelfieMode = require(ReplicatedStorage.SelfieMode) SelfieMode.closeSelfieMode() ``` #### isSelfieModeOpen _ isSelfieModeOpen(): `boolean`_ Returns `true` if selfie mode is open as a result of player action or through [openSelfieMode](#openselfiemode). This function can only be called from a `Class.LocalScript`. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local SelfieMode = require(ReplicatedStorage.SelfieMode) SelfieMode.openSelfieMode() print(SelfieMode.isSelfieModeOpen()) ``` #### setHudButtonEnabled _ setHudButtonEnabled()_ Sets whether the default button to enter selfie mode is shown. Useful when implementing [openSelfieMode](#openselfiemode) through a custom UI button. This function can only be called from a `Class.LocalScript`. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local SelfieMode = require(ReplicatedStorage.SelfieMode) local button = script.Parent -- Remove the default button SelfieMode.setHudButtonEnabled(false) -- Connect the custom button button.Activated:Connect(function() SelfieMode.openSelfieMode() end) ``` #### getAction _ getAction(action: [SelfieMode.Action](#selfiemodeaction)): [Action](#action)_ Gets an [Action](#action) type through a [SelfieMode.Action](#selfiemodeaction) enum. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local SelfieMode = require(ReplicatedStorage.SelfieMode) local lockGazeAction = SelfieMode.getAction(SelfieMode.Action.LockGaze) ``` #### activateAction _ activateAction(action: [SelfieMode.Action](#action))_ Programmatically activates one of the default [actions](#selfie-mode-actions). This is the same as when a player toggles the action on from the action bar. Can only be called from a `Class.LocalScript`. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local SelfieMode = require(ReplicatedStorage.SelfieMode) -- Activate "Filter" action SelfieMode.activateAction(SelfieMode.Action.Filter) ``` #### deactivateAction _ deactivateAction(action: [SelfieMode.Action](#action))_ Programmatically deactivates one of the default [actions](#selfie-mode-actions). This is the same as when a player toggles the action off from the action bar. Can only be called from a `Class.LocalScript`. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local SelfieMode = require(ReplicatedStorage.SelfieMode) -- Deactivate "Filter" action SelfieMode.deactivateAction(SelfieMode.Action.Filter) ``` #### toggleAction _ toggleAction(action: [SelfieMode.Action](#action)): `boolean`_ Toggles an [action](#selfie-mode-actions) on if it's off, or toggles it off if it's on. This is the same as when a player clicks the action from the action bar. Returns the new "is toggled on" state as a boolean. Can only be called from a `Class.LocalScript`. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local SelfieMode = require(ReplicatedStorage.SelfieMode) local lockGazeAction = SelfieMode.getAction(SelfieMode.Action.LockGaze) local isEnabled = SelfieMode.toggleAction(lockGazeAction) if isEnabled then print("Activated", lockGazeAction.name) else print("Deactivated", lockGazeAction.name) end ``` #### setTheme _ setTheme(theme: `Library.table`)_ Configures the selfie mode theme, including text size, font, button/tooltip colors, and more. This function can only be called from a `Class.LocalScript`. #### General | Key | Description | Default | | --- | --- | --- | | `textSize` | Size of all text. | 16 | | `font` | Font used across all UI (`Enum.Font`). | `Enum.Font\|GothamMedium` | | `padding` | Main padding used for laying out UI elements (`Datatype.UDim`). | (0, 12) | | `paddingSmall` | Smaller padding used for applying subtle margins between elements (`Datatype.UDim`). | (0, 6) | | `paddingScreen` | Padding used around the screen edges to give selfie mode some breathing room (`Datatype.UDim`). | (0, 24) | | `backgroundColor` | Background color used for the bar that displays the actions (`Datatype.Color3`). | [0, 0, 0] | | `scrollBarColor` | Color of the scrollbar used in `Class.ScrollingFrame` elements of the module (`Datatype.Color3`). | [255, 255, 255] | #### Buttons and Tooltips | Key | Description | Default | | --- | --- | --- | | `openButtonBackgroundColor` | Background color of the HUD button on the right side of the screen used to open selfie mode (`Datatype.Color3`). | [255, 255, 255] | | `openButtonIconColor` | Color of the HUD button's camera icon (`Datatype.Color3`). | [0, 0, 0] | | `closeButtonBackgroundColor` | Background color of the "close" button (`Datatype.Color3`). | [0, 0, 0] | | `closeButtonIconColor` | Icon color for the "close" button (`Datatype.Color3`). | [255, 255, 255] | | `actionButtonBackgroundColor` | Background color of the various action buttons used for toggling actions (`Datatype.Color3`). | [255, 255, 255] | | `actionButtonIconColor` | Icon color for the various action buttons (`Datatype.Color3`). | [0, 0, 0] | | `tooltipBackgroundColor` | Background color of tooltips and notifications (`Datatype.Color3`). | [0, 0, 0] | | `tooltipNameColor` | Text color of tooltip names (`Datatype.Color3`). Also used as the text color for notifications. | [255, 255, 255] | | `tooltipDescriptionColor` | Color of tooltip descriptions, slightly faded to give focus to tooltip names (`Datatype.Color3`). Not used for notifications. | [169, 169, 169] | ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local SelfieMode = require(ReplicatedStorage.SelfieMode) SelfieMode.setTheme({ textSize = 20, font = Enum.Font.Michroma, backgroundColor = Color3.fromRGB(0, 40, 75), }) ``` #### setEnabled _ setEnabled(isEnabled: `boolean`)_ Sets whether selfie mode is enabled or not. When disabled, all UI for the module is removed and all events are disconnected. This function can only be called from a `Class.LocalScript`. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local SelfieMode = require(ReplicatedStorage.SelfieMode) SelfieMode.setEnabled(false) ``` ### Events #### selfieModeOpened Fires when the player opens selfie mode or when [openSelfieMode](#openselfiemode) is called. This event can only be connected in a `Class.LocalScript`. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local SelfieMode = require(ReplicatedStorage.SelfieMode) SelfieMode.selfieModeOpened:Connect(function() print("Selfie mode open") end) ``` #### selfieModeClosed Fires when the player closes selfie mode or when [closeSelfieMode](#closeselfiemode) is called. This event can only be connected in a `Class.LocalScript`. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local SelfieMode = require(ReplicatedStorage.SelfieMode) SelfieMode.selfieModeClosed:Connect(function() print("Selfie mode closed") end) ``` #### actionActivated | Parameters | | --- | | action: [SelfieMode.Action](#action) | The activated [Action](#action). | Fires when an action is activated; this may be one of the primary actions like [Depth of Field](#depth-of-field), [Lock Gaze](#lock-gaze), or [Hide Others](#hide-others); alternatively it may be a sub-action like a [filter](#filter) or [pose](#pose). The connected function receives the activated [Action](#action). This event can only be connected in a `Class.LocalScript`. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local SelfieMode = require(ReplicatedStorage.SelfieMode) SelfieMode.actionActivated:Connect(function(action) print(action.name, "activated") end) ``` #### actionDeactivated | Parameters | | --- | | action: [SelfieMode.Action](#action) | The deactivated [Action](#action). | Fires when a primary action or sub-action is deactivated. The connected function receives the deactivated [Action](#action). This event can only be connected in a `Class.LocalScript`. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local SelfieMode = require(ReplicatedStorage.SelfieMode) SelfieMode.actionDeactivated:Connect(function(action) print(action.name, "deactivated") end) ``` #### filterChanged | Parameters | | --- | | newFilter: `Library.string` | The new filter. | | oldFilter: `Library.string` | The previous filter. | Fires when a [filter](#filter) is applied or removed. The connected function receives the new filter name and the old filter name. This event can only be connected in a `Class.LocalScript`. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local SelfieMode = require(ReplicatedStorage.SelfieMode) SelfieMode.filterChanged:Connect(function(newFilter, oldFilter) print("Filter changed from", oldFilter, "to", newFilter) end) ``` #### poseChanged | Parameters | | --- | | newPose: `Library.string` | The new pose. | | oldPose: `Library.string` | The previous pose. | Fires when a [pose](#pose) is applied or removed. The connected function receives the new pose name and the old pose name. This event can only be connected in a `Class.LocalScript`. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local SelfieMode = require(ReplicatedStorage.SelfieMode) SelfieMode.poseChanged:Connect(function(newPose, oldPose) print("Pose changed from", oldPose, "to", newPose) end) ``` --- title: "Social Interactions" url: /docs/en-us/resources/modules/social-interactions last_updated: 2026-06-29T19:34:10Z description: "The Social Interactions module lets avatars better express themselves and their natural movements." --- # Social Interactions Your avatar is your identity in any space you enter. The **SocialInteractions** [developer module](/docs/en-us/resources/modules.md) lets each user better express themselves and their natural movements, adding a touch of realism to the experience. This module includes the following features: | **Body Orientation** | Makes the head of everyone's avatar face where their corresponding user's camera is pointing, through a mix of neck and waist rotation. This provides a subtle cue as to who or what someone else is interacting with. | | --- | --- | | **Chat Animations** | Adds some liveliness to the in-experience chat by making avatars occasionally play animations, depending on the content of the messages they send. The list of "trigger words" that activate each animation is configurable. | > **Warning:** The body orientation feature uses `Datatype.CFrame` adjustment of the **Neck** and **Waist** joints on the character, meaning that custom rigs must include `Class.Motor6D` objects of the same name for the module to work properly. ## Module Usage ### Installation To use the **SocialInteractions** module in an experience: 1. From Studio's **Window** menu or **Home** tab toolbar, open the [Toolbox](/docs/en-us/projects/assets/toolbox.md) and select the **Creator Store** tab. 2. Make sure the **Models** sorting is selected, then click the **See All** button for **Categories**. 3. Locate and click the **Packages** tile. 4. Locate the **Social Interactions** module and click it, or drag-and-drop it into the 3D view. 5. In the [Explorer](/docs/en-us/studio/explorer.md) window, move the entire **SocialInteractions** model into `Class.ReplicatedStorage`. Upon running the experience the module will begin running. ### Configuration Simply inserting the **SocialInteractions** module will enable both the **body orientation** and **chat animations** features inside your place. To adjust the default behavior: 1. In **StarterPlayerScripts**, create a new `Class.LocalScript` and rename it to **ConfigureSocialInteractions**. 2. Paste the following code into the new script, using the [configure](#configure) function to customize the module's behavior.```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local SocialInteractions = require(ReplicatedStorage.SocialInteractions) -- Make waist rotation more pronounced and disable the chat animations feature SocialInteractions.configure({ waistOrientationWeight = 0.75, useChatAnimations = false, }) ``` ### Chat Animation Trigger Words The list of "trigger words" that activate each chat animation is configurable and Luau string patterns are utilized to increase recognizable words. For example, one combination used by the **Wave** animation is `he+y+o*`, meaning that `hey`, `heyyy`, `heyo`, `heyyyyo`, `heeeeyyyyo`, and other variations qualify to trigger the animation. Also note that trigger words are **case-insensitive**, so typing `hey` is the same as `HEY`, `Hey`, and other variations. | Animation | Animation ID | Word Patterns | | --- | --- | --- | | Wave | `3344650532` | `hell+o+`    `h+i+o*`    `wa+[sz]+u+p+`    `y+o+`    `greetings*`    `salutations*`    `goo+d+%smorning+`    `he+y+o*`    `howdy+`    `what's*%s*up+`    | | Applaud | `5911729486` | `ya+y+`    `h[ou]+r+a+y+`    `woo+t*`    `woo+h+oo+`    `bravo+`    `congratulations+`    `congrats+`    `gg`    `pog+`    `poggers+`    | | Agree | `4841397952` | `ye+s*`    `ye+a+h*`    `y[eu]+p+`    `o+k+`    `o+k+a+y+`    | | Disagree | `4841401869` | `no+`    `no+pe+`    `yi+ke+s+`    | | Shrug | `3334392772` | `not+%s+sure+`    `idk+`    `don't%s+know+`    `i%s+don't%s+know+`    `who+%s+knows+`    | | Laugh | `3337966527` | `lo+l+`    `rof+l+`    `ha[ha]*`    `he[he]+`    | | Sleep | `4686925579` | `zzz+`    `yawn+`    | The list of trigger words that activate each animation is configurable, and additional animations can be added via the [setTriggerWordsForChatAnimation](#settriggerwordsforchatanimation) function. For example, the following `Class.LocalScript` links the [Tilt](https://www.roblox.com/catalog/3360692915/Tilt) animation with the string pattern of `cra+zy` to support trigger words like `crazy` and `craaaaaazy`. It also registers an additional string pattern of `coo+l` for the [Applaud](https://www.roblox.com/catalog/5915779043/Applaud) animation to support words like `cool` and `coooool`. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local SocialInteractions = require(ReplicatedStorage.SocialInteractions) -- Register string pattern for the "Tilt" animation SocialInteractions.setTriggerWordsForChatAnimation("rbxassetid://3334538554", {"cra+zy"}) -- Register additional string pattern for the "Applaud" animation SocialInteractions.setTriggerWordsForChatAnimation("rbxassetid://5911729486", {"coo+l"}) ``` ## API Reference ### Functions #### configure _ configure(config: `Library.table`)_ Overrides default configuration options through the following keys/values in the `config` table. This function can only be called from a `Class.LocalScript`. | Key | Description | Default | | --- | --- | --- | | `useBodyOrientation` | Toggles the **body orientation** feature. | true | | `waistOrientationWeight` | Body orientation uses a mix of waist and neck rotation; this parameter determines which of the two is prevalent. A value of 1 places complete emphasis on the waist while 0 places complete emphasis on the neck. | 0.5 | | `useChatAnimations` | Toggles the **chat animations** feature. | true | | `useDefaultTriggerWordsForChatEmotes` | Chat animations comes with a default list of [trigger words](#chat-animation-trigger-words). Set this parameter to `false` if you'd like to turn them off and provide your own. | true | ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local SocialInteractions = require(ReplicatedStorage.SocialInteractions) -- Make waist rotation more pronounced and disable the chat animations feature SocialInteractions.configure({ waistOrientationWeight = 0.75, useChatAnimations = false, }) ``` #### setTriggerWordsForChatAnimation _ setTriggerWordsForChatAnimation(animationId: `Library.string`, triggerWords: `Library.table`)_ Registers a new animation in the chat animation feature. Typing any word that matches a string pattern included in the `triggerWords` table will activate the animation whose ID is passed as the first parameter. Note that trigger words are **case-insensitive** to players, so a pattern of `woah` will accept chat phrases of `woah`, `WOAH`, `Woah`, and other variations. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local SocialInteractions = require(ReplicatedStorage.SocialInteractions) -- Register new string pattern for a custom animation SocialInteractions.setTriggerWordsForChatAnimation( "rbxassetid://3334538554", {"cra+zy", "woah+"} ) ``` ### Events #### onChatAnimationPlayed Fires when a chat animation plays. The connected function receives the animation ID and the word that triggered the animation as its arguments. This event can only be connected in a `Class.LocalScript`. | Parameters | | --- | | animationId: `Library.string` | Animation ID that played. | | triggerWord: `Library.string` | Chat word that triggered the animation. | ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local SocialInteractions = require(ReplicatedStorage.SocialInteractions) SocialInteractions.onChatAnimationPlayed:Connect(function(animationId, triggerWord) print(animationId, triggerWord) end) ``` --- title: "Spawn With Friends" url: /docs/en-us/resources/modules/spawn-with-friends last_updated: 2026-06-29T19:34:10Z description: "The Spawn With Friends module automatically moves spawning players near one of their friends." --- # Spawn With Friends It can be challenging to locate friends in-experience. The **SpawnWithFriends** [developer module](/docs/en-us/resources/modules.md) automatically moves spawning players near one of their friends present in the experience. This module can also be configured to teleport a player on command instead of automatically. > **Warning:** Before spawning occurs, in order to avoid spawning a character inside the map's geometry, the system confirms that there is enough space. Thus, this module might work better in experiences with large, open spaces. ## Module Usage ### Installation To use the **SpawnWithFriends** module in an experience: 1. From Studio's **Window** menu or **Home** tab toolbar, open the [Toolbox](/docs/en-us/projects/assets/toolbox.md) and select the **Creator Store** tab. 2. Make sure the **Models** sorting is selected, then click the **See All** button for **Categories**. 3. Locate and click the **Packages** tile. 4. Locate the **Spawn With Friends** module and click it, or drag-and-drop it into the 3D view. 5. In the [Explorer](/docs/en-us/studio/explorer.md) window, move the entire **SpawnWithFriends** model into `Class.ReplicatedStorage`. Upon running the experience the module will begin running. ### Restricted Spawn Areas This module may result in players spawning in restricted areas like VIP rooms, access-only spaces, etc. To prevent players from teleporting to these areas: 1. Fill the restricted area with invisible `Class.BasePart.Anchored|Anchored` blocks. Make sure `Class.BasePart.CanCollide|CanCollide`, `Class.BasePart.CanTouch|CanTouch`, and `Class.BasePart.CanQuery|CanQuery` are **disabled** for all blocks._Block filling the entire prison room to prevent players from spawning inside_ 2. Using the [Tags](/docs/en-us/studio/properties.md#instance-tags) section of each block's properties, apply the tag `RestrictedSpawnArea` so that `Class.CollectionService` detects them. 3. Paste the following code into a `Class.Script` within **ServerScriptService**.```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local CollectionService = game:GetService("CollectionService") local SpawnWithFriends = require(ReplicatedStorage.SpawnWithFriends) local function validator(playerToTeleport, destinationPlayer, teleportationPoint) -- Iterate through all tagged parts for _, area in CollectionService:GetTagged("RestrictedSpawnArea") do local relativePosition = area.CFrame:PointToObjectSpace(teleportationPoint.Position) local size = area.Size local inXBounds = relativePosition.X < size.X / 2 and relativePosition.X > -size.X / 2 local inZBounds = relativePosition.Z < size.Z / 2 and relativePosition.Z > -size.Z / 2 if inXBounds and inZBounds then return false -- Spawn destination is within restricted area; abort teleportation end end return true -- Spawn destination doesn't overlap any restricted area; proceed with teleportation end SpawnWithFriends.setTeleportationValidator(validator) ``` ## API Reference ### Functions #### configure _ configure(config: `Library.table`)_ Overrides default configuration options through the following keys/values in the `config` table. This function can only be called from a `Class.Script`. | Key | Description | Default | | --- | --- | --- | | `teleportToFriendOnRespawn` | If set to `false`, teleportation to a friend will only happen manually via [teleportToRandomFriend](#teleporttorandomfriend). | true | | `teleportDistance` | How far away players should spawn from each other, measured in studs. | 5 | | `maxCharacterVelocity` | Characters moving faster than this value won't be picked as teleportation candidates, for instance those in moving vehicles. | 48 | | `bypassFriendshipCheck` | If set to true, **all** players will be candidates for teleportation, not just friends. | false | | `showLogs` | Whether or not to display log messages in the output. | false | ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local SpawnWithFriends = require(ReplicatedStorage.SpawnWithFriends) SpawnWithFriends.configure({ teleportToFriendOnRespawn = true, teleportDistance = 5, maxCharacterVelocity = 48, bypassFriendshipCheck = false, showLogs = false }) ``` #### teleportToRandomFriend _ teleportToRandomFriend(playerToTeleport: `Class.Player`): `boolean`_ Manually triggers teleportation of a player to one of their friends in the experience. Returns a boolean indicating whether or not teleportation succeeded; failure to teleport can be caused by the absence of friends in the server or the inability to find an unobstructed teleportation point. This function can only be called from a `Class.Script`. #### setTeleportationValidator _ setTeleportationValidator(validator: `function`)_ Allows you to perform custom pre-teleportation checks by hooking up a validator callback function. The callback receives three parameters: | Parameter | Description | | --- | --- | | `playerToTeleport` | Reference to the `Class.Player` that is being teleported. | | `destinationPlayer` | Reference to the target `Class.Player` that `playerToTeleport` is being teleported to. | | `teleportationPoint` | `Datatype.CFrame` where `playerToTeleport` is teleporting to. | This function and its callback can only be used in a `Class.Script` and the callback returns a boolean indicating whether teleportation should proceed. For example, the `return` logic in the following validator function ensures that the spawning player and destination player are on the same team. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local SpawnWithFriends = require(ReplicatedStorage.SpawnWithFriends) -- Teleports players only if they are on the same team local function validator(playerToTeleport, destinationPlayer, teleportationPoint) return playerToTeleport.Team == destinationPlayer.Team end SpawnWithFriends.setTeleportationValidator(validator) ``` --- title: "Surface Art" url: /docs/en-us/resources/modules/surface-art last_updated: 2026-06-29T19:34:10Z description: "The Surface Art module lets players literally leave their mark in an experience." --- # Surface Art Players often enjoy feeling like they're a part of constructing the space they're in. The **SurfaceArt** [developer module](/docs/en-us/resources/modules.md) lets players literally leave their mark in an experience. > **Warning:** By default, a player can place 2 pieces of art across all of the tagged surfaces in the workspace. All of a player's art will be removed when they leave the experience. ## Module Usage ### Installation To use the **SurfaceArt** module in an experience: 1. From Studio's **Window** menu or **Home** tab toolbar, open the [Toolbox](/docs/en-us/projects/assets/toolbox.md) and select the **Creator Store** tab. 2. Make sure the **Models** sorting is selected, then click the **See All** button for **Categories**. 3. Locate and click the **Packages** tile. 4. Locate the **Surface Art** module and click it, or drag-and-drop it into the 3D view. 5. In the [Explorer](/docs/en-us/studio/explorer.md) window, move the entire **SurfaceArt** model into `Class.ReplicatedStorage`. Upon running the experience the module will begin running. ### Positioning the Canvas The module comes with one **SurfaceCanvas** model that you can position in the 3D world. This model is what players will interact with to place art on its surface. 1. Locate the **SurfaceCanvas** mesh inside the **Workspace** folder of the module's main folder. 2. Move it into the top-level **Workspace** hierarchy and position it where desired. 3. Upon publishing/running a test session, players will be able to interact with the object via a `Class.ProximityPrompt` and place art on the defined surface. > **Info:** It is not required that you use the packaged mesh for placing art. The module uses `Class.CollectionService` to locate parts tagged as `SurfaceCanvas` and make them operate as canvases. Any tagged canvas must be a `Class.BasePart` and it should have an [attribute](/docs/en-us/studio/properties.md#instance-attributes) defining the surface face as outlined in [Changing the Canvas Face](#changing-the-canvas-face). ### Changing the Canvas Face Under the hood, the module uses a `Class.SurfaceGui` to display art items. To configure which surface the art appears on: 1. Select the **SurfaceCanvas** mesh. 2. At the bottom of the **Properties** window, locate the **SurfaceCanvasFace** attribute with a default value of **Right**. 3. Click the attribute and enter one of six values that describe a `Enum.NormalId`. | Attribute Value | Corresponding Normal ID | | --- | --- | | **Front** | `Enum.NormalId.Front` | | **Back** | `Enum.NormalId.Back` | | **Right** | `Enum.NormalId.Right` | | **Left** | `Enum.NormalId.Left` | | **Top** | `Enum.NormalId.Top` | | **Bottom** | `Enum.NormalId.Bottom` | ### Using Custom Art Assets > **Info:** Currently, the surface art module only supports using the same set of assets for all tagged canvases. To better fit the theme of your experience, you may use your own set of custom assets instead of the defaults. This can be done via the [configure](#configure) function, called from a `Class.Script` in **ServerScriptService**. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local SurfaceArt = require(ReplicatedStorage.SurfaceArt) local customAssets = { CustomAsset1 = { name = "Custom Asset 1", assetId = "rbxassetid://7322508294", }, CustomAsset2 = { name = "Custom Asset 2", assetId = "rbxassetid://7322547665", }, } SurfaceArt.configure({ assets = customAssets, }) ``` ### Clearing All Canvases To remove all existing art from all canvases in the world, call the [removeAllArt](#removeallart) function from a `Class.Script`. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local SurfaceArt = require(ReplicatedStorage.SurfaceArt) SurfaceArt.removeAllArt() ``` ### Showing Custom Effects There may be cases where you'd like to include additional visual effects when an artwork is placed. This module exposes an event called [artChanged](#artchanged) on the client that you can connect to and add your own logic. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local SurfaceArt = require(ReplicatedStorage.SurfaceArt) local function createParticleEmitter(canvas, position) local attachment = Instance.new("Attachment") attachment.Position = canvas.CFrame:PointToObjectSpace(position) attachment.Axis = Vector3.new(0, 0, 1) attachment.SecondaryAxis = Vector3.new(1, 0, 0) attachment.Parent = canvas local particleEmitter = Instance.new("ParticleEmitter") particleEmitter.Speed = NumberRange.new(50) particleEmitter.Rate = 50 particleEmitter.Color = ColorSequence.new(Color3.fromRGB(128, 254, 7)) particleEmitter.SpreadAngle = Vector2.new(35, 35) particleEmitter.Parent = attachment return attachment end SurfaceArt.artChanged:Connect(function(canvas, spot, spotPosition, artId, ownerId) if artId then -- Show some sparkles for 3 seconds task.spawn(function() local emitterAttachment = createParticleEmitter(canvas, spotPosition) task.wait(3) emitterAttachment:Destroy() end) end end) ``` ## API Reference ### Types #### SurfaceArtAsset Images to be used as art for the canvas are represented by a table with two values. | Key | Description | | --- | --- | | `name` | Metadata display name. | | `assetId` | Asset ID of the image to include. | ### Functions #### configure _ configure(config: `Library.table`)_ Overrides default configuration options through the following keys/values in the `config` table. This function can only be called from a `Class.Script`. #### General | Key | Description | Default | | --- | --- | --- | | `enabled` | Toggles the module's functionality on or off. | true | | `assets` | List of [SurfaceArtAsset](#surfaceartasset) types. | (see code below) | | `quotaPerPlayer` | Maximum number of art pieces that can be placed by each player. | 2 | #### Appearance | Key | Description | Default | | --- | --- | --- | | `rowsPerCanvas` | Number of rows in the canvas grid. | 2 | | `colsPerCanvas` | Number of columns in the canvas grid. | 5 | | `itemsPerPage` | Number of items to skip when paging left and right. | 3 | | `canvasPaddingLeft` | Left padding for the surface canvas (`Datatype.UDim`). | (0, 8) | | `canvasPaddingRight` | Right padding for the surface canvas (`Datatype.UDim`). | (0, 8) | | `canvasPaddingTop` | Top padding for the surface canvas (`Datatype.UDim`). | (0, 8) | | `canvasPaddingBottom` | Bottom padding for the surface canvas (`Datatype.UDim`). | (0, 8) | | `promptImage` | Icon shown in the proximity prompt to enter art selection view. | "rbxassetid://8076723774" | | `leftArrowPageImage` | Image for the left arrow to flip to the previous page. | "rbxassetid://6998633654" | | `leftArrowItemImage` | Image for the left arrow to select the previous art item. | "rbxassetid://8072765021" | | `rightArrowPageImage` | Image for the right arrow to flip to the next page. | "rbxassetid://6998635824" | | `rightArrowItemImage` | Image for the right arrow to select the next art item. | "rbxassetid://8072764852" | #### Interaction | Key | Description | Default | | --- | --- | --- | | `promptKeyCode` | Keyboard shortcut used to activate the prompt to enter art selection (`Enum.KeyCode`). | `Enum.KeyCode\|E` | | `promptRequiresLineOfSight` | Boolean value that determines if the proximity prompt has to be in line of sight between user and canvas. | true | | `promptMaxActivationDistance` | Maximum distance a player's character can be from the canvas for the prompt to appear. | 10 | | `promptExclusivity` | `Enum.ProximityPromptExclusivity` specifying which prompts can be shown at the same time. | `Enum.ProximityPromptExclusivity\|OnePerButton` | | `usePageHotkeys` | Whether page hotkeys are used. If true, `nextPageKey` and `prevPageKey` are used to cycle between pages. | true | | `nextPageKey` | Key used to cycle to the next page of artwork (`Enum.KeyCode`). | `Enum.KeyCode\|E` | | `nextItemKey` | Key used to cycle to the next item of artwork (`Enum.KeyCode`). | `Enum.KeyCode\|Right` | | `prevPageKey` | Key used to cycle to the previous page of artwork (`Enum.KeyCode`). | `Enum.KeyCode\|Q` | | `prevItemKey` | Key used to cycle to the previous item of artwork (`Enum.KeyCode`). | `Enum.KeyCode\|Left` | ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local SurfaceArt = require(ReplicatedStorage.SurfaceArt) SurfaceArt.configure({ quotaPerPlayer = 4, promptKeyCode = Enum.KeyCode.T, promptMaxActivationDistance = 8, }) ``` #### getCanvases _ getCanvases(): `Library.table`_ Returns all of the canvases tagged with the `SurfaceCanvas` tag. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local SurfaceArt = require(ReplicatedStorage.SurfaceArt) local canvases = SurfaceArt.getCanvases() ``` #### placeArt _ placeArt(player: `Class.Player`, canvas: `Class.BasePart`)_ Places an art piece programmatically on behalf of a player. Note that the `canvas` object must be tagged with the `SurfaceCanvas` tag when the server is initialized. It is recommended to use this only with a canvas returned from [getCanvases](#getcanvases). ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local SurfaceArt = require(ReplicatedStorage.SurfaceArt) local remoteEvent = ReplicatedStorage:WaitForChild("SurfaceArtRemoteEvent") remoteEvent.OnServerEvent:Connect(function(player) -- Place the Bloxy Award from default art assets into the first canvas local canvases = SurfaceArt.getCanvases() SurfaceArt.placeArt(player, canvases[1], "BloxyAward") end) ``` #### removeAllArt _ removeAllArt()_ Removes all artwork from all surfaces. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local SurfaceArt = require(ReplicatedStorage.SurfaceArt) SurfaceArt.removeAllArt() ``` ### Events #### artChanged Fires when an artwork is changed at a particular location on a canvas. When an artwork is removed, `artId` will be `nil`. Note that a `Datatype.Vector3` value is passed as the third parameter to the event handler so that you can position a [custom effect](#showing-custom-effects) at the exact position where the artwork is placed. This event can only be connected in a `Class.LocalScript`. | Parameters | | --- | | canvas: `Class.BasePart` | Canvas upon which the artwork was changed. | | spot: `Class.Frame` | Internal `Class.Frame` that contains the artwork `Class.ImageLabel`. | | spotPosition: `Datatype.Vector3` | Exact position where the artwork was placed. | | artId: `Library.string` | Asset ID of the new artwork. | | ownerUserId: `number` | `Class.Player.UserId\|UserId` of the player who placed the art. | ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local SurfaceArt = require(ReplicatedStorage.SurfaceArt) SurfaceArt.artChanged:Connect(function(canvas, spot, spotPosition, artId, ownerId) print("Art placed at:", spotPosition) print("Art asset ID:", artId) print("Art placed by:", ownerId) end) ``` #### promptShown Fires when a canvas interaction prompt is shown to a player. The connected function receives the canvas upon which the prompt is showing. This event can only be connected in a `Class.LocalScript`. | Parameters | | --- | | canvas: `Class.BasePart` | Canvas upon which the prompt is showing. | ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local Players = game:GetService("Players") local SurfaceArt = require(ReplicatedStorage.SurfaceArt) SurfaceArt.promptShown:Connect(function(canvas) print(Players.LocalPlayer, canvas) end) ``` #### promptHidden Fires when a canvas interaction prompt is hidden. The connected function receives the canvas upon which the prompt was showing. This event can only be connected in a `Class.LocalScript`. | Parameters | | --- | | canvas: `Class.BasePart` | Canvas upon which the prompt was showing. | ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local Players = game:GetService("Players") local SurfaceArt = require(ReplicatedStorage.SurfaceArt) SurfaceArt.promptClosed:Connect(function(canvas) print(Players.LocalPlayer, canvas) end) ``` #### selectorShown Fires when the surface art selector UI is shown to a player. This event can only be connected in a `Class.LocalScript`. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local Players = game:GetService("Players") local SurfaceArt = require(ReplicatedStorage.SurfaceArt) SurfaceArt.selectorShown:Connect(function() print(Players.LocalPlayer, "opened surface art selector") end) ``` #### selectorHidden Fires when the surface art selector UI is hidden for a player. This event can only be connected in a `Class.LocalScript`. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local Players = game:GetService("Players") local SurfaceArt = require(ReplicatedStorage.SurfaceArt) SurfaceArt.selectorHidden:Connect(function() print(Players.LocalPlayer, "closed surface art selector") end) ``` --- title: "NPC kit" url: /docs/en-us/resources/npc-kit last_updated: 2026-06-29T19:34:10Z description: "The NPC Kit provides several pre-built, customizable NPCs for use an any experience." --- # NPC kit > **Info:** The content of this project and documentation can be used under Roblox's [Limited Use License](/docs/en-us/resources/limited-use-license.md). NPCs (non-player characters) can add a lot of depth to an experience. All of the following NPCs can be visually customized, their [behavior modified](#configuration), and the zombie/soldiers can even defend an area by attacking players or other characters using a [tag system](#assign-tags) to set behavior. To use an NPC in your game: 1. Select one of the following NPC kits: _Drooling Zombie__Soldiers__RO-01 Robots__NP-C 9000 Robots_ 2. On the NPC's item page, click the green **Get** button and confirm the transaction. 3. In Studio, open the [Toolbox](/docs/en-us/projects/assets/toolbox.md). 4. Select your toolbox **Inventory** section. 5. Locate the NPC and click it to add it into the place. ## Character structure Each NPC model typically contains the following objects: | Object name or [Type] | Type | Description | | --- | --- | --- | | Animations | `Class.Folder` | Contains `Class.Animation\|Animations`, such as an `AttackAnimation` or `DeathAnimation`. | | Initial Poses | `Class.Folder` | Contains posing information. | | [Animate](#animate) | `Class.Script` | Loads and plays animations on the character rig. See [Animate](#animate) for more details. | | [Accessory] | `Class.Accessory` | One of possibly multiple `Class.Accessory\|Accessories` for the NPC such as hats, weapons, etc. | | Health | `Class.Script` | Typically regenerates the `Class.Humanoid` health over time. Disabling this will prevent the character from regenerating health. | | Humanoid | `Class.Humanoid` | Manages `Class.Humanoid` related properties, such as `Class.Humanoid.Health`, `Class.Humanoid.WalkSpeed`, `Class.Humanoid.DisplayDistanceType`, etc. | | NPC | `Class.Script` | Defines character-specific behaviors such as roaming, attacking, etc. Parents the following objects:**Maid** (`Class.ModuleScript`) defines a class useful in releasing resources used.**Ragdoll** (`Class.ModuleScript`) defines a function that transforms a character into a loose physics-affected body (parents a RigTypes `Class.ModuleScript` that defines several helper functions). | | RbxNpcSounds | `Class.Script` | Defines and manages behavior related to character sound effects like running, dying, etc. | | [BodyParts](#bodyparts) | `Class.BasePart` | Various character body parts attached to the HumanoidRootPart or neighboring body parts through Motor6D or constraint objects. See [BodyParts](#bodyparts) for more details. | | HumanoidRootPart | `Class.BasePart` | A special invisible part that's considered the root of the rig; this is also the `PrimaryPart` of the character's `Class.Model`. | | [Configuration](#configuration) | `Class.Configuration` | Contains value objects which tune various behaviors. See [Configuration](#configuration) for more details. | > **Info:****Square brackets []** refer to the object in general and the name doesn't matter. For example, [Model] refers to the weapon's `Class.Model` and you can rename it to whatever makes sense. ### Design notes When using the NPC kit, keep in mind the following design notes: - The visual appearance of an NPC can be customized by adding/modifying various [BodyParts] objects and by adding `Class.Accessory` objects. - The Soldiers, Drooling Zombie, and NP-C 9000 Robots use Rthro as the base of their rig. However, the RO-01 Robots use a modified Rthro base that adds thruster parts connected to the **UpperTorso** using `WeldConstraints`. Using simple joints in this way lets you include extra geometry for your characters without changing the original base rig. - At a basic level, NPC animations can be customized by modifying the `AnimationId` of existing Animation objects within the **Animate** script poses, or those within the **Animations** folder. Such a change is essentially an asset swap - to change the finer details, you can create custom copies of existing animations, and to play animations under different conditions, you can edit the **Animate** or **NPC** scripts directly. For more information, see [Animation](/docs/en-us/animation.md). ### Animate The Animate `Class.Script` in the NPC `Class.Model` handles [animation](/docs/en-us/animation.md) related configurations and contains the following objects: | Object name or [Type] | Type | Description | | --- | --- | --- | | ScaleDampeningPercent | `Class.NumberValue` | Defines how animation speeds are modified as the character is scaled (less than 1 implies animation playback scales inversely as a character is scaled). | | PlayEmote | `Class.BindableFunction` | This can be invoked by other scripts in order to force the assumption of a pose. | | [Pose] | `Class.StringValue` | Reference to a playable animation category such as idle, jump, walk, etc. This object can parent any number of `Class.Animation\|Animations`.

These `Class.Animation\|Animations` parent a **Weight** (`Class.NumberValue`) that prioritizes one of multiple animations to play while the pose is assumed; typically used to add variety to idle and dance poses. | ### BodyParts The BodyPart `Class.BasePart` in the NPC `Class.Model` represent the various character body parts and contains the following objects: | Object name or [Type] | Type | Description | | --- | --- | --- | | AvatarPartScaleType | `Class.StringValue` | Determines how the part will be scaled; values can be **Classic**, **ProportionsNormal**, or **ProportionsSlender**. | | OriginalSize | `Class.Vector3Value` | Determines the size of the part when the character scaling is 1. | | [Attachment] | `Class.Attachment` | Defines a point relative to the individual part which scripts, effects, and objects such as a `Class.Tool` or `Class.Accessory` may utilize during positioning. | | [Motor6D] | `Class.Motor6D` | An animated joint between two body parts. Note that `Class.Animator` depends on the name of `Class.Motor6D\|Motor6Ds` to be consistent with that of the `Class.Motor6D\|Motor6Ds` used when an animation was created, so avoid renaming this object. | | [Joint] | `Class.WeldConstraint`, `Class.Constraint`, `Class.JointInstance` | A non-animated joint between two body parts. | | [Sound] | `Class.Sound` | Commonly found in the head or `HumanoidRootPart`; plays sounds from within the rig as controlled by the `RbxNpcSounds` script. | ### Configuration Each NPC includes a `Class.Configuration` object within its hierarchy which acts as a container of value objects. These are used by the NPC script to tune various behaviors. Unless otherwise specified, these apply to all of the characters. | Object name or [Type] | Type | Description | | --- | --- | --- | | DestroyOnDeath | `Class.BoolValue` | Causes the entire NPC to be destroyed shortly after it dies. Disable this for ragdolls to be persistent. | | PatrolEnabled | `Class.BoolValue` | Causes the NPC to wander in an area around its starting position. | | PatrolRadius | `Class.NumberValue` | Defines the maximum distance an NPC will wander from its starting position, assuming **PatrolEnabled** is true. | | RagdollEnabled | `Class.BoolValue` | Causes the NPC to go limp when it dies, instead of breaking apart. | | AttackDamage | `Class.NumberValue` | Defines how much health is lost by a victim when attacked by the NPC. This applies for Zombie and Soldier kits only. | | AttackDelay | `Class.NumberValue` | Defines the minimum number of seconds between shots. This applies for Soldier kits only. | | AttackMode | `Class.NumberValue` | Specifies what the soldier will attack, based on the [tagging system](#assign-tags). This applies for Soldier kits only. | | AttackRadius | `Class.NumberValue` | Defines the maximum distance the NPC must be from a potential victim before it attempts to attack. This applies for Zombie and Soldier kits only. | | ClipCapacity | `Class.NumberValue` | Defines how many bullets the soldier can fire before needing to reload. This applies for Soldier kits only. | | ReloadDelay | `Class.NumberValue` | Defines how many seconds must pass before the soldier's weapon clip is reloaded. This applies for Soldier kits only. | ## Assign tags The **NPC** script uses `Class.CollectionService` tags to manage aggression toward other characters and players. Various tags from the following table can be assigned as follows: - To assign tag(s) to another NPC, assign them to the NPC's top-level `Class.Model` using the [Tags](/docs/en-us/studio/properties.md#instance-tags) section of its properties. - To assign a tag to a `Class.Player` character, you can add a `Class.Script` to **StarterCharacterScripts** with a `Class.CollectionService:AddTag()` call. For example:```lua local CollectionService = game:GetService("CollectionService") CollectionService:AddTag(script.Parent, "SoldierEnemy") ``` | Tag | Purpose | | --- | --- | | **SoldierEnemy** or **SoldierFriend** | Determines if a soldier, based on its **AttackMode** configuration value, should attack another character. When the soldier's **AttackMode** is set to `1`, other characters must be tagged with **SoldierEnemy** to be considered attackable. When the soldier's **AttackMode** is set to `2`, all objects without the **SoldierFriend** tag are considered attackable. When the soldier's **AttackMode** is set to `3`, these tags are ignored entirely and the soldier will attack all characters. | | **ZombieFriend** | This tag is used by the zombie to determine whether it should not attack a character. When applied, the zombie becomes docile toward the tagged character. |
--- title: "Plant reference project" url: /docs/en-us/resources/plant-reference-project last_updated: 2026-06-29T19:34:10Z description: "A deep dive into building of the Plant experience, an experience where players plant seeds and harvest plants." --- # Plant reference project [Plant](https://www.roblox.com/games/14353060924/Plant) is a reference experience where players plant and water seeds, so they can later harvest and sell the resulting plants. ![Plant project banner](../assets/resources/plant/Project-Banner.jpg) The project focuses on common use cases that you might encounter when developing an experience on Roblox. Where applicable, you'll find notes on tradeoffs, compromises, and the rationale of various implementation choices, so you can make the best decision for your own experiences. ## Get the file 1. Navigate to the [Plant](https://www.roblox.com/games/14353060924/Plant) experience page. 2. Click the **⋯** button and **Edit in Studio**. ## Use cases [Plant](https://www.roblox.com/games/14353060924/Plant) covers the following use cases: - Session data and player data persistence - UI view management - Client-server networking - First Time User Experience (FTUE) - Hard and soft currency purchases In addition, this project solves narrower sets of problems that are applicable to many experiences, including: - Customization of an area in the place that's associated with a player - Managing the player character's movement speed - Creating an object that follows characters around - Detecting what part of the world a character is in Note that there are several use cases in this experience that are too small, too niche, or don't demonstrate a solution to an interesting design challenge; these are not covered. ## Project structure The first decision when creating an experience is deciding how to structure the [project](/docs/en-us/projects.md), which mainly includes where to place specific instances in the [data model](/docs/en-us/projects/data-model.md) and how to organize and structure entry points for both client and server code. ### Data model The following table describes which container services in the data model instances are placed in. | Service | Types of instances | | --- | --- | | `Class.Workspace` | Contains static models representing the 3D world, specifically parts of the world that don't belong to any player. You don't need to dynamically create, modify, or destroy these instances at runtime, so it's acceptable to leave them here. There is also an empty `Class.Folder`, to which players' farm models will be added at runtime. | | `Class.Lighting` | Atmospheric and lighting effects. | | `Class.ReplicatedFirst` | Contains the smallest possible subset of instances needed to display the loading screen and initialize the experience. The more instances that are placed in `Class.ReplicatedFirst`, the longer the wait for them to replicate before code in `Class.ReplicatedFirst` can run. | | `Class.ReplicatedStorage` | Serves as a storage container for all instances for which access is required on both the client and the server. | | `Class.ServerScriptService` | Contains a `Class.Script` serving as the entry point for all server-side code in the project. | | `Class.ServerStorage` | Serves as a storage container for all instances that do not need to be replicated to the client. | | `Class.SoundService` | Contains the `Class.Sound` objects used for sound effects in the experience. Under `Class.SoundService`, these `Class.Sound` objects have no position and are not simulated in 3D space. | ### Entry points Most projects organize code inside reusable `Class.ModuleScript|ModuleScripts` that can be imported across the entire codebase. `Class.ModuleScript|ModuleScripts` are reusable but they do not execute on their own; they need to be imported by a `Class.Script` or `Class.LocalScript`. Many Roblox projects will have a large number of `Class.Script` and `Class.LocalScript` objects, each pertaining to a behavior or particular system in the experience, creating multiple points of entry. For the [Plant](https://www.roblox.com/games/14353060924/Plant) microgame, a different approach is implemented through a single `Class.LocalScript` that is the entry point for all client code, and a single `Class.Script` that is the entry point for all server code. The correct approach for your project depends on your requirements, but a single point of entry provides greater control over the order in which systems are executed in. The following lists describe the tradeoffs of both approaches: #### Single entry point - A single `Class.Script` and a single `Class.LocalScript` cover server and client code respectively. - Greater control over the order in which different systems are started because all code is initialized from a single script. - Can pass objects by reference between systems. #### Multiple entry points - New `Class.Script` or `Class.LocalScript` objects are created as needed. - Greater ability to isolate code. - Can take advantage of `Class.Actor|Actors` and [Multi-Threading](/docs/en-us/scripting/multithreading.md). - Execution is in a non-deterministic order. ### High level systems architecture The top-level systems in the project are detailed below. Some of these systems are substantially more complex than others, and in many cases their functionality is abstracted across a hierarchy of other classes. ![Plant project systems architecture diagram](../assets/resources/plant/Architecture-Systems.png) Each of these systems is a "singleton," in that it's a non-instantiable class that is instead initialized by the relevant client or server `start` script. You can read more about the [singleton pattern](#singletons) later in this guide. #### Server The following systems are associated with the server. | System | Description | | --- | --- | | **Network** | | | **PlayerDataServer** | | | **Market** | | | **CollisionGroupManager** | | | **FarmManagerServer** | | | **PlayerObjectsContainer** | | | **TagPlayers** | | | **FtueManagerServer** | | | **CharacterSpawner** | | #### Client The following systems are associated with the client. | System | Description | | --- | --- | | **Network** | | | **PlayerDataClient** | | | **MarketClient** | | | **LocalWalkJumpManager** | | | **FarmManagerClient** | | | **UISetup** | | | **FtueManagerClient** | | | **CharacterSprint** | | > **Info:** One notable callout for the singleton pattern is the **Network** `Class.ModuleScript` which includes logic that runs on both the client and the server, initialized through separate `startServer()` and `startClientAsync()` methods. This is a pattern that you should generally avoid, as it exposes server-side logic unnecessarily to the client and can make files more complex than needed. In this case, however, a unified `Class.ModuleScript` setup presents a more streamlined interface for what is a high touch part of the codebase. ## Client-server communication Most Roblox experiences involve some element of communication between the client and server. This can include the client requesting the server perform a certain action and the server replicating updates to the client. In this project, client-server communication is kept as generic as possible by limiting the use of `Class.RemoteEvent` and `Class.RemoteFunction` objects in order to decrease the amount of special rules to keep track of. This project uses the following methods, in order of preference: - Replication via the [player data system](#replication-via-player-data-system). - Replication via [attributes](#replication-via-attributes). - Replication via [tags](#replication-via-tags). - Messaging [directly](#message-directly-via-network-module) via the **Network** module. ### Replication via player data system The **player data system** allows data to be associated with the player which persists between save sessions. This system provides replication from client to server and a set of APIs that can be used to query data and subscribe to changes, making it ideal for replicating changes to player state from the server to the client. For example, rather than firing a bespoke `UpdateCoins` `Class.RemoteEvent` to tell the client how many coins it has, you can call the following and let the client subscribe to it via the `PlayerDataClient.updated` event. ```lua PlayerDataServer.setValue(player, "coins", 5) ``` Of course, this is only useful for server-to-client replication and for values that you want to persist between sessions, but this applies to a surprising number of cases in the project, including: - The current FTUE stage - The player's inventory - The amount of coins the player has - The state of the player's farm ### Replication via attributes In situations where the server needs to replicate a custom value to the client that's specific to a given `Class.Instance`, you can use [attributes](/docs/en-us/studio/properties.md#instance-attributes). Roblox automatically replicates attribute values, so you don't need to maintain any code paths to replicate state associated with an object. Another advantage is that this replication happens alongside the instance itself. This is particularly useful for instances created at runtime, as attributes set on a new instance before it is parented to the data model will replicate atomically with the instance itself. This circumvents any need to write code to "wait" for extra data to be replicated via a `Class.RemoteEvent` or `Class.StringValue`. You can also directly read attributes from the data model, from either the client or the server, with the `Class.Instance:GetAttribute()|GetAttribute()` method, and subscribe to changes with the `Class.Instance:GetAttributeChangedSignal()|GetAttributeChangedSignal()` method. In the [Plant](https://www.roblox.com/games/14353060924/Plant) project, this approach is used for, amongst other things, replicating the current status of plants to clients. ### Replication via tags `Class.CollectionService` lets you apply a string tag to an `Class.Instance`. This is useful for categorizing instances and replicating that categorization to the client. For example, the `CanPlant` tag is applied on the server to signify to the client that a given pot is able to receive a plant. ### Message directly via network module For situations where none of the previous options apply, you can use custom network calls through the **Network** module. This is the only option in the project that allows client-to-server communication and is therefore most useful for transmitting client requests and receiving a server response. [Plant](https://www.roblox.com/games/14353060924/Plant) uses direct network calls for a variety of client requests, including: - Watering a plant - Planting a seed - Purchasing an item The drawback with this approach is that each individual message requires some bespoke configuration which can increase the complexity of the project, although this has been avoided wherever possible, particularly for server-to-client communication. ## Classes and singletons Classes in the [Plant](https://www.roblox.com/games/14353060924/Plant) project, like instances on Roblox, can be created and destroyed. Its class syntax is inspired by the idiomatic Lua approach to [object-oriented programming](https://www.lua.org/pil/16.1.html) with a number of changes to enable [strict typechecking](#strict-type-inference) support. ### Instantiation Many classes in the project are associated with one or more `Class.Instance|Instances`. Objects of a given class are created using a `new()` method, consistent with how instances are created in Roblox using `Datatype.Instance.new()`. This pattern is generally used for objects where the class has a physical representation in the data model, and the class extends its functionality. A good example is `BeamBetween` which creates a `Class.Beam` object between two given `Class.Attachment` objects and keeps those attachments orientated so that the beam is always facing upwards. These instances could be cloned from a prefabricated version in `Class.ReplicatedStorage` or passed into `new()` as an argument and stored inside the object under `self`. ### Corresponding instances As noted above, many classes in this project have a data model representation, an instance that corresponds with the class and is manipulated by it. Rather than creating these instances when a class object is instantiated, the code generally opts to `Class.Instance:Clone()|Clone()` a prefabricated version of the `Class.Instance` stored under `Class.ReplicatedStorage` or `Class.ServerStorage`. Although it would be possible to serialize the properties of these instances and create them from scratch in the class' `new()` functions, doing so would make editing the objects very cumbersome and make them harder for a reader to parse. Additionally, cloning an instance is generally a faster operation than creating a new instance and customizing its properties at runtime. ### Composition Although inheritance is possible in Luau using [metatables](/docs/en-us/luau/metatables.md), the project opts to instead allow classes to extend each other through **composition**. When combining classes through composition, the "child" object is instantiated in the `new()` method of the class and is included as a member under `self`. For an example of this in action, see the `CloseButton` class which wraps the `Button` class. ### Cleanup Similar to how an `Class.Instance` can be destroyed with the `Class.Instance:Destroy()|Destroy()` method, classes which can be instantiated can also be destroyed. The destructor method for project classes is `destroy()` with a lowercase `d` for `camelCase` consistency across the codebase's methods, as well as to distinguish between the project's classes and Roblox instances. The role of the `destroy()` method is to destroy any instances created by the object, disconnect any connections, and call `destroy()` on any child objects. This is particularly important for connections because instances with active connections are not cleaned up by the Luau garbage collector, even if no references to the instance or connections to the instance remain. ### Singletons Singletons, as the name suggests, are classes for which only one object can ever exist. They are the project's equivalent of Roblox's [Services](/docs/en-us/scripting/services.md). Rather than storing a reference to the singleton object and passing it around in the Luau code, [Plant](https://www.roblox.com/games/14353060924/Plant) takes advantage of the fact that requiring a `Class.ModuleScript` caches its returned value. This means that requiring the same singleton `Class.ModuleScript` from different places consistently provides the same returned object. The only exception to this rule would be if different environments (client or server) accessed the `Class.ModuleScript`. Singletons are distinguished from instantiable classes by the fact that they don't have a `new()` method. Rather, the object along with its methods and state is returned directly via the `Class.ModuleScript`. As singletons are not instantiated, the `self` syntax is not used and methods are instead called with a dot (`.`) rather than a colon (`:`). ## Strict type inference [Luau](/docs/en-us/luau.md) supports gradual typing which means you're free to add optional type definitions to some or all of your code. In this project, `strict` typechecking is used for every script. This is the least permissive option for Roblox's [Script Analysis](/docs/en-us/studio/script-editor.md#script-analysis) tool and thus the most likely to catch type errors before runtime. > **Info:** Strong typing is undoubtedly powerful, but it's still an evolving feature in the Luau language and Studio. As a result, there are a number of limitations that, at the time of writing, required workarounds detailed in the following sections. ### Typed class syntax The established approach to creating classes in Lua is [well documented](https://www.lua.org/pil/16.1.html), however it is not well suited to strong Luau typing. In Luau, the simplest approach for getting the type of a class is the `Global.RobloxGlobals.typeof()` method: ```lua type ClassType = typeof(Class.new()) ``` This works but it isn't very useful when your class is initiated with values that only exist at runtime, for example `Class.Player` objects. Additionally, the assumption made in idiomatic Lua class syntax is that declaring a method on a class `self` will always be an instance of that class; this is not an assumption the type inference engine can make. In order to support strict type inference, the [Plant](https://www.roblox.com/games/14353060924/Plant) project uses a solution that differs from idiomatic Lua class syntax in a number of ways, some of which may feel non-intuitive: - The definition of `self` is duplicated, both in the type declaration and in the constructor. This introduces a maintainability burden, but warnings will be flagged if the two definitions fall out of sync with each other. - Class methods are declared with a dot, so `self` can be explicitly declared to be of type `ClassType`. Methods can still be called with a colon as expected. ```lua --!strict local MyClass = {} MyClass.__index = MyClass export type ClassType = typeof(setmetatable( {} :: { property: number, }, MyClass )) function MyClass.new(property: number): ClassType local self = { property = property, } setmetatable(self, MyClass) return self end function MyClass.addOne(self: ClassType) self.property += 1 end return MyClass ``` ### Cast types after logical guards At the time of writing, the type of a value is not narrowed after a guard conditional statement. For example, following the guard below, the type of `optionalParameter` is not narrowed to `number`. ```lua --!strict local function foo(optionalParameter: number?) if not optionalParameter then return end print(optionalParameter + 1) end ``` To mitigate this, new variables are created after these guards with their type explicitly cast. ```lua --!strict local function foo(optionalParameter: number?) if not optionalParameter then return end local parameter = optionalParameter :: number print(parameter + 1) end ``` ### Traverse DataModel hierarchies In some cases, the codebase needs to traverse the data model hierarchy of a tree of objects that are created at runtime. This presents an interesting challenge for typechecking. At the time of writing, it's not possible to define a generic data model hierarchy as a type. As a result, there are cases where the only type information available for a data model structure is the type of the top level instance. One approach to this challenge is to cast to `any` and then refine. For example: ```lua local function enableVendor(vendor: Model) local zonePart: BasePart = (vendor :: any).ZonePart end ``` The problem with this approach is that it impacts readability. Instead, the project uses a generic module called `getInstance` for traversing data model hierarchies that casts to `any` internally. ```lua local function enableVendor(vendor: Model) local zonePart: BasePart = getInstance(vendor, "ZonePart") end ``` As the type engine's understanding of the data model evolves, it's possible that patterns like this will no longer be necessary. ## User interface [Plant](https://www.roblox.com/games/14353060924/Plant) includes a variety of complex and simple 2D user interfaces. These include non-interactive heads up display (HUD) items like the coin counter and complex interactive menus like the shop. ### UI approach You can loosely compare Roblox [UI](/docs/en-us/ui.md) to the HTML DOM, because it's a hierarchy of objects that describe what the user should be seeing. Approaches to creating and updating a Roblox UI are broadly divided into **imperative** and **declarative** practices. | Approach | Advantages and drawbacks | | --- | --- | | **Imperative** | In the imperative approach, UI is treated like any other instance hierarchy on Roblox. The UI structure is created before runtime in Studio and added to the data model, typically directly in `Class.StarterGui`. Then, at runtime, code manipulates specific pieces of the UI to reflect the state the creator requires. This approach comes with some advantages. You can create the UI from scratch in Studio and store it in the data model. This is a simple and visual editing experience that can accelerate UI creation. Because imperative UI code only concerns itself with what needs changing, it also makes simple UI changes easy to implement. A notable drawback is that, since imperative UI approaches require state to be manually implemented in the form of transformations, complex representations of state can become very hard to find and debug. It's common for errors to emerge when developing imperative UI code, especially when state and the UI becomes desynchronized due to multiple updates interacting in an unexpected order. Another challenge with imperative approaches is that it's harder to break down UI into meaningful components that can be declared once and reused. Because the entire UI tree is declared at edit time, common patterns may be repeated in multiple parts of the data model. | | **Declarative** | In the declarative approach, the desired state of UI instances are declared explicitly, and the efficient implementation of this state is abstracted away by libraries such as [Roact](https://github.com/Roblox/roact/) or [Fusion](https://github.com/Elttob/Fusion). The advantage of this approach is the implementation of state becomes trivial and you only need to describe what you want your UI to look like. This makes identifying and resolving bugs significantly easier. The key drawback is having to declare the entire UI tree in code. Libraries like Roact and Fusion have syntax to make this easier, but it's still a time consuming process and a less intuitive editing experience when composing UI. | [Plant](https://www.roblox.com/games/14353060924/Plant) uses an **imperative** approach under the notion that showing the transformations directly gives a more effective overview of how UI is created and manipulated on Roblox. This would not be possible with a declarative approach. Some repeated UI structures and logic are also abstracted into reusable [components](#layer-and-components) to avoid a common pitfall in imperative UI design. ### High-level architecture ![Plant project UI architecture diagram](../assets/resources/plant/Architecture-UI.png) ### Layer and components In [Plant](https://www.roblox.com/games/14353060924/Plant), all UI structures are either a `Layer` or a `Component`. - `Layer` is defined as a top level grouping singleton that wraps prefabricated UI structures in `Class.ReplicatedStorage`. A layer may contain a number of components, or it may encapsulate its own logic entirely. Examples of layers are the inventory menu or the number of coins indicator in the heads up display. - `Component` is a reusable UI element. When a new component object is instantiated, it clones a prefabricated template from `Class.ReplicatedStorage`. Components may in themselves contain other components. Examples of components are a generic button class or the concept of a list of items. ### View handling A common UI management problem is view handling. This project has a range of menus and HUD items, some of which listen to user input, and careful management of when they are visible or enabled is required. [Plant](https://www.roblox.com/games/14353060924/Plant) approaches this problem with its **UIHandler** system which manages when a UI layer should or should not be visible. All UI layers in the experience are categorized as `HUD` or `Menu` and their visibility is managed by the following rules: - The enabled state of `Menu` and `HUD` layers can be toggled. - Enabled `HUD` layers are only shown if no `Menu` layers are enabled. - Enabled `Menu` layers are stored in a stack, and only one `Menu` layer is visible at a time. When a `Menu` layer is enabled, it is inserted to the front of the stack and shown. When a `Menu` layer is disabled, it is removed from the stack and the next enabled `Menu` layer in the queue is shown. This approach is intuitive because it allows menus to be navigated with history. If one menu is opened from another menu, closing the new menu will show the old menu again. UI layer singletons register themselves with the **UIHandler** and are provided with a signal that fires when its visibility should change. ## Further reading From this thorough overview of the [Plant](https://www.roblox.com/games/14353060924/Plant) project, you may want to explore the following guides which go further in depth on related concepts and topics. - [Client-Server Model](/docs/en-us/projects/client-server.md) — An overview of the client-server model in Roblox. - [Luau](/docs/en-us/luau.md) — Details on **Luau**, the Roblox-created scripting language descended from [Lua 5.1](https://www.lua.org/pil/5.1.html). - [Remote Events and Callbacks](/docs/en-us/scripting/events/remote.md) — All about remote network events and callbacks for communication across the client-server boundary. - [UI](/docs/en-us/ui.md) — Details on user interface objects and design on Roblox. --- title: "Templates" url: /docs/en-us/resources/templates last_updated: 2026-06-29T19:34:10Z description: "Templates provide a set of default objects you can use as the foundation for your experiences." --- # Templates **Templates** are uncopylocked experiences that provide a set of default objects in the starting place's data model that you can use to start your projects. You can find most templates on Studio's landing page, or click any of the buttons below to start editing a template. ## Platformer The **Platformer** template includes: - Common 3D platformer character mechanics, such as double jump, dashing, rolling, and long jumping. - Common 3D platformer gameplay mechanics, such as moving platforms, one-way platforms, and coin pickups. - An example platformer tower course you can use to get comfortable with all of the mechanics. ## UGC Homestore The **UGC Homestore** template includes everything you need to sell your user-generated content (UCG) directly within an experience, including: - Modular building objects that you can customize to fit the aesthetics of your personal UCG store. - Mannequins objects that you can customize with unique accessories and bundles that visitors can try on and purchase either individually or as a set. When you publish the template as your own experience, all of your items that you've previously published to the Marketplace automatically populate in the template's store. For more information on how to use this template, see [Touring the UGC Homestore Template](https://www.youtube.com/watch?v=6MPWLQmIKLk). ## Laser Tag The **Laser Tag** template includes: - A blaster system with attributes you can customize for your own first-person shooter (FPS) experiences, such as damage, ammo capacity, recoil, and spread. - A simple round system you can extend with custom modes and/or scoring systems. - A high-quality first-person shooter arena with custom physically-based rendering (PBR) materials. ## FPS System The **FPS System** template includes the FPS blaster system from the Laser Tag template, as well as targets you can use to practice firing the two blasters. ## Racing The **Racing** template includes a working race car and modular winding track objects that you can restructure for countless race track configurations. ## Baseplate The **Baseplate** template includes only two default objects: - **Spawn location** – A `Class.SpawnLocation` object is where player characters appear in the 3D space when they join an experience, as well as where they respawn when their health reaches zero. - **Baseplate** – A baseplate is a floor with a 4x4 grid texture that aligns with stud measurements. ## Modern City The **Modern City** template includes modular wall, window, and door objects you can snap together to create unique building variations. ## Village The **Village** template includes house, garden, and orchard objects that you can reuse to create rural villages. ## Castle The **Castle** template includes castle and foliage objects that you can reconfigure to create destructible kingdoms. ## Suburban The **Suburban** template includes common neighborhood objects such as homes, playground equipment, and a gas station that you can reuse to create destructible residential cities. ## Pirate Island The **Pirate Island** template includes a destructible ship with working cannons and multiple island examples that you can customize or reference as you create your own with the **Terrain Editor**. ## Classic Obby The **Classic Obby** template includes common obstacle objects like checkpoints, fast pads, jump pads, and player hazards that you can reconfigure to create your own courses. ## Starting Place The **Starting Place** template includes many basic objects to assist creators in becoming familiar with core Studio functionality, such as modifying basic parts and using the **Terrain Editor**. ## Line Runner The **Line Runner** template includes simple 3D objects and scripts that provide infinite side-scrolling gameplay. You can modify the 3D objects to create new courses in which players try to beat their score in every round. ## Capture the Flag The **Capture the Flag** template includes simple 3D objects and scripts that divide players into a blue or red team, then start a five minute timer which each team tries to take the other team's flag and return across the center line. You can modify the arena, teams, and timer for unique gameplay variations. ## Team / FFA Arena The **Team / FFA Arena** template includes simple 3D objects and scripts that allow players to fight each other in free for all arena. You can customize each object to create unique arenas that meet your own gameplay requirements. ## Combat The **Combat** template includes three different objects that players can equip and store to their inventory during gameplay: a sword, pistol, and health pack. You can use these objects to create action and adventure experiences. ## Concert The **Concert** template includes high-quality 3D objects and scripts that allow players to transition between different sequences of events, such as waiting in a lobby, then teleporting to a concert. You can customize this space to transition players between multiple scenes and synchronize complex animations and visuals to a timeline. For more information, see [Event Sequencer](/docs/en-us/resources/modules/event-sequencer.md). ## Move It Simulator The **Move It Simulator** template includes high-quality 3D objects and scripts that allow players to level up by moving around the 3D space. You can modify the 3D objects and terrain to create new courses. ## Mansion of Wonder The **Mansion of Wonder** template includes high-quality 3D objects and scripts that allow players to ride on a cart through a set track, and shoot at enemies in each new area. You can modify the 3D objects and the track itself to create new courses, and have players try to beat their high score in every round. ## Flat Terrain The **Flat Terrain** template includes an empty data model with flat, grassy terrain. ## Classic Racing The **Classic Racing** template includes simple working race cars and track checkpoints objects that you can use to create racing experiences through terrain. ## Classic Baseplate The **Classic Baseplate** template includes only one default object: a baseplate with a legacy stud texture. --- title: "Construct the house" url: /docs/en-us/resources/the-mystery-of-duvall-drive/construct-the-house last_updated: 2026-06-29T19:34:10Z description: "Explains the design process for house in The Mystery of Duvall Drive." --- # Construct the house It took many iterations and rapid testing at the beginning of the process to make the house both fun and creepy, easy to explore, and something you want to get lost in. We'll show you how we thought through the space and constructed it using **packages** to make updating individual chairs and entire rooms faster toward the end. ## Begin with a 2D layout Since the house is both the majority of the playable area and an entire character unto itself, we wanted to give a lot of thought about how to make sure it remained both real and fun. We started with the idea that the house should contain several visual and audio gags, lore, and corrupt puzzle rooms, and we wanted players to initially approach the house with little knowledge of what was going on, learning what happened to the house and the family that lived there as they were playing. To balance all of these important elements, we started imagining the 3D environment with a 2D layout! Starting with figuring out the general **story beats**, or key moments, we wanted the player to experience, we drew a layout of the space, and thought about how we would guide the player where we wanted them while maintaining an environment that felt larger than life. _An early draft of what we wanted the playable area and surrounding environment to look like._ Once we had our early draft of the overall layout, we used [terrain](/docs/en-us/parts/terrain.md) to quickly lay out the surrounding hills, lake, road, and the driveway up to the house. We didn't bother with the visual quality of the demo at this point, but instead focused on how long it took to get to the house and what it would take to give the player glimpses of the house and the event in the sky. _Early concept of how we wanted the player to approach the house. To drive players to want to get a closer look at the house, we used trees to obscure their view while still giving glimpses of the house and the event in the sky._ Using both simple [parts](/docs/en-us/parts.md) along with [Creator Store](/docs/en-us/production/creator-store.md) assets, we were able to quickly add in content that would help us compose the environment, including: - Making the road bend so players couldn't see all the way down the length of it. - Adding trees with denser areas where we wanted to make it clear the player couldn't access, along with thinner areas where we wanted to encourage them to explore. - Raising the house's driveway so that when players approach it, they would have to climb. This gave the house a more intimidating presence. - Positioning the giant event behind the storm in the sky so players could see parts, but not the whole thing, until they approach the house itself. - Placing and figuring out the general shape of the house we ultimately settled with. No amount of experience guarantees your first ideas are the best ones. This is why we got assets into Studio quickly and used simple parts and Creator Store assets to fill in the environment with temporary assets. This allowed us to not have to make everything up front, and we could start playtesting within the first few days. _Earliest version using simple parts and the Creator Store._ While we wanted to eventually replace the temporary assets with our own, the only thing we cared about early on was laying out the basics and playing the experience to make sure the environment was fun to move around and wasn't too large or too much of a "straight shot" so that players were encouraged to explore. _Early shot of the driveway approach._ ## Plan each room The exterior was only meant to be a taste of the overall story to immerse the player and get them familiar with the world they were playing in. The main character was the house itself, and we wanted players to spend most of their gameplay inside it plumbing for secrets and finding surprises in every room. To do this, we needed to figure out what rooms the house should include, when we wanted players to run into them, and where they should exist within the house. We drew a 2D layout of the house, and the team bounced around ideas for cool things that could happen. No ideas were ignored at this point. _Early 2D "blueprint" with descriptions of what creepy events we wanted to happen in each room._ We used 2D boxes of each room to figure out how we wanted to arrange them with each other. Each room should be an opportunity to do something, and we threw around a lot of ideas, including the main uses of each room, what creepy gags we could use to surprise the player, and potential puzzles they could face. Each room had to work together, and we wanted to tell the story slowly and progressively. We knew we couldn't control where someone went first, so we planned for some of the harder puzzles to be further from the front door and harder to stumble upon. This meant adding a few floors to the house, including an attic and a basement for both gameplay and thematic reasons. Final puzzle in a basement that looks evil and scary? Sign us up! ## Build a 3D layout All the early planning and ideas in the world don't make a fun experience, so it was time to start making the idea 3D! The exterior layout was blocked out and we made space for the house to be expanded or contracted. Earliest iterations were done in another 3D application where we could make simple boxes and shapes to start turning that 2D image into a playable space. You may find that building the early versions in Studio and using simple parts is your favorite method. There's no wrong way, so long as you get your ideas into 3D and playable quickly! _Top-down view of the layout. You'll notice it's flipped from the concept._ _Gray meshes are simple meshes you can use to start making the space in 3D._ Nothing gets easier as the work gets more complex, so it was important to us to experiment often and figure out the environment structure we wanted to create. The earliest iterations of the house were as simple as the images you see above, and the simple shapes allowed us to be as flexible as possible and quickly make changes to the sizes of the rooms as we played through the demo. Scale was also important early on, so we always built with an avatar both in our 3D application and inside Studio so we could know how big a door or how tall the steps should be. _We wanted both realistic and block characters to feel natural in scale to the exterior of the house._ _When they were inside the house, we wanted everything to feel spacious, but not absurdly large!_ We went through many small and large iterations to settle on what we have in the final demo. Compare the early drafts above with a much later version of the foyer, and you'll see many of the original decisions we made after playing through the house were able to stay! _Later iteration of the house layout. Most of the layout stayed the same as the early greybox!_ ## Furniture and props We needed a lot of furniture and props to fill a house of this size, so before we started to gray box any of its content, we made a document to track everything we could use that would compliment what type of room it was and the puzzles we were in the process of creating. By getting all of our thoughts down into one spot, we could organize, prioritize, and see where we could reuse content in different rooms. While this was a fair amount of up-front planning, it saved us from either making not enough content or too many unnecessary items. This list gave us a good understanding of what needed to take priority when gray boxing out the content and populating it throughout the house. By adding high-priority content early, we could easily see when assets were repetitious, had incorrect proportions, or when they would need a second variation or a texture swap when they were in certain rooms. Some content we blocked out early on ended up not being important to the experience and removed, but we didn't lose a lot of work in the process since they were only quick blockouts. For all the furniture and props we kept, we made them into [packages](#utilize-packages) in their blockout phase, and this made it easier for us to replace everything throughout the house with their final versions. _Early gray boxed versions._ _Final content versions._ Because of the sheer quantity of assets needed for this demo, reusing textures as much as we could was extremely important for keeping within our memory budget. A lot of the furniture shares the same trim or tileable sheets for wood, metal, stone, glass, and fabric, and we utilized transparent textures wherever we could. For example, we used a transparent texture for fabrics so we could have color variations without having to create a second Albedo texture. For more information on how to reuse textures, see [Planning, Reuse, and Budgets](/docs/en-us/resources/the-mystery-of-duvall-drive/materialize-the-world.md#plan-reuse-and-budgets). _Four textures we reused in several rooms._ ## Photogrammetry We knew we required a lot more content than we had time to build ourselves, so we decided to speed up the process of creating high-quality content on a 1:1 texture sheet by utilizing photogrammetry sourced from the **[Creative Commons](https://en.wikipedia.org/wiki/Creative_Commons) (CC0)**. **[Photogrammetry](https://en.wikipedia.org/wiki/Photogrammetry)** is the process of capturing hundreds of images of objects from every angle in order to create high-quality models with accurate proportions, and Creative Commons models are mostly free models that are typically educational in nature that you can use and/or share legally. These models can range from hand-modeled assets to assets made from photogrammetry, and working with them in this process made it so we could skip the time-consuming step of creating a detailed high-resolution model. The backstory we created for the family describes them as "eccentric collectors'' dabbling in the occult. They've gathered artifacts from all over the world, and we wanted the house filled with things to reflect that story. We were able to source the scan data from [CC0 museum content](https://sketchfab.com/nebulousflynn/collections/cc0-9e9b8c5442ab4b59ba16b6fa5e43b8da) for models like [antiques](https://sketchfab.com/3d-models/philips-7-39-radio-a0f95588f0ed472b8e135474c7b2516d), [statues](https://sketchfab.com/3d-models/asklepios-879e7f628ecb4683b23e803242abea62), and [taxidermy](https://sketchfab.com/3d-models/barn-owl-0e65de5934db4f0eae02835ba7227dcc), then begin the UV mapping, retopologizing, and texture work required to optimize them for Studio. > **Info:** Content from museums and other similar avenues are great free-to-use artist resources for historically accurate assets, and we want to thank the [museums and all involved](https://sketchfab.com/blogs/community/sketchfab-launches-public-domain-dedication-for-3d-cultural-heritage/) in this project for their important contributions and services. _Original scan data meshes with a Diffuse texture._ **[UV mapping](https://en.wikipedia.org/wiki/UV_mapping)** is the 3D modeling process of unwrapping a 3D model's mesh and projecting a 2D image onto it. "U" and "V" represent the axes of the 2D texture because "X", "Y", and "Z" represent the axes of the 3D model. UVs tend to be automated at the time of the scan, and they tend to be poorly done for the purposes of game development. For example, the UVs for certain photogrammetry models can look like a bunch of individual planes, but this isn't the most performant for a mobile experience when creators are limited to 10,000 vertices. Sometimes we would get lucky with the scan data, but most of the time we needed to either update or completely redo these two-dimensional texture coordinates for the purposes of the experience. _Original scan data with automated UVs._ In addition, most meshes from the models would only include the Diffuse texture with lighting information baked into the texture when the model was initially scanned. While this could be helpful to use as a base for the mesh's Albedo texture, it'd still require manual cleanup where we needed to paint out the light and shadow. If a mesh had strong directional lighting, it might look odd where the world lighting doesn't match. For this reason, we had to recreate most PBR textures for the models we used from the CC0 museum content. _Original scan data textures with baked in lighting in the diffuse._ **[Physically-based rendering](/docs/en-us/art/modeling/surface-appearance.md) (PBR)** is the notion of using realistic shading and lighting by more accurately simulating and representing materials and lighting in the real world. It was important to us when creating PBR textures to either not have any or very little lighting information baked into the [Albedo](/docs/en-us/art/modeling/surface-appearance.md#colormap) texture because Studio's engine calculates this from the Image-based Lighting (IBL), in addition to using the texture's [Normal](/docs/en-us/art/modeling/surface-appearance.md#normalmap), [Roughness](/docs/en-us/art/modeling/surface-appearance.md#roughnessmap), and [Metalness](/docs/en-us/art/modeling/surface-appearance.md#metalnessmap) surface values to correctly represent real world materials. In comparison, a Diffuse texture may have either some or all of the detail from these individual maps baked into one texture, which is helpful when you may need the Diffuse texture to carry that detail on its own without the help of IBL or other individual PBR textures. When you're deciding whether to keep a Diffuse texture or create your own PBR textures, consider your own design or memory budget limits to what makes sense for your own experience. The meshes of the scan data that we used were usually fairly good, but some meshes needed to be cleaned up. For example, some meshes had problematic holes that needed to be filled, jagged edges that needed to be smoothed out, or thin edges that needed to be thickened up. For this process, we used **[Zbrush](https://www.maxon.net/en/zbrush)** to edit the scan data. _Notes from left to right: Too thin and Jagged | Whole model needs a bit of polish | Also too thin and jagged. Fill and smooth out. | Fill Holes._ _The same mesh after we cleaned up the scan data._ This process was by no means perfect, but by clearing up the high-resolution model, it was significantly easier to either automatically or manually retopologize the model's mesh. **Retopologizing** is the process of modifying a 3D model's edge distribution and structure, and for our purposes, retopologizing allows for a better texture bake when creating a lower resolution version of a high-resolution model. Lower resolution models are significantly easier on performance, especially on mobile devices, and it still keeps the high-resolution detail from the mesh and textures. For manually or automatically retopologizing, you can use any 3D application of your choice. We chose to use [3DS Max](https://www.autodesk.com/products/3ds-max/overview) to manually retopologize, and Zbrush to automatically retopologize using the Zremesher or Decimation Tool. The **[Zremesher](https://docs.pixologic.com/user-guide/3d-modeling/topology/zremesher/)** tool allowed us to auto-retopologize a mesh to maintain quads which requires a higher vert count to maintain the shape, while the **[Decimation](https://docs.pixologic.com/user-guide/zbrush-plugins/decimation-master/)** tool allowed us to auto-retopologize using triangles with a lower vert count and was still able to maintain the shape. _The Zremesher Tool in Zbrush._ _The Decimation Tool in Zbrush._ Regardless of the automatic route we could choose for our models/meshes, we likely needed to do some cleanup since the auto-retopologizing greatly relies on the high-resolution mesh, how it was constructed, and its algorithm. We tended to choose the decimation route with a triangulated mesh because it was more important to us to have a lower vertex count while maintaining the original shape. _Overlapping and bad topology causes surface normal errors that can occur when auto-retopologizing. This needed to be fixed, otherwise we'd have mesh and lighting issues on the model._ _The process of hand drawing out the faces adhering to the high-resolution's surface normals._ After we completed the process of retopologizing the mesh with new UVs, we needed to then bake and transfer the high-resolution mesh detail and textures. **[Marmoset](https://marmoset.co/)** made it easy to do this process when baking mesh maps and transferring any original textures from the high-resolution model to low-resolution, but you may find other workflows and methods in other 3D applications that work better for you. Regardless of the tool, ensure any original textures on the high-res model is assigned to the model when baking. _Assigning original textures on the high-res model to the crow model when baking._ _New UVs and 512 sized PBR textures with painted out lighting._ _Retopologized by hand with transferred textures. Doing this by hand meant we could control the way the geometry flowed and where we spent vertices. A lot of times it's a better choice, especially when a high-res mesh is made up of a lot of separate objects or has problematic shapes._ _Retopologizing automatically in a 3D application. Doing this automatically saved the time of creating the low-resolution mesh by hand, but the trade off was a mesh with higher vertex count to maintain the silhouette and a more challenging model to work with when creating UVs_ After we finalized our Albedo, Normal, Metalness, and Roughness PBR textures in **[Substance Painter](https://www.substance3d.com/)**, our new assets were ready to be imported into Studio! There were some quality differences between the scan data assets vs. the retopologized assets, but it was ultimately a worthy trade-off when the asset was only 1,000 vertices instead of 1,000,00. It also kept our memory budget intact and allowed the experience to run more smoothly. _Scan data raven._ _Manually retopologized in-game asset._ _Scan data candelabra._ _Automatically retopologized in-game asset._ ## Utilize packages **[Packages](/docs/en-us/projects/assets/packages.md)** are a system that allows you to store and use a version of either a single object or a group of objects, and when you update that version, it updates all instances of that object or group at once. For example, if you make a tree object and place hundreds of that object within your experience, you only need to update the package in order for the hundreds of trees to update to your new version at once. We knew early on we would have **two states** of most rooms in the house, their normal state and their corrupted state, and we needed to maintain both copies of the house and their contents throughout the entire iteration process. For this reason, we decided on what objects needed to be shared in each house state and converted them into packages. _Example of a package for an entire bedroom. Anything that was unique to its normal or corrupt state lived outside of this package._ Packages allowed us to modify everything from the wood trim on wall panels to entire rooms, then both states of the room would update to our changes. We were also able to convert `Class.SurfaceAppearance` objects to packages so we could update a material's appearance everywhere throughout the experience at once. Reusing materials was instrumental to make the demo as performant as possible for devices despite its clear complexity. For example, the following images display an armillary model package with a complex group of child assets, including a metal [surface appearance](/docs/en-us/art/modeling/surface-appearance.md) package that we use in several places within the demo. Wherever we updated the metal surface appearance package, we could also perform a [mass update](/docs/en-us/projects/assets/packages.md#mass-updates) that would force the armillary model package to update the child surface appearance package too. _Armillary Asset_ _Armillary Model Package_ _Metal Surface Appearance Package_ For more information on how to turn an individual object or a group of objects like `Class.Model|Models` or `Class.Folder|Folders` into a package, or for how to choose a creator of the package for edit access, see [Packages](/docs/en-us/projects/assets/packages.md). We chose to designate the Vistech Demo Group as the creator of all of our packages because it let everyone in our group access and edit the packages whenever they needed to. It also allowed us to share these packages between every experience the group owned! We set parent packages, such as the armillary above or an entire room, to [automatically update](/docs/en-us/projects/assets/packages.md#automatic-updates) so that the changes to the model or folder automatically shared across all instances of that package without us having to manually check. This process meant we could smoothly create changes for each room's normal and corrupt states. For example, the following two images show the study's normal and corrupt state that were created through the **same** package. Only a few items outside of their packages differ between them, even considering that the corrupt state is sideways! _Using packages allowed us to make two versions of a room, while only dealing with the content of one._ Instead of working entirely in the same place file as the experience, we built an asset storage place with all our components with package links. Whenever we made a change to a package in this storage place, it would update automatically and immediately across **every place** that uses that package. We could modify objects, add functions, and work on details without having to navigate the main experience's mood and lighting that could make it hard to see what we were doing. _The asset storage place with every component within the demo._ Packages allowed us more freedom to explore different ideas in 3D without having to do a lot of extra work. We could plan, test, or even [revert to any previous version](/docs/en-us/projects/assets/packages.md#revert-changes) of an asset if an experiment didn't pan out in practice or broke the experience. There were many times where hours worth of work were restored by going back a version in the packages. In the end, we couldn't have achieved the final level of polish in this demo without packages. --- title: "Design dark soundscapes" url: /docs/en-us/resources/the-mystery-of-duvall-drive/design-dark-soundscapes last_updated: 2026-06-29T19:34:10Z description: "Explains the design concepts for gloomy soundscapes in The Mystery of Duvall Drive." --- # Design dark soundscapes In order for The Mystery of Duvall Drive to feel authentic and plausible, we made sure that ambience was king at every stage in development. We included scripted audio events, music, character sounds, and point-source audio to support our "grass is always greener" approach, meaning that we always wanted there to be a more intriguing sound in the distance no matter where the player is located in the house so that they would want to further investigate each room. These sounds ranged anywhere from a radio playing music in a bedroom, the kitchen sink dripping, rain hitting the attic roof, or the oscillating ritual orb in the center of the house. Sound design for an experience with horror themes is a balancing act between embedding both realistic and abstract audio. While realistic audio provides a sense of immersion and authenticity to the world, abstract audio drives the player's attention to the otherworldly elements that pull the story forward. Both audio types have their strengths separately, but together they can create a dark soundscape that feels authentic, plausible, and horrifying. In this section, we'll show you how and why we used volumetric audio and nested `Class.SoundGroup` mixing to balance our dark soundscape and bring life to our 3D space. ## Volumetric audio **[Volumetric audio](/docs/en-us/sound/objects.md#volumetric)** is the most realistic audio option in Studio to how people perceive sound depending on its distance to their ears. This type of audio allows you to place a `Class.Sound` object on a `Class.BasePart`, then use that `Class.BasePart` to cover an entire area of where you want that audio to play. It's highly useful for large objects that make sound, such as waterfalls, vehicles, and large cities. When a player is within a `Class.BasePart` that has a child `Class.Sound` object with volumetric audio enabled, the audio plays all around the player, similar to music in headphones playing at the same volume in each speaker. When the user exits the object, audio gradually decreases in volume and becomes more directional per speaker, moving around the user's head when their listener rotates. This means that if the sound is emitting to the left of your listener, the sound plays louder in your left speaker than your right speaker. _Example of an audio volume that plays intermittent creepy human whispers. This reinforces a dark soundscape, setting the stage for the mood inside the house._ A player's experience with audio is not only dependent on what sounds they can hear, but also **which direction** those sounds come from. In the real world, somebody yelling your name from across a field sounds a lot more directional than something like the soft hum of the air conditioning in a room or the reverb in a cave. This is because sound waves are bouncy, and when they hit solid objects like walls, pavement, and trees, they scatter and reflect back to your ears from different directions. In fact, sound waves can even bend around objects like your own head! _In this section of the house, volumetric audio allows you to immerse yourself in the sounds of rain hitting nearby windows, the crackling of a fireplace, and a soft tune being played through an old radio nearby._ Before volumetric audio, you were limited to having your audio emit from a [single point in space](/docs/en-us/sound/objects.md#point-source), or having it [play back in both headphones like music](/docs/en-us/sound/objects.md#background-audio). Because we wanted our audio to be as realistic as possible while representing a wide range of sound types, neither of these previous implementations would have been sufficient for creating the immersive world that we wanted to capture in this experience. As a result, we used volumetric audio emitters for the vast majority of our sound effects, including the outdoor rain, interior room tones, corrupted zone ambiences, television static, and rain hitting various surfaces. If we were to disable volumetric audio on the project, all of these sounds would collapse down to a single point of emission at their center of mass, and the entire soundscape would shift between the player's ears whenever they rotate their camera. This would make the entire space feel unrealistic and digital, and the sound's movement would be quite jarring and undesirable for the player. _Just a few examples of how we used volumetric audio to create our exterior ambience, as well as rain colliding with various materials in the backyard._ Volumetric audio emitters allowed us to control the directionality of our soundscape to give a real sense of movement through the space. Because their influence is spread over a larger area and the audio spreads naturally to both ears, they give the experience's overall sound bed, or constant hum of audio activity, a far more predictable and stable volume level. This prevents the experience from being silent when the player's character isn't doing anything, and it makes the experience feel realistic as there are very few spaces in real life completely void of noise! _We placed a volumetric audio emitter on the greenhouse to add immersive ambient sounds of the raindrops hitting the glass roof and echoing throughout the room._ When adding detail to our outdoor soundscape, it was important to make the rain feel alive instead of a simple looping rain sound effect. One of the ways we achieved this was by adding rain surface sounds to various objects so that you could hear the storm's impact on your immediate surroundings. In this project, there are generic outdoor rain sounds, but also volumetric audio emitters for rain hitting objects like windows, plastic, and rooftops. For example, we added a volumetric audio emitter to a car through the following process to give players the impression that raindrops were impacting the entire surface of the car instead of just a single point on the car's hood: 1. In the **Explorer** window, we selected **SoundService**. 2. In the **Properties** window, we navigated to the **VolumetricAudio** property, then set it to **Enabled**. This turned on the volumetric sounds behavior for the entire project. 3. In the viewport, we created a new `Class.Part` that matched the size of the car. This defined the entire region we wanted to sound to emit from. 4. In the **Properties** window, we set the following properties for the part to ensure that our part didn't fall through the ground, collide with the car model, or obscure the visibility of the car: - `Class.Part.Anchored` = **Enabled** - `Class.Part.CanCollide` = **Disabled** - `Class.Part.Transparency` = **`1`** 5. We then added a `Class.Sound` object to the invisible part and set the following properties for the `Class.Sound` object so the raindrop sound started playing when players would load into the experience, looped continuously, and became audible when a player was 60 studs away/reached its maximum volume at 15 studs away: - `Class.Sound.SoundId` = **`9178663282`** - `Class.Sound.Playing` = **Enabled** - `Class.Sound.Looped` = **Enabled** - `Class.Sound.RollOffMaxDistance` = **`60`** - `Class.Sound.RollOffMinDistance` = **`15`**_The edges of our part can be seen in white outlining the car._ This process allowed the sound to emit from the entire surface of the invisible part. If the player was inside of the part, the raindrop audio would play all around the player's head. The larger you make your own volumetric audio emitters, the more spread and area the volumetric sound will cover, so experiment with different sizes and shapes for your emitter to get the soundscape you want for your own experiences! ## Nested SoundGroup mixing As human beings, we like to think we are great multitaskers, but in reality it's quite difficult for us to focus on more than 2 things at once, especially in regards to listening to multiple audio sources! In The Mystery Of Duvall Drive, there could be a dozen sounds playing at any given time, such as ambience, music, footsteps, rain, or UI notifications, but we needed a way to make sure that it didn't feel as though they were all competing for your attention. For this reason, we decided to nest `Class.SoundGroup|SoundGroups` in child/parent relationships so we could create an audio mix to ensure both the organization and consistency of the project's sound design. **[Nesting SoundGroups](/docs/en-us/sound/groups.md#nest-soundgroups)** is a new feature that allows you to organize `Class.SoundGroup|SoundGroups` together into meaningful categories under a mix tree for further audio functionality, such as being able to fine-tune the volume of many audio sources at once, or apply common audio effects at a lower CPU cost. As we took a very progression-oriented approach to the mix in this project, we wanted sounds that directly contribute to player progression in the puzzles (puzzle and key object interaction audio) to be given priority over sounds that don't contribute to their progression (wind, rain, footsteps, atmosphere audio). By nesting child `Class.SoundGroup|SoundGroups` into parent `Class.SoundGroup|SoundGroups` of different priority levels, we could then add a `Class.CompressorSoundEffect` that would lower the volume of low-priority `Class.SoundGroup|SoundGroups` in real time whenever audio from high-priority `Class.SoundGroup|SoundGroups` started playing. This process is called [ducking](/docs/en-us/sound/groups.md#ducking). _A collapsed view of our main audio mixer for this experience._ _The Properties window for the CompressorSoundEffect that we added to the low-priority SoundGroup._ The `Class.CompressorSoundEffect` has five main properties we had to set in order to lower the volume of low-priority audio whenever high-priority audio played: | Property | Description | | --- | --- | | `Class.CompressorSoundEffect.Threshold` | How sensitive the compressor is to the incoming signal to lower the volume from a `Class.SoundGroup`. The lower the number, the less loud the high-priority sound has to be to start taking effect. | | `Class.CompressorSoundEffect.Ratio` | How much you want to lower low-priority sounds. A higher number basically means more volume ducking! | | `Class.CompressorSoundEffect.Attack` and `Class.CompressorSoundEffect.Release` | How quickly the volume reduction fades in and out when activated. There are no hard and fast rules here, tune them to fit your soundscape using your own judgment and ears! | | `Class.CompressorSoundEffect.Sidechain` | Which `Class.SoundGroup` we want to duck the low-priority sounds, as we have placed the compressor on the **Low** `Class.SoundGroup`. This is what makes the magic happen! | For more information on these properties and the whole process of prioritizing audio from different `Class.SoundGroup|SoundGroups`, see [Nesting SoundGroups](/docs/en-us/sound/groups.md#nest-soundgroups) and [Ducking](/docs/en-us/sound/groups.md#ducking). --- title: "Develop a moving world" url: /docs/en-us/resources/the-mystery-of-duvall-drive/develop-a-moving-world last_updated: 2026-06-29T19:34:10Z description: "Explains the environmental design concepts used in The Mystery of Duvall Drive." --- # Develop a moving world Creating movement in any environment within an experience helps it to instantly feel more immersive and realistic to our world, whether that's from ambient tree movement, reactive doors from player interaction, or even boxes that move when they bump into them. Studio has many unique methods to create motion in order to help worlds feel more alive, including a physics system, `Class.TweenService`, and animations, and analyzing your experience's specific needs can help you determine which one to use. In this section, we'll demonstrate how we determined what type of movement we wanted to create in Studio, and what tools we used to accomplish these distinct goals. ## Create the storm The storm went through many iterations before we settled on what is live in The Mystery of Duvall Drive. Early on, we thought about the storm as a giant obsidian pillar, and in later iterations we considered it to be a giant portal to the corrupt space. After experimenting with many different storms with unique looks and feels to them, we settled on a storm with a smaller central "eye" because: - The storm should give players a sense of the **impact of this event on the world**, including trees blowing and debris flying around. - The spinning vortex of the cloud themselves should give players a peek at the central portal **without revealing everything**. This would encourage players to investigate closer to see what's going on. - The tighter point of light would allow us to **focus on the composition of the house**, which is both the main character and where most of the gameplay is located. To make the storm feel dynamic, aggressive, and ever-changing within its environment, we used the following systems and features: 1. **`Class.TweenService`** - For cloud movement. 2. **Lighting changes** - For creating the cloud to cloud lightning. 3. **[Beams](/docs/en-us/effects/beams.md)** - For the "volumetric lighting" and the lightning bolts. 4. **[Particle Emitters](/docs/en-us/effects/particle-emitters.md)** - For debris flying up to the portal and flying around due to the wind blowing. 5. **[Animations](/docs/en-us/animation.md)** - For the trees that were blowing in the wind. ### Add clouds with textures While [dynamic clouds](/docs/en-us/environment/clouds.md) are great for normal, high altitude realistic clouds, we needed something that felt dramatic and that we could more heavily direct and customize. To do this, we applied [surface appearance](/docs/en-us/art/modeling/surface-appearance.md) objects with semi-transparency to a series of heavily stacked and layered cloud meshes in order to fake cloud cover. Why did we stack them and layer them so heavily? Because when each cloud mesh moves at different speeds, they intersect and create cloud forms that go inside and out of each other. This process made the clouds feel a bit more dynamic and natural, despite just being spinning discs. It was also important that the clouds were [semi-transparent](/docs/en-us/resources/beyond-the-dark/layered-clothing.md#import-to-studio-1), because we wanted players to be able to peek through them to see something bright in the center prior to arriving at the house! _A single cloud mesh._ _Layered cloud meshes without their textures!_ Since each cloud mesh needed to be massive to fully surround the house and convey how enormous the storm was, we knew we needed to tile the texture we wanted to use on the individual cloud meshes so that it would heavily repeat throughout the surface of the mesh. We tested the materials we made for the cloud on these simple parts, then applied them to the vortex! Unlike particle emitters or beams, meshes allowed us to be able to bounce light off of each mesh, which was important when we wanted to implement cloud-to-cloud lightning. We also modeled in the twisting so that lighting bouncing off of it would look like it had depth! This was important especially in situations where the performance demands of the experience dropped the quality levels of our surface appearance objects. _Once we started adding lighting to it, we needed to add details to the meshes to make them react better to lighting!_ ### Rotate cloud meshes After we were satisfied with the overall visual appearance of the clouds, we needed to get it moving! We had the general shapes of each cloud layer in place, but it took some trial and error to make sure the spinning effect looked good in practice. We initially tried using [constraints](/docs/en-us/physics/mechanical-constraints.md) to introduce velocity that would physically drive the clouds to move. This was more difficult than we wanted it to be to iterate later, and the player would never interact with it, so we didn't need it to be as accurate in its movement. We wanted an easy-to-use method to rotate instances that were either too far to be interactable, such as clouds, or too small or decorative to be important for gameplay/physics, such as indoor furniture like small lamps. We decided to use a `Class.LocalScript` to reduce client-server bandwidth, allow for a smoother movement, and have each cloud mesh be able to have a different rate of rotation and delay. To make it more generic, we also made it possible to specify the axis of rotation. It is possible to use 3 attributes, but for our case we used 3 values: `Axis`, `Delay`, and `Speed`. As in many cases in the demo, we used a `LocalSpaceRotation` tag so that we were able to manage affected instances in Studio using an instance tagging plugin. We utilized only a single `Class.LocalScript` that handled all tagged instances using the `Class.CollectionService` so that we didn't have a ton of scripts to maintain throughout the development process. In our demo, portions of the world are cloned from the `Class.ServerStorage` into the workspace as needed, so we needed to handle cases where tagged objects were created and destroyed. With `Class.LocalScript|LocalScripts`, we also have to be aware of the streaming, where meshes and their child values might be streamed in and out. We processed initially placed objects in the `Init()` function, and connected to `Class.CollectionService.GetInstanceAddedSignal` and `Class.CollectionService.GetInstanceRemovedSignal` for tagged objects to handle newly created/destroyed objects. The same `SetupObj` function was used to initialize new objects in `Init()` and in `Class.CollectionService.GetInstanceAddedSignal`. ```lua local function Init() for _, obj in CollectionService:GetTagged("LocalSpaceRotation") do if obj:IsDescendantOf(workspace) then SetupObj(obj) end end end CollectionService:GetInstanceAddedSignal("LocalSpaceRotation"):Connect(function(obj) objInfoQueue[obj] = true end) CollectionService:GetInstanceRemovedSignal("LocalSpaceRotation"):Connect(function(obj) if objInfo[obj] then objInfo[obj] = nil if objInfoQueue[obj] then objInfoQueue[obj] = nil end end end) Init() ``` `objInfo` is a map that has information for all relevant objects, such as their rotation speed and axis. Note that we do not call `SetupObj` from `Class.CollectionService.GetInstanceAddedSignal` immediately, but we added an object into `objInfoQueue`. With streaming and cloning objects on the server, when `Class.CollectionService.GetInstanceAddedSignal` is called, we might not yet have had our `Axis`, `Delay`, and `Speed` values, so we add the object into a queue, and called `SetupObj` on subsequent frames from the `Update` function until the values were there and we could read them into per object "info" structure. We rotated the instances in the `Update` function connected to heartbeat. We got the parent transform (parentTransform), accumulated a new rotation angle (`curObjInfo.curAngle`) based on rotational speed of this object, calculated the local transform (`rotatedLocalCFrame)`, and finally set it to the `Datatype.CFrame`. Note that both parent and the object can be a `Class.Model` or `Class.MeshPart`, so we had to check IsA("Model") and use either a `PrimaryPart.CFrame` or `Datatype.CFrame`. ```lua local parentTransform if parentObj:IsA("Model") then if not parentObj.PrimaryPart then -- primary part might might not be streamed in yet continue -- wait for primary part to replicate end parentTransform = parentObj.PrimaryPart.CFrame else parentTransform = parentObj.CFrame end curObjInfo.curAngle += dT * curObjInfo.timeToAngle local rotatedLocalCFrame = curObjInfo.origLocalCFrame * CFrame.Angles( curObjInfo.axisMask.X * curObjInfo.curAngle, curObjInfo.axisMask.Y * curObjInfo.curAngle, curObjInfo.axisMask.Z * curObjInfo.curAngle ) if obj:IsA("Model") then obj.PrimaryPart.CFrame = parentTransform * rotatedLocalCFrame else obj.CFrame = parentTransform * rotatedLocalCFrame end ``` We checked for a valid `Class.Model.PrimaryPart` to be set to handle streaming. If an Update was called on our object while a `Class.Model.PrimaryPart` (that can point to a child mesh) wasn't streamed yet, we would just skip the update. The current system is a second iteration of object rotation, and the previous system worked differently: the values were 12 times different! To keep the same data, we converted it in our script, like "12 * obj.Speed.Value". ### Design lightning strikes Because Studio doesn't offer an out of the box lightning generator, and the particle system had some limitations that wouldn't work for the hero lightning strikes, we had to get creative with a solution for the hero lightning strikes. We decided on two main systems to make up the lightning: textured beams for the hero lightning strikes coming from the eye of the storm are scripted textured beams that reveal and sync with audio and post process effects, and a simple particle effect for the distant cloud-to-cloud lightning. #### Texture beams We'd typically either use a sequencer or timeline tool to drive the timing of a lighting bolt strike effect like this, but since Studio doesn't offer this functionality yet, we decided to write scripts that would control lighting bolt timing. The scripting of this effect is fairly simple, but it accomplishes the following important goals: 1. Elements of the lightning bolt strikes, such as their textures, brightness, and delays, are randomized with every strike. 2. Audio and post FX changes are in sync with strike FX. 3. Players that are either indoors or in the corrupt area would not be able to see or hear them. We have a server-side `Class.Script` that calculates various parameters and timings, sends them to all clients, and waits for a random amount of time: ```lua local function LightningUpdate() while true do task.wait(rand:NextNumber(3.0, 10.0)) local info = CreateFXData() lightningEvent:FireAllClients(info) end end ``` Inside `CreateFXData`, we fill in the info structure, so that all clients get the same parameters. On the client side (**LightningVFXClient**), we check if this client should run the FX: ```lua local function LightningFunc(info) … -- no FX when indoors if inVolumesCheckerFunc:Invoke() then return end -- no FX when not in the "normal" world if not gameStateInfoFunc:Invoke("IsInNormal") then return end … ``` In addition, we run the sequence to set textures, positions, and brightness, run tweens, and use `Library.task.wait()|task.wait(number)`. Randomized parameters are from the info structure that we received from the server, and some numbers are fixed. ```lua beam.Texture = textures[info.textIdx] beamPart.Position = Vector3.new(info.center.X + og_center.X, og_center.Y, info.center.Y + og_center.Z) -- Wipe beam.Brightness = 10 ppCC.Brightness = maxPPBrightness ppBloom.Intensity = 1.1 bottom.Position = top.Position tweenBrightness:Play() tweenPPBrightness:Play() tweenPPBrightness:Play() tweenBottomPos:Play() tweenBrightness.Completed:Wait() -- audio if audioFolder and audioPart then if audioFolder.Value and audioPart.Value then audioUtils.PlayOneShot(audioObj, audioFolder.Value, audioPart.Value) end end task.wait(info.waitTillFlashes) -- and so on ``` To check if a player is indoors we use a helper `inVolumesCheckerFunc` function, which goes over pre-placed volumes approximating indoor areas, and checks if player position is inside any of them (PointInABox). We could have used touch-based detection, but we found out that when a player takes a seat inside the volume, they are no longer "touching" the volume. Testing a point in a few boxes is simpler, and we do it only when a player moves far enough from the previously tested position. To check if a player is in corrupt areas, we invoke a helper `gameStateInfoFunc` function, which checks the current game state. To play a random sound from a folder, we also used a helper `PlayOneShot` function. For the lightning bolts themselves, these were super easy to create in Photoshop; we drew a squiggly line, then added an "Outer Glow" layer effect. #### Utilize particle emitter systems The hero lightning strikes are supported by a particle system that suggests distant lightning by creating the impression of a layer of clouds in the background catching light from distant strikes, or cloud-to-cloud lighting. We achieved this effect through a very simple particle system which flashes a cloud billboard on the periphery of the main storm cloud. The system emits a cloud particle periodically with a randomized transparency curve: ### Make trees blow in the wind After we had the clouds and the lightning working the way we wanted it to, we then needed to add two other major components of a storm: the wind and the rain! These elements presented a few challenges, including needing to work within Studio's current limitations of our physics and special effects systems. For example, making trees move with actual wind isn't possible in today's engine, so we utilized [particle emitter](/docs/en-us/effects/particle-emitters.md) effects and [custom character animations](/docs/en-us/animation/editor.md#create-an-animation) for the trees. We knew to really sell the effect of the wind and rain, we needed the trees themselves to move. There are a few ways you can do this within the engine, including moving parts using [plugins](/docs/en-us/studio/plugins.md) that are publicly available, using `Class.TweenService`, or animating models directly. For our purposes, animations gave us the ability to control the motion we wanted out of our trees, and it allowed us to use a single animation we could share among all trees within the experience. > **Warning:** There are pros and cons with any of these systems, and one of the cons for animations specifically skinned meshes are not currently instanced. This means that the more skinned meshes you use in your experience, the greater the impact on performance because of the memory cost, so we used them sparingly for the effect we wanted. At some point this will be resolved, so be sure to check the [rigging and skinning documentation](/docs/en-us/art/modeling/rigging.md) for the most up-to-date information. We started by skinning several trees from the [Endorse Model Pack - Forest Assets](https://www.roblox.com/library/6432306802/Forest-Pack). Since these trees already existed, and our experience took place in the Pacific Northwest, it saved us some time early on from having to create each tree model. _The Forest pack contains several tree types, which can save you time in your own experiences._ After we picked our trees, we knew we needed to skin them. [Skinning a mesh](/docs/en-us/art/modeling/rigging.md) is the act of adding joints (or bones) to a mesh in another 3D modeling application, such as [Blender](https://www.blender.org) or [Maya](https://www.autodesk.com/products/maya/overview), then applying influence to those joints/bones to move the mesh. This is most commonly used in [humanoid characters](/docs/en-us/art/modeling/skin-a-humanoid-model.md), but with [custom characters](/docs/en-us/art/modeling/skin-a-simple-mesh.md), you can skin pretty much anything. We knew we wanted to save time and reuse the same animation, so we built our first tree rig and made sure the joint names were generic because we wanted to use these same names in the rigs for the other trees. We also knew we needed to include primary, secondary, and tertiary joints/bones for the trunks to bend with the wind, the branches to swing, and the leaves to seem like they were shaking in response. For this process, we needed to create a **secondary motion**, which is an animation concept where any action causes other parts of the object to react to that action and appear to catch up with the initial movement. _The trees have primary, secondary, and tertiary joints so that we could have believable movement from being blown around by the wind._ Once we had created our joints/bones, it was time to create a test animation to move around all the joints and bones in Studio to see if it moved the way we wanted it to. To do this, we had to [import the tree into Studio](/docs/en-us/resources/beyond-the-dark/custom-characters.md#import-the-mesh-to-studio) through the **Custom Rig** setting in the **Importer**, then move/animate the mesh using the [Animation Editor](/docs/en-us/resources/beyond-the-dark/custom-characters.md#import-the-mesh-to-studio). We set up the materials and textures after these tests, but you can see the result below. _The same hierarchy within Studio._ After we were happy with the results on that tree, it was time to test the same animation on a different tree! We already knew it was going to be the same animation between the different rigs for each tree type, so we just made sure our animation looked like it was general enough to work between a tall Redwood and stout Beechwood tree! _The animation we imported on the Redwood tree._ To do this, we took the Beechwood tree from that Forest Pack and built a similar rig, using the same exact naming for the joints. This was so the animation we had previously imported could be applied to this tree too. Since the animations were all based on rotating joints, it didn't matter how big, small, tall or wide the tree was! _The Beechwood tree has the same exact naming for its joints, just not the same amount. This is fine since the animation system will only apply animation to those specific joints that match the name in it! For this reason, we could apply the same animations to anything that matched the joint names!_ After we [rig and skin](/docs/en-us/art/modeling/rigging.md) the Beechwood tree, we could then import it and apply the exact same animation. This meant iterating and editing only needed to be done on one file, and it also saved on performance with fewer animations when running the experience. _Using the **Animation Editor**, we could apply the same Redwood tree animation to the Beechwood tree!_ Once we had all the tree types we wanted animated, we made each into [packages](/docs/en-us/projects/assets/packages.md) so we could continue to edit and update while playing several of the animations around the main area of the experience. Since we knew they had a performance cost, we used them sparingly around the house where the effect was most valuable! In the future as this becomes more performant, you'll be able to add more and more skinned mesh instances! _We used animated trees immediately around the house where the vortex was strongest and the visual effect would be the most impactful for players._ ### Make storm debris We wanted the rain to appear heavy, and for the fog and debris to blow through the trees. To do this, we set up a few invisible parts to act as particle volumes with child [particle emitters](/docs/en-us/effects/particle-emitters.md) immediately below the large storm clouds. Because of the particle count limit in Studio, we couldn't use one particle emitter for the entire space. Instead we added several that were the same size as each other in a grid pattern over the playable area space, because the presence of the trees means the players wouldn't be able to see very far. _We used several volumes to both get the amount of rain, and the specific coverage of rain that we wanted._ The rain particles leveraged a new particle emitter property `Class.ParticleEmitter.Squash` that allows you to make a particle longer, or squatter. It is particularly useful for rain because it meant we didn't need a large rain texture, just stretch the one that was there. Just know that if you increase the value of `Class.ParticleEmitter.Squash`, you may need to increase the overall `Class.ParticleEmitter.Size` property too so that it's not too skinny! Overall, it was just a matter of playing around with the values until we got the rain heavy enough, but not so much that it blocked the visibility of the experience! _A Squash value of 3 starts to stretch the texture longer._ _A Squash value of 20 stretches the particles much longer, but we also needed to increase the Size value too._ For the mist, fog, and leaves blowing through, it was much simpler to add a single larger part volume covering fewer areas because we didn't need a ton of particles running at one time. We started by setting up a volume and got the frequency of the particles where wanted them. _There ended up being a few particle part volumes so we didn't have particles entering the house, and because we didn't feel they needed to move through the trees like the fog did._ _The fog particle part volume was much larger since the particles were large, and we didn't need to be as precise with location._ After that, we made our leaf blowing and wind textures, and set the particles to all rotate/move at different rates and start at different speeds. This meant that the larger fog particles would interact more naturally and not look so much like a repeating texture, especially given their size. _Fog particle_ _Leaf particle_ The result was some great action between the trees moving, the window blowing, and the lightning to create the effect of the storm surrounding the central eye of the storm. ### Set up the eye of the storm The fractured stone eye with a glowing core is meant to give players the first hint that there is something sinister and arcane occurring at the house that they should explore further. Since our scene is dark and the eye is far up in the sky, it was important to create a believable fractured stone silhouette, but it wasn't as important to create believable stone surface details because players wouldn't be able to see that. Knowing what is realistic for your players to see within your scene's lighting before putting in a ton of time into unnecessary details can save you many resources in the development process. _Setting up the final lighting in your scene early on can save you a lot of unnecessary work. You wouldn't be able to see surface details on the rings with our scene's final lighting so there was no need to spend the time putting them there!_ The distance from the player also meant that we could rely entirely on a normal map for the surface details of the eye so the mesh is just a plain sphere! We sculpted the details into a high poly mesh and baked its normal map onto a much lower poly sphere so that we could get all that beautiful detail without the massive performance cost. _High poly sculpt_ _Low poly mesh_ _The low poly mesh with the normal information from the high poly sculpt baked in_ In order to add a supernatural feeling to the eye and to emphasize its presence, we decided to create a glowing, neon magma that would seep through its cracks. While there's no emissive channel for surface appearance, we overcome this hurdle by creating the eye out of 2 spheres: one for the rocky outside surface and a second, slightly smaller one for the glowing magma. In [Substance Painter](https://www.substance3d.com/), we created a base color texture for the outer sphere with transparency in the areas where we wanted the inner core to come through. In [Blender](https://www.blender.org), we "vertex painted" the inner sphere for a cheap and easy way to get some color variation on it. _The vertex painting on the inner sphere. We created a gradient that was lightest around the eye in order to give a greater sense of depth and visual interest._ Another challenge we ran into when creating the eye was imposed by our use of [streaming](/docs/en-us/workspace/streaming.md) combined with the eye's distance from the player. Given the centrality of this structure, we wanted it to always be visible despite its distance but, without any hacks to its mesh, players were not able to see the eye unless they were in the solarium. We were able to force the eye's constant presence in the scene by adding some geometry to the eye and its rings. This geometry sits right below the terrain's surface, and this is enough to trick the engine into thinking the sphere is closer to the player than it is and always streaming it in. This should be done pretty sparingly though as forcing too many large objects to be streamed in could negate the benefits of streaming enabled and negatively impact performance. We were able to add movement to the eye and its rings thanks to the same script we used to [rotate the cloud meshes](#rotate-cloud-meshes). For a final touch, we decided to add a hint to the presence of another world beyond the clouds, but we had to take a creative approach in order to avoid adding more geometry to the scene and additionally having to deal with the previously mentioned hurdles posed by streaming enabled. We created a scene that had a lot of depth due to the relative size and distance of objects, rendered an image of this scene, then used said image as a [decal](/docs/en-us/parts/textures-decals.md) on a part placed just behind the eye of the storm. We used the same method for rotating this part as we used for the eye and its rings. _The image we used to create an illusion of a world beyond the clouds. When players are far away from something, a simple image might be enough to create the illusion of more depth and complexity in your scene!_ ## Make the expanding pantry One of the most fun things to produce were the corrupt spaces, where we could subvert players expectations of reality by literally changing it around them. For example, in the father's puzzle we wanted to emulate a moment similar to a nightmare where no matter how fast you run, the room feels like it keeps getting longer. We decided to make an expanding pantry that would run away from players as they were looking for ingredients to turn the room back into its normal state. We set this up with a simple movement of the walls, and a clever layout of our rooms that would appear on either side of the pantry. In the room's normal state, the pantry was a simple hallway, but in the corrupt space, it was actually much longer with several wings and a false wall! _The corrupt state of the kitchen pantry._ _The false wall moving away from players._ The false wall was a model group that we would move back the moment players entered a trigger volume, which was a transparent part earlier in the pantry that they would walk through. That trigger was also used in a script similar to ones used on all our doors, which called the `Class.TweenService` to move from one goal to another. We used part volumes to tell the tweening operation where the start and end positions were for the wall. _The part volume triggers a false wall behind it to move to its end point. It is made visible in this image with a yellow tint._ _Target_Closed was a generic goal part we used on all our doors for where they should rotate. Here it was repurposed to tell the hallway wall where to go._ Because `Class.TweenService` is such a general system, all our wall data model had to contain was the same components. For example, a general "Door_Script" script plays a sound defined by a "value" below the "Grow_Wall" model. That same script, with some modifications in the following code sample, also triggered audio for the pantry moving. This added a lot to the movement! ```lua local Players = game:GetService("Players") local TweenService = game:GetService("TweenService") local model = script.Parent local sound = model.Sound.Value local trigger = model.Trigger local left = model.TargetL_Closed local right = model.TargetR_Closed local tweenInfo = TweenInfo.new( model.Speed.Value, --Time/Speed of Door Tween Enum.EasingStyle.Quart, --Easing Style Enum.EasingDirection.InOut, --EasingDirection 0, --Repeat Count false, --Reverse true 0 --Delay ) local DoorState = { ["Closed"] = 1, ["Opening"] = 2, ["Open"] = 3, ["Closing"] = 4, } local doorState = DoorState.Closed local playersNear = {} local tweenL = TweenService:Create(left, tweenInfo, {CFrame = model.TargetL_Open.CFrame}) local tweenR = TweenService:Create(right, tweenInfo, {CFrame = model.TargetR_Open.CFrame}) local tweenLClose = TweenService:Create(left, tweenInfo, {CFrame = model.TargetL_Closed.CFrame}) local tweenRClose = TweenService:Create(right, tweenInfo, {CFrame = model.TargetR_Closed.CFrame}) local function StartOpening() doorState = DoorState.Opening sound:Play() tweenL:Play() tweenR:Play() end local function StartClosing() doorState = DoorState.Closing --model["Door"]:Play() tweenLClose:Play() tweenRClose:Play() end local function tweenOpenCompleted(playbackState) if next(playersNear) == nil then StartClosing() else doorState = DoorState.Open end end local function tweenCloseCompleted(playbackState) if next(playersNear) ~= nil then StartOpening() else doorState = DoorState.Closed end end tweenL.Completed:Connect(tweenOpenCompleted) tweenLClose.Completed:Connect(tweenCloseCompleted) local function touched(otherPart) if otherPart.Name == "HumanoidRootPart" then local player = Players:GetPlayerFromCharacter(otherPart.Parent) if player then --print("touch") playersNear[player] = 1 if doorState == DoorState.Closed then StartOpening() end end end end ``` Once we had the false wall moving to the back of the room, we needed the rest of the content to move with it. To do that, we needed all loose items on the pantry to be welded to the wall as it moved. Using [Weld Constraints](/docs/en-us/physics/mechanical-constraints.md#weldconstraint), we were quickly able to weld all objects to the pantry wall to move as a single object. Doing this meant we had the option to unweld these items so players could bump into them and knock them around! ## Make the corrupted treehouse Studio is a fantastic physically-based engine that you can use to create everything from a swinging gate to a spinning platform. With our demo, we wanted to use physics to create a sense of realism in an otherwise unrealistic set of environments. Using just a few **constraints**, you can create some fun and challenging obstacle courses within your own experiences! **[Constraints](/docs/en-us/physics/mechanical-constraints.md)** are a group of physically-based motors that align objects and constrain behaviors. For example, you can use a [rod constraint](/docs/en-us/physics/mechanical-constraints.md#rodconstraint) to connect to objects in order to keep them a fixed distance from each other, or the [rope constraint](/docs/en-us/physics/mechanical-constraints.md#ropeconstraint) to have a lamp hanging from the end of the line. For the son's puzzle in which players are transported to the corrupt state of the study, we wanted to literally flip the world on its side. Doing so would subvert players' expectations of reality and the rules there, while still using the physics system as it was intended! _The son's puzzle started with the players in the same room, but everything was sideways._ Once players worked down to the main area of the puzzle, they were greeted with a familiar sight on Roblox: an obstacle course. This particular obstacle course consisted of several spinning platforms and rotating walls, along with "safe areas" that progressed the story. We'll focus on the rotating/spinning elements. _The mind-bending appearance hid the fact that the gameplay here was very simple._ Why did we use constraints here? Because `Class.TweenService` or other methods wouldn't move the player while they stood on them. Without the object moving the player, someone could jump on a platform and it would spin out from under them. Instead, we wanted players to navigate through a spinning platform while trying to make their jump onto the next one. Because of this approach, players felt rooted where they stood while making a decision with how to proceed through the course, and we didn't need to do anything special to ensure they moved with a rotating surface! _You could watch your friends spinning around while trying to navigate the obstacle course too._ To do this, we needed to first use assets from our current kit and add any new content for a visual effect. We made a few incomplete walls and platforms with holes in them to tell the story of the grandmother building the treehouse. Because we didn't want to create a bunch of unique platforms, we made 4 different base pieces and railing pieces separately. This allowed us to mix and match individual base and railing pieces to have plenty of variety. We knew that since we were using constraints, we wouldn't be able to anchor these meshes because they wouldn't move even with the presence of a constraint/motor driving them. The constraint needed to be a child of something that was anchored in order for the platform to not just fall out of the world. We solved this through a part we named **Motor_Anchor** that had a [hinge constraint](/docs/en-us/physics/mechanical-constraints.md#hingeconstraint) to drive the overall movement of the platform. After that, we needed the two meshes to move as one, so we created a part we named **Motor_Turn**, then we welded the two meshes to it. This way the constraint would be able to work on a single part, as opposed to multiple hinges working with multiple parts. It was now time to set up the actual behavior of the hinge constraint itself, and add the attachments that would act as the orientation of the part and the constraint together. We placed the turning attachment on the Motor_Turn, which the walkway pieces were welded to, and another attachment for the anchor behavior on the Motor_Anchor itself, beside the hinge constraint. Since this needed to rotate on its own, as opposed to being influenced by the player (like a door hinge), we set the `Class.HingeConstraint.ActuatorType` to **Motor**, treating the constraint like a self-moving motor. To keep the platforms spinning at a constant speed, we then set up the `Class.HingeConstraint.AngularVelocity`, `Class.HingeConstraint.MotorMaxAcceleration`, and `Class.HingeConstraint.MotorMaxTorque` properties to values that would allow movement and prevent interruption if a player jumped on it. _Attachment0 was essentially the anchor for the hinge and Attachment1 represented the hinge itself. We had the hinge constantly rotating, but you can also use a hinge constraint for doors._ Now we needed to make the rotating walls. The walls needed to rotate on their apparent center, and we knew we wanted them to be able to handle any orientation relative to the rest of the level. Like the platforms, we constructed these so that all the walls were unanchored and welded to the Motor_Turn. _We wanted to reuse as much of the actual treehouse meshes to save on performance, so we followed a similar path as the platforms. Several wall types were made that could stick together in different combinations for some variation._ We used `Class.Texture` objects on top of `Class.SurfaceAppearance` objects to add some variation to our base materials. [Textures](/docs/en-us/parts/textures-decals.md), similar to Decals, allow you to place an image on a plane of a mesh. This can be useful if you want to add dirt to a brick wall, or make wood look aged while using the same base wood material. `Class.Texture` objects have a slightly different behavior than `Class.Decal` objects in that you can tile and offset the image however you want, which is very useful if you want to be able to scale your overlay texture and don't mind it repeating! _You can see both the similar behavior and set up for the hinge constraint, and also how we used `Class.Texture` objects._ Once we'd tested a few platforms and rotating walls, we made several variations and played with their placement to make sure the obstacle course was challenging, mind-bending, and also clear where the player needed to go! It took some tuning to both their values and positions to get them to run well. We had several points where the platforms and walls were hitting each other or the surroundings, but with some moving around and frequent testing, we were able to land on the settings we have in the demo! If you're not sure what your physical objects are hitting, you can toggle on **Collision fidelity** from the [Visualization Options](/docs/en-us/studio/ui-overview.md#visualization-options) widget in the upper‑right corner of the 3D viewport. ![A close up view of the 3D viewport with the Visualization Options button indicated in the upper-right corner.](../../assets/studio/general/Visualization-Options.png) _When collision visualization is disabled, you can see the normal geometry representation that displays in-game._ _When collision visualization is enabled, you can see the tree leaves don't have collisions, so they won't interfere with the spinning platforms or walls._ As you can see below the doorway/window holes are visible, but the smaller details like the sub-paneling are not. This is because the `CollisionFidelity` property for the walls was set to **Box**. We didn't need the precision for these panels, so to save on performance cost, this was detailed enough for players to jump onto. With the platforms and rotating walls done, we only needed to add detailing assets like boxes and lamps, then it was ready to be played! --- title: "Foundational gameplay systems" url: /docs/en-us/resources/the-mystery-of-duvall-drive/foundational-gameplay-systems last_updated: 2026-06-29T19:34:10Z description: "Explains the gameplay design used in The Mystery of Duvall Drive." --- # Foundational gameplay systems The following systems were the foundation in establishing the gameplay we wanted for [The Mystery of Duvall Drive](https://www.roblox.com/games/7902470429/The-Mystery-of-Duvall-Drive). ## GameStateManager The **GameStateManager**/**GameStateClient** is probably the most complicated system in the experience, as it deals with: - Starting players within the lobby, starting the countdown to teleporting the group to the main gameplay area, and teleporting players to reserved servers. - Cloning corrupt rooms, async streaming them, and teleporting players to and from a specific assigned `Datatype.CFrame` coordinate. - Grabbing and placing seals mechanics. - Locking and unlocking doors. - Initializing the final teleport to the foyer and playing the finishing cutscene. We implemented it as a simple state machine (Update function), and the states are in **DemoConfig** (GameStates enum). Some states deal with initial lobby/reserved server teleport, while others deal with finding a mission, triggering the puzzle, and solving the mission. Note that apart from seals, we tried to not have mission-specific code in the **GameStateManager**. GameStates is mostly server-side, but when the client needs to do something, such as show countdown, lore, or disable streaming pause UI, server and client (GameStatesClient) communicate via a remote event called `GameStateEvent`. As with most cases, the event payload has event "type" (Config.GameEvents) as the first parameter, and event specific data after that. ### Teleportation states There is a group of 3 states that run three unique cutscenes that hide the teleportation to the corrupt room: Warmup, InFlight, and Cooldown. **Warmup** runs for the whole duration and ends with an almost black screen in which the 3D world is no longer visible. During this time, we clone the room, get desired player positions in the corrupt room for each player, call `Class.Player.RequestStreamAroundAsync`, and transport players to a specific assigned `Datatype.CFrame` coordinate within the corrupt room. This type of teleportation might trigger a streaming pause. When a streaming pause occurs, the client displays a warning message. We disabled this default UI to keep the experience immersive. > **Info:** Players cannot transport to a specific assigned `Datatype.CFrame` while they are sitting, so the server sends a message to the client to "unseat" players before the teleport, as well as disable the seating state while the teleportation is in progress. While streaming is being handled, **InFlight** runs, keeping a slightly pulsing dark screen. When both `Class.Player.RequestStreamAroundAsync` returns and clients inform the server that streaming pause is off and each player is close enough to the desired location, we cancel the InFlight cutscene, and start the Cooldown cutscene. The goal of **Cooldown** is to make the 3D world visible again by smoothly removing the dark screen. If `Class.Player.RequestStreamAroundAsync` takes too long to return, or the client doesn't report that the streaming pause is off, we still proceed to the Cooldown cutscene after a timeout of several seconds. A similar set of Warmup, InFlight, and Cooldown cutscenes occur when we teleport the player back to the normal state of the room, **TeleportWarmupBack**, **TeleportInFlightBack**, and **TeleportCooldownBack**, and at the end of the experience, we also run **TeleportWarmupFinal**, **TeleportInFlightFinal**, and **TeleportCooldownFinal** to teleport players into the foyer for the finishing cutscene. ### Lighting and atmosphere states We knew that we wanted each room's normal and corrupt state to have a different visual appearance so it could give players clear visual feedback that they were in a completely different location. Game states allowed us to change the lighting and atmosphere properties for normal and corrupt rooms, in which the GameStateManager selected which instances to use based on if players are teleporting from the normal to the corrupt state of the room (`TeleportWarmup`), or vice versa (`TeleportWarmupBack`). Events playing during the teleport make the whole screen either dark or white, so we decided to change the `Class.Lighting` and `Class.Atmosphere` instances at those moments to hide the process from players. To make it simple to change, **DemoConfig** includes maps that define what instances under these services need to change. ### Locking doors game states We wanted to be able to keep players in certain rooms while they finished missions, so we created game states to lock doors: `InMission` and `CanGetSeal`. `InMission` locks players in their active mission room, and `CanGetSeal` keeps the mission room's door locked until they pick up the "restored" seal. We mostly used this to have the doors lock when players return from a mission so that they have an incentive to pick up the seal. After they pick up the seal, doors unlock so they can place it within the seal's location in the foyer. The last mission is unique to this typical process, as the door to the room with its seal is locked until players solve every other puzzle (`EnableRegularMissionDoors`, `EnableOneMissionDoors` functions). ## EventManager **EventManager** allowed us to run "actions" over time by using keyframes, such as: - Interpolating instance properties and attributes. - Running scripts. - Playing audio. - Running camera shakes. We'd ideally use a tool with track-based UI, but for this demo, we typed the keys and property names manually. The **EventManager** system consists of several scripts and an event/function, including: - `EventManager` - Overall logic for creating and stopping events, as well as server-side actions. - `EventManagerClient` - Client-side actions. - `EventManagerModule` - Common code for both server and client-side actions. - `EventManagerConfig` - Small file with a few command declarations. - `EventManagerDemo` - Where all actual events for this demo are defined in game-specific script. - `EventManagerEvent`, `EventManagerFunc` - Remote event and bindable function to run and stop events from the client or server. This is how other systems can set up, run, and stop events. Each event has a name, a section with optional information about cooldown, function to run on start or end, event parameters, and sections with interpolants (interpolating any number of properties or attributes over time), scripts (running registered scripts at keyframes), camera shakes, and playing audio. ### Interpolation Interpolation lets object properties and attributes seamlessly change from one value to another instead of disjointedly jumping between keyframes. We defined interpolants to change a variety of visual effects; for example, the following code snippet shows how we interpolated the `Class.TextLabel.TextTransparency` property on an object defined by `Class.TextLabel` parameter from a value of `1` at the start to `0`, then later back to `1` again: ```lua interpolants = { objectParam = "TextLabel", property = "TextTransparency", keys = { {value = 1}, {time = .5, value = 0}, {time = 2.25, value = 0}, {time = 3, value = 1} } } ``` While we could define which object property or attribute belongs to what like in the following code sample, we wanted to be able to re-use the same events on different "groups of objects" to allow it to work with streaming on the client, and with objects created at run time. ```lua object = workspace.SomeFolder.SomeModel ``` To accomplish this, we allowed referencing by object name and passing named parameters on event start. To find named objects, we allowed specifying a "root" for the event, which let objects to be found by name under this root when the event started. For example, in the following code snippet, the **EventManager** tries to find an object named "Wander" somewhere under `Workspace.Content.Interior.Foyer["Ritual-DemoVersion"]`. ```lua params = { ["RootObject"] = workspace.Content.Interior.Foyer["Ritual-DemoVersion"], }, interpolants = { objectName = "Wander", attribute = "TimeScale", keys = { {value = 0.2} } } ``` We allowed passing parameters into events in the params section, and the scripts running on event start could either change existing parameters, or add more parameters into the "param" table. In the following example, we have `isEnabled` parameter with a default value of false, and the "Enabled" property on an object with the name **FocuserGlow** will be set to the value of `isEnabled`. A script running on event start or a script invoking the event can set isEnabled, so we could use the same event description for both enabling and disabling FocuserGlow. ```lua params = { isEnabled = false }, interpolants = { { objectName = "FocuserGlow", property = "Enabled", keys = { {valueParam = "isEnabled"} } } ``` Parameters allowed us to refer to objects that do not even exist in the beginning of the experience. For example, in the following code sample a function running on event start will create an object, and set BlackScreenObject entry in the params to point to the created object. ```lua {objectParam = "BlackScreenObject", property = "BackgroundTransparency", keys = { {value = 0}, {time = 19, value = 0}, {value = 1}, }} ``` ### Run events, event instances, and connect to triggers To run an event, we would either use a remote event from clients, or a function from the server. In the following example, we passed a couple of parameters to the `RootObject` and `isEnabled` events. Internally, an instance of the event description was created, params resolved to actual objects, and the function returned an ID for the event instance. ```lua local params = { RootObject = workspace.Content.Interior.Foyer["Ritual-DemoVersion"]["SealDropoff_" .. missionName], isEnabled = enabled } local eventId = eventManagerFunc:Invoke("Run", {eventName = "Ritual_Init_Dropoff", eventParams = params} ) ``` We could stop running an event by calling function with "Stop": ```lua eventManagerFunc:Invoke("Stop", {eventInstId = cooldownId} ) ``` Interpolants or other actions that are "cosmetic" (do not change simulation for all players) could be run on clients, which could result in smoother interpolation. In the event description, we could provide a default value for all actions as onServer = true (without it, default is client). Each action can overwrite it by setting its own onServer. To easily connect running an event to a trigger, we used helper functions `ConnectTriggerToEvent` or `ConnectSpawnedTriggerToEvent`, the latter of which finds the trigger by name. To allow the same event to be triggered using different triggers, we could call `eventManagerFunc` with a "Setup" key and a set of trigger volumes. For an example of a trigger volume in action, see [Making the Expanding Pantry](/docs/en-us/resources/the-mystery-of-duvall-drive/develop-a-moving-world.md#making-the-expanding-pantry). #### Event parameters In addition to custom event parameters passed from scripts, other data that can be optionally passed when creating an event includes player, callback (to be called when event ends), and callback parameters. Some events should run only for one player (events with actions running on client), while others should run for all. To make it run for only one player, we used `onlyTriggeredPlayer = true` in the params. Events can have cooldowns defined by `minCooldownTime` and `maxCooldownTime`. The min and max provide a range for scaling based on player count, but we didn't use it in this demo. If we were to have needed cooldown needs to be per player, we had the capability to use `perPlayerCooldown = true`. Each Event has a duration in seconds, and cooldown timings and callbacks are based on it. To inform about finishing an event, invoking code could pass a callback and parameters it will get. #### Call scripts We could call `Class.Script|Scripts` at specific keyframes in the **Scripts** section. For example: ```lua scripts = { {startTime = 2, scriptName = "EnablePlayerControls", params = {true}, onServer = false } } ``` In the previous example, the **EnablePlayerControls** `Class.Script` would need to be registered with the event manager module, like so: ```lua emModule.RegisterFunction("EnablePlayerControls", EnablePlayerControls) ``` RegisterFunction must be called in the client script for functions called on the client, and in the server script for `onServer = true`. The function itself will get eventInstance and parameters passed, but in this case, only one parameter is passed with a true value. ```lua local function EnablePlayerControls(eventInst, params) ``` #### Play audio We have limited support for playing [non-positional audio](/docs/en-us/sound/objects.md#volumetric) at keyframes in the **Sounds** section, for example: ```lua sounds = { {startTime = 2, name = "VisTech_ethereal_voices-001"}, } ``` Note that the event finishing callbacks fire when event duration expires, but audio actions might be still playing after. #### Run camera shakes We could define camera shakes in the **cameraShakes** section, like so: ```lua cameraShakes = { {startTime = 15, shake = "small", sustainDuration = 7, targets = emConfig.ShakeTargets.allPlayers, onServer = true}, } ``` "targets" can be initiated only for the player who triggered the event, allPlayer, or playersInRadius to the triggering player. We used a 3rd party script for camera shakes, and the shakes were pre-defined: `eventManagerDemo.bigShake` and `eventManagerDemo.smallShake`. `sustainDuration` could also be passed. ## Missions logic There are 7 missions total, and only 6 of them use seals. Most missions have common parameters, though some are only for missions with seals and teleporting to corrupt rooms. Each mission has an entry in the **DemoConfig** script with a set of parameters in the **Config.Missions** map: - **MissionRoot**: A folder of all non-corrupt versions of objects. - **Doors**: Doors to lock until a player picks up a seal. - **SealName**/**SolvedSealName**: Non-corrupt seal and corrupted seal names. - **SealPlaceName**: Places to put the seal. - **PlacedSealPlaceholderName**: Placeholder object at the place to put the seal. - **TeleportPositionsName**: Name of a folder with placeholder meshes to define player teleport positions and rotations when moving to the corrupt room, and back to the normal area. The same name is used in both cases. - **CorruptRoomName**: Names of the root folders (relative to ServerStorage) for the corrupt rooms. Corrupt rooms clone under TempStorage.Cloned when the mission starts, and they are destroyed when the mission is finished. - **MissionCompleteButtonName**: A cheat button in the corrupt rooms to finish the mission immediately. This is for [debugging purposes](/docs/en-us/resources/the-mystery-of-duvall-drive/main-design-requirements.md#debug-version). - **CheatKey**: The same cheat as a number or `Ctrl``Shift``[Number]`. Some of the mission logic is in the **[GameStateManager](#gamestatemanager)** scripts, as seals and doors provide the main game flow for most missions, but most of the mission-specific logic is in `MissionsLogic` and `MissionsLogicClient` scripts that define several "types" of missions. The type is defined just by the presence of specifically named members in the mission description. There are a few types of missions: - **Use a key on a lock** - The first mission to open a door. This type is defined by `LockName`, `KeyName`. - **Matching items** - 4 missions match items. This type is defined by `MatchItems`. - **Dressing up a mannequin using layered cloth** - 1 mission in the attic has players collect three items. This type is defined by `DressItemsTagList`. - **Click on item to finish** - 1 mission has this type, which is defined by `ClickTargetName`. Each mission type has its own `StartMissionFunc` and `CompleteMissionFunc`. Starting function usually reads parameters from the **MatchItem** map, resolves names to objects, and sets up any click detectors or UI elements. Almost all the logic is on a server, but MissionsLogicClient provides a UI for showing items counter, used in many missions. `MissionLogicEvent` remote event is used for server - client communications, with a small MissionEvents defining types of commands passed. The `MiscGameLogic` script binds some triggers to events and removes debug objects in Release version. Matching items logic allows you to "use" (click while holding) items marked with **PuzzlePieceXX** tags over items with **PuzzleSlotYY** tag. There are a few options available as parameters in **MatchItems** map (if pieces need to be applied in order, if only one of each is required). We could specify names for simple audio and visual FX. When pieces need to be placed at specific locations, an extra "Placement" map provides mapping from pieces tags to names of placeholder parts that define transforms. ## Grabbing We developed a simple grabbing system for holding an object by attaching the object to the character's right arm. Grabbing is implemented in **GrabServer2** and **GrabClient** scripts. It starts in `ProcessClick`, which fires a ray through the clicked/touched point. It then checks if we hit a mesh that can be grabbed, and the hit is within the `maxMovingDist` where we can start grabbing interaction. If the model clicked on has `Class.Attachment|Attachments` called **GrabHint**, we pick the closest to the clicked spot. We remember the grabbed part, the model it belongs to, and either the closest **GrabHint** or the clicked position in the `grabAttempt` structure. If the distance is more than maxGrabDist, the player first needs to walk close enough to the attempted grab spot, so we call `Class.Humanoid.MoveTo`. On each frame, we check if a grab attempt is in progress. If the player is within **reachDist**, we start playing **ToolHoldAnim**. When a player is within **maxGrabDist**, the client fires a request to the server to actually grab a model (`performGrab` function). Server side script has 2 main functions: - **Grab** - Handles a client's request to grab a model. - **Release** - Handles the request to release a grabbed model. Information about what each players holds is kept in **playerInfos** map. In the grab function, we check if this model is already grabbed by another player. If so - an "EquipWorldFail" is sent to the client, and it cancels the grab attempt. Note that we needed to handle situations where players grab different parts of the same `Class.Model`, and cancel the grab in this case. If grabbing is allowed, the script creates two `Class.Attachment|Attachments`, one on the right hand and another on the object using a passed grab spot from the client. It then creates a `Class.RigidConstraint` between the two `Class.Attachment|Attachments`. `Class.Constraint|Constraints` and `Class.Attachment|Attachments` are stored under the **CurrentGrips** folder under the player's character. Grabbing also plays a sound, disables collisions on the grabbed object, and deals with Restorables, if needed. For releasing a grabbed model, the client script connects to the **GrabReleaseButton** button in HUD ScreenGui. A `Connected` function fires an event to the server. On the server, release deletes the `Class.Attachment|Attachments` and `Class.Constraint|Constraints`, restores collision, deals with any applicable Restorables, and clears grab data for this client in **playerInfos**. --- title: "Immersive narrative" url: /docs/en-us/resources/the-mystery-of-duvall-drive/immersive-narrative last_updated: 2026-06-29T19:34:10Z description: "Explains the narrative concepts used in The Mystery of Duvall Drive." --- # Immersive narrative Like the [Beyond the Dark](/docs/en-us/resources/beyond-the-dark.md) demo, one of our earliest decisions was to have as much of the gameplay and story told diegetically within the world itself. This can be a difficult process when you want to balance both communicating progress to the player through the user interface while also making them feel as though they really are within the world you've built. To meet these goals for our experience, we broke the way we communicate information to players down into four techniques: 1. **Visual cues** - Objects, images, or arrangement of assets that passively reinforce story or give hints to the player when it makes sense for the world. 2. **Lore** - When a player clicks/taps on specific items in the house, important story information displays **on the entire screen**. This narrative is only visible to the player that clicked/tapped the object. The tone is always informational in tone and phrasing. 3. **Thought bubbles** - When a player clicks/taps on specific items in the house, a text dialogue "reaction" displays **near the object**. This narrative is only visible to the player that clicked/tapped the object. The tone is always first person and observational. 4. **Announcements** - When a player clicks/taps on a corrupted item in the house, third-person dialogue displays on **every player's screen at once**. This narrative sometimes transitions all players to the corrupted state of the room they are in. In this section, we will demonstrate how we used these techniques and specific features to immerse players into our narrative while still encouraging exploration, communicating progress, and keeping gameplay both intimate to the individual player and fair in a multiplayer experience. _An announcement on-screen overlay of the character's internal thoughts._ _A thought bubble, or object-specific dialogue, that displays when you select certain objects._ ## Visual cues A visual cue is an important tool to both reinforce narrative and tell players what they need to know about their objective **without using words**. Using an arrangement of assets, such as chalkboards, statues, and wallpaper, we were able to passively provide a depth of information about the main characters of Duvall Drive, as well as guidance on how to complete puzzles without having to include invasive UI elements. We began this process by first figuring out what we wanted to communicate to the player, then how it would make sense to communicate that within the world of our experience. For example, we wanted players to understand how obsessed the grandfather was in his mission to defy death, so we left his room in disarray with a lot of his research and several walls with his scribbled thoughts. _Scribbles throughout the grandfather's room communicate the grandfather's compulsive nature._ _An abundance of sculptures and reference photos demonstrate how the daughter is an artist._ Our first rule was that if we wanted to use a visual cue to teach players something, it must have a reason to exist even if players aren't present. If we put a giant sign with clear instructions of what to do for a puzzle, this would seem out of place within the house. However, if we established a grandfather that is consumed with drawing on everything, including the walls of his bedroom, it is much more believable to have a chalkboard with his plan scribbled out for the player to follow. Our second rule was that we don't always need to tell players what to do or how to solve puzzles because they are insightful to their environment. For example, sometimes a fallen power line in a pool of water is enough of a visual cue to communicate that players are in danger of being electrified if they continue moving in that direction. ## Lore In addition to using visual cues to passively guide players, we also chose to display important story information about the family **on the entire screen** as a precursor to start the room's puzzle whenever a player clicked or tapped on specific items in the house. To guide players to the objects they needed to select in order for lore to display, we used the new highlight visual effect to make specific objects stand out from their surroundings. This effect allows you to either outline and/or overlay objects with a specific color that fits to the shape of the object itself, and choose whether other objects can hide the highlight if they are closer to the camera. We initially used `Class.Highlight` for every object a player could interact with, but the visual noise was overwhelming and players wouldn't clearly know what to interact with to read the lore. We decided to only use it to highlight items that were either corrupted or dangerous, and this visual feedback allowed players to easily drive the story forward. _Fun fact: we originally had lore on everything you could click on! This wasn't helpful because players weren't sure what information was important and what was just for flavor. Eventually we removed much of the lore and used visual cues to leave the rest of the narrative to the player's imagination._ After the player clicked on the object with the lore, we needed a way to display the information on top of the screen. We decided to create an overlay by using a `Class.ScreenGui` as our on-screen UI container with a child `Class.Frame` to control the sizing and rescaling of its children `Class.TextLabel|TextLabels` and `Class.ImageLabel|ImageLabels`. We set the positions and scale of the `Class.TextLabel|TextLabels` and `Class.ImageLabel|ImageLabels` properties so they fit visually together and would scale for different devices players use to access the experience. `Class.TextLabel|TextLabels` and `Class.ImageLabel|ImageLabels` affect visual elements of their respective text and images, including their color, transparency, size, and arrangement. For `Class.TextLabel|TextLabels` specifically, you can adjust their `Class.TextLabel.TextScaled` and `Class.TextLabel.RichText` properties to control the layout and visual styling of text. `Class.TextLabel.TextScaled` allows you to scale text to fill the entire parent UI element's space, and `Class.TextLabel.RichText` allows you to use simple markup tags to style sections of text in bold, italics, color, or even different fonts. We knew that players would be accessing this experience from a variety of devices, including PC, console, and mobile devices, and they would need to be able to either click or tap the screen to interact with the experience. We didn't want a bunch of buttons on the screen to distract from the narrative, so we made an invisible back button the size of the entire screen so that players could either click or tap anywhere on the lore overlay to close the content and get back to gameplay. ## Thought bubbles We needed a way for the player's character to communicate ideas to the player, such as providing additional narrative flavor or reinforcing what they needed to do. Our solution was to display thought bubbles, or **text reactions from the player's character**, near non-lore objects whenever the player selected them. This narrative only displays to the player that clicked or tapped the object, and the tone is always first person and observational. _A relatively simple yet powerful system to have the world react to the player's interactions!_ To create thought bubbles, we parented a `Class.BillboardGui` with a child `Class.TextLabel` to non-lore objects. This allowed us to display dialogue in the 3D space near the object without the text taking up the entirety of the screen. Similar to our process in creating lore, we used the `Class.TextLabel.RichText` property to style the text and add visual effects like another otherworldly being interrupting the player's own thoughts. The following is an example of RichText HTML used to make the image below. `"What is…this…
....hunger….void….pull…` By using `Class.TextLabel|TextLabels` instead of images, we kept the text readable by Roblox, and we were able to auto-localize this content and make it accessible to many more players without having to do anything! For more information on this process, see [automatic translations](/docs/en-us/production/localization/automatic-translations.md). ## Announcements While we wanted the player's character to have reactions to the world, we also wanted the world itself to react to their presence. To meet this goal, we included announcements, or third-person dialogue, that displays **on every player's screen at once**. Similar to lore, we created announcements by using a `Class.ScreenGui` as our on-screen UI container with a child `Class.TextLabel`. We also used the `Class.TextLabel.RichText` property to change the dialogue's color, font, and size to help convey different "entities" talking to the players in the house. _Announcements with plain white text were the player's character reaction that was general to an area, not an object like the lore narrative technique._ _Announcements with purple text were the reaction from a corrupt entity._ _Announcements with yellow text were the reaction from a restored entity._ By using the `Class.TextLabel.RichText` property and providing some HTML commands in it to drive changes, we were also allowed to make changes mid-sentence! For example, the following rich text markup allowed us to interrupt the player character's reaction by the corrupt entity's thoughts: `What is…this…
....hunger….void….pull…` We had several of these types of announcements, either triggered by invisible part volumes that players walked into, or by completing certain goals. To avoid dozens of individual `Class.ScreenGui|ScreenGuis`, we used a separate script in the demo that allowed us to provide the text that the script would then add to a single object we named **StoryNote** in our `Class.PlayerGui` folder. This object was a single `Class.ScreenGui` with a child `Class.TextLabel`. Using scripting, we could inject whatever phrases we wanted directly into the single `Class.TextLabel`, which made it a lot easier to make changes as we figured out how and what we wanted to communicate to the player! _StoryNote was an object we called for any instances that triggered it via script. Those same scripts provided the text and formatting we wanted for each event._ _Example of a script calling that object and defining the text, per event._ _The TextLabel was actually blank, and using scripts we injected the RichText as needed to the same object._
--- title: "The Mystery of Duvall Drive" url: /docs/en-us/resources/the-mystery-of-duvall-drive last_updated: 2026-06-29T19:34:10Z description: "The Mystery of Duvall Drive is a showcase demo with accompanying documentation on the concepts and processes used during development." --- # The Mystery of Duvall Drive ![The Mystery of Duvall Drive Banner](../../assets/resources/mystery-of-duvall-drive/overview/overview.png) [The Mystery of Duvall Drive](https://www.roblox.com/games/7902470429/The-Mystery-of-Duvall-Drive) is an official Roblox experience used to showcase Studio's various features and tools to create an immersive environment. Returning from the [Kerr-Newman DSR-14 space station](https://www.roblox.com/games/7208091524/Beyond-the-Dark-Vistech-Showcase), we now invite you to explore the dark abandoned house and its many mysteries in this official demo, which is both uncopylocked and editable in Studio, as well as a great palette of assets and examples that you can model and use similarly in your own experiences! Similar to the previous [Beyond the Dark](/docs/en-us/resources/beyond-the-dark.md) showcase, this document demonstrates the process of going from simple sketches to a rich 3D environment with mind-bending visuals. Our goal is to provide information on both **how** we used Studio tools and **why** we used the features we did to achieve our results, including: - **`Class.MaterialService`** and custom materials - **Packages** and their ability to streamline processes - **Volumetric audio** and `Class.SoundService` - **Streaming enabled** for complex experiences You can follow the articles sequentially or jump between the following sections: - **[Construct the house](/docs/en-us/resources/the-mystery-of-duvall-drive/construct-the-house.md)** explores the transformation from a simple blueprint to the final environment, including the process to ensure the environment was fun from start to finish. - **[Materialize the world](/docs/en-us/resources/the-mystery-of-duvall-drive/materialize-the-world.md)** details how both `Class.MaterialService` and `Class.SurfaceAppearance` create the look and feel of each room's normal and corrupted state. - **[Immersive narrative](/docs/en-us/resources/the-mystery-of-duvall-drive/immersive-narrative.md)** demonstrates how to utilize UI features to build lore throughout the playable area. - **[Stream in immersion](/docs/en-us/resources/the-mystery-of-duvall-drive/stream-in-immersion.md)** explains how we both code and constructed the experience to take advantage of Streaming Enabled capabilities. - **[Develop a moving world](/docs/en-us/resources/the-mystery-of-duvall-drive/develop-a-moving-world.md)** describes how and why movement can give the world a sense of life and reactiveness. - **[Design dark soundscapes](/docs/en-us/resources/the-mystery-of-duvall-drive/design-dark-soundscapes.md)** details how `Class.SoundService` and [volumetric audio](/docs/en-us/sound/objects.md#volumetric) craft the distinct soundscapes of the normal and corrupted worlds. --- title: "Main design requirements" url: /docs/en-us/resources/the-mystery-of-duvall-drive/main-design-requirements last_updated: 2026-06-29T19:34:10Z description: "Explains the fundamental design concepts used in The Mystery of Duvall Drive." --- # Main design requirements The following list provides an overview of the main technical design requirements we thought about as we worked on [The Mystery of Duvall Drive](https://www.roblox.com/games/7902470429/The-Mystery-of-Duvall-Drive). For more information on the visual design of these elements, see the [Mystery of Duvall Drive Showcase](/docs/en-us/resources/the-mystery-of-duvall-drive.md), which highlights Studio's various features and tools we used to create the immersive environment. ## Missions There are several types of missions players must solve in order to progress through the experience, including navigating a [series of spinning platforms](/docs/en-us/resources/the-mystery-of-duvall-drive/develop-a-moving-world.md#make-the-corrupted-treehouse) until players are able to retrieve a special item, or finding different ingredients in an [expanding pantry](/docs/en-us/resources/the-mystery-of-duvall-drive/develop-a-moving-world.md#make-the-expanding-pantry) and placing them into a boiling pot. To organize the scripting process of creating and debugging missions, we created a mission framework and a debug version. ### Mission framework To ensure that we unified each step of the mission throughout the experience, we designed a **simple puzzle mission framework** for each mission which consisted of hooks for the start and finish of the puzzle, a spot to read configuration data, and a button to complete the puzzle for [debugging purposes](#debug-version). For more detailed information on this process and its parameters, see [Missions Logic](/docs/en-us/resources/the-mystery-of-duvall-drive/foundational-gameplay-systems.md#missions-logic). #### Seals and game states When players entered specific rooms, we wanted them to interact with special small objects known as **seals** to trigger missions. After they solved the mission, we wanted them to grab the seal and place it in a predefined location within the foyer in order to progress through the overall gameplay. After they placed the seal in the correct location, they could then continue to another room within the house and start the next mission by clicking on that room's special object. _We developed a simple grabbing system for holding an object by attaching the object to the character's right arm._ _The foyer where players need to place each seal to progress through the experience._ To implement this, we created **game states**, which would specify the period of time players could begin process like: - Looking for a "corrupted" seal in a room to trigger the mission. - Pick up the "restored" seal when they solve the mission. - Place the seal into the foyer circle. Game states largely control the flow of the experience and how players interact with the experience's [narrative](/docs/en-us/resources/the-mystery-of-duvall-drive/immersive-narrative.md). For more information, see [GameStateManager](/docs/en-us/resources/the-mystery-of-duvall-drive/foundational-gameplay-systems.md#gamestatemanager). #### Normal and corrupt rooms We wanted to have two states of six rooms in the house: a normal state and a corrupted state. When a player would touch a corrupted seal in a room to trigger the room's corrupted state, the environment would change to a darker atmosphere with modified lighting, environmental objects, and special effects. Players would then have to solve the mission in order to return to the room's normal state. _The study's normal state_ _The study's corrupt state_ To implement this, we prepared a special space in the experience that was very far from the house, about 6,500 studs away in the **X** direction that could accommodate the corrupt state of a room. When a player triggers any corrupted state, the corrupt state of that specific room clones into this area from `Class.ServerStorage` to the **TempStorage/Cloned** folder, then players [teleport](#teleportation) into that room. Small `Class.Part|Parts` exist within each normal and corrupt room that serve as spawn points for players when entering or leaving rooms. After they solve the mission, we simultaneously teleport players back into the normal state of the room, and destroy all objects in the corrupt state of the room. For more information on the game states that control this teleportation process, see [GameStateManager](/docs/en-us/resources/the-mystery-of-duvall-drive/foundational-gameplay-systems.md#gamestatemanager). ### Debug version To assist us in periodically debugging missions, we created a version of the experience where we wouldn't have to wait in the lobby or for cutscenes. This version had keyboard-based cheats and buttons that would allow us to automatically complete missions so we could quickly test aspects of the experience. We would periodically copy and publish this version into the version we planned to release that had the proper gameplay flow. To distinguish these two versions, we made a **DemoGlobalSettings** script to check the placeID, see if it's running in Studio, and enable/disable various gameplay cheats and buttons. For more information, see [Missions Logic](/docs/en-us/resources/the-mystery-of-duvall-drive/foundational-gameplay-systems.md#missions-logic) and [Configuration Scripts](/docs/en-us/resources/the-mystery-of-duvall-drive/supporting-systems.md#configuration-scripts). ## Teleportation There are three types of teleportation that occur within the experience: 1. Teleporting players from the simple lobby to the main gameplay area in a [reserved server](#reserved-servers). 2. Teleporting players from a room's normal state to the corrupt state, then back again while showing a [cutscene](#cutscenes). 3. Teleporting players within some puzzles, or when they [respawn](#respawn-players) after falling off the gameplay area. ### Reserved servers We decided to group players into groups of five in a **simple lobby** before teleporting them over to a **reserved server** for the main gameplay area of the house. The lobby provided time for additional players to join and play together, and reserved servers prevented additional players from missing aspects of the gameplay and narrative from joining the experience late. This teleportation only happens once. ### Cutscenes We wanted to be able to transport players throughout the game whenever they accomplish certain tasks, such as touching a seal or completing a mission. To implement this, we developed a simple version of a script-based tool called **EventManager** that controlled various properties and attributes, ran scripts and audio, and performed camera shakes over a duration of time. This allowed us to both move players and stream in and out different rooms while hiding this process from the player using special effects. For more information, see [EventManager](/docs/en-us/resources/the-mystery-of-duvall-drive/foundational-gameplay-systems.md#EventManager). ### Respawn players The third type of teleportation we wanted to implement was a short teleport with only a player `Datatype.CFrame` coordinate change within some puzzles and when players fall and respawn. Unlike the previous two types of teleportation, this type doesn't explicitly request async streaming. ## Gameplay scripting Scripting allowed us to execute on specific gameplay elements, such as fading UI elements, creating trigger volumes for key events, and highlighting objects when they were in focus with the player's cursor. Many of these systems relied on tagging objects to perform custom behavior, then using a variety of client or server scripts to contain specific workflows depending on what action needed to happen at set points in the experience. For more information on how we implemented these systems, see [Foundational gameplay systems](/docs/en-us/resources/the-mystery-of-duvall-drive/foundational-gameplay-systems.md) and [Supporting Systems](/docs/en-us/resources/the-mystery-of-duvall-drive/supporting-systems.md). ### Custom behavior with tags We wanted a way to add custom behavior for objects, such as locking doors so players would have to stay within the room until they completed the active mission. To implement this, we decided to use tags for specific objects, then we used `Class.CollectionService` to find these objects and connect any corresponding `Class.Script|Scripts` to add custom behavior. We had one `Class.Script` for each tag category that acted as a manager to handle all objects tagged under that category so we could keep the `Class.Script` in a single location rather than being copied many times in `Class.DataModel`. For more information, see [DoorManager](/docs/en-us/resources/the-mystery-of-duvall-drive/supporting-systems.md#doormanager), [MasterAnimator](/docs/en-us/resources/the-mystery-of-duvall-drive/supporting-systems.md#masteranimator), [DrawerManager](/docs/en-us/resources/the-mystery-of-duvall-drive/supporting-systems.md#drawermanager), [KillVolumes](/docs/en-us/resources/the-mystery-of-duvall-drive/supporting-systems.md#killvolumes), [PlayerMissionRespawn](/docs/en-us/resources/the-mystery-of-duvall-drive/supporting-systems.md#playermissionrespawn), and [PianoManager](/docs/en-us/resources/the-mystery-of-duvall-drive/supporting-systems.md#pianomanager). The "manager" `Class.Script` uses an `Init` function to find any objects tagged when the experience starts, and connect a custom behavior to them. For example, DoorManager finds any object tagged with **Door**, then it attaches the correct behavior to the door objects (moves the door open when clicked, plays a door swing sound effect, etc.). However, any objects that are added or removed during runtime, such as any objects that are added to a corrupted room after a player interacts with a corrupted seal, miss this initial call and never get the expected behavior. To solve this, we use `Class.CollectionService.GetInstanceAddedSignal` and `Class.CollectionService.GetInstanceRemovedSignal` to grant the same behavior to new objects that are tagged and untagged, respectively. ### Client and server scripts We wanted to reduce bandwidth for different aspects of gameplay that would be expensive on performance. We decided that when object functionality can affect simulation for other players, such as moving object through collision, we would run this on the **server**, but if something is more related to environment design, such as lights, environmental animations that don't affect gameplay, special effects, and audio, we would run them on **clients**. This would reduce bandwidth, and keep both movement and environment changes smoother on user devices. --- title: "Materialize the world" url: /docs/en-us/resources/the-mystery-of-duvall-drive/materialize-the-world last_updated: 2026-06-29T19:34:10Z description: "Explains the use of materials and textures in The Mystery of Duvall Drive." --- # Materialize the world We use [materials](/docs/en-us/parts/materials.md) and [surface appearance](/docs/en-us/art/modeling/surface-appearance.md) objects to create an immersive and realistic indoor and outdoor environment. Without materials, this scene could still have some interesting depth, silhouettes, and lighting, but with our materials and texture systems, we can really bring the room to life and add a dimension of realism to the world. These [physically-based rendering](/docs/en-us/art/modeling/surface-appearance.md) (PBR) materials are what makes surfaces like wood look, react, and reflect the way wood does in the real world. In this section, we will go over some of the decisions and processes we went through to create the palette of materials used in the demo. As in the [Beyond the Dark](/docs/en-us/resources/beyond-the-dark.md) demo, we wanted a realistic look and feel for our materials and we achieved it through a few different approaches. You can break down the process of building all our textures and materials into three areas: - Surface appearance objects for 1:1 or unique materials for meshes, such as a marble statue. - Surface appearance objects for shared trim maps for `Class.MeshPart|MeshParts`, such as repeatable wood trim details on furniture. - [Material variants](/docs/en-us/parts/materials.md#custom-materials) for shared materials on Parts or terrain, such as flagstone replacing cobblestone for both fireplaces and ground. ## Plan, reuse, and budgets When starting any project, give some thought about material art direction for consistency and reuse. Every platform and device has a limit on the amount of memory you can use for textures so some upfront planning goes a long way to help keep a handle on the number of textures in your experience. When considering the look and feel of the experience, we deliberately chose to go with a craftsman style house to ensure we could reuse a handful of materials over a wide array of assets, from small picture frames and furniture to large architectural elements. _Here are some of the reference images we collected for the Craftsman Style._ Distilled down to its basics, [American Craftsman](https://en.wikipedia.org/wiki/American_Craftsman) characteristics include simple, clean lines and natural wood tones, letting the wood and artistry of the carpenter take center stage. These characteristics allow us to use one set of wood trim texture maps for furniture and architectural elements in the house and also allow us to keep our models fairly low poly. With this in mind, we set out to create the trim map texture set you see below. _Wood Trim Texture Set_ ## Surface appearance and trim maps Trim maps are simple repeatable textures that can be applied to a wide variety of assets. Trim maps do a lot of heavy lifting in experiences and fill the gap between completely tiling textures and creating a 1:1 texture set that you would get from painting an object in a program like [Substance Painter](https://www.adobe.com/products/substance3d-painter) or using [photogrammetry](/docs/en-us/resources/the-mystery-of-duvall-drive/construct-the-house.md#photogrammetry). The wood material shown previously was set up as a trim map which means that it could be reused on many different meshes, each mesh taking advantage of the same trim textures just by laying out its UVs creatively. _Tiling Texture_ _Trim Texture_ _1-1 Texture_ When using trim map texture sets for use across a large variety of scales and objects, try to keep your textures fairly clean and free of details that would be easily recognizable as repeating. _Example of too much detail_ _Cleaner textures work best_ For an example of over-detail, say we wanted to add some grime to our BaseColor map. Look how quickly you start to notice the unrealistic direction of textures and stretched UVs. _Notice how the extra detail of stain on the furniture on the left stands out for repetition and incorrect direction compared to the cleaner set on the right._ If an asset really needs heavy staining, wear, and/or dirt buildup it would be better to use a 1:1 map unique for that specific mesh. For additional information on working with trim maps, see the [Create trim sheets](/docs/en-us/resources/beyond-the-dark/building-architecture.md#create-trim-sheets) section in the Beyond the Dark documentation. You can package a single Surface Appearance node with these trim maps and apply the trim to all of the assets shown below. _Just some of the many assets that used the same Wood Trim Surface Appearance Node._ Tinting is another way to make your Surface Appearance nodes and Material Variants go further. By adding an alpha mask to your `Class.SurfaceAppearance.ColorMap` and setting the `Class.SurfaceAppearance.AlphaMode` to **Overlay**, you can use `Datatype.BrickColor` or `Datatype.Color3` to tint your objects for visual variation. _These 4 color variations are all utilizing the same Packaged Surface Appearance node._ Alpha masks, regardless of how they are created, are merged in your `Class.SurfaceAppearance.ColorMap`, changing it from a standard RGB texture to an RGBA. You can play around with values and levels in your alpha mask to get the desired level of tinting, but with a little finesse you can achieve some great results. _BaseColor RGB_ In this leather example, we just blended the height map with some scratches and wear marks to create our alpha mask. _Height Map_ _Scratches and wear marks_ These two maps were then sent to an Alpha Merge Node to create the final RGBA used in the Overlay Surface Appearance Node. _Final Alpha texture. White pixels don't apply tint and only show the Base Color RGB map. Black pixels tint and only show the flat BrickColor/Color with a gradient in between the two._ _Example of the BaseColor and Alpha being merged via the Alpha Merge Node in Substance Designer._ _Examples of the RGBA in combination with Mesh Color on white, brown, red, and blue meshes._ _Same example only showing the ColorMap and Mesh Color._ ## Texture sizes Along with heavily reusing textures, you can apply other techniques to keep your texture memory usage down. You can reduce texture sizes based on usage. For example, you can often drop the resolution of your Surface Appearance object's `Class.SurfaceAppearance.ColorMap`, `Class.SurfaceAppearance.RoughnessMap`, and `Class.SurfaceAppearance.MetalnessMap` down by 1 or 2 times without losing any visual fidelity. Many times the `Class.SurfaceAppearance.MetalnessMap` can be left blank, which results in a pure black/non-metallic surface. _This leather trim texture set consists of a normal map at 1024, a base color map at 512, a roughness map at 256, and no metalness map._ In [Substance Designer](https://www.adobe.com/products/substance3d-designer), the easiest way to resize outputs is to add a [Transformation 2D node](https://substance3d.adobe.com/documentation/sddoc/transformation-2d-172825332.html) just before your final output node and adjust the **Output Size** within the Base Parameters section for that node. _Transform 2D Node with adjusted Output Size._ _Final Surface on couch meshes._ It generally takes several iterations of resizing the various maps in your texture set and visually inspecting the asset in Studio to get them to a spot where they hold up well enough across the variously sized assets. Using this process, you can settle on the smallest texture sizes you feel you can get away with without losing the visual quality needed for the art direction of your experience. With these few techniques you can keep your texture budget under control and still have a beautiful experience. > **Warning:** When importing custom meshes into Studio, base textures included with your model may be imported with your mesh and automatically applied as a textureID. This can become a problem when importing multiple meshes with the same base texture applied. Each import can create a new version of the same texture map and bloat your texture memory. > > When a packaged Surface Appearance is applied for reuse, make sure to delete the textureID within the properties of the mesh to prevent this issue. This is a known behavior and Roblox is looking into improving this flow. ## Base materials Since we used [parts](/docs/en-us/parts.md), [terrain](/docs/en-us/parts/terrain.md), and [meshes](/docs/en-us/parts/meshes.md) side-by-side in this demo we chose to utilize the default [built-in materials](/docs/en-us/parts/materials.md#asset-ids-for-built-in-materials), [Material Variants](/docs/en-us/parts/materials.md#custom-materials), and the newly released [Material Manager](/docs/en-us/parts/materials.md#material-manager) tool to ensure our various parts and terrain surfaces match the look and feel of our PBR-textured meshes. Base materials are available for use as-is or to modify as a new variant. In many cases, the wood base materials serve us very well and only require adjusting the color to match our textured meshes. _Example of a mesh in the foreground using a Surface Appearance node and CSG (constructive solid geometry) objects built in Studio with Parts and using the standard Wood material colored to match._ ## Material variants Since the experience includes an outdoor environment during a downpour, we knew we wanted our ground to feel wet and muddy to match our stormy pacific northwest art direction. We accomplished this by creating a handful of new [Custom Materials](/docs/en-us/parts/materials.md#custom-materials) using [Material Variants](/docs/en-us/parts/materials.md#create-a-custom-material-in-the-explorer) that we could override the base materials with. We use them to paint down things like mulch, large and small gravel, mud, as well as a shallow puddled version of each. Material Variants allow the user to redefine textures that are used for any given default material, adding a lot of opportunities to customize materials used in terrain and parts. _Image of the backyard and use of wet Material Variants._ Material Variants are also used to create wet and dry versions of our materials. This is helpful for surfaces such as concrete and our flag stone hardscape that would be dry under cover of the porch roof and wet when exposed to the rain out in the yard. Creating a Material Variant can be done inside the [Material Manager](/docs/en-us/parts/materials.md#material-manager). As the name suggests, the Material Manager also allows you to manage both default and material variants in one place. Once the Material Manager is open you will click the Circle Plus button in the upper left of the window to edit and add a new Variant. When adding a name, it's a good idea to add a suffix of _MV after your name so you can easily pick out the new material variant from the default materials. Choose a **Base Material** that most closely relates to your new material. You can think of the Base Material field as the **Physical Properties** of the material, which affects things like friction, density, weight, hit effects, and footstep sounds. _Notice that the Metalness map was left blank since it was not needed._ Why create a Material Variant? Material Variants that override a Base Material inherit all properties of that material, except for the visuals. This means your material variant will inherit the physical behaviors of the default material, such as density. It also means future additions, like adding unique acoustics to concrete, will automatically be inherited. In this case, we want to create a new wet concrete variant that inherits nearly all the properties of the concrete material. If the Base Material relates to the **Physical Properties** then the Texture Maps are our **Visual Properties**. **Texture Maps** tell the renderer how light should interact with the material through our PBR rendering pipeline. The Base Material field also defines what default material is overwritten with the new visual properties. In this case, we are proceeding to overwrite the **Concrete** base material. Now that we have created our new "Concrete_Wet_MV" we need to override the default Concrete material with it so that we can use it in our scene. To do that just click on your new MV and click on the **Set Override** toggle. _Notice that the default Concrete material changes to the new Concrete_Wet_MV when the Set Override is toggled._ > **Warning:** Material Variants can be applied on a per-part/meshpart basis, so you can make an unlimited amount of custom materials simultaneously used on parts. Terrain requires using overrides for customization, and you're limited to overriding the 22 built-in materials. Now everything that uses Concrete in your scene will now be using the new override, including any concrete that you might have painted down on your terrain. The only exception to this is parts that have a specifically listed "material variant" on that object. _Example of Material Variant Overrides being used on terrain and parts._ To allow our terrain to have a variety of surfaces, including both wet and with puddles, we ended up overriding about 10 of the default materials. This was necessary for the rain soaked look we were going for on our final terrain. _Before Material Variant Override._ _After Material Variant Override._ Using all the material options and tools at hand, the possibilities of what you can create are limitless. With a little forethought and planning upfront, you can create awe inspiring materials all while keeping your experience textures under budget. _Shot of the backyard showing the variation in the terrain materials._ --- title: "Stream in immersion" url: /docs/en-us/resources/the-mystery-of-duvall-drive/stream-in-immersion last_updated: 2026-06-29T19:34:10Z description: "Explains the use of instance streaming The Mystery of Duvall Drive." --- # Stream in immersion Roblox Studio is a powerful engine that can create experiences on a wide variety of platforms, but even some high-end phones may struggle to load all of our content at once. To address this, we used [streaming](/docs/en-us/workspace/streaming.md) to achieve the highest level of quality possible on a wide spectrum of devices. Streaming allowed us to author all our content in one place and dynamically load smaller portions of the content visible to the user. _Streaming enabled on the workspace itself, with default settings_ ## Technical considerations When enabling streaming on your experience, there are a few things to consider to ensure that your experience runs well and that you maximize the benefits of the streaming feature: 1. **Are there scripts or elements that are reliant on another piece of content?** If so, one or more of your dependencies may not be loaded at certain times. 2. **Are there scripts that scan the workspace when the experience starts?** Client scripts should not rely on finding all instances they require at the time an experience starts and this is not a recommended practice. 3. **Does your experience suffer visually if some content doesn't display at the correct time?** There are various techniques and tricks to create a [streaming-friendly environment](#streaming-friendly-level-design) and ways to use [perspective](#play-with-perspective) to your advantage. When creating The Mystery of Duvall Drive, we encountered all three of these problems and were able to work around them by using a combination of careful script design and cleverly applied set dressing and world layout. When working with streaming on your own experience, remember you can tune your streaming min/max distance to what suits your experience and adjust a variety of [highly customizable options](/docs/en-us/workspace/streaming.md#streaming-properties). _Streaming with a max distance of 64 stud units._ _Streaming with a max distance of 1024 stud units._ ## Streaming-friendly level design To create an immersive environment, players have to be consistently surrounded by the world we built for them. When using instance streaming, we had to make sure that players wouldn't see unexpected content, such as seeing the "end of the world" if there weren't any visual assets loaded in their instance streaming range. Since distant trees behind the house and other blocking geometry like hills and mountains would be out of range, we had to find a solution to keep players from seeing the horizon and breaking their immersion. To address this, we added nearby blocking geometry along all areas of the player's path and designed the path that would wind to effectively hide the lack of content in the distance. We placed blocking geometry in a way that ensures the player would be surrounded by streamable visual assets at any point on the path. This aligned well with our original design to create a windy driveway and is a prime example of thoughtful level design that incorporates aesthetic and technical applications. _The player starts surrounded by nearby trees and bushes to help hide the fact distant content hasn't loaded yet. The winding path keeps much of the content hidden until players are close enough to load the asset._ ## Play with perspective There are some cases where content that a player would expect to see from far away is too far from the camera, such as our [storm in the sky](/docs/en-us/resources/the-mystery-of-duvall-drive/develop-a-moving-world.md#create-the-storm). We wanted players to see this large phenomenon, but it would break the immersion if the sky storm didn't exist when the player was too far away to load the asset. It would also be jarring to see a large asset suddenly load in when the player got close enough to an object that they should've seen further away. We tried to minimize this by playing with perspective and making sure the models in the sky were large enough to be within the bounds of the player's streaming distance. Another solution was to increase the vertical height of the trees surrounding the player when they weren't close enough to the storm. This solved the situation where the player is too far away to load the asset but would still expect to see it in their line of vision. Big trees are common in our Pacific Northwest setting, and this adjustment helped solve the problem with minimal concessions to our aesthetic and design. _Treelines help to obscure some of the content not loading in yet._ --- title: "Supporting systems" url: /docs/en-us/resources/the-mystery-of-duvall-drive/supporting-systems last_updated: 2026-06-29T19:34:10Z description: "Explains the supporting systems and functions in The Mystery of Duvall Drive." --- # Supporting systems We used the following systems to support both the [foundational gameplay systems](/docs/en-us/resources/the-mystery-of-duvall-drive/foundational-gameplay-systems.md), as well as any goals of the [main design requirements](/docs/en-us/resources/the-mystery-of-duvall-drive/main-design-requirements.md). ## UseManager **UseManager** provided a simple API to apply a grabbed object onto something, like a piece of layered clothing onto a mannequin. The main function for this API is `UseManager.AddUse` (tags, targetObjects, distance, onSuccess, onNothingEquipped, onWrongEquipped, extraData), which binds a set of tags to **targetObjects**. When a player has an object with one of the tags and clicks on a targetObject, the onSuccess callback function is called. Other call functions allowed us to show players extra visual info if either a click is made without a grabbed item or with the wrong type of item. We could remove the "use" with `UseManager.RemoveUse`, which was usually helpful when a mission was finished or a specific item was "used". In addition, we could add or remove targets with `AddUseTargets` and `RemoveUseTargets`. ## Highlights When a player was near an item of interest, such as a seal, we wanted to have that item stand out from its surroundings. To implement this, we created a `Class.LocalScript` called **HighlightItems** that uses a sphere centered around the player to detect touches with other meshes, connecting to `Touched` and `TouchEnded` events. The `getHighlight` function checks several tags on a touched mesh or its parents using a **GetTaggedObjectUpHierarchy** helper function. If any highlight isn't needed, we could forcibly remove it by using a **NoHighlight** tag. However, if it's needed but doesn't quite fit various other tags, we could force it using the **Important** tag. This `Class.LocalScript` utilizes a new engine feature `Class.Highlight` that draws an outline of an object and/or fills the object's interior with a defined color; for more information on how to use this feature, see [Highlighting Objects](/docs/en-us/effects/highlighting.md). `Class.Highlight|Highlights` and the mouse cursor **[OnItemIndicator](#onitemindicator)** systems work together, so `Class.Highlight|Highlights` not only determine if a mesh needs a highlight, but it also provides a type of mesh for **OnItemIndicator**. `HighlightItemsFunc` is used to communicate with other client systems. For example, [EventManager](/docs/en-us/resources/the-mystery-of-duvall-drive/foundational-gameplay-systems.md#eventmanager) uses it with an **Enable** command to enable or disable a `Class.Highlight` in certain cutscenes, and **OnItemIndicator** uses `GetType` to enquire about the type of object it is. To detect when an item is no longer present, such as when a corrupt room is destroyed, we connect to `Class.CollectionService.GetInstanceRemovedSignal`. ## Lore and ThoughtBubbles **Lore** and **ThoughtBubbles** are 2 similar systems. Lore uses a `Class.ScreenGui` as an on-screen UI container with a child `Class.Frame` to control the sizing and rescaling of its children `Class.TextLabel|TextLabels` and `Class.ImageLabel|ImageLabels`, and lore waits for the player to click anywhere on the screen to remove it. Similarly, thought bubbles use a `Class.BillboardGui` with a child `Class.TextLabel` to non-lore objects, and it displays dialogue in the 3D space near the object for a specified duration and cooldown period without the text taking up the entirety of the screen. For more information on the design behind these systems, see [Lore](/docs/en-us/resources/the-mystery-of-duvall-drive/immersive-narrative.md#lore) and [Thought Bubbles](/docs/en-us/resources/the-mystery-of-duvall-drive/immersive-narrative.md#thought-bubbles). Lore is implemented in the **LoreManger** `Class.LocalScript`. When clicked or touched, it fires a raycast using a helper function `utils.RaycastAlongPointingDir`, and it uses a **NoPlayerCollision** group. If a mesh under the click or one of the parents has a **Lore** or **ThoughtBubble** tag, we display the UI. The text, caption, and image are defined by the LoreText, LoreCaption, and LoreImage attributes on the object. Note that we use `Class.Camera.ViewportPointToRay` or `Class.Camera.ScreenPointToRay` to construct the ray, depending upon if it was called from a non-touch or touch. The coordinates are in somewhat different coordinate systems. For the mouse, we get them from `Class.UserInputService.InputEnded\`\`:Connect` for the MouseButton1, and for touch devices, we get them from `Class.UserInputService.TouchTapInWorld\`\`:Connect`. ThoughtBubbles are overall similar, using a raycast to check if a mesh or its parents have a **ThoughtBubble** tag. It also uses the ThoughtText attribute for text, and a **ThoughtBubble** tag to point to a placeholder object used for positioning the UI in the world. Thought bubbles that use the same positional object but have different text have different cooldowns. ### Special cases Lore has a couple of special cases, one of which is the corrupted seals. When a player clicks a corrupted seal, it displays lore UI, and it waits for a click to start a mission, which affects the game flow. This is handled by the [`GameStateClient`](/docs/en-us/resources/the-mystery-of-duvall-drive/foundational-gameplay-systems.md#gamestatemanager) that uses a bindable `LoreManagerFunc` to request lore UI. A callback is provided to the Lore system by `GameStateClient` to know when lore is "closed" by the player. Another special case is when **ThoughtBubbles** and **Lore** tags are on the same object. In this case, to avoid an overlap of lore and thought bubble text, we run the thought bubble after the lore is closed. **LoreManager** also handles a special case with showing a small cutscene when clicking on disabled doors that are locked until the player picks up the room's seal. ## OnItemIndicator We want to show different icons in the center of the screen when the player is looking at certain items of interest. The client script **OnItemindicator** does a raycast along the camera `Class.CFrame.LookVector` and analyzes the results. Based on the results, it sets an image in the OnItemIndicator2 `Class.ScreenGui`. When no items of interest are hit, the default icon is a small dot. We could set any icon by adding an **OnItemIndicator** string attribute to a specific mesh, using the names from `onItemIndicatorImages`, such as Hand, Eye, or DoorCurrentlyLocked. The attribute is only needed in rare cases, and most of the time other existing tags or systems provide the type of icon. For more details, see the `Update` function. Type checks some in a priority order. After the `OnItemIndicator` override, we check if it is grabbable or a drawer for the "hand" icon through either `utils.CanGrabModel(model)` or `utils.GetTaggedObjectUpHierarchy("Drawer2", model)`. After that, we call [`HighlightManager`](#highlights) that determines the highlight status, the types of items, and which icon to use. For example: ```lua highlightItemsFunc:Invoke({"GetType", curInst}) ``` The Lore and ThoughtBubble tags are checked later by checking tags. For the doors, we have 2 different icons: **DoorCurrentlyClosed** and **DoorAlwaysLocked**. [`DoorManager`](#doormanager) sets a true or false `DoorEnabled` attribute for doors that can be open or closed, and we use presence and value of the attribute. Objects that look like doors but do not open have the **DoorLocked** tag. ## DoorManager The **DoorManager** `Class.LocalScript` uses a **Door** tag and `Class.CollectionService` to manage opening and closing doors. Door has front and back side triggers that we connect with touch and `touchEnded` events. We created tweens to open and close a door from the front and back sides. We maintain a **playersNear** map (of players touching the triggers, separately for the front and back sides. Each door has a simple state system, `DoorState` (Closed, Opening, Open, Closing), with tweens used for transitions. We could enable or disable a door's ability to open or close from external systems by calling `DoorManager.EnableDoor`, which sets a `DoorEnabled` attribute. ## MasterAnimator The **MasterAnimator** `Class.LocalScript` plays animated images (texture atlas), which we used to animate TV screens. To scroll through images, we needed to know a set of parameters: row and column count, total frames, perios, image dimensions, and a set of image IDs. The system allowed us to animate across multiple images, each possibly split into rows and columns of sub-images. We could provide this data through attributes or values, but in this experience, we used helper scripts. `UpdateImageAnimations(dT)` calculates what image or subimage we needed to show using time and parameters. If we needed to change to a new image, we set Image. If we need to change any subimage, we set `ImageRectOffset`. An object with an animated `Class.SurfaceGui` would have an Animator `Class.ModuleScript`, with the main purpose to provide an `Animator.GetParams` function that returns all the parameters. This helps the MasterAnimator `Class.LocalScript` that uses the **ImageAnimation** tag and `Class.CollectionService` to gather such objects, and find the Animator `Class.ModuleScript` under them. It then uses pcall to require Animator `Class.ModuleScript` and call `GetParams` on it. ## LocalSpaceAnimations The **LocalSpaceAnimations** `Class.LocalScript` uses a **LocalSpaceRotation** tag to rotate mostly "cosmetic" objects with a given rotational velocity and delay around either the X, Y, or Z axis. We used this either for distant objects that players wouldn't interact with, or for smaller objects that don't affect simulation much. Parameters defined through the `Speed`, `Delay`, and `Axis` values. For implementation details, see [Rotating Cloud Meshes](/docs/en-us/resources/the-mystery-of-duvall-drive/develop-a-moving-world.md#rotating-cloud-meshes). ## HeadlampManager The **HeadlampManager** `Class.LocalScript` handles when users select the on-screen `Class.ImageButton` to toggle the spotlight on top of their head on or off, fires comments to the server using HeadlampEvent, and plates toggling on and off sounds. When a character is added, or their `Head` is changed, the `giveCharacterHeadlamp` function clones **templateHeadlamp** lamp, and positions the lamp using some offsets and rotations from the **FaceFrontAttachment**. ## SeatManager We do not want players to automatically seat when near an object they can sit on. Instead, we want to require users to click near a seat in order to sit. The **SeatManager** script adds `Class.ClickDetector|ClickDetectors` based on a **Seat** tag, and calls `seat:Sit(humanoid)` when clicked. When teleporting players between a room's normal and corrupt state, we can't have players sitting because `Datatype.CFrame` coordinate change wouldn't be able to work, so the **SeatManager** has a functionality to disable or enable seating a few seconds before and after the teleport. ## DrawerManager The **DrawerManager** script uses a **Drawer2** tag and `Class.CollectionService` to handle clicking on drawers to open or close them, and play any corresponding audio. The opening and closing action is done by setting TargetPosition for a `Class.PrismaticConstraint`. ## KillVolumes In a couple of areas of the main gameplay area, such as the electric sparks and water near the start of the road leading up to the house, a player can have their `Class.Humanoid.Health` set to `0` when entering a volume with the **KillVolume** tag. The **KillVolumes** script uses `Touched:Connect` to determine when a player enters a volume, then reduces their health to `0`. ## PlayerMissionRespawn The **PlayerMissionRespawn** script uses a **RespawnVolume** tag and `Class.CollectionService` to deal with volumes that make players respawn when touched. We placed these volumes under corrupt rooms, as many missions have gaps or moving platforms in which the player could fall down. When touched, the script plays a small **Teleport_Jump** cutscene and invokes `GameStateFunc` with `GameEvents.PlayerRespawn` command. When processing `GameEvents.PlayerRespawn`, the script can use `RespawnPositions`, if mission config provides it. If not, it uses `TeleportPositions` for the specific mission. We don't have a "checkpoint" system, so `CalcClosestTeleportPos` just selects closest **Respawn** or **Teleport** spots from where the player hit the `RespawnVolume`, using the only horizontal, "2D" distance. ## Small helper systems ### PianoManager The **PianoManager** script uses a **Piano** tag and `Class.CollectionService` to add `Class.ClickDetector|ClickDetectors` and play one of the piano sounds when clicked on the keyboard. ### RitualSupport The foyer where players place seals has a complicated contraption that undergoes changes as each seal is placed in its defined location. For example, depending on the number of seals placed, specific events play to enable/disable lights and beams, change the transparency of certain objects, etc. The **RitualSupport** `Class.ModuleScript` is a small wrapper over `EventManager:Invoke` calls for those events, providing parameters to the event, such as what "root object" to play it on, depending upon what specific seal was placed. ### RestorableManager Some grabbable objects are important for gameplay, such as seals, and we didn't want them to get lost if a player dropped them somewhere. If an object has a **Restorable** tag, the **RestorableManager** script remembers its transform when it is added to the restorable system. When a player drops such an object, the grabbing system calls `restorableManager.StartTracking`. If the object isn't picked up again in five seconds, the **RestorableManger** script positions it at the original transform and resets the tracking time. ### Portals In a few missions, we teleport players a short distance within a mission, such as [respawning players](/docs/en-us/resources/the-mystery-of-duvall-drive/main-design-requirements.md#respawning-players) who fall off a spinning platform. To simplify setting up this type of teleportation, which we call "Portals" in script, a helper function `ProcessPortal` in **DemoUtils** is used. For example, if P1 is the part defining the initial trigger, and P2 is the part defining destination player transform, the following code snippet could define such portal functionality: ```lua P1.Touched:Connect(function(otherPart) utils.ProcessPortal(otherPart, P2) end) ``` **ProcessPortal** handles checking that otherPart is a human, teleporting the player through a `Datatype.CFrame` coordinate change, and invoking a small cutscene to hide the transition using a **Teleport_Jump** event in [EventManager](/docs/en-us/resources/the-mystery-of-duvall-drive/foundational-gameplay-systems.md#eventmanager). ### Configuration scripts We have several configuration, data definition, and common functionality scripts: **DemoConfig**. Definitions of missions. Enumerations for game states, events for client-server communications. **DemoGlobalSettings**. We develop in one place, but release (and playtest) in others. The script checks placeID and enables/disables various cheats and debugging functionality. **DemoUtils**. Various utility functions. Dealing with transforms. Setting visibility, anchored or other properties. Checking for a point in a box. Finding objects in hierarchy by "dotted" name. Managing TempStorage (that can be used to temporarily move models "somewhere far" and bring back later). Click detector helpers. Grabbing support. Support for checking tags (especially along the hierarchy). Connecting triggers to [EventManager](/docs/en-us/resources/the-mystery-of-duvall-drive/foundational-gameplay-systems.md#eventmanager). **AudioUtils**. A couple of functions to play weighted random sounds from a set. **GrabUtil**. Helper functions for grabbing. --- title: "Technical overview" url: /docs/en-us/resources/the-mystery-of-duvall-drive/technical-overview last_updated: 2026-06-29T19:34:10Z description: "Explains the fundamental design and gameplay systems in The Mystery of Duvall Drive." --- # Technical overview The following document is a technical overview of the main design requirements and systems we created for [The Mystery of Duvall Drive](https://www.roblox.com/games/7902470429/The-Mystery-of-Duvall-Drive) experience. This high-level information is meant as an aid for technical designers and engineers as they dig deeper into the experience's source code. You can follow the reference articles sequentially or jump between the following sections: - **[Main design requirements](/docs/en-us/resources/the-mystery-of-duvall-drive/main-design-requirements.md)** covers a high-level overview of the essential systems we created, including a mission framework, three types of teleportation, and gameplay scripting. - **[Foundational gameplay systems](/docs/en-us/resources/the-mystery-of-duvall-drive/foundational-gameplay-systems.md)** digs more into the implementation of the main gameplay systems that meet the main design requirements. - **[Supporting systems](/docs/en-us/resources/the-mystery-of-duvall-drive/supporting-systems.md)** explores any smaller systems made to assist the foundational systems. --- title: "Weapons kit" url: /docs/en-us/resources/weapons-kit last_updated: 2026-06-29T19:34:10Z description: "The Weapons Kit assists in creating competitive combat-based experiences." --- # Weapons kit To assist in creating competitive combat-based experiences, several endorsed weapons are available for use in any experience. The core system features projectile-based weapons with an over-the-shoulder camera, and setting the projectile speed high enough can simulate raycasting weapons like laser guns. > **Info:** The content of this project and documentation can be used under Roblox's [Limited Use License](/docs/en-us/resources/limited-use-license.md). To use an endorsed weapon in your experience: 1. Select a weapon from below, navigating to the asset library link. _Pistol_ _Shotgun_ _Auto Rifle_ _Submachine Gun_ _Sniper Rifle_ _Crossbow_ _Grenade Launcher_ _Rocket Launcher_ _Railgun_ 1. On the weapon's item page, click the green **Get** button and confirm the transaction. 2. In Studio, open the [Toolbox](/docs/en-us/projects/assets/toolbox.md). 3. Select your toolbox **Inventory** section. 4. Locate the weapon and click it to add it into the place. When prompted whether to put the tool into the starter pack, click **Yes** if you want players to start with the weapon in their backpack, or click **No** to simply place the weapon in the 3D world as a pickup. 5. If this is the **first time** bringing in an endorsed weapon, move its **WeaponsSystem** folder into **ServerScriptService** to serve as the unified [system folder](#system-folder-structure) for all endorsed weapons in the experience. ## System folder structure The **WeaponsSystem** folder is a unified folder that contains assets, configurations, and scripts that power all endorsed weapons in the experience. If located in **ServerScriptService**, it overrides any equivalent **WeaponsSystem** folders that may reside within individual weapons. The **WeaponsSystem** folder contains the following instances: - **Assets** (`Class.Folder`) - **Animations** (`Class.Folder`) – Storage for animations used in the weapons systems. - **Effects** (`Class.Folder`) - **Casings** (`Class.Folder`) – Storage for all bullet casing assets. - **HitMarks** (`Class.Folder`) – Storage for all hit mark effects. - **Shots** (`Class.Folder`) – Storage for all shot effects. - **WeaponsSystemGui** (`Class.ScreenGui`) – Settings for the [Weapons System GUI](#weapons-system-gui). - **Configuration** (`Class.Folder`) – Configuration values for the weapons system. - **SlowZoomWalkEnabled** (`Class.BoolValue`) – Setting for [sprint control](#sprint-and-zoom-control). - **SprintEnabled** (`Class.BoolValue`) – Setting for [sprint control](#sprint-and-zoom-control). - **Libraries** (`Class.Folder`) – Stores all other `Class.ModuleScript|ModuleScripts` used in the weapons system. - **WeaponTypes** (`Class.Folder`) – Specifies all weapon types. - **ServerWeaponsScript** (`Class.Script`) - **Version** (`Class.IntValue`) - **ClientWeaponScript** (`Class.LocalScript`) - **NetworkingCallbacks** (`Class.ModuleScript`) - **WeaponData** (`Class.RemoteEvent`) - **WeaponsSystem** (`Class.ModuleScript`) Within the **WeaponsSystem** folder, different aspects are controlled by the following `Class.ModuleScript|ModuleScripts`: | Aspect | Handled primarily within... | | --- | --- | | Weapons functionality | | | [Shoulder camera](#shoulder-camera) | | | [Weapons GUI](#weapons-system-gui) | | ## Weapon structure Endorsed weapons are `Class.Tool|Tools` and are named as they will appear in the player's backpack. Each weapon is structured with a similar hierarchy. ### Weapon type The **WeaponType** `Class.StringValue` corresponds with the `Class.ModuleScript` for the weapon in the **WeaponsSystem**/**WeaponTypes** folder. The two base values are **BulletWeapon** and **BowWeapon**. ### Weapon model Each weapon contains a `Class.Model` made up of one or more `Class.BasePart|BaseParts` to form the physical weapon. One of these should be set as the model's `Class.Model.PrimaryPart|PrimaryPart`. The model also includes the following important descendants which may be parented to one of the model's `Class.BasePart|BaseParts`: - **TipAttachment** – `Class.Attachment` whose position on the parent `Class.BasePart` determines where bullets/projectiles come out. - **HandleAttachment** – `Class.Attachment` whose position on the parent `Class.BasePart` determines where the **Handle** is welded. - **Fired** **optional** – `Class.Sound` that plays when the weapon is fired. - **Reload** **optional** – `Class.Sound` that plays when the weapon is reloaded. - Additional descendants for any [specialized options](#specialized-options). ### Weapon handle The **Handle** part determines where a player character holds the weapon. This must be a `Class.Part`, it must be named **Handle**, and it must be a direct child of the weapon (tool). ### Configuration folder The **Configuration** folder contains specific "value" types for weapon behavior. Beyond the defaults, you can add additional configuration items for [specialized options](#specialized-options) when applicable. As follows are the base configurations and their default values: | Item | Description | Default | | --- | --- | --- | | **AmmoCapacity** | Number of shots in each "ammo clip" before player must reload. Note that ammo is unlimited and this does not specify how much ammo a player is carrying. | 30 | | **FireMode** | Choose from either **Semiautomatic** (one shot per click/tap), **Automatic** (continuous fire), or **Burst** (burst of shots equal to **NumBurstShots** on each click/tap). | **Semiautomatic** | | **ShotCooldown** | Minimum waiting time between clicks. For weapons with **FireMode** of **Automatic**, this is also the time between shots while pressing the fire button or holding click. | 0.1 | | **BurstShotCooldown** | Time between each shot in a burst; only matters if you set **FireMode** to **Burst**. | Value of **ShotCooldown** | | **NumBurstShots** | Number of shots per click/burst; only matters if you set **FireMode** to **Burst**. | 3 | | **HitDamage** | Amount of damage each direct hit does. | 10 | | **FullDamageDistance** | Maximum distance that shots will do full damage. Anything hit beyond this distance will receive less and less damage as the distance nears **ZeroDamageDistance**. | 1000 | | **ZeroDamageDistance** | Anything hit at or beyond this distance will receive no damage. | 10000 | | **BulletSpeed** | Speed that bullets/projectiles travel when shot. Setting this to a very high value like 20000 simulates raycasting weapons like laser guns. | 1000 | | **MaxDistance** | Maximum distance bullets/projectiles travel before disappearing. | 2000 | | **MinSpread** | Minimum amount of spread for the weapon. | 0 | | **MaxSpread** | Maximum amount of spread for the weapon. | Value of **MinSpread** | | **GravityFactor** | Amount that gravity should influence each bullet/projectile. For example, this value for the [Crossbow](https://www.roblox.com/library/4842204072/Crossbow) is 1 because arrows arc during flight, but this value for the [Rocket Launcher](https://www.roblox.com/library/4842186817/Rocket-Launcher) is 0 because propelled rockets travel straight. | 0 | | **HasScope** | Set to **true** if you want to use the scope that's specified in [Weapons System GUI](#weapons-system-gui). | false | | **ReloadAnimation** | Name of the reload animation track in **Assets**/**Animations** of the [system folder](#system-folder-structure). | **RifleReload** | | **AimTrack** | Name of the aim animation track in **Assets**/**Animations** of the [system folder](#system-folder-structure). | **RifleAim** | | **AimZoomTrack** | Name of the aim zooming animation track in **Assets**/**Animations** of the [system folder](#system-folder-structure). | **RifleAimDownSights** | | **RecoilMin** | Minimum recoil added for each shot. | 0.05 | | **RecoilMax** | Maximum recoil added for each shot. | 0.5 | | **TotalRecoilMax** | Total maximum accumulated recoil. Weapon's current recoil will never exceed this value. | 2 | | **RecoilDecay** | Decay multiplier for recoil; essentially the rate at which recoil diminishes after shooting. | 0.825 | | **RecoilDelayTime** | Waiting time after shooting/clicking before recoil is added to camera. | 0.07 | | **StartupTime** | Length of time after equipping the weapon before the player can shoot. This prevents players from firing a single shot from multiple different weapons in quick succession. | 0.2 | | **FiredPlaybackSpeedRange** | Amount that the pitch can vary for the **Fired** sound of the weapon. Set this to 0 to always play it at the same pitch. | 0.1 | | **NumProjectiles** | Number of bullets/projectiles that will fire at the same time when you click/tap once. This is useful for weapons like the [Shotgun](https://www.roblox.com/library/4842215723/Shotgun) that fires multiple bullets at same time. Note that one shot will always use exactly one ammo regardless of this value. | 1 | ## Specialized options You can add/modify the following options for any weapon. These customizations require modifying either the weapon's `Class.Model`, the weapon's `Class.Configuration`, or both. Some configurations are dependent on others, such as [Muzzle Particles](#muzzle-particles) which require the necessary children for [Projectile/Hit Effects and Sounds](#projectilehit-effects-and-sounds). ### Bolt animations and sounds A weapon's **bolt** is the part that moves back and forth each time it's fired. - Descendants of the weapon's [Weapon Model](#weapon-model) | **Bolt** | `Class.BasePart` that moves when the weapon is fired. | | | --- | --- | --- | | **BoltMotor** | `Class.Motor6D` used to animate the bolt. Make sure to set the motor's `Class.Motor6D.Part0\|Part0` to the weapon model's `Class.Model.PrimaryPart\|PrimaryPart` and `Class.Motor6D.Part1\|Part1` to the **Bolt** part. | | **BoltMotorStart** | `Class.Attachment` whose position on the parent `Class.BasePart` determines where the bolt is when it's at rest. | | **BoltMotorTarget** | `Class.Attachment` whose position on the parent `Class.BasePart` determines where the bolt animates to when shooting. | | **BoltOpenSound** | `Class.Sound` that plays when the bolt opens. | **optional** | | **BoltCloseSound** | `Class.Sound` that plays when the bolt closes. | **optional** | - Children of the weapon's [Configuration](#configuration-folder) folder | Item | Description | Default | | --- | --- | --- | | **ActionOpenTime** | Time it takes for the bolt to animate to open position. | 0.025 | | **ActionCloseTime** | Time it takes for the bolt to animate to closed position. | 0.075 | ### Eject bullet casings Weapons can include physical bullet casings that eject upon firing and fall to the ground. - Descendants of the weapon's [Weapon Model](#weapon-model) | **CasingEjectPoint** | `Class.Attachment` whose position on the parent `Class.BasePart` determines where you want bullet casings to pop out. Note that its orientation determines the direction that casings pop out. | | --- | --- | - Children of the weapon's [Configuration](#configuration-folder) folder | Item | Description | Default | | --- | --- | --- | | **CasingEffect** | Name of casing `Class.BasePart` in **Assets**/**Effects**/**Casings** of the [system folder](#system-folder-structure). | | | **CasingEjectSpeedMin** | Minimum eject speed of casings | 15 | | **CasingEjectSpeedMax** | Maximum eject speed of casings | 18 | - Child of the casing `Class.BasePart` in **Assets**/**Effects**/**Casings** of the [system folder](#system-folder-structure) | **CasingHitSound** | `Class.Sound` that plays when casings hit the ground. | **optional** | | --- | --- | --- | ### Projectile/hit effects and sounds You can configure physical projectiles for any weapon, along with `Sounds`, `Beams`, and `ParticleEmitters` for hit effects and other special effects. - Children of the weapon's [Configuration](#configuration-folder) folder | Item | Description | Default | | --- | --- | --- | | **ShotEffect** | Name of a shot effect stored within **Assets**/**Effects**/**Shots** of the [system folder](#system-folder-structure). | | | **ShouldMovePart** | Set to **true** if the weapon's **ShotEffect** should move with the projectile or **false** if not. You should only set this to **true** if there's a visible object that moves with each shot, such as an arrow or rocket. | false | | **BeamFadeTime** | Time it takes for **Beam0** or **Beam1** ([see below](#shoteffect-descendants)) to fade after the bullet/projectile hits something. By default, no manual fade will be applied by code. | 0 | | **BeamWidth0** | Thickness of **Beam0** or **Beam1** at **Attachment0** ([see below](#shoteffect-descendants)). | 1.5 | | **BeamWidth1** | Thickness of **Beam0** or **Beam1** at **Attachment1** ([see below](#shoteffect-descendants)). | 1.8 | | **NumHitParticles** | Number of particles the **HitParticles** ([see below](#shoteffect-descendants)) emitter will emit. | 3 | | **HitParticlesUsePartColor** | Set to **true** if you want the hit particles to be the color of the hit surface; **false** if you want hit particles to not change color. | true | #### ShotEffect Descendants Descendants of the specified **ShotEffect** noted in the previous section | **Flying** | `Class.Sound` that plays while the bullet/projectile is traveling. | **optional** | | --- | --- | --- | | **Beam0** | First slot for a trailing `Class.Beam` behind the bullet/projectile. Don't forget to set **Attachment0** and **Attachment1**. | **optional** | | **Beam1** | Second slot for a trailing `Class.Beam` behind the bullet/projectile. Don't forget to set **Attachment0** and **Attachment1**. | **optional** | | **Attachment0** | `Class.Attachment` whose position on the parent `Class.BasePart` determines the back of trailing beams; make sure to set `Class.Beam.Attachment0` on both **Beam0** and **Beam1** to this. | **optional** | | **Attachment1** | `Class.Attachment` whose position on the parent `Class.BasePart` determines the front of trailing beams; make sure to set `Class.Beam.Attachment1` on both **Beam0** and **Beam1** to this. | **optional** | | **TrailParticles** | `Class.ParticleEmitter` parented as a direct child of **Attachment0**; this will emit while the bullet/projectile is traveling. | **optional** | | **LeadingParticles** | `Class.ParticleEmitter` parented as a direct child of **Attachment1**; this will emit while the bullet/projectile is traveling. | **optional** | | **HitEffect** | `Class.Attachment` whose position will be set to `Class.Beam.Attachment1` of **Beam0** when the bullet/projectile hits. You must specify **Beam0** and its attachments for this to work properly. | **optional** | | **HitSound** | `Class.Sound` parented as a direct child of **HitEffect**; plays when the bullet/projectile hits. | **optional** | | **HitParticles** | `Class.Sound` parented as a direct child of **HitEffect**; emits when the bullet/projectile hits. | **optional** | | **[ProjectilePart]** | Any `Class.Part` or `Class.MeshPart` that you want to appear as a physical projectile. Make sure you set **ShouldMovePart** noted in the previous section to **true** if you have a visible object here. | **optional** | #### Muzzle particles This option emits particles from the specified `Class.ParticleEmitter` at the weapon's **TipAttachment** `Class.Attachment` when it's fired. Configuration descendants: - **ShotEffect** (`Class.StringValue`) — Name of a shot effect stored within `WeaponsSystem/Assets/Effects/Shots`. - **NumMuzzleParticles** (`Class.IntValue`) (optional) — Number of muzzle particles that will be emitted; default is **50**. For the specified shot effect, add a ParticleEmitter asset within `WeaponsSystem/Assets/Effects/Shots` named **MuzzleParticles**. ### Muzzle flashes This option creates a `Class.Beam` flash effect when the weapon is fired. Model descendants: - **MuzzleFlash0** (`Class.Attachment`) — Used to specify one side of muzzle flash. Position doesn't matter. - **MuzzleFlash1** (`Class.Attachment`) — Used to specify opposite side of muzzle flash. Position doesn't matter. - **MuzzleFlash** (`Class.Beam`) — Make sure to set `Attachment0` to **MuzzleFlash0** and `Attachment1` to **MuzzleFlash1**. Configuration descendants: - **MuzzleFlashTime** (`Class.NumberValue`) (optional) — Length of time muzzle flash will show for; default is **0.03**. - **MuzzleFlashRotation0** (`Class.NumberValue`) (optional) — Minimum rotation of muzzle flash; default is **-math.pi**. - **MuzzleFlashRotation1** (`Class.NumberValue`) (optional) — Maximum rotation of muzzle flash; default is **math.pi**. - **MuzzleFlashSize0** (`Class.NumberValue`) (optional) — Minimum size of muzzle flash; default is **1**. - **MuzzleFlashSize1** (`Class.NumberValue`) (optional) — Maximum size of muzzle flash; default is **1**. ### Particle trails This option creates a trail of varying length from the weapon to the projectile impact point. Configuration descendants: - **TrailLength** (`Class.NumberValue`) (optional) — Length of trail behind bullet/projectile; default is `nil` which means the trail length will instead be calculated using **TrailLengthFactor**. - **TrailLengthFactor** (`Class.NumberValue`) (optional) — The trail length will be set to this value multiplied by the distance the bullet/projectile traveled in the last frame; default is **1**. Note that this will be overridden if you include **TrailLength**. - **ShowEntireTrailUntilHit** (`Class.BoolValue`) (optional) — Set to **true** to render the trail from the weapon tip all the way to wherever the projectile is; this will override both **TrailLength** and **TrailLengthFactor** and the trail will only disappear once the projectile hits something. Set to **false** to use one of the above two options to calculate trail length. Default is **false**. ### Hit marks This visual addition appears on the surface where projectiles hit and is useful for arrows, bullet holes, scorch marks, etc. Configuration descendants: - **HitMarkEffect** (`Class.StringValue`) (optional) — Name of hit mark effect stored within `WeaponsSystem/Assets/Effects/HitMarks`; default is **BulletHole**. - **AlignHitMarkToNormal** (`Class.BoolValue`) (optional) — Set to **true** if the hit mark should always align flat against the surface like a bullet hole, or **false** if the hit mark should appear stuck in the surface from the direction the projectile came from (like an arrow). Default is **true**. You can add the following optional asset within `WeaponsSystem/Assets/Effects/HitMarks`: - **Glow** (`Class.Decal`) (optional) — Appears on the hit surface fully opaque, then rapidly gets more transparent, like a glowing effect on the surface that fades quickly. Useful for things like showing a glowing red mark where explosives hit. - **BulletHole** (`Class.Decal`) (optional) — Appears on the hit surface fully opaque and, after 4 seconds, fades to transparent over 1 second. - **ImpactBillboard** (`Class.BillboardGui`) (optional) — Displays at the hit surface, always facing towards the camera. - **Impact** (`Class.ImageLabel`) (optional) — Direct child of **ImpactBillboard**; this begins fully opaque, grows to the full size of the **ImpactBillboard** over 0.1 seconds, then shrinks to half its size and fades to full transparency over 0.1 seconds. - Any `Class.Part`/`Class.MeshPart`/`Class.SpecialMesh` that you want to appear as a physical projectile (optional). For example, including an arrow `Class.MeshPart` and setting **AlignHitMarkToNormal** noted above to **false** will make the arrow stick out of the surface from the direction you shot it. ### Exploding projectiles Projectiles can include an Explosion object to damage player characters in an area around the impact point. Configuration descendants: - **ExplodeOnImpact** (`Class.BoolValue`) (optional) — Set to **true** if you want bullets/projectiles for the weapon to explode on impact, **false** otherwise. Default is **false**. - **BlastRadius** (`Class.NumberValue`) (optional) — BlastRadius of explosion; default is **8**. - **BlastPressure** (`Class.NumberValue`) (optional) — BlastPressure of explosion; default is **10000**. - **BlastDamage** (`Class.NumberValue`) (optional) — Damage dealt to things in the center of the explosion. Note that the explosion does less damage the farther away hit objects are from the center of the explosion. Default is **100**. ### Charging weapon A charging weapon like the Railgun must be charged up between shots before it can fire again. Model descendants: - **Charging** (`Class.Sound`) (optional) — Plays while the weapon is charging. - **Discharging** (`Class.Sound`) (optional) — Plays while the weapon is discharging, for example if you charge the weapon just partially and release the shoot button. - **ChargeComplete** (`Class.Sound`) (optional) — Plays when the weapon has reached full charge. - **DischargeComplete** (`Class.Sound`) (optional) — Plays when the weapon has completely discharged. - **ChargeGlow** (`Class.BasePart`) (optional) — This object will get less transparent as the weapon charges up, such that it will be fully opaque at 100% charge. - **ChargeCompleteParticles** (`Class.ParticleEmitter`) (optional) — Emits when the weapon has finished charging. This emitter can be a child of any model `Class.BasePart` or a child of an `Class.Attachment` within the `Class.BasePart`. - **DischargeCompleteParticles** (`Class.ParticleEmitter`) (optional) — Emits when the weapon has completely discharged. This emitter can be a child of any model `Class.BasePart` or a child of an `Class.Attachment` within the `Class.BasePart`. - **ChargingParticles** (`Class.ParticleEmitter`) (optional) — Emits while the weapon is charging. You can include multiple emitters of this name and each will emit while charging. This emitter can be a child of any model `Class.BasePart` or a child of an `Class.Attachment` within the `Class.BasePart`. Configuration descendants: - **ChargeRate** (`Class.NumberValue`) — Rate at which the weapon will charge. This value must be specified to indicate that the weapon uses charging. - **DischargeRate** (`Class.NumberValue`) (optional) — Rate at which the weapon will discharge; default is **0** which means the weapon will not discharge at all. - **ChargePassively** (`Class.BoolValue`) (optional) — Set to **true** if you want the weapon to passively charge so it will shoot instantly when you click, or **false** if you want to click/touch to charge the weapon and have it fire once full charge is reached. Default is **false**. - **ChargingParticlesRatePerCharge** (`Class.IntValue`) (optional) — Number of particles that will emit out of all **ChargingParticles** emitters multiplied by the current charge of the weapon. Default is **20**, meaning that if the weapon charge is at 10%, each ChargingParticles emitter will emit 2 particles (20×0.1), and if the weapon charge is at 90%, each emitter will emit 18 particles (20×0.9). - **FireDischarge** (`Class.NumberValue`) (optional) — Amount of charge the weapon will lose after firing a fully charged shot; default is **1**. - **NumChargeCompleteParticles** (`Class.IntValue`) (optional) — Number of particles the - ChargeCompleteParticles emitter will emit once the weapon is fully charged. Default is **25**. - **NumDischargeCompleteParticles** (`Class.IntValue`) (optional) — Number of particles the - **DischargeCompleteParticles** emitter will emit when the weapon is completely discharged. Default is **25**. ### Bow weapon A bow weapon like the Crossbow can include a realistic string and arms construction, as well as a visual arrow nocked to the string. In addition to adding model descendants, you need to apply the following: - Make the weapon into a [charging weapon](#charging-weapon). For example, add the required **ChargeRate** within the weapon's `Class.Configuration` that specifies how fast the string is drawn. Additionally, consider adding optional descendants to the weapon's `Class.Model` such as a **Charging** sound for the string/arms being pulled back. - Set the **WeaponType** to **BowWeapon** as indicated in [Weapon Structure](#weapon-structure). Model descendants: - **LeftString** (`Class.Beam`) (optional) — The visual left half of the string. - **RightString** (`Class.Beam`) (optional) — The visual right half of the string. - **Arrow** (`Class.BasePart`) (optional) — The arrow that appears when the bow is fully drawn. Note that this is only for visual appearance on the bow (the actual fired arrow will be a **ShotEffect** as outlined in [Projectile/Hit Effects and Sounds](#projectilehit-effects-and-sounds)). - **String1** (`Class.Attachment`) (optional) — The center point of the string. - **StringLoose** (`Class.Attachment`) (optional) — Point where **String1** should be when the bow is at rest. - **StringTight** (`Class.Attachment`) (optional) — Point where **String1** should be when the bow is fully drawn. - **Arms** (`Class.Part`) (optional) — A part that just serves as an internal indicator that the bow arms will be animated. This may contain the following direct children: - **LeftString0** (`Class.Attachment`) (optional) — Point where the left side of the string is attached to the bow. - **RightString0** (`Class.Attachment`) (optional) — Point where the right side of the string is attached to the bow. - **LeftLoose** (`Class.Attachment`) (optional) — Point where **LeftString0** should be when the bow is at rest. - **RightLoose** (`Class.Attachment`) (optional) — Point where **RightString0** should be when the bow is at rest. - **LeftTight** (`Class.Attachment`) (optional) — Point where **LeftString0** should be when the bow is fully drawn. - **RightTight** (`Class.Attachment`) (optional) — Point where **RightString0** should be when the bow is fully drawn. - [SpecialMesh] (`Class.SpecialMesh`) (optional) — The part of the bow that will actually bend when the bow is drawn. Note that you must specify the following four `Class.Vector3Value` objects to make this animate. - **LooseOffset** (`Class.Vector3Value`) (optional) — Offset of the `Class.SpecialMesh` when the bow is at rest. - **TightOffset** (`Class.Vector3Value`) (optional) — Offset of the `Class.SpecialMesh` when the bow is fully drawn. - **LooseScale** (`Class.Vector3Value`) (optional) — Scale of the `Class.SpecialMesh` when the bow is at rest. - **TightScale** (`Class.Vector3Value`) (optional) — Scale of the `Class.SpecialMesh` when the bow is fully drawn. ## Weapons system GUI The core weapons system interfaces with this system to update the GUI based on things like spread of the gun, indicators for when you get hit or hit others, etc. The **WeaponsSystemGui** is a `Class.ScreenGui` object in `WeaponsSystem/Assets` that is parented to `Class.PlayerGui` when the experience starts. WeaponSystemGui has 4 descendants: - [ScalingElements](#scaling-elements) - A `Class.Folder` of on-screen elements. - [LargeTouchscreen](#largetouchscreen) - A `Class.Frame` for buttons on large touchscreens. - [Scope](#scope) - A `Class.Frame` for zooming in with weapons that use a scope. - [SmallTouchscreen](#smalltouchscreen) - A `Class.Frame` for buttons on small touchscreens. ### Scaling elements ScalingElements is a `Class.Folder` parented under WeaponsSystemGui with the following descendants: | Name | Instance type | Description | | --- | --- | --- | | DirectionalIndicators | `Class.Folder` | A `Class.Folder` where all [directional indicators](#create-a-directional-indicator) are stored. | | Crosshair | `Class.Frame` | A `Class.Frame` containing the following objects:

[UIAspectRationConstraint] - `Class.UIAspectRatioConstraint`
**Bottom** - `Class.ImageLabel`
**Left** - `Class.ImageLabel`
**Right** - `Class.ImageLabel`
**Top** - `Class.ImageLabel` | | HitMarker | `Class.Frame` | A `Class.Frame` containing the following objects:

[UIAspectRatioConstraint] - `Class.UIAspectRatioConstraint`
**HitMarkerImage** - `Class.ImageLabel` that appears and fades when the player successfully hits another player character. | ### LargeTouchscreen LargeTouchscreen is a `Class.Frame` containing buttons that display on large touchscreens. LargeTouchscreen has the following descendants: - AimButton (`Class.ImageButton`) - FireButton (`Class.ImageButton`) ### Scope Scope is a `Class.Frame` that contains **ScopeImage** (`Class.ImageLabel`) which shows up when zooming on a weapon with HasScope enabled (see [Weapon Structure](#weapon-structure)). **Scope** has the following descendants: | Name | Instance type | Description | | --- | --- | --- | | ScopeInstance | `Class.Frame` | A `Class.Frame` containing the following assets used when zooming on a weapon with HasScope enabled:

[UIAspectRationConstraint] - `Class.UIAspectRatioConstraint`
**BottomBlack** - `Class.Frame`
**LeftBlack** - `Class.Frame`
**RightBlack** - `Class.Frame`
**TopBlack** - `Class.Frame` | ### SmallTouchscreen SmallTouchScreen is a `Class.Frame` containing buttons that display on small touchscreens. SmallTouchscreen has the following descendants: - AimButton (`Class.ImageButton`) - FireButton (`Class.ImageButton`) ### Create a directional indicator Directional indicators are used to show the direction of something around the player's crosshair. For example, if someone shoots you, a red semi-circle can show up around your crosshair in the direction the shot came from. Other examples include indicators to show the direction of footsteps, indirect gunfire, or even environmental objects such as chests. To create a new indicator, add a new Indicator `Class.Frame` in `WeaponsSystemGui/ScalingElements/DirectionalIndicators` with the following structure: | Name | Instance type | Description | | --- | --- | --- | | [UIAspectRationConstraint] | `Class.UIAspectRatioConstraint` | | | [ImageLabel] | `Class.ImageLabel` | Image of the directional indicator. Tweaking the rotation of the image in Studio may be required unless you upload the image so that it's facing down and there's little or no blank space around it.
This image label must also contain its own
`Class.UIAspectRatioConstraint`. | | [Configuration] | `Class.Configuration` | Contains optional properties to adjust. See [Indicator Configuration](#indicator-configuration) for more information. | Once created, you can activate an indicator via the following command inside `WeaponsSystem/Libraries/WeaponsGui` where `indicatorName` is the string name of the indicator to activate and worldPos is the world position where the directional indicator should point: ```lua self.DirectionalIndicatorGuiManager:ActivateDirectionalIndicator(indicatorName, worldPos) ``` > **Info:** If an indicator is activated an additional time before it has had time to fade completely, a new indicator of that type will be instantiated. This allows an unlimited number of any type of indicator to be activated at the same time. > > You can also activate directional indicators from outside of WeaponsGui by replacing self in the above code with the instance of `WeaponsGui` in your code. However, it's recommended that you activate it from inside `WeaponsGui` and trigger it via a `Class.RemoteEvent` or a `Class.BindableEvent`. For reference, see how `DamageIndicator` is activated within `WeaponsGui`. #### Indicator configuration The DirectionalIndicators can be further modified by adjusting the `Class.Configuration` object parented under the `[Indicator]`. All of these settings have a default value, so there is no need to set configurations when not modifying a setting. The following configurations can be set: | Name | Instance type | Description | | --- | --- | --- | | DistanceLevelFromCenter | `Class.NumberValue` | Number of distance levels from the center of the screen (each distance level is about 0.03 screen scale); default is **6**. | | FadeTime | `Class.NumberValue` | Indicator fade time following its activation and the `TimeBeforeFade` time; default is **1**. | | Name | `Class.StringValue` | Name of the directional indicator as you want to reference it in code; default is the name of the indicator's top level `Class.Frame`. | | TimeBeforeFade | `Class.NumberValue` | Number of seconds that the indicator will appear for before fading; default is **1**. | | TransparencyBeforeFade | `Class.NumberValue` | Transparency of the indicator before it starts to fade; default is **0**. | | WidthLevel | `Class.NumberValue` | Number of width levels from center (each width level is about 0.03 screen scale); default is the value of **DistanceLevelFromCenter**. | ### Show damage billboard The damage billboard is used to show little numbers above a character's head when they are damaged. These will only show up for the player that damaged another player's character, not for spectating players. Damage billboards are handled in `WeaponsSystem/Libraries/DamageBillboardHandler` and can be activated from any client-side code as follows, where damage is the amount of damage done and adornmentPart is the part on which to adorn the billboard, such as the victim's head: ```lua DamageBillboardHandler:ShowDamageBillboard(damage, adornmentPart) ``` ## Shoulder camera The shoulder camera is a third-person camera that looks over the player character's right shoulder. To customize the shoulder camera, modify the variables under the -- Configuration parameters (constants) comment in the `ShoulderCamera.new()` function of `WeaponsSystem/Libraries/ShoulderCamera`. You can modify things such as field of view, offset from the character, walk speed while [sprinting or zooming](#sprint-and-zoom-control), etc. ## Sprint and zoom control By default, the weapons system adds "sprint" capability, so players can sprint by holding the `Shift` key, pushing fully up on the dynamic thumbstick (mobile), or pushing fully up on the left joystick (gamepad). If you want to disable sprinting, set the value of **SprintEnabled** within **WeaponsSystem**/**Configuration** to **false**. The system also reduces player speed while they're aiming/zooming, but you can disable this behavior by setting the value of **SlowZoomWalkEnabled** to **false**.
--- title: "How you can help us make Roblox safer" url: /docs/en-us/safety last_updated: 2026-06-29T19:34:10Z description: "Roblox is a 3D creation platform that provides everything you need to build, test, distribute, and monetize 3D creations." --- # How you can help us make Roblox safer Creating a successful Roblox experience goes beyond great gameplay; it involves building a safe and welcoming environment for all players. Creating a safe and welcoming platform is a collaborative partnership between Roblox and the developer community, and as a creator, you play a vital role in upholding our [Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410#safety). This guide provides an overview of Roblox's policies, essential safety tools, and design principles to help you foster a positive community. ## Design your experience with a safety-first mindset Proactive design choices are the most effective way to significantly enhance the safety of your experience. - **Safeguard User-Generated Content (UGC):** If your experience includes features that enable players to create and share their own content (such as art, outfits, emotes, text, or structures), consider potential misuse of these features and how to effectively keep them under control. This could involve pre-moderation before content goes public, tools to limit reach by making UGC ephemeral, granting UGC privileges to trusted users or only enabling pre-set options for customization. - **Monitor in-experience avatar editors:** Bad actors can mis-use avatar editor tools to create inappropriate content or combinations. Frequently monitor avatars in your experience and consider restricting the use of avatar/clothing editors if you see a high rate or increase in abuse. - **Avoid designing experiences that encourage abusive user behavior:** Address design decisions that lead to uncivil behaviour quickly. For experiences available to users under 18 years of age, avoid designing environments that primarily take place in private spaces, such as bedrooms, bathrooms, or closets. Build in bounding box limitations for assets that could potentially invoke bad user behavior, such as beds or sleeping surfaces. These settings can be misused by bad actors to create scenarios that violate our Community Standards. Experiences that primarily take place in adult settings like night clubs, dance clubs, or bars should also be restricted to users over 18. For more information, see [here](https://devforum.roblox.com/t/strengthening-our-safety-policies-and-tools/3882864#p-12931585-blocking-users-under-17-from-social-hangouts-with-private-spaces-and-experiences-primarily-set-in-private-spaces-4). ## Essential safety tools for every Creator Roblox provides tools to help you maintain a safe environment in your experience. Integrating these can help you maintain a safe environment in your experience. ### Complete the Content Maturity & Compliance Questionnaire All public experiences are required to have an accurately completed [Maturity & Compliance Questionnaire](https://create.roblox.com/docs/production/promotion/content-maturity). You should answer all questions truthfully to ensure your experience is rated appropriately. If you add new content that changes your answers, you should update the questionnaire immediately. Your experience and account could be moderated if you repeatedly answer the Maturity & Compliance Questionnaire incorrectly. ### Manage player behavior with bans, kicks, and verifications The `Class.Players:BanAsync()|Ban API` is your primary tool for removing disruptive users. When you ban a player using `BanAsync()`, you can provide a clear, policy-based reason. The system also helps detect potential alternate accounts of the banned user, making your moderation efforts more effective. Alternatively, you can use `Class.Players:Kick()|kick API` to disconnect abusive users from a server. You can designate trusted users to trigger this method on other users. For best practices on detecting and managing security and cheating in your experiences, see [Security tactics and cheat mitigation](/docs/en-us/scripting/security/security-tactics.md). If you want to gate parts of your experience to identity-verified players, you can use `Class.Player:IsVerified()|IsVerified API`. Tying identity verification signals to user accounts can deter behaviors like cheating, scamming, or harassment. Common use cases are protecting the integrity of in-experience ranking and trading. We suggest that you make benefits for account verification clear to the user at different points of the experience such as onboarding, exclusive features or special zone access. ### Use text filter on all user communications You should [filter](/docs/en-us/ui/text-filtering.md) all user-generated text that is visible to other players. This includes signs, pet names, or anything a player can type. The `Class.TextService` is essential for this. Use `Class.TextService.FilterStringAsync()` to prevent inappropriate language and the sharing of personally identifiable information. Roblox actively moderates the content of experiences to make sure they meet Community Standards. If Roblox receives reports or automatically detects that your experience doesn't apply text filtering, then the system may remove the experience until you add filtering. ### Ensure policy compliance The `Class.PolicyService` helps you tailor your experience to comply with policies based on a player's age, location, and platform. You can use it to manage content like in-game advertisements (`AreAdsAllowed`), paid random items (loot boxes), brand content and links to approved external social media sites. ### Monitor your safety analytics to understand toxicity in your experience [Analytics](#understand-your-safety-analytics) can help you better understand the abuse channels in your experiences. Prioritize moderating channels that have spikes in abuse reports. ## Understand your safety analytics You can find the Safety Dashboard in your [Creator Dashboard](%5Bcreate.roblox.com%5D(https://create.roblox.com)) for your experiences under **Safety** > **Overview**. The dashboard provides a top-level view of user-submitted abuse reports within your experience. ### Abuse report submitters per 1,000 playtime hours This chart shows the number of unique users who submitted abuse reports in your experience, normalized per 1,000 hours of playtime. You can see this chart if you have 1000+ daily playtime hours in the past week. **How to read it:** This metric allows you to monitor the frequency of abuse reports in your experience. If you see an increase in this number, this means that more abuse reports are being generated in your experience and this can be an early indicator of growing toxicity. You may need to investigate further (e.g. Did reports spike when you introduced a new feature?) take action before it becomes a larger issue. The benchmark is provided as a point of comparison. For example, a spike in abuse reports on February 2nd, after the launch of your new custom avatar editors, could indicate that users are misusing the feature or creating combinations that violate community standards. Potential solutions include rolling back the new functionality, increasing moderator support, or providing more proactive education on policies and community standards. ![A graph depicting categories of abuse reports per 1000 hours.](./assets/publishing/safety/Submitters-Per-Hours.png) ### Total abuse reports per category This chart breaks down abuse reports by category. These are the categories selected by users and may sometimes be inaccurate. Our moderation teams confirm both the category and whether the content violates our policies before taking action on an abuse report. **How to read it:** This breakdown helps you pinpoint the general types of negative behavior occurring in your experience so you can take more targeted action. For example, a high percentage in the "Romance or sexual" category might prompt you to review your in-game avatar editor tools or in-game chat systems. ![A graph depicting categories of abuse reports and trends over time.](./assets/publishing/safety/Abuse-Reports-Per-Category.png) ### Filter by channel You can filter both charts on the dashboard to isolate reports related to a specific part of your experience. When you apply a channel filter, the benchmark will also update to show you a comparison relevant to that channel. ![Dropdowns available to filter your data with per types of abuse reports.](./assets/publishing/safety/Filter-by-channel.png) Click the **Channel** dropdown to filter by: - **Avatar:** Reports related to user avatars, clothing, or accessories. - **Chat:** Reports related to in-experience text chat. - **Voice:** Reports related to in-experience voice chat. - **Experience:** Direct reports about the experience itself (e.g., inappropriate content). - **Audio:** Reports related to audio assets used in the experience. ### Automated safety insights To help you stay proactive, the dashboard will automatically display an insight if your rate of abuse report submitters rises above the 90th percentile benchmark, either overall or for a specific channel. ![An automated insight detailing recent spikes in abuse and suggestions to address it](./assets/publishing/safety/Automated-Insights.png) --- title: "Reach a massive global audience" url: /docs/en-us/scale last_updated: 2026-06-29T19:34:10Z description: "Learn how to scale on Roblox." --- # Reach a massive global audience Our efficient discovery capabilities offer a massive opportunity to find the right audience and acquire new users. We also provide a robust analytics suite of tools to measure and gain insights on your experience's performance. This allows you to adjust content strategies and rapidly iterate to get your desired outcome. ## Discovery for millions of users on a variety of devices and form factors Roblox works out of the box on PlayStation, Xbox, Android, iOS, PC, Mac, and Oculus. You get one-click publish and discovery that reaches hundreds of millions of users on a vast social network. [Discovery](/docs/en-us/discovery.md) [Roblox user base](/docs/en-us/production/roblox-user-base.md) ![Discovery for millions of users on a variety of devices and form factors](/assets/landing/device-family.png) ## Global and localized Users from all over the world can interact on our platform, enabled by our robust localization tools, letting you take advantage of automatic text and chat translation or custom translations provided by you. [Localization](/docs/en-us/production/localization.md) ![Global and localized](/assets/landing/chat-translation.jpg) # Analyze and grow your creations Roblox offers a variety of analytics features to help you chart growth, track user behavior and retention, and find opportunities for optimization. You can use analytics to understand what actions you can take to grow your creations. ## View metrics and performance Measure and gain insight into your experience's performance to adjust content strategies. View dashboards on retention, engagement, acquisition, and monetization. [Core metrics](/docs/en-us/production/analytics/analytics-dashboard.md) [Performance](/docs/en-us/production/analytics/performance.md) ![View metrics and performance](/assets/analytics/analytics-dashboard/Explore-Page.png) ## Create and track events Add event tracking to your experiences and visualize monetization and usage patterns. [Events](/docs/en-us/production/analytics/event-types.md) ![Create and track events](/assets/analytics/event-types/Overview-Economy-Funnels.png) ## Grow your experiences with great design Learn how successful experiences design their core game loops, UI and UX, monetization, and more. [Design](/docs/en-us/production/game-design.md) ![Grow your experiences with great design](./assets/game-design/introduction-to-quest-design/quest-design-7.png) --- title: "Properties and attributes" url: /docs/en-us/scripting/attributes last_updated: 2026-06-29T19:34:10Z description: "How to use scripts to manipulate object properties and attributes." --- # Properties and attributes Making games interactive often means manipulating object properties and attributes: - Properties are part of the object class. For example, the `Class.BasePart.Anchored` property controls physics for the part. In a track and field game, you might want to anchor a discus or javelin the instant it lands so that players have a visual indicator of how far it traveled. - Attributes are essentially custom properties that you define. For example, the [Plant](/docs/en-us/resources/plant-reference-project.md) reference project uses attributes to set the purchase price for seeds and the maximum plant size that a pot can hold. ## Replication order Before you begin retrieving and manipulating objects, you must have an understanding of replication order. The Roblox Engine doesn't guarantee the order in which objects (and changes to objects) are replicated from the server to the client, which makes the `Class.Instance:WaitForChild()` method essential for accessing objects in client scripts, particularly objects in the `Class.Workspace`. For example, if a server script changes a property of some instance in the Workspace and then calls `Class.RemoteEvent:FireAllClients()`, the property change might replicate to the client before or after `Class.RemoteEvent.OnClientEvent` fires. Use the available methods and events to [detect changes](#detect-changes) rather than assuming a change has replicated. Changes of the same type, such as two attribute changes, generally **do** arrive in order. When clients first launch a game, some aspects of replication and script execution are predictable: 1. The client loads the contents of `Class.ReplicatedFirst`, such as a loading screen, assets, and scripts. 2. `Class.LocalScript|LocalScripts` (and `Class.Script|Scripts` with a `Class.Script.RunContext|RunContext` of `Enum.RunContext.Client|Client`) in `Class.ReplicatedFirst` run. These scripts can safely get objects from `Class.ReplicatedFirst` without using `Class.Instance:WaitForChild()`:```lua -- Safe local ReplicatedFirst = game:GetService("ReplicatedFirst") local LoadingScreen = require(ReplicatedFirst.LoadingScreen) ``` These scripts **can't** safely get objects from other [services](/docs/en-us/scripting/services.md), because they might not have loaded yet:```lua -- Not safe local ReplicatedStorage = game:GetService("ReplicatedStorage") local PickupManager = require(ReplicatedStorage.PickupManager) ``` You **can** use `Class.Instance:WaitForChild()|WaitForChild()` in these scripts to get objects from other services, but doing so negates the benefits of using `Class.ReplicatedFirst`. 3. The client continues loading the rest of the game. 4. When it finishes, the `Class.DataModel.Loaded|Loaded` event fires and `Class.DataModel:IsLoaded()|IsLoaded()` returns true. 5. `Class.LocalScript|LocalScripts` in `Players.[Player].PlayerScripts` (copied from `Class.StarterPlayerScripts`) run, as well as client `Class.Script|Scripts` in `Class.ReplicatedStorage`. These scripts can safely get objects from `Class.ReplicatedStorage` without using `Class.Instance:WaitForChild()|WaitForChild()`. 6. The player's `Class.Player.Character|Character` model spawns in the game. 7. `Class.LocalScript|LocalScripts` in `Workspace.[Character]` (copied from `Class.StarterCharacterScripts`) run. If your game uses [instance streaming](/docs/en-us/workspace/streaming.md) (`Class.Workspace.StreamingEnabled`), some or most objects might not have loaded into the workspace, so using `Class.Instance:WaitForChild()|WaitForChild()` to access workspace objects becomes an even more important safety measure. In particular, see [stream in](/docs/en-us/workspace/streaming.md#stream-in) and [per-model streaming controls](/docs/en-us/workspace/streaming.md#per-model-streaming-controls) for additional information on loading and tuning streaming behavior. ## Get objects The first step to modifying object properties and attributes is to get a reference to the object. The simplest solution is to make the script a child of the object in the **Explorer** and use `script.Parent` to reference the object. ![A script parented to a model in the Explorer.](../assets/studio/explorer/Script-Parent-Model.png) ```lua local sign = script.Parent ``` The more universal solution is to get the object from a [service](/docs/en-us/scripting/services.md) using methods like `Class.Instance:FindFirstChild()` or `Class.Instance:WaitForChild()`. ![A Model within a Folder in ReplicatedStorage.](../assets/studio/explorer/Script-Parent-ReplicatedStorage.png) ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local signsFolder = ReplicatedStorage:WaitForChild("Signs") local sign = signsFolder:WaitForChild("InteractiveSign") ``` ## Modify properties Properties are straightforward to access — just use a `.` after the object reference — although if you're working with a model, you might need to choose an individual part rather than the model itself. ![A Model within ReplicatedStorage.](../assets/studio/explorer/ReplicatedStorage-Model.png) ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local chair = ReplicatedStorage:WaitForChild("Chair") chair.LeftArmRest.Size = Vector3.new(10, 1, 10) ``` ## Create attributes Although you can create attributes programmatically, the more common solution is to create them with default values in the Studio user interface. Then you can use scripts to modify their values in response to player actions. ![A script within a folder in ReplicatedStorage.](../assets/studio/properties/Attributes-Example.png) For information on creating attributes in Studio, see [instance attributes](/docs/en-us/studio/properties.md#instance-attributes). ## Set attributes To modify an attribute's value, call `Class.Instance:SetAttribute()` with a name and value. ```lua local cabbage = script.Parent cabbage:SetAttribute("Harvestable", true) ``` If the attribute doesn't already exist, this method creates it. ## Get attribute values To get the value of one existing attribute, call `Class.Instance:GetAttribute()` on the instance. ```lua local cabbage = script.Parent cabbage:SetAttribute("Harvestable", true) local isHarvestable = cabbage:GetAttribute("Harvestable") print(isHarvestable) --> true ``` Similarly, you can get all attributes by calling `Class.Instance:GetAttributes()`. This method returns a dictionary of key-value pairs. ```lua local cabbage = script.Parent local cabbageAttributes = cabbage:GetAttributes() print(cabbageAttributes.GrowthRate) --> 2 for k, v in cabbageAttributes do print(k, v) end ``` ## Delete attributes To delete an attribute, set its value to `nil`. ```lua local cabbage = script.Parent cabbage:SetAttribute("GrowthRate", nil) ``` ## Detect changes There are several ways to listen for changes to properties and attributes: - The `Class.Instance.AttributeChanged` event listens for changes to any attribute and passes the name of the changed attribute as a parameter. - The `Class.Instance:GetPropertyChangedSignal()` method lets you listen for changes to one property and passes no parameters. - The `Class.Instance:GetAttributeChangedSignal()` method lets you listen for changes to one attribute and passes no parameters. Due to the minimal information that these events and methods pass as parameters, all of them are a good fit for anonymous functions, particularly `Class.Instance:GetPropertyChangedSignal()` and `Class.Instance:GetAttributeChangedSignal()`. To learn more about anonymous functions and working with events, see [Events](/docs/en-us/scripting/events.md). ```lua local cabbage = script.Parent -- Local functions local function onAnyPropertyChange(property) -- Ignore changes to attributes if property ~= "Attributes" then print(property) --> Name print(cabbage[property]) --> Cabbage1 end end local function onAnyAttributeChange(attribute) print(attribute) --> Grow, GrowthRate print(cabbage:GetAttribute(attribute)) --> false, 3 end -- Listen for changes and connect to local functions cabbage.Changed:Connect(onAnyPropertyChange) cabbage.AttributeChanged:Connect(onAnyAttributeChange) -- Listen for changes and connect to anonymous functions cabbage:GetPropertyChangedSignal("Name"):Connect(function() print(cabbage.Name) --> Cabbage1 end) cabbage:GetAttributeChangedSignal("GrowthRate"):Connect(function() print(cabbage:GetAttribute("GrowthRate")) --> 3 end) -- Fires Changed and GetPropertyChangedSignal() cabbage.Name = "Cabbage1" -- Fires Changed and AttributeChanged cabbage:SetAttribute("Grow", false) -- Fires Changed, AttributeChanged, and GetAttributeChangedSignal() cabbage:SetAttribute("GrowthRate", 3) ``` --- title: "Script capabilities" url: /docs/en-us/scripting/capabilities last_updated: 2026-06-29T19:34:10Z description: "Script capabilities let you control which actions scripts can perform." --- # Script capabilities > **Success:** Script capabilities are experimental and available as a client beta. **Script capabilities** let you control which actions scripts can perform inside the `Class.DataModel` subtree. Rather than the default "all-or-nothing system," you can set a script to only be able to access certain categories, such as audio, physics, data stores, and more. - This system lets you limit what models taken from the toolbox can do and makes it easier to include user-generated content inside the game. - It can also help ensure better security of games that allow players to run their own code, which is often executed in a restricted or emulated environment. - It can also be used to share libraries that restrict what they can do themselves. For example, a library providing additional math methods can be restricted to the smallest set of capabilities it needs so that other developers using that library don't have to validate the entire codebase to make sure it doesn't include malicious code. ## Enable script capabilities To enable this feature, change the `Class.Workspace.SandboxedInstanceMode|SandboxedInstanceMode` property from `Default` to `Experimental` in the **Explorer** and **Properties** window. ## Sandboxed container Script capabilities introduces a concept of a **sandboxed container**. `Class.Model|Models`, `Class.Folder|Folders`, `Class.Script|Scripts`, or descendants of any of those classes have a `Class.Instance.Sandboxed|Sandboxed` property available in the **Properties** window. ![Sandboxed property of a Folder in the Properties window.](../assets/studio/properties/Folder-Sandboxed.png) Enabling the `Class.Instance.Sandboxed|Sandboxed` property designates the instance as a sandboxed container inside the `Class.DataModel` tree, which limits the actions that the scripts inside that container can perform based on the set of values in the `Class.Instance.Capabilities|Capabilities` property. ## Capabilities The `Capabilities` property is a set of values that control different aspects of execution. Capabilities fall into the following broad categories: - **Execution control** - Specifies if a script can run on client or server - **Instance access control** - Specifies which `Class.DataModel` parts a script can interact with - **Script functionality control** - Specifies which Luau functions scripts can call - **Engine API access control** - Specifies which parts of the Roblox Engine API can be used When a script attempts to perform an action that is not in the set of capabilities, Roblox reports an error. Errors typically include the action being attempted, the target of an action, and the first capability that is missing: ```text The current thread cannot modify 'Workspace' (lacking capability AccessOutsideWrite) The current thread cannot call 'Clone' (lacking capability CreateInstances) The current thread cannot call 'GetSecret' (lacking capability Network) ``` ### Execution control Two capabilities handle execution control: - **RunClientScript** - `Class.LocalScript` or `Class.Script` with a `Class.BaseScript.RunContext|RunContext` value of `Enum.RunContext.Client|Client` is allowed to execute on the client - **RunServerScript** - `Class.Script` with a `Class.BaseScript.RunContext|RunContext` value of `Enum.RunContext.Server|Server` is allowed to execute on the server If the script is `Class.BaseScript.Enabled|Enabled`, but the capability corresponding to the location it attempts to start in is not available, a warning message is displayed in the **Output** window. If a script wasn't supposed to run in that context, disable or delete it. Note that `Class.ModuleScript|ModuleScripts` do not need to have these execution capabilities to be required. When a script fails to start because the execution control capability is missing, it is reported as a warning in the output, for example: ```text Cannot start server script 'Script' (lacking capability RunServerScript) ``` ### Instance access control A single capability handles instance access control: - **AccessOutsideWrite** - Script is allowed to fetch and receive instances from outside the sandboxed container When the capability is not available, the script can only look up instances that are inside its own sandboxed container. For example, if the script is placed directly in the sandboxed container, `script.Parent.Parent` returns `nil`. Additionally, any Roblox API event that passes in an `Class.Instance` instead passes in `nil` for any `Class.Instance` outside the sandboxed container. For example, if the `Class.BasePart.Touched` is signaled by a touch from an instance outside the sandboxed container, the event is still received, but the argument is `nil`. Avoid setting this capability; sandboxing guarantees are weaker when scripts can interact with any instance in a game. #### Access to services Even without **AccessOutsideWrite**, scripts in the sandboxed container can still access `game`, `workspace`, and services. This access is provided so that scripts can still call useful methods of those globals, like `Class.DataModel.GetService`, but access to their child instances is still validated. #### Internally passed instances If an instance is passed through a function call that doesn't go through Roblox APIs, the reference is preserved. However, if a `Class.ModuleScript` is passed in this way, it can't be required without **AccessOutsideWrite**. This is because the return of the `Class.ModuleScript` is often mutable and can be modified by a script in a sandboxed container. ### Script functionality control This set of capabilities controls some general aspects of scripts: - **CreateInstances** - Script can create new instances using `Datatype.Instance.new`, `Datatype.Instance.fromExisting`, or `Class.Instance:Clone` - **LoadString** - Script is allowed to call `Global.LuaGlobals.loadstring` - **LoadUnownedAsset** - Script is allowed to call `Global.LuaGlobals.require` with an asset ID or `Class.AssetService:LoadAssetAsync` - **ScriptGlobals** - Script has `Global.LuaGlobals.shared` and `Global.LuaGlobals._G` available Keep in mind that default function restrictions still apply. Even if **LoadString** is enabled, the game still has to enable it in `Class.ServerScriptService`, and it is still only available on the server. To create new instances, aside from **CreateInstances**, an additional Engine API capability providing access to that instance is required. ### Engine API access control This last group of capabilities controls script access to various Engine APIs: - **Animation** - Access to instances related to animations - **AssetCreateUpdate** - Access to Engine APIs related to creating or updating assets - **AssetManagement** - Access to Engine APIs related to operations on assets that are not read, create, or update - **AssetRead** - Access to Engine APIs related to getting information about assets - **Audio** - Access to instances related to audio APIs - **AvatarAppearance** - Access to instances related to the visual elements of avatars - **AvatarBehavior** - Access to Engine APIs related to controlling or modifying the humanoid - **Basic** - Access to general APIs that offer little risk - **CSG** - Access to instances and functions related to constructive solid geometry (CSG) - **CapabilityControl** - Access to modifying the `Sandboxed` and `Capabilities` properties of instances - **Capture** - Access to Engine APIs related to capturing screenshots or videos on the user's screen - **Chat** - Access to instances related to in-game chat - **Consequences** - Access to APIs for punishing users (kicks or bans) - **DataStore** - Access to data store and memory store APIs - **DynamicGeneration** - Access to Engine APIs related to dynamically modifying assets - **Environment** - Access to instances related to the control of how the environment is displayed - **Groups** - Access to Engine APIs relating to groups/communities - **Input** - Access to instances related to user input - **LegacySound** - Access to Engine APIs related to sound that will be deprecated - **LoadOwnedAsset** - Access to Engine APIs for loading assets that are owned by you, shared to you, owned by Roblox, or are a benign asset type - **Logging** - Access to the logs API - **Material** - Access to Engine APIs related to materials - **Monetization** - Access to Engine APIs related to monetizing a game, excluding most `PromptPurchases` - **Network** - Access to HTTP networking APIs - **Physics** - Access to instances related to physics - **PlatformAvatarEditing** - Access to Engine APIs related to creating or updating avatars, or APIs to aid with that - **Players** - Access to Engine APIs related to the `Class.Player` class or that return/interact with `Class.Players.LocalPlayer` - **PromptExternalPurchase** - Allows access to Engine APIs related to prompting purchase of arbitrary assets - **RemoteEvent** - Access to instances for internal networking operations - **SensitiveInput** - Access to Engine APIs related to capturing sensitive input from the user, i.e. high-privacy hardware sensors. - **ServerCommunication** - Access to Engine APIs related to cross-server communication, such as `Class.MessagingService` - **Social** - Access to Engine APIs related to social features, such as friends and invites - **Teleport** - Access to Engine APIs related to teleporting - **UI** - Access to instances related to user interfaces The capabilities of each class, property, method, and event are displayed in the [Engine API reference](/docs/en-us/reference/engine.md). For example, `Class.Player:GetFriendsOnlineAsync()` requires the `Player` and `Social` capabilities. Several `Class.HttpService` methods are available without any capabilities aside from being able to execute the scripts: - `Class.HttpService:JSONDecode()` - `Class.HttpService:JSONEncode()` - `Class.HttpService:GenerateGUID()` If an instance property or method is accessed without a required capability, an error is reported describing the missing capability. Finally, capabilities do not cover every instance in the Roblox Engine today. Instances not listed in this section or the following one are not available for interaction from a sandboxed container and throw an error saying that an **Unassigned** capability is not available to the current script. An additional limitation is that `Global.LuaGlobals.getfenv()` and `Global.LuaGlobals.setfenv()` functions are not available for scripts in a sandboxed container. Calling them from a sandboxed script reports: ```text The current thread cannot call ['getfenv'/'setfenv'] - the target uses APIs not available in the current sandbox ``` Only script access to instances is limited. The instances themselves can still exist and operate by themselves inside a sandboxed container. Lights still shine, user interfaces are still visible, and audio setups that are already wired play sounds. ## Interactions between containers ### Nested containers When one sandboxed container is nested inside another one, the instances of the inner container are accessible to the outer one. Capabilities of the inner container are limited by the capabilities of the outer one. For example, if the outer container has capabilities of **Basic**, **Audio** and **CSG**, while the inner container has **Basic** and **Network**, only **Basic** capabilities are available to the inner container at runtime. If there are no capabilities in common between the inner and outer containers, the resulting capability set is empty. ### Bindable functions and events `Class.BindableEvent` and `Class.BindableFunction` provide the best way to communicate with the container or allow it to run callbacks with capabilities it itself is not allowed to use directly. When an event is fired or a function is invoked, connections are executed in the context of the function that registered them. This means that if the event or function callback is registered by the sandboxed container, it is called with the capabilities of that container. If the callback is registered by the code outside, when sandboxed container scripts invoke them, they execute your functions with capabilities available to your functions. It is important to note that even with the **AccessOutsideWrite** capability, scripts in sandboxed containers cannot invoke events or functions outside their containers if they have a larger capability set than the container itself. When a sandboxed script tries to fire or invoke an event or function (`Class.BindableEvent`, `Class.BindableFunction`, or `Class.RemoteEvent`) whose ancestor capabilities exceed its own, the error names the property to inspect. If the target is not inside any sandboxed container: ```text The current thread cannot fire '' since '' has the Sandboxed property set to false but the calling thread is sandboxed ``` To resolve this, set `Class.Instance.Sandboxed|Sandboxed` to `true` for the target. Alternatively, you can modify the source of the calling thread to have `Class.Instance.Sandboxed|Sandboxed` set to `false`, but this is not recommended as it eliminates the security benefits sandboxing provides. If the target is sandboxed but has capabilities the caller lacks: ```text The current thread cannot fire '' since '' has additional values for the Capabilities property: (and N more) ``` To resolve this, narrow the target's `Class.Instance.Capabilities|Capabilities` to a subset of the caller's, or expand the caller's `Class.Instance.Capabilities|Capabilities` to match the target's. ### Module require Inner `Class.ModuleScript|ModuleScripts` can be required by the sandboxed container as usual. However, if the target instance is outside the container, the `Class.ModuleScript` can only be required if the capability set that is available to it is smaller or equal to the capabilities available to the container. This limitation does not apply to **RunClientScript** and **RunServerScript** capabilities. If the `Class.ModuleScript` is placed in a container with only **RunClientScript** but is required from a script that has the **RunServerScript** capability, it is allowed to succeed and run those functions on the server. When `Global.LuaGlobals.require()` fails on a capability mismatch, the error names the target module and the property to inspect in the same manner as Bindable functions and events described in the previous section. ### Directly called functions If a `Class.ModuleScript` in a sandboxed container is required from outside the container, some of the protections are not available. In particular, the target function is able to access all instances available to the caller. If the caller is not in a sandboxed container, the call acts as if **AccessOutsideWrite** is available to it. Other capability restrictions still apply. If you have a **DataStore** access capability, but the target module does not, it is unable to call `Class.DataStore` methods. However, if you pass your own function working with `Class.DataStore`, the target can run it during that call. If the target schedules a thread using methods like those from `Library.task`, those threads lose the ability to call that function. Instances can be passed to the target module or assigned to the module fields. If required, it is recommended to assign table members using `Global.LuaGlobals.rawset` to avoid running `__index`/`__newindex` metamethods that might be set on the table. The overall recommendation is to communicate with `Class.BindableEvent` and `Class.BindableFunction` whenever possible. ### Movement of instances Most instances do not have restrictions on movement between containers. Script instances, however, can only be moved into a container that has the same set of capabilities or a subset of those capabilities. This means that a sandboxed container with **AccessOutsideWrite** cannot just re-parent a script inside itself to outside and get more capabilities. --- title: "Debounce patterns" url: /docs/en-us/scripting/debounce last_updated: 2026-06-29T19:34:10Z description: "Debounce patterns are coding techniques that prevent a function from running too many times." --- # Debounce patterns A **debounce** pattern is a coding technique that prevents a function from running too many times or an input from triggering multiple times. The following scripting scenarios illustrate debounce as a best practice. ## Detect collisions Suppose you want to create a hazardous trap part that inflicts 10 damage when touched. An initial implementation might use a basic `Class.BasePart.Touched` connection and a `damagePlayer` function like this: ```lua local part = script.Parent local function damagePlayer(otherPart) print(part.Name .. " collided with " .. otherPart.Name) local humanoid = otherPart.Parent:FindFirstChildWhichIsA("Humanoid") if humanoid then humanoid.Health -= 10 -- Reduce player health end end part.Touched:Connect(damagePlayer) ``` While logical at first glance, testing will show that the `Class.BasePart.Touched|Touched` event fires multiple times in quick succession based on subtle physical collisions. To avoid causing excessive damage on initial contact, you can add a debounce system which enforces a cooldown period on damage through an [instance attribute](/docs/en-us/studio/properties.md#instance-attributes). ```lua local part = script.Parent local RESET_TIME = 1 local function damagePlayer(otherPart) print(part.Name .. " collided with " .. otherPart.Name) local humanoid = otherPart.Parent:FindFirstChildWhichIsA("Humanoid") if humanoid then if not part:GetAttribute("Touched") then part:SetAttribute("Touched", true) -- Set attribute to true humanoid.Health -= 10 -- Reduce player health task.wait(RESET_TIME) -- Wait for reset duration part:SetAttribute("Touched", false) -- Reset attribute end end end part.Touched:Connect(damagePlayer) ``` ## Trigger sounds Debounce is also useful when working with sound effects, such as playing a sound when two parts collide (`Class.BasePart.Touched|Touched`), or playing a sound on the `Class.GuiButton.Activated|Activated` event when a user interacts with an on-screen button. In both cases, calling `Class.Sound:Play()` starts playback from the beginning of its track and — without a debounce system — the sound may play multiple times in quick succession. To prevent sound overlap, you can debounce using the `Class.Sound.IsPlaying|IsPlaying` property of the `Class.Sound` object: ```lua local projectile = script.Parent local function playSound() -- Find child sound on the part local sound = projectile:FindFirstChild("Impact") -- Play the sound only if it's not already playing if sound and not sound.IsPlaying then sound:Play() end end projectile.Touched:Connect(playSound) ``` ```lua local button = script.Parent local function onButtonActivated() -- Find child sound on the button local sound = button:FindFirstChild("Click") -- Play the sound only if it's not already playing if sound and not sound.IsPlaying then sound:Play() end end button.Activated:Connect(onButtonActivated) ``` ## Pickup effects Games often include collectible pickups in the 3D world such as medkits, ammo packs, and more. If you design these pickups to remain in the world for players to grab again and again, a "cooldown" time should be added before the pickup refreshes and reactivates. Similar to [detecting collisions](#detect-collisions), you can manage the debounce state with an [instance attribute](/docs/en-us/studio/properties.md#instance-attributes), and visualize the cooldown period by changing the part's `Class.BasePart.Transparency|Transparency`. ```lua local part = script.Parent part.Anchored = true part.CanCollide = false local COOLDOWN_TIME = 5 local function healPlayer(otherPart) local humanoid = otherPart.Parent:FindFirstChildWhichIsA("Humanoid") if humanoid then if not part:GetAttribute("CoolingDown") then part:SetAttribute("CoolingDown", true) -- Set attribute to true humanoid.Health += 25 -- Increase player health part.Transparency = 0.75 -- Make part semi-transparent to indicate cooldown state task.wait(COOLDOWN_TIME) -- Wait for cooldown duration part.Transparency = 0 -- Reset part to fully opaque part:SetAttribute("CoolingDown", false) -- Reset attribute end end end part.Touched:Connect(healPlayer) ``` > **Info:** This cooldown concept can be applied to other scenarios as well, such as creating a "reload" time for a rocket launcher to limit how frequently players can fire rockets. --- title: "Bindable events and callbacks" url: /docs/en-us/scripting/events/bindable last_updated: 2026-06-29T19:34:10Z description: "Bindable events and callbacks allow for back-and-forth communication on the same side of the client-server boundary." --- # Bindable events and callbacks `Class.BindableEvent` and `Class.BindableFunction` objects let you bind behaviors between scripts **on the same side** of the [client-server](/docs/en-us/projects/client-server.md) boundary and communicate a specific desired outcome for in-game actions. The most common use case for bindable events is for games that have a round-based structure. For example, you might have a "match started" event that lets other scripts start a timer and display a leaderboard, with a corresponding "match ended" event that lets other scripts know when to move players back into a lobby and display the winners. Because they coordinate activities between scripts, bindable events are typically used on the server, but you can use them on the client, too. Depending on how your game works, bindable events can help make your code more modular, but [module scripts](/docs/en-us/scripting/module.md) are often a better alternative for situations in which you need to share data between scripts. You can also use bindable events in conjunction with module scripts for a cleaner syntax, as noted in [Custom events](/docs/en-us/scripting/module.md#custom-events). > **Info:** To communicate between scripts **across** the client-server boundary, see [Remote events](/docs/en-us/scripting/events/remote.md). ## Bindable events The `Class.BindableEvent` object enables custom events through asynchronous, one-way communication between scripts. When you fire a `Class.BindableEvent` through the `Class.BindableEvent:Fire()|Fire()` method, the firing script does **not** yield, and the target function receives the passed arguments with certain [limitations](#argument-limitations). Like all events, `Class.BindableEvent|BindableEvents` create threads of each connected function, so even if one errors, others continue. To create a new `Class.BindableEvent` using the [Explorer](/docs/en-us/studio/explorer.md) window in Studio: 1. Hover over the container into which you want to insert the `Class.BindableEvent`. We recommend using `Class.ServerScriptService` for communication between server scripts and `Class.ReplicatedStorage` for communication between client scripts. 2. Click the **⊕** button that appears to the right of the container's name and insert a **BindableEvent** instance. 3. Rename the instance to `TestBindableEvent`. After you've created a `Class.BindableEvent`, connect a function to its `Class.BindableEvent.Event|Event` event in one script, and then `Class.BindableEvent:Fire()|Fire()` the event from another script. ```lua local ServerScriptService = game:GetService("ServerScriptService") -- Get reference to bindable event instance local bindableEvent = ServerScriptService:WaitForChild("TestBindableEvent") -- Connect anonymous function to event bindableEvent.Event:Connect(function(data) print(data) --> Round started! end) ``` ```lua local ServerScriptService = game:GetService("ServerScriptService") -- Get reference to bindable event instance local bindableEvent = ServerScriptService:WaitForChild("TestBindableEvent") -- Fire bindable event bindableEvent:Fire("Round started!") ``` > **Warning:** You can connect multiple functions to the same `Class.BindableEvent`, but Luau executes them in an unpredictable order. To ensure that functions execute in a particular order, combine them into a single function and connect it to the event. ## Custom callbacks The `Class.BindableFunction` object allows for synchronous, two-way communication between scripts. You can use it to define a custom callback function and invoke it manually by calling `Class.BindableFunction:Invoke()`. The code invoking the function **yields** until the corresponding callback is found, and the callback receives the arguments that you passed to `Class.BindableFunction:Invoke()|Invoke()`. If the callback was never set, the script that invokes it doesn't resume execution. To create a new `Class.BindableFunction` using the [Explorer](/docs/en-us/studio/explorer.md) window in Studio: 1. Hover over the container into which you want to insert the `Class.BindableFunction`. We recommend using `Class.ServerScriptService` for communication between server scripts and `Class.ReplicatedStorage` for communication between client scripts. 2. Click the **⊕** button that appears to the right of the container's name and insert a **BindableFunction** instance. 3. Rename the instance to `TestBindableFunction`. Once you've created a `Class.BindableFunction`, you can connect to its `Class.BindableFunction.OnInvoke|OnInvoke` callback in one script, then `Class.BindableFunction:Invoke()|Invoke()` the callback function from another script. ```lua local ServerScriptService = game:GetService("ServerScriptService") -- Get reference to bindable function local bindableFunction = ServerScriptService:WaitForChild("TestBindableFunction") -- Callback function local function addTwoNumbers(a, b) return a + b end -- Set function as bindable function's callback bindableFunction.OnInvoke = addTwoNumbers ``` ```lua local ServerScriptService = game:GetService("ServerScriptService") -- Get reference to bindable function local bindableFunction = ServerScriptService:WaitForChild("TestBindableFunction") -- Invoke callback function and output returned value local sum = bindableFunction:Invoke(2, 4) print(sum) --> 6 ``` > **Warning:** Each `Class.BindableFunction` can only utilize one `Class.BindableFunction.OnInvoke|OnInvoke` callback. If you make multiple definitions, only the last one assigned runs. Also note that if the `Class.BindableFunction.OnInvoke|OnInvoke` callback does not have a `return` statement, the invocation returns `nil`. ## Argument limitations When you fire a `Class.BindableEvent` or invoke a `Class.BindableFunction`, it forwards any arguments that you pass with the event or to the callback function. You can pass any type of Roblox object (`Datatype.Enum`, `Class.Instance`, etc.), as well as Luau types like numbers, strings, and booleans, although you should carefully consider the following limitations. ### Non-string indices If any **indices** of a passed table are non-string types, such as an `Class.Instance`, [userdata](/docs/en-us/luau/userdata.md), or [function](/docs/en-us/luau/functions.md), Roblox automatically converts those indices to strings. ```lua local ServerScriptService = game:GetService("ServerScriptService") local bindableEvent = ServerScriptService:WaitForChild("TestBindableEvent") local function onEventFire(passedTable) for k, v in passedTable do print(typeof(k)) --> string end end -- Connect function to event bindableEvent.Event:Connect(onEventFire) ``` ```lua local ServerScriptService = game:GetService("ServerScriptService") local bindableEvent = ServerScriptService:WaitForChild("TestBindableEvent") -- Fire event with table containing a workspace instance as a key bindableEvent:Fire({ [workspace.Baseplate] = true }) ``` ### Table indexing If you pass a table of data, do not pass a mixed table of numeric and string keys. Doing so can result in removed elements during the transfer. Instead, pass a table that consists **entirely** of key-value pairs (a dictionary) or **entirely** of numeric indices (an array). > **Warning:** In both cases, whether passing a dictionary table or a numerically indexed table, avoid `nil` values for any index. ```lua local ServerScriptService = game:GetService("ServerScriptService") local bindableEvent = ServerScriptService:WaitForChild("TestBindableEvent") local function onEventFire(passedTable) for k, v in passedTable do print(k .. " = " .. v) --> 1 = Sword --> 2 = Bow --> CharName = Diva Dragonslayer --> CharClass = Rogue end end -- Connect function to event bindableEvent.Event:Connect(onEventFire) ``` ```lua local ServerScriptService = game:GetService("ServerScriptService") local bindableEvent = ServerScriptService:WaitForChild("TestBindableEvent") -- Numerically indexed table local inventoryData = { "Sword", "Bow" } -- Dictionary table local characterData = { CharName = "Diva Dragonslayer", CharClass = "Rogue" } -- Fire event with consistently-indexed tables bindableEvent:Fire(inventoryData) bindableEvent:Fire(characterData) ``` ### Table identities Tables passed as arguments to bindable events and callbacks are copied, meaning they will not be exactly equivalent to those provided when firing the event or invoking the callback. Nor will tables returned to the invoker be exactly equivalent to those provided. You can demonstrate this by running the following script on a `Class.BindableFunction` and observing how the table identities differ. ```lua local ServerScriptService = game:GetService("ServerScriptService") local bindableFunction = ServerScriptService:WaitForChild("TestBindableFunction") -- Callback function local function returnTable(passedTable) -- Output table identity on invocation print(tostring(passedTable)) --> table: 0x48eb7aead27563d9 return passedTable end -- Set function as bindable function's callback bindableFunction.OnInvoke = returnTable ``` ```lua local ServerScriptService = game:GetService("ServerScriptService") local bindableFunction = ServerScriptService:WaitForChild("TestBindableFunction") local inventoryData = { "Sword", "Bow" } -- Output original table identity print(tostring(inventoryData)) --> table: 0x059bcdbb2b576549 local invokeReturn = bindableFunction:Invoke(inventoryData) -- Output table identity upon return print(tostring(invokeReturn)) --> table: 0x9fcae7919563a0e9 ``` ### Metatables If a table has a metatable, all of the metatable information is lost in the transfer. In the following code sample, the `NumWheels` property is part of the `Car` metatable. When the server receives the following table, the `truck` table has the `Name` property but **not** the `NumWheels` property. ```lua local ServerScriptService = game:GetService("ServerScriptService") local bindableEvent = ServerScriptService:WaitForChild("TestBindableEvent") local function onEvent(param) print(param) --> {["Name"] = "MyTruck"} end -- Connect function to event bindableEvent.Event:Connect(onEvent) ``` ```lua local ServerScriptService = game:GetService("ServerScriptService") local bindableEvent = ServerScriptService:WaitForChild("TestBindableEvent") local Car = {} Car.NumWheels = 4 Car.__index = Car local truck = {} truck.Name = "MyTruck" setmetatable(truck, Car) -- Fire event with table including a metatable bindableEvent:Fire(truck) ``` --- title: "Deferred engine events" url: /docs/en-us/scripting/events/deferred last_updated: 2026-06-29T19:34:10Z description: "Deferred engine events defer event handlers until certain resumption points." --- # Deferred engine events The `Class.Workspace.SignalBehavior` property controls whether event handlers are fired immediately or deferred. The `Enum.SignalBehavior.Deferred` option is recommended which helps improve the performance and correctness of the engine. The event handlers for **deferred events** are resumed at the next [resumption point](#resumption-points), along with any newly triggered event handlers. > **Info:** The `Enum.SignalBehavior.Default` value of `Class.Workspace.SignalBehavior` is currently equivalent to `Enum.SignalBehavior.Immediate`, but will eventually switch to being equivalent to `Enum.SignalBehavior.Deferred`. Template places are directly set to `Enum.SignalBehavior.Deferred` by default. The following diagram compares the `Enum.SignalBehavior.Immediate|Immediate` event behavior and the `Enum.SignalBehavior.Deferred|Deferred` event behavior. - With the `Immediate` behavior, if an event triggers another event, the second event handler fires immediately. - With the `Deferred` behavior, the second event is added to the back of a queue and run later. The total time taken does not change, but the ordering is different. ![A comparison of three event handlers firing with Immediate and Deferred behavior](../../assets/scripting/scripts/ImmediateVsDeferredEvents.png) "Re-entrancy" prevents events from continuously firing one another when they reach a certain depth. The current limit for this is 10. ## Deferred event benefits The `Immediate` behavior has some disadvantages. For every instance added to your game, property that changes, or some other trigger that is invoked, the engine needs to run Luau code before anything else happens. - To change 1,000 properties, 1,000 snippets of code potentially need to run after each change. - Strange, hard-to-diagnose bugs can occur, such as a removing event firing before something was even added. - Performance-critical systems can fire events requiring them to yield back and forth to Luau. - Event handlers can make changes to the place or trigger other events any time an event is fired. - An event can fire multiple times despite being redundant, such as a property changing twice. By having specific portions of the engine life cycle in which Luau can run, the engine can gain improved performance by using a number of assumptions: - Performance-critical systems don't need to yield to Luau, which leads to performance gains. - Unless the engine itself changes it, the place never changes outside of a resumption point. ## Resumption points After being deferred, an event handler is resumed at the next resumption point. Currently, the set of resumption points includes: - Input processing (resumes once per input to be processed, see `Class.UserInputService`) - `Class.RunService.PreRender` - Legacy waiting script resumption such as `wait()`, `spawn()`, and `delay()` - `Class.RunService.PreAnimation` - `Class.RunService.PreSimulation` - `Class.RunService.PostSimulation` - Waiting script resumption such as `Library.task.wait()`, `Library.task.spawn()`, and `Library.task.delay()` - `Class.RunService.Heartbeat` - `Class.DataModel.BindToClose` ## Common affected code patterns With remote events, the following examples either stop working correctly or have subtly different behavior; they rely on events being resumed immediately. ### Trigger and catch events mid-execution In this example, `false` is always returned when deferred events are enabled because the callback has not run. To work correctly, the thread must yield until at least when the event should have fired. ```lua local success = false event:Connect(function () success = true end) doSomethingToTriggerEvent() -- Causes \`event\` to fire return success ``` ### Listen for the first occurrence of an event ```lua connection = event:Connect(function () connection:Disconnect() -- do something end) ``` With deferred events enabled, multiple event handler invocations can be queued before you disconnect from the event. Calling `Class.Instance.Disconnect()|Disconnect()` drops all pending event handler invocations—the same behavior that exists for immediate events. > **Warning:** Any other method of disconnection besides `Class.Instance.Disconnect()|Disconnect()`, such as calling `Class.Instance.Destroy()|Destroy()` on the `Class.Instance`, disconnects the signal immediately, but runs the associated event handler for any events that are still pending. Alternatively, use `Datatype.RBXScriptSignal.Once()|Once()` as a more convenient method for connecting to an event that you only need the first invocation of. ### Events that change ancestry or properties Deferred events cause events that handle a change in ancestry or a property to fire after the ancestry or property is changed: ```lua local part = Instance.new("Part", workspace) local function onPartDestroying() print("In signal:", part:GetFullName(), #part:GetChildren()) end part.Destroying:Connect(onPartDestroying) part:Destroy() ``` Because `Class.Instance.Destroy()|Destroy()` works immediately after the script that called it yields, the instance has already been destroyed by the time `onPartDestroying()` is called. For more examples, see `Class.Instance.Destroying`. --- title: "Events" url: /docs/en-us/scripting/events last_updated: 2026-06-29T19:34:10Z description: "Provides an overview of events and a summary of Roblox's built-in events." --- # Events Events are occurrences within your game that you can listen for and respond to. Many Roblox services and objects have built-in events that automatically **fire** in response to specific actions or changes. For example, a player's `Class.Player.Character|Character` touching a `Class.BasePart` automatically fires a `Class.BasePart.Touched|Touched` event. Each time a player joins your game, the `Class.Players.PlayerAdded` event fires. Due to the sheer number of events and client-server architecture, Roblox scripting is often referred to as **event-driven**. This approach is different from many other game engines, which emphasize running code on a frame-by-frame basis. You don't have to listen for events or take any action in response to them, but the events are firing and available nevertheless. When you do want to respond to an event, you connect a function to it. > **Info:** Deferred events can help you ensure more performant and consistent event handling. See [here](/docs/en-us/scripting/deferred.md) for more information. ## Connect functions to events You connect a function to an event using `Datatype.RBXScriptSignal.Connect()|Connect()` to execute code each time the event fires. Most events pass arguments to their connected functions. For example, the `Class.BasePart.Touched` event passes the object that touched the part (such as a left hand or car wheel), and the `Class.Players.PlayerAdded` event passes the `Class.Player` that joined your game. The following code sample demonstrates how to connect a function named `onPartTouched()` to the `Class.BasePart.Touched|Touched` event of a part: ```lua -- Assumes the script is parented to the part local part = script.Parent -- The function you want to run local function onPartTouched(object) print("Part was touched by", object:GetFullName()) end -- Connect the function to the part's Touched event part.Touched:Connect(onPartTouched) ``` > **Success:** As a best practice and for optimal performance, it's recommended that you [disconnect](#disconnect-functions-from-events) functions when the connection is no longer needed. Note, however, that this may not **always** be necessary, as all connections to an object's [non-deferred](/docs/en-us/scripting/deferred.md) events are automatically disconnected if an object is destroyed. You can also connect [anonymous functions](/docs/en-us/luau/functions.md#anonymous-functions) to events when you want to use variables in the parent scope and don't need to use the function elsewhere. For example, this code sample avoids the awkward intermediary function from the similar sample in [Services](/docs/en-us/services.md): ```lua local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") local SaveManager = require(ReplicatedStorage:WaitForChild("SaveManager")) local function saveProgress(character) local position = character:FindFirstChild("HumanoidRootPart").Position SaveManager.saveData(character, position) end -- Anonymous function that calls saveProgress() when a character is removed -- from the game (in this case, when the player leaves). Players.PlayerAdded:Connect(function(player) player.CharacterRemoving:Connect(saveProgress) end) ``` ## Disconnect functions from events The `Datatype.RBXScriptSignal.Connect()|Connect()` method returns an `Datatype.RBXScriptConnection` object. If you connect a function to an event, but don't want to call the function the next time an event fires (such as after some condition is met), disconnect it by calling `Datatype.RBXScriptConnection:Disconnect()|Disconnect()` on the `Datatype.RBXScriptConnection` object. The following code sample shows how to connect and disconnect a function from the `Class.BasePart.Touched|Part.Touched` event: ```lua local Workspace = game:GetService("Workspace") local part = Workspace.Part local targetPart = Workspace.TargetPart -- Declare an empty placeholder variable for the connection local connection local function onPartTouched(otherPart) if otherPart == targetPart then print("The part hit the target!") -- Disconnect the connection connection:Disconnect() end end -- Connect the above function to the Touched event connection = part.Touched:Connect(onPartTouched) ``` > **Success:** If you only want to connect a function to an event once — that is, only run the function the first time the event fires — use the `Datatype.RBXScriptSignal.Once()|Once()` method as a more convenient alternative to connecting and disconnecting the function. > **Info:** When Luau destroys an event's object, such as the `Class.Player` object when a user leaves the game, all of its [non-deferred](/docs/en-us/scripting/deferred.md) connections disconnect automatically. ## Wait for events to fire If you want a script to yield until a specific event fires, use the `Datatype.RBXScriptSignal:Wait()|Wait()` method. This method returns the event's arguments, which you can assign to variables for later use: ```lua local Workspace = game:GetService("Workspace") local part = Workspace.Part local touchedPart = part.Touched:Wait() print("The part was touched by", touchedPart:GetFullName()) ``` ## Other types of events - [Bindable events](/docs/en-us/scripting/bindable.md) let you communicate between scripts on the **same side** of the client-server boundary. - [Remote events](/docs/en-us/scripting/remote.md) let you communicate **across** the client-server boundary. - [Deferred engine events](/docs/en-us/scripting/deferred.md) defer event handlers until certain resumption points. --- title: "Remote events and callbacks" url: /docs/en-us/scripting/events/remote last_updated: 2026-06-29T19:34:10Z description: "Remote network events and callbacks allow for back-and-forth communication across the client-server boundary." --- # Remote events and callbacks Roblox games are multiplayer by default, so all games inherently communicate between the server and the players' connected clients. In the simplest case, as players move their characters, certain `Class.Humanoid` properties, such as states, are communicated to the server, which passes this information to other connected clients. Remote events and callbacks let you communicate **across** the client-server boundary: - `Class.RemoteEvent|RemoteEvents` enable one-way communication (sending a request and **not** yielding for a response). - `Class.UnreliableRemoteEvent|UnreliableRemoteEvents` enable one-way communication for data that changes continuously or isn't critical to the game's state. These events trade ordering and reliability for improved network performance. - `Class.RemoteFunction|RemoteFunctions` enable two-way communication (sending a request and yielding until a response is received from the recipient). Unlike [bindable events](/docs/en-us/scripting/events/bindable.md), which have more limited utility, the use cases for remote events and functions are too numerous to list: - **Gameplay** - Basic gameplay, such as a player reaching the end of a level, can require a remote event. A client script notifies the server, and server scripts reset the player's position. - **Server verification** - If a player tries to drink a potion, do they actually _have_ that potion? To ensure fairness, the server has to be the source of truth for a game. A client script can use a remote event to notify the server that the player is drinking a potion, and then server scripts can decide whether the player actually has that potion and whether to confer any benefits. - **User interface updates** - As the game's state changes, server scripts can use remote events to notify clients of changes to scores, objectives, etc. - **In-game Marketplace purchases** - For an example implementation that uses remote functions, see [Prompt subscription purchases](/docs/en-us/production/monetization/subscriptions.md#prompt-subscription-purchases). ## Quick reference The following tables serve as a quick reference for how to use `Class.RemoteEvent|RemoteEvents` and `Class.RemoteFunction|RemoteFunctions` to communicate between the client and server. #### Remote events | [Client → Server](#client-server) | | --- | | Client | `RemoteEvent:FireServer(args)` | | Server | `RemoteEvent.OnServerEvent:Connect(function(player, args))` | | [Server → Client](#server-client) | | Server | `RemoteEvent:FireClient(player, args)` | | Client | `RemoteEvent.OnClientEvent:Connect(function(args))` | | [Server → All Clients](#server-all-clients) | | Server | `RemoteEvent:FireAllClients(args)` | | Client | `RemoteEvent.OnClientEvent:Connect(function(args))` | #### Remote functions | [Client → Server → Client](#client-server-client) | | --- | | Client | `serverResponse = RemoteFunction:InvokeServer(args)` | | Server | `RemoteFunction.OnServerInvoke = function(player, args)` | | [Server → Client → Server](#server-client-server) | | See [risks](#server-client-server). | ## Remote events A `Class.RemoteEvent` object facilitates asynchronous, one-way communication across the client-server boundary without yielding for a response. To create a new `Class.RemoteEvent` via the [Explorer](/docs/en-us/studio/explorer.md) window in Studio: 1. Hover over the container into which you want to insert the `Class.RemoteEvent`. In order to ensure both server and client access, it must be in a place where both sides can see it, such as `Class.ReplicatedStorage`, although in some cases it's appropriate to store it in `Class.Workspace` or inside a `Class.Tool`. 2. Click the **⊕** button that appears to the right of the container's name and insert a **RemoteEvent** instance. 3. Rename the instance to describe its purpose. Once you've created a `Class.RemoteEvent`, it can facilitate one-way communication from [client to server](#client-server), from [server to client](#server-client), or from the [server to all clients](#server-all-clients). _Client → Server_ _Server → Client_ _Server → All Clients_ > **Info:** Clients cannot communicate directly with other clients, although you can effectively dispatch an event from one client to another by using the `Class.RemoteEvent:FireServer()` method, then calling `Class.RemoteEvent:FireClient()|FireClient()` or `Class.RemoteEvent:FireAllClients()|FireAllClients()` in the event handler for `Class.RemoteEvent.OnServerEvent|OnServerEvent`. ### Client → server You can use a `Class.LocalScript` to trigger an event on the [server](/docs/en-us/projects/client-server.md) by calling the `Class.RemoteEvent:FireServer()|FireServer()` method on a `Class.RemoteEvent`. If you pass arguments to `Class.RemoteEvent:FireServer()|FireServer()`, they pass to the event handler on the server with certain [limitations](#argument-limitations). Note that the first parameter of the event handler on the server is always the `Class.Player` object of the client that calls it, and additional parameters follow. | Client | `RemoteEvent:FireServer(args)` | | --- | --- | | Server | `RemoteEvent.OnServerEvent:Connect(function(player, args))` | The following `Class.Script` connects an event handler to `Class.RemoteEvent.OnServerEvent|OnServerEvent` that creates a new `Class.Part` on the server. The accompanying `Class.LocalScript` then calls `Class.RemoteEvent:FireServer()|FireServer()` on the `Class.RemoteEvent` instance with the desired `Class.BasePart.Color|Color` and `Class.BasePart.Position|Position` for the part. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local Workspace = game:GetService("Workspace") -- Get reference to remote event instance local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent") local function onCreatePart(player, partColor, partPosition) print(player.Name .. " fired the RemoteEvent") local newPart = Instance.new("Part") newPart.Color = partColor newPart.Position = partPosition newPart.Parent = Workspace end -- Connect function to event remoteEvent.OnServerEvent:Connect(onCreatePart) ``` ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") -- Get reference to remote event instance local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent") -- Fire the remote event and pass additional arguments remoteEvent:FireServer(Color3.fromRGB(255, 0, 0), Vector3.new(0, 25, -20)) ``` ### Server → client You can use a `Class.Script` to trigger an event on a [client](/docs/en-us/projects/client-server.md) by calling the `Class.RemoteEvent:FireClient()|FireClient()` method on a `Class.RemoteEvent`. The first argument for `Class.RemoteEvent:FireClient()|FireClient()` is the `Class.Player` object of the client that you want to respond to the event, and additional arguments pass to the client with certain [limitations](#argument-limitations). Note that the event handler doesn't need to include the `Class.Player` object as its first argument because you can determine the player on the client with `Class.Players.LocalPlayer`. | Server | `RemoteEvent:FireClient(player, args)` | | --- | --- | | Client | `RemoteEvent.OnClientEvent:Connect(function(args))` | The following `Class.LocalScript` connects an event handler to the `Class.RemoteEvent.OnClientEvent|OnClientEvent` event. The accompanying `Class.Script` then listens for incoming players to the server and calls `Class.RemoteEvent:FireClient()|FireClient()` for each with arbitrary data. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local Players = game:GetService("Players") -- Get reference to remote event instance local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent") local player = Players.LocalPlayer local function onNotifyPlayer(maxPlayers, respawnTime) print("[Client] Event received by player", player.Name) print(maxPlayers, respawnTime) end -- Connect function to event remoteEvent.OnClientEvent:Connect(onNotifyPlayer) ``` ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local Players = game:GetService("Players") -- Get reference to remote event instance local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent") -- Listen for incoming players and dispatch remote event to each local function onPlayerAdded(player) print("[Server] Firing event to player", player.Name) remoteEvent:FireClient(player, Players.MaxPlayers, Players.RespawnTime) end Players.PlayerAdded:Connect(onPlayerAdded) ``` ### Server → all clients You can use a `Class.Script` to trigger an event on all clients by calling the `Class.RemoteEvent:FireAllClients()|FireAllClients()` method on a `Class.RemoteEvent`. Unlike `Class.RemoteEvent:FireClient()|FireClient()`, the `Class.RemoteEvent:FireAllClients()|FireAllClients()` method doesn't require a `Class.Player` object because it fires the `Class.RemoteEvent` to all clients. | Server | `RemoteEvent:FireAllClients(args)` | | --- | --- | | Client | `RemoteEvent.OnClientEvent:Connect(function(args))` | The following `Class.LocalScript` connects an event handler to the `Class.RemoteEvent.OnClientEvent|OnClientEvent` event which outputs a remaining countdown time. The accompanying `Class.Script` then calls `Class.RemoteEvent:FireAllClients()|FireAllClients()` in a loop every second to fire the `Class.RemoteEvent` for all clients. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") -- Get reference to remote event instance local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent") local function onTimerUpdate(seconds) print(seconds) end -- Connect function to event remoteEvent.OnClientEvent:Connect(onTimerUpdate) ``` ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") -- Get reference to remote event instance local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent") local countdown = 5 -- Fire the RemoteEvent every second until time expires for timeRemaining = -1, countdown do remoteEvent:FireAllClients(countdown - timeRemaining) task.wait(1) end ``` ## Remote callbacks A `Class.RemoteFunction` object facilitates synchronous, two-way communication across the client-server boundary. The sender of a remote function will yield until it receives a response from the recipient. To create a new `Class.RemoteFunction` via the [Explorer](/docs/en-us/studio/explorer.md) window in Studio: 1. Hover over the container into which you want to insert the `Class.RemoteFunction`. In order to ensure both server and client access, it must be in a place where both sides can see it, such as `Class.ReplicatedStorage`, although in some cases it's appropriate to store it in `Class.Workspace` or inside a `Class.Tool`. 2. Click the **⊕** button that appears to the right of the container's name and insert a **RemoteFunction** instance. 3. Rename the instance to describe its purpose. Once you've created a `Class.RemoteFunction`, it can facilitate two-way communication between [client and server](#client-server-client) or between [server and client](#server-client-server). _Client → Server → Client_ _Server → Client → Server_ ### Client → server → client You can use a `Class.LocalScript` to call a function on the [server](/docs/en-us/projects/client-server.md) by calling the `Class.RemoteFunction:InvokeServer()|InvokeServer()` method on a `Class.RemoteFunction`. Unlike a [remote event](#remote-events), the `Class.LocalScript` that invokes the `Class.RemoteFunction` yields until the callback returns. Arguments that you pass to `Class.RemoteFunction:InvokeServer()|InvokeServer()` pass to the `Class.RemoteFunction.OnServerInvoke|OnServerInvoke` callback of the `Class.RemoteFunction` with certain [limitations](#argument-limitations). Note that if you define multiple callbacks to the same `Class.RemoteFunction`, only the last definition executes. | Client | `RemoteFunction:InvokeServer(args)` | | --- | --- | | Server | `RemoteFunction.OnServerInvoke = function(player, args)` | The following `Class.Script` defines the callback function via `Class.RemoteFunction.OnServerInvoke|OnServerInvoke` and returns the requested `Class.Part` through its `return` value. The accompanying `Class.LocalScript` then calls `Class.RemoteFunction:InvokeServer()|InvokeServer()` with extra arguments defining the requested part color and position. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local Workspace = game:GetService("Workspace") -- Get reference to remote function instance local remoteFunction = ReplicatedStorage:FindFirstChildOfClass("RemoteFunction") -- Callback function local function createPart(player, partColor, partPosition) print(player.Name .. " requested a new part") local newPart = Instance.new("Part") newPart.Color = partColor newPart.Position = partPosition newPart.Parent = Workspace return newPart end -- Set function as remote function's callback remoteFunction.OnServerInvoke = createPart ``` ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") -- Get reference to remote function instance local remoteFunction = ReplicatedStorage:FindFirstChildOfClass("RemoteFunction") -- Pass a color and position when invoking the callback local newPart = remoteFunction:InvokeServer(Color3.fromRGB(255, 0, 0), Vector3.new(0, 25, -20)) -- Output the returned part reference print("The server created the requested part:", newPart) ``` ### Server → client → server You can use a `Class.Script` to call a function on the client by calling the `Class.RemoteFunction:InvokeClient()|InvokeClient()` method on a `Class.RemoteFunction`, but it has serious risks as follows: - If the client throws an error, the server throws the error too. - If the client disconnects while it's being invoked, `Class.RemoteFunction:InvokeClient()|InvokeClient()` throws an error. - If the client doesn't return a value, the server yields forever. For actions that don't require two-way communications, such as updating a GUI, use a `Class.RemoteEvent` and communicate from [server to client](#server-client). ## Argument limitations When you fire a `Class.RemoteEvent` or invoke a `Class.RemoteFunction`, it forwards any arguments that you pass with the event or to the callback function. Any type of Roblox object such as an `Datatype.Enum`, `Class.Instance`, or others can be passed, as well as Luau types such as numbers, strings, and booleans, although you should carefully explore the following limitations. ### Non-string indices If any **indices** of a passed table are non-string types such as an `Class.Instance`, [userdata](/docs/en-us/luau/userdata.md), or [function](/docs/en-us/luau/functions.md), Roblox automatically converts those indices to strings. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent") local function onEventFire(passedTable) for k, v in passedTable do print(typeof(k)) --> string end end -- Connect function to event remoteEvent.OnClientEvent:Connect(onEventFire) ``` ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local Players = game:GetService("Players") local Workspace = game:GetService("Workspace") local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent") -- Listen for incoming players and dispatch remote event to each local function onPlayerAdded(player) remoteEvent:FireClient(player, { [Workspace.Baseplate] = true } ) end Players.PlayerAdded:Connect(onPlayerAdded) ``` ### Passed functions Functions included as arguments for a `Class.RemoteEvent` or `Class.RemoteFunction` will **not** be replicated across the [client-server](/docs/en-us/projects/client-server.md) boundary, making it impossible to pass functions remotely. Instead, the resulting argument on the receiving side will be `nil`. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent") local function onClientEvent(func) print(func) --> nil end remoteEvent.OnClientEvent:Connect(onClientEvent) ``` ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent") local function testFunction() print("Hello world!") end -- Fire remote event with function as an argument remoteEvent:FireAllClients(testFunction) ``` ### Table indexing If you pass a table of data, do not pass a mixed table of numeric and string keys. Instead, pass a table that consists **entirely** of key-value pairs (dictionary) or **entirely** of numeric indices. > **Warning:** Whether passing a dictionary table **or** a numerically indexed table, avoid `nil` values for any index. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent") local function onEventFire(player, passedTable) for k, v in passedTable do print(k .. " = " .. v) --> 1 = Sword --> 2 = Bow --> CharName = Diva Dragonslayer --> CharClass = Rogue end end -- Connect function to event remoteEvent.OnServerEvent:Connect(onEventFire) ``` ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent") -- Numerically indexed table local inventoryData = { "Sword", "Bow" } -- Dictionary table local characterData = { CharName = "Diva Dragonslayer", CharClass = "Rogue" } remoteEvent:FireServer(inventoryData) remoteEvent:FireServer(characterData) ``` ### Table identities Tables passed as arguments to remote events/callbacks are copied, meaning they will not be exactly equivalent to those provided when firing the event or invoking the callback. Nor will tables returned to the invoker be exactly equivalent to those provided. You can demonstrate this by running the following script on a `Class.RemoteFunction` and observing how the table identities differ. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local remoteFunction = ReplicatedStorage:FindFirstChildOfClass("RemoteFunction") -- Callback function local function returnTable(player, passedTable) -- Output table identity on invocation print(tostring(passedTable)) --> table: 0x48eb7aead27563d9 return passedTable end -- Set function as remote function's callback remoteFunction.OnServerInvoke = returnTable ``` ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local remoteFunction = ReplicatedStorage:FindFirstChildOfClass("RemoteFunction") local inventoryData = { "Sword", "Bow" } -- Output original table identity print(tostring(inventoryData)) --> table: 0x059bcdbb2b576549 local invokeReturn = remoteFunction:InvokeServer(inventoryData) -- Output table identity upon return print(tostring(invokeReturn)) --> table: 0x9fcae7919563a0e9 ``` ### Metatables If a table has a metatable, all of the metatable information is lost in the transfer. In the following code sample, the `NumWheels` property is part of the `Car` metatable. When the server receives the following table, the `truck` table has the `Name` property but **not** the `NumWheels` property. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent") local function onEvent(player, param) print(param) --> {["Name"] = "MyTruck"} end -- Connect function to event remoteEvent.OnServerEvent:Connect(onEvent) ``` ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent") local Car = {} Car.NumWheels = 4 Car.__index = Car local truck = {} truck.Name = "MyTruck" setmetatable(truck, Car) -- Fire event with table including a metatable remoteEvent:FireServer(truck) ``` ### Non-replicated instances If a `Class.RemoteEvent` or `Class.RemoteFunction` passes a value that's only visible to the sender, Roblox doesn't replicate it across the client-server boundary and passes `nil` instead of the value. For example, if a `Class.Script` passes a descendant of `Class.ServerStorage`, the client listening to the event will receive a `nil` value because that object isn't replicable for the client. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local ServerStorage = game:GetService("ServerStorage") local Players = game:GetService("Players") local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent") -- Will be received as "nil" because client can't access ServerStorage local storedPart = Instance.new("Part") storedPart.Parent = ServerStorage local function onPlayerAdded(player) remoteEvent:FireClient(player, storedPart) end Players.PlayerAdded:Connect(onPlayerAdded) ``` Similarly, if you create a part in a `Class.LocalScript` and try to pass it to a `Class.Script`, the server will see `nil` because the part isn't replicable for the server. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local Workspace = game:GetService("Workspace") local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent") -- Will be received as "nil" because the server doesn't know about this part local clientPart = Instance.new("Part") clientPart.Parent = Workspace remoteEvent:FireServer(clientPart) ``` --- title: "Scripting" url: /docs/en-us/scripting last_updated: 2026-06-29T19:34:10Z description: "An introduction to scripting in Roblox with the Luau programming language." --- # Scripting Scripts are plain text files that let you add custom, dynamic behavior to your games. You can use scripts to trigger in-game events, respond to player input, save player data, create leaderboards, spawn enemies, control NPC behavior, and much, much more. > **Success:** This section is for creators with some coding experience who want to know the specifics of scripting in Roblox. > > If you've never written code before and want an introduction to programming, see [Coding fundamentals](/docs/en-us/tutorials/fundamentals/coding-1/coding-fundamentals.md), which covers concepts like variables, functions, conditionals, loops, and arrays. For a more guided, step-by-step approach, see the [Basic gameplay](/docs/en-us/tutorials/use-case-tutorials/scripting/basic-scripting/intro-to-scripting.md) tutorial. ## Luau Roblox scripts use the [Luau](https://luau.org) programming language, which is derived from [Lua 5.1](https://www.lua.org/manual/5.1/). - Compared to Lua 5.1, Luau adds performance enhancements and many useful features, including an optional typing system, string interpolation, and generalized iteration for tables. - All valid Lua 5.1 code is valid Luau code, but the opposite is not true. Most books and online resources for Lua are still broadly applicable to Luau. For a detailed summary of differences, see [Compatibility](https://luau.org/compatibility) in the Luau documentation. For language syntax, see the [Luau reference](/docs/en-us/luau.md). ### Luau basics Luau is gradually typed, so you don't need to specify a type when you create a variable. You can use `Global.LuaGlobals.type()` to check object type: ```lua logMessage = "User has more than 10 items!" print(logMessage) --> User has more than 10 items! print(type(logMessage)) --> string ``` Luau has global and local [scopes](/docs/en-us/luau/scope.md), but it's almost always better to declare variables and functions locally with the `local` keyword: ```lua local logMessage = "User has more than 10 items!" local function printMessage() print(logMessage) end printMessage() --> User has more than 10 items! ``` Luau uses `nil` to represent nonexistence or nothingness, which evaluates as `false` in conditional statements: ```lua local messageToUser print(messageToUser) --> nil print(type(messageToUser)) --> nil if messageToUser then -- statement evaluates to false end ``` As you might have noticed, `--` starts a one-line comment. `--[[]]` creates a block comment: ```lua --[[ Shuts off the cosmic moon ray immediately. Should only be called within 15 minutes of midnight Mountain Standard Time to avoid damage to the cosmic moon ray. ]] local function stopCosmicMoonRay() -- add this later, it might prove important end ``` [Tables](/docs/en-us/luau/tables.md) are the generic term for arrays and dictionaries. Arrays are one-based rather than zero-based, so the first item is `[1]`. You declare arrays and dictionaries with a single set of curly braces: ```lua local myArray = {"chips", "sparkling water", "salsa"} local myDictionary = { snack = "chips", drink = "sparkling water", dip = "salsa" } print(myArray[1]) --> chips print(myDictionary.dip) --> salsa ``` You can iterate over tables using `for` loops with the `Global.LuaGlobals.ipairs()` function for arrays and the `Global.LuaGlobals.pairs()` function for dictionaries, but Luau also lets you omit these functions for cleaner syntax: ```lua for index, value in ipairs(myArray) do -- standard Lua print(index, value) end for key, value in pairs(myDictionary) do -- standard Lua print(key, value) end for key, value in myDictionary do -- Luau generalized iteration print(key, value) end ``` ## Your first script 1. In Roblox Studio, hover over **ServerScriptService** in the [Explorer](/docs/en-us/studio/explorer.md) window and click **+**. 2. Select **Script** to add a new script. 3. Right-click the script and rename it to `HelloScript`. 4. Double-click the script to open it in the [Script Editor](/docs/en-us/studio/script-editor.md). 5. Add the following code to the file:```lua local helloArray = {"h", "e", "l", "l", "o"} local worldArray = {"w", "o", "r", "l", "d"} for index, value in helloArray do print(value) end print(table.concat(worldArray)) ``` 6. Ensure that the [Output](/docs/en-us/studio/output.md) window is open. 7. [Run your game](/docs/en-us/studio/testing-modes.md#playtesting) and note the output:```text h e l (x2) o world ``` ## Get comfortable A big part of adapting to a new development environment is configuring it to meet your needs and understanding the tools at your disposal: - The **Script Editor** section of [Studio Settings](/docs/en-us/studio/setup.md#customization) lets you adjust quality of life features like font, colors, indentation, autocomplete, brackets, and tooltips. You might also want to enable dark mode in the **Studio** section. - Holding `Ctrl` or `Command` and clicking on a function or variable takes you to its declaration in your codebase (or its online documentation). Using [Find and Find All](/docs/en-us/studio/script-editor.md#find-and-replace) can help you navigate larger projects. - The [Output](/docs/en-us/studio/output.md) window is the most basic tool for understanding the behavior of your scripts. Use the **⋯** menu to enable **Show Context** and **Show Source**. - The [Script Analysis](/docs/en-us/studio/script-editor.md#script-analysis) window shows a summary of errors and warnings, but you might find its utility limited; the Script Editor already highlights these issues as you type. - Logging capabilities are minimal, with no concept of log levels like `DEBUG` or `FATAL`. Use `Global.LuaGlobals.print()` and `Global.RobloxGlobals.warn()`. For more information about configuring Studio for scripting, see [Script Editor](/docs/en-us/studio/script-editor.md). > **Success:** To use your favorite text editor and version control system rather than the built-in editor, see [Script Sync](/docs/en-us/sync.md). ## Your second script 1. In Roblox Studio, add a script to **ReplicatedStorage** in the [Explorer](/docs/en-us/studio/explorer.md) window and rename it to `OhNo`. 2. Add the following code to the file:```lua print("Hello script types and locations!") ``` 3. [Run your game](/docs/en-us/studio/testing-modes.md#playtesting). 4. Note how the output is no different than when you ran [your first script](#your-first-script). To understand why the script didn't run, see [Script Types and Locations](/docs/en-us/locations.md). --- title: "Script types and locations" url: /docs/en-us/scripting/locations last_updated: 2026-06-29T19:34:11Z description: "How scripts run in Roblox, and how location impacts that behavior." --- # Script types and locations For many developers, the fundamental challenge of adapting to Roblox scripting is the importance of file location and the `Class.Script.RunContext` property. Depending on script type, location in the **Explorer**, and run context, scripts can behave very differently. Certain method calls might fail, objects in your game might be inaccessible, or scripts might not run at all. The reason for this complexity is that Roblox games are multiplayer by default. Scripts need the ability to only run on the server, only run on the client, or be shared across both. The evolution of the Roblox platform over time has further complicated the situation. ## Script types Roblox has three types of scripts: - `Class.Script` - Code that runs on either the server or the client, depending on its location and `Class.Script.RunContext` property. - `Class.LocalScript` - Code that runs only on the client. Does not have a run context. - `Class.ModuleScript` - Code that you can reuse in other scripts. Does not have a run context. When you create a `Class.Script`, its default run context is `Legacy`, meaning that it a) is a server-side script and b) only runs if it is in a server container, such as `Class.Workspace` or `Class.ServerScriptService`. - If you change the script's run context to `Server`, it can now also run in `Class.ReplicatedStorage`, but that's not recommended. The contents of that location are replicated to clients, so it's a poor location for server-side scripts. - If you change the script's run context to `Client`, it can run in `ReplicatedStorage`. It can also run in `Class.StarterCharacterScripts` and `Class.StarterPlayerScripts`. Starter containers are copied to clients, though, so the original script **and** the copy run, which isn't desirable. To change a script run context, select it in the [Explorer](/docs/en-us/studio/explorer.md) and change the value in the [Properties](/docs/en-us/studio/properties.md) window. ![RunContext property indicated for a Script.](../assets/studio/properties/Script-RunContext.png) ### Recommendations - Put client scripts into `Class.ReplicatedStorage` with a `RunContext` of `Client` alongside client-only `Class.ModuleScript|ModuleScripts`. When debugging in Studio, you can click lines in the **Output** window and go to `ReplicatedStorage.YourScript` (the stable location of the script) rather than `Players.YourName.PlayerScripts.YourScript` (the ephemeral location that the script was copied to at runtime). - Put server scripts into `Class.ServerScriptService` with a `RunContext` of `Server` alongside server-only `Class.ModuleScript|ModuleScripts`. - To share code between server and client scripts, use `Class.ModuleScript|ModuleScripts` in `ReplicatedStorage`. > **Success:** To stay organized, many creators write the vast majority of their code in `Class.ModuleScript|ModuleScripts` and `Global.LuaGlobals.require()` all of them in exactly one server script and one client script. - If you place scripts within [models](/docs/en-us/parts/models.md) and [packages](/docs/en-us/projects/assets/packages.md), specify a `RunContext` for each script to remove ambiguity from how it runs. Explicitly setting this property makes models and packages more likely to work properly from a variety of locations. - Put the minimal number of client scripts (such as a loading script) into `Class.ReplicatedFirst` with a `RunContext` of `Client`. To learn more, see [Replication order](/docs/en-us/scripting/attributes.md#replication-order). - Use `Class.LocalScript|LocalScripts` in `StarterCharacterScripts`, `StarterPlayerScripts`, `StarterGui`, and `StarterPack`. ## Script locations | Location | Description | | --- | --- | | `Class.Workspace` | Represents the experience's 3D world. This location works well for server scripts that attach directly to objects and control their behavior. | | `Class.ReplicatedFirst` | Contains objects that replicate to the client before anything else. This location is ideal for the absolute minimum set of objects and client scripts necessary to display a loading screen. | | `Class.ReplicatedStorage` | Contains objects that are replicated to both the client and the server. This location is ideal for `Class.ModuleScript\|ModuleScripts` that you want to use on both the server and the client. `Class.LocalScript\|LocalScripts` do not run from this location, but `Class.Script\|Scripts` with a `Class.BaseScript.RunContext\|RunContext` of `Enum.RunContext\|Client` do. | | `Class.ServerScriptService` | Contains server scripts. This location is ideal for scripts that need to access server-side functionality or objects, such as game logic and cloud storage. | | `Class.ServerStorage` | Contains server-side objects. This location is ideal for large objects that don't need to be immediately replicated to clients when they join an experience. Scripts do not run from this location, but you can store server-side `Class.ModuleScript\|ModuleScripts` here. | | `Class.StarterPlayer` ⟩ `Class.StarterCharacterScripts` | Contains `Class.LocalScript\|LocalScripts` that run when the character spawns. | | `Class.StarterPlayer` ⟩ `Class.StarterPlayerScripts` | Contains general-purpose `Class.LocalScript\|LocalScripts` that run when the player joins the experience. | | `Class.StarterGui` | Contains GUI elements that the client displays when it loads the experience. `Class.LocalScript\|LocalScripts` can run from this location. This location is ideal for scripts that modify the experience's user interface, such as adding buttons, menus, and pop-ups. | | `Class.StarterPack` | Generally only contains `Class.Tool\|Tools`, but can also include `Class.LocalScript\|LocalScripts` for setting up player backpacks. | This image shows which **Explorer** window locations can contain client scripts. Remember, `ReplicatedFirst` and `ReplicatedStorage` can contain `Class.Script|Scripts` with a `Class.BaseScript.RunContext|RunContext` of `Enum.RunContext|Client`, whereas the `Starter[]` containers should use `Class.LocalScript|LocalScripts`. ![Diagram showing which script locations run on clients.](../assets/scripting/client-server/Client-Script-Containers.png) ## Example project structure The [Plant](/docs/en-us/resources/plant-reference-project.md) reference project shows how you might organize your code in a large, complex game. Of particular note is how it stores the vast majority of its code as reusable `Class.ModuleScript|ModuleScripts` in `ReplicatedStorage` and `ServerStorage`. --- title: "Reuse code" url: /docs/en-us/scripting/module last_updated: 2026-06-29T19:34:11Z description: "How to use module scripts to reuse code." --- # Reuse code After creating a few scripts, it's never long before you want to reuse some code between them. Depending on [location](/docs/en-us/scripting/locations.md), `Class.ModuleScript|ModuleScripts` let you reuse code between scripts on different sides of the client-server boundary or the same side of the boundary. You can put module scripts anywhere that you put scripts, but `Class.ReplicatedStorage` is a popular location; storing module scripts here lets you reuse code between the server and clients. ## Anatomy of a module script In Roblox Studio, add a module script to **ReplicatedStorage** and rename it to `PickupManager`. Each `Class.ModuleScript` starts with the following code: ```lua local module = {} return module ``` This code creates an empty Luau [table](/docs/en-us/luau/tables.md) and returns it to any script that requires the module script. The return value can be any [data type](/docs/en-us/reference/engine/datatypes.md) except for `nil`, but most module scripts return a function, a table, or a table of functions. To generate its return value, module scripts can of course run arbitrary code, which includes requiring other module scripts. > **Info:** Be careful not to have module scripts require each other in a circular manner, which results in a `Requested module was required recursively` error. The following example returns a table with a single function called `getPickupBonus`. Paste it into the new module script: ```lua -- ModuleScript in ReplicatedStorage local PickupManager = {} local defaultMultiplier = 1.25 local rarityMultipliers = { common = 10, uncommon = 20, rare = 50, legendary = 100 } -- Add the getPickupBonus function to the PickupManager table PickupManager.getPickupBonus = function(rarity) local bonus = rarityMultipliers[rarity] * defaultMultiplier return bonus end return PickupManager ``` Adding the function to a table isn't strictly necessary—you could just return the function itself—but it's a good pattern to follow; it gives you an easy-to-understand syntax when you call the function from another script and lets you easily add more functions to the module script over time. ## Require module scripts To load a module script, you call the `Global.LuaGlobals.require()` function. In `ReplicatedStorage`, add a new script, and change its `RunContext` to `Client`. Then add the following code to call the `PickupManager.getPickupBonus` function: ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") -- Get the value returned by the ModuleScript local PickupManager = require(ReplicatedStorage:WaitForChild("PickupManager")) -- Call a ModuleScript function local bonus = PickupManager.getPickupBonus("legendary") print(bonus) --> 125 ``` > **Info:** The `Class.Instance:WaitForChild()` pattern is an important safety measure in client scripts. For more information, see [Replication order](/docs/en-us/scripting/attributes.md#replication-order). Storing module scripts in `ReplicatedStorage` lets you share code between the server and client, so you can use the same code to require the script from `ServerScriptService`: ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local PickupManager = require(ReplicatedStorage:WaitForChild("PickupManager")) ``` When you call `Global.LuaGlobals.require()` on a `Class.ModuleScript`, it runs once and returns a single item as a reference. Calling `Global.LuaGlobals.require()` again returns the exact same reference, meaning that if you modify a returned [table](/docs/en-us/luau/tables.md) or `Class.Instance`, subsequent `Global.LuaGlobals.require()` calls return that modified reference. The module itself doesn't run multiple times. If you require a `Class.ModuleScript` from both sides of the client-server boundary, as in the example above, the `Class.ModuleScript` returns a unique reference for each side. ## Patterns Module scripts have some common patterns that you can use to simplify your code and avoid pitfalls as your game grows in size and complexity. > **Success:** Most of these patterns require an understanding of events. If you're not familiar with them, see [Events](/docs/en-us/scripting/events.md). ### Data sharing To associate data with individual objects, you can assign attributes to them or create `Class.Configuration` folders with value objects such as `Class.StringValue` or `Class.IntValue`. However, both approaches are troublesome if you want to add or modify dozens of objects or data values. They also don't store tables or functions. If you want to modify the same data for multiple copies of the same object or reuse the same data for different objects, store the data in `Class.ModuleScript|ModuleScripts`. It's an easier way for you to reuse the data in other scripts, and you can store tables and functions. The following example `Class.ModuleScript` in `Class.ReplicatedStorage|ReplicatedStorage` stores the configuration values for a generic gun: ```lua local GunConfig = {} GunConfig.MagazineSize = 20 GunConfig.AmmoCount = 100 GunConfig.Firerate = 600 GunConfig.Damage = { ["Head"] = 50; ["Torso"] = 40; ["Body"] = 25; } return GunConfig ``` ### Custom events Custom events enable scripts to communicate with each other, but having to keep track of references to individual `Class.BindableEvent` objects can clutter your code. You can use `Class.ModuleScript|ModuleScripts` to store `Class.BindableEvent|BindableEvents` and provide custom event handlers that are directly tied to the methods of `Class.ModuleScript`. The following `Class.ModuleScript` in `Class.ReplicatedStorage|ReplicatedStorage` has a custom event that fires when the switch changes state: ```lua local Switch = {} -- Creating bindable so any script can listen to when the switch was changed local bindableEvent = Instance.new("BindableEvent") Switch.Changed = bindableEvent.Event local state = false function Switch.flip() state = not state bindableEvent:Fire(state) end return Switch ``` The following client script in `Class.ReplicatedStorage` connects a function to call when the `Switch.Changed` event fires. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local Switch = require(ReplicatedStorage:WaitForChild("Switch")) Switch.Changed:Connect(function(newState) print("Switch state is now", newState) end) -- Test the flipping a few times task.wait(1) Switch.flip() task.wait(1) Switch.flip() ``` ### Encapsulation Encapsulation is the practice of creating a layer of abstraction around objects or scripting logic to hide complexity. You can use `Class.ModuleScript|ModuleScripts` to encapsulate Roblox objects with custom Luau functions to simplify code. For example, you can use encapsulation to: - Simplify cross-network communication with a single `Class.RemoteEvent` object. - Wrap error handling code around sensitive services such as `Class.DataStoreService`. - Define custom methods to control or extend Roblox object features. It's difficult to keep track of dozens of individual `Class.RemoteEvent` objects to implement networking in a game. You can use a `Class.ModuleScript` to encapsulate a single `Class.RemoteEvent` to help simplify this problem. By including a unique `id` argument, you can still send different network messages while only using a single `Class.RemoteEvent`. In the example below, the `Class.ModuleScript` named `NetworkManagerClient` encapsulates the `Class.RemoteEvent:FireServer()` method to include this extra `id` argument. Additionally, this `Class.ModuleScript` references the `Class.RemoteEvent` object itself so you don't have to reference it in other parts of your code. You only need to require this `Class.ModuleScript` to send network messages and don't need to deal with `Class.RemoteEvent` objects in the rest of your codebase. The following `Class.ModuleScript` in `Class.ReplicatedFirst` provides an encapsulated function that you can call on your client scripts to send a network message: ```lua -- ModuleScript in ReplicatedFirst named NetworkManagerClient local NetworkManagerClient = {} local ReplicatedStorage = game:GetService("ReplicatedStorage") local remoteEvent = ReplicatedStorage:WaitForChild("RemoteEvent") -- Encapsulating the remote object's FireServer function function NetworkManagerClient.FireServer(id, ...) remoteEvent:FireServer(id, ...) end return NetworkManagerClient ``` The following `Class.ModuleScript` in `Class.ServerScriptService` uses `Class.BindableEvent|BindableEvents` for every script to connect to a specific ID. When a client sends a network message, each `Class.BindableEvent` associated with the specified ID fires. ```lua -- ModuleScript in ServerScriptService named NetworkManagerServer local NetworkManagerServer = {} local networkSignalList = {} function NetworkManagerServer.GetServerEventSignal(id) local bindableEvent = Instance.new("BindableEvent") -- Linking the new BindableEvent to the id table.insert(networkSignalList, { id = id, bindableEvent = bindableEvent, }) return bindableEvent.Event end -- Connecting to local ReplicatedStorage = game:GetService("ReplicatedStorage") local remoteEvent = ReplicatedStorage:WaitForChild("RemoteEvent") remoteEvent.OnServerEvent:Connect(function(player, id, ...) -- Finding every bindable event that matches the ID of the received remote event for _, signal in networkSignalList do if signal.id == id then signal.bindableEvent:Fire(player, ...) end end end) return NetworkManagerServer ``` The following `Class.LocalScript` sends a message with the ID `RequestA` with an optional `Hello` argument. ```lua -- LocalScript in ReplicatedFirst local ReplicatedFirst = game:GetService("ReplicatedFirst") local NetworkManagerClient = require(ReplicatedFirst:WaitForChild("NetworkManagerClient")) NetworkManagerClient.FireServer("RequestA", "Hello") ``` The following `Class.Script` connects to the network message ID `RequestA` and prints out a statement with any additional parameters when it receives the request. ```lua -- Script in ServerScriptService local ServerScriptService = game:GetService("ServerScriptService") local NetworkManagerServer = require(ServerScriptService:WaitForChild("NetworkManagerServer")) NetworkManagerServer.GetServerEventSignal("RequestA"):Connect(function(player, ...) print("Received RequestA from", player, ...) end) ``` --- title: "Parallel Luau" url: /docs/en-us/scripting/multithreading last_updated: 2026-06-29T19:34:11Z description: "Parallel Luau runs code on multiple threads simultaneously." --- # Parallel Luau With the **Parallel Luau** programming model, you can run code on multiple threads simultaneously, which can improve the performance of your experience. As you expand your experience with more content, you can adopt this model to help maintain the performance and safety of your Luau scripts. ## Parallel programming model By default, scripts execute sequentially. If your experience has complex logic or content, such as non-player characters (NPCs), raycasting validation, and procedural generation, then sequential execution might cause lag for your users. With the parallel programming model, you can [split tasks into multiple scripts](#split-code-into-multiple-threads) and run them in parallel. This makes your experience code run faster, which improves the user experience. The parallel programming model also adds safety benefits to your code. By splitting code into multiple threads, when you edit code in one thread, it doesn't affect other code running in parallel. This reduces the risk of having one bug in your code corrupting the entire experience, and minimizes the delay for users in live servers when you push an update. Adopting the parallel programming model doesn't mean to put everything in multiple threads. For example, the [server-side raycasting validation](#server-side-raycasting-validation) sets each individual user a remote event in parallel but still requires the initial code to run serially to change global properties, which is a common pattern for parallel execution. Most times you need to combine serial and parallel phases to achieve your desired output, since currently there are some operations not supported in parallel that can prevent scripts from running, such as modifying instances in parallel phases. For more information on the level of usage of APIs in parallel, see [thread safety](#thread-safety). ## Split code into multiple threads To run your experience's scripts in multiple threads concurrently, you need to split them into logical chunks under different **actors** in the [data model](/docs/en-us/projects/data-model.md). Actors are represented by `Class.Actor` instances inheriting from `Class.DataModel`. They work as units of execution isolation that distribute the load across multiple cores running simultaneously. ### Place actor instances You can put actors in proper containers or use them to replace the top-level instance types of your 3D entities such as NPCs and raycasters, then add corresponding scripts. ![An example of a Script under an Actor](../assets/studio/explorer/ServerScriptService-Actor.png) For most situations, you shouldn't put an actor as a child of another actor in the data model. However, if you decide to place a script nested within multiple actors for your specific use case, the script is owned by its closest ancestor actor. ![A tree of actors and scripts that shows how a script is owned by its closest actor](../assets/scripting/scripts/Actor-Tree.png) ### Desynchronize threads Though putting scripts under actors grants them the capability for parallel execution, by default the code still runs on the single thread serially, which doesn't improve the runtime performance. You need to call `Library.task.desynchronize()`, a yieldable function that suspends the execution of the current coroutine for running code in parallel and resumes it at the next parallel execution opportunity. To switch a script back to serial execution, call `Library.task.synchronize()`. Alternatively, you can use `Datatype.RBXScriptSignal:ConnectParallel()` method when you want to schedule a signal callback to immediately run your code in parallel upon triggering. You don't need to call `Library.task.desynchronize()` inside the signal callback. ```lua local RunService = game:GetService("RunService") RunService.Heartbeat:ConnectParallel(function() ... -- Some parallel code that computes a state update task.synchronize() ... -- Some serial code that changes the state of instances end) ``` > **Warning:** You can't use `require()` in a desynchronized parallel phase. Require scripts you want to use first in a serial context. Scripts that are part of the same actor always execute sequentially with respect to each other, so you need multiple actors. For example, if you put all parallel-enabled behavior scripts for your NPC in one actor, they still run serially on a single thread, but if you have multiple actors for different NPC logic, each of them runs in parallel on its own thread. For more information, see [Best practices](#best-practices). _Parallel code in Actors running serially in a single thread_ _Parallel code in Actors running simultaneously in multiple threads_ ### Thread safety During the parallel execution, you can access most instances of the `Class.DataModel` hierarchy as usual, but some API properties and functions aren't safe to read or write. If you use them in your parallel code, the Roblox engine can automatically detect and prevent these accesses from occurring. API members have a thread safety level that indicates whether and how you can use them in your parallel code, as the following table shows: | Safety level | For properties | For functions | | --- | --- | --- | | **Unsafe** | Cannot be read or written in parallel. | Cannot be called in parallel. | | **Read Parallel** | Can be read but not written in parallel. | N/A | | **Local Safe** | Can be used within the same Actor; can be read but not written to by other `Class.Actor\|Actors` in parallel. | Can be called within the same Actor; cannot be called by other `Class.Actor\|Actors` in parallel. | | **Safe** | Can be read and written. | Can be called. | You can find thread safety tags for API members on the [API reference](/docs/en-us/reference/engine.md). When using them, you should also consider how API calls or property changes might interact between parallel threads. Usually it's safe for multiple actors to read the same data as other actors but not modify the state of other actors. > **Info:** If an API member doesn't specify a thread safety level, by default its thread safety level is **Unsafe**. ## Cross-thread communication Under the multithreading context, you can still allow scripts in different actors to communicate with each other to exchange data, coordinate tasks, and synchronize activities. The engine supports the following mechanisms for cross-thread communication: - [Actor messaging](#actor-messaging) API for sending messages to an actor using scripts. - [Shared table](#shared-table) data structure for efficiently sharing a large amount of data between multiple actors on a shared state. - [Direct data model communication](#direct-data-model-communication) for simple communication with restrictions. You can support multiple mechanisms to accommodate your cross-thread communication needs. For example, you can send a shared table through the Actor Messaging API. ### Actor messaging The **actor messaging** API allows a script, either in a serial or parallel context, to send data to an actor in the same data model. Communication through this API is asynchronous, in which the sender doesn't block until the receiver receives the message. When sending messages using this API, you need to define a **topic** for categorizing the message. Each message can only be sent to a single actor, but that actor can internally have multiple callbacks bound to a message. Only scripts that are descendants of an actor can receive messages. The API has the following methods: - `Class.Actor:SendMessage()` for sending a message to an actor. - `Class.Actor:BindToMessage()` for binding a Luau callback to a message with the specified topic in a serial context. - `Class.Actor:BindToMessageParallel()` for binding a Luau callback to a message with the specified topic in a parallel context. The following example shows how to use `Class.Actor:SendMessage()` to define a topic and send a message on the sender's end: ```lua local Workspace = game:GetService("Workspace") -- Send two messages to the worker actor with a topic of "Greeting" local workerActor = Workspace.WorkerActor workerActor:SendMessage("Greeting", "Hello World!") workerActor:SendMessage("Greeting", "Welcome") print("Sent messages") ``` The following example shows how to use `Class.Actor:BindToMessageParallel()` to bind a callback for certain topic in a parallel context on the receiver's end: ```lua -- Get the actor this script is parented to local actor = script:GetActor() -- Bind a callback for the "Greeting" message topic actor:BindToMessageParallel("Greeting", function(greetingString) print(actor.Name, "-", greetingString) end) print("Bound to messages") ``` ### Shared table `Datatype.SharedTable` is a table-like data structure accessible from scripts running under multiple actors. It's useful for situations that involve a large amount of data and require a common shared state between multiple threads. For example, when multiple actors work on a common world state that is not stored in the data model. Sending a shared table to another actor doesn't make a copy of the data. Instead, shared tables allow safe and atomic updates by multiple scripts simultaneously. Every update to a shared table by one actor is immediately visible to all actors. Shared tables can also be cloned in a resource-efficient process that utilizes structural sharing instead of copying the underlying data. ### Direct data model communication You can also facilitate communication between multiple threads directly using the data model, in which different actors can write and subsequently read properties or attributes. However, to maintain the thread-safety, scripts running in parallel generally can't write to the data model. So directly using the data model for communication comes with restrictions and may force scripts to synchronize frequently, which can impact performance of your scripts. ## Examples ### Server-side raycasting validation For a fighting and battle experience, you need to enable [raycasting](/docs/en-us/workspace/raycasting.md) for your users' weapons. With the client simulating the weapons to achieve good latency, the server has to confirm the hit, which involves doing raycasts and some amount of heuristics that compute expected character velocity, and look at past behavior. Instead of using a single centralized script that connects to a remote event that clients use to communicate hit information, you can run each hit validation process on the server side in parallel with every user character having a separate remote event. The server-side script that runs under that character's `Class.Actor` connects to this remote event using a parallel connection to run the relevant logic for confirming the hit. If the logic finds a confirmation of a hit, the damage is deducted, which involves changing properties, so it runs serially initially. ```lua local Workspace = game:GetService("Workspace") local tool = script.Parent.Parent local remoteEvent = Instance.new("RemoteEvent") -- Create new remote event and parent it to the tool remoteEvent.Name = "RemoteMouseEvent" -- Rename it so that the local script can look for it remoteEvent.Parent = tool local remoteEventConnection -- Create a reference for the remote event connection -- Function which listens for a remote event local function onRemoteMouseEvent(player: Player, clickLocation: CFrame) -- SERIAL: Execute setup code in serial local character = player.Character -- Ignore the user's character while raycasting local params = RaycastParams.new() params.FilterType = Enum.RaycastFilterType.Exclude params.FilterDescendantsInstances = { character } -- PARALLEL: Perform the raycast in parallel task.desynchronize() local origin = tool.Handle.CFrame.Position local epsilon = 0.01 -- Used to extend the ray slightly since the click location might be slightly offset from the object local lookDirection = (1 + epsilon) * (clickLocation.Position - origin) local raycastResult = Workspace:Raycast(origin, lookDirection, params) if raycastResult then local hitPart = raycastResult.Instance if hitPart and hitPart.Name == "block" then local explosion = Instance.new("Explosion") -- SERIAL: The code below modifies state outside of the actor task.synchronize() explosion.DestroyJointRadiusPercent = 0 -- Make the explosion non-deadly explosion.Position = clickLocation.Position -- Multiple actors could get the same part in a raycast and decide to destroy it -- This is perfectly safe but it would result in two explosions at once instead of one -- The following double checks that execution got to this part first if hitPart.Parent then explosion.Parent = Workspace hitPart:Destroy() -- Destroy it end end end end -- Connect the signal in serial initially since some setup code is not able to run in parallel remoteEventConnection = remoteEvent.OnServerEvent:Connect(onRemoteMouseEvent) ``` ### Server-side procedural terrain generation To create a vast world for your experience, you can populate the world dynamically. Procedural generation typically creates independent terrain chunks, with the generator performing relatively intricate calculations for object placement, material usage, and voxel filling. Running generation code in parallel can enhance efficiency of the process. The following code sample serves as an example. ```lua -- Parallel execution requires the use of actors -- This script clones itself; the original initiates the process, while the clones act as workers local Workspace = game:GetService("Workspace") local actor = script:GetActor() if actor == nil then local workers = {} for i = 1, 32 do local actor = Instance.new("Actor") script:Clone().Parent = actor table.insert(workers, actor) end -- Parent all actors under self for _, actor in workers do actor.Parent = script end -- Instruct the actors to generate terrain by sending messages -- In this example, actors are chosen randomly task.defer(function() local rand = Random.new() local seed = rand:NextNumber() local sz = 10 for x = -sz, sz do for y = -sz, sz do for z = -sz, sz do workers[rand:NextInteger(1, #workers)]:SendMessage("GenerateChunk", x, y, z, seed) end end end end) -- Exit from the original script; the rest of the code runs in each actor return end function makeNdArray(numDim, size, elemValue) if numDim == 0 then return elemValue end local result = {} for i = 1, size do result[i] = makeNdArray(numDim - 1, size, elemValue) end return result end function generateVoxelsWithSeed(xd, yd, zd, seed) local matEnums = {Enum.Material.CrackedLava, Enum.Material.Basalt, Enum.Material.Asphalt} local materials = makeNdArray(3, 4, Enum.Material.CrackedLava) local occupancy = makeNdArray(3, 4, 1) local rand = Random.new() for x = 0, 3 do for y = 0, 3 do for z = 0, 3 do occupancy[x + 1][y + 1][z + 1] = math.noise(xd + 0.25 * x, yd + 0.25 * y, zd + 0.25 * z) materials[x + 1][y + 1][z + 1] = matEnums[rand:NextInteger(1, #matEnums)] end end end return {materials = materials, occupancy = occupancy} end -- Bind the callback to be called in parallel execution context actor:BindToMessageParallel("GenerateChunk", function(x, y, z, seed) local voxels = generateVoxelsWithSeed(x, y, z, seed) local corner = Vector3.new(x * 16, y * 16, z * 16) -- Currently, WriteVoxels() must be called in the serial phase task.synchronize() Workspace.Terrain:WriteVoxels( Region3.new(corner, corner + Vector3.new(16, 16, 16)), 4, voxels.materials, voxels.occupancy ) end) ``` ## Best practices To apply the maximum benefits of parallel programming, refer to the following best practices when adding your Luau code: - **Avoid Long Computations** — Even in parallel, long computations can block execution of other scripts and cause lag. Avoid using parallel programming to handle a large volume of long, unyielding calculations.![Diagram demonstrating how overloading the parallel execution phase can still cause lag](../assets/scripting/scripts/ParallelExecutionDark.png) - **Use the Right Number of Actors** — For the best performance, use more `Class.Actor|Actors`. Even if the device has fewer cores than `Class.Actor|Actors`, the granularity allows for more efficient load balancing between the cores.![Demonstration of how using more actors balances the load across cores](../assets/scripting/scripts/FewerVsMoreActorsDark.png) This doesn't mean you should use as many `Class.Actor|Actors` as possible. You should still divide code into `Class.Actor|Actors` based on logic units rather than breaking code with connected logic to different `Class.Actor|Actors`. For example, if you want to enable [raycasting validation](#server-side-raycasting-validation) in parallel, it's reasonable to use 64 `Class.Actor|Actors` and more instead of just 4, even if you're targeting 4-core systems. This is valuable for scalability of the system and allows it to distribute the work based on the capability of the underlying hardware. However, you also shouldn't use too many `Class.Actor|Actors`, which are hard to maintain. --- title: "Schedule code" url: /docs/en-us/scripting/scheduler last_updated: 2026-06-29T19:34:11Z description: "Explains how to schedule code so it executes after a specific action or cycle has completed." --- # Schedule code Scheduling code is useful in many situations, such as ensuring code executes after a specific action or cycle has completed, or delaying code for a specific duration of time. You can use the `Library.task` library to optimize Roblox's [task scheduler](/docs/en-us/performance-optimization/microprofiler/task-scheduler.md) to manage and schedule code. You can also use a similar library called `Library.coroutine` to schedule code which has some additional functionality. ### Common methods The following are the most common `Library.task` methods used to schedule code. You should use the task methods over legacy scheduling methods, such as `Global.RobloxGlobals.wait()`, to ensure that your code runs optimally. > **Warning:** Certain legacy global methods, such as (`Global.RobloxGlobals.spawn()`, `Global.RobloxGlobals.delay()`, and `Global.RobloxGlobals.wait()`) can provide similar code scheduling results but are less optimized and configurable as their `Library.task` alternatives. If your game uses these legacy methods, you should use `Library.task` instead to ensure your game's code remains efficient and up-to-date. The following table lists the relevant legacy global methods and their preferred, more optimized counterparts: | Legacy global methods | Task methods | Additional alternatives | | --- | --- | --- | | `wait()` | `Library.task.wait()` | `Class.RunService.Heartbeat` | | `wait(n)` | `Library.task.wait()\|task.wait(n)` | | | `spawn(f)` | `Library.task.defer()\|task.defer(f)` | `Library.task.delay()\|task.delay(0, f)` | | `delay(n, f)` | `Library.task.delay()\|task.delay(n, f)` | | | `spawn(function() f(uv1, ...) end)` | `Library.task.defer()\|task.defer(f, uv1, ...)` | `Library.task.delay()\|task.delay(0, f, uv1, ...)` | | `delay(n, function() f(uv1, ...) end)` | `Library.task.delay()\|task.delay(n, f, uv1, ...)` | | #### task.spawn() `Library.task.spawn()` takes a thread or function and resumes it **immediately** through the engine's scheduler. Additional arguments are passed to the thread or function being resumed. The following code sample is an example of how you can use `Library.task.spawn()` when calling a function that may yield while iterating over a set of objects: ```lua local function playerAdded(player) print(player) end for _, player in Players:GetPlayers() do task.spawn(playerAdded, player) end ``` #### task.defer() `Library.task.defer()` takes a thread or function and defers it until the end of the current resume point within the current frame. Additional arguments are passed to the thread or function resuming. You should typically use this when you want similar behavior to `Library.task.spawn()` but don't care about the thread running immediately. The following code sample illustrates how the `Global.RobloxGlobals.print()` statement for `"A"` will defer until after the `Global.RobloxGlobals.print()` statement for `"B"` executes: ```lua task.defer(print, "A") print("B") --> B --> A ``` > **Info:**`Library.task.defer()` is an optimized version of `Global.RobloxGlobals.spawn()` that schedules a thread to resume as soon as possible (but not immediately) without any throttling. #### task.delay() `Library.task.delay()` takes a thread or function and schedules it for resumption after the given amount of time elapses on the next `Class.RunService.Heartbeat|Heartbeat` step. The thread resumes with built-in error handling and support for other engine features. Any additional arguments are passed to the thread or function resuming. Since the actual delay time may vary, the following code sample illustrates how you can calculate it by passing the current time as an argument: ```lua task.delay(2, function(scheduledTime) print(os.clock() - scheduledTime) --> 2.038702 end, os.clock()) ``` A duration of zero will result in the thread or function resuming on the next step. > **Info:**`Library.task.delay()` is an optimized version of `Global.RobloxGlobals.delay()` that schedules a thread to resume after some time elapses without throttling. #### task.wait() `Library.task.wait()` yields the current thread until the given duration (in seconds) elapses and then resumes the thread on the next `Class.RunService.Heartbeat|Heartbeat` step. The actual yield time may vary. The following code sample illustrates how this method returns it for convenience: Since the actual delay time may vary, the following code sample illustrates how you can get the actual time by storing the method's return value: ```lua local elapsedTime = task.wait(2) -- Wait for 2 seconds print(elapsedTime) --> 2.0792941 ``` If no duration is given the duration will default to zero meaning the thread will automatically resume on the next step. This means `Library.task.wait()` is equivalent in behavior to `Class.RunService.Heartbeat`. > **Info:**`Library.task.wait()` is an optimized version of `Global.RobloxGlobals.wait()` that schedules the current thread to resume after some time elapses without throttling. --- title: "Access control and confidentiality" url: /docs/en-us/scripting/security/access-control last_updated: 2026-06-29T19:34:11Z description: "Learn what information and assets are visible to the client and secure them against exploiters." --- # Access control and confidentiality > **Warning:** The following content covers various concepts and tactics to improve security and mitigate cheating in your Roblox experiences. It's highly recommended that all developers read through these to ensure that your experiences are secure and fair for all users. Check the sidebar for additional security topics. Understanding what information and assets are visible to clients is critical for maintaining both the security and confidentiality of your experience. Developers often underestimate how much is visible to exploiters and replicated to clients. Exploiters have the ability to access "restricted" places within your universe if they can join a single place. They can view any content replicated to their client, regardless of whether or not it's visible or currently in use. Additionally, exploiters can decompile any replicated local scripts and ModuleScripts, even if they are never executed on the client. ## Client-side teleportation within universes Once inside a universe, clients can teleport to any place within that universe, potentially bypassing any intended access restrictions or progression gates. This can also lead to the unintended leak of unreleased content, for example from a development or staging experience. It is important to understand that: - Disabling "Direct Access" only removes the Join button from subplaces on the website — it does not prevent client-initiated teleports - Assume exploiters will discover the existence of all subplaces within your universe - A client can teleport to any subplace if they can access a single place, such as the root place, regardless of your intended design flow Many developers attempt to prevent access by kicking unauthorized users from a subplace. This may work for blocking gameplay, but it does **not** prevent content from replicating to the client, since replication begins as soon as the player joins and before server-side kick logic can execute. ### Use secure teleports The most effective way to prevent unauthorized access via teleportation is to set **Access Control for Places** to **Secure within universe only** in the Creator Dashboard. This restricts all non-start places to server-initiated teleports only, blocking client-initiated teleports at the platform level before the player ever joins the subplace. Because the player never joins, no content replicates to them. > **Success:** Enabling **Secure within universe only** is the recommended setting for any experience with restricted subplaces, progression-gated areas, or test places that players should not have access to. For configuration steps and migration guidance for existing experiences that use client-initiated teleports, see [Teleport between places](/docs/en-us/projects/teleport.md#configure-secure-teleportation). ### Defense-in-depth for restricted places For universes that cannot use secure teleports, or as additional layers of protection alongside them, combine the following measures: - **Keep development and test places in separate, private universes** — this is the only reliable way to ensure confidentiality for unreleased content. Never ship confidential event assets, scripts, or UI elements to the production environment before they are intended to be active. - If feasible, use streaming to limit how much of the world replicates to a new player. - Add server-side group role or badge verification and verify player state or progression requirements. - By default, disallow incoming players. This ensures no player will be allowed in even if the verification process is inconclusive, such as in the case of an exception thrown from the engine. - If practical, use the `Class.Players.BanAsync|Ban API` for players that fail validation. This prevents players from continuously attempting to join on the same account. ## Replication Replication describes how state transfers over the network between instances of the engine. Roblox's replication model is generally simplified in a few key ways: - Instances are server-authoritative, meaning that for an instance to replicate between all participants (the server and all connected clients) it must be created on the server. - Instance properties are also server-authoritative, which means most properties must be changed on the server for the changes to be visible on all clients. - Generally speaking, an instance either replicates to all connected clients or does not. There are some exceptions, such as streaming. Replication containers are top-level instances (parented under the DataModel) that replicate to clients. If an instance ever becomes a descendant of a replication container over its lifetime, you should expect much of its state to replicate to all clients. You can read more about common replication containers in the [Data model guide](/docs/en-us/projects/data-model.md#replication). When in doubt, you can always check how an instance or property replicates in a test environment such as Play Solo. You can read more about testing modes in [Studio testing modes](/docs/en-us/studio/testing-modes.md). ### Security implications of replication Any content that replicates to a client can be extracted and data-mined by an exploiter. As a general rule, avoid publishing or shipping any confidential content to the live production environment unless you are immediately prepared for it to be seen by users. Even if there is logic that gates the release or access of the content in-experience until a later date (or some other condition), assume that exploiters will find a way to discover and leak your content as soon as it is published. Avoid overly descriptive or predictable names for sensitive instances, including but not limited to: scripts, remotes, and models. Having a predictable DataModel hierarchy makes it easier to develop exploits. ### Script decompilation Any `Class.LocalScript`, `Class.Script` with `RunContext::Client`, or `Class.ModuleScript` can be decompiled by an exploiter once replicated to their client, even if such scripts are disabled, never required, or never run on the client. Server-only Scripts and ModuleScripts stored in `ServerStorage` or `ServerScriptService` cannot be decompiled as they never replicate to clients. Writing a ModuleScript that includes server-only and client-only code in the same script is ill-advised since server-sided logic will be exposed in decompilation and can be more easily dissected for bugs that can be exploited. ```lua local module = {} local Remote = script:WaitForChild("RemoteEvent") -- Here's an example of a paradigm to avoid -- Keep server code in Script instances or in ServerStorage/ServerScriptStorage! if game:GetService("RunService"):IsServer() then function module:DoServerThing(password) if password == "SecretPassword" then print("sensitive server code") end end Remote.OnServerEvent:Connect(function(player, password) module:DoServerThing(password) end) else function module:DoServerThing(password) Remote:FireServer(password) end end return module ``` ```lua local table1 = {}; local RemoteEvent = script:WaitForChild("RemoteEvent"); if game:GetService("RunService"):IsServer() then function table1.DoServerThing(_, p2) -- Line: 9 if p2 == "SecretPassword" then print("sensitive server code"); end end RemoteEvent.OnServerEvent:Connect(function(player: Player, p3) -- Line: 15 --[[ Upvalues: [1] = table1 --]] table1:DoServerThing(p3); end); return table1; end function table1.DoServerThing(_, p1) -- Line: 19 --[[ Upvalues: [1] = RemoteEvent --]] RemoteEvent:FireServer(p1); end return table1; ``` As shown above, decompilation reveals server-side logic including hardcoded passwords, business rules, and implementation details that exploiters can analyze for vulnerabilities. ### Impact of confidentiality breaches Confidentiality breaches can have significant consequences, such as: - **Content leaks**: Unreleased items, maps, or features may be discovered and shared publicly before official announcements - **Competitive disadvantage**: Mechanics, algorithms, or upcoming features may be reverse-engineered by competitors - **Exploit development**: Exposed server logic makes it faster and easier for exploiters to identify vulnerabilities and develop targeted attacks --- title: "Securing the client-server boundary" url: /docs/en-us/scripting/security/client-server-boundary last_updated: 2026-06-29T19:34:11Z description: "Best practices client-server actions to protect your experience from attack vectors." --- # Securing the client-server boundary > **Warning:** The following content covers various concepts and tactics to improve security and mitigate cheating in your Roblox experiences. It's highly recommended that all developers read through these to ensure that your experiences are secure and fair for all users. Check the sidebar for additional security topics. Any time a client can trigger an action on the server, there is potential for abuse. While `Class.RemoteEvent|RemoteEvents` and `Class.RemoteFunction|RemoteFunctions` are the most common attack vector here, other instances like `Class.ProximityPrompt` are susceptible. This section covers how to secure this boundary. ## Validating remote inputs Every piece of data sent from a client must be validated by the server before it's used. Apply these layers of validation based on what the remote does. ### Context/permission validation This is necessary for remotes that affect experience's state, progression, or other players. The server must verify if the player has the necessary permissions to make the request. For example, is the player close enough to a shop to buy something? Do they have a key required to open a door? Is their character alive? Validation is necessary for remotes that: - Grant rewards or modify player progression - Affect other players or shared experience state - Perform actions with distance, timing, or permission requirements (for example, shop distance) ### Type and structure validation Another attack that exploiters might launch is to send technically valid types but make them extremely large, long, or otherwise malformed. For example, if the server has to perform an expensive operation on a string that scales with length, an exploiter could send an incredibly large or malformed string to bog down the server. Another common attack that exploiters may use involves sending `Library.table|tables` in place of an `Class.Instance`. Complex payloads can mimic what would be an otherwise ordinary object reference. For example, provided with an [in-experience shop system](#in-experience-shop) where item data like prices are stored in `Class.NumberValue` objects, an exploiter may circumvent all other checks by doing the following: ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local itemDataFolder = ReplicatedStorage:WaitForChild("ItemData") local buyItemEvent = ReplicatedStorage:WaitForChild("BuyItemEvent") local payload = { Name = "Ultra Blade", ClassName = "Folder", Parent = itemDataFolder, Price = { Name = "Price", ClassName = "NumberValue", Value = 0, -- Negative values could also be used, resulting in giving currency rather than taking it! }, } -- Send malicious payload to the server (this will be rejected) print(buyItemEvent:InvokeServer(payload)) -- Outputs "false Invalid item provided" -- Send a real item to the server (this will go through!) print(buyItemEvent:InvokeServer(itemDatafolder["Real Blade"])) -- Outputs "true" and remaining currency if purchase succeeds ``` ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local itemDataFolder = ReplicatedStorage:WaitForChild("ItemData") local buyItemEvent = ReplicatedStorage:WaitForChild("BuyItemEvent") local function buyItem(player, item) -- Check if the passed item isn't spoofed and is in the ItemData folder if typeof(item) ~= "Instance" or not item:IsDescendantOf(itemDataFolder) then return false, "Invalid item provided" end -- The server can then go on to process the purchase based on the example flow below end -- Bind "buyItem()" to the remote function's callback buyItemEvent.OnServerInvoke = buyItem ``` ### Value validation In addition to validating types and data, you should validate the values passed through `Class.RemoteEvent|RemoteEvents` and `Class.RemoteFunction|RemoteFunctions`, ensuring they are valid and logical in the context being requested. This is essential for remotes that handle currency, items, numeric inputs, or user-generated content. You must ensure values are within expected boundaries (e.g., a purchase quantity is greater than zero) and that provided IDs or names are valid (e.g., an itemId corresponds to a real item in your game). **Special considerations for numeric values:** Both inf and NaN are valid number types, but both can cause major issues if an exploiter sends them and they're not handled correctly. Use functions like these to detect and reject them: ```lua local function isNaN(n: number): boolean -- NaN is never equal to itself return n ~= n end local function isInf(n: number): boolean -- Number could be -inf or +inf return math.abs(n) == math.huge end ``` ### The danger of NaN An exploiter can send NaN (Not a Number) as an argument. NaN is uniquely dangerous because it is of type "number" but fails all standard comparisons, allowing it to subtly bypass logical checks that seem safe. Let's look at a vulnerable trading system where a player makes an offer. ```lua -- VULNERABLE SERVER CODE local function onCreateTradeOffer(player, offeredGold) -- 1. TYPE CHECK: This passes! typeof(NaN) is "number". if typeof(offeredGold) ~= "number" then return "Invalid offer" end -- 2. RANGE CHECK: This is bypassed! -- (NaN < 0) is false. (NaN > 1000000) is also false. The check does nothing. if offeredGold < 0 or offeredGold > 1000000 then return "Offer out of range" end -- 3. INVENTORY CHECK: This is bypassed! -- (NaN > player.Gold.Value) is false. if offeredGold > player.Gold.Value then return "Not enough gold" end -- VULNERABILITY: A fraudulent trade offer with NaN gold is created! createTrade(player, {gold = offeredGold}) return "Trade offer created." end ``` The exploiter successfully created a trade offer without having any gold, because every check failed silently. Furthermore, this single NaN value will now poison any other system that tries to use it, and even spread to other players through the offer. Below are some high-level examples covering secure design of common in-game features. ### In-experience shop Consider an in-experience shop system with a user interface, for instance a product selection menu with a "Buy" button. When the button is pressed, you can invoke a `Class.RemoteFunction` between the client and the server to request the purchase. However, it's important that the server, the most reliable manager of the experience, confirms that the user has enough money to buy the item. ![Example purchase flow from client to server through a RemoteEvent](../../assets/scripting/security/Remote-Purchase-Flow.png)_Example purchase flow from client to server through a `Class.RemoteFunction`_ ### Weapon targeting Combat scenarios warrant special attention on validating values, particularly through aiming and hit validation. Imagine an experience where a player can fire a laser beam at another player. Rather than the client telling the server who to damage, it should instead tell the server the origin position of the shot and the part/position it thinks it has hit. The server can then validate the following: - The position the client reports shooting from is near the player's character on the server. Note that the server and client will differ slightly due to latency, so extra tolerance will need to be applied. - The position the client reports hitting is reasonably close to the position of the part the client reports hitting, on the server. - There are no static obstructions between the position the client reports shooting from and the position the client reports shooting to. This check ensures a client isn't attempting to shoot through walls. Note that this should only check static geometry to avoid valid shots being rejected due to latency. Additionally, you may want to implement further server-side validations as follows: - Track when the player last fired their weapon and validate to ensure they're not shooting too fast. - Track each player's ammo amount on the server and confirm that a firing player has enough ammo to execute the weapon attack. - If you've implemented teams or a "players against bots" combat system, confirm that the hit character is an enemy, not a teammate. - Confirm that the hit player is alive. - Store weapon and player state on the server and confirm that a firing player is not blocked by a current action such as reloading or a state like sprinting. ## Rate limiting Whenever server-side logic can be triggered by the client (via remotes, Touched events, proximity prompts, ClickDetectors, etc.), there is a possibility that this logic could be spammed by exploiters or even legitimate users. Rate limiting controls how frequently these actions can be performed, preventing abuse and system overload. While it is practical (and sometimes necessary) to implement rate limits on the client, never rely solely on a client-sided rate limit. For any server-sided logic that can be triggered by a client, always consider the maximum rate at which such logic should run. Many server-sided actions must have their frequency limited to prevent abuse or failure, such as: - **Roblox API calls**: Backend APIs such as `Class.DataStoreService` and `Class.BadgeService` have built-in rate limits that will cause your requests to fail if exceeded - **Computationally expensive operations**: Actions that consume significant server resources or impact other clients - Server example: cloning large models from ServerStorage, even if they're never replicated to clients - **Client example**: instructing all connected clients to update their GUI simultaneously - **Exploitable mechanics**: Actions that could be abused if performed rapidly, such as granting invulnerability, teleporting players, or awarding currency ### Token bucket example A robust and common approach for rate limiting is the **token bucket** algorithm. Imagine each user has a bucket that can hold a certain number of tokens. To perform an action, the user must spend a token. The bucket refills with new tokens at a steady rate. This method allows for short bursts of actions when needed, but prevents sustained spam by enforcing an average rate over time. ```lua --!strict -- Module located in ServerScriptService type UserId = number type Bucket = { tokens: number, last: number, } type TokenBucketT = { capacity: number, refillPerSecond: number, buckets: { [UserId]: Bucket }, allow: (self: TokenBucketT, userId: UserId) -> boolean, } local TokenBucket = {} TokenBucket.__index = TokenBucket -- Creates a limiter that allows at most \`capacity\` events, refilling back to -- full at a rate of \`capacity / windowSeconds\` tokens per second. function TokenBucket.new(capacity: number, windowSeconds: number): TokenBucketT assert(capacity >= 1, "capacity must be >= 1") assert(windowSeconds > 0, "windowSeconds must be > 0") local self: TokenBucketT = { capacity = capacity, refillPerSecond = capacity / windowSeconds, buckets = {}, } return (setmetatable(self, TokenBucket) :: any) :: TokenBucketT end local function refill(b: Bucket, now: number, cap: number, rate: number) local elapsed = now - b.last if elapsed > 0 then b.tokens = math.min(cap, b.tokens + elapsed * rate) b.last = now end end -- Returns false if the user would exceed their limit function TokenBucket:allow(userId: UserId): boolean local now = time() local b = self.buckets[userId] if not b then b = { tokens = self.capacity, last = now } self.buckets[userId] = b else refill(b, now, self.capacity, self.refillPerSecond) end if b.tokens >= 1 then b.tokens -= 1 return true end return false end return TokenBucket ``` To use the module, you first create a new limiter instance with `TokenBucket.new(capacity, windowSeconds`). The `capacity` is the maximum number of requests a user can make in a quick burst, and the `windowSeconds` determines how long it takes to refill all those tokens. For example, `TokenBucket.new(5, 10)` creates a limiter that allows bursts of up to 5 requests and refills one token every two seconds (10 seconds / 5 tokens). Before executing protected logic, make a call to the `:allow(userId)` method, denying the action if it returns false. It's also good practice to clear the user's bucket data when they leave to prevent memory leaks. The following example script demonstrates how to protect a `RemoteEvent` used for a custom chat system from being spammed by players. ```lua -- Example usage in a server script local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") local ServerScriptService = game:GetService("ServerScriptService") local TokenBucket = require(ServerScriptService.TokenBucket) local ChatRemote = ReplicatedStorage:WaitForChild("ChatRemote") :: RemoteEvent -- 5 messages per 10 seconds (capacity 5, refills at 0.5 tokens/sec) local chatLimiter = TokenBucket.new(5, 10) ChatRemote.OnServerEvent:Connect(function(player: Player, message: string) if not chatLimiter:allow(player.UserId) then -- Too fast: drop the request or send a spam warning return end -- Process message (after other validation) broadcastMessage(player, message) end) -- Cleanup to prevent memory leaks Players.PlayerRemoving:Connect(function(p: Player) chatLimiter.buckets[p.UserId] = nil end) ``` ## Securing client-triggered instances `Class.ProximityPrompt|ProximityPrompts`, `Class.ClickDetector|ClickDetectors`, and `Class.DragDetector|DragDetectors` are not remotes, but an exploiter can trigger their events from any distance, at any time, often ignoring properties like `Enabled` or `MaxActivationDistance`. These objects must be secured with the same rigor as remotes. ### ProximityPrompt - An exploiter can fire any event even if `Enabled` is false on the server. - `Enabled`, `MaxActivationDistance`, and `RequiresLineOfSight` can be silently changed on the client, so any replicated `Class.ProximityPrompt` can be seen and potentially interacted with from any location. - The server will accept hold events (such as PromptButtonHoldBegan) even if the server-sided `HoldDuration` is zero. The client can manipulate the `HoldDuration` to perform interactions unnaturally quickly. - Only the `Triggered` event has a server-side distance check. Other events (like `PromptButtonHoldBegan` and `TriggerEnded`) have no distance check, and can be sent arbitrarily by any client. ### ClickDetector - There are no checks at all on the server for any events. - Exploiters can replicate any event at any distance, even if the ClickDetector is disabled (`MaxActivationDistance` of 0 or `DragDetector.Enabled` false). - Events can be replicated even if the `Class.ClickDetector` is not parented to something clickable. ### DragDetector - For events inherited from ClickDetector, there are no checks. - For dragging events, the `Enabled` property and `PermissionPolicy` properties are checked and respected by the server. All other properties are not checked. When one of these events fires, your server script should perform its own validation before taking any action: - Check if it's enabled: Ensure that the instance is intended to be enabled by checking its properties (such as Enabled, HoldDuration, RunLocally, etc.) on the server. This step isn't necessary if the object is always enabled. - Check player state: Ensure that the player can perform the action, given the player's current state. Is the player's character in the world? Is their character close enough to the object? Should the player need to be alive to interact with this object? Check any other player state maintained by your experience on the server as needed. - Apply rate limiting: As covered in [Rate Limiting](#rate-limiting), apply cooldowns on the client and enforce rate limits on the server to prevent these events from being spammed and abused. For interactions that are expected to take a minimum amount of time, ensure that clients are not completing them too quickly. **Note on network ownership**: By default, if any of these objects are children of unanchored parts or an unanchored assembly, an exploiter can take network ownership of the parent parts and move them directly to their character, bypassing distance checks. For critical actions, use anchored parts or the network ownership API along with the necessary server-sided validation. ### RemoteEvents and RemoteFunctions Limit the scope and impact of `Class.RemoteFunction|RemoteFunctions` and `Class.RemoteEvent|RemoteEvents`. Take extreme care in dynamically loading any assets (even textures or sounds) or running experience code (especially via require) based on the arguments of remote functions/events. Avoid implementing remotes that allow a client to specify an arbitrary path or instance reference for the server to delete or modify, even if such modifications seem trivial. Remotes that can change arbitrary instance state or make widespread changes to the DataModel tree can often be chained with other bugs to severely impact overall state or logic. Check not only the type of instance arguments, but also the class and expected location or structure within the DataModel. ## Client to client communication Some remotes are designed to let one client trigger effects on other clients. This happens when a client fires a remote to the server, which then uses `RemoteEvent:FireAllClients()`, `RemoteEvent:FireClient()`, or similar methods to relay information. This pattern is dangerous if not properly secured - the server must be a gatekeeper, not just a relay. Imagine a scenario where players can cast a lightning spell. When cast, nearby players see the flash, hear the sound, and experience camera shake. A vulnerable server script might look like this: ```lua local castLightningEvent = game.ReplicatedStorage.CastLightning -- This server script relays the message to everyone with no validation castLightningEvent.OnServerEvent:Connect(function(player, strikePosition) castLightningEvent:FireAllClients(strikePosition) end) ``` ```lua local castLightningEvent = game.ReplicatedStorage.CastLightning -- Create visual and sound effects on the client local function createLightningEffect(strikePosition) -- Code to shake the camera -- Code to play a loud lightning sound -- Code to create a visual lightning bolt print("Lightning strikes at: " .. tostring(strikePosition)) end -- When the server broadcasts the event, this client runs the effect castLightningEvent.OnClientEvent:Connect(createLightningEffect) ``` An exploiter can abuse this vulnerable system by: - **Spamming**: Calling the remote hundreds of times per second, causing continuous effects that make the experience unplayable - **Invalid data**: Sending nil, NaN, or wrong types that cause script errors on all clients - **Unauthorized use**: Casting spells they haven't unlocked or don't have resources for The server must be a **gatekeeper**, not a relay. Before broadcasting, it must validate the request and apply rate limiting per-player. Here is an example of some methods that could be employed to validate the request. ```lua local castLightningEvent = game.ReplicatedStorage.CastLightning local playerCooldowns = {} local COOLDOWN_TIME = 3 castLightningEvent.OnServerEvent:Connect(function(player, strikePosition) -- 1. Type validation if typeof(strikePosition) ~= "Vector3" then return -- Invalid type, reject silently end -- 2. Check for NaN (NaN ~= NaN is the only way to detect it) if strikePosition.X ~= strikePosition.X then return -- Contains NaN, reject end -- 3. Example Rate limiting local lastCast = playerCooldowns[player] or 0 if tick() - lastCast < COOLDOWN_TIME then return -- Still on cooldown end -- 4. Example Permission check if not player:GetAttribute("HasLightningSpell") then return -- Player doesn't have this spell end -- 5. Example Range validation local character = player.Character local humanoidRootPart = character and character:FindFirstChild("HumanoidRootPart") if not humanoidRootPart then return end local distance = (humanoidRootPart.Position - strikePosition).Magnitude if distance > 100 then return -- Out of range end -- All checks passed - safe to broadcast playerCooldowns[player] = tick() castLightningEvent:FireAllClients(player, strikePosition) end) ``` By implementing these checks, it becomes harder for an exploiter to ruin the experience for other players. The server acts as a protective barrier, ensuring only valid, authorized actions are broadcast to other clients. ## Special considerations The following are some additional cases you should consider that may require specialized handling. ### Data store manipulation In experiences using DataStoreService to save player data, exploiters may take advantage of invalid data, race conditions to corrupt saves or duplicate items in a DataStore. This is especially problematic in experiences with item trading, marketplaces, and currency systems. Ensure that any actions performed through a `Class.RemoteEvent` or `Class.RemoteFunction` that affect player data with client input is sanitized based on the following: - **Instance values** cannot be serialized into a DataStore and will fail. Utilize type validation to prevent this. - **DataStores have data limits**. Strings of arbitrary length should be checked and/or capped to avoid this, alongside ensuring limitless arbitrary keys cannot be added to tables by the client. - **Table indices cannot be NaN or nil**. Iterate over all tables passed by the client and verify all indices are valid. - **DataStores can only accept valid UTF-8 characters**, so you should sanitize all strings provided by the client via utf8.len() to ensure they are valid. utf8.len() will return the length of a string, treating unicode characters as a single character; if an invalid UTF-8 character is encountered it will return nil and the position of the invalid character. Note that invalid UTF-8 strings can also be present in tables as keys and values. - **Infinite/NaN number exploits** - Exploiters may send values like -1/0, 0/0, or math.huge to cause infinite currency or break calculations. Always validate using the methods described in [the danger of NaN](#the-danger-of-nan). Race conditions can occur when players manipulate timing between operations to duplicate items or corrupt data. **Trading exploit example:** A player initiates a trade, sends their item to another player, then immediately leaves the game. If the trade completes but their DataStore save fails due to invalid data, they rejoin with their original items while the other player keeps the traded items - resulting in duplication. **Prevention strategies:** - Validate all data before any trade operations - Use transaction-like patterns where all players' data is validated before committing any changes - Implement proper error handling that reverts all changes if any part fails ### MarketplaceService For interactions involving `Class.MarketplaceService`, such as passes or developer products, all purchase validation and item granting must occur on the server. Specifically, utilize the `ProcessReceipt` callback to securely validate purchase receipts. Never trust a client-side signal, such as `PromptProductPurchaseFinished`, to confirm a purchase without server verification, as these can be spoofed. Ensure that your `ProcessReceipt` function thoroughly checks the receipt details and only grants items or currency after confirming a legitimate transaction with Roblox servers, and be prepared to handle cases where a product has already been granted for a given receipt. --- title: "Defensive design tactics" url: /docs/en-us/scripting/security/defensive-design last_updated: 2026-06-29T19:34:11Z description: "Understand fundamentals of design that help prevent cheaters and exploiters from ruining the playing experience." --- # Defensive design tactics > **Warning:** The following content covers various concepts and tactics to improve security and mitigate cheating in your Roblox experiences. It's highly recommended that all developers read through these to ensure that your experiences are secure and fair for all users. Check the sidebar for additional security topics. Basic design decisions can serve as "first step" security measures to discourage exploits. The core principle is to design systems where cheating is either impossible or provides no meaningful advantage, rather than trying to detect and prevent cheating after the fact. Consider a shooter game where players earn points for kills. An exploiter might create bots that teleport to the same location to be repeatedly killed for easy points. This scenario illustrates the difference between reactive and defensive approaches: | Approach | Predictable outcome | | --- | --- | | Chase down bots by writing code that attempts to detect them (reactive approach). | > **Error:** Exploiters seeking quick points will find a way around your complex detection code and use their bots in a different way. | | Reduce or outright remove point gains for kills on newly spawned players (defensive approach). | > **Success:** Extra time and friction is now required for exploiters because they get no points for instantly killing their bots. Additionally, "spawn campers" are discouraged because they no longer get points for killing newly spawned players. | Additional defensive design scenarios: | Potential exploit scenario | Reactive Approach (less effective) | Defensive approach (more effective) | | --- | --- | --- | | (Obby) Exploiter teleports to the end to claim rewards | Only check the player's final position and try to detect impossible completion times | Design with mandatory, sequential checkpoints. Server validates each checkpoint was activated in order before granting rewardsYou can add another layer by flagging players who complete stages faster than humanly possible. The design of the experience (requiring checkpoints) is what enables the effective server-side validation. | | (Combat) Client reports dealing impossible damage amounts | Try to detect and filter out impossible damage values | Server calculates all damage from server-sided weapon stats and validates hits through server-side raycasting | | (Economy) Exploiter duplicates items through rapid requests | Try to detect duplicate requests or unusual patterns | Implement server-side cooldowns and validate all transactions against current player inventory state | > **Info:** **Key principle** When designing a new feature, ask yourself: "How would an exploiter abuse this, and can I change the design to make that abuse impossible or worthless?" Instead of trying to detect cheating after it happens, design your experience so that cheating either cannot occur or provides no meaningful advantage. --- title: "Network ownership, movement validation, and physics" url: /docs/en-us/scripting/security/network-ownership last_updated: 2026-06-29T19:34:11Z description: "Learn about network ownership and its relationship to assemblies, physics, and more." --- # Network ownership, movement validation, and physics > **Warning:** The following content covers various concepts and tactics to improve security and mitigate cheating in your Roblox experiences. It's highly recommended that all developers read through these to ensure that your experiences are secure and fair for all users. Check the sidebar for additional security topics. [Network ownership](/docs/en-us/physics/network-ownership.md) is one of the more nuanced and commonly misunderstood aspects of the Roblox engine. When a player has network ownership of a part or assembly, they have complete authority over the physics simulation for those objects. By default, the server grants network ownership of unanchored parts to clients with nearby player characters to distribute the load of the physics simulation. This system improves responsiveness but creates significant security implications that every developer must understand. ## Understanding network ownership When a client has network ownership over parts (including their character), they have access to: **Assembly manipulation:** - Teleport to any position. - This can be used to quickly gain ownership and control of as many unanchored parts and assemblies as the server will allow. - Manipulate their movement and state, such as flying or changing their speed. - Exploiters can set their Humanoid WalkSpeed to any value. Properties can be modified locally without firing any events on the local client, and without such property changes capable of being read from local scripts. - Play arbitrary animations and manipulate animation state. - Bypass physical obstacles and alter their collision. **Physics manipulation** - Control the position and rotation of any unanchored parts or mechanisms, including replicating `Inf` or `NaN` components in `Datatype.CFrame|CFrames`. Set part velocities to extreme values (including `Inf` or `NaN`), which can interfere with the physics of other unanchored parts/assemblies, even those that are not owned by the exploiter. This is often used to fling other player characters and nearby parts. Manipulate the firing of Touched events, including not firing Touched at all. Should your experience necessitate gameplay-critical fully unanchored parts or assemblies, consider using the network ownership API to manually set ownership or turn off automatic ownership. ## Movement validation In competitive experiences, server-side validation of player movement is crucial for simulation integrity, as clients control character movement and physics. However, a universal solution doesn't exist due to varying gameplay mechanics. Traditional methods often fall short in complex scenarios. Simple distance-over-time calculations may suffice for competitive shooters but fail in physics-intensive experiences involving vehicles, flight, or intricate interactions. Validation must also account for network latency. > **Warning:**[Server authority](/docs/en-us/projects/server-authority.md) is currently in beta and will be released soon. The most reliable solution for preventing physics and movement exploits is server authority, which moves physics simulation and movement validation entirely to the server. This eliminates the fundamental security weakness of client-controlled movement by ensuring all movement decisions are made on the server with authoritative validation. Until server authority becomes available, developers implementing client-side validation must understand that effective approaches vary dramatically. Basic heuristics can flag innocent players with unstable connections, and straightforward speed calculations break down in experiences with complex movement systems. Position updates are subject to internet latency, requiring averaging over time. Many developers find that leaky bucket-style accumulators work well for handling burst movements while preventing sustained violations. Practical implementation often requires projecting movement onto specific planes (e.g., XZ for ground-based movement) to avoid penalizing legitimate vertical movement while detecting horizontal teleportation. Experiences with legitimate teleportation mechanics need explicit exemption systems. Validation frequency, tolerance levels, and enforcement strategies should be tuned to each experience's specific requirements. --- title: "Security and cheat mitigation tactics" url: /docs/en-us/scripting/security/security-tactics last_updated: 2026-06-29T19:34:11Z description: "Explore security tactics and cheat mitigation tactics for Roblox experiences." --- # Security and cheat mitigation tactics > **Warning:** The following content covers various concepts and tactics to improve security and mitigate cheating in your Roblox experiences. It's highly recommended that all developers read through these to ensure that your experiences are secure and fair for all users. Check the sidebar for additional security topics. Before diving into specific tactics to developing securely and prevent cheating, it's essential to understand the foundational principles of Roblox security. A secure experience is built on a mindset that anticipates adversarial actions. Before writing a single line of code, you must internalize these foundational principles. They should inform every architectural and design decision you make. ## Never trust the client This is the foundational principle. A determined exploiter has complete control over their local state and network traffic. Because exploiters have this level of control, any security measure that relies on client-side enforcement will eventually be bypassed. This is not a limitation of Roblox: it's a fundamental reality of client-server architectures. Assume every piece of data sent from the client has been manipulated, fabricated, or sent with malicious intent. This includes the power to: - Decompile any replicated LocalScript or any ModuleScript, even if they never run on the client - Take network ownership of their character and any unanchored parts - Trigger client-initiated events such as Touched events or ProximityPrompt activations at any range or frequency - Modify their player's position, physics, or interactions with the world - Fire or invoke RemoteEvents and RemoteFunctions at any frequency with arbitrary arguments (besides the first Player argument) - Change anything in their local DataModel without firing any expected events - Arbitrarily alter the behavior of any locally running code Because of this, all critical logic must be validated server-side or run exclusively on the server. The consequences of this control are detailed in [Network Ownership, Movement Validation, and Physics Exploits](/docs/en-us/scripting/security/network-ownership.md) and [Access Control and Confidentiality](/docs/en-us/scripting/security/access-control.md). ## Server authority The server must be the ultimate source of truth for all simulation states, rules, player progression, and critical decisions. The client's role is to render the world and send user input to the server. The server's role is to: - Receive input from the client - Validate that the requested action is possible and permissible - Execute the action and update its authoritative state - Replicate the results to all relevant clients For example, if a player says "I want to buy a Bloxy Cola" the server must know the item's true price, the player's money, and the player character's physical distance from the shop before validating and approving the transaction. As much as possible, the state to be validated should be maintained exclusively by the server and not by clients. In the example, if the Bloxy Cola transaction is approved, the server should subtract the Bloxy Cola's price from the player's money, however, the server cannot necessarily always control the player's character. ## Security by design Integrate security considerations into your experience's design from the very beginning, rather than attempting to bolt them on later as an afterthought. - Threat model every new feature. For every new feature, ask - How could an attacker exploit this if they have full control of their client? - If a client could send any value for any parameters used in the feature, what's the worst possible outcome? - What happens if this feature is used 1,000+ times per second? What is the maximum rate at which it should be used? - Could an exploiter use this to ruin another player's experience? - How much resources, in the worst case, could this feature feasibly use? - What is the minimum internal information or state that I need to expose for this feature? - Partition responsibilities early. Keep logic and data in ServerScriptService from day one. Never place them in replicated containers like ReplicatedStorage or Workspace. --- title: "Server-side detection and consequencing" url: /docs/en-us/scripting/security/server-side-detection last_updated: 2026-06-29T19:34:11Z description: "Find ways to respond to abusive looking behavior and respond accordingly." --- # Server-side detection and consequencing > **Warning:** The following content covers various concepts and tactics to improve security and mitigate cheating in your Roblox experiences. It's highly recommended that all developers read through these to ensure that your experiences are secure and fair for all users. Check the sidebar for additional security topics. This section assumes you already validate inputs and context (see previous sections). This article covers what to do after a request is technically valid but behavior looks abusive, plus how to respond without harming legitimate players. ## Philosophy - **The server decides**. Detection and consequencing live on the server. Try to never ban based on client-side detections, as exploiters can easily bypass them. - **Prevent harm first**. Quietly neutralize the impact of an exploit. For example, if a player is speed-hacking, just rubber-band them back to their last valid position instead of kicking them immediately. Punish only when necessary to protect the experience or other players. - **Be proportional and reversible**. Assume false positives can happen. Prefer actions you can roll back, such as temporary suspensions or resource removal, over permanent bans. - **Design > detection**. Your primary security comes from robust validation and rate limiting (covered in [Securing the client-server boundary](/docs/en-us/scripting/security/client-server-boundary.md) and other sections). The detection methods below are meant to supplement those defenses, not replace them. ## Heuristics Heuristics are behavior checks that flag actions that are technically possible but highly improbable for a legitimate player. They are "rules of thumb" for catching suspicious activity that slips past basic validation. An effective heuristic compares a player's action against a theoretical maximum or a reasonable baseline. For example, if a player suddenly claims to have gained 1 billion coins in your simulator game, your remote validation might confirm the request is structured correctly. But a heuristic check on the server would know that earning that much should take weeks, not seconds, and flag the transaction as suspicious. Here are three classic examples of server-side heuristics. Each one validates player behavior against the experience's rules and physical limitations, creating a strong defense against common exploits. | **Server-side heuristic** | **Description** | **Example** | | --- | --- | --- | | **Fastest Completion Time** | Calculate the theoretical fastest possible time to complete a task, like an obstacle course. This involves finding the optimal path and assuming maximum player speed. | In an obby, if the world record is 30 seconds, a completion time of 5 seconds is a strong signal of teleportation or speed exploits. | | **Rate of Gain** | Track the maximum legitimate rate at which a player can earn a resource (currency, experience, items). | If a player can legitimately earn 100 coins per minute, a sudden gain of 10,000 coins in one second should be flagged. | | **Action Cadence** | The server analyzes the time intervals between a player's repeated actions. Human inputs have natural, slight variations in timing, whereas simple automation scripts (like auto-clickers) often perform actions with robotic consistency. | In a fishing minigame, a player clicks to cast their line. The server logs the timestamp of each cast. If the player casts their line 100 times in a row with the exact same interval (e.g., precisely 2.500 seconds) between each action, it's a strong indicator of a macro or bot. A human's timing would naturally fluctuate | Any one of these examples may not be enough on its own to convict a cheater. Each should be treated as a signal, not as definitive proof. A good practice is to implement a suspicion score. When a player fails a heuristic check, you increment their score. Severe actions like kicking or banning should only occur after multiple, varied detections have pushed a player's score past a high threshold. For example, suppose a skilled player finishes your obby with a record-shattering time, triggering your "Fastest Completion Time" heuristic. Should they be banned? It's possible they're exploiting, but they might also have discovered a legitimate but unintended shortcut. Banning them based on this single signal would be risky and could punish one of your most dedicated players. However, if that same player's run also flagged the "Action Cadence" heuristic for inhumanly consistent inputs, and an hour later they trigger the "Rate of Gain" heuristic in your lobby, the case becomes much stronger. The combination of these different signals paints a much more reliable picture of cheating. The strength of this system lies in correlating multiple data points to build a confident case before taking action. ## Honeypots One way to detect cheaters trying to probe your experience's RemoteEvents is to use a decoy called a honeypot. A honeypot is a `Class.RemoteEvent` or `Class.RemoteFunction` that appears legitimate to an exploiter but is never used by any of your legitimate client scripts. Because no normal player should ever be able to fire this remote, any traffic it receives must be from an exploiter. When the server detects this remote being fired, it's a very high-confidence signal that the client is meddling. The best immediate action is to log the incident and kick the player from the session. Another variation is to design RemoteEvents with specific directional purposes - some exclusively for client → server communication and others exclusively for server → client communication. While the event names don't reveal their intended direction, the server tracks which direction each remote should be used. If an exploiter attempts to fire a server → client remote from the client, this serves as a reliable detection method since legitimate gameplay would never trigger communication in the wrong direction. ## Applying consequences When detection systems flag suspicious behavior, consider your response strategy carefully. Different experiences require different approaches based on their community, competitive stakes, and tolerance for disruption. **The consequence ladder** - Most experiences benefit from escalating responses rather than jumping straight to permanent bans: - **Silent logging** - Build evidence without player impact - **Quiet mitigation** - Block the exploit's effect (drop requests, clamp values, sync correct state) - **Temporary restrictions** - Limit specific capabilities (trading, leaderboards, matchmaking) - **Visible enforcement** - Kicks, temporary suspensions, or permanent bans **Considerations for your strategy** - **Delay visible consequences** to avoid teaching exploiters exactly what triggered detection - **Match severity to response** - minor economic exploits may warrant different treatment than game-breaking physics cheats - **Consider your community** - competitive experiences may need stricter enforcement than casual experiences - **Plan for mistakes** - prefer reversible actions when detection confidence is lower For persistent offenders, consider using the `Class.Players.BanAsync|Ban API` rather than relying solely on kicks. The Ban API prevents banned players from rejoining and provides better tracking across your universe. Version your detection rules and track consequence rates so you can identify problematic systems. Monitor player feedback to gauge whether your enforcement feels fair to the community. The right balance depends entirely on your experience's needs and your players' expectations. --- title: "Vulnerabilities from third-party assets" url: /docs/en-us/scripting/security/third-party-vulnerabilities last_updated: 2026-06-29T19:34:11Z description: "Learn about vulnerability vectors from third-party assets." --- # Vulnerabilities from third-party assets > **Warning:** The following content covers various concepts and tactics to improve security and mitigate cheating in your Roblox experiences. It's highly recommended that all developers read through these to ensure that your experiences are secure and fair for all users. Check the sidebar for additional security topics. Third-party assets from the Creator Store (Toolbox) are a common source of security risks, as they can contain malicious scripts called backdoors. These scripts give their creators unauthorized server-side access to your experience, allowing them to manipulate its state, steal sensitive data, or disrupt the experience for your players. Backdoors are particularly dangerous because they often appear in otherwise legitimate-looking models. A seemingly innocent building or vehicle might contain a script that activates when certain conditions are met, such as when a specific player joins or a particular command is executed. When a model contains scripts, this is indicated in the Toolbox user interface before and during insertion. Roblox moderates Models for malicious scripts both proactively and in response to reports, but this process is not guaranteed to remove all such scripts. ## Protecting your game The most effective defense against backdoors is sandboxing. When you insert a model, you should contain its scripts to prevent malicious code from reaching sensitive APIs or affecting systems outside the model itself. 1. Set `SandboxedInstance` to `Experimental` on Workspace. 2. In the Properties window, set the `Sandboxed` property of the model to `true`. 3. Configure the `Capabilities` property to grant only the specific permissions the asset needs to function. > **Warning:** Note that during the beta, some features may not function as expected when `Sandboxed`, erroring with a message saying "...lacking Capability Unassigned". These will be fixed as Capabilities rolls out of beta. Be especially cautious about granting Network, DataStore, AssetRequire, CapabilityControl, or LoadString capabilities to third-party assets unless you understand exactly why the asset needs them. These capabilities provide access to powerful systems that backdoors commonly exploit. Before using any asset, carefully inspect its scripts. Pay particular attention to obfuscated or unusually complex code. Malicious actors often use deceptive techniques to hide harmful content, such as using whitespace to position malicious code off-screen when viewed in the studio editor. Consider favoring highly-rated community assets that have been vetted by other developers, though remember that popularity doesn't guarantee safety. For comprehensive guidance on configuring script restrictions, see [Script Capabilities](/docs/en-us/scripting/capabilities.md). > **Success:** In the future, we plan to automatically sandbox all assets from the Creator Store with appropriate Capabilities. --- title: "Services" url: /docs/en-us/scripting/services last_updated: 2026-06-29T19:34:11Z description: "Services provide pre-built functionality in the Roblox Engine." --- # Services In [Reuse code](/docs/en-us/scripting/module.md), you might have noticed frequent use of the `game:GetService()` method. Roblox services let you access the built-in features of the engine, like selling in-game items, enabling chat, playing sounds, animating objects, and managing instances. In fact, services are the first step in **the most fundamental, common pattern of Roblox development**: 1. Get services. 2. Require module scripts. 3. Add local functions. 4. Add the [events](/docs/en-us/scripting/events.md) that trigger those functions. For example, you might want to save players' positions in the world when they exit your game: ```lua local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") local SaveManager = require(ReplicatedStorage:WaitForChild("SaveManager")) -- Local function that calls a reusable function in the module script. local function saveProgress(character) -- Get the position of the player's character. local position = character:FindFirstChild("HumanoidRootPart").Position -- Use the saveData function in the module script, which writes to the -- DataStoreService. SaveManager.saveData(character, position) end -- Another local function that calls saveProgress() when a character is removed -- from the game (in this case, when the player leaves). local function onPlayerAdded(player) player.CharacterRemoving:Connect(saveProgress) end -- Calls onPlayerAdded when a player first connects to the game. Players.PlayerAdded:Connect(onPlayerAdded) ``` Some key details include: - Because you should only retrieve a service once per script, the convention is to give the variable the same name as the service. This convention applies to module scripts, as well. - You retrieve services with the global variable `Class.DataModel|game`, a reference to the root of the data model. - Roblox doesn't make guarantees around loading order (and [instance streaming](/docs/en-us/workspace/streaming.md) further complicates what is and isn't loaded at any given time), so the use of `Class.Instance:WaitForChild()` is an important safety measure. Rather than comparing [standard libraries](/docs/en-us/reference/engine/libraries.md), [global functions and variables](/docs/en-us/reference/engine/globals.md), or third-party libraries, a big part of Roblox development is identifying which of the many, many services can help you add the desired functionality to your games. In the example above, instead of using a standard I/O library to write to disk, you use [cloud services](#cloud-services) to store data. ## Container services Container services can contain and influence other objects. These container services reside at the root of the data model and are visible in Studio's Explorer window. Collectively, these container services form a structured hierarchy for the data model, so the Roblox Engine can properly interpret and render your place. The following table includes some common container services. | Service | Description | | --- | --- | | `Class.Workspace` | Contains all objects that render in the 3D world, such as parts and terrain. | | `Class.Lighting` | Contains objects for setting universal lighting effects, such as `Class.Atmosphere` and `Class.Sky`. | | `Class.ReplicatedStorage` and `Class.ReplicatedFirst` | Contain content and logic that replicates between the server and client. | To further examine the data model, you can use these methods: - `Class.DataModel:FindService()|game:FindService()` searches for the instance of the specified service. - `Class.DataModel:GetChildren()|game:GetChildren()` returns an array of all root children of the data model, which are the top-level container services. - `Class.DataModel:GetDescendants()|game:GetDescendants()` returns an array of all the descendants of the data model, including all container services and their children. For more information on container services, see the [data model](/docs/en-us/projects/data-model.md#object-organization) documentation. ## Scripting services Scripting services provide standard functionality in the Roblox Engine that you call within scripts. The following table includes some common scripting services. | Service | Description | | --- | --- | | `Class.TweenService` | Used to interpolate numeric properties of other instances from a start to end value, with options for easing direction and style, repeat, and delay. | | `Class.MarketplaceService` | The service responsible for in-game transactions, such as prompting the player to purchase a developer product, subscription, pass, Roblox Premium, etc. | | `Class.RunService` | Contains methods and events for frame-by-frame time management, as well as for checking the context (server, client, Studio mode) in which the game is running. Useful for running any process or update on every runtime frame. | | `Class.SoundService` | Controls various global aspects of how audio plays in a game, such as the doppler scale and volumetric audio. Can also contain sound groups to control the volume and dynamic effects properties of multiple audio signals at once. | | `Class.CollectionService` | Manages groups (collections) of instances with tags that replicate from the server to the client, letting you more easily assign and work with groups of related instances. | ## Cloud services Roblox also has special cloud services for handling tasks and processes that occur in the Roblox cloud. The following table includes some common cloud services. | Service | Description | | --- | --- | | `Class.DataStoreService` | For storing persistent data across sessions. | | `Class.MemoryStoreService` | For storing frequent and ephemeral data that change rapidly. | | `Class.MessagingService` | For communicating between multiple servers during live sessions. | Cloud services also have corresponding web APIs; they're accessible from external scripts or tools. For more information, see [Open Cloud](/docs/en-us/cloud/guides.md). --- title: "Script Sync" url: /docs/en-us/scripting/sync last_updated: 2026-06-29T19:34:11Z description: "Use Studio's Script Sync feature to edit your Luau scripts on your local disk using an external text editor or your favorite integrated development environment (IDE)." --- # Script Sync Roblox Studio has a built-in [script editor](/docs/en-us/studio/script-editor.md) for editing the code in your Roblox project. The editor includes key features like linting, type checking, autocomplete and collaborative editing. Many developers, however, have a preferred text editor or integrated development environment (IDE), such as [Visual Studio Code](https://code.visualstudio.com), [Cursor](https://cursor.com), [Sublime Text](https://www.sublimetext.com), or [Notepad++](https://notepad-plus-plus.org). Popular development environments often have features or plugins that don't exist in Studio. To use an external editor or IDE with Studio, enable the **Script Sync** feature. Script Sync works by synchronizing the scripts in your [data model](/docs/en-us/projects/data-model.md) to text files on your local disk. Changes made to the Luau files on disk are applied to the scripts in Studio, and vice versa. This synchronization means that you can edit a script in an external editor like VS Code and immediately see the change in Studio. ## When to use Script Sync - If you want to use your preferred text editor or check code into version control, but use Studio for everything else, Script Sync is a good fit. It also works with Roblox's [built-in collaboration tools](/docs/en-us/projects/collaboration.md), which third-party tools often do not. Script Sync works well with existing Roblox projects, as it's an extra feature rather than a total shift in how you manage the project. - If you want to check your entire project (not just code) into version control or use your file system as the source of truth for your project, [third-party tools](/docs/en-us/projects/external-tools.md) like Rojo are a better choice. > **Info:** It is currently not possible to control Roblox Studio's debugger from an external development environment. ## Set up Script Sync 1. In the **Explorer**, select one or more folders that you want to sync. 2. Right-click and select **Script Sync** ⟩ **Sync with Directory**.![The Explorer window with the Script Sync menu expanded.](../assets/studio/explorer/Script-Sync.png) 3. Choose a directory on your computer (or create a new one) and click **Save**. 4. In Studio, right-click on the folder again and select **Script Sync** ⟩ **Reveal in Explorer** (Windows) or **Reveal in Finder** (macOS). 5. Open the directory in your preferred text editor. Alternatively, right-click an individual script in Studio and select **Open in External Editor**. You can sync individual scripts, but we **highly** recommend syncing folders; if you sync a folder, Script Sync automatically syncs its child folders and scripts, which lets you create, delete, and rename scripts without having to enable syncing each time. After you set up Script Sync, you almost certainly want to [add an LSP extension](#set-up-a-language-server) to your text editor. > **Success:** You only have to set up syncing once. When you restart Studio and open your place, Script Sync remembers what you were syncing and resumes automatically. ## Key tips - Script Sync only syncs `Class.Script`, `Class.LocalScript`, `Class.ModuleScript` and `Class.Folder` instances. All other instances in a synced folder are ignored. Avoid syncing folders that contain scripts alongside other instances. - Don't sync scripts that have attributes or tags. Script Sync ignores these, which can lead to data loss if you create or delete scripts or start syncing with an older version of your files on disk. - Script Sync requires the names of scripts be compatible with your file system—no duplicate names or unsupported characters. If Studio encounters any of these issues, it throws an error and asks you to resolve the situation before resuming sync. - If any synced files or folders change while Studio is closed, Studio asks you how you want to resolve the situation when you reopen the place. - Deleting a top-level (or "root") instance—the instance you initially synced—on your local disk can cause Studio to throw an error. To delete a top-level instance: 1. In Studio, right-click the script or folder and select **Script Sync** ⟩ **Stop Sync**. 2. Choose the **Delete file(s)** or **Delete folder(s)** option to delete the instance from your local disk. 3. Delete the script or folder in Studio. You can delete child instances in Studio or on your local disk without disabling syncing, and the deletion syncs as you'd expect. ## Set up a language server Language servers give your text editor language-specific features like autocomplete, type checking, "go to definition," and more. For VS Code, we recommend [Luau Language Server](https://marketplace.visualstudio.com/items?itemName=JohnnyMorganz.luau-lsp). Language server protocol (LSP) extensions need to understand the data model hierarchy of a project to work properly. The VS Code extension comes with a companion Studio plugin ([Luau Language Server Companion](https://create.roblox.com/store/asset/10913122509/Luau-Language-Server-Companion)) that provides this data. **We highly recommend installing both.** If you use the VS Code extension and companion Studio plugin, language features will work correctly in VS Code while using Script Sync. ## Recommended extensions > **Info:** The community maintains these extensions. We recommend them based on their functionality, but can't offer guarantees about performance or security. Before installing an extension, check that it comes from a reputable source and isn't malicious. - [Roblox Luau LSP](https://marketplace.visualstudio.com/items?itemName=JohnnyMorganz.luau-lsp) and [Luau Language Server Companion](https://create.roblox.com/store/asset/10913122509/Luau-Language-Server-Companion) - Language features (see above) - [selene](https://marketplace.visualstudio.com/items?itemName=Kampfkarren.selene-vscode) - Static code analysis (linting) - [StyLua](https://marketplace.visualstudio.com/items?itemName=JohnnyMorganz.stylua) - Automatic code formatting ## Sync rules When syncing an instance with children with a directory on disk, Studio will automatically sync create, rename, move, and delete operations on children with the corresponding files and folders on disk. This approach lets you maintain your code without having to manually stop and start syncing on individual scripts. On disk, Script Sync uses the following rules to disambiguate the different types of scripts from each other. | Naming convention on disk | Instance type in Studio | | --- | --- | | `name.luau` | `Class.ModuleScript` | | `name.server.luau` | Script with `Server` `Class.Script.RunContext` | | `name.client.luau` | Script with `Client` `Class.Script.RunContext` | | `name.local.luau` | `Class.LocalScript` | | `name.legacy.luau` | Script with `Legacy` `Class.Script.RunContext` | | `name.plugin.luau` | Script with `Plugin` `Class.Script.RunContext` | | `name/` (directory) | `Class.Folder` | | `name/init.*.luau` | Script instance with children. Type (`*`) is determined by the above rules. | ## Settings Script Sync has several settings in the [Studio Settings](/docs/en-us/studio/setup.md#customization) window (`Alt``S` or ``⌥`S`). The default options are a good fit for most use cases. | Setting | Description | | --- | --- | | Auto resume sync on place open | Whether Studio should resume syncing when you open a place. Disabling this setting dramatically reduces the utility of Script Sync. | | Resume conflicted sync on place open | How Studio should handle conflicts when you open a place. The default option, **Do not resume**, forces you to resolve conflicts manually to avoid data loss. You can also choose to prefer the version on disk or the version in Studio. | | Keep local files/directories after sync | How to handle files on disk after you select **Script Sync** > **Stop Sync** in Studio. | | File extension | The file extension to use for scripts on disk. | --- title: "Dynamic effects" url: /docs/en-us/sound/dynamic-effects last_updated: 2026-06-29T19:34:11Z description: "Legacy effects that modify or enhance Sound objects in your experiences." --- # Dynamic effects > **Warning:** There is a newer set of audio effects that offer more control and address robust use cases. For more information, see [Audio effects](/docs/en-us/audio/effects.md). Dynamic effects modify or enhance the audio of individual `Class.Sound` objects or an entire `Class.SoundGroup`. You can apply these effects to make your audio more immersive within your experience, such as using `Class.ReverbSoundEffect` in large rooms to make them feel massive. ## Apply dynamic effects Each dynamic effect you apply has additional properties you can adjust until you achieve the type of sound you desire. For more information on these properties, see each dynamic effect's API page. To apply a dynamic effect: 1. In the **Explorer** window, insert a new dynamic effect into a `Class.Sound` or `Class.SoundGroup` object. 1. Hover over the object and click the ⊕ button. A contextual menu displays. 2. From the menu, insert a **dynamic effect**. 2. **(Optional)** In the **Properties** window, select the new dynamic effect and adjust its properties. ## Types ### Equalizer The `Class.EqualizerSoundEffect` allows for control of the volume of various frequency ranges. This dynamic effect is useful to highlight or minimize particular elements of audio, such as muffling all audio in your experience when a user goes underwater. | | | | --- | --- | | Audio without effect | Audio with equalizer | ### Compressor The `Class.CompressorSoundEffect` reduces the dynamic range of audio by lowering the volume of the highest parts of a source while at the same time raising the overall volume. This dynamic effect is useful to set a consistent volume for users to hear even if you record audio while whispering or speaking loudly. You can also use this dynamic effect to prioritize sounds from one `Class.SoundGroup` over another through the process of ducking. For more information, see [Ducking](/docs/en-us/sound/groups.md#ducking). | | | | --- | --- | | Audio without effect | Audio with compressor | ### Reverb The `Class.ReverbSoundEffect` simulates the effect of sounds bouncing off of several surfaces. This dynamic effect is useful to simulate more realistic reflections of sounds in interior spaces, such as bouncing a basketball inside a gymnasium, or playing rock music in a stadium. | | | | --- | --- | | Audio without effect | Audio with reverb | ### Chorus The `Class.ChorusSoundEffect` simulates the effect of multiple vocals or instruments playing simultaneously by taking the original sound and overlaying copies, each with slight variations in pitch. This dynamic effect is useful for simulating a robotic or futuristic quality to your audio. | | | | --- | --- | | Audio without effect | Audio with chorus | ### Distortion The `Class.DistortionSoundEffect` simulates the effect that would occur when overdriving older style audio equipment. This dynamic effect causes clipping in the sound and adds a general "fuzziness", which is useful for either adding intensity and character to musical instruments. | | | | --- | --- | | Audio without effect | Audio with distortion | ### Echo The `Class.EchoSoundEffect` causes a sound to repeat on a delay with diminishing volume, simulating the real effect of an echo that bounces off of hard surfaces, such as walls and caves. | | | | --- | --- | | Audio without effect | Audio with echo | ### Flange The `Class.FlangeSoundEffect` creates a sweeping or whooshing effect by copying the original audio signal and playing it slightly offset and modulated on top of the original. This dynamic effect is useful for adding strange, otherworldly digital quality to your audio. For example, you can use it on an air conditioner recording to make a sci-fi spaceship engine rumble. | | | | --- | --- | | Audio without effect | Audio with flange | ### Pitch Shift The `Class.PitchShiftSoundEffect` raises or lowers pitch without changing the playback speed. This dynamic effect is useful for changing the scale of your sounds. You can use it to make small sounds feel huge by pitching them down or conversely pitch sounds up to make an explosion sound more like a balloon pop. | | | | --- | --- | | Audio without effect | Audio with pitch shift | ### Tremolo The `Class.TremoloSoundEffect` creates a trembling effect on a sound by varying its volume up and down. This dynamic effect is useful to transform instrument sounds into a wavy, dreamlike quality, or to create swells and dips in weather-related audio. | | | | --- | --- | | Audio without effect | Audio with tremolo | --- title: "Sound groups" url: /docs/en-us/sound/groups last_updated: 2026-06-29T19:34:11Z description: "Legacy audio mixers that group multiple Sound objects so you can control the properties of multiple audio signals at once." --- # Sound groups > **Warning:**`Class.SoundGroup` objects are now discouraged in favor of the more robust functionality of audio objects. For more information, see [Audio objects](/docs/en-us/audio/objects.md). A `Class.SoundGroup` is an **audio mixer** that groups multiple audio objects, such as `Class.Sound` objects or additional `Class.SoundGroup|SoundGroups`, allowing you to control the volume and dynamic effects properties of multiple audio signals at once. Useful applications include: - [Assigning audio](#assign-audio-objects-to-sound-groups) to **SoundEffects** and **BackgroundMusic** sound groups so that you can adjust each group's master volume for optimal audio balancing. - [Nesting sound groups](#nest-sound-groups) into meaningful categories under a mix tree. - Grouping all sounds that need a specific [dynamic effect](/docs/en-us/sound/dynamic-effects.md). For example, you can group all sounds inside a cave to a **Cave** sound group, then apply a `Class.ReverbSoundEffect` to simulate the sounds reflecting off of the cave's environment. When creating `Class.SoundGroup|SoundGroups`, it's best to keep them all in a single location for organizational purposes as you continue to add and edit audio within your experience. The following example stores the new `Class.SoundGroup` under `Class.SoundService`, as this service determines how `Class.Sound` objects play in experiences. ## Create sound groups To create a `Class.SoundGroup`: 1. In the **Explorer** window, hover over `Class.SoundService` and click the ⊕ button. A contextual menu displays. 2. From the menu, insert a `Class.SoundGroup`. 3. Triple-click the new sound group and rename it according to its purpose, such as **SoundEffects** or **BackgroundMusic**. ## Assign audio objects to sound groups `Class.SoundGroup|SoundGroups` don't have the typical parent-child behavior of other forms of object grouping like `Class.Model|Models` and `Class.Folder|Folders`. Instead, you must **assign** each `Class.Sound` object to its applicable `Class.SoundGroup` object, regardless of either object's location in the workspace hierarchy, because where you place the `Class.Sound` object changes where audio emits from, not the `Class.SoundGroup` object itself. For more information, see [sound objects](/docs/en-us/sound/objects.md). To assign a `Class.Sound` object to a `Class.SoundGroup`: 1. In the **Explorer** window, select a sound object. 2. In the **Properties** window, click the **SoundGroup** property field. Your cursor changes. 3. In the **Explorer** window, click on the sound group object you want to assign your sound object to. The sound object's **SoundGroup** property updates accordingly. ## Nest sound groups You can nest `Class.SoundGroup|SoundGroups` together into meaningful categories under a mix tree for organization and scripting purposes. When you're planning the parent-child relationships in your mix, consider the different sound categories in your experience, and how important they should be for the listener. For example, player ability sounds are likely much more important to gameplay than environmental sounds. If you group them into separate parent `Class.SoundGroup|SoundGroups`, you can easily access and [adjust their volume levels](#adjust-volume) as you add more `Class.Sound` objects to your experience. To nest `Class.SoundGroup|SoundGroups`: 1. In the **Explorer** window, click-and-drag a sound group over the sound group you want to nest it under. The sound group name becomes translucent, and your cursor changes to a plus icon. 2. Drop the sound group. It displays as a child of the sound group you nested it under. ## Adjust volume There are two main ways to think about a sound's volume: how loud the sound is by itself, and how loud it is in relation to other sounds. For example, a waterfall sounds loud when you play it by itself, but when you compare it to other sound effects like tires screeching, it likely sounds much quieter. To ensure that each sound plays at the correct volume for your experience, you can either [add multipliers](#add-multipliers) to `Class.SoundGroup|SoundGroups`, or prioritize audio from one `Class.SoundGroup` over another through the process of [ducking](#ducking). ### Add multipliers The `Class.SoundGroup.Volume|Volume` property of a `Class.SoundGroup` lets you apply a **volume multiplier** between `0` and `10` to each of its child audio objects while they retain their relative volumes. This means that if a `Class.Sound` object has a volume of `0.5` and you child it to a `Class.SoundGroup` that has a volume multiplier of `0.5`, the effective volume of the `Class.Sound` object is `0.25`. This property is useful when you want to test volume changes to different `Class.SoundGroup|SoundGroups` without having to manually change the volume of each `Class.Sound` object. For example, if you want to check what it'd sound like to increase the volume of all of your experience's music, you can create a **Music** `Class.SoundGroup` for every musical `Class.Sound` object or `Class.SoundGroup`, then apply a volume multiplier of `2`. ### Ducking You can prioritize audio from one `Class.SoundGroup` over another through the `Class.CompressorSoundEffect`. This dynamic effect allows you to **duck**, or reduce in volume, `Class.Sound` objects in low-priority `Class.SoundGroup|SoundGroups` whenever `Class.Sound` objects in high-priority `Class.SoundGroup|SoundGroups` start to play. This allows the listener to concentrate on the specific audio that you want them to pay attention to while not suddenly cutting the audio from low-priority sounds completely. When the high-priority audio finishes playing, low-priority audio returns to its original volume, keeping the listener immersed in your experience. The sounds that you choose to duck depend on the needs of your specific experience. For example, one experience might benefit from reducing the volume of background music whenever a GUI notification plays, while others might benefit from reducing the volume of GUI notifications whenever dialogue plays. The following reference mix tree prioritizes `Class.Sound` objects in the high-priority **GUI Notifications** and **Weapons** `Class.SoundGroup|SoundGroups` while de-prioritizing `Class.Sound` objects in low-priority **3D** and **2D Ambience** `Class.SoundGroup|SoundGroups`. The `Class.CompressorSoundEffect` has four main properties you must set in order to duck audio whenever your high priority audio plays: | Property | Description | | --- | --- | | `Class.CompressorSoundEffect.Threshold\|Threshold` | The threshold decibel level that Studio applies ducking. You can set this property to any value between `-80` (full compression) and `0` (full volume). | | `Class.CompressorSoundEffect.Attack\|Attack` | The rate you want audio to duck. You can set this property to any value between `0.001` and `1` second. | | `Class.CompressorSoundEffect.Release\|Release` | The rate you want the audio to stop ducking. You can set this property to any value between `0.001` and `1` second. | | `Class.CompressorSoundEffect.Ratio\|Ratio` | The ratio of how much ducking you want to occur between the audio object that Studio prioritizes and the audio object that ducks. You can set this property to any value between `1` (no compression) and `50` (fifty times the compression). | The values of each of these properties are highly dependent on your specific audio. Each source audio has a different volume, and you might only need to duck by a minimal amount in order for your high priority audio to have emphasis in your experience. To prioritize `Class.SoundGroup|SoundGroups` through the `Class.CompressorSoundEffect`: 1. In the **Explorer** window, navigate to the SoundGroup you want to duck and insert a CompressorSoundEffect. 1. Hover over the **SoundGroup** and click the ⊕ button. A contextual menu displays. 2. From the menu, insert a **CompressorSoundEffect**. 2. Select the **CompressorSoundEffect**, then navigate to the **Properties** window. 3. Select the **SideChain** property. Your cursor changes. 4. Select the **Sound** or **SoundGroup** object that you want to prioritize when the compressor applies. The **SideChain** property updates accordingly. 5. In the **Threshold property** field, input the decibel level in which you want the ducking to start. 6. In the **Attack** property field, input how quickly you want audio to duck. 7. In the **Release** property field, input how quickly you want the audio to stop ducking. 8. In the **Ratio** property field, input the ratio of how much compression you want to occur between the audio object that ducks and the audio object that Studio prioritizes. 9. **(Optional)** Play test your experience to see if the audio sounds correct. If not, adjust the **CompressorSoundEffect** properties accordingly. --- title: "Sound" url: /docs/en-us/sound last_updated: 2026-06-29T19:34:11Z description: "An overview of legacy Sound objects and effects." --- # Sound > **Warning:** There is a newer set of [audio objects](/docs/en-us/audio/objects.md) and [effects](/docs/en-us/audio/effects.md) that offer more control and address robust use cases. For more information, see [Audio](/docs/en-us/audio.md). Sound effects and music enhance your experiences and make them more immersive. You can import your own [audio assets](/docs/en-us/audio/assets.md) or search for free-to-use audio in the [Creator Store](/docs/en-us/production/creator-store.md), play audio through `Class.Sound` or objects, and enhance audio playback through [dynamic effects](#dynamic-effects). ## Sound objects A [sound object](/docs/en-us/sound/objects.md) emits audio within an experience. Roblox assigns each [audio asset](/docs/en-us/audio/assets.md) a unique ID that you can assign to `Class.Sound` objects to play a specific sound effect or music track. You can either set this audio to play automatically at runtime, or trigger it to play from scripts. The location of where you place a `Class.Sound` object in the [Explorer](/docs/en-us/studio/explorer.md) hierarchy affects how users hear audio. If you want users to only hear audio near a specific position, you must parent the `Class.Sound` object to a 3D object or `Class.Attachment` to behave as positional audio. If you want users to hear audio regardless of their position, you must insert the `Class.Sound` object directly into `Class.Workspace` or `Class.SoundService` to behave as background audio. ## Sound groups A [sound group](/docs/en-us/sound/groups.md) acts as an audio mixer for multiple audio objects, such as `Class.Sound` objects or additional `Class.SoundGroup|SoundGroups`, allowing you to control the volume and dynamic effects properties of multiple audio signals at once. ## Dynamic effects [Dynamic effects](/docs/en-us/sound/dynamic-effects.md) modify or enhance the audio of individual `Class.Sound` objects or an entire `Class.SoundGroup`. You can apply these effects to make audio more immersive within the experience, such as using `Class.EqualizerSoundEffect` to make rain sound muffled, `Class.CompressorSoundEffect` to control a sound's maximum volume, or `Class.ReverbSoundEffect` to add more realistic reflections of sound in interior spaces. --- title: "Sound objects" url: /docs/en-us/sound/objects last_updated: 2026-06-29T19:34:11Z description: "Sound objects are legacy instances that emit sound within an experience." --- # Sound objects > **Warning:** There is a newer set of [audio objects](/docs/en-us/audio/objects.md) that offer more control and address robust use cases. For more information, see [Audio](/docs/en-us/audio.md). Audio playback occurs through `Class.Sound` objects which emit audio within an experience. Roblox assigns each [audio asset](/docs/en-us/audio/assets.md) a unique ID that you can assign to `Class.Sound` objects to play a specific sound effect or music track. You can either set this audio to play automatically at runtime, or trigger it to [play from scripts](#script-sound-objects). To modify playback of multiple `Class.Sound` objects, you can assign them to a [sound group](/docs/en-us/sound/groups.md) and control the entire group's volume, as well as apply [dynamic effects](/docs/en-us/sound/dynamic-effects.md). ## Create sound objects There are three locations you can create a `Class.Sound` object, and each location determines how audio emits and volume changes in relation to the user's position within the experience. | Location | How audio emits | How volume changes | | --- | --- | --- | | Child of a block, sphere, or cylinder `Class.BasePart`. | Audio emits outward from the entire surface of the part. | Volume changes depending on the distance between the user's sound listener and the position of the part, as well as its size. | | Child of an `Class.Attachment`, `Class.MeshPart`, `Class.TrussPart`, `Class.WedgePart`, or `Class.CornerWedgePart`. | Audio emits outward from the single attachment point or part center. | Volume changes depending on the distance between the user's sound listener and the attachment/part position. | | Within `Class.SoundService` or `Class.Workspace`. | Audio emits throughout the experience. | Volume and pan position remain the same regardless of the user's sound listener position or rotation. | ### Positional audio Positional audio is audio that users can only hear near a specific position within the experience. There are two types of positional audio you can utilize: volumetric and point source. #### Volumetric Volumetric audio is the most realistic audio option, as it dynamically changes depending on the user's position to the audio source. For example, if the user is within a `Class.BasePart` with a child `Class.Sound` object, audio plays all around the user, similar to music in headphones playing at the same volume in each speaker. When the user exits the part, audio gradually decreases in volume and becomes more directional per speaker, moving around the user's head when their listener rotates. For information on the properties that control how loud your audio is at different distances from the part, see [RollOffMinDistance and RollOffMaxDistance](#rolloffmindistance-and-rolloffmaxdistance). The size of the `Class.BasePart` also affects the audio's volume, as larger `Class.BasePart|BaseParts` produce a wider area in which users can hear the audio and when a user navigates away from a large `Class.BasePart`, volume decreases slower. The following example displays a `Class.BasePart` that is the size of a city with a `Class.Sound` object playing ambient audio. Users A and B experience the audio differently: user B is closer to the large `Class.BasePart`, and they mostly hear the audio around the same loudness in both speakers. Conversely, user A is further away from the city, so the audio they hear is quieter and more directional than user B, playing at differing loudness in each of their speakers. Volumetric audio is useful for any audio that needs to be immersive around a user and change dynamically depending on the user's position, such as a concert stage or ambience zones like rainfall. When you experiment with placing a `Class.Sound` object on `Class.BasePart|BaseParts` of different sizes and positions from users, you can hear how immersive volumetric audio can be. To create a `Class.Sound` object for volumetric audio: 1. In the **Explorer** window, select **SoundService**. 2. In the **Properties** window, navigate to the **VolumetricAudio** property, then set it to **Enabled**. 3. In the **Explorer** window, hover over a `Class.BasePart`, then click the ⊕ button. A contextual menu displays. 4. From the menu, insert a **Sound**. 5. In the **Properties** window, navigate to the **SoundId** property and input a valid [audio asset ID](/docs/en-us/audio/assets.md). 6. **(Optional)** If you want the audio to start playing when the experience begins, enable the **Playing** property. #### Point source Contrary to volumetric audio, point source audio only emits from a single point source. This type of audio is useful for explosions, impact noises, electronic devices, and dialogue. To create a `Class.Sound` object for point source audio: 1. In the **Explorer** window, hover over an attachment, truss, wedge, or corner wedge, then click the ⊕ button. A contextual menu displays. 2. From the menu, insert a **Sound**. 3. In the **Properties** window, navigate to the **SoundId** property and input a valid [audio asset ID](/docs/en-us/audio/assets.md). 4. **(Optional)** If you want the audio to start playing when the experience begins, enable the **Playing** property. ### Background audio Background audio plays at the same volume no matter where the user travels within your experience. This type of audio is useful for music that you want to play for users, especially when you want to create a soundtrack of multiple audio files. It's best to keep all `Class.Sound` objects for background audio in a single location of the Workspace for organization purposes as you continue to add and edit audio within your experience. The following example stores the new `Class.Sound` object under the `Class.SoundService` container, as this service determines how `Class.Sound` objects play in experiences. To create a `Class.Sound` object for background audio: 1. In the **Explorer** window, hover over **SoundService**, then click the ⊕ button. A contextual menu displays. 2. From the menu, insert a **Sound**. 3. In the **Properties** window, navigate to the **SoundId** property and input a valid [audio asset ID](/docs/en-us/audio/assets.md). 4. **(Optional)** If you want the audio to start playing when the experience begins, enable the **Playing** property. 5. **(Optional)** If this `Class.Sound` object is the only track you want to play in the place, enable its **Looped** property. ## Customize sound objects `Class.Sound` object properties work together to influence how users experience your audio, such as: - How loud they hear specific audio individually and in relation to one another ([Volume](#volume)). - How they perceive volume when they move away from the audio source ([RollOffMinDistance](#rolloffmindistance-and-rolloffmaxdistance), [RollOffMaxDistance](#rolloffmindistance-and-rolloffmaxdistance), and [RollOffMode](#rolloffmode)) - What part of the audio they can listen to ([TimePosition](#timeposition)). - How fast and at what pitch they hear it ([PlaybackSpeed](#playbackspeed)). - If the audio automatically replays upon completion ([Looped](#looped)). ### Volume The `Class.Sound.Volume|Volume` property allows you to set the volume of your audio anywhere from `0` (silence) to `10` (booming). If you want to change the volume of multiple `Class.Sound` objects at once, parent them under a [sound group](/docs/en-us/sound/groups.md). > **Warning:** If your source audio is sufficiently loud, you likely don't need to increase the volume to more than `2`, as this property is multiplicative. For example: a `Class.Sound.Volume|Volume` value of `4` is four times as loud than a `Class.Sound.Volume|Volume` value of `1`, so if your source audio was already loud, the audio might become overwhelming to users. ### RollOffMinDistance and RollOffMaxDistance `Class.Sound.RollOffMinDistance|RollOffMinDistance` and `Class.Sound.RollOffMaxDistance|RollOffMaxDistance` determine the range of how a user perceives volume from positional audio. For volumetric audio, `Class.Sound.RollOffMinDistance|RollOffMinDistance` is the minimum distance in studs that audio begins to decrease in volume when the client's listener **moves away** from the parent `Class.BasePart`, and `Class.Sound.RollOffMaxDistance|RollOffMaxDistance` property is the maximum distance in studs that a client's listener can hear audio from the surface of the object. For point source audio, `Class.Sound.RollOffMinDistance|RollOffMinDistance` and `Class.Sound.RollOffMaxDistance|RollOffMaxDistance` work similarly to volumetric audio, but the roll off distance these properties control doesn't surround the object, it only influences the distance outward from the point source. ### RollOffMode The `Class.Sound.RollOffMode|RollOffMode` property allows you to decide how audio fades as the distance between the user's listener and the `Class.Sound` object's parent increases. You can set this property to one of four values of the `Enum.RollOffMode` enum. | RollOffMode | Description | | --- | --- | | `Inverse` (default) | Volume initially fades abruptly from `Class.Sound.RollOffMinDistance\|RollOffMinDistance`, but volume fading becomes more gradual the closer a user is to reaching the `Class.Sound.RollOffMaxDistance\|RollOffMaxDistance`. Once they reach the `Class.Sound.RollOffMaxDistance\|RollOffMaxDistance`, the audio falls silent. | | `Linear` | Volume fades linearly between `Class.Sound.RollOffMinDistance\|RollOffMinDistance` and `Class.Sound.RollOffMaxDistance\|RollOffMaxDistance`. Once a user reaches the `Class.Sound.RollOffMaxDistance\|RollOffMaxDistance`, the audio falls silent. | | `InverseTapered` | Volume follows the inverse model when close to `Class.Sound.RollOffMinDistance\|RollOffMinDistance` and the linear square model when a user is close to `Class.Sound.RollOffMaxDistance\|RollOffMaxDistance`. Audio approaches silence at the maximum distance point. | | `LinearSquare` | Volume fades between `Class.Sound.RollOffMinDistance\|RollOffMinDistance` and `Class.Sound.RollOffMaxDistance\|RollOffMaxDistance` with a linear squared relationship. Audio approaches silence at the maximum distance point. | > **Warning:** For most `Inverse` and `Linear` `Class.Sound.RollOffMode|RollOffMode` types, low `Class.Sound.RollOffMaxDistance|RollOffMaxDistance` values cause audio to abruptly cut off when the listener reaches the `Class.Sound.RollOffMaxDistance|RollOffMaxDistance`. If you want your audio to gradually decrease in volume, set a higher `Class.Sound.RollOffMaxDistance|RollOffMaxDistance` value. ### PlaybackSpeed The `Class.Sound.PlaybackSpeed|PlaybackSpeed` property allows you to determine the speed your audio plays. For example, if you set the `Class.Sound.PlaybackSpeed|PlaybackSpeed` to a value of **2**, your audio plays twice as fast and one octave higher in pitch. Similarly, if you set it to a value of **0.5**, your audio plays twice as slow and one octave lower in pitch. | | | | | --- | --- | --- | | `Class.Sound.PlaybackSpeed\|PlaybackSpeed` = 0.5 | `Class.Sound.PlaybackSpeed\|PlaybackSpeed` = 1.0 | `Class.Sound.PlaybackSpeed\|PlaybackSpeed` = 2.0 | ### TimePosition The `Class.Sound.TimePosition|TimePosition` property displays, in seconds, what position within the audio sample a user is currently hearing. This property is useful for either only playing a section of the audio sample, or triggering an event to occur once audio reaches a specific position. For example, the following code sample causes a particle emitter to emit a white ring particle above a part within a limited time range of an audio track. ```lua local RunService = game:GetService("RunService") local Workspace = game:GetService("Workspace") -- Create a new part local part = Instance.new("Part") part.Anchored = true part.Color = Color3.new(0.75, 0.2, 0.5) part.Size = Vector3.new(2, 1, 2) part.Material = Enum.Material.Neon part.Position = Vector3.new(0, 4, 0) part.Parent = Workspace -- Create an attachment on the part local attachment = Instance.new("Attachment") attachment.Position = Vector3.new(0, 0.5, 0) attachment.Parent = part -- Create a particle emitter on the attachment local emitter = Instance.new("ParticleEmitter") emitter.Rate = 5 emitter.Lifetime = NumberRange.new(1.5, 1.5) emitter.Texture = "rbxassetid://1266170131" emitter.Speed = NumberRange.new(1, 1) emitter.Size = NumberSequence.new{NumberSequenceKeypoint.new(0, 1), NumberSequenceKeypoint.new(1, 6)} emitter.Transparency = NumberSequence.new{NumberSequenceKeypoint.new(0, 1), NumberSequenceKeypoint.new(0.25, 0), NumberSequenceKeypoint.new(1, 1)} emitter.Orientation = Enum.ParticleOrientation.VelocityPerpendicular emitter.Parent = attachment -- Create a sound on the attachment local sound = Instance.new("Sound") sound.SoundId = "rbxassetid://1835405646" sound.Parent = attachment -- Play the sound sound:Play() -- Start checking if emitter should be enabled RunService.Heartbeat:Connect(function() -- Enable the emitter within a time range of the audio; otherwise disable it emitter.Enabled = sound.TimePosition >= 5 and sound.TimePosition < 20 end) ``` ### Looped The `Class.Sound.Looped|Looped` property allows you to repeat audio after it has finished playing. When set to `true`, the `Class.Sound` object's audio plays again. This is useful to apply to [background audio](#background-audio) to ensure your experience never has abrupt silence. ## Script sound objects ### Play audio contextually Aside from auto-playing audio through the `Class.Sound` object's `Class.Sound.Playing|Playing` property, you can play audio contextually from a `Class.LocalScript` by calling `Class.Sound:Play()|Play()` on the corresponding `Class.Sound` object. For example: ```lua local Workspace = game:GetService("Workspace") local sound = Instance.new("Sound") sound.SoundId = "rbxassetid://9120386436" sound.Looped = true sound.Parent = Workspace sound:Play() ``` Alternatively, if the place will feature multiple tracks, you can play a specific track from a `Class.LocalScript`: ```lua local SoundService = game:GetService("SoundService") local musicTrack = SoundService:FindFirstChild("LucidDream") if musicTrack not musicTrack.IsPlaying then musicTrack:Play() end ``` ### Play interface audio You can play interface audio for `Class.GuiObject|GuiObjects` such as [buttons](/docs/en-us/ui/buttons.md) by hooking up a `Class.Sound` object to the `Class.GuiButton.Activated|Activated` event listener. This lets you provide auditory feedback to users, such as when they hover over or press it. To play a `Class.Sound` object's audio when a user activates a `Class.TextButton` or `Class.ImageButton`: 1. In the [Explorer](/docs/en-us/studio/explorer.md) window, hover over the `Class.TextButton` or `Class.ImageButton`, then click the ⊕ button. A contextual menu displays. 2. From the menu, insert either a `Class.LocalScript` or a `Class.Script` with `Class.BaseScript.RunContext|RunContext` set to `Enum.RunContext.Client`. 3. Paste the following code into the script, then replace `SOUND_NAME` with the name of a `Class.Sound` object located inside the `Class.SoundService` container. ```lua local SoundService = game:GetService("SoundService") local button = script.Parent local sound = SoundService:FindFirstChild("SOUND_NAME") if not sound.isLoaded then sound.Loaded:Wait() end local function onButtonActivated() if sound and not sound.IsPlaying then sound:Play() end end button.Activated:Connect(onButtonActivated) ``` --- title: "Align tool" url: /docs/en-us/studio/align-tool last_updated: 2026-06-29T19:34:11Z description: "The Align tool lets you align objects or groups of objects along the X, Y, or Z axes." --- # Align tool The **Align** tool, accessible from Studio's **Model** tab toolbar or the **Window** ⟩ **3D** menu, lets you align objects or groups of objects along the **X**, **Y**, or **Z** axes. ![Align tool highlighted in Studio's toolbar.](../assets/studio/general/Toolbar-Align.png) Primary benefits include: - Aligning the center or edge of multiple objects in one action. - Aligning entire `Class.Model|Models` or parts within them relative to other parts, all while keeping the model intact. - Dynamically previewing the point of alignment before confirming. ## Mode You can set the alignment mode to either **Min**, **Center**, or **Max**. ![The Min icon in the toolbar.](../assets/studio/align-tool/Icon-Min.png) _Min_ ![The Center icon in the toolbar.](../assets/studio/align-tool/Icon-Center.png) _Center_ ![The Max icon in the toolbar.](../assets/studio/align-tool/Icon-Max.png) _Max_ #### Min ![An angled side view of a small red cube, large blue cube, and a small purple cube that are aligned with the Min button on the world Z axis. A translucent orange rectangle represents where the objects align in context to each other.](../assets/studio/align-tool/Mode-Min.jpg) #### Center ![An angled side view of a small red cube, large blue cube, and a small purple cube that are aligned with the Center button on the world Z axis. A translucent orange rectangle represents where the objects align in context to each other.](../assets/studio/align-tool/Mode-Center.jpg) #### Max ![An angled side view of a small red cube, large blue cube, and a small purple cube that are aligned with the Max button on the world Z axis. A translucent orange rectangle represents where the objects align in context to each other.](../assets/studio/align-tool/Mode-Max.jpg) ## Align In The **World** or **Local** option specifies which relative coordinates to align on. This is especially useful for aligning parts which don't share the same orientation or rotation. The **X**, **Y**, and **Z** checkboxes let you select which axes to align on. Note that you can select multiple axes in a single alignment operation. #### Before Alignment ![An angled side view of a small red cube, large blue cube, and a small purple cube that are unaligned.](../assets/studio/align-tool/Align-In-Before.jpg) #### Max / World Z ![An angled side view of a small red cube, large blue cube, and a small purple cube that are aligned with the Max button on the world Z axis. A translucent orange rectangle represents where the objects align in context to each other.](../assets/studio/align-tool/Align-In-World-Z.jpg) #### Max / Local Z ![An angled side view of a small red cube, large blue cube, and a small purple cube that are aligned with the Max button on their local Z axis. A translucent orange rectangle represents where the objects align in context to each other.](../assets/studio/align-tool/Align-In-Local-Z.jpg) ## Relative To An alignment operation can be relative to either the **Selection Bounds** or the **Active Object**. ### Selection Bounds **Selection Bounds** aligns the selected objects relative to the bounding box around them. #### Before Alignment ![An angled side view of a small red cube, large blue cube, and a small purple cube that are unaligned. A translucent red rectangle represents where the objects' collective bounding box.](../assets/studio/align-tool/Relative-To-Selection-Bounds-Before.jpg) #### Min / World X ![An angled side view of a small red cube, large blue cube, and a small purple cube that are aligned with the Min button on the world X axis relative to their collective bounding box. A translucent red rectangle represents where the objects' collective bounding box.](../assets/studio/align-tool/Relative-To-Selection-Bounds-Min-X.jpg) #### Max / World X ![An angled side view of a small red cube, large blue cube, and a small purple cube that are aligned with the Max button on the world X axis relative to their collective bounding box. A translucent red rectangle represents where the objects' collective bounding box.](../assets/studio/align-tool/Relative-To-Selection-Bounds-Max-X.jpg) ### Active Object **Active Object** aligns the objects relative to the **last selected object**. This object is outlined in orange and it will not move during the operation. While parts are selected, you can switch the active object by clicking on a different object. #### Before Alignment ![An angled side view of a small red cube, large blue cube, and a small purple cube that are unaligned. The large blue cube has a yellow outline to represent that it's the active object.](../assets/studio/align-tool/Relative-To-Active-Object-Before.jpg) #### Min / World X ![An angled side view of a small red cube, large blue cube, and a small purple cube that are aligned with the Min button on the world X axis relative to the active object. The large blue cube has a yellow outline to represent that it's the active object.](../assets/studio/align-tool/Relative-To-Active-Object-Min-X.jpg) #### Max / World X ![An angled side view of a small red cube, large blue cube, and a small purple cube that are aligned with the Max button on the world X axis relative to the active object. The large blue cube has a yellow outline to represent that it's the active object.](../assets/studio/align-tool/Relative-To-Active-Object-Max-X.jpg) --- title: "Avatar Settings" url: /docs/en-us/studio/avatar-settings last_updated: 2026-06-29T19:34:11Z description: "The Avatar Settings window contains all Studio-level settings and customization options for an experience." --- # Avatar Settings The **Avatar Settings** window, accessible from Studio's **File** menu or the **Avatar** tab, allows you to control the look and behavior of user avatars across your entire experience. ![Avatar Settings indicated in Studio's toolbar](../assets/studio/general/Toolbar-Avatar-Settings.png)![Avatar Settings window layout](../assets/studio/general/Avatar-Settings-Window.png) > **Warning:****Avatar Settings** modifies underlying experience defaults that are not visible outside of the settings interface or accessible with scripts. This can lead to difficult-to-debug behaviors, especially when working across teams or on a universe with multiple place files. Test your changes by using the window's preview and by playtesting in Studio so that you fully understand the impact on player avatars before publishing changes to your live experience. The top bar of the **Avatar Settings** window provides easy access to quickly set the avatar type, assign a preset, and enable preview. | Setting | Description | | --- | --- | | **Preset** | Displays which settings preset is enabled and allows you to toggle between them (for descriptions of each setting, see [General](#general)). If the preset displays `--`, this indicates that the current settings are customized and do not match a preset. | | **Preview** | Toggles a preview lineup of avatars based off the avatar settings applied and creates an `AvatarPreview` folder under `Class.Workspace` ⟩ `Class.Camera` with four representative avatar types. You can add or remove avatars to the lineup by dragging them into or out of the folder. The folder is removed during Studio runtime playtesting and it is not saved to your place file. | | **⋯** | Sets the default avatar type to either **R6**, **R15**, or **R15 & R6**. | ## General The **General** tab offers simple presets tailored to the needs of most experiences. In many cases, one of these presets will fit your experience's avatar use-cases and additional configuration is not necessary. However, advanced developers can further customize avatar behavior using the more detailed settings available in the other tabs. | Setting | Description | | --- | --- | | **Player Choice** | Loads player avatars without any modifications. | | **Consistent Gameplay** | Sets all avatars to the same height with the same box collider for consistent collision behavior. | ## Body The **Body** tab contains settings for body proportions, parts, scale. | Setting | Description | | --- | --- | | **Scale** | For reference, classic style avatars are around 5 studs tall. More humanoid style proportions are around 6 to 6.5 studs. | | **Appearance** | | | **Build** | | ## Clothing The **Clothing** tab contains controls for layered and classic clothing. | Setting | Description | | --- | --- | | **Clothing Scale** | | | **Custom Clothing** | | ## Accessories The **Accessories** tab contains settings related to accessories in your experience. | Setting | Description | | --- | --- | | **Accessory Scale** | | | **Custom Accessories** | | | **Accessory Behaviors** | Controls to enable/disable accessory behaviors like **VFX** and **Sound**. | ## Movement The **Movement** tab contains collision and animation settings, as well as ability configurations for the [Character Controller Library](/docs/en-us/characters/character-controller-library.md). | Setting | Description | | --- | --- | | **Collision** | | | **Animation Packs** | | | **Animation Clips** | | | **Abilities** | | --- title: "Studio widgets" url: /docs/en-us/studio/build-studio-widgets last_updated: 2026-06-29T19:34:11Z description: "Building custom widgets in Studio allows you to customize workflows and views." --- # Studio widgets Studio gives you the power to create custom **widgets** and use them as tools and extensions. These widgets behave as custom windows/panels in Studio, and you can dock them inside of your interface or have them float as separate windows. ## Create widget UIs All Studio widgets begin as `Class.DockWidgetPluginGui` objects which you can fill with `Class.GuiObject|GuiObjects`, such as text labels and buttons. To create an empty widget GUI, call the `Class.Plugin:CreateDockWidgetPluginGui()|CreateDockWidgetPluginGui()` function, passing in an ID and a `Datatype.DockWidgetPluginGuiInfo` object. Note the `Datatype.DockWidgetPluginGuiInfo.new()` constructor expects its parameters in a **specific order** as follows: | # | Property | Type | Description | | --- | --- | --- | --- | | 1 | `Enum.InitialDockState` | Enum | One of the `Enum.InitialDockState` enumerations. | | 2 | `InitialEnabled` | Boolean | The initial enabled (visible) state of the widget GUI. | | 3 | `InitialEnabledShouldOverrideRestore` | Boolean | If true, the value of **InitialEnabled** overrides the previously saved enabled state. | | 4 | `FloatingXSize` | Integer | The initial width of the GUI when **InitialDockState** is set to `Enum.InitialDockState.Float`. | | 5 | `FloatingYSize` | Integer | The initial height of the GUI when **InitialDockState** is set to `Enum.InitialDockState.Float`. | | 6 | `MinWidth` | Integer | The minimum width of the GUI, with some platform-specific variations. | | 7 | `MinHeight` | Integer | The minimum height of the GUI, with some platform-specific variations. | ```lua -- Create new "DockWidgetPluginGuiInfo" object local widgetInfo = DockWidgetPluginGuiInfo.new( Enum.InitialDockState.Float, -- Widget will be initialized in floating panel true, -- Widget will be initially enabled false, -- Don't override the previous enabled state 200, -- Default width of the floating window 300, -- Default height of the floating window 150, -- Minimum width of the floating window 150 -- Minimum height of the floating window ) -- Create new widget GUI local testWidget = plugin:CreateDockWidgetPluginGui("TestWidget", widgetInfo) testWidget.Title = "Test Widget" -- Optional widget title ``` > **Warning:** You cannot use the `Class.Plugin:CreateDockWidgetPluginGui()|CreateDockWidgetPluginGui()` function in scripts. You can only call it from the command bar or a [plugin](/docs/en-us/studio/plugins.md) script. ### Customize widget UI Once you create a widget, you can customize its user interface with `Class.GuiObject|GuiObjects` such as informative `Class.TextLabel|TextLabels` or interactive `Class.ImageButton|ImageButtons`. For example, the following code adds a basic `Class.TextButton` to the GUI window: ```lua -- Create new widget GUI local testWidget = plugin:CreateDockWidgetPluginGui("TestWidget", widgetInfo) testWidget.Title = "Test Widget" -- Optional widget title local testButton = Instance.new("TextButton") testButton.BorderSizePixel = 0 testButton.TextSize = 20 testButton.TextColor3 = Color3.new(1,0.2,0.4) testButton.AnchorPoint = Vector2.new(0.5,0.5) testButton.Size = UDim2.new(1,0,1,0) testButton.Position = UDim2.new(0.5,0,0.5,0) testButton.SizeConstraint = Enum.SizeConstraint.RelativeYY testButton.Text = "Click Me" testButton.Parent = testWidget ``` > **Info:** Note that **you must parent the button to the widget** in the same way that you must parent any in-experience screen `Class.GuiObject` to a `Class.ScreenGui`. > **Warning:** To help you build consistent, effective Studio widgets, Roblox provides a [GitHub repo](https://github.com/Roblox/StudioWidgets) that contains GUI elements with a standard "Studio" look and feel. These include checkboxes, radio buttons, and input fields which have a Studio style, and they all support dark theme. ### Change Studio color themes Effective Studio widgets ideally match the Studio **theme** setting and dynamically adjust when the theme changes. For instance, if a developer is using the dark theme, the widget's background color, images, and text labels should look nice alongside Studio's native theme colors. The following code addition uses a `syncGuiColors()` function which is initially called along with a table of GUI objects to sync. Inside the function, a nested `setColors()` function loops through the objects and syncs specific aspects of them using `Class.StudioTheme:GetColor()|GetColor()` with `Enum.StudioStyleGuideColor` enums. This `setColors()` function is immediately run to sync the Studio theme, then it's connected to the `Class.Studio.ThemeChanged|ThemeChanged` event to detect future theme changes. ```lua testButton.Parent = testWidget local function syncGuiColors(objects) local function setColors() for _, guiObject in objects do -- Sync background color guiObject.BackgroundColor3 = settings().Studio.Theme:GetColor(Enum.StudioStyleGuideColor.MainBackground) -- Sync text color guiObject.TextColor3 = settings().Studio.Theme:GetColor(Enum.StudioStyleGuideColor.MainText) end end -- Run 'setColors()' function to initially sync colors setColors() -- Connect 'ThemeChanged' event to the 'setColors()' function settings().Studio.ThemeChanged:Connect(setColors) end -- Run 'syncGuiColors()' function to sync colors of provided objects syncGuiColors({testButton}) ``` ### Customize mouse cursors To improve the expected interaction with widget elements, you can set system-specific **mouse cursors** to GUI events, such as `Class.GuiObject.MouseEnter|MouseEnter` and `Class.GuiObject.MouseLeave|MouseLeave`. The following code demonstrates how to connect a function to the `Class.GuiObject.MouseEnter|MouseEnter` and `Class.GuiObject.MouseLeave|MouseLeave` events of `testButton` to change the mouse cursor: ```lua local function setCursor(cursorAsset) plugin:GetMouse().Icon = cursorAsset end testButton.MouseEnter:Connect(function() setCursor("rbxasset://SystemCursors/PointingHand") end) testButton.MouseLeave:Connect(function() setCursor("") end) ``` Reference the following table for a list of mouse cursors and their potential use cases: | Mouse Cursor Icon | Asset | Use Case | | --- | --- | --- | | | `rbxasset://SystemCursors/Arrow` | Default clicking and selection. | | | `rbxasset://SystemCursors/PointingHand` | Hovering over an active link/button. | | | `rbxasset://SystemCursors/OpenHand` | Hovering over a draggable item. | | | `rbxasset://SystemCursors/ClosedHand` | Dragging an item. | | | `rbxasset://SystemCursors/IBeam` | Hovering in a text field. | | | `rbxasset://SystemCursors/SizeNS` | Hovering over a vertical resize handle. | | | `rbxasset://SystemCursors/SizeEW` | Hovering over a horizontal resize handle. | | | `rbxasset://SystemCursors/SizeNESW` | Hovering over a corner resize handle. | | | `rbxasset://SystemCursors/SizeNWSE` | Hovering over a corner resize handle. | | | `rbxasset://SystemCursors/SizeAll` | Hovering over a multi-direction resize handle. | | | `rbxasset://SystemCursors/SplitNS` | Hovering over a vertical "split" handle. | | | `rbxasset://SystemCursors/SplitEW` | Hovering over a horizontal "split" handle. | | | `rbxasset://SystemCursors/Forbidden` | Hovering over a locked/forbidden item. | | | `rbxasset://SystemCursors/Wait` | Indicating an action is in progress. | | | `rbxasset://SystemCursors/Busy` | Indicating the system is busy. | | | `rbxasset://SystemCursors/Cross` | Hovering over a pinpoint selection area. | > **Warning:** Note that these cursor looks are only an approximation — the actual cursors match your default operating system cursors. ## Gather user input UI elements such as `Class.TextBox` and `Class.TextButton` work normally in Studio widgets, and you can build interfaces just like you normally would on Roblox. However, `Class.UserInputService` doesn't work since these services expect the main experience window to be in focus. One workaround for generic input events is to create a transparent `Class.Frame` and overlay it over the entire screen. The following code sample creates a frame, and when the user clicks on the frame, the `Class.GuiObject.InputBegan` event captures keyboard input on the frame until the user clicks away: ```lua local frame = Instance.new("Frame") frame.BackgroundTransparency = 1 -- Hide the frame frame.Size = UDim2.new(1, 0, 1, 0) -- Cover the screen frame.Position = UDim2.new(0, 0, 0, 0) frame.Parent = testWidget local function onInputBegan(inputObject) -- Process the input object here, for example detect key presses end frame.InputBegan:Connect(onInputBegan) ``` ## Drag-and-drop interaction Using drag-and-drop interactions for your widgets is a simple way to improve the flow of data. To create this interaction, you must define the element to drag, initiate the drag, create a drop target, and process the drop action. ### Create drag source You can start a drag action by calling `Class.Plugin:StartDrag()` when the user presses a mouse button on some UI element, typically a `Class.TextButton` or `Class.ImageButton` within a widget. The following code sample creates a single window widget with a text button inside it. ```lua -- Create the widget first local widgetInfo = DockWidgetPluginGuiInfo.new(Enum.InitialDockState.Float, true, true, 300, 200) local dragSourceWidget = plugin:CreateDockWidgetPluginGui("Drag Source", widgetInfo) dragSourceWidget.Title = "Drag Source" -- Create a TextButton that will initiate the drag local dragButton = Instance.new("TextButton") dragButton.Size = UDim2.new(1, 0, 1, 0) dragButton.Text = "Drag me!" dragButton.Parent = dragSourceWidget ``` ### Initiate the drag When the user clicks on the `Class.TextButton`, you can initiate drag through the `Class.GuiButton.MouseButton1Down|MouseButton1Down()` event which fires as soon as the user presses the mouse button. Within the connected function, determine the data to drag. The data's **type** should be reflected in the `MimeType` key, the **content** of the drag should be reflected within the `Data` key, and the **sender** should describe itself in the `Sender` key. See the `Class.Plugin:StartDrag()` page for more details. ```lua local function onButton1Down() local dragInfo = { Data = "Hello, world", -- The data being dragged MimeType = "text/plain", -- Describes the MIME type of the data Sender = "SomeDragSource", -- Describes from where the data originated MouseIcon = "", -- Image content to use for the cursor DragIcon = "", -- Image content to render under the cursor during drag HotSpot = Vector2.zero -- Where on the DragIcon to center the cursor } plugin:StartDrag(dragInfo) end dragButton.MouseButton1Down:Connect(onButton1Down) ``` ### Create drop target The `Class.PluginGui.PluginDragDropped` event fires when the user releases their mouse on a window during a drag. When this occurs, you need to define a **drop target** such as a second widget with a `Class.TextLabel` to detect drops. ```lua local dragTargetWidget = plugin:CreateDockWidgetPluginGui("Drop Target", widgetInfo) dragTargetWidget.Title = "Drop Target" -- This TextLabel will display what was dropped local textLabel = Instance.new("TextLabel") textLabel.Size = UDim2.new(1, 0, 1, 0) textLabel.Text = "Drop here..." textLabel.Parent = dragTargetWidget ``` ### Process the drop action After creating a drop target, connect the `Class.PluginGui.PluginDragDropped` event on the drop target widget: ```lua local function onDragDrop(dragData) print("PluginDragDropped") if dragData.MimeType == "text/plain" then textLabel.Text = dragData.Data else textLabel.Text = dragData.MimeType end end dragTargetWidget.PluginDragDropped:Connect(onDragDrop) ``` While a drag is still in progress, these three events fire as the user moves their mouse over a widget: - `Class.PluginGui.PluginDragEntered|PluginDragEntered` – fires when the user hovers the mouse over a window - `Class.PluginGui.PluginDragMoved|PluginDragMoved` – fires repeatedly as the user moves their mouse over a window. This is useful for showing a "Drop here!" message. - `Class.PluginGui.PluginDragLeft|PluginDragLeft` – fires when the user's cursor leaves a window. This is useful for hiding a "Drop here!" message. --- title: "Studio command-line interface" url: /docs/en-us/studio/command-line-interface last_updated: 2026-06-29T19:34:11Z description: "Roblox Studio supports command-line arguments that let you start Studio in a variety of ways and access special Studio functionality." --- # Studio command-line interface Roblox Studio supports command-line arguments that let you launch it with special functionality, such as opening a specific place or script when Studio starts. The Roblox Studio executable is typically installed in the following locations: | OS | Location | | --- | --- | | Windows | `%localappdata%\Roblox\Versions\[version]\RobloxStudioBeta.exe` | | Mac | `/Applications/RobloxStudio.app/Contents/MacOS/RobloxStudio` | When launched from the command line, Studio pipes verbose logs to `stdout` that are more detailed than what appears in the **Output** window. Arguments use a double-dash prefix and are case-insensitive. File paths that contain spaces must be quoted. > **Info:** Any arguments not documented here are for internal use only and are subject to change without notice. ## Open a place To open a published place, specify the place ID, universe ID, and a task. ### Required arguments | Argument | Description | | --- | --- | | `--placeId ` | The numeric ID of the place to open. | | `--universeId ` | The universe that owns the place. | | `--task ` | Specifies how Studio opens the place based on the given task. See the [supported tasks](#supported-tasks) table below for more information. | ### Supported tasks | Argument | Description | | --- | --- | | `EditPlace` | Opens the latest published version of the place for editing. | | `EditPlaceRevision` | Opens a specific previous version of the place. Requires `--placeVersion ` to specify the version. | ### Optional arguments | Argument | Description | | --- | --- | | `--targetInstanceId ` | Moves the Studio camera to focus on the instance with this GUID after the place loads. | | `--annotationId ` | Opens the annotation with this GUID after the place loads. | ### Examples ```shell # Open the latest version of a place RobloxStudio.exe --task EditPlace --placeId 74265016723074 --universeId 7127583708 # Open a specific previous version RobloxStudio.exe --task EditPlaceRevision --placeId 74265016723074 --universeId 7127583708 --placeVersion 2 ``` ## Open a local file To open a local place, use `--task EditFile` with the following argument. | Argument | Description | | --- | --- | | `--localPlaceFile ` | Absolute path to a local `.rbxl` or `.rbxlx` place file to open. | You can also open a local place file by passing the file path as the first positional argument without a flag name. ### Examples ```shell # Open a local place file RobloxStudio.exe --task EditFile --localPlaceFile "Projects\MyGame.rbxl" # Shorthand RobloxStudio.exe "Projects\MyGame.rbxl" ``` ## Open a script To open a script, specify the place ID, universe ID, and task as described in the [open a place](#open-a-place) section. Then provide one of the following arguments depending on how you want to open the script. | Argument | Description | | --- | --- | | `--openScriptPath ` | The path to the script. Must use `/` as separators and follow absolute `require-by-string` semantics. | | `--openScriptFromId ` | The unique ID of the script to open. | ### Optional highlighting Optionally, highlight specific lines or a character range when opening the script. | Argument | Description | | --- | --- | | `--startLine ` | Starting line of the highlight range. 1-based and inclusive. | | `--endLine ` | Ending line of the highlight range. 1-based and exclusive. | | `--startCharacter ` | Starting character position within the line. 1-based. | | `--endCharacter ` | Ending character position within the line. 1-based and exclusive. | ### Examples ```shell # Open a script by path RobloxStudio.exe --task EditPlace --placeId 74265016723074 --universeId 7127583708 --openScriptPath Workspace/MyFolder/MyScript # Open a script by ID RobloxStudio.exe --task EditPlace --placeId 74265016723074 --universeId 7127583708 --openScriptFromId 604a6aa2-04cc-c820-09b3-a1dc0000537f # Open a script and highlight specific lines RobloxStudio.exe --task EditPlace --placeId 74265016723074 --universeId 7127583708 --openScriptPath Workspace/MyFolder/MyScript --startLine 1 --endLine 5 # Open a script and highlight a character range RobloxStudio.exe --task EditPlace --placeId 74265016723074 --universeId 7127583708 --openScriptPath Workspace/MyFolder/MyScript --startLine 1 --endLine 1 --startCharacter 1 --endCharacter 6 ``` ## Run a script Use `--task RunScript` to execute a Luau script after a place finishes loading. Scripts run at the same permission level as the Studio command bar. You can combine this with `--placeId` and `--universeId` to run the script in a specific experience, or `--localPlaceFile` to run the script in a local place file. If those arguments are omitted, Studio opens the default empty baseplate template and runs the script there. ### Required arguments | Argument | Description | | --- | --- | | `--runScriptFile ` | Absolute path to a `.lua` or `.luau` file to execute. | ### Optional arguments | Argument | Description | | --- | --- | | `--outputFile ` | Path to a file where script output is written. | | `--quitAfterExecution` | If provided, Studio closes after the script finishes executing. | ### Examples ```shell # Run a script in a specific experience and exit when done RobloxStudio.exe --task RunScript --placeId 74265016723074 --universeId 7127583708 --runScriptFile smokeTest.luau --outputFile out.log --quitAfterExecution # Run a script in the default Baseplate RobloxStudio.exe --task RunScript --runScriptFile smokeTest.luau ``` ## Try an asset Use `--task TryAsset` to insert a specific asset into a baseplate template and open it for editing. ### Required arguments | Argument | Description | | --- | --- | | `--assetId ` | The asset ID to insert. | ### Example ```shell RobloxStudio.exe --task TryAsset --assetId 53326 ``` ## API dump Use `--api`, `--fullApi`, or `--apiV2` to write the Roblox API surface to a file and exit. | Argument | Description | | --- | --- | | `--api ` | Writes the Luau scripting API as JSON to the specified file. | | `--fullApi ` | Writes the full API surface as JSON to the specified file. | | `--apiV2 ` | Writes the full API as JSON in the V2 schema format to the specified file. | ### Examples ```shell RobloxStudio.exe --api "Desktop/output.json" RobloxStudio.exe --fullApi "Desktop/output.json" RobloxStudio.exe --apiV2 "Desktop/output.json" ``` --- title: "Debugging" url: /docs/en-us/studio/debugging last_updated: 2026-06-29T19:34:11Z description: "Debugging tools help you resolve errors and inspect scripts line-by-line as they run.\n" --- # Debugging Studio offers many debugging tools and workflows commonly found in Integrated Development Environments (IDEs). These help you inspect and resolve errors in scripts as they run. ## Breakpoint debugging Breakpoints are checkpoints that pause or "break" the execution of your scripts at specific lines. You can use the pauses to inspect and debug your experience, [watch](#watch) variables, and inspect the [call stack](#call-stack). Breakpoints are one of the most effective ways to debug functions, so they're one of the most important debugging tools. ### Management In the [Script Editor](/docs/en-us/studio/script-editor.md), you can [insert](#insert) several types of breakpoints and edit the configuration of a breakpoint at any time, including during playtest sessions. If you edit breakpoints during a playtest session, the edits persist even after you finish it. You can also edit a breakpoint that's actively pausing your playtest session, but changes don't apply until the next playtest session. #### Insert Breakpoints take multiple variations and can be inserted at any line of executable code as follows: #### Standard To insert a standard breakpoint at a line of code, click in the gutter directly to the right of its line number. Alternatively, right‑click in the gutter and select **Insert Breakpoint**. The breakpoint icon appears as a red circle. ![Standard breakpoint shown in the script gutter](../assets/studio/debugging/Gutter-Breakpoint.png) #### Conditional A conditional breakpoint pauses your script only if a specified condition is true, so it's useful for debugging how functions execute when certain variables have certain values or if you want to break only on certain executions in a loop. To insert a conditional breakpoint, right‑click in the gutter and select **Insert Conditional Breakpoint**. In the popup window that appears, enter the desired condition, such as `var == 10` , then click **Save**. The conditional breakpoint icon appears as a circled **=** sign. ![Conditional breakpoint shown in the script gutter](../assets/studio/debugging/Gutter-Breakpoint-Conditional.png) #### Logpoint A logpoint is a special breakpoint that logs messages to the [Output](/docs/en-us/studio/output.md) window without pausing the script, making it useful for debugging the values of the variables before the line executes. To insert a logpoint, right‑click in the gutter and select **Insert Logpoint**. In the popup window that appears, enter the desired log message, such as `"The value of var:", var` , then click **Save**. The logpoint icon appears as a red diamond. ![Logpoint shown in the script gutter](../assets/studio/debugging/Gutter-Breakpoint-Logpoint.png) #### Temporary A temporary breakpoint offers the same [options](#edit) as other breakpoint types, except that it automatically removes itself after the first [playtest session](#workflow). This makes it ideal for quickly testing variable values or functionality without needing to disable or delete the breakpoint afterward. To insert a temporary breakpoint, right‑click in the gutter and select **Insert Temporary Breakpoint**. It initially appears as red circle and then you can [edit](#edit) its options to specify the behavior. ![Standard breakpoint shown in the script gutter](../assets/studio/debugging/Gutter-Breakpoint.png) #### Disable To temporarily disable a breakpoint without [deleting](#delete) it, use any of the following workflows: - Click the breakpoint's icon. - Right-click the breakpoint's icon and select **Disable Breakpoint**. - [Edit](#edit) the breakpoint and toggle its **Enabled** checkbox. A disabled breakpoint appears as a hollow version of its enabled icon, for example a hollow circle for a disabled standard breakpoint, or a hollow diamond for a disabled logpoint. ![Disabled standard breakpoint shown in the script gutter](../assets/studio/debugging/Gutter-Breakpoint-Disabled.png)_Disabled standard breakpoint_ ![Disabled logpoint shown in the script gutter](../assets/studio/debugging/Gutter-Breakpoint-Logpoint-Disabled.png)_Disabled logpoint_ #### Delete To delete a breakpoint, middle-click its icon or right‑click its icon and select **Delete Breakpoint**. > **Warning:** Deleting a breakpoint deletes its **Condition** and **Log Message**. If you want to keep this information for future debugging, [disable](#disable) the breakpoint instead. #### Edit You can edit the configuration of a breakpoint at any time, including during playtest sessions. If you edit breakpoints during a playtest session, the edits persist even after you finish it. You can also edit a breakpoint that's actively pausing your playtest session, but changes don't apply until the next playtest session. To edit a breakpoint: 1. Right-click the breakpoint icon and select **Edit Breakpoint**. 2. In the **Edit Breakpoint** window, configure the breakpoint as desired. You can mix these configurations together to best suit your debugging needs. | Option | Description | | --- | --- | | **Condition** | An optional expression that determines whether the breakpoint activates. If the condition is empty, the breakpoint always activates. If the condition exists, the breakpoint becomes a [conditional breakpoint](#insert) and activates only if the condition is true. For example, if you want the breakpoint to activate only if the variable `var` equals `10`, set the condition to`var == 10`. | | **Log Message** | An expression that prints to the [Output](/docs/en-us/studio/output.md) window when the breakpoint is hit. The format of the log message is the same as that of a `Global.LuaGlobals.print()` statement. For example, set the log message as`"The value of var:", var` to output the same message as`print("The value of var:", var)`. | | **Continue Execution** | Determines whether the breakpoint pauses the script if it activates. This option is disabled by default unless the inserted breakpoint is a [logpoint](#insert). | | **Remove Breakpoint on Hit** | If checked, the breakpoint automatically removes itself after the first playtest session, effectively making it a [temporary](#insert) breakpoint. | | **Trigger At** | The **context** of a breakpoint determines whether it should activate on the **Client** (client‑side scripts), **Server** (server‑side scripts), or **Edit** (debugging [plugin](/docs/en-us/studio/plugins.md) scripts). If you choose **Custom Context**, the window indicates the current trigger contexts. | ### Workflow The typical workflow for breakpoint debugging is as follows: 1. Open the script you'd like to debug and [insert breakpoints](#insert) on the lines of codes that you want to examine. 2. From Studio's mezzanine, [initiate a playtest](/docs/en-us/studio/testing-modes.md#playtesting) to begin debugging.![Test option in the testing modes dropdown of Studio's mezzanine.](../assets/studio/general/Mezzanine-Testing-Mode-Test.png) 3. When the script hits a breakpoint, the playtest session **pauses** unless that breakpoint is [configured](#edit) to continue execution. A yellow arrow over the breakpoint indicates which line of code executes next.![Active breakpoint in the debugger showing yellow arrow](../assets/studio/debugging/Gutter-Breakpoint-Active.png) 4. As the script pauses, inspect the [Breakpoints](#breakpoints) window, [Watch](#watch) window, [Call Stack](#call-stack) window, [Output](/docs/en-us/studio/output.md) window, and [Script Editor](#script-editor) to find information about variable values and function executions. 5. To continue executing code after hitting a breakpoint, click **Resume Scripts** in the mezzanine.![Resume Scripts button indicated in Studio's mezzanine](../assets/studio/general/Mezzanine-Debug-Resume-Scripts.png) You can also execute code one line at a time with the stepping buttons in the mezzanine:![Breakpoint stepping buttons indicated in Studio's mezzanine](../assets/studio/general/Mezzanine-Debug-Tools.png) | Action | Shortcut | Description | | --- | --- | --- | | **Step Into** | `F11` | The **Step Into** button moves the debugger into the code of the function on the current line. If there is no function on the current line, the debugger moves to the next line. | | **Step Over** | `F10` | The **Step Over** button moves the debugger to the next line of code, **not** moving into functions. | | **Step Out** | `Shift``F11` | The **Step Out** button moves the debugger out of the current function and to the next line of code after the function call. If the current line isn't inside a function, the debugger moves to the next line. | 6. When you're finished debugging the current session, click the **Stop** button.![Stop button indicated in Studio's mezzanine.](../assets/studio/general/Mezzanine-Debug-Stop.png) ### Monitoring Studio includes multiple windows and tools to monitor variable values and function executions while debugging. With this information, you can find the root cause of most problems in your scripts. ![Breakpoints button indicated in Studio's toolbar](../assets/studio/general/Toolbar-Debugging-Tools.png) #### Breakpoints The **Breakpoints** window is a unified view that shows all breakpoints within all scripts. To open it, click **Breakpoints** from Studio's **Script** tab toolbar, or navigate to the **Window** ⟩ **Debug** menu and toggle on **Breakpoints**. In the window, the **Script** and **Line** columns always display, and you can toggle on/off other [configuration](#edit) columns through the **⋮** menu in the upper‑right corner. Within the **Script** column, right‑click any breakpoint to reveal an options menu to [edit](#edit), [disable/enable](#disable), [delete](#delete), or jump to the breakpoint's line within the script. ![Breakpoints window in Studio](../assets/studio/debugging/Breakpoints-Window.png) While in a debugging session, the furthest left column shows **(x3)** which indicates the multiple contexts (**Client**, **Server**, **Edit**) for the breakpoint. By expanding any breakpoint's branch, you can then click the icon next to a specific context to [disable/enable](#disable) it for that context, such as disabling a breakpoint only in the **Client** context while keeping it enabled in the **Server** context. ![Breakpoint expanded to show contexts](../assets/studio/debugging/Breakpoints-Window-Contexts.png) #### Call Stack The **Call Stack** window, accessible from Studio's **Script** tab toolbar or the **Window** ⟩ **Debug** menu, shows which line of code is going to execute next when the debugger reaches a breakpoint. If you call functions from inside other functions, it indicates which lines those functions were called from, letting you confirm whether the actual call flow matches the expected flow. ![Call stack flow in a script](../assets/studio/debugging/Call-Stack-Flow-Script.png)![Call stack flow in the Call Stack window](../assets/studio/debugging/Call-Stack-Flow-Window.png) As the script pauses at breakpoints, use the stepping buttons in the mezzanine to step into, over, or out of a function/line as outlined in [workflow](#workflow). ![Breakpoint stepping buttons indicated in Studio's mezzanine](../assets/studio/general/Mezzanine-Debug-Tools.png) #### Watch The **Watch** window, accessible from Studio's **Script** tab toolbar or the **Window** ⟩ **Debug** menu, lets you actively watch variable and expression values as you step through breakpoints. #### Variables The **Variables** tab shows information about the current variables in [scope](/docs/en-us/luau/scope.md), filterable by clicking the **All Scopes** button in the window's upper bar. Expanding a branch shows the members of that variable or instance and their values. You can also watch any variable by double‑click highlighting it in the [Script Editor](/docs/en-us/studio/script-editor.md), right‑clicking, and selecting **Add Watch** from the popup menu. ![Variables to watch inside a script](../assets/studio/debugging/Watch-Variables-Script.png)![Variables information in the Watch window](../assets/studio/debugging/Watch-Variables-Window.png) #### My Watches The **My Watches** tab shows the value of variables or expressions that you define. To watch a specific expression, click an empty row in the **Expression** column and type the expression into it. This allows you to check any expression value without monitoring individual variables in the **Variables** tab or adding `Global.LuaGlobals.print()` statements in the script to check expression values. ![Variables to watch inside a script](../assets/studio/debugging/Watch-Expressions-Script.png)![Expression entered into Watch window for analysis](../assets/studio/debugging/Watch-Expressions-Window.png) #### Script Editor The debugger is integrated with the [Script Editor](/docs/en-us/studio/script-editor.md). When your experience pauses at a breakpoint in a script, you can hover your mouse over the name of a variable to see its value. For example, you can see the composition of a table and its contained keys/values. ![Mouseover a variable in Script Editor to show value](../assets/studio/debugging/Script-Editor-Mouseover-Table.png) ## Additional debugging tools In addition to the debugger, Studio offers additional debugging tools for you to fix problems and bugs in your experience. ### Command Bar The [Command Bar](/docs/en-us/studio/ui-overview.md#command-bar) allows you to run Luau commands while the experience is running. It is available in Studio from the **Window** ⟩ **Script** menu and in the [Developer Console](/docs/en-us/studio/developer-console.md). ### Developer Console The [Developer Console](/docs/en-us/studio/developer-console.md) provides a wide array of details including client and server output, memory usage, network performance, and more. To open console while testing or playing an experience, type `/console` into the chat or press `F9`. ### Log files When a script prints or errors in Studio or the player app, the app records the message in a log file in the local file system. These files are located in different places depending on the operating system. #### Windows To access log files on Windows: 1. Open **File Explorer**. 2. Navigate to the `%LOCALAPPDATA%\Roblox\logs` directory. 3. Double-click a log to open it. Logs with the same `XXXXX` value are from the same Studio session. #### Mac To access log files on Mac: 1. Open **Finder**. 2. In the menu bar, select **Go** ⟩ **Go to Folder...**. 3. In the dialog, enter `~/Library/Logs/Roblox`. 4. Double-click the result to navigate to the Roblox logs directory. 5. Inside the directory, double-click a log to open it. #### iOS You can collect iOS logs using a Mac or using an iOS device. #### Mac To access iOS log files on a Mac: 1. Connect the iOS device to a Mac. 2. Open **Finder**. 3. Navigate to **Utilities** and open the **Console** application. 4. To populate real-time logs in the Console application, select the iOS device from the sidebar, click the **Start** button, and reproduce the issue on the iOS device. 5. To populate archived logs in the Console application, run `sudo log collect --device-name "[iOS Device Name]"` in the **Terminal**. Make sure there are no apostrophes in the device's name or you might get an error when running the command. #### Device To access iOS log files on an iOS device: 1. Press and hold both volume buttons and the top or side button on the iOS device for 1 to 1.5 seconds to start the `sysdiagnose` program. This process might take up to 10 minutes to finish. 2. Navigate to **Settings** ⟩ **Privacy & Security** ⟩ **Analytics & Improvements** ⟩ **Analytics Data** to access the log files. 3. Tap on the a log file to open it. #### Android To access log files on Android: 1. Navigate to **Settings** ⟩ **System** ⟩ **Developer options**. 2. Toggle **Developer options** on. 3. On a computer, download and install [Android Studio](https://developer.android.com/studio). 4. In Android Studio, click **Logcat**. 5. Connect the Android device to the computer to automatically populate **Logcat** with logs. --- title: "Developer Console" url: /docs/en-us/studio/developer-console last_updated: 2026-06-29T19:34:11Z description: "Developer Console is a tool for debugging your experience during testing or in production." --- # Developer Console The **Developer Console** is a tool for debugging your experience when testing in Studio or running it in production. It shows log messages and errors similar to the [Output](/docs/en-us/studio/output.md) window and detailed information on [Memory](#memory), [Network](#network), and more. You can open the console during a testing or live experience session using any of the following ways: - Press `F9`. - Type `/console` into the chat. - Use the in-experience menu: 1. Open the in-experience **Roblox Menu**. 2. Select the **Settings** tab. 3. Scroll down to **Developer Console** and click **Open**. ## Console sections The shortcut bar at the top of the console shows the number of critical errors and warnings, client memory usage, and average ping time; click any of these to jump to its corresponding section in the console. Below the shortcut bar is a series of tools as outlined below. ### Log The **Log** tool shows diagnostic messages from scripts. #### Client/server toggle Using the client-server switch, you can toggle between **Client** and **Server** views: - The **Client** view shows output from client-side `Class.LocalScript|LocalScripts` and/or `Class.Script|Scripts` with `Class.Script.RunContext|RunContext` set to `Enum.RunContext.Client`. Anyone running an experience can view local output messages. - The **Server** view shows output from server-side `Class.Script|Scripts` (`Class.Script.RunContext|RunContext` set to `Enum.RunContext.Server` or `Enum.RunContext.Legacy`). Only the experience owner or [group](/docs/en-us/projects/groups.md) members with editing permission can view server output messages. #### Output filters Using the checkboxes, you can filter output messages in the log: | **Output** | Messages generated by calls to `Global.LuaGlobals.print()` and `Global.RobloxGlobals.warn()` statements within scripts. | | --- | --- | | **Information** | Messages generated by the experience that aren't errors or custom output statements. | | **Warning** | Messages that indicate a potential problem but not a critical issue. | | **Error** | Messages that indicate that something critical has happened. | #### Command bar The **Server** view also includes a command bar that lets users with editing permissions run arbitrary Luau code. Unlike the [Command Bar](/docs/en-us/studio/ui-overview.md#command) in Studio, this console command bar has the same security restrictions as `Class.Script|Scripts` and `Class.LocalScript|LocalScripts`, so it can't run protected functions. ### Memory **Developer Console** provides two tools for monitoring memory usage, including: - **Memory** — View real-time memory consumption by usage categories, including memory usage by both your custom components and the engine internal processes. - **Luau heap** — Create snapshots on the heap memory, which refers to the memory allocation to your scripts. This tool provides various memory allocation views to help you identify current memory allocation and issues from different perspectives, such as object types and engine classes. It also allows you to create multiple snapshots to compare differences in memory usage over time. For more information, see [Memory Usage](/docs/en-us/studio/optimization/memory-usage.md) ### Network The **Network** tool shows the number of web calls that the experience has made. This includes explicit calls made through `Class.HttpService` and web requests made by Roblox services like `Class.DataStoreService`. The **Network** tool has two sections: - The **Summary** section lists a summary of all the web calls by type. Each type has details on the number of requests, the number of failed requests, and the amount of time that the request took. - The **Details** section lists each individual web call. Each row shows the HTTP method, such as `GET` or `POST`, along with the status code, time to execute, request type, and request URL. Click on a web call to see its response details, for example:_ {"Version":"1.1":"Content":{"Headers":[]},"StatusCode":"OK","ReasonPhrase":"OK","Headers":[],"TrailingHeaders":[],"RequestMessage":null,"IsSuccessStatusCode":true}_ ### Script Profiler The **Script Profiler** tool allows you to record profiling sessions of all running scripts and view their CPU time costs with custom recording and display settings. It's helpful for identifying scripts that take up the most CPU resources and slow down the performance. For more information, see [Script Profiler](/docs/en-us/studio/optimization/scriptprofiler.md). --- title: "Experience Settings" url: /docs/en-us/studio/experience-settings last_updated: 2026-06-29T19:34:11Z description: "The Experience Settings window contains Studio-level settings and customization options for an experience." --- # Experience Settings The **Experience Settings** window, accessible from Studio's **File** menu, contains a wide array of Studio-level settings and customization options for Roblox experiences, including settings for [permissions](#permissions), [monetization](#monetization), [security](#security), and [localization](#localization). > **Warning:** Many settings in this window modify underlying properties in your place file that aren’t visible in Studio or accessible with scripts. This can lead to unexpected or difficult-to-debug behavior, especially when working across teams or reverting changes. Make sure you fully understand the impact of each setting before applying changes. ## Basic Info The **Basic Info** tab contains general settings for an experience, such as its name, description, and promotional images. | Setting | Description | | --- | --- | | **Name** | The experience's title. | | **Description** | A description of the experience that describes what a potential player should expect. | | **Content Maturity Label** | [Content Maturity Labels](/docs/en-us/production/promotion/content-maturity.md) provide information on the experience's main page about what kind of content the experience contains. | | **Game Icon** | Lets you upload an [icon](/docs/en-us/production/publishing/experience-icons.md) to represent your experience. | | **Screenshots & Videos** | Lets you upload [promotional thumbnails](/docs/en-us/production/publishing/thumbnails.md). | | **Playable Devices** | Lets you enable each applicable device that supports your experience. | ## Communication | Setting | Description | | --- | --- | | **Enable Microphone** | Enables voice-eligible users to use voice chat within your experience. See [Voice Chat](/docs/en-us/chat/voice-chat.md) for more information. | | **Enable Camera** | Enables eligible users to animate their avatar with their camera in your experience. See [Animate Your Avatar](https://en.help.roblox.com/hc/en-us/articles/17877687557396) for more information. | ## Permissions The **Permissions** tab lets you control the audience for your experience. | Setting | Description | | --- | --- | | **Playability** | Sets which users can access the experience. | ## Monetization The **Monetization** tab contains settings that let you [monetize](/docs/en-us/production/monetization.md) your experience. | Setting | Description | | --- | --- | | **Badges** | Lets you create and manage all [badges](/docs/en-us/production/publishing/badges.md) for your experience. | | **Paid Access** | Lets you enable [paid access in Robux](/docs/en-us/production/monetization/paid-access-robux.md) or [paid access in local currency](/docs/en-us/production/monetization/paid-access-local-currency.md). | | **Private Servers** | Lets you enable [private servers](/docs/en-us/production/monetization/private-servers.md). | | **Developer Products** | Lets you create and manage all [developer products](/docs/en-us/production/monetization/developer-products.md) for your experience. | ## Security The **Security** tab contains settings related to network communication, sales, and teleports. > **Warning:** You should only enable the following settings if you trust all assets from other users or groups within your experience, otherwise your experience is vulnerable to security risks. | Setting | Description | | --- | --- | | **Allow HTTP Requests** | Allows experiences' servers to issue requests to remote servers via `Class.HttpService`. | | **Secrets** | Allows for the creation and configuration of `Datatype.Secret\|Secrets` for local Studio sessions using `Class.HttpService`. For more information, see [Work with secrets](/docs/en-us/cloud-services/secrets.md#local-secrets). | | **Enable Studio Access to API Services** | Lets Studio access API services. This setting is useful for testing the implementation of services like [data stores](/docs/en-us/cloud-services/data-stores.md). | | **Allow Third Party Sales** | Lets players purchase items from third parties. When this setting is disabled, all purchase prompts from assets you own will continue to work while purchase prompts from assets published by other users or groups will fail. | | **Allow Third Party Teleports** | Lets players [teleport](/docs/en-us/projects/teleport.md) to other experiences. | | **Allow Mesh / Image APIs** | Lets you use `Class.EditableImage` and `Class.EditableMesh` in published experiences. To enable usage, you must be 13+ age verified and ID verified. | ## Places The **Places** tab contains place-specific settings. | Setting | Description | | --- | --- | | **Create** | Lets you create a new place within your experience. | | **⋯** | Opens a pop-up menu with options to **Configure Place** (edit place-specific settings such as the maximum number of players), or **Version History** to view, download, and open any previous version of the experience. | ## Localization The **Localization** tab contains language settings related to [localization](/docs/en-us/production/localization.md) both within the experience and in the cloud. | Setting | Description | | --- | --- | | **Source Language** | Lets you choose the language that you used to create the experience. | | **Automatic Text Capture** | Automatically captures text from the experience's UI while users are accessing the experience. | | **Use Translated Content** | Enables translated content within the experience. | | **Automatic Translation** | The languages for which you'd like to enable [automatic translation](/docs/en-us/production/localization/automatic-translations.md). | ## World The **World** tab includes global settings for the experience, such as gravity, character jump behavior, and walk speed. | Setting | Description | | --- | --- | | **Presets** | Sets the options lower in the tab to presets for **Classic**, **Realistic**, or **Action**. | | **Gravity** | Sets the overall world gravity in studs per second² (note the meters per second² equivalent in parentheses). | | **Jump** | Sets either the humanoid **jump height** in studs or the **jump power** in studs per second. Note how adjusting this value changes the max jump distance in relation to the walk speed. | | **Walk** | Sets the humanoid walk speed in studs per second. Adjusting this value changes the **max jump distance** in relation to jump height or power. | | **Slope** | Determines the maximum slope angle that humanoids can climb. If the angle of a slope is greater than this value, the humanoids will slide down the slope. | ## Other The **Other** tab includes settings that are typically reserved for special situations, such as collaborative scripting and shutting down all servers. | Setting | Description | | --- | --- | | **Enable Drafts Mode** | Enables asynchronous, drafts-based script editing in a [collaborative](/docs/en-us/projects/collaboration.md) session. | | **Shutdown All Servers** | Shuts down all servers currently running the experience. | --- title: "Explorer window" url: /docs/en-us/studio/explorer last_updated: 2026-06-29T19:34:11Z description: "The Explorer window shows a hierarchical list of every instance inside an experience." --- # Explorer window The **Explorer** window, accessible from Studio's **Window** menu or **Home** tab toolbar, shows a hierarchical list of every instance inside an experience. At the highest level of the hierarchy are the services; `Class.Workspace`, for example, is where visible 3D content such as [parts](/docs/en-us/parts.md) are stored. ![Explorer highlighted in Studio's toolbar.](../assets/studio/general/Toolbar-Explorer.png) ## Parent-child hierarchy All children of a parent object appear under its branch when expanded. Click the arrow next to a parent branch — or press the `→`/`←` arrow keys when a parent is selected — to expand/collapse only that branch. ![Explorer hierarchy showing Camera, Terrain, and SignModel as children of Workspace; Board and Post as children of SignModel](../assets/studio/explorer/Parent-Child-Hierarchy.png) ![Only the topmost parent expanded with click](../assets/studio/explorer/Branch-Expanded.png) > **Info:** To expand or collapse **all** branches within a multi‑nested hierarchy, hold `Shift` when clicking the expand/collapse arrow. To change the parent of one or more children (reparent), simply drag and drop them onto the new parent. ![Dragging one object over another to reparent it](../assets/studio/explorer/Reparent-Object.png) ## Object insertion While primitive [parts](/docs/en-us/parts.md) can be [inserted](/docs/en-us/parts.md#insert-parts) from the **Home** or **Model** tab toolbars, you can select from a full array of objects by hovering over the intended parent and clicking the ⊕ button (shortcut of `Ctrl``I` on Windows or `⌘``I` on Mac). ![Hovering over Workspace object to reveal insertion button](../assets/studio/explorer/Workspace-Add-Object.png) You can further customize insertion behavior by clicking the **⋯** button to the right of the search field: ![Additional customization options for insertion workflow](../assets/studio/explorer/Insert-Object-Options.png) | Option | Description | | --- | --- | | **Increment names for new instances** | When enabled, inserted/pasted/duplicated instances of the same type will have numbered names for differentiation. | | **Expand hierarchy when selecting** | When enabled, inserting/pasting an instance or selecting an object in the [3D viewport](/docs/en-us/studio/ui-overview.md#3d-viewport) will expand the **Explorer** hierarchy to reveal that instance. When disabled, the top‑level parent within the hierarchy will be highlighted, but it won't expand to reveal the selected instance. | > **Success:** To keep the hierarchy cleaner, services such as `Class.VoiceChatService` are hidden by default. You can show hidden services by right‑clicking in the window and selecting **Show Services…** from the context menu. Similarly, you can hide a visible service by right‑clicking it and selecting **Hide Service in Explorer**. ## Duplicate and paste Objects can be quickly duplicated into the same branch, while items copied to the clipboard can be pasted into the top‑level `Class.Workspace` or directly into one or more existing parents. | Action | Windows | Mac | Description | | --- | --- | --- | --- | | **Duplicate** | `Ctrl``D` | `⌘``D` | Duplicates the selected objects into the same branch. | | **Paste** | `Ctrl``V` | `⌘``V` | Pastes the clipboard contents into the top‑level `Class.Workspace` branch. | | **Paste Into** | `Ctrl``Shift``V` | `⌘``Shift``V` | Pastes the clipboard contents into the selected object(s). Using this action on multiple selected objects is a convenient way to paste the same clipboard items into multiple parents, such as a common `Class.Texture` into several different `Class.Part\|Parts`. | > **Info:** When pasting 3D objects copied to the clipboard, you can maintain their original `Datatype.CFrame` position by right‑clicking the target parent instance in the hierarchy and selecting **Paste Options** ⟩ **Paste Into At Original Location**. ## Contextual options Right-clicking on an instance opens the options menu, contextually adjusted for the object type. For example, right‑clicking a `Class.Model` reveals standard options like **Copy** and **Duplicate**, and also options specific to `Class.Model|Models` like **Ungroup**. In contrast, right‑clicking a service like `Class.Lighting` reveals a more concise menu of actions valid for services. ## Folder organization Like directories/folders on a computer, the `Class.Folder` object is a useful way to organize objects. For instance, if you want to store all environmental audio assets in a logical place, you can create a folder and place multiple `Class.AudioPlayer` objects within it. ![Multiple Sound objects grouped inside a Folder object](../assets/studio/explorer/Folder-Children.png) You can organize objects into folders as follows: - Create a `Class.Folder` instance through a standard [insertion](#object-insertion) method, then drag and drop existing objects into it. - Select multiple objects, right-click them, and then select **Group As a Folder** from the context menu (shortcut of `Alt``Ctrl``G` on Windows or `⌥``⌘``G` on Mac). ## Search methods Through the **search** input near the top of the window, you can find instances by [name](#name-search), [property](#property-search), class/subclass/tag [bespoke](#bespoke-search) queries, and [ancestry](#ancestry-search) queries. You can also [combine parameters](#combined) for advanced logical searches. Quickly access this input by pressing `Ctrl``Shift``X` (Windows) or `⌘``Shift``X` (Mac). ![Example search query and search options](../assets/studio/explorer/Search-Diagram.png) Navigate up and down through search results Select all search results (`Ctrl``A` on Windows or `⌘``A` on Mac) Refresh search results > **Success:** A name is a sequence of characters that are alphanumeric, `_`, `-`, or `.`. In all of the following search methods, exercise caution when searching for instances whose names contain spaces, as the search logic may confuse spaces for a [combined search](#combined). When searching for names with spaces, you should surround the full name with double quotes, for example`tag:"Light Source"` to search by the full [tag](/docs/en-us/studio/properties.md#instance-tags) name of`Light Source`. ### Name search Typing in a basic string yields **name** matched instances, case‑insensitive. For example, searching by the keyword `script` finds all instances containing it, such as `ServerScriptService` and `LocalScript`. ![Objects filtered by query of 'script'](../assets/studio/explorer/Search-By-Name.png) ### Property search You can filter by property equality or value comparisons. Most properties are supported, it works with partial matches, ignores spacing, and is case‑insensitive. #### Equality Property **equality** is searched through the operators `=` or `==`. | Example | Result | | --- | --- | | `ClassName = Decal` | All instances of class `Class.Decal`. | | `Locked = true` | `Class.BasePart\|BaseParts` with `Class.BasePart.Locked\|Locked` set to `true`. | | `Material == plas` | `Class.BasePart\|BaseParts` with `Class.BasePart.Material\|Material` of either `Enum.Material\|Plastic` or `Enum.Material\|SmoothPlastic`. | #### Value Comparisons Property **value comparisons** are searched through the operators `~=`, `>`, `<`, `<=`, or `>=`. | Example | Result | | --- | --- | | `Health > 50` | Every `Class.Humanoid` with `Class.Humanoid.Health\|Health` more than `50`. | | `Transparency ~= 0.5` | `Class.BasePart\|BaseParts` with `Class.BasePart.Transparency\|Transparency` **not** equal to `0.5`. | #### Individual Fields Property types such as `Datatype.Vector3` and `Datatype.Color3` support searching by **individual fields**. This works either for exact equality or for value comparisons. | Example | Result | | --- | --- | | `Position.X = 1` | Objects with an `X` position of exactly `1`. | | `Color.R > 120` | Objects with a red (`R`) color channel value greater than `120`. | #### Complete Units Property types such as `Datatype.Vector3` and `Datatype.Color3` support searching as a **complete unit**, with the unit surrounded by quotes. This works either for exact equality, or with comparison operators in which every value is compared against the comparator. | Example | Result | | --- | --- | | `Size > "20, 5, 20"` | Objects with an `X`, `Y`, and `Z` size greater than `20`, `5`, `20`, respectively. | | `Color = "255, 0, 0"` | Objects with an RGB color value of exactly`[255, 0, 0]`. | ### Bespoke search Classes, and subclasses, and tags are searched through various **bespoke** queries, including: - `is:[Class]` finds everything that is of **class** `[Class]`. For example, `is:Part` finds everything that is a `Class.Part`. - `is:[SubClass]` finds everything that is a **subclass** of `[SubClass]`. For example, `is:BasePart` finds everything that is a subclass of `Class.BasePart`, such as `Class.Part` and `Class.MeshPart`. - `tag:[TagName]` finds everything with a `Class.CollectionService` tag of `[TagName]`. For example, `tag:LightSource` finds everything that is tagged `LightSource`. You can add tags using the [Tags](/docs/en-us/studio/properties.md#instance-tags) section of an instance's properties and tag names will autocomplete. ### Ancestry search You can search within a specific scope using the `.` operator, chain operators together for highly specific searches, or use `*` as a wildcard. - `[Parent].[Child]` finds named children inside a named parent. For example, `workspace.Model` finds instances named `Model` inside instances named `workspace`, similar to scripting. - Chaining `.` operators adds specificity. For instance, `Animals.Dog.Tail` finds objects named `Tail` inside `Dog` inside `Animals`. - `*` acts as a wildcard, for instance: | Example | Result | | --- | --- | | `Cart.*` | All children of an object named `Cart`. | | `Cart.Barrier.*` | All children of `Barrier`, itself a child of `Cart`. | | `Cart.*.*` | All grandchildren of `Cart`, excluding direct children of `Cart`. | | `Cart.*.Trim` | All grandchildren of `Cart` that are named `Trim`. | - `[Parent].**` finds all descendants of a parent. For example, `CarModel.**` finds all descendants of `CarModel`. ### Combined For advanced logical searches, you can combine queries through unions and grouping. - Multiple queries separated by spaces or `and` operate on **all** of the conditions. | Example | Result | | --- | --- | | `Anchored=true CanCollide=true` | Every `Class.BasePart` that is anchored and can collide. | | `Cart.*.* Transparency < 1` | All grandchildren of `Cart` that are not fully transparent. | | `Anchored=false and CanCollide=false` | Every `Class.BasePart` that isn't anchored and can't collide. | - The `or` conjunction operates just like the `or` operator in code. Groups within parentheses, conjoined by `or`, find everything within multiple combined queries. | Example | Result | | --- | --- | | `Cat or Dog` | Everything that has `Cat` or `Dog` in its name. | | `(Anchored=true CanCollide=true) or (Anchored=false CanCollide=false)` | Every `Class.BasePart` that either is anchored and can collide, **or** isn't anchored and can't collide. | ## Selection methods Within a [searched](#search-methods) hierarchy, certain key shortcuts and mouse operations behave differently than they do within a non‑searched hierarchy. ### Select all Within a non‑searched hierarchy, pressing `Ctrl``A` on Windows or `⌘``A` on Mac selects all instances. Within a [searched](#search-methods) hierarchy, the same shortcut or the **select all** button selects only the query‑matching objects, for example all objects matching **Board** in a hierarchy searched for `board`. ![Image showing how only matching objects are selected in a searched hierarchy using the Select All shortcut](../assets/studio/explorer/Select-All-Searched.png) ### Shift-select Clicking an object and `Shift`-clicking another object behaves differently within a non‑searched hierarchy versus a [searched](#search-methods) hierarchy. #### Non-Searched Hierarchy Within a non-searched hierarchy, `Shift`-clicking two objects at the same hierarchy level selects both of them and every object between which is also at the same hierarchy level, as shown in image **🅰** where **LeftFoot** is initially clicked, **LeftLowerArm** is `Shift`‑clicked, and **LeftHand** at same hierarchy level is also selected. Duplicating, copying, or pasting the selected objects will result in a deep copy of those objects **and** all children. If the first `Shift`-clicked object and the second `Shift`-clicked object are at **different** hierarchy levels, all objects in between them will be selected, as shown in image **🅱** where **LeftFoot** is initially clicked, **LeftHand** is `Shift`‑clicked, and everything between is also selected. _`Shift`-click at same hierarchy level_ _`Shift`-click at different hierarchy level_ #### Searched Hierarchy Within a searched hierarchy, if both the initially‑clicked object and the `Shift`‑clicked object match the query, only query‑matching objects between are selected. For example, in the following image with a hierarchy searched for `anim`, **FallAnim** is initially clicked, **RunAnim** is `Shift`‑clicked, and every object matching `anim` between is also selected. ![Image showing how only matching objects in range are selected within a searched hierarchy](../assets/studio/explorer/Shift-Click-Searched.png) ### Drag select Clicking and dragging from an empty region of the window initiates a selection box. Within a non‑searched hierarchy, everything inside the box is selected. Within a [searched](#search-methods) hierarchy, only query‑matching objects inside the box are selected. ![Image showing how only matching objects inside the selection box are selected within a searched hierarchy](../assets/studio/explorer/Drag-Select-Searched.png) ## Key shortcuts | Shortcut | Description | | --- | --- | | `→` | With a collapsed branch selected, expands that branch. When pressed again, selects the **first child** immediately under the parent node. | | `←` | With any child of a branch selected, moves selection back to the parent node. When pressed again, collapses the entire branch. | | `Home` | Selects the topmost object in the hierarchy (`Class.Workspace`). | | `End` | Selects the bottommost object in the hierarchy. | | `PageUp` | Selects the object in the hierarchy that's above the topmost **visible** hierarchy item. | | `PageDown` | Selects the object in the hierarchy that's below the bottommost **visible** hierarchy item. | | `Ctrl``Shift``X`
`⌘``Shift``X` | Jump to the [search input](#search-methods) near the top of the window. |
--- title: "Importer" url: /docs/en-us/studio/importer last_updated: 2026-06-29T19:34:11Z description: "Importer imports third-party .fbx, .gltf, and .obj 3D model assets into Studio." --- # Importer The Importer allows you to import 3D models, images, sounds, and video files into Studio. 3D model import supports meshes with PBR textures, meshes with rigging, skinning, and animation data, and meshes designed as avatar items. Import general 3D custom models into Studio. Use the Importer to import assets with avatar item components. To import an asset: 1. From Studio's **File** menu, select **Import**. 2. In the file browser, select the [supported file type](#supported-file-types) you intend to import. 3. Configure your import settings and verify any [warning or error messages](#warnings-and-errors). 4. Click **Import**. 5. After importing, you can **right-click** the imported asset in the importer and perform the following: 1. **Copy asset ID** - Copies the uploaded asset ID to your clipboard. For models, this copies the asset ID of the `Class.Model`. 2. **Find in Workspace** (3D models only) - Automatically positions the camera to the location of the imported object in workspace. > **Info:** To directly import assets using HTTP requests, see the [Open Cloud usage guide for assets](/docs/en-us/cloud/guides/usage-assets.md). ## Supported file types Before importing a 3D object, ensure that the `.fbx`, `.gltf` or `.obj` meets Studio's [mesh requirements](/docs/en-us/avatar/character-bodies/specifications.md) to reduce errors or unexpected behavior. | Asset Type | Details | | --- | --- | | **Image** | You can import images in `.png`, `.jpg`, `.gif`, `.tga`, or `.bmp` format for use as [textures/decals](/docs/en-us/parts/textures-decals.md) on parts, [image labels](/docs/en-us/ui/labels.md), [mesh textures](/docs/en-us/parts/meshes.md#texture), textures for [custom materials](/docs/en-us/parts/materials.md#custom-materials), textures for [special effects](/docs/en-us/effects.md), and more. | | **Mesh** | You can import meshes in the `.fbx`, `.obj`, or `.gltf` format.

The `.fbx` and `.gltf` formats support multiple mesh objects and hierachies, basic and [PBR textures](/docs/en-us/art/modeling/surface-appearance.md), cage mesh objects, [rigs](/docs/en-us/art/modeling/rigging.md), [avatar components](/docs/en-us/avatar.md), animation data, and [vertex colors](/docs/en-us/art/blender.md#vertex-painting). | | **Audio** | You can import audio assets in either `.ogg`, `.mp3`, `.flac`, or `.wav` format. See [Audio Assets](/docs/en-us/audio/assets.md#import-audio) for details. | | **Video** | You can import video assets in either `.mp4` or `.mov` format if all of the [requirements](/docs/en-us/ui/video-frames.md) are met. | ## Import queue If you selected multiple files with the Importer, you can use the import queue to bulk manage your various imports. ![The Importer interface, showing a preview of the mesh in the top left, a list of 3D objects on the bottom left, and a list of toggle-able properties on the right side.](../assets/modeling/meshes/3d-Import-Queue.png) The **top bar** allows you to add new files, clear the queue, search and filter, and import all enabled assets. The **import queue** lists all added files, with quick access dropdowns to change creator, presets, and file paths. ### Add files to queue To open a file browser and add additional files to the import queue, you can: - Click the **Add file** button in the top bar of the import queue window. - From Studio's **File** menu, select **Import**. ### Remove files from queue To remove all files from the queue, click the **Clear queue** button with the broom icon. To remove an individual file from the queue, right-click an item in the queue and select **Delete from queue**. ### Apply preset settings Use the **Import Preset** column to select a preset configuration to apply to your asset. For more information, see [Presets](#presets). You can also apply the settings of one item to all items by right-clicking an item with the desired settings and selecting **Apply settings to all**. ### Access individual preview and settings If you are importing a 3D model, click the individual **Asset** name of an unimported model to access the [import preview](#import-preview-models) for that item. Use the import preview to view and check your model, as well as set any individual [import settings](#import-settings). ### Import files Ensure that each item you want to import has a checkbox enabled in the first column. By default, all items are selected if they are importable. Press the **Import** button to start the import process. ## Import preview (Models) The import preview window appears if you are importing a single model file, or if you selected an individual asset from the import queue. This preview window provides individual controls for the various objects in your model. The preview window is divided into multiple sections: ![The Importer interface, showing a preview of the mesh in the top left, a list of 3D objects on the bottom left, and a list of toggle-able properties on the right side.](../assets/modeling/meshes/3d-Importer-Panels.png) The **file path** of your model file. Click **Browse** to edit. The **preset** applied to your import. Use the dropdown to select multiple presets, or the hamburger menu to set a new preset. The **3D preview** of your model. Use mouse buttons and top-right icons for various camera options and views. The **object hierarchy** of the imported object. Includes mesh objects and other supported components. The **import settings** for your import. For a complete list of settings, see [Import settings](#import-settings). ### Import settings Depending on the object selected in the hierarchy panel, the inspector panel displays the following groups of settings: - **File General**: Affects the entire imported file. - **Rig General**: Affects the selected mesh object if rigging data is present. - **File Transform**: Affects the transform properties of the imported file. - **File Geometry**: Affects the geometry properties of the imported file. - **Object General**: Affects the selected child object. - **Object Geometry**: Affects the geometry of the selected child object. #### File general The Importer provides the following settings for all meshes: | Setting | Description | | --- | --- | | Name | Sets the name of the imported asset as it will appear in your project. | | Import Only As Model | If enabled, the Importer imports the model as a single asset even if the model contains multiple children. By default, this is **enabled**.

If disabled, the Importer imports the model and all descendants, such as the multiple meshes, as individual assets. | | Upload to Roblox | If enabled, the Importer adds the model to your **Toolbox** and **Asset Manager** inventory as a new asset. By default, this is **enabled**.

If disabled, the asset is kept local to your workspace and not assigned an asset ID. This is ideal for testing and iterating on a model before integrating into your project. | | Import as Package | Adds your asset to the workspace as a [package](/docs/en-us/projects/assets/packages.md), an auto-updating, scalable, and sharable format recommended for assets that are reused or commonly redistributed. By default, this is **disabled**. | | Creator | Sets the creator, or creator group, for the asset. | | Add to Workspace | If enabled, the Importer inserts the model into the `Class.Workspace`. If importing from a saved or published experience, this setting also grants permission to the experience to use the restricted asset. By default, this is **enabled**.

If disabled, this setting only adds the model to your inventory and does **not** grant the experience permission to use the asset. | | Insert Using Scene Position | If enabled, the Importer uses the current scene position when inserting the model into the workspace. By default, this is **disabled**. | | Keep Zero Influence Bones | If enabled, the importer keeps bones that do not influence the mesh. By default, this is **disabled**.

If disabled, the importer removes zero influence bones. | | Set Pivot to Scene Origin | If enabled, the Importer sets the pivot point of the entire model to the scene origin. By default, this is **enabled**. | | Anchored | If enabled, the Importer sets the `Class.BasePart.Anchored\|Anchored` property to `true` on all the imported `Class.MeshPart\|MeshParts`. This is disabled for meshes with rig data / avatars. By default, this is **disabled**. | | Uses Cage | If enabled, the Importer finds cage meshes in the model and converts them to `WrapInstance` objects, such as `Class.WrapLayer` or `Class.WrapTarget`. If disabled, the Importer treats them as regular meshes. If the Importer initially detects cage meshes in the model, this is **enabled** by default. | #### Rig general The Importer provides the following settings for meshes with rigging data, typically character bodies or clothing accessories: | Setting | Description | | --- | --- | | Rig Type | Sets the type of rig the mesh should be associated with. The options are:
- **R15**
- **Custom**
- **No Rig**

By default, the Importer attempts to select the most appropriate setting based on the detected rigging and skinning data of the mesh. | | Validate UGC Body | After importing, Studio opens the assets in the [Avatar Setup](/docs/en-us/avatar-setup.md) tool, allowing you to quickly begin testing and uploading avatar assets to the Marketplace. | | Rig Scale | If Rig Type is set to R15, the importer provides additional options to specify [body type scaling](/docs/en-us/avatar/character-bodies/specifications.md#body-scale). The options are:
- **Default**
- **Rthro**
- **Rthro Narrow** | #### File transform The Importer provides the following settings for all meshes: | Setting | Description | | --- | --- | | World Forward | Sets the forward facing axis of the object. By default, this is set to **Front**. | | World Up | Sets the upward facing axis of the object. By default, this is set to **Top**. | #### File geometry File geometry includes information on the file dimensions and polycount of the entire model. You can edit the following settings for all meshes: | Setting | Description | | --- | --- | | Scale Unit | Sets what units the file was modeled in so that it's sized appropriately when imported. By default, this is set to **Studs**. | | Merge Meshes | If enabled, merges all MeshParts in the model into a single MeshPart and creates one MeshPart rather than a Model. By default, this is **disabled**. | | Invert Negative Faces | Inverts the orientation of negative faces in the mesh. By default, this is **disabled**. | #### Object general When selecting a specific child object of your mesh, the Importer populates Object General settings. You can edit the following settings for the selected child objects: | Setting | Description | | --- | --- | | Name | Sets the name of the selected child object within the model. | | Anchored | If enabled, sets the `Class.BasePart.Anchored\|Anchored` property to `true` on the selected child object. By default, this is **disabled**. | | Use Imported Pivot | If enabled, imports the object using the pivot point set within the child object. By default, this is **enabled**. | #### Object geometry Object Geometry includes information on the dimensions and polycount of the selected child object of the model. You can edit the following settings for the selected child objects: | Setting | Description | | --- | --- | | Make Double Sided | If disabled, sets the vertices as one-sided. One-sided vertices means that you can see through them from one side.

If enabled, sets the vertices as double-sided. Vertices that are double sided visually render on both sides. Double-sided is more performance intensive.

By default, this is **disabled**. | | Ignore Vertex Colors | If enabled, ignores the vertex color data in the child object. By default, this is **disabled**. | ## Presets You can save presets of various import settings to quickly apply later within the [import preview](#import-preview-models) or [import queue](#import-queue). By default, you can select between two presets: - **Studio Default**: The default import behavior. - **Last Imported**: Uses the settings applied to the most recent import. You can use the **⋯** menu next to the Presets dropdown for additional options: - **Reset all**: Removes any changes to the current configuration, putting settings back in the default state for the current preset. - **Save selection**: Allows you to save any new changes to the importer settings into the currently selected preset. - **Save as new**: Sets the current settings as a new preset. A dialogue box displays allowing you to set a name. - **Set as default**: Sets the current preset as the default for all imports moving forward. - **Rename**: Renames the current preset. - **Delete**: Deletes the current preset. Resets to `Studio Default` preset. ## Warnings and errors If Studio detects any issues with your model, a small indicator displays in the hierarchy panel or the settings panel. If you see a warning icon in the hierarchy, expand the hierarchy until you reach the specific child object to see more information about the issue. Warning and error messages display in the settings panel or as a tooltip. ![A warning message in orange stating an error about cages mismatching.](../assets/modeling/meshes/Warning-Settings-Panel.png)_Warning and error messages display in the appropriate section in the inspector panel._ ![An error message in red with a pop-up message about missing textures.](../assets/modeling/meshes/Warning-Mouse-Over.png)_Some errors display a tooltip when hovering over the error icon._
--- title: "Roblox Studio" url: /docs/en-us/studio last_updated: 2026-06-29T19:34:11Z description: "An overview of Roblox Studio, the all-in-one IDE for Roblox creation." --- # Roblox Studio Roblox Studio is free to use and has everything you need to start publishing creations to hundreds of millions of Roblox users on consoles, desktops, and mobile devices. [Get Studio](/docs/en-us/studio/setup.md) # Build, script, test, and publish Access a robust set of building, scripting, testing, and publishing tools directly in Studio. ### Easy 3D world building Drag and drop objects into your workspace, move and rotate to get things just right, and texture and polish your world to get the right look. [Studio tools](/docs/en-us/studio/align-tool.md) ### An intelligent script editor Syntax highlighting, code completion, and inline documentation let you script easily. [Script editor](/docs/en-us/studio/script-editor.md) ### Device emulation and playtesting Emulate a wide range of screen sizes and form factors to make sure everything looks and feels great before sharing your experience with the world. [Studio testing modes](/docs/en-us/studio/testing-modes.md) # Unleash the power of the Roblox engine Roblox Studio comes bundled with the powerful Roblox engine that runs anywhere with endless customization. ### A standard and flexible data model Data models tell the engine how to render and simulate your experience. Studio can manipulate data models at both edit and runtime, allowing you to create and iterate freely. [Data model](/docs/en-us/projects/data-model.md) ### Out-of-the-box primitives and services Construct 3d worlds that closely simulate real life with out-of-the-box primitives like simple 3D objects, physical constraints, and services for lighting, sound, and more. Choose what Roblox provides by default or customize behavior with property overrides and scripting. [See all guides](/docs/en-us/creation.md) # AI tools to accelerate creation Assistant lets you focus on creativity by using AI to automate mundane tasks, manipulate the data model, generate materials and textures, and more. ### Let Assistant take action Ask questions and get answers, or let Assistant update the data model directly. Assistant can add objects in bulk, modify scripts in place, optimize code, and much more. [Assistant](/docs/en-us/assistant/guide.md#insert-and-modify-scripts) ### Generate materials and textures Bring your experiences to life with custom materials and textures for your 3D objects. [Material generator](/docs/en-us/studio/material-generator.md) [Texture generator](/docs/en-us/studio/texture-generator.md) ### Rig and cage avatars Import character models and let Studio automatically rig and cage them to support layered clothing. [Avatar Setup tool](/docs/en-us/art/modeling/avatar-setup.md) # Collaborative at its core Studio lets you collaboratively create in real time, with small or large teams alike. ### Real-time team creation Build and script collaboratively in Studio. Watch your teammates' creations come to life before your eyes. [Studio collaboration](/docs/en-us/projects/collaboration.md) ### Group-based access Control access to your creations and control how multiple team members work on the same experiences, use the same assets, and share profits. [Groups](/docs/en-us/projects/groups.md) ### Share and access millions of assets You can create and access millions of freely available models, meshes images, sounds, and videos in the Creator Store. [Assets](/docs/en-us/assets.md) --- title: "Material Generator" url: /docs/en-us/studio/material-generator last_updated: 2026-06-29T19:34:11Z description: "The Material Generator is designed to create material variants from text entries." --- # Material Generator The **Material Generator** is designed to create material variants from text entries. Using it, you can type any phrase and hit **Generate** to see results within a few seconds. Once you find a satisfying result, you can instantly save it as a new custom material. ## Generate materials To generate materials, open the **Material Generator** from Studio's **Window** ⟩ **3D** menu, or click **Generate** from the [Material](/docs/en-us/parts/materials.md#material-widget) widget's picker popup. With the tool's window open: 1. In the text box at the top of the window, enter keywords and then click the **Generate** button. See [Best Practices](#best-practices) for guidelines. As follows are some example keyword combos and the approximate results. Note that every click of **Generate** yields different results, even with the exact same keywords. #### Example 1 "Stained Glass" #### Example 2 "Rainbow Pebbles Pile Matte Finish" #### Example 3 "Rusted Metal Charred" #### Example 4 "Japanese Cherry Blossom Fabric" 2. Click a generated image tile to view more options, as well as apply the material in "preview" mode to all selected parts. 3. Adjust the **Studs Per Tile** slider to interactively preview how the material's texture will appear on the selected parts. Additionally, test out the **Organic** toggle which makes materials appear less "repetitive" by randomizing the output._Adjustment of **Studs Per Tile** value and **Organic** toggle_ 4. When ready, choose a **Base Material** to apply that material's [default physical properties](/docs/en-us/parts/materials.md#default-physical-properties) to your custom material. Then click the **Save & Apply Variant** button to save the custom material to the [Material Manager](/docs/en-us/parts/materials.md#material-manager). ## Best practices Generating satisfying materials can be an iterative process requiring a longer list of descriptors to help focus in on the material you want. Here are some tips: - For close-up patterns, try using terms like "close up," "top down," and "texture." - For simpler repeating patterns, try using terms like "simple," "pattern," "symmetrical," and "flat." - For more control, add stylistic terms like "photorealistic," "cartoon," or "hand-drawn." - For the ability to change colors, try including terms like "grayscale" which will allow you to tint the material afterwards. --- title: "Connect to the Roblox Studio MCP server" url: /docs/en-us/studio/mcp last_updated: 2026-06-29T19:34:11Z description: "Learn how to connect your AI coding tools to Roblox Studio, enabling them to read your game structure, edit scripts, insert models, execute code, and control play mode." --- # Connect to the Roblox Studio MCP server The Roblox Studio MCP server is built into Roblox Studio. It implements the [Model Context Protocol (MCP)](https://modelcontextprotocol.io/docs/getting-started/intro), an open standard that lets AI tools securely communicate with external applications. Once connected, your AI client can interact directly with your open Studio session, exploring the data model, writing scripts, running Luau code, and testing your experience in play mode. This guide shows you how to connect the Studio MCP server to popular AI clients. While the setup varies by client, the core idea is the same: you configure your client to connect to the MCP server and then send commands from the client to your active Studio session. ## Prerequisites Before you can connect to the server, make sure you have the **latest version of Roblox Studio** and your **preferred MCP client** installed on your computer. ## How the Studio MCP server works The server runs as a local process on your machine and communicates with the AI client using **`stdio` transport**, which uses standard input/output streams. All actions are initiated through your AI client, which then sends a request through this channel to perform actions inside your Studio session. The server provides the following tools: | **Scripts** | | | --- | --- | | `script_read` | Reads a scripts from the game using dot-notation paths (for example, `game.ServerScriptService.MyScript`). Supports reading entire scripts or specific line ranges. | | `multi_edit` | Applies multiple edits to a script. If the target path doesn't exist, it creates a new script. | | `script_search` | Searches for scripts by name using fuzzy matching. Returns up to 10 results. | | `script_grep` | Searches for a string pattern across all script in the game. Returns up to 50 matches. | | **Asset and content generation** | | | `generate_mesh` | Generates a textured 3D mesh. | | `generate_material` | Generates custom material or texture. | | `generate_procedural_model` | Generates custom procedural models that scale and adapt automatically. | | `insert_from_creator_store` | Inserts assets, plugins, and models from the Creator Store. | | **Data model exploration** | | | `explore_subagent` | Investigates your place in parallel and return a compact summary without cluttering the main conversation. | | `search_game_tree` | Explores the instance hierarchy as a flat JSON array. Supports filtering by path, instance type, and keywords. | | `inspect_instance` | Returns detailed information about a specific instance, including readable properties, custom attributes, and a summary of its children and descendants. | | **Luau execution** | | | `execute_luau` | Runs Luau code in Studio. Returns either the result or an error. | | **Playtesting** | | | `start_stop_play` | Starts or stops playtesting. | | `console_output` | Retrieves output logs while the game is running. | | `screen_capture` | Captures the current Studio viewport in Play mode and returns the image data. | | `playtest_subagent` | Spawns a test character that runs through gameplay scenarios. | | **Player input simulation** | | | `character_navigation` | Moves the player character to a position or instance. | | `keyboard_input` | Simulates key presses, key holds, and text input. | | `mouse_input` | Simulates mouse clicks, movement, and scrolling. | | **Session management** | | | `list_roblox_studios` | Lists all connected Studio instances, including their name, ID, and active status. This is useful when multiple Studio windows are open. | | `set_active_studio` | Sets a Studio instance as active so that all subsequent tool calls target that instance. | ## Enable the MCP server in Studio To enable the MCP server in Studio: 1. Open **Assistant**. 2. Click **…** ⟩ **Manage MCP Servers**. 3. Turn on **Enable Studio as MCP server**. Once enabled, the settings panel displays the quick connect option and setup instructions for different clients. When a client connects successfully, a green indicator shows the number of connected clients. ## Connect your client You can connect your client to the Studio MCP server using quick connect, a JSON configuration, or a CLI command. - Use **quick connect** if your client is supported. - If not, use a **JSON configuration** if your client supports MCP config files. - Otherwise, use a **CLI command**. The Studio MCP server works with any client that supports `stdio` transport. After adding the configuration, follow your client's documentation to complete setup, then restart the client to apply your changes. > **Warning:** MCP clients can read and modify content in your open Roblox places. Make sure to only connect clients you trust. ### Quick connect Quick connect supports the following clients: - Antigravity - Codex CLI - Claude Code - Claude Desktop - Cursor - Gemini CLI - Visual Studio Code To connect using quick connect: 1. Go to **Assistant Settings** ⟩ **MCP Servers**. 2. Expand the **Quick connect** dropdown to view supported clients installed on your computer. 3. Turn on your chosen client. If the client you want doesn't appear in the **Quick connect** list, install it and restart Roblox Studio. ### JSON configuration Most MCP clients support JSON configuration files. The following examples show complete configurations you can use. If Roblox Studio is your only MCP server, use these configurations as-is. If you're using multiple MCP servers, copy the `Roblox_Studio` entry and add it to your existing `mcpServers` dictionary. > **Info:** Your `mcp.json` file might already contain other entries. When adding the `Roblox_Studio` configuration, make sure each entry is separated by a comma. Missing commas will result in invalid JSON and prevent the configuration from loading. Configurations vary by operating system: ```json { "mcpServers": { "Roblox_Studio": { "command": "cmd.exe", "args": [ "/c", "%LOCALAPPDATA%\\Roblox\\mcp.bat" ] } } } ``` ```json { "mcpServers": { "Roblox_Studio": { "command": "/Applications/RobloxStudio.app/Contents/MacOS/StudioMCP" } } } ``` ### CLI command Some MCP clients require a CLI command instead of a JSON configuration. Use the appropriate command for your operating system: ```bash cmd.exe /c %LOCALAPPDATA%\Roblox\mcp.bat ``` ```bash /Applications/RobloxStudio.app/Contents/MacOS/StudioMCP ``` ## Use multiple Studio instances You can connect a single MCP client to multiple running instances of Studio at the same time. The server automatically determines which instance to use based on context (for example, if you reference a specific experience or an object that exists only in that instance). You can manually switch instances using `list_roblox_studios` and `set_active_studio`. ## Verify your connection After setting up your client, verify that the connection is working in Roblox Studio: 1. Open **Assistant**. 2. Click **…** ⟩ **Manage MCP Servers**. 3. Under **Enable Studio as MCP server**, check for the green indicator to confirm that the client has connected successfully. ## Troubleshooting If the server is not showing up, or the tools aren't available: - Restart both Roblox Studio and your MCP client. - Verify that the command or binary path is correct and the file exists. - Check your JSON syntax. Even a missing comma or bracket can prevent the configuration from loading. --- title: "Memory usage" url: /docs/en-us/studio/optimization/memory-usage last_updated: 2026-06-29T19:34:11Z description: "Use Memory and Luau Heap tools within the Developer Console to monitor the memory usage of your experience." --- # Memory usage Every building and scripting component that you add to your experience consumes memory. When memory usage reaches engine limits, the user's device or server may crash, so you need to actively monitor usage. The **Developer Console** provides two tools for monitoring memory usage: - [Memory](#memory) — View real-time memory consumption by usage categories, including your custom components and the engine internal processes. - [Luau heap](#luau-heap) — Create snapshots of heap memory, which refers to the memory allocation of your scripts. This tool provides several views to help you identify current memory allocation and issues from different perspectives, such as object types and engine classes. It also allows you to create multiple snapshots to compare differences in memory usage over time. You can open the console during a testing or live experience session using any of the following ways: - Press `F9`. - Type `/console` into the chat. - Use the in-experience menu: 1. Open the in-experience **Roblox Menu**. 2. Select the **Settings** tab. 3. Scroll down to **Developer Console** and click **Open**. ## Memory The **Memory** tool categorizes and displays real-time memory usage. It displays memory usage in MB of each category and generates a chart on usage change over time. To view memory allocation: 1. Open the **Developer Console**. 2. Expand the tools dropdown to select **Memory**.![Dropdown menu of all Developer Console tools with the Memory option highlighted for selection.](../../assets/studio/console/Memory-Open.png) 3. Expand the client-server dropdown to select **Client** or **Server**. 4. Browse the memory usage categories and items. If you want to see usage over time, expand the category to display a chart. - **CoreMemory** — Memory usage by the core building processes of the engine, including networking, avatars, and GUI elements that you don't have direct control over. - **PlaceMemory** — Memory usage based on how you build your experience, including models, terrain, parts, scripts, and all other custom elements that you add to your experience.**###### PlaceMemory reference**| Category | Description | | --- | --- | | **HttpCache** | Assets (images, meshes, etc.) loaded from Roblox servers and now held in a cache in memory. | | **Instances** | `Class.Instance\|Instances` in the place. | | **Signals** | Signals that fire between instances (an event firing on one instance to trigger an event on another instance). | | **LuaHeap** | Heap memory for both core scripts (scripts that ship with the Roblox client) and custom scripts. | | **Script** | Luau scripts. | | **PhysicsCollision** | Collision data for physics simulations. | | **PhysicsParts** | Physics geometry and kinetics. | | **GraphicsSolidModels** | Graphics data to render solid models. | | **GraphicsMeshParts** | Graphics for `Class.MeshPart` objects. | | **GraphicsParticles** | Graphics for particle systems. | | **GraphicsParts** | Graphics for parts. | | **GraphicsSpatialHash** | General rendering. | | **GraphicsTerrain** | Graphics for terrain. | | **GraphicsTexture** | Texture memory. | | **GraphicsTextureCharacter** | Texture memory for characters. | | **Sounds** | In-memory sounds. | | **StreamingSounds** | Streaming sounds. | | **TerrainVoxels** | Terrain voxels. | | **TerrainPhysics** | Terrain physics. | | **Gui** | Memory used by common GUI elements. | | **Animation** | Memory used for animation data, such as poses and `Class.KeyframeSequence` cached data for avatar animations. | | **Navigation** | Memory used by supporting structures for `Class.PathfindingService`. | - **UntrackedMemory** — Memory allocations that the system can't easily attribute to a particular source. - **PlaceScriptMemory** — Memory usage of your scripts with insights into how individual scripts and custom memory tags contribute to overall memory usage. - **CoreScriptMemory** — Memory usage by internal engine scripts that you don't have direct control over. Among these categories, **PlaceMemory** and **PlaceScriptMemory** are the most important ones for performance optimization, because they help you understand how your building and scripting choices affect memory consumption and potential areas for optimization. For more insights into **PlaceScriptMemory**, use the [Luau heap](#luau-heap) tool to create snapshots and analyze memory allocation by different metrics. > **Info:** For more information on common memory usage problems and best practices, see [Performance Optimization](/docs/en-us/performance-optimization/improve.md#memory-usage). ## Luau heap The **Luau heap** tool allows you to create snapshots of the current allocation of heap memory, which refers to the memory Luau scripts use to store variables, tables, functions, and other runtime data structures. This tool provides several views to help you identify issues from different perspectives, such as object types and engine classes. It also allows you to create multiple snapshots to compare differences in memory usage over time. ### Create snapshots To create a snapshot of your memory allocation: 1. Open the **Developer Console**. 2. Expand the tools dropdown to select **LuauHeap**.![Dropdown menu of all Developer Console tools with the LuauHeap option highlighted for selection.](../../assets/studio/console/LuauHeap-Open.png) 3. Expand the client-server dropdown to select **Client** or **Server**. 4. Click the **Create Snapshot** button. ### Analyze memory usage The tool provides many views into Luau memory usage: - **Graph** — Shows an aggregated memory usage tree, with each node representing an object with memory allocated. - **Object Tags** — Shows memory sizes and counts by runtime types, such as `function`, `table`, and `thread`. - **Memory Categories** — Shows memory sizes and counts by engine-assigned memory categories. The engine assigns a memory category to an object at allocation time. - **Object Classes** — Shows memory sizes and counts by engine classes that your scripts use and store their instances, such as `EnumItem`, `Animation`, `CFrame`. Critically, this view does not show memory usage within the engine itself, only the Luau VM. - **Unique References** and **Unparented Instances** — Shows counts and total numbers of instances for **unique references** or **all remaining references** that don't have a parent in the data model and are only reachable by scripts. Also shows all paths that pin the instance object. #### Graph The **Graph** view is the most detailed and complex view among all Luau heap views. It shows an aggregated memory usage tree with each node representing an object with memory allocated. The tree shows how objects connect to each other and derives the shortest path between object references. It has the following columns of memory size: - Size — The self memory usage plus the memory usage by contents within the data structure. - Self — The memory directly allocated for the data structure itself, excluding the memory usage by any content it contains. > **Info:** The graph view displays elements smaller than 2 KB as a single `…` node. ![An example Graph view](../../assets/studio/console/Graph-View.jpeg) The root of the tree graph is `registry`, which stores all engine and Luau references, such as functions connected to signals or the task library, tables returned by module scripts, and global functions, tables and classes. It usually parents the following common entries: - `Module @Path.To.Module` is the table returned by a module script. - `name:123 =Path.To.Module` is a function inside a specified script. Anonymous functions don't have names. The top level node often refers to the global script function. Example: `:1= Workspace.[Username].Animate`. - `upvalue` is a reference for captured functions. See [Capture local scope](/docs/en-us/luau/scope.md#capture) for more information. - `env` refers to the environment of a function. For most cases, it's a table representing the global scope of a script. - `globals` refers to the environment of a thread. - `[key]` represents objects that serve as table keys. - `array` represents an array. - `stack` refers to the array that stores all function locals. - `constants` represents all constant values that functions use. #### Memory categories The **Memory Categories** view shows memory sizes and counts by memory categories, which the engine assigns to objects at allocation time. By default, the memory category has the same name as the script, or you can assign custom memory category names using the `debug.setmemorycategory` function. #### Unique references and unparented instances The **Unique References** and **Unparented Instances** views are related. Both show memory usage of instances that don't have a parent in the data model and are only reachable by scripts, along with all paths that pin the instance object. If an instance is only referenced within Luau scripts, it appears under **Unique References**. Otherwise, it's also in use by Roblox systems and appears under **Unparented Instances**. Both views show two metrics: - **Count** — The number of instances with the same name that are reachable from the same path, such as multiple instances named `Dragon` in the same table. - **Total Instances** — The total number of objects inside those roots, such as all parts, scripts, and sound objects that construct a `Dragon` instance. ![An example Unique Reference view](../../assets/studio/console/Unique-Reference-View.jpeg) These views are useful for identifying unnecessary connected instances, which you need to disconnect when you no longer need them. If you see many unexpected instances in this view, check out the paths holding them and evaluate whether they are necessary. --- title: "Script Profiler" url: /docs/en-us/studio/optimization/scriptprofiler last_updated: 2026-06-29T19:34:11Z description: "Script Profiler is a tool within the Developer Console that records profiling sessions of all running scripts." --- # Script Profiler **Script Profiler** is a tool within the [Developer Console](/docs/en-us/studio/developer-console.md) that allows you to record profiling sessions of all running scripts and view their CPU time costs with custom recording and display settings. It can record all types of function calls, including Luau functions, method calls, and property accesses. This tool is helpful for identifying scripts that take up the most CPU resources and slow down the performance. > **Info:** Script Profiler only records function calls that consume CPU resources, so it doesn't record threads that sleep or wait for results. ## Record profiling sessions Before recording, you need to select the recording environment from: - **Client** (Default) — Records client-side scripts, including `Class.LocalScript|LocalScripts` and `Class.Script|Scripts` with `Class.BaseScript.RunContext|RunContext` set to `Enum.RunContext.Client|Client`. - **Server** — Records server-side scripts, including `Class.Script|Scripts` with `Class.BaseScript.RunContext|RunContext` set to `Enum.RunContext.Server|Server` or `Enum.RunContext.Legacy|Legacy`. Script Profiler clears all collected server-side data at the end of each session. You can also set the following recording options: | Behavior | Options | Default | Description | | --- | --- | --- | --- | | Frequency | 1000 times per second (1 KHz)
10,000 times per second (10 KHz) | 1 KHz | The 10 KHz frequency has higher precision, as Script Profiler might not pick up API calls that execute more frequently than your selected frequency, but it also has a higher performance cost. | | Session Length | 1-minute
5-minute
10-minute
Manual | Manual | The manual option requires you to stop recording manually. | | Live Polling Behavior | On
Off | Off | This behavior polls and refreshes profiling data each second during a profiling session. | To record a new profiling session: 1. Open [Developer Console](/docs/en-us/studio/developer-console.md#opening-the-developer-console). 2. Expand the tools dropdown to select **ScriptProfiler**.![Dropdown menu of all Developer Console tools with the ScriptProfiler option highlighted for selection.](../../assets/studio/console/ScriptProfiler-Open.png) 3. Expand the client-server dropdown to select **Client** or **Server**.![Dropdown menu with Client and Server options for selection.](../../assets/studio/console/Client-Server-Dropdown.png) 4. (Optional) Check the **Live** checkbox to enable the live polling behavior. 5. (Optional) Select **Freq** and **Time** to choose the recording frequency and session time length if you don't want to use the default values. 6. Click **Start** to begin the profiling session. If you set a duration, Script Profiler displays a countdown timer with the remaining time in the session. 7. Click **Stop** or wait until the recording finishes to display the profiling data. ## Read profiling data After a session stops, Script Profiler generates a table showing how much time each function call costs in CPU time. The table sorts function calls from the most-time-spent to least-time-spent, and allows you to search for specific functions by their name. It provides the following two views: - **Callgraph** (Default): Categorizes and displays function calls into a tree structure based on frame tasks. This view displays each task category as nodes under the same root and allows you to expand them to view functions. You can also hover over any node in the tree to view file and line information. For example, **Stepped/CameraInput/** might reveal `Players.[LocalPlayer].PlayerScripts.PlayerModule.CameraModule.CameraInput:125`. ![Example callgraph view of a profiling session.](../../assets/studio/console/Callgraph.jpeg) - **Functions**: Lists all functions without categorizing them by tasks. ![Example functions view of a profiling session.](../../assets/studio/console/Functions.jpeg) You can also select from the following display options to tailor your debugging needs: | Name | Options | Default | Description | | --- | --- | --- | --- | | Unit | Milliseconds (ms)
Percentage (%) | ms | Displays time spent on each API call in milliseconds or percentages of the total recording session. | | Average | Off
1-second
1-minute
5-minute
10-minute | Off | Calculates the average time spent on each API call by the selected value. If you select an option that is longer than the session length, Script profiler extrapolates the session length to calculate the average. For example, you can select the 5-minute option for a 1-minute session to see the expected average value if you run the code for 5 minutes. | ## Export profiling data Script Profiler allows you to export recorded profiling data as a JSON file. To export recorded data after a profiling session: 1. On the Script Profiler window, click **Export**. 2. On the export window, select the profiling session that you want to export. Rename the default file name if you want to set a custom name. 3. Click **Export** to save the JSON file.![Example export window.](../../assets/studio/console/Export-Window.jpeg) The exported JSON file includes the following fields: - **Version**: The version number. - **SessionStartTime**: A timestamp in milliseconds that records the session start time. - **SessionEndTime**: A timestamp in milliseconds that records the session end time. - **Categories**: An array of frame task categories recorded in the profiling session. Each entry includes: - **Name**: The name of each frame task category. - **NodeId**: The unique identifier of a task category (node). It's a 1-based index into the `Nodes` array. For example, you can look up the node with the `NodeId` of `123` by retrieving the 123rd element in `Nodes`. - **Nodes**: An array of nodes recorded in the profiling session. Each entry includes: - **TotalDuration**: The amount of time that the node costs in CPU time in microseconds. - **FunctionIds**: An array of unique identifiers of functions. - **NodeIds**: An array of Node IDs. - **Functions**: An array of functions recorded in the profiling session. - **TotalDuration**: The amount of time that the function costs in CPU time in microseconds. - **Name**: The name of the function, if available. - **Source**: The source of the function, if available. - **Line**: The line number of the function, if available. - **Flags**: A bit field that indicates any specific function execution environment. Currently can have the following values: - `0`: The 0th bit represents `IsNative` for execution under Native CodeGen. - `1`: The 1 th bit represents `IsPlugin` for execution as part of a plugin. ```json { "Version":2, "SessionStartTime":1704850750514, "SessionEndTime":1704850751198, "Categories": [ {"Name":"Parallel Luau","NodeId":4}, {"Name":"Heartbeat","NodeId":1} ], "Nodes": [ {"TotalDuration":2530,"FunctionIds":[1],"NodeIds":[2]}, {"TotalDuration":2530,"FunctionIds":[2,5],"NodeIds":[3,7]}, {"TotalDuration":1267}, {"TotalDuration":7746,"FunctionIds":[3],"NodeIds":[5]}, {"TotalDuration":7746,"FunctionIds":[4],"NodeIds":[6]}, {"TotalDuration":7746}, {"TotalDuration":1263,"FunctionIds":[6],"NodeIds":[8]}, {"TotalDuration":1263,"FunctionIds":[7],"NodeIds":[9]}, {"TotalDuration":1263,"FunctionIds":[8],"NodeIds":[10]}, {"TotalDuration":1263} ], "Functions": [ {"Name":"main","TotalDuration":2530}, {"Source":"builtin_ManageCollaborators.rbxm.ManageCollaborators.Packages._Index.roblox_rodux-3.0.0.rodux.Store","Line":81,"TotalDuration":1267}, {"Name":"Script","TotalDuration":7746}, {"Source":"Workspace.Actor.Script","Line":1,"TotalDuration":7746}, {"Source":"builtin_DeveloperInspector.rbxm.DeveloperInspector.Packages._Index.DeveloperFramework.DeveloperFramework.UI.Components.Grid","Line":221,"TotalDuration":1263}, {"Source":"builtin_DeveloperInspector.rbxm.DeveloperInspector.Packages._Index.DeveloperFramework.DeveloperFramework.UI.Components.Grid","Name":"_update","Line":236,"TotalDuration":1263}, {"Source":"builtin_DeveloperInspector.rbxm.DeveloperInspector.Packages._Index.DeveloperFramework.DeveloperFramework.UI.Components.Grid","Name":"_getRange","Line":277,"TotalDuration":1263}, {"Source":"[C]","Name":"ScrollingFrame.CanvasPosition","TotalDuration":1263} ] } ```
--- title: "Output" url: /docs/en-us/studio/output last_updated: 2026-06-29T19:34:11Z description: "The Output window in Roblox Studio displays error messages, calls to print(), and calls to warn()." --- # Output The **Output** window, accessible from Studio's **Window** menu or **Script** tab toolbar, displays errors captured from running scripts, messages from Roblox Engine, messages from calls to `print()`, and errors from calls to `warn()`. Plugins can interact with the **Output** window through `Class.LogService` which can record and clear the window's contents. You can customize the output through the following elements: ![Sections of Output window indicated by lettered pointers](../assets/studio/general/Output-Window-Diagram.png) Filters output by type, such as **Error** or **Warning**. Filters output by context, such as **Client**, **Server**, or **User Plugin**. Filters output by the text entered into the field, such as an object name. Clears all output messages from the window. This action has a shortcut of `Ctrl``K` on Windows or `⌘``K` on Mac. Controls the level of detail displayed for each output message. - **Show Timestamp** — Shows a detailed timestamp in `HH:MM:SS.SSS` format. - **Show Context** — Shows the message context, such as **Client** or **Server**. - **Show Source** — If applicable, shows the associated script name and line number. - **Show Tables Expanded by Default** — For outputted tables, contents are expanded by default. - **Show Memory Address for Expandable Tables** — For expandable tables, shows the internal memory address, such as `0xb4f0417581144ec5`. - **Log Mode** — Simplifies output by removing options, such as expandable trees. --- title: "Pivot tools" url: /docs/en-us/studio/pivot-tools last_updated: 2026-06-29T19:34:11Z description: "Pivot tools and properties give you full control over the points around which objects rotate and translate." --- # Pivot tools The **pivot** tools and related properties give you full control over the points around which objects rotate and translate. ## Interactive Studio tools The interactive Studio tools are located in the **Model** tab toolbar. ![Pivot tool highlighted in Studio's toolbar.](../assets/studio/general/Toolbar-Pivot.png) When **Pivot** is toggled on, you can move or rotate the pivot point of a [part](/docs/en-us/parts.md) or [model](/docs/en-us/parts/models.md). Once set, rotation and scaling occur around the pivot point. For more information, see [setting a primary part](/docs/en-us/parts/models.md#set-a-primary-part). The following diagrams show example pivot locations in relation to object bounds. #### Center ![A blue cube with its pivot point set in the center of the cube. A green circle represents the object's rotation in relation to its pivot point.](../assets/studio/pivot-tools/Edit-Pivot-Center-A.jpg) #### Corner ![A blue cube with its pivot point set on a corner of the cube. A green circle represents the object's rotation in relation to its pivot point.](../assets/studio/pivot-tools/Edit-Pivot-Corner-A.jpg) #### Outside Object Bounds ![A blue cube with its pivot point set on outside of the cube's geometry. A green circle represents the object's rotation in relation to its pivot point.](../assets/studio/pivot-tools/Edit-Pivot-Outside-A.jpg) When you rotate the pivot of a [model](/docs/en-us/parts/models.md), the bounding box of the model also rotates. #### Orientation = (0, 90, 0) ![A model of a purple, blue, and red block with its pivot point set on a the edge of the model's bounding box. A green circle represents the object's rotation in relation to its pivot point.](../assets/studio/pivot-tools/Edit-Pivot-Model-A.jpg) #### Orientation = (0, 135, 0) #### Orientation = (0, 180, 0) You can specify a `Class.Model.PrimaryPart|PrimaryPart` for models, which is a `Class.BasePart` within the model that you can use as a positional basis. In terms of pivot points: 1. When you assign a `Class.Model.PrimaryPart|PrimaryPart` to a model, the existing pivot changes to the pivot point of the `Class.Model.PrimaryPart|PrimaryPart`. If you later unassign/clear the `Class.Model.PrimaryPart|PrimaryPart`, the pivot point resets to the **center** of the model's bounding box. 2. If you **delete** the `Class.Model.PrimaryPart|PrimaryPart` from a model, the pivot point remains in the same location and does **not** revert to its previous position. This prevents a sudden "jump" if you delete any parts from the model. #### Initial Pivot Point #### A. PrimaryPart Assigned #### B. PrimaryPart Deleted ### Snap The **Snap** checkbox, available by click‑holding the small corner arrow on the **Pivot** button, toggles whether the pivot point snaps to **hotspots** such as corners, edges, or centers when you move it with the **Edit Pivot** tool. When snapping is enabled, hotspots display as small magenta points. _Hotspots on a part_ _Hotspots on a model_ ### Reset The **Reset** button moves the pivot point to the **center** of an object or model's bounding box. This operation is useful after you've changed the composition of a model and wish to place its pivot point at the center of its new bounding box. ![Pivot Reset tool highlighted in Studio's toolbar.](../assets/studio/general/Toolbar-Pivot-Reset.png) ## Pivot properties In addition to the interactive tools, pivot values can be directly set in the [Properties](/docs/en-us/studio/properties.md) window. | Property | Description | | --- | --- | | **Origin Position** | Current world location of the object based on its pivot point rather than its bounding box. Useful for setting the precise location of a `Class.Model`. | | **Origin Orientation** | Current orientation of the object based around its pivot point rather than its bounding box. | | **Pivot Offset Position** | Precise location of the pivot relative to the object.¹ | | **Pivot Offset Orientation** | Precise pivot orientation.¹ | | **World Pivot Position** | The precise world location of the pivot for `Class.Model\|Models` without a `Class.Model.PrimaryPart\|PrimaryPart`. If a `Class.Model.PrimaryPart\|PrimaryPart` is set, the model will use **Pivot Offset Position** since the pivot becomes relative to that part.¹ | | **World Pivot Orientation** | The precise world orientation for `Class.Model\|Models` without a `Class.Model.PrimaryPart\|PrimaryPart`. If a `Class.Model.PrimaryPart\|PrimaryPart` is set, the model will use **Pivot Offset Orientation** since the pivot becomes relative to that part.¹ | _1 Changing this property will not move or rotate the object._## Manipulate pivots in scripts Beyond the Studio tools, pivot points can be accessed and manipulated in scripts. | Function or Property | Description | | --- | --- | | `Class.PVInstance:GetPivot()\|GetPivot()` | Queries the current world location of an object's pivot as a `Datatype.CFrame`. This returns an object's position in 3D space regardless of whether it's an individual part or a `Class.Model`. | | `Class.PVInstance:PivotTo()\|PivotTo()` | Moves the object such that its pivot will be located at the specified `Datatype.CFrame`. This allows for uniform object movement of both individual parts and models. | | `Class.BasePart.PivotOffset` | The `Datatype.CFrame` which specifies the pivot offset; effectively the offset of the pivot from the `Datatype.CFrame` of the `Class.BasePart`.² | | `Class.BasePart.PivotOffset\|Model.WorldPivot` | For `Class.Model\|Models` without a `Class.Model.PrimaryPart\|PrimaryPart`, the `Datatype.CFrame` which specifies the model's pivot in world space. If a `Class.Model.PrimaryPart\|PrimaryPart` is set, the model will use `Class.BasePart.PivotOffset` since the pivot becomes relative to that part.² | _2 Changing this property will not move or rotate the object._ --- title: "Studio plugins" url: /docs/en-us/studio/plugins last_updated: 2026-06-29T19:34:11Z description: "Explains how to create, publish, and monetize extensions to Studio that add custom functionality." --- # Studio plugins A **plugin** is an extension that adds additional features or functionality to Studio. You can [install](/docs/en-us/production/creator-store.md#find-assets) community-made plugins from the Creator Store, or you can [create](#create-new-plugins) and [publish](#upload-plugins) your own to the [Toolbox](/docs/en-us/projects/assets/toolbox.md) to use across your experiences. If you choose to also distribute your plugins to the Creator Store, you can either offer them for free or sell them for **United States Dollars**. Roblox offers a market-leading revenue share for these sales, as only taxes and payment processing fees are deducted. For more information on selling plugins, see [Creator Store - Distribute and sell assets](/docs/en-us/production/creator-store.md#distribute-and-sell-assets). ## Create new plugins You can create your own plugins to improve your workflow in Studio. The following code sample is a plugin called **AddEmptyScript** that inserts an empty `Class.Script` as the child of an `Class.Instance` or in `Class.ServerScriptService`. The following sections explain the major parts to creating this plugin. To begin, you should enable **Plugin Debugging Enabled** in the **Studio** section of Studio's settings. This will expose the `Class.PluginDebugService` in Studio which provides real-time debugging for your plugin's code and makes it easier to reload and save your plugin. ```lua local ServerScriptService = game:GetService("ServerScriptService") local Selection = game:GetService("Selection") -- Create a new toolbar section and Plugins menu folder titled "Custom" local toolbar = plugin:CreateToolbar("Custom") -- Add a toolbar button labeled "Empty Script" local newScriptButton = toolbar:CreateButton("Empty Script", "Create an empty script", "rbxassetid://14978048121") -- Make button clickable even if 3D viewport is hidden newScriptButton.ClickableWhenViewportHidden = true local function onPluginButtonClicked() local selectedObjects = Selection:Get() local parent = selectedObjects[1] or ServerScriptService local newScript = Instance.new("Script") newScript.Source = "" newScript.Parent = parent end newScriptButton.Click:Connect(onPluginButtonClicked) ``` ### Save plugin script To create a plugin, first create a `Class.Script` and save it locally. 1. Insert a new `Class.Script` inside `Class.ServerStorage` and rename it to `AddEmptyScript`. 2. Copy and paste the **AddEmptyScript Plugin** code above into the new script. 3. With the new script selected in the **Explorer** window, select **Save as Local Plugin** from Studio's **Plugins** menu. 4. In the popup window, click **Save** to insert the plugin script into your local **Plugins** folder of the Studio installation. 5. The plugin should appear in `Class.PluginDebugService` and start running. 6. **IMPORTANT** Delete the original script in `Class.ServerStorage` and work from the `AddEmptyScript` plugin inside `Class.PluginDebugService`, otherwise you may end up applying changes to the wrong script. > **Info:** To update a specific plugin within `Class.PluginDebugService`, right‑click it and select **Save and Reload Plugin** from the context menu. If you simply want to reload the plugin, for example to step through a section of code using a breakpoint without saving the plugin, right‑click it and select **Reload Plugin**. > > At the broadest level, you can also update **all** plugins by right-clicking `Class.PluginDebugService` and selecting **Save and Reload All Plugins in Debugger** (shortcut `Ctrl``Shift``L` or `⌘``Shift``L`). ### Add toolbar button To add a button for your plugin to the **Plugins** tab of the Studio toolbar, use the `Class.Plugin:CreateToolbar()` and `Class.PluginToolbar:CreateButton()` methods. In the code for `AddEmptyScript`, line 5 creates a new section in the toolbar and **Plugins** menu folder titled **Custom**, while line 8 creates a button labeled **Empty Script**. ![New plugin button added to toolbar in Studio](../assets/studio/general/Toolbar-Custom-Plugin-Button.png) ### Execute code on click To make the plugin execute code when a user clicks the toolbar button, connect a function to the button's `Class.PluginToolbarButton.Click` event. In the code for `AddEmptyScript`, the connecting function is `onPluginButtonClicked()`. ### Check user selection To modify a plugin's behavior based on what the user has selected, use the `Class.Selection` service. The `onPluginButtonClicked()` function checks if the user has anything selected and creates the new script as its child instead of inside `Class.ServerScriptService`. If the user doesn't have anything selected, it creates the new script in `Class.ServerScriptService`. ### Support undo and redo Use `Class.ChangeHistoryService` to allow users to undo and redo changes made by a plugin within an experience. In your script, set the plugin to call `Class.ChangeHistoryService:TryBeginRecording()|TryBeginRecording()` and store the identifier assigned to the API call before making changes. Then set the plugin to call `Class.ChangeHistoryService:FinishRecording()|FinishRecording()` after making changes, so it captures any changes made during the recording session for undo and redo. The following code sample creates an example plugin that can apply the neon material to selected parts. It uses `Class.ChangeHistoryService` to record and manage the changes made by the plugin: ```lua local ChangeHistoryService = game:GetService("ChangeHistoryService") local Selection = game:GetService("Selection") -- Create an example plugin local toolbar = plugin:CreateToolbar("Example Plugin") local button = toolbar:CreateButton("Neon it up", "", "") -- Connect a function to the click event button.Click:Connect(function() -- Try to begin a recording with a specific identifier local recording = ChangeHistoryService:TryBeginRecording("Set selection to neon") -- Check if recording was successfully initiated if not recording then -- This indicates that your plugin began a previous recording and never completed it -- You may only have one recording per plugin active at a time return end -- Iterate through the selected instances for _, instance in Selection:Get() do -- Check if the instance is a BasePart if instance:IsA("BasePart") then instance.Material = Enum.Material.Neon -- Set the material of the part to Neon end end -- Finish the recording, committing the changes to the history ChangeHistoryService:FinishRecording(recording, Enum.FinishRecordingOperation.Commit) end) ``` > **Warning:** Note the following caveats with `Class.ChangeHistoryService` recordings: - If you're using the [Multi-line Command Bar](/docs/en-us/studio/ui-overview.md#command-bar), don't run any code in the command bar that will always be running while you have a place open, since the command bar uses `Class.ChangeHistoryService` recordings to allow for undo/redo with commands that make changes to the `Class.DataModel`. - Reloading a plugin won't cancel its ongoing `Class.ChangeHistoryService` recordings, so make use of [attributes](/docs/en-us/scripting/attributes.md#create-attributes) to store the last recording identifier so it can be canceled by the plugin when it reloads. ## Upload plugins After you finish creating a plugin, you can upload it to your inventory so that it is accessible across all of your projects. To upload a plugin: 1. In the **Explorer** window, select a plugin script. 2. Navigate to Studio's **Plugins** menu, then select **Publish as Plugin**. 3. **OPTIONAL** In the upper-left corner of the asset configuration window, click the default puzzle piece image to upload a 512×512 image thumbnail for your plugin. 4. Fill in the following fields: - **Name** — A title for your plugin. - **Description** — A description that describes what a potential user can expect the plugin to do. - **Creator** — The creator or group that you want to attribute as the creator of the plugin. 5. Click the **Submit** button. Your plugin is now available to you in the [Toolbox](/docs/en-us/projects/assets/toolbox.md) under the **Inventory** and **Creations** tabs. ## Distribute and sell plugins You can publicly distribute and sell your own plugins on the Creator Store for others to use within their own game development. This monetization method lets you earn 100% of net proceeds on transactions, bypassing platform fees and DevEx rates. For more information on this process, including how to set up a seller account to set prices and receive payouts, see [Creator Store - Requirements](/docs/en-us/production/creator-store.md#requirements). --- title: "Properties window" url: /docs/en-us/studio/properties last_updated: 2026-06-29T19:34:11Z description: "The Properties window lets you adjust properties of a selected object to change how it looks and behaves." --- # Properties window The **Properties** window, accessible from Studio's **Window** menu or **Home** tab toolbar, allows you to adjust certain properties of a selected object to change how the object looks and behaves. Additionally, you can manage [tags](#instance-tags) and configure instance [attributes](#instance-attributes) at the bottom of the window. ![Properties highlighted in Studio's toolbar.](../assets/studio/general/Toolbar-Properties.png) When you select an object, the window's header bar changes to reflect the object's class and name, and its properties are divided into sections. For example, a `Class.MeshPart` includes sections like **Appearance** and **Transform** which respectively allow you to change its appearance and transform its `Class.BasePart.Size|Size`, `Class.BasePart.CFrame|CFrame`, and origin. > **Info:** You can collapse or expand any section or subsection by clicking the small arrow to the left of its name. After you expand or collapse a section, it remains expanded or collapsed for other objects of the same class, as well as related objects that share the same property grouping. Some properties are collapsed by default, but you can expand them by clicking the small arrow next to its name. For instance, `Class.BasePart|BaseParts` contain a `Class.BasePart.Position|Position` property which represents an **X**, **Y**, **Z** coordinate in the 3D world. In Studio, these coordinates can be entered in two ways: _All three coordinates typed into the parent **Size** field, separated by commas_ _Property expanded and an individual size vector typed into the **X**, **Y**, and **Z** fields_ > **Info:** After you expand or collapse a property, it remains expanded or collapsed for other objects of the same class, as well as related objects that share the same property. ## Property filter You can filter properties by typing a search query into the **Filter Properties** input near the top of the window. For example, filtering by the letters "velo" finds all properties containing them, such as **AssemblyLinearVelocity** and **AssemblyAngularVelocity**. Quickly access this input by pressing `Ctrl``Shift``P` (Windows) or `⌘``Shift``P` (Mac). ## Instance tags The **Tags** section allows you to apply specific tags for use with `Class.CollectionService`. Tags are sets of strings applied to instances that replicate from the server to the client. They are also serialized when places are saved. To add tags to an instance through the **Properties** window: 1. Select the instance, scroll to the **Tags** section, and click the **+** button. 2. In the popup window, enter a new tag name **or** select a tag you've already created for any other instance from the list below the input field. The tag will appear within the **Tags** section. 3. If necessary, you can remove an existing tag by clicking the **×** icon. ## Instance attributes **Attributes** allow you to customize instances with your own data. They are similar to built-in object properties, but you can create and modify your own attributes for any instance. Key features include: - Attributes can be created, edited, and deleted directly within Studio or through scripting (see [properties and attributes](/docs/en-us/scripting/attributes.md) for script commands to create, set, get, and delete attributes). - Attributes and their values are saved with your place and assets. - Value changes can be viewed in real time and are **replicated** so that clients can access them immediately. You can store the following types/values in attributes: - [string](/docs/en-us/luau/strings.md) - [boolean](/docs/en-us/luau/booleans.md) - [number](/docs/en-us/luau/numbers.md) - `Datatype.UDim` - `Datatype.UDim2` - `Datatype.BrickColor` - `Datatype.Color3` - `Datatype.Vector2` - `Datatype.Vector3` - `Datatype.CFrame` - `Datatype.NumberSequence` - `Datatype.ColorSequence` - `Datatype.NumberRange` - `Datatype.Rect` - `Datatype.Font` New attributes can be created and modified in Studio as follows: 1. Select the instance, scroll to the **Attributes** section, and click the **+** button. 2. In the popup window, enter the attribute **Name**, select its **Type**, and click **Save**. 3. The new attribute will appear with a default value that you can change just like any other property. 4. If necessary, you can rename or delete an attribute by clicking the gear icon. ## Configurable defaults The **configurable defaults** feature allows you to save properties from a selected `Class.Instance` as the new default for that type, reset them as needed, and share them from a local file if desired. Use cases include: - A default `Class.MeshPart` instance where `Class.BasePart.Anchored|Anchored` is `true`, so that every new `Class.MeshPart` inserted into the 3D workspace is anchored. - A set of default [mechanical constraints](/docs/en-us/physics/mechanical-constraints.md) with physical behaviors that match the type of mechanisms you're currently building, such as massive vehicles vs. smaller machines with less mass. - A set of default [light](/docs/en-us/effects/light-sources.md) sources (`Class.PointLight`, `Class.SpotLight`, `Class.SurfaceLight`), each with `Class.Light.Brightness|Brightness` that suits the environment you're designing. To **set** a specific instance as the default: 1. Select the instance in the 3D viewport or [Explorer](/docs/en-us/studio/explorer.md) hierarchy. 2. From Studio's main **Edit** menu, select **Save as Default [Instance]**. A toast notification will indicate that the default has been updated, and newly inserted instances of that type will match the default properties. > **Info:** Instance defaults are saved **locally** on each machine to a `DefaultInstances.rbxm` file, not to your Roblox or group account. The location of this directory can be changed in Studio's settings (`Alt``S` or `⌥``S`) under the **Studio** tab ⟩ **Instance Defaults Dir**. To **reset** a configurable default instance: 1. Select any instance of that type in the 3D viewport or [Explorer](/docs/en-us/studio/explorer.md) hierarchy. 2. From Studio's main **Edit** menu, select **Reset Default [Instance]**. Existing instances of that type will retain their properties, but newly inserted instances will use Studio's default properties. > **Warning:** Note the following exceptions and behavioral rules to default instances: - The **positional** instance properties `Class.BasePart.Position`, `Class.BasePart.CFrame`, `Class.PVInstance.Origin`, and `Class.Model.WorldPivot` are **not** affected by default properties, but rather are overridden by Studio insertion rules and workflows. However, `Class.GuiObject.Position` and positional properties for `Class.Attachment|Attachments` (`Class.Attachment.Position|Position`, `Class.Attachment.WorldCFrame|WorldCFrame`, `Class.Attachment.WorldPosition|WorldPosition`) are stored/maintained in configured defaults of those instance types. - Studio's **Group as Model** (`Ctrl``G` or `⌘``G`) and **Group as Folder** (`Ctrl``Alt``G` or `⌘``⌥``G`) actions do **not** use the instance defaults for `Class.Model` or `Class.Folder`, respectively. --- title: "Rig Generator" url: /docs/en-us/studio/rig-builder last_updated: 2026-06-29T19:34:11Z description: "The Rig Generator tool lets you insert pre-built character models." --- # Rig Generator The **Rig Generator** tool lets you quickly insert basic pre-built character rigs into your experience. You can also insert a rig of your account's avatar character, including character customizations and accessories. The tool inserts character `Class.Model|Models` with the appropriate joints and humanoid structure to support [animation](/docs/en-us/animation.md), [accessories](/docs/en-us/avatar/rigid-accessories.md), and [character customizations](/docs/en-us/characters/appearance.md). 1. From the toolbar's **Avatar** or **Home** tab, click **Character**.![Character button highlighted in Studio's toolbar.](../assets/studio/general/Toolbar-Character.png) 2. From the popup, select from the available options. You can insert either a legacy **R6** rig consisting of 6 mesh objects, or an **R15** rig which consists of 15 mesh objects and significantly expands upon the R6's limited motion range. > **Warning:** Note that you can only equip clothing and other layerable accessories to **R15** models. 3. Choose a body shape of either **Masculine** or **Feminine** and then click one of the style buttons. The rig displays both in the 3D viewport and in the [Explorer](/docs/en-us/studio/explorer.md) window under the name **Rig**. --- title: "Script Editor" url: /docs/en-us/studio/script-editor last_updated: 2026-06-29T19:34:11Z description: "Roblox's built-in, fully-featured script editor includes modern conveniences like autocomplete, code highlighting, and multi-cursor editing." --- # Script Editor The **Script Editor** in Studio is the primary tool for scripting on Roblox. It's a self-improving environment that can help you write high-impact code, shorten your development time, and iterate on your experiences. It can improve your scripting experience by: - Formatting and highlighting syntax in your code. - Offering ways to [autocomplete](#autocomplete-features) phrases in your code as you type. - Helping you [navigate code](#code-navigation) by jumping to variable and function declarations. - Helping you [find and replace](#find-and-replace) code in open scripts or all scripts. - Providing [real-time feedback](#real-time-feedback) on your code quality and performance. The Script Editor supports all types of [scripts](/docs/en-us/scripting.md) and opens automatically when you create a new script or double-click an existing script in the [Explorer](/docs/en-us/studio/explorer.md) window. > **Success:** You can [customize](#editor-settings) the Script Editor to suit your preferences and workflows, including font family/size, formatting behavior, and colors for syntax highlighting. You can also toggle features such as [autocomplete](#autocomplete-features), and [script analysis](#script-analysis). ## Autocomplete features The Script Editor's autocomplete features generate code-related information that can improve your programming efficiency, such as: - **Informed suggestions** on how to complete phrases as you type them that are contextual to the experience's [data model](/docs/en-us/projects/data-model.md). For example, if you have a `Class.Model` in `Class.Workspace` called **RocketShip**, autocomplete suggests `RocketShip` when you type `workspace.roc` and indicates that it's a `Class.Model`.![Autocomplete showing suggestions based on the experience's data model](../assets/studio/script-editor/Autocomplete-Data-Model.png) > **Info:** Use the `↑` and `↓` keys to browse the suggestions, then press `Tab` or `Enter` to accept a suggestion and insert the complete phrase. - **Autofill names** for variables and functions that you declare, helping you avoid pesky typos.![Autocomplete showing suggestions based on a previously declared variable.](../assets/studio/script-editor/Autocomplete-Variable.png) - **Documentation pop-ups with code samples** that are similar to those on the [Engine API Reference](/docs/en-us/reference/engine.md), giving you context on the API's usage.![Autocomplete showing suggestions based on a Roblox Engine API.](../assets/studio/script-editor/Autocomplete-API.png) - **On-hover tooltips** that you can customize with your own defined documentation.![Autocomplete showing custom documentation.](../assets/studio/script-editor/Autocomplete-CustomDocs.png) - **On-hover script analysis diagnostics** with information you can use to troubleshoot errors.![Autocomplete showing diagnostic information.](../assets/studio/script-editor/Autocomplete-Diagnostics.png) - **Function signatures** when you type an argument, providing you a reference for its parameters and return values.![Autocomplete showing function signature](../assets/studio/script-editor/Autocomplete-Signature.png) ## Code navigation ### Go to declaration You can jump to the declaration of a function or variable by holding `Ctrl` on Windows or `⌘` on Mac when clicking the call, or by right-clicking its call and clicking **Go to Declaration**. ![Go to Declaration workflow on a declared function](../assets/studio/script-editor/Go-To-Declaration.png) ### Script function filter The **Script Function Filter** displays a list of all functions declared in a script. To open it, press `Alt``F` on Windows or `⌥``F` on Mac. With the list open, you can browse the signatures for each function, filter through them by name, and double-click one to jump to its declaration. ![Script Function Filter showing all functions inside a script](../assets/studio/script-editor/Script-Functions-Filter.png) ## Find and replace The **Find/Replace** widget lets you find and replace code in an open script. The widget supports matching case, matching the whole word, and searching by regular expressions. To open it, press `Ctrl``F` on Windows or `⌘``F` on Mac. ![Find/Replace widget labeled](../assets/studio/script-editor/Find-Replace-Widget-Labeled.png) > **Info:** For broader searches, the **Find All / Replace All** window lets you find/replace code across multiple scripts in the experience. To open it, press `Ctrl``Shift``F` on Windows or `⌘``Shift``F` on Mac. ## Real-time feedback ### Script Analysis The **Script Analysis** window, accessible through the **Analysis** button in Studio's **Script** tab toolbar, performs static analysis on your scripts and displays active errors and warnings. For more information on the errors and warnings, see the [Luau linting](https://luau.org/lint) documentation. ![Script with various marked errors](../assets/studio/script-editor/Script-Analysis-1.png)_Script errors underlined in Script Editor_ ![Script Analysis window with details on marked errors from script](../assets/studio/script-editor/Script-Analysis-2.png)_Errors explained in Script Analysis window_ ### Output The [Output](/docs/en-us/studio/output.md) window displays errors captured from running scripts, messages from the Roblox engine, messages from calls to `print()`, and errors from calls to `warn()`. ## Code Assist **Code Assist** is a feature that suggests lines or functions of code as you type, helping you code more efficiently and stay focused. Based on contexts from your comment and code, suggestions will be triggered in two ways: - **Automatically** when you pause on a line for a few seconds and the AI model has enough context for a suggestion. - **Manually** with shortcut `Alt``∖` on Windows or `⌥``∖` on Mac. Press `Tab` to accept a suggestion, or ignore it by continuing to type. Currently, your script needs to contain at least a few lines of code to trigger a suggestion. > **Warning:****Code Assist** helps automate basic coding tasks so you can focus on creative work, but it does not always suggest perfect code (see [Limitations](#limitations)). It's still your responsibility to review, test, and determine if the code suggestion is contextually appropriate. ### Improve suggestions To get more accurate and relevant suggestions, it's recommended that you follow clean coding practices, regardless of assistance, and: - Break down your code into smaller functions. - Use descriptive script names that capture the overall intent of what each script does. For example, name a script **SyncCustomSounds** instead of just **Sounds**. - Assign descriptive names for parameters, functions, and scripts. For example, name a part **GreenSphere** instead of simply **grs**, or name a function `generateSphere()` instead of `gen()`. Using named functions versus anonymous functions can also produce better hints. - Consistently include well-written [comments](/docs/en-us/luau/comments.md) that describe the task you're implementing and what the inputs/outputs should be. - Consider including some sample calls with expected results in comments. - Suggest how to solve a problem, for instance `-- Use raycast`. - Use the exact function or variable name you defined, for example `-- Create 10 greenSphere objects` instead of `-- Create 10 spheres`. - If you're a novice scripter, start with basic projects such as "make the player jump when they touch the part" or use the tool to generate small code snippets that you can expand upon as your knowledge grows. ### Limitations The tool helps automate basic coding tasks but it does not always suggest perfect code. Known limitations include: - Manual triggering does not **always** force-generate a suggestion. - Suggestions are machine learned from a corpus of code and can thus reflect some limitations of the code they're trained on. For example, suggestions may not use newer APIs in favor of older APIs, or they may use Lua instead of [Luau](/docs/en-us/luau.md). - The tool may generate incorrect or misleading information that is not useful for your purpose. - Internal filters attempt to block offensive language, but they're not all-encompassing and there's a possibility the tool may generate offensive or biased information. - The suggestions may be the same, similar, or different among users, even with the same prompts. Your code, however, will never be shared with others. - The suggestion may be incomplete due to the limited length of output from the learning models. - There's a daily cap for the number of suggestions and, once the cap is reached, you will get no suggestions until the next day. ### Code privacy Currently, Roblox does not use any non-public data to train the learning models. The tool only uses a small subset of free marketplace assets for tuning large language models and the subset has passed various checks for quality and safety filters. Furthermore, all suggestions are generated **by** the AI model and do not transfer from one user to another. Since your code is not used for model training, it won't be suggested to other users of **Code Assist**, with the one exception of code being posted to free marketplace items. ## Multi-cursor The Script Editor supports usage of multiple cursors to make edits simultaneously. You can add cursors based on your needs with a mouse click or keyboard shortcut. The initial cursor is called the **primary cursor** and additional cursors are called **secondary cursors**. - Edits that you make at the primary cursor copy to the secondary cursors. Each edit counts as one action, so undo/redo of an edit applies to all cursors. - Widgets such as [autocomplete](#autocomplete-features) appear on the primary cursor but not the secondary cursors. - All of the standard [keyboard shortcuts](#keyboard-shortcuts) for script editing work with multi-cursor editing, including code indentation, toggling comments, and deleting lines. The following table summarizes multi-cursor workflows and their shortcuts. | Command | Windows | Mac | | --- | --- | --- | | **Add/remove cursor at mouse location** | `Alt` + click | `⌥` + click | | **Remove most recently added cursor** | `Ctrl``U` | `⌘``U` | | **Add/modify cursor on mouse drag** | `Alt` + drag | `⌥` + drag | | **Add cursor above/below** | `Ctrl``Alt``↑` / `Ctrl``Alt``↓` | `⌘``⌥``↑` / `⌘``⌥``↓` | | **Add cursor to next matching selection** | `Ctrl``D` | `⌘``D` | | **Add cursor to every matching selection** | `Shift``Alt``L` | `Shift``⌥``L` | | **Column/block select** | `Shift``Alt` + drag | `Shift``⌥` + drag | | **Split selections into lines** | `Shift``Alt``I` | `Shift``⌥``I` | > **Info:** To remove all secondary cursors, exit multi-cursor editing, and return to the primary cursor, press `Esc`. ### Add cursors You can add cursors with a combination of keyboard shortcuts and mouse maneuvers. Cursors merge if they occupy the same space, such as if you add cursors with arrow keys or delete all the characters between cursors. #### At mouse location To add a cursor at your mouse pointer location: 1. Hold `Alt` on Windows or `⌥` on Mac. 2. Click where you want to add the cursor. #### With mouse drag You can drag the mouse to add a cursor to a selection, split a multi-line selection into lines, or select columns and blocks of code/whitespace. #### Drag select To add a cursor to a selection of code through dragging: 1. Hold `Alt` on Windows or `⌥` on Mac. 2. Click and drag your mouse over the selection of code. #### Split into lines To split a multi-line selection into lines and add a cursor at the end of each line, press `Shift``Alt``I` on Windows or `Shift``⌥``I` on Mac. #### Column/block select To select columns and blocks of code or whitespace, drag the mouse while holding `Shift``Alt` on Windows or `Shift``⌥` on Mac. As you drag, a cursor will be added on each highlighted line. #### Above and below primary cursor To add a cursor directly above or below the primary cursor: 1. Press and hold `Ctrl``Alt` on Windows or `⌘``⌥` on Mac. 2. Press the `↑` or `↓` arrows. #### To matching selections You can add cursors to all matches of a selection or to the next/previous match, and optionally toggle whether matches are case-sensitive and/or match the whole word. #### All matching To add a cursor to all matches of a selected portion: 1. Select the code that you want to search for matches of. 2. Press `Shift``Alt``L` on Windows or `Shift``⌥``L` on Mac. #### Next/previous match To add a cursor to the **next** matching selection: 1. Select the code that you want to search for matches of. 2. Press `Ctrl``D` on Windows or `⌘``D` on Mac. 3. Continue pressing `Ctrl``D` or `⌘``D` until you've selected all the next matches that you want. There is no default shortcut for adding a cursor to the **previous** matching selection, but you can add your own: 1. Open **File** ⟩ **Advanced** ⟩ **Customize Shortcuts**. 2. In the shortcuts window, locate **Select previous occurrence**. 3. Double-click the action's **Shortcut** field and enter your own custom key combination, such as `Shift``Ctrl``D` to pair alongside "next" matching's default of `Ctrl``D`. 4. Confirm your setting by clicking **OK** in the bottom-right corner of the window. #### Case or whole word match For each of the matching-related workflows above, you can match **case** and/or **whole word** as follows: 1. Open the Find/Replace tool (`Ctrl``F` on Windows or `⌘``F` on Mac). 2. Use the toggle buttons to choose if a matched selection should be case-sensitive and/or match the whole word only. ### Remove cursors You can remove cursors with the following keyboard shortcuts and mouse maneuvers. Alternatively, you can exit multi-cursor editing by pressing `Esc`. #### At mouse location To remove a cursor: 1. Press and hold `Alt` on Windows or `⌥` on Mac. 2. Click the cursor you want to remove. #### Most recently added To remove the most recently added cursor, press `Ctrl``U` on Windows or `⌘``U` on Mac. ### Copy and paste cursors Copying a selection of code includes the cursors within it. The behavior of the paste depends on the number of cursors at the source and the number of cursors at the destination: - If the number of cursors are the same, then each copied cursor pastes to each corresponding destination cursor. - If the number of cursors are different, then each cursor at the destination receives the entire paste with each copied cursor as a new line. ### On-type formatting Pressing enter/return will auto-indent each cursor at the new line relative to the previous line. If the previous line starts with an incomplete block, the formatter will try to complete it. ## Editor settings Many customization options are accessible via the **Script Editor** tab of [Studio Settings](/docs/en-us/studio/setup.md#customization). Commonly modified options include: | Option | Description | | --- | --- | | **Font** | Font face and size for code in the editor. | | **Tab width** | Number of spaces representing a `Tab` indent. | | **Indent using spaces** | If enabled, spaces equal to **Tab Width** are inserted with `Tab` press. | | **Text wrapping** | If enabled, longer lines of code wrap to the next line. | | **Script Editor color preset** | Color preset for code elements, selection colors, and more. After choosing a preset, you can set specific colors for options like **Selection Color** and **Comment Color**. | ## Keyboard shortcuts The Script Editor has the following keyboard shortcuts. #### File and display | Command | Windows | Mac | | --- | --- | --- | | **Close script** | `Ctrl``W` | `⌘``W` | | **Reopen last closed script** | `Ctrl``Shift``T` | `⌘``Shift``T` | | **Quick open** | `Ctrl``P` | `⌘``P` | | **Show script in Explorer** | `Ctrl``Alt``K` | `⌘``⌥``K` | | **Zoom in** | `Ctrl``=` | `⌘``=` | | **Zoom out** | `Ctrl``-` | `⌘``-` | | **Reset script zoom** | `Ctrl``0` | `⌘``0` | #### Basic editing | Command | Windows | Mac | | --- | --- | --- | | **Cut** | `Ctrl``X` | `⌘``X` | | **Copy** | `Ctrl``C` | `⌘``C` | | **Paste** | `Ctrl``V` | `⌘``V` | | **Undo** | `Ctrl``Z` | `⌘``Z` | | **Redo** | `Ctrl``Y` | `Shift``⌘``Z` | | **Select all** | `Ctrl``A` | `⌘``A` | | **Find** / **Replace** | `Ctrl``F` | `⌘``F` | | **Find all** / **replace all** | `Ctrl``Shift``F` | `⌘``Shift``F` | | **Indent** / **unindent** | `Ctrl``]` / `Ctrl``[` | `⌘``]` / `⌘``[` | | **Toggle comment** | `Ctrl``/` | `⌘``/` | | **Expand all folds** | `Ctrl``E` | `⌘``E` | | **Collapse all folds** | `Ctrl``Shift``E` | `⌘``Shift``E` | | **Delete line** | `Ctrl``Shift``K` | `⌘``Shift``K` | | **Delete to start of line** | `Ctrl``Shift``Backspace` | `⌘``Delete` | | **Move line up/down** | `Alt``↑` / `Alt``↓` | `⌥``↑` / `⌥``↓` | | **Go to declaration** | `Ctrl``F12` | `⌘``F12` | | **Open script function filter** | `Alt``F` | `⌥``F` | | **Format selection** | `Alt``Shift``F` | `⌥``Shift``F` | #### Multi-Cursor | Command | Windows | Mac | | --- | --- | --- | | **Add/remove cursor at mouse location** | `Alt` + click | `⌥` + click | | **Remove most recently added cursor** | `Ctrl``U` | `⌘``U` | | **Add/modify cursor on mouse drag** | `Alt` + drag | `⌥` + drag | | **Add cursor above/below** | `Ctrl``Alt``↑` / `Ctrl``Alt``↓` | `⌘``⌥``↑` / `⌘``⌥``↓` | | **Add cursor to next matching selection** | `Ctrl``D` | `⌘``D` | | **Add cursor to every matching selection** | `Shift``Alt``L` | `Shift``⌥``L` | | **Column/block select** | `Shift``Alt` + drag | `Shift``⌥` + drag | | **Split selections into lines** | `Shift``Alt``I` | `Shift``⌥``I` | --- title: "Roblox Studio setup" url: /docs/en-us/studio/setup last_updated: 2026-06-29T19:34:11Z description: "Explains how to install Roblox Studio on your system." --- # Roblox Studio setup Create immersive 3D experiences on Roblox with **Roblox Studio**, a free application available on Windows and Mac. ## System requirements | | **MINIMUM** | **RECOMMENDED** | | --- | --- | --- | | **OS Version** | Windows 10
macOS 10.14 | Windows 11
macOS 14+ | | **Memory (RAM)** | 3 GB | 8 GB | | **Resolution** | | 1600×900 or higher | ## Installation 1. . 2. In the pop-up dialog, click the **Download Studio** button. 3. Find the Studio installer in your browser's download history and double-click the file. - On Windows, the file is `RobloxStudio.exe`. - On Mac, the file is `RobloxStudio.dmg`. 4. After Studio finishes installing, a pop-up confirmation displays. Click the **Launch Studio** button. 5. Sign in to Studio with your Roblox account (if you don't have an account, create one at [roblox.com](https://www.roblox.com/)). ## Customization You can customize Studio to suit your workflow across various levels: - [Configure the toolbar](/docs/en-us/studio/ui-overview.md#toolbar-and-mezzanine) with custom tabs, reorganize tools within custom tabs, hide infrequently used tabs, and more. - Customize the [window layout](/docs/en-us/studio/ui-overview.md#layout-customization) to best suit your workflows. - View/edit Studio shortcuts and bind actions without defaults through **File** ⟩ **Customize Shortcuts**. - Access a wide array of **Studio Settings** (`Alt``S` on Windows; `⌥``S` on Mac). Locate known settings by typing keywords into the search field at the top of the window, for example `theme` to locate the setting for dark theme or light theme. ## Updates In contrast to certain other engines, every experience runs on the latest version of the Roblox engine. You should keep Studio up‑to‑date to utilize the latest APIs and features. If your current version of Studio is outdated, you'll see an **Update** button in the upper‑right corner. Clicking the button will prompt you to save/close the currently open place so that Studio can automatically update and restart. ![Update button in Studio highlighted.](../assets/studio/general/Toolbar-Update-Available.png) ## Beta features Many beta features are available through Studio's **File** ⟩ **Beta Features** menu. Once you've enabled beta features, click the **Save** button and you'll be prompted to restart Studio for the features to take effect.
--- title: "Terrain Editor" url: /docs/en-us/studio/terrain-editor last_updated: 2026-06-29T19:34:11Z description: "The Terrain Editor tools generate and sculpt realistic terrain environments such as mountains, bodies of water, grass-covered hills, or a flat desert." --- # Terrain Editor The **Terrain Editor**, accessible from Studio's **Home** tab or **Window** ⟩ **3D** menu, lets you generate and manipulate [environmental terrain](/docs/en-us/parts/terrain.md) through its [Create](#create-tab) and [Edit](#edit-tab) toolsets. ![Terrain Editor indicated in Studio's toolbar](../assets/studio/general/Toolbar-Terrain-Editor.png) > **Success:** For detailed instructions on working with terrain, see the [environmental terrain](/docs/en-us/parts/terrain.md) guide. ## Create tab The **Create** tab includes the [Import](#import), [Generate](#generate), and [Clear](#clear) tools. ### Import The **Import** tool imports a [heightmap](/docs/en-us/parts/terrain.md#heightmaps-and-colormaps) and optional [colormap](/docs/en-us/parts/terrain.md#heightmaps-and-colormaps) and applies them to a selected region. ![Import tool indicated in Create tab of Terrain Editor](../assets/studio/terrain-editor/Create-Tab-Import.png) ![Example heightmap image](../assets/modeling/terrain/Terrain-Heightmap.png)_Heightmap_ ![Example colormap image](../assets/modeling/terrain/Terrain-Colormap.png)_Colormap_ ![Terrain generated from the example heightmap and colormap](../assets/modeling/terrain/Terrain-Colormap-Result.jpg)_Generated terrain_ | Selection settings | | --- | | Size, position, and snapping options as outlined for the [Select](#select) tool. | | Heightmap settings | | **Heightmap** | Imports a heightmap from a local image file. | | **Colormap** | Imports a colormap from a local image file to use alongside a heightmap. | | **Default Material** | Specifies one consistent material to apply across all of the generated terrain. This is overridden by materials based on the colormap, if imported. | ### Generate The **Generate** tool procedurally generates terrain within a selected region. This is useful if you want to create a large map and then fine-tune terrain details using the [Edit](#edit-tab) tab tools. ![Generate tool indicated in Create tab of Terrain Editor](../assets/studio/terrain-editor/Create-Tab-Generate.png) | Selection settings | | --- | | Size, position, and snapping options as outlined for the [Select](#select) tool. | | Biome settings | | **Biomes** | Includes the selected biomes in the generated terrain. | | **Blending** | Adjusts how the biomes blend into one another, with higher values a bit smoother. | | **Caves** | Includes caves in the generated terrain. | | **Biome Size** | Sets the size of the biomes within the overall terrain size. | | **Seed** | A number that Studio generates to determine the shape of the terrain. When you change this number, Studio generates a new terrain with the same settings. | ### Clear The **Clear** tool reveals a button that clears all terrain within the entire place. ![Clear tool indicated in Create tab of Terrain Editor](../assets/studio/terrain-editor/Create-Tab-Clear.png) ## Edit tab The **Edit** tab includes the [Select](#select), [Transform](#transform), [Fill](#fill), [Sea Level](#sea-level), [Draw](#draw), [Sculpt](#sculpt), [Smooth](#smooth), [Paint](#paint), and [Flatten](#flatten) tools. ### Select The **Select** tool is a universal tool for selecting rectangular regions of terrain. With the tool enabled, select a region by clicking and dragging in the 3D viewport, reposition it with the **move** draggers, and edit its size with the **scale** handles. Alternatively, enter values into the tool's **X**/**Y**/**Z** inputs to set a specific position and size. ![Select tool indicated in Edit tab of Terrain Editor](../assets/studio/terrain-editor/Edit-Tab-Select.png) | Selection settings | | --- | | **Size** | Sets specific **X**/**Y**/**Z** values for the selection's size, in studs. | | **Position** | Sets specific **X**/**Y**/**Z** values for the selection's position. | | **Snap to Voxels** | Snaps the selection bounds to the nearest voxel. | ![Move draggers and scale handles on a selected region](../assets/studio/terrain-editor/Select-Region-Labeled.jpg)_Move draggers and scale handles on a selected region_ Studio also supports the following keyboard and mouse shortcuts, assuming the **Select** tool is active and nothing is selected in the [Explorer](/docs/en-us/studio/explorer.md) hierarchy. | Windows | Mac | Action | | --- | --- | --- | | `Ctrl``C` | `⌘``C` | Copy terrain within the selected region to the clipboard. | | `Ctrl``V` | `⌘``V` | Paste terrain that has been copied to the clipboard and swap to the [Transform](#transform) tool so that the new terrain can be transformed. | | `Ctrl``X` | `⌘``X` | Cut terrain within the selected region to the clipboard. | | `Ctrl``D` | `⌘``D` | Duplicate terrain within the selected region and swap to the [Transform](#transform) tool so that the new terrain can be transformed. | | `Delete` | `Delete` | Delete terrain within the selected region. | | `Shift` | `Shift` | When held down while dragging any **scale** handle, scales the region proportionally across all other axes. | | `Ctrl` | `⌘` | When held down while dragging any **scale** handle, scales the region equally in both the positive and negative direction along that axis. | ### Transform The **Transform** tool manipulates entire [selected](#select) regions to a new position, size, or orientation. With the tool enabled, transform a selected region with the **move** draggers, **rotate** rings, and **scale** handles. Alternatively, enter values into the tool's **X**/**Y**/**Z** inputs to set a specific position, size, and rotation. ![Transform tool indicated in Edit tab of Terrain Editor](../assets/studio/terrain-editor/Edit-Tab-Transform.png) | Transform settings | | --- | | **Size** | Sets specific **X**/**Y**/**Z** values for the selection's size in studs. | | **Position** | Sets specific **X**/**Y**/**Z** values for the selection's position. | | **Rotation** | Sets specific **X**/**Y**/**Z** values for the selection's rotation in degrees. | | **Merge Empty** | When enabled, air voxels in the selection will overwrite existing voxels at the destination. | | **Live Edit** | When enabled, terrain is constantly updated while being transformed. To view only a wireframe preview of the transformation, disable live edit mode and then, while transforming, press `Enter`/`Return` or click the **Apply** button to apply the changes. | | **Snap to Voxels** | Snaps the selection bounds to the nearest voxel. | ![Move draggers, rotate rings, and scale handles on a selected region](../assets/studio/terrain-editor/Transform-Region-Labeled.jpg)_Move draggers, rotate rings, and scale handles on a selected region_ > **Warning:** Before rotating a region with the visual **rotate** rings, check the rotation snapping setting in Studio's toolbar. Disable rotation snapping entirely for free-form rotation. > **Info:** Similar to [selecting](#select) a region, holding `Shift` while dragging any **scale** handle scales the region proportionally across all other axes, and holding `Ctrl` or `⌘` while dragging scales the region equally in both the positive and negative direction along that axis. ### Fill The **Fill** tool fills an entire [selected](#select) region with a specific material, or replaces all material within the region with another material. ![Fill tool indicated in Edit tab of Terrain Editor](../assets/studio/terrain-editor/Edit-Tab-Fill.png) | Selection settings | | --- | | Size, position, and snapping options as outlined for the [Select](#select) tool. | | Material settings | | **Fill Mode** | Toggles the tool between **Fill** and **Replace** mode. | | **Source Material** | In **Fill** mode, the desired fill material. In **Replace** mode, the material to replace. | | **Target Material** | In **Replace** mode, the material to replace the source material with. | ![Region filled with Salt material](../assets/studio/terrain-editor/Fill-Region.jpg)_Selected region filled with Salt material_ ### Sea Level The **Sea Level** tool lets you create a consistent water level or remove all water within a region. ![Sea Level tool indicated in Edit tab of Terrain Editor](../assets/studio/terrain-editor/Edit-Tab-Sea-Level.png) | Sea level settings | | --- | | **Size** | Sets specific **X**/**Y**/**Z** values for the selection's size in studs. | | **Position** | Sets specific **X**/**Y**/**Z** values for the selection's position. | | **Snap to Voxels** | Snaps the selection bounds to the nearest voxel. | ### Draw The **Draw** tool **adds** or **subtracts** terrain using the brush. This tool functions in a dual mode where holding down `Ctrl` or `⌘` toggles on "subtract" mode instead of the default "add" mode. Additionally, holding down `Shift` temporarily activates the [Smooth](#smooth) tool. ![Draw tool indicated in Edit tab of Terrain Editor](../assets/studio/terrain-editor/Edit-Tab-Draw.png) | Brush settings | | --- | | **Brush Mode** | Toggles the tool between **Add** and **Subtract** mode. Also toggled by holding down `Ctrl` or `⌘`. | | **Brush Shape** | Sets the brush shape to either a **sphere**, **box**, or **cylinder**.
![Brush shape icons in the Terrain Editor](../assets/studio/terrain-editor/Brush-Shape-Icons.png) | | **Brush Size** | Sets the brush size from 1 to 64. If you choose a brush shape of **box** or **cylinder**, a **Height** slider/input also appears to adjust the brush height proportionally or independently of the base size. | | **Pivot Position** | Sets the vertical pivot point of the brush to either the bottom, center, or top of where the brush connects with existing terrain. | | **Snapping** | Whether snapping is off or to the nearest voxel. | | **Plane Lock** | Toggles whether to enable a visual plane which the brush locks to. In **Auto** mode, the plane tilts and rotates with the camera. In **Manual** mode, the **Edit Plane** option appears, letting you set a specific position/orientation through the visual draggers or **X**/**Y**/**Z** inputs. | | **Ignore Water** | Ignores water when adding or subtracting terrain. | | **Ignore Parts** | Ignores [parts](/docs/en-us/parts.md) in regards to brush placement while moving the mouse pointer across part surfaces. | | Material settings | | **Auto Material** | Uses the same terrain material in the brush's vicinity when adding new terrain. | | **Source Material** | Sets the [material](/docs/en-us/parts/terrain.md#terrain-materials) for **Add** mode. | ### Sculpt The **Sculpt** tool **adds** or **subtracts** terrain using the brush. Unlike the [Draw](#draw) tool, this tool includes a **strength** slider/input to allow for more gentle manipulation of terrain. This tool functions in a dual mode where holding down `Ctrl` or `⌘` toggles on "subtract" mode instead of the default "add" mode, and holding down `Shift` temporarily activates the [Smooth](#smooth) tool. ![Sculpt tool indicated in Edit tab of Terrain Editor](../assets/studio/terrain-editor/Edit-Tab-Sculpt.png) | Option | Description | | --- | --- | | **Brush Mode** | Toggles the tool between **Add** and **Subtract** mode. Also toggled by holding down `Ctrl` or `⌘`. | | **Brush Shape** | Sets the brush shape to either a **sphere**, **box**, or **cylinder**.
![Brush shape icons in the Terrain Editor](../assets/studio/terrain-editor/Brush-Shape-Icons.png) | | **Brush Size** | Sets the brush size from 1 to 64. If you choose a brush shape of **box** or **cylinder**, a **Height** slider/input also appears to adjust the brush height proportionally or independently of the base size. | | **Strength** | Sets the brush strength from 0.1 to 1. | | **Plane Lock** | Toggles whether to enable a visual plane which the brush locks to. In **Auto** mode, the plane tilts and rotates with the camera. In **Manual** mode, the **Edit Plane** option appears, letting you set a specific position/orientation through the visual draggers or **X**/**Y**/**Z** inputs. | | **Ignore Water** | Ignores water when adding or subtracting terrain. | | **Ignore Parts** | Ignores [parts](/docs/en-us/parts.md) in regards to brush placement while moving the mouse pointer across part surfaces. | | **Auto Material** | Uses the same terrain material in the brush's vicinity when adding new terrain. | | **Source Material** | Sets the [material](/docs/en-us/parts/terrain.md#terrain-materials) for **Add** mode. | ### Smooth The **Smooth** tool smoothes out abrupt edges in terrain using the brush. This tool can be used in standalone mode, or you can toggle it on by holding `Shift` while using the [Draw](#draw) or [Sculpt](#sculpt) tools. ![Smooth tool indicated in Edit tab of Terrain Editor](../assets/studio/terrain-editor/Edit-Tab-Smooth.png) | Option | Description | | --- | --- | | **Brush Shape** | Sets the brush shape to either a **sphere**, **box**, or **cylinder**.
![Brush shape icons in the Terrain Editor](../assets/studio/terrain-editor/Brush-Shape-Icons.png) | | **Brush Size** | Sets the brush size from 1 to 64. If you choose a brush shape of **box** or **cylinder**, a **Height** slider/input also appears to adjust the brush height proportionally or independently of the base size. | | **Strength** | Sets the brush strength from 0.1 to 1. | | **Pivot Position** | Sets the vertical pivot point of the brush to either the bottom, center, or top of where the brush connects with existing terrain. | | **Snapping** | Whether snapping is off or to the nearest voxel. | | **Plane Lock** | Toggles whether to enable a visual plane which the brush locks to. In **Auto** mode, the plane tilts and rotates with the camera. In **Manual** mode, the **Edit Plane** option appears, letting you set a specific position/orientation through the visual draggers or **X**/**Y**/**Z** inputs. | | **Ignore Water** | Ignores water when smoothing terrain. | | **Ignore Parts** | Ignores [parts](/docs/en-us/parts.md) in regards to brush placement while moving the mouse pointer across part surfaces. | ### Paint The **Paint** tool, using the brush, **paints** a terrain [material](/docs/en-us/parts/terrain.md#terrain-materials) over an existing material or **replaces** one material with another material. ![Paint tool indicated in Edit tab of Terrain Editor](../assets/studio/terrain-editor/Edit-Tab-Paint.png) | Option | Description | | --- | --- | | **Brush Shape** | Sets the brush shape to either a **sphere**, **box**, or **cylinder**.
![Brush shape icons in the Terrain Editor](../assets/studio/terrain-editor/Brush-Shape-Icons.png) | | **Brush Size** | Sets the brush size from 1 to 64. If you choose a brush shape of **box** or **cylinder**, a **Height** slider/input also appears to adjust the brush height proportionally or independently of the base size. | | **Pivot Position** | Sets the vertical pivot point of the brush to either the bottom, center, or top of where the brush connects with existing terrain. | | **Snapping** | Whether snapping is off or to the nearest voxel. | | **Plane Lock** | Toggles whether to enable a visual plane which the brush locks to. In **Auto** mode, the plane tilts and rotates with the camera. In **Manual** mode, the **Edit Plane** option appears, letting you set a specific position/orientation through the visual draggers or **X**/**Y**/**Z** inputs. | | **Ignore Water** | Ignores water when flattening terrain. | | **Ignore Parts** | Ignores [parts](/docs/en-us/parts.md) in regards to brush placement while moving the mouse pointer across part surfaces. | | **Material Mode** | Toggles the tool between **Paint** and **Replace** mode. In **Paint** mode, choose the desired painting material from the tool's picker tiles. In **Replace** mode, choose both the **source** material to replace and the **target** material to replace it with. | ### Flatten The **Flatten** tool flattens terrain to a consistent level across a visualized plane. By default, the tool lowers terrain above the plane **and** raises terrain below to the plane, but you can opt to selectively lower **or** raise through the tool's **Flatten Mode** option. ![Flatten tool indicated in Edit tab of Terrain Editor](../assets/studio/terrain-editor/Edit-Tab-Flatten.png) | Option | Description | | --- | --- | | **Brush Shape** | Sets the brush shape to either a **sphere**, **box**, or **cylinder**.
![Brush shape icons in the Terrain Editor](../assets/studio/terrain-editor/Brush-Shape-Icons.png) | | **Flatten Mode** | **Erode to Flat**¹ flattens terrain above the plane. **Grow to Flat**² fills in terrain below the plane. **Flatten All**³ removes terrain above the plane and fills in terrain below the plane.
![Flatten Mode icons in the Terrain Editor](../assets/studio/terrain-editor/Flatten-Mode-Icons.png) | | **Brush Size** | Sets the brush size from 1 to 64. If you choose a brush shape of **box** or **cylinder**, a **Height** slider/input also appears to adjust the brush height proportionally or independently of the base size. | | **Strength** | Sets the brush strength from 0.1 to 1. | | **Pivot Position** | Sets the vertical pivot point of the brush to either the bottom, center, or top of where the brush connects with existing terrain. | | **Snapping** | Whether snapping is off or to the nearest voxel. | | **Flatten Plane** | If set to **Fixed**, flattens terrain to a fixed visual plane in **Y** world space. | | **Ignore Water** | Ignores water when flattening terrain. | | **Ignore Parts** | Ignores [parts](/docs/en-us/parts.md) in regards to brush placement while moving the mouse pointer across part surfaces. | ## Studio shortcuts For tools which use the **brush** ([Draw](#draw), [Sculpt](#sculpt), [Smooth](#smooth), [Flatten](#flatten), [Paint](#paint)), Studio supports the following keyboard and mouse shortcuts. | Windows | Mac | Action | | --- | --- | --- | | `Ctrl` | `⌘` | When held down while using the [Draw](#draw) and [Sculpt](#sculpt) tools, toggles on the alternate brush mode. For example, toggles on "subtract" mode instead of the default "add" mode. | | `Shift` | `Shift` | When held down while using the [Draw](#draw) and [Sculpt](#sculpt) tools, temporarily activates the [Smooth](#smooth) tool. | | `B` | `B` | When held down while dragging the mouse or using the scroll wheel, adjusts the brush's **base size**. | | `Ctrl``B` | `⌘``B` | When held down while dragging the mouse or using the scroll wheel, adjusts the brush's **height**. Only applies if the brush's shape is set to **box** or **cylinder**. | | `Shift``B` | `Shift``B` | When held down while dragging the mouse or using the scroll wheel, adjusts the brush's **strength**. Only applies when using the [Sculpt](#sculpt), [Smooth](#smooth), or [Flatten](#flatten) tool. | | `Alt` | `⌥` | When held down on mouse click, shows the material picker. |
--- title: "Studio testing modes" url: /docs/en-us/studio/testing-modes last_updated: 2026-06-29T19:34:12Z description: "Explore the built-in Studio testing modes for games." --- # Studio testing modes Because of the underlying [client-server model](/docs/en-us/projects/client-server.md) of the Roblox Engine, it's important that you test your game in various modes before [releasing it to the public](/docs/en-us/production/publishing/publish-games-and-places.md#make-game-public). All of the testing options are accessible in the left portion of the mezzanine. ![Testing controls indicated in Studio's mezzanine.](../assets/studio/general/Mezzanine-Testing-Controls.png) ## Playtesting There are three common options for playtesting a game. Choose the desired option from the dropdown menu and click the **Play** button to its right to begin the playtest. ![Test option in the testing modes dropdown of Studio's mezzanine.](../assets/studio/general/Mezzanine-Testing-Mode-Test.png) | Mode | Shortcut | Description | | --- | --- | --- | | **Test** | `F5` | Starts simulating the experience, inserting your avatar at either a `Class.SpawnLocation` or coordinates of around`(0, 100, 0)`. | | **Test Here** | | Starts simulating the experience, inserting your avatar in front of the camera's current position. | | **Run** | `F8` | Starts simulating the experience but does not insert your avatar. The simulation begins at the current camera position and you can navigate around using the Studio camera controls. | Once a playtest is running, additional options become available in the mezzanine. ![Additional options available during a playtest from Studio's mezzanine.](../assets/studio/general/Mezzanine-Testing-Options.png) | Action | Shortcut | Description | | --- | --- | --- | | **Pause** / **Resume** | | Lets you [pause/resume physics and animations](#pauseresume). | | **Stop** | `Shift``F5` | Stops simulation of the game and resets all objects and instances to how they were before the playtest. | | **Client** / **Server** | | During playtesting in a "solo" mode (**Test** or **Test Here**), toggles between **Client** mode and **Server** mode. See [toggle client/server](/docs/en-us/studio/testing-modes.md#toggle-clientserver) for details. | ### Toggle client/server When testing in either **Test** or **Test Here** mode, Studio runs two separate simulations — one [client](/docs/en-us/projects/client-server.md#client) simulation and one [server](/docs/en-us/projects/client-server.md#server) simulation — which can provide a more accurate impression of how the game will execute in production. While solo playtesting, you can toggle between **Client** and **Server** modes by clicking the **Client/Server** toggle button. When you toggle, the button changes to reflect the current simulation mode. _Client_ _Server_ Depending on the simulation mode, control of your character and the camera changes as follows: #### Client Mode _In **Client** mode, the 3D viewport is surrounded by a **blue** border and the simulation uses your character controls and camera setup. This testing mode is a close simulation of the game running on the Roblox application, without multiple players._ #### Server Mode _In **Server** mode, the 3D viewport is surrounded by a **green** border and your character is inserted but is not under your control. You can move about the place with a free‑floating camera, select objects, and inspect their properties._ Within the [Explorer](/docs/en-us/studio/explorer.md) window hierarchy, certain objects only exist in their expected containers. ![Explorer window showing items that exist on client only](../assets/studio/debugging/Client-Server-Toggle-Explorer-Client.png)_In **Client** mode, the expected client-side objects are present in the hierarchy, including those copied over from `Class.StarterPack` to the player's `Class.Backpack` and from `Class.StarterPlayer` into the player's `Class.PlayerScripts`._ ![Explorer window showing items that exist on server only](../assets/studio/debugging/Client-Server-Toggle-Explorer-Server.png)_In **Server** mode, the expected server-side objects are present in the hierarchy, including scripts in `Class.ServerScriptService` and objects you placed in `Class.ServerStorage`._ In the [Output](/docs/en-us/studio/output.md) window, messages are labeled **blue** (client) or **green** (server), indicating their origin from either the client or server. For messages output from `Class.ModuleScript|ModuleScripts`, the label color is determined by whether the module was called from a client-side `Class.LocalScript` or from a server-side `Class.Script`. ![Output window showing green label for server output and blue label for client output](../assets/studio/general/Output-Window-Client-Server-Labels.png) ### Pause/resume While solo playtesting, the **Pause/Resume** button is useful for debugging ephemeral scenes and mechanisms, without disabling rendering. ![Pause/Resume button indicated in Studio's mezzanine.](../assets/studio/general/Mezzanine-Testing-Pause.png) By default, pausing and resuming acts upon both the client **and** server. If you wish to pause or resume only the client **or** server: 1. Set the [client/server toggle](#toggle-clientserver) based on which simulation you want to pause/resume. 2. Click the small arrow on the **Pause/Resume** button and select the alternate option of **Pause/Resume Client** or **Pause/Resume Server**. Clicking the button will then pause or resume only that side of the simulation. When the simulation is paused, you can **step forward** 1/60th of a second (60 Hz) by clicking the **Step Forward** button. Like the **Pause/Resume** button to its left, clicking the small arrow lets you select stepping for only the client **or** server based on the [client/server toggle](#toggle-clientserver). ![Step Forward button indicated in Studio's mezzanine.](../assets/studio/general/Mezzanine-Testing-Step-Forward.png) Note the following technical details in relation to pause/resume: - Although `Class.Animation|Animations` are not based on physics, these toolbar buttons also pause/resume animations. - Pausing or resuming has no effect on running scripts. - Only the `Class.RunService` callbacks `Class.RunService.PreAnimation|PreAnimation`, `Class.RunService.PreSimulation|PreSimulation`, `Class.RunService.PostSimulation|PostSimulation`, and `Class.RunService.Stepped|Stepped` pause or resume through these toolbar buttons. Other callbacks (`Class.RunService.PreRender|PreRender`, `Class.RunService.Heartbeat|Heartbeat`, and `Class.RunService.RenderStepped|RenderStepped`) continue to fire, maintaining normal functionality of camera scripts, rendered visualizations, and plugins. ## Multi-client simulation The **Server & Clients** option from the dropdown menu lets you launch multiple sessions of Studio, one acting as the server and each other acting as a client. This testing mode is a valuable tool for comparing how a client "sees" other clients within the game. 1. With the **Server & Clients** option selected in the dropdown menu, choose the number of client sessions to test. Usually 1–2 clients is sufficient, although you can simulate up to eight.![Server & Clients option in the testing modes dropdown of Studio's mezzanine.](../assets/studio/general/Mezzanine-Testing-Mode-Server-Clients.png) 2. Press the **Play** button or `F7` to begin the client-server simulation. 3. When you're finished testing, press the **End Session** button from any of the simulation sessions to close all simulated clients and the simulated server.![End Session indicated in Studio's mezzanine](../assets/studio/general/Mezzanine-Testing-End-Session.png) ## Party simulation The **Party Simulator** allows you to test and debug games that use party-related APIs directly within Studio, without needing to publish and coordinate multiple devices. This tool lets you configure and emulate parties for test players in your [Server & Clients](#multi-client-simulation) play tests. When play testing, Party Simulator automatically assigns all test players to the configured simulated parties. ![Party Emulator displaying simulated parties and various assignment options](../assets/studio/general/Party-Simulator.png) If Party Simulator is enabled: - Each `Class.Player.PartyId` property reflects the Party Simulator assignments. - `Class.SocialService:GetPartyAsync()` returns a simulated table of party data. - `Class.SocialService:GetPlayersByPartyId()` returns a list of `Class.Player` instances assigned to the provided `Class.Player.PartyId`. You can adjust the number of local test players at any time using Studio's [Server & Clients](#multi-client-simulation) setting. When the test player count changes, the emulator automatically updates to include or remove players in the configuration. ## Collaborative testing If you're working on a game with others in [collaboration](/docs/en-us/projects/collaboration.md) mode, you can test with other collaborators as follows: 1. Select **Team Test** in the dropdown menu and press the **Play** button to open a new Studio session with your character inserted.![Team Test option in the testing modes dropdown of Studio's mezzanine.](../assets/studio/general/Mezzanine-Testing-Mode-Team-Test.png) 2. Other collaborators can then join by pressing the **Play** button from the mezzanine in their Studio session. > **Info:** Only one team test session can run at any given time. To close a session and kick out all testers, click the **End Session** button. ## Device emulation The **Device Emulator**, accessible from Studio's **Test** menu, lets you emulate various devices directly in Studio, providing insight into how controls work on mobile devices, consoles, and VR headsets, as well as how [on‑screen UI](/docs/en-us/ui/on-screen-containers.md) elements display on different screen resolutions and aspect ratios. In emulation mode, you can select devices from the **device selector** dropdown menu above the 3D viewport to emulate less powerful devices and test [streaming‑enabled](/docs/en-us/workspace/streaming.md) experiences where 3D content dynamically loads and unloads based on available memory. You can also adjust the **view size** and change the **orientation** between landscape and portrait modes. ![Emulation options above the 3D viewport](../../assets/studio/general/Editor-Window-Emulation-Options.png) ## Touch simulation Touch simulation is automatically enabled when a mobile device is selected in the **Device Emulator**. Both single‑touch and two‑touch gestures can be simulated by clicking and dragging with the mouse while playtesting. By default, mouse interactions will simulate single‑touch gestures. Studio supports the following shortcuts for simulating two-touch gestures. Shortcuts can also be customized via Studio's **Customize Shortcuts** menu. | Windows | Mac | Action | | --- | --- | --- | | `Alt` | `⌥` | When held down while dragging the mouse, simulates pinching and rotating. | | `Alt``Shift` | `⌥``Shift` | When held down while dragging the mouse, simulates two-touch panning. | When a two-touch shortcut is active, two circles will appear, indicating where the simulated touches are positioned. ![View of a multitouch gesture in the Device Emulator.](../../assets/studio/general/Touch-Simulation.png) ## Controller simulation The **Controller Emulator**, accessible from Studio's **Test** menu, lets you accurately emulate gamepad input directly in Studio. The default controller is a generic gamepad, but you can select alternatives from the upper‑left picker menu. ![View of the generic controller in the Controller Emulator.](../../assets/studio/general/Controller-Emulator.png) While playtesting, you can control the experience with the virtual controller using your mouse. You can also click **Edit mappings** in the upper‑right corner to view and edit key mappings for the virtual controller, for example `E` to `Enum.KeyCode.ButtonL2|ButtonL2` or `9` to `Enum.KeyCode.ButtonA|ButtonA`. These mappings are saved like other Studio settings (per controller, per user, per computer) and are translated to gamepad events in both the emulator window and the 3D viewport. ## Network simulation Network simulation, accessible from the **Network** tab of [Studio Settings](/docs/en-us/studio/setup.md#customization) (`Alt``S` on Windows; `⌥``S` on Mac), allows you to simulate real-world network conditions like latency, packet loss, and jitter to [playtest](#playtesting) connections. This is particularly useful for testing games that use [Server Authority](/docs/en-us/projects/server-authority.md) since the prediction and rollback/resimulation mechanisms are sensitive to networking conditions. It is also useful for testing `Class.UnreliableRemoteEvent|UnreliableRemoteEvents` which may be lost or arrive out of order. Network simulation settings are applied to all playtest connections, including [Test](#playtesting), [Server & Clients](#multi-client-simulation), and [Team Test](#collaborative-testing). Note that for [Team Test](#collaborative-testing), the latency and loss numbers are in **addition** to any latency and packet loss from your computer to Roblox's servers. For example, if your network ping in a team test is normally 20 milliseconds and you configure 50 milliseconds of delay in Studio's settings, you will see a ping of 70 milliseconds. > **Success:** Network simulation settings can be updated during a test session and the new values will be applied immediately. For best results, make only small changes to latency (+/− 20%) while running a test session, as large increases in latency may trigger congestion control detection that reduces network transmission speeds. > **Warning:** The legacy **Incoming Replication Lag** setting will continue to add lag to connections at the [replication](/docs/en-us/projects/client-server.md#replication) level, even if network simulation properties are also utilized. For simplicity and consistency, network simulation is recommended for all new use cases. | Studio Setting | Property | Description | | --- | --- | --- | | **Min Inbound Delay** | `Class.NetworkSettings.InboundNetworkMinDelayMs\|InboundNetworkMinDelayMs` | Adds latency to playtest connections in the server‑to‑client direction. | | **Min Outbound Delay** | `Class.NetworkSettings.OutboundNetworkMinDelayMs\|OutboundNetworkMinDelayMs` | Adds latency to playtest connections in the client‑to‑server direction. | | **Inbound Jitter** | `Class.NetworkSettings.InboundNetworkJitterMs\|InboundNetworkJitterMs` | Adds jitter to playtest connections in the server‑to‑client direction. | | **Outbound Jitter** | `Class.NetworkSettings.OutboundNetworkJitterMs\|OutboundNetworkJitterMs` | Adds jitter to playtest connections in the client‑to‑server direction. | | **Inbound Packet Loss** | `Class.NetworkSettings.InboundNetworkLossPercent\|InboundNetworkLossPercent` | Sets the probability that packets on playtest connections from server to client are dropped. | | **Outbound Packet Loss** | `Class.NetworkSettings.OutboundNetworkLossPercent\|OutboundNetworkLossPercent` | Sets the probability that packets on playtest connections from client to server are dropped. | In each of the above settings, the following concepts apply: - **Inbound** refers to traffic from server to client. **Outbound** is traffic from client to server. - **Delay** — Network ping is measured as round-trip time. To simulate 20 milliseconds network ping, set both **Min Inbound Delay** and **Min Outbound Delay** to 10 milliseconds. - **Jitter** — Jitter is added on top of any configured minimum delay. Jitter can cause some traffic, such as `Class.UnreliableRemoteEvent|UnreliableRemoteEvents`, to arrive out of order. - **Precision** — Per-packet delay sampled from delay and jitter is rounded to 1 millisecond. - **Packet Loss** — Each packet can be dropped randomly based on the configured probability. ## VR headsets If you'd like to support virtual reality (VR) headsets for your game, make sure to test or [emulate](#vr-emulation) VR in Studio. ### VR emulation **VR emulation** lets you test VR games in Studio without a physical headset. Just like emulating any other device, use the [device selector](#device-emulation) menu to choose either **Meta Quest 2** or **Meta Quest 3**. The [controller emulator](#controller-simulation) automatically selects the appropriate controller for the headset. ![The Controller Emulator with a Quest 3 controller.](../assets/studio/general/Controller-Emulator-VR.png) ![Emulator with a Quest 3 emulation.](../assets/studio/general/Emulator-Viewport-VR.jpg) The combination of a headset and multiple controllers, each with motion tracking, make VR emulation more complex: - For motion tracking emulation, press `Alt``1` (`⌥``1`) to lock the mouse to and unlock the mouse from the viewport. - Use `Shift``←` or `Shift``→` to switch between common combinations of the headset, left controller, and right controller. For example, you might use the **Headset** option to look around as you walk forward with the left controller button, but then switch to the **Right Controller** when you need to use motion controls to aim at a target. ### Headset configuration Studio supports testing for all VR headsets that are compatible with [OpenXR](https://www.khronos.org/openxr/), the open‑source industry standard providing access to VR. > **Info:** Currently, testing in VR is only supported on Windows. To enable Studio testing in VR, you must connect your headset to your PC and configure the OpenXR runtime **before** launching Studio. If you've already launched Studio, quit and complete the configuration steps first. If you only have one VR headset, installing the corresponding VR app automatically configures the runtime for you. If you have multiple headsets, you must set up the runtime manually and make sure to **only** configure the one that you want to use for testing. The following steps are for the two most common VR apps: - SteamVR for headsets such as the HTC Vive and Valve Index. - Oculus for headsets such as the Meta Quest and Oculus Rift. #### Steam VR 1. Install and open the SteamVR app on your computer. 2. Under **SteamVR Settings**, select the **Developer** tab. Then select **Show Advanced Settings**. 3. Set SteamVR as OpenXR runtime. 4. Turn the controllers on by pressing the **System** button until you hear a beeping sound. - To turn the controller off, press and hold the **System** button until you hear the same beeping sound. If you follow the steps correctly, the status icons on the SteamVR app and the status lights on the hardware should all be green, indicating that the configuration is completed. > **Info:** When you quit the SteamVR app, the controllers automatically turn off. The controllers also automatically turn off after being idle for a period of time. #### Oculus VR 1. Install and open the Oculus app on your computer. 2. Confirm that your device is connected and the status is ready. 3. Under **Settings / General** in the Oculus app, select **OpenXR Runtime**. 4. **OPTIONAL** Bring up the **Devices** tab and find your headset to confirm the configuration by checking if the status is green without errors. > **Info:** When using Oculus VR, ensure the following: - At minimum, Oculus drivers require an NVIDIA GeForce GTX 1060 graphics card or equivalent. - If you are using a Quest 2 with a link cable, you must enable the link inside the headset. ### Roblox Quest app You can test your game in the Roblox app on Quest without linking your headset to your computer. Use the following steps to access your game on your headset in this mode: 1. In Studio, publish the game and set the game to private. 2. Using your [Creator Dashboard](https://create.roblox.com/dashboard/creations) or the link in Studio, open the game page in your web browser. 3. On the game page, add the game to your favorites by clicking the **Favorite** icon. 4. Using your headset, open the standalone Roblox app. 5. Scroll down to the **Favorites** section in the home page and run your game. ### Studio VR mode After [configuring your headset](#headset-configuration), you can turn on the Studio VR testing mode through the following steps: 1. In the **File** menu, select **Studio Settings**. 2. Select **Rendering**. 3. Under **General** settings, turn on **Enable VR Mode**. You can now test your game using your VR headset using any of the available [playtest options](#playtesting). During a VR testing session, if your headset cable disconnects or you close the Roblox Studio Beta app on the headset, you'll need to restart Studio to re-run testing. ## Player emulation For detailed emulation of game [localization](/docs/en-us/production/localization.md) and content policies, you can test through the **Player Emulator**, accessible from Studio's **Test** menu. With the emulator window open, toggle on **Enable Test Profile**. Emulation will remain as toggled (enabled or disabled) even if you close the window. Lower down in the window, commonly used options include: | Option | Description | | --- | --- | | **Locale** | Lets you emulate a [localized](/docs/en-us/production/localization.md) language while playtesting. | | **Pseudolocalize** | Swaps out characters with similar but slightly different characters so that it's easy to identify which strings are going through the [translation](/docs/en-us/production/localization.md) system. Enabling this helps you identify **unlocalized** text without having to change the emulation language. For example: | | **Elongate** | Elongates text strings by a factor defined via the slider. For example:Using elongation helps you identify places where your user interfaces might not be able to handle text that's longer than the default translated text. Note that Spanish is on average 30% longer than English and the equivalent for German is even longer. Also note that this only affects text that goes through the [translation](/docs/en-us/production/localization.md) system. | | **Region** | Lets you emulate a player's country/region while playtesting; this selection may impact other toggles and checkboxes in the window as outlined in `Class.PolicyService:GetPolicyInfoForPlayerAsync()\|GetPolicyInfoForPlayerAsync()`. | --- title: "Texture Generator" url: /docs/en-us/studio/texture-generator last_updated: 2026-06-29T19:34:11Z description: "The Texture Generator tool quickly creates custom textures for meshes through text prompts." --- # Texture Generator > **Success:** This tool is currently in beta. Enable it through **File** ⟩ **Beta Features** ⟩ **Texture Generator**. The **Texture Generator** tool is designed to quickly create custom textures for your meshes through text prompts. You can select a mesh or `Class.Model` made up of several meshes¹, type in any prompt to preview results within a few seconds, then generate full textures. _"rusted metal castle"_ _"mossy stone fortress"_ _"ice covered sculpture"_ > **Info:****Texture Generator** is best suited for custom 3D assets where the desired texture is contextual to the asset itself. To generate repeating or tiled images for texturing surfaces like a wood floor or cobblestone pathway, [Material Generator](/docs/en-us/studio/material-generator.md) may produce better results. _1 When generating a texture for a `Class.Model` group of `Class.MeshPart|MeshParts`, the meshes should be arranged in a sensible way such that a unified body is formed. Avoid generating a texture for an arbitrary group of meshes scattered about the model's bounds, or for more than one identical mesh in the same operation._## Generate textures To begin generating textures, open the **Texture Generator** from Studio's **Window** ⟩ **3D** menu. With the tool's window open: 1. Select a `Class.MeshPart` or a `Class.Model` comprised of `Class.MeshPart|MeshParts` in your scene. 2. In the tool's preview pane, click/drag or enter **X**/**Y**/**Z** values to set the **generation angle** which highlights the significant features or surfaces for texturing. See [best practices](#best-practices) for tips on choosing a generation angle.![Good generation angle focusing on primary faces of a tower mesh](../assets/studio/texture-generator/Generation-Angle-Good.png)_ > **Success:** Focus on the primary faces of a tower mesh, visible to players_![Bad generation angle focusing on bottom of tower mesh](../assets/studio/texture-generator/Generation-Angle-Bad.png)_ > **Warning:** Bottom of tower mesh will not likely be visible to players_ 3. **OPTIONAL** Below the preview pane and prompt input text box, expand the **Art Style** section to upload an image or use an existing [image asset](/docs/en-us/projects/assets.md) to influence the overall style, color palette, and mood of your generated texture.![Art Style options in the Texture Generator window](../assets/studio/texture-generator/Art-Style.png) 1. Expand the **Art Style** section of the tool's window. 2. Specify an image asset that you've already [imported](/docs/en-us/projects/assets/manager.md#importing-assets) to Roblox, or upload an image using the built‑in asset picker. 3. Adjust the **Strength** value. A lower value blends the image more subtly while a higher value makes it the dominant style. 4. In the prompt text box input below the preview pane, enter a prompt describing your desired texture, for instance: - `rusted metal castle` - `mossy stone fortress` - `snow covered ice sculpture` Including `+`, `-`, or a number after keywords increases, decreases, or specifies the emphasis of that keyword, respectively. For example: - `futuristic pirate++ backpack` puts stronger emphasis on `pirate` - `futuristic-- backpack` puts less emphasis on `futuristic` if the original result was too futuristic - `(robotic)1.5 pirate backpack` puts a precise emphasis on `robotic` > **Info:** Using more descriptors in your prompt and iterating can help you find a texture that suits your needs. For more information, see [best practices](#best-practices). 5. Click **Preview** to create a texture preview. When the preview is ready: - Use the "rotate" buttons to rotate the view 90° around the mesh. - Use the **Reroll** button to generate a new texture using the same parameters. - Click the "back" button to return to the prompt input text box.![Texture preview showing in tool window](../assets/studio/texture-generator/Generation-Post-Preview.png) > **Info:** In the preview stage, the texture may not appear visually consistent from all four views. However, the model will create a texture that's more consistent during the texture generation process. 6. When you're satisfied with the texture, click **Save & Apply** to create a full texture for your mesh. When complete, the new texture will be applied to the mesh through a `Class.SurfaceAppearance` instance.![Texture applied to mesh in 3D world](../assets/studio/texture-generator/Texture-Applied.jpg) Generated textures appear in the **Saved Textures** section of the tool's window. For each texture, you can click the **⋯** to access various options. ![Saved Textures list in tool window](../assets/studio/texture-generator/Saved-Textures-List.png) | Option | Description | | --- | --- | | **Insert Mesh with Texture** | Inserts a copy of the textured mesh/model into the workspace. | | **Show in Inventory** | Opens your **Images** inventory on the [Creator Dashboard](https://create.roblox.com/dashboard/creations?activeTab=Image) where you can inspect the texture image. | | **Export** | Exports the mesh to your local system as a `.obj` file. | | **Remove** | Removes the texture from the **Saved Textures** list. | ## Advanced options Expanding the **Advanced Options** section in the tool's window allows you to configure the following aspects. #### Seed Control You can choose to either randomize the seed or set a specific seed for texture generation. Setting a specific seed before generating a texture ensures you get consistent results each time you use a specific prompt. #### Smart UV Unwrap In 3D modeling, a **UV map** is a 2D representation of the surface of a 3D model, allowing 2D textures to be accurately applied to the 3D model. UV coordinates **U** and **V** refer to the horizontal and vertical axes of this 2D space, similar to the **X** and **Y** axes in a 2D graph. For the texture generator tool to create well-formed textures, your mesh's underlying UV maps need to fit a few guidelines. First, all UV coordinates must be **within the unit square**, as UV maps outside of the unit square will wrap around to the other side of the UV map and create artifacts or seams in the final texture. Second, all UVs must be **non‑overlapping**, as overlapping UVs will result in inconsistent textures and color placements. Selecting the **Smart UV Unwrap** option will take a mesh with no UVs (or incompatible UVs) and apply the necessary UV coordinates for texturing. If your UVs are compatible, they won't be affected and you can use your mesh as‑is. #### Specify Front View When this setting is enabled, the **generation angle** selected during the preview stage is specified as the "front" of your mesh. This allows the tool to better texture meshes with a clear front and back by identifying each side, resulting in more consistent and coherent textures. This is particularly helpful for objects with a clear front and back, like avatars, animals, and clothing. ![Front view of character's lab coat with Specify Front View disabled](../assets/studio/texture-generator/Specify-Front-View-Bad-Front.jpg) ![Back view of character's lab coat with Specify Front View disabled](../assets/studio/texture-generator/Specify-Front-View-Bad-Back.jpg) _> **Error:** "Lab Coat with Badge" prompt with **Specify Front View** disabled, yielding lab coat "front" on both front and back_ ![Front view of character's lab coat with Specify Front View enabled](../assets/studio/texture-generator/Specify-Front-View-Good-Front.jpg) ![Back view of character's lab coat with Specify Front View enabled](../assets/studio/texture-generator/Specify-Front-View-Good-Back.jpg) _> **Success:** Same prompt with **Specify Front View** enabled, yielding correct "front" and "back" of lab coat_ ## Best practices #### Prompting tips The following prompting tips enable the tool to provide better and more consistent results. - Provide as much detail as possible in your prompt. Short, vague prompts will lead to inconsistent or unexpected results. - Include texture attributes like material types, colors, patterns, wear and tear, etc. The more constraints you give the tool, the better it can generate what you have in mind. - Use descriptive phrases instead of single words for key items, for example "worn leather" instead of just "leather." - Add styling cues like "cinematic," "photorealistic," "cartoon," or "low poly" to influence the overall look. #### Generation angle The generation angle sets the most important view of your mesh which will be prioritized during generation. Changing this angle when [generating textures](#generate-textures) can often help eliminate inconsistencies in textures. - Setting the generation angle to highlight the meaningful faces of your mesh will result in better generations. - For flat objects like plates, swords, and fences, make sure the generation angle displays a view with a high surface area. - For humanoid and animal meshes, experiment with different generation angles, such as a head-on view of the face and a side view profile, to find the most consistent and coherent texture. #### Iterative refinement Generating the most suitable textures is an iterative process. It helps to preview often and tweak prompts incrementally to reach your vision. - If a texture doesn't meet your expectations, identify what to change instead of starting over. For example, tweak the color or pattern description. - Change the prompt word order. Words at the beginning of the prompt can have more weight when generating. - Generate several previews to compare different prompts, or try the same prompts with different [seeds](#advanced-options) to get a preview that fits your vision. --- title: "Studio interface" url: /docs/en-us/studio/ui-overview last_updated: 2026-06-29T19:34:12Z description: "User interface overview for Roblox Studio, the essential building tool for Roblox experiences." --- # Studio interface ## Toolbar and mezzanine The **toolbar** and **mezzanine** reside at the top of Studio. ![Mezzanine and toolbar indicated in Studio.](../assets/studio/general/Toolbar-Mezzanine.png) The left side of the mezzanine contains easily-accessible [testing controls](/docs/en-us/studio/testing-modes.md#playtest-options). ![Testing controls indicated in Studio's mezzanine.](../assets/studio/general/Mezzanine-Testing-Controls.png) The right side of the mezzanine contains buttons/menus for [collaboration](/docs/en-us/projects/collaboration.md), sharing, [Assistant](/docs/en-us/assistant/guide.md), creator notifications, and account options. ![Other options indicated in Studio's mezzanine.](../assets/studio/general/Mezzanine-Right.png) In the center of the mezzanine are the default tool tabs of **Home**, **Model**, **Avatar**, **UI**, **Script**, and **Plugins**. You can also add [custom tabs](#custom-tabs) for your specific needs. ![Default tabs indicated in Studio's mezzanine.](../assets/studio/general/Mezzanine-Default-Tabs.png) #### Home The **Home** tab contains the core [transform](/docs/en-us/parts.md#transform-parts) tools, [part inserter](/docs/en-us/parts.md#insert-parts), the [color](/docs/en-us/parts.md#color) and [material](/docs/en-us/parts.md#material) widgets, the [group](/docs/en-us/parts.md#group) and [lock](/docs/en-us/parts.md#lock) tools, and [anchor](/docs/en-us/parts.md#anchor) toggle. Also contains the [Terrain Editor](/docs/en-us/studio/terrain-editor.md). #### Model The **Model** tab contains the core [transform](/docs/en-us/parts.md#transform-parts) tools, the [pivot](/docs/en-us/studio/pivot-tools.md) and [align](/docs/en-us/studio/align-tool.md) tools, insertion widgets for [effects](/docs/en-us/effects.md) and [constraints](/docs/en-us/physics.md#constraints), and [solid‑modeling](/docs/en-us/parts/solid-modeling.md) tools. #### Avatar The **Avatar** tab contains the core [transform](/docs/en-us/parts.md#transform-parts) tools, as well as specialized tools for building default rigs, configuring [avatars](/docs/en-us/avatar-setup.md), working with [animations](/docs/en-us/animation.md), and [creating/fitting accessories](/docs/en-us/avatar/accessory-fitting-tool.md). #### UI The **UI** tab contains insertion widgets for [UI objects](/docs/en-us/ui.md#ui-objects) and lets you access the [Style Editor](/docs/en-us/ui/styling/editor.md), a comprehensive tool that allows you to create, manage, and apply UI styles. #### Script The **Script** tab contains tools for writing and testing scripts, including [debugging](/docs/en-us/studio/debugging.md) tools. #### Plugins The **Plugins** tab contains [plugins](/docs/en-us/studio/plugins.md) created by the community or plugins you've created yourself to use across your experiences. ### Appearance You can customize the appearance of the toolbar by right-clicking in an empty area within the mezzanine/toolbar and selecting from the following options: - **Manage tabs** — Reveals a popup window with the following options:![The Manage Tabs popup in Studio.](../assets/studio/general/Manage-Tabs-Popup.png) 1. Show or hide the tab. 2. With a tab selected, reorder it relative to the other tabs. 3. Delete a selected [custom tab](#custom-tabs). 4. Choose between tabs **Center** alignment or **Left** alignment (next to the [testing controls](/docs/en-us/studio/testing-modes.md#playtest-options)). - **Collapse toolbar** — Maximizes space by hiding the toolbar while keeping the mezzanine visible. Hovering over a tab in the mezzanine temporarily reveals the toolbar. - **Compact toolbar** — Makes the toolbar considerably more compact through smaller icons and labels, ideal for laptops or smaller monitors. - **Show labels** — Toggles the text labels below tools on or off. ### Custom tabs Beyond the default tool tabs, you can add **custom tabs** for your specific needs, either through Studio's interface or by editing local `.json` files. #### Studio The easiest way to create custom tabs is through Studio's workflow. 1. Click the `+` button to the right of the default tabs, then type in a name for the new tab and press `Enter`.![Button to add custom tab indicated in Studio's mezzanine.](../assets/studio/general/Toolbar-Add-Custom-Tab.png) > **Success:** Alternatively to creating a new, empty tab, you can duplicate any existing tab by right‑clicking it and selecting **Duplicate tab**. Unlike default tabs, tools within duplicated tabs can be freely modified. 2. Click the button or right‑click in an empty region of the toolbar and select **Add tools**.![Button to add new tool to a custom tab in Studio's toolbar.](../assets/studio/general/Toolbar-Custom-Tab-Add-Tool.png) 3. In the popup window, search for a tool and toggle its checkbox to add. New tools appear to the right of other tools, but you can right‑click any tool and select **Move left** or **Move right** to reorder it relative to other tools. You can also insert a divider by right‑clicking in an empty region of the toolbar and selecting **Add divider**.![Custom tab populated with tools.](../assets/studio/general/Toolbar-Custom-Tab-Filled.png) > **Info:** If you need to remove a tool from a custom tab, right‑click it and select **Remove tool**. #### JSON Custom tab configurations are saved **locally** on each machine in `.json` format, allowing you to build customized tabs and share them with your team or the community. 1. **OPTIONAL** To modify `.json` from a default tab toolset, begin by right‑clicking an existing tab and selecting **Duplicate tab**. 2. Open the custom tabs folder which is stored in a location unaffected by Studio updates. - **Windows** — `%LOCALAPPDATA%\Roblox\\CustomRibbonTabs` - **Mac** — `~/Documents/Roblox//CustomRibbonTabs` 3. In the `CustomRibbonTabs` directory are `.json` files which define the toolbar's structure: - The `RibbonLayout.json` file controls the overall ordering and visibility of tabs.```json { "TabLayout": [ { "Identifier": { "Type": "BuiltIn", "Filename": "HomeTab" }, "Visible": true }, { ``` - Other `.json` files represent individual tabs and can be named whatever you prefer. Each file's `Name` represents the tab's name as it will appear in Studio.```json { "Name": "Custom", "Controls": [ ] } ``` 4. In the `Controls` array for a tab file, insert various buttons, submenus, and more. Each control's `Id` value must be **unique** within the file. The following examples illustrate common elements in a custom tab: #### IconButton`IconButton` inserts a button with an optional text label.```json { "Name": "Custom", "Controls": [ { "Id": "Select", "Type": "IconButton", "Action": { "Category": "Actions", "DataModel": "Standalone", "ItemId": "Select", "PluginId": "BuilderTools" } }, ] } ```#### Separator`Separator` inserts a non-interactable separation line. `Size` options are `Large`, `Medium`, or `Small`.```json { "Name": "Custom", "Controls": [ { "Type": "Separator", "Size": "Large" }, ] } ```#### SplitButton`SplitButton` inserts a button which reveals a dropdown menu when the small triangle to the right of its icon is pressed.```json { "Name": "Custom", "Controls": [ { "Id": "SimpleSplit", "Type": "SplitButton", "Action": { "Category": "Actions", "DataModel": "Standalone", "ItemId": "Select", "PluginId": "BuilderTools" }, "Children": [ { "Type": "Column", "Children": [ { "Id": "Move", "Type": "Option", "Action": { "Category": "Actions", "DataModel": "Standalone", "ItemId": "Move", "PluginId": "BuilderTools" } }, { "Id": "Scale", "Type": "Option", "Action": { "Category": "Actions", "DataModel": "Standalone", "ItemId": "Scale", "PluginId": "BuilderTools" } }, { "Id": "Rotate", "Type": "Option", "Action": { "Category": "Actions", "DataModel": "Standalone", "ItemId": "Rotate", "PluginId": "BuilderTools" } }, ] } ] } ] } ```#### IconButton (Plugin)`IconButton` can be used to insert a button which links to a [plugin](/docs/en-us/studio/plugins.md) through its ID, assuming the plugin, such as [Tailor Swiftly](https://create.roblox.com/store/asset/130172194397869/Tailor-Swiftly), is installed in your version of Studio.```json { "Name": "Custom", "Controls": [ { "Id": "PrinceTybalt_Tailor Swiftly", "Type": "IconButton", "Action": { "Category": "Actions", "DataModel": "Edit", "ItemId": "PrinceTybalt_Tailor Swiftly", "PluginId": "130172194397869", "PluginType": "Cloud" } } ] } ```#### Text`Text` inserts a basic text label, useful to label groups of other controls in submenus.```json { "Name": "Custom", "Controls": [ { "Id": "CustomHeader", "Type": "Text", "Title": "Insert UI" }, ] } ``` > **Success:** Beyond the above examples, you can right-click any default tab and select **Duplicate tab**. This will create a new custom tab and corresponding `.json` file in your local `CustomRibbonTabs` directory which you can inspect to explore how various default tools and menus are constructed. 5. Save the `.json` file and then reload Studio's plugins: 1. Right-click anywhere in the toolbar or mezzanine and select **Manage tabs**. 2. Access the options menu from the popup's upper‑right corner and select **Reload custom tabs**.![Options menu in the Manage Tabs popup.](../assets/studio/general/Manage-Tabs-Popup-Options.png) ## 3D viewport The 3D viewport represents the `Class.Workspace` of a place. From here, you can move the camera around the virtual world, manipulate objects with the mouse, and playtest an experience without leaving Studio. ![An example viewport display of a desert environment with multiple stone pillars. One of the pillars displays movement visual aids from the Move tool.](../assets/studio/general/Editor-Window.jpg)_3D viewport in Roblox Studio_ ### Camera controls Inside the viewport, you can move the camera with the following controls. | Keys/Shortcuts | Action | | --- | --- | | `W` `A` `S` `D` | Moves the camera forward/left/back/right. | | `Q` `E` | Moves the camera down/up. | | `Shift` | In combination with any movement key, changes the camera speed. | | `F` | Focuses the camera on a selected part. | | **Right Mouse Button** | When pressed, dragging the mouse moves the camera view around. | | **Mouse Scroll Wheel**
`Ctrl``=` or `⌘``=`
`Ctrl``-` or `⌘``-` or `O` | Zooms the camera in or out.
Zooms the camera in.
Zooms the camera out. | | `,` / `.` | Rotates the camera left/right. If a part/model is in focus via the `F` shortcut, rotates the camera counterclockwise/clockwise around that focused object. | | **Middle Mouse Button** | When pressed, dragging the mouse pans the camera. | | **Right Mouse Button** & **Mouse Scroll Wheel** | Pressing the right mouse button and scrolling the mouse wheel **up** increases the camera scroll speed. Conversely, pressing the right mouse button and scrolling the mouse wheel **down** reduces the camera scroll speed. > **Success:** This feature is currently in beta. Enable it through **File** ⟩ **Beta Features** ⟩ **New Studio Camera Controls**. | ### Object selection As you hover over parts and models in the viewport, they are outlined to indicate their potential selection. You can select an outlined object by clicking it, or you can select multiple objects by holding `Shift`, `Ctrl`, or `⌘` as you hover over and click them. ![A warehouse environment with the stairs and multiple pallet box objects with a blue highlight to signify that they are selected in the 3D viewport.](../assets/studio/general/Editor-Window-Object-Selection.jpg) In more complex environments or when zoomed in, objects will commonly be hidden from your view by other objects in front. To select hidden objects without moving the camera around, hold `Alt` or `⌥` and click to perform **selection cycling** to the next further object behind the currently selected object. > **Info:** When one or more objects or [models](/docs/en-us/parts/models.md) are selected in the [Explorer](/docs/en-us/studio/explorer.md) window, you can select all of their **children** by right-clicking and choosing **Hierarchy** ⟩ **Select Children** from the context menu. Similarly, choosing **Select Parent(s)** selects the **direct parents** of those objects. To customize the appearance of selection lines and boxes around objects, set your desired options in the **Selection Visualization** section within the **Selection** panel of Studio's settings. ![A warehouse environment with rope object's selection outline and bounding box highlighted.](../assets/studio/general/Selected-Object-Bounding-Box.jpg) ### Visualization options In the upper-right corner of the 3D viewport, you can quickly toggle or set common visualization options related to [on‑screen UI](/docs/en-us/ui/on-screen-containers.md) overlays, [light sources](/docs/en-us/effects/light-sources.md), physics simulation, character [pathfinding](/docs/en-us/characters/pathfinding.md), and more. The menu also contains a control for viewing/setting the camera scroll speed. ![A close up view of the 3D viewport with the Visualization Options button indicated in the upper-right corner.](../assets/studio/general/Visualization-Options.png) ## Frequently-used tools Commonly used windows include the [Explorer](/docs/en-us/studio/explorer.md), the [Properties](/docs/en-us/studio/properties.md) window, [Asset Manager](/docs/en-us/projects/assets/manager.md), [Toolbox](/docs/en-us/projects/assets/toolbox.md), and others. ### Explorer The [Explorer](/docs/en-us/studio/explorer.md) window, visible by default and accessible from Studio's **Window** menu or **Home** tab, shows a hierarchical list of every object and service inside the place, collectively referred to as its **data model**. Frequently used services in the hierarchy include `Class.Workspace` which mirrors the [3D viewport](#3d-viewport), as well as `Class.ReplicatedStorage` and `Class.ServerStorage`. ![Explorer hierarchy showing Camera, Terrain, and SignModel as children of Workspace; Board and Post as children of SignModel](../assets/studio/explorer/Parent-Child-Hierarchy.png) ### Properties The [Properties](/docs/en-us/studio/properties.md) window, visible by default and accessible from Studio's **Window** menu or **Home** tab, lets you adjust properties of a selected object to change how it looks and behaves. Object properties are divided into sections; for example, a `Class.MeshPart` includes sections like **Appearance** which allows you to change its color, material, transparency, and more. At the bottom of the window, you can define custom [attributes](/docs/en-us/studio/properties.md#instance-attributes) for an object, similar to its default properties. Attributes and their values are saved with your place/assets and they're an ideal way for team members to experiment with different values during runtime, even if they don't understand the underlying code. ![A close up view of the Attributes section of the Properties window.](../assets/studio/properties/Attributes-Example.png) ### Asset Manager The [Asset Manager](/docs/en-us/projects/assets/manager.md), accessible from Studio's **Window** menu or **Home** tab, lets you manage places, images, meshes, audio, and more. It also provides a mechanism to bulk import large groups of assets. ### Toolbox The [Toolbox](/docs/en-us/projects/assets/toolbox.md), accessible from Studio's **Window** menu or **Home** tab, contains a selection of models, images, meshes, audio, plugins, videos, and fonts made by Roblox or Roblox community members. It also includes all of the creations that you've personally [distributed on the Creator Store](/docs/en-us/production/creator-store.md) or those which were distributed by [groups](/docs/en-us/projects/groups.md) you belong to. ![The Toolbox window.](../assets/studio/toolbox/Model-Search-Example.png) ### Output The [Output](/docs/en-us/studio/output.md) window, accessible from Studio's **Window** menu or **Script** tab, displays errors captured from running scripts, messages from Roblox Engine, messages from calls to `Global.LuaGlobals.print()`, and errors from calls to `Global.RobloxGlobals.warn()`. ### Command Bar The **Command Bar**, accessible from Studio's **Script** tab toolbar or the **Window** ⟩ **Script** menu, lets you execute Luau code outside of scripts. To quickly jump to the command bar at any time, press `Ctrl``9` (Windows) or `⌘``9` (Mac). > **Success:** Most of the features outlined below are in beta and must be enabled through **File** ⟩ **Beta Features** ⟩ **Multi-line Command Bar**. Features include: - **Multiline editing** — Press `Enter` to insert new lines while typing. The bar starts with 2 lines, grows up to 10 lines as new lines are inserted, and shrinks as lines are deleted. When you're ready to run/execute the contents of the bar, click the **Run** button or use the shortcut `Ctrl``Enter` (`⌘``Enter`). - **Command history** — The last 10 executed commands are visible by clicking the **History** button and navigating to the **Recent** tab. Clicking on a command in the list loads it into the bar, or you can navigate the list with `↑`/`↓` and insert the highlighted command with `Enter`. Alternatively, you can scrub through commands with`Ctrl`/`⌘` + `↑`/`↓` when the command bar is selected. - **Saved commands** — You can explicitly save commands with the **Save** button or the default shortcut `Ctrl``B` (`⌘``B`). Saved commands can be accessed through the **Saved** tab in the popup list, and the mechanics of loading saved commands from the list are the same as those in the command history. - **Script Editor integration** — The command bar is an embedded script editor and supports a wide subset of Studio's [Script Editor](/docs/en-us/studio/script-editor.md) features, including [autocomplete](/docs/en-us/studio/script-editor.md#autocomplete-features), Luau [linting](https://luau.org/lint), [go to declaration](/docs/en-us/studio/script-editor.md#go-to-declaration), [function filter](/docs/en-us/studio/script-editor.md#script-function-filter), [Code Assist](/docs/en-us/studio/script-editor.md#code-assist), and [multi‑cursor](/docs/en-us/studio/script-editor.md#multi-cursor). ## Layout customization Studio's drag-and-drop interface lets you easily customize window layout to best suit your workflows. ### Reposition windows You can reposition any window by click-dragging its **header bar**. As you begin dragging the window, the interface reveals **empty docking area** regions. If you drag the mouse pointer into any empty region, a floating **position selector** appears in that region. Dragging the pointer over the "center" of the floating selector targets the empty region as the dragged window's destination, indicated by the region darkening and becoming outlined. ![Mouse pointer hovering over the 'center' icon of the position selector in an empty docking area, showing that region as the dragged window's destination.](../assets/studio/general/Docking-Position-Selector-Empty.png)_Mouse pointer hovering over the "center" icon of the position selector in an empty docking area_ If you drag the mouse pointer into a **populated** region such as the 3D viewport, a floating position selector appears with multiple options for the window's destination. For example, dragging the pointer over the "left" icon will position the window on the region's left side. #### Viewport left ![Mouse pointer hovering over the 'left half' icon of the position selector in a populated region, showing the left side of the region as the dragged window's destination.](../assets/studio/general/Docking-Position-Selector-Left.png) #### Viewport right ![Mouse pointer hovering over the 'right half' icon of the position selector in a populated region, showing the right side of the region as the dragged window's destination.](../assets/studio/general/Docking-Position-Selector-Right.png) ### Group windows If you drag a window into a **populated** region and choose the "center" icon of the [position selector](#reposition-windows), the dragged window will group into that region as a tab. This allows you to set up tab groups of commonly used windows. ![An example of an Explorer window if you choose the center icon of the position selection. It includes three tabs at the bottom of the window: the Explorer window, the Properties window, and the Terrain Editor window.](../assets/studio/general/Docking-Grouped-Tabs.png)_Three windows grouped together as tabs_ ### Pin windows To maximize screen space for the 3D viewport, you can **pin** windows (or an entire [group](#group-windows) of windows) to the edges of the overall Studio window. Once pinned, you can temporarily open any window by clicking its tab — this does not disturb other windows, and the expanded window will automatically collapse when you click in another window or click its tab again. ![An example 3D viewport where the Explorer, Properties, and Terrain Editor windows are pinned to the right of the Studio window.](../assets/studio/general/Docking-Pinned-Tabs.png)_Three windows pinned to the right side of the Studio window_ To pin a window or an entire [group](#group-windows) of windows, click the "collapse" button. Alternatively, to un‑pin a pinned window/group, open it by clicking its tab, then click the "expand" button. ![A close up view of the Explorer window with the Collapse button highlighted.](../assets/studio/general/Docking-Expand-Collapse.png) ### Float windows To float a window freely of other windows, simply drag‑and‑drop it without selecting any icon from the floating [position selector](#reposition-windows).
--- title: "Get started" url: /docs/en-us/tutorials/curriculums/animator/get-started last_updated: 2026-06-29T19:34:11Z description: "Explains how to get started with opening a project." --- # Get started Welcome to your first beginner animation lesson in Studio! To get started, let's walk through how to open the sample template, customize Studio's layout for your tasks, and playtest the core gameplay of the experience where players move around a track to earn stars. ## Open template Templates are pre-made 3D worlds that you can use as the base for your own experiences. To introduce you to animation in Studio, Roblox has created the **Island of Move** template with everything necessary for the experience to work properly, such as the code to run the gameplay and an environment that you can customize at a later time. > **Info:** For a full list of Roblox-provided templates, see [Templates](/docs/en-us/resources/templates.md). Click the following button to open the **Island of Move** template: ## Customize workspace Roblox Studio launches with extra windows open that you don't need for this lesson. You can close these extra windows to create more space for what you're currently working on. To customize the workspace for your animating task: 1. Close any open window on the **left side** of Studio by clicking the **×** in the window's upper-right corner. If you don't see anything to close, go to the next step. 2. Leave the **Explorer** and **Properties** window open on the right side of Studio. If you have done each step correctly, Studio looks like the following image. > **Warning:** If you don't see the **Explorer** and **Properties** windows, open them from Studio's **Window** menu. ## Playtest the experience Before you start animating, it's important to take a moment to playtest the template so you can see how the gameplay works. When players join the experience, they make progress around the track by clicking the move button, or using a mobile device and moving around in real-life. To playtest the experience's gameplay: 1. In the top-left corner of Studio, keep the default **Test** playtest option, then click the **Play** button. Studio enters playtest mode. > **Info:** There are a variety of additional playtest options that you will explore in a later chapter of the tutorial. 2. In the experience, use the following player controls to walk up to the golem character on the stage, then press `E` to open up a menu. From that menu, click **Play Game**. | **Action** | **Control** | | --- | --- | | **Move** | `W` `A` `S` `D` or arrow keys | | **Rotate** | Hold the right mouse button and look around. | | **Pan** | Hold the middle mouse button to drag your camera around. | 3. When the experience starts, go forward by repeatedly clicking the **Move** button. Your character automatically follows the track as you click. 4. When you're done, return to the top-left corner of Studio, then click the **Stop** button. Studio exits playtest mode. Now that you are familiar with the gameplay, it's time to start animating! --- title: "Intro to animation" url: /docs/en-us/tutorials/curriculums/animator last_updated: 2026-06-29T19:34:11Z description: "Learn the basics of animating characters." --- # Intro to animation Using the [Island of Move](https://www.roblox.com/games/5306359293/Island-of-Move) sample as a guide, this tutorial acts as an introduction to creating animations in Roblox Studio. As you follow each chapter, you will learn how to create keyframes for various poses, then loop them into a swimming animation that you can reuse in any experience. This lesson is intended for readers who are **brand new** to creating animations and working in Studio. If you are familiar with both, try another beginner lesson plan or jump straight to [building your first experience](/docs/en-us/tutorials/core.md). > **Info:** If you don't have a Roblox account, Studio active on your device, or familiarity with Studio's UI, see the [Studio lesson](/docs/en-us/tutorials/studio.md). ## Course contents **#### Chapter 1 - Get started** Learn how to [open and customize](/docs/en-us/tutorials/curriculums/get-started.md) the template's workspace so that you can work with a poseable figure. **#### Chapter 2 - Work with the Animation Editor** Learn how to [use the Animation Editor](/docs/en-us/tutorials/curriculums/work-with-the-animation-editor.md) to create keyframes for different poses of an animation. **#### Chapter 3 - Play your animation** Learn how to [export and replace](/docs/en-us/tutorials/curriculums/play-your-animation.md) the character's default run animation with your new animation. **#### Chapter 4 - Test and save** Learn how to [playtest your animation](/docs/en-us/tutorials/curriculums/test-and-save.md) and publish your work to Roblox. **#### Chapter 5 - Next steps** Learn where you can [find more tutorials](/docs/en-us/tutorials/curriculums/next-steps.md) about animating characters for experiences. --- title: "Next steps" url: /docs/en-us/tutorials/curriculums/animator/next-steps last_updated: 2026-06-29T19:34:12Z description: "Explains ways to continue learning about creating animations in Studio." --- # Next steps Congratulations on completing your first animation with Roblox Studio! Now that you have experience working with keyframes and posing characters, you can share your work with others and extend your skills by learning about Studio's additional animation features. ## Share with others When you first publish an experience, it's automatically set to **private** so that you are the only one who can play it. However, creating on Roblox is always more fun when you can share what you make with others, so let's set up your experience so that they can play too. To make your experience public: 1. Using the steps from the last chapter, publish your experience so others can see your latest changes. 2. In the top-left corner of your computer, click **File** ⟩ **Experience Settings**. 3. In the settings window: 1. Go to the left bar and select the **Permissions** tab. New settings display. 2. Set **Playability** to **Public**. 4. Click the **Save** button. Now everyone on Roblox can play your experience! ## Continue learning The basic **Animation Editor** tools are an excellent start while learning to create animations, but Studio offers many additional animation features to add liveliness to your characters and environment, including: - [Event markers](/docs/en-us/animation/events.md) that trigger custom behavior at specific keyframes in an animation. - [Inverse Kinematics](/docs/en-us/animation/inverse-kinematics.md) that let you pose and animate multiple character parts by posing or adjusting a single object. - The [Curve Editor](/docs/en-us/animation/curve-editor.md) interface that lets you see and modify how a rig's position and orientation changes between keyframes through color-coded curve graphs. If you want to continue learning through step-by-step lessons, check out the following use case tutorials: - [Create character animations](/docs/en-us/tutorials/use-case-tutorials/animation/create-an-animation.md) - [Play character animations](/docs/en-us/tutorials/use-case-tutorials/animation/play-character-animations.md) - [Rig simple meshes in Blender](/docs/en-us/art/modeling/rig-a-simple-mesh.md) - [Skin simple meshes in Blender](/docs/en-us/art/modeling/skin-a-simple-mesh.md) - [Rig humanoid models in Blender](/docs/en-us/art/modeling/rig-a-humanoid-model.md) - [Skin humanoid models in Blender](/docs/en-us/art/modeling/skin-a-humanoid-model.md) --- title: "Play your animation" url: /docs/en-us/tutorials/curriculums/animator/play-your-animation last_updated: 2026-06-29T19:34:12Z description: "Explains how to export and replace a default character animation with your custom animation." --- # Play your animation In order to play your animation in your open experience, as well as store it for reuse in other projects, you must publish the animation to the cloud. This process creates a unique animationID for your animation that you can reference in scripts, which is especially important if you want to replace any of Roblox's default character animations. In this chapter, let's go over how to export your animation by publishing it to Roblox, then swap out the template's default walk animation with your own creation. ## Export new animation If you were to close Studio right now, you would lose all of your work on your animation because your animation doesn't have an **animationID** that Studio can reference for your projects. To fix this, you must export your animation to Roblox so that Studio can connect the animation to your account. To export your new animation: 1. In the **Animation Editor**, click on the **⋯** and select **Publish to Roblox**. The **Asset Configuration** window displays. 2. In the **Asset Configuration** window, give your animation a name and description, then click the **Save** button. After a few moments, the window confirms your animation was successfully submitted. 3. Copy your animation's animationID by clicking the icon next to the ID. A green "_ID Copied!_" text pop-up displays when you have done this step correctly. 4. Click the **Close** button. ## Replace default animation Every player character includes a set of **default animations** that play whenever the character performs specific in-experience actions, such as running, climbing, or jumping. Roblox provides these animations out-of-the-box for every experience without any additional coding effort. _Default Fall Animation_ _Default Swim Animation_ _Default Climb Animation_ You can swap out default animations with your own custom animations so that they play for every player that joins your experience. For this lesson, let's swap out the default walking animation with your swim animation so that you can see how it looks with your avatar as soon as you start playing. To replace the default walking animation with your custom swimming animation: 1. Find the default walk animation. 1. In the **Explorer** window, go to the search bar, then type **walk**. 2. In your search results, click on **WalkAnimationID**. > **Warning:** If you don't see the **Explorer** and **Properties** windows, open them from Studio's **Window** menu. 2. In the **Properties** window, click the **Value** field and paste your animation ID by pressing `Ctrl``V` or `⌘``V`, depending on your computer. > **Info:****Need to Get the Animation ID?** If your animationID didn't paste into the Value field, you need to get your animation's animationID again. To do this: 1. Go to the [Creator Dashboard](https://create.roblox.com/dashboard/creations). 2. In the horizontal navigation, select **Development Items**, then click the **Animations** button. All animations you have ever published display here. 3. Select your animation. The animation's **Configure** page displays. 4. In the left-hand navigation, click the vertical three dots icon, then select **Copy Asset ID** from the contextual menu. --- title: "Test and save" url: /docs/en-us/tutorials/curriculums/animator/test-and-save last_updated: 2026-06-29T19:34:12Z description: "Explains how to playtest and save your fitness experience." --- # Test and save Now that your poses are complete and set up to play as your character's run animation, it's time to playtest your experience to verify that the animation plays properly, then publish your work to Roblox so that you can save your progress. ## Playtest your animation It's important to playtest your experience often to ensure that your animation plays exactly the way you want it to play for your players. Studio offers several playtest options: - **Test** – Starts the playtest by inserting your avatar at a spawn location. - **Test Here** – Starts the playtest by inserting your avatar in front of the camera's current position. - **Run** – Starts the playtest without inserting your avatar. Instead, the playtest begins at the current camera position and you can navigate around using the camera controls. While all options are useful at different times, it's important to use a playtest option that inserts your avatar so that you can see your animation play. To playtest your animation: 1. In the top-left corner of Studio, verify that your playtest is set to either **Test** or **Test Here**, then click the **Play** button. Studio enters playtest mode. 2. Use the `WASD` keys to walk around and see the animation play. > **Warning:** If you make updates to the animation, remember to **Save** it in the **Animation Editor**. 3. When you're done, return to the top-left corner of Studio, then click the **Stop** button. Studio exits playtest mode. If you're running into any issues with your animation, check out the following troubleshooting tips: **Issue:** Animation is spotty, skips, or looks glitchy. - Make sure that the first and last poses are the same. If they aren't, right-click on the diamond for the first pose and copy it, then paste that pose at the end of the timeline. - Check that the figure stays inside the white grid when you select it. If not, it may go through walls or the ground when the animation plays. **Issue:** Animation isn't the one that you were working on. - Check that you've published the animation through the **Animation Editor**. - In the **Explorer** window, find an object named **WalkAnimationID**, then make sure its `Value` property is only a number. ## Publish to Roblox You have access to your animation ID for other projects, but if you were to close Studio now, you would lose every edit you made to your experience. For this reason, it's important to publish your work to Roblox often to save your work and connect the experience to your account. > **Info:** It's recommended to publish to Roblox every ten minutes or after making a big change. 1. In the top-left corner of your computer, click **File** → **Publish to Roblox** to open the publishing window. 2. In the **Publish Experience** window, 1. In the **Name** field, provide a name for your experience. 2. **OPTIONAL** In the **Description** field, provide a summary of what a player can expect from the experience. 3. In the **Devices** section, enable every device you want players to use to access your experience. Because this experience allows players to use mobile devices with accelerometers as a way to move characters, it's recommended to at least enable phones. 4. At the bottom-right of the window, click the **Create** button. Now that your experience is published and connected to your account, you can edit it from any computer! > **Info:****Saving after publishing** Next time you want to save your work, just go to **File** → **Publish to Roblox** or use the hotkey combo (`Alt``P`/`⌥``P`). --- title: "Work with the Animation Editor" url: /docs/en-us/tutorials/curriculums/animator/work-with-the-animation-editor last_updated: 2026-06-29T19:34:12Z description: "Explains how to work with the Animation Editor to create a custom animation." --- # Work with the Animation Editor The **Animation Editor** lets you move and rotate your characters' body parts like an action figure into unique poses at different keyframes of an animation. Studio then automatically creates keyframes in between those poses that flow together to make your characters appear as if they're moving. In this chapter, let's explore how to open the **Animation Editor**, customize your settings to help with posing your character, and create an animation with two unique poses that Studio flows together to make it look like the character is swimming in water. ## Open Animation Editor The **Animation Editor** is Studio's primary animation tool. While it comes with a lot of functionality, such as [animation capture](/docs/en-us/animation/capture.md) and [inverse kinematics](/docs/en-us/animation/inverse-kinematics.md) tools, this lesson focuses on its basic tools to help you get started in learning how to create animations. 1. At the top of the toolbar, go to the **Avatar** tab. 2. Click the **Clip Editor** button. The **Animation Editor** displays at the bottom of Studio. ## Prepare rig The **Animation Editor** requires a **rig**, or poseable figure, to create character animations because rigs include the internal structure you need to move and rotate body parts into different poses. While you can create your own rigs or access several pre-built rigs through the **Rig Generator** tool, this lesson provides a rig on top of the pedestal in the experience. To prepare your rig: 1. Select the **Figure** rig on top of the pedestal. The part names that make up the rig's body parts display in the **Animation Editor**. 2. In the toolbar, find the snapping tools and make sure **Rotate** and **Move** are not checked. 3. Using the camera controls, move the camera to get a good view of the part you want to animate. While moving, hold `Shift` to slow down camera movements. | **Action** | **Control** | | --- | --- | | **Move** | `W` `A` `S` `D` | | **Rotate** | Hold the right mouse button to rotate around the selected object. | | **Zoom** | Use your mouse's scroll wheel. | | **Focus** | Press `F` to focus on a the selected object. | > **Warning:****Can't move the camera?** If the camera doesn't move when you press `W` `A` `S` `D`, click somewhere inside the 3D viewport. ## Create first pose Now that the **Animation Editor** is open and your rig preparation is complete, it's time to start posing your character for the first pose of your animation. This lesson focuses on a swimming animation, but feel free to adjust the learnings for your own poses. Depending on the animation you want to create, your first pose may be very different. _Swimming_ _Crouching_ _Dancing_ > **Info:** Creating poses is an art form, and it takes time to figure out how to move and rotate body parts to meet the needs of your animation. It's normal and expected for your poses to look and feel different from the lesson's video examples. To create your first pose for a swimming animation: 1. Back in the toolbar, select the **Rotate** tool, then click on the rig's **LeftUpperArm**. A set of handles display around the body part. 2. Click and drag the handles to rotate the rig's left arm, and note how each handle rotates the arm in a different direction. You can undo any change by pressing either `Ctrl``Z` or `⌘``Z`, depending on your computer. 3. Rotate different body parts until your first pose is complete. Make sure to change the camera angle as you're working so that you can see the pose from multiple points of view. > **Warning:** If you're running into any issues while trying to rotate your rig's body parts, check out the following troubleshooting tips: > > **Issue:** You can't click on rotation handles. - Another part may be blocking your click. - Try rotating around your camera until you see a blue box whenever you hover over the part you want to click. > > **Issue:** You can't see the rotation handles. - If you see a set of arrows instead of the circles, you need to select the **Rotate** tool in the toolbar. ## Duplicate keyframes Before you begin your second pose, let's take a moment to duplicate your first pose at the end of your animation. This process ensures that your animation repeats smoothly as it loops. To move your first pose to the end of the animation, you need to move the **scrubber** that marks the current keyframe position to the end of the timeline. The **timeline** is the numbered line at the top of the **Animation Editor** that represents your animation in seconds. _Scrubber_ _Timeline_ To duplicate your first pose's keyframes to the end of the animation: 1. In the **Animation Editor**, click on the **white diamond** at the top of the timeline. This diamond represents every body part's rotational data for your first pose at `0:00` on the timeline. Note that the white diamond displays with a blue border when you have done this step correctly. 2. Copy the first pose by pressing `Ctrl``C` or `⌘``C`, depending on your computer. 3. Click the **scrubber**, then drag it to the end of the animation at `1:00` on the timeline. 4. Paste your first pose at this new timeline position by pressing `Ctrl``V` or `⌘``V`, depending on your computer. A new set of diamonds display at the end keyframe. 5. To loop the animation, click the **Loop** icon. Your animation will now repeat seamlessly. ## Create second pose Using the same process as earlier in the lesson, it's time to create your second pose! After you design this unique pose, Studio will automatically create keyframes in between those poses that flow together to make your characters appear as if they're swimming. To create your second pose for a swimming animation: 1. Set the scrubber in the middle of the animation. You can either drag the scrubber to roughly be in the middle of the timeline, or you can manually set it to `0.5` in the **position indicator** to the left of the timeline. 2. With the **Rotate** tool still active, rotate different body parts until your second pose is complete. Remember to change the camera angle as you're working so that you can see the pose from multiple points of view. > **Info:****Posing Faster** Speed up posing by rotating body parts connected to the torso first, such as the upper arm and thigh, then work outwards to pose the hands and feet. > **Warning:****Start a Pose from Scratch** Don't like a pose and want to just start from the beginning? You can reset your character to the default pose using the following process: 1. Right click on the top white diamond of the pose you want to reset. A menu displays. 2. From the menu, select **Reset Selected**. 3. In the **Animation Editor**, click the **Play** button to test your animation. Feel free to make adjustments to either of your poses until it looks like the character is smoothly swimming in water. Congratulations on posing your character for your first animation! The next lesson details how to save and export your animation so that you can use it within experiences. --- title: "Get started" url: /docs/en-us/tutorials/curriculums/artist/get-started last_updated: 2026-06-29T19:34:12Z description: "Explains how to get started with opening a project." --- # Get started Welcome to your first beginner VFX art lesson in Studio! To get started, let's walk through how to open the sample template, customize Studio's layout for your tasks, and playtest the core gameplay of the experience where players join a carnival ride and defend their cart from enemies. ## Open template Templates are pre-made 3D worlds that you can use as the base for your own experiences. To introduce you to animation in Studio, Roblox has created the **Mansion of Wonder** template with everything necessary for the experience to work properly, such as the code to run the gameplay and an environment that you can customize at a later time. > **Info:** For a full list of Roblox-provided templates, see [Templates](/docs/en-us/resources/templates.md). Click the following button to open the **Mansion of Wonder** template: ## Customize workspace Roblox Studio launches with extra windows open that you don't need for this lesson. You can close these extra windows to create more space for what you're currently working on. To customize the workspace for your VFX art task: 1. Close each window on the **left** of the 3D view by clicking the **×** in the window's upper-right corner. If you don't see anything to close on the left, go to the next step. 2. Leave the **Explorer** and **Properties** windows open on the right side of Studio. If you have done each step correctly, Studio looks like the following image. > **Warning:** If you don't see the **Explorer** and **Properties** windows, toggle them on from Studio's **Window** menu. ## Playtest the experience Before you start creating VFX art, it's important to take a moment to playtest the template so you can see how the gameplay works. When players join the experience and jump into the carnival ride, slimes and ghosts begin to slowly advance toward the player's moving cart. If these enemies touch the cart too many times, the cart explodes. To playtest the experience's gameplay: 1. In the top-left corner of Studio, keep the default **Test** playtest option, then click the **Play** button. Studio enters playtest mode. > **Info:** There are a variety of additional playtest options that you will explore in a later chapter of the tutorial. 2. In the experience, use the following player controls to walk up to the golem character on the stage, then press `E` to open up a menu. From that menu, click **Play Game**. | **Action** | **Control** | | --- | --- | | **Move** | `W` `A` `S` `D` or arrow keys | | **Rotate** | Hold the right mouse button and look around. | | **Pan** | Hold the middle mouse button to drag your camera around. | 3. When the ride starts, left click to shoot the blaster at enemies. Watch the cart's health at the top of the screen and try and make it to the treasure room at the end. > **Warning:** During the ride, you can't use your mouse to click parts of Studio. To use the mouse, press `1` on the keyboard. 4. When you're done, return to the top-left corner of Studio, then click the **Stop** button. Studio exits playtest mode. Now that you are familiar with the gameplay, it's time to start creating special effects! --- title: "Intro to VFX art" url: /docs/en-us/tutorials/curriculums/artist last_updated: 2026-06-29T19:34:12Z description: "Learn the basics of creating special effects for experiences." --- # Intro to VFX art Using the [Mansion of Wonder](https://www.roblox.com/games/6901029464/Mansion-of-Wonder) sample as a guide, this tutorial acts as an introduction to creating special effects (VFX) in Roblox Studio using particle emitters. As you follow each chapter, you will learn how to create a laser blast and explosion that players can use as they defeat sinister slimes and ghastly ghosts in a spooky carnival experience. This lesson is intended for readers who are **brand new** to creating VFX art and working in Studio. If you are familiar with both, try another beginner lesson plan or jump straight to [building your first experience](/docs/en-us/tutorials/core.md). > **Info:** If you don't have a Roblox account, Studio active on your device, or familiarity with Studio's UI, see the [Studio lesson](/docs/en-us/tutorials/studio.md). ## Course contents **#### Chapter 1 - Get started** Learn how to [open and customize](/docs/en-us/tutorials/curriculums/get-started.md) the template's workspace so that you can focus on creating colorful VFX. **#### Chapter 2 - Work with particle emitters** Learn how to [change the appearance and behavior](/docs/en-us/tutorials/curriculums/work-with-particle-emitters.md) of particles in the carnival ride. **#### Chapter 3 - Test and save** Learn how to [playtest your art](/docs/en-us/tutorials/curriculums/test-and-save.md) and publish your work to Roblox. **#### Chapter 4 - Next steps** Learn where you can [find more tutorials](/docs/en-us/tutorials/curriculums/next-steps.md) about VFX for experiences. --- title: "Next steps" url: /docs/en-us/tutorials/curriculums/artist/next-steps last_updated: 2026-06-29T19:34:12Z description: "Explains ways to continue learning about creating special effects in Studio." --- # Next steps Congratulations on completing your first VFX art with Roblox Studio! Now that you have experience creating custom special effects with unique visuals and behavior, you can share your art with others and extend your skills by learning about Studio's additional VFX features. ## Share with others When you first publish an experience, it's automatically set to **private** so that you are the only one who can play it. However, creating on Roblox is always more fun when you can share what you make with others, so let's set up your experience so that they can play too. To make your experience public: 1. Using the steps from the last chapter, publish your experience so others can see your latest changes. 2. In the top-left corner of your computer, click **File** ⟩ **Experience Settings**. 3. In the settings window: 1. Go to the left bar and select the **Permissions** tab. New settings display. 2. Set **Playability** to **Public**. 4. Click the **Save** button. Now everyone on Roblox can play your experience! ## Continue learning Particle emitters are an excellent start for creating VFX, but Studio offers many additional VFX features to boost the visuals of your experiences, including: - [Beams](/docs/en-us/effects/beams.md) that render a texture between two points that stay still. - [Trails](/docs/en-us/effects/trails.md) that render a texture between two points that move. - [Flipbooks](/docs/en-us/effects/particle-emitters.md#flipbooks) that animate a texture over time. - [Light sources](/docs/en-us/effects/light-sources.md) that emit light at specific points in the 3D space. _Beams_ _Trails_ _Flipbooks_ ![A far out view of a diner bulding in the middle of a nighttime city. The diner is utilizing several local light sources.](../../../assets/lighting-and-effects/light-sources/Showcase.jpg)_Light sources_ If you want to continue learning through step-by-step lessons, check out the following use case tutorials: - [Create laser beams](/docs/en-us/tutorials/use-case-tutorials/vfx/laser-traps-with-beams.md) - [Create waterfalls](/docs/en-us/tutorials/use-case-tutorials/vfx/create-waterfalls.md) - [Create volcanoes](/docs/en-us/tutorials/use-case-tutorials/vfx/create-volcanoes.md) - [Enhance indoor lighting](/docs/en-us/tutorials/use-case-tutorials/lighting/enhance-indoor-environments.md) - [Enhance outdoor lighting](/docs/en-us/tutorials/use-case-tutorials/lighting/enhance-outdoor-environments.md) --- title: "Test and save" url: /docs/en-us/tutorials/curriculums/artist/test-and-save last_updated: 2026-06-29T19:34:12Z description: "Explains how to playtest and save your carnival experience." --- # Test and save Now that your VFX art is complete, it's time to playtest your experience to verify that both particle emitters emit particles properly and with the correct visuals, then publish your work to Roblox so that you can save your progress. ## Playtest your art It's important to playtest your experience often to ensure that your VFX art looks exactly the way you want it to display to your players. Studio offers several playtest options: - **Test** – Starts the playtest by inserting your avatar at a spawn location. - **Test Here** – Starts the playtest by inserting your avatar in front of the camera's current position. - **Run** – Starts the playtest without inserting your avatar. Instead, the playtest begins at the current camera position and you can navigate around using the camera controls. While all options are useful at different times, it's important to use a playtest option that inserts your avatar so that you can access the carnival ride and test your laser blast and explosion VFX. To playtest your art: 1. In the top-left corner of Studio, verify that your playtest is set to either **Test** or **Test Here**, then click the **Play** button. Studio enters playtest mode. 2. In the experience, walk up to the character under the **Play It** sign and press `E` to open up a menu. From that menu, click the **Join Game** button. > **Warning:** During the ride, you can't use your mouse to click parts of Studio. To use the mouse, press `1` on the keyboard. 3. When the ride starts, left click to shoot the blaster at your enemies. Watch the cart's health at the top of the screen and try to make it to the treasure room at the end. 4. When you're done, return to the top-left corner of Studio, then click the **Stop** button. Studio exits playtest mode. ## Publish to Roblox If you were to close Studio now, you would lose every edit you made to your experience. For this reason, it's important to publish your work to Roblox often to save your work and connect the experience to your account. > **Info:** It's recommended to publish to Roblox every ten minutes or after making a big change. 1. In the top-left corner of your computer, click **File** → **Publish to Roblox**. 2. In the **Publish Experience** window, 1. In the **Name** field, provide a name for your experience. 2. **OPTIONAL** In the **Description** field, provide a summary of what a player can expect from the experience. 3. In the **Devices** section, enable every device you want players to use to access your experience. 4. At the bottom-right of the window, click the **Create** button. Now that your experience is published and connected to your account, you can edit it from any computer! > **Info:****Saving after publishing** Next time you want to save your work, just go to **File** ⟩ **Publish to Roblox** or use the hotkey combo (`Alt``P`/`⌥``P`). --- title: "Work with particle emitters" url: /docs/en-us/tutorials/curriculums/artist/work-with-particle-emitters last_updated: 2026-06-29T19:34:12Z description: "Explains how to work with particle emitters to create special effects for a carnival experience." --- # Work with particle emitters **Particle emitters** create special effects by emitting 2D images, also referred to as particles, into the 3D world. By customizing how particles look and behave as they emit into the environment, you can create visual art that makes your experiences feel unique and captivating to potential players. To demonstrate, check out the **Mansion of Wonder** template uses the following three images to create VFX art in different areas of the carnival ride: Using the exact same techniques, in this chapter, let's explore how to find the carnival ride's laser blast and explosion particle emitters, then customize their properties until you transform the bland starting particles into your own VFX creations. ## Find sample emitters This lesson's template provides two basic particle emitters that players interact with during the carnival ride: - One for the **laser blast** that flies through the air when players shoot their blaster. - Another for the **explosion** when the laser blast hits a ghost or slime enemy. Both particle emitters start with simple white particles so that you can see your progress in real time as you customize their visual appearance and behavior and visual appearance throughout this chapter. To find the sample particle emitters: 1. Using the following camera controls, move your camera to get a good view of the two particle emitter stands near the front of the mansion. The first particle emitter you will customize is the laser blast particle emitter on the left with the white sparkles. | **Action** | **Control** | | --- | --- | | **Move** | `W` `A` `S` `D` | | **Rotate** | Hold the right mouse button to rotate around the selected object. | | **Zoom** | Use your mouse's scroll wheel. | | **Focus** | Press `F` to focus on a the selected object. | 2. Find the laser blast particle emitter. 1. In the **Explorer** window, go to the search bar, then type **BlastParticles**. 2. In your search results, click the particle emitter result. > **Warning:** If you don't see the **Explorer** and **Properties** windows, toggle them on from Studio's **Window** menu. ## Customize laser blast Whenever you select something in the 3D viewport or the **Explorer** window, the **Properties** window updates to display all properties that you can directly customize within Studio's interface. For example, when you selected the **BlastParticles** particle emitter in the previous section, the **Properties** window updated with all properties for that specific particle emitter. To introduce you to customizing properties, let's start with customizing the color and image of the laser blast's particles. As you get more comfortable with this process, you can experiment with more properties. ### Color A particle emitter's `Class.ParticleEmitter.Color|Color` property tints each particle's texture to either a specific hue or a gradient of color from the time it emits to the time it fades. This lesson focuses on setting particles to a specific hue, but you can learn more about how to set them to a gradient in the [Particle emitters](/docs/en-us/effects/particle-emitters.md#color) guide after you finish the tutorial. To tint the laser blast particles with a new color: 1. In the **Properties** window, find the **Color** property, then double-click the light grey box to its right. A pop-up **Colors** window displays. 2. In the **Colors** window, select a color and then click the **OK** button. For example, if you set the **Color** property to `202, 210, 20`, your particles will look like the following image. ### Texture A particle emitter's `Class.ParticleEmitter.Texture|Texture` property tells the particle emitter what picture, also referred to as **textures**, to emit into the environment. Textures can be any 2D image you want, such as a symbol or even something you've drawn. For example, the following video demonstrates how changing a particle emitter's texture creates entirely different effects. To set the laser blast particles to a new texture: 1. Pick one of the following images to use as the particle emitter's texture, then copy the assetID number beneath the image. You can return to this starter pack of textures when you are ready to customize the explosion._`rbxassetid://5860841663`__`rbxassetid://5857851812`__`rbxassetid://5857851618`__`rbxassetid://6711256324`__`rbxassetid://5833235272`__`rbxassetid://6772783963`__`rbxassetid://5833323391`__`rbxassetid://5857892330`__`rbxassetid://5857892405`__`rbxassetid://5857931724`__`rbxassetid://5857851618`__`rbxassetid://5860841737`_ 2. In the **Properties** window, find the **Texture** property, then click on it to highlight the text. > **Warning:** If you don't see the same properties as the example image, return to the **Explorer** window and reselect the **BlastParticles** particle emitter. 3. Paste your texture's assetID by pressing `Ctrl``V` or `⌘``V`, depending on your computer, then press **Enter**. Your particle emitter now emits particles that look like your texture. For example, if you use the `5833323391` texture, your particles will look like the example below. ## Customize explosion Now that you have custom VFX art for your laser blast, you can move on to customizing the explosion whenever the laser blast hits a sinister slime or ghastly ghost. To verify that you're customizing the explosion's properties instead of the laser blast, make sure you select the explosion particle emitter so that the **Properties** window updates with its specific properties: 1. In the **Explorer** window, go to the search bar, then type **ExplosionParticles**. 2. In your search results, click the particle emitter result. > **Info:** Even though this section focuses on customizing the size and speed of the explosion's particles, feel free to use your previous learnings to customize their color and texture too. ### Size A particle emitter's `Class.ParticleEmitter.Size|Size` property changes the size of each particle's texture to either a specific size or size that changes from the time it emits to the time it fades. This lesson focuses on setting particles to a specific size, but you can learn more about how to set them to a changing size in the [Particle emitters](/docs/en-us/effects/particle-emitters.md#size) guide after you finish the tutorial. To set the explosion particles to a new size: 1. In the **Properties** window, find the **Size** property. 2. For best results, set **Size** to a number between `1` and `10`. When it comes to particles, there isn't a "one size fits all"; different particle textures look better at various sizes. To demonstrate, review the following video to see how a star texture transforms as the `Class.ParticleEmitter.Size|Size` value increases. ### Speed A particle emitter's `Class.ParticleEmitter.Speed|Speed` property changes the rate at which particles emit from the particle emitter. In other words, you can change this property to ensure that particles either burst out quickly or creep out slowly. To set the explosion particles to a new speed: 1. In the **Properties** window, find the **Speed** property, then set it to a number between `10` (slow) and `100` (fast). 2. Using your learnings from the previous chapter, playtest the experience to see your particles in action. If anything doesn't look right, you can go back and make changes to your design. ## Additional properties Your laser blast and explosion VFX art are almost done! While you've customized a few particle emitter properties, there are many more you can modify to personalize your work. The following sections highlight a few, but feel free to also experiment with any that aren't listed here. ### LightEmission The `Class.ParticleEmitter.LightEmission|LightEmission` property makes particles glow. Higher values of this property can make particles glow even in environments with low lighting. _0_ _0.5_ _1_ ### RotSpeed The `Class.ParticleEmitter.RotSpeed|RotSpeed` property makes particles rotate over time. To demonstrate how this works, review the following video to see how the particles' straight line texture changes rotation as the `Class.ParticleEmitter.RotSpeed|RotSpeed` property increases. ### Lifetime The `Class.ParticleEmitter.Lifetime|Lifetime` property changes how long in seconds particles are visible once they emit from the particle emitter. Depending on your VFX art, you may want your particles to only be visible for a moment (`0.5` seconds), or for them to linger in the air (`2` seconds). ### Randomized numbers Randomizing properties can make particles feel less repetitive. You can add randomization to some particle emitters properties, such as `Class.ParticleEmitter.Lifetime|Lifetime` and `Class.ParticleEmitter.RotSpeed|RotSpeed`, by adding a minimum and maximum value range for the respective property. To randomize numbers: 1. Click the small arrow next to the property's name. The property expands. 2. Set **Min** to the minimum value you want your particles to have. 3. Set **Max** to the maximum value you want your particles to have. Using your new knowledge with particle emitters, take some time to improve your VFX art. When you are ready, continue to the next section to learn more about playtesting and how to save your work to Roblox. --- title: "AI Assistant" url: /docs/en-us/tutorials/curriculums/building/code-with-assistant last_updated: 2026-06-29T19:34:12Z description: "Explains how use AI Assistant prompts to add code to your obby." --- # AI Assistant **Assistant** is Roblox's AI helper that can help you create and code your experiences. In this chapter, let's walk through how to open Assistant in Studio, write requests that change part behavior, and playtest your results to see if you need to make any changes to the Assistant's code sample. ## Open Assistant Assistant is available in the top-right corner of this webpage, as well as directly in Studio. This second placement allows Assistant to directly reference everything that makes up your experience, then make suggestions on how you can add or edit 3D objects and scripts for your specific gameplay goals. ![Assistant button indicated on the right side of Studio's mezzanine bar.](../../../assets/tutorials/building-lesson/Mezzanine-Assistant.png) ## Create prompt Once Assistant is open in Studio, it can reference all of your platforms and add scripts to change their behavior, such as: - Turning a different color every time a player touches the part. - Moving upward, waiting for 3 seconds, then moving back to its previous position. - Reducing the player's health the longer they stay on the part. To demonstrate how this process works, this section of the tutorial shows you how to work with Assistant to make your first platform sparkle whenever a player jumps onto it. To create a prompt for this behavior: 1. **OPTIONAL** Rename your first platform so that Assistant knows which part to create a script for. 1. In the viewport, select your first platform. 2. In the **Explorer** window, right-click the active object, then select Rename from the contextual menu. 3. Type `GlitterPart`, then press **Enter**. 2. In the **Assistant** window, navigate to the **Enter request here** input, then type the following: `Add a new script that creates yellow sparkles while GlitterPart is touched`. > **Warning:** You can use a different color, but using different words might change the results. 3. Press **Enter**. Assistant starts generating a code sample, then a window displays with a suggested code sample that's similar to the following:```lua local part = script.Parent local sparkles = nil local touchCount = 0 local function createSparkles() if not sparkles then sparkles = Instance.new("Sparkles") sparkles.Color = Color3.new(1, 1, 0) -- Yellow sparkles.Parent = part end end local function removeSparkles() if sparkles then sparkles:Destroy() sparkles = nil end end part.Touched:Connect(function(hit) touchCount = touchCount + 1 createSparkles() end) part.TouchEnded:Connect(function(hit) touchCount = touchCount - 1 if touchCount <= 0 then removeSparkles() touchCount = 0 end end) ``` > **Info:** Assistant is constantly learning, so it might not always produce the same results for the exact same request. 4. Click **Accept**. As you learn more about coding with Luau, it will be easier to review Assistant's code samples to make sure they work for your use cases. There is more than one right way to make any particular request, but every word counts. For example, the following chart shows how even slightly different word choices to the original prompt can produce different results. | **Request Variationn** | **Possible Results** | | --- | --- | | `Create yellow sparkles while GlitterPart is touched.` | Assistant might show you the code, but not make a new script. It might also add the code to a random existing script. | | `Make purple sparkles when the part named GlitterPart is stepped on.` | Assistant might make sparkles before GlitterPart is stepped on, or make only a few sparkles. | | `Make GlitterPart sparkle` | Instead of adding a script, Assistant will just add a particle emitter (the object which makes sparkles) to GlitterPart. Which can be good if you want it to sparkle at all times. | The fewer details you provide, the more unpredictable the results. For example, possible results for the prompt Make parts sparkle when you touch them may include: - The resultant script not working at all. - Only a single random part working. - Parts sparkling before they are touched. If you make a request and do not get the results you want, rephrase the request and try again. > **Info:** Accidentally accept a suggestion that you didn't want? You can undo the change using one of the following methods: - For non-script changes, press `Ctrl``Z` or `⌘``Z`, depending on your computer. - If Assistant made a script, click on the script's name in the **Explorer** window, then press `Delete`. ## Playtest results Although Assistant is constantly learning and getting better, it sometimes produces results that are different than you intended. You must always check the results of its suggestions to make sure they work as intended. To playtest Assistant's results: 1. In the top-left corner of Studio, keep the default **Test** playtest option, then click the **Play** button. Studio enters playtest mode. 2. In the experience, jump onto the first platform. If the code sample works correctly, yellow sparkles appear when your character touches the part. 3. When you're done verifying the results, return to the top-left corner of Studio, then click the **Stop** button. Studio exits playtest mode. 4. **OPTIONAL** Return to the Assistant window, then press the thumbs up or thumbs down icon to let Assistant know if the code sample worked as you intended. Now that you know how to code with Assistant, try creating your own prompts to change how parts behave in your obby. When you're happy with your creation, move onto the next section of the tutorial to test and save your work to Roblox. --- title: "Get started" url: /docs/en-us/tutorials/curriculums/building/get-started last_updated: 2026-06-29T19:34:12Z description: "Explains how to get started with opening a project." --- # Get started Welcome to your first beginner building lesson in Studio! To get started, let's walk through how to open the sample template, customize Studio's layout for your tasks, and playtest the core gameplay of the experience in which players race through an obstacle course (obby) until they reach the final platform. ## Open template Templates are pre-made 3D worlds that you can use as the base for your own experiences. To introduce you to building in Studio, Roblox has created the **Classic Obby** template with everything necessary for the experience to work properly, such as the code to run the gameplay and a simple skybox that you can customize at a later time. > **Info:** For a full list of Roblox-provided templates, see [Templates](/docs/en-us/resources/templates.md). Click the following button to open the **Classic Obby** template: ## Customize workspace Roblox Studio launches with extra windows open that you don't need for this lesson. You can close these extra windows to create more space for what you're currently working on. To customize the workspace for your coding task: 1. Close each window on the **left** of the 3D view by clicking the **×** in the window's upper-right corner. If you don't see anything to close on the left, go to the next step. 2. Leave the **Explorer** and **Properties** windows open on the right side of Studio. If you have done each step correctly, Studio looks like the following image. > **Warning:** If you don't see the **Explorer** and **Properties** windows, open them from Studio's **Window** menu. ## Playtest the experience Before you start building your obby, it's important to take a moment to playtest the template so you can see how the gameplay works. When players join the experience, they begin in an obstacle course that starts easy and becomes more difficult, including platforms that move, push players high in the air, or set their character's health to zero. To playtest the experience's gameplay: 1. In the top-left corner of Studio, keep the default **Test** playtest option, then click the **Play** button. Studio enters playtest mode. > **Info:** There are a variety of additional playtest options that you will explore in a later chapter of the tutorial. 2. In the experience, use the following player controls to try to avoid the obstacles and reach the final platform at the end of the course. | **Action** | **Control** | | --- | --- | | **Move** | `W` `A` `S` `D` or arrow keys | | **Rotate** | Hold the right mouse button and look around. | | **Pan** | Hold the middle mouse button to drag your camera around. | 3. When you're done, return to the top-left corner of Studio, then click the **Stop** button. Studio exits playtest mode. Now that you are familiar with the gameplay, it's time to start building your own obby using basic parts! --- title: "Intro to building" url: /docs/en-us/tutorials/curriculums/building last_updated: 2026-06-29T19:34:12Z description: "Learn the basics of building experiences." --- # Intro to building Using the [Classic Obby](https://www.roblox.com/games/203812057/Classic-Obby) template as a guide, this tutorial acts as an introduction to building in Roblox Studio using basic parts and AI Assistant. As you follow each chapter, you will learn how to create an obstacle course (obby) where players jump across platforms and avoid obstacles until they reach the final platform. This lesson is intended for readers who are **brand new** to building with parts and working in Studio. If you are familiar with both, try another beginner lesson plan or jump straight to [building your first experience](/docs/en-us/tutorials/core.md). > **Info:** If you don't have a Roblox account, Studio active on your device, or familiarity with Studio's UI, see the [Studio lesson](/docs/en-us/tutorials/studio.md). ## Course contents **#### Chapter 1 - Get started** Learn how to [open and customize](/docs/en-us/tutorials/curriculums/get-started.md) the template's workspace so that you can focus on building your obby. **#### Chapter 2 - Work with parts** Learn how to [create and design platforms](/docs/en-us/tutorials/curriculums/work-with-parts.md) for your obby using basic part shapes. **#### Chapter 3 - Code with AI Assistant** Learn how to [modify part behavior](/docs/en-us/tutorials/curriculums/code-with-assistant.md) with code from AI Assistant. **#### Chapter 4 - Test and save** Learn how to [playtest your obby](/docs/en-us/tutorials/curriculums/test-and-save.md) and publish your work to Roblox. **#### Chapter 5 - Next steps** Learn where you can [find more tutorials](/docs/en-us/tutorials/curriculums/next-steps.md) about building experiences with parts and meshes. --- title: "Next steps" url: /docs/en-us/tutorials/curriculums/building/next-steps last_updated: 2026-06-29T19:34:12Z description: "Explains ways to continue learning about building in Studio." --- # Next steps Congratulations on building your first obby with Roblox Studio! Now that you have experience creating and customizing basic parts with unique visuals and behavior, you can share your creation with others and extend your skills by learning about Studio's additional building features. ## Share with others When you first publish an experience, it's automatically set to **private** so that you are the only one who can play it. However, creating on Roblox is always more fun when you can share what you make with others, so let's set up your experience so that they can play too. To make your experience public: 1. Using the steps from the last chapter, publish your experience so others can see your latest changes. 2. In the top-left corner of your computer, click **File** ⟩ **Experience Settings**. 3. In the settings window: 1. Go to the left bar and select the **Permissions** tab. New settings display. 2. Set **Playability** to **Public**. 4. Click the **Save** button. Now everyone on Roblox can play your experience! ## Continue learning Basic parts are an excellent start for building in the 3D space, but Studio offers many additional features to create environments in your experiences, including: - [Materials](/docs/en-us/parts/materials.md) that change the visual appearance and physical properties of parts. - [Terrain Editor](/docs/en-us/parts/terrain.md) that generates realistic terrain environments, such as mountains, grass-covered hills, or a flat desert. - [Textures and decals](/docs/en-us/parts/textures-decals.md) that decorate parts with 2D images that either scale or stretch, respectively. - [Solid modeling operations](/docs/en-us/parts/solid-modeling.md) that join parts together in unique ways to form more complex shapes known as unions or intersections. ![Two rows of optional materials.](../../../assets/tutorials/building-lesson/materials.png)_Materials_ ![Region filled with Salt material](../../../assets/studio/terrain-editor/Fill-Region.jpg)_Salt terrain_ ![The same blue texture repeated eight times on a block part.](../../../assets/tutorials/building-lesson/Texture-On-Surface.jpg)_Hexagon texture on a part_ ![Negated part subtracted from cylinder](../../../assets/modeling/solid-modeling/Negate-Result.jpg)_Solid modeling union_ --- title: "Test and save" url: /docs/en-us/tutorials/curriculums/building/test-and-save last_updated: 2026-06-29T19:34:12Z description: "Explains how to playtest and save your obby experience." --- # Test and save Now that your obstacle course is complete, it's time to playtest your experience to verify that players are able to make it to your final platform, then publish your work to Roblox so that you can save your progress. ## Playtest your obby It's important to playtest your experience often to ensure that your obby is possible to complete as expected. Studio offers several playtest options: - **Test** – Starts the playtest by inserting your avatar at a spawn location. - **Test Here** – Starts the playtest by inserting your avatar in front of the camera's current position. - **Run** – Starts the playtest without inserting your avatar. Instead, the playtest begins at the current camera position and you can navigate around using the camera controls. While all options are useful at different times, it's important to use a playtest option that inserts your avatar so that you can test the jump distance between platforms. To playtest your obby: 1. In the top-left corner of Studio, verify that your playtest is set to either **Test** or **Test Here**, then click the **Play** button. Studio enters playtest mode. 2. In the experience, try to avoid the obstacles and reach the final platform at the end of the course. 3. When you're done, return to the top-left corner of Studio, then click the **Stop** button. Studio exits playtest mode. ## Publish to Roblox If you were to close Studio now, you would lose every edit you made to your experience. For this reason, it's important to publish your work to Roblox often to save your work and connect the experience to your account. > **Info:** It's recommended to publish to Roblox every ten minutes or after making a big change. 1. In the top-left corner of your computer, click **File** ⟩ **Publish to Roblox** to open the publishing window. > **Warning:** If **Publish to Roblox** is greyed out, make sure you stopped your playtest. 2. In the **Publish Experience** window, 1. In the **Name** field, provide a name for your experience. 2. **OPTIONAL** In the **Description** field, provide a summary of what a player can expect from the experience. 3. In the **Devices** section, enable every device you want players to use to access your experience. 4. At the bottom-right of the window, click the **Create** button. Now that your experience is published and connected to your account, you can edit it from any computer! > **Info:****Saving after publishing** Next time you want to save your work, just go to **File** ⟩ **Publish to Roblox** or use the hotkey combo (`Alt``P`/`⌥``P`). --- title: "Work with parts" url: /docs/en-us/tutorials/curriculums/building/work-with-parts last_updated: 2026-06-29T19:34:12Z description: "Explains how to insert and customize parts for your obby." --- # Work with parts **Parts** are basic 3D shapes that Roblox provides out-of-the-box for building in the 3D space. In this chapter, let's explore how to insert parts of all shapes and sizes, change their position and rotation for your obby, then anchor them in the air so that they don't fall due to Studio's gravity. ## Insert parts There are five different part shapes that you can insert from the toolbar to build environments in your experiences: **block**, **sphere**, **cylinder**, **wedge**, and **corner wedge**. While you can join these part shapes together in unique ways to form more complex shapes, this lesson focuses on keeping the shapes as-is for your obby's platforms. ![A single Block part.](../../../assets/modeling/parts/Basic-Part-Block.png)_ Block_ ![A single Sphere part.](../../../assets/modeling/parts/Basic-Part-Sphere.png)_ Sphere_ ![A single Cylinder part.](../../../assets/modeling/parts/Basic-Part-Cylinder.png)_ Cylinder_ ![A single Wedge part.](../../../assets/modeling/parts/Basic-Part-Wedge.png)_ Wedge_ ![A single Corner Wedge part.](../../../assets/modeling/parts/Basic-Part-Corner-Wedge.png)_ Corner Wedge_ To insert a part for your first platform: 1. **OPTIONAL** When you insert parts, they appear at the exact center of your camera view. Select the **START** platform, then press `F` to focus the camera on this part so your additional platforms insert near the start of the obby. > **Info:** If you want more control over where the part inserts into the 3D space, use your mouse to zoom in and center your view where you want to insert your part. 2. In the **Home** or **Model** tab's toolbar, click the small arrow next to the **Part** inserter tool to reveal the dropdown.![A close-up view of the part picker.](../../../assets/tutorials/building-lesson/part-picker.png) 3. From the dropdown menu, select the part shape you want for your first platform. The part displays at the exact center of your camera view. 4. Using this process, insert additional shape types until you have as many platforms as you want for your obby. ## Customize course Now that you have platforms for your obby, it's time to customize their position, size, and rotation to create a jumping puzzle for the start of your obby. To do this, you will use three different object tools: - **Move** — Changes your object's **position**. - **Scale** — Changes your object's **size**. - **Rotate** — Changes your object's **orientation**. Each of these tools includes visual handles you can use to move, scale, or rotate your object using Studio's coordinate system for the 3D space. When you click and drag one of the handles, the object moves, scales, or rotates, respectively. ![Move draggers shown on part in 3D viewport.](../../../assets/modeling/parts/Transform-Move-SM.png)_Move tool handles_ ![Scale draggers shown on part in 3D viewport.](../../../assets/modeling/parts/Transform-Scale-SM.png)_Scale tool handles_ ![Rotate draggers shown on part in 3D viewport.](../../../assets/modeling/parts/Transform-Rotate-SM.png)_Rotate tool handles_ To customize your course for your obby's starting jumping puzzle: 1. Change the **position** of your first platform. 1. In the viewport, click on the part shape that you want for your first platform. 2. In the toolbar, select the **Move** tool. A set of handles display around the part.![Move tool highlighted.](../../../assets/education/general/Move-Tool.png) 3. Click and drag one of the handles until your part is at a good jump distance away from where players join the experience. > **Info:** Keep in mind that the jump distance between platforms is important. If you make it too difficult from the start, players may quit instead of continuing to play. 2. Change the **size** of your first platform. 1. In the toolbar, select the **Scale** tool. A set of handles display around the part.![Scale tool highlighted.](../../../assets/education/general/Scale-Tool.png) 2. Click and drag one of the handles until your part is a different size. 3. Change the **orientation** of your first platform. 1. In the toolbar, select the **Rotate** tool. A set of handles display around the part.![Rotate tool highlighted.](../../../assets/education/general/Rotate-Tool.png) 2. Click and drag one of the handles until your part has a different rotation. 4. Using the following camera controls, check your part's position and orientation from multiple angles to make sure that players would be able to jump to the first platform. | **Action** | **Control** | | --- | --- | | **Move** | `W` `A` `S` `D` | | **Rotate** | Hold the right mouse button to rotate around the selected object. | | **Zoom** | Use your mouse's scroll wheel. | | **Focus** | Press `F` to focus on a the selected object. | > **Info:** If the camera doesn't move, click anywhere in the viewport, then try again. 5. Repeat this process for the rest of your platforms. ## Anchor platforms If you were to playtest your experience now, all of your new platforms would immediately fall and disappear. This is because the Roblox engine simulates gravity as soon as the experience runs, allowing the physical force to push objects down. To make sure your platforms stay at their set position and orientation when the experience runs, you must anchor them within the 3D space using the **Anchor** tool. In addition to preventing gravity from pushing the platforms down, anchoring your platforms ensures they will stay in place when players and other objects bump into them. To anchor your platforms: 1. While holding the `Shift` key, select all of your new parts. 2. In the **Home** or **Model** tab, toggle on **Anchor**.![Anchor tool highlighted.](../../../assets/education/general/Anchor-Tool.png) Now that you have all of the platforms in place for your obby's jumping puzzle, it's time to add code with AI Assistant to modify part behavior. --- title: "Customize strings" url: /docs/en-us/tutorials/curriculums/coding/customize-strings last_updated: 2026-06-29T19:34:12Z description: "Explains how to customize the strings for your story." --- # Customize strings With the first sentence of your story done and working in Studio, it's time to customize your string with punctuation and line breaks. Once you are happy with how it looks and reads to players, you can add more sentences and finish the rest of your story. ## Add punctuation When you playtest your experience, your first sentence doesn't include any punctuation to let players know the sentence is complete. To fix this, you can add another string using the same process of concatenation to connect your words with your punctuation. To add punctuation: 1. On the same line as the **story** variable, type two periods. 2. Add another string with a punctuation mark, such as a period or exclamation point, then an extra space so your next sentence doesn't start immediately after the punctuation mark.```lua -- ============================================= local name1 = storyMaker:GetInput("What is your favorite name?") local story = ("In a tree on a hill lives the great wizard " .. name1 .. "! ") -- ============================================= -- ============================================= storyMaker:Write(story) ``` ## Add line breaks A line break moves the cursor to the next line of text. This is helpful when you want every sentence to start from the left-side of the page, and enhance your story's readability in the giant book. To add a line break: 1. On the same line as the **story** variable, type `\n`.```lua -- ============================================= local name1 = storyMaker:GetInput("What is your favorite name?") local story = ("In a tree on a hill lives the great wizard " .. name1 .. "! \n") -- ============================================= -- ============================================= storyMaker:Write(story) ``` 2. **OPTIONAL** You can add more than one line break by adding as many `\n` as you need. For example, the following code sample adds in three line breaks.```lua -- ============================================= local name1 = storyMaker:GetInput("What is your favorite name?") local story = ("In a tree on a hill lives the great wizard " .. name1 .. "! \n\n\n") -- ============================================= -- ============================================= storyMaker:Write(story) ``` ## Add more sentences Now that your first sentence includes punctation and any applicable line breaks, you can add more sentences to your story and ask players more questions for your placeholder words. To add more sentences: 1. Under your first variable, create a new variable for your second placeholder and ask a question related to what type of placeholder word it is. For example:```lua -- ============================================= local name1 = storyMaker:GetInput("What is your favorite name?") local food1 local story = ("In a tree on a hill lives the great wizard " .. name1 .. "! \n\n\n") -- ============================================= -- ============================================= storyMaker:Write(story) ``` 2. Using the same process as your first variable, set the variable and use the provided `storyMaker:GetInput()` method that displays your questions in the giant book. For example:```lua -- ============================================= local name1 = storyMaker:GetInput("What is your favorite name?") local food1 = storyMaker:GetInput("What do you like to eat?") local story = ("In a tree on a hill lives the great wizard " .. name1 .. "! \n\n\n") -- ============================================= -- ============================================= storyMaker:Write(story) ``` 3. Combine the first string of the story with the player's answer using `..`. 1. On the same line as the **story** variable, add two periods. 2. Add a space, then type the name of the variable that stores the player's second answer. 3. Add two more periods, then finish the sentence with a punctuation mark.```lua -- ============================================= local name1 = storyMaker:GetInput("What is your favorite name?") local food1 = storyMaker:GetInput("What do you like to eat?") local story = ("In a tree on a hill lives the great wizard " .. name1 .. "! \n\n\n" .. "Every morning, the wizard loves eating a giant bowl of honey roasted " .. food1 .. ". ") -- ============================================= -- ============================================= storyMaker:Write(story) ``` 4. Repeat this process for your entire story, then playtest your experience using the steps from the previous chapter to make sure your code works properly. --- title: "Get started" url: /docs/en-us/tutorials/curriculums/coding/get-started last_updated: 2026-06-29T19:34:12Z description: "Explains how to get started with opening a project." --- # Get started Welcome to your first beginner coding lesson in Studio! To get started, let's walk through how to open the sample template, customize Studio's layout for your tasks, and brainstorm the core gameplay's story that you will code throughout the tutorial. ## Open template Templates are pre-made 3D worlds that you can use as the base for your own experiences. To introduce you to coding in Studio, Roblox has created the **Story Game** template with everything necessary for the experience to work properly, except for the code for your story. > **Info:** For a full list of Roblox-provided templates, see [Templates](/docs/en-us/resources/templates.md). Click the following button to open the **Story Games** template: ## Customize workspace Roblox Studio launches with extra windows open that you don't need for this lesson. You can close these extra windows to create more space for what you're currently working on. To customize the workspace for your coding task: 1. Close each window on the **left** of the 3D view by clicking the **×** in the window's upper-right corner. If you don't see anything to close on the left, go to the next step. 2. Leave the **Explorer** and **Properties** windows open on the right side of Studio. If you have done each step correctly, Studio looks like the following image. > **Warning:** If you don't see the **Explorer** and **Properties** windows, open them from Studio's **Window** menu. ## Brainstorm your story Before you start coding, it's important to take a moment and write a story for your core gameplay where players can replace placeholder words with their own answers. The story can be silly, scary, mysterious, or about anything you want. Here are a few story ideas: - Your dream vacation - A superhero's shopping trip - What a wizard eats for breakfast The more details you include, the more options you have for placeholder words in your game. The sky is the limit, so have a good time! To brainstorm your story: 1. Using a piece of paper or a text editor on your computer, write 2-3 sentences for the opening paragraph. 2. In the first sentence, pick a single word that you want your players to replace, such as a noun, action, or character's name. For example:_In a tree on a hill lives the great wizard **Nia**._ 3. Replace that word with a placeholder for what type of word that you are going to ask the player for, such as thing, action, or name. Because you likely are going to ask the player for more than one noun or adjective, it's recommended to number the placeholder, such as thing1, action1, or name1._In a tree on a hill lives the great wizard **name1**._ 4. Repeat this process for every sentence of your story._In a tree on a hill lives the great wizard **name1**. Every morning, the wizard loves eating a giant bowl of honey roasted **food1**. Unless it's a Saturday, then the wizard likes to go to a local restaurant called **building1** with their dear friend **name2**. They always order at least one **food2**, of course!_ Now that you have a story, it's time to turn it into code! --- title: "Intro to coding" url: /docs/en-us/tutorials/curriculums/coding last_updated: 2026-06-29T19:34:12Z description: "Learn the basics of coding for experiences." --- # Intro to coding Using the [Story Games](https://www.roblox.com/games/2546610365/Roblox-Creator-Challenge) sample as a guide, this tutorial acts as an introduction to coding in Roblox Studio using the Luau programming language. As you follow each chapter, you will learn how to create a story game where players can replace placeholder words with their own answers. This lesson is intended for readers who are **brand new** to coding concepts and working in Studio. If you are familiar with both, try another beginner lesson plan or jump straight to [building your first experience](/docs/en-us/tutorials/core.md). > **Info:** If you don't have a Roblox account, Studio active on your device, or familiarity with Studio's UI, see the [Studio lesson](/docs/en-us/tutorials/studio.md). ## Course contents **#### Chapter 1 - Get started** Learn how to [open and customize](/docs/en-us/tutorials/curriculums/get-started.md) the template's workspace so that you can work with your main script. **#### Chapter 2 - Work with variables** Learn how to [use variables](/docs/en-us/tutorials/curriculums/work-with-variables.md) to ask players questions and use their answers within your story. **#### Chapter 3 - Test and save** Learn how to [playtest your story](/docs/en-us/tutorials/curriculums/test-and-save.md) and publish your work to Roblox. **#### Chapter 4 - Customize strings** Learn how to [style your code](/docs/en-us/tutorials/curriculums/customize-strings.md) with extra punctuation and line breaks. **#### Chapter 5 - Next steps** Learn where you can [find more tutorials](/docs/en-us/tutorials/curriculums/next-steps.md) about coding for experiences. --- title: "Next steps" url: /docs/en-us/tutorials/curriculums/coding/next-steps last_updated: 2026-06-29T19:34:12Z description: "Explains ways to continue learning about coding in Studio." --- # Next steps Congratulations on completing your first code sample with Roblox Studio! Now that you have experience working with variables, you can share your work with others and extend your skills by learning about Studio's additional coding features. ## Share with others When you first publish an experience, it's automatically set to **private** so that you are the only one who can play it. However, creating on Roblox is always more fun when you can share what you make with others, so let's set up your experience so that they can play too. To make your experience public: 1. Using the steps from the **Test and save** chapter, publish your experience so others can see your latest changes. 2. In the top-left corner of your computer, click **File** ⟩ **Experience Settings**. 3. In the settings window: 1. Go to the left bar and select the **Permissions** tab. New settings display. 2. Set **Playability** to **Public**. 4. Click the **Save** button. Now everyone on Roblox can play your experience! ## Continue learning Variables are an excellent start in your coding journey, but Luau includes additional fundamentals that are important to understand to add custom behavior to your experiences, including: - [Script types and locations](/docs/en-us/scripting/locations.md) - [Services](/docs/en-us/scripting/services.md) - [Properties and attributes](/docs/en-us/scripting/attributes.md) - [Events](/docs/en-us/scripting/events.md) It's recommended to continue learning with the [Coding fundamentals](/docs/en-us/tutorials/fundamentals/coding-1/coding-fundamentals.md) series, then if you want to continue learning through step-by-step lessons, check out the following use case tutorials: - [Create deadly lava](/docs/en-us/tutorials/use-case-tutorials/scripting/basic-scripting/deadly-lava.md) - [Create fading traps](/docs/en-us/tutorials/use-case-tutorials/scripting/basic-scripting/fading-trap.md) - [Create health pickups](/docs/en-us/tutorials/use-case-tutorials/scripting/intermediate-scripting/create-a-health-pickup.md) - [Save player data](/docs/en-us/tutorials/use-case-tutorials/scripting/intermediate-scripting/save-data.md) --- title: "Test and save" url: /docs/en-us/tutorials/curriculums/coding/test-and-save last_updated: 2026-06-29T19:34:12Z description: "Explains how to playtest and save your story experience." --- # Test and save Now that the first sentence of your main story is complete and ready for players to write their own answer for the playerholder word, it's time to playtest your experience to verify that the code runs properly, then publish your work to Roblox so that you can save your progress. ## Playtest your code It's important to playtest your experience often to ensure that your code works as expected. Studio offers several playtest options: - **Test** – Starts the playtest by inserting your avatar at a spawn location. - **Test Here** – Starts the playtest by inserting your avatar in front of the camera's current position. - **Run** – Starts the playtest without inserting your avatar. Instead, the playtest begins at the current camera position and you can navigate around using the camera controls. While all options are useful at different times, it's important to use a playtest option that inserts your avatar so that you can access the book and play through your story. To playtest your code: 1. In the top-left corner of Studio, verify that your playtest is set to either **Test** or **Test Here**, then click the **Play** button. Studio enters playtest mode. 2. In the experience, use the following player controls to walk up to the pedestal, then press `E` to open the book and start your story. Your first question should display if the code is working properly. | **Action** | **Control** | | --- | --- | | **Move** | `W` `A` `S` `D` or arrow keys | | **Rotate** | Hold the right mouse button and look around. | | **Pan** | Hold the middle mouse button to drag your camera around. | 3. Answer the question and click the **Submit** button. 4. When you're done, return to the top-left corner of Studio, then click the **Stop** button. Studio exits playtest mode. If your first sentence didn't show up, check out the following troubleshooting tips: **Issue**: The book doesn't display your first question. Check that the question is inside of quotation marks. **Issue**: The story is combined. Check the following: - The first part of the story is inside of quotation marks. - The name of the variable holding the player answers matches exactly. Capitalization counts! - The name of the variable holding the player's answer is not inside of quotation marks. - The two strings are separated by `..`. **Issue**: The book doesn't display the first sentence of your story. Check the `storyMaker:Write()` function to make sure the **story** variable is in between the parentesis. ## Publish to Roblox If you were to close Studio now, you would lose every edit you made to your experience. For this reason, it's important to publish your work to Roblox often to save your work and connect the experience to your account. > **Info:** It's recommended to publish to Roblox every ten minutes or after making a big change. 1. In the top-left corner of your computer, click **File** ⟩ **Publish to Roblox** to open the publishing window. 2. In the **Publish Experience** window, 1. In the **Name** field, provide a name for your experience. 2. **OPTIONAL** In the **Description** field, provide a summary of what a player can expect from the experience. 3. In the **Devices** section, enable every device you want players to use to access your experience. 4. At the bottom-right of the window, click the **Create** button. Now that your experience is published and connected to your account, you can edit it from any computer! > **Info:****Saving after publishing** Next time you want to save your work, just go to **File** ⟩ **Publish to Roblox** or use the hotkey combo (`Alt``P`/`⌥``P`). --- title: "Work with variables" url: /docs/en-us/tutorials/curriculums/coding/work-with-variables last_updated: 2026-06-29T19:34:12Z description: "Explains how to work with variables to create a story experience." --- # Work with variables In coding, **variables** are placeholder words for different types of information in your experience, such as player names, a team's score, or words for a story game. In this chapter, let's explore how to open the main script for the core gameplay of the experience and add code to ask players questions and use their answers for your story. ## Open script Roblox creators write their code inside of **scripts** that tell Studio how and when to add custom behavior to experiences, such as triggering different types of events, saving player information, or spawning enemies. Roblox scripts use the [Luau](https://luau.org) programming language, which you can learn more about later in the [Coding fundamentals](/docs/en-us/tutorials/fundamentals/coding-1.md) series. The Story Game template uses a single script named **StoryManager** to control the core gameplay of the experience. This script already contains some code that's necessary for showing the full story to the player, but you will add more to display your interactive story. To open the **StoryManager** script: 1. In the **Explorer** window, click the arrow next to **StarterGui** to see everything it contains. **GameGui** displays. 2. Click the arrow next to **GameGui** to see everything it contains. The **StoryManager** script displays at the top along with a few other things that aren't important for this lesson. 3. Double-click the **StoryManager** script. The script's code displays. ```lua -- GLOBAL VARIABLES local storyMaker = require(script:WaitForChild("StoryMaker")) -- Code controlling the game local playing = true while playing do storyMaker:Reset() -- Code story between the dashes -- ============================================= -- ============================================= -- Add the story variable between the parenthesis below storyMaker:Write() -- Play again? playing = storyMaker:PlayAgain() end ``` Notice how a large portion of the code uses the `--` symbol, which is called a **comment**. Coders use comments to leave notes, and they don't change the way the script runs. For this lesson, you are going to create your code between the dashed line comments so that you can focus on creating your interactive story in one spot. ## Ask players questions As a best practice, coders create variables with names that describe the type of information they store. For example, if you were working on a game where players are split into teams: - The `team` variable stores information about the player's **team**. - The `teamColor` variable stores information about the player's **team color**. - The `teamScore` variable stores information for the player's **team score**. Variables can store all sorts of information, such as small numbers, true or false answers, and strings. String type variables are special because they can store whole sentences, including the questions that you want to ask your players. Using your placeholder words from the previous chapter, you can create string type variables with the same placeholder names that describe what type of word that you are going to ask the player for as they play your story game. To create variables for your player questions: 1. Click below the dashed line comments and create a local variable for the first word you want players to replace with their own answer. For example:```lua -- ============================================= local name1 -- ============================================= ``` 2. Set the variable using the `=` symbol to the provided `storyMaker:GetInput()` method that displays your questions in the giant book. You must type this method exactly as it is with its specific capitalization in order for the code to work. For example:```lua -- ============================================= local name1 = storyMaker:GetInput() -- ============================================= ``` 3. Create a string in between the parenthesis with the question you want to ask your players. Remember, strings are always in quotation marks!```lua -- ============================================= local name1 = storyMaker:GetInput("What is your favorite name?") -- ============================================= ``` ## Use their answers Now that you have a question that players can answer, it's time to code your story and add behavior that replaces your placeholder word with their answer. To create variables for your story and to use player answers: 1. Under your question, create a new variable named story. Remember, variables are lowercase!```lua -- ============================================= local name1 = storyMaker:GetInput("What is your favorite name?") local story -- ============================================= ``` 2. Set your string type variable to the first sentence of your story before your first placeholder, leaving a space after the last word and before the quotation mark. If your placeholder word happens to be in the middle of a sentence, you can add the rest of the sentence in a later chapter of the tutorial.```lua -- ============================================= local name1 = storyMaker:GetInput("What is your favorite name?") local story = ("In a tree on a hill lives the great wizard ") -- ============================================= ``` 3. Combine the first string of the story with the player's answer using `..`. Combining things together is called **concatenation**. 1. On the same line as the **story** variable, add two periods. 2. Add a space, then type the name of the variable that stores the player's first answer.```lua -- ============================================= local name1 = storyMaker:GetInput("What is your favorite name?") local story = ("In a tree on a hill lives the great wizard " .. name1) -- ============================================= ``` 4. Show this first part of the story to players by calling the story variable from the provided `storyMaker:Write()` method that writes your story in the giant book.```lua -- ============================================= local name1 = storyMaker:GetInput("What is your favorite name?") local story = ("In a tree on a hill lives the great wizard " .. name1) -- ============================================= -- ============================================= storyMaker:Write(story) ``` Now's a good point to stop momentarily, playtest the experience in the next chapter, and make sure the story is working! --- title: "Apply polished assets" url: /docs/en-us/tutorials/curriculums/core/building/apply-polished-assets last_updated: 2026-06-29T19:34:12Z description: "Explains how to finish your scene by replacing simple parts with complex, imported models." --- # Apply polished assets **Applying polished assets** is the last step in building your final environment, in which you swap out your greybox placeholder assets for high-quality polished assets in order to meet your experience's aesthetic goals and design requirements. This exciting section of the curriculum is where you see your world come to life into a cohesive environment that is complete and ready for publication. Using the [Island Jump - Final](https://www.roblox.com/games/14238807008/Island-Jump-Completed-Sample) file as a reference, this section of the tutorial teaches you how to use the Creator Store to find and add a sample asset library into your project, continue your organization structure to sort your new assets into meaningful categories, and apply the asset library to the 3D space. > **Info:** This chapter involves a lot of precise positioning and property configuration. Each section includes a **Build with Assistant** option that places assets using the exact `CFrame.Position` and `Scale` values from the tutorial, as well as an **Apply your own** option if you'd prefer to create your own layout. > > Assistant is particularly effective at this kind of repetitive, deterministic setup, making it a useful way to speed up the building process. ## Get asset library The **Creator Store** is a tab of the **Toolbox** that you can use to find all assets that are made by Roblox and the Roblox community for use within your projects, including model, image, mesh, audio, plugin, video, and font assets. You can use the Creator Store to add an individual asset or asset library directly into an open experience. Asset libraries are collections of assets you can place into a central location of your experience for easy access and reuse. The asset library you will use for your project from the Creator Store includes six 3D assets, two custom `Class.MaterialVariant` materials, and the final particle effects from [Create basic visual effects](/docs/en-us/tutorials/curriculums/core/building/create-basic-visual-effects.md). The six meshes are as follows: ![A mesh that's a grassy, circular platform with concrete surrounding its edges.](../../../../assets/tutorials/core-building-and-scripting/Platform-A-Model.jpg)_PlatformA_ ![A mesh that's a grassy, circular platform with rock underneath.](../../../../assets/tutorials/core-building-and-scripting/Platform-B-Model.jpg)_PlatformB_ ![A mesh that's a shiny golden coin with a Roblox logo in the middle.](../../../../assets/tutorials/core-building-and-scripting/Coin-Mesh.jpg)_Coin_ ![A mesh that's a large rock sea stack.](../../../../assets/tutorials/core-building-and-scripting/Sea-Stack-Mesh.jpg)_SeaStackMesh_ ![A mesh that's a large rock sea stack with a hollow tunnel in the middle.](../../../../assets/tutorials/core-building-and-scripting/Sea-Stack-Cave-Mesh.jpg)_SeaStackCaveMesh_ ![A mesh that's a large snowy mountain.](../../../../assets/tutorials/core-building-and-scripting/Mountain-Mesh.jpg)_MountainMesh_ These assets are either single `Class.MeshPart` objects or `Class.Model` objects containing multiple meshes. They use custom materials and physically-based rendering (PBR) textures for realistic shading. For more information, see [Materials - Custom Materials](/docs/en-us/parts/materials.md#custom-materials) and [PBR Textures](/docs/en-us/art/modeling/surface-appearance.md). To get the asset library from your inventory into your experience: 1. Open the asset library's [details page](https://create.roblox.com/store/asset/14238769242) on the Creator Store and click **Get Model** in the top-right corner. The library is now in your inventory, and you can reuse these assets in any project on the platform. 2. Back in Studio, click **Toolbox** in the **Home** toolbar tab, switch to the **Inventory** tab, and click the **Core Building and Scripting** tile. The library appears in the viewport.![All meshes from the asset library hover above the water. Some of the meshes are missing their textures so they appear gray.](../../../../assets/tutorials/core-building-and-scripting/Asset-Library-Complete.jpg) 3. A few meshes might look gray instead of textured becasue their materials reference `Class.MaterialVariant` objects that aren't installed yet. To fix this, drag **Moss_Lumpy_A** and **Moss_Strata_Noisy_A** from the asset library into the **MaterialService** container in the Explorer. The textures snap into place.![All meshes from the asset library hover above the water, now complete with all of their textures.](../../../../assets/tutorials/core-building-and-scripting/Asset-Library-Complete-With-Materials.jpg) ## Continue organization structure Before placing assets, extend the folder structure you started in [Greybox a playable area](/docs/en-us/tutorials/curriculums/core/building/greybox-a-playable-area.md): 1. In the **Explorer**, add two new folders inside **World** and name them **Platforms** and **Mountains**. 2. Inside **Platforms**, add a `Class.Model` for each sea stack level and name them to match your greybox levels. For example, the sample experience has 18 individual model containers for every platform in the environment. ![Studio's Explorer window with all of the level model objects underneat the Platforms folder.](../../../../assets/tutorials/core-building-and-scripting/ApplyAssetLibrary/Organization-Structure.jpg) ## Apply asset library Now place the polished assets over your greybox. Each section gives you three paths: **Build with Assistant** (which lets Assistant place every asset at the sample's exact coordinates), **Apply your own** (where you place assets to fit your own custom layout), and **Recreate the sample** (where you place them by hand at the exact coordinates of the sample). The screenshots make the placeholder assets semi-transparent so you can see your progress between steps. #### Graybox Version ![A version of the sample Island Jump game's placeholder greybox geometry.](../../../../assets/tutorials/core-building-and-scripting/Pre-Polished-Assets.jpg) #### Polished Assets ![A version of the sample Island Jump game with polished geometry.](../../../../assets/tutorials/core-building-and-scripting/Final-Polished-Assets.jpg) ### Platforms The library includes two platform `Class.Model` objects (each made of two `Class.MeshPart` children): - **PlatformA** — Includes a metallic circular base, used for the first and final platforms. - **PlatformB** — Includes basic terrain cap, used for the in-between platforms. ![Platform A and Platform B are side-by-side, and highlighted with their platform type.](../../../../assets/tutorials/core-building-and-scripting/Platform-Types.jpg) #### Build with Assistant ```text For each row below, copy the matching mesh from the asset library named "Core Building and Scripting" into Workspace > World > Platforms > and set its Origin.Position and Scale. PlatformA assets: - Level_1: Origin.Position -30, 3, 9; Scale 2.2 - Level_2: Origin.Position -24, 11, 24; Scale 1.4 - Level_3a: Origin.Position 34, 31, 9.5; Scale 0.7 - Level_3b: Origin.Position 79, 31, 2; Scale 0.6 - Level_7: Origin.Position 402, 312, 79; Scale 1.1 PlatformB assets: - Level_3c: Origin.Position 89, 31, 48; Scale 0.8 - Level_4a: Origin.Position 104, 65.5, 46.5; Scale 0.6 - Level_4b: Origin.Position 127, 66, 67.5; Scale 0.6 - Level_4b: Origin.Position 133.5, 164.5, 70.5; Scale 0.7 - Level_4c: Origin.Position 152, 65.5, 91; Scale 1 - Level_4d: Origin.Position 200, 66, 107; Scale 0.5 - Level_4e: Origin.Position 238.5, 66, 81; Scale 0.8 - Level_5a: Origin.Position 262, 120, 57.5; Scale 0.7 - Level_5b: Origin.Position 245, 122, 15; Scale 0.4 - Level_5c: Origin.Position 271, 122.5, -5.5; Scale 0.4 - Level_5d: Origin.Position 318, 121, -22; Scale 0.9 - Level_6a: Origin.Position 363, 201, -52.5; Scale 1.2 - Level_6b: Origin.Position 381, 201, 11; Scale 0.4 - Level_6c: Origin.Position 389, 201, 47; Scale 0.6 ``` ![A view of the sample laser tag game with all of the platforms visible with the polished assets.](../../../../assets/tutorials/core-building-and-scripting/ApplyAssetLibrary/Platforms-5.jpg) #### Apply your own platforms To build out your own arrangement of platforms, grab one polished asset at a time from the asset library and drop it onto each sea stack. The library includes two variants (**PlatformA** and **PlatformB**) so you can vary the look as you go. 1. In the **Explorer**, navigate to the asset library, right-click **PlatformA**, and choose **Copy**. 2. Paste **PlatformA** into the **Level_1** model inside your **Platforms** folder. 3. Use the **Move** and **Scale** tools in the toolbar to size and position the platform on top of your first sea stack.![A view of the sample laser tag game with only the first platform visible with the polished assets. Other greybox sea stacks are translucent in the distance.](../../../../assets/tutorials/core-building-and-scripting/First-Platform.jpg) 4. Repeat for the rest of your sea stacks. Use **PlatformA** for the first and final stacks (where players start and end), and use **PlatformB** for the platforms in between to add visual variety.![A view of the sample laser tag game with all of the platforms visible with the polished assets. Other greybox sea stacks are translucent in the distance.](../../../../assets/tutorials/core-building-and-scripting/ApplyAssetLibrary/Platforms-5.jpg) #### Recreate the sample 1. In the **Explorer** window, navigate to the asset library and copy **PlatformA**. 1. Right-click **PlatformA**. A contextual menu displays. 2. From the contextual menu, select **Copy**. 2. In the **Platforms** folder, paste **PlatformA** into the **Level_1** model. 3. In the **Properties** window, 1. Set **Origin.Position** to `-30, 3, 9`. 2. Set **Scale** to `2.2`.![A view of the sample laser tag game with only the first platform visible with the polished assets. Other greybox sea stacks are translucent in the distance.](../../../../assets/tutorials/core-building-and-scripting/First-Platform.jpg) 4. Repeat this process, adding and configuring the following **PlatformA** assets into their respective sea stack platform models: | Parent Model | Origin.Position | Scale | | --- | --- | --- | | Level_2 | `-24, 11, 24` | `1.4` | | Level_3a | `34, 31, 9.5` | `0.7` | | Level_3b | `79, 31, 2` | `0.6` | | Level_7 | `402, 312, 79` | `1.1` |![A view of the sample laser tag game with all Platform A platforms visible with the polished assets. Other greybox sea stacks are translucent in the distance.](../../../../assets/tutorials/core-building-and-scripting/ApplyAssetLibrary/Platforms-4.jpg) 5. Add and configure the following **PlatformB** assets into their respective sea stack platform models: | Parent Model | Origin.Position | Scale | | --- | --- | --- | | Level_3c | `89, 31, 48` | `0.8` | | Level_4a | `104, 65.5, 46.5` | `0.6` | | Level_4b | `127, 66, 67.5` | `0.6` | | Level_4b | `133.5, 164.5, 70.5` | `0.7` | | Level_4c | `152, 65.5, 91` | `1` | | Level_4d | `200, 66, 107` | `0.5` | | Level_4e | `238.5, 66, 81` | `0.8` | | Level_5a | `262, 120, 57.5` | `0.7` | | Level_5b | `245, 122, 15` | `0.4` | | Level_5c | `271, 122.5, -5.5` | `0.4` | | Level_5d | `318, 121, -22` | `0.9` | | Level_6a | `363, 201, -52.5` | `1.2` | | Level_6b | `381, 201, 11` | `0.4` | | Level_6c | `389, 201, 47` | `0.6` |![A view of the sample laser tag game with all of the platforms visible with the polished assets. Other greybox sea stacks are translucent in the distance.](../../../../assets/tutorials/core-building-and-scripting/ApplyAssetLibrary/Platforms-5.jpg) ### Sea stacks The sample asset library includes two types of sea stack rock formations you can stack in creative ways to form the pillars of your sea stack platforms: - **SeaStackMesh** – Includes a solid rock formation. - **SeaStackCaveMesh** – Includes a tunnel and solid top. Both platform types are `Class.MeshPart` objects. ![A comparison of a single sea stack next to multiple sea stacks that are stacked on top of each other.](../../../../assets/tutorials/core-building-and-scripting/Sea-Stacks.jpg) ![A demonstration of a SeaStackCaveMesh stacked on top of a PlatformB on top of a SeaStackMesh.](../../../../assets/tutorials/core-building-and-scripting/Completed-Sea-Stack.jpg) #### Build with Assistant ```text For each row below, copy the matching mesh from the asset library named "Core Building and Scripting" into Workspace > World > Platforms > and set its Size, CFrame.Position, and CFrame.Orientation. All meshes are SeaStackMesh unless otherwise noted. Level_3a: - Size 31, 31, 34; CFrame.Position 31, 14, 15.5; CFrame.Orientation 0, 72, 0 - Size 32, 37, 34; CFrame.Position 39, 11, 6; CFrame.Orientation 0, 162, 0 - Size 32, 36, 34; CFrame.Position 31, 13, 7.5; CFrame.Orientation 0, 68, 0 Level_3b: - Size 35, 47, 36; CFrame.Position 80, 7, 4; CFrame.Orientation 0, -18, 0 Level_3c: - Size 52, 69, 53; CFrame.Position 90, -4, 48; CFrame.Orientation 0, -18, 0 Level_4a: - Size 43, 49, 43; CFrame.Position 103, 5, 48; CFrame.Orientation 0, -119, 0 - Size 37, 49, 38; CFrame.Position 104, 41, 47; CFrame.Orientation 0, -18, 0 Level_4b: - SeaStackCaveMesh; Size 54, 67, 53; CFrame.Position 131, 88, 71; CFrame.Orientation 0, -111, 0 - Size 66, 85, 65; CFrame.Position 133, 23, 71; CFrame.Orientation 0, 87, 0 - Size 46, 55, 47; CFrame.Position 133, 136, 71; CFrame.Orientation 0, -79, 0 Level_4c: - Size 63, 81, 62; CFrame.Position 151, 25, 91; CFrame.Orientation 0, -50, 0 Level_4d: - Size 35, 39, 35; CFrame.Position 198, -4, 108; CFrame.Orientation 0, -44, 0 - Size 33, 36, 32; CFrame.Position 199, 21, 108; CFrame.Orientation 0, -119, 0 - Size 27, 36, 28; CFrame.Position 199, 48, 108; CFrame.Orientation 0, -18, 0 Level_4e: - Size 57, 65, 57; CFrame.Position 239, -14, 82; CFrame.Orientation 0, -63, 0 - Size 48, 65, 50; CFrame.Position 239, 34, 81; CFrame.Orientation 0, 39, 0 Level_5a: - Size 56, 62, 54; CFrame.Position 264, 11, 55; CFrame.Orientation 0, 150, 0 - Size 50, 57, 50; CFrame.Position 263, 50, 57; CFrame.Orientation 0, 75, 0 - Size 43, 57, 44; CFrame.Position 262, 92, 57; CFrame.Orientation 0, 176, 0 Level_5b: - Size 47, 36, 40; CFrame.Position 245, -16, 15; CFrame.Orientation 0, 0, 0 - Size 38, 38, 41; CFrame.Position 245, 11, 14; CFrame.Orientation 0, 100, 0 - Size 36, 38, 34; CFrame.Position 243, 34, 15; CFrame.Orientation 0, -164, 0 - Size 31, 34, 30; CFrame.Position 244, 62, 16; CFrame.Orientation 0, -45, 0 - Size 28, 32, 28; CFrame.Position 244, 83, 15; CFrame.Orientation 0, -120, 0 - Size 24, 32, 24; CFrame.Position 244, 106, 15; CFrame.Orientation 0, -18, 0 Level_5c: - Size 45, 34, 38; CFrame.Position 270, -8, -6; CFrame.Orientation 0, 180, 0 - Size 36, 35, 38; CFrame.Position 270, 17, -5; CFrame.Orientation 0, -80, 0 - Size 34, 35, 32; CFrame.Position 272, 40, -5; CFrame.Orientation 0, 16, 0 - Size 29, 32, 29; CFrame.Position 271, 65, -7; CFrame.Orientation 0, 136, 0 - Size 26, 30, 26; CFrame.Position 271, 86, -6; CFrame.Orientation 0, 61, 0 - Size 22, 30, 23; CFrame.Position 270, 108, -6; CFrame.Orientation 0, 162, 0 Level_5d: - Size 65, 71, 63; CFrame.Position 315, -5, -27; CFrame.Orientation 0, -161, 0 - Size 58, 66, 58; CFrame.Position 315, 39, -25; CFrame.Orientation 0, 124, 0 - Size 50, 66, 51; CFrame.Position 315, 89, -24; CFrame.Orientation 0, -134, 0 Level_6a: - Size 94, 104, 92; CFrame.Position 358, 32, -56; CFrame.Orientation 0, -123, 0 - Size 85, 96, 84; CFrame.Position 360, 97, -54; CFrame.Orientation 0, 162, 0 - Size 72, 71, 74; CFrame.Position 361, 165, -52; CFrame.Orientation 0, -79, 0 Level_6b: - Size 44, 65, 47; CFrame.Position 380, 17, 9; CFrame.Orientation 0, -143, 0 - Size 46, 44, 39; CFrame.Position 380, 61, 11; CFrame.Orientation 0, -78, 0 - Size 37, 37, 40; CFrame.Position 381, 92, 11; CFrame.Orientation 0, 22, 0 - Size 35, 37, 33; CFrame.Position 381, 115, 10; CFrame.Orientation 0, 118, 0 - Size 30, 33, 30; CFrame.Position 379, 141, 10; CFrame.Orientation 0, -123, 0 - Size 27, 31, 27; CFrame.Position 380, 162, 10; CFrame.Orientation 0, 162, 0 - Size 23, 31, 24; CFrame.Position 380, 185, 11; CFrame.Orientation 0, -96, 0 Level_6c: - Size 68, 52, 57; CFrame.Position 387, 3, 48; CFrame.Orientation 0, -4, 0 - Size 54, 54, 58; CFrame.Position 388, 41, 47; CFrame.Orientation 0, 96, 0 - Size 52, 54, 48; CFrame.Position 385, 75, 47; CFrame.Orientation 0, -168, 0 - Size 44, 49, 43; CFrame.Position 385, 114, 49; CFrame.Orientation 0, -49, 0 - Size 40, 45, 40; CFrame.Position 386, 145, 48; CFrame.Orientation 0, -124, 0 - Size 34, 45, 35; CFrame.Position 387, 178, 48; CFrame.Orientation 0, -22, 0 Level_7a: - Size 96, 98, 102; CFrame.Position 406, 19, 82; CFrame.Orientation 0, -41, 0 - Size 92, 98, 84; CFrame.Position 407, 81, 82; CFrame.Orientation 0, -19, 0 - Size 81, 90, 79; CFrame.Position 407, 152, 80; CFrame.Orientation 0, 90, 0 - Size 73, 83, 73; CFrame.Position 404, 208, 80; CFrame.Orientation 0, 15, 0 - Size 62, 83, 64; CFrame.Position 403, 270, 79; CFrame.Orientation 0, 116, 0 After placing all sea stacks, delete the placeholder greybox cylinders inside each level model. ``` ![A view of the sample laser tag game with all of the platforms and sea stacks visible with the polished assets.](../../../../assets/tutorials/core-building-and-scripting/ApplyAssetLibrary/SeaStacks-18.jpg) #### Apply your own sea stacks Sea stacks are the towers underneath your platforms. The asset library includes a standard **SeaStackMesh**, plus a **SeaStackCaveMesh** with a tunnel cut through it. Use the cave variant wherever you greyboxed a hollow tunnel. 1. In the **Explorer**, navigate to the asset library, right-click **SeaStackMesh**, and choose **Copy**. 2. Paste it into one of your level models inside the **Platforms** folder. 3. Use the **Move**, **Scale**, and **Rotate** tools to match the height and position of your placeholder cylinder. 4. If your sea stack is taller than a single mesh covers, paste additional copies and stack them on top of each other to reach the full height. 5. For any level that includes a tunnel, copy and paste **SeaStackCaveMesh** instead and align it with the opening you carved out earlier.![A view of the sample laser tag game with all of the platforms visible with the polished assets, as well as the first sea stack. Other greybox sea stacks are translucent in the distance.](../../../../assets/tutorials/core-building-and-scripting/ApplyAssetLibrary/SeaStacks-3.jpg) 6. Repeat for every level until each placeholder cylinder is covered by a polished mesh. 7. Once every sea stack is in place, delete the placeholder greybox cylinders inside each level model.![A view of the sample laser tag game with all of the platforms and sea stacks visible with the polished assets.](../../../../assets/tutorials/core-building-and-scripting/ApplyAssetLibrary/SeaStacks-18.jpg) #### Recreate the sample 1. In the **Explorer** window, navigate to the asset library and copy **SeaStackMesh**. 2. In the **Platforms** folder, paste **SeaStackMesh** into the **Level_3a** model three times. 3. In the **Properties** window, configure the meshes with the following values: | Size | CFrame.Position | CFrame.Orientation | | --- | --- | --- | | `31, 31, 34` | `31, 14, 15.5` | `0, 72, 0` | | `32, 37, 34` | `39, 11, 6` | `0, 162, 0` | | `32, 36, 34` | `31, 13, 7.5` | `0, 68, 0` |![A view of the sample laser tag game with all of the platforms and level 1 sea stacks visible with the polished assets. Other greybox sea stacks are translucent in the distance.](../../../../assets/tutorials/core-building-and-scripting/ApplyAssetLibrary/SeaStacks-3.jpg) 4. Repeat this process, adding and configuring the following **SeaStackMesh** asset into the **Level_3b** model: | Size | CFrame.Position | CFrame.Orientation | | --- | --- | --- | | `35, 47, 36` | `80, 7, 4` | `0, -18, 0` |![A view of the sample laser tag game with all of the platforms and level 1-2 sea stacks visible with the polished assets. Other greybox sea stacks are translucent in the distance.](../../../../assets/tutorials/core-building-and-scripting/ApplyAssetLibrary/SeaStacks-4.jpg) 5. Add and configure the following **SeaStackMesh** asset into the **Level_3c** model: | Size | CFrame.Position | CFrame.Orientation | | --- | --- | --- | | `52, 69, 53` | `90, -4, 48` | `0, -18, 0` |![A view of the sample laser tag game with all of the platforms and level 1-3 sea stacks visible with the polished assets. Other greybox sea stacks are translucent in the distance.](../../../../assets/tutorials/core-building-and-scripting/ApplyAssetLibrary/SeaStacks-5.jpg) 6. Add and configure the following **SeaStackMesh** assets into the **Level_4a** model: | Size | CFrame.Position | CFrame.Orientation | | --- | --- | --- | | `43, 49, 43` | `103, 5, 48` | `0, -119, 0` | | `37, 49, 38` | `104, 41, 47` | `0, -18, 0` |![A view of the sample laser tag game with all of the platforms and level 1-4a sea stacks visible with the polished assets. Other greybox sea stacks are translucent in the distance.](../../../../assets/tutorials/core-building-and-scripting/ApplyAssetLibrary/SeaStacks-6.jpg) 7. Add and configure the following assets into the **Level_4b** model: | Sea stack type | Size | CFrame.Position | CFrame.Orientation | | --- | --- | --- | --- | | SeaStackCaveMesh | `54, 67, 53` | `131, 88, 71` | `0, -111, 0` | | SeaStackMesh | `66, 85, 65` | `133, 23, 71` | `0, 87, 0` | | SeaStackMesh | `46, 55, 47` | `133, 136, 71` | `0, -79, 0` |![A view of the sample laser tag game with all of the platforms and level 1-4a and 4b sea stacks visible with the polished assets. Other greybox sea stacks are translucent in the distance.](../../../../assets/tutorials/core-building-and-scripting/ApplyAssetLibrary/SeaStacks-7.jpg) 8. Add and configure the following **SeaStackMesh** asset into the **Level_4c** model: | Size | CFrame.Position | CFrame.Orientation | | --- | --- | --- | | `63, 81, 62` | `151, 25, 91` | `0, -50, 0` |![A side view of the sample laser tag game with level 4c sea stacks visible with the polished assets.](../../../../assets/tutorials/core-building-and-scripting/ApplyAssetLibrary/SeaStacks-8.jpg) 9. Add and configure the following **SeaStackMesh** assets into the **Level_4d** model: | Size | CFrame.Position | CFrame.Orientation | | --- | --- | --- | | `35, 39, 35` | `198, -4, 108` | `0, -44, 0` | | `33, 36, 32` | `199, 21, 108` | `0, -119, 0` | | `27, 36, 28` | `199, 48, 108` | `0, -18, 0` |![A side view of the sample laser tag game with level 4c and 4d sea stacks visible with the polished assets.](../../../../assets/tutorials/core-building-and-scripting/ApplyAssetLibrary/SeaStacks-9.jpg) 10. Add and configure the following **SeaStackMesh** assets into the **Level_4e** model: | Size | CFrame.Position | CFrame.Orientation | | --- | --- | --- | | `57, 65, 57` | `239, -14, 82` | `0, -63, 0` | | `48, 65, 50` | `239, 34, 81` | `0, 39, 0` |![A side view of the sample laser tag game with level 4c-4e sea stacks visible with the polished assets.](../../../../assets/tutorials/core-building-and-scripting/ApplyAssetLibrary/SeaStacks-10.jpg) 11. Add and configure the following **SeaStackMesh** assets into the **Level_5a** model: | Size | CFrame.Position | CFrame.Orientation | | --- | --- | --- | | `56, 62, 54` | `264, 11, 55` | `0, 150, 0` | | `50, 57, 50` | `263, 50, 57` | `0, 75, 0` | | `43, 57, 44` | `262, 92, 57` | `0, 176, 0` |![A side view of the sample laser tag game with level 4c-5a sea stacks visible with the polished assets.](../../../../assets/tutorials/core-building-and-scripting/ApplyAssetLibrary/SeaStacks-11.jpg) 12. Add and configure the following **SeaStackMesh** assets into the **Level_5b** model: | Size | CFrame.Position | CFrame.Orientation | | --- | --- | --- | | `47, 36, 40` | `245, -16, 15` | `0, 0, 0` | | `38, 38, 41` | `245, 11, 14` | `0, 100, 0` | | `36, 38, 34` | `243, 34, 15` | `0, -164, 0` | | `31, 34, 30` | `244, 62, 16` | `0, -45, 0` | | `28, 32, 28` | `244, 83, 15` | `0, -120, 0` | | `24, 32, 24` | `244, 106, 15` | `0, -18, 0` |![A view of the sample laser tag game with all of the platforms and level 1-5b sea stacks visible with the polished assets. Other greybox sea stacks are translucent in the distance.](../../../../assets/tutorials/core-building-and-scripting/ApplyAssetLibrary/SeaStacks-12.png) 13. Add and configure the following **SeaStackMesh** assets into the **Level_5c** model: | Size | CFrame.Position | CFrame.Orientation | | --- | --- | --- | | `45, 34, 38` | `270, -8, -6` | `0, 180, 0` | | `36, 35, 38` | `270, 17, -5` | `0, -80, 0` | | `34, 35, 32` | `272, 40, -5` | `0, 16, 0` | | `29, 32, 29` | `271, 65, -7` | `0, 136, 0` | | `26, 30, 26` | `271, 86, -6` | `0, 61, 0` | | `22, 30, 23` | `270, 108, -6` | `0, 162, 0` |![A view of the sample laser tag game with all of the platforms and level 1-5c sea stacks visible with the polished assets. Other greybox sea stacks are translucent in the distance.](../../../../assets/tutorials/core-building-and-scripting/ApplyAssetLibrary/SeaStacks-13.jpg) 14. Add and configure the following **SeaStackMesh** assets into the **Level_5d** model: | Size | CFrame.Position | CFrame.Orientation | | --- | --- | --- | | `65, 71, 63` | `315, -5, -27` | `0, -161, 0` | | `58, 66, 58` | `315, 39, -25` | `0, 124, 0` | | `50, 66, 51` | `315, 89, -24` | `0, -134, 0` |![A view of the sample laser tag game with all of the platforms and level 1-5d sea stacks visible with the polished assets. Other greybox sea stacks are translucent in the distance.](../../../../assets/tutorials/core-building-and-scripting/ApplyAssetLibrary/SeaStacks-14.jpg) 15. Add and configure the following **SeaStackMesh** assets into the **Level_6a** model: | Size | CFrame.Position | CFrame.Orientation | | --- | --- | --- | | `94, 104, 92` | `358, 32, -56` | `0, -123, 0` | | `85, 96, 84` | `360, 97, -54` | `0, 162, 0` | | `72, 71, 74` | `361, 165, -52` | `0, -79, 0` |![A view of the sample laser tag game with all of the platforms and level 1-6a sea stacks visible with the polished assets. Other greybox sea stacks are translucent in the distance.](../../../../assets/tutorials/core-building-and-scripting/ApplyAssetLibrary/SeaStacks-15.jpg) 16. Add and configure the following **SeaStackMesh** assets into the **Level_6b** model: | Size | CFrame.Position | CFrame.Orientation | | --- | --- | --- | | `44, 65, 47` | `380, 17, 9` | `0, -143, 0` | | `46, 44, 39` | `380, 61, 11` | `0, -78, 0` | | `37, 37, 40` | `381, 92, 11` | `0, 22, 0` | | `35, 37, 33` | `381, 115, 10` | `0, 118, 0` | | `30, 33, 30` | `379, 141, 10` | `0, -123, 0` | | `27, 31, 27` | `380, 162, 10` | `0, 162, 0` | | `23, 31, 24` | `380, 185, 11` | `0, -96, 0` |![A view of the sample laser tag game with all of the platforms and level 1-6b sea stacks visible with the polished assets. Other greybox sea stacks are translucent in the distance.](../../../../assets/tutorials/core-building-and-scripting/ApplyAssetLibrary/SeaStacks-16.jpg) 17. Add and configure the following **SeaStackMesh** assets into the **Level_6c** model: | Size | CFrame.Position | CFrame.Orientation | | --- | --- | --- | | `68, 52, 57` | `387, 3, 48` | `0, -4, 0` | | `54, 54, 58` | `388, 41, 47` | `0, 96, 0` | | `52, 54, 48` | `385, 75, 47` | `0, -168, 0` | | `44, 49, 43` | `385, 114, 49` | `0, -49, 0` | | `40, 45, 40` | `386, 145, 48` | `0, -124, 0` | | `34, 45, 35` | `387, 178, 48` | `0, -22, 0` |![A view of the sample laser tag game with all of the platforms and level 1-6c sea stacks visible with the polished assets. Other greybox sea stacks are translucent in the distance.](../../../../assets/tutorials/core-building-and-scripting/ApplyAssetLibrary/SeaStacks-17.jpg) 18. Add and configure the following **SeaStackMesh** assets into the **Level_7a** model: | Size | CFrame.Position | CFrame.Orientation | | --- | --- | --- | | `96, 98, 102` | `406, 19, 82` | `0, -41, 0` | | `92, 98, 84` | `407, 81, 82` | `0, -19, 0` | | `81, 90, 79` | `407, 152, 80` | `0, 90, 0` | | `73, 83, 73` | `404, 208, 80` | `0, 15, 0` | | `62, 83, 64` | `403, 270, 79` | `0, 116, 0` |![A view of the sample laser tag game with all of the platforms and level 1-7a sea stacks visible with the polished assets. Other greybox sea stacks are translucent in the distance.](../../../../assets/tutorials/core-building-and-scripting/ApplyAssetLibrary/SeaStacks-18.jpg) 19. Delete your placeholder greybox sea stack platforms. ### Coins The sample asset library includes a single **Coin** asset, which is a `Class.MeshPart` with a child `Class.SurfaceAppearance` that gives it a shiny metallic finish. Place one wherever you added a placeholder coin, then adjust its position and orientation as needed. ![A close up view of a shiny gold coin with a Roblox icon in the middle. The coin floats over a grassy path of island.](../../../../assets/tutorials/core-building-and-scripting/Final-Coin.jpg) #### Build with Assistant ```text For each row below, copy the Coin mesh from the asset library named "Core Building and Scripting" into Workspace > World > Coins and set its CFrame.Position and CFrame.Orientation: - CFrame.Position -121, 3, 6; CFrame.Orientation 0, -105, 0 - CFrame.Position -107, 3, 10; CFrame.Orientation 0, -105, 0 - CFrame.Position -104, 3, 35; CFrame.Orientation 0, 120, 0 - CFrame.Position -86, 3, 61; CFrame.Orientation 0, 30, 0 - CFrame.Position -101, 3, -12; CFrame.Orientation 0, -15, 0 - CFrame.Position -22, 6, -39; CFrame.Orientation 0, 120, 0 - CFrame.Position -48, 6, -26; CFrame.Orientation 0, 75, 0 - CFrame.Position -65, 6, -6; CFrame.Orientation 0, 75, 0 - CFrame.Position -74, 6, 19; CFrame.Orientation 0, 75, 0 - CFrame.Position -67, 6, 46; CFrame.Orientation 0, 75, 0 - CFrame.Position -38, 14, 28; CFrame.Orientation 0, 75, 0 - CFrame.Position -8, 14, 8; CFrame.Orientation 0, 75, 0 - CFrame.Position -3, 14, 34; CFrame.Orientation 0, 75, 0 - CFrame.Position 35, 33, 9; CFrame.Orientation 0, -105, 0 - CFrame.Position 59, 39, 6; CFrame.Orientation 0, 90, 0 - CFrame.Position 78, 33, 4; CFrame.Orientation 0, -75, 0 - CFrame.Position 83, 39, 22; CFrame.Orientation 0, -165, 0 - CFrame.Position 113, 70, 56; CFrame.Orientation 0, -135, 0 - CFrame.Position 134, 70, 76; CFrame.Orientation 0, -135, 0 - CFrame.Position 175, 70, 103; CFrame.Orientation 0, 75, 0 - CFrame.Position 197, 70, 107; CFrame.Orientation 0, 105, 0 - CFrame.Position 214, 81, 95; CFrame.Orientation 0, 120, 0 - CFrame.Position 235, 99, 78; CFrame.Orientation 0, 135, 0 - CFrame.Position 258, 125, 54; CFrame.Orientation 0, 135, 0 - CFrame.Position 251, 132, 11; CFrame.Orientation 0, 135, 0 - CFrame.Position 260, 140, 4; CFrame.Orientation 0, 135, 0 - CFrame.Position 270, 132, -5; CFrame.Orientation 0, 135, 0 - CFrame.Position 312, 162, -19; CFrame.Orientation 0, 105, 0 - CFrame.Position 360, 206, -40; CFrame.Orientation 0, 69, 0 - CFrame.Position 377, 260, -4; CFrame.Orientation 0, -145, 0 - CFrame.Position 380, 206, 13; CFrame.Orientation 0, 12, 0 - CFrame.Position 386, 260, 27; CFrame.Orientation 0, -145, 0 - CFrame.Position 391, 206, 45; CFrame.Orientation 0, 12, 0 After placing all coins, delete the placeholder coins. ``` ![A view of shiny gold coins hovering over each level of sea stack platform.](../../../../assets/tutorials/core-building-and-scripting/ApplyAssetLibrary/Coins-Final.jpg) #### Apply your own coins Swap each placeholder coin for the polished version. Because your script already references coins by name, you only need to make sure each replacement keeps the name **Coin** and lives inside the **Coins** folder. 1. In the **Explorer**, navigate to the asset library, right-click **Coin**, and choose **Copy**. 2. Paste **Coin** into the **Coins** folder inside **World**. 3. Use the **Move** and **Rotate** tools to match the position and angle of one of your placeholder coins. 4. Repeat for each placeholder coin until every spot you want collectable has a polished coin in place. 5. Once all coins are placed, delete the placeholder coins so only the polished versions remain in the **Coins** folder.![A view of shiny gold coins hovering over each level of sea stack platform.](../../../../assets/tutorials/core-building-and-scripting/ApplyAssetLibrary/Coins-Final.jpg) #### Recreate the sample 1. In the **Explorer** window, navigate to the asset library and copy **Coin**. 2. In the **Platforms** folder, paste **Coin** into the **Coins** folder. 3. In the **Properties** window: 1. Set **CFrame.Position** to `-121, 3, 6`. 2. Set **CFrame.Orientation** to `0, -105, 0`. 4. Repeat this process, adding and configuring the following **coin** assets wherever you set your initial coin placeholder objects: | CFrame.Position | CFrame.Orientation | | --- | --- | | `-121, 3, 6` | `0, -105, 0` | | `-107, 3, 10` | `0, -105, 0` | | `-104, 3, 35` | `0, 120, 0` | | `-86, 3, 61` | `0, 30, 0` | | `-101, 3, -12` | `0, -15, 0` | | `-22, 6, -39` | `0, 120, 0` | | `-48, 6, -26` | `0, 75, 0` | | `-65, 6, -6` | `0, 75, 0` | | `-74, 6, 19` | `0, 75, 0` | | `-67, 6, 46` | `0, 75, 0` | | `-38, 14, 28` | `0, 75, 0` | | `-8, 14, 8` | `0, 75, 0` | | `-3, 14, 34` | `0, 75, 0` | | `35, 33, 9` | `0, -105, 0` | | `59, 39, 6` | `0, 90, 0` | | `78, 33, 4` | `0, -75, 0` | | `83, 39, 22` | `0, -165, 0` | | `113, 70, 56` | `0, -135, 0` | | `134, 70, 76` | `0, -135, 0` | | `175, 70, 103` | `0, 75, 0` | | `197, 70, 107` | `0, 105, 0` | | `214, 81, 95` | `0, 120, 0` | | `235, 99, 78` | `0, 135, 0` | | `258, 125, 54` | `0, 135, 0` | | `251, 132, 11` | `0, 135, 0` | | `260, 140, 4` | `0, 135, 0` | | `270, 132, -5` | `0, 135, 0` | | `312, 162, -19` | `0, 105, 0` | | `360, 206, -40` | `0, 69, 0` | | `377, 260, -4` | `0, -145, 0` | | `380, 206, 13` | `0, 12, 0` | | `386, 260, 27` | `0, -145, 0` | | `391, 206, 45` | `0, 12, 0` |![A view of shiny gold coins hovering over each level of sea stack platform.](../../../../assets/tutorials/core-building-and-scripting/ApplyAssetLibrary/Coins-Final.jpg) 5. Delete your placeholder coins. ### Mountains The asset library's single **MountainMesh** decorates the background, hides the edge of your water terrain, and encloses your playable area. Rotating and scaling each instance breaks up the repetition so players don't notice you're using the same mesh over and over. ![Several mountain meshes with different scales and rotation values overlap each other's edges to look like a mountain range. Each mesh has a light blue outline.](../../../../assets/tutorials/core-building-and-scripting/Mountain-Meshes-Stacked.jpg) #### Build with Assistant ```text For each row below, copy the MountainMesh from the asset library named "Core Building and Scripting" into Workspace > World > Mountains and set its Size, CFrame.Position, and CFrame.Orientation: - Size 2048, 334, 2048; CFrame.Position -1243, 115, -1402; CFrame.Orientation 0, 46, 0 - Size 2048, 212, 2048; CFrame.Position -326, 230, -1614; CFrame.Orientation -15, -155, 0 - Size 2048, 416, 1871; CFrame.Position 434, 160, -1543; CFrame.Orientation 0, -2, 0 - Size 2048, 400, 2048; CFrame.Position 1200, 147, -1534; CFrame.Orientation -15, -155, 0 - Size 2048, 540, 1488; CFrame.Position 1726, 220, -1139; CFrame.Orientation 0, -95, 0 - Size 2044, 442, 2044; CFrame.Position 2344, 174, -413; CFrame.Orientation 0, 5, 0 - Size 2048, 458, 2048; CFrame.Position 2267, 157, 1084; CFrame.Orientation 0, -81, 0 - Size 2048, 678, 2048; CFrame.Position 1662, 221, 1804; CFrame.Orientation 0, 9, 0 - Size 2043, 352, 1467; CFrame.Position 1025, 147, 2462; CFrame.Orientation 0, 54, 0 - Size 1980, 531, 1742; CFrame.Position 361, 180, 2119; CFrame.Orientation 0, 21, 0 - Size 2048, 415, 2048; CFrame.Position -580, 160, 1961; CFrame.Orientation 0, -122, 0 - Size 2048, 415, 2048; CFrame.Position -900, 160, 1652; CFrame.Orientation 0, 24, 0 - Size 1321, 403, 1321; CFrame.Position -1303, 133, 770; CFrame.Orientation 0, 55, 0 - Size 2048, 335, 2048; CFrame.Position -1653, 114, 161; CFrame.Orientation 0, -20, 0 - Size 2048, 249, 2048; CFrame.Position -1928, 79, -674; CFrame.Orientation 0, 24, 0 - Size 2048, 450, 2048; CFrame.Position -1426, 73, -895; CFrame.Orientation 0, -20, 0 ``` ![A view of the polished sea stacks, platform, and coins, with a mountain range in the background.](../../../../assets/tutorials/core-building-and-scripting/ApplyAssetLibrary/Mountains-Final.jpg) #### Apply your own mountains Mountains sit far outside the playable area to fill in the horizon, so exact placement matters less than visual coverage. You only need to make sure no gaps in the mountain range expose the empty edge of your water terrain. 1. In the **Explorer**, navigate to the asset library, right-click **MountainMesh**, and choose **Copy**. 2. Paste **MountainMesh** into the **Mountains** folder inside **World**. 3. Use the **Move**, **Scale**, and **Rotate** tools to position it along the edge of your water terrain. 4. Repeat the paste-and-position process several times, varying **Scale** and **Orientation** on each copy so the mountains feel naturally arranged rather than uniform. 5. Continue until the horizon is fully covered from every angle a player can look out from your platforms.![A view of the polished sea stacks, platform, and coins, with a mountain range in the background.](../../../../assets/tutorials/core-building-and-scripting/ApplyAssetLibrary/Mountains-Final.jpg) #### Recreate the sample 1. In the **Explorer** window, navigate to the asset library and copy **MountainMesh**. 2. In the **Platforms** folder, paste **MountainMesh** into the **Mountains** folder. 3. In the **Properties** window: 1. Set **Size** to `2048, 334, 2048`. 2. Set **CFrame.Position** to `-1243, 115, -1402`. 3. Set **CFrame.Orientation** to `0, 46, 0`. 4. Repeat this process, adding and configuring the following mountain assets along the border of your water terrain: | Size | CFrame.Position | CFrame.Orientation | | --- | --- | --- | | `2048, 212, 2048` | `-326, 230, -1614` | `-15, -155, 0` | | `2048, 416, 1871` | `434, 160, -1543` | `0, -2, 0` | | `2048, 400, 2048` | `1200, 147, -1534` | `-15, -155, 0` | | `2048, 540, 1488` | `1726, 220, -1139` | `0, -95, 0` | | `2044, 442, 2044` | `2344, 174, -413` | `0, 5, 0` | | `2048, 458, 2048` | `2267, 157, 1084` | `0, -81, 0` | | `2048, 678, 2048` | `1662, 221, 1804` | `0, 9, 0` | | `2043, 352, 1467` | `1025, 147, 2462` | `0, 54, 0` | | `1980, 531, 1742` | `361, 180, 2119` | `0, 21, 0` | | `2048, 415, 2048` | `-580, 160, 1961` | `0, -122, 0` | | `2048, 415, 2048` | `-900, 160, 1652` | `0, 24, 0` | | `1321, 403, 1321` | `-1303, 133, 770` | `0, 55, 0` | | `2048, 335, 2048` | `-1653, 114, 161` | `0, -20, 0` | | `2048, 249, 2048` | `-1928, 79, -674` | `0, 24, 0` | | `2048, 450, 2048` | `-1426, 73, -895` | `0, -20, 0` |![A view of the polished sea stacks, platform, and coins, with a mountain range in the background.](../../../../assets/tutorials/core-building-and-scripting/ApplyAssetLibrary/Mountains-Final.jpg) ## Playtest Polished assets can change the shape of a sea stack just enough to affect a jump that worked in the greybox version of the level. Take one final pass through the game to identify and fix any issues before publishing. Select **Test** from the dropdown and click **Play**. Climb to the flare at the top of the stacks; if a jump suddenly feels off, nudge the offending mesh and try again. Click **Stop** when you're done. Congratulations on completing the Core Curriculum! From here you can extend your project with new gameplay features or levels, browse Studio's full [feature set](/docs/en-us/platform.md), or move on to another curriculum like [Environmental art](/docs/en-us/tutorials/curriculums/environmental-art.md). Happy creating! --- title: "Create a project" url: /docs/en-us/tutorials/curriculums/core/building/create-a-project last_updated: 2026-06-29T19:34:12Z description: "This part of the tutorial gets you started with creating a project." --- # Create a project > **Info:** This curriculum uses Studio's built-in Assistant for routine tasks like creating instances, configuring properties, and generating scripts. This lets you spend more time on the creative aspects of building your game. > > Steps that include an Assistant workflow provide a **Build with Assistant** tab alongside a **Build it Yourself** tab. You can follow either approach, depending on how you'd like to work. To start recreating the **Island Jump** [3D platformer experience](https://www.roblox.com/games/14238807008), you need a project to build in. A project is a collection of places, assets, and settings that together make up a Roblox game. The fastest way to start is to use a Studio template. For this project, you'll use the default **Baseplate** template, which has a flat ground and a `Class.SpawnLocation` object. 1. In Roblox Studio, click **New Experience** in the upper-left corner. 2. In the **Explorer** window, expand **Workspace** and delete the **Baseplate** object so that the viewport shows an empty world with just the spawn location in the middle. > **Warning:** If you don't see the **Explorer** window, toggle it on from Studio's **Window** menu. ![A viewport view of the baseplate template without the baseplate. The spawn location is highlighted in the middle of the view.](../../../../assets/tutorials/core-building-and-scripting/Baseplate-Deleted.jpg) In the next section, you'll learn how to use the **Terrain Editor** to create an island environment around the spawn location. --- title: "Create an environment with terrain" url: /docs/en-us/tutorials/curriculums/core/building/create-an-environment-with-terrain last_updated: 2026-06-29T19:34:12Z description: "Explains how to create the island where players spawn using Studio's terrain tools." --- # Create an environment with terrain **Creating an environment with terrain** allows you to generate and customize organic materials within the 3D space that look and behave like natural materials from the real world. Using the tools in the [Terrain Editor](/docs/en-us/studio/terrain-editor.md), this section teaches you how to generate and sculpt terrain for a small island where players spawn and begin navigating your game. > **Info:** This is the one chapter where you must work directly in Studio instead of relying on Assistant. Terrain creation is a creative process, and it's difficult to exactly reproduce brush strokes, shapes, and material edits with AI. > > Don't worry if your environment doesn't match the sample [Island Jump](https://www.roblox.com/games/14239096301/) game perfectly. As long as your terrain supports the gameplay and goals of your game, differences in appearance are both normal and expected. ## Create the island The first step in creating your environment is to create a small island that players will spawn to when they start the game. 1. In the Terrain Editor, click the **Edit** tab and then click the **Draw** tool. 2. Set **Brush Size** to `32`. 3. Set **Source Material** to **Sand**. 4. Click in the viewport near your spawn location to drop a sphere of sand. ![Studio's Terrain Editor window with both the Edit tab and Draw button highlighted.](../../../../assets/studio/terrain-editor/Edit-Tab-Draw.png) > **Info:** If you can't see the sphere, zoom your camera out until your spawn location is smaller. ## Shape the island If you keep the island in its current spherical shape, players would have a difficult time navigating without falling off the island. 1. Switch to the **Flatten** tool. 2. Set **Brush Size** to `18`. 3. Set **Flatten Plane** to **Fixed**. 4. Set **Fixed Y Plane** to `0`. 5. Click and drag across the top of the sphere until you have a level surface. Now switch to the **Sculpt** tool. Set **Source Material** to **Sand**. Click and drag your mouse around the island's edges and below the water line to make the island look more natural. > **Info:** The **Draw** tool adds or subtracts material based on the brush's position, while the **Sculpt** tool only grows or erodes terrain that already exists. Use Draw to create new shapes and Sculpt to refine them. ## Create water To create a large body of water, this tutorial uses the **Fill** tool, which replaces all material within a selected region with another material. 1. Switch to the **Fill** tool. 2. Under **Selection Settings**, set: 1. **Size** to `1800, 5, 1800` (so the water reaches the horizon). 2. **Position** to `0, -15, 0` (so the water sits below the top of the island). 3. Under **Material Settings**, set: 1. **Fill Mode** to **Replace**. 2. **Source Material** to **Air**. 3. **Target Material** to **Water**. 4. Click **Replace** to generate the water around the island. ## Apply materials Now that you've created the base shape of your island, use the **Paint** tool to apply grass materials to the terrain and give the island a more natural appearance. 1. Switch to the **Paint** tool. 2. Set **Material Mode** to **Paint**. 3. Set **Source Material** to **Leafy Grass**. 4. Drag across the middle of the island to lay down a grassy field, leaving the edges sandy. Add finishing details. 1. Change **Brush Size** to `3`. 2. Change **Material** to **Grass**. 3. Drag along the edges of the leafy area to add visible blades. Make sure to leave space in the center for the spawn location and the first few platforms you'll build in the next chapter. Finally, position the spawn point. 1. In the **Explorer**, select **SpawnLocation**. 2. Using the **Move** tool from the toolbar, drag the spawn point towards the edge of the island so there's room for a launch platform. The sample game uses a position of `-127, -3, 9`, but anywhere near the edge works. > **Info:** For a more custom look, you can replace default terrain textures with custom texture assets using [Material Overrides](/docs/en-us/parts/materials.md#material-overrides). ## Playtest Once you are happy with your island's appearance, playtest your experience to check the scale of the island. Click **Test** from the dropdown menu in the toolbar and then click **Play**. Run around the island and see how it feels. When you're done, click **Stop**. ![Test option in the testing modes dropdown of Studio's mezzanine.](../../../../assets/studio/general/Mezzanine-Testing-Mode-Test.png) > **Warning:** If your character spawns facing the wrong direction, such as toward the ocean instead of the center of the island, rotate the `Class.SpawnLocation` object and playtest again until they spawn the way you want. Next up, you'll learn how to create a playable area for players to follow in order to reach the highest platform. --- title: "Create basic visual effects" url: /docs/en-us/tutorials/curriculums/core/building/create-basic-visual-effects last_updated: 2026-06-29T19:34:12Z description: "Explains how to create two different kinds of visual effect using particle emitters." --- # Create basic visual effects A static world can feel lifeless. **Visual effects** add motion, make the environment feel more dynamic, and help draw players' attention to important areas. In this section, you'll use **particle emitters** to create two effects. The first is a glowing flare on top of the tallest sea stack that draws players' eyes upward, and the second is a layer of drifting dust motes that adds atmosphere and depth to the environment. > **Info:** Particle emitters are primarily configured through numeric properties, making them a good fit for Assistant. Use the **Build with Assistant** tabs to generate a set of working starting values, then adjust the colors and `NumberSequence` curves yourself until the effect looks the way you want. ## Create a flare The sample [Island Jump - Final](https://www.roblox.com/games/14238807008/Island-Jump-Completed-Sample) game puts a giant flare on top of its tallest platform. Because the rest of the world is still, the flare becomes a focal point, making players naturally want to climb toward it. #### Build with Assistant ```text In Workspace > World, create a new folder named VFX if it doesn't exist. Inside the VFX folder, create a block Part named VFX_Flare with Size 20, 20, 20, CFrame.Position 400, 331, 79, and CFrame.Orientation 0, 0, 0. Set Transparency to 1 so the part is invisible, and enable Anchored. Inside VFX_Flare, create an Attachment. Inside that Attachment, create a ParticleEmitter named Emitter_Flare with these properties: - Texture: rbxassetid://8983307836 - Color: Color3.fromRGB(127, 84, 59) - LightEmission: 1 - ZOffset: 1 - Lifetime: NumberRange.new(10, 10) - Rate: 0.45 - RotSpeed: NumberRange.new(20, 20) - Speed: NumberRange.new(0, 0) - Size: a NumberSequence that starts at 0, grows slowly early in the lifetime, then quickly rises to 10 and stays at 10 for the rest of the lifetime. - Transparency: a NumberSequence that stays near 0 (visible) for most of the lifetime, then bounces between values toward the end and settles at 1. Also inside VFX_Flare (a sibling of the Attachment), create a PointLight with Brightness 2 and Range 36. ``` After running the prompt, open the **Size** and **Transparency** sequences in the Properties window and tweak the curves yourself until the pulsing feels right. #### Build it Yourself 1. In the **Explorer**, hover over **World**, click **⊕**, and insert a new folder. Rename it **VFX**. This is where you'll keep all visual effect parts. 2. Insert a **block** part into the **VFX** folder. The sample places it above `Level_7` with the following properties: 1. Set **Name** to `VFX_Flare`. 2. Set **Size** to `20, 20, 20`. 3. Set **CFrame.Position** to `400, 331, 79`. 4. Set **CFrame.Orientation** to `0, 0, 0`. 5. Set **Transparency** to `1` so the part itself is invisible and only the particles will show. 6. Enable **Anchored** so physics doesn't move it. 3. Hover over **VFX_Flare**, click **⊕**, and insert an attachment. It appears in the center of the part, and particles will emit from this point. 4. Insert a **ParticleEmitter** into the attachment and rename it **Emitter_Flare**. Particles start emitting immediately. ### Configure the flare Now shape the emitter to actually look like a flare. Each property below has a purpose. To learn more about how each property affects the resulting visual effect, see `Class.ParticleEmitter|ParticleEmitter` and [Customizing particles](/docs/en-us/effects/particle-emitters.md#customize-particles). 1. Select `Emitter_Flare`. In the **Properties** window, set **Texture** to `rbxassetid://8983307836`. You can also upload your own [asset](/docs/en-us/projects/assets.md) if you want. 2. Set the rest of the basic properties: 1. Set **Color** to `127, 84, 59` (or pick your own). 2. Set **LightEmission** to `1` so colors blend additively — overlapping particles brighten, which is what the flare texture is designed for. 3. Set **ZOffset** to `1`. 4. Set **Lifetime** to `10, 10` (`Lifetime` takes a min/max range so each particle picks a random duration; equal values mean no variation). 5. Set **Rate** to `0.45`. Rate is particles per second, so this is roughly one every two seconds. 6. Set **RotSpeed** to `20`. 7. Set **Speed** to `0`. 3. Some properties use a `Datatype.NumberSequence` so the value can change across a particle's lifetime, which is what gives the flare its pulse. Click the **…** next to **Size** and shape the curve so it starts at `0`, grows slowly, then jumps to `10` and holds for the rest of the lifetime.![A number sequence window where the size starts at 0 and grows slowly in the beginning of its lifetime, then quickly grows to a size of 10 and remains 10 for the duration of the lifetime.](../../../../assets/tutorials/core-building-and-scripting/Flare-NumberSequence-Size.png)_The Y axis represents each particle's size and the X axis represents each particle's lifetime. The size starts at 0 and grows slowly in the beginning of its lifetime, then quickly grows to a size of 10 and remains 10 for the duration of the lifetime._ 4. Click the **…** next to **Transparency** to shape that curve. Stay near `0` (visible) for most of the lifetime, then bounce around as the particle fades out and settle at `1`.![A number sequence window where the particle is visible (equal or close to 0) for the majority of its lifetime. As the particle approaches the end of its lifetime, its transparency value bounces up and down at different values, settling at 1 at the very end.](../../../../assets/tutorials/core-building-and-scripting/Flare-NumberSequence-Transparency.png)_The particle is visible (equal or close to 0) for the majority of its lifetime. As the particle approaches the end of its lifetime, its transparency value bounces up and down at different values, settling at 1 at the very end._![The final version of the flare against a bright blue sky.](../../../../assets/tutorials/core-building-and-scripting/Final-Flare.jpg) ### Add a PointLight To make the flare cast actual light on its surroundings, use a **PointLight**. Roblox has three light types: `Class.PointLight` (light from a single point in all directions), `Class.SpotLight` (a cone in a chosen direction), and `Class.SurfaceLight` (light from one face of a part). For a flare, use `PointLight`. 1. Hover over **VFX_Flare**, click **⊕**, and insert a **PointLight**. 2. With the **PointLight** selected, in the **Properties** window: 1. Set **Brightness** to `2`. 2. Set **Range** to `36`.![The final version of the flare hovering over a gray cylinder sea stack. The flare emits a gentle glow over the sea stack.](../../../../assets/tutorials/core-building-and-scripting/Flare-With-PointLight.jpg) ## Create the dust particles The second type of particle emitter the sample game uses to add dynamic movement to the experience is one that dust particles throughout the atmosphere. These particles surround the player, adding a sense of texture and depth to the air itself. #### Build with Assistant ```text In Workspace > World > VFX, create a block Part named VFX_DustMotes with Size 645, 355, 275, CFrame.Position 198, 168, 26, and CFrame.Orientation 0, 0, 0. Set Transparency to 1 so the part is invisible. Disable CanCollide. Enable Anchored. Inside VFX_DustMotes, create a ParticleEmitter named Emitter_DustMotes with these properties: - Texture: rbxassetid://14302399641 - Color: Color3.fromRGB(192, 241, 255) - ZOffset: -5 - Lifetime: NumberRange.new(1, 10) - Rate: 50000 - Rotation: NumberRange.new(-45, 45) - RotSpeed: NumberRange.new(-60, -60) - Speed: NumberRange.new(1, 5) - Acceleration: Vector3.new(1, -1, 1) - Size: a NumberSequence that rises to about 0.25 shortly after creation, then fades gradually to 0. - Transparency: a NumberSequence that begins fully transparent, becomes more opaque (with an envelope of about 0.1) in the middle of the lifetime, then slowly fades out to 1. ``` After running, open the Size and Transparency sequences and tune the curves by eye until the dust feels right. #### Build it Yourself 1. Insert a **block** part into the **VFX** folder and scale it to cover your entire playable area. The sample uses: 1. Set **Name** to `VFX_DustMotes`. 2. Set **Size** to `645, 355, 275`. 3. Set **CFrame.Position** to `198, 168, 26`. 4. Set **CFrame.Orientation** to `0, 0, 0`. 5. Set **Transparency** to `1` so the volume itself is invisible. 6. Disable **CanCollide** so players walk through it freely. 7. Enable **Anchored**. 2. Insert a **ParticleEmitter** into the part and rename it **Emitter_DustMotes**. Particles fill the volume immediately. ### Configure the dust particles The dust particle emitter requires some new properties to change. `Class.ParticleEmitter.Acceleration` determines how a particle's `Class.ParticleEmitter.Speed` changes throughout its lifetime. Acceleration is often used to apply a gravity effect to particles with a negative Y value. `Class.ParticleEmitter.Rotation` defines the range of rotations in degrees for emitted particles, with positive values corresponding with the clockwise direction. To add some randomness to the rotation of each dust mote, you can create a range of angles to choose from. For each point in a `Class.NumberSequence`, you can set an envelope using the number input at the bottom of the window. An envelope sets the range from which Studio picks a random value higher or lower than the point's value each time a particle emits. The size of the envelope determines the range of the random selection. The sequence for `Class.ParticleEmitter.Transparency` includes an envelope so that each particle's visibility is unpredictable. 1. Select **Emitter_DustMotes** and in the **Properties** window: 1. Set **Color** to `192, 241, 255`. 2. Set **Texture** to `rbxassetid://14302399641`. 3. Set **ZOffset** to `-5` so dust renders behind players and objects. 4. Set **Lifetime** to `1, 10`. 5. Set **Rate** to `50000`. This sounds enormous, but the part is huge so the dust still appears sparse. 6. Set **Rotation** to `-45, 45`. 7. Set **RotSpeed** to `-60`. 8. Set **Speed** to `1, 5`. 9. Set **Acceleration** to `1, -1, 1`. 2. Click the **…** next to **Size** and shape the `NumberSequence` so it rises to about `0.25` shortly after creation, then fades gradually to `0`.![A number sequence window where the size rises to 0.25 shortly after creation, then fades down gradually to 0.](../../../../assets/tutorials/core-building-and-scripting/DustMotes-NumberSequence-Size.png) 3. Click the **…** next to **Transparency** and shape that curve so it starts fully transparent, becomes more opaque in the middle (with an envelope of about `0.1`), then slowly fades out.![A number sequence window where the particle begins fully transparent, becomes randomly more opaque with an envelope of 0.1, then slowly fades out.](../../../../assets/tutorials/core-building-and-scripting/DustMotes-NumberSequence-Transparency.png) ![The final version of the dust particles against a bright blue sky.](../../../../assets/tutorials/core-building-and-scripting/Dust-Motes-Sky.jpg)_Faint dust particles in the air looking up at the sky from the platforms_ --- title: "Customize global lighting" url: /docs/en-us/tutorials/curriculums/core/building/customize-global-lighting last_updated: 2026-06-29T19:34:12Z description: "Explains how to use global lighting settings to refine the look and feel of your experience." --- # Customize global lighting **Global lighting** controls the overall look and atmosphere of your experience. By adjusting a few properties on the `Class.Lighting` service, you can change the color, shadows, time of day, and atmospheric effects of a scene. In this section, you'll update the lighting to match the [Island Jump - Final](https://www.roblox.com/games/14238807008/Island-Jump-Completed-Sample) sample. > **Info:** Lighting is mostly configured through property values, making it a great fit for Assistant. After applying the prompt, spend some time in the viewport adjusting **Ambient**, **ClockTime**, and **Density** until the scene looks the way you want. For an overview of every lighting object Studio offers, see [Lighting and effects](/docs/en-us/environment.md). ## Set lighting properties The `Class.Lighting` service controls how light behaves in your experience. The sample game adjusts properties such as ambient color, lighting technology, shadow settings, and sun position to create a cooler ocean palette, sharper shadows, and stronger directional lighting. #### Build with Assistant ```text On the Lighting service, set the following properties: - Ambient: Color3.fromRGB(16, 16, 16) - ColorShift_Top: Color3.fromRGB(196, 222, 255) - OutdoorAmbient: Color3.fromRGB(134, 158, 190) - ShadowSoftness: 0 - LightingStyle: Realistic - ClockTime: 9 - GeographicLatitude: 78 ``` After it runs, the world picks up a cooler blue-grey tone, shadows get hard edges, and the sun shifts to mid-morning over to the right of your sea stacks. #### Build it Yourself In the **Properties** window of **Lighting**: 1. **Tint the ambient and reflective light.** `Class.Lighting.Ambient` colors light in shaded areas (like the inside of your tunnel), `Class.Lighting.OutdoorAmbient` colors light where the sky is visible, and `Class.Lighting.ColorShift_Top` tints surfaces facing the sun. Together they pull the whole world toward a cool blue-grey ocean tone. 1. Set **Ambient** to `16, 16, 16`. 2. Set **ColorShift_Top** to `196, 222, 255`. 3. Set **OutdoorAmbient** to `134, 158, 190`.**A comparison between the sample Island Jump game's default and custom ambient lighting**![The sample Island Jump game with default ambient lighting visuals.](../../../../assets/tutorials/core-building-and-scripting/Default-Color.jpg)_Default Properties_![The sample Island Jump game with custom ambient lighting visuals.](../../../../assets/tutorials/core-building-and-scripting/Custom-Color.jpg)_Custom Properties_ 2. **Switch to realistic lighting.** Studio defaults to `Enum.LightingStyle|Soft`, which produces a flatter look. Set **LightingStyle** to `Enum.LightingStyle|Realistic` so local light sources cast precise shadows and illumination. Your flare will start dropping a real shadow on the platform beneath it.![The sample Island Jump game with Soft lighting style.](../../../../assets/tutorials/core-building-and-scripting/LightingStyle-Soft.jpg)_`Enum.LightingStyle|Soft`_![The sample Island Jump game with Realistic lighting produces more prominent lighting from the flare.](../../../../assets/tutorials/core-building-and-scripting/LightingStyle-Realistic.jpg)_`Enum.LightingStyle|Realistic`_ 3. **Harden shadows.** Set **ShadowSoftness** to `0` to give shadows crisp edges. This makes the moment players step from outside into the tunnel feel more dramatic.![The sample Island Jump game with default shadow visuals that produce fuzzy shadows.](../../../../assets/tutorials/core-building-and-scripting/Default-Shadows.jpg)_Default Shadows_![The sample Island Jump game with custom shadow visuals that produce sharp shadows.](../../../../assets/tutorials/core-building-and-scripting/Custom-Shadows.jpg)_Custom Shadows_ 4. **Move the sun.** `Class.Lighting.ClockTime|ClockTime` is the time of day on a 0-24 hour scale, and `Class.Lighting.GeographicLatitude|GeographicLatitude` rotates the world to position the sun at a different angle. The values below drop the sun toward the right of the sea stacks for longer, more directional shadows. 1. Set **ClockTime** to `9`. 2. Set **GeographicLatitude** to `78`. > **Info:****ClockTime** and **TimeOfDay** are linked. Changing one updates the other, so you only need to set one.![The sample Island Jump game with the default sun position high in the sky.](../../../../assets/tutorials/core-building-and-scripting/Default-SunPosition.jpg)_Default sun position_![The sample Island Jump game with a custom sun position approaching the horizon.](../../../../assets/tutorials/core-building-and-scripting/Custom-SunPosition.jpg)_Custom sun position_ For an overview of the rest of the `Class.Lighting` properties, see [Global lighting](/docs/en-us/environment/lighting.md). ## Atmosphere properties The `Class.Atmosphere` object is a child of the `Class.Lighting` service. It adds depth by simulating particles in the air and blending distant scenery into the sky. The two most important properties are `Density`, which controls the thickness of the atmosphere, and `Offset`, which controls where that blending begins. #### Build with Assistant ```text On the Atmosphere object inside the Lighting service, set: - Density: 0.375 - Offset: 0.17 ``` #### Build it Yourself 1. In the **Explorer**, expand the **Lighting** service and select its child **Atmosphere** object. In the **Properties** window: 1. Set **Density** to `0.375` to thicken the air. This obscures the edge of your water terrain, so players don't see where the world ends.![The sample Island Jump game with the default air particle density that produces a clear background.](../../../../assets/tutorials/core-building-and-scripting/Custom-SunPosition.jpg)_Default air particle density_![The sample Island Jump game with custom air particle density that produces a hazy background.](../../../../assets/tutorials/core-building-and-scripting/Custom-AirParticleDensity.jpg)_Custom air particle density_ 1. Set **Offset** to `0.17`. Lower offset values blend distant objects into the sky, which is good for endless-feeling worlds. Higher values keep a clear silhouette. This middle value keeps the mountains you'll add next chapter visible while still giving you a hint of distant fog.![The sample Island Jump game with default Offset values that keep the background visible.](../../../../assets/tutorials/core-building-and-scripting/Custom-AirParticleDensity.jpg)_Default `Class.Atmosphere.Offset`_![The sample Island Jump game with custom Offset values that hide the edges of the background.](../../../../assets/tutorials/core-building-and-scripting/Custom-Offset.jpg)_Custom `Class.Atmosphere.Offset`_ For more information about Atmosphere, see [Atmospheric Effects](/docs/en-us/environment/atmosphere.md). Now that you've configured the game's lighting, the next section shows you how to replace the greybox layout with polished assets. #### Pre-Customization ![The sample Island Jump game's lighting visuals before the customization from this page.](../../../../assets/tutorials/core-building-and-scripting/Lighting-Pre-Customization.jpg) #### Customized Visual Effects ![The sample Island Jump game's lighting visuals after the customization from this page.](../../../../assets/tutorials/core-building-and-scripting/Lighting-Post-Customization.jpg) --- title: "Greybox a playable area" url: /docs/en-us/tutorials/curriculums/core/building/greybox-a-playable-area last_updated: 2026-06-29T19:34:12Z description: "Explains how to use Studio's solid modeling tools to plan out the basic structure of your platforms." --- # Greybox a playable area **Greyboxing** is the process of building a level with simple shapes before investing time in scripting or creating final art assets. It helps you test gameplay, identify layout issues, and make changes quickly while the environment is still easy to modify. In this section, you'll greybox the sea stack platforms that make up the playable area of the experience. Once the layout feels right, you can begin scripting. ## Plan the playable area In the final experience, players collect coins to increase their jump power and reach higher platforms. You'll implement this progression system later in the tutorial, but it's important to plan for it while greyboxing your environment. As a general rule, the height difference between platforms should gradually increase to encourage players to collect coins and continue progressing. As a reference, the sample [Island Jump - Building](https://www.roblox.com/games/14239096301/Island-Jump-Building) game uses seven height levels. The first platform sits low enough that players only need a few coins to reach it. The subsequent height differences increase to 8, 20, 35, 55, 81, and 110 studs, creating a steady sense of progression throughout the experience. > **Info:** A stud is Studio's primary unit of length, equivalent to about 28 cm. If you create your own layout instead of using the Assistant prompt or recreating the sample, keep each new height level that requires a jump upgrade at least 30 studs above the previous one. ![A distant view of the sample island jump game's greybox geometry. Each level of height difference between sea stacks is highlighted.](../../../../assets/tutorials/block-out-a-playable-area/Platform-Levels.jpg) ## Add the platforms Now that you have a plan for the difference in height between platforms, add placeholder `Class.Part` objects to represent the sea stacks. Parts are Roblox's basic building blocks, with properties that control their shape, size, color, and other physical characteristics. For this project, use **cylinder** parts to give players a flat top to land on when jumping. **A comparison between the sample Island Jump game's greybox and final geometry** ![The sample Island Jump game's greybox geometry.](../../../../assets/tutorials/block-out-a-playable-area/Add-Platforms-Greybox.jpg) ![The sample Island Jump game's final geometry.](../../../../assets/tutorials/block-out-a-playable-area/Add-Platforms-Final.jpg) ### Create an organization structure Before adding parts, create two containers to keep the project organized. Roblox provides `Class.Folder` objects for grouping related instances and `Class.Model` objects for grouping 3D objects. In this tutorial, you'll create a `World` folder to hold the environment and a `Blockout_Parts` model inside that folder to hold the greybox platforms. 1. In the **Explorer**, create a folder named **World** inside **Workspace**. 2. Create a model named **Blockout_Parts** inside the new folder. > **Warning:** In Studio, names are case-sensitive. You must spell **World** and **Blockout_Parts** exactly as shown or the scripts you add later will break. ### Insert parts Now that you have an organization structure, add the cylinders for your platforms. You can use an Assistant prompt, build your own layout manually, or recreate the layout of the sample game. #### Build with Assistant To get Assistant to generate the entire blockout in one step, copy the prompt that matches whether you want a unique layout or an exact copy of the sample game. ```text Create multiple cylinder Parts inside Workspace > World > Blockout_Parts to form platforms. Start with one large, flat cylinder centered on the island, then add at least seven more cylinders that all start at the same vertical base but vary in height (like tall buildings) to create an upward path. Adjust Size, Position, and Orientation so the top surfaces are flat and walkable, and set all Parts to Anchored. ``` ```text Create the following cylinder parts inside Workspace > World > Blockout_Parts: 1. Level_1 (Size 12, 131, 131; CFrame.Position -23, -4, 9; CFrame.Orientation 0, 0, 90) 2. Level_2 (Size 20, 81, 81; CFrame.Position -8, 0, 24; CFrame.Orientation 0, 0, 90) 3. Level_3a (Size 40, 44, 44; CFrame.Position 42, 10, 9; CFrame.Orientation 0, 0, 90) 4. Level_3b (Size 40, 34, 34; CFrame.Position 87, 10, 4; CFrame.Orientation 0, 0, 90) 5. Level_3c (Size 40, 44, 44; CFrame.Position 97, 10, 49; CFrame.Orientation 0, 0, 90) 6. Level_4a (Size 75, 39, 39; CFrame.Position 112, 27.5, 46.5; CFrame.Orientation 0, 0, 90) 7. Level_4b (Size 75, 65, 65; CFrame.Position 137, 27.5, 69; CFrame.Orientation 0, 0, 90) 8. Level_4c (Size 75, 60, 60; CFrame.Position 159.5, 27.5, 91.5; CFrame.Orientation 0, 0, 90) 9. Level_4d (Size 75, 30, 30; CFrame.Position 207, 27.5, 106.5; CFrame.Orientation 0, 0, 90) 10. Level_4e (Size 75, 61, 61; CFrame.Position 250, 27.5, 74; CFrame.Orientation 0, 0, 90) 11. Level_5a (Size 130, 60, 60; CFrame.Position 268, 55, 50; CFrame.Orientation 0, 0, 90) 12. Level_5b (Size 130, 25, 25; CFrame.Position 256, 55, 2.5; CFrame.Orientation 0, 0, 90) 13. Level_5c (Size 130, 25, 25; CFrame.Position 276, 55, -17.5; CFrame.Orientation 0, 0, 90) 14. Level_5d (Size 130, 54, 54; CFrame.Position 322, 55, -21; CFrame.Orientation 0, 0, 90) 15. Level_6a (Size 211, 79, 79; CFrame.Position 367, 94.5, -41; CFrame.Orientation 0, 0, 90) 16. Level_6b (Size 211, 24, 24; CFrame.Position 387, 94.5, 11.5; CFrame.Orientation 0, 0, 90) 17. Level_6c (Size 211, 44, 44; CFrame.Position 397, 94.5, 51.5; CFrame.Orientation 0, 0, 90) 18. Level_7 (Size 321, 61, 61; CFrame.Position 407, 149.5, 79; CFrame.Orientation 0, 0, 90) Make sure all Parts are anchored, correctly positioned, and oriented to form a continuous level layout. ``` #### Create your own 1. In the **Home** or **Model** toolbar tab, click the small arrow next to the **Part** tool and choose **Cylinder**.![A close-up view of the part picker.](../../../../assets/tutorials/block-out-a-playable-area/Part-Picker.png) 2. In the **Explorer**, drag the new cylinder Part onto your `Blockout_Parts` model so that it becomes a child of the model. 3. Use the **Move**, **Scale**, and **Rotate** tools (in the same toolbar) to size and position your cylinder as a large, flat platform in the middle of the island. See [Transform parts](/docs/en-us/parts.md#transform-parts) for more information on these tools. > **Success:** To grab a single part, select it in the **Explorer** instead of the viewport. If you select a part in the viewport, it selects the entire model.![A large cylinder object partially sticking out of an island surrounded by water.](../../../../assets/tutorials/block-out-a-playable-area/First-Platform.jpg) 4. Repeat the previous steps for at least seven more cylinders, increasing the height of each one as you build outward from the island. Stop once you have a layout you can jump across.![Many large cylinder objects partially sticking out of an island surrounded by water, and the water itself.](../../../../assets/tutorials/block-out-a-playable-area/Final-Platforms.jpg) 5. Select the `Blockout_Parts` model in the Explorer and click **Anchor** in the **Home** or **Model** toolbar to make sure the physics system doesn't move your parts when the game starts. #### Recreate the sample 1. In the **Home** toolbar, click the **Part** dropdown and choose **Cylinder**. Drag the new part onto `Blockout_Parts` in the Explorer. 2. With the new cylinder selected, in the **Properties** window: 1. Set **Name** to `Level_1`. 2. Set **Size** to `12, 131, 131`. 3. Set **CFrame.Position** to `-23, -4, 9`. 4. Set **CFrame.Orientation** to `0, 0, 90` so the flat top faces the sky.![A large cylinder object partially sticking out of an island surrounded by water.](../../../../assets/tutorials/block-out-a-playable-area/First-Platform.jpg) 3. Repeat for the remaining seventeen platforms using the values below: | Name | Size | CFrame.Position | CFrame.Orientation | | --- | --- | --- | --- | | Level_2 | `20, 81, 81` | `-8, 0, 24` | `0, 0, 90` | | Level_3a | `40, 44, 44` | `42, 10, 9` | `0, 0, 90` | | Level_3b | `40, 34, 34` | `87, 10, 4` | `0, 0, 90` | | Level_3c | `40, 44, 44` | `97, 10, 49` | `0, 0, 90` | | Level_4a | `75, 39, 39` | `112, 27.5, 46.5` | `0, 0, 90` | | Level_4b | `75, 65, 65` | `137, 27.5, 69` | `0, 0, 90` | | Level_4c | `75, 60, 60` | `159.5, 27.5, 91.5` | `0, 0, 90` | | Level_4d | `75, 30, 30` | `207, 27.5, 106.5` | `0, 0, 90` | | Level_4e | `75, 61, 61` | `250, 27.5, 74` | `0, 0, 90` | | Level_5a | `130, 60, 60` | `268, 55, 50` | `0, 0, 90` | | Level_5b | `130, 25, 25` | `256, 55, 2.5` | `0, 0, 90` | | Level_5c | `130, 25, 25` | `276, 55, -17.5` | `0, 0, 90` | | Level_5d | `130, 54, 54` | `322, 55, -21` | `0, 0, 90` | | Level_6a | `211, 79, 79` | `367, 94.5, -41` | `0, 0, 90` | | Level_6b | `211, 24, 24` | `387, 94.5, 11.5` | `0, 0, 90` | | Level_6c | `211, 44, 44` | `397, 94.5, 51.5` | `0, 0, 90` | | Level_7 | `321, 61, 61` | `407, 149.5, 79` | `0, 0, 90` | ![Many large cylinder objects partially sticking out of an island and its surrounding water.](../../../../assets/tutorials/block-out-a-playable-area/Final-Platforms.jpg) 1. Once all platforms are in, select the `Blockout_Parts` model and click **Anchor** in the **Home** toolbar to make sure the physics system doesn't move your parts when the game starts. ### Align parts > **Info:** If you used the sample **Island Jump - Building** game values when inserting your sea stack platforms, you can skip this step. If you're creating your own layout, it's often easier to manage platform heights when every platform shares the same base elevation and only varies in size. The [Align Tool](/docs/en-us/studio/align-tool.md) can do this in a single click by aligning the **bottom** edge on the **Y** axis. 1. In the **Explorer**, select all of your platforms. 2. In the **Model** toolbar tab, click **Align**. 3. In the Align Tool window: 1. Set **Mode** to **Min**. 2. Set **Align In** to **World** and **Y**. 3. Set **Relative To** in **Selection Bounds**. 4. Click **Align**. ![An underwater view of many cylinders that are aligned along their bottom edge.](../../../../assets/tutorials/block-out-a-playable-area/Platforms-Aligned-Underwater.jpg)_All platforms align their bottom edge_ ### Create a hollow tunnel To add more visual variety, carve a tunnel through one of the sea stacks using Studio's **solid modeling** tools. For this tutorial, you'll use **Negate** to create the hole and **Union** to combine the parts into a single shape. For more information on all solid modeling tools, see [solid modeling](/docs/en-us/parts/solid-modeling.md). #### Build with Assistant ```text Create a hollow tunnel structure using these steps: 1. Create a cylinder Part named Tunnel with Size 24, 65, 69 and CFrame.Position 137, 77, 69 and CFrame.Orientation 0, 0, 90. Set Anchored to true. 2. Create a block Part named Hollow_Part with Size 24.5, 72, 22 and CFrame.Position 134.5, 77, 71 and CFrame.Orientation 0, 135, 90. Set Anchored to true. This part should intersect the cylinder to define the hollow space. 3. Negate the Hollow_Part so it becomes a subtraction shape. 4. Select both the Tunnel part and the negated Hollow_Part, then perform a Union operation so the hollow shape is cut out of the cylinder. 5. Rename the resulting union to Level_4b_Union. 6. Find the existing Level_4b platform, duplicate it, and rename the duplicate to Level_4b_Top. 7. Set Level_4b_Top to Size 74, 65, 69 and CFrame.Position 137, 126, 69 and CFrame.Orientation 0, 0, 90. Make sure it is Anchored and positioned above the tunnel. Make sure all Parts are properly aligned and that the final result is a cylinder tunnel with an open passage and a platform placed on top. ``` #### Build it Yourself 1. Insert a **cylinder** part above one of your sea stacks to make the body of the tunnel. To recreate the sample, place this cylinder above `Level_4b` with and: 1. Set **Name** to `Tunnel`. 2. Set **Size** to `24, 65, 69`. 3. Set **CFrame.Position** to `137, 77, 69`. 4. Set **CFrame.Orientation** to `0, 0, 90`. 2. Insert a **block** part next, positioned so it pokes through the cylinder where you want the tunnel opening. The block needs to be at least as tall as the cylinder and wide enough for a player to walk through. To recreate the sample: 1. Set **Name** to `Hollow_Part`. 2. Set **Size** to `24.5, 72, 22`. 3. Set **CFrame.Position** to `134.5, 77, 71`. 4. Set **CFrame.Orientation** to `0, 135, 90`.![A close-up view of a gray block sticking out of a gray cylinder.](../../../../assets/tutorials/block-out-a-playable-area/HollowTunnel-Start.jpg) 3. Select the block in the **Explorer** and click **Negate** in the **Model** toolbar. The block turns translucent pink to show it's now a hole punch.![A close-up view of a partially translucent pink block sticking out of a gray cylinder.](../../../../assets/tutorials/block-out-a-playable-area/HollowTunnel-Negate.jpg) 4. Select both the negated block and the cylinder, then click **Union** in the **Model** toolbar. The negated shape gets carved out of the cylinder, leaving a tunnel.![A close-up view of a gray cylinder with an open tunnel through itself. The tunnel does not have a roof.](../../../../assets/tutorials/block-out-a-playable-area/HollowTunnel-Union.jpg) 5. Rename the resulting union to something descriptive like `Level_4b_Union`. 6. Duplicate the platform underneath the tunnel and place the duplicate on top of the tunnel as a roof. To recreate the sample: 1. Set **Name** to `Level_4b_Top`. 2. Set **Size** to `74, 65, 69`. 3. Set **CFrame.Position** to `137, 126, 69`. 4. Set **CFrame.Orientation** to `0, 0, 90`.![A close-up view of a tunnel through a cylinder.](../../../../assets/tutorials/block-out-a-playable-area/HollowTunnel-Final.jpg) ## Playtest the layout Before you start scripting, playtest the layout to catch any issues while it's still easy to make changes. Start a playtest from the **Test** tab and simulate the player's starting state. 1. Find your character in the **Explorer** and select **Humanoid**. 2. Enable **UseJumpPower**. 3. In the **Properties** window, set **JumpPower** to `0`. 4. As you reach higher platforms, increase **JumpPower** in increments of `30` to simulate collecting jump upgrades and make sure that each level is reachable. ![Jump Settings with the JumpPower and UseJumpPower properties highlighted.](../../../../assets/tutorials/block-out-a-playable-area/Humanoid-Jump-Settings.png) In the next section, you'll learn how to script the overall gameplay. --- title: "Core curriculum" url: /docs/en-us/tutorials/curriculums/core last_updated: 2026-06-29T19:34:12Z description: "Learn the technical and creative essentials to building Roblox experiences." --- # Core curriculum The core curriculum helps you learn many essential Studio features across technical and creative disciplines. You'll learn how to recreate a simple [3D platformer experience](https://www.roblox.com/games/14238807008) where players collect coins to trade for jump power. Players jump to navigate progressively taller platforms until they reach a flare on top of the highest platform. This course is intended for readers who are familiar with general coding concepts but are new to Roblox. If you need help learning how to code, try the [absolute basics](/docs/en-us/first-experience.md) of working in Studio and [coding fundamentals](/docs/en-us/fundamentals/coding-1/coding-fundamentals.md). ## Course contents The core curriculum is split into three chapters, each with a corresponding place file. You can go through the steps for each chapter or inspect the place files if you want to see the end result of each chapter. **#### Chapter 1 - Build a greybox** - [Create a project](/docs/en-us/tutorials/curriculums/building/create-a-project.md) - Learn how to create a `.rbxl` file that represents your experience on the Roblox platform. - [Create an environment with terrain](/docs/en-us/tutorials/curriculums/building/create-an-environment-with-terrain.md) - Learn how to create the island where players spawn using Studio's terrain tools. - [Greybox a playable area](/docs/en-us/tutorials/curriculums/building/greybox-a-playable-area.md) - Learn how to use solid modeling tools to plan out the basic shape of your platforms. **#### Chapter 2 - Script the gameplay** - [Create a coin collection mechanic](/docs/en-us/tutorials/curriculums/scripting/script-game-behavior.md) - Learn how to track and store players' coin collecting. - [Record and display player data](/docs/en-us/tutorials/curriculums/scripting/record-and-display-player-data.md) - Learn how to store, retrieve, and display individual player data visually through a leaderboard. - [Create player hazards](/docs/en-us/tutorials/curriculums/scripting/create-player-hazards.md) - Learn how to create a hazard in the water by modifying player behavior and creating a player life cycle. - [Script an upgrade button](/docs/en-us/tutorials/curriculums/scripting/script-an-upgrade-button.md) - Learn how to upgrade player jump power by communicating with the Roblox server and handling GUI interactions. **#### Chapter 3 - Polish the experience** - [Create basic visual effects](/docs/en-us/tutorials/curriculums/building/create-basic-visual-effects.md) - Learn how to create two different kinds of visual effect using particle emitters. - [Customize global lighting](/docs/en-us/tutorials/curriculums/building/customize-global-lighting.md) - Learn how to use global lighting settings to refine the look and feel of your experience. - [Apply polished assets](/docs/en-us/tutorials/curriculums/building/apply-polished-assets.md) - Learn how to finish your scene by replacing simple parts with complex, imported models. ## Why build with Assistant? Unlike many game development workflows that rely on multiple third-party tools, Roblox provides the engine, networking, asset marketplace, publishing pipeline, and AI-powered Assistant in a single platform. This allows you to build, test, and publish a multiplayer game without integrating additional tools or services. Assistant runs inside Studio, so it can create instances, configure properties, and generate scripts in your project. This means you can complete common development tasks without switching between Studio and external tools. For creators who are new to Roblox, Assistant can help reduce the amount of Studio functionality you need to learn before you can start building games. Throughout this curriculum, you'll find **Build with Assistant** and **Build it Yourself** options. You can follow either workflow. Completing the manual steps can help you understand how Studio works, while using Assistant can help you complete routine tasks more quickly and efficiently. | Assistant can help with | You'll still want to do yourself | | --- | --- | | Creating and organizing instances such as folders, models, and parts | Sculpting terrain and building environments | | Configuring properties and positioning objects according to specific requirements | Choosing colors, lighting, and visual style | | Generating boilerplate code, including event handlers, leaderboards, RemoteFunctions, and GUI logic | Designing gameplay and deciding where objects should be placed | | Following detailed tutorial instructions without repetitive copy-pasting | Playtesting, balancing, and refining the player experience | --- title: "Create player hazards" url: /docs/en-us/tutorials/curriculums/core/scripting/create-player-hazards last_updated: 2026-06-29T19:34:12Z description: "Explains how to create player hazards by modifying player behavior and creating a player life cycle." --- # Create player hazards Right now, players can fall off any platform with no consequence. This section walks you through creating a large invisible hazard at the same height as the water in your game. When a player falls into the hazard, their health is reduced to zero and they respawn at the start of the game. ## Create a basic water hazard #### Build with Assistant ```text In Workspace > World, create a new folder named Hazards. Inside the Hazards folder, create a block Part named Hazard with Size 825, 1, 576 and CFrame.Position 174, -6.5, 38 so the part covers the water around the island and platforms. Set Transparency to 1 so the part is invisible and the actual water appears to be the hazard, disable CanCollide so players fall through it into the water, and enable Anchored so physics doesn't move it. ``` #### Build it Yourself 1. In the **Explorer**, hover over the **World** model, click **⊕**, and insert a new **Folder**. Rename it `Hazards`. 2. Inside the `Hazards` folder, insert a **block** part and rename it `Hazard`. Use the **Move** and **Scale** tools to flatten and stretch the part so it covers the water around the island and platforms. The sample uses **Size** = `825, 1, 576` and **CFrame.Position** = `174, -6.5, 38`.![A far out view of all of the cylinder sea stacks and the island. A large block part covers the water where a player could land if they fell from a sea stack.](../../../../assets/tutorials/create-player-hazards/Hazard-Part-On-Water.jpg) 3. With the part still selected, in the **Properties** window: 1. Set **Transparency** to `1` so the part is invisible — the actual water below is what the player sees as the hazard. 2. Disable **CanCollide** so players fall through the part and into the water instead of standing on the part. 3. Enable **Anchored** so physics doesn't push the part around. ## Detect when players touch the hazard The hazard doesn't do anything until you write a script that listens `Touched` and lowers the player's health to zero. #### Build with Assistant ```text In ServerScriptService, create a Script named HazardService with the following code: \`\`\`lua local Players = game:GetService("Players") local Workspace = game:GetService("Workspace") local hazardsFolder = Workspace.World.Hazards local hazards = hazardsFolder:GetChildren() local function onHazardTouched(otherPart) local character = otherPart.Parent local player = Players:GetPlayerFromCharacter(character) if player then local humanoid = character:FindFirstChildWhichIsA("Humanoid") if humanoid then humanoid.Health = 0 end end end for _, hazard in hazards do hazard.Touched:Connect(onHazardTouched) end \`\`\` ``` #### Build it Yourself 1. In the **Explorer**, hover over **ServerScriptService**, click **⊕**, and insert a new **Script**. Rename it `HazardService`. Put it in `ServerScriptService` to keep the code on the server, where players can't read or make changes to it. 2. Replace the default code with the following code to loop through every part in the `Hazards` folder and set the player's health to zero when they touch the hazard: ```lua local Players = game:GetService("Players") local Workspace = game:GetService("Workspace") local hazardsFolder = Workspace.World.Hazards local hazards = hazardsFolder:GetChildren() local function onHazardTouched(otherPart) local character = otherPart.Parent local player = Players:GetPlayerFromCharacter(character) if player then local humanoid = character:FindFirstChildWhichIsA("Humanoid") if humanoid then humanoid.Health = 0 end end end for _, hazard in hazards do hazard.Touched:Connect(onHazardTouched) end ``` **#### Code explanation** The **HazardService** has many similarities to **CoinService**. However, instead of collecting a coin, the player has their **health set to 0** when they touch a hazard. Feel free to modify, add, or remove hazard objects in your game to create unique obstacles. As long as they are contained in the **Hazards** folder, the code loop connects the event handler to all of your hazards. ## Connect to the player lifecycle The player lifecycle represents events that occur when players interact in your game, such as joining, leaving, or respawning. You need to connect handlers to these events to execute logic for each major lifecycle event. #### Build with Assistant ```text In ServerScriptService, append the following code to the bottom of the existing CoinService Script (don't replace any of the existing code — add this after it): \`\`\`lua local function onPlayerAdded(player) -- Reset player coins to 0 updatePlayerCoins(player, function(_) return 0 end) player.CharacterAdded:Connect(function(character) -- WaitForChild would stop the player loop, so below should be done in a separate thread task.spawn(function() -- When a player dies character:WaitForChild("Humanoid").Died:Connect(function() -- Reset player coins to 0 updatePlayerCoins(player, function(_) return 0 end) end) end) end) end -- Initialize any players added before connecting to PlayerAdded event for _, player in Players:GetPlayers() do onPlayerAdded(player) end local function onPlayerRemoved(player) updatePlayerCoins(player, function(_) return nil end) end Players.PlayerAdded:Connect(onPlayerAdded) Players.PlayerRemoving:Connect(onPlayerRemoved) \`\`\` ``` #### Build it Yourself 1. In the **Explorer**, open the `CoinService` script inside **ServerScriptService**. 2. Scroll to the bottom of the file (don't replace the existing coin-collection code) and add the following lifecycle handlers to reset a player's coin count when they join the game and when they die: ```lua local function onPlayerAdded(player) -- Reset player coins to 0 updatePlayerCoins(player, function(_) return 0 end) player.CharacterAdded:Connect(function(character) -- WaitForChild would stop the player loop, so below should be done in a separate thread task.spawn(function() -- When a player dies character:WaitForChild("Humanoid").Died:Connect(function() -- Reset player coins to 0 updatePlayerCoins(player, function(_) return 0 end) end) end) end) end -- Initialize any players added before connecting to PlayerAdded event for _, player in Players:GetPlayers() do onPlayerAdded(player) end local function onPlayerRemoved(player) updatePlayerCoins(player, function(_) return nil end) end Players.PlayerAdded:Connect(onPlayerAdded) Players.PlayerRemoving:Connect(onPlayerRemoved) ``` **#### Code explanation** The code defines functions to reset coin counts during the appropriate lifecycle events: - `Class.Players.PlayerAdded` fires when a player joins the game, and sets the coin count to `0`. - `Class.Player.CharacterAdded` fires when a player's character model is added to the world. It occurs after `Class.Players.PlayerAdded|PlayerAdded` and whenever the player respawns. - `Class.Humanoid.Died` fires when a player dies, and sets the coin count to `0`. `Library.task.spawn()` creates a separate thread for handling this, so other aspects of the player life cycle can execute. - `Class.Player.PlayerRemoved` fires when a player leaves the game to clean up player state. - This code contains a potential issue where players could collect coins before the `Players.PlayerAdded` event executes and then have their coin counts reset to zero. To mitigate this issue, consider solutions such as code scheduling or freezing the player's character until initialization finishes. However, these solutions involve more complex scripting concepts that are beyond the scope of this tutorial. ## Playtest Select **Test** from the dropdown and click **Play**. Collect a few coins, then jump into the water. You character should die and respawn at the start, with their coin count reset to `0`. --- title: "Record and display player data" url: /docs/en-us/tutorials/curriculums/core/scripting/record-and-display-player-data last_updated: 2026-06-29T19:34:13Z description: "Explains how to store and retrieve individual player data in an experience and display it visually through a leaderboard." --- # Record and display player data Now that you can detect when a player has collected a coin, you need to count how many coins players have collected and make that amount visible on a leaderboard. ## Create a module script to record coin collection To handle the storage and management of each player's coin collection data, you need to create a `Class.ModuleScript` object to contain a data structure and functions that access coin collection data for every player. Module scripts are reusable code that other scripts can require. In this case, the CoinService requires this module script so that it can update coin collection data when players touch coins. #### Build with Assistant ```text In ServerStorage, create a ModuleScript named PlayerData with the following code: \`\`\`lua local PlayerData = {} PlayerData.COIN_KEY_NAME = "Coins" local playerData = { --[[ [userId: string] = { ["Coins"] = coinAmount: number } ]] } local function defaultPlayerData() return { [PlayerData.COIN_KEY_NAME] = 0 } end local function getData(player) local data = playerData[tostring(player.UserId)] or defaultPlayerData() playerData[tostring(player.UserId)] = data return data end function PlayerData.getValue(player, key) return getData(player)[key] end function PlayerData.updateValue(player, key, updateFunction) local data = getData(player) local oldValue = data[key] local newValue = updateFunction(oldValue) data[key] = newValue return newValue end return PlayerData \`\`\` ``` #### Build it Yourself 1. In the **Explorer**, hover over **ServerStorage**, click **⊕**, and insert a **ModuleScript**. Rename it **PlayerData**. You should place the module script into **ServerStorage** to manage the coin collection logic on the server. 2. Replace the default code with the following code to define the table that maps each player's ID to their data: ```lua local PlayerData = {} PlayerData.COIN_KEY_NAME = "Coins" local playerData = { --[[ [userId: string] = { ["Coins"] = coinAmount: number } ]] } local function defaultPlayerData() return { [PlayerData.COIN_KEY_NAME] = 0 } end local function getData(player) local data = playerData[tostring(player.UserId)] or defaultPlayerData() playerData[tostring(player.UserId)] = data return data end function PlayerData.getValue(player, key) return getData(player)[key] end function PlayerData.updateValue(player, key, updateFunction) local data = getData(player) local oldValue = data[key] local newValue = updateFunction(oldValue) data[key] = newValue return newValue end return PlayerData ``` **#### Code explanation** The module script defines a `PlayerData` table that contains zero or many `playerData` tables, which represent coin collection data for a player. Every script that requires this module script receives the same copy of the `PlayerData` table, allowing multiple scripts to modify and share coin collection data. #### Declare the data structures The module script begins with a declaration of an empty table, `PlayerData`, which is returned at the end of the script. It also contains accessor methods to get and set values in the table. The `playerData` table contains comments that describe the structure of the table, which makes the code easier to understand. In this case, a `playerData` table contains a `userId` and a corresponding field named `Coins` that represents the amount of collected coins for that player. ```lua local PlayerData = {} PlayerData.COIN_KEY_NAME = "Coins" local playerData = { --[[ [userId: string] = { ["Coins"] = coinAmount: number } ]] } ... return PlayerData ``` #### Define a local data accessor `getData()` is a local function that retrieves data for a specific `playerData` table. If a player hasn't collected a coin, it returns a table from the `defaultPlayerData()` function to ensure every player has some data associated with them. A common convention is to create simple, public-facing functions that offload logic to locally-scoped functions that do the heavy lifting. ```lua local function defaultPlayerData() return { [PlayerData.COIN_KEY_NAME] = 0 } end local function getData(player) local data = playerData[tostring(player.UserId)] or defaultPlayerData() playerData[tostring(player.UserId)] = data return data end ``` #### Define public data accessors `getValue()` and `updateValue()` are public-facing functions that other scripts that require this module script can call. In our case, the **CoinService** uses these functions to update a player's coin collection data whenever that player touches a coin. ```lua function PlayerData.getValue(player, key) return getData(player)[key] end function PlayerData.updateValue(player, key, updateFunction) local data = getData(player) local oldValue = data[key] local newValue = updateFunction(oldValue) data[key] = newValue return newValue end ``` ## Implement a leaderboard You can represent the coin collection data visually with an on-screen leaderboard. Roblox includes a built-in system that automatically generates a leaderboard using a default UI. #### Build with Assistant ```text In ServerStorage, create a ModuleScript named Leaderboard with the following code: \`\`\`lua local Leaderboard = {} -- Creating a new leaderboard local function setupLeaderboard(player) local leaderstats = Instance.new("Folder") -- 'leaderstats' is a reserved name Roblox recognizes for creating a leaderboard leaderstats.Name = "leaderstats" leaderstats.Parent = player return leaderstats end -- Creating a new leaderboard stat value local function setupStat(leaderstats, statName) local stat = Instance.new("IntValue") stat.Name = statName stat.Value = 0 stat.Parent = leaderstats return stat end -- Updating a player's stat value function Leaderboard.setStat(player, statName, value) local leaderstats = player:FindFirstChild("leaderstats") if not leaderstats then leaderstats = setupLeaderboard(player) end local stat = leaderstats:FindFirstChild(statName) if not stat then stat = setupStat(leaderstats, statName) end stat.Value = value end return Leaderboard \`\`\` ``` #### Build it Yourself 1. In the **Explorer**, insert another **ModuleScript** into **ServerStorage** and rename it `Leaderboard`. This one sits next to `PlayerData` so `CoinService` can `require` both from the same place. 2. Replace the default code with the following code to create the folder and the value object on the first call, and then update the value on every call after: ```lua local Leaderboard = {} -- Creating a new leaderboard local function setupLeaderboard(player) local leaderstats = Instance.new("Folder") -- 'leaderstats' is a reserved name Roblox recognizes for creating a leaderboard leaderstats.Name = "leaderstats" leaderstats.Parent = player return leaderstats end -- Creating a new leaderboard stat value local function setupStat(leaderstats, statName) local stat = Instance.new("IntValue") stat.Name = statName stat.Value = 0 stat.Parent = leaderstats return stat end -- Updating a player's stat value function Leaderboard.setStat(player, statName, value) local leaderstats = player:FindFirstChild("leaderstats") if not leaderstats then leaderstats = setupLeaderboard(player) end local stat = leaderstats:FindFirstChild(statName) if not stat then stat = setupStat(leaderstats, statName) end stat.Value = value end return Leaderboard ``` **#### Code explanation** The following sections describe how the leaderboard works in more detail. #### Create a leaderboard The `setupLeaderboard()` function creates a new folder instance named `leaderstats` and sets it as a child of the specified player. Roblox automatically recognizes a folder named `leaderstats` as a container of stats and creates a UI element to display the stats. It requires the values in `leaderstats` to be stored as "value" objects (such as `Class.StringValue`, `Class.IntValue` or `Class.NumberValue`). ```lua -- Creating a new leaderboard local function setupLeaderboard(player) local leaderstats = Instance.new("Folder") -- 'leaderstats' is a reserved name Roblox recognizes for creating a leaderboard leaderstats.Name = "leaderstats" leaderstats.Parent = player return leaderstats end -- Creating a new leaderboard stat value local function setupStat(leaderstats, statName) local stat = Instance.new("IntValue") stat.Name = statName stat.Value = 0 stat.Parent = leaderstats return stat end ``` #### Update player stats `setStat()` is the only public function in the **Leaderboard** module. It creates stat values for a specified player or the leaderboard itself if it doesn't already exist. `Class.Instance:FindFirstChild()|FindFirstChild()` takes the name of an object and returns the object if it exists, or `nil` if it doesn't. It's a common, safe method of finding out if an object exists before you use it. ```lua -- Updating a player's stat value function Leaderboard.setStat(player, statName, value) local leaderstats = player:FindFirstChild("leaderstats") if not leaderstats then leaderstats = setupLeaderboard(player) end local stat = leaderstats:FindFirstChild(statName) if not stat then stat = setupStat(leaderstats, statName) end stat.Value = value end ``` ## Integrate the module scripts With both modules in place, require them in the `CoinService` script to manage and display player coin data. #### Build with Assistant ```text In ServerScriptService, replace the existing CoinService Script's code with the following: \`\`\`lua -- Initializing services and variables local Workspace = game:GetService("Workspace") local Players = game:GetService("Players") local ServerStorage = game:GetService("ServerStorage") -- Modules local Leaderboard = require(ServerStorage.Leaderboard) local PlayerData = require(ServerStorage.PlayerData) local coinsFolder = Workspace.World.Coins local coins = coinsFolder:GetChildren() local COIN_KEY_NAME = PlayerData.COIN_KEY_NAME local COOLDOWN = 10 local COIN_AMOUNT_TO_ADD = 1 local function updatePlayerCoins(player, updateFunction) -- Update the coin table local newCoinAmount = PlayerData.updateValue(player, COIN_KEY_NAME, updateFunction) -- Update the coin leaderboard Leaderboard.setStat(player, COIN_KEY_NAME, newCoinAmount) end -- Defining the event handler local function onCoinTouched(otherPart, coin) if coin:GetAttribute("Enabled") then local character = otherPart.Parent local player = Players:GetPlayerFromCharacter(character) if player then -- Player touched a coin coin.Transparency = 1 coin:SetAttribute("Enabled", false) updatePlayerCoins(player, function(oldCoinAmount) oldCoinAmount = oldCoinAmount or 0 return oldCoinAmount + COIN_AMOUNT_TO_ADD end) task.wait(COOLDOWN) coin.Transparency = 0 coin:SetAttribute("Enabled", true) end end end -- Setting up event listeners for _, coin in coins do coin:SetAttribute("Enabled", true) coin.Touched:Connect(function(otherPart) onCoinTouched(otherPart, coin) end) end \`\`\` ``` #### Build it Yourself 1. In the **Explorer**, open the existing `CoinService` script inside **ServerScriptService**. 2. Replace the code with the following code to update `PlayerData` and the leaderboard in a single step: ```lua -- Initializing services and variables local Workspace = game:GetService("Workspace") local Players = game:GetService("Players") local ServerStorage = game:GetService("ServerStorage") -- Modules local Leaderboard = require(ServerStorage.Leaderboard) local PlayerData = require(ServerStorage.PlayerData) local coinsFolder = Workspace.World.Coins local coins = coinsFolder:GetChildren() local COIN_KEY_NAME = PlayerData.COIN_KEY_NAME local COOLDOWN = 10 local COIN_AMOUNT_TO_ADD = 1 local function updatePlayerCoins(player, updateFunction) -- Update the coin table local newCoinAmount = PlayerData.updateValue(player, COIN_KEY_NAME, updateFunction) -- Update the coin leaderboard Leaderboard.setStat(player, COIN_KEY_NAME, newCoinAmount) end -- Defining the event handler local function onCoinTouched(otherPart, coin) if coin:GetAttribute("Enabled") then local character = otherPart.Parent local player = Players:GetPlayerFromCharacter(character) if player then -- Player touched a coin coin.Transparency = 1 coin:SetAttribute("Enabled", false) updatePlayerCoins(player, function(oldCoinAmount) oldCoinAmount = oldCoinAmount or 0 return oldCoinAmount + COIN_AMOUNT_TO_ADD end) task.wait(COOLDOWN) coin.Transparency = 0 coin:SetAttribute("Enabled", true) end end end -- Setting up event listeners for _, coin in coins do coin:SetAttribute("Enabled", true) coin.Touched:Connect(function(otherPart) onCoinTouched(otherPart, coin) end) end ``` **#### Code explanation** The changes to the original **CoinService** script include: - Importing the **PlayerData** and **Leaderboard** modules with the `Global.LuaGlobals.require()` function. - Declaring `COIN_AMOUNT_TO_ADD` as the number of coins to add when a player collects a coin, and `COIN_KEY_NAME` as the key name defined in **PlayerData**. - Creating the helper function `updatePlayerCoins()` to update the player's coin count and associated leaderboard stat. - Replacing the placeholder `print()` statement in `onCoinTouched()` with a call to `updatePlayerCoins()`. ## Playtest Select **Test** from the dropdown and click **Play**. Walk into a coin and confirm that the leaderboard appears in the corner and that your coin count increases as you collect more coins. --- title: "Script an upgrade button" url: /docs/en-us/tutorials/curriculums/core/scripting/script-an-upgrade-button last_updated: 2026-06-29T19:34:13Z description: "Explains how to communicate with the Roblox server and handle GUI interactions." --- # Script an upgrade button Players can now collect coins and lose them when they die, but the coins don't do anything, and most of the 3D world is inaccessible without the ability to jump very high. This section of the tutorial teaches you how to finish the logic for your experience by adding an on-screen button that spends coins to increase jumping power. ## Create the upgrade button 2D interfaces in Roblox are typically made up of a collection of GUI components inside of a GUI container. In this case, you only need a `Class.TextButton` component that says Upgrade Jump (5 Coins) inside a `Class.ScreenGui` container. #### Build with Assistant ```text In ReplicatedStorage, create a folder named Instances. Inside the Instances folder, create a ScreenGui named JumpPurchaseGui with ResetOnSpawn disabled. Inside JumpPurchaseGui, create a TextButton named JumpButton with the following properties: - Text: "Upgrade Jump (5 Coins)" - TextSize: 25 - AnchorPoint: 1, 1 - Position: {1, 0}, {1, 0} Also inside the Instances folder, create a RemoteFunction named IncreaseJumpPowerFunction. Finally, on the StarterPlayer service, enable the CharacterUseJumpPower property. ``` #### Build it Yourself For the upgrade button, you'll create a folder inside **ReplicatedStorage** to hold the upgrade system, including a **ScreenGui** that contains the button and a **RemoteFunction** that clients use to request upgrades from the server. Storing these objects in **ReplicatedStorage** makes them accessible to both clients and the server. 1. In the **Explorer**, hover over **ReplicatedStorage**, click **⊕**, and insert a folder. Rename it **Instances**. This is where you'll keep shared objects that the client and server both need. 2. Insert a **ScreenGui** into the **Instances** folder. In the **Properties** window: 1. Set **Name** to **JumpPurchaseGui**. 2. Disable **ResetOnSpawn** so the GUI doesn't get wiped when the player respawns. 3. Insert a **TextButton** inside **JumpPurchaseGui** and rename it **JumpButton**. In the **Properties** window: 1. Set **Text** to **Upgrade Jump (5 Coins)**. 2. Set **TextSize** to `25`. 3. Set **AnchorPoint** to `1, 1`. 4. Set **Position** to `{1, 0},{1, 0}`. 4. Back in the **Instances** folder, insert a **RemoteFunction** and rename it **IncreaseJumpPowerFunction**. Remote functions need to live in `ReplicatedStorage` so the client and server can both reference them.![Studio's Explorer window with the IncreaseJumpPowerFunction script highlighted under the Instances folder.](../../../../assets/tutorials/script-an-upgrade-button/Insert-RemoteFunction.png) 5. Select **StarterPlayer** in the Explorer, then in the **Properties** window enable **CharacterUseJumpPower**. Without this, jump power values are ignored and every character jumps at a fixed height. ## Define jump power data Currently, only coin count is stored for each player in the **PlayerData** module script. You need to also store and update jump power in the same way. Because the functions in **PlayerData** are non-specific to the data being changed, all that's required to store player jump power is to add a `Jump` key and initialize its initial value in `defaultPlayerData()`. #### Build with Assistant ```text In ServerStorage, replace the existing PlayerData ModuleScript's code with the following: \`\`\`lua local PlayerData = {} PlayerData.COIN_KEY_NAME = "Coins" PlayerData.JUMP_KEY_NAME = "Jump" local playerData = { --[[ [userId: string] = { ["Coins"] = coinAmount: number, ["Jump"] = jumpPower: number } --]] } local function defaultPlayerData() return { [PlayerData.COIN_KEY_NAME] = 0, [PlayerData.JUMP_KEY_NAME] = 0, } end local function getData(player) local data = playerData[tostring(player.UserId)] or defaultPlayerData() playerData[tostring(player.UserId)] = data return data end function PlayerData.getValue(player, key) return getData(player)[key] end function PlayerData.updateValue(player, key, updateFunction) local data = getData(player) local oldValue = data[key] local newValue = updateFunction(oldValue) data[key] = newValue return newValue end return PlayerData \`\`\` ``` #### Build it Yourself 1. In the **Explorer**, open the **PlayerData** ModuleScript inside **ServerStorage**. 2. Replace its code with the following code to add a `JUMP_KEY_NAME` constant and initialize `Jump` to `0` so that every new player starts with zero jump power: ```lua local PlayerData = {} PlayerData.COIN_KEY_NAME = "Coins" PlayerData.JUMP_KEY_NAME = "Jump" local playerData = { --[[ [userId: string] = { ["Coins"] = coinAmount: number, ["Jump"] = jumpPower: number } --]] } local function defaultPlayerData() return { [PlayerData.COIN_KEY_NAME] = 0, [PlayerData.JUMP_KEY_NAME] = 0, } end local function getData(player) local data = playerData[tostring(player.UserId)] or defaultPlayerData() playerData[tostring(player.UserId)] = data return data end function PlayerData.getValue(player, key) return getData(player)[key] end function PlayerData.updateValue(player, key, updateFunction) local data = getData(player) local oldValue = data[key] local newValue = updateFunction(oldValue) data[key] = newValue return newValue end return PlayerData ``` ## Update jump power data Now that **PlayerData** is able to track jump power, you need to implement logic on the server to upgrade jump power from a player's client request. The server and client can communicate through either `Class.RemoteEvent|RemoteEvents` or `Class.RemoteFunction|RemoteFunctions`. Remote events don't yield when they're fired and can be used for one-way communication. Remote functions, on the other hand, yield until they receive a reply, which allows for two-way communication. For this tutorial, the client needs to know if the server upgraded the player's jump power successfully, so you should use a remote function. #### Build with Assistant ```text In ServerScriptService, create a Script named JumpService with the following code: \`\`\`lua -- Services local ReplicatedStorage = game:GetService("ReplicatedStorage") local ServerStorage = game:GetService("ServerStorage") local Players = game:GetService("Players") -- Modules local Leaderboard = require(ServerStorage.Leaderboard) local PlayerData = require(ServerStorage.PlayerData) -- Events local IncreaseJumpPowerFunction = ReplicatedStorage.Instances.IncreaseJumpPowerFunction local JUMP_KEY_NAME = PlayerData.JUMP_KEY_NAME local COIN_KEY_NAME = PlayerData.COIN_KEY_NAME local JUMP_POWER_INCREMENT = 30 local JUMP_COIN_COST = 5 local function updateJumpPower(player, updateFunction) -- Update the jump power table local newJumpPower = PlayerData.updateValue(player, JUMP_KEY_NAME, updateFunction) -- Update the players jump power local character = player.Character or player.CharacterAdded:Wait() local humanoid = character:FindFirstChildWhichIsA("Humanoid") if humanoid then humanoid.JumpPower = newJumpPower -- Update the jump leaderboard Leaderboard.setStat(player, JUMP_KEY_NAME, newJumpPower) end end local function onPurchaseJumpIncrease(player) local coinAmount = PlayerData.getValue(player, COIN_KEY_NAME) if coinAmount < JUMP_COIN_COST then return false end -- Increase player's jump power updateJumpPower(player, function(oldJumpPower) oldJumpPower = oldJumpPower or 0 return oldJumpPower + JUMP_POWER_INCREMENT end) -- Update the coin table local newCoinAmount = PlayerData.updateValue(player, COIN_KEY_NAME, function(oldCoinAmount) return oldCoinAmount - JUMP_COIN_COST end) -- Update the coin leaderboard Leaderboard.setStat(player, COIN_KEY_NAME, newCoinAmount) return true end local function onCharacterAdded(player) -- Reset player's jump power when the character is added updateJumpPower(player, function(_) return 0 end) end -- Initialize any players added before connecting to PlayerAdded event for _, player in Players:GetPlayers() do onCharacterAdded(player) end -- Normal initialization of players from PlayerAdded event local function onPlayerAdded(player) player.CharacterAdded:Connect(function() onCharacterAdded(player) end) end local function onPlayerRemoved(player) updateJumpPower(player, function(_) return nil end) end IncreaseJumpPowerFunction.OnServerInvoke = onPurchaseJumpIncrease Players.PlayerAdded:Connect(onPlayerAdded) Players.PlayerRemoving:Connect(onPlayerRemoved) \`\`\` ``` #### Build it Yourself 1. In the **Explorer**, insert a new **Script** into **ServerScriptService** and rename it **JumpService**. This script holds the server-side logic for the jump upgrade: it receives the request, charges coins, and updates the character's **JumpPower**. 2. Replace the default code with the following: ```lua -- Services local ReplicatedStorage = game:GetService("ReplicatedStorage") local ServerStorage = game:GetService("ServerStorage") local Players = game:GetService("Players") -- Modules local Leaderboard = require(ServerStorage.Leaderboard) local PlayerData = require(ServerStorage.PlayerData) -- Events local IncreaseJumpPowerFunction = ReplicatedStorage.Instances.IncreaseJumpPowerFunction local JUMP_KEY_NAME = PlayerData.JUMP_KEY_NAME local COIN_KEY_NAME = PlayerData.COIN_KEY_NAME local JUMP_POWER_INCREMENT = 30 local JUMP_COIN_COST = 5 local function updateJumpPower(player, updateFunction) -- Update the jump power table local newJumpPower = PlayerData.updateValue(player, JUMP_KEY_NAME, updateFunction) -- Update the players jump power local character = player.Character or player.CharacterAdded:Wait() local humanoid = character:FindFirstChildWhichIsA("Humanoid") if humanoid then humanoid.JumpPower = newJumpPower -- Update the jump leaderboard Leaderboard.setStat(player, JUMP_KEY_NAME, newJumpPower) end end local function onPurchaseJumpIncrease(player) local coinAmount = PlayerData.getValue(player, COIN_KEY_NAME) if coinAmount < JUMP_COIN_COST then return false end -- Increase player's jump power updateJumpPower(player, function(oldJumpPower) oldJumpPower = oldJumpPower or 0 return oldJumpPower + JUMP_POWER_INCREMENT end) -- Update the coin table local newCoinAmount = PlayerData.updateValue(player, COIN_KEY_NAME, function(oldCoinAmount) return oldCoinAmount - JUMP_COIN_COST end) -- Update the coin leaderboard Leaderboard.setStat(player, COIN_KEY_NAME, newCoinAmount) return true end local function onCharacterAdded(player) -- Reset player's jump power when the character is added updateJumpPower(player, function(_) return 0 end) end -- Initialize any players added before connecting to PlayerAdded event for _, player in Players:GetPlayers() do onCharacterAdded(player) end -- Normal initialization of players from PlayerAdded event local function onPlayerAdded(player) player.CharacterAdded:Connect(function() onCharacterAdded(player) end) end local function onPlayerRemoved(player) updateJumpPower(player, function(_) return nil end) end IncreaseJumpPowerFunction.OnServerInvoke = onPurchaseJumpIncrease Players.PlayerAdded:Connect(onPlayerAdded) Players.PlayerRemoving:Connect(onPlayerRemoved) ``` **#### Code explanation** The following sections describe the code in more detail. - **Update the jump power data** - `updateJumpPower()` updates the jump power of the player and the leaderboard to provide visual feedback. This function resembles the code that damages players in [Create player hazards](/docs/en-us/tutorials/curriculums/core/scripting/create-player-hazards.md). Provided a `Class.Player.Character|Character` model and `Class.Humanoid` exist for the player being upgraded, the function updates the `Class.Humanoid.JumpPower|JumpPower` property to the new value stored by **PlayerData**, increasing it by 30. If you want your game to last slightly longer, you can decrease this number. - **Validate server requests** - `onPurchaseJumpIncrease()` first checks that the player actually has the number of coins required to purchase the upgrade. **All requests** from clients to the server should be **validated** to prevent bad actors submitting false requests and exploiting your game. ## Add the button to the player GUI A `Class.ScreenGui` object only shows on-screen if it is parented to a player's `Class.PlayerGui` object. By default, this contains the system GUI such as the chat window. You now need to create a script in **ReplicatedStorage** to copy the upgrade button into each player's GUI and implement behavior for when the button is pressed. #### Build with Assistant ```text In ReplicatedStorage, create a Script named JumpButtonClickHandler with RunContext set to Client and the following code: \`\`\`lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local Players = game:GetService("Players") local player = Players.LocalPlayer local playerGui = player.PlayerGui local IncreaseJumpPowerFunction = ReplicatedStorage.Instances.IncreaseJumpPowerFunction local jumpPurchaseGui = ReplicatedStorage.Instances.JumpPurchaseGui local jumpButton = jumpPurchaseGui.JumpButton local function onButtonClicked() local success, purchased = pcall(IncreaseJumpPowerFunction.InvokeServer, IncreaseJumpPowerFunction) if not success then -- purchased will be the error message if success is false error(purchased) elseif success and not purchased then warn("Not enough coins!") end end jumpButton.Activated:Connect(onButtonClicked) -- Add the JumpPurchaseGui to the player's Gui jumpPurchaseGui.Parent = playerGui \`\`\` ``` #### Build it Yourself 1. In the **Explorer**, insert a new **Script** into **ReplicatedStorage**. 2. With the script selected, in the **Properties** window: 1. Set **Name** to **JumpButtonClickHandler**. 2. Set **RunContext** to **Client**. This tells the engine to run always run this script on the client to optimize network communication. 3. Replace the default code with the following: ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local Players = game:GetService("Players") local player = Players.LocalPlayer local playerGui = player.PlayerGui local IncreaseJumpPowerFunction = ReplicatedStorage.Instances.IncreaseJumpPowerFunction local jumpPurchaseGui = ReplicatedStorage.Instances.JumpPurchaseGui local jumpButton = jumpPurchaseGui.JumpButton local function onButtonClicked() local success, purchased = pcall(IncreaseJumpPowerFunction.InvokeServer, IncreaseJumpPowerFunction) if not success then -- purchased will be the error message if success is false error(purchased) elseif success and not purchased then warn("Not enough coins!") end end jumpButton.Activated:Connect(onButtonClicked) -- Add the JumpPurchaseGui to the player's Gui jumpPurchaseGui.Parent = playerGui ``` **#### Code explanation** The following sections describe the code in more detail. - **Obtain references to the GUI and server function** - The variables `IncreaseJumpPowerFunction`, `jumpPurchaseGui`, and `jumpButton` contain references to the function and GUI that calls the function that you'll need later. - **Define the event handler** - `onButtonClicked()` defines logic for when users click the upgrade button. It uses `Global.LuaGlobals.pcall|pcall()` (protected call) to invoke the `Class.RemoteFunction`. Any client-server communication like this requires `Global.LuaGlobals.pcall|pcall()` to handle errors or connection issues. - **Connect the handler to the button** - The `Class.GuiButton.Activated|Activated` event is compatible on all platforms, including mouse, touchscreen, or gamepad contexts. It triggers when a **click**, **touch**, or **gamepad button** is released. ## Playtest Select **Test** from the dropdown and click **Play**. The **Upgrade Jump** button shows up in the bottom-right corner. Try clicking the button before you collect any coins to check that it doesn't award you additional jumping power, then try collecting some coins and see if the upgrade works when you click again. With the code complete, try tuning the difficulty: add coins if the pace feels slow, or remove a few and tuck them in trickier spots if upgrades come too easily. --- title: "Create a coin collection mechanic" url: /docs/en-us/tutorials/curriculums/core/scripting/script-game-behavior last_updated: 2026-06-29T19:34:13Z description: "Explains how to implement event-driven logic in an experience." --- # Create a coin collection mechanic Now that you have a 3D world, it's time to add your first script. In this section, you'll create a coin collection mechanic that lets players collect coins and temporarily disables coins after they've been collected. ## Create the coins Before you can script anything, you need to have placeholder objects in the world to use as your coins. Like the sea stack platforms you made in the previous section, the coins can be simple `Class.Part` objects. #### Build with Assistant ```text In Workspace > World, create a new folder named Coins. Inside the Coins folder, create a cylinder Part named Coin with BrickColor set to Gold, Material set to Metal, Size 0.6, 8, 4, and Orientation 90, 0, 0. Disable CanCollide and enable Anchored. Then duplicate the Coin part several times and scatter the duplicates around the island and on the platforms so they're reachable from the sea stack levels. Keep each duplicate named Coin. ``` #### Build it Yourself 1. In the **Explorer**, insert a new folder inside the existing **World** folder and rename it **Coins**. Grouping all coins in a single folder makes it easier for scripts to find and manage them. 2. Inside the **Coins** folder, insert a **cylinder** part and rename it **Coin**. With the part selected, configure the following properties in the **Properties** window: 1. Set **BrickColor** to `Gold`. 2. Set **Material** to `Metal`. 3. Set **Size** to `0.6, 8, 4`. 4. Set **Orientation** to `90, 0, 0` so the coin lies flat like a disc. 5. Disable **CanCollide** so players can walk through the coin to collect it. 6. Enable **Anchored** so physics doesn't push the coin around.![A close up view of a gold coin next to two gray cylinder sea stacks on the island.](../../../../assets/tutorials/script-game-behavior/Single-Coin-In-Viewport.jpg) 3. Duplicate the coin a few times and scatter the copies around the island and on the platforms so you have somewhere to test pickups. Make sure each duplicate is also named `Coin`.![A view of multiple coins on the island and two gray cylinder sea stacks.](../../../../assets/tutorials/script-game-behavior/Multiple-Coins-In-Level.jpg) ## Create the script Next, you'll make the coins respond to players. The Roblox engine can detect when something touches a coin, but you need to use a script to define how the coin should react. #### Build with Assistant ```text In ServerScriptService, create a Script named CoinService with the following code: \`\`\`lua -- Initializing services and variables local Workspace = game:GetService("Workspace") local Players = game:GetService("Players") local coinsFolder = Workspace.World.Coins local coins = coinsFolder:GetChildren() local COOLDOWN = 10 -- Defining the event handler local function onCoinTouched(otherPart, coin) if coin:GetAttribute("Enabled") then local character = otherPart.Parent local player = Players:GetPlayerFromCharacter(character) if player then -- Player touched a coin coin.Transparency = 1 coin:SetAttribute("Enabled", false) print("Player collected coin") task.wait(COOLDOWN) coin.Transparency = 0 coin:SetAttribute("Enabled", true) end end end -- Setting up event listeners for _, coin in coins do coin:SetAttribute("Enabled", true) coin.Touched:Connect(function(otherPart) onCoinTouched(otherPart, coin) end) end \`\`\` ``` #### Build it Yourself 1. In the **Explorer**, insert a new **Script** inside **ServerScriptService** and rename it **CoinService**. Placing the script in **ServerScriptService** ensures it runs on the server and can't be accessed by clients. 2. Replace the default code with the following script, which retrieves every coin in the **Coins** folder, connects a `Touched` handler to each one, and uses an `Enabled` attribute to track whether a coin can currently be collected: ```lua -- Initializing services and variables local Workspace = game:GetService("Workspace") local Players = game:GetService("Players") local coinsFolder = Workspace.World.Coins local coins = coinsFolder:GetChildren() local COOLDOWN = 10 -- Defining the event handler local function onCoinTouched(otherPart, coin) if coin:GetAttribute("Enabled") then local character = otherPart.Parent local player = Players:GetPlayerFromCharacter(character) if player then -- Player touched a coin coin.Transparency = 1 coin:SetAttribute("Enabled", false) print("Player collected coin") task.wait(COOLDOWN) coin.Transparency = 0 coin:SetAttribute("Enabled", true) end end end -- Setting up event listeners for _, coin in coins do coin:SetAttribute("Enabled", true) coin.Touched:Connect(function(otherPart) onCoinTouched(otherPart, coin) end) end ``` Now, whenever a player touches a coin, the coin disappears for 10 seconds, and the output log prints `Player collected coin`. **#### Code explanation** The following sections describe how the script works in more detail. #### Initialize services and variables Like with a lot of the code you've probably written in other languages, you define variables that you need later at the top of the script. Our code does the following: - **Obtain service instances** - Roblox services provide built-in functionality for common features. The script first obtains instances of the `Class.Workspace` service, which contains every object in the 3D world, and the `Class.Player` service, which manages and contains all players connected to your game. - **Obtain references to all coins** - The script then queries the 3D workspace for all references to coin objects with the `Class.Instance:GetChildren()|GetChildren()` method. This method returns an array containing everything parented to the object it's associated with, which in this case is the `Workspace.World.Coins` folder you created previously. - **Defines a global variable** - The `COOLDOWN` variable is used later to define how long to disable a coin after it's collected. ```lua local Workspace = game:GetService("Workspace") local Players = game:GetService("Players") local coinsFolder = Workspace.World.Coins local coins = coinsFolder:GetChildren() local COOLDOWN = 10 ... ``` #### Define the event handler The Roblox Engine physically simulates the 3D world and handles a lot of the logic to handle events related to rendering, physics, and networking. When you're interested in scripting your own logic during some of these events, you can listen for and handle them, while letting the engine do the rest. In this case, you listen for and handle events related to coins being touched. The script defines the logic for handling this event in the `onCoinTouched()` method, which does the following: - **Detects whether the coin is enabled** - Every `Class.Instance` has an `Enabled` boolean attribute that defines whether or not the object exists in the 3D world. You can get instance attributes with the `Class.Instance:GetAttribute()|GetAttribute()` method. - **Detects whether a player touched the coin** - If a coin is enabled, the method uses the player service to check if the object that touched the coin was indeed a player. When a touch event occurs, the Roblox Engine passes the object that touched the coin as an `otherPart` parameter. The script checks to see if the parent of `otherPart` belongs to a player. - **Disables the coin if a player touched it, and re-enables it after 10 seconds** - Finally, if a player touched the coin, the method disables the coin, waits for 10 seconds, and then re-enables the coin for collection. `Library.task.wait()` is used instead of `wait()` because it provides better performance by not pausing code execution entirely, allowing tasks in other threads to run concurrently. ```lua local function onCoinTouched(otherPart, coin) if coin:GetAttribute("Enabled") then local character = otherPart.Parent local player = Players:GetPlayerFromCharacter(character) if player then -- Player touched a coin coin.Transparency = 1 coin:SetAttribute("Enabled", false) print("Player collected coin") task.wait(COOLDOWN) coin.Transparency = 0 coin:SetAttribute("Enabled", true) end end end ``` #### Connect the event handler All simulated 3D objects inherit from `Class.BasePart` and therefore have a `Class.BasePart.Touched|Touched()` event. The following loop connects the `onTouchedEvent()` handler to every coin's touch event by doing the following: - **Loop through all the coins** - Loop through each of the coins using general iteration. - **Connect the handler to the event** - In each iteration of the loop, the coin is enabled by default, so it's visible in the 3D world during the initial start of the game. The `onCoinTouched()` handler method is also connected to the `Touched` event of the coin so that it runs every time the event occurs. When the engine detects a touch, it also passes in the object that touched the object, `otherPart`. ```lua for _, coin in coins do coin:SetAttribute("Enabled", true) coin.Touched:Connect(function(otherPart) onCoinTouched(otherPart, coin) end) end ``` ## Playtest the mechanic Select **Test** from the dropdown and click **Play**. Move your character to touch a coin. If everything is working, the coin disappears, the [Output](/docs/en-us/studio/output.md) window prints `Player collected coin`, and the coin reappears 10 seconds later. ![Studio's Output window that displays confirmation that the player collected a coin.](../../../../assets/tutorials/script-game-behavior/Output-Collect-Coin.png) --- title: "Advanced curriculum paths" url: /docs/en-us/tutorials/curriculums/curriculum-overview last_updated: 2026-06-29T19:34:12Z description: "An overview of creation curriculum paths covering topics like modeling, scripting, and gameplay logic." --- # Advanced curriculum paths **#### Prerequisites** If you've never used Roblox Studio or coded before, start here before following any of the advanced curriculum paths: - [Studio lesson](/docs/en-us/tutorials/curriculums/building.md) - Install and configure Roblox Studio. - [Create your first experience](/docs/en-us/tutorials/curriculums/core.md) - Build a simple experience. - [Luau basics](/docs/en-us/tutorials/fundamentals/coding-1/coding-fundamentals.md) - Learn the fundamentals of programming with Luau. ## Environmental art Learn the foundational skills you need to become an environmental artist. 1. ##### Plan 2. ##### Construct 3. ##### Optimize ## Gameplay scripting Learn the programming skills you need to introduce gameplay to the laser tag environment. 1. ##### Spawn 2. ##### Blast 3. ##### Detect ## User interface design Learn the design skills you need to implement UI that informs players of the laser tag gameplay. 1. ##### Plan 2. ##### Wireframe 3. ##### Implement --- title: "Assemble an asset library" url: /docs/en-us/tutorials/curriculums/environmental-art/assemble-an-asset-library last_updated: 2026-06-29T19:34:13Z description: "Explains how to import and configure your assets within a central location in your experience." --- # Assemble an asset library **Assembling an asset library** is the process of importing and configuring a collection of assets in a central location within your experience for easy access and reuse. This process can save you significant time as you prepare to construct your environment, especially if you configure each asset's values to enhance performance and optimize memory usage on mid to low-end devices. Using the [Environment Art - Assembling](https://www.roblox.com/games/14447787049/Environment-Art-Assembling) `.rbxl` file as a reference, this section of the environmental art curriculum shows you how to assemble an asset library from your polished assets, including step-by-step instructions on: - Creating custom materials for your terrain from your tileable textures. - Creating SurfaceAppearance objects for your trim sheet, and applying them to unwrapped meshes in your asset library. - Setting rendering parameters for your assets to ensure optimal performance across all devices. - Converting your assets into packages so that you can update and reuse them across all of your projects. After you complete this section, you will learn how to utilize your asset library in creative ways to replace or convert the greybox environment, and add final touches that enhance the overall 3D space. ![The sample laser tag experience's modular and prop kit.](../../../assets/tutorials/environmental-art-curriculum/Section3/Overview.png) > **Info:** Before you begin to compile your asset library by importing your tileable textures, trim sheets, modular assets, and props into Studio, it's best practice to arrange and organize all of your content in a folder structure according to their data type, such as trim sheets, decals, modular assets, and props. This can provide you an ease of reference as you create custom materials and visual treatments. ## Create custom materials Studio represents custom materials as `Class.MaterialVariant` objects within the `Class.MaterialService`. These `Class.MaterialVariant` objects have four properties that combine the four tileable [texture maps](/docs/en-us/art/modeling/surface-appearance.md#texture-maps) to create a high-quality custom material with physical characteristics: - `Class.MaterialVariant.ColorMap` – Represents the albedo texture map. - `Class.MaterialVariant.MetalnessMap` – Represents the metalness texture map. - `Class.MaterialVariant.NormalMap` – Represents the normal texture map. - `Class.MaterialVariant.RoughnessMap` – Represents the roughness texture map. After you supply the texture maps to these properties, you can apply your new custom material to both parts, meshes, and terrain. You can use or modify the sample texture map `.png` files to create the Lumpy Moss, Flowering Lumpy Moss, Lumpy Moss with Stones, and Stones custom materials within the final sample laser tag environment, or you can use your own that you designed in the previous section of the tutorial. To create `Class.MaterialVariant` objects for your tileable textures: 1. From the **Window** ⟩ **3D** menu, select **Material** to open the **Material Manager**. 2. In the **Materials** list, select the base material that most aligns with your tileable texture. For example, if you were creating a `Class.MaterialVariant` object for the Lumpy Moss tileable texture, choose the default ground material so the material inherits its physical properties. 3. In the upper toolbar, click the **⊕** icon. A new `Class.MaterialVariant` displays in the palette with an icon in the bottom-right corner that indicates it's a custom material.![New MaterialVariant tile in Material Manager with icon to indicate a custom material](../../../assets/studio/material-manager/Custom-Material-Icon.png) 4. In the **Inspector**, navigate to the **General** section, then rename the material to align with the name of your tileable texture.![The Inspector view with the name of the material highlighted.](../../../assets/tutorials/environmental-art-curriculum/Section3/CustomMaterials-4.jpg) 5. In the **Texture Maps** section, on the right-hand side of **Color**, click the **Import** button. A file browser displays.![The Texture Maps section of the Inspector view with the Import button highlighted.](../../../assets/tutorials/environmental-art-curriculum/Section3/CustomMaterials-5.jpg) 6. In the file browser, select the **Albedo** texture map `.png` file for the corresponding tileable texture, then the **Open** button. The file browser closes, and the new color map displays with its asset ID. 7. Repeat this process for the **Metalness**, **Normal**, and **Roughness** texture maps. The custom material updates to reflect the texture maps.![All texture maps for the Moss_Lumpy_A material.](../../../assets/tutorials/environmental-art-curriculum/Section3/CustomMaterials-7a.jpg)![ sphere part with the Moss_Lumpy_A material applied.](../../../assets/tutorials/environmental-art-curriculum/Section3/CustomMaterials-7b.jpg) 8. In the **Overrides** section, enable the **Set as Override** toggle to ensure that when you apply ground to your terrain, Studio uses this custom material.![The Set as Override toggle enabled.](../../../assets/tutorials/environmental-art-curriculum/Section3/CustomMaterials-8.jpg) 9. **(Optional)** If you are creating a custom material that represents an organic material, 1. In the **Tiling** section, click the **Pattern** dropdown. 2. Select **Organic** to randomize the output and reduce visible tiling.![The Tiling section of the Inspector view with its settings.](../../../assets/tutorials/environmental-art-curriculum/Section3/CustomMaterials-9.jpg) 10. Repeat this process for each tileable texture you want to include in your experience. ## Create SurfaceAppearance packages Studio utilizes trim sheets within `Class.SurfaceAppearance` objects that you can parent to `Class.MeshPart|MeshParts` that contain UV data. Similar to custom materials, `Class.SurfaceAppearance` objects have four properties that combine the four trim sheet texture maps to create a high-quality 3D visual surface treatment: `Class.SurfaceAppearance.ColorMap` – Represents the albedo texture map. `Class.SurfaceAppearance.MetalnessMap` – Represents the metalness texture map. `Class.SurfaceAppearance.NormalMap` – Represents the normal texture map. `Class.SurfaceAppearance.RoughnessMap` – Represents the roughness texture map. After you supply the texture maps to these properties, you can make the `Class.SurfaceAppearance` object a child of a `Class.MeshPart` with uv data that maps to your trim sheet, and the uv data automatically applies to the parent mesh. It's for this reason that it's useful to make the `Class.SurfaceAppearance` object a [package](/docs/en-us/projects/assets/packages.md) so that you can reuse the same `Class.SurfaceAppearance` object across all of your modular assets and props. For example, the following meshes all have uv data that maps to the same trim sheet layout that's represented within a single `Class.SurfaceAppearance` object package. You can use or modify the sample texture map `.png` files to create a `Class.SurfaceAppearance` object that respects the uv data for the sample modular assets and props within the final sample laser tag environment, or you can use your own that you designed in the previous section of the tutorial that apply to your own modular assets and props. To create `Class.SurfaceAppearance` packages that you can child to your modular kit and props: 1. Insert a **SurfaceAppearance** object into `Class.Workspace`. 2. Select the **SurfaceAppearance** object, then in the **Properties** window, select the **ColorMap** property. A pop-up displays. 3. Click the **Add Image…** button. A file browser displays.![The ColorMap pop-up with the Add Image button highlighted.](../../../assets/tutorials/environmental-art-curriculum/Section3/SAPackages-3.jpg) 4. Select the **Albedo** texture map for the corresponding tileable texture, then the **Open** button. The file browser closes, and the **ColorMap** property updates with a new asset ID. 5. Repeat this process for the **MetalnessMap**, **NormalMap**, and **RoughnessMap** properties, selecting their respective texture maps from the file browser. 6. In the **Explorer** window, right-click the **SurfaceAppearance** object, then select **Convert to Packag**e from the contextual menu. The **Convert to Package** dialog displays. 7. Fill out the **Title** and **Description** fields, set the ownership to yourself or a group, then click the **Submit** button. Once complete, a chain link symbol displays over the SurfaceAppearance object's icon to identify it as a package.![The Surface Appearance icon with a chain link symbol.](../../../assets/tutorials/environmental-art-curriculum/Section3/SAPackages-7.jpg) 8. **(Optional)** Select the **SurfaceAppearance** object's child **PackageLink** object, then in the **Properties** window, enable **AutoUpdate** to automatically update the package if you make any modifications to the **SurfaceAppearance** object. ## Import modular assets and props Studio represents the modular assets and props that you import as `Class.Model` objects with child `Class.MeshPart|MeshParts` for each component of the asset. For example, if you import a wall section with a top trim, bottom trim, and wall component, Studio represents the `.fbx` or `.obj` file as a **Wall_Section** `Class.Model` with separate child **Top_Trim**, **Bottom_Trim**, and **Wall** `Class.MeshPart|MeshParts`. Using the Importer, you can import the sample modular kit and props into Studio for use in your asset library, or you can import any assets you designed in the previous section of the tutorial. For more information on this tool's functionality, see [Importer](/docs/en-us/studio/importer.md). To import your modular assets and props into the experience: 1. From the **File** menu, select **Import**. A file browser displays. 2. Select a `.fbx` file for either a modular asset or prop. 3. Verify the object preview and check that the import settings are correct for your object. 4. Verify any [warning or error messages](/docs/en-us/studio/importer.md#warnings-and-errors). 5. Click **Import**. The asset displays in the **Explorer** window and in the viewport. 6. Repeat this process for each modular asset and prop. 7. Move all of your assets into one area of your experience near your greybox geometry. This zone is your asset library. ## Apply SurfaceAppearance data When you go through the process of unwrapping the UVs of a mesh in third-party modeling software, the software stores the UV data within the mesh's `.fbx` or `.obj` file. When you import that mesh into Studio, the resulting `Class.MeshPart` object retains that data, but you still need to apply a `Class.SurfaceAppearance` object with texture map properties from your trim sheet in order to display your trim textures on the asset. To apply `Class.SurfaceAppearance` texture map data to unwrapped meshes in your asset library: 1. In the **Explorer** window, click your **SurfaceAppearance** package, then press `Ctrl``D` (`⌘``D`) to duplicate it. 2. Child the duplicate **SurfaceAppearance** package to either a modular asset or a prop. The asset applies its UV data to the texture maps and displays its visual treatment. 3. Repeat this process for each modular asset and prop. ## Set physics and rendering parameters Now that you have your assets within Studio, it's important to set physics and rendering parameters that allow the assets to retain the highest possible visual quality across devices with memory and GPU limitations. In general, the more closely you adhere to the guidance in this section, the more performant you make your experience. However, it's important to carefully consider the context of where you plan to place each asset within the overall environment to ensure you maintain your aesthetic goals and gameplay requirements. ### Anchored The `Class.BasePart.Anchored` property determines whether the Roblox Engine's physics system affects the position of the object. When you enable this property for a `Class.Part` or `Class.MeshPart`, it removes the object from the physical calculations of the dynamic simulation that's always running in experience, meaning the object will never change position due to gravity or collision from other objects. Objects that don't simulate a physics response are cheaper to render because they don't take up the GPU that's necessary for those physics calculations. When you free up GPU, you improve performance for your experience, especially on low-end devices that need to save GPU for fast-paced gameplay. It's for this reason that **every** asset aside from doors in the final sample laser tag environment are anchored. ### CanCollide The `Class.BasePart.CanCollide` property determines whether the object can physically interact with other objects. When you enable this property for a `Class.Part` or `Class.MeshPart`, the object is impenetrable, and the Roblox Engine accounts for it in physics calculations. These calculations can impact your experience's performance when there are many objects the engine must consider in its physics calculations. To alleviate some of this impact on performance, disable this property for objects that the user will never interact with. For example, the final sample laser tag environment disables this property for all foliage. ### CanTouch The `Class.BasePart.CanTouch` property determines if `Class.BasePart.Touched|Touched` and `Class.BasePart.TouchEnded|TouchEnded` events fire on the object. When you enable this property for a `Class.Part` or `Class.MeshPart`, the Roblox Engine checks the object's touch event state to see if it needs to trigger or stop an event. This process happens for every single frame, which can take up a significant amount of memory if the engine needs to check the touch event state for many objects at once. To alleviate some of this impact on memory, only enable this property for objects that must trigger an event. ### CanQuery The `Class.BasePart.CanQuery` property determines whether the Roblox Engine considers the object during spatial query operations, such as raycasting. Studio enables this property for every `Class.Part` or `Class.MeshPart` by default, meaning that the engine checks to see if each object needs to call a spatial query operation. This process occurs for every single frame, which can take up a significant amount of memory when the engine needs to make these checks for many objects at once. For this reason, it's recommended to disable this property for objects that the Roblox Engine doesn't need to consider for spatial query operations. When making decisions about where to disable this property, consider how each asset impacts gameplay. For example, the final sample laser tag environment keeps this property enabled for every wall in the building because the engine needs to account for these surfaces as users fire lasers from their laser tag gun. If the engine doesn't account for these assets, then the laser would shoot straight through the building as though it wasn't there at all. ### CastShadow The `Class.BasePart.CastShadow` property determines whether the object casts a shadow. When you enable this property for a `Class.Part` or `Class.MeshPart`, the Roblox Engine calculates every vertex location of the object at runtime, then draws a raycast from the sun to any of its neighboring objects until it collides with another object to create the shadow. These calculations can come at a performance cost, especially when you have many objects of geometric complexity. This is because objects with geometric complexity have more polygons than objects that are geometrically simple, meaning they have more vertices that the engine needs to calculate for the object's shadow. The fewer polygons in an object, the faster the operation and cheaper its shadow. ![A wall section with few polygons, and a plant with many polygons casting shadows on a white wall.](../../../assets/tutorials/environmental-art-curriculum/Section3/SettingParameters-CastShadow1.png) Shadows can provide a great sense of realism to objects with 3D geometrical depth, so when you're deciding where to disable this property, consider where objects provide a significant visual improvement to the environment, and where users will notice missing shadows. For example, the complex shadows of foliage can provide a great sense of immersion in the outdoor space where users are likely to see them, but that same immersion isn't necessary for the foliage that peaks through the ceiling that users will never interact with. ![Foliage casting shadows.](../../../assets/tutorials/environmental-art-curriculum/Section3/CastShadow-Enabled.jpg)_`Class.BasePart.CastShadow` = Enabled_ ![Foliage not casting shadows.](../../../assets/tutorials/environmental-art-curriculum/Section3/CastShadow-Disabled.jpg)_`Class.BasePart.CastShadow` = Disabled_ ### DoubleSided The `Class.MeshPart.DoubleSided` property determines whether to render both faces or polygons in the mesh. When you enable this property for a planar `Class.MeshPart`, such as leaves, hair, or cloth cards, the Roblox Engine renders both faces or polygons in the mesh to allow users to see the fullness of the object no matter what angle they are looking at the object. For example, the following tree leaves are single-sided planar meshes, and when you enable this property, any of the leaves that face away from the camera become visible to the user. ![A tree with all planar meshes facing the camera.](../../../assets/tutorials/environmental-art-curriculum/Section3/DoubleSided-Enabled.png)_`Class.MeshPart.DoubleSided` = Enabled_ ![A tree with less leaves because all planar meshes facing away from the camera are disabled.](../../../assets/tutorials/environmental-art-curriculum/Section3/DoubleSided-Disabled.png)_`Class.MeshPart.DoubleSided` = Disabled_ This property is useful for adding realism to your environment, but it comes at a performance cost because the engine needs to render the object's polygons twice: once for the direction that faces the user's camera, and again for any meshes that face away from the user's camera. To alleviate some of this impact on performance, the final sample laser tag environment only enables this property for foliage because it has a strong visual impact for realism in the 3D space. ### CollisionFidelity The `Class.MeshPart.CollisionFidelity` property determines how closely the physical hitbox of a mesh or union matches its visual representation. By default, this setting renders a hitbox that's roughly the same as the polygonal shape of the mesh, meaning that where users collide with the object is similar to the actual geometry of the mesh. For example, the castle mesh in the following images has a default hitbox that almost matches the shape of its geometry. ![A grey castle mesh.](../../../assets/physics/collisions/Collision-Fidelity-MeshPart.jpg)_Original Mesh_ ![The same castle mesh with a colorful hitbox that shows where users can collide with the castle.](../../../assets/physics/collisions/Collision-Fidelity-Default.jpg)_Default_ You can set this property to a different value to reduce the precision of a mesh's hitbox. For example, when you set this property to **Hull**, the Roblox Engine significantly reduces the vertices of the mesh's hitbox, and when you set this property to **Box**, the engine reduces the mesh's hitbox to a cube that surrounds the mesh. In addition, if you want to increase the precision of a mesh's hitbox, you can set this property to **PreciseConvexDecomposition** to increase the amount of vertices in the hitbox. ![The same castle mesh with cube hitbox that shows where users can collide with the castle.](../../../assets/physics/collisions/Collision-Fidelity-Box.jpg)_Box_ ![The same castle mesh with a cylindrical hitbox that shows where users can collide with the castle.](../../../assets/physics/collisions/Collision-Fidelity-Hull.jpg)_Hull_ ![The same castle mesh with a colorful hitbox that shows where users can collide with the castle. This version shows more colorful faces than the default hitbox.](../../../assets/physics/collisions/Collision-Fidelity-Precise.jpg)_PreciseConvexDecomposition_ Similar to `Class.BasePart.CastShadow`, the more vertices the Roblox Engine needs to render, the higher the performance cost. For this reason, it's important to consider how you want users to interact with each mesh to see if you can reduce the amount of vertices the engine needs to render for the mesh's hitbox. In cases where it isn't necessary for a hitbox to be precise, set this property to either **Box** or **Hull**. To demonstrate this concept, reference the following images that display the hitbox of every mesh of the wall model that provides a rise in elevation between the first and second floor of the final sample laser tag environment. The default hitbox for the main wall has a lot of unnecessary geometry when it only needs to provide a surface to prevent the user from moving in that direction. If you set the property to **Box** for this mesh, you can remove this redundant geometry while keeping the purpose of the hitbox intact. ![A wall model that provides a rise in elevation.](../../../assets/tutorials/environmental-art-curriculum/Section3/CollisionFidelity-WallMesh.jpg)_Original Mesh_ ![The same wall model with a colorful hitbox that shows where users can collide with the wall.](../../../assets/tutorials/environmental-art-curriculum/Section3/CollisionFidelity-Default.jpg)_Default_ ![The same wall model with a colorful hitbox for the trim, but only a box hitbox for the wall mesh.](../../../assets/tutorials/environmental-art-curriculum/Section3/CollisionFidelity-Box.jpg)_Box_ As you're making these decisions, it's important to consider how changing the default value may negatively affect users as they're navigating your environment. For example, if you set the property to **Box** for the trim meshes of the wall model, users would collide with the hitbox if they try to jump near the wall. Instead, set these meshes to **Hull** so you reduce its vertex count while keeping the hitbox closer to the shape of the mesh's geometry. ![A wall model with a box hitbox for the top trip. Because the box sticks out, users can unintentionally collide with the top trim.](../../../assets/tutorials/environmental-art-curriculum/Section3/CollisionFidelity-BoxTop.jpg)_Box_ ![The same wall model with a hull hitbox for the top trip. Because hull hitbox conforms much closer to the top of the model, allowing users to avoid colliding with the trim.](../../../assets/tutorials/environmental-art-curriculum/Section3/CollisionFidelity-Hull.jpg)_Hull_ Note that it can be important to have a hitbox that precisely conforms to the shape of your mesh, especially when you need to control exactly how users collide with its shape. For example, in the final sample laser tag environment, it's important that users only collide with the edges of the doorway and not the doorway itself, otherwise they would never be able to enter or exit a combat pocket. ![A doorway model with optimized hitbox meshes.](../../../assets/tutorials/environmental-art-curriculum/Section3/CollisionFidelity-Doorway.png) > **Success:** To view collision fidelity in Studio, toggle on **Collision fidelity** from the [Visualization Options](/docs/en-us/studio/ui-overview.md#visualization-options) widget in the upper‑right corner of the 3D viewport. ### RenderFidelity The `Class.MeshPart.RenderFidelity` property determines the mesh's level of detail that displays to the user. When you set this property to `Enum.RenderFidelity.Automatic`, the Roblox Engine reduces the mesh's fidelity the further the user is from the mesh, and when you set this property to `Enum.RenderFidelity.Precise`, every vertex of your mesh renders exactly as you intend no matter the distance between them. The more objects the engine must render precisely to the user from any distance, especially if they have a high vertex count, the higher the performance cost. To alleviate some of this impact on performance, set this property to `Enum.RenderFidelity.Performance` for objects with geometric complexity, such as foliage in the sample asset library, because they don't need to display in their entirety from a distance for any gameplay requirement. This reduces your experience's polygon count without yielding overall visual quality. ## Convert assets into packages Now that all of your modular assets are in Studio with rendering parameters that are set up to retain their high visual quality for all users, it's time to convert them into packages. It's important to convert your assets into packages because it allows you to reuse them repeatedly throughout your current experience and other projects. In addition, you can easily make edits to a package that instantly populate to all of its instances, saving you a lot of time in the iteration process. Because your `Class.SurfaceAppearance` objects are already packages, when you convert your `Class.Model` objects into packages, they become nested packages. Nested packages allow you to maintain complex hierarchies of child objects that you can modify independent of the `Class.Model` object package. This allows you greater control over the individual components of your assets. To convert your modular assets and props into packages: 1. In the **Explorer** window, right-click a modular asset or a prop, then select **Convert to Package** from the contextual menu. The **Convert to Package** dialog displays. 2. Fill out the **Title** and **Description** fields, set the ownership to yourself or a group, then click the **Submit** button. Once complete, a chain link symbol displays over the model's icon to identify it as a package.![The Model icon with a chain link symbol.](../../../assets/tutorials/environmental-art-curriculum/Section3/ConvertingAssets-2.png) 3. Repeat this process for each modular asset and prop. Once you are happy with the overall layout of your asset library, you can move on to decorating the environment with your newly polished assets, and configuring additional elements of the 3D space to bring your world to life. --- title: "Construct your world" url: /docs/en-us/tutorials/curriculums/environmental-art/construct-your-world last_updated: 2026-06-29T19:34:13Z description: "Explains how to utilize your asset library, configure additional elements of the 3D space to bring your world to life." --- # Construct your world **Constructing your world**, also known as worldbuilding, is the process of decorating the environment with polished assets from your asset library, then adding and configuring additional elements of the 3D space to bring your world to life. This exciting step of the workflow is where you get to see all of your vision and hard work come together into a cohesive environment that is complete and ready for publication. Using the [Environment Art - Constructing](https://www.roblox.com/games/14447826396/Environment-Art-Constructing) `.rbxl` file as a reference, this section of the environmental art curriculum shows you how to use and apply your high quality assets and custom materials to your greybox geometry in a way that differentiates each distinct area of the team-based first-person laser tag shooter experience, including step-by-step instructions on: - Applying your asset library to the building's architecture and outdoor space to provide users information about the overall world. - Sculpting terrain with your custom tileable textures to create the overall island that the building rests on. - Containing the playable areas to ensure users remain in combat without falling off the island. - Configuring special effects and lighting sources to enrich the 3D space with movement, warmth, and realism. After you complete this section, you will learn how to review and configure your content to ensure your experience has optimal performance across devices. > **Info:** The instructions in this section of the tutorial show you how to **exactly** recreate the sample final environment using the [Environment Art Asset Library](https://www.roblox.com/library/14447738661/Environment-Art-Asset-Library), which takes about 90 minutes or less from start to finish. If you don't want to use the provided coordinates, you can adjust the geometry however you need to meet the specifications of your own experience, or use the sample itself for the rest of the tutorial. ## Apply your asset library The first step in constructing your environment is to apply your asset library to the 3D space, and this process often falls into one of the following categories of work: - Applying your asset library directly onto the greybox geometry to exactly match the original design. - Converting the greybox geometry into something else entirely that still respects the intention behind the original design. - Converting the greybox geometry into something else while also applying your asset library to the new object to maintain cohesion throughout the environment. The process between greyboxing and constructing your environment with your asset library hardly ever looks like a straight line from your original design to the final art. Iteration between each step and across steps is normal and almost always necessary in the development process as you find new ways to improve the user's experience in your world. For example, the guidance in the **Floors** section converts the original floor greybox geometry into new divisions in order to better orient users to where they are in the building. As you follow these instructions that exactly recreate the final environment within the sample [Environment Art - Constructing](https://www.roblox.com/games/14447826396/Environment-Art-Constructing) `.rbxl` file, observe how each step works together to add character to the environment, and provide users information about the overall world. **You can adjust any part of the process to meet the specifications of your own experience**, as your environment may have different needs according to the world that you're creating. ### Floors The original greybox design for floors was to divide the building into two halves, one for each team. This was useful to help visualize each half as its own space for its respective team, and to delineate the center of the map. However, as you begin to construct your environment, you may find that walls can fulfill this purpose more effectively, and that floors are more useful to help orient users to where they are in relation to the exterior of the building. For example, the final sample laser tag environment uses new floor pieces with unique materials to inform users whether they're navigating somewhere inside the building, or approaching its outdoor space. When a user is inside of the building, they walk on surfaces with the **GlossyTiles** `Class.MaterialVariant`, and when they're walking in the middle courtyard or under the overhang pieces you will make later in this tutorial, they walk on surfaces with the **Concrete_Tiles_A** `Class.MaterialVariant`. You can find both of these materials in the sample [Environment Art Asset Library](https://www.roblox.com/library/14447738661/Environment-Art-Asset-Library). While this technique replaces and converts the original greybox geometry, it still respects the intention behind the original design to create peaks and valleys that control sight lines and engagement distances throughout the experience outside of horizontal movement. This means that users retain all physical and emotional senses of ascension and descension while also having the benefit of knowing where they are in the 3D space. #### Create your own To apply your own asset library to the floors: 1. Using **block** parts, create symmetrical surfaces for your indoor floors. 2. Using **wedge** parts, create a **rise in elevation** between the main and mezzanine floors on the inside of your building. 3. Select these indoor parts, then in the **Properties** window, set **Color**, **Material**, and/or **MaterialVariant** to values that apply a distinct visual treatment from other surfaces in your environment. This allows players to always know they are within the building. 4. Using **block** parts, create symmetrical surfaces for your **outdoor floors**. 5. Using **wedge** parts, create a **rise in elevation** between the main and mezzanine floors on the outside of your building. 6. Using **block** or **wedge** parts, create a drop in elevation between the main and outdoor floors on the outside of your building. 7. Select these outdoor parts, then in the **Properties** window, set **Color**, **Material**, and/or **MaterialVariant** to values that apply a distinct visual treatment from other surfaces in your environment. This allows players to always know they are outside of the building. 8. Anchor all of these floor parts. #### Recreate the sample To exactly recreate the floors within the sample [Environment Art - Constructing](https://www.roblox.com/games/14447826396/Environment-Art-Constructing) place file: 1. Add and configure the following **block** parts for the indoor floors: | Part | Size | CFrame.Position | | --- | --- | --- | | Left-side top area | `57.5, 6, 142` | `-123.75, 2, 281` | | Left-side bottom area | `64, 6, 164.5` | `-63, 2, 269.75` | | Middle top area | `53, 6, 60` | `-119.5, 7, 160` | | Right-side top area | `57.5, 6, 142` | `-123.75, 2, 48.5` | | Right-side bottom area | `64, 5, 162.5` | `-63, 2.5, 51.25` | 2. Select these parts, then in the **Properties** window, 1. Set **Color** to `248, 248, 248`. 2. Set **Material** to **Concrete**. 3. Set **MaterialVariant** to **GlossyTiles**. 3. Add and configure the following **block** parts for the outdoor floors: | Part | Size | CFrame.Position | | --- | --- | --- | | Middle top area | `13, 6, 64` | `-86.5, 7, 158` | | Middle middle area | `29, 1, 55` | `-45.5, 4.5, 160` | | Middle bottom area | `18, 11, 100` | `-14, -0.5, 160` | 4. Select these parts, then in the **Properties** window, 1. Set **Color** to `233, 218, 218`. 2. Set **Material** to **Concrete**. 3. Set **MaterialVariant** to **Concrete_Tiles_A**. 5. Add and configure the following **FloorRamp** modular assets for the elevation between floors: | Size | CFrame.Position | CFrame.Orientation | MaterialVariant | | --- | --- | --- | --- | | `14, 6, 20` | `-102.5, 7, 200` | `0, 0, 0` | `GlossyTiles` | | `14, 6, 20` | `-102.5, 7, 120` | `0, 180, 0` | `GlossyTiles` | | `50, 6, 20` | `-70, 7, 160` | `0, 90, 0` | `Concrete_Tiles_A` | 6. Add and configure the following **block** parts for the stairs leading down into the outdoor space: | Part | Size | CFrame.Position | | --- | --- | --- | | Top step | `8, 1, 260` | `-27, 4.5, 160` | | Left-side step one | `3, 1, 74` | `-14.5, 0.5, 250.75` | | Left-side step two | `3, 1, 74` | `-17, 1.5, 250.75` | | Left-side step three | `3, 1, 74` | `-19.5, 2.5, 250.75` | | Left-side step four | `3, 1, 74` | `-22, 3.5, 250.75` | | Right-side step one | `3, 1, 74` | `-14.5, 0.5, 72` | | Right-side step two | `3, 1, 74` | `-17, 1.5, 72` | | Right-side step three | `3, 1, 74` | `-19.5, 2.5, 72` | | Right-side step four | `3, 1, 74` | `-22, 3.5, 72` | 7. Select these parts, then in the **Properties** window, 1. Set **Color** to `233, 218, 218`. 2. Set **Material** to **Concrete**. 3. Set **MaterialVariant** to **Concrete_Tiles_A**. 8. Anchor all of these parts and modular assets. ### Spawn zones The original greybox design for spawn zones was to create an area at opposite ends of their map for users to join their respective team when the match starts. The placement of these spawn zones allows users to have a safe location at the start of the match that's far away from enemy fire. The final sample laser tag environment keeps the intent of this design while also adding a color theme: **mint** for the team that assembles on the left side of the map, and **carnation pink** for the team that assembles on the right side of the map. You will continue to use this same color scheme throughout this section of the tutorial to differentiate areas of the map near each team's respective spawn zone. #### Create your own To apply your own asset library to the spawn zones: 1. Add and configure any applicable **wall**, **corner**, and **door frame** modular assets to the inside of your **left spawn zone**. 2. Select your wall components, then in the **Properties** window, set **Color** to a **unique** hue. You will continue to use this color throughout the rest of the map to orient players to their location relative to this team's color theme. 3. Repeat this process for your **right spawn zone**. 4. Anchor all of these spawn zone assets. #### Recreate the sample To exactly recreate the spawn zones within the sample [Environment Art - Constructing](https://www.roblox.com/games/14447826396/Environment-Art-Constructing) place file: 1. Add and configure the following **modular** assets for the left spawn zone: | Asset Name | CFrame.Position | CFrame.Orientation | | --- | --- | --- | | WallCornerInner | `-110, 5, 335` | `0, 180, 0` | | WallLarge | `-110, 5, 320` | `0, 0, 0` | | WallLarge | `-110, 5, 305` | `0, 0, 0` | | WallCornerInner | `-105, 5, 300` | `0, 0, 0` | | DoorFrame | `-85, 5, 300` | `0, -90, 0` | | WallLarge | `-70, 5, 300` | `0, -90, 0` | | DoorFrame | `-50, 5, 300` | `0, -90, 0` | | WallCornerInner | `-45, 5, 305` | `0, -90, 0` | | WallLarge | `-45, 5, 320` | `0, 180, 0` | | WallLarge | `-45, 5, 335` | `0, 180, 0` | | WallCornerInner | `-50, 5, 340` | `0, 180, 0` | | WallLarge | `-65, 5, 340` | `0, 90, 0` | | WallLarge | `-80, 5, 340` | `0, 90, 0` | | WallLarge | `-95, 5, 340` | `0, 90, 0` | | WallMid | `-105, 5, 340` | `0, 90, 0` | 2. Select the wall component of these assets, then in the **Properties** window, set **Color** to `88, 218, 171`. 3. Add and configure the following **modular** assets for the right spawn zone: | Asset Name | CFrame.Position | CFrame.Orientation | | --- | --- | --- | | WallCornerInner | `-110, 5, 15` | `0, 90, 0` | | WallLarge | `-110, 5, -0` | `0, 0, 0` | | WallLarge | `-110, 5, -15` | `0, 0, 0` | | WallCornerInner | `-105, 5, -20` | `0, 0, 0` | | WallLarge | `-90, 5, -20` | `0, -90, 0` | | WallLarge | `-75, 5, -20` | `0, -90, 0` | | WallLarge | `-60, 5, -20` | `0, -90, 0` | | WallMid | `-50, 5, -20` | `0, -90, 0` | | WallCornerInner | `-45, 5, -15` | `0, -90, 0` | | WallLarge | `-45, 5, -0` | `0, 180, 0` | | WallLarge | `-45, 5, 15` | `0, 180, 0` | | WallCornerInner | `-50, 5, 20` | `0, 180, 0` | | DoorFrame | `-70, 5, 20` | `0, 90, 0` | | WallLarge | `-85, 5, 20` | `0, 90, 0` | | WallLarge | `-105, 5, 20` | `0, 90, 0` | 4. Select the wall component of these assets, then in the **Properties** window, set **Color** to `255, 170, 255`. 5. Anchor all of these modular assets. ### Combat pockets The original greybox design for the combat pockets was to create intentional spaces for combat where the map's primary lanes intersect with the cross lanes. In addition, the original greybox design required that each combat pocket needed to only include three entrance or exit points at most to refrain from giving users choice overload as they're navigating the space. The final sample laser tag environment applies the sample [Environment Art Asset Library](https://www.roblox.com/library/14447738661/Environment-Art-Asset-Library) directly onto the greybox geometry to exactly match the original design while also using the color theme introduced in the previous section to differentiate areas of the map near each team's respective spawn zone. Using these colors and the floor material, users can quickly orient themselves to where they are no matter where they are around the building, respecting the original intention behind the greybox design for the building's floors. In addition, the technique in this section also applies props from the sample asset library to left and right combat pockets in order to add character to the environment and provide believability that it has life on its own even if users weren't present. This process, also called **set dressing**, provides users both direct and indirect information about the world they're visiting. For example, the signage and foliage props in this section reinforce the sample art style from [Develop Polishing Assets](/docs/en-us/tutorials/curriculums/environmental-art/develop-polished-assets.md), and inform users that they're in a high-tech environment that still prioritizes organic life. #### Create your own To apply your own asset library to the combat pockets: 1. Add and configure any applicable props or **wall**, **corner**, **door frame**, and **door** modular assets to the inside and outside of your **left combat pocket**. 2. Select your wall components on the outside of your left combat pocket, then in the **Properties** window, set **Color** to the **same** hue as the left spawn zone's color theme. This allows players to orient themselves relative to this team's color theme as they are navigating primary and cross lanes. 3. Select your wall components on the inside of your left combat pocket, then in the **Properties** window, set **Color** to a **unique** hue. This allows players to orient themselves when they are within the combat pocket and looking through exit points. 4. Repeat this process for your **middle combat pocket** and **right combat pocket**. 5. Anchor all of these combat pocket assets. #### Recreate the sample To exactly recreate the combat pockets within the sample [Environment Art - Constructing](https://www.roblox.com/games/14447826396/Environment-Art-Constructing) place file: To apply your asset library to the combat pockets: 1. Add and configure the following **modular** assets for the left combat pocket's exterior: | Asset Name | CFrame.Position | CFrame.Orientation | | --- | --- | --- | | WallCornerOuter | `-105, 5, 280` | `0, -90, 0` | | WallLarge | `-105, 5, 280` | `0, 180, 0` | | WallCornerOuter | `-105, 5, 265` | `0, 180, 0` | | WallSmall | `-105, 5, 265` | `0, 90, 0` | | WallCornerInner | `-100, 5, 265` | `0, 180, 0` | | Door | `-95, 5, 260` | `0, 180, 0` | | DoorFrame | `-95, 5, 260` | `0, -180, 0` | | WallLarge | `-95, 5, 240` | `0, 180, 0` | | WallCornerOuter | `-95, 5, 225` | `0, 180, 0` | | WallLarge | `-95, 5, 225` | `0, 90, 0` | | DoorFrame | `-80, 5, 225` | `0, 90, 0` | | WallLarge | `-60, 5, 225` | `0, 90, 0` | | WallCornerOuter | `-45, 5, 225` | `0, 90, 0` | | WallSmall | `-45, 5, 225` | `0, 0, 0` | | WallLarge | `-45, 5, 230` | `0, 0, 0` | | WallLarge | `-45, 5, 245` | `0, 0, 0` | | WallLarge | `-45, 5, 260` | `0, 0, 0` | | WallCornerOuter | `-45, 5, 275` | `0, 0, 0` | | WallMid | `-45, 5, 275` | `0, -90, 0` | | WallSmall | `-55, 5, 275` | `0, -90, 0` | | DoorFrame | `-60, 5, 275` | `0, -90, 0` | | WallMid | `-80, 5, 275` | `0, -90, 0` | | WallCornerInner | `-90, 5, 275` | `0, 0, 0` | | WallCornerOuter | `-95, 5, 280` | `0, 0, 0` | | WallMid | `-95, 5, 280` | `0, -90, 0` | 2. Select the wall component of these assets, then in the **Properties** window, set **Color** to `88, 218, 171`. 3. Add and configure the following **prop** assets for the left combat pocket's exterior: | Asset Name | CFrame.Position | CFrame.Orientation | | --- | --- | --- | | Extinguisher | `-44, 10, 254.5` | `0, 0, 0` | | WallHanging | `-44, 12.5, 248.5` | `0, 0, 0` | | WallConsoleA | `-44, 5.5, 233` | `0, 0, 0` | | Extinguisher | `-96, 10, 242` | `0, 0, 0` | 4. Add and configure the following **modular** assets for the left combat pocket's interior: | Asset Name | CFrame.Position | CFrame.Orientation | | --- | --- | --- | | WallCornerInner | `-90, 5, 265` | `0, 90, 0` | | WallSmall | `-90, 5, 260` | `0, 0, 0` | | DoorFrame | `-90, 5, 240` | `0, 0, 0` | | WallCornerInner | `-85, 5, 235` | `0, 0, 0` | | WallSmall | `-80, 5, 235` | `0, -90, 0` | | DoorFrame | `-60, 5, 235` | `0, -90, 0` | | WallCornerInner | `-55, 5, 240` | `0, -90, 0` | | WallLarge | `-55, 5, 255` | `0, 180, 0` | | WallMid | `-55, 5, 265` | `0, 180, 0` | | WallCornerInner | `-60, 5, 270` | `0, 180, 0` | | DoorFrame | `-80, 5, 270` | `0, 90, 0` | | WallSmall | `-85, 5, 270` | `0, 90, 0` | 5. Select the wall component of these assets, then in the **Properties** window, 1. Set **Color** to `211, 190, 150`. 2. Set **MaterialVariant** to **MetalPanels**. 6. Add a **PlanterSmall** prop asset for a piece of cover at a **CFrame.Position** of `-67.5, 5, 252.5` and a **CFrame.Orientation** of `0, 90, 0`. You can add foliage assets from the asset library to the top of the planter to add more organic life to the building. 7. Add and configure the following **modular** assets for the middle combat pocket's exterior: | Asset Name | CFrame.Position | CFrame.Orientation | | --- | --- | --- | | WallSmall | `-40, 5, 195` | `0, 0, 0` | | WallCornerOuter | `-40, 5, 200` | `0, 0, 0` | | DoorFrame | `-40, 5, 200` | `0, -90, 0` | | WallLarge | `-60, 5, 200` | `0, -90, 0` | | WallCornerInner | `-75, 5, 200` | `0, 0, 0` | | WallSmall | `-80, 5, 205` | `0, 0, 0` | | WallCornerOuter | `-80, 5, 210` | `0, 0, 0` | | WallLarge | `-80, 5, 210` | `0, -90, 0` | | WallCornerOuter | `-95, 5, 210` | `0, -90, 0` | | WallRiseA | `-95, 5, 210` | `0, 180, 0` | | WallMid | `-95, 10, 190` | `0, 180, 0` | | WallCornerInner | `-95, 10, 180` | `0, -90, 0` | | WallLarge | `-100, 10, 175` | `0, -90, 0` | | WallCornerOuter | `-115, 10, 175` | `0, -90, 0` | | WallSmall | `-115, 10, 175` | `0, 180, 0` | | DoorFrame | `-115, 10, 170` | `0, 180, 0` | | WallSmall | `-115, 10, 150` | `0, 180, 0` | | WallCornerOuter | `-115, 10, 145` | `0, 180, 0` | | WallLarge | `-115, 10, 145` | `0, 90, 0` | | WallCornerInner | `-100, 10, 145` | `0, 180, 0` | | WallMid | `-95, 10, 140` | `0, 180, 0` | | WallRiseB | `-95, 5, 110` | `0, -180, 0` | | WallCornerOuter | `-95, 5, 110` | `0, 180, 0` | | WallLarge | `-95, 5, 110` | `0, 90, 0` | | WallCornerOuter | `-80, 5, 110` | `0, 90, 0` | | WallSmall | `-80, 5, 110` | `0, 0, 0` | | WallCornerInner | `-80, 5, 115` | `0, 90, 0` | | WallLarge | `-75, 5, 120` | `0, 90, 0` | | DoorFrame | `-60, 5, 120` | `0, 90, 0` | | WallCornerOuter | `-40, 5, 120` | `0, 90, 0` | | WallSmall | `-40, 5, 120` | `0, 0, 0` | 8. Select the wall component of these assets, then in the **Properties** window, 1. Set **Color** to `181, 173, 156`. 2. Set **MaterialVariant** to **Concrete_Ribbed_A**. 9. Add and configure the following **modular** assets for the middle combat pocket's interior: | Asset Name | CFrame.Position | CFrame.Orientation | | --- | --- | --- | | DoorFrame | `-60, 5, 185` | `0, 90, 0` | | WallRiseB | `-60, 5, 185` | `0, 90, 0` | | WallSmall | `-85, 10, 185` | `0, 90, 0` | | WallCornerInner | `-90, 10, 180` | `0, 90, 0` | | WallMid | `-90, 10, 170` | `0, 0, 0` | | DoorFrame | `-90, 10, 150` | `0, 0, 0` | | WallMid | `-90, 10, 140` | `0, 0, 0` | | WallCornerInner | `-85, 10, 135` | `0, 0, 0` | | WallSmall | `-80, 10, 135` | `0, -90, 0` | | WallRiseA | `-60, 5, 135` | `0, -90, 0` | | DoorFrame | `-40, 5, 135` | `0, -90, 0` | | ExtTrimAShort | `-65, 5, 151` | `0, 90, 0` | | ExtTrimAShort | `-75, 5, 169` | `0, -90, 0` | | ExtTrimAMid | `-65, 5, 169` | `0, 0, 0` | | ExtTrimAMid | `-65, 10, 167` | `0, 0, 0` | | ExtTrimACorner | `-65, 5, 169` | `0, 0, 0` | | ExtTrimACorner | `-65, 5, 151` | `0, 90, 0` | | DoorFramePlug | `-40, 5, 200` | `0, -90, 0` | | DoorFramePlug | `-60, 5, 185` | `0, 90, 0` | | DoorFramePlug | `-100, 10, 170` | `0, 180, 0` | | DoorFramePlug | `-105, 10, 170` | `0, 180, 0` | | DoorFramePlug | `-110, 10, 170` | `0, 180, 0` | | DoorFramePlug | `-105, 10, 150` | `0, 0, 0` | | DoorFramePlug | `-60, 5, 120` | `0, 90, 0` | | DoorFramePlug | `-40, 5, 135` | `0, -90, 0` | 10. Select the wall component of these assets, then in the **Properties** window, 1. Set **Color** to `181, 173, 156`. 2. Set **MaterialVariant** to **Concrete_Ribbed_A**. 11. Add and configure the following **modular** assets for the right combat pocket's exterior: | Asset Name | CFrame.Position | CFrame.Orientation | | --- | --- | --- | | WallCornerOuter | `-95, 5, 95` | `0, -90, 0` | | WallLarge | `-95, 5, 95` | `0, 180, 0` | | DoorFrame | `-95, 5, 80` | `0, 180, 0` | | WallCornerInner | `-95, 5, 60` | `0, -90, 0` | | WallSmall | `-100, 5, 55` | `0, -90, 0` | | WallCornerOuter | `-105, 5, 55` | `0, -90, 0` | | WallLarge | `-105, 5, 55` | `0, 180, 0` | | WallCornerOuter | `-105, 5, 40` | `0, 180, 0` | | WallMid | `-105, 5, 40` | `0, 90, 0` | | WallCornerOuter | `-95, 5, 40` | `0, 90, 0` | | WallCornerInner | `-95, 5, 40` | `0, 90, 0` | | WallMid | `-90, 5, 45` | `0, 90, 0` | | DoorFrame | `-80, 5, 45` | `0, 90, 0` | | WallLarge | `-60, 5, 45` | `0, 90, 0` | | WallCornerOuter | `-45, 5, 45` | `0, 90, 0` | | WallSmall | `-45, 5, 45` | `0, 0, 0` | | WallLarge | `-45, 5, 50` | `0, 0, 0` | | WallLarge | `-45, 5, 65` | `0, 0, 0` | | WallLarge | `-45, 5, 80` | `0, 0, 0` | | WallCornerOuter | `-45, 5, 95` | `0, 0, 0` | | WallMid | `-45, 5, 95` | `0, -90, 0` | | WallSmall | `-55, 5, 95` | `0, -90, 0` | | DoorFrame | `-60, 5, 95` | `0, -90, 0` | | WallLarge | `-80, 5, 95` | `0, -90, 0` | 12. Select the wall component of these assets, then in the **Properties** window, set **Color** to `255, 170, 255`. 13. Add and configure the following **prop** assets for the right combat pocket's exterior: | Asset Name | CFrame.Position | CFrame.Orientation | | --- | --- | --- | | Extinguisher | `-44, 10, 75.5` | `0, 0, 0` | | WallHanging | `-44, 5.5, 88` | `0, 0, 0` | | WallConsoleA | `-44, 12.5, 70.5` | `0, 0, 0` | | Extinguisher | `-96, 10, 62` | `0, 180, 0` | 14. Add and configure the following **modular** assets for the right combat pocket's interior: | Asset Name | CFrame.Position | CFrame.Orientation | | --- | --- | --- | | WallCornerInner | `-90, 5, 80` | `0, 90, 0` | | DoorFrame | `-90, 5, 60` | `0, 0, 0` | | WallSmall | `-90, 5, 55` | `0, 0, 0` | | WallCornerInner | `-85, 5, 50` | `0, 0, 0` | | WallSmall | `-80, 5, 50` | `0, -90, 0` | | DoorFrame | `-60, 5, 50` | `0, -90, 0` | | WallCornerInner | `-55, 5, 55` | `0, -90, 0` | | WallLarge | `-55, 5, 70` | `0, 180, 0` | | WallMid | `-55, 5, 80` | `0, 180, 0` | | WallCornerInner | `-60, 5, 85` | `0, 180, 0` | | DoorFrame | `-80, 5, 85` | `0, 90, 0` | | WallSmall | `-85, 5, 85` | `0, 90, 0` | 15. Select the wall component of these assets, then in the **Properties** window, 1. Set **Color** to `255, 170, 255`. 2. Set **MaterialVariant** to **MetalPanels**. 16. Add a **PlanterSmall** prop asset for a piece of cover at a **CFrame.Position** of `-67.5, 5, 67.5` and a **CFrame.Orientation** of `0, 90, 0`. You can add foliage assets from the asset library to the top of the planter to add more organic life to the building. 17. Anchor all of these prop and modular assets. ### Perimeter hallways By applying your asset library to the exterior of the combat pockets in the previous section, most cross lanes now include polished assets aside from the hallways that make up the perimeter of the building. In addition to using the same color scheme from previous sections, the final sample laser tag environment also uses wall assets with their default orange color to inform users where they are in relation to the back of the building. For example, using the point-of-view in the following image, users immediately have the following information to orient themselves to where they are within the building: - The **ribbed concrete** informs users that if they move left, then they will enter the middle combat pocket. - The **yellow wall** informs users that if they move forward, then they will reach the back of the building and the interior primary lane. - The **pink wall** informs users that if they move right, then they will enter the right combat pocket, and that they are nearest the right side of the map. Aside from this technique that applies the sample [Environment Art Asset Library](https://www.roblox.com/library/14447738661/Environment-Art-Asset-Library) directly onto the greybox geometry for the walls to exactly match the original design, it also adds geometry in the form of crate prop assets in order to provide users cover from enemy fire as they navigate the interior primary lane. This design iteration still respects the intention to break up sight lights and engagement distances with protruding hallway spaces and elevation changes. #### Create your own To apply your own asset library to the perimeter hallways: 1. Add and configure any applicable prop or **wall**, **corner**, and **door frame** modular assets to the perimeter hallways on the **left-side** of the building. 2. Select your wall components, then in the **Properties** window, set **Color** to the **same** hue as the left spawn zone's color theme. 3. Add and configure any applicable prop or **wall**, **corner**, and **door frame** modular assets to the perimeter hallways at the **back** of the building. 4. Select your wall components, then in the **Properties** window, set **Color** to a **unique** hue. This allows players to orient themselves relative to where they are in relation to the interior primary lane. The sample experience keeps these assets their default color. 5. Add and configure any applicable prop or **wall**, **corner**, and **door frame** modular assets to the perimeter hallways on the right-side of the building. 6. Select your wall components, then in the **Properties** window, set **Color** to the same hue as the right spawn zone's color theme. 7. Anchor all of these perimeter hallway assets. #### Recreate the sample To exactly recreate the perimeter hallways within the sample [Environment Art - Constructing](https://www.roblox.com/games/14447826396/Environment-Art-Constructing) place file: 1. Add and configure the following **modular** assets for the perimeter hallways on the left-side of the building: | Asset Name | CFrame.Position | CFrame.Orientation | | --- | --- | --- | | WallLarge | `-50, 5, 295` | `0, 90, 0` | | DoorFrame | `-70, 5, 295` | `0, 90, 0` | | WallLarge | `-85, 5, 295` | `0, 90, 0` | | DoorFrame | `-105, 5, 295` | `0, 90, 0` | | WallLarge | `-120, 5, 295` | `0, 90, 0` | 2. Select the wall component of these assets, then in the **Properties** window, set **Color** to `255, 170, 255`. 3. Add and configure the following **modular** assets for the perimeter hallways at the back of the building: | Asset Name | CFrame.Position | CFrame.Orientation | | --- | --- | --- | | WallCornerInner | `-125, 5, 290` | `0, 90, 0` | | WallLarge | `-125, 5, 275` | `0, 0, 0` | | WallLarge | `-125, 5, 260` | `0, 0, 0` | | WallLarge | `-125, 5, 245` | `0, 0, 0` | | WallSmall | `-125, 5, 240` | `0, 0, 0` | | WallCornerInner | `-120, 5, 235` | `0, 0, 0` | | WallMid | `-110, 5, 235` | `0, -90, 0` | | WallCornerOuter | `-110, 5, 235` | `0, 0, 0` | | WallLarge | `-110, 5, 220` | `0, 0, 0` | | WallMid | `-110, 5, 210` | `0, 0, 0` | | WallRiseB | `-110, 5, 210` | `0, 0, 0` | | WallCornerOuter | `-110, 10, 190` | `0, 90, 0` | | WallLarge | `-125, 10, 190` | `0, 90, 0` | | WallLarge | `-140, 10, 190` | `0, 90, 0` | | WallCornerInner | `-145, 10, 185` | `0, 90, 0` | | WallSmall | `-145, 10, 180` | `0, 0, 0` | | WallLarge | `-145, 10, 165` | `0, 0, 0` | | WallLarge | `-145, 10, 150` | `0, 0, 0` | | WallLarge | `-145, 10, 135` | `0, 0, 0` | | WallCornerInner | `-140, 10, 130` | `0, 0, 0` | | WallLarge | `-125, 10, 130` | `0, -90, 0` | | WallLarge | `-110, 10, 130` | `0, -90, 0` | | WallCornerOuter | `-110, 10, 130` | `0, 0, 0` | | WallRiseA | `-110, 5, 110` | `0, 0, 0` | | WallMid | `-110, 5, 100` | `0, 0, 0` | | WallLarge | `-110, 5, 85` | `0, 0, 0` | | WallCornerOuter | `-110, 5, 85` | `0, 90, 0` | | WallMid | `-120, 5, 85` | `0, 90, 0` | | WallCornerInner | `-125, 5, 80` | `0, 90, 0` | | WallLarge | `-125, 5, 65` | `0, 0, 0` | | WallLarge | `-125, 5, 50` | `0, 0, 0` | | WallLarge | `-125, 5, 35` | `0, 0, 0` | | WallSmall | `-125, 5, 30` | `0, 0, 0` | | WallCornerInner | `-120, 5, 25` | `0, 0, 0` | 4. Add and configure the following **prop** assets for the perimeter hallways at the back of the building: | Asset Name | CFrame.Position | CFrame.Orientation | | --- | --- | --- | | SecurityCamera | `-120, 20, 290` | `0, 0, 0` | | WallHanging | `-124, 13, 287.5` | `0, 0, 0` | | CrateMedium | `-120, 5, 260` | `0, -180, 0` | | CrateSmall | `-110, 5, 250` | `0, 135, 0` | | WallHanging | `-109, 13, 217.5` | `0, 0, 0` | | WallHanging | `-109, 13, 103` | `0, 0, 0` | | CrateSmall | `-110, 5, 70` | `0, 135, 0` | | CrateMedium | `-120, 5, 65` | `0, -180, 0` | | WallHanging | `-124, 13, 37.5` | `0, 0, 0` | | SecurityCamera | `-120, 20, 30` | `0, -90, 0` | 5. Add and configure the following assets for the perimeter hallways on the right-side of the building: | Part | Size | CFrame.Position | | --- | --- | --- | | WallLarge | `-105, 5, 25` | `0, -90, 0` | | DoorFrame | `-85, 5, 25` | `0, -90, 0` | | WallLarge | `-70, 5, 25` | `0, -90, 0` | | DoorFrame | `-50, 5, 25` | `0, -90, 0` | | WallLarge | `-35, 5, 25` | `0, -90, 0` | 6. Select the wall component of these assets, then in the **Properties** window, set **Color** to `255, 170, 255`. 7. Anchor all of these prop and modular assets. ### Doors The final sample laser tag environment applies doors directly onto the greybox geometry to exactly match the original design, however, not all entrance or exit points have door assets. You may find that the requirements of your own experience require more or less doors, depending on how you want to control combat within your combat pockets. More doors often leads to longer wait times before users enter or exit a combat pocket, but less doors may open up unwanted lines of sight. For this reason, it's recommended to playtest with multiple variations to experiment different play styles. In fact, you may experiment with placing door assets into your combat pockets that only open to users from one side of the door, or not at all. #### Create your own To apply your own asset library to the doors: 1. Add and configure any **door** modular assets to the doorways of your combat pockets. 2. **(Optional)** Anchor all of these door assets. #### Recreate the sample To exactly recreate the doors within the sample [Environment Art - Constructing](https://www.roblox.com/games/14447826396/Environment-Art-Constructing) place file: 1. Add and configure the following **Door** assets for the left combat pocket: | CFrame.Position | CFrame.Orientation | | --- | --- | | `-60, 5, 275` | `0, -90, 0` | | `-95, 5, 260` | `0, 180, 0` | 2. Add and configure the following **Door** assets for the right combat pocket: | CFrame.Position | CFrame.Orientation | | --- | --- | | `-90, 5, 60` | `0, 0, 0` | | `-80, 5, 45` | `0, 90, 0` | ### Exterior assets The original greybox design for exterior assets was to set placeholder positions for objects that provide cover from enemy fire while users navigate the exterior primary lane. This was useful to help visualize what would help users as they ascend or descend stairs, cross the middle combat pocket, or otherwise exist in the outdoor space, but as you construct your environment, you can improve this design by providing different uses for these cover objects. The following sections provide guidance on how to convert these initial placeholder assets into aesthetically pleasing objects that follow your experience's art style, and keep the intent behind the original design. #### Towers The first exterior assets you can convert for the outdoor space are the two towers right outside of the middle combat pocket that intersect with the exterior primary lane. The technique in this section not only creates objects that are aesthetically pleasing to the eye, but it also creates objects that provide users a sense of scale in their environment. Every other object so far is about the size of the user's character **except** for these towers, which can lead to intrigue in the story behind the architecture or overall island that the building rests on. #### Create your own To apply your own asset library to the towers: 1. Using **block** parts, create a tower that is much larger than the player to provide a sense of scale in the environment. 2. Apply unique colors and materials to different block parts according to the art style of your experience. 3. Duplicate this tower, then position it to a symmetrical location on your map. 4. Anchor all of these parts. #### Recreate the sample To exactly recreate the towers within the sample [Environment Art - Constructing](https://www.roblox.com/games/14447826396/Environment-Art-Constructing) place file: 1. Add and configure the following **block** parts for the left tower: | Part | Size | CFrame.Position | Color | Material | MaterialVariant | | --- | --- | --- | --- | --- | --- | | Large Spire | `16, 98, 11` | `-32, 58, 186.5` | `248, 248, 248` | `Concrete` | `Concrete_Board_Formed_A` | | Middle Spire | `17.369, 77.734, 3.42` | `-32, 72, 186.5` | `181, 173, 156` | `Concrete` | `Concrete_Board_Formed_A` | | Bottom wide spire | `13, 75, 15` | `-31.5, 42.5, 186.5` | `181, 173, 156` | `Concrete` | `Concrete_Ribbed_A` | | Top Wide Spire | `17.288, 4, 15` | `-33.5, 83, 186.5` | `181, 173, 156` | `Concrete` | `Concrete_Ribbed_A` | | Base | `21, 5, 15` | `-29.5, 7.5, 186.5` | `181, 173, 156` | `Sand` | `MossStones` | 2. Group everything into a model, then duplicate the model. 3. Move the duplicate tower to a **CFrame.Position** of `-30.572, 57.93, 133.5`. 4. Anchor both of these models. #### Columns The second exterior assets you can convert for the outdoor space are the two columns that hold up the overhang pieces you will make later in this tutorial. Similar to the towers, the technique in this section not only creates objects that are aesthetically pleasing to the eye, but it also provides further visual cues to users about where they are in the overall environment. For example, each column has either mint or carnation pink detailing to inform users if they are closest to either their own or their enemy's spawn zone. #### Create your own 1. Using **block** and **wedge** parts, create a column that will hold the roof of your building. 2. Apply unique colors and materials to different parts according to the art style of your experience. 3. Duplicate this column, then position it to a symmetrical location on your map. 4. **(Optional)** Change the color of one part in each column to match each team's respective color theme. 5. Anchor all of these parts. #### Recreate the Sample To exactly recreate the columns within the sample [Environment Art - Constructing](https://www.roblox.com/games/14447826396/Environment-Art-Constructing) place file: 1. Add and configure the following **block** parts for the left column: | Part | Size | CFrame.Position | CFrame.Orientation | Color | Material | MaterialVariant | | --- | --- | --- | --- | --- | --- | --- | | Top wall | `15, 20, 5` | `-22.5, 20, 238.5` | `0, 0, 0` | `248, 248, 248` | `Concrete` | `Concrete_Ribbed_A` | | Bottom wall | `15, 9, 5` | `-22.5, 5.5, 238.5` | `0, 0, 0` | `181, 173, 156` | `Concrete` | `Concrete_Ribbed_A` | | Base top | `6.483, 1, 6.583` | `-27.304, 5.298, 238.498` | `0, 0, 0` | `181, 173, 156` | `Concrete` | `Concrete_Ribbed_A` | | Base bottom | `6.483, 1.5, 19.652` | `-15.154, 1.479, 238.5` | `-21.622, -90, 0` | `181, 173, 156` | `Concrete` | `Concrete_Tie_Holes_A` | | Color detail | `1.734, 18.179, 6` | `-22.547, 23.597, 238.4` | `0, -180, 62` | `103, 255, 199` | `Concrete` | `` | 2. Add and configure the following **wedge** part for the top of the left column: | Size | CFrame.Position | CFrame.Orientation | Color | Material | MaterialVariant | | --- | --- | --- | --- | --- | --- | | `7, 11, 21` | `-18.5, 25.5, 238.5` | `0, -90, 180` | `248, 248, 248` | `Concrete` | `Concrete_Board_Formed_A` | 3. Group everything into a model, then duplicate the model. 4. Move the duplicate column to a **CFrame.Position** of `-18.169, 14.081, 81.5` and a **CFrame.Orientation** of `0, -90, 180`. 5. Select the mint **block** part, then in the **Properties** window, set the **Color** property to `255, 170, 255` to match the color theme of the team on the right of the map. 6. Anchor both of these models. #### Planters The final exterior assets you can convert for the outdoor space are the eight planters that extend along the entirety of the exterior primary lane. Using [solid modeling](/docs/en-us/parts/solid-modeling.md) operations, you can negate and join parts together to form the base of each planter, then apply your asset library using different materials, foliage, and/or trim asset configurations so that each one feels unique in the 3D space. To apply your asset library to the left-most planter: 1. Add and configure the following **block** parts for the left-most planter: | Part | Size | CFrame.Position | | --- | --- | --- | | Planter exterior | `20, 11, 5` | `-15, 4.5, 257.5` | | Divot | `18.5, 1, 4` | `-15, 9.75, 257.5` | 2. Select the divot part. 3. Navigate to Studio's **Model** tab toolbar, then click the **Negate** button. The part turns translucent. 4. Select both the negated part and the planter exterior part. 5. In Studio's **Model** tab toolbar, click the **Union** button. The negated part is cut out from the overlapping planter exterior part. 6. Select the union, then in the **Properties** window, 1. Set **Color** to `181, 173, 156`. 2. Set **Material** to **Concrete**. 3. Set **MaterialVariant** to **Concrete_Board_Formed_A**. 4. Enable **UsePartColor**. 7. Add and configure the following **block** parts for the dirt and edging of the planter: | Part | Size | CFrame.Position | CFrame.Orientation | Color | Material | MaterialVariant | | --- | --- | --- | --- | --- | --- | --- | | Dirt | `19, 0.5, 4` | `-15, 9.25, 257.5` | `0, 0, 0` | `248, 248, 248` | `LeafyGrass` | `Moss_Lumpy_Stones_A` | | Top planter edging | `1.652, 1, 6.483` | `-24.838, 5.298, 257.741` | `0, 0, 0` | `181, 173, 156` | `Concrete` | `Concrete_Tie_Holes_A` | | Bottom planter edging | `6.483, 1.5, 19.652` | `-15.154, 1.479, 257.758` | `0, 0, -21.62` | `181, 173, 156` | `Concrete` | `Concrete_Tie_Holes_A` | 8. Add foliage assets to the top of the planter to the top of the planter to add more organic life to the outdoor space. 9. Anchor all of these parts and foliage assets. Now that you know how to use the solid modeling tools, you can experiment in creating types of planters with different exteriors, materials, plant variety, and trim pieces. For example, the other variations of planters within the final sample laser tag environment either make an 'L' shape, or they layer **block** parts and white trim together to provide higher cover in the middle of the map. ### Roof The original greybox design didn't include a roof to the building because it would have prevented you from seeing your progress as you designed and constructed your three lane map layout. However, now that the interior of the building is complete outside of configuring its light sources, you can apply your asset library to new geometry to finish the building and provide users shelter from the outdoor space. The roof of the building is one of the most complex assets in the final sample laser tag environment because it includes multiple layers that must fit together with each other and with the interior geometry. The following instructions walk you through how to construct each layer, starting with the top-most geometry to the final layer of trim that surrounds the perimeter of the roof. #### Skylights The first, top-most layer of the roof is the skylight geometry. The purpose of this layer is to provide outdoor light into interior combat areas. #### Create your own To apply your own asset library to the skylights: 1. Add and configure any **skylight** modular assets to the ceiling of your building. This allows global lighting into combat areas, and provides a richer sense of immersion for players. 2. Using **block** parts, create glass for some of your skylights. 1. Add and position a **block** part into a skylight. 2. Select the part, then in the **Properties** window, 1. Set **Color** to `105, 162, 172`. 2. Set **Material** to **Neon**. 3. Set **Transparency** to `0.6`. 3. Anchor all of these skylight assets. #### Recreate the sample To exactly recreate the skylights within the sample [Environment Art - Constructing](https://www.roblox.com/games/14447826396/Environment-Art-Constructing) place file: 1. Add and configure the following **modular** assets for the left skylight: | Asset Name | CFrame.Position | CFrame.Orientation | | --- | --- | --- | | SkylightCornerInner | `-60, 20, 265` | `0, 180, 0` | | SkylightCornerInner | `-85, 20, 240` | `0, 0, 0` | | SkylightCornerInner | `-60, 20, 240` | `0, -90, 0` | | SkylightCornerInner | `-85, 20, 265` | `0, 90, 0` | | SkylightSmall | `-70, 20, 240` | `0, -90, 0` | | SkylightSmall | `-85, 20, 250` | `0, 0, 0` | | SkylightSmall | `-75, 20, 265` | `0, 90, 0` | | SkylightSmall | `-60, 20, 255` | `0, 180, 0` | 2. Add and configure the following **modular** assets for the top-left skylight: | Asset Name | CFrame.Position | CFrame.Orientation | | --- | --- | --- | | SkylightCornerInner | `-120, 20, 280` | `0, 90, 0` | | SkylightCornerInner | `-110, 20, 280` | `0, 180, 0` | | SkylightCornerInner | `-120, 20, 240` | `0, 0, 0` | | SkylightCornerInner | `-110, 20, 240` | `0, -90, 0` | | SkylightLarge | `-110, 20, 252.5` | `0, 180, 0` | | SkylightLarge | `-120, 20, 245` | `0, 0, 0` | | SkylightLarge | `-120, 20, 260` | `0, 0, 0` | | SkylightLarge | `-110, 20, 260` | `0, 180, 0` | | SkylightLarge | `-120, 20, 267.5` | `0, 0, 0` | | SkylightLarge | `-120, 20, 252.5` | `0, 0, 0` | | SkylightLarge | `-110, 20, 267.5` | `0, 180, 0` | | SkylightLarge | `-110, 20, 275` | `0, 180, 0` | 3. Add and configure the following **modular** assets for the middle skylight: | Asset Name | CFrame.Position | CFrame.Orientation | | --- | --- | --- | | SkylightCornerInner | `-120, 25, 140` | `0, -90, 0` | | SkylightCornerInner | `-140, 25, 140` | `0, 0, 0` | | SkylightCornerInner | `-120, 25, 180` | `0, 180, 0` | | SkylightCornerInner | `-140, 25, 180` | `0, 90, 0` | | SkylightLarge | `-120, 25, 170` | `0, 180, 0` | | SkylightLarge | `-140, 25, 150` | `0, 0, 0` | | SkylightSmall | `-120, 25, 155` | `0, 180, 0` | | SkylightSmall | `-140, 25, 165` | `0, 0, 0` | 4. Add and configure the following **modular** assets for the top-right skylight: | Asset Name | CFrame.Position | CFrame.Orientation | | --- | --- | --- | | SkylightCornerInner | `-110, 20, 40` | `0, -90, 0` | | SkylightCornerInner | `-120, 20, 80` | `0, 90, 0` | | SkylightCornerInner | `-110, 20, 80` | `0, 180, 0` | | SkylightCornerInner | `-120, 20, 40` | `0, 0, 0` | | SkylightLarge | `-110, 20, 67.5` | `0, 180, 0` | | SkylightLarge | `-110, 20, 60` | `0, 180, 0` | | SkylightLarge | `-110, 20, 75` | `0, 180, 0` | | SkylightLarge | `-120, 20, 52.5` | `0, 0, 0` | | SkylightLarge | `-110, 20, 52.5` | `0, 180, 0` | | SkylightLarge | `-120, 20, 60` | `0, 0, 0` | | SkylightLarge | `-120, 20, 67.5` | `0, 0, 0` | | SkylightLarge | `-120, 20, 45` | `0, 0, 0` | 5. Add and configure the following **modular** assets for the right skylight: | Asset Name | CFrame.Position | CFrame.Orientation | | --- | --- | --- | | SkylightCornerInner | `-85, 20, 80` | `0, 90, 0` | | SkylightCornerInner | `-60, 20, 80` | `0, 180, 0` | | SkylightCornerInner | `-85, 20, 55` | `0, 0, 0` | | SkylightCornerInner | `-60, 20, 55` | `0, -90, 0` | | SkylightSmall | `-70, 20, 55` | `0, -90, 0` | | SkylightSmall | `-85, 20, 65` | `0, 0, 0` | | SkylightSmall | `-75, 20, 80` | `0, 90, 0` | | SkylightSmall | `-60, 20, 70` | `0, 180, 0` | 6. Add and configure the following **block** parts for the skylight glass for the two skylights near the back of the building: | Part | Size | CFrame.Position | | --- | --- | --- | | Left glass | `10, 1.574, 39` | `-115, 21.395, 260.5` | | Right glass | `10, 1.574, 39` | `-115, 21.395, 60.5` | 7. Select these two parts, then in the **Properties** window: 1. Set **Color** to `105, 162, 172`. 2. Set **Material** to **Neon**. 3. Set **Transparency** to `0.6`. 8. Anchor all of these parts and modular assets. 9. **(Optional)** You can add foliage assets around the left, middle, and right skylights for more visual variety when users navigate through the building, and to add a pop of color to the ceiling. #### Ceiling The second layer of the roof is the ceiling geometry that users see at the top of their view as they navigate the interior of the building. The purpose of this layer is to fill the open areas between the top trim of each wall asset. #### Create your own To apply your own asset library to the ceiling of the roof: 1. Using **block** and **wedge** parts, fill in the open space around your skylights. 2. Select these parts, then in the **Properties** window, set **Color**, **Material**, and/or **MaterialVariant** to values that apply a distinct visual treatment from other surfaces in your environment. This helps players orient themselves when they are moving their camera quickly. 3. Anchor all of these ceiling assets. #### Recreate the sample To exactly recreate the ceiling within the sample [Environment Art - Constructing](https://www.roblox.com/games/14447826396/Environment-Art-Constructing) place file: 1. Add and configure the following **block** parts for the fill: | Size | CFrame.Position | | --- | --- | | `55, 1, 30` | `-77.5, 20.5, 320` | | `30, 1, 5` | `-105, 20.5, 287.5` | | `10, 1, 5` | `-115, 20.5, 282.5` | | `50, 1, 10` | `-65, 20.5, 285` | | `10, 1, 85` | `-35, 20.5, 247.5` | | `10, 1, 20` | `-105, 20.5, 250` | | `5, 1, 35` | `-102.5, 20.5, 222.5` | | `25, 1, 5` | `-87.5, 20.5, 217.5` | | `35, 1, 15` | `-57.5, 20.5, 212.5` | | `5, 1, 10` | `-32.5, 20.5, 200` | | `5, 1, 15` | `-102.5, 25.5, 187.5` | | `35, 1, 5` | `-122.5, 25.5, 182.5` | | `35, 1, 5` | `-122.5, 25.5, 137.5` | | `5, 1, 15` | `-102.5, 25.5, 132.5` | | `5, 1, 35` | `-102.5, 20.5, 97.5` | | `25, 1, 5` | `-87.5, 20.5, 102.5` | | `35, 1, 15` | `-57.5, 20.5, 107.5` | | `10, 1, 85` | `-35, 20.5, 72.5` | | `5, 1, 10` | `-32.5, 20.5, 120.5` | | `10, 1, 20` | `-105, 20.5, 70` | | `10, 1, 5` | `-115, 20.5, 37.5` | | `30, 1, 5` | `-105, 20.5, 32.5` | | `50, 1, 10` | `-65, 20.5, 35` | | `55, 1, 30` | `-77.5, 20.5, 0` | 2. Add and configure the following **wedge** parts for the elevated ceiling: | Size | CFrame.Position | CFrame.Orientation | | --- | --- | --- | | `5, 5, 10` | `-102.5, 22.5, 200` | `0, 0, 180` | | `5, 5, 10` | `-102.5, 22.5, 120` | `0, 180, 180` | 3. Select all of these ceiling parts, then in the **Properties** window: 1. Set **Color** to `248, 248, 248`. 2. Set **MaterialVariant** to **MetalPanels**. 4. Anchor all of these parts. #### Top roof The third layer of the roof is the top roof geometry. The purpose of this layer is to provide bulk to the outermost roof area that users see as they look at the building from the outdoor space. It also adds a sense of depth to the skylights, making the building look more complete from the inside and out. #### Create your own To apply your own asset library to the top of the roof: 1. Using **block** and **wedge** parts, add in a thick layer to cover the ceiling without hiding your skylights. This surface is what players can see when they are looking at the building from the outdoor space. 2. Select these parts, then in the **Properties** window, set **Color**, **Material**, and/or **MaterialVariant** to values that apply a visual treatment according to the art style of your experience. 3. Anchor all of these top roof assets. #### Recreate the sample To exactly recreate the top of the roof within the sample [Environment Art - Constructing](https://www.roblox.com/games/14447826396/Environment-Art-Constructing) place file: 1. Add and configure the following **block** parts for the top roof on the left of the map: | Size | CFrame.Position | | --- | --- | | `126, 15, 90` | `-92, 28.5, 325` | | `35, 15, 40` | `-137.5, 28.5, 260` | | `25, 15, 40` | `-97.5, 28.5, 260` | | `35, 15, 15` | `-72.5, 28.5, 272.5` | | `25, 15, 75` | `-42.5, 28.5, 242.5` | | `100, 15, 35` | `-105, 28.5, 222.5` | | `65, 15, 10` | `-62.5, 28.5, 200` | 2. Add and configure the following **block** parts for the top roof on the top middle of the map: | Size | CFrame.Position | | --- | --- | | `15, 10, 90` | `-147.5, 31, 160` | | `20, 10, 25` | `-130, 31, 192.5` | | `20, 10, 25` | `-130, 31, 127.5` | | `26, 10, 90` | `-107, 31, 160` | 3. Add and configure the following **block** parts for the top roof on the right of the map: | Size | CFrame.Position | | --- | --- | | `100, 15, 35` | `-105, 28.5, 97.5` | | `65, 15, 10` | `-62.5, 28.5, 120` | | `35, 15, 40` | `-137.5, 28.5, 60` | | `25, 15, 40` | `-97.5, 28.5, 60` | | `35, 15, 15` | `-72.5, 28.5, 47.5` | | `25, 15, 75` | `-42.5, 28.5, 77.5` | | `126, 15, 90` | `-92, 28.5, -5` | 4. Select all of these top roof parts, then in the **Properties** window: 1. Set **Color** to `181, 173, 156`. 2. Set **Material** to **Concrete**. 3. Set **MaterialVariant** to **Concrete_Tie_Holes_A**. 5. Add and configure the following **block** parts for the top roof on the lower middle of the map: | Size | CFrame.Position | | --- | --- | | `54, 5, 15` | `-67, 28.5, 187.5` | | `9, 5, 40` | `-89.5, 28.5, 160` | | `54, 5, 15` | `-67, 28.5, 132.5` | 6. Select all of these top roof parts, then in the **Properties** window, then set **Material** to **Concrete**. 7. Add and configure the following **block** parts for the top roof on the sides of the middle of the map: | Size | CFrame.Position | | --- | --- | | `25, 5, 15` | `-52.5, 23.5, 187.5` | | `25, 5, 15` | `-52.5, 23.5, 132.5` | 8. Add and configure the following **wedge** parts for the top roof on the sides of the middle of the map: | Size | CFrame.Position | CFrame.Orientation | | --- | --- | --- | | `15, 5, 10` | `-70, 23.5, 187.5` | `0, 90, 180` | | `15, 5, 10` | `-70, 23.5, 132.5` | `0, 90, 180` | 9. Select all of these top roof parts, then in the **Properties** window: 1. Set **Color** to `248, 248, 248`. 2. Set **MaterialVariant** to **MetalPanels**. 10. Anchor all of these parts. #### Overhang The fourth layer of the roof is the overhang geometry that the column assets hold up in order for the building to be structurally sound. The purpose of this layer is to provide an aesthetically pleasing awning space for users navigating the exterior primary lane. #### Create your own To apply your own asset library to the overhang of the roof: 1. Using **block** and **wedge** parts, add in a thick overhang for your columns to hold up. This surface provides players partial cover from the sun while they are navigating the exterior primary lane. 2. Select these parts, then in the **Properties** window, set **Color**, **Material**, and/or **MaterialVariant** to values that apply a visual treatment according to the art style of your experience. 3. Anchor all of these overhang parts. #### Recreate the sample To exactly recreate the overhang within the sample [Environment Art - Constructing](https://www.roblox.com/games/14447826396/Environment-Art-Constructing) place file: 1. Add and configure the following **block** parts for the overhang on the left of the map: | Size | CFrame.Position | Color | Material | MaterialVariant | | --- | --- | --- | --- | --- | | `16, 5, 90` | `-16, 38.5, 240` | `181, 173, 156` | `Concrete` | `Concrete_Board_Formed_A` | | `21, 5, 90` | `-18.5, 33.5, 240` | `181, 173, 156` | `Concrete` | `Concrete_Tie_Holes_A` | | `1, 16, 91` | `-29.5, 28, 239.5` | `181, 173, 156` | `Concrete` | `Concrete_Board_Formed_A` | | `0.5, 0.9, 91` | `-30, 20.4, 239.5` | `91, 93, 105` | `Metal` | `` | 2. Add and configure the following **wedge** parts for the overhang on the left of the map: | Size | CFrame.Position | CFrame.Orientation | Color | Material | MaterialVariant | | --- | --- | --- | --- | --- | --- | | `2, 11, 21` | `-18.5, 25.5, 278` | `0, -90, 180` | `248, 248, 248` | `Concrete` | `Concrete_Board_Formed_A` | | `88, 10, 20` | `-19, 26, 240` | `0, -90, 180` | `181, 173, 156` | `Concrete` | `Concrete_Board_Formed_A` | | `2, 11, 21` | `-18.5, 25.5, 202` | `0, -90, 180` | `248, 248, 248` | `Concrete` | `Concrete_Board_Formed_A` | 3. Add and configure the following **block** parts for the overhang on the right of the map: | Size | CFrame.Position | Color | Material | MaterialVariant | | --- | --- | --- | --- | --- | | `12, 5, 90` | `-14, 38.5, 80` | `181, 173, 156` | `Concrete` | `Concrete_Board_Formed_A` | | `21, 5, 90` | `-18.5, 33.5, 80` | `181, 173, 156` | `Concrete` | `Concrete_Board_Formed_A` | | `1, 16, 91` | `-29.5, 28, 80.5` | `181, 173, 156` | `Concrete` | `` | | `0.5, 0.9, 91` | `-30, 20.4, 80.5` | `91, 93, 105` | `Metal` | `` | 4. Add and configure the following **wedge** parts for the overhang on the right of the map: | Size | CFrame.Position | CFrame.Orientation | Color | Material | MaterialVariant | | --- | --- | --- | --- | --- | --- | | `2, 11, 21` | `-18.5, 25.5, 118` | `0, -90, 180` | `255, 255, 255` | `Concrete` | `Concrete_Board_Formed_A` | | `88, 10, 20` | `-19, 26, 80` | `0, -90, 180` | `181, 173, 156` | `Concrete` | `Concrete_Board_Formed_A` | | `2, 11, 21` | `-18.5, 25.5, 42` | `0, -90, 180` | `255, 255, 255` | `Concrete` | `Concrete_Board_Formed_A` | 5. Anchor all of these parts. #### Trim The final layer of the roof is the trim geometry that surrounds the perimeter of the roof. The purpose of this layer is to provide visual cohesion between each previous layer from users looking at the room from the outdoor space. #### Create your own To apply your own asset library to the trim of the roof: 1. Add and configure **trim** modular assets along the overhang of your roof. 2. Select these parts, then in the **Properties** window, set **Color**, **Material**, and/or **MaterialVariant** to values that apply a visual treatment according to the art style of your experience. 3. Anchor all of these overhang parts. #### Recreate the sample To exactly recreate the trim of the roof within the sample [Environment Art - Constructing](https://www.roblox.com/games/14447826396/Environment-Art-Constructing) place file: 1. Add and configure the following **modular** assets for the top-most trim: | Asset Name | CFrame.Position | CFrame.Orientation | | --- | --- | --- | | ExtTrimBLong | `-48, 37, 285` | `0, -90, 0` | | ExtTrimBLong | `-8, 37, 285` | `0, 0, 0` | | ExtTrimBLong | `-8, 37, 240` | `0, 0, 0` | | ExtTrimBLong | `-8, 37, 195` | `0, 90, 0` | | ExtTrimBLong | `-48, 37, 195` | `0, 90, 0` | | ExtTrimBLong | `-89, 37, 195` | `0, 0, 0` | | ExtTrimBLong | `-89, 37, 160` | `0, 0, 0` | | ExtTrimBLong | `-88, 37, 125` | `0, -90, 0` | | ExtTrimBLong | `-48, 37, 125` | `0, -90, 0` | | ExtTrimBLong | `-8, 37, 125` | `0, 0, 0` | | ExtTrimBLong | `-8, 37, 80` | `0, 0, 0` | | ExtTrimBLong | `-8, 37, 35` | `0, 90, 0` | | ExtTrimBCorner | `-8, 37, 285` | `0, 0, 0` | | ExtTrimBCorner | `-8, 37, 195` | `0, 90, 0` | | ExtTrimBCorner | `-8, 37, 125` | `0, 0, 0` | | ExtTrimBCorner | `-8, 37, 35` | `0, 90, 0` | 2. Select these trim assets, then in the **Properties** window, set **Color** to `248, 248, 248`. 3. Add and configure the following **modular** assets for the bottom-most trim: | Asset Name | CFrame.Position | CFrame.Orientation | | --- | --- | --- | | ExtTrimALong | `-48, 31, 285` | `0, -90, 0` | | ExtTrimALong | `-8, 31, 285` | `0, 0, 0` | | ExtTrimAShort | `-8, 31, 245` | `0, 0, 0` | | ExtTrimALong | `-8, 31, 235` | `0, 0, 0` | | ExtTrimALong | `-8, 31, 195` | `0, 90, 0` | | ExtTrimALong | `-48, 31, 195` | `0, 90, 0` | | ExtTrimALong | `-88, 31, 195` | `-88, 31, 195` | | ExtTrimALong | `-88, 31, 125` | `0, -90, 0` | | ExtTrimALong | `-48, 31, 125` | `0, -90, 0` | | ExtTrimALong | `-8, 31, 125` | `0, 0, 0` | | ExtTrimAShort | `-8, 31, 85` | `0, 0, 0` | | ExtTrimALong | `-8, 31, 75` | `0, 0, 0` | | ExtTrimALong | `-8, 31, 35` | `0, 90, 0` | | ExtTrimBCorner | `-8, 31, 285` | `0, 0, 0` | | ExtTrimBCorner | `-8, 31, 195` | `0, 90, 0` | | ExtTrimBCorner | `-8, 31, 125` | `0, 0, 0` | | ExtTrimBCorner | `-8, 31, 35` | `0, 90, 0` | 4. Add and configure the following **modular** assets for the trim in the middle of the roof: | Asset Name | CFrame.Position | CFrame.Orientation | | --- | --- | --- | | ExtTrimAShort | `-24, 26, 195` | `0, 90, 0` | | ExtTrimAMid | `-24, 26, 194` | `0, 0, 0` | | ExtTrimALong | `-24, 26, 179` | `0, 90, 0` | | ExtTrimAMid | `-64, 26, 179` | `0, 90, 0` | | ExtTrimALong | `-85, 26, 178` | `0, 0, 0` | | ExtTrimAMid | `-84, 26, 141` | `0, -90, 0` | | ExtTrimALong | `-64, 26, 141` | `0, -90, 0` | | ExtTrimAMid | `-24, 26, 141` | `0, 0, 0` | | ExtTrimAShort | `-24, 26, 126` | `0, 90, 0` | | ExtTrimACorner | `-24, 26, 194` | `0, 0, 0` | | ExtTrimACorner | `-24, 26, 179` | `0, 90, 0` | | ExtTrimACorner | `-84, 26, 178` | `0, -90, 0` | | ExtTrimACorner | `-84, 26, 142` | `0, 180, 0` | | ExtTrimACorner | `-24, 26, 141` | `0, 0, 0` | | ExtTrimACorner | `-24, 26, 126` | `0, 90, 0` | 5. Select these trim assets, then in the **Properties** window, set **Color** to `255, 170, 0`. 6. Anchor all of these trim assets. ## Delete excess geometry Now that your building is complete, it's time to delete the underlying greybox geometry and the baseplate. This process removes excess polygons that users will never see nor interact with, which quickly improves memory and performance for devices with memory and graphics processing unit (GPU) limitations. In addition, removing the baseplate also allows the building to float in the sky, which is good preparation for you to sculpt terrain for the outdoor space in the next section. To delete excess geometry: 1. In the **Explorer** window, delete all greybox geometry from the first section of this tutorial. 2. Delete the baseplate object. ## Sculpt terrain Right now your environment is mostly architectural aside from foliage in your planters. However, following the sample art style from [Develop Polished Assets](/docs/en-us/tutorials/curriculums/environmental-art/develop-polished-assets.md), the 3D space should contain a healthy mix of futuristic technology and lush greenery to inform users that the world values technological advances, but not at the expense of the earth. Using the [Terrain Editor](/docs/en-us/parts/terrain.md), you can quickly generate and sculpt detailed and realistic terrain to bring organic life to the outdoor space. Because you set material overrides as you created your custom materials in [Assemble an Asset Library](/docs/en-us/tutorials/curriculums/environmental-art/assemble-an-asset-library.md), you can also utilize your tileable textures through **Terrain Editor** brushes to add moss, flowers, and stone paths that are cohesive with the rest of your environment. > **Info:** Sculpting terrain is an art form, and it's difficult to exactly recreate brush strokes and subtle material edits. As long as your terrain meets the needs of your own experience, it's normal and expected for your environment to look and feel different from the sample laser tag environment. #### Create your own To apply your own terrain to the outdoor area: 1. From the toolbar's **Home** tab, click **Terrain**. The [Terrain Editor](/docs/en-us/studio/terrain-editor.md) window opens. 2. Navigate to the **Edit** tab, then click the **Draw** button. 3. In the **Brush Settings** section, customize your brush according to the material you want to add to your map. For example, the sample starts this process with the settings in the following image. 4. In the **Materials Settings** section, select a material for the **unplayable area**, such as water or cracked lava, then in the viewport, draw where you want your playable area to end. This material communicates where the map ends for the outdoor space. 5. Navigate back to the **Materials Settings** section, select a material for the **playable area**, such as grass, pavement, or wood planks, then in the viewport, draw where you want your playable area to start. This material communicates where the map starts for the outdoor space 6. Navigate back to the **Terrain Editor**, then select the **Paint** tool. 7. In the **Brush Settings** section, customize your brush according to the material you want to add to your map, and select a new material. 8. In the viewport, draw along the exterior lane. This material communicates the primary path players can take as they are navigating the outdoor space. 9. **(Optiona)** Add and configure any applicable props to the outdoor space for visual variation. #### Recreate the sample To recreate the terrain within the sample [Environment Art - Constructing](https://www.roblox.com/games/14447826396/Environment-Art-Constructing) place file: 1. From the toolbar's **Home** tab, click **Terrain**. The [Terrain Editor](/docs/en-us/studio/terrain-editor.md) window opens. 2. Navigate to the **Edit** tab, then click the **Draw** button. 3. In the **Brush Settings** section, 1. Set **Brush Mode** to **Add**. 2. Set the brush shape to the cylinder shape. 3. Set **Base Size** to `16`. 4. Set **Height** to `5`. 5. Set **Pivot Position** to **Top**. 6. Set **Plane Lock** to **Manual**. 7. In the **Edit Plane** setting, 1. Click the **Edit** button. 2. Set **Position** to `0, -1, 0`. 3. Click the **Apply** button. 4. In the **Material Settings** section, select the **Water** material, then in the viewport, draw a crescent shaped pool around the building. 5. Navigate back to the **Brush Settings** section, then 1. Set **Base Size** to `10`. 2. Set **Height** to `10`. 3. Set **Ignore Parts** to **True/Enabled**. 4. In the **Edit Plane** setting, 1. Click the **Edit** button. 2. Set **Position** to `0, 0, 0`. 3. Click the **Apply** button. 6. In the **Material Settings** section, select the **Slate** material, then in the viewport, draw another crescent shape that separates the pool from the architecture, and then a thin boundary around the outside of the pool. 7. Navigate back to the **Terrain Editor**, then select the **Paint** tool. 8. In the **Brush Settings** section, 1. Set the brush shape to the cylinder shape. 2. Set **Base Size** to `3`. 3. Set **Pivot Position** to **Top**. 4. Set **Plane Lock** to **Off**. 9. In the **Material Settings** section, alternate between your **Mud**, **Leafy Grass**, **Ground**, **Sand**, **Salt**, **Slate**, and **Snow** custom materials, then in the viewport, draw on the remaining area. 10. Using your asset library, 1. Add a **FloatingIsland** asset with a `Datatype.CFrame.Position` of `201.726, -14.315, 183.5` for a spawn area users join before they separate into teams. 2. Intersperse rock assets with varying `Class.MeshPart.Size` and `Datatype.CFrame.Orientation` values along the border of the playable area. 3. Intersperse foliage assets around the outdoor space to add more organic life to the outdoor space. ## Contain the playable area If you playtest your experience and navigate off the island, your character will fall until they reach the project's `Class.Workspace.FallenPartsDestroyHeight` and respawn back in your team's spawn zone. To ensure that users cannot fall off the island, or join the match too early from the floating area spawn zone before they separate into teams, you must contain the playable area with an invisible barrier. The values in this section are highly dependent on the terrain within the final sample laser tag environment. It's recommended to playtest and verify that there are no gaps in the barrier according to the custom terrain within your own experience. #### Create your own To contain the playable area in your experience: 1. Using **block** parts, surround the playable area in your outdoor area. It's important to ensure these parts are tall enough to block players from jumping over your barrier, and that there are no cracks they can slip through and fall off the edge of your map. 2. Select these parts, then in the **Properties** window, 1. Set **Transparency** to `1`. 2. Disable **CanCollide**. 3. Enable **Anchored**. #### Recreate the sample To exactly recreate the containment of the playable area within the sample [Environment Art - Constructing](https://www.roblox.com/games/14447826396/Environment-Art-Constructing) place file: 1. Add and configure the following **block** parts to surround the playable area. The top containment part in the following image is semi-transparent for visual reference. | Size | CFrame.Position | CFrame.Orientation | | --- | --- | --- | | `69, 71, 17` | `12.5, 23, -47.5` | `0, 0, 0` | | `89, 71, 22` | `39, 23, -18` | `0, -45, 0` | | `83, 71, 22` | `70.5, 23, 46` | `0, -90, 0` | | `54, 71, 42` | `93, 23, 95` | `0, -50, 0` | | `129, 71, 85` | `137, 23, 179` | `0, -90, 0` | | `155, 71, 22` | `55, 23, 292` | `0, -135, 0` | | `29, 71, 22` | `-7.5, 23, 337` | `0, 180, 0` | | `34, 71, 6` | `182, 23, 165` | `0, -135, 0` | | `34, 71, 6` | `196.5, 23, 157` | `0, 180, 0` | | `34, 71, 6` | `212.5, 23, 160` | `0, 135, 0` | | `34, 71, 6` | `224, 23, 181` | `0, -90, 0` | | `34, 71, 6` | `216, 23, 198` | `0, -135, 0` | | `34, 71, 6` | `195, 23, 203` | `0, 180, 0` | | `34, 71, 6` | `181, 23, 194` | `0, 135, 0` | | `379, 32, 389` | `41, 42, 153` | `0, 180, 0` | 2. Select these parts, then in the **Properties** window, 1. Set **Transparency** to `1`. 2. Disable **CanCollide**. 3. Enable **Anchored**. ## Configure special effects Your building's architecture and outdoor space are nearly complete with assets from your asset library, but you may have noticed either consciously or unconsciously that the overall environment feels static. This is because realistic environments include movement from many different sources, whether that's from clouds crawling through the sky, wind brushing through greenery, or life forms navigating the space. You can mimic realistic environments by configuring special effects that add dynamic movement to your environment, and provoke emotion from users. This process often makes worlds feel more alive, supporting a deep sense of immersion while people access your experience. ### Background clouds The first type of special effect the final sample laser tag environment uses to add movement to the 3D space is an effect called dynamic clouds. [Dynamic clouds](/docs/en-us/environment/clouds.md) are realistic clouds that drift slowly across the sky, and they are useful for creating movement that feels far away from the user while still present in the environment. You can adjust their appearance through the `Class.Clouds` object to create unique atmospheres, and customize their direction and speed through global wind. _Without dynamic clouds_ _With default dynamic clouds_ #### Create your own To add and configure your own dynamic clouds in the background: 1. In the **Explorer** window, add a **Clouds** object to the **Terrain** object. 1. Hover over the **Terrain** object and click the **⊕** button. A contextual menu displays. 2. From the contextual menu, insert a **Clouds** object. 2. Select the **Clouds** object, then in the **Properties** window, 1. Set **Cover** to a value between `0` (no clouds) and `1` (full cloud cover). 2. Set **Density** to a value between `0` (light, translucent clouds) and `1` (heavy, dark clouds). 3. Set **Color** to a material color of cloud particles according to the art style of your experience. #### Recreate the sample To add and configure dynamic clouds in the background: 1. In the **Explorer** window, add a **Clouds object** to the **Terrain** object. 1. Hover over the **Terrain** object and click the **⊕** button. A contextual menu displays. 2. From the contextual menu, insert a **Clouds** object. 2. Select the **Clouds** object, then in the **Properties** window, 1. Set **Cover** to `0.625`. 2. Set **Density** to `0.5`. 3. Set **Color** to `143, 143, 143`. ### Foreground clouds The second type of special effect the final sample laser tag environment uses to add movement to the 3D space is an effect called particle emitters. [Particle emitters](/docs/en-us/effects/particle-emitters.md) emit 2D images, or particles, that look and behave for the duration of their lifetime according to the particle emitter's set properties, and they are useful for creating movement that feels and often is close to the user. Particle emitters have many properties you can adjust to achieve both subtle and bold effects. The following instructions show you how to exactly recreate the foreground clouds within the sample [Environment Art - Constructing](https://www.roblox.com/games/14447826396/Environment-Art-Constructing) place file, but you can modify any property for the needs of your own experience. It's recommended to keep these clouds large and with a gentle motion between the gameplay space and the static background. To add and configure foreground clouds: 1. Add a **block** part to a position where you want users to see a floating cloud. 2. Select this **block** part, then in the **Properties** window, 1. Set **Transparency** to `1`. 2. Disable **CanCollide**. 3. Enable **Anchored**. 3. Create a particle emitter within this part. 1. In the **Explorer** window, hover over the **block** part and click the **⊕** button. A contextual menu displays. 2. From the contextual menu, insert a **ParticleEmitter**. The particle emitter immediately emits particles within the part's area. 4. Select this particle emitter, then in the **Properties** window, 1. Set **Color** to a color sequence that looks like the following image. 2. Set **LightEmission** to `0.3` to blend the colors of the texture and its environment. 3. Set **LightInfluence** to `0.2` to allow environment light to have a subtle effect on the color of particles. 4. Set **Orientation** to **FacingCameraWorldUp** so particles always emit up toward the sky. 5. Set **Size** to `100` to create large clouds. 6. Set **Squash** to `-0.25` to shrink particles horizontally. 7. Set **Texture** to an image of a cloud. The sample uses `rbxassetid://10714362544`. 8. Set **Transparency** to a number sequence that looks like the following image. 9. Set **Lifetime** to `30` to fade out particles at 30 seconds. 10. Set **Rate** to `0.25` to emit particles slowly. 11. Set **Acceleration** to `0, -0.8, 0` to impact particle speed over their lifetime. 12. Set **Drag** to `0.1` to make particles look their speed over time. 13. Enable **LockedToPart** to keep particles close to the particle emitter. 5. Repeat this process, moving cloud particle emitters throughout the environment. In addition, you can layer multiple particle emitters together to give clouds more depth. For example, in the final sample laser tag environment, foreground clouds layer an additional particle emitter with a **Color** property of `248, 248, 248`, **Texture** property of `rbxassetid://10714433747`, and a **Rate** property of `0.1`. You can find both of these particle emitters in the sample [Environment Art Asset Library](https://www.roblox.com/library/14447738661/Environment-Art-Asset-Library). ### Dust particles Particle emitters are such a versatile type of special effect because they offer so many properties you can customize to create interesting visual effects, such as glowing portals, green billowing smoke, or vibrant explosions. The final sample laser tag environment uses particle emitters again in this section to create floating dust particles that surround the user as they navigate the outdoor space. Similar to the foreground clouds steps, the following instructions show you how to exactly recreate the dust particles within the sample [Environment Art - Constructing](https://www.roblox.com/games/14447826396/Environment-Art-Constructing) place file, but you can modify any property for the needs of your own experience. It's recommended to keep particle speed slow to only provide micro motion within the environment. To add and configure dust particles for the outdoor space: 1. Add a **block** part to the environment that covers the entire island. 2. Select this **block** part, then in the **Properties** window, 1. Set **Transparency** to `1`. 2. Disable **CanCollide**. 3. Enable **Anchored**. 3. Create a particle emitter within this part. 1. In the **Explorer** window, hover over the **block** part and click the **⊕** button. A contextual menu displays. 2. From the contextual menu, insert a **ParticleEmitter**. The particle emitter immediately emits particles within the part's area. 4. Select this particle emitter, then in the **Properties** window, 1. Set **Color** to `192, 241, 255` to give the particles a light blue hue. 2. Set **Size** to a number sequence that looks like the following image. 3. Set **Texture** to a dust mote image. The sample uses `rbxassetid://14302399641`. 4. Set **Transparency** to a number sequence that looks like the following image. 5. Set **ZOffset** to `-5` to move particles away from the camera. 6. Set **Lifetime** to `1, 10` to set the minimum and maximum age of a particle to 1 and 10 seconds, respectively. 7. Set **Rate** to `50000` to create many particles for your environment. 8. Set **RotSpeed** to `-60` to create a range of speeds for newly emitted particles. 9. Set **Speed** to `1, 5` to set the minimum and maximum speed of a particle to 1 and 5 studs per second, respectively. 10. Set **Acceleration** to `1, -1, 1` to impact particle speed over its lifetime. 11. Enable **LockedToPart** to keep particles close to the particle emitter. ## Configure lighting sources Now that your environment has movement, the final step in constructing your environment is to configure lighting sources. Studio offers two high-level types of lighting sources: - **Global lighting** - Produces lighting for the entire outdoor environment. - **Local lighting** - Produces lighting around where you place them within your experience. Both lighting sources are important to consider because your experience has both an indoor and outdoor environment that impact the user's ability to see what is happening around them while they're in combat. ### Global lighting Global lighting is the luminescence from either the sun or moon in an experience. By adjusting a couple of key default properties in the `Class.Lighting` service, you can dramatically change how that light appears to users, as well as how it interacts with any other object you place in the experience. Studio begins every experience with `Enum.LightingStyle|Soft` lighting which renders a flatter look with softer lights and shadows. However, to enhance the environment and equip your local light sources to produce precise shadows and illumination, such as the lighting in the hallways and above the signage, you must enable `Enum.LightingStyle|Realistic` lighting. This allows both your global and local lighting to work together and provide more realistic and immersive visuals. For example, the `Enum.LightingStyle|Realistic` lighting style automatically detects when a user is either in an interior or exterior space, then it responds by enabling the appropriate lighting model. This means that reflections are able to reflect off the floor and ceiling within the building, providing a richer visual experience as users navigate through combat pockets. #### Create your own To configure your own global lighting: 1. In the **Explorer** window, select the **Lighting** service, then in the **Properties** window, set its properties to values that reflect the art style of your experience. For more information on these properties, see [Global lighting](/docs/en-us/environment/lighting.md). 2. In the **Explorer** window, select the **Lighting** service's child **Atmosphere** object, then in the **Properties** window, set its properties to values that reflect the art style of your experience. For more information on these properties, see [Atmospheric effects](/docs/en-us/environment/atmosphere.md). 3. **(Optional)** Apply one or more customizable filters, such as bloom, depth‑of‑field, or sun rays. For more information on these properties, see [Post-processing effects](/docs/en-us/environment/post-processing-effects.md). #### Recreate the sample To exactly recreate the global lighting configuration within the sample [Environment Art - Constructing](https://www.roblox.com/games/14447826396/Environment-Art-Constructing) place file: 1. In the **Explorer** window, select the **Lighting** service, then in the **Properties** window, 1. Set **Ambient** to `26, 34, 36`. 2. Set **OutdoorAmbient** to `26, 34, 36`. 3. Set **LightingStyle** to **Realistic**. 4. Set **ShadowSoftness** to `0.15`. 5. Set **GeographicLatitude** to `-18`. 6. Set **TimeOfDay** to `-15:16:23`. 2. In the **Explorer** window, select the **Lighting** service's child **Atmosphere** object, then in the **Properties** window, 1. Set **Density** to `0.285`. 2. Set **Offset** to `0.65`. 3. Set **Decay** to `254, 254, 254` 4. Set **Glare** to `0.3`. 5. Set **Haze** to `2`. 3. In the **Explorer** window, select the **Lighting** service's child **Bloom** object, then in the **Properties** window, 1. Set **Intensity** to `1.5`. 2. Set **Size** to `56`. 4. In the **Explorer** window, select the **Lighting** service's child **DepthOfField** object, then in the **Properties** window, set **FarIntensity** to `0.05`. ### Local lighting Local lighting is the luminescence from local [light sources](/docs/en-us/effects/light-sources.md) in your experience, such as `Class.SpotLight`, `Class.SurfaceLight`, and `Class.PointLight` objects. It's important to analyze the needs of your experience to know which type of local light source to use. For example, the final sample laser tag environment needs lighting to help users see the different areas inside of the building while they're in combat, as well as lighting to differentiate the different spawn zone areas. Local light sources create points of reference and directionality for users. For example, the sample uses surface lights to inform users if they are navigating the perimeter or are near the blue-green or pink spawn zone, and spotlights to highlight doorways. > **Info:** Applying light sources is an art form, and it takes time to figure out what lighting configurations work to meet the needs of your experience. It's normal and expected for your lighting to look and feel different from the final sample laser tag environment. #### Create your own To configure your own local lighting: 1. Add and configure glowing lights around the perimeter hallways that provide orientation cues to users navigating around the building. 1. Add and position a **block** part near the top of one of your perimeter hallways, then in the **Properties** window, 1. Set **Color** to a bright color. 2. Set **Material** to **Neon**. 3. Enable **Anchored**. 2. In the **Explorer** window, add a **SurfaceLight** object to the part. 1. Hover over the part and click the **⊕** button. A contextual menu displays. 2. From the contextual menu, insert a **SurfaceLight** object. 3. Select the **SurfaceLight** object, then in the **Properties** window, 1. Set **Color** to a bright color. 2. Set **Face** to the direction you want the light to shine. 3. Set **Range** to a value that reflects how far you want your light to reach. 4. Repeat this process, positioning and orienting parts to complete the glowing perimeter lights. 2. Add and configure small hallway interior lights that illuminate cross lanes. 1. Add and position a **block** part near the top of one of your cross lane hallways, then in the **Properties** window, 1. Set **Color** to a bright color. 2. Set **Material** to **Neon**. 3. Enable **Anchored**. 2. In the **Explorer** window, add a **SpotLight** object to the part. 1. Hover over the part and click the **⊕** button. A contextual menu displays. 2. From the contextual menu, insert a **SpotLight** object. 3. Select the **SpotLight** object, then in the **Properties** window, 1. Set **Angle** to a value that reflects the angle you want your light to shine. 2. Set **Face** to the direction you want the light to shine. 3. Set **Range** to a value that reflects how far you want your light to reach. 4. Repeat this process, positioning and orienting parts above doors and hallways until you are happy with the interior lighting. 3. Add and configure the spawn zone lights. 1. Add a **block** part that is the size of your left spawn zone, and position it above the spawn zone's ceiling. 2. In the **Properties** window, 1. Set **Color** to the color theme of the team on the left of the map. 2. Set **Material** to **Neon**. 3. Enable **Anchored**. 1. Repeat this process for the right spawn zone. 1. **(Optional)** Add and anchor light fixtures around the interior and exterior of the building from your own asset library. #### Recreate the sample To exactly recreate the local lighting configuration within the sample [Environment Art - Constructing](https://www.roblox.com/games/14447826396/Environment-Art-Constructing) place file: 1. Add and configure the glowing perimeter lights. 1. Add a **block** part for one of the glowing perimeter lights, then in the **Properties** window, 1. Set **Color** to `199, 166, 147`. 2. Set **Material** to **Neon**. 3. Set **Size** to `89.254, 1, 1`. 4. Set **CFrame.Position** to `-78.297, 21, 293.439`. 5. Enable **Anchored**. 2. In the **Explorer** window, add a **SurfaceLight** object to the part. 1. Hover over the **block** part and click the **⊕** button. A contextual menu displays. 2. From the contextual menu, insert a **SurfaceLight** object. 3. Select the **SurfaceLight** object, then in the **Properties** window, 1. Set **Color** to `211, 190, 150`. 2. Set **Face** to **Bottom**. 3. Set **Range** to `21`. 4. Repeat this process, positioning and orienting parts to complete the glowing perimeter lights. 2. Add and configure the small hallway interior lights. 1. Add a **block** part for one of the small hallway interior lights, then in the **Properties** window, 1. Set **Color** to `163, 162, 165`. 2. Set **Material** to **Neon**. 3. Set **Size** to `0.25, 0.25, 1`. 4. Set **CFrame.Position** to `-53.962, 19.936, 291.932`. 5. Enable **Anchored**. 2. In the **Explorer** window, add a **SpotLight** object to the part. 1. Hover over the **block** part and click the **⊕** button. A contextual menu displays. 2. From the contextual menu, insert a **SpotLight** object. 3. Select the **SpotLight** object, then in the **Properties** window, 1. Set **Angle** to `135`. 2. Set **Face** to **Bottom**. 3. Set **Range** to `20`. 4. Repeat this process, positioning and orienting parts above doors and hallways until you are happy with the interior lighting. 3. Add and configure the spawn zone lights. 1. Add a **block** part for the left spawn zone, then in the **Properties** window, 1. Set **Color** to `88, 218, 171`. 2. Set **Material** to **Neon**. 3. Set **Size** to `62.5, 1, 37.5`. 4. Set **CFrame.Position** to `-77, 20.6, 321`. 5. Enable **Anchored**. 1. Add a **block** part for the right spawn zone, then in the **Properties** window, 1. Set **Color** to `255, 170, 255`. 2. Set **Material** to **Neon**. 3. Set **Size** to `62.5, 1, 37.5`. 4. Set **CFrame.Position** to `-77, 20.6, 1`. 5. Enable **Anchored**. 4. **(Optional)** Using the sample [Environment Art Asset Library](https://www.roblox.com/library/14447738661/Environment-Art-Asset-Library), add and anchor light fixtures around the interior and exterior of the building. For example, the final sample laser tag environment uses **LightFixtureTall**, **LightFixtureShort**, **TouchScreenA**, and **Roblox Sign** lighting fixtures to complete the environment. Once you are happy with the look and feel of your environment, you can move on to configure your assets and Studio settings to keep your frame rate and performance levels high for low-end devices. This process ensures that nearly every user has the same gameplay and visual experience while they're viewing your environment. --- title: "Develop polished assets" url: /docs/en-us/tutorials/curriculums/environmental-art/develop-polished-assets last_updated: 2026-06-29T19:34:13Z description: "Explains the high-level concepts regarding how to design and develop polished assets." --- # Develop polished assets **Developing polished assets** is the process of planning and creating high-quality assets to replace or convert the greybox environment and meet your experience's aesthetic goals and design requirements. After you have the basis for how you want to lay out your assets in the 3D space from the greybox process, it's much easier to visualize what assets are necessary to bring your environment to life. Using the [Environment Art Asset Library](https://www.roblox.com/library/14447738661/Environment-Art-Asset-Library) as a reference, this section of the environmental art curriculum shows you how to prepare what you need to turn your basic greybox layout into a futuristic laser tag environment, including guidance on: - Choosing an art style to influence every decision you make for your assets in the design process. - Designing tileable textures, trim sheets, a modular kit, and props that are cohesive with one another, and in accordance with your art style. After you complete this section, you will learn how to pull all of your polished assets into the 3D space with additional terrain, light sources, and special effects to add character and storytelling elements to your environment. ![A polished group of assets that make up an entryway.](../../../assets/tutorials/environmental-art-curriculum/Section2/Overview.png) > **Info:** It's important to note the process between polishing assets and constructing your environment hardly ever looks like a straight line from your original idea to the final art. Iteration between each step and across steps is normal and almost always necessary in the development process. ## Choose an art style Choosing an art style at the start of your design process allows you to have a main source of truth for the aesthetic treatment of your assets. You can treat it as your visual ground rules, constantly referencing it as you design your assets to ensure they always inform users of what you want your environment to communicate. An effective art style provides users information about the world they're playing in, such as its climate, rich history, or relationship to technology. Many developers choose to create a mood or theme board, then add as many pictures as you need until you feel that you have a visual representation of what you want your world to look like. For example, the final sample laser tag environment's art style references the following images to pull references for the focal elements of the 3D space, such as lush foliage, rounded frames, rectangular concrete forms, and stylistically uniform metal adornments. Although users may never see your reference images, they will feel the impact of their influence in the cohesiveness of your world's identity. ![A collage of reference images for the sample laser tag experience.](../../../assets/tutorials/environmental-art-curriculum/Section2/Art-Style.png) ## Design textures Now that you have an art style for your experience, it's time to review your layout and figure out what textures you need to create materials for the 3D space. Environments often require textures that can cover large surfaces, as well as textures that can provide a level of detail to your assets without adding additional geometry. For example, your greybox environment needs textures that can cover the outdoor space, such as moss, flowers and stones, as well as textures to provide details to the combat pockets, such as panels and bolts to frame doorways. There are two high-level texturing methods that you can use to meet these requirements: tileable textures and trim sheets. The following sections provide information and guidance on both texture methods, including what to consider as you find textures on the [Creator Store](/docs/en-us/production/creator-store.md), or design them in third-party tools. ### Tileable textures **Tileable textures** are textures that tile both on the X **and** Y axis, and they allow you to cover large surfaces in your environment, such as floors, walls, and terrain. This type of texture requires minimal manual adjustments after you apply them to block `Class.Part|Parts` because blocks have flat surfaces, meaning you don't need to account for how the texture might deform on more complex geometry. In addition, you can import tileable textures `Class.MaterialVariant` objects within the `Class.MaterialService` to apply to your terrain, which is a process you will learn in [Create custom materials](/docs/en-us/tutorials/curriculums/environmental-art/assemble-an-asset-library.md#create-custom-materials) in the next section of this tutorial. ![A top-down view of a block part with a tileable moss with flowers material applied.](../../../assets/tutorials/environmental-art-curriculum/Section2/TileableTextures-Part.png) The most fundamental rule for tileable textures is that they **cannot have a seam**, otherwise where the texture begins and ends on a surface is noticeable to users, which can break their sense of immersion in the experience. It's for this reason that tileable textures work well for natural materials, such as grass and stone, which are seamless by nature in the real world. In fact, the laser tag sample experience uses the following four organic tileable textures to represent natural materials for the outdoor space, which you can use in the [world building](/docs/en-us/tutorials/curriculums/environmental-art/construct-your-world.md) section of this tutorial. ![A sphere part with a tileable moss material applied.](../../../assets/tutorials/environmental-art-curriculum/Section2/TileableTextures-Moss.png)_Moss_ ![A sphere part with a tileable moss with flowers material applied.](../../../assets/tutorials/environmental-art-curriculum/Section2/TileableTextures-MossFlowers.png)_MossFlowers_ ![A sphere part with a tileable moss with stones material applied.](../../../assets/tutorials/environmental-art-curriculum/Section2/TileableTextures-MossStones.png)_MossStones_ ![A sphere part with a tileable stones material applied.](../../../assets/tutorials/environmental-art-curriculum/Section2/TileableTextures-Stones.png)_Stones_ Notice how all of these sample tileable textures have equal visible distribution of their moss, flower, and rock elements. If you were to include any distinctive elements, such as a massive rock or a patch of flowers, it would attract user attention to the fact that the texture is repeating. For example, the first image in the following two images has a noticeable patch of dirt that pulls the eye to the texture's repetition. The second image makes this patch much smaller, which balances the texture's elements, and makes its repetition much less noticeable. ![A tileable rock texture with a distinct patch of rocks highlighted to show that the repetition of the distinct element is noticeable.](../../../assets/tutorials/environmental-art-curriculum/Section2/TileableTextures-WithElement.jpg) ![A tileable rock texture with no distinct elements to show that this technique makes the repetition of elements not noticeable.](../../../assets/tutorials/environmental-art-curriculum/Section2/TileableTextures-NoElement.jpg) If you decide to design your own tileable textures in third-party modeling tools like [Substance Designer](https://www.adobe.com/products/substance3d-designer) or [Blender](https://www.blender.org/), keep the following in mind: - You can create textures as large as 1024x1024, but the closer you get to this maximum size, the higher the performance cost. - Create equal visible distribution so that no one element is more distinguishable than others. - Even if a tileable texture is technically seamless, review the overall image to ensure it doesn't have an element that is so pronounced that the texture's repetition is noticeable. The previous point is near impossible to remove entirely, but you can set Studio material to tile organically, or add in additional decal overlays to hide the repetition effectively. For more information on these techniques, see [Assemble an asset library - Creating custom materials](/docs/en-us/tutorials/curriculums/environmental-art/assemble-an-asset-library.md#create-custom-materials) and [Assemble modular environments - Reduce visible repetition](/docs/en-us/tutorials/use-case-tutorials/modeling/assemble-modular-environments.md#reduce-visible-repetition), respectively. ### Trim sheets **Trim sheets** are textures that tile on either the X **or** Y axis, and they allow you to add significantly more visual complexity to your experiences without having to import additional textures, saving you a negative impact on memory. Each row or column of a trim sheet has a unique visual appearance, giving you many different surface treatments to choose from when you're mapping UV data to a mesh. For example, the door frame and ceiling assets in the following two images use different layers of the same trim sheet to add detail work to the space. ![An doorway with trim sheet textures applied.](../../../assets/tutorials/environmental-art-curriculum/Section2/TrimSheets-Doorway.jpg) ![A group of ceiling tiles with trim sheet textures applied.](../../../assets/tutorials/environmental-art-curriculum/Section2/TrimSheets-Ceiling.jpg) The most fundamental rule for trim sheets is to avoid contextual details that you can only apply to a single object. This is because trim sheets must have use for many types of objects in your world, and highly specific details are also distinguishable to users as they repeat in the 3D space. For example, in the following image from the [Mystery of Duvall showcase](/docs/en-us/resources/the-mystery-of-duvall-drive/materialize-the-world.md#surface-appearance-and-trim-maps), the left-side furniture set's trim sheet includes more stain detail than the right-side furniture set's trim sheet. Notice how the extra stain detail makes its repetition prominent in comparison to the furniture set on the right. ![The same furniture set has different trim sheets applied. The furniture set on the left with more distict stain elements has noticeable repetition.](../../../assets/resources/mystery-of-duvall-drive/materializing-the-world/furniture-set-stain.png) Following this fundamental rule, the final sample laser tag environment uses the following trim sheet texture maps with six rows of simple detail work to add visual interest and cohesion to its modular kit and props. You can use this [trim sheet](../../../assets/tutorials/environmental-art-curriculum/Section2/TrimSheetTextureMaps.zip) for UV unwrapping in third-party modeling tools, then utilize its texture maps in a `Class.SurfaceAppearance` object when you assemble your asset library. For more information on what each of these texture maps provide to meshes, see [PBR textures - Texture maps](/docs/en-us/art/modeling/surface-appearance.md#texture-maps). > **Info:** This trim sheet file is only useful for UV unwrapping processes in third-party modeling tools. If you try to upload its texture maps to a `Class.SurfaceAppearance` object without completing a UV unwrapping process, there isn't any data for Studio to map to the texture itself. ![The sample laser tag experience's trim sheet.](../../../assets/tutorials/environmental-art-curriculum/Section2/Trimsheet.png) ![The sample laser tag experience's trim sheet's albedo texture map.](../../../assets/tutorials/environmental-art-curriculum/Section2/TrimSheets-Albedo.png)_Albedo_ ![The sample laser tag experience's trim sheet's normal texture map.](../../../assets/tutorials/environmental-art-curriculum/Section2/TrimSheets-Normal.png)_Normal_ ![The sample laser tag experience's trim sheet's roughness texture map.](../../../assets/tutorials/environmental-art-curriculum/Section2/TrimSheets-Roughness.png)_Roughness_ ![The sample laser tag experience's trim sheet's metalness texture map.](../../../assets/tutorials/environmental-art-curriculum/Section2/TrimSheets-Metalness.png)_Metalness_ If you decide to design your own trim sheets in third-party modeling tools like [Substance Designer](https://www.adobe.com/products/substance3d-designer), [Blender](https://www.blender.org/), or [ZBrush](https://www.maxon.net/en/zbrush), keep the following in mind: - You can create textures as large as 1024x1024, but the closer you get to this maximum size, the higher the performance cost. - The trim sheet must be equal in length both along the X and Y axis. - You can make different design choices to include more contextual details, but once the details are on the trim sheet, you cannot easily hide them. - Export your trim sheet's texture maps in a location that is easy to reference when you're creating an asset library. For more information on this technique, see [Building architecture - Creating trim sheets](/docs/en-us/resources/beyond-the-dark/building-architecture.md#create-trim-sheets). ## Design modular kits Modular kits are sets of assets that seamlessly snap together to create variations of a larger complex object. Designing and using modular kits as a part of your development process is useful because it means you don't need to manually create each individual asset in your experience. Instead, you only need to create a few assets that you can reuse and customize to create variety throughout the scene. Not only does this process significantly speed up how quickly you can replace or convert the greybox environment, but when you UV wrap a trim sheet onto the meshes in your modular kit for detail work, it can also help each distinct object throughout the experience feel cohesive. For example, the final sample laser tag environment utilizes a single **SA_EC_Trim_Metal_A** trimsheet to UV wrap onto all of its modular assets that create the overall building where most of the gameplay takes place, including assets that accommodate a rise in elevation between the first and second floor. ![The sample laser tag experience's modular kit.](../../../assets/tutorials/environmental-art-curriculum/Section2/DesigningModularKits-ModularKit.jpg) Each asset in this modular kit has a consistent pivot point location either at the forward-most, lower corner, or in a location that allows them to snap to a logical position on the building in 5 stud increments when you enable grid snapping, such as trim pieces onto walls, or doors into their doorway position. In addition, each asset is at least 5 studs tall and wide, so there is never any clashing geometry, even when you rotate and move the assets. For more information on this concept, see [Assemble modular environments - The importance of consistent pivot point locations](/docs/en-us/tutorials/use-case-tutorials/modeling/assemble-modular-environments.md#the-importance-of-consistent-pivot-point-locations). You can use or modify the [sample modular kit](https://www.roblox.com/library/14447738661/Environment-Art-Asset-Library) for replacing graybox geometry in the [world building](/docs/en-us/tutorials/curriculums/environmental-art/construct-your-world.md) section of this tutorial. However, if you decide to design your own modular kit in third-party modeling tools like [Blender](https://www.blender.org/) or [Maya](https://www.autodesk.com/products/maya/overview), keep the following in mind: - It's of vital importance that modular assets have a consistent pivot location so they can connect together at predetermined, incremental distances relative to one another when you enable grid snapping. - Each asset must have a maximum height and width that is divisible by the smallest height and width in the kit so they can move and rotate in incremental distances. For example, the sample kit's tallest and widest asset is 15 by 5 studs, which is divisible by the smallest asset of 5 by 5 studs. - It's useful to separate larger assets like hallways into separate meshes under a model with careful naming conventions, such as a hallway `Class.Model` with child **UpperTrim**, **LowerTrim**, and **Wall** `Class.MeshPart` objects. This allows you to customize each mesh of the overall model, such as modifying their individual material, collision, and rendering parameters, or removing unnecessary geometry according to where the model is within the experience. No matter what modular kit you use, it's important to test often and look at your assets from multiple perspectives to see if there is any clashing geometry. If there is, it often means that one of the asset's pivot point locations isn't relatively consistent with the other. ## Design props Props are non-modular assets that enhance an environment's level of visual storytelling while providing users important context about the world they're in. For example, if users are exploring a cave and see props like glowing orbs and skeletons, they can infer that their environment is both magical and dangerous, so it might be wise to proceed with caution. Props are most effective when they connect to larger themes in your experience's art style. For example, the final sample laser tag environment utilizes props to provide users context regarding the environment's relationship to technology and nature. In particular, the props kit includes clean, high-tech panels, crates, fire extinguishers, and security cameras alongside large rocks and a variety of plants, informing users that the world values technological advances, but not at the expense of the earth. ![The sample laser tag experience's prop kit.](../../../assets/tutorials/environmental-art-curriculum/Section2/DesigningProps-PropsKit.png) Unlike modular assets, props don't need to have consistent pivot point locations because they don't need to snap together to create a larger complex object. However, they do need to have logical pivot point locations according to where you need to place them in the 3D space. For example, in the following image, the fire extinguisher prop has a pivot point location on the back of the object so it can snap onto walls, and the crate has a pivot point location on the bottom of the object so it can snap onto floors. This allows for universal contextual placement, no matter the respective wall or floor. You can use or modify props from the [Environment Art Asset Library](https://www.roblox.com/library/14447738661/Environment-Art-Asset-Library) in the world building section of this tutorial. However, if you decide to design your own props in third-party modeling tools like [Blender](https://www.blender.org/) or [Maya](https://www.autodesk.com/products/maya/overview), keep the following in mind: - Users often see props from multiple angles, so it's important to include enough detail for users to understand what the prop is, but not too much detail so that it impacts performance. A good rule is to only include enough geometry that users can tell what a prop is from its silhouette. For more information on this concept, see [Developing a moving world - Setting up the eye of the storm](/docs/en-us/resources/the-mystery-of-duvall-drive/develop-a-moving-world.md). - You can use the same trim sheet you used for your modular assets so that you make efficient use of textures that are already within your environment, and so that every asset remains stylistically cohesive. - Distinct visual markers in props are more noticeable the more you reuse them in your environment. Once you have all of the high-quality assets you need to replace or convert the greybox environment, you can move on to the next section of the tutorial to learn how to assemble an asset library within your place file that you can use to turn your greybox environment into an aesthetically pleasing setting. --- title: "Greybox your environment" url: /docs/en-us/tutorials/curriculums/environmental-art/greybox-your-environment last_updated: 2026-06-29T19:34:13Z description: "Explains how to greybox the laser tag environment using basic parts." --- # Greybox your environment **Greyboxing your environment**, also known as massing out or blocking your environment, is the process of adding simple shapes to the 3D space to figure out how users will experience gameplay before investing time into polishing assets. This process is crucial for finding issues in the layout of playable areas, such as difficult regions to traverse, unfair advantages from certain vantage points, or assets with disproportionate scale to the user's character. Using the [Environment Art - Greyboxing](https://www.roblox.com/games/14447721254/Environment-Art-Greyboxing) `.rbxl` file as a reference, this section of the environmental art curriculum shows you how to greybox a standard three lane map layout for a team-based first-person laser tag shooter experience, including step-by-step instructions on: - Creating playable areas that encourage strategic combat for multiple playstyles. - Applying placeholder materials that help inform users of their position and orientation. - Testing the layout to ensure it's fun and doesn't include playability issues. After you complete this section, you will learn how to develop high-quality assets to replace or convert the greybox environment, and meet your experience's game design requirements. ![An angled top-down view of the final greybox environment with placeholder materials to mark unique sections of the map.](../../../assets/tutorials/environmental-art-curriculum/Section1/PlaceholderMaterials.jpg) ## Three lane map layout The **three lane map layout** is a first-person shooter map layout that includes a spawn zone for each team on opposite sides of the map, three primary lanes that each team can use to travel to either spawn zone, and cross lanes that allow for users to travel from one primary lane to another. This type of map layout is a common layout for first-person shooter experiences because it quickly places users into combat zones as soon as they join a match, and it allows for a variety of playstyles depending on which primary lane users choose to follow. The following sections explain each component of the three lane map layout, including considerations of how each component works together to create intentional gameplay interactions within first-person shooter experiences. ### Spawn zones **Spawn zones** are areas of the map where users either join their team at the start of a match, or rejoin the gameplay after their health reaches zero. At minimum, each team needs to have a central spawn zone when users first join the experience. Many developers place these central spawn zones at opposite ends of their maps to allow users time to navigate the experience at the start of the match before seeing the enemy team. ![A top-down view of the baseplate with each team's spawn zone highlighted on opposite sides of the map.](../../../assets/tutorials/environmental-art-curriculum/Section1/ThreeLaneLayout-SpawnZones.jpg) In addition, many team-based first-person shooter experiences also include spawn zones throughout the map that users can randomly respawn at once their health reaches zero. The placement of these decentralized spawn zones can significantly increase the experience's difficulty, especially when you place spawn zones near areas with heavy combat. To keep the gameplay simple, the sample laser tag greybox environment only includes one central spawn zone for each team. ### Primary lanes **Primary lanes** are paths that extend the length of the map from one spawn zone to another. The three lane map layout includes three primary lanes, and how developers conceptualize them is often dependent on the overall environmental context of the experience. For example, because the sample laser tag map has both an indoor and outdoor environment, the names of the primary lanes are as follows: - **Interior** - The innermost lane that's furthest from the entrance of the building. - **Middle** - The lane that's in-between the interior and exterior primary lanes. - **Exterior** - The lane closest to the entrance of the building that's partially indoors and outdoors. For most first-person shooter experiences using the three lane map layout, the middle primary lane intersects with areas of the map that contain the most combat because users traveling the middle primary lane can be attacked from both the interior or exterior primary lanes, while users traveling the interior or exterior primary lanes can only be attacked from the middle primary lane. ![A top-down view of the baseplate with three primary lanes highlighted between each team's spawn zone on opposite sides of the map.](../../../assets/tutorials/environmental-art-curriculum/Section1/ThreeLaneLayout-PrimaryLanes.jpg) ### Cross lanes **Cross lanes** are paths that intersect all of the primary lanes, extending from the interior to the exterior primary lane. These are the paths that users can use to travel from one primary lane to another, and they often contain minimal obstructions to help users take cover from enemy fire. This is because coverless zones create transitory spaces that encourage users to not stay in one place for too long without it being dangerous. ![A top-down view of the baseplate with three primary lanes between each team's spawn zone on opposite sides of the map, as well as five cross lanes that intersect with the primary lanes.](../../../assets/tutorials/environmental-art-curriculum/Section1/ThreeLaneLayout-CrossLanes.jpg) Similar to the reason why the most combat occurs around the middle primary lane, areas where cross lanes intersect with the middle primary lane, or **combat pockets**, contain the most combat because they allow users traveling from the interior or exterior lanes to access and shoot into the middle lane, and users traveling the middle lane can shoot in the direction of either the interior or exterior lane. ![A top-down view of the baseplate with three primary lanes between each team's spawn zone on opposite sides of the map, as well as five cross lanes that intersect with the primary lanes. The intersections between the primary and cross lanes are highlighted.](../../../assets/tutorials/environmental-art-curriculum/Section1/ThreeLaneLayout-IntersectingPoints.jpg) Building a three lane map layout with these combat pockets in mind allows you to segment the distance from spawn zones, and create intentional spaces where users have to interact with each other. This encourages users to engage in rapid gameplay and quickly become familiar with the map as they jump between primary lanes using cross lanes. ## Create playable areas Now that you are familiar with three lane map layouts, it's time to learn how to create the playable areas for the sample laser tag greybox environment that follows a three lane map layout. As you follow these instructions that exactly recreate the geometry within the sample [Environment Art - Greyboxing](https://www.roblox.com/games/14447721254/Environment-Art-Greyboxing) `.rbxl` file, you will start to see how it all works together to make up two spawn zones, three primary lanes, and five cross lanes that mirror across the middle of the map when you look at it from a top-down view. ![A top-down view of the final greybox geometry with three primary lanes between each team's spawn zone on opposite sides of the map, as well as five cross lanes that intersect with the primary lanes. The intersections between the primary and cross lanes are highlighted.](../../../assets/tutorials/environmental-art-curriculum/Section1/CreatingPlayableAreas-Intro1.jpg) > **Info:** The following instructions provide two different instructional paths: you can either insert parts to represent your own unique laser tag environment, or you can insert parts in a way that exactly recreates the greybox environment within the sample [Environment Art - Greyboxing](https://www.roblox.com/games/14447721254/Environment-Art-Greyboxing) place file. If you adjust the geometry to meet the specifications of your own experience, note that the sample file keeps every doorway and hallway at least 10 studs wide, and every wall at least 10 studs tall. These parameters ensure that two users are able to pass through hallways and doorways concurrently, no one is able to jump over walls with Roblox's default jump height of 5 studs, and that the camera can safely maneuver the map without interfering with geometry. ![In a doorway, two lizard avatars are stacked on top of each other on the left, and two blocky avatars are stacked on top of each other on the right. This demonstrates that only two characters can pass each other in a doorway, and they cannot jump higher than the walls.](../../../assets/tutorials/environmental-art-curriculum/Section1/CreatingPlayableAreas-Intro2.png) ### Floor geometry The first step in creating the laser tag greybox environment is to create the geometry for each of the following floors: - **Main Floor** – Spans the length of one spawn zone to another. - **Mezzanine Floor** – Spans half of the middle combat pocket, and provides a rise in elevation. - **Outdoor Floor** – Spans the outdoor space, and provides a drop in elevation. To help visualize the geometry of each floor you are creating for the experience, reference the following image that marks the main floor in yellow, the mezzanine floor in red, and the outdoor floor in blue. ![A top-down view of the final greybox environment with placeholder materials to mark the main, mezzanine, and outdoor floors.](../../../assets/tutorials/environmental-art-curriculum/Section1/Floor-Intro.jpg) It's important to have spaces in the experience with peaks and valleys because it allows you to control sight lines and engagement distances throughout the experience outside of horizontal movement. For example, if the entire playable space was a single floor with no change in elevation, every user would be able to interact with each other as long as there wasn't a wall in the way, meaning there is little ability for the user to develop a strategy outside of not being seen from other users. However, with peaks and valleys, you can decide where users can see each other. In addition, a rise in elevation creates both a physical and emotional sense of ascension, allowing users with a high ground to have a bird's eye view of the battlefield in order to get a better sense of where to travel next. When they're ready to move on, the drop in elevation creates both a physical and emotional sense of descension, pushing users to make quick decisions to maneuver around enemy lines of sight and achieve their goals. #### Create your own To create your own floor geometry: 1. Open Roblox Studio with a **Baseplate** template. 2. Navigate to Studio's **Home** or **Model** tab toolbar, then set [transform snapping](/docs/en-us/parts.md#transform-parts) to **5 studs** and **90°**. This allows you to equally position all greybox geometry on or away from each other in 5 stud increments. 3. Using **block** parts, create symmetrical left and right surfaces for the **main floor** of your building. This geometry represents the length of your playable interior, and its symmetry denotes the center of the map.![An angled top-down view of the main floor greybox geometry highlighted in yellow.](../../../assets/tutorials/environmental-art-curriculum/Section1/GeneralFloor-1.jpg) 4. Using **block** parts, create symmetrical left and right surfaces for the **mezzanine floor** of your building. This geometry represents the highest ground on the map.![An angled top-down view of both the main and mezzanine floors. The mezzanine floor greybox geometry is highlighted in yellow.](../../../assets/tutorials/environmental-art-curriculum/Section1/Floor-3.jpg) 5. Using **wedge** parts, create a **rise in elevation** between the main and mezzanine floors. This geometry breaks the lines of sight for players traveling the interior primary lane, or the cross lane in the middle of the map.![An angled top-down view of both the main, mezzanine floor, and rise in elevation parts. The rise in elevation greybox geometry is highlighted in yellow.](../../../assets/tutorials/environmental-art-curriculum/Section1/Floor-4.jpg) 6. Using **wedge** parts, create a **drop in elevation** between the main and outdoor floor. This geometry is your exterior primary lane, and it descends to the lowest point on the map.![An angled top-down view of both the main, mezzanine floor, and rise and drop in elevation parts. The drop in elevation greybox geometry is highlighted in yellow.](../../../assets/tutorials/environmental-art-curriculum/Section1/Floor-5.jpg) 7. Anchor all of your parts. #### Recreate the sample To exactly recreate the floor geometry within the sample [Environment Art - Greyboxing](https://www.roblox.com/games/14447721254/Environment-Art-Greyboxing) place file: 1. Open Roblox Studio with a **Baseplate** template. 2. Navigate to Studio's **Home** or **Model** tab toolbar, then set [transform snapping](/docs/en-us/parts.md#transform-parts) to **5 studs** and **90°**. This allows you to equally position all greybox geometry on or away from each other in 5 stud increments. 3. Add and configure a **block** part for the left-side surface of the **main floor**. You can use this same process for each step as you create all of the playable areas. 1. In the **Explorer** window, select the block, then in the **Properties** window, 1. Set **Size** to `105, 1, 185`. 2. Set **CFrame.Position** to `-77.5, 4.5, 252.5`.![An angled top-down view of the left-side of the main floor greybox geometry highlighted in yellow.](../../../assets/tutorials/environmental-art-curriculum/Section1/Floor-1.jpg) 4. Add and configure the following **block** parts for the **main floor**: | Part | Size | CFrame.Position | | --- | --- | --- | | Right-side floor | `105, 1, 185` | `-77.5, 4.5, 67.5` | | Middle left-side floor | `20, 10, 50` | `-15, 0, 185` | | Middle right-side floor | `20, 10, 50` | `-15, 0, 135` |![An angled top-down view of the main floor geometry. The right-side and middle portion of the main floor greybox geometry is highlighted in yellow.](../../../assets/tutorials/environmental-art-curriculum/Section1/Floor-2.jpg) 5. Add and configure the following **block** parts for the **mezzanine floor**: | Part | Size | CFrame.Position | | --- | --- | --- | | Left-side floor | `70, 5, 35` | `-110, 7.5, 177.5` | | Right-side floor | `70, 5, 35` | `-110, 7.5, 142.5` | | Left-side ledge | `10, 5, 10` | `-70, 7.5, 165` | | Right-side ledge | `10, 5, 10` | `-70, 7.5, 155` | | Ledge wall | `1, 3, 20` | `-65.5, 11.5, 160` |![An angled top-down view of both the main and mezzanine floors. The mezzanine floor greybox geometry is highlighted in yellow.](../../../assets/tutorials/environmental-art-curriculum/Section1/Floor-3.jpg) 6. Add and configure the following **wedge** parts for the **rise in elevation** between the main and mezzanine floor: | Part | Size | CFrame.Position | CFrame.Orientation | | --- | --- | --- | --- | | Left-side elevation | `15, 5, 10` | `-102.5, 7.5, 200` | `0, 180, 0` | | Right-side elevation | `15, 5, 10` | `-102.5, 7.5, 120` | `0, 0, 0` | | Middle left-side elevation | `15, 5, 10` | `-70, 7.5, 177.5` | `0, -90, 0` | | Middle right-side elevation | `15, 5, 10` | `-70, 7.5, 142.5` | `0, -90, 0` |![An angled top-down view of both the main, mezzanine floor, and rise in elevation parts. The rise in elevation greybox geometry is highlighted in yellow.](../../../assets/tutorials/environmental-art-curriculum/Section1/Floor-4.jpg) 7. Add and configure the following **wedge** part for the **drop in elevation** between the main and outdoor floor with a **Size** of `270, 11, 45`, **CFrame.Position** of `-2.5, -0.5, 160`, and a **CFrame.Orientation** of `0, -90, 0`.![An angled top-down view of both the main, mezzanine floor, and rise and drop in elevation parts. The drop in elevation greybox geometry is highlighted in yellow.](../../../assets/tutorials/environmental-art-curriculum/Section1/Floor-5.jpg) 8. Anchor all of these floor parts. ### Perimeter wall geometry The second step in creating the laser tag greybox environment is to create the geometry for perimeter walls of the building. This provides boundaries for the interior gameplay of the experience, as well as guidance to users on where they can go to engage with one another. To help visualize the geometry of the perimeter walls of the building you are creating for the experience, reference the following image that marks them in yellow. ![A top-down view of the final greybox environment with the perimeter wall geometry highlighted in yellow.](../../../assets/tutorials/environmental-art-curriculum/Section1/Perimeter-Intro.jpg) #### Create your own To create your own perimeter wall geometry: 1. Using **block** parts, create a **perimeter boundary** around your main floor except for the edge that opens to the exterior primary lane. This geometry prevents players from leaving your playable area while allowing them to enter and exit the building.![An angled top-down view of the floor and perimeter walls. The perimeter wall geometry is highlighted in yellow.](../../../assets/tutorials/environmental-art-curriculum/Section1/GeneralPerimeter-1.jpg) 2. Anchor these parts. #### Recreate the sample To exactly recreate the perimeter wall geometry within the sample [Environment Art - Greyboxing](https://www.roblox.com/games/14447721254/Environment-Art-Greyboxing) place file: 1. Add and configure the following **block** parts for the **top** perimeter walls: | Part | Size | CFrame.Position | | --- | --- | --- | | Top-left wall | `30, 30, 185` | `-140, 20, 282.5` | | Top-middle wall | `10, 30, 60` | `-150, 20, 160` | | Top-right wall | `30, 30, 185` | `-140, 20, 37.5` |![An angled top-down view of the floor and top perimeter walls. The top perimeter wall geometry is highlighted in yellow.](../../../assets/tutorials/environmental-art-curriculum/Section1/Perimeter-1.jpg) 2. Add and configure the following **block** parts for the **side** perimeter walls: | Part | Size | CFrame.Position | | --- | --- | --- | | Left wall | `105, 30, 35` | `-72.5, 20, 357.5` | | Right wall | `105, 30, 35` | `-72.5, 20, -37.5` |![An angled top-down view of the floor, top perimeter walls, and side perimeter walls. The side perimeter wall geometry is highlighted in yellow.](../../../assets/tutorials/environmental-art-curriculum/Section1/Perimeter-2.jpg) 3. Add and configure the following **block** parts for the **bottom left** perimeter walls: | Part | Size | CFrame.Position | | --- | --- | --- | | Left wall | `20, 20, 40` | `-34, 15, 320` | | Middle wall | `12, 45, 15` | `-29, 24.5, 297.5` | | Right wall | `5, 35, 5` | `-27.5, 19.5, 287.5` |![An angled top-down view of the floor, top perimeter walls, side perimeter walls, and bottom-left perimeter walls. The bottom-left perimeter wall geometry is highlighted in yellow.](../../../assets/tutorials/environmental-art-curriculum/Section1/Perimeter-3.jpg) 4. Add and configure the following **block** parts for the **bottom right** perimeter walls: | Part | Size | CFrame.Position | | --- | --- | --- | | Left wall | `5, 35, 5` | `-27.5, 19.5, 32.5` | | Middle wall | `12, 45, 15` | `-29, 24.5, 22.5` | | Right wall | `20, 20, 40` | `-35, 15, 0` |![An angled top-down view of the floor, top perimeter walls, side perimeter walls, bottom perimeter walls. The bottom-right perimeter wall geometry is highlighted in yellow.](../../../assets/tutorials/environmental-art-curriculum/Section1/Perimeter-4.jpg) 5. Anchor all of these perimeter wall parts. ### Spawn zone geometry The third step in creating the laser tag greybox environment is to create the geometry to contain each team's spawn zone. For experiences that only contain centralized spawn zones, sectioning them off into their own rooms lets users acclimate to the experience when they first join the match, and it provides them cover from enemy fire when they rejoin the match after their health reaches zero. There are two spawn zones in the [Environment Art - Greyboxing](https://www.roblox.com/games/14447721254/Environment-Art-Greyboxing) `.rbxl` file, one for each team a user can join, and they're located at opposite ends of the map. Each spawn zone has two exit points users can choose to enter the main gameplay area, which allows for quick access to two primary lanes, and provides cover from enemy fire from users within the cross lane near the spawn zone. Having two exit points is important because a single exit point would result in a bottleneck of users trying to enter or exit the spawn zone, and three spawn zones wouldn't provide enough cover from enemy fire after the start of the match. ![An angled top-down view of a spawn zone with arrows pointing toward the two exit points and all paths users can take away from the spawn zone.](../../../assets/tutorials/environmental-art-curriculum/Section1/SpawnZone-ExitPoints.jpg) To help visualize the geometry of the spawn zone areas you are creating for the experience, reference the following image that marks them in yellow. ![A top-down view of the final greybox environment with the spawn zone wall geometry highlighted in yellow.](../../../assets/tutorials/environmental-art-curriculum/Section1/SpawnZone-Intro.jpg) #### Create your own To create your own spawn zone geometry: 1. Using **block** parts, segment symmetrical sections on the far left and right of the map for each team's **spawn zon**e with two exit points.![An angled top-down view of the floor, perimeter, and spawn zone walls. The spawn zone wall geometry is highlighted in yellow.](../../../assets/tutorials/environmental-art-curriculum/Section1/GeneralSpawnZone-1.jpg) 2. Anchor these parts. #### Recreate the Sample To exactly recreate the spawn zone geometry within the sample [Environment Art - Greyboxing](https://www.roblox.com/games/14447721254/Environment-Art-Greyboxing) place file: 1. Add and configure the following **block** parts for the additional **spawn zone walls**: | Part | Size | CFrame.Position | | --- | --- | --- | | Left spawn zone wall | `15, 15, 45` | `-117.5, 12.5, 317.5` | | Right spawn zone wall | `15, 15, 45` | `-117.5, 12.5, 2.5` |![An angled top-down view of the floor, perimeter, and top spawn zone walls. The top spawn zone wall geometry is highlighted in yellow.](../../../assets/tutorials/environmental-art-curriculum/Section1/SpawnZone-1.jpg) 2. Add and configure the following **block** parts for the left spawn zone's **doorways**: | Part | Size | CFrame.Position | | --- | --- | --- | | Left spawn, top wall | `10, 15, 5` | `-105, 12.5, 297.5` | | Left spawn, top door | `10, 5, 5` | `-95, 17.5, 297.5` | | Left spawn, middle wall | `25, 15, 5` | `-77.5, 12.5, 297.5` | | Left spawn, bottom door | `10, 5, 5` | `-60, 17.5, 297.5` | | Left spawn, bottom wall | `20, 15, 5` | `-45, 12.5, 297.5` |![An angled top-down view of the floor, perimeter, top spawn zone walls, and left spawn zone walls. The left spawn zone wall geometry is highlighted in yellow.](../../../assets/tutorials/environmental-art-curriculum/Section1/SpawnZone-2.jpg) 3. Add and configure the following **block** parts for the right spawn zone's **doorways**: | Part | Size | CFrame.Position | | --- | --- | --- | | Right spawn top wall | `10, 15, 5` | `-105, 12.5, 22.5` | | Right spawn, top door | `10, 5, 5` | `-95, 17.5, 22.5` | | Right spawn, middle wall | `25, 15, 5` | `-77.5, 12.5, 22.5` | | Right spawn, bottom door | `10, 5, 5` | `-60, 17.5, 22.5` | | Right spawn, bottom wall | `20, 15, 5` | `-45, 12.5, 22.5` |![An angled top-down view of the floor, perimeter, all spawn zone walls. The right spawn zone wall geometry is highlighted in yellow.](../../../assets/tutorials/environmental-art-curriculum/Section1/SpawnZone-3.jpg) 4. Anchor all of these spawn zone parts. ### Combat pockets geometry The fourth step in creating the laser tag greybox environment is to create the geometry for combat pockets in the interior of the building. This geometry makes up the majority of the playable areas in the experience because combat pockets arise from the intersections of primary lane and cross lanes, which span the entirety of the building. The instructions in this section refer to this geometry as three separate combat pockets according to their location on a top-view of the map: the left combat pocket, the middle combat pocket, and the right combat pocket. Most combat pockets include a maximum of three entrance or exit points to refrain from giving users choice overload as they're navigating the space. ![A top-down view of the final greybox environment with the left, middle, and right combat pockets highlighted.](../../../assets/tutorials/environmental-art-curriculum/Section1/CombatPockets-Intro1.jpg) To help visualize geometry of the combat pockets you are creating for the experience, reference the following image that marks them in yellow. ![A top-down view of the final greybox environment with the combat pocket geometry highlighted in yellow.](../../../assets/tutorials/environmental-art-curriculum/Section1/CombatPockets-Intro2.jpg) #### Create your own To create your own combat pockets geometry: 1. Using **block** parts, create a **left combat pocket** with two exit points that allow players to traverse the middle lane, and one exit point that opens to the interior primary lane. This geometry must have space on either side of the combat pocket to allow for cross lanes, and block player entry from the exterior primary lane.![An angled top-down view of the floor, perimeter, spawn zone walls, and left combat pocket. The left combat pocket geometry is highlighted in yellow.](../../../assets/tutorials/environmental-art-curriculum/Section1/CombatPockets-1.jpg) 2. Using **block** parts, create and position a symmetrical copy of this combat pocket near the other team's spawn zone. This geometry represents your **right combat pocket**.![An angled top-down view of the floor, perimeter, spawn zone walls, left combat pocket, and right combat pocket. The right combat pocket geometry is highlighted in yellow.](../../../assets/tutorials/environmental-art-curriculum/Section1/GeneralCombatPockets-2.jpg) 3. Using **block** parts, create a **middle combat pocket** with two exit points that allow players to traverse the middle lane, one exit point that opens to the interior primary lane, and open space along the edge that flows into the exterior primary lane.![An angled top-down view of the floor, perimeter, spawn zone walls, left combat pocket, middle combat pocket, and right combat pocket. The middle combat pocket geometry is highlighted in yellow.](../../../assets/tutorials/environmental-art-curriculum/Section1/GeneralCombatPockets-3.jpg) 4. **(Optional)** Using **block** parts, create **hallway additions** to the middle combat pocket to break up sight lines across the interior primary lane.![An angled top-down view of the floor, perimeter, spawn zone walls, left combat pocket, middle combat pocket, right combat pocket, and hallway addition walls. The hallway addition geometry is highlighted in yellow.](../../../assets/tutorials/environmental-art-curriculum/Section1/GeneralCombatPockets-4.jpg) 5. Anchor these parts. #### Recreate the sample To exactly recreate the combat pockets geometry within the sample [Environment Art - Greyboxing](https://www.roblox.com/games/14447721254/Environment-Art-Greyboxing) place file: 1. Add and configure the following **block** parts for the **left combat pocket**. | Part | Size | CFrame.Position | | --- | --- | --- | | Top hallway extension | `10, 15, 15` | `-100, 12.5, 272.5` | | Top doorway, left wall | `5, 15, 15` | `-92.5, 12.5, 262.5` | | Top doorway, overhang | `5, 5, 10` | `-92.5, 17.5, 250` | | Top doorway, right wall | `5, 15, 20` | `-92.5, 12.5, 235` | | Left doorway, top wall | `20, 15, 5` | `-85, 12.5, 272.5` | | Left doorway, overhang | `10, 5, 5` | `-70, 17.5, 272.5` | | Left doorway, bottom wall | `20, 15, 5` | `-55, 12.5, 272.5` | | Right doorway, top wall | `15, 15, 10` | `-82.5, 12.5, 230` | | Right doorway, overhang | `10, 5, 10` | `-70, 17.5, 230` | | Right doorway, bottom wall | `20, 15, 10` | `-55, 12.5, 230` | | Bottom wall | `10, 15, 35` | `-50, 12.5, 252.5` |![An angled top-down view of the floor, perimeter, spawn zone walls, and left combat pocket. The left combat pocket geometry is highlighted in yellow.](../../../assets/tutorials/environmental-art-curriculum/Section1/CombatPockets-1.jpg) 2. Add and configure the following **block** parts for the **top hallway addition** to the middle combat pocket that breaks up sight lines across the interior primary lane: | Part | Size | CFrame.Position | | --- | --- | --- | | Left hallway extension | `15, 20, 45` | `-117.5, 15, 212.5` | | Right hallway extension | `15, 20, 45` | `-117.5, 15, 107.5` | | Left hallway | `5, 20, 45` | `-92.5, 15, 187.5` | | Right hallway | `5, 20, 45` | `-92.5, 15, 132.5` | | Top hallway entrance, left wall | `15, 15, 5` | `-102.5, 17.5, 172.5` | | Top hallway entrance, left wall on entrance | `5, 15, 10` | `-112.5, 17.5, 170` | | Top hallway entrance, right wall | `15, 15, 5` | `-102.5, 17.5, 147.5` | | Top hallway entrance, right wall on entrance | `5, 15, 10` | `-112.5, 17.5, 150` | | Door overhang | `5, 5, 10` | `-92.5, 22.5, 160` |![An angled top-down view of the floor, perimeter, spawn zone walls, left combat pocket, and top hallway addition walls. The top hallway addition wall geometry is highlighted in yellow.](../../../assets/tutorials/environmental-art-curriculum/Section1/CombatPockets-2.jpg) 3. Add and configure the following **block** parts for the **middle room** of the middle combat pocket: | Part | Size | CFrame.Position | | --- | --- | --- | | Left wall that extends into the hallway | `10, 20, 25` | `-85, 15, 197.5` | | Right wall that extends into the hallway | `10, 20, 25` | `-85, 15, 122.5` | | Left doorway, top wall | `25, 20, 15` | `-67.5, 15, 192.5` | | Left doorway, overhang | `10, 10, 15` | `-50, 20, 192.5` | | Left doorway, bottom wall | `5, 20, 15` | `-42.5, 15, 192.5` | | Right doorway, top wall | `25, 20, 15` | `-67.5, 15, 127.5` | | Right doorway, overhang | `10, 10, 15` | `-50, 20, 127.5` | | Right doorway, bottom wall | `5, 20, 15` | `-42.5, 15, 127.5` |![An angled top-down view of the floor, perimeter, spawn zone walls, left combat pocket, top hallway addition walls, and the middle room of the middle combat pocket. The middle room of the middle combat pocket geometry is highlighted in yellow.](../../../assets/tutorials/environmental-art-curriculum/Section1/CombatPockets-3.jpg) 4. Add and configure the following **block** parts for the **right combat pocket**: | Part | Size | CFrame.Position | | --- | --- | --- | | Top hallway extension | `10, 15, 15` | `-100, 12.5, 47.5` | | Top doorway, left wall | `5, 15, 20` | `-92.5, 12.5, 85` | | Top doorway, overhang | `5, 5, 10` | `-92.5, 17.5, 70` | | Top doorway, right wall | `5, 15, 15` | `-92.5, 12.5, 57.5` | | Left doorway, top wall | `15, 15, 10` | `-82.5, 12.5, 90` | | Left doorway, overhang | `10, 5, 10` | `-70, 17.5, 90` | | Left doorway, bottom wall | `20, 15, 10` | `-55, 12.5, 90` | | Right doorway, top wall | `20, 15, 5` | `-85, 12.5, 47.5` | | Right doorway, overhang | `10, 5, 5` | `-70, 17.5, 47.5` | | Right doorway, bottom wall | `20, 15, 5` | `-55, 12.5, 47.5` | | Bottom wall | `10, 15, 35` | `-50, 12.5, 67.5` |![An angled top-down view of the floor, perimeter, spawn zone walls, left combat pocket, top hallway addition walls, middle combat pocket, and right combat pocket. The right combat pocket geometry is highlighted in yellow.](../../../assets/tutorials/environmental-art-curriculum/Section1/CombatPockets-4.jpg) 5. Anchor all of these combat pocket parts. ### Exterior geometry The final step in creating the laser tag greybox environment is to create the placeholder exterior assets that create interesting composition for the outdoor space, as well as minimal cover for users traveling the exterior primary lane. This area of the experience is important because while it comes with risk due to the minimal cover from enemy fire, it also provides users a quick route to the enemy team's spawn zone. To help visualize the geometry of the exterior assets you are creating for the experience, reference the following image that marks them in yellow. ![A top-down view of the final greybox environment with the exterior geometry highlighted in yellow.](../../../assets/tutorials/environmental-art-curriculum/Section1/Exterior-Intro.jpg) #### Create your own To create your own exterior geometry: 1. Using **block** parts, create symmetrical **obstacles** along the exterior primary lane. For example, the sample greybox environment adds parts to block lines of sight that will later become towers, pillars, and planters.![An angled top-down view of the floor, perimeter, spawn zone walls, combat pockets, and exterior geometry. The exterior geometry is highlighted in yellow.](../../../assets/tutorials/environmental-art-curriculum/Section1/GeneralExterior-1.jpg) 2. Anchor these parts. #### Recreate the sample To exactly recreate the exterior geometry within the sample [Environment Art - Greyboxing](https://www.roblox.com/games/14447721254/Environment-Art-Greyboxing) place file: 1. Add and configure the following **block** parts for the **left tower**: | Part | Size | CFrame.Position | | --- | --- | --- | | Left wall | `15, 25, 5` | `-32.5, 22.5, 192.5` | | Right wall | `15, 40, 10` | `-32.5, 30, 185` | | Bottom ledge | `20, 5, 15` | `-30, 7.5, 187.5` |![An angled top-down view of the floor, perimeter, spawn zone walls, combat pockets, and left tower geometry. The left tower geometry is highlighted in yellow.](../../../assets/tutorials/environmental-art-curriculum/Section1/Exterior-1.jpg) 2. Add and configure the following **block** parts for the **right tower**: | Part | Size | CFrame.Position | | --- | --- | --- | | Left wall | `15, 40, 10` | `-32.5, 30, 135` | | Right wall | `15, 25, 5` | `-32.5, 22.5, 127.5` | | Bottom ledge | `20, 5, 15` | `-30, 7.5, 132.5` |![An angled top-down view of the floor, perimeter, spawn zone walls, combat pockets, left tower, and right tower geometry. The right tower geometry is highlighted in yellow.](../../../assets/tutorials/environmental-art-curriculum/Section1/Exterior-2.jpg) 3. Add and configure the following **block** parts for the **obstacles** to the left of the towers: | Part | Size | CFrame.Position | | --- | --- | --- | | Left obstacle | `20, 10, 5` | `-15, 5, 262.5` | | Middle obstacle | `15, 30, 5` | `-22.5, 15, 237.5` | | Right obstacle | `10, 10, 15` | `-5, 5, 202.5` |![An angled top-down view of the floor, perimeter, spawn zone walls, combat pockets, left tower, right tower, and obstacle geometry to the left of the left tower. The obstacle geometry to the left of the left tower are highlighted in yellow.](../../../assets/tutorials/environmental-art-curriculum/Section1/Exterior-3.jpg) 4. Add and configure the following **block** parts for the **obstacles** to the right of the towers: | Part | Size | CFrame.Position | | --- | --- | --- | | Left obstacle | `10, 10, 15` | `-5, 5, 117.5` | | Middle obstacle | `15, 30, 5` | `-22.5, 15, 82.5` | | Right obstacle | `20, 10, 5` | `-15, 5, 57.5` |![An angled top-down view of the floor, perimeter, spawn zone walls, combat pockets, left tower, right tower, and obstacle geometry to the left and right of the towers. The obstacle geometry to the right of the right tower are highlighted in yellow.](../../../assets/tutorials/environmental-art-curriculum/Section1/Exterior-4.jpg) 5. Add and configure the following **block** parts for the **obstacles** between the towers: | Part | Size | CFrame.Position | | --- | --- | --- | | Top obstacle | `10, 5, 5` | `-50, 7.5, 160` | | Middle obstacle | `20, 10, 10` | `-35, 10, 160` | | Bottom obstacle | `10, 10, 30` | `0, 5, 160` |![An angled top-down view of the floor, perimeter, spawn zone walls, combat pockets, left tower, right tower, and obstacle geometry. The obstacle geometry between the towers are highlighted in yellow.](../../../assets/tutorials/environmental-art-curriculum/Section1/Exterior-5.jpg) 6. Anchor all of these tower parts. ## Apply placeholder materials Now that you have your placeholder geometry in place, it's time to apply placeholder materials to the map in key areas to help orient users to where they are in the experience. The sample [Environment Art - Greyboxing](https://www.roblox.com/games/14447721254/Environment-Art-Greyboxing) `.rbxl` file uses the following color map, but you can use can use any color to achieve the same purpose: - **Deep Orange for top perimeter walls** – Informs users where they are in relation to the back of the building. - **Persimmon for all left-side floors** – Informs users where they are in relation to the right-side of the building. - **Lapis for all right-side floors** – Informs users where they are in relation to the left-side of the building. - **Bright Green for the exterior elevation** – Informs users where they are in relation to outside of the building. Applying materials to these specific key areas is important because no matter where a user is in the experience, as long as they can see at least one of these colors, they can quickly deduce their approximate location within the overall map, as well as where they are in relation to a spawn zone. For example, if the following user is on the "red" team, when they're walking on the red floor with the yellow wall to their right, they know that they're in the interior primary lane and moving toward their spawn zone. Conversely, if they're walking on the lapis floor with the yellow wall to their left, they know they're in the interior lane and moving toward the enemy team's spawn zone. ![A Rthro avatar standing on a red floor with a yellow wall to their right. This color configuration informs the user that they're in the interior primary lane and moving toward the red spawn zone.](../../../assets/tutorials/environmental-art-curriculum/Section1/Placeholder-Left.jpg) ![A Rthro avatar standing on a blue floor with a yellow wall to their left. This color configuration informs the user that they're in the interior primary lane and moving toward the blue spawn zone.](../../../assets/tutorials/environmental-art-curriculum/Section1/Placeholder-Right.jpg) To apply placeholder materials: 1. Select the top perimeter walls and the top hallway editions, then set their `Class.Part.Color` to `255, 176, 0`. 2. Select the left-side floor parts, then set their `Class.Part.Color` to `255, 89, 89`. 3. Select the right-side floor parts, then set their `Class.Part.Color` to `16, 42, 220`. 4. Select the part for the elevation between the ground and first floor, then set their `Class.Part.Color` to `75, 151, 75`. ![An angled top-down view of the final greybox environment with placeholder materials to mark unique sections of the map.](../../../assets/tutorials/environmental-art-curriculum/Section1/PlaceholderMaterials.jpg) ## Playtest your layout It's important to continuously playtest the layout of your environment **at nearly every step of the development process** so you can ensure the experience is both fun and functional, and you can catch small issues before they turn into much larger problems the further you are in the process. As you playtest, ask yourself the following questions: - Are there advantages or disadvantages to either team? - Are users able to successfully orient themselves and understand their location at any point on the map? - Are there any areas on the map that overwhelm users with too many choices? - What am I enjoying or getting frustrated about the layout or gameplay? - Will this make users feel the way I want them to when they're in this area? - Is there any part of the map that I can bypass and still achieve my goals? To playtest, select one of the following simulation modes from the mezzanine's dropdown menu and click the **Play** button to begin. ![Test option in the testing modes dropdown of Studio's mezzanine.](../../../assets/studio/general/Mezzanine-Testing-Mode-Test.png) | Mode | Shortcut | Description | | --- | --- | --- | | **Test** | `F5` | Starts simulating the experience, inserting your avatar at either a `Class.SpawnLocation` or coordinates of around`(0, 100, 0)`. | | **Test Here** | | Starts simulating the experience, inserting your avatar in front of the camera's current position. | | **Run** | `F8` | Starts simulating the experience but does not insert your avatar. The simulation begins at the current camera position and you can navigate around using the Studio camera controls. | While playtesting, you can use the same controls as a default Roblox experience. Once you are happy with the overall layout of your experience, you can move on to creating polishing assets that will replace the greybox geometry in accordance with your art style. --- title: "Environmental art curriculum" url: /docs/en-us/tutorials/curriculums/environmental-art last_updated: 2026-06-29T19:34:13Z description: "Learn how to build 3D environments that enhance your experience's gameplay." --- # Environmental art curriculum **Environmental art** is the discipline of creating and configuring 3D environments that embody and facilitate gameplay requirements, immerse users within your experience, and provide contextual information about the world itself. You'll learn how to recreate a [high-quality environment](https://www.roblox.com/games/14447845297/Environment-Art-Optimizing) for a first-person shooter laser tag experience by following step-by-step processes and best practices for level design, developing assets, and optimization efforts for memory and performance that you can then implement in your own experiences. This course is intended for readers who are familiar with general building concepts and tools in Roblox Studio. If you need help learning how to build an environment, try the [core curriculum](/docs/en-us/tutorials/core.md). ## Course contents **#### Chapter 1 - Greybox your environment** Learn how to [add simple shapes to the 3D space](/docs/en-us/tutorials/curriculums/greybox-your-environment.md) in order to figure out how users will experience gameplay before investing time into polishing assets. **#### Chapter 2 - Develop polished assets** Learn about the concepts behind [designing high-quality assets](/docs/en-us/tutorials/curriculums/develop-polished-assets.md) in third-party modeling tools that replace the greybox environment. **#### Chapter 3 - Assemble an asset library** Learn how to [import and configure your assets](/docs/en-us/tutorials/curriculums/assemble-an-asset-library.md) in a central location within your experience for easy access and reuse. **#### Chapter 4 - Construct your world** Learn how to [utilize your asset library](/docs/en-us/tutorials/curriculums/construct-your-world.md), as well as how to add and configure additional elements of the 3D space to bring your world to life. **#### Chapter 5 - Optimize your experience** Learn different strategies to [keep your frame rate and performance levels high](/docs/en-us/tutorials/curriculums/optimize-your-experience.md) across devices, especially those with memory limitations. --- title: "Optimize your experience" url: /docs/en-us/tutorials/curriculums/environmental-art/optimize-your-experience last_updated: 2026-06-29T19:34:13Z description: "Explains how to configure your assets and Studio settings to improve frame rate and performance levels." --- # Optimize your experience **Optimizing your experience** is the process of configuring your assets and Studio settings to keep your frame rate and performance levels high for devices with memory and graphics processing unit (GPU) limitations. This process ensures that nearly every user has the same gameplay and visual experience while they're viewing your environment. Using the [Environment Art - Optimizing](https://www.roblox.com/games/14447845297/Environment-Art-Optimizing) `.rbxl` file as a reference, this section of the environmental art curriculum shows you how to review and configure your place file for optimal graphics, including guidance on: - Reviewing the physics and rendering parameters of each asset to confirm they preserve memory and engine performance. - Culling excess texture, geometry, or transparencies that unnecessarily increase the amount of calculations the Roblox Engine must perform to render your assets. While the Roblox Engine handles most optimization work for you, you can assist in these optimization efforts by utilizing the [Microprofiler](/docs/en-us/performance-optimization/microprofiler.md) to see where it takes more time to render specific frames. Using this information, you can make informed decisions about what assets need your attention in regards to their parameters or excess content. [Scene Analysis](/docs/en-us/performance-optimization/scene-analysis.md) is another powerful tool for checking resource usage. ## Review physics and render parameters In [Assemble an asset library](/docs/en-us/tutorials/curriculums/environmental-art/assemble-an-asset-library.md), you learned how important it is to set physics and rendering parameters that allow your assets to retain their high visual quality across devices with memory and GPU limitations. However, it's common as you construct your environment to adjust these parameters according to an asset's contextual position and purpose within your experience. For example, much of the foliage in the final sample laser tag environment casts shadows despite a performance cost because it adds to the realism of the environment. When you modify physics and rendering parameters, it's useful near the end of the development process to review all parameters to see where you can optimize a parameter while maintaining aesthetic goals and gameplay requirements. To illustrate, you can disable `Class.BasePart.CastShadow` property for the foliage near the edges of the gameplay area to save on performance without interfering with a user's gameplay or visual experience. ![An outdoor view of the sample laser tag experience that casts shadows.](../../../assets/tutorials/environmental-art-curriculum/Section5/ReviewingParameters-Disabled.jpg)_`Class.BasePart.CastShadow` = Disabled_ ![An outdoor view of the sample laser tag experience that doesn't cast shadows. There is almost no difference in this view, but it improves performance.](../../../assets/tutorials/environmental-art-curriculum/Section5/ReviewingParameters-Enabled.jpg)_`Class.BasePart.CastShadow` = Enabled_ ## Cull nonessential content After you review your physics and rendering parameters, you can review the assets themselves to see where you can cull any nonessential content from the experience that doesn't affect your gameplay, such as identical textures with different asset IDs, complex geometry with many vertices, or transparencies that layer on top of each other depending on the camera view. The following sections detail what you can do to review this content, and why it helps optimization efforts. ### Remove duplicate textures As you transition between developing your assets and constructing your environment, it's common to iterate over meshes or textures as you find what's necessary for your aesthetic goals or gameplay requirements. If you don't convert your assets into [packages](/docs/en-us/projects/assets/packages.md), when you import these iterations into Studio, you're making unique asset IDs that the Roblox Engine needs to reference as it renders your assets within the environment. For example, if you were to import the following two fire hydrant meshes into Studio separately, even if they are exactly the same in appearance, the Roblox Engine treats them as two objects with unique asset IDs. The more unique calls the engine needs to make, the more of an impact on memory and performance. For this reason, it's important to confirm when you're reusing an asset multiple times, each instance of that asset uses the same asset ID so that the engine only needs to make a single call to render it repeatedly. ![Two of the exact same fire hydrant with unique asset IDs. There is no visual difference but the duplicate asset IDs negatively impacts performance.](../../../assets/tutorials/environmental-art-curriculum/Section5/RemovingTextures-Firehydrants.jpg) ### Optimize geometry If you find that you need to make more adjustments to increase frame rate across devices, it's useful to see where you can optimize your geometry by either: - Combining groups of meshes into a single asset. - Decreasing the polygon count of assets with geometric complexity. Expanding on this first technique, every unique asset in your experience represents a draw call on the GPU in which it sends a signal to the GPU to call information in order for the Roblox Engine to render the asset correctly. The more unique assets you have, the more draw calls the system needs to make. For this reason, if you have a group of meshes that make up a larger component in your experience, you can group them together in third-party modeling tools to reduce the need for multiple draw calls. To illustrate this point, the final sample laser tag environment parents multiple parts and meshes together to create the large towers outside of the building. If you were to combine all of these individual components together, you could make it a single asset with only one asset ID, and reduce the number of draw calls from 8 to 1. However, it's important to note that this technique removes your ability to freely change the visual and physical characteristics of each component, such as its position or material. For example, in the following image, the left tower remains multiple assets under a `Class.Model` object, and the right tower is a single asset. You can modify each component of the left tower individually, but when you modify the right tower, such as changing its color to black, it affects the entirety of the object. This is why it's important to only consider this technique near the end of your environment's development when you know where you can improve performance without affecting your aesthetic goals. ![A front view of the two towers. The tower on the left includes multiple assets under a single model, while the tower on the right decreases the amount of assets by removing all texture objects.](../../../assets/tutorials/environmental-art-curriculum/Section5/OptimizingGeo-Towers.jpg) Expanding on the second technique, assets with geometric complexity have more polygons, meaning they have more vertices that the engine needs to calculate as it renders their visual appearance. This means that assets with less complexity and fidelity are less costly to render, leading to an improvement in both performance and memory. > **Warning:** If you choose to utilize this second technique, after you simplify your geometry in third-party tooling, it's important to remember to update your packages with the new import instead of creating a new asset ID, otherwise the Roblox Engine needs to make two separate calls for each asset ID. ### Delete layered transparencies To provide a sense of realism to the environment, the final sample laser tag environment includes many meshes with varying levels of transparency, such as the foliage in the outdoor space, glass on the futuristic signage or planter railing. When the camera views multiple semi-transparent objects that are in front or behind each other, the Roblox Engine must render the overlapping pixels multiple times to account for the transparent areas. This process is called high transparency overdraw, and it comes at a significant impact to performance. For example, consider the following view of a planter in the sample environment. The engine must render the transparent areas of the leaves between the plant closest to the camera to the plant closest to the outdoor area in layers, equating to hundreds of thousands of overdrawn pixels. To alleviate some of this impact, it's important to review the layout of all semi-transparent objects in your environment, and ensure there aren't too many places where there are many layers of overlap, especially in large areas of the screen. ![A Rthro avatar facing a planter with multiple plants with transparency between the leaves.](../../../assets/tutorials/environmental-art-curriculum/Section5/LayeredTransparencies-Left.jpg) ![A side view of the Rthro avatar facing a planter with multiple plants with transparency between the leaves, and example layers of overdrawn pixels are highlighted to show where there is overdrawn in the environment.](../../../assets/tutorials/environmental-art-curriculum/Section5/LayeredTransparencies-Right.jpg) When you finish reviewing all of your content to ensure it's optimal across devices, your experience is now ready for publication! > **Info:** We're interested in hearing from you about your experience following the Environmental Art Curriculum. If you have any questions, concerns, or additional feedback on the process, please comment on our [Environmental Art Curriculum Q&A](https://devforum.roblox.com/t/feedback-on-environmental-art-curriculum/2592218). --- title: "Add rounds" url: /docs/en-us/tutorials/curriculums/gameplay-scripting/add-rounds last_updated: 2026-06-29T19:34:13Z description: "Explains how to implement round-based behavior." --- # Add rounds **Adding rounds** lets you structure gameplay into phases with a clear start and finish point so that players can measure their progress and have a periodic opportunity for an equal playing field. This is particularly important for team-based gameplay because it offers players the chance to switch up their play style depending on who is on their team during that round. Using the [sample laser tag experience](https://www.roblox.com/games/14817965191/Laser-Tag-1A) as a reference, this section of the tutorial teaches you how to use and customize Roblox's built-in features to structure each round, including scripting guidance on: - Beginning a round by resetting individual and team points, then spawning players to their team spawn zones. - Customizing variables that set the objective for the round at the top of each player's screen. - Tracking player point contributions for their team score. - Triggering unique UI screens depending on if the player's team won or lost the round. - Ending a round by disconnecting players and spawning them in the neutral lobby. After you complete this section, you will learn how to implement blaster behavior that is both accurate and satisfying to players. ## Start loop **ServerScriptService** ⟩ **Gameplay** ⟩ **Rounds** handles most of the logic for implementing rounds, and it starts by calling the `startRoundLoopAsync()` function to mark the beginning of a round loop. While players join the lobby and wait to sort into a team, `startRoundLoopAsync()` calls the `resetScores()` function in **ServerScriptService** ⟩ **Gameplay** ⟩ **Scoring** to reset both the leaderboard and team points. ```lua function Scoring.resetScores() for _, player in Players:GetPlayers() do player.leaderstats.Points.Value = 0 end for _, team in Teams:GetTeams() do team:SetAttribute(GuiAttribute.teamPoints, 0) end end ``` Now that everyone is starting at zero points, `startRoundLoopAsync()` then sets the **Neutral** spawn location's `Class.SpawnLocation.Neutral|Neutral` property to **false** so that only players with the same `Class.Team.Color` property as the spawn location's `Class.SpawnLocation.TeamColor|TeamColor` property can spawn there. Because the spawn location's `Class.SpawnLocation.TeamColor|TeamColor` property is set to **white** instead of the sample's mint or carnation pink teams, this configuration prevents all players from spawning or respawning there while a round is active. For players who are currently in the lobby, `startRoundLoopAsync()` passes all players currently within the experience to the `spawnPlayersInMap` function in **ServerScriptService** ⟩ **Gameplay** ⟩ **Rounds** ⟩ **spawnPlayersInMap** to sort and balance everyone into a team with approximately the same number of players. For any new players that join the experience after the lobby group was sorts into a team, `startRoundLoopAsync()` listens for the `Players.PlayerAdded:Connect` event, then calls the `spawnPlayersInMap` function again to add them to the team with the least amount of players. For more information on this process, see [Configure Spawn Locations](/docs/en-us/tutorials/curriculums/gameplay-scripting/spawn-respawn.md#configure-spawn-locations) from the previous **Spawning and Respawning** section of the tutorial. ```lua -- Spawn all players in the map neutralSpawn.Neutral = false spawnPlayersInMap(Players:GetPlayers()) -- Spawn new players in the map when they join local playerAddedConnection = Players.PlayerAdded:Connect(function(player: Player) spawnPlayersInMap({ player }) end) ``` ## Set objective Now that each player is in the arena with their teammates, the experience needs to provide instructions for what to do to be successful within the round. The sample laser tag experience tackles this requirement by providing an objective prompt at the top of each player's screen with clear guidance on what the team needs to do to win. While you can learn more about how to configure and display the [Objective UI](/docs/en-us/tutorials/curriculums/user-interface-design/implement-designs-in-studio.md#objective-ui) component in the UI Curriculum, this section focuses on how to implement the objective goal as the round begins, starting with how to set the amount of points each team needs to complete the round. Even though the objective prompt at runtime informs players they need to score three points to win, if you examine the prompt in **StarterGui** ⟩ **HUDGui**, you can see that it instead contains a configurable "`%d`" for the point value. "`%d`" is a placeholder string that you can increase or decrease at any time to meet your own gameplay requirements by updating the `TEAM_SCORE_LIMIT` variable in **ReplicatedStorage** ⟩ **TEAM_SCORE_LIMIT**. For example, if you were to set this number to an excessively high `200`, the prompt and team point counter would update accordingly. ```lua local TEAM_SCORE_LIMIT = 200 -- updated line, be sure to change back return TEAM_SCORE_LIMIT ``` This simple variable update works on runtime because as the round starts, **ReplicatedStorage** ⟩ **HUDGuiSetup** ⟩ **SetObjective** requires the `TEAM_SCORE_LIMIT` module script so that it can swap the placeholder string in the UI Objective's `Class.TextLabel` object. ```lua local TEAM_SCORE_LIMIT = require(ReplicatedStorage.TEAM_SCORE_LIMIT) local function setObjective(gui: ScreenGui) local bodyTextLabel = gui.Objective.ObjectiveDisplay.Body.BodyTextLabel bodyTextLabel.Text = bodyTextLabel.Text:format(TEAM_SCORE_LIMIT) end ``` ## Track points Now that players have a goal for the round, the experience needs to keep track of each team's points until they meet their objective. While the `Class.Teams` service's default behavior automatically groups each player underneath their team and adds up each player's contributions to their team score, it's important to store and monitor points in a separate location for round-based gameplay because if a player scores then leaves before the round is over, their contribution is deducted from the leaderboard as soon as they disconnect from the experience. To ensure this doesn't happen and every contribution toward the team goal is preserved, **ReplicatedStorage** ⟩ **HUDGuiSetup** ⟩ **StartSyncingTeamPoints** stores all points separately under the `teamPoints` attribute in `Class.Teams` service. As `teamPoints` increments, this module script calls function `startSyncingTeamPoints` to find the team counter `Class.GuiObjects` within the Objective UI component. When it locates **TeamACounter** and **TeamBCounter**, it gets their `teamColor` attribute, which correlates with the team spawn zones: TeamACounter displays the green team's points, and TeamBCounter tracks the pink team's points. ```lua local function startSyncingTeamPoints(gui: ScreenGui) for _, teamPointCounter in gui.Objective.TeamPointCounter:GetChildren() do if not teamPointCounter:IsA("GuiObject") then continue end local iconTeamColor = teamPointCounter:GetAttribute(GuiAttribute.teamColor) ``` The module script then calls its `getTeamFromTeamColor` function to validate that the TeamACounter's **mint** `teamColor` attribute and the TeamBCounter's **carnation pink** `teamColor` attribute both match the corresponding `Class.Team.Color` properties underneath the `Class.Teams` service. If so, it returns both of the teams. ```lua local function getTeamFromTeamColor(teamColor: Color3): Team? for _, team in Teams:GetTeams() do if team.TeamColor == teamColor then return team end end return nil end ``` When this occurs, `startSyncingTeamPoints` sets both team counters' `Class.TextLabel` objects to their corresponding `teamPoints` values, and continues to update them whenever a player scores a point by tagging another player out on the opposite team. ```lua teamPointCounter.TextLabel.Text = team:GetAttribute(GuiAttribute.teamPoints) team:GetAttributeChangedSignal(GuiAttribute.teamPoints):Connect(function() teamPointCounter.TextLabel.Text = team:GetAttribute(GuiAttribute.teamPoints) ``` Everything in this section so far has focused on how to track points on the player's screen, but it's important to review the logic that handles tracking points on the server so it knows when a team meets the objective goal and wins the round. If you revisit **ServerScriptService** ⟩ **Gameplay** ⟩ **Scoring**, you can see that the module script starts by creating a bindable event, which will fire each time a player scores a point. ```lua local teamScoreChangedBindable = Instance.new("BindableEvent") local Scoring = { teamScoreChanged = teamScoreChangedBindable.Event, } ``` It then calls the `incrementScore` function, which performs the following actions: - Grabs the player's team and their current teamPoints value on the `Class.Team` object in the `Class.Teams` service, then adds one. - Grabs the player's individual score on the leaderboard, and adds one. - Fires the previously mentioned bindable event with both the player's team and their score. This process effectively keeps both the client and the server aligned regarding both players' individual scores and their team scores. ```lua function Scoring.incrementScore(player: Player, amount: number) local team = player.Team assert(team, \`Player {player.Name} must be on a team to score a point, but has no team\`) local teamPoints = team:GetAttribute(GuiAttribute.teamPoints) teamPoints += amount team:SetAttribute(GuiAttribute.teamPoints, teamPoints) local leaderstat = player.leaderstats.Points leaderstat.Value += amount teamScoreChangedBindable:Fire(team, teamPoints) end ``` ## Display results As players tag each other and score points for their team, **ServerScriptService** ⟩ **Gameplay** ⟩ **Rounds** checks to see if the team that scored met the round objective. If their team score is lower than the `TEAM_SCORE_LIMIT` variable in **ReplicatedStorage** ⟩ **TEAM_SCORE_LIMIT**, the server continues to wait until one of the teams scores again. However, once a team's score reaches the `TEAM_SCORE_LIMIT` variable, the script fires a `roundWinnerRemote` event instance with the player's name and their team. ```lua -- Check if the round has finished after each score local team: Team local score: number = 0 while score < TEAM_SCORE_LIMIT do team, score = Scoring.teamScoreChanged:Wait() end -- Display winning team for _, player in Players:GetPlayers() do -- Sending what team the player is on at the end of the round -- because the player's team is about to be removed, so the client -- won't be able to check its own team roundWinnerRemote:FireClient(player, team, player.Team) end ``` The **ReplicatedStorage** ⟩ **RoundResultsGuiSetup** script on each client listens for this `roundWinnerRemote` event instance so it can: - Display a unique **StarterGui** ⟩ **RoundResultsGui** UI screen that announces the round's results and if the player was on the winning team. - Play a victory or defeat audio clip. For example, if a player is on the team that scored the winning point, they receive multiple forms of feedback on the round results in the form of a UI screen that displays victory text, and an audio clip that plays a joyful sound. Conversely, if a player is not on the team that scored the winning point, they receive a UI screen that displays defeat text, and an audio clip that plays an ominous sound. _Victory feedback_ _Defeat feedback_ ```lua local function onRoundWinner(winner: Team, localTeam: Team?) local victoryDefeatText = "Round ended!" if localTeam then -- If our team won, we'll display Victory! Otherwise display Defeat... local isVictory = winner == localTeam if isVictory then victorySound:Play() victoryDefeatText = VICTORY_TEXT else defeatSound:Play() victoryDefeatText = DEFEAT_TEXT end end ``` ## Reset teams At the same time that **ServerScriptService** ⟩ **Gameplay** ⟩ **Rounds** verifies that a team met the round objective and triggers the appropriate UI display for each player, it also transports all players from the arena to the lobby by disconnecting them from the round. This starts the process of formally ending the round and resetting both teams. Using the same logic in [Configure Spawn Locations](/docs/en-us/tutorials/curriculums/gameplay-scripting/spawn-respawn.md#configure-spawn-locations), **Rounds** then sets the **Neutral** spawn location's `Class.SpawnLocation.Neutral|Neutral` property to **true** so players can spawn there regardless of their team status. This means the lobby becomes the only location that players can spawn after they are disconnected from the round. ```lua -- Send everyone to the lobby playerAddedConnection:Disconnect() neutralSpawn.Neutral = true spawnPlayersInLobby(Players:GetPlayers()) ``` After waiting ten seconds for an intermission, the **Rounds** server script then starts the loop again by resetting everyone's scores and sorting them into new teams. The sample repeats this cyclical round process again until there aren't any players within the server. Now that players can spawn into the map with their own team and play a full round, the next section teaches you about the scripts behind each blaster's behavior. --- title: "Create teams" url: /docs/en-us/tutorials/curriculums/gameplay-scripting/create-teams last_updated: 2026-06-29T19:34:13Z description: "Explains how separate players into teams as they join an experience." --- # Create teams **Creating teams** lets you sort players into groups that compete to complete a common objective, such as scoring the most points or crossing a finish line before other enemy team members. For a first-person shooter, creating teams is particularly important because it establishes complex, coordinated gameplay strategies beyond the skill set of any individual player. Using the [sample laser tag experience](https://www.roblox.com/games/14817965191/Laser-Tag-1A) as a reference, this section of the tutorial teaches you how to sort players into teams, including scripting guidance on: - Using the `Class.Teams` service's default functionality to assign players into either the green or pink team. - Helping players differentiate between their allies and enemy team members through on-screen and in-experience team indicators. - Enabling forgiving gameplay that doesn't penalize players for blasting their teammates. After you complete this section, you will learn about the scripts that allow players to spawn or respawn to a neutral lobby or team spawn zone, customize first-person force field visuals, and handle client state both from the server and the client. ## Assign team colors The sample laser tag experience uses the `Class.Teams` service as a basis for creating two teams because the service offers built-in team sorting behavior that broadly works out-of-the-box. For example, without any additional scripting effort, the service handles actions like: - Sorting and balancing players evenly into each team. - Grouping players under their team on the leaderboard. - Tinting player names in the 3D space to their corresponding team color. - Spawning players to distinct spawn locations that only their team can access. Because of this default functionality, the sample enables the `Class.Teams` service, then uses two distinct `Class.Team` objects with different `Class.Team.Color` properties values to represent each team: **mint** for the team that assembles on one side of the map, and **carnation pink** for the team that assembles on the opposite side of the map. These specific colors are useful because they are complementary, meaning that they contrast each other well, and they enable players to easily scan their surroundings and orient themselves within the building and to other players. > **Info:** To learn more about this color theme, see [Select a Color Theme](/docs/en-us/tutorials/curriculums/user-interface-design/choose-an-art-style.md#select-a-color-theme) from the UI Curriculum. It's important to note the exact `Datatype.BrickColor` name for the `Class.Team.Color` property because the experience uses this name throughout many scripts in the experience to keep track of players as they sort into teams, increment points as players tag out enemy players during a round, and update custom UI elements. For example, when a round begins, **ReplicatedStorage** ⟩ **HUDGuiSetup** requires `startSyncingTeamColor`,and `startSyncingTeamPoints` which both reference `Class.Team.Color` to perform unique actions for each team. #### startSyncingTeamColor `startSyncingTeamColor` references `Class.Team.Color` so that it can assign the correct color and icon for the player's team indicator at the bottom-left corner of their screen. _Green Team_ _Pink Team_ ```lua local function setPlayerTeamIcon(gui: ScreenGui) for _, teamColorIcon in gui.PlayerDisplay.TeamIcons:GetChildren() do local iconTeamColor = teamColorIcon:GetAttribute(GuiAttribute.teamColor) teamColorIcon.Visible = localPlayer.TeamColor == iconTeamColor end end local function startSyncingTeamColor(gui: ScreenGui) setPlayerTeamIcon(gui) localPlayer:GetPropertyChangedSignal("Team"):Connect(function() setPlayerTeamIcon(gui) end) end ``` #### startSyncingTeamPoints `startSyncingTeamPoints` references `Class.Team.Color` so that it can track team points correctly in the objective UI at the top of the screen. ```lua local function getTeamFromTeamColor(teamColor: Color3): Team? for _, team in Teams:GetTeams() do if team.TeamColor == teamColor then return team end end return nil end local function startSyncingTeamPoints(gui: ScreenGui) for _, teamPointCounter in gui.Objective.TeamPointCounter:GetChildren() do if not teamPointCounter:IsA("GuiObject") then continue end local iconTeamColor = teamPointCounter:GetAttribute(GuiAttribute.teamColor) local team = getTeamFromTeamColor(iconTeamColor) if not team then warn(\`No team found matching the color {iconTeamColor} to sync team points on {teamPointCounter}\`) continue end teamPointCounter.TextLabel.Text = team:GetAttribute(GuiAttribute.teamPoints) team:GetAttributeChangedSignal(GuiAttribute.teamPoints):Connect(function() teamPointCounter.TextLabel.Text = team:GetAttribute(GuiAttribute.teamPoints) end) end end ``` ## Display team indicators Once a player is sorted into a team, they need to be able to quickly decipher what team they belong to, and differentiate between their allies and enemy team members. This ability is important because the gameplay of a first-person shooter experience requires players to make quick strategic decisions while they're in combat zones so that they don't get tagged out and lose the round. To set players up for success, the sample laser tag experience provides multiple team indicators both on-screen and in the 3D space: - **Player Indicator** – Custom on-screen UI that displays the player's team color and icon. - **Team Indicator** – Custom in-experience UI that displays the player's team color. - **Leaderboard Indicator** – Default Teams service on-screen UI that groups players underneath their team color. - **Name Tint** – Default Teams service in-experience UI that tints the player's name above their head to their team color. **On-screen Indicators** _Player Indicator_ _Leaderboard Indicator_ **In-Experience Indicators** _Team Indicator_ _Name Tint_ You don't need to do any additional work for the Leaderboard Indicator or Name Tint outside of assigning team colors from the previous section of the tutorial, but the custom UI team indicators require more programming effort to both display the indicators and occlude enemy indicators when they are behind walls. For a full explanation of the scripts that control the custom UI behavior, as well as how to implement each UI element itself, see [Player Indicator](/docs/en-us/tutorials/curriculums/user-interface-design/implement-designs-in-studio.md#player-indicator) and [Team Indicator](/docs/en-us/tutorials/curriculums/user-interface-design/implement-designs-in-studio.md#team-indicator) from the UI Curriculum. ## Disable friendly fire While some first-person shooter experiences penalize players who blast their own teammates, the sample laser tag experience allows for more forgiving gameplay by disabling friendly fire. This design decision allows everyone to only increase their team score, not subtract from it due to gameplay accidents. To understand how the sample implements this functionality, examine how **ServerScriptService** ⟩ **LaserBlastHandler** ⟩ **processTaggedPlayers** ⟩ **onPlayerTagged** handles dealing damage to tagged players. When the server detects a collision between a player's blast and another player, it calls the `onPlayerTagged` function to understand which player fired the blast, which player was hit by the blast, and how much damage it should remove from the player's health according to the blaster type. ```lua local function onPlayerTagged(playerBlasted: Player, playerTagged: Player, damageAmount: number) ``` Before it decreases health, the script first verifies if the tagged player is on the same team as the player who initiated the blast. If they are on the same team, the script ignores the blast data entirely. ```lua local character = playerTagged.Character local isFriendly = playerBlasted.Team == playerTagged.Team -- Disallow friendly fire if isFriendly then return end ``` However, if the tagged player **is** on the enemy team, the player takes the appropriate amount of damage according to the blaster type. In addition, if the player takes enough damage to become tagged out, the script rewards a point to the team of the player that initiated the blast. For more information on how the sample tracks points, see [Add rounds](/docs/en-us/tutorials/curriculums/gameplay-scripting/add-rounds.md#track-points) later in the tutorial. ```lua local humanoid = character and character:FindFirstChild("Humanoid") if humanoid and humanoid.Health > 0 then local damage = math.min(damageAmount, humanoid.Health) humanoid:TakeDamage(damage) if humanoid.Health <= 0 then Scoring.incrementScore(playerBlasted, 1) end end end return onPlayerTagged ``` > **Info:** For more information on what happens when a player fires their blaster, see [Implement blaster behavior](/docs/en-us/tutorials/curriculums/gameplay-scripting/implement-blasters.md) and [Detect hits](/docs/en-us/tutorials/curriculums/gameplay-scripting/detect-hits.md) later on in the tutorial. If you were to test the experience right now, all players would randomly spawn at either spawn zone at opposite ends of the arena regardless of what team they belong to, meaning that each team would spawn right next to each other. To address this issue, the following section of the tutorial teaches you about the custom logic behind restricting team spawning to certain spawn locations, handling client state as players leave the spawn area after selecting their blaster, and respawning players back to their spawn zone or lobby area when their health reaches zero. --- title: "Detect hits" url: /docs/en-us/tutorials/curriculums/gameplay-scripting/detect-hits last_updated: 2026-06-29T19:34:13Z description: "Explains how hit detection works in a laser tag experience." --- # Detect hits **Detecting hits** is the process of identifying when blasts collide with players, then reducing their health accordingly. At a high-level, you can think of this work as either: 1. A physically simulated check of whether a projectile struck the target. 2. An instantaneous check of whether the blaster was aimed at the target. The type of hit detection you use depends on the gameplay requirements of your experience. For example, a physically simulated check is appropriate for a dodgeball experience where balls need to leave the hand at a certain velocity, drop as they move through the air, or change direction from weather conditions. However, an instantaneous check is a better match for a laser tag experience where beams must have a near-infinite velocity and ignore environmental factors like gravity and wind speed. Using the [sample laser tag experience](https://www.roblox.com/games/14817965191/Laser-Tag-1A) as a reference, this section of the tutorial teaches you about the scripts behind hit detection in the 3D space, including guidance on: - Getting the blast direction from the current camera values and the player's blaster type. - Casting rays in a straight path from the blaster as it blasts. - Validating the blast to prevent exploitation of blaster data. - Reducing player health according to the blast damage from each type of blaster and how many rays hit the player. After you complete this section, you can explore additional development topics to enhance your gameplay, such as audio, lighting, and special effects. ## Get blast direction After a player blasts their blaster, **ReplicatedStorage** ⟩ **Blaster** ⟩ **attemptBlastClient** ⟩ **blastClient** ⟩ **generateBlastData** calls two functions to start the hit detection process: `rayDirections()` and `rayResults()`. ```lua local rayDirections = getDirectionsForBlast(currentCamera.CFrame, blasterConfig) local rayResults = castLaserRay(localPlayer, currentCamera.CFrame.Position, rayDirections) ``` The inputs for `rayDirections` are straightforward: the current camera position and rotation values, and the player's blaster type. If the sample laser tag experience only gave players blasters that produce a single laser beam, **ReplicatedStorage** ⟩ **LaserRay** ⟩ **getDirectionsForBlast** would be unnecessary because you could use `currentCamera.CFrame.LookVector` to calculate the direction for the blast. However, because the sample provides an additional blaster type that produces several laser beams with a wide, horizontal spread, `getDirectionsForBlast` must calculate the direction for each laser beam of the spread according to their angles within the blaster configuration: ```lua if numLasers == 1 then -- For single lasers, they aim straight table.insert(directions, originCFrame.LookVector) elseif numLasers > 1 then -- For multiple lasers, spread them out evenly horizontally -- over an interval laserSpreadDegrees around the center local leftAngleBound = laserSpreadDegrees / 2 local rightAngleBound = -leftAngleBound local degreeInterval = laserSpreadDegrees / (numLasers - 1) for angle = rightAngleBound, leftAngleBound, degreeInterval do local direction = (originCFrame * CFrame.Angles(0, math.rad(angle), 0)).LookVector table.insert(directions, direction) end end ``` To demonstrate this concept further, if you were to include a third blaster type with a wide, **vertical** spread, you could create a new blaster attribute, such as `spreadDirection`, then adjust the `Datatype.CFrame` calculation to use a different axis. For example, note the difference in the `direction` calculations in the following script below for this hypothetical third blaster type. ```lua if numLasers == 1 then table.insert(directions, originCFrame.LookVector) elseif numLasers > 1 then local leftAngleBound = laserSpreadDegrees / 2 local rightAngleBound = -leftAngleBound local degreeInterval = laserSpreadDegrees / (numLasers - 1) for angle = rightAngleBound, leftAngleBound, degreeInterval do local direction if spreadDirection == "vertical" then direction = (originCFrame * CFrame.Angles(math.rad(angle), 0, 0)).LookVector else direction = (originCFrame * CFrame.Angles(0, math.rad(angle), 0)).LookVector end table.insert(directions, direction) end end return directions ``` Ultimately, the `rayDirections()` function returns a table of `Datatype.Vector3|Vectors` that represent the direction of each laser beam. If it's helpful, you can add some logging to get a sense of what this data looks like. ```lua local rayDirections = getDirectionsForBlast(currentCamera.CFrame, blasterConfig) for _, direction in rayDirections do -- new line print(direction) -- new line end -- new line local rayResults = castLaserRay(localPlayer, currentCamera.CFrame.Position, rayDirections) ``` ## Cast rays `castLaserRay()`, the second function in **ReplicatedStorage** ⟩ **Blaster** ⟩ **attemptBlastClient** ⟩ **blastClient** ⟩ **generateBlastData**, performs the more complex operations within the script. It begins by specifying parameters so that it can make `Class.Workspace:Raycast()` calls for raycasting purposes. Raycasting is the process of sending out an invisible ray from a `Datatype.Vector3` point in a specific direction with a defined length, then checking its path to see where it intersects with other objects. This information is particularly useful for first-person shooter experiences because it allows you to see when and where blasts intersect with players or the environment. For example, the following image demonstrates two rays that are casting parallel to each other. According to their point of origin and direction, Ray A misses the wall and continues until it meets its maximum distance, while Ray B collides with the wall. For more information on this process, see [Raycasting](/docs/en-us/workspace/raycasting.md). ![A diagram where Ray A continues through the wall, and Ray B collides with the wall.](../../../assets/tutorials/gameplay-scripting/Detecting-Hits/Two-Rays.png) The `castLaserRay()` parameters specify that `Raycast()` calls must consider every part in the workspace **except** the character who blasted. The script then casts a ray for each direction in the `directions` table. If a ray hits something, it generates a `Datatype.RaycastResult`, which has five properties: - `Datatype.RaycastResult.Distance|Distance` – The distance between the ray origin and the intersection point. - `Datatype.RaycastResult.Instance|Instance` – The `Class.BasePart` or `Class.Terrain` cell that the ray intersects. - `Datatype.RaycastResult.Material|Material` – The `Enum.Material` at the intersection point. - `Datatype.RaycastResult.Position|Position` – The `Datatype.Vector3` position of the intersection between the ray and the Instance. - `Datatype.RaycastResult.Normal|Normal` – The `Datatype.Vector3` of the normal vector of the face the ray intersects with. > **Warning:** By default, `Raycast()` does **not** respect the `Class.BasePart.CanCollide` property. If some parts in your experience are strictly decorative, consider how you want them to behave and see `Datatype.RaycastParams.RespectCanCollide`. The `Datatype.RaycastResult.Instance|Instance` value is the most critical of these properties for the sample laser tag experience's gameplay because it communicates when rays collide with other players. To retrieve this information, the experience uses the **ReplicatedStorage** ⟩ **LaserRay** ⟩ **castLaserRay** ⟩ **getPlayerFromDescendant** helper function. If it returns `nil`, the instance isn't part of a player, meaning the ray hit an inanimate object within the environment. `castLaserRay()` then uses `Datatype.RaycastResult.Position|Position` and `Datatype.RaycastResult.Normal|Normal` to create a new `Datatype.CFrame` that it calls the ray's `destination`. Every ray has a destination, and it's either where the ray hit in the 3D space, or the point at the end of its maximum distance. Depending on how well your players aim, many or most `taggedPlayer` values are `nil`. ```lua if result then -- The blast hit something, check if it was a player. destination = CFrame.lookAt(result.Position, result.Position + result.Normal) taggedPlayer = getPlayerFromDescendant(result.Instance) else -- The blast didn't hit anything, so its destination is -- the point at its maximum distance. local distantPosition = origin + rayDirection * MAX_DISTANCE destination = CFrame.lookAt(distantPosition, distantPosition - rayDirection) taggedPlayer = nil end ``` > **Info:**`Datatype.RaycastResult.Distance|Distance` isn't particularly interesting in this section's scripts, but you could utilize it in unique ways if you want blasters to inflict more damage at a close range. Similarly, this section's scripts don't consider `Datatype.RaycastResult.Material|Material`, but you could use material types to distinguish between an armored body and a set of weak points during damage calculations. ## Validate the blast To prevent cheating, the previous chapter [Implementing Blasters](/docs/en-us/tutorials/curriculums/gameplay-scripting/implement-blasters.md) explains how `blastClient` notifies the server of the blast using a `Class.RemoteEvent` so that it can verify all data that each client sends, such as whether or not they truly tagged another player with their blaster. This ray validation process occurs in **ServerScriptService** ⟩ **LaserBlastHandler** ⟩ **getValidatedBlastData** ⟩ **getValidatedRayResults**, and each check correlates to a nested module script: 1. First, `getValidatedRayResults` calls `validateRayResult` to check that each entry in the `rayResults` table from the client is a `Datatype.CFrame` and a `Player` (or nil). 2. Next, it calls `isRayAngleFromOriginValid` to compare the expected angles of the laser spread to the ones from the client. This code in particular shows the advantage of using `ReplicatedStorage` because the server can call `getDirectionsForBlast` itself, store the return as the "expected" data, and then compare it against the data from the client. Just like blaster validation from the previous chapter, `isRayAngleFromOriginValid` relies on a tolerance value to determine what constitutes an "excessive" difference in angles:```lua local claimedDirection = (rayResult.destination.Position - originCFrame.Position).Unit local directionErrorDegrees = getAngleBetweenDirections(claimedDirection, expectedDirection) return directionErrorDegrees <= ToleranceValues.BLAST_ANGLE_SANITY_CHECK_TOLERANCE_DEGREES ``` Roblox abstracts away the most involved bits of math, so the result is a short, highly reusable helper function with applicability across a range of experiences:```lua local function getAngleBetweenDirections(directionA: Vector3, directionB: Vector3) local dotProduct = directionA:Dot(directionB) local cosAngle = math.clamp(dotProduct, -1, 1) local angle = math.acos(cosAngle) return math.deg(angle) end ``` 3. The next check is the most intuitive. Whereas `getValidatedBlastData` uses `DISTANCE_SANITY_CHECK_TOLERANCE_STUDS` to verify that the player who blasted was near the beam's point of origin, `isPlayerNearPosition` uses identical logic to check if the tagged player was near the beam's destination:```lua local distanceFromCharacterToPosition = position - character:GetPivot().Position if distanceFromCharacterToPosition.Magnitude > ToleranceValues.DISTANCE_SANITY_CHECK_TOLERANCE_STUDS then return false end ``` 4. The final check `isRayPathObstructed` uses a variation of the ray cast operation to check if the ray's destination is behind a wall or other obstruction from the client's position. For example, if a malicious player were to systematically remove all walls from the experience to tag other players, the server would check and confirm that the rays are invalid because it knows every object position within the environment.```lua local scaledDirection = (rayResult.destination.Position - blastData.originCFrame.Position) scaledDirection *= (scaledDirection.Magnitude - 1) / scaledDirection.Magnitude ``` No anti-exploit strategy is comprehensive, but it's important to consider how malicious players may approach your experience so that you can put checks in place that the server can run to flag suspicious behavior. ## Reduce player health After verifying that a player tagged another player, the final steps in completing the main gameplay loop in the sample laser tag experience are to reduce the tagged player's health, increment the leaderboard, and respawn the player back into the round. Starting with reducing the tagged player's health, [Spawning and respawning](/docs/en-us/tutorials/curriculums/gameplay-scripting/spawn-respawn.md) covers the distinction between `Class.Player` and `Class.Player.Character`, specifically that a character is a `Class.Humanoid` model. `Class.Humanoid` models have a `Class.Humanoid.Health|Health` property with a default value of 100. Rather than implementing its own system, the sample laser tag experience uses this built-in property to keep track of how much damage a player needs before they are tagged out of the round. The experience stores damage values in the `damagePerHit` attribute of each blaster. For example, the blaster that blasts a single laser beam inflicts 10 points of damage, so it takes ten blasts with this blaster to tag out another player. To start the process of tagging a player out, `LaserBlastHandler` calls **ServerScriptService** ⟩ **LaserBlastHandler** ⟩ **processTaggedPlayers**, which checks the now-validated `rayResults` table for players and passes `damagePerHit` to `onPlayerTagged`. > **Info:** Note that this process occurs for each **ray**, not each player. A blast can have multiple laser beams, so a player can receive damage several times from a single blast. `Class.Humanoid.Health|Health` doesn't accept negative values, so `onPlayerTagged` has some logic to keep player health at or above zero. After verifying that player health is above zero, it compares health to `damagePerHit` and uses the smaller of the two values. For example, if a player has 10 health and is hit by a 15 damage laser beam, the laser only inflicts 10 points of damage. This way of approaching the problem might seem a bit convoluted. For example, why not just set player health to zero if it would be negative? The reason is because setting health values circumvents the force field. Using the `Class.Humanoid:TakeDamage()` method ensures that players don't take damage while their force fields are active. ```lua local function onPlayerTagged(playerBlasted: Player, playerTagged: Player, damageAmount: number) local character = playerTagged.Character local isFriendly = playerBlasted.Team == playerTagged.Team -- Disallow friendly fire if isFriendly then return end local humanoid = character and character:FindFirstChild("Humanoid") if humanoid and humanoid.Health > 0 then -- Avoid negative health local damage = math.min(damageAmount, humanoid.Health) -- TakeDamage ensures health is not lowered if ForceField is active humanoid:TakeDamage(damage) if humanoid.Health <= 0 then -- Award playerBlasted a point for tagging playerTagged Scoring.incrementScore(playerBlasted, 1) end end end ``` The next step is to increment the leaderboard. It might have seemed unnecessary for `LaserBlastHandler` to include the player who blasted alongside the blast data, but without that information, the experience can't credit the player with tagging someone out. Finally, the tagged out player respawns back into the round, which you can review in [Spawning and Respawning](/docs/en-us/tutorials/curriculums/gameplay-scripting/spawn-respawn.md). The five chapters in this curriculum cover the experience's core gameplay loop, but there are still plenty of areas to explore, such as: - **Blaster visuals**: See **ReplicatedStorage** ⟩ **FirstPersonBlasterVisuals** and **ServerScriptService** ⟩ **ThirdPersonBlasterVisuals**. - **Audio**: See **ReplicatedStorage** ⟩ **SoundHandler**. - **Custom Modes**: How could you modify this experience to introduce new types of objectives, such as scoring the most points before the time runs out? For extended gameplay logic for the laser tag experience, as well as reusable, high-quality environmental assets, review the [Laser Tag](/docs/en-us/resources/templates.md#laser-tag) template. > **Info:** We're interested in hearing from you about your experience following the Gameplay Scripting Curriculum. If you have any questions, concerns, or additional feedback on the process, please comment on our [Gameplay Scripting Curriculum Q&A](https://devforum.roblox.com/t/gameplay-scripting-curriculum-qa/2731896). --- title: "Implement blaster behavior" url: /docs/en-us/tutorials/curriculums/gameplay-scripting/implement-blasters last_updated: 2026-06-29T19:34:13Z description: "Explains end-to-end how a blast mechanic works in a laser tag experience." --- # Implement blaster behavior **Implementing blaster behavior** is the process of programming a blast mechanic in first-person shooter experiences. While players can blast with a single click or press of a button, creating a satisfying and accurate blast behavior is important because it enhances players' enjoyment of the overall gameplay. Using the [sample laser tag experience](https://www.roblox.com/games/14817965191/Laser-Tag-1A) as a reference, this section of the tutorial teaches you about the scripts behind implementing blaster behavior for two different types of blasters, including guidance on: - Detecting when players press the blast button. - Checking whether the player can use their blaster if they recently pressed the blast button. - Generating blast data that tells the server who initiated the blast, where it came from, and what was each laser beam's final destination. - Notifying the server of the blast data so it can perform the appropriate actions if the blast collided with another player. - Resetting the blaster between each blast to give the blaster enough time to cool down before it can blast again. After you complete this section, you will learn about the scripts that allow the blaster to detect when its blasts collide with other players, then deduct the corresponding amount of health according to each blaster type. ## Detect player input The first step to implementing blaster behavior is to listen for when a player presses the blast button. The input type that players use to press the blast button depends on which device they're using to access the experience. For example, the sample laser tag experience supports mouse and keyboard controls, gamepads, and touch controls. You can see each of these input types in **ReplicatedStorage** ⟩ **UserInputHandler**. This client script uses `Class.ContextActionService` to bind `MouseButton1` and `ButtonR2` to the blasting action. This means that every time a player either presses a left mouse button or a gamepad's R2 button, it triggers a laser beam to blast out of the blaster. Note that the **HUDGui** contains a button for blasting on mobile devices, which it connects to later in the script. ```lua ContextActionService:BindAction("_", onBlasterActivated, false, Enum.UserInputType.MouseButton1, Enum.KeyCode.ButtonR2 ) ``` Another important note is the use of `Enum.UserInputState.Begin` in the `onBlasterActivated()` definition. Many user interface interactions, such as choosing a blaster in this example, don't occur until after the mouse button comes up (`Enum.UserInputState.End`), which gives users a last-second chance to avoid the interaction. However, a blasting mechanic doesn't feel responsive unless it occurs the instant the button goes down. To demonstrate, you can change `Enum.UserInputState.Begin` to `Enum.UserInputState.End`, then playtest to see how the responsiveness of the blast impacts the gameplay of the experience. For example, if players can hold down the button without triggering the blast, how might that change the their experience while tagging other players? ```lua local function onBlasterActivated(_actionName: string, inputState: Enum.UserInputState, _inputObject: InputObject) if inputState == Enum.UserInputState.End then -- updated line, be sure to change back attemptBlastClient() end end ``` ## Check whether the player can blast After `UserInputHandler` detects a button press or screen tap, it calls **ReplicatedStorage** ⟩ **Blaster** ⟩ **attemptBlastClient** to check whether the player can blast or not. Like most checks in the sample laser tag experience, it occurs twice: first on the client, then later on the server. `attemptBlastClient` then calls **ReplicatedStorage** ⟩ **Blaster** ⟩ **canLocalPlayerBlast** to perform a simple check of the `blasterStateClient` player attribute: ```lua local function canLocalPlayerBlast(): boolean return localPlayer:GetAttribute(PlayerAttribute.blasterStateClient) == BlasterState.Ready end ``` If you examine **ReplicatedStorage** ⟩ **Blaster** ⟩ **BlasterState**, you can see that the experience has three blaster states: `Ready`, `Blasting`, and `Disabled`. To see the effect of each of these states, you can playtest the experience, select your player under the **Players** service, then observe the **blasterStateClient** attribute in the **Properties** window. Notice how it displays `Disabled` while you choose your blaster, `Ready` most of the time, and `Blasting` for less than a second after you press the button. This slight pause prevents you from being able to blast as quickly as you can click. For example, if you change the function to always return true, you can rapidly blast your blaster without any delay, which is unrealistic for laser tag gameplay. ```lua local function canLocalPlayerBlast(): boolean return true -- updated line, be sure to change back end ``` ## Generate blast data After verifying that the player's blaster is in the `Ready` state, `attemptBlastClient` calls **ReplicatedStorage** ⟩ **attemptBlastClient** ⟩ **blastClient**. The first step that `blastClient` takes is to set the `blasterStateClient` player attribute to `Blasting`, which avoids the same rapid fire case from earlier. > **Info:** Changing `BlasterState` to `Blasting` also plays a sound in **ReplicatedStorage** ⟩ **SoundHandler**. The next step is to generate the blast data. If you review **ReplicatedStorage** ⟩ **Blaster** ⟩ **BlastData**, you can see that each blast consists of three pieces of information: - The player who initiates the blast. - A `DataType.CFrame` that represents the blast's point of origin. - A `RayResult` table that contains each laser beam's final destination and the hit player, if hit another player. To generate this data, `blastClient` calls **ReplicatedStorage** ⟩ **attemptBlastClient** ⟩ **blastClient** ⟩ **generateBlastData**, which you can review below. ```lua local function generateBlastData(): BlastData.Type local blasterConfig = getBlasterConfig() local rayDirections = getDirectionsForBlast( currentCamera.CFrame, blasterConfig) local rayResults = castLaserRay( localPlayer, currentCamera.CFrame.Position, rayDirections) local blastData: BlastData.Type = { player = localPlayer, originCFrame = currentCamera.CFrame, rayResults = rayResults, } return blastData end ``` This function starts by using `getBlasterConfig` to retrieve the player's blaster type. The sample provides two types of blasters: one that produces several beams with a wide, horizontal spread, and another that produces a single beam. You can find their configurations in **ReplicatedStorage** ⟩ **Instances** ⟩ **LaserBlastersFolder**. The function then uses `currentCamera.CFrame` as the point of origin for the blast, passing it to `getDirectionsForBlast`. At this point, the code is no longer about the blaster, it's about the laser beam, which you will learn more about in the [detect hits](/docs/en-us/tutorials/curriculums/gameplay-scripting/detect-hits.md) section of the tutorial. Finally, after creating the `rayResults` table, `generateBlastData` has all the information it needs to return the blast data to `blastClient`. ## Notify the server Once `blastClient` has complete data for the blast, it fires two events: ```lua local laserBlastedBindableEvent = ReplicatedStorage.Instances.LaserBlastedBindableEvent local laserBlastedEvent = ReplicatedStorage.Instances.LaserBlastedEvent laserBlastedBindableEvent:Fire(blastData) laserBlastedEvent:FireServer(blastData) ``` The `Class.BindableEvent` notifies other client scripts of the blast. For example, **ReplicatedStorage** ⟩ **FirstPersonBlasterVisuals** uses this event to know when to display visual effects, such as the blast animation and cooldown bar. Similarly, the `Class.RemoteEvent` notifies server scripts of the blast, which begins processing the blast in **ServerScriptService** ⟩ **LaserBlastHandler**. ```lua local function onLaserBlastedEvent(playerBlasted: Player, blastData: BlastData.Type) local validatedBlastData = getValidatedBlastData(playerBlasted, blastData) if not validatedBlastData then return end if not canPlayerBlast(playerBlasted) then return end blastServer(playerBlasted) processTaggedPlayers(playerBlasted, blastData) for _, replicateToPlayer in Players:GetPlayers() do if playerBlasted == replicateToPlayer then continue end replicateBlastEvent:FireClient(replicateToPlayer, playerBlasted, blastData) end end ``` To help prevent cheating, the server must verify all data that each client sends. These checks include: 1. Is `BlastData` a table? Does it contain a `Class.CFrame` and another table named `rayResults`? 2. Does the player have a blaster equipped? 3. Does the player have a character and a location within the world? 4. After sending the blast data, has the player moved an excessive distance away from where they blasted the laser beam? This last check involves a judgment call, and according to server latency and player movement speed, you might decide that different values are excessive for your own experience. To demonstrate how to make this judgment call, you can get a sense of the typical magnitude of positional change by adding a print statement in `getValidatedBlastData` and playtesting the experience. ```lua local distanceFromCharacterToOrigin = blastData.originCFrame.Position - rootPartCFrame.Position print(distanceFromCharacterToOrigin.Magnitude) -- updated line, be sure to remove if distanceFromCharacterToOrigin.Magnitude > ToleranceValues.DISTANCE_SANITY_CHECK_TOLERANCE_STUDS then warn(\`Player {player.Name} failed an origin sanity check while blasting\`) return end ``` As you move and blast, note the output. It might look something like this: ```text 1.9019629955291748 3.1549558639526367 2.5742883682250977 4.8044586181640625 2.6434271335601807 ``` If you increase the movement speed for players in **ReplicatedStorage** ⟩ **PlayerStateHandler** ⟩ **togglePlayerMovement**, then playtest again, you will likely encounter many failed checks due to excessive movement between blasts. ```lua local ENABLED_WALK_SPEED = 60 -- updated line, be sure to change back ``` > **Info:** For this reason, if you decide to increase movement speed, consider adjusting `DISTANCE_SANITY_CHECK_TOLERANCE_STUDS` in **ServerStorage** ⟩ **ToleranceValues**. For more information on how to approach this problem, see [Movement Validation](/docs/en-us/scripting/security/security-tactics.md#movement-validation). The server then does the following: - Validates `rayResults`. - Checks whether the player can blast. - Resets the blaster state. - Reduces health for any tagged players. - Replicates the blast to all other players so that they can see third-person visuals. For more information on these server operations, see the [detect hits](/docs/en-us/tutorials/curriculums/gameplay-scripting/detect-hits.md) section of the tutorial. ## Reset the blaster In the sample laser tag experience, blasters use a heat mechanic. Rather than reloading after a set number of blasts, they need time to "cool down" between each blast. This same cooldown delay occurs on both the client (`blastClient`) and the server (`blastServer`), with the server acting as the source of truth. ```lua local blasterConfig = getBlasterConfig(player) local secondsBetweenBlasts = blasterConfig:GetAttribute("secondsBetweenBlasts") task.delay(secondsBetweenBlasts, function() local currentState = player:GetAttribute(PlayerAttribute.blasterStateServer) if currentState == BlasterState.Blasting then player:SetAttribute(PlayerAttribute.blasterStateServer, BlasterState.Ready) end end) ``` The `secondsBetweenBlasts` attribute is part of the blaster configuration in **ReplicatedStorage** ⟩ **Instances** ⟩ **LaserBlastersFolder**. After the `secondsBetweenBlasts` delay passes, the player can blast again, and the entire process repeats. To help the player understand when they can blast again, the experience includes a cooldown bar. At this point, players can spawn and respawn, aim and blast, but the experience still has to determine the results of each blast. In the next section of the tutorial, you will learn how to program the ability for the blaster to detect when the blast hits another player, then reduce the appropriate amount of player health according to blaster settings. --- title: "Gameplay scripting curriculum" url: /docs/en-us/tutorials/curriculums/gameplay-scripting last_updated: 2026-06-29T19:34:13Z description: "Learn how to implement and organize a complex scripting project." --- # Gameplay scripting curriculum **Gameplay scripting** is the discipline of programming the behavior that makes experiences engaging and fun to play, such as their logic, movement, events, and interactions between objects in the 3D space. You'll learn the gameplay scripting for a first-person shooter laser tag experience by following the general organization and key implementation details of a large, complex project, including several opportunities to create new behavior with custom values. This course is intended for readers who are familiar with general coding concepts, Studio's data model, and the client-server relationship. If you need help learning how to code, try [coding fundamentals](/docs/en-us/fundamentals/coding-1/coding-fundamentals.md). ## Course Contents **#### Chapter 1 - Create teams** Learn how to [sort players into teams](/docs/en-us/tutorials/curriculums/create-teams.md) when a round is active. **#### Chapter 2 - Spawn and respawn** Learn how to [trigger unique actions](/docs/en-us/tutorials/curriculums/spawn-respawn.md) when players spawn and respawn back into a round. **#### Chapter 3 - Add rounds** Learn how to [implement rounds](/docs/en-us/tutorials/curriculums/add-rounds.md) that track points, display final results, and reset teams. **#### Chapter 4 - Implement blasters** Learn how to [script blast behavior](/docs/en-us/tutorials/curriculums/implement-blasters.md) for two different blaster types. **#### Chapter 5 - Detect hits** Learn how to [perform hit detection](/docs/en-us/tutorials/curriculums/detect-hits.md) from blast data, and allocate damage to reduce player health. --- title: "Spawn and respawn" url: /docs/en-us/tutorials/curriculums/gameplay-scripting/spawn-respawn last_updated: 2026-06-29T19:34:13Z description: "Explains how spawning and respawning works in a laser tag experience." --- # Spawn and respawn **Spawning** is the process of creating an object or character in an experience, and **respawning** is the process of adding an object or character back into an experience after they meet a removal condition, such as a character's health reaching zero or falling off the map. Both processes are important because they ensure players are able to join your experience, and can continue playing to improve their skills. Using the [sample laser tag experience](https://www.roblox.com/games/14817965191/Laser-Tag-1A) as a reference, this section of the tutorial teaches you how to use and customize Roblox's built-in features to handle spawning and respawning, including scripting guidance on: - Configuring spawn locations so that players can only spawn into their team's spawn zone. - Adding new players and their character to the round as they join the experience. - Customizing force fields that prevent damage as players spawn and respawn. - Handling client state so gameplay works correctly at the appropriate time. - Respawning characters after they are tagged out of the round. - Performing small, miscellaneous actions that are crucial for setting gameplay and character parameters. This section includes plenty of scripting content, but instead of writing everything from scratch when creating an experience, it encourages you to leverage existing components, rapidly iterate, and figure out which systems need a custom implementation to match your vision. After you complete this section, you will learn how to implement round-based gameplay that tracks points, monitors player state, and displays round results. ## Configure spawn locations If you were to playtest the experience right now, all players would randomly spawn at either the `Class.SpawnLocation` object in the green team's spawn zone, or the `Class.SpawnLocation` object in the pink team's spawn zone. This presents a gameplay issue where players could tag each other within each spawn zone as soon as their opponent's force field disappears. To combat this problem, the sample laser tag experience configures both spawn locations with a `Class.SpawnLocation.Neutral|Neutral` property set to **false** to restrict the opposing team's players from spawning in the wrong spawn zone, and a `Class.SpawnLocation.TeamColor|TeamColor` property set to the corresponding `Class.Team.Color` value from [Assign Team Colors](/docs/en-us/tutorials/curriculums/gameplay-scripting/create-teams.md#assign-team-colors) in the previous section of the tutorial: - **TeamASpawn** – The spawn location in the green team's spawn zone with a `Class.SpawnLocation.TeamColor|TeamColor` property set to **Mint**. - **TeamBSpawn** – The spawn location in the pink team's spawn zone with a `Class.SpawnLocation.TeamColor|TeamColor` property set to **Carnation Pink**. _TeamASpawn_ _TeamBSpawn_ > **Warning:** The `Class.SpawnLocation.TeamColor` property is different from the `Class.SpawnLocation.BrickColor` and `Class.SpawnLocation.Color` properties which represent the visual color of the spawn object and are not related to team functionality. When a player joins the experience, **ServerScriptService** ⟩ **Gameplay** ⟩ **Rounds** ⟩ **spawnPlayersInMap** checks to see how many players are already on each team, then returns the team with the least amount of players. ```lua local function getSmallestTeam(): Team local teams = Teams:GetTeams() -- Sort teams in ascending order from smallest to largest table.sort(teams, function(teamA: Team, teamB: Team) return #teamA:GetPlayers() < #teamB:GetPlayers() end) -- Return the smallest team return teams[1] end ``` Once it knows the team with the least amount of players, it sorts the player into that team, sets their `Class.Player.Neutral` property to **false** so the player can only spawn and respawn to their team's spawn location, then sets their `PlayerState` to `SelectingBlaster`, which you'll learn more about later in the tutorial. ```lua local function spawnPlayersInMap(players: { Player }) for _, player in players do player.Team = getSmallestTeam() player.Neutral = false player:SetAttribute(PlayerAttribute.playerState, PlayerState.SelectingBlaster) task.spawn(function() player:LoadCharacter() end) end end ``` If you examine **Workspace** ⟩ **World** ⟩ **Map** ⟩ **Spawns**, you can see that there is one more spawn location in the map: **NeutralSpawn**. This spawn location is unique from the others because it doesn't have a `Class.SpawnLocation.TeamColor|TeamColor` property set to one of the two teams in the experience; instead, this spawn location has a `Class.SpawnLocation.Neutral|Neutral` property that changes depending on if a round is active. For example, if the round is active, the `Class.SpawnLocation.Neutral|Neutral` property sets to **false** so **spawnPlayersInMap** can sort players into teams and spawn them into the arena. However, if the round isn't active, such as the time between one round and the next, the `Class.SpawnLocation.Neutral|Neutral` property sets to **true** so players can spawn there regardless of their team status. This process is what makes the **Neutral** spawn location a functional lobby. _Neutral_ To demonstrate, if you examine **ServerScriptService** ⟩ **Gameplay** ⟩ **Rounds** ⟩ **SpawnPlayersInLobby**, which runs at the end of a round, you can see that for every player that is passed into the `players: { Player }` table, the script: - Sets their `Class.Player.Neutral` property to true to automatically reset their `Class.Player.Team` to `nil`, allowing the player to respawn in the lobby when a round isn't active, as the spawn location's `Class.SpawnLocation.Neutral|Neutral` property is also set to **true**. - Changes their `PlayerState` to `InLobby` to remove the player's blaster and first-person UI visuals. For more information on the neutral spawn zone and its functionality for each round, see [Adding Rounds](/docs/en-us/tutorials/curriculums/gameplay-scripting/adding-rounds.md) in the next section of the tutorial. ```lua local function spawnPlayersInLobby(players: { Player }) for _, player in players do player.Neutral = true player:SetAttribute(PlayerAttribute.playerState, PlayerState.InLobby) task.spawn(function() player:LoadCharacter() end) end end ``` ## Connect new players Luau code in Studio is often event-driven, meaning that scripts listen for events from a Roblox service, then call a function in response. For example, when adding new players to a multiplayer experience, there must be an event that handles everything necessary for players to connect successfully. In the sample laser tag experience, this corresponding event is `Players.PlayerAdded:Connect`. `Players.PlayerAdded:Connect` is a part of multiple scripts in the experience. If you use the `Ctrl/Cmd+Shift+F` shortcut and search for `Players.PlayerAdded:Connect`, the results provide a good starting point for understanding the experience's initial setup. ![Studio's Find All window with the Players.PlayerAdded results highlighted.](../../../assets/tutorials/gameplay-scripting/Spawn-Respawn/Find-All.png) To demonstrate, open **ServerScriptService** ⟩ **SetupHumanoid**. The distinction between `Class.Player` and `Class.Player.Character|Character` is key to understanding this script: - A **player** is a connected client, and a **character** is a `Class.Humanoid` model. - Players need to choose a blaster and be added to the leaderboard. Characters need to spawn and receive a blaster. `SetupHumanoid` immediately checks if the player has a character (just joined) or doesn't (is respawning). After it finds one, it calls `onCharacterAdded()`, gets the `Class.Humanoid` model from the character, and passes it to **ServerScriptService** ⟩ **SetupHumanoid** ⟩ **setupHumanoidAsync** for customization. After setting these values, the script then waits for the character's health to reach zero. You will learn more about respawning later in this section of the tutorial. ```lua local function setupHumanoidAsync(player: Player, humanoid: Humanoid) humanoid.DisplayDistanceType = Enum.HumanoidDisplayDistanceType.Subject humanoid.NameDisplayDistance = 1000 humanoid.HealthDisplayDistance = 1000 humanoid.NameOcclusion = Enum.NameOcclusion.OccludeAll humanoid.HealthDisplayType = Enum.HumanoidHealthDisplayType.AlwaysOn humanoid.BreakJointsOnDeath = false humanoid.Died:Wait() onHumanoidDied(player, humanoid) end ``` The important note with this script is that the properties are completely optional, meaning that if you remove the first six lines of the function, the experience still works properly. Rather than being functional requirements, each property allows you to make design decisions that meet your gameplay goals. For example: - If you want character names to display at closer distances, reduce the value of `Class.Humanoid.NameDisplayDistance`. - If you only want a character's health to display if it's below 100%, set `Class.Humanoid.HealthDisplayType` to **DisplayWhenDamaged**. - If you want characters to break apart when their health reaches 0, set `Class.Humanoid.BreakJointsOnDeath` to **True**. If you change the values of these properties, it's important to playtest so that you can see the impact of your new settings. You can recreate what players experience in a [multi‑client simulation](/docs/en-us/studio/testing-modes.md#multi-client-simulation) by selecting at least two characters in a **Server & Clients** playtest from the mezzanine. ![Server & Clients option in the testing modes dropdown of Studio's mezzanine.](../../../assets/studio/general/Mezzanine-Testing-Mode-Server-Clients.png) > **Info:** Try changing some of these values and playtesting with multiple players to see how they affect the gameplay of the experience. For instance, how might player strategy change if you chose to hide the health display? Another example of the `Players.PlayerAdded:Connect` event is in **ServerScriptService** ⟩ **PlayerStateHandler**. Just like in the previous example, `PlayerStateHandler` immediately checks for a character. If the player isn't in the lobby, the script sets a player attribute to the `SelectingBlaster` state, the initial state for a round in which players can select from one of two different blaster types after spawning into the arena. This state also includes a force field that prevents players from taking damage while they're making their selection. ```lua local function onPlayerAdded(player: Player) player.CharacterAdded:Connect(function() if not player.Neutral then player:SetAttribute(PlayerAttribute.playerState, PlayerState.SelectingBlaster) onPlayerStateChanged(player, PlayerState.SelectingBlaster) end end) ``` One particular variable in `PlayerStateHandler` warrants discussion: `attributeChangedConnectionByPlayer`. This table stores all players and their `Datatype.RBXScriptConnection|Connections` to the `GetAttributeChangedSignal`. The reason for storing this connection in a table is so that `PlayerStateHandler` can **disconnect** it when the player leaves the experience. This process serves as a sort of memory management to prevent the number of connections from growing ever-larger over time. ```lua local attributeChangedConnectionByPlayer = {} local function onPlayerAdded(player: Player) -- Handle all future updates to player state attributeChangedConnectionByPlayer[player] = player :GetAttributeChangedSignal(PlayerAttribute.playerState) :Connect(function() local newPlayerState = player:GetAttribute(PlayerAttribute.playerState) onPlayerStateChanged(player, newPlayerState) end) end -- Disconnect from the attribute changed connection when the player leaves local function onPlayerRemoving(player: Player) if attributeChangedConnectionByPlayer[player] then attributeChangedConnectionByPlayer[player]:Disconnect() attributeChangedConnectionByPlayer[player] = nil end end ``` You can see that both connected functions in `onPlayerAdded()` call `onPlayerStateChanged()`. During initial setup after a player sorts into a team, `onPlayerAdded()` sets `PlayerState` to `SelectingBlaster`, so the first `if` statement evaluates to false and disables the `BlasterState`. In the later [Implement blasters](/docs/en-us/tutorials/curriculums/gameplay-scripting/implement-blasters.md) section of the tutorial, you will learn more details about this process. ```lua local function onPlayerStateChanged(player: Player, newPlayerState: string) -- Blaster state is 'Ready' only if player state is 'Playing' local newBlasterState = if newPlayerState == PlayerState.Playing then BlasterState.Ready else BlasterState.Disabled -- Schedule the destroy force field logic when the player begins playing if newPlayerState == PlayerState.Playing then scheduleDestroyForceField(player) end player:SetAttribute(PlayerAttribute.blasterStateServer, newBlasterState) end ``` If you add breakpoints or even just a `print()` statement, you can see that `onPlayerStateChanged()` gets called frequently throughout the experience: such as during initial setup of a round, to set itself on the main code path, after the player chooses a blaster, and when the player returns to the lobby, or the **Neutral** spawn location. Furthermore, after the player chooses a blaster, **ServerScriptService** ⟩ **BlasterSelectedHandler** sets the `PlayerState` to `Playing`, and `PlayerStateHandler` can finally remove the force field by calling `scheduleDestroyForceField()`. ## Customize force fields Instead of using a custom implementation, the sample laser tag experience uses Studio's built-in `Class.ForceField` class to prevent players from taking damage while they're selecting their blaster. This ensures that the only requirement for players to spawn with a force field is to include spawn locations with a `Class.SpawnLocation.Duration` property that is greater than 0. The sample uses an arbitrary value of 9,999 to enable force fields, then handles the actual duration programmatically in **ReplicatedStorage** ⟩ **ForceFieldClientVisuals**. Similar to `setupHumanoidAsync`, most of the lines in `ForceFieldClientVisuals` are optional. For example, if you comment out the contents of the function like the following script does, the experience uses the default sparkling force field instead of the hexagonal script in **StarterGui** ⟩ **ForceFieldGui**. ```lua local function onCharacterAddedAsync(character: Model) -- local forceField = character:WaitForChild("ForceField", 3) -- if not forceField then -- return -- end -- forceField.Visible = false -- localPlayer.PlayerGui:WaitForChild("ForceFieldGui").Enabled = true -- forceField.Destroying:Wait() -- localPlayer.PlayerGui.ForceFieldGui.Enabled = false end ``` Because the custom force field is a GUI rather than a new `Class.ParticleEmitter`, the `ForceFieldClientVisuals` script only affects the first-person visuals for each player, **not** third-person visuals when players look at other players. Third-person visuals retain the default Roblox appearance. For more information on modifying force fields, see `Class.ForceField.Visible`. ![First-person force field visuals include a futuristic hexagonal grid on the perimeter of the screen.](../../../assets/tutorials/gameplay-scripting/Spawn-Respawn/First-Person-Visuals.png)_First-person force field visuals_ ![Third-person force field visuals include a blue sparkling orb around the player spawning into the experience.](../../../assets/tutorials/gameplay-scripting/Spawn-Respawn/Third-Person-Visuals.png)_Third-person force field visuals_ Force fields are useful because they provide players enough time to between spawning and respawning without needing to worry about enemy players, but eventually they need to disappear for the main laser tag gameplay. The script that handles force field removal is in **ReplicatedStorage** ⟩ **scheduleDestroyForceField**, and it checks for three unique conditions: - After players select a blaster, force fields need to last long enough to allow players to acclimate to their surroundings. - During this acclimation time, force fields can't be an advantage, so they need to disappear the moment a player blasts their blaster. - Force fields need to disappear when players reset their characters either before blasting or before the force field times out. Each of these checks in the `scheduleDestroyForceField` script call `endForceField()` for these conditions. ```lua -- End force field if player blasts local blasterStateAttribute = getBlasterStateAttribute() attributeChangedConnection = player:GetAttributeChangedSignal(blasterStateAttribute):Connect(function() local currentBlasterState = player:GetAttribute(blasterStateAttribute) if currentBlasterState == BlasterState.Blasting then endForceField() end end) -- End force field if player resets characterRespawnedConnection = player.CharacterRemoving:Connect(endForceField) -- End force field after 8 seconds task.delay(MAX_FORCE_FIELD_TIME, endForceField) ``` `endForceField()` includes a seemingly odd `if` statement around the `forceFieldEnded` boolean. Because the checks run sequentially, the script can call the `endForceField()` function two or even three times. The `forceFieldEnded` boolean ensures that the function only tries to destroy a force field once. ```lua local function endForceField() if forceFieldEnded then return end forceFieldEnded = true attributeChangedConnection:Disconnect() characterRespawnedConnection:Disconnect() destroyForceField(player) end ``` > **Info:** To learn more about dealing with this type of situation, see [Debounce Patterns](/docs/en-us/scripting/debounce.md). ## Handle client state While most of this section focuses on **ServerScriptService** ⟩ **PlayerStateHandler**, there's another script of the same name in **ReplicatedStorage**. The reason for the split is the client-server architecture: - The client needs to understand player state information so that it can respond appropriately in real time, such as displaying the right user interface elements, or enabling players to move and blast. - The server needs all this same information so that it can prevent exploits. For example, the server also needs player state to perform actions like spawning and equipping characters, disabling force fields, and displaying a leaderboard. This is why this script is in **ReplicatedStorage** and not a purely client-side location. To see this core logic, review the following script in **ReplicatedStorage** ⟩ **PlayerStateHandler** that verifies the user's current state, then calls the appropriate function that handles the corresponding actions for that state. ```lua local function onPlayerStateChanged(newPlayerState: string) if newPlayerState == PlayerState.SelectingBlaster then onSelectingBlaster() elseif newPlayerState == PlayerState.Playing then onPlaying() elseif newPlayerState == PlayerState.TaggedOut then onTaggedOut() elseif newPlayerState == PlayerState.InLobby then onInLobby() else warn(\`Invalid player state ({newPlayerState})\`) end end ``` All event responses are logically grouped together in this script because they require similar behavior of enabling or disabling player controls, camera movement, and which UI layer is visible. For example, during blaster selection, players need to be both invulnerable and unable to move. The server already handles the force field, but the client handles movement. To demonstrate, if you check the logic for the `onSelectingBlaster()` function, you can see that the client disables player movement while they're selecting a blaster. ```lua local function onSelectingBlaster() togglePlayerCamera(true) togglePlayerMovement(false) setGuiExclusivelyEnabled(playerGui.PickABlasterGui) localPlayer:SetAttribute(PlayerAttribute.blasterStateClient, BlasterState.Disabled) end ``` The `onPlaying()` function is similarly straightforward. It enables movement, transitions to the main heads-up display (HUD), enables the blaster, and calls the same force field function as the server. ```lua local function onPlaying() togglePlayerMovement(true) setGuiExclusivelyEnabled(playerGui.HUDGui) localPlayer:SetAttribute(PlayerAttribute.blasterStateClient, BlasterState.Ready) scheduleDestroyForceField() end ``` ## Respawn characters The sample laser tag experience handles respawning character back into a round through the `onTaggedOut()` state in **ReplicatedStorage** ⟩ **PlayerStateHandler**. Like the `onSelectingBlaster()` and `onPlaying()` state, `onTaggedOut()` triggers unique behavior according to the changes to the `playerState` attribute. Specifically, it disables player movement, presents the respawn UI, and disables the blaster. ```lua local function onTaggedOut() -- Disable controls while tagged out togglePlayerMovement(false) togglePlayerCamera(false) setGuiExclusivelyEnabled(playerGui.OutStateGui) -- Disable blaster while tagged out localPlayer:SetAttribute(PlayerAttribute.blasterStateClient, BlasterState.Disabled) end ``` If you want to test this behavior, you can press `Esc`, navigate to the **Settings** tab, then click the **Reset Character** button. Notice that when you trigger the respawn screen, you cannot move, rotate the camera, or blast your blaster. ![Roblox's settings menu with the Reset Character button highlighted.](../../../assets/tutorials/gameplay-scripting/Spawn-Respawn/Reset-Character-Button.png)_Reset Character Button_ ![The respawn screen displays as a player respawns back into the round.](../../../assets/tutorials/gameplay-scripting/Spawn-Respawn/Respawn-Screen.png)_Respawn Screen_ It's important to note that this script doesn't actually respawn characters, it just stops them from acting and provides visual feedback to players that the **server** is respawning their characters. To demonstrate, if you examine **ServerScriptService** ⟩ **SetupHumanoid** ⟩ **setupHumanoidAsync** ⟩ **onHumanoidDied**, the script sets `PlayerState` to `TaggedOut` (essentially notifying **ReplicatedStorage** ⟩ **PlayerStateHandler**), and adds some visual indicators. The actual logic of respawning is a built-in Roblox behavior. When players respawn back into the round, they respawn at their team's spawn location according to the `Class.SpawnLocation.TeamColor` property. To customize the respawn time, you can add the following line to the top of `SetupHumanoid`. To learn more about this technique, see `Class.Players.RespawnTime`. ```lua local Players = game:GetService("Players") Players.RespawnTime = 10 -- new line, in seconds ``` > **Info:** In an experience with a progression system, such as checkpoints, you can use `Class.Player.RespawnLocation` to specify an exact location for players to respawn to after their health reaches zero. ## Miscellaneous setup As part of initial setup, the sample laser tag experience also performs some small, but critical steps: - The experience includes an empty script named **StarterPlayer** ⟩ **StarterCharacterScripts** ⟩ **Health** that disables the default Roblox health regeneration. For an explanation of this property's behavior, see `Class.Humanoid.Health`. - The experience uses a first-person camera by setting the `StarterPlayer.CameraMode.LockFirstPerson` property. Note that if you want to let users change between first- and third-person cameras, you must change the property programmatically rather than just setting it once in Studio, and modify the controls and UI to compensate for the change in perspective. - The experience uses the built-in Roblox leaderboard with the unit of "points", which players earn each time they tag another player out. You can see the configuration in **ServerScriptService** ⟩ **SetupLeaderboard**, but [In-Experience Leaderboards](/docs/en-us/players/leaderboards.md) offers a full overview. Note that `onPlayerTagged` adds points to the leaderboard, which you'll learn about in [Add rounds](/docs/en-us/tutorials/curriculums/gameplay-scripting/add-rounds.md) and [Detect hits](/docs/en-us/tutorials/curriculums/gameplay-scripting/detect-hits.md). Now that players can spawn, choose a blaster, and aim it from a first-person point of view, the next section teaches you about the scripts behind creating round-based gameplay. --- title: "Create Roblox account" url: /docs/en-us/tutorials/curriculums/studio/create-account last_updated: 2026-06-29T19:34:13Z description: "Explains how to create a Roblox account." --- # Create Roblox account Welcome to creating on Roblox! Whether you came to this tutorial as a player, a developer from another engine, or as someone who is brand new to development, the platform has a spot for you to create anything that you can imagine. Before you start learning about the Roblox engine, it's important to first create a Roblox account. This process is free and it allows you to access the Roblox app to play experiences, as well as Roblox Studio to build experiences. To create a Roblox account: 1. Go to Roblox's [home page](https://www.roblox.com/CreateAccount). Because you don't have an account yet, the sign up screen displays. 2. Follow each prompt, then click the **Sign Up** button. After a moment, Roblox logs you in and the home page displays. With your new account, remember these safety tips: - **Never share your password** — This tip applies to even real life friends. - **Make your password hard to guess** — If your username is "bloxcool", your password should not be "bloxcool123". - **Roblox employees will never ask for your password** — Report anyone who asks using the [Report Abuse](https://en.help.roblox.com/hc/articles/203312410) feature. - **There's no such thing as free Robux** — Never trust players or sites who say they have a way to get free Robux! > **Info:** For more account security information, see [Keep Your Account Safe](https://en.help.roblox.com/hc/en-us/articles/203313380-Keep-Your-Account-Safe). Now that you have an official Roblox account, it's time to install Roblox Studio! --- title: "Explore Studio UI" url: /docs/en-us/tutorials/curriculums/studio/explore-ui last_updated: 2026-06-29T19:34:14Z description: "Explains how to navigate Studio's fundamental UI." --- # Explore Studio UI Welcome to your first session in Roblox Studio! Before you jump in and begin building experiences, let's take a moment to walk through Studio's essential UI. While this overview provides a solid foundation to get started, there are many additional windows and tools that you can discover and customize in the [Studio interface](/docs/en-us/studio/ui-overview.md). > **Info:****Coming to Studio from another game engine?** Check out one of the following guides that explains the key differences between Studio and Unity or Unreal: [Roblox for Unity developers](/docs/en-us/unity.md) or [Roblox for Unreal developers](/docs/en-us/unreal.md). ## Mezzanine ![Mezzanine indicated at the top of Studio's window.](../../../assets/tutorials/studio-lesson/Mezzanine.jpg) The **mezzanine** is the top-most section of Studio and it includes: - [Playtest options](/docs/en-us/studio/testing-modes.md) that allow you to simulate what players see on their devices. - Tabs like **Model**, **Avatar**, and **Script** that filter the [toolbar](#toolbar) tools for their respective tasks. - [Collaborator](/docs/en-us/projects/collaboration.md) information for experiences that you work on with others. - The [Assistant](/docs/en-us/assistant/guide.md) tool that you can use as an aid to supplement your skills and assist with development tasks. ## Toolbar ![Toolbar indicated below the mezzanine bar near the top of Studio's window.](../../../assets/tutorials/studio-lesson/Toolbar.jpg) The **toolbar** is the section right below the mezzanine that displays all tools and insertable objects that relate to your active tab: - **Home** contains the core [transform](/docs/en-us/parts.md#transform-parts) tools, [part inserter](/docs/en-us/parts.md#insert-parts), the [color](/docs/en-us/parts.md#color) and [material](/docs/en-us/parts.md#material) widgets, the [group](/docs/en-us/parts.md#group) and [lock](/docs/en-us/parts.md#lock) tools, and [anchor](/docs/en-us/parts.md#anchor) toggle. Also contains the [Terrain Editor](/docs/en-us/studio/terrain-editor.md). - **Model** contains the core [transform](/docs/en-us/parts.md#transform-parts) tools, the [pivot](/docs/en-us/studio/pivot-tools.md) and [align](/docs/en-us/studio/align-tool.md) tools, insertion widgets for [effects](/docs/en-us/effects.md) and [constraints](/docs/en-us/physics.md#constraints), and [solid‑modeling](/docs/en-us/parts/solid-modeling.md) tools. - **Avatar** contains the core [transform](/docs/en-us/parts.md#transform-parts) tools, as well as specialized tools for building default rigs, configuring [avatars](/docs/en-us/avatar-setup.md), working with [animations](/docs/en-us/animation.md), and [creating/fitting accessories](/docs/en-us/avatar/accessory-fitting-tool.md). - **UI** contains insertion widgets for [UI objects](/docs/en-us/ui.md#ui-objects) and lets you access the [Style Editor](/docs/en-us/ui/styling/editor.md), a comprehensive tool that allows you to create, manage, and apply UI styles. - **Script** contains tools for writing and testing scripts, including [debugging](/docs/en-us/studio/debugging.md) tools. - **Plugins** contains [plugins](/docs/en-us/studio/plugins.md) created by the community or plugins you've created yourself to use across your experiences. When you are more comfortable with Studio, you can also create [custom tabs](/docs/en-us/studio/ui-overview.md#custom-tabs) for your own development needs. ## 3D viewport ![3D viewport indicated in center of Studio's window.](../../../assets/tutorials/studio-lesson/3D-Viewport.jpg) The **3D viewport** is the largest window in Studio that provides you a view into the 3D space. After you click in the 3D viewport, you can move the camera, edit objects with your mouse, and playtest the experience from your players' point-of-view. For common camera controls, see the following table. | Keys/Shortcuts | Action | | --- | --- | | `W` `A` `S` `D` | Moves the camera forward/left/back/right. | | `Q` `E` | Moves the camera down/up. | | `Shift` | In combination with any movement key, changes the camera speed. | | `F` | Focuses the camera on a selected part. | | **Right Mouse Button** | When pressed, dragging the mouse moves the camera view around. | | **Mouse Scroll Wheel**
`Ctrl``=` or `⌘``=`
`Ctrl``-` or `⌘``-` or `O` | Zooms the camera in or out.
Zooms the camera in.
Zooms the camera out. | | `,` / `.` | Rotates the camera left/right. If a part/model is in focus via the `F` shortcut, rotates the camera counterclockwise/clockwise around that focused object. | | **Middle Mouse Button** | When pressed, dragging the mouse pans the camera. | | **Right Mouse Button** & **Mouse Scroll Wheel** | Pressing the right mouse button and scrolling the mouse wheel **up** increases the camera scroll speed. Conversely, pressing the right mouse button and scrolling the mouse wheel **down** reduces the camera scroll speed. > **Success:** This feature is currently in beta. Enable it through **File** ⟩ **Beta Features** ⟩ **New Studio Camera Controls**. | > **Info:** It's **strongly** recommended to use a 2-button mouse with a scroll wheel to make navigating the 3D space easier. ## Toolbox ![Toolbox shown on the left side of Studio's window.](../../../assets/tutorials/studio-lesson/Toolbox.jpg) The [Toolbox](/docs/en-us/projects/assets/toolbox.md) contains a selection of models, images, meshes, audio, plugins, videos, and fonts from either Roblox or fellow creators on the platform. It offers four tabs: - **Creator Store** is a marketplace of both free and for sale assets to use within your experiences. - **Inventory** contains assets that you or your groups have uploaded, or those that you found on the Creator Store. - **Recent** contains your most recently used assets. - **Creations** is similar to **Inventory**, but it excludes assets from the Creator Store. ## Explorer ![Explorer window shown on the right side of Studio's window.](../../../assets/tutorials/studio-lesson/Explorer.jpg) The [Explorer](/docs/en-us/studio/explorer.md) window displays a hierarchical list of every object and service that runs out-of-the-box gameplay logic for the active **place** of your experience. You can think of places like levels inside of a game. > **Info:** Places are comparable to scenes in Unity or maps in Unreal Engine. Experiences can have one or multiple places that each contain all components for that portion of the experience, including its specific environment, 3D objects, and scripts; these components collectively are called the **data model**. When you switch to a different place, the **Explorer** window updates accordingly to show that place's data model. For more information on how the **Explorer** window organizes objects in the data model, see [Data model](/docs/en-us/projects/data-model.md). ## Properties ![Properties window shown on the right side of Studio's window.](../../../assets/tutorials/studio-lesson/Properties.jpg) The [Properties](/docs/en-us/studio/properties.md) window displays a selection of properties for a selected object that you can customize to change how the object looks and behaves in the 3D environment. Every time you select a different object in the 3D viewport or **Explorer** window, the **Properties** window updates accordingly to show that object's properties. Now that you're familiar with Studio's core UI and what they're responsible for, you can continue learning about how to create with Studio.
--- title: "Learn about Studio" url: /docs/en-us/tutorials/curriculums/studio last_updated: 2026-06-29T19:34:13Z description: "Learn the basics of creating an account, downloading Studio, and navigating its UI." --- # Learn about Studio This tutorial acts as an introduction to Roblox Studio, a free application on Windows or Mac that has everything you need to start creating experiences on the Roblox platform! As you follow each chapter, you will learn how to login into Studio, then navigate its core user interface windows. This lesson is intended for readers who are **brand new** to working in Studio. If you are already familiar with Studio, try another beginner lesson plan or jump straight to [building your first experience](/docs/en-us/tutorials/core.md). ## Course contents **#### Chapter 1 - Create Roblox account** Learn how to [create a free account](/docs/en-us/tutorials/curriculums/create-account.md) on Roblox so that you can login to Studio. **#### Chapter 2 - Install Roblox Studio** Learn how to [install Studio](/docs/en-us/tutorials/curriculums/install-studio.md) on your computer. **#### Chapter 3 - Explore Studio UI** Learn about the [fundamental windows](/docs/en-us/tutorials/curriculums/explore-ui.md) you need to know to navigate Studio. **#### Chapter 4 - Next steps** Learn where you can [find more tutorials](/docs/en-us/tutorials/curriculums/next-steps.md) about creating experiences. --- title: "Install Roblox Studio" url: /docs/en-us/tutorials/curriculums/studio/install-studio last_updated: 2026-06-29T19:34:13Z description: "Explains how to install Studio on your Windows or Mac computer." --- # Install Roblox Studio **Roblox Studio**, or **Studio** for short, is Roblox's free application with powerful 3D creation tools you can use to build and publish experiences to hundreds of millions of Roblox users on consoles, desktops, and mobile devices. It's available on Windows and Mac computers that meet the following system requirements: | | **MINIMUM** | **RECOMMENDED** | | --- | --- | --- | | **OS Version** | Windows 10
macOS 10.14 | Windows 11
macOS 14+ | | **Memory (RAM)** | 3 GB | 8 GB | | **Resolution** | | 1600×900 or higher | Studio comes bundled with the Roblox engine that also powers experiences on the Roblox app. This means that when you develop and publish your experiences in Studio, players interact with the same engine from your development on their personal devices. > **Info:** For an overview on Studio's functionality and the Roblox engine, see [Roblox Studio](/docs/en-us/studio.md). To install Roblox Studio: 1. Click the following **Download Studio** button. 2. In the pop-up dialog, click the **Download Studio** button. 3. Find the Studio installer in your browser's download history and double-click the file. - On a Windows computer, the file is called `RobloxStudio.exe`. - On a Mac computer, the file is called `RobloxStudio.dmg`. 4. After Studio finishes installing, a pop-up confirmation displays. Click the **Launch Studio** button. 5. After Studio opens, log in with your new Roblox account. Now that you have Studio open, let's explore the fundamental UI you need to know to navigate Studio.
--- title: "Next steps" url: /docs/en-us/tutorials/curriculums/studio/next-steps last_updated: 2026-06-29T19:34:13Z description: "Explains ways to continue learning about creating in Studio." --- # Next steps Congratulations on completing your introduction to Roblox Studio! Now that you have Studio on your computer, login credentials, and familiarity with Studio's core user interface, you are ready to start building anything you can imagine. ## New to development? If you are brand new to development, check out the following beginner tutorials on different development areas that you can specialize in: - [Building lesson](/docs/en-us/tutorials/curriculums/building.md) — Learn how to **build a simple platformer** using one of Studio's templates. - [Coding lesson](/docs/en-us/tutorials/curriculums/coding.md) — Learn how to **code an interactive story** where players replace placeholder words with their own answers. - [VFX lesson](/docs/en-us/tutorials/curriculums/artist.md) — Learn how to **customize special effects art** for a pre-made carnival ride experience. - [Animation lesson](/docs/en-us/tutorials/curriculums/animator.md) — Learn how to **create keyframes for various poses**, then loop them into a swimming animation that you can reuse in any experience. After you complete each of these tutorials, you will be ready to [create your first experience](/docs/en-us/tutorials/curriculums/core.md) and learn about [coding fundamentals](/docs/en-us/tutorials/fundamentals/coding-1/landing.md) for Luau, Studio's programming language. > **Info:** Game development is a massive field with a lot of different specializations. If one development area doesn't interest you, try another! ## Just new to Studio? If you aren't new to development, but are new to Studio, check out the following guides for an overview of how experiences technically work: - [Projects](/docs/en-us/projects.md) — Learn how to create and manage projects in Studio. - [Assets](/docs/en-us/projects/assets.md) — Learn about how everything in Roblox is represented as a cloud-based asset. - [Data model](/docs/en-us/projects/data-model.md) — Learn how places look and function in your experiences. - [Client-server runtime](/docs/en-us/projects/client-server.md) — Learn how experiences run in the Cloud. After you have a solid understanding of these guides, you will be ready to examine the [Plant](/docs/en-us/resources/plant-reference-project.md) reference project that focuses on common use cases that you might encounter when you develop an experience on Roblox, including tradeoffs, compromises, and the rationale of various implementation choices so that you can make the best decisions for your own experiences. As you start to build your own experiences, be sure to check out the intermediate tutorials for common use cases, such as: - [Create HUD meters](/docs/en-us/tutorials/use-case-tutorials/ui/create-hud-meters.md) - [Add 3D audio](/docs/en-us/tutorials/use-case-tutorials/audio/add-3D-audio.md) - [Save player data](/docs/en-us/tutorials/use-case-tutorials/data-storage/save-player-data.md) When you reach a point where you feel comfortable developing in Studio, you will be ready for the advanced paths that offer a deep dive into industry-standard workflows for areas like [environmental art](/docs/en-us/tutorials/curriculums/environmental-art.md), [gameplay scripting](/docs/en-us/tutorials/curriculums/gameplay-scripting.md), and [UI design](/docs/en-us/tutorials/curriculums/user-interface-design.md). --- title: "Choose an art style" url: /docs/en-us/tutorials/curriculums/user-interface-design/choose-an-art-style last_updated: 2026-06-29T19:34:13Z description: "Explains how to greybox the laser tag environment using basic parts." --- # Choose an art style **Choosing an art style** is the process of planning an aesthetic direction for your UI elements. It's important to begin your design process with an art style because art styles provide visual ground rules you can reference to ensure your UI intuitively provides players information about what they can do in your experience. Using the [sample laser tag experience](https://www.roblox.com/games/14817965191/Laser-Tag-1A) `.rbxl` file as a reference, this section of the user interface curriculum shows you how to make meaningful decisions on how to design your UI elements according to the genre of your experience, including guidance on: - Identifying necessary UI elements according to what you want to communicate to players throughout your experience. - Selecting a color theme that reinforces genre-specific color conventions that are intuitive for your audience. - Outlining simple icons that are easy to decipher on any screen size. - Establishing a button order that matches the likelihood of different player workflows. - Determining a text system that ensures legibility on target devices. After you complete this section, you will learn how to design the structure and flow of the information you want to communicate to players through UI within your experience. ## Identify your UI elements The first step in choosing an art style for your UI is to identify what UI elements you need for the varying types of information you want to communicate to your audience. Doing this work at the start of your design process is crucial because it allows you to categorize UI elements by their functional purpose, make semantic decisions according to where and when players are going to interact with each UI element, and plan where you can reuse UI elements across your experience. There are many different ways to brainstorm which UI elements are necessary for your gameplay requirements, but it's recommended to start with envisioning what a player needs to know as soon as they join your experience. For example, when a player opens the sample laser tag experience, they may ask themselves the following questions: - What is the goal of the experience? - How do I know who is on my team? - How do I keep track of my team's points? - How do I select a blaster? - How do I know where the blaster shoots a laser? - If I'm on a mobile device, how do I shoot my blaster? - After I shoot a laser, when can I shoot again? - How do I know when I am starting the round? - How do I know when I successfully tag someone with my blaster? - How do I know when the enemy team successfully tags me? By using these questions to understand what information is imperative for players to know to be successful, you can sort the sample laser tag experience's UI needs into three categories: 1. Information about the experience's objective. 2. Information about the blaster. 3. Information about the state of the player. Sorting your experience's UI needs into categories is helpful because you can formulate your art style around each grouping to reinforce what information each category needs to teach players. For example, if you want your UI to tell players what actions they can take regarding their character's health status, you might choose an art style with UI elements that prioritize the color green and/or plus icons so players can quickly recognize their function. After you sort your experience's UI needs into categories, you can create a list of the UI elements necessary to meet the requirements of each category. To demonstrate, the sample laser tag experience uses the following table of UI elements to address the previous list of potential player questions. As you work through this tutorial, the UI curriculum will continue to refer to this list and highlight major design decisions that relate to each category of UI elements. | Category | UI Elements | | --- | --- | | Information about the experience's objective | | | Information about the blaster | | | Information about the state of the player | | Now that you have a list of UI elements for your experience, it's time to begin making stylistic and semantic choices for each grouping of UI elements, starting with a color theme. ## Select a color theme A **color theme**, or color palette, is a selection of colors that each communicate a message through consistent application within your experience, such as using a bright color to indicate when something is selectable. Applying a color theme to your UI elements is important, especially when you rely on color conventions within the genre of your experience, because it allows players to quickly understand your UI with minimal effort. In the [Environmental Art Curriculum](/docs/en-us/tutorials/curriculums/environmental-art.md), each half of the laser tag environment is color-coded from a top-down view to differentiate which area of the map is near each team's respective spawn zone: **mint** for the team that assembles on the left side of the map, and **carnation pink** for the team that assembles on the right side of the map. These specific colors are useful because they are **complementary**, meaning that they contrast each other well, and enable players to easily scan their surroundings and orient themselves regardless of what direction they're facing in the building. ![The door on the mint green side of the map.](../../../assets/tutorials/user-interface-design/Section1/GreenLeft.jpg)_Pastel Blue-green_ ![The door on the carnation pink side of the map.](../../../assets/tutorials/user-interface-design/Section1/PinkRight.jpg)_Carnation Pink_ The sample laser tag experience uses this same color theme in its UI to highlight information that pertains to each team, such as each player's team indicator within the 3D space, or the team point tracker that overlays the screen. This consistency assists players in being able to quickly understand information about the experience's objective during the fast-paced gameplay of a first-person shooter experience, especially as players need to make rapid decisions while traversing the environment. _A preview of the team point tracker in the sample laser tag experience._ When selecting a color theme for your own experience, consider the following: - The strength of a color theme relies on helping players make quick mental associations between each color and their function. For this reason, **limit your color theme** to only highlight the key information you want players to associate with your UI elements. - To ensure players with colorblindness can understand the message of your color theme, **don't always rely on color alone** to distinguish between UI elements. Instead, combine colors with icons, shapes, and/or animations to ensure your UI communicates effectively with every player. - It's more important for your UI to be legible than aesthetically pleasing. For this reason, **prioritize simple UI** with color that remains readable over both light and dark elements in the 3D world. To highlight the guidance in the last point, the sample laser tag experience utilizes the neutral colors of black and white for almost every other UI element that overlays the 2D screen. Black and white contrast each other well, and they are easy to read as the rest of the screen displays an otherwise colorful 3D environment. ## Outline simple icons An **icon** is a symbol that represents an action, object, or concept in an experience. Outlining icons that are simple and intuitive is important because the end result enables players to easily recognize what they are able to do and what you want to tell them through your UI without using text, which can clutter the screen and pull attention away from content that matters. This process is even more crucial if your audience accesses your experience using a small screen on mobile devices. Simple icons ideally have a distinct style from your 3D elements while still complimenting the overall world of your experience. For example, in the final environment of the [Environmental Art Curriculum](/docs/en-us/tutorials/curriculums/environmental-art.md), both modular and prop 3D assets have a clean, high-tech art style that utilizes rectangular forms with soft, rounded corners. From the beveled panels along the floor to the near circular windows in the ceiling, nothing includes a sharp edge. To complement this art style while still remaining unique, all UI elements that you will learn to make later in this tutorial include a futuristic aesthetic and round angles without matching the shape language of the 3D assets in the environment. This keeps each icon's meaning distinct from other information in both the 2D and 3D space. To demonstrate this concept, see the following two images from the sample laser tag experience of the crosshair that tells players where their blaster shoots on the screen, and the button that allows players to shoot their blaster on mobile devices. Both icons include soft angles to be cohesive with the overall world, but their hexagonal and circular shapes set them apart from anything a player could associate the icons with in the experience. When outlining simple icons for your own experience, consider the following: - Simple icons are legible even when they are small. For this reason, **limit details on your icons** that would become unrecognizable on mobile device screens. - Icons are powerful because they can communicate a message no matter the player's language. As long as it doesn't impact player comprehension of your UI, **replace unnecessary text with icons** to improve localization efforts. - Many experiences of the same genre use icons that are stylistically similar, such as a sword icon that represents strength, or a beaker that represents magic. **Embrace the symbolism within your experience's genre** so players can understand your icons without additional guidance. If you don't know what types of icons are common within your experience's genre, check out the [Game UI Database](https://gameuidatabase.com/). This free resource tool for UI designers includes screenshots from hundreds of experiences of different genres that you can reference during your design process. ## Establish an interaction order An **interaction order** is the sequence of interactions players can have with your UI. As there are often multiple interactable UI elements on the screen, it's important to establish an intuitive interaction order to assist players in making decisions as they navigate various workflows. There are typically three types of interactions in a workflow: - **Primary Interaction** – The action a player is most likely to perform. - **Secondary Interaction** – The action a player is likely to perform as an alternative to the primary action. - **Tertiary Interaction** – The action a player is least likely to perform. Each interaction type must have a different level of visual emphasis depending on the likelihood of a player performing the action. To illustrate this concept, examine the following image of the interaction order for the workflow to select a blaster in the sample laser tag experience, in which A represents the primary interaction, B represents the secondary interaction, and C represents the tertiary interaction. In this workflow, the action a player is most likely to perform is to select between the two different types of blasters, so the blaster buttons are much larger than any other interactable element in the design. This level of visual emphasis grabs the player's attention, and draws their eye to the middle of the overall UI element. After the player makes their decision, the logical next step in the workflow is to confirm their selection and start the round. For this reason, the SELECT button is directly below the primary interaction. While unlikely, the player may not know they are able to select one of the blaster buttons to communicate which blaster they want to use. To assist in this situation, there are two arrow tertiary buttons the player can utilize to cycle between their choices. These buttons are subtle and much smaller than the elements of the primary and secondary interactions, but they are also perceivable to the player that needs direction on what actions they are able to perform. If you were to place these primary, secondary, and tertiary interactions in a different interaction order, such as swapping the SELECT button with the left arrow button, players wouldn't have clear direction on the sequence of choices they need to make. For this reason, in addition to the visual emphasis of a button order, effective workflows follow a **visual hierarchy** that guides players through your ideal order of decisions according to the direction they are likely to scan information, such as top-down and left-to-right. _The success of this design is that it's natural for players familiar with both left-to-right and right-to-left languages!_ When establishing an interaction order for the workflows in your own experience, consider the following: - Players need to have a clear understanding of when they can interact with your UI to perform actions. For this reason, it's recommended to **provide at least one form of visual feedback** for interactable UI elements, such as displaying an outline or changing a button's size, color, or animation when it's in focus. - If labels on interactable UI elements are vague or similar to one another in the same workflow, players can misunderstand how to complete an action or process. To avoid a negative player experience, **create labels that are clear, concise, and distinct from one another**. - If interactive UI elements are too large, they can distract from other important information on the screen. Conversely, if they are too small, they can be hard to read or difficult to select, especially if they're in close proximity on mobile devices. For this reason, it's essential to **review the size of your interactable UI elements on various screen sizes**. In [Implement in Studio](/docs/en-us/tutorials/curriculums/user-interface-design/implement-designs-in-studio.md), you will learn how to use `Class.UIAspectRatioConstraint` objects to ensure UI elements maintain a specific aspect ratio no matter what device players use to access your experience. In addition to making your design process easier, this technique can also help you meet the Web Content Accessibility Guidelines' [Touch Target Size and Spacing](https://w3c.github.io/Mobile-A11y-TF-Note/#targetSize) recommendation to create a touch zone for interactive UI elements that's at least 9x9 mm on mobile devices. ## Determine a text system A **text system** is a set of rules about fonts and style for all of the words in your UI, such as "always bold headers" or "use green font when referencing a health stat." Determining a text system early into your design process allows you to have a structure that you can consistently apply throughout your experience so players know what to expect as they search for the information they need. While text systems can vary depending on the experience's genre or 3D world requirements, the most important rule that all text systems must follow is to **ensure all of your UI text is clear and easy to read**. By using this rule as a basis for all decisions related to your text system, you can improve the accessibility and user experience for players reading your UI by considering the different ways players may interact with your text, such as: - The device players may use to access your experience. - The language in which players may read your localized text. - The possible underlying background behind on-screen text. For example, the following screen response design of when a player is tagged out scales to a smaller or larger font size according to a player's device, includes enough room on either side of the english text for languages with longer translations, and includes a contrasting background so players can read the text no matter what color is in their background. When determining a text system for your own experience, consider the following: - Text is difficult to read when it blends in with the noise of its background. To improve the legibility of your UI, **display text on top of a contrasting color or with a stroke**. - If you don't scale your text for different devices, the text will either take up too much room or become small and indecipherable on the player's screen. To catch discrepancies in your text, **test your design on multiple devices** throughout the design process. - Words can extend beyond your original design when you localize them into other languages. To improve the composition of your design, **reference the most space your text can take up on the screen**. - While some fonts can fit the aesthetics of your experience, they may be difficult to read in large quantities. For this reason, **use stylized text sparingly**, such as for titles or alert text. Once you have a plan for the art style of your UI, you can move on to the next section of the tutorial to learn how to wireframe the layout of each element in various player workflows. --- title: "Implement designs in Studio" url: /docs/en-us/tutorials/curriculums/user-interface-design/implement-designs-in-studio last_updated: 2026-06-29T19:34:13Z description: "Explains how to greybox the laser tag environment using basic parts." --- # Implement designs in Studio **Implementing your designs** is the process of creating your wireframes in Studio using both built-in and custom UI elements with scripts that trigger your UI contextually. This exciting step of the tutorial is where you get to see all of your designs and hard work come together into a cohesive set of workflows that are complete and ready for player interaction. Using the [sample laser tag experience](https://www.roblox.com/games/14817965191/Laser-Tag-1A) `.rbxl` file as a reference, this section of the user interface design curriculum shows you how to bring your UI planning to life, including guidance on: - Retrieving asset IDs from the UI asset library so that you can recreate the sample laser tag experience's UI components. - Emulating various devices directly in Studio to see how your UI displays on different screens and aspect ratios. - Creating `Class.ScreenGui`, `Class.SurfaceGui`, and `Class.BillboardGui` objects to display your UI on players' screens, part surfaces, and within the 3D space, respectively. After you review the techniques in this section, you can apply them to your own projects to make exciting UI components that help players navigate what they can do within your experiences. > **Info:** The instructions in this section of the tutorial show you how to exactly recreate the UI components using the UI Design Asset Library. This process takes about 90 minutes or less from start to finish. If you don't want to use the provided values, you can adjust each UI element to meet the specifications of your own experience, or use the sample itself for the rest of the tutorial. ## Get asset library Asset libraries are collections of assets you can add into your inventory for easy access and reuse. The asset library you will use for your project from the [Creator Store](/docs/en-us/production/creator-store.md) includes nine 2D individual UI element assets, and the final versions of the objective, blaster selector, and player info components you are creating in this section of the tutorial. #### Individual UI Elements _**MultiBlaster Icon**rbxassetid://14309094777_ _**SingleBlaster Icon**rbxassetid://14309094641_ _**Pink Team Icon**rbxassetid://14309678581_ _**Trapezoid**rbxassetid://14304828203_ _**Upside Down Trapezoid**rbxassetid://14304827304_ _**Green Team Icon**rbxassetid://14309678701_ _**Fade**rbxassetid://14304826876_ _**Multi-directional Fade**rbxassetid://14304827147_ _**Blast Button Icon - Default**rbxassetid://18308375067_ _**Blast Button Icon - Pressed**rbxassetid://18308372597_ _**Crosshair Icon**rbxassetid://14400935532_ _**Hit Marker Icon**rbxassetid://14401148777_ _**Hexagon**rbxassetid://14462567943_ _**Selection Arrow Icon**rbxassetid://14309187282_ _**Border Fade**rbxassetid://14309518632_ To get the asset library from your inventory into your experience: 1. Add the asset library to your inventory. 1. Navigate to the asset library's [details page](https://create.roblox.com/store/asset/102445640805652) on the Creator Store. 2. In the top-right corner, click the **Get Model** button. The asset library is now in your inventory, and you can reuse these assets in any project on the platform. 2. In Studio, navigate to the **Home** tab, then click the **Toolbox** button. The **Toolbox** window opens.![Toolbox highlighted in Studio's toolbar.](../../../assets/studio/general/Toolbar-Toolbox.png) 3. In the **Toolbox** window, click the **Inventory** tab. The **My Models** sort displays. 4. Click the dropdown menu, then select the **My Packages** sort. 5. Click the **Final Screen UI Components** tile, then in the **Explorer** window, select **Completed Components**, then drag them into the **StarterGui** service. You can now enable any of the final components to reference their design. ## Emulate devices Studio's [Device Emulator](/docs/en-us/studio/testing-modes.md#device-emulation) allows you to test how players will see and interact with your UI on various devices. This tool is a vital part of the implementation process because the aspect ratio of your viewport in Studio doesn't necessarily reflect the aspect ratio of the screens players use to access your experience, and it's important that your UI is both legible and accessible on every device. For example, if you don't test your UI on a range of screen sizes, players with large screens may not be able to read your text or decipher your icons, and players with small screens may not be able to see the 3D space because your UI elements take up too much room on the display. To emulate your UI on various screen sizes: 1. From Studio's **Test** menu, toggle on **Device Emulator**. 2. In the resolution dropdown, select **Actual Resolution**. This allows you to see the true resolution of your UI elements on the device you're emulating.![Device Emulator settings options indicated at top of viewport window.](../../../assets/tutorials/user-interface-design/Section3/Device-Emulator-Setup.png) 3. In the device dropdown, select at least one device within the **Phone**, **Tablet**, **Desktop**, and **Console** sections. ## Create ScreenGui objects To display UI elements on every player's screen, you can create a `Class.ScreenGui` object in the `Class.StarterGui` service. `Class.ScreenGui` objects are the primary containers for on-screen UI, and the `Class.StarterGui` service copies its contents to each player's `Class.PlayerGui` container as they enter an experience. You can create multiple `Class.ScreenGui` objects to organize and display groupings of UI elements contextually throughout gameplay. For example, the sample laser tag experience includes five separate `Class.ScreenGui` objects that are initially disabled until players meet different conditions during the main [user flow](/docs/en-us/tutorials/curriculums/user-interface-design/wireframe-your-layouts.md#develop-user-flows) of the experience: - **HUDGui** - Displays key information about the experience's gameplay when players are active in a round, such as the objective and each team's total points. - **PickABlasterGui** - Displays all blaster choices when players start or rejoin a round. - **ForceFieldGui** - Displays a hexagonal grid when players are selecting a blaster and while they are temporarily invincible. - **OutStateGui** - Displays a dark border around the screen when players are tagged out. - **RoundResultsGui** - Displays a dark overlay on top of the screen with information on which team won the round. After you create a `Class.ScreenGui` object, you can create and customize its child `Class.GuiObject|GuiObjects` according to each container's purpose. To demonstrate, in the immediate sections that follow, you will learn how to implement UI elements for the [three categories of information](/docs/en-us/tutorials/curriculums/user-interface-design/choose-an-art-style.md#identify-your-ui-elements) players need to know to be successful in the sample laser tag experience. **You can adjust any part of the process to meet the specifications of your own experience**. To create a `Class.ScreenGui` object: 1. In the **Explorer** window, hover over the **StarterGui** service, then click the **⊕** icon. A contextual menu displays. 2. Insert a **ScreenGui**. 3. Rename the **ScreenGui** according to the context of its child UI elements. 4. Repeat this process for each grouping of UI elements you need to display on every player's screen. ### Objective UI Following the visual hierarchy best practices from [Wireframe Your Layouts](/docs/en-us/tutorials/curriculums/user-interface-design/wireframe-your-layouts.md), this section teaches you how to implement all on-screen UI elements relating to the experience's objective. This grouping of UI elements is near the top of the screen because the objective and each team's points have the most significance on how to win the game. For example, the sample provides an objective UI component that players reference to know what they need to do to be successful in a round. As players tag out enemy team members and earn points, this component keeps track of each team's score against the overall goal within the header's prompt. For a high-level review of all of the client and server scripts that work together to track points, see [Track points](/docs/en-us/tutorials/curriculums/gameplay-scripting/add-rounds.md#track-points) in the Gameplay Scripting Curriculum. To exactly recreate the objective UI within the sample [Laser Tag](https://www.roblox.com/games/14817965191/Laser-Tag-1A) experience: 1. Create a container for the entire component. 1. Insert a **Frame** into the **HUDGui** `Class.ScreenGui` object. 1. In the **Explorer** window, navigate to the **StarterGui** service. 2. Hover over its child **HUDGui** object, then click the ⊕ icon. A contextual menu displays. 3. From the contextual menu, insert a **Frame**. 2. Select the new **Frame**, then in the **Properties** window, 1. Set **AnchorPoint** to `0.5, 0` to set the frame's origin point in the top-middle of itself (50% from the left to the right of the frame, and 0% from the top to the bottom of the frame). 2. Set **BackgroundTransparency** to `1` to make the frame's background completely transparent. 3. Set **Position** to `{0.5, 0},{0.03, 0}` to set the frame near the top-middle of the screen (50% from the left to the right of the screen, and 3% from the top to the bottom of the screen so there is a little buffer). 4. Set **Size** to `{0.5, 0},{0.13, 0}` so the frame's elements take up a large portion of the top of the screen to grab players' attention (50% horizontally, and 13% vertically). 5. Set **Name** to **Objective**. 3. **(Optional)** Insert a **UIAspectRatioConstraint** into **Objective** to ensure the label's aspect ratio remains the same no matter the player's screen size. The sample sets its `Class.UIAspectRatioConstraint.AspectRatio` property to `7`. 2. Create a container for the objective's prompt objects. 1. Insert a **Frame** into **Objective**. 2. Select the new **Frame**, then in the **Properties** window, 1. Set **AnchorPoint** to `0.5, 0` to set the frame's origin point in the top-middle of itself (50% from the left to the right of the frame, and 0% from the top to the bottom of the frame). 2. Set **BackgroundTransparency** to `1` to make the frame's background completely transparent. 3. Set **Position** to `{0.5, 0},{0, 0}` to set the frame in the middle of the container (50% from the left to the right of the parent frame, and 0% from the top to the bottom of the parent frame). 4. Set **Size** to `{1, 0},{0.67, 0}` so the selection UI components take up about more than half of the container from top to bottom (100% horizontally and 67% vertically of the parent frame). 5. Set **Name** to **ObjectiveDisplay**. 3. Create the title elements. 1. Insert an **ImageLabel** into **ObjectiveDisplay**. 2. Select the **ImageLabel**, then in the **Properties** window, 1. Set **AnchorPoint** to `0.5, 1` to set the label's origin point in the bottom-middle of itself (50% from the left to the right of the label, and 100% from the top to the bottom of the label). 2. Set **BackgroundTransparency** to `1` to make the label's background completely transparent. 3. Set **LayoutOrder** to `-1`. 4. Set **Position** to `{0.5, 0},{0.34, 0}` to set the label near the top-upper middle of the frame (50% from the left to the right of the parent frame, and 34% from the top to the bottom of the parent frame). 5. Set **Size** to `{0.46, 0},{0.34, 0}` to widen the prompt area to almost half of the frame (46% horizontally and 34% vertically of the parent frame). 6. Set **Name** to Header. 7. Set **Image** to `rbxassetid://14304828123` to display a trapezoid. 8. Set **ImageTransparency** to `0.15` to make the header semi-transparent. 3. **(Optional)** Insert a **UIAspectRatioConstraint** into the **ImageLabel** to ensure the label's aspect ratio remains the same no matter the player's screen size. The sample sets its `Class.UIAspectRatioConstraint.AspectRatio` property to **13.781**. 4. Insert a **TextLabel** into **Header** to display a title. 5. Select the new label, then in the **Properties** window, 1. Set **AnchorPoint** to `0.5, 0.5` to set the new label's origin point in the middle of itself (50% from the left to the right of the label, and 50% from the top to the bottom of the label). 2. Set **BackgroundTransparency** to `1` to make the label's background completely transparent. 3. Set **Position** to `{0.5, 0},{0.5, 0}` to move the label to the middle of its parent label (50% from the left to the right of the parent label, and 50% from the top to the bottom of the parent label). 4. Set **Size** to `{0.62, 0},{0.55, 0}` to widen the text space to more than half of the parent label (62% horizontally and 55% vertically of the parent label). 5. Set **Name** to **HeaderTextLabel**. 6. Set **FontFace** to **Montserrat** to fit the futuristic aesthetic. 7. Set **Weight** to **Medium** to thicken the font. 8. Set **Text** to **OBJECTIVE**. 9. Enable **TextScaled**. 4. Create the prompt elements. 1. Insert an **ImageLabel** into **ObjectiveDisplay**. 2. Select the **ImageLabel**, then in the **Properties** window, 1. Set **AnchorPoint** to `0.5, 1` to set the label's origin point in the bottom-middle of itself (50% from the left to the right of the label, and 100% from the top to the bottom of the label). 2. Set **BackgroundTransparency** to `1` to make the label's background completely transparent. 3. Set **Position** to `{0.5, 0},{1, 0}` to move the label to the bottom-middle of its parent frame (50% from the left to the right of the parent frame, and 100% from the top to the bottom of the parent frame). 4. Set **Size** to `{0.89, 0},{0.66, 0}` to widen the text space to almost the full width of the parent frame (89% horizontally and 66% vertically of the parent frame). 5. Set **Name** to **Body**. 6. Set **Image** to `rbxassetid://14304827265` to display an upside-down trapezoid. 7. Set **ImageColor3** to `0, 0, 0` to tint the image black. 8. Set **ImageTransparency** to `0.3` to make the header semi-transparent. 3. **(Optional)** Insert a **UIAspectRatioConstraint** into the **ImageLabel** to ensure the label's aspect ratio remains the same no matter the player's screen size. The sample sets its `Class.UIAspectRatioConstraint.AspectRatio` property to `13.781`. 4. Insert a **TextLabel** into **Body** to display a prompt. 5. Select the new label, then in the **Properties** window, 1. Set **AnchorPoint** to `0.5, 0.5` to set the new label's origin point in the middle of itself (50% from the left to the right of the label, and 50% from the top to the bottom of the label). 2. Set **BackgroundTransparency** to `1` to make the label's background completely transparent. 3. Set **Position** to `{0.5, 0},{0.5, 0}` to move the label to the middle of its parent label (50% from the left to the right of the parent label, and 50% from the top to the bottom of the parent label). 4. Set **Size** to `{0.85, 0},{0.39, 0}` to widen the text space to more than half of the parent label (85% horizontally and 39% vertically of the parent label). 5. Set **Name** to **BodyTextLabel**. 6. Set **FontFace** to **Montserrat** to fit the futuristic aesthetic. 7. Set **Weight** to **Medium** to thicken the font. 8. Set **TextColor3** to `255, 255, 255` to make the text white against the dark background. 9. Set **Text** to **Tag opposing players to score points! First team to %d points wins.**. 10. Enable **TextScaled**. 5. Create a container for the objective's team counters. 1. Insert a **Frame** into **Objective**. 2. Select the new **Frame**, then in the **Properties** window, 1. Set **AnchorPoint** to `0.5, 1` to set the label's origin point in the bottom-middle of itself (50% from the left to the right of the frame, and 100% from the top to the bottom of the frame). 2. Set **BackgroundTransparency** to `1` to make the frame's background completely transparent. 3. Set **Position** to `{0.5, 0},{1, 0}` to set the frame in the bottom-middle of the container (50% from the left to the right of the parent frame, and 100% from the top to the bottom of the parent frame). 4. Set **Size** to `{0.44, 0},{0.27, 0}` so the selection UI components take up about less than half of the container from left to right (44% horizontally and 27% vertically of the parent frame). 5. Set **Name** to **TeamPointCounter**. 6. Create padding for the team counters. 1. Insert a **UIListLayout** object into the frame from step 5. 2. Select the **UIListLayout** object, then in the **Properties** window, 1. Set **Padding** to `0.025, 0` to provide space between the future team counters. 2. Set **FillDirection** to **Horizontal** so each team counter displays next to each other. 3. Set **HorizontalAlignment** to **Center** so each team counter aligns to the middle of one another. 7. Create the green team counter elements. 1. Insert an **ImageLabel** into **TeamPointCounter**. 2. Select the **ImageLabel**, then in the **Properties** window, 1. Set **BackgroundTransparency** to `1` to make the label's background completely transparent. 2. Set **Position** to `{0.5, 0},{1, 0}` to move the label to the bottom-middle of its parent frame (50% from the left to the right of the parent frame, and 100% from the top to the bottom of the parent frame). 3. Set **Size** to `{0.5, 0},{1, 0}` to widen the label to half width of the parent frame (50% horizontally and 100% vertically of the parent frame). 4. Set **Name** to **TeamACounter**. 5. Set **Image** to `rbxassetid://14304826831` to display a directional fade. 6. Set **ImageColor3** to `88, 218, 171` to tint the image mint green. 3. Configure a custom attribute to track that this label is for the green team. 1. In the **Properties** window, navigate to the **Attributes** section, then click the plus icon. A pop-up dialog displays. 2. In the **Name** field, input **teamColor**. 3. In the **Type** dropdown menu, select **BrickColor**. 4. Click the **Save** button. 5. Set the new **teamColor** attribute to **Mint**. 4. Insert a **TextLabel** into **TeamACounter** to display a prompt. 5. Select the new label, then in the **Properties** window, 1. Set **AnchorPoint** to `1, 0.5` to set the new label's origin point in the right-middle of itself (100% from the left to the right of the label, and 50% from the top to the bottom of the label). 2. Set **BackgroundTransparency** to `1` to make the label's background completely transparent. 3. Set **Position** to `{0.95, 0},{0.5, 0}` to move the label to the right of its parent label (95% from the left to the right of the parent label, and 50% from the top to the bottom of the parent label). 4. Set **Size** to `{0.85, 0},{0.39, 0}` to widen the text space to more than half of the parent label (85% horizontally and 39% vertically of the parent label). 5. Set **FontFace** to **Montserrat** to fit the futuristic aesthetic. 6. Set **Weight** to **Bold** to thicken the font. 7. Set **TextColor3** to `255, 255, 255` to make the text white against the dark background. 8. Set **Text** to **-**. 9. Enable **TextScaled**. 10. Set **TextXAlignment** to **Right**. 6. Insert a **UIStroke** object into the **TextLabel**, then in the **Properties** window, set **Color** to `8, 78, 52` to outline the dash with a dark green stroke. 8. Create the pink team counter elements. 1. Duplicate **TeamAICounter** and its children. 2. Select the duplicate **TeamACounter**, then in the **Properties** window, 1. Set **Name** to **TeamBCounter**. 2. Set **Image** to `rbxassetid://14305849451` to display a directional fade in the opposite direction. 3. Set **ImageColor3** to `255, 170, 255` to tint the image carnation pink. 4. Set the **teamColor** attribute to **Carnation Pink**. 3. Select the duplicate **TextLabel** child of **TeamBCounter**, then in the **Properties** window, 1. Set **AnchorPoint** to `0, 0.5` to set the new label's origin point in the left-middle of itself (0% from the left to the right of the label, and 50% from the top to the bottom of the label). 2. Set **Position** to `{0.05, 0},{0.5, 0}` to move the label to the left of its parent label (5% from the left to the right of the parent label, and 50% from the top to the bottom of the parent label). 3. Set **TextXAlignment** to **Left**. 4. Select the duplicate **UIStroke** child of **TeamBCounter**, then in the **Properties** window, set **Color** to `158, 18, 94` to outline the dash with a dark pink stroke. 9. Reference the following `Class.ReplicatedStorage` scripts within the sample [Laser Tag](https://www.roblox.com/games/14817965191/Laser-Tag-1A) place file that programmatically update the objective prompt and track team points. #### HUDGuiSetup The following script requires a set of module scripts that work together to set up the main Heads Up Display (HUD), including `setObjective` and `startSyncingTeamPoints`. After a player joins a round and selects their blaster, this script ensures all HUD UI elements display appropriately for the player's state, device, and team status. ```lua local Players = game:GetService("Players") local setPlayerPortrait = require(script.setPlayerPortrait) local setPlayerName = require(script.setPlayerName) local startSyncingTeamColor = require(script.startSyncingTeamColor) local setObjective = require(script.setObjective) local setupTouchButtonAsync = require(script.setupTouchButtonAsync) local startSyncingTeamPoints = require(script.startSyncingTeamPoints) local disableMouseWhileGuiEnabled = require(script.disableMouseWhileGuiEnabled) local setupHitmarker = require(script.setupHitmarker) local localPlayer = Players.LocalPlayer local gui = localPlayer.PlayerGui:WaitForChild("HUDGui") setPlayerPortrait(gui) setPlayerName(gui) startSyncingTeamColor(gui) setObjective(gui) startSyncingTeamPoints(gui) disableMouseWhileGuiEnabled(gui) setupHitmarker(gui) setupTouchButtonAsync(gui) ``` #### setObjective After a player selects their blaster, the following `ReplicatedStorage.HUDGuiSetup.setObjective` script requires the **TEAM_SCORE_LIMIT** module script so that it can swap the placeholder string "%d" in the UI objective's `Class.TextLabel` object. To learn more about this placeholder string, see [Set Objective](/docs/en-us/tutorials/curriculums/gameplay-scripting/add-rounds.md#set-objective) in the Gameplay Scripting curriculum. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local TEAM_SCORE_LIMIT = require(ReplicatedStorage.TEAM_SCORE_LIMIT) local function setObjective(gui: ScreenGui) local bodyTextLabel = gui.Objective.ObjectiveDisplay.Body.BodyTextLabel bodyTextLabel.Text = bodyTextLabel.Text:format(TEAM_SCORE_LIMIT) end return setObjective ``` #### startSyncingTeamPoints As the round begins, the following script stores all points separately under the teamPoints attribute in `Class.Teams` service. As `teamPoints` increments, this module script calls the `startSyncingTeamPoints` function to find the team counter `Class.GuiObject|GuiObjects` within the Objective UI component. When it locates **TeamACounter** and **TeamBCounter**, it gets their `teamColor` attribute, which correlates with the team spawn zones: TeamACounter displays the green team's points, and TeamBCounter tracks the pink team's points. The module script then calls its `getTeamFromTeamColor` function to validate that the TeamACounter's **mint** teamColor attribute and the TeamBCounter's **carnation pink** teamColor attribute matches the `Class.Team.Color` properties underneath the `Class.Teams` service. If so, it returns both of the teams. When this occurs, `startSyncingTeamPoints` sets both team counters' `Class.TextLabel` objects to their corresponding `teamPoints` values, and continues to update them whenever a player scores a point by tagging another player out on the opposite team. To learn more about how the server knows when a team meets the objective goal and wins the round, see [Track Points](/docs/en-us/tutorials/curriculums/gameplay-scripting/add-rounds.md#track-points) in the Gameplay Scripting Curriculum. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local Teams = game:GetService("Teams") local GuiAttribute = require(ReplicatedStorage.GuiAttribute) local function getTeamFromTeamColor(teamColor: Color3): Team? for _, team in Teams:GetTeams() do if team.TeamColor == teamColor then return team end end return nil end local function startSyncingTeamPoints(gui: ScreenGui) for _, teamPointCounter in gui.Objective.TeamPointCounter:GetChildren() do if not teamPointCounter:IsA("GuiObject") then continue end local iconTeamColor = teamPointCounter:GetAttribute(GuiAttribute.teamColor) local team = getTeamFromTeamColor(iconTeamColor) if not team then warn(\`No team found matching the color {iconTeamColor} to sync team points on {teamPointCounter}\`) continue end teamPointCounter.TextLabel.Text = team:GetAttribute(GuiAttribute.teamPoints) team:GetAttributeChangedSignal(GuiAttribute.teamPoints):Connect(function() teamPointCounter.TextLabel.Text = team:GetAttribute(GuiAttribute.teamPoints) end) end end return startSyncingTeamPoints ``` > **Success:** Now, after a player selects their blaster, the objective UI displays on the top of their screen, and tracks each team's points. ### Blaster UI Following the visual hierarchy best practices from [Wireframe Your Layouts](/docs/en-us/tutorials/curriculums/user-interface-design/wireframe-your-layouts.md), this section teaches you how to implement all on-screen UI elements relating to the player's blaster. This grouping of UI elements takes up the majority of the screen space near the center of the screen because it acts as the focal point to draw players' attention to the action in 3D space, and it has the most significance for playing the game. #### Crosshair A crosshair is a UI element that informs players where they're going to make impact when they blast their weapon. This UI element is a vital gameplay requirement for first-person shooter experiences because players need to be able to accurately aim their blaster and tag out enemy team members. Like most other experiences in the first-person shooter genre, the sample laser tag experience positions the crosshair in the center of the screen so players have something static to focus on while their avatar moves through the 3D space. In addition to reducing motion sickness, this placement allows the crosshair to be perceptible while also blending into the overall environment. To exactly recreate the crosshair within the sample [Laser Tag](https://www.roblox.com/games/14817965191/Laser-Tag-1A) experience: 1. Insert an **ImageLabel** into the **HUDGui** `Class.ScreenGui` object. 1. In the **Explorer** window, navigate to the **StarterGui** service. 2. Hover over its child **HUDGui** object, then click the **⊕** icon. A contextual menu displays. 3. From the contextual menu, insert an **ImageLabel**. 2. Select the new **ImageLabel**, then in the **Properties** window, 1. Set **Image** to `rbxassetid://14400935446`. 2. Set **AnchorPoint** to `0.5, 0.5` to set the label's origin point in the middle of the label (50% from the left to the right of the label, and 50% from the top to the bottom of the label). 3. Set **BackgroundTransparency** to `1` to make the label's background completely transparent. 4. Set **Name** to **Crosshair**. 5. Set **Position** to `{0.5,0},{0.5,0}` to set the label in the middle of the screen. 6. Set **ScaleType** to **Fit** so the image fits within its container and doesn't stretch on various screen sizes. 3. **(Optional)** Insert a **UIAspectRatioConstraint** into **Crosshair** to ensure the label's aspect ratio remains the same no matter the player's screen size. The sample sets its `Class.UIAspectRatioConstraint.AspectRatio` property to **0.895**. #### Hit marker A hit marker is a UI element that only displays when a blast makes impact with another player on the enemy team. Like the crosshair, this UI element is a vital gameplay requirement for first-person shooter experiences because it provides visual feedback of when players are successful in tagging out their opponents. To exactly recreate the hit marker within the sample [Laser Tag](https://www.roblox.com/games/14817965191/Laser-Tag-1A) experience: 1. Insert an **ImageLabel** into the **Crosshair** `Class.ImageLabel` object. 1. In the **Explorer** window, navigate to the **StarterGui** service. 2. Hover over its child **Crosshair** object, then click the **⊕** icon. A contextual menu displays. 3. From the contextual menu, insert an **ImageLabel**. 2. Select the new **ImageLabel**, then in the **Properties** window, 1. Set **Image** to `rbxassetid://14401148736` to display the rectangular hit marker icon. 2. Set **AnchorPoint** to `0.5, 0.5` to set the label's origin point in the middle of the label. 3. Set **BackgroundTransparency** to `1` to make the label's background completely transparent. 4. Set **Position** to `{0.5,0},{0.5,0}` to set the label in the middle of the screen. 5. Set **Name** to **Hitmarker**. 6. Set **Size** to `{0.6, 0},{0.06, 0}` to reduce the size of the rectangles around the middle of the crosshair. 7. Set **ImageTransparency** to `1` to make the hit marker completely transparent. The scripts in the following step turn the transparency back to 0 every time a player's blast makes impact with another player on the enemy team. 3. Reference the following `Class.ReplicatedStorage` scripts within the sample [Laser Tag](https://www.roblox.com/games/14817965191/Laser-Tag-1A) place file that programmatically display the hit marker when a blast makes impact with a player on the enemy team. #### HUDGuiSetup The following script requires a set of module scripts that work together to set up the main Heads Up Display (HUD), including `setupHitmarker`. After a player joins a round and selects their blaster, this script ensures all HUD UI elements display appropriately for the player's state, device, and team status. ```lua local Players = game:GetService("Players") local setPlayerPortrait = require(script.setPlayerPortrait) local setPlayerName = require(script.setPlayerName) local startSyncingTeamColor = require(script.startSyncingTeamColor) local setObjective = require(script.setObjective) local setupTouchButtonAsync = require(script.setupTouchButtonAsync) local startSyncingTeamPoints = require(script.startSyncingTeamPoints) local disableMouseWhileGuiEnabled = require(script.disableMouseWhileGuiEnabled) local setupHitmarker = require(script.setupHitmarker) local localPlayer = Players.LocalPlayer local gui = localPlayer.PlayerGui:WaitForChild("HUDGui") setPlayerPortrait(gui) setPlayerName(gui) startSyncingTeamColor(gui) setObjective(gui) startSyncingTeamPoints(gui) disableMouseWhileGuiEnabled(gui) setupHitmarker(gui) setupTouchButtonAsync(gui) ``` #### setupHitmarker The following `HUDGuiSetup.setupHitmarker` module script starts by listening for `playerTaggedBindableEvent`, which fires within `renderBlast` if a player is tagged. When it detects that another player is tagged, it creates a `Class.Tween` that eases the hit marker's `Class.ImageLabel.ImageTransparency` property from `1` to `0`. After 0.4 seconds, the module script resets the hit marker's transparency back to `0`, then the process starts again. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local TweenService = game:GetService("TweenService") local playerTaggedBindableEvent = ReplicatedStorage.Instances.PlayerTaggedBindableEvent -- How long the hitmarker should be visible for after a blast connects with a target player local HITMARKER_FLASH_TIME = 0.4 local HITMARKER_TWEEN_INFO = TweenInfo.new(HITMARKER_FLASH_TIME, Enum.EasingStyle.Quint, Enum.EasingDirection.Out, 0, true) local function setupHitmarker(gui: ScreenGui) local propertyTable = { ImageTransparency = 0, } local tweenHitmarker = TweenService:Create(gui.Crosshair.Hitmarker, HITMARKER_TWEEN_INFO, propertyTable) local function onPlayerTaggedEvent() tweenHitmarker:Cancel() -- The hitmarker will remain at the transparency value at the time of canceling. Reset it to be invisible. gui.Crosshair.Hitmarker.ImageTransparency = 1 tweenHitmarker:Play() end playerTaggedBindableEvent.Event:Connect(onPlayerTaggedEvent) end return setupHitmarker ``` > **Success:** Now, whenever a player blasts their blaster and the blast makes impact with another player, the hit marker momentarily displays. #### Blaster selector A blaster selector is a UI component that players use to select their blaster type before joining or rejoining a round. The sample laser tag experience provides two types of blasters: one that produces several beams with a wide, horizontal spread, and another that produces a single beam. The type of blaster that players select influences their strategy during the round, making this UI component an essential workflow for the overall experience. The following steps detail how to create several containers for the different UI element groupings, a header with a prompt, the navigation and select buttons, and a blaster button prefab. The scripting logic for the overall component populates different visual characteristics into the blaster button prefab according to `Class.Configuration` instances that represent each blaster type. This setup allows you to create additional `Class.Configuration` instances for more blaster types that automatically display correctly within the blaster selector without needing to create individual buttons within `StarterGui.PickABlasterGui`. To exactly recreate the blaster selector within the sample [Laser Tag](https://www.roblox.com/games/14817965191/Laser-Tag-1A) experience: 1. Create a container for the entire component. 1. Insert a **Frame** into the **PickABlaster** `Class.ScreenGui` object. 1. In the **Explorer** window, navigate to the **StarterGui** service. 2. Hover over its child **PickABlaster** object, then click the **⊕** icon. A contextual menu displays. 3. From the contextual menu, insert a **Frame**. 2. Select the new frame, then in the **Properties** window, 1. Set **AnchorPoint** to `0.5, 1` to set the frame's origin point in the bottom-middle of itself (50% from the left to the right of the frame, and 100% from the top to the bottom of the frame). 2. Set **BackgroundTransparency** to `1` to make the frame's background completely transparent. 3. Set **Position** to `{0.5, 0},{0.9, 0}` to set the frame near the bottom-middle of the screen (50% from the left to the right of the screen, and 92.4% from the top to the bottom of the screen). 4. Set **Size** to `{0.8, 0},{0.25, 0}` so the blaster selector's UI components take up a large portion of the screen to grab players' attention (80% horizontally, and 25% vertically). 5. Set **Name** to **Component**. 3. **(Optional)** Insert a **UIAspectRatioConstraint** into **Component** to ensure the frame and its children UI elements' aspect ratio remains the same no matter the player's screen size. The sample sets its `Class.UIAspectRatioConstraint.AspectRatio` property to **5**. 2. Create a container to hold UI element groupings. 1. Insert a **Frame** into **Component**. 2. Select the new frame, then in the **Properties** window, 1. Set **AnchorPoint** to `0.5, 0.5` to set the frames's origin point in the middle of itself (50% from the left to the right of the frame, and 50% from the top to the bottom of the frame). 2. Set **BackgroundTransparency** to `1` to make the frame's background completely transparent. 3. Set **Position** to `{0.5, 0},{0.375, 0}` to set the frame near the top-middle of the container (50% from the left to the right of the parent frame, and 37.5% from the top to the bottom of the parent frame). 4. Set **Size** to `{1, 0},{0.75, 0}` so the selection UI components take up 3/4th of the container (100% horizontally and 75% vertically of the parent frame). 5. Set **Name** to **SelectionFrame**. 3. Create a prompt for the blaster selector. 1. Insert an **ImageLabel** into **SelectionFrame**. 2. Select the new label, then in the **Properties** window, 1. Set **AnchorPoint** to `0.5, 1` to set the label's origin point in the bottom-middle of itself (50% from the left to the right of the label, and 100% from the top to the bottom of the label). 2. Set **BackgroundTransparency** to `1` to make the label's background completely transparent. 3. Set **LayoutOrder** to `-1`. 4. Set **Position** to `{0.5, 0},{0.22, 0}` to set the label near the top-upper middle of the frame (50% from the left to the right of the parent frame, and 22% from the top to the bottom of the parent frame). 5. Set **Size** to `{0.45, 0},{0.22, 0}` to widen the prompt area to almost half of the frame (45% horizontally and 22% vertically of the parent frame). 6. Set **Name** to **Header**. 7. Set **Image** to `rbxassetid://14304828123` to display a trapezoid. 8. Set **ImageTransparency** to `0.15` to make the header semi-transparent. 3. **(Optional)** Insert a **UIAspectRatioConstraint** into the label to ensure the label's aspect ratio remains the same no matter the player's screen size. The sample sets its `Class.UIAspectRatioConstraint.AspectRatio` property to `13.78`. 4. Insert a **TextLabel** into **Header** to display a prompt. 5. Select the new label, then in the **Properties** window, 1. Set **AnchorPoint** to `0.5, 0.5` to set the new label's origin point in the middle of itself (50% from the left to the right of the label, and 50% from the top to the bottom of the label). 2. Set **BackgroundTransparency** to `1` to make the label's background completely transparent. 3. Set **Position** to `{0.5, 0},{0.5, 0}` to move the label to the middle of its parent label (50% from the left to the right of the parent label, and 50% from the top to the bottom of the parent label). 4. Set **Size** to `{0.6, 0},{0.55, 0}` to widen the text space to more than half of the parent label (60% horizontally and 55% vertically of the parent label). 5. Set **Name** to **HeaderTextLabel**. 6. Set **FontFace** to **Montserrat** to fit the futuristic aesthetic. 7. Set **Weight** to **Medium** to thicken the font. 8. Set **Text** to **PICK A BLASTER**. 9. Enable **TextScaled**. 4. Create the container for your blaster button container and selection arrows. 1. Insert an **ImageLabel** into **SelectionFrame**. 2. Select the new label, then in the **Properties** window, 1. Remove the default **Image** value. 2. Set **AnchorPoint** to `0.5, 1` to set the label's origin point in the bottom-middle of itself (50% from the left to the right of the label, and 100% from the top to the bottom of the label). 3. Set **BackgroundColor** to `0, 0, 0` to make the label black. 4. Set **BackgroundTransparency** to `0.3` to reduce the opacity of the label by 30%, and match all black UI elements in the experience. 5. Set **Position** to `{0.5, 0},{1, 0}` to set the label to the bottom-middle of the frame (50% from the left to the right of the parent frame, and 100% from the top to the bottom of the parent frame). 6. Set **Size** to `{1, 0},{0.77, 0}` to widen the label area to the space below the prompt (100% horizontally and 77% vertically of the parent frame). 3. Round the corners of the container. 1. Insert a **UICorner** object into the label. 2. Select the new corner object, then in the **Properties** window, set **CornerRadius** to `0.075, 0` to round the corners. 5. Create the container for your blaster buttons. 1. Insert a **Frame** into the label from step 4. 2. Select the new frame, then in the **Properties** window, 1. Set **AnchorPoint** to `0.5, 0.5` to set the new frame's origin point in the middle of itself (50% from the left to the right of the frame, and 50% from the top to the bottom of the frame). 2. Set **BackgroundTransparency** to `1` to make the frame's background completely transparent. 3. Set **Position** to `{0.5, 0},{0.5, 0}` to set the frame in the middle of its parent label (50% from the left to the right of the parent frame, and 50% from the top to the bottom of the parent frame). 4. Set **Size** to `{0.85, 0},{0.77, 0}` to widen the frame area to most of the label (85% horizontally and 77% vertically of the parent label). 5. Set **Name** to **Container**. 6. Create padding for all future blaster buttons. 1. Insert a **UIListLayout** object into the frame from step 5. 2. Select the new layout object, then in the **Properties** window, 1. Set **Padding** to `0.035, 0` to provide space between all future buttons. 2. Set **FillDirection** to **Horizontal** so each button displays next to each other. 3. Set both **HorizontalAlignment** and **VerticalAlignment** to **Center** so each button aligns to the middle of one another. 7. Create the left navigation button. 1. Insert an **ImageButton** object into the **ImageLabel** from step 4. 2. Select the new button, then in the **Properties** window, 1. Remove the default **Image** value. 2. Set **AnchorPoint** to `0, 0.5` to set the new button's origin point in the left-middle of itself (0% from the left to the right of the button, and 50% from the top to the bottom of the button). 3. Set **BackgroundTransparency** to `0.15` to provide visual feedback on hover that the button is selectable. 4. Set **Position** to `{0.02, 0},{0.5, 0}` to provide padding to the left of the button from its parent container (2% from the left to the right of the parent label, and 50% from the top to the bottom of the parent label). 5. Set **Size** to `{0.04, 0},{0.33, 0}` to make the selection button much smaller than the blaster buttons (4% horizontally and 33% vertically of the parent frame). 6. Set **Name** to **NavigationButtonLeft**. 3. Round the corners of the button. 1. Insert a **UICorner** object into the button. 2. Select the new corner object, then in the **Properties** window, set **CornerRadius** to `0.1, 0` to round the corners. 4. Insert an **ImageLabel** object into the button. 5. Select the new label, then in the **Properties** window, 1. Set **AnchorPoint** to `0.5, 0.5` to set the new label's origin point in the middle of itself (50% from the left to the right of the label, and 50% from the top to the bottom of the label). 2. Set **Position** to `{0.45, 0},{0.5, 0}` to set the label near the middle of its parent button (45% from the left to the right of the parent button, and 50% from the top to the bottom of the parent button). This value isn't in the middle because an arrow doesn't _visually_ look like it's in the middle of the button at `{0.5, 0},{0.5, 0}`. 3. Set **Size** to `{0.8, 0},{0.8, 0}` to widen the label area to space below the prompt (80% horizontally and 80% vertically of the parent frame). 4. Set **BackgroundTransparency** to `1` to make the image's background completely transparent. 5. Set **Image** to `rbxassetid://14309187238`. 6. Set **ScaleType** to **Fit**. 8. Create the right navigation button. 1. Duplicate **NavigationButtonLeft**. 2. Select the duplicate button, then in the **Properties** window, 1. Set **AnchorPoint** to `1, 0.5` to set the new button's origin point in the right-middle of itself (100% from the left to the right of the button, and 50% from the top to the bottom of the button). 2. Set **Position** to `{0.98, 0},{0.5, 0}` to provide padding to the right of the button from its parent container (98% from the left to the right of the parent label, and 50% from the top to the bottom of the parent label). 3. Set **Name** to **NavigationButtonRight**. 3. Select its **ImageLabel** child object. 1. Set **Rotation** to `180` to flip the image. 2. Set **Position** to `{0.55, 0},{0.5, 0}` to set the label near the middle of its parent button (55% from the left to the right of the parent button, and 50% from the top to the bottom of the parent button). This value isn't in the middle because an arrow doesn't _visually_ look like it's in the middle of the button at `{0.5, 0},{0.5, 0}`. 9. Create the **SELECT** button. 1. Insert an **ImageButton** into **Component**. Notice how this process keeps the select button separate from **SelectionFrame** so that you can add padding between the main part of the component from the select button. 2. Select the new button, then in the **Properties** window, 1. Remove the default **Image** value. 2. Set **AnchorPoint** to `0.5, 1` to set the new button's origin point in the bottom-middle of itself (50% from the left to the right of the button, and 100% from the top to the bottom of the button). 3. Set **BackgroundTransparency** to `0.15` to provide visual feedback on hover that the button is selectable. 4. Set **Position** to `{0.5, 0},{0.99, 0}` to set the button near the bottom middle of its container (50% from the left to the right of the parent frame, and 99% from the top to the bottom of the parent frame). 5. Set **Size** to `{0.17, 0},{0.18, 0}` to length the button underneath the blaster buttons (17% horizontally and 18% vertically of the parent frame). 6. Set **Name** to **SelectButton**. 3. Round the corners of the button. 1. Insert a **UICorner** object into the button. 2. Select the new corner object, then in the **Properties** window, set **CornerRadius** to `0.2, 0` to round the corners. 4. Insert a **TextLabel** object into the button so you can display a call to action. 5. Select the new label, then in the **Properties** window, 1. Set **AnchorPoint** to `0.5, 0.5` to set the new label's origin point in the middle of itself (50% from the left to the right of the label, and 50% from the top to the bottom of the label). 2. Set **BackgroundTransparency** to `1` to make the label's background completely transparent. 3. Set **Position** to `{0.5, 0},{0.5, 0}` to move the label to the middle of the button (50% from the left to the right of the parent button, and 50% from the top to the bottom of the parent button). 4. Set **Size** to `{0.9, 0},{0.55, 0}` to widen the text space to almost all of the width of the parent label (90% horizontally and 55% vertically of the parent label). 5. Set **Name** to **SelectTextLabel**. 6. Set **FontFace** to **Montserrat** to fit the futuristic aesthetic. 7. Set **Weight** to **Medium** to thicken the font. 8. Set **Text** to **SELECT**. 9. Enable **TextScaled**. 10. Create a blaster button prefab. 1. In the **ReplicatedStorage** service, create a folder structure to organize your UI objects. The sample uses an **Instances** folder with a child **Guis** folder. 2. Insert an **ImageButton** object into the **Guis** folder. 3. Select the new button, then in the **Properties** window, 1. Remove the default **Image** value. 2. Set **AnchorPoint** to `0.5, 0.5` to set the new button's origin point in the middle of itself (50% from the left to the right of the button, and 50% from the top to the bottom of the button). 3. Set **BackgroundTransparency** to `0.65` to provide visual feedback that the button isn't in focus. Scripts in step 12 provide programmatic visual feedback when the button is in focus. 4. Set **LayoutOrder** to `2`. 5. Set **Name** to **BlasterButtonPrefab**. 6. Set **Size** to `{0.8, 0},{0.8, 0}`. 7. Set **ImageTransparency** to `1` to make the image completely transparent. 4. Insert a **UIAspectRatioConstraint** into **BlasterButtonPrefab** to ensure the button's aspect ratio remains the same within the component no matter the player's screen size. 5. Round the corners of the button. 1. Insert a **UICorner** object into **BlasterButtonPrefab**. 2. Select the **UICorner**, then in the **Properties** window, set **CornerRadius** to `0.05, 0` to round the corners. 6. Insert an **ImageLabel** into **BlasterButtonPrefab**. 7. Select the new label, then in the **Properties** window, 1. Remove the default **Image** value. 2. Set **AnchorPoint** to `0.5, 0.5` to set the new label's origin point in the middle of itself (50% from the left to the right of the label, and 50% from the top to the bottom of the label). 3. Set **BackgroundTransparency** to `1` to make the label's background completely transparent. 4. Set **Position** to `{0.52, 0},{0.497, 0}` to set the label near the middle of its parent button (52% from the left to the right of the parent button, and 49.7% from the top to the bottom of the parent button). This value isn't in the middle because the blaster doesn't _visually_ look like it's in the middle of the button at `{0.5, 0},{0.5, 0}`. 5. Set **Size** to `{1.20, 0},{0.9, 0}` to widen the label area outside of the button (120% horizontally and 90% vertically of the parent button). 6. Set **ScaleType** to **Fit**. 11. Reference the following `Class.ReplicatedStorage` scripts within the sample [Laser Tag](https://www.roblox.com/games/14817965191/Laser-Tag-1A) place file that programmatically display buttons for each blaster, scale the buttons when a player selects a button that isn't in focus, and attach a player's blaster selection to their avatar. #### PickABlasterGuiController The following script requires a set of scripts that work together to create the blaster selector. When a player joins the experience or respawns back into a round after their health reaches zero, this script activates all of the blaster selector's UI elements until the player makes their selection. ```lua local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") local GuiAttribute = require(ReplicatedStorage.GuiAttribute) local setupBlasterButtons = require(script.setupBlasterButtons) local connectResetSelectionOnEnabled = require(script.connectResetSelectionOnEnabled) local localPlayer = Players.LocalPlayer local gui = localPlayer.PlayerGui:WaitForChild("PickABlasterGui") setupBlasterButtons(gui) connectResetSelectionOnEnabled(gui) gui:SetAttribute(GuiAttribute.selectedIndex, 1) ``` #### setupBlasterButtons The following `PickABlasterGuiController.setupBlasterButtons` module script requires a set of scripts that generate the blaster buttons, navigation buttons, and select button. It starts by referencing `ReplicatedStorage.LaserBlastersFolder` to see how many blaster buttons it needs to generate within the blaster button container. This folder contains two `Class.Configuration` instances: - **SingleBlaster** - Produces a single beam that inflicts 10 points of damage. - **MultiBlaster** - Produces several beams with a wide, horizontal spread that each inflict 15 points of damage. Each `Class.Configuration` instance includes properties and attributes that determine the blaster's behavior within a round, and its visual representation within the blaster selector. _SingleBlaster Configuration_ _MultiBlaster Configuration_ For example, the module script uses the following information to populate an individual blaster button prefab with unique visual characteristics for each `Class.Configuration` instance within the folder: - `name` - The name of the `Class.Configuration` instance. - `IconID` - The image that displays in the button prefab to communicate its respective blaster type. - `iconLayoutOrder` - The left-to-right order buttons display in the blaster selector. When a player is in the process of selecting a blaster, `guid:GetAttributeChangedSignal()` listens for when the `selectedIndex` attribute changes. When this attribute changes, the blaster buttons update according to the new `selectedIndex` value. You can learn more about the `selectedIndex` attribute in the `ReplicatedStorage.UpdateSelectedIndex`. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local setupSelectButton = require(script.setupSelectButton) local setupNavButtons = require(script.setupNavButtons) local updateSelectedIndex = require(script.updateSelectedIndex) local GuiAttribute = require(ReplicatedStorage.GuiAttribute) local laserBlastersFolder = ReplicatedStorage.Instances.LaserBlastersFolder local blasterButtonPrefab = ReplicatedStorage.Instances.Guis.BlasterButtonPrefab local function setupBlasterButtons(gui: ScreenGui) local frame = gui.Frame.SelectionFrame.Frame local blasterButtonContainer = frame.Container local blasterButtons = {} local function createBlasterButton(blasterConfiguration: Configuration) local index = blasterConfiguration:GetAttribute("iconLayoutOrder") local blasterButton = blasterButtonPrefab:Clone() -- Name the blaster button the same as the blaster, so we can read the name -- of the button later to get the associated blaster type blasterButton.Name = blasterConfiguration.Name blasterButton.ImageLabel.Image = blasterConfiguration:GetAttribute("iconId") blasterButton.LayoutOrder = index blasterButton.Parent = blasterButtonContainer blasterButton.Activated:Connect(function() gui:SetAttribute(GuiAttribute.selectedIndex, index) end) table.insert(blasterButtons, index, blasterButton) end for _, blaster in laserBlastersFolder:GetChildren() do createBlasterButton(blaster) end -- Setup other buttons that depend on the generated blasterButtons setupSelectButton(gui, blasterButtons) setupNavButtons(gui, blasterButtons) -- Change blaster buttons appearance when they are selected or deselected gui:GetAttributeChangedSignal(GuiAttribute.selectedIndex):Connect(function() local newIndex = gui:GetAttribute(GuiAttribute.selectedIndex) updateSelectedIndex(newIndex, blasterButtons) end) end return setupBlasterButtons ``` #### updateSelectedIndex When a player selects a blaster button with a different `selectedIndex` value from their previous selection, the following `PickABlasterGuiController.setupBlasterButtons.updateSelectedIndex` module script updates the scale and transparency of both the previously selected blaster button and the newly selected blaster button. To start, it defines a value for both selected and unselected buttons. When a player selects another button from the one that is currently in focus, the new selection becomes larger and more opaque than others within the blaster selector, and the previously selected button becomes the size of the button prefab and more translucent. The module script then updates the `newIndex` value, which also updates the `selectedIndex` attribute on the `PickABlasterGui`. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local blasterButtonPrefab = ReplicatedStorage.Instances.Guis.BlasterButtonPrefab local prevIndex = nil -- Size and BackgroundTransparency values for a selected and unselected button local ImageButtonProperties = { Selected = { Size = UDim2.fromScale(1, 1), BackgroundTransparency = 0.1, }, Unselected = { Size = blasterButtonPrefab.Size, BackgroundTransparency = blasterButtonPrefab.BackgroundTransparency, }, } local function updateSelectedIndex(newIndex: number, blasterButtons: { ImageButton }) local selectedProperties = ImageButtonProperties.Selected local selectedButton = blasterButtons[newIndex] selectedButton.Size = selectedProperties.Size selectedButton.BackgroundTransparency = selectedProperties.BackgroundTransparency local unselectedProperties = ImageButtonProperties.Unselected local deselectedButton = blasterButtons[prevIndex] if deselectedButton then deselectedButton.Size = unselectedProperties.Size deselectedButton.BackgroundTransparency = unselectedProperties.BackgroundTransparency end prevIndex = newIndex end return updateSelectedIndex ``` #### setupSelectButton The following `PickABlasterGuiController.setupBlasterButtons.setupSelectButton` module script confirms the player's blaster selection with the server. To demonstrate, when a player presses the select button, this script gets the `blasterName` of the `selectedIndex`value. It then sends this info to the server so that it can equip the correct blaster type to their avatar. For more information on what the server does with this information to equip the correct blaster type to the player, see [Add New Players](/docs/en-us/tutorials/curriculums/gameplay-scripting/spawn-respawn.md#add-new-players) from the Gameplay Scripting curriculum. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local GuiAttribute = require(ReplicatedStorage.GuiAttribute) local blasterSelectedEvent = ReplicatedStorage.Instances.BlasterSelectedEvent local function setupSelectButton(gui: ScreenGui, blasterButtons: { ImageButton }) gui.Frame.SelectButton.Activated:Connect(function() -- During button generation, we set the name of the button to correspond to its associated blaster type local blasterName = blasterButtons[gui:GetAttribute(GuiAttribute.selectedIndex)].Name blasterSelectedEvent:FireServer(blasterName) end) end return setupSelectButton ``` #### setupNavButtons The following `PickABlasterGuiController.setupBlasterButtons.setupNavButtons` module script listens for activations of the left and right navigation buttons. When a player presses the left navigation button, it subtracts `1` from the `selectedIndex` attribute, meaning that the new `selectedIndex` value becomes the blaster button to the left of the previous `selectedIndex` value. Similarly, when a player presses the right navigation button, it adds `1` to the `selectedIndex` attribute, meaning that the new `selectedIndex` value becomes the blaster button to the right of the previous `selectedIndex` value. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local GuiAttribute = require(ReplicatedStorage.GuiAttribute) local function setupNavButtons(gui: ScreenGui, blasterButtons: { ImageButton }) local frame = gui.Frame.SelectionFrame.Frame local navigationButtonLeft = frame.NavigationButtonLeft local navigationButtonRight = frame.NavigationButtonRight navigationButtonLeft.Activated:Connect(function() local currentIndex = gui:GetAttribute(GuiAttribute.selectedIndex) local newIndex = math.clamp(currentIndex - 1, 1, #blasterButtons) gui:SetAttribute(GuiAttribute.selectedIndex, newIndex) end) navigationButtonRight.Activated:Connect(function() local currentIndex = gui:GetAttribute(GuiAttribute.selectedIndex) local newIndex = math.clamp(currentIndex + 1, 1, #blasterButtons) gui:SetAttribute(GuiAttribute.selectedIndex, newIndex) end) end return setupNavButtons ``` > **Success:** Now, whenever a player joins the experience or respawns back into a round after their health reaches zero, the blaster selector UI displays, they can make a selection, and each button functions as expected. #### Blast button A blast button is a UI component that players use to blast their blaster if they are accessing the experience through a mobile or tablet device. The sample laser tag experience uses a blaster button with an icon that depicts both a crosshair and a blast to communicate the button's function without text. To exactly recreate the blast button within the sample [Laser Tag](https://www.roblox.com/games/14817965191/Laser-Tag-1A) experience: 1. Insert an **ImageButton** into the **HUDGui** `Class.ScreenGui` object. 1. In the **Explorer** window, navigate to the **StarterGui** service. 2. Hover over its child **HUDGui** object, then click the **⊕** icon. A contextual menu displays. 3. From the contextual menu, insert an **ImageButton**. 2. In the viewport, move the button to where a player's thumb naturally rests so you can get a visual sense of what the button will look like on a player's device, then in the **Properties** window, 1. Set **Image** to `rbxassetid://18308375035` to display the blast button icon. 2. Set **PressedImage** to `rbxassetid://18308372558` to display an inverted version of the blast button icon when a player presses the button. 3. Set **BackgroundTransparency** to `1` to make the label's background completely transparent. 4. Set **Name** to **BlastButton**. 5. Set **ScaleType** to **Fit** so the image fits within its container and doesn't stretch on various screen sizes. 6. Set **ImageTransparency** to `0.3` to reduce the opacity of the label so that it matches all black UI elements in the experience. 3. Insert a **UIAspectRatioConstraint** into **BlastButton** to ensure the button's aspect ratio remains the same no matter the player's screen size. 4. Reference the following `Class.ReplicatedStorage` scripts within the sample [Laser Tag](https://www.roblox.com/games/14817965191/Laser-Tag-1A) place file that programmatically display the blaster button when a player is using touch input on a device that accepts touch controls. #### HUDGuiSetup The following script requires a set of module scripts that work together to set up the main Heads Up Display (HUD), including `setupTouchButtonAsync`. After a player joins a round and selects their blaster, this script ensures all HUD UI elements display appropriately for the player's state, device, and team status. ```lua local Players = game:GetService("Players") local setPlayerPortrait = require(script.setPlayerPortrait) local setPlayerName = require(script.setPlayerName) local startSyncingTeamColor = require(script.startSyncingTeamColor) local setObjective = require(script.setObjective) local setupTouchButtonAsync = require(script.setupTouchButtonAsync) local startSyncingTeamPoints = require(script.startSyncingTeamPoints) local disableMouseWhileGuiEnabled = require(script.disableMouseWhileGuiEnabled) local setupHitmarker = require(script.setupHitmarker) local localPlayer = Players.LocalPlayer local gui = localPlayer.PlayerGui:WaitForChild("HUDGui") setPlayerPortrait(gui) setPlayerName(gui) startSyncingTeamColor(gui) setObjective(gui) startSyncingTeamPoints(gui) disableMouseWhileGuiEnabled(gui) setupHitmarker(gui) setupTouchButtonAsync(gui) ``` #### setupTouchButtonAsync The following `HUDGuiSetup.setupTouchButtonAsync` module script positions and scales the blaster button near the jump button if the player is accessing the experience with a device that supports touch controls, **and** they are using touch input. It starts by using `Class.UserInputService.TouchEnabled` to check if the player is on a device that supports touch controls. If they are, the script waits for Roblox's core scripts to add the `TouchGui` jump button, then scales and positions `StarterGui.HUDGui.BlastButton` relative to the jump button. Specifically, it scales the blaster button to half of the jump button's size, and offsets the blaster button to the upper right of the jump button's position. This technique is useful because the engine dynamically changes the size and position of its core UI according to the player's device, and the blaster button takes advantage of this preexisting logic so it can also automatically be in the correct relative size and position on any device. To finish, the script checks the player's last input type to verify if they are using a device that supports touch controls but **aren't** using touch input, such as using a gamepad alongside a tablet. If so, the button becomes invisible. ```lua local Players = game:GetService("Players") local UserInputService = game:GetService("UserInputService") local localPlayer = Players.LocalPlayer local TOUCH_BUTTON_SIZE_RATIO_TO_JUMP_BUTTON = 0.75 local function setupTouchButtonAsync(gui: ScreenGui) local blastButton = gui.BlastButton -- TouchEnabled only needs to be read once. If this device doesn't support touch input, -- then we don't need to do anything. if not UserInputService.TouchEnabled then return end -- Since touch is supported, set up the a touch button for firing the blaster. -- Base the size and position of our blast button off of the default jump button, which can differ by device -- Wait for Roblox core scripts to add the default JumpButton local jumpButton = localPlayer.PlayerGui:WaitForChild("TouchGui"):WaitForChild("TouchControlFrame"):WaitForChild("JumpButton") local function updateTouchButtonSizeAndPosition() local scaledTouchButtonSize = UDim2.fromOffset( jumpButton.AbsoluteSize.X * TOUCH_BUTTON_SIZE_RATIO_TO_JUMP_BUTTON, jumpButton.AbsoluteSize.Y * TOUCH_BUTTON_SIZE_RATIO_TO_JUMP_BUTTON ) blastButton.Size = scaledTouchButtonSize blastButton.Position = jumpButton.Position + UDim2.fromOffset(jumpButton.AbsoluteSize.X, 0) end jumpButton:GetPropertyChangedSignal("AbsoluteSize"):Connect(updateTouchButtonSizeAndPosition) jumpButton:GetPropertyChangedSignal("AbsolutePosition"):Connect(updateTouchButtonSizeAndPosition) updateTouchButtonSizeAndPosition() -- Only show the touch button when user is using touch input local function updateTouchVisibility() local lastInputType = UserInputService:GetLastInputType() local isTouchInput = lastInputType == Enum.UserInputType.Touch blastButton.Visible = isTouchInput end UserInputService.LastInputTypeChanged:Connect(updateTouchVisibility) updateTouchVisibility() end return setupTouchButtonAsync ``` #### UserInputHandler The following `ReplicatedStorage.UserInputHandler` client script connects the blast functionality with user input. If the player is using touch controls and the blaster button is enabled on their device, the script listens for touch activation on the blaster button, then activates `attemptBlastClient`. For more information on `attemptBlastClient`and blaster behavior, see [Check Whether the Player Can Blast](/docs/en-us/tutorials/curriculums/gameplay-scripting/implement-blasters.md#check-whether-the-player-can-blast) from the Gameplay Scripting curriculum. ```lua local ContextActionService = game:GetService("ContextActionService") local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") local attemptBlastClient = require(ReplicatedStorage.Blaster.attemptBlastClient) local function onBlasterActivated(_actionName: string, inputState: Enum.UserInputState, _inputObject: InputObject) if inputState == Enum.UserInputState.Begin then attemptBlastClient() end end -- Listen for activation input -- An 'actionName' is irrelevant as we never unbind the action ContextActionService:BindAction("_", onBlasterActivated, false, Enum.UserInputType.MouseButton1, Enum.KeyCode.ButtonR2) -- Listen for touch activation on the HUD blast button local HUDGui = Players.LocalPlayer.PlayerGui:WaitForChild("HUDGui") local blastButton = HUDGui.BlastButton blastButton.MouseButton1Down:Connect(attemptBlastClient) ``` > **Success:** Now, when a player joins the experience and uses touch input on a device that accepts touch controls, the blaster button displays to the top-right of the jump button. ### Player UI Following the visual hierarchy best practices from [Wireframe Your Layouts](/docs/en-us/tutorials/curriculums/user-interface-design/wireframe-your-layouts.md), this section teaches you how to implement all on-screen UI elements relating to the state of the player. This grouping of UI elements is near the sides of the screen because players can comprehend this peripheral information without diverting their attention from the gameplay. #### Player indicator A player indicator is a UI component that players reference to quickly decipher what team they belong to as soon as they spawn into their team's spawn zone. The sample laser tag experience provides two versions of the player indicator depending on if the player is on the **green** or **pink** team. _Green Team_ _Pink Team_ Following the guidance from [Select a Color Theme](/docs/en-us/tutorials/curriculums/user-interface-design/choose-an-art-style.md#select-a-color-theme), both versions of the player indicator combine the team color with a unique, simple icon with minimal detail so that they remain legible on small screens. Providing two forms of visual feedback is important because it helps to keep the design accessible for players with colorblindness. To exactly recreate the player indicator component within the sample [Laser Tag](https://www.roblox.com/games/14817965191/Laser-Tag-1A) experience: 1. Insert a **Frame** into the **HUDGui** `Class.ScreenGui` object. 1. In the **Explorer** window, navigate to the **StarterGui** service. 2. Hover over its child **HUDGui** object, then click the ⊕ icon. A contextual menu displays. 3. From the contextual menu, insert a **Frame**. 2. Select the new **Frame**, then in the **Properties** window, 1. Set **AnchorPoint** to `0, 1` to set the frame's origin point in the bottom-middle of itself (0% from the left to the right of the frame, and 100% from the top to the bottom of the frame). 2. Set **BackgroundTransparency** to `1` to make the label's background completely transparent. 3. Set **Name** to **PlayerDisplay**. 4. Set **Position** to `{0.02, 0},{0.97, 0}` to set the frame near the bottom-left of the screen. 5. Set **Size** to `{0.23, 0},{0.08, 0}` to both shorten and widen the frame. 6. Enable **ClipsDescendants** to trim child GuiObjects that extend beyond the frame. 3. Create the polygonal shape. 1. Insert an **ImageLabel** into **PlayerDisplay**. 2. Select the new label, then in the **Properties** window, 1. Set **Image** to `rbxassetid://14304828123` to display the trapezoid icon. 2. Set **AnchorPoint** to `1, 1` to set the label's origin point in the bottom-right of itself (100% from the left to the right of the label, and 100% from the top to the bottom of the label). 3. Set **BackgroundTransparency** to `1` to make the label's background completely transparent. 4. Set **Name** to **Block**. 5. Set **Position** to `{1,0},{1,0}` to set the label to the right side of the frame. 6. Set **Size** to `{1.858, 0},{0.581, 0}` to widen the label outside of the frame, and shorten it to a little over half the length of the frame. 7. Set **ImageTransparency** to `0.15` to make the label slightly transparent. 8. Set **ScaleType** to **Fit** so the image fits within its container and doesn't stretch on various screen sizes. 1. Insert a **UIAspectRatioConstraint** into **Block** to ensure the label and its children UI elements' aspect ratio remains the same no matter the player's screen size. 2. Select the new constraint, then in the **Properties** window, set **AspectRatio** to `13.78`. 4. Create the box for the player's portrait. 1. Insert an **ImageLabel** into **PlayerDisplay**. 2. Select the new label, then in the **Properties** window, 1. Remove the placeholder asset ID within the **Image** property. The scripts in step 7 programmatically insert the player's portrait into the image label. 2. Set **AnchorPoint** to `0, 1` to set the label's origin point in the bottom-left of itself (0% from the left to the right of the label, and 100% from the top to the bottom of the label). 3. Set **BackgroundColor3** to `0, 0, 0` to set the label's background color to black. 4. Set **BackgroundTransparency** to `0.3` to reduce the opacity of the label by 30%, and match all black UI elements in the experience. 5. Set **Name** to **PlayerPortrait**. 6. Set **Position** to `{0.11, 0},{1, 0}` to set the label to the left side of the polygonal shape. 7. Set **Size** to `{0.23, 0},{1, 0}` to shrink the label. 8. Set **ImageTransparency** to `0.15` to make the label slightly transparent. 9. Set **ScaleType** to **Fit** so the image fits within its container and doesn't stretch on various screen sizes. 1. Insert a **UIAspectRatioConstraint** into **PlayerPortrait** to ensure the label and its children UI elements' aspect ratio remains the same no matter the player's screen size. 2. Insert a **UICorner** into **PlayerPortrait**, then in the **Properties** window, set **CornerRadius** to `0.05, 0` to slightly round the corners. 5. Create the text label for the player's name. 1. Insert a **TextLabel** object into **PlayerDisplay**. 2. Select the new label, then in the **Properties** window, 1. Set **AnchorPoint** to `0, 0.5` to set the new button's origin point in the left-middle of itself (0% from the left to the right of the button, and 50% from the top to the bottom of the button). 2. Set **BackgroundTransparency** to `1` to make the label's background completely transparent. 3. Set **Name** to **PlayerNameTextLabel**. 4. Set **Position** to `{0.35, 0},{0.72, 0}` to set the label to the right side of its container (35% from the left to the right of the parent label, and 72% from the top to the bottom of the parent label). 5. Set **Size** to `{0.52, 0},{0.3, 0}` so the text can take up most of the polygonal shape area (52% horizontally and 30% vertically of the parent frame). 6. Set **FontFace** to **Montserrat** to fit the futuristic aesthetic. 7. Set **Weight** to **Bold** to thicken the font. 8. Remove the placeholder text within the **Text** property. The scripts in step 7 programmatically insert the player's name into the text label. 9. Enable **TextScaled**. 10. Set **TextXAlignment** to **Left**. 6. Create the team icons and colors that display to the left of the player's portrait. 1. Insert a **Folder** into **PlayerDisplay**, then rename it **TeamIcons**. 2. Create the **green** team icon and color. 1. Insert an **ImageLabel** into **TeamIcons**. 2. Select the new label, then in the **Properties** window, 1. Set **AnchorPoint** to `0, 1` to set the label's origin point in the bottom-left of itself (0% from the left to the right of the label, and 100% from the top to the bottom of the label). 2. Set **BackgroundColor3** to `88, 218, 171` to set the label's background color to mint green. 3. Set **Name** to **TeamAIcon**. 4. Set **Position** to `{0, 0},{1, 0}` to set the label to the left side of the frame. 5. Set **Size** to `{0.135, 0},{0.58, 0}` to shrink the label to the left of the player portrait. 6. Set **ImageTransparency** to `1` to make the label transparent. 1. Configure a custom attribute to track that this label is for the green team. This step is very important for the scripts in step 7. 1. In the **Properties** window, navigate to the **Attributes** section, then click the plus icon. A pop-up dialog displays. 2. In the **Name** field, input **teamColor**. 3. In the **Type** dropdown menu, select **BrickColor**. 4. Click the **Save** button. 5. Set the new **teamColor** attribute to **Mint**. 2. Insert a **UIAspectRatioConstraint** into **TeamAIcon** to ensure the label and its children UI elements' aspect ratio remains the same no matter the player's screen size. 3. Create the icon. 1. Insert an **ImageLabel** into **TeamAIcon**. 2. Select the new label, then in the **Properties** window, 1. Set **Image** to **rbxassetid://14309678670** to display the green team icon. 2. Set **AnchorPoint** to `0.5, 0.5` to set the label's origin point in the middle of itself (50% from the left to the right of the label, and 50% from the top to the bottom of the label). 3. Set **BackgroundTransparency** to `1` to make the label's background completely transparent. 4. Set **Name** to **Icon**. 5. Set **Position** to `{0.5, 0},{0.5, 0}` to set the label to the middle of its parent label. 6. Set **Size** to `{0.7, 0},{0.6, 0}` to shrink the label. 7. Set **ScaleType** to **Fit** so the image fits within its container and doesn't stretch on various screen sizes. 3. Create the **pink** team icon and color. 1. Duplicate **TeamAIcon** and its children. 2. Select the duplicate **TeamAIcon**, then in the **Properties** window, 1. Set **BackgroundColor3** to `255, 170, 255` to set the label's background color to carnation pink. 2. Set **Name** to **TeamBIcon**. 3. Set the **teamColor** attribute to **Carnation Pink**. 4. Select the duplicate **Icon** child of **TeamBIcon**, then in the **Properties** window, set **Image** to `rbxassetid://14309678549` to display the pink team icon. 7. Reference the following `Class.ReplicatedStorage` scripts within the sample [Laser Tag](https://www.roblox.com/games/14817965191/Laser-Tag-1A) place file that programmatically display the player indicator with the appropriate team color and icon while a player is active in a round. #### HUDGuiSetup The following script requires a set of module scripts that work together to set up the main Heads Up Display (HUD), including `startSyncingTeamColor`, `setPlayerName`, and `setPlayerPortrait`. After a player joins a round and selects their blaster, this script ensures all HUD UI elements display appropriately for the player's state, device, and team status. ```lua local Players = game:GetService("Players") local setPlayerPortrait = require(script.setPlayerPortrait) local setPlayerName = require(script.setPlayerName) local startSyncingTeamColor = require(script.startSyncingTeamColor) local setObjective = require(script.setObjective) local setupTouchButtonAsync = require(script.setupTouchButtonAsync) local startSyncingTeamPoints = require(script.startSyncingTeamPoints) local disableMouseWhileGuiEnabled = require(script.disableMouseWhileGuiEnabled) local setupHitmarker = require(script.setupHitmarker) local localPlayer = Players.LocalPlayer local gui = localPlayer.PlayerGui:WaitForChild("HUDGui") setPlayerPortrait(gui) setPlayerName(gui) startSyncingTeamColor(gui) setObjective(gui) startSyncingTeamPoints(gui) disableMouseWhileGuiEnabled(gui) setupHitmarker(gui) setupTouchButtonAsync(gui) ``` #### startSyncingTeamColor After a player selects their blaster, the following `ReplicatedStorage.HUDGuiSetup.StartSyncingTeamColor` script calls the `setPlayerTeamIcon` function to match the player indicator's team color and icon to the player's corresponding team. It starts by checking the player's `Class.Player.TeamColor` value set by the `Class.Teams` service. If their `Class.Player.TeamColor|TeamColor` value equals **mint**, the **TeamAIcon** from step 6 becomes visible; conversely, if their `Class.Player.TeamColor|TeamColor` value equals **carnation pink**, the **TeamBIcon** from step 6 becomes visible. For more information on how players sort into a team with a unique team color, see [Creating Teams](/docs/en-us/tutorials/curriculums/gameplay-scripting/create-teams.md) in the Gameplay Scripting curriculum. ```lua local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") local GuiAttribute = require(ReplicatedStorage.GuiAttribute) local localPlayer = Players.LocalPlayer local function setPlayerTeamIcon(gui: ScreenGui) for _, teamColorIcon in gui.PlayerDisplay.TeamIcons:GetChildren() do local iconTeamColor = teamColorIcon:GetAttribute(GuiAttribute.teamColor) teamColorIcon.Visible = localPlayer.TeamColor == iconTeamColor end end local function startSyncingTeamColor(gui: ScreenGui) setPlayerTeamIcon(gui) localPlayer:GetPropertyChangedSignal("Team"):Connect(function() setPlayerTeamIcon(gui) end) end return startSyncingTeamColor ``` #### setPlayerPortrait The following `ReplicatedStorage.HUDGuiSetup.setPlayerPortrait` script sets the player portrait from step 4 to the player's avatar headshot. ```lua local Players = game:GetService("Players") local localPlayer = Players.LocalPlayer local function setPlayerPortrait(gui: ScreenGui) local playerPortrait = gui.PlayerDisplay.PlayerPortrait local userId = localPlayer.UserId local thumbType = "AvatarHeadShot" local rbxthumbContentString = \`rbxthumb://type={thumbType}&id={userId}&w=150&h=150\` playerPortrait.Image = rbxthumbContentString end return setPlayerPortrait ``` #### setPlayerName The following `ReplicatedStorage.HUDGuiSetup.setPlayerName` script sets the text in the text label from step 5 to the player's display name. ```lua local Players = game:GetService("Players") local localPlayer = Players.LocalPlayer local function setPlayerName(gui: ScreenGui) gui.PlayerDisplay.PlayerNameTextLabel.Text = localPlayer.DisplayName end return setPlayerName ``` > **Success:** Now, whenever a player joins or rejoins the round after respawning, the player indicator displays on the bottom-left of their screen. #### Force field screen A force field screen is a UI element that overlays the viewport to inform players they're safe from enemy team fire while joining or rejoining a round. Following the aesthetic guidelines for icons from [Choose an Art Style](/docs/en-us/tutorials/curriculums/user-interface-design/choose-an-art-style.md), the sample laser tag experience utilizes a semi-transparent hexagonal pattern to symbolize a force field. This design decision not only reinforces the overall futuristic art style for all UI in the experience, but it also communicates the player's state without any text or additional guidance. To exactly recreate the force field screen within the sample [Laser Tag](https://www.roblox.com/games/14817965191/Laser-Tag-1A) experience: 1. Insert an **ImageLabel** into the **ForceFieldGui** `Class.ScreenGui` object. 1. In the **Explorer** window, navigate to the **StarterGui** service. 2. Hover over its child **ForceFieldGui** object, then click the **⊕** icon. A contextual menu displays. 3. From the contextual menu, insert an **ImageLabel**. 2. Select the new label, then in the **Properties** window, 1. Set **Image** to `rbxassetid://14462567888`. 2. Set **BackgroundTransparency** to `0.8` to make the force field translucent. 3. Set **Size** to `{1, 0},{1, 0}` to make the image fill the entire screen (100% horizontally and 100% vertically of the parent ScreenGui). 4. Set **ScaleType** to **Tile** to make the hexagon tile across the entire screen. 5. Set **TileSize** to `{0, 104},{0, 180}`. 3. Insert a **UIGradient** object into the label. 4. Select the new gradient object, then in the **Properties** window, 1. Set **Color** to a color sequence that starts blue, turns white, then turns blue again. 1. Set **Color** to `120, 192, 250` to apply a light blue hue to all of the hexagons. 2. Click the **Color** property, then click the **⋯** button. A color sequence pop-up displays. Each triangle on the bottom axis of the color sequence is a keypoint that determines the color value of the property at that point of the image from left to right. 3. Click and drag on the color sequence until you reach a **Time** value of `0.05`, then click the small square next to **Color** to open the **Colors** pop-up window. 4. Select a bright white, then close the pop-up window. 5. Click and drag on the color sequence until you reach a **Time** value of `0.95`, then open the **Colors** pop-up window again, and select the same color white as before. 2. Set **Rotation** to `225` to make the blue part of your color sequence display in the top-left and bottom-right corners. 3. Set **Transparency** to a number sequence that makes the force field look like it's shimmering. 1. Click the **Transparency** property, then click the **⋯** button. A number sequence pop-up displays. Each square at the start and end of the number sequence is a keypoint that determines the transparency value of the property at that point of the image from left to right. 2. Set the following time and value properties throughout the number sequence: - **Time** = `0`, **Value** = `0.25` - **Time** = `.101`, **Value** = `0.875` - **Time** = `.183`, **Value** = `0` - **Time** = `.3`, **Value** = `1` - **Time** = `.7`, **Value** = `1` - **Time** = `1`, **Value** = `0.9` 5. Duplicate the **ImageLabel** from step 2. 6. Select the **UIGradient** object within the duplicate label, then in the **Properties** window, 1. Set **Rotation** to `-45` to flip the image so that it nearly mirrors each other along the Y axis. 2. Modify **Transparency** to make the shimmer look more organic. 1. Click the **Transparency** property, then click the **⋯** button. A number sequence pop-up displays. 2. Select the third keyframe, then click the **Delete** button. 7. Reference the following `Class.ReplicatedStorage` scripts within the sample [Laser Tag](https://www.roblox.com/games/14817965191/Laser-Tag-1A) place file that programmatically display the force field screen while a player joins or rejoins a round. #### ForceFieldClientVisuals The following `ReplicatedStorage.ForceFieldClientVisuals` client script substitutes the default `Class.ForceField` visual with `StarterGui.ForceFieldGui`. When players load into an experience and spawn on a `Class.SpawnLocation` with a `Duration` property that is greater than 0, the default behavior in every experience is to provide their avatar with a protective blue orb that momentarily prevents them from losing health. This script starts by listening to when the `Class.ForceField` is added to a character, disables the default first-person force field visuals, then enables the **ForceFieldGui** `Class.ScreenGui` object. Note that this does **not** impact third-person visuals when players look at other players respawning back into the experience. ![First-person force field visuals include a futuristic hexagonal grid on the perimeter of the screen.](../../../assets/tutorials/gameplay-scripting/Spawn-Respawn/First-Person-Visuals.png)_First-person force field visuals_ ![Third-person force field visuals include a blue sparkling orb around the player spawning into the experience.](../../../assets/tutorials/gameplay-scripting/Spawn-Respawn/Third-Person-Visuals.png)_Third-person force field visuals_ ```lua local Players = game:GetService("Players") local localPlayer = Players.LocalPlayer local function onCharacterAddedAsync(character: Model) local forceField = character:WaitForChild("ForceField", 3) if not forceField then -- If the player spawns at a spawn point with ForceField disabled return end forceField.Visible = false localPlayer.PlayerGui:WaitForChild("ForceFieldGui").Enabled = true forceField.Destroying:Wait() localPlayer.PlayerGui.ForceFieldGui.Enabled = false end if localPlayer.Character then onCharacterAddedAsync(localPlayer.Character) end localPlayer.CharacterAdded:Connect(onCharacterAddedAsync) ``` #### scheduleDestroyForceField After a player selects their blaster, the following `ReplicatedStorage.scheduleDestroyForceField` module script listens for three conditions to check when to disable the **ForceFieldGui** `Class.ScreenGui` object: - They blast their blaster. - They reset their character. - They are in the round for 8 seconds. Once the player meets one of these three conditions, they can receive damage from the enemy team. For more information on these conditions, see [Customize Force Fields](/docs/en-us/tutorials/curriculums/gameplay-scripting/spawn-respawn.md#customize-force-fields) from the Gameplay Scripting curriculum. ```lua local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") local getBlasterStateAttribute = require(ReplicatedStorage.Blaster.getBlasterStateAttribute) local BlasterState = require(ReplicatedStorage.Blaster.BlasterState) local MAX_FORCE_FIELD_TIME = 8 local function destroyForceField(player: Player) if not player.Character then return end local forceField = player.Character:FindFirstChildWhichIsA("ForceField") if forceField then forceField:Destroy() end end local function scheduleDestroyForceField(player: Player) if not player then player = Players.LocalPlayer end local attributeChangedConnection, characterRespawnedConnection local forceFieldEnded = false local function endForceField() -- Set a debounce flag to avoid trying to destroy the same force field more than once if forceFieldEnded then return end forceFieldEnded = true attributeChangedConnection:Disconnect() characterRespawnedConnection:Disconnect() destroyForceField(player) end -- This listens for the first activation of the blaster, disabling the ForceField -- to avoid an unfair situation where a player uses their blaster while protected by the ForceField local blasterStateAttribute = getBlasterStateAttribute() attributeChangedConnection = player:GetAttributeChangedSignal(blasterStateAttribute):Connect(function() local currentBlasterState = player:GetAttribute(blasterStateAttribute) if currentBlasterState == BlasterState.Blasting then endForceField() end end) -- This listens for the character to despawn, ensuring we cancel all our listeners and give the next -- character a fresh start if the character respawns (e.g. player resets) before the timeout ends or the player blasts characterRespawnedConnection = player.CharacterRemoving:Connect(endForceField) -- This handles the timeout for the ForceField after a blaster is selected task.delay(MAX_FORCE_FIELD_TIME, endForceField) end return scheduleDestroyForceField ``` > **Success:** Now, whenever a player joins or rejoins the round after respawning, the new force field screen UI displays instead of the default first-person `Class.ForceField` visuals. #### Respawn screen A respawn screen is a UI element that dims the viewport to inform players that they have been tagged out, and that the server is in the process of respawning them back to their spawn zone. This UI element is important because it gives players time to process that they've been tagged out, and strategize their next move before they rejoin the active round. For more information on custom respawning behavior in the sample laser tag experience, see [Respawn Characters](/docs/en-us/tutorials/curriculums/gameplay-scripting/spawn-respawn.md#respawn-characters) from the Gameplay Scripting curriculum. To exactly recreate the respawn screen within the sample [Laser Tag](https://www.roblox.com/games/14817965191/Laser-Tag-1A) experience: 1. Create the center information banner. 1. Insert an **ImageLabel** into the **OutStateGui** `Class.ScreenGui` object. 1. In the **Explorer** window, navigate to the **StarterGui** service. 2. Hover over its child **OutStateGui** object, then click the **⊕** icon. A contextual menu displays. 3. From the contextual menu, insert an **ImageLabel**. 2. Select the new label, then in the **Properties** window, 1. Set **AnchorPoint** to `0.5, 0.5` to set the new button's origin point in the middle of itself (50% from the left to the right of the label, and 50% from the top to the bottom of the label). 2. Set **BackgroundTransparency** to `1` to make the label's background completely transparent. 3. Set **Position** to `{0.5, 0},{0.5, 0}` to set the label in the middle of its container (50% from the left to the right of the parent ScreenGui, and 50% from the top to the bottom of the parent ScreenGui). 4. Set **Size** to `{0.48, 0},{0.06, 0}` to widen the label (48% horizontally and 6% vertically of the parent ScreenGui). 5. Set **Name** to **Block**. 6. Set **Image** to `rbxassetid://14304827265` to make the image a trapezoid. 7. Set **ImageColor** to `0,0,0` to make the trapezoid black. 8. Set **ImageTransparency** to `0.3` to reduce the opacity of the label by 30%, and match all black UI elements in the experience. 3. Insert a **UIAspectRatioConstraint** into **Block** to ensure the label and its children UI elements' aspect ratio remains the same no matter the player's screen size. 4. Select the new constraint, then in the **Properties** window, set **AspectRatio** to `13.78`. 5. Insert a **TextLabel** into **Block** for the informational text. 6. Select the new label, then in the **Properties** window, 1. Set **AnchorPoint** to `0.5, 0.5` to set the label's origin point in the middle of itself (50% from the left to the right of the label, and 50% from the top to the bottom of the label). 2. Set **BackgroundTransparency** to `1` to make the label's background completely transparent. 3. Set **Position** to `{0.5, 0},{0.5, 0}` to set the label in the middle of its parent label (50% from the left to the right of the parent label, and 50% from the top to the bottom of the parent label). 4. Set **Size** to `{.85, 0},{0.55, 0}` so the text can take up most of the trapezoid area (85% horizontally and 55% vertically of the parent label). 5. Set **Name** to **BodyTextLabel**. 6. Set **FontFace** to **Montserrat** to fit the futuristic aesthetic. 7. Set **Weight** to **Bold** to thicken the font. 8. Set **Text** to **Respawning…**. 9. Set **TextColor3** to `255, 255, 255` to make the text white. 10. Enable **TextScaled**. 2. Create the header. 1. Insert an **ImageLabel** into **Block**. 2. Select the new label, then in the **Properties** window, 1. Set **AnchorPoint** to `0.5, 1` to set the label's origin point in the bottom-middle of itself (50% from the left to the right of the label, and 100% from the top to the bottom of the label). 2. Set **BackgroundTransparency** to `1` to make the label's background completely transparent. 3. Set **Position** to `{0.5, 0},{0, 0}` to set the label at the top-middle of its parent label (50% from the left to the right of the parent label, and 0% from the top to the bottom of the parent label). 4. Set **Size** to `{0.46, 0},{0.56, 0}` to widen the label (46% horizontally and 56% vertically of the parent label). 5. Set **Name** to **Header**. 6. Set **Image** to `rbxassetid://14304826985` to make the image a multi-directional fade. 7. Set **ImageColor** to `245, 46, 46` to make the fade red to signify that the player is temporarily inactive while they're tagged out of the round. 3. Insert a **TextLabel** into **Header** for the informational text. 4. Select the new label, then in the **Properties** window, 1. Set **AnchorPoint** to `0.5, 0.5` to set the label's origin point in the middle of itself (50% from the left to the right of the label, and 50% from the top to the bottom of the label). 2. Set **BackgroundTransparency** to `1` to make the label's background completely transparent. 3. Set **Position** to `{0.5, 0},{0.5, 0}` to set the label in the middle of its parent label (50% from the left to the right of the parent label, and 50% from the top to the bottom of the parent label). 4. Set **Size** to `{.85, 0},{0.55, 0}` so the text can take up most of the fade area (85% horizontally and 55% vertically of the parent label). 5. Set **Name** to **HeaderTextLabel**. 6. Set **FontFace** to **Montserrat** to fit the futuristic aesthetic. 7. Set **Weight** to **Black** to thicken the font. 8. Set **Text** to **TAGGED - YOU'RE OUT!** 9. Set **TextColor3** to `255, 255, 255` to make the text white. 10. Enable **TextScaled**. 3. Create the fade around the borders of the screen. 1. Insert an **ImageLabel** into **OutStateGui**. 2. Select the new label, then in the **Properties** window, 1. Set **AnchorPoint** to `0.5, 0.5` to set the label's origin point in the middle of itself (50% from the left to the right of the label, and 50% from the top to the bottom of the label). 2. Set **BackgroundColor3** to `0,0,0` to set the label's background color to black. 3. Set **BackgroundTransparency** to `0.5` to make the label's background halfway transparent. 4. Set **Position** to `{0.5, 0},{0.5, 0}` to set the label at the middle of its container (50% from the left to the right of the parent ScreenGui, and 50% from the top to the bottom of the parent ScreenGui). 5. Set **Size** to `{1, 0},{1, 0}` to widen the label to the whole screen (100% horizontally and 100% vertically of the parent ScreenGui). 6. Set **ZIndex** to `-1` to display the fade behind the other UI elements. 7. Set **Name** to **Header**. 8. Set **Image** to `rbxassetid://14309518613` to make the image a border fade. 9. Set **ImageTransparency** to `0.1` to make the fade slightly translucent. > **Info:** To improve performance, you can skip the image asset and instead use a `Class.UIGradient` object with a custom transparency number sequence. To learn more about this fade technique, see [UI Appearance Modifiers - Gradient](/docs/en-us/ui/appearance-modifiers.md#gradient). 4. Reference the following `Class.ReplicatedStorage` script within the sample [Laser Tag](https://www.roblox.com/games/14817965191/Laser-Tag-1A) place file that programmatically displays the respawn screen when a player's health reaches zero, and they're in the process of respawning back to their team's spawn zone. #### PlayerStateHandler The following `ReplicatedStorage.PlayerStateHandler` client script contains functions that trigger different types of behavior according to the `playerState` attribute. All event responses are logically grouped together in this script because they require similar behavior of enabling or disabling player controls, camera movement, and which UI layer is visible. When a player's health reaches zero, their `playerState` becomes `TaggedOut`, which triggers the `onTaggedOut()` function. `onTaggedOut()` immediate triggers the following behavior: - The player can't move in the arena. - The player can't move their camera. - The player can't use their blaster. - The `StarterGui.OutStateGui` becomes exclusively enabled. When the player respawns, their `playerState` becomes `SelectingBlaster`, which triggers the `onSelectingBlaster()` function. `onSelectingBlaster()` then exclusively enables the `StarterGui.PickABlasterGui`, which automatically disables the respawn screen. For more information on these conditions, see [Handle Client State](/docs/en-us/tutorials/curriculums/gameplay-scripting/spawn-respawn.md#handle-client-state) from the Gameplay Scripting curriculum. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local Players = game:GetService("Players") local PlayerState = require(ReplicatedStorage.PlayerState) local PlayerAttribute = require(ReplicatedStorage.PlayerAttribute) local BlasterState = require(ReplicatedStorage.Blaster.BlasterState) local togglePlayerMovement = require(script.togglePlayerMovement) local togglePlayerCamera = require(script.togglePlayerCamera) local scheduleDestroyForceField = require(ReplicatedStorage.scheduleDestroyForceField) local localPlayer = Players.LocalPlayer local playerGui = localPlayer.PlayerGui local guiLayers = { playerGui:WaitForChild("HUDGui"), playerGui:WaitForChild("OutStateGui"), playerGui:WaitForChild("PickABlasterGui"), } -- Disable all UI Layers except the given exception local function setGuiExclusivelyEnabled(enabledGui: ScreenGui?) -- guiLayers contains a list of the guis that should be set exclusively. for _, screenGui in guiLayers do screenGui.Enabled = screenGui == enabledGui end end local function onSelectingBlaster() -- Enable the camera so players can look around while selecting a blaster togglePlayerCamera(true) togglePlayerMovement(false) setGuiExclusivelyEnabled(playerGui.PickABlasterGui) -- Disable blaster while selecting a blaster localPlayer:SetAttribute(PlayerAttribute.blasterStateClient, BlasterState.Disabled) end local function onPlaying() -- Enable player movement after picking a blaster togglePlayerMovement(true) setGuiExclusivelyEnabled(playerGui.HUDGui) -- Enable blaster while playing localPlayer:SetAttribute(PlayerAttribute.blasterStateClient, BlasterState.Ready) -- Schedule the destroy force field logic when the player begins playing scheduleDestroyForceField() end local function onTaggedOut() -- Disable controls while tagged out togglePlayerMovement(false) togglePlayerCamera(false) setGuiExclusivelyEnabled(playerGui.OutStateGui) -- Disable blaster while tagged out localPlayer:SetAttribute(PlayerAttribute.blasterStateClient, BlasterState.Disabled) end local function onInLobby() -- Enable controls while in the lobby togglePlayerMovement(true) togglePlayerCamera(true) -- Hide all HUD while in the lobby setGuiExclusivelyEnabled(nil) -- Disable blaster while in the lobby localPlayer:SetAttribute(PlayerAttribute.blasterStateClient, BlasterState.Disabled) end local function onPlayerStateChanged(newPlayerState: string) if newPlayerState == PlayerState.SelectingBlaster then onSelectingBlaster() elseif newPlayerState == PlayerState.Playing then onPlaying() elseif newPlayerState == PlayerState.TaggedOut then onTaggedOut() elseif newPlayerState == PlayerState.InLobby then onInLobby() else warn(\`Invalid player state ({newPlayerState})\`) end end -- Handle the initial player state if set local initialPlayerState = localPlayer:GetAttribute(PlayerAttribute.playerState) onPlayerStateChanged(initialPlayerState) local function updateFromPlayerState() onPlayerStateChanged(localPlayer:GetAttribute(PlayerAttribute.playerState)) end -- Handle future player state updates localPlayer:GetAttributeChangedSignal(PlayerAttribute.playerState):Connect(updateFromPlayerState) -- Make sure changes are still applied after respawning localPlayer.CharacterAdded:Connect(updateFromPlayerState) ``` > **Success:** Now, whenever a player's health reaches zero, the respawn screen displays until the player respawns back into their spawn zone. > **Info:** Now that you are familiar with common `Class.GuiObject|GuiObjects` for on-screen UI, try to recreate the **RoundResultsGui** `Class.ScreenGui` object and all of its children for the on-screen display when players win or lose a match. You can use the [sample laser tag experience](https://www.roblox.com/games/14817965191/Laser-Tag-1A) `.rbxl` file as a reference, or adjust the values to meet the gameplay requirements of your own experience. ## Create SurfaceGui objects To display UI on a part's surface in the 3D space that responds to scripting logic for **each individual player**, you can parent a `Class.SurfaceGui` object to the part that you want to display your UI within the `Class.ReplicatedStorage` service. This technique ensures your UI and its scripting logic are available to both the server and each player's client. `Class.SurfaceGui` objects contain all `Class.GuiObject|GuiObjects` that display on a part's surface in the 3D space. The sample laser tag experience only includes one instance of a `Class.SurfaceGui` object: the cooldown meter that displays over each player's blaster. This object needs scripting logic for every player because it actively responds to each individual player's input, and provides visual feedback of when they can blast their blaster again. To create a `Class.SurfaceGui` object: 1. In the **Explorer** window, hover over the **ReplicatedStorage** service, then click the **⊕** icon. A contextual menu displays. 2. From the contextual menu, insert a **Part** object. 3. Insert a **ScreenGui** object into the part. 4. Rename the **SurfaceGui** according to the context of its child UI elements. 5. Repeat this process for every UI element you need to display on a part's surface in the 3D space. ### Cooldown meter A cooldown meter is a UI component that informs players how long they have to wait before they're able to blast their blaster again. This slight pause prevents players from being able to blast as quickly as they can click or press a button, which is unrealistic for laser tag gameplay. To exactly recreate the cooldown meter within the sample [Laser Tag](https://www.roblox.com/games/14817965191/Laser-Tag-1A) experience: 1. Create a part to hold your `Class.SurfaceGui` object. 1. In the **Explorer** window, hover over the **Workspace**, then click the ⊕ icon. A contextual menu displays. 2. From the contextual menu, insert a **block** part. This is a temporary location for the part so that you can visualize the changes in each step of the process. 2. Position and orient the part around the position of where a player's character would hold their blaster, then in the **Properties** window, 1. Set **Transparency** to 1 to make the part completely transparent. 2. Set **Name** to **CooldownBarPrefab**. 3. Set **Size** to `0.169, 0.027, 2.537` to scale the part to a size about the length of the blaster. 4. Disable **CanCollide** and **CanQuery**. 3. Insert a **SurfaceGui** into **CooldownBarPrefab**. 4. Select the new **SurfaceGui**, then in the **Properties** window, 1. Set **Face** to **Top** so the UI displays facing upward. 2. Set **LightInfluence** and **MaxDistance** to `0`. 3. Set **PixelsPerStud** to `200`. 5. Create the black bar. 1. Insert an **ImageLabel** into the **SurfaceGui**. 2. Select the new label, then in the **Properties** window, 1. Remove the default **Image** value. 2. Set **AnchorPoint** to `0.5, 0.5` to set the label's origin point in the middle of itself (50% from the left to the right of the label, and 50% from the top to the bottom of the label). 3. Set **BackgroundColor3** to `0,0,0` to set the label's background color to black. 4. Set **BackgroundTransparency** to `0.4` to make the label's background semi-transparent. 5. Set **Position** to `{0.5, 0},{0.5, 0}` to set the label at the middle of its container (50% from the left to the right of the parent SurfaceGui, and 50% from the top to the bottom of the parent SurfaceGui). 6. Set **Size** to `{1, 0},{1, 0}` to widen the label to the whole part (100% horizontally and 100% vertically of the parent SurfaceGui). 7. Set **Name** to **Container**. 6. Round the corners of the container. 1. Insert a **UICorner** object into **Container**. 2. Select the **UICorner**, then in the **Properties** window, set **CornerRadius** to `0.15, 0` to slightly round the corners. 7. Create the red bar. 1. Insert an **ImageLabel** into **Container**. 2. Select the new label, then in the **Properties** window, 1. Remove the default **Image** value. 2. Set **AnchorPoint** to `1, 0.5` to set the label's origin point in the right-middle of itself (100% from the left to the right of the label, and 50% from the top to the bottom of the label). 3. Set **BackgroundColor3** to `172, 13, 13` to set the label's background color to a dark red. 4. Set **BackgroundTransparency** to `0.2` to make the label's background slightly transparent. 5. Set **Name** to **Bar**. 6. Set **Position** to `{1, 0},{0.5, 0}` to set the label at the right-middle of its container (100% from the left to the right of the parent label, and 50% from the top to the bottom of the parent label). 7. Set **Size** to `{0, 0},{1, 0}` to lengthen the label to the top of its parent label (0% horizontally and 100% vertically of the parent label). This step is also beneficial for the tweening behavior that occurs in the scripts in step 8. 8. Round the corners of the label. 1. Insert a **UICorner** object into **Bar**. 2. Select the **UICorner**, then in the **Properties** window, set **CornerRadius** to `0.15, 0` to slightly round the corners. 9. Move **CooldownBarPrefab** to **ReplicatedStorage**. 1. Create a folder structure to organize your UI objects. The sample uses an **Instances** folder with a child **Guis** folder. 2. Move **CooldownBarPrefab** into **Guis**. 10. Reference the following `Class.ReplicatedStorage` scripts within the sample [Laser Tag](https://www.roblox.com/games/14817965191/Laser-Tag-1A) place file that programmatically attach the cooldown meter to the player's blaster, then animate the red bar after a player blasts their blaster. #### FirstPersonBlasterVisuals The following `ReplicatedStorage.FirstPersonBlasterVisuals` client script handles all visual logic for the player's first-person blaster. It requires a set of module scripts that work together to set up blaster visuals that feel more realistic for laser tag gameplay, including `FirstPersonBlasterVisuals.addCooldownBar` and `FirstPersonBlasterVisuals.runCooldownBarEffect`. ```lua local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") local Workspace = game:GetService("Workspace") local RunService = game:GetService("RunService") local BlastData = require(ReplicatedStorage.Blaster.BlastData) local PlayerAttribute = require(ReplicatedStorage.PlayerAttribute) local PlayerState = require(ReplicatedStorage.PlayerState) local getBlasterConfig = require(ReplicatedStorage.Blaster.getBlasterConfig) local runBlastVisuals = require(script.runBlastVisuals) local setupAnimations = require(script.setupAnimations) local addCooldownBar = require(script.addCooldownBar) local runCooldownBarEffect = require(script.runCooldownBarEffect) local laserBlastedBindableEvent = ReplicatedStorage.Instances.LaserBlastedBindableEvent local RIG_OFFSET_FROM_CAMERA = CFrame.new(2, -2, -3) * CFrame.Angles(math.rad(0.25), math.rad(95.25), 0) local localPlayer = Players.LocalPlayer local currentCamera = Workspace.CurrentCamera local rigModel = nil local cooldownBar = nil local animations = {} local function addFirstPersonVisuals() local blasterConfig = getBlasterConfig() -- Add the first person rig rigModel = blasterConfig.RigModel:Clone() rigModel.Parent = Workspace -- Add the cooldownBar cooldownBar = addCooldownBar(rigModel.PrimaryPart.CooldownBarAttachment) animations = setupAnimations(blasterConfig, rigModel) end local function removeFirstPersonVisuals() for _, animation in animations do animation:Stop() animation:Destroy() animation = nil end if rigModel then -- This also destroys the cooldown bar since it is parented to the rig rigModel:Destroy() rigModel = nil end end -- Run first person visual effects when a blast occurs laserBlastedBindableEvent.Event:Connect(function(blastData: BlastData.Type) runBlastVisuals(rigModel.PrimaryPart.TipAttachment, blastData, animations.blastAnimation) runCooldownBarEffect(cooldownBar) end) -- Bind the rig to the camera if it exists RunService.RenderStepped:Connect(function() if rigModel then -- Update to rig's CFrame relative to the camera's position and RIG_OFFSET_FROM_CAMERA rigModel:PivotTo(currentCamera.CFrame * RIG_OFFSET_FROM_CAMERA) end end) -- Handles changing visuals when the blasterType changes while playing localPlayer:GetAttributeChangedSignal(PlayerAttribute.blasterType):Connect(function() local playerState = localPlayer:GetAttribute(PlayerAttribute.playerState) if playerState == PlayerState.Playing then removeFirstPersonVisuals() addFirstPersonVisuals() end end) -- Handles changing visuals when the playerState changes localPlayer:GetAttributeChangedSignal(PlayerAttribute.playerState):Connect(function() local newPlayerState = localPlayer:GetAttribute(PlayerAttribute.playerState) -- Remove the visuals when the player is selecting a blaster or is in the lobby if newPlayerState == PlayerState.SelectingBlaster or newPlayerState == PlayerState.InLobby then removeFirstPersonVisuals() -- Add the visuals back when the player finishes selecting the blaster. elseif newPlayerState == PlayerState.Playing then addFirstPersonVisuals() end end) ``` #### addCooldownBar The following `FirstPersonBlasterVisuals.addCooldownBar` module script attaches a new instance of the cooldown meter to the player's blaster. When a player selects a blaster, the script moves and welds the cooldown meter part to the blaster in a location that tilts the cooldown meter toward the camera. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local cooldownBarPrefab = ReplicatedStorage.Instances.Guis.CooldownBarPrefab local function addCooldownBar(attachment: Attachment): Part local part = cooldownBarPrefab:Clone() -- Move and weld the cooldown bar part to the blaster part:PivotTo(attachment.WorldCFrame) local weld = Instance.new("WeldConstraint") weld.Part0 = attachment.Parent weld.Part1 = part weld.Parent = part -- Make sure the weld is setup prior to parenting to the blaster part.Parent = attachment.Parent return part end return addCooldownBar ``` #### runCooldownBarEffect The following `FirstPersonBlasterVisuals.runCooldownBarEffect` module script handles the tweening visual effect that communicates the blaster is unable to blast again until the red bar is no longer visible. When a player blasts their blaster, the script sets `Bar` to the full width of `Container`, then tweens the size down to `0` for the duration of `secondsBetweenBlasts`. Once the size of `Bar` is `0`, the player can blast again, and the entire process repeats. For more information on blaster behavior, see [Implement Blasters](/docs/en-us/tutorials/curriculums/gameplay-scripting/implement-blasters.md) from the Gameplay Scripting curriculum. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local TweenService = game:GetService("TweenService") local getBlasterConfig = require(ReplicatedStorage.Blaster.getBlasterConfig) local END_SIZE = UDim2.fromScale(0, 1) local EASING_DIRECTION = Enum.EasingDirection.In local EASING_STYLE = Enum.EasingStyle.Quad local function runCooldownBarEffect(part: Part) local bar = part.SurfaceGui.Container.Bar -- Set bar size to 1 (bar filled) bar.Size = UDim2.fromScale(1, 1) -- Tween the size to 0 (bar empty) for the duration of secondsBetweenBlasts local secondsBetweenBlasts = getBlasterConfig():GetAttribute("secondsBetweenBlasts") local tweenInfo = TweenInfo.new(secondsBetweenBlasts, EASING_STYLE, EASING_DIRECTION) local propertyTable = { Size = END_SIZE, } local tween = TweenService:Create(bar, tweenInfo, propertyTable) tween:Play() end return runCooldownBarEffect ``` > **Success:** Now, whenever a player blasts their blaster, the cooldown bar animates to communicate when the player can blast again. ## Create BillboardGui objects In order to display UI elements within the 3D space that respond to scripting logic and always face each player's camera regardless of their viewing angle, such as player names or map markers, you can create a `Class.BillboardGui` object as a child of a `Class.BasePart` or `Class.Attachment` that exists in the 3D space. The sample laser tag experience includes two separate `Class.BillboardGui` objects within the `Class.ReplicatedStorage` service: - **OtherPlayerIndicatorGuiPrefab** - Displays a pink or green circle above each player's head when they are active in a round. - **TaggedOutIndicatorGuiPrefab** - Displays above a player's head when they are tagged out of the round. After you create a `Class.BillboardGui` object, you can create and customize its child `Class.GuiObject|GuiObjects` according to each container's purpose. To demonstrate, in the immediate sections that follow, you will learn how to implement UI elements for both indicator types within the sample laser tag experience. **You can adjust any part of the process to meet the specifications of your own experience**. To create a `Class.BillboardGui` object: 1. In the **Explorer** window, hover over a `Class.BasePart` or `Class.Attachment`, then click the **⊕** icon. A contextual menu displays. 2. From the contextual menu, insert a **BillboardGui** object. 3. Rename the **BillboardGui** according to the context of its child UI elements. 4. Repeat this process for every UI element you need to contextually display above players' heads. ### Team indicator A team indicator is a UI element that informs players which team other players in the round belong to so that they can easily differentiate between their allies and enemy team members. This information is important because the gameplay of a first-person shooter experience requires players to make quick strategic decisions while they're in combat zones so that they don't get tagged out and lose the match. To exactly recreate the team indicator within the sample [Laser Tag](https://www.roblox.com/games/14817965191/Laser-Tag-1A) experience: 1. Insert a **BillboardGui** object into a temporary rig. 1. From the toolbar's **Home** or **Avatar** tab, click **Character**. 2. Select from the available options. The sample uses a **R15** rig type, a **feminine** body shape, and a **Rthro** avatar. The rig displays both in the 3D viewport and in the **Explorer** window under the name **Rig**. 3. In the **Explorer** window, navigate to the rig's child **Head** mesh, then click the **⊕** icon. A contextual menu displays. 4. From the contextual menu, insert a **BillboardGui**. 2. Select the new **BillboardGui**, then in the **Properties** window, 1. Set **LightInfluence** to `0` to prevent environmental light from affecting the color of the indicator. 2. Set **Name** to **OtherPlayerIndicatorPrefab**. 3. Set **Size** to `{0, 10},{0, 10}` to make the label significantly smaller. 4. Set **StudsOffsetWorldSpace** to `0, 4, 0` to position it above the rig's head. 3. Insert a **Frame** object into **OtherPlayerIndicatorPrefab**. 4. Select the new frame, then in the **Properties** window, 1. Set **AnchorPoint** to `0.5, 0.5` to set the frame's origin point in the middle of itself (50% from the left to the right of the label, and 50% from the top to the bottom of the label). 2. Set **BackgroundColor3** to `255, 3, 0` to set the frame's background color to red as a placeholder color. 3. Set **Position** to `{0.5, 0},{0.5, 0}` to set the frame to the middle of its container (50% from the left to the right of the parent BillboardGui, and 50% from the top to the bottom of the parent BillboardGui). 4. Set **Size** to `{1, -2},{1, -2}` to shorten the frame to the surface area of the BillboardGui. 5. Insert a **UICorner** object into **Frame** to completely round the corners. 6. Insert a **UIStroke** object into **Frame** to outline the circle of the indicator. 7. Move **OtherPlayerIndicatorPrefab** to **ReplicatedStorage**. 8. Reference the following `Class.ReplicatedStorage` script within the sample [Laser Tag 1A](https://www.roblox.com/games/14817965191/Laser-Tag-1A) place file that programmatically displays the team indicator for every player in an active round unless they are on the enemy team and occluded. #### OtherPlayerIndicatorGuiSetup The following `ReplicatedStorage.OtherPlayerIndicatorGuiSetup` script runs when players spawn into the arena for an active round. It attaches the team indicator by calling the `addIndicatorToCharacter()` function, which locates the `Head` object of each player character participating in the round. If they don't already have a team indicator, the script then clones and adds the **otherPlayerIndicatorPrefab** UI to the character's `Head`, and sets the team indicator color to their team color. If other players are on the same team, the team indicator always displays, even if they hide behind objects in the 3D space; if other players are on the enemy team, the team indicator only displays if there isn't an object in the 3D space to occlude them. ```lua local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") local localPlayer = Players.LocalPlayer local otherPlayerIndicatorPrefab = ReplicatedStorage.Instances.Guis.OtherPlayerIndicatorPrefab local characterSpawnConnectionsByPlayer: { [Player]: RBXScriptConnection } = {} local playerAddedConnection: RBXScriptConnection? local function removeIndicatorFromPlayer(player: Player) if not player.Character then return end local head = player.Character:WaitForChild("Head", 3) if not head then return end local gui = head:FindFirstChild(otherPlayerIndicatorPrefab.Name) if gui then gui:Destroy() end end local function addIndicatorToCharacter(otherCharacter: Model?) local otherPlayer = Players:GetPlayerFromCharacter(otherCharacter) if not otherPlayer then return end task.spawn(function() local otherHead = otherCharacter:WaitForChild("Head", 3) if not otherHead then return end -- Only add indicators to players participating in the round if not otherPlayer.Team then return end -- Avoid adding duplicate indicators, creating a new one only if it doesn't exist local gui = otherHead:FindFirstChild(otherPlayerIndicatorPrefab.Name) if not gui then gui = otherPlayerIndicatorPrefab:Clone() gui.Frame.BackgroundColor3 = otherPlayer.TeamColor.Color gui.Parent = otherHead end -- The indicator is always on top only if the player is friendly local isFriendly = otherPlayer.Team == localPlayer.Team gui.AlwaysOnTop = isFriendly end) end local function addIndicatorWhenCharacterSpawns(player: Player) if characterSpawnConnectionsByPlayer[player] then return end local connection = player.CharacterAdded:Connect(addIndicatorToCharacter) characterSpawnConnectionsByPlayer[player] = connection end local function stopSyncingIndicators() for _, connection in characterSpawnConnectionsByPlayer do connection:Disconnect() end table.clear(characterSpawnConnectionsByPlayer) if playerAddedConnection then playerAddedConnection:Disconnect() playerAddedConnection = nil end for _, player in Players:GetPlayers() do removeIndicatorFromPlayer(player) end end local function addIndicatorToPlayer(player: Player) if player == localPlayer then return end addIndicatorToCharacter(player.Character) addIndicatorWhenCharacterSpawns(player) end local function startSyncingIndicators() for _, player in Players:GetPlayers() do addIndicatorToPlayer(player) end if not playerAddedConnection then playerAddedConnection = Players.PlayerAdded:Connect(addIndicatorToPlayer) end end local function onLocalTeamChanged() local localTeam = localPlayer.Team if localTeam then startSyncingIndicators() else stopSyncingIndicators() end end localPlayer:GetPropertyChangedSignal("Team"):Connect(onLocalTeamChanged) onLocalTeamChanged() ``` > **Success:** Now, whenever a player's is in an active round, team indicators display over other players' heads unless they are on the enemy team and behind an object. ### Tagged out indicator A tagged out indicator is a UI element that informs players when other players are no longer active in the round and are in the process of respawning back to their spawn zone. This information is important because the gameplay of a first-person shooter experience requires players to move onto their next target as soon as they tag out a player so that they don't become vulnerable in the arena by playing in the same location for too long. To exactly recreate the tagged out indicator within the sample [Laser Tag](https://www.roblox.com/games/14817965191/Laser-Tag-1A) experience: 1. Insert a **BillboardGui** object into a temporary rig so that you can visualize the changes in each step of the process. 1. From the toolbar's **Home** or **Avatar** tab, click **Character**. 2. ​Select from the available options. The sample uses a **R15** rig type, a **masculine** body shape, and a **Rthro** avatar. The rig displays both in the 3D viewport and in the **Explorer** window under the name **Rig**. 3. In the **Explorer** window, navigate to the rig's child **Head** mesh, then click the **⊕** icon. A contextual menu displays. 4. From the contextual menu, insert a **BillboardGui**. 2. Select the new **BillboardGui**, then in the **Properties** window, 1. Set **LightInfluence** to `0` to prevent environmental light from affecting the color of the indicator. 2. Set **Name** to **TaggedOutIndicatorGuiPrefab**. 3. Set **Size** to `{3, 0},{0.5, 0}` to widen the space for a label. 4. Set **StudsOffset** to `0, 3.25, 0` to position it above a player's head. 3. Insert an **ImageLabel** object into **TaggedOutIndicatorGuiPrefab**. 4. Select the new label, then in the **Properties** window, 1. Set **AnchorPoint** to `0.5, 0.5` to set the label's origin point in the middle of itself (50% from the left to the right of the label, and 50% from the top to the bottom of the label). 2. Set **BackgroundTransparency** to `1` to make the label's background completely transparent. 3. Set **Name** to **Frame**. 4. Set **Position** to `{0.5, 0},{0.5, 0}` to set the label to the middle of its container (50% from the left to the right of the parent BillboardGui, and 50% from the top to the bottom of the parent BillboardGui). 5. Set **Size** to `{1, 0},{1, 0}` to widen the label to the whole BillboardGui (100% horizontally and 100% vertically of the parent BillboardGui). 6. Set **Image** to `rbxassetid://14304826985` to make the image a multi-directional fade. 7. Set **ImageColor** to `245, 46, 46` to tint the label red. > **Info:** To improve performance, you can skip the image asset and instead use a `Class.UIGradient` object with a custom transparency number sequence. To learn more about this fade technique, see [UI Appearance Modifiers - Gradient](/docs/en-us/ui/appearance-modifiers.md#gradient). 5. Insert a **TextLabel** object into **Frame**. 6. Select the new label, then in the **Properties** window, 1. Set **AnchorPoint** to `0.5, 0.5` to set the label's origin point in the middle of itself (50% from the left to the right of the label, and 50% from the top to the bottom of the label). 2. Set **BackgroundTransparency** to `1` to make the label's background completely transparent. 3. Set **Name** to **BodyTextLabel**. 4. Set **Position** to `{0.5, 0},{0.5, 0}` to set the label to the middle of its container (50% from the left to the right of the parent label, and 50% from the top to the bottom of the parent label). 5. Set **Size** to `{0.85, 0},{0.7, 0}` so the text can take up most of the fade area (85% horizontally and 70% vertically of the parent image label). 6. Set **FontFace** to **Montserrat** to fit the futuristic aesthetic. 7. Set **Weight** to **Bold** to thicken the font. 8. Set **Text** to **TAGGED**. 9. Set **TextColor3** to `255, 255, 255` to make the text white. 10. Enable **TextScaled**. 7. Move **TaggedOutIndicatorGuiPrefab** to **ReplicatedStorage**. 8. Reference the following `Class.ServerScriptService` scripts within the sample [Laser Tag 1A](https://www.roblox.com/games/14817965191/Laser-Tag-1A) place file that programmatically display the tagged out indicator while a player is respawning back to their team's spawn zone. #### SetupHumanoid The following `ServerScriptService.SetupHumanoid` server script runs as soon as a player loads the experience. It ensures that whenever a player's character is added to the data model, `setupHumanoidAsync` is called with their `Class.Humanoid`. ```lua local Players = game:GetService("Players") local setupHumanoidAsync = require(script.setupHumanoidAsync) local function onCharacterAdded(player: Player, character: Model) local humanoid = character:WaitForChild("Humanoid") setupHumanoidAsync(player, humanoid) end local function onPlayerAdded(player: Player) -- Call onCharacterAdded if the player already has a character if player.Character then onCharacterAdded(player, player.Character) end -- Call onCharacterAdded for all future character spawns for this player player.CharacterAdded:Connect(function(character: Model) onCharacterAdded(player, character) end) end -- Call onPlayerAdded for any players already in the game for _, player in Players:GetPlayers() do onPlayerAdded(player) end -- Call onPlayerAdded for all future players Players.PlayerAdded:Connect(onPlayerAdded) ``` #### setupHumanoidAsync The following `SetupHumanoid.setupHumanoidAsync` module script configures settings on the player character's `Class.Humanoid` that ensures that their name and health display during a round, as long as they aren't occluded. In addition, this script listens for the `Died` event in the player character's `Class.Humanoid` when their health reaches zero. Once a player is tagged out from their health reaching zero, the script disables `BreakJointsOnDeath` to prevent the default behavior of the character falling apart, then it runs all logic within `setupHumanoidAsync.onHumanoidDied`. ```lua local onHumanoidDied = require(script.onHumanoidDied) local function setupHumanoidAsync(player: Player, humanoid: Humanoid) -- Give each humanoid full control over its name/health display distance humanoid.DisplayDistanceType = Enum.HumanoidDisplayDistanceType.Subject -- Set name and health display distances to a sufficiently large value to show display name -- and health bar at any practical distance, as long as it is not occluded humanoid.NameDisplayDistance = 1000 humanoid.HealthDisplayDistance = 1000 humanoid.NameOcclusion = Enum.NameOcclusion.OccludeAll humanoid.HealthDisplayType = Enum.HumanoidHealthDisplayType.AlwaysOn -- Prevent character from falling apart when health is depleted humanoid.BreakJointsOnDeath = false humanoid.Died:Wait() onHumanoidDied(player, humanoid) end return setupHumanoidAsync ``` #### onHumanoidDied The following `setupHumanoidAsync.onHumanoidDied` module script handles all of the logic when a player character's `Class.Humanoid` is tagged out from a round. The script starts by changing the `playerState` to`TaggedOut`, which allows other scripts to trigger behavior for the player whose health reached zero, such as displaying the first-person respawn screen. The script then clones and adds the tagged out indicator to the character's `Head` so other players know they are in the process of respawning back to their team's spawn zone. In addition, it adds an `AlignOrientation` instance to the character so that it doesn't fall over due to gravity or other players colliding with the character. When the player respawns, their `playerState` becomes `SelectingBlaster`, automatically disabling the tagged out indicator. `SetupHumanoid`handles calling `setupHumanoidAsync`, then the process starts again. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local PlayerState = require(ReplicatedStorage.PlayerState) local PlayerAttribute = require(ReplicatedStorage.PlayerAttribute) local taggedOutIndicatorGuiPrefab = ReplicatedStorage.Instances.Guis.TaggedOutIndicatorGuiPrefab local function onHumanoidDied(player: Player, humanoid: Humanoid) -- Update player state to be tagged out except when they're in the lobby, -- which happens when they're tagged out right as the round ends if player:GetAttribute(PlayerAttribute.playerState) ~= PlayerState.InLobby then player:SetAttribute(PlayerAttribute.playerState, PlayerState.TaggedOut) end -- Add Tagged Out indicator to character local newIndicator = taggedOutIndicatorGuiPrefab:Clone() local character = humanoid.Parent newIndicator.Parent = character:FindFirstChild("Head") -- Add an AlignOrientation to the character to prevent it from falling over local alignOrientation = Instance.new("AlignOrientation") alignOrientation.RigidityEnabled = true alignOrientation.Mode = Enum.OrientationAlignmentMode.OneAttachment alignOrientation.Attachment0 = humanoid.RootPart.RootAttachment alignOrientation.CFrame = humanoid.RootPart.CFrame alignOrientation.Parent = humanoid.RootPart end return onHumanoidDied ``` > **Success:** Now, whenever a player's health reaches zero, the tagged out indicator displays above their head until they respawn. Congratulations on completing the User Interface Design Curriculum! Now that you have experience creating an art style, wireframing your layouts, and implementing your designs in Studio from start to finish, you can extend your project with new UI and functionality, or follow additional tutorial curricula, such as the [Gameplay Scripting Curriculum](/docs/en-us/tutorials/curriculums/gameplay-scripting.md) that teaches you about the general organization and key implementation details of the sample laser tag experience. Happy creating! --- title: "User interface design curriculum" url: /docs/en-us/tutorials/curriculums/user-interface-design last_updated: 2026-06-29T19:34:13Z description: "Learn best practices for creating user interfaces, including displaying critical information and implementing an art style." --- # User interface design curriculum **User Interface Design** is the discipline of planning and implementing UI elements that either provide essential information for gameplay, or teach players how to complete unique user flows within your experiences. You'll learn how to recreate high-quality UI elements for a first-person shooter laser tag experience by following step-by-step processes and best practices for following an art style, wireframing the structure and flow of UI information, and implementing UI designs within Studio. This course is intended for readers who are familiar with general building concepts and tools in Roblox Studio. If you need help learning how to build an environment and navigating Studio's UI, try the [core curriculum](/docs/en-us/tutorials/core.md). ## Course contents **#### Chapter 1 - Choose an art style** Learn how to [plan an aesthetic direction](/docs/en-us/tutorials/curriculums/choose-an-art-style.md) for your UI elements. **#### Chapter 2 - Wireframe your layouts** Learn how to [design the structure and flow](/docs/en-us/tutorials/curriculums/wireframe-your-layouts.md) of the information you want to communicate through in various workflows. **#### Chapter 3 - Implement designs in Studio** Learn how to [implement your UI layouts](/docs/en-us/tutorials/curriculums/implement-designs-in-studio.md) in Studio using both built-in and custom UI elements with scripts. --- title: "Wireframe your layouts" url: /docs/en-us/tutorials/curriculums/user-interface-design/wireframe-your-layouts last_updated: 2026-06-29T19:34:13Z description: "Explains how to greybox the laser tag environment using basic parts." --- # Wireframe your layouts **Wireframing your layouts** is the process of designing the structure and flow of the information you want to communicate through UI in various workflows. This critical step of the tutorial allows you to iterate on the arrangement of your UI elements, discover pain points, and improve the player's experience before you invest time and resources on a problematic design that doesn't work for your experience. Using the [sample laser tag experience](https://www.roblox.com/games/14817965191/Laser-Tag-1A) `.rbxl` file as a reference, this section of the user interface curriculum shows you how to structure your UI elements on the screen, including guidance on: - Planning a visual hierarchy for your UI that intuitively leads players to features and information they need to be successful in your experience. - Blocking in UI elements with basic shapes to review the composition of your UI elements alongside Roblox's core UI and touch controls. - Developing user flows to account for the necessary actions players to take to achieve their goals. After you complete this section, you will learn how to construct and configure your UI in Studio that respects your art style and wireframes. ## Plan a visual hierarchy A visual hierarchy is an organization structure that highlights UI elements by their order of importance. Planning a visual hierarchy for the different workflows in your experience is important because it sets players up for success in accomplishing their goals, which is especially necessary for the rapid gameplay of first-person shooter experiences in which goals change from moment to moment. Effective visual hierarchies guide players through the features and information they need to meet your gameplay requirements according to where they naturally look on the screen to complete workflows. For example, the visual hierarchy of the sample laser tag experience must guide players through the following categories of UI elements as they navigate the map and tag players on the enemy team: 1. Information about the experience's objective. 2. Information about the blaster. 3. Information about the state of the player. The location each of these categories have on the screen in the final design highlights their order of importance: - The first category is near the **top** because it contains UI elements that have the most significance on how to win the game. - The second category takes up the majority of the screen space near the **middle** because it acts as the focal point to draw players' attention to the action in 3D space, and it has the most significance for playing the game. - The third category is near the **sides** because players can read this peripheral information without diverting their attention from the gameplay. For some experience genres, you only need to display one category of information at a time because it's the only type of information that's important to the player during that workflow. However, for first-person shooter experiences, players need to see UI elements from multiple categories at the same time because they all provide information that the player needs to be successful, such as their team's score, the cooldown period for their blaster, and if they are tagged out of a round. When planning a visual hierarchy for the workflows in your own experience, consider the following: - If you group UI elements from different categories together throughout the screen, players won't know where to look for what they need. To assist players in navigating your experience, **group UI elements from the same category together**. - If you have too many categories on the screen at one time, players may not know what information they need to pay attention to. To avoid overwhelming players with clutter, **display UI elements contextually for different workflows**. - Similarly, if there are too many UI elements per category, it can distract players from understanding on how to complete an action or process. For this reason, it's important to **use your art style to provide visual emphasis for your ideal interaction orders**. For a full review of the visual hierarchy for the sample laser tag experience, see the following table: | Category | UI Elements | Visual Hierarchy | | --- | --- | --- | | Information about the experience's objective | | | | Information about the blaster | | | | Information about the state of the player | | | > **Info:** For more information on prioritizing UI elements on the screen, see [UI & UX Design](/docs/en-us/production/game-design/ui-ux-design.md#prioritization). ## Block in UI elements Now that you have a visual hierarchy in mind, you can block in individual UI elements using basic shapes to review the composition of your custom UI layout alongside Roblox's core UI on multiple devices. This process allows you to see where you need to modify your design so that it's effective on all screen sizes that players can use to access your experience, such as on mobile, tablet, laptop, and TV screens. Before you begin, you need to figure out how much screen space is available for your custom UI elements while factoring in Roblox's core UI. For example, by default, Roblox displays the following UI elements in every experience: - A list of players within the experience. - The character's health bar. - The character's backpack. - A chat window. - A capture button. - A popup menu of character emotes. You can [disable](/docs/en-us/players/disable-ui.md) any of these elements that don't meet the gameplay requirements of your own experience in order to save screen space for your custom UI. For example, the sample laser tag experience doesn't require a backpack because players only have the ability to select a single blaster. However, if players could select multiple blasters at once, the experience could disable this core UI to ensure there's always room for the blaster selector when players respawn, then provide another location to select from your inventory. In addition to Roblox's core UI, you must also consider the necessary screen space for Roblox's default touch controls. For example, when players access your experience using a mobile device, a virtual thumbstick displays in the bottom-left corner of the screen, and a jump button displays in the bottom-right corner of the screen. This ensures that players are always able to navigate your experience, even when they don't have access to a keyboard or controller. By taking into account both Roblox's core UI elements that are necessary for the gameplay of your experience, and the possibility of touch controls, you can block in your custom UI elements in **one design** that's adaptive across devices. This is important because it means you don't need to maintain separate versions of your UI as you update your experience. To demonstrate this method, review the following two images of how the sample laser tag experience blocks in the custom UI elements for both mobile and PC devices. Both images include: - Basic shapes to represent custom UI because it allows the design to easily go through several iterations before achieving the final design. - Grayscale coloring that enables the eye to follow information without the distraction of a colorful background environment - The open state of the chat window and list of active players and chat window to see how much space they take up on the screen. - Space for mobile controls even if they aren't necessary for PC devices. When you design your layouts in this way while thinking about how many UI elements can exist on-screen at the same time, you are future-proofing for all potential layouts according to players' devices and workflows. #### Mobile #### PC When blocking in UI elements for your layouts, consider the following: - Where and how you block in your UI can impact the composition of your layouts. **Aim for balance and symmetry** in the amount and size of your custom UI elements alongside Roblox's core UI. - Interacting with UI that are far from the bottom corners of some mobile and tablet devices is either uncomfortable or impossible. **Place interactive elements in easy-to-reach zones** near the natural resting position for thumbs. - When players navigate through your environment, the 3D space can become distracting from your on-screen UI. **Test your layouts against a variety of possible backgrounds** to ensure your on-screen UI elements remain clear and legible. ## Develop user flows A user flow is a collection of paths that players can take in an experience to complete a task, such as to choose a weapon, purchase an item, or heal a character. User flows typically start where you expect players to begin the task, and finish with the final action or achievement players reach at the end of the task. In addition, effective user flows also account for unusual paths players may want to take on their own to achieve the same goal. The following flow chart displays the user flow of a player entering and playing the sample laser tag experience. When a player opens the experience, they join a lobby. If the round isn't yet in progress, they wait until the round loop starts, otherwise they join a team that's currently playing the game. If the end condition hasn't been met, such as hitting a time limit or tagging out 10 players per team, then players select their blaster and experience the main gameplay of tagging or getting tagged by the enemy team, equipping a new blaster within the arena, or respawning after they get tagged out. Once players meet the end condition, they finish the round and join the lobby again. It's important to develop user flows for everything players can do in your experience because it allows you to assess where and how you want them to complete tasks, and anticipate where you can alleviate the pain points for anyone who follows their own path. For example, if you were to develop additional user flows for the sample laser tag experience, what happens when players: - Quit a round before the end condition is met? - Leave the experience while joining a team? - Receive a phone call in the middle of a round? - Lose their connection to the round for less than 15 seconds? By visualizing all potential actions players can take or scenarios that can happen to players while completing tasks, you can better determine how the layout of your UI elements negatively impacts their experience, then make adjustments before implementing your designs in Studio. If tasks feel intuitive, unobtrusive, and convenient, players are more likely to spend time in your experience, and return at a later date. When developing user flows for your own experience, consider the following: - What you want players to do in your experience and what they want to do may be completely different. It's best to **test your layouts with multiple types of players** to get an understanding of the paths they want to take to achieve their goals. - Flow charts can become difficult to read and ineffective if they try to capture multiple scenarios at once within the diagram. To focus your attention on each task, **limit flow charts to one primary task at a time**. - Interaction patterns vary depending on if players are using touch controls, gamepads, or computer mice to interact with your experience. If you're designing for multiple devices, **develop unique user flows for each device** so you can identify pain points and simplify workflows. After you finalize the design of your wireframes, it's time to move into Studio and make your UI come to life. --- title: "Create your first experience" url: /docs/en-us/tutorials/first-experience last_updated: 2026-06-29T19:34:13Z description: "Explains the process of creating your first experience in Roblox Studio." --- # Create your first experience After you complete the onboarding tour and are familiar with Roblox Studio's user interface, you're ready to start creating experiences on the platform. Using a sample [high-quality catapult](https://create.roblox.com/store/asset/17266332444/IntroToStudioCatapult) asset pack, this tutorial walks you through the creation process for an experience in which players can launch five projectiles toward targets on floating platforms, including guidance on: - Building and organizing a data model for a single place using one of Studio's project templates. - Customizing both primitive and complex 3D objects from the Creator Store with unique properties for your own gameplay requirements. - Organizing and placing scripts in their proper locations so the Roblox Engine can properly simulate and render gameplay. - Playtesting and publishing your creation to a global audience that accesses the platform using a variety of devices. After you complete this process, you can learn how to recreate a simple 3D platformer experience with the [Core curriculum](/docs/en-us/curriculums/core.md), experiment with different use case tutorials, or start making your own projects. ## Create a project A **project** is a collection of assets, settings, and other resources that together represent an experience. All projects start with a single **place** that players load into when they join an experience, but you can create additional places within that same experience to organize assets for different gameplay areas. For example, if you want players to join a dungeon before teleporting to either a vast desert or spooky island, you can organize the assets for each area into their own place. ![An experience grouping of three individual places with unique environments.](../../assets/publishing/experiences-places-assets/Experience-Hierarchy.png) A place's object hierarchy is its **data model**, and it describes everything that compiles that gameplay area, such as the objects that make up the 3D world to objects that control runtime behavior. When you first open a project, the starting place's data model is relatively simple, but it can quickly become more complex depending on the amount of objects, interactivity, or behavior you want to implement in your experience. _Data model at the start of the tutorial_ _Data model at the end of the tutorial_ For your first experience, this tutorial teaches you how to build and organize a data model for a single place using one of Studio's project templates. **Project templates** are useful because they provide a set of default objects in the starting place's data model that you can use to build experiences for different genres. For example, if you want to build a roleplay experience with a sprawling city, you can open the [Modern City](https://www.roblox.com/games/13165709401/Modern-City) template that includes reusable wall, window, and door objects you can snap together to create unique building variations. Similarly, if you want to build a racing experience, you can open the [Racing](https://www.roblox.com/games/16078915506/Racing) template that includes a working race car and winding track object you can restructure in countless configurations. _Modern City Template Tile_ _Racing Template Tile_ To keep things simple, this tutorial instructs you to create a project with the [Baseplate](https://www.roblox.com/games/95206881/Baseplate) template because it includes two default objects that are important for almost every experience on the platform: - **Spawn location** – A `Class.SpawnLocation` object is where player characters appear in the 3D space when they join an experience, as well as where they respawn when their health reaches zero. - **Baseplate** – A baseplate is a floor with a 4x4 grid texture. This template is a great starting point to create your first experience because it provides a neutral world and a floor that aligns with stud measurements that you can use to position and orient 3D objects along a grid. Studs are Studio's base unit of length, and each stud equates to about 28 cm. For more information on Studio's primary units, see [Roblox Units](/docs/en-us/physics/units.md). _Baseplate Template Tile_ To open a project with the Baseplate template: 1. Open **Roblox Studio**. The landing page displays all available project templates. 2. Select the **Baseplate** template tile. Studio opens a new experience with a spawn location and a baseplate.![New Baseplate template open in Studio with the default spawn location highlighted.](../../assets/getting-started/New-Template-With-Spawn-Location.jpg) ## Get asset pack Now that you have a project open, you can add additional 3D objects to the data model aside from the spawn location and baseplate. Studio represents 3D objects as `Class.BasePart` objects that render with physical simulation in the 3D space, and emulate real-world physical behavior like gravity, friction, and force. There are several types of `Class.BasePart` objects, but the most common are: - **Parts** – A `Class.Part` object is Studio's primitive 3D object that comes in five different shapes: ball, block, cylinder, wedge, and corner wedge. - **Meshes** – A `Class.MeshPart` object is a collection of vertices, edges, and faces that make up an object from a third-party modeling application. To help you become familiar with both types of 3D objects, this tutorial provides you an asset pack that includes the following parts and meshes that create the core gameplay for your first experience: _A catapult model of meshes that you can aim left or right from a grounded platform._ _Three ball part projectiles that you can equip to the catapult._ _Two floating platform meshes with block part targets you can knock over with projectiles._ You can find this asset pack on the **Creator Store**, a marketplace that features assets from Roblox and the Roblox community for use within your projects, including model, image, mesh, audio, plugin, video, and font assets. The Creator Store is accessible directly in Studio within the **Toolbox**, as well as on the [Creator Hub](https://create.roblox.com/store/models). For more information about this marketplace, see [Creator Store](/docs/en-us/production/creator-store.md). To insert this tutorial's asset pack from the Creator Store to your Studio inventory, click the **Add to Inventory** link in the following component. Once assets are within your inventory, you can reuse them in any project on the platform. #### Intro to Studio - Catapult & Platforms Create your first experience using this high-quality catapult model. To get this asset pack from your inventory into your experience: 1. From Studio's **Window** menu or **Home** tab toolbar, open the [Toolbox](/docs/en-us/projects/assets/toolbox.md). 2. In the **Toolbox** window, click the **Inventory** tab. The **My Models** sort displays.![Studio's Toolbox window with the Inventory tab highlighted.](../../assets/studio/toolbox/Inventory-Tab.png) 3. Click the **Intro to Studio - Catapult & Platforms** tile. The asset pack displays in your viewport. ## Customize targets When you add a 3D object into your experience, Studio updates the **Explorer** window to display the name of the object and a nest of its children within the `Class.Workspace` service. For example, after you add the catapult model into your viewport, the **Explorer** window displays the **IntrotoStudioCatapult** folder and its child assets alongside the spawn location and baseplate. The **Explorer** window is a fundamental Studio window that represents the data model for the place in your experience that you have open. This means that if you have an experience with multiple places, this window displays different objects depending on the place you're currently working on. When you select an object in the **Explorer** window, Studio updates the **Properties** window to display a selection of properties you can customize for that object without using a script, such as the object's size, color, position, or orientation. To demonstrate this process, this section of the tutorial provides guidance on customizing the visual and behavioral characteristics of the gray targets on the floating platforms. To customize your targets: 1. Select one of the target objects. 1. In the **Explorer** window, click the dropdown arrow to the left of the **IntroToStudioCatapult** folder to display all of its children. 2. Using this process, expand the **TargetPlatforms** folder, the child **TargetPlatform** model for the floating platform closest to the grounded catapult platform, and its **TargetParts** folder to display every part on the floating platform. 3. Click one of the parts. The **viewport** displays an outline around the object to indicate that it is selected, and the **Properties** window updates to display a selection of customizable properties for that specific part. 2. In the **Properties** window, 1. Set **BrickColor** to the color you want to tint the part. 2. Set **Size** to the scale you want the part to have along the X, Y, and Z axes. 3. Set **CFrame.Position** to the location you want the part to have on the platform. 4. Set **CFrame.Orientation** to the rotation you want the part to have along the Y axis. 3. Repeat this process for other parts on the floating platforms. ## Organize scripts While you have a lot of flexibility in how you organize data models within your projects, the Roblox Engine expects certain objects to be in specific **container services** for simulation and rendering functionality to work properly between the server and the client. The **server** refers to a Roblox computer that acts as the ultimate authority for maintaining the experience's state, and it keeps all connected **clients**, or player devices like mobile phones and laptops, in sync with its source of truth. ![A server grouping with connections to three client devices.](../../assets/scripting/client-server/Client-Server-Model.png)_Server with connections to three client devices_ The main categories of container services include: - **Workspace** - Contains objects that render in the 3D world. - **Environment** - Contains objects for environmental settings and elements. - **Replication** - Contains content and logic that replicates between the server and client. - **Server** - Contains server-side only content and logic. - **Client** - Contains client-side content and logic. - **Chat** - Contains objects that enable chat features. Up to this point in the tutorial, you have only interacted with objects in the `Class.Workspace` service of your data model. However, in order for the catapult to operate properly, you must move some of its child script objects to different container services. Roblox offers two types of script objects to contain Luau code that modify object behavior and implement the overall logic of your experience: - `Class.Script|Scripts` - A script that can run on the server or client depending on its set `Class.Script.RunContext|RunContext` property. - `Class.ModuleScript|ModuleScripts` - A reusable script that you can require from both server and client scripts. A `Class.Script` object's `Class.Script.RunContext|RunContext` property determines whether the script runs on the client or server. There are three types of run context: - **Legacy** - The script runs based on its parent container. Legacy is the default run context. - **Server** - The script runs only on the server, regardless of its parent container. - **Client** - The script runs only on the client, regardless of its parent container. It's important to be mindful of where scripts run, otherwise your objects might not behave the way you want them to. For example, if you playtest the experience right now, players cannot equip projectiles to the catapult or launch them at the targets. To ensure that the gameplay works properly, you must move the children within the **ReplicatedStorage** and **ServerScriptService** folders into their respective container services. `Class.ReplicatedStorage` contains objects that are available to both the server and connected clients, making it the best location for the experience's gameplay logic that needs to keep track of how many projectiles a player launches before it displays UI. `Class.ServerScriptService` contains scripting-related assets that are only meant for server use, making it the best location for the experience's gameplay logic that connects projectiles to the catapult, launches projectiles in a particular direction, and resets the catapult to its starting position. To organize folders into their correct container services for the catapult to work properly: 1. In the **Explorer** window, expand the **ReplicatedStorage** and **ServerScriptService** folders. 2. Select all children within the **ReplicatedStorage** folder, then drag them into the **ReplicatedStorage** service. > **Info:** The **UIHandler** `Class.Script` object has a `Class.Script.RunContext|RunContext` property set to **Client**, so it runs client-side only. 3. Select all children within the **ServerScriptService** folder, then drag them into the **ServerScriptService** service. 4. Delete the **ReplicatedStorage** and **ServerScriptService** folders. 1. Select both folders. 2. Press `Delete`. 5. Playtest to verify that the catapult now works properly. 1. Choose **Test** from the dropdown menu and click the **Play** button to its right to begin the playtest.![Test option in the testing modes dropdown of Studio's mezzanine.](../../assets/studio/general/Mezzanine-Testing-Mode-Test.png) 2. Equip the **Ice** projectile to the catapult, aim it toward the nearest floating platform, then launch it at the targets. 3. Equip the **WoodPlanks** projectile to the catapult, aim it toward the farthest floating platform, then launch it at the targets. 4. When you're done playtesting, click the **Stop** button. Studio exits playtest mode.![Stop button indicated in Studio's mezzanine.](../../assets/studio/general/Mezzanine-Testing-Stop.png) ## Customize projectiles While your projectiles are exactly the same size as each other, they travel different distances when you launch them from the catapult. This is because each projectile has a unique **material** that emulates the physical characteristics of its real-world counterpart, including its density, elasticity, and friction. According to Newton's second [law of motion](https://en.wikipedia.org/wiki/Newton%27s_laws_of_motion#Second_law), the acceleration of an object depends on the force acting on the object and the mass of the object itself. As the catapult provides the same amount of force for each launch, each projectile's acceleration changes in proportion to their mass. Projectiles with a small amount of mass accelerate faster than projectiles with a large amount of mass, and projectiles with a large amount of mass accelerate slower than projectiles with a small amount of mass. To see this principle in action, examine the results from your previous playtest. The `WoodPlanks` material is less dense than the `Ice` material, so the wooden projectile is able to accelerate more quickly, and therefore travel a larger distance than the icy projectile from the same launch point. If you were to launch the wooden projectile at the targets closest to the catapult, the projectile would travel above and beyond the platform entirely. Similarly, if you were to launch the icy projectile at the targets furthest from the catapult, the projectile would never be able to travel the distance and make impact with the targets. Roblox Studio is a real-world simulation engine that emulates physical behavior in real time, so it's important to consider how your objects behave differently depending on their physical characteristics. For the final gameplay section of the tutorial, you will experiment with customizing the third projectile with a material that can reach a third floating platform of targets. To reference a list of physical properties for each material, see [Materials - Default Physical Properties](/docs/en-us/parts/materials.md#default-physical-properties). To customize the third projectile: 1. Create a third floating platform of targets. 1. In the **Explorer** window, select a **TargetPlatform** object. 2. Press `Ctrl``D` (`⌘``D`) to duplicate the platform and targets. 3. Use the **Move** tool to move the new platform to a new position. 2. Change the third projectile's material to have the right amount of mass to travel to your third platform's targets. 1. In the **Explorer** window, expand the **ProjectileMaterials** folder, then select **ProjectileC**. 2. In the **Properties** window, set **Material** to the real-world material you want the part to simulate, including its visual and physical characteristics. This material needs the right amount of mass to reach the new platform. 3. Playtest the experience to verify that the projectile makes impact with the targets on the third floating platform. ## Publish experience Roblox not only provides the tooling and engine for you to create and run experiences, it also gives you access to a large social network of players that access the platform on a wide array of devices, including phones, computers, tablets, consoles, and VR hardware. When you're ready to release your experience to this global audience, you must publish and configure the experience's settings so that it's available to all players on any device you want to support. Almost everything in Roblox is represented as a cloud-based asset with a unique corresponding ID. This ID is typically in the form of `rbxassetid://[ID]`, which gets applied to various objects as a property that's appropriate for that particular asset type. When you publish an experience, the experience itself receives a `Class.DataModel.GameID|UniverseID`, and each of its individual places receives a `Class.DataModel.PlaceId|PlaceID`. > **Info:**`Class.DataModel.GameID|UniverseIDs` and `Class.DataModel.PlaceId|PlaceIDs` are useful for managing Roblox resources through Open Cloud APIs, such as automating internal workflows, improving efficiency with content creation and management, and supporting experience operations from the web. For more information, see the [Open Cloud](/docs/en-us/cloud/guides.md) overview. Once this occurs, the **Creator Dashboard** provides you tools and resources to monitor, manage, and perform tasks for your experience and its places, such as: - Monitoring player activity, retention, and growth with [analytics features](/docs/en-us/production/analytics.md). - Automatically translating in-experience content for a global audience that speaks a variety of languages with [localization features](/docs/en-us/production/localization.md). - Configuring in-experience purchases and immersive advertising with [monetization features](/docs/en-us/production/monetization.md). What you learn on the Creator Dashboard can give you important insights into how to further iterate on your project and engage your audience. For example, if you learn that your audience values multiplayer competition, you could create multiple catapults that keep track of how many targets each player knocks over, then republish the experience to make your update instantaneously available on the platform with only one click. To publish your experience for the first time: 1. Upload your experience to the cloud. 1. In the menu bar, click **File**, then select **Publish to Roblox**. 2. In the **Publish Experience** window, fill out all applicable fields. 1. In the **Name** field, provide a name for your experience. 2. In the **Description** field, provide a summary of what a player can expect from the experience. 3. From the **Genre** dropdown menu, select the genre that you want to represent your experience. 4. In the **Devices** section, enable every device you want players to use to access your experience. 3. At the bottom-right of the window, click the **Create** button. Your experience is now in the cloud with a `Class.DataModel.GameID|UniverseID` and a `Class.DataModel.PlaceID|PlaceID` for the starting place. 2. Make the experience public to all players. 1. Navigate to the [Creator Dashboard](https://create.roblox.com/dashboard/creations). The **Creations** landing page displays your first experience. 2. Hover over the experience's tile, click the **⋯** button, and select **Make Public** from the contextual menu. 3. **(Optional)** Share your experience with others. 1. Hover over the experience's tile, click the **⋯** button, and select **Copy URL** from the contextual menu. 2. Share the URL with others as a direct link to your experience's landing page. > **Success:** Congratulations on creating your first Roblox experience! To learn more about building immersive experiences on Roblox, check out [tutorials](/docs/en-us/tutorials.md). --- title: "Coding fundamentals" url: /docs/en-us/tutorials/fundamentals/coding-1/coding-fundamentals last_updated: 2026-06-29T19:34:13Z description: "Teaches the basics of coding with Luau." --- # Coding fundamentals ### Series description Start coding on Roblox with this easy-to-follow series covering the fundamentals of how to use the programming language Luau. These courses are perfect for those new to coding or just starting with Luau. Each course centers around a foundational computer science principle, and features individual lessons with step-by-step tutorials you can use to create your own experiences on Roblox. Before beginning this course, the reader should have basic knowledge of Roblox Studio, as demonstrated in the [Studio](/docs/en-us/tutorials/curriculums/studio.md) and [Building](/docs/en-us/tutorials/curriculums/building.md) lesson. ### Series contents | Series | Description | | --- | --- | | [Variables and objects](/docs/en-us/tutorials/fundamentals/coding-1/landing.md) | Create your first script and start making things happen with code. These tutorials cover variables, data types, and properties. | | [Functions and events](/docs/en-us/tutorials/fundamentals/coding-2/functions-and-events.md) | Use functions to create chunks of code that run only when you tell it. Connect your code to in-world events to create interactive objects like traps or bridges. | | [Conditionals](/docs/en-us/tutorials/fundamentals/coding-3/landing.md) | Conditionals use the coding pattern of "if this happens then do that." Apply this pattern to check if the time is right to unlock power ups for your players. | | [Loops](/docs/en-us/tutorials/fundamentals/coding-4/landing.md) | Learn how to repeat code a certain amount of time or make it run forever using for and while loops. Practice using these loop types to code timed bridges and flashing lights. | | [Dictionaries and arrays](/docs/en-us/tutorials/fundamentals/coding-5/landing.md) | Learn how to store a lot of data in a single variable using tables and arrays. Data structures such as these can store large data sets like a player's inventory. | | [Organize code](/docs/en-us/tutorials/fundamentals/coding-6/landing.md) | Take your code organization a step further and learn about module scripts, a unique type of script that can interact with other scripts in Luau. | --- title: "Create a script" url: /docs/en-us/tutorials/fundamentals/coding-1/create-a-script last_updated: 2026-06-29T19:34:13Z description: "Learn how to create a script in Roblox Studio using Luau. Great for beginners interested in coding or computer science on Roblox." --- # Create a script **Coding** is the process of creating instructions for computers to follow. Just like people use different languages, such as English and Spanish, so do programs. Roblox uses the coding language **Luau**. This article will cover how to start coding in Roblox, introducing common concepts like scripts, data types, and variables. By the end, you'll be able to type out code that displays messages in Roblox Studio. ## Code with scripts In Roblox Studio, lines of Luau code are held in **scripts**. These scripts give the experience sets of instructions on how to give players health points, create a rain of fireballs, or anything else imaginable. ### Create a script Scripts are commonly created in **ServerScriptService**, a special folder made just for holding scripts. 1. In Explorer, hover over **ServerScriptService** to see the **+** button. 2. Click the **+** button and select **Script**. A new script will be created and the script editor will open. 3. Right-click Script and select **Rename**. Name the script _PracticeScript_. Naming scripts helps you and any teammates remember what each script does. ### Hello world New scripts include a print function at the top of the script editor. **Print functions** display text on the screen. It's one of the first functions many people learn, and you'll use it often. This code will make "Hello world!" appear on the screen. ```lua print("Hello world!") ``` To find the script next time you open up Roblox Studio, click on the name of the script above the 3D viewport, or double-click the script's name in Explorer. ### Test output You can see the result of running the default code with the **Output** window. If you've never used it before, you'll need to enable it. 1. From Studio's Window menu, open the [Output](/docs/en-us/studio/output.md) window. 2. To test the script, [initiate a playtest](/docs/en-us/studio/testing-modes.md#playtesting). `Hello world!` will show up in the output. 3. Click **Stop** to end the playtest. You can now return to the **Script** tab. ## Identify data types Coding languages classify different kinds of values into **data types**. For example, one data type is a **number**. Number data types are self-explanatory as they are made up of only numbers. Another data type is a **string**. Strings can hold numbers, letters, and characters. Take another look at the default code in the new script; the words and quotations within the parenthesis is an example of a **string** data type. ```lua print("Hello world!") ``` Strings like `"Hello World"` always sit inside quotation marks, `"like this"`. More examples of strings are below. Notice how they hold a mix of letters and numbers. - `"You just joined the game!"` - `"There are 50 players left"` - `"10"` ## Create variables **Variables** are containers for information the program can use and change, like player names or points. **Declaring** a variable is what coders call the process of creating a new variable. In Luau, to declare a new variable, type `local`, then type the name for the new variable. A variable that can hold a player name might look like: `local playerName` > **Info:** When declaring new variables, some coding languages require that you also state what data type the variable can use. For example, a variable in Java would be `String name = "Pavel"`. Luau only requires a name. In Luau, variables can be global or local. You'll usually use **local** variables. Local variables can only be used within the script or chunk of code where they were created. Global variables can potentially be used by other scripts, but too many global variables can make your experience slow and unresponsive. It's better to stay in the habit of making variables local unless necessary. ### Use variables and strings together Time to declare your own variables. These steps will use a string to store the name of your favorite animal. 1. Delete `print("Hello world!")`. It's best practice not to leave unnecessary code in your scripts. 2. Declare a new variable by first typing `local`, then naming the variable `myAnimal`.```lua local myAnimal ``` > **Warning:** Variable names can't include spaces. Be careful not to include spaces or the code won't work as intended. ### Name variables Variables can be named anything, but good names will always describe their purpose. Generic names make your code difficult to read and update later. Coders will also use different capitalization styles to remind themselves how the variable is used within the script. A good default style is **camelCase**. To write in camelCase: - Begin with a lowercase letter - Leave out spaces - Capitalize additional words Good Variable Names - `playerPoints` - `numberStorageArray` Bad Variable Names - `myVariable` - Doesn't describe the purpose of the variable - `player name` - The included space will cause issues ### Assign values to variables New variables are empty. To **assign** it a value, or put something inside its container, use the `=` symbol. In this case, assign the variable the name of your favorite animal. 1. After the variable name, type `=`.```lua local myAnimal = ``` 2. On the same line, type a string that contains your favorite animal. Remember the quotation marks.```lua local myAnimal = "Porcupines" ``` ### Use print() for your own messages Print functions display text on the screen, as you saw earlier. It's one of the first functions many people learn since it's a simple way of giving the script a command. To see your variable, use the `Global.LuaGlobals.print()` function. 1. On a new line, type `print()`.```lua local myAnimal = "Porcupines" print() ``` 2. Type the name of your variable within the parenthesis.```lua local myAnimal = "Porcupines" print(myAnimal) ``` > **Info:** If you see a red error in the output editor, it's like there's an error in the code. Each error has a unique way of describing itself. Try and use the information from the error to fix your code. 3. Test your code with the play button. You should see the name of your animal in the Output window. ### Combine strings You can display any string in the Output using `print()`; you can even print multiple strings stored within variables or typed directly within the function. **Concatenation** is combining strings. To concatenate the string assigned to your variable and a second string, use two dots `..` The following example concatenates two variables and two typed strings. ```lua local firstAnimal = "porcupines" local secondAnimal = "dolphins" print("I like " .. firstAnimal .. " and " .. secondAnimal) ``` Play around with printing different combinations of strings. ## Summary New scripts can be created by clicking the + button next to the name of an object. ServerScriptService is a common place to create new scripts. New scripts include the default code `print("Hello world!")`. This code will display `Hello world!` in the Output window, where you can see the results of your code and if any errors have occurred. "Hello world!" is an example of a string data type. Strings can include any combination of characters that you might type on your keyboard. Concatenation is when multiple strings are combined. Variables are containers that can be assigned to hold different data types, such as strings and numbers. --- title: "Variables and objects" url: /docs/en-us/tutorials/fundamentals/coding-1/landing last_updated: 2026-06-29T19:34:13Z description: "Learn how to start coding in Roblox Studio in this tutorial series." --- # Variables and objects ### Series description For anyone interested in learning computer science or coding, this serves as a foundational series. This series is recommended for anyone interested in taking other coding lessons in Roblox Studio. ### Objectives and prerequisites | **Learning objectives** | Understand the process of scripting, such as creating scripts and being able to identify interface elements like the Output window. Utilize data types, and variables use strings to print messages to the Output window and change object properties. | | --- | --- | | **Prerequisites** | Basic knowledge of how to create and manipulate parts in Roblox Studio. | ### Series contents | Article | Description | | --- | --- | | [Create a script](/docs/en-us/tutorials/fundamentals/coding-1/create-a-script.md) | Learn how to create new script objects and print messages to the Output window. | | [Object properties](/docs/en-us/tutorials/fundamentals/coding-1/object-properties.md) | Change the color of a part using just code | | [Parents and children](/docs/en-us/tutorials/fundamentals/coding-1/parents-and-children.md) | Instead of calling out a specific part name, learn how to navigate the hierarchy to make code that will work for any part | --- title: "Object properties" url: /docs/en-us/tutorials/fundamentals/coding-1/object-properties last_updated: 2026-06-29T19:34:13Z description: "Learn how to use Luau to change the appearance properties of different objects, such as color and shape." --- # Object properties **Properties** control how objects look and function. Each object in Roblox Studio has its own set of properties. For example, a part object has color, size, and shape properties. Properties can be changed in the Properties window or through code. To learn about properties, you'll explore common properties found in parts and then write a script to change part colors. ## The Properties window The **Properties** window can be used to learn about an object's properties. Use it to take a look at a part's properties. 1. Select a part. 2. In the **Properties** Window on the bottom-right, look at the different properties that can be changed, like color, size, material and transparency. You can also change most properties in this window from within a script. ## Add comments to scripts Comments are special lines starting with `--` that help coders remember what parts of scripts do. Unlike other code, comments don't run; they're just there so you can leave notes to yourself and other programmers. This script will change a part's `Class.BasePart.Color|Color` property at the start of the experience. 1. Select an existing part or create a new one. Rename the part PracticePart. 2. In **ServerScriptService**, create a new script named ChangeColor. 3. In the script, delete the default code. Then, write a **comment** by typing `--` and a brief description of what the script will do.```lua -- Changes the color of a part ``` ### Locate the part To make changes to a part, you must be able to describe the part's location. The Explorer is an excellent tool for **referencing** locations. In this case, **PracticePart** is under **Workspace**. Now that you know where the part is, the part's location needs to be translated into something a script can understand. 1. Under the comment, type `workspace.PracticePart`.```lua -- Changes the color of a part workspace.PracticePart ``` ### Change a property with code You'll use an RGB value to change the part's color. Computers use **RGB values**, the combination of red, green, and blue, to create all the colors on your screen. RGB values use three numbers from 0 to 255, separated by commas. For example, black is `(0, 0, 0)` while white is `(255, 255, 255)`. For the part, the script will change its Color property to a new Color3, a data type that stores colors. 1. After `PracticePart`, type `.Color` to access the Color property.```lua -- Changes the color of a part workspace.PracticePart.Color ``` > **Info:** Roblox will autocomplete words as you type to help speed up the coding process. You can use the arrow keys to move down the list when the words appear. Pick an option by pressing `Enter`. 2. Next, type `= Color3.fromRGB()` This code will allow you to assign a new color.```lua -- Changes the color of a part workspace.PracticePart.Color = Color3.fromRGB() ``` 3. RGB color values can be manually typed inside the parentheses, but using the color picker is easier. Click **inside** the parentheses, and then click the color wheel. Follow the popup to create a color. Your code should look similar to the code below.```lua -- Changes the color of a part workspace.PracticePart.Color = Color3.fromRGB(255, 230, 50) ``` > **Info:** If you want to type an RGB value manually, make sure each number is between 0 to 255 and separated by commas, such as (10, 50, 10). 4. [Initiate a playtest](/docs/en-us/studio/testing-modes.md#playtesting) to test that your part changes color. ## Summary All objects have properties. Parts have properties like color and transparency. At the same time, other object types have their unique properties. To change the color of a part, you need to be able to describe where to find it. If the part is in Workspace, use the keyword `workspace`. Then use dot operators to state the desired part and access its properties. --- title: "Parents and children" url: /docs/en-us/tutorials/fundamentals/coding-1/parents-and-children last_updated: 2026-06-29T19:34:13Z description: "Practice navigating object hierarchies instead of using specific names in your code." --- # Parents and children Instead of running scripts from ServerScriptService, you may want to attach a script to the part. For instance, using a script to turn a part into a trap or make a part give the player points when they touch it. By understanding how scripts and parts are related in the hierarchy, you can create a script that works automatically without naming which part to use. To do this, you must understand **parent and child relationships**. Parents and children are ways to describe the hierarchy between different objects. Anytime you've added a new part to Workspace, Workspace has been the parent object, and the part became a child object. When you added a script to ServerScriptService, ServerScriptService was the parent, and the script was a new child. ## Set up the project and script To demonstrate parent and child relationships, you'll start with a single part that changes colors and then duplicate the script across multiple parts. 1. Create a new part and rename it. This lesson will use ColorPart. 2. Right-click the **part** and select **Insert Object** ⟩ **New Script**. Rename the script **ColorChanger**. 3. Copy and paste the code below into **ColorChanger**. This version of the code is the same that you used before. It assigns a specific part to a variable.```lua local Workspace = game:GetService("Workspace") local colorPart = Workspace.ColorPart colorPart.Color = Color3.fromRGB(50, 240, 255) ``` 4. Test and check that the part changes color as desired. ## Parent and child relationships A **parent** is anything with objects, like scripts or parts, attached below it. Anything under the parent is its **children**. In the example below, ColorPart is the parent, and ColorChanger is the child. With the current script, you can only change the color of a **single part** named ColorPart. To change any part's color, you can design the code to work on the script's parent object, whatever it happens to be named. The code `script.Parent`, will go up the hierarchy and find the object the script is attached to. ### Use script.Parent `script.Parent` can be assigned to a variable like any other value with the `=` symbol. 1. In the script, replace the named part with `script.Parent`. Check your code below.```lua local colorPart = script.Parent colorPart.Color = Color3.fromRGB(50, 240, 255) ``` ### Test reusable parts Now that the variable will refer to whatever part the script is attached to, you can make as many copies as you want. 1. Right-click on the color part and select Duplicate, or use the hot-key `Ctrl``D` (`⌘``D`). Create at least three total parts. 2. Run the experience to check that all parts change color. ## Summary Instead of always directly referencing parts by name and location, script.Parent can be used as an alternative. That code will grab the parent, or object, that the script is attached to, thus letting the script make modifications to it. Using code such as `script.Parent` will come in handy to make your code reusable. One drawback to remember is that you now have multiple scripts that require updating if you want to change the code. --- title: "Code a function" url: /docs/en-us/tutorials/fundamentals/coding-2/code-a-function last_updated: 2026-06-29T19:34:13Z description: "What a function is, and how to code a function in Luau" --- # Code a function **Functions** are sets of instructions that can be used many times in a script. There are pre-made functions (also called methods) like `print()` and `Library.task.wait()` that are built into most programming languages. Coders can also create their own custom functions for code they want to use more than once, but don't feel like writing over and over. In Luau, this is done by typing: `local function nameOfTheFunction()` Functions can be thought of like a recipe. They're both lists of specific instructions. For example, you could use a function to teach a robot how to make spaghetti. First, make a `local function` that lists the steps to prep the meal. Then, whenever you want dinner, just say, "Robot, run makeSpaghetti()". The steps to make dinner might look like this: 1. Boil water 2. Add pasta 3. Cook 15 minutes 4. Add sauce 5. Serve on a plate While the code version might look like something like this: ```lua local function makeDinner() -- List of instructions end -- Tell the code to run makeDinner() ``` ## Create a function To practice, your first function will print your favorite food in the Output window. First set up the script you're going to use, if you don't remember how, here's a reminder: 1. Create a new script in **ServerScriptService**. 2. Rename the script _FunctionsPractice_. 3. Delete `"Hello World"`. 4. Add comment at the top. For example: `-- Prints your favorite food` Next, you create and name the function. All functions should have names that help you remember what their purpose is. Functions names are camelCased, with the first letter lowercase and next words capitalized. Examples: `addPoints()`, `restartGame()`, `rainFlamingLlamas()` - Type `local function printFood()`, then press `Enter` on your keyboard. It should autocomplete the function to look like: ```lua local function printFood() end ``` > **Warning:** Type `end` yourself if the code doesn't autocomplete for some reason. If the function doesn't have an `end`, your code won't run. ### Add code to functions All of the code for your function has to be typed between the lines `local function printFood()` and `end`. Any code not between those two points won't run when the function does. ```lua local function printFood() -- Code here runs when the function is called end -- Code here will not run ``` Information that is inside the function, or can be used by the function, is in **scope**. - Between `local function printFood()` and `end`, use `print()` to state your favorite food. ```lua local function printFood() print("Curry!") end ``` > **Success:** As you typed, your code should have indented automatically. This makes it easier for you and others to see the start and end of a function. If it didn't auto indent, you can press `Tab` at the beginning of the line to quickly indent it. ### Tell the function to run There's one more thing we need to do before the function will work, and that's to tell it to run, also known as **calling** a function. Functions won't run until they are called. To call a function, type the function's name including the `()`. 1. Under `end`, type `printFood()`.```lua local function printFood() print("Curry!") end printFood() ``` 2. Test the code. Your favorite food should appear in the [Output](/docs/en-us/studio/output.md) window. ### Troubleshooting tips If your code doesn't run, check for errors such as the following: - Check that `print("Your Food Here")` is between `local function printFood()` and `end`. - Make sure that your food is a string, with quotations on `"each side"`. - Check that the function is called after it's been created. `printFood()` should be below `end`. --- title: "Functions and events" url: /docs/en-us/tutorials/fundamentals/coding-2/functions-and-events last_updated: 2026-06-29T19:34:13Z description: "Landing page for chapter on coding functions in Luau and using events on Roblox." --- # Functions and events ### Series description Use functions and events to make code run when you want, as often as you want. With functions and events you can set up traps, buttons, health pickups, and more. Covered in this course are functions, parameters, arguments, and returning values. ### Objectives and prerequisites | **Learning Objectives** | Learn to place code into reusable chunks called functions. Understand what it means for code to be "in scope". Set up events within an experience and use them to tell the functions when to run. Practice using functions and events to create traps and buttons. | | --- | --- | | **Prerequisites** | Basic knowledge of Roblox Studio such as moving and rotating parts. Previous experience creating Script objects and using variables, properties, and strings. | ### Series Contents | Article | Description | | --- | --- | | [Code a function](/docs/en-us/tutorials/fundamentals/coding-2/code-a-function.md) | Set up chunks of code in a way that only runs when you tell it to run. | | [Use parameters and events](/docs/en-us/tutorials/fundamentals/coding-2/use-parameters-and-events.md) | How to code a function when you don't have all of the information yet. | | [Parameters practice - buttons](/docs/en-us/tutorials/fundamentals/coding-2/parameters-practice-buttons.md) | Practice using events and parameters by making a bridge appear in-world with the push of a button. | | [Multiple parameters and arguments](/docs/en-us/tutorials/fundamentals/coding-2/multiple-parameters-and-arguments.md) | Pass multiple pieces of information into a function at once. | --- title: "Multiple parameters and arguments" url: /docs/en-us/tutorials/fundamentals/coding-2/multiple-parameters-and-arguments last_updated: 2026-06-29T19:34:13Z description: "Pass multiple pieces of information into a function in Luau using arguments and parameters." --- # Multiple parameters and arguments Functions can actually have more than one parameter, allowing for multiple arguments (pieces of information), to be passed into the function. An example might be assigning a player to a team. Here you would need a parameter to pass in the player, and a parameter to pass in which team to assign them to. To set up multiple parameters, separate the parameter names with a comma. ```lua local function assignTeam(playerName, whichTeam) end ``` ## Practice with multiple parameters To practice working with multiple parameters, create a function that sorts objects into trash or recycling. - The **first** parameter will be trash, which will be destroyed. - The **second** parameter will be for recycling, which will be made shiny and new again. ### Set up the function First set up the function with multiple parameters and then test your work. 1. Create a new script in **ServerScriptService**. 2. Code a new function named `sortGarbage()` with two parameters, `trash` and `recyclable`. 3. At the bottom of the script, call the function. 4. Quickly check the code so far using a print statement inside the function. ```lua local function sortGarbage(trash, recyclable) print("garbage test") end sortGarbage() ``` ### Use multiple parameters Inside the function, parameters can be used similar to variables. In this example, `trash` is destroyed and then `recyclable` is given sparkles using a Particle Emitter. 1. Delete the print statement, and use Instance.new() to create a new Particle Emitter as shown. This will be used to give recyclables their sparkle.```lua local function sortGarbage(trash,recyclable) -- Create a new Particle Emitter local sparkle = Instance.new("ParticleEmitter") end sortGarbage() ``` > **Success:**`Datatype.Instance.new()` can also be used to create objects such as Fire, folders, and parts. 2. Destroy whatever is passed in through the trash parameter the same as if trash were any other variable.```lua local function sortGarbage(trash,recyclable) local sparkle = Instance.new("ParticleEmitter") -- Destroy the trash trash:Destroy() end sortGarbage() ``` 3. Parent the Particle Emitter to the recyclable.```lua local function sortGarbage(trash,recyclable) local sparkle = Instance.new("ParticleEmitter") -- Destroy the trash and make recyclables shiny trash:Destroy() sparkle.Parent = recyclable end sortGarbage() ``` ## Pass values through parameters Time to take out the trash! With the parameters set up, the functions are ready to take in information. 1. Create two objects using basic parts to represent things you might throw away. One should be trash and one recyclable. Make sure they have unique names. 2. At the top of the script, add variables for the objects you created. These will be your arguments.```lua local Workspace = game:GetService("Workspace") local cementBlock = Workspace.Cement local glassBottle = Workspace.GlassBottle ``` 3. At the bottom of the script, where `sortGarbage()` is called, pass in each argument.```lua local Workspace = game:GetService("Workspace") -- Objects to use as arguments local cementBlock = Workspace.CementBlock local glassBottle = Workspace.GlassBottle --Destroys trash and cleans up recyclables local function sortGarbage(trash,recyclable) local sparkle = Instance.new("ParticleEmitter") trash:Destroy() sparkle.Parent = recyclable end -- Pass in the arguments sortGarbage(cementBlock, glassBottle) ``` 4. Test your code. The object passed in first will be destroyed. The second object will sparkle. ## Order of parameters Typically, arguments are matched to parameters based on the order they're passed in. For instance, the first argument is used for the first parameter. The second argument is used for the second. In our example, there are two parameters. Whatever gets passed in first will always be trash, and the second will always be recyclable. ```lua --Destroys trash and cleans up recyclables local function sortGarbage(trash,recyclable) local sparkle = Instance.new("ParticleEmitter") trash:Destroy() sparkle.Parent = recyclable end --Goes in order. Cement is destroyed and the glass bottle is recycled sortGarbage(cementBlock, glassBottle) --GlassBottle is destroyed and cementBlock is recycled sortGarbage(glassBottle, cementBlock) ``` If you were to only pass in only one argument, that argument would be treated as trash. If you were to pass in three arguments, nothing would happen to the third argument because there is no third parameter. ## Summary Parameters are placeholders through which values can be passed into functions. Arguments are the values that get passed through the placeholder. You can create multiple parameters by separating their names with commas. When calling functions, the order that values get passed into the receiving function determines which parameter they're used. --- title: "Parameters practice - buttons" url: /docs/en-us/tutorials/fundamentals/coding-2/parameters-practice-buttons last_updated: 2026-06-29T19:34:13Z description: "Practice connecting functions to events by creating a button that activates a bridge." --- # Parameters practice - buttons Buttons can be used by players to unlock new areas, give points, and reveal treasure. Whenever a player touches a button, it should give some feedback to let players know it's being interacted with, like change colors or make sounds. In this example, the button will be used to activate a bridge. When the button has been pressed, it'll turn green and players will be able to use the bridge. If the player hasn't pressed the button, they'll fall through the bridge when they try to walk on it. ## Make a bridge Start off by setting up the bridge. 1. Create an area in your environment where players will need a bridge. 2. Create a part for the bridge and rename it Bridge. 3. Select the bridge, in the Properties window: - Change **Transparency** to .5. For Transparency, 0 is visible, 1 is invisible. - Check **Anchor**. - Uncheck **CanCollide**. 4. When you test the experience, the bridge should be misty looking and people shouldn't be able to walk on it yet. ## Create the button Now that the bridge is set up, create the button. 1. Create a new part named _Button_. 2. Change the button color to red. 3. **Anchor** the button. 4. Move the button so it's slightly floating and not touching anything. This is so to make sure the `Touched` event doesn't accidentally fire. ## Make the button interactive This time, instead of using the `Class.BasePart.Touched|Touched` event to create a trap, you'll use it to create a button that makes the bridge usable. To make the bridge collidable, use the code `bridge.CanCollide = true` within a custom function that runs when a player touches the button. You know everything else you need to complete the following steps. Try to figure it out yourself before looking at the code solution. 1. Insert a new **script** into the button named _ActivateBridge_. 2. Delete `Hello World`. 3. Create variables for the bridge and for the button. 4. Create a **local function** that does the following the button is touched: - Prints `"button touched"`. - Changes button's color from red to green when touched. - Changes bridge's transparency to 0 to make it visible. - Makes the bridge usable by using the code `bridge.CanCollide = true` 5. Connect the function to the button's `Touched` event. 6. Test the script and make sure the bridge appears when the button is touched. Code Solution Below is one possible code solution ```lua -- Insert this script into button -- Turns the button green when something touches the button. local button = script.Parent local bridge = workspace.Bridge local function buttonPressed() print("button touched") button.Color = Color3.fromRGB(0, 170, 0) bridge.Transparency = 0 bridge.CanCollide = true end button.Touched:Connect(buttonPressed) ``` ### Troubleshoot your code **Issue: The bridge is already solid when the experience starts.** Make sure that the parts are Anchored and not touching anything. The parts might touch something, like terrain or another part, and cause the buttonPressed() function to fire accidentally. **Issue: You get an error in the Output window saying: "Bridge is not a valid member of Workspace"** Check the following: - The naming of your bridge. The bridge in your script must be named exactly like in the Explorer. - That `part.Touched:Connect(buttonPressed)` is outside the `buttonPressed()` function. ### Optional code challenge The script in this lesson can also be used to keep doors that keep players out of specific areas. Practice your coding skills and do the following: - Create a door part. - Create a button further away from the door. - Change the script in this lesson so it makes the door change transparency and so the player can't collide with it. Your version might look something like this: ```lua local button = script.Parent -- Reminder: Replace the name of the part, Gate, with the one in your project local gate = workspace.Gate local function buttonPressed() print("button touched") -- Changes button to red to give player feedback button.BrickColor = BrickColor.Red() -- CHANGE THIS -- Make the gate invisible gate.Transparency = 1 -- Makes it so the player can walk through the gate gate.CanCollide = false end button.Touched:Connect(buttonPressed) ``` --- title: "Use parameters and events" url: /docs/en-us/tutorials/fundamentals/coding-2/use-parameters-and-events last_updated: 2026-06-29T19:34:13Z description: "How to pass information into functions using parameters and arguments. Also covers connecting functions to events." --- # Use parameters and events Normally, functions can only use the information they have coded within them. Sometimes however, you might not know in advance what that information will be, or you want to be able to reuse the same function with multiple pieces of similar information. For example, if you wanted to use a function to display the name of the person who finished an obby course the fastest in giant letters to everyone. You won't know the name of the future winner until the race is finished. **Parameters** are placeholders for information you want to give to the function at a later time. They're like windows that allow you to **pass** information to the function. ## Use parameters and events to set up a trap This script will create a trap part that destroys whatever touches the part, including other parts. You'll have to use a parameter to set it up. Be careful to anchor the trap where it doesn't fall and destroy things unintentionally. ### Create a new part A part needs to be set up that will destroy anything that touches it. 1. Create a new part that's not touching anything. If it's touching something it might go off too soon. 2. In the Explorer, rename the part:TrapPart. 3. **Anchor** the part. ### Set up the script Use what you know about variables and the experience hierarchy to reference the trap part. 1. Add a new script into the trap part. Rename the script _TrapScript_. 2. Delete Hello World and add a descriptive comment. 3. Under the comment, create a new variable which finds the script's parent.```lua -- Destroys whatever touches the parent local trap = script.Parent ``` ### Create a function with a parameter The trap will use a function to destroy whatever touched the part. To work, the function needs to know what touched the part. And that means using parameters. Parameters are typed inside the `()` that comes after a function's name. They look like this: ```lua local function functionName(parameterName) end ``` The actual information that gets passed through the parameter is called an **argument**. You'll create a new function called `onTouch()` with a parameter called `objectTouched` that will track whatever touches the trap and then destroy the touching part. 1. Create a local function. It can be named anything, but this lesson will use `onTouch````lua local trap = script.Parent local function onTouch() end ``` 2. Inside the `()`, type a name for the parameter. This lesson will use objectTouched.```lua local trap = script.Parent local function onTouch(objectTouched) end ``` 3. Between `local function onTouch()` and `end`, create a print statement. You'll use this to check if something is touching the part in the next section.```lua local trap = script.Parent local function onTouch(objectTouched) print("Something touched the trap") end ``` ### Use an event to call the function We want the function to run whenever something touches the part. To make that happen, connect the function to the `Touched` event. **Events** are things that happen in the experience. Like a player touching a part or losing health. When a function is connected to an event, the function runs whenever the event happens. The `Touched` event fires whenever one part touches another part and can be used to create buttons, traps, and other objects that players interact with. 1. Beneath the function's `end`, type `trap.Touched:Connect(onTouch)````lua local trap = script.Parent local function onTouch(objectTouched) print("Something touched the trap") end -- Connect the function to the Touched event trap.Touched:Connect(onTouch) ``` 2. Click Test and then touch the part. Check for your test print statement: `Something touched the trap`. If you can't see the string in the Output window, check the following: - Make sure the string "Something touched the part!" is between local function onTouch() and end. - Check that Touched is capitalized. 3. Now that the function is correctly set up, use it to destroy whatever touches the part. Inside the function, after the string, type `objectTouched:Destroy()` The completed code should be:```lua local trap = script.Parent local function onTouch(objectTouched) print("Something touched the trap") -- Destroy the touching object objectTouched:Destroy() end trap.Touched:Connect(onTouch) ``` 4. Test again and see what happens when the part is touched. Your avatar should end up missing feet or arms. If the part touches the avatar's head or neck, it might even be completely destroyed. ## Summary Parameters are used to pass information that would be normally out of scope (where a function can't see it), into a function. Meanwhile, events are special signals that go off inside of the experience when something important happens. Different objects have different events. Functions can be connected to events so that they are called each time the event gets fired. This little script connected a function to the trap's `Class.BasePart.Touched|Touched` event, and passed in what was touching the trap through the parameter. --- title: "Else/if practice with giving points" url: /docs/en-us/tutorials/fundamentals/coding-3/give-points last_updated: 2026-06-29T19:34:14Z description: "Create a part that gives players points using if and else statements in Luau." --- # Else/if practice with giving points This project will use conditional statements to create a part that will give or subtract points on a leaderboard depending on what color the part is when touched. If blue, then it'll give players a few points. If green, then it'll give a lot of points. Finally, if red, then it'll take away points. ## Set up the project The point giving part can be added into any project where points are relevant. For instance, an adventure game where players collect points. ### Point tracking To setup this project, you'll need a leaderboard to track the points and a part that changes colors. Code for the leaderboard will be provided. 1. Create a new script in **ServerScriptService** named Leaderboard. Copy and paste the code below into the script.```lua --In ServerScriptService, create a script named PlayerSetup with the contents below. local Players = game:GetService("Players") local function onPlayerJoin(player) local leaderstats = Instance.new("Folder") leaderstats.Name = "leaderstats" leaderstats.Parent = player -- Example of an IntValue local points = Instance.new("IntValue") points.Name = "Points" points.Value = 0 points.Parent = leaderstats end -- Run onPlayerJoin when the PlayerAdded event fires Players.PlayerAdded:Connect(onPlayerJoin) ``` ### Color changing part The script will cycle through three different colors for the part. Each color will have a variable to store it's RGB value, a data type that includes a set of three numbers (red, green, blue) that create colors. 1. Create a part named PointPart with an attached script named PointScript. 2. In PointScript, use `script.Parent` to refer to the part.```lua local pointPart = script.Parent ``` 3. Create variables to store the different colors. Each variable should be set to `Datatype.Color3.fromRGB()`, which creates a color value. - **Blue** (Some Points): (0, 0, 255) - **Green** (Many Points): (0, 255, 0) - **Red** (Lose Points): (255, 0, 0)```lua local pointPart = script.Parent -- Colors local blue = Color3.fromRGB(0, 0, 255) local green = Color3.fromRGB(0, 255, 0) local red = Color3.fromRGB(255, 0, 0) ``` 4. Add variables for a small amount of points, a larger amount of points, and a third for removing points.```lua -- Colors local blue = Color3.fromRGB(0, 0, 255) local green = Color3.fromRGB(0, 255, 0) local red = Color3.fromRGB(255, 0, 0) -- Points values local smallPoints = 10 local largePoints = 50 local losePoints = 100 ``` ### Add the players service To award points, you'll need to get access to the player's information which is stored in the Explorer under Players, and is separate from the character object. This is where information like leaderboard stats can be found. You can do so by adding the **Players** service to your script. **Services** are additional sets of pre-built functions made by Roblox engineers to save you time. 1. Get the **Players** service by typing:`local Players = game:GetService("Players")````lua -- Points values local smallPoints = 10 local largePoints = 50 local losePoints = 100 -- Services needed local Players = game:GetService("Players") ``` ### Functions and events PointsScript will need two functions. The first function will give and subtract parts. The second function will check if a player has touched the part. These functions will then be connected with a touch event, which runs whenever that part is touched. 1. Create a new function named `givePoints()` and a parameter named `player`. Inside, add a print statement to use for testing.```lua local Players = game:GetService("Players") -- Gives or subtracts points local function givePoints(player) print("Giving player points") end ``` 2. Under that, create a second function named `partTouched()` with a parameter named `otherPart`.```lua -- Gives or subtracts points local function givePoints(player) print("Giving player points") end -- Checks if player touched the part local function partTouched(otherPart) end ``` 3. Inside the function, use the function `GetPlayerFromCharacter()` to check if there's a player in the otherPart variable.```lua -- Checks if player touched the part local function partTouched(otherPart) local player = Players:GetPlayerFromCharacter(otherPart.Parent) end ``` 4. If a player touched the part, it'll be stored inside the player variable. If not, the variable will stay empty. On your own: - Inside the function, check if player has a value. If there is, then call `givePoints(player)`. - Beneath the function, connect `partTouched()` to the Touched event of `pointPart`.```lua -- Checks if player touched the part local function partTouched(otherPart) -- Gets the player if one touched the part local player = Players:GetPlayerFromCharacter(otherPart.Parent) if player then givePoints(player) end end pointPart.Touched:Connect(partTouched) ``` 5. **Run** the project. Whenever a player touches the part, you should see a message in the Output window saying: `"Giving player points"` **Troubleshooting Tips:** - Check that `"Players"` in `game:GetService("Players")` is capitalized and in quotations. - `partTouched()` should be connected to pointPart's `Touched` event. ## Create Looping Colors To loop through colors, the script will use a while =loop that changes the part's color every few seconds. The condition for this loop will be true, so it can run indefinitely. 1. At the end of the script, create a new while loop where the condition is true, meaning the loop always runs.```lua -- Checks if player touched the part local function partTouched(otherPart) -- Gets the player if one touched the part local player = Players:GetPlayerFromCharacter(otherPart.Parent) if player then givePoints(player) end end pointPart.Touched:Connect(partTouched) -- Loops through colors while true do end ``` > **Info:** If the while true do loop is not at the bottom of the script, any code below it will never be run. Since the while loop doesn't stop, it'll keep running the loop instead of any code below it. 2. On your own, code a while true do loop that changes pointPart to the color variables you've created. Don't forget to use `Library.task.wait()` between colors. When finished, check your code against the version below.```lua -- Loops through 3 colors, waiting between each color while true do pointPart.Color = blue task.wait(3) pointPart.Color = green task.wait(2) pointPart.Color = red task.wait(1) end ``` 3. Play-test and check that all three colors loop without stopping. ### Troubleshooting tips At this point, if the color looping doesn't work as intended, try one of the following below. - Check that the while loop is at the **bottom** of the script, below the Touched event. If the loop is not at the bottom, it'll keep other parts of the script from running correctly. - Check that each color inside `Datatype.Color3.fromRGB()` is correctly written. There must be three numbers between 0 and 255 separated by commas, like `(255, 50, 0)`. ## Give players points Players will be given points based on the current color of the part when they touch it. ### Find the current color Whenever a player touches the part, the script will need to know the current color of the part to award points later. 1. Find `givePoints()`. Replace your testing message with a variable for the current color of pointPart. This variable will determine how many points the player gets (or loses).```lua local function givePoints(player) local currentColor = pointPart.Color end ``` 2. To affect a player's points, that function needs access to the player's leaderboard. Create a variable to store it.```lua local function givePoints(player) local currentColor = pointPart.Color local playerStats = player:WaitForChild("leaderstats") end ``` 3. Now add a variable to get the player's Points value, which is a child of their leaderboard.```lua local function givePoints(player) local currentColor = pointPart.Color local playerStats = player:WaitForChild("leaderstats") local playerPoints = playerStats:WaitForChild("Points") end ``` ### Give or subtract points Next, you'll use if and elseif to give or subtract points depending on the color of the part when touched. Remember that blue provides a small amount, green provides many, and red subtracts points. 1. Inside `givePoints()`, beneath the variables, use an if statement to check if the current color is blue and if so then add `smallPoints` to the player's current points value.```lua local function givePoints(player) local currentColor = pointPart.Color local playerStats = player:WaitForChild("leaderstats") local playerPoints = playerStats:WaitForChild("Points") if currentColor == blue then playerPoints.Value += smallPoints end end ``` 2. To check for green, add an else if condition. If green, then add the `largePoints` variable to the player's points.```lua if currentColor == blue then playerPoints.Value += smallPoints elseif currentColor == green then playerPoints.Value += largePoints end ``` 3. Use an else statement to subtract points if pointsPart was neither blue nor green.```lua if currentColor == blue then playerPoints.Value += smallPoints elseif currentColor == green then playerPoints.Value += largePoints else playerPoints.Value -= losePoints end ``` 4. Lastly, **destroy** the part after the if statement so that the script can't keep giving out points.```lua if currentColor == blue then playerPoints.Value += smallPoints elseif currentColor == green then playerPoints.Value += largePoints else playerPoints.Value -= losePoints end pointPart:Destroy() ``` 5. Playtest and check that each color gives points as expected. Be sure to test all **three** conditions. ## Give players feedback The PointPart works, but players might not notice something happened unless they happen to be looking at their leaderboard. Fix that by creating particles when the PointPart is destroyed. Adding feedback when players use a part, like sounds, shakes, or particles, makes interactions with objects more satisfying to players. ### Create a particle effect The particle effect will be the same color as the part when touched. Since the colors were stored in variables, it's easy to reuse them. 1. In `givePoints()` at the bottom, create a new **ParticleEmitter** instance. Make sure the instance name is spelled exactly as shown.```lua local particle = Instance.new("ParticleEmitter") end ``` 2. ParticleEmitters use color sequences to control their Color property. Create a new ColorSequence and pass in the current part color.```lua -- Destroy part pointPart:Destroy() -- Create particles local particle = Instance.new("ParticleEmitter") particle.Color = ColorSequence.new(currentColor) ``` 3. The particle will need to be parented to player that touched it. Create a variable to get the player's Character model. Then, parent the particle to the player's head.```lua local particle = Instance.new("ParticleEmitter") particle.Color = ColorSequence.new(currentColor) local playerCharacter = player.Character particle.Parent = playerCharacter:WaitForChild("Head") ``` 4. Use `Library.task.wait()` for a quick second, then destroy the particles.```lua local particle = Instance.new("ParticleEmitter") particle.Color = ColorSequence.new(currentColor) local playerCharacter = player.Character particle.Parent = playerCharacter:WaitForChild("Head") task.wait(1) particle:Destroy() ``` 5. Playtest the game and make sure particles briefly follow the player after touching each color. ### Troubleshooting tips At this point, if particles doesn't work as intended, try one of the following below. - Make when creating a new instance that ParticleEmitter is spelled **exactly** as shown and inside quotations. - When parenting the particles, make sure to use `:` between `playerCharacter` and `WaitForChild()` with no spaces between. ## Complete PointScript A finished version of the script can be referenced below. ```lua local pointPart = script.Parent --local storage = game:GetService("ServerStorage") -- Gives some points local blue = Color3.fromRGB(0, 0, 255) -- Gives more points local green = Color3.fromRGB(0, 255, 0) -- Makes players lose points local red = Color3.fromRGB(255, 0, 0) -- gold given to players local smallPoints = 10 local largePoints = 50 local losePoints = 100 local Players = game:GetService("Players") local function givePoints(player) local currentColor = pointPart.Color local playerStats = player:WaitForChild("leaderstats") local playerPoints = playerStats:WaitForChild("Points") -- Gives player gold based on the color of the part if currentColor == blue then playerPoints.Value += smallPoints elseif currentColor == green then playerPoints.Value += largePoints else playerPoints.Value -= losePoints end -- Destroy the part, wait a second, and then destroy the particle pointPart:Destroy() -- Creates a sparkles effect and destroys it local playerCharacter = player.Character local particle = Instance.new("ParticleEmitter") particle.Color = ColorSequence.new(currentColor) particle.Parent = playerCharacter:WaitForChild("Head") task.wait(1) particle:Destroy() end local function partTouched(otherPart) local player = Players:GetPlayerFromCharacter(otherPart.Parent) if player then givePoints(player) end end pointPart.Touched:Connect(partTouched) -- Changes the color of the part based on variables. Needs to be at bottom of script. while true do pointPart.Color = blue task.wait(4) pointPart.Color = green task.wait(3) pointPart.Color = red task.wait(2) end ``` --- title: "Intro to if statements" url: /docs/en-us/tutorials/fundamentals/coding-3/intro-to-if-statements last_updated: 2026-06-29T19:34:14Z description: "Learn how to code if statements in Luau." --- # Intro to if statements In experiences, there are often many cause-and-effect relationships. For example: - If a player scores 10 points, then they win the game. - If a player has a power-up, then they can run super fast. - If a player says "Happy Birthday" in chat, then confetti rains. Scripts use conditional statements to handle these types of situations. **Conditional statements** are lines of code that only run if certain conditions are true. One type of conditional statement is an **if/then statement**. In Luau, the syntax pattern for if statements looks like this: ```lua if "something happens" then -- Make something else happen end ``` Code chunks using conditionals are **control structures.** Control structures are like flow diagrams in code form and can have several conditional statements. ## If statement practice These steps show how to create a script that changes a part's color if a statement is true. 1. In **ServerScriptService**, create a new script and name it `TruthChecker`. Add an appropriate comment to the script.```lua -- Changes the part if a condition is true ``` 2. Create a new part named LieDetector. ### Format if statements **Conditions** can come in various forms but are often simple statements like math equations. For example, if 1+1 equals 2, then run some code. Like ordinary math equations, conditional can use **operators** such as plus (`+`) or less than (`<`) to evaluate statements. One particular operator to be aware of is `==`; it stands for "is equal to." So the statement `2 + 2 == 4` can be read as "two plus two is equal to four". Be very careful not to mix it up with `=`, which assigns new values to objects like variables. 1. Set up the empty conditional. In the script, type `if then`, and press `Enter` to autocomplete the conditional. The keyword `then` will be underlined because the code is incomplete.```lua if then -- empty code end ``` 2. After the keyword `if`, type a true statement such as `3 + 3 == 6`.```lua if 3 + 3 == 6 then -- empty code end ``` 3. Within the conditional, reference the part you named LieDetector and change the part's `Color` property to green.```lua if 3 + 3 == 6 then workspace.LieDetector.Color = Color3.fromRGB(0, 255, 0) end ``` > **Info:** Indenting the lines of code within the conditional makes it easier to see where the code chunk starts and stops. 4. **Test** your code. If three plus three is equal to six, the part will turn green. ## Check a false condition Now, purposely change the statement to see what happens when the math equation is false. 1. In the if statement, change the equation to something inaccurate, such as `3 + 3 >= 10`.```lua if 3 + 3 >= 10 then workspace.LieDetector.Color = Color3.fromRGB(0, 255, 0) end ``` 2. Test your code now. The part shouldn't turn green for a false statement. ### Math operators The table below lists some common Luau operators. More information about operators can be found on [Luau Operators](/docs/en-us/luau/operators.md). | Symbol | Meaning | | --- | --- | | `+` | Addition | | `-` | Subtraction | | `*` | Multiplication | | `/` | Division | ### Comparison operators - `==` : Is equal to. - `~=` : Is not equal to. - `<` or `>` are used for less or greater than, respectively. - `<=` or `>=` are used for less/greater than or equal to, respectively. ## Variables and properties Conditional statements are also used to evaluate the status of properties and variables. The following steps check whether a variable was successfully assigned a value. 1. Delete all of the code and copy the following snippet into the script. Test it, and a new error appears in Output.```lua local mysteryPart = workspace.MysteryPart -- Evaluates as true if MysteryPart was successfully assigned if mysteryPart then workspace.LieDetector.Color = Color3.fromRGB(0, 255, 0) end ``` 2. Insert a new part into the workspace named MysteryPart. Test again, and LieDetector should turn green. 3. Explore how properties can be evaluated with conditionals. Keep MysteryPart, but once again, delete your code and copy the code box below.```lua local mysteryPart = workspace.MysteryPart -- Evaluates as true if MysteryPart is fully opaque if mysteryPart.Transparency == 0 then workspace.LieDetector.Color = Color3.fromRGB(0, 255, 0) mysteryPart.Transparency = .2 end ``` 4. Test the code, and if MysteryPart has the default transparency of 0, it will become ghostly while LieDetector turns green. ## Summary Conditional statements check to see if a statement is accurate, and if so, run some code. If statements are a very common type of conditional statement. They use the pattern "If this is true, then do that." Code chunks using conditional statements are called control structures. Control structures can hold multiple conditional statements. In addition to evaluating if simple math statements are factual, conditionals are also used to check on the status of variables and properties. --- title: "Conditionals" url: /docs/en-us/tutorials/fundamentals/coding-3/landing last_updated: 2026-06-29T19:34:14Z description: "A tutorial series on how to use if statements in Luau within Roblox Studio." --- # Conditionals ### Series description Many experiences have actions that only take place if a list of requirements are met. You may have seen rooms only specific teams could enter or inventory items only some players could purchase. This series will cover how to set up code that says, "If this happens, then do that." ### Objectives and prerequisites | **Learning Objectives** | Understand the logic behind `if` `then` statements and how to set up multiple scenarios with the keywords `elseif` and `else`. | | --- | --- | | **Prerequisites** | Before beginning this course, readers should be able to navigate Roblox Studio, as well as declare new variables and functions. | ### Series contents | Article | Description | | --- | --- | | [Intro to If Statements](/docs/en-us/tutorials/fundamentals/coding-3/intro-to-if-statements.md) | Learn how if statements work and how to code them in Luau. | | [If Statement Practice: Trap Parts](/docs/en-us/tutorials/fundamentals/coding-3/traps-with-if-statements.md) | Use conditionals to create better traps that only hurt avatars and humanoids. | | [If Statement Practice: Power Ups](/docs/en-us/tutorials/fundamentals/coding-3/powerups-with-if-statements.md) | Practice using `if` statements and create a part that increases the players' walking speed. | | [Multiple Conditions](/docs/en-us/tutorials/fundamentals/coding-3/multiple-conditions.md) | Design code that accounts for multiple scenarios with `else` and `elseif`. | | [Multiple Conditions Practice: Awarding Points](/docs/en-us/tutorials/fundamentals/coding-3/give-points.md) | Use everything learned about conditionals to create a part that can award points to players. | --- title: "Multiple conditions" url: /docs/en-us/tutorials/fundamentals/coding-3/multiple-conditions last_updated: 2026-06-29T19:34:14Z description: "Learn how to use elseif and else to create more complex control structures in Luau." --- # Multiple conditions Control structures can have more than one condition. The keywords `else` and `elseif` can create additional scenarios for what should happen under several conditions. The syntax looks like: ```lua if isGreen then print("Go") elseif isYellow then print("Slow") elseif isPedestrians then print("Wait") else print("stop") end ``` In this project, `else` and `elseif` are used to code a parkour course where runners receive awards depending on their finishing time. ## Code multiple conditions For this project, you could write a unique if statement for each medal to award players, but that takes a lot of time. Take, for instance, the imaginary code below. ```lua if finishTime < 5 then -- Get a gold medal end if finishTime >= 5 and <= 10 then -- Get a silver medal end if finishTime > 10 and <= 15 then -- Get a bronze medal end ``` A more readable and efficient way to code this is to use a single if statement and use the keyword `elseif` to provide alternative conditions to test if the previous conditions aren't true. ```lua if finishTime < 5 then -- Get a gold medal elseif finishTime >= 5 and <= 10 then -- Get a silver medal elseif finishTime > 10 and <= 15 then -- Get a bronze medal end ``` When the if/then statement runs, it'll start at the top and only run the code for the **first** true condition it finds. ## Set up the race course Start by placing the course's starting point and finish line, and then create a script to time the player and award different medals. 1. Create an anchored part named FinishLine. To make testing faster, place the start and end close together. You can move the finish line after finishing the script._Finish Part__Entire Course_ 2. In FinishLine, insert a script named RaceManager. Then, add one variable to store how many seconds have passed since the race started and a second variable to reference FinishLine.```lua local finishLine = script.Parent local timePassed = 0 ``` 3. Create a function named `finish()`.```lua local timePassed = 0 local finishLine = script.Parent local function finish() end ``` 4. On your own: - Code a function named `partTouched()` that runs `finish()` when a player touches it. - Connect the finish line part to `partTouched()`. - Compare your code to the example below.```lua local function finish() print("touched the finish line") end local function partTouched(otherPart) local character = otherPart.Parent local humanoid = character:FindFirstChildWhichIsA("Humanoid") if humanoid then finish() end end finishLine.Touched:Connect(partTouched) ``` 5. Playtest and check that `finish()` is called when you touch the finish line. ### Keep finish() from repeating Right now, whenever a player touches the finish line, `finish()` gets continuously called as long as the player touches the part. Use a **boolean**, a variable that stores true or false, to ensure that finish() is only called once. 1. Create a new variable named `raceActive` and set it to `true`.```lua local timePassed = 0 local finishLine = script.Parent local raceActive = true -- Runs whenever the player touches the finish line part local function finish() ``` 2. Add a second condition to the if statement to check if raceActive is true before calling `finish()`.```lua local function partTouched(otherPart) local character = otherPart.Parent local humanoid = character:FindFirstChildWhichIsA("Humanoid") if humanoid and raceActive == true then finish() end end ``` 3. To stop `finish()` from being called again, set `raceActive` to `false`.```lua local function finish() print("touched the finish line") raceActive = false end ``` 4. Playtest your experience to check that you only see your test print statement once. ### Keep track of time Like an if statement, a while loop can also use a condition to see if it should run. To time the player, create a timer using a while true do loop that only runs when the `raceActive` boolean is true. 1. At the bottom of the script, type `while raceActive == true do`. Press Enter to auto-complete and add the `end`.```lua finishLine.Touched:Connect(partTouched) while raceActive == true do end ``` > **Info:** Make sure the loop is at the **bottom** of the script. Even though this while true do loop, eventually stops, it should still stay at the bottom of the script. This ensures that the previous code runs before it reaches the loop. 2. To time the players, in the loop, add 1 to the `timePassed` variable once every second. Include a print statement to test your work.```lua finishLine.Touched:Connect(partTouched) while raceActive == true do task.wait(1) timePassed += 1 print(timePassed) end ``` 3. Playtest the experience and check that you see each second displayed in the **Output** window. ## Award player medals To finish, use an if statement with multiple conditions to award players a different prize medal based on their performance. Use an if statement and two elseif statements to check the player's finish time and award them the correct medal. 1. To confirm each medal can be awarded, use print statements.```lua local function finish() raceActive = false print("You finished in " .. timePassed) end ``` 2. To award a gold medal, code an if statement that compares `timePassed` to the desired finish time. This example checks if the player's time was less than or equal to 10 seconds.```lua local function finish() raceActive = false print("You finished in " .. timePassed) if timePassed <= 10 then print("You get a gold medal!") end end ``` 3. Playtest and confirm the gold medal can be awarded. ### Add additional conditions Now that you've tested for the gold medal, code conditions for the other medals using the `elseif` keyword. 1. For the silver medal, use `elseif` and the next desired time range. In this example, the range is greater than 10 seconds but less than or equal to 20 seconds.```lua local function finish() raceActive = false print("You finished in " .. timePassed) if timePassed <= 10 then print("You get a gold medal!") elseif timePassed > 10 and timePassed <= 20 then print("You get a silver medal!") end end ``` 2. Use the same pattern for the bronze medal. Check your code with the example below.```lua local function finish() raceActive = false print("You finished in " .. timePassed) if timePassed <= 10 then print("You get a gold medal!") elseif timePassed > 10 and timePassed <= 20 then print("You get a silver medal!") elseif timePassed > 20 and timePassed <= 30 then print("You get a bronze medal!") end end ``` 3. Playtest for the silver and bronze medals. ### Troubleshooting tips If you don't see the silver and bronze metals appear, try one of the below. - Each `elseif` should have a then after its condition. - In `partTouched()`, make sure the second condition of the if statement uses `==`, like in `raceActive == true`. - Check that each `elseif` is in scope. Each `elseif` condition must be between the first line of the if/then statement and it's last `end`. ### Add the else condition If the player didn't earn any of the medals, you should encourage them to try again. In this case, you can use an `else` statement, which runs if **no other** conditions are true, to show them a message. > **Info:** It's recommended that every if statement has an else, just in case the code doesn't find anything true. 1. Below the last `elseif` and above `end`, start a new line and type `else`. **Do not** add then. Beneath else, use a print statement to prompt them to try again.```lua local function finish() raceActive = false print("You finished in " .. timePassed) if timePassed <= 10 then print("You get a gold medal!") elseif timePassed > 10 and timePassed <= 20 then print("You get a silver medal!") elseif timePassed > 20 and timePassed <= 30 then print("You get a bronze medal!") else print("Try again!") end end ``` > **Info:** An `else` statement should always be the **last** check in an a control structure. 2 . Playtest to see the else message. ## Summary Control structures can have more than one scenario. Use if statements to set up the initial statement to check, and then add as many `elseif` conditions as necessary. Finally, use `else` to state what should happen if all given conditions are false. Starting at the top, all conditions will be checked, and only the first true condition will run the code. The remaining conditions will not be checked, nor will their code run. After finishing the project, you can expand upon the script to add new elements in a few extra ways. - Add code so players can repeat the race by touching the start line when they finish. - Design a way to display time during a race. You can either display the time on a part using a Surface GUI, like in the [Create a Timed Bridge tutorial](/docs/en-us/tutorials/fundamentals/coding-4/create-a-timed-bridge.md). ```lua local timePassed = 0 local finishLine = script.Parent -- Used to keep finish() and timer from repeating when race is over local raceActive = true -- Runs when the player touches the finish line and shows them an award local function finish() raceActive = false print("You finished in " .. timePassed) if timePassed <= 10 then print("You get a gold medal!") elseif timePassed > 10 and timePassed <= 20 then print("You get a silver medal!") elseif timePassed > 20 and timePassed <= 30 then print("You get a bronze medal!") else print("Try again!") end end -- Checks if a player touches the part when a race is active local function partTouched(otherPart) local character = otherPart.Parent local humanoid = character:FindFirstChildWhichIsA("Humanoid") if humanoid and raceActive == true then finish() end end finishLine.Touched:Connect(partTouched) -- Keeps track of race time while the race is active. Needs to be at script bottom. while raceActive == true do task.wait(1) timePassed += 1 print(timePassed) end ``` --- title: "Evaluate multiple statements" url: /docs/en-us/tutorials/fundamentals/coding-3/powerups-with-if-statements last_updated: 2026-06-29T19:34:14Z description: "How to use the and keyword to evaluate more than one statement at a time in Roblox Luau." --- # Evaluate multiple statements If statements can have multiple requirements that need to be true before running code. The keyword `and` allows you to combine statements. The following code evaluates first if two plus two equals six and then if four does not equal six. If both statements are true, the code will run. ```lua -- will not run if 2 + 2 == 6 and 4 ~= 6 then print("Both statements are true") end -- will run if 4 + 2 == 6 and 4 ~= 6 then print("Both statements are true") end ``` ## Create a powerup Powerups are in-experience items that give players special abilities like flying, invisibility, or speed. This powerup will boost the player's walking speed every time the powerup is touched. Continuously applying boosts can make the player go way too fast, so `and` will be used to control the upper walking speed limit. ### Set up the powerup Use this code with a simple part or a model, such as a crystal, coin, or glowing neon orb. 1. Create a new part named **Powerup** and insert a script named **WalkSpeedManager**. 2. Declare a variable named `speedBoost` and assign the script's parent object.```lua -- Gives a temporary speed boost when touched local speedBoost = script.Parent ``` 3. Set up a function named `onTouch` and connect it to the parent object's `Touched` event. Then playtest and check your work.```lua local speedBoost = script.Parent local function onTouch(otherPart) print("Something touched speedBoost") end speedBoost.Touched:Connect(onTouch) ``` 4. The WalkSpeed property is found on Humanoid objects. Use the same pattern used when creating a trap part and create a conditional that checks for Humanoid objects.```lua local function onTouch(otherPart) local character = otherPart.Parent local humanoid = character:FindFirstChildWhichIsA("Humanoid") if humanoid then print("A Humanoid was found") end end ``` ## Speed players up The speed boost will make avatars walk faster every time the speed boost is touched. That will quickly become very, very fast. The keyword `and` will ensure players can't go too fast by only enabling the speed boost if the player is under a certain speed. 1. If a Humanoid is found, take the current WalkSpeed value and add 10 to it. Playtest, and your avatar will get faster every time it touches the speed boost.```lua local function onTouch(otherPart) local character = otherPart.Parent local humanoid = character:FindFirstChildWhichIsA("Humanoid") if humanoid then humanoid.WalkSpeed += 10 end end speedBoost.Touched:Connect(onTouch) ``` 2. In the if statement, use the keyword `and` to add a second condition that the current WalkSpeed value is less than 50. After adding the boost, the fastest WalkSpeed value will be 60.```lua if humanoid and humanoid.WalkSpeed <= 50 then humanoid.WalkSpeed += 10 end ``` ### Fine tune the speed boost OnTouch is called every time the speed boost is touched. Every step or slightest bounce triggers the Touched event and calls the connected function. The part's property, `CanTouch` can keep the Touched event from firing. Take advantage of CanTouch and turn off the speed boost for one second every time it's been activated. 1. After applying the boost, set the part's CanTouch property to false. Playtest and make sure the boost only applies once.```lua local speedBoost = script.Parent local function onTouch(otherPart) local character = otherPart.Parent local humanoid = character:FindFirstChildWhichIsA("Humanoid") if humanoid and humanoid.WalkSpeed <= 50 then humanoid.WalkSpeed += 10 speedBoost.CanTouch = false end end speedBoost.Touched:Connect(onTouch) ``` 2. Use `task.wait(1)` to pause the script for one second, then set CanTouch to true. Playtest and make sure the speed boost can be reapplied after one second.```lua local speedBoost = script.Parent local function onTouch(otherPart) local character = otherPart.Parent local humanoid = character:FindFirstChildWhichIsA("Humanoid") if humanoid and humanoid.WalkSpeed <= 50 then humanoid.WalkSpeed += 10 speedBoost.CanTouch = false task.wait(1) speedBoost.CanTouch = true end end speedBoost.Touched:Connect(onTouch) ``` 3. Play with the values in the finished script. WalkSpeed can go up to 100. The default WalkSpeed value is 16. ## Summary The keyword `and` can be used to require multiple conditions before running a code chunk, such as a value being more than 0 and less than 100. Or that if there is a Humanoid and its WalkSpeed is less or equal to 50. --- title: "If/then practice with traps" url: /docs/en-us/tutorials/fundamentals/coding-3/traps-with-if-statements last_updated: 2026-06-29T19:34:14Z description: "Create a trap that sets the player's health to zero with Luau." --- # If/then practice with traps Traps that decrease players' health are a fun game-play element that can be coded with conditional statements. Practice using conditionals by creating a part that sets the player's health to zero when touched. ## Set up the trap Traps work exceptionally well in experiences with movement-based challenges, like obbies. These steps will start by setting up the necessary variables and functions. Do as much as you can without looking at the code boxes first. 1. Create and name a trap part. Insert a script into the part. 2. In the script, add a descriptive comment and then use a variable to reference the script's parent.```lua -- If a player touches this part, set their health to 0 local trapPart = script.Parent ``` 3. Create a function named `onTouch()` with a parameter named `otherPart`.```lua -- If a player touches this part, set their health to 0 local trapPart = script.Parent local function onTouch(otherPart) end ``` 4. Connect the function to the trap part's `Touched` event to run whenever something touches the part.```lua local trapPart = script.Parent local function onTouch(otherPart) end trapPart.Touched:Connect(onTouch) ``` ## Check for player touch Remember, the parameter `otherPart` records whatever touches the trap part, which might be a part of a player or just the baseplate. To ensure the trap will only destroy players and won't destroy random decor items, use an if/then statement to check if whatever is in `otherPart` contains a Humanoid object. > **Info:** Humanoids are present in player avatars and many NPCs. Humanoids have several unique properties. One such property is Health. ### Find a specific object The function `FindFirstChildWhichIsA()` can be used to look for specific object types, which is handy because we're looking for a Humanoid-type object. Players will likely touch the trap with only a part of their avatar, so a variable must be set up to find the parent of the touching part and search it for a Humanoid. 1. In `onTouch()`, type `local character = otherPart.Parent`.```lua local trapPart = script.Parent local function onTouch(otherPart) -- Finds otherPart's parent object local character = otherPart.Parent end trapPart.Touched:Connect(onTouch) ``` 2. Check to see if `character` has a `Class.Humanoid` by typing:`local humanoid = character:FindFirstChildWhichIsA("Humanoid")````lua local trapPart = script.Parent local function onTouch(otherPart) local character = otherPart.Parent local humanoid = character:FindFirstChildWhichIsA("Humanoid") end trapPart.Touched:Connect(onTouch) ``` ### Check with an if statement If a Humanoid is found, then set the Humanoid's Health to zero. 1. Use an if statement to check if a Humanoid was successfully assigned to `local humanoid`.```lua local trapPart = script.Parent local function onTouch(otherPart) local character = otherPart.Parent local humanoid = character:FindFirstChildWhichIsA("Humanoid") -- Evaluates if a Humanoid was found if humanoid then end end trapPart.Touched:Connect(onTouch) ``` 2. Add a print statement and check the code so far.```lua local trapPart = script.Parent local function onTouch(otherPart) local character = otherPart.Parent local humanoid = character:FindFirstChildWhichIsA("Humanoid") -- Evaluates if a Humanoid was found if humanoid then print("Found a Humanoid") end end trapPart.Touched:Connect(onTouch) ``` 3. **Run** the code and check that you can see the output whenever a player touches the part. ## Change the player's health If the statement is true, you can use the same humanoid variable to set the player's health to 0. 1. Between `then` and `end`, type `humanoid.Health = 0`.```lua local trapPart = script.Parent local function onTouch(otherPart) local character = otherPart.Parent local humanoid = character:FindFirstChildWhichIsA("Humanoid") -- Evaluates if a Humanoid was found if humanoid then print("Found a Humanoid") humanoid.Health = 0 end end trapPart.Touched:Connect(onTouch) ``` 2. Test the trap. ## Summary This trap part used conditionals to detect Humanoid parts and set the Humanoid's health to zero. This script is an improvement on the previous trap script, which destroyed any touching object no matter what it was. It does, however, still have a few flaws. Humanoids aren't just in players. Humanoids are also found in non-playable characters. The script is also only good at setting the player's health to zero. You can experiment with subtracting a small amount of health, but it's likely to subtract the health faster than desired. Later lessons provide further improvements to give greater control over how much health is subtracted from players. --- title: "Loops practice - create a timed bridge" url: /docs/en-us/tutorials/fundamentals/coding-4/create-a-timed-bridge last_updated: 2026-06-29T19:34:14Z description: "Make a bridge that disappears to add challenge to a Roblox experience. Combine for loops and while loops in this computer science practice." --- # Loops practice - create a timed bridge This project is another example of using for loops in a practical way. For this bridge, players will touch a button to make a bridge walkable for a limited time before it disappears. To create a timer and show the remaining time to a player, you'll use for loops. ## Set up the project This bridge can be included in any Roblox project with movement-based challenges, like an obby. ### Create parts 1. Find a place to build a bridge, like a river or a large gap in an obby. Create three **anchored** parts like below. - TimerDisplay - Bridge - ButtonBridge 2. When inactive, the bridge will be semi-transparent. To do so, select the **Bridge** and change its properties. - **Transparency** = 0.8 - **CanCollide** = False ### Create the timer display When crossing the bridge, players will need to see how many seconds are left before the bridge disappears. One way to display images or text is by adding an object called a Surface GUI to a part. **Surface GUIs** can also be used to create in-game signs, custom health bars, and inventory systems. This tutorial will go through this quickly, but more information can be found in the [Tutorials](/docs/en-us/tutorials.md) section. 1. Select TimerDisplay and add a **SurfaceGui**. Then, parented to the Surface Gui, add a **TextLabel**. 2. Select the Surface GUI. In the Properties, make the following changes: - Change the **Face** so you can see the text label on the front of the timer where the player is looking. 3. Select the TextLabel. In the Properties, make the following changes: - Set **Size** to `{1, 0},{1, 0}`. - Set **TextScaled** to true. - Set **Text** to be blank. Text will be updated using the script. ### Set up the script Now that the timer is in place, create a script to control the bridge and display the countdown number to players. 1. In the Bridge part, add a new script named TimedBridge. In that script, create the following variables.```lua local bridge = script.Parent local button = workspace.ButtonBridge local timerText = workspace.TimerDisplay.SurfaceGui.TextLabel -- How long the bridge will stay solid local timerDuration = 5 ``` ## Code the touch interaction To use the bridge, you'll need to create two functions. One function will make the bridge walkable and display the timer. The other function will listen for if a player touches the button that activates the bridge. 1. Create a new function named `startTimer()` with a print statement inside. You'll use the print statement to test your code.```lua local timerDuration = 5 local function startTimer() print("Countdown started") end ``` 2. Code a function named `buttonPressed()` to check if a humanoid touches the button. The function should accept a parameter of `partTouched`, and then have an empty if statement if a humanoid is detected in that part.```lua local function startTimer() print("Countdown started") end local function buttonPressed(partTouched) local character = partTouched.Parent local humanoid = character:FindFirstChildWhichIsA("Humanoid") if humanoid then end end ``` 3. To start the timer, in the `if` statement, call the `startTimer()` function.```lua local function buttonPressed(otherPart) local character = otherPart.Parent local humanoid = character:FindFirstChildWhichIsA("Humanoid") if humanoid then startTimer() end end ``` 4. Under the end of `buttonPressed()`, connect the `buttonPressed()` function to the button's `Touched` event.```lua local function buttonPressed(otherPart) local character = otherPart.Parent local humanoid = character:FindFirstChildWhichIsA("Humanoid") if humanoid then startTimer() end end button.Touched:Connect(buttonPressed) ``` 5. Run the project. Touch the part and check in the Output window to see the print statement. ### Troubleshooting tips At this point, if the bridge doesn't work as intended, try one of the following below. **Issue**: Error message saying: `"... is not a valid member of workspace"`. - In the TimedBridge script, check that all parts are spelled exactly as they're seen in the Explorer. **Issue**: Can't see the parts. - Make sure all three parts are Anchored. - Check the Transparency property for the bridge. ## Create the timer Whenever players step on the bridge, `startTimer()` will make the bridge walkable and start the timer. Once the timer reaches 0, the bridge will become unwalkable, causing anyone who's not fast enough to fall. ### Make the bridge walkable To start, the script will need to make the bridge solid, or collidable and then start a timer until it becomes unwalkable. 1. To make the bridge walkable, in `startTimer()`, change the bridge's Transparency property to 0 (opaque) and the CanCollide property to true.```lua local function startTimer() -- Make the bridge visible and walkable bridge.Transparency = 0 bridge.CanCollide = true end ``` 2. To create a timer that counts down, create a for loop with the following values. - **Control Variable**: named `count`, set to `timerDuration`. - **End**: 0 - **Increment**: -1```lua local function startTimer() -- Make the bridge visible and walkable bridge.Transparency = 0 bridge.CanCollide = true for count = timerDuration, 0, -1 do end end ``` 3. To show the timer to players, change the text in `timerText` to display count by typing `timerText.Text = count`. Each time the for loop goes through an iteration, it'll show players the next number in the timer.```lua for count = timerDuration, 0, -1 do timerText.Text = count end ``` 4. Use a task.wait function to make the for loop run only once a second.```lua for count = timerDuration, 0, -1 do timerText.Text = count task.wait(1) end ``` 5. Run the experience. When you touch the button, the bridge should appear and the timer will start, then complete. ## Keep the bridge from restarting Notice though, if you move around on the button, the timer will keep restarting. This is because the for loop is being called each time you touch the button and starting the for loop from the beginning. To keep the timer from constantly restarting, you'll need to add a boolean, a type of variable, that will control whether or not `startTimer()` is allowed to be called again. **Booleans** are written the same way as other variables, but instead of using numbers or strings, they can only be set to true or false. In this situation, the script will use a boolean to check if the timer is currently running before starting it. 1. At the top of your script, under your variables, create a variable named `timerActive` and set it to `false` since nobody has pressed the button yet.```lua local timerDuration = 5 local timerActive = false local function startTimer() ``` 2. To makes sure the timer only starts when the `timerActive` boolean is false, add a second condition to the if statement in `buttonPressed()`.```lua local function buttonPressed(otherPart) local character = otherPart.Parent local humanoid = character:FindFirstChildWhichIsA("Humanoid") if humanoid and timerActive == false then startTimer() end end ``` 3. To keep `startTimer()` from running again before the timer runs out, set the boolean `timerActive` to true.```lua local function startTimer() timerActive = true bridge.Transparency = 0 bridge.CanCollide = true ``` 4. In the `startTimer()` function, **after** the for loop, set the bridge back to its original properties by changing the bridge's transparency to 0.8 and CanCollide to false.```lua local function startTimer() timerActive = true bridge.Transparency = 0 bridge.CanCollide = true -- For loop that counts down from timerDuration for count = timerDuration, 0, -1 do timerText.Text = count task.wait(1) end -- Make the bridge not walkable bridge.Transparency = 0.8 bridge.CanCollide = false end ``` 5. One last thing to reset the bridge is to change the `timerText` to an empty string like how it was originally. Then, set the `timerActive` boolean to false.```lua bridge.Transparency = 0.8 bridge.CanCollide = false timerText.Text = "" timerActive = false ``` 6. Playtest and check to make sure the bridge can be used multiple times. ## Complete timed bridge script ```lua local bridge = script.Parent -- Gets the button as it's typed in the Explorer local button = workspace.ButtonBridge -- Gets the part for the display local timerPart = workspace.TimerDisplay -- Gets the Text that will display the timer local timerText = timerPart.SurfaceGui.TextLabel -- How long players have to cross the bridge local timerDuration = 5 local timerActive = false local function startTimer() print("Countdown started") timerActive = true bridge.Transparency = 0 bridge.CanCollide = true -- For loop that counts down from timerDuration for count = timerDuration, 0, -1 do timerText.Text = count task.wait(1) end -- Make the bridge not walkable bridge.Transparency = 0.8 bridge.CanCollide = false timerText.Text = "" timerActive = false end local function buttonPressed(partTouched) local character = partTouched.Parent local humanoid = character:FindFirstChildWhichIsA("Humanoid") print("part touched") if humanoid and timerActive == false then print("starting timer") startTimer() end end button.Touched:Connect(buttonPressed) ``` ## Summary Loops can be combined with different means of interaction to create fun gameplay moments. In this tutorial, a timed bridge is created using a for loop that functions as a countdown. During the countdown, players are given a limited time to get across. --- title: "Glow lights with for loops" url: /docs/en-us/tutorials/fundamentals/coding-4/glow-lights-with-for-loops last_updated: 2026-06-29T19:34:14Z description: "Create glowing lights in Roblox Studio using a for loop. This practical example teaches computer science with Roblox." --- # Glow lights with for loops To practice for loops, you'll create a lamp that gradually glows brighter and then dims over time. This script can then be applied to any other lights in a project, whether it's a street light or glowing crystal. ## Set up the part and script The lamp will be a part with an attached light and script. 1. To make it easier to see the light, change the 3D world to **night time**. In the **Explorer** ⟩ **Lighting**, change **ClockTime** to `0`.![image](../../../assets/education/coding-4/glowing-lights-setClockTime.png) 2. Create a new part or model named Lamp. 3. Select Lamp, and add a **light**, such as a PointLight or SpotLight. You'll work with the brightness property of the light to create a glowing lamp. 4. In Lamp, add a new script named GlowScript. In that script, create variables to store the lamp part and the point light.```lua local lightPart = script.Parent local light = lightPart.SpotLight ``` > **Warning:** The specific path to the light and part might differ based on the model you're using. Be sure to double check in the Explorer. 5. Next, create a variable to hold how much the light's brightness will change each time the loop runs. Then, add another variable for how many seconds the loops will wait before running again.```lua -- How much the light's brightness will change each time local brightnessChange = 1 -- How often in seconds the light will change brightness local timeChange = 1 ``` ## Make the lamp glow The lamp will use two **for loops**, one that counts up to make the lamp brighter, and one that counts down to dim it. Each for loop will have a control variable called currentBrightness. That way, as the for loop's control variable goes up and down, so will the brightness of the light. ### First loop (light increase) Remember, a for loop starts with keyword `for` followed by a control variable. This script will set the brightness value of the light to value in the control variable. 1. Code the for loop using the following values. Remember to separate the control variable and end values with a comma. - **Control variable**: `currentBrightness` - **Start value**: 0 - **End value**: 5 - **Increment Value**: `brightnessChange````lua local timeChange = 1 for currentBrightness = 0, 5, brightnessChange do end ``` 2. In scope of the for loop, set the brightness property of the light to the value in the control variable by typing `light.Brightness = currentBrightness`. Now, when the loop runs, the light will become brighter.```lua for currentBrightness = 0, 5, brightnessChange do light.Brightness = currentBrightness end ``` 3. So the change doesn't happen all at once, add a wait function using the value in `timeChange`.```lua for currentBrightness = 0, 5, brightnessChange do light.Brightness = currentBrightness task.wait(timeChange) end ``` 4. Run the experience to see the light increase in brightness each second. > **Info:** Depending on the lighting in your project, you make need to adjust the brightness variables, or the environment's lighting. If you can't see the brightness change over time in the first loop: - Make sure that `light.Brightness = currentBrightness` is between the do and end of your for loop. - Check that `timeChange` is at least 1 or higher. Smaller numbers will make the brightness change faster, but be harder to see over time. - Make sure that the first line of your for loop has two total commas separating the control variable, end value, and increment value. ### Second loop (light decrease) To dim the light, use a second for loop. The values of this loop will be reversed so the light starts bright and each second, gets dimmer. 1. Code a second for loop that decreases the brightness over time. The solution is in the code box below. Use the following values: - **Control Variable** - `currentBrightness` set to 5, the end of the last loop. - **End** - 0, turning off the light. - **Increment** - subtract `brightnessChange`.```lua -- Second loop dims lamp for currentBrightness = 5, 0, -brightnessChange do light.Brightness = currentBrightness task.wait(timeChange) end ``` 2. Run your experience; you should see the light brighten and then dim. ### Make the light repeat Right now, the light only turns on and off once. To make the lamp continuously glow on and off, the for loops will be placed inside a repeating while loop. 1. Under the for loops, create a new while loop.```lua while true do end ``` 2. Place both for loops inside a while loop. Indent the for loops to make them easier to tell apart from the while loop.```lua while true do for currentBrightness = 0, 5, brightnessChange do light.Brightness = currentBrightness task.wait(timeChange) end for currentBrightness = 5, 0, -brightnessChange do light.Brightness = currentBrightness task.wait(timeChange) end end ``` 3. Run the experience to see the light turn bright and dim continuously. ## Finished light script A finished version of the script can be referenced below. ```lua -- Stores the light attached to this lamp lightPart = script.Parent light = lightPart.SpotLight -- How much the light's brightness will change each time local brightnessChange = 1 -- How often in seconds the light will change brightness local timeChange = 0.2 while true do for count = 0, 5, brightnessChange do light.Brightness = count task.wait(timeChange) end -- Second loop to turn off light for count = 5, 0, -brightnessChange do light.Brightness = count task.wait(timeChange) end end ``` ## Summary To create complex effects in a project, scripts can use multiple loops, and even combinations of different types of loops. To code a glowing light, two loops are nested within a while loop. As coders add in more loops, be sure to check indentation so code can be easy to read. --- title: "Intro to for loops" url: /docs/en-us/tutorials/fundamentals/coding-4/intro-to-for-loops last_updated: 2026-06-29T19:34:14Z description: "Use for loops to repeat code in this lesson for Luau. This lesson explains how for loops work and includes practice scripts." --- # Intro to for loops There are different ways to make code run over and over. If you want the code to only run a certain amount of times, use a **for loop**. This article will cover the logic behind for loops and demonstrate some practical examples, such as coding a countdown. ## How for loops work For loops use three values to control how many times they run: a **control** variable, an **end** value, and an **increment** value. Starting from the value of the control variable, the for loops will either count up or down each time it runs code inside the loop until it passes the end value. Positive increment values count up, and negative increment values count down. > **Info:** Different computer languages might use other terminology with for loops. For instance, in C# or JavaScript, the end value might be called the condition. ## Steps in a for loop To understand for loops, it helps to see a flow chart diagram showing the logic of how they progress. First, the for loop compares the control variable with the end value. After running the code, the increment value is added to the control variable. The loop then checks the control variable and starts over. Once the control variable passes the end value, the loop will stop. For example, if a loop has an end value of 10, once the control variable has passed 10, the for loop will stop. ## Code a countdown To see how a for loop works, use these steps to code a for loop that starts at 10 and counts down to 0, one number at a time. Every time the loop runs, it'll print the current value inside the control variable. 1. In **ServerScriptService**, create a new script named PracticeLoop. In the script, start by typing the keyword `for`.```lua for ``` 2. Create a **control variable** named `count` and set a starting value of 10.```lua for count = 10 ``` 3. Set the **end value** to 0, by typing `, 0`. Make sure to include a comma to separate the values.```lua for count = 10, 0 ``` 4. Create an **increment value** of -1 by typing `, -1`. After the loop has finished its action, it'll add the increment value to the control variable, count. Because the increment is negative, it'll be subtracted when added to the control variable.```lua for count = 10, 0, -1 ``` > **Info:** A for loop doesn't explicitly need an increment. Without one, the loop will, by default, add 1 after each loop. > > `for count = 10, 0` 5. To finish the for loop, type do and press `Enter` to add `end`. Any code typed between `do` and `end` will run each time the loop repeats.```lua for count = 10, 0, -1 do end ``` 6. Inside the loop, create a countdown by printing the value of the control variable, `count`, and then delay the script with a wait function.```lua for count = 10, 0, -1 do -- Prints the current number the for loop is on print(count) -- Wait 1 second task.wait(1) end ``` 7. Run the project and watch the Output window to see the for loop. Notice that the loop will print out the current value of count each time it goes through an **iteration**. An iteration is the complete process of checking the control value, running code, and updating the increment value. Because the control variable starts at 0 and has to go pass 10, the loop will go through 11 iterations before stopping. ### Troubleshooting tips At this point, if the loop doesn't work as intended, try one of the following below. - Check that you have **two commas** separating the numbers in your for loop. Having extra or missing commas will make the loop not start. - If the for loop prints all at once, make sure that there is a wait function that uses at least 1 second. ## Different for loop examples Changing the three values of a for loop will change how the loop functions. Below are different examples of for loops with different start, end, and increment values. Try putting them into scripts and see what happens. ### Count up by one ```lua for count = 0, 5, 1 do print(count) task.wait(1) end ``` ### Count up in even numbers ```lua for count = 0, 10, 2 do print(count) task.wait(1) end ``` ### If for loops don't run at all If the control variable starts out **beyond** the end value, like in the example below, the for loop won't run at all. ```lua for count = 10, 0, 1 do print(count) task.wait(1) end ``` In this case, the for loop is counting up and checking if count is greater than 0. When the for loop does it's first check, it sees that 10 is greater than 0, and so it'll stop the loop without printing anything. ## Summary A for loop is a common type of loop that's used when a set of instructions should repeat a specific amount of times. To create a for loop, use three variables with the syntax below: ```lua for count = 0, 10, 1 do print(count) end ``` In the example above, the loop will start at 0. For each loop, it will print the count variable, add 1 to count, and finally finish looping when count is equal to 10. --- title: "Loops" url: /docs/en-us/tutorials/fundamentals/coding-4/landing last_updated: 2026-06-29T19:34:14Z description: "Learn how to use loops like while and for loops in this tutorial series for Roblox Studio. Great for beginners learning Luau or computer science." --- # Loops ### Series description In computer science, loops are a common feature in many scripts, often used to repeat sets of code. This series will cover two of the most common loops: for and while loops. ### Objectives and prerequisites | **Learning objectives** | Understand situations where loops are useful as opposed to alternative forms of writing a script. Be able to write out for and while loops, while identifying the syntax for both. | | --- | --- | | **Prerequisites** | Have a general understanding of variables in Luau. | ### Series Contents | Article | Description | | --- | --- | | [Repeat code with while Loops](/docs/en-us/tutorials/fundamentals/coding-4/repeat-code-with-while-loops.md) | Learn how to repeat code to create a color changing part. | | [Intro to for loops](/docs/en-us/tutorials/fundamentals/coding-4/intro-to-for-loops.md) | Use a common loop that repeats instructions for a set amount of times. | | [Loops practice: glowing lights with for loops](/docs/en-us/tutorials/fundamentals/coding-4/glow-lights-with-for-loops.md) | Practice using for loops by making lights glow. | | [Loops Practice: Creating a Timed Bridge](/docs/en-us/tutorials/fundamentals/coding-4/create-a-timed-bridge.md) | A more advanced implementation of a for loop that makes a bridge disappear and reappear. | | [Nesting loops](/docs/en-us/tutorials/fundamentals/coding-4/nested-loops.md) | Add a loop within a loop for more complex behavior and code. | --- title: "Nested loops" url: /docs/en-us/tutorials/fundamentals/coding-4/nested-loops last_updated: 2026-06-29T19:34:14Z description: "Learn how to use nested loops. This computer science lesson uses Luau to combine multiple nested for loops." --- # Nested loops Nesting loops allows you to repeat tasks in batches. For example, baking three batches of six cupcakes, or assigning weapons to players on two teams. ## How nested loops run When loops are nested, scripts go line by line until it reaches the next loop. The inner loop will run until it's condition is met before returning to the outer loop. ### Nested loop logic The following diagram shows the steps a loop takes. | | | | --- | --- | ### Nested loop example Nested loops can seem somewhat abstract, so a visual example can help. For this exercise, copy and paste a sample script and run it in Studio. This script will create towers of parts. The outer loop will control how many parts to make, while the inner loop will create the actual batch. 1. Create a new script in **ServerScriptService** named PartMaker. Copy the code below.```lua local numberOfBatches = 7 local partsPerBatch = 5 local partsMade = 0 -- Makes a single cube local function createPart() local part = Instance.new("Part") part.Size = Vector3.new(2, 2, 2) part.CFrame = CFrame.new(20, 0, 20) part.Color = currentColor part.Parent = workspace end -- Outer loop for partBatch = 1, numberOfBatches do print("Top outer loop: part batch " .. partBatch) currentColor = Color3.fromRGB(math.random(0, 255), math.random(0, 255), math.random(0, 255)) -- Inner loop for partNumber = 1, partsPerBatch do createPart() print("Inner loop: part " .. partNumber) partsMade += 1 task.wait(0.5) end print("Bottom outer loop: " .. partsMade .. " parts made so far.") task.wait(2) end ``` 2. Watch as the script spawns a different batch of colored parts. After going through one batch, it'll pause for 2 seconds. The print statement in the outer loop will run only one time per completed inner loop. ## Nested for loop tower Each loop has its own set of code, so can be responsible for different tasks. One thing nested loops can do is change the placement of where an object spawns to create a tower like the one in this video. There's three different loops, one each for controlling where along the width, length, and height of the tower the cube spawns. ## Code a cube maker script To practice nested loops, you'll make a script that creates a tower of cubes. For the cube tower script, first code a function that spawns a single cube. The tower will be built by repeatedly calling this function. ### Set up the script For the cube tower script, first code a function that spawns a single cube. The tower will be built by repeatedly calling this function. 1. **Delete** the PartMaker script or disable it (in the script properties, check Disabled). If you don't, there will be two scripts making parts at the same time in the same place. 2. Create a new script named TowerBuilder. Add variables for tower size and cube size at the top.```lua local TOWER_SIZE = 4 local CUBE_SIZE = 2 ``` > **Info:** Notice how the variables above are all capitalized. These are called **constants**, or variables that don't change. To set them apart from variables that will be changed within the script, type them using capitals and underscores. 3. Add a local function named `makeCube()` that creates a single square cube using `CUBE_SIZE`.```lua local TOWER_SIZE = 4 local CUBE_SIZE = 2 -- Creates individual cubes local function makeCube() local cube = Instance.new("Part") cube.Size = Vector3.new(CUBE_SIZE, CUBE_SIZE, CUBE_SIZE) end ``` 4. Set the cube's color to a variable which will be updated in the nested loops.```lua local function makeCube() local cube = Instance.new("Part") cube.Size = Vector3.new(CUBE_SIZE, CUBE_SIZE, CUBE_SIZE) cube.Color = currentColor end ``` 5. Lastly, **parent** the new cube to the workspace so it appears.```lua local function makeCube() local cube = Instance.new("Part") cube.Size = Vector3.new(CUBE_SIZE, CUBE_SIZE, CUBE_SIZE) cube.Color = currentColor cube.Parent = workspace end ``` > **Info:** In Roblox code, once an object is created by script, it's recommended to parent it at the end. It's faster to make changes to an instance before there is a physical object in the 3D world. ### Spawn in different directions To create a tower, spawn cubes at specific points by setting the X, Y, Z properties of each new cube. X and Z are side to side. Y is up and down. 1. In `makeCube()`, add parameters for `spawnX`, `spawnY`, and `spawnZ`. These numbers will set each new cube's spawn location.```lua -- Creates individual cubes local function makeCube(spawnX, spawnY, spawnZ) local cube = Instance.new("Part") cube.Size = Vector3.new(CUBE_SIZE, CUBE_SIZE, CUBE_SIZE) cube.Color = currentColor cube.Parent = workspace end ``` 2. Inside the function, set the cube's CFrame property to a new CFrame using the `spawnX`, `spawnY`, `spawnZ` parameters.```lua local function makeCube(spawnX, spawnY, spawnZ) local cube = Instance.new("Part") cube.Size = Vector3.new(CUBE_SIZE, CUBE_SIZE, CUBE_SIZE) cube.Color = currentColor cube.CFrame = CFrame.new(spawnX, spawnY, spawnZ) cube.Parent = workspace end ``` > **Info:** In Roblox, CFrame is a data type used to store 3D position and orientation. It's used to place objects in a world. ### Spawn with nested loops The script will have three loops total, one each for the length, width, and height of the tower. To complete an entire floor before moving upwards, start with setting the Y coordinate in the first, outermost loop. 1. Under the `makeCube()` function, Create a for loop to set how **high** each cube spawns. - **Control variable**: `heightIndex = 1` - **End point**: `TOWER_SIZE` - Inside the loop, add: local spawnY = (heightIndex - 1) * CUBE_SIZE```lua -- Builds tower for heightIndex = 1, TOWER_SIZE do local spawnY = (heightIndex - 1) * CUBE_SIZE end ``` > **Info:** The calculation for `spawnY` makes sure the first cube spawns at ground height and then moves over the space of a single cube every time. Ground level is at 0, which is why 1 needs to be subtracted from the `heightIndex`. 2. With the first loop for height finished, start on the second. Inside the first for loop, add a new for loop for where to place the cube along the length of the tower. - **Control variable**: lengthIndex = 1 - **End point**: `TOWER_SIZE` - Inside that loop add: local spawnX = lengthIndex * CUBE_SIZE```lua for heightIndex = 1, TOWER_SIZE do local spawnY = (heightIndex - 1) * CUBE_SIZE for lengthIndex = 1, TOWER_SIZE do local spawnX = lengthIndex * CUBE_SIZE end end ``` > **Warning:** When working with nested loops, it's important to make sure code is well indented. This helps make your code readable and organized. 3. Inside the **second** loop, add a **third** for loop for the tower **width**. In this final loop, call `makeCube()` and pass in the X,Y, Z parameters. - **Control variable**: `widthIndex = 1` - **End point**: `TOWER_SIZE` - Inside the loop add: - `local spawnZ = widthIndex * CUBE_SIZE` - `makeCube(spawnX, spawnY, spawnZ)` - A wait time of 0.25 so you can watch the tower be built.```lua -- Builds tower for heightIndex = 1, TOWER_SIZE do local spawnY = (heightIndex - 1) * CUBE_SIZE for lengthIndex = 1, TOWER_SIZE do local spawnX = lengthIndex * CUBE_SIZE for widthIndex = 1, TOWER_SIZE do local spawnZ = widthIndex * CUBE_SIZE makeCube(spawnX, spawnY, spawnZ) task.wait(0.25) end end end ``` 4. To have each floor be a random color, change `currentColor` to random `RGB` numbers in the same loop in which you create a new floor.```lua for heightIndex = 1, TOWER_SIZE do local spawnY = (heightIndex - 1) * CUBE_SIZE currentColor = Color3.fromRGB(math.random(0, 255), math.random(0, 255), math.random(0, 255)) for lengthIndex = 1, TOWER_SIZE do local spawnX = lengthIndex * CUBE_SIZE for widthIndex = 1, TOWER_SIZE do local spawnZ = widthIndex * CUBE_SIZE makeCube(spawnX, spawnY, spawnZ) task.wait(0.25) end end end ``` 5. Run the project and wait to see that a full tower has been created without any errors in the Output window. ## Optional challenges Below are different self-directed challenges that use nested loops in different ways. Try and code on your own before looking at the solution. ### Fade away parts As the tower is built, have the parts fade in transparency from left to right. The code solution is below. ```lua local TOWER_SIZE = 6 local CUBE_SIZE = 2 -- Creates individual cubes local function makeCube(spawnX, spawnY, spawnZ) local cube = Instance.new("Part") cube.Size = Vector3.new(CUBE_SIZE, CUBE_SIZE, CUBE_SIZE) cube.Color = currentColor cube.Transparency = cubeTransparency -- Sets transparency cube.CFrame = CFrame.new(spawnX, spawnY, spawnZ) cube.Parent = workspace end -- Builds tower for heightIndex = 1, TOWER_SIZE do local spawnY = (heightIndex - 1) * CUBE_SIZE currentColor = Color3.fromRGB(math.random(0, 255), math.random(0, 255), math.random(0, 255)) for lengthIndex = 1, TOWER_SIZE do local spawnX = lengthIndex * CUBE_SIZE cubeTransparency = (lengthIndex - 1) * 0.10 --Updates every loop starting at 0 for widthIndex = 1, TOWER_SIZE do local spawnZ = widthIndex * CUBE_SIZE makeCube(spawnX, spawnY, spawnZ) task.wait(0.05) end end end ``` ### Rain down objects Instead of parts, try spawning an actual object. The example here used cupcakes. On your own, see if you can: - Create an object from base parts. Make sure to weld all parts together so the object doesn't fall apart. - Place the object in ServerStorage - Modify the PartMaker found in the [Nested Loop Example](#nested-loop-example)to use your object instead of parts. A sample is shown here. A code solution using cupcakes is included. ```lua local numberOfBatches = 30 local cupcakesPerBatch = 6 local cupcakesBaked = 0 --Makes a single cupcake local function makeCupcake() local ServerStorage = game:GetService("ServerStorage") local cupcake = ServerStorage.Cupcake:Clone() local cup = cupcake.Cup local frosting = cupcake.Frosting cupcake:SetPrimaryPartCFrame(CFrame.new(0, 20, 0) * CFrame.Angles(0, 0, -90)) frosting.Color = frostingColor cup.Color = cupColor cupcake.Parent = workspace end -- Outer loop for cupcakeBatch = 1, numberOfBatches do print("Top outer loop: cupcake batch " .. cupcakeBatch) frostingColor = Color3.fromRGB(math.random(0, 255), math.random(0, 255), math.random(0, 255)) cupColor = Color3.fromRGB(math.random(0, 255), math.random(0, 255), math.random(0, 255)) -- Inner loop for cupcakeNumber = 1, cupcakesPerBatch do makeCupcake() print("Inner loop: cupcake " .. cupcakeNumber) -- Track muffins baked cupcakesBaked += 1 task.wait(0.5) end print("Bottom outer loop: " .. cupcakesBaked .. " cupcakes baked so far.") end ``` ## Summary To accomplish more complex tasks, coders will find it helpful to combine multiple loops, and even different types of loops. All loops can be nested, meaning that one loop is inside another. Nested loops follow the same logic as any other loop. It starts in the first, outermost loop, runs tasks through inner loops, and then cycles back to the first loop if applicable. --- title: "Repeat code with while loops" url: /docs/en-us/tutorials/fundamentals/coding-4/repeat-code-with-while-loops last_updated: 2026-06-29T19:34:14Z description: "Learn how to use while loops with Roblox's Luau language in this computer science lesson." --- # Repeat code with while loops Many things in computer science or game development often repeat. For instance, an app might update content in a feed every few seconds, or a game might reward players a prize every day they're logged in. To accomplish this behavior, coders often use **loops**. In computer science, a **loop** is a coding pattern that repeats a set of instructions, often until a specific condition is met. To practice this, you'll code a part that changes colors indefinitely. Later lessons will show how to stop the looping. ## Set up the part and script Use any Roblox project for this exercise. When finished, this part can be used as a way of decorating an environment. 1. Create a new part with a descriptive name. This lesson uses **LoopingPart**. 2. In **ServerScriptService**, add a new script named LoopingScript. 3. In the script, delete `Hello World` and write a **comment** stating the purpose of this script.```lua -- Changes the color of LoopingPart every few seconds ``` ## Use variables to store a part Variables can also hold references to objects besides strings. For example, when a script knows the reference to a part, the script can change it. Rather than typing `workspace.NameOfYourPart` over and over, that entire line can be stored inside a local variable. Local variables are created by typing `local` and then the name of the variable. For example: `local nameOfVariable`. > **Info:** A local variable only works in the script where it was created. If a local variable named `part1` is created in Script1, it cannot be used in Script2. Most variables you use will be local variables. 1. Under the comment, create a local variable by typing `local loopingPart`.```lua -- Changes the color of LoopingPart every few seconds local loopingPart ``` 2. Set the variable to the looping part by typing `= workspace.LoopingPart` on the same line. Remember that the `=` operator sets the value of a variable to whatever is on the right of it.```lua -- Changes the color of LoopingPart every few seconds local loopingPart = workspace.LoopingPart ``` > **Info:** Remember that the `=` operator sets the value of a variable to whatever is on the right of it. ## While loops Before creating the while loop for the color changing part, it's worthwhile to explain its syntax. A while loop has three components: - The `while` keyword. - A condition, always after the keyword. - Instructions, between the `do` and `end` keywords. In loops, conditions can be a variety of logical statements. Some examples might be if two numbers are the same. A loop will run if its condition is true. Notice in the sample below, the condition is true, meaning this loop will run forever. In later lessons, you'll code specific conditions that stop. ```lua while true do print("This loop is running") end ``` ## Code a while loop Next, you'll set up the while loop for the color changing part. 1. On the next line type `while true do`. Then, press `Enter` to autocomplete and add the word `end`.```lua -- Changes the color of loopingPart every few seconds local loopingPart = workspace.LoopingPart -- Looping Code while true do end ``` > **Info:** You might have noticed the editor automatically added `end` to the script and indented the next line of code. Indenting makes code easier to read. Instructions in a loop should **always** be indented. ### Add code in the loop With the while loop created, instructions can be added to change the part's color over time. Inside the loop, add a line of code for each color. Each color will use RGB values, a way of storing colors as numbers in computer science. 1. Between `while true do` and `end`, set the Color property of the part to `Datatype.Color3.fromRGB()`.```lua local loopingPart = workspace.LoopingPart while true do loopingPart.Color = Color3.fromRGB() end ``` 2. Setting the color can be done using a color picker in Studio. To do so, left click inside the `()` next to `fromRGB`. Then, click on the color wheel icon. Once you have a desired color, press OK to automatically add the color value in the code.```lua local loopingPart = workspace.LoopingPart while true do loopingPart.Color = Color3.fromRGB(82, 227, 255) end ``` > **Info:** If desired, a color can be created manually by assigning a value in each number from 0 to 255. ### Make the script wait If you add a second line of color changing code right now, it would change the brick's color so fast you might not even see the first color go by. To make the script wait before running the next line of code, use a `Library.task.wait()` function. 1. On the line after changing the brick color, type `task.wait(3)`. Whatever number placed inside the `()` is how many seconds the script will wait.```lua local loopingPart = workspace.LoopingPart while true do loopingPart.Color = Color3.fromRGB(82, 227, 255) task.wait(3) end ``` 2. Under the wait function, repeat the same process of adding a new color. Finish it with an additional wait function.```lua while true do loopingPart.Color = Color3.fromRGB(82, 227, 255) task.wait(3) loopingPart.Color = Color3.fromRGB(177, 52, 255) task.wait(3) end ``` > **Warning:** As you add more code in the loop, make sure it's all in **scope**, meaning code is in the right place. In order for code to run in the loop, it has to be between the `while true do` and before `end`. 3. Test the code and see if the colors change. **Troubleshooting Notes** At this point, if the color changing part doesn't work as intended, try one of the following below. **Issue**: Colors are skipped - Make sure you have a wait function between each color change, especially at the last line. - Check that all the color change and wait code is between the `while true do` and `end` sections. **Issue**: Part is still gray or doesn't change color as intended - Make sure all RGB values have numbers from 0 to 255, are whole or decimals, and are separated by commas. - The numbers inside the `()` of a wait function should be greater than 1. It's possible that if a wait is too short, you may not see that color. ## Complete color looping part script ```lua -- Create a variable to store the part local loopingPart = workspace.LoopingPart -- Looping Code while true do -- Changes loopingPart's color loopingPart.Color = Color3.fromRGB(82, 227, 255) -- Wait 3 seconds before next instruction task.wait(3) loopingPart.Color = Color3.fromRGB(177, 52, 255) task.wait(3) end ``` ## Summary Loops are a common element in most computer languages. They are used to repeat instructions, sometimes until specific conditions are met. In this article, the while loop is used to repeat instructions forever. To create a while loop that repeats forever, use the syntax below, being sure to include instructions between the `do` and `end` keywords. ```lua while true do -- Instructions end ``` --- title: "Intro to arrays" url: /docs/en-us/tutorials/fundamentals/coding-5/intro-to-arrays last_updated: 2026-06-29T19:34:14Z description: "Learn how to use arrays in Luau to store multiple values at a time. This tutorial shows how to create and change arrays while making an interactive NPC in Roblox." --- # Intro to arrays **Data structures** are how coders store and organize entire sets of data. In Luau, data structures are created with tables. **Tables** can hold any number of values. This article covers using **arrays**, a specific table type, to create a talking character. ## Tables **Tables** are data types that can hold multiple values. Unlike other data types that store a single value, tables don't have a fixed size and can hold a mix of different value types. With tables, you can store items in a player's inventory or create a list of thousands of player names. ### Arrays There are different types of tables. One type is an **array**, which stores lists of values in a specific order. To create an array, create a variable and assign it curly brackets `{ }`. Separate values within the brackets with commas like below: `local myArray = {"item1", "item2", 10, workspace.Part, myVariable}` ### Create a talking character To explore arrays, you'll work with a non-playable character (NPC) that, when clicked, shows a different line of dialogue. This project will use a pre-made NPC model, which includes a partial script and Prompt Detector but lacks dialogue. 1. Download the template NPC. 2. In Explorer, import the NPC by right-clicking on **Workspace** ⟩ **Insert From File** and select the downloaded file. ## Code a dialogue array These steps use an array to store different phrases for the NPC to say when players interact with it. 1. In the **Explorer**, go to **NPC** ⟩ **ProximityPrompt** ⟩ **ChatManager**. 2. In ChatManager, where marked in the script, create an **empty array** to store dialogue options.```lua -- Cycles through chat dialogue when prompt is used local Chat = game:GetService("Chat") local prompt = script.Parent local npc = prompt.Parent local characterParts = npc.CharacterParts local head = characterParts.Head -- Add array here local dialogueArray = {} local function speak() local dialogue = "I've got one thing to say!" Chat:Chat(head, dialogue) end prompt.Triggered:Connect(speak) ``` > **Warning:** This lesson uses the legacy chat API and is strictly for demonstrating how to use arrays. In real experiences, use `Class.TextChatService`. 3. Within the brackets `{}` of the array just created, type at least three strings of dialogue, separated by commas.```lua local dialogueArray = {"Hi!", "Do I know you?", "Goodbye!"} ``` ## Use array indexes Each value in the array is assigned an `index` number. Indexes are assigned to values in the order in which the values are stored. The first value is at index 1, the second at index 2, and so forth. Some coding languages, like Java, start indexes at 0. In the array just created, `"Hi"` is at index 1, and `"Goodbye!"` is at index 3. | Index | Value | | --- | --- | | 1 | Hi | | 2 | Today's a great day! | | 3 | Good bye! | ## Use specific index values Use index values to assign specific pieces of dialogue to the NPC. To use a value at a specific index, add the index in brackets directly after the array's name, like `dialogueArray[1]`. 1. Replace the `dialogue` variable's default string value with index 2.```lua local dialogueArray = {"Hi!", "Do I know you?", "Goodbye!"} local function speak() local dialogue = dialogueArray[2] Chat:Chat(head, dialogue) end ``` > **Warning:** If an index doesn't exist, a `nil` error will display in Output. For example, since this array only has three values, using `dialogueArray[4]` will result in an error. 2. Playtest and click the NPC. The second array value should appear in the chat bubble. Try changing the code to test out each value in the table. ## Change dialogue lines When the player interacts with the NPC, the NPC will always say the same line. That's boring. Instead, use a variable to update which index value to use. Whenever a player interacts with the NPC, increment the variable value by 1 to display the following line of dialogue. 1. To keep track of the current index, add a new variable named `dialogueIndex`. Set the variable to 1 to start at the beginning of the array.```lua local dialogueArray = {"Hi!", "Do I know you?", "Goodbye!"} local dialogueIndex = 1 ``` 2. In `speak()`, **replace** the index number in `dialogueArray[2]` with the variable you just created.```lua local function speak() local dialogue = dialogueArray[dialogueIndex] Chat:Chat(head, dialogue) end ``` 3. At the bottom of the function, add 1 to `dialogueIndex`. The next time `speak()` is called, the dialogue will display the next string.```lua local function speak() local dialogue = dialogueArray[dialogueIndex] Chat:Chat(head, dialogue) dialogueIndex += 1 end ``` 4. Playtest and click the NPC to see each dialogue string from the array. Notice there's an **error** in the Output window once the script reaches the end of the array. You'll fix this in the next section so the dialogue restarts from the beginning after it shows the last string. ## Array sizes You can use the size of the array to know when to reset the desired index to 1. Find the **size** of an array by typing `#`, without spaces, before an array's name. For example: `#dialogueArray` Check the array's size against the variable's current value to know when it's time to start back at the beginning. ### Restart the dialogue Use the array size to check when it's time to cycle back to the first piece of dialogue. 1. Add an if statement and check if `dialogueIndex` equals `#dialogueArray`, the total size of this array. If so, then set `dialogueIndex` to 1.```lua local function speak() local dialogue = dialogueArray[dialogueIndex] Chat:Chat(head, dialogue) if dialogueIndex == #dialogueArray then dialogueIndex = 1 end dialogueIndex += 1 end ``` 2. If `dialogueIndex` isn't at the end, it should still add 1 to `dialogueIndex`. Move `dialogueIndex += 1` under an else statement.```lua local function speak() local dialogue = dialogueArray[dialogueIndex] Chat:Chat(head, dialogue) dialogueIndex = if dialogueIndex == #dialogueArray then 1 else dialogueIndex + 1 end ``` 3. Play and confirm you can cycle through and restart the dialogue. ## Summary Data structures are how sets of data are stored. Luau uses tables to create data structures. Arrays are a type of table that can hold ordered lists of information. Each value within the array is assigned an index number, starting with index 1. This script used an array to create a list of possible dialogue lines for a Non-Playable Character (NPC). ```lua -- Cycles through chat dialogue when prompt is used local Chat = game:GetService("Chat") local prompt = script.Parent local npc = prompt.Parent local characterParts = npc.CharacterParts local head = characterParts.Head -- Add array here local dialogueArray = {"Hi!", "Do I know you?", "Goodbye!"} local dialogueIndex = 1 local function speak() local dialogue = dialogueArray[dialogueIndex] Chat:Chat(head, dialogue) dialogueIndex = if dialogueIndex == #dialogueArray then 1 else dialogueIndex + 1 end prompt.Triggered:Connect(speak) ``` ### Troubleshooting tips If the character doesn't go through the array of dialogue, try the following troubleshooting tips. - Check the if statement that `dialogueIndex` is set back to 1. In the else statement, check that `dialogueIndex` has 1 added to itself. - When getting the array's size, ensure there are no spaces after the # in `#dialogueArray`. ## Optional challenges Try one of the optional challenges below. - Code the script so the NPC's dialogue goes backward through the array. The dialogue index should start at the array length and subtract each time instead of adding. - Instead of showing dialogue in order, have the NPC show a random line of dialogue each time using `Datatype.Random.new()`. A sample script is included below to reference ```lua local randomGenerator = Random.new() -- Shows a new dialogue whenever the NPC is clicked local function speak() local randomIndex = randomGenerator:NextInteger(1, #dialogueArray) local dialogue = dialogueArray[randomIndex] Chat:Chat(head, dialogue) end ``` --- title: "Intro to dictionaries" url: /docs/en-us/tutorials/fundamentals/coding-5/intro-to-dictionaries last_updated: 2026-06-29T19:34:14Z description: "Learn how to use a dictionary table to tag values within data sets." --- # Intro to dictionaries Dictionaries are tables that associate names or **keys** with a value instead of an index. Example: ```lua local pet = { Name = "Bobbie", Type = "Dog", } ``` Use dictionaries when you need to label values, not just list them in order as an array does. Practice using dictionaries in this tutorial by manipulating values associated with a player. ## Dictionary syntax Like arrays, dictionaries are assigned to a variable with curly brackets `{}`. **Key-value pairs** are stored on separate lines followed by a comma. Keys and values can be any data type, including strings, numbers, and variable names. ```lua local playerNames = { player1 = "Zap", player2 = "Kel", } print(playerNames["player1"]) ``` To reference parts or other instanced objects as keys, use brackets. ```lua local greenPart = workspace.GreenPart local redPart = workspace.RedPart local partList = { [greenPart] = true, [redPart] = false, } print(partList[redPart]) ``` > **Warning:** Use consistent data types for dictionary keys. Mixing data types, such as using both strings and variables for keys, can lead to inconsistent results when manipulating the array and confuse other coders. ## Create a dictionary One everyday use of dictionaries is organizing player or character information. These steps explore how a theoretical enemy character's information can be stored and accessed. 1. In a new script, create a dictionary named `enemy`.```lua local enemy = { } ``` 2. The first key in the dictionary will track the enemy's name with a variable called `Name`.```lua local enemy = { Name } ``` 3. Assign an enemy name to the key, followed by a comma.```lua local enemy = { Name = "Spike", } ``` 4. Add a second key-value pair for how much health the enemy should have. Remember, keys should always use the same data type, but values don't have to.```lua local enemy = { Name = "Spike", Health = 1000, } ``` ### Use dictionary values There are two ways to access dictionary values: - `tableName["keyName"]` (importantly, note the quotations) - `tableName.keyName` ```lua local enemy = { Name = "Spike", Health = 1000, } print("The villain " .. enemy["Name"] .. " approaches!") print("The villain " .. enemy.Name .. " approaches!") ``` Which style to use usually depends on the purpose of the table. For tables holding a collection of values like a list of players in a server, coders will usually use `tableName["keyName"]`. For a dictionary used to describe an object, coders are more likely to use `tableName.keyName`. ## Change a dictionary value Changing a key's value is the same as any other variable; use the equal `=` operator. 1. Beneath the `enemy` table, set the enemy's name to something else.```lua local enemy = { Name = "Spike", Health = 1000, } enemy.Name = "Rana" print("The enemy's name is " .. enemy.Name) ``` 2. Playtest and check the Output window. ## Pre-existing variables as keys Dictionaries can interact with pre-existing variables declared in other parts of a script. The following coding example uses a variable to add a player's name as a key when they join the experience and then sets their points value to 0. 1. In **ServerScriptService**, create a new script named PlayerPoints. In the script, get the Players Service and create an empty dictionary named `playerPoints`.```lua local Players = game:GetService("Players") local playerPoints = { } ``` 2. Code a local function for setting player points with a parameter for a new player variable. Connect the function to the `Class.Players.PlayerAdded` event.```lua local playerPoints = { } local function setPoints(newPlayer) end Players.PlayerAdded:Connect(setPoints) ``` 3. In the function, add a variable to get the player's `Name`, a property in every **Player** object, and a print statement for testing.```lua local function setPoints(newPlayer) local name = newPlayer.Name print("hello " .. name) end ``` 4. Insert `name` into the `playerPoints` dictionary as a key, and set the value, the player's points, to 0.```lua local function setPoints(newPlayer) local name = newPlayer.Name print("hello " .. name) playerPoints[name] = 0 end ``` > **Warning:** Since `name` was created as a variable, it can be accessed with the actual variable name. If `name` were a key name, it would need to be accessed the same as other strings: `playerPoints["name"]`. 5. Use `name` to print the name of the player and `playerPoints[name]` to print the value of the key matching the variable.```lua local function setPoints(newPlayer) local name = newPlayer.Name print("hello " .. name) playerPoints[name] = 0 print(name .. " has " .. playerPoints[name] .. " points.") end ``` 6. Run the project and observe the output. ```lua local Players = game:GetService("Players") local playerPoints = { } local function setPoints(newPlayer) local name = newPlayer.Name print("hello " .. name) playerPoints[name] = 0 print(name .. " has " .. playerPoints[name] .. " points.") end Players.PlayerAdded:Connect(setPoints) ``` ### Optional challenges Below are some challenges that apply to using dictionaries in different ways. See if you can build out the code for these. - Create a trap part that does damage over time to a player. Once a player touches the trap, damage them, wait, then allow them to be damaged again. - Create a function that checks which of two players has the most points by accessing a dictionary. - Create a cipher, a system of swapping one string for another to create a "secret" code. For example, the letter "A" can be swapped with "G," or the word "apple" can be swapped with "orange." ## Dictionaries and pairs() `pairs()` is a function that's often used to iterate through **dictionaries**. An example is seen below. ```lua local myDictionary = { ["Blue Player"] = "Ana", ["Gold Player"] = "Binh", ["Red Player"] = "Cate", } for key, value in pairs(myDictionary) do print(key .. " is " .. value) end ``` `pairs()` can be used to work with a dictionary element's key, value, or both. In the for loop below, the first variable is the key. The second variable is the value. The dictionary that you want to work with is passed in to `pairs()`. ```lua local inventory = { ["Gold Bricks"] = 43, Carrots = 3, Torches = 2, } print("You have:") for itemName, itemValue in pairs(inventory) do print(itemValue, itemName) end ``` When executed, the code will print the following: ```lua You have: 43 Gold Bricks 3 Carrots 2 Torches ``` ## Summary Dictionaries are tables that use key-value pairs instead of indexed values. Dictionaries and arrays start similarly by assigning curly brackets to a variable. Keep entries separated with commas. All keys within a dictionary should use the same data type, but the values can mix data types without issue. The way in which a dictionary is accessed can convey its purpose. A dictionary of enemy properties will likely be accessed with the dot operator, while a list of names will likely use `tableName[keyName]`. When using brackets, be careful; key names created within the table must be treated as strings: `tableName["keyName"]`. However, when referencing objects like parts, the quotations are not needed: `tableName[keyName]`. Many scripts need to go through dictionaries or arrays, but you may not always know the start and end of these data structures. For example, a dictionary of players may be changing, or players may have an array inventory of varying sizes. In these situations, you can use `pairs()` and `ipairs()` in a `for` loop to go through each element without known start and end points. You can also use general iteration through the `in` keyword to loop through elements without the need for `pairs()` or `ipairs()`. --- title: "Arrays and dictionaries" url: /docs/en-us/tutorials/fundamentals/coding-5/landing last_updated: 2026-06-29T19:34:14Z description: "Learn how to store data in tables with arrays and dictionaries in Luau." --- # Arrays and dictionaries ### Series description Data structures are how coders store and organize entire sets of data. In Luau, data structures are tables. Unlike variables, tables can hold any number of values. Tables can store a list of high scores, a player's inventory, or all of an object's properties. Two types of tables covered in this series are **dictionaries** and **arrays**. ### Objectives and prerequisites | **Learning Objectives** | Learn common structures for storing sets of information, such as arrays and dictionaries, and use cases for each structure. Be able to manipulate data structures, such as adding, removing, or returning values from a structure like an array. | | --- | --- | | **Prerequisites** | Have a general understanding of variables in Luau. Feel comfortable using conditional statements (`if` and `else`) and loops (`for` and `while`). | ### Series contents | Article | Description | | --- | --- | | [Intro to arrays](/docs/en-us/tutorials/fundamentals/coding-5/intro-to-arrays.md) | Learn how to store like groupings of data together with arrays. | | [Loops and arrays](/docs/en-us/tutorials/fundamentals/coding-5/loops-and-arrays.md) | Combine arrays with loops to repeat code with a disappearing path trap. | | [Make changes to arrays](/docs/en-us/tutorials/fundamentals/coding-5/make-changes-to-arrays.md) | Add and remove values in arrays by coding a script to manage a player's inventory | | [Intro to dictionaries](/docs/en-us/tutorials/fundamentals/coding-5/intro-to-dictionaries.md) | Organize complex sets of data with pairs using dictionaries. | --- title: "Loops and arrays" url: /docs/en-us/tutorials/fundamentals/coding-5/loops-and-arrays last_updated: 2026-06-29T19:34:14Z description: "Learn how to iterate, or go through, arrays in Luau using loops." --- # Loops and arrays **Arrays** can be combined with loops, such as **while** and **for**, to repeat the same code for each indexed value. For example, teleporting each player in an array to a new place or making an array of parts catch fire. To explore looping through arrays, create a disappearing bridge path where parts are placed into an array and then made to vanish one by one. ## Loop through an array For the project, find or create a set of three parts to make disappear. The parts must all be anchored. 1. Create three parts. Name the parts in the order they should disappear, Part1, Part2, Part3. 2. In ServerScriptService, insert a new script and create an array to store the parts.```lua local pathArray = { } ``` 3. On a single line each, type the name of the parts in the order they should disappear. Using a new line for each value makes it easier to read.```lua local pathArray = { workspace.Part1, workspace.Part2, workspace.Part3, } ``` ### Use a for loop with an array Use a for loop to go through the array and control how fast the parts should disappear in the order the parts are indexed within the array. 1. To control how often a part disappears in seconds, create a variable named `VANISH_RATE`. For testing, it's best to keep it to a small number.```lua local VANISH_RATE = 1.5 local pathArray = { workspace.Part1, workspace.Part2, workspace.Part3 } ``` > **Info:****Constants** are variables that shouldn't be changed by any other code in the script. They're likely to need adjusting and are put at the top of the script in `ALL_CAPS` to make them easy to spot. 2. Create a for loop with the following: **Start:** `partIndex = 1`, the index of the first value in the array. **End:** `#pathArray`, the size of that array.```lua for partIndex = 1, #pathArray do end ``` 3. So there's a delay before a part disappears, in the loop, add a `Library.task.wait()` using `VANISH_RATE`.```lua for partIndex = 1, #pathArray do task.wait(VANISH_RATE) end ``` 4. To get a part to disappear, create a new variable named `whichPart` and set it equal to `partsArray[partIndex]`. Then, to disappear that part, set it's `CanCollide` property to false and `Transparency` to 1.```lua for partIndex = 1, #pathArray do task.wait(VANISH_RATE) local whichPart = pathArray[partIndex] whichPart.CanCollide = false whichPart.Transparency = 1 end ``` 5. Check that parts of the path disappear over time. ### Troubleshooting tips If the bridge doesn't disappear, check the possible issues below: **Issue:** Parts disappear too fast or are all gone at the start. - Depending on how fast your character loads into the experience, the first parts may already be invisible. To address this, add a small wait, such as `task.wait(2)`, at the start of the script. ### Code the second loop Right now, the parts disappear forever. To make them reappear, create a second for loop that will go through each part and instantly make each piece walkable. 1. After the first loop, add a `Library.task.wait()` to create a short delay before the path reappears.```lua for partIndex = 1, #pathArray do task.wait(VANISH_RATE) local whichPart = pathArray[partIndex] whichPart.CanCollide = false whichPart.Transparency = 1 end task.wait(1) ``` 2. **On your own**, try coding a second for loop that makes the path usable again by changing each part's CanCollide property to true and Transparency to 0. When finished, check your work against the code below.```lua -- Reset the path by making all parts walkable again for partIndex = 1, #pathArray do local whichPart = pathArray[partIndex] whichPart.CanCollide = true whichPart.Transparency = 0 end ``` > **Warning:** Unanchored parts will fall through the baseplate when CanCollide is false. If the parts aren't reappearing, and there are no Output errors, anchor the parts and test again. 3. Test the project to confirm that once all parts disappear, they come back. ## Repeat with a while loop The parts disappear and reappear, but only once. To make the code keep repeating, nest all of the code within a while loop. 1. At the bottom of the script, create a new while true do loop. Then, **move both** for loops into the while loop.```lua while true do -- Make a part disappear from the array in order for partIndex = 1, #pathArray do task.wait(VANISH_RATE) local whichPart = pathArray[partIndex] whichPart.CanCollide = false whichPart.Transparency = 1 end -- Wait for a second before making the path reappear task.wait(1) -- Reset the path by making all parts walkable again for partIndex = 1, #pathArray do local whichPart = pathArray[partIndex] whichPart.CanCollide = true whichPart.Transparency = 0 end end ``` 2. Check that once all parts disappear, they reappear. A finished version of the project can be downloaded. ```lua local VANISH_RATE = 1.0 local pathArray = { workspace.Part1, workspace.Part2, workspace.Part3, } while true do -- Make a part disappear from the array in order for partIndex = 1, #pathArray do task.wait(VANISH_RATE) local whichPart = pathArray[partIndex] whichPart.CanCollide = false whichPart.Transparency = 1 end -- Wait for a second before making the path reappear task.wait(1.0) -- Reset the path by making all parts walkable again for partIndex = 1, #pathArray do local whichPart = pathArray[partIndex] whichPart.CanCollide = true whichPart.Transparency = 0 end end ``` ## Arrays and ipairs() `ipairs()` is used with **arrays**. The "i" in `ipairs()` stands for "index." ipairs() can be used to quickly repeat an operation on a lot of objects, like, say, a folder full of parts. This will be demonstrated by quickly adding particles to a whole folder of parts. 1. In Workspace, create a folder named PartsFolder. Add as many parts as you like. 2. In ServerScriptService, Create a new script and reference the folder. 3. Use GetChildren() to automatically get an array listing all the objects in the folder.```lua local partsFolder = workspace.PartsFolder -- Gets an array listing the parts in PartsFolder local partsArray = partsFolder:GetChildren() ``` 4. Use `in ipairs(ArrayToUse)` with the for loop to go through partsArray and add particles.```lua -- Gets an array listing the parts in PartsFolder local partsArray = partsFolder:GetChildren() -- Adds particles to every part in the array for index, part in ipairs(partsArray) do local particles = Instance.new("ParticleEmitter") particles.Parent = part end ``` Playtest and watch the particles float up from every part in the folder. ## Summary Loops can be combined with arrays to work with any number of values quickly. If you want to have exact control over what happens to a few parts and in what order, an array must be hard-coded with specific values. To work with many parts where order doesn't matter, GetChildren() can create an array of an object's children. Utilizing `ipairs()`, the list can be quickly iterated over without knowing the array's size. --- title: "Make changes to arrays" url: /docs/en-us/tutorials/fundamentals/coding-5/make-changes-to-arrays last_updated: 2026-06-29T19:34:14Z description: "Learn how to add, remove, and find values in an array in Luau using tables." --- # Make changes to arrays An array's contents will often have to be changed, like when needing to remove an item from a player's inventory. Luau has **built-in functions** for use with tables to make this easier. This article will outline prebuilt functions for tables like add and remove while describing how to search for a value. To apply these skills, you'll create a script that helps to manage a player's inventory. ## Add values to arrays To add a new value to an array, use `table.insert(array, valueToInsert)`. The second parameter can be any value such as a string, number, or entire object, like `Class.Player` or `Class.IntValue`. To practice, you'll create a script that stores player items in a table, then add to it. Since this example is just a script, it can be used with any file or project. Testing the code will be done using the Output window. 1. Create an empty array named `playerItems`.```lua playerItems = {} ``` 2. Add inventory items to the array by typing `Library.table.insert()`. In the parenthesis, type `playerItems`, the table to add to, followed by a string for each item. Insert at least three values.```lua playerItems = {} table.insert(playerItems, "Potion") table.insert(playerItems, "Bread") table.insert(playerItems, "Sleeping Bag") print(playerItems) ``` 3. Run the project. In Output, expand the three dots `...` to see the printed table. ## Remove values from arrays To remove a value, like if a player used an item or someone in a list of active players leaves an experience, use `Library.table.remove()`. Depending on the parameters provided, the function can either remove the last value of a table, or at a specific index. ### Remove the last value Sometimes a script needs to remove a specific item. For instance, removing the first item in a player's inventory, or picking the first player in a list. To remove the last value in an array, use `Library.table.remove(arrayName)`. In this use case, the only parameter needed is the table itself. 1. Use the array of items from the previous example. Then, to remove the last value from the table, in this case `"Sleeping Bag"`, type `Library.table.remove(playerItems)````lua playerItems = {} table.insert(playerItems, "Potion") table.insert(playerItems, "Bread") table.insert(playerItems, "Sleeping bag") table.remove(playerItems) print(playerItems) ``` 2. Run the project. In the Output window, the last value, `"Sleeping Bag"`, shouldn't print. ### Remove by index To remove a value at a specific point in the array, input in the second parameter the index to remove, such as `table.remove(arrayName, 1)`. 1. Using the same table, type `Library.table.remove()`. In the parameters, input the name of the table and `1`, the value to remove.```lua playerItems = {} table.insert(playerItems, "Potion") table.insert(playerItems, "Bread") table.insert(playerItems, "Sleeping bag") table.remove(playerItems, 1) ``` > **Warning:** The second parameter for `Library.table.remove()` only accepts a numerical index. Typing something like `"Potion"` will create an error. 2. Run the project. Check that the first value, "Potion" was removed. > **Info:** Notice also that all the index values changed or have been pushed. Removing a value from an array will change the array size and shift index values **down** after the one removed. `"Bread"` is now index 1, and `"Sleeping Bag"` is now index 2. ## Search for values in an array To find specific values in arrays, like the name of a winning player, use the `Library.table.find()` function. Alternatively, you can code your own search function using `for` loops and `if` statements. ### Find and return a single value To find a value in an array, create a function named `findValue()` that goes through an array and stops the first time it finds a matching value. Once it finds the value, the function will use the `return` keyword to return that value's index. With an index, you can remove it from the array. 1. Copy the array named `playerItems` below.```lua local playerItems = { "Potion", "Bread", "Bread", "Sleeping Bag" } ``` 2. Code a new function named findValue() with two parameters: - `whichArray` - the array to search through. - `itemName` - the specific string to check for.```lua local function findValue(whichArray, itemName) end ``` 3. In `findValue()`, use a for loop to check if any value in the array matches `itemName`. If the value matches, use the keyword `return` to return the value found in `currentIndex`.```lua local function findValue(whichArray, itemName) for currentIndex = 1, #whichArray do if whichArray[currentIndex] == itemName then return currentIndex end end end ``` 4. Test the search function by creating a variable named `valueFound` and calling `findValue()`. Pass in the array to search, and what value it should search for. **Run** the code to confirm that the expected output is index 2.```lua local function findValue(whichArray, itemName) for currentIndex = 1, #whichArray do if whichArray[currentIndex] == itemName then --Sends the value of currentIndex back return currentIndex end end end local valueFound = findValue(playerItems, "Bread") print("The value is at index " .. valueFound) ``` ### Remove a value If a value was found using the find function, it can be removed. Check if a value was found with an if statement. 1. Check if a value is inside `valueFound`; if so, remove the value using `Library.table.remove()`.```lua if valueFound then table.remove(playerItems, valueFound) end ``` 2. Print out the array using the code below.```lua for index = 1, #playerItems do local itemString = playerItems[index] print("Index " .. index .. ": " .. itemString) end ``` 3. Playtest and check that the first `"Bread"` value was removed from the array. Try removing other values by changing the second parameter in `findValue()`. Notice that because this function was called once, only the first instance of `"Bread"` was removed. The following section will cover how to find and remove all instances. ## Find and remove all of a specific value While the previous code could only remove the first instance of a value found, this code snippet will find and remove all occurrences from an array. For example, if, say, a player wanted to sell all their bread at an in-game store. 1. Use an array named `playerItems` with at least four values and a set of **duplicates**.```lua local playerItems = { "Potion", "Bread", "Bread", "Sleeping Bag" } ``` 2. To go through the array, create a for loop that goes **backward** through `playerItems`, starting at `#playerItems`, ending at 1, and incrementing by -1.```lua for index = #playerItems, 1, -1 do end ``` > **Warning:** Remember, removing items causes later indexes to shift. By starting at the end, you avoid skipping a value that may have shifted if you started at the beginning. 3. In the loop, use an if statement to check if the value inside `playerItems[index]` is equal to `"Bread"`, and if so, remove the item.```lua for index = #playerItems, 1, -1 do if playerItems[index] == "Bread" then table.remove(playerItems, index) end end ``` 4. Use the code below to add a second for loop that prints the array.```lua for index = 1, #playerItems do local itemString = playerItems[index] print("Index " .. index .. ": " .. itemString) end ``` 5. Run the script and check that all values named "Bread" are removed. ## Summary Values can be removed or added to an array, but be careful of indexes shifting doing so. Use loops to iterate through a table to remove all instances of a value, or just the first instance found. The keyword `return` can be used to stop a loop, and send information back where needed. A finished version of the script can be referenced below. The following project includes all scripts in this tutorial. Download [here](../../../assets/education/coding-5/Making_Changes_to_Arrays_-_Final_Project.rbxl). Note all scripts are in **ServerScriptService** and **disabled**. To use a script, in its properties, uncheck the Disabled field and run Studio. > **Info:** For complete list of all table functions, go to `Library.table|Table API` --- title: "pairs and ipairs" url: /docs/en-us/tutorials/fundamentals/coding-5/pairs-and-ipairs last_updated: 2026-06-29T19:34:14Z description: "pairs() and ipairs() are used to go through Luau tables. This lesson covers how to code them for a script and when to use them." --- # pairs and ipairs Many scripts will often need to go through dictionaries or arrays. But sometimes, you may not know the start and end of these data structures. For example, a dictionary of players may be changing, or players may have an array inventory of varying sizes. In these situations, you can use `pairs()` and `ipairs()`. These can be used with a for loop to go through each element without known start and end points. This article will cover using them, and have a practical script example where you track restaurant orders in a cooking game. ## Dictionaries and pairs() `pairs()` is used with **dictionaries**. An example is seen below. ```lua local myDictionary = { ["Blue Player"] = "Ana", ["Gold Player"] = "Binh", ["Red Player"] = "Cate", } for key, value in pairs(myDictionary) do print(key .. " is " .. value) end ``` `pairs()` can be used to work with a dictionary element's key, value, or both. In the for loop below, the first variable is the key. The second variable is the value. The dictionary that you want to work with is passed in to `pairs()`. ```lua local inventory = { ["Gold Bricks"] = 43, Carrots = 3, Torches = 2, } print("You have:") for itemName, itemValue in pairs(inventory) do print(itemValue, itemName) end ``` When executed, the code will print the following: ```lua You have: 43 Gold Bricks 3 Carrots 2 Torches ``` ## Arrays and ipairs() `ipairs()` is used with **arrays**. The "i" in `ipairs()` stands for "index." Use ipairs() and arrays in combination with a for loop to work with ordered values like leaderboards. An example is below. ```lua local players = {"Ali", "Ben", "Cammy"} for playerRank, playerName in ipairs(players) do print("Winner #" .. playerRank .. " is " .. playerName) end ``` When executed, the code will print the following: ```lua Winner #1 is Ali Winner #2 is Ben Winner #3 is Cammy ``` ## Practice - Restaurant Soup Simulator Let's make some soup for a restaurant simulator where players pick from a list of possible soup ingredients. The ingredients will be stored as keys, and the values will start off as false so the players only get what they select. Use `pairs()` to see what was picked, and then `ipairs()` to print the list of ingredients. 1. Create a new **dictionary** named `menu`. Add 3 - 5 key-value pairs where the key is the ingredient and the value is false.```lua local menu = { cabbage = false, beef = false, tomato = false, noodles = false, } ``` 2. Beneath the menu dictionary, add an **empty array** which will be filled with the customer's choices later.```lua -- Customer's soup local selectedIngredients = {} ``` 3. Use `pairs()` to check if each ingredient in the dictionary is marked true or false. If true, add the ingredient to soup.```lua -- Customer's soup local selectedIngredients = {} -- Adds customer's choices to their soup for menuChoice, value in pairs(menu) do if value then table.insert(selectedIngredients, menuChoice) end end ``` 4. Repeat the order back to the customer. In the script, code the following below. - Check if there is a menu item in `selectedIngredients`. If so, print `"You ordered soup with: "`. - Use `ipairs()` to go through the `selectedIngredients` array and print each ingredient. - Test by changing at least one menu item to true.```lua -- Prints soup order from "selectedIngredients" if #selectedIngredients > 0 then print("You ordered soup with: ") for index, soupIngredient in ipairs(selectedIngredients) do print(soupIngredient) end end ``` 5. In the if statement that checks if there is a menu item, add an else condition which tells customers if no ingredients were selected.```lua if #selectedIngredients > 0 then print("You ordered soup with: ") for index, soupIngredient in ipairs(selectedIngredients) do print(soupIngredient) end else print("Nothing was selected. Are you not hungry?") end ``` > **Info:** The above step is an example of error checking in computer science. It's always a good practice to code what happens if there's a missing value possible in your code. If not, it's possible there might be an error during run time. ## Optional challenges Below are some challenges that apply using pairs and ipairs in different ways. Try seeing if you can build out the code for these. **Challenge**: Create a Waiter NPC Instead of using the Output window, use the NPC from [Intro To Arrays](/docs/en-us/tutorials/fundamentals/coding-5/intro-to-arrays.md) to create a waiter to take customer orders. **Challenge**: Allow Players to Place Orders Allow players to select an ingredient by touching a physical part such as a proximity prompt. For more information, see [Proximity Prompts](/docs/en-us/ui/proximity-prompts.md). **Advanced Challenge**: Make Me Some Soup Simulator Create a restaurant simulator where players are the chefs! Players will have to put together ingredients to match the order given to them by the waiter. 1. Create in-game props for 3 - 5 different ingredients. 2. Create 3 - 5 recipes using dictionaries similar to the one in this lesson. 3. Create an array that holds all of the recipes. 4. Randomly select a recipe and use an NPC to tell players what the ingredients are. 5. Create a new dictionary to make a recipe based off which prop ingredients the player touches. 6. Compare the player's recipe to the recipe chosen by the NPC. 7. Increase the challenge by adding an extra condition, such as a timer. --- title: "Return values from tables" url: /docs/en-us/tutorials/fundamentals/coding-5/return-values-from-tables last_updated: 2026-06-29T19:34:14Z description: "Learn how to write a search function using pairs and ipairs to return values from tables in Luau." --- # Return values from tables A common need in many data structures is to return values. This article will cover searching a table using `pairs()` or `ipairs()` for half of any table element, such as the key or value, to find and return the other half. The example uses an array listing ships waiting to be repaired. `ipairs()` is used to return a ship's place in line. ```lua local shipToFind = "Void Racer" -- Ships waiting for repair local waitingShips = {"Battle Sun", "Void Racer", "The Big Slow"} -- Get place in line local function getPlaceInLine(shipName) for placeInLine, ship in ipairs(waitingShips) do if ship == shipName then return placeInLine end end end --Print place in line local placeInLine = getPlaceInLine(shipToFind) print("Your place in line is " .. placeInLine) ``` ## Dictionary search example Have you ever searched for a lost pet in house going room by room? On your own code a function to search through a dictionary named house to see which room holds a lost puppy. 1. Copy the dictionary below to act as the house.```lua local house = { kitchen = "pile of Junk", livingRoom = "kitten", bedroom1 = "nobody there", bedroom2 = "puppy", } ``` 2. Create a new function called `findPet()` to search the dictionary. Include parameters for what dictionary to search, and what value to search for. 3. Use `pairs()` to iterate through the dictionary. Try coding your own before checking your work in the solution below.```lua local function findPet(whereToSearch, searchFor) for place, value in pairs(whereToSearch) do end end ``` 4. As the for loop iterates through the dictionary, use `return` to pass back the room where the pet was found.```lua local function findPet(whereToSearch, searchFor) for place, value in pairs(whereToSearch) do if value == searchFor then return place end end end ``` --- title: "Coding concept - abstraction" url: /docs/en-us/tutorials/fundamentals/coding-6/coding-concept-abstraction last_updated: 2026-06-29T19:34:14Z description: "Abstractions in computer science are simplified representations of a larger idea or concept. This is used for computer science AP CSP lessons." --- # Coding concept - abstraction **Abstractions** in computer science provide a simplified representation of something larger. They pull out only the most necessary information and hide everything else. **Functions** are reusable abstractions. When called, users get the benefits of the function without having to rewrite or even look at the code for the entire function. A common example in coding languages is `print()`. Most of its code is hidden, so the coder can focus on what needs to be printed and not the rest of the code. ## Why create abstractions Abstractions keeps programs organized, reduces complexity, and makes code easier to update. ### Shop example Say that you have an in-game shop that sells just two different backpacks. The code for the second backpack was copied with slight changes, such as a different name and selling price. _Yellow Backpack - 10 Robux_ _Violet Backpack - 25 Robux_ Here, the code is **not** abstracted. Each backpack has a script of its own. What would happen if you tried to add the following? - 20 more backpacks. - The ability for some bags to hold more items than other bags. - A holiday sale, 25% off all backpacks. ## Design abstractions Having separate backpack scripts makes adding and updating backpacks time consuming. Instead, create an abstraction so you don't have to make updates in so many different places. To design an abstraction decide: - Which parts of the code will be reused. - What elements will be different each time. The abstraction should pull out the information that changes, and hide the rest. In the backpack example, the differences are the backpack's name, price, and number of items it can carry. So an example of an abstraction, you can design is a function that takes in the backpack's name and returns it's price and capacity. | | | | --- | --- | | **No Abstraction** | **Abstraction** | | Four different backpacks, four different places to update. | Use a function to search a table for unique information. Only one place to update. | > **Info:****Check for Understanding - Using Abstractions** What are other game mechanics you can create abstractions for? > > Possible Examples - Trap parts - Allowing for different harm values - Creating a function for awarding random power-ups - Creating character classes ## Summary **Abstractions** provide a simplified representation of something larger by leaving out details. When deciding whether to create an abstraction, look for code that is often reused but with small changes each time. For example, a generic item like a backpack can be abstracted to a reusable function that looks up price and capacity. Taking time to plan and structure code with abstractions helps coders focus on what's important. This investment in time keeps programs better organized and makes updating them easier. > **Info:** Galactic Speedway - The Settings script has only the variables for unique features of the driftspeeder. The rest of the code is hidden. This allows students new to coding to stay focused without getting lost. > > Story Game Creator Challenge - Students call `getInput()` from the StoryMaker module script to create questions for players to answer. Students can practice typing strings and ask as many questions as they like, but don't need to see the code in the StoryMaker module. --- title: "Coding concept - algorithms" url: /docs/en-us/tutorials/fundamentals/coding-6/coding-concept-algorithms last_updated: 2026-06-29T19:34:14Z description: "Algorithms in computer science are a series of steps used to determine an outcome. This is used for computer science AP CSP lessons." --- # Coding concept - algorithms An **algorithm** uses a series of steps to determine an outcome. Steps can be a set of directions, comparisons, or even a mathematical formula. Your daily life is full of algorithms that you use without thinking about it. They help you make decisions, create things, and solve problems. **Real life examples:** - Checking if you have enough money to buy a snack. - Getting dressed in the morning. - Drawing stick figures. **Coding examples:** - Checking if a skill level is high enough to use an item. - Sorting a list of items into alphabetical order. - A player standing on lava loses 5 health every second. If they reach 0, they respawn. > **Success:** For an offline activity, try using this combination of art and computer science to create an algorithm that makes stick figures. - Have everyone draw a stick figure. - Have everyone write down the steps they took. - Pair up students and have one student direct the second student using the directions they wrote down previously. ## Create algorithms in code In real life, we don't usually think about the algorithms we use everyday. Computers however, need algorithms to be coded step-by-step and use at least one of three methods to solve a problem or produce an outcome. | Methodology | Example | | --- | --- | | **Selection** - Uses conditional statements such as if/then to determine an output. | ```lua if time == 0 then stopLightColor = red end ``` | | **Sequencing** - A set of precise steps. | ```lua local function createBridge() create new block size block set color rotate set location parent to workspace end ``` | | **Iteration** - Repeats parts of the code as necessary, such as in for loops or multiplication. | ```lua for countDown = 10, 1, -1 do time -= 1 task.wait(1) end ``` | ## Combine algorithms Much like larger problems can be broken into smaller problems, some algorithms can be broken down into a series of smaller algorithms. Think of your real-life morning time algorithm to get dressed. If that's your main algorithm, it might use one algorithm for picking out your clothes, and a second algorithm for putting clothes on. ```lua -- First algorithm for picking clothes local function pickClothes() pick top clothing pick bottom clothing pick Socks pick Shoes end -- Second algorithm for putting clothes on local function putOnClothes() put on top clothing put on bottom clothing put on socks put on shoes end -- Main algorithm, calls pickClothes()and putOnClothes() local function getDressed() pickClothes() putOnClothes() end ``` > **Success:** For an optional challenge, try and figure out what are additional algorithms `pickClothes()` might use? - Check the weather. - Check if clothes are clean. - Check if clothes match. ## Summary **Algorithms** are predefined steps that provide an outcome. In daily life, algorithms solve problems like getting dressed, going to work, or making a cake. In code, algorithms solve problems like managing websites, handling traffic congestion, or running game mechanics. To complete their goal, algorithms will often call on other algorithms. Algorithms use three different ways to come to a conclusion; **selection**, **iteration**, and **sequencing**. Selection uses conditionals such as if/then statements. Iteration repeats parts of the code as necessary. Sequencing uses a series of steps to produce a result. --- title: "Create with module scripts" url: /docs/en-us/tutorials/fundamentals/coding-6/create-with-module-scripts last_updated: 2026-06-29T19:34:14Z description: "Apply your knowledge of modular scripts in Roblox with this practical example." --- # Create with module scripts To apply your knowledge of module scripts, create a module script that allows players to pick up keys and use them to open treasure chests. ## Project setup This project includes a starter map with leaderboard and scripted pickup objects for the keys and treasure chests. ### Load the starter project 1. Download the starter project. 2. In Roblox Studio, open the downloaded file: **Intro to Module Scripts - Starter Project.rbxl**. ### Create a module script So players can get treasure from chests, create a module script named TreasureManager. Using a module script will connect the pickups and leaderboards together. 1. In **ServerStorage**, create a new **ModuleScript** and rename it **TreasureManager**. 2. In **TreasureManager**, rename the default module table by replacing `module` with `TreasureManager` in both places.```lua local TreasureManager = {} return TreasureManager ``` ## Use functions in module scripts To test how functions work in module scripts, create a new function named `getKey()`. When the `getKey()` function is called from another script, it'll receive a key part to destroy and add 1 to the number of keys in the player's inventory. ### Create a module function for keys 1. This module script will use a combination of module and local functions, type **two** comments to help you keep them separated.```lua local TreasureManager = {} ------------------ Local Functions ------------------ Module Functions return TreasureManager ``` 2. Under the **Module Functions** comment, add a new module function to `TreasureManager` named `getKey()`. Use two parameters: - `keyPart` - the part to destroy. - `whichCharacter` - the player that touched the key part.```lua local TreasureManager = {} ------------------ Local Functions ------------------ Module Functions function TreasureManager.getKey(keyPart, whichCharacter) end return TreasureManager ``` 3. In `getKey()`, destroy `keyPart`.```lua function TreasureManager.getKey(keyPart, whichCharacter) keyPart:Destroy() end ``` ### Use the module function Now, the module function `getKey()` can be used in other scripts. To test that function, you'll open a premade script and call it. 1. Open the key script in **Workspace** ⟩ **Keys** ⟩ **KeyScript**. 2. In keyScript, store the module script in a variable named `treasureManager` and set it equal to: `require(ServerStorage:WaitForChild("TreasureManager"))````lua local ServerStorage = game:GetService("ServerStorage") -- Require the module script below ⯆ local treasureManager = require(ServerStorage:WaitForChild("TreasureManager")) local keys = script.Parent local keysFolder = keys.Parts local keysArray = keysFolder:GetChildren() ``` > **Success:** Prevent errors by using `WaitForChild()` instead of the dot operator to make the script wait until `TreasureManager` has loaded before moving on. For scripts in ServerScriptService or ServerStorage, it's safe to use the dot operator instead, like in `Class.ServerStorage.TreasureManager`. 3. There's already a function named `partTouched()` to check for a player touching the part. Inside `partTouched()`: - Call the `getKey()` module function to destroy the key. - Pass in `keyPart` and `whichCharacter`.```lua local ServerStorage = game:GetService("ServerStorage") -- Require the module script below ⯆ local treasureManager = require(ServerStorage:WaitForChild("TreasureManager")) local keys = script.Parent local keysFolder = keys.Parts local keysArray = keysFolder:GetChildren() local function partTouched(otherPart, keyPart) local whichCharacter = otherPart.Parent local humanoid = whichCharacter:FindFirstChildWhichIsA("Humanoid") if humanoid then -- Give the player a key and destroy the key part -- ============================================= treasureManager.getKey(keyPart, whichCharacter) -- ============================================= end end ``` 4. Run the project and check that touching a key destroys it. ### Troubleshooting tips **Issue:** Get an error message including: `"Infinite yield possible"`. - Check the spelling of your module script in a script. If a module script, like **TreasureManager**, is spelled differently, there will be an error. **Issue:** Get an error message including: `"attempt to index global"`. - Check the line that includes the require for the module script in **keyScript**. If the module does not include require, it can't use functions and variables from that module script. **Issue:** Script doesn't run or can't pick up keys. - In the module script, make sure that all the code is between `local TreasureManager = {}` and `return TreasureManager`. The return must be the last line of code in a Module Script. - Check that there are two parenthesis at the end of the line with require, like in `WaitForChild("TreasureManager"))`. ## Create a Local Function Right now, a leaderboard keeps track of a player's keys and treasure. To change the leaderboard numbers, use a local function in the module script. A local function is used because changing a player's key or treasure values will only be needed in the TreasureManager script, not anywhere else. 1. In **ServerStorage**, open the **TreasureManager** script. 2. Create local variables to do the following: - Get the Players service so the script can work with player's leaderboard stats. - Store the number of keys the player receives after touching **keyPart**.```lua local TreasureManager = {} local Players = game:GetService("Players") local keyDrop = 1 ------------------ Local Functions ------------------ Module Functions function TreasureManager.getKey(keyPart, whichCharacter) keyPart:Destroy() end return TreasureManager ``` 3. Copy and paste these two local functions into the **Local Functions** section. - `getPlayerKeys()` returns the value of the player's `Lockpicks` leaderstat. - `getPlayerTreasure()` returns the value of the player's `Treasure` leaderstat.```lua ------------------ Local Functions local function getPlayerKeys(whichCharacter) local player = Players:GetPlayerFromCharacter(whichCharacter) local leaderstats = player:FindFirstChild("leaderstats") return leaderstats:WaitForChild("Lockpicks") end local function getPlayerTreasure(whichCharacter) local player = Players:GetPlayerFromCharacter(whichCharacter) local leaderstats = player:FindFirstChild("leaderstats") return leaderstats:WaitForChild("Treasure") end ------------------ Module Functions ``` 4. To add to the player's keys, in the `getKey()` module function: - Create a `local` variable to call `getPlayerKeys(whichCharacter)`. - Add the value of `keyDrop` to `playerKeys`.```lua ------------------ Module Functions function TreasureManager.getKey(keyPart, whichCharacter) local playerKeys = getPlayerKeys(whichCharacter) playerKeys.Value = playerKeys.Value + keyDrop keyPart:Destroy() end ``` 5. Run the project. Check that touching a key destroys it and adds 1 to the player's keys in the leaderboard. If needed, check your script against the one below for any troubleshooting issues. ```lua local TreasureManager = {} local Players = game:GetService("Players") local keyDrop = 1 ------------------ Local Functions local function getPlayerKeys(whichCharacter) local player = Players:GetPlayerFromCharacter(whichCharacter) local leaderstats = player:FindFirstChild("leaderstats") return leaderstats:WaitForChild("Lockpicks") end local function getPlayerTreasure(whichCharacter) local player = Players:GetPlayerFromCharacter(whichCharacter) local leaderstats = player:FindFirstChild("leaderstats") return leaderstats:WaitForChild("Treasure") end ------------------ Module Functions function TreasureManager.getKey(keyPart, whichCharacter) local playerKeys = getPlayerKeys(whichCharacter) playerKeys.Value = playerKeys.Value + keyDrop keyPart:Destroy() end return TreasureManager ``` ## Get information from module scripts The **TreasureManager** module script will be used when players touch a treasure chest to check if they have at least one key before opening it and giving them gold. ### Check if chests can be opened 1. First in **ServerStorage** ⟩ **TreasureManager** script, set up variables for how many keys it costs to open a chest, and how much gold each chest contains.```lua local TreasureManager = {} local Players = game:GetService("Players") local keyDrop = 1 local chestPickCost = 1 local chestReward = 100 ------------------ Local Functions local function getPlayerKeys(whichCharacter) local player = Players:GetPlayerFromCharacter(whichCharacter) local leaderstats = player:FindFirstChild("leaderstats") return leaderstats:WaitForChild("Lockpicks") end ``` 2. To create a function that checks if a player can open a chest, in the **Module Functions** section, add a new function to the `TreasureManager` table named `canOpenChest()` with the parameter `whichCharacter`.```lua ------------------ Module Functions function TreasureManager.canOpenChest(whichCharacter) end function TreasureManager.getKey(keyPart, whichCharacter) local playerKeys = getPlayerKeys(whichCharacter) playerKeys.Value = playerKeys.Value + keyDrop keyPart:Destroy() end ``` 3. Copy and paste the code below into `canOpenChest()` to return `true` if the player has enough keys, and `false` if they don't.```lua function TreasureManager.canOpenChest(whichCharacter) local playerKeys = getPlayerKeys(whichCharacter) if playerKeys.Value >= chestPickCost then return true else return false end end ``` ### Give players treasure So the player can open a chest, create a function in **TreasureManager** that awards them treasure. 1. Add a new module function to `TreasureManager` named `openChest()`. Pass in two arguments: - `chestPart` - the chest part to destroy. - `whichCharacter` - the player to give treasure.```lua function TreasureManager.openChest(chestPart, whichCharacter) end ``` 2. To subtract a player's keys and award them treasure, copy and paste the code below in `openChest()`. This code uses the variables created previously, like `chestReward`, the amount of treasure given per chest.```lua function TreasureManager.openChest(chestPart, whichCharacter) local playerKeys = getPlayerKeys(whichCharacter) local playerTreasure = getPlayerTreasure(whichCharacter) playerKeys.Value = playerKeys.Value - chestPickCost playerTreasure.Value = playerTreasure.Value + chestReward chestPart.Parent:Destroy() end ``` ### Call the chest functions Now that the two module functions, `canOpenChest()` and `openChest()`, have been created, they can be called by the Chest parts whenever a player touches them using the premade `partTouched()` function. 1. In **Workspace** ⟩ **Chests** open **ChestScript**. 2. Create a new variable named `treasureManager` and require the **TreasureManager** module script in **ServerStorage**.```lua local ServerStorage = game:GetService("ServerStorage") -- Require the module script below local treasureManager = require(ServerStorage:WaitForChild("TreasureManager")) local chests = script.Parent local chestsFolder = chests.Parts local chestsArray = chestsFolder:GetChildren() ``` 3. In `partTouched()`, under the `if humanoid` statement, create a new variable named `canOpen` and set it equal to:`treasureManager.canOpenChest(whichCharacter)````lua local function partTouched(otherPart, chestPart) local whichCharacter = otherPart.Parent local humanoid = whichCharacter:FindFirstChildWhichIsA("Humanoid") if humanoid then -- Check if the player can open a chest, then let them get treasure -- ============================================= local canOpen = treasureManager.canOpenChest(whichCharacter) -- ============================================= end end ``` 4. Next, create an if statement to check if `canOpen` is true. - If so, call the TreasureManager's `openChest()` function. - Then, pass in two parameters: `chestPart`, the chest to destroy, and `whichCharacter`, the player to award treasure.```lua local function partTouched(otherPart, chestPart) local whichCharacter = otherPart.Parent local humanoid = whichCharacter:FindFirstChildWhichIsA("Humanoid") if humanoid then -- Check if the player can open a chest, then let them get treasure -- ============================================= local canOpen = treasureManager.canOpenChest(whichCharacter) if canOpen == true then treasureManager.openChest(chestPart, whichCharacter) end -- ============================================= end end ``` 5. Run the project. Check that: - If you have at least 1 key, touching a chest will destroys it and awards treasure. - If you have 0 keys, you can't open a treasure chest. ### Troubleshooting tips - In **ChestScript**, make sure that functions called from the module script like `canOpenChest()` are spelled exactly how they're found in the **TreasureManager** script. Any difference will cause an error. - Check that copy and pasted functions, like `treasureManager.openChest()`, are exactly as shown in the lesson. Any differences can cause subtle errors in the script. ## Finished scripts ```lua local TreasureManager = {} local Players = game:GetService("Players") local keyDrop = 1 local chestPickCost = 1 local chestReward = 100 ------------------ Local Functions local function getPlayerKeys(whichCharacter) local player = Players:GetPlayerFromCharacter(whichCharacter) local leaderstats = player:FindFirstChild("leaderstats") return leaderstats:WaitForChild("Lockpicks") end local function getPlayerTreasure(whichCharacter) local player = Players:GetPlayerFromCharacter(whichCharacter) local leaderstats = player:FindFirstChild("leaderstats") return leaderstats:WaitForChild("Treasure") end ------------------ Module Functions function TreasureManager.openChest(chestPart, whichCharacter) local playerKeys = getPlayerKeys(whichCharacter) local playerTreasure = getPlayerTreasure(whichCharacter) playerKeys.Value = playerKeys.Value - chestPickCost playerTreasure.Value = playerTreasure.Value + chestReward chestPart.Parent:Destroy() end function TreasureManager.canOpenChest(whichCharacter) local playerKeys = getPlayerKeys(whichCharacter) if playerKeys.Value >= chestPickCost then return true else return false end end function TreasureManager.getKey(keyPart, whichCharacter) local playerKeys = getPlayerKeys(whichCharacter) playerKeys.Value = playerKeys.Value + keyDrop keyPart:Destroy() end return TreasureManager ``` ```lua local ServerStorage = game:GetService("ServerStorage") -- Require the module script below ⯆ local treasureManager = require(ServerStorage:WaitForChild("TreasureManager")) local chests = script.Parent local chestsFolder = chests.Parts local chestsArray = chestsFolder:GetChildren() local function partTouched(otherPart, chestPart) local whichCharacter = otherPart.Parent local humanoid = whichCharacter:FindFirstChildWhichIsA("Humanoid") if humanoid then -- Check if the player can open a chest, then let them get treasure -- ============================================= local canOpen = treasureManager.canOpenChest(whichCharacter) if canOpen == true then treasureManager.openChest(chestPart, whichCharacter) end -- ============================================= end end -- Binds every chest part to the touch function so it works on all parts for chestIndex = 1, #chestsArray do local chestPart = chestsArray[chestIndex]:WaitForChild("Collider") chestPart.Touched:Connect(function(otherPart) partTouched(otherPart, chestPart) end) end ``` ```lua local ServerStorage = game:GetService("ServerStorage") -- Require the module script below ⯆ local treasureManager = require(ServerStorage:WaitForChild("TreasureManager")) local keys = script.Parent local keysFolder = keys.Parts local keysArray = keysFolder:GetChildren() local function partTouched(otherPart, keyPart) local whichCharacter = otherPart.Parent local humanoid = whichCharacter:FindFirstChildWhichIsA("Humanoid") if humanoid then -- Give the player a key and destroy the key part -- ============================================= treasureManager.getKey(keyPart, whichCharacter) -- ============================================= end end -- Binds every key part to the touch function so it works on all parts for keyIndex = 1, #keysArray do local keyPart = keysArray[keyIndex] keyPart.Touched:Connect(function(otherPart) partTouched(otherPart, keyPart) end) end ``` ## Summary A common application of using module scripts in Roblox experiences is to handle common tasks used by players, such as granting them points. For this example, a module script named TreasureManager was created to handle giving players keys and treasure whenever they interact with in-game objects. --- title: "Intro to module scripts" url: /docs/en-us/tutorials/fundamentals/coding-6/intro-to-module-scripts last_updated: 2026-06-29T19:34:14Z description: "Learn key concepts around organizing and reusing code in Roblox with modular scripts." --- # Intro to module scripts As projects become complex, it becomes important to think how scripts are organized. Good organization practices can ensure code isn't duplicated between scripts, or becomes hard to manage. A better way to organize and reuse code is with **module scripts**, a unique type of script that stores a set of functions and variables designed to meet a shared purpose, like managing player money or enemies. Code within module scripts can be used by other scripts. That way, you can call the same function that gives coins from multiple different scripts whenever a player finishes a quest or finds a pickup. By storing commonly used code in module scripts, it makes maintaining and organizing code easier since changes only need to be made to one module script, rather than updating multiple scripts. > **Success:** Normal scripts should be used for standalone elements of an experience, such as touching a pickup, while module scripts are useful for storing code that can be reused by multiple independent scripts, like rewarding points. ## Module script basics Module scripts are actually their own separate object compared to script objects. In Roblox, module scripts can be denoted with a **purple** icon. ### Create a module script ModuleScripts are commonly placed in **ServerScriptService** when used by server-side scripts and **ReplicatedStorage** when used by client-side local scripts (such as GUI interactions). 1. Create a **ModuleScript** in **ServerScriptService**. ### Structure of module scripts When created, every module script starts out with the code below: ```lua local Module = {} return Module ``` The line `local Module = {}` creates a table, or container of code, where the module's shared functions and variables can be stored. This table should be renamed to the module's purpose, such as `RewardManager` or `ParticleController`. As opposed to other variables which are camel case (`myVariable`), module tables are recommended to use pascal case and start capitalized (`MyModule`). ```lua local RewardManager = {} return RewardManager ``` So other scripts can use a module's non-local functions or variables, every module ends with return `MyModule`. Whenever another script tries to get code from the module, return lets that script access code stored inside the module table. ### Add to module scripts To add a function or variable to the module which can be used in another script, type the module table's name, followed by a dot, and the name of the function or variable, like in `TestModule.myVariable`. Using the dot operator is another way of adding code into a table, allowing other scripts to access that code whenever the module table is returned. ```lua local TestModule = {} -- Adds a variable to 'TestModule' table TestModule.myVariable = 100 -- Adds a function to 'TestModule' table function TestModule.doTask(player) -- Placeholder code end return TestModule ``` > **Warning:** Anything added to the module table should be typed between `local MyModule = {}` and `return MyModule`, or else the code may create an error. ### Scope in module scripts For a module function or variable to be used in an outside script, **don't** type `local`. Typing `local` in front of variables and functions means they are **only** usable by that script. While this is a good practice for most scripts for reducing errors and troubleshooting, you cannot make module script functions and variables local. Any code used **only** by that module script should still include `local`. For instance, the code below includes the local variable `difficultyModifier`, which can only be used in that module script, and the function `getCoinReward()`, which can be used in scripts outside the module. ```lua local RewardManager = {} -- Usable only in the module script local rewardCoins = 50 -- Usable only in the module script local difficultyModifier = { easy = 0.5, normal = 1, hard = 2 } -- Usable in other scripts function RewardManager.getCoinReward(difficulty) local coins = difficultyModifier[difficulty] * rewardCoins return coins end return RewardManager ``` ## Use modules in other scripts By itself, a module script can't run code — it needs to be loaded in another script using the keyword `Global.LuaGlobals.require()`. The function `Global.LuaGlobals.require()` accepts one argument, the location of the module script in the Explorer. To use a module, in a separate script, set a variable equal to `require(moduleScript)`. ```lua local MyModule = require(ServerStorage.ModuleScript) ``` Now, the variable `MyModule` contains the module table created in that module script. To use functions and variables from that table, type the variable name, followed by a dot, and the exact name of what to use in that module script, like `MyModule.myFunction()`. When the script runs and reaches that line, it'll access that specific function or variable stored in the module table. ```lua local MyModule = require(ServerStorage.ModuleScript) MyModule.myFunction() ``` ## RewardManager example ```lua local RewardManager = {} -- Usable only in the module script local rewardCoins = 50 -- Usable only in the module script local difficultyModifier = { easy = 0.5, normal = 1, hard = 2 } -- Usable in other scripts function RewardManager.getCoinReward(difficulty) local coins = difficultyModifier[difficulty] * rewardCoins return coins end return RewardManager ``` ```lua local ServerStorage = game:GetService("ServerStorage") -- Load module script local RewardManager = require(ServerStorage.RewardManager) --Calls function from module script local coins = RewardManager.getCoinReward("easy") print("Should award " .. coins .. " coins") ``` > **Warning:** If you're in another script, make sure that the module script function or variable is spelled **exactly** the same as found in that module. To help, you can copy the exact function or variable name from the module and then just paste it in the normal script where it'll be used. ## General troubleshooting Some of the tips here address common issues when working with module scripts. Keep in mind that module scripts can be a complicated topic with more nuance. For more details, see this more technical guide on [Module Scripts](/docs/en-us/scripting/module.md). **Issue:** Get an error message including: `"Infinite yield possible"` or `"not a valid member"`. - Check the spelling of the module script where it's loaded. `Global.LuaGlobals.require()` must include the exact path and spelling of the module script, which may be named differently than the module table. **Issue:** Get an error message including: `"attempt to index global"`. - In any scripts using a module script, make sure it's loaded using the function `Global.LuaGlobals.require()`. If not, that script cannot use functions and variables from the module script. ## Summary **Module scripts** in Roblox are a method coders use to organize and reuse code. A module script is often stored in ServerStorage (or ReplicatedStorage for client-based interactions). From there, other scripts are able to call functions and variables stored in that module script. For instance, one experience may award points to players for collecting objects. A module script can handle code to give points. Then, scripts for different types of objects can just call the module script function. This reduces the need to reuse code between scripts, making code easier to understand and maintain. --- title: "Organizing code" url: /docs/en-us/tutorials/fundamentals/coding-6/landing last_updated: 2026-06-29T19:34:14Z description: "Roblox module scripts are one way of organizing code. This series covers how they work and how to implement them in an experience." --- # Organizing code ### Series description As you continue to work with code, scripts will become more complex. To address this, coders have a variety of techniques to make coding efficient, easy to understand, and reduce the chance of error. This series covers methods of organizing scripts, such as module scripts, a special type in script that can store functions and variables used by other scripts. ### Objectives and prerequisites | **Learning objectives** | Understand the logic behind module scripts, as well as how to code a module script and access it in other scripts. Be able to identify patterns of abstraction and algorithms in computer science. | | --- | --- | | **Prerequisites** | Have an understanding of functions, events, and if/then statements. | ### Series contents | Article | Description | | --- | --- | | [Intro to module scripts](/docs/en-us/tutorials/fundamentals/coding-6/intro-to-module-scripts.md) | Learn how to use module scripts, a type of script that stores functions and variables that can be called or accessed from other scripts. | | [Create with module scripts](/docs/en-us/tutorials/fundamentals/coding-6/create-with-module-scripts.md) | Apply using module scripts to create a mini-game where players gather keys to open chests. | | [Coding abstraction](/docs/en-us/tutorials/fundamentals/coding-6/coding-concept-abstraction.md) | Abstractions in computer science provide a simplified representation of something larger. They pull out only the most necessary information and hide everything else. | | [Coding algorithms](/docs/en-us/tutorials/fundamentals/coding-6/coding-concept-algorithms.md) | An **algorithm** uses a series of steps to determine an outcome. Steps can be a set of directions, comparisons, or even a mathematical formula. | --- title: "Start learning with tutorials" url: /docs/en-us/tutorials last_updated: 2026-06-29T19:34:14Z description: "Learn how to create Roblox experiences, avatars, and avatar items with use case tutorials, curriculum paths, and videos." --- # Start learning with tutorials Roblox offers both short and long-form tutorials to help you learn various creation processes and techniques for the platform. There are three common areas of creation: - **Experiences** - 3D worlds that you can create and publish using Roblox Studio. - **Avatars** - Character models with many specialized features that allow players to interact with experiences and express themselves. - **Avatar items** - Clothing and accessories that players can use to customize their avatars. While many creators focus on a single area, the following tutorials walk you through the fundamentals of each process. #### Create your first experience Learn about important concepts for developing on Roblox by recreating a simple 3D platformer experience where players collect coins to trade for jump power. #### Create your first avatar character Learn how to create your own custom avatar character using Blender and one of Roblox's downloadable template models. #### Create your first avatar item Learn how to convert a mesh object in Blender to either a publishable accessory item or an equipable and layerable clothing item for avatars. ## Use case tutorials **Use case tutorials** focus on a specific task that you can complete in order to learn about various aspects of developing in Studio. In addition to teaching you how to use collections of related features to recreate common components for experiences, this short-form type of structured learning highlights techniques you can use to achieve effects for a variety of design requirements. #### Modeling - Learn the basics of [solid modeling](/docs/en-us/use-case-tutorials/modeling/create-neon-signs.md) in Studio - Learn how to [rig](/docs/en-us/art/modeling/rig-a-simple-mesh.md) and [skin](/docs/en-us/art/modeling/skin-a-simple-mesh.md) a simple mesh in Blender - Learn how to [rig](/docs/en-us/art/modeling/rig-a-humanoid-model.md) and [skin](/docs/en-us/art/modeling/skin-a-humanoid-model.md) a humanoid model in Blender #### Environments - Learn how to set global and local lighting properties to enhance [indoor](/docs/en-us/use-case-tutorials/lighting/enhance-indoor-environments.md) and [outdoor](/docs/en-us/use-case-tutorials/lighting/enhance-outdoor-environments.md) environments - Learn how to play [positional](/docs/en-us/use-case-tutorials/audio/add-3D-audio.md) and [non-positional](/docs/en-us/use-case-tutorials/audio/add-2D-audio.md) audio for player immersion - Learn how to generate [audio from text](/docs/en-us/use-case-tutorials/audio/add-text-to-speech.md) using an artificial voice - Learn how to integrate different [voice chat](/docs/en-us/use-case-tutorials/audio/add-voice-chat.md) configurations into your gameplay - Learn how to [assemble modular assets](/docs/en-us/use-case-tutorials/modeling/assemble-modular-environments.md) that snap together in numerous configurations #### Effects - Learn how to use [beams](/docs/en-us/use-case-tutorials/vfx/laser-traps-with-beams.md) for player hazards - Learn how to customize [particle emitters](/docs/en-us/use-case-tutorials/vfx/use-particles-for-explosions.md) for explosions - Learn how to [combine VFX objects](/docs/en-us/use-case-tutorials/vfx/create-waterfalls.md) to emulate real-world physical behavior #### UI - Learn how to combine UI objects with scripts to create a [custom health meter](/docs/en-us/use-case-tutorials/ui/create-hud-meters.md) - Learn how to make [interactive UI objects](/docs/en-us/use-case-tutorials/ui/interactive-ui.md) hooked to in‑experience behavior and state transitions - Learn how to create [interactive prompts](/docs/en-us/use-case-tutorials/ui/proximity-prompts.md) that only appear as players approach 3D objects #### Physics - Learn how to [move](/docs/en-us/use-case-tutorials/physics/create-moving-objects.md) and [spin](/docs/en-us/use-case-tutorials/physics/create-spinning-objects.md) objects in a way that emulates real-world physical behavior - Learn how to [move objects](/docs/en-us/use-case-tutorials/physics/create-moving-objects.md) to transport players linearly from point A to B - Learn how to use [constraints](/docs/en-us/use-case-tutorials/physics/build-a-hinged-door.md) for mechanical connections, such as door hinges, rope, and welds #### Animation - Learn the basics of [creating animations](/docs/en-us/use-case-tutorials/animation/create-an-animation.md) using the built-in **Animation Editor** - Learn how to [swap default avatar animations](/docs/en-us/use-case-tutorials/animation/play-character-animations.md#change-default-animations) with your own creations - Learn how to [trigger avatar animations](/docs/en-us/use-case-tutorials/animation/play-character-animations.md#trigger-animations) from gameplay interactions #### Scripting - Learn how to script the gameplay logic for player hazards, such as [deadly lava](/docs/en-us/use-case-tutorials/scripting/basic-scripting/deadly-lava.md) and [fading traps](/docs/en-us/use-case-tutorials/scripting/basic-scripting/fading-trap.md) - Learn how to script the gameplay logic for player information, such as their [score](/docs/en-us/use-case-tutorials/scripting/basic-scripting/score-points.md), [health](/docs/en-us/use-case-tutorials/scripting/intermediate-scripting/create-a-health-pickup.md), and [data](/docs/en-us/use-case-tutorials/scripting/intermediate-scripting/save-data.md) that needs to persist between sessions #### Input and camera - Learn how to create a [first-person camera](/docs/en-us/use-case-tutorials/input-and-camera/control-the-users-camera.md#create-a-first-person-camera), [side-scrolling camera](/docs/en-us/use-case-tutorials/input-and-camera/control-the-users-camera.md#create-a-side-scrolling-camera), or [isometric camera](/docs/en-us/use-case-tutorials/input-and-camera/control-the-users-camera.md#create-an-isometric-camera) to suit your specific gameplay - Learn how to [detect user input](/docs/en-us/use-case-tutorials/input-and-camera/detect-user-input.md) to trigger unique actions in your experiences ## Curriculum paths **Curriculum paths** provide a comprehensive overview of the various skills you need for different development areas after you familiarize yourself with Studio's core functionality. In addition to demonstrating how to use Studio features for each creative discipline, this long-form type of structured learning shows you how to examine and solve common design problems within your own experiences. #### Environmental art Learn how to recreate a high-quality environment for a first-person laser tag experience. #### Gameplay scripting Learn how to organize and implement the gameplay logic for a large, complex project. #### UI design Learn how to design and implement UI components for unique user flows. --- title: "Create character animations" url: /docs/en-us/tutorials/use-case-tutorials/animation/create-an-animation last_updated: 2026-06-29T19:34:14Z description: "The process for using the Animation Editor to create custom character animations." --- # Create character animations **Character animations** include a series of key poses that programmatically flow together to make your characters appear as if they're moving in their environment. While Roblox provides a set of default character animations for every experience, creating custom animations for your characters helps players understand their unique personalities by how they carry themselves and interact with other characters. Using the [Walking Character Animations](https://www.roblox.com/games/134383324873456/Walking-Character-Animation) `.rbxl` file as a starting place, this tutorial shows you how to create a walk cycle character animation from start to finish, including guidance on: - Adding a pre-built character rig to the 3D space that you can move and rotate into different key poses. - Breaking down a reference image to guide your animation decisions and ensure each pose reflects your character's personality. - Looping the animation to test how it looks at different speeds, angles, and easing styles. - Publishing the animation so that you can use it across projects in different scripts and contexts. After you complete this tutorial, you will have an asset ID for your animation, and skills to create additional types of animations that meet the needs for your own characters and experiences. ## Add rig **Rigs**, or collections of parts connected by joints like `Class.Bone` or `Class.Motor6D` objects, are necessary to create character animations because they include the internal structure you need to move and rotate body parts into different poses. While you can [create your own rigs](/docs/en-us/art/modeling/rig-a-humanoid-model.md) using external 3D modeling tools, Studio provides several pre-built rigs that you can access through the [Rig Generator](/docs/en-us/studio/rig-builder.md) tool. _R15 Masculine Skinned Rig_ _R15 Masculine Block Rig_ _R15 Feminine Rthro Rig_ The rig you choose to animate is your first important design decision because the shape of the body directly influences **how** you animate the character. For example, a masculine block character has a lot of mass throughout its body with minimal range of motion, so the character may shuffle their weight as they walk with a slow, short stride. Conversely, a feminine rthro rig carries more mass near its hips with much more realistic range of motion, so the character may sway as they distribute their weight with a fast, long stride. The rest of the tutorial provides an in-depth analysis of the different design decisions and techniques you can use while animating all different types of rigs. As you review these decisions for a rthro masculine rig, you will learn how to adjust your methods to meet the requirements of your own characters and world. To add a pre-built rig to the 3D space: 1. From the toolbar's **Home** or **Avatar** tab, click **Character**. 2. Select a rig type, body shape, then an avatar option. For example, this tutorial uses a **R15** rig type, a **masculine** body shape, and an **Rthro Avatar**. The rig displays in the viewport. ## Pose rig Every animation is made up of a sequence of key poses at different frames, then Studio programmatically **interpolates**, or "fills in", the in-between frames to create smooth movement. For example, if you were to create a key pose of an arm reaching toward the sky at the 0:00 frame, then another key pose of the same arm reaching toward the ground at the 0:09 frame, Studio fills in the 0:01-0:08 frames between the poses. This process is called **tweening** or **inbetweening**, and it means that you don't need to manually animate every single frame. Instead, you can just animate the key poses that you want your character to make to exaggerate their personality, then edit the interpolated frames until you're happy with the final result. Walk cycles for humanoid characters typically have 4 key poses that repeat for each foot's step: - **Contact** - The moment when one foot touches the ground in front of the character, and the other is about to lift off behind the character. Both feet support the character's weight. - **Low** - The moment when the front foot fully supports the character's weight, and the back leg lifts off the ground. - **Passing** - The midstep moment when the back leg passes the front leg, and the character's weight begins to shift from one foot to the other. - **High** - The moment when the character lifts their body up onto their newly back foot, and the newly front foot is about to touch the ground. To demonstrate how this works, let's review the following walk cycle reference where a yellow humanoid robot takes a step first with their left foot, then with their right foot. Note that the robot's left arm and leg are purple to differentiate how each side of the body moves throughout the walk cycle. As the robot transitions from the **Contact** to the **Low** pose, its weight falls forward and pulls the robot's body toward the ground. After the robot catches itself and stands back up, its weight falls backward and launches its body forward as the robot transitions from the **Passing** to the **High** pose. If you look at the character's head throughout the walk cycle, you can see how they fall and rise with each step. While the central idea of these key poses remain the same for all walk cycles, different characters exaggerate different parts of the cycle depending on how they're feeling or who they are, such as their personality, age, gender expression, and well-being. For example: - An elderly character may hunch forward and take slow steps that barely leave the ground. - A timid character may keep their arms tucked and their head down to avoid eye contact. - A cold character may cross their arms and take measured steps as they look around for warmth. Animation is an art form, and the design decisions you make for your character may look different from the design decisions within this tutorial for the masculine Rthro rig. While the following instruction focuses on how to recreate the key poses for a confident character's walk cycle, feel free to adjust the learnings to meet the design needs for your own experiences. ### Left step The first course of action for a walk cycle is to create the four key poses that make up the character's left step, or the step they take to move forward with their left foot. As you complete each pose, consider how the character's weight shifts from an even distribution between both feet to balancing on a single foot, and how that affects movement throughout the rest of their body. #### Contact The **Contact** pose for the left step sequence represents the moment when the left foot touches the ground in front of the character while the right foot is about to lift off behind the character. Both of the character's arms swing opposite the direction of their respective legs; for example, because the left foot is in front of the character, their left arm is behind the character. This is an important moment in a character's stride because both feet equally support the character's weight while they're in motion. To create a first pass Contact pose for the left foot cycle: 1. From the toolbar's **Avatar** tab, click **Clip Editor**. The [Animation Editor](/docs/en-us/animation/editor.md) window displays. 2. Select your rig. A contextual menu displays. 3. In the **Animation Name** field, input a name for your animation, then click the **Create** button. 4. **(Optional)** Set the timeline to 24 frames per second. 1. In the editor's playback tools, click the gear icon. 2. From the contextual menu, set **Frame Rate** to **24 fps**. 5. Add a keyframe for each of the rig's body parts to the editor's track list. 1. In the **Animation Editor**, click the **+** button. A contextual menu displays. 2. Select **Add All Body**. The editor's track list updates with all the rig's body parts. 3. Right-click on the top bar beneath the timeline, then in the contextual menu, select **Add Keyframe**. The editor adds a keyframe for every body part in the timeline. 6. Pose the rig's upper body in a bent position. 1. From the toolbar, select the **Rotate** tool. 2. In the viewport, select and angle the **UpperTorso** body part so that the torso is slightly bent forward. 3. Select and angle the **Head** body part so that the rig's face is looking straight forward. 7. Pose the rig's legs so that the left foot touches the ground in front of the rig while the right foot touches the ground behind the rig. 1. In the viewport, select and angle the **LeftUpperLeg**, **LeftLowerLeg**, and **LeftFoot** body parts until the left leg is bent while the left foot is slightly raised. 2. Repeat this process for the **RightUpperLeg**, **RightLowerLeg**, and **RightFoot** body parts until the right leg is bent while the foot's toes skim the ground. 8. Pose the rig's arms so that each respective arm swings as far forward or behind the rig for the character's stride. 1. In the viewport, select and angle the **LeftUpperArm** and **LeftLowerArm** body parts until the left arm is visible and slightly bent behind the rig. 2. Repeat this process for the **RightUpperArm** and **RightLowerArm** body parts until the right arm is slightly bent in front of the rig. 9. Save the animation. 1. In the **Media and Playback Controls**, select the ellipsis button. A contextual menu displays. 2. Select **Save**. > **Warning:** Until you publish an animation, it's stored locally to your place file and cannot be used in other experiences. When you save an animation, it does **not** save your experience. #### Low The **Low** pose for the left step sequence represents the moment when the left foot fully supports the character's weight as they fall toward the ground, and their right leg lifts off of the ground to offset the unequal weight distribution between their legs. This key pose in both the left and right step sequences is when the character is as low to the ground as they will be in their stride because they are falling forward before their front foot stops their descent. The forward motion from this process also causes the upper body and head to tilt toward the ground, which you can exaggerate for more energetic characters. To create a first pass Low pose for the left foot cycle: 1. In the **Explorer** window, click-and-drag the scrubber to the 3rd frame. 2. Pose the rig's upper body so that it leans even more toward the ground than the previous pose. 1. In the viewport, select and angle the **UpperTorso** body part so that the torso is significantly bent forward. 2. Select and angle the **Head** body part so that the rig's face is looking toward the ground. 3. Pose the rig's legs so that the rig's body weight is supported by its left leg. 1. In the viewport, select and angle the **LeftUpperLeg**, **LeftLowerLeg**, and **LeftFoot** body parts until the left leg is nearly bent at a 90 degree angle with a foot flat on the ground. 2. Repeat this process for the **RightUpperLeg**, **RightLowerLeg**, and **RightFoot** body parts until the right leg is nearly straight behind the rig with the bottom of its foot angled toward the sky. 4. Move the rig down so that its left foot touches the ground. 1. In the viewport, select the **LowerTorso** body part. 2. Move it down until the rig's left foot is parallel to the ground. 5. Pose the rig's arms so that they swing closer to the torso. 1. In the viewport, select and angle the **LeftUpperArm** and **LeftLowerArm** body parts until the left arm is closer to the character's waist. 2. Repeat this process for the **RightUpperArm** and **RightLowerArm** body parts until the right arm is closer to the character's waist. 6. Save the animation. #### Passing The **Passing** pose for the left step sequence represents the moment when the right leg passes the left leg, and the character's weight begins to shift from the left foot to the right foot. Both of the character's arms fall toward the waist as they swing in opposite directions, which causes the character to appear as though they are standing straight up like a flamingo. To create a first pass Passing pose for the left foot cycle: 1. In the **Explorer** window, click-and-drag the scrubber to the 6th frame. 2. Pose the rig's upper body in a straight position. 1. In the viewport, select and angle the **UpperTorso** body part so that the torso is about 90 degrees with the ground. 2. Select and angle the **Head** body part so that the rig's face is looking forward again. 3. Pose the rig's legs so that the character balances its weight on its left leg while the right leg swings forward. 1. In the viewport, select and angle the **LeftUpperLeg**, **LeftLowerLeg**, and **LeftFoot** body parts until the left leg is nearly straight with a flat foot on the ground. 2. Repeat this process for the **RightUpperLeg**, **RightLowerLeg**, and **RightFoot** body parts until the right leg is bent at a 45 degree angle with the front of the foot angled toward the ground. 4. Move the rig up so that its left foot touches the ground. 1. In the viewport, select the **LowerTorso** body part. 2. Move it up until the rig's left foot is parallel to the ground. 5. Pose the rig's arms so that they fall on either side of the character's waist. 1. In the viewport, select and angle the **LeftUpperArm** and **LeftLowerArm** body parts until the left arm is nearly straight on the character's left side. 2. Repeat this process for the **RightUpperArm** and **RightLowerArm** body parts until the right arm is nearly straight on the character's right side. 6. Save the animation. #### High The **High** pose for the left step sequence represents the moment when the character lifts their body up onto their left foot while their right foot is about to touch the ground. Most of the character's weight balances on their toes, which in turn push off the ground to propel them forward. This key pose in both the left and right step sequences is when the character is as high off the ground as they will be in their stride because they are pushing off of the floor. Note that the forward motion from this process also causes the upper body and head to tilt toward the sky, which you can exaggerate for more optimistic characters. To create a first pass High pose for the left foot cycle: 1. In the **Explorer** window, click-and-drag the scrubber to the 9th frame. 2. Pose the rig's upper body so that the chest faces the sky. 1. In the viewport, select and angle the **UpperTorso** body part so that the character is slightly angled backward. 2. Select and angle the **Head** body part so that the rig's face is looking toward the sky. 3. Pose the rig's legs so that the character's weight shifts forward from the toes of its left foot to the thigh of its right leg. 1. In the viewport, select and angle the **LeftUpperLeg**, **LeftLowerLeg**, and **LeftFoot** body parts until the left leg is nearly straight with the foot almost perpendicular to the ground. 2. Repeat this process for the **RightUpperLeg**, **RightLowerLeg**, and **RightFoot** body parts until the right leg is bent at a near 90 degree angle in front of the character. 4. Move the rig up so that its left foot touches the ground. 1. In the viewport, select the **LowerTorso** body part. 2. Move it slightly up until the left foot's toes touch the ground. 5. Pose the rig's arms so that they swing further from the torso. 1. In the viewport, select and angle the **LeftUpperArm** and **LeftLowerArm** body parts until the left arm is further from the character's waist. 2. Repeat this process for the **RightUpperArm** and **RightLowerArm** body parts until the right arm is further from the character's waist. 6. Save the animation. ### Right step The second course of action for a walk cycle is to create the four key poses that make up the character's right step, or the step they take to move forward with their right foot. While this tutorial focuses on recreating the same process as the character's left foot sequence, you can make subtle adjustments that add personality to your walk cycle, such as a lean to the right or extra pep in their right step. #### Contact The **Contact** pose for the right step sequence represents the moment when the right foot touches the ground in front of the character while the left foot is about to lift off behind the character. Most of the process for creating this pose is the same as the left step sequence, but it's helpful to first position the rig at the same vertical orientation as the Contact pose for the left step sequence so that the character has a more consistent gait. To create a first pass Contact pose for the right foot cycle: 1. In the **Explorer** window, click-and-drag the scrubber to the 12th frame. 2. Move the rig down so that the character is in the same starting vertical position as the left step's Contact pose. 1. In the viewport, select the **LowerTorso** body part. 2. Move it down until the rig's head is below the purple reference line. 3. Pose the rig's upper body in the same bent position as the left step's Contact pose. 1. In the viewport, select and angle the **UpperTorso** body part so that the torso is slightly bent forward. 2. Select and angle the **Head** body part so that the rig's face is looking straight forward. 4. Pose the rig's legs so that the right foot touches the ground in front of the rig while the left foot touches the ground behind the rig. 1. In the viewport, select and angle the **LeftUpperLeg**, **LeftLowerLeg**, and **LeftFoot** body parts until the left leg is bent while the foot's toes skim the ground. 2. Repeat this process for the **RightUpperLeg**, **RightLowerLeg**, and **RightFoot** body parts until the right leg is bent while the foot is slightly raised. 5. Pose the rig's arms so that each respective arm swings as far forward or behind the rig for the character's stride. 1. In the viewport, select and angle the **LeftUpperArm** and **LeftLowerArm** body parts until the left arm is visible and slightly bent in front of the rig. 2. Repeat this process for the **RightUpperArm** and **RightLowerArm** body parts until the right arm is slightly bent behind the rig. 6. Save the animation. #### Low The **Low** pose for the right step sequence represents the moment when the right foot fully supports the character's weight as they fall toward the ground, and their left leg lifts off of the ground to offset the unequal weight distribution between their legs. Similar to the previous pose, while not mandatory, it's useful to position the rig at the same vertical orientation as the Low pose for the left step sequence so that the character isn't falling more forward on their right leg than their left. To create a first pass Low pose for the right foot cycle: 1. In the **Explorer** window, click-and-drag the scrubber to the 15th frame. 2. Move the rig down so that the character is in the same starting vertical position as the left step's Low pose. 1. In the viewport, select the **LowerTorso** body part. 2. Move it down until the rig's head is slightly above the gray reference line. 3. Pose the rig's upper body in the same bent position as the left step's Low pose. 1. In the viewport, select and angle the **UpperTorso** body part so that the torso is significantly bent forward. 2. Select and angle the **Head** body part so that the rig's face is looking toward the ground. 4. Pose the rig's legs so that the rig's body weight is supported by its right leg. 1. In the viewport, select and angle the **LeftUpperLeg**, **LeftLowerLeg**, and **LeftFoot** body parts until the left leg is nearly straight behind the rig with the bottom of its foot angled toward the sky. 2. Repeat this process for the **RightUpperLeg**, **RightLowerLeg**, and **RightFoot** body parts until the right leg is nearly bent at a 90 degree angle with a foot flat on the ground. 5. Pose the rig's arms so that they swing closer to the torso. 1. In the viewport, select and angle the **LeftUpperArm** and **LeftLowerArm** body parts until the left arm is closer to the character's waist. 2. Repeat this process for the **RightUpperArm** and **RightLowerArm** body parts until the right arm is closer to the character's waist. 6. Save the animation. #### Passing The **Passing** pose for the right step sequence represents the moment when the left leg passes the right leg, and the character's weight begins to shift from the right foot to the left foot. To create a first pass Passing pose for the right foot cycle: 1. In the **Explorer** window, click-and-drag the scrubber to the 18th frame. 2. Move the rig up so that the character is in the same starting vertical position as the left step's Passing pose. 1. In the viewport, select the **LowerTorso** body part. 2. Move it up until the rig's head is slightly below the purple reference line. 3. Pose the rig's upper body in the same straight position as the left step's Passing pose. 1. In the viewport, select and angle the **UpperTorso** body part so that the torso is about 90 degrees with the ground. 2. Select and angle the **Head** body part so that the rig's face is looking forward again. 4. Pose the rig's legs so that the character balances its weight on its right leg while the left leg swings forward. 1. In the viewport, select and angle the **LeftUpperLeg**, **LeftLowerLeg**, and **LeftFoot** body parts until the left leg is bent at a 45 degree angle with the front of the foot angled toward the ground. 2. Repeat this process for the **RightUpperLeg**, **RightLowerLeg**, and **RightFoot** body parts until the right leg is nearly straight with a flat foot on the ground. 5. Pose the rig's arms so that they fall on either side of the character's waist. 1. In the viewport, select and angle the **LeftUpperArm** and **LeftLowerArm** body parts until the left arm is nearly straight on the character's left side. 2. Repeat this process for the **RightUpperArm** and **RightLowerArm** body parts until the right arm is nearly straight on the character's right side. 6. Save the animation. #### High The **High** pose for the right step sequence represents the moment when the character lifts their body up onto their right foot while their left foot is about to touch the ground. To create a first pass High pose for the right foot cycle: 1. In the **Explorer** window, click-and-drag the scrubber to the 21st frame. 2. Move the rig up so that the character is in the same starting vertical position as the left step's High pose. 1. In the viewport, select the **LowerTorso** body part. 2. Move it up until the rig's head overlaps the purple reference line. 3. Pose the rig's upper body in the same angled position as the left step's High pose. 1. In the viewport, select and angle the **UpperTorso** body part so that the character is slightly angled backward. 2. Select and angle the **Head** body part so that the rig's face is looking toward the sky. 4. Pose the rig's legs so that the character's weight shifts forward from the toes of its right foot to the thigh of its left leg. 1. In the viewport, select and angle the **LeftUpperLeg**, **LeftLowerLeg**, and **LeftFoot** body parts until the left leg is bent at a near 90 degree angle in front of the character. 2. Repeat this process for the **RightUpperLeg**, **RightLowerLeg**, and **RightFoot** body parts until the right leg is nearly straight with the foot almost perpendicular to the ground. 5. Pose the rig's arms so that they swing further from the torso. 1. In the viewport, select and angle the **LeftUpperArm** and **LeftLowerArm** body parts until the left arm is further from the character's waist. 2. Repeat this process for the **RightUpperArm** and **RightLowerArm** body parts until the right arm is further from the character's waist. 6. Save the animation. ## Test animation After you complete your first pass of your key poses, it's important to test your animation to see how it flows together. If there are any inconsistencies or choppy transitions, you can make subtle adjustments to ensure the animation is as smooth as it should be for your character's body and personality. In other words, if you want your character to be graceful and light on their feet, their movements should be fluid and flow together. However, if you want your character to be clumsy and stumble as they walk, smooth transitions may be less desirable for how you want them to present themselves in your experience. To test your poses: 1. In the **Animation Editor**'s playback tools, click the looping button to repeat the animation indefinitely. 2. Click the play button to start the animation. 3. Review your animation to see where it needs adjustments. 1. Slow your animation's speed. 1. In the playback tools, click the gear icon. 1. From the contextual menu, set **Playback Speed** to either **0.25x** or **0.5x**. 2. Evaluate the animation from multiple angles. 1. In Studio's **View** menu, enable **Show View Selector**. 2. In the viewport, click any of view selector's 14 faces to move your camera to a different world orientation. 3. Select, move, and rotate your rig until the animation matches your character's personality. ## Publish animation In order to play your animation in your open experience, as well as store it for reuse in other projects, you must publish the animation to the cloud. This process creates a unique asset ID for your animation that you can reference in scripts, which is especially important if you want to replace any of Roblox's default character animations. To publish your animation: 1. In the upper-left corner of the **Animation Editor**, click the ellipsis button. 2. From the contextual menu, select **Publish to Roblox**. The **Asset Configuration** window displays. 3. Fill out all applicable fields, then click the **Save** button. 4. **(Optional)** You can copy the animation's asset ID to use within scripts by clicking the copy icon. Now that your animation is in the cloud, you can find, edit, and reuse the asset across all of your projects through the [Creator Dashboard](https://create.roblox.com/dashboard/creations) under **Development Items** ⟩ **Animations**. --- title: "Play character animations" url: /docs/en-us/tutorials/use-case-tutorials/animation/play-character-animations last_updated: 2026-06-29T19:34:14Z description: "The process for changing default character animations and triggering custom animations." --- # Play character animations **Playing character animations** is an important part of what makes avatars and non-playable characters (NPCs) expressive, realistic, and engaging to your audience. In addition to providing immersive visuals, character animations provide players feedback from their actions, guidance on how to navigate the environment, and vital information about their character and others. Using the [Hazardous Space Station](https://www.roblox.com/games/134383324873456/Walking-Character-Animation) `.rbxl` file as a reference, this tutorial shows you how to play character animations using two different techniques, including guidance on: - Swapping default character animation asset IDs with your own custom animations. - Triggering animations in response to character actions within the 3D space. After you complete this tutorial, you will have the skills to customize animations for a wide variety of gameplay situations. ## Change default animations Every character with a default `Class.Humanoid` object, whether it's a player-controlled avatar or a non-player character (NPC), includes a set of **default animations** that play whenever the character performs specific in-experience actions, such as running, climbing, and jumping. Roblox provides these animations out-of-the-box for every experience without any additional scripting effort. _Default Fall Animation_ _Default Swim Animation_ _Default Climb Animation_ However, if these default animations don't meet the design requirements for your world's environment, aesthetic, or overall narrative, you can swap them out with custom animations that apply to every player that joins your experience. To demonstrate, the following section teaches you how to swap out the default walk animation with a custom walk cycle animation from [Create Character Animations](/docs/en-us/tutorials/use-case-tutorials/animation/create-an-animation.md). Using this same process, you can swap out any of the default animations with your own animation asset IDs. _Default Walk Animation_ _Custom Walk Animation_ ### Create script Every character's `Class.Humanoid` object includes a child `Class.Animator` object that stores all of the character's default animations. In order to set any of these default animations to new asset IDs, you must create a script in the `Class.ServerScriptService` so that it can reference and override the `Class.Animator` object's default values as soon as players load into the experience. To create a script that will reference the default animation asset IDs: 1. In the **Explorer** window, add a new script to **ServerScriptService**. 1. Hover over **ServerScriptService** and click the ⊕ button. 2. From the contextual menu, insert a **Script**. 2. In the new script, paste the following code:```lua local Players = game:GetService("Players") local function onCharacterAdded(character) local humanoid = character:WaitForChild("Humanoid") local animator = humanoid:WaitForChild("Animator") print("Animator found!") end local function onPlayerAdded(player) player.CharacterAdded:Connect(onCharacterAdded) end Players.PlayerAdded:Connect(onPlayerAdded) ``` **#### Code Explanation** The `ResetDefaultAnimations` script starts by getting the `Class.Players` service, which contains all `Class.Player` objects for players as they connect to a server. When each of the player's characters load into the experience, the `Class.Player.onCharacterAdded|onCharacterAdded` function waits until it detects the character's `Class.Humanoid` and `Class.Animator` objects. When it detects an `Class.Animator` object for the first time, the script then prints "Animator found!" to let you know that the script is working as intended. ### Replace asset ID Now that you know your script is able to detect when players load and connect to the server, you can modify your script to specifically reference the animation ID(s) you want to swap with your own custom animations. The following table contains all of the default character animations that you can call and replace within the `Class.Animator` object. Note that Idle has two variations that you can program to play more or less frequently. | Character Action | Animate Script Reference | | --- | --- | | **Run** | `animateScript.run.RunAnim.AnimationId` | | **Walk** | `animateScript.walk.WalkAnim.AnimationId` | | **Jump** | `animateScript.jump.JumpAnim.AnimationId` | | **Idle** | `animateScript.idle.Animation1.AnimationId`
`animateScript.idle.Animation2.AnimationId` | | **Fall** | `animateScript.fall.FallAnim.AnimationId` | | **Swim** | `animateScript.swim.Swim.AnimationId` | | **Swim (Idle)** | `animateScript.swimidle.SwimIdle.AnimationId` | | **Climb** | `animateScript.climb.ClimbAnim.AnimationId` | To replace the default walk animation asset ID: 1. Call the default walk animate script reference, then replace the asset ID with your own custom animation asset ID. For example, the following code sample references the walk cycle animation from [Create Character Animations](/docs/en-us/tutorials/use-case-tutorials/animation/create-an-animation.md).```lua local Players = game:GetService("Players") local function onCharacterAdded(character) local humanoid = character:WaitForChild("Humanoid") local animator = humanoid:WaitForChild("Animator") print("Animator found!") local animateScript = character:WaitForChild("Animate") animateScript.walk.WalkAnim.AnimationId = "rbxassetid://122652394532816" end local function onPlayerAdded(player) player.CharacterAdded:Connect(onCharacterAdded) end Players.PlayerAdded:Connect(onPlayerAdded) ``` 2. Playtest your experience to ensure your custom walk animation overrides the default animation. 1. Choose **Test** from the dropdown menu and click the **Play** button to its right to begin the playtest.![Test option in the testing modes dropdown of Studio's mezzanine.](../../../assets/studio/general/Mezzanine-Testing-Mode-Test.png) 2. Walk around the space station with your avatar. ## Trigger animations While the previous technique focuses on swapping out default animations that automatically play whenever a character performs specific in-experience actions, you can programmatically trigger animations to play in response to **any** character action within the 3D space, such as picking up an item or taking damage from a hazard. _In this example, when players touch the golden platform, they trigger a non-default character dance animation._ This method of playing animations is useful because it provides players instantaneous feedback on how they should interact with objects in their environment. To demonstrate, the following section shows you how to trigger an animation whenever characters are too close to hazardous steam leaks as a way of subtly teaching players to avoid walking too close to the walls. ### Insert volume One of the most common ways to trigger unique gameplay behavior is to use **volumes**, or invisible regions within the 3D space, to detect when characters or objects interact with specific areas of the environment. When you pair volumes with scripts, you can use their collision feedback to programmatically trigger actions, such as reducing the player's health or playing an animation. ![A far out view of a mansion room. An outline of a box is in the middle of the room to signify the volume that triggers gameplay events.](../../../assets/tutorials/playing-character-animations/Volume-Example.jpg)_The Mystery of Duvall Drive uses volumes to trigger gameplay events that change the visual appearance of the room._ When adding a volume to your experience, it's important to scale it so that it only covers the space that you want to trigger your animation. If you make your volume too small, players may never collide with the area to play the animation; conversely, if you make your volume too large, the animation will play before players reach the item or area of interest, and they may not understand what they did to trigger the animation. To insert a volume around a steam leak that will trigger an animation: 1. In the **Explorer** window, add a new block part. 2. Position and resize the block until it covers the area that you want to trigger your animation. 3. In the **Properties** window, 1. Set **Name** to **AnimationDetector**. 2. Set **Transparency** to `1` to make the block invisible.![An outline of a block is visible around a steam vent to signify the position of the volume.](../../../assets/tutorials/playing-character-animations/Insert-Volume-3.jpg) ### Create script Now that you have a defined region for triggering your animation, it's time to create a script that programmatically detects whenever players collide with the volume. You can then listen for collision events to trigger any animation that makes sense for your gameplay requirements. For example, this animation technique uses a `Class.LocalScript` instead of a `Class.Script` to provide players immediate feedback when they collide with the volume. If the server were to listen for the collision and play the animation, there could be a delay between the player touching the volume on their client and seeing the animation play because of the replication time from the server to the client. To create a local script that will detect when the local player's character touches the volume: 1. In the **Explorer** window, add a new script to **StarterCharacterScripts**. This placement ensures the script and its children clone into the player character on join **and** when they respawn back into the experience. 1. Expand **StarterPlayer**, then hover over its **StarterCharacterScripts** child and click the ⊕ button. 2. From the contextual menu, insert a **LocalScript** and rename it **TriggerAnimation**. 2. In the new script, paste the following code:```lua local Workspace = game:GetService("Workspace") local animation = script:WaitForChild("Animation") local humanoid = script.Parent:WaitForChild("Humanoid") local animator = humanoid:WaitForChild("Animator") local animationTrack = animator:LoadAnimation(animation) local animationDetector = Workspace:WaitForChild("AnimationDetector") local debounce = false animationDetector.Touched:Connect(function(hit) if debounce then return end local hitCharacter = hit:FindFirstAncestorWhichIsA("Model") if hitCharacter ~= localCharacter then return end debounce = true animationTrack:Play() animationTrack.Ended:Wait() debounce = false end) ``` **#### Code Explanation** The `TriggerAnimation` script starts by getting the `Class.Workspace` service, which contains all objects that exist in the 3D world. This is important because the script needs to reference the `Class.Part` object acting as your volume. For each player character that loads or respawns back into the experience, the script waits for: - Its child `Class.Animation` object, which you will add in the next section. - The character's `Class.Humanoid` and `Class.Animator` objects. - The volume object in the workspace named **AnimationDetector**. When anything collides with the volume, the `Touched` event handler function gets the first ancestor that's a `Class.Model`, which should be the character if the `Class.BasePart` that collided with the volume is a descendant of a character model. If it is, the function then checks to see if the `Class.Model` is the **local** player's character. If it is, the function then: - Sets debounce to `true`. - Plays and waits for the animation to end. - Sets debounce back to `false`. Setting debounce from `false` to `true` to `false` again after the animation finishes playing is a debounce pattern that prevents the animation from repeatedly triggering as players continuously collide with the volume. For more information on this debounce pattern, see [Detect collisions](/docs/en-us/scripting/debounce.md#detect-collisions). ### Add animation If you were to playtest your experience right now, your `TriggerAnimation` script still wouldn't be able to play an animation in response to the local player-volume collision. This is because it's waiting for a child `Class.Animation` object with an animation asset ID it can reference, and that `Class.Animation` object doesn't currently exist. To add an animation for the local script to reference as players collide with the volume: 1. In the **Explorer** window, add a new animation to **TriggerAnimation**. 1. Hover over **TriggerAnimation** and click the ⊕ button. 2. From the contextual menu, insert an **Animation**. 2. Select the new animation object, then in the **Properties** window, set **AnimationID** to the animation asset ID you want to trigger when players touch the volume. For example, the [Hazardous Space Station](https://www.roblox.com/games/134383324873456/Walking-Character-Animation) sample references `rbxassetid://3716468774` to play an animation of a character falling backwards. 3. Playtest your experience to ensure your animation plays when players are near the first steam leak.
--- title: "Add 2D audio" url: /docs/en-us/tutorials/use-case-tutorials/audio/add-2D-audio last_updated: 2026-06-29T19:34:14Z description: "Explains how to add non-directional audio to your experiences." --- # Add 2D audio **2D audio** is non-directional sound that doesn't emit from any particular location, remaining the same regardless of a listener's position or orientation in the 3D space. This means that as players move their listeners around the environment, they can consistently hear 2D audio from anywhere they turn. Using the [Gingerbread House - Start](https://www.roblox.com/games/134812596370919/Gingerbread-House-Start) `.rbxl` file as a starting place and [Gingerbread House - Complete Audio](https://www.roblox.com/games/94670255584609/Gingerbread-House-Complete-Audio) as a reference, this tutorial shows you how to add both looping and one shot 2D audio to your experiences, including guidance on: - Looping background music that plays as soon as players connect to the server. - Triggering audio to provide players instantaneous feedback according to their gameplay objectives. - Activating various sounds to indicate different user interface interactions. If at any point you become stuck in the process, you can use **Gingerbread House - Complete Audio** as a reference to compare your progress. > **Info:** If you want to add positional audio to your experience, such as ambient noise within the environment, object interaction sounds, or non-playable character dialogue, see [Add 3D audio](/docs/en-us/tutorials/use-case-tutorials/audio/add-3D-audio.md). ## 2D audio objects To create non-directional audio, it's important to understand the audio objects that you will be working with throughout this tutorial. There are three main types of audio objects for 2D audio: - The `Class.AudioPlayer` object loads and plays the **audio file**. - The `Class.AudioDeviceOutput` object is a **physical hardware device** within the real world, such as a speaker or headphones. - `Class.Wire|Wires` carry audio streams from one object to another. All of these audio objects work together to carry sound from file playback to each player's hardware. Let's take a look at how this works in practice using an example of a player wearing a headset while playing an experience with their laptop: - The `Class.AudioPlayer` loads the `127645268874265` audio asset ID into the experience for a currency purchase sound. - A `Class.Wire` carries the stream from the `Class.AudioPlayer` to the `Class.AudioDeviceOutput` so that the stream comes out of the player's headset. - The `Class.AudioDeviceOutput` object carries the sound to the player's physical speaker, or in this case, their headphones. The following sections dive deeper and reference these objects as you learn how to play both looping and one shot 2D audio. As you review these objects with the upcoming techniques, you can more accurately predict how to capture and feed sound from the experience to the player. ## Looping audio **Looping 2D audio**, or non-directional audio that repeats seamlessly as soon as players connect to the server, is a common sound design technique to provide a cohesive auditory backdrop regardless of what players are doing or where they're located within the 3D space. In addition, looping 2D audio ensures that your experience is never completely silent, which can disorient and distract players from their objectives. To demonstrate this concept, review how the following 2D audio significantly enhances the quality of the [Hazardous Space Station](https://www.roblox.com/games/99416825187098/Hazardous-Space-Station) sample. Without background music, the space station feels hollow and lifeless. However, when the space station includes background music, the space station feels both ominous and dangerous, adding to the overall spooky atmosphere. _Without looping 2D audio_ _With looping 2D audio_ > **Info:** To create a seamless loop, you must match the end and start points of your audio track precisely so that players cannot detect its repetition. This process requires third-party audio editing software like [Reaper](https://www.reaper.fm/) or [Audacity](https://www.audacityteam.org/) so that you can find a zero crossing and make a carefully timed cut. For step-by-step instruction on these methods, see [Creating Perfect Audio Loops](https://devforum.roblox.com/t/creating-perfect-audio-loops/2849057). Looping audio is particularly useful for establishing how you want players to feel as soon as they join your experience. For example, the sample aims to provide players a cheerful adventure in a winter wonderland, so it uses an upbeat audio track to positively influence their mood the moment they enter the environment. To see how influential this technique can be, consider how the player's experience would change if you were to swap the looping audio out for a metal track. To recreate the looping 2D audio in the sample [Gingerbread House - Complete Audio](https://www.roblox.com/games/94670255584609/Gingerbread-House-Complete-Audio) place file: 1. Enable a default listener that's attached to your player character. 1. In the **Explorer** window, select the **SoundService**. 2. In the **Properties** window, set **DefaultListenerLocation** to **Character**. When you run the experience, the engine automatically creates an `Class.AudioDeviceOutput` under **SoundService**. 2. In the **Explorer** window, navigate to **SoundService**, then: 1. Insert an **AudioPlayer** object to create an audio source. 2. Insert an **AudioDeviceOutput** object to create a speaker that plays throughout the experience. 3. Insert a **Wire** object to carry the stream from the audio player to the speaker. 3. Select the **AudioPlayer**, then in the **Properties** window, 1. Set **AssetID** to `rbxassetid://1841461968` to play an upbeat audio track. 2. Enable **Looping** so that the audio repeats seemlessly. 3. Set **Volume** to `0.2` to play the audio at a low volume so you can still hear the other audio sources within the experience. 4. Select the **Wire**, then in the **Properties** window, 1. Set **SourceInstance** to your new **AudioPlayer** to specify that you want the wire to carry audio from this specific audio player. 2. Set **TargetInstance** to your new **AudioDeviceOutput** to specify that you want the wire to carry audio to this specific speaker. 5. Back in the **Explorer** window, insert a **Script** into **AudioPlayer**, rename it **LoopBackgroundMusic**, set its **RunContext** property to **Client**, then paste the following code into the script:```lua local audioPlayer = script.Parent audioPlayer:Play() ```**#### Code explanation** The script starts by declaring a variable to represent the script's parent `Class.AudioPlayer`. The script then sets the audio source to play from the moment the player joins the experience to the moment they exit the experience. 6. Playtest the experience to hear the looping background music. ## One shot audio **One shot 2D audio**, or non-directional audio that plays once at a specific time and position unless a player triggers it again, provides players important context about anything you don't want them to miss. Using this type of auditory feedback in your experiences is essential because it provides an instant, clear response to crucial gameplay functions and player actions, such as starting a boss fight or losing a significant amount of health. The following sections provide implementation details for common gameplay scenarios in which players need timely, non-directional feedback, including gameplay feedback, UI element interactions, and voiceover narration for tutorials. ### Gameplay feedback As players progress through your experience, it's important that they receive immediate auditory feedback about the status of anything that impacts their gameplay, such as their score, health, or level progress. Because this type of audio directly connects to player objectives, it's important that these sounds are clear, audible, and convey useful and accurate information. To demonstrate this concept, let's review the following one shot 2D audio from the [Platformer](/docs/en-us/resources/templates.md#platformer) template that plays as players jump and collect coins: - An airy whoosh sound plays as soon as the player presses the jump key on their device. - A high-pitched ding and coins jostling sound play as soon as the player's character touches a coin. Both of these sounds are instantly recognizable within the context of the 3D space, and they help players understand the success of their actions without relying solely on visual cues. When you play multiple sounds at once, certain sounds may be more important for players to hear than others. In this instance, the coin collection sound is conveying more valuable gameplay information than the jump sound, so the experience plays it at a slightly higher volume. There are no standards or rules to achieve a great mix however, so tune the sounds in your experience to reflect what you feel is most important. For example, the sample includes various audio triggers throughout the experience, but they aren't all at the same level of importance for understanding the experience's main objective: collect gumdrops to open the gingerbread house. To ensure that players are aware of what they need to do, the sample triples the amplitude of the auditory feedback whenever the player collects a gumdrop. To recreate the one shot state feedback audio in the sample [Gingerbread House - Complete Audio](https://www.roblox.com/games/94670255584609/Gingerbread-House-Complete-Audio) place file: 1. In the **Explorer** window, navigate to **Workspace** ⟩ **Gumdrops** ⟩ **Gumdrop**, then: 1. Insert an **AudioPlayer** object to create an audio source. 2. Insert an **AudioDeviceOutput** object to create a speaker that plays throughout the experience. 3. Insert a **Wire** object to carry the stream from the audio player to the speaker. 2. Select the **AudioPlayer**, then in the **Properties** window, 1. Set **AssetID** to `rbxassetid://9113723699` to play a gentle chomping audio track. 2. Set **TimePosition** to `0.15` to start the audio track closer to the actual chomping sound. 3. Set **Volume** to `3` to play the audio at a high volume so you hear the gumdrop sound over other audio sources within the experience. 3. Select the **Wire**, then in the **Properties** window, 1. Set **SourceInstance** to your new **AudioPlayer** to specify that you want the wire to carry audio from this specific audio player. 2. Set **TargetInstance** to your new **AudioDeviceOutput** to specify that you want the wire to carry audio to this specific speaker. 4. Duplicate **Gumdrop** until you have three gumdrops, then scatter them around the environment. 5. Back in the **Explorer** window, navigate to **ServerScriptService**, then insert a **Script**, rename it **GumdropService**, set its **RunContext** property to **Server**, then paste the following code into the script:```lua -- Initializing services local Workspace = game:GetService("Workspace") local Players = game:GetService("Players") local ServerStorage = game:GetService("ServerStorage") local TweenService = game:GetService("TweenService") -- Modules local Leaderboard = require(ServerStorage.Leaderboard) local PlayerData = require(ServerStorage.PlayerData) -- Variables local gumdropsFolder = Workspace.Gumdrops local gumdrops = gumdropsFolder:GetChildren() local GUMDROP_KEY_NAME = PlayerData.GUMDROP_KEY_NAME local GUMDROP_AMOUNT_TO_ADD = 1 local function updatePlayerGumdrops(player, updateFunction) -- Update the gumdrop table local newGumdropAmount = PlayerData.updateValue(player, GUMDROP_KEY_NAME, updateFunction) -- Update the gumdrop leaderboard Leaderboard.setStat(player, GUMDROP_KEY_NAME, newGumdropAmount) -- Check if the player has collected three gumdrops if newGumdropAmount >= 3 then -- Play the door event audio when the player collects three gumdrops local audioPlayer = Workspace.Door.AudioPlayer audioPlayer:Play() -- Animate the door to move downward local doorPart = Workspace.Door local tweenInfo = TweenInfo.new(2, Enum.EasingStyle.Linear) local tween = TweenService:Create(doorPart, tweenInfo, {Position = doorPart.Position + Vector3.new(0, -15, 0)}) tween:Play() end end -- Defining the event handler local function onGumdropTouched(otherPart, gumdrop) if gumdrop:GetAttribute("Active") then local character = otherPart.Parent local player = Players:GetPlayerFromCharacter(character) if player then -- Player touched a gumdrop local audioPlayer = gumdrop.AudioPlayer audioPlayer:Play() gumdrop.Transparency = 1 gumdrop:SetAttribute("Active", false) updatePlayerGumdrops(player, function(oldGumdropAmount) oldGumdropAmount = oldGumdropAmount or 0 return oldGumdropAmount + GUMDROP_AMOUNT_TO_ADD end) end end end -- Setting up event listeners for _, gumdrop in gumdrops do gumdrop:SetAttribute("Active", true) gumdrop.Touched:Connect(function(otherPart) onGumdropTouched(otherPart, gumdrop) end) end ```**#### Code explanation** > **Info:** This script handles additional functionality outside of triggering the non-positional audio, such as updating the leaderboard every time a player collects a gumdrop and triggering a positional sound and animation for the gingerbread house's door when a player collects all three gumdrops. This is important for the gameplay of the sample, but it isn't important for understanding how to implement 2D audio, so this explanation will keep those sections at a high level. This script starts by initializing the `Class.Workspace`, `Class.Players`, `Class.ServerStorage`, `and Class.TweenService` services so it can reference their children and functionality. Then, it requires the **Leaderboard** and **PlayerData** modules in `Class.ServerStorage`; these modules are responsible for creating and updating a leaderboard in the upper-right corner of the screen that tracks the amount of gumballs a player collects in the environment. The script's `updatePlayerGumdrops` function is largely in charge of both updating the leaderboard each time a player collects a gumdrop, and waiting for the moment that they collect all three gumdrops so that it can trigger a positional sound and animation for the gingerbread house's door. For more information on this part of the script, see [Add 3D audio - Event feedback](/docs/en-us/tutorials/use-case-tutorials/audio/add-3D-audio.md#event-feedback). The script's `onGumdropTouched` function is where the bulk of the work occurs for triggering 2D audio for gameplay feedback, and it takes two arguments: - `otherPart` - A part that collides with the gumdrop. - `gumdrop` - A spherical part that represents a gumdrop. When anything collides with the gumdrop part, the script checks first to see if the gumdrop is active. If so, it checks to verify that the colliding object is a player. If so, the script then: - Plays the 2D audio track from the audio player. - Turns the gumdrop invisible. - Deactivates the gumdrop so that players cannot interact with the invisible gumdrop. - Updates the leaderboard for the player that touched the gumdrop. Finally, the script sets up event listeners so that for each gumdrop, it connects a function to the `Class.BasePart.Touched|Touched` event, which calls the `onGumdropTouched` function when a player touches the gumdrop. 6. Playtest the experience to hear the chomping sound after you collect a gumdrop. ### UI interaction When players interact with your user interface (UI) elements, such as hovering over a checkbox or selecting a purchase button, it's important to provide instant feedback so that they intuitively understand **how** they're interacting with your UI. For example, many designers change the visual characteristics of UI when players enable or disable settings to communicate the status of the setting. However, for players with visual impairments in which color changes or animations are more difficult to decipher on their own, it's useful to provide multiple forms of sensory feedback for your UI interactions so that they remain accessible and intuitive for as many players as possible. To expand on this concept, let's consider the following UI states that are important for the [UGC Homestore](/docs/en-us/resources/templates.md#ugc-homestore) template's purchase buttons: - **In focus** - Indicates that the player is highlighting a UI element in preparation to select it. - **On hover** - Indicates that the player is hovering over a UI element with their cursor. - **Selected** - Indicates that the player has selected a UI element through their input. All three of these interactions are essential for the template's main user flow of players browsing, selecting, and purchasing avatar clothing and accessories, so it's of the utmost importance that no one is confused about how to interact with the UI. To set players up for success, the template provides both visual and auditory feedback for each UI interaction. **Visual Feedback** _Default view_ _In focus state_ _On hover state_ _Selected state_ To give an example of how you can configure multiple forms of sensory feedback, the sample provides both visual and auditory feedback whenever players press the on-screen peppermint button. When players aren't interacting with the button, it appears like a typical peppermint candy, but when they press the button, the sample: - Plays a delightful jingle audio track. - Tints the button with a teal hue. - Moves the button slightly downward on the screen. From here, you can connect this interaction to all sorts of useful gameplay actions, such as opening a menu or purchasing an item. _Default view_ _Pressed state_ To recreate the one shot UI interaction audio in the sample [Gingerbread House - Complete Audio](https://www.roblox.com/games/94670255584609/Gingerbread-House-Complete-Audio) place file: 1. In the **Explorer** window, navigate to **StarterGui** ⟩ **2DAudioButton**, then: 1. Insert an **AudioPlayer** object to create an audio source. 2. Insert an **AudioDeviceOutput** object to create a speaker that plays throughout the experience. 3. Insert a **Wire** object to carry the stream from the audio player to the speaker. 2. Select the **AudioPlayer**, then in the **Properties** window, set **AssetID** to `rbxassetid://3422389728` to play a retro jingle audio track. 3. Select the **Wire**, then in the **Properties** window, 1. Set **SourceInstance** to your new **AudioPlayer** to specify that you want the wire to carry audio from this specific audio player. 2. Set **TargetInstance** to your new **AudioDeviceOutput** to specify that you want the wire to carry audio to this specific speaker. 4. Back in the **Explorer** window, insert a **LocalScript** into **2DAudioButton**, rename it **PlayAudioWhenPressed**, then paste the following code into the script:```lua local TweenService = game:GetService("TweenService") local buttonGui = script.Parent local buttonImageButton = buttonGui.ButtonFrame.ButtonImageButton local buttonAudioPlayer = buttonGui.AudioPlayer local tweenInfo = TweenInfo.new(.2, Enum.EasingStyle.Exponential) local buttonTweenByIsPressed = { -- Pressed [true] = TweenService:Create(buttonImageButton, tweenInfo, { Position = buttonImageButton.Position + UDim2.fromScale(0, .1), ImageColor3 = Color3.fromRGB(117, 255, 255), }), -- Default [false] = TweenService:Create(buttonImageButton, tweenInfo, { Position = buttonImageButton.Position, ImageColor3 = Color3.fromRGB(255, 255, 255), }), } local function onIsPlayingChanged() local isPlaying = buttonAudioPlayer.IsPlaying local tween = buttonTweenByIsPressed[isPlaying] tween:Play() end onIsPlayingChanged() buttonAudioPlayer:GetPropertyChangedSignal("IsPlaying"):Connect(onIsPlayingChanged) buttonAudioPlayer.Ended:Connect(onIsPlayingChanged) buttonImageButton.Activated:Connect(function(_hit) buttonAudioPlayer:Play() end) ```**#### Code explanation** > **Info:** This script handles additional functionality outside of just playing 2D audio for UI interactions, such as tweening the button's color and position. This is important for providing multiple sources of sensory feedback, but the design requirements for your use case may only require auditory feedback. If you just want to play audio when players touch UI elements, you can remove the tween functionality. The script starts by getting: - The `Class.TweenService` so it can animate UI elements. - The script's parent **2DAudioButton** `Class.ScreenGui` object. - The peppermint candy `Class.ImageButton`. - The relevant audio player with your jingle audio track. The script then defines: - A `TweenInfo` object that specifies that the button's animation will play with an exponential animation style. - Two tweens that represent the button's pressed or unpressed state. - The `true` pressed state moves the button slightly downward on the screen and tints it with a teal hue. - The `false` unpressed state moves the button back to its original position on the screen and removes the previous tint. The remainder of the script is where the bulk of the work occurs for triggering 2D UI interaction feedback, so let's review how the `onIsPlayingChanged` function and event listeners work together for the UI user flow: 1. `buttonImageButton.Activated` listens for a player to click the button, then calls the `Play()` function to start playing the associated audio from the audio player. This process switches the `Class.AudioPlayer.IsPlaying` property from `false` to `true`. 2. `buttonAudioPlayer:GetPropertyChangedSignal("IsPlaying")` listens for the audio player's `IsPlaying` property to change, then calls the `onIsPlayingChanged` function. 3. The `onIsPlayingChanged` function uses this information to trigger the tween that changes its visual appearance on the screen. 4. To prevent the player from accidentally restarting the audio if they were to click the button in rapid succession, `buttonAudioPlayer.Ended` listens for the audio player to finish playing before calling the `onIsPlayingChanged` function again. It's important to note that the `onIsPlayingChanged` event only fires when it changes from `false` to `true`, meaning that it doesn't fire when changing from `true` to `false`. This is intended behavior due to complications with timing of replicating properties from server to client. Because of this, the `Ended` event is provided and listened to in this example to cover both cases. 5. Playtest the experience to hear the jingle every time you press the on-screen button. --- title: "Add 3D audio" url: /docs/en-us/tutorials/use-case-tutorials/audio/add-3D-audio last_updated: 2026-06-29T19:34:14Z description: "Explains how to add directional audio to your experiences." --- # Add 3D audio **3D audio** is directional sound that emits from a particular location in the 3D space, increasing or decreasing in volume depending on the distance and orientation between the audio emitter and listener. This means that as listeners or emitters move around the environment, players can dynamically hear that audio from different directions and volume levels. Using the [Gingerbread House - Start](https://www.roblox.com/games/134812596370919/Gingerbread-House-Start) `.rbxl` file as a starting place and [Gingerbread House - Complete Audio](https://www.roblox.com/games/94670255584609/Gingerbread-House-Complete-Audio) as a reference, this tutorial shows you how to add both looping and one shot 3D audio to your experiences, including guidance on: - Looping environmental sounds that play as soon as players connect to the server. - Triggering audio to inform players about key situational events that are important for their gameplay. - Activating audio to provide players auditory feedback when they interact with 3D objects. - Playing character sound bites that engage and guide players toward points of interest within their environment. If at any point you become stuck in the process, you can use **Gingerbread House - Complete Audio** as a reference to compare your progress. > **Info:** If you want to add non-positional audio to your experience, such as background music, UI interaction sounds, or voiceover cutscene dialogue, see [Add 2D audio](/docs/en-us/tutorials/use-case-tutorials/audio/add-2D-audio.md). ## Audio objects To create directional audio, it's important to understand the audio objects that you will be working with throughout this tutorial. There are six main types of audio objects for 3D audio: - The `Class.AudioPlayer` object loads and plays the **audio file**. - The `Class.AudioEmitter` object is a **virtual speaker** that emits audio into the 3D environment. - The `Class.AudioListener` object is a **virtual microphone** that picks up audio from the 3D environment. - The `Class.AudioDeviceOutput` object is a **physical hardware device** within the real world, such as a speaker or headphones. - The `Class.AudioDeviceInput` object is a **physical microphone** within the real world. - `Class.Wire|Wires` carry audio streams from one object to another. All of these audio objects work together to emit sound just like their real-world counterparts. Let's take a look at how this works in practice using an example of a player wearing a headset while playing an experience with their laptop: - The `Class.AudioPlayer` loads the `1516791621` audio asset ID into the experience for a rain track. - The `Class.AudioEmitter` emits a stream of the rain track audio into the 3D environment. - A `Class.Wire` carries the stream from the `Class.AudioPlayer` to the `Class.AudioEmitter` so that the stream comes out of the 3D speaker. - The character's child `Class.AudioListener` object listens to that sound within the 3D environment and feeds it back to their headset. - The `Class.AudioDeviceOutput` object carries the sound from the `Class.AudioListener` to the player's physical speaker, or in this case, their headphones. - The `Class.AudioDeviceInput` object captures sound from the real world and feeds it back into the experience for voice chat. _Object representations within the experience_ _Object representations in the real world_ The following sections dive deeper and reference these objects as you learn how to play both looping and one shot 3D audio. As you review these objects with the upcoming techniques, you can more accurately predict how to capture and feed sound from the experience to the player and vice-versa. ## Looping audio **Looping 3D audio**, or directional audio that repeats seamlessly as soon as players connect to the server, is a common sound design technique to enhance the atmosphere of the 3D space by making it feel alive and dynamic. In addition, looping 3D audio keeps your environmental sound sources consistent, such as the static of a television or the roar of a waterfall; if these sounds were to suddenly stop, the environment would feel unrealistic. To demonstrate this concept, review how the following 3D audio for two large cascades stops as soon as the unlooped audio track is complete. While the water sounds initially immerse players into the outdoor environment, the sudden auditory change is jarring and unnatural to how a waterfall behaves in the real world. > **Info:** To create a seamless loop, you must match the end and start points of your audio track precisely so that players cannot detect its repetition. This process requires third-party audio editing software like [Reaper](https://www.reaper.fm/) or [Audacity](https://www.audacityteam.org/) so that you can find a zero crossing and make a carefully timed cut. For step-by-step instruction on these methods, see [Creating Perfect Audio Loops](https://devforum.roblox.com/t/creating-perfect-audio-loops/2849057). Similarly, the sample uses this technique for the flowing chocolate waterfall, and adjusts its volume according to the player's distance from the audio emitter. When the player is less than 20 studs away, the emitter emits the sound at full volume. As the player moves further away, the sloshing audio decreases in volume every 20 studs away from the audio emitter. This emulates real-world sound that decreases in volume the further you are from the source. To recreate the looping 3D audio in the sample [Gingerbread House - Complete Audio](https://www.roblox.com/games/94670255584609/Gingerbread-House-Complete-Audio) place file: 1. Enable a default listener that's attached to your player character. 1. In the **Explorer** window, select the **SoundService**. 2. In the **Properties** window, set **DefaultListenerLocation** to **Character**. When you run the experience, the engine automatically: - Creates a `Class.AudioListener` under each player character's `Class.Humanoid.RootPart` so that you can hear sounds shift in your real-world speakers according to the position and scale of sound sources within the experience. - Creates an `Class.AudioDeviceOutput` under **SoundService**. 2. In the **Explorer** window, navigate to **Workspace** ⟩ **WaterfallAudioObject**, then: 1. Insert an **AudioPlayer** object to create an audio source for the waterfall. 2. Insert an **AudioEmitter** object to emit a positional stream from **WaterfallAudioObject**. 3. Insert a **Wire** object to carry the stream from the audio player to the audio emitter. 3. Select the **AudioPlayer**, then in the **Properties** window, 1. Set **AssetID** to `rbxassetid://1516791621` to play a rainy audio track. 2. Enable **Looping** so that the audio repeats seemlessly. 4. Select the **AudioEmitter**, then in the **Properties** window, set **DistanceAttenuation** to `{0: 1}, {20: 0.8}, {40: 0.4}, {80: 0}` so that the sound progressively decreases in volume every 20 studs away from the audio emitter. 5. Select the **Wire**, then in the **Properties** window, 1. Set **SourceInstance** to your new **AudioPlayer** to specify that you want the wire to carry audio from this specific audio player. 2. Set **TargetInstance** to your new **AudioEmitter** to specify that you want the wire to carry audio to this specific audio emitter within the waterfall. 6. Back in the **Explorer** window, insert a **Script** into **WaterfallAudioObject**, rename it **LoopWaterfallMusic**, set its **RunContext** property to **Client**, then paste the following code into the script:```lua local audioPlayer = script.Parent audioPlayer:Play() ```**#### Code explanation** The script starts by declaring a variable to represent the script's parent `Class.AudioPlayer`. The script then sets the audio source to play from the moment the player joins the experience to the moment they exit the experience. 7. Playtest the experience to hear the looping chocolate rain sound when your avatar is near the waterfall. When you rotate your character's head to look in a different direction, the sound dynamically shifts in your real-world speakers according to the emitter's position in the 3D space. ## One shot audio **One shot 3D audio**, or directional audio that plays once at a specific time and position unless a player triggers it again, provides players context about their actions, the environment, and any characters around them. Using this type of auditory feedback in your experiences is essential because it allows players to make strategic decisions like avoiding incoming enemies or picking up useful items. The following sections provide implementation details for common gameplay scenarios in which players need timely, directional feedback, including situational gameplay events, object interaction, and non-playable character dialogue. ### Event feedback As players trigger key situational events within their environment, such as unlocking new gameplay areas or prompting enemy fire, it's essential that they understand where in the 3D space they need to direct their focus and attention. If they don't receive immediate auditory feedback, they may miss information that's important for their gameplay, leading to frustration at not knowing where to go or what to do next. To demonstrate why this is important, let's review the one shot 3D audio from the [Laser Tag](/docs/en-us/resources/templates.md#laser-tag) template that plays from each player's blaster: - A deep pop sound plays for every blast the player fires from their blaster. - A clicking and robotic beep sound plays each time the player reloads their blaster with a new round. Both of these sounds provide situational awareness by alerting all nearby players to the direction of where blasts are coming from so that they can make informed decisions to either join in on the fun or avoid potential danger. The sample uses this same technique to provide players situational awareness about their reward for completing the main objective within the experience. After they collect all three gumdrops, the door to the gingerbread house opens up to allow players access to the present inside. Because there isn't a specific order that players need to collect the gumdrops, it's important that players are aware of the door opening no matter which gumdrop they collect last. Positional sound makes this possible so that players are aware of their success and where they need to go next regardless of their relative distance and direction from the door. To recreate the one shot event feedback 3D audio in the sample [Gingerbread House - Complete Audio](https://www.roblox.com/games/94670255584609/Gingerbread-House-Complete-Audio) place file: 1. In the **Explorer** window, navigate to **Workspace** ⟩ **Door**, then: 1. Insert an **AudioPlayer** object to create an audio source for the volume. 2. Insert an **AudioEmitter** object to emit a positional stream from **Door**. 3. Insert a **Wire** object to carry the stream from the audio player to the audio emitter. 2. Select the **AudioPlayer**, then in the **Properties** window, set **AssetID** to `rbxassetid://5930776613` to play a sliding metal gate audio track. 3. Select the **Wire**, then in the **Properties** window, 1. Set **SourceInstance** to your new **AudioPlayer** to specify that you want the wire to carry audio from this specific audio player. 2. Set **TargetInstance** to your new **AudioEmitter** to specify that you want the wire to carry audio to this specific audio emitter within the volume. 4. Back in the **Explorer** window, navigate to **ServerScriptService**, then insert a **Script**, rename it **GumdropService**, set its **RunContext** property to **Server**, then paste the following code into the script:```lua -- Initializing variables local Workspace = game:GetService("Workspace") local Players = game:GetService("Players") local ServerStorage = game:GetService("ServerStorage") local TweenService = game:GetService("TweenService") -- Modules local Leaderboard = require(ServerStorage.Leaderboard) local PlayerData = require(ServerStorage.PlayerData) -- Variables local gumdropsFolder = Workspace.Gumdrops local gumdrops = gumdropsFolder:GetChildren() local GUMDROP_KEY_NAME = PlayerData.GUMDROP_KEY_NAME local GUMDROP_AMOUNT_TO_ADD = 1 local function updatePlayerGumdrops(player, updateFunction) -- Update the gumdrop table local newGumdropAmount = PlayerData.updateValue(player, GUMDROP_KEY_NAME, updateFunction) -- Update the gumdrop leaderboard Leaderboard.setStat(player, GUMDROP_KEY_NAME, newGumdropAmount) -- Check if the player has collected three gumdrops if newGumdropAmount >= 3 then -- Play the door event audio when the player collects three gumdrops local audioPlayer = Workspace.Door.AudioPlayer audioPlayer:Play() -- Animate the door to move downward local doorPart = Workspace.Door local tweenInfo = TweenInfo.new(2, Enum.EasingStyle.Linear) local tween = TweenService:Create(doorPart, tweenInfo, {Position = doorPart.Position + Vector3.new(0, -15, 0)}) tween:Play() end end -- Defining the event handler local function onGumdropTouched(otherPart, gumdrop) if gumdrop:GetAttribute("Enabled") then local character = otherPart.Parent local player = Players:GetPlayerFromCharacter(character) if player then -- Player touched a gumdrop local audioPlayer = gumdrop.AudioPlayer audioPlayer:Play() gumdrop.Transparency = 1 gumdrop:SetAttribute("Enabled", false) updatePlayerGumdrops(player, function(oldGumdropAmount) oldGumdropAmount = oldGumdropAmount or 0 return oldGumdropAmount + GUMDROP_AMOUNT_TO_ADD end) print("Player collected gumdrop") end end end -- Setting up event listeners for _, gumdrop in gumdrops do gumdrop:SetAttribute("Enabled", true) gumdrop.Touched:Connect(function(otherPart) onGumdropTouched(otherPart, gumdrop) end) end ```**#### Code explanation** > **Info:** This script handles additional functionality outside of triggering the positional audio, such as updating the leaderboard and triggering a non-positional sound every time a player collects a gumdrop. This is important for the gameplay of the sample, but it isn't important for understanding how to implement 2D audio, so this explanation will keep those sections at a high level. This script starts by initializing the `Class.Workspace`, `Class.Players`, `Class.ServerStorage`, and `Class.TweenService` services so it can reference their children and functionality. Then, it requires the **Leaderboard** and **PlayerData** modules in `Class.ServerStorage`; these modules are responsible for creating and updating a leaderboard in the upper-right corner of the screen that tracks the amount of gumballs a player collects in the environment. The script's `updatePlayerGumdrops` function is where the bulk of the work occurs for triggering 3D audio for event feedback, and it takes two arguments: - `player` - A player that collects a gumdrop. - `updateFunction` - A callback function that updates the player's collected gumdrop amount. When a player collides with a gumdrop, the script: - Gets the player's new gumdrop collection amount value by calling the `PlayerData.updateValue` function. - Updates the leaderboard with this new amount by calling the `Leaderboard.setStat` function. - Checks to see if the amount is greater than or equal to `3`. When this value is greater than or equal to `3`, the script: - Plays the 3D audio track from the audio player to the audio emitter. - Moves the door linearly `15` studs below its current position. The rest of the script is largely in charge of detecting anything that collides with the gumdrop is a player so that it can trigger a non-positional sound for collection feedback. For more information on this part of the script, see [Add 2D audio - Gameplay feedback](/docs/en-us/tutorials/use-case-tutorials/audio/add-2D-audio.md). 5. Playtest the experience to hear the slide gate sound after you collect all three gumdrops in the environment. When you rotate the camera, the sound dynamically shifts in your real-world speakers so that you hear it according to the emitter's position in the 3D space. ### Object interaction When players interact with 3D objects within their environment, such as turning on a light switch or picking up a weapon, it's important to provide instant feedback so that they intuitively understand **how** they're interacting with the object. The pairing of both a visual and auditory feedback reinforces the cause and effect relationship between the player's actions and the environmental response. To expand on this concept, let's review the following one shot 3D audio from the [Plant](https://www.roblox.com/games/18221564209/Plant-Complete-Audio) sample for the user flow of gardening a cabbage: - A gentle clink sound plays when the player plants a seed. - A wet, splash-like sound plays when the player waters their growing plant. - A clip sound plays when the player collects the full-grown plant. - A soft thud sound plays when the player places the cabbage into the wagon. All of these sounds reinforce the player's [proximity prompt](/docs/en-us/ui/proximity-prompts.md) key interactions with the object changing forms in the 3D space. For players with visual impairments in which color changes or animations are more difficult to decipher on their own, providing these multiple forms of sensory feedback help your 3D object interactions remain accessible and intuitive for as many players as possible. To give a different example of how you can configure object interaction with multiple forms of sensory feedback, the sample provides both visual and auditory feedback whenever players step on the 3D peppermint button within the gingerbread house. When players aren't interacting with the button, it appears like a typical peppermint candy, but when they step on the button, the sample: - Plays a celebratory jingle audio track. - Tints the sides of the button with a green hue. - Moves the button into the ground. From here, you can connect this interaction to all sorts of unique gameplay actions, such as unlocking an item or triggering a special ability. _Default view_ _Pressed state_ To recreate the one shot object interaction 3D audio in the sample [Gingerbread House - Complete Audio](https://www.roblox.com/games/94670255584609/Gingerbread-House-Complete-Audio) place file: 1. In the **Explorer** window, navigate to **Workspace** ⟩ **3DAudioButton**, then: 1. Insert an **AudioPlayer** object to create an audio source for the button. 2. Insert an **AudioEmitter** object to emit a positional stream from **3DAudioButton**. 3. Insert a **Wire** object to carry the stream from the audio player to the audio emitter. 2. Select the **AudioPlayer**, then in the **Properties** window, set **AssetID** to `rbxassetid://1846248593` to play a cheerful, celebratory audio track. 3. Select the **Wire**, then in the **Properties** window, 1. Set **SourceInstance** to your new **AudioPlayer** to specify that you want the wire to carry audio from this specific audio player. 2. Set **TargetInstance** to your new **AudioEmitter** to specify that you want the wire to carry audio to this specific audio emitters within the button. 4. Back in the **Explorer** window, insert a **Script** into **3DAudioButton**, rename it **PlayAudioWhenPressed**, then paste the following code into the script:```lua local TweenService = game:GetService("TweenService") local buttonModel = script.Parent.Parent local buttonPart = buttonModel.ButtonPart local buttonPressedAudioPlayer = buttonModel.ButtonPressedAudioPlayer local tweenInfo = TweenInfo.new(.2, Enum.EasingStyle.Exponential) local buttonTweenByIsPressed = { -- Pressed [true] = TweenService:Create(buttonPart, tweenInfo, { Size = buttonPart.Size / Vector3.new(2, 1, 1), Color = Color3.fromRGB(75, 151, 75), }), -- Default [false] = TweenService:Create(buttonPart, tweenInfo, { Size = buttonPart.Size, Color = Color3.fromRGB(196, 40, 28), }), } local function onIsPlayingChanged() local isPlaying = buttonPressedAudioPlayer.IsPlaying local tween = buttonTweenByIsPressed[isPlaying] tween:Play() end onIsPlayingChanged() buttonPressedAudioPlayer:GetPropertyChangedSignal("IsPlaying"):Connect(onIsPlayingChanged) buttonPressedAudioPlayer.Ended:Connect(onIsPlayingChanged) buttonPart.Touched:Connect(function(_hit) buttonPressedAudioPlayer:Play() end) ```**#### Code explanation** > **Info:** This script handles additional functionality outside of just playing 3D audio for object interactions, such as tweening the button's color and position. This is important for providing multiple sources of sensory feedback, but the design requirements for your use case may only require auditory feedback. If you just want to play audio when players touch the model, you can remove the tween functionality. The script starts by getting: - The `Class.TweenService` so it can animate the button's part that sticks out of the ground. - The script's parent **3DAudioButton** model. - The button's part that sticks out of the ground. - The relevant audio player with your celebration sound audio track. The script then defines: - A `TweenInfo` object that specifies that the button's animation will play with an exponential animation style. - Two tweens that represent the button's pressed or unpressed state. - The `true` pressed state moves the button slightly downward into the ground and tints the sides of the part with a green hue. - The `false`unpressed state moves the button back to its original position and removes the previous tint. The remainder of the script is where the bulk of the work occurs for object interaction feedback, so let's review how the `onIsPlayingChanged` function and event listeners work together: 1. `buttonPart.Touched` listens for a player to touch the button, then calls the `Play()` function to start playing the associated audio from the audio player. This process switches the `Class.AudioPlayer.IsPlaying` property from `false` to `true`. 2. `buttonPressedAudioPlayer:GetPropertyChangedSignal("IsPlaying")` listens for the audio player's `IsPlaying` property to change, then calls the `onIsPlayingChanged` function. 3. The `onIsPlayingChanged` function uses this information to trigger the tween that changes its visual appearance in the 3D space. 4. To prevent the player from accidentally restarting the audio if they were to quickly jump on the button in rapid succession, `buttonPressedAudioPlayer.Ended` listens for the audio player to finish playing before calling the `onIsPlayingChanged` function again. It's important to note that the `onIsPlayingChanged` event only fires when it changes from `false` to `true`, meaning that it doesn't fire when changing from `true` to `false`. This is intended behavior due to complications with timing of replicating properties from server to client. Because of this, the `Ended` event is provided and listened to in this example to cover both cases. 5. Playtest the experience to hear the celebration sound when your player character touches the 3D button in the gingerbread house. When you walk away from the button, the sound's volume decreases. ### Character dialogue Providing directional audio from your non-playable characters (NPC) is helpful to guide players toward points of interest within their environment, and add depth to their interactions with other characters. In fact, in narrative-driven experiences, many designers strategically use character dialogue to indirectly teach players about their character, ally and enemy characters, or the world itself. Common examples of this technique include: - **Dialogue style** to set the tone of your experience. - **Banter** to teach players about character relationships. - **Enemy conversations** to confess motivations or their position in relation to the player. - **Player characters speaking their thoughts aloud** to gently guide the player to what they should do next, such as heal themselves, move to another location, or find an item. - **Ally characters talking with the player character** to reveal in-experience world details like its history, culture, and social issues. To demonstrate what this can look like in practice, let's review the following one shot 3D audio from [Beyond the Dark](https://www.roblox.com/games/7208091524/Beyond-the-Dark-Vistech-Showcase) showcase that periodically plays when players are in the main lobby area of the space station. Using the space station as a character, this dialogue clip provides players important context and lore about the overall setting. For example, from this single sentence players learn: - They're in outer space, specifically on a space station named Kerr-Newman Deep Space Relay 14. - Their environment is futuristic and welcoming. - They are a visitor and will likely be leaving soon. These details together immerse players within their environment, and add a sense of urgency to their mission. However, if players don't know what their main mission is, you can also use character dialogue to inform players what you want them to do within your experience. To illustrate, the sample uses a **volume**, or invisible region within the 3D space, to trigger character dialogue from the snowman to guide players to collect three gumdrops in order to open the door to his home. As one of the first things that players see as they join the experience, players are more likely to trigger the dialogue and know what they need to do to be successful. _The volume around the snowman uses collision feedback to play audio when players enter the 3D region._ To recreate the one shot character dialogue 3D audio in the sample [Gingerbread House - Complete Audio](https://www.roblox.com/games/94670255584609/Gingerbread-House-Complete-Audio) place file: 1. In the **Explorer** window, navigate to **Workspace** ⟩ **DialogueVolume**, then: 1. Insert an **AudioPlayer** object to create an audio source for the volume. 2. Insert an **AudioEmitter** object to emit a positional stream from **DialogueVolume**. 3. Insert a **Wire** object to carry the stream from the audio player to the audio emitter. 2. Select the **AudioPlayer**, then in the **Properties** window, set **AssetID** to `rbxassetid://92917410841704` to play an instructional audio track for the objective of the experience. 3. Select the **Wire**, then in the **Properties** window, 1. Set **SourceInstance** to your new **AudioPlayer** to specify that you want the wire to carry audio from this specific audio player. 2. Set **TargetInstance** to your new **AudioEmitter** to specify that you want the wire to carry audio to this specific audio emitter within the volume. 4. Back in the **Explorer** window, navigate to **StarterPlayer** ⟩ **StarterCharacterScripts**, insert a **LocalScript**, rename it **PlayAudioWhenInVolume**, and paste the following code into the local script:```lua local Workspace = game:GetService("Workspace") local Players = game:GetService("Players") local humanoid = script.Parent:WaitForChild("Humanoid") local volumeDetector = Workspace.DialogueVolume local trigger = humanoid:WaitForChild("Animator") local debounce = false local localPlayer = Players.LocalPlayer volumeDetector.Touched:Connect(function(hit) if debounce then return end local hitCharacter = hit:FindFirstAncestorWhichIsA("Model") local hitPlayer = Players:GetPlayerFromCharacter(hitCharacter) if hitPlayer ~= localPlayer then return end debounce = true local audioPlayer = Workspace.DialogueVolume.AudioPlayer audioPlayer:Play() audioPlayer.Ended:Wait() debounce = false end) ```**#### Code explanation** This script starts by getting the Workspace and Players services so it can reference their children and functionality. For each player character that loads or respawns back into the experience, the script waits for: - The character's `Class.Humanoid` and `Class.Animator` objects. - The volume object in the workspace named **DialogueVolume**. When anything collides with the volume, the `Touched` event handler function gets the first ancestor that's a `Class.Model`, which should be the character if the `Class.BasePart` that collided with the volume is a descendant of a character model. If it is, the function then: - Sets debounce to `true`. - Plays and waits for the audio to end. - Sets debounce back to `false`. Setting debounce from `false` to `true` to `false` again after the audio finishes playing is a debounce pattern that prevents the audio from repeatedly triggering as players continuously collide with the volume. For more information on this debounce pattern, see [Debounce - Detect collisions](/docs/en-us/scripting/debounce.md#detect-collisions). 5. Playtest the experience to hear the instructional character dialogue when your player character touches the volume around the snowman. --- title: "Add text-to-speech" url: /docs/en-us/tutorials/use-case-tutorials/audio/add-text-to-speech last_updated: 2026-06-29T19:34:14Z description: "Explains how to add text-to-speech audio to your experiences." --- # Add text-to-speech **Text-to-speech** is a form of assistive technology that converts text strings into speech sounds using an artificial voice. In addition to improving the accessibility of your experiences for players with vision, mobility, or cognitive disabilities, TTS allows you to generate speech dynamically so that you don't have to pre-record audio for all possible narrative scenarios. Using the [Gingerbread House - Start](https://www.roblox.com/games/134812596370919/Gingerbread-House-Start) `.rbxl` file as a starting place and [Gingerbread House - Text-to-Speech](https://www.roblox.com/games/129041658365712/Gingerbread-House-Text-to-Speech) as a reference, this tutorial shows you how to add both basic and context-aware TTS audio to your experiences, including guidance on: - Triggering TTS for common gameplay scenarios that will never change, such as UI interactions and tutorials. - Configuring TTS so that it adapts to player actions, environmental status, or flexible objectives. If at any point you become stuck in the process, you can use **Gingerbread House - Text-to-Speech** as a reference to compare your progress. ## Audio Objects To create TTS audio, it's important to understand the audio objects that you will be working with throughout this tutorial. There are five main types of audio objects for TTS: - The `Class.AudioTextToSpeech` object converts text strings into speech sounds. - The `Class.AudioEmitter` object is a **virtual speaker** that emits audio into the 3D environment. - The `Class.AudioListener` object is a **virtual microphone** that picks up audio from the 3D environment. - The `Class.AudioDeviceOutput` object is a **physical hardware device** within the real world, such as a speaker or headphones. - `Class.Wire|Wires` carry audio streams from one object to another. All of these audio objects work together to emit TTS sound in response to player actions. Let's take a look at how this works in practice for 3D audio using an example of a player wearing a headset while playing an experience with their laptop: - The `Class.AudioTextToSpeech` loads and converts text into audio whenever a player touches a part near a non-playable character (NPC). - The `Class.AudioEmitter` emits a stream of the TTS audio from the NPC into the 3D environment - A `Class.Wire` carries the stream from the `Class.AudioTextToSpeech` to the `Class.AudioEmitter` so that the stream comes out of the NPC. - The character's child `Class.AudioListener` object listens to that sound within the 3D environment and feeds it back to their headset. - The `Class.AudioDeviceOutput` object carries the sound from the `Class.AudioListener` to the player's physical speaker, or in this case, their headphones. The following sections dive deeper and reference these objects as you learn how to play both basic and context-aware audio. As you review these objects with the upcoming techniques, you can more accurately predict how to capture and feed sound from the experience to the player. ## Basic TTS Basic TTS is the most common form of text-to-speech in which the artificial voice reads a text string regardless of player or environment context. This means that whenever the player triggers the TTS audio, the words and the way that the artificial voice reads the words remains consistent no matter the state of the player, their actions, or environmental status. This form of TTS is useful in most gameplay scenarios, such as players interacting with UI menus, tutorials, or routine NPC interactions like vendor offerings or enemy barks. Roblox supplies the following types of voices that you can experiment with for any of these interactions: | VoiceID | Voice Description | Audio Example | | --- | --- | --- | | 1 | British male | | | 2 | British female | | | 3 | United States male #1 | | | 4 | United States female #1 | | | 5 | United States male #2 | | | 6 | United States female #2 | | | 7 | Australian male | | | 8 | Australian female | | | 9 | Retro voice #1 | | | 10 | Retro voice #2 | | | 11 | Host voice | | | 101 | Spanish male | | | 102 | Spanish female | | | 201 | German male | | | 202 | German female | | | 301 | Italian male | | | 302 | Italian female | | | 401 | French male | | | 402 | French female | | To recreate the basic 3D TTS audio in the sample [Gingerbread House - Text-to-Speech](https://www.roblox.com/games/129041658365712/Gingerbread-House-Text-to-Speech) place file: 1. Enable a default listener that's attached to your player character. 1. In the **Explorer** window, select the **SoundService**. 2. In the **Properties** window, set **DefaultListenerLocation** to **Character**. When you run the experience, the engine automatically: - Creates a `Class.AudioListener` under each player character's `Class.Humanoid.RootPart` so that you can hear sounds shift in your real-world speakers according to the position and scale of sound sources within the experience. - Creates an `Class.AudioDeviceOutput` under **SoundService**. 2. In the **Explorer** window, navigate to **Workspace** ⟩ **DialogueVolume**, then: 1. Insert an **AudioTextToSpeech** object to create an audio speech generator for the volume around the snowman. 2. Insert an **AudioEmitter** object to emit a positional stream from **DialogueVolume**. 3. Insert a **Wire** object to carry the stream from the audio speech generator to the audio emitter. 3. Select the **AudioTextToSpeech** object, then in the **Properties** window: 1. Set **Text** to "Collect every single last gumpdrop to open my home!" 2. Set **VoiceId** to `2` to set the artificial voice to emulate a British female. 3. Set the **Volume** to `3` to play the audio at a high volume so you hear the TTS sound over other audio sources within the experience. 4. Select the **Wire**, then in the **Properties** window: 1. Set **SourceInstance** to your new **AudioTextToSpeech** to specify that you want the wire to carry audio from this specific audio speech generator. 2. Set **TargetInstance** to your new **AudioEmitter** to specify that you want the wire to carry audio to this specific audio emitter within the volume. 5. Back in the **Explorer** window, navigate to **StarterPlayer** ⟩ **StarterCharacterScripts**, then insert a **LocalScript**, rename it **PlayBasicTTSAudioWhenInVolume**, and paste the following code into the local script: ```lua local Workspace = game:GetService("Workspace") local Players = game:GetService("Players") local humanoid = script.Parent:WaitForChild("Humanoid") local volumeDetector = Workspace.DialogueVolume local trigger = humanoid:WaitForChild("Animator") local debounce = false local localPlayer = Players.LocalPlayer volumeDetector.Touched:Connect(function(hit) if debounce then return end local hitCharacter = hit:FindFirstAncestorWhichIsA("Model") local hitPlayer = Players:GetPlayerFromCharacter(hitCharacter) if hitPlayer ~= localPlayer then return end debounce = true local audioTextToSpeech = Workspace.DialogueVolume.AudioTextToSpeech audioTextToSpeech:Play() audioTextToSpeech.Ended:Wait() debounce = false end) ``` **#### Code explanation** This script starts by getting the `Class.Workspace` and `Class.Players` services so it can reference their children and functionality. For each player character that loads or respawns back into the experience, the script waits for: - The character's `Class.Humanoid` and `Class.Animator` objects. - The volume object in the workspace named **DialogueVolume**. When anything collides with the volume, the `Touched` event handler function gets the first ancestor that's a `Class.Model`, which should be the character if the `Class.BasePart` that collided with the volume is a descendant of a character model. If it is, the function then: - Sets debounce to `true`. - Plays and waits for the TTS audio to end. - Sets debounce back to `false`. Setting debounce from `false` to `true` to `false` again after the basic TTS audio finishes playing is a debounce pattern that prevents the audio from repeatedly triggering as players continuously collide with the volume. For more information on this debounce pattern, see [Debounce - Detect collisions](/docs/en-us/scripting/debounce.md#detect-collisions). 1. Playtest the experience to hear the instructional character dialogue when your player character touches the volume around the snowman. You can further experiment with this audio by modifying the `Class.AudioTextToSpeech.Text|Text`, `Class.AudioTextToSpeech.VoiceID|VoiceID`, `Class.AudioTextToSpeech.Pitch|Pitch`, and `Class.AudioTextToSpeech.Speed|Speed` properties to new values. The generated speech becomes entirely different without the need to record and upload a new audio file for each scenario. | | | | | --- | --- | --- | | | | | ## Context-aware TTS Context-aware TTS is a more advanced form of text-to-speech in which the artificial voice reads a text string in relation to the player, the state of their environment, or gameplay status. This means that whenever the player triggers the TTS audio, the words and the way the artificial voice reads the words adapts accordingly. This form of TTS is useful for gameplay scenarios that are ever-changing, such as directional audio cues, objective status, or unique NPC interactions. Consequently, because context-aware TTS needs to transform to be accurate, you must configure gameplay elements so that you can track their status as players navigate through the environment and complete gameplay objectives. While there are many ways to accomplish this task, the sample uses custom attributes to track the **color** and **location** of each gumdrop that the player must collect in order to enter the gingerbread house. For more information on attributes, see [Properties and attributes](/docs/en-us/scripting/attributes.md). **Each gumdrop object has attributes that describe their color and location in the environment.** ![A close up view of the yellow gumdrop](../../../assets/tutorials/add-text-to-speech/YellowGumdrop.png) ![A close up view of the green gumdrop](../../../assets/tutorials/add-text-to-speech/GreenGumdrop.png) ![A close up view of the red gumdrop](../../../assets/tutorials/add-text-to-speech/RedGumdrop.png) To recreate the context-aware 3D TTS audio in the sample [Gingerbread House - Text-to-Speech](https://www.roblox.com/games/129041658365712/Gingerbread-House-Text-to-Speech) place file: 1. In the **Explorer** window, navigate to **Workspace** ⟩ **GumDrops**. 2. Configure three custom attributes to track the yellow gumdrop. 1. Select the yellow gumdrop, then in the **Properties** window, navigate to the **Attributes** section, then click the plus icon. A pop-up dialog displays. 2. In the **Name** field, input **ColorDescription**. 3. In the **Type** dropdown menu, select **string**. 4. Click the **Save** button. 5. Set the new **ColorDescription** attribute to **yellow**. 6. Using this process, create two more attributes using the following values. | Name | Type | Value | | --- | --- | --- | | HintOrder | number | `0` | | LocationDescription | string | by the waterfall | 3. Configure three custom attributes to track the green gumdrop. 1. In the **Explorer** window, select the green gumdrop. 2. In the **Properties** window, create three attributes using the following values. | Name | Type | Value | | --- | --- | --- | | ColorDescription | string | green | | HintOrder | number | `1` | | LocationDescription | string | on the ledge | 4. Configure three custom attributes to track the red gumdrop. 1. In the **Explorer** window, select the red gumdrop. 2. In the **Properties** window, create three attributes using the following values. | Name | Type | Value | | --- | --- | --- | | ColorDescription | string | red | | HintOrder | number | `2` | | LocationDescription | string | behind the fence | 5. In the **Explorer** window, navigate to **Workspace** ⟩ **HintVolume**, then: 1. Insert an **AudioTextToSpeech** object to create an audio speech generator for the volume around the reindeer. 2. Insert an **AudioEmitter** object to emit a positional stream from **HintVolume**. 3. Insert a **Wire** object to carry the stream from the audio speech generator to the audio emitter. 6. Select the **Wire**, then in the **Properties** window: 1. Set **SourceInstance** to your new **AudioTextToSpeech** to specify that you want the wire to carry audio from this specific audio speech generator. 2. Set **TargetInstance** to your new **AudioEmitter** to specify that you want the wire to carry audio to this specific audio emitter within the volume. 7. Back in the **Explorer** window, navigate to **StarterPlayer** ⟩ **StarterCharacterScripts**, then insert a **LocalScript**, rename it **PlayContextTTSAudioWhenInVolume**, and paste the following code into the local script: ```lua local Workspace = game:GetService("Workspace") local Players = game:GetService("Players") local humanoid = script.Parent:WaitForChild("Humanoid") local volumeDetector = Workspace.HintVolume local trigger = humanoid:WaitForChild("Animator") local debounce = false local localPlayer = Players.LocalPlayer function getRemainingGumdrops(): {Part} local gumdropsFolder = Workspace.Gumdrops local gumdrops = gumdropsFolder:GetChildren() local remainingGumdrops = {}; for _, gumdrop in gumdrops do if gumdrop:GetAttribute("Active") then remainingGumdrops[#remainingGumdrops + 1] = gumdrop end end table.sort(remainingGumdrops, function(a, b) return a:GetAttribute("HintOrder") < b:GetAttribute("HintOrder") end) return remainingGumdrops end function getReindeerHint(remainingGumdrops: {Part}): string local remainingGumdrops = getRemainingGumdrops() if (#remainingGumdrops == 0) then return "There are no gumdrops left. Check inside the house." end local nextGumdrop = remainingGumdrops[1] local colorDescription = nextGumdrop:GetAttribute("ColorDescription") local locationDescription = nextGumdrop:GetAttribute("LocationDescription") local message = #remainingGumdrops > 1 and "There are " .. #remainingGumdrops .. " gumdrops left. Look for the " .. colorDescription .. " one " .. locationDescription .. "." or "There is one gumdrop left. It's " .. colorDescription .. " and it's " .. locationDescription .. "." return message end volumeDetector.Touched:Connect(function(hit) if debounce then return end local hitCharacter = hit:FindFirstAncestorWhichIsA("Model") local hitPlayer = Players:GetPlayerFromCharacter(hitCharacter) if hitPlayer ~= localPlayer then return end print("Player touched volume.") debounce = true local remainingGumdrops = getRemainingGumdrops() local message = getReindeerHint(remainingGumdrops) local audioTextToSpeech = Workspace.HintVolume.AudioTextToSpeech audioTextToSpeech.Text = message audioTextToSpeech:Play() audioTextToSpeech.Ended:Wait() debounce = false end) ``` **#### Code explanation** This script starts by getting the `Class.Workspace` and `Class.Players` services so it can reference their children and functionality. For each player character that loads or respawns back into the experience, the script waits for: - The character's `Class.Humanoid` and `Class.Animator` objects. - The volume object in the workspace named **HintVolume**. The script's `getRemainingGumdrops()` function returns a list of gumdrop `Class.Part` objects within the **Gumdrops** folder and filters out any part that the player has collected. The remaining gumdrops are sorted in a specific order according to each gumdrop's `HintOrder` attribute. The script's `getReindeerHint(remainingGumdrops)` function takes that list `from getRemainingGumdrops()` and returns a string message that describes where the player can find any remaining gumdrops, including both the color and location description of the next gumdrop the player needs to collect. However, if the player has collected all gumdrops in the environment, the string message describes where to go next after they have collected all gumdrops in the environment. When anything collides with the volume, the `Touched` event handler function gets the first ancestor that's a `Class.Model`, which should be the character if the `Class.BasePart` that collided with the volume is a descendant of a character model. If it is, the function then: - Sets debounce to `true`. - Gets the list of remaining gumdrops and the string message describing the next gumdrop the player needs to find. - Converts the string into audio using the `Class.AudioTextToSpeech` object, plays the audio, and waits for the audio to end. - Sets debounce back to `false`. Setting debounce from `false` to `true` to `false` again after the context-aware TTS audio finishes playing is a debounce pattern that prevents the audio from repeatedly triggering as players continuously collide with the volume. For more information on this debounce pattern, see [Debounce - Detect collisions](/docs/en-us/scripting/debounce.md#detect-collisions). 1. Playtest the experiences to hear contextual hints when your player character touches the volume around the reindeer. The TTS audio changes according to the number and color of gumdrops the player character has already found in the environment. --- title: "Add voice chat" url: /docs/en-us/tutorials/use-case-tutorials/audio/add-voice-chat last_updated: 2026-06-29T19:34:14Z description: "Explains how to add different forms of voice chat to your experiences." --- # Add voice chat **Voice chat** is a proximity-based chat feature that simulates realistic communication by adjusting the volume of players speaking as they move closer or further from one another in the 3D space. By letting players talk to each other with their microphones, they are able to socialize and strategize together in real time across the globe to complete your experience's objectives. Using the [Gingerbread Island - Voice Chat](https://www.roblox.com/games/91891961746476/Gingerbread-Island-Voice-Chat) `.rbxl` file as a reference, this tutorial shows you how to incorporate different forms of voice chat into your gameplay, including guidance on: - Sorting players into teams that can only use voice chat with their teammates. - Allowing players to temporarily activate their microphone while they're pushing a specific button on their device. - Configuring time periods where voice chat is either enabled or disabled. As you review the following sections alongside the sample, you can adjust each code sample to better meet the needs of your own voice chat requirements. > **Info:** Voice chat is only available to players who are at least 13 years of age and have verified their account through either a phone number or government issued ID, depending on their country. For a full list of country verification requirements, see [Voice chat](/docs/en-us/chat/voice-chat.md). ## Configure settings In order for team chat, push to chat, or time-based chat to work appropriately, you must [configure your voice chat setup](/docs/en-us/chat/voice-chat.md#enable-voice-chat) to enable voice chat and create the appropriate audio objects necessary for picking up and emitting audio within the 3D environment. When players join your experience, your voice chat setup now: - Allows voice-eligible players to use their microphones. - Creates and parents an `Class.AudioDeviceInput` object under each voice-eligible `Class.Player` object. - Creates and parents an `Class.AudioEmitter` object under each voice-eligible `Class.Player.Character`. - Creates and parents an `Class.AudioListener` under the `Class.Workspace.CurrentCamera`. The following sections detail three unique voice chat configurations using these settings. As you follow along with the sample [Gingerbread Island - Voice Chat](https://www.roblox.com/games/91891961746476/Gingerbread-Island-Voice-Chat) place file, you can enable the corresponding disabled script in the **Explorer** window to test their behavior. To ensure that each one works properly, **make sure to disable the script again before moving on to the next configuration**. ![Disabled scripts in the Explorer window](../../../assets/tutorials/add-voice-chat/DisabledScripts.jpg) ## Add team chat **Team chat** is a voice chat configuration in which only players on the same team can speak or hear one another in an experience. Integrating team chat into your gameplay is useful when you want players to collaborate and strategize together to solve problems in the experiences, such as coordinating information for enemy team positions, resources, and assignments. To recreate the team voice chat in the sample [Gingerbread Island - Voice Chat](https://www.roblox.com/games/91891961746476/Gingerbread-Island-Voice-Chat) place file: 1. In the **Explorer** window, insert a **Script** into **ServerScriptService**. 2. Rename the script **TeamChat**, then paste the following code into the script:```lua local Teams = game:GetService("Teams") local Players = game:GetService("Players") local redTeam = Instance.new("Team", Teams) redTeam.TeamColor = BrickColor.new("Bright red") redTeam.AutoAssignable = true redTeam.Name = "Red Team" local blueTeam = Instance.new("Team", Teams) blueTeam.TeamColor = BrickColor.new("Bright blue") blueTeam.AutoAssignable = true blueTeam.Name = "Blue Team" local function getUserIds(team : Team) : {number} local userIds = {} for _, player : Player in team:GetPlayers() do table.insert(userIds, player.UserId) end return userIds end local function getDevices(team : Team) : {AudioDeviceInput} local devices = {} for _, player : Player in team:GetPlayers() do local device : AudioDeviceInput = player:FindFirstChild("AudioDeviceInput") if not device then continue end table.insert(devices, device) end return devices end local function updateTeam(team : Team) local users = getUserIds(team) for _, device in getDevices(team) do device.AccessType = Enum.AccessModifierType.Allow device:SetUserIdAccessList(users) end end local function onDeviceAdded(device : AudioDeviceInput) local player : Player = device.Parent if player.Team then updateTeam(player.Team) end end local function onPlayerAdded(player : Player) local device = player:FindFirstChild("AudioDeviceInput") if device then onDeviceAdded(device) end player.ChildAdded:Connect(function(child) if child.Name == "AudioDeviceInput" then onDeviceAdded(child) end end) end updateTeam(blueTeam) updateTeam(redTeam) for _, player in Players:GetPlayers() do onPlayerAdded(player) end Players.PlayerAdded:Connect(onPlayerAdded) blueTeam.PlayerAdded:Connect(function() updateTeam(blueTeam) end) blueTeam.PlayerRemoved:Connect(function() updateTeam(blueTeam) end) redTeam.PlayerAdded:Connect(function() updateTeam(redTeam) end) redTeam.PlayerRemoved:Connect(function() updateTeam(redTeam) end) ```**#### Code explanation** The script starts by getting the `Class.Teams` and `Class.Players` services so that it can use their out-of-the-box functionality to sort players into teams as soon as they join the experience. For example, without any additional scripting effort, the `Class.Teams` service handles actions like: - Sorting and balancing players evenly into each team. - Grouping players under their team on the leaderboard. - Tinting player names in the 3D space to their corresponding team color. Using this service, the script creates two distinct `Class.Team` objects with different `Class.Team.Color|Color` properties values to represent each team: **bright red** for one team and **bright blue** for the other. The script then defines three functions where the bulk of the work occurs for setting up the team voice chat configuration: - `getUserIds` - Returns an array of userIDs for all players in a team. - `getDevices` - Returns an array of `Class.AudioDeviceInput` objects for all players in a team. Every Class.`AudioDeviceInput` object represents a player's **physical microphone** in the real world. - `updateTeam` - Retrieves all userIDs from `getUserIds`, iterates over the `Class.AudioDeviceInput` objects from `getDevices`, sets their `Enum.AccessModifierType` property to **Allow** so that only the userIDs in each team are permitted to hear from the microphones of their teammates, then updates the `SetUserIdAccessList` with the userIDs from the team. The remainder of the script controls how these functions and event listeners work together: - When a new player joins the experience, the script verifies if the player has a microphone and updates their team. - When the script detects a new `Class.AudioDeviceInput` object, it calls `updateTeam` for their respective red or blue team. - As players join or leave the experience, the script connects to the `PlayerAdded` and `PlayerRemoved` events to update each team's settings. 3. Playtest the experience with a couple friends to verify that teammates from each team can only hear each other through voice chat. ## Add push to chat **Push to chat** is a voice chat configuration in which players are only able to activate their microphone within an experience while they press and hold a specific button on their device. Integrating push to chat into your gameplay is useful when you want players to have more privacy and control when they want to be heard over other players and ambient noise. To recreate the push to voice chat in the sample [Gingerbread Island - Voice Chat](https://www.roblox.com/games/91891961746476/Gingerbread-Island-Voice-Chat) place file: 1. In the **Explorer** window, insert a **Script** into **ReplicatedStorage**. 2. In the **Properties** window, set **RunContext** to **Client** so that the script only controls the local player's microphone. 3. Rename the script **PushToChat**, then paste the following code into the script:```lua local Players = game:GetService("Players") local UserInputService = game:GetService("UserInputService") local audioIn: AudioDeviceInput = Players.LocalPlayer:WaitForChild("AudioDeviceInput") local pushToTalkKey = Enum.KeyCode.V audioIn.Muted = true UserInputService.InputBegan:Connect(function(input: InputObject) if input.KeyCode == pushToTalkKey then audioIn.Muted = false end end) UserInputService.InputEnded:Connect(function(input: InputObject) if input.KeyCode == pushToTalkKey then audioIn.Muted = true end end) ```**#### Code explanation** The script starts by getting: - The `Class.Players` service so that it can reference all players within the experience. - The `Class.UserInputService` service so that it can check when a player presses buttons on their device. - The local player's `Class.AudioDeviceInput` object, or physical microphone on their device. The script then: - Sets the `V` key to be the button the player needs to press and hold in order to speak into their microphone. - Mutes the player's microphone. The remainder of the script sets up two event listeners for `InputBegan` and `InputEnded` from `Class.UserInputService`. When they press down the `V` key, the `InputBegan` event unmutes the player's microphone, and when they release the `V` key, the `InputEnded` event mutes their microphone again. 4. Playtest the experience to verify that players can only hear each other through voice chat when they are pressing the `V` key. ## Add time-based chat **Time-based chat** is a voice chat configuration in which players are only able to speak and hear one another during a set period of time. Integrating time-based chat into your gameplay is useful when you want players to carefully plan how they communicate with each other during specific phases, such as during round-based discussions or after cutscenes. To recreate the time-based voice chat in the sample [Gingerbread Island - Voice Chat](https://www.roblox.com/games/91891961746476/Gingerbread-Island-Voice-Chat) place file: 1. In the **Explorer** window, insert a **Script** into **ServerScriptService**. 2. Rename the script **TimeBasedChat**, then paste the following code into the script:```lua local Players = game:GetService("Players") local muteAll = false local function toggleMuteAll() muteAll = not muteAll for _, player in Players:GetPlayers() do local device : AudioDeviceInput = player:FindFirstChild("AudioDeviceInput") if not device then continue end device.Muted = muteAll end end while true do task.wait(15) -- every 15 seconds toggleMuteAll() -- either allow people to speak, or prevent them from speaking end ```**#### Code explanation** This script starts by getting the `Class.Players` service so it can reference its functionality to manage all players in the experience. It then sets a flag `muteAll` to **false**, which the following `toggleMuteAll` function uses to toggle voice chat on and off. Let's review the `toggleMuteAll` function: - It starts by looping through all players in the experience using `Class.Players.GetPlayers`. - For each player, it checks to see if it has a child `Class.AudioDeviceInput` object, or physical microphone on their device. - If they do, the function sets the `Class.AudioDeviceInput` object's `Class.AudioDeviceInput.Muted|Muted` property to the value of `muteAll`. For example, if `muteAll` is **true**, the player's microphone is disabled, and if `muteAll` is **false**, the player's microphone is enabled. The script then enters an infinite loop, waiting 15 seconds to mute and unmute all `Class.AudioDeviceInput` objects. 3. Playtest the experience to verify that players can only hear each other through voice chat in 15 second increments. --- title: "In-game sounds" url: /docs/en-us/tutorials/use-case-tutorials/audio/in-game-sounds last_updated: 2026-06-29T19:34:14Z description: "The process for creating positional and feedback sounds to enhance an experience." --- # In-game sounds In addition to background music, in-game audio can enhance a player's experience. This tutorial will cover two forms of in-game sounds: **positional** and **feedback** sounds. For the first example, you'll create a positional sound for a waterfall. In the second example, a script will be used to play a jingle when players touch a collectable. ## Positional sounds When a **Sound** object is parented to a part or attachment, it becomes positional. Audio will emit from its location and grow louder as players get closer, as in the case of this waterfall. ### Create a sound 1. In any desired part, create a new **Sound** object named **WaterfallSound**. 2. In the properties, find **SoundId** and change it to this waterfall ambience: `rbxassetid://6564308795`.![alt](../../../assets/tutorials/in-game-sounds/ingameSounds-soundID.png) > **Info:** Custom sounds can be imported using the [Asset Manager](/docs/en-us/projects/assets/manager.md). Additionally, free sounds uploaded by Roblox and the community can be found using the [Toolbox](/docs/en-us/projects/assets/toolbox.md). 3. For continuous playback when the experience starts, toggle **Playing** and **Looped** to be **on**.![alt](../../../assets/tutorials/in-game-sounds/ingameSounds-looping.png) 4. Test the experience to confirm you hear the waterfall ambience. ### Adjust sound distance Notice when testing, the audio plays immediately, even if the player is far away from the object. Using the roll-off properties, you can modify the distance at which a player hears a sound to create fading effects. 1. Change the `Class.Sound.RollOffMaxDistance|RollOffMaxDistance` to **30**. This property is measured in studs.![alt](../../../assets/tutorials/in-game-sounds/ingameSounds-rollOffDistance.png) 2. For a smoother fade, change the **RollOffMode** to **InverseTapered**. This makes approaching the sound feel less sudden.![alt](../../../assets/tutorials/in-game-sounds/ingameSounds-rollOffMode.png) 3. Run the project. Notice how sound is only heard near the object. ### Fine tune the roll off Depending on your needs, you may want to adjust different properties for special effects or increased realism. See the following properties: - `Class.Sound.RollOffMaxDistance|RollOffMinDistance` - Minimum distance (in studs) a sound decreases in volume. - `Class.SoundGroup` - Used to adjust and balance volume between groups of sounds, like background music and in-game effects. ## Feedback sounds Sounds can be played on command using scripts. You can link sounds to events, such as players touching a part or interacting with a menu. Here, you'll create a script that plays a chime whenever players touch collectable objects. ### Set up collectables The remainder of this tutorial uses a pre-made model. This model includes parts and scripts so players can collect gemstones. 1. In a browser, open the [Collectable Gems Model](https://www.roblox.com/library/6564500052/Collectable-Gems) page, and click the **Get** button.![alt](../../../assets/tutorials/in-game-sounds/ingameSounds-collectablePage.png) - From Studio's **Window** menu or **Home** tab toolbar, open the [Toolbox](/docs/en-us/projects/assets/toolbox.md) and select the **Inventory** tab. - Make sure the dropdown is on **My Models**. - Select the **Collectable Gems** model to add it into the experience. 1. In **SoundService**, create a new **Sound** named **FeedbackSound**.![alt](../../../assets/tutorials/in-game-sounds/ingameSounds-createFeedbackSound.png) 2. In FeedbackSound, set the **SoundId** to `rbxassetid://4110925712`- the SoundId of the simple chime downloaded from the model page.![alt](../../../assets/tutorials/in-game-sounds/ingameSounds-createFeedbackSound.png) ### Set up the script 1. In **StarterPlayer** ⟩ **StarterPlayerScripts**, create a new local script named **CollectableSounds**. 2. The code below will run the `partTouched` function whenever the player touches a collectable. Copy the code into your script.```lua local pickupObjects = workspace.Collectables.Objects local objectsArray = pickupObjects:GetChildren() local function partTouched(otherPart, objectPart) local whichCharacter = otherPart.Parent local humanoid = whichCharacter:FindFirstChildWhichIsA("Humanoid") if humanoid and objectPart.CanCollide == true then end end -- Binds every object part to the touch function so it works on all parts for objectIndex = 1, #objectsArray do local objectPart = objectsArray[objectIndex] objectPart.Touched:Connect(function(otherPart) partTouched(otherPart, objectPart) end) end ``` ### Play a sound 1. Create a variable for **SoundService**, then another variable to store the **feedback sound**.```lua local pickupObjects = workspace.Collectables.Objects local objectsArray = pickupObjects:GetChildren() local SoundService = game:GetService("SoundService") local feedbackSound = SoundService:FindFirstChild("FeedbackSound") local function partTouched(otherPart, objectPart) ``` 2. To play the chime, find the function `partTouched`. Within the if statement, call `feedbackSound:Play()` to play the sound.```lua local function partTouched(otherPart, objectPart) local whichCharacter = otherPart.Parent local humanoid = whichCharacter:FindFirstChildWhichIsA("Humanoid") -- Play the sound, once finished, destroy the object if humanoid and objectPart.CanCollide == true then feedbackSound:Play() end end ``` 3. Test the experience to confirm that when the player touches a collectable, it both disappears and plays a sound. --- title: "Play background music" url: /docs/en-us/tutorials/use-case-tutorials/audio/play-background-music last_updated: 2026-06-29T19:34:14Z description: "The process for creating background music in your experience." --- # Play background music Audio in Roblox is created with a `Class.Sound` object. Sounds can be positional, such as the sound of a waterfall, or universal for all players. This tutorial will show you how to create a universal sound to play background music. ## Play music You can [upload](/docs/en-us/audio/assets.md#import-audio) music or [obtain it from the marketplace](/docs/en-us/audio/assets.md#find-audio), which contains thousands of free-to-use tracks. For this tutorial, you need the **asset ID** of a track. The basic steps are to copy an asset ID, create a `Class.Sound` object, and use a script to play the music. ### Sound setup If a sound object is parented to a part, sound will emit from its position. If a sound object is parented to **SoundService**, it will play at the same volume at every point in the 3D world. This makes SoundService ideal for storing background music. 1. In **SoundService**, insert a **Sound** object named **BackgroundMusic**.![alt](../../../assets/tutorials/playing-background-music/playingBGMusic_createSoundInSoundService.png) 2. In the newly created sound, find the **SoundId** property. Paste in the previously copied sound ID (or use one below) and press `Enter`.![alt](../../../assets/tutorials/playing-background-music/playingBGMusic_soundID.png) 3. Test that the sound works by clicking the Preview button.![alt](../../../assets/tutorials/playing-background-music/playingBGMusic_pressPreview.png) Here are some sample music IDs you can use: - **Creepy Organ/Dungeon** - `rbxassetid://1843463175` - **Upbeat Electronica** - `rbxassetid://1837849285` - **Grandiose Fantasy** - `rbxassetid://1848183670` ### Play the song Background music can be played through a script. 1. In **StarterPlayer** ⟩ **StarterPlayerScripts**, create a **LocalScript** named **MusicPlayer**.![alt](../../../assets/tutorials/playing-background-music/playingBGMusic_pressPreview.png) 2. In the script, create variables to store **SoundService** and the **BackgroundMusic** object.```lua local SoundService = game:GetService("SoundService") local backgroundMusic = SoundService.BackgroundMusic ``` 3. Sounds are played using the `Class.Sound:Play()|Play` function. In a new line, call it on the `backgroundMusic` variable.```lua local SoundService = game:GetService("SoundService") local backgroundMusic = SoundService.BackgroundMusic backgroundMusic:Play() ``` 4. Test the experience and confirm that the music is audible. ### Audio properties Currently, the music doesn't loop. Additionally, the original sound file may be too loud for background music. These settings can be changed through two properties. 1. In the **BackgroundMusic** properties, toggle **Looped** to be **on**.![alt](../../../assets/tutorials/playing-background-music/playingBGMusic_changeProperties_looped.png) 2. Lower the **Volume** to around **0.25**.![alt](../../../assets/tutorials/playing-background-music/playingBGMusic_changeProperties_volume.png) With this project done, explore using scripts to implement other features in music. For instance, try using a script to shuffle songs in a soundtrack or play songs in different areas of your 3D world. For more on sounds and background music, see [Audio](/docs/en-us/sound.md). --- title: "Add speech-to-text" url: /docs/en-us/tutorials/use-case-tutorials/audio/speech-to-text last_updated: 2026-06-29T19:34:14Z description: "Explains how to add speech-to-text audio to your experiences." --- # Add speech-to-text **Speech-to-text** (STT) is a form of technology that automatically generates text from speech sounds. With STT, you can create more immersive worlds by allowing players to talk to NPCs, generate text without the need to pre-record specific audio, improve accessibility for players with motor or vision limitations, and provide players with hands-free controls through voice shortcuts for actions like opening their inventory or casting a spell. Using the [Gingerbread House - Start](https://www.roblox.com/games/134812596370919/Gingerbread-House-Start) `.rbxl` file as a starting place, this tutorial shows you how to add text generated dynamically from speech to your experience, including guidance on how to implement STT so that the player can interact with objects in your 3D world. > **Info:** For more general guidance on adding audio to your experience, see the [Add 2D audio](/docs/en-us/tutorials/use-case-tutorials/audio/add-2D-audio.md) and [Add 3D audio](/docs/en-us/tutorials/use-case-tutorials/audio/add-3D-audio.md) tutorials. > **Info:** For more reliable voice detection, use [push to chat](/docs/en-us/tutorials/use-case-tutorials/audio/add-voice-chat.md#add-push-to-chat) alongside STT. This ensures players' microphones are only active while they press and hold a specific button on their device. ## Audio objects To create STT audio, it's important to understand the audio objects that you will be working with throughout this tutorial. There are three types of audio objects for STT: - The `Class.AudioDeviceInput` object is a physical hardware device within the real world, such as a microphone in a laptop, phone, or headset. - The `Class.AudioSpeechToText` object converts speech sounds into text strings. - `Class.Wire|Wires` carry audio streams from one object to another. All of these audio objects work together to generate STT text in response to player actions. For example, if the player is wearing a headset while playing an experience with their laptop: - The `AudioDeviceInput` captures the player's speech, as spoken into their microphone and as a stream of audio. - A `Wire` carries the audio stream from the `AudioDeviceInput` to the `AudioSpeechToText`. - The `AudioSpeechToText` converts the player's speech into text. - The experience reads this text and performs an action, such as displaying the spoken text on the screen or opening a door at the player's command. ## Enable microphone usage Before adding STT to an experience, you must first enable microphone usage and publish the experience. 1. Open the [Gingerbread House - Start](https://www.roblox.com/games/134812596370919/Gingerbread-House-Start) file in Studio. 2. Create a local copy. 3. Publish your local copy of the experience. 4. Back in Studio, go to **File** ⟩ **Experience Settings** ⟩ **Communication**. 5. Turn on **Enable Microphone**. 6. Save your changes. ## Add simple speech capture To add simple speech capture to your local copy of the [Gingerbread House - Start](https://www.roblox.com/games/134812596370919/Gingerbread-House-Start) file: 1. Enable the use of the latest API for voice. 1. In the **Explorer** window, select the **VoiceChatService**. 2. In the **Properties** window, set **UseAudioApi** to **Enabled**. 2. Create the basic audio objects that will let you capture spoken words. 1. In the **Explorer** window, go to **Workspace** > **DialogueVolume**. 2. Delete all objects inside the **DialogueVolume** folder. 3. Insert the following objects into the **DialogueVolume** folder: - An **AudioDeviceInput** to capture speech. - An **AudioSpeechToText** to convert the speech into text. - A **Wire** to carry the stream from the audio device input to the STT instance. 4. In the **Properties** window of the **AudioSpeechToText** object, set the **Enabled** state to on. 5. In the **Properties** window of the **Wire** object: 1. Set **SourceInstance** to your new **AudioDeviceInput** to specify that you want the wire to carry audio from this specific audio instance. 2. Set **TargetInstance** to your new **AudioSpeechToText** to specify that you want the wire to carry audio to this specific audio instance. 3. Set the **Player** property of the audio device input to the local player at runtime. This tells Roblox which user's microphone to capture audio from. 1. In the **Explorer** window, go to **StarterPlayer** > **StarterCharacterScripts**. 2. Insert a `Class.LocalScript` and name it **ListenForMagicWord**. 3. Paste the following code into the new local script:```lua local audioDeviceInput = workspace:WaitForChild('DialogueVolume'):WaitForChild('AudioDeviceInput') -- Binds the microphone to the local player audioDeviceInput.Player = game.Players.LocalPlayer ``` ### Test your speech capture After you add simple speech capture, you can playtest the experience to make sure your STT implementation works properly. Speaking into your microphone should populate the **AudioSpeechToText** property with the text version of your speech. To test your speech capture: 1. Under **Test** > **Clients and Servers**, select **Team Test**. 2. Click **Start** to start the test server. 3. In the new test server, unmute your microphone and speak a few words. 4. In the **Explorer** window, go to **Workspace** > **DialogueVolume** > **AudioSpeechToText**. 5. In the **Properties** window of **AudioSpeechToText**, confirm that the words you spoke into your microphone have appeared as the value of the **Text** property. ## Use STT with gameplay To connect STT to gameplay and make the gingerbread house door open when the player says "Open Sesame" into their microphone: 1. Create a remote event to connect the client to the server. 1. In the **Explorer** window, insert a `Class.RemoteEvent` under **ReplicatedStorage**. 2. Name it **OpenDoor**. 2. Add a new script to animate the opening of the gingerbread house door. 1. Under **ServerScriptService**, insert a server-side `Class.Script`. 2. Name it **DoorService**. 3. Paste the following code into the new script:```lua -- Gets the ReplicatedStorage service for shared storage between server and client -- Gets the TweenService for the tweening system local ReplicatedStorage = game:GetService("ReplicatedStorage") local TweenService = game:GetService("TweenService") -- Allows the client to ask the server to open the door local remoteEvent = ReplicatedStorage:WaitForChild("OpenDoor") -- Function that animates the door to move downward and open local function openDoor() local doorPart = workspace:WaitForChild("Door") local tweenInfo = TweenInfo.new(2, Enum.EasingStyle.Linear) local tween = TweenService:Create(doorPart, tweenInfo, {Position = doorPart.Position + Vector3.new(0, -15, 0)}) tween:Play() end -- Connects the function to the event remoteEvent.OnServerEvent:Connect(openDoor) ``` 3. Connect the door to speech-to-text so that an action happens when the player speaks into their microphone. 1. Go to **StarterPlayer** > **StarterCharacterScripts**. 2. Open the **ListenForMagicWord** script you created earlier. 3. Add the following lines:```lua -- Gets the ReplicatedStorage service for shared storage between server and client local ReplicatedStorage = game:GetService("ReplicatedStorage") -- Gets the RemoveEvent that the client will fire to the server local remoteEvent = ReplicatedStorage:WaitForChild("OpenDoor") -- Finds the STT node in the scene graph -- The node's Text property updates as it recognizes speech local speechToText = workspace:WaitForChild('DialogueVolume'):WaitForChild('AudioSpeechToText') -- Function that tells the server to open the door if STT captures the player saying "Open Sesame" speechToText:GetPropertyChangedSignal('Text'):Connect(function() local text = speechToText.Text if text == '' then return end print(text) if (text.find(text, 'open sesame') ~= nil) then remoteEvent:FireServer() end speechToText.Text = '' end) ``` 4. Test your STT implementation. 1. Under **Test** > **Clients and Servers**, select **Team Test**. 2. Click **Start** to start the test server. 3. In the new test server, walk up to the door, unmute your microphone, and say "Open Sesame". The door should open and allow you into the gingerbread house. --- title: "Create a custom leaderboard with ordered data stores" url: /docs/en-us/tutorials/use-case-tutorials/data-storage/create-leaderboard last_updated: 2026-06-29T19:34:14Z description: "Use ordered data stores to track and rank players by the amount of gold they have collected." --- # Create a custom leaderboard with ordered data stores **Data stores** are a service you can use to save and load **persistent player data** across different player sessions. They store important information, like a player's progress or inventory, and allow you to retrieve it for the player next time they join your game. Without data stores, a player would lose all of their progress every time they left the game. There are two types of data stores: standard and ordered. This tutorial uses **ordered data stores**, which store data that you can rank and sort numerically and retrieve in ascending or descending order based on the stored numerical values. Using the [Gold Rush create leaderboard tutorial - Start](https://www.roblox.com/games/123363025813820/Gold-Rush-create-leaderboard-tutorial-Start) `.rbxl` file as a starting place and the [Gold Rush create leaderboard tutorial - Complete](https://www.roblox.com/games/123931337104144/Gold-Rush-create-leaderboard-tutorial-Complete) `.rbxl` file as a reference, this tutorial gives you the foundation you need to create a custom player leaderboard, including guidance on: - Storing gold scores in an ordered data store. - Fetching and sorting the top scores globally across all servers. - Displaying the top scores in a custom leaderboard in the UI. - Automatically refreshing the custom leaderboard every few seconds. By the end of this tutorial, you should have the following: 1. Under `Class.ServerScriptService`: 1. An updated [GoldManager](#goldmanager) script that saves the player's score to an ordered data store for the leaderboard. 2. A new [LeaderboardRemoteHandler](#leaderboardremotehandler) module script that fetches and returns the sorted list of top scores from the leaderboard. 3. A new [LeaderboardManager](#leaderboardmanager) script that responds to the leaderboard data requests from the client, acting as a bridge between the client in **LeaderboardGuiScript** and the server in **LeaderboardManager**. 2. Under `Class.StarterGui`, a new [LeaderboardGuiScript](#leaderboardguiscript) local script that renders the leaderboard UI for the player. ## Enable Studio access to API services Data stores aren't stored locally on your device, so your game relies on server-to-server communication with Roblox's backend in order to use them. By default, Studio restricts this communication to prevent abuse or accidental use. Access to API services in Studio is disabled until you explicitly enable it to make sure that only trusted games can read from and write to Roblox's backend servers. To enable Studio access to API services so that you can use data stores: 1. Open the [Gold Rush create leaderboard tutorial - Start](https://www.roblox.com/games/123363025813820/Gold-Rush-create-leaderboard-tutorial-Start) file in Studio. This starting place file already includes the code from the [Save player data](/docs/en-us/tutorials/use-case-tutorials/data-storage/save-player-data.md) tutorial and the UI skeleton for the player leaderboard. 2. [Publish your game](/docs/en-us/production/publishing/publish-games-and-places.md#publish-games). 3. Back in Studio, go to **File** ⟩ **Experience Settings** ⟩ **Security**. 4. Turn on **Enable Studio Access to API Services**. 5. Save your changes. ## Create an ordered data store When creating a data store, you should always call the `DataStoreService` from a server-side `Class.Script`. Doing so is important because: - Roblox blocks all data store access from the client so that only the server has permission to access and modify persistent player data. - Server-side scripts run in a centralized environment, making sure that the data being read and written to your data store is accurate and valid. - Since client-side scripts run on the player's device, any player could potentially exploit the game and steal or overwrite other players' data if the client had access to your data stores. You should also give your data store a unique name to keep your data organized and to prevent it from overlapping or conflicting in different data stores. In this tutorial, the data store for the leaderboard is called **GlobalLeaderboard**. To create the **GlobalLeaderboard** ordered data store: 1. Open the existing **GoldManager** script under **ServerScriptService**. 2. Under the existing **PlayerGold** data store, call `Class.DataStoreService:GetOrderedDataStore|GetOrderedDataStore` with the string **"GlobalLeaderboard"**. This creates an ordered data store to save the player's score when they earn gold.```lua local leaderboardStore = DataStoreService:GetOrderedDataStore("GlobalLeaderboard") ``` After creating a data store, you're able to reference this same data store across different scripts. For example, you can call `Class.DataStoreService:GetOrderedDataStore|GetOrderedDataStore` in both the **GoldManager** script and the **LeaderboardManager** module script that you will create later in this tutorial. Referencing the same data store in two places doesn't mean you're duplicating data; it just means that two scripts are accessing the same centralized storage, like two teammates using one folder to share information. ## Send player data to the leaderboard Data stores are made up of keys, which identify data, and values, which store data. Unlike standard data stores, ordered data stores let you sort data by **value**, which makes them ideal for creating things like leaderboards. Each entry in an ordered data store is stored under a unique key that you can rank and sort numerically, and then retrieve in ascending or descending order based on the stored numerical values. This means that you can use ordered data stores to, for example, show a top 3 list that updates in real time as players in your game collect more gold. | | **Key** | **Value** | | --- | --- | --- | | **Description** | A key is a unique string identifier you can use to access a specific piece of data, like a player's user ID.

Keys let you organize your data so you can easily manage and debug it. | A value is the actual numeric data you want to save or load, like a player's score.

Values let you store progress between sessions, as well as retrieve player data next time they join your game. | | **Allowed formats** | Can only be strings. If you're using numbers, like a user ID, convert them to a string using `tostring()`. | Can only be numbers. | | **Examples** | `"Player_A"`, `"TopScore"`, `"123456"` (userIs as string) | `100`, `3.14`, `0`, `-25` | Now that you have created your **GlobalLeaderboard** ordered data store, update the existing `saveGold` function in the **GoldManager** script to send the player's gold score to your custom leaderboard with `Class.GlobalDataStore.SetAsync|SetAsync`: ```lua local function saveGold(player, value) local userId = player.UserId local success, err = pcall(function() goldStore:SetAsync(userId, value) leaderboardStore:SetAsync(tostring(userId), value) end) if not success then warn("Could not save gold for", player.Name, ":", err) end end ``` You can think of the ordered data store **GlobalLeaderboard** like a shared folder: `SetAsync` adds content to the folder, while `GetSortedAsync` (which you'll create later in the **LeaderboardManager** module script) reads the folder's content and sorts it by score. ## Fetch the leaderboard scores To fetch the leaderboard scores, you should use a `Class.ModuleScript|ModuleScript`. Module scripts let you organize and reuse your code across multiple scripts. Instead of copying the same logic into different places, you can write it once in a module script and call it from anywhere that needs it. When you store your leaderboard logic in a module, both the UI handler and the server scripts can use the same function to fetch the top player scores. If you ever need to, for example, update how usernames are fetched or how scores are sorted, you only have to change the code in one place. After you update the module, all scripts that use it also automatically get the udpated behavior. You custom leaderboard uses the following ordered data store methods to share data across different servers: - `SetAsync` (from the **GoldManager** script) to send player scores to the **GlobalLeaderboard** ordered data store. - `Class.OrderedDataStore.GetSortedAsync|GetSortedAsync` (in the module script) to fetch the top scores from all servers from the same data store. The `GetSortedAsync` method is exclusive to ordered data stores and can retrieve multiple sorted keys based on a specific sorting order, page size, and minimum and maximum values. > **Info:** As long as each server updates the **GlobalLeaderboard** ordered data store when a player collects gold, and each client retrieves this data from the server, everyone inside your game will see the same leaderboard with the same top scores. To fetch the leaderboard scores: 1. Under **ServerScriptService**, create a new `Class.ModuleScript` called **LeaderboardManager**. 2. At the top of the module script, call `Class.DataStoreService:GetOrderedDataStore|GetOrderedDataStore` with the string **"GlobalLeaderboard"**. Referencing the same ordered data store that you created in the **GoldManager** script will allow the leaderboard UI to fetch and sort the top player scores later.```lua local DataStoreService = game:GetService("DataStoreService") local Players = game:GetService("Players") local leaderboardStore = DataStoreService:GetOrderedDataStore("GlobalLeaderboard") ``` 3. Prepare a module table to hold all functions and data you want to expose to other scripts.```lua local leaderboardManager = {} ``` 4. Create a function called **GetTopScores** to get the top player scores. This function: - Calls `GetSortedAsync` to fetch the `limit` number of top scores in descending order. If the call fails, an empty list is returned to prevent the rest of your code from crashing. - Loops through the leaderboard to get a table of entries with `Class.Pages.GetCurrentPage|GetCurrentPage` and player usernames with `Class.Players.GetNameFromUserIdAsync|GetNameFromUserIdAsync`. - Adds each result to a final list and builds a leaderboard table with player usernames and their scores. - Returns the final list of players and sends the leaderboard data back to any script that calls this `leaderboardManager.GetTopScores` function.```lua function leaderboardManager.GetTopScores(limit) local success, pages = pcall(function() return leaderboardStore:GetSortedAsync(false, limit) end) if not success or not pages then return {} end local results = {} for _, entry in ipairs(pages:GetCurrentPage()) do local userId = tonumber(entry.key) local score = entry.value local username = "Unknown" local ok, name = pcall(function() return Players:GetNameFromUserIdAsync(userId) end) if ok then username = name end table.insert(results, { Username = username, Score = score }) end return results end ``` 5. Make the module usable by returning the module table you created earlier. Whenever you create a module script, you must return the module table so that other scripts can access it and call the functions inside it.```lua return leaderboardManager ``` ## Request leaderboard data from the server In order for the client UI to display the leaderboard scores, the server has to respond when the client asks for data. A `Class.RemoteFunction` allows the client to request information from the server and wait for a reply. To request leaderboard data from the server: 1. Under **ReplicatedStorage**, insert a **RemoteFunction** called **LeaderboardRemote**. This remote function acts as the bridge between the UI script on the client and the leaderboard logic on the server. 2. Under **ServerScriptService**, create a script called **LeaderboardRemoteHandler**. In this script: 1. Call `Class.ReplicatedStorage|ReplicatedStorage` (the shared space between client and server), `Class.ServerScriptService|ServerScriptService` (which holds the leaderboard module), and `leaderboardRemote` (the remote function that the client will call to ask for the leaderboard data).```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local ServerScriptService = game:GetService("ServerScriptService") local leaderboardRemote = ReplicatedStorage:WaitForChild("LeaderboardRemote") ``` 2. Load the **LeaderboardManager** module that you created earlier, which includes the function that gets the top scores from the ordered data store.```lua local leaderboardManager = require(ServerScriptService:WaitForChild("LeaderboardManager")) ``` 3. Set the behavior for when the client calls `Class.RemoteFunction.InvokeServer|InvokeServer`. The server responds by fetching the top 3 scores and sending the result back to the client.```lua leaderboardRemote.OnServerInvoke = function(player) return leaderboardManager.GetTopScores(3) end ``` ## Display data in the custom leaderboard UI > **Info:** Because of backend caching, the scores in your custom leaderboard can take a few extra seconds to update. To get instant, real-time updates across servers, you can use [memory stores](/docs/en-us/cloud-services/memory-stores.md). After setting up the server-side scripts, create a local script to fetch the top 3 players every 3 seconds and display the results in a global UI leaderboard inside the game. To display data in your custom leaderboard: 1. Under **StarterGui** ⟩ **LeaderboardGui**, create a local script called **LeaderboardGuiScript**. 2. At the top of the script, connect to the remote function. Call `Class.ReplicatedStorage|ReplicatedStorage` to get access to shared objects between the server and client, then call `leaderboardRemote` to allow the client to request the leaderboard data from the server.```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local leaderboardRemote = ReplicatedStorage:WaitForChild("LeaderboardRemote") ``` 3. Reference the existing leaderboard UI elements under **LeaderboardGui**.```lua local gui = script.Parent local leaderboardFrame = gui:WaitForChild("LeaderboardFrame") local entriesFrame = leaderboardFrame:WaitForChild("EntriesFrame") local template = entriesFrame:WaitForChild("LeaderboardEntry") ``` 4. Create a function called **clearEntries** to clear old leaderboard entries. This function loops through all children of `entriesFrame`, which contains the leaderboard rows, and makes sure the leaderboard is fully refreshed before displaying new data.```lua local function clearEntries() for _, child in ipairs(entriesFrame:GetChildren()) do if child:IsA("TextLabel") and child.Name:match("^Row%d+$") then child:Destroy() end end end ``` 5. Create a function called **renderLeaderboard** to insert and display player data in the leaderboard rows. The `data` object is a table returned by the server that contains entries with the player usernames and scores.```lua local function renderLeaderboard(data) clearEntries() for i, entry in ipairs(data) do local row = template:Clone() row.Name = "Row" .. i row.Visible = true row.LayoutOrder = i row.Text = string.format("%d%s place: %s - %d", i, i == 1 and "st" or i == 2 and "nd" or i == 3 and "rd" or "th", entry.Username, entry.Score) row.Parent = entriesFrame end end ``` 6. Create a loop to periodically fetch the leaderboard data. Every 3 seconds, the loop runs and calls the server using the `Class.RemoteFunction.InvokeServer|InvokeServer` function you created earlier in the **LeaderboardRemoteHandler** script. If the call is successful, the UI refreshes with the updated scores.```lua while true do local success, data = pcall(function() return leaderboardRemote:InvokeServer() end) if success and data then renderLeaderboard(data) end task.wait(3) end ``` ## Final code See the following code snippets for the complete **GoldManager**, **LeaderboardManager**, **LeaderboardRemoteHandler**, and **LeaderboardGuiScript** scripts. You can paste and run them directly in the [Gold Rush create leaderboard tutorial - Start](https://www.roblox.com/games/123363025813820/Gold-Rush-create-leaderboard-tutorial-Start) `.rbxl` file in Studio. ### GoldManager The **GoldManager** script creates an ordered data store to manage leaderboard updates. ```lua -- Get services local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") local CollectionService = game:GetService("CollectionService") local DataStoreService = game:GetService("DataStoreService") -- Remote event to update the UI local uiEvent = ReplicatedStorage:WaitForChild("UIEvent") -- Data store for saving the player's gold chunks local goldStore = DataStoreService:GetDataStore("PlayerGold") -- Ordered data store to save gold to the leaderboard local leaderboardStore = DataStoreService:GetOrderedDataStore("GlobalLeaderboard") -- Create a random number generator for positioning gold chunks local random = Random.new() -- Table to store the player's gold chunks for a single session local playerGold = {} -- Constants local GOLD_VALUE = 10 -- How much each gold chunk is worth local GOLD_REGENERATION_BOUNDS = { -- Area bounds used to reposition gold chunks randomly minX = -72, maxX = 72, minZ = -72, maxZ = 72 } local GOLD_REGEN_DELAY = 2 -- The delay before a chunk respawns (in seconds) local AUTOSAVE_INTERVAL = 30 -- How often the player's chunks are saved (in seconds) -- Function to handle the player collecting a gold chunk local function processGoldTouch(chunk, player) -- Hide the chunk when the player collects it chunk.Parent = nil -- Add the chunk to the player's session gold score local userId = player.UserId playerGold[userId] = (playerGold[userId] or 0) + GOLD_VALUE print(player.Name, "now has", playerGold[userId], "gold") -- Update the UI score bar to reflect the new gold score for this session uiEvent:FireClient(player, { gold = playerGold[userId], doTween = true, showAlert = false }) -- Wait a bit and then respawn the gold chunk that was just collected in a random location task.delay(GOLD_REGEN_DELAY, function() -- Respawn the gold chunk at a new random position local nextPositionX = random:NextInteger(GOLD_REGENERATION_BOUNDS.minX, GOLD_REGENERATION_BOUNDS.maxX) local nextPositionZ = random:NextInteger(GOLD_REGENERATION_BOUNDS.minZ, GOLD_REGENERATION_BOUNDS.maxZ) chunk.CFrame = CFrame.new(nextPositionX, 50, nextPositionZ) -- Move chunk to new position chunk:SetAttribute("DropCollided", false) -- Reset any collision flags chunk.Parent = workspace.GoldChunks end) end -- Loop through all parts tagged with "Gold" and connect them to touch detection for _, chunk in ipairs(CollectionService:GetTagged("Gold")) do chunk.Touched:Connect(function(otherPart) local player = Players:GetPlayerFromCharacter(otherPart.Parent) if player then processGoldTouch(chunk, player) end end) end -- Function to save the player's gold to both standard and ordered data stores local function saveGold(player, value) local userId = player.UserId local success, err = pcall(function() goldStore:SetAsync(userId, value) leaderboardStore:SetAsync(tostring(userId), value) end) if not success then warn("Could not save gold for", player.Name, ":", err) end end -- Function to handle the player joining the game local function onPlayerAdded(player) local userId = player.UserId print(player.Name, "has joined the game") -- Try to load the player's gold from the data store local success, storedGold = pcall(function() return goldStore:GetAsync(userId) end) -- Set the current session gold score using the data store value for the player local currentGold = if success and storedGold then storedGold else 0 playerGold[userId] = currentGold -- Update the UI with the data store value for the player uiEvent:FireClient(player, { gold = currentGold, doTween = false, showAlert = not success }) if success then print("Loaded", currentGold, "gold for", player.Name) else -- Notify the player their gold couldn't be loaded uiEvent:FireClient(player, { showAlert = true }) warn("Could not load gold for", player.Name) end -- Start the autosave coroutine to save the player's gold every 30 seconds coroutine.wrap(function() while player.Parent do task.wait(AUTOSAVE_INTERVAL) if playerGold[userId] then saveGold(player, playerGold[userId]) print("Autosaving", playerGold[userId], "gold for", player.Name) end end end)() end -- Function to handle the player leaving the game local function onPlayerRemoving(player) local userId = player.UserId print(player.Name, "has left the game") -- Check if the player's gold was tracked if playerGold[userId] then -- Save the player's gold to the data store before they leave saveGold(player, playerGold[userId]) end end -- Connect the joining and leaving player events Players.PlayerAdded:Connect(onPlayerAdded) Players.PlayerRemoving:Connect(onPlayerRemoving) ``` ### LeaderboardManager The **LeaderboardManager** module script: - Calls `GetSortedAsync()` on the GlobalLeaderboard ordered data store. - Converts userIds into usernames using `Class.Players.GetNameFromUserIdAsync|GetNameFromUserIdAsync`. - Returns a list of `{ Username, Score }` tables for the top-ranked players. - Is called by LeaderboardRemoteHandler whenever a client requests leaderboard data. ```lua -- Get services local Players = game:GetService("Players") local DataStoreService = game:GetService("DataStoreService") -- Ordered data store to save leaderboard scores local leaderboardStore = DataStoreService:GetOrderedDataStore("GlobalLeaderboard") -- Table to store the leaderboard scores local leaderboardManager = {} -- Function to return the top scores function leaderboardManager.GetTopScores(limit) -- Try to retrieve a sorted page of leaderboard entries from the data store local success, pages = pcall(function() -- false = descending (highest scores first) return leaderboardStore:GetSortedAsync(false, limit) end) -- If the call fails or returns nothing, log a warning and return an empty table if not success or not pages then warn("Failed to get leaderboard data") return {} end -- Create a table to store top player data local topPlayers = {} -- Loop through each entry on the current page of leaderboard results for _, entry in ipairs(pages:GetCurrentPage()) do local userId = tonumber(entry.key) -- Convert the entry key to a number (userId was stored as a string) local score = entry.value -- The value is the player's score (amount of gold) local username = "Unknown" -- A default name in case lookup fails -- Try to get the player's username from their userId local ok, name = pcall(function() return Players:GetNameFromUserIdAsync(userId) end) -- If lookup succeeds, store the real username if ok then username = name end -- Add this player and their score to the list table.insert(topPlayers, { Username = username, Score = score }) end -- Return the full list of top players with their usernames and scores return topPlayers end -- Return the LeaderboardManager module so other scripts can use it return leaderboardManager ``` ### LeaderboardRemoteHandler The **LeaderboardRemoteHandler** script fetches and returns the top 3 scores to the calling client. ```lua -- Get services local ServerScriptService = game:GetService("ServerScriptService") -- Access server-side modules local ReplicatedStorage = game:GetService("ReplicatedStorage") -- Shared client-server communication objects -- Wait for the RemoteFunction that the client uses to request leaderboard data local leaderboardRemote = ReplicatedStorage:WaitForChild("LeaderboardRemote") -- Load the LeaderboardManager module that contains the GetTopScores function local leaderboardManager = require(ServerScriptService:WaitForChild("LeaderboardManager")) -- Set up a handler for when the client invokes the leaderboard RemoteFunction leaderboardRemote.OnServerInvoke = function(player) -- Call the leaderboard module to get the top 3 scores and return them to the client return leaderboardManager.GetTopScores(3) end ``` ### LeaderboardGuiScript The **LeaderboardGuiScript** local script: - Requests leaderboard data from the server every 3 seconds using `Class.RemoteHandler.InvokeServer|InvokeServer`. - Uses a hidden LeaderboardEntry template to display each leaderboard row. - Formats each row as "1st place: Username - Score". - Manages the creation, display, and layout of leaderboard rows using `Class.UIListLayout`. ```lua -- Get shared storage for RemoteFunction access local ReplicatedStorage = game:GetService("ReplicatedStorage") -- Get the RemoteFunction used to request leaderboard data from the server local leaderboardRemote = ReplicatedStorage:WaitForChild("LeaderboardRemote") -- UI references local gui = script.Parent -- The LocalScript under the ScreenGui local leaderboardFrame = gui:WaitForChild("LeaderboardFrame") -- Main container for the leaderboard local entriesFrame = leaderboardFrame:WaitForChild("EntriesFrame") -- Frame where leaderboard entries appear local template = entriesFrame:WaitForChild("LeaderboardEntry") -- Hidden template TextLabel used to create rows -- Function to remove old leaderboard rows (but not the template) local function clearEntries() for _, child in ipairs(entriesFrame:GetChildren()) do -- Only destroy labels named "Row1", "Row2", etc (leave the template untouched) if child:IsA("TextLabel") and child.Name:match("^Row%d+$") then child:Destroy() end end end -- Function to display leaderboard data on screen local function renderLeaderboard(data) -- Remove old rows first clearEntries() for i, entry in ipairs(data) do local row = template:Clone() -- Clone the hidden template row.Name = "Row" .. i -- Give it a unique name row.Visible = true -- Make it visible row.LayoutOrder = i -- Let UIListLayout order it properly -- Leaderboard format: "1st place: Username - 123" row.Text = string.format("%d%s place: %s - %d", i, i == 1 and "st" or i == 2 and "nd" or i == 3 and "rd" or "th", entry.Username, entry.Score ) -- Add the data to the UI row.Parent = entriesFrame end end -- Main loop: update the leaderboard every 3 seconds while true do -- Ask the server for leaderboard data local success, data = pcall(function() return leaderboardRemote:InvokeServer() end) if success and data then -- Show results on leaderboard renderLeaderboard(data) else warn("Failed to fetch leaderboard data") end -- Wait 3 seconds before updating again task.wait(3) end ```
--- title: "Save player data with standard data stores" url: /docs/en-us/tutorials/use-case-tutorials/data-storage/save-player-data last_updated: 2026-06-29T19:34:14Z description: "Create basic standard data stores to save, store, and load player data." --- # Save player data with standard data stores **Data stores** are a service you can use to save and load **persistent player data** across different player sessions. They store important information, like a player's progress or inventory, and allow you to retrieve it for the player next time they join your game. Without data stores, a player would lose all of their progress every time they left the game. There are two types of data stores: standard and ordered. This tutorial uses **standard data stores**, which store data like numbers, strings, and tables that don't need to be ranked or sorted. Using the [Gold Rush data store tutorial - Start](https://www.roblox.com/games/116344152904993/Gold-Rush-save-data-tutorial-Start) `.rbxl` file as a starting place and the [Gold Rush data store tutorial - Complete](https://www.roblox.com/games/114290473705422/Gold-Rush-save-data-tutorial-Complete) `.rbxl` file as a reference, this tutorial gives you the foundation you need to save player progress and create more consistent games, including guidance on: - Saving and loading the amount of gold a player has collected. - Updating the UI to display the player's gold inventory. - Automatically saving the player's gold inventory every 30 seconds. - Saving the player's position when they leave the game. - Restoring the player's position when they join the game again. > **Info:** The [Gold Rush data store tutorial - Start](https://www.roblox.com/games/116344152904993/Gold-Rush-save-data-tutorial-Start) starting place file already includes code that allows the player to collect gold and updates the UI with their gold score, but this data doesn't persist across sessions because the file lacks data stores. Without data stores, the player's score and position reset when they leave the game. By the end of this tutorial, you should have two scripts with data stores under `Class.ServerScriptService`: 1. An updated [GoldManager](#goldmanager) script that tracks, loads, and automatically saves the player's gold. 2. An updated [PositionManager](#positionmanager) script that saves the player's position when they leave the game and restores their position when they come back to the game. ## Enable Studio access to API services Data stores aren't stored locally on your device, so your game relies on server-to-server communication with Roblox's backend in order to use them. By default, Studio restricts this communication to prevent abuse or accidental use. Access to API services in Studio is disabled until you explicitly enable it to make sure that only trusted games can read from and write to Roblox's backend servers. To enable Studio access to API services so that you can use data stores: 1. Open the [Gold Rush data store tutorial - Start](https://www.roblox.com/games/116344152904993/Gold-Rush-save-data-tutorial-Start) `.rbxl` file in Studio and create a local copy. 2. [Publish your game](/docs/en-us/production/publishing/publish-games-and-places.md#publish-games). 3. Back in Studio, go to **File** ⟩ **Experience Settings** ⟩ **Security**. 4. Turn on **Enable Studio Access to API Services**. 5. Save your changes. ## Create a standard data store When creating a data store, you should always call the `DataStoreService` from a server-side `Class.Script`. Doing so is important because: - Roblox blocks all data store access from the client so that only the server has permission to access and modify persistent player data. - Server-side scripts run in a centralized environment, making sure that the data being read and written to your data store is accurate and valid. - Since client-side scripts run on the player's device, any player could potentially exploit the game and steal or overwrite other players' data if the client had access to your data stores. You should also give your data store a unique name to keep your data organized and to prevent it from overlapping or conflicting in different data stores. In this tutorial, the data store that saves and stores the player's gold inventory is called **PlayerGold**. To create the PlayerGold data store: 1. Open the existing **GoldManager** script under **ServerScriptService**. 2. Under the other services at the top of the script, initialize the **DataStoreService** and call **`Class.DataStoreService.GetDataStore|GetDataStore`** with the string **"PlayerGold"**.```lua local DataStoreService = game:GetService("DataStoreService") local goldStore = DataStoreService:GetDataStore("PlayerGold") ``` ## Save and load player data Data stores are made up of keys, which identify data, and values, which store data. | | **Key** | **Value** | | --- | --- | --- | | **Description** | A key is a unique identifier you can use to access a specific piece of data.

Keys let you organize your data so you can easily manage and debug it. | A value is the actual data you want to save or load, like a player's inventory or their settings.

Values let you store progress between sessions, as well as retrieve player data next time they join your game. | | **Allowed formats** | Can only be strings. | Can be numbers, strings, booleans, or tables. | | **Examples** | `"Player_A"`, `"TopScore"`, `"GameSetting"` | `100`, `"Level_5"`, `true`, `{gold = 100, level = 5}` | In this tutorial, the key is the player's `userId` and the value is the amount of gold they have collected. To use this key and value to save and load player data: 1. In the **GoldManager** script, add a helper function called **saveGold** to save the player's gold to the PlayerGold data store.```lua local function saveGold(userId, value) local success, err = pcall(function() -- \`userId\` is the unique ID of the player -- \`value\` is the amount of gold to save for that player goldStore:SetAsync(userId, value) end) end ``` 2. **OPTIONAL** Add a warning to **saveGold** that prints the player's ID and an error message. While not mandatory, this step is good practice for easier debugging in case your function fails.```lua local function saveGold(userId, value) local success, err = pcall(function() -- \`userId\` is the unique ID of the player -- \`value\` is the amount of gold to save for that player goldStore:SetAsync(userId, value) end) if not success then warn("[SAVE ERROR] Could not save gold for", userId, ":", err) end end ``` 3. Modify the **onPlayerAdded** event to load the player's gold when they first join the game. The updated **onPlayerAdded** function: - Tries to load the content inside the PlayerGold data store. It returns either the player's saved gold or a value of 0 if the player doesn't have any gold or hasn't played the game before. - Provides you a debug log to help you debug your code more easily. This debug log includes the joining player's saved gold, or an error message if **onPlayerAdded** isn't able to access the data store. - Updates the UI to display the player's saved gold on their screen.```lua local function onPlayerAdded(player) local userId = player.UserId local success, storedGold = pcall(function() return goldStore:GetAsync(userId) end) if success then local currentGold = storedGold or 0 playerGold[userId] = currentGold uiEvent:FireClient(player, { gold = currentGold, doTween = false, showAlert = not success }) else uiEvent:FireClient(player, { showAlert = true }) warn("Could not load gold for", player.Name) end end ``` 4. Modify the **onPlayerRemoving** event to call the **saveGold** function you created earlier and save the player's gold when they leave the game.```lua local function onPlayerRemoving(player) local userId = player.UserId if playerGold[userId] then saveGold(userId, playerGold[userId]) end end ``` ## Automatically save player data Automatically saving a player's data is good practice because it protects the player from losing their data. If you only save the player's data when they leave the game with `onPlayerRemoving`, you might lose their latest progress if they unexpectedly disconnect from the server. > **Info:** Make sure not to autosave too often to avoid throttling errors and request limits. A good autosave interval is anywhere between 30 to 120 seconds, depending on how often players gain valuable progress in your game. To autosave the player's data: 1. Create a new constant to determine how often you want your game to automatically save the player's data. A constant is a variable whose value does not change while the game is running. You can use this constant to refer to the autosave interval throughout your script and to easily adjust the autosaving frequency.```lua -- This number is in seconds local AUTOSAVE_INTERVAL = 30 ``` 2. Add an autosave coroutine to the existing **onPlayerAdded** function to create a background loop that runs while the player is inside the game. A coroutine is a function that runs asynchronously; it can pause at certain points, resume where it left off, and run in parallel with other parts of your script without blocking them. In this script, the autosave coroutine background loop waits for the number of seconds in your **AUTOSAVE_INTERVAL** constant, then calls the **saveGold** function you created earlier.```lua coroutine.wrap(function() while player.Parent do task.wait(AUTOSAVE_INTERVAL) if playerGold[userId] then print("[AUTOSAVE] Saving", playerGold[userId], "gold for", player.Name) saveGold(userId, playerGold[userId]) end end end)() ``` ## Save and load player position To save a player's position, work with their `Class.Player.Character|Character` instead of the `Class.Player` object itself. A player's `Character` represents their physical model in the 3D world and has the coordinates for their current position and orientation, while the `Player` object only represents the user account. Because data stores can only save basic data types like numbers, strings, and tables, you can't directly store complex objects like `Datatype.Vector3` or `Datatype.CFrame` values. To save a player's position, you need to split it into three separate numbers (the X, Y, and Z coordinates) and save them individually. When loading the position later, you can rebuild the `Vector3` using the saved coordinates. To save and load the character's position: 1. Open the existing **PositionManager** script under **ServerScriptService**. 2. At the top of the script, initialize the **DataStoreService** and call **GetDataStore** with the string **"PlayerPosition"**.```lua local DataStoreService = game:GetService("DataStoreService") local positionStore = DataStoreService:GetDataStore("PlayerPosition") ``` 3. Add a helper function called **savePosition** that takes in a player's **userId** and a **Vector3** position. Since you can't save **Vector3** values directly, convert the position to a table of **X, Y, Z** numbers.```lua local function savePosition(player, position) local userId = player.UserId local success, err = pcall(function() positionStore:SetAsync(userId, {position.X, position.Y, position.Z}) end) end ``` 4. **OPTIONAL** Add a warning to **savePosition** that prints an error message and the name of the player you failed to save the position for. While not mandatory, this step is good practice for easier debugging in case your function fails.```lua local function savePosition(player, position) local userId = player.UserId local success, err = pcall(function() positionStore:SetAsync(userId, {position.X, position.Y, position.Z}) end) if not success then warn("Could not save position for", player.Name, ":", err) end end ``` 5. Add another helper function called **loadPosition** to load the saved coordinates for a player in the PlayerPosition data store. Use **`Class.PVInstance.PivotTo|PivotTo`** to restore the character's position in the world. Since the player's saved position is returned as a table, you have to rebuild a **Vector3** and then convert it to a **CFrame** to move the character.```lua local function loadPosition(userId, character) local userId = player.UserId local success, savedCoords = pcall(function() return positionStore:GetAsync(userId) end) if success and savedCoords then local pos = Vector3.new(savedCoords[1], savedCoords[2], savedCoords[3]) print("Restored position for", player.Name) character:PivotTo(CFrame.new(pos)) elseif not success then warn("Could not load position for", player.Name) end end ``` 6. Modify the **onPlayerAdded** event to set up the position logic for each player that joins your game. Inside the updated **onPlayerAdded** function: - **CharacterAdded** calls **loadPosition** to restore the character's last known location when a player's character spawns. - **CharacterRemoving** calls **savePosition** to save the current position of the player's character if they're removed from the game. - **`Class.PVInstance.GetPivot|GetPivot`** gets the character's exact location in the world at the time they're removed.```lua local function onPlayerAdded(player) local userId = player.UserId player.CharacterAdded:Connect(function(character) loadPosition(player, character) end) player.CharacterRemoving:Connect(function(character) local pos = character:GetPivot().Position savePosition(player, pos) end) end Player.PlayerAdded:Connect(onPlayerAdded) ``` ## Final code See the following code snippets for the complete **GoldManager** and **PositionManager** scripts. You can paste and run them directly in the [Gold Rush save data tutorial - Start](https://www.roblox.com/games/5268331031/Gold-Rush) `.rbxl` file in Studio. ### GoldManager ```lua -- Get services -- Get services local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") local CollectionService = game:GetService("CollectionService") local DataStoreService = game:GetService("DataStoreService") -- Remote event to update the UI local uiEvent = ReplicatedStorage:WaitForChild("UIEvent") -- Data store for saving the player's gold chunks local goldStore = DataStoreService:GetDataStore("PlayerGold") -- Table to store the player's gold chunks for a single session local playerGold = {} -- Constants local GOLD_VALUE = 10 -- How much each gold chunk is worth local GOLD_REGEN_DELAY = 2 -- The delay before a chunk respawns (in seconds) local AUTOSAVE_INTERVAL = 30 -- How often the player's chunks are saved (in seconds) -- Function to handle the player collecting a gold chunk local function processGoldTouch(chunk, player) -- Hide the chunk when the player collects it chunk.Parent = nil -- Add the chunk to the player's session gold score local userId = player.UserId playerGold[userId] = (playerGold[userId] or 0) + GOLD_VALUE print(player.Name, "now has", playerGold[userId], "gold") -- Update the UI to reflect the new gold score for this session uiEvent:FireClient(player, { gold = playerGold[userId], doTween = true, showAlert = false }) -- Wait a bit and then respawn the gold chunk that was just collected task.delay(GOLD_REGEN_DELAY, function() chunk.Parent = workspace.GoldChunks end) end -- Loop through all parts tagged with "Gold" and connect them to touch detection for _, chunk in ipairs(CollectionService:GetTagged("Gold")) do chunk.Touched:Connect(function(otherPart) local player = Players:GetPlayerFromCharacter(otherPart.Parent) if player then processGoldTouch(chunk, player) end end) end -- Function to save the player's gold to the data store local function saveGold(player, value) local userId = player.UserId local success, err = pcall(function() goldStore:SetAsync(userId, value) end) if not success then warn("Could not save gold for", player.Name, ":", err) end end -- Function to handle the player joining the game local function onPlayerAdded(player) local userId = player.UserId print(player.Name, "has joined the game") -- Try to load the player's gold from the data store local success, storedGold = pcall(function() return goldStore:GetAsync(userId) end) if success then -- Set the current session gold score using the data store value for the player local currentGold = storedGold or 0 playerGold[userId] = currentGold -- Update the UI with the data store value for the player uiEvent:FireClient(player, { gold = currentGold, doTween = false, showAlert = false }) print("Loaded", currentGold, "gold for", player.Name) else -- Notify the player their gold couldn't be loaded uiEvent:FireClient(player, { showAlert = true }) warn("Could not load gold for", player.Name) end -- Start the autosave coroutine to save the player's gold every 30 seconds coroutine.wrap(function() while player.Parent do task.wait(AUTOSAVE_INTERVAL) if playerGold[userId] then saveGold(player, playerGold[userId]) print("Autosaving", playerGold[userId], "gold for", player.Name) end end end)() end -- Function to handle the player leaving the game local function onPlayerRemoving(player) local userId = player.UserId print(player.Name, "has left the game") -- Check if the player's gold was tracked if playerGold[userId] then -- Save the player's gold to the data store before they leave saveGold(player, playerGold[userId]) end end -- Connect the joining and leaving player events Players.PlayerAdded:Connect(onPlayerAdded) Players.PlayerRemoving:Connect(onPlayerRemoving) ``` ### PositionManager ```lua -- Get services local Players = game:GetService("Players") local DataStoreService = game:GetService("DataStoreService") -- Data store for saving the player's position local positionStore = DataStoreService:GetDataStore("PlayerPosition") -- Function to save the player's position as a table of {X, Y, Z} local function savePosition(player, position) local userId = player.UserId local success, err = pcall(function() positionStore:SetAsync(userId, {position.X, position.Y, position.Z}) end) if not success then warn("Could not save position for", player.Name, ":", err) end end -- Function to load the player's last saved position and move their character local function loadPosition(player, character) local userId = player.UserId local success, savedCoords = pcall(function() return positionStore:GetAsync(userId) end) if success and savedCoords then local pos = Vector3.new(savedCoords[1], savedCoords[2], savedCoords[3]) print("Restored position for", player.Name) character:PivotTo(CFrame.new(pos)) elseif not success then warn("Could not load position for", player.Name) end end -- Function to handle the player joining the game local function onPlayerAdded(player) local userId = player.UserId -- Try to load the player's position when their character spawns player.CharacterAdded:Connect(function(character) loadPosition(player, character) end) -- Save the player's position when their character is removed player.CharacterRemoving:Connect(function(character) local pos = character:GetPivot().Position savePosition(player, pos) end) end Players.PlayerAdded:Connect(onPlayerAdded) ```
--- title: "Control the user's camera" url: /docs/en-us/tutorials/use-case-tutorials/input-and-camera/control-the-users-camera last_updated: 2026-06-29T19:34:14Z description: "Explains how to customize the default camera view for a user." --- # Control the user's camera The user's view of the world is represented by a `Class.Camera` object. You can change the camera behavior to suit your experience in a variety of ways. For example, the camera can react to events in the world, such as shaking when a monster walks by, or locked to the side of the user character, as in a side-scroller. ## Create a first-person camera A first-person camera is a view where the camera stays locked with the character's head, which is more accurate to real life. It's common in shooter and story experiences where the goal is to make the user feel immersed in the world. _First Person Camera_ _Classic Roblox Camera_ In Studio, the `Class.StarterPlayer` object contains a number of properties that affects the user's camera. The **CameraMode** property determines how the camera behaves. 1. Select **StarterPlayer**.![alt](../../../assets/tutorials/controlling-the-players-camera/StarterPlayerSelection.png) 2. Change CameraMode to **LockFirstPerson**. This ensures the user's camera doesn't move away from their head.![alt](../../../assets/tutorials/controlling-the-players-camera/CameraModeProperty.png) 3. Playtest to see the first person camera in action. > **Warning:** If your cursor is stuck in the middle of the screen while testing, you can free up your mouse by pressing the `Escape` key or press `Shift``F5` to end the test. ## Create a side-scrolling camera A side-scrolling view keeps the camera at a fixed position relative to the side of the character, giving the world a two-dimensional feel. ![alt](../../../assets/tutorials/controlling-the-players-camera/SidescrollingCameraExample.jpg) ### Script the camera 1. Expand StarterPlayer, and in StarterPlayerScripts add a **LocalScript** named `CameraManager`.![alt](../../../assets/tutorials/controlling-the-players-camera/CameraManagerScript.png) 2. At the top of the script, copy and paste the following code sample to get the **Players service**, and then in a new variable get the local user.```lua local Players = game:GetService("Players") local player = Players.LocalPlayer ``` 3. Create a function called `updateCamera`. This holds the logic needed to get and set a new position for the camera.```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local function updateCamera() end ``` 4. Inside the function, get the user's character model and check if it exists by using an if statement.```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local function updateCamera() local character = player.Character if character then end end ``` > **Info:** Only a user can see their own camera configuration, so it is always controlled using `Class.LocalScript`. ### Point the camera All character models contain a part named **HumanoidRootPart**, which can be used to get the character's position in the world. This sets position the camera points at. 1. Use `FindFirstChild` to get the HumanoidRootPart and check it exists using an if statement.```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local function updateCamera() local character = player.Character if character then local root = character:FindFirstChild("HumanoidRootPart") if root then end end end ``` 2. The position of the HumanoidRootPart is actually 2 studs below the user's head. To fix this, add a new `Datatype.Vector3` with a height of **2 studs** to the root's position.```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local HEIGHT_OFFSET = 2 local function updateCamera() local character = player.Character if character then local root = character:FindFirstChild("HumanoidRootPart") if root then local rootPosition = root.Position + Vector3.new(0, HEIGHT_OFFSET, 0) end end end ``` > **Info:** Separate individual numbers into variables to make them easier to tweak later. ### Set the camera position The camera also needs a position. To give the user's view a 2D side-scrolling look, the camera needs to look directly at the side of the character. Place the camera to the side of the user by adding depth to just the **Z axis** of the camera's position using a `Datatype.Vector3`. ```lua local player = Players.LocalPlayer local CAMERA_DEPTH = 24 local HEIGHT_OFFSET = 2 local function updateCamera() local character = player.Character if character then local root = character:FindFirstChild("HumanoidRootPart") if root then local rootPosition = root.Position + Vector3.new(0, HEIGHT_OFFSET, 0) local cameraPosition = Vector3.new(rootPosition.X, rootPosition.Y, CAMERA_DEPTH) end end end ``` ### Update CurrentCamera Now that the variables for the camera's position and the camera's target are ready, it's time to update the camera's position. You can access the user's camera through the `CurrentCamera` property of Workspace. The camera has a `Datatype.CFrame` property to determine its position. You can use `Datatype.CFrame.lookAt()` to update the camera. It takes two positions and creates a CFrame located at the first position pointed towards the second. Use `Datatype.CFrame.lookAt()` to create a CFrame that is positioned at `cameraPosition` and pointed toward `rootPosition`. ```lua local player = Players.LocalPlayer local camera = workspace.CurrentCamera local CAMERA_DEPTH = 24 local HEIGHT_OFFSET = 2 local function updateCamera() local character = player.Character if character then local root = character:FindFirstChild("HumanoidRootPart") if root then local rootPosition = root.Position + Vector3.new(0, HEIGHT_OFFSET, 0) local cameraPosition = Vector3.new(rootPosition.X, rootPosition.Y, CAMERA_DEPTH) camera.CFrame = CFrame.lookAt(cameraPosition, rootPosition) end end end ``` ### Sync the camera The last step is to run this function repeatedly to keep the camera in sync with the user. The image the user sees is constantly refreshing. The split second it takes to do all of the calculations necessary is called the **render step**. `Class.RunService:BindToRenderStep()` makes it simple to execute a function on every frame by accepting these three parameters: - `name` - The name of this binding, which should be unique so it won't clash with other functions of the same name. - `priority` - The higher the number, the higher the priority. This function should run **after** Roblox's default camera update, so the priority is set to 1 level higher than the internal camera's RenderPriority. - `function` - The function to be bound to the render step. 1. Use `Class.RunService:BindToRenderStep()` to bind the `updateCamera` function to the render step.```lua local Players = game:GetService("Players") local RunService = game:GetService("RunService") local player = Players.LocalPlayer local camera = workspace.CurrentCamera local CAMERA_DEPTH = 24 local HEIGHT_OFFSET = 2 local function updateCamera() local character = player.Character if character then local root = character:FindFirstChild("HumanoidRootPart") if root then local rootPosition = root.Position + Vector3.new(0, HEIGHT_OFFSET, 0) local cameraPosition = Vector3.new(rootPosition.X, rootPosition.Y, CAMERA_DEPTH) camera.CFrame = CFrame.lookAt(cameraPosition, rootPosition) end end end RunService:BindToRenderStep("SidescrollingCamera", Enum.RenderPriority.Camera.Value + 1, updateCamera) ``` 2. Playtest your code. Use the `A` and `D` keys to move your character from side to side. ## Create an isometric camera The basic structure of getting the user's position and updating the camera's position every frame can be adapted to many other camera styles, such as an **isometric camera**. An isometric camera is a 3D view pointing slightly down at a fixed angle towards the user character. ![alt](../../../assets/tutorials/controlling-the-players-camera/IsometricCameraExample.jpg) ### Modify position and view 1. Using the code from the previous example, modify `cameraPosition` to add the same amount to all 3 dimensions.![alt](../../../assets/tutorials/controlling-the-players-camera/VectorComponentDemonstration.jpg)```lua local function updateCamera() local character = player.Character if character then local root = character:FindFirstChild("HumanoidRootPart") if root then local rootPosition = root.Position + Vector3.new(0, HEIGHT_OFFSET, 0) local cameraPosition = rootPosition + Vector3.new(CAMERA_DEPTH, CAMERA_DEPTH, CAMERA_DEPTH) camera.CFrame = CFrame.lookAt(cameraPosition, rootPosition) end end end RunService:BindToRenderStep("IsometricCamera", Enum.RenderPriority.Camera.Value + 1, updateCamera) ``` 2. Changing the camera's `Class.Camera.FieldOfView|FieldOfView` property simulates zooming it in and out, which can give the view a flatter look. Try setting it to a value of `20` to zoom in, and increasing the camera's distance from the user to compensate.```lua local Players = game:GetService("Players") local RunService = game:GetService("RunService") local player = Players.LocalPlayer local camera = workspace.CurrentCamera local CAMERA_DEPTH = 64 local HEIGHT_OFFSET = 2 camera.FieldOfView = 20 local function updateCamera() ``` By changing the way the camera behaves, you can achieve a whole new look for your experience. See if you can change the `cameraPosition` to achieve a top-down camera with the same script. Try tweaking settings to get a result you like! --- title: "Detect user input" url: /docs/en-us/tutorials/use-case-tutorials/input-and-camera/detect-user-input last_updated: 2026-06-29T19:34:14Z description: "Explains how to customize and connect user input to actions." --- # Detect user input Connecting user input to actions gives users much better and more intuitive control over your experience's features. In this tutorial, you will bind a reloading action to a specific key. ## Get started This tutorial uses the **Blaster** tool created in [Create Player Tools](/docs/en-us/tutorials/use-case-tutorials/scripting/intermediate-scripting/create-player-tools.md). You can follow those instructions to create the tool or you can download the [Blaster](https://www.roblox.com/library/6571559694/Blaster) model and insert it into **StarterPack**. Models can be added into your Inventory to be used between any experience. To add a model to your experience: 1. In a browser, open the [model](https://www.roblox.com/library/6571559694/Blaster) page, click the **Get** button. This adds the model into your inventory. 2. From Studio's **Window** menu or **Home** tab toolbar, open the [Toolbox](/docs/en-us/projects/assets/toolbox.md) and select the **Inventory** tab. 3. Make sure the dropdown is on **My Models**. 4. Select the **Blaster** model to add it into the experience. ## Create an action handler First, you'll need a function to handle when user input is detected. 1. Open the **ToolController** `Class.LocalScript` inside of the Blaster.![View of the Explorer where the ToolController script inside of the Blaster tool is selected](../../../assets/tutorials/detecting-user-input/ToolControllerExplorer.png) 2. Make a variable to store a name for the action.```lua local tool = script.Parent local RELOAD_ACTION = "reloadWeapon" local function toolEquipped() tool.Handle.Equip:Play() end local function toolActivated() tool.Handle.Activate:Play() end tool.Equipped:Connect(toolEquipped) tool.Activated:Connect(toolActivated) ``` 3. Create a function named onAction that receives three arguments: `actionName`, `inputState`, and `inputObject`. This will be the function that runs when user input is detected.```lua local tool = script.Parent local RELOAD_ACTION = "reloadWeapon" local function onAction(actionName, inputState, inputObject) end local function toolEquipped() tool.Handle.Equip:Play() end ``` 4. Inside the function, check that the given `actionName` matches the reload action name and make sure `inputState` is `Enum.UserInputState|UserInputState.Begin` (the beginning state). This is important because the function will run every time the `inputState` changes, but the reload only needs to happen once.```lua local function onAction(actionName, inputState, inputObject) if actionName == RELOAD_ACTION and inputState == Enum.UserInputState.Begin then end end ``` 5. To make it obvious when the user reloads, change the `Class.BackpackItem.TextureId|TextureId` of the tool to `"rbxassetid://6593020923"` for a moment, and then change it back to its original value of `"rbxassetid://92628145"`.```lua local function onAction(actionName, inputState, inputObject) if actionName == RELOAD_ACTION and inputState == Enum.UserInputState.Begin then tool.TextureId = "rbxassetid://6593020923" task.wait(2) tool.TextureId = "rbxassetid://92628145" end end ``` ## Bind the action `Class.ContextActionService` can be used to **bind** a function to a specific input by using the `Class.ContextActionService:BindAction()|BindAction` function, which accepts several arguments: - The name of the action. - The function to handle the action (also called a "callback"). - Whether or not a touchscreen button should be displayed. - Any amount of `Enum.KeyCodes` to detect and associate with the action. KeyCodes are values that represent different input buttons, such as keyboard keys or controller buttons. A full list of codes is available `Enum.KeyCode|here`. 1. Get `Class.ContextActionService` at the top of the script.```lua local ContextActionService = game:GetService("ContextActionService") local tool = script.Parent local RELOAD_ACTION = "reloadWeapon" ``` 2. Inside of the `toolEquipped` function, call `BindAction` and pass through the following arguments: - The name of the action (`RELOAD_ACTION`) - The action handler (`onAction`) - A value to create a touch button (`true`) - A key press to detect (`Enum.KeyCode.R`)```lua local RELOAD_ACTION = "reloadWeapon" local function onAction(actionName, inputState, inputObject) if actionName == RELOAD_ACTION and inputState == Enum.UserInputState.Begin then tool.TextureId = "rbxassetid://6593020923" task.wait(2) tool.TextureId = "rbxassetid://92628145" end end local function toolEquipped() ContextActionService:BindAction(RELOAD_ACTION, onAction, true, Enum.KeyCode.R) tool.Handle.Equip:Play() end ``` 3. Playtest by equipping the tool and pressing the `R` key on your keyboard. The backpack icon should momentarily change to a waiting symbol to signal that the weapon is reloading: ## Unbind the action When the user unequips the tool, the action needs to be **unbound** so they can't reload without the tool being equipped. 1. Create a new function called `toolUnequipped` and call `Class.ContextActionService:UnbindAction()|UnbindAction`, passing through the action name.```lua local function toolEquipped() ContextActionService:BindAction(RELOAD_ACTION, onAction, true, Enum.KeyCode.R) tool.Handle.Equip:Play() end local function toolUnequipped() ContextActionService:UnbindAction(RELOAD_ACTION) end local function toolActivated() tool.Handle.Activate:Play() end tool.Equipped:Connect(toolEquipped) tool.Activated:Connect(toolActivated) ``` 2. Connect the `toolUnequipped` function to the `Class.Tool.Unequipped|Unequipped` event so the function will run when the event fires.```lua local ContextActionService = game:GetService("ContextActionService") local tool = script.Parent local RELOAD_ACTION = "reloadWeapon" local function onAction(actionName, inputState, inputObject) if actionName == RELOAD_ACTION and inputState == Enum.UserInputState.Begin then tool.TextureId = "rbxassetid://6593020923" task.wait(2) tool.TextureId = "rbxassetid://92628145" end end local function toolEquipped() ContextActionService:BindAction(RELOAD_ACTION, onAction, true, Enum.KeyCode.R) tool.Handle.Equip:Play() end local function toolUnequipped() ContextActionService:UnbindAction(RELOAD_ACTION) end local function toolActivated() tool.Handle.Activate:Play() end tool.Equipped:Connect(toolEquipped) tool.Unequipped:Connect(toolUnequipped) tool.Activated:Connect(toolActivated) ``` 3. Playtest to confirm that everything works correctly. You should be able to reload when the tool is equipped, but not when it is unequipped. Your reloading animation is now complete - for an extra challenge, try counting down an ammo counter each time the blaster is fired. You can then deactivate the `toolActivated` function when the gun has no ammo, then reactivate it once the reload animation is finished. --- title: "Create flickering lights" url: /docs/en-us/tutorials/use-case-tutorials/lighting/create-flickering-lights last_updated: 2026-06-29T19:34:14Z description: "Explains the process of creating flickering local light sources in your experience." --- # Create flickering lights **Flickering lights** is a powerful force in influencing an environment's tone. For example, a home with lighting of a consistent brightness can feel warm and inviting, but if you add flickering lights to that same home's hallway, it becomes haunting and might hint at possible danger ahead. By strategically modeling and scripting different light sources within your experience, you can add a whole new level to your environmental storytelling. As with all 3D creation, there are many ways to achieve any particular goal. In this guide, you can quickly create a flickering light animation using tools and methods available only within Studio with only a few basic assets, including a `.rbxm` file for the banker's lamp model that contains child `Class.MeshParts` you can customize to fit the aesthetic of your own experiences. In the following method to create flickering lights, follow each section to learn how to create: - A brightness `Datatype.NumberSequence` to influence the lamp's brightness over time. - A loop duration attribute to determine the amount of time each flickering loop must take. - A `Class.Script` that configures how the two attributes work with the model's child `Class.MeshPart|MeshParts` to flicker the lamp's light. > **Info:** You can create your own assets in third-party modeling tools and follow along with your own design. For information on exporting models for use in Studio, see [Exporting Requirements](/docs/en-us/art/modeling/export-requirements.md). ## Import the sample lamp This guide uses a downloadable `.rbxm` file of a high-quality, customizable banker's lamp model to demonstrate a flickering light technique. You can use this model to understand the basic principles, then apply them to your own models you create within Studio or other third-party modeling software. To import the [BankersLamp](../../../assets/tutorials/creating-flickering-lights/BankersLamp.rbxm) `.rbxm`: 1. In the **Explorer** window, right-click **Workspace**. A contextual menu displays. 2. Select **Insert From File…** A file browser displays. 3. Select the **BankersLamp** `.rbxm`, then click the **Open** button. The model displays within the viewport. ## Create a brightness NumberSequence A `Datatype.NumberSequence` is a data type that represents a series of number values from 0 to 1 over an instance's lifetime. This type of data type is useful for creating flickering lights because you can specify how you want the lamp light's brightness to change over its lifetime, and when you change brightness from full intensity to no light at all in short succession, you can achieve a flickering effect. The X axis of a `Datatype.NumberSequence` represents time, and the Y axis represents relative brightness. Each square at the start and end of the number sequence is a **keypoint** that determines the value of the property at that point of the light brightness's lifetime. When you first create a `Datatype.NumberSequence`, the graph is a straight line and the light remains the same brightness intensity throughout its lifetime, but by adding and moving keypoints around, you can create curves within the sequence and change the lamp's brightness over time. While the lamp doesn't have a brightness `Datatype.NumberSequence` by default, you can create a brightness attribute, set it to a `Datatype.NumberSequence` type, then experiment by adding keypoints with different values until the light flickers at your set cadence. To create a brightness `Datatype.NumberSequence`: 1. Add a new NumberSequence attribute to the lamp model. 1. In the **Explorer** window, select the lamp model. 2. In the **Properties** window, navigate to the **Attributes** section and click on the **Add Attribute** button. The **Add Attribute** dialog displays. 3. In the **Add Attribute** dialog, 1. In the **Name** field, input **BrightnessCurve**. 2. Click the **Type** dropdown menu, then select **NumberSequence**. 3. Click the **Save** button. The new attribute displays in the **Attributes** section of the **Properties** window. 2. Select the new **BrightnessCurve** property, then click the **…** button. A number sequence pop-up displays. 3. Perform one of the following actions: - To change the brightness at a point, click on a keypoint and either drag it up or down, or enter a value in the **Value** field. - To insert new keypoints, click on any point in the graph. - To delete a keypoint, select the keypoint, then the **Delete** button. - To add a random range for brightness, click on any keypoint and drag the envelope lines up or down. At that time, the light generates at a random brightness between the pink envelope. For example, the following graph makes the light flicker to full brightness for the first flicker, 50% brightness for the second flicker, then 75% brightness for the third flicker. ## Create a loop duration Now that you have a `Datatype.NumberSequence` to determine how the lamp light's brightness changes over its lifetime, you must determine how long you want the flickering loop to take. In other words, this loop duration essentially controls how often the `Datatype.NumberSequence` repeats, in seconds. To create a loop duration: 1. Add a new loop duration attribute to the lamp model. 1. In the **Explorer** window, select the lamp model. 2. In the **Properties** window, navigate to the **Attributes** section and click on the **Add Attribute** button. The **Add Attribute** dialog displays. 3. In the **Add Attribute** dialog, 1. In the **Name** field, input **LoopDuration**. 2. Click the **Type** dropdown menu, then select **Number**. 3. Click the **Save** button. The new attribute displays in the **Attributes** section of the **Properties** window. 2. Set the new **LoopDuration** attribute to **1**. This tells the `Datatype.NumberSequence` to repeat after one second. ## Script the light flicker Now that you have all of the elements to control the brightness of your lamp over its lifetime, it's time to create a `Class.Script` that will get everything to work together and flicker the light. To script the light flicker: 1. In the **Explorer** window, hover over the lamp model and click the ⊕ button. A contextual menu displays. 2. From the menu, insert a **Script**. 3. In the new script, enter the following: ```lua local RunService = game:GetService("RunService") -- Gets the attribute values set on the model. local brightnessCurve = script.Parent:GetAttribute("BrightnessCurve") local loopDuration = script.Parent:GetAttribute("LoopDuration") -- Stores references to the model's instances that will change. local light = script.Parent.lamp_hood.SpotLight local bulb = script.Parent.lightbulb local beam = script.Parent.lamp_hood.Beam -- Stores properties' original values that will change. local origLightBrightness = light.Brightness local origBeamBrightness = beam.Brightness local origBulbColor = bulb.Color -- Gets the value of the NumberSequence (ns) at a specific time (nsTime) function evaluateNumberSequence(ns: NumberSequence, nsTime: number) -- If we are at 0 or 1, returns the first or last keypoint's value, respectively. if nsTime == 0 then return ns.Keypoints[1].Value end if nsTime == 1 then return ns.Keypoints[#ns.Keypoints].Value end -- Otherwise, steps through each sequential pair of keypoints. for i = 1, #ns.Keypoints - 1 do -- Gets the current and next keypoint. local currKp = ns.Keypoints[i] local nextKp = ns.Keypoints[i + 1] -- If nsTime is between the keypoints' times, if nsTime >= currKp.Time and nsTime < nextKp.Time then -- Calculates where nsTime lies between the keypoints' times, calls this alpha. local alpha = (nsTime - currKp.Time) / (nextKp.Time - currKp.Time) -- Returns the value between the points for nsTime using alpha. return currKp.Value + (nextKp.Value - currKp.Value) * alpha end end end RunService.Heartbeat:Connect(function() -- Solves for the NumberSequence's time (between 0 and 1). local t = time() / loopDuration local numberSequenceTime = t - (t // 1) -- Gets the NumberSequence's value at this time. local brightnessValue = evaluateNumberSequence(brightnessCurve, numberSequenceTime) -- Adjusts brightness and color properties based on this value. light.Brightness = origLightBrightness * brightnessValue beam.Brightness = origBeamBrightness * brightnessValue bulb.Color = Color3.new( origBulbColor.r * brightnessValue, origBulbColor.g * brightnessValue, origBulbColor.b * brightnessValue ) end) ``` When you [playtest your experience](/docs/en-us/studio/testing-modes.md#playtesting), the `Class.RunService.Heartbeat|Heartbeat` event connection executes the following every frame: 1. Solves for a time (`numberSequenceTime`) within the `brightnessCurve` `Datatype.NumberSequence` based on the current time. - The time is between 0 and 1 as these represent the beginning and end of the NumberSequence, respectively. 2. Solves for the value (`brightnessValue`) of the `brightnessCurve` `Datatype.NumberSequence` at time `numberSequenceTime`. - `evaluateNumberSequence()` computes the value associated with the time for any `NumberSequence`. - This value serves as a relative brightness value to apply to the properties that change over time. 3. Changes the lamps' properties by multiplying `brightnessValue` by the light's brightness, beam's brightness, and bulb's color. The changes to these properties over time result in the flickering effect below. --- title: "Enhance indoor environments with realistic lighting" url: /docs/en-us/tutorials/use-case-tutorials/lighting/enhance-indoor-environments last_updated: 2026-06-29T19:34:14Z description: "Explains how to leverage realistic lighting to enhance indoor environments." --- # Enhance indoor environments with realistic lighting `Enum.LightingStyle|Realistic` lighting is the most advanced and powerful `Class.Lighting.LightingStyle|LightingStyle` you can use for rendering the 3D environment within your experiences. It offers pixel perfect light emission, detailed shadows, and specular highlights that mimic real-world lighting for both indoor and outdoor spaces. Using the [Lighting Indoors - Start](https://www.roblox.com/games/17561948176/UCT-Lighting-Indoors) `.rbxl` file as a starting place and [Lighting Indoors - Complete](https://www.roblox.com/games/17562253150/UCT-Lighting-Indoors-After) as a reference, this tutorial shows you how to utilize realistic lighting with strategic global and local light source configurations to produce realistic, immersive indoor lighting behavior that navigates players toward the exit of the cabin, including guidance on: - Ensuring shiny surfaces have accurate reflections that update periodically as the environment dynamically changes. - Moving the sun and earth to new positions to angle sunlight from windows onto specific surfaces. - Customizing the atmosphere's ambient hue and density. - Choosing light sources to solve unique environmental concerns with performance and optimization in mind. - Balancing your light sources with how the camera perceives their lighting behavior. If at any point you become stuck in the process, you can use **Lighting Indoors - Complete** as a reference to compare your progress. ![The starting indoor environment you can use to complete this tutorial.](../../../assets/tutorials/enhancing-indoor-environments/Sample-Start.jpg)_Lighting Indoors - Start_ ![The complete indoor environment with global and local lighting you will create by the end of this tutorial.](../../../assets/tutorials/enhancing-indoor-environments/Sample-Complete.jpg)_Lighting Indoors - Complete_ ## Configure global lighting Global lighting is the luminescence from either the sun or moon in an experience. While this tutorial focuses on enhancing the lighting inside of the cabin that's covered from the sun, it's important to configure global lighting because it impacts the air particles within the general atmosphere of the experience, regardless of whether they are indoors or outdoors. By adjusting a couple of key default properties in the `Class.Lighting` service and its child `Class.Atmosphere` object, you can dramatically change how both the atmosphere and sunlight coming through the window appears to players, as well as how this lighting interacts with any other object you place in the experience. ### Enable realistic lighting The `Class.Lighting.LightingStyle|LightingStyle` property determines the behavior of both global and local lighting in your experience. Studio begins every experience with `Enum.LightingStyle|Soft` lighting which renders a flatter look with softer lights and shadows. However, to enhance the environment and enable your local light sources to also produce precise shadows, illumination, and specular highlights, you must enable `Enum.LightingStyle|Realistic` lighting. This lighting configuration allows both your global and local lighting to work together and provide more realistic and immersive visuals. To demonstrate, review how the lighting behavior for the radio changes when **Lighting Indoors - Complete** uses different lighting styles. The local lighting from the candlelight and radio with the `Enum.LightingStyle|Soft` lighting style doesn't produce shadows like the global lighting from the sun, and the details from the radio's leather and wood materials vanish. ![A closeup view of the radio illuminated with Soft lighting style.](../../../assets/tutorials/enhancing-indoor-environments/Radio-Soft.jpg)_`Enum.LightingStyle|Soft` lighting style_ ![A closeup view of the radio illuminated with Realistic lighting style.](../../../assets/tutorials/enhancing-indoor-environments/Radio-Realistic.jpg)_`Enum.LightingStyle|Realistic` lighting style_ It's important to note that because of the method in which the `Enum.LightingStyle|Realistic` lighting system detects indoor spaces to calculate and render applicable shadows, it's best to surround indoor spaces with `Class.Part` objects with a thickness of at least 1 stud to prevent undesirable outdoor light from leaking into the cabin. For example, to avert the indoor and outdoor lighting from blending, the sample **Lighting Indoors - Start** uses parts with a minimum thickness of 2.5 studs to surround all `Class.MeshPart` walls and ceiling objects without obstructing the window so that the sunlight can still shine into the room. ![A top-down view of the cabin that doesn't surround the mesh walls and ceilings with parts. The outdoor light is seeping into the cabin.](../../../assets/tutorials/enhancing-indoor-environments/Cabin-NoParts.jpg)_Cabin that doesn't surround the mesh walls and ceiling with parts_ ![A top-down view of the cabin that surrounds the mesh walls and ceilings with parts. The indoor light unaffected from the outdoor lighting.](../../../assets/tutorials/enhancing-indoor-environments/Cabin-WithParts.jpg)_Cabin that surrounds the mesh walls and ceiling with parts_ To enable **Realistic** lighting: 1. In the **Explorer** window, select **Lighting**. 2. In the **Properties** window, click the **LightingStyle** dropdown, then select **Realistic**.![A full view of the starting cabin with realistic lighting.](../../../assets/tutorials/enhancing-indoor-environments/LightingStyle-Realistic.jpg) ### Elevate metal reflections A core advantage of using realistic lighting is its ability to produce specular highlights on shiny, metallic surfaces. This increases the realism of indoor environments because it emulates real-world lighting behavior, and it provides a sense of depth to objects in the 3D space. By default, all materials use [physically-based rendering](/docs/en-us/art/modeling/surface-appearance.md) (PBR) textures that allow you to display realistic surfaces in various lighting scenarios by using multiple texture maps on a single object. This means that when you use Studio's built-in materials: - The metalness and roughness of a particular surface is already defined for you without any additional steps. - Objects with Studio's built-in materials naturally react more accurately to the lighting in your environment with realistic reflections. You can enhance this effect with the realistic lighting style by increasing the global lighting's `Class.Lighting.EnvironmentDiffuseScale` and `Class.Lighting.EnvironmentSpecularScale` properties, especially when you set each property to `1`. This step is particularly important in this tutorial because it ensures that any PBR textures in the experience, including those from `Class.MaterialVariant|MaterialVariants` or `Class.SurfaceAppearance` objects, look their best and reflect their surroundings accurately. To demonstrate this concept, examine the candle's metal bases with different `Class.Lighting.EnvironmentDiffuseScale` and `Class.Lighting.EnvironmentSpecularScale` property values. When you increase these values, the metal becomes more accurately reflects the lighting from both the global and local light sources, which allows the material to be more tangible to players as they explore the environment. ![A close up view of a statue and candle grouping with dull metal reflections.](../../../assets/tutorials/enhancing-indoor-environments/WithoutReflections.jpg)_`Class.Lighting.EnvironmentDiffuseScale` and `Class.Lighting.EnvironmentSpecularScale` = `0`_ ![A close up view of a statue and candle grouping with shiny metal reflections.](../../../assets/tutorials/enhancing-indoor-environments/WithReflections.jpg)_`Class.Lighting.EnvironmentDiffuseScale` and `Class.Lighting.EnvironmentSpecularScale` = `1`_ To recreate the metal reflections in the sample [Lighting Indoors - Complete](https://www.roblox.com/games/17562253150/UCT-Lighting-Indoors-After) place file: 1. In the **Explorer** window, select **Lighting**. 2. In the **Properties** window, ensure that **EnvironmentalDiffuseScale** and **EnvironmentSpecularScale** are set to `1`. If so, the metal in the experience is accurately reflective. ### Change the time of day In addition to customizing the general atmosphere of the 3D space, global lighting is a powerful tool in creating points of interest within the environment that you want players to explore. When you pair this technique with local light sources, you can indirectly guide players to each section of the gameplay area and prevent them from missing anything important. To demonstrate this process, the **Lighting Indoors - Complete** sample strategically repositions the sun so that its light shines into the cabin at an angle, highlighting the cabin's only door. As players scan the room, each light source indirectly grabs player attention and pulls them clockwise: first toward the local light source of the fireplace, secondly toward the global and local light sources near the window, then finally toward the global light source that shines on the exit. ![A full view of the cabin's indoor space with all light sources visible.](../../../assets/tutorials/enhancing-indoor-environments/Cabin-Final.jpg) To recreate the time of day in the sample [Lighting Indoors - Complete](https://www.roblox.com/games/17562253150/UCT-Lighting-Indoors-After) place file: 1. In the **Explorer** window, select **Lighting**. 2. In the **Properties** window, 1. Set **ClockTime** to `15.6`. The sun moves to the approximate position it would be in at 3:45 pm. 2. **(Optional)** Set **GeographicLatitude** to `323`.![A full view of the cabin with the sun in its new position.](../../../assets/tutorials/enhancing-indoor-environments/TimeOfDay-2.jpg) ### Amplify the sun rays Now that the sun is at an ideal position in the sky to shine its light in through the window and create a point of interest, you can use the `Class.Lighting` service's child `Class.SunRaysEffect|SunRays` object to exaggerate the sun's illumination by amplifying its individual sun rays. Unlike other static atmospheric effects, sun rays dynamically change shape as objects come between the player's camera and the sun, creating realistic light and shadow visuals. To demonstrate, review how the shape of the sun rays changes when you increase their intensity and spread. While the sun rays using the default configuration subtly approach the window, the sun rays using the custom configuration shine into the cabin. In addition to enhancing your visuals, this effect also improves the air particle density effect later in the tutorial because the sun rays highlight the dense air particles from nearly every angle as players traverse the space. ![A front view of the window with the sun rays barely visible.](../../../assets/tutorials/enhancing-indoor-environments/SunRays-Default.jpg)_Default sun rays_ ![A front view of the window with the sun rays fully visible and shining into the cabin.](../../../assets/tutorials/enhancing-indoor-environments/SunRays-Custom.jpg)_Custom sun rays_ To recreate the sun rays in the atmosphere in the sample [Lighting Indoors - Complete](https://www.roblox.com/games/17562253150/UCT-Lighting-Indoors-After) place file: 1. In the **Explorer** window, select the **Lighting** service's child **SunRays** object. 2. In the **Properties** window, 1. Set **Intensity** to `0.023` to increase the opacity of the sun's halo. 2. Set **Spread** to `0.266` to widen the sun rays spread across the sky. ### Adjust the color of ambient light Customizing the color of ambient light, or the general, indirect light in the 3D space, is a common method for both setting the mood of an environment and determining whether its lighting is warm or cool. There are two `Class.Lighting` properties that control the color of ambient lighting: - `Class.Lighting.OutdoorAmbient` controls ambient lighting where the sky is visible. - `Class.Lighting.Ambient` controls ambient lighting within spaces where anything blocks the sky, such as indoor environments. By default, both properties are set to produce a cool gray hue, but this doesn't match the light sources within the cabin. To solve this problem, the **Lighting Indoors - Complete** sample adjusts the `Class.Lighting.Ambient` to produce a warm, orange hue that matches the warmth of the fire in the fireplace and candles, as well as the glow of the setting sun. While this change is subtle, you can significantly see the difference in indirectly lit areas, such as the shadows on the floor. ![An angled top-down view of shadows on the floor that have a cool ambient hue.](../../../assets/tutorials/enhancing-indoor-environments/Ambient-Default.png)_`Class.Lighting.Ambient` = `70, 70, 70`_ ![An angled top-down view of shadows on the floor that have a warm ambient hue.](../../../assets/tutorials/enhancing-indoor-environments/Ambient-Custom.png)_`Class.Lighting.Ambient` = `83, 70, 57`_ To recreate the color of ambient lighting in the sample [Lighting Indoors - Complete](https://www.roblox.com/games/17562253150/UCT-Lighting-Indoors-After) place file: 1. In the **Explorer** window, select **Lighting.** 2. In the **Properties** window, set **Ambient** to `83, 70, 57`. The ambient lighting changes to a more warm and dark orange hue.![A full view of the cabin with the new warm ambient lighting.](../../../assets/tutorials/enhancing-indoor-environments/Ambient-2.jpg) ### Choose a complementary skybox The `Class.Lighting` service has a child `Class.Sky` object with six individual properties that together create the skybox that makes up an experience's sky. Skyboxes can have a major impact on the look and feel of what's in your environment, so it's important to carefully consider how you can choose a skybox that enhances your experience's visual quality, especially as it influences the overall atmosphere that seeps into indoor spaces. Because the **Lighting Indoors - Complete** sample requires a warm atmosphere, it uses a skybox that mostly prioritizes warm hues near the skyline, such as bright yellows, vibrant oranges, and some light greens. For information on how to create and customize skyboxes, see [Skyboxes](/docs/en-us/environment/skybox.md). ![The 2D texture that represents back cubical face of the skybox.](../../../assets/tutorials/enhancing-indoor-environments/SkyboxBk.png)_SkyboxBk_ ![The 2D texture that represents back cubical bottom of the skybox.](../../../assets/tutorials/enhancing-indoor-environments/SkyboxDn.png)_SkyboxDn_ ![The 2D texture that represents front cubical face of the skybox.](../../../assets/tutorials/enhancing-indoor-environments/SkyboxFt.png)_SkyboxFt_ ![The 2D texture that represents left cubical face of the skybox.](../../../assets/tutorials/enhancing-indoor-environments/SkyboxLf.png)_SkyboxLf_ ![The 2D texture that represents right cubical face of the skybox.](../../../assets/tutorials/enhancing-indoor-environments/SkyboxRt.png)_SkyboxRt_ ![The 2D texture that represents top cubical face of the skybox.](../../../assets/tutorials/enhancing-indoor-environments/SkyboxUp.png)_SkyboxUp_ To recreate the skybox in the sample [Lighting Indoors - Complete](https://www.roblox.com/games/17562253150/UCT-Lighting-Indoors-After) place file: 1. In the **Explorer** window, select the **Lighting** service's child **Sky** object. 2. In the **Properties** window, 1. Set **SkyboxBk** to `rbxassetid://162001887`. 2. Set **SkyboxDn** to `rbxassetid://161998893`. 3. Set **SkyboxFt** to `rbxassetid://162001897`. 4. Set **SkyboxLf** to `rbxassetid://162001904`. 5. Set **SkyboxRt** to `rbxassetid://162001919`. 6. Set **SkyboxUp** to `rbxassetid://162001926`.![A full view of the cabin with the new skybox.](../../../assets/tutorials/enhancing-indoor-environments/Skybox-2.jpg) ### Increase air particle density The `Class.Lighting` service has a child `Class.Atmosphere` object with properties that allow you to simulate realistic environments by scattering sunlight in unique ways. While some of these properties are more evident in outdoor environments, such as those that impact the silhouette and blending of distant objects near the skyline, others influence the density and color of the air particles present throughout the 3D space, regardless of whether they are indoors or outdoors. For example, the `Class.Atmosphere.Density` property controls how many particles exist in the air of your experience. When you increase this property, the additional amount of particles create a sense of thickness and weight in the 3D space that's tangible to players exploring your world. This is helpful for adding texture and indirect lighting to an environment without distinct local lighting objects. To illustrate this technique, review the following two images of the **Lighting Indoors - Complete** cabin with different air particle densities. When `Class.Atmosphere.Density` is set to `0`, the cabin is cold and dark despite having local light sources, but when `Class.Atmosphere.Density` is set to `0.5`, the cabin is warm and hazy. This effect is particularly impactful because the `Class.Atmosphere` object adds a warm orange hue to air particles based on the colors of its skybox. ![An angled side view of the cabin that's dark with clear air.](../../../assets/tutorials/enhancing-indoor-environments/LowDensity.jpg)_`Class.Atmosphere.Density` = `0`_ ![An angled side view of the cabin that's bright with hazy air.](../../../assets/tutorials/enhancing-indoor-environments/HighDensity.jpg)_`Class.Atmosphere.Density` = `0.5`_ To recreate the density of the atmosphere's air particles in the sample [Lighting Indoors - Complete](https://www.roblox.com/games/17562253150/UCT-Lighting-Indoors-After) place file: 1. In the **Explorer** window, select the **Lighting** service's child **Atmosphere** object. 2. In the **Properties** window, set **Density** to `0.5`. The air becomes hazy.![A full view of the cabin with dense air particles.](../../../assets/tutorials/enhancing-indoor-environments/Density-2.jpg) ## Configure local lighting Local lighting is the luminescence from local [light sources](/docs/en-us/effects/light-sources.md) in your experience, such as `Class.PointLight`, `Class.SpotLight`, and `Class.SurfaceLight` objects. After you configure your global lighting to meet the general lighting and atmosphere requirements of the experience, it's important to utilize these local light sources to meet the specific lighting needs of anything in the scene that you want to illuminate within the 3D space. The following sections demonstrate how to create each type of local light source and adjust a couple of its default properties to significantly alter how the local lighting compliments your global lighting and interacts with the overall environment. ### Light the candles The first objects in the scene that need to illuminate the space are the groupings of candles on the dresser near the window. Their default configuration in the **Lighting Indoors - Start** sample includes the following `Class.ParticleEmitter` objects to add gentle movement to their flames: - **CandleFire** - Produces light tapered particles to emulate the candle's flame. - **CandleSmoke** - Produces dark tapered particles to emulate the cande's smoke. - **CandleFillLight** - Produces circular particles to emulate the candle's soft glow. While this is an excellent start, the candles still don't produce realistic lighting behavior that fully illuminates the nearby area or bounces off surrounding objects. To solve this lighting need, the **Lighting Indoors - Complete** sample introduces a `Class.PointLight` object in the middle of the candle grouping. `Class.PointLight` objects emit light spherically from a single point in every direction, similar to a small sun. This lighting behavior makes them useful for objects that produce omnidirectional light, such as uncovered light bulbs, torches, and candlelight. To illustrate, review the following two images of how the scene changes with and without a point light. The scene without a point light produces a soft lighting that only illuminates the candle itself, but the scene with a point light produces stronger lighting that illuminates the candle, nearby walls, and the statue's subtle details. ![An angled view of the cabin's radio, statue, and one candle grouping without a point light.](../../../assets/tutorials/enhancing-indoor-environments/Candles-WithoutPL.jpg)_Without point light_ ![An angled view of the cabin's radio, statue, and one candle grouping with a point light.](../../../assets/tutorials/enhancing-indoor-environments/Candles-WithPL.jpg)_With point light_ Point lights can be more performance intensive on low-end devices than other local light sources because point lights project in every direction. To optimize your experience, consider how many point lights are necessary without compromising your ideal lighting behavior. For example, the sample places only a single point light within the middle of the candle because each candle flame is small enough that individual point lights don't notably enhance the visuals within the gameplay area. To recreate candle local lighting in the sample [Lighting Indoors - Complete](https://www.roblox.com/games/17562253150/UCT-Lighting-Indoors-After) place file: 1. Insert a point light into one of the candle groupings. 1. In the **Explorer** window, expand the **Candle_Group_A** model. 2. Hover over the **FillLight** part, then click the ⊕ icon. A contextual menu displays. 3. From the contextual menu, insert a **PointLight**. 2. Select the new point light, then in the **Properties** window, 1. Set **Brightness** to `0.7` to reduce the intensity of the light to be more compatible for candlelight. 2. Set **Color** to `255, 202, 156` to tint the light to a peach hue and replicate the warmth of the candle source. 3. Enable **Shadows** to allow the candlelight to produce shadows. 3. Repeat this process for **Candle_Group_B**. 4. **(Optional)** Using the same technique from the previous steps, add a point light with your own configuration to illuminate the fire burning in the fireplace. ### Turn on the desk lamp The second object in the scene that needs to illuminate the space is the desk lamp near the back corner of the cabin. When you're deciding which local light object you want to use, it's important to review how the type and shape of the light source affects how it shines light in the real world. For example, because the **Lighting Indoors - Start** sample's desk lamp includes a white light bulb under a curved, dark green hood, the lamp must produce two different types of light: - Bright white light that illuminates only in the direction of the desk. - Subtle green light that leaks through the dark green hood in all other directions aside from the bright white light. To solve the first lighting need, the **Lighting Indoors - Complete** sample introduces a `Class.SpotLight` into the desk lamp to shine outward from where it's uncovered from the lamp's hood. `Class.SpotLight` objects emit light in a single direction in the shape of a cone with a spherical base, and they include a `Class.SpotLight.Face|Face` property that determines which face/axis light emits from. This makes their lighting behavior useful for objects that produce directional light, such as street lamps, flashlights, and headlights. To solve the second lighting need, the sample uses a `Class.PointLight` that produces a soft green light that illuminates the entire corner of the cabin without being significantly noticeable within the bright white light. To illustrate, review the following two images of how the scene changes with and without an additional point light. Both scenes include comparable bright white light, but the scene with the point light creates realistic lighting behavior that bounces off the nearby statue and bookcases to make the cabin's corner visible and a point of interest. ![A close up view of the desk lamp without a point light to illuminate the space behind the lamp.](../../../assets/tutorials/enhancing-indoor-environments/Lamp-2.jpg)_Without point light_ ![A close up view of the desk lamp with a point light to illuminate the space behind the lamp.](../../../assets/tutorials/enhancing-indoor-environments/Lamp-4.jpg)_With point light_ To recreate the lamp local lighting in the sample [Lighting Indoors - Complete](https://www.roblox.com/games/17562253150/UCT-Lighting-Indoors-After) place file: 1. Insert a spotlight into one of the candle groupings. 1. In the **Explorer** window, expand the **Bankers_Lamp** model, then the **Lamp_Hood** mesh. 2. Hover over the **Lightbulb** mesh, then click the ⊕ icon. A contextual menu displays. 3. From the contextual menu, insert a **SpotLight**.![A close up view of the desk lamp with a default spotlight.](../../../assets/tutorials/enhancing-indoor-environments/Lamp-1.jpg) 2. Select the new spotlight, then in the **Properties** window, 1. Set **Face** to **Bottom** to shine the spotlight toward the desk. 2. Set **Angle** to `140` to project the light in a hemisphere shape rather than a cone shape. 3. Set **Brightness** to `4` to increase the intensity of the light. 4. Set **Color** to `255, 238, 202` to tint the light to a light tan hue and replicate the subtle warmth of the lamp source. 5. Set **Range** to `12` to decrease the size of the area that the spotlight illuminates so that it touches the floor. 6. Enable **Shadows** to let the lamp light to produce shadows and a dramatic effect.![A close up view of the desk lamp with a customized spotlight.](../../../assets/tutorials/enhancing-indoor-environments/Lamp-2.jpg) 3. Insert a point light that mimics the indirect green lighting from the lamp's hood. 1. In the **Explorer** window, hover over the **FillLight** part, then click the ⊕ icon. A contextual menu displays. 2. From the contextual menu, insert a **PointLight**.![A close up view of the desk lamp with a customized spotlight and a default point light.](../../../assets/tutorials/enhancing-indoor-environments/Lamp-3.jpg) 4. Select the new point light, then in the **Properties** window, 1. Set **Range** to `12` to increase the size of the area that the point light illuminates so that it can illuminate the walls behind the desk. 2. Set **Color** to `142, 157, 125` to tint the light to a light moss green hue and indirectly reduce its brightness.![A close up view of the desk lamp with a customized spotlight and point light.](../../../assets/tutorials/enhancing-indoor-environments/Lamp-4.jpg) ### Illuminate the radio screen The final object in the scene that needs to illuminate the space is the classic radio in the middle of the statues on the dresser. Similar to the previous section, when deciding which local light object you want to use, it's important to review how the type and shape of the light source **doesn't** emit light in the real world. For example, neither of the previous local light objects work for this radio because its screen wouldn't emit light in every direction like a `Class.PointLight` object, nor from a single point in one direction like a `Class.SpotLight`. ![A front view of the cabin's radio and statues. The radio includes a point light that illuminates in every direction.](../../../assets/tutorials/enhancing-indoor-environments/Radio-PointLight.jpg)_Radio using a point light_ ![A front view of the cabin's radio and statues. The radio includes a spotlight that illuminates from a single point in one direction.](../../../assets/tutorials/enhancing-indoor-environments/Radio-SpotLight.jpg)_Radio using a spotlight_ Instead, to solve this lighting, the **Lighting Indoors - Complete** sample introduces a `Class.SurfaceLight` object into the radio to shine outward from the entire surface of its screen. `Class.SurfaceLight` objects emit light from a `Class.Attachment` or a face of a `Class.BasePart`, and they include a `Class.SurfaceLight.Face|Face` property that determines which face/axis light emits from. This makes their lighting behavior useful for objects that produce directional light from a near flat surface, such as TV or computer screens, billboards, and fluorescent panels. It's important to note that despite both surface lights and spotlights using a `Face` property to determine which cubical surface to emit light, surface lights are distinct from spotlights because their light emission changes according to the surface size of their parent object. To demonstrate, review the following images to see how the surface light changes how it emits light relative to the size of its parent block part. ![An angled view of the deer with a block part in front of it. The block part includes a spotlight that illuminates only from the center of the part, and it only partially illuminates the deer.](../../../assets/tutorials/enhancing-indoor-environments/Deer-SpotLight.jpg)_The spotlight illuminates the deer from the center of its parent part._ ![An angled view of the deer with a block part in front of it. The block part includes a surface light that illuminates from the face of the part, and it fully illuminates the deer and the nearby wall.](../../../assets/tutorials/enhancing-indoor-environments/Deer-SurfaceLight.jpg)_The surface light illuminates the deer from the entire surface of its parent part._ To recreate the radio surface light in the sample [Lighting Indoors - Complete](https://www.roblox.com/games/17562253150/UCT-Lighting-Indoors-After) place file: 1. Insert a surface light into the screen of the radio. 1. In the **Explorer** window, expand the **Radio_Noise** model, then its child **Radio** model. 2. Hover over the **Radio_Backglow** mesh, then click the ⊕ icon. A contextual menu displays. 3. From the contextual menu, insert a **SurfaceLight**.![A front view of the cabin's radio and statues. The radio includes a surface light, but it doesn't appear to be illuminating anything.](../../../assets/tutorials/enhancing-indoor-environments/Radio-1.jpg) 2. Select the new surface light, then in the **Properties** window, 1. Set **Face** to **Left** to shine the light away from the clock;s surface 2. Set **Brightness** to `2` to slightly increase the intensity of the light. 3. Set **Color** to `146, 255, 251` to tint the light to a teal blue hue. 4. Set **Range** to `4` to decrease the size of the area that the surface light illuminates so that it only touches the desk.![A front view of the cabin's radio and statues. The radio includes a surface light that gently illuminates the table with a teal blue light.](../../../assets/tutorials/enhancing-indoor-environments/Radio-2.jpg) ## Balance light sources Lighting an environment is a balance between your light sources and the camera's perception of their lighting. Even if your light sources have appropriate settings for how they'd illuminate the space in the real world, you may have to adjust and rebalance how the camera perceives that lighting in order to achieve your ideal lighting behavior. For example, the scene in its current state has an intentionally warm glow throughout the cabin, but its colors and shadows appear both dull and desaturated. To solve this problem, the **Lighting Indoors - Complete** sample adjusts its global lighting to increase its richness in the player's camera without losing details or contrast between light and dark hues throughout the 3D space. ### Adjust light exposure Similar to customizing how long a real-life camera's lens stays open for a picture, the `Class.Lighting` service's `Class.Lighting.ExposureCompensation|ExposureCompensation` property allows you to adjust how much light reaches the camera. Using this property is unique from adjusting the `Class.Lighting.Brightness|Brightness` property because `Class.Lighting.ExposureCompensation|ExposureCompensation` applies a bias toward increasing the effect of brighter parts of the environment. For example, examine the following images to see how the lighting changes depending on if you brighten the cabin by either increasing your global lighting's `Class.Lighting.Brightness|Brightness` or `Class.Lighting.ExposureCompensation|ExposureCompensation` property. The cabin with a higher `Class.Lighting.Brightness|Brightness` property increases all brightness of the space, including brightness within the shadows, which leads to an unintentional murkiness in the cabin. Conversely, the cabin with a higher `Class.Lighting.ExposureCompensation|ExposureCompensation` property doesn't increase the brightness of light and shadows equally, allowing the space to appear brighter without completely washing out its darker colors. ![An angled view of a globe. The lighting displays at the current step in the tutorial.](../../../assets/tutorials/enhancing-indoor-environments/Exposure-NoChange.jpg)_Cabin as-is_ ![An angled view of a globe. The lighting is dull and washed out.](../../../assets/tutorials/enhancing-indoor-environments/Exposure-Brightness.jpg)_Cabin with a higher brightness_ ![An angled view of a globe. The lighting is richer and shadows are more apparent.](../../../assets/tutorials/enhancing-indoor-environments/Exposure-ExposureCompensation.jpg)_Cabin with a higher exposure_ To recreate the exposure levels in the sample [Lighting Indoors - Complete](https://www.roblox.com/games/17562253150/UCT-Lighting-Indoors-After) place file: 1. In the **Explorer** window, select **Lighting**. 2. In the **Properties** window, set **ExposureCompensation** to `0.5` to apply extra exposure to the scene.![A full view of the cabin with a higher exposure level.](../../../assets/tutorials/enhancing-indoor-environments/Exposure-2.jpg) ### Adjust color contrast Similar to applying a filter over a camera, you can add a `Class.ColorCorrectionEffect` post-processing object to the `Class.Lighting` service to adjust how the camera perceives color. This is useful when you want to make color adjustments that impact the entire environment instead of just a single object or gameplay area. The **Lighting Indoors - Complete** sample uses `Class.ColorCorrectionEffect` to increase the vibrancy and contrast between all dark and light colors. This is useful for creating a warm, saturated space that's inviting to players. To recreate how the camera perceives color in the sample [Lighting Indoors - Complete](https://www.roblox.com/games/17562253150/UCT-Lighting-Indoors-After) place file: 1. In the **Explorer** window, hover over the **Lighting** service, then click the ⊕ icon. A contextual menu displays. 2. From the contextual menu, insert a **ColorCorrectionEffect**. 3. Select the new post-processing effect, then in the **Properties** window, 1. Set **Contrast** to `0.05` to increase the contrast between dark and light colors. 2. Set **Saturation** to `0.1` to make all colors more vivid.![A full view of the cabin with more color contrast.](../../../assets/tutorials/enhancing-indoor-environments/Contrast-2.jpg) --- title: "Enhance outdoor environments with realistic lighting" url: /docs/en-us/tutorials/use-case-tutorials/lighting/enhance-outdoor-environments last_updated: 2026-06-29T19:34:14Z description: "Explains how to leverage realistic lighting to enhance outdoor environments." --- # Enhance outdoor environments with realistic lighting `Enum.LightingStyle|Realistic` lighting is the most advanced and powerful `Class.Lighting.LightingStyle|LightingStyle` you can use for rendering the 3D environment within your experiences. It offers pixel perfect light emission, detailed shadows, and specular highlights that mimic real-world lighting for both indoor and outdoor spaces. Using the [Lighting Outdoors - Start](https://www.roblox.com/games/17835285085/Lighting-Outdoors-Start) `.rbxl` file as a starting place and [Lighting Outdoors - Complete](https://www.roblox.com/games/17835194683/Lighting-Outdoors-Complete) as a reference, this tutorial shows you how to utilize realistic lighting with strategic global and local light source configurations to produce realistic, immersive outdoor lighting behavior for an evening campfire scene, including guidance on: - Ensuring metallic surfaces produce accurate reflections as light sources continuously shift in the environment, such as dynamic movement from the roaring campfire. - Moving the sun to a new position that's realistic for the real world's time of day. - Customizing the atmosphere's layered hues, density, and haze. - Configuring point source local lighting to impact how it interacts with the overall environment. If at any point you become stuck in the process, you can use **Lighting Outdoors - Complete** as a reference to compare your progress. ![The starting outdoor environment you can use to complete this tutorial.](../../../assets/tutorials/enhancing-outdoor-environments/Intro-Before.png)_Lighting Outdoors - Start_ ![The complete outdoor environment with global and local lighting you will create by the end of this tutorial.](../../../assets/tutorials/enhancing-outdoor-environments/Intro-After.png)_Lighting Outdoors - Complete_ ## Configure global lighting Global lighting is the luminescence from either the sun or moon in an experience. By adjusting a couple of key default properties in the `Class.Lighting` service, you can dramatically change how that light appears to players, as well as how it interacts with any other object you place in the experience. ### Enable realistic lighting The `Class.Lighting.LightingStyle|LightingStyle` property determines the behavior of both global and local lighting in your experience. Studio begins every experience with `Enum.LightingStyle|Soft` lighting which renders a flatter look with softer lights and shadows. However, to enhance the environment and equip your local light sources to also produce precise shadows and illumination, such as the light from the campfire, you must enable `Enum.LightingStyle|Realistic` lighting. To demonstrate this concept, see the following two images of the same campfire with different lighting styles. The local lighting from the campfire with `Enum.LightingStyle|Soft` lighting doesn't produce shadows in the same way that the global lighting from the sun does, making this area of the environment unevenly lit with unrealistic shadows. By contrast, the local lighting from the campfire with `Enum.LightingStyle|Realistic` lighting interacts with the kindling, rocks, and brush around the environment, producing crisp and realistic shadows for evening time. _`Enum.LightingStyle|Soft` lighting style_ _`Enum.LightingStyle|Realistic` lighting style_ To enable realistic lighting: 1. In the **Explorer** window, select **Lighting**. 2. In the **Properties** window, click the **LightingStyle** dropdown, then select **Realistic**. ### Elevate metal reflections By default, all materials use physically-based rendering (PBR) textures that allow you to display realistic surfaces in various lighting scenarios by using multiple image files on a single object. This means that when you use Studio's built-in materials, the metalness and roughness of a particular surface is already defined for you, and the objects with those materials naturally react more accurately to the lighting in your environment with realistic reflections. You can enhance this effect by setting the `Class.Lighting.EnvironmentDiffuseScale` and `Class.Lighting.EnvironmentSpecularScale` properties to `1` to truly take advantage of metal reflections from `Enum.LightingStyle.Realistic` lighting. This step is important because it ensures that any PBR textures in your experience, including those from `Class.MaterialVariant|MaterialVariants` or `Class.SurfaceAppearance` objects, look their best and reflect their surroundings better. For example, examine the following two images of the same pan and utensils near the campfire with different `Class.Lighting.EnvironmentDiffuseScale` and `Class.Lighting.EnvironmentSpecularScale` property values. When you adjust these values, the metal becomes more apparent and reflects the lighting from both the global and local light sources significantly more than before. _`Class.Lighting.EnvironmentDiffuseScale` and `Class.Lighting.EnvironmentSpecularScale` = `0`_ _`Class.Lighting.EnvironmentDiffuseScale` and `Class.Lighting.EnvironmentSpecularScale` = `1`_ To elevate metal reflections: 1. In the **Explorer** window, select **Lighting**. 2. In the **Properties** window, set **EnvironmentalDiffuseScale** and **EnvironmentSpecularScale** to `1`. The metal in the experience becomes more reflective. ### Change the time of day Now that your experience is using the `Enum.LightingStyle.Realistic` lighting style and materials are reacting realistically to the light sources in your experience, it's time to move the sun to a different position according to where it would be in the real world for the time of day. The sun's default position is high in the sky, emulating around midday in the real world, so it's best to move it nearer to the skyline, right above the mountains. This step also allows the light to move down the path onto the campfire and achieve a nice golden sun. _The default sun is high in the sky. While this placement is great if the campfire was happening around noon, it's not realistic for the evening._ _The new sun position is much more appropriate for the time of day right before sunset._ To change the time of day: 1. In the **Explorer** window, select **Lighting**. 2. In the **Properties** window, set **ClockTime** to `17`. The sun moves to the approximate position it would be in at 5pm. ### Adjust the color of ambient light There are two `Class.Lighting` properties that control the color of ambient lighting: - `Class.Lighting.OutdoorAmbient` controls ambient lighting where the sky is visible. - `Class.Lighting.Ambient` controls ambient lighting within spaces where anything blocks the sky, such as indoor spaces or under tree cover. By default, these properties are set to produce gray ambient lighting, but to compliment the evening sky, you must adjust these values to add a realistic hue and brightness in darker spaces of the experience for evening time. For example, an evening sky has a lot more purple than gray, so picking a purple hue for ambient lighting creates a realistic environment. _`Class.Lighting.OutdoorAmbient` and `Class.Lighting.Ambient` = `70, 70, 70`_ _`Class.Lighting.OutdoorAmbient` and `Class.Lighting.Ambient` = `156, 136, 176`_ To adjust the color of ambient lighting: 1. In the **Explorer** window, select **Lighting**. 2. In the **Properties** window, set **Outdoor Ambient** and **Ambient** to `156, 136, 176`. The ambient lighting changes to a light purple hue. ### Choose a skybox A skybox is a cube made up of six individual images that create an experience's sky, including what's above and below the horizon. Skyboxes can have a major impact on the look and feel of what's in your environment, so it's important to carefully consider how you can choose a skybox that enhances your experience's visual quality. For example: - A skybox's lower hemisphere should be similar to the color of your general terrain. This ensures the lower hemisphere closely relates to the ground surface, and that the colors that reflect off of objects will roughly match the skybox. - A skybox's lower hemisphere should be darker than the upper hemisphere because a darker lower hemisphere replicates the natural occlusion of light from below the ground, making your lighting more immersive. - A skybox doesn't require clouds, because you can easily add in [dynamic clouds](/docs/en-us/environment/clouds.md) to achieve the same effect and supplement your skybox. To illustrate these concepts, examine the following two images to see how the same chrome sphere reflects two different skyboxes. The first skybox has the same level of brightness for both the upper and lower hemispheres, so it doesn't seem like the sphere is reflecting the world around it well. By contrast, the second skybox has a darker lower hemisphere from its upper hemisphere, achieving a more natural look. For information on how to create and customize skyboxes, see [Skyboxes](/docs/en-us/environment/skybox.md). ### Atmospheric effects The `Class.Lighting` service has a child `Class.Atmosphere` object with properties that allow you to simulate realistic environments by scattering sunlight in unique ways. These properties can be very useful in creating a thickness in the experience's air, giving the environment a tangible sense of depth. The `Class.Atmosphere` object pulls most of its colors from the skybox directly, which is why the previous decisions about your skybox were so important. #### Increase air particle density The `Class.Atmosphere.Density` property controls how many particles exist in the air of your experience. When you increase this property, the additional amount of particles obstruct the player's view of objects in the background. For example, when `Class.Atmosphere.Density` is `0`, the background trees, sun, and skybox are clearly visible, but when you increase this property to `0.391`, the particles start to scatter the light and conceal the trees. _`Class.Atmosphere.Density` = `0`_ _`Class.Atmosphere.Density` = `0.391`_ To increase density of the air particles in the atmosphere: 1. In the **Explorer** window, select **Atmosphere**. 2. In the **Properties** window, set **Density** to `0.272`. #### Add a haze The `Class.Atmosphere.Haze` property controls the overall haziness of the atmosphere to create a visible effect both above the horizon and far into the distance from the camera. When you increase this property, it not only affects the overall environment, but it also affects objects that have a particularly powerful fresnel effect, such as metal objects that reflect the environment around them. To add haze to the atmosphere: 1. In the **Explorer** window, select **Atmosphere**. 2. In the **Properties** window, set **Haze** to `1`. #### Adjust the color of the atmosphere The `Class.Atmosphere.Color` property sets the hue of the atmosphere for subtle environmental moods and themes, and it can really enhance the haze within your experience. You can set this to any color you want to suit your experience, but it's recommended to set it to a color value that is close to the average of the objects in the environment. To adjust the color of the atmosphere: 1. In the **Explorer** window, select **Atmosphere**. 2. In the **Properties** window, set **Color** to `85, 78, 54`. ## Configure local lighting Local lighting is the luminescence from local [light sources](/docs/en-us/effects/light-sources.md) in your experience, such as `Class.SpotLight`, `Class.SurfaceLight`, and `Class.PointLight` objects. The key local light source you can create for this experience is the campfire's glow, and by adjusting a couple of its default properties, you can significantly alter how this local lighting interacts with the overall environment and complement your global lighting configuration. ### Add a PointLight Unlike `Class.SpotLight` or `Class.SurfaceLight` objects that only project light from one direction, `Class.PointLight` objects allow you to project omnidirectional lighting. This means that when you add a `Class.PointLight` to your campfire mesh, it projects in all directions outward from its source, similar to a real-life campfire, and it illuminates all surrounding objects in shadows and allows players to see the roughness of their surfaces much clearer. _The scene without a local light source_ _The same scene with a local light source_ To add a `Class.PointLight` to the campfire: 1. In the **Explorer** window, hover over **FireLight** and click the **⊕** button. A contextual menu displays. 2. From the menu, select **PointLight**. The `Class.PointLight` object displays as a child of the campfire mesh. ### Increase the range of the PointLight The default properties of the `Class.PointLight` aren't enough to fully brighten the objects surrounding the campfire, so you need to increase the range that the light can reach. Because the fire is large and bright, the light needs to cast far enough to illuminate the nearby trees, rocks, and brush. This also helps to make the space feel warm and cozy, as though the heat of the fire is naturally expanding outward. To increase the range of the `Class.PointLight`: 1. In the **Explorer** window, select the campfire's **PointLight**. 2. In the **Properties** window, set **Range** to `48`. The light's maximum lighting range expands. ### Enable shadows While the lighting's range is realistic to its size, it's unrealistic that the surrounding trees and rocks don't cast shadows from the campfire's light. Sometimes this is useful if you need to add in a couple of point lights to brighten dark spaces within your experience, but when you're aiming to emulate the real world, you can enable local lighting's ability to cast shadows. It's important to note that additional shadows can impact your experience's performance on low-end devices, so only enable shadows when they significantly add to the scene. To enable shadows from the campfire's local lighting: 1. In the **Explorer** window, select the campfire's **PointLight**. 2. In the **Properties** window, enable **Shadows**. ### Adjust the lighting's brightness and color While the local lighting is already looking and feeling closer to realistic behavior, it's still weak in strength and too white for a warm glow. When you increase the campfire's brightness and add a warmer hue, it really brings life to the fire and adds to the coziness of the scene. To enable shadows from the campfire's local lighting: 1. In the **Explorer** window, select the campfire's **PointLight**. 2. In the **Properties** window, 1. Set **Brightness** to `2`. 2. Set **Color** to `255, 179, 73`. You now have a campfire scene that is complete and welcoming for players to relax. Using the skills in this tutorial, you can combine the realistic lighting style with the PBR materials available to create rich and immersive experiences. It only takes setting up the correct properties and making decisions about these features that suit your environment. --- title: "Light with props" url: /docs/en-us/tutorials/use-case-tutorials/lighting/light-with-props last_updated: 2026-06-29T19:34:14Z description: "The process for creating light sources out of props." --- # Light with props While overall world lighting is globally controlled through the `Class.Lighting` service, experiences can also include in-game **light sources** like lamps, torches, spotlights, TV screens, etc. ![alt](../../../assets/tutorials/lighting-with-props/Misty-Harbor-Banner.jpg) ## Starter project The remainder of this tutorial uses the [Misty Harbor](https://www.roblox.com/games/6445909934/Misty-Harbor) project as a showcase. To follow along, open it in Studio before proceeding. ![Edit in Studio option from the experience's main page](../../../assets/tutorials/lighting-with-props/Misty-Harbor-Edit-Place.png) ## Point lights A `Class.PointLight` emits light **spherically** from a single point. This object is ideal for non-directional light sources like bulbs, torches, and fireballs. Light sources like point lights need to be inserted directly into parts, meshes, or attachments. Here, a point light will be inserted into an existing model within the darkened city. 1. Find and select a **LightPole** model like the one shown below, near the central fountain.![alt](../../../assets/tutorials/lighting-with-props/LightPole-Select-Model.jpg) 2. In the **Explorer** window, expand the model's tree and select the **LightCasing** part. 3. Change its **Material** property to **Neon** — this will make the part appear to glow brightly, although it will not actually emit any light. 4. With **LightCasing** still selected, insert a new **PointLight** object. 5. With **PointLight** selected, customize its **Brightness** and **Color** values._Brightness = 4__Color = 255, 175, 100_ 6. Further customize the light by adjusting its **Range** property. This value defines the radial distance of illumination from the light's position, measured in studs._Range = 8__Range = 12_ ## Spot lights A `Class.SpotLight` emits light in the shape of a **cone**. This object is ideal for light sources like street lamps, flashlights, and headlights. 1. Select the taller arched **StreetLight** model next to the construction across the street. Currently the light is pointed out instead of down at the street.![alt](../../../assets/tutorials/lighting-with-props/StreetLight-Select-Model.jpg) 2. Expand the model's tree to reveal the **SpotLight** object. 3. With **SpotLight** selected, test out different values for its **Face** property to change which face/axis light is emitted from._Face = Left__Face = Bottom_ 4. Adjust the **Angle** property to increase or decrease the spread of light emission. The maximum value is 180 which illuminates a full half sphere from the cone's apex._Angle = 30__Angle = 75_ 5. Experiment with different **Brightness** and **Color** values, as with the point light. ## Surface lights A `Class.SurfaceLight` emits light from the entire surface/face of a part, rather than just from a single point. This object is useful for light sources like TV or computer screens, lighted billboards, and fluorescent panels. 1. Find and select the lighted **RobloxBillboard** sign on top of the café.![alt](../../../assets/tutorials/lighting-with-props/Billboard-Select-Model.jpg) 2. Expand the model's tree to reveal the SurfaceLight object. 3. With **SurfaceLight** selected, test out different **Face** values to change which face/surface light is emitted from. Notice that light is emitted from the **entire surface**, not a point upon it._Face = Bottom__Face = Right_ 4. Adjust the **Angle** property to increase or decrease the spread of light emission from the part's surface. An angle of 0 means that light travels directly outward from the surface while an angle of 180 means light travels outward perpendicular to the surface._Angle = 60__Angle = 0_ By using lighting props instead of just general world lighting, you open up the possibility of creating new genres and environments like cyberpunk cities, traditional light festivals, or moody detective scenes. --- title: "Assemble modular environments" url: /docs/en-us/tutorials/use-case-tutorials/modeling/assemble-modular-environments last_updated: 2026-06-29T19:34:14Z description: "Explains how to configure and assemble reusable assets that seamlessly snap together." --- # Assemble modular environments A **modular environment** is a 3D space made up of reusable assets that seamlessly snap together in numerous configurations to create variations of a larger complex object. For example, Studio's [Modern City template](https://www.roblox.com/games/13165709401/Modern-City) is a modular environment that utilizes reusable walls, windows, and doors from both a [modular building kit](https://create.roblox.com/store/asset/13168370735/Modular-Building-Kit-Modern-City) and [materials pack](https://create.roblox.com/store/asset/13168345645/Modern-City-Materials-Pack) to create building variations that comprise an entire downtown area. Assembling modular environments is useful because it means you don't need to manually create each individual asset in your experience. Instead, you only need to create a few assets that you can reuse and customize to create variety throughout the scene. Not only does this process significantly speed up how quickly you can build large environments, but it can also help each distinct object feel cohesive within your experience. _Sample Modular Building Kit_ _Studio's Modern City Template_ Using the same modular building kit pieces that built Studio's Modern City template, this guide walks you through the importance of consistent pivot point locations in order for modular assets to align and snap together, shows you how to combine modular assets to create a realistic building, and demonstrates how to customize modular assets to create variety within your environment. ## The importance of consistent pivot point locations Every object in Studio moves and rotates according to the location of its pivot point. By default, parts and meshes start with a pivot point location in the center of the object, so when you move them, they move from the center of the object outwards. The default pivot point location is useful for having symmetrical scaling and rotation, but it becomes problematic when you want to snap together objects of varying shapes in a predictable and uniform way. For example, the following two parts both have a size value of [10, 10, 1] and keep their default pivot point locations. If you move the yellow part in increments of 5 studs on the X axis, it moves from its center to its edge, and is easily able to align and snap to either side of the blue part. However, if you rotate the yellow part and continue to move it in increments of 5 studs on the X axis, it cannot align and snap to either side of the blue part. This is because the width of the yellow part is now one stud, so when it moves 5 studs from its center, it moves 0.5 studs from the center + 4.5 studs on the world grid, resulting in 0.5 studs of overlap on either side of the blue part. If you want to align them again, you could change the stud increment movement value or manually adjust the position until they're aligned, but this process quickly becomes tedious and unsustainable, especially when you need to apply it for many objects of differing sizes, unique pivot point locations, or complex geometry, such as meshes you create in third-party modeling tools. This is why having a consistent pivot location for modular assets is of vital importance so they can connect together at predetermined, incremental distances relative to one another when you enable grid snapping. For example, each mesh in the Modern City sample modular building kit has a minimum length of 7.5 studs and a maximum length that's divisible by 7.5 studs so every mesh can seamlessly align and connect without overlap even when you rotate them. Modular assets can consist of a nearly infinite variety of different shapes, sizes, and silhouettes and embody various purposes such as walls, corners, or decorative additions, so where that consistent pivot should be is different for each modular building kit. For the Modern City sample modular building kit, every mesh has a consistent pivot location either at the forward-most, lower corner, or in a location that allows them to snap to a logical position on the building, such as balconies, cornices, and awnings. For information on how to configure pivot locations for parts and models you create within Studio, see [Pivot Tools](/docs/en-us/studio/pivot-tools.md#edit-pivot). For information on how to configure pivot locations for meshes you create in third-party modeling tools, see the pivot point documentation for [Blender](https://docs.blender.org/manual/en/2.80/scene_layout/object/editing/transform/control/pivot_point/index.html) or [Maya](https://knowledge.autodesk.com/support/maya/learn-explore/caas/CloudHelp/cloudhelp/2022/ENU/Maya-Basics/files/GUID-150B390E-840B-4FE3-B8E9-8DEBCE7CEC97-htm.html). ## Configure snapping behavior Studio's default settings allow you to freely move and rotate objects to any fraction of a stud or degree, respectively. However, because modular building kits rely on assets snapping together at exact increments, you must change these default settings **according to the requirements of each modular building kit**. For example, the Modern City sample [modular building kit](https://create.roblox.com/store/asset/13168370735/Modular-Building-Kit-Modern-City) requires each mesh to rotate in 7.5 stud and 45 degree increments with collisions off. This ensures that as you move and rotate meshes around the 3D environment, they can connect and maintain clear alignment to one another as the building grows. To configure Studio settings for ideal snapping behavior for the sample modular building kit, look in the toolbar and: 1. Disable **Collisions**. 2. Enable **Rotate** snapping and set it to `45`. 3. Enable **Move** snapping and set it to `7.5`. ## Combine modular assets When you start to assemble modular assets into a larger complex object, it's helpful to choose one core object whose position coordinates you can reference throughout the entire process. This ensures that each additional object quickly aligns to the right pivot location before moving them to a different position. To snap modular assets together to create a building using the sample modular building kit: 1. Select one of the bottom modular meshes. This example uses a door. 2. In the viewport, position this mesh in a location within your experience where you want to create a building. 3. In the **Properties** window, copy the **Position** coordinates. 4. In the viewport, select a single mesh you want to connect to your first mesh. 5. In the **Properties** window, paste the **Position** coordinates from the first mesh. The second mesh displays on top of the first mesh. 6. In the toolbar, select the **Move** tool, then move the second mesh to a new location on either side of the first mesh. It snaps in 7.5 increments along the same axis. > **Warning:** The process of combining modular meshes requires you to only move one mesh at a time. If you select multiple meshes at once, Studio averages both of their pivot point locations together, effectively removing the intentional consistent pivot point locations the meshes need in order to align and snap together. 7. Repeat this process for every mesh you want to comprise your building, continuously pasting the first mesh's position coordinates for each additional mesh to ensure the starting pivot location remains the same for each mesh of the building. ## Customize modular assets To ensure every object looks and feels unique from one another, it's important to customize your modular assets to create visual variety. While the following sections explore customization strategies for the sample modular building kit, such as using alternate materials for the walls and trims, experimenting with different hues for these adornments, removing repetition for materials that tile on large surfaces, and dressing objects with decorative props, you can utilize similar methods for any modular building kit you use to create modular environments. ### Use custom materials Every mesh within the sample modular building kit uses a [custom material](/docs/en-us/parts/materials.md#custom-materials) from the sample [materials pack](https://create.roblox.com/store/asset/13168345645/Modern-City-Materials-Pack) that you can use to alter the appearance and physical qualities of your buildings' walls. For example, by default, all walls start with a **PaintedBrick** custom material, but you can change it to various forms of alternative brick, concrete, and plaster, then set a distinct tint color to the material in order to give each building a unique appearance. To use alternative custom materials for wall meshes: 1. In the viewport, hold down `Alt``Shift` (`⌥``Shift`) then click on any mesh of your building with a brick material. 2. In the **Properties** window, click the **MaterialVariant** dropdown menu. All alternative custom materials display. 3. Select an alternative material variant. All active wall meshes update their material in the viewport. 4. **(Optional)** To further customize the new custom material, select a new tint color. 1. In the **Properties** window, click either **BrickColor** or **Color**. The hexagon map or colors popup display, respectively. 2. Select a new color. All active wall meshes update their tint color in the viewport. ### Use SurfaceAppearance objects Every mesh with a trim in the sample modular building kit includes a `Class.SurfaceAppearance` object that utilizes custom textures to make each building's adornments feel more realistic. To make each building in your experience feel unique, you can swap any `Class.SurfaceAppearance` object for another in the **SurfaceAppearance** folder in the workspace, including materials like concrete, plaster, and wood, then set a distinct tint color to the trim for additional variety. To swap `Class.SurfaceAppearance` objects for trim meshes: 1. In the viewport, select a mesh with a trim. 2. In the **Explorer** window, expand the parent model, then the child mesh until the `Class.SurfaceAppearance` object is visible. 3. Delete the `Class.SurfaceAppearance` object. The mesh's visual appearance updates in the viewport. 4. In the **SurfaceAppearance** folder, copy an alternative `Class.SurfaceAppearance` object you want to use for the trim. 5. Navigate back to the trim model, then paste the new `Class.SurfaceAppearance` object into the child mesh. The mesh's visual appearance updates in the viewport. 6. Repeat this process for each trim mesh on your building. 7. **(Optional)** To further customize the new `Class.SurfaceAppearance` object, select a new tint color. 1. In the viewport, hold down `Alt``Shift` (`⌥``Shift`) then click on any mesh of your building with a trim. 2. In the **Properties** window, click either **BrickColor** or **Color**. The hexagon map or colors popup display, respectively. 3. Select a new color. All active trim meshes update their tint color in the viewport. ### Reduce visible repetition When you're building large objects that utilize tiling materials like bricks and concrete, the tiling pattern can rapidly reveal visible repetition. To help reduce the visible tiling of these materials, you can create a grunge decal or texture, then add it as a child of your wall meshes to overlay on top of the wall's active materials. This customization strategy has the benefit of adding additional realism to your building, quickly improving the quality of your 3D environment. For information on how to apply and customize textures, see [Textures and decals](/docs/en-us/parts/textures-decals.md). _Wall without a grunge texture overlay_ _Wall with a grunge texture overlay_ ### Add decorative props As a final step in making objects feel distinct in their larger modular environment, you can add a significant amount of character to each object by dressing them with decorative props. For example, the sample modular building kit includes assets like fire escapes, window balconies, air conditioner units, and foliage that you can experiment with to flourish each building and make it feel lived in. Even just including a few decorative props can add a vast amount of storytelling to your scene. --- title: "Create neon signs" url: /docs/en-us/tutorials/use-case-tutorials/modeling/create-neon-signs last_updated: 2026-06-29T19:34:15Z description: "Explains how to create neon signage to draw attention to something in an experience." --- # Create neon signs **Neon signs** are bright and colorful electric signage that draws attention to something within an experience, such as where users can enter a building or purchase an item. By combining modeling and lighting techniques, you can create and use neon signs to elevate the immersion within your environments. As with all 3D creation, there are many ways to achieve any particular goal. In this guide, you can quickly create your own neon sign using tools and methods available only within Studio with only a few basic assets, including a [`.obj`](../../../assets/tutorials/creating-neon-signs/open-text.obj) file for the 3D text of the sign model. In the following method to create a neon sign, follow each section to learn how to: - Create the signage's backboard and border with basic parts. - Shape the sign using Studio's solid modeling tools. - Incorporate 3D text with your sign and save the sign as a model. > **Info:** You can create your own assets in third-party modeling tools and follow along with your own design. For information on exporting models for use in Studio, see [Exporting Requirements](/docs/en-us/art/modeling/export-requirements.md). ## Create the backboard and border A `Class.Part` is Roblox's primary building block that you can move, resize, rotate, and customize to change their appearance, such as their color and material. Using [basic parts](/docs/en-us/parts.md) to create the foundation of neon signs is useful because the signage's backboard and border only require basic shapes. To create the backboard and border: 1. Navigate to Studio's **Home** or **Model** tab toolbar. 2. Click the dropdown arrow on the **Part** button and select **Block**. A block part displays in the workspace that's about to become the backboard of your neon sign. 3. In the **Explorer** window, select the block, then in the **Properties** window, 1. Set **BrickColor** to **Black**. 2. Set **Size** to `8,4,1`. 3. Set **Name** to **Backboard**. 4. Enable the **Anchored** property. 4. In the **Explorer** window, select **Backboard**, then press `Ctrl``D` (`⌘``D`) to duplicate the part. 5. In the toolbar, select the **Move** tool and use one of the axis arrows to pull the duplicate backboard part away from the original position so you can see each object. 6. In the **Explorer** window, select the duplicate backboard part that's about to become the border of your neon sign, then in the **Properties** window, 1. Set **BrickColor** to **Lime Green**. 2. Set **Size** to `7.75, 3.75, 0.25`. 3. Set **Name** to **Border**. 7. In the **Explorer** window, select **Border**, then press `Ctrl``D` (`⌘``D`) to duplicate the part. **Do not move this new part** because it needs to be in its current position for the next sculpting step. Now that you have three parts that make up the basic shapes of your neon sign, you can sculpt the border's shape. ## Shape the neon border Using [solid modeling](/docs/en-us/parts/solid-modeling.md), you can join and separate parts in unique ways to form more complex shapes known as **unions**. This process enables you to resize and modify the duplicate border part to become the neon border. To create the neon border shape: 1. In the **Explorer** window, select the duplicate border part, then in the **Properties** window, set the **Size** to `7.5, 3.5, 1.0`. 2. With the duplicate border part still selected, click the **Negate** button in the toolbar. The border part turns translucent and its **Name** property automatically changes to **NegativePart**. 3. With **NegativePart** still selected, hold `Ctrl`/`⌘` and click on the original border part to select both parts at the same time. 4. In the toolbar, select **Union** to fuse both parts together. A border-shaped part displays, and the **Name** property automatically changes to **Union**. 5. In the **Explorer** window, select the union, then in the **Properties** window, 1. Set **Name** to **Border**. This helps organize all objects within the workspace. 2. Set **Material** to **Neon**. This allows the part to glow. 6. In the toolbar, select the **Move** tool and use one of the axis arrows to pull **Border** in front of **Backboard**. Now that you have a complete backboard and a glowing neon border, you can create extruding neon 3D text for the words of the sign. ## Incorporate neon 3D text Since Studio doesn't natively support 3D text, this guide provides an open-text.obj file for you to import into your scene that contains a 3D model of text that spells the word "OPEN". You can also use other methods to create 3D texts or custom designs for this process, such as using your own models from third-part modeling software, working with community plugins, or creating your own text manually in Studio through solid modeling. To incorporate neon 3D text from the [open-text](../../../assets/tutorials/creating-neon-signs/open-text.obj) `.obj` file: 1. Import the **open-text** `.obj` file. 1. From Studio's **File** menu, select **Import**. A file browser displays. 2. Select the **open-text** `.obj` file, then click the **Open** button. The **Import Preview** window displays. 3. Keep the default import settings, then click the **Import** button. The open text models display within the viewport. 2. In the toolbar, select the Move tool and use one of the axis arrows to center the text on the sign. 3. In the **Properties** window, 1. Set **Color** to `170,0,0`. 2. Set **Material** to **Neon**. 4. In the **Explorer** window, select the text models, **Border**, and **Backboard**, then press `Ctrl``G` (`⌘``G`) to group them into a single `Class.Model` object. > **Info:** For a reference of what this neon sign looks like within Studio, you can download the [base project file](../../../assets/tutorials/creating-neon-signs/neon-sign-baseplate.rbxl) and compare it to your model. 5. Rename the new model to **NeonSign**. 6. In the **Explorer** window, right-click **NeonSign**. A contextual menu displays. 7. Select **Save to Roblox**. After you save the asset in your [Toolbox](/docs/en-us/projects/assets/toolbox.md), you can use it within any of your experiences. In addition, you can [distribute your asset to the Creator Store](/docs/en-us/production/creator-store.md) to make it publicly available to all creators to use within their experiences too. --- title: "Build a ferris wheel" url: /docs/en-us/tutorials/use-case-tutorials/physics/build-a-ferris-wheel last_updated: 2026-06-29T19:34:15Z description: "The process for creating a ferris wheel using a motor." --- # Build a ferris wheel Many contraptions in Roblox will use multiple constraints to build more complicated mechanisms. In particular, you can configure several constraints to be **actuated**, meaning they will move under their own power. This tutorial will show you how to actuate a `Class.HingeConstraint` to be a **motor** in order to make a ferris wheel. ## Ferris wheel setup 1. Add a ferris wheel into a place using either [this model](https://www.roblox.com/library/6448931648/FerrisWheel) or this [pre-built place](https://www.roblox.com/games/6448937909/Ferris-Wheel).![Edit in Studio option from the experience's main page](../../../assets/tutorials/building-a-ferris-wheel/Ferris-Wheel-Edit-Place.png) 2. To view constraints and attachments, toggle on **Show Constraint Details** from Studio's **View** menu. ## Add attachments You will need to add attachments to the ferris wheel to determine where it will rotate. When working with attachments, it helps to move the pieces you are working with apart so you can clearly see the position and orientation of the attachments. 1. In the **Explorer**, expand **FerrisWheel**, select the **MainSupport** model, and move it so you can see the side of the wheel axle as well as the side of the support axle.![alt](../../../assets/tutorials/building-a-ferris-wheel/makingAFerrisWheel_moveMainSupport.jpg) 2. Expand MainSupport and select **SupportAxle**. Insert an attachment and rename it **SupportAttachment**. 3. Move **SupportAttachment** so that it is on the inside edge of the **SupportAxle**.![alt](../../../assets/tutorials/building-a-ferris-wheel/makingAFerrisWheel_movedSupportAttachment.jpg) 4. In the FerrisWheel, select **WheelAxle** and add a new attachment named **WheelAttachment**. 5. Move the **WheelAttachment** to the edge of the axle. Make sure this is the side facing the support where you placed the **SupportAttachment**.![alt](../../../assets/tutorials/building-a-ferris-wheel/makingAFerrisWheel_movedWheelAttachment.jpg) 6. If you hover over the attachments, you'll see yellow and orange arrows appear. Make sure the yellow arrows for both attachments are pointing in the same direction. If they aren't, use the **Rotate** tool to make sure they are pointed the same way.![alt](../../../assets/tutorials/building-a-ferris-wheel/makingAFerrisWheel_rotateWheelAttachment.jpg) ## Create a HingeConstraint Now that both attachments are in place, it's time to add a `Class.HingeConstraint` to act as the motor for the wheel. 1. In the SupportAxle, create a new **HingeConstraint** and name it **MainMotor**. 2. In the properties of **MainMotor**, set Attachment0 to SupportAttachment, and Attachment1 to WheelAttachment. 3. Select the **MainSupport** model and return it to its original position.![alt](../../../assets/tutorials/building-a-ferris-wheel/makingAFerrisWheel_originalPosition.jpg) ## Change to motor By default, `Class.HingeConstraint|HingeConstraints` will only turn if an outside force acts on them, such as a user character pushing in the connected parts. To make a `Class.HingeConstraint` turn on its own, we have to configure it to be a **Motor**, set our desired turn rate, and make sure the hinge has enough torque. 1. Select **MainMotor** and, in the properties, change **ActuatorType** to **Motor**. 2. Change **AngularVelocity** to 0.314. > **Info:** The **AngularVelocity** property uses **radians per second** to set how fast its motor turns. Radians are a unit used to measure angles. Most radian values are based on pi, which is approximately 3.14. If you want to precisely configure how quickly or slowly your hinge turns, trying some values related to pi is a good starting point. - 1 revolution per second = 2 * pi = 6.28 - ½ revolution per second = pi = 3.14 - ¼ revolution per second = pi / 2 = 1.57 - 1/10 revolution per second = pi / 10 = .314 3. Copy the `inf` value from **MotorMaxAcceleration** to **MotorMaxTorque** so that the wheel can handle any amount of weight. 4. [Initiate a playtest](/docs/en-us/studio/testing-modes.md#playtesting) to test your wheel turning behavior. Notice that you only need the motor on one side of the wheel; you do not need motors on both sides. When building with contraptions, try using as few constraints as possible. This ensures that your contraptions are stable and reliable. Now that you have the ferris wheel fully built, try experimenting with more constraints. You can add more cars to the ferris wheel, or you can try building an original contraption. --- title: "Build a hinged door" url: /docs/en-us/tutorials/use-case-tutorials/physics/build-a-hinged-door last_updated: 2026-06-29T19:34:15Z description: "The process for creating a door using a HingeConstraint." --- # Build a hinged door Roblox's physics system allows you to construct moving mechanisms like doors, rotating platforms, and even vehicles using **constraints**. For instance, a swinging door can be built using the `Class.HingeConstraint`. ## Door setup Start by creating parts for the door and its attachments. **Attachments** are where one object can connect to another. These attachments will later be used to connect the door to its frame with a hinge. 1. Create two parts with names like **Door** and **DoorFrame**.![alt](../../../assets/tutorials/building-a-hinged-door/introToConstraints-showDoorCreated.jpg) 2. Select **DoorFrame**. In the **Properties** window, enable **Anchored** so it won't move.![alt](../../../assets/tutorials/building-a-hinged-door/introToConstraints_selectAnchored.png) 3. In the **Explorer**, hover over **DoorFrame** and add a new **Attachment**. Repeat the same to add an attachment to the **Door**.![alt](../../../assets/tutorials/building-a-hinged-door/introToConstraints_attachmentsCreated.png) 4. Rename the attachments to indicate what they're attached to, such as **DoorAttachment** and **FrameAttachment**.![alt](../../../assets/tutorials/building-a-hinged-door/introToConstraints_attachmentsRenamed.png) ## Move the attachments New attachments are created in the center of a part. So they can work with the door, the two attachments need to be moved to face each other. 1. To view constraints and attachments, toggle on **Show Constraint Details** from Studio's **View** menu. 2. In the **Explorer**, select **FrameAttachment**.![alt](../../../assets/tutorials/building-a-hinged-door/introToConstraints_selectFrameAttachment.png) 3. Press `F` to focus on the attachment and zoom in if needed. Then, use the **Move** tool to position the attachment on the surface of the door frame, facing the door. > **Info:** It's best to position attachments so they're precisely aligned with one another. In this case, misaligned attachments may cause the door to swing incorrectly. > > For precise positioning, use **Snap to Grid** with increments appropriate for the size of the part. Alternatively, edit the attachment's position in the **Properties** window. 4. Repeat the same to move **DoorAttachment**. Your attachments should be positioned on the surface facing their counterpart. _**Left**: FrameAttachment / **Right**: DoorAttachment_ ## Rotate the attachments The orientation of an attachment affects how a constraint can move. For the door, both attachments must be rotated so the hinge swings left and right, like those on a standard door. 1. On the door frame, hover over **FrameAttachment**. Notice the **yellow arrow**. This arrow, the **axis**, determines the hinge's rotation.![alt](../../../assets/tutorials/building-a-hinged-door/introToConstraints_showAttachmentAxis.jpg) > **Info:** Depending on the **DoorFrame** object, the axis may point in a different direction. For instance, a hinge added to the attachment shown above would pivot around the yellow arrow, like in the video below. 2. For accurate rotation, turn on rotation snapping in Studio's toolbar by checking **Rotate** and setting the value to `90`.![alt](../../../assets/tutorials/building-a-hinged-door/introToConstraints_snapRotate.png) 3. Use the **Rotate** tool to orient **both** yellow attachments to point **upwards**. If your axes are already vertical, no action is needed.![alt](../../../assets/tutorials/building-a-hinged-door/introToConstraints_primaryAxis.jpg) ## Add the constraint Remember, constraints are a way of connecting two attachments to move in a specific way. This door will use a `Class.HingeConstraint`, a common constraint that rotates objects along the axes of two attachments. 1. Under **DoorFrame**, create a new **HingeConstraint**.![alt](../../../assets/tutorials/building-a-hinged-door/introToConstraints_showCreateHingeConstraint.png) 2. In the constraint's properties, find **Attachment0**. Click the empty box right of the property and then, in the **Explorer**, click **DoorAttachment**. 3. Repeat the same process by connecting **Attachment1** to **FrameAttachment**. The properties should appear as below.![alt](../../../assets/tutorials/building-a-hinged-door/introToConstraints_attachmentsConnected.png) 4. Test the project by walking into the door with your character. > **Warning:** You may encounter the following issues when testing: > > **Parts not moving:** - Make sure that your door is not anchored. - Make sure that the door's motion isn't blocked by terrain or nearby parts. > > **Door not swinging as expected:** - Ensure the axis of each attachment is pointed up (see [Rotating Attachments](#rotate-the-attachments)). ## Adjust the door The door is currently able to swing past the door frame. This can be fixed by adjusting the hinge **limits**. 1. In the properties for **HingeConstraint**, find and toggle **LimitsEnabled**. When active, you can set the rotation limits of the hinge.![alt](../../../assets/tutorials/building-a-hinged-door/introToConstraints_limitsEnabled.png) 2. To make sure this is oriented correctly, select **DoorAttachment** and use the rotate tool so the orange arrow points **towards** the door frame, like below.![alt](../../../assets/tutorials/building-a-hinged-door/introToConstraints_secondaryAxisArrow.png) > **Warning:** Remember that the yellow axis arrows affect the pivot of the hinge. Limits are affected by the orange axis arrows. 3. Under the **Limits** section of the properties, both LowerAngle and UpperAngle to -90 and 90, respectively. This creates a range of motion like the image on the left. 4. Test the door and notice that the hinge is now limited. > **Warning:** If the door limits don't work as expected, it's possible that the orange axis on an attachment isn't correctly aligned. Select DoorAttachment, and make sure that a green plane with an arc points **towards** the door frame, as in the image below. > > ![alt](../../../assets/tutorials/building-a-hinged-door/introToConstraints_showAxisArc.jpg) With the door complete, take some time to explore using hinge constraints in different situations, such as a trap door or a swinging axe trap. --- title: "Create elevators" url: /docs/en-us/tutorials/use-case-tutorials/physics/create-elevators last_updated: 2026-06-29T19:34:15Z description: "The process for creating moving platforms to move users from one area to another." --- # Create elevators **Elevators** are platforms users can operate to travel from one part of an experience to another. This device is useful when you want to allow users to reach new areas within the worlds you create. In this guide, you can quickly create your own elevator that elevates users upwards and downwards when they interact with a proximity prompt. In the following method to create an elevator, follow each section to learn how to: - Using basic parts, create a platform for users to stand on and a track the platform can follow to move upwards and downwards. - Configure a `Class.PrismaticConstraint` to drive the platform's motion. - Create a `Class.ProximityPrompt` for users to interact with to start the platform's motion. - Create a `Class.Script` to connect all of the elevator's components and enable the platform to move along the track. ## Create the platform and track A `Class.Part` is Roblox's primary building block that you can move, resize, rotate, and customize to change their appearance, such as their color and material. Using basic parts to create the foundation of the elevator is useful because the elevator's platform and track only require basic shapes. To create the platform and track of your elevator: 1. Navigate to Studio's **Home** or **Model** tab toolbar. 2. Click the dropdown arrow under **Part** and select **Block**. A block part displays in the workspace that's about to become the platform users ride up and down the elevator. 3. In the **Explorer** window, select the block, then in the **Properties** window, 1. Set **Name** to **Platform**. 2. Set **Size** to **[8,1,4]**. 4. In the **Explorer** window, select the platform, then press `Ctrl``D` (`⌘``D`) to duplicate the part. This duplicate part is about to become the track the platform uses to move up and down. 5. In the toolbar, select the **Move** tool, then use one of the axis arrows to pull the duplicate part away from the original position so there is a small gap between each object. 6. In the **Properties** window, 1. Set **Name** to **Track**. 2. In the **Size** property, set the Y axis to **20** studs tall. 3. Enable the **Anchored** property. 7. In the **Explorer** window, select both parts, then press `Ctrl``G` (`⌘``G`) to group them. 8. Rename the model **Elevator**._In the viewport, the model appears as two separate objects. In the Explorer window, the model contains the separate Platform and Track objects._ ## Configure the PrismaticConstraint Now that you have two parts that make up the foundation of your elevator, you can create a `Class.PrismaticConstraint`, align the associated attachments so the platform moves along an ideal path, and set the constraint's values to enable the platform to move up and down the track. ### Create the PrismaticConstraint and attachments A `Class.PrismaticConstraint` creates a rigid joint between two `Class.Attachment|Attachments`, allowing the attachments to slide along one axis without rotating. This type of [constraint](/docs/en-us/physics/mechanical-constraints.md) is ideal for elevators because it keeps the platform at a single orientation while still being able to move upwards and downwards. To create a PrismaticConstraint and its attachments: 1. In the **Explorer** window, insert a PrismaticConstraint into **Track**. 1. Hover over **Track** and click the **⊕** button. A contextual menu displays. 2. From the menu, insert a **PrismaticConstraint**. 2. Insert an attachment into **Track** and **Platform**. 1. Hover over **Track** and click the **⊕** button. A contextual menu displays. 2. From the menu, insert an **Attachment**. 3. Repeat this process for **Platform**. 4. Rename both attachments **TrackAttachment** and **PlatformAttachment**, respectively. 3. Select the **PrismaticConstraint**. 4. In the **Properties** window, assign the attachments to the PrismaticConstraint. 1. Select the `Class.PrismaticConstraint.Attachment0` property. Your cursor changes. 2. In the **Explorer** window, select **TrackAttachment**. 3. Select the `Class.PrismaticConstraint.Attachment1` property. Your cursor changes. 4. In the **Explorer** window, select **PlatformAttachment**. ### Align the attachments If you keep both attachments at their default positions within the center of their parent parts, the attachments will try to pull each part inside of the other, causing the physics of both parts to collide and render the elevator non-functional. To ensure this doesn't happen, you must move the attachments outside of their parent parts so the platform can freely travel through an unobstructed space along the outside of the track, then align them along their X and Z axes so that the platform only moves up and down the Y axis. Before you begin to reposition and align your attachments, make sure you are able to view them within the viewport by enabling **Show Constraint Details** from Studio's **View** menu. To align the constraint's attachments: 1. In the toolbar, select the **Rotate** tool and rotate the **TrackAttachment** and **PlatformAttachment** so that the yellow arrow of each attachment points upwards on the Y axis. 2. Select the **Move** tool and reposition the attachment points so that they are both outside of their parent parts and aligned on their X and Z axes. ### Set PrismaticConstraint values Now that you have a `Class.PrismaticConstraint` and have aligned its associated `Class.Attachment|Attachments`, it's time to set the constraint's values that a `Class.Script` can use to enable the platform to move up and down the track to a set lower and upper range of motion that correlates to the bottom and top of the track. Because the bottom and top of the track are each 10 studs away from the **TrackAttachment** that's in the middle of the track that's 20 studs in length, the constraint's lower and upper limits must be `-10` and `10`, respectively. _The elevator in comparison to a track that has 1 stud segments to help visualize how to determine what lower and upper limits a constraint must have._ _The platform must move ten studs up and down from the middle of the track in order to transport a user from the bottom to the top of the track._ To set values for your constraint to enable elevator movement within a set range of motion: 1. In the **Explorer** window, select **PrismaticConstraint**. 2. In the **Properties** window, navigate to the **Slider** section, then enable the ability to set limits on the platform's range of motion with a servo style motor. New property fields display. 1. Set **Limits Enabled** to **True**. 2. Set **ActuatorType** to **Servo**. 3. Navigate to the **Limits** section, then set the platform's movement range to 10 studs above and below the middle of the track with no elasticity (bounce) as it reaches the upper and lower limits. After you set the following properties, the lower and upper limit visual aids elongate to meet their new values. 1. Set **LowerLimit** to **-10**. 2. Set **Restitution** to **0**. 3. Set **UpperLimit** to **10**. 4. Navigate to the **Servo** section, then ensure that the platform can hold the weight against physics working against the platform, it moves at a nice pace upwards and downwards, and that its initialization point is at the bottom of the constraint's lower limit. 1. Set **ServoMaxForce** to **10000**. 2. Set **Speed** to **10**. 3. Set **TargetPosition** to **-10**. ## Create the proximity prompt A `Class.ProximityPrompt` is an object that encourages user interaction to trigger an action when they approach in-experience objects such as doors, light switches, and buttons. This process uses a [proximity prompt](/docs/en-us/ui/proximity-prompts.md) to allow users to press a key when they are near the platform in order to activate the elevator's movement. To create a proximity prompt: 1. In the **Explorer** window, hover over **Platform** and click the **⊕** button. A contextual menu displays. 2. From the menu, insert a **ProximityPrompt**. ## Script elevator movement Now that you have all of the elements of your elevator ready to go, it's time to create a `Class.Script` that gets everything to work together and move the platform up and down the track. To script the elevator's movement: 1. In the **Explorer** window, hover over **Elevator** and click the **⊕** button. A contextual menu displays. 2. From the menu, insert a **Script**. 3. In the new script, enter the following: ```lua local platform = script.Parent.Platform local prismaticConstraint = script.Parent.Track.PrismaticConstraint platform.ProximityPrompt.Triggered:Connect(function(player) print(prismaticConstraint.CurrentPosition) if prismaticConstraint.CurrentPosition <= -9 then prismaticConstraint.TargetPosition = 10 elseif prismaticConstraint.CurrentPosition >= 9 then prismaticConstraint.TargetPosition = -10 end end) ``` When you [playtest your experience](/docs/en-us/studio/testing-modes.md#playtesting) and input the key for the elevator's proximity prompt, the script runs to check if the platform is below or above 9 studs from the constraint. If it's below 9 studs and a user interacts with the proximity prompt, the platform moves upwards until it reaches the constraint's upper limit; conversely, if it's above 9 studs and a user interacts with the proximity prompt, the platform moves downwards until it reaches the constraint's lower limit. --- title: "Create moving objects" url: /docs/en-us/tutorials/use-case-tutorials/physics/create-moving-objects last_updated: 2026-06-29T19:34:15Z description: "Explains the process of creating dynamic motion by moving objects." --- # Create moving objects **Moving objects** are objects that move on one or more axes within the 3D space. Using the built-in power of Roblox's simulation engine, you can make objects move and interact with their environment in a way that emulates real-world physical behavior that's familiar and intuitive to players, such as gravity, aerodynamics, and friction. Using the [Moving Objects](https://www.roblox.com/games/17560154079/UCT-Linear-Movement) `.rbxl` file as a reference, this tutorial explains how physical forces impact linear motion in Studio, and shows you various techniques to move objects from point A to point B in your experiences with different movement behavior, including guidance on: - Using a `Class.LinearVelocity` mover constraint to move an entire assembly at a constant linear velocity. - Using a `Class.PrismaticConstraint` to constrain an assembly to a single axis and move it at a constant linear velocity relative to a point in the 3D space. - Using the `Class.BasePart.ApplyImpulse|ApplyImpulse` method to move an assembly with an initial impulse of force so that the assembly slowly decelerates over time. > **Info:** You can create your own assemblies using basic parts or meshes from third-party modeling tools, then follow along with your own assets. For information on exporting meshes for use in Studio, see [Exporting Requirements](/docs/en-us/art/modeling/export-requirements.md). ## Linear motion and physical forces Roblox Studio is a real-world simulation engine that emulates physical behavior in real time, so in order to predict how objects moving linearly can behave in experiences, it's important to have a high-level understanding of how objects move in real life with linear motion. **Linear motion** is movement along an axis. For example, when a block has linear motion, it moves along a set axis. ![A gray block in front of a dark background. The movement axis is highlighted, and it faces toward the left of the screen to signify that the block is going to move along the world's Y axis.](../../../assets/tutorials/creating-moving-objects/Movement-Axis.jpg) Linear motion cannot exist without external, physical forces pushing or pulling objects to move. According to Newton's [first law of motion](https://en.wikipedia.org/wiki/Newton%27s_laws_of_motion#First_law), stationary objects remain stationary and moving objects remain in motion with a constant velocity unless they are acted on by an external force. For example, a stationary block remains stationary unless a physical force like wind pushes it to move. **Force** is the measure of the direction and magnitude of a physical push or pull that causes objects to change their linear velocity along an axis. A change in velocity is known as **acceleration**. This concept is particularly important for objects to move in Studio; the more force you apply to objects, the more quickly they accelerate. This is because the force needs to be greater than any physical forces pushing back against the object, such as gravity or friction. For example, if you were to place the block on a metal plate, the physical force of the wind needs to overcome the amount of friction from the metal plate to continue accelerating the block. If the wind's force is not much greater than the friction from the metal plate, the block accelerates, just more slowly than the previous example. ![A gray block in front of a dark grey metal plate with a diagram that signifies the block will have a plate friction to slow the block's movement from the wind.](../../../assets/tutorials/creating-moving-objects/Plate-Friction.jpg) **Linear velocity** is the measure of an object's movement, or how fast the object changes its position along an axis over a period of time. Studio measures linear velocity according to how many studs an object moves per second. Studs are Roblox's primary physical units for measuring length, and each stud equates to about 28cm in the real world. ![A gray block in front of the default baseplate's 4x4 grid texture. A single stud cube rectangle is highlighted.](../../../assets/tutorials/creating-moving-objects/One-Stud.jpg) Understanding linear velocity is important for designing gameplay in your experiences because it helps you determine how much force you need in order to achieve a particular speed for your moving objects. For instance, when you want to propel objects upward, it's important to consider how you must adjust your force to overcome gravity within the environment so that the objects move accurately. The following sections dive deeper into these concepts as you learn how to move objects at either a constant or initial linear velocity with the necessary force to overcome any oppositional physical forces within the environment. As you review these physics concepts with the upcoming techniques, you can more accurately predict how to adjust property values to achieve any ideal linear movement behavior in Studio. ## Maintain a constant linear velocity For an object to reach and maintain a constant linear velocity, it needs a force to overcome any oppositional physical forces that either decelerate the object's linear velocity, or cause the object to remain stationary. For example, if you want an object to have a linear velocity of `[0, 12, 0]` in Studio, you need enough force for the object to reach and maintain 12 studs per second along the Y axis in its environment. The amount of force necessary not only depends on oppositional physical forces within the environment itself, such as gravity and friction, but also on the object itself. For example, if you have two objects of the same shape that are moving on the same axis, the object with the larger amount of mass requires more force to achieve the same linear acceleration. _The little triangle part has a small amount of mass, so it needs less force to achieve the same acceleration._ _The large triangle part has a large amount of mass, so it needs more force to achieve the same acceleration._ The following subsections use assemblies of different shapes and sizes to teach you how to move either an entire object or only a portion of the object at a constant linear velocity. As you experiment with different property values, you will learn how to estimate the maximum amount of force you need for assemblies in your own experiences. ### Use LinearVelocity constraints `Class.LinearVelocity` objects are a type of [mover constraint](/docs/en-us/physics/mechanical-constraints.md) that apply force on an entire assembly to maintain a constant linear velocity. By not locking the assembly's position to an axis during its motion, the assembly is free to rotate as it collides with other objects in the 3D space. This type of movement leads to surprising gameplay scenarios that are more difficult for players to predict. _As the lily pads collide with one another, they change orientation but continue to flow down the river at a constant linear velocity._ To begin moving the assembly, the `Class.LinearVelocity` constraint needs to know: - The point and positive or negative direction to apply a force. - The amount of studs you want the assembly to move per second. - The maximum amount of force the engine can apply for the assembly to reach the constant linear velocity. To demonstrate this process, you will configure a lily pad with an attachment that a `Class.LinearVelocity` constraint references to move the lily pad `15` studs per second along the world's negative X axis at a constant linear velocity. ![A close up view of a lily pad. The lily pad's constraint visual aid is visible, and it points to the right, which is the world's negative X axis.](../../../assets/tutorials/creating-moving-objects/LV-3.jpg) #### Add attachment You can specify the point to apply force by adding an `Class.Attachment` object to the assembly, then configuring the attachment's position in the 3D space. The sample [Moving Objects](https://www.roblox.com/games/17560154079/UCT-Linear-Movement) experience places an attachment in the center of the lily pad so that the constraint can move the mesh from the attachment along a particular axis. Attachments include visual aids to help you visualize their axes of motion. The yellow arrow denotes the attachment's primary axis, and the orange arrow denotes the attachment's secondary axis. While neither axis of motion influences the lily pad's movement in the steps of this technique, it's important to understand these visual aids for future reference because they can assist you in determining ideal behavior for different types of constraints, such as the `Class.PrismaticConstraint` in the next technique. ![A attachment's visual aid arrows. The yellow arrow that signifies the attachment's primary axis points up, and the orange arrow that signifies the attachment's secondary axis points to the right.](../../../assets/tutorials/creating-spinning-objects/Attachment-Visual-Aids.png) To add an attachment: 1. In the **Explorer** window, expand the **LinearVelocityExample** folder, then expand its child **LilyPad_DIY** model. 2. Insert an attachment into the **Pad** mesh. 1. Hover over the mesh and click the ⊕ button. A contextual menu displays. 2. From the menu, insert an **Attachment**. The attachment displays in the center of the part. 3. Rename the attachment to **MoveAttachment**.![A close up view of a lily pad and its attachment visual aid. The primary axis points away from the camera, and the secondary axis points upward.](../../../assets/tutorials/creating-moving-objects/LV-Attachment-2.jpg) #### Configure constraint Now that your mesh has a fixed point to move the lily pad, you can configure the properties of a `Class.LinearVelocity` constraint to specify the direction and magnitude for the constant linear velocity, the amount of studs you want the mesh to move per second, and the maximum amount of force the engine can apply for the mesh to reach a constant linear velocity. The sample [Moving Objects](https://www.roblox.com/games/17560154079/UCT-Linear-Movement) experience applies up to 5000 Rowtons of constant force to move the lily pad 15 studs per second along the world's negative X axis at a constant linear velocity. Rowtons are Roblox's primary physical units for measuring force. To reference Roblox physical units and how they convert to metric units, see [Roblox Units](/docs/en-us/physics/units.md). To configure a `Class.LinearVelocity` constraint: 1. To make the constraint visible in the viewport so that you can reference its linear direction, enable **Show Constraint Details** from Studio's **View** menu. 2. Insert a `Class.LinearVelocity` constraint into the **Pad** mesh. 1. In the **Explorer** window, hover over the mesh, then click the ⊕ icon. A contextual menu displays. 2. From the contextual menu, insert **LinearVelocity**. 3. Assign the mesh's attachment to the new constraint. 1. In the **Explorer** window, select the constraint. 2. In the **Properties** window, 1. Set **Attachment0** to **MoveAttachment**. 2. Set **MaxForce** to `5000` to apply up to 5000 Rowtons of constant force to achieve the target linear velocity. 3. Keep **RelativeTo** to **World** to move the lily pad relative to the world's position and orientation. 4. Set **VelocityConstraint** to **Line** to constrain the force along a line from the attachment. 5. Set **LineDirection** to `-1, 0, 0` to move the lily pad along the world's negative X axis. Note that if you were to set this property to `1, 0, 0` the lily pad would move along the world's positive X axis. 6. Set **LineVelocity** to `15` to move the lily pad 15 studs per second. > **Info:** If you were to set **RelativeTo** to **Attachment**, the constraint would move the lily pad in relation to its attachment's orientation. However, as soon as the lily pad collides with objects, the attachment would rotate too and move the lily pad along a new orientation.![A close up view of a lily pad and its constraint visual aid arrow that points to the right, or the world's negative X axis.](../../../assets/tutorials/creating-moving-objects/LV-3.jpg) 4. Verify the amount of force you set moves the mesh 15 studs per second along the world's negative X axis. - Select the **Run** simulation mode from the mezzanine's dropdown menu and click the **Play** button to begin. Studio simulates the experience at the current camera position without your avatar in the 3D space.![Run option in the testing modes dropdown of Studio's mezzanine.](../../../assets/studio/general/Mezzanine-Testing-Mode-Run.png) ### Use PrismaticConstraint constraints `Class.PrismaticConstraint` objects are a type of [mechanical constraint](/docs/en-us/physics/mechanical-constraints.md) that create a rigid joint between two attachments, allowing their parent assemblies to move along one axis **relative to each other**. By locking both assemblies' position to a single axis, each assembly is only able to rotate if they rotate together in the same direction. This type of movement leads to stable gameplay scenarios that are easier for players to predict. For example, the sample [Moving Objects](https://www.roblox.com/games/17560154079/UCT-Linear-Movement) experience uses `Class.PrismaticConstraint` objects to move log platforms that players can use to carefully cross a giant river. When you set `Class.PrismaticConstraint.ActuatorType` to **Motor**, this constraint applies force on the two attachments with the goal of the attachments reaching and maintaining a constant linear velocity. If you anchor one of the attachments' parent assemblies, the force continues to move the unanchored assembly at a constant linear velocity while the anchored assembly remains stationary. To begin moving the assembly, the `Class.PrismaticConstraint` constraint needs to know: - The point and positive or negative direction to apply a force. - The amount of studs you want the attachments to move per second. - The maximum amount of force the engine can apply for the attachments and their parent assemblies to reach a constant linear velocity. To demonstrate this process, you will configure a log assembly with two objects that have child attachments that a `Class.PrismaticConstraint` references to move the log 40 studs per second along the world's negative X axis at a constant linear velocity. ![A close up view of a brown log on top of a river. The log's constraint visual aid is visible and points to the right, or the world's negative X axis.](../../../assets/tutorials/creating-moving-objects/PC-3.jpg) #### Configure attachments You can specify the direction to move a particular object within an assembly by adding two `Class.Attachment` objects to the assembly, then configuring their alignment and orientation in the 3D space. The sample [Moving Objects](https://www.roblox.com/games/17560154079/UCT-Linear-Movement) experience aligns two attachments along the world's X axis near the position of where the unanchored log overlaps with the anchored part, and orients each attachment's primary axis to face the world's negative X axis. When you configure your PrismaticConstraint constraint in the next section, it will move the log **in relation** to the anchor part. In other words, the log will move away from the stationary part that cannot move because it's anchored in the 3D space. To configure attachments for the prismatic constraint: 1. In the **Explorer** window, expand the **PrismaticConstraintExample** folder, then expand its child **Log_DIY** model. 2. Insert an attachment into the **Log** mesh. 1. Hover over the mesh and click the ⊕ button. A contextual menu displays. 2. From the menu, insert an **Attachment**. The attachment displays in the center of the part. 3. Rename the attachment to **LogAttachment**.![A close up view of a log and its attachment visual aid. The primary axis points toward the camera, and the secondary axis points upward.](../../../assets/tutorials/creating-moving-objects/PC-Attachment-2.jpg) 3. Using the same process, insert an attachment into the **Anchor** part, then rename the attachment **AnchorAttachment**.![A close up view of a log and two attachment visual aids. The primary axis of the new attachment visual aid points to the right, and the secondary axis points upward.](../../../assets/tutorials/creating-moving-objects/PC-Attachment-3.jpg) 4. Using the **View Selector** tool as a reference for the world's coordinates, rotate **LogAttachment** and **AnchorAttachment** until each attachment's primary axis faces the world's negative X axis![A close up view of a log and its attachment visual aid. Both attachments have a primary axis that points to the right, and a secondary axis that points upward.](../../../assets/tutorials/creating-moving-objects/PC-Attachment-4.jpg) 5. Reposition **AnchorAttachment** so that both attachments are aligned on the world's X axis.![A close up view of a log and its attachment visual aids that are now positioned horizontally. Both attachments have a primary axis that points to the right, and a secondary axis that points upward.](../../../assets/tutorials/creating-moving-objects/PC-Attachment-5.jpg) #### Configure constraint Now that your attachments are aligned on the same axis and face the same direction you want the log to move, you can configure the properties of a `Class.PrismaticConstraint` constraint to specify whether to apply the target constant linear velocity in the positive or negative direction of each attachment's' primary axis, the amount of studs you want the attachments to move per second, and the maximum amount of force the engine can apply for the log to reach a constant linear velocity. While you can choose different values for your own use cases, the sample [Moving Objects](https://www.roblox.com/games/17560154079/UCT-Linear-Movement) experience applies up to 50000 Rowtons of constant force to move the attachments 40 radians per second along the world's negative X axis at a constant linear velocity. However, because the anchor attachment is in an anchored object, only the log's attachment can move. To configure a prismatic constraint: 1. Insert a `Class.PrismaticConstraint` object into the **Log** mesh. 1. In the **Explorer** window, hover over the mesh, then click the ⊕ icon. A contextual menu displays. 2. From the contextual menu, insert a **PrismaticConstraint**. 2. Assign the log's attachments to the new constraint so that the log moves in **relation to** the anchored block part. 1. In the **Explorer** window, select the constraint. 2. In the **Properties** window, 1. Set **Attachment0** to **AnchorAttachment**. 2. Set **Attachment1** to **LogAttachment**. The constraint displays in the viewport.![A close up view of a log and its constraint visual aid. It doesn't have a arrow visual aid because it doesn't yet have a set velocity.](../../../assets/tutorials/creating-moving-objects/PC-2.jpg) 3. In the **Explorer** window, select the constraint, then in the **Properties** window: 1. Set **ActuatorType** to **Motor**. New property fields display. 2. Set **MotorMaxForce** to `50000` to apply up to 50000 Rowtons of constant force to achieve the target linear velocity. 3. Set **Velocity** to `40` to move the log 40 studs per second.![A close up view of a log and its constraint visual aid arrow that points to the right, or the world's negative X axis.](../../../assets/tutorials/creating-moving-objects/PC-3.jpg) 4. Verify the amount of force you set moves the log 40 studs per second along the world's negative X axis. - Select the **Run** simulation mode from the mezzanine's dropdown menu and click the **Play** button to begin. Studio simulates the experience at the current camera position without your avatar in the 3D space.![Run option in the testing modes dropdown of Studio's mezzanine.](../../../assets/studio/general/Mezzanine-Testing-Mode-Run.png) ## Apply an initial linear force Another way of changing an object's linear velocity is by applying an impulse of force. After the impulse of force, the object either decelerates until it becomes stationary if there is an oppositional force such as friction, or remains in motion with constant linear velocity if there aren't any oppositional forces. This technique is useful to move objects after a significant gameplay event, such as an explosion or impactful collision, because it provides players instantaneous feedback. To demonstrate, the following subsection teaches you how to launch a player's character upward toward the sky when they collide with a jump pad with an initial impulse that you can adapt with new values to meet your own gameplay requirements. ### Use ApplyImpulse The `Class.BasePart.ApplyImpulse|ApplyImpulse` method applies force on an entire assembly to obtain an initial linear velocity before slowing to a stop when there are oppositional forces. To begin moving the assembly, the method needs to know: - The assembly to move. - The axis to apply force to reach an initial linear velocity. - The amount of force to apply to each axis. You can define all of these values in a script. For example, the sample script defines the assembly to move as the player character's `Class.Humanoid` object, then it applies an impulse of force that's 2500 Rowton-seconds to launch the player upwards on the world's positive Y axis. Note that player characters have different amounts of mass, so you may need to increase and balance this force to launch every player without launching player characters with smaller amounts of mass too high. To move an assembly using `Class.BasePart.ApplyImpulse|ApplyImpulse`: 1. In the **Explorer** window, expand the **ApplyImpulseExample** folder, then expand its child **JumpPad_DIY** model. 2. Insert a script into the **JumpPad** part. 1. Hover over the part and click the ⊕ button. A contextual menu displays. 2. From the menu, insert a **Script**. The attachment displays in the center of the part. 3. Rename the script to **JumpScript**. 3. Replace the default code with the following code:```lua local volume = script.Parent local function onTouched(other) local impulse = Vector3.new(0, 2500, 0) local character = other.Parent local humanoid = character:FindFirstChildWhichIsA("Humanoid") if humanoid and other.Name == "LeftFoot" then other:ApplyImpulse(impulse) end end volume.Touched:Connect(onTouched) ``` --- title: "Create spinning objects" url: /docs/en-us/tutorials/use-case-tutorials/physics/create-spinning-objects last_updated: 2026-06-29T19:34:15Z description: "Explains the process of creating dynamic motion by spinning objects." --- # Create spinning objects **Spinning objects** are objects that rotate on one or more axes within the 3D space. Using the built-in power of Roblox's simulation engine, you can make objects spin and interact with their environment in a way that emulates real-world physical behavior that's familiar and intuitive to players, such as gravity, aerodynamics, and friction. Using the [Spinning Objects](https://www.roblox.com/games/16550477904/Spinning-Objects) `.rbxl` file as a reference, this tutorial explains how physical forces impact angular motion in Studio, and shows you various techniques to spin objects in your experiences with different spinning behavior, including guidance on: - Using an `Class.AngularVelocity` mover constraint to spin an entire assembly at a constant angular velocity. - Using a `Class.HingeConstraint` mechanical constraint to spin a part within an assembly at a constant angular velocity as the rest of the assembly remains stationary. - Using the `Class.BasePart.ApplyAngularImpulse|ApplyAngularImpulse` method to spin an assembly with an initial impulse of angular force so that the assembly slowly decelerates over time. > **Info:** You can create your own assemblies using basic parts or meshes from third-party modeling tools, then follow along with your own assets. For information on exporting meshes for use in Studio, see [Exporting Requirements](/docs/en-us/art/modeling/export-requirements.md). ## Angular motion and physical forces Roblox Studio is a real-world simulation engine that emulates physical behavior in real time, so in order to predict the behavior of spinning objects in experiences, it's important to have a high-level understanding of how objects spin in real life with angular motion. **Angular motion**, or rotational motion, is movement around a fixed point or axis. For example, when a propeller has angular motion, it spins around its rotational axis in the middle of the propeller. Angular motion cannot exist without external, physical forces pushing or pulling objects to spin. According to Newton's [first law of motion](https://en.wikipedia.org/wiki/Newton%27s_laws_of_motion#First_law), stationary objects remain stationary and moving objects remain in motion with a constant velocity unless they are acted on by an external force. For example, a stationary propeller remains stationary unless a physical force like wind pushes it to spin. **Torque** is the measure of the physical force that causes objects to spin, and it's responsible for objects obtaining angular acceleration. This concept is particularly important for objects to spin in Studio; the more torque you apply to objects, the more quickly they can accelerate. This is because torque needs to be _greater_ than any directional physical forces pushing back against the object, such as gravity or friction. For example, if you were to place the propeller in dirt, the physical force of the wind needs to overcome the amount of friction from the dirt to continue accelerating the spinning propeller. If the wind's force is not much greater than the friction from the dirt, the propeller accelerates, just more slowly than the previous example. **Angular velocity** is the measure of an object's rotation rate, or how fast the object rotates around a fixed point or axis over a period of time. Studio measures angular velocity according to how many [radians](https://en.wikipedia.org/wiki/Radian) an object spins per second. There are 2π radians (6.283) in one rotation, so in order for an object to make a full rotation per second, it must have enough torque to spin about 6 radians. Understanding angular velocity is important for designing gameplay in your experiences because it helps you determine how much torque you need in order to achieve a particular acceleration for your spinning objects. The following sections dive deeper into these concepts as you learn how to spin objects at either a constant or initial angular velocity with the necessary torque to overcome any oppositional physical forces within the environment. As you review these physics concepts with the upcoming techniques, you can more accurately predict how to adjust property values to achieve any ideal spinning behavior in Studio. ## Maintain a constant angular force For an object to reach and maintain a constant angular velocity, it needs an angular force to overcome any oppositional physical forces that either decelerate the object's angular velocity, or cause the object to remain stationary. For example, if you want an object to have an angular velocity of `[0, 12, 0]` in Studio, you need enough torque for the object to reach and maintain `12` radians per second along the Y axis in its environment, or about two full rotations per second. The amount of torque you apply to your objects not only depends on oppositional physical forces within the environment itself, such as gravity and friction, but also on the object itself. For example, if you have two objects of the same shape that are spinning on the same axis, the larger object with the larger [moment of inertia](https://en.wikipedia.org/wiki/Moment_of_inertia) requires more torque to achieve the same angular acceleration. _The little triangle part has a lower moment of inertia, so it needs less angular force to achieve the same acceleration._ _The large triangle part has a larger moment of inertia, so it needs more angular force to achieve the same acceleration._ The following subsections use assemblies of different shapes and sizes to teach you how to spin either an entire object or only a portion of the object. As you experiment with different property values, you will learn how to estimate the maximum amount of torque you need for assemblies in your own experiences. ### Use AngularVelocity constraints `Class.AngularVelocity` objects are a type of [mover constraint](/docs/en-us/physics/mover-constraints.md) that apply torque on an entire assembly to maintain a constant angular velocity. To begin spinning the assembly, the `Class.AngularVelocity` constraint needs to know: - The point and positive or negative direction to apply an angular force. - The amount of radians you want the assembly to spin per second. - The maximum amount of torque the engine can apply for the assembly to reach a constant angular velocity. To demonstrate this process, you will add a block to your workspace with an attachment that an `Class.AngularVelocity` constraint references to spin the block `6` radians per second along the world's Y axis at a constant angular velocity, or about one full rotation. #### Add attachment You can specify the fixed point to spin an assembly by adding an `Class.Attachment` object to the assembly, then configuring the attachment's position in the 3D space. The sample [Spinning Objects](https://www.roblox.com/games/16550477904/Spinning-Objects) experience places an attachment in the center of a block part so that the constraint can spin the part counterclockwise around the center of itself. Attachments include visual aids to help you visualize their axes of rotation. The yellow arrow denotes the attachment's primary axis, and the orange arrow denotes the attachment's secondary axis. While neither axis of rotation influences the block's rotation in the steps of this technique, it's important to understand these visual aids for future reference because they can assist you in determining ideal behavior for different types of constraints, such as the `Class.HingeConstraint` in the next technique. To add an attachment: 1. In the **Explorer** window, insert a **block** part into the **Workspace**. 2. Insert an attachment into the new part. 1. In the **Explorer** window, hover over the part and click the ⊕ button. A contextual menu displays. 2. From the menu, insert an **Attachment**. The attachment displays in the center of the part. 3. Rename the attachment to **SpinAttachment**. #### Configure constraint Now that your block has a fixed point to spin the block, you can configure the properties of an `Class.AngularVelocity` constraint to specify the rotational direction, axis or axes to apply a target constant angular velocity, the amount of radians you want the block to spin per second, and the maximum amount of torque the engine can apply for the block to reach a constant angular velocity. The sample [Spinning Objects](https://www.roblox.com/games/16550477904/Spinning-Objects) experience applies up to 1000 Rowton-studs of constant angular force to spin the block 6 radians per second along the world's Y axis at a constant angular velocity. Rowton-studs are Roblox's primary physical units for measuring torque. To reference Roblox physical units and how they convert to metric units, see [Roblox Units](/docs/en-us/physics/units.md). To configure an `Class.AngularVelocity` constraint: 1. To make the constraint visible in the viewport so that you can reference its rotational direction, enable **Show Constraint Details** from Studio's **View** menu. 2. Insert an `Class.AngularVelocity` constraint into the part. 1. In the **Explorer** window, hover over the part, then click the ⊕ icon. A contextual menu displays. 2. From the contextual menu, insert **AngularVelocity**. The constraint's visual aid displays in the middle of the part. 3. Assign the part's attachment to the new constraint. 1. In the **Explorer** window, select the constraint. 2. In the **Properties** window, 1. Set **Attachment0** to **SpinAttachment**. 2. Set **AngularVelocity** to `0, 6, 0` to spin the part 6 radians per second along the Y axis. Note that if you were to set this property to `0, -6, 0`, the block would spin clockwise. 3. Set **MaxTorque** to `1000` to apply up to 1000 Rowton-studs of constant angular force per second to achieve the target angular velocity. 4. Keep **RelativeTo** to **World** to spin the block relative to the world's position and orientation. 4. Verify the amount of torque you set spins the block 6 radians per second along the world's Y axis. - Select the **Run** simulation mode from the mezzanine's dropdown menu and click the **Play** button to begin. Studio simulates the experience at the current camera position without your avatar in the 3D space.![Run option in the testing modes dropdown of Studio's mezzanine.](../../../assets/studio/general/Mezzanine-Testing-Mode-Run.png) You may need to adjust your torque depending on your block's scale and any oppositional physical forces in your environment. For example, the properties of the `Class.AngularVelocity` constraint in the sample experience work for a block part with a default size of `4, 1, 2` on a flat platform with a plastic material, and an environment with the classic preset gravity. However, if your block is a larger size and on grass terrain, you need to increase your `Class.AngularVelocity.MaxTorque` property because the angular force needs to overcome both the block's mass and friction from the environment. For example, the large block part that's quadruple the size of the sample's part needs at least `300000` Rowton-studs of constant angular force to achieve the set angular velocity! ### Use HingeConstraint constraints `Class.HingeConstraint` objects are a type of [mechanical constraint](/docs/en-us/physics/mechanical-constraints.md) that allows two attachments to rotate around one axis, constraining the attachments to the same position and their primary axes in the same direction. When you set `Class.HingeConstraint.ActuatorType` to **Motor**, this constraint applies torque on the two attachments with the goal of the attachments reaching and maintaining a constant angular velocity. Further, when you place attachments into an assembly with two objects, the objects lock together and attempt to spin together according to the attachment's fixed primary axis. If you anchor one of these objects, the angular force continues to spin the other object at a constant angular velocity while the rest of the assembly remains stationary. For example, to begin spinning a particular object within an assembly, the `Class.HingeConstraint` constraint needs to know: - The position where you want the attachments to overlap. - The point and positive or negative direction to apply an angular force. - The amount of radians you want the attachment to spin per second. - The maximum amount of torque the engine can apply for the attachment to reach a constant angular velocity. To demonstrate this process, you will add a propeller assembly with two objects to your workspace with attachments in both objects that a `Class.HingeConstraint` constraint references to spin the propeller `3` radians per second (about half a full rotation per second) along the Y axis at a constant angular velocity while the base of the propeller remains stationary. #### Get propeller asset The **Creator Store** is a tab of the **Toolbox** that you can use to find all assets that are made by Roblox and the Roblox community for use within your projects, including model, image, mesh, audio, plugin, video, and font assets. You can use the Creator Store to add an individual asset or asset library directly into an open experience. This tutorial references a propeller model that you can use as you replicate each step of the `Class.HingeConstraint` technique of spinning objects. To get this propeller asset from your inventory into your experience: 1. Add the propeller to your inventory. 1. Navigate to the asset's [details page](https://create.roblox.com/store/asset/16558528602) on the Creator Store. 2. In the top-right corner, click the **Get Model** button. The propeller asset is now in your inventory, and you can reuse it in any project on the platform. 2. In Studio, navigate to the **Home** tab, then click the **Toolbox** button. The **Toolbox** window opens.![Toolbox highlighted in Studio's toolbar.](../../../assets/studio/general/Toolbar-Toolbox.png) 3. In the **Toolbox** window, click the **Inventory** tab. The **My Models** sort displays. 4. Click the **Propeller** tile. The model displays in your viewport. #### Configure attachments You can specify both the position of where you want the attachments to overlap and the direction of rotational movement to spin a particular object within an assembly by adding two `Class.Attachment` objects to the assembly, then configuring their alignment and orientation in the 3D space. The sample [Spinning Objects](https://www.roblox.com/games/16550477904/Spinning-Objects) experience aligns two attachments near the position of where the unanchored propeller overlaps with the anchored base, and orients their primary axis of rotation upwards so that they spin counterclockwise. The base attachment cannot spin in this example because the base is anchored. To configure attachments for the hinge constraint: 1. Insert an `Class.Attachment` object into **Head** and **Base**. 1. In the **Explorer** window, hover over **Head** and click the ⊕ button. A contextual menu displays. 2. From the menu, insert an **Attachment**. 3. Repeat this process for **Base**. 4. Rename both attachments **HeadAttachment** and **BaseAttachment**, respectively. 2. Rotate **HeadAttachment** and **BaseAttachment** so that the primary axis of each attachment points upwards on the Y axis. This tells Studio to rotate the attachments counterclockwise. 3. Move **BaseAttachment** to the top of **Base**, and **HeadAttachment** to the bottom edge of **Propeller**. This tells Studio where to connect the hinge itself, and overlap both attachments at runtime. #### Configure constraint Now that your attachments have position to overlap and a direction of rotational movement, you can configure the properties of a `Class.HingeConstraint` constraint to specify the amount of radians you want the attachment to spin per second, and the maximum amount of torque the engine can apply for the attachment to reach a constant angular velocity. Similar to the previous technique, the sample [Spinning Objects](https://www.roblox.com/games/16550477904/Spinning-Objects) experience applies up to 1000 Rowton-studs of constant angular force to spin the attachment 3 radians per second along the Y axis at a constant angular velocity. However, because the base attachment is in an anchored object, only the propeller's attachment can spin. To configure a hinge constraint: 1. Insert a `Class.HingeConstraint` object into **Head**. 1. In the **Explorer** window, hover over **Head**, then click the ⊕ icon. A contextual menu displays. 2. From the contextual menu, insert a **HingeConstraint**. 2. Assign the propeller's attachments to the new constraint so that the propeller spins in **relation to** the anchored base. 1. In the **Explorer** window, select the constraint. 2. In the **Properties** window, 1. Set **Attachment0** to **BaseAttachment**. 2. Set **Attachment1** to **HeadAttachment**. The hinge displays in the viewport. 3. In the **Explorer** window, select the constraint, then in the **Properties** window, 1. Set **ActuatorType** to **Motor**. New property fields display. 2. Set **MotorMaxTorque** to `1000` to apply up to 1000 Rowton-studs of constant angular force to achieve the target angular velocity. 3. Set **AngularVelocity** to `3` to spin the head of the propeller 3 radians per second. 4. Verify the amount of torque you set spins the propeller 3 radians per second along the Y axis. - Select the **Run** simulation mode from the mezzanine's dropdown menu and click the **Play** button to begin. Studio simulates the experience at the current camera position without your avatar in the 3D space.![Run option in the testing modes dropdown of Studio's mezzanine.](../../../assets/studio/general/Mezzanine-Testing-Mode-Run.png) ## Apply an initial angular force Another way of changing an object's angular velocity is by applying an impulse of angular force. After the impulse of angular force, the object either decelerates until it becomes stationary if there is an oppositional force such as friction, or remains in motion with constant velocity if there aren't any oppositional forces. This technique is useful to spin objects after a significant gameplay or weather event, such as a strong gust of wind, because it provides players instantaneous feedback. To demonstrate, the following subsection teaches you how to spin an assembly with an initial random angular force that you can adapt with new values to meet your own gameplay requirements. ### Use ApplyAngularImpulse The `Class.BasePart.ApplyAngularImpulse|ApplyAngularImpulse` method applies torque on an entire assembly to obtain an initial angular velocity before slowing to a stop. To begin spinning the assembly, the method needs to know: - The assembly to spin. - The axis to apply torque to reach an initial angular velocity. - The amount of torque to apply to each axis. You can define all of these values in a script. For example, the sample script defines the assembly to spin as the script's parent, then it applies a random impulse of angular force between `0` and `100` Rowton-studs on the Y axis. To spin an assembly using `Class.BasePart.ApplyAngularImpulse|ApplyAngularImpulse`: 1. Insert a **sphere** part into the **Workspace**. The sample uses a sphere with a `Class.MaterialVariant` so you can clearly visualize the sphere's movement. 2. Insert a script into the new part. 1. In the **Explorer** window, hover over the part and click the ⊕ button. A contextual menu displays. 2. From the menu, insert a **Script**. 3. Replace the default code with the following code: ```lua local part = script.Parent local impulse = Vector3.new(0, math.random(0, 100), 0) part:ApplyAngularImpulse(impulse) ``` --- title: "Deadly lava" url: /docs/en-us/tutorials/use-case-tutorials/scripting/basic-scripting/deadly-lava last_updated: 2026-06-29T19:34:15Z description: "The process for creating a lava floor that kills players on contact." --- # Deadly lava In [Introduction to scripting](/docs/en-us/tutorials/use-case-tutorials/scripting/basic-scripting/intro-to-scripting.md), you learned how to make changes in an experience in a loop over time. What if you want to make changes based on user behavior? In this tutorial, you'll learn how to make a deadly lava floor which kills users when they step on it. ## Set up You need a place in your world to put the deadly lava. If you followed the [Introduction to scripting](/docs/en-us/tutorials/use-case-tutorials/scripting/basic-scripting/intro-to-scripting.md) course, the lava floor would fit nicely in the gap covered by the disappearing platforms. 1. Insert a `Part` and move it into place in your world. Name it **LavaFloor**. 2. Resize it so it covers the floor of the enclosing space. 3. Make the floor look more like lava by setting the `Material` property to `Neon` and the `Color` to an orange shade. 4. Insert a **Script** into the `LavaFloor` part and rename it to `Kill`. 5. Remove the default code and create a variable for the lava.```lua local lava = script.Parent ``` ## Connect to an event Use an **event** to detect when a user touches the lava. Every part has a `Touched` event that fires when something touches it. You can **connect** to this event to run a function when it fires. 1. Declare a new function called `kill`. 2. Access the `Touched` event on the lava object using a dot, just like a property: `lava.Touched`. 3. Call the `Connect` function on the `Touched` event, passing `kill()` as the function to call when the part is touched.```lua local lava = script.Parent local function kill() end lava.Touched:Connect(kill) ``` Any code you write in the `kill` function will now run whenever something touches the lava. Note that a **colon** is used for the `Connect` function, **not** a dot - don't worry about why at this point, just remember the difference. ## Get the touching part To kill the user, the function will need an object associated with that user. A part's `Touched` event can provide the "other part" that touched it — but only if you request it by making it a **parameter** of the function. Parameters are definitions of what a function expects to receive when it's called. A parameter can be used in a function just like any other variable. You can pass information to a parameter by including it in the parentheses when a function is called. Parameters are defined in the parentheses on the first line of a function. Create a **parameter** called `otherPart` for the `kill` function. ```lua local lava = script.Parent local function kill(otherPart) end lava.Touched:Connect(kill) ``` When the `kill` function is called, the `otherPart` parameter will represent the part that touched the lava floor, and the code you'll write in the function will be able to use it. ## Character and humanoid When a user touches the lava, Roblox can detect the specific body part of the user that touched it, such as the left leg or right foot. This part is in the user's **Character** model, which contains all of the objects that make up the user's avatar in the experience, including: - The individual body parts of the user such as the head, limbs, and torso. - Any clothing and accessories worn by the user. - The `Class.Humanoid`, a special object which contains many properties related to the user, including the user's health. - The HumanoidRootPart which controls the user's movement. As previously noted, any body part that touches the lava is part of the Character model, so you can get a reference to that character with `otherPart.Parent`. Create a variable to store the parent of the part that touched the lava floor. ```lua local lava = script.Parent local function kill(otherPart) local partParent = otherPart.Parent end lava.Touched:Connect(kill) ``` From the character model, you'll need to get the Humanoid object in order to kill the user. You can do this with the `FindFirstChild()` function—just pass it the name of the thing you're looking for and it provides the first matching child it finds in that object. Call `FindFirstChild()` on the `partParent` variable with `"Humanoid"` as the child to find, and store the result in a new variable called `humanoid`. ```lua local lava = script.Parent local function kill(otherPart) local partParent = otherPart.Parent local humanoid = partParent:FindFirstChild("Humanoid") end lava.Touched:Connect(kill) ``` ## Check the humanoid You can easily check if the Humanoid was found using an **if** statement. The code in an if statement will only run if the condition defined in the first line is true. There are a variety of [operators](/docs/en-us/luau/operators.md) that can be used to build more complex conditions which you'll encounter in future courses - for now, just put the `humanoid` variable there. Create an **if** statement with `humanoid` as the condition. ```lua local lava = script.Parent local function kill(otherPart) local partParent = otherPart.Parent local humanoid = partParent:FindFirstChild("Humanoid") if humanoid then end end lava.Touched:Connect(kill) ``` > **Info:** In Luau, any value other than false or `nil` (an empty value) is evaluated as true in a conditional statement, so in this case you can use `humanoid` directly as the condition. ## Set character health Now that the `Class.Humanoid` has been checked, its properties can be changed. If you set its `Health` property to **0**, the associated Character dies. In the body of the if statement, set the `Health` property of humanoid to 0. ```lua local function kill(otherPart) local partParent = otherPart.Parent local humanoid = partParent:FindFirstChild("Humanoid") if humanoid then humanoid.Health = 0 end end lava.Touched:Connect(kill) ``` With that, your lava floor is complete! Test your experience and you should find that your deadly lava successfully kills users on contact. Try using your lava as an extra challenge in an obby, or as a boundary for a world. ## Final code ```lua local lava = script.Parent local function kill(otherPart) local partParent = otherPart.Parent local humanoid = partParent:FindFirstChild("Humanoid") if humanoid then humanoid.Health = 0 end end lava.Touched:Connect(kill) ``` --- title: "Fading trap" url: /docs/en-us/tutorials/use-case-tutorials/scripting/basic-scripting/fading-trap last_updated: 2026-06-29T19:34:15Z description: "The process for creating a platform that fades away when a player steps on it." --- # Fading trap In [Deadly Lava](/docs/en-us/tutorials/use-case-tutorials/scripting/basic-scripting/deadly-lava.md), you learned how to trigger code based on user behavior. This tutorial shows you how to make a platform which fades away when a user steps on it. ## Set up If you followed [Deadly Lava](/docs/en-us/tutorials/use-case-tutorials/scripting/basic-scripting/deadly-lava.md), you can place your fading platform above the lava floor that users can't jump across. 1. Insert a part and move it into place in your world. Name it `FadingPlatform`. 2. Resize it so a user can jump on it. 3. Make sure it's **Anchored**. 4. Insert a **Script** into the part, rename it to **FadeOnTouch**, and remove the default code. 5. Create a variable for the platform and an empty function connected to the platform's `Touched` event.```lua local platform = script.Parent local function fade() end platform.Touched:Connect(fade) ``` ## Fade the platform Having the platform vanish in an instant would be no fun at all as users would find it impossible to get across the gap. Instead, the platform should **fade away** before users fall through it to give them a chance to jump off. You could change the `Transparency` property and wait a very short time over and over again to get this effect, but a gradual fade would require at least 10 changes between 0 and 1. That's 20 lines of very repetitive code. This can be achieved much more effectively using a **for** loop which repeats code a specific number of times. Each loop of the code is known as an **iteration**. A for loop is defined with three things, separated by commas: - **Control variable** - The variable created and used to count the loops. In this example, it's `count` and the starting value is 1. - **End value** - The value it has to get to for the loop to stop. In this example, it's 10. - **Step increment** (optional) - Determines what to add to the control variable each loop. If left out, it defaults to 1, so in this example it's unnecessary. In **FadeOnTouch**: 1. In the function, create a `for` loop starting from `1` which iterates `10` times. 2. Inside the for loop, set the `Transparency` property to the control variable divided by `10`. 3. Call the `Library.task.wait()` function with a time of `0.1`.```lua local platform = script.Parent local function fade() for count = 1, 10 do platform.Transparency = count / 10 task.wait(0.1) end end platform.Touched:Connect(fade) ``` When the loop runs, count increases by 1 with each iteration. This means that the platform's `Transparency` will increase by 0.1 every 0.1 seconds, reaching full transparency after 1 second. ## Reappear After the platform has vanished, users should fall through it. The platform should also come back a few seconds after it fades - otherwise, users would never get to try the jump again if they failed. The CanCollide property controls whether users can fall through a part. 1. Set the `CanCollide` property of the platform to `false` after the for loop. 2. Wait for a few seconds using the `Library.task.wait()` function. 3. Set the `CanCollide` property back to `true`. 4. Set the `Transparency` property back to `0`.```lua local platform = script.Parent local function fade() for count = 1, 10 do platform.Transparency = count / 10 task.wait(0.1) end platform.CanCollide = false task.wait(3) platform.CanCollide = true platform.Transparency = 0 end platform.Touched:Connect(fade) ``` ## Debounce variable In [Deadly Lava](/docs/en-us/tutorials/use-case-tutorials/scripting/basic-scripting/deadly-lava.md), you learned that the `Touched` event runs each time a user's body part comes into contact with the part. This behavior causes issues when a user runs across the fading platform: the function will run multiple times, resetting the loop each time. For the code to work properly, the function should only run once when the user touches the platform for the first time. Ensuring that an action is only triggered once when it would otherwise be triggered multiple times is known as **debouncing**. To debounce a function, a [boolean](/docs/en-us/luau/booleans.md) variable can be used to keep track of when the platform has already been touched. Booleans can only contain **true** and **false** values. Create a variable called `isTouched` and set it to `false`. ```lua local platform = script.Parent local isTouched = false local function fade() for count = 1, 10 do platform.Transparency = count / 10 task.wait(0.1) end platform.CanCollide = false task.wait(3) platform.CanCollide = true platform.Transparency = 0 end platform.Touched:Connect(fade) ``` ## Check the variable An if statement can be used to only run the code in the fade function if the `isTouched` debouncing variable is false. Wrap the body of the fade function in an `if` statement with the condition `not isTouched`. ```lua local platform = script.Parent local isTouched = false local function fade() if not isTouched then for count = 1, 10 do platform.Transparency = count / 10 task.wait(0.1) end platform.CanCollide = false task.wait(3) platform.CanCollide = true platform.Transparency = 0 end end platform.Touched:Connect(fade) ``` The Luau `not` operator reverses the value of whatever follows it. In conditional terms, this means that the first if statement behaves the same as the statements which follow. ```lua if not isTouched then end if isTouched == false then end if isTouched == nil then end ``` ## Toggle the debounce Currently, the code in the `fade` function will always run because `isTouched` is false and `not isTouched` evaluates to true. To complete the debounce routine, you'll need to toggle the variable's value in two places. 1. Set `isTouched` to `true` inside the `if` statement, before the platform begins to fade. 2. Set it back to `false` once the platform has reappeared. ```lua local function fade() if not isTouched then isTouched = true for count = 1, 10 do platform.Transparency = count / 10 task.wait(0.1) end platform.CanCollide = false task.wait(3) platform.CanCollide = true platform.Transparency = 0 isTouched = false end end platform.Touched:Connect(fade) ``` And that's it! Test your code now, and you should find that the platform fades away when the user jumps on it and comes back a few seconds later. You can duplicate this platform across a wider gap to create a challenging obstacle and change the speed at which they fade to balance the difficulty. ## Final code ```lua local platform = script.Parent local isTouched = false local function fade() if not isTouched then isTouched = true for count = 1, 10 do platform.Transparency = count / 10 task.wait(0.1) end platform.CanCollide = false task.wait(3) platform.CanCollide = true platform.Transparency = 0 isTouched = false end end platform.Touched:Connect(fade) ``` --- title: "Introduction to scripting" url: /docs/en-us/tutorials/use-case-tutorials/scripting/basic-scripting/intro-to-scripting last_updated: 2026-06-29T19:34:15Z description: "The process for creating a basic script that makes a platform disappear." --- # Introduction to scripting In [Introduction to Roblox Studio](/docs/en-us/tutorials/first-experience.md), you learned how to create and manipulate parts in Roblox Studio. In this tutorial, you'll learn how to apply a **script** to parts to make a platform appear and disappear. You can use this in a platforming experience to span a gap, challenging users to time their jumps carefully to get to the other side. ## Set the scene First off, you need a **Part** to act as the platform. Making and moving parts should be familiar to you from [Introduction to Roblox Studio](/docs/en-us/tutorials/first-experience.md). You don't need a complicated world aside from the platform — you just need a gap that your users can't easily jump across. 1. Insert a **Part** and rename it to `DisappearingPlatform`. 2. Resize it to large enough for a user to jump on. 3. Move it to a proper location so that you can reach it and jump on it when testing your experience.![alt](../../../../assets/tutorials/intro-to-scripting/partInPlace.jpg) 4. Set the **Anchored** property to **true** in the **Properties** window. > **Info:** Remember that setting a part's Anchored property to **true** makes it stay in place no matter what. Your platform falls down if it's not anchored. ## Insert a script Code in Roblox is written in a language called [Luau](/docs/en-us/luau.md) which you can put in scripts within various containers in the **Explorer**. If you put a script under a **Part**, Roblox will run the script's code when the part is loaded into the experience. 1. Hover over the `DisappearingPlatform` part in the **Explorer** window and click the **+** button to insert a new script into the platform. Rename your new script as **Disappear**. 2. Delete the default code inside. > **Info:** Remember to **rename** parts and scripts as soon as you create them so you don't lose track of things in the **Explorer**. ## First variable It's a good idea to start off your script by making a **variable** for the platform. A variable is a **name** associated with a **value**. Once a variable is created, it can be used again and again. You can change the value as needed. In Luau, a variable is created as follows: `local variableName = variableValue`. The term `local` means that the variable is only going to be used in the block of the script where it's declared. The `=` sign is used to set the value of the variable. Names for variables are typically written in **camel case**. This is lowercase with every word following the first being capitalized, `justLikeThis`. Copy the following code to create a **variable** for the platform called `platform`, where the value is `script.Parent`. ```lua local platform = script.Parent ``` > **Info:**`script.Parent` is used to find the object the script is located in. As you might have guessed, `script` refers to the script you're writing in and the `Parent` of the script is where it's located. ## Disappear function Time to make the platform disappear. It's always best to group code for achieving a specific action into a **function**. A function is a named block of code that helps you organize your code and use it in multiple places without writing it again. Create a **function** in the script and call it `disappear`. ```lua local platform = script.Parent local function disappear() end ``` The first new line **declares** the function — it indicates the start of the function and names it as `disappear`. The code for a function goes between the first line and `end`. The parentheses are for including additional information as needed. You'll learn more about passing information to functions in a later course. ## Part properties When the platform disappears, it needs to be invisible and users need to fall through it — but you can't just destroy the platform since it needs to reappear later. Parts have various **properties** that can be used here. Remember that you can see a part's properties if you select it and look at the **Properties** window. A part can be made invisible by changing the `Class.BasePart.Transparency|Transparency` property. Transparency can be a value between 0 and 1, where 1 is fully transparent and therefore invisible. _Changing the Transparency property of the cube_ The `CanCollide` property determines if other parts (and users) can pass right through the part. If you set it to **false**, users will fall through the platform. _Changing the CanCollide property of the cube_ Just like `script.Parent`, properties are accessed using a **dot**. Values are assigned using an equals sign. 1. In the `disappear` function, set the `CanCollide` property of the platform to **false**. 2. On the line following, set the `Transparency` property to **1**.```lua local platform = script.Parent local function disappear() platform.CanCollide = false platform.Transparency = 1 end ``` > **Info:** You might notice that Studio automatically **indents** your code inside a function. Always make sure to indent your code like this — it helps indicate where the function begins and ends, which makes your code more readable. ## Call the function Once you've declared a function, you can run it by writing its name with parentheses next to it. For example, `disappear()` will run the `disappear` function. This is known as **calling** a function. 1. **Call** the `disappear` function at the end of the script.```lua local platform = script.Parent local function disappear() platform.CanCollide = false platform.Transparency = 1 end disappear() ``` 2. [Initiate a playtest](/docs/en-us/studio/testing-modes.md#playtesting). If your code works, the platform should have disappeared by the time the user spawns in. ## Appear function You can easily make the platform reappear by writing a function that does the exact opposite of the `disappear` function. 1. Delete the `disappear()` line from the script. 2. Declare a new function called `appear`. 3. In the function body, set the `CanCollide` property to **true** and the `Transparency` property to **0**.```lua local platform = script.Parent local function disappear() platform.CanCollide = false platform.Transparency = 1 end local function appear() platform.CanCollide = true platform.Transparency = 0 end ``` ## Loop code The platform should be constantly disappearing and reappearing, with a few seconds between each change. It's impossible to write an infinite number of function calls — fortunately, with a **while loop**, you don't have to. A while loop runs the code inside it for as long as the **statement** after `while` remains true. This particular loop needs to run forever, so the statement should just be `true`. Create a `while true` loop at the end of your script. ```lua local platform = script.Parent local function disappear() platform.CanCollide = false platform.Transparency = 1 end local function appear() platform.CanCollide = true platform.Transparency = 0 end while true do end ``` ## Toggle the platform In the while loop, you need to write code to wait a few seconds between the platform disappearing and reappearing. The built-in function `Library.task.wait()` can be used for this. In the parentheses the number of seconds to wait is needed: for example `task.wait(3)`. > **Error:** Whatever you do, never make a `while true` loop without including a `task.wait()` — and don't test your code before you've put one in! If you don't wait, your experience will **freeze** because Studio will never have a chance to leave the loop and do anything else. Three seconds is a sensible starting point for the length of time between each platform state. 1. In the while loop, call the `task.wait()` function with **3** in the parentheses. 2. Call the `disappear` function. 3. Call the `task.wait()` function again with **3** in the parentheses. 4. Call the `appear` function. ```lua while true do task.wait(3) disappear() task.wait(3) appear() end ``` The code for the platform is now complete! Test your code now and you should find that the platform disappears after three seconds and reappears three seconds later in a loop. You could duplicate this platform to cover a wider gap, but you need to change the wait times in each script. Otherwise, the platforms will all disappear at the same time and users will never be able to cross. ## Final code ```lua local platform = script.Parent local function disappear() platform.CanCollide = false platform.Transparency = 1 end local function appear() platform.CanCollide = true platform.Transparency = 0 end while true do task.wait(3) disappear() task.wait(3) appear() end ``` --- title: "Score points" url: /docs/en-us/tutorials/use-case-tutorials/scripting/basic-scripting/score-points last_updated: 2026-06-29T19:34:15Z description: "The process for creating a scoring system to award and track points for players." --- # Score points In previous tutorials, you made a variety of experience features including [fading platforms](/docs/en-us/tutorials/use-case-tutorials/scripting/basic-scripting/fading-trap.md) and [deadly lava](/docs/en-us/tutorials/use-case-tutorials/scripting/basic-scripting/deadly-lava.md). This tutorial ties these together into a playable experience where users see who can stay alive the longest. Every second they stay alive, a point will be added to their score. ## Set up First up, you'll need to set the scene for your experience. Duplicate the [fading platforms](/docs/en-us/tutorials/use-case-tutorials/scripting/basic-scripting/fading-trap.md) you made in the previous tutorial and let users compete to stay on the board of platforms for as long as possible. You can also use [deadly lava](/docs/en-us/tutorials/use-case-tutorials/scripting/basic-scripting/deadly-lava.md) to kill users when they fall off the platforms, or just let them fall to their doom. Make sure you place a **SpawnLocation** somewhere where users can jump onto the platforms to start playing. ![Spawn point above a grid of fading platforms, above a lava floor](../../../../assets/tutorials/scoring-points/sceneInPlace.jpg) ## Player points Roblox has a built-in **Leaderboard** for showing user stats. When you set player points through the leaderboard, they show up on the right side of the screen in the experience. ![Leaderboard with username and points shown](../../../../assets/tutorials/scoring-points/leaderboard.jpg) You'll learn more customizable ways to display information in later tutorials, but the leaderboard is the simplest way of making a visible scoring system in Roblox. It's best to put scripts which set up experience state into **ServerScriptService** because they will automatically run when the experience starts. In **ServerScriptService**, create a script called **SetupPoints**. ![SetupPoints script in place in ServerScriptService in the Explorer window](../../../../assets/tutorials/scoring-points/setupPointsScript.jpg) ## Listen for players In Roblox, a **service** is an object which performs a range of useful functions. The `Class.Players` service has an event called `Class.Players.PlayerAdded|PlayerAdded` that you can use to set up points for each user who joins the experience. You can access services with the `Class.ServiceProvider:GetService()|GetService` function in the `Class.DataModel|game` object. `Class.DataModel|game` is a variable accessible from anywhere which contains everything in your experience. 1. Create a variable for the Players service using `game:GetService("Players")`. 2. Create a function called `onPlayerAdded` with a parameter for the incoming player. 3. Connect the `onPlayerAdded` function to the PlayerAdded event.```lua local Players = game:GetService("Players") local function onPlayerAdded(player) end Players.PlayerAdded:Connect(onPlayerAdded) ``` > **Info:** When declaring a variable to contain a service, it's best to name it with the exact name of the service (`"Players"`), even though this means breaking usual naming conventions for variables. ## Create a stats folder To make a user's points display in the leaderboard, all you need to do is create a new `Class.Folder` in their `Class.Player` object called `"leaderstats"` and put their points in there. New objects can be created from within a script via the `Datatype.Instance.new()` function. 1. Create a new `Folder` object using `Instance.new("Folder")`, storing the result in a new variable called `leaderstats`.```lua local function onPlayerAdded(player) local leaderstats = Instance.new("Folder") end ``` 2. Set the Name property of `leaderstats` to `"leaderstats"`. 3. Parent `leaderstats` to `player`.```lua local Players = game:GetService("Players") local function onPlayerAdded(player) local leaderstats = Instance.new("Folder") leaderstats.Name = "leaderstats" leaderstats.Parent = player end Players.PlayerAdded:Connect(onPlayerAdded) ``` > **Warning:** Make sure you name the folder **exactly** as it is shown here (`"leaderstats"`) or it will not work! ## Create the points The leaderboard system reads any values in the `leaderstats` folder and displays whatever it finds. To add a stat which will track a player's points, a new `IntValue` object can be parented to the `leaderstats` folder. The name of the value object will be displayed alongside its current value. 1. Use a variable named `points` to create a new `IntValue` object using `Datatype.Instance.new()`. 2. Set the `Name` to `"Points"`. 3. Set the `Value` to **0**; this is what the leaderboard will initially display for the player. 4. Parent the `points` object to the `leaderstats` folder. ```lua local Players = game:GetService("Players") local function onPlayerAdded(player) local leaderstats = Instance.new("Folder") leaderstats.Name = "leaderstats" leaderstats.Parent = player local points = Instance.new("IntValue") points.Name = "Points" points.Value = 0 points.Parent = leaderstats end Players.PlayerAdded:Connect(onPlayerAdded) ``` Test your experience and you should see the leaderboard appear in the top right with the names of your users and a points score next to them. ## Count time Each user should earn a point for each second they are alive. A `while` loop and the `Library.task.wait()` function can be used to update the value of points every second. 1. At the end of the script, create a `while` loop with `true` as the condition. 2. In the loop, `Library.task.wait()` for 1 second. ```lua Players.PlayerAdded:Connect(onPlayerAdded) while true do task.wait(1) end ``` ## Player list To run code for every user in the experience, you need to iterate through the **array** of users returned by the `Class.Players:GetPlayers()|GetPlayers` function. An array is a list of items stored in order. Each item can be accessed by its **index** position, starting from `1`. You can get the length of an array by prefixing it with `#`. 1. Store the result of `Class.Players:GetPlayers()` in a `playerList` variable. 2. Create a **for** loop with a starting value of 1 and an end value of `#playerList`, so that you get one iteration of the loop per player. ```lua while true do task.wait(1) local playerList = Players:GetPlayers() for currentPlayer = 1, #playerList do -- Add your logic here for each player in the playerList end end ``` ## Award points To award a point to each user in the for loop, you'll need to get the user out of the array and add 1 to the **Points** object stored in their `leaderstats` folder. Objects stored in an array are accessed using **square brackets** - for instance, the first item in the `playerList` array can be accessed with `playerList[1]`. If you write `playerList[currentPlayer]` in the for loop, you can move through each user in the list with every iteration of the loop. 1. Store the user at `playerList[currentPlayer]` in a variable called `player`. 2. Store the user's **Points** object in a variable called `points`. 3. Set the `Class.IntValue.Value|Value` property of `points` to `points.Value + 1`. ```lua while true do task.wait(1) local playerList = Players:GetPlayers() for currentPlayer = 1, #playerList do local player = playerList[currentPlayer] local points = player.leaderstats.Points points.Value += 1 end end ``` Test your experience and you should find that the leaderboard shows your player's score counting up by 1 every second. ## Listen for characters The goal of the experience is to see who can stay alive the longest, so users who die will need to have their points reset to 0. You'll need to get the **Character** model for the user in order to detect when they have died. This model is only added to the experience _after_ the `Class.Player` object has been loaded and you can use the `Class.Player.CharacterAdded|CharacterAdded` event to listen for when the character is ready to use. Create a function called `onCharacterAdded` with two parameters: one for the character, one for the player. ```lua local Players = game:GetService("Players") local function onCharacterAdded(character, player) end local function onPlayerAdded(player) local leaderstats = Instance.new("Folder") ``` Although you included user in the `onCharacterAdded` function's parameters, the actual `Class.Player.CharacterAdded|CharacterAdded` event only returns the character, not the associated user. To pass the `player` object as well, use an [anonymous function](/docs/en-us/luau/functions.md#anonymous-functions) for the event connection. ```lua local function onPlayerAdded(player) local leaderstats = Instance.new("Folder") leaderstats.Name = "leaderstats" leaderstats.Parent = player local points = Instance.new("IntValue") points.Name = "Points" points.Value = 0 points.Parent = leaderstats player.CharacterAdded:Connect(function(character) onCharacterAdded(character, player) end) end ``` ## Reset points When a user dies, their `Class.Humanoid` automatically fires a `Class.Humanoid.Died|Died` event. You can use this event to find out when to reset their points. The Humanoid is found inside the Character model, but the contents of that model are only assembled as the user spawns. To make your code safely wait for the Humanoid object to load, use the `Class.Instance:WaitForChild()|WaitForChild()` function. You can call it on any parent object, passing the string name of the child you're waiting for. Create a variable to wait for the Humanoid using `character:WaitForChild("Humanoid")`. ```lua local Players = game:GetService("Players") local function onCharacterAdded(character, player) local humanoid = character:WaitForChild("Humanoid") end ``` The function you need to connect to the Died event is very short and will only ever be needed here, so you can use an anonymous function again. 1. Connect a new anonymous function to the Humanoid's **Died** event. 2. In the anonymous function, create a variable called `points` for the player's **Points** object. 3. Set the `Class.IntValue.Value|Value` property of `points` to **0**. ```lua local Players = game:GetService("Players") local function onCharacterAdded(character, player) local humanoid = character:WaitForChild("Humanoid") humanoid.Died:Connect(function() local points = player.leaderstats.Points points.Value = 0 end) end ``` Test this out and you'll see the user's score resets when they die. ## Check the player If users keep earning points even when dead, it's hardly in the spirit of the experience, so the code needs to check if users are alive before awarding a point. You need to start by defining an **attribute** in the `onPlayerAdded` function which can be used to check whether the user is alive. At this point, the user is not yet alive and spawned, as their character model still needs to be added. Attributes allow you to customize objects in Roblox with your own data. An attribute consists of a name and a value. You can create one on any object using the `Class.Instance:SetAttribute()|SetAttribute` function. Call `Class.Instance:SetAttribute()|SetAttribute` on `player` to create a new **attribute** called `"IsAlive"` with the value `false`. ```lua local function onPlayerAdded(player) local leaderstats = Instance.new("Folder") leaderstats.Name = "leaderstats" leaderstats.Parent = player local points = Instance.new("IntValue") points.Name = "Points" points.Value = 0 points.Parent = leaderstats player:SetAttribute("IsAlive", false) player.CharacterAdded:Connect(function(character) onCharacterAdded(character, player) end) end ``` Once the user's character model respawns, the value of `IsAlive` needs to be changed to `true` so that the user can start earning points again. 1. In `onCharacterAdded`, set the `IsAlive` attribute of `player` to **true**. 2. In `onCharacterDied`, set the `IsAlive` attribute of `player` to **false**. ```lua local Players = game:GetService("Players") local function onCharacterAdded(character, player) player:SetAttribute("IsAlive", true) local humanoid = character:WaitForChild("Humanoid") humanoid.Died:Connect(function() local points = player.leaderstats.Points points.Value = 0 player:SetAttribute("IsAlive", false) end) end ``` Finally, `IsAlive` should be **checked** before any point is awarded in the `while` loop at the end of the script. The `Class.Instance:GetAttribute()|GetAttribute` function takes the name of an attribute and returns the value. In the `while` loop, wrap the code to award a point in an `if` statement with the condition `player:GetAttribute("IsAlive")`. ```lua while true do task.wait(1) local playerList = Players:GetPlayers() for currentPlayer = 1, #playerList do local player = playerList[currentPlayer] if player:GetAttribute("IsAlive") then local points = player.leaderstats.Points points.Value += 1 end end end ``` Test your experience out now and you should find the user earns points every second they are alive, and stays at 0 when not alive. Have your friends play with you and see who can get the highest score. This is just the start: you can continue improving your experience for your users. Here are some tips: - Place the code for all of the platforms into a single script, making it much easier to update. - Create a lobby area where users wait to be teleported to the experience area, allowing users to start simultaneously. - Announce the winners of each round. ## Final code ```lua local Players = game:GetService("Players") local function onCharacterAdded(character, player) player:SetAttribute("IsAlive", true) local humanoid = character:WaitForChild("Humanoid") humanoid.Died:Connect(function() local points = player.leaderstats.Points points.Value = 0 player:SetAttribute("IsAlive", false) end) end local function onPlayerAdded(player) local leaderstats = Instance.new("Folder") leaderstats.Name = "leaderstats" leaderstats.Parent = player local points = Instance.new("IntValue") points.Name = "Points" points.Value = 0 points.Parent = leaderstats player:SetAttribute("IsAlive", false) player.CharacterAdded:Connect(function(character) onCharacterAdded(character, player) end) end Players.PlayerAdded:Connect(onPlayerAdded) while true do task.wait(1) local playerList = Players:GetPlayers() for i = 1, #playerList do local player = playerList[i] if player:GetAttribute("IsAlive") then local points = player.leaderstats.Points points.Value += 1 end end end ``` --- title: "Create a health pickup" url: /docs/en-us/tutorials/use-case-tutorials/scripting/intermediate-scripting/create-a-health-pickup last_updated: 2026-06-29T19:34:15Z description: "The process for creating a pickup that restores health." --- # Create a health pickup Throughout the Basic Scripting tutorials, you have scripted individual parts to create playable scenes. With the previous method, if you were to duplicate the parts you would then also have duplicate scripts. This would make updating the scripts tedious as changes would have to be done script by script. In this tutorial, a different pattern will be used to create a number of health pickups, with only a single copy of the script which determines the health pickup behavior. When the pickup is touched, it will restore the player's health, fade slightly and be disabled for a short period of time. ## Set up First up, you'll need a part or a model to use as a pickup. The [Showdown Town example world](https://www.roblox.com/games/6407123421/Showdown-Town-Health-Pickups) includes plenty of health pickups spread all over the map. ![Edit in Studio option from the experience's main page](../../../../assets/tutorials/creating-a-health-pickup/Showdown-Town-Edit-Place.png) Each health pickup is a Union of two rectangular parts with a green PointLight inside. They're all stored in one folder in the Workspace called **HealthPickups**, which is where the script will look for them. If you add any more to the map, it's essential you ensure that they are also stored in this folder. ## Restore health To begin with, the script needs to restore a player's health. This pattern should be familiar to you from the [Deadly Lava](/docs/en-us/tutorials/use-case-tutorials/scripting/basic-scripting/deadly-lava.md) tutorial. 1. In **ServerScriptService**, add a script called **PickupManager**. 2. In this script, declare a constant called `MAX_HEALTH` with the value **100**. 3. Create a function called `onTouchHealthPickup` with parameters for the other part that touched the pickup and the pickup itself.```lua local MAX_HEALTH = 100 local function onTouchHealthPickup(otherPart, healthPickup) end ``` 4. In the function, get the character model from the parent of `otherPart`. Next, check to see if it has a `Class.Humanoid` using `Class.Instance:FindFirstChildWhichIsA()|FindFirstChildWhichIsA()`. 5. If it has a humanoid, set their **Health** property to `MAX_HEALTH`.```lua local MAX_HEALTH = 100 local function onTouchHealthPickup(otherPart, healthPickup) local character = otherPart.Parent local humanoid = character:FindFirstChildWhichIsA("Humanoid") if humanoid then humanoid.Health = MAX_HEALTH end end ``` > **Info:** The code here calls `Class.Instance:FindFirstChildWhichIsA()|FindFirstChildWhichIsA()` – which takes the **type** of the object desired – instead of `Class.Instance:FindFirstChild()|FindFirstChild()` which only takes the name. This is a safer option as it can only ever return a Humanoid instead of something which just happens to be called "Humanoid". ## Get the pickups folder The folder holding the health pickups may not have loaded into the experience by the time the script runs. `WaitForChild` can be used to pause the script and get the HealthPickups folder when it loads. When called on a folder, the `GetChildren` function will return an array of the folder's contents. 1. Beneath MAX_HEALTH, declare a variable called `healthPickupsFolder` and use the `WaitForChild` function to get the **HealthPickups** folder from the Workspace. 2. Create a variable named `healthPickups` to store the result of calling the `GetChildren` function on `healthPickupsFolder`.```lua local MAX_HEALTH = 100 local healthPickupsFolder = workspace:WaitForChild("HealthPickups") local healthPickups = healthPickupsFolder:GetChildren() local function onTouchHealthPickup(otherPart, healthPickup) local character = otherPart.Parent local humanoid = character:FindFirstChildWhichIsA("Humanoid") if humanoid then humanoid.Health = MAX_HEALTH end end ``` ## Loop with ipairs `onTouchHealthPickup` needs to be called for each health pickup in the array. To do this efficiently, a new kind of loop syntax will be used. `ipairs` is a function that can be used with a for loop to go through each element of an array. You do not need to specify where the loop begins and ends. A for loop using `ipairs` is defined as follows: ![for index, value in ipairs(array) do](../../../../assets/tutorials/creating-a-health-pickup/forLoop.jpg) - **Index**: this is equivalent to the control variable in a regular for loop. - **Value**: this will be populated with each element in the array as the loop iterates. It's a good idea to name the value variable after what it will actually contain. - **Array**: the array you want to iterate over is passed to the ipairs function. In the following code, you don't need the index for anything, so it can be left blank with `_`. Create a **for** loop using the `ipairs` function, passing `healthPickups`. ```lua local function onTouchHealthPickup(otherPart, healthPickup) local character = otherPart.Parent local humanoid = character:FindFirstChildWhichIsA("Humanoid") if humanoid then humanoid.Health = MAX_HEALTH end end for _, healthPickup in ipairs(healthPickups) do end ``` A wrapper function will be needed to pass the health pickup to the `onTouchHealthPickup` function when connecting to the `Touched` event. 1. In the for loop, connect the Touched event to an anonymous function with a parameter called `otherPart`. 2. Call the `onTouchHealthPickups` function, passing both the `otherPart` parameter and the `healthPickup`.```lua for _, healthPickup in ipairs(healthPickups) do healthPickup.Touched:Connect(function(otherPart) onTouchHealthPickup(otherPart, healthPickup) end) end ``` Test your code now and you should find that the health pickup restores your health. You will need to damage your player first - try standing on the vent next to the spawn point. ![Steaming vent in example world to the right of the spawn point](../../../../assets/tutorials/creating-a-health-pickup/dangerousVent.jpg) A health bar should appear in the top right which will disappear when the player is healed. ## Pickup cooldown Currently, the pickup will indefinitely heal any player who touches it. It would be more effective if it could only be picked up once, with a short cooldown before it can be used again. First, you need to record whether or not the pickup is in the cooldown period. The pattern below should be familiar from [Fade Trap](/docs/en-us/tutorials/use-case-tutorials/scripting/basic-scripting/fading-trap.md) - this time, the debounce will be achieved by setting an attribute on the health pickup. 1. In the for loop, set a new **attribute** called `"Enabled"` to `true`. 2. Wrap the code inside `onTouchHealthPickup` in an if statement with the condition `healthPickup:GetAttribute("Enabled")`.```lua local function onTouchHealthPickup(otherPart, healthPickup) if healthPickup:GetAttribute("Enabled") then local character = otherPart.Parent local humanoid = character:FindFirstChildWhichIsA("Humanoid") if humanoid then humanoid.Health = MAX_HEALTH end end end for _, healthPickup in ipairs(healthPickups) do healthPickup:SetAttribute("Enabled", true) healthPickup.Touched:Connect(function(otherPart) onTouchHealthPickup(otherPart, healthPickup) end) end ``` ## Disable the pickup The pickup should provide visual feedback that it is disabled - a common way to indicate this is to make it slightly transparent. 1. Declare three constants at the top of the script (feel free to adjust each value to your liking): - `ENABLED_TRANSPARENCY = 0.4` - `DISABLED_TRANSPARENCY = 0.9` - `COOLDOWN = 10````lua local MAX_HEALTH = 100 local ENABLED_TRANSPARENCY = 0.4 local DISABLED_TRANSPARENCY = 0.9 local COOLDOWN = 10 local healthPickupsFolder = workspace:WaitForChild("HealthPickups") ``` 2. In the if statement in `onTouchHealthPickup`, set the `Class.BasePart.Transparency|Transparency` of the pickup to `DISABLED_TRANSPARENCY`, and the value of the `Enabled` attribute to false.```lua local function onTouchHealthPickup(otherPart, healthPickup) if healthPickup:GetAttribute("Enabled") then local character = otherPart.Parent local humanoid = character:FindFirstChildWhichIsA("Humanoid") if humanoid then humanoid.Health = MAX_HEALTH healthPickup.Transparency = DISABLED_TRANSPARENCY healthPickup:SetAttribute("Enabled", false) end end end ``` 3. Call the `Library.task.wait()` function, passing `COOLDOWN` as the amount to wait. 4. Set `Transparency` back to `ENABLED_TRANSPARENCY` and `Enabled` back to `true`.```lua local function onTouchHealthPickup(otherPart, healthPickup) if healthPickup:GetAttribute("Enabled") then local character = otherPart.Parent local humanoid = character:FindFirstChildWhichIsA("Humanoid") if humanoid then humanoid.Health = MAX_HEALTH healthPickup.Transparency = DISABLED_TRANSPARENCY healthPickup:SetAttribute("Enabled", false) task.wait(COOLDOWN) healthPickup.Transparency = ENABLED_TRANSPARENCY healthPickup:SetAttribute("Enabled", true) end end end ``` Test your pickup again: you should find that when you touch the pickup it restores your health, goes transparent, then comes back ready to be used again. If you want to make the feedback more impactful for the player when the pickup is collected, try cutting the brightness of the PointLight in the pickup when you change the transparency. Try using these health pickups in your own projects, or change the appearance and effect to give a different kind of power-up to your players. ## Final code ```lua local MAX_HEALTH = 100 local ENABLED_TRANSPARENCY = 0.4 local DISABLED_TRANSPARENCY = 0.9 local COOLDOWN = 10 local healthPickupsFolder = workspace:WaitForChild("HealthPickups") local healthPickups = healthPickupsFolder:GetChildren() local function onTouchHealthPickup(otherPart, healthPickup) if healthPickup:GetAttribute("Enabled") then local character = otherPart.Parent local humanoid = character:FindFirstChildWhichIsA("Humanoid") if humanoid then humanoid.Health = MAX_HEALTH healthPickup.Transparency = DISABLED_TRANSPARENCY healthPickup:SetAttribute("Enabled", false) task.wait(COOLDOWN) healthPickup.Transparency = ENABLED_TRANSPARENCY healthPickup:SetAttribute("Enabled", true) end end end for _, healthPickup in ipairs(healthPickups) do healthPickup:SetAttribute("Enabled", true) healthPickup.Touched:Connect(function(otherPart) onTouchHealthPickup(otherPart, healthPickup) end) end ``` --- title: "Create player tools" url: /docs/en-us/tutorials/use-case-tutorials/scripting/intermediate-scripting/create-player-tools last_updated: 2026-06-29T19:34:15Z description: "The process for creating and customizing Tools that players can equip and use." --- # Create player tools Tools are a simple way to manage items that the player can hold in their hand and use in-game. They can range from weapons such as swords to food items. In this tutorial, you'll learn how to create a tool in the shape of a laser blaster that will play sound effects when equipped or activated. ## Create the tool The `Class.Tool` object is the basis of any tool in Roblox, so you'll need to create one. It's easier to change how tools look by adding objects such as Parts and MeshParts to the tool in the workspace where they are visible. 1. Insert a **Tool** into the workspace and name it **Blaster**. 2. Insert a `Class.MeshPart` into the tool. 3. Set the **MeshId** property to `rbxassetid://92656610`. 4. Set the **TextureId** property to `rbxassetid://92658105`. 5. The tool needs a part named **Handle** for the player to hold. Change the name of the MeshPart to **Handle**. > **Warning:** If you don't include a part named **Handle** in the tool, it will drop to the ground when a player tries to equip it. ## Store the tool Tools can be kept in the 3D world as **collectable tools** or given to all players as **starter tools**. ### Collectable tool The blaster is currently a child of **Workspace** so it will be collectable. A player can pick up the tool by touching it, causing it to become a child of the character model; the tool will then be equipped and placed in their hotbar. During gameplay, unequipped tools are stored within the player's hierarchy in the Backpack and then moved to their character model when equipped. Any tool that becomes a child of a character will be automatically equipped. _Tripmine Unequipped_ _Tripmine Equipped_ ### Starter tool Storing a tool in `Class.StarterPack` will place it in a player's `Class.Backpack` when they join or respawn. 1. Move the **Blaster** to **StarterPack** in the **Explorer**. 2. Play the experience to test the tool. Click on the hotbar at the bottom of the screen or press **1** on the keyboard to equip the tool. ## Tool properties ### Position / orientation A tool's position and orientation can be changed using the **grip** properties. **GripPos** changes the position of the grip, whereas **GripForward**, **GripRight** and **GripUp** affect the rotation. Currently the player is holding the center of the blaster instead of the grip. 1. Set the **GripPos** property of the tool to **0, -0.4, 1.1**. 2. [Initiate a playtest](/docs/en-us/studio/testing-modes.md#playtesting) to test the tool. Notice how the tool is now being gripped in a different position._Before__After_ ### Hotbar icon By default, the tool **name** will be displayed on the hotbar icon. It's good practice to change the icon to be an image of the tool. Set the **TextureId** property of the tool to `rbxassetid://92628145`. _Before_ _After_ ### Tooltip A **tooltip** is a small text description that appears when the mouse hovers over a tool in the hotbar. They typically include the name of the tool and/or a brief description of its function. Change the **ToolTip** property to **Blaster**. ## Use scripts with tools A tool has three key events you can connect to: `Class.Tool.Equipped|Equipped`, `Class.Tool.Unequipped|Unequipped` and `Class.Tool.Activated|Activated`. | Event | Description | | --- | --- | | **`Class.Tool.Equipped\|Equipped`** | Fired when a tool is equipped by the player, for example when a tool is selected in the hotbar. | | **`Class.Tool.Unequipped\|Unequipped`** | Fired when a tool is unequipped by the player, for example when a tool is deselected in the hotbar. | | **`Class.Tool.Activated\|Activated`** | Fired when a tool is activated by the player, for example when a player left-clicks. | These methods only work in `Class.LocalScript|LocalScripts` because only the player's device knows when input happens, such as a mouse button being clicked or a screen being touched. ## Add the sounds To see these events in action, you can play a sound when they fire. First, you'll need to create Sound objects to use for this. 1. Insert two `Class.Sound` objects into the **Handle**. 2. Rename one sound **Equip** and set its SoundId property to `rbxassetid://282906960`. 3. Rename the other sound **Activate** and set its SoundId property to `rbxassetid://130113322`. ## Add the code The example code below plays the **Equip** sound when the tool is equipped and the **Fire** sound when activated. 1. Insert a **LocalScript** into the tool and name it **ToolController**. 2. Insert the following lines of code into the script.```lua local tool = script.Parent local function toolEquipped() tool.Handle.Equip:Play() end local function toolActivated() tool.Handle.Activate:Play() end tool.Equipped:Connect(toolEquipped) tool.Activated:Connect(toolActivated) ``` 3. Test the blaster sound effects by equipping and clicking to activate the tool. Now you know how to create and script a basic tool, try creating other simple tools such as a flashlight or speaker. --- title: "Hit detection with lasers" url: /docs/en-us/tutorials/use-case-tutorials/scripting/intermediate-scripting/hit-detection-with-lasers last_updated: 2026-06-29T19:34:15Z description: "The process for casting a laser from a blaster and detecting collision." --- # Hit detection with lasers In this tutorial, you'll learn how to cast a laser from the blaster in [Create player tools](/docs/en-us/tutorials/use-case-tutorials/scripting/intermediate-scripting/create-player-tools.md) and detect whether or not it hits a player. ## Raycasting to find collisions **Raycasting** creates an invisible ray from a start position towards a given direction with a defined length. If the ray collides with objects or terrain in its path, it will return information on the collision such as the position and the object it collided with. _Raycast from A towards B colliding with a wall_ ## Find the mouse location Before a laser can be shot, you must first know where the player is aiming. This can be found by raycasting from the player's 2D mouse location on the screen directly forwards from the camera into the 3D world. The ray will collide with whatever the player is aiming at with the mouse. 1. Open the **ToolController** script inside the Blaster tool from [Create player tools](/docs/en-us/tutorials/use-case-tutorials/scripting/intermediate-scripting/create-player-tools.md). If you haven't completed that tutorial yet you can download the [Blaster](https://www.roblox.com/library/6571559694/Blaster) model and insert it into StarterPack. > **Info:** Models can be added into your Inventory to be used between any experience. 1. In a browser, open the [model](https://www.roblox.com/library/6571559694/Blaster) page, click the **Get** button. This adds the model into your inventory. 2. From Studio's **Window** menu or **Home** tab toolbar, open the [Toolbox](/docs/en-us/projects/assets/toolbox.md) and select the **Inventory** tab. 3. Make sure the dropdown is on **My Models**. 4. Select the **Blaster** model to add it into the **StarterPack**. 2. At the top of the script, declare a constant named MAX_MOUSE_DISTANCE with a value of **1000**. 3. Create a function called `getWorldMousePosition`.```lua local tool = script.Parent local MAX_MOUSE_DISTANCE = 1000 local function getWorldMousePosition() end local function toolEquipped() tool.Handle.Equip:Play() end local function toolActivated() tool.Handle.Activate:Play() end -- Connect events to appropriate functions tool.Equipped:Connect(toolEquipped) tool.Activated:Connect(toolActivated) ``` 4. Use the GetMouseLocation function of `Class.UserInputService` to get the player's 2D mouse location on the screen. Assign this to a variable named **mouseLocation**.```lua local UserInputService = game:GetService("UserInputService") local tool = script.Parent local MAX_MOUSE_DISTANCE = 1000 local function getWorldMousePosition() local mouseLocation = UserInputService:GetMouseLocation() end ``` Now the 2D mouse location is known, its **X** and **Y** properties can be used as parameters for the `Class.Camera:ViewportPointToRay()` function, which creates a `Datatype.Ray` from the screen into the 3D world. 1. Use the **X** and **Y** properties of `mouseLocation` as arguments for the `Class.Camera:ViewportPointToRay()|ViewportPointToRay()` function. Assign this to a variable named **screenToWorldRay**.```lua local function getWorldMousePosition() local mouseLocation = UserInputService:GetMouseLocation() -- Create a ray from the 2D mouse location local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y) end ``` It's time to use the `Class.WorldRoot:Raycast()|Raycast` function to check if the ray hits an object. This requires a start position and direction vector: in this example, you will use the origin and direction properties of `screenToWorldRay`. The length of the direction vector determines how far the ray will travel. The ray needs to be as long as the `MAX_MOUSE_DISTANCE`, so you'll have to multiply the direction vector by `MAX_MOUSE_DISTANCE`. 1. Declare a variable named **directionVector** and assign it the value of `screenToWorldRay.Direction` multiplied by `MAX_MOUSE_DISTANCE`.```lua local function getWorldMousePosition() local mouseLocation = UserInputService:GetMouseLocation() -- Create a ray from the 2D mouseLocation local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y) -- The unit direction vector of the ray multiplied by a maximum distance local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE ``` 2. Call the `Class.WorldRoot:Raycast()|Raycast` function of workspace, passing the **Origin** property of `screenToWorldRay` as the first argument and `directionVector` as the second. Assign this to a variable named **raycastResult**.```lua local function getWorldMousePosition() local mouseLocation = UserInputService:GetMouseLocation() -- Create a ray from the 2D mouseLocation local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y) -- The unit direction vector of the ray multiplied by a maximum distance local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE -- Raycast from the ray's origin towards its direction local raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector) ``` ## Collision information If the raycast operation finds an object hit by the ray, it will return a `Datatype.RaycastResult`, which contains information about the collision between the ray and object. | RaycastResult Property | Description | | --- | --- | | **Instance** | The `Class.BasePart` or `Class.Terrain` cell that the ray intersected. | | **Position** | Where the intersection occurred; usually a point directly on the surface of a part or terrain. | | **Material** | The material at the collision point. | | **Normal** | The normal vector of the intersected face. This can be used to determine which way the face is pointing. | The **Position** property will be the position of the object that the mouse is hovering over. If the mouse isn't hovering over any object within a distance of `MAX_MOUSE_DISTANCE`, `raycastResult` will be `nil`. 1. Create an if statement to check whether `raycastResult` exists. 2. If `raycastResult` has a value, return its **Position** property. 3. If `raycastResult` is `nil` then find the end of the raycast. Calculate the 3D position of the mouse by adding `screenToWorldRay.Origin` and `directionVector` together. ```lua local function getWorldMousePosition() local mouseLocation = UserInputService:GetMouseLocation() -- Create a ray from the 2D mouseLocation local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y) -- The unit direction vector of the ray multiplied by a maximum distance local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE -- Raycast from the ray's origin towards its direction local raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector) if raycastResult then -- Return the 3D point of intersection return raycastResult.Position else -- No object was hit so calculate the position at the end of the ray return screenToWorldRay.Origin + directionVector end end ``` ## Fire towards the target Now that the 3D mouse position is known, it can be used as a **target position** to fire a laser towards. A second ray can be cast between the player's weapon and the target position using the **Raycast** function. 1. Declare a constant called `MAX_LASER_DISTANCE` at the top of the script and assign it to **500**, or your chosen range for the laser blaster.```lua local UserInputService = game:GetService("UserInputService") local tool = script.Parent local MAX_MOUSE_DISTANCE = 1000 local MAX_LASER_DISTANCE = 500 ``` 2. Create a function called **fireWeapon** under the `getWorldMousePosition` function. 3. Call `getWorldMousePosition` and assign the result to a variable named **mousePosition**. This will be the target position for raycast.```lua -- No object was hit so calculate the position at the end of the ray return screenToWorldRay.Origin + directionVector end end local function fireWeapon() local mouseLocation = getWorldMousePosition() end local function toolEquipped() tool.Handle.Equip:Play() end ``` This time, the direction vector for the raycast function will represent the direction from the player's tool position to the target location. 1. Declare a variable named **targetDirection** and calculate the direction vector by subtracting the tool position from `mouseLocation`. 2. Normalise the vector by using its **Unit** property. This gives it a magnitude of 1, which makes it easy to multiply by a length later.```lua local function fireWeapon() local mouseLocation = getWorldMousePosition() -- Calculate a normalized direction vector and multiply by laser distance local targetDirection = (mouseLocation - tool.Handle.Position).Unit end ``` 3. Declare a variable named **directionVector** and assign to it the `targetDirection` multiplied by the `MAX_LASER_DISTANCE`.```lua local targetDirection = (mouseLocation - tool.Handle.Position).Unit -- The direction to fire the weapon, multiplied by a maximum distance local directionVector = targetDirection * MAX_LASER_DISTANCE end ``` A `Datatype.RaycastParams` object can be used to store additional parameters for the raycast function. It will be used in your laser blaster to make sure the raycast doesn't accidentally collide with the player firing the weapon. Any parts included in the `Datatype.RaycastParams.FilterDescendantsInstances|FilterDescendantsInstances` property of a RaycastParams object will be **ignored** in the raycast. 1. Continue the `fireWeapon` function and declare a variable called **weaponRaycastParams**. Assign a new RaycastParams object to it. 2. Create a table containing the player's local **character** and assign it to the `weaponRaycastParams.FilterDescendantsInstances` property. 3. Raycast from the player's tool handle position, in a direction towards the `directionVector`. Remember to add `weaponRaycastParams` as an argument this time. Assign this to a variable named **weaponRaycastResult**. ```lua local UserInputService = game:GetService("UserInputService") local Players = game:GetService("Players") local tool = script.Parent local MAX_MOUSE_DISTANCE = 1000 local MAX_LASER_DISTANCE = 500 local function getWorldMousePosition() ``` ```lua local function fireWeapon() local mouseLocation = getWorldMousePosition() -- Calculate a normalized direction vector and multiply by laser distance local targetDirection = (mouseLocation - tool.Handle.Position).Unit -- The direction to fire the weapon multiplied by a maximum distance local directionVector = targetDirection * MAX_LASER_DISTANCE -- Ignore the player's character to prevent them from damaging themselves local weaponRaycastParams = RaycastParams.new() weaponRaycastParams.FilterDescendantsInstances = {Players.LocalPlayer.Character} local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams) end ``` Finally, you'll need to check that the raycast operation returned a value. If a value is returned, an object was hit by the ray and a laser can be created between the weapon and hit location. If nothing was returned, the final position needs to be calculated in order to create the laser. 1. Declare an empty variable named **hitPosition**. 2. Use an **if** statement to check whether `weaponRaycastResult` has a value. If an object was hit, assign `weaponRaycastResult.Position` to `hitPosition`.```lua local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams) -- Check if any objects were hit between the start and end position local hitPosition if weaponRaycastResult then hitPosition = weaponRaycastResult.Position end ``` 3. If `weaponRaycastResult` has no value, calculate the end position of the raycast by adding together the **position** of the tool handle with the `directionVector`. Assign this to **hitPosition**.```lua local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams) -- Check if any objects were hit between the start and end position local hitPosition if weaponRaycastResult then hitPosition = weaponRaycastResult.Position else -- Calculate the end position based on maximum laser distance hitPosition = tool.Handle.Position + directionVector end end ``` 4. Navigate to the `toolActivated` function and call the `fireWeapon` function so that the laser fires each time the tool is activated.```lua local function toolActivated() tool.Handle.Activate:Play() fireWeapon() end ``` ## Check the object hit To find if the object hit by the laser is part of a player's character or just a piece of scenery, you'll need to look for a `Class.Humanoid`, as every character has one. First, you'll need to find the **character model**. If a part of the character was hit, you cannot assume the parent of the object hit would be the character. The laser could have hit a body part, an accessory, or a tool, all of which are located in different parts of the character's hierarchy. You can use `Class.Instance:FindFirstAncestorOfClass()|FindFirstAncestorOfClass` to find a character model ancestor of the object hit by the laser, if one exists. If you find a model and it contains a humanoid, in most cases you can assume it's a character. > **Warning:** If you have other models in the experience that contain humanoids, further checks will be needed. 1. Add the highlighted code below to the `weaponRaycastResult` **if** statement to check if a character was hit.```lua -- Check if any objects were hit between the start and end position local hitPosition if weaponRaycastResult then hitPosition = weaponRaycastResult.Position -- The instance hit will be a child of a character model -- If a humanoid is found in the model then it's likely a player's character local characterModel = weaponRaycastResult.Instance:FindFirstAncestorOfClass("Model") if characterModel then local humanoid = characterModel:FindFirstChildWhichIsA("Humanoid") if humanoid then print("Player hit") end end else -- Calculate the end position based on maximum laser distance hitPosition = tool.Handle.Position + directionVector end ``` Now the laser blaster should print `Player hit` to the **Output** window every time the raycast operation hits another player. ## Test with multiple players Two players are needed to test if the weapon raycast is finding other players, so you need to start a local server. See [multi‑client simulation](/docs/en-us/studio/testing-modes.md#multi-client-simulation) for instructions. On one client, test shooting the other player with the weapon by clicking on them. "Player hit" should be displayed in the output each time a player is shot. ## Find the laser position The blaster should fire a red beam of light at its target. The function for this will be inside a `Class.ModuleScript` so it can be reused in other scripts later on. First, the script will need to find the position where the laser beam should be rendered. 1. Create a **ModuleScript** named **LaserRenderer**, parented to StarterPlayerScripts under StarterPlayer. 2. Open the script and rename the module table to the name of the script **LaserRenderer**. 3. Declare a variable named **SHOT_DURATION** with a value of **0.15**. This will be the amount of time (in seconds) the laser is visible for. 4. Create a function of LaserRenderer named **createLaser** with two parameters called **toolHandle** and **endPosition**.```lua local LaserRenderer = {} local SHOT_DURATION = 0.15 -- Time that the laser is visible for -- Create a laser beam from a start position towards an end position function LaserRenderer.createLaser(toolHandle, endPosition) end return LaserRenderer ``` 5. Declare a variable named **startPosition** and set the **Position** property of `toolHandle` as its value. This will be the position of the player's laser blaster. 6. Declare a variable named **laserDistance** and subtract `endPosition` from `startPosition` to find the difference between the two vectors. Use the **Magnitude** property of this to get the length of the laser beam.```lua function LaserRenderer.createLaser(toolHandle, endPosition) local startPosition = toolHandle.Position local laserDistance = (startPosition - endPosition).Magnitude end ``` 7. Declare a **laserCFrame** variable to store the position and orientation of the laser beam. The position needs to be the midpoint of the start and end of the beam. Use **CFrame.lookAt** to create a new `Datatype.CFrame` located at `startPosition` and facing towards `endPosition`. Multiply this by a new CFrame with a Z axis value of half of negative `laserDistance` to get the midpoint.```lua function LaserRenderer.createLaser(toolHandle, endPosition) local startPosition = toolHandle.Position local laserDistance = (startPosition - endPosition).Magnitude local laserCFrame = CFrame.lookAt(startPosition, endPosition) * CFrame.new(0, 0, -laserDistance / 2) end ``` ## Create the laser part Now that you know where to create a laser beam, you need to add the beam itself. This can be done easily with a Neon part. 1. Declare a variable **laserPart** and assign to it a new `Class.Part` instance. 2. Set the following properties of `laserPart`: 1. **Size**: Vector3.new(0.2, 0.2, laserDistance) 2. **CFrame**: laserCFrame 3. **Anchored**: true 4. **CanCollide**: false 5. **Color**: Color3.fromRGB(225, 0, 0) (a strong red color) 6. **Material**: Enum.Material.Neon 3. Parent `laserPart` to **Workspace**. 4. Add the part to the `Class.Debris` service so that it gets removed after the amount of seconds in the `SHOT_DURATION` variable.```lua function LaserRenderer.createLaser(toolHandle, endPosition) local startPosition = toolHandle.Position local laserDistance = (startPosition - endPosition).Magnitude local laserCFrame = CFrame.lookAt(startPosition, endPosition) * CFrame.new(0, 0, -laserDistance / 2) local laserPart = Instance.new("Part") laserPart.Size = Vector3.new(0.2, 0.2, laserDistance) laserPart.CFrame = laserCFrame laserPart.Anchored = true laserPart.CanCollide = false laserPart.Color = Color3.fromRGB(225, 0, 0) laserPart.Material = Enum.Material.Neon laserPart.Parent = workspace -- Add laser beam to the Debris service to be removed & cleaned up Debris:AddItem(laserPart, SHOT_DURATION) end ``` Now the function to render the laser beam is complete, it can be called by the **ToolController**. 1. At the top of the **ToolController** script, declare a variable named **LaserRenderer** and require the LaserRenderer ModuleScript located in PlayerScripts.```lua local UserInputService = game:GetService("UserInputService") local Players = game:GetService("Players") local LaserRenderer = require(Players.LocalPlayer.PlayerScripts.LaserRenderer) local tool = script.Parent ``` 2. At the bottom of the `fireWeapon` function, call the LaserRenderer `createLaser` function using the tool handle and `hitPosition` as arguments.```lua -- Calculate the end position based on maximum laser distance hitPosition = tool.Handle.Position + directionVector end LaserRenderer.createLaser(tool.Handle, hitPosition) end ``` 3. Test the weapon by clicking the Play button. A laser beam should be visible between the weapon and the mouse when the tool is activated. ## Control weapon fire rate Weapons need a delay between each shot to stop players from dealing too much damage in a short amount of time. This can be controlled by checking if enough time has passed since a player last fired. 1. Declare a variable at the top of the **ToolController** called **FIRE_RATE**. This will be the minimum time between each shot. Give it a value of your choosing; this example uses **0.3** seconds. 2. Declare another variable underneath called **timeOfPreviousShot** with a value of **0**. This stores the last time the player fired and will be updated with each shot.```lua local MAX_MOUSE_DISTANCE = 1000 local MAX_LASER_DISTANCE = 300 local FIRE_RATE = 0.3 local timeOfPreviousShot = 0 ``` 3. Create a function called **canShootWeapon** with no parameters. This function will look at how much time has passed since the previous shot and return true or false.```lua local FIRE_RATE = 0.3 local timeOfPreviousShot = 0 -- Check if enough time has passed since previous shot was fired local function canShootWeapon() end local function getWorldMousePosition() ``` 4. Inside the function declare a variable named **currentTime**; assign to it the result of calling the `Global.RobloxGlobals.tick()|tick()` function. This returns how much time has elapsed, in seconds, since the 1st of January 1970 (an arbitrary date widely used to calculate time). 5. Subtract the `timeOfPreviousShot` from `currentTime` and return **false** if the result is smaller than `FIRE_RATE`; otherwise, return **true**.```lua -- Check if enough time has passed since previous shot was fired local function canShootWeapon() local currentTime = tick() if currentTime - timeOfPreviousShot < FIRE_RATE then return false end return true end ``` 6. At the end of the `fireWeapon` function, update `timeOfPreviousShot` each time the weapon is fired using `tick`.```lua hitPosition = tool.Handle.Position + directionVector end timeOfPreviousShot = tick() LaserRenderer.createLaser(tool.Handle, hitPosition) end ``` 7. Inside the `toolActivated` function, create an **if** statement and call `canShootWeapon` to check if the weapon can be fired.```lua local function toolActivated() if canShootWeapon() then tool.Handle.Activate:Play() fireWeapon() end end ``` When you test the blaster you should find that no matter how fast you click, there will always be a short 0.3 seconds delay between each shot. ## Damage the player Clients cannot damage other clients directly; the server needs to be responsible for issuing damage when a player is hit. Clients can use a `Class.RemoteEvent` to tell the server that a character has been hit. These should be stored in **ReplicatedStorage**, where they are visible to both client and server. 1. Create a **Folder** in ReplicatedStorage named **Events**. 2. Insert a RemoteEvent into the Events folder and name it **DamageCharacter**. 3. In **ToolController**, create variables at the start of the script for ReplicatedStorage and the Events folder.```lua local UserInputService = game:GetService("UserInputService") local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") local LaserRenderer = require(Players.LocalPlayer.PlayerScripts.LaserRenderer) local tool = script.Parent local eventsFolder = ReplicatedStorage.Events local MAX_MOUSE_DISTANCE = 1000 local MAX_LASER_DISTANCE = 500 ``` 4. Replace the `"Player hit"` print statement in `fireWeapon` with a line of Luau to fire the **DamageCharacter** remote event with the `characterModel` variable as an argument.```lua local characterModel = weaponRaycastResult.Instance:FindFirstAncestorOfClass("Model") if characterModel then local humanoid = characterModel:FindFirstChildWhichIsA("Humanoid") if humanoid then eventsFolder.DamageCharacter:FireServer(characterModel) end end else -- Calculate the end position based on maximum laser distance hitPosition = tool.Handle.Position + directionVector end ``` The server needs to deal damage to the player who has been hit when the event is fired. 1. Insert a **Script** into ServerScriptService and name it **ServerLaserManager**. 2. Declare a variable named `LASER_DAMAGE` and set it to **10**, or a value of your choice. 3. Create a function named `damageCharacter` with two parameters called **playerFired** and **characterToDamage**. 4. Inside the function, find the character's Humanoid and subtract `LASER_DAMAGE` from its health. 5. Connect the `damageCharacter` function to the **DamageCharacter** remote event in the Events folder.```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local eventsFolder = ReplicatedStorage.Events local LASER_DAMAGE = 10 function damageCharacter(playerFired, characterToDamage) local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid") if humanoid then -- Remove health from character humanoid.Health -= LASER_DAMAGE end end -- Connect events to appropriate functions eventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter) ``` 6. Test the blaster with 2 players by starting a local server. When you shoot the other player, their health will decrease by the number assigned to `LASER_DAMAGE`. ## Render other player's laser beams Currently, the laser beam is created by the client firing the weapon, so only they will be able to see the laser beam. If the laser beam was created on the server then everyone would be able to see it. However, there would be a small **delay** between the client shooting the weapon and the server receiving the information about the shot. This would mean the client shooting the weapon would see a delay between when they activate the weapon and when they see the laser beam; the weapon would feel laggy as a result. To solve this issue, every client will create their own laser beams. This means the client shooting the weapon will see the laser beam instantly. Other clients will experience a small delay between when another player shoots and a beam appears. This is the best case scenario: there is no way to communicate one client's laser to other clients any faster. ### Shooter's client First, the client needs to tell the server it has fired a laser and provide the end position. 1. Insert a **RemoteEvent** into the Events folder in ReplicatedStorage and name it **LaserFired**. 2. Locate the `fireWeapon` function in the **ToolController** script. At the end of the function, fire the **LaserFired** remote event using `hitPosition` as an argument.```lua hitPosition = tool.Handle.Position + directionVector end timeOfPreviousShot = tick() eventsFolder.LaserFired:FireServer(hitPosition) LaserRenderer.createLaser(tool.Handle, hitPosition) end ``` ### The server The server now must receive the event that the client has fired and tell all clients the start and end position of the laser beam so they can also render it. 1. In the **ServerLaserManager** script, create a function named **playerFiredLaser** above `damageCharacter` with two parameters called `playerFired` and `endPosition`. 2. Connect the function to the **LaserFired** remote event.```lua -- Notify all clients that a laser has been fired so they can display the laser local function playerFiredLaser(playerFired, endPosition) end ``````lua -- Connect events to appropriate functions eventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter) eventsFolder.LaserFired.OnServerEvent:Connect(playerFiredLaser) ``` The server needs the start position of the laser. This could be sent from the client, but it's best to avoid trusting the client where possible. The character's weapon handle position will be the start position, so the server can find it from there. 1. Create a function **getPlayerToolHandle** above the `playerFiredLaser` function with a parameter called `player`. 2. Use the following code to search the player's character for the weapon and return the handle object.```lua local LASER_DAMAGE = 10 -- Find the handle of the tool the player is holding local function getPlayerToolHandle(player) local weapon = player.Character:FindFirstChildOfClass("Tool") if weapon then return weapon:FindFirstChild("Handle") end end -- Notify all clients that a laser has been fired so they can display the laser local function playerFiredLaser(playerFired, endPosition) ``` The server can now call **FireAllClients** on the **LaserFired** remote event to send the information required to render the laser to the clients. This includes the **player** who fired the laser (so the client for that player does not render the laser twice), the **handle** of the blaster (which acts as a starting position for the laser) and the end position of the laser. 1. In the `playerFiredLaser` function, call the `getPlayerToolHandle` function with `playerFired` as an argument and assign the value to a variable named **toolHandle**. 2. If **toolHandle** exists, fire the LaserFired event for all clients using `playerFired`, `toolHandle` and `endPosition` as arguments.```lua -- Notify all clients that a laser has been fired so they can display the laser local function playerFiredLaser(playerFired, endPosition) local toolHandle = getPlayerToolHandle(playerFired) if toolHandle then eventsFolder.LaserFired:FireAllClients(playerFired, toolHandle, endPosition) end end ``` ### Render on the clients Now **FireAllClients** has been called, each client will receive an event from the server to render a laser beam. Each client can reuse the **LaserRenderer** module from earlier to render the laser beam using the tool's handle position and end position value sent by the server. The player that fired the laser beam in the first place should ignore this event otherwise they'll see 2 lasers. 1. Create a **LocalScript** in StarterPlayerScripts called **ClientLaserManager**. 2. Inside the script, require the **LaserRenderer** module. 3. Create a function named **createPlayerLaser** with the parameters `playerWhoShot`, `toolHandle` and `endPosition`. 4. Connect the function to the **LaserFired** remote event in the Events folder. 5. In the function, use an **if** statement to check if `playerWhoShot` does **not equal** the LocalPlayer. 6. Inside the if statement, call the `createLaser` function from the LaserRenderer module using `toolHandle` and `endPosition` as arguments.```lua local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") local LaserRenderer = require(script.Parent:WaitForChild("LaserRenderer")) local eventsFolder = ReplicatedStorage.Events -- Display another player's laser local function createPlayerLaser(playerWhoShot, toolHandle, endPosition) if playerWhoShot ~= Players.LocalPlayer then LaserRenderer.createLaser(toolHandle, endPosition) end end eventsFolder.LaserFired.OnClientEvent:Connect(createPlayerLaser) ``` 7. Test the blaster with 2 players by starting a local server. Position each client on different sides of your monitor so you can see both windows at once. When you shoot on one client you should see the laser on the other client. ## Sound effects The shooting sound effect currently only plays on the client that's shooting the projectile. You'll need to move the code to play the sound so that other players will hear it too. 1. In the **ToolController** script, navigate to the **toolActivated** function and remove the line which plays the Activate sound.```lua local function toolActivated() if canShootWeapon() then fireWeapon() end end ``` 2. At the bottom of the `createLaser` function in **LaserRenderer**, declare a variable named **shootingSound** and use the `Class.Instance:FindFirstChild()|FindFirstChild()` method of `toolHandle` to check for the **Activate** sound. 3. Use an **if** statement to check if `shootingSound` exists; if it does, call its `Play()` function.```lua laserPart.Parent = workspace -- Add laser beam to the Debris service to be removed & cleaned up Debris:AddItem(laserPart, SHOT_DURATION) -- Play the weapon's shooting sound local shootingSound = toolHandle:FindFirstChild("Activate") if shootingSound then shootingSound:Play() end end ``` ## Secure remotes using validation If the server isn't checking data from incoming requests, a hacker can abuse remote functions and events and use them to send fake values to the server. It's important to use **server-side validation** to prevent this. In its current form, the **DamageCharacter** remote event is very vulnerable to attack. Hackers could use this event to damage any player they want without shooting them. Validation is the process of checking that the values being sent to the server are realistic. In this case, the server will need to: - Check if the distance between the player and the position hit by the laser is within a certain boundary. - Raycast between the weapon that fired the laser and the hit position to make sure the shot was possible and didn't go through any walls. ### Client The client needs to send the server the position hit by the raycast so it can check the distance is realistic. 1. In **ToolController**, navigate to the line where the DamageCharacter remote event is fired in the `fireWeapon` function. 2. Add `hitPosition` as an argument.```lua if characterModel then local humanoid = characterModel:FindFirstChildWhichIsA("Humanoid") if humanoid then eventsFolder.DamageCharacter:FireServer(characterModel, hitPosition) end end ``` ### Server The client is now sending an extra parameter through the DamageCharacter remote event, so the **ServerLaserManager** needs to be adjusted to accept it. 1. In the **ServerLaserManager** script, add a `hitPosition` parameter to the `damageCharacter` function.```lua function damageCharacter(playerFired, characterToDamage, hitPosition) local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid") if humanoid then -- Remove health from character humanoid.Health -= LASER_DAMAGE end end ``` 2. Below the `getPlayerToolHandle` function, create a function named **isHitValid** with three parameters: `playerFired`, `characterToDamage` and `hitPosition`.```lua end local function isHitValid(playerFired, characterToDamage, hitPosition) end ``` The first check will be the distance between the hit position and the character hit. 1. Declare a variable named **MAX_HIT_PROXIMITY** at the top of the script and assign it a value of **10**. This will be the maximum distance allowed between the hit and character. A tolerance is needed because the character may have moved slightly since the client fired the event.```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local eventsFolder = ReplicatedStorage.Events local LASER_DAMAGE = 10 local MAX_HIT_PROXIMITY = 10 ``` 2. In the `isHitValid` function, calculate the distance between the character and the hit position. If the distance is larger than `MAX_HIT_PROXIMITY` then return **false**.```lua local function isHitValid(playerFired, characterToDamage, hitPosition) -- Validate distance between the character hit and the hit position local characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitude if characterHitProximity > MAX_HIT_PROXIMITY then return false end end ``` The second check will involve a raycast between the weapon fired and the hit position. If the raycast returns an object that isn't the character, you can assume the shot wasn't valid since something was blocking the shot. 1. Copy the code below to perform this check. Return **true** at the end of the function: if it reaches the end, all checks have passed.```lua local function isHitValid(playerFired, characterToDamage, hitPosition) -- Validate distance between the character hit and the hit position local characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitude if characterHitProximity > 10 then return false end -- Check if shooting through walls local toolHandle = getPlayerToolHandle(playerFired) if toolHandle then local rayLength = (hitPosition - toolHandle.Position).Magnitude local rayDirection = (hitPosition - toolHandle.Position).Unit local raycastParams = RaycastParams.new() raycastParams.FilterDescendantsInstances = {playerFired.Character} local rayResult = workspace:Raycast(toolHandle.Position, rayDirection * rayLength, raycastParams) -- If an instance was hit that was not the character then ignore the shot if rayResult and not rayResult.Instance:IsDescendantOf(characterToDamage) then return false end end return true end ``` 2. Declare a variable in the `damageCharacter` function called **validShot**. Assign to it the result of a call to the `isHitValid` function with three arguments: `playerFired`, `characterToDamage` and `hitPosition`. 3. In the below if statement, add an **and** operator to check if `validShot` is **true**.```lua function damageCharacter(playerFired, characterToDamage, hitPosition) local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid") local validShot = isHitValid(playerFired, characterToDamage, hitPosition) if humanoid and validShot then -- Remove health from character humanoid.Health -= LASER_DAMAGE end end ``` Now the damageCharacter remote event is more secure and will prevent most players from abusing it. Note that some malicious players will often find ways around validation; keeping remote events secure is a continuous effort. Your laser blaster is now complete, with a basic hit detection system using raycasting. Try the [Detecting User Input](/docs/en-us/tutorials/use-case-tutorials/input-and-camera/detect-user-input.md) tutorial to find out how you can add a reloading action to your laser blaster, or create a fun map and try your laser blaster out with other players! ## Final code ### ToolController ```lua local UserInputService = game:GetService("UserInputService") local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") local LaserRenderer = require(Players.LocalPlayer.PlayerScripts.LaserRenderer) local tool = script.Parent local eventsFolder = ReplicatedStorage.Events local MAX_MOUSE_DISTANCE = 1000 local MAX_LASER_DISTANCE = 500 local FIRE_RATE = 0.3 local timeOfPreviousShot = 0 -- Check if enough time has passed since previous shot was fired local function canShootWeapon() local currentTime = tick() if currentTime - timeOfPreviousShot < FIRE_RATE then return false end return true end local function getWorldMousePosition() local mouseLocation = UserInputService:GetMouseLocation() -- Create a ray from the 2D mouse location local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y) -- The unit direction vector of the ray multiplied by a maximum distance local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE -- Raycast from the roy's origin towards its direction local raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector) if raycastResult then -- Return the 3D point of intersection return raycastResult.Position else -- No object was hit so calculate the position at the end of the ray return screenToWorldRay.Origin + directionVector end end local function fireWeapon() local mouseLocation = getWorldMousePosition() -- Calculate a normalized direction vector and multiply by laser distance local targetDirection = (mouseLocation - tool.Handle.Position).Unit -- The direction to fire the weapon, multiplied by a maximum distance local directionVector = targetDirection * MAX_LASER_DISTANCE -- Ignore the player's character to prevent them from damaging themselves local weaponRaycastParams = RaycastParams.new() weaponRaycastParams.FilterDescendantsInstances = {Players.LocalPlayer.Character} local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams) -- Check if any objects were hit between the start and end position local hitPosition if weaponRaycastResult then hitPosition = weaponRaycastResult.Position -- The instance hit will be a child of a character model -- If a humanoid is found in the model then it's likely a player's character local characterModel = weaponRaycastResult.Instance:FindFirstAncestorOfClass("Model") if characterModel then local humanoid = characterModel:FindFirstChildWhichIsA("Humanoid") if humanoid then eventsFolder.DamageCharacter:FireServer(characterModel, hitPosition) end end else -- Calculate the end position based on maximum laser distance hitPosition = tool.Handle.Position + directionVector end timeOfPreviousShot = tick() eventsFolder.LaserFired:FireServer(hitPosition) LaserRenderer.createLaser(tool.Handle, hitPosition) end local function toolEquipped() tool.Handle.Equip:Play() end local function toolActivated() if canShootWeapon() then fireWeapon() end end tool.Equipped:Connect(toolEquipped) tool.Activated:Connect(toolActivated) ``` ### LaserRenderer ```lua local LaserRenderer = {} local Debris = game:GetService("Debris") local SHOT_DURATION = 0.15 -- Time that the laser is visible for -- Create a laser beam from a start position towards an end position function LaserRenderer.createLaser(toolHandle, endPosition) local startPosition = toolHandle.Position local laserDistance = (startPosition - endPosition).Magnitude local laserCFrame = CFrame.lookAt(startPosition, endPosition) * CFrame.new(0, 0, -laserDistance / 2) local laserPart = Instance.new("Part") laserPart.Size = Vector3.new(0.2, 0.2, laserDistance) laserPart.CFrame = laserCFrame laserPart.Anchored = true laserPart.CanCollide = false laserPart.Color = Color3.fromRGB(255, 0, 0) laserPart.Material = Enum.Material.Neon laserPart.Parent = workspace -- Add laser beam to the Debris service to be removed & cleaned up Debris:AddItem(laserPart, SHOT_DURATION) -- Play the weapon's shooting sound local shootingSound = toolHandle:FindFirstChild("Activate") if shootingSound then shootingSound:Play() end end return LaserRenderer ``` ### ServerLaserManager ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local eventsFolder = ReplicatedStorage.Events local LASER_DAMAGE = 10 local MAX_HIT_PROXIMITY = 10 -- Find the handle of the tool the player is holding local function getPlayerToolHandle(player) local weapon = player.Character:FindFirstChildOfClass("Tool") if weapon then return weapon:FindFirstChild("Handle") end end local function isHitValid(playerFired, characterToDamage, hitPosition) -- Validate distance between the character hit and the hit position local characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitude if characterHitProximity > MAX_HIT_PROXIMITY then return false end -- Check if shooting through walls local toolHandle = getPlayerToolHandle(playerFired) if toolHandle then local rayLength = (hitPosition - toolHandle.Position).Magnitude local rayDirection = (hitPosition - toolHandle.Position).Unit local raycastParams = RaycastParams.new() raycastParams.FilterDescendantsInstances = {playerFired.Character} local rayResult = workspace:Raycast(toolHandle.Position, rayDirection * rayLength, raycastParams) -- If an instance was hit that was not the character then ignore the shot if rayResult and not rayResult.Instance:IsDescendantOf(characterToDamage) then return false end end return true end -- Notify all clients that a laser has been fired so they can display the laser local function playerFiredLaser(playerFired, endPosition) local toolHandle = getPlayerToolHandle(playerFired) if toolHandle then eventsFolder.LaserFired:FireAllClients(playerFired, toolHandle, endPosition) end end function damageCharacter(playerFired, characterToDamage, hitPosition) local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid") local validShot = isHitValid(playerFired, characterToDamage, hitPosition) if humanoid and validShot then -- Remove health from character humanoid.Health -= LASER_DAMAGE end end -- Connect events to appropriate functions eventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter) eventsFolder.LaserFired.OnServerEvent:Connect(playerFiredLaser) ``` ### ClientLaserManager ```lua local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") local LaserRenderer = require(Players.LocalPlayer.PlayerScripts:WaitForChild("LaserRenderer")) local eventsFolder = ReplicatedStorage.Events -- Display another player's laser local function createPlayerLaser(playerWhoShot, toolHandle, endPosition) if playerWhoShot ~= Players.LocalPlayer then LaserRenderer.createLaser(toolHandle, endPosition) end end eventsFolder.LaserFired.OnClientEvent:Connect(createPlayerLaser) ``` --- title: "Save data" url: /docs/en-us/tutorials/use-case-tutorials/scripting/intermediate-scripting/save-data last_updated: 2026-06-29T19:34:15Z description: "The process for creating a basic data store to save, store, and read data back." --- # Save data Games often need to store **persistent data** between sessions, such as a player's level, game points, money, inventory items, location, and more. This tutorial shows how to create a basic **data store**, save player data, and read the data back into a player session. ## Enable Studio access By default, games tested in Studio cannot access data stores, so you must first enable them: 1. [Publish](/docs/en-us/production/publishing/publish-games-and-places.md) the game. 2. Open Studio's **File** ⟩ **Experience Settings** window. 3. In the **Security** section, turn on **Enable Studio Access to API Services** and click **Save**. ## Create a data store Data stores require a unique **name**. This example creates a data store named **PlayerGold** that saves each player's gold in persistent storage: 1. Create a `Class.Script` within `Class.ServerScriptService` called **GoldManager**. 2. Data stores are managed by `Class.DataStoreService`, so get the service:```lua local DataStoreService = game:GetService("DataStoreService") ``` 3. Call `Class.DataStoreService:GetDataStore()` with the string `"PlayerGold"`. This method accesses the **PlayerGold** data store if it already exists. If it doesn't exist, the method creates a new data store and names it **PlayerGold**.```lua local DataStoreService = game:GetService("DataStoreService") local goldStore = DataStoreService:GetDataStore("PlayerGold") ``` ## Save data A data store is essentially a dictionary, like a Luau table. Each value in the data store is indexed by a unique **key**, which might be the player's unique `Class.Player.UserId|UserId` or simply a named string. #### Player Data Example | Key | Value | | --- | --- | | `31250608` | `50` | | `351675979` | `20` | | `505306092` | `78000` | #### Promo Examples | Key | Value | | --- | --- | | `ActiveSpecialEvent` | `SummerParty2` | | `ActivePromoCode` | `BONUS123` | | `CanAccessPartyPlace` | `true` | To save player data in the data store: 1. Create a variable named `playerUserID` for the data store key. Then, use `playerGold` to store a player's starting gold amount.```lua local DataStoreService = game:GetService("DataStoreService") local goldStore = DataStoreService:GetDataStore("PlayerGold") -- Data store key and value local playerUserID = 505306092 local playerGold = 250 ``` 2. To save data into the `PlayerGold` data store, call `Class.GlobalDataStore:SetAsync()|SetAsync` within a protected call, passing the key and value variables previously created.```lua local DataStoreService = game:GetService("DataStoreService") local goldStore = DataStoreService:GetDataStore("PlayerGold") -- Data store key and value local playerUserID = 505306092 local playerGold = 250 -- Set data store key local success, error = pcall(function() goldStore:SetAsync(playerUserID, playerGold) end) if not success then warn(error) end ``` Functions like `Class.GlobalDataStore:SetAsync()|SetAsync()` are network calls that may occasionally fail. As shown above, `Global.LuaGlobals.pcall()` is used to detect and handle when such failures occur. In its most basic form, `Global.LuaGlobals.pcall()` accepts a function and returns two values: - The status, which is `true` if the function executed without errors, or `false` otherwise. - The return value of the function or an error message. The sample above checks status on line 13. If `Class.GlobalDataStore:SetAsync()|SetAsync()` fails for any reason, the sample displays the error in the [Output](/docs/en-us/studio/output.md) window. > **Warning:** Be careful to not send requests to data stores too often. Requests on a data store key are placed in a queue and, if the queue fills up, additional requests are [dropped](/docs/en-us/cloud-services/data-stores.md#error-codes). > > A common mistake may be updating a player's gold data every time they collect a gold piece. Instead, store the player's gold in a variable and only update the data store occasionally, such as with a periodic auto-save and/or when the player leaves the game. ## Read data To read data from a data store, call `Class.GlobalDataStore:GetAsync()|GetAsync()` with the desired key name. ```lua local DataStoreService = game:GetService("DataStoreService") local goldStore = DataStoreService:GetDataStore("PlayerGold") -- Data store key and value local playerUserID = 505306092 local playerGold = 250 -- Set data store key local setSuccess, errorMessage = pcall(function() goldStore:SetAsync(playerUserID, playerGold) end) if not setSuccess then warn(errorMessage) end -- Read data store key local getSuccess, currentGold = pcall(function() return goldStore:GetAsync(playerUserID) end) if getSuccess then print(currentGold) end ``` To test the script, [initiate a playtest](/docs/en-us/studio/testing-modes.md#playtesting) and notice the `currentGold` value printed to the [Output](/docs/en-us/studio/output.md) window. Note that it may take a couple seconds, as the functions must connect to data store servers. ## Read and save automatically The previous script works, but has a fundamental problem: it includes hard-coded values for `playerUserID` and `playerGold`, so it doesn't support multiple players with different amounts of gold. A more realistic solution reads the gold value when the player connects to the game and then saves it when the player leaves. This approach means connecting the data store calls to [events](/docs/en-us/scripting/events.md) from the `Class.Players` service. ```lua local DataStoreService = game:GetService("DataStoreService") local Players = game:GetService("Players") local goldStore = DataStoreService:GetDataStore("PlayerGold") -- Add gold values for each player to a local table to avoid hitting the data -- store repeatedly. local playerGold = {} local function incrementGold(player, amount) playerGold[player.UserId] += amount end local function onPlayerAdded(player) -- Read data store key local success, storedGold = pcall(function() return goldStore:GetAsync(player.UserId) end) if success then local currentGold if storedGold then currentGold = storedGold else currentGold = 0 end playerGold[player.UserId] = currentGold print(currentGold) end -- Test incrementing gold incrementGold(player, 5) end local function onPlayerRemoving(player) -- Set data store key local success, err = pcall(function() goldStore:SetAsync(player.UserId, playerGold[player.UserId]) end) if not success then warn(err) end -- Clean up entry so that the table doesn't grow for the lifespan of the server playerGold[player.UserId] = nil end Players.PlayerAdded:Connect(onPlayerAdded) Players.PlayerRemoving:Connect(onPlayerRemoving) ``` ## Read and save character position To save player position, you work with the `Class.Player.Character|Character` rather than the `Class.Player`, but the principle is similar. This time, create a `Class.Script` within `Class.ServerScriptService` called **PositionManager**: ```lua local Players = game:GetService("Players") local DataStoreService = game:GetService("DataStoreService") local Workspace = game:GetService("Workspace") local playerPositionStore = DataStoreService:GetDataStore("PlayerPositionStore") local function positionHandler(player) -- Load position on character add player.CharacterAdded:Connect(function(character) local success, coords = pcall(function() return playerPositionStore:GetAsync(player.UserId) end) local position = Vector3.new(coords[1], coords[2], coords[3]) if success and position then character:PivotTo(CFrame.new(position)) print("Loaded player position!") else warn("Failed to load position for player " .. player.Name .. ". Placing in default position.") end -- Handle player respawn on death local humanoid = character:FindFirstChildOfClass("Humanoid") humanoid.Died:Connect(function() local spawnLocation = Workspace:FindFirstChild("SpawnLocation") character:PivotTo(spawnLocation.CFrame) end) end) -- Save position on character removal player.CharacterRemoving:Connect(function(character) local position = character:GetPivot().Position local success, err = pcall(function() playerPositionStore:SetAsync(player.UserId, {position.X, position.Y, position.Z}) print("Saved player position!") end) if not success then warn("Failed to save position for player " .. player.Name .. ": " .. err) end end) end Players.PlayerAdded:Connect(positionHandler) ``` This script adds a new data store, `playerPositionStore`. Because data stores only store basic types rather than objects, you have to store X, Y, and Z coordinates as individual numbers rather than a single `Datatype.Vector3` or `Datatype.CFrame` value. As you test your game, move your character around. Note how your character returns to the same position the next time you test your game. ## Sample project Now that you understand basic data store usage, test it out in the [Gold Rush](https://www.roblox.com/games/5268331031/Gold-Rush) sample game. You can also edit the game in Studio and explore the enhanced **GoldManager** script, which displays gold as part of the UI and includes auto-saving. --- title: "Create score bars" url: /docs/en-us/tutorials/use-case-tutorials/ui/create-a-score-bar last_updated: 2026-06-29T19:34:15Z description: "The process for creating a score bar UI that displays current player information." --- # Create score bars A **score bar** is a UI element that displays player information that is important for your experience's gameplay, such as their leveling up statistics, currency count, or power-up items in their inventory. By displaying score bars directly on the player's screen, you can keep their attention on what they need in order to accomplish various goals within your experience. Using the [Gold Rush](https://www.roblox.com/games/5268331031/Gold-Rush) `.rbxl` file as a reference, this tutorial shows you how to create a score bar that tracks the amount of gold players collect, including guidance on: - Creating a frame in the top-center of the screen. - Adding a crown icon that communicates what the score bar is tracking without any textual guidance. - Inserting score text that records the amount of gold the player collects. - Testing your UI design on multiple emulated devices to review its appearance on different screens and aspect ratios. > **Info:** As with all 2D creation, there are many ways to achieve any particular goal. You can create your own 2D assets in third-party UI tools and follow along with your own design. ## Create the frame To display UI elements on every player's screen, you can create a `Class.ScreenGui` object in the `Class.StarterGui` service. `Class.ScreenGui` objects are the primary containers for on-screen UI, and the `Class.StarterGui` service copies its contents to each player's `Class.PlayerGui` container as they enter an experience. After you create a `Class.ScreenGui` object, you can create and customize its child `Class.GuiObject|GuiObjects` according to each container's purpose. To demonstrate this concept, this section teaches you how to create a `Class.ScreenGui` object with a child `Class.Frame` object that will contain both the icon and text of the score bar. In addition to customizing the frame's properties, this section also provides instructions on adding a child `Class.UISizeConstraint` and `Class.UIListLayout` object to the frame. This technique ensures that `Class.GuiObject|GuiObjects` automatically arrange horizontally as you insert them into the frame, and that they are always legible on smaller screen sizes. If you don't follow this guidance, every `Class.GuiObject` you add to the frame will arrange outside of the frame's perimeter. To recreate the frame container within the sample [Gold Rush](https://www.roblox.com/games/5268331031/Gold-Rush) place file: 1. Create a `Class.ScreenGui` object to contain your on-screen UI. 1. In the **Explorer** window, hover over **StarterGui** and click the ⊕ icon. A contextual menu displays. 2. Insert a **ScreenGui**. 2. Create a container for the entire score bar UI component. 1. Insert a **Frame** into the **ScreenGui** object. 2. Select the new **Frame**, then in the **Properties** window, 1. Set **AnchorPoint** to `0.5, 0` to set the frame's origin point in the top-middle of itself (50% from the left to the right of the frame, and 0% from the top to the bottom of the frame). 2. Set **BackgroundColor** to `0.6` to make the frame's background black. 3. Set **BackgroundTransparency** to `0.6` to make the frame's background semi-transparent. 4. Set **Position** to `{0.5, 0},{0.01, 0}` to set the frame near the top-middle of the screen (50% from the left to the right of the screen, and 1% from the top to the bottom of the screen). 5. Set **Size** to `{0.25, 0},{0.08, 0}` so the frame takes up a large portion of the middle of the screen to grab the player's attention (25% horizontally, and 8% vertically). 6. Set **Name** to **ScoreBarFrame**. 3. Add a constraint to the frame so that its contents are always legible on small screen sizes. 1. Insert a **UISizeConstraint** object into **ScoreBarFrame**. 2. Select the new constraint, then in the **Properties** window, set **MinSize** to `0, 40` to ensure the frame never shrinks to less than 40 pixels vertically. 4. Add a layout object to the frame so that its contents arrange from left-to-right and vertically center within the frame's perimeter. 1. Insert a **UIListLayout** object into **ScoreBarFrame**. 2. Select the new layout object, then in the **Properties** window, 1. Set **FillDirection** to **Horizontal**. 2. Set **VerticalAlignment** to **Center**. ## Add an icon An icon is a symbol that represents an action, object, or concept in an experience. Using icons that are simple and intuitive allows players to easily recognize what you're communicating with your UI without using text, which can clutter the screen and pull attention away from content that matters. For example, the sample uses a simple gold crown icon to indicate how much gold a player has collected. This icon is easily recognizable as being the most important goal within the experience, and it includes minimal details so that it remains legible on mobile device screens. To recreate the gold crown icon within the sample [Gold Rush](https://www.roblox.com/games/5268331031/Gold-Rush) place file: 1. Insert an **ImageLabel** object into **ScoreBarFrame**. 1. In the **Explorer** window, hover over **ScoreBarFrame** and click the ⊕ icon. A contextual menu displays. 2. Insert an **ImageLabel**. 2. Select the new label, then in the **Properties** window, 1. Set **Image** to `rbxassetid://5673786644` to make the icon a crown. 2. Set **BackgroundTransparency** to `1` to make the label's background completely transparent. 3. Set **LayoutOrder** to `1`. This ensures the icon remains the first GuiObject in the frame from left-to-right when you insert text in the next section of the tutorial. 4. Set **Size** to `{1.25,0},{1,0}` to widen the label area beyond the full width of the frame. 5. Set **SizeConstraint** to **RelativeYY** to preserve the icon's aspect ratio by scaling the size of the label with the height of the parent frame. ## Insert score text Score text records the player's score within an experience, such as how many points they earn within a match. It's important that all UI text is both clear and easy to read so players can quickly understand the information they need to be successful within your experience. For example, the sample uses large text on top of a contrasting color so that it doesn't blend in with the noise of the background. This is particularly significant for accessibility because it ensures the text remains legible as the player moves through the 3D space, which may include objects that are the same color as the text. To recreate the score text within the sample [Gold Rush](https://www.roblox.com/games/5268331031/Gold-Rush) place file: 1. Insert a **TextLabel** object into **ScoreBarFrame**. 1. In the **Explorer** window, hover over **ScoreBarFrame** and click the ⊕ icon. A contextual menu displays. 2. Insert a **TextLabel**. 2. Select the new label, then in the **Properties** window, 1. Set **BackgroundTransparency** to `1` to make the label's background completely transparent. 2. Set **Size** to `{1,0},{1,0}` to widen the label to the whole frame (100% horizontally and 100% vertically of the parent frame). The label extends beyond the frame's boundaries because it's being offset by the icon. 3. Set **SizeConstraint** to **RelativeYY** to ensure the size of the label scales with the height of the parent frame, and preserve the icon's aspect ratio. This step also makes the label a square and keeps it within the frame's boundaries. 4. Set **Font** to **GothamSSm** to match the aesthetics of the environment. 5. Set **Text** to `0` to start the score from zero. 6. Set **TextColor3** to `255, 200, 100` to tint the text gold. 7. Set **TextSize** to `30` to make the text bigger on the screen. 8. Set **TextXAlignment** to **Left** to ensure the score text remains left-aligned near the crown icon regardless of whether the player's score is 0, 1,000, or 1,000,000. ## Test the design Studio's [Device Emulator](/docs/en-us/studio/testing-modes.md#device-emulation) allows you to test how players will see and interact with your UI on various devices. This tool is a vital part of designing UI because the aspect ratio of your viewport in Studio doesn't necessarily reflect the aspect ratio of the screens players use to access your experience, and it's important that your UI is both legible and accessible on every device. For example, if you don't test your UI on a range of screen sizes, players with large screens may not be able to read your text or decipher your icons, and players with small screens may not be able to see the 3D space because your UI elements take up too much room on the display. To emulate your UI on various screen sizes: 1. From Studio's **Test** menu, toggle on **Device Emulator**. 2. In the resolution dropdown, select **Actual Resolution**. This allows you to see the true resolution of your UI elements on the device you're emulating.![Device Emulator settings options indicated at top of viewport window.](../../../assets/tutorials/creating-a-score-bar/Device-Emulator-Setup.png) 3. In the device dropdown, select at least one device within the **Phone**, **Tablet**, **Desktop**, and **Console** sections. --- title: "Create HUD meters" url: /docs/en-us/tutorials/use-case-tutorials/ui/create-hud-meters last_updated: 2026-06-29T19:34:15Z description: "Learn how to build a custom HUD meter to replace the default character health meter." --- # Create HUD meters A **HUD** or **Heads-Up Display** is a set of UI elements that are always visible or accessible during gameplay, such as score displays, health meters, and menu buttons. Including a HUD is essential for most experiences because it displays information that helps players to be successful in their gameplay objectives. A common HUD element is a health meter with an icon on the left which can be adapted into a timer bar, progress bar, or similar. ![In-game view showing custom health meter in upper-right region.](../../../assets/tutorials/creating-hud-meters/Intro.png) Using [Hazardous Space Station](https://www.roblox.com/games/99416825187098/Hazardous-Space-Station) as a starting place and [UI Fundamentals - HUD Meter](https://www.roblox.com/games/104506915856758/UI-Fundamentals-HUD-Meter) as a finished reference place, this tutorial demonstrates: - Setup and use of the **Device Emulator** to test your design on multiple emulated screens. - Usage of `Class.StarterGui` as both a design and storage container. - How to position/size UI elements around built‑in Roblox controls and device notches/islands, such as the camera notch on modern phones. - How to replace the default Roblox health meter with your own meter and hook it up to the character's health level. - How to animate the center portion of the health meter and set its color between five color gradient keypoints (red, orange, yellow, lime, green). ## Enable the Device Emulator Roblox is inherently [cross‑platform](/docs/en-us/projects/cross-platform.md), as players can discover and join experiences on their phone or tablet, then later continue where they left off on their PC or console. Mobile devices (phones and tablets) have the least amount of screen space, so it's important that your UI elements fit on smaller screens and that they're clearly visible to players. The best way to test UI designs across platforms is Studio's [Device Emulator](/docs/en-us/studio/testing-modes.md#device-emulation). This tool provides a preset selection of devices and allows you to add your own custom presets. 1. Open the [Hazardous Space Station](https://www.roblox.com/games/99416825187098/Hazardous-Space-Station) template in Studio. 2. From Studio's **Test** menu, toggle on **Device Emulator**. 3. From the bar directly above the main viewport, select a phone emulation such as **iPhone X** or **Samsung Galaxy A51**. Then, set the view size to **Fit to Window** to utilize the maximum space in Studio.![Device Emulator settings options indicated at top of viewport window.](../../../assets/studio/general/Device-Emulator-Phone.png) ## Create a screen container A `Class.ScreenGui` container holds UI objects (`Class.GuiObject|GuiObjects`) to display on a player's screen (in this tutorial, the entirety of the health meter). To display a `Class.ScreenGui` and its child objects to every player who joins the experience, place it inside the `Class.StarterGui` container. When a player joins and their character first spawns, the `Class.ScreenGui` and its contents clone into the `Class.PlayerGui` container for that player, located within the `Class.Players` container. ![Diagram of how a ScreenGui clones from StarterGui to a player's PlayerGui](../../../assets/ui/ui-objects/StarterGui-To-PlayerGui.png) To insert an empty `Class.ScreenGui`: 1. In the **Explorer** window, locate the `Class.StarterGui` container.![Explorer window showing the StarterGui container.](../../../assets/studio/explorer/StarterGui.png) 2. Hover over the container, click the ⊕ button, and insert a `Class.ScreenGui`.![ScreenGui inserted into the StarterGui container.](../../../assets/studio/explorer/StarterGui-ScreenGui.png) 3. Rename the new container **HUDContainer** to reflect its purpose.![ScreenGui renamed to HUDContainer.](../../../assets/tutorials/creating-hud-meters/StarterGui-HUDContainer.png) ### Utilize safe areas Modern phones take advantage of the entire screen but typically include notches, cutouts, and other elements that occupy screen space. Every Roblox experience also includes the **top bar controls** for quick access to the main menu, [chat](/docs/en-us/chat/in-experience-text-chat.md), [leaderboard](/docs/en-us/players/leaderboards.md), and more. ![Mobile device showing Roblox top bar buttons and device cutout.](../../../assets/engine-api/enums/ScreenInsets/Top-Bar-Cutout.png) To ensure players can see and access all UI without obstruction, Roblox provides the `Class.ScreenGui.ScreenInsets|ScreenInsets` property which controls the **safe area** insets for the contents of a `Class.ScreenGui`. Every UI object that you position inside a `Class.ScreenGui` is relative to the inset bounds. ![Mobile device showing the core UI safe area.](../../../assets/engine-api/enums/ScreenInsets/CoreUISafeInsets.png) While the default of `Enum.ScreenInsets|CoreUISafeInsets` ensures all UI objects remain clear of Roblox UI and device cutouts, `Enum.ScreenInsets|DeviceSafeInsets` can be a better option to utilize limited screen space, as illustrated below. #### CoreUISafeInsets > **Error:**`Enum.ScreenInsets|CoreUISafeInsets` keeps the meter clear of all obstructions, but it adds a large gap between the meter and the physical top edge of the screen. ![ScreenInsets set to CoreUISafeInsets.](../../../assets/tutorials/creating-hud-meters/Meter-CoreUISafeInsets.png) #### DeviceSafeInsets > **Success:**`Enum.ScreenInsets|DeviceSafeInsets` tucks the meter against the physical top edge of the screen, and the meter's right alignment keeps it clear of the Roblox UI. ![ScreenInsets set to DeviceSafeInsets.](../../../assets/tutorials/creating-hud-meters/Meter-DeviceSafeInsets.png) 1. In the **Explorer** window, select **HUDContainer**.![Explorer window showing the HUDContainer selected.](../../../assets/tutorials/creating-hud-meters/StarterGui-HUDContainer.png) 2. In the [Properties](/docs/en-us/studio/properties.md) window, set the `Class.ScreenGui.ScreenInsets|ScreenInsets` property to `Enum.ScreenInsets|DeviceSafeInsets`.![ScreenInsets set to DeviceSafeInsets in the Properties window.](../../../assets/studio/properties/ScreenGui-ScreenInsets-DeviceSafeInsets.png) ### Set edge padding With `Class.ScreenGui.ScreenInsets|ScreenInsets` set to `Enum.ScreenInsets|DeviceSafeInsets`, content can now extend directly up to the physical top edge of the screen. However, a small amount of **padding** can help push the health meter (and other objects inside the container) slightly away from the screen edges for a cleaner appearance and to prevent them from being clipped. ![Screen container with padding around all edges.](../../../assets/tutorials/creating-hud-meters/ScreenGui-With-Padding.png) One way to apply padding to a UI container is through the insertion of a `Class.UIPadding` modifier: 1. Insert a `Class.UIPadding` modifier into **HUDContainer**.![HUDContainer with UIPadding modifier inserted.](../../../assets/tutorials/creating-hud-meters/StarterGui-HUDContainer-UIPadding.png) 2. With the new `Class.UIPadding` object selected, enter a value of`0, 16` for all edges of the container (`Class.UIPadding.PaddingBottom|PaddingBottom`, `Class.UIPadding.PaddingLeft|PaddingLeft`, `Class.UIPadding.PaddingRight|PaddingRight`, `Class.UIPadding.PaddingTop|PaddingTop`). This applies padding of 16 pixels all around the container, regardless of the screen's resolution.![Properties window showing the UIPadding modifier with 0, 16 set for all edges.](../../../assets/tutorials/creating-hud-meters/StarterGui-HUDContainer-UIPadding-Values.png) ## Construct the health meter With the [screen container configured](#create-a-screen-container), you can begin constructing the health meter using Roblox UI objects such as [frames](/docs/en-us/ui/frames.md) and an [image label](/docs/en-us/ui/labels.md). ![Basic components used for the health meter.](../../../assets/tutorials/creating-hud-meters/Meter-Design-Components.png) ### Create the parent frame Similar to design applications like Figma and Photoshop, a `Class.Frame` in Roblox serves as a container for other UI objects. For this tutorial, the entire health meter will be contained in a single parent frame, making it simple to reposition across various HUD layouts. 1. Insert a `Class.Frame` into **HUDContainer**. The new frame appears in the upper-left corner as an empty white square.![New frame in viewport.](../../../assets/tutorials/creating-hud-meters/Meter-Design-Parent-Frame.png) > **Warning:** If the new frame doesn't appear in the viewport, make sure you've toggled on **GUI overlay** from the [Visualization Options](/docs/en-us/studio/ui-overview.md#visualization-options) widget in the upper‑right corner of the 3D viewport. 2. Rename the new frame instance to **MeterBar**.![New frame inserted and renamed to MeterBar.](../../../assets/tutorials/creating-hud-meters/StarterGui-HUDContainer-MeterBar.png) ### Position the frame In Roblox, the position of a UI object is represented by a `Datatype.UDim2` coordinate set containing `Datatype.UDim.Scale|Scale` and `Datatype.UDim.Offset|Offset` values for both the **X** and **Y** axes: - `Datatype.UDim.Scale|Scale` values represent a **percentage** of the container's size along the corresponding axis, additive of any `Datatype.UDim.Offset|Offset` values. - `Datatype.UDim.Offset|Offset` values represent how many **pixels** to shift the object on the corresponding axis, additive of any `Datatype.UDim.Scale|Scale` values. To position a UI object in the upper-right corner of the screen container, `Datatype.UDim.Scale|Scale` is the best approach because an **X** value of `1` (100%) represents the right edge of the container, regardless of the screen's physical pixel size. Similarly, a **Y** scale value of `0` (0%) represents the top edge of the container. ![Scale ranges for the X and Y axes of a container.](../../../assets/tutorials/creating-hud-meters/ScreenGui-Axis-Ranges.png) Additionally, you'll need to set an upper-right [anchor point](/docs/en-us/ui/position-and-size.md#anchorpoint) for the parent frame to define its origin point. Acceptable values are between `0` and `1`, relative to the size of the object, so an anchor value of `1, 0` puts the frame's anchor point in its upper‑right corner. ![Frame anchor point in its upper-right corner.](../../../assets/tutorials/creating-hud-meters/Meter-Design-Parent-Frame-Anchor.png) 1. In the **Explorer** window, select the **MeterBar** frame that you inserted previously.![Explorer window showing the MeterBar frame selected.](../../../assets/tutorials/creating-hud-meters/StarterGui-HUDContainer-MeterBar.png) 2. In the **Properties** window, enter`1, 0, 0, 0` for `Class.Frame.Position|Position` and press `Enter`. Studio will automatically add the brackets to form the `Datatype.UDim2` of`{1, 0},{0, 0}`. 3. Enter`1, 0` for the `Class.Frame.AnchorPoint|AnchorPoint` property. The frame should now be positioned in the upper‑right corner of the [device safe area](#utilize-safe-areas), slightly indented from the edge as a result of the [padding](#set-edge-padding).![Frame repositioned in upper-right corner of container.](../../../assets/tutorials/creating-hud-meters/Meter-Design-Parent-Frame-Repositioned.png) ### Resize the frame Like position, the `Class.Frame.Size|Size` of a UI object is represented by a `Datatype.UDim2` coordinate set containing `Datatype.UDim.Scale|Scale` and `Datatype.UDim.Offset|Offset` values for both the **X** and **Y** axes. By default, the new frame's size is `{0, 100},{0, 100}` , meaning 100 **pixels** in both width (**X**) and height (**Y**). While a strict pixel value is useful in certain cases, many UI elements scale more responsively across multiple screens when set to a **percentage** of the overall screen container size. 1. With the **MeterBar** frame selected, access the **Properties** window and navigate to the `Class.Frame.Size|Size` property. 2. Enter a value of `0.35, 0, 0.05, 0` to set a percentage size of 35% wide and 5% tall with no added pixel offsets.![Frame resized to 35% wide and 5% tall.](../../../assets/tutorials/creating-hud-meters/Meter-Design-Parent-Frame-Resized.png) ### Style the frame By default, `Class.Frame|Frames` are filled in solid white. The final health meter should have a darker and slightly opaque fill, as well as a dark outline, so that it stands out better on both light and dark backgrounds. ![Frame styled with opacity, border, and rounded corners.](../../../assets/tutorials/creating-hud-meters/Meter-Design-Parent-Frame-Preview.png) 1. With the **MeterBar** frame selected, enter `0` for the `Class.Frame.BackgroundColor3|BackgroundColor3` property. Studio will automatically convert it to the RGB value of`[0, 0, 0]`. 2. Enter `0.75` for the `Class.Frame.BackgroundTransparency|BackgroundTransparency` property. In Roblox, transparency ranges from `0` for fully opaque to `1` for fully transparent, so `0.75` equals 25% opacity in other applications such as Figma or Photoshop.![Frame restyled with dark background and 25% opacity.](../../../assets/tutorials/creating-hud-meters/Meter-Design-Parent-Frame-Opaque.png) 3. Insert a `Class.UIStroke` object, a powerful UI modifier that adds a [customizable stroke](/docs/en-us/ui/appearance-modifiers.md#stroke) to the frame.![Explorer window showing the MeterBar frame with a child UIStroke modifier.](../../../assets/tutorials/creating-hud-meters/StarterGui-HUDContainer-MeterBar-UIStroke.png) 4. With the new `Class.UIStroke` selected, set the following properties: - `Class.UIStroke.Thickness|Thickness` = `3` - `Class.UIStroke.Transparency|Transparency` = `0.25` (75% opaque)![Frame restyled with a UIStroke modifier.](../../../assets/tutorials/creating-hud-meters/Meter-Design-Parent-Frame-Stroked.png) To finalize the meter frame's styling, you can round the corners to form a "pill" shape instead of a sharp rectangle. 1. Insert a `Class.UICorner` instance into the **MeterBar** frame.![Explorer window showing the MeterBar frame with a child UICorner modifier.](../../../assets/tutorials/creating-hud-meters/StarterGui-HUDContainer-MeterBar-UICorner.png) 2. With the new `Class.UICorner` selected, set the `Class.UICorner.CornerRadius|CornerRadius` to`0.5, 0`. Using a **scale** value of `0.5` (50%) instead of a pixel value is especially convenient for the meter bar because it ensures a fully rounded curve no matter how tall or wide the container becomes.![Frame corners rounded with a UICorner modifier.](../../../assets/tutorials/creating-hud-meters/Meter-Design-Parent-Frame-Rounded.png) ### Create the inner fill Now that the health meter's containing frame is complete, you can add an **inner fill** portion to represent the character's variable health. Since it only needs to be a solid‑filled region, a sub‑child `Class.Frame` inside the parent frame is suitable. ![Inner fill frame added to parent frame to represent the character's variable health.](../../../assets/tutorials/creating-hud-meters/Meter-Design-Inner-Fill-Preview.png) 1. Insert a child `Class.Frame` into the **MeterBar** frame. 2. Rename the new frame instance to **InnerFill**.![Explorer window showing the parent MeterBar frame with a child frame named InnerFill.](../../../assets/tutorials/creating-hud-meters/StarterGui-HUDContainer-MeterBar-InnerFill.png) 3. With **InnerFill** selected, set the following properties: - `Class.Frame.AnchorPoint|AnchorPoint` = `0, 0.5` (left edge and vertical center) - `Class.Frame.Position|Position` = `0, 0, 0.5, 0` - `Class.Frame.Size|Size` = `1, 0, 1, 0` Since children of frames are positioned and sized relative to their parent, use of scale makes the inner frame fill the parent's full width and height, starting from the parent's left edge.![Inner fill frame repositioned and resized to fill entire parent frame.](../../../assets/tutorials/creating-hud-meters/Meter-Design-Inner-Fill-Resized.png) 4. To match the "pill" shape of the parent frame, insert an additional `Class.UICorner` into **InnerFill**.![Explorer window showing the InnerFill frame with a child UICorner modifier.](../../../assets/tutorials/creating-hud-meters/StarterGui-HUDContainer-MeterBar-InnerFill-UICorner.png) 5. With the new `Class.UICorner` modifier selected, set its `Class.UICorner.CornerRadius|CornerRadius` property to`0.5, 0` to match the "pill" shape of the parent **MeterBar** frame.![Inner fill frame corners rounded with a UICorner modifier.](../../../assets/tutorials/creating-hud-meters/Meter-Design-Inner-Fill-Rounded.png) 6. To better represent that a full meter indicates good health, select **InnerFill** and set its `Class.Frame.BackgroundColor3|BackgroundColor3` property to`[0, 225, 50]` (in a later task, you'll [script](#listen-for-health-changes) this color to change based on actual health).![Inner fill frame recolored green to represent good health.](../../../assets/tutorials/creating-hud-meters/Meter-Design-Inner-Fill-Colored.png) ### Add the icon To more clearly indicate the purpose of the meter, you can add an [image label](/docs/en-us/ui/labels.md) to the left side, in this case a red heart which commonly symbolizes health or life. ![Image label of heart added to more clearly indicate a health meter.](../../../assets/tutorials/creating-hud-meters/Meter-Design-Icon-Preview.png) 1. Insert an `Class.ImageLabel` into the **MeterBar** frame. This object lets you apply a 2D image asset that has been uploaded as a decal to Roblox. 2. Rename the new label instance to **Icon**.![Explorer window showing the MeterBar frame with a child ImageLabel.](../../../assets/tutorials/creating-hud-meters/StarterGui-HUDContainer-MeterBar-Icon.png) 3. With **Icon** selected, set its `Class.ImageLabel.ZIndex|ZIndex` property to `2`. While newly inserted UI objects always layer in front of objects inserted previously, this change ensures the icon always displays in front of the meter's frame elements.![Properties window showing the ZIndex of the ImageLabel set to 2.](../../../assets/tutorials/creating-hud-meters/StarterGui-HUDContainer-MeterBar-Icon-Values.png) 4. Locate the icon's `Class.ImageLabel.Image|Image` property and enter `rbxassetid://91715286435585`, the reference to a pre‑uploaded heart image (if desired, you can [import](/docs/en-us/projects/assets/manager.md#asset-import) your own image and use its asset ID).![Image label of heart added to MeterBar frame.](../../../assets/tutorials/creating-hud-meters/Meter-Design-Icon-Large.png) 5. To ensure the icon `Class.ImageLabel` always stays at a 1:1 aspect ratio, insert a `Class.UIAspectRatioConstraint`. Although this [constraint](/docs/en-us/ui/size-modifiers.md#constraints) has customizable properties to control the aspect ratio, you can leave its default values intact.![Explorer window showing the ImageLabel with a child UIAspectRatioConstraint.](../../../assets/tutorials/creating-hud-meters/StarterGui-HUDContainer-MeterBar-Icon-UIAspectRatioConstraint.png) 6. With **Icon** selected, finalize the appearance and position by changing the following properties: - `Class.ImageLabel.AnchorPoint|AnchorPoint` = `0.5, 0.5` (center anchor) - `Class.ImageLabel.BackgroundTransparency|BackgroundTransparency` = `1` (100% transparent) - `Class.ImageLabel.Position|Position` = `0, 0, 0.5, 0` (left side of meter and vertical center) - `Class.ImageLabel.Size|Size` = `2, 0, 2, 0` (200% the overall size of the **MeterBar** frame, constrained to 1:1 by the `Class.UIAspectRatioConstraint`)![Image label of heart repositioned and resized with background fill made transparent.](../../../assets/tutorials/creating-hud-meters/Meter-Design-Icon-Finalized.png) ### Constrain the size While a [scale height](#resize-the-frame) of `0.05` (5%) looks good on modern phone screens and gaming monitors which are 16:9 aspect ratio or wider, the meter may look slightly too tall on tablet screens and older phones. You can check this by emulating a tablet like **iPad 7th Generation** from the **Device Emulator**. ![Device Emulator set to emulate a tablet device.](../../../assets/studio/general/Device-Emulator-Tablet.png)![Emulation on tablet device with meter bar taller than desired.](../../../assets/tutorials/creating-hud-meters/Emulation-Tablet-Unconstrained.png) To keep the meter bar's height more consistent with wider screens, you can apply a `Class.UISizeConstraint` to limit the maximum pixel height. 1. Insert a `Class.UISizeConstraint` into the **MeterBar** frame.![Explorer window showing the MeterBar frame with a child UISizeConstraint.](../../../assets/tutorials/creating-hud-meters/StarterGui-HUDContainer-MeterBar-UISizeConstraint.png) 2. With the new constraint selected, set its `Class.UISizeConstraint.MaxSize|MaxSize` property to`inf, 20` to restrict its height to 20 pixels while enforcing no width restriction.![Properties window showing the MaxSize of the UISizeConstraint set to inf, 20.](../../../assets/tutorials/creating-hud-meters/StarterGui-HUDContainer-MeterBar-UISizeConstraint-Values.png) Now, the meter bar maintains a more consistent height between wider and taller screens. #### Phone ![Emulation on phone.](../../../assets/tutorials/creating-hud-meters/Emulation-Phone-Constrained.png) #### Tablet ![Emulation on tablet device with meter bar height constrained.](../../../assets/tutorials/creating-hud-meters/Emulation-Tablet-Constrained.png) ## Replace the default health meter Roblox experiences include a default health meter which becomes visible when characters take damage. If you keep the default meter visible, it will duplicate and potentially overlap the custom meter. ![Default health meter overlapping and duplicating the custom health meter.](../../../assets/tutorials/creating-hud-meters/Default-Health-Meter.png) ### Disable the default meter To disable the default health meter, you'll use a **client script** (`Class.LocalScript`) within `Class.StarterPlayerScripts` that calls `Class.StarterGui:SetCoreGuiEnabled()`. 1. In the **Explorer** window, expand the `Class.StarterPlayer` container and locate the `Class.StarterPlayerScripts` container within it.![Explorer window showing the StarterPlayerScripts container inside the StarterPlayer container.](../../../assets/tutorials/creating-hud-meters/StarterPlayer-StarterPlayerScripts.png) 2. Insert a new `Class.LocalScript` into the container and rename it to **HideDefaultHealthMeter** to describe its purpose. Scripts within `Class.StarterPlayerScripts` automatically run when the local player joins an experience, making it an ideal container to run a script that permanently hides the default meter.![Explorer window showing the new HideDefaultHealthMeter client script inside the StarterPlayerScripts container.](../../../assets/tutorials/creating-hud-meters/StarterPlayer-StarterPlayerScripts-HideDefaultHealthMeter.png) 3. When you insert a new script, it automatically opens in a new script editor tab (if it doesn't, double‑click the script in the **Explorer** window). Paste the following code inside the **HideDefaultHealthMeter** script:```lua local StarterGui = game:GetService("StarterGui") -- Hide default health meter StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType.Health, false) ```** Code Explanation**| Line | Purpose | | --- | --- | | `1` | Gets a reference to a core [service](/docs/en-us/scripting/services.md), `Class.StarterGui`, which represents the same container where you created the custom health meter and whose contents clone into the `Class.PlayerGui` container for each player that joins the experience. | | `4` | Calls the service's `Class.StarterGui:SetCoreGuiEnabled()\|SetCoreGuiEnabled()` method and instructs the default health meter to be disabled (`false`). | If you playtest the experience now and take damage, you'll notice that the default meter is disabled and hidden (you'll script the custom meter to [reflect health changes](#listen-for-health-changes) in the next section). ![Default health meter disabled.](../../../assets/tutorials/creating-hud-meters/Default-Health-Meter-Disabled.png) ### Listen for health changes All default Roblox character models contain a `Class.Humanoid` class which provides special behaviors and functionality to the character, such as setting its walk/run speed and managing its health. `Class.Humanoid.Health|Health` changes on the server [replicate](/docs/en-us/projects/client-server.md#replication) to each player's client and you can detect these changes to update both the size and color of the custom health meter. 1. In the **Explorer** window, locate the `Class.StarterCharacterScripts` container within `Class.StarterPlayer`.![Explorer window showing the StarterCharacterScripts container inside the StarterPlayer container.](../../../assets/tutorials/creating-hud-meters/StarterPlayer-StarterCharacterScripts.png) 2. Insert a new `Class.LocalScript` into the container and rename it to **UpdateCustomMeter** to describe its purpose. Scripts within `Class.StarterCharacterScripts` automatically run each time the player's character spawns, making it an ideal container to run a script that fully resets the health meter on each respawn.![Explorer window showing the new UpdateCustomMeter client script inside the StarterCharacterScripts container.](../../../assets/tutorials/creating-hud-meters/StarterPlayer-StarterCharacterScripts-UpdateCustomMeter.png) 3. In the editor window for the **UpdateCustomMeter** script, paste the following code:```lua local Players = game:GetService("Players") -- Reference to local player, character, and humanoid local player = Players.LocalPlayer local character = player.Character local humanoid = character:WaitForChild("Humanoid") -- Reference to meter bar inner frame local playerGui = player:WaitForChild("PlayerGui") local meterBarInner = playerGui.HUDContainer.MeterBar.InnerFill -- Gradient sequence colors (red, orange, yellow, lime, green) local gradient = { Color3.fromRGB(225, 50, 0), Color3.fromRGB(255, 100, 0), Color3.fromRGB(255, 200, 0), Color3.fromRGB(150, 225, 0), Color3.fromRGB(0, 225, 50) } -- Function to get color in gradient sequence from fractional point local function getColorFromSequence(fraction: number): Color3 -- Each color in gradient defines the beginning and/or end of a section local numSections = #gradient - 1 -- Each section represents a portion of 1 local sectionSize = 1 / numSections -- Determine which section the requested fraction falls into local sectionStartIndex = 1 + math.clamp(fraction, 0, 1) // sectionSize -- Get the colors at the start and end of the section local sectionColorStart = gradient[sectionStartIndex] local sectionColorEnd = gradient[sectionStartIndex + 1] or sectionColorStart -- Normalize fraction to be a number from 0 to 1 within the section local fractionOfSection = math.clamp(fraction, 0, 1) % sectionSize / sectionSize -- Lerp between beginning and end based on the normalized fraction return sectionColorStart:Lerp(sectionColorEnd, fractionOfSection) end local function onHealthChanged() -- Calculate new health as percentage of max local healthFraction = math.max(0, humanoid.Health / humanoid.MaxHealth) -- Set the bar to new size/color targets meterBarInner.Size = UDim2.new(healthFraction, 0, 1, 0) meterBarInner.BackgroundColor3 = getColorFromSequence(healthFraction) end -- Listen for changes to humanoid health humanoid.HealthChanged:Connect(onHealthChanged) -- Initially set (or reset) bar size/color to current health onHealthChanged() ```** Code Explanation**| Lines | Purpose | | --- | --- | | `4`‑`6` | Gets references to the local `Class.Player`, their `Class.Player.Character\|Character` model, and the `Class.Humanoid` class within it. | | `9`‑`10` | Gets a reference to the meter's **InnerFill** object which must be resized and recolored as the character's health changes. | | `13`‑`19` | Declares an array of five colors (red, orange, yellow, lime, green) to recolor the meter at various points; for example, green for 100% health, yellow for 50%, red for 0%, or a blend at any fraction between the keypoints. | | `22`‑`41` | Helper function which returns the color blend between any of the gradient color keypoints. | | `43`‑`50` | Function which handles any changes to health. Here, it calculates the new health as a percentage of the character's `Class.Humanoid.MaxHealth\|MaxHealth`, resizes **InnerFill** to that scale percentage, and recolors it to the color returned from the `getColorFromSequence()` function. | | `53` | The main [event](/docs/en-us/scripting/events.md) connection which detects `Class.Humanoid.Health\|Health` changes replicated from the server and calls the `onHealthChanged()` function. | | `56` | Initially (upon character spawn or respawn) calls the `onHealthChanged()` function to size and color **InnerFill** to the correct percentage. Typically this will be the full width and green. | If you playtest the experience now, you'll notice that the custom meter correctly updates both size and color as the character takes damage: > **Warning:** By default, the Roblox health metering system includes a gradual healing effect which allows characters to slowly regenerate health. If you wish to disable this effect, insert an empty `Class.Script` named **Health** into the `Class.StarterCharacterScripts` container. This will prevent the default internal script from loading for each player character, effectively removing the regeneration effect. ### Animate the meter bar To add an extra level of polish to the custom meter, you can animate health changes through **tweening**, gradually changing the meter bar's size and color over ½ second. 1. Access the script editor tab for the **UpdateCustomMeter** script that you edited previously. 2. Select all lines (`Ctrl``A` or `⌘``A`) and then paste over them (`Ctrl``V` or `⌘``V`) with the following code:```lua local Players = game:GetService("Players") local TweenService = game:GetService("TweenService") -- Reference to local player, character, and humanoid local player = Players.LocalPlayer local character = player.Character local humanoid = character:WaitForChild("Humanoid") -- Tween properties local tweenInfo = TweenInfo.new(0.5, Enum.EasingStyle.Exponential, Enum.EasingDirection.Out) -- Reference to meter bar inner frame local playerGui = player:WaitForChild("PlayerGui") local meterBarInner = playerGui.HUDContainer.MeterBar.InnerFill -- Gradient sequence colors (red, orange, yellow, lime, green) local gradient = { Color3.fromRGB(225, 50, 0), Color3.fromRGB(255, 100, 0), Color3.fromRGB(255, 200, 0), Color3.fromRGB(150, 225, 0), Color3.fromRGB(0, 225, 50) } -- Function to get color in gradient sequence from fractional point local function getColorFromSequence(fraction: number): Color3 -- Each color in gradient defines the beginning and/or end of a section local numSections = #gradient - 1 -- Each section represents a portion of 1 local sectionSize = 1 / numSections -- Determine which section the requested fraction falls into local sectionStartIndex = 1 + math.clamp(fraction, 0, 1) // sectionSize -- Get the colors at the start and end of the section local sectionColorStart = gradient[sectionStartIndex] local sectionColorEnd = gradient[sectionStartIndex + 1] or sectionColorStart -- Normalize fraction to be a number from 0 to 1 within the section local fractionOfSection = math.clamp(fraction, 0, 1) % sectionSize / sectionSize -- Lerp between beginning and end based on the normalized fraction return sectionColorStart:Lerp(sectionColorEnd, fractionOfSection) end local function onHealthChanged() -- Calculate new health as percentage of max local healthFraction = math.max(0, humanoid.Health / humanoid.MaxHealth) -- Tween the bar to new size/color targets local tweenGoal = { Size = UDim2.new(healthFraction, 0, 1, 0), BackgroundColor3 = getColorFromSequence(healthFraction) } local meterBarTween = TweenService:Create(meterBarInner, tweenInfo, tweenGoal) meterBarTween:Play() end -- Listen for changes to humanoid health humanoid.HealthChanged:Connect(onHealthChanged) -- Initially set (or reset) bar size/color to current health onHealthChanged() ```** Key Additions/Changes**| Lines | Purpose | | --- | --- | | `2` | Gets a reference to `Class.TweenService` to implement tweening functionality within the script. | | `10` | Creates a `Datatype.TweenInfo` constructor which defines the intended tween's duration, [easing style](/docs/en-us/ui/animation.md#style), and [easing direction](/docs/en-us/ui/animation.md#direction). | | `52`‑`57` | Instead of simply setting the bar's size and color as in the [previous version](#listen-for-health-changes), declares a `tweenGoal` table with the target size/color, creates a new tween using the `tweenInfo` and `tweenGoal` parameters, and plays the new tween. | If you playtest the experience now, you'll notice that the custom meter tweens between each change in health: ### Add a damage effect The default health meter system includes a brief, subtle red tint on the screen edges when the character is damaged. By [disabling the default meter](#disable-the-default-meter), this effect is removed, but you can replace it with your own implementation. 1. Access the script editor tab for the **UpdateCustomMeter** script that you edited previously. 2. Select all lines and paste over them with the following code:```lua local Workspace = game:GetService("Workspace") local Players = game:GetService("Players") local TweenService = game:GetService("TweenService") -- Reference to local player, character, and humanoid local player = Players.LocalPlayer local character = player.Character local humanoid = character:WaitForChild("Humanoid") -- Tween properties local tweenInfo = TweenInfo.new(0.5, Enum.EasingStyle.Exponential, Enum.EasingDirection.Out) -- Variable to store/cache character health local cachedHealth = humanoid.Health / humanoid.MaxHealth -- Get (or create new) color correction effect inside player camera local colorCorrection = Workspace.CurrentCamera:FindFirstChildWhichIsA("ColorCorrectionEffect") or Instance.new("ColorCorrectionEffect", Workspace.CurrentCamera) colorCorrection.Name = "DamageColorEffect" -- Reference to meter bar inner frame local playerGui = player:WaitForChild("PlayerGui") local meterBarInner = playerGui.HUDContainer.MeterBar.InnerFill -- Gradient sequence colors (red, orange, yellow, lime, green) local gradient = { Color3.fromRGB(225, 50, 0), Color3.fromRGB(255, 100, 0), Color3.fromRGB(255, 200, 0), Color3.fromRGB(150, 225, 0), Color3.fromRGB(0, 225, 50) } -- Function to get color in gradient sequence from fractional point local function getColorFromSequence(fraction: number): Color3 -- Each color in gradient defines the beginning and/or end of a section local numSections = #gradient - 1 -- Each section represents a portion of 1 local sectionSize = 1 / numSections -- Determine which section the requested fraction falls into local sectionStartIndex = 1 + math.clamp(fraction, 0, 1) // sectionSize -- Get the colors at the start and end of the section local sectionColorStart = gradient[sectionStartIndex] local sectionColorEnd = gradient[sectionStartIndex + 1] or sectionColorStart -- Normalize fraction to be a number from 0 to 1 within the section local fractionOfSection = math.clamp(fraction, 0, 1) % sectionSize / sectionSize -- Lerp between beginning and end based on the normalized fraction return sectionColorStart:Lerp(sectionColorEnd, fractionOfSection) end local function onHealthChanged() -- Calculate new health as percentage of max local healthFraction = math.max(0, humanoid.Health / humanoid.MaxHealth) -- Tween the bar to new size/color targets local tweenGoal = { Size = UDim2.new(healthFraction, 0, 1, 0), BackgroundColor3 = getColorFromSequence(healthFraction) } local meterBarTween = TweenService:Create(meterBarInner, tweenInfo, tweenGoal) meterBarTween:Play() -- Show damage effect if new health is lower than cached health if healthFraction < cachedHealth then -- Cache new health value cachedHealth = healthFraction -- Set color correction to red as the initial tint before tweening colorCorrection.TintColor = Color3.fromRGB(255, 25, 25) colorCorrection.Saturation = 2.5 -- Tween the tint back to white (neutral and no tint change from normal) local colorCorrectionTweenGoal = { TintColor = Color3.fromRGB(255, 255, 255), Saturation = 0 } local colorCorrectionTween = TweenService:Create(colorCorrection, tweenInfo, colorCorrectionTweenGoal) colorCorrectionTween:Play() end end -- Listen for changes to humanoid health humanoid.HealthChanged:Connect(onHealthChanged) -- Initially set (or reset) bar size/color to current health onHealthChanged() ```** Key additions/changes**| Lines | Purpose | | --- | --- | | `14` | Sets a placeholder reference (`cachedHealth`) to track the character's health amount between changes, so you can compare if a change is lower (damage). | | `17`‑`18` | On the initial character spawn, creates a new `Class.ColorCorrectionEffect` inside the player's current `Class.Camera`, or gets a reference to the same instance on later respawns. By parenting this [post‑processing effect](/docs/en-us/environment/post-processing-effects.md) to the player's camera, it only applies to their local screen, not to every player's screen on the server. | | `68`‑`83` | First performs a conditional check to confirm that the health change is lower than the `cachedHealth` value, indicating damage; if so, it sets `cachedHealth` to the new value. Next, it sets the `Class.ColorCorrectionEffect` tint to`[255, 25, 25]` (red) with higher saturation, then tweens the tint back to the default of neutral white (`[255, 255, 255]`) with no saturation. | If you playtest the experience now, you'll notice that the screen briefly flashes red whenever the character takes damage: > **Info:** If you decide to adapt the custom meter bar to another purpose, such as a mana meter or poison meter, you can change the `Class.ColorCorrectionEffect.TintColor|TintColor` on line `73` to something more suitable to the event, such as light blue (`[200, 225, 255]`) or toxic green (`[0, 255, 75]`). > **Success:** If your health meter doesn't match the appearance or behavior displayed in this tutorial, remember that you can inspect [UI Fundamentals - HUD Meter](https://www.roblox.com/games/104506915856758/UI-Fundamentals-HUD-Meter) as a completed reference template. --- title: "Create interactive UI" url: /docs/en-us/tutorials/use-case-tutorials/ui/interactive-ui last_updated: 2026-06-29T19:34:15Z description: "Learn how to build interactive UI and connect stateful scripts to power it." --- # Create interactive UI A **HUD** (**Heads-Up Display**) in your experience commonly displays non‑interactive UI elements such as the health meter demonstrated in [Create HUD meters](/docs/en-us/tutorials/use-case-tutorials/ui/create-hud-meters.md). Atop this, almost every experience requires **interactive** UI such as buttons that respond to player activation, animate when activated, tween in/out menus with other interactive controls, etc. ![In-game view showing a settings button and settings menu with volume sliders.](../../../assets/tutorials/interactive-ui/Intro.png) Using [UI Fundamentals - HUD Meter](https://www.roblox.com/games/104506915856758/UI-Fundamentals-HUD-Meter) as a starting place and [UI Fundamentals - Interactivity](https://www.roblox.com/games/138499606610292/UI-Fundamentals-Interactivity) as a finished reference place, this tutorial demonstrates: - How to position a settings button along the top screen edge. - Design of a settings menu containing interactive draggable sliders. - Usage of a `Class.ModuleScript|ModuleScripts` to form "controller" modules for extensible control of UI objects with stateful declarations. - How to connect buttons to player activation to toggle in/out the settings menu. - How to connect draggable UI sliders to adjust the volume of sound effects and background ambience separately. ## Enable the Device Emulator As noted in [Create HUD meters](/docs/en-us/tutorials/use-case-tutorials/ui/create-hud-meters.md), phones and tablets have the least amount of screen space, so it's important that your UI elements fit on smaller screens and that they're clearly visible to players. If you haven't done so already, enable the [Device Emulator](/docs/en-us/studio/testing-modes.md#device-emulation) in Studio: 1. Open the [UI Fundamentals - HUD Meter](https://www.roblox.com/games/104506915856758/UI-Fundamentals-HUD-Meter) template in Studio. 2. From Studio's **Test** menu, toggle on **Device Emulator**. 3. From the bar directly above the main viewport, select a phone emulation such as **iPhone X** or **Samsung Galaxy A51**. Then, set the view size to **Fit to Window** to utilize the maximum space in Studio.![Device Emulator settings options indicated at top of viewport window.](../../../assets/studio/general/Device-Emulator-Phone.png) ## Create the settings button `Class.GuiButton|GuiButtons` are interactive user interface elements with built‑in functionality such as the multi‑platform `Class.GuiButton.Activated|Activated` event which fires when the button is clicked or tapped. The `Class.GuiButton` class extends to two variations, `Class.TextButton` and `Class.ImageButton`, and this tutorial uses a "gear" shaped `Class.ImageButton` to toggle the settings menu open or closed. ![Roblox component used for the settings button.](../../../assets/tutorials/interactive-ui/Settings-Button-Design-Components.png) To construct the settings button: 1. In the **Explorer** window, locate **HUDContainer** inside `Class.StarterGui`.![Explorer window showing the HUDContainer selected.](../../../assets/tutorials/interactive-ui/StarterGui-HUDContainer.png) 2. Insert an `Class.ImageButton` into **HUDContainer** and rename it to **SettingsButton**.![Explorer window showing new ImageButton inserted and renamed to SettingsButton.](../../../assets/tutorials/interactive-ui/StarterGui-HUDContainer-SettingsButton.png)![New ImageButton in viewport.](../../../assets/tutorials/interactive-ui/Settings-Button-Design-Initial.png) 3. With the new button selected, set the following in the **Properties** window: - `Class.ImageButton.AnchorPoint|AnchorPoint` = `0.5, 0.25` (horizontal center and vertical upper quarter) - `Class.ImageButton.BackgroundTransparency|BackgroundTransparency` = `1` (fully transparent) - `Class.ImageButton.Position|Position` = `0.5, 0, 0, 0` (top-center positioning) - `Class.ImageButton.Size|Size` = `0.1, 0, 0.1, 0` (10% of screen height once aspect ratio is constrained) - `Class.ImageButton.Image|Image` = `rbxassetid://104919049969988` (asset ID of "gear" symbol) 4. Insert a `Class.UIAspectRatioConstraint` into the button with default properties (1:1 width to height).![Explorer window showing a new UIAspectRatioConstraint inserted into the SettingsButton button.](../../../assets/tutorials/interactive-ui/StarterGui-HUDContainer-SettingsButton-UIAspectRatioConstraint.png) 5. To limit the button's maximum pixel height, insert a `Class.UISizeConstraint` and set its `Class.UISizeConstraint.MaxSize|MaxSize` to`inf, 44`.![Explorer window showing a new UISizeConstraint inserted into the SettingsButton button.](../../../assets/tutorials/interactive-ui/StarterGui-HUDContainer-SettingsButton-UISizeConstraint.png)![Properties window showing expected values for the new UISizeConstraint.](../../../assets/tutorials/interactive-ui/StarterGui-HUDContainer-SettingsButton-UISizeConstraint-Values.png)![Final settings button in top-center of the screen.](../../../assets/tutorials/interactive-ui/Settings-Button-Design-Finalized.png) ## Create the settings menu The new [settings button](#create-the-settings-button) will be scripted to toggle a settings menu open and closed, giving players on‑demand access to settings or other information. In this tutorial, the menu will contain interactive sliders to adjust the volume levels of background audio and sound effects independently. ![Basic components used for the settings menu.](../../../assets/tutorials/interactive-ui/Settings-Menu-Design-Components.png) ### Create the parent frame As noted in [Create HUD meters](/docs/en-us/tutorials/use-case-tutorials/ui/create-hud-meters.md), a `Class.Frame` serves as a container for other UI objects. The entire settings menu will be constructed with a single parent frame, making it simple to manage as a stateful object that reacts to input differently depending on the current state. 1. Insert a `Class.Frame` into **HUDContainer** and rename it **SettingsMenu**.![Explorer window showing new frame inserted and renamed to SettingsMenu.](../../../assets/tutorials/interactive-ui/StarterGui-HUDContainer-SettingsMenu.png) 2. With **SettingsMenu** selected, set the following properties: - `Class.Frame.AnchorPoint|AnchorPoint` = `0.5` (center anchor) - `Class.Frame.BackgroundColor3|BackgroundColor3` = `30, 30, 60` (navy blue) - `Class.Frame.BackgroundTransparency|BackgroundTransparency` = `0.25` (75% opaque) - `Class.Frame.Position|Position` = `0.5, 0, 0.5, 0` (center of screen) - `Class.Frame.Size|Size` = `0.75, 0, 0.75, 0` (75% width/height of the inset screen area)![Frame repositioned, resized, and styled with transparent navy blue background.](../../../assets/tutorials/interactive-ui/Settings-Menu-Design-Parent-Frame.png) 3. Insert a `Class.UIAspectRatioConstraint` into **SettingsMenu** and set its `Class.UIAspectRatioConstraint.AspectRatio|AspectRatio` property to `2.5`. (2.5:1 width to height).![Explorer window showing a new UISizeConstraint inserted into the SettingsMenu frame.](../../../assets/tutorials/interactive-ui/StarterGui-HUDContainer-SettingsMenu-UIAspectRatioConstraint.png)![Properties window showing expected values for the new UISizeConstraint.](../../../assets/tutorials/interactive-ui/StarterGui-HUDContainer-SettingsMenu-UIAspectRatioConstraint-Values.png) 4. Insert a `Class.UICorner` modifier into **SettingsMenu** and set its `Class.UICorner.CornerRadius|CornerRadius` to`0.1, 0`.![Explorer window showing a new UICorner inserted into the SettingsMenu frame.](../../../assets/tutorials/interactive-ui/StarterGui-HUDContainer-SettingsMenu-UICorner.png)![Properties window showing expected values for the new UICorner modifier.](../../../assets/tutorials/interactive-ui/StarterGui-HUDContainer-SettingsMenu-UICorner-Values.png) 5. To constrain the pixel width and height of **SettingsMenu**, insert a `Class.UISizeConstraint`. Set its `Class.UISizeConstraint.MaxSize|MaxSize` to`800, inf` and its `Class.UISizeConstraint.MinSize|MinSize` to`350, 0`.![Explorer window showing a new UISizeConstraint inserted into the SettingsMenu frame.](../../../assets/tutorials/interactive-ui/StarterGui-HUDContainer-SettingsMenu-UISizeConstraint.png)![Properties window showing expected values for the new UISizeConstraint.](../../../assets/tutorials/interactive-ui/StarterGui-HUDContainer-SettingsMenu-UISizeConstraint-Values.png)![Frame styled with rounded corners.](../../../assets/tutorials/interactive-ui/Settings-Menu-Design-Parent-Frame-Finalized.png) ### Construct a slider To allow players to adjust volume levels, the settings menu will contain two draggable **slider widgets** powered by `Class.UIDragDetector`, a convenient object that facilitates interaction with 2D user interface elements. To create a parent container for the first slider: 1. Insert a new `Class.Frame` into the **SettingsMenu** container and rename it **EffectsVolumeSlider**.![Explorer window showing a new frame inserted into the SettingsMenu frame, renamed to EffectsVolumeSlider.](../../../assets/tutorials/interactive-ui/StarterGui-HUDContainer-SettingsMenu-EffectsVolumeSlider.png) 2. With the **EffectsVolumeSlider** frame selected, set the following properties: - `Class.Frame.AnchorPoint|AnchorPoint` = `0.5` (center anchor) - `Class.Frame.BackgroundTransparency|BackgroundTransparency` = `1` (fully transparent) - `Class.Frame.Position|Position` = `0.5, 0, 0.35, 0` (horizontal center and 35% from container's top) - `Class.Frame.Size|Size` = `0.8, 0, 0.1, 0` (80% of container's width and 10% of its height)![Empty frame inserted to contain slider elements.](../../../assets/tutorials/interactive-ui/Settings-Menu-Design-Slider-Frame-Measurements.png) 3. Insert a new `Class.UIListLayout` into **EffectsVolumeSlider**. This layout modifier is a powerful way to auto‑arrange sibling `Class.GuiObject|GuiObjects` into horizontal rows or vertical columns within their parent container, including the ability to apply [flex](/docs/en-us/ui/list-flex-layouts.md) concepts.![Explorer window showing a new UIListLayout inserted into the EffectsVolumeSlider frame.](../../../assets/tutorials/interactive-ui/StarterGui-HUDContainer-SettingsMenu-EffectsVolumeSlider-UIListLayout.png) 4. Set the following properties for the new `Class.UIListLayout`: - `Class.UIListLayout.Padding|Padding` = `0.06, 0` (6% padding between list elements) - `Class.UIListLayout.FillDirection|FillDirection` = `Enum.FillDirection.Horizontal|Horizontal` (left-to-right arrangement of elements) - `Class.UIListLayout.HorizontalFlex|HorizontalFlex` = `Enum.UIFlexAlignment.Fill|Fill` (resize elements horizontally to fill the entire parent container, overriding their defined width) - `Class.UIListLayout.VerticalAlignment|VerticalAlignment` = `Enum.VerticalAlignment.Center|Center` (align elements along their vertical center) #### Slider icon A simple icon including an audio note and "burst" symbol helps players identify the slider's purpose when they open the settings menu. ![Icon indicating purpose of slider in settings menu.](../../../assets/tutorials/interactive-ui/Settings-Menu-Design-Slider-Icon.png) 1. Insert an `Class.ImageLabel` into the **EffectsVolumeSlider** frame and rename it to **Icon**. This icon will help players understand what in‑experience aspect the slider controls.![Explorer window showing an ImageLabel inserted into the EffectsVolumeSlider frame.](../../../assets/tutorials/interactive-ui/StarterGui-HUDContainer-SettingsMenu-EffectsVolumeSlider-Icon.png) > **Warning:** Importantly, note that the label must be a **sibling** of the `Class.UIListLayout`, not a child of it. 2. With **Icon** selected, set the following properties: - `Class.ImageLabel.BackgroundTransparency|BackgroundTransparency` = `1` (fully transparent) - `Class.ImageLabel.Size|Size` = `2.5, 0, 2.5, 0` (250% height of parent frame once aspect ratio is constrained) - `Class.ImageLabel.Image|Image` = `rbxassetid://90019827067389` (asset ID of audio note and "burst" symbol) 3. Insert a `Class.UIAspectRatioConstraint` into **Icon** and leave its properties as the defaults (1:1 width to height).![Explorer window showing a UIAspectRatioConstraint inserted into the ImageLabel.](../../../assets/tutorials/interactive-ui/StarterGui-HUDContainer-SettingsMenu-EffectsVolumeSlider-Icon-UIAspectRatioConstraint.png)![Finalized icon in the settings menu.](../../../assets/tutorials/interactive-ui/Settings-Menu-Design-Slider-Icon-Finalized.png) #### Range frame Directly to the right of the [icon](#slider-icon), the interactive portion of the slider should be contained within another `Class.Frame`. ![Target size and position of the interactive portion of the slider.](../../../assets/tutorials/interactive-ui/Settings-Menu-Design-Range-Frame-Measurements.png) 1. Insert a new `Class.Frame` into the **EffectsVolumeSlider** frame and rename it **SliderFrame**. Note that it must be a direct **sibling** of the `Class.UIListLayout` layout modifier.![Explorer window showing a child Frame inserted into the EffectsVolumeSlider frame.](../../../assets/tutorials/interactive-ui/StarterGui-HUDContainer-SettingsMenu-EffectsVolumeSlider-SliderFrame.png) 2. With **SliderFrame** selected, set the following properties: - `Class.Frame.BackgroundColor3|BackgroundColor3` = `0` (black) - `Class.Frame.BackgroundTransparency|BackgroundTransparency` = `0.75` (25% opaque) - `Class.Frame.LayoutOrder|LayoutOrder` = `1` - `Class.Frame.Size|Size` = `1, 0, 1, 0` (100% width/height of parent frame) > **Info:** Note that it's not necessary to assign **positional** changes such as `Class.Frame.AnchorPoint|AnchorPoint` or `Class.Frame.Position|Position`, as the `Class.UIListLayout` controls the positioning of its siblings, in this case ordering them horizontally from left to right and keeping them vertically centered with each other. > **Warning:** The `Class.Frame.LayoutOrder|LayoutOrder` property is important when working with layout modifiers such as `Class.UIListLayout`, as it determines the sequence in which siblings are placed respective to each other. Changing it to `1` for **SliderFrame** ensures it's placed to the right of **Icon** which has a default `Class.Frame.LayoutOrder|LayoutOrder` of `0`. 3. Insert the following modifiers into **SliderFrame**: - `Class.UICorner` with its `Class.UICorner.CornerRadius|CornerRadius` property set to`0.5, 0` ("pill" shape) - `Class.UISizeConstraint` with its `Class.UISizeConstraint.MaxSize|MaxSize` property set to`inf, 30` (maximum 30 pixels in height) - `Class.UIStroke` with its `Class.UIStroke.Color|Color` set to `0`, `Class.UIStroke.Thickness|Thickness` to `3`, and `Class.UIStroke.Transparency|Transparency` to `0.25` (75% opaque black outline)![Slider frame positioned to the right of the icon.](../../../assets/tutorials/interactive-ui/Settings-Menu-Design-Range-Frame-Outline.png) #### Interactive handle With the [slider container](#range-frame) constructed, you can now create a **draggable handle** for players to interact with during gameplay. ![Interactive slider handle positioned within range frame.](../../../assets/tutorials/interactive-ui/Settings-Menu-Design-Slider-Handle.png) 1. Insert a new `Class.Frame` into the **SliderFrame** container and rename it to **Handle**.![Explorer window showing a child Frame inserted into the SliderFrame frame and renamed to Handle.](../../../assets/tutorials/interactive-ui/StarterGui-HUDContainer-SettingsMenu-EffectsVolumeSlider-SliderFrame-Handle.png) 2. With **Handle** selected, set the following properties: - `Class.Frame.AnchorPoint|AnchorPoint` = `0.5` (center anchor) - `Class.Frame.Position|Position` = `0.5, 0, 0.5, 0` (horizontal and vertical center of parent frame) - `Class.Frame.Size|Size` = `1.2, 0, 1.2, 0` (120% height of parent frame once aspect ratio is constrained) - `Class.Frame.ZIndex|ZIndex` = `3` (layer handle visually in front of other slider elements) 3. Insert the following modifiers into **Handle**: - `Class.UIAspectRatioConstraint` with its default properties (1:1 width to height) - `Class.UICorner` with its `Class.UICorner.CornerRadius|CornerRadius` property set to`0.5, 0` (circular shape) - `Class.UIStroke` with its `Class.UIStroke.Color|Color` set to`[0, 150, 255]` and `Class.UIStroke.Thickness|Thickness` to `8` (thick blue outline) 4. Insert a `Class.UIDragDetector` object into **Handle**. This convenient object facilitates and encourages interaction with 2D user interface elements.![Explorer window showing a UIDragDetector inserted into the Handle frame.](../../../assets/tutorials/interactive-ui/StarterGui-HUDContainer-SettingsMenu-EffectsVolumeSlider-SliderFrame-Handle-UIDragDetector.png) 5. Set the following properties for the new `Class.UIDragDetector`: - `Class.UIDragDetector.DragStyle|DragStyle` = `Enum.UIDragDetectorDragStyle.TranslateLine|TranslateLine` (1D motion along the detector's `Class.UIDragDetector.DragAxis|DragAxis`.) - `Class.UIDragDetector.ResponseStyle|ResponseStyle` = `Enum.UIDragDetectorResponseStyle.Scale|Scale` (move by scale values of the detector's parent's position) 6. To ensure the handle's linear drag range is limited to its container, link its `Class.UIDragDetector.BoundingUI|BoundingUI` property to the **SliderFrame** container: 1. Select the `Class.UIDragDetector`. 2. Click its `Class.UIDragDetector.BoundingUI|BoundingUI` property in the **Properties** window. 3. Back in the **Explorer** window, click the handle's parent **SliderFrame**.![Diagram showing process of linking the detector's BoundingUI property to the SliderFrame container.](../../../assets/tutorials/interactive-ui/Link-UIDragDetector-BoundingUI.png) The `Class.UIDragDetector.BoundingUI|BoundingUI` property link now reflects the **SliderFrame** container:![Properties window showing the detector's BoundingUI property linked to the SliderFrame container.](../../../assets/tutorials/interactive-ui/StarterGui-HUDContainer-SettingsMenu-EffectsVolumeSlider-SliderFrame-Handle-UIDragDetector-Values.png) If you playtest the experience now, you'll be able to drag the handle left and right within its parent container: #### Inner fill To more clearly indicate that the slider controls a 0% to 100% range, you can add an **inner fill** to the left side of the container which will sync with the handle's variable position. ![Inner fill portion of slider inserted into SliderFrame.](../../../assets/tutorials/interactive-ui/Settings-Menu-Design-Slider-Inner-Fill.png) 1. Insert a new `Class.Frame` into the **SliderFrame** container and rename it to **InnerFill**.![Explorer window showing a child Frame inserted into the SliderFrame frame and renamed to InnerFill.](../../../assets/tutorials/interactive-ui/StarterGui-HUDContainer-SettingsMenu-EffectsVolumeSlider-SliderFrame-InnerFill.png) 2. Set the following properties for the **InnerFill** frame: - `Class.Frame.AnchorPoint|AnchorPoint` = `0, 0.5` (left edge and vertical center) - `Class.Frame.BackgroundColor3|BackgroundColor3` = `[0, 150, 255]` (blue matching the slider's handle) - `Class.Frame.BackgroundTransparency|BackgroundTransparency` = `0.35` (65% opaque) - `Class.Frame.Position|Position` = `0, 0, 0.5, 0` - `Class.Frame.Size|Size` = `0.5, 0, 1, 0` (50% width and 100% height of parent frame) - `Class.Frame.ZIndex|ZIndex` = `2` (layer visually in front of parent frame's fill/outline) 3. To match the "pill" shape of the parent **SliderFrame** container, insert a `Class.UICorner` modifier and set its `Class.UICorner.CornerRadius|CornerRadius` property to`0.5, 0`.![Explorer window showing a UICorner modifier inserted into the InnerFill frame.](../../../assets/tutorials/interactive-ui/StarterGui-HUDContainer-SettingsMenu-EffectsVolumeSlider-SliderFrame-InnerFill-UICorner.png)![Properties window showing expected values for the new UICorner.](../../../assets/tutorials/interactive-ui/StarterGui-HUDContainer-SettingsMenu-EffectsVolumeSlider-SliderFrame-InnerFill-UICorner-Values.png)![InnerFill frame styled with blue background and rounded corners.](../../../assets/tutorials/interactive-ui/Settings-Menu-Design-Slider-Inner-Fill-Finalized.png) ### Duplicate the slider With the [first slider](#construct-a-slider) constructed, you can easily duplicate it and modify some visual aspects to indicate another purpose, in this case the volume level of background audio symbolized by an icon of musical notes. ![Target position of the background audio slider.](../../../assets/tutorials/interactive-ui/Settings-Menu-Design-BackgroundVolumeSlider-Measurements.png) 1. Select the completed **EffectsVolumeSlider** object.![Explorer window showing the completed EffectsVolumeSlider selected.](../../../assets/tutorials/interactive-ui/StarterGui-HUDContainer-SettingsMenu-EffectsVolumeSlider-Full.png) 2. Duplicate it (`Ctrl``D` or `⌘``D`) and then rename the duplicate to **BackgroundVolumeSlider**.![Explorer window showing the duplicated slider renamed to BackgroundVolumeSlider.](../../../assets/tutorials/interactive-ui/StarterGui-HUDContainer-SettingsMenu-BackgroundVolumeSlider.png) 3. Change the duplicate's `Class.Frame.Position|Position` to`0.5, 0, 0.7, 0` to move it below the first slider.![BackgroundVolumeSlider positioned below EffectsVolumeSlider.](../../../assets/tutorials/interactive-ui/Settings-Menu-Design-BackgroundVolumeSlider-Initial.png) 4. Expand the top-level branch of **BackgroundVolumeSlider**, select the **Icon** image label, and change its `Class.ImageLabel.Image|Image` property to `rbxassetid://101125859760167` (asset ID of symbol with musical notes).![Explorer window showing the Icon label within BackgroundVolumeSlider.](../../../assets/tutorials/interactive-ui/StarterGui-HUDContainer-SettingsMenu-BackgroundVolumeSlider-Icon.png)![Properties window showing expected values for the Icon label.](../../../assets/tutorials/interactive-ui/StarterGui-HUDContainer-SettingsMenu-BackgroundVolumeSlider-Icon-Values.png)![BackgroundVolumeSlider with modified icon image.](../../../assets/tutorials/interactive-ui/Settings-Menu-Design-BackgroundVolumeSlider-Icon.png) 5. Expand the **SliderFrame** ⟩ **Handle** branch, select the `Class.UIStroke` modifier within, and set its `Class.UIStroke.Color|Color` property to`[255, 0, 125]` (magenta).![Explorer window showing the UIStroke modifier within the handle of BackgroundVolumeSlider.](../../../assets/tutorials/interactive-ui/StarterGui-HUDContainer-SettingsMenu-BackgroundVolumeSlider-SliderFrame-Handle-UIStroke.png)![Properties window showing expected values for the UIStroke modifier.](../../../assets/tutorials/interactive-ui/StarterGui-HUDContainer-SettingsMenu-BackgroundVolumeSlider-SliderFrame-Handle-UIStroke-Values.png)![BackgroundVolumeSlider with modified icon image.](../../../assets/tutorials/interactive-ui/Settings-Menu-Design-BackgroundVolumeSlider-Handle.png) 6. Select the **InnerFill** frame within **SliderFrame** and change its `Class.Frame.BackgroundColor3|BackgroundColor3` property to the same magenta color (`[255, 0, 125]`).![Explorer window showing the InnerFill frame within the SliderFrame container of BackgroundVolumeSlider.](../../../assets/tutorials/interactive-ui/StarterGui-HUDContainer-SettingsMenu-BackgroundVolumeSlider-SliderFrame-InnerFill.png)![Properties window showing expected values for the InnerFill frame.](../../../assets/tutorials/interactive-ui/StarterGui-HUDContainer-SettingsMenu-BackgroundVolumeSlider-SliderFrame-InnerFill-Values.png)![BackgroundVolumeSlider with modified InnerFill color.](../../../assets/tutorials/interactive-ui/Settings-Menu-Design-BackgroundVolumeSlider-Inner-Fill.png) ### Create the close button The final element of the settings menu is a **close button** which provides players an additional input to close the menu (the **SettingsButton** in the top‑center will serve the same purpose). ![Settings menu with close button indicated in upper-right corner.](../../../assets/tutorials/interactive-ui/Settings-Menu-Design-Close-Button.png) 1. Insert a new `Class.ImageButton` into the **SettingsMenu** container and rename it **CloseButton**.![Explorer window showing a new ImageButton inserted into SettingsMenu and renamed to CloseButton.](../../../assets/tutorials/interactive-ui/StarterGui-HUDContainer-SettingsMenu-CloseButton.png)![Empty ImageButton positioned in upper-left corner of the settings menu.](../../../assets/tutorials/interactive-ui/Settings-Menu-Design-Close-Button-Initial.png) 2. With **CloseButton** selected, set the following properties: - `Class.ImageButton.AnchorPoint|AnchorPoint` = `1, 0` (top-right corner) - `Class.ImageButton.BackgroundTransparency|BackgroundTransparency` = `1` (fully transparent) - `Class.ImageButton.Position|Position` = `1, -10, 0, 10` (10 pixel inset from top-right corner) - `Class.ImageButton.Size|Size` = `0.15, 0, 0.15, 0` (15% of frame height once aspect ratio is constrained) - `Class.ImageButton.Image|Image` = `rbxassetid://5577404210` (asset ID of close button symbol) - `Class.ImageButton.ImageTransparency|ImageTransparency` = `0.25` (75% opaque) 3. Insert a `Class.UIAspectRatioConstraint` into the button with default properties (1:1 width to height).![Explorer window showing a UIAspectRatioConstraint inserted into the CloseButton button.](../../../assets/tutorials/interactive-ui/StarterGui-HUDContainer-SettingsMenu-CloseButton-UIAspectRatioConstraint.png)![Finalized close button in upper-right corner of settings menu.](../../../assets/tutorials/interactive-ui/Settings-Menu-Design-Close-Button-Finalized.png) ## Create the control modules An extensible **control module** setup makes interactive UI management more streamlined than individual scripts placed within each object. `Class.ModuleScript|ModuleScripts` facilitate this extensible functionality by letting you reuse code between scripts on different sides of the client‑server boundary or the same side of the boundary. ### Stateful object controller The following stateful object controller module lets you attach behavior to UI objects such as **SettingsButton** and **SettingsMenu**, and easily toggle/tween between various states. To create the module: 1. Insert a `Class.ModuleScript` into the `Class.ReplicatedStorage` container and rename it to **StatefulObjectController**.![Explorer window showing the StatefulObjectController module inside the ReplicatedStorage container.](../../../assets/tutorials/interactive-ui/ReplicatedStorage-StatefulObjectController.png) 2. Paste the following code inside the module:```lua local TweenService = game:GetService("TweenService") local StatefulObjectController = {} StatefulObjectController.__index = StatefulObjectController export type StateName = string export type State = { transition: TweenInfo, properties: { [string]: any }, } function StatefulObjectController.hydrate(props: { object: Instance, states: { [StateName]: State }, initialStateName: StateName }) local object, states, initialStateName = props.object, props.states, props.initialStateName local self = setmetatable({ states = states, currentStateName = initialStateName, tweens = {}, }, StatefulObjectController) -- Create tweens for reuse to avoid making new tweens every time state is changed for stateName, state in states do self.tweens[stateName] = TweenService:Create(object, state.transition, state.properties) end self:setState(self.currentStateName) return self end function StatefulObjectController:setState(stateName: StateName) local stateTween: Tween = self.tweens[stateName] if not stateTween then warn(string.format("Attempted to set %s to unknown state '%s'", self.object:GetFullName(), stateName)) return end self.currentStateName = stateName -- Make sure other tweens aren't conflicting for _, tween in self.tweens do tween:Cancel() end stateTween:Play() end return StatefulObjectController ```** Code Explanation**| Lines | Purpose | | --- | --- | | `6`‑`10` | Defines and exports the Luau types to improve autocomplete and linting when using this module. | | `12`‑`33` | Function used to initialize and attach behavior to a stateful UI object, accepting a `Class.GuiObject` as `object`, a table of states and their respective `Datatype.TweenInfo` data, and an initial state name. Inside this function, lines `26`‑`28` construct `Class.Tween\|Tweens` for each state, and line `30` calls the module's `setState()` function to set the object to the initial state. | | `35`‑`50` | Function used to set/change the state of a stateful object. Line `42` sets the object's `currentStateName` to the state being set, lines `45`‑`47` cancel any running tweens to prevent conflicts, and line `49` executes the state's tween(s). | ### Slider controller An additional module initializes and controls the two volume sliders. It also allows you to connect a callback function to each slider in order to detect player interaction with the slider and apply desired changes in the experience. 1. Insert a `Class.ModuleScript` into the `Class.ReplicatedStorage` container and rename it to **SliderController**.![Explorer window showing the SliderController module inside the ReplicatedStorage container.](../../../assets/tutorials/interactive-ui/ReplicatedStorage-SliderController.png) 2. Paste the following code inside the module:```lua local SliderController = {} SliderController.__index = SliderController export type Value = number export type OnChanged = (Value) -> () function SliderController.hydrate(props: { object: Instance, onChanged: OnChanged, initialValue: Value? }) local object, onChanged, initialValue = props.object, props.onChanged, props.initialValue local handle = object:FindFirstChild("Handle", true) if not handle then warn(string.format("Attempted to hydrate slider %s but couldn't find Handle", object:GetFullName())) end local innerFill = object:FindFirstChild("InnerFill", true) if not innerFill then warn(string.format("Attempted to hydrate slider %s but couldn't find InnerFill", object:GetFullName())) end local dragDetector = handle:FindFirstChildWhichIsA("UIDragDetector") if not dragDetector then warn(string.format("Attempted to hydrate slider %s but couldn't find UIDragDetector", object:GetFullName())) end local self = setmetatable({ handle = handle, innerFill = innerFill, dragDetector = dragDetector, value = initialValue or 0.5, onChanged = onChanged, }, SliderController) -- Set initial value self:setValue(self.value) -- Connect detector to player manipulation self.dragConnection = dragDetector.DragContinue:Connect(function() self:setValue(handle.Position.X.Scale) end) return self end function SliderController:setValue(value: Value) local clampedValue = math.clamp(value, 0, 1) self.value = clampedValue -- Update the handle position and inner frame size to match self.handle.Position = UDim2.fromScale(clampedValue, 0.5) self.innerFill.Size = UDim2.fromScale(clampedValue, 1) -- Run the user's callback with the latest value local changeSuccess, changeResult = pcall(self.onChanged, clampedValue) if not changeSuccess then warn("Error in slider callback:", changeResult) end end return SliderController ```** Code Explanation**| Lines | Purpose | | --- | --- | | `7`‑`46` | Function used to initialize and attach behavior to a slider element, accepting the slider's parent `Class.Frame` as `object`, an `onChanged` callback function to process slider changes, and an initial value for the slider's handle position. Inside the function, lines `14`‑`27` confirm that the slider's [handle](#interactive-handle), [inner fill](#inner-fill), and `Class.UIDragDetector` elements exist. Line `38` calls the module's `setValue()` function and lines `41`‑`43` connect the drag detector's `Class.UIDragDetector.DragContinue\|DragContinue` event to the module's `setValue()` function (see next row for details). | | `48`‑`61` | Function used to set the value of the slider. Inside the function, lines `53`‑`54` ensure the handle's position and inner fill size are synced, and line `57` passes the changed value to the callback function so that you can utilize the value as needed. | ## Create the settings script With the [settings button](#create-the-settings-button) and [settings menu](#create-the-settings-menu) finalized, you can hook everything together with a single script that utilizes the [control modules](#create-the-control-modules). 1. Insert a new `Class.LocalScript` into **HUDContainer** and rename it to **SettingsScript** to describe its purpose. Note that this script should be at the same level (sibling) as **SettingsMenu** and **SettingsButton**, the top‑level UI objects it will manage.![Explorer window showing the SettingsScript script inside the HUDContainer container.](../../../assets/tutorials/interactive-ui/StarterGui-HUDContainer-SettingsScript.png) 2. Paste the following code inside the script:```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local SoundService = game:GetService("SoundService") local SliderController = require(ReplicatedStorage.SliderController) local StatefulObjectController = require(ReplicatedStorage.StatefulObjectController) local HUDContainer = script.Parent -- Initialize settings button local settingsButton = StatefulObjectController.hydrate({ object = HUDContainer:FindFirstChild("SettingsButton"), states = { menuOpen = { transition = TweenInfo.new(0.5, Enum.EasingStyle.Exponential, Enum.EasingDirection.Out), properties = { Rotation = 45, }, }, menuClosed = { transition = TweenInfo.new(0.5, Enum.EasingStyle.Exponential, Enum.EasingDirection.Out), properties = { Rotation = 0, }, }, }, initialStateName = "menuClosed" }) -- Initialize settings menu frame local settingsMenu = StatefulObjectController.hydrate({ object = HUDContainer:FindFirstChild("SettingsMenu"), states = { menuOpen = { transition = TweenInfo.new(0.5, Enum.EasingStyle.Bounce, Enum.EasingDirection.Out), properties = { Position = UDim2.fromScale(0.5, 0.5), Visible = true, }, }, menuClosed = { transition = TweenInfo.new(0), properties = { Position = UDim2.fromScale(0.5, 0.4), Visible = false, }, }, }, initialStateName = "menuClosed" }) -- Initialize effects volume slider local effectsAudio = SoundService:FindFirstChild("Effects") local effectsVolumeSlider = SliderController.hydrate({ object = HUDContainer:FindFirstChild("EffectsVolumeSlider", true), initialValue = effectsAudio and effectsAudio.Volume or 0.5, onChanged = function(value: SliderController.Value) if effectsAudio then effectsAudio.Volume = value end end, }) -- Initialize background volume slider local backgroundAudio = SoundService:FindFirstChild("Background") local backgroundVolumeSlider = SliderController.hydrate({ object = HUDContainer:FindFirstChild("BackgroundVolumeSlider", true), initialValue = backgroundAudio and backgroundAudio.Volume or 0.5, onChanged = function(value: SliderController.Value) if backgroundAudio then backgroundAudio.Volume = value end end, }) -- Connect buttons to player interaction HUDContainer:FindFirstChild("SettingsButton").Activated:Connect(function() local targetState = if settingsButton.currentStateName == "menuClosed" then "menuOpen" else "menuClosed" settingsButton:setState(targetState) settingsMenu:setState(targetState) end) HUDContainer:FindFirstChild("CloseButton", true).Activated:Connect(function() settingsButton:setState("menuClosed") settingsMenu:setState("menuClosed") end) ``` 3. Reference the following sections to explore how the script utilizes the [control modules](#create-the-control-modules).** Settings Button** Lines `10`-`27` initialize and attach behavior to the [settings button](#create-the-settings-button) as follows: | Lines | Purpose | | --- | --- | | `10`‑`11` | A local reference `settingsButton` is declared and hydrated with the `StatefulObjectController.hydrate()` function, and the **SettingsButton** button within **HUDContainer** is referenced as the object to hydrate. | | `12`‑`25` | A `states` table is passed with two unique states, `menuOpen` and `menuClosed`. Each state contains a transition to declare (through a `Datatype.TweenInfo`) how the particular state will reach its target properties defined in the `properties` table. In this case, `menuOpen` declares a state change to 45° for the button's `Class.ImageButton.Rotation\|Rotation` with an `Enum.EasingStyle.Exponential\|Exponential` tween of ½ second, while `menuClosed` effectively restores the button to its default rotation of `0`. | | `26` | The button's **initial** state is set to `menuClosed` (this could be set to any state name within the `states` table). | Further down in the script (lines `76`-`86`), buttons are connected to player interaction to trigger UI state changes: | Lines | Purpose | | --- | --- | | `76`‑`82` | **SettingsButton** is connected to the `Class.GuiButton.Activated\|Activated` event of the `Class.GuiButton` class to call an anonymous function. Inside the anonymous function, lines `77`‑`79` toggle a `targetState` variable between `menuOpen` and `menuClosed`. Line `80` then calls `setState()` to set the button's state to `targetState` and line `81` does the same to **SettingsMenu** so that it toggles open or closed with activation of **SettingsButton**. | | `83`‑`86` | **CloseButton** is connected to the `Class.GuiButton.Activated\|Activated` event to call an anonymous function which sets the state of both **SettingsButton** and **SettingsMenu** to `menuClosed`. |** Settings Menu** Lines `30`-`49` initialize and attach behavior to the [settings menu](#create-the-settings-menu) as follows: | Lines | Purpose | | --- | --- | | `30`‑`31` | A local reference `settingsMenu` is declared and hydrated with the `StatefulObjectController.hydrate()` function and the **SettingsMenu** frame within **HUDContainer** is referenced as the object to hydrate. | | `32`‑`46` | Similar to the settings button, a `states` table is passed with two unique states, `menuOpen` and `menuClosed`. Here, `menuOpen` declares a state change to`Datatype.UDim2.fromScale(0.5, 0.5)` for the frame's `Class.Frame.Position\|Position` with a `Enum.EasingStyle.Bounce\|Bounce` tween of ½ second, as well as a `Class.Frame.Visible\|Visible` state of `true`. In contrast, `menuClosed` declares a positional state change to`Datatype.UDim2.fromScale(0.5, 0.4)` and `Class.Frame.Visible\|Visible` to `false`, but the `Datatype.TweenInfo` with `0` duration on line `41` effectively makes the state change occur instantly. | | `48` | The frame's initial state is set to `menuClosed`. | Further down in the script (lines `76`-`86`), the settings menu is linked to player interaction: | Lines | Purpose | | --- | --- | | `81` | Sets the same `targetState` for **SettingsMenu** to sync with activation of **SettingsButton**. | | `85` | Sets the state of **SettingsMenu** to `menuClosed` when **CloseButton** is activated. |** Volume Sliders** Lines `51`-`73` initialize and attach behavior to **EffectsVolumeSlider** and **BackgroundVolumeSlider** as follows: | Lines | Purpose | | --- | --- | | `52` | Sets a local reference to the **Effects** `Class.SoundGroup` within `Class.SoundService`. A `Class.SoundGroup` lets you interrelate multiple `Class.Sound\|Sounds` and control the volume of every sound in the group through the group's `Class.SoundGroup.Volume\|Volume` property. | | `53`‑`54` | A local reference `effectsVolumeSlider` is declared and hydrated with the `SliderController.hydrate()` function and the **EffectsVolumeSlider** frame within **HUDContainer** ⟩ **SettingsMenu** is referenced as the object to hydrate. Note that the second parameter of `true` for `Class.Instance:FindFirstChild()\|FindFirstChild()` tells the method to look recursively down the **HUDContainer** branch to locate **EffectsVolumeSlider**. | | `55` | The slider's initial value is set to the current `Class.SoundGroup.Volume\|Volume` of the **Effects** sound group, or `0.5` as a fallback. | | `56`‑`60` | Establishes the callback function to apply slider value changes to the volume of the **Effects** sound group. | | `64`‑`73` | Essentially the same as lines `52`-`61`, except to hydrate the **BackgroundVolumeSlider** frame within **HUDContainer** ⟩ **SettingsMenu** and apply slider value changes to the volume of the **Background** sound group. | With **SettingsScript** in place, the experience now offers a fully operational interactive UI example that links a related set of UI objects to player interaction. > **Success:** If your interactive UI doesn't match the appearance or behavior displayed in this tutorial, remember that you can inspect [UI Fundamentals - Interactivity](https://www.roblox.com/games/138499606610292/UI-Fundamentals-Interactivity) as a completed reference template. --- title: "Proximity prompts" url: /docs/en-us/tutorials/use-case-tutorials/ui/proximity-prompts last_updated: 2026-06-29T19:34:15Z description: "The process for creating an interactive prompt that triggers an action on user input." --- # Proximity prompts You can create interactive proximity prompts that appear when users approach objects in the 3D space, then trigger actions on user input. This tutorial uses the [Dungeon Delve](https://www.roblox.com/games/6749940622/Dungeon-Delve-Learn) project as a showcase. To follow along, open it in Studio before proceeding. ![Edit in Studio option from the experience's main page](../../../assets/tutorials/proximity-prompts/Dungeon-Delve-Edit-Place.png) ## Create a prompt On-screen prompts are generated by a `Class.ProximityPrompt` object parented to an `Class.Attachment`, `Class.BasePart`, or `Class.Model`. 1. Select the **PrisonDoor** model in the 3D view or from the **Explorer** (**Workspace** → **PromptObjects** → **PrisonDoor**). 2. Expand its tree and select the **Door** object. 3. Placing a prompt on an `Class.Attachment` gives you more control on where the point of interaction occurs, versus placing it directly on the part/model. Insert a new **Attachment** and rename it to **PromptAttachment**. 4. Locate the new attachment's **Position** property and set it to **-2.25**, **-0.5**, -**0.5**. This will move it in front of the door's key hole. 5. Hover over **PromptAttachment** and insert a new **ProximityPrompt** object. ### Prompt appearance Prompts consist of three primary elements, each of which can be controlled by the following properties: - **ObjectText** — An optional name for the object being interacted with. - **ActionText** — An optional action name shown to the user. - **KeyboardKeyCode** — The keyboard key which will trigger the prompt. - **GamepadKeyCode** — The gamepad button which will trigger the prompt. To customize the appearance of the prison door prompt, make the following changes: 1. In the **Properties** window, locate the **ObjectText** property and type in **Door**. 2. For the **ActionText** property, type in **Pick Lock**. ### Activation distance Prompts appear when the user's **character** moves within the defined **MaxActivationDistance** range of the prompt object's parent. The default value works well in most cases, but you can push user interaction closer to the lock by changing **MaxActivationDistance** to **4**. ### Hold duration The **HoldDuration** property value determines how quickly the prompt's action is triggered, in seconds. Since this door must be picked to unlock it, change the **HoldDuration** property to **4**. ## Implement an action The best way to detect prompt events is through `Class.ProximityPromptService` — this lets you detect events centrally without attaching a script to each prompt object. A basic framework is as follows: ```lua local ProximityPromptService = game:GetService("ProximityPromptService") -- Detect when prompt is triggered local function onPromptTriggered(promptObject, player) end -- Detect when prompt hold begins local function onPromptHoldBegan(promptObject, player) end -- Detect when prompt hold ends local function onPromptHoldEnded(promptObject, player) end -- Connect prompt events to handling functions ProximityPromptService.PromptTriggered:Connect(onPromptTriggered) ProximityPromptService.PromptButtonHoldBegan:Connect(onPromptHoldBegan) ProximityPromptService.PromptButtonHoldEnded:Connect(onPromptHoldEnded) ``` | Event | Description | | --- | --- | | `Class.ProximityPromptService.PromptTriggered\|PromptTriggered` | Fires when a user interacts with a prompt (**after** the duration for a prompt with non-zero `Class.ProximityPrompt.HoldDuration\|HoldDuration`). | | `Class.ProximityPromptService.PromptButtonHoldBegan\|PromptButtonHoldBegan` | Fires when a user begins interacting with a prompt of non-zero `Class.ProximityPrompt.HoldDuration\|HoldDuration`. | | `Class.ProximityPromptService.PromptButtonHoldEnded\|PromptButtonHoldEnded` | Fires when a user stops interacting with a prompt of non-zero `Class.ProximityPrompt.HoldDuration\|HoldDuration` | In the [Dungeon Delve](https://www.roblox.com/games/6749940622/Dungeon-Delve-Learn) project, these events are managed by the **PromptEvents** script within **ServerScriptService**. Inside the script, the events above simply call functions within the **ObjectActions** `Class.ModuleScript`, also located in **ServerScriptService**. ```lua local ProximityPromptService = game:GetService("ProximityPromptService") local ServerScriptService = game:GetService("ServerScriptService") local ObjectActions = require(ServerScriptService.ObjectActions) -- Detect when prompt is triggered local function onPromptTriggered(promptObject, player) ObjectActions.promptTriggeredActions(promptObject, player) end -- Detect when prompt hold begins local function onPromptHoldBegan(promptObject, player) ObjectActions.promptHoldBeganActions(promptObject, player) end -- Detect when prompt hold ends local function onPromptHoldEnded(promptObject, player) ObjectActions.promptHoldEndedActions(promptObject, player) end -- Connect prompt events to handling functions ProximityPromptService.PromptTriggered:Connect(onPromptTriggered) ProximityPromptService.PromptButtonHoldBegan:Connect(onPromptHoldBegan) ProximityPromptService.PromptButtonHoldEnded:Connect(onPromptHoldEnded) ``` Proximity prompts are a convenient and customizable solution for in-game object interaction. Check out the `Class.ProximityPrompt` and `Class.ProximityPromptService` reference pages for even more ways to control prompt behavior, and explore other interactive objects in [Dungeon Delve](https://www.roblox.com/games/6749940622/Dungeon-Delve-Learn) for creative inspiration. --- title: "Use case tutorials" url: /docs/en-us/tutorials/use-case-tutorials/use-case-tutorial-overview last_updated: 2026-06-29T19:34:15Z description: "An overview of creation use case tutorials covering topics like modeling, UI, and visual effects." --- # Use case tutorials # Modeling **Modeling** is the process of crafting 3D objects for props, environments, and characters within your experiences. This process is important because it allows you to create both the visual and interactive content that encourages players to explore and engage with the 3D space. ## Studio modeling Use solid modeling operations directly in Studio to form complex shapes by unioning and negating basic parts. Intro to world building Create neon signs with solid modeling Assemble modular environments ## Blender modeling Use third-party modeling tools like Blender to create meshes, then import them into Studio using the Importer. Box modeling 101 Vertex coloring meshes **##### Related docs** - [Parts](/docs/en-us/parts.md) - [Solid modeling](/docs/en-us/parts/solid-modeling.md) - [Meshes](/docs/en-us/parts/meshes.md) - [Texture and decals](/docs/en-us/parts/textures-decals.md) - [Mesh specifications](/docs/en-us/art/modeling/specifications.md) - [Texture specifications](/docs/en-us/art/modeling/texture-specifications.md) - [Export requirements](/docs/en-us/art/modeling/export-requirements.md) - [Roblox Blender plugin](/docs/en-us/art/modeling/roblox-blender-plugin.md) # User interfaces **User interfaces** (UI) are visual or interactive elements that either provide essential gameplay information or teach players how to complete unique user flows within your experiences. Strategically implementing UI in key locations on-screen and in the 3D space is important because UI communicates what players need to know to be successful and to have a positive user experience, such as information about their main objective or their location in the overall map. Create HUD meters Create interactive UI Create proximity prompts Create score bars **##### Related docs** - [On-screen UI](/docs/en-us/ui/on-screen-containers.md) - [In-experience UI](/docs/en-us/ui/in-experience-containers.md) - [Labels](/docs/en-us/ui/labels.md) - [Buttons](/docs/en-us/ui/buttons.md) - [Frames](/docs/en-us/ui/frames.md) - [UI appearance modifiers](/docs/en-us/ui/appearance-modifiers.md) - [Proximity prompts](/docs/en-us/ui/proximity-prompts.md) - [Position and size UI objects](/docs/en-us/ui/position-and-size.md) # Audio **Audio** is sound that either increases a player's enjoyment of an experience, or delivers feedback for their actions, gameplay status, or events. While often overlooked, audio is important because it immerses players within your environments, makes 3D worlds feel realistic, and provides information that helps players meet their goals. Add 2D audio Add 3D audio Add voice chat Add text-to-speech Add speech-to-text **##### Related docs** - [Audio overview](/docs/en-us/audio.md) - [Audio assets](/docs/en-us/audio/assets.md) - [Audio objects](/docs/en-us/audio/objects.md) - [Audio effects](/docs/en-us/audio/effects.md) # Lighting **Lighting** is luminescence within the 3D space that allows players to see and interact with objects in your experiences. Adding and customizing light sources to meet the needs of your experience is important because lighting establishes mood, highlights important objects, and immerses players in either realistic or stylized environments. Enhance indoor environments Enhance outdoor environments **##### Related docs** - [Light sources](/docs/en-us/effects/light-sources.md) - [Global lighting](/docs/en-us/environment/lighting.md) - [Atmospheric effects](/docs/en-us/environment/atmosphere.md) - [Post-processing effects](/docs/en-us/environment/post-processing-effects.md) - [Skyboxes](/docs/en-us/environment/skybox.md) # VFX **Visual Effects** (VFX) are elements that enhance the player's user experience by either improving the environment with aesthetics that have real-world or custom physical behavior, or providing visual feedback on gameplay actions, such as casting a spell or interacting with a hazard. Designing environments and workflows with VFX in mind is important because VFX adds dynamic movement to scenes that would otherwise be static, and it can provoke emotions like joy or fear from players at key narrative moments. Create laser beams Create waterfalls Create volcanoes **##### Related docs** - [Particle emitters](/docs/en-us/effects/particle-emitters.md) - [Beams](/docs/en-us/effects/beams.md) - [Trails](/docs/en-us/effects/trails.md) - [Flipbooks](/docs/en-us/effects/particle-emitters.md#flipbooks) # Physics **Physics** in Studio is the simulation of real-world physical systems like gravity, aerodynamics, and friction. Understanding how Roblox emulates physics in real time is important because it allows you to create realistic and intuitive behavior in your experiences that's familiar and intuitive to players. Physics 101 Create moving objects Creating spinning objects **##### Related docs** - [Assemblies](/docs/en-us/physics/assemblies.md) - [Network ownership](/docs/en-us/physics/network-ownership.md) - [Mechanical constraints](/docs/en-us/physics/mechanical-constraints.md) - [Mover constraints](/docs/en-us/physics/mover-constraints.md) - [Roblox units](/docs/en-us/physics/units.md) # Animation **Animation** is the process of creating motion for your characters, objects, and environments using internal rigs that you can pose between keyframes. Designing models with rigging and skinning data is important because it allows you to create engaging, dynamic, and expressive objects that bend and flex naturally. ## Studio animation Use the **Animation Editor** directly in Studio to create animations for models or meshes with rigging and skinning data. Create character animations Play character animations ## Blender animation Use third-party modeling tools like Blender to create meshes with rigging and skinning data, then import them into Studio using the Importer. Rig and skin a simple mesh Rig and skin a humanoid model **##### Related docs** - [Animation editor](/docs/en-us/animation/editor.md) - [Animation capture](/docs/en-us/animation/capture.md) - [Curve editor](/docs/en-us/animation/curve-editor.md) - [Inverse kinematics](/docs/en-us/animation/inverse-kinematics.md) - [Use animations](/docs/en-us/animation/using.md) # Scripting **Scripting** in Studio is the use of the [Luau](https://luau.org/) programming language to add custom, dynamic behavior to your experiences so that they are engaging and fun to play. Understanding how programming works on Roblox is important because it allows you to write and organize your scripts that control things like logic, movement, events, and interactions between objects in the 3D space in a way that improves performance and optimization. Script types and locations Services Properties and attributes Events **##### Related docs** - [Reuse code](/docs/en-us/scripting/module.md) - [Schedule code](/docs/en-us/scripting/scheduler.md) - [Luau reference](/docs/en-us/luau.md) # Camera and input **Camera** is the object that determines what the player is able to see in the 3D space, and **input** is the method or device in which players access and interact with your experience, such as a mobile device or gamepad. Designing gameplay around your camera configuration and which input types players can use to access your experience is important because your design decisions directly impact accessibility, user experience, and how players can engage with objects. Create a first-person camera Create a side-scrolling camera Create an isometric camera Detect user input **##### Related docs** - [Customize the camera](/docs/en-us/workspace/camera.md) - [Mouse and keyboard input](/docs/en-us/input/mouse-and-keyboard.md) - [Mobile input](/docs/en-us/input/mobile.md) - [Gamepad input](/docs/en-us/input/gamepad.md) # Data storage **Data stores** are a service you can use to save and load persistent player data across different player sessions. They store important information, like a player's progress or inventory, and allow you to retrieve it for the player next time they join your experience. Without data stores, your players would lose all of their progress every time they left the experience. Save player data Create a custom leaderboard **##### Related docs** - [Data stores vs memory stores](/docs/en-us/cloud-services/data-stores-vs-memory-stores.md) - [Data stores](/docs/en-us/cloud-services/data-stores.md) --- title: "Basic particle effects" url: /docs/en-us/tutorials/use-case-tutorials/vfx/basic-particle-effects last_updated: 2026-06-29T19:34:15Z description: "The process of creating particles to elevate the visual effects in your experience." --- # Basic particle effects **Particles** are used to create a wide range of effects in experiences, from smoke and fire to splashing water. They can help bring your experience to life and can provide valuable visual feedback for your players. In this tutorial, you will use a `Class.ParticleEmitter` object to create a sparkling effect around a part. ## Particle emitters The `Class.ParticleEmitter` is a special type of object that will **emit** particles from its position when parented to a part or `Class.Attachment`. The particles themselves are **2D images** that always face the camera. Particles move or change color according to the emitter's properties. By changing just a few of these, you can make an incredible array of different effects. The example provided below is a model of a gold nugget. A simple particle effect will be used to make it sparkle. 1. Create a `Class.MeshPart` in the `Class.Workspace` and name it **GoldNugget**. 2. Set the **MeshId** property to `rbxassetid://2903918852`. 3. Set the **Material** property to **Neon** and the **Color** property to `255, 180, 0`.![Color, Material and MeshID properties](../../../assets/tutorials/basic-particle-effects/meshProperties.jpg) 4. Insert a `Class.ParticleEmitter` object into the part.![GoldNugget and ParticleEmitter in the Explorer](../../../assets/tutorials/basic-particle-effects/explorerMeshPart.jpg) Once you add the emitter, white stars will immediately start emitting from the part. The star is the default particle image.![Default particles emitting from the GoldNugget part](../../../assets/tutorials/basic-particle-effects/defaultParticles.jpg) > **Info:** The pre-made `Class.Fire` and `Class.Smoke` objects can be a convenient way to save time on creating effects, but they are far less customizable than ParticleEmitters. ## Particle emission Particles are generated randomly throughout the entire volume of a part. They emerge from one **face** of the part according to the **EmissionDirection** property. It defaults to **Top**, meaning the top surface of a part. If you rotate the part, the direction the particles are emitted from will change accordingly. If you resize the part to be bigger, the particles will be emitted over a wider area. ## Particle properties ### Color The first thing to change is the emitter color — **gold** would be a much more appropriate color for a sparkle effect. Change the **Color** property to **255, 200, 50**. ### Lifetime By default, the particles last for 5 to 10 seconds, which is too long for sparkles. You can change this using the **Lifetime** property. Lifetime can have either a single value or a **Min** and a **Max** value, with particles lasting for a random amount of time in seconds between the two values. Change the **Lifetime** property of the emitter to **0.5, 1** to shorten the duration of the particles. ### Rate The **Rate** property specifies how many particles are emitted per second. The default value is 20 which is fairly high for a sparkle effect. Change the **Rate** property of the emitter to **7**. ### Speed The **Speed** property determines how fast a particle will travel in studs per second. It can be either a single value or a range of values. A sparkle effect should be nice and slow — a range of 2 to 3 seconds works nicely. Change the **Speed** property to **2, 3**. ![Particle Speed property](../../../assets/tutorials/basic-particle-effects/particleSpeed.jpg) ### SpreadAngle The particles are currently emitting straight up from the part. To get an even spread of particles in all directions, change the **SpreadAngle** property. SpreadAngle has an **X** and a **Y** value which determine the range of angles that a particle can be emitted. This is calculated from both sides around the axes. A value of **180, 0** would emit particles in a flat circle across the X-axis; a value of **180, 180** would emit particles in every direction around the part. Change the **SpreadAngle** property of the emitter to **180, 180**. ### Additional properties To further improve the sparkle effect, try the following property values. | Property | Value | | --- | --- | | Size | 0.3 | | LightEmission | 1 | | Transparency | 0.5 | | Drag | 1.5 | With that, your sparkling part is complete. You can try complementing the sparkling effect with a gentle glow by adding a `Class.PointLight` to the part, as shown at the start of the course — check out the [Lighting with Props](/docs/en-us/tutorials/use-case-tutorials/lighting/light-with-props.md) course to learn more about light objects. --- title: "Create volcanic eruptions with VFX" url: /docs/en-us/tutorials/use-case-tutorials/vfx/create-volcanoes last_updated: 2026-06-29T19:34:15Z description: "The process of creating volcanoes to elevate your visual and gameplay requirements." --- # Create volcanic eruptions with VFX A **volcano** is a crater within the earth's crust that spews lava and smoke due to internal pressure from magma and dissolved gasses. Experiences often include volcanoes within challenging gameplay areas, such as locations where players must balance the danger of lava and molten rock hazards with the reward of valuable resources, boss battle arenas, or environments that dynamically change as the volcano cyclically erupts. Using the [Volcano Island - Start](https://create.roblox.com/dashboard/creations/experiences/5825677169/overview) `.rbxl` file as a starting place and [Volcano Island - Complete](https://www.roblox.com/games/17374256579/Volcano-Island-Complete) as a reference, this tutorial shows you how to transform the environment into a volcanic eruption with custom lighting and VFX objects that represent real-world physical behavior, including guidance on: - Breaking down referential material into individual components with distinct visual and behavioral characteristics. - Configuring surface ripples to emulate hot fluid rising to the surface and disrupting the surface of the caldera. - Configuring embers to emulate pieces of molten rock quickly cooling as they rise into the atmosphere. - Configuring lava that splashes and flows from the caldera to emulate various types of lava viscosity states. - Configuring a smoke plume to emulate harsh impurities rising into the sky that catch players' attention. > **Info:** You can create your own textures in third-party texture creation tools and follow along with your own design. For information on importing textures for use in Studio, see [Asset Manager](/docs/en-us/projects/assets/manager.md). ![The starting volcano terrain you can use to complete this tutorial.](../../../assets/tutorials/creating-volcanoes/Volcano-Start.jpg)_Volcano Island - Start_ ![The complete volcano with VFX objects you will create by the end of this tutorial.](../../../assets/tutorials/creating-volcanoes/Volcano-Complete.jpg)_Volcano Island - Complete_ ## Break down reference To create credible volcanoes, it's important to reference real-world volcanic eruptions in the design process because it allows you to break down the subject matter into individual components with distinct visual and behavioral characteristics. For example, the sample [Volcano Island - Complete](https://www.roblox.com/games/17374256579/Volcano-Island-Complete) experience references the volcanic eruption in Iceland to inform all texture and VFX design decisions relating to the caldera and its surrounding terrain. ![A far out view of an Iceland volcanic eruption.](../../../assets/tutorials/creating-volcanoes/Volcano-NoComponents.jpg) It's useful to break down a volcano that's erupting into individual components so that you can plan how to utilize different VFX objects to mimic their real-world counterparts. To demonstrate, this tutorial breaks the sample volcano into five unique components: - **Surface Ripples** – The small waves of lava on the surface of the caldera. - **Embers** – The small, light pieces of molten rock that rise to the sky from the caldera. - **Lava Splashes** – The thin molten rock that bursts out from the caldera due to internal pressure within the volcano. - **Lava Flow** – The viscous molten rock that oozes away from the caldera. - **Smoke Plume** – The warm, billowing gas that rises into the sky from the caldera. ![The volcanic eruption reference with all five components highlighted.](../../../assets/tutorials/creating-volcanoes/Volcano-Components.jpg) ![The sample volcanic eruption with the same five components highlighted to compare the reference image with the final result.](../../../assets/tutorials/creating-volcanoes/Sample-Components.jpg) The following sections provide an in-depth analysis of the different design decisions and techniques you can use to recreate each of these components. As you review these decisions and experiment with various lighting, `Class.ParticleEmitter`, and `Class.Beam` properties, you will learn how to utilize lighting and VFX to solve the unique environmental requirements for your own experiences. ## Configure lighting To make an environmental element a point of interest within your experience, it's important to increase its contrast against the overall environment so that it stands out as something that players should explore. For example, to draw players into the caldera, you can configure your lighting sources so that the volcano's lava appears to glow as the only light source within an otherwise dark environment. Studio provides two high-level types of lighting sources you can use for this technique: - **Global lighting** - Produces lighting for the entire outdoor environment. - **Local lighting** - Produces lighting around where you place them within your experience. This section of the tutorial teaches you how to utilize both types of lighting sources to make your volcanic eruption the most significant point of interest within your scene, as well as create a dramatic effect for your environmental storytelling. To illustrate, review how the same final volcano without custom lighting feels like an unobtrusive hazard in an otherwise cheerful environment while the volcano with custom lighting feels like a dangerous presence in a dark, melancholic environment. ![The complete version of the sample volcanic eruption with default light sources.](../../../assets/tutorials/creating-volcanoes/Default-Lighting.jpg)_With default light sources_ ![The complete version of the sample volcanic eruption with custom light sources.](../../../assets/tutorials/creating-volcanoes/Custom-Lighting.jpg)_With custom light sources_ ### Local lighting Local lighting is the luminescence from local [light sources](/docs/en-us/effects/light-sources.md) in your experience, such as `Class.SpotLight`, `Class.SurfaceLight`, and `Class.PointLight` objects. Local lighting is important to add to your volcano because while you can apply brightness to your `Class.ParticleEmitter` and `Class.Beam` textures, they cannot fill the canyon with enough light alone to realistically simulate how the caldera and its flowing lava would illuminate the environment in the real world. ![The complete version of the sample volcanic eruption without local lighting.](../../../assets/tutorials/creating-volcanoes/Without-LocalLighting.jpg)_Without local lighting_ ![The complete version of the sample volcanic eruption with local lighting.](../../../assets/tutorials/creating-volcanoes/With-LocalLighting.jpg)_With local lighting_ It's helpful to configure your local lighting before your global lighting for this tutorial because without the local light sources, you cannot see the 3D space to configure your VFX objects. However, common workflows require you to iterate on both local and global lighting concurrently to see the effects of your changes on VFX objects, so it's important to be flexible to design requirements of your own experiences. To recreate the local lighting for the volcano in the sample [Volcano Island - Complete](https://www.roblox.com/games/17374256579/Volcano-Island-Complete) place file: 1. In the **Explorer** window, create a **Folder** in the **Workspace** to contain all local light source objects, then rename the folder **LocalLighting**. 2. Insert three **block** parts into the **LocalLighting** folder, then rename them **LightCaldera**, **LightMagma**, and **LightOutflow**, respectively. 3. Move the parts to a position where they can light the entirety of the volcano. 1. Move **LightCaldera** to the center of the space between the caldera and the cliff. 2. Move **LightMagma** to the center of the crevice between the caldera and the magma outflow. 3. Move **LightOutflow** to slightly above where the outflow forks into two streams.![A front view of the volcano with three block parts positioned throughout the volcano's valley.](../../../assets/tutorials/creating-volcanoes/LL-3.jpg) 4. Insert a **Pointlight** into each part.![A front view of the volcano with three block parts with point light visual aids.](../../../assets/tutorials/creating-volcanoes/LL-4.jpg) 5. Select the **PointLight** child of **LightCaldera**, then in the **Properties** window, 1. Set **Brightness** to `15` to make the light source much brighter. 2. Set **Color** to `255, 85, 0` to tint the light to a dark orange hue. 3. Set **Range** to `60` to illuminate the entire caldera area.![A front view of the volcano with three block parts. The block part nearest the caldera emits orange light.](../../../assets/tutorials/creating-volcanoes/LL-5.jpg) 6. Select the **PointLight** children of **LightMagma** and **LightOutflow**, then in the **Properties** window, 1. Set **Brightness** to `2` to make the light source slightly brighter. 2. Set **Color** to `255, 81, 0` to tint the light to an orange hue. 3. Set **Range** to `50` to illuminate the crevice and outflow areas.![A front view of the volcano with three block parts that are all emitting orange light.](../../../assets/tutorials/creating-volcanoes/LL-6.jpg) 7. In the **Explorer** window, select all block parts in the **LocalLighting** folder, then in the **Properties** window, set **Transparency** to `1` to make the blocks invisible.![A front view of the volcano that has orange lighting throughout the volcano's valley.](../../../assets/tutorials/creating-volcanoes/LL-7.jpg) ### Global lighting Global lighting is the luminescence from either the sun or moon in an experience. By adjusting a couple of key default properties in the `Class.Lighting` service and its child post-processing effects objects, you can dramatically change how global light appears to players, as well as how it interacts with any other object you place in the experience, including `Class.ParticleEmitter` and `Class.Beam` textures. For example, to ensure that the `Class.Beam` textures that produce a flowing lava effect later in the tutorial are able to glow, you must configure `Class.BloomEffect` properties to exaggerate the lighting like a camera viewing a bright light. Similarly, to simulate more realistic colors at night, you must also adjust the effect's properties to desaturate the overall environment. ![The complete version of the sample volcanic eruption without bloom.](../../../assets/tutorials/creating-volcanoes/Without-Bloom.jpg)_Without bloom_ ![The complete version of the sample volcanic eruption with bloom.](../../../assets/tutorials/creating-volcanoes/With-Bloom.jpg)_With bloom_ To recreate the global lighting in the sample [Volcano Island - Complete](https://www.roblox.com/games/17374256579/Volcano-Island-Complete) place file: 1. In the **Explorer** window, select the **Lighting** service, then in the **Properties** window, 1. Set **Ambient** to `133, 152, 176` to set a light gray hue over the entire outdoor environment. 2. Set **Brightness** to `2` to make the global light source slightly dimmer. 3. Set **ColorShift_Top** to `207, 178, 72` to set a yellow hue to the light that reflects from surfaces facing the global light source. 4. Set **LightingStyle** to **Realistic** to utilize Roblox's most advanced lighting technology. 5. Set **ClockTime** to `4.3` to set the time of day about a quarter after 4am. 6. Set **GeographicLatitude** to `199` to modify the position of the moon. 7. Set **ExposureCompensation** to `-1` to expose the environment to half of the exposure from the moon.![A front view of the volcano with darkened outdoor lighting.](../../../assets/tutorials/creating-volcanoes/GL-1.jpg) 2. In the **Explorer** window, select **Bloom** child of the **Lighting** service, then in the **Properties** window, 1. Set **Intensity** to `0.75` to slightly dim all colors within the environment. 2. Set **Size** to `80` to create a wider bloom effect. 3. Set **Threshold** to `0.85` to allow more colors in the environment to glow.![A front view of the volcano with darkened outdoor lighting and local lighting that glows orange.](../../../assets/tutorials/creating-volcanoes/GL-2.jpg) 3. In the **Explorer** window, insert a **ColorCorrection** object into the **Lighting** service, then in the **Properties** window, 1. Set **Brightness** to `0.017` to slightly shift the color of your pixels. 2. Set **Contrast** to `0.25` to make a sharper contrast between light and dark colors in the environment. 3. Set **Saturation** to `-0.15` to desaturate colors in the environment. 4. Set **TintColor** to `255, 214, 143` to tint pixels with a light yellow hue.![A front view of the volcano with pitch black outdoor lighting and local lighting that glows orange.](../../../assets/tutorials/creating-volcanoes/GL-3.jpg) 4. **(Optional)** Provide indirect light with clouds in the sky. 1. In the **Explorer** window, insert a **Clouds** object into the **Terrain** service. 2. Select the **Clouds** object, then in the **Properties** window, 1. Set **Cover** to `1` to provide full cloud cover to the sky. 2. Set **Density** to `0.08` to make the cloud cover less dense. 3. Set **Color** to `136, 143, 152` to set a light gray hue to the cloud cover.![A front view of the volcano with pitch black outdoor lighting, gray clouds, and local lighting that glows orange.](../../../assets/tutorials/creating-volcanoes/GL-4.jpg) ## Configure volcano Now that your local and global lighting configuration is complete, it's time to configure all of the VFX objects relating to the actual volcano and its surrounding terrain. As you follow these instructions that exactly recreate the final environment within the sample [Volcano Island](https://www.roblox.com/games/17374256579/Volcano-Island-Complete) `.rbxl` file, observe how each step works together to add character, movement, and illuminance to the environment. ### Surface ripples **Surface ripples** are the small lava waves that flow across the surface of the caldera as a result of internal magma and pressurized gasses moving upward from beneath the earth's crust. This visual phenomenon conveys the real-world physical process of convection, or the movement within a fluid when hotter fluid rises to the surface, and it adds to the realism of your scene. Surface ripples provide valuable insight for players into the state of the volcanic eruption. For example, if magma and gas are moving upward with enough force to disrupt the state of the lava on top of the caldera and cause it to churn, players can deduce that the volcano is actively erupting with boiling magma, so they should exercise caution around this gameplay area. ![A top-down view of the caldera without surface ripples.](../../../assets/tutorials/creating-volcanoes/Without-SurfaceRipples.jpg)_Without surface ripples_ _With surface ripples_ To demonstrate this process, the sample uses a `Class.ParticleEmitter` object slightly below the base of the caldera to emit flat, bright particles that slowly expand and contract on top of a dark backdrop. This allows the particles to emulate the behavior of lava smoothly and continuously churning in a way that's similar to the realistic way elements move in nature. ![The 2D texture that represents surface ripples that result from churning lava.](../../../assets/tutorials/creating-waterfalls/Foam-Texture.png)_Foam Ripples Texture = rbxassetid://16811365086_ To recreate the surface ripples on top of the caldera in the sample [Volcano Island - Complete](https://www.roblox.com/games/17374256579/Volcano-Island-Complete) place file: 1. In the **Explorer** window, create a **Folder** in the **Workspace** to contain all caldera objects, then rename the folder **Caldera**. 2. Create a backdrop to provide a high contrast background for the surface ripples. 1. Insert a **block** part into the **Caldera** folder, then rename it **Backdrop**. 2. Position and scale **Backdrop** to slightly beyond the full surface area of the caldera. The part appears as though it's glowing because of its close proximity to the caldera's local light source.![A top-down view of the caldera with a highlighted block part that's glowing yellow.](../../../assets/tutorials/creating-volcanoes/Ripples-2B.jpg) 3. Select **Backdrop**, the in the **Properties** window, 1. Set **Color** to `0, 0, 0` to make the block black. 2. Set **Material** to **Foil** to provide texture to the backdrop.![A top-down view of the caldera with a block part that's black with some rough texture.](../../../assets/tutorials/creating-volcanoes/Ripples-2C.jpg) 4. Create the ripple effect. 1. Duplicate **Backdrop**, rename it **MagmaRipples**, then scale it slightly down to the surface area of the caldera. 2. Select **MagmaRipples**, then in the **Properties** window, set **Transparency** to `1` to make the block invisible. 3. Insert a **ParticleEmitter** into **MagmaRipples**, then rename the emitter **Ripples**. 4. Select **Ripples**, then in the **Properties** window, 1. Set **Texture** to `rbxassetid://16829556885` to render particles that look like foam ripples. 2. Set **Orientation** to **VelocityPerpendicular** to emit particles perpendicular to the direction of their movement. 3. Set **Color** to a color sequence in which particles are brown, become bright red, then turn maroon. 1. Click the **Color** property, then click the **⋯** button. A color sequence pop-up displays.![A close up view of Studio's Properties window with the Color property's ellipsis button highlighted.](../../../assets/tutorials/creating-waterfalls/Cascades-1G1.png) Each triangle on the bottom axis of the color sequence is a **keypoint** that determines the color value of the property at that point of the particle's lifetime. 1. Set the following time and value properties throughout the color sequence: - **Time** = `0`, **RGB Value** = `130, 53, 2` - **Time** = `0.5`, **RGB Value** = `224, 37, 0` - **Time** = `1`, **RGB Value** = `147, 5, 0` 4. Set **Size** to a number sequence in which particles increase in size toward the middle of their lifetime before returning to their original size with a small window of variation. 1. Click the **Size** property, then click the **⋯** button. A number sequence pop-up displays. By default, the graph is a straight line and the image remains the same size from left to right.![A close up view of the Size number sequence graph.](../../../assets/tutorials/creating-waterfalls/Cascades-1H1.png) Each square at the start and end of the number sequence is a **keypoint** that determines the size value of the property at that point of the texture from left to right. 1. Set the following time and value properties throughout the number sequence: - **Time** = `0`, **Value** = `4.81`, **Envelope** = `0.438` - **Time** = `0.341`, **Value** = `8.75`, **Envelope** = `0.48` - **Time** = `0.497`, **Value** = `9.38`, **Envelope** = `0.5` - **Time** = `0.644`, **Value** = `8.75`, **Envelope** = `0.48` - **Time** = `1`, **Value** = `4.81`, **Envelope** = `0.438` 5. Set **Transparency** to a number sequence in which particles start as transparent, become more opaque toward the middle of their lifetime, then become transparent again at the end of their lifetime. 1. Click the **Transparency** property, then click the **⋯** button. 2. Set the following time and value properties throughout the number sequence: - **Time** = `0`, **Value** = `1`, **Envelope** = `0` - **Time** = `0.3`, **Value** = `0.387`, **Envelope** = `0.0375` - **Time** = `0.5`, **Value** = `0.269`, **Envelope** = `0.0812` - **Time** = `0.7`, **Value** = `0.381`, **Envelope** = `0.05` - **Time** = `0`, **Value** = `1`, **Envelope** = `0` 6. Set **ZOffset** to `-2` to offset the texture to be slightly away from the caldera. 7. Set **Lifetime** to `5, 8` to randomly set each particle's lifetime between 5 and 8 seconds. 8. Set **Rate** to `12` to emit 12 particles per second. 9. Set **Rotation** to `-360, 360` to randomly orient each particle in a circle. 10. Set **Speed** to `0.01` to emit each particle one tenth of a stud per second. 11. Set **LightEmission** to `1` to significantly brighten the particles. 12. Set **LightInfluence** to `0` to prevent the environmental light from affecting the color of the particles. 13. Set **Brightness** to `15` to scale the light emitted from the emitter. ### Embers **Embers** are the small, light pieces of molten rock that burst out of the caldera, rapidly expelling heat as they rise into the atmosphere. Similar to surface ripples, embers reveal that internal pressure below the earth's crust is pressing upwards, causing hot elements to break the lava's surface tension to release pressure. The sample emulates this process by using a `Class.ParticleEmitter` object to emit particles with motion blur that's baked into the texture. As the particles rise and reach the end of their lifetime, the emitter squashes the particles into a 1:1 ratio to reform the particles into circles. This technique allows the particles to seem as though they are rapidly moving as they exit the caldera, then slow down as they dissipate into the sky. ![The 2D texture that represents embers bursting from the caldera.](../../../assets/tutorials/creating-volcanoes/Embers-Texture.jpg)_Embers Texture = rbxassetid://17581858560_ In addition, as particles rise, they change their color, opacity, and size to reflect their changing temperature. For example, they begin their lifetime as large, brown particles, but quickly transform into small orange, then maroon particles. This strategy also has the benefit of subtly reflecting the lighting throughout the canyon, increasing a player's immersion within the environment. To recreate the glowing embers from the surface of the caldera in the sample [Volcano Island - Complete](https://www.roblox.com/games/17374256579/Volcano-Island-Complete) place file: 1. Insert a **cylinder** part into the **Caldera** folder, then rename it **GlowingEmbers**. 2. Position **GlowingEmbers** so that it is on top of the surface ripples, then scale it until it fills the interior of the caldera. The part appears as though it's glowing because of its close proximity to the caldera's local light source.![A close front view of the caldera with a cylinder part that's glowing yellow.](../../../assets/tutorials/creating-volcanoes/Embers-2.jpg) 3. Select **GlowingEmbers**, then in the **Properties** window, set **Transparency** to `1` to make the cylinder invisible. 4. Insert a **ParticleEmitter** into **GlowingEmbers**, then rename the emitter **Embers**. 5. Select **Embers**, then in the **Properties** window, 1. Set **Texture** to `rbxassetid://17581858560` to render particles that look like an elongated oval horizontally and vertically. 2. Set **Orientation** to **VelocityParallel** to emit particles parallel to their direction of movement. 3. Set **Color** to a color sequence in which particles are brown, become orange, then turn maroon. - **Time** = `0`, **RGB Value** = `130, 53, 2` - **Time** = `0.5`, **RGB Value** = `224, 82, 0` - **Time** = `1`, **RGB Value** = `147, 5, 0` 1. Set **Size** to a number sequence in which particles slowly decrease in size across their lifetime. - **Time** = `0`, **Value** = `0.313`, **Envelope** = `0.1` - **Time** = `1`, **Value** = `0`, **Envelope** = `0` 1. Set **Squash** to a number sequence in which particles slightly elongate about midway through their lifetime. - **Time** = `0`, **Value** = `-3`, **Envelope** = `0` - **Time** = `0.323`, **Value** = `-0.188`, **Envelope** = `0` - **Time** = `1`, **Value** = `-0.5`, **Envelope** = `0` 1. Set **Transparency** to a number sequence in which particles randomly change their opacity to simulate embers glowing as they rise. The actual values are not important, just that they change a lot during the course of their lifetimes. 1. Set **ZOffset** to `1` to offset the texture to be slightly away from the caldera. 2. Set **Lifetime** to `1, 5` to randomly set each particle's lifetime between 1 and 5 seconds. 3. Set **Speed** to `5, 8` to randomly emit each particle between 5 and 8 studs per second. 4. Set **SpreadAngle** to `180, 180` to emit particles in an angle along the X and Z axis. 5. Set **Acceleration** to `0, 10, 0` to simulate upward force and pull particles to the sky. 6. Set **Drag** to `0.8` to have particles lose their speed with exponential decay. 7. Set **LightEmission** to `1` to significantly brighten the particles. 8. Set **LightInfluence** to `0` to prevent the environmental light from affecting the color of the particles. 9. Set **Brightness** to `20` to scale the light emitted from the emitter. ### Lava splashes **Lava splashes** are bursts of thin molten rock that erupt upward from the volcano as a result of internal magma and pressurized gasses applying enough force to break the surface tension of the lava on top of the caldera. This core component of a volcano is one of the most common signs in determining that a volcano is no longer dormant and is actively erupting. The sample represents this process with two `Class.ParticleEmitter` objects that utilize [flipbooks](/docs/en-us/effects/particle-emitters.md#flipbooks) to animate each particle's texture over their lifetime. The first particle emitter emits particles that look like dense, high-viscosity splashes that are heavy and thick, causing them to rise and fall slowly back down into the volcano. Conversely, the second particle emitter emits particles that look like webby, low-viscosity splashes that are light and thin, causing them to rise and fall quickly. ![The 2D texture that represents a dense splash texture.](../../../assets/tutorials/creating-volcanoes/DenseSplash-Texture.jpg)_Dense Splash Texture = rbxassetid:/17363669906_ ![The 2D texture that represents a webby splash texture.](../../../assets/tutorials/creating-volcanoes/WebbySplash-Texture.jpg)_Webby Splash Texture = rbxassetid://17363668312_ Each particle emitter animates their texture over 64 frames to emulate smooth, lifelike physical behavior. While you could use only one particle emitter, the repetition of the animation would become apparent and break the immersion for your players because they would see the exact same animation every time. However, when two particles animate flipbooks with slightly different custom properties, it is much more difficult to spot the repetition. The sample also provides a `Class.ParticleEmitter` object that emits particles that look like aerated splashes to represent even lighter webby splashes. This technique fills the space with dynamic movement and further hides the repetition of the flipbook animations. As an extra bonus, if your experience also includes the waterfall from [Creating Waterfalls](/docs/en-us/tutorials/use-case-tutorials/vfx/create-waterfalls.md), you're able to reuse the same texture twice for different gameplay areas, saving you memory and improving performance on low-end devices. ![The 2D texture that represents aerated splashes.](../../../assets/tutorials/creating-waterfalls/Splashes-Droplets.png)_Webby Texture = rbxassetid://17082061238_ To recreate the splashing lava from the surface of the caldera in the sample [Volcano Island - Complete](https://www.roblox.com/games/17374256579/Volcano-Island-Complete) place file: 1. Insert a **cylinder** part into the **Caldera** folder, then rename it **SplashingLava**. 2. Position **SplashingLava**. so that it is on top of the surface ripples, then scale it until it covers the middle of the caldera where you want the lava to splash. The part appears as though it's glowing because of its close proximity to the caldera's local light source.![A close front view of the caldera with a cylinder part that's glowing yellow.](../../../assets/tutorials/creating-volcanoes/Splashes-2.jpg) 3. Select **SplashingLava**, then in the **Properties** window, set **Transparency** to `1` to make the cylinder invisible. 4. Insert a **ParticleEmitter** into **SplashingLava**, then rename the emitter **WebbySplashes**. 5. Select **WebbySplashes**, then in the **Properties** window, 1. Set **Texture** to **rbxassetid://17363668312** to render particles that look like webby splashes. 2. Set **Orientation** to **FacingCameraWorldUp** to emit particles facing the camera, but rotating only on the vertical upward world Y axis. 3. Set **Color** to `255, 152, 79` to tint the particles to a light orange hue. 4. Set **Size** to a number sequence in which particles increase in size across their lifetime with a window of variation. - **Time** = `0`, **Value** = `4.31`, **Envelope** = `0.762` - **Time** = `1`, **Value** = `6.2`, **Envelope** = `0.875` 1. Set **Squash** to a number sequence in which particles slightly elongate throughout their lifetime with a window of variation. - **Time** = `0`, **Value** = `-0.075`, **Envelope** = `0.263` - **Time** = `1`, **Value** = `-0.413`, **Envelope** = `0.412` 1. Set **ZOffset** to `1` to offset the texture to be slightly away from the caldera. 2. Set **Lifetime** to `1.5, 2` to randomly set each particle's lifetime between 1.5 and 2 seconds. 3. Set **Rate** to `0.37` to emit a particle about every 3 seconds. 4. Set **RotSpeed** to `-20, 20` to randomly emit each particle between -20 and 20 degrees per second. 5. Set **Speed** to `2` to emit each particle 2 studs per second. 6. Set **SpreadAngle** to `5, 5` to emit particles in a small angle along the X and Z axis. 7. Set **FlipbookLayout** to **Grid8x8** to animate the texture over a 64-frame duration. 8. Set **FlipbookMode** to **Oneshot** to ensure the animation only plays once during its lifetime. 9. Set **Drag** to `0.5` to have particles lose their speed with exponential decay. 10. Set **LightEmission** to `0.1` to slightly brighten the particles. 11. Set **LightInfluence** to `0.25` to significantly reduce how much the environmental light affects the color of particles. 6. Duplicate **DenseSplashes**, then in the **Properties** window, modify the following properties to provide variation to the additional lava splashes. 1. Set **Name** to **DenseSplashes**. 2. Set **Texture** to **rbxassetid://17363669906** to render particles that look like dense splashes. 3. Set **Size** to a number sequence in which particles increase in size across their lifetime with a window of variation. - **Time** = `0`, **Value** = `5.75`, **Envelope** = `0.762` - **Time** = `1`, **Value** = `7.37`, **Envelope** = `0.875` 1. Set **Squash** to a number sequence in which particles slightly elongate throughout their lifetime with a window of variation. - **Time** = `0`, **Value** = `0`, **Envelope** = `0.225` - **Time** = `1`, **Value** = `-0.262`, **Envelope** = `0.15` 1. Set **Rate** to `0.289` to emit a particle nearly every fourth of a second. 7. Fill the caldera with additional splashes. 1. Insert a **ParticleEmitter** into **SplashingLava**, then rename the emitter **SplashFill**. 2. Select **SplashFill**, then in the **Properties** window, 1. Set **Texture** to **rbxassetid://17082061238** to render particles that look like lighter webby splashes 2. Set **Orientation** to **FacingCameraWorldUp** to emit particles facing the camera, but rotating only on the vertical upward world Y axis. 3. Set **Color** to `255, 152, 33` to tint the particles to an orange hue. 4. Set **Size** to a number sequence in which particles increase in size across their lifetime with a window of variation. - **Time** = `0`, **Value** = `1.25`, **Envelope** = `0.388` - **Time** = `1`, **Value** = `6.38`, **Envelope** = `0.563` 1. Set **Transparency** to a number sequence in which particles start as transparent, become opaque, then become transparent again toward the end of their lifetime. - **Time** = `0`, **Value** = `1`, **Envelope** = `0` - **Time** = `0.19`, **Value** = `0`, **Envelope** = `0` - **Time** = `0.795`, **Value** = `0`, **Envelope** = `0` - **Time** = `1`, **Value** = `1`, **Envelope** = `0` 1. Set **ZOffset** to `1` to offset the texture to be slightly away from the caldera. 2. Set **Lifetime** to `1.5` to set each particle's lifetime to 1.5 seconds. 3. Set **Rate** to `8` to emit a particle every 8 seconds. 4. Set **Rotation** to `0, 360` to randomly orient each particle in a half circle. 5. Set **RotSpeed** to `-50, 50` to randomly emit each particle between -50 and 50 degrees per second. 6. Set **Speed** to `12, 20` to randomly emit each particle between 12 and 20 studs per second. 7. Set **SpreadAngle** to `45, 45` to emit particles in an angle along the X and Z axis. 8. Set **Acceleration** to `0, -25, 0` to simulate gravity and pull particles back down. 9. Set **Drag** to `1` to have particles lose their speed with exponential decay. 10. Set **LightEmission** to `1` to significantly brighten the particles. 11. Set **LightInfluence** to `0` to prevent the environmental light from affecting the color of the particles. 12. Set **Brightness** to `8` to scale the light emitted from the emitter. > **Warning:** Features with custom textures such as flipbooks cost memory to render. If you use many flipbooks with other features that use high memory, then clients automatically deactivate flipbooks when they are low on memory, which is likely for older mobile phones. To reduce the memory usage of flipbooks, use fewer unique animated particle effects or choose a texture of smaller resolution. ### Lava flow A **lava flow** is a mass of lava that erupts and oozes away from the caldera and across the earth's surface during a volcanic eruption. As the lava cools due to its exposure to air, it solidifies and transforms into solid rock, creating new land mass. To simulate this process, the sample layers multiple `Class.Beam` objects on top of each other with seamless textures and property configurations that emulate the behavioral characteristics of lava cooling as it travels further away from the caldera: - The **bottom layer** renders a flat color that transforms from a warm to a cool color to communicate that the lava is beginning to drop in temperature, such as bright red to a dark maroon. - The **middle layer** renders a black texture that looks like dark crust with holes that reveal the glowing lava underneath. - The **top layer** renders the same texture as the middle layer at a slower rate, flipped attachments, and opposite property configurations. This ensures that the textures never have the opportunity to fully line up with each other while rendering in the same direction, which would allow players to easily detect the unrealistic texture repetition. _Bottom layer_ _Middle Layer_ _Top Layer_ Layering three `Class.Beam` objects creates an illusion of parallax to make the lava seem like it has a sense of depth and volume where the lava is flowing at different rates, even though they are only three 2D images. This lets players know that there is a sense of turbulence within the canyon, both on and below the lava's surface. ![The 2D texture that represents the crust on top of the flowing lava.](../../../assets/tutorials/creating-volcanoes/Crust-Texture.jpg)_Crust Texture = rbxassetid://17023930265_ To recreate the flowing magma from the caldera in the sample [Volcano Island - Complete](https://www.roblox.com/games/17374256579/Volcano-Island-Complete) place file: 1. In the **Explorer** window, create a **Folder** in the **Workspace** to contain all flowing magma objects, then rename the folder **FlowingMagma**. 2. Insert a **block** part into the **FlowingMagma** folder, then rename it **MagmaRiverBeam**. 3. Move **MagmaRiverBeam** to slightly under the edge of the caldera.![An angled top down view of the volcano with a block part highlighted.](../../../assets/tutorials/creating-volcanoes/LavaFlow-3.jpg) 4. Configure attachments for all flowing magma beams from the caldera to use to render their textures. 1. Insert an attachment into **MagmaRiverBeam**, then rotate the attachment until the yellow visual aid points toward the caldera. 2. Insert another attachment into **MagmaRiverBeam**, position it toward the fork in the crevice, then rotate the attachment until the yellow visual aid points down into the ground.![An angled top down view of the volcano with two attachment visual aids highlighted.](../../../assets/tutorials/creating-volcanoes/LavaFlow-4.jpg) 5. Insert a **Beam** into **MagmaRiverBeam**, then rename it **Magma**. 6. Assign the part's attachments to **Magma**. 1. In the **Explorer** window, select **Magma**. 2. In the **Properties** window, 1. Set **Attachment0** to the attachment at the edge of the caldera. 2. Set **Attachment1** to the attachment at the fork in the crevice. The beam renders its default texture between the two attachments.![An angled top down view of the volcano with two block parts rendering the default beam texture between the attachments.](../../../assets/tutorials/creating-volcanoes/LavaFlow-6.jpg) 7. Customize the beam's visual appearance so it looks like flowing magma. 1. In the **Explorer** window, verify **Magma** is still selected. 2. In the **Properties** window, 1. Set **Width0** to `50` to widen the texture from the axis that it starts to render. 2. Set **Width1** to `50` to widen the texture as it meets the fork in the crevice. 3. Set **CurveSize0** to `-50` to curve the texture away from the crevice's floor. 4. Set **CurveSize1** to `5` to curve the texture into the fork in the crevice. 5. Set **Color** to a color sequence that starts bright red and becomes dark red over the beam's lifetime to simulate the magma cooling. - **Time** = `0`, **RGB Value** = `255, 51, 0` - **Time** = `0.5`, **RGB Value** = `211, 39, 0` - **Time** = `1`, **RGB Value** = `118, 24, 0` 1. Set **Transparency** to a number sequence that allows the magma to be more vibrant between the attachment points. - **Time** = `0`, **Value** = `1` - **Time** = `0.0916`, **Value** = `0` - **Time** = `0.867`, **Value** = `0` - **Time** = `0.941`, **Value** = `0.725` - **Time** = `1`, **Value** = `1` 1. Set **LightEmission** to `1` to significantly brighten the beam. 2. Set **LightInfluence** to `0` to prevent the environmental light from affecting the color of the beam. 3. Set **Brightness** to `8` to scale the light emitted from the beam. 8. Insert another **Beam** into **MagmaRiverBeam**, rename it **Crust1**, then attach the part's attachments to **Crust1** using the same process in step 6. 9. Customize the beam's visual appearance so it looks like crust on top of the magma. 1. In the **Explorer** window, verify **Crust1** is still selected. 2. In the **Properties** window, 1. Set **Texture** to **rbxassetid://17023930265** to render a new texture that looks like flowing crust. 2. Set **Width0** to `35` to widen the texture from the axis that it starts to render. 3. Set **Width1** to `25` to widen the texture as it meets the fork in the crevice. 4. Set **TextureSpeed** to `0.01` to significantly slow down the flow of the texture. 5. Set **TextureLength** to `3` to slightly stretch the texture's length. 6. Set **CurveSize0** to `-50` to curve the texture away from the crevice's floor. 7. Set **CurveSize1** to `5` to curve the texture into the fork in the crevice. 8. Set Color to `83, 83, 83` to tint the beam gray. 9. Set **Transparency** to a number sequence that allows the crust to be more vibrant between the attachment points. - **Time** = `0`, **Value** = `1` - **Time** = `0.22`, **Value** = `0` - **Time** = `0.85`, **Value** = `0` - **Time** = `1`, **Value** = `1` 1. Set **ZOffset** to `1` to offset the texture to be slightly away from the caldera. 10. Duplicate **Crust1**, rename it **Crust2**, then in the **Properties** window, 1. Set **Attachment0** to the attachment at the fork in the crevice. 2. Set **Attachment1** to the attachment at the edge of the caldera. 3. Set **Width0** to `25` to widen the texture from the axis that it starts to render. 4. Set **Width1** to `35` to widen the texture as it meets the fork in the crevice. 5. Set **TextureSpeed** to `-0.008` to significantly slow down the flow of the texture. 6. Set **TextureLength** to `2` to slightly unstretch the texture's length. 7. Set **CurveSize0** to `-5` to curve the texture away from the crevice's floor. 8. Set **CurveSize1** to `50` to curve the texture into the fork in the crevice. 9. Set **ZOffset** to `2` to offset the texture from the other crust. 11. **(Optional)** Using this same process, create more beams around the fork in the crevice to offshoot the magma. Be sure to adjust the properties to slow the textures and simulate magma that's darker in color as it cools in temperature. ### Smoke plume A **smoke plume** from the caldera emits warm pressurized gas, steam, and volcanic ash into the atmosphere. This mixture of volcanic emission can be seen for miles in the real world, so volcano designs often incorporate large smoke plumes to be a significant point of interest in the 3D space. Rather than emitting thick, pyroclastic smoke that one would see immediately following an explosive eruption, the sample uses a `Class.ParticleEmitter` object to emit particles that look like thin smoke vapors that change color as they rise upwards. This technique accomplishes two goals: - It breaks up the silhouette of the background crater, creating more visual interest around the volcanic eruption. - It allows the smoke to look like it accepts light from the environment while still emitting a dark presence as though the smoke is burning impurities into the air before turning gray like the night sky. ![The 2D texture that represents the smoke rising out of the caldera.](../../../assets/tutorials/creating-waterfalls/Mist-Texture.png)_Smoke Texture = rbxassetid://16830667309_ To recreate the smoke plume from the caldera in the sample [Volcano Island - Complete](https://www.roblox.com/games/17374256579/Volcano-Island-Complete) place file: 1. Insert a **block** part into the **Caldera** folder, then rename it **SmokePlume**. 2. Position **SmokePlume** underneath the caldera, then scale it to roughly the size of the caldera's surface area.![An angled side view of the volcano with a block part highlighted underneath the caldera.](../../../assets/tutorials/creating-volcanoes/Plume-2.jpg) 3. Insert a **ParticleEmitter** into **SmokePlume**, then rename the emitter **Smoke**. 4. Select **Smoke**, then in the **Properties** window, 1. Set **Texture** to **rbxassetid://16830673704** to render particles that look like thick smoke. 2. Set **Color** to a color sequence in which particles simulate lighting in the environment from the caldera to the sky by starting black, becoming a light peach, then gray. - **Time** = `0`, **RGB Value** = `0, 0, 0` - **Time** = `0.374`, **RGB Value** = `195, 104, 76` - **Time** = `0.469`, **RGB Value** = `225, 121, 86` - **Time** = `0.709`, **RGB Value** = `111, 111, 111` - **Time** = `1`, **RGB Value** = `113, 113, 113` 1. Set **Transparency** to a number sequence in which particles start as transparent, become fully opaque early into their lifetime, then become transparent again near the end of their lifetime. - **Time** = `0`, **Value** = `1` - **Time** = `0.0622`, **Value** = `0` - **Time** = `0.845`, **Value** = `0` - **Time** = `0`, **Value** = `1` 1. Set **ZOffset** to `-10` to offset the texture to be closer to the top of the caldera. 2. Set **Lifetime** to `50, 60` to randomly set each particle's lifetime between 50 and 60 seconds. 3. Set **Rate** to `0.3` to emit a particle about every 3 seconds. 4. Set **Rotation** to `-360, 360` to randomly orient each particle in a circle. 5. Set **RotSpeed** to `-5, 5` to randomly emit each particle between -5 and 5 degrees per second. 6. Set **SpreadAngle** to `5, 5` to emit particles in a small angle along the X and Z axis. 7. Set **Acceleration** to `0, 7, 0` to simulate upward force and pull particles to the sky. 8. Set **Drag** to `1` to have particles lose their speed with exponential decay. 9. Enable **WindAffectsDrag** to have the wind in the environment push the smoke around. 10. Set **LightEmission** to `0.1` to slightly brighten the particles. 11. Set **LightInfluence** to `0.06` to significantly reduce how much the environmental light affects the color of particles. 5. In the **Command Bar**, input the following string to increase each particle's size from 40 to 100 studs across their lifetime with a small window of variation:```lua workspace.Caldera.SmokePlume.Smoke.Size = NumberSequence.new{NumberSequenceKeypoint.new(0,40,5), NumberSequenceKeypoint.new(1,100,15)} ``` --- title: "Create waterfalls with VFX" url: /docs/en-us/tutorials/use-case-tutorials/vfx/create-waterfalls last_updated: 2026-06-29T19:34:15Z description: "The process of creating waterfalls to elevate your visual and gameplay requirements." --- # Create waterfalls with VFX A **waterfall** is a point in a river or stream where water flows over one or more vertical drops into a body of water. Experiences often include waterfalls for visual aesthetics, such as to enhance the environment or create a visual point of interest, or for functional gameplay purposes, such as to hide resources or transition players between different areas in the place itself. Using the [Waterfall Island](https://www.roblox.com/games/16454663889/Use-Case-Tutorials-Volcano-Island) `.rbxl` file as a reference, this tutorial shows you how to create a waterfall with VFX objects that represent real-world physical behavior, including guidance on: - Breaking down referential material into individual components with distinct visual and behavioral characteristics. - Configuring cascades that fall at different rates to emulate water dispersing as it descends from a cliff. - Configuring splashes to emulate water becoming aerosolized as cascades impact the underlying plunge pool. - Configuring white water to emulate turbulent aerated water where the outflow crashes into boulders. - Configuring foam to emulate a capillary wave breaking surface tension. - Configuring mist to emulate mist vapors rising away and upward from the impact point, creating a rainbow players can view from any angle. > **Info:** You can create your own textures in third-party texture creation tools and follow along with your own design. For information on importing textures for use in Studio, see [Asset Manager](/docs/en-us/projects/assets/manager.md). ## Break down reference To create credible waterfalls, it's important to reference real-world natural features in the design process because it allows you to break down the subject matter into individual components with distinct visual and behavioral characteristics. For example, the sample [Waterfall Island](https://www.roblox.com/games/16454663889/Use-Case-Tutorials-Volcano-Island) experience references Snoqualmie Falls in Washington state to inform all texture and VFX design decisions relating to the waterfall and its surrounding terrain. ![A far out view of Snoqualmie Falls.](../../../assets/tutorials/creating-waterfalls/Falls-NoComponents.png) While a waterfall is a continuous and connected stream of water that involves multiple states of matter with dynamic fluid and air motion, it's useful to break down this complex system into individual components so that you can plan how to utilize different VFX objects to mimic their real-world counterparts. To demonstrate, this tutorial breaks the sample waterfall down into five unique components: - **Cascades** – The water that descends off the cliff. - **Splashes** – The water that scatters as cascades collide with the underlying plunge pool. - **White Water** – The turbulent water that arises as the water source approaches the edge of the cliff. - **Foam** – The aerated water that scatters horizontally upon the collision of the cascades and the plunge pool. - **Mist** – The cloud-like water that evaporates in the air as a result of the overall waterfall. ![Snoqualmie Falls with all five components highlighted.](../../../assets/tutorials/creating-waterfalls/Falls-Components.png) ![The sample waterfall with the same five components highlighted to compare the reference image with the final result.](../../../assets/tutorials/creating-waterfalls/Sample-Components.png) The following sections provide an in-depth analysis of the different design decisions and techniques you can use to recreate each of these waterfall components that make up the main drop in the sample's 3D space. As you review these decisions and experiment with various `Class.Beam` and `Class.ParticleEmitter` properties, you will learn how to utilize VFX to solve the unique environmental requirements for your own experiences. ## Configure cascades A **cascade** is the falling water that descends over the edge of cliffs or bluffs into underlying plunge pools. Cascades fall at different rates depending on both their volume of water and the scale of their drop into the plunge pool. For example, the sample's main drop appears to fall more slowly because there is a large volume of water falling over a large distance, but the sample's second drop appears to fall more quickly because a smaller volume of water is falling over a short distance. _Main drop_ _Second drop_ Furthermore, waterfalls often have layers of cascades that fall at different rates because the water disperses as it descends into the plunge pool. To demonstrate this principle, the sample uses multiple `Class.Beam` objects with seamless textures that render between `Class.Attachment` objects at varying speeds and lengths. In addition to providing the main drop with a more realistic falling behavior, this technique also creates an illusion of parallax to make the cascade seem like it has a sense of depth and volume even though it's only a 2D image. _This waterfall seems natural because the water descends and disperses at various rates._ _This waterfall seems unnatural because the water descends and disperses at the same rate._ To recreate the cascades for the main drop in the sample [Waterfall Island](https://www.roblox.com/games/16454663889/Use-Case-Tutorials-Volcano-Island) place file: 1. Create the outflow for the cascading water. 1. In the **Explorer** window, create a **Folder** in the **Workspace** to contain all outflow objects, then rename the folder **Outflow**. 2. Insert two **block** parts into the **Outflow** folder, then rename them **OutflowStart** and **OutflowStop**, respectively. 3. Move **OutflowStart** to where you want the outflow to begin, and move **OutflowStop** to the edge of the cliff.![An angled top-down view of the outflow water with two block parts positioned where the outflow texture should render.](../../../assets/tutorials/creating-waterfalls/Cascades-1C.png) 4. Insert an attachment into both **OutflowStart** and **OutflowStop**, then rotate the attachments until the yellow visual aid points upward.![An angled top-down view of the outflow water with two block parts that include attachements where the yellow visual aids point upward.](../../../assets/tutorials/creating-waterfalls/Cascades-1D.png) 5. Insert a **Beam** into the **Outflow** folder, then rename it **OutflowWater**. 6. Assign each part's attachment to **OutflowWater**. 1. In the **Explorer** window, select **OutflowWater**. 2. In the **Properties** window, 1. Set **Attachment0** to the attachment in **OutflowStart**. 2. Set **Attachment1** to the attachment in **OutflowStop**. The beam renders its default texture between the two attachments.![An angled top-down view of the outflow water with two block parts rendering the default beam texture between the attachments.](../../../assets/tutorials/creating-waterfalls/Cascades-1F.png) 7. Customize the beam's visual appearance so it looks like flowing water approaching the edge of the cliff. 1. In the **Explorer** window, verify **OutflowWater** is still selected. 2. In the **Properties** window, 1. Set **Texture** to `rbxassetid://4787437624` to render a new texture that looks like flowing foam. 2. Set **Width0** to `60` to widen the texture from the axis that it starts to render. 3. Set **Width1** to `20` to funnel the texture to the cliff's edge. 4. Set **TextureSpeed** to `0.4` to slow down the flow of the texture. 5. Set **TextureLength** to `64` to stretch the texture's length. 6. Set **TextureMode** to **Wrap** to repeat the texture to the amount of the beam's overall length in the 3D world between the attachments divided by its **TextureLength**. This allows the texture to look a lot more realistic to flowing water. 7. Set **Color** to a color sequence that alternates in color from dark and light blues to white. 1. Click the **Color** property, then click the **⋯** button. A color sequence pop-up displays.![A close up view of Studio's Properties window with the Color property's ellipsis button highlighted.](../../../assets/tutorials/creating-waterfalls/Cascades-1G1.png) Each triangle on the bottom axis of the color sequence is a **keypoint** that determines the color value of the property at that point of the particle's lifetime. 2. Set the following time and value properties throughout the color sequence: - **Time** = `0`, **RGB Value** = `208, 247, 255` - **Time** = `0.135`, **RGB Value** = `146, 235, 255` - **Time** = `0.248`, **RGB Value** = `255, 255, 255` - **Time** = `0.384`, **RGB Value** = `128, 183, 202` - **Time** = `0.757`, **RGB Value** = `166, 213, 248` - **Time** = `1`, **RGB Value** = `255, 255, 255` 8. Set **Transparency** to a number sequence that makes the water become more vibrant as it approaches the cliff's edge. 1. Click the **Transparency** property, then click the **⋯** button. A number sequence pop-up displays. By default, the graph is a straight line and the image remains the same transparency from left to right.![A close up view of the Transparency number sequence graph.](../../../assets/tutorials/creating-waterfalls/Cascades-1H1.png) Each square at the start and end of the number sequence is a **keypoint** that determines the transparency value of the property at that point of the texture from left to right. 2. Set the following time and value properties throughout the number sequence: - **Time** = `0`, **Value** = `1` - **Time** = `0.375`, **Value** = `0.725` - **Time** = `0.615`, **Value** = `0` - **Time** = `0.92`, **Value** = `1` - **Time** = `1`, **Value** = `1` 9. Scale each part to ensure the texture covers the width of the outflow pool. The outflow now appears to be flowing toward the edge of the cliff from all angles. 2. Create the fast running cascading water from the main drop. 1. In the **Explorer** window, create a **Folder** in the **Workspace** to contain all cascading water objects, then rename the folder **Cascades**. 2. Insert two **block** parts into the **Cascades** folder, then rename them **MainDropStart** and **MainDropStop**, respectively. 3. Move **MainDropStart** to the edge of the cliff, and move **MainDropStop** to where it's underneath the plunge pool.![An angled side view of the cliff with two block parts positioned where the fast running cascade texture should render.](../../../assets/tutorials/creating-waterfalls/Cascades-2C.png) 4. Configure attachments for all cascading water beams from the main drop to use to render their textures. 1. Insert an attachment into **MainDropStart**, then rotate the attachment until the yellow visual aid points away from the cliff. 2. Insert an attachment into **MainDropStop**, then rotate the attachment until the yellow visual aid points toward from the cliff.![An angled side view of the cliff with two block parts that include attachements. The block part at the top of the cliff includes a yellow visual aid that points away from the cliff, and the block part at the bottom of the cliff includes a yellow visual aid that points toward the cliff.](../../../assets/tutorials/creating-waterfalls/Cascades-2D.png) 5. Insert a **Beam** into the **Cascades** folder, then rename it **FastDrop**. 6. Assign each part's attachment to **FastDrop**. 1. In the **Explorer** window, select **FastDrop**. 2. In the **Properties** window, 1. Set **Attachment0** to the attachment in **MainDropStart**. 2. Set **Attachment1** to the attachment in **MainDropStop**. The beam renders its default texture between the two attachments.![An angled side view of the cliff with two block parts rendering the default beam texture between the attachments.](../../../assets/tutorials/creating-waterfalls/Cascades-2F.png) 7. Customize the beam's visual appearance so it looks like fast-running cascading water from the main drop. 1. In the **Explorer** window, verify **FastDrop** is still selected. 2. In the **Properties** window, 1. Set **Texture** to `rbxassetid://16808804567` to render a new texture that looks like flowing water. 2. Set **Width0** to `5` to widen the texture from the axis that it starts to render. 3. Set **Width1** to `10` to widen the texture as it meets the plunge pool. 4. Set **CurveSize0** to `10` to curve the texture away from the cliff. 5. Set **CurveSize1** to `20` to curve the texture into the plunge pool. 6. Set **TextureSpeed** to `1.3` to make the texture flow quickly. 7. Set **TextureLength** to `2` to slightly stretch the texture's length. 8. Set **Color** to a color sequence that alternates in color from dark and light blues to white. - **Time** = `0`, **RGB Value** = `208, 247, 255` - **Time** = `0.135`, **RGB Value** = `210, 246, 255` - **Time** = `0.25`, **RGB Value** = `255, 255, 255` - **Time** = `0.384`, **RGB Value** = `163, 187, 202` - **Time** = `0.757`, **RGB Value** = `214, 229, 248` - **Time** = `1`, **RGB Value** = `255, 255, 255` 1. Set **Transparency** to a number sequence that allows the cascading water to be more vibrant between the attachment points. - **Time** = `0`, **Value** = `1` - **Time** = `0.115`, **Value** = `0` - **Time** = `0.835`, **Value** = `0` - **Time** = `0.881`, **Value** = `.994` - **Time** = `1`, **Value** = `1` 1. Set **ZOffset** to `2` to offset the texture to be slightly away from the cliff. 2. Enable **FaceCamera** to ensure the cascading water is visible no matter the angle of the player from the water. 3. Create the slow running cascading water from the main drop. 1. Duplicate **FastDrop**, then rename the duplicate beam **SlowDrop**. 2. Customize the beam's visual appearance so it looks like fast running cascading water from the main drop. 1. In the **Explorer** window, select **SlowDrop**. 2. In the **Properties** window, 1. Set **Width1** to `20` to widen the texture even more as it meets the plunge pool. 2. Set **TextureLength** to `1.5` to slightly shorten the texture's length. 3. Set **TextureSpeed** to `1` to make the texture flow less quickly. 4. Set **ZOffset** to `0` to allow the texture to flow directly from the cliff's edge. 4. In the **Explorer** window, select all block parts in the **Outflow** folder, then in the **Properties** window, set **Transparency** to `1` to make the blocks invisible. ## Configure splashes When cascades impact the density of the underlying plunge pool, the water propels upward from the impact point to create **splashes**. As this aerosolized water propels upward, it expands and breaks apart from itself to produce droplets that scatter in various directions. To demonstrate this process, the sample uses two `Class.ParticleEmitter` objects at the base of the main drop. The first particle emitter emits particles that look like dense splashes that represent the weight of the water that begins to propel upward as the cascades impact the plunge pool. The second particle emitter emits particles that look like droplets that represent the water becoming aerosolized. When you configure these particle emitters to emit both of these particles at the same time but at different rates, the resulting visual effect emulates the real-world physical behavior of splashes. These types of details contribute to the realism of your VFX, and immerse players within the 3D space. ![The 2D texture that represents heavily aerosolized water before it breaks apart.](../../../assets/tutorials/creating-waterfalls/Splashes-Dense.png)_Dense Texture = rbxassetid://16829556885_ ![The 2D texture that represents heavily aerosolized water as it breaks apart.](../../../assets/tutorials/creating-waterfalls/Splashes-Droplets.png)_Droplets Texture = rbxassetid://17082061238_ To recreate the splashes at the base of the main drop in the sample [Waterfall Island](https://www.roblox.com/games/16454663889/Use-Case-Tutorials-Volcano-Island) place file: 1. In the **Explorer** window, create a **Folder** in the **Workspace** to contain all splash objects, then rename the folder **Splashes**. 2. Insert a **block** part into **Splashes**, then rename it **MainDropSplashes**. 3. Position and scale **MainDropSplashes** to the full surface area where the cascade impacts the plunge pool.![A close up view of the bottom of the cliff with a block part positioned at the point where the cascades impact the plunge pool.](../../../assets/tutorials/creating-waterfalls/Splashes-3.png) 4. Create the dense splash where the main drop's cascading water impacts the plunge pool. 1. Insert a **ParticleEmitter** into **MainDropSplashes**, then rename the emitter **SplashDense**. 2. Select **SplashDense**, then in the **Properties** window, 1. Set **Texture** to `rbxassetid://16829556885` to render particles that look like dense splashes. 2. Set **Color** to a color sequence that starts blue then turns white. - **Time** = `0`, **RGB Value** = `189, 246, 255` - **Time** = `1`, **RGB Value** = `255, 255, 255` 1. Set **Size** to a number sequence that steadily increases in size with a window of variation. - **Time** = `0`, **Value** = `1.81`, **Envelope** = `0.562` - **Time** = `1`, **Value** = `5.75`, **Envelope** = `1.31` 1. Set **Transparency** to a number sequence in which particles start as transparent, turn opaque, then become transparent again across their lifetime with a large window of variation. - **Time** = `0`, **Value** = `1`, **Envelope** = `0` - **Time** = `0.5`, **Value** = `0.181`, **Envelope** = `0.181` - **Time** = `1`, **Value** = `1`, **Envelope** = `0` 1. Set **Lifetime** to `0.25, 0.35` to randomly set each particle's lifetime between 250 and 350 milliseconds. 2. Set **Rate** to `30` to emit 30 particles per second. 3. Set **Rotation** to `-45, 45` to randomly orient each particle between -45 and 45 degrees per second. 4. Set **RotSpeed** to `-40, 40` to randomly emit each particle between -45 and 40 degrees per second. 5. Set **Speed** to `20, 35` to randomly emit each particle between 20 and 35 studs per second. 6. Set **SpreadAngle** to `50, 50` to emit particles in an angle along the X and Z axis. 7. Set **Acceleration** to `0, -40, 0` to simulate gravity and pull particles downward. 8. Set **LightEmission** to `0.5` to brighten the particles. 9. Set **LightInfluence** to `0.1` to significantly reduce how much the environmental light affects the color of particles. 5. Create the droplets where the main drop's cascading water impacts the plunge pool. 1. Duplicate **SplashDense**, then rename it **SplashDroplets**. 2. Select **SplashDroplets**, then in the **Properties** window, 1. Set **Texture** to `rbxassetid://17082061238` to render particles that look like droplets. 2. Set **Size** to a number sequence that rapidly increases in size with a window of variation. - **Time** = `0`, **Value** = `1.81`, **Envelope** = `0.562` - **Time** = `1`, **Value** = `8.69`, **Envelope** = `1.31` 1. Set **Transparency** to a number sequence in which particles start as transparent, rapidly turn opaque, then slowly start to become transparent again near the middle of their lifetime. - **Time** = `0`, **Value** = `1`, **Envelope** = `0` - **Time** = `0.104`, **Value** = `0.0625`, **Envelope** = `0.0625` - **Time** = `0.429`, **Value** = `0.0562`, **Envelope** = `0.0562` - **Time** = `1`, **Value** = `1`, **Envelope** = `0` 1. Set **Lifetime** to `0.15, 0.6` to randomly set each particle's lifetime between 150 and 600 milliseconds. 2. Set **Rate** to `20` to emit 20 particles per second. 6. In the **Explorer** window, select all block parts in the **Splashes** folder, then in the **Properties** window, set **Transparency** to `1` to make the blocks invisible. ## Configure white water **White water** arises as the water source becomes more turbulent as it gains velocity when approaching its descent and colliding with objects in its path. This results in aerated, webby water that appears white due to more air bubbles in the water source. To mimic this process, the sample uses two `Class.ParticleEmitter` objects that emit particles that look like heavily aerated splashes where the outflow crashes into the boulders at the edge of the cliff. The emitter doesn't use built in lighting, and instead prioritizes white and gray hues with various falling rates to demonstrate the quantity of air in the water. ![The 2D texture that represents heavily aerosolized water as it crashes into boulders.](../../../assets/tutorials/creating-waterfalls/Splashes-WhiteWater.png)_White Water Texture = rbxassetid://16808075391_ To recreate the white water where the outflow collides with the cliff's boulders in the sample [Waterfall Island](https://www.roblox.com/games/16454663889/Use-Case-Tutorials-Volcano-Island) place file: 1. In the **Explorer** window, create a **Folder** in the **Workspace** to contain all white water objects, then rename the folder **WhiteWater**. 2. Insert a **block** part into **WhiteWater**, then rename it **MainDropWW**. 3. Position and scale **MainDropWW** to the full surface area where the outflow begins to flow over the cliff.![A angled side view of the top of the cliff with a block part positioned at the point where outflow begins to flow over the cliff.](../../../assets/tutorials/creating-waterfalls/WW-3.png) 4. Create the less turbulent white water where the main outflow collides with the surrounding boulders. 1. Insert a **ParticleEmitter** into **MainDropWW**, then rename the emitter **GentleWW**. 2. Select **GentleWW**, then in the **Properties** window, 1. Set **Texture** to `rbxassetid://16808075391` to render particles that look like webby splashes. 2. Set **Color** to a color sequence that starts blue then turns white. - **Time** = `0`, **RGB Value** = `189, 246, 255` - **Time** = `1`, **RGB Value** = `255, 255, 255` 1. Set **Size** to a number sequence that steadily increases in size with a window of variation. - **Time** = `0`, **Value** = `1.13`, **Envelope** = `0.562` - **Time** = `1`, **Value** = `5.56`, **Envelope** = `0.563` 1. Set **Transparency** to a number sequence in which particles start as transparent, turn about halfway opaque, then have slight peaks to become transparent again across their lifetime. - **Time** = `0`, **Value** = `1`, **Envelope** = `0` - **Time** = `0.143`, **Value** = `0.462`, **Envelope** = `0.0625` - **Time** = `0.336`, **Value** = `0.462`, **Envelope** = `0.0562` - **Time** = `0.622`, **Value** = `0.788`, **Envelope** = `0.0538` - **Time** = `1`, **Value** = `1`, **Envelope** = `0` 1. Set **ZOffset** to `2` to offset the texture to be slightly away from the cliff. 2. Set **Lifetime** to `1.25, 1.5` to randomly set each particle's lifetime between 1250 and 1500 milliseconds. 3. Set **Rate** to `12` to emit 12 particles per second. 4. Set **Rotation** to `-45, 45` to randomly orient each particle between -45 and 45 degrees per second. 5. Set **RotSpeed** to `-40, 40` to randomly emit each particle between -45 and 40 degrees per second. 6. Set **Speed** to `15, 18` to randomly emit each particle between 20 and 35 studs per second. 7. Set **SpreadAngle** to `5, 5` to emit particles in a small angle along the X and Z axis. 8. Set **Acceleration** to `0, -35, 0` to simulate gravity and pull particles downward. 9. Set **Drag** to `0.25` to have particles lose their speed with exponential decay. 10. Set **LightEmission** to `0.6` to brighten the particles. 11. Set **LightInfluence** to `0.1` to significantly reduce how much the environmental light affects the color of particles. 5. Create the more turbulent white water where the main outflow collides with the surrounding boulders. 1. Duplicate **GentleWW**, then rename it **TurbulentWW**. 2. Select **TurbulentWW**, then in the **Properties** window, 1. Set **Size** to a number sequence that subtly increases in size with a window of variation. - **Time** = `0`, **Value** = `1.6`, **Envelope** = `0.562` - **Time** = `1`, **Value** = `2.63`, **Envelope** = `0.563` 1. Set **Transparency** to a number sequence in which particles start as transparent, turn opaque, then become transparent again near the middle of their lifetime. - **Time** = `0`, **Value** = `1`, **Envelope** = `0` - **Time** = `0.156`, **Value** = `0.0437`, **Envelope** = `0.0437` - **Time** = `0.55`, **Value** = `0.075`, **Envelope** = `0.0252` - **Time** = `1`, **Value** = `1`, **Envelope** = `0` 1. Set **Lifetime** to `0.25, 0.5` to randomly set each particle's lifetime between 250 and 500 milliseconds. 2. Set **Rate** to `20` to emit 20 particles per second. 3. Set **Speed** to `5, 6` to randomly emit each particle between 5 and 6 studs per second. 4. Set **Acceleration** to `0, -15, 0` to simulate gravity and pull particles downward. 6. In the **Explorer** window, select all block parts in the **WhiteWater** folder, then in the **Properties** window, set **Transparency** to `1` to make the blocks invisible. ## Configure foam Unlike splashes that propel upward as cascades impact the plunge pool, **foam** is aerated water that ripples outward from the base of the impact point. Similar to splashes, foam expands and breaks apart from itself into web-like droplets as it becomes aerosolized. To mimic this effect, the sample uses a `Class.ParticleEmitter` object to emit particles that look like foam ripples, and it emits them slowly parallel to the plunge pool. This allows the particle to emulate the visual and behavioral effect of a capillary wave breaking the surface tension of the underlying pool of water. ![The 2D texture that represents the aerosolized water that ripples away from where the cascades impacts the plunge pool.](../../../assets/tutorials/creating-waterfalls/Foam-Texture.png)_Foam Ripples Texture = rbxassetid://16811365086_ To recreate the foam at the base of the main drop in the sample [Waterfall Island](https://www.roblox.com/games/16454663889/Use-Case-Tutorials-Volcano-Island) place file: 1. In the **Explorer** window, create a **Folder** in the **Workspace** to contain all foam objects, then rename the folder **Foam**. 2. Insert a **block** part into **Foam**, then rename it **MainDropFoam**. 3. Position and scale **MainDropFoam** to the full surface area where the densest part of the main cascade impacts the plunge pool.![A angled side view of the bottom of the cliff with a block part positioned at the point where the cascades impact the plunge pool.](../../../assets/tutorials/creating-waterfalls/Foam-2B.png) 4. Insert a **ParticleEmitter** into **MainDropFoam**, then rename the emitter **FoamRipples**. 5. In the **Explorer** window, select **FoamRipples**, then in the **Properties** window, 1. Set **Texture** to `rbxassetid://16811365086` to render particles that look like foam ripples. 2. Set **Orientation** to **VelocityPerpendicular** to emit particles perpendicular to the direction of their movement. 3. Set **Transparency** to a number sequence in which particles start as transparent, rapidly become opaque, then quickly become transparent toward the end of their lifetime. - **Time** = `0`, **Value** = `1`, **Envelope** = `0` - **Time** = `0.143`, **Value** = `0.119`, **Envelope** = `0.1` - **Time** = `0.664`, **Value** = `0.125`, **Envelope** = `0.112` - **Time** = `1`, **Value** = `1`, **Envelope** = `0` 4. Set **Lifetime** to `0.5, 0.7` to randomly set each particle's lifetime between 500 and 700 milliseconds. 5. Set **Rate** to `5` to emit 5 particles per second. 6. Set **Rotation** to `0, 360` to randomly orient each particle in a circle from the emission point. 7. Set **RotSpeed** to `-15, 15` to randomly emit each particle between -15 and 15 degrees per second. 8. Set **Speed** to `0, .01` to randomly emit each particle between 0 and .01 studs per second. 9. Set **LightEmission** to `0.25` to subtly brighten the particles. 10. Set **LightInfluence** to `0` to prevent the environmental light from affecting the color of particles. 6. In the **Command Bar**, input the following string to increase each particle's size from 5 to 20 studs across their lifetime with a small window of variation:```lua workspace.Foam.MainDropFoam.FoamRipples.Size = NumberSequence.new{NumberSequenceKeypoint.new(0,5,0), NumberSequenceKeypoint.new(1,20,5)} ``` 7. In the **Explorer** window, select all block parts in the **Foam** folder, then in the **Properties** window, set **Transparency** to `1` to make the blocks invisible. ## Configure mist As cascades make impact with the plunge pool, some of the water evaporates and condenses in the cool, humid air to create **mist**. Mist vapors don't catch lighting in the same way that hard surfaces do; instead, they reflect light in often unexpected ways to appear bright within the overall environment until they completely evaporate. The sample emulates this process by using two `Class.ParticleEmitter` objects at the base of the drop. The first particle emitter energetically emits particles away from the impact point and cliff, and the second emits particles that slowly rise toward the sky. Both particle emitter's particles are bright at the start of their lifetime, and slowly become transparent toward the end of their lifetime as they evaporate. Similar to the real-world, the mist evaporation process allows a rainbow to form as light reflects off of the small water droplets, which the sample also emulates with a `Class.ParticleEmitter` object that emits rainbow particles with a short lifespan. These particles face the camera no matter the player's angle from the waterfall to produce an optical illusion of light catching on the vapor itself. ![The 2D texture that represents the mist.](../../../assets/tutorials/creating-waterfalls/Mist-Texture.png)_Mist Texture = rbxassetid://16830667309_ ![The 2D texture that represents the rainbow.](../../../assets/tutorials/creating-waterfalls/Rainbow-Texture.png)_Rainbow Texture = rbxassetid://16828911033_ To recreate the mist that moves outward and upward from the base of the main drop in the sample [Waterfall Island](https://www.roblox.com/games/16454663889/Use-Case-Tutorials-Volcano-Island) place file: 1. In the **Explorer** window, create a **Folder** in the **Workspace** to contain all mist related objects, then rename the folder **Mist**. 2. Create the mist that radiates away from the cascade's impact on the plunge pool. 1. Insert a **block** part into **Mist**, then rename it **BaseMist**. 2. Position, orient, and scale **BaseMist** to face away from the densest part of the location where the main cascade impacts the plunge pool.![A angled side view of the bottom of the cliff with a block part positioned at the point where the cascades impact the plunge pool. The block part is slightly angled so the top face faces away from the cliff.](../../../assets/tutorials/creating-waterfalls/Mist-2B.png) 3. Insert a **ParticleEmitter** into **BaseMist**, then rename the emitter **Mist**. 4. Select **Mist**, then in the **Properties** window, 1. Set **Texture** to `rbxassetid://16830667309` to render particles that look like thick mist. 2. Set **Color** to a color sequence that starts blue then turns white. - **Time** = `0`, **RGB Value** = `171, 244, 255` - **Time** = `0.339`, **RGB Value** = `251, 254, 255` - **Time** = `1`, **RGB Value** = `255, 255, 255` 1. Set **Size** to a number sequence that steadily increases in size. - **Time** = `0`, **Value** = `8`, **Envelope** = `0` - **Time** = `1`, **Value** = `10`, **Envelope** = `0` 1. Set **Transparency** to a number sequence in which particles start as transparent, quickly turn slightly opaque, then rapidly become transparent again across their lifetime. - **Time** = `0`, **Value** = `1`, **Envelope** = `0` - **Time** = `0.0971`, **Value** = `0.8`, **Envelope** = `0.0625` - **Time** = `1`, **Value** = `1`, **Envelope** = `0` 1. Set **ZOffset** to `2` to offset the texture to be slightly away from the plunge pool. 2. Set **Lifetime** to `0.5, 1` to randomly set each particle's lifetime between 500 and 100 milliseconds. 3. Set **Rate** to `20` to emit 20 particles per second. 4. Set **Rotation** to `-360, 360` to randomly orient each particle in a circle. 5. Set **RotSpeed** to `-50, 50` to randomly emit each particle between -50 and 50 degrees per second. 6. Set **Speed** to `35, 50` to randomly emit each particle between 35 and 50 studs per second. 7. Set **SpreadAngle** to `25, 25` to emit particles in a small angle along the X and Z axis. 8. Set **Acceleration** to `-10, -25, -10` to simulate the impact of the splash exploding outward from the plunge pool. 9. Set **Drag** to `1.5` to have particles lose their speed with exponential decay. 3. Create the mist that rises upward from the plunge pool. 1. Duplicate **BaseMist**, then rename it **RiseMist**. 2. Orient **RiseMist** so that the top of the part that emits particles faces toward the sky. 3. Select its child particle emitter, then in the **Properties** window, 1. Set **Lifetime** to `4` to set each particle's lifetime to 4 seconds. 2. Set **Rate** to `3` to emit 3 particles per second. 3. Set **RotSpeed** to `-10, 10` to randomly emit each particle between -10 and 10 degrees per second. 4. Set **Speed** to `25` to emit each particle 25 studs per second. 5. Set **Acceleration** to `0, 0, 0` to remove the previous simulation. 6. Set **Drag** to `1` to have particles lose their speed with exponential decay. 4. In the **Command Bar**, input the following string to increase each particle's size from 20 to 20 studs across their lifetime with a small window of variation:```lua workspace.Mist.RiseMist.Mist.Size = NumberSequence.new{NumberSequenceKeypoint.new(0,20,1), NumberSequenceKeypoint.new(1,25,5)} ``` 4. Create the rainbow. 1. Insert a **block** part into **Mist**, then rename it **RainbowPart**. 2. Position **RainbowPart** slightly above the other block parts. 3. Insert a **ParticleEmitter** into **Rainbow**, then rename the emitter **Rainbow**. 4. Select **Rainbow**, then in the **Properties** window, 1. Set **Texture** to `rbxassetid://16828911033` to render particles that look like a faint rainbow. 2. Set **Size** to `25` to render large particles. 3. Set **Transparency** to a number sequence in which particles start as transparent, turn slightly opaque, then become transparent again across their lifetime. - Time = `0`, Value = `1`, Envelope = `0` - Time = `0.497`, Value = `0.363`, Envelope = `0.05` - Time = `1`, Value = `1`, Envelope = `0` 1. Set **Lifetime** to `2, 4` to randomly set each particle's lifetime between 200 and 400 milliseconds. 2. Set **Rate** to `0.25` to emit a particle every 4 seconds. 3. Set **Rotation** to `-20` to orient each particle to a slight degree. 4. Set **Speed** to `0` to emit each particle 0 studs per second. 5. Set **Drag** to `1` to have particles lose their speed with exponential decay. 6. Set **LightEmission** to `1` to use additive blending to render the particles with the colors behind them. This step also removes the black background from the texture itself. 7. Set **LightInfluence** to `0` to prevent the environmental light from affecting the color of particles. 5. In the **Explorer** window, select all block parts in the **Mist** folder, then in the **Properties** window, set **Transparency** to `1` to make the blocks invisible. --- title: "Custom particle effects" url: /docs/en-us/tutorials/use-case-tutorials/vfx/custom-particle-effects last_updated: 2026-06-29T19:34:15Z description: "The process for creating a multicolor plume of smoke from an active volcano." --- # Custom particle effects Time to look at a more complex example of particle effects. You'll make a multicolored plume of smoke pouring out of an active volcano. ## Starter project First up, you'll need something like a volcano you can use for the effect. The [Treasure Island example world](https://www.roblox.com/games/6307505882/Treasure-Island) pictured in this tutorial includes a volcano. ![Edit in Studio option from the experience's main page](../../../assets/tutorials/custom-particle-effects/Treasure-Island-Edit-Place.png) A volcano with an orange neon part will be used to emit particles. This part is called **CenterLava** and is parented to the workspace. 1. Locate and select the **CenterLava** part in the workspace. 2. Insert a **ParticleEmitter** onto the part. ## Particle texture A `Class.ParticleEmitter` has a property called **Texture** that determines the image that will be repeated in the effect. If you want to use your own image, you'll need to upload it to Roblox and get the **Asset ID** to paste into that property. See [Textures and Decals](/docs/en-us/parts/textures-decals.md) for more details. > **Info:** If you want to make your own texture using image editing software, consider these best practices: - Make your image **grayscale**. This allows you complete control over the final color of the particle with the Color property. - Ensure the **background** is **transparent**. - **Blur** the **edges** of your image to get a more polished, continuous effect. For a smoke effect, a circle with faded edges works well. A pre-made example of this is provided below. Change the emitter's **Texture** property to `rbxassetid://3845808160`. ## Fading effect Despite the change of texture, the volcano effect still doesn't look much like smoke. It would look better if the particles faded out over time in the same way that smoke dissipates in the air. Some emitter properties can be set up to change over time with a **sequence**. Start with **Transparency** which can be used to create a fading effect. 1. Open the sequence window for the emitter's **Transparency** by clicking the three dots next to the property. The sequence window is a graph, with the **time** on the X-axis and the **property value** on the Y-axis. By default, the transparency value is a flat line, with starting and ending **keypoints** at 0, meaning the particles will be opaque for their entire lifetime. 2. Drag the keypoint at the end of the sequence to the top of the graph to create a smooth fade from 0 to 1. Your particles should now slowly fade away as they rise from the volcano. ## Growing smoke To really look like smoke, the particles should be large enough to overlap with each other and should spread as they rise from the source. This can be achieved by applying a sequence to the **Size** property. By making the size start at **3** and increase to **10**, the particles will look much more like a cloud of smoke. 1. Open the sequence window for the emitter's **Size** property. 2. Drag the starting keypoint to approximately **3**. 3. Drag the ending keypoint to approximately **10**. ## Color sequences Sequences aren't just for linear property values - you can even use them to change the color of particles over time. The window for **color sequences** is slightly different: it shows the color as it changes across the particle's lifetime. By default, the sequence is all white. If you start your sequence at orange to reflect the lava, then move to darker grey, and then finish at white, you can get a really immersive effect for your smoke. 1. Open the sequence window for the emitter's **Color** property. 2. Click the small square next to **Color** to open a color picker for the keypoint at the start of the sequence. Set the color to **orange**. 3. Click in the middle of the region to create a new keypoint. Set the color at this keypoint to **dark grey**. 4. Set the color at the end of the sequence to **white.** ## Acceleration For a final touch, you can make the smoke drift away from the volcano as if the wind were blowing it away. The **Acceleration** property determines how the speed of particles changes over time, measured on the **X**, **Y**, and **Z** axes. Set the **Acceleration** property of the emitter to **2, 2, 0** to achieve a drifting effect. > **Info:****Acceleration** is the force applied to particles **after** they have been emitted, while **Speed** is the **initial** emission speed. > > Understanding how these properties can work together is the key to achieving many effects — for instance, you could simulate water drops falling from a surface to the ground by setting **Speed** to **0** and **Y** acceleration to a negative value. ## Thicker smoke If the acceleration change has spread the smoke particles out too much, you can increase the **Rate** property to thicken it back up again. A rate of **40** works nicely here. Set the emitter's **Rate** property to **40.** Your volcano smoke is now complete! With just a few simple changes, your emitter is producing a realistic, immersive smoke effect. For a complete list of emitter properties you can use to tweak your effects, see the `Class.ParticleEmitter` API page. If you're working with the example world, look for other spots that could benefit from particle effects. --- title: "Create laser beams with VFX" url: /docs/en-us/tutorials/use-case-tutorials/vfx/laser-traps-with-beams last_updated: 2026-06-29T19:34:15Z description: "A tutorial on creating a laser beam that sets an avatar's health to 0 on impact." --- # Create laser beams with VFX A **laser beam** is a ray of light. While rarely dangerous in real life, sci-fi experiences often use laser beams as a mechanism to damage players on impact. However, because of their versatility and prominence in futuristic media, laser beams are useful for all sorts of gameplay mechanics, such as blaster weapons, puzzles, obstacles, and environment aesthetics. Using a sample [Laser Beam Blaster](https://create.roblox.com/store/asset/16382650186) model, this tutorial shows you how to create a laser beam special effect with an optional script to set the player's health to zero on collision, including guidance on: - Setting up an invisible collision box that detects when a player collides with the laser beam. - Configuring attachments that represent the reach of the laser beam's emission. - Customizing a beam that emulates the visual characteristics of a futuristic laser beam. - Scripting behavior for the collision box that damages the player's character. > **Info:** You can create your own assets in third-party modeling tools and follow along with your own design. For information on exporting models for use in Studio, see [Exporting Requirements](/docs/en-us/art/modeling/export-requirements.md). ## Get blaster asset The **Creator Store** is a tab of the **Toolbox** that you can use to find all assets that are made by Roblox and the Roblox community for use within your projects, including model, image, mesh, audio, plugin, video, and font assets. You can use the Creator Store to add an individual asset or asset library directly into an open experience. This tutorial references a high-quality laser beam blaster model that you can use as you replicate each step within the following sections. To get this blaster asset from your inventory into your experience: 1. Add the blaster to your inventory. 1. Navigate to the asset's [details page](https://create.roblox.com/store/asset/16382650186) on the Creator Store. 2. In the top-right corner, click the **Get Model** button. The blaster asset is now in your inventory, and you can reuse it in any project on the platform. 2. In Studio, navigate to the **Home** tab, then click the **Toolbox** button. The **Toolbox** window opens.![Toolbox highlighted in Studio's toolbar.](../../../assets/studio/general/Toolbar-Toolbox.png) 3. In the **Toolbox** window, click the **Inventory** tab. The **My Models** sort displays. 4. Click the **Laser Beam Blaster** tile. The model displays in your viewport. ## Set up a collision box The complete laser beam that sets players health to zero on impact needs to be able to detect when players collide with the laser. Because `Class.Beam` objects don't have default collision detection capabilities, you must set up collision detection with basic parts. For example, this tutorial uses an invisible block part with a `Class.Beam` object as a collision box that detects when a character's `Class.Humanoid` object touches the laser beam. In the final section of the tutorial, you will create a script that uses this information to trigger damage to the player's health. To set up the collision box: 1. Insert a **block** part into **LaserBeamBlaster**. 2. Select the part, then in the **Properties** window, 1. Set **Name** to **CollisionBox**. The part's name and its case style are important for the script later in the tutorial. 2. Enable **Anchored** to ensure that the physics system doesn't move the part when the experience starts. 3. Scale **CollisionBox** to the length you want your laser beam to blast from the blaster. For example, this tutorial scales the it to be the same length as the blaster. 4. Move **CollisionBox** to a position where it extends from the blaster's emitter bulb. Your collision box now represents the reach of the laser beam's blast from the blaster. ## Configure attachments Before you add a `Class.Beam` object to your blaster, it's important to configure two `Class.Attachment` objects in the 3D space to represent the reach of the laser's emission from the blaster's emitter bulb. Beams operate by rendering a texture between attachments, so if you don't have attachments for the beam to reference, it cannot function at all. To configure attachments for the laser beam: 1. To make attachments visible in the viewport, enable **Show Constraint Details** from Studio's **View** menu. 2. Insert two attachments into the collision box. 1. In the **Explorer** window, hover over **CollisionBox**, then click the ⊕ icon. A contextual menu displays. 2. From the contextual menu, insert an **Attachment**. 3. Repeat this process so that **CollisionBox** has two attachment objects. 4. Rename both attachments **StartAttachment** and **EndAttachment**, respectively. 3. Move **StartAttachment** to the edge of **CollisionBox** that overlaps with the emitter bulb, then move **EndAttachment** to the edge of **CollisionBox** that represents the reach of the laser beam. 4. Make the **CollisionBox** transparent so that you will be able to see the texture the beam renders between the attachments without obstruction. 1. In the **Explorer** window, select **CollisionBox**. 2. In the **Properties** window, set **Transparency** to `1` to make the part completely transparent. ## Customize the beam Now that you have `Class.Attachment` objects in the 3D space, you can add and customize a `Class.Beam` object to emulate the visual characteristics of a laser beam. This tutorial provides guidance on how to create a futuristic, bright pink beam that animates quickly, but by experimenting with the same properties, you can create a variety of different special effects. To customize the beam: 1. Insert a beam into **CollisionBox**. 1. In the **Explorer** window, hover over **CollisionBox**, then click the ⊕ icon. A contextual menu displays. 2. From the contextual menu, insert a **Beam**. 2. Assign the collision box's attachments to the new `Class.Beam` object. 1. In the **Explorer** window, select the beam. 2. In the **Properties** window, 1. Set **Attachment0** to **StartAttachment**. 2. Set **Attachment1** to **EndAttachment**. The beam renders its default texture between the two attachments. 3. Customize the beam's visual appearance so it looks like a futuristic laser beam. 1. In the **Explorer** window, verify the beam is still selected. 2. In the **Properties** window, 1. Set **Texture** to `rbxassetid://6060542021` to render a new texture that looks like a laser beam. 2. Set **Color** to `255, 47, 137` to tint the laser bright pink. 3. Set **LightEmission** to `0.5` to add a faint glow to the laser. 4. Set **Width0** and **Width1** to `4` to widen the laser. 5. Set **TextureSpeed** to `2` to make the laser animate more quickly. 6. Enable **FaceCamera** to ensure the laser is visible no matter the angle of the player from the laser. > **Info:** For more information on all beam properties you can customize, see [Beams](/docs/en-us/effects/beams.md). ## Script damage behavior Your laser beam is currently aesthetically pleasing for its environment, but it's also completely harmless as a blaster weapon. To modify the laser blaster so that it can deal damage to players, you must add in a script to the collision box that triggers this behavior. The sample script works by waiting to see which objects touch the collision box. If an object that touches the collision box includes a child `Class.Humanoid` object, the script sets its `Class.Humanoid.Health|Health property` to `0`. By default, every player character includes a `Class.Humanoid` object, so every time a player collides with the collision box, the script instantaneously sets their health to zero, and the character falls apart. To script the behavior that damages the player: 1. Insert a script into **LaserBeamBlaster**. 1. In the **Explorer** window, hover over **LaserBeamBlaster**, then click the ⊕ icon. A contextual menu displays. 2. From the contextual menu, insert a **Script**. 2. Replace the default code with the following code:```lua local laserTrap = script.Parent local collisionBox = laserTrap.CollisionBox local function onTouch(otherPart) local character = otherPart.Parent local humanoid = character:FindFirstChildWhichIsA("Humanoid") if humanoid then humanoid.Health = 0 end end collisionBox.Touched:Connect(onTouch) ``` 3. Test the behavior by walking into the laser beam. 1. Choose **Test** from the dropdown menu and click the **Play** button to its right to begin the playtest.![Test option in the testing modes dropdown of Studio's mezzanine.](../../../assets/studio/general/Mezzanine-Testing-Mode-Test.png) 2. Walk into the laser beam to see your character fall apart. When you're done, click the **Stop** button. Studio exits playtest mode.![Stop button indicated in Studio's mezzanine.](../../../assets/studio/general/Mezzanine-Testing-Stop.png) > **Warning:** If the behavior isn't working correctly, make sure the script is a child of **LaserBeamBlaster**, and that the collision box is named `CollisionBox`. You now have a dangerous laser beam blaster! Using the skills in this tutorial, you can customize all sorts of beam special effects, such as glowing force fields, rapid waterfalls, and speed ramps. For example, you can experiment with additional beam properties like `CurveSize0` and `CurveSize1`, [import](/docs/en-us/production/creator-store.md) your own textures, and pair beams with other special effects, like [Particle Emitters](/docs/en-us/effects/particle-emitters.md) and [Light Sources](/docs/en-us/effects/light-sources.md). Happy creating! --- title: "Create explosions with VFX" url: /docs/en-us/tutorials/use-case-tutorials/vfx/use-particles-for-explosions last_updated: 2026-06-29T19:34:15Z description: "The process for creating a trap that emits a burst of particles when it kills a player." --- # Create explosions with VFX Previously, you worked with particles that played continuously, like [smoke from a volcano](/docs/en-us/tutorials/use-case-tutorials/vfx/custom-particle-effects.md). Particles can also be used in a single burst, such as explosions. This tutorial will show you how to create a trap that emits a burst of particles and kills a player. ## Emitter setup The explosion will use a ParticleEmitter with some changed properties that will create a burst. 1. Design a dangerous looking trap. Then, insert a **ParticleEmitter** named **Explosion** into the part. 2. Create an electric spark effect using these properties. | Property | Value | Description | | --- | --- | --- | | **Texture** | rbxassetid://6101261905 | Electric spark texture. | | **Drag** | 10 | How fast particles lose speed. | | **Lifetime** | 0.2, 0.6 | Makes explosion particles exist for a short time. | | **Speed** | 20, 40 | Compensates for the short lifetime. | | **SpreadAngle** | 180, 180 | Fires particles in all directions. | 3. So the trap doesn't emit particles constantly toggle **Enabled** to **off**.![alt](../../../assets/tutorials/using-particles-for-explosions/burstParticles_toggleEnabled.png) ## Test particle bursts To test the particle burst, you can use a Studio plugin developed by Roblox. 1. Go to the Marketplace page for the [Emit() Plugin](https://www.roblox.com/library/303835976/ParticleEmitter-Emit-n) Plugin. On that page, click the **Install** button.![alt](../../../assets/tutorials/using-particles-for-explosions/burstParticles_pluginInstall.png) 2. When Studio opens, the plugin should install automatically.![alt](../../../assets/tutorials/using-particles-for-explosions/burstParticles_confirmPluginInstall.png) 3. Select the **Explosion** emitter and notice the plugin UI that appears in the top left of the 3D viewport. In the number box, type **100** (the amount of particles to emit) and press `Enter`.![alt](../../../assets/tutorials/using-particles-for-explosions/burstParticles_enterEmitRate.png) 4. Press the **Emit** button to test the emitter. ## Color and transparency Some extra steps can make the explosion look more impressive. 1. Open the sequence window for the emitter's **Color** by clicking the three dots next to the property. Then, create keypoints in the window to make a color gradient.![alt](../../../assets/tutorials/using-particles-for-explosions/burstParticles_colorSequence.png) 2. For **Transparency**, use a **number sequence** that increases transparency over a smooth curve to show a gradual fade out.![alt](../../../assets/tutorials/using-particles-for-explosions/burstParticles_transparencyGradient.png) A finished particle effect may look like below.![alt](../../../assets/tutorials/using-particles-for-explosions/burstParticles_transparencyGradientExample.jpg) ## Script setup With the emitter complete, the explosion can now be played through a script. The script works by checking for players touching the trap. Whenever it detects someone, the particles will emit and the player will die. 1. In the trap part, add a new **Script** named **PlayExplosion**.![alt](../../../assets/tutorials/using-particles-for-explosions/burstParticles_explorerCreateScript.png) 2. Set up variables to store the part and emitter. Then, include a variable named `EMIT_AMOUNT` that stores the number of particles emitted per explosion.```lua local trapObject = script.Parent local particleEmitter = trapObject.Explosion local EMIT_AMOUNT = 100 ``` 3. Code an event to check if a `Class.Humanoid` touches the part. If so, set that humanoid's health to 0, forcing them to respawn.```lua local trapObject = script.Parent local particleEmitter = trapObject.Explosion local EMIT_AMOUNT = 100 local function killPlayer(otherPart) local character = otherPart.Parent local humanoid = character:FindFirstChildWhichIsA("Humanoid") if humanoid then humanoid.Health = 0 end end trapObject.Touched:Connect(killPlayer) ``` ## Play the explosion In scripts, particles are emitted using the `Class.ParticleEmitter:Emit()|Emit()` function. This creates a one-time burst of a number of particles. 1. Call the `Emit()` function and pass in `EMIT_AMOUNT`, the variable created earlier.```lua local trapObject = script.Parent local particleEmitter = trapObject.Explosion local EMIT_AMOUNT = 100 local function killPlayer(otherPart) local character = otherPart.Parent local humanoid = character:FindFirstChildWhichIsA("Humanoid") if humanoid then humanoid.Health = 0 particleEmitter:Emit(EMIT_AMOUNT) end end trapObject.Touched:Connect(killPlayer) ``` 2. Test the script by walking into the trap. With just a few changes to the example in this tutorial, you can create a variety of different effects. Some alternatives include sparkles for gathering collectable objects, or explosions to indicate a projectile's impact. --- title: "2D paths" url: /docs/en-us/ui/2D-paths last_updated: 2026-06-29T19:34:15Z description: "The Path2D instance, along with its API methods and properties, lets you implement 2D splines and 2D curved lines." --- # 2D paths The `Class.Path2D` instance, along with its API methods and properties, lets you implement 2D splines and 2D curved lines, useful for UI effects like path‑based animations and graph editors. ## Create a 2D path To add a `Class.Path2D` to the screen or an in-experience object: 1. In the [Explorer](/docs/en-us/studio/explorer.md) window, insert a `Class.Path2D` instance under a visible `Class.ScreenGui` or `Class.SurfaceGui` (it does not need to be a direct child).![Path2D instance parented to a ScreenGui in the Explorer hierarchy.](../assets/studio/explorer/StarterGui-ScreenGui-Path2D.png) 2. Select the new `Class.Path2D` to reveal the in-viewport tooling widget. By default, the **Add Point** tool is selected.![Add Point tool indicated in the 2D path editor widget.](../assets/ui/2D-paths/Widget-Add-Point.png) 3. Begin clicking on the screen to add a series of **control points** to form a path. The initial path will likely be imprecise but you can [fine‑tune](#move-points) the position of any control point later.![Diagram illustrating an example path created using the Add Point tool to connect a series of points.](../assets/ui/2D-paths/Path-Example-Create-Basic.png) > **Info:** If you drag the mouse after clicking, [tangents](#control-point-tangents) will be created on that point. Tangents can also be procedurally [added](#add-tangents) to any control point. 4. When finished, click the widget's **Done** button or press `Enter`. ## Modify control points With a `Class.Path2D` selected in the [Explorer](/docs/en-us/studio/explorer.md) hierarchy, you can modify its individual control points as well as their [tangents](#control-point-tangents). ### Move points To move an individual control point on a path, enable the **Select** tool (`V`) and then click‑and‑drag it to a new position. ![Select tool indicated in the 2D path editor widget.](../assets/ui/2D-paths/Widget-Select.png)![Diagram illustrating how a path control point is moved to a new position by clicking and dragging with the Select tool enabled.](../assets/ui/2D-paths/Path-Example-Move-Point.png) For very specific positioning, select the control point and then, in the [Properties](/docs/en-us/studio/properties.md) window, set a new position for the point's `Class.Path2D.SelectedControlPointData|SelectedControlPointData` property (`Datatype.UDim2`). ![SelectedControlPointData property of a Path2D instance indicated in the Properties window.](../assets/studio/properties/Path2D-SelectedControlPointData.png) Note that a point's position is not absolute, but rather **relative** to the path's parent container. For example, compare a control point 30% from left and 20% from top for a path inside a `Class.ScreenGui`, versus an identical path placed inside a `Class.Frame` located in the upper‑right corner of the `Class.ScreenGui`. ![Diagram illustrating how the positions of path control points are relative to their parent container.](../assets/ui/2D-paths/Control-Point-Relativity.png) ### Add points New control points can be added to a `Class.Path2D` either between two existing points or from either end point using the **Add Point** tool (`P`). ![Add Point tool indicated in the 2D path editor widget.](../assets/ui/2D-paths/Widget-Add-Point.png) ![Diagram illustrating how a control point is added between two existing points using the Add Point tool.](../assets/ui/2D-paths/Path-Example-Add-Point-Between.png)_Hover anywhere over the path and click to add a control point between two existing points_ ![Diagram illustrating how a control point is added to the end of a path using the Add Point tool.](../assets/ui/2D-paths/Path-Example-Add-Point-From-End.png)_Click on an end point and then click again to add a control point from that end_ > **Info:** Remember that if you drag the mouse after clicking to add a new control point, [tangents](#control-point-tangents) will be created on that point. ### Delete points To delete a control point, hover over and right‑click it, then select **Delete Point** from the contextual popup menu. ![Diagram illustrating how a path control point is deleted by right-clicking it and selecting Delete Point.](../assets/ui/2D-paths/Path-Example-Delete-Point.png) ### Control point tangents Control point **tangents** let you create and adjust curves on a path. ![Diagram illustrating a curved path with several tangent control points.](../assets/ui/2D-paths/Tangents.png) #### Add tangents To add tangents to any control point that doesn't already have tangents: 1. Enable the **Add Tangent** tool in the tooling widget.![Add Tangent tool indicated in the 2D path editor widget.](../assets/ui/2D-paths/Widget-Add-Tangent.png) 2. Hover over the desired control point, then click to add two [mirrored](#break-and-mirror) tangents (optionally drag after clicking to [adjust](#adjust-tangents) the new tangents).![Diagram illustrating how a path control point with tangents is created with the Add Tangent tool.](../assets/ui/2D-paths/Path-Example-Add-Tangent.png) #### Adjust tangents To adjust existing tangents for an individual control point: 1. Enable the **Select** tool (`V`).![Select tool indicated in the 2D path editor widget.](../assets/ui/2D-paths/Widget-Select.png) 2. Hover over a tangent marker (not the control point), then click‑and‑drag it to a new position. If the tangents are mirrored, the paired tangent point will move in unison.![Diagram illustrating how the tangents of a control point are adjusted with the Select tool enabled.](../assets/ui/2D-paths/Path-Example-Adjust-Tangent.png) To manually set a specific `Datatype.UDim2` position for a tangent: 1. Enable the **Select** tool (`V`) and select the control point. In the [Properties](/docs/en-us/studio/properties.md) window, expand the `Class.Path2D.SelectedControlPointData|SelectedControlPointData` field to expose the `Datatype.Path2DControlPoint.LeftTangent|LeftTangent` and `Datatype.Path2DControlPoint.RightTangent|RightTangent` properties.![LeftTangent and RightTangent properties of a Path2D control point indicated in the Properties window.](../assets/studio/properties/Path2D-SelectedControlPointData-Expanded.png) 2. Set the position for `Datatype.Path2DControlPoint.LeftTangent|LeftTangent` and/or `Datatype.Path2DControlPoint.RightTangent|RightTangent`. Note that this will [break the mirrored behavior](#break-and-mirror) of the tangents. #### Delete tangents To delete both tangents from a control point, hover over and right-click that point, then select **Clear Tangents** from the contextual popup menu. ![Diagram illustrating how both tangents of a control point are cleared by right-clicking it and selecting Clear Tangents.](../assets/ui/2D-paths/Path-Example-Clear-Tangents.png) To delete just one of the tangents (left or right), hover over and right‑click that tangent's marker, then select **Delete Tangent**. ![Diagram illustrating how one tangent of a control point is deleted by right-clicking it and selecting Delete Tangent.](../assets/ui/2D-paths/Path-Example-Delete-Tangent.png) #### Break and mirror By default, tangents mirror each other. When you [drag to adjust](#adjust-tangents) one tangent marker, the paired tangent point will move in unison. ![Diagram illustrating how the tangents of a control point are adjusted with the Select tool enabled.](../assets/ui/2D-paths/Path-Example-Adjust-Tangent.png) To "break" the tangents so that each can be moved independently of the other, hover over and right‑click the associated control point, then select **Break Tangents** from the contextual menu. Once broken, you can move each tangent marker without affecting the other. ![Diagram illustrating how the tangents of a control point are broken by right-clicking it and selecting Break Tangents.](../assets/ui/2D-paths/Path-Example-Break-Tangents.png) > **Info:** To re-establish mirror behavior, hover over and right‑click the associated control point, then select **Mirror Tangents** from the contextual menu. Note that you will **not** see an immediate change in the path after re‑establishing mirror behavior, as the system cannot predict which tangent point you want to begin mirroring. Mirror behavior will resume only once you [drag to adjust](#adjust-tangents) one of the tangent markers. ## Path visual properties You can customize the visual appearance of a `Class.Path2D` with the following properties: | Property | Description | | --- | --- | | `Class.Path2D.Color3\|Color3` | Sets the color of the path line. | | `Class.Path2D.Thickness\|Thickness` | Sets the thickness of the path line, in pixels. | | `Class.Path2D.Visible\|Visible` | Make the path visible or not in both edit and runtime. | | `Class.Path2D.ZIndex\|ZIndex` | Order in which a path renders relative to other GUIs. | ![Diagram illustrating a path with Thickness of 2 and Color3 of (125, 125, 125).](../assets/ui/2D-paths/Path-Example-Thin-Grey.png)_Thickness = 2Color3 = (125, 125, 125)_ ![Diagram illustrating a path with Thickness of 10 and Color3 of (225, 0, 50).](../assets/ui/2D-paths/Path-Example-Thick-Red.png)_Thickness = 10Color3 = (225, 0, 50)_ ![Diagram illustrating a path layered in front of another using the ZIndex property.](../assets/ui/2D-paths/Path-Example-ZIndex-Layering.png)_ZIndex layering_ ## Path scripting Scripting is useful for several path-related workflows. The following examples use methods such as `Class.Path2D:GetControlPoints()|GetControlPoints()` which returns a table of `Datatype.Path2DControlPoint|Path2DControlPoints` and `Class.Path2D:GetPositionOnCurveArcLength()|GetPositionOnCurveArcLength()` which returns the `Datatype.UDim2` position at a given `t` value along the spline. ```lua local parent = script.Parent local path = parent:FindFirstChildWhichIsA("Path2D") local function arrangeChildren() local segmentCount = #path:GetControlPoints() local objectsToArrange = {} for _, child in parent:GetChildren() do if child:IsA("GuiObject") then table.insert(objectsToArrange, child) end end for idx, child in objectsToArrange do local t = idx / (#objectsToArrange + 1) child.Position = path:GetPositionOnCurveArcLength(t) end end -- Initially arrange child UI objects across path arrangeChildren() -- Listen for children being added/removed to adjust arrangement parent.ChildAdded:Connect(arrangeChildren) parent.ChildRemoved:Connect(arrangeChildren) ``` ```lua local Tweenservice = game:GetService("TweenService") local parent = script.Parent local path = parent:FindFirstChildWhichIsA("Path2D") local objectToAnimate = parent:FindFirstChildWhichIsA("GuiObject") local TWEEN_DURATION = 4 local TWEEN_EASING_STYLE = Enum.EasingStyle.Cubic local TWEEN_EASING_DIRECTION = Enum.EasingDirection.InOut local pathSampleValue = Instance.new("NumberValue") local tweenInfo = TweenInfo.new(TWEEN_DURATION, TWEEN_EASING_STYLE, TWEEN_EASING_DIRECTION, 0, true, 2) local tween = Tweenservice:Create(pathSampleValue, tweenInfo, {Value = 1}) local function onSampleValueChanged() objectToAnimate.Position = path:GetPositionOnCurveArcLength(pathSampleValue.Value) end pathSampleValue.Changed:Connect(onSampleValueChanged) tween:Play() ``` --- title: "3D drag detectors" url: /docs/en-us/ui/3D-drag-detectors last_updated: 2026-06-29T19:34:15Z description: "3D drag detectors facilitate and encourage interaction with 3D objects in an experience, such as opening doors and drawers, sliding a part around, and much more." --- # 3D drag detectors The `Class.DragDetector` instance facilitates and encourages interaction with 3D objects in an experience, such as opening doors and drawers, sliding a part around, grabbing and tossing a bowling ball, pulling back and firing a slingshot, and much more. Key features include: - Place a `Class.DragDetector` under any `Class.BasePart` or `Class.Model` to [make it draggable](#make-objects-draggable) via all inputs (mouse, touch, gamepad, and VR), all without a single line of code. - Choose from several [drag styles](#drag-style), define how the object [responds to motion](#response-to-motion), and optionally apply [axis or movement limits](#axis--movement-limits). - Scripts can [respond to manipulation of dragged objects](#script-responses-to-clicking-and-dragging) to drive UI or make logical decisions, such as adjusting the light level in a room based on a sliding wall switch dimmer. - Players can manipulate anchored parts or models and they'll stay exactly where you put them upon release. - `Class.DragDetector|DragDetectors` work in Studio as long as you're **not** using the **Select**, **Move**, **Scale**, or **Rotate** tools, making it easier to test and adjust draggable objects while editing. ## Make objects draggable To make any part or model draggable, simply add a `Class.DragDetector` as a direct descendant. 1. In the [Explorer](/docs/en-us/studio/explorer.md) window, hover over the `Class.Part`, `Class.MeshPart`, or `Class.Model` and click the ⊕ button. A contextual menu displays. 2. From the menu, insert a **DragDetector**. 3. By default, the object will now be draggable in the ground plane, but you can customize its `Class.DragDetector.DragStyle|DragStyle`, define how it [responds to motion](#response-to-motion), and optionally apply [axis or movement limits](#axis--movement-limits). > **Warning:** Remember that `Class.DragDetector|DragDetectors` only work in Studio if you're **not** using the **Select**, **Move**, **Scale**, or **Rotate** tools. ## Customize drag detectors ### Drag style `Class.DragDetector|DragDetectors` map cursor motion to virtual lines and planes to calculate proposed 3D motion. Through the `Class.DragDetector.DragStyle|DragStyle` property, you can choose from different mappings to suit your needs. For example, **TranslatePlane** produces translation in a virtual plane, whereas **RotateAxis** produces rotation about a virtual axis. | Setting | Description | | --- | --- | | `Enum.DragDetectorDragStyle.TranslateLine\|TranslateLine` | 1D motion along the detector's `Class.DragDetector.Axis\|Axis`, by default the world **Y** axis. | | `Enum.DragDetectorDragStyle.TranslatePlane\|TranslatePlane` | 2D motion in the plane perpendicular to the detector's `Class.DragDetector.Axis\|Axis`, by default the world **XZ** plane. | | `Enum.DragDetectorDragStyle.TranslatePlaneOrLine\|TranslatePlaneOrLine` | 2D motion in the plane perpendicular to the detector's `Class.DragDetector.Axis\|Axis` and, when the [modifier](#modifier-input) is active, 1D motion along the detector's `Class.DragDetector.Axis\|Axis`. | | `Enum.DragDetectorDragStyle.TranslateLineOrPlane\|TranslateLineOrPlane` | 1D motion along the detector's `Class.DragDetector.Axis\|Axis` and, when the [modifier](#modifier-input) is active, 2D motion in the plane perpendicular to the detector's `Class.DragDetector.Axis\|Axis`. | | `Enum.DragDetectorDragStyle.TranslateViewPlane\|TranslateViewPlane` | 2D motion in the plane perpendicular to the camera's view. In this mode, the plane is constantly updated, even while dragging, and will always face the camera's current view. | | `Enum.DragDetectorDragStyle.RotateAxis\|RotateAxis` | Rotation about the detector's `Class.DragDetector.Axis\|Axis`, by default the world **Y** axis. | | `Enum.DragDetectorDragStyle.RotateTrackball\|RotateTrackball` | Trackball rotation, further customized through the `Class.DragDetector.TrackballRadialPullFactor\|TrackballRadialPullFactor` and `Class.DragDetector.TrackballRollFactor\|TrackballRollFactor` properties. | | `Enum.DragDetectorDragStyle.BestForDevice\|BestForDevice` | **TranslatePlaneOrLine** for mouse and gamepad; **TranslatePlane** for touch; **6DOF** for VR. | | `Enum.DragDetectorDragStyle.Scriptable\|Scriptable` | Calculates desired motion via a custom function provided through `Class.DragDetector:SetDragStyleFunction()\|SetDragStyleFunction()`. | ### Drag direction By default, 3D motion and the associated `Class.DragDetector.DragStyle|DragStyle` map to world space. However, you may want to change the `Class.DragDetector.ReferenceInstance|ReferenceInstance`, `Class.DragDetector.Orientation|Orientation`, or `Class.DragDetector.Axis|Axis`, for example when building drag detectors into [models with adjustable parts](#anchored-models-with-adjustable-parts). | Property | Description | Default | | --- | --- | --- | | `Class.DragDetector.ReferenceInstance\|ReferenceInstance` | An instance whose pivot provides the **reference frame** for the drag detector. The `Class.DragDetector.DragFrame\|DragFrame` is expressed relative to this reference frame which may be retrieved via `Class.DragDetector:GetReferenceFrame()\|GetReferenceFrame()`. If the reference frame is `nil`, translation will be in the direction of (or in the plane perpendicular to) the `Class.DragDetector.Axis\|Axis` property in world space. | `nil` | | `Class.DragDetector.Orientation\|Orientation` | Specifies the **YXZ** rotation of axes of motion relative to the reference frame (does not change the orientation of the reference frame itself). Linear translation and axial rotation will be on this reoriented **Y** axis, and planar translation in the **XZ** plane. Changing this value automatically updates `Class.DragDetector.Axis\|Axis` and vice versa. | (0, 0, 0) | | `Class.DragDetector.Axis\|Axis` | The primary axis of motion, expressed relative to the reference frame. Changing this value automatically updates `Class.DragDetector.Orientation\|Orientation` and vice versa. | (0, 1, 0) | ### Response to motion The `Class.DragDetector.ResponseStyle|ResponseStyle` property specifies how an object responds to the proposed motion, depending on whether the object is `Class.BasePart.Anchored|Anchored` or not. | Setting | Anchored behavior | Unanchored behavior | | --- | --- | --- | | `Enum.DragDetectorResponseStyle.Geometric\|Geometric` | Both inside the running experience and in Studio edit mode, the position/orientation of an anchored object will be updated to exactly reflect the proposed motion. | For an unanchored object, behavior is the same as for an anchored object. However, in a running experience, the object will be anchored at the start of the drag and restored to unanchored upon drag release. | | `Enum.DragDetectorResponseStyle.Physical\|Physical` | An anchored object will default to **Geometric** behavior, as it is not affected by forces. | An unanchored object will be moved by [constraint forces](#physics-response) that attempt to bring it to the desired position and/or orientation given by the proposed motion. | | `Enum.DragDetectorResponseStyle.Custom\|Custom` | The object will not move at all, but `Class.DragDetector.DragFrame\|DragFrame` will still be updated and you can [respond to drag manipulation](#script-responses-to-clicking-and-dragging) however you'd like. | (same as anchored) | > **Warning:** Remember that `Class.DragDetector|DragDetectors` only work in Studio if you're **not** using the **Select**, **Move**, **Scale**, or **Rotate** tools. ### Axis & movement limits By default, there are no limits to 3D motion beyond the inherent restrictions of the `Class.DragDetector.DragStyle|DragStyle`. If necessary, you can apply minimum and maximum limits to both translation and rotation. Note, however, that these are **not** constraints; they merely impede the drag detector's attempts to generate motion in order to remain within limits. | Properties | Description | Default | | --- | --- | --- | | `Class.DragDetector.MinDragTranslation\|MinDragTranslation`
`Class.DragDetector.MaxDragTranslation\|MaxDragTranslation` | Limits to drag translation in each dimension. If MaxDragTranslation is greater than MinDragTranslation, translation will be clamped within that range. | (0, 0, 0) | | `Class.DragDetector.MinDragAngle\|MinDragAngle`
`Class.DragDetector.MaxDragAngle\|MaxDragAngle` | Only relevant if `Class.DragDetector.DragStyle\|DragStyle` is set to **RotateAxis**. If `Class.DragDetector.MaxDragAngle\|MaxDragAngle` is greater than `Class.DragDetector.MinDragAngle\|MinDragAngle`, rotation will be clamped within that range. | 0 | > **Warning:** When using axis/movement limits, you should always set the detector's `Class.DragDetector.ReferenceInstance|ReferenceInstance` so that the limits are relative to a dedicated reference frame. If you fail to establish the reference frame, each drag of the object will reset its limits to its own current world space position/orientation. ### Drag permissions Permission of players to interact with a given drag detector instance can be specified by the `Class.DragDetector.PermissionPolicy|PermissionPolicy` property. This is set to `Enum.DragDetectorPermissionPolicy.Everybody` by default, and it can also be changed to support scripted permission controls as shown in the code sample. | Setting | Description | | --- | --- | | `Enum.DragDetectorPermissionPolicy.Nobody\|Nobody` | No players can interact with the `Class.DragDetector`. | | `Enum.DragDetectorPermissionPolicy.Everybody\|Everybody` | All players can interact with the `Class.DragDetector`. | | `Enum.DragDetectorPermissionPolicy.Scriptable\|Scriptable` | Players' drag permissions will be determined by a function registered through `Class.DragDetector:SetPermissionPolicyFunction()\|SetPermissionPolicyFunction()`. Under this setting, failure to register a function or returning an invalid result will prevent all players from dragging. | ```lua local dragDetector = script.Parent.DragDetector dragDetector.PermissionPolicy = Enum.DragDetectorPermissionPolicy.Scriptable dragDetector:SetPermissionPolicyFunction(function(player, part) if player and player:GetAttribute("IsInTurn") then return true elseif part and not part:GetAttribute("IsDraggable") then return false else return true end end) ``` ### Physics response Assuming a dragger's [response style](#response-to-motion) is set to **Physical** and it is applied to an unanchored object, that object will be moved by constraint forces that attempt to bring it to the position/orientation given by the proposed motion. You can further customize the physical response through the following properties: | Property | Description | Default | | --- | --- | --- | | `Class.DragDetector.ApplyAtCenterOfMass\|ApplyAtCenterOfMass` | When false, drag force is applied at the point the user clicks on. When true, force is applied at the object's center of mass. | false | | `Class.DragDetector.MaxForce\|MaxForce` | Maximum force applied for the object to reach its goal. | 10000000 | | `Class.DragDetector.MaxTorque\|MaxTorque` | Maximum torque applied for the object to reach its goal. | 10000 | | `Class.DragDetector.Responsiveness\|Responsiveness` | Higher values cause the object to reach its goal more rapidly. | 10 | ### Modifier input Some `Class.DragDetector.DragStyle|DragStyle` modes allow users to hold down a **modifier** key/button to manipulate the dragged object in different ways. By default, the modifier is `Enum.KeyCode|LeftControl` on PC, `Enum.KeyCode|ButtonR1` on gamepad, or `Enum.KeyCode|ButtonL2` on VR. You can customize these modifiers through the `Class.DragDetector.KeyboardModeSwitchKeyCode|KeyboardModeSwitchKeyCode`, `Class.DragDetector.GamepadModeSwitchKeyCode|GamepadModeSwitchKeyCode`, or `Class.DragDetector.VRSwitchKeyCode|VRSwitchKeyCode` properties of the drag detector instance. ### Replication When the `Class.DragDetector.RunLocally|RunLocally` property is false (default), the client interprets all input to produce data that it sends to the server to perform the drag. In this mode, all custom event signals and registered functions must be in server `Class.Script|Scripts`. When the `Class.DragDetector.RunLocally|RunLocally` property is true, no events are replicated to the server. All custom event signals and registered functions must be in client `Class.LocalScript|LocalScripts` and you must use [remote events](/docs/en-us/scripting/events/remote.md) to propagate necessary changes to the server. ## Script responses to clicking and dragging Through [event signals](#event-signals), property changes, `Enum.DragDetectorDragStyle.Scriptable|Scriptable` drag style, and custom functions, scripts can respond to the manipulation of dragged objects to drive UI or make logical decisions, such as adjusting the light level in a room based on a sliding wall switch dimmer. ### Event signals Through the following event signals, you can detect when a user starts, continues, and ends dragging an object. | Event | Description | | --- | --- | | `Class.DragDetector.DragStart\|DragStart` | Fires when a user starts dragging the object. | | `Class.DragDetector.DragContinue\|DragContinue` | Fires when a user continues dragging the object after `Class.DragDetector.DragStart\|DragStart` has been initiated. | | `Class.DragDetector.DragEnd\|DragEnd` | Fires when a user stops dragging the object. | ```lua local dragDetector = script.Parent.DragDetector local highlight = Instance.new("Highlight") highlight.Enabled = false highlight.Parent = script.Parent dragDetector.DragStart:Connect(function() highlight.Enabled = true end) dragDetector.DragContinue:Connect(function() end) dragDetector.DragEnd:Connect(function() highlight.Enabled = false end) ``` ### Drag frame changes In addition to [event signals](#event-signals), you can monitor changes to the detector's `Class.DragDetector.DragFrame|DragFrame` directly. ```lua local dragDetector = script.Parent.DragDetector dragDetector:GetPropertyChangedSignal("DragFrame"):Connect(function() local currentDragTranslation = dragDetector.DragFrame.Position print(currentDragTranslation) end) ``` ### Scripted drag style If you set a detector's `Class.DragDetector.DragStyle|DragStyle` to **Scriptable**, you can provide your own function that takes in a `Datatype.Ray` and returns a world space `Datatype.CFrame`. The detector will move the motion so that the dragged object goes to that custom location/orientation. ```lua local Workspace = game:GetService("Workspace") local dragDetector = script.Parent.DragDetector dragDetector.DragStyle = Enum.DragDetectorDragStyle.Scriptable local cachedHitPoint = Vector3.zero local cachedHitNormal = Vector3.yAxis local function followTheCursor(cursorRay) -- Exclude dragged object from raycast detection local raycastParams = RaycastParams.new() raycastParams.FilterDescendantsInstances = {dragDetector.Parent} raycastParams.FilterType = Enum.RaycastFilterType.Exclude local hitPoint = Vector3.zero local hitNormal = Vector3.yAxis local raycastResult = Workspace:Raycast(cursorRay.Origin, cursorRay.Direction, raycastParams) if raycastResult then hitPoint = raycastResult.Position hitNormal = raycastResult.Normal.Unit else hitPoint = cachedHitPoint hitNormal = cachedHitNormal end cachedHitPoint = hitPoint cachedHitNormal = hitNormal local lookDir1 = hitNormal:Cross(Vector3.xAxis) local lookDir2 = hitNormal:Cross(Vector3.yAxis) local lookDir = if lookDir1.Magnitude > lookDir2.Magnitude then lookDir1.Unit else lookDir2.Unit return CFrame.lookAt(hitPoint, hitPoint + lookDir, hitNormal) end dragDetector:SetDragStyleFunction(followTheCursor) ``` ### Custom constraint function Drag detectors do not have built-in motion rules about grids and snapping, but you can register custom constraint functions to edit the detector's `Class.DragDetector.DragFrame|DragFrame` before it is applied. For example, you can keep motion on a grid by rounding positions to multiples of the grid increment, or simulate a chess game with rules of motion legal to each piece. ```lua local dragDetector = script.Parent.DragDetector local startPartPosition = nil local SNAP_INCREMENT = 4 dragDetector.DragStart:Connect(function() startPartPosition = script.Parent.Position end) dragDetector.DragEnd:Connect(function() startPartPosition = nil end) local function snapToWorldGrid(proposedMotion) if startPartPosition == nil then return proposedMotion end local snapIncrement = SNAP_INCREMENT // 1 if snapIncrement < 1 then return proposedMotion end local newWorldPosition = startPartPosition + proposedMotion.Position local roundedX = ((newWorldPosition.X / snapIncrement + 0.5) // 1) * snapIncrement local roundedY = ((newWorldPosition.Y / snapIncrement + 0.5) // 1) * snapIncrement local roundedZ = ((newWorldPosition.Z / snapIncrement + 0.5) // 1) * snapIncrement local newRoundedWorldPosition = Vector3.new(roundedX, roundedY, roundedZ) return proposedMotion.Rotation + (newRoundedWorldPosition - startPartPosition) end local connection = dragDetector:AddConstraintFunction(2, snapToWorldGrid) -- When applicable, remove the constraint function by invoking connection:Disconnect() ``` ## Example usage ### Unanchored physical objects A basic implementation of drag detectors is a tower balance game where players must carefully remove pieces and attempt to keep the tower upright. In the following tower structure, each piece has a child `Class.DragDetector` with a default `Class.DragDetector.DragStyle|DragStyle` of **TranslatePlane** so that players can pull the pieces outward but not upward or downward. ### Anchored models with adjustable parts You can easily create and share models which are primarily anchored, but which have one or more child parts/models that players can drag. For example, the following desk has two drawers which players can open to inspect what's inside. > **Warning:** When making children of models draggable, you should set the drag detector's `Class.DragDetector.ReferenceInstance|ReferenceInstance` to an object within the model that can serve as a dedicated reference frame, for example the desk's top surface. This establishes a consistent reference `Datatype.CFrame` for the style/direction of dragging, even if the model is rotated. ### Drag detectors and constraints You can combine drag detectors with `Class.Constraint|Constraints`, for example a marionette puppet. In the following setup, the control handles are anchored, the body parts are unanchored, and constraints hold the marionette together. Moving the handles with the **TranslateViewPlane** `Class.DragDetector.DragStyle|DragStyle` makes the marionette dance, and the individual body parts may also be moved with drag detectors, all while the model retains its integrity. ### 3D user interfaces 3D user interfaces are easily achievable through drag detectors, such as adjusting the brightness of a `Class.SpotLight` based on a sliding switch dimmer. You can also detect the **X** and **Z** axes individually to control two different aspects of a 3D user interface, such as the `Class.ParticleEmitter.Size|Size`, `Class.ParticleEmitter.Speed|Speed`, and `Class.ParticleEmitter.Color|Color` of a `Class.ParticleEmitter`. ```lua local model = script.Parent local slider = model.SliderPart local originPart = model.OriginPart local emitter = script.Parent.EmitterPart.ParticleEmitter local dragDetector = slider.DragDetector dragDetector.ReferenceInstance = originPart dragDetector.MinDragTranslation = Vector3.zero dragDetector.MaxDragTranslation = Vector3.new(10, 0, 10) local dragRangeX = dragDetector.MaxDragTranslation.X - dragDetector.MinDragTranslation.X local dragRangeZ = dragDetector.MaxDragTranslation.Z - dragDetector.MinDragTranslation.Z local MIN_PARTICLE_SIZE = 1 local MAX_PARTICLE_SIZE = 1.5 local MIN_PARTICLE_SPEED = 2.5 local MAX_PARTICLE_SPEED = 5 local COLOR1 = Color3.fromRGB(255, 150, 0) local COLOR2 = Color3.fromRGB(255, 0, 50) local function updateParticles(emitter) local dragFactorX = (dragDetector.DragFrame.Position.X - dragDetector.MinDragTranslation.X) / dragRangeX local dragFactorZ = (dragDetector.DragFrame.Position.Z - dragDetector.MinDragTranslation.Z) / dragRangeZ -- Adjust particle size and speed based on drag detector X factor emitter.Size = NumberSequence.new{ NumberSequenceKeypoint.new(0, 0), NumberSequenceKeypoint.new(0.1, MIN_PARTICLE_SIZE + ((MAX_PARTICLE_SIZE - MIN_PARTICLE_SIZE) * dragFactorX)), NumberSequenceKeypoint.new(1, 0) } local speed = MIN_PARTICLE_SPEED + ((MAX_PARTICLE_SPEED - MIN_PARTICLE_SPEED) * dragFactorX) emitter.Speed = NumberRange.new(speed, speed * 1.2) -- Adjust particle color based on drag detector Z factor local color = COLOR2:Lerp(COLOR1, dragFactorZ) emitter.Color = ColorSequence.new{ ColorSequenceKeypoint.new(0, color), ColorSequenceKeypoint.new(1, color) } end dragDetector:GetPropertyChangedSignal("DragFrame"):Connect(function() updateParticles(emitter) end) ```
--- title: "UI 9-slice design" url: /docs/en-us/ui/9-slice last_updated: 2026-06-29T19:34:16Z description: "UI 9-slice design lets you create UI elements of varying sizes without distorting the borders or corners." --- # UI 9-slice design When creating UI with custom borders and corners, you'll often need to render elements at different aspect ratios and visually surround [localized text](/docs/en-us/production/localization.md) or other contents of unknown dimensions. This lets you create UI elements of varying sizes without distorting the borders or corners. _Without 9-Slice_ _With 9-Slice_ Under the **9-slice** approach, one image (a single Roblox image asset) is internally divided into nine sub-images, each with different scaling rules. | Sub-image | Scaling | | --- | --- | | **1** **3** **7** **9** (corners) | none | | **2** **8** (top/bottom edges) | horizontal | | **4** **6** (left/right edges) | vertical | | **5** (center) | horizontal + vertical | ## Studio editor Slice configuration is possible by directly setting the `Enum.ScaleType` and slice properties on an image label or button, but Studio's built-in **9-Slice Editor** is more intuitive. ### Opening the editor To open the visual **9-Slice Editor** in Studio: 1. Select an `Class.ImageLabel` or `Class.ImageButton` with a valid asset ID entered into its **Image** field. 1. Set the **ScaleType** property to **Slice**. 1. Click on the **SliceCenter** property that appears, then click on the **…** within the row. 1. In the window that opens, 4 red lines overlay the image, representing the edges of the slices. The source image size is also displayed. ### Set offsets In Studio, drag the red lines to set the **offsets** from the left, right, top, and bottom edges of the image. > **Info:** If you need better precision, enter offset values in the fields on the right side of the editor window. As you reposition the dragger lines, the UI element automatically updates to show the result. _Slice setup_ _Result on 500×200 ImageLabel_ _Slice setup_ _Result on 500×200 ImageLabel_ > **Warning:** The offset values are different from the UI object's **SliceCenter** property values and represent the number of pixels between an edge of the source image and the dragger associated with that side. --- title: "UI animation/tweens" url: /docs/en-us/ui/animation last_updated: 2026-06-29T19:34:16Z description: "Explains how to animate GuiObjects using the process of tweening." --- # UI animation/tweens In animation, **tweening** is the process of generating intermediate frames between two key points in a sequence. When designing a user interface, you can use tweening to transition a `Class.GuiObject` smoothly from one state to another, such as: - Smoothly increasing the size of a button when a user selects it. - Sliding UI menus in and out from the screen edges. - Gradually animating a health bar between two widths when a user receives a health boost. ## Single-property tweens ### Position To tween the **position** of a `Class.GuiObject`: 1. Set the `Class.GuiObject.AnchorPoint|AnchorPoint` for the object. 2. Determine `Datatype.UDim2` coordinates for the object's target position, using the **scale** parameters of `Datatype.UDim2` instead of exact pixel values so that the object tweens to the exact center of the screen. 3. Pass a `Datatype.TweenInfo` and the target position to `Class.TweenService:Create()`. 4. Play the tween with `Class.Tween:Play()`. The following code snippet moves an `Class.ImageLabel` within a `Class.ScreenGui` to the exact center of the screen: ```lua local TweenService = game:GetService("TweenService") local Players = game:GetService("Players") local PlayerGui = Players.LocalPlayer:WaitForChild("PlayerGui") local ScreenGui = PlayerGui:WaitForChild("ScreenGui") local object = ScreenGui:WaitForChild("ImageLabel") object.AnchorPoint = Vector2.new(0.5, 0.5) local targetPosition = UDim2.new(0.5, 0, 0.5, 0) local tweenInfo = TweenInfo.new(2) local tween = TweenService:Create(object, tweenInfo, {Position = targetPosition}) tween:Play() ``` ### Size To tween the **size** of a `Class.GuiObject`: 1. Determine `Datatype.UDim2` coordinates for the object's target size, using the **scale** parameters of `Datatype.UDim2` instead of exact pixel values so that the object tweens to a relative percentage of the screen size. 2. Attach a `Class.UIAspectRatioConstraint` to the object to maintain its designed aspect ratio when tweening. 3. Pass a `Datatype.TweenInfo` and the target size to `Class.TweenService:Create()`. 4. Play the tween with `Class.Tween:Play()`. The following code snippet scales an `Class.ImageLabel` within a `Class.ScreenGui` to 40% of the screen width or height (whichever is smaller) from the object's center anchor point: ```lua local TweenService = game:GetService("TweenService") local Players = game:GetService("Players") local PlayerGui = Players.LocalPlayer:WaitForChild("PlayerGui") local ScreenGui = PlayerGui:WaitForChild("ScreenGui") local object = ScreenGui:WaitForChild("ImageLabel") object.AnchorPoint = Vector2.new(0.5, 0.5) local aspectRatioConstraint = Instance.new("UIAspectRatioConstraint") aspectRatioConstraint.Parent = object local targetSize = UDim2.new(0.4, 0, 0.4, 0) local tweenInfo = TweenInfo.new(2) local tween = TweenService:Create(object, tweenInfo, {Size = targetSize}) tween:Play() ``` ### Rotation To tween the **rotation** of a `Class.GuiObject`: 1. Set the `Class.GuiObject.AnchorPoint|AnchorPoint` for the object to rotate around. 2. Determine the target `Class.GuiObject.Rotation|Rotation` for the object. 3. Pass a `Datatype.TweenInfo` and the target rotation to `Class.TweenService:Create()`. 4. Play the tween with `Class.Tween:Play()`. ```lua local TweenService = game:GetService("TweenService") local Players = game:GetService("Players") local PlayerGui = Players.LocalPlayer:WaitForChild("PlayerGui") local ScreenGui = PlayerGui:WaitForChild("ScreenGui") local object = ScreenGui:WaitForChild("ImageLabel") object.AnchorPoint = Vector2.new(0.5, 0.5) local targetRotation = 45 local tweenInfo = TweenInfo.new(2) local tween = TweenService:Create(object, tweenInfo, {Rotation = targetRotation}) tween:Play() ``` ### Transparency Multiple properties control UI transparency, depending on the object type. You can tween each of these properties individually or combined through a [multi-property tween](#multi-property-tweens). Alternatively, you can tween an object's overall transparency by placing it inside a `Class.CanvasGroup` and tweening the group's `Class.CanvasGroup.GroupTransparency|GroupTransparency`. | UI Object | Properties | | --- | --- | | `Class.Frame` | `Class.GuiObject.BackgroundTransparency\|BackgroundTransparency` | | `Class.TextLabel` | `Class.GuiObject.BackgroundTransparency\|BackgroundTransparency`, `Class.TextLabel.TextTransparency\|TextTransparency`, `Class.TextLabel.TextStrokeTransparency\|TextStrokeTransparency` | | `Class.TextButton` | `Class.GuiObject.BackgroundTransparency\|BackgroundTransparency`, `Class.TextButton.TextTransparency\|TextTransparency`, `Class.TextButton.TextStrokeTransparency\|TextStrokeTransparency` | | `Class.ImageLabel` | `Class.GuiObject.BackgroundTransparency\|BackgroundTransparency`, `Class.ImageLabel.ImageTransparency\|ImageTransparency` | | `Class.ImageButton` | `Class.GuiObject.BackgroundTransparency\|BackgroundTransparency`, `Class.ImageButton.ImageTransparency\|ImageTransparency` | > **Info:** As a modern alternative to `Class.TextLabel.TextStrokeTransparency` and `Class.TextButton.TextStrokeTransparency`, you can tween stroke transparency on a `Class.UIStroke` object as outlined in [Stroke](#stroke). ```lua local TweenService = game:GetService("TweenService") local Players = game:GetService("Players") local PlayerGui = Players.LocalPlayer:WaitForChild("PlayerGui") local ScreenGui = PlayerGui:WaitForChild("ScreenGui") local object = ScreenGui:WaitForChild("ImageLabel") local targetTransparency = 0.8 local tweenInfo = TweenInfo.new(2) local tween = TweenService:Create(object, tweenInfo, {ImageTransparency = targetTransparency}) tween:Play() ``` ```lua local TweenService = game:GetService("TweenService") local Players = game:GetService("Players") local PlayerGui = Players.LocalPlayer:WaitForChild("PlayerGui") local ScreenGui = PlayerGui:WaitForChild("ScreenGui") local canvasGroup = ScreenGui:WaitForChild("CanvasGroup") local targetTransparency = 0.8 local tweenInfo = TweenInfo.new(2) local tween = TweenService:Create(canvasGroup, tweenInfo, {GroupTransparency = targetTransparency}) tween:Play() ``` ### Color Multiple properties control UI color, depending on the object type. You can tween each of these properties individually or combined through a [multi-property tween](#multi-property-tweens). Alternatively, you can tween an object's overall color by placing it inside a `Class.CanvasGroup` and tweening the group's `Class.CanvasGroup.GroupColor3|GroupColor3`. | UI Object | Properties | | --- | --- | | `Class.Frame` | `Class.GuiObject.BackgroundColor3\|BackgroundColor3`, `Class.GuiObject.BorderColor3\|BorderColor3` | | `Class.TextLabel` | `Class.GuiObject.BackgroundColor3\|BackgroundColor3`, `Class.GuiObject.BorderColor3\|BorderColor3`, `Class.TextLabel.TextColor3\|TextColor3`, `Class.TextLabel.TextStrokeColor3\|TextStrokeColor3` | | `Class.TextButton` | `Class.GuiObject.BackgroundColor3\|BackgroundColor3`, `Class.GuiObject.BorderColor3\|BorderColor3`, `Class.TextButton.TextColor3\|TextColor3`, `Class.TextButton.TextStrokeColor3\|TextStrokeColor3` | | `Class.ImageLabel` | `Class.GuiObject.BackgroundColor3\|BackgroundColor3`, `Class.GuiObject.BorderColor3\|BorderColor3`, `Class.ImageLabel.ImageColor3\|ImageColor3` | | `Class.ImageButton` | `Class.GuiObject.BackgroundColor3\|BackgroundColor3`, `Class.GuiObject.BorderColor3\|BorderColor3`, `Class.ImageButton.ImageColor3\|ImageColor3` | > **Info:** As a modern alternative to `Class.GuiObject.BorderColor3`, `Class.TextLabel.TextStrokeColor3`, and `Class.TextButton.TextStrokeColor3`, you can tween stroke color on a `Class.UIStroke` object as outlined in [Stroke](#stroke). ```lua local TweenService = game:GetService("TweenService") local Players = game:GetService("Players") local PlayerGui = Players.LocalPlayer:WaitForChild("PlayerGui") local ScreenGui = PlayerGui:WaitForChild("ScreenGui") local object = ScreenGui:WaitForChild("ImageLabel") local targetColor = Color3.fromRGB(255, 0, 0) local tweenInfo = TweenInfo.new(2) local tween = TweenService:Create(object, tweenInfo, {ImageColor3 = targetColor}) tween:Play() ``` ```lua local TweenService = game:GetService("TweenService") local Players = game:GetService("Players") local PlayerGui = Players.LocalPlayer:WaitForChild("PlayerGui") local ScreenGui = PlayerGui:WaitForChild("ScreenGui") local canvasGroup = ScreenGui:WaitForChild("CanvasGroup") local targetColor = Color3.fromRGB(255, 0, 0) local tweenInfo = TweenInfo.new(2) local tween = TweenService:Create(canvasGroup, tweenInfo, {GroupColor3 = targetColor}) tween:Play() ``` ### Stroke Multiple properties control UI borders, depending on the object type. | UI object | Properties | | --- | --- | | `Class.Frame` | `Class.GuiObject.BorderSizePixel\|BorderSizePixel`, `Class.GuiObject.BorderColor3\|BorderColor3` | | `Class.TextLabel` | `Class.GuiObject.BorderSizePixel\|BorderSizePixel`, `Class.GuiObject.BorderColor3\|BorderColor3`, `Class.TextLabel.TextStrokeColor3\|TextStrokeColor3`, `Class.TextLabel.TextStrokeTransparency\|TextStrokeTransparency` | | `Class.TextButton` | `Class.GuiObject.BorderSizePixel\|BorderSizePixel`, `Class.GuiObject.BorderColor3\|BorderColor3`, `Class.TextButton.TextStrokeColor3\|TextStrokeColor3`, `Class.TextButton.TextStrokeTransparency\|TextStrokeTransparency` | | `Class.ImageLabel` | `Class.GuiObject.BorderSizePixel\|BorderSizePixel`, `Class.GuiObject.BorderColor3\|BorderColor3` | | `Class.ImageButton` | `Class.GuiObject.BorderSizePixel\|BorderSizePixel`, `Class.GuiObject.BorderColor3\|BorderColor3` | Alternatively, you can apply a `Class.UIStroke` child and tween its thickness, color, and/or transparency. | UI Object | Properties | | --- | --- | | `Class.UIStroke` | `Class.UIStroke.Color\|Color`, `Class.UIStroke.Thickness\|Thickness`, `Class.UIStroke.Transparency\|Transparency` | ```lua local TweenService = game:GetService("TweenService") local Players = game:GetService("Players") local PlayerGui = Players.LocalPlayer:WaitForChild("PlayerGui") local ScreenGui = PlayerGui:WaitForChild("ScreenGui") local object = ScreenGui:WaitForChild("TextLabel") local stroke = Instance.new("UIStroke") stroke.Color = Color3.fromRGB(255, 255, 255) stroke.Thickness = 5 stroke.Parent = object local targetColor = Color3.fromRGB(255, 0, 0) local targetThickness = 10 local tweenInfo = TweenInfo.new(2) local tween = TweenService:Create(stroke, tweenInfo, {Color = targetColor, Thickness = targetThickness}) tween:Play() ``` ## Multi-property tweens You can combine any of the [single-property tweens](#single-property-tweens) into more complex tweens by passing multiple target properties to `Class.TweenService:Create()`, for example **position + rotation** or **size + transparency**. ```lua local TweenService = game:GetService("TweenService") local Players = game:GetService("Players") local PlayerGui = Players.LocalPlayer:WaitForChild("PlayerGui") local ScreenGui = PlayerGui:WaitForChild("ScreenGui") local object = ScreenGui:WaitForChild("ImageLabel") object.AnchorPoint = Vector2.new(0.5, 0.5) local targetPosition = UDim2.new(0.5, 0, 0.5, 0) local targetRotation = 45 local tweenInfo = TweenInfo.new(2) local tween = TweenService:Create(object, tweenInfo, {Position = targetPosition, Rotation = targetRotation}) tween:Play() ``` ```lua local TweenService = game:GetService("TweenService") local Players = game:GetService("Players") local PlayerGui = Players.LocalPlayer:WaitForChild("PlayerGui") local ScreenGui = PlayerGui:WaitForChild("ScreenGui") local object = ScreenGui:WaitForChild("ImageLabel") object.AnchorPoint = Vector2.new(0.5, 0.5) local aspectRatioConstraint = Instance.new("UIAspectRatioConstraint") aspectRatioConstraint.Parent = object local targetSize = UDim2.new(0.4, 0, 0.4, 0) local targetTransparency = 0.8 local tweenInfo = TweenInfo.new(2) local tween = TweenService:Create(object, tweenInfo, {Size = targetSize, ImageTransparency = targetTransparency}) tween:Play() ``` ## Tween sequences You can chain UI animations to occur one after another by playing subsequent tweens upon the previous tween's `Class.TweenBase.Completed|Completed` event. For example, the following script moves an object to the center of the screen, then rotates it by 45°. ```lua local TweenService = game:GetService("TweenService") local Players = game:GetService("Players") local PlayerGui = Players.LocalPlayer:WaitForChild("PlayerGui") local ScreenGui = PlayerGui:WaitForChild("ScreenGui") local object = ScreenGui:WaitForChild("ImageLabel") object.AnchorPoint = Vector2.new(0.5, 0.5) local targetPosition = UDim2.new(0.5, 0, 0.5, 0) local targetRotation = 45 local tweenInfo = TweenInfo.new(2) local positionTween = TweenService:Create(object, tweenInfo, {Position = targetPosition}) local rotationTween = TweenService:Create(object, tweenInfo, {Rotation = targetRotation}) -- Initially play position tween positionTween:Play() -- Play rotation tween upon completion of position tween positionTween.Completed:Connect(function() rotationTween:Play() end) ``` ## Style transitions > **Success:** This feature is currently in beta. Enable it through **File** ⟩ **Beta Features** ⟩ **Styling Transitions**. UI objects can also be animated through **style transitions**, comparable to [CSS transitions](/docs/en-us/ui/styling/css-comparisons.md#transitions), where one or more UI properties are tweened through `Class.StyleRule` definitions. See the [Style Editor](/docs/en-us/ui/styling/editor.md#style-transitions) guide for details. ![Final styled state rule in the Style Editor.](../assets/ui/ui-styling/SE-Transition-Rule-Result.png) ## Easing options Using the easing options of `Datatype.TweenInfo`, you can control the easing **style** and **direction** of UI animations. ### Style `Enum.EasingStyle` sets the rate of interpolation from start to end. By default, easing style is set to `Enum.EasingStyle.Quad`. | Style | Description | | --- | --- | | **Linear** | Moves at a constant speed. | | **Sine** | Speed is determined by a sine wave for a gentle easing motion. | | **Quad** | Similar to **Sine** but with a slightly sharper curve based on quadratic interpolation. | | **Cubic** | Similar to **Quad** but with a slightly sharper curve based on cubic interpolation. | | **Quart** | Similar to **Cubic** but with an even sharper curve based on quartic interpolation. | | **Quint** | Similar to **Quart** but with an even sharper curve based on quintic interpolation. | | **Exponential** | The sharpest curve based on exponential interpolation. | | **Circular** | Follows a circular arc, such that acceleration is more sudden and deceleration more gradual versus **Quint** or **Exponential**. | | **Back** | Slightly overshoots the target, then backs into place. | | **Bounce** | Bounces backwards multiple times after reaching the target, before eventually settling. | | **Elastic** | Moves as if attached to a rubber band, overshooting the target several times. | #### EasingDirection = In ![Graphs of EasingStyle variations with an 'In' EasingDirection.](../assets/engine-api/enums/EasingStyle/Easing-Styles-In.png) #### EasingDirection = Out ![Graphs of EasingStyle variations with an 'Out' EasingDirection.](../assets/engine-api/enums/EasingStyle/Easing-Styles-Out.png) #### EasingDirection = InOut ![Graphs of EasingStyle variations with an 'InOut' EasingDirection.](../assets/engine-api/enums/EasingStyle/Easing-Styles-InOut.png) ```lua local tweenInfo = TweenInfo.new(2, Enum.EasingStyle.Cubic) local tween = TweenService:Create(object, tweenInfo, {Rotation = 45}) ``` ### Direction `Enum.EasingDirection` defines how the [easing style](#style) interpolation applies to an object, with a default of **Out**. Note that a tween with **Linear** easing style is not affected, as linear interpolation is constant from start to end. | Direction | Description | | --- | --- | | **In** | The easing style applies in a forward direction. | | **Out** | The easing style applies in a reverse direction. | | **InOut** | The easing style applies forward for the first half and in reverse for the second half. | ```lua local tweenInfo = TweenInfo.new(2, Enum.EasingStyle.Cubic, Enum.EasingDirection.InOut) local tween = TweenService:Create(object, tweenInfo, {Rotation = 45}) ``` ## Typewriter effect on text You can easily enhance text-based UI, such as cutscene banners, player instructions, and prompts, with animated effects. The "typewriter" effect is ideal for `Class.TextLabel|TextLabels` that tell a story, output NPC conversations, etc. 1. Create a new `Class.ModuleScript` within **ReplicatedStorage**. 2. Rename the new script **AnimateUI**. 3. Paste the following code into the script:```lua local LocalizationService = game:GetService("LocalizationService") local Players = game:GetService("Players") local SOURCE_LOCALE = "en" local translator = nil local AnimateUI = {} function AnimateUI.loadTranslator() pcall(function() translator = LocalizationService:GetTranslatorForPlayerAsync(Players.LocalPlayer) end) if not translator then pcall(function() translator = LocalizationService:GetTranslatorForLocaleAsync(SOURCE_LOCALE) end) end end function AnimateUI.typeWrite(guiObject, text, delayBetweenChars) guiObject.Visible = true guiObject.AutoLocalize = false local displayText = text -- Translate text if possible if translator then displayText = translator:Translate(guiObject, text) end -- Replace line break tags so grapheme loop will not miss those characters displayText = displayText:gsub("", "\n") -- Remove RichText tags since char-by-char animation will break the tags displayText = displayText:gsub("<[^<>]->", "") -- Set translated/modified text on parent guiObject.Text = displayText local index = 0 for first, last in utf8.graphemes(displayText) do index += 1 guiObject.MaxVisibleGraphemes = index task.wait(delayBetweenChars) end end return AnimateUI ``` > **Info:** If your experience's **source language** is not English (`"en"`), change the locale code for `SOURCE_LOCALE` to match the source language in [localization settings](/docs/en-us/production/localization.md#localization-settings). 4. Create a `Class.TextLabel` in a suitable location, such as within a `Class.ScreenGui` parented to `Class.StarterGui`. 5. Insert a new `Class.LocalScript` as a direct child of the label and paste in the following code. Note that each message is output by calling `AnimateUI.typeWrite()` with parameters for the parent object, the string to output, and the delay between characters.```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local AnimateUI = require(ReplicatedStorage:WaitForChild("AnimateUI")) local label = script.Parent -- Load translator if experience is localized --AnimateUI.loadTranslator() local message1 = [[Beyond this door is the
Great Zorgoth... 🗡]] AnimateUI.typeWrite(label, message1, 0.05) task.wait(1) local message2 = [[...who rules this dungeon unchallenged! 😈]] AnimateUI.typeWrite(label, message2, 0.05) ```
--- title: "UI appearance modifiers" url: /docs/en-us/ui/appearance-modifiers last_updated: 2026-06-29T19:34:16Z description: "Explains how to use appearance modifiers to customize basic user interface objects." --- # UI appearance modifiers By utilizing **appearance modifiers**, you can further customize the appearance of your `Class.GuiObject|GuiObjects`. - Apply a [gradient](#gradient) to the background of an object. - Apply a [stroke](#stroke) to text or a border. - Set [rounded corners](#corners) for an object. - Increase [padding](#padding) between the borders of an object. - Add a [drop shadow](#shadow) behind an object. ## Gradient The `Class.UIGradient` object applies a color and transparency gradient to its parent `Class.GuiObject`. You can configure the gradient by: - Setting its [colors](#color-sequence) through a `Datatype.ColorSequence` in the gradient's `Class.UIGradient.Color|Color` property. - Setting its [transparency](#transparency) through a `Datatype.NumberSequence` in the gradient's `Class.UIGradient.Transparency|Transparency` property. - Choosing the gradient's starting point (inside or outside the parent's bounds) through the `Class.UIGradient.Offset|Offset` property. - Choosing the gradient's angle through the `Class.UIGradient.Rotation|Rotation` property. ### Color sequence To set a gradient's color sequence: 1. In the [Explorer](/docs/en-us/studio/explorer.md) window, select the `Class.UIGradient`. 2. In the [Properties](/docs/en-us/studio/properties.md) window, click inside the `Class.UIGradient.Color|Color` property field, then click the **⋯** button to the right of the input box. A color sequence pop-up displays. Each triangle on the bottom axis of the color sequence is a **keypoint** that determines the color value at that point.![Color sequence popup from white to white](../assets/studio/general/ColorSequence-White-Keypoints-Labeled.png) 3. Click a keypoint in the color sequence, then click the small square next to **Color** to open the **Colors** pop-up window. 4. Select the desired color for the keypoint.![Color sequence popup from red to white](../assets/studio/general/ColorSequence-Red-White.png) 5. If needed, you can: - Add another keypoint by clicking anywhere on the graph. - Drag an existing keypoint to a new position, or select a keypoint and enter a specific time value through the **Time** input. - Delete a keypoint by selecting it and clicking the **Delete** button. - Reset the sequence by clicking the **Reset** button. ### Transparency To adjust a gradient's transparency across its range: 1. In the [Explorer](/docs/en-us/studio/explorer.md) window, select the `Class.UIGradient`. 2. In the [Properties](/docs/en-us/studio/properties.md) window, click inside the `Class.UIGradient.Transparency|Transparency` property field, then click the **⋯** button to the right of the input box. A number sequence pop-up displays. Each square across the number sequence graph is a **keypoint** that determines the transparency value at that point.![Keypoints labeled in number sequence popup](../assets/studio/general/NumberSequence-0-0-Keypoints-Labeled.png) 3. Click and drag any keypoint around, or select a keypoint and enter a specific time/value combination through the **Time** and **Value** inputs.![Number sequence popup from 0 to 1](../assets/studio/general/NumberSequence-0-1.png) 4. If needed, you can: - Add another keypoint by clicking anywhere on the graph. - Delete a keypoint by selecting it and clicking the **Delete** button. - Reset the sequence by clicking the **Reset** button. ### Offset and rotation The `Class.UIGradient.Offset|Offset` and `Class.UIGradient.Rotation|Rotation` properties let you adjust the gradient's control points and its angle. As illustrated in the following diagrams, `Class.UIGradient.Offset|Offset` is based on a **percentage** of the parent's width or height, and both positive or negative values are valid. _`Class.UIGradient.Offset|Offset.X` = `0`_ _`Class.UIGradient.Offset|Offset.X` = `0.25`_ _`Class.UIGradient.Offset|Offset.X` = `-0.25`_ Similarly, when you rotate the gradient, the control points also rotate: _`Class.UIGradient.Rotation|Rotation` = `0`_ _`Class.UIGradient.Rotation|Rotation` = `45`_ _`Class.UIGradient.Rotation|Rotation` = `-90`_ ## Stroke The `Class.UIStroke` instance applies an outline to text or a border. Key features include: - Adjustable [color](#color-or-gradient) and [thickness](#thickness) of the stroke outline. - Ability to set the stroke [transparency](#transparency-1) independently of the parent's transparency. - Customizable [position and/or offset](#border-positionoffset) relative to the parent's border. - Three [corner styles](#corner-style) (round, bevel, or miter). - Stroke [gradient](#color-or-gradient) support through the `Class.UIGradient` instance. ### Thickness You can set the stroke width through the `Class.UIStroke.Thickness|Thickness` property which is measured in pixels (default) or scaled relative to the parent, depending on the modifier's `Class.UIStroke.StrokeSizingMode|StrokeSizingMode`. If stroke is on text and `Class.UIStroke.StrokeSizingMode|StrokeSizingMode` is set to `Enum.StrokeSizingMode.ScaledSize|ScaledSize`, thickness is relative to the font size. _`Class.UIStroke.Thickness` = `4`_ _`Class.UIStroke.Thickness` = `12`_ > **Warning:** Avoid [tweening](/docs/en-us/ui/animation.md) the `Class.UIStroke.Thickness|Thickness` property of a `Class.UIStroke` instance applied to **text** objects. This renders and stores many glyph sizes each frame, potentially causing performance issues or text flickering. ### Color or gradient You can set the stroke color through the `Class.UIStroke.Color|Color` property, as well as insert a child `Class.UIGradient` instance to create gradient strokes. _`Class.UIStroke.Color` = `(0, 95, 225)`_ _`Class.UIStroke` with `Class.UIGradient` child_ > **Info:** Both the parent object and `Class.UIStroke` can have child `Class.UIGradient` instances, letting you set gradients on the **stroke** and **fill** independently. ### Text outline or border Depending on its parent type, `Class.UIStroke` operates as either a **text outline** or as a **border**. When you parent `Class.UIStroke` to a text object like `Class.TextLabel`, it applies to the text's outline; when you parent `Class.UIStroke` to other `Class.GuiObject|GuiObjects`, it applies to the border. _`Class.TextLabel` with `Class.UIStroke` child_ _`Class.Frame` with `Class.UIStroke` and `Class.UICorner` children_ When applied to a text object, you can override the default stroke behavior by the `Class.UIStroke.ApplyStrokeMode|ApplyStrokeMode` property, letting you apply the stroke to the object's bounds instead of the text itself. You can even control the text outline and border independently by parenting two `Class.UIStroke` instances to a text object, one set to **Contextual** and the other to **Border**. _`Class.UIStroke.ApplyStrokeMode` = `Enum.ApplyStrokeMode.Contextual|Contextual`_ _`Class.UIStroke.ApplyStrokeMode` = `Enum.ApplyStrokeMode.Border|Border`_ ### Border position/offset `Class.UIStroke.BorderStrokePosition|BorderStrokePosition` sets the stroke's position relative to its parent's border and `Class.UIStroke.BorderOffset|BorderOffset` lets you specify an additional offset to the stroke's position. _`Class.UIStroke.BorderStrokePosition|BorderStrokePosition` = `Enum.BorderStrokePosition.Center|Center`_ _`Class.UIStroke.BorderStrokePosition|BorderStrokePosition` = `Enum.BorderStrokePosition.Inner|Inner`_ _`Class.UIStroke.BorderStrokePosition|BorderStrokePosition` = `Enum.BorderStrokePosition.Outer|Outer`_ _`Class.UIStroke.BorderOffset|BorderOffset` = `(0.15, 0)`_ _`Class.UIStroke.BorderOffset|BorderOffset` = `(0, -16)`_ ### Transparency The `Class.UIStroke.Transparency|Transparency` property sets the stroke transparency independently of the parent object's `Class.GuiObject.BackgroundTransparency|BackgroundTransparency` or `Class.TextLabel.TextTransparency|TextTransparency`. This allows you to render text and borders that are "hollow" (consisting of only an outline). _`Class.TextLabel.TextTransparency` = `0``Class.UIStroke.Transparency` = `0.5`_ _`Class.TextLabel.TextTransparency` = `1``Class.UIStroke.Transparency` = `0`_ ### Corner style The `Class.UIStroke.LineJoinMode|LineJoinMode` property lets you control how corners are interpreted. It accepts a value of either `Enum.LineJoinMode.Round|Round`, `Enum.LineJoinMode.Bevel|Bevel`, or `Enum.LineJoinMode.Miter|Miter`. _`Class.UIStroke.LineJoinMode|LineJoinMode` = `Enum.LineJoinMode.Round|Round`_ _`Class.UIStroke.LineJoinMode|LineJoinMode` = `Enum.LineJoinMode.Bevel|Bevel`_ _`Class.UIStroke.LineJoinMode|LineJoinMode` = `Enum.LineJoinMode.Miter|Miter`_ ### Layering To layer multiple sibling `Class.UIStroke` instances on a `Class.GuiObject` from front to back, you can utilize the `Class.UIStroke.ZIndex|ZIndex` property. Those with a lower `Class.UIStroke.ZIndex|ZIndex` render under (behind) those with a higher `Class.UIStroke.ZIndex|ZIndex`. Note that the rendering order for `Class.UIStroke` instances with the same `Class.UIStroke.ZIndex|ZIndex` is undefined. Do not apply multiple `Class.UIStroke` instances with the same index if their rendering order matters. ## Corners The `Class.UICorner` instance applies deformation to the corners of its parent `Class.GuiObject`. You can control the applied radius through the `Class.UICorner.CornerRadius|CornerRadius` property using either `Datatype.UDim.Scale|Scale` or `Datatype.UDim.Offset|Offset`. `Datatype.UDim.Scale|Scale` rounds the corners to a **percentage** based on the total length of the **shortest** edge of the parent, meaning that a scale of `0.5` or higher deforms the parent into a "pill" shape, regardless of its width or height. `Datatype.UDim.Offset|Offset` rounds the corners to a specific number of **pixels**, regardless of the width/height of the parent. _`Datatype.UDim.Scale|Scale` = `0.25`  ·  `Datatype.UDim.Offset|Offset` = `0`_ _`Datatype.UDim.Scale|Scale` = `0.5`  ·  `Datatype.UDim.Offset|Offset` = `0`_ _`Datatype.UDim.Scale|Scale` = `0`  ·  `Datatype.UDim.Offset|Offset` = `32`_ _`Datatype.UDim.Scale|Scale` = `0`  ·  `Datatype.UDim.Offset|Offset` = `64`_ You can also control **individual corners** through the `Class.UICorner.TopLeftRadius|TopLeftRadius`, `Class.UICorner.TopRightRadius|TopRightRadius`, `Class.UICorner.BottomRightRadius|BottomRightRadius`, and `Class.UICorner.BottomLeftRadius|BottomLeftRadius` properties. > **Success:** The ability to set individual corners is currently in beta. Enable the capability through **File** ⟩ **Beta Features** ⟩ **New UI Capabilities**. During this Studio beta period, you cannot publish live games that use individually rounded UI corners._`Class.UICorner.TopLeftRadius|TopLeftRadius` = `(0, 0)``Class.UICorner.TopRightRadius|TopRightRadius` = `(0, 48)``Class.UICorner.BottomRightRadius|BottomRightRadius` = `(0, 48)``Class.UICorner.BottomLeftRadius|BottomLeftRadius` = `(0, 0)`_ ## Padding A `Class.UIPadding` object applies top, bottom, left, and/or right padding to the contents of the parent `Class.GuiObject`. For example, you can move the text inside a text label downward or upward by applying an offset to the modifier's `Class.UIPadding.PaddingBottom|PaddingBottom` property. _`Class.TextLabel` without `Class.UIPadding`_ _`Class.UIPadding.PaddingBottom|PaddingBottom` = `(0, -20)`_ _`Class.UIPadding.PaddingBottom|PaddingBottom` = `(0, 30)`_ ## Shadow > **Success:** This feature is currently in beta. Enable it through **File** ⟩ **Beta Features** ⟩ **New UI Capabilities**. During this Studio beta period, you cannot publish live games that use UI shadows. A `Class.UIShadow` object renders a drop shadow behind its parent UI instance. Key features include: - Adjust the shadow's [bluriness](#bluriness). - Adjust the shadow's [color and transparency](#color-and-transparency). - Adjust the shadow's [offset and spread](#offset-and-spread). - If the parent is rotated by `Class.GuiObject.Rotation`, the shadow will also be rotated. - If the parent has [round corners](#corners), the shadow will also have round corners. > **Info:**`Class.UIShadow` does not support text. If you add this modifier to a `Class.TextLabel` or `Class.TextButton`, it renders a shadow of the object's rectangular bounding area, not of the text. ### Bluriness The `Class.UIShadow.BlurRadius|BlurRadius` property determines the shadow's blurriness. A larger value makes the shadow's edges appear softer. Note that the **scale** factor of this `Datatype.UDim` property is relative to the parent's width or height, whichever is shorter. _`Class.UIShadow.BlurRadius|BlurRadius` = `(0, 0)`_ _`Class.UIShadow.BlurRadius|BlurRadius` = `(0.75, 0)`_ ### Color and transparency The `Class.UIShadow.Color|Color` property determines the shadow's color, while `Class.UIShadow.Transparency|Transparency` sets the shadow's transparency with `0` being fully opaque and `1` being fully transparent. _`Class.UIShadow.Color|Color` = `(255, 255, 255)`  ·  `Class.UIShadow.Transparency|Transparency` = `0.6`_ _`Class.UIShadow.Color|Color` = `(50, 0, 35)`  ·  `Class.UIShadow.Transparency|Transparency` = `0`_ ### Offset and spread The `Class.UIShadow.Offset|Offset` property moves the shadow relative to the parent's position, while `Class.UIShadow.Spread|Spread` expands or shrinks the shadow relative to the parent's size. _`Class.UIShadow.Offset|Offset` = `{0, -14}, {0, 6}`_ _`Class.UIShadow.Offset|Offset` = `{0.1, 0}, {-0.15, 0}`_ _`Class.UIShadow.Spread|Spread` = `{0, 10}, {0, 10}`_ _`Class.UIShadow.Spread|Spread` = `{0.3, 0}, {0.3, 0}`_ --- title: "Text & image buttons" url: /docs/en-us/ui/buttons last_updated: 2026-06-29T19:34:16Z description: "Buttons allow users to prompt an action." --- # Text & image buttons **Buttons** are `Class.GuiObject|GuiObjects` that allow users to perform an action. You can customize buttons to provide context and feedback, such as changing the visual appearance or [scripting](#script-buttons) audible feedback when a user clicks a button. There are two types of buttons which you can place [on‑screen](/docs/en-us/ui/on-screen-containers.md) or [in‑experience](/docs/en-us/ui/in-experience-containers.md): - A `Class.TextButton` is a rectangle with text that triggers the `Class.GuiButton.Activated|Activated` event on click/tap. - An `Class.ImageButton` is a rectangle with an image that triggers the `Class.GuiButton.Activated|Activated` event on click/tap. It features additional states for swapping the image on user hover or press. ## Create buttons on the screen Buttons on a screen are useful to quickly guide users to various menus or pages. To add a button to the screen: 1. In the **Explorer** window, select **StarterGui** and add a **ScreenGui**. 1. Hover over **StarterGui** and click the ⊕ button. A contextual menu displays. 2. Insert a **ScreenGui**. 2. Select the new **ScreenGui** and add a button. 1. Hover over **ScreenGui** and click the ⊕ button. A contextual menu displays. 2. Insert either a **TextButton** or **ImageButton**. ## Create buttons on part faces Buttons on a part are useful for allowing users to interact with parts. For example, you can let users step on a button to complete an action. To add a button to the face of a part: 1. In the **Explorer** window, select the **part** and add a **SurfaceGui**. 1. Hover over the **part** and click the ⊕ button. A contextual menu displays. 2. Insert a **SurfaceGui**. 2. Select the new **SurfaceGui** and add any type of button or input. 1. Hover over **SurfaceGui** and click the ⊕ button. A contextual menu displays. 2. Insert either a **TextButton** or **ImageButton**. > **Warning:** If you don't see the button, try [choosing a different face](/docs/en-us/parts/textures-decals.md#choose-a-face) in the **Face** property of the **SurfaceGui**. ## Change the appearance of an ImageButton Changing the appearance of an `Class.ImageButton` when a user is interacting with it provides useful visual feedback. For example, when an `Class.ImageButton` changes visual appearance when a user hovers over it, it lets the user know that it isn't disabled and that they have the option to click it if they want to perform that `Class.ImageButton` action. An `Class.ImageButton` has three properties to change its visual appearance: - `Class.ImageButton.Image|Image` - The image that displays when a user is not interacting with the `Class.ImageButton`. - `Class.ImageButton.HoverImage|HoverImage` - The image that displays when a user is hovering their cursor over the `Class.ImageButton`. - `Class.ImageButton.PressedImage|PressedImage` - The image that displays when a user is clicking the `Class.ImageButton`. _Normal_ _Hover_ _Pressed_ To change the appearance of an `Class.ImageButton` with user input: 1. Add an **ImageButton** to a [screen](#create-buttons-on-the-screen) or a [surface](#create-buttons-on-the-screen). 2. In the **Explorer** window, click the **ImageButton** object. 3. In the **Properties** window, assign three different respective asset IDs for the **Image**, **HoverImage**, and **PressedImage** properties. ## Script buttons You can script an action when a user presses a button by connecting the button to a `Class.GuiButton.Activated` event. For example, when you parent the following `Class.LocalScript` to a button, the button changes to a random color every time a user clicks it. ```lua local button = script.Parent local RNG = Random.new() local function onButtonActivated() -- randomize the button color button.BackgroundColor3 = Color3.fromHSV(RNG:NextNumber(), 1, 1) end button.Activated:Connect(onButtonActivated) ``` --- title: "Frames" url: /docs/en-us/ui/frames last_updated: 2026-06-29T19:34:16Z description: "Frames are basic containers for UI objects." --- # Frames Basic `Class.Frame|Frames` act as containers for other `Class.GuiObject|GuiObjects` such as [labels](/docs/en-us/ui/labels.md) and [buttons](/docs/en-us/ui/buttons.md). You can apply frames to display on a user's [screen](/docs/en-us/ui/on-screen-containers.md) or on a [surface](/docs/en-us/ui/in-experience-containers.md) within the experience. ![Example Frame on the screen containing a TextLabel, TextBox, and ImageButton.](../assets/ui/ui-objects/Frame-Example.jpg) Frames are ideal containers for responsive layouts such as [list and flex layouts](/docs/en-us/ui/list-flex-layouts.md), allowing you to change the size of the frame and dynamically adjust how layout items fit within it. `Class.Frame|Frames` are also core `Class.GuiObject|GuiObjects`, so you can customize properties such as `Class.GuiObject.BackgroundColor3|BackgroundColor3`, `Class.GuiObject.Transparency|Transparency`, apply a [background gradient](/docs/en-us/ui/appearance-modifiers.md#gradient) or [border](/docs/en-us/ui/appearance-modifiers.md#stroke), and more. Aside from their common use as containers, you can also use `Class.Frame|Frames` for UI design. For example, as a visual separator between other UI elements, you can scale a frame to be thin and long until it becomes a line. > **Success:** See also [scrolling frames](/docs/en-us/ui/scrolling-frames.md), [viewport frames](/docs/en-us/ui/viewport-frames.md), and [video frames](/docs/en-us/ui/video-frames.md) as specialty frame types with unique built‑in capabilities. ## Clipping By default, `Class.Frame` containers **clip** their content (child `Class.GuiObject|GuiObjects`) through the `Class.Frame.ClipsDescendants|ClipsDescendants` boolean. If you want children to appear outside of a frame's bounds, simply set `Class.Frame.ClipsDescendants|ClipsDescendants` to `false`. Importantly, note that `Class.Frame.ClipsDescendants|ClipsDescendants` does **not** apply if the frame or any of its ancestors have a non‑zero `Class.Frame.Rotation|Rotation`; in such cases, descendants will render outside of the frame's bounds. ## Automatic sizing Frames can be set to [automatically size](/docs/en-us/ui/size-modifiers.md#automatic-sizing) depending on their content. To achieve this, set the frame's `Class.Frame.Size|Size` to the **minimum** width and/or height, then set its `Class.Frame.AutomaticSize|AutomaticSize` property to either `Enum.AutomaticSize|X`, `Enum.AutomaticSize|Y`, or `Enum.AutomaticSize|XY` depending on which axes automatic sizing should apply to. Once set, the frame will automatically resize based on the size of `Class.GuiObject|GuiObjects` and layouts within it. --- title: "Grid and table layouts" url: /docs/en-us/ui/grid-table-layouts last_updated: 2026-06-29T19:34:16Z description: "How to use grid and table layouts for highly structured user interfaces." --- # Grid and table layouts In comparison to `Class.UIListLayout`, `Class.UIGridLayout` and `Class.UITableLayout` allow for more structured and organized layouts. These are most appropriate for interfaces like a shop inventory where each item can be presented in a grid of equally‑sized tiles, or items can be sorted into related rows/columns. _`Class.UIGridLayout`_ _`Class.UITableLayout`_ ## Grid layout `Class.UIGridLayout` positions sibling `Class.GuiObject|GuiObjects` in a grid of uniform cells of the same size within their parent container. Cells are added by row or column based on the layout's `Class.UIGridLayout.FillDirection|FillDirection` until the next cell doesn't fit, then a new row or column begins. For further control, you can use the `Class.UIGridLayout.FillDirectionMaxCells|FillDirectionMaxCells` property to set the maximum number of cells per row or column. By default, `Class.UIGridLayout` positions sibling `Class.GuiObject|GuiObjects` in order of their `Class.GuiObject.LayoutOrder|LayoutOrder` where lower values go before higher values, and equal values sort depending on the order in which you added them. If you change the layout's `Class.UIGridStyleLayout.SortOrder|SortOrder` to `Enum.SortOrder.Name`, siblings sort in alphabetical order. > **Warning:** Once you insert a `Class.UIGridLayout`, it either overrides or influences the `Class.GuiObject.Position|Position` and/or `Class.GuiObject.Size|Size` of all sibling UI objects, so changes to those properties within the [Properties](/docs/en-us/studio/properties.md) window or within a script will not have the normal effect. > **Info:**`Class.UIGridLayout` respects any [constraints](/docs/en-us/ui/size-modifiers.md) you place on objects within the grid. For example, if an object has a `Class.UISizeConstraint` with a `Class.UISizeConstraint.MinSize|MinSize` property that is higher than the grid's `Class.UIGridLayout.CellSize|CellSize` property, the object will span multiple cells. ## Table layout `Class.UITableLayout` positions sibling `Class.GuiObject|GuiObjects` and their children into table format. The default `Class.UITableLayout.FillDirection|FillDirection` of `Enum.FillDirection|Vertical` means that siblings are first positioned into rows, and children of those siblings are positioned horizontally to form columns, such that each cell within a row has the same height and each cell within a column has the same width. This pattern mimics the standard HTML row/column structure: ```html
Label 1 Label 2 Label 3
Label 4 Label 5 Label 6
Label 7 Label 8 Label 9
``` Unless you enable the layout's `Class.UITableLayout.FillEmptySpaceColumns|FillEmptySpaceColumns` or `Class.UITableLayout.FillEmptySpaceRows|FillEmptySpaceRows` properties, the size of the sibling `Class.GuiObject|GuiObjects` determines the dimensions of the cells. For example, a `Class.TextLabel` of size `Datatype.UDim2.new(0.25, 0, 0.1, 0)` will create a table cell of 25% width and 10% height inside the parent container. > **Warning:** Once you insert a `Class.UITableLayout`, it either overrides or influences the `Class.GuiObject.Position|Position` and/or `Class.GuiObject.Size|Size` of all sibling UI objects, so changes to those properties within the [Properties](/docs/en-us/studio/properties.md) window or within a script will not have the normal effect. > **Info:**`Class.UITableLayout` respects and is responsive to any `Class.UISizeConstraint` you place on objects within the table. For example, if an object has a `Class.UISizeConstraint` with a `Class.UISizeConstraint.MinSize|MinSize` property within a header cell, the rest of the cells within the column will be the same size as the header cell. > > Note that if the `Class.UISizeConstraint.MaxSize|MaxSize` property restricts a cell's size from filling the allotted space of a row or column that is wider than the cell, the object aligns to the top‑left of the cell.
--- title: "In-experience UI containers" url: /docs/en-us/ui/in-experience-containers last_updated: 2026-06-29T19:34:16Z description: "In-experience UI containers hold SurfaceGuis, BillboardGuis, and GuiObjects that you want to display in the 3D space." --- # In-experience UI containers In-experience UI containers hold `Class.GuiObject|GuiObjects` that you want to display within your experience's 3D world. - A `Class.SurfaceGui` allows for the rendering of UI objects onto a part's surface in the 3D world while also allowing for basic user interaction to occur. - A `Class.BillboardGui` is a container for UI objects to appear in the 3D space but always face the camera. ## Surface UI Similar to `Class.Decal|Decals` and `Class.Texture|Textures`, UI objects such as `Class.TextLabel|TextLabels` and `Class.ImageLabel|ImageLabels` parented to a `Class.SurfaceGui` face the same direction as the surface they're on, editable through the `Class.SurfaceGui.Face` property. ![SurfaceGui on a 3D part in the place with an ImageLabel child to depict a screen console.](../assets/ui/in-experience/SurfaceGui-Diagram.jpg) To apply a `Class.SurfaceGui` to an in-experience `Class.BasePart`, simply parent it to that part and set the `Class.SurfaceGui.Face` property. Child UI objects then appear on that face of the parent part. Alternatively, you can place the `Class.SurfaceGui` inside a container like `Class.StarterGui` and then set its `Class.SurfaceGui.Adornee|Adornee` property to any `Class.BasePart`, as well as the target `Class.SurfaceGui.Face|Face`. Setting `Class.SurfaceGui.Adornee|Adornee` overrides direct parent association, allowing for more flexibility in placement since it can be set from a script during runtime. > **Warning:** Interactive UI elements like `Class.ImageButton|ImageButtons` and `Class.TextButton|TextButtons` inside a `Class.SurfaceGui` will only receive user input if they are parented to the player's `Class.PlayerGui`, typically via placing the `Class.SurfaceGui` inside `Class.StarterGui` as noted above. Additionally, the part's `Class.BasePart.CanQuery|CanQuery` property must be `true` for the interactive UI element to receive input. > **Success:** To assist in choosing the correct face for a `Class.SurfaceGui`, right-click the target part and select **Show Orientation Indicator**. This displays a blue circle with an **F** and a line attached to the object's `Enum.NormalId.Front|Front` face, and a green arrow that points in the direction of the object's `Enum.NormalId.Top|Top` face. ### Size and position The "canvas" of a `Class.SurfaceGui` occupies the entire `Class.SurfaceGui.Face|Face` of the parent or `Class.SurfaceGui.Adornee|Adornee` part. As a best practice, it's recommended that you use **scale** values for the [size](/docs/en-us/ui/position-and-size.md#size) and [position](/docs/en-us/ui/position-and-size.md#position) of child UI objects like `Class.ImageLabel|ImageLabels`. You can also apply a `Class.UIAspectRatioConstraint` to children of the `Class.SurfaceGui` to maintain their desired aspect ratio regardless of the face's size. ### Occlusion mode The `Class.SurfaceGui.AlwaysOnTop|AlwaysOnTop` property determines whether the `Class.SurfaceGui` will render over top of 3D content or be occluded by it. When set to `false` (default), the `Class.SurfaceGui` renders like other 3D content and is occluded by other 3D objects. When set to `true`, it always renders over top of 3D content and it is not influenced by [brightness/light](#brightness-and-light-influence) in the 3D environment. ### Brightness and light influence `Class.SurfaceGui.Brightness|Brightness` and `Class.SurfaceGui.LightInfluence|LightInfluence` work in conjunction to determine how environmental light affects the UI content of the `Class.SurfaceGui`. `Class.SurfaceGui.Brightness|Brightness` determines the factor by which emitted light is scaled in a range of `0` to `1000`, letting you match the `Class.SurfaceGui` to its environment. For instance, a video billboard can be brightened inside a dark room by increasing `Class.SurfaceGui.Brightness|Brightness` to `10`. `Class.SurfaceGui.LightInfluence|LightInfluence` controls how much the `Class.SurfaceGui` is influenced by lighting in the place, in a range from `0` to `1`. Setting this to `1` means that surrounding lighting has complete control over the appearance, while setting it to `0` means that the lighting has no effect. #### Brightness = 0 #### Brightness = 10 > **Warning:** Note that `Class.SurfaceGui.Brightness|Brightness` is inaccessible in Studio and has no effect when either `Class.SurfaceGui.LightInfluence|LightInfluence` is `1` or `Class.SurfaceGui.AlwaysOnTop|AlwaysOnTop` is `true`. ### Distance visibility `Class.SurfaceGui.MaxDistance|MaxDistance` controls how far from the camera the `Class.SurfaceGui` will be displayed before it stops rendering. A value of `0` means there is no limit and it will render infinitely far away. The default value of `1000` works fine for most cases. For `Class.SurfaceGui|SurfaceGuis` that appear outdoors, it's recommended that `Class.SurfaceGui.MaxDistance|MaxDistance` is high enough to ensure that the container's UI is sufficiently small on the screen when it appears or disappears, minimizing the sudden pop‑in/out effect. ### Display order If multiple `Class.SurfaceGui` containers exist on the same face, you can layer them by Z‑index through their `Class.SurfaceGui.ZOffset|ZOffset` property (changing this does not visually "lift" or "sink" the container from the surface). ## Billboard UI The `Class.BillboardGui` container displays UI objects in the 3D space but, unlike `Class.SurfaceGui`, children of a `Class.BillboardGui` always face the camera. This container is useful for displaying front‑facing info above in‑experience objects such as health meters or names above player characters, markers to guide players to an object in the 3D world, and more. ![BillboardGui with a TextLabel describing the screen console it floats above.](../assets/ui/in-experience/BillboardGui-Diagram.jpg) To link a `Class.BillboardGui` to an in-experience `Class.BasePart` or `Class.Attachment`, simply parent it to that part or attachment and, if desired, adjust its [size/position](#size-and-position-1). Alternatively, you can place the `Class.BillboardGui` inside a container like `Class.StarterGui` and then set its `Class.BillboardGui.Adornee|Adornee` property to any `Class.BasePart` or `Class.Attachment`. This method offers more flexibility in placement since `Class.BillboardGui.Adornee|Adornee` can be set from a script during runtime, for example to place a `Class.BillboardGui` over the head of player characters during runtime. > **Warning:** Interactive UI elements like `Class.ImageButton|ImageButtons` and `Class.TextButton|TextButtons` inside a `Class.BillboardGui` will only receive user input if they are parented to the player's `Class.PlayerGui`, typically via placing the `Class.BillboardGui` inside `Class.StarterGui` as noted above. ### Size and position For billboard sizing, the [scale](/docs/en-us/ui/position-and-size.md#size) components of the `Class.BillboardGui.Size|Size` property set the billboard's stud size in 3D space. For example, a setting of `{10, 0},{2, 0}` (`Datatype.UDim2.fromScale(10, 2)`) forms a billboard with a 10:2 aspect ratio that scales larger or smaller depending on its distance from the camera. For positioning, the `Class.BillboardGui.StudsOffset|StudsOffset` property shifts the billboard canvas on the **X** axis (left/right), **Y** axis (up/down), and **Z** axis (forward/back) relative to the camera. > **Success:** When creating a size-scaled `Class.BillboardGui` that contains a `Class.TextLabel`, it's useful to enable the label's `Class.TextLabel.TextScaled|TextScaled` property so that its text scales along with the billboard canvas as the camera distance changes. ### Occlusion mode The `Class.BillboardGui.AlwaysOnTop|AlwaysOnTop` property determines whether the `Class.BillboardGui` will render over top of 3D content or be occluded by it. When set to `false` (default), the `Class.BillboardGui` renders like other 3D content and is occluded by other 3D objects. When set to `true`, it always renders over top of 3D content and it is not influenced by [brightness/light](#brightness-and-light-influence-1) in the 3D environment. ### Brightness and light influence `Class.BillboardGui.Brightness|Brightness` and `Class.BillboardGui.LightInfluence|LightInfluence` work in conjunction to determine how environmental light affects the UI content of the `Class.BillboardGui`. `Class.BillboardGui.Brightness|Brightness` determines the factor by which emitted light is scaled in a range of `0` to `1000`, letting you match the `Class.BillboardGui` to its environment. For instance, a video billboard can be brightened inside a dark room by increasing `Class.BillboardGui.Brightness|Brightness` to `10`. `Class.BillboardGui.LightInfluence|LightInfluence` controls how much the `Class.BillboardGui` is influenced by lighting in the place, in a range from `0` to `1`. Setting this to `1` means that surrounding lighting has complete control over the appearance, while setting it to `0` means that the lighting has no effect. #### LightInfluence = 1 #### LightInfluence = 0 > **Warning:** Note that `Class.BillboardGui.Brightness|Brightness` is inaccessible in Studio and has no effect when either `Class.BillboardGui.LightInfluence|LightInfluence` is `1` or `Class.BillboardGui.AlwaysOnTop|AlwaysOnTop` is `true`. ### Distance visibility `Class.BillboardGui.MaxDistance|MaxDistance` controls how far from the camera the `Class.BillboardGui` will be displayed before it stops rendering. A value of `0` or `inf` (default) means there is no limit and it will render infinitely far away. --- title: "User interface" url: /docs/en-us/ui last_updated: 2026-06-29T19:34:16Z description: "Explore the wide variety of user interface elements that players can interact with." --- # User interface You can quickly create high-quality graphical user interfaces with minimal scripting requirements using built-in [UI objects](#ui-objects). Depending on where you create it, UI renders either [on-screen](#on-screen-ui) or [within an experience's 3D world](#in-experience-ui). ## On-screen UI [On-screen containers](/docs/en-us/ui/on-screen-containers.md) hold UI objects that you want to display on a player's screen, including [frames](/docs/en-us/ui/frames.md), [labels](/docs/en-us/ui/labels.md), [buttons](/docs/en-us/ui/buttons.md), and more. All on-screen UI objects and code are stored and changed on the client. ![Example ScreenGui with various GuiObject children, including a Frame, TextLabel, TextBox, and ImageButton.](../assets/ui/ui-objects/ScreenGui-Example.jpg) ## In-experience UI [In-experience containers](/docs/en-us/ui/in-experience-containers.md) such as `Class.SurfaceGui|SurfaceGuis` and `Class.BillboardGui|BillboardGuis` hold UI objects that you want to display within your experience's 3D world. ## UI objects Most UI elements are `Class.GuiObject|GuiObjects`, 2D graphical user interface objects that you can parent to containers. The four most common are [frames](/docs/en-us/ui/frames.md), [labels](/docs/en-us/ui/labels.md), [buttons](/docs/en-us/ui/buttons.md), and [text input](/docs/en-us/ui/text-input.md) objects. | Object | Description | | --- | --- | | [Frame](/docs/en-us/ui/frames.md) | `Class.Frame\|Frames` act as containers for other UI objects. When you manipulate frames, you also manipulate the objects they contain. | | [Label](/docs/en-us/ui/labels.md) | `Class.TextLabel` and `Class.ImageLabel` objects allow you to display customizable text and images. | | [Button](/docs/en-us/ui/buttons.md) | `Class.TextButton` and `Class.ImageButton` objects allow users to prompt an action. | | [Text Input](/docs/en-us/ui/text-input.md) | `Class.TextBox` objects allow users to input text. | Using the `Class.GuiObject.Position|Position`, `Class.GuiObject.Size|Size`, `Class.GuiObject.AnchorPoint|AnchorPoint`, and `Class.GuiObject.ZIndex|ZIndex` properties, you have complete control on how to [position](/docs/en-us/ui/position-and-size.md#position), [size](/docs/en-us/ui/position-and-size.md#size), and [layer](/docs/en-us/ui/position-and-size.md#zindex) `Class.GuiObject|GuiObjects`. You can also use [tweening](/docs/en-us/ui/animation.md) to transition a `Class.GuiObject` smoothly from one state to another and provide dynamic visual feedback. ## Layout and design Beyond basic properties for adjusting position and size, Roblox also provides layout structures like [list/flex](/docs/en-us/ui/list-flex-layouts.md) and [grid](/docs/en-us/ui/grid-table-layouts.md), as well as [size modifiers](/docs/en-us/ui/size-modifiers.md) and [appearance modifiers](/docs/en-us/ui/appearance-modifiers.md). _`Class.UIListLayout` for horizontal or vertical item sequences_ _`Class.UIStroke` on `Class.TextLabel`_ _`Class.UIStroke` with `Class.UIGradient`_ ## Universal styling [Universal styling](/docs/en-us/styling.md) is a Roblox solution to stylesheets, similar to CSS, that lets you declare and globally apply overrides to UI instance properties. This engine‑level support is the foundation for the [Style Editor](/docs/en-us/styling/editor.md) and the end‑to‑end token pipeline. ## Interactive frameworks In addition to the core [user interface objects](#ui-objects), the following frameworks provide built‑in and customizable interactivity for your experiences. ### Proximity prompts [Proximity prompts](/docs/en-us/ui/proximity-prompts.md) are unique built-in UI objects which prompt user interaction to trigger an action when they approach in-experience objects such as doors, light switches, and buttons. ### UI drag detectors [UI drag detectors](/docs/en-us/ui/ui-drag-detectors.md) facilitate and encourage interaction with 2D user interface elements such as sliders, spinners, and more. ### 3D drag detectors [3D drag detectors](/docs/en-us/ui/3D-drag-detectors.md) encourage physical interaction with 3D objects in an experience, such as opening doors and drawers, sliding a part around, grabbing and tossing a bowling ball, pulling back and firing a slingshot, and much more. --- title: "Text & image labels" url: /docs/en-us/ui/labels last_updated: 2026-06-29T19:34:16Z description: "Explore how labels display customizable text and images in user interfaces." --- # Text & image labels **Labels** are `Class.GuiObject|GuiObjects` that let you display customizable text and images [on‑screen](/docs/en-us/ui/on-screen-containers.md) or [in‑experience](/docs/en-us/ui/in-experience-containers.md). There are two types of labels: - A `Class.TextLabel` is a rectangle with text that you can style through customizable properties. This is the primary way to display text in an experience. - An `Class.ImageLabel` is a rectangle with an image [asset](/docs/en-us/projects/assets.md) that you can style through customizable properties. Alongside [textures and decals](/docs/en-us/parts/textures-decals.md), this lets you display images in an experience. ## Create labels on the screen Labels on a screen are useful for things like displaying images of characters with dialog. To add a label to a screen: 1. In the **Explorer** window, select **StarterGui** and add a **ScreenGui**. 1. Hover over StarterGui and click the ⊕ button. A contextual menu displays. 2. From the menu, insert a **ScreenGui**. 2. Select the new **ScreenGui** and add a label. 1. Hover over **ScreenGui** and click the ⊕ button. A contextual menu displays. 2. From the menu, insert a **TextLabel** or **ImageLabel**. ## Create labels on part faces Labels on a part are useful for things like creating billboards, posters, and wallpaper. To add a label to the face of a part: 1. In the **Explorer** window, insert a **SurfaceGui** onto the part. 1. Hover over the part instance and click the ⊕ button. A contextual menu displays. 2. From the menu, insert a **SurfaceGui**. 2. Select the new **SurfaceGui** and add a label. 1. Hover over the **SurfaceGui** and click the ⊕ button. A contextual menu displays. 2. From the menu, insert a **TextLabel** or **ImageLabel**. > **Warning:** If you don't see the button, try [choosing a different face](/docs/en-us/parts/textures-decals.md#choose-a-face) in the **Face** property of the **SurfaceGui**. ## Adjust ImageLabel properties All images within an `Class.ImageLabel` must be assets that have been [imported](/docs/en-us/projects/assets/manager.md#asset-import) to Studio. While the image automatically scales to fit the area of the rectangle, the image looks best when it displays at its native resolution. You can customize the visual appearance of an image with the following properties: - `Class.ImageLabel.ImageColor3|ImageColor3` - `Class.ImageLabel.ImageTransparency|ImageTransparency` - `Class.GuiObject.BackgroundTransparency|BackgroundTransparency` For example, to display only the image and hide the rectangle, set the `Class.GuiObject.BackgroundTransparency|BackgroundTransparency` property to `1`. ## Text filtering Applied to various sources and inputs, [text filtering](/docs/en-us/ui/text-filtering.md) prevents users from seeing inappropriate language and personally identifiable information such as phone numbers. Roblox automatically filters common text outputs such as messages that have passed through [in-experience text chat](/docs/en-us/chat/in-experience-text-chat.md), but **you are responsible for filtering any displayed text that you don't have explicit control over**, including characters/strings that are displayed on text labels. > **Error:** Because filtering is crucial for a safe environment, Roblox actively moderates the content of experiences to make sure they meet [community standards](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards). If Roblox receives reports or automatically detects that your experience doesn't apply text filtering, then the system removes the experience until you add filtering. --- title: "List and flex layouts" url: /docs/en-us/ui/list-flex-layouts last_updated: 2026-06-29T19:34:16Z description: "How to use list layouts and flex settings for highly responsive user interfaces." --- # List and flex layouts The `Class.UIListLayout` positions sibling `Class.GuiObject|GuiObjects` into horizontal rows or vertical columns within their parent container. Whenever you add or remove a sibling object, the layout adjusts accordingly. > **Warning:** Once you insert a `Class.UIListLayout`, it either overrides or influences the `Class.GuiObject.Position|Position`, `Class.GuiObject.Rotation|Rotation`, and/or `Class.GuiObject.Size|Size` of all sibling UI objects, so changes to those properties within the [Properties](/docs/en-us/studio/properties.md) window or within a script will not have the normal effect. ## Fill direction The `Class.UIListLayout.FillDirection|FillDirection` property determines the direction in which the list layout's siblings will render. ![UIListLayouts illustrating FillDirection of either horizontal or vertical.](../assets/engine-api/classes/UIListLayout/FillDirection.png) Ordering is determined by the layout's `Class.UIListLayout.SortOrder|SortOrder` property which can be either **ascending numeric**, based on each item's `Class.GuiObject.LayoutOrder|LayoutOrder` integer value, or **alphanumeric** based on the item's `Class.Instance.Name|Name`. ![List layout examples illustrating numerical LayoutOrder sorting or alphanumerical Name sorting.](../assets/engine-api/classes/UIListLayout/SortOrder.png) > **Info:** To reverse elements, such as right-to-left ordering in a horizontal list, you'll need to reverse the sorting. For example, if four items are ordered by `Class.GuiObject.LayoutOrder|LayoutOrder` of `0`, `1`, `2`, `3` respectively, negate each value to form an order of `0`, `-1`, `-2`, `-3`. ## Alignment The `Class.UIListLayout.HorizontalAlignment|HorizontalAlignment` and `Class.UIListLayout.VerticalAlignment|VerticalAlignment` properties determine the respective **X** and **Y** alignment of both the list's siblings in relation to each other and the list's overall bounds within its container. For example, a horizontally-filled list with `Class.UIListLayout.VerticalAlignment|VerticalAlignment` of `Enum.VerticalAlignment|Center` centrally aligns the list's siblings with each other **and** centers the list vertically in its container. ![List layout illustrating VerticalAlignment of Center.](../assets/engine-api/classes/UIListLayout/VerticalAlignment-Center.png) > **Info:**`Class.UIListLayout.HorizontalAlignment|HorizontalAlignment` and `Class.UIListLayout.VerticalAlignment|VerticalAlignment` differ slightly from [item line alignment](#item-line-alignment) in a [flex](#flex-layouts) layout, with which you can align items in the same line without applying the same alignment to the list's overall bounds in its container. ## Wrapping The `Class.UIListLayout.Wraps|Wraps` boolean controls whether siblings within the parent container wrap to another line when their default size exceeds the width/height of the container's bounds. ![Diagram showing how Wraps affects how siblings are distributed within the parent container's bounds.](../assets/engine-api/classes/UIListLayout/Wraps.png) ## Padding The `Class.UIListLayout.Padding|Padding` property determines the amount of empty space **between** each list item, set to either a scale (percentage of the container's size in the current direction) or an offset (static spacing value similar to pixel size). > **Info:** This property does not apply padding **around** the list's overall bounds; see `Class.UIPadding` to apply top, bottom, left, and/or right padding to the contents of the parent. ## Flex layouts Integrating **flex** into a `Class.UIListLayout` is a powerful way to [equally fill/distribute](#equal-fill-or-distribution) or [align/stretch](#item-line-alignment) list items across their line, or [flex specific items](#flex-individual-items) across a variable space. > **Warning:** While flex is a powerful tool, you should not apply it to a `Class.UIListLayout` without purpose, as it adds a slight performance cost above non‑flex, especially when resizing the layout or dynamically adding/removing flex items. > > Additionally, a [grid layout](/docs/en-us/ui/grid-table-layouts.md#grid-layout) may be preferable when items should strictly align to a grid in both the **X** and **Y** directions, since grid layouts enforce a consistent cell size while flex layouts may use a variable number of items per line. ### Equal fill or distribution When the list layout's [fill direction](#fill-direction) is set to `Enum.FillDirection|Horizontal`, the `Class.UIListLayout.HorizontalFlex|HorizontalFlex` property specifies how to distribute extra horizontal space in the parent container. Similarly, when the fill direction is set to `Enum.FillDirection|Vertical`, the `Class.UIListLayout.VerticalFlex|VerticalFlex` property specifies how to distribute extra vertical space. #### HorizontalFlex ![UIListLayout examples showing how each HorizontalFlex option affects the size and spacing of sibling UI objects.](../assets/engine-api/classes/UIListLayout/HorizontalFlex-Options.png) #### VerticalFlex ![UIListLayout examples showing how each VerticalFlex option affects the size and spacing of sibling UI objects.](../assets/engine-api/classes/UIListLayout/VerticalFlex-Options.png) One practical use of `Class.UIListLayout.HorizontalFlex|HorizontalFlex` is a **tabbed window** interface where flex fills the tab bar equally, regardless of the number of tabs. No approach is easier than flex in this case, because it automatically calculates the width of each tab without developer‑defined width settings, and it automatically adjusts if the number of tabs changes. #### 3 Tabs #### 6 Tabs ### Item line alignment The `Class.UIListLayout.ItemLineAlignment|ItemLineAlignment` property defines the **cross‑directional** alignment of siblings within a line, letting you align objects of different widths/heights or make objects of lesser width/height fill their entire line. ![Examples of options for ItemLineAlignment in a horizontal fill direction.](../assets/engine-api/classes/UIListLayout/ItemLineAlignment.png) One practical use case for `Class.UIListLayout.ItemLineAlignment|ItemLineAlignment` is to stretch a series of inconsistently sized tiles to fill their entire row (`Enum.ItemLineAlignment|Stretch`), making the layout more cohesive. #### Uneven Layout _Tiles with varying content heights result in an uneven and staggered layout_ #### Filled Lines _Tiles stretched to fill their line result in a more even and consistent layout_ ### Flex individual items While flexing an entire layout is a powerful utility, certain layouts are more suited to **individual item flexing**. In such layouts, some elements in the list maintain their core size while other items flex to fill variable spaces. One practical usage is a slider bar widget with uniform [labels](/docs/en-us/ui/labels.md) on both ends and a flexible slider bar that fills the entire width between. ![Example of UIFlexItem applied to a specific GuiObject under control of a UIListLayout.](../assets/ui/ui-objects/UIFlexItem-Example.png) To set one specific list item as flexible, insert a `Class.UIFlexItem` as a child of the flexible item, then set its `Class.UIFlexItem.FlexMode|FlexMode` property to `Enum.UIFlexMode.Fill|Fill`, `Enum.UIFlexMode.Grow|Grow`, `Enum.UIFlexMode.Shrink|Shrink`, or `Enum.UIFlexMode.Custom|Custom`. ![Example hierarchy of a UIFlexItem parented to a GuiObject under control of a UIListLayout.](../assets/ui/ui-objects/UIFlexItem-Hierarchy.png) --- title: "On-screen UI containers" url: /docs/en-us/ui/on-screen-containers last_updated: 2026-06-29T19:34:16Z description: "Learn how to display UI objects on a user's screen." --- # On-screen UI containers The `Class.ScreenGui` container holds `Class.GuiObject|GuiObjects` to display on a player's screen, including [frames](/docs/en-us/ui/frames.md), [labels](/docs/en-us/ui/labels.md), [buttons](/docs/en-us/ui/buttons.md), and more. All on‑screen UI objects and code are stored and changed on the client. ![Example ScreenGui with various GuiObject children, including a Frame, TextLabel, TextBox, and ImageButton.](../assets/ui/ui-objects/ScreenGui-Example.jpg) > **Info:** For UI containers that hold `Class.GuiObject|GuiObjects` that you want to display **within** the 3D world, such as on the face of a part, see [In-Experience UI Containers](/docs/en-us/ui/in-experience-containers.md). To display a `Class.ScreenGui` and its child `Class.GuiObject|GuiObjects` to every player who joins the experience, place it inside the `Class.StarterGui` container. When a player joins an experience and their character first spawns, the `Class.ScreenGui` and its contents clone into the `Class.PlayerGui` container for that player, located within the `Class.Players` container. ![Diagram of how a ScreenGui clones from StarterGui to a player's PlayerGui](../assets/ui/ui-objects/StarterGui-To-PlayerGui.png) > **Success:** By default, `Class.GuiObject|GuiObjects` inside a `Class.ScreenGui` within `Class.StarterGui` appear as an overlay of the [3D viewport](/docs/en-us/studio/ui-overview.md#3d-viewport), simulating their appearance and position in a running experience. To hide all such screen overlays, toggle off **GUI overlay** from the [Visualization Options](/docs/en-us/studio/ui-overview.md#visualization-options) widget in the upper‑right corner of the 3D viewport. > **Info:** If `Class.Players.CharacterAutoLoads` is disabled, the contents of `Class.StarterGui` will not be cloned until `Class.Player:LoadCharacterAsync()` is called. As an experience grows in scope, you may require multiple screen interfaces such as a title screen, settings menu, shop interface, and more. In such cases, you can place multiple unique `Class.ScreenGui` containers inside `Class.StarterGui` and toggle each container's `Class.ScreenGui.Enabled|Enabled` property depending on whether it should be visible and active (while `false`, contents will not render, process user input, or update in response to changes). ![Explorer hierarchy showing multiple ScreenGui containers, one enabled and the others disabled, in order to control which are visible at a given time.](../assets/ui/ui-objects/ScreenGuis-Enabled.png) The `Class.ScreenGui.Enabled|Enabled` property can be initially toggled through the [Properties](/docs/en-us/studio/properties.md) window and/or you can set it during playtime from a client‑side script by [accessing](#access-player-ui) the player's `Class.PlayerGui` and setting it to `true` or `false` for the desired container(s). > **Info:** When using multiple `Class.ScreenGui` interfaces, you can layer them by Z‑index through their `Class.ScreenGui.DisplayOrder|DisplayOrder` property. See [Display Order](#display-order) for more information. ## Container properties The following properties let you customize the [screen insets](#screen-insets) across multiple devices, the [display order](#display-order) when using multiple screen containers, and more. ### Screen insets Modern phones take advantage of the entire screen but typically include notches, cutouts, and other elements that occupy screen space. Every Roblox experience also includes the **top bar controls** for quick access to the main menu, [chat](/docs/en-us/chat/in-experience-text-chat.md), [leaderboard](/docs/en-us/players/leaderboards.md), and more. ![Mobile device showing Roblox top bar buttons and device cutout.](../../assets/engine-api/enums/ScreenInsets/Top-Bar-Cutout.png) To ensure players can see and access all UI easily and without obstruction, Roblox provides the `Class.ScreenGui.ScreenInsets|ScreenInsets` property which controls the **safe area** insets for the contents of a `Class.ScreenGui`. #### CoreUISafeInsets The default of `Enum.ScreenInsets|CoreUISafeInsets` keeps all descendant `Class.GuiObject|GuiObjects` inside the core UI safe area, clear of the top bar buttons and other screen cutouts. This setting is recommended if the `Class.ScreenGui` contains interactive UI elements. ![Mobile device showing the core UI safe area.](../../assets/engine-api/enums/ScreenInsets/CoreUISafeInsets.png) #### DeviceSafeInsets A setting of `Enum.ScreenInsets|DeviceSafeInsets` guarantees that no descendant `Class.GuiObject|GuiObjects` are occluded by any device screen cutouts such as the camera notch, although no inset is added for Roblox core UI elements like the top bar buttons. ![Mobile device showing the device safe area.](../../assets/engine-api/enums/ScreenInsets/DeviceSafeInsets.png) #### TopbarSafeInsets A setting of `Enum.ScreenInsets|TopbarSafeInsets` keeps all descendant `Class.GuiObject|GuiObjects` between the top bar controls and the right edge of the device safe area. The `Class.ScreenGui` will then automatically flex in horizontal size based on the top bar's content. ![Mobile device showing the top bar safe area within the Roblox controls.](../../assets/engine-api/enums/ScreenInsets/TopbarSafeInsets.png) #### None No insets are added to the fullscreen area. This mode may result in UI that is obscured or completely hidden by device notches and cutouts, so you should only use it for a `Class.ScreenGui` that contains non‑interactive content like background images. ![Mobile device showing the entire screen region with no account for safe areas.](../../assets/engine-api/enums/ScreenInsets/None.png) ### Display order When using multiple `Class.ScreenGui` interfaces, you can layer them by Z‑index through their `Class.ScreenGui.DisplayOrder|DisplayOrder` property. For example, to display a modal settings menu on one `Class.ScreenGui` in front of the experience's main user interface on another `Class.ScreenGui`, assign a higher `Class.ScreenGui.DisplayOrder|DisplayOrder` to the modal's than the underlying interface's. ### Reset on spawn The `Class.ScreenGui.ResetOnSpawn|ResetOnSpawn` boolean property determines if the `Class.ScreenGui` resets (deletes itself and re‑clones into the player's `Class.PlayerGui`) every time the player's character respawns. | Condition | Resets | | --- | --- | | `Class.ScreenGui.ResetOnSpawn\|ResetOnSpawn` is `true` (default). | **YES** | | The `Class.ScreenGui` is an **indirect** descendant of `Class.StarterGui`; for example it's placed inside a `Class.Folder` located within `Class.StarterGui`. | **YES** | | `Class.ScreenGui.ResetOnSpawn\|ResetOnSpawn` is `false` **and** the `Class.ScreenGui` is a **direct** descendant of `Class.StarterGui`. | **NO** | ## Access player UI As noted, parenting a `Class.ScreenGui` to `Class.StarterGui` clones it and its child `Class.GuiObject|GuiObjects` into a player's `Class.PlayerGui` container when they join the experience and their character first spawns. If you need to control a player's UI container during playtime, for example to show/hide a specific `Class.ScreenGui` or any of its children, access it as follows from a `Class.LocalScript`: ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer local playerGui = player.PlayerGui local titleScreen = playerGui:WaitForChild("TitleScreen") local settingsMenu = playerGui:WaitForChild("SettingsMenu") titleScreen.Enabled = false -- Hide title screen settingsMenu.Enabled = true -- Show settings menu ``` ## Disable default UI All Roblox experiences include several UI elements that are enabled by default. If you don't need any of these elements or if you want to replace them with your own creations, you can use the `Class.StarterGui:SetCoreGuiEnabled()|SetCoreGuiEnabled()` method in a client‑side script with the associated `Enum.CoreGuiType` option. | Default UI | Associated enum | | --- | --- | | Dynamically updated `Class.Players` list, commonly used as a [leaderboard](/docs/en-us/players/leaderboards.md). | `Enum.CoreGuiType.PlayerList` | | The character's `Class.Humanoid.Health\|Health` bar. Does not appear if the character's `Class.Humanoid` is at full health. | `Enum.CoreGuiType.Health` | | The character's `Class.Backpack` which contains [in‑experience tools](/docs/en-us/players/tools.md). Does not appear if there are no `Class.Tool\|Tools` in the backpack. | `Enum.CoreGuiType.Backpack` | | The [text chat](/docs/en-us/chat/in-experience-text-chat.md) window. | `Enum.CoreGuiType.Chat` | | Popup menu of character [emotes](/docs/en-us/characters/emotes.md). | `Enum.CoreGuiType.EmotesMenu` | | A window displaying a player's perspective or view of their own character. Does not appear unless the player has enabled **Self View** from the Roblox menu. | `Enum.CoreGuiType.SelfView` | | A **capture screenshot** button along the right side of the screen. Does not appear unless the player has enabled **Captures** from the Roblox menu. | `Enum.CoreGuiType.Captures` | | The **Avatar Switcher** allows users to change their platform avatar. | `Enum.CoreGuiType.AvatarSwitcher` | ![Core UI elements in every Roblox experience.](../../assets/ui/misc/CoreGui-Elements.jpg) ```lua local StarterGui = game:GetService("StarterGui") -- Disable default health bar and backpack StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType.Health, false) StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType.Backpack, false) ``` Additionally, devices with touch capabilities include a virtual thumbstick and a jump button by default. If desired, you can hide these elements by setting `Class.GuiService.TouchControlsEnabled` to `false` in a client‑side script. ![UI elements for touch-capable devices in every Roblox experience](../../assets/ui/misc/TouchGui-Elements.png) ```lua local GuiService = game:GetService("GuiService") GuiService.TouchControlsEnabled = false ``` --- title: "Page layouts" url: /docs/en-us/ui/page-layouts last_updated: 2026-06-29T19:34:16Z description: "Explore how page layouts organize interface content into distinct pages." --- # Page layouts When you parent a `Class.UIPageLayout` to a UI container, every sibling `Class.GuiObject` becomes a unique page that you can transition to through scripting. This layout is useful when you want to create user interfaces such as tabbed modals, tutorials, or character customization screens. After you create multiple pages within the `Class.UIPageLayout`, you need to use scripting to transition from page to page. For example, the following code, pasted into a client‑side sibling `Class.Script` of the layout, transitions forward and then backward between the pages every two seconds. ```lua local frame = script.Parent local pageLayout = frame:FindFirstChildWhichIsA("UIPageLayout") task.wait(2) pageLayout:Next() task.wait(2) pageLayout:Next() task.wait(2) pageLayout:Previous() task.wait(2) pageLayout:Previous() ``` If you want to view pages while editing in Studio, you can use the [Command Bar](/docs/en-us/studio/ui-overview.md#command-bar) to navigate from one page to another, letting you review where you need to make changes without having to play your experience each time. 1. In the [Explorer](/docs/en-us/studio/explorer.md) window hierarchy, select the `Class.UIPageLayout` object. 2. Open the [Command Bar](/docs/en-us/studio/ui-overview.md#command-bar). 3. Input any of the following commands and press `Enter`. | Action | Command | | --- | --- | | **Next Page** | `game:GetService("Selection"):Get()[1]:Next()` | | **Previous Page** | `game:GetService("Selection"):Get()[1]:Previous()` | | **First Page** | `game:GetService("Selection"):Get()[1]:JumpToIndex(0)` | --- title: "Position and size UI objects" url: /docs/en-us/ui/position-and-size last_updated: 2026-06-29T19:34:16Z description: "Explains the process of positioning, sizing, and ordering UI objects on a player's screen." --- # Position and size UI objects Unless UI objects are under control of a [layout structure](#layout-structures) or a [size modifier/constraint](/docs/en-us/ui/size-modifiers.md), you have complete control over their [position](#position) and [size](#size). You can also set the Z‑index [layering](#zindex) order in which objects overlap. ## Core properties All `Class.GuiObject|GuiObjects` share a core set of properties to [position](#position), [size](#size), [anchor](#anchorpoint), and [layer](#zindex) them within an on‑screen or in‑experience container. ### Position The `Class.GuiObject.Position|Position` property is a `Datatype.UDim2` coordinate set that positions the object along the **X** and **Y** axes. A `Datatype.UDim2` is represented by both `Datatype.UDim.Scale|Scale` and `Datatype.UDim.Offset|Offset` values for each axis: - `Datatype.UDim.Scale|Scale` values represent a **percentage** of the container's size along the corresponding axis, additive of any `Datatype.UDim.Offset|Offset` values. - `Datatype.UDim.Offset|Offset` values represent how many **pixels** to shift the object on the corresponding axis, additive of any `Datatype.UDim.Scale|Scale` values. To edit the position of a selected `Class.GuiObject`, click the **Position** field in the [Properties](/docs/en-us/studio/properties.md) window and enter a new `Datatype.UDim2` coordinate set. > **Success:** Brackets and spaces are **optional** when entering a `Datatype.UDim2` in Studio — you can simply enter the four values separated by commas, for instance `0.25,40,0.1,20`, and Studio will infer the intended value set. > > Studio also infers the intended value set when a single number is entered. For example, entering simply `0.5` will be converted to`{0.5, 0},{0.5, 0}` since `0.5` is likely a **Scale** value of 50%. Conversely, entering `20` will be converted to`{0, 20},{0, 20}` since `20` is likely an **Offset** value of 20 pixels and not a scale of 2000%. ### Size The `Class.GuiObject.Size|Size` property is a `Datatype.UDim2` coordinate set that sizes the object along the **X** and **Y** axes. A `Datatype.UDim2` is represented by both `Datatype.UDim.Scale|Scale` and `Datatype.UDim.Offset|Offset` values for each axis: - **Scale** — Values that represent a **percentage** of the container's size along the corresponding axis, additive to any **Offset** values. - **Offset** — Values that represent the object's **pixel** size along the corresponding axis, additive to any **Scale** values. To edit the size of a selected `Class.GuiObject`, click the **Size** field in the [Properties](/docs/en-us/studio/properties.md) window and enter a new `Datatype.UDim2` coordinate set. > **Success:** Brackets and spaces are **optional** when entering a `Datatype.UDim2` in Studio — you can simply enter the four values separated by commas, for instance `0.75,0,0.25,0`, and Studio will infer the intended value set. > > Studio also infers the intended value set when a single number is entered. For example, entering simply `0.5` will be converted to`{0.5, 0},{0.5, 0}` since `0.5` is likely a **Scale** value of 50%. Conversely, entering `20` will be converted to`{0, 20},{0, 20}` since `20` is likely an **Offset** value of 20 pixels and not a scale of 2000%. ### AnchorPoint The `Class.GuiObject.AnchorPoint|AnchorPoint` property defines the **origin point** from where the object changes [position](#position) and [size](#size). The default `Class.GuiObject.AnchorPoint|AnchorPoint` values are `(0, 0)` which places the anchor in the top‑left corner of the object. `Class.GuiObject.AnchorPoint|AnchorPoint` values are a **fraction** from `0` to `1`, relative to the [size](#size) of the object, meaning an object with `Class.GuiObject.AnchorPoint|AnchorPoint` values of `(0.5, 0.5)` places the anchor point halfway (50%) through the object both horizontally and vertically, and any changes to its [position](#position) or [size](#size) both move and scale outward from this point. To view and edit the anchor point of a selected `Class.GuiObject`: 1. In the [Properties](/docs/en-us/studio/properties.md) window, click inside the **AnchorPoint** field. 2. Enter a new `Datatype.Vector2` coordinate and press `Enter`. > **Success:** Studio infers the intended value pair when a single number is entered. For example, entering simply `0.5` will be converted to`0.5, 0.5` for a center anchor point. ### ZIndex The `Class.GuiObject.ZIndex|ZIndex` property defines the layer order in which `Class.GuiObject|GuiObjects` render and overlap each other. If you want to create new rendering layers, you must set the `Class.GuiObject.ZIndex|ZIndex` property to different positive or negative integer values for each object. For UI containers like `Class.ScreenGui`, the default `Class.ScreenGui.ZIndexBehavior|ZIndexBehavior` always renders children above their parents, and each child's `Class.GuiObject.ZIndex|ZIndex` is used to decide the order in which it renders over others. To edit an object's `Class.GuiObject.ZIndex|ZIndex`, locate **ZIndex** in the [Properties](/docs/en-us/studio/properties.md) window and enter a new integer value. ## Layout structures Layout structures let you quickly organize and display `Class.GuiObject|GuiObjects`, for example into a horizontal or vertical [list](/docs/en-us/ui/list-flex-layouts.md), a [grid](/docs/en-us/ui/grid-table-layouts.md#grid-layout) of equally‑sized tiles, a [page sequence](/docs/en-us/ui/page-layouts.md), and more. Layouts typically override or influence the [position](#position)/ [size](#size) of objects under their control. | Layout | Description | | --- | --- | | [List](/docs/en-us/ui/grid-table-layouts.md#grid-layout) | `Class.UIListLayout` positions sibling `Class.GuiObject\|GuiObjects` into horizontal rows or vertical columns within their parent container. | | [Grid](/docs/en-us/ui/grid-table-layouts.md#grid-layout) | `Class.UIGridLayout` positions sibling `Class.GuiObject\|GuiObjects` in a grid of uniform cells of the same size within their parent container. | | [Table](/docs/en-us/ui/grid-table-layouts.md#table-layout) | `Class.UITableLayout` positions sibling `Class.GuiObject\|GuiObjects` and their children into table format. | | [Page](/docs/en-us/ui/page-layouts.md) | `Class.UIPageLayout` organizes its sibling `Class.GuiObject\|GuiObjects` into unique pages that you can transition to through scripting. | ## Cross-platform factors Roblox is inherently [cross‑platform](/docs/en-us/projects/cross-platform.md), as players can discover and join experiences on their phone or tablet, then later continue where they left off on their PC or console. You should design your Roblox experiences to be accessible and enjoyable on **all** platforms that you choose to support, instead of optimizing for one platform and neglecting others. ### Reserved zones On mobile devices, the default controls occupy a portion of the bottom-left and bottom-right corners of the screen. When you design an experience's UI, avoid placing important info or virtual buttons in these zones. ### Thumb zones Most mobile players use two thumbs — one on the virtual thumbstick and one on the jump button. Depending on the physical size of the device and the player's hands, reaching too far from the bottom corners becomes uncomfortable or impossible, so you should avoid placing frequently‑used buttons outside of easy‑to‑reach zones. > **Success:** Button comfortably within reach of player's right thumb > **Warning:** Button difficult to reach unless player stretches hand or thumb Remember that comfortable thumb zones differ between phones and tablets because tablets have a larger screen. A button placed 40% below the screen's top edge is reachable on a phone but almost unreachable on a tablet. > **Success:** Button 40% from top edge of phone, within reach of player's thumb > **Warning:** Button 40% from top edge of tablet, difficult to reach without stretching A reliable approach on both phones and tablets is **relative** positioning of custom buttons near frequently‑used controls like the default jump button, placing them within easy reach. The following code, placed in a client-side script within `Class.StarterPlayerScripts`, fetches the position of the jump button and creates a placeholder [button](/docs/en-us/ui/buttons.md) 20 pixels to its left. ```lua local Players = game:GetService("Players") local UserInputService = game:GetService("UserInputService") local player = Players.LocalPlayer local playerGui = player:WaitForChild("PlayerGui") if UserInputService.TouchEnabled then -- Wait for jump button to be fully loaded while not (playerGui:FindFirstChild("JumpButton", true) and playerGui:FindFirstChild("JumpButton", true).IsLoaded) do task.wait() end local jumpButton = playerGui:FindFirstChild("JumpButton", true) -- Place new custom button to left of jump button local customButton = Instance.new("ImageButton") customButton.AnchorPoint = Vector2.new(1, 1) customButton.Size = UDim2.fromOffset(jumpButton.Size.X.Offset * 0.8, jumpButton.Size.Y.Offset * 0.8) customButton.Position = jumpButton.Position + UDim2.fromOffset(-20, jumpButton.Size.Y.Offset) customButton.Parent = jumpButton.Parent else warn("Device is not touch-enabled or Studio is not emulating a touch-enabled device!") end ``` ### Context-based UI Screen space is limited on mobile devices, so you should show only the most vital information during active gameplay. For example, if your experience includes a special input action to open doors and treasure chests, it doesn't make sense to constantly show an "open" button on the screen. Instead, use a [proximity prompt](/docs/en-us/ui/proximity-prompts.md) or similar method to accept input only when the character approaches a door or chest. _Custom button that you display only when character is near a door or chest_ --- title: "Proximity prompts" url: /docs/en-us/ui/proximity-prompts last_updated: 2026-06-29T19:34:16Z description: "Proximity Prompts allow users to trigger actions when they approach objects in the 3D space." --- # Proximity prompts `Class.ProximityPrompt` objects encourage user interaction to trigger an action when they approach in-experience objects such as doors, light switches, and buttons. Using this object, you can: - Indicate what objects a user can interact with in the experience. - Display the action a user can take on the object, then trigger the action through user input such as pressing or holding a key. - Display the correct input for all input types, such a keyboard, gamepad, and touchscreen keys. > **Info:** See the [Dungeon Delve](https://www.roblox.com/games/6074153281/Dungeon-Delve) sample place for the fully working proximity prompt examples shown in the video above. ## Create proximity prompts You must parent proximity prompts to the part, model, or attachment that you want a user to interact with. To add a proximity prompt to a `Class.BasePart`, `Class.Model`, or `Class.Attachment` object: 1. In the [Explorer](/docs/en-us/studio/explorer.md) window, hover over the `Class.BasePart`, `Class.Model`, or `Class.Attachment` and click the ⊕ button. A contextual menu displays. 2. From the menu, insert a **ProximityPrompt**.![Explorer hierarchy showing a ProximityPrompt parented to an Attachment](../assets/ui/proximity-prompt/ProximityPrompt-New.png) ## Customize proximity prompts You can customize a proximity prompt based on how you want it to [appear](#appearance), when you want it to be [visible](#visibility), and what you want a user to do to [trigger the action](#interactivity). ### Appearance Proximity prompts need to communicate three things: - The **object** that a user can interact with. - The **action** that triggers when they interact with the proximity prompt. - The **key** that a user must press or hold. You can specify these through the following properties: - `Class.ProximityPrompt.ObjectText|ObjectText` An optional name for the object a user can interact with. - `Class.ProximityPrompt.ActionText|ActionText` An optional name for the action a user will trigger. - `Class.ProximityPrompt.KeyboardKeyCode|KeyboardKeyCode` The keyboard key a user must press or hold to trigger the action. - `Class.ProximityPrompt.GamepadKeyCode|GamepadKeyCode` The gamepad key a user must press or hold to trigger the action.![Diagram indicating basic elements of a ProximityPrompt](../assets/ui/proximity-prompt/Prompt-Diagram.png) ### Visibility You can control when the proximity prompt is visible through its `Class.ProximityPrompt.MaxActivationDistance|MaxActivationDistance`, `Class.ProximityPrompt.RequiresLineOfSight|RequiresLineOfSight`, and `Class.ProximityPrompt.Exclusivity|Exclusivity` properties. #### MaxActivationDistance The `Class.ProximityPrompt.MaxActivationDistance|MaxActivationDistance` property allows you to define the range from around the `Class.ProximityPrompt` object that activates the visibility of the proximity prompt. Once a user's character enters that range, the proximity prompt becomes visible. ![Diagram indicating how a character's distance from a ProximityPrompt object affects whether the prompt appears on screen](../assets/ui/proximity-prompt/MaxActivationDistance.jpg) #### RequiresLineOfSight The `Class.ProximityPrompt.RequiresLineOfSight|RequiresLineOfSight` property activates the visibility of the proximity prompt when there's a clear path from the **camera** to the `Class.ProximityPrompt` object. By default, this property is set to true. #### Exclusivity If a user's character is within range of multiple proximity prompts, each proximity prompt's visibility depends on which proximity prompt the camera is pointing at, as well as each proximity prompt's `Class.ProximityPrompt|Exclusivity` property value. #### OnePerButton ![ProximityPrompt objects set to exclusivity of OnePerButton](../assets/ui/proximity-prompt/Exclusivity-OnePerButton.jpg)_Only one proximity prompt is visible per input keycode. If multiple in-range proximity prompts use different keycodes, they are all visible. However, if multiple in-range proximity prompts use the same keycode, only one proximity prompt is visible at any given time, depending on the camera's view direction._ #### OneGlobally ![ProximityPrompt objects set to exclusivity of OneGlobally](../assets/ui/proximity-prompt/Exclusivity-OneGlobally.jpg)_If multiple proximity prompts are set to this exclusivity, only one prompt is visible at any given time, regardless of the character's proximity or the prompt's keycode. The camera's view direction determines which proximity prompt is visible, useful for minimizing prompt distraction when there are several in an area._ #### AlwaysShow ![ProximityPrompt objects set to exclusivity of AlwaysShow](../assets/ui/proximity-prompt/Exclusivity-AlwaysShow.jpg)_Proximity prompts are always visible when in range, assuming no other factors prevent them from being visible. Be cautious when using this setup, as all visible prompts using the same keycode will trigger on a player's input of that keycode._ ### Interactivity You can customize how a user interacts with a proximity prompt through its `Class.ProximityPrompt.HoldDuration|HoldDuration` and `Class.ProximityPrompt.ClickablePrompt|ClickablePrompt` properties. #### HoldDuration The `Class.ProximityPrompt.HoldDuration|HoldDuration` property determines how many seconds a user has to press a key before the proximity prompt's action triggers. If this property has a value of `0`, the proximity prompt's action triggers immediately. #### ClickablePrompt The `Class.ProximityPrompt.ClickablePrompt|ClickablePrompt` property specifies if a user can click on a proximity prompt to trigger its action. When set to **true**, a user can interact with the proximity prompt by directly clicking the proximity prompt **or** by pressing the specified key. When set to **false**, a user can only interact with the proximity prompt by pressing the specified key. > **Warning:** Note that if a user is using a phone or a tablet, they can always interact with the proximity prompt by directly clicking the proximity prompt regardless of the `Class.ProximityPrompt.ClickablePrompt|ClickablePrompt` property's value. ## Script proximity prompts You can connect to proximity prompt events either on the `Class.ProximityPrompt` object itself or globally through `Class.ProximityPromptService`. The `Class.ProximityPromptService` allows you to manage all proximity prompt behavior from one location, preventing any need for duplicate code in your experience. | Event | Description | | --- | --- | | `Class.ProximityPromptService.PromptTriggered\|PromptTriggered` | Fires when a player interacts with a proximity prompt (after the duration for a prompt with non-zero `Class.ProximityPrompt.HoldDuration\|HoldDuration`). | | `Class.ProximityPromptService.PromptTriggerEnded\|PromptTriggerEnded` | Triggers when the player stops interacting with a proximity prompt. | | `Class.ProximityPromptService.PromptButtonHoldBegan\|PromptButtonHoldBegan` | Fires when a player begins interacting with a proximity prompt with a non-zero `Class.ProximityPrompt.HoldDuration\|HoldDuration` value. | | `Class.ProximityPromptService.PromptButtonHoldEnded\|PromptButtonHoldEnded` | Fires when a player stops interacting with a proximity prompt with a non-zero `Class.ProximityPrompt.HoldDuration\|HoldDuration` value. | | `Class.ProximityPromptService.PromptShown\|PromptShown` | Triggers in `Class.LocalScript\|LocalScripts` when a proximity prompt is made visible. | | `Class.ProximityPromptService.PromptHidden\|PromptHidden` | Triggers in `Class.LocalScript\|LocalScripts` when a prompt is hidden. | The following code sample includes a basic framework for using `Class.ProximityPromptService`: ```lua local ProximityPromptService = game:GetService("ProximityPromptService") -- Detect when prompt is triggered local function onPromptTriggered(promptObject, player) end -- Detect when prompt hold begins local function onPromptHoldBegan(promptObject, player) end -- Detect when prompt hold ends local function onPromptHoldEnded(promptObject, player) end -- Connect prompt events to handling functions ProximityPromptService.PromptTriggered:Connect(onPromptTriggered) ProximityPromptService.PromptButtonHoldBegan:Connect(onPromptHoldBegan) ProximityPromptService.PromptButtonHoldEnded:Connect(onPromptHoldEnded) ``` --- title: "Rich text markup" url: /docs/en-us/ui/rich-text last_updated: 2026-06-29T19:34:16Z description: "Rich Text Markup are simple markup tags to style sections of a string." --- # Rich text markup UI **rich text** utilizes simple markup tags to style sections of a string in bold, italics, underline, fill color, stroke variations, and more. You can apply styling tags to `Class.TextLabel`, `Class.TextButton`, and `Class.TextBox` objects. ## Enable rich text You must enable rich text on a per-object basis through its **RichText** property in the [Properties](/docs/en-us/studio/properties.md) window, or by setting the property to `true` in a `Class.LocalScript`. ```lua local title = Instance.new("TextLabel") title.RichText = true title.Text = "Use a bold title" ``` > **Info:** When editing an object's **Text** property in Studio, toggling the **RichText** checkbox in the [Properties](/docs/en-us/studio/properties.md) window displays the text string as a final render. This is useful for identifying and correcting mistakes in any [supported tags](#supported-tags). > **Warning:**[Localizing](/docs/en-us/production/localization.md) an experience to support other languages removes rich text formatting tags. To ensure formatting appears in other languages, re-apply the tags manually to your localized strings. ## Supported tags Rich text tags are similar to XML/HTML tags and you must include both an opening and closing tag around the formatted text. `Formatted text` You can also nest tags inside each other as long as you close them in the reverse order of how you opened them. `Formatted text` ### Font color ` ` > `I want the orange candy.` `I want the orange candy.` ### Font size ` ` > `This is big. This is small.` ### Font face ` ` > `This is Michroma face.` > **Info:** Font face/family names are listed on the `Datatype.Font` enum reference page. ### Font family ` ` > `This is Michroma face.` > **Info:** Font face/family names are listed on the `Datatype.Font` enum reference page. ### Font weight ` ` > `This is normal. This is heavy.` `This is normal. This is heavy.` > **Info:** Font weight can be a case-insensitive string name including `Thin`, `ExtraLight`, `Light`, `Regular`, `Medium`, `SemiBold`, `Bold`, `ExtraBold`, or `Heavy`; it can also be a number in factors of 100 between `100` and `900`. ### Font transparency ` ` > `Text at 50% transparency.` ### Stroke ` ` | Parameter | Equivalent Property | Example Values | | --- | --- | --- | | `color` | `Class.UIStroke.Color` | `"rgb(255,125,0)"`, `"#FF7800"` | | `thickness` or `th` | `Class.UIStroke.Thickness` | `"1"`, `"4"`, `"0.5"` | | `transparency` or `tr` | `Class.UIStroke.Transparency` | `"0"`, `"0.5"`, `"1"` | | `joins` | `Class.UIStroke.LineJoinMode` | `"round"`, `"bevel"`, `"miter"` | | `sizing` | `Class.UIStroke.StrokeSizingMode` | `"fixed"`, `"scaled"` | > `You won 25 gems.` > **Info:** See [UI appearance modifiers](/docs/en-us/ui/appearance-modifiers.md#stroke) for details on `` tag parameters such as `thickness` and `joins`. ### Bold ` ` > `Text in bold.` ### Italic ` ` > `Text italicized.` ### Underline ` ` > `Text underlined.` ### Strikethrough ` ` > `Text with strikethrough applied.` ### Line break `
` > `New line occurs after this sentence.
Next sentence...` ### Uppercase ` `` ` > `Uppercase makes words read loudly!` `Uppercase makes words read loudly!` ### Small caps ` `` ` > `My name is Diva Dragonslayer.` `My name is Diva Dragonslayer.` ### Mark ` ` > `Text highlighted.` ### Comment `` > `After this is a comment... and now more text...` ## Escape forms If you want to render certain characters like `<` or `>` and exempt them from consideration as part of rich text tags, you can write them in their **escape form**. | Character | Escape | Example | Result | | --- | --- | --- | --- | | `<` | `<` | `10 < 100` | 10 < 100 | | `>` | `>` | `100 > 10` | 100 > 10 | | `"` | `"` | `Meet "Diva Dragonslayer"` | Meet "Diva Dragonslayer" | | `'` | `'` | `Diva's pet is a falcon!` | Diva's pet is a falcon! | | `&` | `&` | `Render another escape form &lt; by escaping an ampersand` | Render another escape form **<** by escaping an ampersand |
--- title: "Scrolling frames" url: /docs/en-us/ui/scrolling-frames last_updated: 2026-06-29T19:34:16Z description: "ScrollingFrames feature built-in scrolling interactivity and are ideal for displaying a lot of information in a confined space." --- # Scrolling frames A `Class.ScrollingFrame` consists of a customizable **canvas** and **scroll bars** with built‑in scrolling interactivity and different ways to customize how the scrolling works. `Class.ScrollingFrame` is ideal for displaying a lot of information in a confined space and it works well with [list](/docs/en-us/ui/list-flex-layouts.md) and [grid](/docs/en-us/ui/grid-table-layouts.md) layouts. ![Example ScrollingFrame on the screen containing a tabbed category bar and a list of magical items for the player to consider purchasing.](../assets/ui/ui-objects/ScrollingFrame-Example.jpg) ## Canvas The **canvas** is the primary area of a `Class.ScrollingFrame` that can contain other `Class.GuiObject|GuiObjects`. Scrolling behavior automatically adapts in the following scenarios: | Frame Setup | Result | | --- | --- | | The frame's `Class.ScrollingFrame.CanvasSize\|CanvasSize` is taller than its overall height; specifically the total `Class.ScrollingFrame.CanvasSize\|CanvasSize.Y` exceeds the total `Class.ScrollingFrame.Size\|Size.Y`. | Vertical scrolling is enabled and a vertical scroll bar appears. | | The frame's `Class.ScrollingFrame.CanvasSize\|CanvasSize` is wider than its overall width; specifically the total `Class.ScrollingFrame.CanvasSize\|CanvasSize.X` exceeds the total `Class.ScrollingFrame.Size\|Size.X`. | Horizontal scrolling is enabled and a horizontal scroll bar appears. | | The frame's `Class.ScrollingFrame.AutomaticCanvasSize\|AutomaticCanvasSize` is set to `Enum.AutomaticSize\|Y` or `Enum.AutomaticSize\|XY` and the total height of its contents (child `Class.GuiObject\|GuiObjects`) exceed its total `Class.ScrollingFrame.Size\|Size.Y`. | Vertical scrolling is enabled and a vertical scroll bar appears. | | The frame's `Class.ScrollingFrame.AutomaticCanvasSize\|AutomaticCanvasSize` is set to `Enum.AutomaticSize\|X` or `Enum.AutomaticSize\|XY` and the total width of its contents (child `Class.GuiObject\|GuiObjects`) exceed its total `Class.ScrollingFrame.Size\|Size.X`. | Horizontal scrolling is enabled and a horizontal scroll bar appears. | The `Class.ScrollingFrame.CanvasPosition|CanvasPosition` property reflects the **current** positional offset of the canvas within the frame, in pixels, and it sets the position of scroll bars accordingly. Note that this property doesn't do anything if scroll bars aren't visible. ## Insets A scrolling frame's `Class.ScrollingFrame.CanvasSize|CanvasSize` may be affected by the `Class.ScrollingFrame.VerticalScrollBarInset|VerticalScrollBarInset` and/or `Class.ScrollingFrame.HorizontalScrollBarInset|HorizontalScrollBarInset` properties. In the following example diagram, a vertically scrolling frame uses a canvas width of 100%. With `Class.ScrollingFrame.VerticalScrollBarInset|VerticalScrollBarInset` set to `Enum.ScrollBarInset|None` (default), the canvas extends to the full width of the scrolling frame, obscuring a slight amount of content under the scroll bar. In contrast, a `Class.ScrollingFrame.VerticalScrollBarInset|VerticalScrollBarInset` setting of either `Enum.ScrollBarInset|Always` or `Enum.ScrollBarInset|ScrollBar` insets the canvas by the `Class.ScrollingFrame.ScrollBarThickness|ScrollBarThickness` amount so that the canvas meets edge‑to‑edge with the scroll bar. #### None (Default) #### Always / ScrollBar ## Scroll bars The frame's **scroll bars** reflect the current canvas position and provide automatic drag‑to‑scroll interactivity. For vertical scroll bars, the `Class.ScrollingFrame.VerticalScrollBarPosition|VerticalScrollBarPosition` property switches the bar's position to either the left or right of the canvas, but horizontal scroll bars can only be positioned below the canvas. Scroll bar construction consists of three asset properties: - `Class.ScrollingFrame.TopImage|TopImage` — Displays on the top of a vertical scroll bar, or the left of a horizontal scroll bar (rotated 90° counterclockwise for a horizontal scroll bar). - `Class.ScrollingFrame.MidImage|MidImage` — Spans the area between `Class.ScrollingFrame.TopImage|TopImage` and `Class.ScrollingFrame.BottomImage|BottomImage` (rotated 90° counterclockwise for a horizontal scroll bar). - `Class.ScrollingFrame.BottomImage|BottomImage` — Displays on the bottom of a vertical scroll bar, or the right of a horizontal scroll bar (rotated 90° counterclockwise for a horizontal scroll bar). ![Diagram showing the three image asset elements which construct a scrolling frame's scroll bar.](../assets/ui/ui-objects/ScrollingFrame-Scroll-Bar-Elements.png) Each image scales based on the `Class.ScrollingFrame.ScrollBarThickness|ScrollBarThickness` property which changes the width of a vertical scroll bar or the height of a horizontal scroll bar. Additional visual modifications can be made through the `Class.ScrollingFrame.ScrollBarImageColor3|ScrollBarImageColor3` and `Class.ScrollingFrame.ScrollBarImageTransparency|ScrollBarImageTransparency` properties which tint the bar's image assets and modify its opacity, respectively. ## Elasticity By default, `Class.ScrollingFrame|ScrollingFrames` exhibit elastic scrolling behavior on touch‑enabled devices, meaning that users can drag the canvas slightly further than its bounds and, upon release, the canvas will spring back to its minimum or maximum limit. If you want to remove the elastic behavior completely and ensure the canvas never scrolls past its limits, set the `Class.ScrollingFrame.ElasticBehavior|ElasticBehavior` property to `Enum.ElasticBehavior|Never`. --- title: "Size modifiers and constraints" url: /docs/en-us/ui/size-modifiers last_updated: 2026-06-29T19:34:16Z description: "Explains how use size modifiers and size constraints with user interface objects." --- # Size modifiers and constraints Alongside basic [sizing](/docs/en-us/ui/position-and-size.md) of UI objects, you can utilize size modifiers to [scale](#scale) an object proportionally or [automatically resize](#automatic-sizing) it. You can also insert [size constraints](#constraints) to control **aspect ratio**, or set a minimum and maximum **size** or **text size**. ## Scale A `Class.UIScale` object stores a numerical value that multiplies the `Class.GuiBase2d.AbsoluteSize|AbsoluteSize` property of the parent `Class.GuiObject`. For example, if you want an object to be twice as large as it currently is, you can insert a `Class.UIScale` object with a `Class.UIScale.Scale|Scale` property of `2`. This modifier is useful for "zooming in" while designing a detailed user interface in Studio, since it proportionally scales the object and all of its children, including any applied [appearance modifiers](/docs/en-us/ui/appearance-modifiers.md) like `Class.UIStroke` or `Class.UICorner`. It's also useful to [tween](/docs/en-us/ui/animation.md) the size of an object, for example to slightly increase the size of a button when a player hovers their mouse over it. ## Automatic sizing The `Class.GuiObject.AutomaticSize|AutomaticSize` property automatically resizes a parent `Class.GuiObject` to the size of its descendants. You can use this property in a variety of cases, including: - Expanding a `Class.GuiObject` to fit text that has been [localized](/docs/en-us/production/localization.md) in many languages. - Allowing users to [input text](/docs/en-us/ui/text-input.md) within a `Class.TextBox`, automatically adjusting its size based on the amount of text entered. - Automatically adjusting the size of text objects using [rich text markup](/docs/en-us/ui/rich-text.md), including font type and size. You can enable the `Class.GuiObject.AutomaticSize|AutomaticSize` property for any `Class.GuiObject`. By default, it is set to `Enum.AutomaticSize|None`, but you can change its value to have specific control over which direction(s) the `Class.GuiObject` resizes to fit its content. > **Info:**`Class.ScrollingFrame` uses the `Class.ScrollingFrame.AutomaticCanvasSize|AutomaticCanvasSize` property instead of `Class.GuiObject.AutomaticSize|AutomaticSize`. It behaves the same but resizes the frame's `Class.ScrollingFrame.CanvasSize|CanvasSize`, not its base `Class.GuiObject.Size|Size`. To enable automatic sizing: 1. In the [Explorer](/docs/en-us/studio/explorer.md) window, click any `Class.GuiObject`, such as a `Class.Frame`, `Class.TextBox`, or `Class.ImageLabel`. 2. In the [Properties](/docs/en-us/studio/properties.md) window, navigate to the `Class.GuiObject.AutomaticSize|AutomaticSize` property (`Class.ScrollingFrame.AutomaticCanvasSize|AutomaticCanvasSize` for `Class.ScrollingFrame`) and set it to one of the following options: - `Enum.AutomaticSize|X` — Resizes content horizontally. - `Enum.AutomaticSize|Y` — Resizes content vertically. - `Enum.AutomaticSize|XY` — Resizes content both horizontally and vertically. Once automatic sizing is set, note that other object properties behave as follows: #### AnchorPoint Similar to the concept of [sizing](/docs/en-us/ui/position-and-size.md#size) an object, `Class.GuiObject.AutomaticSize|AutomaticSize` takes the `Class.GuiObject.AnchorPoint|AnchorPoint` property into consideration when resizing content. For example, the following video displays three `Class.TextLabel|TextLabels` that automatically resize from their anchor point. - The top label has an `Class.GuiObject.AnchorPoint|AnchorPoint` value of `Datatype.Vector2.new()|(0, 0.5)` which anchors it to the middle of the left side. - The middle label has an `Class.GuiObject.AnchorPoint|AnchorPoint` value of `Datatype.Vector2.new()|(0.5, 0.5)` which anchors it to the center. - The bottom label has an `Class.GuiObject.AnchorPoint|AnchorPoint` value of `Datatype.Vector2.new()|(1, 0.5)` which anchors it to the middle of the right side. #### Size When you enable `Class.GuiObject.AutomaticSize|AutomaticSize`, the object's `Class.GuiObject.Size|Size` property controls its **minimum size**. This means that the `Class.GuiObject` cannot resize smaller than its core size along any axis. For example, the following white `Class.Frame` has a `Class.GuiObject.Size|Size` of `0` for both axes, meaning it doesn't have a minimum width/height and can resize according to its child objects. In comparison, the following white `Class.Frame` has a `Class.GuiObject.Size|Size.X` of `100`, so it cannot resize to a width less than 100 pixels. #### TextWrapped When you enable **text wrapping** for a UI text object (`Class.TextLabel.TextWrapped|TextLabel`, `Class.TextButton.TextWrapped|TextButton`, or `Class.TextBox.TextWrapped|TextBox`) and set the `Class.GuiObject.AutomaticSize|AutomaticSize` property to either `Enum.AutomaticSize|Y` or `Enum.AutomaticSize|XY`, the label resizes to accommodate any text that wraps across multiple lines. For example, the following `Class.TextLabel` has `Class.TextLabel.TextWrapped|TextWrapped` enabled and an `Class.GuiObject.AutomaticSize|AutomaticSize` of `Enum.AutomaticSize|Y`, so when text overflows the width and wraps, the label expands vertically. ## Constraints There are three types of **constraints** you can use for a user interface object: [size](#size), [text size](#text-size), and [aspect ratio](#aspect-ratio). To use a constraint, you must set it as a **child** of the `Class.GuiObject` you want to constrain. ### Size The `Class.UISizeConstraint` specifies a minimum and maximum size for a `Class.GuiObject`. This constraint ensures that the `Class.GuiObject` doesn't become too small or large on different screen sizes. For example, if you set the `Class.UISizeConstraint.MinSize|MinSize` property to `(200, 200)` and the `Class.UISizeConstraint.MaxSize|MaxSize` property to `(400, 400)` , the `Class.GuiObject` cannot scale smaller than 200×200 pixels or larger than 400×400 pixels. > **Warning:** When a UI object is under control of both a layout structure such as `Class.UIListLayout` and a `Class.UISizeConstraint`, the constraint will **override** the layout and control the object's size. ### Text size The `Class.UITextSizeConstraint` specifies a minimum and maximum font size for a `Class.GuiObject` with text, such as a `Class.TextLabel`, `Class.TextButton`, or `Class.TextBox`. This constraint ensures that the **text** within a `Class.GuiObject` doesn't become illegible or too large. If you enable the `Class.TextLabel.TextScaled|TextScaled` property of the parent `Class.GuiObject`, the text size scales with the container's size and respects constraints even if the object becomes smaller or larger than the `Class.UITextSizeConstraint.MinTextSize|MinTextSize` and `Class.UITextSizeConstraint.MaxTextSize|MaxTextSize` values. For example, the following `Class.TextLabel` object has a `Class.UITextSizeConstraint` with a `Class.UITextSizeConstraint.MinTextSize|MinTextSize` value of `50` and a MaxTextSize value of `80`. Even when the `Class.TextLabel` becomes smaller, the font never becomes smaller than 50 pixels, and when the object becomes large, the font next exceeds 80 pixels. > **Error:** Do not use `Class.UITextSizeConstraint.MinTextSize|MinTextSize` property values lower than `9` or the text will be difficult to read for many viewers. ### Aspect ratio The `Class.UIAspectRatioConstraint` enforces a **width‑to‑height** aspect ratio on a `Class.GuiObject` regardless of its core size, even if that size is set as a percentage of its parent. For example, inserting this constraint as a child of a `Class.Frame` and setting the constraint's `Class.UIAspectRatioConstraint.AspectRatio|AspectRatio` property to `2` (`2:1`) keeps the frame's width at twice that of its height. Similarly, setting this constraint's `Class.UIAspectRatioConstraint.AspectRatio|AspectRatio` property to `0.5` (`0.5:1`) keeps the frame's width at half that of its height. Setting this constraint's `Class.UIAspectRatioConstraint.AspectRatio|AspectRatio` to the default of `1` (`1:1`) is a convenient way to prevent non‑proportional scaling/stretching of an `Class.ImageLabel` with a square image asset, such as an [avatar thumbnail](/docs/en-us/projects/assets.md#rbxthumb). > **Warning:** When a UI object is under control of both a layout structure such as `Class.UIListLayout` and a `Class.UIAspectRatioConstraint`, the constraint will **override** the layout and control the object's size. --- title: "UI styling compatibility" url: /docs/en-us/ui/styling/compatibility last_updated: 2026-06-29T19:34:16Z description: "Outlines all of the classes and associated properties which can be styled." --- # UI styling compatibility The following tables outline all of the classes and associated properties which can be styled. Additional support may be added over time, so please bookmark this page for reference. ## UI objects ### GuiObject `Class.GuiObject` is an abstract class from which most UI classes inherit, including [frames](/docs/en-us/ui/frames.md), [labels](/docs/en-us/ui/labels.md), [buttons](/docs/en-us/ui/buttons.md), and more. | Property | Type | | --- | --- | | `Class.GuiObject.Active` | boolean | | `Class.GuiObject.AnchorPoint` | `Datatype.Vector2` | | `Class.GuiObject.AutomaticSize` | `Enum.AutomaticSize` | | `Class.GuiObject.BackgroundColor` | `Datatype.BrickColor` | | `Class.GuiObject.BackgroundColor3` | `Datatype.Color3` | | `Class.GuiObject.BackgroundTransparency` | float | | `Class.GuiObject.BorderColor3` | `Datatype.Color3` | | `Class.GuiObject.BorderMode` | `Enum.BorderMode` | | `Class.GuiObject.BorderSizePixel` | integer | | `Class.GuiObject.ClipsDescendants` | boolean | | `Class.GuiObject.Interactable` | boolean | | `Class.GuiObject.LayoutOrder` | integer | | `Class.GuiObject.Position` | `Datatype.UDim2` | | `Class.GuiObject.Rotation` | float | | `Class.GuiObject.Selectable` | boolean | | `Class.GuiObject.SelectionOrder` | integer | | `Class.GuiObject.Size` | `Datatype.UDim2` | | `Class.GuiObject.SizeConstraint` | `Enum.SizeConstraint` | | `Class.GuiObject.Transparency` | float | | `Class.GuiObject.Visible` | boolean | | `Class.GuiObject.ZIndex` | integer | ### GuiButton | Property | Type | | --- | --- | | `Class.GuiButton.AutoButtonColor` | boolean | ### TextLabel | Property | Type | | --- | --- | | `Class.TextLabel.Font` | `Enum.Font` | | `Class.TextLabel.FontFace` | `Datatype.Font` | | `Class.TextLabel.LineHeight` | float | | `Class.TextLabel.MaxVisibleGraphemes` | integer | | `Class.TextLabel.OpenTypeFeatures` | string | | `Class.TextLabel.RichText` | boolean | | `Class.TextLabel.Text` | string | | `Class.TextLabel.TextColor3` | `Datatype.Color3` | | `Class.TextLabel.TextDirection` | `Enum.TextDirection` | | `Class.TextLabel.TextScaled` | boolean | | `Class.TextLabel.TextSize` | float | | `Class.TextLabel.TextStrokeColor3` | `Datatype.Color3` | | `Class.TextLabel.TextStrokeTransparency` | float | | `Class.TextLabel.TextTransparency` | float | | `Class.TextLabel.TextTruncate` | `Enum.TextTruncate` | | `Class.TextLabel.TextWrapped` | boolean | | `Class.TextLabel.TextXAlignment` | `Enum.TextXAlignment` | | `Class.TextLabel.TextYAlignment` | `Enum.TextYAlignment` | ### TextButton | Property | Type | | --- | --- | | `Class.TextButton.Font` | `Enum.Font` | | `Class.TextButton.FontFace` | `Datatype.Font` | | `Class.TextButton.LineHeight` | float | | `Class.TextButton.MaxVisibleGraphemes` | integer | | `Class.TextButton.OpenTypeFeatures` | string | | `Class.TextButton.RichText` | boolean | | `Class.TextButton.Text` | string | | `Class.TextButton.TextColor3` | `Datatype.Color3` | | `Class.TextButton.TextDirection` | `Enum.TextDirection` | | `Class.TextButton.TextScaled` | boolean | | `Class.TextButton.TextSize` | float | | `Class.TextButton.TextStrokeColor3` | `Datatype.Color3` | | `Class.TextButton.TextStrokeTransparency` | float | | `Class.TextButton.TextTransparency` | float | | `Class.TextButton.TextTruncate` | `Enum.TextTruncate` | | `Class.TextButton.TextWrapped` | boolean | | `Class.TextButton.TextXAlignment` | `Enum.TextXAlignment` | | `Class.TextButton.TextYAlignment` | `Enum.TextYAlignment` | ### ImageLabel | Property | Type | | --- | --- | | `Class.ImageLabel.Image` | `ContentId` | | `Class.ImageLabel.ImageColor3` | `Datatype.Color3` | | `Class.ImageLabel.ImageContent` | `Datatype.Content` | | `Class.ImageLabel.ImageRectOffset` | `Datatype.Vector2` | | `Class.ImageLabel.ImageRectSize` | `Datatype.Vector2` | | `Class.ImageLabel.ImageTransparency` | float | | `Class.ImageLabel.ResampleMode` | `Enum.ResamplerMode` | | `Class.ImageLabel.ScaleType` | `Enum.ScaleType` | | `Class.ImageLabel.SliceCenter` | `Datatype.Rect` | | `Class.ImageLabel.SliceScale` | float | | `Class.ImageLabel.TileSize` | `Datatype.UDim2` | ### ImageButton | Property | Type | | --- | --- | | `Class.ImageButton.HoverImage` | `ContentId` | | `Class.ImageButton.HoverImageContent` | `Datatype.Content` | | `Class.ImageButton.Image` | `ContentId` | | `Class.ImageButton.ImageColor3` | `Datatype.Color3` | | `Class.ImageButton.ImageContent` | `Datatype.Content` | | `Class.ImageButton.ImageRectOffset` | `Datatype.Vector2` | | `Class.ImageButton.ImageRectSize` | `Datatype.Vector2` | | `Class.ImageButton.ImageTransparency` | float | | `Class.ImageButton.PressedImage` | `ContentId` | | `Class.ImageButton.PressedImageContent` | `Datatype.Content` | | `Class.ImageButton.ResampleMode` | `Enum.ResamplerMode` | | `Class.ImageButton.ScaleType` | `Enum.ScaleType` | | `Class.ImageButton.SliceCenter` | `Datatype.Rect` | | `Class.ImageButton.SliceScale` | float | | `Class.ImageButton.TileSize` | `Datatype.UDim2` | ### TextBox | Property | Type | | --- | --- | | `Class.TextBox.ClearTextOnFocus` | boolean | | `Class.TextBox.MultiLine` | boolean | | `Class.TextBox.PlaceholderColor3` | `Datatype.Color3` | | `Class.TextBox.PlaceholderText` | string | | `Class.TextBox.ShowNativeInput` | string | | `Class.TextBox.TextEditable` | boolean | ### ScrollingFrame | Property | Type | | --- | --- | | `Class.ScrollingFrame.AutomaticCanvasSize` | `Enum.AutomaticSize` | | `Class.ScrollingFrame.BottomImage` | `ContentId` | | `Class.ScrollingFrame.BottomImageContent` | `Datatype.Content` | | `Class.ScrollingFrame.CanvasSize` | `Datatype.UDim2` | | `Class.ScrollingFrame.ElasticBehavior` | `Enum.ElasticBehavior` | | `Class.ScrollingFrame.HorizontalScrollBarInset` | `Enum.ScrollBarInset` | | `Class.ScrollingFrame.MidImage` | `ContentId` | | `Class.ScrollingFrame.MidImageContent` | `Datatype.Content` | | `Class.ScrollingFrame.ScrollBarImageColor3` | `Datatype.Color3` | | `Class.ScrollingFrame.ScrollBarImageTransparency` | float | | `Class.ScrollingFrame.ScrollBarThickness` | float | | `Class.ScrollingFrame.ScrollingDirection` | `Enum.ScrollingDirection` | | `Class.ScrollingFrame.ScrollingEnabled` | boolean | | `Class.ScrollingFrame.TopImage` | `ContentId` | | `Class.ScrollingFrame.TopImageContent` | `Datatype.Content` | | `Class.ScrollingFrame.VerticalScrollBarInset` | `Enum.ScrollBarInset` | | `Class.ScrollingFrame.VerticalScrollBarPosition` | `Enum.VerticalScrollBarPosition` | ### ViewportFrame | Property | Type | | --- | --- | | `Class.ViewportFrame.Ambient` | `Datatype.Color3` | | `Class.ViewportFrame.ImageColor3` | `Datatype.Color3` | | `Class.ViewportFrame.ImageTransparency` | float | | `Class.ViewportFrame.LightColor` | `Datatype.Color3` | | `Class.ViewportFrame.LightDirection` | `Datatype.Vector3` | ### Path2D | Property | Type | | --- | --- | | `Class.Path2D.Closed` | boolean | | `Class.Path2D.Color3` | `Datatype.Color3` | | `Class.Path2D.Thickness` | float | | `Class.Path2D.Visible` | boolean | | `Class.Path2D.ZIndex` | integer | ### CanvasGroup | Property | Type | | --- | --- | | `Class.CanvasGroup.GroupColor3` | `Datatype.Color3` | | `Class.CanvasGroup.GroupTransparency` | float | ## Appearance modifiers ### UICorner | Property | Type | | --- | --- | | `Class.UICorner.CornerRadius` | `Datatype.UDim` | | `Class.UICorner.BottomLeftRadius` | `Datatype.UDim` | | `Class.UICorner.BottomRightRadius` | `Datatype.UDim` | | `Class.UICorner.TopLeftRadius` | `Datatype.UDim` | | `Class.UICorner.TopRightRadius` | `Datatype.UDim` | ### UIGradient | Property | Type | | --- | --- | | `Class.UIGradient.Color` | `Datatype.ColorSequence` | | `Class.UIGradient.Enabled` | boolean | | `Class.UIGradient.Offset` | `Datatype.Vector2` | | `Class.UIGradient.Rotation` | float | | `Class.UIGradient.Transparency` | `Datatype.NumberSequence` | ### UIPadding | Property | Type | | --- | --- | | `Class.UIPadding.PaddingBottom` | `Datatype.UDim` | | `Class.UIPadding.PaddingLeft` | `Datatype.UDim` | | `Class.UIPadding.PaddingRight` | `Datatype.UDim` | | `Class.UIPadding.PaddingTop` | `Datatype.UDim` | ### UIShadow | Property | Type | | --- | --- | | `Class.UIShadow.BlurRadius` | `Datatype.UDim` | | `Class.UIShadow.Color` | `Datatype.Color3` | | `Class.UIShadow.Offset` | `Datatype.UDim2` | | `Class.UIShadow.Spread` | `Datatype.UDim2` | | `Class.UIShadow.Transparency` | float | | `Class.UIShadow.ZIndex` | integer | ### UIStroke | Property | Type | | --- | --- | | `Class.UIStroke.ApplyStrokeMode` | `Enum.ApplyStrokeMode` | | `Class.UIStroke.Color` | `Datatype.Color3` | | `Class.UIStroke.Enabled` | boolean | | `Class.UIStroke.LineJoinMode` | `Enum.LineJoinMode` | | `Class.UIStroke.Thickness` | float | | `Class.UIStroke.Transparency` | float | ## Layout structures ### UIListLayout | Property | Type | | --- | --- | | `Class.UIListLayout.HorizontalFlex` | `Enum.UIFlexAlignment` | | `Class.UIListLayout.HorizontalPadding` | `Datatype.UDim` | | `Class.UIListLayout.ItemLineAlignment` | `Enum.ItemLineAlignment` | | `Class.UIListLayout.Padding` | `Datatype.UDim` | | `Class.UIListLayout.VerticalFlex` | `Enum.UIFlexAlignment` | | `Class.UIListLayout.VerticalPadding` | `Datatype.UDim` | | `Class.UIListLayout.Wraps` | boolean | ### UIGridStyleLayout | Property | Type | | --- | --- | | `Class.UIGridStyleLayout.FillDirection` | `Enum.FillDirection` | | `Class.UIGridStyleLayout.HorizontalAlignment` | `Enum.HorizontalAlignment` | | `Class.UIGridStyleLayout.SortOrder` | `Enum.SortOrder` | | `Class.UIGridStyleLayout.VerticalAlignment` | `Enum.VerticalAlignment` | ### UIGridLayout | Property | Type | | --- | --- | | `Class.UIGridLayout.CellPadding` | `Datatype.UDim2` | | `Class.UIGridLayout.CellSize` | `Datatype.UDim2` | | `Class.UIGridLayout.FillDirectionMaxCells` | integer | | `Class.UIGridLayout.StartCorner` | `Enum.StartCorner` | ### UIPageLayout | Property | Type | | --- | --- | | `Class.UIPageLayout.Animated` | boolean | | `Class.UIPageLayout.Circular` | boolean | | `Class.UIPageLayout.EasingDirection` | `Enum.EasingDirection` | | `Class.UIPageLayout.EasingStyle` | `Enum.EasingStyle` | | `Class.UIPageLayout.GamepadInputEnabled` | boolean | | `Class.UIPageLayout.Padding` | `Datatype.UDim` | | `Class.UIPageLayout.ScrollWheelInputEnabled` | boolean | | `Class.UIPageLayout.TouchInputEnabled` | boolean | | `Class.UIPageLayout.TweenTime` | float | ## Size modifiers and constraints ### UIAspectRatioConstraint | Property | Type | | --- | --- | | `Class.UIAspectRatioConstraint.AspectRatio` | float | | `Class.UIAspectRatioConstraint.AspectType` | `Enum.AspectType` | | `Class.UIAspectRatioConstraint.DominantAxis` | `Enum.DominantAxis` | ### UISizeConstraint | Property | Type | | --- | --- | | `Class.UISizeConstraint.MaxSize` | `Datatype.Vector2` | | `Class.UISizeConstraint.MinSize` | `Datatype.Vector2` | ### UITextSizeConstraint | Property | Type | | --- | --- | | `Class.UITextSizeConstraint.MaxTextSize` | integer | | `Class.UITextSizeConstraint.MinTextSize` | integer | ### UIScale | Property | Type | | --- | --- | | `Class.UIScale.Scale` | float | ### UIFlexItem | Property | Type | | --- | --- | | `Class.UIFlexItem.FlexMode` | `Enum.UIFlexMode` | | `Class.UIFlexItem.GrowRatio` | float | | `Class.UIFlexItem.ItemLineAlignment` | `Enum.ItemLineAlignment` | | `Class.UIFlexItem.ShrinkRatio` | float | ### StyleQuery `Class.StyleQuery` lets you define conditional breakpoints (like container/media queries) that control when styles apply to the query's parent instance. | Property | Type | | --- | --- | | `Class.StyleQuery.AspectRatioRange` | `Datatype.NumberRange` | | `Class.StyleQuery.MaxSize` | `Datatype.Vector2` | | `Class.StyleQuery.MinSize` | `Datatype.Vector2` | | `Class.StyleQuery.PreferredInputType` | `Enum.PreferredInput` | | `Class.StyleQuery.ReduceMotionEnabled` | boolean | | `Class.StyleQuery.ViewportDisplaySize` | `Enum.DisplaySize` | --- title: "CSS Comparisons" url: /docs/en-us/ui/styling/css-comparisons last_updated: 2026-06-29T19:34:16Z description: "Explore how CSS concepts map to Roblox UI styling concepts." --- # CSS Comparisons Most CSS concepts map to Roblox styling concepts. The following examples show how CSS and HTML align with Luau and Roblox classes/properties. To test each of the following Luau script examples: 1. In the [Explorer](/docs/en-us/studio/explorer.md), create the following: 1. `Class.StyleSheet` instance inside `Class.ReplicatedStorage`. 2. `Class.ScreenGui` container in `Class.StarterGui`. 3. `Class.LocalScript` instance inside the `Class.ScreenGui`. 4. `Class.StyleLink` object inside the `Class.ScreenGui`. 2. In the `Class.LocalScript`, paste the following supporting code:```lua local CollectionService = game:GetService("CollectionService") local ReplicatedStorage = game:GetService("ReplicatedStorage") local coreSheet = ReplicatedStorage:FindFirstChildWhichIsA("StyleSheet") local screenGui = script.Parent local styleLink = screenGui:FindFirstChildWhichIsA("StyleLink") styleLink.StyleSheet = coreSheet ``` 3. For each example below, paste the **Luau** code lines following the supporting lines 1–7. ## Selectors The `Class.StyleRule.Selector|Selector` property of a `Class.StyleRule` specifies which instances the rule should affect. The following selector types map from CSS to Luau and can be used with combinators. ### Element Equivalent to CSS element selectors are Roblox **class selectors** which select all instances of a given `Class.GuiObject` class, for example `Class.Frame`, `Class.ImageLabel`, `Class.TextButton`, etc. ```text button { background-color: #335FFF; color: #E1E1E1; width: 15%; height: 40px; border: none; } ``` ```text ``` ```lua local rule = Instance.new("StyleRule") rule.Parent = coreSheet rule.Selector = "TextButton" -- Roblox class selector rule:SetProperties({ BackgroundColor3 = Color3.fromHex("335FFF"), TextColor3 = Color3.fromHex("E1E1E1"), Size = UDim2.new(0.15, 0, 0, 40), BorderSizePixel = 0 }) local button = Instance.new("TextButton") button.Text = "Main Menu" button.Parent = screenGui ``` ### Class The Roblox equivalent to CSS `class` selectors are **tag selectors** which utilize [tags](/docs/en-us/studio/properties.md#instance-tags) applied through `Class.CollectionService`. ```text .button-primary { background-color: #335FFF; color: #E1E1E1; } ``` ```text ``` ```lua local rule = Instance.new("StyleRule") rule.Parent = coreSheet rule.Selector = ".ButtonPrimary" -- Roblox tag selector rule:SetProperties({ BackgroundColor3 = Color3.fromHex("335FFF"), TextColor3 = Color3.fromHex("E1E1E1"), AutomaticSize = Enum.AutomaticSize.XY }) local button = Instance.new("TextButton") button.Text = "Main Menu" button.Parent = screenGui -- Apply tag to button CollectionService:AddTag(button, "ButtonPrimary") ``` ### Identifier The closest Roblox comparison to CSS `id` is the `#[name]` selector which selects according to the value of `Class.Instance.Name`. Unlike the W3C specification of the `id` attribute, names are not expected to be unique. ```text #modal-frame { background-color: #000022; opacity: 0.5; width: 50%; min-height: 100px; } ``` ```text ``` ```lua local rule = Instance.new("StyleRule") rule.Parent = coreSheet rule.Selector = "#ModalFrame" -- Instance name selector rule:SetProperties({ BackgroundColor3 = Color3.fromHex("000022"), BackgroundTransparency = 0.5, Size = UDim2.new(0.5, 0, 0, 100), AutomaticSize = Enum.AutomaticSize.Y }) local frame = Instance.new("Frame") frame.Parent = screenGui -- Rename frame to match selector frame.Name = "ModalFrame" ``` ## Combinators Combinators let you mix basic [selectors](#selectors) to match deeper hierarchy relationships. ### Child The child selector of `>` is identical between CSS and Roblox. ```text .menu-container { width: 25%; } .menu-container > button { background-color: #335FFF; color: #E1E1E1; width: 80%; height: 40px; border: none; } ``` ```text ``` ```lua local rule = Instance.new("StyleRule") rule.Parent = coreSheet rule.Selector = ".MenuContainer > TextButton" -- Child selector rule:SetProperties({ BackgroundColor3 = Color3.fromHex("335FFF"), TextColor3 = Color3.fromHex("E1E1E1"), Size = UDim2.new(0.8, 0, 0, 40), BorderSizePixel = 0 }) -- Create menu container local menuContainer = Instance.new("Frame") menuContainer.Size = UDim2.new(0.25, 0, 0, 0) menuContainer.AutomaticSize = Enum.AutomaticSize.Y menuContainer.Parent = screenGui -- Apply tag CollectionService:AddTag(menuContainer, "MenuContainer") -- Create button local button = Instance.new("TextButton") button.Text = "Options" -- Set menu container as parent of button button.Parent = menuContainer ``` ### Descendant Unlike the CSS whitespace syntax, for example `.menu-container button` seen here, Roblox uses the `>>` combinator to indicate a descendant relationship. ```text .menu-container { width: 25%; } .sub-container { width: 75%; } .menu-container button { background-color: #335FFF; color: #E1E1E1; width: 80%; height: 40px; border: none; } ``` ```text ``` ```lua local rule = Instance.new("StyleRule") rule.Parent = coreSheet rule.Selector = ".MenuContainer >> TextButton" -- Descendant selector rule:SetProperties({ BackgroundColor3 = Color3.fromHex("335FFF"), TextColor3 = Color3.fromHex("E1E1E1"), Size = UDim2.new(0.8, 0, 0, 40), BorderSizePixel = 0 }) -- Create menu container local menuContainer = Instance.new("Frame") menuContainer.Size = UDim2.new(0.25, 0, 0, 0) menuContainer.AutomaticSize = Enum.AutomaticSize.Y menuContainer.Parent = screenGui -- Apply tag CollectionService:AddTag(menuContainer, "MenuContainer") -- Create sub-container local subContainer = Instance.new("Frame") subContainer.Size = UDim2.new(0.75, 0, 0, 0) subContainer.AutomaticSize = Enum.AutomaticSize.Y -- Set menu container as parent of sub-container subContainer.Parent = menuContainer -- Create button local button = Instance.new("TextButton") button.Text = "Options" -- Set sub-container as parent of button button.Parent = subContainer ``` ### Selector list Multiple selectors (including selectors with combinators) can be declared with the same property block, separated by commas, to reduce redundancy. ```text img, p { background-color: #FF0033; } ``` ```text

Main Menu

``` ```lua local rule = Instance.new("StyleRule") rule.Parent = coreSheet rule.Selector = "ImageLabel, TextLabel" -- Selector for image labels AND text labels rule:SetProperty("BackgroundColor3", Color3.fromHex("ff0033")) -- Create image label local imageLabel = Instance.new("ImageLabel") imageLabel.Image = "rbxassetid://104919049969988" imageLabel.Size = UDim2.new(0, 100, 0, 100) imageLabel.Parent = screenGui -- Create text label local textLabel = Instance.new("TextLabel") textLabel.Size = UDim2.new(1, 0, 0, 0) textLabel.AutomaticSize = Enum.AutomaticSize.Y textLabel.TextXAlignment = Enum.TextXAlignment.Left textLabel.TextYAlignment = Enum.TextYAlignment.Top textLabel.Text = "Main Menu" textLabel.Parent = screenGui ``` ## Pseudo-classes The Roblox equivalent to CSS [pseudo-class](https://developer.mozilla.org/docs/Web/CSS/Pseudo-classes) selectors are **state selectors** which correspond to one of the four `Enum.GuiState` values such as `Enum.GuiState|Hover` or `Enum.GuiState|Press`. ```text img:hover { opacity: 0.5; } ``` ```text ``` ```lua local rule = Instance.new("StyleRule") rule.Parent = coreSheet rule.Selector = "ImageLabel:Hover" -- State selector rule:SetProperty("ImageTransparency", 0.5) -- Create image label local imageLabel = Instance.new("ImageLabel") imageLabel.Image = "rbxassetid://104919049969988" imageLabel.Size = UDim2.new(0, 100, 0, 100) imageLabel.BackgroundTransparency = 1 imageLabel.Parent = screenGui ``` ## Pseudo-instances Similar to how CSS [pseudo-elements](https://developer.mozilla.org/docs/Web/CSS/Pseudo-elements) can modify specific parts of an element, Roblox can create phantom `Class.UIComponent|UIComponents` through a style rule's `Class.StyleRule.Selector|Selector` property. For example, the following rule effectively creates a `Class.UICorner` modifier under every `Class.Frame` tagged with `RoundedCorner20` and sets each modifier's `Class.UICorner.CornerRadius|CornerRadius` to `20` pixels. ```lua local rule = Instance.new("StyleRule") rule.Parent = coreSheet rule.Selector = "Frame.RoundedCorner20::UICorner" -- UI component selector rule:SetProperty("CornerRadius", UDim.new(0, 20)) -- Create frame local frame = Instance.new("Frame") frame.Size = UDim2.new(0.4, 0, 0.2, 0) frame.Parent = screenGui -- Apply tag to frame CollectionService:AddTag(frame, "RoundedCorner20") ``` ## Queries Roblox **style queries** bridge the gap between CSS [media queries](https://developer.mozilla.org/docs/Web/CSS/CSS_media_queries) and [container queries](https://developer.mozilla.org/docs/Web/CSS/CSS_containment/Container_queries). Using the `@` prefix, you can toggle styles based on parent dimensions, input device types, or user accessibility settings. In CSS, `@container` applies styles based on the size of a parent element. In Roblox, you define a pseudo‑instance query using `::StyleQuery` followed by an [identifier](#identifier) for the condition's `Class.StyleRule.Selector|Selector` property, for example `"::StyleQuery #WideContainer"` , alongside a `Class.StyleQuery` condition name like `"MinSize"` to evaluate the parent's `Class.GuiObject.AbsoluteSize`. Then, to apply styles to the instance or its children when the query is active, create a `Class.StyleRule` with the `@[identifier]` prefix as its `Class.StyleRule.Selector|Selector`, for example `@WideContainer`. ```text .container { container-type: inline-size; background-color: rgba(0, 0, 34, 0.5); } button { background-color: #335FFF; color: #E1E1E1; width: 75%; height: 40px; border: none; } @container (min-width: 400px) { button { background-color: #cc0033; } } ``` ```text
``` ```lua -- Container rule local containerRule = Instance.new("StyleRule") containerRule.Selector = "Frame" -- Roblox class selector containerRule:SetProperties({ BackgroundColor3 = Color3.fromRGB(0, 0, 34), BackgroundTransparency = 0.5, BorderSizePixel = 0 }) containerRule.Parent = coreSheet -- Button rule local buttonRule = Instance.new("StyleRule") buttonRule.Selector = "TextButton" -- Roblox class selector buttonRule:SetProperties({ BackgroundColor3 = Color3.fromHex("335FFF"), TextColor3 = Color3.fromHex("E1E1E1"), Size = UDim2.new(0.75, 0, 0, 40), BorderSizePixel = 0 }) buttonRule.Parent = containerRule -- Child of container rule -- Query condition (#WideContainer) local queryCondition = Instance.new("StyleRule") queryCondition.Selector = "::StyleQuery #WideContainer" queryCondition:SetProperty("MinSize", Vector2.new(400, 0)) queryCondition.Parent = containerRule -- Child of container rule -- Rule that applies when condition is active (@WideContainer) local queryStyle = Instance.new("StyleRule") queryStyle.Selector = "@WideContainer Frame > TextButton" -- Child selector queryStyle:SetProperty("BackgroundColor3", Color3.fromHex("CC0033")) queryStyle.Parent = containerRule -- Child of container rule -- Create instances local container = Instance.new("Frame") container.Size = UDim2.new(0.8, 0, 0, 200) container.Parent = screenGui local button = Instance.new("TextButton") button.Text = "Main Menu" button.Parent = container ``` Roblox also provides **built-in queries** that map directly to global environment states such as `Class.GuiService.ViewportDisplaySize|ViewportDisplaySize` or `Class.GuiService.ReducedMotionEnabled|ReducedMotionEnabled`. These do not require a `::StyleQuery` definition and can be used directly with a `@` prefix. | CSS Media Feature | Roblox Style Query Selector | | --- | --- | | `(max-width: 600px)` | `@ViewportDisplaySizeSmall` | | `(min-width: 601px) and (max-width: 1200px)` | `@ViewportDisplaySizeMedium` | | `(min-width: 1201px)` | `@ViewportDisplaySizeLarge` | | `(pointer: fine)` | `@PreferredInputKeyboardAndMouse` | | `(pointer: coarse)` | `@PreferredInputTouch` | | `(any-pointer: coarse)` | `@PreferredInputGamepad` | | `(prefers-reduced-motion: reduce)` | `@ReducedMotionEnabledTrue` | | `(prefers-reduced-motion: no-preference)` | `@ReducedMotionEnabledFalse` | ```text .container { container-type: inline-size; background-color: rgba(0, 0, 34, 0.5); } @media (prefers-reduced-motion: reduce) { .container { background-color: rgba(0, 0, 34, 1); } } ``` ```text
``` ```lua -- Container rule local containerRule = Instance.new("StyleRule") containerRule.Selector = "Frame" -- Roblox class selector containerRule:SetProperties({ BackgroundColor3 = Color3.fromRGB(0, 0, 34), BackgroundTransparency = 0.5, BorderSizePixel = 0 }) containerRule.Parent = coreSheet -- Built-in query local motionRule = Instance.new("StyleRule") motionRule.Selector = "@ReducedMotionEnabledTrue Frame" motionRule:SetProperty("BackgroundTransparency", 0) motionRule.Parent = containerRule -- Child of container rule -- Create instances local container = Instance.new("Frame") container.Size = UDim2.new(0.8, 0, 0, 200) container.Parent = screenGui ``` ## Variables CSS lets you declare and reference [variables](https://developer.mozilla.org/docs/Web/CSS/--*) throughout a style system. Roblox achieves this through tokens and the [instance attributes](/docs/en-us/studio/properties.md#instance-attributes) system. Using `$` as a prefix, you can reference attributes declared in a `Class.StyleRule` or `Class.StyleSheet` inheritance chain when setting style properties. ```text :root { --button-bg-color: #335FFF; --button-text-color: #E1E1E1; } button { background-color: var(--button-bg-color); color: var(--button-text-color); } ``` ```text ``` ```lua local rule = Instance.new("StyleRule") rule.Parent = coreSheet -- Set style sheet tokens using attributes coreSheet:SetAttribute("ButtonBgColor", Color3.fromHex("335FFF")) coreSheet:SetAttribute("ButtonTextColor", Color3.fromHex("E1E1E1")) rule.Selector = "TextButton" -- Class selector rule:SetProperties({ BackgroundColor3 = "$ButtonBgColor", TextColor3 = "$ButtonTextColor" }) -- Create button local button = Instance.new("TextButton") button.AutomaticSize = Enum.AutomaticSize.XY button.Text = "Main Menu" button.Parent = screenGui ``` ## Transitions CSS [transitions](https://developer.mozilla.org/docs/Web/CSS/Guides/Transitions) let you tween property values over a set duration. You can achieve this in Roblox by setting property transitions on a `Class.StyleRule` through `Class.StyleRule:SetPropertyTransition()|SetPropertyTransition()` (single) or `Class.StyleRule:SetPropertyTransitions()|SetPropertyTransitions()` (multiple). > **Success:** This feature is currently in beta. Enable it through **File** ⟩ **Beta Features** ⟩ **Styling Transitions**. ```text button { background-color: #335FFF; color: #E1E1E1; width: 15%; height: 40px; border: none; transition: background-color 1s ease-out, transform 1.25s ease-out } button:hover { background-color: #33AAFF; transform: rotate(-5deg); } ``` ```text ``` ```lua -- Button rule local buttonRule = Instance.new("StyleRule") buttonRule.Selector = "TextButton" -- Roblox class selector buttonRule:SetProperties({ BackgroundColor3 = Color3.fromHex("335FFF"), TextColor3 = Color3.fromHex("E1E1E1"), Size = UDim2.new(0.15, 0, 0, 40), BorderSizePixel = 0 }) -- Set property transition behaviors buttonRule:SetPropertyTransitions({ BackgroundColor3 = TweenInfo.new(1, Enum.EasingStyle.Cubic, Enum.EasingDirection.Out), Rotation = TweenInfo.new(1.25, Enum.EasingStyle.Cubic, Enum.EasingDirection.Out) }) buttonRule.Parent = coreSheet -- Button hover rule local hoverRule = Instance.new("StyleRule") hoverRule.Selector = "TextButton:Hover" -- State selector hoverRule:SetProperties({ AutoButtonColor = false, BackgroundColor3 = Color3.fromHex("33AAFF"), Rotation = -5 }) hoverRule.Parent = coreSheet -- Create text button local button = Instance.new("TextButton") button.Text = "Main Menu" button.Parent = screenGui ``` ## Nesting and merging Borrowing a concept from [SCSS](https://sass-lang.com/guide/#nesting), `Class.StyleRule|StyleRules` can be nested together and their selectors will **merge**. ```text #menu-frame { background-color: #000022; width: 25%; min-height: 200px; display: flex; flex-direction: column; justify-content: space-evenly; align-items: center; > button { background-color: #335FFF; color: #E1E1E1; width: 80%; height: 40px; border: none; &:hover { opacity: 0.5; } } } ``` ```text ``` ```lua -- Menu frame rule local menuFrameRule = Instance.new("StyleRule") menuFrameRule.Selector = "#MenuFrame" menuFrameRule:SetProperties({ BackgroundColor3 = Color3.fromHex("000022"), Size = UDim2.new(0.25, 0, 0, 200), AutomaticSize = Enum.AutomaticSize.Y }) menuFrameRule.Parent = coreSheet -- Menu layout rule local menuLayoutRule = Instance.new("StyleRule") menuLayoutRule.Selector = "::UIListLayout" menuLayoutRule:SetProperties({ FillDirection = Enum.FillDirection.Vertical, VerticalFlex = Enum.UIFlexAlignment.SpaceEvenly, HorizontalAlignment = Enum.HorizontalAlignment.Center }) menuLayoutRule.Parent = menuFrameRule -- Set menu frame rule as parent -- Button rule local buttonRule = Instance.new("StyleRule") buttonRule.Selector = "> TextButton" buttonRule:SetProperties({ BackgroundColor3 = Color3.fromHex("335FFF"), TextColor3 = Color3.fromHex("E1E1E1"), Size = UDim2.new(0.8, 0, 0, 40), BorderSizePixel = 0 }) buttonRule.Parent = menuFrameRule -- Set menu frame rule as parent -- Button hover rule local buttonHoverRule = Instance.new("StyleRule") buttonHoverRule.Selector = ":Hover" buttonHoverRule:SetProperties({ AutoButtonColor = false, BackgroundTransparency = 0.5, TextTransparency = 0.5 }) buttonHoverRule.Parent = buttonRule -- Set button rule as parent -- Create parent frame local menuFrame = Instance.new("Frame") menuFrame.Name = "MenuFrame" menuFrame.Parent = screenGui -- Create buttons inside frame local button1 = Instance.new("TextButton") button1.Text = "Charms" button1.Parent = menuFrame local button2 = Instance.new("TextButton") button2.Text = "Mana" button2.Parent = menuFrame local button3 = Instance.new("TextButton") button3.Text = "Scrolls" button3.Parent = menuFrame ```
--- title: "Style Editor" url: /docs/en-us/ui/styling/editor last_updated: 2026-06-29T19:34:16Z description: "Explore the built-in Style Editor, a comprehensive tool that allows you to create, manage, and apply UI styles for Roblox games." --- # Style Editor The built-in **Style Editor** is a comprehensive tool that allows you to create, manage, and apply UI styles for Roblox games through a combination of [tokens](#style-tokens), [design sheets](#design-sheets), [style rules](#style-rules), and [themes](#style-themes). Access the **Style Editor** via the **UI** tab. ![Style Editor tool indicated in UI tab of Studio toolbar](../../assets/studio/general/Toolbar-Style-Editor.png) Once open, click the **Create Design** button to generate a base style set. ![Create Design button in opening dialog of Style Editor.](../../assets/ui/ui-styling/SE-Create-Design-Entry.png) > **Warning:** Creating a design style set through the Style Editor generates a **BaseStyleSheet** inside a **Design** folder within `Class.ReplicatedStorage`. This base sheet syncs specific engine‑level defaults like the `Datatype.Color3` default of`[163, 162, 165]` with defaults that come from insertion workflows in Studio. It's highly recommended that you do **not** delete or attempt to modify the base style sheet — instead, build tokens and style rules around a design sheet to match your UI goals. > > Additionally, a **StyleSheet** sheet is generated in the **Design** folder which contains a `Class.StyleDerive` to the base style sheet. If you choose to remove that `Class.StyleDerive`, unstyled values for `Class.GuiObject|GuiObjects` may become unexpected values, for example a default `Class.GuiObject.Size|Size` of`0, 0, 0, 0`. ## Style tokens Style **tokens**, defined through [attributes](/docs/en-us/studio/properties.md#instance-attributes) of a token `Class.StyleSheet`, represent UI property variables that can be used across styles and components; for example, there could be a common color for a `Class.Frame.BackgroundColor3`, `Class.TextLabel.TextColor3`, and `Class.UIStroke.Color`. Tokens are comparable to [CSS variables](/docs/en-us/ui/styling/css-comparisons.md#variables). > **Success:** Although not required for UI styling, tokens are highly recommended for extensible UI design and style updates over time. You should begin by creating the following tokens to understand how they tie into other aspects of universal styling. 1. Create a new token style sheet: 1. In the left column of the Style Editor, hover over **Tokens**, click the **⊕**, and select **New Token StyleSheet**. 2. Rename the new sheet to `TokenSheet`.![New token sheet created in the Style Editor.](../../assets/ui/ui-styling/SE-TokenSheet-New.png) 2. With the new token sheet selected, create several tokens by clicking **Add a Token…** in the main panel. These tokens will be used throughout this guide for both [rules](#style-rules) and [themes](#style-themes).![Token addition field in the Style Editor.](../../assets/ui/ui-styling/SE-TokenSheet-Add-Token.png) | Token Name | Type | Value | | --- | --- | --- | | `Gold` | `Datatype.Color3` | `#ffcc00` | | `Magenta` | `Datatype.Color3` | `#ff0099` | | `Orange` | `Datatype.Color3` | `#dd6030` | | `Oswald` | `Datatype.Font` | `Oswald` | | `Rad20` | `Datatype.UDim` | `0, 20` | | `RectL` | `Datatype.UDim2` | `0, 300, 0, 160` | | `SquareL` | `Datatype.UDim2` | `0, 200, 0, 200` | | `Text24` | number | `24` | | `Text32` | number | `32` |![Tokens added to TokenSheet in Style Editor.](../../assets/ui/ui-styling/SE-TokenSheet-Tokens.png) ## Design sheets A design style sheet aggregates [style rules](#style-rules) and can be linked to `Class.DataModel` trees to apply style properties to UI instances. Only one `Class.StyleSheet` can apply to a given tree, although you can use [themes](#style-themes) to swap related styles across your UI, a concept covered later in this guide. A prepopulated design sheet named **StyleSheet** is created via the **Create Design** button. It contains class rules for common UI objects such as `Class.Frame` and `Class.TextLabel`. It also contains two `Class.StyleDerive` instances which derive (inherit) [tokens](#style-tokens) and styles from the base style sheet for use in your custom styling configurations. ![Initial configuration of design sheet in Style Editor.](../../assets/ui/ui-styling/SE-Initial-Config.png) Once you have a design sheet, you can set up a test [on‑screen container](/docs/en-us/ui/on-screen-containers.md) to use with the Style Editor, or an [in‑experience container](/docs/en-us/ui/in-experience-containers.md) if desired. 1. Hover over `Class.StarterGui` in the [Explorer](/docs/en-us/studio/explorer.md) and insert a `Class.ScreenGui`. 2. Confirm that a new `Class.StyleLink` instance appears under the `Class.ScreenGui` with its `Class.StyleLink.StyleSheet|StyleSheet` property set to the **StyleSheet** design sheet. > **Info:** If the `Class.StyleLink` is not created, you can quickly create one by selecting the `Class.ScreenGui` in the [Explorer](/docs/en-us/studio/explorer.md) as well as the **StyleSheet** design sheet in the left column of the Style Editor. Then, click the **Insert StyleLink** button in the upper‑right section of the editor's main panel. ## Style rules Style **rules** apply to every instance that matches the rule's `Class.StyleRule.Selector|Selector` definition to match characteristics such as class name, instance tag, and hierarchy relationships. At a high level, instance matching and modification via a rule's `Class.StyleRule.Selector|Selector` definition operates through: - Roblox [class](#class-rule) selectors which target all instances of a given UI class, for example `Class.Frame`, `Class.ImageLabel`, `Class.TextButton`, etc. - Instance [tag](#tag-rule) selectors that target specific UI objects [tagged](/docs/en-us/studio/properties.md#instance-tags) through `Class.CollectionService`. - Instance [modifiers](#ui-modifier) applied through phantom `Class.UIComponent|UIComponents` such as `Class.UICorner` or `Class.UIStroke`. - `Class.GuiObject` [state](#state-rule) selectors which correspond to one of the four `Enum.GuiState` enum values such as `Enum.GuiState.Hover|Hover`. - Instance name selection according to the value of the UI object's `Class.Instance.Name`. ### Class rule A style **class** selector targets all instances of a given UI class. The following rule setup styles all `Class.Frame|Frames` with a uniform background color and size. 1. In the left column of the Style Editor, select **Frame** in the **UI Elements** branch.![Default class style in the Style Editor.](../../assets/ui/ui-styling/SE-Class-Rule-New-Class.png) 2. Link two previously created [style tokens](#style-tokens) to two `Class.Frame` properties: | Property | Style Token | | --- | --- | | `Class.Frame.BackgroundColor3\|BackgroundColor3` | `$Magenta` | | `Class.Frame.Size\|Size` | `$SquareL` | 1. Click **Add a Property…** in the main panel and select the necessary property. Note that you can type keywords to more quickly find properties in the dropdown menu. 2. Instead of entering a static value, click the **⋮** button and select **Link Token**. 3. Click the `$` which appears in the value field and select the proper associated token.![Token linking workflow diagrammed in the Style Editor.](../../assets/ui/ui-styling/SE-Class-Rule-Configure.png)![Class rule configured with tokens in the Style Editor.](../../assets/ui/ui-styling/SE-Class-Rule-Tokens.png) 3. Insert a new `Class.Frame` into the `Class.ScreenGui` you previously [created and linked](#design-sheets). The styles you defined should automatically apply to it.![Final styled class rule in the Style Editor.](../../assets/ui/ui-styling/SE-Class-Rule-Result.png) ### Tag rule Instance **tag** selectors target specific UI objects [tagged](/docs/en-us/studio/properties.md#instance-tags) through `Class.CollectionService`. The following rule setup styles a `Class.TextButton` tagged as `ButtonPrimary` with a custom background color, font, and text size. 1. In the left column of the Style Editor, hover over the **StyleSheet** sheet, click the **⋮** button, and navigate through to **New** ⟩ **Tag**.![Creation of tag rule in the Style Editor.](../../assets/ui/ui-styling/SE-Tag-Rule-New.png) 2. Rename the new tag rule `.ButtonPrimary` (note the leading `.`).![Tag rule renamed in the Style Editor.](../../assets/ui/ui-styling/SE-Tag-Rule-Renamed.png) 3. Link three previously created [style tokens](#style-tokens) to three `Class.TextButton` properties: | Property | Style Token | | --- | --- | | `Class.TextButton.BackgroundColor3\|BackgroundColor3` | `$Gold` | | `Class.TextButton.FontFace\|FontFace` | `$Oswald` | | `Class.TextButton.TextSize\|TextSize` | `$Text32` | 1. Click **Add a Property…** in the main panel and select the necessary property. Remember that you can type keywords to more quickly find properties in the dropdown menu. 2. Instead of entering a static value, click the **⋮** button and select **Link Token**. 3. Click the `$` which appears in the value field and select the proper associated token.![Token linking workflow diagrammed in the Style Editor.](../../assets/ui/ui-styling/SE-Tag-Rule-Configure.png)![Tag rule configured with tokens in the Style Editor.](../../assets/ui/ui-styling/SE-Tag-Rule-Tokens.png) 4. Insert a new `Class.TextButton` into the `Class.ScreenGui` you previously [created and linked](#design-sheets) and [tag](/docs/en-us/studio/properties.md#instance-tags) it as `ButtonPrimary`. A convenient shortcut is as follows: 1. Make sure the new `Class.TextButton` is selected in the [Explorer](/docs/en-us/studio/explorer.md). 2. With the **.ButtonPrimary** tag rule selected in the left column of the Style Editor, click **Apply Tag** in the main panel. The styles you defined should automatically apply to the button.![Tag linking workflow diagrammed in the Style Editor.](../../assets/ui/ui-styling/SE-Tag-Rule-Apply-Tag.png)![Final styled tag rule in the Style Editor.](../../assets/ui/ui-styling/SE-Tag-Rule-Result.png) ### UI modifier Instance modifier selectors apply phantom `Class.UIComponent|UIComponents` such as `Class.UICorner` or `Class.UIStroke` to further style an object. The following rule setup styles a `Class.TextLabel` with a custom text size and rounded corners. 1. In the left column of the Style Editor, select **TextLabel** in the **UI Elements** branch.![Default class style in the Style Editor.](../../assets/ui/ui-styling/SE-Modifier-Rule-New-Class.png) 2. Link a previously created [style token](#style-tokens) to the `Class.TextLabel.TextSize|TextSize` property: 1. Click **Add a Property…** in the main panel and select the `Class.TextLabel.TextSize|TextSize` property. Remember that you can type keywords to more quickly find properties in the dropdown menu. 2. Instead of entering a static value, click the **⋮** button and select **Link Token**. 3. Click the `$` which appears in the value field and select the `$Text32` token.![Token linking workflow diagrammed in the Style Editor.](../../assets/ui/ui-styling/SE-Modifier-Rule-Configure.png)![Class rule configured with tokens in the Style Editor.](../../assets/ui/ui-styling/SE-Modifier-Rule-Tokens.png) A **pseudo instance** is required to configure and apply rounded corners to other elements. To create one: 1. In the left column, hover over **TextLabel**, click the **⋮** button, and navigate through to **New** ⟩ **Pseudo Instance** ⟩ **UICorner**.![Creation of a pseudo instance in the Style Editor.](../../assets/ui/ui-styling/SE-Modifier-Rule-Pseudo-Instance-New.png) A new **UICorner** pseudo instance appears under the **TextLabel** element in the left column.![Resulting pseudo instance in the Style Editor.](../../assets/ui/ui-styling/SE-Modifier-Rule-Pseudo-Instance-Created.png) 2. With the new **UICorner** instance selected in the left column, link a previously created [style token](#style-tokens) to the `Class.UICorner.CornerRadius|CornerRadius` property: 1. Click **Add a Property…** in the main panel and select the `Class.UICorner.CornerRadius|CornerRadius` property. 2. Instead of entering a static value, click the **⋮** button and select **Link Token**. 3. Click the `$` which appears in the value field and select the `$Rad20` token.![Pseudo instance configured with tokens in the Style Editor.](../../assets/ui/ui-styling/SE-Modifier-Rule-Pseudo-Instance-Tokens.png) 3. Insert a new `Class.TextLabel` into the `Class.ScreenGui` you previously [created and linked](#design-sheets). The styles you defined should automatically apply to it.![Final styled modifier rule in the Style Editor.](../../assets/ui/ui-styling/SE-Modifier-Rule-Result.png) ### State rule `Class.GuiObject` **state** selectors correspond to one of the four `Enum.GuiState|GuiState` enum values such as `Enum.GuiState|Hover`, letting you automatically configure style changes for interactive states. The following rule setup creates a hover state of `-4` degrees rotation for all `Class.ImageButton|ImageButtons`. 1. In the left column of the Style Editor, select **ImageButton** in the **UI Elements** branch.![Default class style in the Style Editor.](../../assets/ui/ui-styling/SE-State-Rule-New-Class.png) 2. Click the **⋮** button and navigate through to **New** ⟩ **GuiState** ⟩ **Hover**.![Creation of state rule in the Style Editor.](../../assets/ui/ui-styling/SE-State-Rule-New.png) A new **Hover** state modifier instance appears under the **ImageButton** element in the left column.![Resulting state rule in the Style Editor.](../../assets/ui/ui-styling/SE-State-Rule-Created.png) 3. With the new **Hover** modifier selected in the left column, click **Add a Property…** in the main panel and select `Class.ImageButton.Rotation|Rotation`. Remember that you can type keywords to more quickly find properties in the dropdown menu. 4. Enter `-4` in the property's value field.![State rule configured with tokens in the Style Editor.](../../assets/ui/ui-styling/SE-State-Rule-Tokens.png) 5. Insert a new `Class.ImageButton` into the `Class.ScreenGui` you previously [created and linked](#design-sheets). When you hover over the button in the viewport, it should rotate 4 degrees counterclockwise.![Final styled state rule in the Style Editor.](../../assets/ui/ui-styling/SE-State-Rule-Result.png) ## Style queries Style **queries**, similar to [CSS container and media queries](/docs/en-us/ui/styling/css-comparisons.md#queries), allow you to apply conditional logic to your UI based on container size, input type, or other factors. The Style Editor provides a streamlined workflow to create both the query logic (conditions) and the resulting styles as a unified set. ### Built-in queries Some `Class.StyleQuery` conditions are very common, such as changing properties based on the `Class.GuiService.ViewportDisplaySize|ViewportDisplaySize`. To simplify this, the Style Editor includes **built‑in** queries which allow you to create conditional rules without needing a pseudo or real instance of a `Class.StyleQuery`; instead, you simply use a query rule with a predefined selector like `ViewportDisplaySizeSmall`. The following steps demonstrate how to generate built‑in queries for a `Class.TextLabel` to handle `Enum.DisplaySize.Small|Small`, `Enum.DisplaySize.Medium|Medium`, and `Enum.DisplaySize.Large|Large` viewport sizes. 1. In the left column of the Style Editor, hover over **TextLabel**, click the **⋮** button, and navigate through to **New** ⟩ **StyleQuery** ⟩ **Builtin** ⟩ **ViewportDisplaySize**.![Creation of a built-in StyleQuery in the Style Editor.](../../assets/ui/ui-styling/SE-Query-New-Builtin.png) This creates a **Queries** folder under the **TextLabel** rule containing three query rules: - **@ViewportDisplaySizeLarge** — Define properties that apply when the viewport is `Enum.DisplaySize.Large|Large`. - **@ViewportDisplaySizeMedium** — Define properties that apply when the viewport is `Enum.DisplaySize.Medium|Medium`. - **@ViewportDisplaySizeSmall** — Define properties that apply when the viewport is `Enum.DisplaySize.Small|Small`.![Hierarchy of a StyleQuery within a class rule.](../../assets/ui/ui-styling/SE-Query-Builtin-Hierarchy.png) 2. Click **Add a Property…** under each query rule and set the specific `Class.TextLabel.Text|Text` string that should display when that query is active: | Query Rule | Property | Value | | --- | --- | --- | | **@ViewportDisplaySizeLarge** | `Class.TextLabel.Text\|Text` | `Large Text` | | **@ViewportDisplaySizeMedium** | `Class.TextLabel.Text\|Text` | `Medium Text` | | **@ViewportDisplaySizeSmall** | `Class.TextLabel.Text\|Text` | `Small Text` | 3. To verify the queries, use the [Device Emulator](/docs/en-us/studio/testing-modes.md#device-emulation) to test how the UI responds to different resolutions across three distinct devices: 1. Insert a new `Class.TextLabel` into the `Class.ScreenGui`. 2. Change the **Device** in the [Device Emulator](/docs/en-us/studio/testing-modes.md#device-emulation) to see the text change in real time. | Size | Example Emulations | | --- | --- | | `Enum.DisplaySize.Large\|Large` | **PS5**, **PS4**, **Xbox One** | | `Enum.DisplaySize.Medium\|Medium` | **Average Laptop**, **HD 720** | | `Enum.DisplaySize.Small\|Small` | **iPhone XR**, **iPad 7th Generation** | ### Custom query You can add a style query to any existing rule to create responsive UI that adapts to its container. The following setup configures a `Class.TextLabel` to automatically change its appearance only when it expands to a width of 400 pixels or more. 1. In the left column of the Style Editor, hover over **TextLabel**, click the **⋮** button, and navigate through to **New** ⟩ **StyleQuery** ⟩ **Custom**.![Creation of a custom StyleQuery in the Style Editor.](../../assets/ui/ui-styling/SE-Query-New-Custom.png) This action creates a **Queries** folder under the **TextLabel** rule containing two linked items: - **::StyleQuery** pseudo-instance rule where you define the **Conditions**. - **@StyleQuery** rule where you define the **Properties** to apply when those conditions are met.![Hierarchy of a StyleQuery within a class rule.](../../assets/ui/ui-styling/SE-Query-Custom-Hierarchy.png) 2. Select the parent **TextLabel** rule in the main panel and define the following default properties: | Property | Value | | --- | --- | | `Class.TextLabel.BackgroundColor3\|BackgroundColor3` | `$Magenta` | | `Class.TextLabel.Text\|Text` | `Default Text` | 3. Select the **::StyleQuery** rule in the main panel, click **Add a Condition…**, and configure the following: | Condition | Value | | --- | --- | | `MinSize` | `400, 0` | > **Info:** See `Class.StyleQuery.IsActive` as a reference for other conditions available through the **::StyleQuery** rule. 4. Select the **@StyleQuery** rule in the main panel and set the properties that should apply when the query is active: | Property | Value | | --- | --- | | `Class.TextLabel.BackgroundColor3\|BackgroundColor3` | `$Gold` | | `Class.TextLabel.Text\|Text` | `Large Text` | To verify the query, you can manually resize a `Class.TextLabel` in the viewport. 1. Insert a new `Class.TextLabel` into the `Class.ScreenGui`. 2. Adjust its width to see the style swap in real-time. #### 0–400 Pixel Width While the `Class.TextLabel` width is smaller, the `MinSize` condition is not met and the label uses its default background color and text.![TextLabel appearing magenta with 'Default Text' on a phone viewport.](../../assets/ui/ui-styling/SE-Query-Test-Small.png) #### Over 400 Pixel Width As you drag the `Class.TextLabel` wider than 400 pixels, the query becomes active and the `@StyleQuery` immediately overrides the base style.![TextLabel appearing gold with 'Large Text' on the same phone viewport.](../../assets/ui/ui-styling/SE-Query-Test-Large.png) > **Info:** While manual resizing is a quick way to check responsive logic, you can also use Studio's built‑in emulation tools for more precise testing: - Use the [Device Emulator](/docs/en-us/studio/testing-modes.md#device-emulation) to test how your UI responds to specific screen resolutions and `Class.GuiService.ViewportDisplaySize|ViewportDisplaySize` conditions. - Use the [Controller Emulator](/docs/en-us/studio/testing-modes.md#controller-simulation) to verify `Class.UserInputService.PreferredInput|PreferredInput` logic, such as switching styles when a gamepad is detected. ## Style transitions Style **transitions**, comparable to [CSS transitions](/docs/en-us/ui/styling/css-comparisons.md#transitions), let you tween property values over a set duration. > **Success:** This feature is currently in beta. Enable it through **File** ⟩ **Beta Features** ⟩ **Styling Transitions**. ### Default To tween many UI properties with the same parameters, you can set a default transition that applies to **all** properties unless another transition is explicitly set for a specific property. 1. Select **TextButton** in the **UI Elements** branch and then, in the main panel, add three new properties with the following values: | Property | Value | | --- | --- | | `Class.TextButton.AutoButtonColor\|AutoButtonColor` | `false` | | `Class.TextButton.BackgroundColor3\|BackgroundColor3` | `#335FFF` | | `Class.TextButton.Rotation\|Rotation` | `0` |![Class style in the Style Editor.](../../assets/ui/ui-styling/SE-Transition-Rule-New-Class.png) 2. Hover over the parent **TextButton** row, click the ⋮ button, and navigate through to **Insert** ⟩ **Transition**.![Add default transition to a rule in the Style Editor.](../../assets/ui/ui-styling/SE-Transition-Rule-Add-Default-Transition.png) A default transition of `1, Quad, Out, 0` inserts directly under the **TextButton** rule, indicating a transition of 1 second, easing style `Enum.EasingStyle.Quad|Quad`, easing direction `Enum.EasingDirection.Out|Out`, and no delay.![Default transition highlighted in the Style Editor.](../../assets/ui/ui-styling/SE-Transition-Rule-Default-Transition-Added.png) 3. Hover over the parent **TextButton** row, click the **⋮** button, and navigate through to **New** ⟩ **GuiState** ⟩ **Hover**.![Creation of state rule in the Style Editor.](../../assets/ui/ui-styling/SE-Transition-Rule-Default-New-State.png) 4. For the new **Hover** modifier, add two new properties as the **targets** for the hover state. | Property | Value | | --- | --- | | `Class.TextButton.BackgroundColor3\|BackgroundColor3` | `#33AAFF` | | `Class.TextButton.Rotation\|Rotation` | `-5` |![Properties on hover state rule in the Style Editor.](../../assets/ui/ui-styling/SE-Transition-Rule-State-Properties.png) 5. Insert a new `Class.TextButton` into the `Class.ScreenGui` you previously [created and linked](#design-sheets). When you hover over the button in the viewport, it should gradually brighten and rotate 5 degrees counterclockwise.![Final styled state rule in the Style Editor.](../../assets/ui/ui-styling/SE-Transition-Rule-Result.png) ### Token-linked To reuse transition behaviors across multiple rules, you can define **tokens** representing the `Datatype.TweenInfo` data type. The following rule setup creates a hover state to transition all `Class.TextButton|TextButtons` to/from -5 degrees rotation and a lighter background color. 1. If you haven't already, create a [token style sheet](#style-tokens) named `TokenSheet`.![New token sheet created in the Style Editor.](../../assets/ui/ui-styling/SE-TokenSheet-New.png) 2. With the new token sheet selected, create two new tokens by clicking **Add a Token…** in the main panel. | Token Name | Type | Tween Parameters | | --- | --- | --- | | `CubicOut` | `Datatype.TweenInfo` | | | `QuadInOut` | `Datatype.TweenInfo` | |![Tokens added to TokenSheet in Style Editor.](../../assets/ui/ui-styling/SE-Transition-Rule-Tokens-Defined.png) 3. Select **TextButton** in the **UI Elements** branch and then, in the main panel, add three new properties with the following values: | Property | Value | | --- | --- | | `Class.TextButton.AutoButtonColor\|AutoButtonColor` | `false` | | `Class.TextButton.BackgroundColor3\|BackgroundColor3` | `#335FFF` | | `Class.TextButton.Rotation\|Rotation` | `0` |![Class style in the Style Editor.](../../assets/ui/ui-styling/SE-Transition-Rule-New-Class.png) 4. For each new property except for `Class.TextButton.AutoButtonColor|AutoButtonColor`, hover over its row, click the **⋮** button, and select **Insert** ⟩ **Transition**.![Add transition to a property in the Style Editor.](../../assets/ui/ui-styling/SE-Transition-Rule-Add-Transition.png) 5. Hover over the new **Transition** row under each property, click the **⋮** button, and select **Link Token**. Then, instead of entering a static value, click the `$` which appears in the value field and select the proper transition token.![Token linking workflow diagrammed in the Style Editor.](../../assets/ui/ui-styling/SE-Transition-Rule-Tokens-Link.png) | Property Transition | Transition Token | | --- | --- | | `Class.TextButton.BackgroundColor3\|BackgroundColor3` | `$CubicOut` | | `Class.TextButton.Size\|Size` | `$QuadInOut` |![Transition tokens linked in the Style Editor.](../../assets/ui/ui-styling/SE-Transition-Rule-Tokens-Linked.png) 6. Hover over the parent **TextButton** row, click the **⋮** button, and navigate through to **New** ⟩ **GuiState** ⟩ **Hover**.![Creation of state rule in the Style Editor.](../../assets/ui/ui-styling/SE-Transition-Rule-New-State.png) 7. For the new **Hover** modifier, add two new properties as the **targets** for the hover state. | Property | Value | | --- | --- | | `Class.TextButton.BackgroundColor3\|BackgroundColor3` | `#33AAFF` | | `Class.TextButton.Rotation\|Rotation` | `-5` |![Properties on hover state rule in the Style Editor.](../../assets/ui/ui-styling/SE-Transition-Rule-State-Properties.png) 8. Insert a new `Class.TextButton` into the `Class.ScreenGui` you previously [created and linked](#design-sheets). When you hover over the button in the viewport, it should gradually brighten and rotate 5 degrees counterclockwise.![Final styled state rule in the Style Editor.](../../assets/ui/ui-styling/SE-Transition-Rule-Result.png) ## Style themes Style **themes** consist of sets of specific tokens that can be swapped, for example color tokens that define a "light" and "dark" theme. ### Theme creation For extensibility, themes are organized into folders. While a single folder may suffice for most purposes, you're free to organize themes in folders like "colors" or "fonts" if desired. 1. Create a new theme folder: 1. In the left column of the Style Editor, hover over **Themes**, click the **⊕**, and select **New Theme**. 2. Rename the new **Folder** item to **General**.![New themes folder created in the Style Editor.](../../assets/ui/ui-styling/SE-Themes-New-Folder.png) 2. Create a new theme style sheet: 1. Hover over the new **General** folder, click the **⋮** button, and select **New Theme StyleSheet**. 2. Rename it to **ThemeA**.![New theme created in the Style Editor.](../../assets/ui/ui-styling/SE-Themes-New-Theme.png) ### Theme tokens Once a theme is constructed, you can link its tokens to various UI object properties such as the `Class.TextButton.BackgroundColor3|BackgroundColor3` of a `Class.TextButton`. Theme sheets must use a common set of tokens to work correctly. With **ThemeA** selected in the left column of the Style Editor: 1. Link three previously created [style tokens](#style-tokens) to three new theme tokens: | Theme Token | Style Token | | --- | --- | | `BackColor` | `$Gold` | | `ButtonFont` | `$Oswald` | | `ButtonTextSize` | `$Text32` | 1. Click **Add a Token…** in the main panel and enter the theme token's name. 2. Click the `$` which appears in the value field and select the associated style token.![Theme configured with tokens in the Style Editor.](../../assets/ui/ui-styling/SE-Themes-ThemeA-Tokens.png) 2. In the left column of the Style Editor, select **TextButton** in the **UI Elements** branch.![Default class style in the Style Editor.](../../assets/ui/ui-styling/SE-Themes-New-Class.png) 3. Link the theme's tokens to three new `Class.TextButton` properties: | Property | Theme Token | | --- | --- | | `Class.TextButton.BackgroundColor3\|BackgroundColor3` | `$BackColor` | | `Class.TextButton.FontFace\|FontFace` | `$ButtonFont` | | `Class.TextButton.TextSize\|TextSize` | `$ButtonTextSize` | 1. Click **Add a Property…** in the main panel and select the necessary property. 2. Instead of entering a static value, click the **⋮** button and select **Link Token**. 3. Click the `$` which appears in the value field and select the proper theme token.![Token linking workflow diagrammed in the Style Editor.](../../assets/ui/ui-styling/SE-Themes-Theme-Configure.png) > **Warning:** When linking the theme's tokens, look for **$[Token] in ThemeA** in the hover‑over to know the token is coming from the **theme** and not from the global token sheet (remember that the theme already derives the global tokens).![Class rule configured with theme tokens in the Style Editor.](../../assets/ui/ui-styling/SE-Themes-Class-Tokens.png) ### Theme duplication Once you have a theme generally established, you can **duplicate** it and change various tokens to define a unique theme style. 1. In the left column of the Style Editor, hover over the **ThemeA** theme, click the **⋮** button, and select **Duplicate**. 2. Rename the duplicated theme to **ThemeB**.![Theme duplicated in the Style Editor.](../../assets/ui/ui-styling/SE-Themes-Duplicated-Theme.png) 3. Link two of the theme's tokens to two different style tokens: | Theme Token | Style Token | | --- | --- | | `BackColor` | `$Magenta` | | `ButtonTextSize` | `$Text24` | 1. To the right of the property's value field, click the **⋮** button and select **Unlink Token**. 2. Click **⋮** again and select **Link Token**. 3. In its value field, click the `$` and select the new associated style token.![Theme configured with tokens in the Style Editor.](../../assets/ui/ui-styling/SE-Themes-ThemeB-Tokens.png) ### Theme swapping Once you have [multiple themes](#theme-duplication), you can swap between them via the theme's folder, or via a script as outlined in `Class.StyleSheet:SetDerives()|SetDerives()`. 1. In the left column of the Style Editor, select the **General** folder in the **Themes** branch. 2. In the main panel, swap between the themes using the radio buttons. #### ThemeA![ThemeA swapped in the Style Editor.](../../assets/ui/ui-styling/SE-Themes-Swapping-ThemeA.png) #### ThemeB![ThemeB swapped in the Style Editor.](../../assets/ui/ui-styling/SE-Themes-Swapping-ThemeB.png) --- title: "UI styling" url: /docs/en-us/ui/styling last_updated: 2026-06-29T19:34:16Z description: "Explore how stylesheets let you declare and globally apply overrides to UI instance properties." --- # UI styling UI styling is a Roblox solution to stylesheets, [similar to CSS](/docs/en-us/ui/css-comparisons.md), that lets you declare and globally apply overrides to UI instance properties. This engine‑level support is the foundation for the [Style Editor](/docs/en-us/ui/editor.md) and the end‑to‑end token pipeline. ## Concepts Style **rules** (part of a `Class.StyleSheet`) apply to every instance that matches the rule's `Class.StyleRule.Selector|Selector` definition to match characteristics such as class name, instance name, and hierarchy relationships. See the `Class.StyleRule.Selector|Selector` documentation for details. Style **tokens**, defined through [attributes](/docs/en-us/studio/properties.md#instance-attributes) of a token `Class.StyleSheet`, represent UI property variables that can be used across styles and components, for example a common color for a `Class.Frame.BackgroundColor3`, `Class.TextLabel.TextColor3`, and `Class.UIStroke.Color`. Tokens are comparable to CSS [variables](/docs/en-us/ui/css-comparisons.md#variables). Style **themes**, configured through [attributes](/docs/en-us/studio/properties.md#instance-attributes) of a theme `Class.StyleSheet`, consist of sets of specific tokens that can be swapped, for example color tokens that define a "light" and "dark" theme. Related themes must have the same set of tokens to work correctly. Style **queries** allow you to apply styles conditionally based on the UI's context or environment. These are comparable to CSS [container queries and media queries](/docs/en-us/ui/css-comparisons.md#queries), enabling UI to adapt to screen size, input types, or user accessibility settings. ## Rule propagation A `Class.StyleLink` instance links a `Class.StyleSheet` and its associated rules to a parent `Class.ScreenGui` and all of the `Class.GuiObject|GuiObjects` within it. Only one `Class.StyleSheet` can apply to a given tree. ## Selector definitions At a high level, instance matching and modification via a rule's `Class.StyleRule.Selector|Selector` definition operates through: - Roblox **class** selectors which target all instances of a given `Class.GuiObject` class, for example `Class.Frame`, `Class.ImageLabel`, `Class.TextButton`, etc. - Instance [tags](/docs/en-us/studio/properties.md#instance-tags) applied to specific UI objects through `Class.CollectionService`. - Instance **name** selection according to the value of the UI object's `Class.Instance.Name`. - Instance **modifiers**, similar to [CSS pseudo-elements](/docs/en-us/ui/css-comparisons.md#pseudo-instances), applied through phantom `Class.UIComponent|UIComponents` such as `Class.UICorner` or `Class.UIStroke`. - `Class.GuiObject` **state** selectors, similar to [CSS pseudo-class](/docs/en-us/ui/css-comparisons.md#pseudo-classes) selectors, which correspond to one of the four `Enum.GuiState|GuiState` enum values such as `Enum.GuiState|Hover`. - Roblox **query** selectors, similar to [CSS container and media queries](/docs/en-us/ui/css-comparisons.md#queries), which use the `@` prefix to apply styles only while a `Class.StyleQuery` instance or pseudo‑instance with the same name is active. #### Class Selector The following setup shows how size and color tokens, a theme, and a class selector of `"Frame"` produce a magenta `Class.Frame` of size 200×200 pixels. ** Code Equivalent** ```lua local CollectionService = game:GetService("CollectionService") local ReplicatedStorage = game:GetService("ReplicatedStorage") local screenGui = script.Parent -- Tokens local tokens = Instance.new("StyleSheet") tokens.Name = "Tokens" tokens.Parent = ReplicatedStorage tokens:SetAttribute("SquareS", UDim2.fromOffset(50, 50)) tokens:SetAttribute("SquareM", UDim2.fromOffset(100, 100)) tokens:SetAttribute("SquareL", UDim2.fromOffset(200, 200)) tokens:SetAttribute("Fit", UDim2.fromScale(1, 1)) tokens:SetAttribute("Magenta", Color3.fromHex("FF0099")) tokens:SetAttribute("Gold", Color3.fromHex("FFCC00")) tokens:SetAttribute("Aqua", Color3.fromHex("0093F0")) -- ThemeA local themeA = Instance.new("StyleSheet") themeA.Name = "ThemeA" themeA.Parent = ReplicatedStorage local tokensDerive = Instance.new("StyleDerive") tokensDerive.StyleSheet = tokens -- Derive global tokens tokensDerive.Parent = themeA themeA:SetAttribute("FrameSize", "$SquareM") themeA:SetAttribute("FrameColor", "$Aqua") -- ThemeB local themeB = Instance.new("StyleSheet") themeB.Name = "ThemeB" themeB.Parent = ReplicatedStorage local tokensDerive = Instance.new("StyleDerive") tokensDerive.StyleSheet = tokens -- Derive global tokens tokensDerive.Parent = themeB themeB:SetAttribute("FrameSize", "$SquareL") themeB:SetAttribute("FrameColor", "$Magenta") -- DesignSheet local designSheet = Instance.new("StyleSheet") designSheet.Name = "DesignSheet" designSheet.Parent = ReplicatedStorage local themeDerive = Instance.new("StyleDerive") themeDerive.StyleSheet = themeB -- Derive from ThemeB themeDerive.Parent = designSheet -- Link DesignSheet to ScreenGui local styleLink = Instance.new("StyleLink") styleLink.StyleSheet = designSheet styleLink.Parent = screenGui -- Configure rule local rule = Instance.new("StyleRule") rule.Selector = "Frame" -- Class selector rule.Parent = designSheet -- Set rule properties rule:SetProperties({ ["BackgroundColor3"] = "$FrameColor", ["Size"] = "$FrameSize", ["BorderSizePixel"] = 0 }) local frame = Instance.new("Frame") frame.Parent = screenGui ``` #### Tag Selector This setup shows how size/color/font tokens, a theme, and a tag selector of `".ButtonPrimary"` produces a gold button using the `Enum.Font|Oswald` font at `32` pixels. ** Code Equivalent** ```lua local CollectionService = game:GetService("CollectionService") local ReplicatedStorage = game:GetService("ReplicatedStorage") local screenGui = script.Parent -- Tokens local tokens = Instance.new("StyleSheet") tokens.Name = "Tokens" tokens.Parent = ReplicatedStorage tokens:SetAttribute("Text24", 24) tokens:SetAttribute("Text32", 32) tokens:SetAttribute("Magenta", Color3.fromHex("FF0099")) tokens:SetAttribute("Gold", Color3.fromHex("FFCC00")) tokens:SetAttribute("Aqua", Color3.fromHex("0093F0")) tokens:SetAttribute("DenkOne", Enum.Font.DenkOne) tokens:SetAttribute("Oswald", Enum.Font.Oswald) -- ThemeA local themeA = Instance.new("StyleSheet") themeA.Name = "ThemeA" themeA.Parent = ReplicatedStorage local tokensDerive = Instance.new("StyleDerive") tokensDerive.StyleSheet = tokens -- Derive global tokens tokensDerive.Parent = themeA themeA:SetAttribute("BackColor", "$Gold") themeA:SetAttribute("TextSize", "$Text32") themeA:SetAttribute("ButtonFont", "$Oswald") -- ThemeB local themeB = Instance.new("StyleSheet") themeB.Name = "ThemeB" themeB.Parent = ReplicatedStorage local tokensDerive = Instance.new("StyleDerive") tokensDerive.StyleSheet = tokens -- Derive global tokens tokensDerive.Parent = themeB themeB:SetAttribute("BackColor", "$Aqua") themeB:SetAttribute("TextSize", "$Text24") themeB:SetAttribute("ButtonFont", "$DenkOne") -- DesignSheet local designSheet = Instance.new("StyleSheet") designSheet.Name = "DesignSheet" designSheet.Parent = ReplicatedStorage local themeDerive = Instance.new("StyleDerive") themeDerive.StyleSheet = themeA -- Derive from ThemeA themeDerive.Parent = designSheet -- Link DesignSheet to ScreenGui local styleLink = Instance.new("StyleLink") styleLink.StyleSheet = designSheet styleLink.Parent = screenGui -- Configure rule local rule = Instance.new("StyleRule") rule.Selector = ".ButtonPrimary" -- Tag selector rule.Parent = designSheet -- Set rule properties rule:SetProperties({ ["BackgroundColor3"] = "$BackColor", ["TextSize"] = "$TextSize", ["Font"] = "$ButtonFont", ["BorderSizePixel"] = 0 }) local menuButton = Instance.new("TextButton") menuButton.Size = UDim2.fromOffset(200, 44) menuButton.Text = "Menu" menuButton.Parent = screenGui CollectionService:AddTag(menuButton, "ButtonPrimary") -- Apply tag to button ``` #### UI Modifier Selector This setup shows how tokens, a theme, a class selector of `"Frame"`, and a nested UI modifier selector of `"::UICorner"` produce a 300×160 orange `Class.Frame` with a `Class.UICorner` modifier of `20` pixels. ** Code Equivalent** ```lua local CollectionService = game:GetService("CollectionService") local ReplicatedStorage = game:GetService("ReplicatedStorage") local screenGui = script.Parent -- Tokens local tokens = Instance.new("StyleSheet") tokens.Name = "Tokens" tokens.Parent = ReplicatedStorage tokens:SetAttribute("RectS", UDim2.fromOffset(75, 40)) tokens:SetAttribute("RectM", UDim2.fromOffset(150, 80)) tokens:SetAttribute("RectL", UDim2.fromOffset(300, 160)) tokens:SetAttribute("Magenta", Color3.fromHex("FF0099")) tokens:SetAttribute("Orange", Color3.fromHex("DD6030")) tokens:SetAttribute("Radius20", UDim.new(0, 20)) tokens:SetAttribute("RadiusPill", UDim.new(0.5, 0)) -- ThemeA local themeA = Instance.new("StyleSheet") themeA.Name = "ThemeA" themeA.Parent = ReplicatedStorage local tokensDerive = Instance.new("StyleDerive") tokensDerive.StyleSheet = tokens -- Derive global tokens tokensDerive.Parent = themeA themeA:SetAttribute("FrameSize", "$RectM") themeA:SetAttribute("FrameColor", "$Magenta") themeA:SetAttribute("CornerRad", "$Radius20") -- ThemeB local themeB = Instance.new("StyleSheet") themeB.Name = "ThemeB" themeB.Parent = ReplicatedStorage local tokensDerive = Instance.new("StyleDerive") tokensDerive.StyleSheet = tokens -- Derive global tokens tokensDerive.Parent = themeB themeB:SetAttribute("FrameSize", "$RectL") themeB:SetAttribute("FrameColor", "$Orange") themeB:SetAttribute("CornerRad", "$Radius20") -- DesignSheet local designSheet = Instance.new("StyleSheet") designSheet.Name = "DesignSheet" designSheet.Parent = ReplicatedStorage local themeDerive = Instance.new("StyleDerive") themeDerive.StyleSheet = themeB -- Derive from ThemeB themeDerive.Parent = designSheet -- Link DesignSheet to ScreenGui local styleLink = Instance.new("StyleLink") styleLink.StyleSheet = designSheet styleLink.Parent = screenGui -- Configure rules local frameRule = Instance.new("StyleRule") frameRule.Selector = "Frame" -- Class selector frameRule.Parent = designSheet local modifierRule = Instance.new("StyleRule") modifierRule.Selector = "::UICorner" -- UI modifier selector modifierRule.Parent = frameRule -- Set rule properties frameRule:SetProperties({ ["BackgroundColor3"] = "$FrameColor", ["Size"] = "$FrameSize", ["BorderSizePixel"] = 0 }) modifierRule:SetProperty("CornerRadius", "$CornerRad") local frame = Instance.new("Frame") frame.Parent = screenGui ``` ## Modified properties Instance properties affected by styling are flagged with a **⚠** in the **Properties** window, and hovering over an affected property shows which style is influencing it. For example, if a style rule tells a tagged `Class.Frame` to use an `Class.Frame.AnchorPoint|AnchorPoint` of `0.5, 0.5` , that object property will show the **default** value of `0, 0` but its actual `Class.Frame.AnchorPoint|AnchorPoint` will use the rule's **styled** value. When a default or styled property value is further modified for a specific UI object, it becomes **bold** to indicate an overridden value. For example — assuming a default `Class.Frame.AnchorPoint|AnchorPoint` of `0, 0`  — setting that property to `1, 1` for a specific instance reveals an override in bold: #### Default Property Value #### Overridden Property Value For any overridden property value, you can right-click it in the **Properties** window and select **Reset to Default** to revert it to the styled value, or to the default property value if it hasn't been styled. --- title: "Text filtering" url: /docs/en-us/ui/text-filtering last_updated: 2026-06-29T19:34:16Z description: "Text filtering prevents users from seeing inappropriate language and blocks personally identifiable information." --- # Text filtering Applied to various sources and inputs, **text filtering** prevents users from seeing inappropriate language and personally identifiable information such as phone numbers. Roblox automatically filters common text outputs such as messages that have passed through [in-experience text chat](/docs/en-us/chat/in-experience-text-chat.md), but **you are responsible for filtering any displayed text that you don't have explicit control over**. > **Error:** Because filtering is crucial for a safe environment, Roblox actively moderates the content of experiences to make sure they meet [community standards](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards). If Roblox receives reports or automatically detects that your experience doesn't apply text filtering, then the system removes the experience until you add filtering. ## Filter scenarios Text can be gathered and/or displayed to users in a variety of scenarios, including: - An experience that gathers users' [text input](/docs/en-us/ui/text-input.md) through `Class.TextBox` entries, a custom GUI with buttons such as a keyboard/keypad interface, or an interactive keyboard model in the 3D space. - An experience that generates words from random characters and displays them to users, as there's a chance it will create inappropriate words. - An experience that connects to an external web server to fetch content that is displayed in-experience. Often you will not have control over the content of the external site and a third party can edit the information. - An experience that stores text such as users' pet names using [data stores](/docs/en-us/cloud-services/data-stores.md), where the stored text might include inappropriate words that should be filtered when retrieving them. ## Filtering process `Class.TextService:FilterStringAsync()` filters in-experience text by taking a string of text and the `Class.Player.UserId|UserId` of the user who created the text as input. It returns a `Class.TextFilterResult` object which has two additional methods that you can call in different scenarios: - `Class.TextFilterResult:GetNonChatStringForBroadcastAsync()` for filtering text visible to all users in an experience, such as for a dialog that lets a user write a message on a sign that's visible to all users on the server. - `Class.TextFilterResult:GetNonChatStringForUserAsync()` to display filtered text for one specific user, based on age and other details. In the context of `Class.TextBox` input, the following example gathers input on the `Class.TextBox.FocusLost|FocusLost` event and sends it to the server through a `Class.RemoteEvent`. On the server, it is filtered first through `Class.TextService:FilterStringAsync()|FilterStringAsync()` and then `Class.TextFilterResult:GetNonChatStringForBroadcastAsync()|GetNonChatStringForBroadcastAsync()` with the intention that the text will be displayed to all users on a server‑side object such as a `Class.SurfaceGui` in the 3D world. > **Warning:** Do not filter text in real time "per character entered" into a `Class.TextBox`, as doing so yields for text that's only visible to the user typing it. Instead, filter the entered text **after** the user submits it. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local textBox = script.Parent textBox.ClearTextOnFocus = false textBox.PlaceholderText = "..." textBox.TextXAlignment = Enum.TextXAlignment.Left textBox.TextScaled = true -- RemoteEvent to send text input to server for filtering local inputRemoteEvent = ReplicatedStorage:FindFirstChild("InputRemoteEvent") -- Event handler for focus lost and enter being pressed local function onFocusLost(enterPressed, inputObject) if enterPressed then print("SUBMITTED:", textBox.Text) if inputRemoteEvent then inputRemoteEvent:FireServer(textBox.Text) end end end textBox.FocusLost:Connect(onFocusLost) ``` ```lua local TextService = game:GetService("TextService") local ReplicatedStorage = game:GetService("ReplicatedStorage") -- RemoteEvent to receive text input from client for filtering local inputRemoteEvent = ReplicatedStorage:FindFirstChild("InputRemoteEvent") local function getFilterResult(text, fromUserId) local filterResult local success, errorMessage = pcall(function() filterResult = TextService:FilterStringAsync(text, fromUserId) end) if success then return filterResult else warn("Error generating TextFilterResult:", errorMessage) end end -- Fired when client submits input from the TextBox local function onInputReceived(player, text) if text ~= "" then local filterResult = getFilterResult(text, player.UserId) if filterResult then local success, filteredText = pcall(function() return filterResult:GetNonChatStringForBroadcastAsync() end) if success then print("FILTERED:", filteredText) else warn("Error filtering text!") end end end end inputRemoteEvent.OnServerEvent:Connect(onInputReceived) ``` --- title: "Text input fields" url: /docs/en-us/ui/text-input last_updated: 2026-06-29T19:34:16Z description: "Text input fields allow users to input text from their physical or on-screen keyboard." --- # Text input fields A `Class.TextBox` is a rectangle that allows a user to provide text input while it's in focus. When you [script](#script-text-inputs) a `Class.TextBox`, you can use it as a search bar or an input field on a form. To help users know what type of text they should input, you can also provide a prompt through the `Class.TextBox.PlaceholderText|PlaceholderText` property. Because these objects are `Class.GuiObject|GuiObjects`, you can customize properties such as `Class.GuiObject.BackgroundColor3|BackgroundColor3`, `Class.GuiObject.BorderMode|BorderMode`, `Class.GuiObject.Transparency|Transparency`, and `Class.GuiObject.Rotation|Rotation` to fit the aesthetics of your experience. ## Create text inputs on the screen A `Class.TextBox` on a screen is useful for things like an input field for a form. To add a `Class.TextBox` to a screen: 1. In the **Explorer** window, select **StarterGui** and add a **ScreenGui**. 1. Hover over StarterGui and click the ⊕ button. A contextual menu displays. 2. Insert a **ScreenGui**. 2. Select the new **ScreenGui** and add a **TextBox**. 1. Hover over **ScreenGui** and click the ⊕ button. A contextual menu displays. 2. Insert a **TextBox**. ## Create text inputs on part faces To add a `Class.TextBox` to the face of a part: 1. In the **Explorer** window, select **StarterGui** and add a **SurfaceGui**. 1. Hover over StarterGui and click the ⊕ button. A contextual menu displays. 2. Insert a **ScreenGui**. 2. Select the new **SurfaceGui** and add a **TextBox**. 1. Hover over **SurfaceGui** and click the ⊕ button. A contextual menu displays. 2. Insert a **TextBox**. 3. Adorn the **SurfaceGui** to the **part** on which you want to display the **TextBox**. 1. In the **Properties** window, select the **Adornee** property. Your cursor changes. 2. In the **Explorer** window, select the part. > **Warning:** If you don't see the `Class.TextBox`, try [choosing a different face](/docs/en-us/parts/textures-decals.md#choose-a-face) in the **Face** property of the **SurfaceGui**. ## Script text inputs Like [buttons](/docs/en-us/ui/buttons.md), you can script any action for a `Class.TextBox` object when a user interacts with it. For example, the following script connects the `Class.TextBox.FocusLost|FocusLost` event which fires when the user presses the `Enter` button or clicks outside the box. If `enterPressed` is `true`, meaning the user submitted the input instead of merely clicking outside the box, the script prints the contents of the entry to the [Output](/docs/en-us/studio/output.md) window. ```lua local textBox = script.Parent local function onFocusLost(enterPressed, inputObject) if enterPressed then print(textBox.Text) end end textBox.FocusLost:Connect(onFocusLost) ``` As another example, you might want to allow only numbers in a `Class.TextBox`. The following code uses the `Class.TextBox.GetPropertyChangedSignal` event to detect when the `Class.TextBox.Text` changes, such as when a user starts typing, then it uses the `Library.string.gsub()` function to disallow non‑numbers. ```lua local textBox = script.Parent local function allowOnlyNumbers() textBox.Text = string.gsub(textBox.Text, "%D", "") end textBox:GetPropertyChangedSignal("Text"):Connect(allowOnlyNumbers) ``` ## Text filtering Applied to various sources and inputs, [text filtering](/docs/en-us/ui/text-filtering.md) prevents users from seeing inappropriate language and personally identifiable information such as phone numbers. Roblox automatically filters common text outputs such as messages that have passed through [in-experience text chat](/docs/en-us/chat/in-experience-text-chat.md), but **you are responsible for filtering any displayed text that you don't have explicit control over**, including characters/strings that users input through text inputs. > **Error:** Because filtering is crucial for a safe environment, Roblox actively moderates the content of experiences to make sure they meet [community standards](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards). If Roblox receives reports or automatically detects that your experience doesn't apply text filtering, then the system removes the experience until you add filtering. --- title: "UI drag detectors" url: /docs/en-us/ui/ui-drag-detectors last_updated: 2026-06-29T19:34:16Z description: "UI drag detectors facilitate and encourage interaction with 2D user interface elements in an experience, such as sliders and spinners." --- # UI drag detectors The `Class.UIDragDetector` instance facilitates and encourages interaction with 2D user interface elements in an experience, such as sliders, spinners, and more. Key features include: - Place a `Class.UIDragDetector` under any `Class.GuiObject` instance to make it draggable via all user inputs without a single line of code. - Choose from several `Class.UIDragDetector.DragStyle|DragStyle` options, define how the object responds to motion via `Class.UIDragDetector.ResponseStyle|ResponseStyle`, and optionally apply axis, movement limits, or drag boundaries. - Scripts can respond to manipulation of dragged objects to drive logic responses, such as adjusting settings. - `Class.UIDragDetector|UIDragDetectors` work in Studio's edit and play mode as long as you're **not** using the **Select**, **Move**, **Scale**, or **Rotate** tools, nor certain plugins or Studio's **UI** editor tools. > **Info:** For drag detectors that manipulate 3D objects in an experience, such as opening doors and drawers or sliding a part around, see [3D Drag Detectors](/docs/en-us/ui/3D-drag-detectors.md). ## Make UI elements draggable To make any `Class.GuiObject` instances draggable, simply add a `Class.UIDragDetector` as a direct descendant. 1. In the [Explorer](/docs/en-us/studio/explorer.md) window, hover over the `Class.GuiObject` instance and click the ⊕ button. A contextual menu displays. 2. From the menu, insert a **UIDragDetector**. 3. By default, the object will now be draggable in the `Class.LayerCollector|LayerCollector` interface. > **Warning:** Remember that `Class.UIDragDetector|UIDragDetectors` only work in Studio if you're **not** using the **Select**, **Move**, **Scale**, or **Rotate** tools, nor certain plugins or Studio's **UI** editor tools. ## Customize UI drag detectors ### Drag style `Class.UIDragDetector|UIDragDetectors` map cursor motion to calculate proposed 2D motion and/or rotation. Through the `Class.UIDragDetector.DragStyle|DragStyle` property, you can choose from different mappings to suit your needs. For example, `Enum.UIDragDetectorDragStyle.TranslatePlane` produces translation in the 2D plane of the `Class.LayerCollector`, while `Enum.UIDragDetectorDragStyle.Rotate` normally produces a rotation instead of translation. | Setting | Description | | --- | --- | | `Enum.UIDragDetectorDragStyle.TranslateLine\|TranslateLine` | 1D motion along the detector's `Class.UIDragDetector.DragAxis\|DragAxis`. | | `Enum.UIDragDetectorDragStyle.TranslatePlane\|TranslatePlane` | 2D motion in the plane of the `Class.LayerCollector`. | | `Enum.UIDragDetectorDragStyle.Rotate\|Rotate` | By default, rotation about the absolute center position of the detector's parent `Class.GuiObject`. If `Class.UIDragDetector.ReferenceUIInstance\|ReferenceUIInstance` is set, rotation happens about that instance's absolute center position. | | `Enum.UIDragDetectorDragStyle.Scriptable\|Scriptable` | Calculates desired motion via a custom function provided through `Class.UIDragDetector:SetDragStyleFunction()\|SetDragStyleFunction()`. | ### Drag direction By default, 2D motion and the associated `Class.UIDragDetector.DragStyle|DragStyle` map to the space of the ancestor `Class.LayerCollector`. However, you may want to change the `Class.UIDragDetector.ReferenceUIInstance|ReferenceUIInstance` or the `Class.UIDragDetector.DragAxis|DragAxis` when building different UI components. | Setting | Description | Default | | --- | --- | --- | | `Class.UIDragDetector.ReferenceUIInstance\|ReferenceUIInstance` | A `Class.GuiObject` instance whose local space and absolute center position is the reference space and origin for the detector. Setting this reference affects properties such as `Class.UIDragDetector.DragUDim2\|DragUDim2`, `Class.UIDragDetector.DragRotation\|DragRotation`, and the behavior of `Class.UIDragDetector.DragAxis\|DragAxis`. | `nil` | | `Class.UIDragDetector.DragAxis\|DragAxis` | `Datatype.Vector2` value that defines the axis of movement for the dragged object when `Class.UIDragDetector.DragStyle\|DragStyle` is set to `Enum.UIDragDetectorDragStyle.TranslateLine`. The axis is defined in the local space of the `Class.UIDragDetector` unless `Class.UIDragDetector.ReferenceUIInstance\|ReferenceUIInstance` is defined, in which case the axis is defined in that instance's local space. | (1, 0) | ### Response to motion The `Class.UIDragDetector.ResponseStyle` property specifies how an object's position value is changed by the proposed motion. The custom response styles let you use the resulting `Class.UIDragDetector.DragUDim2` and `Class.UIDragDetector.DragRotation` values as desired, without having the detector's parent execute the proposed motion. | Setting | Description | | --- | --- | | `Enum.UIDragDetectorResponseStyle.Offset\|Offset` | Move by the `Datatype.UDim.Offset\|Offset` values of the detector's parent's `Class.GuiObject.Position` value. This is the setting by default. | | `Enum.UIDragDetectorResponseStyle.Scale\|Scale` | Move by the `Datatype.UDim.Scale\|Scale` values of the detector's parent's `Class.GuiObject.Position` value. | | `Enum.UIDragDetectorResponseStyle.CustomOffset\|CustomOffset` | The UI element will not move at all, but the `Datatype.UDim.Offset\|Offset` values of the detector's `Class.UIDragDetector.DragUDim2\|DragUDim2` will still be updated and the detector's events will still fire, allowing you to respond to drag manipulation however you'd like. | | `Enum.UIDragDetectorResponseStyle.CustomScale\|CustomScale` | The UI element will not move at all, but the `Datatype.UDim.Scale\|Scale` values of the detector's `Class.UIDragDetector.DragUDim2\|DragUDim2` will still be updated and the detector's events will still fire, allowing you to respond to drag manipulation however you'd like. | ### Translation & rotation limits By default, there are no limits to 2D motion behind the inherent restrictions of the `Class.UIDragDetector.DragStyle|DragStyle`. Limits for both minimum and maximum translations and rotations can be declared with the following properties if desired. Additionally, you can define how the dragged object is constrained within the bounds of a specified `Class.GuiObject` such as a `Class.Frame`. | Properties | Description | Default | | --- | --- | --- | | `Class.UIDragDetector.MinDragTranslation\|MinDragTranslation`
`Class.UIDragDetector.MaxDragTranslation\|MaxDragTranslation` | Limits to drag translation in each dimension, defined by a `Datatype.UDim2` value. If `Class.UIDragDetector.MaxDragTranslation\|MaxDragTranslation` is greater than `Class.UIDragDetector.MinDragTranslation\|MinDragTranslation`, translation will be clamped within that range. | {0, 0}, {0, 0} | | `Class.UIDragDetector.MinDragAngle\|MinDragAngle`
`Class.UIDragDetector.MaxDragAngle\|MaxDragAngle` | Only relevant if `Class.UIDragDetector.DragStyle\|DragStyle` is set to `Enum.UIDragDetectorDragStyle.Rotate`, or if the functions set through `Class.UIDragDetector:SetDragStyleFunction()\|SetDragStyleFunction()` or `Class.UIDragDetector:AddConstraintFunction()\|AddConstraintFunction()` defines a rotation value. If `Class.UIDragDetector.MaxDragAngle\|MaxDragAngle` is greater than `Class.UIDragDetector.MinDragAngle\|MinDragAngle`, rotation will be clamped within that range. | 0 | | `Class.UIDragDetector.BoundingBehavior\|BoundingBehavior` | Determines the `Class.UIDragDetector` instance's bounding behavior when its `Class.UIDragDetector.BoundingUI\|BoundingUI` is set. Setting this to `Enum.UIDragDetectorBoundingBehavior.EntireObject\|EntireObject` bounds the entire dragged UI within the `Class.UIDragDetector.BoundingUI\|BoundingUI`, while setting it to `Enum.UIDragDetectorBoundingBehavior.HitPoint\|HitPoint` bounds the dragged UI only by the exact hit/grab point and its respective position after translation/rotation. As a convenience, the default of `Enum.UIDragDetectorBoundingBehavior.Automatic\|Automatic` mimics the `Enum.UIDragDetectorBoundingBehavior.EntireObject\|EntireObject` behavior for a UI object that's entirely contained by the `Class.UIDragDetector.BoundingUI\|BoundingUI`, or else `Enum.UIDragDetectorBoundingBehavior.HitPoint\|HitPoint` for a UI object that's partially outside the `Class.UIDragDetector.BoundingUI\|BoundingUI`. | `Enum.UIDragDetectorBoundingBehavior.Automatic\|Automatic` | ### Speed adjustments Through `Class.UIDragDetector.SelectionModeDragSpeed|SelectionModeDragSpeed` and `Class.UIDragDetector.SelectionModeRotateSpeed|SelectionModeRotateSpeed`, you can fine‑tune the maximum drag/rotate speeds for a detector. Furthermore, through `Class.UIDragDetector.UIDragSpeedAxisMapping|UIDragSpeedAxisMapping`, you can fine‑tune the **X**/**Y** dimension dragging speeds, based on the detector's `Class.UIDragDetector.SelectionModeDragSpeed|SelectionModeDragSpeed`. | Property | Description | | --- | --- | | `Class.UIDragDetector.SelectionModeDragSpeed\|SelectionModeDragSpeed` | Defines the maximum drag speed for translation as a combination of `Datatype.UDim.Scale\|Scale` and `Datatype.UDim.Offset\|Offset` of the first ancestor `Class.ScreenGui` or `Class.SurfaceGui` the `Class.UIDragDetector` belongs to. | | `Class.UIDragDetector.SelectionModeRotateSpeed\|SelectionModeRotateSpeed` | Defines the maximum angle per second at which the `Class.UIDragDetector` can rotate. | | `Class.UIDragDetector.UIDragSpeedAxisMapping\|UIDragSpeedAxisMapping` | Determines the **X**/**Y** dimension dragging speeds, based on the detector's `Class.UIDragDetector.SelectionModeDragSpeed\|SelectionModeDragSpeed`. The default is `Enum.UIDragSpeedAxisMapping.XY\|XY`, meaning the **X** and **Y** axis speeds are based off the **X** and **Y** `Datatype.UDim.Scale\|Scale`/`Datatype.UDim.Offset\|Offset` values respectively.

Alternatives are `Enum.UIDragSpeedAxisMapping\|XX` and `Enum.UIDragSpeedAxisMapping\|YY`, meaning both the **X** and **Y** axis speeds are based off the **X** (`Enum.UIDragSpeedAxisMapping\|XX`) or **Y** (`Enum.UIDragSpeedAxisMapping\|YY`) axis for `Datatype.UDim.Scale\|Scale`, while the `Datatype.UDim.Offset\|Offset` values still apply to their respective axis. For example, if the first ancestor `Class.ScreenGui` is sized 800×600 and `Class.UIDragDetector.SelectionModeDragSpeed\|SelectionModeDragSpeed` is`{0.1, 10}, {0.1, 20}`, a setting of `Enum.UIDragSpeedAxisMapping\|XX` results in an **X**/**Y** drag speed of `80+10`/`80+20`, while `Enum.UIDragSpeedAxisMapping\|YY` results in `60+10`/`60+20` (note the `Datatype.UDim.Offset\|Offset` values remain the same in both cases). | ## Script responses to clicking and dragging Through [event signals](#event-signals), property changes, `Enum.UIDragDetectorDragStyle.Scriptable|Scriptable` drag style, and custom functions, scripts can respond to the manipulation of dragged UI elements to drive various settings or make logical decisions, such as sliders that adjust music and sound effect volume separately. > **Warning:** For user-initiated script responses like [Event Signals](#event-signals) and [Scripted Drag Style](#scripted-drag-style), you'll most commonly need to put your script code inside a `Class.LocalScript`, or a `Class.Script` with `Class.BaseScript.RunContext|RunContext` set to `Enum.RunContext.Client|Client`. ### Event signals Through the following event signals, you can detect when a user starts, continues, and ends dragging an object. | Event | Description | | --- | --- | | `Class.UIDragDetector.DragStart\|DragStart` | Fires when a user starts dragging the object. | | `Class.UIDragDetector.DragContinue\|DragContinue` | Fires when a user continues dragging the object after `Class.UIDragDetector.DragStart\|DragStart` has been initiated. | | `Class.UIDragDetector.DragEnd\|DragEnd` | Fires when a user stops dragging the object. | The following slider designates its container as the `Class.UIDragDetector.BoundingUI|BoundingUI` to limit its movement within the container area, allowing the scale‑based `Enum.UIDragDetectorDragStyle|TranslateLine` drag to be limited to the full width of the container without extra scripting. > **Note:** To test this example, download the [`TransparencySlider.rbxm`](../assets/ui/ui-drag-detectors/TransparencySlider.rbxm) file, right‑click `Class.StarterGui` in the [Explorer](/docs/en-us/studio/explorer.md) window, select **Insert** ⟩ **Import Roblox Model**, and choose the downloaded file. ```lua -- Hierarchy is SliderContainer ⟩ Handle ⟩ UIDragDetector ⟩ (this script) local sliderContainer = script.Parent.Parent.Parent local handle = sliderContainer:FindFirstChild("Handle") local uiDragDetector = handle:FindFirstChildWhichIsA("UIDragDetector") uiDragDetector.ResponseStyle = Enum.UIDragDetectorResponseStyle.Scale -- Set dragging by scale uiDragDetector.DragStyle = Enum.UIDragDetectorDragStyle.TranslateLine -- Restricts dragging to line uiDragDetector.BoundingUI = sliderContainer -- Initially set container transparency to X scale value of handle sliderContainer.BackgroundTransparency = 1 - handle.Position.X.Scale -- Expand handle border to indicate grab start uiDragDetector.DragStart:Connect(function(inputPosition) handle:FindFirstChildWhichIsA("UIStroke").Thickness = 6 end) -- Change transparency by how much it dragged in scale uiDragDetector.DragContinue:Connect(function(inputPosition) sliderContainer.BackgroundTransparency = 1 - handle.Position.X.Scale end) -- Revert handle border to indicate grab end uiDragDetector.DragEnd:Connect(function(inputPosition) handle:FindFirstChildWhichIsA("UIStroke").Thickness = 4 end) ``` ### Position & rotation changes In addition to [event signals](#event-signals), you can monitor changes to the detector's `Class.UIDragDetector.DragUDim2|DragUDim2` and/or `Class.UIDragDetector.DragRotation|DragRotation` properties directly. The following detector has its `Class.UIDragDetector.DragStyle|DragStyle` set to `Enum.UIDragDetectorDragStyle|Rotate`, allowing users to drag the handle around the hue rotator ring, all while detecting changes to drag rotation through `Class.Instance:GetPropertyChangedSignal()`. > **Note:** To test this example, download the [`HueRotator.rbxm`](../assets/ui/ui-drag-detectors/HueRotator.rbxm) file, right‑click `Class.StarterGui` in the [Explorer](/docs/en-us/studio/explorer.md) window, select **Insert** ⟩ **Import Roblox Model**, and choose the downloaded file. ```lua local handle = script.Parent.Parent -- UI element to drag local uiDragDetector = handle:FindFirstChildWhichIsA("UIDragDetector") uiDragDetector.DragStyle = Enum.UIDragDetectorDragStyle.Rotate -- Set drag style to rotate local function changeHue() local currAngle = (math.fmod(handle.Rotation, 360)) / 360 if currAngle < 0 then currAngle += 1 end handle.BackgroundColor3 = Color3.fromHSV(currAngle, 1, 1) end -- Initially set hue to handle rotation changeHue() -- Connect function to GetPropertyChangedSignal() of the detector's drag rotation uiDragDetector:GetPropertyChangedSignal("DragRotation"):Connect(changeHue) ``` ### Scripted drag style If you set a detector's `Class.UIDragDetector.DragStyle` to `Enum.UIDragDetectorDragStyle.Scriptable`, you can provide your own function that takes in a `Datatype.Vector2` of the input position and returns a `Datatype.UDim2` (position) and a float (rotation). The detector will update the object to the computed position/rotation based off of the returns, the `Class.UIDragDetector.DragSpace|DragSpace` property, and the `Class.UIDragDetector.DragRelativity|DragRelativity` property. By default, the returned `Datatype.UDim2` and float will be the **final** desired position/rotation in the local space of the detector's parent. Existing translation/rotation limits will still apply, as will boundary limits imposed by a specified `Class.UIDragDetector.BoundingUI|BoundingUI` instance. The following example drags a UI element following a sine wave computed by the change in **X** coordinate input. Note that the detector's `Class.UIDragDetector.DragSpace|DragSpace` is set to `Enum.UIDragDetectorDragSpace.Relative`. ```lua local frame = script.Parent -- UI element to drag local uiDragDetector = frame:FindFirstChildWhichIsA("UIDragDetector") local initialXValue = 0 local maxHeightChange = 200 local pixelsPerRadian = 75 -- Lower this value to increase frequency uiDragDetector.DragStart:Connect(function(inputPosition) initialXValue = inputPosition.X end) local function computeSinWaveCoordinate(inputPosition) local deltaX = inputPosition.X - initialXValue -- Negative Y delta so that it goes "up" on the screen with positive Y change local deltaY = -math.sin(deltaX / pixelsPerRadian) * maxHeightChange return UDim2.fromOffset(deltaX, deltaY) end uiDragDetector:SetDragStyleFunction(computeSinWaveCoordinate) ``` ### Custom constraint function `Class.UIDragDetector|UIDragDetectors` do not have built-in motion rules about grids and snapping, but you can register custom constraint functions to edit the detector's `Class.UIDragDetector.DragUDim2` and `Class.UIDragDetector.DragRotation` before they are applied. For example, you can keep motion on a grid by rounding positions to specific increments, or define allowed areas of motion. Note that this is applied **before** any existing translation/rotation limits. > **Info:** Multiple constraint functions can be registered and they will be called in the order of their priority value passed in upon registration, from least to greatest. The following example utilizes a constraint function that clamps the planar drag into an **X**/**Y** grid based on the number of rows and columns. Note that the detector's `Class.UIDragDetector.ResponseStyle|ResponseStyle` is set to `Enum.UIDragDetectorResponseStyle.Scale` and its `Class.UIDragDetector.BoundingUI|BoundingUI` is set to the grid container. > **Note:** To test this example, download the [`GridDrag.rbxm`](../assets/ui/ui-drag-detectors/GridDrag.rbxm) file, right‑click `Class.StarterGui` in the [Explorer](/docs/en-us/studio/explorer.md) window, select **Insert** ⟩ **Import Roblox Model**, and choose the downloaded file. ```lua -- Hierarchy is GridContainer ⟩ Handle ⟩ UIDragDetector ⟩ (this script) local gridContainer = script.Parent.Parent.Parent local handle = gridContainer:FindFirstChild("Handle") -- UI element to drag local uiDragDetector = handle:FindFirstChildWhichIsA("UIDragDetector") uiDragDetector.ResponseStyle = Enum.UIDragDetectorResponseStyle.Scale -- Set dragging by scale uiDragDetector.DragRelativity = Enum.UIDragDetectorDragRelativity.Relative uiDragDetector.BoundingUI = gridContainer local NUM_COLUMNS = 10 local NUM_ROWS = 5 local xScaleIncrement = 1 / NUM_COLUMNS local yScaleIncrement = 1 / NUM_ROWS local initialParentPosition = uiDragDetector.Parent.Position uiDragDetector.DragStart:Connect(function() initialParentPosition = uiDragDetector.Parent.Position end) local function dragToGridOnly(proposedPosition, proposedRotation) local griddedXScale = math.round(proposedPosition.X.Scale / xScaleIncrement) * xScaleIncrement local griddedYScale = math.round(proposedPosition.Y.Scale / yScaleIncrement) * yScaleIncrement return UDim2.fromScale(griddedXScale, griddedYScale), proposedRotation end uiDragDetector:AddConstraintFunction(1, dragToGridOnly) ```
--- title: "Video frames" url: /docs/en-us/ui/video-frames last_updated: 2026-06-29T19:34:16Z description: "Video assets used on VideoFrame instances allow for video playback in experiences." --- # Video frames Video assets used in `Class.VideoFrame` instances allow for video playback in experiences. You can [upload](#upload-videos) videos that you're certain you have permission to use, such as videos you make yourself, and the [asset privacy](/docs/en-us/projects/assets/privacy.md) system automatically ensures that the IDs of your uploaded videos can't be accessed by users without the proper permissions. ## Upload videos > **Success:** To upload video assets, enable the beta feature through **File** ⟩ **Beta Features** ⟩ **Video Uploads**. If you're a 13+ [ID verified](https://en.help.roblox.com/hc/en-us/articles/4407282410644-Age-ID-Verification) user, you can upload videos through the [Asset Manager](/docs/en-us/projects/assets/manager.md), the [Creator Dashboard](https://create.roblox.com/dashboard/creations?activeTab=Video), or the [Open Cloud API](/docs/en-us/cloud/guides/usage-assets.md). You can upload a video as long as it meets the following requirements: - You have the legal rights to use the video asset. - It adheres to the [Roblox Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410) and [Terms of Use](https://en.help.roblox.com/hc/en-us/articles/115004647846). - It's 5 minutes or less in either `.mp4` or `.mov` format. - Its resolution is less than or equal to 4096×2160. - It's less than 3.75 GB. Videos that don't meet these requirements are rejected. Alpha channels are not supported and will be ignored. When uploading videos, consider the following: - Each video upload costs 2,000 Robux. - You can upload a maximum of 20 videos a day. ## Play videos A `Class.VideoFrame` must be parented to a `Class.ScreenGui`, `Class.SurfaceGui`, or `Class.BillboardGui` in order to be playable. Currently, a maximum of two videos can play simultaneously. To play a video in your experience without code: 1. Create a `Class.ScreenGui` as outlined in [On-Screen UI Containers](/docs/en-us/ui/on-screen-containers.md), or a `Class.SurfaceGui` or `Class.BillboardGui` as outlined in [In-Experience UI Containers](/docs/en-us/ui/in-experience-containers.md). 2. Insert a video from the [Toolbox](/docs/en-us/projects/assets/toolbox.md) or [Asset Manager](/docs/en-us/projects/assets/manager.md). A new `Class.VideoFrame` object is inserted for the video. 3. Parent the `Class.VideoFrame` to the container. 4. With the new `Class.VideoFrame` selected, enable its **Looped** and **Playing** properties in the [Properties](/docs/en-us/studio/properties.md) window. If you want to play a video in your experience with code, paste the following code into a `Class.Script` within `Class.ServerScriptService` to create a `Class.Part` and play the video on its front surface. ```lua local Workspace = game:GetService("Workspace") local screenPart = Instance.new("Part") screenPart.Size = Vector3.new(16, 9, 1) screenPart.Position = Vector3.new(0, 8, -20) screenPart.Orientation = Vector3.new(0, 180, 0) screenPart.Anchored = true screenPart.Parent = Workspace local surfaceGui = Instance.new("SurfaceGui") surfaceGui.Parent = screenPart local videoFrame = Instance.new("VideoFrame") videoFrame.Size = UDim2.new(1, 0, 1, 0) videoFrame.Parent = surfaceGui videoFrame.Looped = true videoFrame.Video = "rbxassetid://5608384572" -- Replace with your video's asset ID while not videoFrame.IsLoaded do videoFrame.Loaded:Wait() end videoFrame:Play() ``` --- title: "Viewport frames" url: /docs/en-us/ui/viewport-frames last_updated: 2026-06-29T19:34:16Z description: "ViewportFrames use a camera to render 3D objects into a 2D viewport." --- # Viewport frames A `Class.ViewportFrame` uses a camera to render 3D objects into a 2D viewport. Ideal use cases include: - A minimap of your experience directly in the corner of a user's screen. - 3D models of items in an inventory menu. - Rotating objects that a character has equipped. ## Viewport configurations 3D objects that users view through a `Class.ViewportFrame` can either move with their camera, remain static, or rotate within the `Class.ViewportFrame`. This object can also include a `Class.Sky` child as a cubemap for reflections. #### With Camera If you want a 3D object to move with the camera: 1. Position your camera view within the experience so that the object you want to see within the frame is visible. 2. Add a new `Class.ViewportFrame` to the [screen](/docs/en-us/ui/on-screen-containers.md) and then make sure it's selected in the **Explorer**. 3. In the **Properties** window, assign the `Class.ViewportFrame.CurrentCamera|CurrentCamera` property to the camera: 1. Select the `Class.ViewportFrame.CurrentCamera|CurrentCamera` property. Your cursor changes. 2. In the **Explorer** window, click on the top-level `Class.Camera` object. 4. Parent the desired 3D object to the new `Class.ViewportFrame`. Note that if you still want to see the object within your experience, you must duplicate it in the `Class.Workspace` and then parent the **duplicate** object to the `Class.ViewportFrame`. When you move your camera, the object will also move within the `Class.ViewportFrame`. > **Warning:** When you want to update the view of your `Class.ViewportFrame`, be sure to update the camera, **not** the objects within the view. #### Static If you want the 3D object to remain static: 1. Position your camera view within the experience so that the object you want to see is in the exact position you want to see it within the frame. 2. In the **Explorer** window, duplicate the top-level `Class.Camera` object, then rename it to an identifiable name like `ViewportCam`. 3. Add a new `Class.ViewportFrame` to the [screen](/docs/en-us/ui/on-screen-containers.md) and then make sure it's selected in the **Explorer**. 4. In the **Properties** window, assign the frame's `Class.ViewportFrame.CurrentCamera|CurrentCamera` property to the **duplicated** camera: 1. Select the `Class.ViewportFrame.CurrentCamera|CurrentCamera` property. Your cursor changes. 2. In the **Explorer** window, click on the duplicated camera object. 5. Parent the desired 3D object to the new `Class.ViewportFrame`. Note that if you still want to see the object within your experience, you must duplicate it in the `Class.Workspace` and then parent the **duplicate** object to the `Class.ViewportFrame`. #### Rotation If you want a 3D object such as a `Class.BasePart` to rotate on its own within the frame: 1. Add a new `Class.ViewportFrame` to the [screen](/docs/en-us/ui/on-screen-containers.md). 2. In the **Explorer** window, drag the desired `Class.BasePart` into the new `Class.ViewportFrame`. 3. Insert a new `Class.LocalScript` into the `Class.ViewportFrame` and paste in the following code.```lua local RunService = game:GetService("RunService") local viewportFrame = script.Parent -- Parameters to experiment with local cameraDistance = 10 local cameraFieldOfView = 50 local objectPitchAngle = 40 local objectRotationSpeed = 50 -- Viewport camera initialization local viewportCamera = Instance.new("Camera") viewportCamera.FieldOfView = cameraFieldOfView viewportFrame.CurrentCamera = viewportCamera viewportCamera.Parent = viewportFrame -- Viewport object initialization local object = viewportFrame:FindFirstChildWhichIsA("BasePart") if object then object.CFrame = CFrame.new(0, 0, 0) * CFrame.Angles(math.rad(objectPitchAngle), 0, 0) -- Update loop local t = 0 RunService.PostSimulation:Connect(function(delta) t += delta viewportCamera.CFrame = CFrame.Angles(0, math.rad(t * objectRotationSpeed), 0) * CFrame.new(0, 0, cameraDistance) end) else warn("3D object not found as child of viewport frame") end ``` #### Skybox Reflections `Class.ViewportFrame|ViewportFrames` can use a `Class.Sky` child as a cubemap for reflections, in which case only the `Class.Sky` object's six `Skybox[…]` properties are used. Assuming these properties are valid, lighting inside the `Class.ViewportFrame` acts similarly to when `Class.Lighting.EnvironmentSpecularScale` and `Class.Lighting.EnvironmentDiffuseScale` are both set to `1`. To implement skybox cubemap reflections: 1. Insert a `Class.Sky` object as a direct child of the `Class.ViewportFrame`. 2. Set the `Class.Sky` object's six texture properties (`Class.Sky.SkyboxBk|SkyboxBk`, `Class.Sky.SkyboxDn|SkyboxDn`, `Class.Sky.SkyboxFt|SkyboxFt`, `Class.Sky.SkyboxLf|SkyboxLf`, `Class.Sky.SkyboxRt|SkyboxRt`, `Class.Sky.SkyboxUp|SkyboxUp`). 3. For `Class.Part|Parts` that should appear within the frame, set their `Class.BasePart.Reflectance|Reflectance` property greater than 0, or use a reflectant material like `Enum.Material.Glass|Glass` or `Enum.Material.Foil|Foil`. For `Class.MeshPart|MeshParts` that should appear within the frame, apply a `Class.SurfaceAppearance` with a properly-configured `Class.SurfaceAppearance.MetalnessMap|MetalnessMap`. ## Lighting and appearance Lighting within a `Class.ViewportFrame` is controlled through three properties: | Property | Description | | --- | --- | | `Class.ViewportFrame.Ambient\|Ambient` | Determines the overall lighting hue applied to the area within the viewport frame. Defaults to`Datatype.Color3.fromRGB(200, 200, 200)` (ghost grey). | | `Class.ViewportFrame.LightDirection\|LightDirection` | A `Datatype.Vector3` representing the direction of the light source from position`(0, 0, 0)`. Defaults to`(-1, -1, -1)`. | | `Class.ViewportFrame.LightColor\|LightColor` | Color of the directional light. Defaults to`Datatype.Color3.fromRGB(140, 140, 140)` (silver). | Additionally, you can adjust the overall rendered appearance of the viewport throuugh the following properties: | Property | Description | | --- | --- | | `Class.ViewportFrame.ImageColor3\|ImageColor3` | Changes the image color/tint without modification of the rendered object. The default colorization value is`Datatype.Color3.new(1, 1, 1)` (white) at which no color modification occurs. | | `Class.ViewportFrame.ImageTransparency\|ImageTransparency` | Changes the image transparency without modification of the rendered object. A value of `0` (default) is completely opaque and a value of `1` is completely transparent (invisible). | --- title: "Roblox for Unity developers" url: /docs/en-us/unity last_updated: 2026-06-29T19:34:17Z description: "If you're an experienced Unity developer, use this page to get oriented with Roblox." --- # Roblox for Unity developers This page contains information to help experienced Unity developers get started with Roblox, including basic orientation, a conceptual comparison, and key differences between the two platforms. ## Get oriented ![The Unity user interface with markup to show the various windows and panels.](./assets/engine-comparisons/Unity-Editor-Layout.png)![The Roblox Studio user interface with markup to show the various windows and panels.](./assets/studio/general/Studio-Layout.png) Unity's **Hierarchy** window and Roblox Studio's [Explorer](/docs/en-us/studio/explorer.md) are the primary windows for organizing elements in 3D scenes: - Both allow you to manage and organize objects (for example, characters and environmental assets). - Both use a tree structure for the parent-child relationships between objects. However, the **Hierarchy** window has no predefined structure, whereas the **Explorer** window has a strict structure. It might help to think of the **Explorer** window as a combination of Unity's **Hierarchy** and **Project** windows, with the `Class.Workspace` folder as the most recognizable element. Similarly, the Roblox Studio [Asset Manager](/docs/en-us/projects/assets/manager.md) and [Toolbox](/docs/en-us/projects/assets/toolbox.md) overlap with the Unity **Project** window. The **Asset Manager** lets you manage all assets within your game, whereas the **Toolbox** lets you access any assets you've published. The **Toolbox** also lets you search the [Creator Store](/docs/en-us/production/creator-store.md) for assets from Roblox or the community, similar to the Unity **Asset Store**. ## Philosophical differences Roblox is a "simulation engine" rather than a traditional game engine. Unity `GameObjects` and Roblox `Class.Part|Parts` both serve as the fundamental building blocks for creating objects in a 3D environment, but in practice, the two are quite different: - **Representation**: `GameObjects` in Unity are a higher-level concept for any object in a scene, whereas `Parts` in Roblox are designed to represent physical objects like wooden blocks and plastic spheres, rather than abstract geometry like primitive objects in Unity. - **Physics**: To perform physics simulations in Unity, you attach components like `Rigidbody` and `Collider` to a `GameObject`. In Roblox, physics are built into the `Parts` data type; the engine handles interactions automatically. You can see the difference immediately if you create a `GameObject` and a `Part`. The `GameObject` has nothing more than a position, rotation, and scale. The `Part` has that same information—plus a material and color, values for reflectance and transparency, mass and shape, and much more. Turning a `Part` into something more akin to an empty `GameObject` means **removing** a lot of built-in properties. Conversely, you can make a `GameObject` that looks a lot like a `Part` by adding `MeshFilter`, `MeshRenderer`, `Collider`, and `Rigidbody` components to it. From a scripting perspective, `GameObject` is most similar to the Roblox `Class.Instance`, the base class for all other Roblox classes, but because you don't (and can't) create objects of type `Instance`, the comparison isn't especially practical. Another comparison is the Unity `GameObject` to the Roblox `Class.Model`. Models act as a container for a collection of interconnected parts in the same way that you might establish a parent-child relationship between many `GameObjects` in Unity. You specify one of the model's parts as its [primary part](/docs/en-us/parts/models.md#set-a-primary-part) to define the pivot point. Models also hold scripts, animations, sound effects, prompts, constraints, particle emitters, and more. For example, a Unity `GameObject` might have components for `ParticleSystem`, `Physics3D`, `SpringConstraint`, and a script. In the Hierarchy window, you see a single `GameObject` named `SpringyFireball`. The Inspector window shows the collection of components and properties. In Roblox, a comparable `SpringyFireball` model in the **Explorer** window might look something like this: ```text Model |- ParticleEmitter |- MeshPart |- SpringConstraint |- ClickDetector | |- Script ``` Roblox's physics-by-default philosophy extends to the process of building 3D models. In Roblox, welding multiple parts together into an [assembly](/docs/en-us/physics/assemblies.md) is an excellent way to quickly build things, because Roblox treats the welded parts as a single rigid body. This approach isn't available in Unity. Rather than using standard metric units for length and mass, Roblox uses notional units called studs and Roblox Mass Units (RMUs). For approximate metric conversions and recommendations around use, see [Units](/docs/en-us/physics/units.md). ## Location matters Roblox games are multiplayer by default, so Roblox Studio includes many different storage locations with specific behaviors. For more information, see [client-server runtime](/docs/en-us/projects/client-server.md) and [object organization](/docs/en-us/projects/data-model.md#object-organization). | Location | Description | | --- | --- | | `Class.Workspace` | Represents the experience's 3D world. This location works well for server scripts that attach directly to objects and control their behavior. | | `Class.ReplicatedFirst` | Contains objects that replicate to the client before anything else. This location is ideal for the absolute minimum set of objects and client scripts necessary to display a loading screen. | | `Class.ReplicatedStorage` | Contains objects that are replicated to both the client and the server. This location is ideal for `Class.ModuleScript\|ModuleScripts` that you want to use on both the server and the client. `Class.LocalScript\|LocalScripts` do not run from this location, but `Class.Script\|Scripts` with a `Class.BaseScript.RunContext\|RunContext` of `Enum.RunContext\|Client` do. | | `Class.ServerScriptService` | Contains server scripts. This location is ideal for scripts that need to access server-side functionality or objects, such as game logic and cloud storage. | | `Class.ServerStorage` | Contains server-side objects. This location is ideal for large objects that don't need to be immediately replicated to clients when they join an experience. Scripts do not run from this location, but you can store server-side `Class.ModuleScript\|ModuleScripts` here. | | `Class.StarterPlayer` ⟩ `Class.StarterCharacterScripts` | Contains `Class.LocalScript\|LocalScripts` that run when the character spawns. | | `Class.StarterPlayer` ⟩ `Class.StarterPlayerScripts` | Contains general-purpose `Class.LocalScript\|LocalScripts` that run when the player joins the experience. | | `Class.StarterGui` | Contains GUI elements that the client displays when it loads the experience. `Class.LocalScript\|LocalScripts` can run from this location. This location is ideal for scripts that modify the experience's user interface, such as adding buttons, menus, and pop-ups. | | `Class.StarterPack` | Generally only contains `Class.Tool\|Tools`, but can also include `Class.LocalScript\|LocalScripts` for setting up player backpacks. | ## Scripting Roblox experiences support three different types of Luau scripts: - **Client scripts** These scripts run on the client, and the server has no visibility into their behavior. For legacy reasons, these scripts can take the form of `Class.LocalScript|LocalScripts` or `Class.Script|Scripts` with a `Class.BaseScript.RunContext|RunContext` value of `Enum.RunContext|Client`. Client scripts typically live in `Class.ReplicatedStorage`, `Class.StarterPlayerScripts`, or `Class.StarterCharacterScripts`. - **Server scripts** These scripts run on the server, and the client has no visibility into their behavior. Server scripts have a `Class.BaseScript.RunContext|RunContext` value of `Enum.RunContext|Server` and typically live in `Class.ServerScriptService`, the contents of which are not replicated to the client. - **Module scripts** These scripts are reusable pieces of code that return exactly one value, typically a function or table (or a table of functions). Rather than duplicating code in client and server scripts, use module scripts to share code and data between the two. Module scripts often live in `Class.ReplicatedStorage`, but can live elsewhere if you want to share code between scripts on the same side of the client-server boundary. Unity doesn't have the concept of different script types. If you choose to make a multiplayer game, Unity uses its networking libraries to indicate when a `GameObject` (and its scripts) should be exclusive to the server. In Unity, much of the engine's functionality is available through the methods of `MonoBehaviour`. For example, to run code before the render loop, you add code to the `Update()` method. To handle physics collision events, you add code to the `OnCollideEnter()` method. Roblox scripts are more event-driven. You access similar functionality by subscribing to services and listening for updates. ### C# and Luau For scripting, Unity uses C#. Roblox uses [Luau](/docs/en-us/luau.md), a scripting language derived from [Lua 5.1](https://www.lua.org/manual/5.1/). Compared to C#, Luau is gradually typed and generally has a less verbose syntax. In larger projects, however, gradual typing can introduce categories of bugs that strongly typed languages like C# avoid, so consider enabling [strict type checking](/docs/en-us/luau/type-checking.md#inference-modes) in Roblox scripts. For basic syntax differences between the scripting languages, see [Luau and C# comparison](/docs/en-us/luau/luau-csharp-comparison.md). ### Luau code sample The following Luau code sample demonstrates how to, after a player equips a fishing pole, listen for user input (in this case, the `E` key) and call additional functions: ```lua local ContextActionService = game:GetService("ContextActionService") local ReplicatedStorage = game:GetService("ReplicatedStorage") -- Get a module script from ReplicatedStorage that returns a single function local performSomeAction = require(ReplicatedStorage.performSomeAction) -- Assumes that this script is a child of the fishing pole local fishingPole = script.Parent local ACTION_CAST = "Cast" -- Check that the key is down, then call another function local function castLine(_actionName, inputState, _inputObject) if inputState == Enum.UserInputState.Begin then performSomeAction() end end -- Only enable the action when the player equips the fishing pole fishingPole.Equipped:Connect(function() ContextActionService:BindAction(ACTION_CAST, castLine, true, Enum.KeyCode.E) end) -- Disable the action when the player unequips the fishing pole fishingPole.Unequipped:Connect(function() ContextActionService:UnbindAction(ACTION_CAST) end) ``` The Roblox script can be relatively concise because Roblox has many built-in assumptions: a `Class.Player` with a `Class.Humanoid` character connected to the server and can equip `Class.Tool|Tools`. These assumptions don't exist in Unity, so the implementation would be very different. ## Assets Unity and Roblox both support importing custom meshes and models in `.fbx` format. Certain types of assets may require specific configurations and export settings from your third-party modeling software. For more information, see the following pages: - [Importer](/docs/en-us/studio/importer.md) - [General specifications](/docs/en-us/art/modeling/specifications.md) - [Blender and Maya export requirements](/docs/en-us/art/modeling/export-requirements.md) In Unity, objects import into your `Assets` directory, visible in the **Project** window. In Roblox, assets import into your `Class.Workspace` and into the [Toolbox](/docs/en-us/projects/assets/toolbox.md) or [Asset Manager](/docs/en-us/projects/assets/manager.md). Roblox also offers an open-source [Blender plugin](/docs/en-us/art/modeling/roblox-blender-plugin.md) to streamline the import process. ## Transforms Unity's transforms and Roblox's `Datatype.CFrame|CFrames` serve similar purposes in representing 3D transformations of objects: - Both transforms and `Datatype.CFrame|CFrames` represent the position and rotation of an object in 3D space. Transforms include scale, whereas Roblox uses a `Class.BasePart.Size` property that isn't part of the `Datatype.CFrame`. - Both support multiplication for complex transformations and have built-in methods for other manipulations. ## Collaboration In Unity, you collaborate with standard version control systems or paid services like Unity Version Control. Roblox files live in the cloud (although you can export copies), so Roblox Studio provides built-in collaboration workflows for simultaneous editing, group management, permissions, script drafting, and more. See [Collaboration](/docs/en-us/projects/collaboration.md). > **Info:** Cloud syncing provides further benefits with [packages](/docs/en-us/projects/assets/packages.md), the Roblox equivalent of Unity prefabs. Converting an asset or asset hierarchy to a package helps with local reusability, but also with collaboration. When you or your collaborators publish a new version of a package, you can quickly update existing instances of that package within a game or set them to auto-update. ## Plugins Similar to Unity tools, Roblox Studio supports [plugins](/docs/en-us/studio/plugins.md), which can simplify or give you additional control over various aspects of the development process. Plugins are available in the [Creator Store](/docs/en-us/production/creator-store.md), just like assets, many for free. ## Glossary | Unity | Roblox | Notes | | --- | --- | --- | | Scene | [Place](/docs/en-us/projects.md#places) | | | GameObject | `Class.Part` or `Class.Model` | See [Philosophical differences](#philosophical-differences). | | Prefab | [Package](/docs/en-us/projects/assets/packages.md) | | | Transform | `Datatype.CFrame` | `Datatype.CFrame` doesn't include scale information. See [Transforms](#transforms). | | Hierarchy | [Explorer](/docs/en-us/studio/explorer.md) | | | Inspector | [Properties](/docs/en-us/studio/properties.md) | | | Scene view | [3D viewport](/docs/en-us/studio/ui-overview.md#3d-viewport) | | | Game view | [3D viewport](/docs/en-us/studio/ui-overview.md#3d-viewport) | The viewport transitions into a gameplay view when you test your game. | | Project window | [Asset Manager](/docs/en-us/projects/assets/manager.md) or [Toolbox](/docs/en-us/projects/assets/toolbox.md) | | | Terrain Inspector | [Terrain Editor](/docs/en-us/studio/terrain-editor.md) | | | Spawn point | `Class.SpawnLocation` | | | Console | [Output](/docs/en-us/studio/output.md) | | | Asset Store | [Creator Store](/docs/en-us/production/creator-store.md) | | | Overlays | [Toolbar](/docs/en-us/studio/ui-overview.md#toolbar-and-mezzanine) | | | Tool | [Plugin](/docs/en-us/studio/plugins.md) | | --- title: "Roblox for Unreal developers" url: /docs/en-us/unreal last_updated: 2026-06-29T19:34:17Z description: "If you're an experienced Unreal developer, use this page to get oriented with Roblox." --- # Roblox for Unreal developers This page contains information to help experienced Unreal Engine developers get started with Roblox, including basic orientation, a conceptual comparison, and key differences between the two platforms. ## Get oriented ![The Unreal Editor user interface with markup to show the various windows and panels.](./assets/engine-comparisons/Unreal-Editor-Layout.png)![The Roblox Studio user interface with markup to show the various windows and panels.](./assets/studio/general/Studio-Layout.png) Unreal's **Outliner** and Roblox Studio's [Explorer](/docs/en-us/studio/explorer.md) are the primary windows for organizing elements in 3D spaces. Both display a hierarchy of objects and folders. However, **Outliner** has a flatter, less defined structure and only shows `Actors`. The **Explorer** window has a deeply nested, strict structure and displays all objects as part of the hierarchy, even objects that would be considered components in Unreal. The Roblox Studio [Asset Manager](/docs/en-us/projects/assets/manager.md) and [Toolbox](/docs/en-us/projects/assets/toolbox.md) overlap with the Unreal **Content Browser**. The **Asset Manager** lets you manage all assets within your game, whereas the **Toolbox** lets you access any assets you've published. The **Toolbox** also lets you search the [Creator Store](/docs/en-us/production/creator-store.md) for assets from Roblox or the community, similar to the Unreal Engine **Marketplace** but accessible directly from the Studio user interface. ## Philosophical differences Roblox is a "simulation engine" rather than a traditional game engine. Unreal `Actors` and Roblox `Class.Part|Parts` both serve as fundamental building blocks, but in practice, the two are quite different: - **Representation**: `Actors` in Unreal are a higher-level concept for any object in a level, whereas `Class.Part|Parts` in Roblox are designed to represent physical objects like wooden blocks and plastic spheres. - **Physics**: To perform physics simulations in Unreal, you enable physics within certain components (such as the `StaticMeshComponent`) or by adding components to `Actors`, such as physics constraints. In Roblox, physics are built into the `Class.Part|Parts` data type; the engine handles interactions automatically. You can see the difference immediately if you create an `Actor` and a `Class.Part`. The `Actor` has little more than a location, rotation, and scale. The `Class.Part` has that same information, plus a material and color, values for reflectance and transparency, mass and shape, and much more. The two only start to share similar properties when you compare a `StaticMeshActor` in Unreal to a `Class.MeshPart` in Roblox. ![An example Unreal actor in the Details panel.](./assets/engine-comparisons/Unreal-Details-Panel.png)_Unreal Editor Details panel_ ![An example Roblox part in the Properties window.](./assets/engine-comparisons/Studio-Properties.png)_Roblox Studio Properties window_ Another useful comparison is the Unreal `Actor` to the Roblox `Class.Model`. Models act as a container for a collection of interconnected parts in the same way that `Actors` in Unreal are containers for components. You specify one of the model's parts as its [primary part](/docs/en-us/parts/models.md#set-a-primary-part) to define the pivot point. Models also hold scripts, animations, sound effects, prompts, constraints, particle emitters, and more. For example, an Unreal `Actor` might have a `NiagaraComponent` that uses several emitters to achieve the desired visual effect, a mesh for the shape, a physics constraint to add springiness, and a script for player interactivity. In Outliner, you see a single `Actor` named `SpringyFireball`. In Roblox, a comparable `SpringyFireball` model in the **Explorer** window might look something like this: ```text Model |- ParticleEmitter |- MeshPart |- SpringConstraint |- ClickDetector | |- Script ``` Roblox's physics-by-default philosophy extends to the process of building 3D models. In Roblox, welding multiple parts together into an [assembly](/docs/en-us/physics/assemblies.md) is an excellent way to quickly build things, because Roblox treats the welded parts as a single rigid body. This approach isn't practical in Unreal. Rather than using standard metric units for length and mass, Roblox uses notional units called studs and Roblox Mass Units (RMUs). For approximate metric conversions and recommendations around use, see [Units](/docs/en-us/physics/units.md). ## Location matters Roblox games are multiplayer by default, so Roblox Studio includes many different storage locations with specific behaviors. For more information, see [client-server runtime](/docs/en-us/projects/client-server.md) and [object organization](/docs/en-us/projects/data-model.md#object-organization). | Location | Description | | --- | --- | | `Class.Workspace` | Represents the experience's 3D world. This location works well for server scripts that attach directly to objects and control their behavior. | | `Class.ReplicatedFirst` | Contains objects that replicate to the client before anything else. This location is ideal for the absolute minimum set of objects and client scripts necessary to display a loading screen. | | `Class.ReplicatedStorage` | Contains objects that are replicated to both the client and the server. This location is ideal for `Class.ModuleScript\|ModuleScripts` that you want to use on both the server and the client. `Class.LocalScript\|LocalScripts` do not run from this location, but `Class.Script\|Scripts` with a `Class.BaseScript.RunContext\|RunContext` of `Enum.RunContext\|Client` do. | | `Class.ServerScriptService` | Contains server scripts. This location is ideal for scripts that need to access server-side functionality or objects, such as game logic and cloud storage. | | `Class.ServerStorage` | Contains server-side objects. This location is ideal for large objects that don't need to be immediately replicated to clients when they join an experience. Scripts do not run from this location, but you can store server-side `Class.ModuleScript\|ModuleScripts` here. | | `Class.StarterPlayer` ⟩ `Class.StarterCharacterScripts` | Contains `Class.LocalScript\|LocalScripts` that run when the character spawns. | | `Class.StarterPlayer` ⟩ `Class.StarterPlayerScripts` | Contains general-purpose `Class.LocalScript\|LocalScripts` that run when the player joins the experience. | | `Class.StarterGui` | Contains GUI elements that the client displays when it loads the experience. `Class.LocalScript\|LocalScripts` can run from this location. This location is ideal for scripts that modify the experience's user interface, such as adding buttons, menus, and pop-ups. | | `Class.StarterPack` | Generally only contains `Class.Tool\|Tools`, but can also include `Class.LocalScript\|LocalScripts` for setting up player backpacks. | ## Scripting Roblox experiences support three different types of Luau scripts: - **Client scripts** These scripts run on the client, and the server has no visibility into their behavior. For legacy reasons, these scripts can take the form of `Class.LocalScript|LocalScripts` or `Class.Script|Scripts` with a `Class.BaseScript.RunContext|RunContext` value of `Enum.RunContext|Client`. Client scripts typically live in `Class.ReplicatedStorage`, `Class.StarterPlayerScripts`, or `Class.StarterCharacterScripts`. - **Server scripts** These scripts run on the server, and the client has no visibility into their behavior. Server scripts have a `Class.BaseScript.RunContext|RunContext` value of `Enum.RunContext|Server` and typically live in `Class.ServerScriptService`, the contents of which are not replicated to the client. - **Module scripts** These scripts are reusable pieces of code that return exactly one value, typically a function or table (or a table of functions). Rather than duplicating code in client and server scripts, use module scripts to share code and data between the two. Module scripts often live in `Class.ReplicatedStorage`, but can live elsewhere if you want to share code between scripts on the same side of the client-server boundary. Unreal doesn't have the concept of different script types. If you choose to make a multiplayer game, you write additional code to synchronize game state between the server and the clients. In Unreal, much of the engine's functionality is available by extending built-in classes like `UObject`, `ACharacters`, `ULevel`, and `UWorld` in C++ or Blueprint. Unreal supports custom events, but many classes include events that the engine automatically invokes as part of the natural life cycle of the level. Compared to the Unreal "ticking" system, Roblox scripts are much more event-driven. You access similar engine functionality by subscribing to services and listening for updates. ### C++ and Luau For scripting, Unreal uses C++. Roblox uses [Luau](/docs/en-us/luau.md), a scripting language derived from [Lua 5.1](https://www.lua.org/manual/5.1/). Compared to Luau, C++ has an overall performance advantage, which might or might not be relevant to the kinds of games you want to build. Luau is gradually typed and has a less verbose syntax. In larger projects, however, gradual typing can introduce categories of bugs that strongly typed languages like C++ avoid, so consider enabling [strict type checking](/docs/en-us/luau/type-checking.md#inference-modes) in Roblox scripts. Unreal also includes a visual scripting system called **Blueprints**. Roblox has third-party plugins that offer similar functionality, but no comparable system built-in. ### Luau code sample The following Luau code sample demonstrates how to, after a player equips a fishing pole, listen for user input (in this case, the `E` key) and call additional functions: ```lua local ContextActionService = game:GetService("ContextActionService") local ReplicatedStorage = game:GetService("ReplicatedStorage") -- Get a module script from ReplicatedStorage that returns a single function local performSomeAction = require(ReplicatedStorage.performSomeAction) -- Assumes that this script is a child of the fishing pole local fishingPole = script.Parent local ACTION_CAST = "Cast" -- Check that the key is down, then call another function local function castLine(_actionName, inputState, _inputObject) if inputState == Enum.UserInputState.Begin then performSomeAction() end end -- Only enable the action when the player equips the fishing pole fishingPole.Equipped:Connect(function() ContextActionService:BindAction(ACTION_CAST, castLine, true, Enum.KeyCode.E) end) -- Disable the action when the player unequips the fishing pole fishingPole.Unequipped:Connect(function() ContextActionService:UnbindAction(ACTION_CAST) end) ``` The Roblox script can be relatively concise because Roblox has many built-in assumptions: a `Class.Player` with a `Class.Humanoid` character connected to the server and can equip `Class.Tool|Tools`. These assumptions don't exist in Unreal, so the implementation would be very different. ## Assets Unreal and Roblox both support importing custom meshes and models in `.fbx` format. Certain types of assets may require specific configurations and export settings from your third-party modeling software. For more information, see the following pages: - [Importer](/docs/en-us/studio/importer.md) - [3D modeling specifications](/docs/en-us/art/modeling/specifications.md) - [Blender and Maya export requirements](/docs/en-us/art/modeling/export-requirements.md) In Unreal, assets import into your `Content` directory, visible in the **Content Browser**. In Roblox, assets import into your Workspace and into the [Toolbox](/docs/en-us/projects/assets/toolbox.md) or [Asset Manager](/docs/en-us/projects/assets/manager.md). Roblox also offers an open-source [Blender plugin](/docs/en-us/art/modeling/roblox-blender-plugin.md) to streamline the import process, similar to the **Send to Unreal** feature of Blender Tools. ## Transforms Unreal Engine's transforms and Roblox's `Datatype.CFrame|CFrames` serve similar purposes in representing 3D transformations of objects: - Both transforms and `Datatype.CFrame|CFrames` represent the position and rotation of an object in 3D space. Transforms include scale, whereas Roblox uses a `Class.BasePart.Size` property that isn't part of the `Datatype.CFrame`. - Both support multiplication for complex transformations and have built-in methods for other manipulations. ## Collaboration In Unreal, you collaborate with version control systems like Perforce or SVN, generally through Unreal's built-in user interface. These version control systems use the centralized "checkout" model that locks files while one person works on them. Roblox files live in the cloud (although you can export copies), so Roblox Studio provides built-in collaboration workflows for simultaneous editing, group management, permissions, script drafting, and more. See [Collaboration](/docs/en-us/projects/collaboration.md). > **Info:** Cloud syncing provides further benefits with [packages](/docs/en-us/projects/assets/packages.md), the Roblox equivalent of Unreal Blueprint classes. Converting an asset or asset hierarchy to a package helps with local reusability, but also with collaboration. When you or your collaborators publish a new version of a package, you can quickly update existing instances of that package within a game or set them to auto-update. ## Plugins Similar to Unreal, Roblox Studio supports [plugins](/docs/en-us/studio/plugins.md), which can simplify or give you additional control over various aspects of the development process. Plugins are available in the [Creator Store](/docs/en-us/production/creator-store.md), just like assets, many for free. ## Glossary | Unreal | Roblox | Notes | | --- | --- | --- | | Level | [Place](/docs/en-us/projects.md#places) | | | Actor | `Class.Part` or `Class.Model` | See [Philosophical differences](#philosophical-differences). | | Blueprint Class | [Package](/docs/en-us/projects/assets/packages.md) | | | Transform | `Datatype.CFrame` | `Datatype.CFrame` doesn't include scale information. See [Transforms](#transforms). | | Outliner | [Explorer](/docs/en-us/studio/explorer.md) | | | Details panel | [Properties](/docs/en-us/studio/properties.md) | | | Level Viewport | [3D viewport](/docs/en-us/studio/ui-overview.md#3d-viewport) | | | Content Browser | [Asset Manager](/docs/en-us/projects/assets/manager.md) or [Toolbox](/docs/en-us/projects/assets/toolbox.md) | | | Landscape Mode | [Terrain Editor](/docs/en-us/studio/terrain-editor.md) | | | PlayerStart | `Class.SpawnLocation` | | | Output Log | [Output](/docs/en-us/studio/output.md) | | | Marketplace | [Creator Store](/docs/en-us/production/creator-store.md) | | | Menu bar | [Toolbar](/docs/en-us/studio/ui-overview.md#toolbar-and-mezzanine) | | | Plugin | [Plugin](/docs/en-us/studio/plugins.md) | | --- title: "What is Roblox?" url: /docs/en-us/what-is-roblox last_updated: 2026-06-29T19:34:16Z description: "Roblox is a 3D creation platform that provides everything you need to build, test, distribute, and monetize 3D creations." --- # What is Roblox? Roblox is a 3D creation platform that provides everything you need to build, test, distribute, and monetize your creations. Creations can be items that users equip for their avatars, the 3D experiences that users join, or assets and plugins that you provide to other creators to build with. ## Experiences Learn, share, and interact with the Roblox community in the following places. #### Create your first experience Create a catapult that launches spheres into blocks to learn important Roblox concepts. #### Intro to world building Roblox is designed for massive 3D worlds. Learn how to use Roblox Studio to start building! #### Intro to physics Roblox is a simulation-first engine that mimics the real world. Learn more about how physics operates in the Roblox Engine. ## Avatars Avatars are the 3D representations of users on Roblox. They persist across all Roblox experiences and are fully customizable. #### Create avatar items Learn all about creating and selling avatar items, with step-by-step tutorials and tools to improve your workflow. #### Get started with UGC Watch this interview with PolarCub, one of the top UGC item creators on Roblox. #### Avatar items Get an overview of everything you can build for Roblox avatars. #### Rigid accessories Accessories are items that users can attach to their avatars at a single attachment point. #### Layered clothing Build clothing that can layer and stretch on a variety of avatar sizes and shapes. #### Custom characters Build completely custom avatar heads and bodies that users can equip. ## Growth and monetization Attract, retain, and monetize users. #### Discover Users find experiences on their Discover page, where they can immediately join and enjoy your experiences. #### Marketplace The Marketplace is the place where users can find, buy, sell, and trade items for their avatars. #### Creator Store The Creator Store lets creators share and sell assets with other creators. ## Tools From the web to studio, Roblox provides all the tools you need to start creating for free. #### Creator Hub Join a thriving community of creators and start your creation journey. #### Roblox Studio Get acclimated with Studio by downloading and launching the onboarding tour. #### Open Cloud Manage and access your creations from the web with a suite of REST APIs. --- title: "Customize the camera" url: /docs/en-us/workspace/camera last_updated: 2026-06-29T19:34:16Z description: "Explains how to configure an experience's camera settings." --- # Customize the camera Roblox's built-in camera powers a default **third person** mode and an optional **first person** mode, so you don't need to build your own following camera. For more customized scenarios, you can adjust the default properties in `Class.Camera` or replace it entirely like for over-the-shoulder, isometric, and weapon scoping views. ## Basic settings You can configure common camera settings directly within Studio's `Class.StarterPlayer` object. These settings include [zoom distance](#zoom-distance) and various [camera](#camera-mode), [occlusion](#occlusion-mode), and [movement](#movement-mode) modes. 1. In the **Explorer** window, select the **StarterPlayer** object.![StarterPlayer in Explorer](../assets/studio/explorer/StarterPlayer.png) 2. In the **Properties** window, scroll down to locate the **Camera** section. You can configure the following properties directly or through a script.![Camera properties on StarterPlayer in Properties Window](../assets/studio/properties/StarterPlayer-Camera-Group.png) ### Zoom distance Together, `Class.StarterPlayer.CameraMaxZoomDistance|CameraMaxZoomDistance` and `Class.StarterPlayer.CameraMinZoomDistance|CameraMinZoomDistance` set the range in which players can zoom the camera in respect to their player character. Setting a very high maximum such as 500 allows players to zoom the camera far out in space. If you want to lock the camera to a specific distance away from the character and prevent zooming, set both of these properties to the same value. ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer player.CameraMaxZoomDistance = 25 player.CameraMinZoomDistance = 50 ``` ### Camera mode The `Class.StarterPlayer.CameraMode|CameraMode` property sets the overall behavior of the camera between two options: | Setting | Description | | --- | --- | | **Classic** | The classic Roblox third-person camera which can be zoomed into first-person. Allows players to zoom in and out (unless zoom is locked) and rotate the camera around their character. | | **LockFirstPerson** | Locks the camera to first-person mode. When in this mode, all parts/elements of the player's character are invisible to them, except for equipped `Class.Tool\|Tools`. | ### Occlusion mode The `Class.StarterPlayer.DevCameraOcclusionMode|DevCameraOcclusionMode` property controls camera behavior when the player cannot see their character, such as when it's obscured by a `Class.BasePart`. | Setting | Description | | --- | --- | | **Zoom** | If the player's character moves behind an object with `Class.BasePart.Transparency\|Transparency` lower than 0.25, the camera zooms in very close to the character so that it can be seen. Once the character moves back into a viewable position, the camera zooms back out. | | **Invisicam** | If the player's character moves behind an object with `Class.BasePart.Transparency\|Transparency` lower than 0.75, the camera remains unmoved but the object becomes semi-transparent so that the character can be seen. Once the character moves back into a viewable position, the object returns to its normal opacity. | _**Zoom** and **Invisicam** occlusion modes_ ### Movement mode The `Class.StarterPlayer.DevComputerCameraMovementMode|DevComputerCameraMovementMode` (computer) and `Class.StarterPlayer.DevTouchCameraMovementMode|DevTouchCameraMovementMode` (phone/tablet) determine how the player can move the camera around. | Setting | Description | | --- | --- | | **UserChoice** | The camera will move based on the player's in-experience camera settings. | | **Classic** | The camera remains at its [zoom distance](#zoom-distance), tracking the player's character as it moves around the world. Players can also pitch the camera view up/down and orbit it around their character. | | **Follow** | Similar to **Classic** but the camera may rotate slightly to face the player's character if they're moving in any direction that isn't parallel to the camera's facing direction. | | **Orbital** | The camera remains at a fixed zoom distance and tracks the player's character as it moves around the world. Players can orbit the camera around their character but can't pitch the view up or down. | | **CameraToggle** | Only functional on computers (not phones/tablets) through `Class.StarterPlayer.DevComputerCameraMovementMode\|DevComputerCameraMovementMode`. When the player clicks the right mouse button, the camera toggles between **Classic** mode and a "free look" mode where moving the mouse looks around the world. | ## Script the camera Each player [client](/docs/en-us/projects/client-server.md) has its own `Class.Camera` object residing in the local `Class.Workspace`, accessible through the `Class.Workspace.CurrentCamera` property. You can override Roblox's default camera scripts by setting its `Class.Camera.CameraType|CameraType` to `Enum.CameraType|Scriptable` and then, most commonly, control the camera through the following properties. | Property | Description | | --- | --- | | `Class.Camera.CFrame` | The `Datatype.CFrame` of the camera. This is the most frequently used property for positioning and orienting a `Enum.CameraType\|Scriptable` camera in an experience. | | `Class.Camera.FieldOfView` | The extent of the observable 3D space that can be seen on screen, measured between 1–120 degrees in the direction defined by `Class.Camera.FieldOfViewMode`. Default is 70. | | `Class.Camera.CameraType` | Toggles between the various camera behaviors outlined in `Enum.CameraType`, some of which mimic the selectable [movement modes](#movement-mode). Setting this to `Enum.CameraType\|Scriptable` gives you full control of the camera. | | `Class.Camera.Focus` | The point in 3D space where the camera is looking. If you've set `Class.Camera.CameraType` to `Enum.CameraType\|Scriptable`, you should update this property every frame because certain visuals are more detailed depending on how close they are to the focus point. | --- title: "CFrames" url: /docs/en-us/workspace/cframes last_updated: 2026-06-29T19:34:16Z description: "CFrames, or Coordinate Frames, are a data type that you can use to rotate and position objects in the 3D space." --- # CFrames A `Datatype.CFrame`, short for **Coordinate Frame**, is a data type used to rotate and position 3D objects. As either an object property or a standalone unit, a `Datatype.CFrame` contains global **X**, **Y**, and **Z** coordinates as well as rotation data for each axis. In addition, `Datatype.CFrame|CFrames` contain helpful functions for working with objects in the 3D space. ## Position a CFrame You can create an empty `Datatype.CFrame` at the default position of `(0, 0, 0)` by using `Datatype.CFrame.new()`. To position a `Datatype.CFrame` at a specific point, provide **X**, **Y**, and **Z** coordinates as arguments to `Datatype.CFrame.new()`. In the following example, the `redBlock` part's `Datatype.CFrame` property changes to `newCFrame`, repositioning it to `(-2, 2, 4)` . ```lua local Workspace = game:GetService("Workspace") local redBlock = Workspace.RedBlock -- Create new CFrame local newCFrame = CFrame.new(-2, 2, 4) -- Overwrite the red block's current CFrame with the new CFrame redBlock.CFrame = newCFrame ``` _Before_ _After_ Alternatively, you can provide a new `Datatype.Vector3` position to `Datatype.CFrame.new()` and achieve the same result: ```lua local Workspace = game:GetService("Workspace") local redBlock = Workspace.RedBlock -- Create new CFrame local newVector3 = Vector3.new(-2, 2, 4) local newCFrame = CFrame.new(newVector3) -- Overwrite the red block's current CFrame with the new CFrame redBlock.CFrame = newCFrame ``` ## Rotate a CFrame To create a rotated `Datatype.CFrame`, use the `Datatype.CFrame.fromEulerAnglesXYZ()` constructor, providing a rotation angle in radians for the desired axes. The parameters to `Datatype.CFrame.fromEulerAnglesXYZ()` are in radians, not degrees; if you prefer degrees, use `Library.math.rad()` to convert degrees to radians. In the following example, the `redBlock` part rotates 45 degrees counterclockwise on its **Y** axis. ```lua local Workspace = game:GetService("Workspace") local redBlock = Workspace.RedBlock -- Create new rotated CFrame local newCFrame = CFrame.fromEulerAnglesXYZ(0, math.rad(45), 0) -- Overwrite the red block's current CFrame with the new CFrame redBlock.CFrame = newCFrame ``` _Before_ _After_ ## Face a CFrame toward a point You can use `Datatype.CFrame.lookAt()` to point the front surface of a `Datatype.CFrame` at a specific point in the world. The following example positions `redBlock` at `(0, 3, 0)` and points its front surface, marked by the white circle, at the `blueCube` part. ```lua local Workspace = game:GetService("Workspace") local redBlock = Workspace.RedBlock local blueCube = Workspace.BlueCube redBlock.Position = Vector3.new(0, 3, 0) -- Point the front surface of the red block at the blue cube redBlock.CFrame = CFrame.lookAt(redBlock.Position, blueCube.Position) ``` _Before_ _After_ ## Offset a CFrame To offset an object by a specific number of studs from its current position, add or subtract a `Datatype.Vector3` to or from a new `Datatype.CFrame` at the object's position. To get a properly-formatted `Datatype.Vector3` position of an object to use with `Datatype.CFrame.new()`, as seen here, its `Class.BasePart.Position|Position` property (`redBlock.Position`) is a convenient shortcut. ```lua local Workspace = game:GetService("Workspace") local redBlock = Workspace.RedBlock redBlock.CFrame = CFrame.new(redBlock.Position) + Vector3.new(0, 1.25, 0) ``` _Before_ _After_ You can use the same technique to offset an object from the position of another object. In the following example, a `Datatype.Vector3` adds to a new `Datatype.CFrame` created at the blue cube's position instead of the block's position. ```lua local Workspace = game:GetService("Workspace") local redBlock = Workspace.RedBlock local blueCube = Workspace.BlueCube redBlock.CFrame = CFrame.new(blueCube.Position) + Vector3.new(0, 2, 0) ``` _Before_ _After_ ## Dynamic CFrame orientation The `Datatype.CFrame.new()` and `Datatype.CFrame.fromEulerAnglesXYZ()` constructors reposition or rotate an object at a specific orientation within the world, but you sometimes can't rely on a fixed world position and rotation angle. For example: - Placing a floating treasure directly in front of a player who may be standing anywhere in the world, facing any direction. - Making a magical genie appear directly above a player's right shoulder. In these cases, use `Datatype.CFrame` methods instead of their constructors. ### Relative position The `Datatype.CFrame:ToWorldSpace()` function transforms an object's `Datatype.CFrame` — respecting its own local orientation — to a new **world** orientation. This makes it ideal for offsetting a part relative to itself or another object, regardless of how it's currently positioned/rotated. In the following example, the `redBlock` part offsets 2 studs relative to the **Y** axis of the blue cube (the green arrow pointing through it) and **not** relative to the global **Y** axis pointing straight up. ```lua local Workspace = game:GetService("Workspace") local redBlock = Workspace.RedBlock local blueCube = Workspace.BlueCube local offsetCFrame = CFrame.new(0, 2, 0) redBlock.CFrame = blueCube.CFrame:ToWorldSpace(offsetCFrame) ``` _Before_ _After_ ### Relative rotation You can also use `Datatype.CFrame:ToWorldSpace()` to rotate an object relative to itself. In the following example, the `redBlock` part rotates 70 degrees counterclockwise on its **Y** axis and 20 degrees clockwise on its **Z** axis. ```lua local Workspace = game:GetService("Workspace") local redBlock = Workspace.RedBlock local rotatedCFrame = CFrame.fromEulerAnglesXYZ(0, math.rad(70), math.rad(20)) redBlock.CFrame = redBlock.CFrame:ToWorldSpace(rotatedCFrame) ``` _Before_ _After_ ### Face a specific surface toward a point You can make the front of an object face another object by supplying a `Datatype.Vector3` point as the second parameter of `Datatype.CFrame.new()`. You can also use relative rotation to make any face of the object point toward a `Datatype.Vector3` point. The following example performs two consecutive `Datatype.CFrame` operations: 1. Point the **front** surface, marked by the white circle, at the target. 2. Rotate the `Datatype.CFrame` to make the **top** surface, marked by the black circle, point toward the target. ```lua local Workspace = game:GetService("Workspace") local redBlock = Workspace.RedBlock local blueCube = Workspace.BlueCube -- Point the red block's front surface at the blue cube redBlock.CFrame = CFrame.lookAt(redBlock.Position, blueCube.Position) -- Rotate CFrame relative to itself so that top surface (not front) points toward blue cube local rotatedCFrame = CFrame.fromEulerAnglesXYZ(math.rad(-90), 0, 0) redBlock.CFrame = redBlock.CFrame:ToWorldSpace(rotatedCFrame) ``` _Before_ _After_ ### Find a point between points You can use **linear interpolation**, or **lerp**, to position a `Datatype.CFrame` between two points. In the following example, the `redBlock` part repositions between the `greenCube` and `cyanCube` parts. The value of `0.7` places it 70% of the distance away from the green cube. ```lua local Workspace = game:GetService("Workspace") local redBlock = Workspace.RedBlock local greenCube = Workspace.GreenCube local cyanCube = Workspace.CyanCube redBlock.CFrame = greenCube.CFrame:Lerp(cyanCube.CFrame, 0.7) ``` _Before_ _After_ --- title: "Collisions" url: /docs/en-us/workspace/collisions last_updated: 2026-06-29T19:34:16Z description: "Explains methods to detect physical collisions, handle collision events, and fine-tune which objects collide with others." --- # Collisions A collision occurs when two 3D objects come into contact within the 3D world. For customized collision handling, `Class.BasePart` has a set of [collision events](#collision-events) and [collision filtering](#collision-filtering) techniques, so you can control which physical assemblies collide with others. ## Collision events Collision **events** occur when two `Class.BasePart|BaseParts` touch or stop touching in the 3D world. You can detect these collisions through the `Class.BasePart.Touched|Touched` and `Class.BasePart.TouchEnded|TouchEnded` events which occur regardless of either part's `Class.BasePart.CanCollide|CanCollide` property value. When considering collision handling on parts, note the following: - A part's `Class.BasePart.CanTouch|CanTouch` property determines whether it triggers collision events. If set to `false`, neither `Class.BasePart.Touched|Touched` nor `Class.BasePart.TouchEnded|TouchEnded` will fire. - A part's `Class.BasePart.CanCollide|CanCollide` property affects whether it will **physically** collide with other parts and cause forces to act upon them. Even if `Class.BasePart.CanCollide|CanCollide` is disabled for a part, you can detect touch and non‑touch through `Class.BasePart.Touched|Touched` and `Class.BasePart.TouchEnded|TouchEnded` events. - The `Class.BasePart.Touched|Touched` and `Class.BasePart.TouchEnded|TouchEnded` events only fire as a result of **physical** movement, not from a `Class.BasePart.Position|Position` or `Class.BasePart.CFrame|CFrame` changes that cause a part to intersect or stop intersecting another part. - The top-level `Class.Terrain` class inherits from `Class.BasePart`, so you can assign a [collision group](#collision-groups) to `Class.Terrain` to determine whether other `Class.BasePart|BaseParts` collide with [Terrain](/docs/en-us/parts/terrain.md) voxels. > **Info:** For performance optimization, set `Class.BasePart.CanTouch|CanTouch` to `false` for objects that don't require collisions. ### Touched The `Class.BasePart.Touched|Touched` event fires when a `Class.BasePart` comes in contact with another, or with a [Terrain](/docs/en-us/parts/terrain.md) voxel. It only fires as a result of **physical simulation** and will not fire when the part's `Class.BasePart.Position|Position` or `Class.BasePart.CFrame|CFrame` is explicitly set such that it intersects another part or voxel. The following code pattern shows how the `Class.BasePart.Touched|Touched` event can be connected to a custom `onTouched()` function. Note that the event sends the `otherPart` argument to the function, indicating the other part involved in the collision. ```lua local Workspace = game:GetService("Workspace") local part = Workspace.Part local function onTouched(otherPart) print(part.Name .. " collided with " .. otherPart.Name) end part.Touched:Connect(onTouched) ``` Note that the `Class.BasePart.Touched|Touched` event can fire multiple times in quick succession based on subtle physical collisions, such as when a moving object "settles" into a resting position or when a collision involves a [multi‑part model](#model-collisions). To avoid triggering more `Class.BasePart.Touched|Touched` events than necessary, you can implement a simple debounce system which enforces a "cooldown" period through an instance [attribute](/docs/en-us/studio/properties.md#instance-attributes). ```lua local Workspace = game:GetService("Workspace") local part = Workspace.Part local COOLDOWN_TIME = 1 local function onTouched(otherPart) if not part:GetAttribute("Touched") then print(part.Name .. " collided with " .. otherPart.Name) part:SetAttribute("Touched", true) -- Set attribute to true task.wait(COOLDOWN_TIME) -- Wait for cooldown duration part:SetAttribute("Touched", false) -- Reset attribute end end part.Touched:Connect(onTouched) ``` ### TouchEnded The `Class.BasePart.TouchEnded|TouchEnded` event fires when the entire collision bounds of a `Class.BasePart` exits the bounds of another `Class.BasePart` or a filled [Terrain](/docs/en-us/parts/terrain.md) voxel. It only fires as a result of **physical simulation** and will not fire when the part's `Class.BasePart.Position|Position` or `Class.BasePart.CFrame|CFrame` is explicitly set such that it stops intersecting another part or voxel. The following code pattern shows how the `Class.BasePart.TouchEnded|TouchEnded` event can be connected to a custom `onTouchEnded()` function. Like `Class.BasePart.Touched|Touched`, the event sends the `otherPart` argument to the function, indicating the other part involved. ```lua local Workspace = game:GetService("Workspace") local part = Workspace.Part local function onTouchEnded(otherPart) print(part.Name .. " is no longer touching " .. otherPart.Name) end part.TouchEnded:Connect(onTouchEnded) ``` ## Collision filtering Collision **filtering** defines which physical parts collide with others. You can configure filtering for numerous objects through [collision groups](#collision-groups) or you can control collisions on a [part‑to‑part](#part-to-part-filtering) basis with `Class.NoCollisionConstraint` instances. ### Collision groups Collision **groups** let you assign `Class.BasePart|BaseParts` to dedicated groups and specify whether or not they collide with those in other groups. Parts within non‑colliding groups pass through each other completely, even if both parts have their `Class.BasePart.CanCollide|CanCollide` property set to `true`. _In the video above, the spinning objects are in different collision groups such that they collide with objects of another color but not with objects of their own color_ You can easily set up collision groups through Studio's **Collision Groups** editor, accessible through Studio's **Window** ⟩ **3D** menu. The editor functions in either **List View** which favors [docking](/docs/en-us/studio/ui-overview.md#reposition-windows) to the left or right side of Studio, or in a wider **Table View**, which favors docking to the top or bottom. #### List View ![List View example in Collision Groups Editor](../assets/studio/collision-groups-editor/List-View.png) #### Table View ![Table View example in Collision Groups Editor](../assets/studio/collision-groups-editor/Table-View.png) #### Register groups #### Studio Editor The editor includes one **Default** collision group which cannot be renamed or deleted. All `Class.BasePart|BaseParts` automatically belong to this default group unless assigned to another group, meaning that they will collide with all other objects in the **Default** group. To create a new collision group: 1. Click the **Add Group** button along the top of the editor panel, enter a new group name, and press `Enter`. The new group appears in both columns of list view, or in both the left column and upper row of table view. #### List View![New group added to Collision Groups Editor in List View](../assets/studio/collision-groups-editor/New-Group-List-View.png) #### Table View![New group added to Collision Groups Editor in Table View](../assets/studio/collision-groups-editor/New-Group-Table-View.png) 2. Repeat the process if necessary, choosing a unique and descriptive name for each group. Note that you can change a group's name during development by clicking in its field, or by selecting it and clicking the **rename** button.![Button and field indicated for renaming a group in the Collision Groups Editor](../assets/studio/collision-groups-editor/Rename-Group.png) #### Scripting To create a new collision group through scripting, include the `Class.PhysicsService` service and register the group with `Class.PhysicsService:RegisterCollisionGroup()`. It may be helpful to pre-declare your group names in local variables, as the same strings can be used for [assigning objects](#assign-objects-to-groups) and [configuring groups](#configure-group-collisions) within the same script. ```lua local PhysicsService = game:GetService("PhysicsService") local cubes = "Cubes" local doors = "Doors" -- Register two collision groups PhysicsService:RegisterCollisionGroup(cubes) PhysicsService:RegisterCollisionGroup(doors) ``` > **Warning:** Since scripts are not guaranteed to execute in any particular order, it's highly recommended that you register collision groups in a single script. Abstracting group registration among multiple scripts may result in a race condition where a group is not yet registered at the time you [configure groups](#configure-group-collisions) or [assign objects](#assign-objects-to-groups) to them. #### Configure group collisions #### Studio Editor Under default configuration, objects in all groups collide with each other. To prevent objects in one group from colliding with objects in another group, **uncheck** the box in the respective row/column. In the following example, objects in the **Cubes** group will **not** collide with objects in the **Doors** group. #### List View ![Group configured in List View of Collision Groups Editor](../assets/studio/collision-groups-editor/Configure-Groups-List-View.png) #### Table View ![Group configured in Table View of Collision Groups Editor](../assets/studio/collision-groups-editor/Configure-Groups-Table-View.png) #### Scripting To configure how objects in two collision groups interact, call `Class.PhysicsService:CollisionGroupSetCollidable()|CollisionGroupSetCollidable()`, providing the two collision groups and a boolean `true` (collidable) or `false` (non‑collidable). If objects in the same group should or shouldn't collide with each other, use that group name for both the first and second parameters. ```lua local PhysicsService = game:GetService("PhysicsService") local cubes = "Cubes" local doors = "Doors" -- Register two collision groups PhysicsService:RegisterCollisionGroup(cubes) PhysicsService:RegisterCollisionGroup(doors) -- Set cubes to be non-collidable with doors PhysicsService:CollisionGroupSetCollidable(cubes, doors, false) ``` #### Assign objects to groups #### Studio Editor To assign objects to groups you've [registered](#register-groups) through the Studio editor: 1. Select one or more `Class.BasePart|BaseParts` that qualify as part of a collision group. 2. Assign them to the group by clicking the **⊕** button for its row. Objects can belong to only one collision group at a time, so placing them in a new group removes them from their current group.![Plus button indicated in Collision Groups Editor for adding selected parts to a group](../assets/studio/collision-groups-editor/Add-To-Group.png) Once assigned, the new group is reflected under the object's `Class.BasePart.CollisionGroup|CollisionGroup` property. ![Chosen collision group indicated as the part's CollisionGroup property](../assets/physics/collisions/BasePart-CollisionGroup.png) #### Scripting To add a `Class.BasePart` to a collision group through scripting, simply assign the group's **string name**, previously registered through `Class.PhysicsService:RegisterCollisionGroup()|RegisterCollisionGroup()`, to the part's `Class.BasePart.CollisionGroup|CollisionGroup` property. ```lua local PhysicsService = game:GetService("PhysicsService") local Workspace = game:GetService("Workspace") local cubes = "Cubes" local doors = "Doors" -- Register two collision groups PhysicsService:RegisterCollisionGroup(cubes) PhysicsService:RegisterCollisionGroup(doors) -- Set cubes to be non-collidable with doors PhysicsService:CollisionGroupSetCollidable(cubes, doors, false) -- Assign an object to each group Workspace.Cube1.CollisionGroup = cubes Workspace.Door1.CollisionGroup = doors ``` #### StudioSelectable collision group Tools in Studio use the collision filtering system to determine which objects are candidates for selection when clicking in the 3D viewport. Objects whose assigned collision group does **not** collide with **StudioSelectable** will be ignored. For example, if you have checkpoints in a racing experience whose effective areas are defined by large transparent parts, you can assign them to a **Checkpoints** collision group and then make that group non‑collidable with **StudioSelectable** so that they don't get in the way when you're editing the underlying map geometry. ![Checkpoints group configured to be non-collidable with StudioSelectable group](../assets/studio/collision-groups-editor/StudioSelectable-Off.png) For plugin code, it's recommended that you assign `"StudioSelectable"` as the collision group filter of your `Datatype.RaycastParams` when finding parts under the cursor. This allows your plugins to match the selection mechanics that creators have learned to expect from built‑in Studio tools. ```lua local UserInputService = game:GetService("UserInputService") local Workspace = game:GetService("Workspace") local raycastParams = RaycastParams.new() raycastParams.CollisionGroup = "StudioSelectable" -- To follow the convention raycastParams.BruteForceAllSlow = true -- So that parts with CanQuery of "false" can be selected local mouseLocation = UserInputService:GetMouseLocation() local mouseRay = Workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y) local filteredSelectionHit = Workspace:Raycast(mouseRay.Origin, mouseRay.Direction * 10000, raycastParams) ``` ### Part-to-part filtering To prevent collisions between two specific parts without setting up [collision groups](#collision-groups), such as between a vehicle's wheel and its chassis, consider the [No Collision](/docs/en-us/physics/mechanical-constraints.md#nocollisionconstraint) constraint. Advantages include: - Collision groups and/or configuration scripts are not required, so you can easily create and share models with customized collision filtering. - Connected parts will not collide with each other, but they can still collide with other objects. ### Disable character collisions Roblox player characters collide with each other by default. This can lead to interesting but unintended gameplay, such as characters jumping on top of each other to reach specific areas. If this behavior is undesirable, you can prevent it through the following `Class.Script` in `Class.ServerScriptService`. ```lua local PhysicsService = game:GetService("PhysicsService") local Players = game:GetService("Players") PhysicsService:RegisterCollisionGroup("Characters") PhysicsService:CollisionGroupSetCollidable("Characters", "Characters", false) local function onDescendantAdded(descendant) -- Set collision group for any part descendant if descendant:IsA("BasePart") then descendant.CollisionGroup = "Characters" end end local function onCharacterAdded(character) -- Process existing and new descendants for physics setup for _, descendant in character:GetDescendants() do onDescendantAdded(descendant) end character.DescendantAdded:Connect(onDescendantAdded) end Players.PlayerAdded:Connect(function(player) -- Detect when the player's character is added player.CharacterAdded:Connect(onCharacterAdded) end) ``` ## Model collisions `Class.Model` objects are containers for parts rather than inheriting from `Class.BasePart`, so they can't directly connect to `Class.BasePart.Touched` or `Class.BasePart.TouchEnded` events. To determine whether a model triggers a collision events, you need to loop through its children and connect the custom `onTouched()` and `onTouchEnded()` functions to each child `Class.BasePart`. > **Info:** For joined parts by [solid modeling](/docs/en-us/parts/solid-modeling.md) instead of `Class.Model` objects, see [Mesh and Solid Modeling Collisions](#mesh-and-solid-model-collisions). The following code sample connects all `Class.BasePart|BaseParts` of a multi‑part model to collision events and tracks the total number of collisions with other parts. ```lua local model = script.Parent local numTouchingParts = 0 local function onTouched(otherPart) -- Ignore instances of the model intersecting with itself if otherPart:IsDescendantOf(model) then return end -- Increase count of model parts touching numTouchingParts += 1 print(model.Name, "intersected with", otherPart.Name, "| Model parts touching:", numTouchingParts) end local function onTouchEnded(otherPart) -- Ignore instances of the model un-intersecting with itself if otherPart:IsDescendantOf(model) then return end -- Decrease count of model parts touching numTouchingParts -= 1 print(model.Name, "un-intersected from", otherPart.Name, "| Model parts touching:", numTouchingParts) end for _, child in model:GetChildren() do if child:IsA("BasePart") then child.Touched:Connect(onTouched) child.TouchEnded:Connect(onTouchEnded) end end ``` ## Mesh and solid model collisions `Class.MeshPart` and `Class.PartOperation` (parts joined by [solid modeling](/docs/en-us/parts/solid-modeling.md)) are subclasses of `Class.BasePart`, so meshes and solid modeled parts inherit the same [collision events](#collision-events) and [collision filtering](#collision-filtering) options as regular parts. However, since meshes and solid modeled parts usually have more complex geometries, they have a distinctive `Class.TriangleMeshPart.CollisionFidelity|CollisionFidelity` property which determines how precisely the physical bounds align with the visual representation for collision handling. The `Class.TriangleMeshPart.CollisionFidelity|CollisionFidelity` property has the following options, in order of fidelity and performance impact from lowest to highest: - **Box** — Creates a bounding collision box, ideal for small or non‑interactive objects. - **Hull** — Generates a convex hull, suitable for objects with less pronounced indentations or cavities. - **Default** — Produces an approximate collision shape that supports concavity, suitable for complex objects with semi-detailed interaction needs. - **PreciseConvexDecomposition** — Offers the most precise fidelity but still not a 1:1 representation of the visual. This option has the most expensive performance cost and takes longer for the engine to compute. #### Original Mesh ![Original mesh of castle tower](../assets/physics/collisions/Collision-Fidelity-MeshPart.jpg) #### Default ![Collision fidelity of Default shown for mesh](../assets/physics/collisions/Collision-Fidelity-Default.jpg) #### Box ![Collision fidelity of Box shown for mesh](../assets/physics/collisions/Collision-Fidelity-Box.jpg) #### Hull ![Collision fidelity of Hull shown for mesh](../assets/physics/collisions/Collision-Fidelity-Hull.jpg) #### Precise ![Collision fidelity of PreciseConvexDecomposition shown for mesh](../assets/physics/collisions/Collision-Fidelity-Precise.jpg) > **Success:** To view collision fidelity in Studio, toggle on **Collision fidelity** from the [Visualization Options](/docs/en-us/studio/ui-overview.md#visualization-options) widget in the upper‑right corner of the 3D viewport. For more information on the performance impact of collision fidelity options and how to mitigate them, see [Performance optimization](/docs/en-us/performance-optimization/improve.md#physics-computation). --- title: "3D workspace" url: /docs/en-us/workspace last_updated: 2026-06-29T19:34:16Z description: "Describes the workspace container service, which holds all objects that exist in the 3D world." --- # 3D workspace `Class.Workspace` is a container service that holds objects that you want the Roblox Engine to render in the 3D world. You typically will add these objects to the workspace: - `Class.BasePart` objects, which includes both `Class.Part` and `Class.MeshPart` objects. - `Class.Attachment` objects, which you can attach to special effect generators like a `Class.ParticleEmitter`, UI objects like a `Class.BillboardGui`, physical `Class.Constraint|Constraints`, and more. - `Class.Model` objects that organize geometric groupings. - `Class.Script` objects that are parented by other objects in the workspace. Scripts aren't rendered but can affect another object's rendering. ## Parts `Class.Part` objects represent the primitive building blocks in Roblox. By default, all parts have their physics simulated and are rendered if they appear in the 3D workspace. Parts can take the shape of blocks, spheres, cylinders, wedges, or corner wedges. In addition, `Class.TrussPart` acts as a truss beam that characters can climb like a ladder. ![A single Block part.](../assets/modeling/parts/Basic-Part-Block.png)_ Block_ ![A single Sphere part.](../assets/modeling/parts/Basic-Part-Sphere.png)_ Sphere_ ![A single Cylinder part.](../assets/modeling/parts/Basic-Part-Cylinder.png)_ Cylinder_ ![A single Wedge part.](../assets/modeling/parts/Basic-Part-Wedge.png)_ Wedge_ ![A single Corner Wedge part.](../assets/modeling/parts/Basic-Part-Corner-Wedge.png)_ Corner Wedge_ You can also apply [solid modeling](/docs/en-us/parts/solid-modeling.md) operations to parts, such as union or negate, to combine them into something more complex like bowls or hollow pipes. ## Meshes A `Class.MeshPart` is an object that represents a mesh (a collection of vertices, edges, and faces that make up a 3D object). You typically create meshes using third-party software such as [Blender](https://www.blender.org) or [Maya](https://www.autodesk.com/products/maya/overview), then import them as a `Class.MeshPart` using Studio. Meshes can include far more detail than any solid modeling you can do in Studio. They can also have internal rigs and textures, allowing you to create lifelike objects that you can pose and animate. ![A high-quality treasure chest mesh with a texture.](../assets/modeling/meshes/Mesh-Textured.jpg)_Mesh with texture_ ![A realistic looking leafy bush with shadows and depth.](../assets/modeling/meshes/Mesh-SurfaceAppearance.jpg)_Mesh with SurfaceAppearance_ ## Terrain The `Class.Terrain` object allows you to generate and sculpt detailed and realistic terrain environments, such as mountains, bodies of water, grass-covered hills, or a flat desert. Using the [Terrain Editor](/docs/en-us/studio/terrain-editor.md), you can easily generate and alter large regions of terrain. ![A viewport view of desert terrain with mountains in the distance.](../assets/modeling/terrain/Showcase.jpg) ## Models A `Class.Model` is a container object for **geometric groupings**, such as `Class.BasePart`, `Class.Motor6D` objects, and other models. Models can be simple groupings or you can set a primary part within the model, so that it functions as an assembly, which the physics engine treats as a single rigid body. Models can also contain scripts that act on the individual objects of the model. ![A humanoid model of a creepy girl with four red eyes standing in an A pose. She wears a maroon dress with webs and bright red stockings.](../assets/modeling/model-objects/Model-Example-3D.jpg)_A model named Octavia_ ![A close up view of the model's children in the Explorer window that comprise the model.](../assets/modeling/model-objects/Model-Example-Hierarchy.png)_The groupings that comprise the model_ ## Workspace access in scripts Within a script, you can access a place's `Class.Workspace` in three different ways, all of which are valid. - `game:GetService("Workspace")` - `workspace` - `game.Workspace` From there, you can carry out a large set of use cases to script logic for your experiences and create dynamic worlds and interactions. For example: - Obtain a reference to any object in the workspace to change its properties during runtime. - Obtain a reference to a user's `Class.Camera` object to [manipulate their view](/docs/en-us/workspace/camera.md) of the workspace. - Listen for events on objects in the workspace to carry out logic at specific times, such as when a user's playable character [touches an object](/docs/en-us/workspace/collisions.md). --- title: "Raycasting" url: /docs/en-us/workspace/raycasting last_updated: 2026-06-29T19:34:16Z description: "Raycasting allows you to detect collisions and determine the position of objects." --- # Raycasting At its most basic level, **raycasting** is the act of sending out an invisible ray from a `Datatype.Vector3` point in a specific direction with a defined length. Once cast, you can detect if the ray hits a `Class.BasePart` or `Class.Terrain` cell. _Lasers are fired by floating orbs, and raycasting determines whether a laser hits a platform. Platforms touched by the lasers are temporarily destroyed._ You can cast a ray with the `Class.WorldRoot:Raycast()` method (`Class.Workspace:Raycast()`) from a `Datatype.Vector3` origin in a `Datatype.Vector3` direction. ```lua local Workspace = game:GetService("Workspace") local rayOrigin = Vector3.new(0, 0, 0) local rayDirection = Vector3.new(0, -100, 0) local raycastResult = Workspace:Raycast(rayOrigin, rayDirection) ``` > **Warning:** When casting a ray, the direction parameter should encompass the desired length of the ray. For instance, if the magnitude of the direction is 10, the resulting ray will also be of length 10. The maximum length is 15,000 studs. When applicable, you can calculate an unknown directional vector (`rayDirection`) using a known **origin** and **destination**. This is useful when casting a ray between two points that can change, such as from one player character to another. 1. The origin plus a directional vector indicate the ray's destination: rayOrigin + rayDirection = rayDestination \text{rayOrigin} + \text{rayDirection} = \text{rayDestination}rayOrigin+rayDirection=rayDestination 2. Subtract rayOrigin \text{rayOrigin}rayOrigin from both sides of the equation: rayOrigin + rayDirection − rayOrigin = rayDestination − rayOrigin \text{rayOrigin} + \text{rayDirection} - \text{rayOrigin} = \text{rayDestination} - \text{rayOrigin}rayOrigin+rayDirection−rayOrigin=rayDestination−rayOrigin 3. The ray's direction equals the destination minus the origin: rayDirection = rayDestination − rayOrigin \text{rayDirection} = \text{rayDestination} - \text{rayOrigin}rayDirection=rayDestination−rayOrigin ```lua local Workspace = game:GetService("Workspace") local rayOrigin = Workspace.TestOrigin.Position local rayDestination = Workspace.TestDestination.Position local rayDirection = rayDestination - rayOrigin local raycastResult = Workspace:Raycast(rayOrigin, rayDirection) ``` ## Filter options `Class.WorldRoot:Raycast()` accepts an optional `Datatype.RaycastParams` object which tells the raycast to selectively include or exclude certain `Class.BasePart|BaseParts`, ignore the **Water** material for `Class.Terrain`, or use a [collision group](/docs/en-us/workspace/collisions.md#collision-filtering). | Key | Description | | --- | --- | | `FilterDescendantsInstances` | `Array` of objects whose descendants are used in filtering raycasting candidates. | | `FilterType` | `Enum.RaycastFilterType` enum which determines how the `FilterDescendantsInstances` array is used in the raycast operation. | | `IgnoreWater` | Boolean which determines whether the **Water** material is considered when raycasting against `Class.Terrain`. | | `CollisionGroup` | String name of the [collision group](/docs/en-us/workspace/collisions.md#collision-filtering) used for the raycasting operation. | ```lua local Workspace = game:GetService("Workspace") local rayOrigin = Vector3.zero local rayDirection = Vector3.new(0, -100, 0) local raycastParams = RaycastParams.new() raycastParams.FilterDescendantsInstances = {script.Parent} raycastParams.FilterType = Enum.RaycastFilterType.Exclude raycastParams.IgnoreWater = true local raycastResult = Workspace:Raycast(rayOrigin, rayDirection, raycastParams) ``` ## Hit detection If the raycasting operation hits an eligible `Class.BasePart` or `Class.Terrain` cell, a `Datatype.RaycastResult` object is returned containing the results. To test for a hit, confirm that the result is not `nil` and utilize the following properties as needed. | Property | Description | | --- | --- | | `Class.Instance` | The `Class.BasePart` or `Class.Terrain` cell that the ray intersected. | | `Position` | `Datatype.Vector3` position of the intersection between the ray and the `Class.Instance`. | | `Distance` | Distance between the ray origin and intersection point. | | `Material` | The `Enum.Material` of the `Class.BasePart` or `Class.Terrain` at the intersection point. | | `Normal` | `Datatype.Vector3` of the normal vector of the intersected face. | > **Warning:** When used in a place with [instance streaming](/docs/en-us/workspace/streaming.md) enabled, a client may not have streamed in distant `Class.BasePart|BaseParts`, so a raycast may not detect a hit. Additionally, low-detail "imposter" `Class.Terrain` and `Class.Model|Models` generated by the streaming system are purely visual and are not eligible raycast targets. > **Info:** You can exempt any `Class.BasePart` from hit detection by setting its `Class.BasePart.CanQuery` property to `false`. ```lua local Workspace = game:GetService("Workspace") local rayOrigin = Vector3.zero local rayDirection = Vector3.new(0, -100, 0) local raycastResult = Workspace:Raycast(rayOrigin, rayDirection) if raycastResult then print("Instance:", raycastResult.Instance) print("Position:", raycastResult.Position) print("Distance:", raycastResult.Distance) print("Material:", raycastResult.Material) print("Normal:", raycastResult.Normal) else warn("No raycast result!") end ``` --- title: "Instance streaming" url: /docs/en-us/workspace/streaming last_updated: 2026-06-29T19:34:16Z description: "Instance streaming allows the Roblox engine to dynamically load and unload 3D content in regions of the world." --- # Instance streaming In-game **instance streaming** allows the Roblox engine to dynamically load and unload 3D content and related instances in the `Class.Workspace`. This can improve the overall player experience in several ways, including: - **Faster Join Times** — Players can start playing in one part of the world while more of the world loads in the background. - **Memory Efficiency** — Games can be played on devices with less memory since content is dynamically streamed in and out. More immersive and detailed worlds can be played on a wider range of devices. - **Improved Performance** — Better frame rates and performance, as the server can spend less time and bandwidth synchronizing changes between the world and players in it. Clients spend less time updating instances that aren't currently relevant to the player. - **Level of Detail** — When configured, distant models, platform avatars, and terrain remain visible even when they're not streamed to clients, keeping the game optimized without entirely sacrificing background visuals. Instance streaming is controlled through the `Class.Workspace.StreamingEnabled` property, enabled by default for new places created in Studio. This property cannot be set in a script. ![The Properties window with the StreamingEnabled property enabled.](../../assets/studio/properties/Workspace-StreamingEnabled.png) > **Success:** Once you review this technical guide, it's recommended that you review the [streaming techniques](/docs/en-us/workspace/techniques.md) guide on how to use streaming efficiently and effectively. ## Technical behavior ### Scope Streaming logic and features apply exclusively to instances that are descendants of `Class.Workspace`, while instances stored in other containers such as `Class.ReplicatedStorage` and `Class.ReplicatedFirst` are ineligible for streaming. For example, placing an [atomic](#atomic) model under `Class.ReplicatedStorage` does not guarantee atomic replication. ### Stream in When a player joins a game with instance streaming enabled: 1. Instances in the `Class.Workspace` are replicated to the client **excluding** the following:`Class.BasePart|BaseParts` (`Class.Part|Parts` and `Class.MeshPart|MeshParts`)`Class.Model|Models` set to [Atomic](#atomic), [Persistent](#persistent), or [PersistentPerPlayer](#persistentperplayer); see [per‑model streaming controls](#model-streaming-controls)`Class.Model|Models` set to [Nonatomic](#nonatomic) (default) when `Class.Workspace.ModelStreamingBehavior` is set to `Enum.ModelStreamingBehavior.Improved|Improved` Descendants of the above instances 2. During gameplay, the server may stream instances in the above deferred categories to the client based on the game's [streaming properties](#streaming-properties), player position, client device performance, and other conditions. ### Stream out During gameplay, a client may remove regions of `Class.BasePart|BaseParts` from the player's `Class.Workspace` based on the behavior set by `Class.Workspace.StreamOutBehavior`. The process begins with regions furthest away from the [replication foci](#replication-focus) and moves in closer, as needed. Regions inside the `Class.Workspace.StreamingMinRadius` range never stream out. Note that a `Class.Model` with no `Class.BasePart` descendants, such as one used merely as a "container" for non‑`Class.BasePart` instances like scripts, is **exempt** from streaming out **unless** it is made a descendant of a `Class.BasePart`. > **Info:** Instances which are **created** or **cloned** by client-side scripts are exempted from streaming out unless they are parented under a server‑created instance. > **Warning:** When an instance streams out, it is parented to `nil` so that any existing Luau state will reconnect if the instance streams back in. As a result, removal signals such as `Class.Instance.ChildRemoved|ChildRemoved` or `Class.Instance.DescendantRemoving|DescendantRemoving` fire on its **parent** or **ancestor**, but the instance itself is not destroyed in the same sense as an `Class.Instance:Destroy()` call. Local-only changes to instance properties (changes that exist on a client and have not been replicated to the server) can be lost if the instances streams out and later streams back in. ### Assemblies Physical [assemblies](/docs/en-us/physics/assemblies.md) stream **in** as complete units, including their associated `Class.Constraint|Constraints` and `Class.Attachment|Attachments`, helping to ensure consistent physics updates on clients. The only exception is when the assembly is [anchored](/docs/en-us/physics/assemblies.md#anchoring-behavior), in which case only the `Class.BasePart|BaseParts` within the streaming radius are streamed in alongside their associated `Class.Constraint|Constraints` and `Class.Attachment|Attachments`. Assemblies do not stream **out** until all of their `Class.BasePart|BaseParts` are eligible to stream out. > **Warning:** Avoid creating moving assemblies with unnecessarily large numbers of instances, as all of the instances streaming in unison may cause network/CPU spikes. ## Streaming properties The following properties control how instance streaming applies to your game. All of these properties are **non-scriptable** and must be set on the `Class.Workspace` object in Studio. | Property | Description | | --- | --- | | `Class.Workspace.EnableSLIMAvatars\|EnableSLIMAvatars` | Controls whether a `Enum.ModelLevelOfDetail.SLIM\|SLIM` model is generated for avatar characters in the game. When enabled, avatars render using SLIM in the same way that setting `Class.Model.LevelOfDetail` to `Enum.ModelLevelOfDetail.SLIM\|SLIM` works for other models. | | `Class.Workspace.ModelStreamingBehavior\|ModelStreamingBehavior` | Controls how [Nonatomic](#nonatomic) (default) models stream in and out.

**RECOMMENDED** Use `Enum.ModelStreamingBehavior.Improved\|Improved` to enable the most efficient streaming for `Class.Model\|Models` with `Class.BasePart` descendants. | | `Class.Workspace.StreamingIntegrityMode\|StreamingIntegrityMode` | The game may behave in unintended ways if a player moves into a region of the world that hasn't been streamed to them. This property offers a way to avoid those potentially problematic situations.

**RECOMMENDED** Use `Enum.StreamingIntegrityMode.PauseOutsideLoadedArea\|PauseOutsideLoadedArea` to balance gameplay integrity without pausing unnecessarily or too often. You can also [customize the pause screen](#pause-screen-customization). | | `Class.Workspace.StreamingMinRadius\|StreamingMinRadius` | This property indicates the radius around the [replication foci](#replication-focus) in which instances stream in at the highest priority. Care should be taken when increasing the default, as doing so will require more memory and more server bandwidth at the expense of other components.

**RECOMMENDED** Use the default of `64` to maximize how much the engine can scale the game down for low‑end devices. | | `Class.Workspace.StreamingTargetRadius\|StreamingTargetRadius` | This property controls the maximum distance away from the [replication foci](#replication-focus) in which instances stream in. Note that the engine is allowed to retain previously loaded instances beyond the target radius, memory permitting.

A smaller `Class.Workspace.StreamingTargetRadius\|StreamingTargetRadius` reduces server workload, as the server will not stream in additional instances beyond the set value. However, the target radius is also the maximum distance players will be able to see the full detail of your game, so you should pick a value that creates a nice balance between these.

**RECOMMENDED** Use the default of `1024` to strike a good balance between visibility for players on high‑end devices and a reasonable memory footprint. | | `Class.Workspace.StreamOutBehavior\|StreamOutBehavior` | This property sets the [streaming out](#stream-out) behavior according to the value of `Enum.StreamOutBehavior`. If set to `Enum.StreamOutBehavior.LowMemory\|LowMemory` (default), the client only streams out regions beyond the minimum radius in a low memory situation. If set to `Enum.StreamOutBehavior.Opportunistic\|Opportunistic`, regions beyond `Class.Workspace.StreamingTargetRadius\|StreamingTargetRadius` can be removed on the client even when there is no memory pressure (in this mode, the client never removes instances that are within the target radius, except in low memory situations).

**RECOMMENDED** Use `Enum.StreamOutBehavior.Opportunistic\|Opportunistic` to allow the client to aggressively garbage collect content, significantly reducing memory usage and helping prevent out‑of‑memory crashes. | > **Warning:**`Class.Workspace.StreamingTargetRadius|StreamingTargetRadius` should be larger than `Class.Workspace.StreamingMinRadius|StreamingMinRadius`. 3D content between the target radius and the minimum radius acts as a buffer in case the client temporarily stops receiving new content from the server. If the minimum radius and the target radius are equal, there is no buffer, which can lead to an increase in network pauses or an otherwise suboptimal player experience. ## Replication focus By default, streaming occurs around the local player's character's `Class.Model.PrimaryPart|PrimaryPart`, although you can specify a different replication focus point through `Class.Player.ReplicationFocus`. You can also add and remove additional replication foci through `Class.Player:AddReplicationFocus()` and `Class.Player:RemoveReplicationFocus()` to dynamically enable streaming in multiple areas of the game. > **Warning:** Use caution when adding additional replication foci as each additional focus increases the server's workload for streaming and updating regions. For example, a single player with nine dynamically moving foci could generate server networking and streaming processing comparable to ten players moving around the game. > > On the client, too many foci for a player can limit the engine's ability to adjust to memory limitations and make it more likely for clients to be killed by the OS for using too much memory. #### Physics Simulation Client-side physics simulation, including prediction and resimulations when [server authority](/docs/en-us/projects/server-authority.md) is implemented, only occurs in streamed areas, even for locally created instances and for `Enum.ModelStreamingMode.Persistent|Persistent` instances. If you have instances that you'd like to keep simulating even when they're far away from the character, create an additional replication focus near those instances. #### Movement Between Zones In many games, players frequently move back and forth between the same areas, for example between their "home base" and a "trading hub." In such cases, you can create a replication focus point in each area to ensure those areas are readily present on client devices. #### Distant Viewpoints Multiple replication points are useful when players can view specific, important regions through a scope, such as enemy bases scattered across a barren landscape. In such cases, you can create a replication focus point in each base to ensure players see details and simulated physics from afar. ## Model streaming controls To avoid issues with streaming on a per-model basis and minimize use of `Class.Instance:WaitForChild()|WaitForChild()`, you can customize how `Class.Model|Models` and their descendants stream through their `Class.Model.ModelStreamingMode|ModelStreamingMode` property. ### Nonatomic By default, models are `Enum.ModelStreamingMode.Nonatomic|Nonatomic` and they stream in/out based on whether `Class.Workspace.ModelStreamingBehavior` is set to `Enum.ModelStreamingBehavior.Legacy|Legacy` (default) or `Enum.ModelStreamingBehavior.Improved|Improved`. ###### Legacy When `Class.Workspace.ModelStreamingBehavior` is set to `Enum.ModelStreamingBehavior.Legacy|Legacy` (default), the `Class.Model` container and its **non**‑`Class.BasePart` descendants such as `Class.Script|Scripts` replicate to the client when the player joins. Then, when eligible, the model's `Class.BasePart` descendants stream in. ###### Improved When `Class.Workspace.ModelStreamingBehavior` is set to `Enum.ModelStreamingBehavior.Improved|Improved`, model streaming behavior varies by whether the model contains `Class.BasePart` descendants or does **not** contain `Class.BasePart` descendants: - A model with `Class.BasePart` descendants streams **in** only when **one** of its `Class.BasePart` descendants is eligible to stream in. At that point, the model and the eligible `Class.BasePart` streams in, along with the model's **non**‑`Class.BasePart` descendants. When the very last remaining `Class.BasePart` of the model streams **out**, the model itself will stream out along with it. - A model with no `Class.BasePart` descendants, such as one used merely as a "container" for non‑`Class.BasePart` instances like scripts, replicates to the client soon after the player joins. This type of model is **exempt** from streaming out **unless** it is made a descendant of a `Class.BasePart`. ### Atomic If a `Class.Model` is set to `Enum.ModelStreamingMode.Atomic|Atomic`, all of its initial descendants are streamed in together when a descendant `Class.BasePart` is eligible to be streamed in. As a result, a client‑side script that needs to access instances **inside** the model would need to use `Class.Instance:WaitForChild()|WaitForChild()` on the model itself, but not on a descendant `Class.MeshPart` or `Class.Part` since they are sent alongside the model. An atomic model is only streamed out when all of its descendant parts are eligible for streaming out, at which point the entire model streams out together. ![A diagram showing Atomic model streaming along with children.](../../assets/optimization/streaming/Model-Streaming-Mode-Atomic.png) ### Persistent `Enum.ModelStreamingMode.Persistent|Persistent` models are not subject to normal streaming in or out. They are sent as a complete atomic unit soon after the player joins and before the `Class.Workspace.PersistentLoaded` event fires. Persistent models and their descendants are never streamed out, but to safely handle streaming in within a client‑side script, you should wait for the `Class.Workspace.PersistentLoaded|PersistentLoaded` event to fire. ![A diagram showing Persistent model streaming along with children.](../../assets/optimization/streaming/Model-Streaming-Mode-Persistent.png) > **Warning:** Persistent models are intended for very rare circumstances, such as when a small number of parts must always be present on clients for client‑side scripts to access. If possible, server-side `Class.Script|Scripts` should be used, or client scripts should be tolerant of parts streaming in and out. Persistent models are **not** intended to circumvent streaming, and overuse may negatively impact performance. ### PersistentPerPlayer Models set to `Enum.ModelStreamingMode.PersistentPerPlayer|PersistentPerPlayer` behave the same as `Enum.ModelStreamingMode.Persistent|Persistent` for players that have been added using `Class.Model:AddPersistentPlayer()`. For other players, behavior is the same as `Enum.ModelStreamingMode.Atomic|Atomic`. You can revert a model from player persistence via `Class.Model:RemovePersistentPlayer()`. ## Pause screen customization Assuming `Class.Workspace.StreamingIntegrityMode` is set to the recommended `Enum.StreamingIntegrityMode.PauseOutsideLoadedArea|PauseOutsideLoadedArea`, the game will pause if a player moves into a region of the world that hasn't been streamed to them. In these cases, the `Class.Player.GameplayPaused` property indicates the player's current pause state which can be used with a `Class.Instance:GetPropertyChangedSignal()|GetPropertyChangedSignal()` connection to show or hide a custom GUI. ```lua local Players = game:GetService("Players") local GuiService = game:GetService("GuiService") local player = Players.LocalPlayer -- Disable default pause modal GuiService:SetGameplayPausedNotificationEnabled(false) local function onPauseStateChanged() if player.GameplayPaused then -- Show custom GUI else -- Hide custom GUI end end player:GetPropertyChangedSignal("GameplayPaused"):Connect(onPauseStateChanged) ``` ## Runtime debugging The engine includes multiple on-screen debug panels that can be enabled on the client using keyboard shortcuts. To access streaming debug information: 1. Open the **Network Summary** debug overlay via `Shift``Ctrl``F3` (Windows) or `Shift``⌘``F3` (Mac). 2. Once the debug overlay is open, press `Shift``1` repeatedly to cycle through the available panels. The fourth panel is the **Streaming** debug view which displays useful runtime information: - Active streaming settings - Currently loaded (streamed in) regions - Streaming behavior and state Once enabled, colored highlighted regions appear in the 3D viewport: | Color | Description | | --- | --- | | `rgb(255,0,0)` | Less than `Class.Workspace.StreamingMinRadius\|StreamingMinRadius` | | `rgb(255,255,0)` | Greater than or equal to `Class.Workspace.StreamingMinRadius\|StreamingMinRadius` and less than `Class.Workspace.StreamingTargetRadius\|StreamingTargetRadius` | | `rgb(0,0,255)` | Equal to `Class.Workspace.StreamingTargetRadius\|StreamingTargetRadius` | | `rgb(0,255,0)` | Greater than `Class.Workspace.StreamingTargetRadius\|StreamingTargetRadius` | > **Info:** To increase the ability to zoom the default camera considerably far out while debugging, temporarily increase `Class.StarterPlayer.CameraMaxZoomDistance` while in edit mode or `Class.Player.CameraMaxZoomDistance` during runtime. A value of `1000`, for example, allows you to pull the camera out to view most of the 3D world while still navigating the character around.
--- title: "Techniques and conversion" url: /docs/en-us/workspace/streaming/techniques last_updated: 2026-06-29T19:34:16Z description: "Step‑by‑step guidelines on implementing instance streaming in a non‑streaming game, as well as techniques for using it efficiently and effectively." --- # Techniques and conversion This guide outlines several techniques for using in-game [instance streaming](/docs/en-us/workspace/streaming.md) efficiently and effectively. While there's no "one size fits all" solution for designing a streaming game or converting a non‑streaming game to streaming, following these high‑level steps will get you most of the way there. > **Success:** Set [recommended streaming properties](#streaming-properties) on the top-level `Class.Workspace` object. > **Success:** Configure [model level-of-detail](#model-level-of-detail) to make models look good even when they're not streamed in. > **Success:**[Restructure models](#model-structure) to work best under streaming and enable [SLIM avatars](#slim-avatars). > **Success:**[Update scripts](#script-patterns) to handle a partial client `Class.DataModel` and manage client-side changes that streaming may undo. > **Success:** Test thoroughly under [realistic streaming conditions](#realistic-test-conditions). ## Streaming properties Once `Class.Workspace.StreamingEnabled|StreamingEnabled` is toggled on for the `Class.Workspace` object in Studio, set its related properties to the following recommended values: | Property | Recommendation | | --- | --- | | `Class.Workspace.EnableSLIMAvatars\|EnableSLIMAvatars` | Use `Enum.RolloutState.Enabled\|Enabled` to render standard rig avatars as lightweight, animated stand‑ins when appropriate. See [SLIM avatars](#slim-avatars) for more info. | | `Class.Workspace.ModelStreamingBehavior\|ModelStreamingBehavior` | Use `Enum.ModelStreamingBehavior.Improved\|Improved` to enable the most efficient streaming for `Class.Model\|Models` with `Class.BasePart` descendants. | | `Class.Workspace.StreamingIntegrityMode\|StreamingIntegrityMode` | Use `Enum.StreamingIntegrityMode.PauseOutsideLoadedArea\|PauseOutsideLoadedArea` to balance gameplay integrity without pausing unnecessarily or too often. | | `Class.Workspace.StreamingMinRadius\|StreamingMinRadius` | Use the default of `64` to maximize how much the engine can scale the game down for low‑end devices. | | `Class.Workspace.StreamingTargetRadius\|StreamingTargetRadius` | Use the default of `1024` to strike a good balance between visibility for players on high‑end devices and a reasonable memory footprint. | | `Class.Workspace.StreamOutBehavior\|StreamOutBehavior` | Use `Enum.StreamOutBehavior.Opportunistic\|Opportunistic` to allow the client to aggressively garbage collect content, significantly reducing memory usage and helping prevent out‑of‑memory crashes. | ## Model level-of-detail `Class.Model.LevelOfDetail` helps fill in non‑streamed `Class.Model` content with lightweight composite or imposter meshes, making the world look visually complete. **SLIM** (Scalable Lightweight Interactive Model) composite meshes are particularly effective, as players often cannot distinguish a SLIM mesh from the fully streamed‑in original. For best results: - Group parts that are spatially and logically related, for example all the parts of a car. - Set `Class.Model.LevelOfDetail|LevelOfDetail` to `Enum.ModelLevelOfDetail.SLIM|SLIM` on models that contain **static** meshes and parts. Models that contain skinned meshes, are modified at runtime, or play animations are not supported. - Keep the spatial extent of each model under ~64 cubic studs to increase the likelihood that the entire actual model streams in together. If a model has very large extents, break it up into smaller modular models and apply an appropriate `Class.Model.LevelOfDetail|LevelOfDetail` to each one. _`Enum.ModelLevelOfDetail.Disabled|Disabled`/`Enum.ModelLevelOfDetail.Automatic|Automatic`_ _`Enum.ModelLevelOfDetail.SLIM|SLIM`_ ## Model structure Beyond setting [model level‑of‑detail](#model-level-of-detail), the structure and settings of your `Class.Model|Models` has a significant impact on how well streaming performs. As you build out or convert an existing game: - **Use atomic models for logical grouping** — When a script needs access to all of the parts within a model, set its `Class.Model.ModelStreamingMode|ModelStreamingMode` to `Enum.ModelStreamingMode.Atomic|Atomic`. This allows client‑side scripts to safely access instances inside the model without excessive use of `Class.Instance:WaitForChild()|WaitForChild()` (although such scripts must still use `Class.Instance:WaitForChild()|WaitForChild()` for the overall atomic model). > **Info:** Atomicity guarantees apply only during initial replication. Once an atomic model has been replicated to the client, any **new** instances added under that model are **not** replicated atomically and are instead streamed according to normal [streaming behavior](/docs/en-us/workspace/streaming.md#technical-behavior). Systems that dynamically modify atomic models must handle late‑arriving instances explicitly where necessary. - **Minimize persistent models** — [Persistent](/docs/en-us/workspace/streaming.md#persistent) models load after join and never stream out, permanently occupying memory. Set a model's `Class.Model.ModelStreamingMode|ModelStreamingMode` to `Enum.ModelStreamingMode.Persistent|Persistent` only if it **must** remain available and accessible to scripts at all times. - **Decompose container models** — A common non‑streaming pattern is a single huge `Class.Model` that contains many NPCs, props, or similar groupings. Under streaming, container models decrease streaming efficiency and they aren't optimal for [model level‑of‑detail](#model-level-of-detail) which works best with closely grouped instances. Decompose container models into smaller models with physically close or logically related parts. - **Flatten deeply nested model hierarchies** — Nesting a [persistent](/docs/en-us/workspace/streaming.md#persistent) model inside an [atomic](/docs/en-us/workspace/streaming.md#atomic) model effectively forces the atomic model to behave as persistent. Flat hierarchies are easier to reason about under streaming. ## SLIM avatars Platform avatars outside of the currently streamed area are not visible by default, but enabling `Class.Workspace.EnableSLIMAvatars` renders [standard rig](/docs/en-us/avatar/character-bodies/specifications.md#standard-rigs) avatars as lightweight, animated stand‑ins when appropriate. Effectively, the engine: - Renders a SLIM version when an actual avatar model streams out. - Swaps SLIM/actual by available resources inside the streaming radius, as SLIM models can be significantly less costly to render than the full resolution model. - Throttles SLIM animations by scene importance and bandwidth. The avatar level-of-detail system has specific optimizations for avatars. The following describes what will or won't be processed by SLIM: > **Success:** **SLIM Supported**- Roblox-added standard-rig avatars on player join, including the character's body, head, layered clothing, and accessories. - Avatar changes made between the `Class.Player.CharacterAdded()|CharacterAdded()` event and `Class.Player.CharacterAppearanceLoaded()|CharacterAppearanceLoaded()` event. > **Error:** **Excluded**- R6 and NPC avatars (even standard-rig). - Custom proportions/clothing/body parts (falls back to "Player Choice"). - Avatar changes made after the `Class.Player.CharacterAppearanceLoaded|CharacterAppearanceLoaded()` event (equipping tools, accessory/clothing changes, etc.). ## Script patterns The following script patterns are most commonly affected by streaming. The correct strategy depends on the intent of the code, so each pattern lists multiple options where appropriate. ### Direct index to descendants Indexing into `Class.Workspace` descendants with the `.` operator throws an error if any instance in the path isn't currently streamed in. The same applies to `Class.Instance:FindFirstChild()|FindFirstChild()`, `Class.Instance:FindFirstChildWhichIsA()|FindFirstChildWhichIsA()`, and `Class.Instance:FindFirstChildOfClass()|FindFirstChildOfClass()` which return `nil` if the child has not streamed in. ```lua local house1 = workspace:FindFirstChild("House1") -- nil if "House1" has not streamed in local door = workspace.House1.Door -- Broken if "House1" or "Door" has not streamed in ``` A similar pattern is accessing the `Class.Humanoid` or other character descendants directly inside a `Class.Player.CharacterAdded` connection. Under streaming, the character model is parented to `Class.Workspace` before all of its descendants have replicated, so direct indexing fails. ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer player.CharacterAdded:Connect(function(character) local humanoid = character.Humanoid end) ``` #### Strategy #1 If the script cannot make progress without an instance, wait for it with `Class.Instance:WaitForChild()|WaitForChild()`: ```lua local house1 = workspace:WaitForChild("House1") local door = house1:WaitForChild("Door") ``` #### Strategy #2 When an instance may not be streamed in but the script can proceed without it, `nil`‑check with `Class.Instance:FindFirstChild()`. ```lua local house1 = workspace:FindFirstChild("House1") local door = house1 and house1:FindFirstChild("Door") if door then door.Color = Color3.new(1, 0, 0) end ``` #### Strategy #3 Make the parent a `Class.Model`, set its `Class.Model.ModelStreamingMode|ModelStreamingMode` to `Enum.ModelStreamingMode.Atomic|Atomic`, and place objects like `Door` as descendants. The engine guarantees all of the **initial** descendants are present once the atomic model itself is available, but you'll still need to use `Class.Instance:WaitForChild()|WaitForChild()` on the model itself, as well as instances added to the model **after** it's been replicated. ```lua local house1 = workspace:WaitForChild("House1") -- House model is atomic; its initial descendants are guaranteed to be present local door = house1.Door ``` ### Instances sent remotely A `Class.RemoteEvent`/`Class.RemoteFunction` signal and the instance it refers to travel independently, so the signal can arrive on the client before the instance is present — or the instance may never be present at all. Two likely causes include: - Under streaming, there may be a slight delay between when a part/model is created on the server and when it gets replicated to clients. Effectively, a part referenced by a `Class.RemoteEvent`/`Class.RemoteFunction` may simply not exist yet, even inside a streamed area. - Sending a part/model reference from server to client through a `Class.RemoteEvent` or `Class.RemoteFunction` requires that the instance is replicated to the receiving client. Sending an instance path as a string has the same issue, as the path may resolve to a non‑existent location on the client:```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent") remoteEvent.OnClientEvent:Connect(function(data) local checkpoint = data.checkpoint -- Errors if "checkpoint" isn't streamed in local level = workspace.Levels[data.levelPath] -- Errors if path isn't streamed in end) ``` #### Strategy #1 If the receiving client script needs the instance to proceed, include `Class.Instance:WaitForChild()|WaitForChild()` before using it. Note that this can yield indefinitely if the instance never streams in, so consider adding a timeout as the second parameter of `Class.Instance:WaitForChild()|WaitForChild()`. #### Strategy #2 Pre‑fetch the area with `Class.Player:RequestStreamAroundAsync()` before firing the event. This makes it likely (though not guaranteed) that the instance is present when the client receives the event. See [proactive streaming](#proactive-streaming). #### Strategy #3 If the script can tolerate the instance being absent for some clients, simply `nil`‑check with `Class.Instance:FindFirstChild()` and skip the work for those clients. ### Client desynchronization Client‑side desynchronization should be treated as an exception, not a standard design pattern. Introducing client‑only copies or reparenting instances locally can create serious issues. Audit your code for places which rely on these types of changes persisting on the client. For example, reparenting an instance locally from `Class.ReplicatedStorage` to `Class.Workspace` can make that instance eligible to be streamed out. Similarly, cloning an instance locally (`Class.Instance:Clone()`) from `Class.ReplicatedStorage` into `Class.Workspace` creates a client‑only copy that is no longer part of the server replication pipeline and will not receive property updates from the original server‑owned instance. The same concept applies when calling `Class.Instance:Destroy()` on the client for a server‑owned object. This removes the instance locally but the server still has it, so it will stream in again with its original state when eligible. ### Proactive streaming When a player's next destination can be anticipated, make server‑side calls to `Class.Player:RequestStreamAroundAsync()` to stream in transient areas for temporary loading, or use `Class.Player:AddReplicationFocus()` on a **limited** basis for areas that should remain loaded until explicitly released. For example, when a player character is about to teleport by `Datatype.CFrame` change to another player's house in a distant location, you can pre‑fetch the destination area to minimize pop‑in and provide a smoother transition. The following script shows how a client-to-server [remote event](/docs/en-us/scripting/events/remote.md) can be fired to move a player character using a pre-fetching method. If the pre-fetch request is successful when the function returns, the minimum radius around the target location should be present on the client. ```lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local teleportEvent = ReplicatedStorage:WaitForChild("TeleportEvent") local function teleportPlayer(player, teleportTarget) -- Request streaming around target location player:RequestStreamAroundAsync(teleportTarget) -- Teleport character local character = player.Character if character and character.Parent then local currentPivot = character:GetPivot() character:PivotTo(currentPivot * CFrame.new(teleportTarget)) end end -- Call teleport function when the client fires the remote event teleportEvent.OnServerEvent:Connect(teleportPlayer) ``` ### Instance property reads Once an instance streams out, its property updates are no longer replicated to that client. Reading properties such as `Class.BasePart.Position` continue to succeed but return the last replicated value which can be arbitrarily stale. ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer -- Position may be stale if "target" has streamed out local dist = (target.Position - player.Character.HumanoidRootPart.Position).Magnitude ``` #### Strategy #1 Move the logic to the server, as server‑side scripts see all instances at all times. This is generally the most reliable option for distance checks and other position‑sensitive logic. #### Strategy #2 Detect stream out using [signal change](#signal-change-handling) detection, or test `Class.Instance:FindFirstAncestorWhichIsA()|Instance:FindFirstAncestorWhichIsA("Workspace")` as `false` to skip the stale property check for instances no longer present. #### Strategy #3 If the part must always be available, group it inside a `Class.Model` and set the model's `Class.Model.ModelStreamingMode|ModelStreamingMode` to `Enum.ModelStreamingMode.Persistent|Persistent`. Use this approach sparingly, as persistent models never stream out and permanently occupy memory. ### Waiting on the critical path Some non‑streaming games load their map by cloning it from `Class.ReplicatedStorage` into `Class.Workspace`, then wait for it on the client before dismissing a loading screen and signaling readiness. Under streaming this hangs indefinitely — the client's character hasn't spawned yet, so there's no [replication focus](/docs/en-us/workspace/streaming.md#replication-focus), and the spatial map instance never streams in. #### Strategy #1 Move the loading screen logic so that it doesn't depend on a specific spatial instance being present, for example by signaling readiness once the character has spawned and the immediate surrounding area is streamed in. #### Strategy #2 Call `Class.Player:AddReplicationFocus()` at the desired spawn location to give the client a [replication focus](/docs/en-us/workspace/streaming.md#replication-focus) before the character spawns. ### Signal change handling Signals such as `Class.Instance.ChildAdded`/`Class.Instance.ChildRemoved` and `Class.CollectionService` signals like `Class.CollectionService:GetInstanceAddedSignal()|GetInstanceAddedSignal()` or `Class.CollectionService:GetInstanceRemovedSignal()|GetInstanceRemovedSignal()` also fire on stream in/out, indistinguishable from real spawns/removals. Receiving scripts can't tell the difference from the signal alone, so logic which assumes a signal corresponds to a "real" event needs to be updated. #### Strategy #1 Audit scripts for any signal listeners that could break or change significantly when triggered by stream in and/or stream out. For example, if you play audio or visual effects when an enemy NPC initially spawns into the world, assign each enemy an [attribute](/docs/en-us/studio/properties.md#instance-attributes) such as `Spawned` on the first spawn, and skip replaying the same audio/effects in future stream‑ins of the enemy. ```lua local CollectionService = game:GetService("CollectionService") local TAG_NAME = "Enemy" CollectionService:GetInstanceAddedSignal(TAG_NAME):Connect(function(enemy) if not enemy:GetAttribute("Spawned") then -- Set "Spawned" attribute on enemy for initial spawn enemy:SetAttribute("Spawned", true) -- Play audio/visual effects for this initial spawn playSpawnEffects(enemy) end end) ``` #### Strategy #2 Assign a logical `Class.CollectionService` tag to all of the affected objects using `Class.CollectionService:AddTag()|AddTag()` or the [Tags](/docs/en-us/studio/properties.md#instance-tags) section of an instance's properties in Studio. Then, from a client‑side script, detect when a tagged object streams in or out through `Class.CollectionService:GetInstanceAddedSignal()|GetInstanceAddedSignal()` and `Class.CollectionService:GetInstanceRemovedSignal()|GetInstanceRemovedSignal()` and handle the object accordingly. ```lua local CollectionService = game:GetService("CollectionService") local TAG_NAME = "FlickerLightSource" local random = Random.new() local flickerSources = {} -- Detect tagged parts currently streamed in for _, light in CollectionService:GetTagged(TAG_NAME) do flickerSources[light] = true end -- Detect new tagged parts streaming in or out CollectionService:GetInstanceAddedSignal(TAG_NAME):Connect(function(light) flickerSources[light] = true end) CollectionService:GetInstanceRemovedSignal(TAG_NAME):Connect(function(light) flickerSources[light] = nil end) -- Flicker loop while true do for light in flickerSources do light.Brightness = 8 + random:NextNumber(-0.4, 0.4) end task.wait(0.05) end ``` ### Iteration over collections Client-side collection iterations like `Class.Instance:GetChildren()` and `Class.Instance:GetDescendants()` return only the streamed‑in subset of descendants. This applies even when the parent itself is always replicated, such as a `Class.Folder` directly under `Class.Workspace` whose spatial descendants stream in and out. ```lua local Players = game:GetService("Players") local player = Players.LocalPlayer -- Folder "Homes" is always replicated but its children stream in and out -- This loop may miss homes that aren't currently streamed in for _, home in workspace.Homes:GetChildren() do if home.Settings.Owner.Value == player.Name then return home end end ``` #### Strategy #1 If a complete enumeration is required, perform the scan on the server and pass the result to the player through a `Class.RemoteEvent` if needed. #### Strategy #2 If the anticipated enumeration set is small enough, group each descendant as a `Class.Model` and set `Class.Model.ModelStreamingMode|ModelStreamingMode` to `Enum.ModelStreamingMode.PersistentPerPlayer|PersistentPerPlayer`. This is appropriate for things like a roster of player‑owned plots that's small and game‑critical. #### Strategy #3 If the script's purpose is to find the nearest match or operate on whatever's currently visible, partial results may be acceptable. Comment on this assumption inside the script so that other maintainers don't reintroduce it as a bug. ### Spatial queries Client‑side spatial queries like `Class.WorldRoot:Raycast()`, `Class.WorldRoot:GetPartBoundsInBox()`, and `Class.Model:GetBoundingBox()` reflect only streamed‑in content. Whether that's a problem depends on what the query is used for. #### Strategy #1 Use the server for queries whose result must reflect the full world, for example a raycast that checks whether the player has line of sight to a distant target. #### Strategy #2 If the query is intentionally local, for example a small radius around the player, it's already operating on streamed content and no change is needed. #### Strategy #3 To spatially query a complete `Class.Model`, set its `Class.Model.ModelStreamingMode|ModelStreamingMode` to `Enum.ModelStreamingMode.Atomic|Atomic`. An atomic model is guaranteed to have all of its initial descendants present once the model itself is available, so bounds and extents will be accurate. #### Strategy #4 If a model's geometry is known at edit time, store its bounds as [attributes](/docs/en-us/studio/properties.md#instance-attributes) on the model itself and read those instead of recomputing on the client. ### Other patterns The following patterns also may also apply and should be carefully considered: - A `Class.Sound` or `Class.AudioPlayer` parented to a 3D object stops when that object streams out. For ambient audio that should persist regardless of streaming, parent the emitter to a [persistent](/docs/en-us/workspace/streaming.md#persistent) model or to a non‑streaming container. - In-game [UI](/docs/en-us/ui/in-experience-containers.md) objects like `Class.BillboardGui` or `Class.SurfaceGui` as well as [visual effects](/docs/en-us/effects.md) like `Class.Beam|Beams` or `Class.Highlight|Highlights` whose adornee or attachment streams out simply stop rendering. This may be the intended behavior, but you should verify it. - `Class.BasePart.Touched` events, `Class.ProximityPrompt|ProximityPrompts`, `Class.DragDetector|DragDetectors`, and `Class.ClickDetector|ClickDetectors` do not operate for players whose client doesn't have the associated part/model streamed in. If interaction must be possible from any range, the model needs to be [persistent](/docs/en-us/workspace/streaming.md#persistent) or the interaction needs a different mechanism. - For `Class.PathfindingService` and client‑side pathfinding, the pathfinder only sees streamed‑in geometry on the client and it may route through obstacles that exist on the server. See [here](/docs/en-us/characters/pathfinding.md#streaming-compatibility) for strategies. ## Realistic test conditions Once [scripts are updated](#script-patterns), test the game thoroughly. Streaming bugs often only manifest at the edges of the streamed area or during transitions, so only testing near spawn or at target radius isn't sufficient. - Test with `Class.Workspace.StreamingTargetRadius` set to its minimum value (`64`). Some streaming bugs only appear when the streamed area is small. - Play through the full traversal patterns of the game, teleport between distant areas, and revisit areas after leaving them. These are the situations that exercise stream in and stream out the most. - Use the [streaming debug overlay](/docs/en-us/workspace/streaming.md#runtime-debugging) to monitor the active streaming settings, currently loaded regions, and runtime streaming state. - Watch the [Output](/docs/en-us/studio/output.md) window and [Developer Console](/docs/en-us/studio/developer-console.md) for errors, as many of the [script patterns](#script-patterns) produce errors rather than silent misbehavior. Pay particular attention to errors of the form `attempt to index nil with ...` which often indicate a missing `Class.Instance:WaitForChild()|WaitForChild()` call. - Equip and activate `Class.Tool|Tools`, fire weapons, and trigger different game interactions. --- title: "Avatar Lab Preview disclaimer" url: /docs/en-us/avatar/avatar-lab-disclaimer last_updated: 2026-06-29T19:34:16Z description: "Policy and other notices regarding the Avatar Lab Preview experience." --- # Avatar Lab Preview disclaimer Avatar Lab Preview is made available as an early experiment demo. You will have the opportunity, at your discretion, to provide voluntary feedback, comments and suggestions, and if you do, we will be free to use such feedback, comments, or suggestions as we see fit and without any obligation to you. We're still working some things out, so manual tweaks, creating accessories, and saving your avatar are not available just yet, and there may be incomplete features, errors, or inaccuracies. The features within Avatar Lab Preview may generate incorrect, misleading, offensive, unreliable, or other content and information that is not useful for your purposes. As a reminder, your use of Avatar Lab Preview is governed by the [Roblox Terms of Use](https://en.help.roblox.com/hc/en-us/articles/115004647846-Roblox-Terms-of-Use), including the [Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards). You should not input any personally identifiable information or any data or content that you do not have all necessary rights to use in Avatar Lab Preview. --- title: "Asset delivery" url: /docs/en-us/cloud/api/asset-delivery last_updated: 2026-06-29T19:34:17Z description: "Asset delivery API on Roblox Cloud." --- # Asset delivery ## Asset delivery v1 --- title: "Asset permissions" url: /docs/en-us/cloud/api/asset-permissions last_updated: 2026-06-29T19:34:17Z description: "Asset permissions API on Roblox Cloud." --- # Asset permissions ## Asset permissions v1 --- title: "Badges" url: /docs/en-us/cloud/api/badges last_updated: 2026-06-29T19:34:17Z description: "Badges API on Roblox Cloud." --- # Badges ## Badges v1 Robux might be required to create a badge. To identify the number of remaining free badges you can create for the current UTC day in the specified universe, use the `/v1/universes/{universeId}/free-badges-quota` endpoint found [here](/docs/en-us/cloud/legacy/badges/v1.md). --- title: "Develop" url: /docs/en-us/cloud/api/develop last_updated: 2026-06-29T19:34:17Z description: "Develop API on Roblox Cloud." --- # Develop ## Develop v2 ## Develop v1 --- title: "Developer products" url: /docs/en-us/cloud/api/developer-products last_updated: 2026-06-29T19:34:17Z description: "Developer products API on Roblox Cloud." --- # Developer products ## Developer products --- title: "Followings" url: /docs/en-us/cloud/api/followings last_updated: 2026-06-29T19:34:17Z description: "Followings API on Roblox Cloud." --- # Followings ## Followings v2 ## Followings v1 --- title: "Game internationalization" url: /docs/en-us/cloud/api/game-internationalization last_updated: 2026-06-29T19:34:17Z description: "Game internationalization API on Roblox Cloud." --- # Game internationalization ## Game internationalization v1 --- title: "Game passes" url: /docs/en-us/cloud/api/game-passes last_updated: 2026-06-29T19:34:17Z description: "Game passes API on Roblox Cloud." --- # Game passes ## Game passes --- title: "Followings" url: /docs/en-us/cloud/api/groups last_updated: 2026-06-29T19:34:17Z description: "Followings API on Roblox Cloud." --- # Followings ## Groups v1 --- title: "Localization tables" url: /docs/en-us/cloud/api/localization last_updated: 2026-06-29T19:34:17Z description: "Game internationalization API on Roblox Cloud." --- # Localization tables ## Localization tables v1 --- title: "Open eval" url: /docs/en-us/cloud/api/open-eval last_updated: 2026-06-29T19:34:17Z description: "Open eval API on Roblox Cloud." --- # Open eval ## Open eval --- title: "Publish" url: /docs/en-us/cloud/api/publish last_updated: 2026-06-29T19:34:17Z description: "Publish API on Roblox Cloud." --- # Publish ## Publish v1 --- title: "Secrets store service" url: /docs/en-us/cloud/api/secrets-store last_updated: 2026-06-29T19:34:17Z description: "Use Open Cloud to manage secrets for your universe." --- # Secrets store service ## Secrets store service --- title: "Toolbox service" url: /docs/en-us/cloud/api/toolbox-service last_updated: 2026-06-29T19:34:17Z description: "Search Creator Store assets on Roblox Cloud." --- # Toolbox service ## Toolbox service --- name: "Account Information Api v1" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://accountinformation.roblox.com" version: "v1" endpoints: 16 auth: [cookie] --- # Account Information Api v1 **API Version:** v1 **Base URL:** `https://accountinformation.roblox.com` ## Endpoints ### GET `/v1/birthdate` *(deprecated)* Get the user's birthdate **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.AccountInformation.Api.Models.BirthdateResponse` - `400`: 1: User not found. - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.AccountInformation.Api.Models.BirthdateResponse`) See [Roblox.AccountInformation.Api.Models.BirthdateResponse](#roblox-accountinformation-api-models-birthdateresponse) in Models. **Response example:** ```json { "birthMonth": 0, "birthDay": 0, "birthYear": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountinformation.roblox.com/v1/birthdate" ``` ### GET `/v1/description` *(deprecated)* Get the user's description **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.AccountInformation.Api.Models.DescriptionResponse` - `400`: 1: User not found. - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.AccountInformation.Api.Models.DescriptionResponse`) See [Roblox.AccountInformation.Api.Models.DescriptionResponse](#roblox-accountinformation-api-models-descriptionresponse) in Models. **Response example:** ```json { "description": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountinformation.roblox.com/v1/description" ``` ### POST `/v1/description` *(deprecated)* Update the user's description **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.AccountInformation.Api.Models.DescriptionRequest` See [Roblox.AccountInformation.Api.Models.DescriptionRequest](#roblox-accountinformation-api-models-descriptionrequest) in Models. **Request example:** ```json { "description": "string" } ``` **Responses:** - `200`: OK → `Roblox.AccountInformation.Api.Models.DescriptionResponse` - `400`: 1: User not found. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `500`: 0: An unknown error occured. - `503`: 3: This feature is currently disabled. Please try again later. **Response fields** (`Roblox.AccountInformation.Api.Models.DescriptionResponse`) See [Roblox.AccountInformation.Api.Models.DescriptionResponse](#roblox-accountinformation-api-models-descriptionresponse) in Models. **Response example:** ```json { "description": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountinformation.roblox.com/v1/description" \ -H "Content-Type: application/json" \ -d '{ "description": "string" }' ``` ### GET `/v1/gender` *(deprecated)* Get the user's gender **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.AccountInformation.Api.Models.GenderResponse` - `400`: 1: User not found. - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.AccountInformation.Api.Models.GenderResponse`) See [Roblox.AccountInformation.Api.Models.GenderResponse](#roblox-accountinformation-api-models-genderresponse) in Models. **Response example:** ```json { "gender": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountinformation.roblox.com/v1/gender" ``` ### POST `/v1/gender` *(deprecated)* Update the user's gender **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.AccountInformation.Api.Models.GenderRequest` See [Roblox.AccountInformation.Api.Models.GenderRequest](#roblox-accountinformation-api-models-genderrequest) in Models. **Request example:** ```json { "gender": "string" } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: User not found. 6: The gender provided is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `500`: 0: An unknown error occured. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountinformation.roblox.com/v1/gender" \ -H "Content-Type: application/json" \ -d '{ "gender": "string" }' ``` ### GET `/v1/metadata` Get the metadata **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.AccountInformation.Api.Models.MetadataResponse` **Response fields** (`Roblox.AccountInformation.Api.Models.MetadataResponse`) See [Roblox.AccountInformation.Api.Models.MetadataResponse](#roblox-accountinformation-api-models-metadataresponse) in Models. **Response example:** ```json { "isAllowedNotificationsEndpointDisabled": false, "isAccountSettingsPolicyEnabled": false, "isPhoneNumberEnabled": false, "MaxUserDescriptionLength": 0, "isUserDescriptionEnabled": false, "isUserBlockEndpointsUpdated": false } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountinformation.roblox.com/v1/metadata" ``` ### GET `/v1/phone` Get Verified Phone Number **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.AccountInformation.Api.Models.PhoneResponse` - `401`: 0: Authorization has been denied for this request. - `500`: 0: An unknown error occured. **Response fields** (`Roblox.AccountInformation.Api.Models.PhoneResponse`) See [Roblox.AccountInformation.Api.Models.PhoneResponse](#roblox-accountinformation-api-models-phoneresponse) in Models. **Response example:** ```json { "countryCode": "string", "prefix": "string", "phone": "string", "isVerified": false, "verificationCodeLength": 0, "canBypassPasswordForPhoneUpdate": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountinformation.roblox.com/v1/phone" ``` ### POST `/v1/phone` Set Phone Number **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.AccountInformation.Api.Models.PhoneRequest` See [Roblox.AccountInformation.Api.Models.PhoneRequest](#roblox-accountinformation-api-models-phonerequest) in Models. **Request example:** ```json { "countryCode": "string", "prefix": "string", "phone": "string", "password": "string", "verificationChannel": "string" } ``` **Responses:** - `200`: OK → `Roblox.Platform.UserPhoneNumberVerification.Models.PendingVerificationResponse` - `400`: 2: Invalid Phone Number 3: Phone Number Already Associated 8: Invalid Phone Number Type - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 5: Incorrect Password 10: - `429`: 6: Flooded - `500`: 0: An unknown error occured. - `503`: 1: This feature is currently disabled. Please try again later. **Response fields** (`Roblox.Platform.UserPhoneNumberVerification.Models.PendingVerificationResponse`) See [Roblox.Platform.UserPhoneNumberVerification.Models.PendingVerificationResponse](#roblox-platform-userphonenumberverification-models-pendingverificationresponse) in Models. **Response example:** ```json { "verificationChannel": "string", "data": "string" } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountinformation.roblox.com/v1/phone" \ -H "Content-Type: application/json" \ -d '{ "countryCode": "string", "prefix": "string", "phone": "string", "password": "string", "verificationChannel": "string" }' ``` ### GET `/v1/promotion-channels` Get the user's promotion channels **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `alwaysReturnUrls` | query | `boolean` | No | Whether all promotion channel links should be returned as full URLs. | | `filterLink` | query | `boolean` | No | Whether all promotion channel links should be filtered. | | `onlyShortenTwitter` | query | `boolean` | No | Whether all promotion channels links except for Twitter should be returned as full URLs. If false, all promotion channels will be shortened. | **Responses:** - `200`: OK → `Roblox.AccountInformation.Api.Models.PromotionChannelsResponse` - `400`: 1: User not found. - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.AccountInformation.Api.Models.PromotionChannelsResponse`) See [Roblox.AccountInformation.Api.Models.PromotionChannelsResponse](#roblox-accountinformation-api-models-promotionchannelsresponse) in Models. **Response example:** ```json { "promotionChannelsVisibilityPrivacy": "string", "facebook": "string", "twitter": "string", "youtube": "string", "twitch": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountinformation.roblox.com/v1/promotion-channels" ``` ### POST `/v1/promotion-channels` Update the user's promotion channels **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.AccountInformation.Api.Models.PromotionChannelsRequest` See [Roblox.AccountInformation.Api.Models.PromotionChannelsRequest](#roblox-accountinformation-api-models-promotionchannelsrequest) in Models. **Request example:** ```json { "facebook": "string", "twitter": "string", "youtube": "string", "twitch": "string", "promotionChannelsVisibilityPrivacy": "string" } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 2: The request was empty. 11: The Facebook profile url is invalid. 12: The Twitter handle is invalid. 13: The YouTube url is invalid. 14: The Twitch profile url is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 4: Only users who are over twelve years of age may edit social network channels. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountinformation.roblox.com/v1/promotion-channels" \ -H "Content-Type: application/json" \ -d '{ "facebook": "string", "twitter": "string", "youtube": "string", "twitch": "string", "promotionChannelsVisibilityPrivacy": "string" }' ``` ### GET `/v1/users/{userId}/promotion-channels` Get promotion channels for a given user ID **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The ID of the user to fetch the promotion channels for. | | `alwaysReturnUrls` | query | `boolean` | No | Whether all promotion channel links should be returned as full URLs. | | `filterLink` | query | `boolean` | No | Whether all promotion channel links should be filtered. | **Responses:** - `200`: OK → `Roblox.AccountInformation.Api.Models.PromotionChannelsByUserIdResponse` - `400`: 1: User not found. **Response fields** (`Roblox.AccountInformation.Api.Models.PromotionChannelsByUserIdResponse`) See [Roblox.AccountInformation.Api.Models.PromotionChannelsByUserIdResponse](#roblox-accountinformation-api-models-promotionchannelsbyuseridresponse) in Models. **Response example:** ```json { "facebook": "string", "twitter": "string", "youtube": "string", "twitch": "string" } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountinformation.roblox.com/v1/users/{USERID}/promotion-channels" ``` ### GET `/v1/users/{userId}/roblox-badges` Returns a list of Roblox badges belonging to a user. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | | **Responses:** - `200`: OK → `Roblox.AccountInformation.Api.RobloxBadgeResponse[]` **Response fields** (`Roblox.AccountInformation.Api.RobloxBadgeResponse[]`) See [Roblox.AccountInformation.Api.RobloxBadgeResponse](#roblox-accountinformation-api-robloxbadgeresponse) in Models. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountinformation.roblox.com/v1/users/{USERID}/roblox-badges" ``` ### POST `/v1/email/verify` Verify the user's email address from token **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.AccountInformation.Api.Models.VerifyEmailRequest` See [Roblox.AccountInformation.Api.Models.VerifyEmailRequest](#roblox-accountinformation-api-models-verifyemailrequest) in Models. **Request example:** ```json { "ticket": "string" } ``` **Responses:** - `200`: OK → `Roblox.AccountInformation.Api.Models.VerifyEmailResponse` - `403`: 0: Token Validation Failed **Response fields** (`Roblox.AccountInformation.Api.Models.VerifyEmailResponse`) See [Roblox.AccountInformation.Api.Models.VerifyEmailResponse](#roblox-accountinformation-api-models-verifyemailresponse) in Models. **Response example:** ```json { "verifiedUserHatAssetId": 0 } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountinformation.roblox.com/v1/email/verify" \ -H "Content-Type: application/json" \ -d '{ "ticket": "string" }' ``` ### POST `/v1/phone/delete` Delete Phone **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.AccountInformation.Api.Models.PhoneRequest` See [Roblox.AccountInformation.Api.Models.PhoneRequest](#roblox-accountinformation-api-models-phonerequest) in Models. **Request example:** ```json { "countryCode": "string", "prefix": "string", "phone": "string", "password": "string", "verificationChannel": "string" } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 5: Incorrect Password - `429`: 6: Flooded - `500`: 0: An unknown error occured. - `503`: 1: This feature is currently disabled. Please try again later. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountinformation.roblox.com/v1/phone/delete" \ -H "Content-Type: application/json" \ -d '{ "countryCode": "string", "prefix": "string", "phone": "string", "password": "string", "verificationChannel": "string" }' ``` ### POST `/v1/phone/resend` Resend Phone code **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.AccountInformation.Api.Models.EmptyRequest` See [Roblox.AccountInformation.Api.Models.EmptyRequest](#roblox-accountinformation-api-models-emptyrequest) in Models. **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `429`: 6: Flooded - `500`: 0: An unknown error occured. - `503`: 1: This feature is currently disabled. Please try again later. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountinformation.roblox.com/v1/phone/resend" \ -H "Content-Type: application/json" \ -d '"..."' ``` ### POST `/v1/phone/verify` Verify Phone **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.AccountInformation.Api.Models.VerifyPhoneRequest` See [Roblox.AccountInformation.Api.Models.VerifyPhoneRequest](#roblox-accountinformation-api-models-verifyphonerequest) in Models. **Request example:** ```json { "code": "string" } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 2: Invalid Phone Number 3: Phone Number Already Associated 7: Invalid Code - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `429`: 6: Flooded - `500`: 0: An unknown error occured. - `503`: 1: This feature is currently disabled. Please try again later. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountinformation.roblox.com/v1/phone/verify" \ -H "Content-Type: application/json" \ -d '{ "code": "string" }' ``` ## Models ### Roblox.AccountInformation.Api.Models.BirthdateResponse The birthdate response | Property | Type | Required | Description | |----------|------|----------|-------------| | `birthMonth` | `integer` | No | The birth month | | `birthDay` | `integer` | No | The birth day | | `birthYear` | `integer` | No | The birth year | ### Roblox.AccountInformation.Api.Models.DescriptionRequest The description request | Property | Type | Required | Description | |----------|------|----------|-------------| | `description` | `string` | No | The description | ### Roblox.AccountInformation.Api.Models.DescriptionResponse The description response | Property | Type | Required | Description | |----------|------|----------|-------------| | `description` | `string` | No | The description | ### Roblox.AccountInformation.Api.Models.GenderRequest The gender request | Property | Type | Required | Description | |----------|------|----------|-------------| | `gender` | `string` | No | The gender | ### Roblox.AccountInformation.Api.Models.GenderResponse The gender response | Property | Type | Required | Description | |----------|------|----------|-------------| | `gender` | `integer` | No | The gender | ### Roblox.AccountInformation.Api.Models.MetadataResponse The metadata response | Property | Type | Required | Description | |----------|------|----------|-------------| | `isAllowedNotificationsEndpointDisabled` | `boolean` | No | Switch for account/settings/allowed-notification-destinations endpoint | | `isAccountSettingsPolicyEnabled` | `boolean` | No | The account settings policy enabled setting | | `isPhoneNumberEnabled` | `boolean` | No | Switch for phone endpoints | | `MaxUserDescriptionLength` | `integer` | No | The limit on the length used for description | | `isUserDescriptionEnabled` | `boolean` | No | Switch for determining if user description is enabled | | `isUserBlockEndpointsUpdated` | `boolean` | No | Switch for determining block endpoints to use for the user | | `shouldUsePersonaForIdVerification` | `boolean` | No | Whether to use Persona for ID verification. | | `shouldDisplaySessionManagement` | `boolean` | No | Whether to display Session Management. | | `isPasswordRequiredForAgingDown` | `boolean` | No | Switch for requiring password to change age below 13. | ### Roblox.AccountInformation.Api.Models.PhoneRequest The phone request | Property | Type | Required | Description | |----------|------|----------|-------------| | `countryCode` | `string` | No | The country Code | | `prefix` | `string` | No | The Phone Prefix | | `phone` | `string` | No | The Phone number | | `password` | `string` | No | Password | | `verificationChannel` | `string` | No | Verification Channel | ### Roblox.AccountInformation.Api.Models.PhoneResponse The phone response | Property | Type | Required | Description | |----------|------|----------|-------------| | `countryCode` | `string` | No | The country Code | | `prefix` | `string` | No | The Phone Prefix | | `phone` | `string` | No | The Phone number | | `isVerified` | `boolean` | No | Is the phone verified | | `verificationCodeLength` | `integer` | No | Verification Code Length | | `canBypassPasswordForPhoneUpdate` | `boolean` | No | Whether user needs to provide password to update their phone numbers | ### Roblox.AccountInformation.Api.Models.PromotionChannelsByUserIdResponse The promotion channels response | Property | Type | Required | Description | |----------|------|----------|-------------| | `facebook` | `string` | No | The Facebook channel | | `twitter` | `string` | No | The Twitter channel | | `youtube` | `string` | No | The YouTube channel | | `twitch` | `string` | No | The Twitch channel | ### Roblox.AccountInformation.Api.Models.PromotionChannelsRequest The promotion channels request | Property | Type | Required | Description | |----------|------|----------|-------------| | `facebook` | `string` | No | The Facebook channel | | `twitter` | `string` | No | The Twitter channel | | `youtube` | `string` | No | The YouTube channel | | `twitch` | `string` | No | The Twitch channel | | `promotionChannelsVisibilityPrivacy` | `string` | No | The promotion channels visibility privacy level | ### Roblox.AccountInformation.Api.Models.PromotionChannelsResponse The promotion channels response | Property | Type | Required | Description | |----------|------|----------|-------------| | `promotionChannelsVisibilityPrivacy` | `string` | No | The promotion channels visibility privacy level | | `facebook` | `string` | No | The Facebook channel | | `twitter` | `string` | No | The Twitter channel | | `youtube` | `string` | No | The YouTube channel | | `twitch` | `string` | No | The Twitch channel | ### Roblox.AccountInformation.Api.Models.VerifyEmailRequest Verify Email Request | Property | Type | Required | Description | |----------|------|----------|-------------| | `ticket` | `string` | No | Ticket to verify email | ### Roblox.AccountInformation.Api.Models.VerifyEmailResponse The verify email response | Property | Type | Required | Description | |----------|------|----------|-------------| | `verifiedUserHatAssetId` | `integer` | No | Free item id after email verification | ### Roblox.AccountInformation.Api.Models.VerifyPhoneRequest Verify Phone Request | Property | Type | Required | Description | |----------|------|----------|-------------| | `code` | `string` | No | Code to verify phone | ### Roblox.AccountInformation.Api.RobloxBadgeResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | The ID belonging to this Roblox badge. | | `name` | `string` | No | The name of this Roblox badge. | | `description` | `string` | No | The description belonging to this Roblox badge. | | `imageUrl` | `string` | No | The URL corresponding to the image which represents this Roblox badge. | ### Roblox.Platform.UserPhoneNumberVerification.Models.PendingVerificationResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `verificationChannel` | `string` | No | | | `data` | `string` | No | | --- name: "AccountSettings Api v1" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://accountsettings.roblox.com" version: "v1" endpoints: 15 auth: [cookie] --- # AccountSettings Api v1 **API Version:** v1 **Base URL:** `https://accountsettings.roblox.com` ## Endpoints ### GET `/v1/account/settings/account-country` Get a user's current account country setting. **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.AccountSettings.Api.Models.Response.AccountCountrySettingsResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.AccountSettings.Api.Models.Response.AccountCountrySettingsResponse`) See [Roblox.AccountSettings.Api.Models.Response.AccountCountrySettingsResponse](#roblox-accountsettings-api-models-response-accountcountrysettingsresponse) in Models. **Response example:** ```json { "value": { "countryName": "string", "subdivisionIso": "string", "localizedSubdivision": "string", "localizedName": "string", "countryId": 0 } } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountsettings.roblox.com/v1/account/settings/account-country" ``` ### POST `/v1/account/settings/account-country` Updates the user's account country. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.AccountSettings.Api.UpdateAccountCountryRequest` See [Roblox.AccountSettings.Api.UpdateAccountCountryRequest](#roblox-accountsettings-api-updateaccountcountryrequest) in Models. **Request example:** ```json { "targetCountryId": 0 } ``` **Responses:** - `200`: OK → `Roblox.AccountSettings.Api.Models.Response.UpdateAccountCountryResponse` - `400`: 1: InvalidRequest - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: OperationNotPermitted - `404`: 2: OperationNotPermitted - `500`: 0: Unknown **Response fields** (`Roblox.AccountSettings.Api.Models.Response.UpdateAccountCountryResponse`) See [Roblox.AccountSettings.Api.Models.Response.UpdateAccountCountryResponse](#roblox-accountsettings-api-models-response-updateaccountcountryresponse) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountsettings.roblox.com/v1/account/settings/account-country" \ -H "Content-Type: application/json" \ -d '{ "targetCountryId": 0 }' ``` ### GET `/v1/account/settings/metadata` Returns metadata used by the account settings page **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.AccountSettings.Api.Models.AccountsSettingsMetadataModel` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.AccountSettings.Api.Models.AccountsSettingsMetadataModel`) See [Roblox.AccountSettings.Api.Models.AccountsSettingsMetadataModel](#roblox-accountsettings-api-models-accountssettingsmetadatamodel) in Models. **Response example:** ```json { "IsAccountsRestrictionsSpamBugFixEnabled": false, "MaximumParentalControlsMonthlySpendLimitInUSD": 0, "IsParentalMonthlyLimitInUIEnabled": false, "IsParentalNotificationSettingsInUIEnabled": false, "IsContentControlsEnabled": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountsettings.roblox.com/v1/account/settings/metadata" ``` ### GET `/v1/email` Gets the authenticated user's email address and verified status **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.AccountSettings.Api.EmailResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.AccountSettings.Api.EmailResponse`) See [Roblox.AccountSettings.Api.EmailResponse](#roblox-accountsettings-api-emailresponse) in Models. **Response example:** ```json { "emailAddress": "string", "verified": false, "canBypassPasswordForEmailUpdate": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountsettings.roblox.com/v1/email" ``` ### POST `/v1/email` Updates the authenticated user's email address **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.AccountSettings.Api.UpdateEmailRequest` See [Roblox.AccountSettings.Api.UpdateEmailRequest](#roblox-accountsettings-api-updateemailrequest) in Models. **Request example:** ```json { "password": "string", "emailAddress": "string", "skipVerificationEmail": false, "isAdsAccount": false } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 8: Password is incorrect. 9: Invalid email address. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: This feature is currently disabled. Please try again later. 3: There are too many accounts associated with this email address. 11: You must be on the Corporate network to log in. - `409`: 4: This is already the current email. - `429`: 6: Too many attempts to update email. Please try again later. 7: Too many attempts to send verification email. Please try again later. - `503`: 2: This feature is currently disabled. Please try again later. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountsettings.roblox.com/v1/email" \ -H "Content-Type: application/json" \ -d '{ "password": "string", "emailAddress": "string", "skipVerificationEmail": false, "isAdsAccount": false }' ``` ### PATCH `/v1/email` Updates the authenticated user's email address **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.AccountSettings.Api.UpdateEmailRequest` See [Roblox.AccountSettings.Api.UpdateEmailRequest](#roblox-accountsettings-api-updateemailrequest) in Models. **Request example:** ```json { "password": "string", "emailAddress": "string", "skipVerificationEmail": false, "isAdsAccount": false } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 8: Password is incorrect. 9: Invalid email address. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: This feature is currently disabled. Please try again later. 3: There are too many accounts associated with this email address. 11: You must be on the Corporate network to log in. - `409`: 4: This is already the current email. - `429`: 6: Too many attempts to update email. Please try again later. 7: Too many attempts to send verification email. Please try again later. - `503`: 2: This feature is currently disabled. Please try again later. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountsettings.roblox.com/v1/email" \ -H "Content-Type: application/json" \ -d '{ "password": "string", "emailAddress": "string", "skipVerificationEmail": false, "isAdsAccount": false }' ``` ### GET `/v1/emails` Gets the authenticated user's verified email and pending (unverified) email if one exists **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.AccountSettings.Api.CurrentEmailsResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.AccountSettings.Api.CurrentEmailsResponse`) See [Roblox.AccountSettings.Api.CurrentEmailsResponse](#roblox-accountsettings-api-currentemailsresponse) in Models. **Response example:** ```json { "verifiedEmail": "string", "pendingEmail": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountsettings.roblox.com/v1/emails" ``` ### GET `/v1/themes/{consumerType}/{consumerId}` returns the theme type for a specific consumer. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `consumerType` | path | `integer (int32)` | Yes | The consumer type Valid values: `1` | | `consumerId` | path | `string` | Yes | The consumer's theme configuration to get. If the consumerType is User always return the AuthenticatedUser's theme type. | **Responses:** - `200`: OK → `Roblox.AccountSettings.Api.ThemeConfigurationResponse` - `400`: 3: Invalid consumer type. - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.AccountSettings.Api.ThemeConfigurationResponse`) See [Roblox.AccountSettings.Api.ThemeConfigurationResponse](#roblox-accountsettings-api-themeconfigurationresponse) in Models. **Response example:** ```json { "themeType": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountsettings.roblox.com/v1/themes/{CONSUMERTYPE}/{CONSUMERID}" ``` ### PATCH `/v1/themes/{consumerType}/{consumerId}` Modify the theme type for consumer. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `consumerType` | path | `integer (int32)` | Yes | The consumer type Valid values: `1` | | `consumerId` | path | `integer (int64)` | Yes | The consumer's theme configuration to modify. If the consumerType is User always modify the AuthenticatedUser's theme type. | **Request Body:** `application/json` — Type: `Roblox.AccountSettings.Api.ThemeConfigurationRequest` See [Roblox.AccountSettings.Api.ThemeConfigurationRequest](#roblox-accountsettings-api-themeconfigurationrequest) in Models. **Request example:** ```json { "themeType": "string" } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 2: Invalid theme type. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountsettings.roblox.com/v1/themes/{CONSUMERTYPE}/{CONSUMERID}" \ -H "Content-Type: application/json" \ -d '{ "themeType": "string" }' ``` ### GET `/v1/themes/types` returns all the enabled theme types. **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[System.String]` **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[System.String]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[System.String]](#roblox-web-webapi-models-apiarrayresponse-system-string-) in Models. **Response example:** ```json { "data": [ "string" ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountsettings.roblox.com/v1/themes/types" ``` ### GET `/v1/trade-privacy` Get a user's trade privacy setting **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.AccountSettings.Api.TradePrivacyResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.AccountSettings.Api.TradePrivacyResponse`) See [Roblox.AccountSettings.Api.TradePrivacyResponse](#roblox-accountsettings-api-tradeprivacyresponse) in Models. **Response example:** ```json { "tradePrivacy": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountsettings.roblox.com/v1/trade-privacy" ``` ### POST `/v1/trade-privacy` Updates a user's trade privacy setting **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.AccountSettings.Api.UpdateTradePrivacyRequest` See [Roblox.AccountSettings.Api.UpdateTradePrivacyRequest](#roblox-accountsettings-api-updatetradeprivacyrequest) in Models. **Request example:** ```json { "tradePrivacy": 0 } ``` **Responses:** - `200`: OK → `Roblox.AccountSettings.Api.TradePrivacyUpdateResponse` - `400`: Roblox.AccountSettings.Api.ResponseEnums.TradeSettingsErrors.InvalidTradePrivacy - `401`: 0: Authorization has been denied for this request. - `403`: Roblox.AccountSettings.Api.ResponseEnums.TradeSettingsErrors.AccountLocked OR Roblox.AccountSettings.Api.ResponseEnums.TradeSettingsErrors.UserCannotTrade 0: Token Validation Failed **Response fields** (`Roblox.AccountSettings.Api.TradePrivacyUpdateResponse`) See [Roblox.AccountSettings.Api.TradePrivacyUpdateResponse](#roblox-accountsettings-api-tradeprivacyupdateresponse) in Models. **Response example:** ```json { "tradePrivacy": 0, "inventoryPrivacy": 1, "privacySettingResponse": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountsettings.roblox.com/v1/trade-privacy" \ -H "Content-Type: application/json" \ -d '{ "tradePrivacy": 0 }' ``` ### GET `/v1/trade-value` Get a user's trade quality filter setting **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.AccountSettings.Api.TradeValueResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.AccountSettings.Api.TradeValueResponse`) See [Roblox.AccountSettings.Api.TradeValueResponse](#roblox-accountsettings-api-tradevalueresponse) in Models. **Response example:** ```json { "tradeValue": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountsettings.roblox.com/v1/trade-value" ``` ### POST `/v1/trade-value` Updates a user's trade quality filter setting **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.AccountSettings.Api.TradeValueRequest` See [Roblox.AccountSettings.Api.TradeValueRequest](#roblox-accountsettings-api-tradevaluerequest) in Models. **Request example:** ```json { "tradeValue": 0 } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: Roblox.AccountSettings.Api.ResponseEnums.TradeSettingsErrors.InvalidTradeValue - `401`: 0: Authorization has been denied for this request. - `403`: Roblox.AccountSettings.Api.ResponseEnums.TradeSettingsErrors.AccountLocked OR Roblox.AccountSettings.Api.ResponseEnums.TradeSettingsErrors.UserCannotTrade 0: Token Validation Failed **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountsettings.roblox.com/v1/trade-value" \ -H "Content-Type: application/json" \ -d '{ "tradeValue": 0 }' ``` ### POST `/v1/email/verify` Send verify email to the authenticated user's email address **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.AccountSettings.Api.SendVerifyEmailRequest` See [Roblox.AccountSettings.Api.SendVerifyEmailRequest](#roblox-accountsettings-api-sendverifyemailrequest) in Models. **Request example:** ```json { "freeItem": false, "isAdsAccount": false } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 10: No email address is associated with the account. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: This feature is currently disabled. Please try again later. 11: You must be on the Corporate network to log in. - `409`: 5: The email is already verified. - `429`: 7: Too many attempts to send verification email. Please try again later. - `503`: 2: This feature is currently disabled. Please try again later. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://accountsettings.roblox.com/v1/email/verify" \ -H "Content-Type: application/json" \ -d '{ "freeItem": false, "isAdsAccount": false }' ``` ## Models ### Roblox.AccountSettings.Api.CurrentEmailsResponse Response model for getting the user's verified and pending email addresses | Property | Type | Required | Description | |----------|------|----------|-------------| | `verifiedEmail` | `string` | No | The user's most recent verified email address (masked) | | `pendingEmail` | `string` | No | The user's pending (unverified) email address if one exists (masked) | ### Roblox.AccountSettings.Api.EmailResponse Response model for getting the user's email address and verified status | Property | Type | Required | Description | |----------|------|----------|-------------| | `emailAddress` | `string` | No | The masked and formatted email address | | `verified` | `boolean` | No | The verified status of the email | | `canBypassPasswordForEmailUpdate` | `boolean` | No | Whether password is required for updating email | ### Roblox.AccountSettings.Api.Models.AccountsSettingsMetadataModel A model containing website metadata for avatars | Property | Type | Required | Description | |----------|------|----------|-------------| | `IsAccountsRestrictionsSpamBugFixEnabled` | `boolean` | No | Whether or not account restrictions spam bug fix is enabled | | `MaximumParentalControlsMonthlySpendLimitInUSD` | `integer` | No | The max amount a user can enter as their monthly spending limit in USD | | `IsParentalMonthlyLimitInUIEnabled` | `boolean` | No | Enables/disables the section in the account parental controls page where you can set the monthly spend limit | | `IsParentalNotificationSettingsInUIEnabled` | `boolean` | No | Enables/disables the section in the account parental controls page where you can set the parental notifications settings | | `IsContentControlsEnabled` | `boolean` | No | Enables/disables the section in the account parental controls page where you can set the content control settings | ### Roblox.AccountSettings.Api.Models.Response.AccountCountrySettingsResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `value` | `Roblox.AccountSettings.Api.Models.Response.UserAccountCountry` | No | | ### Roblox.AccountSettings.Api.Models.Response.UserAccountCountry | Property | Type | Required | Description | |----------|------|----------|-------------| | `countryName` | `string` | No | | | `subdivisionIso` | `string` | No | | | `localizedSubdivision` | `string` | No | | | `localizedName` | `string` | No | | | `countryId` | `integer` | No | | ### Roblox.AccountSettings.Api.SendVerifyEmailRequest Request model for sending a verify email request | Property | Type | Required | Description | |----------|------|----------|-------------| | `freeItem` | `boolean` | No | Whether the user will see messaging that they received a free item after verifying their email | | `isAdsAccount` | `boolean` | No | Whether the request is coming from ads site | ### Roblox.AccountSettings.Api.ThemeConfigurationRequest Response model for get user's theme type | Property | Type | Required | Description | |----------|------|----------|-------------| | `themeType` | `string` | No | theme Type | ### Roblox.AccountSettings.Api.ThemeConfigurationResponse Response model for get user's theme type | Property | Type | Required | Description | |----------|------|----------|-------------| | `themeType` | `string` | No | theme Type | ### Roblox.AccountSettings.Api.TradePrivacyResponse Response model for getting the user's trade privacy settings | Property | Type | Required | Description | |----------|------|----------|-------------| | `tradePrivacy` | `string` | No | The current trade privacy setting for the current user | ### Roblox.AccountSettings.Api.TradePrivacyUpdateResponse Response model for updating the user's trade privacy settings | Property | Type | Required | Description | |----------|------|----------|-------------| | `tradePrivacy` | `integer enum (8 values)` | No | The trade privacy setting after updating ['Undefined' = 0, 'Disabled' = 1, 'NoOne' = 2, 'Friends' = 3, 'TopFriends' = 4, 'Following' = 5, 'Followers' = 6, 'All' = 7] Values: 0, 1, 2, 3, 4, 5, 6, 7 | | `inventoryPrivacy` | `integer enum (6 values)` | No | The inventory privacy setting after updating ['NoOne' = 1, 'Friends' = 2, 'FriendsAndFollowing' = 3, 'FriendsFollowingAndFollowers' = 4, 'AllAuthenticatedUsers' = 5, 'AllUsers' = 6] Values: 1, 2, 3, 4, 5, 6 | | `privacySettingResponse` | `0 \| 1` | No | The response state after updating trade privacy ['Success' = 0, 'Conflict' = 1] | ### Roblox.AccountSettings.Api.TradeValueRequest Request model for trade value setting update | Property | Type | Required | Description | |----------|------|----------|-------------| | `tradeValue` | `0 \| 1 \| 2 \| 3 \| 4` | No | The desired trade value setting for the active user ['Undefined' = 0, 'None' = 1, 'Low' = 2, 'Medium' = 3, 'High' = 4] | ### Roblox.AccountSettings.Api.TradeValueResponse Response model for getting the user's trade value settings | Property | Type | Required | Description | |----------|------|----------|-------------| | `tradeValue` | `string` | No | The current trade value setting for the current user | ### Roblox.AccountSettings.Api.UpdateAccountCountryRequest Request Model for updating a user's account country | Property | Type | Required | Description | |----------|------|----------|-------------| | `targetCountryId` | `integer` | No | The targetCountryId | ### Roblox.AccountSettings.Api.UpdateEmailRequest Request model for updating an email | Property | Type | Required | Description | |----------|------|----------|-------------| | `password` | `string` | No | The user's password. | | `emailAddress` | `string` | No | The new email address to set. | | `skipVerificationEmail` | `boolean` | No | Should the email controller skip sending the verification email. | | `isAdsAccount` | `boolean` | No | Whether the request is coming from ads site. | ### Roblox.AccountSettings.Api.UpdateTradePrivacyRequest Request model for trade privacy setting update | Property | Type | Required | Description | |----------|------|----------|-------------| | `tradePrivacy` | `integer enum (8 values)` | No | The desired trade privacy setting for the active user ['Undefined' = 0, 'Disabled' = 1, 'NoOne' = 2, 'Friends' = 3, 'TopFriends' = 4, 'Following' = 5, 'Followers' = 6, 'All' = 7] Values: 0, 1, 2, 3, 4, 5, 6, 7 | ### Roblox.Web.WebAPI.Models.ApiArrayResponse[System.String] | Property | Type | Required | Description | |----------|------|----------|-------------| | `data` | `string[]` | No | | --- name: "AdConfiguration Api v2" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://adconfiguration.roblox.com" version: "v2" endpoints: 10 auth: [cookie] --- # AdConfiguration Api v2 **API Version:** v2 **Base URL:** `https://adconfiguration.roblox.com` ## Endpoints ### GET `/v2/sponsored-campaigns` Gets a page of Roblox.AdConfiguration.Api.SponsoredCampaignModel with specified input parameters. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `campaignTargetType` | query | `integer (int32)` | Yes | The campaign target type enum value Valid values: `0`, `1`, `2`, `3` | | `campaignTargetId` | query | `integer (int64)` | Yes | The id of the campaign target | | `includeReportingStats` | query | `boolean` | No | Indicates whether to include reporting stats in the response | | `isArchived` | query | `boolean` | No | Indicates whether to retrieve archived ads | | `pageCursor` | query | `string` | No | The cursor of the page to retrieve. If empty, fetches the first page | **Responses:** - `200`: OK → `Roblox.AdConfiguration.Api.GetSponsoredCampaignsResponse` - `400`: 22: Invalid campaign target ID. 23: Invalid campaign target type. - `401`: 0: Authorization has been denied for this request. - `503`: 1: This feature is disabled. **Response fields** (`Roblox.AdConfiguration.Api.GetSponsoredCampaignsResponse`) See [Roblox.AdConfiguration.Api.GetSponsoredCampaignsResponse](#roblox-adconfiguration-api-getsponsoredcampaignsresponse) in Models. **Response example:** ```json { "sponsoredCampaigns": [ { "adId": "...", "adSetId": "...", "adName": "...", "adStatus": "...", "creativeType": "...", "creativeTargetId": "..." } ], "previousPageCursor": "string", "nextPageCursor": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://adconfiguration.roblox.com/v2/sponsored-campaigns?campaignTargetType={VALUE}&campaignTargetId={VALUE}" ``` ### GET `/v2/sponsored-campaigns/eligible-asset-type-ids` Get all asset type IDs that are eligible to be sponsored. **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `integer[]` - `401`: 0: Authorization has been denied for this request. - `403`: 10: Insufficient permissions. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://adconfiguration.roblox.com/v2/sponsored-campaigns/eligible-asset-type-ids" ``` ### GET `/v2/sponsored-campaigns/multi-get-can-user-sponsor` Checks whether the targets are eligible for sponsorship, and if the user is authorized to sponsor the targets. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `campaignTargetType` | query | `integer (int32)` | Yes | Ads.Management.Service.CampaignTargetType. Valid values: `0`, `1`, `2`, `3` | | `campaignTargetIds` | query | `array` | Yes | The IDs of the campaign targets. | **Responses:** - `200`: OK → `object` - `400`: Bad Request - `401`: 0: Authorization has been denied for this request. - `403`: Forbidden - `500`: Server Error **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://adconfiguration.roblox.com/v2/sponsored-campaigns/multi-get-can-user-sponsor?campaignTargetType={VALUE}&campaignTargetIds={VALUE}" ``` ### GET `/v2/sponsored-games` Gets a page of Roblox.AdConfiguration.Api.SponsoredGameV2Model with specified input parameters. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | query | `integer (int64)` | Yes | The universe id of the ad campaign. | | `includeReportingStats` | query | `boolean` | No | Indicates whether to include reporting stats in the response. | | `isArchived` | query | `boolean` | No | Indicates whether to retrieve archived ads. | | `pageCursor` | query | `string` | No | The cursor of the page to retrieve. | **Responses:** - `200`: OK → `Roblox.AdConfiguration.Api.GetSponsoredGamesResponse` - `400`: 9: Cannot load the universe for the specified universe id. 9: Cannot load the universe for the specified universe id. - `401`: 0: Authorization has been denied for this request. - `503`: 1: This feature is disabled. **Response fields** (`Roblox.AdConfiguration.Api.GetSponsoredGamesResponse`) See [Roblox.AdConfiguration.Api.GetSponsoredGamesResponse](#roblox-adconfiguration-api-getsponsoredgamesresponse) in Models. **Response example:** ```json { "sponsoredGames": [ { "adId": "...", "adSetId": "...", "adName": "...", "adStatus": "...", "creativeType": "...", "creativeTargetId": "..." } ], "previousPageCursor": "string", "nextPageCursor": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://adconfiguration.roblox.com/v2/sponsored-games?universeId={VALUE}" ``` ### GET `/v2/sponsored-games/universes` Gets a list of universes for the authenticated user, or the given group, ordered by most recently created sponsored game ads. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | query | `integer (int64)` | No | The group id, if applicable. | **Responses:** - `200`: OK → `Roblox.AdConfiguration.Api.Models.GetRecentAdsRankedUniversesResponse` - `400`: 19: Invalid group id. - `401`: 0: Authorization has been denied for this request. - `403`: 10: Insufficient permissions. **Response fields** (`Roblox.AdConfiguration.Api.Models.GetRecentAdsRankedUniversesResponse`) See [Roblox.AdConfiguration.Api.Models.GetRecentAdsRankedUniversesResponse](#roblox-adconfiguration-api-models-getrecentadsrankeduniversesresponse) in Models. **Response example:** ```json { "universes": [ { "id": "...", "name": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://adconfiguration.roblox.com/v2/sponsored-games/universes" ``` ### POST `/v2/sponsored-campaigns/create` Creates a complete ad. Including ad campaign, ad set, escrow, and the ad. Currently intended for creation of sponsorships only. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.AdConfiguration.Api.Models.CreateSponsoredCampaignRequest` See [Roblox.AdConfiguration.Api.Models.CreateSponsoredCampaignRequest](#roblox-adconfiguration-api-models-createsponsoredcampaignrequest) in Models. **Request example:** ```json { "campaignTargetId": 0, "campaignTargetType": 0, "targetGender": 1, "targetAgeBracket": 1, "startDate": "2024-01-01T00:00:00Z", "endDate": "2024-01-01T00:00:00Z" } ``` **Responses:** - `200`: OK → `integer` - `400`: 2: Daily budget is lower than minimum allowed. 3: Total budget must be greater than 0. 4: Ad name cannot be empty. 5: Start date must not be a future date. 6: End date must be a future date. 7: Start date must be earlier than end date. 8: Total budget does not match daily spend and number of days being scheduled 9: Cannot load the universe for the specified universe id. 11: Invalid target age bracket. 12: Invalid target gender. 13: Invalid target device type. 14: Invalid ad set id. 15: Ad name cannot exceed 255 characters. 16: Insufficient Robux balance. 17: Name has already been taken. 18: Daily budget is higher than maximum allowed. 19: Invalid group id. 20: Number of days scheduled exceeded maximum days allowed. 21: Your experience is currently not eligible for advertising. 22: Invalid campaign target ID. 23: Invalid campaign target type. 24: Invalid creative ID. 25: Invalid creative type. 28: Total budget must be campaign duration * daily bid 29: The target is not eligible for new campaigns 30: Invalid user ID - `401`: 0: Authorization has been denied for this request. 10: Insufficient permissions. - `403`: 0: Token Validation Failed - `500`: 0: An unknown error occurred. 31: Internal server error - `503`: 1: This feature is disabled. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://adconfiguration.roblox.com/v2/sponsored-campaigns/create" \ -H "Content-Type: application/json" \ -d '{ "campaignTargetId": 0, "campaignTargetType": 0, "targetGender": 1, "targetAgeBracket": 1, "startDate": "2024-01-01T00:00:00Z", "endDate": "2024-01-01T00:00:00Z" }' ``` ### POST `/v2/sponsored-campaigns/eligible-campaign-targets` Returns a collection of Roblox.AdConfiguration.Api.Models.CampaignTargetModel that the user is authorized to sponsor, ordered by most recently advertised **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.AdConfiguration.Api.Models.GetEligibleCampaignTargetsRequest` See [Roblox.AdConfiguration.Api.Models.GetEligibleCampaignTargetsRequest](#roblox-adconfiguration-api-models-geteligiblecampaigntargetsrequest) in Models. **Request example:** ```json { "campaignTargetTypes": [ 0 ], "groupId": 0 } ``` **Responses:** - `200`: OK → `Roblox.AdConfiguration.Api.Models.GetCampaignTargetsResponse` - `400`: 19: Invalid group id. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 10: Insufficient permissions. **Response fields** (`Roblox.AdConfiguration.Api.Models.GetCampaignTargetsResponse`) See [Roblox.AdConfiguration.Api.Models.GetCampaignTargetsResponse](#roblox-adconfiguration-api-models-getcampaigntargetsresponse) in Models. **Response example:** ```json { "campaignTargetModels": [ { "campaignTargetType": "...", "campaignTargetId": "...", "name": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://adconfiguration.roblox.com/v2/sponsored-campaigns/eligible-campaign-targets" \ -H "Content-Type: application/json" \ -d '{ "campaignTargetTypes": [ 0 ], "groupId": 0 }' ``` ### POST `/v2/sponsored-campaigns/stop` Stops a sponsored campaign / ad (ad set) from running. Initiated by a user. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.AdConfiguration.Api.Models.StopSponsoredCampaignRequest` See [Roblox.AdConfiguration.Api.Models.StopSponsoredCampaignRequest](#roblox-adconfiguration-api-models-stopsponsoredcampaignrequest) in Models. **Request example:** ```json { "adSetId": 0 } ``` **Responses:** - `200`: OK → `integer` - `400`: 10: Insufficient permissions. 14: Invalid ad set id. 31: Internal server error - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `503`: 1: This feature is disabled. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://adconfiguration.roblox.com/v2/sponsored-campaigns/stop" \ -H "Content-Type: application/json" \ -d '{ "adSetId": 0 }' ``` ### POST `/v2/sponsored-games/create` Creates a new sponsored game ad with specified input parameters. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.AdConfiguration.Api.CreateSponsoredGameV2Request` See [Roblox.AdConfiguration.Api.CreateSponsoredGameV2Request](#roblox-adconfiguration-api-createsponsoredgamev2request) in Models. **Request example:** ```json { "universeId": 0, "targetGender": 1, "targetAgeBracket": 1, "budgetInRobux": 0, "startDate": "2024-01-01T00:00:00Z", "endDate": "2024-01-01T00:00:00Z" } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 2: Daily budget is lower than minimum allowed. 3: Total budget must be greater than 0. 4: Ad name cannot be empty. 5: Start date must not be a future date. 6: End date must be a future date. 7: Start date must be earlier than end date. 8: Total budget does not match daily spend and number of days being scheduled 9: Cannot load the universe for the specified universe id. 11: Invalid target age bracket. 12: Invalid target gender. 13: Invalid target device type. 14: Invalid ad set id. 15: Ad name cannot exceed 255 characters. 16: Insufficient Robux balance. 17: Name has already been taken. 18: Daily budget is higher than maximum allowed. 19: Invalid group id. 20: Number of days scheduled exceeded maximum days allowed. 21: Your experience is currently not eligible for advertising. 22: Invalid campaign target ID. 23: Invalid campaign target type. 24: Invalid creative ID. 25: Invalid creative type. 28: Total budget must be campaign duration * daily bid 29: The target is not eligible for new campaigns 30: Invalid user ID - `401`: 0: Authorization has been denied for this request. 10: Insufficient permissions. - `403`: 0: Token Validation Failed - `500`: 0: An unknown error occurred. 31: Internal server error - `503`: 1: This feature is disabled. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://adconfiguration.roblox.com/v2/sponsored-games/create" \ -H "Content-Type: application/json" \ -d '{ "universeId": 0, "targetGender": 1, "targetAgeBracket": 1, "budgetInRobux": 0, "startDate": "2024-01-01T00:00:00Z", "endDate": "2024-01-01T00:00:00Z" }' ``` ### POST `/v2/sponsored-games/stop` To stop a sponsored-game ad (ad set) from running, initiated by a user. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.AdConfiguration.Api.Models.StopSponsoredGameV2Request` See [Roblox.AdConfiguration.Api.Models.StopSponsoredGameV2Request](#roblox-adconfiguration-api-models-stopsponsoredgamev2request) in Models. **Request example:** ```json { "adSetId": 0 } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 14: Invalid ad set id. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 10: Insufficient permissions. - `503`: 1: This feature is disabled. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://adconfiguration.roblox.com/v2/sponsored-games/stop" \ -H "Content-Type: application/json" \ -d '{ "adSetId": 0 }' ``` ## Models ### Roblox.AdConfiguration.Api.CreateSponsoredGameV2Request A request model for creating a sponsored game | Property | Type | Required | Description | |----------|------|----------|-------------| | `universeId` | `integer` | No | The target universe id | | `targetGender` | `1 \| 2 \| 4` | No | Targeting gender(s) of the ad set ['Undefined' = 1, 'Male' = 2, 'Female' = 4] | | `targetAgeBracket` | `1 \| 2 \| 4 \| 8 \| 16` | No | Targeting age bracket(s) of the ad set ['Undefined' = 1, 'AgeUnder13' = 2, 'Age13OrOver' = 4, 'Age13To16' = 8, 'Age17OrOver' = 16] | | `budgetInRobux` | `integer` | No | The budget in Robux | | `startDate` | `string` | No | The start date of the ad set | | `endDate` | `string` | No | The end date of the ad set | | `targetDeviceType` | `integer enum (6 values)` | No | Targeting device type(s) of the ad set ['Undefined' = 1, 'Computer' = 2, 'Phone' = 4, 'Tablet' = 8, 'Console' = 16, 'VR' = 32] Values: 1, 2, 4, 8, 16, 32 | | `adName` | `string` | No | The name of the Ad | | `bidAmountInRobux` | `integer` | No | The bid amount of the Ad in Robux | ### Roblox.AdConfiguration.Api.CreativeModel A model representing an Ad Creative (for example, an ad thumbnail). | Property | Type | Required | Description | |----------|------|----------|-------------| | `creativeId` | `integer` | No | The ID of the creative. Typically, a thumbnail's imageId. | | `creativeType` | `0 \| 1 \| 2` | No | The type of the ad creative. Typically, CreativeType.Image. ['Undefined' = 0, 'Image' = 1, 'Video' = 2] | ### Roblox.AdConfiguration.Api.GetSponsoredCampaignsResponse A response model for retrieving a page of Roblox.AdConfiguration.Api.SponsoredCampaignModel. | Property | Type | Required | Description | |----------|------|----------|-------------| | `sponsoredCampaigns` | `Roblox.AdConfiguration.Api.SponsoredCampaignModel[]` | No | A collection of Roblox.AdConfiguration.Api.SponsoredCampaignModel. | | `previousPageCursor` | `string` | No | The cursor for retrieving the previous page, if present. | | `nextPageCursor` | `string` | No | The cursor for retrieving the next page, if present. | ### Roblox.AdConfiguration.Api.GetSponsoredGamesResponse A response model for retrieving a page of Roblox.AdConfiguration.Api.SponsoredGameV2Model. | Property | Type | Required | Description | |----------|------|----------|-------------| | `sponsoredGames` | `Roblox.AdConfiguration.Api.SponsoredGameV2Model[]` | No | A collection of Roblox.AdConfiguration.Api.SponsoredGameV2Model. | | `previousPageCursor` | `string` | No | The cursor for retrieving the previous page, if present. | | `nextPageCursor` | `string` | No | The cursor for retrieving the next page, if present. | ### Roblox.AdConfiguration.Api.Models.CampaignTargetModel Represents a sponsored ad campaign target | Property | Type | Required | Description | |----------|------|----------|-------------| | `campaignTargetType` | `0 \| 1 \| 2 \| 3` | No | The campaign target type ['Undefined' = 0, 'Universe' = 1, 'Asset' = 2, 'ImmersiveAd' = 3] | | `campaignTargetId` | `integer` | No | The ID of the campaign target | | `name` | `string` | No | The name of the campaign target (i.e. the asset name, universe name, group name, etc.) | ### Roblox.AdConfiguration.Api.Models.CreateSponsoredCampaignRequest A request model for creating a sponsored game | Property | Type | Required | Description | |----------|------|----------|-------------| | `campaignTargetId` | `integer` | No | The ID of the campaign target | | `campaignTargetType` | `0 \| 1 \| 2 \| 3` | No | The type of the campaign target ['Undefined' = 0, 'Universe' = 1, 'Asset' = 2, 'ImmersiveAd' = 3] | | `targetGender` | `1 \| 2 \| 4` | No | Targeting gender(s) of the ad set ['Undefined' = 1, 'Male' = 2, 'Female' = 4] | | `targetAgeBracket` | `1 \| 2 \| 4 \| 8 \| 16` | No | Targeting age bracket(s) of the ad set ['Undefined' = 1, 'AgeUnder13' = 2, 'Age13OrOver' = 4, 'Age13To16' = 8, 'Age17OrOver' = 16] | | `startDate` | `string` | No | The start date of the ad set | | `endDate` | `string` | No | The end date of the ad set | | `targetDeviceType` | `integer enum (6 values)` | No | Targeting device type(s) of the ad set ['Undefined' = 1, 'Computer' = 2, 'Phone' = 4, 'Tablet' = 8, 'Console' = 16, 'VR' = 32] Values: 1, 2, 4, 8, 16, 32 | | `campaignName` | `string` | No | The name of the Campaign / Ad | | `dailyBidAmountInRobux` | `integer` | No | The daily bid amount for the campaign / ad, in Robux | | `placementLocation` | `integer enum (8 values)` | No | The location to place the campaign ['Undefined' = 1, 'GameSort' = 2, 'AvatarShop' = 4, 'ItemDetails' = 8, 'HomePage' = 16, 'Billboard300x250' = 32, 'Billboard600x300' = 64, 'Billboard300x600' = 128] Values: 1, 2, 4, 8, 16, 32, 64, 128 | | `creativeModel` | `Roblox.AdConfiguration.Api.CreativeModel` | No | | ### Roblox.AdConfiguration.Api.Models.GetCampaignTargetsResponse The response model which returns a collection of Roblox.AdConfiguration.Api.Models.CampaignTargetModel | Property | Type | Required | Description | |----------|------|----------|-------------| | `campaignTargetModels` | `Roblox.AdConfiguration.Api.Models.CampaignTargetModel[]` | No | Gets or sets a collection of Roblox.AdConfiguration.Api.Models.CampaignTargetModel | ### Roblox.AdConfiguration.Api.Models.GetEligibleCampaignTargetsRequest A model represents a request to stop a sponsored campaign / ad. | Property | Type | Required | Description | |----------|------|----------|-------------| | `campaignTargetTypes` | `0 \| 1 \| 2 \| 3[]` | No | The list of campaign types we want to include in the results | | `groupId` | `integer` | No | The group id, if applicable. | ### Roblox.AdConfiguration.Api.Models.GetRecentAdsRankedUniversesResponse The response model for getting a list of recent-ads-ranked universes. | Property | Type | Required | Description | |----------|------|----------|-------------| | `universes` | `Roblox.AdConfiguration.Api.Models.UniverseModel[]` | No | Gets or sets a list of Roblox.AdConfiguration.Api.Models.UniverseModel. | ### Roblox.AdConfiguration.Api.Models.StopSponsoredCampaignRequest A model represents a request to stop a sponsored campaign / ad. | Property | Type | Required | Description | |----------|------|----------|-------------| | `adSetId` | `integer` | No | The ID of the ad set to stop. | ### Roblox.AdConfiguration.Api.Models.StopSponsoredGameV2Request A model represents a request to stop a sponsored game ad. | Property | Type | Required | Description | |----------|------|----------|-------------| | `adSetId` | `integer` | No | Gets the ID of the ad set to stop. | ### Roblox.AdConfiguration.Api.Models.UniverseModel Represents a universe in API endpoint results. | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | The universe Id. | | `name` | `string` | No | The name of the universe | ### Roblox.AdConfiguration.Api.SponsoredCampaignModel A response model represents an Ad with complete information, including infos from its associated campaign and ad set | Property | Type | Required | Description | |----------|------|----------|-------------| | `adId` | `integer` | No | The id of the ad | | `adSetId` | `integer` | No | The id of the ad set | | `adName` | `string` | No | The name of the Ad | | `adStatus` | `0 \| 1 \| 2 \| 3 \| 4` | No | The status of the ad ['Undefined' = 0, 'Draft' = 1, 'PendingApproval' = 2, 'Approved' = 3, 'Moderated' = 4] | | `creativeType` | `0 \| 1 \| 2` | No | The type of the creative associated with the Ad ['Undefined' = 0, 'Image' = 1, 'Video' = 2] | | `creativeTargetId` | `integer` | No | The target id of the creative associated with the Ad | | `bidAmountInRobux` | `integer` | No | The bid amount of the Ad in Robux | | `budgetInRobux` | `integer` | No | The budget of the ad set in Robux | | `adSetStatus` | `integer enum (7 values)` | No | The status of the ad set ['Undefined' = 0, 'Draft' = 1, 'Scheduled' = 2, 'Running' = 3, 'Paused' = 4, 'Stopped' = 5, 'Completed' = 6] Values: 0, 1, 2, 3, 4, 5, 6 | | `startDate` | `string` | No | The start date of the ad set | | `endDate` | `string` | No | The end date of the ad set | | `targetGender` | `1 \| 2 \| 4` | No | The target gender of the ad set ['Undefined' = 1, 'Male' = 2, 'Female' = 4] | | `targetAgeBracket` | `1 \| 2 \| 4 \| 8 \| 16` | No | The target age bracket of the ad set ['Undefined' = 1, 'AgeUnder13' = 2, 'Age13OrOver' = 4, 'Age13To16' = 8, 'Age17OrOver' = 16] | | `targetDeviceType` | `integer enum (6 values)` | No | The target device type of the ad set ['Undefined' = 1, 'Computer' = 2, 'Phone' = 4, 'Tablet' = 8, 'Console' = 16, 'VR' = 32] Values: 1, 2, 4, 8, 16, 32 | | `campaignTargetType` | `0 \| 1 \| 2 \| 3` | No | The target type of the campaign ['Undefined' = 0, 'Universe' = 1, 'Asset' = 2, 'ImmersiveAd' = 3] | | `campaignTargetId` | `integer` | No | The target id of the campaign | | `totalSpendInRobux` | `integer` | No | The total spend of the ad set in Robux | | `totalImpressions` | `integer` | No | The total number of impressions generated by the ad | | `totalClicks` | `integer` | No | The total number of clicks generated by the ad | | `totalConversions` | `integer` | No | The total number of conversions generated by the ad | | `impressionConversions` | `integer` | No | The number of conversions by ad impression generated by the ad | | `clickConversions` | `integer` | No | The number of conversions by ad click generated by the ad | ### Roblox.AdConfiguration.Api.SponsoredGameV2Model A response model represents an Ad with complete information, including infos from its associated campaign and ad set | Property | Type | Required | Description | |----------|------|----------|-------------| | `adId` | `integer` | No | The id of the ad | | `adSetId` | `integer` | No | The id of the ad set | | `adName` | `string` | No | The name of the Ad | | `adStatus` | `0 \| 1 \| 2 \| 3 \| 4` | No | The status of the ad ['Undefined' = 0, 'Draft' = 1, 'PendingApproval' = 2, 'Approved' = 3, 'Moderated' = 4] | | `creativeType` | `0 \| 1 \| 2` | No | The type of the creative associated with the Ad ['Undefined' = 0, 'Image' = 1, 'Video' = 2] | | `creativeTargetId` | `integer` | No | The target id of the creative associated with the Ad | | `creativeUrl` | `string` | No | The Url of the creative | | `bidAmountInRobux` | `integer` | No | The bid amount of the Ad in Robux | | `budgetInRobux` | `integer` | No | The budget of the ad set in Robux | | `adSetStatus` | `integer enum (7 values)` | No | The status of the ad set ['Undefined' = 0, 'Draft' = 1, 'Scheduled' = 2, 'Running' = 3, 'Paused' = 4, 'Stopped' = 5, 'Completed' = 6] Values: 0, 1, 2, 3, 4, 5, 6 | | `startDate` | `string` | No | The start date of the ad set | | `endDate` | `string` | No | The end date of the ad set | | `targetGender` | `1 \| 2 \| 4` | No | The target gender of the ad set ['Undefined' = 1, 'Male' = 2, 'Female' = 4] | | `targetAgeBracket` | `1 \| 2 \| 4 \| 8 \| 16` | No | The target age bracket of the ad set ['Undefined' = 1, 'AgeUnder13' = 2, 'Age13OrOver' = 4, 'Age13To16' = 8, 'Age17OrOver' = 16] | | `targetDeviceType` | `integer enum (6 values)` | No | The target device type of the ad set ['Undefined' = 1, 'Computer' = 2, 'Phone' = 4, 'Tablet' = 8, 'Console' = 16, 'VR' = 32] Values: 1, 2, 4, 8, 16, 32 | | `campaignTargetType` | `0 \| 1 \| 2 \| 3` | No | The target type of the campaign ['Undefined' = 0, 'Universe' = 1, 'Asset' = 2, 'ImmersiveAd' = 3] | | `campaignTargetId` | `integer` | No | The target id of the campaign | | `totalSpendInRobux` | `integer` | No | The total spend of the ad set in Robux | | `totalImpressions` | `integer` | No | The total number of impressions generated by the ad | | `totalClicks` | `integer` | No | The total number of clicks generated by the ad | | `totalConversions` | `integer` | No | The total number of conversions generated by the ad | | `impressionConversions` | `integer` | No | The number of conversions by ad impression generated by the ad | | `clickConversions` | `integer` | No | The number of conversions by ad click generated by the ad | --- name: "Asset Delivery Api v1" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://assetdelivery.roblox.com" version: "v1" endpoints: 8 auth: [cookie] --- # Asset Delivery Api v1 **API Version:** v1 **Base URL:** `https://assetdelivery.roblox.com` ## Endpoints ### GET `/v1/alias/{alias}` Retrieves an asset by its alias (universeID/name) **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `alias` | path | `string` | Yes | The alias of the asset to retrieve. | | `Accept-Encoding` | header | `string` | Yes | | | `Roblox-Place-Id` | header | `integer (int64)` | Yes | | | `AssetType` | header | `string` | Yes | | | `Accept` | header | `string` | Yes | | | `AssetFormat` | header | `string` | Yes | | | `Roblox-AssetFormat` | header | `string` | Yes | | | `skipSigningScripts` | query | `boolean` | No | | | `clientInsert` | query | `integer (int32)` | No | | | `scriptinsert` | query | `integer (int32)` | No | | | `modulePlaceId` | query | `integer (int64)` | No | | | `serverplaceid` | query | `integer (int64)` | No | | | `expectedAssetType` | query | `string` | No | | | `accessContext` | query | `string` | No | | | `usageContext` | query | `integer (int32)` | No | | **Responses:** - `200`: OK → `Roblox.Web.Assets.AssetResponseItemV1` **Response fields** (`Roblox.Web.Assets.AssetResponseItemV1`) See [Roblox.Web.Assets.AssetResponseItemV1](#roblox-web-assets-assetresponseitemv1) in Models. **Response example:** ```json { "location": "string", "errors": [ { "Code": "...", "Message": "...", "CustomErrorCode": "..." } ], "requestId": "string", "isArchived": false, "assetTypeId": 0, "contentRepresentationSpecifier": { "format": "string", "majorVersion": "string", "fidelity": "string", "skipGenerationIfNotExist": false } } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://assetdelivery.roblox.com/v1/alias/{ALIAS}" ``` ### GET `/v1/asset` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `Accept-Encoding` | header | `string` | Yes | | | `Roblox-Place-Id` | header | `integer (int64)` | Yes | | | `AssetType` | header | `string` | Yes | | | `Accept` | header | `string` | Yes | | | `AssetFormat` | header | `string` | Yes | | | `Roblox-AssetFormat` | header | `string` | Yes | | | `id` | query | `integer (int64)` | No | | | `userAssetId` | query | `integer (int64)` | No | | | `assetVersionId` | query | `integer (int64)` | No | | | `version` | query | `integer (int32)` | No | | | `universeId` | query | `integer (int64)` | No | | | `clientInsert` | query | `integer (int32)` | No | | | `scriptinsert` | query | `integer (int32)` | No | | | `modulePlaceId` | query | `integer (int64)` | No | | | `serverplaceid` | query | `string` | No | | | `assetName` | query | `string` | No | | | `hash` | query | `string` | No | | | `marAssetHash` | query | `string` | No | | | `marCheckSum` | query | `string` | No | | | `expectedAssetType` | query | `string` | No | | | `skipSigningScripts` | query | `boolean` | No | | | `permissionContext` | query | `string` | No | | | `doNotFallbackToBaselineRepresentation` | query | `boolean` | No | | | `contentRepresentationPriorityList` | query | `string` | No | | | `assetResolutionMode` | query | `string` | No | | | `accessContext` | query | `string` | No | | | `usageContext` | query | `integer (int32)` | No | | **Responses:** - `200`: OK **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://assetdelivery.roblox.com/v1/asset" ``` ### GET `/v1/assetId/{assetId}` Retrieves an asset by its ID **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetId` | path | `integer (int64)` | Yes | The ID of the asset to retrieve. | | `Accept-Encoding` | header | `string` | Yes | | | `Roblox-Place-Id` | header | `integer (int64)` | Yes | | | `AssetType` | header | `string` | Yes | | | `Accept` | header | `string` | Yes | | | `AssetFormat` | header | `string` | Yes | | | `Roblox-AssetFormat` | header | `string` | Yes | | | `skipSigningScripts` | query | `boolean` | No | | | `clientInsert` | query | `integer (int32)` | No | | | `scriptinsert` | query | `integer (int32)` | No | | | `modulePlaceId` | query | `integer (int64)` | No | | | `serverplaceid` | query | `integer (int64)` | No | | | `expectedAssetType` | query | `string` | No | | | `doNotFallbackToBaselineRepresentation` | query | `boolean` | No | | | `contentRepresentationPriorityList` | query | `string` | No | | | `accessContext` | query | `string` | No | | | `usageContext` | query | `integer (int32)` | No | | **Responses:** - `200`: OK → `Roblox.Web.Assets.AssetResponseItemV1` **Response fields** (`Roblox.Web.Assets.AssetResponseItemV1`) See [Roblox.Web.Assets.AssetResponseItemV1](#roblox-web-assets-assetresponseitemv1) in Models. **Response example:** ```json { "location": "string", "errors": [ { "Code": "...", "Message": "...", "CustomErrorCode": "..." } ], "requestId": "string", "isArchived": false, "assetTypeId": 0, "contentRepresentationSpecifier": { "format": "string", "majorVersion": "string", "fidelity": "string", "skipGenerationIfNotExist": false } } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://assetdelivery.roblox.com/v1/assetId/{ASSETID}" ``` ### GET `/v1/assetId/{assetId}/version/{versionNumber}` Retrieves an asset by its ID and version number. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetId` | path | `integer (int64)` | Yes | The ID of the asset to retrieve. | | `versionNumber` | path | `integer (int32)` | Yes | The version of the asset to retrieve. | | `Accept-Encoding` | header | `string` | Yes | | | `Roblox-Place-Id` | header | `integer (int64)` | Yes | | | `AssetType` | header | `string` | Yes | | | `Accept` | header | `string` | Yes | | | `AssetFormat` | header | `string` | Yes | | | `Roblox-AssetFormat` | header | `string` | Yes | | | `skipSigningScripts` | query | `boolean` | No | | | `clientInsert` | query | `integer (int32)` | No | | | `scriptinsert` | query | `integer (int32)` | No | | | `modulePlaceId` | query | `integer (int64)` | No | | | `serverplaceid` | query | `integer (int64)` | No | | | `expectedAssetType` | query | `string` | No | | | `doNotFallbackToBaselineRepresentation` | query | `boolean` | No | | | `contentRepresentationPriorityList` | query | `string` | No | | | `accessContext` | query | `string` | No | | | `usageContext` | query | `integer (int32)` | No | | **Responses:** - `200`: OK → `Roblox.Web.Assets.AssetResponseItemV1` **Response fields** (`Roblox.Web.Assets.AssetResponseItemV1`) See [Roblox.Web.Assets.AssetResponseItemV1](#roblox-web-assets-assetresponseitemv1) in Models. **Response example:** ```json { "location": "string", "errors": [ { "Code": "...", "Message": "...", "CustomErrorCode": "..." } ], "requestId": "string", "isArchived": false, "assetTypeId": 0, "contentRepresentationSpecifier": { "format": "string", "majorVersion": "string", "fidelity": "string", "skipGenerationIfNotExist": false } } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://assetdelivery.roblox.com/v1/assetId/{ASSETID}/version/{VERSIONNUMBER}" ``` ### GET `/v1/marAssetHash/{marAssetHash}/marCheckSum/{marCheckSum}` Retrieves an asset by its mar (moderation agnostic) hash and mar (moderation agnostic) checksum. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `marAssetHash` | path | `string` | Yes | The mar (moderation agnostic) hash of the asset to retrieve. | | `marCheckSum` | path | `string` | Yes | The mar (moderation agnostic) checksum of the asset to retrieve. | | `Accept-Encoding` | header | `string` | Yes | | | `Roblox-Place-Id` | header | `integer (int64)` | Yes | | | `AssetType` | header | `string` | Yes | | | `Accept` | header | `string` | Yes | | | `AssetFormat` | header | `string` | Yes | | | `Roblox-AssetFormat` | header | `string` | Yes | | | `skipSigningScripts` | query | `boolean` | No | | | `clientInsert` | query | `integer (int32)` | No | | | `scriptinsert` | query | `integer (int32)` | No | | | `modulePlaceId` | query | `integer (int64)` | No | | | `serverplaceid` | query | `integer (int64)` | No | | | `expectedAssetType` | query | `string` | No | | **Responses:** - `200`: OK → `Roblox.Web.Assets.AssetResponseItemV1` - `400`: 2: invalid server request 3: Encoding cannot be empty - `404`: 5: Asset hash cannot be empty **Response fields** (`Roblox.Web.Assets.AssetResponseItemV1`) See [Roblox.Web.Assets.AssetResponseItemV1](#roblox-web-assets-assetresponseitemv1) in Models. **Response example:** ```json { "location": "string", "errors": [ { "Code": "...", "Message": "...", "CustomErrorCode": "..." } ], "requestId": "string", "isArchived": false, "assetTypeId": 0, "contentRepresentationSpecifier": { "format": "string", "majorVersion": "string", "fidelity": "string", "skipGenerationIfNotExist": false } } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://assetdelivery.roblox.com/v1/marAssetHash/{MARASSETHASH}/marCheckSum/{MARCHECKSUM}" ``` ### GET `/v1/openCloud/assetId/{assetId}` Retrieves an asset by its ID with OpenCloud auth. Returns an object containing a `location` property which is a temporary CDN URL for the asset content. All asset types are supported. You should request that URL with the `Accept-Encoding: gzip` header and decompress the result if the response is gzipped. If you are using cURL, the `--compressed` flag will automate these steps for you. This endpoint is expected to be called with API key authentication through `apis.roblox.com/asset-delivery-api/v1/assetId/{assetId}`. While you are able to make requests to this endpoint with Cookie authentication via `assetdelivery.roblox.com/v1/openCloud/assetId/{assetId}`, we highly discourage use this way. Expect unannounced removal of this second route in the future. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetId` | path | `integer (int64)` | Yes | The ID of the asset to retrieve. | | `Accept-Encoding` | header | `string` | No | The Accept-Encoding header value specifying compression formats (e.g., "gzip, deflate"). Defaults to "gzip, deflate" if not provided. | | `Roblox-Place-Id` | header | `integer (int64)` | No | The Roblox-Place-Id header value identifying the place making the request. | | `AssetType` | header | `string` | No | The AssetType header value specifying the expected asset type. | | `Accept` | header | `string` | No | The Accept header value specifying the preferred response content type. | | `AssetFormat` | header | `string` | No | The AssetFormat header value specifying the desired asset format. Overridden by robloxAssetFormat if both are provided. | | `Roblox-AssetFormat` | header | `string` | No | The Roblox-AssetFormat header value specifying the preferred Roblox-specific asset format. Takes precedence over assetFormat. | | `skipSigningScripts` | query | `boolean` | No | Whether to skip script signing for the returned asset. Used for script assets that don't require signing. | | `clientInsert` | query | `integer (int32)` | No | Set to 1 to indicate this is a client insert request. | | `scriptinsert` | query | `integer (int32)` | No | Set to 1 to indicate this is a script insert request. | | `modulePlaceId` | query | `integer (int64)` | No | The place ID of the module making the request. | | `serverplaceid` | query | `integer (int64)` | No | The server place ID making the request. | | `expectedAssetType` | query | `string` | No | The expected asset type as a fallback when assetType header is not provided. | | `doNotFallbackToBaselineRepresentation` | query | `boolean` | No | Whether to prevent fallback to baseline representation when specific content representations are not available. | | `contentRepresentationPriorityList` | query | `string` | No | Base64URL-encoded JSON string specifying the priority list of desired content representations (format, version, fidelity). | | `accessContext` | query | `string` | No | | | `usageContext` | query | `integer (int32)` | No | | **Responses:** - `200`: OK → `Roblox.Web.Assets.AssetResponseItemV1` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Web.Assets.AssetResponseItemV1`) See [Roblox.Web.Assets.AssetResponseItemV1](#roblox-web-assets-assetresponseitemv1) in Models. **Response example:** ```json { "location": "string", "errors": [ { "Code": "...", "Message": "...", "CustomErrorCode": "..." } ], "requestId": "string", "isArchived": false, "assetTypeId": 0, "contentRepresentationSpecifier": { "format": "string", "majorVersion": "string", "fidelity": "string", "skipGenerationIfNotExist": false } } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://assetdelivery.roblox.com/v1/openCloud/assetId/{ASSETID}" ``` ### GET `/v1/openCloud/assetId/{assetId}/version/{versionNumber}` Retrieves an asset by its ID and version number with OpenCloud auth. Refer to the assetId endpoint for details on usage. This endpoint is expected to be called with API key authentication through `apis.roblox.com/asset-delivery-api/v1/assetId/{assetId}/version/{versionNumber}`. While you are able to make requests to this endpoint with Cookie authentication via `assetdelivery.roblox.com/v1/openCloud/assetId/{assetId}/version/{versionNumber}`, we highly discourage use this way. Expect unannounced removal of this second route in the future. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetId` | path | `integer (int64)` | Yes | The ID of the asset to retrieve. | | `versionNumber` | path | `integer (int32)` | Yes | The version number of the asset to retrieve. | | `Accept-Encoding` | header | `string` | No | | | `Roblox-Place-Id` | header | `integer (int64)` | No | | | `AssetType` | header | `string` | No | | | `Accept` | header | `string` | No | | | `AssetFormat` | header | `string` | No | | | `Roblox-AssetFormat` | header | `string` | No | | | `skipSigningScripts` | query | `boolean` | No | | | `clientInsert` | query | `integer (int32)` | No | | | `scriptinsert` | query | `integer (int32)` | No | | | `modulePlaceId` | query | `integer (int64)` | No | | | `serverplaceid` | query | `integer (int64)` | No | | | `expectedAssetType` | query | `string` | No | | | `doNotFallbackToBaselineRepresentation` | query | `boolean` | No | | | `contentRepresentationPriorityList` | query | `string` | No | | | `accessContext` | query | `string` | No | | | `usageContext` | query | `integer (int32)` | No | | **Responses:** - `200`: OK → `Roblox.Web.Assets.AssetResponseItemV1` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Web.Assets.AssetResponseItemV1`) See [Roblox.Web.Assets.AssetResponseItemV1](#roblox-web-assets-assetresponseitemv1) in Models. **Response example:** ```json { "location": "string", "errors": [ { "Code": "...", "Message": "...", "CustomErrorCode": "..." } ], "requestId": "string", "isArchived": false, "assetTypeId": 0, "contentRepresentationSpecifier": { "format": "string", "majorVersion": "string", "fidelity": "string", "skipGenerationIfNotExist": false } } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://assetdelivery.roblox.com/v1/openCloud/assetId/{ASSETID}/version/{VERSIONNUMBER}" ``` ### POST `/v1/assets/batch` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `Roblox-Place-Id` | header | `integer (int64)` | Yes | | | `Accept` | header | `string` | Yes | | | `Roblox-Browser-Asset-Request` | header | `string` | Yes | | **Request Body:** `application/json` — Type: `Roblox.Web.Assets.BatchAssetRequestItem[]` See [Roblox.Web.Assets.BatchAssetRequestItem](#roblox-web-assets-batchassetrequestitem) in Models. **Responses:** - `200`: OK → `Roblox.Web.Assets.AssetResponseItemV1[]` **Response fields** (`Roblox.Web.Assets.AssetResponseItemV1[]`) See [Roblox.Web.Assets.AssetResponseItemV1](#roblox-web-assets-assetresponseitemv1) in Models. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://assetdelivery.roblox.com/v1/assets/batch" \ -H "Content-Type: application/json" \ -d '[ { "assetName": "string", "assetType": "string", "clientInsert": false, "placeId": 0, "requestId": "string", "scriptInsert": false } ]' ``` ## Models ### Roblox.AssetDelivery.Api.AssetMetadata An asset piece of metadata. | Property | Type | Required | Description | |----------|------|----------|-------------| | `metadataType` | `1` | No | Asset metadata type. ['UncompressedSize' = 1] | | `value` | `string` | No | | ### Roblox.Web.Assets.AssetContentRepresentationSpecifier | Property | Type | Required | Description | |----------|------|----------|-------------| | `format` | `string` | No | | | `majorVersion` | `string` | No | | | `fidelity` | `string` | No | | | `skipGenerationIfNotExist` | `boolean` | No | | ### Roblox.Web.Assets.AssetResponseItemV1 | Property | Type | Required | Description | |----------|------|----------|-------------| | `location` | `string` | No | | | `errors` | `Roblox.Web.Assets.IAssetItemError[]` | No | | | `requestId` | `string` | No | | | `isArchived` | `boolean` | No | Whether the asset has been archived. | | `assetTypeId` | `integer` | No | Asset Type. | | `contentRepresentationSpecifier` | `Roblox.Web.Assets.AssetContentRepresentationSpecifier` | No | | | `assetMetadatas` | `Roblox.AssetDelivery.Api.AssetMetadata[]` | No | | | `isRecordable` | `boolean` | No | Whether the asset is recordable in screen recordings. | ### Roblox.Web.Assets.BatchAssetRequestItem | Property | Type | Required | Description | |----------|------|----------|-------------| | `assetName` | `string` | No | | | `assetType` | `string` | No | | | `clientInsert` | `boolean` | No | | | `placeId` | `integer` | No | | | `requestId` | `string` | No | | | `scriptInsert` | `boolean` | No | | | `serverPlaceId` | `integer` | No | | | `universeId` | `integer` | No | | | `accept` | `string` | No | | | `encoding` | `string` | No | | | `hash` | `string` | No | | | `userAssetId` | `integer` | No | | | `assetId` | `integer` | No | | | `version` | `integer` | No | | | `assetVersionId` | `integer` | No | | | `modulePlaceId` | `integer` | No | | | `assetFormat` | `string` | No | | | `roblox-assetFormat` | `string` | No | | | `assetResolutionMode` | `string` | No | | | `accessContext` | `string` | No | | | `usageContext` | `integer` | No | | | `contentRepresentationPriorityList` | `string` | No | | | `doNotFallbackToBaselineRepresentation` | `boolean` | No | | ### Roblox.Web.Assets.IAssetItemError | Property | Type | Required | Description | |----------|------|----------|-------------| | `Code` | `integer` | No | | | `Message` | `string` | No | | | `CustomErrorCode` | `integer enum (24 values)` | No | Custom error code for Roblox.Web.Assets.IAssetItemError ['UnknownError' = 0, 'NoPermissionToAsset' = 1, 'AssetPermissionCheckFailed' = 2, 'NotAuthorizedForAgeRecommendation' = 3, 'AgeRecommendationCheckFailed' = 4, 'InvalidPlaceRequestFromGameServer' = 5, 'BlockedAssetTypeRequestedFromInsertService' = 6, 'BlockedAssetTypeRequestedFromGameServer' = 7, 'AssetTypeMismatch' = 8, 'MissingAssetTypeInRequestHeader' = 9, 'AssetNotTrustedForPlace' = 10, 'NoAuthentication' = 11, 'AssetContentRepresentationBlockedDueToModeration' = 12, 'AssetNotFound' = 13, 'AssetVersionNotFound' = 14, 'AssetContentRepresentationNotFound' = 15, 'BlockedByAgeGeoRestriction' = 16, 'BlockedAssetTypeRequestedFromNonGameServer' = 17, 'AssetPendingReview' = 18, 'NotApprovedForRequestor' = 19, 'NotApprovedByContentCompliance' = 20, 'AssetContentRepresentationGenerating' = 21, 'AssetArchived' = 22, 'AssetUsageNotAllowed' = 23] Values: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 | --- name: "Asset Delivery Api v2" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://assetdelivery.roblox.com" version: "v2" endpoints: 6 auth: [cookie] --- # Asset Delivery Api v2 **API Version:** v2 **Base URL:** `https://assetdelivery.roblox.com` ## Endpoints ### GET `/v2/alias/{alias}` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `alias` | path | `string` | Yes | | | `Accept-Encoding` | header | `string` | Yes | | | `Roblox-Place-Id` | header | `integer (int64)` | Yes | | | `AssetType` | header | `string` | Yes | | | `Accept` | header | `string` | Yes | | | `AssetFormat` | header | `string` | Yes | | | `Roblox-AssetFormat` | header | `string` | Yes | | | `skipSigningScripts` | query | `boolean` | No | | | `clientInsert` | query | `integer (int32)` | No | | | `scriptinsert` | query | `integer (int32)` | No | | | `modulePlaceId` | query | `integer (int64)` | No | | | `serverplaceid` | query | `integer (int64)` | No | | | `expectedAssetType` | query | `string` | No | | | `accessContext` | query | `string` | No | | | `usageContext` | query | `integer (int32)` | No | | **Responses:** - `200`: OK → `Roblox.Web.Assets.AssetResponseItemV2` **Response fields** (`Roblox.Web.Assets.AssetResponseItemV2`) See [Roblox.Web.Assets.AssetResponseItemV2](#roblox-web-assets-assetresponseitemv2) in Models. **Response example:** ```json { "locations": [ { "assetFormat": "...", "location": "...", "assetMetadatas": "..." } ], "errors": [ { "Code": "...", "Message": "...", "CustomErrorCode": "..." } ], "requestId": "string", "isArchived": false, "assetTypeId": 0, "contentRepresentationSpecifier": { "format": "string", "majorVersion": "string", "fidelity": "string", "skipGenerationIfNotExist": false } } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://assetdelivery.roblox.com/v2/alias/{ALIAS}" ``` ### GET `/v2/asset` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `Accept-Encoding` | header | `string` | Yes | | | `Roblox-Place-Id` | header | `integer (int64)` | Yes | | | `AssetType` | header | `string` | Yes | | | `Accept` | header | `string` | Yes | | | `AssetFormat` | header | `string` | Yes | | | `Roblox-AssetFormat` | header | `string` | Yes | | | `id` | query | `integer (int64)` | No | | | `userAssetId` | query | `integer (int64)` | No | | | `assetVersionId` | query | `integer (int64)` | No | | | `version` | query | `integer (int32)` | No | | | `universeId` | query | `integer (int64)` | No | | | `clientInsert` | query | `integer (int32)` | No | | | `scriptinsert` | query | `integer (int32)` | No | | | `modulePlaceId` | query | `integer (int64)` | No | | | `serverplaceid` | query | `string` | No | | | `assetName` | query | `string` | No | | | `hash` | query | `string` | No | | | `marAssetHash` | query | `string` | No | | | `marCheckSum` | query | `string` | No | | | `expectedAssetType` | query | `string` | No | | | `skipSigningScripts` | query | `boolean` | No | | | `permissionContext` | query | `string` | No | | | `doNotFallbackToBaselineRepresentation` | query | `boolean` | No | | | `contentRepresentationPriorityList` | query | `string` | No | | | `accessContext` | query | `string` | No | | | `usageContext` | query | `integer (int32)` | No | | **Responses:** - `200`: OK → `Roblox.Web.Assets.AssetResponseItemV2` **Response fields** (`Roblox.Web.Assets.AssetResponseItemV2`) See [Roblox.Web.Assets.AssetResponseItemV2](#roblox-web-assets-assetresponseitemv2) in Models. **Response example:** ```json { "locations": [ { "assetFormat": "...", "location": "...", "assetMetadatas": "..." } ], "errors": [ { "Code": "...", "Message": "...", "CustomErrorCode": "..." } ], "requestId": "string", "isArchived": false, "assetTypeId": 0, "contentRepresentationSpecifier": { "format": "string", "majorVersion": "string", "fidelity": "string", "skipGenerationIfNotExist": false } } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://assetdelivery.roblox.com/v2/asset" ``` ### GET `/v2/assetId/{assetId}` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetId` | path | `integer (int64)` | Yes | | | `Accept-Encoding` | header | `string` | Yes | | | `Roblox-Place-Id` | header | `integer (int64)` | Yes | | | `AssetType` | header | `string` | Yes | | | `Accept` | header | `string` | Yes | | | `AssetFormat` | header | `string` | Yes | | | `Roblox-AssetFormat` | header | `string` | Yes | | | `skipSigningScripts` | query | `boolean` | No | | | `clientInsert` | query | `integer (int32)` | No | | | `scriptinsert` | query | `integer (int32)` | No | | | `modulePlaceId` | query | `integer (int64)` | No | | | `serverplaceid` | query | `integer (int64)` | No | | | `expectedAssetType` | query | `string` | No | | | `doNotFallbackToBaselineRepresentation` | query | `boolean` | No | | | `contentRepresentationPriorityList` | query | `string` | No | | | `accessContext` | query | `string` | No | | | `usageContext` | query | `integer (int32)` | No | | **Responses:** - `200`: OK → `Roblox.Web.Assets.AssetResponseItemV2` **Response fields** (`Roblox.Web.Assets.AssetResponseItemV2`) See [Roblox.Web.Assets.AssetResponseItemV2](#roblox-web-assets-assetresponseitemv2) in Models. **Response example:** ```json { "locations": [ { "assetFormat": "...", "location": "...", "assetMetadatas": "..." } ], "errors": [ { "Code": "...", "Message": "...", "CustomErrorCode": "..." } ], "requestId": "string", "isArchived": false, "assetTypeId": 0, "contentRepresentationSpecifier": { "format": "string", "majorVersion": "string", "fidelity": "string", "skipGenerationIfNotExist": false } } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://assetdelivery.roblox.com/v2/assetId/{ASSETID}" ``` ### GET `/v2/assetId/{assetId}/version/{versionNumber}` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetId` | path | `integer (int64)` | Yes | | | `versionNumber` | path | `integer (int32)` | Yes | | | `Accept-Encoding` | header | `string` | Yes | | | `Roblox-Place-Id` | header | `integer (int64)` | Yes | | | `AssetType` | header | `string` | Yes | | | `Accept` | header | `string` | Yes | | | `AssetFormat` | header | `string` | Yes | | | `Roblox-AssetFormat` | header | `string` | Yes | | | `skipSigningScripts` | query | `boolean` | No | | | `clientInsert` | query | `integer (int32)` | No | | | `scriptinsert` | query | `integer (int32)` | No | | | `modulePlaceId` | query | `integer (int64)` | No | | | `serverplaceid` | query | `integer (int64)` | No | | | `expectedAssetType` | query | `string` | No | | | `doNotFallbackToBaselineRepresentation` | query | `boolean` | No | | | `contentRepresentationPriorityList` | query | `string` | No | | | `accessContext` | query | `string` | No | | | `usageContext` | query | `integer (int32)` | No | | **Responses:** - `200`: OK → `Roblox.Web.Assets.AssetResponseItemV2` **Response fields** (`Roblox.Web.Assets.AssetResponseItemV2`) See [Roblox.Web.Assets.AssetResponseItemV2](#roblox-web-assets-assetresponseitemv2) in Models. **Response example:** ```json { "locations": [ { "assetFormat": "...", "location": "...", "assetMetadatas": "..." } ], "errors": [ { "Code": "...", "Message": "...", "CustomErrorCode": "..." } ], "requestId": "string", "isArchived": false, "assetTypeId": 0, "contentRepresentationSpecifier": { "format": "string", "majorVersion": "string", "fidelity": "string", "skipGenerationIfNotExist": false } } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://assetdelivery.roblox.com/v2/assetId/{ASSETID}/version/{VERSIONNUMBER}" ``` ### GET `/v2/marAssetHash/{marAssetHash}/marCheckSum/{marCheckSum}` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `marAssetHash` | path | `string` | Yes | | | `marCheckSum` | path | `string` | Yes | | | `Accept-Encoding` | header | `string` | Yes | | | `Roblox-Place-Id` | header | `integer (int64)` | Yes | | | `AssetType` | header | `string` | Yes | | | `Accept` | header | `string` | Yes | | | `AssetFormat` | header | `string` | Yes | | | `Roblox-AssetFormat` | header | `string` | Yes | | | `skipSigningScripts` | query | `boolean` | No | | | `clientInsert` | query | `integer (int32)` | No | | | `scriptinsert` | query | `integer (int32)` | No | | | `modulePlaceId` | query | `integer (int64)` | No | | | `serverplaceid` | query | `integer (int64)` | No | | | `expectedAssetType` | query | `string` | No | | **Responses:** - `200`: OK → `Roblox.Web.Assets.AssetResponseItemV2` **Response fields** (`Roblox.Web.Assets.AssetResponseItemV2`) See [Roblox.Web.Assets.AssetResponseItemV2](#roblox-web-assets-assetresponseitemv2) in Models. **Response example:** ```json { "locations": [ { "assetFormat": "...", "location": "...", "assetMetadatas": "..." } ], "errors": [ { "Code": "...", "Message": "...", "CustomErrorCode": "..." } ], "requestId": "string", "isArchived": false, "assetTypeId": 0, "contentRepresentationSpecifier": { "format": "string", "majorVersion": "string", "fidelity": "string", "skipGenerationIfNotExist": false } } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://assetdelivery.roblox.com/v2/marAssetHash/{MARASSETHASH}/marCheckSum/{MARCHECKSUM}" ``` ### POST `/v2/assets/batch` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `Roblox-Place-Id` | header | `integer (int64)` | Yes | | | `Accept` | header | `string` | Yes | | | `Roblox-Browser-Asset-Request` | header | `string` | Yes | | **Request Body:** `application/json` — Type: `Roblox.Web.Assets.BatchAssetRequestItem[]` See [Roblox.Web.Assets.BatchAssetRequestItem](#roblox-web-assets-batchassetrequestitem) in Models. **Responses:** - `200`: OK → `Roblox.Web.Assets.AssetResponseItemV2[]` **Response fields** (`Roblox.Web.Assets.AssetResponseItemV2[]`) See [Roblox.Web.Assets.AssetResponseItemV2](#roblox-web-assets-assetresponseitemv2) in Models. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://assetdelivery.roblox.com/v2/assets/batch" \ -H "Content-Type: application/json" \ -d '[ { "assetName": "string", "assetType": "string", "clientInsert": false, "placeId": 0, "requestId": "string", "scriptInsert": false } ]' ``` ## Models ### Roblox.AssetDelivery.Api.AssetMetadata An asset piece of metadata. | Property | Type | Required | Description | |----------|------|----------|-------------| | `metadataType` | `1` | No | Asset metadata type. ['UncompressedSize' = 1] | | `value` | `string` | No | | ### Roblox.Web.Assets.AssetContentRepresentationSpecifier | Property | Type | Required | Description | |----------|------|----------|-------------| | `format` | `string` | No | | | `majorVersion` | `string` | No | | | `fidelity` | `string` | No | | | `skipGenerationIfNotExist` | `boolean` | No | | ### Roblox.Web.Assets.AssetFormatLocation | Property | Type | Required | Description | |----------|------|----------|-------------| | `assetFormat` | `string` | No | | | `location` | `string` | No | | | `assetMetadatas` | `Roblox.AssetDelivery.Api.AssetMetadata[]` | No | | ### Roblox.Web.Assets.AssetResponseItemV2 | Property | Type | Required | Description | |----------|------|----------|-------------| | `locations` | `Roblox.Web.Assets.AssetFormatLocation[]` | No | | | `errors` | `Roblox.Web.Assets.IAssetItemError[]` | No | | | `requestId` | `string` | No | | | `isArchived` | `boolean` | No | Whether the asset has been archived. | | `assetTypeId` | `integer` | No | Asset Type. | | `contentRepresentationSpecifier` | `Roblox.Web.Assets.AssetContentRepresentationSpecifier` | No | | | `isRecordable` | `boolean` | No | Whether the asset is recordable in screen recordings. | ### Roblox.Web.Assets.BatchAssetRequestItem | Property | Type | Required | Description | |----------|------|----------|-------------| | `assetName` | `string` | No | | | `assetType` | `string` | No | | | `clientInsert` | `boolean` | No | | | `placeId` | `integer` | No | | | `requestId` | `string` | No | | | `scriptInsert` | `boolean` | No | | | `serverPlaceId` | `integer` | No | | | `universeId` | `integer` | No | | | `accept` | `string` | No | | | `encoding` | `string` | No | | | `hash` | `string` | No | | | `userAssetId` | `integer` | No | | | `assetId` | `integer` | No | | | `version` | `integer` | No | | | `assetVersionId` | `integer` | No | | | `modulePlaceId` | `integer` | No | | | `assetFormat` | `string` | No | | | `roblox-assetFormat` | `string` | No | | | `assetResolutionMode` | `string` | No | | | `accessContext` | `string` | No | | | `usageContext` | `integer` | No | | | `contentRepresentationPriorityList` | `string` | No | | | `doNotFallbackToBaselineRepresentation` | `boolean` | No | | ### Roblox.Web.Assets.IAssetItemError | Property | Type | Required | Description | |----------|------|----------|-------------| | `Code` | `integer` | No | | | `Message` | `string` | No | | | `CustomErrorCode` | `integer enum (24 values)` | No | Custom error code for Roblox.Web.Assets.IAssetItemError ['UnknownError' = 0, 'NoPermissionToAsset' = 1, 'AssetPermissionCheckFailed' = 2, 'NotAuthorizedForAgeRecommendation' = 3, 'AgeRecommendationCheckFailed' = 4, 'InvalidPlaceRequestFromGameServer' = 5, 'BlockedAssetTypeRequestedFromInsertService' = 6, 'BlockedAssetTypeRequestedFromGameServer' = 7, 'AssetTypeMismatch' = 8, 'MissingAssetTypeInRequestHeader' = 9, 'AssetNotTrustedForPlace' = 10, 'NoAuthentication' = 11, 'AssetContentRepresentationBlockedDueToModeration' = 12, 'AssetNotFound' = 13, 'AssetVersionNotFound' = 14, 'AssetContentRepresentationNotFound' = 15, 'BlockedByAgeGeoRestriction' = 16, 'BlockedAssetTypeRequestedFromNonGameServer' = 17, 'AssetPendingReview' = 18, 'NotApprovedForRequestor' = 19, 'NotApprovedByContentCompliance' = 20, 'AssetContentRepresentationGenerating' = 21, 'AssetArchived' = 22, 'AssetUsageNotAllowed' = 23] Values: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 | --- name: "Authentication Api v1" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://auth.roblox.com" version: "v1" endpoints: 60 auth: [cookie] --- # Authentication Api v1 **API Version:** v1 **Base URL:** `https://auth.roblox.com` ## Endpoints ### GET `/v1/account-creation/metadata` Get metadata for adding auth methods. **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.AuthMethodsMetadataResponse` **Response fields** (`Roblox.Authentication.Api.Models.AuthMethodsMetadataResponse`) See [Roblox.Authentication.Api.Models.AuthMethodsMetadataResponse](#roblox-authentication-api-models-authmethodsmetadataresponse) in Models. **Response example:** ```json { "isEligibleForALSignup": false } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/account-creation/metadata" ``` ### GET `/v1/account/pin` Gets the account pin status. **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.AccountPinStatusResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Authentication.Api.Models.AccountPinStatusResponse`) See [Roblox.Authentication.Api.Models.AccountPinStatusResponse](#roblox-authentication-api-models-accountpinstatusresponse) in Models. **Response example:** ```json { "isEnabled": false, "unlockedUntil": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/account/pin" ``` ### POST `/v1/account/pin` Request to create the account pin. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.AccountPinRequest` See [Roblox.Authentication.Api.Models.AccountPinRequest](#roblox-authentication-api-models-accountpinrequest) in Models. **Request example:** ```json { "pin": "string", "reauthenticationToken": "string" } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiSuccessResponse` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Web.WebAPI.Models.ApiSuccessResponse`) See [Roblox.Web.WebAPI.Models.ApiSuccessResponse](#roblox-web-webapi-models-apisuccessresponse) in Models. **Response example:** ```json { "success": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/account/pin" \ -H "Content-Type: application/json" \ -d '{ "pin": "string", "reauthenticationToken": "string" }' ``` ### PATCH `/v1/account/pin` Request made to update the account pin on the account. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.AccountPinRequest` See [Roblox.Authentication.Api.Models.AccountPinRequest](#roblox-authentication-api-models-accountpinrequest) in Models. **Request example:** ```json { "pin": "string", "reauthenticationToken": "string" } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiSuccessResponse` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Web.WebAPI.Models.ApiSuccessResponse`) See [Roblox.Web.WebAPI.Models.ApiSuccessResponse](#roblox-web-webapi-models-apisuccessresponse) in Models. **Response example:** ```json { "success": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/account/pin" \ -H "Content-Type: application/json" \ -d '{ "pin": "string", "reauthenticationToken": "string" }' ``` ### DELETE `/v1/account/pin` Request for deletes the account pin from the account. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.AccountPinRequest` See [Roblox.Authentication.Api.Models.AccountPinRequest](#roblox-authentication-api-models-accountpinrequest) in Models. **Request example:** ```json { "pin": "string", "reauthenticationToken": "string" } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiSuccessResponse` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Web.WebAPI.Models.ApiSuccessResponse`) See [Roblox.Web.WebAPI.Models.ApiSuccessResponse](#roblox-web-webapi-models-apisuccessresponse) in Models. **Response example:** ```json { "success": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/account/pin" \ -H "Content-Type: application/json" \ -d '{ "pin": "string", "reauthenticationToken": "string" }' ``` ### GET `/v1/auth/metadata` Gets Auth meta data **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.AuthMetaDataResponse` **Response fields** (`Roblox.Authentication.Api.Models.AuthMetaDataResponse`) See [Roblox.Authentication.Api.Models.AuthMetaDataResponse](#roblox-authentication-api-models-authmetadataresponse) in Models. **Response example:** ```json { "cookieLawNoticeTimeout": 0 } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/auth/metadata" ``` ### GET `/v1/client-assertion` Creates a client assertion to be used when generating an auth ticket. **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.Response.GetClientAssertionResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Authentication.Api.Models.Response.GetClientAssertionResponse`) See [Roblox.Authentication.Api.Models.Response.GetClientAssertionResponse](#roblox-authentication-api-models-response-getclientassertionresponse) in Models. **Response example:** ```json { "clientAssertion": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/client-assertion" ``` ### GET `/v1/external/{identityProviderId}/sso/oauth/callback` Callback function that external identity provider calls post user authentication. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `identityProviderId` | path | `integer (int64)` | Yes | | | `code` | query | `string` | Yes | | | `state` | query | `string` | Yes | | **Responses:** - `302`: Redirect **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/external/{IDENTITYPROVIDERID}/sso/oauth/callback?code={VALUE}&state={VALUE}" ``` ### GET `/v1/external/{identityProviderId}/sso/oauth/init` Signs a user up for Roblox and links the account to the authenticated external provider ID. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `identityProviderId` | path | `integer (int64)` | Yes | | **Responses:** - `302`: Redirect **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/external/{IDENTITYPROVIDERID}/sso/oauth/init" ``` ### GET `/v1/metadata` Get the metadata **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.MetadataResponse` **Response fields** (`Roblox.Authentication.Api.Models.MetadataResponse`) See [Roblox.Authentication.Api.Models.MetadataResponse](#roblox-authentication-api-models-metadataresponse) in Models. **Response example:** ```json { "isUpdateUsernameEnabled": false, "ftuxAvatarAssetMap": "string", "IsEmailUpsellAtLogoutEnabled": false, "ShouldFetchEmailUpsellIXPValuesAtLogout": false, "IsAccountRecoveryPromptEnabled": false, "IsContactMethodRequiredAtSignup": false } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/metadata" ``` ### GET `/v1/passkey/su-eligibility` Checks whether the authenticated user is eligible for silent passkey upgrade. Route and response are intentionally obfuscated ("su-eligibility" = "silent-upgrade-eligibility"). **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.Response.SilentUpgradeEligibilityResponse` - `401`: 0: Authorization has been denied for this request. 0: An unknown error occurred with the request. - `503`: 2: Feature disabled. **Response fields** (`Roblox.Authentication.Api.Models.Response.SilentUpgradeEligibilityResponse`) See [Roblox.Authentication.Api.Models.Response.SilentUpgradeEligibilityResponse](#roblox-authentication-api-models-response-silentupgradeeligibilityresponse) in Models. **Response example:** ```json { "suEligibility": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/passkey/su-eligibility" ``` ### GET `/v1/passwords/validate` Endpoint for checking if a password is valid. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `Username` | query | `string` | Yes | | | `Password` | query | `string` | Yes | | **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.PasswordValidationResponse` - `400`: 1: Valid Username and Password are required. Please try again. **Response fields** (`Roblox.Authentication.Api.Models.PasswordValidationResponse`) See [Roblox.Authentication.Api.Models.PasswordValidationResponse](#roblox-authentication-api-models-passwordvalidationresponse) in Models. **Response example:** ```json { "code": 0, "message": "string" } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/passwords/validate?Username={VALUE}&Password={VALUE}" ``` ### POST `/v1/passwords/validate` Endpoint for checking if a password is valid. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.PasswordValidationModel` See [Roblox.Authentication.Api.Models.PasswordValidationModel](#roblox-authentication-api-models-passwordvalidationmodel) in Models. **Request example:** ```json { "username": "string", "password": "string" } ``` **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.PasswordValidationResponse` - `400`: 1: Valid Username and Password are required. Please try again. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Authentication.Api.Models.PasswordValidationResponse`) See [Roblox.Authentication.Api.Models.PasswordValidationResponse](#roblox-authentication-api-models-passwordvalidationresponse) in Models. **Response example:** ```json { "code": 0, "message": "string" } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/passwords/validate" \ -H "Content-Type: application/json" \ -d '{ "username": "string", "password": "string" }' ``` ### GET `/v1/recovery/metadata` Get metadata for forgot endpoints **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.RecoveryMetadataResponse` - `503`: 7: The Roblox WeChat API is currently unavailable. **Response fields** (`Roblox.Authentication.Api.Models.RecoveryMetadataResponse`) See [Roblox.Authentication.Api.Models.RecoveryMetadataResponse](#roblox-authentication-api-models-recoverymetadataresponse) in Models. **Response example:** ```json { "isOnPhone": false, "codeLength": 0, "isPhoneFeatureEnabledForUsername": false, "isPhoneFeatureEnabledForPassword": false, "isBedev2CaptchaEnabledForPasswordReset": false, "isUsernameRecoveryDeprecated": false } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/recovery/metadata" ``` ### GET `/v1/revert/account` Get Revert Account ticket info **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `ticket` | query | `string` | Yes | Ticket Guid to revert account. | **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.RevertAccountInfoResponse` - `400`: 2: The account revert ticket is not valid - `403`: 13: Revert links are disabled for users in the Enhanced Protection Program. - `503`: 1: This feature is disabled **Response fields** (`Roblox.Authentication.Api.Models.RevertAccountInfoResponse`) See [Roblox.Authentication.Api.Models.RevertAccountInfoResponse](#roblox-authentication-api-models-revertaccountinforesponse) in Models. **Response example:** ```json { "isTwoStepVerificationEnabled": false, "isEmailVerified": false, "isEmailChanged": false, "isPhoneVerified": false, "userId": 0, "username": "string" } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/revert/account?ticket={VALUE}" ``` ### POST `/v1/revert/account` Submit Revert Account Request **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.RevertAccountSubmitRequest` See [Roblox.Authentication.Api.Models.RevertAccountSubmitRequest](#roblox-authentication-api-models-revertaccountsubmitrequest) in Models. **Request example:** ```json { "UserId": 0, "NewPassword": "string", "NewPasswordRepeated": "string", "Ticket": "string", "TwoStepVerificationChallengeId": "string", "TwoStepVerificationToken": "string" } ``` **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.LoginResponse` - `400`: 2: The account revert ticket is not valid 3: Password is not valid 4: Passwords do not match 5: Password cannot be used 8: The account security ticket is expired. - `403`: 0: Token Validation Failed - `503`: 0: Unknown 1: This feature is disabled **Response fields** (`Roblox.Authentication.Api.Models.LoginResponse`) See [Roblox.Authentication.Api.Models.LoginResponse](#roblox-authentication-api-models-loginresponse) in Models. **Response example:** ```json { "user": { "id": 0, "name": "string", "displayName": "string" }, "twoStepVerificationData": { "mediaType": 0, "ticket": "string" }, "identityVerificationLoginTicket": "string", "isBanned": false, "accountBlob": "string", "shouldUpdateEmail": false } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/revert/account" \ -H "Content-Type: application/json" \ -d '{ "UserId": 0, "NewPassword": "string", "NewPasswordRepeated": "string", "Ticket": "string", "TwoStepVerificationChallengeId": "string", "TwoStepVerificationToken": "string" }' ``` ### GET `/v1/social/connected-providers` Get social network user information if the given social auth method is connected to current user. **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.SocialProvidersResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Authentication.Api.Models.SocialProvidersResponse`) See [Roblox.Authentication.Api.Models.SocialProvidersResponse](#roblox-authentication-api-models-socialprovidersresponse) in Models. **Response example:** ```json { "providers": [ { "provider": "...", "identifier": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/social/connected-providers" ``` ### GET `/v1/username/change/price` Get the current price for a username change **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.UsernameChangePriceResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Authentication.Api.Models.UsernameChangePriceResponse`) See [Roblox.Authentication.Api.Models.UsernameChangePriceResponse](#roblox-authentication-api-models-usernamechangepriceresponse) in Models. **Response example:** ```json { "priceInRobux": 0, "basePriceInRobux": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/username/change/price" ``` ### GET `/v1/usernames` Gets a list of existing usernames on Roblox based on the query parameters This endpoint can be expanded in the future to include other query parameters such as "startsWith" **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `username` | query | `string` | Yes | The username | **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.UsernamesResponse` **Response fields** (`Roblox.Authentication.Api.Models.UsernamesResponse`) See [Roblox.Authentication.Api.Models.UsernamesResponse](#roblox-authentication-api-models-usernamesresponse) in Models. **Response example:** ```json { "usernames": [ "string" ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/usernames?username={VALUE}" ``` ### GET `/v1/usernames/validate` Checks if a username is valid. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `Username` | query | `string` | Yes | | | `Birthday` | query | `string (date-time)` | Yes | | | `Context` | query | `integer (int32)` | Yes | Valid values: `0`, `1`, `2` | **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.UsernameValidationResponse` - `400`: 1: A valid username is required. 2: A valid birthday or authenticated user is required. **Response fields** (`Roblox.Authentication.Api.Models.UsernameValidationResponse`) See [Roblox.Authentication.Api.Models.UsernameValidationResponse](#roblox-authentication-api-models-usernamevalidationresponse) in Models. **Response example:** ```json { "code": 0, "message": "string" } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/usernames/validate?Username={VALUE}&Birthday={VALUE}&Context={VALUE}" ``` ### POST `/v1/usernames/validate` Checks if a username is valid. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.UsernameValidationRequest` See [Roblox.Authentication.Api.Models.UsernameValidationRequest](#roblox-authentication-api-models-usernamevalidationrequest) in Models. **Request example:** ```json { "username": "string", "birthday": "2024-01-01T00:00:00Z", "context": 0 } ``` **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.UsernameValidationResponse` - `400`: 1: A valid username is required. 2: A valid birthday or authenticated user is required. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Authentication.Api.Models.UsernameValidationResponse`) See [Roblox.Authentication.Api.Models.UsernameValidationResponse](#roblox-authentication-api-models-usernamevalidationresponse) in Models. **Response example:** ```json { "code": 0, "message": "string" } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/usernames/validate" \ -H "Content-Type: application/json" \ -d '{ "username": "string", "birthday": "2024-01-01T00:00:00Z", "context": 0 }' ``` ### GET `/v1/validators/email` Tries to check if an email is valid **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `Email` | query | `string` | Yes | | **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.EmailValidationResponse` **Response fields** (`Roblox.Authentication.Api.Models.EmailValidationResponse`) See [Roblox.Authentication.Api.Models.EmailValidationResponse](#roblox-authentication-api-models-emailvalidationresponse) in Models. **Response example:** ```json { "isEmailValid": false } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/validators/email?Email={VALUE}" ``` ### GET `/v1/validators/recommendedUsernameFromDisplayName` Validates the given display name, and if valid, will convert it to a valid username and return suggested username(s) if available. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `DisplayName` | query | `string` | Yes | | | `BirthDay` | query | `string (date-time)` | Yes | | **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.RecommendedUsernameResponse` **Response fields** (`Roblox.Authentication.Api.Models.RecommendedUsernameResponse`) See [Roblox.Authentication.Api.Models.RecommendedUsernameResponse](#roblox-authentication-api-models-recommendedusernameresponse) in Models. **Response example:** ```json { "didGenerateNewUsername": false, "suggestedUsernames": [ "string" ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/validators/recommendedUsernameFromDisplayName?DisplayName={VALUE}&BirthDay={VALUE}" ``` ### POST `/v1/validators/recommendedUsernameFromDisplayName` Validates the given display name, and if valid, will convert it to a valid username and return suggested username(s) if available. This is a POST request and explicitly does not receive the parameter values from the query **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.RecommendedUsernameFromDisplayNameRequest` See [Roblox.Authentication.Api.Models.RecommendedUsernameFromDisplayNameRequest](#roblox-authentication-api-models-recommendedusernamefromdisplaynamerequest) in Models. **Request example:** ```json { "displayName": "string", "birthday": "2024-01-01T00:00:00Z" } ``` **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.RecommendedUsernameResponse` - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Authentication.Api.Models.RecommendedUsernameResponse`) See [Roblox.Authentication.Api.Models.RecommendedUsernameResponse](#roblox-authentication-api-models-recommendedusernameresponse) in Models. **Response example:** ```json { "didGenerateNewUsername": false, "suggestedUsernames": [ "string" ] } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/validators/recommendedUsernameFromDisplayName" \ -H "Content-Type: application/json" \ -d '{ "displayName": "string", "birthday": "2024-01-01T00:00:00Z" }' ``` ### GET `/v1/validators/username` Tries to get a valid username if the current username is taken **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `Username` | query | `string` | Yes | | | `BirthDay` | query | `string (date-time)` | Yes | | **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.RecommendedUsernameResponse` **Response fields** (`Roblox.Authentication.Api.Models.RecommendedUsernameResponse`) See [Roblox.Authentication.Api.Models.RecommendedUsernameResponse](#roblox-authentication-api-models-recommendedusernameresponse) in Models. **Response example:** ```json { "didGenerateNewUsername": false, "suggestedUsernames": [ "string" ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/validators/username?Username={VALUE}&BirthDay={VALUE}" ``` ### POST `/v1/validators/username` Tries to get a valid username if the current username is taken This is a POST request and explicitly does not receive the parameter values from the query **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.RecommendedUsernameRequest` See [Roblox.Authentication.Api.Models.RecommendedUsernameRequest](#roblox-authentication-api-models-recommendedusernamerequest) in Models. **Request example:** ```json { "username": "string", "birthday": "2024-01-01T00:00:00Z" } ``` **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.RecommendedUsernameResponse` - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Authentication.Api.Models.RecommendedUsernameResponse`) See [Roblox.Authentication.Api.Models.RecommendedUsernameResponse](#roblox-authentication-api-models-recommendedusernameresponse) in Models. **Response example:** ```json { "didGenerateNewUsername": false, "suggestedUsernames": [ "string" ] } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/validators/username" \ -H "Content-Type: application/json" \ -d '{ "username": "string", "birthday": "2024-01-01T00:00:00Z" }' ``` ### GET `/v1/xbox/connection` Check if the current user has an Xbox connected. Also returns the gamertag of the Xbox account if connected. **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.XboxConnectionModel` - `401`: 0: Authorization has been denied for this request. - `403`: **Response fields** (`Roblox.Authentication.Api.Models.XboxConnectionModel`) See [Roblox.Authentication.Api.Models.XboxConnectionModel](#roblox-authentication-api-models-xboxconnectionmodel) in Models. **Response example:** ```json { "hasConnectedXboxAccount": false, "gamertag": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/xbox/connection" ``` ### GET `/v1/xbox/get-login-consecutive-days` Get the consecutive days the xbox user has been logged in. **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.XboxLoginConsecutiveDaysResponse` - `400`: 36: Invalid Xbox Live Account - `401`: 0: Authorization has been denied for this request. - `403`: **Response fields** (`Roblox.Authentication.Api.Models.XboxLoginConsecutiveDaysResponse`) See [Roblox.Authentication.Api.Models.XboxLoginConsecutiveDaysResponse](#roblox-authentication-api-models-xboxloginconsecutivedaysresponse) in Models. **Response example:** ```json { "count": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/xbox/get-login-consecutive-days" ``` ### POST `/v1/account/pin/lock` Request to locks the account which has an account pin enabled. **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiSuccessResponse` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Web.WebAPI.Models.ApiSuccessResponse`) See [Roblox.Web.WebAPI.Models.ApiSuccessResponse](#roblox-web-webapi-models-apisuccessresponse) in Models. **Response example:** ```json { "success": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/account/pin/lock" ``` ### POST `/v1/account/pin/unlock` Requests to unlock the account pin. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.AccountPinRequest` See [Roblox.Authentication.Api.Models.AccountPinRequest](#roblox-authentication-api-models-accountpinrequest) in Models. **Request example:** ```json { "pin": "string", "reauthenticationToken": "string" } ``` **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.AccountPinResponse` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Authentication.Api.Models.AccountPinResponse`) See [Roblox.Authentication.Api.Models.AccountPinResponse](#roblox-authentication-api-models-accountpinresponse) in Models. **Response example:** ```json { "unlockedUntil": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/account/pin/unlock" \ -H "Content-Type: application/json" \ -d '{ "pin": "string", "reauthenticationToken": "string" }' ``` ### POST `/v1/external/access` Signs a user up for Roblox and links the account to the authenticated external provider ID. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.Request.ExternalAccessRequest` See [Roblox.Authentication.Api.Models.Request.ExternalAccessRequest](#roblox-authentication-api-models-request-externalaccessrequest) in Models. **Request example:** ```json { "authenticationProof": "string", "identityProviderPlatformType": 0, "additionalInfoPayload": "..." } ``` **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.Response.ExternalIdentityGateway.ExternalIdentityAccessResponse` **Response fields** (`Roblox.Authentication.Api.Models.Response.ExternalIdentityGateway.ExternalIdentityAccessResponse`) See [Roblox.Authentication.Api.Models.Response.ExternalIdentityGateway.ExternalIdentityAccessResponse](#roblox-authentication-api-models-response-externalidentitygateway-externalidentityaccessresponse) in Models. **Response example:** ```json { "placeId": 0, "isolationContext": "string", "launchData": "string" } ``` **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/external/access" \ -H "Content-Type: application/json" \ -d '{ "authenticationProof": "string", "identityProviderPlatformType": 0, "additionalInfoPayload": "..." }' ``` ### POST `/v1/external/login` Logs in a user to Roblox based on the user's authenticated external provider session **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.Request.ExternalLoginRequest` See [Roblox.Authentication.Api.Models.Request.ExternalLoginRequest](#roblox-authentication-api-models-request-externalloginrequest) in Models. **Request example:** ```json { "identityProvider": 0, "additionalData": "...", "authenticationProof": "string" } ``` **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.Response.ExternalIdentityGateway.ExternalLoginResponse` - `400`: 30: Platform is not supported for SSO login. - `401`: 31: No linked account found for SSO login. - `403`: 0: Token Validation Failed - `500`: 0: An unexpected error occurred. - `501`: 0: An unexpected error occurred. **Response fields** (`Roblox.Authentication.Api.Models.Response.ExternalIdentityGateway.ExternalLoginResponse`) See [Roblox.Authentication.Api.Models.Response.ExternalIdentityGateway.ExternalLoginResponse](#roblox-authentication-api-models-response-externalidentitygateway-externalloginresponse) in Models. **Response example:** ```json { "success": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/external/login" \ -H "Content-Type: application/json" \ -d '{ "identityProvider": 0, "additionalData": "...", "authenticationProof": "string" }' ``` ### POST `/v1/external/loginAndLink` Deprecated endpoint Logins in a user to Roblox, then links the Roblox account to the external provider ID **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.Request.ExternalLoginAndLinkRequest` See [Roblox.Authentication.Api.Models.Request.ExternalLoginAndLinkRequest](#roblox-authentication-api-models-request-externalloginandlinkrequest) in Models. **Request example:** ```json { "ctype": 0, "cvalue": "string", "password": "string", "authenticationProof": "string", "IdentityProviderPlatformType": 0, "additionalInfoPayload": "..." } ``` **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.LoginResponse` - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Authentication.Api.Models.LoginResponse`) See [Roblox.Authentication.Api.Models.LoginResponse](#roblox-authentication-api-models-loginresponse) in Models. **Response example:** ```json { "user": { "id": 0, "name": "string", "displayName": "string" }, "twoStepVerificationData": { "mediaType": 0, "ticket": "string" }, "identityVerificationLoginTicket": "string", "isBanned": false, "accountBlob": "string", "shouldUpdateEmail": false } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/external/loginAndLink" \ -H "Content-Type: application/json" \ -d '{ "ctype": 0, "cvalue": "string", "password": "string", "authenticationProof": "string", "IdentityProviderPlatformType": 0, "additionalInfoPayload": "..." }' ``` ### POST `/v1/external/signup` Signs a user up for Roblox and links the account to the authenticated external provider ID **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.Request.ExternalSignupRequest` See [Roblox.Authentication.Api.Models.Request.ExternalSignupRequest](#roblox-authentication-api-models-request-externalsignuprequest) in Models. **Request example:** ```json { "username": "string", "password": "string", "birthday": "2024-01-01T00:00:00Z", "locale": "string", "authenticationProof": "string", "IdentityProviderPlatformType": 0 } ``` **Responses:** - `200`: OK - `403`: 0: Token Validation Failed **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/external/signup" \ -H "Content-Type: application/json" \ -d '{ "username": "string", "password": "string", "birthday": "2024-01-01T00:00:00Z", "locale": "string", "authenticationProof": "string", "IdentityProviderPlatformType": 0 }' ``` ### POST `/v1/external/unlink` Unlink the logged in Roblox account from the current external provider ID **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.Request.ExternalUnlinkRequest` See [Roblox.Authentication.Api.Models.Request.ExternalUnlinkRequest](#roblox-authentication-api-models-request-externalunlinkrequest) in Models. **Request example:** ```json { "IdentityProviderPlatformType": 0, "additionalInfoPayload": "..." } ``` **Responses:** - `200`: OK - `403`: 0: Token Validation Failed **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/external/unlink" \ -H "Content-Type: application/json" \ -d '{ "IdentityProviderPlatformType": 0, "additionalInfoPayload": "..." }' ``` ### POST `/v1/identity-verification/login` Endpoint for login with identity verification **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.Request.IdentityVerificationLoginRequest` See [Roblox.Authentication.Api.Models.Request.IdentityVerificationLoginRequest](#roblox-authentication-api-models-request-identityverificationloginrequest) in Models. **Request example:** ```json { "loginTicket": "string", "resultToken": "string" } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `403`: 0: Token Validation Failed 1: Invalid login ticket. 2: Invalid result token. 3: Invalid user. 4: Authentication failure. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/identity-verification/login" \ -H "Content-Type: application/json" \ -d '{ "loginTicket": "string", "resultToken": "string" }' ``` ### POST `/v1/identity/initialize-login` Initiates identifier-first login flow by returning a list of login methods for user(s). **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.Request.InitializeLoginRequest` See [Roblox.Authentication.Api.Models.Request.InitializeLoginRequest](#roblox-authentication-api-models-request-initializeloginrequest) in Models. **Request example:** ```json { "ctype": 0, "cvalue": "string", "captchaId": "string", "captchaToken": "string", "captchaProvider": "string", "challengeId": "string" } ``` **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.Response.InitializeLoginResponse` - `400`: 3: User identifier and type are required. - `403`: 0: Token Validation Failed 2: Invalid user identifier. 4: No login methods available. Please use account recovery. - `500`: 0: An unknown error occurred. - `503`: 1: This feature is disabled. **Response fields** (`Roblox.Authentication.Api.Models.Response.InitializeLoginResponse`) See [Roblox.Authentication.Api.Models.Response.InitializeLoginResponse](#roblox-authentication-api-models-response-initializeloginresponse) in Models. **Response example:** ```json { "loginMethods": [ { "method": "...", "priority": "..." } ] } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/identity/initialize-login" \ -H "Content-Type: application/json" \ -d '{ "ctype": 0, "cvalue": "string", "captchaId": "string", "captchaToken": "string", "captchaProvider": "string", "challengeId": "string" }' ``` ### POST `/v1/login` Authenticates a user. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.LoginRequest` See [Roblox.Authentication.Api.Models.LoginRequest](#roblox-authentication-api-models-loginrequest) in Models. **Request example:** ```json { "ctype": 0, "cvalue": "string", "password": "string", "userId": 0, "securityQuestionSessionId": "string", "securityQuestionRedemptionToken": "string" } ``` **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.LoginResponse` - `400`: 0: An unexpected error occurred. 3: Username and Password are required. Please try again. 8: Login with received credential type is not supported. - `403`: 0: Token Validation Failed 1: Incorrect username or password. Please try again. 2: You must pass the robot test before logging in. 4: Account has been locked. Please request a password reset. 5: Unable to login. Please use Social Network sign on. 6: Account issue. Please contact Support. 9: Unable to login with provided credentials. Default login is required. 10: Received credentials are unverified. 12: Existing login session found. Please log out first. 14: The account is unable to log in. Please log in to the LuoBu app. 15: Too many attempts. Please wait a bit. 27: The account is unable to login. Please log in with the VNG app. - `429`: 7: Too many attempts. Please wait a bit. - `503`: 11: Service unavailable. Please try again. **Response fields** (`Roblox.Authentication.Api.Models.LoginResponse`) See [Roblox.Authentication.Api.Models.LoginResponse](#roblox-authentication-api-models-loginresponse) in Models. **Response example:** ```json { "user": { "id": 0, "name": "string", "displayName": "string" }, "twoStepVerificationData": { "mediaType": 0, "ticket": "string" }, "identityVerificationLoginTicket": "string", "isBanned": false, "accountBlob": "string", "shouldUpdateEmail": false } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/login" \ -H "Content-Type: application/json" \ -d '{ "ctype": 0, "cvalue": "string", "password": "string", "userId": 0, "securityQuestionSessionId": "string", "securityQuestionRedemptionToken": "string" }' ``` ### POST `/v1/login/linked` Endpoint for logging in a user, specifically for linked authentication on PCGDK **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.LoginRequest` See [Roblox.Authentication.Api.Models.LoginRequest](#roblox-authentication-api-models-loginrequest) in Models. **Request example:** ```json { "ctype": 0, "cvalue": "string", "password": "string", "userId": 0, "securityQuestionSessionId": "string", "securityQuestionRedemptionToken": "string" } ``` **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.LoginResponse` - `400`: 0: An unexpected error occurred. 3: Username and Password are required. Please try again. 8: Login with received credential type is not supported. - `403`: 0: Token Validation Failed 1: Incorrect username or password. Please try again. 2: You must pass the robot test before logging in. 4: Account has been locked. Please request a password reset. 5: Unable to login. Please use Social Network sign on. 6: Account issue. Please contact Support. 9: Unable to login with provided credentials. Default login is required. 10: Received credentials are unverified. 12: Existing login session found. Please log out first. 14: The account is unable to log in. Please log in to the LuoBu app. 15: Too many attempts. Please wait a bit. 27: The account is unable to login. Please log in with the VNG app. - `429`: 7: Too many attempts. Please wait a bit. - `503`: 11: Service unavailable. Please try again. **Response fields** (`Roblox.Authentication.Api.Models.LoginResponse`) See [Roblox.Authentication.Api.Models.LoginResponse](#roblox-authentication-api-models-loginresponse) in Models. **Response example:** ```json { "user": { "id": 0, "name": "string", "displayName": "string" }, "twoStepVerificationData": { "mediaType": 0, "ticket": "string" }, "identityVerificationLoginTicket": "string", "isBanned": false, "accountBlob": "string", "shouldUpdateEmail": false } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/login/linked" \ -H "Content-Type: application/json" \ -d '{ "ctype": 0, "cvalue": "string", "password": "string", "userId": 0, "securityQuestionSessionId": "string", "securityQuestionRedemptionToken": "string" }' ``` ### POST `/v1/logout` Destroys the current authentication session. **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/logout" ``` ### POST `/v1/logoutfromallsessionsandreauthenticate` Logs out user from all other sessions. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.Request.LogoutFromAllSessionsAndReauthenticateRequest` See [Roblox.Authentication.Api.Models.Request.LogoutFromAllSessionsAndReauthenticateRequest](#roblox-authentication-api-models-request-logoutfromallsessionsandreauthenticaterequest) in Models. **Request example:** ```json { "SecureAuthenticationIntent": { "clientPublicKey": "string", "clientEpochTimestamp": 0, "saiSignature": "string", "serverNonce": "string" } } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/logoutfromallsessionsandreauthenticate" \ -H "Content-Type: application/json" \ -d '{ "SecureAuthenticationIntent": { "clientPublicKey": "string", "clientEpochTimestamp": 0, "saiSignature": "string", "serverNonce": "string" } }' ``` ### POST `/v1/passkey/DeleteCredentialBatch` Disables a batch of credentials for the specified user. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.Request.DeletePasskeysRequest` See [Roblox.Authentication.Api.Models.Request.DeletePasskeysRequest](#roblox-authentication-api-models-request-deletepasskeysrequest) in Models. **Request example:** ```json { "credentialNicknames": [ "string" ] } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `401`: 0: Authorization has been denied for this request. 0: An unknown error occurred with the request. - `403`: 0: Token Validation Failed - `503`: 2: Feature disabled. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/passkey/DeleteCredentialBatch" \ -H "Content-Type: application/json" \ -d '{ "credentialNicknames": [ "string" ] }' ``` ### POST `/v1/passkey/finish-ar-preauth-registration` Finishes account recovery pre-auth passkey registration by validating the recovery session, deactivating the user's password, and completing passkey registration. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.Request.FinishARPreAuthPasskeyRegistrationRequest` See [Roblox.Authentication.Api.Models.Request.FinishARPreAuthPasskeyRegistrationRequest](#roblox-authentication-api-models-request-finisharpreauthpasskeyregistrationrequest) in Models. **Request example:** ```json { "recoverySession": "string", "passkeySessionId": "string", "passkeyRegistrationResponse": "string", "userId": 0, "isPostRecovery": false, "source": "string" } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 0: An unknown error occurred with the request. - `401`: 0: An unknown error occurred with the request. - `403`: 0: Token Validation Failed 1: Reached limit of pass keys registered. - `500`: 0: An unknown error occurred with the request. - `503`: 2: Feature disabled. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/passkey/finish-ar-preauth-registration" \ -H "Content-Type: application/json" \ -d '{ "recoverySession": "string", "passkeySessionId": "string", "passkeyRegistrationResponse": "string", "userId": 0, "isPostRecovery": false, "source": "string" }' ``` ### POST `/v1/passkey/finish-preauth-registration` **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.Request.FinishPasskeyPreauthRegistrationRequest` See [Roblox.Authentication.Api.Models.Request.FinishPasskeyPreauthRegistrationRequest](#roblox-authentication-api-models-request-finishpasskeypreauthregistrationrequest) in Models. **Request example:** ```json { "sessionId": "string", "registrationResponse": "string", "source": "string" } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 0: An unknown error occurred with the request. - `401`: 0: Authorization has been denied for this request. 0: An unknown error occurred with the request. - `403`: 0: Token Validation Failed 1: Reached limit of pass keys registered. - `500`: 0: An unknown error occurred with the request. - `503`: 2: Feature disabled. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/passkey/finish-preauth-registration" \ -H "Content-Type: application/json" \ -d '{ "sessionId": "string", "registrationResponse": "string", "source": "string" }' ``` ### POST `/v1/passkey/FinishRegistration` Complete Passkey registration by providing credential creation options. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.Request.FinishPasskeyRegistrationRequest` See [Roblox.Authentication.Api.Models.Request.FinishPasskeyRegistrationRequest](#roblox-authentication-api-models-request-finishpasskeyregistrationrequest) in Models. **Request example:** ```json { "sessionId": "string", "credentialNickname": "string", "attestationResponse": "string", "source": "string" } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 0: An unknown error occurred with the request. 3: Invalid security key nickname. - `401`: 0: Authorization has been denied for this request. 0: An unknown error occurred with the request. - `403`: 0: Token Validation Failed 3: Invalid security key nickname. - `503`: 2: Feature disabled. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/passkey/FinishRegistration" \ -H "Content-Type: application/json" \ -d '{ "sessionId": "string", "credentialNickname": "string", "attestationResponse": "string", "source": "string" }' ``` ### POST `/v1/passkey/ListCredentials` List a user's registered passkeys. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.Request.ListPasskeysRequest` See [Roblox.Authentication.Api.Models.Request.ListPasskeysRequest](#roblox-authentication-api-models-request-listpasskeysrequest) in Models. **Request example:** ```json { "all": false } ``` **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.Response.ListPasskeyCredentialResponse` - `401`: 0: Authorization has been denied for this request. 0: An unknown error occurred with the request. - `403`: 0: Token Validation Failed - `503`: 2: Feature disabled. **Response fields** (`Roblox.Authentication.Api.Models.Response.ListPasskeyCredentialResponse`) See [Roblox.Authentication.Api.Models.Response.ListPasskeyCredentialResponse](#roblox-authentication-api-models-response-listpasskeycredentialresponse) in Models. **Response example:** ```json { "credentials": [ { "nickname": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/passkey/ListCredentials" \ -H "Content-Type: application/json" \ -d '{ "all": false }' ``` ### POST `/v1/passkey/start-authentication-by-user` Initializes passkey authentication for the user(s) corresponding to the identifier provided. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.Request.StartAuthenticationByUserRequest` See [Roblox.Authentication.Api.Models.Request.StartAuthenticationByUserRequest](#roblox-authentication-api-models-request-startauthenticationbyuserrequest) in Models. **Request example:** ```json { "ctype": 0, "cvalue": "string" } ``` **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.Response.StartAuthenticationByUserResponse` - `400`: 5: User identifier and type are required. 6: Multi-user passkey authentication is not supported for this credential type. - `403`: 0: Token Validation Failed 4: No passkeys registered for any users found. - `503`: 2: Feature disabled. **Response fields** (`Roblox.Authentication.Api.Models.Response.StartAuthenticationByUserResponse`) See [Roblox.Authentication.Api.Models.Response.StartAuthenticationByUserResponse](#roblox-authentication-api-models-response-startauthenticationbyuserresponse) in Models. **Response example:** ```json { "authenticationOptions": "string", "sessionId": "string" } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/passkey/start-authentication-by-user" \ -H "Content-Type: application/json" \ -d '{ "ctype": 0, "cvalue": "string" }' ``` ### POST `/v1/passkey/start-preauth-registration` Initiates Passkey preauthenticated registration by providing credential creation options. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.Request.StartPasskeyPreauthRegistrationRequest` See [Roblox.Authentication.Api.Models.Request.StartPasskeyPreauthRegistrationRequest](#roblox-authentication-api-models-request-startpasskeypreauthregistrationrequest) in Models. **Request example:** ```json { "username": "string" } ``` **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.Response.StartPasskeyPreauthRegistrationResponse` - `403`: 0: Token Validation Failed - `503`: 2: Feature disabled. **Response fields** (`Roblox.Authentication.Api.Models.Response.StartPasskeyPreauthRegistrationResponse`) See [Roblox.Authentication.Api.Models.Response.StartPasskeyPreauthRegistrationResponse](#roblox-authentication-api-models-response-startpasskeypreauthregistrationresponse) in Models. **Response example:** ```json { "creationOptions": "string", "sessionId": "string" } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/passkey/start-preauth-registration" \ -H "Content-Type: application/json" \ -d '{ "username": "string" }' ``` ### POST `/v1/passkey/StartAuthentication` Provides a challenge for the Passkey to authenticate. **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.Response.StartAuthenticationResponse` - `403`: 0: Token Validation Failed - `503`: 2: Feature disabled. **Response fields** (`Roblox.Authentication.Api.Models.Response.StartAuthenticationResponse`) See [Roblox.Authentication.Api.Models.Response.StartAuthenticationResponse](#roblox-authentication-api-models-response-startauthenticationresponse) in Models. **Response example:** ```json { "authenticationOptions": "string", "sessionId": "string" } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/passkey/StartAuthentication" ``` ### POST `/v1/passkey/StartRegistration` Initiates Passkey registration by providing credential creation options. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `flow` | query | `string` | No | | **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.Request.StartPasskeyRegistrationRequest` See [Roblox.Authentication.Api.Models.Request.StartPasskeyRegistrationRequest](#roblox-authentication-api-models-request-startpasskeyregistrationrequest) in Models. **Request example:** ```json { "isSilentUpgrade": false } ``` **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.Response.StartPasskeyRegistrationResponse` - `401`: 0: Authorization has been denied for this request. 0: An unknown error occurred with the request. - `403`: 0: Token Validation Failed 1: Reached limit of pass keys registered. - `503`: 2: Feature disabled. **Response fields** (`Roblox.Authentication.Api.Models.Response.StartPasskeyRegistrationResponse`) See [Roblox.Authentication.Api.Models.Response.StartPasskeyRegistrationResponse](#roblox-authentication-api-models-response-startpasskeyregistrationresponse) in Models. **Response example:** ```json { "creationOptions": "string", "sessionId": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/passkey/StartRegistration" \ -H "Content-Type: application/json" \ -d '{ "isSilentUpgrade": false }' ``` ### POST `/v1/revert/invalidate-tickets` Invalidates all account security tickets for the authenticated user. This endpoint should be called before enrolling in EPP to ensure old revert links cannot be used. **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `503`: 1: This feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/revert/invalidate-tickets" ``` ### POST `/v1/session/refresh` Logs out user from the current session and create a new one. **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/session/refresh" ``` ### POST `/v1/signup` Endpoint for signing up a new user **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.SignupRequest` See [Roblox.Authentication.Api.Models.SignupRequest](#roblox-authentication-api-models-signuprequest) in Models. **Request example:** ```json { "username": "string", "password": "string", "gender": 1, "birthday": "2024-01-01T00:00:00Z", "displayName": "string", "isTosAgreementBoxChecked": false } ``` **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.SignupResponse` - `400`: Bad request 16: User agreement ids are null. 21: Empty account switch blob required - `403`: 0: Token Validation Failed 2: Captcha Failed. 4: Invalid Birthday. 5: Invalid Username. 6: Username already taken. 7: Invalid Password. 8: Password and Username are same. 9: Password is too simple. 10: Email is invalid. 11: Asset is invalid. 12: Too many attempts. Please wait a bit. 17: One time Passcode session was not valid 22: Maximum logged in accounts limit reached. - `429`: 3: Too many attempts. Please wait a bit. - `500`: Internal server error 15: Insert acceptances failed. 27: Pre-auth passkey registration failed - `503`: Service unavailable **Response fields** (`Roblox.Authentication.Api.Models.SignupResponse`) See [Roblox.Authentication.Api.Models.SignupResponse](#roblox-authentication-api-models-signupresponse) in Models. **Response example:** ```json { "userId": 0, "starterPlaceId": 0, "returnUrl": "string", "accountBlob": "string" } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/signup" \ -H "Content-Type: application/json" \ -d '{ "username": "string", "password": "string", "gender": 1, "birthday": "2024-01-01T00:00:00Z", "displayName": "string", "isTosAgreementBoxChecked": false }' ``` ### POST `/v1/signup/linked` Endpoint for signing up a new user, specifically for linked authentication on PCGDK **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.SignupRequest` See [Roblox.Authentication.Api.Models.SignupRequest](#roblox-authentication-api-models-signuprequest) in Models. **Request example:** ```json { "username": "string", "password": "string", "gender": 1, "birthday": "2024-01-01T00:00:00Z", "displayName": "string", "isTosAgreementBoxChecked": false } ``` **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.SignupResponse` - `400`: Bad request 16: User agreement ids are null. 21: Empty account switch blob required - `403`: 0: Token Validation Failed 2: Captcha Failed. 4: Invalid Birthday. 5: Invalid Username. 6: Username already taken. 7: Invalid Password. 8: Password and Username are same. 9: Password is too simple. 10: Email is invalid. 11: Asset is invalid. 12: Too many attempts. Please wait a bit. 17: One time Passcode session was not valid 22: Maximum logged in accounts limit reached. - `429`: 3: Too many attempts. Please wait a bit. - `500`: Internal server error 15: Insert acceptances failed. 27: Pre-auth passkey registration failed **Response fields** (`Roblox.Authentication.Api.Models.SignupResponse`) See [Roblox.Authentication.Api.Models.SignupResponse](#roblox-authentication-api-models-signupresponse) in Models. **Response example:** ```json { "userId": 0, "starterPlaceId": 0, "returnUrl": "string", "accountBlob": "string" } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/signup/linked" \ -H "Content-Type: application/json" \ -d '{ "username": "string", "password": "string", "gender": 1, "birthday": "2024-01-01T00:00:00Z", "displayName": "string", "isTosAgreementBoxChecked": false }' ``` ### POST `/v1/social/{provider}/disconnect` Removes the given social authentication method from current Roblox user if it is connected. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `provider` | path | `string` | Yes | The social authentication provider, e.g. Facebook | **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.SocialAuthenticationDisconnectRequest` See [Roblox.Authentication.Api.Models.SocialAuthenticationDisconnectRequest](#roblox-authentication-api-models-socialauthenticationdisconnectrequest) in Models. **Request example:** ```json { "Password": "string" } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: Bad request 2: Unsupported social provider type. - `401`: 0: Authorization has been denied for this request. - `403`: Forbidden 0: Token Validation Failed 3: Cannot disconnect the only authentication method. Password on account is required. 4: The password provided is invalid. - `500`: Internal server error **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/social/{PROVIDER}/disconnect" \ -H "Content-Type: application/json" \ -d '{ "Password": "string" }' ``` ### POST `/v1/user/passwords/change` Changes the password for the authenticated user. The current password is needed for verification that the password can be changed. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.PasswordChangeModel` See [Roblox.Authentication.Api.Models.PasswordChangeModel](#roblox-authentication-api-models-passwordchangemodel) in Models. **Request example:** ```json { "currentPassword": "string", "newPassword": "string", "secureAuthenticationIntent": { "clientPublicKey": "string", "clientEpochTimestamp": 0, "saiSignature": "string", "serverNonce": "string" } } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: Roblox.Web.Authentication.Passwords.PasswordResponseCodes.InvalidCurrentPassword OR Roblox.Web.Authentication.Passwords.PasswordResponseCodes.InvalidPassword - `401`: 0: Authorization has been denied for this request. - `403`: Roblox.Web.Authentication.Passwords.PasswordResponseCodes.PinLocked 0: Token Validation Failed - `429`: Roblox.Web.Authentication.Passwords.PasswordResponseCodes.Flooded **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/user/passwords/change" \ -H "Content-Type: application/json" \ -d '{ "currentPassword": "string", "newPassword": "string", "secureAuthenticationIntent": { "clientPublicKey": "string", "clientEpochTimestamp": 0, "saiSignature": "string", "serverNonce": "string" } }' ``` ### POST `/v1/username` Change the user's username **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.UsernameChangeRequest` See [Roblox.Authentication.Api.Models.UsernameChangeRequest](#roblox-authentication-api-models-usernamechangerequest) in Models. **Request example:** ```json { "username": "string", "password": "string" } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 5: You don't have enough Robux to change your username. 10: This username is already in use 11: Username not appropriate for Roblox 12: Usernames can be 3 to 20 characters long 13: Usernames can’t start or end with _ and can have at most one _ 14: Only a-z, A-Z, 0-9, and _ are allowed 15: Username is null 16: Username might contain private information 17: This username is not available 18: Username is same as current - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 1: PIN is locked. 2: A verified email is missing 3: Your password is incorrect. 100: Unknown birthday - `500`: 0: An unknown error occured. 5: You don't have enough Robux to change your username. - `503`: 4: The feature is currently not available. Please try again later. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/username" \ -H "Content-Type: application/json" \ -d '{ "username": "string", "password": "string" }' ``` ### POST `/v1/usernames/recover` Sends an email of all accounts belonging to an email **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.RecoverUsernameRequest` See [Roblox.Authentication.Api.Models.RecoverUsernameRequest](#roblox-authentication-api-models-recoverusernamerequest) in Models. **Request example:** ```json { "targetType": 0, "target": "string" } ``` **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.RecoverUsernameResponse` - `400`: 20: Invalid Email 21: Invalid Phone 23: No Account Found - `403`: 0: Token Validation Failed 11: Too many attempts. Please wait a bit. - `500`: 0: An unexpected error occurred. **Response fields** (`Roblox.Authentication.Api.Models.RecoverUsernameResponse`) See [Roblox.Authentication.Api.Models.RecoverUsernameResponse](#roblox-authentication-api-models-recoverusernameresponse) in Models. **Response example:** ```json { "transmissionType": 0 } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/usernames/recover" \ -H "Content-Type: application/json" \ -d '{ "targetType": 0, "target": "string" }' ``` ### POST `/v1/xbox/disconnect` Unlink the current ROBLOX account from the Xbox live account. **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiSuccessResponse` - `401`: 0: Authorization has been denied for this request. - `403`: Forbidden 0: Token Validation Failed **Response fields** (`Roblox.Web.WebAPI.Models.ApiSuccessResponse`) See [Roblox.Web.WebAPI.Models.ApiSuccessResponse](#roblox-web-webapi-models-apisuccessresponse) in Models. **Response example:** ```json { "success": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/xbox/disconnect" ``` ### POST `/v1/xbox/translate` Translate the xbox user to roblox user. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.XboxTranslateRequest` See [Roblox.Authentication.Api.Models.XboxTranslateRequest](#roblox-authentication-api-models-xboxtranslaterequest) in Models. **Request example:** ```json { "ids": [ "string" ] } ``` **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.XboxCollectionsOfUserResponse` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Authentication.Api.Models.XboxCollectionsOfUserResponse`) See [Roblox.Authentication.Api.Models.XboxCollectionsOfUserResponse](#roblox-authentication-api-models-xboxcollectionsofuserresponse) in Models. **Response example:** ```json { "Users": [ { "Id": "...", "UserId": "...", "Username": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v1/xbox/translate" \ -H "Content-Type: application/json" \ -d '{ "ids": [ "string" ] }' ``` --- name: "Authentication Api v2" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://auth.roblox.com" version: "v2" endpoints: 28 auth: [cookie] --- # Authentication Api v2 **API Version:** v2 **Base URL:** `https://auth.roblox.com` ## Endpoints ### GET `/v2/auth/metadata` Gets Auth meta data **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.AuthMetaDataResponse` **Response fields** (`Roblox.Authentication.Api.Models.AuthMetaDataResponse`) See [Roblox.Authentication.Api.Models.AuthMetaDataResponse](#roblox-authentication-api-models-authmetadataresponse) in Models. **Response example:** ```json { "cookieLawNoticeTimeout": 0 } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v2/auth/metadata" ``` ### GET `/v2/metadata` Get the metadata **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.MetadataResponse` **Response fields** (`Roblox.Authentication.Api.Models.MetadataResponse`) See [Roblox.Authentication.Api.Models.MetadataResponse](#roblox-authentication-api-models-metadataresponse) in Models. **Response example:** ```json { "isUpdateUsernameEnabled": false, "ftuxAvatarAssetMap": "string", "IsEmailUpsellAtLogoutEnabled": false, "ShouldFetchEmailUpsellIXPValuesAtLogout": false, "IsAccountRecoveryPromptEnabled": false, "IsContactMethodRequiredAtSignup": false } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v2/metadata" ``` ### GET `/v2/passwords/current-status` Returns password status for current user, asynchronously. **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.PasswordStatusResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Authentication.Api.Models.PasswordStatusResponse`) See [Roblox.Authentication.Api.Models.PasswordStatusResponse](#roblox-authentication-api-models-passwordstatusresponse) in Models. **Response example:** ```json { "valid": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v2/passwords/current-status" ``` ### GET `/v2/passwords/reset` Gets metadata needed for the password reset view. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `TargetType` | query | `integer (int32)` | Yes | Valid values: `0`, `1`, `2` | | `Ticket` | query | `string` | Yes | | **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.PasswordResetMetadataResponse` - `400`: 3: Request was empty. 9: The target type is invalid. 11: The password reset ticket is invalid. 14: The nonce is invalid. - `403`: 11: The password reset ticket is invalid. 16: The ticket is expired. - `500`: 0: Unknown error occured. - `503`: 1: Feature temporarily disabled. Please try again later. **Response fields** (`Roblox.Authentication.Api.Models.PasswordResetMetadataResponse`) See [Roblox.Authentication.Api.Models.PasswordResetMetadataResponse](#roblox-authentication-api-models-passwordresetmetadataresponse) in Models. **Response example:** ```json { "users": [ { "userId": "...", "username": "...", "displayName": "..." } ] } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v2/passwords/reset?TargetType={VALUE}&Ticket={VALUE}" ``` ### POST `/v2/passwords/reset` Resets a password for a user that belongs to the password reset ticket. This will log the user out of all sessions and re-authenticate. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.PasswordResetModel` See [Roblox.Authentication.Api.Models.PasswordResetModel](#roblox-authentication-api-models-passwordresetmodel) in Models. **Request example:** ```json { "targetType": 0, "ticket": "string", "userId": 0, "password": "string", "passwordRepeated": "string", "twoStepVerificationChallengeId": "string" } ``` **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.LoginResponse` - `400`: 3: Request was empty. 11: The password reset ticket is invalid. 12: The user is invalid. 20: The password is invalid. 21: Passwords do not match. - `403`: 0: Token Validation Failed 16: The ticket is expired. 17: The nonce is expired. - `500`: 0: Unknown error occured. - `503`: 1: Feature temporarily disabled. Please try again later. **Response fields** (`Roblox.Authentication.Api.Models.LoginResponse`) See [Roblox.Authentication.Api.Models.LoginResponse](#roblox-authentication-api-models-loginresponse) in Models. **Response example:** ```json { "user": { "id": 0, "name": "string", "displayName": "string" }, "twoStepVerificationData": { "mediaType": 0, "ticket": "string" }, "identityVerificationLoginTicket": "string", "isBanned": false, "accountBlob": "string", "shouldUpdateEmail": false } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v2/passwords/reset" \ -H "Content-Type: application/json" \ -d '{ "targetType": 0, "ticket": "string", "userId": 0, "password": "string", "passwordRepeated": "string", "twoStepVerificationChallengeId": "string" }' ``` ### GET `/v2/passwords/validate` Endpoint for checking if a password is valid. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `Username` | query | `string` | Yes | | | `Password` | query | `string` | Yes | | **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.PasswordValidationResponse` - `400`: 1: Valid Username and Password are required. Please try again. **Response fields** (`Roblox.Authentication.Api.Models.PasswordValidationResponse`) See [Roblox.Authentication.Api.Models.PasswordValidationResponse](#roblox-authentication-api-models-passwordvalidationresponse) in Models. **Response example:** ```json { "code": 0, "message": "string" } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v2/passwords/validate?Username={VALUE}&Password={VALUE}" ``` ### POST `/v2/passwords/validate` Endpoint for checking if a password is valid. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.PasswordValidationModel` See [Roblox.Authentication.Api.Models.PasswordValidationModel](#roblox-authentication-api-models-passwordvalidationmodel) in Models. **Request example:** ```json { "username": "string", "password": "string" } ``` **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.PasswordValidationResponse` - `400`: 1: Valid Username and Password are required. Please try again. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Authentication.Api.Models.PasswordValidationResponse`) See [Roblox.Authentication.Api.Models.PasswordValidationResponse](#roblox-authentication-api-models-passwordvalidationresponse) in Models. **Response example:** ```json { "code": 0, "message": "string" } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v2/passwords/validate" \ -H "Content-Type: application/json" \ -d '{ "username": "string", "password": "string" }' ``` ### GET `/v2/recovery/metadata` Get metadata for forgot endpoints **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.RecoveryMetadataResponse` - `503`: 7: The Roblox WeChat API is currently unavailable. **Response fields** (`Roblox.Authentication.Api.Models.RecoveryMetadataResponse`) See [Roblox.Authentication.Api.Models.RecoveryMetadataResponse](#roblox-authentication-api-models-recoverymetadataresponse) in Models. **Response example:** ```json { "isOnPhone": false, "codeLength": 0, "isPhoneFeatureEnabledForUsername": false, "isPhoneFeatureEnabledForPassword": false, "isBedev2CaptchaEnabledForPasswordReset": false, "isUsernameRecoveryDeprecated": false } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v2/recovery/metadata" ``` ### GET `/v2/revert/account` Get Revert Account ticket info **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `ticket` | query | `string` | Yes | Ticket Guid to revert account. | **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.RevertAccountInfoResponse` - `400`: 2: The account revert ticket is not valid - `403`: 13: Revert links are disabled for users in the Enhanced Protection Program. - `503`: 1: This feature is disabled **Response fields** (`Roblox.Authentication.Api.Models.RevertAccountInfoResponse`) See [Roblox.Authentication.Api.Models.RevertAccountInfoResponse](#roblox-authentication-api-models-revertaccountinforesponse) in Models. **Response example:** ```json { "isTwoStepVerificationEnabled": false, "isEmailVerified": false, "isEmailChanged": false, "isPhoneVerified": false, "userId": 0, "username": "string" } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v2/revert/account?ticket={VALUE}" ``` ### POST `/v2/revert/account` Submit Revert Account Request **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.RevertAccountSubmitRequest` See [Roblox.Authentication.Api.Models.RevertAccountSubmitRequest](#roblox-authentication-api-models-revertaccountsubmitrequest) in Models. **Request example:** ```json { "UserId": 0, "NewPassword": "string", "NewPasswordRepeated": "string", "Ticket": "string", "TwoStepVerificationChallengeId": "string", "TwoStepVerificationToken": "string" } ``` **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.LoginResponse` - `400`: 2: The account revert ticket is not valid 3: Password is not valid 4: Passwords do not match 5: Password cannot be used 8: The account security ticket is expired. - `403`: 0: Token Validation Failed - `503`: 0: Unknown 1: This feature is disabled **Response fields** (`Roblox.Authentication.Api.Models.LoginResponse`) See [Roblox.Authentication.Api.Models.LoginResponse](#roblox-authentication-api-models-loginresponse) in Models. **Response example:** ```json { "user": { "id": 0, "name": "string", "displayName": "string" }, "twoStepVerificationData": { "mediaType": 0, "ticket": "string" }, "identityVerificationLoginTicket": "string", "isBanned": false, "accountBlob": "string", "shouldUpdateEmail": false } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v2/revert/account" \ -H "Content-Type: application/json" \ -d '{ "UserId": 0, "NewPassword": "string", "NewPasswordRepeated": "string", "Ticket": "string", "TwoStepVerificationChallengeId": "string", "TwoStepVerificationToken": "string" }' ``` ### GET `/v2/username/change/price` Get the current price for a username change **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.UsernameChangePriceResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Authentication.Api.Models.UsernameChangePriceResponse`) See [Roblox.Authentication.Api.Models.UsernameChangePriceResponse](#roblox-authentication-api-models-usernamechangepriceresponse) in Models. **Response example:** ```json { "priceInRobux": 0, "basePriceInRobux": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v2/username/change/price" ``` ### GET `/v2/usernames` Gets a list of existing usernames on Roblox based on the query parameters This endpoint can be expanded in the future to include other query parameters such as "startsWith" **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `username` | query | `string` | Yes | The username | **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.UsernamesResponse` **Response fields** (`Roblox.Authentication.Api.Models.UsernamesResponse`) See [Roblox.Authentication.Api.Models.UsernamesResponse](#roblox-authentication-api-models-usernamesresponse) in Models. **Response example:** ```json { "usernames": [ "string" ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v2/usernames?username={VALUE}" ``` ### GET `/v2/usernames/validate` Checks if a username is valid. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `Username` | query | `string` | Yes | | | `Birthday` | query | `string (date-time)` | Yes | | | `Context` | query | `integer (int32)` | Yes | Valid values: `0`, `1`, `2` | **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.UsernameValidationResponse` - `400`: 1: A valid username is required. 2: A valid birthday or authenticated user is required. **Response fields** (`Roblox.Authentication.Api.Models.UsernameValidationResponse`) See [Roblox.Authentication.Api.Models.UsernameValidationResponse](#roblox-authentication-api-models-usernamevalidationresponse) in Models. **Response example:** ```json { "code": 0, "message": "string" } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v2/usernames/validate?Username={VALUE}&Birthday={VALUE}&Context={VALUE}" ``` ### POST `/v2/usernames/validate` Checks if a username is valid. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.UsernameValidationRequest` See [Roblox.Authentication.Api.Models.UsernameValidationRequest](#roblox-authentication-api-models-usernamevalidationrequest) in Models. **Request example:** ```json { "username": "string", "birthday": "2024-01-01T00:00:00Z", "context": 0 } ``` **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.UsernameValidationResponse` - `400`: 1: A valid username is required. 2: A valid birthday or authenticated user is required. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Authentication.Api.Models.UsernameValidationResponse`) See [Roblox.Authentication.Api.Models.UsernameValidationResponse](#roblox-authentication-api-models-usernamevalidationresponse) in Models. **Response example:** ```json { "code": 0, "message": "string" } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v2/usernames/validate" \ -H "Content-Type: application/json" \ -d '{ "username": "string", "birthday": "2024-01-01T00:00:00Z", "context": 0 }' ``` ### POST `/v2/identity-verification/login` Endpoint for login with identity verification **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.Request.IdentityVerificationLoginRequest` See [Roblox.Authentication.Api.Models.Request.IdentityVerificationLoginRequest](#roblox-authentication-api-models-request-identityverificationloginrequest) in Models. **Request example:** ```json { "loginTicket": "string", "resultToken": "string" } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `403`: 0: Token Validation Failed 1: Invalid login ticket. 2: Invalid result token. 3: Invalid user. 4: Authentication failure. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v2/identity-verification/login" \ -H "Content-Type: application/json" \ -d '{ "loginTicket": "string", "resultToken": "string" }' ``` ### POST `/v2/login` Authenticates a user. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.LoginRequest` See [Roblox.Authentication.Api.Models.LoginRequest](#roblox-authentication-api-models-loginrequest) in Models. **Request example:** ```json { "ctype": 0, "cvalue": "string", "password": "string", "userId": 0, "securityQuestionSessionId": "string", "securityQuestionRedemptionToken": "string" } ``` **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.LoginResponse` - `400`: 0: An unexpected error occurred. 3: Username and Password are required. Please try again. 8: Login with received credential type is not supported. - `403`: 0: Token Validation Failed 1: Incorrect username or password. Please try again. 2: You must pass the robot test before logging in. 4: Account has been locked. Please request a password reset. 5: Unable to login. Please use Social Network sign on. 6: Account issue. Please contact Support. 9: Unable to login with provided credentials. Default login is required. 10: Received credentials are unverified. 12: Existing login session found. Please log out first. 14: The account is unable to log in. Please log in to the LuoBu app. 15: Too many attempts. Please wait a bit. 27: The account is unable to login. Please log in with the VNG app. - `429`: 7: Too many attempts. Please wait a bit. - `503`: 11: Service unavailable. Please try again. **Response fields** (`Roblox.Authentication.Api.Models.LoginResponse`) See [Roblox.Authentication.Api.Models.LoginResponse](#roblox-authentication-api-models-loginresponse) in Models. **Response example:** ```json { "user": { "id": 0, "name": "string", "displayName": "string" }, "twoStepVerificationData": { "mediaType": 0, "ticket": "string" }, "identityVerificationLoginTicket": "string", "isBanned": false, "accountBlob": "string", "shouldUpdateEmail": false } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v2/login" \ -H "Content-Type: application/json" \ -d '{ "ctype": 0, "cvalue": "string", "password": "string", "userId": 0, "securityQuestionSessionId": "string", "securityQuestionRedemptionToken": "string" }' ``` ### POST `/v2/login/linked` Endpoint for logging in a user, specifically for linked authentication on PCGDK **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.LoginRequest` See [Roblox.Authentication.Api.Models.LoginRequest](#roblox-authentication-api-models-loginrequest) in Models. **Request example:** ```json { "ctype": 0, "cvalue": "string", "password": "string", "userId": 0, "securityQuestionSessionId": "string", "securityQuestionRedemptionToken": "string" } ``` **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.LoginResponse` - `400`: 0: An unexpected error occurred. 3: Username and Password are required. Please try again. 8: Login with received credential type is not supported. - `403`: 0: Token Validation Failed 1: Incorrect username or password. Please try again. 2: You must pass the robot test before logging in. 4: Account has been locked. Please request a password reset. 5: Unable to login. Please use Social Network sign on. 6: Account issue. Please contact Support. 9: Unable to login with provided credentials. Default login is required. 10: Received credentials are unverified. 12: Existing login session found. Please log out first. 14: The account is unable to log in. Please log in to the LuoBu app. 15: Too many attempts. Please wait a bit. 27: The account is unable to login. Please log in with the VNG app. - `429`: 7: Too many attempts. Please wait a bit. - `503`: 11: Service unavailable. Please try again. **Response fields** (`Roblox.Authentication.Api.Models.LoginResponse`) See [Roblox.Authentication.Api.Models.LoginResponse](#roblox-authentication-api-models-loginresponse) in Models. **Response example:** ```json { "user": { "id": 0, "name": "string", "displayName": "string" }, "twoStepVerificationData": { "mediaType": 0, "ticket": "string" }, "identityVerificationLoginTicket": "string", "isBanned": false, "accountBlob": "string", "shouldUpdateEmail": false } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v2/login/linked" \ -H "Content-Type: application/json" \ -d '{ "ctype": 0, "cvalue": "string", "password": "string", "userId": 0, "securityQuestionSessionId": "string", "securityQuestionRedemptionToken": "string" }' ``` ### POST `/v2/logout` Destroys the current authentication session. **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v2/logout" ``` ### POST `/v2/logoutfromallsessionsandreauthenticate` Logs out user from all other sessions. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.Request.LogoutFromAllSessionsAndReauthenticateRequest` See [Roblox.Authentication.Api.Models.Request.LogoutFromAllSessionsAndReauthenticateRequest](#roblox-authentication-api-models-request-logoutfromallsessionsandreauthenticaterequest) in Models. **Request example:** ```json { "SecureAuthenticationIntent": { "clientPublicKey": "string", "clientEpochTimestamp": 0, "saiSignature": "string", "serverNonce": "string" } } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v2/logoutfromallsessionsandreauthenticate" \ -H "Content-Type: application/json" \ -d '{ "SecureAuthenticationIntent": { "clientPublicKey": "string", "clientEpochTimestamp": 0, "saiSignature": "string", "serverNonce": "string" } }' ``` ### POST `/v2/passwords/reset/send` Sends a password reset email or challenge to the specified target. Phone target must be a csv with 3 values: "internationalPrefixNumber,nationalNumber,countryCode" **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.SendResetPasswordRequest` See [Roblox.Authentication.Api.Models.SendResetPasswordRequest](#roblox-authentication-api-models-sendresetpasswordrequest) in Models. **Request example:** ```json { "targetType": 0, "target": "string", "captchaId": "string", "captchaToken": "string", "captchaProvider": "string", "challengeId": "string" } ``` **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.SendResetPasswordResponse` - `400`: 3: Request was empty. 9: The target type is invalid. 10: The target is invalid. 12: The user is invalid. - `403`: 0: Token Validation Failed 18: Captcha is required. 23: Authenticate with Luobu instead. - `429`: 2: Too many attempts. Please try again later. - `500`: 0: Unknown error occured. 19: Message could not be sent. - `503`: 1: Feature temporarily disabled. Please try again later. **Response fields** (`Roblox.Authentication.Api.Models.SendResetPasswordResponse`) See [Roblox.Authentication.Api.Models.SendResetPasswordResponse](#roblox-authentication-api-models-sendresetpasswordresponse) in Models. **Response example:** ```json { "nonce": "string", "transmissionType": 0 } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v2/passwords/reset/send" \ -H "Content-Type: application/json" \ -d '{ "targetType": 0, "target": "string", "captchaId": "string", "captchaToken": "string", "captchaProvider": "string", "challengeId": "string" }' ``` ### POST `/v2/passwords/reset/verify` Verifies a password reset challenge solution. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.PasswordResetVerificationRequest` See [Roblox.Authentication.Api.Models.PasswordResetVerificationRequest](#roblox-authentication-api-models-passwordresetverificationrequest) in Models. **Request example:** ```json { "targetType": 0, "nonce": "string", "code": "string" } ``` **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.PasswordResetVerificationResponse` - `400`: 3: Request was empty. 9: The target type is invalid. 14: The nonce is invalid. - `403`: 0: Token Validation Failed 13: The code is invalid. - `500`: 0: Unknown error occured. - `503`: 1: Feature temporarily disabled. Please try again later. **Response fields** (`Roblox.Authentication.Api.Models.PasswordResetVerificationResponse`) See [Roblox.Authentication.Api.Models.PasswordResetVerificationResponse](#roblox-authentication-api-models-passwordresetverificationresponse) in Models. **Response example:** ```json { "userTickets": [ { "user": "...", "ticket": "..." } ] } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v2/passwords/reset/verify" \ -H "Content-Type: application/json" \ -d '{ "targetType": 0, "nonce": "string", "code": "string" }' ``` ### POST `/v2/revert/invalidate-tickets` Invalidates all account security tickets for the authenticated user. This endpoint should be called before enrolling in EPP to ensure old revert links cannot be used. **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `503`: 1: This feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v2/revert/invalidate-tickets" ``` ### POST `/v2/session/refresh` Logs out user from the current session and create a new one. **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v2/session/refresh" ``` ### POST `/v2/signup` Endpoint for signing up a new user **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.SignupRequest` See [Roblox.Authentication.Api.Models.SignupRequest](#roblox-authentication-api-models-signuprequest) in Models. **Request example:** ```json { "username": "string", "password": "string", "gender": 1, "birthday": "2024-01-01T00:00:00Z", "displayName": "string", "isTosAgreementBoxChecked": false } ``` **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.SignupResponse` - `400`: Bad request 16: User agreement ids are null. 21: Empty account switch blob required - `403`: 0: Token Validation Failed 2: Captcha Failed. 4: Invalid Birthday. 5: Invalid Username. 6: Username already taken. 7: Invalid Password. 8: Password and Username are same. 9: Password is too simple. 10: Email is invalid. 11: Asset is invalid. 12: Too many attempts. Please wait a bit. 17: One time Passcode session was not valid 22: Maximum logged in accounts limit reached. - `429`: 3: Too many attempts. Please wait a bit. - `500`: Internal server error 15: Insert acceptances failed. 27: Pre-auth passkey registration failed - `503`: Service unavailable **Response fields** (`Roblox.Authentication.Api.Models.SignupResponse`) See [Roblox.Authentication.Api.Models.SignupResponse](#roblox-authentication-api-models-signupresponse) in Models. **Response example:** ```json { "userId": 0, "starterPlaceId": 0, "returnUrl": "string", "accountBlob": "string" } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v2/signup" \ -H "Content-Type: application/json" \ -d '{ "username": "string", "password": "string", "gender": 1, "birthday": "2024-01-01T00:00:00Z", "displayName": "string", "isTosAgreementBoxChecked": false }' ``` ### POST `/v2/signup/linked` Endpoint for signing up a new user, specifically for linked authentication on PCGDK **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.SignupRequest` See [Roblox.Authentication.Api.Models.SignupRequest](#roblox-authentication-api-models-signuprequest) in Models. **Request example:** ```json { "username": "string", "password": "string", "gender": 1, "birthday": "2024-01-01T00:00:00Z", "displayName": "string", "isTosAgreementBoxChecked": false } ``` **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.SignupResponse` - `400`: Bad request 16: User agreement ids are null. 21: Empty account switch blob required - `403`: 0: Token Validation Failed 2: Captcha Failed. 4: Invalid Birthday. 5: Invalid Username. 6: Username already taken. 7: Invalid Password. 8: Password and Username are same. 9: Password is too simple. 10: Email is invalid. 11: Asset is invalid. 12: Too many attempts. Please wait a bit. 17: One time Passcode session was not valid 22: Maximum logged in accounts limit reached. - `429`: 3: Too many attempts. Please wait a bit. - `500`: Internal server error 15: Insert acceptances failed. 27: Pre-auth passkey registration failed **Response fields** (`Roblox.Authentication.Api.Models.SignupResponse`) See [Roblox.Authentication.Api.Models.SignupResponse](#roblox-authentication-api-models-signupresponse) in Models. **Response example:** ```json { "userId": 0, "starterPlaceId": 0, "returnUrl": "string", "accountBlob": "string" } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v2/signup/linked" \ -H "Content-Type: application/json" \ -d '{ "username": "string", "password": "string", "gender": 1, "birthday": "2024-01-01T00:00:00Z", "displayName": "string", "isTosAgreementBoxChecked": false }' ``` ### POST `/v2/user/passwords/change` Changes the password for the authenticated user. The current password is needed for verification that the password can be changed. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.PasswordChangeModel` See [Roblox.Authentication.Api.Models.PasswordChangeModel](#roblox-authentication-api-models-passwordchangemodel) in Models. **Request example:** ```json { "currentPassword": "string", "newPassword": "string", "secureAuthenticationIntent": { "clientPublicKey": "string", "clientEpochTimestamp": 0, "saiSignature": "string", "serverNonce": "string" } } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: Roblox.Web.Authentication.Passwords.PasswordResponseCodes.InvalidCurrentPassword OR Roblox.Web.Authentication.Passwords.PasswordResponseCodes.InvalidPassword - `401`: 0: Authorization has been denied for this request. - `403`: Roblox.Web.Authentication.Passwords.PasswordResponseCodes.PinLocked 0: Token Validation Failed - `429`: Roblox.Web.Authentication.Passwords.PasswordResponseCodes.Flooded **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v2/user/passwords/change" \ -H "Content-Type: application/json" \ -d '{ "currentPassword": "string", "newPassword": "string", "secureAuthenticationIntent": { "clientPublicKey": "string", "clientEpochTimestamp": 0, "saiSignature": "string", "serverNonce": "string" } }' ``` ### POST `/v2/username` Change the user's username **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.UsernameChangeRequest` See [Roblox.Authentication.Api.Models.UsernameChangeRequest](#roblox-authentication-api-models-usernamechangerequest) in Models. **Request example:** ```json { "username": "string", "password": "string" } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 5: You don't have enough Robux to change your username. 10: This username is already in use 11: Username not appropriate for Roblox 12: Usernames can be 3 to 20 characters long 13: Usernames can’t start or end with _ and can have at most one _ 14: Only a-z, A-Z, 0-9, and _ are allowed 15: Username is null 16: Username might contain private information 17: This username is not available 18: Username is same as current - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 1: PIN is locked. 2: A verified email is missing 3: Your password is incorrect. 100: Unknown birthday - `500`: 0: An unknown error occured. 5: You don't have enough Robux to change your username. - `503`: 4: The feature is currently not available. Please try again later. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v2/username" \ -H "Content-Type: application/json" \ -d '{ "username": "string", "password": "string" }' ``` ### POST `/v2/usernames/recover` Sends an email of all accounts belonging to an email **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.RecoverUsernameRequest` See [Roblox.Authentication.Api.Models.RecoverUsernameRequest](#roblox-authentication-api-models-recoverusernamerequest) in Models. **Request example:** ```json { "targetType": 0, "target": "string" } ``` **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.RecoverUsernameResponse` - `400`: 20: Invalid Email 21: Invalid Phone 23: No Account Found - `403`: 0: Token Validation Failed 11: Too many attempts. Please wait a bit. - `500`: 0: An unexpected error occurred. **Response fields** (`Roblox.Authentication.Api.Models.RecoverUsernameResponse`) See [Roblox.Authentication.Api.Models.RecoverUsernameResponse](#roblox-authentication-api-models-recoverusernameresponse) in Models. **Response example:** ```json { "transmissionType": 0 } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v2/usernames/recover" \ -H "Content-Type: application/json" \ -d '{ "targetType": 0, "target": "string" }' ``` ## Models ### Roblox.Authentication.Api.Models.AccountLinkParameters | Property | Type | Required | Description | |----------|------|----------|-------------| | `LinkingPlatform` | `integer enum (10 values)` | No | ['Invalid' = 0, 'Xbox' = 1, 'Qq' = 2, 'WeChat' = 3, 'Facebook' = 4, 'RobloxDeveloper' = 5, 'RobloxGroupCreator' = 6, 'Playstation' = 7, 'ExternalProvider' = 8, 'Example' = 999] Values: 0, 1, 2, 3, 4, 5, 6, 7, 8, 999 | ### Roblox.Authentication.Api.Models.AuthMetaDataResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `cookieLawNoticeTimeout` | `integer` | No | | ### Roblox.Authentication.Api.Models.ForgotPasswordUserResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `user` | `Roblox.Web.Responses.Users.LegacyUserResponse` | No | | | `ticket` | `string` | No | | ### Roblox.Authentication.Api.Models.LoginRequest | Property | Type | Required | Description | |----------|------|----------|-------------| | `ctype` | `integer enum (10 values)` | No | ['Email' = 0, 'Username' = 1, 'PhoneNumber' = 2, 'EmailOtpSessionToken' = 3, 'AuthToken' = 4, 'Passkey' = 5, 'AsUser' = 6, 'TwoStepVerification' = 7, 'XboxLive' = 8, 'PlatformLive' = 9] Values: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 | | `cvalue` | `string` | No | | | `password` | `string` | No | | | `userId` | `integer` | No | | | `securityQuestionSessionId` | `string` | No | | | `securityQuestionRedemptionToken` | `string` | No | | | `secureAuthenticationIntent` | `Roblox.Authentication.Api.Models.Request.SecureAuthenticationIntentModel` | No | | | `accountBlob` | `string` | No | | | `accountLinkParameters` | `Roblox.Authentication.Api.Models.AccountLinkParameters` | No | | | `captchaId` | `string` | No | | | `captchaToken` | `string` | No | | | `captchaProvider` | `string` | No | | | `challengeId` | `string` | No | | ### Roblox.Authentication.Api.Models.LoginResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `user` | `Roblox.Web.Responses.Users.SkinnyUserResponse` | No | | | `twoStepVerificationData` | `Roblox.Authentication.Api.Models.TwoStepVerificationSentResponse` | No | | | `identityVerificationLoginTicket` | `string` | No | | | `isBanned` | `boolean` | No | | | `accountBlob` | `string` | No | | | `shouldUpdateEmail` | `boolean` | No | | | `recoveryEmail` | `string` | No | | | `passkeyRegistrationSucceeded` | `boolean` | No | | | `shouldAutoLoginFromRecovery` | `boolean` | No | | | `shouldPrompt2svRemoval` | `boolean` | No | | | `shouldPromptPasskeyAddition` | `boolean` | No | | ### Roblox.Authentication.Api.Models.MetadataResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `isUpdateUsernameEnabled` | `boolean` | No | | | `ftuxAvatarAssetMap` | `string` | No | | | `IsEmailUpsellAtLogoutEnabled` | `boolean` | No | | | `ShouldFetchEmailUpsellIXPValuesAtLogout` | `boolean` | No | | | `IsAccountRecoveryPromptEnabled` | `boolean` | No | | | `IsContactMethodRequiredAtSignup` | `boolean` | No | | | `IsUserAgreementsSignupIntegrationEnabled` | `boolean` | No | | | `IsPasswordRequiredForUsernameChange` | `boolean` | No | | | `IsPasskeyFeatureEnabled` | `boolean` | No | | | `IsAltBrowserTracker` | `boolean` | No | | | `IsLoginRedirectPageEnabled` | `boolean` | No | | ### Roblox.Authentication.Api.Models.PasswordChangeModel | Property | Type | Required | Description | |----------|------|----------|-------------| | `currentPassword` | `string` | No | | | `newPassword` | `string` | No | | | `secureAuthenticationIntent` | `Roblox.Authentication.Api.Models.Request.SecureAuthenticationIntentModel` | No | | ### Roblox.Authentication.Api.Models.PasswordResetMetadataResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `users` | `Roblox.Web.Responses.Users.LegacyUserResponse[]` | No | | ### Roblox.Authentication.Api.Models.PasswordResetModel | Property | Type | Required | Description | |----------|------|----------|-------------| | `targetType` | `0 \| 1 \| 2` | No | ['Email' = 0, 'PhoneNumber' = 1, 'RecoverySessionID' = 2] | | `ticket` | `string` | No | | | `userId` | `integer` | No | | | `password` | `string` | No | | | `passwordRepeated` | `string` | No | | | `twoStepVerificationChallengeId` | `string` | No | | | `twoStepVerificationToken` | `string` | No | | | `accountBlob` | `string` | No | | | `secureAuthenticationIntent` | `Roblox.Authentication.Api.Models.Request.SecureAuthenticationIntentModel` | No | | | `newEmail` | `string` | No | | | `passkeySessionId` | `string` | No | | | `passkeyRegistrationResponse` | `string` | No | | ### Roblox.Authentication.Api.Models.PasswordResetVerificationRequest | Property | Type | Required | Description | |----------|------|----------|-------------| | `targetType` | `0 \| 1 \| 2` | No | ['Email' = 0, 'PhoneNumber' = 1, 'RecoverySessionID' = 2] | | `nonce` | `string` | No | | | `code` | `string` | No | | ### Roblox.Authentication.Api.Models.PasswordResetVerificationResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `userTickets` | `Roblox.Authentication.Api.Models.ForgotPasswordUserResponse[]` | No | | ### Roblox.Authentication.Api.Models.PasswordStatusResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `valid` | `boolean` | No | | ### Roblox.Authentication.Api.Models.PasswordValidationModel | Property | Type | Required | Description | |----------|------|----------|-------------| | `username` | `string` | No | | | `password` | `string` | No | | ### Roblox.Authentication.Api.Models.PasswordValidationResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `code` | `integer enum (6 values)` | No | ['ValidPassword' = 0, 'WeakPasswordError' = 1, 'PasswordLengthError' = 2, 'PasswordSameAsUsernameError' = 3, 'ForbiddenPasswordError' = 4, 'DumbStringsError' = 5] Values: 0, 1, 2, 3, 4, 5 | | `message` | `string` | No | | ### Roblox.Authentication.Api.Models.RecoverUsernameRequest | Property | Type | Required | Description | |----------|------|----------|-------------| | `targetType` | `0 \| 1 \| 2` | No | ['Email' = 0, 'PhoneNumber' = 1, 'RecoverySessionID' = 2] | | `target` | `string` | No | | ### Roblox.Authentication.Api.Models.RecoverUsernameResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `transmissionType` | `0 \| 1` | No | | ### Roblox.Authentication.Api.Models.RecoveryMetadataResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `isOnPhone` | `boolean` | No | | | `codeLength` | `integer` | No | | | `isPhoneFeatureEnabledForUsername` | `boolean` | No | | | `isPhoneFeatureEnabledForPassword` | `boolean` | No | | | `isBedev2CaptchaEnabledForPasswordReset` | `boolean` | No | | | `isUsernameRecoveryDeprecated` | `boolean` | No | | ### Roblox.Authentication.Api.Models.ReferralDataModel | Property | Type | Required | Description | |----------|------|----------|-------------| | `acquisitionTime` | `string` | No | | | `acquisitionReferrer` | `string` | No | | | `medium` | `string` | No | | | `source` | `string` | No | | | `campaign` | `string` | No | | | `adGroup` | `string` | No | | | `keyword` | `string` | No | | | `matchType` | `string` | No | | | `sendInfo` | `boolean` | No | | | `requestSessionId` | `string` | No | | | `offerId` | `string` | No | | ### Roblox.Authentication.Api.Models.Request.AuditContentValue | Property | Type | Required | Description | |----------|------|----------|-------------| | `translationKey` | `string` | No | | | `translationNamespace` | `string` | No | | | `translatedSourceString` | `string` | No | | | `parameters` | `object` | No | | ### Roblox.Authentication.Api.Models.Request.AuditSystemContent | Property | Type | Required | Description | |----------|------|----------|-------------| | `capturedAuditContent` | `object` | No | | | `additionalAuditContent` | `object` | No | | ### Roblox.Authentication.Api.Models.Request.IdentityVerificationLoginRequest | Property | Type | Required | Description | |----------|------|----------|-------------| | `loginTicket` | `string` | No | | | `resultToken` | `string` | No | | ### Roblox.Authentication.Api.Models.Request.LogoutFromAllSessionsAndReauthenticateRequest | Property | Type | Required | Description | |----------|------|----------|-------------| | `SecureAuthenticationIntent` | `Roblox.Authentication.Api.Models.Request.SecureAuthenticationIntentModel` | No | | ### Roblox.Authentication.Api.Models.Request.OtpSessionModel | Property | Type | Required | Description | |----------|------|----------|-------------| | `otpSessionToken` | `string` | No | | | `otpContactType` | `1 \| 2` | No | ['Unset' = 1, 'Email' = 2] | ### Roblox.Authentication.Api.Models.Request.SecureAuthenticationIntentModel | Property | Type | Required | Description | |----------|------|----------|-------------| | `clientPublicKey` | `string` | No | | | `clientEpochTimestamp` | `integer` | No | | | `saiSignature` | `string` | No | | | `serverNonce` | `string` | No | | ### Roblox.Authentication.Api.Models.RevertAccountInfoResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `isTwoStepVerificationEnabled` | `boolean` | No | | | `isEmailVerified` | `boolean` | No | | | `isEmailChanged` | `boolean` | No | | | `isPhoneVerified` | `boolean` | No | | | `userId` | `integer` | No | | | `username` | `string` | No | | | `ticket` | `string` | No | | ### Roblox.Authentication.Api.Models.RevertAccountSubmitRequest | Property | Type | Required | Description | |----------|------|----------|-------------| | `UserId` | `integer` | No | | | `NewPassword` | `string` | No | | | `NewPasswordRepeated` | `string` | No | | | `Ticket` | `string` | No | | | `TwoStepVerificationChallengeId` | `string` | No | | | `TwoStepVerificationToken` | `string` | No | | ### Roblox.Authentication.Api.Models.SendResetPasswordRequest | Property | Type | Required | Description | |----------|------|----------|-------------| | `targetType` | `0 \| 1 \| 2` | No | ['Email' = 0, 'PhoneNumber' = 1, 'RecoverySessionID' = 2] | | `target` | `string` | No | | | `captchaId` | `string` | No | | | `captchaToken` | `string` | No | | | `captchaProvider` | `string` | No | | | `challengeId` | `string` | No | | ### Roblox.Authentication.Api.Models.SendResetPasswordResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `nonce` | `string` | No | | | `transmissionType` | `0 \| 1` | No | ['Sms' = 0, 'Email' = 1] | ### Roblox.Authentication.Api.Models.SignupRequest | Property | Type | Required | Description | |----------|------|----------|-------------| | `username` | `string` | No | | | `password` | `string` | No | | | `gender` | `1 \| 2 \| 3` | No | ['Unknown' = 1, 'Male' = 2, 'Female' = 3] | | `birthday` | `string` | No | | | `displayName` | `string` | No | | | `isTosAgreementBoxChecked` | `boolean` | No | | | `email` | `string` | No | | | `locale` | `string` | No | | | `assetIds` | `integer[]` | No | | | `bodyColorId` | `integer` | No | | | `bodyTypeScale` | `number` | No | | | `headScale` | `number` | No | | | `heightScale` | `number` | No | | | `widthScale` | `number` | No | | | `proportionScale` | `number` | No | | | `referralData` | `Roblox.Authentication.Api.Models.ReferralDataModel` | No | | | `agreementIds` | `string[]` | No | | | `identityVerificationResultToken` | `string` | No | | | `secureAuthenticationIntent` | `Roblox.Authentication.Api.Models.Request.SecureAuthenticationIntentModel` | No | | | `otpSession` | `Roblox.Authentication.Api.Models.Request.OtpSessionModel` | No | | | `dataToken` | `string` | No | | | `accountBlob` | `string` | No | | | `passkeySessionId` | `string` | No | | | `passkeyRegistrationResponse` | `string` | No | | | `accountLinkParameters` | `Roblox.Authentication.Api.Models.AccountLinkParameters` | No | | | `auditSystemContent` | `Roblox.Authentication.Api.Models.Request.AuditSystemContent` | No | | | `captchaId` | `string` | No | | | `captchaToken` | `string` | No | | | `captchaProvider` | `string` | No | | | `challengeId` | `string` | No | | ### Roblox.Authentication.Api.Models.SignupResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `userId` | `integer` | No | | | `starterPlaceId` | `integer` | No | | | `returnUrl` | `string` | No | | | `accountBlob` | `string` | No | | ### Roblox.Authentication.Api.Models.TwoStepVerificationSentResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `mediaType` | `integer enum (8 values)` | No | ['Email' = 0, 'SMS' = 1, 'Authenticator' = 2, 'RecoveryCode' = 3, 'SecurityKey' = 4, 'CrossDevice' = 5, 'Password' = 6, 'Passkey' = 7] Values: 0, 1, 2, 3, 4, 5, 6, 7 | | `ticket` | `string` | No | | ### Roblox.Authentication.Api.Models.UsernameChangePriceResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `priceInRobux` | `integer` | No | | | `basePriceInRobux` | `integer` | No | | ### Roblox.Authentication.Api.Models.UsernameChangeRequest | Property | Type | Required | Description | |----------|------|----------|-------------| | `username` | `string` | No | | | `password` | `string` | No | | ### Roblox.Authentication.Api.Models.UsernameValidationRequest | Property | Type | Required | Description | |----------|------|----------|-------------| | `username` | `string` | No | | | `birthday` | `string` | No | | | `context` | `0 \| 1 \| 2` | No | | ### Roblox.Authentication.Api.Models.UsernameValidationResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `code` | `integer enum (10 values)` | No | ['ValidUsername' = 0, 'AlreadyInUseError' = 1, 'ModerationError' = 2, 'InvalidLengthError' = 3, 'StartsOrEndsWithUnderscoreError' = 4, 'TooManyUnderscoresError' = 5, 'ContainsSpacesError' = 6, 'InvalidCharactersError' = 7, 'ContainsPiiError' = 10, 'ContainsReservedUsernameError' = 12] Values: 0, 1, 2, 3, 4, 5, 6, 7, 10, 12 | | `message` | `string` | No | | ### Roblox.Authentication.Api.Models.UsernamesResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `usernames` | `string[]` | No | | ### Roblox.Web.Responses.Users.LegacyUserResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `userId` | `integer` | No | | | `username` | `string` | No | | | `displayName` | `string` | No | | ### Roblox.Web.Responses.Users.SkinnyUserResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | | | `name` | `string` | No | | | `displayName` | `string` | No | | --- name: "Authentication Api v3" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://auth.roblox.com" version: "v3" endpoints: 2 auth: [cookie] --- # Authentication Api v3 **API Version:** v3 **Base URL:** `https://auth.roblox.com` ## Endpoints ### POST `/v3/logout` Destroys the current authentication session while reporting metrics like url and reason for logout. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.Models.Request.LogoutV3Request` See [Roblox.Authentication.Api.Models.Request.LogoutV3Request](#roblox-authentication-api-models-request-logoutv3request) in Models. **Request example:** ```json { "logoutReason": "string", "url": "string", "userId": "string" } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v3/logout" \ -H "Content-Type: application/json" \ -d '{ "logoutReason": "string", "url": "string", "userId": "string" }' ``` ### POST `/v3/users/{userId}/two-step-verification/login` Authenticates as a user given a two step verification verification token. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user ID to authenticate as. | **Request Body:** `application/json` — Type: `Roblox.Authentication.Api.TwoStepVerificationLoginRequest` See [Roblox.Authentication.Api.TwoStepVerificationLoginRequest](#roblox-authentication-api-twostepverificationloginrequest) in Models. **Request example:** ```json { "challengeId": "string", "verificationToken": "string", "rememberDevice": false, "secureAuthenticationIntent": { "clientPublicKey": "string", "clientEpochTimestamp": 0, "saiSignature": "string", "serverNonce": "string" }, "accountBlob": "string", "accountLinkParameters": { "LinkingPlatform": 0 } } ``` **Responses:** - `200`: OK → `Roblox.Authentication.Api.Models.TwoStepVerificationV3LoginResponse` - `400`: 1: User is invalid. 5: Invalid two step verification ticket. 10: Invalid verification token. - `403`: 0: Token Validation Failed 11: Maxium logged in accounts limit reached. **Response fields** (`Roblox.Authentication.Api.Models.TwoStepVerificationV3LoginResponse`) See [Roblox.Authentication.Api.Models.TwoStepVerificationV3LoginResponse](#roblox-authentication-api-models-twostepverificationv3loginresponse) in Models. **Response example:** ```json { "identityVerificationLoginTicket": "string", "accountBlob": "string" } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://auth.roblox.com/v3/users/{USERID}/two-step-verification/login" \ -H "Content-Type: application/json" \ -d '{"challengeId":"string","verificationToken":"string","rememberDevice":false,"secureAuthenticationIntent":{"clientPublicKey":"string","clientEpochTimestamp":0,"saiSignature":"string","serverNonce":"string"},"accountBlob":"string","accountLinkParameters":{"LinkingPlatform":0}}' ``` ## Models ### Roblox.Authentication.Api.Models.AccountLinkParameters | Property | Type | Required | Description | |----------|------|----------|-------------| | `LinkingPlatform` | `integer enum (10 values)` | No | ['Invalid' = 0, 'Xbox' = 1, 'Qq' = 2, 'WeChat' = 3, 'Facebook' = 4, 'RobloxDeveloper' = 5, 'RobloxGroupCreator' = 6, 'Playstation' = 7, 'ExternalProvider' = 8, 'Example' = 999] Values: 0, 1, 2, 3, 4, 5, 6, 7, 8, 999 | ### Roblox.Authentication.Api.Models.Request.LogoutV3Request | Property | Type | Required | Description | |----------|------|----------|-------------| | `logoutReason` | `string` | No | | | `url` | `string` | No | | | `userId` | `string` | No | | ### Roblox.Authentication.Api.Models.Request.SecureAuthenticationIntentModel | Property | Type | Required | Description | |----------|------|----------|-------------| | `clientPublicKey` | `string` | No | | | `clientEpochTimestamp` | `integer` | No | | | `saiSignature` | `string` | No | | | `serverNonce` | `string` | No | | ### Roblox.Authentication.Api.Models.TwoStepVerificationV3LoginResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `identityVerificationLoginTicket` | `string` | No | | | `accountBlob` | `string` | No | | ### Roblox.Authentication.Api.TwoStepVerificationLoginRequest | Property | Type | Required | Description | |----------|------|----------|-------------| | `challengeId` | `string` | No | | | `verificationToken` | `string` | No | | | `rememberDevice` | `boolean` | No | | | `secureAuthenticationIntent` | `Roblox.Authentication.Api.Models.Request.SecureAuthenticationIntentModel` | No | | | `accountBlob` | `string` | No | | | `accountLinkParameters` | `Roblox.Authentication.Api.Models.AccountLinkParameters` | No | | --- name: "Avatar Api v1" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://avatar.roblox.com" version: "v1" endpoints: 13 auth: [cookie] --- # Avatar Api v1 **API Version:** v1 **Base URL:** `https://avatar.roblox.com` ## Endpoints ### GET `/v1/avatar` Returns details about the authenticated user's avatar. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `Roblox-Place-Id` | header | `integer (int64)` | No | | **Responses:** - `200`: OK → `Roblox.Api.Avatar.Models.AvatarModelV2` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Api.Avatar.Models.AvatarModelV2`) See [Roblox.Api.Avatar.Models.AvatarModelV2](#roblox-api-avatar-models-avatarmodelv2) in Models. **Response example:** ```json { "scales": { "height": 0, "width": 0, "head": 0, "depth": 0, "proportion": 0, "bodyType": 0 }, "playerAvatarType": 1, "bodyColors": { "headColorId": 0, "torsoColorId": 0, "rightArmColorId": 0, "leftArmColorId": 0, "rightLegColorId": 0, "leftLegColorId": 0 }, "assets": [ { "id": "...", "name": "...", "assetType": "...", "currentVersionId": "...", "meta": "...", "availabilityStatus": "..." } ], "defaultShirtApplied": false, "defaultPantsApplied": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://avatar.roblox.com/v1/avatar" ``` ### GET `/v1/avatar-rules` Returns the business rules related to avatars. BodyColorsPalette is a list of valid brickColors you can choose for your avatar. WearableAssetTypes contains a list of asset types with names, ids, and the maximum number that you can wear at a time. Does not include packages because they cannot be worn on your avatar directly. PlayerAvatarTypes are the types of avatars you can choose between. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `Roblox-Place-Id` | header | `integer (int64)` | No | | **Responses:** - `200`: OK → `Roblox.Api.Avatar.Models.AvatarRulesModel` **Response fields** (`Roblox.Api.Avatar.Models.AvatarRulesModel`) See [Roblox.Api.Avatar.Models.AvatarRulesModel](#roblox-api-avatar-models-avatarrulesmodel) in Models. **Response example:** ```json { "playerAvatarTypes": [ 1 ], "scales": "...", "wearableAssetTypes": [ { "maxNumber": "...", "id": "...", "name": "..." } ], "accessoryRefinementTypes": [ 0 ], "accessoryRefinementLowerBounds": "...", "accessoryRefinementUpperBounds": "..." } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://avatar.roblox.com/v1/avatar-rules" ``` ### GET `/v1/avatar/metadata` Returns metadata used by the avatar page of the website. **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Api.Avatar.Models.AvatarMetadataModel` **Response fields** (`Roblox.Api.Avatar.Models.AvatarMetadataModel`) See [Roblox.Api.Avatar.Models.AvatarMetadataModel](#roblox-api-avatar-models-avatarmetadatamodel) in Models. **Response example:** ```json { "enableDefaultClothingMessage": false, "isAvatarScaleEmbeddedInTab": false, "isBodyTypeScaleOutOfTab": false, "scaleHeightIncrement": 0, "scaleWidthIncrement": 0, "scaleHeadIncrement": 0 } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://avatar.roblox.com/v1/avatar/metadata" ``` ### GET `/v1/game-start-info` The server will call this on game server start to request general information about the universe This is version 1.1, which returns an entry from the UniverseAvatarType enum. During mixed mode this may return unreliable results. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | query | `integer (int64)` | Yes | | | `Roblox-Place-Id` | header | `integer (int64)` | No | | **Responses:** - `200`: OK → `Roblox.Api.Avatar.Models.GameStartInfoResponse` **Response fields** (`Roblox.Api.Avatar.Models.GameStartInfoResponse`) See [Roblox.Api.Avatar.Models.GameStartInfoResponse](#roblox-api-avatar-models-gamestartinforesponse) in Models. **Response example:** ```json { "gameAvatarType": "string", "allowCustomAnimations": "string", "universeAvatarCollisionType": "string", "universeAvatarBodyType": "string", "jointPositioningType": "string", "message": "string" } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://avatar.roblox.com/v1/game-start-info?universeId={VALUE}" ``` ### GET `/v1/outfits/{userOutfitId}/details` Gets details about the contents of an outfit. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userOutfitId` | path | `integer (int64)` | Yes | The user outfit id. | | `Roblox-Place-Id` | header | `integer (int64)` | No | | **Responses:** - `200`: OK → `Roblox.Api.Avatar.Models.OutfitDetailsModel` - `400`: 2: The outfit for the specified userOutfit is invalid. - `403`: 3: The requester does not have access to the details for the given user outfit. - `404`: 1: The specified userOutfitId is invalid. **Response fields** (`Roblox.Api.Avatar.Models.OutfitDetailsModel`) See [Roblox.Api.Avatar.Models.OutfitDetailsModel](#roblox-api-avatar-models-outfitdetailsmodel) in Models. **Response example:** ```json { "id": 0, "universeId": 0, "name": "string", "assets": [ { "id": "...", "name": "...", "assetType": "...", "currentVersionId": "...", "meta": "...", "availabilityStatus": "..." } ], "bodyColors": { "headColorId": 0, "torsoColorId": 0, "rightArmColorId": 0, "leftArmColorId": 0, "rightLegColorId": 0, "leftLegColorId": 0 }, "scale": { "height": 0, "width": 0, "head": 0, "depth": 0, "proportion": 0, "bodyType": 0 } } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://avatar.roblox.com/v1/outfits/{USEROUTFITID}/details" ``` ### GET `/v1/users/{userId}/avatar` Returns details about a specified user's avatar. Includes assets, bodycolors, and playerAvatarType. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | | | `Roblox-Place-Id` | header | `integer (int64)` | No | | **Responses:** - `200`: OK → `Roblox.Api.Avatar.Models.AvatarModelV2` - `400`: 1: The specified user does not exist. 2: An account for the given userId does not exist! **Response fields** (`Roblox.Api.Avatar.Models.AvatarModelV2`) See [Roblox.Api.Avatar.Models.AvatarModelV2](#roblox-api-avatar-models-avatarmodelv2) in Models. **Response example:** ```json { "scales": { "height": 0, "width": 0, "head": 0, "depth": 0, "proportion": 0, "bodyType": 0 }, "playerAvatarType": 1, "bodyColors": { "headColorId": 0, "torsoColorId": 0, "rightArmColorId": 0, "leftArmColorId": 0, "rightLegColorId": 0, "leftLegColorId": 0 }, "assets": [ { "id": "...", "name": "...", "assetType": "...", "currentVersionId": "...", "meta": "...", "availabilityStatus": "..." } ], "defaultShirtApplied": false, "defaultPantsApplied": false } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://avatar.roblox.com/v1/users/{USERID}/avatar" ``` ### GET `/v1/users/{userId}/currently-wearing` Gets a list of asset ids that the user is currently wearing. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user id. | | `Roblox-Place-Id` | header | `integer (int64)` | No | | **Responses:** - `200`: OK → `Roblox.Api.Avatar.Models.AssetIdListModel` - `400`: 1: The specified user does not exist. 2: An account for the given userId does not exist! **Response fields** (`Roblox.Api.Avatar.Models.AssetIdListModel`) See [Roblox.Api.Avatar.Models.AssetIdListModel](#roblox-api-avatar-models-assetidlistmodel) in Models. **Response example:** ```json { "assetIds": [ 0 ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://avatar.roblox.com/v1/users/{USERID}/currently-wearing" ``` ### GET `/v1/users/{userId}/outfits` Deprecated, user v2. Gets a list of outfits for the specified user. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user id. | | `outfitType` | query | `string` | No | The outfit type being searched for, null will return all outfitTypes. | | `page` | query | `integer (int32)` | No | The page number of the current page of requests, default is 1. | | `itemsPerPage` | query | `integer (int32)` | No | The max number of outfits that can be returned. | | `isEditable` | query | `boolean` | No | Whether the outfits are editable. A null value will lead to no filtering. | | `Roblox-Place-Id` | header | `integer (int64)` | No | The placeId of the caller, not required to be passed in. | **Responses:** - `200`: OK → `Roblox.Api.Avatar.Models.AvatarFilteredPageResponse[Roblox.Api.Avatar.Models.OutfitModel]` - `400`: 1: The specified user does not exist. 2: An account for the given userId does not exist! **Response fields** (`Roblox.Api.Avatar.Models.AvatarFilteredPageResponse[Roblox.Api.Avatar.Models.OutfitModel]`) See [Roblox.Api.Avatar.Models.AvatarFilteredPageResponse[Roblox.Api.Avatar.Models.OutfitModel]](#roblox-api-avatar-models-avatarfilteredpageresponse-roblox-api-avatar-models-outfitmodel-) in Models. **Response example:** ```json { "filteredCount": 0, "data": [ { "id": "...", "name": "...", "isEditable": "...", "outfitType": "..." } ], "total": 0 } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://avatar.roblox.com/v1/users/{USERID}/outfits" ``` ### POST `/v1/avatar/redraw-thumbnail` Requests the authenticated user's thumbnail be redrawn. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `Roblox-Place-Id` | header | `integer (int64)` | No | | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `429`: 1: Redrawing your avatar thumbnail is floodchecked at this time. 1: Redrawing your avatar thumbnail is floodchecked at this time **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://avatar.roblox.com/v1/avatar/redraw-thumbnail" ``` ### POST `/v1/avatar/set-body-colors` Sets the authenticated user's body colors. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `Roblox-Place-Id` | header | `integer (int64)` | No | | **Request Body:** `application/json` — Type: `Roblox.Api.Avatar.Models.BodyColorsModel` See [Roblox.Api.Avatar.Models.BodyColorsModel](#roblox-api-avatar-models-bodycolorsmodel) in Models. **Request example:** ```json { "headColorId": 0, "torsoColorId": 0, "rightArmColorId": 0, "leftArmColorId": 0, "rightLegColorId": 0, "leftLegColorId": 0 } ``` **Responses:** - `200`: OK → `Roblox.Api.Avatar.Models.AvatarApiSuccessResponse` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Api.Avatar.Models.AvatarApiSuccessResponse`) See [Roblox.Api.Avatar.Models.AvatarApiSuccessResponse](#roblox-api-avatar-models-avatarapisuccessresponse) in Models. **Response example:** ```json { "success": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://avatar.roblox.com/v1/avatar/set-body-colors" \ -H "Content-Type: application/json" \ -d '{ "headColorId": 0, "torsoColorId": 0, "rightArmColorId": 0, "leftArmColorId": 0, "rightLegColorId": 0, "leftLegColorId": 0 }' ``` ### POST `/v1/avatar/set-player-avatar-type` Sets the authenticated user's player avatar type (e.g. R6 or R15). This is the avatar type chosen on the Avatar page. Some games can override this and force your character to be R6 or R15. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `Roblox-Place-Id` | header | `integer (int64)` | No | | **Request Body:** `application/json` — Type: `Roblox.Api.Avatar.Models.PlayerAvatarTypeModel` See [Roblox.Api.Avatar.Models.PlayerAvatarTypeModel](#roblox-api-avatar-models-playeravatartypemodel) in Models. **Request example:** ```json { "playerAvatarType": 1 } ``` **Responses:** - `200`: OK → `Roblox.Api.Avatar.Models.AvatarApiSuccessResponse` - `400`: 1: Invalid playerAvatarType. Valid values are: - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: You are not allowed to change player avatar type. **Response fields** (`Roblox.Api.Avatar.Models.AvatarApiSuccessResponse`) See [Roblox.Api.Avatar.Models.AvatarApiSuccessResponse](#roblox-api-avatar-models-avatarapisuccessresponse) in Models. **Response example:** ```json { "success": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://avatar.roblox.com/v1/avatar/set-player-avatar-type" \ -H "Content-Type: application/json" \ -d '{ "playerAvatarType": 1 }' ``` ### POST `/v1/avatar/set-scales` Sets the authenticated user's scales. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `Roblox-Place-Id` | header | `integer (int64)` | No | | **Request Body:** `application/json` — Type: `Roblox.Web.Responses.Avatar.ScaleModel` See [Roblox.Web.Responses.Avatar.ScaleModel](#roblox-web-responses-avatar-scalemodel) in Models. **Request example:** ```json { "height": 0, "width": 0, "head": 0, "depth": 0, "proportion": 0, "bodyType": 0 } ``` **Responses:** - `200`: OK → `Roblox.Api.Avatar.Models.AvatarApiSuccessResponse` - `400`: 1: Please pass in the scales JSON - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 3: The user does not have permissions to change scales. **Response fields** (`Roblox.Api.Avatar.Models.AvatarApiSuccessResponse`) See [Roblox.Api.Avatar.Models.AvatarApiSuccessResponse](#roblox-api-avatar-models-avatarapisuccessresponse) in Models. **Response example:** ```json { "success": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://avatar.roblox.com/v1/avatar/set-scales" \ -H "Content-Type: application/json" \ -d '{ "height": 0, "width": 0, "head": 0, "depth": 0, "proportion": 0, "bodyType": 0 }' ``` ### POST `/v1/outfits/{userOutfitId}/delete` Deletes the outfit. You are only allowed to delete outfits you created. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userOutfitId` | path | `integer (int64)` | Yes | The user outfit id. | | `Roblox-Place-Id` | header | `integer (int64)` | No | | **Responses:** - `200`: OK → `Roblox.Api.Avatar.Models.AvatarApiSuccessResponse` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: You don't have permission to delete this outfit. - `404`: 1: The specified userOutfitId is invalid! - `500`: 3: An error occurred while deleting the outfit. **Response fields** (`Roblox.Api.Avatar.Models.AvatarApiSuccessResponse`) See [Roblox.Api.Avatar.Models.AvatarApiSuccessResponse](#roblox-api-avatar-models-avatarapisuccessresponse) in Models. **Response example:** ```json { "success": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://avatar.roblox.com/v1/outfits/{USEROUTFITID}/delete" ``` ## Models ### Roblox.Api.Avatar.Models.AccessoryPositionModel A model which contains accessory position coordinates | Property | Type | Required | Description | |----------|------|----------|-------------| | `xPosition` | `number` | No | Float that determines the absolute position refinement in studs that this accessory is relocated in the x-direction | | `yPosition` | `number` | No | Float that determines the absolute position refinement in studs that this accessory is relocated in the y-direction | | `zPosition` | `number` | No | Float that determines the absolute position refinement in studs that this accessory is relocated in the z-direction | ### Roblox.Api.Avatar.Models.AccessoryRefinementModel A model containing details about accessory refinement data | Property | Type | Required | Description | |----------|------|----------|-------------| | `position` | `Roblox.Api.Avatar.Models.AccessoryPositionModel` | No | | | `rotation` | `Roblox.Api.Avatar.Models.AccessoryRotationModel` | No | | | `scale` | `Roblox.Api.Avatar.Models.AccessoryScaleModel` | No | | ### Roblox.Api.Avatar.Models.AccessoryRotationModel A model which contains accessory rotation coordinates | Property | Type | Required | Description | |----------|------|----------|-------------| | `xRotation` | `number` | No | Float from -180 to 180 that determines the absolute rotation refinement that this accessory is rotated in the x-direction | | `yRotation` | `number` | No | Float from -180 to 180 that determines the absolute rotation refinement that this accessory is rotated in the y-direction | | `zRotation` | `number` | No | Float from -180 to 180 that determines the absolute rotation refinement that this accessory is rotated in the z-direction | ### Roblox.Api.Avatar.Models.AccessoryScaleModel A model which contains accessory Scale, that is the multiplier with respect to the default size. For example, scale = 2 means twice the default size of an asset | Property | Type | Required | Description | |----------|------|----------|-------------| | `xScale` | `number` | No | Float that determines the scale refinement that this accessory is resized in the x-direction Default size is 1 | | `yScale` | `number` | No | Float that determines the scale refinement that this accessory is resized in the y-direction Default size is 1 | | `zScale` | `number` | No | Float that determines the scale refinement that this accessory is resized in the z-direction Default size is 1 | ### Roblox.Api.Avatar.Models.AssetIdListModel A model that contains a list of asset ids | Property | Type | Required | Description | |----------|------|----------|-------------| | `assetIds` | `integer[]` | No | The asset ids | ### Roblox.Api.Avatar.Models.AssetMetaModelV1 Exhaustive model denoting all possible metadata fields of an asset | Property | Type | Required | Description | |----------|------|----------|-------------| | `order` | `integer` | No | Layered-clothing order | | `puffiness` | `number` | No | Layered-clothing puffiness | | `position` | `Roblox.Api.Avatar.Models.AssetPosition` | No | | | `rotation` | `Roblox.Api.Avatar.Models.AssetRotation` | No | | | `scale` | `Roblox.Api.Avatar.Models.AssetScale` | No | | | `headShape` | `integer enum (30 values)` | No | Head Shape selected for the asset id. Applicable for dynamic head assets. Values: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29 | | `staticFacialAnimation` | `boolean` | No | Indicates user choice for facial animation. staticFacialAnimation=false, implies the toggle as on and face will animate. Applicable for dynamic head assets. | | `version` | `integer` | No | Client-authoritative meta model format version - default is always 1 | ### Roblox.Api.Avatar.Models.AssetModelV2 A model containing details about an asset - V2: adds CurrentVersionId, AssetMetaModel | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | The id | | `name` | `string` | No | The name | | `assetType` | `Roblox.Api.Avatar.Models.AssetTypeModel` | No | | | `currentVersionId` | `integer` | No | Id of the current version of asset | | `meta` | `Roblox.Api.Avatar.Models.AssetMetaModelV1` | No | | | `availabilityStatus` | `string` | No | Asset availability status. | | `expirationTime` | `string` | No | For rental assets only. (Future) ownership expiration time of the asset. | | `supportsHeadShapes` | `boolean` | No | If the "Id" is swappable, applicable for DH assets. | ### Roblox.Api.Avatar.Models.AssetPosition A model which contains accessory position coordinates. | Property | Type | Required | Description | |----------|------|----------|-------------| | `X` | `number` | No | X coordinate of the position. | | `Y` | `number` | No | y coordinate of the position. | | `Z` | `number` | No | Z coordinate of the position. | ### Roblox.Api.Avatar.Models.AssetRotation A model which contains accessory rotation coordinates. | Property | Type | Required | Description | |----------|------|----------|-------------| | `X` | `number` | No | X degrees of the rotation. | | `Y` | `number` | No | Y degrees of the rotation. | | `Z` | `number` | No | Z degrees of the rotation. | ### Roblox.Api.Avatar.Models.AssetScale A model which contains accessory scale. | Property | Type | Required | Description | |----------|------|----------|-------------| | `X` | `number` | No | X scale multiplier of accessory. | | `Y` | `number` | No | Y scale multiplier of accessory. | | `Z` | `number` | No | Z scale multiplier of accessory. | ### Roblox.Api.Avatar.Models.AssetTypeModel A model containing details about an asset type | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | The id | | `name` | `string` | No | The name | ### Roblox.Api.Avatar.Models.AssetTypeRulesModel A model containing details about an asset type and its business rules | Property | Type | Required | Description | |----------|------|----------|-------------| | `maxNumber` | `integer` | No | The max number of this asset type that can be worn | | `id` | `integer` | No | The id | | `name` | `string` | No | The name | ### Roblox.Api.Avatar.Models.AvatarApiSuccessResponse Success response class | Property | Type | Required | Description | |----------|------|----------|-------------| | `success` | `boolean` | No | Gets or sets a value indicating whether the request was a success. | ### Roblox.Api.Avatar.Models.AvatarFilteredPageResponse[Roblox.Api.Avatar.Models.OutfitModel] Filtered page response | Property | Type | Required | Description | |----------|------|----------|-------------| | `filteredCount` | `integer` | No | Number of !:TPagedObject filtered. | | `data` | `Roblox.Api.Avatar.Models.OutfitModel[]` | No | | | `total` | `integer` | No | | ### Roblox.Api.Avatar.Models.AvatarMetadataModel A model containing website metadata for avatars | Property | Type | Required | Description | |----------|------|----------|-------------| | `enableDefaultClothingMessage` | `boolean` | No | Whether or not to show the Default Clothing message | | `isAvatarScaleEmbeddedInTab` | `boolean` | No | Whether or not the Scales is embedded in the tab | | `isBodyTypeScaleOutOfTab` | `boolean` | No | Whether or not the Boby Type scale is embedded in the tab | | `scaleHeightIncrement` | `number` | No | How much the height scaler should increment by | | `scaleWidthIncrement` | `number` | No | How much the width scaler should increment by | | `scaleHeadIncrement` | `number` | No | How much the head scaler should increment by | | `scaleProportionIncrement` | `number` | No | How much the proportion scaler should increment by | | `scaleBodyTypeIncrement` | `number` | No | How much the body type scaler should increment by | | `supportProportionAndBodyType` | `boolean` | No | Whether or not to support proportion and body type | | `showDefaultClothingMessageOnPageLoad` | `boolean` | No | Whether or not to show the default clothing message when the page loads | | `areThreeDeeThumbsEnabled` | `boolean` | No | Whether or not 3D thumbnails are shown | | `isAvatarWearingApiCallsLockingOnFrontendEnabled` | `boolean` | No | Does the frontend lock avatar editor input until the wearing call returns | | `isOutfitHandlingOnFrontendEnabled` | `boolean` | No | Does the frontend lock avatar editor input until the wearing call returns | | `isJustinUiChangesEnabled` | `boolean` | No | Determines whether a bunch of UI improvements are released | | `isCategoryReorgEnabled` | `boolean` | No | Determines whether Category Reorg is released | | `LCEnabledInEditorAndCatalog` | `boolean` | No | Flag for both web UI and App, name is fixed due to sharing, do not change | | `isLCCompletelyEnabled` | `boolean` | No | Useful for the time between enabling Jackets for most users and all LC types for everyone, meanwhile Soothsayers need all types at all times | ### Roblox.Api.Avatar.Models.AvatarModelV2 A model containing details about an avatar | Property | Type | Required | Description | |----------|------|----------|-------------| | `scales` | `Roblox.Web.Responses.Avatar.ScaleModel` | No | | | `playerAvatarType` | `1 \| 3` | No | The avatar type | | `bodyColors` | `Roblox.Api.Avatar.Models.BodyColorsModel` | No | | | `assets` | `Roblox.Api.Avatar.Models.AssetModelV2[]` | No | The assets worn on the character | | `defaultShirtApplied` | `boolean` | No | Whether default clothing has been applied to this avatar. | | `defaultPantsApplied` | `boolean` | No | Whether default clothing has been applied to this avatar. | | `emotes` | `Roblox.Api.Avatar.Models.EmoteResponseModel[]` | No | The emotes on the character | ### Roblox.Api.Avatar.Models.AvatarRulesModel A model containing details about avatar-related business rules | Property | Type | Required | Description | |----------|------|----------|-------------| | `playerAvatarTypes` | `1 \| 3[]` | No | The avatar type | | `scales` | `object` | No | The scales | | `wearableAssetTypes` | `Roblox.Api.Avatar.Models.AssetTypeRulesModel[]` | No | The assets worn on the character | | `accessoryRefinementTypes` | `integer[]` | No | The list of asset type ids for Accessory Refinement. | | `accessoryRefinementLowerBounds` | `object` | No | The lower bounds for accessory refinement settings. | | `accessoryRefinementUpperBounds` | `object` | No | The lower bounds for accessory refinement settings. | | `bodyColorsPalette` | `Roblox.Api.Avatar.Models.BodyColorModel[]` | No | The full set of usable body colors | | `basicBodyColorsPalette` | `Roblox.Api.Avatar.Models.BodyColorModel[]` | No | The basic set of body colors | | `minimumDeltaEBodyColorDifference` | `number` | No | The minimum Delta-E difference in body colors for default clothing not to be applied | | `proportionsAndBodyTypeEnabledForUser` | `boolean` | No | Whether proportion and bodyType scales are allowed to be set by the authenticated user | | `defaultClothingAssetLists` | `Roblox.Api.Avatar.Models.DefaultClothingAssets` | No | | | `bundlesEnabledForUser` | `boolean` | No | Whether or not bundles are enabled for the specific user | | `emotesEnabledForUser` | `boolean` | No | Whether or not emotes are enabled | ### Roblox.Api.Avatar.Models.BodyColorModel A model container BrickColor ids for each body part. | Property | Type | Required | Description | |----------|------|----------|-------------| | `brickColorId` | `integer` | No | The BrickColor id | | `hexColor` | `string` | No | The hex color, e.g. #FFFFFF | | `name` | `string` | No | The name of the BrickColor | ### Roblox.Api.Avatar.Models.BodyColorsModel A model container BrickColor ids for each body part. | Property | Type | Required | Description | |----------|------|----------|-------------| | `headColorId` | `integer` | No | The BrickColor id for head color | | `torsoColorId` | `integer` | No | The BrickColor id for torso color | | `rightArmColorId` | `integer` | No | The BrickColor id for right arm color | | `leftArmColorId` | `integer` | No | The BrickColor id for left arm color | | `rightLegColorId` | `integer` | No | The BrickColor id for right leg color | | `leftLegColorId` | `integer` | No | The BrickColor id for left leg color | ### Roblox.Api.Avatar.Models.DefaultClothingAssets A model containing details about avatar-related business rules | Property | Type | Required | Description | |----------|------|----------|-------------| | `defaultShirtAssetIds` | `integer[]` | No | List of asset Ids used to equip shirts for default clothing when the avatar appears nude. | | `defaultPantAssetIds` | `integer[]` | No | List of asset Ids used to equip pants for default clothing when the avatar appears nude. | ### Roblox.Api.Avatar.Models.EmoteResponseModel Response object representing a user's emote | Property | Type | Required | Description | |----------|------|----------|-------------| | `assetId` | `integer` | No | The asset id of the emote. | | `assetName` | `string` | No | The name of the emote. | | `position` | `integer` | No | The position the emote is equipped to. | ### Roblox.Api.Avatar.Models.GameStartInfoResponse The game start info | Property | Type | Required | Description | |----------|------|----------|-------------| | `gameAvatarType` | `string` | No | Avatar Type | | `allowCustomAnimations` | `string` | No | Custom animation enabled | | `universeAvatarCollisionType` | `string` | No | collision type for the univers | | `universeAvatarBodyType` | `string` | No | Body type for the univers | | `jointPositioningType` | `string` | No | Joing positioning type | | `message` | `string` | No | Mesasge | | `universeAvatarMinScales` | `Roblox.Web.Responses.Avatar.ScaleModel` | No | | | `universeAvatarMaxScales` | `Roblox.Web.Responses.Avatar.ScaleModel` | No | | | `universeAvatarAssetOverrides` | `Roblox.Api.Avatar.Models.UniverseAvatarAssetOverrideResponseModel[]` | No | asset overrides for the univers | | `moderationStatus` | `string` | No | Moderation status | ### Roblox.Api.Avatar.Models.OutfitDetailsModel A model containing details about a user outfit | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | The id | | `universeId` | `integer` | No | The universe id of the outfit, null when outfit is not created in-experience | | `name` | `string` | No | The name | | `assets` | `Roblox.Api.Avatar.Models.AssetModelV2[]` | No | A list of assetIds | | `bodyColors` | `Roblox.Api.Avatar.Models.BodyColorsModel` | No | | | `scale` | `Roblox.Web.Responses.Avatar.ScaleModel` | No | | | `playerAvatarType` | `string` | No | The player avatar type - this can be R6 or R15. | | `outfitType` | `string` | No | The outfit type of the outfit | | `isEditable` | `boolean` | No | Whether the outfit can be edited by the user | | `moderationStatus` | `string` | No | The moderation status of the outfit, not applicable when outfit is created outside experience | ### Roblox.Api.Avatar.Models.OutfitModel A slim model for user outfits. | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | The id. | | `name` | `string` | No | The name. | | `isEditable` | `boolean` | No | Whether the outfit can be modified by the user. | | `outfitType` | `string` | No | The type of the Outfit. | ### Roblox.Api.Avatar.Models.PlayerAvatarTypeModel A model that contains a playerAvatarType | Property | Type | Required | Description | |----------|------|----------|-------------| | `playerAvatarType` | `1 \| 3` | No | The playerAvatarType | ### Roblox.Api.Avatar.Models.ScaleRulesModel A model that contains information about the max/mins for each scale | Property | Type | Required | Description | |----------|------|----------|-------------| | `min` | `number` | No | The min scale | | `max` | `number` | No | The max scale | | `increment` | `number` | No | The increment of the scale | ### Roblox.Api.Avatar.Models.UniverseAvatarAssetOverrideResponseModel | Property | Type | Required | Description | |----------|------|----------|-------------| | `assetID` | `integer` | No | | | `assetTypeID` | `integer` | No | | | `isPlayerChoice` | `boolean` | No | | ### Roblox.Web.Responses.Avatar.ScaleModel | Property | Type | Required | Description | |----------|------|----------|-------------| | `height` | `number` | No | | | `width` | `number` | No | | | `head` | `number` | No | | | `depth` | `number` | No | | | `proportion` | `number` | No | | | `bodyType` | `number` | No | | --- name: "Avatar Api v2" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://avatar.roblox.com" version: "v2" endpoints: 7 auth: [cookie] --- # Avatar Api v2 **API Version:** v2 **Base URL:** `https://avatar.roblox.com` ## Endpoints ### GET `/v2/avatar/avatar` Returns details about the authenticated user's avatar. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `Roblox-Place-Id` | header | `integer (int64)` | No | | | `checkAssetAvailability` | query | `boolean` | No | Whether to return assets with availability status. | **Responses:** - `200`: OK → `Roblox.Api.Avatar.Models.AvatarModelV3` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Api.Avatar.Models.AvatarModelV3`) See [Roblox.Api.Avatar.Models.AvatarModelV3](#roblox-api-avatar-models-avatarmodelv3) in Models. **Response example:** ```json { "scales": { "height": 0, "width": 0, "head": 0, "depth": 0, "proportion": 0, "bodyType": 0 }, "playerAvatarType": 1, "bodyColor3s": { "headColor3": "string", "torsoColor3": "string", "rightArmColor3": "string", "leftArmColor3": "string", "rightLegColor3": "string", "leftLegColor3": "string" }, "assets": [ { "id": "...", "name": "...", "assetType": "...", "currentVersionId": "...", "meta": "...", "availabilityStatus": "..." } ], "defaultShirtApplied": false, "defaultPantsApplied": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://avatar.roblox.com/v2/avatar/avatar" ``` ### GET `/v2/avatar/users/{userId}/avatar` Returns details about a specified user's avatar. Includes assets, bodycolors, and playerAvatarType. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | | | `Roblox-Place-Id` | header | `integer (int64)` | No | | | `checkAssetAvailability` | query | `boolean` | No | Whether to return assets with availability status. | **Responses:** - `200`: OK → `Roblox.Api.Avatar.Models.AvatarModelV3` - `400`: 1: The specified user does not exist. 2: An account for the given userId does not exist! **Response fields** (`Roblox.Api.Avatar.Models.AvatarModelV3`) See [Roblox.Api.Avatar.Models.AvatarModelV3](#roblox-api-avatar-models-avatarmodelv3) in Models. **Response example:** ```json { "scales": { "height": 0, "width": 0, "head": 0, "depth": 0, "proportion": 0, "bodyType": 0 }, "playerAvatarType": 1, "bodyColor3s": { "headColor3": "string", "torsoColor3": "string", "rightArmColor3": "string", "leftArmColor3": "string", "rightLegColor3": "string", "leftLegColor3": "string" }, "assets": [ { "id": "...", "name": "...", "assetType": "...", "currentVersionId": "...", "meta": "...", "availabilityStatus": "..." } ], "defaultShirtApplied": false, "defaultPantsApplied": false } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://avatar.roblox.com/v2/avatar/users/{USERID}/avatar" ``` ### GET `/v2/avatar/users/{userId}/outfits` Gets a list of outfits for the specified user. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user id. | | `paginationToken` | query | `string` | No | The token received from the response to get the next page. For the first request, this value should be empty. Note : If no value is sent the 1st page will be returned. | | `outfitType` | query | `string` | No | The outfit type being searched for, null will return all outfitTypes. | | `page` | query | `integer (int32)` | No | The page number of the current page of requests, default is 1. | | `itemsPerPage` | query | `integer (int32)` | No | The max number of outfits that can be returned. | | `isEditable` | query | `boolean` | No | Whether the outfits are editable. A null value will lead to no filtering. | | `Roblox-Place-Id` | header | `integer (int64)` | No | The placeId of the caller, not required to be passed in. | **Responses:** - `200`: OK → `Roblox.Api.Avatar.Models.AvatarPageResponse[Roblox.Api.Avatar.Models.OutfitModel]` - `400`: 1: The specified user does not exist. 2: An account for the given userId does not exist! **Response fields** (`Roblox.Api.Avatar.Models.AvatarPageResponse[Roblox.Api.Avatar.Models.OutfitModel]`) See [Roblox.Api.Avatar.Models.AvatarPageResponse[Roblox.Api.Avatar.Models.OutfitModel]](#roblox-api-avatar-models-avatarpageresponse-roblox-api-avatar-models-outfitmodel-) in Models. **Response example:** ```json { "data": [ { "id": "...", "name": "...", "isEditable": "...", "outfitType": "..." } ], "paginationToken": "string" } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://avatar.roblox.com/v2/avatar/users/{USERID}/outfits" ``` ### POST `/v2/avatar/set-body-colors` Sets the authenticated user's body colors. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `Roblox-Place-Id` | header | `integer (int64)` | No | | **Request Body:** `application/json` — Type: `Roblox.Platform.Avatar.BodyColorsModelV2` See [Roblox.Platform.Avatar.BodyColorsModelV2](#roblox-platform-avatar-bodycolorsmodelv2) in Models. **Request example:** ```json { "headColor3": "string", "torsoColor3": "string", "rightArmColor3": "string", "leftArmColor3": "string", "rightLegColor3": "string", "leftLegColor3": "string" } ``` **Responses:** - `200`: OK → `Roblox.Api.Avatar.Models.AvatarApiSuccessResponse` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Api.Avatar.Models.AvatarApiSuccessResponse`) See [Roblox.Api.Avatar.Models.AvatarApiSuccessResponse](#roblox-api-avatar-models-avatarapisuccessresponse) in Models. **Response example:** ```json { "success": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://avatar.roblox.com/v2/avatar/set-body-colors" \ -H "Content-Type: application/json" \ -d '{ "headColor3": "string", "torsoColor3": "string", "rightArmColor3": "string", "leftArmColor3": "string", "rightLegColor3": "string", "leftLegColor3": "string" }' ``` ### POST `/v2/avatar/set-wearing-assets` Sets the avatar's current assets to the list. Only allows items that you own, are not expired, and are wearable asset types. Any assets being worn before this method is called are automatically removed. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `Roblox-Place-Id` | header | `integer (int64)` | No | | **Request Body:** `application/json` — Type: `Roblox.Api.Avatar.Models.WearRequestModel` See [Roblox.Api.Avatar.Models.WearRequestModel](#roblox-api-avatar-models-wearrequestmodel) in Models. **Request example:** ```json { "assets": [ { "id": "...", "meta": "..." } ] } ``` **Responses:** - `200`: OK → `Roblox.Api.Avatar.Models.WearResponseModel` - `400`: 3: Invalid assetId 5: Meta does not apply to specified asset type 7: Required meta is not provided for the specific asset type - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `500`: 2: Failed to wear asset. **Response fields** (`Roblox.Api.Avatar.Models.WearResponseModel`) See [Roblox.Api.Avatar.Models.WearResponseModel](#roblox-api-avatar-models-wearresponsemodel) in Models. **Response example:** ```json { "invalidAssets": [ { "id": "...", "name": "...", "assetType": "...", "currentVersionId": "...", "meta": "...", "availabilityStatus": "..." } ], "invalidAssetIds": [ 0 ], "success": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://avatar.roblox.com/v2/avatar/set-wearing-assets" \ -H "Content-Type: application/json" \ -d '{ "assets": [ { "id": "...", "meta": "..." } ] }' ``` ### POST `/v2/outfits/create` *(deprecated)* Creates a new outfit. Fails if any of the assetIds are not owned by the user, or not wearable types. The name property of the request is optional as one will be auto-generated when the request has a null name. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `Roblox-Place-Id` | header | `integer (int64)` | No | | **Request Body:** `application/json` — Type: `Roblox.Api.Avatar.Models.OutfitUpdateModelV2` See [Roblox.Api.Avatar.Models.OutfitUpdateModelV2](#roblox-api-avatar-models-outfitupdatemodelv2) in Models. **Request example:** ```json { "name": "string", "bodyColors": { "headColorId": 0, "torsoColorId": 0, "rightArmColorId": 0, "leftArmColorId": 0, "rightLegColorId": 0, "leftLegColorId": 0 }, "assets": [ { "id": "...", "meta": "..." } ], "scale": { "height": 0, "width": 0, "head": 0, "depth": 0, "proportion": 0, "bodyType": 0 }, "playerAvatarType": "string", "outfitType": 0 } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 3: Body colors must be valid BrickColor IDs 4: Invalid outfit name 5: Asset is not wearable by you and was not added to the outfit 7: Invalid Player Avatar Type. Valid types are R6 and R15 8: Invalid assetIds 9: Meta does not apply to specified asset type 10: Required meta is not provided for the specific asset type 12: Outfit type invalid or not permitted 13: Invalid Scale - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 1: You already have the maximum number of outfits - `500`: 6: An error occurred while creating the outfit **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://avatar.roblox.com/v2/outfits/create" \ -H "Content-Type: application/json" \ -d '{"name":"string","bodyColors":{"headColorId":0,"torsoColorId":0,"rightArmColorId":0,"leftArmColorId":0,"rightLegColorId":0,"leftLegColorId":0},"assets":[{"id":"...","meta":"..."}],"scale":{"height":0,"width":0,"head":0,"depth":0,"proportion":0,"bodyType":0},"playerAvatarType":"string","outfitType":0}' ``` ### PATCH `/v2/outfits/{userOutfitId}` *(deprecated)* Updates the contents of an outfit. Fails if the user does not own any of the assetIds or if they are not wearable asset types. Accepts partial updates. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userOutfitId` | path | `integer (int64)` | Yes | The user outfit id. | | `Roblox-Place-Id` | header | `integer (int64)` | No | | **Request Body:** `application/json` — Type: `Roblox.Api.Avatar.Models.OutfitUpdateModelV2` See [Roblox.Api.Avatar.Models.OutfitUpdateModelV2](#roblox-api-avatar-models-outfitupdatemodelv2) in Models. **Request example:** ```json { "name": "string", "bodyColors": { "headColorId": 0, "torsoColorId": 0, "rightArmColorId": 0, "leftArmColorId": 0, "rightLegColorId": 0, "leftLegColorId": 0 }, "assets": [ { "id": "...", "meta": "..." } ], "scale": { "height": 0, "width": 0, "head": 0, "depth": 0, "proportion": 0, "bodyType": 0 }, "playerAvatarType": "string", "outfitType": 0 } ``` **Responses:** - `200`: OK → `Roblox.Api.Avatar.Models.OutfitModel` - `400`: 3: Body colors must be valid BrickColor IDs 4: Invalid outfit name 5: Asset is not wearable by you 8: Invalid Player Avatar Type. Valid types are R6 and R15 11: Meta does not apply to specified asset type 12: Meta is required for this specific asset type 13: Invalid Outfit Type 14: Invalid scale - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: You don't have permission to update this outfit. - `404`: 1: The specified userOutfit does not exist! - `500`: 6: An error occurred while trying to update the outfit **Response fields** (`Roblox.Api.Avatar.Models.OutfitModel`) See [Roblox.Api.Avatar.Models.OutfitModel](#roblox-api-avatar-models-outfitmodel) in Models. **Response example:** ```json { "id": 0, "name": "string", "isEditable": false, "outfitType": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://avatar.roblox.com/v2/outfits/{USEROUTFITID}" \ -H "Content-Type: application/json" \ -d '{"name":"string","bodyColors":{"headColorId":0,"torsoColorId":0,"rightArmColorId":0,"leftArmColorId":0,"rightLegColorId":0,"leftLegColorId":0},"assets":[{"id":"...","meta":"..."}],"scale":{"height":0,"width":0,"head":0,"depth":0,"proportion":0,"bodyType":0},"playerAvatarType":"string","outfitType":0}' ``` ## Models ### Roblox.Api.Avatar.Models.AssetMetaModelV1 Exhaustive model denoting all possible metadata fields of an asset | Property | Type | Required | Description | |----------|------|----------|-------------| | `order` | `integer` | No | Layered-clothing order | | `puffiness` | `number` | No | Layered-clothing puffiness | | `position` | `Roblox.Api.Avatar.Models.AssetPosition` | No | | | `rotation` | `Roblox.Api.Avatar.Models.AssetRotation` | No | | | `scale` | `Roblox.Api.Avatar.Models.AssetScale` | No | | | `headShape` | `integer enum (30 values)` | No | Head Shape selected for the asset id. Applicable for dynamic head assets. Values: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29 | | `staticFacialAnimation` | `boolean` | No | Indicates user choice for facial animation. staticFacialAnimation=false, implies the toggle as on and face will animate. Applicable for dynamic head assets. | | `version` | `integer` | No | Client-authoritative meta model format version - default is always 1 | ### Roblox.Api.Avatar.Models.AssetModelV2 A model containing details about an asset - V2: adds CurrentVersionId, AssetMetaModel | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | The id | | `name` | `string` | No | The name | | `assetType` | `Roblox.Api.Avatar.Models.AssetTypeModel` | No | | | `currentVersionId` | `integer` | No | Id of the current version of asset | | `meta` | `Roblox.Api.Avatar.Models.AssetMetaModelV1` | No | | | `availabilityStatus` | `string` | No | Asset availability status. | | `expirationTime` | `string` | No | For rental assets only. (Future) ownership expiration time of the asset. | | `supportsHeadShapes` | `boolean` | No | If the "Id" is swappable, applicable for DH assets. | ### Roblox.Api.Avatar.Models.AssetPosition A model which contains accessory position coordinates. | Property | Type | Required | Description | |----------|------|----------|-------------| | `X` | `number` | No | X coordinate of the position. | | `Y` | `number` | No | y coordinate of the position. | | `Z` | `number` | No | Z coordinate of the position. | ### Roblox.Api.Avatar.Models.AssetRotation A model which contains accessory rotation coordinates. | Property | Type | Required | Description | |----------|------|----------|-------------| | `X` | `number` | No | X degrees of the rotation. | | `Y` | `number` | No | Y degrees of the rotation. | | `Z` | `number` | No | Z degrees of the rotation. | ### Roblox.Api.Avatar.Models.AssetScale A model which contains accessory scale. | Property | Type | Required | Description | |----------|------|----------|-------------| | `X` | `number` | No | X scale multiplier of accessory. | | `Y` | `number` | No | Y scale multiplier of accessory. | | `Z` | `number` | No | Z scale multiplier of accessory. | ### Roblox.Api.Avatar.Models.AssetTypeModel A model containing details about an asset type | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | The id | | `name` | `string` | No | The name | ### Roblox.Api.Avatar.Models.AssetWearModel A model which contains - an asset id - AssetMetaModel | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | An asset id | | `meta` | `Roblox.Api.Avatar.Models.AssetMetaModelV1` | No | | ### Roblox.Api.Avatar.Models.AvatarApiSuccessResponse Success response class | Property | Type | Required | Description | |----------|------|----------|-------------| | `success` | `boolean` | No | Gets or sets a value indicating whether the request was a success. | ### Roblox.Api.Avatar.Models.AvatarModelV3 A model containing details about an avatar | Property | Type | Required | Description | |----------|------|----------|-------------| | `scales` | `Roblox.Web.Responses.Avatar.ScaleModel` | No | | | `playerAvatarType` | `1 \| 3` | No | The avatar type | | `bodyColor3s` | `Roblox.Api.Avatar.Models.BodyColors3Model` | No | | | `assets` | `Roblox.Api.Avatar.Models.AssetModelV2[]` | No | The assets worn on the character | | `defaultShirtApplied` | `boolean` | No | Whether default clothing has been applied to this avatar. | | `defaultPantsApplied` | `boolean` | No | Whether default clothing has been applied to this avatar. | | `emotes` | `Roblox.Api.Avatar.Models.EmoteResponseModel[]` | No | The emotes on the character | ### Roblox.Api.Avatar.Models.AvatarPageResponse[Roblox.Api.Avatar.Models.OutfitModel] Avatar page response. | Property | Type | Required | Description | |----------|------|----------|-------------| | `data` | `Roblox.Api.Avatar.Models.OutfitModel[]` | No | The data | | `paginationToken` | `string` | No | A non empty string indicates that there is more data available than this response contains. An empty string indicates the last page. | ### Roblox.Api.Avatar.Models.BodyColors3Model A model containing RGB hex colors for each body part. | Property | Type | Required | Description | |----------|------|----------|-------------| | `headColor3` | `string` | No | The RGB hex color for head color, e.g. #FFFFFF | | `torsoColor3` | `string` | No | The RGB hex color for torso color, e.g. #FFFFFF | | `rightArmColor3` | `string` | No | The RGB hex color for right arm color, e.g. #FFFFFF | | `leftArmColor3` | `string` | No | The RGB hex color for left arm color, e.g. #FFFFFF | | `rightLegColor3` | `string` | No | The RGB hex color for right leg color, e.g. #FFFFFF | | `leftLegColor3` | `string` | No | The RGB hex color for left leg color, e.g. #FFFFFF | ### Roblox.Api.Avatar.Models.BodyColorsModel A model container BrickColor ids for each body part. | Property | Type | Required | Description | |----------|------|----------|-------------| | `headColorId` | `integer` | No | The BrickColor id for head color | | `torsoColorId` | `integer` | No | The BrickColor id for torso color | | `rightArmColorId` | `integer` | No | The BrickColor id for right arm color | | `leftArmColorId` | `integer` | No | The BrickColor id for left arm color | | `rightLegColorId` | `integer` | No | The BrickColor id for right leg color | | `leftLegColorId` | `integer` | No | The BrickColor id for left leg color | ### Roblox.Api.Avatar.Models.EmoteResponseModel Response object representing a user's emote | Property | Type | Required | Description | |----------|------|----------|-------------| | `assetId` | `integer` | No | The asset id of the emote. | | `assetName` | `string` | No | The name of the emote. | | `position` | `integer` | No | The position the emote is equipped to. | ### Roblox.Api.Avatar.Models.OutfitModel A slim model for user outfits. | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | The id. | | `name` | `string` | No | The name. | | `isEditable` | `boolean` | No | Whether the outfit can be modified by the user. | | `outfitType` | `string` | No | The type of the Outfit. | ### Roblox.Api.Avatar.Models.OutfitUpdateModelV2 A model containing details needed to update or create an outfit | Property | Type | Required | Description | |----------|------|----------|-------------| | `name` | `string` | No | The outfit name | | `bodyColors` | `Roblox.Api.Avatar.Models.BodyColorsModel` | No | | | `assets` | `Roblox.Api.Avatar.Models.AssetWearModel[]` | No | Array of assets | | `scale` | `Roblox.Web.Responses.Avatar.ScaleModel` | No | | | `playerAvatarType` | `string` | No | The avatar scale | | `outfitType` | `0 \| 1 \| 2 \| 4 \| 5` | No | The type of outfit | ### Roblox.Api.Avatar.Models.WearRequestModel A model that contains a list of AssetWear models | Property | Type | Required | Description | |----------|------|----------|-------------| | `assets` | `Roblox.Api.Avatar.Models.AssetWearModel[]` | No | The asset ids | ### Roblox.Api.Avatar.Models.WearResponseModel A model for wear outfit responses | Property | Type | Required | Description | |----------|------|----------|-------------| | `invalidAssets` | `Roblox.Api.Avatar.Models.AssetModelV2[]` | No | The assets that could not be worn Unlike invalidAssetIds, only contains assets that are wearable types | | `invalidAssetIds` | `integer[]` | No | The asset ids that could not be worn | | `success` | `boolean` | No | Whether or not all the outfit contents were successfully worn | ### Roblox.Platform.Avatar.BodyColorsModelV2 | Property | Type | Required | Description | |----------|------|----------|-------------| | `headColor3` | `string` | No | | | `torsoColor3` | `string` | No | | | `rightArmColor3` | `string` | No | | | `leftArmColor3` | `string` | No | | | `rightLegColor3` | `string` | No | | | `leftLegColor3` | `string` | No | | ### Roblox.Web.Responses.Avatar.ScaleModel | Property | Type | Required | Description | |----------|------|----------|-------------| | `height` | `number` | No | | | `width` | `number` | No | | | `head` | `number` | No | | | `depth` | `number` | No | | | `proportion` | `number` | No | | | `bodyType` | `number` | No | | --- name: "Avatar Api v3" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://avatar.roblox.com" version: "v3" endpoints: 3 auth: [cookie] --- # Avatar Api v3 **API Version:** v3 **Base URL:** `https://avatar.roblox.com` ## Endpoints ### GET `/v3/outfits/{userOutfitId}/details` Gets details about the contents of an outfit. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userOutfitId` | path | `integer (int64)` | Yes | The user outfit id. | | `Roblox-Place-Id` | header | `integer (int64)` | No | | | `checkAssetAvailability` | query | `boolean` | No | Whether to return assets with availability status. | **Responses:** - `200`: OK → `Roblox.Api.Avatar.Models.OutfitDetailsModelV2` - `400`: 2: The outfit for the specified userOutfit is invalid. - `403`: 3: The requester does not have access to the details for the given user outfit. - `404`: 1: The specified userOutfitId is invalid. **Response fields** (`Roblox.Api.Avatar.Models.OutfitDetailsModelV2`) See [Roblox.Api.Avatar.Models.OutfitDetailsModelV2](#roblox-api-avatar-models-outfitdetailsmodelv2) in Models. **Response example:** ```json { "id": 0, "name": "string", "assets": [ { "id": "...", "name": "...", "assetType": "...", "currentVersionId": "...", "meta": "...", "availabilityStatus": "..." } ], "bodyColor3s": { "headColor3": "string", "torsoColor3": "string", "rightArmColor3": "string", "leftArmColor3": "string", "rightLegColor3": "string", "leftLegColor3": "string" }, "scale": { "height": 0, "width": 0, "head": 0, "depth": 0, "proportion": 0, "bodyType": 0 }, "playerAvatarType": "string" } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://avatar.roblox.com/v3/outfits/{USEROUTFITID}/details" ``` ### POST `/v3/outfits/create` Creates a new outfit. Fails if any of the assetIds are not owned by the user, or not wearable types. The name property of the request is optional as one will be auto-generated when the request has a null name. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `Roblox-Place-Id` | header | `integer (int64)` | No | | **Request Body:** `application/json` — Type: `Roblox.Api.Avatar.Models.OutfitUpdateModelV3` See [Roblox.Api.Avatar.Models.OutfitUpdateModelV3](#roblox-api-avatar-models-outfitupdatemodelv3) in Models. **Request example:** ```json { "name": "string", "bodyColor3s": { "headColor3": "string", "torsoColor3": "string", "rightArmColor3": "string", "leftArmColor3": "string", "rightLegColor3": "string", "leftLegColor3": "string" }, "assets": [ { "id": "...", "meta": "..." } ], "scale": { "height": 0, "width": 0, "head": 0, "depth": 0, "proportion": 0, "bodyType": 0 }, "playerAvatarType": "string", "outfitType": 0 } ``` **Responses:** - `200`: OK → `Roblox.Api.Avatar.Models.OutfitModel` - `400`: 3: Body colors must be valid BrickColor IDs 4: Invalid outfit name 5: Asset is not wearable by you and was not added to the outfit 7: Invalid Player Avatar Type. Valid types are R6 and R15 8: Invalid assetIds 9: Meta does not apply to specified asset type 10: Required meta is not provided for the specific asset type 12: Outfit type invalid or not permitted 13: Invalid Scale - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 1: You already have the maximum number of outfits - `500`: 6: An error occurred while creating the outfit **Response fields** (`Roblox.Api.Avatar.Models.OutfitModel`) See [Roblox.Api.Avatar.Models.OutfitModel](#roblox-api-avatar-models-outfitmodel) in Models. **Response example:** ```json { "id": 0, "name": "string", "isEditable": false, "outfitType": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://avatar.roblox.com/v3/outfits/create" \ -H "Content-Type: application/json" \ -d '{"name":"string","bodyColor3s":{"headColor3":"string","torsoColor3":"string","rightArmColor3":"string","leftArmColor3":"string","rightLegColor3":"string","leftLegColor3":"string"},"assets":[{"id":"...","meta":"..."}],"scale":{"height":0,"width":0,"head":0,"depth":0,"proportion":0,"bodyType":0},"playerAvatarType":"string","outfitType":0}' ``` ### PATCH `/v3/outfits/{userOutfitId}` Updates the contents of an outfit. Fails if the user does not own any of the assetIds or if they are not wearable asset types. Accepts partial updates. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userOutfitId` | path | `integer (int64)` | Yes | The user outfit id. | | `Roblox-Place-Id` | header | `integer (int64)` | No | | **Request Body:** `application/json` — Type: `Roblox.Api.Avatar.Models.OutfitUpdateModelV3` See [Roblox.Api.Avatar.Models.OutfitUpdateModelV3](#roblox-api-avatar-models-outfitupdatemodelv3) in Models. **Request example:** ```json { "name": "string", "bodyColor3s": { "headColor3": "string", "torsoColor3": "string", "rightArmColor3": "string", "leftArmColor3": "string", "rightLegColor3": "string", "leftLegColor3": "string" }, "assets": [ { "id": "...", "meta": "..." } ], "scale": { "height": 0, "width": 0, "head": 0, "depth": 0, "proportion": 0, "bodyType": 0 }, "playerAvatarType": "string", "outfitType": 0 } ``` **Responses:** - `200`: OK → `Roblox.Api.Avatar.Models.OutfitModel` - `400`: 3: Body colors must be valid BrickColor IDs 4: Invalid outfit name 5: Asset is not wearable by you 8: Invalid Player Avatar Type. Valid types are R6 and R15 11: Meta does not apply to specified asset type 12: Meta is required for this specific asset type 13: Invalid Outfit Type 14: Invalid scale - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: You don't have permission to update this outfit. - `404`: 1: The specified userOutfit does not exist! - `500`: 6: An error occurred while trying to update the outfit **Response fields** (`Roblox.Api.Avatar.Models.OutfitModel`) See [Roblox.Api.Avatar.Models.OutfitModel](#roblox-api-avatar-models-outfitmodel) in Models. **Response example:** ```json { "id": 0, "name": "string", "isEditable": false, "outfitType": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://avatar.roblox.com/v3/outfits/{USEROUTFITID}" \ -H "Content-Type: application/json" \ -d '{"name":"string","bodyColor3s":{"headColor3":"string","torsoColor3":"string","rightArmColor3":"string","leftArmColor3":"string","rightLegColor3":"string","leftLegColor3":"string"},"assets":[{"id":"...","meta":"..."}],"scale":{"height":0,"width":0,"head":0,"depth":0,"proportion":0,"bodyType":0},"playerAvatarType":"string","outfitType":0}' ``` ## Models ### Roblox.Api.Avatar.Models.AssetMetaModelV1 Exhaustive model denoting all possible metadata fields of an asset | Property | Type | Required | Description | |----------|------|----------|-------------| | `order` | `integer` | No | Layered-clothing order | | `puffiness` | `number` | No | Layered-clothing puffiness | | `position` | `Roblox.Api.Avatar.Models.AssetPosition` | No | | | `rotation` | `Roblox.Api.Avatar.Models.AssetRotation` | No | | | `scale` | `Roblox.Api.Avatar.Models.AssetScale` | No | | | `headShape` | `integer enum (30 values)` | No | Head Shape selected for the asset id. Applicable for dynamic head assets. Values: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29 | | `staticFacialAnimation` | `boolean` | No | Indicates user choice for facial animation. staticFacialAnimation=false, implies the toggle as on and face will animate. Applicable for dynamic head assets. | | `version` | `integer` | No | Client-authoritative meta model format version - default is always 1 | ### Roblox.Api.Avatar.Models.AssetModelV2 A model containing details about an asset - V2: adds CurrentVersionId, AssetMetaModel | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | The id | | `name` | `string` | No | The name | | `assetType` | `Roblox.Api.Avatar.Models.AssetTypeModel` | No | | | `currentVersionId` | `integer` | No | Id of the current version of asset | | `meta` | `Roblox.Api.Avatar.Models.AssetMetaModelV1` | No | | | `availabilityStatus` | `string` | No | Asset availability status. | | `expirationTime` | `string` | No | For rental assets only. (Future) ownership expiration time of the asset. | | `supportsHeadShapes` | `boolean` | No | If the "Id" is swappable, applicable for DH assets. | ### Roblox.Api.Avatar.Models.AssetPosition A model which contains accessory position coordinates. | Property | Type | Required | Description | |----------|------|----------|-------------| | `X` | `number` | No | X coordinate of the position. | | `Y` | `number` | No | y coordinate of the position. | | `Z` | `number` | No | Z coordinate of the position. | ### Roblox.Api.Avatar.Models.AssetRotation A model which contains accessory rotation coordinates. | Property | Type | Required | Description | |----------|------|----------|-------------| | `X` | `number` | No | X degrees of the rotation. | | `Y` | `number` | No | Y degrees of the rotation. | | `Z` | `number` | No | Z degrees of the rotation. | ### Roblox.Api.Avatar.Models.AssetScale A model which contains accessory scale. | Property | Type | Required | Description | |----------|------|----------|-------------| | `X` | `number` | No | X scale multiplier of accessory. | | `Y` | `number` | No | Y scale multiplier of accessory. | | `Z` | `number` | No | Z scale multiplier of accessory. | ### Roblox.Api.Avatar.Models.AssetTypeModel A model containing details about an asset type | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | The id | | `name` | `string` | No | The name | ### Roblox.Api.Avatar.Models.AssetWearModel A model which contains - an asset id - AssetMetaModel | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | An asset id | | `meta` | `Roblox.Api.Avatar.Models.AssetMetaModelV1` | No | | ### Roblox.Api.Avatar.Models.BodyColors3Model A model containing RGB hex colors for each body part. | Property | Type | Required | Description | |----------|------|----------|-------------| | `headColor3` | `string` | No | The RGB hex color for head color, e.g. #FFFFFF | | `torsoColor3` | `string` | No | The RGB hex color for torso color, e.g. #FFFFFF | | `rightArmColor3` | `string` | No | The RGB hex color for right arm color, e.g. #FFFFFF | | `leftArmColor3` | `string` | No | The RGB hex color for left arm color, e.g. #FFFFFF | | `rightLegColor3` | `string` | No | The RGB hex color for right leg color, e.g. #FFFFFF | | `leftLegColor3` | `string` | No | The RGB hex color for left leg color, e.g. #FFFFFF | ### Roblox.Api.Avatar.Models.OutfitDetailsModelV2 A model containing details about a user outfit | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | The id | | `name` | `string` | No | The name | | `assets` | `Roblox.Api.Avatar.Models.AssetModelV2[]` | No | A list of assetIds | | `bodyColor3s` | `Roblox.Api.Avatar.Models.BodyColors3Model` | No | | | `scale` | `Roblox.Web.Responses.Avatar.ScaleModel` | No | | | `playerAvatarType` | `string` | No | The player avatar type - this can be R6 or R15. | | `outfitType` | `string` | No | The outfit type of the outfit | | `isEditable` | `boolean` | No | Whether the outfit can be edited by the user | | `universeId` | `integer` | No | The universe id of the outfit, null when outfit is not created in-experience | | `moderationStatus` | `string` | No | The moderation status of the outfit, not applicable when outfit is created outside experience | | `bundleId` | `integer` | No | The bundle ID, currently only returned for in-experience created outfits. | | `inventoryType` | `string` | No | The inventory type of the outfit. | ### Roblox.Api.Avatar.Models.OutfitModel A slim model for user outfits. | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | The id. | | `name` | `string` | No | The name. | | `isEditable` | `boolean` | No | Whether the outfit can be modified by the user. | | `outfitType` | `string` | No | The type of the Outfit. | ### Roblox.Api.Avatar.Models.OutfitUpdateModelV3 A model containing details needed to update or create an outfit. | Property | Type | Required | Description | |----------|------|----------|-------------| | `name` | `string` | No | The outfit name. | | `bodyColor3s` | `Roblox.Api.Avatar.Models.BodyColors3Model` | No | | | `assets` | `Roblox.Api.Avatar.Models.AssetWearModel[]` | No | Array of assets. | | `scale` | `Roblox.Web.Responses.Avatar.ScaleModel` | No | | | `playerAvatarType` | `string` | No | The avatar scale. | | `outfitType` | `0 \| 1 \| 2 \| 4 \| 5` | No | The type of outfit. | ### Roblox.Web.Responses.Avatar.ScaleModel | Property | Type | Required | Description | |----------|------|----------|-------------| | `height` | `number` | No | | | `width` | `number` | No | | | `head` | `number` | No | | | `depth` | `number` | No | | | `proportion` | `number` | No | | | `bodyType` | `number` | No | | --- name: "Badges Api v1" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://badges.roblox.com" version: "v1" endpoints: 11 auth: [cookie] --- # Badges Api v1 **API Version:** v1 **Base URL:** `https://badges.roblox.com` ## Endpoints ### GET `/v1/badges/{badgeId}` Gets badge information by the badge Id. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `badgeId` | path | `integer (int64)` | Yes | The badge Id. | **Responses:** - `200`: OK → `Roblox.Badges.Api.BadgeResponse` - `404`: 1: Badge is invalid or does not exist. 3: The game is invalid or does not exist. **Response fields** (`Roblox.Badges.Api.BadgeResponse`) See [Roblox.Badges.Api.BadgeResponse](#roblox-badges-api-badgeresponse) in Models. **Response example:** ```json { "id": 0, "name": "string", "description": "string", "displayName": "string", "displayDescription": "string", "enabled": false } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://badges.roblox.com/v1/badges/{BADGEID}" ``` ### PATCH `/v1/badges/{badgeId}` Updates badge configuration. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `badgeId` | path | `integer (int64)` | Yes | The badge Id. | **Request Body:** `application/json` — Type: `Roblox.Badges.Api.UpdateBadgeRequest` See [Roblox.Badges.Api.UpdateBadgeRequest](#roblox-badges-api-updatebadgerequest) in Models. **Request example:** ```json { "name": "string", "description": "string", "enabled": false } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 6: Text moderated. 14: Invalid badge name. 15: Invalid badge description. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: You do not have permission to manage this badge. - `404`: 1: Badge is invalid or does not exist. 3: The game is invalid or does not exist. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://badges.roblox.com/v1/badges/{BADGEID}" \ -H "Content-Type: application/json" \ -d '{ "name": "string", "description": "string", "enabled": false }' ``` ### GET `/v1/badges/metadata` Gets metadata about the badges system. **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Badges.Api.BadgeMetadataResponse` **Response fields** (`Roblox.Badges.Api.BadgeMetadataResponse`) See [Roblox.Badges.Api.BadgeMetadataResponse](#roblox-badges-api-badgemetadataresponse) in Models. **Response example:** ```json { "badgeCreationPrice": 0, "maxBadgeNameLength": 0, "maxBadgeDescriptionLength": 0 } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://badges.roblox.com/v1/badges/metadata" ``` ### GET `/v1/universes/{universeId}/badges` Gets badges by their awarding game. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer (int64)` | Yes | The universe Id. | | `sortBy` | query | `string` | No | The key to sort badges by. Valid values: `Rank`, `DateCreated` | | `limit` | query | `integer (int32)` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `string` | No | The order the results are sorted in. Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Badges.Api.BadgeResponse]` - `400`: 26: The pagination cursor is invalid or incompatible with the current request. - `404`: 3: The game is invalid or does not exist. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Badges.Api.BadgeResponse]`) See [Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Badges.Api.BadgeResponse]](#roblox-web-webapi-models-apipageresponse-roblox-badges-api-badgeresponse-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "id": "...", "name": "...", "description": "...", "displayName": "...", "displayDescription": "...", "enabled": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://badges.roblox.com/v1/universes/{UNIVERSEID}/badges" ``` ### POST `/v1/universes/{universeId}/badges` Creates a new badge. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer (int64)` | Yes | The ID of the universe to create the badge for. | | `name` | formData | `string` | No | The badge name. | | `description` | formData | `string` | No | The badge description. | | `paymentSourceType` | formData | `integer (int32)` | No | Whether or not to pay for the badge with user funds, or group funds. ['User' = 1, 'Group' = 2] Valid values: `1`, `2` | | `files` | formData | `file` | No | The badge icon. | | `expectedCost` | formData | `integer (int64)` | No | User expected cost of a badge. | | `isActive` | formData | `boolean` | No | Whether or not the badge should be created in the active state. | **Responses:** - `200`: OK → `Roblox.Web.Responses.Badges.BadgeResponseV2` - `400`: 11: The badge icon is invalid. 14: Invalid badge name. 15: Invalid badge description. 16: Payment source is invalid. 18: Expected badge cost is different from the actual badge cost. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 6: Text moderated. 12: You do not have permission to manage this game's badges. 17: Insufficient funds. - `404`: 3: The game is invalid or does not exist. - `429`: 13: Too many requests, try again later. **Response fields** (`Roblox.Web.Responses.Badges.BadgeResponseV2`) See [Roblox.Web.Responses.Badges.BadgeResponseV2](#roblox-web-responses-badges-badgeresponsev2) in Models. **Response example:** ```json { "id": 0, "name": "string", "description": "string", "displayName": "string", "displayDescription": "string", "enabled": false } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://badges.roblox.com/v1/universes/{UNIVERSEID}/badges" ``` ### GET `/v1/universes/{universeId}/free-badges-quota` Gets the number of free badges left for the current UTC day by their awarding game. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer (int64)` | Yes | The universe Id. | **Responses:** - `200`: OK → `integer` - `404`: 3: The game is invalid or does not exist. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://badges.roblox.com/v1/universes/{UNIVERSEID}/free-badges-quota" ``` ### GET `/v1/users/{userId}/badges` Gets a list of badges a user has been awarded. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user Id. | | `limit` | query | `integer (int32)` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `string` | No | The order the results are sorted in. Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Badges.Api.GetBadgesByUserResponse]` - `404`: 4: User is invalid or does not exist. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Badges.Api.GetBadgesByUserResponse]`) See [Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Badges.Api.GetBadgesByUserResponse]](#roblox-web-webapi-models-apipageresponse-roblox-badges-api-getbadgesbyuserresponse-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "creator": "...", "id": "...", "name": "...", "description": "...", "displayName": "...", "displayDescription": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://badges.roblox.com/v1/users/{USERID}/badges" ``` ### GET `/v1/users/{userId}/badges/{badgeId}/awarded-date` Gets timestamp for when a single badge was awarded to a user. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | User id. | | `badgeId` | path | `integer (int64)` | Yes | Badge id. | **Responses:** - `200`: OK - `404`: 4: User is invalid or does not exist. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://badges.roblox.com/v1/users/{USERID}/badges/{BADGEID}/awarded-date" ``` ### GET `/v1/users/{userId}/badges/awarded-dates` Gets timestamps for when badges were awarded to a user. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user Id. | | `badgeIds` | query | `array` | Yes | The CSV of badge Ids. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Badges.Api.BadgeAwardResponse]` - `400`: 5: Too many badge Ids. - `404`: 4: User is invalid or does not exist. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Badges.Api.BadgeAwardResponse]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Badges.Api.BadgeAwardResponse]](#roblox-web-webapi-models-apiarrayresponse-roblox-badges-api-badgeawardresponse-) in Models. **Response example:** ```json { "data": [ { "badgeId": "...", "awardedDate": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://badges.roblox.com/v1/users/{USERID}/badges/awarded-dates?badgeIds={VALUE}" ``` ### POST `/v1/badges/{badgeId}/icon` Overwrites a badge icon with a new one. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `badgeId` | path | `integer (int64)` | Yes | The badge Id. | | `Files` | formData | `file` | No | | **Responses:** - `200`: OK → `Roblox.Badges.Api.IconUploadResponse` - `400`: 6: Text moderated. 22: Icon file is not present in the request. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: You do not have permission to manage this badge. - `404`: 1: Badge is invalid or does not exist. - `429`: 13: Too many requests, try again later. **Response fields** (`Roblox.Badges.Api.IconUploadResponse`) See [Roblox.Badges.Api.IconUploadResponse](#roblox-badges-api-iconuploadresponse) in Models. **Response example:** ```json { "targetId": 0 } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://badges.roblox.com/v1/badges/{BADGEID}/icon" ``` ### DELETE `/v1/user/badges/{badgeId}` Removes a badge from the authenticated user. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `badgeId` | path | `integer (int64)` | Yes | The badge Id. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `404`: 1: Badge is invalid or does not exist. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://badges.roblox.com/v1/user/badges/{BADGEID}" ``` ## Models ### Roblox.Badges.Api.BadgeAwardResponse The result of being awarded a badge. | Property | Type | Required | Description | |----------|------|----------|-------------| | `badgeId` | `integer` | No | The badge Id. | | `awardedDate` | `string` | No | When the badge was awarded. | ### Roblox.Badges.Api.BadgeCreatorResponse Represents information about the badge creator. (Creator of the place that awarded the badge) | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | The creator ID | | `name` | `string` | No | The name of the creator | | `type` | `string` | No | The type of the creator | ### Roblox.Badges.Api.BadgeMetadataResponse Metadata about badges. | Property | Type | Required | Description | |----------|------|----------|-------------| | `badgeCreationPrice` | `integer` | No | The cost in Robux for creating a new badge. | | `maxBadgeNameLength` | `integer` | No | The max length for a badge name. | | `maxBadgeDescriptionLength` | `integer` | No | The max length for a badge description. | ### Roblox.Badges.Api.BadgeResponse A response containing badge information. | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | The badge Id. | | `name` | `string` | No | The name of the badge. | | `description` | `string` | No | The badge description. | | `displayName` | `string` | No | The localized name of the badge. | | `displayDescription` | `string` | No | The localized badge description. | | `enabled` | `boolean` | No | Whether or not the badge is enabled. | | `iconImageId` | `integer` | No | The badge icon asset Id. | | `displayIconImageId` | `integer` | No | The localized badge icon asset Id. | | `created` | `string` | No | When the badge was created. | | `updated` | `string` | No | When the badge was updated. | | `statistics` | `Roblox.Web.Responses.Badges.BadgeAwardStatisticsResponse` | No | | | `awardingUniverse` | `Roblox.Badges.Api.UniverseResponse` | No | | ### Roblox.Badges.Api.GetBadgesByUserResponse Response for the GetBadgesByUser endpoint. | Property | Type | Required | Description | |----------|------|----------|-------------| | `creator` | `Roblox.Badges.Api.BadgeCreatorResponse` | No | | | `id` | `integer` | No | | | `name` | `string` | No | | | `description` | `string` | No | | | `displayName` | `string` | No | | | `displayDescription` | `string` | No | | | `enabled` | `boolean` | No | | | `iconImageId` | `integer` | No | | | `displayIconImageId` | `integer` | No | | | `awarder` | `Roblox.Web.Responses.RelatedEntityTypeResponse[Roblox.Platform.Badges.BadgeAwarderType]` | No | | | `statistics` | `Roblox.Web.Responses.Badges.BadgeAwardStatisticsResponse` | No | | | `created` | `string` | No | | | `updated` | `string` | No | | ### Roblox.Badges.Api.IconUploadResponse Badge icon upload response. | Property | Type | Required | Description | |----------|------|----------|-------------| | `targetId` | `integer` | No | The asset id of the uploaded icon. | ### Roblox.Badges.Api.UniverseResponse A response containing universe information. | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | The universe Id. | | `name` | `string` | No | The universe name. | | `rootPlaceId` | `integer` | No | The description of the universe. | ### Roblox.Badges.Api.UpdateBadgeRequest A request model used for updating badge information. | Property | Type | Required | Description | |----------|------|----------|-------------| | `name` | `string` | No | The new badge name. | | `description` | `string` | No | The new badge description. | | `enabled` | `boolean` | No | The new enabled state of the badge. | ### Roblox.Web.Responses.Badges.BadgeAwardStatisticsResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `pastDayAwardedCount` | `integer` | No | | | `awardedCount` | `integer` | No | | | `winRatePercentage` | `number` | No | | ### Roblox.Web.Responses.Badges.BadgeResponseV2 | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | | | `name` | `string` | No | | | `description` | `string` | No | | | `displayName` | `string` | No | | | `displayDescription` | `string` | No | | | `enabled` | `boolean` | No | | | `iconImageId` | `integer` | No | | | `displayIconImageId` | `integer` | No | | | `awarder` | `Roblox.Web.Responses.RelatedEntityTypeResponse[Roblox.Platform.Badges.BadgeAwarderType]` | No | | | `statistics` | `Roblox.Web.Responses.Badges.BadgeAwardStatisticsResponse` | No | | | `created` | `string` | No | | | `updated` | `string` | No | | ### Roblox.Web.Responses.RelatedEntityTypeResponse[Roblox.Platform.Badges.BadgeAwarderType] | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | | | `type` | `1` | No | ['Place' = 1] | | `name` | `string` | No | | ### Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Badges.Api.BadgeAwardResponse] | Property | Type | Required | Description | |----------|------|----------|-------------| | `data` | `Roblox.Badges.Api.BadgeAwardResponse[]` | No | | ### Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Badges.Api.BadgeResponse] | Property | Type | Required | Description | |----------|------|----------|-------------| | `previousPageCursor` | `string` | No | | | `nextPageCursor` | `string` | No | | | `data` | `Roblox.Badges.Api.BadgeResponse[]` | No | | ### Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Badges.Api.GetBadgesByUserResponse] | Property | Type | Required | Description | |----------|------|----------|-------------| | `previousPageCursor` | `string` | No | | | `nextPageCursor` | `string` | No | | | `data` | `Roblox.Badges.Api.GetBadgesByUserResponse[]` | No | | --- name: "Catalog Api v1" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://catalog.roblox.com" version: "v1" endpoints: 22 auth: [cookie] --- # Catalog Api v1 **API Version:** v1 **Base URL:** `https://catalog.roblox.com` ## Endpoints ### GET `/v1/asset-to-category` Lists a mapping for assets to category IDs to convert from inventory ID to catalog ID. Creates a mapping to link 'Get More' button in inventory page to the relevant catalog page. **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `object` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://catalog.roblox.com/v1/asset-to-category" ``` ### GET `/v1/asset-to-subcategory` Lists a mapping for assets to subcategory IDs to convert from inventory ID to catalog ID. Creates a mapping to link 'Get More' button in inventory page to the relevant catalog page. **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `object` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://catalog.roblox.com/v1/asset-to-subcategory" ``` ### GET `/v1/assets/{assetId}/bundles` Lists the bundles a particular asset belongs to. Use the Id of the last bundle in the response to get the next page. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetId` | path | `integer (int64)` | Yes | | | `limit` | query | `integer (int32)` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `string` | No | The order the results are sorted in. Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Catalog.Api.BundleDetailsModel]` - `400`: 1: Invalid assetId 4: Invalid Cursor. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Catalog.Api.BundleDetailsModel]`) See [Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Catalog.Api.BundleDetailsModel]](#roblox-web-webapi-models-apipageresponse-roblox-catalog-api-bundledetailsmodel-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "id": "...", "name": "...", "description": "...", "bundleType": "...", "isRecolorable": "...", "items": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://catalog.roblox.com/v1/assets/{ASSETID}/bundles" ``` ### GET `/v1/bundles/{bundleId}/details` Returns details about the given bundleId. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `bundleId` | path | `integer (int64)` | Yes | | **Responses:** - `200`: OK → `Roblox.Catalog.Api.BundleDetailsModel` - `400`: 1: Invalid bundle **Response fields** (`Roblox.Catalog.Api.BundleDetailsModel`) See [Roblox.Catalog.Api.BundleDetailsModel](#roblox-catalog-api-bundledetailsmodel) in Models. **Response example:** ```json { "id": 0, "name": "string", "description": "string", "bundleType": "string", "isRecolorable": false, "items": [ { "owned": "...", "id": "...", "name": "...", "type": "...", "supportsHeadShapes": "...", "assetType": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://catalog.roblox.com/v1/bundles/{BUNDLEID}/details" ``` ### GET `/v1/bundles/{bundleId}/recommendations` Gets recommendations for a given bundle, bundleId of 0 returns randomized bundles - Accepts both public and authenticated users. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `bundleId` | path | `integer (int64)` | Yes | | | `numItems` | query | `integer (int32)` | No | The number of recommended items to return. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Catalog.Api.BundleDetailsModel]` - `400`: 1: Invalid bundle 2: Error retrieving bundles 3: Error getting bundle recommendations 4: NumItems exceed maximum **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Catalog.Api.BundleDetailsModel]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Catalog.Api.BundleDetailsModel]](#roblox-web-webapi-models-apiarrayresponse-roblox-catalog-api-bundledetailsmodel-) in Models. **Response example:** ```json { "data": [ { "id": "...", "name": "...", "description": "...", "bundleType": "...", "isRecolorable": "...", "items": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://catalog.roblox.com/v1/bundles/{BUNDLEID}/recommendations" ``` ### GET `/v1/bundles/details` Returns details about the given bundleIds. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `bundleIds` | query | `array` | Yes | | **Responses:** - `200`: OK → `Roblox.Catalog.Api.BundleDetailsModel[]` - `400`: 3: Cannot request so many bundles at once. **Response fields** (`Roblox.Catalog.Api.BundleDetailsModel[]`) See [Roblox.Catalog.Api.BundleDetailsModel](#roblox-catalog-api-bundledetailsmodel) in Models. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://catalog.roblox.com/v1/bundles/details?bundleIds={VALUE}" ``` ### GET `/v1/categories` Lists Category Names and their Ids. **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Catalog.Api.CategoryModel[]` **Response fields** (`Roblox.Catalog.Api.CategoryModel[]`) See [Roblox.Catalog.Api.CategoryModel](#roblox-catalog-api-categorymodel) in Models. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://catalog.roblox.com/v1/categories" ``` ### GET `/v1/favorites/assets/{assetId}/count` Gets the favorite count for the given asset Id. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetId` | path | `integer (int64)` | Yes | | **Responses:** - `200`: OK → `integer` - `400`: 2: Invalid asset Id. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://catalog.roblox.com/v1/favorites/assets/{ASSETID}/count" ``` ### GET `/v1/favorites/bundles/{bundleId}/count` Gets the favorite count for the given bundle Id. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `bundleId` | path | `integer (int64)` | Yes | | **Responses:** - `200`: OK → `integer` - `400`: 2: Invalid bundle Id. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://catalog.roblox.com/v1/favorites/bundles/{BUNDLEID}/count" ``` ### GET `/v1/favorites/users/{userId}/assets/{assetId}/favorite` Gets the favorite model for the asset and user. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | | | `assetId` | path | `integer (int64)` | Yes | | **Responses:** - `200`: OK → `Roblox.Catalog.Api.AssetFavoriteModel` - `400`: 1: Invalid user Id. 2: Invalid asset Id. - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Catalog.Api.AssetFavoriteModel`) See [Roblox.Catalog.Api.AssetFavoriteModel](#roblox-catalog-api-assetfavoritemodel) in Models. **Response example:** ```json { "assetId": 0, "userId": 0, "created": "2024-01-01T00:00:00Z" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://catalog.roblox.com/v1/favorites/users/{USERID}/assets/{ASSETID}/favorite" ``` ### POST `/v1/favorites/users/{userId}/assets/{assetId}/favorite` Create a favorite for an asset by the authenticated user. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | | | `assetId` | path | `integer (int64)` | Yes | | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: Invalid user Id. 2: Invalid asset Id. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 6: You are not authorized to perform this action. - `409`: 3: Asset is already favorited. - `429`: 5: This action was floodchecked. Please try again later. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://catalog.roblox.com/v1/favorites/users/{USERID}/assets/{ASSETID}/favorite" ``` ### DELETE `/v1/favorites/users/{userId}/assets/{assetId}/favorite` Delete a favorite for an asset by the authenticated user. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | | | `assetId` | path | `integer (int64)` | Yes | | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: Invalid user Id. 2: Invalid asset Id. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 6: You are not authorized to perform this action. - `409`: 4: Asset is already not favorited. - `429`: 5: This action was floodchecked. Please try again later. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://catalog.roblox.com/v1/favorites/users/{USERID}/assets/{ASSETID}/favorite" ``` ### GET `/v1/favorites/users/{userId}/bundles/{bundleId}/favorite` Gets the favorite model for the bundle and user. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | | | `bundleId` | path | `integer (int64)` | Yes | | **Responses:** - `200`: OK → `Roblox.Catalog.Api.BundleFavoriteModel` - `400`: 1: Invalid user Id. 2: Invalid bundle Id. - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Catalog.Api.BundleFavoriteModel`) See [Roblox.Catalog.Api.BundleFavoriteModel](#roblox-catalog-api-bundlefavoritemodel) in Models. **Response example:** ```json { "bundleId": 0, "userId": 0, "created": "2024-01-01T00:00:00Z" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://catalog.roblox.com/v1/favorites/users/{USERID}/bundles/{BUNDLEID}/favorite" ``` ### POST `/v1/favorites/users/{userId}/bundles/{bundleId}/favorite` Create a favorite for the bundle by the authenticated user. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | | | `bundleId` | path | `integer (int64)` | Yes | | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: Invalid user Id. 2: Invalid bundle Id. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 6: You are not authorized to perform this action. - `409`: 3: Bundle is already favorited. - `429`: 5: This action was floodchecked. Please try again later. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://catalog.roblox.com/v1/favorites/users/{USERID}/bundles/{BUNDLEID}/favorite" ``` ### DELETE `/v1/favorites/users/{userId}/bundles/{bundleId}/favorite` Delete favorite for the bundle by the authenticated user. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | | | `bundleId` | path | `integer (int64)` | Yes | | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: Invalid user Id. 2: Invalid bundle Id. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 6: You are not authorized to perform this action. - `409`: 4: Bundle is already not favorited. - `429`: 5: This action was floodchecked. Please try again later. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://catalog.roblox.com/v1/favorites/users/{USERID}/bundles/{BUNDLEID}/favorite" ``` ### GET `/v1/favorites/users/{userId}/favorites/{assetTypeId}/assets` Lists the marketplace assets favorited by a given user with the given assetTypeId. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | | | `assetTypeId` | path | `integer (int32)` | Yes | | | `limit` | query | `integer (int32)` | No | The number of results per request. Valid values: `10`, `18`, `24`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `string` | No | The order the results are sorted in. Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Catalog.Api.CatalogSearchDetailedResponseItem]` - `400`: 1: Invalid user Id. 8: Ascending order is not allowed. 11: Invalid asset type id. - `500`: 99: Internal server error. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Catalog.Api.CatalogSearchDetailedResponseItem]`) See [Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Catalog.Api.CatalogSearchDetailedResponseItem]](#roblox-web-webapi-models-apipageresponse-roblox-catalog-api-catalogsearchdetailedresponseitem-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "id": "...", "itemType": "...", "assetType": "...", "bundleType": "...", "isRecolorable": "...", "name": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://catalog.roblox.com/v1/favorites/users/{USERID}/favorites/{ASSETTYPEID}/assets" ``` ### GET `/v1/favorites/users/{userId}/favorites/{subtypeId}/bundles` Lists the bundles favorited by a given user with the given bundle subtypeId.Switched to EAAS style pagination cursors since July 2024. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | | | `subtypeId` | path | `integer (int32)` | Yes | | | `itemsPerPage` | query | `integer (int32)` | No | | | `cursor` | query | `string` | No | | | `isPrevious` | query | `boolean` | No | | **Responses:** - `200`: OK → `Roblox.Catalog.Api.FavoriteBundlesResponse` - `400`: 1: Invalid user Id. 3: Cannot request so many bundles at once. 10: Invalid previous pagination request. Please provide a cursor when isPrevious is true - `401`: 0: Authorization has been denied for this request. - `403`: 6: You are not authorized to perform this action. - `500`: 11: Internal server error. Please check if you have provided correct pagination cursor **Response fields** (`Roblox.Catalog.Api.FavoriteBundlesResponse`) See [Roblox.Catalog.Api.FavoriteBundlesResponse](#roblox-catalog-api-favoritebundlesresponse) in Models. **Response example:** ```json { "favorites": [ { "id": "...", "name": "...", "description": "...", "bundleType": "...", "isRecolorable": "...", "items": "..." } ], "moreFavorites": false, "nextCursor": "string", "previousCursor": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://catalog.roblox.com/v1/favorites/users/{USERID}/favorites/{SUBTYPEID}/bundles" ``` ### GET `/v1/subcategories` Lists Subcategory Names and their Ids. **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `object` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://catalog.roblox.com/v1/subcategories" ``` ### GET `/v1/users/{userId}/bundles` Lists the bundles owned by a given user. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | | | `cursor` | query | `string` | No | | | `limit` | query | `integer (int32)` | No | | | `sortOrder` | query | `integer (int32)` | No | Valid values: `1`, `2` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Catalog.Api.OwnedBundleModel]` - `400`: 1: Invalid bundle **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Catalog.Api.OwnedBundleModel]`) See [Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Catalog.Api.OwnedBundleModel]](#roblox-web-webapi-models-apipageresponse-roblox-catalog-api-ownedbundlemodel-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "id": "...", "name": "...", "bundleType": "...", "creator": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://catalog.roblox.com/v1/users/{USERID}/bundles" ``` ### GET `/v1/users/{userId}/bundles/{bundleType}` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | | | `bundleType` | path | `integer (int32)` | Yes | Valid values: `1`, `2`, `3`, `4` | | `cursor` | query | `string` | Yes | | | `limit` | query | `integer (int32)` | No | | | `sortOrder` | query | `integer (int32)` | No | Valid values: `1`, `2` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Catalog.Api.OwnedBundleModel]` **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Catalog.Api.OwnedBundleModel]`) See [Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Catalog.Api.OwnedBundleModel]](#roblox-web-webapi-models-apipageresponse-roblox-catalog-api-ownedbundlemodel-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "id": "...", "name": "...", "bundleType": "...", "creator": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://catalog.roblox.com/v1/users/{USERID}/bundles/{BUNDLETYPE}?cursor={VALUE}" ``` ### POST `/v1/catalog/items/details` Returns details for one or more catalog items. There is an item count limit per request. Exceeding this returns 400 Bad Request. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Catalog.Api.MultigetItemDetailsRequestModel` See [Roblox.Catalog.Api.MultigetItemDetailsRequestModel](#roblox-catalog-api-multigetitemdetailsrequestmodel) in Models. **Request example:** ```json { "items": [ { "itemType": "...", "id": "..." } ] } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Catalog.Api.CatalogSearchDetailedResponseItemV2]` - `400`: 2: Invalid count - `403`: 0: Token Validation Failed 7: User is unauthorized. - `429`: 8: The flood limit has been exceeded. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Catalog.Api.CatalogSearchDetailedResponseItemV2]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Catalog.Api.CatalogSearchDetailedResponseItemV2]](#roblox-web-webapi-models-apiarrayresponse-roblox-catalog-api-catalogsearchdetailedresponseitemv2-) in Models. **Response example:** ```json { "data": [ { "bundledItems": "...", "taxonomy": "...", "itemCreatedUtc": "...", "discountInformation": "...", "id": "...", "itemType": "..." } ] } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://catalog.roblox.com/v1/catalog/items/details" \ -H "Content-Type: application/json" \ -d '{ "items": [ { "itemType": "...", "id": "..." } ] }' ``` ### POST `/v1/topic/get-topics` Get topic given TopicRequestModel. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Catalog.Api.TopicRequestModel` See [Roblox.Catalog.Api.TopicRequestModel](#roblox-catalog-api-topicrequestmodel) in Models. **Request example:** ```json { "items": [ { "TargetId": "...", "ItemType": "..." } ], "selectTopics": [ "string" ], "inputQuery": "string", "maxResult": 0, "genderType": 1 } ``` **Responses:** - `200`: OK → `Roblox.Catalog.Api.TopicResponse` - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Catalog.Api.TopicResponse`) See [Roblox.Catalog.Api.TopicResponse](#roblox-catalog-api-topicresponse) in Models. **Response example:** ```json { "topics": [ { "displayName": "...", "originalTopicName": "..." } ], "error": { "Message": "string", "Code": 0 } } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://catalog.roblox.com/v1/topic/get-topics" \ -H "Content-Type: application/json" \ -d '{ "items": [ { "TargetId": "...", "ItemType": "..." } ], "selectTopics": [ "string" ], "inputQuery": "string", "maxResult": 0, "genderType": 1 }' ``` ## Models ### Roblox.Catalog.Api.AssetFavoriteModel A model to represent asset favorites. | Property | Type | Required | Description | |----------|------|----------|-------------| | `assetId` | `integer` | No | The Id of the asset being favorited. | | `userId` | `integer` | No | The Id of the user favoriting the asset. | | `created` | `string` | No | The time at which the user favorited the asset. | ### Roblox.Catalog.Api.BundleCreatorModel | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | | | `name` | `string` | No | | | `type` | `string` | No | | | `hasVerifiedBadge` | `boolean` | No | | ### Roblox.Catalog.Api.BundleDetailsModel The hydration model representing a bundle on marketplace. Returned in all bundles controller endpoints. Bound in the game-engine MarketplaceService.GetProductInfo method. https://create.roblox.com/docs/reference/engine/classes/MarketplaceService#GetProductInfo. | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | | | `name` | `string` | No | | | `description` | `string` | No | | | `bundleType` | `string` | No | | | `isRecolorable` | `boolean` | No | | | `items` | `Roblox.Catalog.Api.BundleItemDetailModel[]` | No | | | `creator` | `Roblox.Catalog.Api.BundleCreatorModel` | No | | | `product` | `Roblox.Catalog.Api.BundleProductModel` | No | | | `itemRestrictions` | `integer enum (9 values)[]` | No | | | `collectibleItemDetail` | `Roblox.Catalog.Api.CollectibleItemDetail` | No | | | `discountInformation` | `Roblox.Catalog.Api.DiscountInformation` | No | | ### Roblox.Catalog.Api.BundleFavoriteModel A model to represent bundle favorites. | Property | Type | Required | Description | |----------|------|----------|-------------| | `bundleId` | `integer` | No | The Id of the bundle being favorited. | | `userId` | `integer` | No | The Id of the user favoriting the bundle. | | `created` | `string` | No | The time at which the user favorited the bundle. | ### Roblox.Catalog.Api.BundleItemDetailModel | Property | Type | Required | Description | |----------|------|----------|-------------| | `owned` | `boolean` | No | | | `id` | `integer` | No | | | `name` | `string` | No | | | `type` | `string` | No | | | `supportsHeadShapes` | `boolean` | No | Whether the bundle item supports head shapes. | | `assetType` | `integer` | No | The asset type of the bundle item, if it's an asset. | ### Roblox.Catalog.Api.BundleItemDetailModelV2 The is the beta (non game-engine) version of BundleItemDetailModel for internal consumption on Roblox web and universal-app. | Property | Type | Required | Description | |----------|------|----------|-------------| | `owned` | `boolean` | No | | | `id` | `integer` | No | | | `name` | `string` | No | | | `type` | `string` | No | | | `supportsHeadShapes` | `boolean` | No | Whether the bundle item supports head shapes. | | `assetType` | `integer` | No | The asset type of the bundle item, if it's an asset. | ### Roblox.Catalog.Api.BundleProductModel | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | | | `type` | `string` | No | | | `isPublicDomain` | `boolean` | No | | | `isForSale` | `boolean` | No | | | `priceInRobux` | `integer` | No | | | `isFree` | `boolean` | No | | | `noPriceText` | `string` | No | | | `premiumPricing` | `Roblox.Catalog.Api.PremiumPricingModel` | No | | ### Roblox.Catalog.Api.CatalogSearchDetailedResponseItem Game-engine version of fully hydrated asset or bundle in catalog marketplace. Documented here: https://create.roblox.com/docs/reference/engine/classes/AvatarEditorService#SearchCatalog. | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | The Item Id. | | `itemType` | `1 \| 2` | No | The Roblox.Catalog.Api.CatalogSearchDetailedResponseItem.ItemType item type. ['Asset' = 1, 'Bundle' = 2] | | `assetType` | `integer enum (85 values)` | No | The Roblox.Platform.Assets.AssetType serialized if item is an asset. Values: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 16, 17, 18, 19, 21, 22, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92 | | `bundleType` | `1 \| 2 \| 3 \| 4` | No | The Roblox.Platform.Bundles.Core.BundleType serialized if item is a bundle. | | `isRecolorable` | `boolean` | No | Gets or sets the property whether a bundle is recolorable or not. Not serialized for asset. | | `name` | `string` | No | The item name. | | `description` | `string` | No | The item description. | | `productId` | `integer` | No | The product id of corresponding item. | | `bundledItems` | `Roblox.Catalog.Api.BundleItemDetailModel[]` | No | The System.Collections.Generic.IEnumerable`1 contained in the bundle, serialized if item is a bundle. | | `itemStatus` | `1 \| 2 \| 7 \| 8[]` | No | The System.Collections.Generic.IEnumerable`1 if item has Roblox.Catalog.Api.CatalogItemStatus. | | `itemRestrictions` | `integer enum (9 values)[]` | No | The System.Collections.Generic.IEnumerable`1 if item has Roblox.Catalog.Api.CatalogItemRestriction. | | `creatorHasVerifiedBadge` | `boolean` | No | The verified status of a creator. | | `creatorType` | `0 \| 1 \| 2` | No | The Roblox.Catalog.Api.CatalogSearchDetailedResponseItem.CreatorType of the item's creator. | | `creatorTargetId` | `integer` | No | The creator id of the item's creator. | | `creatorName` | `string` | No | The creator name of the item's creator. | | `price` | `integer` | No | The item's price. | | `lowestPrice` | `integer` | No | The item's lowest price, only if the item is resellable and there are resellers. | | `lowestResalePrice` | `integer` | No | The item's lowest resale price, only if the item is resellable and there are resellers, including current user. | | `priceStatus` | `string` | No | The localized string item status if the item's price should not be displayed. | | `unitsAvailableForConsumption` | `integer` | No | The number of items in stock, only if the item is resellable and is limitedEdition. | | `favoriteCount` | `integer` | No | The number of times the item has been favorited. | | `offSaleDeadline` | `string` | No | When the item will go off sale, if the item has an off deadline. | | `collectibleItemId` | `string` | No | The item's collectible item id. It is an UUID if a item is collectible type. Otherwise, it is null. | | `totalQuantity` | `integer` | No | The collectible or limited-unique item's total quantity of unique instances. | | `saleLocationType` | `integer enum (8 values)` | No | The sale location type of the item. ['NotApplicable' = 0, 'ShopOnly' = 1, 'MyExperiencesOnly' = 2, 'ShopAndMyExperiences' = 3, 'ExperiencesById' = 4, 'ShopAndAllExperiences' = 5, 'ExperiencesDevApiOnly' = 6, 'ShopAndExperiencesById' = 7] Values: 0, 1, 2, 3, 4, 5, 6, 7 | | `hasResellers` | `boolean` | No | An indicator if the item has resellers or not (null if not resellable). | | `isOffSale` | `boolean` | No | An indicator if the item is off sale or not. | | `quantityLimitPerUser` | `integer` | No | Quantity limit for how many instances a user can buy. | | `supportsHeadShapes` | `boolean` | No | Whether the item supports head shapes. | | `timedOptions` | `Roblox.Catalog.Api.TimedOption[]` | No | The timed options for the item. | ### Roblox.Catalog.Api.CatalogSearchDetailedResponseItemV2 This is the beta (non game-engine) version of our hydration model representing asset or bundle in marketplace. | Property | Type | Required | Description | |----------|------|----------|-------------| | `bundledItems` | `Roblox.Catalog.Api.BundleItemDetailModelV2[]` | No | The System.Collections.Generic.IEnumerable`1 contained in the bundle, serialized if item is a bundle. | | `taxonomy` | `Roblox.Catalog.Api.TaxonomyModel[]` | No | The taxonomy ids and names for this item. | | `itemCreatedUtc` | `string` | No | The UTC creation date-time of the asset or bundle. | | `discountInformation` | `Roblox.Catalog.Api.DiscountInformation` | No | | | `id` | `integer` | No | The Item Id. | | `itemType` | `1 \| 2` | No | The Roblox.Catalog.Api.CatalogSearchDetailedResponseItem.ItemType item type. ['Asset' = 1, 'Bundle' = 2] | | `assetType` | `integer enum (85 values)` | No | The Roblox.Platform.Assets.AssetType serialized if item is an asset. Values: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 16, 17, 18, 19, 21, 22, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92 | | `bundleType` | `1 \| 2 \| 3 \| 4` | No | The Roblox.Platform.Bundles.Core.BundleType serialized if item is a bundle. | | `isRecolorable` | `boolean` | No | Gets or sets the property whether a bundle is recolorable or not. Not serialized for asset. | | `name` | `string` | No | The item name. | | `description` | `string` | No | The item description. | | `productId` | `integer` | No | The product id of corresponding item. | | `itemStatus` | `1 \| 2 \| 7 \| 8[]` | No | The System.Collections.Generic.IEnumerable`1 if item has Roblox.Catalog.Api.CatalogItemStatus. | | `itemRestrictions` | `integer enum (9 values)[]` | No | The System.Collections.Generic.IEnumerable`1 if item has Roblox.Catalog.Api.CatalogItemRestriction. | | `creatorHasVerifiedBadge` | `boolean` | No | The verified status of a creator. | | `creatorType` | `0 \| 1 \| 2` | No | The Roblox.Catalog.Api.CatalogSearchDetailedResponseItem.CreatorType of the item's creator. | | `creatorTargetId` | `integer` | No | The creator id of the item's creator. | | `creatorName` | `string` | No | The creator name of the item's creator. | | `price` | `integer` | No | The item's price. | | `lowestPrice` | `integer` | No | The item's lowest price, only if the item is resellable and there are resellers. | | `lowestResalePrice` | `integer` | No | The item's lowest resale price, only if the item is resellable and there are resellers, including current user. | | `priceStatus` | `string` | No | The localized string item status if the item's price should not be displayed. | | `unitsAvailableForConsumption` | `integer` | No | The number of items in stock, only if the item is resellable and is limitedEdition. | | `favoriteCount` | `integer` | No | The number of times the item has been favorited. | | `offSaleDeadline` | `string` | No | When the item will go off sale, if the item has an off deadline. | | `collectibleItemId` | `string` | No | The item's collectible item id. It is an UUID if a item is collectible type. Otherwise, it is null. | | `totalQuantity` | `integer` | No | The collectible or limited-unique item's total quantity of unique instances. | | `saleLocationType` | `integer enum (8 values)` | No | The sale location type of the item. ['NotApplicable' = 0, 'ShopOnly' = 1, 'MyExperiencesOnly' = 2, 'ShopAndMyExperiences' = 3, 'ExperiencesById' = 4, 'ShopAndAllExperiences' = 5, 'ExperiencesDevApiOnly' = 6, 'ShopAndExperiencesById' = 7] Values: 0, 1, 2, 3, 4, 5, 6, 7 | | `hasResellers` | `boolean` | No | An indicator if the item has resellers or not (null if not resellable). | | `isOffSale` | `boolean` | No | An indicator if the item is off sale or not. | | `quantityLimitPerUser` | `integer` | No | Quantity limit for how many instances a user can buy. | | `supportsHeadShapes` | `boolean` | No | Whether the item supports head shapes. | | `timedOptions` | `Roblox.Catalog.Api.TimedOption[]` | No | The timed options for the item. | ### Roblox.Catalog.Api.CategoryModel Response model for category. | Property | Type | Required | Description | |----------|------|----------|-------------| | `category` | `integer enum (14 values)` | No | Category type. Values: 0, 1, 2, 3, 4, 5, 11, 12, 13, 14, 15, 16, 17, 18 | | `taxonomy` | `string` | No | The associated public facing web_stable_id corresponding to internal taxonomy uuid for this category. | | `assetTypeIds` | `integer[]` | No | List of AssetTypeIds corresponding to AssetType enum that this category returns. | | `bundleTypeIds` | `integer[]` | No | List of bundleTypeIds corresponding to BundleType enum that this category returns. | | `categoryId` | `integer` | No | Category id. | | `name` | `string` | No | Category name. | | `orderIndex` | `integer` | No | Category order index. | | `subcategories` | `Roblox.Catalog.Api.SubcategoryModel[]` | No | Subcategories under this category. | | `isSearchable` | `boolean` | No | Gets or sets whether the category is searchable in search bar. | ### Roblox.Catalog.Api.CollectibleItemDetail | Property | Type | Required | Description | |----------|------|----------|-------------| | `collectibleItemId` | `string` | No | | | `collectibleProductId` | `string` | No | | | `price` | `integer` | No | | | `lowestPrice` | `integer` | No | | | `lowestResalePrice` | `integer` | No | | | `totalQuantity` | `integer` | No | | | `unitsAvailable` | `integer` | No | | | `saleLocation` | `Roblox.Catalog.Api.SaleLocation` | No | | | `hasResellers` | `boolean` | No | | | `saleStatus` | `0 \| 1 \| 2 \| 3 \| 4` | No | ['Invalid' = 0, 'Draft' = 1, 'OffSale' = 2, 'OnSale' = 3, 'PendingSale' = 4] | | `quantityLimitPerUser` | `integer` | No | | | `offSaleDeadline` | `string` | No | | | `collectibleItemType` | `0 \| 1 \| 2` | No | The type of collectible item, limited or non-limited for now. ['Invalid' = 0, 'Limited' = 1, 'NonLimited' = 2] | | `lowestAvailableResaleProductId` | `string` | No | | | `lowestAvailableResaleItemInstanceId` | `string` | No | | | `resaleRestriction` | `0 \| 1 \| 2` | No | ['Invalid' = 0, 'None' = 1, 'Disabled' = 2] | ### Roblox.Catalog.Api.Discount | Property | Type | Required | Description | |----------|------|----------|-------------| | `robuxDiscountAmount` | `integer` | No | | | `robuxDiscountPercentage` | `number` | No | | | `discountCampaign` | `string` | No | | | `localizedDiscountAttribution` | `string` | No | | ### Roblox.Catalog.Api.DiscountInformation | Property | Type | Required | Description | |----------|------|----------|-------------| | `originalPrice` | `integer` | No | | | `totalDiscountPercentage` | `number` | No | | | `totalDiscountAmount` | `integer` | No | | | `discounts` | `Roblox.Catalog.Api.Discount[]` | No | | ### Roblox.Catalog.Api.FavoriteBundlesResponse A response containing favorited bundles and whether there are more. | Property | Type | Required | Description | |----------|------|----------|-------------| | `favorites` | `Roblox.Catalog.Api.BundleDetailsModel[]` | No | Collection of favorited bundles and associated details. | | `moreFavorites` | `boolean` | No | True if there exists a next page of favorited bundles. | | `nextCursor` | `string` | No | Pagination cursor for the next page. | | `previousCursor` | `string` | No | Pagination cursor for the previous page. | ### Roblox.Catalog.Api.MultigetItemDetailsRequestItem | Property | Type | Required | Description | |----------|------|----------|-------------| | `itemType` | `1 \| 2` | No | ['Asset' = 1, 'Bundle' = 2] | | `id` | `integer` | No | | ### Roblox.Catalog.Api.MultigetItemDetailsRequestModel | Property | Type | Required | Description | |----------|------|----------|-------------| | `items` | `Roblox.Catalog.Api.MultigetItemDetailsRequestItem[]` | No | The items to retrieve details for. Each endpoint has an item count limit per request. | ### Roblox.Catalog.Api.OwnedBundleModel A model to represent owned bundles. | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | | | `name` | `string` | No | | | `bundleType` | `string` | No | | | `creator` | `Roblox.Catalog.Api.BundleCreatorModel` | No | | ### Roblox.Catalog.Api.PremiumPricingModel Defines the Premium pricing for a catalog item. | Property | Type | Required | Description | |----------|------|----------|-------------| | `premiumDiscountPercentage` | `integer` | No | The Premium discount percentage for a catalog item. | | `premiumPriceInRobux` | `integer` | No | The Premium price for a catalog item. | ### Roblox.Catalog.Api.SaleLocation SaleLocation information for a collectible item (asset or bundle). | Property | Type | Required | Description | |----------|------|----------|-------------| | `saleLocationType` | `integer enum (8 values)` | No | Sale location type related for an item detail. ['NotApplicable' = 0, 'ShopOnly' = 1, 'MyExperiencesOnly' = 2, 'ShopAndMyExperiences' = 3, 'ExperiencesById' = 4, 'ShopAndAllExperiences' = 5, 'ExperiencesDevApiOnly' = 6, 'ShopAndExperiencesById' = 7] Values: 0, 1, 2, 3, 4, 5, 6, 7 | | `saleLocationTypeId` | `integer` | No | | | `universeIds` | `integer[]` | No | | | `enabledUniverseIds` | `integer[]` | No | | ### Roblox.Catalog.Api.SubcategoryModel Response model for subcategory. | Property | Type | Required | Description | |----------|------|----------|-------------| | `subcategory` | `integer enum (52 values)` | No | Subcategory type. Values: 0, 1, 2, 3, 4, 5, 9, 10, 12, 13, 14, 15, 19, 20, 21, 22, 23, 24, 25, 26, 27, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67 | | `taxonomy` | `string` | No | The taxonomy UUID associated with this node. | | `assetTypeIds` | `integer[]` | No | List of AssetTypeIds corresponding to AssetType enum that this category returns. | | `bundleTypeIds` | `integer[]` | No | List of bundleTypeIds corresponding to BundleType enum that this category returns. | | `subcategoryId` | `integer` | No | Subcategory id. | | `name` | `string` | No | Subcategory name. | | `shortName` | `string` | No | Subcategory short name. | ### Roblox.Catalog.Api.TaxonomyModel public api model coaslescing taxonomy information for a single item. | Property | Type | Required | Description | |----------|------|----------|-------------| | `taxonomyId` | `string` | No | The id value to pass into taxonomy field in SearchV2 catalog-api. | | `taxonomyName` | `string` | No | The localized human readable name of the category. | ### Roblox.Catalog.Api.TimedOption Defines a timed option for a catalog item. | Property | Type | Required | Description | |----------|------|----------|-------------| | `days` | `integer` | No | The days for the timed option. | | `price` | `integer` | No | The price for the timed option. | | `discountInformation` | `Roblox.Catalog.Api.DiscountInformation` | No | | | `selected` | `boolean` | No | To indicate if this option is selected by the client. | ### Roblox.Catalog.Api.TopicModel Response model for avatar topics. | Property | Type | Required | Description | |----------|------|----------|-------------| | `displayName` | `string` | No | The display topic name. | | `originalTopicName` | `string` | No | The original topic name stored in the table. | ### Roblox.Catalog.Api.TopicRequestModel | Property | Type | Required | Description | |----------|------|----------|-------------| | `items` | `Roblox.MarketplaceTopicDiscovery.TopicDiscoveryService.V1Beta1.AvatarItem[]` | No | | | `selectTopics` | `string[]` | No | | | `inputQuery` | `string` | No | | | `maxResult` | `integer` | No | Maximum number of topic results returned from the server. | | `genderType` | `1 \| 2 \| 3` | No | ['Unknown' = 1, 'Male' = 2, 'Female' = 3] | ### Roblox.Catalog.Api.TopicResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `topics` | `Roblox.Catalog.Api.TopicModel[]` | No | | | `error` | `Roblox.MarketplaceTopicDiscovery.TopicDiscoveryService.V1Beta1.Error` | No | | ### Roblox.MarketplaceTopicDiscovery.TopicDiscoveryService.V1Beta1.AvatarItem | Property | Type | Required | Description | |----------|------|----------|-------------| | `TargetId` | `integer` | No | | | `ItemType` | `0 \| 1 \| 2` | No | ['Invalid' = 0, 'Asset' = 1, 'Bundle' = 2] | ### Roblox.MarketplaceTopicDiscovery.TopicDiscoveryService.V1Beta1.Error | Property | Type | Required | Description | |----------|------|----------|-------------| | `Message` | `string` | No | | | `Code` | `integer` | No | | ### Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Catalog.Api.BundleDetailsModel] | Property | Type | Required | Description | |----------|------|----------|-------------| | `data` | `Roblox.Catalog.Api.BundleDetailsModel[]` | No | | ### Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Catalog.Api.CatalogSearchDetailedResponseItemV2] | Property | Type | Required | Description | |----------|------|----------|-------------| | `data` | `Roblox.Catalog.Api.CatalogSearchDetailedResponseItemV2[]` | No | | ### Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Catalog.Api.BundleDetailsModel] | Property | Type | Required | Description | |----------|------|----------|-------------| | `previousPageCursor` | `string` | No | | | `nextPageCursor` | `string` | No | | | `data` | `Roblox.Catalog.Api.BundleDetailsModel[]` | No | | ### Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Catalog.Api.CatalogSearchDetailedResponseItem] | Property | Type | Required | Description | |----------|------|----------|-------------| | `previousPageCursor` | `string` | No | | | `nextPageCursor` | `string` | No | | | `data` | `Roblox.Catalog.Api.CatalogSearchDetailedResponseItem[]` | No | | ### Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Catalog.Api.OwnedBundleModel] | Property | Type | Required | Description | |----------|------|----------|-------------| | `previousPageCursor` | `string` | No | | | `nextPageCursor` | `string` | No | | | `data` | `Roblox.Catalog.Api.OwnedBundleModel[]` | No | | --- name: "Catalog Api v2" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://catalog.roblox.com" version: "v2" endpoints: 2 auth: [cookie] --- # Catalog Api v2 **API Version:** v2 **Base URL:** `https://catalog.roblox.com` ## Endpoints ### GET `/v2/assets/{assetId}/bundles` Lists bundles that contain the given asset (hydrated search-detail shape for marketplace). **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetId` | path | `integer (int64)` | Yes | Asset id. | | `Roblox-Place-Id` | header | `integer (int64)` | Yes | Roblox-Place-Id header. | | `Roblox-Game-Id` | header | `string` | Yes | Roblox-Game-Id header. | | `limit` | query | `integer (int32)` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `string` | No | The order the results are sorted in. Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Catalog.Api.CatalogSearchDetailedResponseItem]` - `400`: 1: Invalid assetId 4: Invalid Cursor. - `403`: 7: User is unauthorized. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Catalog.Api.CatalogSearchDetailedResponseItem]`) See [Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Catalog.Api.CatalogSearchDetailedResponseItem]](#roblox-web-webapi-models-apipageresponse-roblox-catalog-api-catalogsearchdetailedresponseitem-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "id": "...", "itemType": "...", "assetType": "...", "bundleType": "...", "isRecolorable": "...", "name": "..." } ] } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://catalog.roblox.com/v2/assets/{ASSETID}/bundles" ``` ### GET `/v2/search/items/details` Search for catalog items. This endpoint is for search by item type ids. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `Taxonomy` | query | `string` | No | | | `AssetTypeIds` | query | `array` | No | | | `BundleTypeIds` | query | `array` | No | | | `CategoryFilter` | query | `integer (int32)` | No | Valid values: `0`, `1`, `2`, `3`, `4`, `5`, `6`, `7` | | `SortAggregation` | query | `integer (int32)` | No | Valid values: `0`, `1`, `2`, `3`, `4`, `5` | | `SortType` | query | `integer (int32)` | No | Valid values: `0`, `1`, `2`, `3`, `4`, `5` | | `CreatorType` | query | `integer (int32)` | No | Valid values: `0`, `1`, `2` | | `CreatorTargetId` | query | `integer (int64)` | No | | | `CreatorName` | query | `string` | No | | | `MaxPrice` | query | `integer (int32)` | No | | | `MinPrice` | query | `integer (int32)` | No | | | `Keyword` | query | `string` | No | | | `IncludeNotForSale` | query | `boolean` | No | | | `TriggeredByTopicDiscovery` | query | `boolean` | No | | | `SalesTypeFilter` | query | `integer (int32)` | No | Valid values: `0`, `1`, `2`, `3`, `4` | | `Topics` | query | `string` | No | The input topics format is split by ",". E.g "topics=cat,hat,red". | | `limit` | query | `integer (int32)` | No | The number of results per request. Valid values: `10`, `28`, `30`, `60`, `120` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `string` | No | The order the results are sorted in. Valid values: `Desc` | **Responses:** - `200`: OK → `Roblox.Catalog.Api.CatalogSearchPageResponse[Roblox.Catalog.Api.CatalogSearchDetailedResponseItemV2]` - `400`: 1: Category subcategory selection not supported. 2: Creator id not found. 3: Creator type not found or cannot search by CreatorTargetId with CreatorType.All 4: Genre not found. 5: Sort combination not supported. 6: Invalid price range. 10: StartRequest is invalid. - `403`: 7: User is unauthorized. 22: In-experience search is denied for this place or universe. - `429`: 8: The flood limit has been exceeded. 8: The flood limit has been exceeded. - `503`: 18: Search request timed out **Response fields** (`Roblox.Catalog.Api.CatalogSearchPageResponse[Roblox.Catalog.Api.CatalogSearchDetailedResponseItemV2]`) See [Roblox.Catalog.Api.CatalogSearchPageResponse[Roblox.Catalog.Api.CatalogSearchDetailedResponseItemV2]](#roblox-catalog-api-catalogsearchpageresponse-roblox-catalog-api-catalogsearchdetailedresponseitemv2-) in Models. **Response example:** ```json { "keyword": "string", "elasticsearchDebugInfo": { "elasticsearchQuery": "string", "isFromCache": false, "indexName": "string", "isTerminatedEarly": false, "isForceTerminationEnabledByRequest": false, "searchResultDataSource": "string" }, "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "bundledItems": "...", "taxonomy": "...", "itemCreatedUtc": "...", "discountInformation": "...", "id": "...", "itemType": "..." } ] } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://catalog.roblox.com/v2/search/items/details" ``` ## Models ### Roblox.Catalog.Api.BundleItemDetailModel | Property | Type | Required | Description | |----------|------|----------|-------------| | `owned` | `boolean` | No | | | `id` | `integer` | No | | | `name` | `string` | No | | | `type` | `string` | No | | | `supportsHeadShapes` | `boolean` | No | Whether the bundle item supports head shapes. | | `assetType` | `integer` | No | The asset type of the bundle item, if it's an asset. | ### Roblox.Catalog.Api.BundleItemDetailModelV2 The is the beta (non game-engine) version of BundleItemDetailModel for internal consumption on Roblox web and universal-app. | Property | Type | Required | Description | |----------|------|----------|-------------| | `owned` | `boolean` | No | | | `id` | `integer` | No | | | `name` | `string` | No | | | `type` | `string` | No | | | `supportsHeadShapes` | `boolean` | No | Whether the bundle item supports head shapes. | | `assetType` | `integer` | No | The asset type of the bundle item, if it's an asset. | ### Roblox.Catalog.Api.CatalogSearchDetailedResponseItem Game-engine version of fully hydrated asset or bundle in catalog marketplace. Documented here: https://create.roblox.com/docs/reference/engine/classes/AvatarEditorService#SearchCatalog. | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | The Item Id. | | `itemType` | `1 \| 2` | No | The Roblox.Catalog.Api.CatalogSearchDetailedResponseItem.ItemType item type. ['Asset' = 1, 'Bundle' = 2] | | `assetType` | `integer enum (85 values)` | No | The Roblox.Platform.Assets.AssetType serialized if item is an asset. Values: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 16, 17, 18, 19, 21, 22, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92 | | `bundleType` | `1 \| 2 \| 3 \| 4` | No | The Roblox.Platform.Bundles.Core.BundleType serialized if item is a bundle. | | `isRecolorable` | `boolean` | No | Gets or sets the property whether a bundle is recolorable or not. Not serialized for asset. | | `name` | `string` | No | The item name. | | `description` | `string` | No | The item description. | | `productId` | `integer` | No | The product id of corresponding item. | | `bundledItems` | `Roblox.Catalog.Api.BundleItemDetailModel[]` | No | The System.Collections.Generic.IEnumerable`1 contained in the bundle, serialized if item is a bundle. | | `itemStatus` | `1 \| 2 \| 7 \| 8[]` | No | The System.Collections.Generic.IEnumerable`1 if item has Roblox.Catalog.Api.CatalogItemStatus. | | `itemRestrictions` | `integer enum (9 values)[]` | No | The System.Collections.Generic.IEnumerable`1 if item has Roblox.Catalog.Api.CatalogItemRestriction. | | `creatorHasVerifiedBadge` | `boolean` | No | The verified status of a creator. | | `creatorType` | `0 \| 1 \| 2` | No | The Roblox.Catalog.Api.CatalogSearchDetailedResponseItem.CreatorType of the item's creator. | | `creatorTargetId` | `integer` | No | The creator id of the item's creator. | | `creatorName` | `string` | No | The creator name of the item's creator. | | `price` | `integer` | No | The item's price. | | `lowestPrice` | `integer` | No | The item's lowest price, only if the item is resellable and there are resellers. | | `lowestResalePrice` | `integer` | No | The item's lowest resale price, only if the item is resellable and there are resellers, including current user. | | `priceStatus` | `string` | No | The localized string item status if the item's price should not be displayed. | | `unitsAvailableForConsumption` | `integer` | No | The number of items in stock, only if the item is resellable and is limitedEdition. | | `favoriteCount` | `integer` | No | The number of times the item has been favorited. | | `offSaleDeadline` | `string` | No | When the item will go off sale, if the item has an off deadline. | | `collectibleItemId` | `string` | No | The item's collectible item id. It is an UUID if a item is collectible type. Otherwise, it is null. | | `totalQuantity` | `integer` | No | The collectible or limited-unique item's total quantity of unique instances. | | `saleLocationType` | `integer enum (8 values)` | No | The sale location type of the item. ['NotApplicable' = 0, 'ShopOnly' = 1, 'MyExperiencesOnly' = 2, 'ShopAndMyExperiences' = 3, 'ExperiencesById' = 4, 'ShopAndAllExperiences' = 5, 'ExperiencesDevApiOnly' = 6, 'ShopAndExperiencesById' = 7] Values: 0, 1, 2, 3, 4, 5, 6, 7 | | `hasResellers` | `boolean` | No | An indicator if the item has resellers or not (null if not resellable). | | `isOffSale` | `boolean` | No | An indicator if the item is off sale or not. | | `quantityLimitPerUser` | `integer` | No | Quantity limit for how many instances a user can buy. | | `supportsHeadShapes` | `boolean` | No | Whether the item supports head shapes. | | `timedOptions` | `Roblox.Catalog.Api.TimedOption[]` | No | The timed options for the item. | ### Roblox.Catalog.Api.CatalogSearchDetailedResponseItemV2 This is the beta (non game-engine) version of our hydration model representing asset or bundle in marketplace. | Property | Type | Required | Description | |----------|------|----------|-------------| | `bundledItems` | `Roblox.Catalog.Api.BundleItemDetailModelV2[]` | No | The System.Collections.Generic.IEnumerable`1 contained in the bundle, serialized if item is a bundle. | | `taxonomy` | `Roblox.Catalog.Api.TaxonomyModel[]` | No | The taxonomy ids and names for this item. | | `itemCreatedUtc` | `string` | No | The UTC creation date-time of the asset or bundle. | | `discountInformation` | `Roblox.Catalog.Api.DiscountInformation` | No | | | `id` | `integer` | No | The Item Id. | | `itemType` | `1 \| 2` | No | The Roblox.Catalog.Api.CatalogSearchDetailedResponseItem.ItemType item type. ['Asset' = 1, 'Bundle' = 2] | | `assetType` | `integer enum (85 values)` | No | The Roblox.Platform.Assets.AssetType serialized if item is an asset. Values: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 16, 17, 18, 19, 21, 22, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92 | | `bundleType` | `1 \| 2 \| 3 \| 4` | No | The Roblox.Platform.Bundles.Core.BundleType serialized if item is a bundle. | | `isRecolorable` | `boolean` | No | Gets or sets the property whether a bundle is recolorable or not. Not serialized for asset. | | `name` | `string` | No | The item name. | | `description` | `string` | No | The item description. | | `productId` | `integer` | No | The product id of corresponding item. | | `itemStatus` | `1 \| 2 \| 7 \| 8[]` | No | The System.Collections.Generic.IEnumerable`1 if item has Roblox.Catalog.Api.CatalogItemStatus. | | `itemRestrictions` | `integer enum (9 values)[]` | No | The System.Collections.Generic.IEnumerable`1 if item has Roblox.Catalog.Api.CatalogItemRestriction. | | `creatorHasVerifiedBadge` | `boolean` | No | The verified status of a creator. | | `creatorType` | `0 \| 1 \| 2` | No | The Roblox.Catalog.Api.CatalogSearchDetailedResponseItem.CreatorType of the item's creator. | | `creatorTargetId` | `integer` | No | The creator id of the item's creator. | | `creatorName` | `string` | No | The creator name of the item's creator. | | `price` | `integer` | No | The item's price. | | `lowestPrice` | `integer` | No | The item's lowest price, only if the item is resellable and there are resellers. | | `lowestResalePrice` | `integer` | No | The item's lowest resale price, only if the item is resellable and there are resellers, including current user. | | `priceStatus` | `string` | No | The localized string item status if the item's price should not be displayed. | | `unitsAvailableForConsumption` | `integer` | No | The number of items in stock, only if the item is resellable and is limitedEdition. | | `favoriteCount` | `integer` | No | The number of times the item has been favorited. | | `offSaleDeadline` | `string` | No | When the item will go off sale, if the item has an off deadline. | | `collectibleItemId` | `string` | No | The item's collectible item id. It is an UUID if a item is collectible type. Otherwise, it is null. | | `totalQuantity` | `integer` | No | The collectible or limited-unique item's total quantity of unique instances. | | `saleLocationType` | `integer enum (8 values)` | No | The sale location type of the item. ['NotApplicable' = 0, 'ShopOnly' = 1, 'MyExperiencesOnly' = 2, 'ShopAndMyExperiences' = 3, 'ExperiencesById' = 4, 'ShopAndAllExperiences' = 5, 'ExperiencesDevApiOnly' = 6, 'ShopAndExperiencesById' = 7] Values: 0, 1, 2, 3, 4, 5, 6, 7 | | `hasResellers` | `boolean` | No | An indicator if the item has resellers or not (null if not resellable). | | `isOffSale` | `boolean` | No | An indicator if the item is off sale or not. | | `quantityLimitPerUser` | `integer` | No | Quantity limit for how many instances a user can buy. | | `supportsHeadShapes` | `boolean` | No | Whether the item supports head shapes. | | `timedOptions` | `Roblox.Catalog.Api.TimedOption[]` | No | The timed options for the item. | ### Roblox.Catalog.Api.CatalogSearchPageResponse[Roblox.Catalog.Api.CatalogSearchDetailedResponseItemV2] ApiPageResponse for catalog search. | Property | Type | Required | Description | |----------|------|----------|-------------| | `keyword` | `string` | No | Keyword used for search query. | | `elasticsearchDebugInfo` | `Roblox.Catalog.Api.ElasticsearchDebugInfo` | No | | | `previousPageCursor` | `string` | No | | | `nextPageCursor` | `string` | No | | | `data` | `Roblox.Catalog.Api.CatalogSearchDetailedResponseItemV2[]` | No | | ### Roblox.Catalog.Api.Discount | Property | Type | Required | Description | |----------|------|----------|-------------| | `robuxDiscountAmount` | `integer` | No | | | `robuxDiscountPercentage` | `number` | No | | | `discountCampaign` | `string` | No | | | `localizedDiscountAttribution` | `string` | No | | ### Roblox.Catalog.Api.DiscountInformation | Property | Type | Required | Description | |----------|------|----------|-------------| | `originalPrice` | `integer` | No | | | `totalDiscountPercentage` | `number` | No | | | `totalDiscountAmount` | `integer` | No | | | `discounts` | `Roblox.Catalog.Api.Discount[]` | No | | ### Roblox.Catalog.Api.ElasticsearchDebugInfo | Property | Type | Required | Description | |----------|------|----------|-------------| | `elasticsearchQuery` | `string` | No | Gets or sets the nest query that resulted from the operation. | | `isFromCache` | `boolean` | No | Gets or sets if the results were returned from cache. | | `indexName` | `string` | No | Gets or sets the index name. | | `isTerminatedEarly` | `boolean` | No | Gets or sets if the query was Terminated Early. | | `isForceTerminationEnabledByRequest` | `boolean` | No | Gets or sets if Force Termination was Enabled in the Request. | | `searchResultDataSource` | `string` | No | Gets or sets the search result data source. | | `searchResultRelevanceScore` | `string` | No | Gets or sets the search result relevance score. | | `searchResultEngagementScore` | `string` | No | Gets or sets the search result engagement score. | ### Roblox.Catalog.Api.TaxonomyModel public api model coaslescing taxonomy information for a single item. | Property | Type | Required | Description | |----------|------|----------|-------------| | `taxonomyId` | `string` | No | The id value to pass into taxonomy field in SearchV2 catalog-api. | | `taxonomyName` | `string` | No | The localized human readable name of the category. | ### Roblox.Catalog.Api.TimedOption Defines a timed option for a catalog item. | Property | Type | Required | Description | |----------|------|----------|-------------| | `days` | `integer` | No | The days for the timed option. | | `price` | `integer` | No | The price for the timed option. | | `discountInformation` | `Roblox.Catalog.Api.DiscountInformation` | No | | | `selected` | `boolean` | No | To indicate if this option is selected by the client. | ### Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Catalog.Api.CatalogSearchDetailedResponseItem] | Property | Type | Required | Description | |----------|------|----------|-------------| | `previousPageCursor` | `string` | No | | | `nextPageCursor` | `string` | No | | | `data` | `Roblox.Catalog.Api.CatalogSearchDetailedResponseItem[]` | No | | --- name: "ClientSettings Api v1" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://clientsettings.roblox.com" version: "v1" endpoints: 3 auth: [cookie] --- # ClientSettings Api v1 **API Version:** v1 **Base URL:** `https://clientsettings.roblox.com` ## Endpoints ### GET `/v1/client-version/{binaryType}` Get client version information for specific binary type **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `binaryType` | path | `string` | Yes | Platform(WindowsPlayer, WindowsStudio, MacPlayer or MacStudio) for which we want the latest version | **Responses:** - `200`: OK → `Roblox.ClientSettings.Api.Models.Response.ClientVersionResponse` **Response fields** (`Roblox.ClientSettings.Api.Models.Response.ClientVersionResponse`) See [Roblox.ClientSettings.Api.Models.Response.ClientVersionResponse](#roblox-clientsettings-api-models-response-clientversionresponse) in Models. **Response example:** ```json { "version": "string", "clientVersionUpload": "string", "bootstrapperVersion": "string", "nextClientVersionUpload": "string", "nextClientVersion": "string" } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://clientsettings.roblox.com/v1/client-version/{BINARYTYPE}" ``` ### GET `/v1/installer-cdns` Get information about which CDNs to use for installation. **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://clientsettings.roblox.com/v1/installer-cdns" ``` ### GET `/v1/mobile-client-version` Get mobile client version information based on app version parameter **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `appVersion` | query | `string` | Yes | AppiOSV2.13, AppVersioniOS2.0.1, etc | **Responses:** - `200`: OK → `Roblox.ClientSettings.Api.Models.Response.MobileClientVersionResponse` - `400`: 2: Invalid binaryType. **Response fields** (`Roblox.ClientSettings.Api.Models.Response.MobileClientVersionResponse`) See [Roblox.ClientSettings.Api.Models.Response.MobileClientVersionResponse](#roblox-clientsettings-api-models-response-mobileclientversionresponse) in Models. **Response example:** ```json { "activeVersion": "string", "upgradeSource": "string", "MD5Sum": "string", "data": { "UpgradeAction": "string" } } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://clientsettings.roblox.com/v1/mobile-client-version?appVersion={VALUE}" ``` ## Models ### Roblox.ClientSettings.Api.Models.Response.ClientVersionResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `version` | `string` | No | | | `clientVersionUpload` | `string` | No | | | `bootstrapperVersion` | `string` | No | | | `nextClientVersionUpload` | `string` | No | | | `nextClientVersion` | `string` | No | | ### Roblox.ClientSettings.Api.Models.Response.MobileClientVersionResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `activeVersion` | `string` | No | | | `upgradeSource` | `string` | No | | | `MD5Sum` | `string` | No | | | `data` | `Roblox.ClientSettings.Api.Models.Response.MobileClientVersionResponseData` | No | | ### Roblox.ClientSettings.Api.Models.Response.MobileClientVersionResponseData | Property | Type | Required | Description | |----------|------|----------|-------------| | `UpgradeAction` | `string` | No | | --- name: "ClientSettings Api v2" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://clientsettings.roblox.com" version: "v2" endpoints: 7 auth: [cookie] --- # ClientSettings Api v2 **API Version:** v2 **Base URL:** `https://clientsettings.roblox.com` ## Endpoints ### GET `/v2/android-binaries/{version}/channels/{channelName}` Retrieve the Android binary information for a given version and channel name. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `version` | path | `string` | Yes | The version string of the application. i.e. 2.660.392 | | `channelName` | path | `string` | Yes | The name of the channel. E.g. ZFlag, ZIntegration | **Responses:** - `200`: OK → `Roblox.ClientSettings.Api.Models.Response.AndroidBinaryResponse` **Response fields** (`Roblox.ClientSettings.Api.Models.Response.AndroidBinaryResponse`) See [Roblox.ClientSettings.Api.Models.Response.AndroidBinaryResponse](#roblox-clientsettings-api-models-response-androidbinaryresponse) in Models. **Response example:** ```json { "moduleName": "string", "libraryNames": { "engine": "string" }, "supportsAndroidBinaries": false } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://clientsettings.roblox.com/v2/android-binaries/{VERSION}/channels/{CHANNELNAME}" ``` ### GET `/v2/client-version/{binaryType}` Get client version information for specific binary type **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `binaryType` | path | `string` | Yes | Platform(WindowsPlayer, WindowsStudio, MacPlayer or MacStudio) for which we want the latest version | **Responses:** - `200`: OK → `Roblox.ClientSettings.Api.Models.Response.ClientVersionResponse` **Response fields** (`Roblox.ClientSettings.Api.Models.Response.ClientVersionResponse`) See [Roblox.ClientSettings.Api.Models.Response.ClientVersionResponse](#roblox-clientsettings-api-models-response-clientversionresponse) in Models. **Response example:** ```json { "version": "string", "clientVersionUpload": "string", "bootstrapperVersion": "string", "nextClientVersionUpload": "string", "nextClientVersion": "string" } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://clientsettings.roblox.com/v2/client-version/{BINARYTYPE}" ``` ### GET `/v2/client-version/{binaryType}/channel/{channelName}` Get client version information for specific binary type **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `binaryType` | path | `string` | Yes | Platform(WindowsPlayer, WindowsStudio, MacPlayer or MacStudio) for which we want the latest version | | `channelName` | path | `string` | Yes | Channel Name | **Responses:** - `200`: OK → `Roblox.ClientSettings.Api.Models.Response.ClientVersionResponse` - `401`: 5: Not authorized to perform this action. **Response fields** (`Roblox.ClientSettings.Api.Models.Response.ClientVersionResponse`) See [Roblox.ClientSettings.Api.Models.Response.ClientVersionResponse](#roblox-clientsettings-api-models-response-clientversionresponse) in Models. **Response example:** ```json { "version": "string", "clientVersionUpload": "string", "bootstrapperVersion": "string", "nextClientVersionUpload": "string", "nextClientVersion": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://clientsettings.roblox.com/v2/client-version/{BINARYTYPE}/channel/{CHANNELNAME}" ``` ### GET `/v2/compression-dictionaries` Returns a listing of all known compression dictionaries, including their SHA256 and creation date. This will be sorted by creation date, with the most recent dictionaries first. **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://clientsettings.roblox.com/v2/compression-dictionaries" ``` ### GET `/v2/compression-dictionaries/{dictionarySha256}` Returns the specified compression dictionary as a file download. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `dictionarySha256` | path | `string` | Yes | The SHA256 of the dictionary we wish to download. | **Responses:** - `200`: OK **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://clientsettings.roblox.com/v2/compression-dictionaries/{DICTIONARYSHA256}" ``` ### GET `/v2/ota-version/{binaryType}` Get OTA information for a specific binary type with a given version on some channel. Returns empty list if no updates are found or channel/application with the given version does not exist in CVS. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `binaryType` | path | `string` | Yes | Binary type of the application to get info for | | `channel` | query | `string` | No | Channel name. If not provided, production is assumed. | | `version` | query | `string` | No | Application version | | `tag` | query | `string` | No | Tag to filter results by. Only applicable to non-studio application types. | | `name` | query | `string` | No | Name to filter results by. Only applicable to non-studio application types. | **Responses:** - `200`: OK → `Roblox.ClientSettings.Api.Models.Response.OtaVersionResponse[]` - `400`: 2: Invalid binaryType. 4: Invalid app version. 6: Missing or invalid channel. 7: Unsupported binaryType. - `401`: 5: Not authorized to perform this action. **Response fields** (`Roblox.ClientSettings.Api.Models.Response.OtaVersionResponse[]`) See [Roblox.ClientSettings.Api.Models.Response.OtaVersionResponse](#roblox-clientsettings-api-models-response-otaversionresponse) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://clientsettings.roblox.com/v2/ota-version/{BINARYTYPE}" ``` ### GET `/v2/user-channel` Get channel name for currently logged in user **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `binaryType` | query | `string` | No | | **Responses:** - `200`: OK → `Roblox.ClientSettings.Api.Models.Response.UserChannelResponse` **Response fields** (`Roblox.ClientSettings.Api.Models.Response.UserChannelResponse`) See [Roblox.ClientSettings.Api.Models.Response.UserChannelResponse](#roblox-clientsettings-api-models-response-userchannelresponse) in Models. **Response example:** ```json { "channelName": "string", "channelAssignmentType": 0, "token": "string", "program": { "name": "string", "id": "string" }, "isFlagOnly": false } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://clientsettings.roblox.com/v2/user-channel" ``` ## Models ### Roblox.ClientSettings.Api.Models.Response.AndroidBinaryLibraryNames Contains the names of the libraries in an Android Binary Module. | Property | Type | Required | Description | |----------|------|----------|-------------| | `engine` | `string` | No | Name of the engine library. | ### Roblox.ClientSettings.Api.Models.Response.AndroidBinaryResponse Contains information about an Android Binary Module for a given channel. including its module name, the names of its libraries, and whether the channel supports Android binaries. | Property | Type | Required | Description | |----------|------|----------|-------------| | `moduleName` | `string` | No | The name of the Android Binary Module. | | `libraryNames` | `Roblox.ClientSettings.Api.Models.Response.AndroidBinaryLibraryNames` | No | | | `supportsAndroidBinaries` | `boolean` | No | True if the channel supports android binaries. False otherwise. | ### Roblox.ClientSettings.Api.Models.Response.BetaProgramInfo Beta program information included in the user channel response. | Property | Type | Required | Description | |----------|------|----------|-------------| | `name` | `string` | No | The display name of the beta program. | | `id` | `string` | No | The ID of the beta program. | ### Roblox.ClientSettings.Api.Models.Response.ClientVersionResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `version` | `string` | No | | | `clientVersionUpload` | `string` | No | | | `bootstrapperVersion` | `string` | No | | | `nextClientVersionUpload` | `string` | No | | | `nextClientVersion` | `string` | No | | ### Roblox.ClientSettings.Api.Models.Response.OtaVersionResponse Response for endpoints returning ota information. | Property | Type | Required | Description | |----------|------|----------|-------------| | `name` | `string` | No | Ota library/plugin name | | `version` | `string` | No | Version number of asset | | `downloadUrl` | `string` | No | URL to download the ota asset | | `isStandalone` | `boolean` | No | Refers to whether the plugin is core to Studio functions, and is used to determine when it is loaded. | | `assetId` | `string` | No | The asset ID of the LuaApp OTA. | | `assetVersion` | `string` | No | The asset version of the LuaApp OTA. | | `maxAppVersion` | `string` | No | The maximum application version compatible with the LuaApp OTA. | | `tryoutName` | `string` | No | The name of the tryout for the LuaApp OTA. | | `localAssetURI` | `string` | No | The local asset URI for the LuaApp OTA. | | `isForcedUpdate` | `boolean` | No | Indicates whether the LuaApp OTA is a forced update. | | `appStorageResetId` | `string` | No | The application storage reset ID for the LuaApp OTA. | | `isDevelopmentConfig` | `boolean` | No | Indicates whether the LuaApp OTA is a development configuration. | | `assetsManifest` | `object` | No | Inlined assets manifest for the LuaApp OTA. | | `versionV2` | `integer` | No | VersionV2 of the OTA asset. | ### Roblox.ClientSettings.Api.Models.Response.UserChannelResponse Response data object for getting a user's channel information. | Property | Type | Required | Description | |----------|------|----------|-------------| | `channelName` | `string` | No | Name of the channel. | | `channelAssignmentType` | `integer enum (6 values)` | No | How the user was bound to the channel. If the user is not assigned to any channel, this is omitted. ['None' = 0, 'PerMille' = 1, 'BoundToPrivateChannel' = 2, 'BoundToPublicChannel' = 3, 'OptedInToBetaProgramWithPrivateChannel' = 4, 'OptedInToBetaProgramWithPublicChannel' = 5] Values: 0, 1, 2, 3, 4, 5 | | `token` | `string` | No | JWT token. If the channel is not private, this is omitted. | | `program` | `Roblox.ClientSettings.Api.Models.Response.BetaProgramInfo` | No | | | `isFlagOnly` | `boolean` | No | Whether the channel is flag-only. A flag-only channel uses production binaries. If the channel is not flag-only, this is omitted. | --- name: "Contacts Api v1" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://contacts.roblox.com" version: "v1" endpoints: 3 auth: [cookie] --- # Contacts Api v1 **API Version:** v1 **Base URL:** `https://contacts.roblox.com` ## Endpoints ### GET `/v1/user/tag/validate` Validates the tag for a user **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `alias` | query | `string` | No | The tag to validate | **Responses:** - `200`: OK → `Roblox.Contacts.Api.Response.ValidateUserTagResponseModel` - `400`: 4: Invalid parameters. - `401`: 0: Authorization has been denied for this request. - `429`: 10: The flood limit has been exceeded. **Response fields** (`Roblox.Contacts.Api.Response.ValidateUserTagResponseModel`) See [Roblox.Contacts.Api.Response.ValidateUserTagResponseModel](#roblox-contacts-api-response-validateusertagresponsemodel) in Models. **Response example:** ```json { "status": "Success" } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://contacts.roblox.com/v1/user/tag/validate" ``` ### POST `/v1/user/get-tags` Gets the tags for multiple users **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Contacts.Api.Request.GetUserTagsRequestModel` See [Roblox.Contacts.Api.Request.GetUserTagsRequestModel](#roblox-contacts-api-request-getusertagsrequestmodel) in Models. **Request example:** ```json { "targetUserIds": [ 0 ] } ``` **Responses:** - `200`: OK → `Roblox.Contacts.Api.Response.GetUserTagsResponseModel[]` - `400`: 4: Invalid parameters. 8: Too many user Tags are requested. - `401`: 0: Authorization has been denied for this request. - `429`: 10: The flood limit has been exceeded. **Response fields** (`Roblox.Contacts.Api.Response.GetUserTagsResponseModel[]`) See [Roblox.Contacts.Api.Response.GetUserTagsResponseModel](#roblox-contacts-api-response-getusertagsresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://contacts.roblox.com/v1/user/get-tags" \ -H "Content-Type: application/json" \ -d '{ "targetUserIds": [ 0 ] }' ``` ### POST `/v1/user/tag` Sets the tag for a user **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Contacts.Api.Request.SetUserTagRequestModel` See [Roblox.Contacts.Api.Request.SetUserTagRequestModel](#roblox-contacts-api-request-setusertagrequestmodel) in Models. **Request example:** ```json { "targetUserId": 0, "userTag": "string" } ``` **Responses:** - `200`: OK → `Roblox.Contacts.Api.Response.SetUserTagResponseModel` - `400`: 2: The target user is invalid or does not exist. 4: Invalid parameters. 6: The userTag is too long. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 5: The user cannot tag themselves. - `429`: 10: The flood limit has been exceeded. **Response fields** (`Roblox.Contacts.Api.Response.SetUserTagResponseModel`) See [Roblox.Contacts.Api.Response.SetUserTagResponseModel](#roblox-contacts-api-response-setusertagresponsemodel) in Models. **Response example:** ```json { "status": "Success" } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://contacts.roblox.com/v1/user/tag" \ -H "Content-Type: application/json" \ -d '{ "targetUserId": 0, "userTag": "string" }' ``` ## Models ### Roblox.Contacts.Api.Request.GetUserTagsRequestModel | Property | Type | Required | Description | |----------|------|----------|-------------| | `targetUserIds` | `integer[]` | No | | ### Roblox.Contacts.Api.Request.SetUserTagRequestModel | Property | Type | Required | Description | |----------|------|----------|-------------| | `targetUserId` | `integer` | No | The userId of the target of the userTag. | | `userTag` | `string` | No | The userTag to be set | ### Roblox.Contacts.Api.Response.GetUserTagsResponseModel | Property | Type | Required | Description | |----------|------|----------|-------------| | `targetUserId` | `integer` | No | | | `targetUserTag` | `string` | No | | ### Roblox.Contacts.Api.Response.SetUserTagResponseModel | Property | Type | Required | Description | |----------|------|----------|-------------| | `status` | `Success \| Moderated` | No | Status message to be sent to the setter of the userTag ['Success' = 0, 'Moderated' = 1] | ### Roblox.Contacts.Api.Response.ValidateUserTagResponseModel | Property | Type | Required | Description | |----------|------|----------|-------------| | `status` | `Success \| Moderated \| TooLong` | No | Status message to be sent to the requester of the userTag validation ['Success' = 0, 'Moderated' = 1, 'TooLong' = 2] | --- name: "Develop Api v1" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://develop.roblox.com" version: "v1" endpoints: 27 auth: [cookie] --- # Develop Api v1 **API Version:** v1 **Base URL:** `https://develop.roblox.com` ## Endpoints ### GET `/v1/assets/voting` *(deprecated)* Gets the voting information of the given assets **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetIds` | query | `array` | Yes | The ids of the Roblox.Platform.Assets.IAsset. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Api.Develop.Models.Response.AssetVotingModel]` **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Api.Develop.Models.Response.AssetVotingModel]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Api.Develop.Models.Response.AssetVotingModel]](#roblox-web-webapi-models-apiarrayresponse-roblox-api-develop-models-response-assetvotingmodel-) in Models. **Response example:** ```json { "data": [ { "assetId": "...", "hasUserVoted": "...", "canUserVote": "...", "shouldShowVotes": "...", "upVotes": "...", "downVotes": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/assets/voting?assetIds={VALUE}" ``` ### GET `/v1/gametemplates` Gets a page of templates that can be used to start off making games. Templates subject to change without notice. Sort order of templates specified by Roblox. **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Api.Develop.Models.GameTemplateModel]` **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Api.Develop.Models.GameTemplateModel]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Api.Develop.Models.GameTemplateModel]](#roblox-web-webapi-models-apiarrayresponse-roblox-api-develop-models-gametemplatemodel-) in Models. **Response example:** ```json { "data": [ { "gameTemplateType": "...", "hasTutorials": "...", "universe": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/gametemplates" ``` ### GET `/v1/groups/{groupId}/universes` Gets a list of universes for the given group. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group id. | | `isArchived` | query | `boolean` | No | Whether or not to return archived games. | | `limit` | query | `integer (int32)` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `string` | No | Sorted by universeId Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Api.Develop.Models.UniverseModel]` - `400`: Invalid groupId. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Api.Develop.Models.UniverseModel]`) See [Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Api.Develop.Models.UniverseModel]](#roblox-web-webapi-models-apipageresponse-roblox-api-develop-models-universemodel-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "id": "...", "name": "...", "description": "...", "isArchived": "...", "rootPlaceId": "...", "isActive": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/groups/{GROUPID}/universes" ``` ### GET `/v1/places/{placeId}/teamcreate/active_session/members` List of users in the active Team Create session **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `placeId` | path | `integer (int64)` | Yes | The place Id. | | `limit` | query | `integer (int32)` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Web.Responses.Users.SkinnyUserResponse]` - `400`: 1: The universe is invalid. 5: The place is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: 2: Not authorized to perform this action. 4: TeamCreate on universe is disabled. - `404`: 0: An unknown error occurred. - `500`: 6: Multiple active sessions in a Team Create place. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Web.Responses.Users.SkinnyUserResponse]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Web.Responses.Users.SkinnyUserResponse]](#roblox-web-webapi-models-apiarrayresponse-roblox-web-responses-users-skinnyuserresponse-) in Models. **Response example:** ```json { "data": [ { "id": "...", "name": "...", "displayName": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/places/{PLACEID}/teamcreate/active_session/members" ``` ### GET `/v1/plugins` Gets plugin details by ids. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `pluginIds` | query | `array` | Yes | The plugin ids. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Web.Responses.Plugins.PluginResponse]` - `400`: 1: Too many ids. 2: The format of the ids are invalid. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Web.Responses.Plugins.PluginResponse]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Web.Responses.Plugins.PluginResponse]](#roblox-web-webapi-models-apiarrayresponse-roblox-web-responses-plugins-pluginresponse-) in Models. **Response example:** ```json { "data": [ { "id": "...", "name": "...", "description": "...", "commentsEnabled": "...", "versionId": "...", "created": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/plugins?pluginIds={VALUE}" ``` ### GET `/v1/universes/{universeId}` Gets a Roblox.Api.Develop.Models.UniverseModel. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer (int64)` | Yes | The Universe id. | **Responses:** - `200`: OK → `Roblox.Api.Develop.Models.UniverseModel` - `400`: 1: The universe does not exist. **Response fields** (`Roblox.Api.Develop.Models.UniverseModel`) See [Roblox.Api.Develop.Models.UniverseModel](#roblox-api-develop-models-universemodel) in Models. **Response example:** ```json { "id": 0, "name": "string", "description": "string", "isArchived": false, "rootPlaceId": 0, "isActive": false } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/universes/{UNIVERSEID}" ``` ### GET `/v1/universes/{universeId}/activation-eligibility` Returns the result of various checks for a user's eligibility to activate a given universe from private to public universeId for authenticated user **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer (int64)` | Yes | The universe id. | **Responses:** - `200`: OK → `Roblox.Api.Develop.Models.ActivationEligibilityResponse` **Response fields** (`Roblox.Api.Develop.Models.ActivationEligibilityResponse`) See [Roblox.Api.Develop.Models.ActivationEligibilityResponse](#roblox-api-develop-models-activationeligibilityresponse) in Models. **Response example:** ```json { "isEligible": false, "maturityRated": false, "isUserEligibleForPublicPublish": false, "remainingPublicPublishCount": 0, "isPublicPublish": false, "isPublishToExistingUniverse": false } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/universes/{UNIVERSEID}/activation-eligibility" ``` ### GET `/v1/universes/{universeId}/configuration` Get settings for an owned universe. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer (int64)` | Yes | The universe Id. | **Responses:** - `200`: OK → `Roblox.Api.Develop.Models.UniverseSettingsResponse` - `400`: 1: The universe does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 2: You are not authorized to configure this universe. **Response fields** (`Roblox.Api.Develop.Models.UniverseSettingsResponse`) See [Roblox.Api.Develop.Models.UniverseSettingsResponse](#roblox-api-develop-models-universesettingsresponse) in Models. **Response example:** ```json { "allowPrivateServers": false, "privateServerPrice": 0, "isMeshTextureApiAccessAllowed": false, "isRewardedOnDemandAdsAllowed": false, "id": 0, "name": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/universes/{UNIVERSEID}/configuration" ``` ### PATCH `/v1/universes/{universeId}/configuration` Update universe settings for an owned universe. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer (int64)` | Yes | The universeId. | **Request Body:** `application/json` — Type: `Roblox.Api.Develop.Models.UniverseSettingsRequest` See [Roblox.Api.Develop.Models.UniverseSettingsRequest](#roblox-api-develop-models-universesettingsrequest) in Models. **Request example:** ```json { "name": "string", "universeAvatarType": 1, "universeScaleType": 1, "universeAnimationType": 1, "universeCollisionType": 1, "universeBodyType": 1 } ``` **Responses:** - `200`: OK → `Roblox.Api.Develop.Models.UniverseSettingsResponse` - `400`: 1: The universe does not exist. 3: Invalid UniverseAvatarType. 4: Invalid UniverseScaleType. 5: Invalid UniverseAnimationType. 6: Invalid UniverseCollisionType. 7: New universe name or description has been rejected. 8: New universe name is too long. 10: Invalid UniverseBodyType. 11: Invalid UniverseJointPositioningType. 12: The universe has no root place. 15: Price is required when isForSale is true. 16: This game cannot be offered for sale because it is not public. 17: This game cannot be offered for sale because it has private servers enabled. 18: The game price is outside of the allowed range. 19: Invalid genre. 20: The request body is missing. 21: Invalid device type. 22: Invalid asset type. 23: Invalid value, the min must be less than or equal to the max 24: Invalid scale value 41: You cannot change the private server price again so soon after the previous change. Please try again later. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: You are not authorized to configure this universe. 14: You are not authorized to sell games. - `409`: 9: Failed to shutdown all intances of game after changing AvatarType. The change has been reverted. **Response fields** (`Roblox.Api.Develop.Models.UniverseSettingsResponse`) See [Roblox.Api.Develop.Models.UniverseSettingsResponse](#roblox-api-develop-models-universesettingsresponse) in Models. **Response example:** ```json { "allowPrivateServers": false, "privateServerPrice": 0, "isMeshTextureApiAccessAllowed": false, "isRewardedOnDemandAdsAllowed": false, "id": 0, "name": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/universes/{UNIVERSEID}/configuration" \ -H "Content-Type: application/json" \ -d '{ "name": "string", "universeAvatarType": 1, "universeScaleType": 1, "universeAnimationType": 1, "universeCollisionType": 1, "universeBodyType": 1 }' ``` ### GET `/v1/universes/{universeId}/configuration/vip-servers` Get settings for an owned universe's VIP servers. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer (int64)` | Yes | The universe Id. | **Responses:** - `200`: OK → `Roblox.Api.Develop.Models.PrivateServerDetailsResponse` - `400`: 1: The universe does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 2: You are not authorized to configure this universe. **Response fields** (`Roblox.Api.Develop.Models.PrivateServerDetailsResponse`) See [Roblox.Api.Develop.Models.PrivateServerDetailsResponse](#roblox-api-develop-models-privateserverdetailsresponse) in Models. **Response example:** ```json { "isEnabled": false, "price": 0, "activeServersCount": 0, "activeSubscriptionsCount": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/universes/{UNIVERSEID}/configuration/vip-servers" ``` ### GET `/v1/universes/{universeId}/permissions` Returns list of granted and declined permissions related to the universe with the id universeId for authenticated user **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer (int64)` | Yes | The universe id. | **Responses:** - `200`: OK → `Roblox.Api.Develop.Models.UniversePermissionsModel` - `400`: 1: The universe does not exist. - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Api.Develop.Models.UniversePermissionsModel`) See [Roblox.Api.Develop.Models.UniversePermissionsModel](#roblox-api-develop-models-universepermissionsmodel) in Models. **Response example:** ```json { "canManage": false, "canCloudEdit": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/universes/{UNIVERSEID}/permissions" ``` ### GET `/v1/universes/{universeId}/places` Gets a list of places for a universe. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer (int64)` | Yes | The asset id. | | `isUniverseCreation` | query | `boolean` | No | | | `limit` | query | `integer (int32)` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `string` | No | Sorted by placeId Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Api.Develop.Models.IPlaceModel]` **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Api.Develop.Models.IPlaceModel]`) See [Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Api.Develop.Models.IPlaceModel]](#roblox-web-webapi-models-apipageresponse-roblox-api-develop-models-iplacemodel-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ "..." ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/universes/{UNIVERSEID}/places" ``` ### GET `/v1/universes/{universeId}/teamcreate` Gets TeamCreate settings for an Roblox.Platform.Universes.IUniverse. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer (int64)` | Yes | The universe Id. | **Responses:** - `200`: OK → `Roblox.Api.Develop.Models.Response.TeamCreateSettingsResponse` - `400`: Roblox.Api.Develop.ResponseEnums.TeamCreateErrors.InvalidUniverse - `401`: 0: Authorization has been denied for this request. - `403`: Roblox.Api.Develop.ResponseEnums.TeamCreateErrors.Unauthorized **Response fields** (`Roblox.Api.Develop.Models.Response.TeamCreateSettingsResponse`) See [Roblox.Api.Develop.Models.Response.TeamCreateSettingsResponse](#roblox-api-develop-models-response-teamcreatesettingsresponse) in Models. **Response example:** ```json { "isEnabled": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/universes/{UNIVERSEID}/teamcreate" ``` ### PATCH `/v1/universes/{universeId}/teamcreate` Edit team create settings for a universe. Enables, or disables team create for a universe. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer (int64)` | Yes | The universe Id. | **Request Body:** `application/json` — Type: `Roblox.Api.Develop.Models.UpdateTeamCreateSettingsRequest` See [Roblox.Api.Develop.Models.UpdateTeamCreateSettingsRequest](#roblox-api-develop-models-updateteamcreatesettingsrequest) in Models. **Request example:** ```json { "isEnabled": false } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: Roblox.Api.Develop.ResponseEnums.TeamCreateErrors.InvalidUniverse - `401`: 0: Authorization has been denied for this request. - `403`: Roblox.Api.Develop.ResponseEnums.TeamCreateErrors.Unauthorized 0: Token Validation Failed **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/universes/{UNIVERSEID}/teamcreate" \ -H "Content-Type: application/json" \ -d '{ "isEnabled": false }' ``` ### GET `/v1/universes/multiget` Gets a list of universes. If a universe can not be found for a given ID (such as -1) it will be skipped. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `ids` | query | `array` | Yes | The universe IDs to get. Limit 100. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Api.Develop.Models.UniverseModel]` - `400`: 8: No universe IDs sent to get. 9: Too many universe IDs sent to get, the limit is: **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Api.Develop.Models.UniverseModel]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Api.Develop.Models.UniverseModel]](#roblox-web-webapi-models-apiarrayresponse-roblox-api-develop-models-universemodel-) in Models. **Response example:** ```json { "data": [ { "id": "...", "name": "...", "description": "...", "isArchived": "...", "rootPlaceId": "...", "isActive": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/universes/multiget?ids={VALUE}" ``` ### GET `/v1/universes/multiget/permissions` Returns an array of granted and declined permissions related to the universes with the ids in ids for the authenticated user. If a universe can not be found for a given ID (such as -1) it will be skipped. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `ids` | query | `array` | Yes | The universe ids. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Api.Develop.Models.UniverseIdPermissionsModel]` - `400`: 8: No universe IDs sent to get. 9: Too many universe IDs sent to get, the limit is: - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Api.Develop.Models.UniverseIdPermissionsModel]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Api.Develop.Models.UniverseIdPermissionsModel]](#roblox-web-webapi-models-apiarrayresponse-roblox-api-develop-models-universeidpermissionsmodel-) in Models. **Response example:** ```json { "data": [ { "universeId": "...", "canManage": "...", "canCloudEdit": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/universes/multiget/permissions?ids={VALUE}" ``` ### GET `/v1/universes/multiget/teamcreate` Gets TeamCreate settings for multiple universes specified by Ids **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `ids` | query | `array` | Yes | The universe Ids. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Api.Develop.Models.UniverseTeamCreateSettingsModel]` - `400`: Roblox.Api.Develop.ResponseEnums.TeamCreateErrors.TooManyUniverseIdsSent - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Api.Develop.Models.UniverseTeamCreateSettingsModel]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Api.Develop.Models.UniverseTeamCreateSettingsModel]](#roblox-web-webapi-models-apiarrayresponse-roblox-api-develop-models-universeteamcreatesettingsmodel-) in Models. **Response example:** ```json { "data": [ { "id": "...", "isEnabled": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/universes/multiget/teamcreate?ids={VALUE}" ``` ### GET `/v1/universes/user-public-publish-eligibility` Returns the result of various checks for a user's eligibility to publish a public universe **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Api.Develop.Models.UserPublicPublishEligibilityResponse` **Response fields** (`Roblox.Api.Develop.Models.UserPublicPublishEligibilityResponse`) See [Roblox.Api.Develop.Models.UserPublicPublishEligibilityResponse](#roblox-api-develop-models-userpublicpublisheligibilityresponse) in Models. **Response example:** ```json { "isEligible": false, "hasTransactions": 0, "idVerified": 0, "hasDevex": 0 } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/universes/user-public-publish-eligibility" ``` ### GET `/v1/user/groups/canmanage` Gets a list of Groups that a user can manage. **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Api.Develop.Models.GroupModel]` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Api.Develop.Models.GroupModel]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Api.Develop.Models.GroupModel]](#roblox-web-webapi-models-apiarrayresponse-roblox-api-develop-models-groupmodel-) in Models. **Response example:** ```json { "data": [ { "id": "...", "name": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/user/groups/canmanage" ``` ### GET `/v1/user/groups/canmanagegamesoritems` Gets a list of groups a user can manage games or items for. **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Api.Develop.Models.GroupModel]` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Api.Develop.Models.GroupModel]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Api.Develop.Models.GroupModel]](#roblox-web-webapi-models-apiarrayresponse-roblox-api-develop-models-groupmodel-) in Models. **Response example:** ```json { "data": [ { "id": "...", "name": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/user/groups/canmanagegamesoritems" ``` ### GET `/v1/user/universes` Gets a list of universes for the authenticated user. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `isArchived` | query | `boolean` | No | Whether or not to return archived games. | | `limit` | query | `integer (int32)` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `string` | No | Sorted by universeId Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Api.Develop.Models.UniverseModel]` - `400`: cursor is not valid. - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Api.Develop.Models.UniverseModel]`) See [Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Api.Develop.Models.UniverseModel]](#roblox-web-webapi-models-apipageresponse-roblox-api-develop-models-universemodel-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "id": "...", "name": "...", "description": "...", "isArchived": "...", "rootPlaceId": "...", "isActive": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/user/universes" ``` ### POST `/v1/places/{placeId}` Updates the place configuration for the place with the id placeId Currently the only supported functionality for updating the configuration is around Name, and Description. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `placeId` | path | `integer (int64)` | Yes | The place id for the place to be updated. | **Request Body:** `application/json` — Type: `Roblox.Api.Develop.Models.PlaceConfigurationModel` See [Roblox.Api.Develop.Models.PlaceConfigurationModel](#roblox-api-develop-models-placeconfigurationmodel) in Models. **Request example:** ```json { "name": "string", "description": "string" } ``` **Responses:** - `200`: OK → `Roblox.Api.Develop.Models.PlaceModel` - `400`: placeId is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: Authenticated user is not authorized to manage this place. 0: Token Validation Failed **Response fields** (`Roblox.Api.Develop.Models.PlaceModel`) See [Roblox.Api.Develop.Models.PlaceModel](#roblox-api-develop-models-placemodel) in Models. **Response example:** ```json { "id": 0, "universeId": 0, "name": "string", "description": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/places/{PLACEID}" \ -H "Content-Type: application/json" \ -d '{ "name": "string", "description": "string" }' ``` ### PATCH `/v1/places/{placeId}` Updates the place configuration for the place with the id placeId Currently the only supported functionality for updating the configuration is around Name, and Description. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `placeId` | path | `integer (int64)` | Yes | The place id for the place to be updated. | **Request Body:** `application/json` — Type: `Roblox.Api.Develop.Models.PlaceConfigurationModel` See [Roblox.Api.Develop.Models.PlaceConfigurationModel](#roblox-api-develop-models-placeconfigurationmodel) in Models. **Request example:** ```json { "name": "string", "description": "string" } ``` **Responses:** - `200`: OK → `Roblox.Api.Develop.Models.PlaceModel` - `400`: placeId is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: Authenticated user is not authorized to manage this place. 0: Token Validation Failed **Response fields** (`Roblox.Api.Develop.Models.PlaceModel`) See [Roblox.Api.Develop.Models.PlaceModel](#roblox-api-develop-models-placemodel) in Models. **Response example:** ```json { "id": 0, "universeId": 0, "name": "string", "description": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/places/{PLACEID}" \ -H "Content-Type: application/json" \ -d '{ "name": "string", "description": "string" }' ``` ### POST `/v1/universes/{universeId}/activate` Activates a universes. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer (int64)` | Yes | The universe id. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: The universe does not exist. 2: This universe does not have a root place. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 3: You are not authorized to configure this universe. 6: The root place for this universe is under review and can not be activated. 7: Creator already has the maximum number of places active. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/universes/{UNIVERSEID}/activate" ``` ### POST `/v1/universes/{universeId}/deactivate` Deactivates a universe. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer (int64)` | Yes | The universe id. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: The universe does not exist. 2: This universe does not have a root place. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 3: You are not authorized to configure this universe. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/universes/{UNIVERSEID}/deactivate" ``` ### PATCH `/v1/plugins/{pluginId}` Updates a plugin. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `pluginId` | path | `integer (int64)` | Yes | The id of the plugin. | **Request Body:** `application/json` — Type: `Roblox.Develop.Api.UpdatePluginRequest` See [Roblox.Develop.Api.UpdatePluginRequest](#roblox-develop-api-updatepluginrequest) in Models. **Request example:** ```json { "name": "string", "description": "string", "commentsEnabled": false } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 5: Description too long. 6: Text moderated. 7: Invalid name. 8: The request body is missing. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 4: Insufficient permissions. - `404`: 3: The id is invalid. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/plugins/{PLUGINID}" \ -H "Content-Type: application/json" \ -d '{ "name": "string", "description": "string", "commentsEnabled": false }' ``` ### DELETE `/v1/universes/{universeId}/teamcreate/memberships` Removes a user from a TeamCreate permissions list. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer (int64)` | Yes | The universe Id. | **Request Body:** `application/json` — Type: `Roblox.Api.Develop.Models.TeamCreateMembershipRequest` See [Roblox.Api.Develop.Models.TeamCreateMembershipRequest](#roblox-api-develop-models-teamcreatemembershiprequest) in Models. **Request example:** ```json { "userId": 0 } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: Roblox.Api.Develop.ResponseEnums.TeamCreateErrors.InvalidUniverse OR Roblox.Api.Develop.ResponseEnums.TeamCreateErrors.InvalidUser - `401`: 0: Authorization has been denied for this request. - `403`: Roblox.Api.Develop.ResponseEnums.TeamCreateErrors.TeamCreateDisabled 0: Token Validation Failed **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v1/universes/{UNIVERSEID}/teamcreate/memberships" \ -H "Content-Type: application/json" \ -d '{ "userId": 0 }' ``` ## Models ### Roblox.Api.Develop.Models.ActivationEligibilityResponse The result of various checks for a user's eligibility to activate a given universe from private to public. | Property | Type | Required | Description | |----------|------|----------|-------------| | `isEligible` | `boolean` | No | | | `maturityRated` | `boolean` | No | Whether the place has an active content maturity rating or not. | | `isUserEligibleForPublicPublish` | `boolean` | No | isUserEligibleForPublicPublish | | `remainingPublicPublishCount` | `integer` | No | How many public publishes are remaining for the given user / group. | | `isPublicPublish` | `boolean` | No | Whether the universe is publicly published or not. | | `isPublishToExistingUniverse` | `boolean` | No | Whether the universeId is within the tracked list of distinct allowed published universes. | | `isUniverseSelect` | `boolean` | No | Whether the universe is in the "Select" program. | | `creatorTier` | `0 \| 1 \| 2 \| 3 \| 4` | No | The creator tier of the universe owner. ['Invalid' = 0, 'Blocked' = 1, 'Private' = 2, 'Trusted' = 3, 'Everyone' = 4] | | `allowedAudiences` | `0 \| 1 \| 2 \| 3 \| 4[]` | No | Audiences the universe is allowed to be published to. | ### Roblox.Api.Develop.Models.GameTemplateModel Represents a game template in API endpoint responses. | Property | Type | Required | Description | |----------|------|----------|-------------| | `gameTemplateType` | `string` | No | The type of this game template. | | `hasTutorials` | `boolean` | No | Whether this game template has tutorials. | | `universe` | `Roblox.Api.Develop.Models.UniverseModel` | No | | ### Roblox.Api.Develop.Models.GroupModel A model containing information about a group | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | The Roblox.Platform.Groups.IGroup's Id. | | `name` | `string` | No | The Roblox.Platform.Groups.IGroup's name. | ### Roblox.Api.Develop.Models.PlaceConfigurationModel A model containing information about a place to be configured | Property | Type | Required | Description | |----------|------|----------|-------------| | `name` | `string` | No | The name for the place. | | `description` | `string` | No | The new description for the place. | ### Roblox.Api.Develop.Models.PlaceModel A model containing information about a place | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | Returns the place id. | | `universeId` | `integer` | No | Returns the id of the place's universe, or null - if the place is not part of a universe. | | `name` | `string` | No | Returns the place name. | | `description` | `string` | No | Returns the place description. | ### Roblox.Api.Develop.Models.PrivateServerDetailsResponse Model for private server details responses from the UniverseSettings controller. | Property | Type | Required | Description | |----------|------|----------|-------------| | `isEnabled` | `boolean` | No | Whether or not VIP servers are enabled on this universe. | | `price` | `integer` | No | The price of the VIP server. | | `activeServersCount` | `integer` | No | The number of active VIP servers for this universe. A negative value indicates at least this many exist (i.e. -100 means 100+ active private servers). | | `activeSubscriptionsCount` | `integer` | No | The number of active VIP server subscriptions. A negative value indicates at least this many exist (i.e. -100 means 100+ active subscriptions). | ### Roblox.Api.Develop.Models.Response.AssetVotingModel Asset voting information | Property | Type | Required | Description | |----------|------|----------|-------------| | `assetId` | `integer` | No | The !:IAsset's id. | | `hasUserVoted` | `boolean` | No | Whether the user has voted on this !:IAsset. | | `canUserVote` | `boolean` | No | Whether the user can vote on this !:IAsset. | | `shouldShowVotes` | `boolean` | No | Whether votes should be shown. | | `upVotes` | `integer` | No | The number of up votes. | | `downVotes` | `integer` | No | The number of down votes. | | `reasonForNotAbleToVote` | `string` | No | The reason this !:IAsset cannot be voted on. | ### Roblox.Api.Develop.Models.Response.TeamCreateSettingsResponse Team create settings | Property | Type | Required | Description | |----------|------|----------|-------------| | `isEnabled` | `boolean` | No | Whether or not the universe should be enabled for team create | ### Roblox.Api.Develop.Models.TeamCreateMembershipRequest Request model for a TeamCreate membership | Property | Type | Required | Description | |----------|------|----------|-------------| | `userId` | `integer` | No | The id of the user. | ### Roblox.Api.Develop.Models.UniverseIdPermissionsModel A model containing information about a universe permissions | Property | Type | Required | Description | |----------|------|----------|-------------| | `universeId` | `integer` | No | The universe Id these permissions reference | | `canManage` | `boolean` | No | Determines whether or not consumer can manage the target universe | | `canCloudEdit` | `boolean` | No | Determines whether or not consumer can cloud the target universe This is only nullable/optional in the context of https://develop.roblox.com/docs#!/Universes/get_v1_universes_universeId_context_permissions endpoint which is consumed only internally. It should be computed and set for all other usages. | ### Roblox.Api.Develop.Models.UniverseModel Represents a universe in API endpoint results. | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | The universe Id. | | `name` | `string` | No | The name of the universe | | `description` | `string` | No | The description of the universe | | `isArchived` | `boolean` | No | Is this universe archived | | `rootPlaceId` | `integer` | No | The universe's root place id | | `isActive` | `boolean` | No | Is this universe active | | `privacyType` | `string` | No | The universe's privacy type. | | `creatorType` | `string` | No | The creator type, either "User" or "Group" | | `creatorTargetId` | `integer` | No | The id of the user or group that created this universe. | | `creatorName` | `string` | No | The name of the creator of the universe. | | `created` | `string` | No | The created System.DateTime | | `updated` | `string` | No | The updated System.DateTime | | `audiences` | `0 \| 1 \| 2 \| 3 \| 4[]` | No | The audiences this universe is visible to (e.g. Editors, PlayTesters, Friends, Public). Always non-null; may be empty when audience visibility has not been configured. | ### Roblox.Api.Develop.Models.UniversePermissionsModel A model containing information about a universe permissions | Property | Type | Required | Description | |----------|------|----------|-------------| | `canManage` | `boolean` | No | Determines whether or not consumer can manage the target universe | | `canCloudEdit` | `boolean` | No | Determines whether or not consumer can cloud the target universe This is only nullable/optional in the context of https://develop.roblox.com/docs#!/Universes/get_v1_universes_universeId_context_permissions endpoint which is consumed only internally. It should be computed and set for all other usages. | ### Roblox.Api.Develop.Models.UniverseSettingsRequest Model for UniverseSettings patch requests | Property | Type | Required | Description | |----------|------|----------|-------------| | `name` | `string` | No | The name of the universe. | | `universeAvatarType` | `1 \| 2 \| 3` | No | Which avatar types are allowed in the universe. | | `universeScaleType` | `1 \| 2` | No | Whether custom scales allowed in the universe. | | `universeAnimationType` | `1 \| 2` | No | Whether custom animations are allowed in the universe. | | `universeCollisionType` | `1 \| 2` | No | What type of collisions are used by the universe. | | `universeBodyType` | `1 \| 2` | No | What avatar body types are allowed by the universe. | | `universeJointPositioningType` | `1 \| 2` | No | What avatar joint positioning type is allowed by the universe. | | `isArchived` | `boolean` | No | Archive status of the universe. | | `isFriendsOnly` | `boolean` | No | Whether game access is limited to friends for user-owned games or group members for group-owned games. | | `genre` | `integer enum (15 values)` | No | Game genre. Values: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 | | `playableDevices` | `integer enum (6 values)[]` | No | List of device types this game can be played on. | | `isForSale` | `boolean` | No | Whether the game is offered for sale. | | `price` | `integer` | No | Price of the game, in Robux. | | `isMeshTextureApiAccessAllowed` | `boolean` | No | Sets whether access to APIs for mesh and texture is enabled for this universe. | | `isRewardedOnDemandAdsAllowed` | `boolean` | No | Set is rewarded on-demand ads are allowed for this universe. | | `fiatBasePriceId` | `string` | No | Sets the base price of the experience for Fiat purchases. | | `fiatProductChangeType` | `0 \| 1 \| 2 \| 3` | No | Determines the change type of the Fiat Product Change request. Can either Activate the product, Update the price, or Deactivate the product. | | `demoModeEnabled` | `boolean` | No | Whether demo mode is enabled for this paid access game. | ### Roblox.Api.Develop.Models.UniverseSettingsResponse Model for UniverseSettings controller responses | Property | Type | Required | Description | |----------|------|----------|-------------| | `allowPrivateServers` | `boolean` | No | If the universe allows the use of private servers. | | `privateServerPrice` | `integer` | No | The price to purchase a private server in robux. | | `isMeshTextureApiAccessAllowed` | `boolean` | No | Whether access to APIs for mesh and texture is enabled for this universe. | | `isRewardedOnDemandAdsAllowed` | `boolean` | No | Whether rewarded on-demand ads are allowed for this universe. | | `id` | `integer` | No | The universe Id. | | `name` | `string` | No | The universe name. | | `universeAvatarType` | `1 \| 2 \| 3` | No | Which avatar types are allowed in the universe. ['MorphToR6' = 1, 'PlayerChoice' = 2, 'MorphToR15' = 3] | | `universeScaleType` | `1 \| 2` | No | Whether custom scales allowed in the universe. ['NoScales' = 1, 'AllScales' = 2] | | `universeAnimationType` | `1 \| 2` | No | Whether custom animations are allowed in the universe. ['Standard' = 1, 'PlayerChoice' = 2] | | `universeCollisionType` | `1 \| 2` | No | What type of collisions are used by the universe. ['InnerBox' = 1, 'OuterBox' = 2] | | `universeBodyType` | `1 \| 2` | No | What avatar body types are allowed by the universe. ['Standard' = 1, 'PlayerChoice' = 2] | | `universeJointPositioningType` | `1 \| 2` | No | What avatar joint positioning is allowed by the universe. ['Standard' = 1, 'ArtistIntent' = 2] | | `isArchived` | `boolean` | No | Archive status of the universe | | `isFriendsOnly` | `boolean` | No | Whether game access is limited to friends for user-owned games or group members for group-owned games. | | `genre` | `integer enum (15 values)` | No | Game genre. ['All' = 0, 'Tutorial' = 1, 'Scary' = 2, 'TownAndCity' = 3, 'War' = 4, 'Funny' = 5, 'Fantasy' = 6, 'Adventure' = 7, 'SciFi' = 8, 'Pirate' = 9, 'FPS' = 10, 'RPG' = 11, 'Sports' = 12, 'Ninja' = 13, 'WildWest' = 14] Values: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 | | `playableDevices` | `integer enum (6 values)[]` | No | List of device types this game can be played on. | | `isForSale` | `boolean` | No | Whether the game is offered for sale. | | `price` | `integer` | No | Price of the game, in Robux. | | `isStudioAccessToApisAllowed` | `boolean` | No | Whether studio access to APIs is allowed or not. | | `privacyType` | `string` | No | Privacy type of the universe. | | `isForSaleInFiat` | `boolean` | No | Whether the game is offered for sale in fiat. | | `fiatBasePriceId` | `string` | No | The basePriceId for the Fiat product. | | `fiatModerationStatus` | `0 \| 1 \| 2 \| 3 \| 4` | No | The current moderation status of the game if it is for sale in fiat. ['Invalid' = 0, 'NotModerated' = 1, 'Pending' = 2, 'Approved' = 3, 'Rejected' = 4] | | `audiences` | `0 \| 1 \| 2 \| 3 \| 4[]` | No | The audiences this universe is visible to (e.g. Editors, PlayTesters, Friends, Public). Always non-null; may be empty when audience visibility has not been configured. | | `demoModeEnabled` | `boolean` | No | Whether demo mode is enabled. | | `demoModeLastChangedTime` | `string` | No | When demo mode was last toggled. For client-side throttle UX. | ### Roblox.Api.Develop.Models.UniverseTeamCreateSettingsModel Team create settings of a universe | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | Id of the universe. | | `isEnabled` | `boolean` | No | Whether or not the universe is enabled for team create | ### Roblox.Api.Develop.Models.UpdateTeamCreateSettingsRequest Team create settings request | Property | Type | Required | Description | |----------|------|----------|-------------| | `isEnabled` | `boolean` | No | Whether or not the universe should be enabled for team create | ### Roblox.Api.Develop.Models.UserPublicPublishEligibilityResponse The result of various checks for a user's eligibility to publish a public universe. | Property | Type | Required | Description | |----------|------|----------|-------------| | `isEligible` | `boolean` | No | The overall result of eligibility. | | `hasTransactions` | `0 \| 1 \| 2` | No | Whether user has spent any Robux since 1/1/25, including gift cards. ['Incomplete' = 0, 'NotRequired' = 1, 'Completed' = 2] | | `idVerified` | `0 \| 1 \| 2` | No | Whether user has completed ID Verification or not. ['Incomplete' = 0, 'NotRequired' = 1, 'Completed' = 2] | | `hasDevex` | `0 \| 1 \| 2` | No | Whether user is eligible for the developer exchange program. ['Incomplete' = 0, 'NotRequired' = 1, 'Completed' = 2] | ### Roblox.Develop.Api.UpdatePluginRequest A request model for updating a plugin. | Property | Type | Required | Description | |----------|------|----------|-------------| | `name` | `string` | No | The new plugin name. | | `description` | `string` | No | The new plugin description. | | `commentsEnabled` | `boolean` | No | Whether or not comments should be enabled. | ### Roblox.Web.Responses.Plugins.PluginResponse Represents information about a plugin. | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | The plugin Id. | | `name` | `string` | No | The plugin name. | | `description` | `string` | No | The plugin description. | | `commentsEnabled` | `boolean` | No | Whether or not the plugin allows comments to be posted. | | `versionId` | `integer` | No | plugin version id | | `created` | `string` | No | The time the plugin was created. | | `updated` | `string` | No | The last time the plugin was updated. | ### Roblox.Web.Responses.Users.SkinnyUserResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | | | `name` | `string` | No | | | `displayName` | `string` | No | | ### Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Api.Develop.Models.GameTemplateModel] | Property | Type | Required | Description | |----------|------|----------|-------------| | `data` | `Roblox.Api.Develop.Models.GameTemplateModel[]` | No | | ### Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Api.Develop.Models.GroupModel] | Property | Type | Required | Description | |----------|------|----------|-------------| | `data` | `Roblox.Api.Develop.Models.GroupModel[]` | No | | ### Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Api.Develop.Models.Response.AssetVotingModel] | Property | Type | Required | Description | |----------|------|----------|-------------| | `data` | `Roblox.Api.Develop.Models.Response.AssetVotingModel[]` | No | | ### Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Api.Develop.Models.UniverseIdPermissionsModel] | Property | Type | Required | Description | |----------|------|----------|-------------| | `data` | `Roblox.Api.Develop.Models.UniverseIdPermissionsModel[]` | No | | ### Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Api.Develop.Models.UniverseModel] | Property | Type | Required | Description | |----------|------|----------|-------------| | `data` | `Roblox.Api.Develop.Models.UniverseModel[]` | No | | ### Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Api.Develop.Models.UniverseTeamCreateSettingsModel] | Property | Type | Required | Description | |----------|------|----------|-------------| | `data` | `Roblox.Api.Develop.Models.UniverseTeamCreateSettingsModel[]` | No | | ### Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Web.Responses.Plugins.PluginResponse] | Property | Type | Required | Description | |----------|------|----------|-------------| | `data` | `Roblox.Web.Responses.Plugins.PluginResponse[]` | No | | ### Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Web.Responses.Users.SkinnyUserResponse] | Property | Type | Required | Description | |----------|------|----------|-------------| | `data` | `Roblox.Web.Responses.Users.SkinnyUserResponse[]` | No | | ### Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Api.Develop.Models.IPlaceModel] | Property | Type | Required | Description | |----------|------|----------|-------------| | `previousPageCursor` | `string` | No | | | `nextPageCursor` | `string` | No | | | `data` | `Roblox.Api.Develop.Models.IPlaceModel[]` | No | | ### Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Api.Develop.Models.UniverseModel] | Property | Type | Required | Description | |----------|------|----------|-------------| | `previousPageCursor` | `string` | No | | | `nextPageCursor` | `string` | No | | | `data` | `Roblox.Api.Develop.Models.UniverseModel[]` | No | | --- name: "Develop Api v2" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://develop.roblox.com" version: "v2" endpoints: 5 auth: [cookie] --- # Develop Api v2 **API Version:** v2 **Base URL:** `https://develop.roblox.com` ## Endpoints ### GET `/v2/assets/{id}/versions` *(deprecated)* Retrieves asset information for the specified asset ID. The authenticated user must be able to manage the asset or granted by package permission. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `id` | path | `integer (int64)` | Yes | The ID of the asset.Roblox.Platform.Assets.IAsset | | `Roblox-Place-Id` | header | `integer (int64)` | Yes | The ID of the place.Roblox.Platform.Assets.IPlace | | `limit` | query | `integer (int32)` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `string` | No | Sort by version number, default is desc. Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Api.Develop.AssetVersion]` **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Api.Develop.AssetVersion]`) See [Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Api.Develop.AssetVersion]](#roblox-web-webapi-models-apipageresponse-roblox-api-develop-assetversion-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "Id": "...", "assetId": "...", "assetVersionNumber": "...", "creatorType": "...", "creatorTargetId": "...", "creatingUniverseId": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v2/assets/{ID}/versions" ``` ### GET `/v2/places/{placeId}` Gets the place configuration for the place with the id placeId **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `placeId` | path | `integer (int64)` | Yes | The place id for the place to be updated. | **Responses:** - `200`: OK → `Roblox.Api.Develop.Models.PlaceModelV2` - `401`: 0: Authorization has been denied for this request. - `403`: Authenticated user is not authorized to manage this place. - `404`: placeId Place not found. **Response fields** (`Roblox.Api.Develop.Models.PlaceModelV2`) See [Roblox.Api.Develop.Models.PlaceModelV2](#roblox-api-develop-models-placemodelv2) in Models. **Response example:** ```json { "maxPlayerCount": 0, "socialSlotType": "string", "customSocialSlotsCount": 0, "allowCopying": false, "currentSavedVersion": 0, "isAllGenresAllowed": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v2/places/{PLACEID}" ``` ### PATCH `/v2/places/{placeId}` Updates the place configuration for the place with the id placeId **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `placeId` | path | `integer (int64)` | Yes | The place id for the place to be updated. | **Request Body:** `application/json` — Type: `Roblox.Api.Develop.Models.PlaceConfigurationModelV2` See [Roblox.Api.Develop.Models.PlaceConfigurationModelV2](#roblox-api-develop-models-placeconfigurationmodelv2) in Models. **Request example:** ```json { "name": "string", "description": "string", "maxPlayerCount": 0, "socialSlotType": "string", "customSocialSlotsCount": 0, "allowCopying": false } ``` **Responses:** - `200`: OK → `Roblox.Api.Develop.Models.PlaceModelV2` - `401`: 0: Authorization has been denied for this request. - `403`: Authenticated user is not authorized to manage this place. 0: Token Validation Failed - `404`: placeId Place not found. **Response fields** (`Roblox.Api.Develop.Models.PlaceModelV2`) See [Roblox.Api.Develop.Models.PlaceModelV2](#roblox-api-develop-models-placemodelv2) in Models. **Response example:** ```json { "maxPlayerCount": 0, "socialSlotType": "string", "customSocialSlotsCount": 0, "allowCopying": false, "currentSavedVersion": 0, "isAllGenresAllowed": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v2/places/{PLACEID}" \ -H "Content-Type: application/json" \ -d '{ "name": "string", "description": "string", "maxPlayerCount": 0, "socialSlotType": "string", "customSocialSlotsCount": 0, "allowCopying": false }' ``` ### PATCH `/v2/universes/{universeId}/configuration` Update universe settings for an owned universe. V2 Contains data for avatar scale and asset override. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer (int64)` | Yes | The universeId. | **Request Body:** `application/json` — Type: `Roblox.Api.Develop.Models.UniverseSettingsRequestV2` See [Roblox.Api.Develop.Models.UniverseSettingsRequestV2](#roblox-api-develop-models-universesettingsrequestv2) in Models. **Request example:** ```json { "allowPrivateServers": false, "privateServerPrice": 0, "name": "string", "description": "string", "universeAvatarType": 1, "universeAnimationType": 1 } ``` **Responses:** - `200`: OK → `Roblox.Api.Develop.Models.UniverseSettingsResponseV2` - `400`: 1: The universe does not exist. 3: Invalid UniverseAvatarType. 4: Invalid UniverseScaleType. 5: Invalid UniverseAnimationType. 6: Invalid UniverseCollisionType. 7: New universe name or description has been rejected. 8: New universe name is too long. 10: Invalid UniverseBodyType. 11: Invalid UniverseJointPositioningType. 12: The universe has no root place. 15: Price is required when isForSale is true. 16: This game cannot be offered for sale because it is not public. 17: This game cannot be offered for sale because it has private servers enabled. 18: The game price is outside of the allowed range. 19: Invalid genre. 20: The request body is missing. 21: Invalid device type. 22: Invalid asset type. 23: Invalid value, the min must be less than or equal to the max 24: Invalid scale value 28: OptIn/Out Regions Not Supported. 41: You cannot change the private server price again so soon after the previous change. Please try again later. 44: The provided audience configuration is invalid. Ensure the audience list contains only supported audience values. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: You are not authorized to configure this universe. 14: You are not authorized to sell games. 29: Luobu app terms of service user agreement is missing. 30: Unknown error while updating Opt in out region. 45: The creator of this experience is not eligible to set this audience. - `409`: 9: Failed to shutdown all intances of game after changing AvatarType. The change has been reverted. - `500`: 43: Failed to update the audience configuration. The change was not applied. Please try again. **Response fields** (`Roblox.Api.Develop.Models.UniverseSettingsResponseV2`) See [Roblox.Api.Develop.Models.UniverseSettingsResponseV2](#roblox-api-develop-models-universesettingsresponsev2) in Models. **Response example:** ```json { "allowPrivateServers": false, "privateServerPrice": 0, "optInRegions": [ { "region": "...", "status": "..." } ], "isMeshTextureApiAccessAllowed": false, "isRewardedOnDemandAdsAllowed": false, "id": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v2/universes/{UNIVERSEID}/configuration" \ -H "Content-Type: application/json" \ -d '{ "allowPrivateServers": false, "privateServerPrice": 0, "name": "string", "description": "string", "universeAvatarType": 1, "universeAnimationType": 1 }' ``` ### DELETE `/v2/teamtest/{placeId}` Close a game instance that is being used for team testing **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `placeId` | path | `integer (int64)` | Yes | The Id of the place we are setting the metadata for. | | `gameId` | query | `string (uuid)` | Yes | the Guid of the game instance System.Guid | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://develop.roblox.com/v2/teamtest/{PLACEID}?gameId={VALUE}" ``` ## Models ### Roblox.Api.Develop.AssetVersion Model of an asset version. | Property | Type | Required | Description | |----------|------|----------|-------------| | `Id` | `integer` | No | The VersionID of the asset version. | | `assetId` | `integer` | No | The ID of the asset. | | `assetVersionNumber` | `integer` | No | The version number. | | `creatorType` | `string` | No | Type of the asset version creator. | | `creatorTargetId` | `integer` | No | ID of the asset version creator. | | `creatingUniverseId` | `integer` | No | ID of the universe this asset version was created in. | | `created` | `string` | No | The created date of this asset version. | | `isEqualToCurrentPublishedVersion` | `boolean` | No | Indicates if this version is same to current published version. This property is available on /v1/{assetId}/published-versions and /v1/{assetId}/version/{versionNumber}. | | `isPublished` | `boolean` | No | Indicates if this version is / was published. This property is available on /v1/{assetId}/saved-versions. This should be true for all assets coming from GetAssetPublishedVersionsByAssetId | ### Roblox.Api.Develop.Models.PlaceConfigurationModelV2 A model containing information about a place to be configured | Property | Type | Required | Description | |----------|------|----------|-------------| | `name` | `string` | No | The name for the place. | | `description` | `string` | No | The new description for the place. | | `maxPlayerCount` | `integer` | No | The max number of players for the place. | | `socialSlotType` | `string` | No | The social slot type for the place. Determines how users are placed into servers. Examples: Automatic, Empty, Custom | | `customSocialSlotsCount` | `integer` | No | The number of social slots for the place when the slot type is custom. | | `allowCopying` | `boolean` | No | Determines if copying of the place is allowed. | | `allowedGearTypes` | `string[]` | No | List of allowed gear types | | `isAllGenresAllowed` | `boolean` | No | If all genres are allowed, or only the experience type | ### Roblox.Api.Develop.Models.PlaceModelV2 A model containing information about a place | Property | Type | Required | Description | |----------|------|----------|-------------| | `maxPlayerCount` | `integer` | No | The max number of players for the place. | | `socialSlotType` | `string` | No | The social slot type for the place. Determines how users are placed into servers. Examples: Automatic, Empty, Custom | | `customSocialSlotsCount` | `integer` | No | The number of social slots for the place when the slot type is custom. | | `allowCopying` | `boolean` | No | Determines if copying of the place is allowed. | | `currentSavedVersion` | `integer` | No | The current saved version number of the place. | | `isAllGenresAllowed` | `boolean` | No | Whether all genres allowed in the place. | | `allowedGearTypes` | `integer enum (10 values)[]` | No | Types of Roblox gear that are allowed to exist in the place. Valid values are from amp::AssetCategory | | `maxPlayersAllowed` | `integer` | No | The maximum allowed number of players for the place that the user can set, based on user roleset. | | `created` | `string` | No | The time place was created. | | `updated` | `string` | No | The time place was updated. | | `id` | `integer` | No | Returns the place id. | | `universeId` | `integer` | No | Returns the id of the place's universe, or null - if the place is not part of a universe. | | `name` | `string` | No | Returns the place name. | | `description` | `string` | No | Returns the place description. | | `isRootPlace` | `boolean` | No | Returns whether this place is the root place. | ### Roblox.Api.Develop.Models.UniverseModerationPolicyStatus Represents a universe moderation policy status | Property | Type | Required | Description | |----------|------|----------|-------------| | `region` | `0 \| 1` | No | The region policy label ['Unknown' = 0, 'China' = 1] | | `status` | `string` | No | The status of the universe | ### Roblox.Api.Develop.Models.UniverseSettingsRequestV2 Model for UniverseSettings patch requests | Property | Type | Required | Description | |----------|------|----------|-------------| | `allowPrivateServers` | `boolean` | No | If the universe allows the use of private servers. | | `privateServerPrice` | `integer` | No | The price to purchase a private server in robux. | | `name` | `string` | No | The name of the universe. | | `description` | `string` | No | The description of the universe. | | `universeAvatarType` | `1 \| 2 \| 3` | No | Which avatar types are allowed in the universe. | | `universeAnimationType` | `1 \| 2` | No | Whether custom animations are allowed in the universe. | | `universeCollisionType` | `1 \| 2` | No | What type of collisions are used by the universe. | | `universeJointPositioningType` | `1 \| 2` | No | What avatar joint positioning type is allowed by the universe. | | `engineAvatarSettings` | `string` | No | Optional JSON string used to store avatar settings used by the game engine. Will not be updated if null or empty. To clear settings, set to empty JSON object "{}". Note: This is an experimental field which may be changed or removed in future. | | `isArchived` | `boolean` | No | Archive status of the universe. | | `isFriendsOnly` | `boolean` | No | Whether game access is limited to friends for user-owned games or group members for group-owned games. | | `genre` | `integer enum (15 values)` | No | Game genre. Values: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 | | `playableDevices` | `integer enum (6 values)[]` | No | List of device types this game can be played on. | | `isForSale` | `boolean` | No | Whether the game is offered for sale. | | `price` | `integer` | No | Price of the game, in Robux. | | `universeAvatarAssetOverrides` | `Roblox.Platform.UniverseSettings.UniverseAvatarAssetOverrideResponseModel[]` | No | A list of avatar asset overrides. | | `universeAvatarMinScales` | `Roblox.Web.Responses.Avatar.ScaleModel` | No | | | `universeAvatarMaxScales` | `Roblox.Web.Responses.Avatar.ScaleModel` | No | | | `studioAccessToApisAllowed` | `boolean` | No | Whether Studio can access data stores of this universe. | | `permissions` | `Roblox.UniversePluginPermissionAuthority.Models.UniversePluginPermissions` | No | | | `optInRegions` | `0 \| 1[]` | No | A list of opt in region. | | `optOutRegions` | `0 \| 1[]` | No | A list of opt out region. | | `isMeshTextureApiAccessAllowed` | `boolean` | No | Sets whether access to APIs for mesh and texture is enabled for this universe. | | `isRewardedOnDemandAdsAllowed` | `boolean` | No | Whether rewarded on-demand ads are allowed for this universe. | | `fiatBasePriceId` | `string` | No | Sets the base price of the experience for Fiat purchases. | | `fiatProductChangeType` | `0 \| 1 \| 2 \| 3` | No | Determines the change type of the Fiat Product Change request. Can either Activate, Update Base Price, or Deactivate. | | `audiences` | `0 \| 1 \| 2 \| 3 \| 4[]` | No | The audiences this universe should be visible to (e.g. Editors, PlayTesters, Friends, Public). When provided, replaces the universe's existing audience set with the supplied values. | | `demoModeEnabled` | `boolean` | No | Whether demo mode is enabled for this paid access game. | ### Roblox.Api.Develop.Models.UniverseSettingsResponseV2 Model for UniverseSettings controller responses | Property | Type | Required | Description | |----------|------|----------|-------------| | `allowPrivateServers` | `boolean` | No | If the universe allows the use of private servers. | | `privateServerPrice` | `integer` | No | The price to purchase a private server in robux. | | `optInRegions` | `Roblox.Api.Develop.Models.UniverseModerationPolicyStatus[]` | No | The regions the universe has opted in for | | `isMeshTextureApiAccessAllowed` | `boolean` | No | Whether access to APIs for mesh and texture is enabled for this universe. | | `isRewardedOnDemandAdsAllowed` | `boolean` | No | Whether rewarded on-demand ads are allowed for this universe. | | `id` | `integer` | No | The universe Id. | | `name` | `string` | No | The universe name. | | `description` | `string` | No | The universe description. | | `universeAvatarType` | `1 \| 2 \| 3` | No | Which avatar types are allowed in the universe. ['MorphToR6' = 1, 'PlayerChoice' = 2, 'MorphToR15' = 3] | | `universeAnimationType` | `1 \| 2` | No | Whether custom animations are allowed in the universe. ['Standard' = 1, 'PlayerChoice' = 2] | | `universeCollisionType` | `1 \| 2` | No | What type of collisions are used by the universe. ['InnerBox' = 1, 'OuterBox' = 2] | | `universeJointPositioningType` | `1 \| 2` | No | What avatar joint positioning is allowed by the universe. ['Standard' = 1, 'ArtistIntent' = 2] | | `engineAvatarSettings` | `string` | No | Optional JSON string used to store avatar settings used by the game engine. Note: This is an experimental field which may be changed or removed in future. | | `isArchived` | `boolean` | No | Archive status of the universe | | `isFriendsOnly` | `boolean` | No | Whether game access is limited to friends for user-owned games or group members for group-owned games. | | `genre` | `integer enum (15 values)` | No | Game genre. ['All' = 0, 'Tutorial' = 1, 'Scary' = 2, 'TownAndCity' = 3, 'War' = 4, 'Funny' = 5, 'Fantasy' = 6, 'Adventure' = 7, 'SciFi' = 8, 'Pirate' = 9, 'FPS' = 10, 'RPG' = 11, 'Sports' = 12, 'Ninja' = 13, 'WildWest' = 14] Values: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 | | `playableDevices` | `integer enum (6 values)[]` | No | List of device types this game can be played on. | | `isForSale` | `boolean` | No | Whether the game is offered for sale. | | `price` | `integer` | No | Price of the game, in Robux. | | `universeAvatarAssetOverrides` | `Roblox.Platform.UniverseSettings.UniverseAvatarAssetOverrideResponseModel[]` | No | A collection of avatar asset settings allowed by the universe. | | `universeAvatarMinScales` | `Roblox.Web.Responses.Avatar.ScaleModel` | No | | | `universeAvatarMaxScales` | `Roblox.Web.Responses.Avatar.ScaleModel` | No | | | `studioAccessToApisAllowed` | `boolean` | No | Whether Studio can access data stores of this universe. | | `permissions` | `Roblox.UniversePluginPermissionAuthority.Models.UniversePluginPermissions` | No | | | `isForSaleInFiat` | `boolean` | No | Whether the game is offered for sale in fiat. | | `fiatBasePriceId` | `string` | No | The basePriceId for the Fiat product. | | `fiatModerationStatus` | `0 \| 1 \| 2 \| 3 \| 4` | No | The current moderation status of the game if it is for sale in fiat. ['Invalid' = 0, 'NotModerated' = 1, 'Pending' = 2, 'Approved' = 3, 'Rejected' = 4] | | `audiences` | `0 \| 1 \| 2 \| 3 \| 4[]` | No | The audiences this universe is visible to (e.g. Editors, PlayTesters, Friends, Public). Always non-null; may be empty when audience visibility has not been configured. | | `demoModeEnabled` | `boolean` | No | Whether demo mode is enabled. | | `demoModeLastChangedTime` | `string` | No | When demo mode was last toggled. For client-side throttle UX. | ### Roblox.Platform.UniverseSettings.UniverseAvatarAssetOverrideResponseModel | Property | Type | Required | Description | |----------|------|----------|-------------| | `assetID` | `integer` | No | | | `assetTypeID` | `integer` | No | | | `isPlayerChoice` | `boolean` | No | | ### Roblox.UniversePluginPermissionAuthority.Models.UniversePluginPermissions | Property | Type | Required | Description | |----------|------|----------|-------------| | `IsThirdPartyTeleportAllowed` | `boolean` | No | | | `IsThirdPartyAssetAllowed` | `boolean` | No | | | `IsThirdPartyPurchaseAllowed` | `boolean` | No | | | `IsClientTeleportAllowed` | `boolean` | No | | ### Roblox.Web.Responses.Avatar.ScaleModel | Property | Type | Required | Description | |----------|------|----------|-------------| | `height` | `number` | No | | | `width` | `number` | No | | | `head` | `number` | No | | | `depth` | `number` | No | | | `proportion` | `number` | No | | | `bodyType` | `number` | No | | ### Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Api.Develop.AssetVersion] | Property | Type | Required | Description | |----------|------|----------|-------------| | `previousPageCursor` | `string` | No | | | `nextPageCursor` | `string` | No | | | `data` | `Roblox.Api.Develop.AssetVersion[]` | No | | --- name: "Economy Api v1" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://economy.roblox.com" version: "v1" endpoints: 1 auth: [cookie] --- # Economy Api v1 **API Version:** v1 **Base URL:** `https://economy.roblox.com` ## Endpoints ### GET `/v1/user/currency` Gets currency for the authenticated user. Currency can only be retrieved for the authenticated user. **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Web.Responses.Economy.CurrencyResponse` - `401`: 0: Authorization has been denied for this request. - `403`: 1: The user is invalid. **Response fields** (`Roblox.Web.Responses.Economy.CurrencyResponse`) See [Roblox.Web.Responses.Economy.CurrencyResponse](#roblox-web-responses-economy-currencyresponse) in Models. **Response example:** ```json { "robux": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://economy.roblox.com/v1/user/currency" ``` ## Models ### Roblox.Web.Responses.Economy.CurrencyResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `robux` | `integer` | No | | --- name: "Roblox.EconomyCreatorStats.Api v1" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://economycreatorstats.roblox.com" version: "v1" endpoints: 1 auth: [cookie] --- # Roblox.EconomyCreatorStats.Api v1 **API Version:** v1 **Base URL:** `https://economycreatorstats.roblox.com` ## Endpoints ### GET `/v1/universes/{universeId}/stats` Get statistics data for a universe. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer (int64)` | Yes | The universe id. | | `Type` | query | `string` | Yes | Valid values: `PremiumUpsells`, `PremiumVisits` | | `StartTime` | query | `string (date-time)` | Yes | | | `EndTime` | query | `string (date-time)` | Yes | | **Responses:** - `200`: OK → `Roblox.EconomyCreatorStats.Api.Models.StatisticsResponse` - `400`: 1: The Universe is invalid. 3: Too many data points requested. 4: The requested data type is not known. - `401`: 0: Authorization has been denied for this request. 2: Not authorized to perform this action. **Response fields** (`Roblox.EconomyCreatorStats.Api.Models.StatisticsResponse`) See [Roblox.EconomyCreatorStats.Api.Models.StatisticsResponse](#roblox-economycreatorstats-api-models-statisticsresponse) in Models. **Response example:** ```json { "dataGranularity": 0, "data": "..." } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://economycreatorstats.roblox.com/v1/universes/{UNIVERSEID}/stats?Type={VALUE}&StartTime={VALUE}&EndTime={VALUE}" ``` ## Models ### Roblox.EconomyCreatorStats.Api.Models.StatisticsResponse The response model | Property | Type | Required | Description | |----------|------|----------|-------------| | `dataGranularity` | `0 \| 1 \| 2` | No | The Roblox.EconomyCreatorStats.Api.StatisticsDataGranularity. ['Hourly' = 0, 'Daily' = 1, 'Monthly' = 2] | | `data` | `object` | No | The actual data. | --- name: "EngagementPayouts Api v1" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://engagementpayouts.roblox.com" version: "v1" endpoints: 1 auth: [cookie] --- # EngagementPayouts Api v1 **API Version:** v1 **Base URL:** `https://engagementpayouts.roblox.com` ## Endpoints ### GET `/v1/universe-payout-history` Gets the engagement payout history for a specific universe and a given date range, specified by start and end dates. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | query | `integer (int64)` | Yes | The ID of the universe in question. | | `startDate` | query | `string` | Yes | The first date in the range, specified as yyyy-MM-dd. | | `endDate` | query | `string` | Yes | The last date in the range, specified as yyyy-MM-dd. | **Responses:** - `200`: OK → `object` - `400`: 1: InvalidUniverseId 2: InvalidStartDate 3: InvalidEndDate 4: InvalidDateRange 5: Forbidden 6: TooManyDays - `401`: 0: Authorization has been denied for this request. **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://engagementpayouts.roblox.com/v1/universe-payout-history?universeId={VALUE}&startDate={VALUE}&endDate={VALUE}" ``` ## Models ### Roblox.EngagementPayouts.Api.PayoutResponseModel A model for payout responses | Property | Type | Required | Description | |----------|------|----------|-------------| | `engagementScore` | `number` | No | Gets the engagement score | | `payoutInRobux` | `integer` | No | Gets the payout in Robux | | `payoutType` | `string` | No | Gets the payout type | | `eligibilityType` | `string` | No | Gets the eligibility type | --- name: "Followings Api v1" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://followings.roblox.com" version: "v1" endpoints: 4 auth: [cookie] --- # Followings Api v1 **API Version:** v1 **Base URL:** `https://followings.roblox.com` ## Endpoints ### GET `/v1/users/{userId}/universes` Gets all the followings between a user with userId and universes **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | | **Responses:** - `200`: OK → `Roblox.Followings.Api.Models.UserFollowingUniverseResponse[]` - `401`: 0: Authorization has been denied for this request. - `403`: User is not authorized for this action. **Response fields** (`Roblox.Followings.Api.Models.UserFollowingUniverseResponse[]`) See [Roblox.Followings.Api.Models.UserFollowingUniverseResponse](#roblox-followings-api-models-userfollowinguniverseresponse) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://followings.roblox.com/v1/users/{USERID}/universes" ``` ### GET `/v1/users/{userId}/universes/{universeId}/status` Gets the status of a following relationship between a user and a universe. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | | | `universeId` | path | `integer (int64)` | Yes | | **Responses:** - `200`: OK → `Roblox.Followings.Api.Models.UserFollowingUniverseStatusResponse` - `401`: 0: Authorization has been denied for this request. - `403`: User is not authorized for this action. **Response fields** (`Roblox.Followings.Api.Models.UserFollowingUniverseStatusResponse`) See [Roblox.Followings.Api.Models.UserFollowingUniverseStatusResponse](#roblox-followings-api-models-userfollowinguniversestatusresponse) in Models. **Response example:** ```json { "UniverseId": 0, "UserId": 0, "CanFollow": false, "IsFollowing": false, "FollowingCountByType": 0, "FollowingLimitByType": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://followings.roblox.com/v1/users/{USERID}/universes/{UNIVERSEID}/status" ``` ### POST `/v1/users/{userId}/universes/{universeId}` Creates the following between a user with userId and universe with universeId **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | | | `universeId` | path | `integer (int64)` | Yes | | **Responses:** - `200`: OK → `Roblox.Followings.Api.Models.UserFollowingUniverseResponse` - `400`: The user has reached the limit of number of followed universes. - `401`: 0: Authorization has been denied for this request. - `403`: User is not authorized for this action. 0: Token Validation Failed **Response fields** (`Roblox.Followings.Api.Models.UserFollowingUniverseResponse`) See [Roblox.Followings.Api.Models.UserFollowingUniverseResponse](#roblox-followings-api-models-userfollowinguniverseresponse) in Models. **Response example:** ```json { "universeId": 0, "userId": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://followings.roblox.com/v1/users/{USERID}/universes/{UNIVERSEID}" ``` ### DELETE `/v1/users/{userId}/universes/{universeId}` Deletes the following between a user with userId and universe with universeId **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | | | `universeId` | path | `integer (int64)` | Yes | | **Responses:** - `200`: OK → `Roblox.Followings.Api.Models.UserFollowingUniverseResponse` - `401`: 0: Authorization has been denied for this request. - `403`: User is not authorized for this action. 0: Token Validation Failed **Response fields** (`Roblox.Followings.Api.Models.UserFollowingUniverseResponse`) See [Roblox.Followings.Api.Models.UserFollowingUniverseResponse](#roblox-followings-api-models-userfollowinguniverseresponse) in Models. **Response example:** ```json { "universeId": 0, "userId": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://followings.roblox.com/v1/users/{USERID}/universes/{UNIVERSEID}" ``` ## Models ### Roblox.Followings.Api.Models.UserFollowingUniverseResponse Model for a user following a universe controller responses | Property | Type | Required | Description | |----------|------|----------|-------------| | `universeId` | `integer` | No | The id of the universe being followed | | `userId` | `integer` | No | The id of the user that is following | ### Roblox.Followings.Api.Models.UserFollowingUniverseStatusResponse Model for a user following a universe state controller responses | Property | Type | Required | Description | |----------|------|----------|-------------| | `UniverseId` | `integer` | No | The id of the universe. | | `UserId` | `integer` | No | The id of the user. | | `CanFollow` | `boolean` | No | If the user can follow the universe. | | `IsFollowing` | `boolean` | No | If the user is currently following the universe. | | `FollowingCountByType` | `integer` | No | The number of followings between this user and a universe. | | `FollowingLimitByType` | `integer` | No | The limit to the number of followings between a user and a universe for a specific user. | --- name: "Followings Api v2" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://followings.roblox.com" version: "v2" endpoints: 1 auth: [cookie] --- # Followings Api v2 **API Version:** v2 **Base URL:** `https://followings.roblox.com` ## Endpoints ### GET `/v2/users/{userId}/universes` Gets all universes followed by a user. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user ID. | **Responses:** - `200`: OK → `Roblox.Followings.Api.Models.FollowsByTypeResponse` - `401`: 0: Authorization has been denied for this request. - `403`: User is not authorized for this action. **Response fields** (`Roblox.Followings.Api.Models.FollowsByTypeResponse`) See [Roblox.Followings.Api.Models.FollowsByTypeResponse](#roblox-followings-api-models-followsbytyperesponse) in Models. **Response example:** ```json { "followerType": 0, "followerId": 0, "sourceType": 0, "followedSources": "..." } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://followings.roblox.com/v2/users/{USERID}/universes" ``` ## Models ### Roblox.Followings.Api.Models.FollowsByTypeResponse Data model containing collection of all followed sources of a specific type. | Property | Type | Required | Description | |----------|------|----------|-------------| | `followerType` | `0 \| 1` | No | Type of the follower entity. ['Invalid' = 0, 'User' = 1] | | `followerId` | `integer` | No | ID of the follower entity. | | `sourceType` | `0 \| 1` | No | Type of the source entity. ['Invalid' = 0, 'Universe' = 1] | | `followedSources` | `object` | No | Followed sources: map of (source ID => follow date) | --- name: "Friends Api v1" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://friends.roblox.com" version: "v1" endpoints: 24 auth: [cookie] --- # Friends Api v1 **API Version:** v1 **Base URL:** `https://friends.roblox.com` ## Endpoints ### GET `/v1/metadata` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `targetUserId` | query | `integer (int64)` | No | | **Responses:** - `200`: OK → `Roblox.Friends.Api.Models.Response.FriendsPageMetadataResponse` **Response fields** (`Roblox.Friends.Api.Models.Response.FriendsPageMetadataResponse`) See [Roblox.Friends.Api.Models.Response.FriendsPageMetadataResponse](#roblox-friends-api-models-response-friendspagemetadataresponse) in Models. **Response example:** ```json { "isFriendsFilterBarEnabled": false, "isFriendsPageSortExperimentEnabled": false, "isFriendsUserDataStoreCacheEnabled": false, "frequentFriendSortRollout": 0, "userName": "string", "displayName": "string" } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://friends.roblox.com/v1/metadata" ``` ### GET `/v1/my/friends/{userId}/check-qr-session` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | user Id that shows the qr code | **Responses:** - `200`: OK → `boolean` - `401`: 0: Authorization has been denied for this request. **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://friends.roblox.com/v1/my/friends/{USERID}/check-qr-session" ``` ### GET `/v1/my/friends/count` Get the number of friends a user has **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Friends.Api.FriendsCountResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Friends.Api.FriendsCountResponse`) See [Roblox.Friends.Api.FriendsCountResponse](#roblox-friends-api-friendscountresponse) in Models. **Response example:** ```json { "count": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://friends.roblox.com/v1/my/friends/count" ``` ### GET `/v1/my/friends/requests` Get all users that friend requests with targetUserId using exclusive start paging **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `limit` | query | `integer (int32)` | No | The number of results per request. | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sessionId` | query | `string` | No | Optional session identifier. | | `friendRequestSort` | query | `integer (int32)` | No | Valid values: `0`, `1`, `2` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Friends.Api.FriendRequestResponse]` - `400`: 1: The target user is invalid or does not exist. 6: Invalid parameters. - `401`: 0: Authorization has been denied for this request. - `403`: 2: The user is banned from performing operation. 3: The user is blocked from performing this action. - `429`: 9: The flood limit has been exceeded. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Friends.Api.FriendRequestResponse]`) See [Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Friends.Api.FriendRequestResponse]](#roblox-web-webapi-models-apipageresponse-roblox-friends-api-friendrequestresponse-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "friendRequest": "...", "mutualFriendsList": "...", "hasVerifiedBadge": "...", "description": "...", "created": "...", "isBanned": "..." } ] } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://friends.roblox.com/v1/my/friends/requests" ``` ### GET `/v1/my/new-friend-requests/count` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Friends.Api.Models.Response.NewFriendRequestsCountResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Friends.Api.Models.Response.NewFriendRequestsCountResponse`) See [Roblox.Friends.Api.Models.Response.NewFriendRequestsCountResponse](#roblox-friends-api-models-response-newfriendrequestscountresponse) in Models. **Response example:** ```json { "count": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://friends.roblox.com/v1/my/new-friend-requests/count" ``` ### GET `/v1/my/trusted-friends/count` Get the number of trusted friends a user has **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Friends.Api.FriendsCountResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Friends.Api.FriendsCountResponse`) See [Roblox.Friends.Api.FriendsCountResponse](#roblox-friends-api-friendscountresponse) in Models. **Response example:** ```json { "count": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://friends.roblox.com/v1/my/trusted-friends/count" ``` ### GET `/v1/user/friend-requests/count` Return the number of pending friend requests. **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Friends.Api.PendingFriendRequestCountModel` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Friends.Api.PendingFriendRequestCountModel`) See [Roblox.Friends.Api.PendingFriendRequestCountModel](#roblox-friends-api-pendingfriendrequestcountmodel) in Models. **Response example:** ```json { "count": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://friends.roblox.com/v1/user/friend-requests/count" ``` ### GET `/v1/users/{targetUserId}/followers` Get all users that follow user with targetUserId in page response format **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `targetUserId` | path | `integer (int64)` | Yes | | | `limit` | query | `integer (int32)` | No | The number of results per request. Valid values: `10`, `18`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `string` | No | The order the results are sorted in. Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Friends.Api.Models.Response.UserResponse]` - `400`: 1: The target user is invalid or does not exist. 6: Invalid parameters. - `403`: 2: The user is banned from performing operation. 3: The user is blocked from performing this action. - `429`: 9: The flood limit has been exceeded. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Friends.Api.Models.Response.UserResponse]`) See [Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Friends.Api.Models.Response.UserResponse]](#roblox-web-webapi-models-apipageresponse-roblox-friends-api-models-response-userresponse-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "isOnline": "...", "presenceType": "...", "isDeleted": "...", "friendFrequentScore": "...", "friendFrequentRank": "...", "hasVerifiedBadge": "..." } ] } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://friends.roblox.com/v1/users/{TARGETUSERID}/followers" ``` ### GET `/v1/users/{targetUserId}/followers/count` Get the number of following a user has **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `targetUserId` | path | `integer (int64)` | Yes | | **Responses:** - `200`: OK → `Roblox.Friends.Api.FollowCountResponse` - `400`: 1: The target user is invalid or does not exist. **Response fields** (`Roblox.Friends.Api.FollowCountResponse`) See [Roblox.Friends.Api.FollowCountResponse](#roblox-friends-api-followcountresponse) in Models. **Response example:** ```json { "count": 0 } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://friends.roblox.com/v1/users/{TARGETUSERID}/followers/count" ``` ### GET `/v1/users/{targetUserId}/followings` Get all users that user with targetUserId is following in page response format **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `targetUserId` | path | `integer (int64)` | Yes | | | `limit` | query | `integer (int32)` | No | The number of results per request. Valid values: `10`, `18`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `string` | No | The order the results are sorted in. Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Friends.Api.Models.Response.UserResponse]` - `400`: 1: The target user is invalid or does not exist. 6: Invalid parameters. - `403`: 2: The user is banned from performing operation. 3: The user is blocked from performing this action. - `429`: 9: The flood limit has been exceeded. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Friends.Api.Models.Response.UserResponse]`) See [Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Friends.Api.Models.Response.UserResponse]](#roblox-web-webapi-models-apipageresponse-roblox-friends-api-models-response-userresponse-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "isOnline": "...", "presenceType": "...", "isDeleted": "...", "friendFrequentScore": "...", "friendFrequentRank": "...", "hasVerifiedBadge": "..." } ] } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://friends.roblox.com/v1/users/{TARGETUSERID}/followings" ``` ### GET `/v1/users/{targetUserId}/followings/count` Get the number of following a user has **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `targetUserId` | path | `integer (int64)` | Yes | | **Responses:** - `200`: OK → `Roblox.Friends.Api.FollowCountResponse` - `400`: 1: The target user is invalid or does not exist. **Response fields** (`Roblox.Friends.Api.FollowCountResponse`) See [Roblox.Friends.Api.FollowCountResponse](#roblox-friends-api-followcountresponse) in Models. **Response example:** ```json { "count": 0 } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://friends.roblox.com/v1/users/{TARGETUSERID}/followings/count" ``` ### GET `/v1/users/{userId}/friends` Get list of all friends for the specified user. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user Id to get the friends for. | | `userSort` | query | `integer (int32)` | No | Specifies how to sort the returned friends. Valid values: `0`, `1`, `2` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Friends.Api.Models.Response.UserResponse]` - `400`: 1: The target user is invalid or does not exist. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Friends.Api.Models.Response.UserResponse]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Friends.Api.Models.Response.UserResponse]](#roblox-web-webapi-models-apiarrayresponse-roblox-friends-api-models-response-userresponse-) in Models. **Response example:** ```json { "data": [ { "isOnline": "...", "presenceType": "...", "isDeleted": "...", "friendFrequentScore": "...", "friendFrequentRank": "...", "hasVerifiedBadge": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://friends.roblox.com/v1/users/{USERID}/friends" ``` ### GET `/v1/users/{userId}/friends/count` Get the number of friends a user has **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | | **Responses:** - `200`: OK → `Roblox.Friends.Api.FriendsCountResponse` - `400`: 1: The target user is invalid or does not exist. **Response fields** (`Roblox.Friends.Api.FriendsCountResponse`) See [Roblox.Friends.Api.FriendsCountResponse](#roblox-friends-api-friendscountresponse) in Models. **Response example:** ```json { "count": 0 } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://friends.roblox.com/v1/users/{USERID}/friends/count" ``` ### GET `/v1/users/{userId}/friends/find` Get a paginated list of all friends for the specified user. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user Id to get the friends for. | | `userSort` | query | `integer (int32)` | No | Specifies how to sort the returned friends. Valid values: `0`, `1`, `2` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `limit` | query | `integer (int32)` | No | The number of results per request. | | `findFriendsType` | query | `integer (int32)` | No | Valid values: `0`, `1` | **Responses:** - `200`: OK → `Roblox.Paging.CursoredPagedResult[Roblox.Friends.Api.Models.Response.FriendResponse]` - `400`: 1: The target user is invalid or does not exist. 6: Invalid parameters. **Response fields** (`Roblox.Paging.CursoredPagedResult[Roblox.Friends.Api.Models.Response.FriendResponse]`) See [Roblox.Paging.CursoredPagedResult[Roblox.Friends.Api.Models.Response.FriendResponse]](#roblox-paging-cursoredpagedresult-roblox-friends-api-models-response-friendresponse-) in Models. **Response example:** ```json { "PreviousCursor": "string", "PageItems": [ { "id": "...", "hasVerifiedBadge": "..." } ], "NextCursor": "string", "HasMore": false } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://friends.roblox.com/v1/users/{USERID}/friends/find" ``` ### GET `/v1/users/{userId}/friends/inactive` Get list of inactive friends for the specified user. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user Id to get the friends for. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Friends.Api.Models.Response.UserResponse]` - `400`: 1: The target user is invalid or does not exist. 6: Invalid parameters. - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Friends.Api.Models.Response.UserResponse]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Friends.Api.Models.Response.UserResponse]](#roblox-web-webapi-models-apiarrayresponse-roblox-friends-api-models-response-userresponse-) in Models. **Response example:** ```json { "data": [ { "isOnline": "...", "presenceType": "...", "isDeleted": "...", "friendFrequentScore": "...", "friendFrequentRank": "...", "hasVerifiedBadge": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://friends.roblox.com/v1/users/{USERID}/friends/inactive" ``` ### GET `/v1/users/{userId}/friends/online` Get list of all online friends for the specified user. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user Id to get the friends for. | | `userSort` | query | `integer (int32)` | No | The sort order to return the friends. Valid values: `0`, `1`, `2` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Friends.Api.Models.Response.UserPresenceResponse]` - `400`: 1: The target user is invalid or does not exist. 6: Invalid parameters. - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Friends.Api.Models.Response.UserPresenceResponse]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Friends.Api.Models.Response.UserPresenceResponse]](#roblox-web-webapi-models-apiarrayresponse-roblox-friends-api-models-response-userpresenceresponse-) in Models. **Response example:** ```json { "data": [ { "userPresence": "...", "sortScore": "...", "id": "...", "name": "...", "displayName": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://friends.roblox.com/v1/users/{USERID}/friends/online" ``` ### GET `/v1/users/{userId}/friends/search` Search for friends by name using a text query. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user Id to get the friends for. | | `query` | query | `string` | No | The string to search names of friends for. | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `limit` | query | `integer (int32)` | No | The number of results per request. | **Responses:** - `200`: OK → `Roblox.Paging.CursoredPagedResult[Roblox.Friends.Api.Models.Response.FriendResponse]` - `400`: 1: The target user is invalid or does not exist. 6: Invalid parameters. - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Paging.CursoredPagedResult[Roblox.Friends.Api.Models.Response.FriendResponse]`) See [Roblox.Paging.CursoredPagedResult[Roblox.Friends.Api.Models.Response.FriendResponse]](#roblox-paging-cursoredpagedresult-roblox-friends-api-models-response-friendresponse-) in Models. **Response example:** ```json { "PreviousCursor": "string", "PageItems": [ { "id": "...", "hasVerifiedBadge": "..." } ], "NextCursor": "string", "HasMore": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://friends.roblox.com/v1/users/{USERID}/friends/search" ``` ### GET `/v1/users/{userId}/friends/statuses` Gets a list of friend statuses of specified users against the specified user. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user to check the friend statuses against. | | `userIds` | query | `array` | Yes | | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Friends.Api.FriendStatusResponse]` - `400`: 1: The target user is invalid or does not exist. 15: Too many ids. 16: Invalid ids. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Friends.Api.FriendStatusResponse]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Friends.Api.FriendStatusResponse]](#roblox-web-webapi-models-apiarrayresponse-roblox-friends-api-friendstatusresponse-) in Models. **Response example:** ```json { "data": [ { "id": "...", "status": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://friends.roblox.com/v1/users/{USERID}/friends/statuses?userIds={VALUE}" ``` ### GET `/v1/users/{userId}/trusted-friends/count` Get the number of trusted friends a user has **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | | **Responses:** - `200`: OK → `Roblox.Friends.Api.FriendsCountResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Friends.Api.FriendsCountResponse`) See [Roblox.Friends.Api.FriendsCountResponse](#roblox-friends-api-friendscountresponse) in Models. **Response example:** ```json { "count": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://friends.roblox.com/v1/users/{USERID}/trusted-friends/count" ``` ### POST `/v1/my/friends/refresh-qr-session` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Friends.Api.Models.Response.RefreshQrSessionResponse` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Friends.Api.Models.Response.RefreshQrSessionResponse`) See [Roblox.Friends.Api.Models.Response.RefreshQrSessionResponse](#roblox-friends-api-models-response-refreshqrsessionresponse) in Models. **Response example:** ```json { "Success": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://friends.roblox.com/v1/my/friends/refresh-qr-session" ``` ### POST `/v1/user/{userId}/multiget-are-friends` Check if the requesting user is friends with the specified users. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The requesting userId. | **Request Body:** `application/json` — Type: `Roblox.Friends.Api.MultigetAreFriendsRequestModel` See [Roblox.Friends.Api.MultigetAreFriendsRequestModel](#roblox-friends-api-multigetarefriendsrequestmodel) in Models. **Request example:** ```json { "targetUserIds": [ 0 ] } ``` **Responses:** - `200`: OK → `Roblox.Friends.Api.MultigetAreFriendsResponse` - `400`: 1: The target user is invalid or does not exist. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Friends.Api.MultigetAreFriendsResponse`) See [Roblox.Friends.Api.MultigetAreFriendsResponse](#roblox-friends-api-multigetarefriendsresponse) in Models. **Response example:** ```json { "friendsId": [ 0 ] } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://friends.roblox.com/v1/user/{USERID}/multiget-are-friends" \ -H "Content-Type: application/json" \ -d '{ "targetUserIds": [ 0 ] }' ``` ### POST `/v1/user/following-exists` Returns whether or not the current user is following each userId in a list of userIds **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Friends.Api.FollowingExistsRequestModel` See [Roblox.Friends.Api.FollowingExistsRequestModel](#roblox-friends-api-followingexistsrequestmodel) in Models. **Request example:** ```json { "targetUserIds": [ 0 ] } ``` **Responses:** - `200`: OK → `Roblox.Friends.Api.Models.Response.FollowingExistsResponseModel` - `400`: 0: An invalid userId was passed in. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `503`: 1: Followers are disabled at this time. **Response fields** (`Roblox.Friends.Api.Models.Response.FollowingExistsResponseModel`) See [Roblox.Friends.Api.Models.Response.FollowingExistsResponseModel](#roblox-friends-api-models-response-followingexistsresponsemodel) in Models. **Response example:** ```json { "followings": [ { "isFollowing": "...", "isFollowed": "...", "userId": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://friends.roblox.com/v1/user/following-exists" \ -H "Content-Type: application/json" \ -d '{ "targetUserIds": [ 0 ] }' ``` ### POST `/v1/users/{targetUserId}/follow` Creates the following between a user and user with targetUserId **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `targetUserId` | path | `integer (int64)` | Yes | | **Request Body:** `application/json` — Type: `Roblox.Web.Captcha.Models.Request.CaptchaTokenRequest` See [Roblox.Web.Captcha.Models.Request.CaptchaTokenRequest](#roblox-web-captcha-models-request-captchatokenrequest) in Models. **Request example:** ```json { "captchaId": "string", "captchaToken": "string", "captchaProvider": "string", "challengeId": "string" } ``` **Responses:** - `200`: OK → `Roblox.Friends.Api.CaptchaStatusResponseModel` - `400`: 1: The target user is invalid or does not exist. 6: Invalid parameters. 8: The user cannot follow itself. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: The user is banned from performing operation. 3: The user is blocked from performing this action. 14: The user has not passed the captcha. - `429`: 9: The flood limit has been exceeded. **Response fields** (`Roblox.Friends.Api.CaptchaStatusResponseModel`) See [Roblox.Friends.Api.CaptchaStatusResponseModel](#roblox-friends-api-captchastatusresponsemodel) in Models. **Response example:** ```json { "success": false, "isCaptchaRequired": false } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://friends.roblox.com/v1/users/{TARGETUSERID}/follow" \ -H "Content-Type: application/json" \ -d '{ "captchaId": "string", "captchaToken": "string", "captchaProvider": "string", "challengeId": "string" }' ``` ### POST `/v1/users/{targetUserId}/unfollow` Deletes the following between a user and user with targetUserId **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `targetUserId` | path | `integer (int64)` | Yes | | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: The target user is invalid or does not exist. 6: Invalid parameters. 8: The user cannot follow itself. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: The user is banned from performing operation. 3: The user is blocked from performing this action. 14: The user has not passed the captcha. - `429`: 9: The flood limit has been exceeded. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://friends.roblox.com/v1/users/{TARGETUSERID}/unfollow" ``` ## Models ### Roblox.Friends.Api.CaptchaStatusResponseModel This is response model to notify when action succeeded, failed, or captcha is required | Property | Type | Required | Description | |----------|------|----------|-------------| | `success` | `boolean` | No | | | `isCaptchaRequired` | `boolean` | No | Captcha is set to true if captcha is required from user to perform action | ### Roblox.Friends.Api.FollowCountResponse Response model for following or followers Count | Property | Type | Required | Description | |----------|------|----------|-------------| | `count` | `integer` | No | Count of following or followers | ### Roblox.Friends.Api.FollowingExistsRequestModel Request model for FollowingExists endpoint | Property | Type | Required | Description | |----------|------|----------|-------------| | `targetUserIds` | `integer[]` | No | The userIds which the user may or may not be following. | ### Roblox.Friends.Api.FriendRequest A response model representing a friend request. | Property | Type | Required | Description | |----------|------|----------|-------------| | `sentAt` | `string` | No | When the friend request was sent. | | `senderId` | `integer` | No | The sender user Id. | | `sourceUniverseId` | `integer` | No | The source universe Id which the request was sent in. | | `originSourceType` | `integer enum (12 values)` | No | The origin source type associated with the friend request. ['Unknown' = 0, 'PlayerSearch' = 1, 'QrCode' = 2, 'InGame' = 3, 'UserProfile' = 4, 'QqContactImporter' = 5, 'WeChatContactImporter' = 6, 'ProfileShare' = 7, 'PhoneContactImporter' = 8, 'FriendRecommendations' = 9, 'UserCommunities' = 10, 'TrustedFriend' = 11] Values: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 | | `contactName` | `string` | No | The contact name associated with the friend request. | | `senderNickname` | `string` | No | The nickname associated with the friend request. | ### Roblox.Friends.Api.FriendRequestResponse A response model representing a friend request. | Property | Type | Required | Description | |----------|------|----------|-------------| | `friendRequest` | `Roblox.Friends.Api.FriendRequest` | No | | | `mutualFriendsList` | `string[]` | No | mutualFriendsList | | `hasVerifiedBadge` | `boolean` | No | The user's verified badge status. | | `description` | `string` | No | The user description. | | `created` | `string` | No | When the user signed up. | | `isBanned` | `boolean` | No | Whether or not the user is banned | | `externalAppDisplayName` | `string` | No | Used when user is logged in from third party app (e.g. QQ) ExternalAppDisplayName is the name used in that app (e.g. QQ nickname) | | `id` | `integer` | No | The user Id. | | `name` | `string` | No | The user name. | | `displayName` | `string` | No | The user DisplayName. | ### Roblox.Friends.Api.FriendStatusResponse The friendship status response model. | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | The user Id of the friend. | | `status` | `0 \| 1 \| 2 \| 3` | No | The friendship status. ['NotFriends' = 0, 'Friends' = 1, 'RequestSent' = 2, 'RequestReceived' = 3] | ### Roblox.Friends.Api.FriendsCountResponse Response model for Friends Count | Property | Type | Required | Description | |----------|------|----------|-------------| | `count` | `integer` | No | Count of friends | ### Roblox.Friends.Api.Models.Response.FollowingExistsResponse Response contained in list for FollowingExists endpoint. Corresponds to a single userId. | Property | Type | Required | Description | |----------|------|----------|-------------| | `isFollowing` | `boolean` | No | Whether or not a user is following userId in FriendsController.FollowingExists | | `isFollowed` | `boolean` | No | Whether or not a user is followed by userId in FriendsController.FollowingExists | | `userId` | `integer` | No | The userId being potentially followed | ### Roblox.Friends.Api.Models.Response.FollowingExistsResponseModel Response model for FollowingExists endpoint. | Property | Type | Required | Description | |----------|------|----------|-------------| | `followings` | `Roblox.Friends.Api.Models.Response.FollowingExistsResponse[]` | No | A list of userIds and whether or not the given user is following them. | ### Roblox.Friends.Api.Models.Response.FriendResponse A response model representing friend information | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | The friend's userId | | `hasVerifiedBadge` | `boolean` | No | The friend's verified badge status. | ### Roblox.Friends.Api.Models.Response.FriendsPageMetadataResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `isFriendsFilterBarEnabled` | `boolean` | No | | | `isFriendsPageSortExperimentEnabled` | `boolean` | No | | | `isFriendsUserDataStoreCacheEnabled` | `boolean` | No | | | `frequentFriendSortRollout` | `integer` | No | | | `userName` | `string` | No | | | `displayName` | `string` | No | | ### Roblox.Friends.Api.Models.Response.NewFriendRequestsCountResponse The friendship status response model. | Property | Type | Required | Description | |----------|------|----------|-------------| | `count` | `integer` | No | Count of new friend requests. | ### Roblox.Friends.Api.Models.Response.RefreshQrSessionResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `Success` | `boolean` | No | | ### Roblox.Friends.Api.Models.Response.UserPresenceResponse A response model representing user presence information. | Property | Type | Required | Description | |----------|------|----------|-------------| | `userPresence` | `Roblox.Friends.Api.Models.Response.UserPresenceResponseModel` | No | | | `sortScore` | `number` | No | Carousel sort score | | `id` | `integer` | No | The user Id. | | `name` | `string` | No | The user name. | | `displayName` | `string` | No | The user DisplayName. | ### Roblox.Friends.Api.Models.Response.UserPresenceResponseModel Response model for !:IUserPresence objects | Property | Type | Required | Description | |----------|------|----------|-------------| | `UserPresenceType` | `string` | No | User Presence Type | | `UserLocationType` | `string` | No | Location Type | | `lastLocation` | `string` | No | Last Location | | `placeId` | `integer` | No | Place Id | | `rootPlaceId` | `integer` | No | Root Place Id | | `gameInstanceId` | `string` | No | Game Instance Id | | `universeId` | `integer` | No | Universe Id | | `lastOnline` | `string` | No | Most recent time online | ### Roblox.Friends.Api.Models.Response.UserResponse A response model representing user information that also contains select presence information | Property | Type | Required | Description | |----------|------|----------|-------------| | `isOnline` | `boolean` | No | Whether the user is online. | | `presenceType` | `0 \| 1 \| 2 \| 3 \| 4` | No | Where the user is online. | | `isDeleted` | `boolean` | No | Whether the user is deleted. | | `friendFrequentScore` | `integer` | No | Frequents value for the user. | | `friendFrequentRank` | `integer` | No | Frequents rank for the user. | | `hasVerifiedBadge` | `boolean` | No | The user's verified badge status. | | `description` | `string` | No | The user description. | | `created` | `string` | No | When the user signed up. | | `isBanned` | `boolean` | No | Whether or not the user is banned | | `externalAppDisplayName` | `string` | No | Used when user is logged in from third party app (e.g. QQ) ExternalAppDisplayName is the name used in that app (e.g. QQ nickname) | | `id` | `integer` | No | The user Id. | | `name` | `string` | No | The user name. | | `displayName` | `string` | No | The user DisplayName. | ### Roblox.Friends.Api.MultigetAreFriendsRequestModel Request model for MultigetAreFriends endpoint | Property | Type | Required | Description | |----------|------|----------|-------------| | `targetUserIds` | `integer[]` | No | The target userIds to check against with the requesting user regarding friendship. | ### Roblox.Friends.Api.MultigetAreFriendsResponse Response model for MultigetAreFriendsResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `friendsId` | `integer[]` | No | friends id list | ### Roblox.Friends.Api.PendingFriendRequestCountModel Response model for Pending Friend Request Count | Property | Type | Required | Description | |----------|------|----------|-------------| | `count` | `integer` | No | Count of pending friend requests | ### Roblox.Paging.CursoredPagedResult[Roblox.Friends.Api.Models.Response.FriendResponse] | Property | Type | Required | Description | |----------|------|----------|-------------| | `PreviousCursor` | `string` | No | | | `PageItems` | `Roblox.Friends.Api.Models.Response.FriendResponse[]` | No | | | `NextCursor` | `string` | No | | | `HasMore` | `boolean` | No | | ### Roblox.Web.Captcha.Models.Request.CaptchaTokenRequest | Property | Type | Required | Description | |----------|------|----------|-------------| | `captchaId` | `string` | No | | | `captchaToken` | `string` | No | | | `captchaProvider` | `string` | No | | | `challengeId` | `string` | No | | ### Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Friends.Api.FriendStatusResponse] | Property | Type | Required | Description | |----------|------|----------|-------------| | `data` | `Roblox.Friends.Api.FriendStatusResponse[]` | No | | ### Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Friends.Api.Models.Response.UserPresenceResponse] | Property | Type | Required | Description | |----------|------|----------|-------------| | `data` | `Roblox.Friends.Api.Models.Response.UserPresenceResponse[]` | No | | ### Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Friends.Api.Models.Response.UserResponse] | Property | Type | Required | Description | |----------|------|----------|-------------| | `data` | `Roblox.Friends.Api.Models.Response.UserResponse[]` | No | | ### Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Friends.Api.FriendRequestResponse] | Property | Type | Required | Description | |----------|------|----------|-------------| | `previousPageCursor` | `string` | No | | | `nextPageCursor` | `string` | No | | | `data` | `Roblox.Friends.Api.FriendRequestResponse[]` | No | | ### Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Friends.Api.Models.Response.UserResponse] | Property | Type | Required | Description | |----------|------|----------|-------------| | `previousPageCursor` | `string` | No | | | `nextPageCursor` | `string` | No | | | `data` | `Roblox.Friends.Api.Models.Response.UserResponse[]` | No | | --- name: "GameInternationalization Api v1" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://gameinternationalization.roblox.com" version: "v1" endpoints: 65 auth: [cookie] --- # GameInternationalization Api v1 **API Version:** v1 **Base URL:** `https://gameinternationalization.roblox.com` ## Endpoints ### GET `/v1/autolocalization/metadata` *(deprecated)* Metadata for AutoLocalization Configuration **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.AutoLocalizationMetadataResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.GameInternationalization.Api.AutoLocalizationMetadataResponse`) See [Roblox.GameInternationalization.Api.AutoLocalizationMetadataResponse](#roblox-gameinternationalization-api-autolocalizationmetadataresponse) in Models. **Response example:** ```json { "isReactVersionEnabledForAutoLocalizationSettings": false, "isTabbedUIEnabledForConfigureLocalizationPage": false, "isAutomaticTranslationToggleUIEnabled": false, "isAutomaticTranslationQuotaUIEnabled": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/autolocalization/metadata" ``` ### GET `/v1/automatic-translation/games/{gameId}/feature-status` Checks if automatic translation can be enabled for a game. The user must still have proper permissions for the game to get this info. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer (int64)` | Yes | The game id. | **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.GetAutomaticTranslationFeatureStatusForGameResponse` - `401`: 0: Authorization has been denied for this request. - `403`: 18: You do not have permission to manage this game - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.GetAutomaticTranslationFeatureStatusForGameResponse`) See [Roblox.GameInternationalization.Api.GetAutomaticTranslationFeatureStatusForGameResponse](#roblox-gameinternationalization-api-getautomatictranslationfeaturestatusforgameresponse) in Models. **Response example:** ```json { "gameId": 0, "isAutomaticTranslationAllowed": false, "isAutomaticTranslationSwitchesUIEnabled": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/automatic-translation/games/{GAMEID}/feature-status" ``` ### GET `/v1/automatic-translation/games/{gameId}/quota` Returns the automatic translation quota info for a game. The user must still have proper permissions for the game to get this info. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer (int64)` | Yes | The game id. | **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.GetAutomaticTranslationQuotaForGameResponse` - `400`: 14: Invalid game id - `401`: 0: Authorization has been denied for this request. - `403`: 18: You do not have permission to manage this game - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.GetAutomaticTranslationQuotaForGameResponse`) See [Roblox.GameInternationalization.Api.GetAutomaticTranslationQuotaForGameResponse](#roblox-gameinternationalization-api-getautomatictranslationquotaforgameresponse) in Models. **Response example:** ```json { "monthlyQuota": { "previousRefreshDate": "2024-01-01T00:00:00Z", "nextRefreshDate": "2024-01-01T00:00:00Z", "remaining": 0, "capacity": 0 }, "bankQuota": { "remaining": 0, "capacity": 0 } } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/automatic-translation/games/{GAMEID}/quota" ``` ### GET `/v1/automatic-translation/languages/{languageCode}/target-languages` Checks if the requested target languages are allowed for automatic translation based on the source language. If there are no requested target languages, then all allowed target languages for the source language will be returned. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `languageCode` | path | `string` | Yes | The source language. | | `targetLanguages` | query | `array` | No | Optional target languages. If not passed in, all allowed target languages for the source language will be returned. | | `gameId` | query | `integer (int64)` | No | Optional gameId. If not passed in, we'll return the default list of languages allowed. | **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.GetAllowedAutomaticTranslationStatusForLanguagesResponse` - `400`: 73: Maximum languages exceeded. Please keep the number of languages per request below the maximum. 74: A target language cannot be null or whitespace. - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.GetAllowedAutomaticTranslationStatusForLanguagesResponse`) See [Roblox.GameInternationalization.Api.GetAllowedAutomaticTranslationStatusForLanguagesResponse](#roblox-gameinternationalization-api-getallowedautomatictranslationstatusforlanguagesresponse) in Models. **Response example:** ```json { "sourceLanguage": "string", "targetLanguages": [ { "languageCode": "...", "isAutomaticTranslationAllowed": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/automatic-translation/languages/{LANGUAGECODE}/target-languages" ``` ### GET `/v1/badges/{badgeId}/icons` Get all icons for a badge **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `badgeId` | path | `integer (int64)` | Yes | The id of the badge | | `width` | query | `integer (int32)` | No | The width of the icon to request | | `height` | query | `integer (int32)` | No | The height of the icon to request | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.GameInternationalization.Api.GetBadgeIconResponse]` - `400`: 52: Image dimensions are invalid 62: Invalid game badge id - `401`: 0: Authorization has been denied for this request. - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.GameInternationalization.Api.GetBadgeIconResponse]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.GameInternationalization.Api.GetBadgeIconResponse]](#roblox-web-webapi-models-apiarrayresponse-roblox-gameinternationalization-api-getbadgeiconresponse-) in Models. **Response example:** ```json { "data": [ { "imageId": "...", "imageUrl": "...", "state": "...", "languageCode": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/badges/{BADGEID}/icons" ``` ### GET `/v1/badges/{badgeId}/name-description` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `badgeId` | path | `integer (int64)` | Yes | | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.GameInternationalization.Api.NameDescription]` - `400`: 62: Invalid game badge id - `401`: 0: Authorization has been denied for this request. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.GameInternationalization.Api.NameDescription]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.GameInternationalization.Api.NameDescription]](#roblox-web-webapi-models-apiarrayresponse-roblox-gameinternationalization-api-namedescription-) in Models. **Response example:** ```json { "data": [ { "name": "...", "description": "...", "updateType": "...", "languageCode": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/badges/{BADGEID}/name-description" ``` ### GET `/v1/developer-products/{developerProductId}/icons` Get all icons for a developer product **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `developerProductId` | path | `integer (int64)` | Yes | The id of the developer product | | `width` | query | `integer (int32)` | No | The width of the icon to request | | `height` | query | `integer (int32)` | No | The height of the icon to request | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.GameInternationalization.Api.GetDeveloperProductIconResponse]` - `400`: 52: Image dimensions are invalid 70: Invalid developer product id - `401`: 0: Authorization has been denied for this request. - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.GameInternationalization.Api.GetDeveloperProductIconResponse]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.GameInternationalization.Api.GetDeveloperProductIconResponse]](#roblox-web-webapi-models-apiarrayresponse-roblox-gameinternationalization-api-getdeveloperproducticonresponse-) in Models. **Response example:** ```json { "data": [ { "imageId": "...", "imageUrl": "...", "state": "...", "languageCode": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/developer-products/{DEVELOPERPRODUCTID}/icons" ``` ### GET `/v1/developer-products/{developerProductId}/name-description` Get all names and descriptions of a developer product **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `developerProductId` | path | `integer (int64)` | Yes | The developer product Id | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.GameInternationalization.Api.NameDescription]` - `400`: 70: Invalid developer product id - `401`: 0: Authorization has been denied for this request. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.GameInternationalization.Api.NameDescription]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.GameInternationalization.Api.NameDescription]](#roblox-web-webapi-models-apiarrayresponse-roblox-gameinternationalization-api-namedescription-) in Models. **Response example:** ```json { "data": [ { "name": "...", "description": "...", "updateType": "...", "languageCode": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/developer-products/{DEVELOPERPRODUCTID}/name-description" ``` ### GET `/v1/game-icon/games/{gameId}` Get all icons for a game **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer (int64)` | Yes | The id of the game | | `width` | query | `integer (int32)` | No | The width of the icon to request | | `height` | query | `integer (int32)` | No | The height of the icon to request | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.GameInternationalization.Api.GetGameIconResponse]` - `400`: 14: Invalid game id 52: Image dimensions are invalid - `401`: 0: Authorization has been denied for this request. - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.GameInternationalization.Api.GetGameIconResponse]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.GameInternationalization.Api.GetGameIconResponse]](#roblox-web-webapi-models-apiarrayresponse-roblox-gameinternationalization-api-getgameiconresponse-) in Models. **Response example:** ```json { "data": [ { "imageId": "...", "imageUrl": "...", "state": "...", "languageCode": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/game-icon/games/{GAMEID}" ``` ### GET `/v1/game-localization-status/{gameId}/translation-counts` Gets the language translation counts for all languages of a game **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer (int64)` | Yes | GameID of the game to get translation counts for | **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.GetTranslationCountsForGameResponse` - `400`: 4: Table does not exist. 14: Invalid game id - `401`: 0: Authorization has been denied for this request. - `500`: 0: An unknown error occurred. **Response fields** (`Roblox.GameInternationalization.Api.GetTranslationCountsForGameResponse`) See [Roblox.GameInternationalization.Api.GetTranslationCountsForGameResponse](#roblox-gameinternationalization-api-gettranslationcountsforgameresponse) in Models. **Response example:** ```json { "gameId": 0, "languagesOrLocales": [ { "status": "...", "categories": "...", "name": "...", "languageCodeType": "...", "languageCode": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/game-localization-status/{GAMEID}/translation-counts" ``` ### GET `/v1/game-localization-status/translation-counts-for-language-or-locale` Gets the language translation counts for the specified table. The languages to retrieve must be provided. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameIds` | query | `array` | Yes | List of game ids to retrieve translation counts for. | | `languageOrLocaleCode` | query | `string` | Yes | The code for the language or locale. | | `languageOrLocaleType` | query | `string` | Yes | Indicates whether the languageOrLocaleCode represents a language or locale. Valid values: `Language`, `Locale` | **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.GetTranslationCountsForLanguageOrLocaleResponse` - `400`: 66: Games can't be null or empty 67: Maximum games exceeded. Please keep the number of games per request below the maximum. 68: LanguageOrLocaleCode is null or whitespace - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.GameInternationalization.Api.GetTranslationCountsForLanguageOrLocaleResponse`) See [Roblox.GameInternationalization.Api.GetTranslationCountsForLanguageOrLocaleResponse](#roblox-gameinternationalization-api-gettranslationcountsforlanguageorlocaleresponse) in Models. **Response example:** ```json { "languageOrLocaleCode": "string", "languageOrLocaleType": "Language", "games": [ { "gameId": "...", "status": "...", "categories": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/game-localization-status/translation-counts-for-language-or-locale?gameIds={VALUE}&languageOrLocaleCode={VALUE}&languageOrLocaleType={VALUE}" ``` ### GET `/v1/game-passes/{gamePassId}/icons` Get all icons for a game pass **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gamePassId` | path | `integer (int64)` | Yes | The game pass id | | `width` | query | `integer (int32)` | No | The width of the icon to request | | `height` | query | `integer (int32)` | No | The height of the icon to request | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.GameInternationalization.Api.GetGamePassIconResponse]` - `400`: 52: Image dimensions are invalid 61: Invalid game pass id - `401`: 0: Authorization has been denied for this request. - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.GameInternationalization.Api.GetGamePassIconResponse]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.GameInternationalization.Api.GetGamePassIconResponse]](#roblox-web-webapi-models-apiarrayresponse-roblox-gameinternationalization-api-getgamepassiconresponse-) in Models. **Response example:** ```json { "data": [ { "imageId": "...", "imageUrl": "...", "state": "...", "languageCode": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/game-passes/{GAMEPASSID}/icons" ``` ### GET `/v1/game-passes/{gamePassId}/name-description` Get all names and descriptions of a game pass **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gamePassId` | path | `integer (int64)` | Yes | The game pass Id | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.GameInternationalization.Api.NameDescription]` - `400`: 61: Invalid game pass id - `401`: 0: Authorization has been denied for this request. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.GameInternationalization.Api.NameDescription]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.GameInternationalization.Api.NameDescription]](#roblox-web-webapi-models-apiarrayresponse-roblox-gameinternationalization-api-namedescription-) in Models. **Response example:** ```json { "data": [ { "name": "...", "description": "...", "updateType": "...", "languageCode": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/game-passes/{GAMEPASSID}/name-description" ``` ### GET `/v1/game-thumbnails/games/{gameId}/images` Get the localized image ids in all languages for a game. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer (int64)` | Yes | The game identifier. | | `width` | query | `integer (int32)` | No | The width. | | `height` | query | `integer (int32)` | No | The height. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.GameInternationalization.Api.GetGameThumbnailsResponse]` - `400`: 14: Invalid game id 52: Image dimensions are invalid - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.GameInternationalization.Api.GetGameThumbnailsResponse]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.GameInternationalization.Api.GetGameThumbnailsResponse]](#roblox-web-webapi-models-apiarrayresponse-roblox-gameinternationalization-api-getgamethumbnailsresponse-) in Models. **Response example:** ```json { "data": [ { "languageCode": "...", "mediaAssets": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/game-thumbnails/games/{GAMEID}/images" ``` ### GET `/v1/name-description/games/{gameId}` Gets a game's name and description in all supported languages **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer (int64)` | Yes | The id of the game | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.GameInternationalization.Api.NameDescription]` - `400`: 14: Invalid game id - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.GameInternationalization.Api.NameDescription]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.GameInternationalization.Api.NameDescription]](#roblox-web-webapi-models-apiarrayresponse-roblox-gameinternationalization-api-namedescription-) in Models. **Response example:** ```json { "data": [ { "name": "...", "description": "...", "updateType": "...", "languageCode": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/name-description/games/{GAMEID}" ``` ### PATCH `/v1/name-description/games/{gameId}` Updates a game's name and/or description in multiple languages. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer (int64)` | Yes | The id of the game. | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.UpdateNameDescriptionsRequest` See [Roblox.GameInternationalization.Api.UpdateNameDescriptionsRequest](#roblox-gameinternationalization-api-updatenamedescriptionsrequest) in Models. **Request example:** ```json { "data": [ { "name": "...", "description": "...", "updateType": "...", "languageCode": "..." } ] } ``` **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.UpdateNameDescriptionsResponse` - `400`: 14: Invalid game id 19: New name is null or whitespaces or new name/description is too long 20: New name or description is moderated 22: Invalid language code 23: You can't delete translations for source language 26: You can't update translations for source language 53: Language is not supported for the game. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.UpdateNameDescriptionsResponse`) See [Roblox.GameInternationalization.Api.UpdateNameDescriptionsResponse](#roblox-gameinternationalization-api-updatenamedescriptionsresponse) in Models. **Response example:** ```json { "successOperations": [ { "name": "...", "description": "...", "updateType": "...", "languageCode": "..." } ], "failedOperations": [ { "languageCode": "...", "errorCode": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/name-description/games/{GAMEID}" \ -H "Content-Type: application/json" \ -d '{ "data": [ { "name": "...", "description": "...", "updateType": "...", "languageCode": "..." } ] }' ``` ### GET `/v1/name-description/metadata` Rollout settings for name/description migration to new page **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.Models.Response.GameNameDescriptionMetadataResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.GameInternationalization.Api.Models.Response.GameNameDescriptionMetadataResponse`) See [Roblox.GameInternationalization.Api.Models.Response.GameNameDescriptionMetadataResponse](#roblox-gameinternationalization-api-models-response-gamenamedescriptionmetadataresponse) in Models. **Response example:** ```json { "isNameDescriptionMigrationEnabled": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/name-description/metadata" ``` ### GET `/v1/source-language/games/{gameId}` Gets the source language of a game **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer (int64)` | Yes | | **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.Language` - `400`: 14: Invalid game id - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.Language`) See [Roblox.GameInternationalization.Api.Language](#roblox-gameinternationalization-api-language) in Models. **Response example:** ```json { "name": "string", "nativeName": "string", "languageCode": "string" } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/source-language/games/{GAMEID}" ``` ### PATCH `/v1/source-language/games/{gameId}` Sets the source language of a game **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer (int64)` | Yes | | | `languageCode` | query | `string` | Yes | | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 14: Invalid game id 22: Invalid language code - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `500`: 85: Failed to disable automatic translation status for languages - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/source-language/games/{GAMEID}?languageCode={VALUE}" ``` ### GET `/v1/source-language/games/{gameId}/language-with-locales` Gets the source language of a game **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer (int64)` | Yes | | **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.SourceLanguageWithLocales` - `400`: 14: Invalid game id - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.SourceLanguageWithLocales`) See [Roblox.GameInternationalization.Api.SourceLanguageWithLocales](#roblox-gameinternationalization-api-sourcelanguagewithlocales) in Models. **Response example:** ```json { "languageFamily": { "name": "string", "nativeName": "string", "languageCode": "string" }, "defaultLocale": { "id": 0, "locale": "en_us", "localeCode": "string", "name": "string", "nativeName": "string", "language": { "id": "...", "name": "...", "nativeName": "...", "languageCode": "...", "isRightToLeft": "..." } }, "childLocales": [ { "id": "...", "locale": "...", "localeCode": "...", "name": "...", "nativeName": "...", "language": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/source-language/games/{GAMEID}/language-with-locales" ``` ### GET `/v1/supported-languages/games/{gameId}` Get the supported languages for a game. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer (int64)` | Yes | The id of the game. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.GameInternationalization.Api.LanguageOrLocale]` - `400`: 14: Invalid game id - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.GameInternationalization.Api.LanguageOrLocale]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.GameInternationalization.Api.LanguageOrLocale]](#roblox-web-webapi-models-apiarrayresponse-roblox-gameinternationalization-api-languageorlocale-) in Models. **Response example:** ```json { "data": [ { "name": "...", "languageCodeType": "...", "languageCode": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/supported-languages/games/{GAMEID}" ``` ### PATCH `/v1/supported-languages/games/{gameId}` Add or remove supported languages for a game. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer (int64)` | Yes | The id of the game. | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.PatchLanguage[]` See [Roblox.GameInternationalization.Api.PatchLanguage](#roblox-gameinternationalization-api-patchlanguage) in Models. **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 14: Invalid game id 22: Invalid language code 49: Duplicate language codes are not allowed. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/supported-languages/games/{GAMEID}" \ -H "Content-Type: application/json" \ -d '[ { "languageCodeType": "Language", "languageCode": "string", "delete": false } ]' ``` ### GET `/v1/supported-languages/games/{gameId}/automatic-translation-status` Get the automatic translation status of supported languages for a game. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer (int64)` | Yes | The id of the game. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.GameInternationalization.Api.LanguageOrLocaleSettings]` - `400`: 14: Invalid game id - `401`: 0: Authorization has been denied for this request. - `403`: 18: You do not have permission to manage this game - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.GameInternationalization.Api.LanguageOrLocaleSettings]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.GameInternationalization.Api.LanguageOrLocaleSettings]](#roblox-web-webapi-models-apiarrayresponse-roblox-gameinternationalization-api-languageorlocalesettings-) in Models. **Response example:** ```json { "data": [ { "languageCodeType": "...", "languageCode": "...", "isAutomaticTranslationEnabled": "...", "isImageTranslationEnabled": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/supported-languages/games/{GAMEID}/automatic-translation-status" ``` ### GET `/v1/supported-languages/games/{gameId}/in-experience-language-selection` Get the user's in-experience language selector languages for a game. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer (int64)` | Yes | The id of the game. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.GameInternationalization.Api.LanguageOrLocale]` - `400`: 14: Invalid game id - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.GameInternationalization.Api.LanguageOrLocale]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.GameInternationalization.Api.LanguageOrLocale]](#roblox-web-webapi-models-apiarrayresponse-roblox-gameinternationalization-api-languageorlocale-) in Models. **Response example:** ```json { "data": [ { "name": "...", "languageCodeType": "...", "languageCode": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/supported-languages/games/{GAMEID}/in-experience-language-selection" ``` ### GET `/v1/supported-languages/games/{gameId}/universe-display-info-automatic-translation-settings` Get UniverseDisplayInfo automatic translation settings. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer (int64)` | Yes | The game id. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.GameInternationalization.Api.UniverseDisplayInfoAutomaticTranslationSettings]` - `400`: 14: Invalid game id - `401`: 0: Authorization has been denied for this request. - `403`: 18: You do not have permission to manage this game - `500`: 0: An unknown error occurred. 22: Invalid language code 83: Failed to get UniverseDisplayInformation content instance auto translation settings 84: Count of language code is larger than max batch get size **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.GameInternationalization.Api.UniverseDisplayInfoAutomaticTranslationSettings]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.GameInternationalization.Api.UniverseDisplayInfoAutomaticTranslationSettings]](#roblox-web-webapi-models-apiarrayresponse-roblox-gameinternationalization-api-universedisplayinfoautomatictranslationsettings-) in Models. **Response example:** ```json { "data": [ { "languageCode": "...", "isUniverseDisplayInfoAutomaticTranslationEnabled": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/supported-languages/games/{GAMEID}/universe-display-info-automatic-translation-settings" ``` ### GET `/v1/supported-languages/metadata` Rollout settings for supported languages of a game **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.SupportedLanguagesMetadataResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.GameInternationalization.Api.SupportedLanguagesMetadataResponse`) See [Roblox.GameInternationalization.Api.SupportedLanguagesMetadataResponse](#roblox-gameinternationalization-api-supportedlanguagesmetadataresponse) in Models. **Response example:** ```json { "isFeatureEnabled": false, "areAllLanguagesEnabled": false, "minimumUniverseIdForFeature": 0, "isHumanTranslationProgressUIEnabled": false, "isAutomaticTranslationProgressUIEnabled": false, "isSupportedLanguagesChildLocalesUIEnabled": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/supported-languages/metadata" ``` ### GET `/v1/translation-analytics/games/{gameId}/download-translation-analytics-report` Download translation analytics report after the report is ready **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer (int64)` | Yes | The game's id | | `startDateTime` | query | `string (date-time)` | Yes | The inclusive start dateTime of report in UTC | | `endDateTime` | query | `string (date-time)` | Yes | The exclusive end dateTime of report in UTC | | `reportType` | query | `string` | Yes | The report type Valid values: `GameTranslationStatus`, `GameTranslationStatusForTranslator`, `GameTranslationStatusForTranslatorGroup`, `Test` | | `reportSubjectTargetId` | query | `integer (int64)` | Yes | The translator group id | **Responses:** - `200`: OK → `object` - `400`: 14: Invalid game id 56: You need to provide a valid translator group id to get report. 58: Start datetime or end datetime is invlaid. 59: Report type is invalid - `401`: 0: Authorization has been denied for this request. - `403`: 57: You do not have permission to request translation analytics report. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/translation-analytics/games/{GAMEID}/download-translation-analytics-report?startDateTime={VALUE}&endDateTime={VALUE}&reportType={VALUE}&reportSubjectTargetId={VALUE}" ``` ### GET `/v1/translation-analytics/metadata` Get metadata related to UI and rollout settings **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.TranslationAnalyticsMetadataResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.GameInternationalization.Api.TranslationAnalyticsMetadataResponse`) See [Roblox.GameInternationalization.Api.TranslationAnalyticsMetadataResponse](#roblox-gameinternationalization-api-translationanalyticsmetadataresponse) in Models. **Response example:** ```json { "isFeatureEnabledOnUI": false, "reportRequestPollingIntervalSeconds": 0, "minimumDateTimeForAnalyticsReport": "2024-01-01T00:00:00Z" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/translation-analytics/metadata" ``` ### GET `/v1/ui-configurations` Get ui configurations for frontend to use. **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.GetUiConfigurationsResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.GameInternationalization.Api.GetUiConfigurationsResponse`) See [Roblox.GameInternationalization.Api.GetUiConfigurationsResponse](#roblox-gameinternationalization-api-getuiconfigurationsresponse) in Models. **Response example:** ```json { "isGameProductsEnabled": false, "isBadgeIconEnabled": false, "isGamePassEnabled": false, "isDeveloperProductEnabled": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/ui-configurations" ``` ### GET `/v1/user-localization-settings/player-choice/{universeId}` Get user player choice settings for universe. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer (int64)` | Yes | The universe's ID. | **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.Models.Response.GetPlayerChoiceUniverseSettingsResponse` - `400`: 14: Invalid game id - `401`: 0: Authorization has been denied for this request. - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.Models.Response.GetPlayerChoiceUniverseSettingsResponse`) See [Roblox.GameInternationalization.Api.Models.Response.GetPlayerChoiceUniverseSettingsResponse](#roblox-gameinternationalization-api-models-response-getplayerchoiceuniversesettingsresponse) in Models. **Response example:** ```json { "IsPlayerChoiceEnabled": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/user-localization-settings/player-choice/{UNIVERSEID}" ``` ### GET `/v1/user-localization-settings/universe/{universeId}` Get user localization settings for universe. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer (int64)` | Yes | The universe's ID. | **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.Models.Response.GetUserLocalizationSettingsForUniverseResponse` - `400`: 14: Invalid game id 21: The language is not supported 22: Invalid language code - `401`: 0: Authorization has been denied for this request. - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.Models.Response.GetUserLocalizationSettingsForUniverseResponse`) See [Roblox.GameInternationalization.Api.Models.Response.GetUserLocalizationSettingsForUniverseResponse](#roblox-gameinternationalization-api-models-response-getuserlocalizationsettingsforuniverseresponse) in Models. **Response example:** ```json { "userUniverseLocalizationSettingValue": { "settingType": "LanguageFamily", "settingTargetId": 0, "settingTargetCode": "string" } } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/user-localization-settings/universe/{UNIVERSEID}" ``` ### POST `/v1/user-localization-settings/universe/{universeId}` Set user localization settings for universe. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer (int64)` | Yes | The universe's ID. | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.Models.Request.SetUserLocalizationSettingsRequest` See [Roblox.GameInternationalization.Api.Models.Request.SetUserLocalizationSettingsRequest](#roblox-gameinternationalization-api-models-request-setuserlocalizationsettingsrequest) in Models. **Request example:** ```json { "settingValue": { "settingType": "LanguageFamily", "settingTargetId": 0, "settingTargetCode": "string" } } ``` **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.Models.Response.SetUserLocalizationSettingsResponse` - `400`: 14: Invalid game id 22: Invalid language code - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.Models.Response.SetUserLocalizationSettingsResponse`) See [Roblox.GameInternationalization.Api.Models.Response.SetUserLocalizationSettingsResponse](#roblox-gameinternationalization-api-models-response-setuserlocalizationsettingsresponse) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/user-localization-settings/universe/{UNIVERSEID}" \ -H "Content-Type: application/json" \ -d '{ "settingValue": { "settingType": "LanguageFamily", "settingTargetId": 0, "settingTargetCode": "string" } }' ``` ### POST `/v1/autolocalization/games/{gameId}/autolocalizationtable` *(deprecated)* **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer (int64)` | Yes | | **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.GameAutolocalizationInformationResponse` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.GameInternationalization.Api.GameAutolocalizationInformationResponse`) See [Roblox.GameInternationalization.Api.GameAutolocalizationInformationResponse](#roblox-gameinternationalization-api-gameautolocalizationinformationresponse) in Models. **Response example:** ```json { "isAutolocalizationEnabled": false, "shouldUseLocalizationTable": false, "autoLocalizationTableId": "string", "assetId": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/autolocalization/games/{GAMEID}/autolocalizationtable" ``` ### PATCH `/v1/autolocalization/games/{gameId}/autolocalizationtable` *(deprecated)* **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer (int64)` | Yes | | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.SetAutolocalizationTableForGameRequest` See [Roblox.GameInternationalization.Api.SetAutolocalizationTableForGameRequest](#roblox-gameinternationalization-api-setautolocalizationtableforgamerequest) in Models. **Request example:** ```json { "tableId": "string" } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/autolocalization/games/{GAMEID}/autolocalizationtable" \ -H "Content-Type: application/json" \ -d '{ "tableId": "string" }' ``` ### POST `/v1/badges/{badgeId}/icons/language-codes/{languageCode}` Update a badge's icon **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `badgeId` | path | `integer (int64)` | Yes | The id of the badge | | `languageCode` | path | `string` | Yes | The language code of this icon to update | | `Files` | formData | `file` | No | | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 22: Invalid language code 26: You can't update translations for source language 45: File uploaded does not match known image format 46: File not present in request 53: Language is not supported for the game. 62: Invalid game badge id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `429`: 24: Too many attempts.Please try again later. - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/badges/{BADGEID}/icons/language-codes/{LANGUAGECODE}" ``` ### DELETE `/v1/badges/{badgeId}/icons/language-codes/{languageCode}` Delete a localized icon from a badge **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `badgeId` | path | `integer (int64)` | Yes | The id of the badge | | `languageCode` | path | `string` | Yes | The language code of the localized icon to delete | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 22: Invalid language code 23: You can't delete translations for source language 53: Language is not supported for the game. 62: Invalid game badge id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/badges/{BADGEID}/icons/language-codes/{LANGUAGECODE}" ``` ### POST `/v1/developer-products/{developerProductId}/icons/language-codes/{languageCode}` Update a developer product's icon **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `developerProductId` | path | `integer (int64)` | Yes | The id of the developer product | | `languageCode` | path | `string` | Yes | The language code of this icon to update | | `Files` | formData | `file` | No | | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 22: Invalid language code 26: You can't update translations for source language 45: File uploaded does not match known image format 46: File not present in request 53: Language is not supported for the game. 70: Invalid developer product id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `429`: 24: Too many attempts.Please try again later. - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/developer-products/{DEVELOPERPRODUCTID}/icons/language-codes/{LANGUAGECODE}" ``` ### DELETE `/v1/developer-products/{developerProductId}/icons/language-codes/{languageCode}` Delete a localized icon from a developer product **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `developerProductId` | path | `integer (int64)` | Yes | The id of the developer product | | `languageCode` | path | `string` | Yes | The language code of the localized icon to delete | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 22: Invalid language code 23: You can't delete translations for source language 53: Language is not supported for the game. 70: Invalid developer product id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/developer-products/{DEVELOPERPRODUCTID}/icons/language-codes/{LANGUAGECODE}" ``` ### POST `/v1/game-icon/games/{gameId}/language-codes/{languageCode}` Update a game's icon **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer (int64)` | Yes | The id of the game | | `languageCode` | path | `string` | Yes | The language code of this icon to update | | `Files` | formData | `file` | No | | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 14: Invalid game id 22: Invalid language code 26: You can't update translations for source language 45: File uploaded does not match known image format 46: File not present in request 53: Language is not supported for the game. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `429`: 24: Too many attempts.Please try again later. - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/game-icon/games/{GAMEID}/language-codes/{LANGUAGECODE}" ``` ### DELETE `/v1/game-icon/games/{gameId}/language-codes/{languageCode}` Delete a localized icon from a game **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer (int64)` | Yes | The id of the game | | `languageCode` | path | `string` | Yes | The language code of the localized icon to delete | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 14: Invalid game id 22: Invalid language code 23: You can't delete translations for source language 53: Language is not supported for the game. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/game-icon/games/{GAMEID}/language-codes/{LANGUAGECODE}" ``` ### POST `/v1/game-passes/{gamePassId}/icons/language-codes/{languageCode}` Update a game pass's icon **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gamePassId` | path | `integer (int64)` | Yes | The game pass id | | `languageCode` | path | `string` | Yes | The language code of this icon to update | | `Files` | formData | `file` | No | | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 22: Invalid language code 26: You can't update translations for source language 45: File uploaded does not match known image format 46: File not present in request 53: Language is not supported for the game. 61: Invalid game pass id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `429`: 24: Too many attempts.Please try again later. - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/game-passes/{GAMEPASSID}/icons/language-codes/{LANGUAGECODE}" ``` ### DELETE `/v1/game-passes/{gamePassId}/icons/language-codes/{languageCode}` Delete a localized icon from a game pass **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gamePassId` | path | `integer (int64)` | Yes | The game pass id | | `languageCode` | path | `string` | Yes | The language code of the localized icon to delete | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 22: Invalid language code 23: You can't delete translations for source language 53: Language is not supported for the game. 61: Invalid game pass id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/game-passes/{GAMEPASSID}/icons/language-codes/{LANGUAGECODE}" ``` ### POST `/v1/game-thumbnails/games/{gameId}/language-codes/{languageCode}/alt-text` Updates the game thumbnail alt text. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer (int64)` | Yes | The game identifier. | | `languageCode` | path | `string` | Yes | The language code. | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.UpdateThumbnailAltTextRequest` See [Roblox.GameInternationalization.Api.UpdateThumbnailAltTextRequest](#roblox-gameinternationalization-api-updatethumbnailalttextrequest) in Models. **Request example:** ```json { "thumbnailId": 0, "altText": "string" } ``` **Responses:** - `200`: OK → `string` - `400`: 14: Invalid game id 19: New name is null or whitespaces or new name/description is too long 20: New name or description is moderated 22: Invalid language code 26: You can't update translations for source language 45: File uploaded does not match known image format 53: Language is not supported for the game. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `429`: 24: Too many attempts.Please try again later. - `500`: 0: An unknown error occurred. 88: Failed to filter text - `503`: 17: Feature is disabled **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/game-thumbnails/games/{GAMEID}/language-codes/{LANGUAGECODE}/alt-text" \ -H "Content-Type: application/json" \ -d '{ "thumbnailId": 0, "altText": "string" }' ``` ### POST `/v1/game-thumbnails/games/{gameId}/language-codes/{languageCode}/image` Uploads the game thumbnail. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer (int64)` | Yes | The game identifier. | | `languageCode` | path | `string` | Yes | The language code. | | `Files` | formData | `file` | No | | **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.Models.Response.UploadImageForGameThumbnailResponse` - `400`: 14: Invalid game id 22: Invalid language code 26: You can't update translations for source language 53: Language is not supported for the game. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `429`: 24: Too many attempts.Please try again later. - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.Models.Response.UploadImageForGameThumbnailResponse`) See [Roblox.GameInternationalization.Api.Models.Response.UploadImageForGameThumbnailResponse](#roblox-gameinternationalization-api-models-response-uploadimageforgamethumbnailresponse) in Models. **Response example:** ```json { "mediaAssetId": "string" } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/game-thumbnails/games/{GAMEID}/language-codes/{LANGUAGECODE}/image" ``` ### POST `/v1/game-thumbnails/games/{gameId}/language-codes/{languageCode}/images/order` Orders the specified image Ids for the game thumbnails. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer (int64)` | Yes | The game identifier. | | `languageCode` | path | `string` | Yes | The language code. | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.SortImageIdsRequest` See [Roblox.GameInternationalization.Api.SortImageIdsRequest](#roblox-gameinternationalization-api-sortimageidsrequest) in Models. **Request example:** ```json { "mediaAssetIds": [ 0 ] } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 14: Invalid game id 22: Invalid language code 26: You can't update translations for source language 53: Language is not supported for the game. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/game-thumbnails/games/{GAMEID}/language-codes/{LANGUAGECODE}/images/order" \ -H "Content-Type: application/json" \ -d '{ "mediaAssetIds": [ 0 ] }' ``` ### POST `/v1/name-description/games/translation-history` Gets the history for name or description in a provided language. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.GetNameDescriptionHistoryV2Request` See [Roblox.GameInternationalization.Api.GetNameDescriptionHistoryV2Request](#roblox-gameinternationalization-api-getnamedescriptionhistoryv2request) in Models. **Request example:** ```json { "contentId": 0, "contentType": "UniverseDisplayNames", "languageCode": "string", "cursor": "string", "count": 0, "sortOrder": "Asc" } ``` **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.GetNameDescriptionHistoryResponse` - `400`: 13: Request body can't be null 14: Invalid game id 18: You do not have permission to manage this game 22: Invalid language code 39: Count should be at least 1 and less than 50. 53: Language is not supported for the game. 54: No history available for source data 55: Invalid exclusive start Id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.GetNameDescriptionHistoryResponse`) See [Roblox.GameInternationalization.Api.GetNameDescriptionHistoryResponse](#roblox-gameinternationalization-api-getnamedescriptionhistoryresponse) in Models. **Response example:** ```json { "history": [ { "translationText": "...", "translator": "...", "created": "..." } ], "lastEvaluatedId": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/name-description/games/translation-history" \ -H "Content-Type: application/json" \ -d '{ "contentId": 0, "contentType": "UniverseDisplayNames", "languageCode": "string", "cursor": "string", "count": 0, "sortOrder": "Asc" }' ``` ### POST `/v1/translation-analytics/games/{gameId}/request-translation-analytics-report` Request translation analytics report to be generated **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer (int64)` | Yes | The game's id | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.RequestTranslationAnalyticsReportRequest` See [Roblox.GameInternationalization.Api.RequestTranslationAnalyticsReportRequest](#roblox-gameinternationalization-api-requesttranslationanalyticsreportrequest) in Models. **Request example:** ```json { "startDateTime": "2024-01-01T00:00:00Z", "endDateTime": "2024-01-01T00:00:00Z", "reportType": "GameTranslationStatus", "reportSubjectTargetId": 0 } ``` **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.RequestTranslationAnalyticsReportResponse` - `400`: 14: Invalid game id 56: You need to provide a valid translator group id to get report. 58: Start datetime or end datetime is invlaid. 59: Report type is invalid - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 57: You do not have permission to request translation analytics report. **Response fields** (`Roblox.GameInternationalization.Api.RequestTranslationAnalyticsReportResponse`) See [Roblox.GameInternationalization.Api.RequestTranslationAnalyticsReportResponse](#roblox-gameinternationalization-api-requesttranslationanalyticsreportresponse) in Models. **Response example:** ```json { "reportGenerationStatus": "inProgress" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/translation-analytics/games/{GAMEID}/request-translation-analytics-report" \ -H "Content-Type: application/json" \ -d '{ "startDateTime": "2024-01-01T00:00:00Z", "endDateTime": "2024-01-01T00:00:00Z", "reportType": "GameTranslationStatus", "reportSubjectTargetId": 0 }' ``` ### PATCH `/v1/autolocalization/games/{gameId}/settings` *(deprecated)* Sets a game's auto-localization related settings **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer (int64)` | Yes | The id of the game. | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.SetAutolocalizationSettingsForGameRequest` See [Roblox.GameInternationalization.Api.SetAutolocalizationSettingsForGameRequest](#roblox-gameinternationalization-api-setautolocalizationsettingsforgamerequest) in Models. **Request example:** ```json { "isAutolocalizationEnabled": false, "shouldUseLocalizationTable": false } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 14: Invalid game id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/autolocalization/games/{GAMEID}/settings" \ -H "Content-Type: application/json" \ -d '{ "isAutolocalizationEnabled": false, "shouldUseLocalizationTable": false }' ``` ### PATCH `/v1/badges/{badgeId}/description/language-codes/{languageCode}` Update localized description of a badge **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `badgeId` | path | `integer (int64)` | Yes | The badge id | | `languageCode` | path | `string` | Yes | The language code of the description to update | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.UpdateBadgeDescriptionRequest` See [Roblox.GameInternationalization.Api.UpdateBadgeDescriptionRequest](#roblox-gameinternationalization-api-updatebadgedescriptionrequest) in Models. **Request example:** ```json { "description": "string" } ``` **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.UpdateBadgeDescriptionResponse` - `400`: 13: Request body can't be null 19: New name is null or whitespaces or new name/description is too long 20: New name or description is moderated 22: Invalid language code 26: You can't update translations for source language 53: Language is not supported for the game. 62: Invalid game badge id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.UpdateBadgeDescriptionResponse`) See [Roblox.GameInternationalization.Api.UpdateBadgeDescriptionResponse](#roblox-gameinternationalization-api-updatebadgedescriptionresponse) in Models. **Response example:** ```json { "description": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/badges/{BADGEID}/description/language-codes/{LANGUAGECODE}" \ -H "Content-Type: application/json" \ -d '{ "description": "string" }' ``` ### PATCH `/v1/badges/{badgeId}/name-description/language-codes/{languageCode}` Update localized name and description of a badge **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `badgeId` | path | `integer (int64)` | Yes | The badge id | | `languageCode` | path | `string` | Yes | The language code of the name and description to Update | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.UpdateBadgeNameDescriptionRequest` See [Roblox.GameInternationalization.Api.UpdateBadgeNameDescriptionRequest](#roblox-gameinternationalization-api-updatebadgenamedescriptionrequest) in Models. **Request example:** ```json { "name": "string", "description": "string" } ``` **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.UpdateBadgeNameDescriptionResponse` - `400`: 13: Request body can't be null 19: New name is null or whitespaces or new name/description is too long 20: New name or description is moderated 22: Invalid language code 26: You can't update translations for source language 53: Language is not supported for the game. 62: Invalid game badge id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.UpdateBadgeNameDescriptionResponse`) See [Roblox.GameInternationalization.Api.UpdateBadgeNameDescriptionResponse](#roblox-gameinternationalization-api-updatebadgenamedescriptionresponse) in Models. **Response example:** ```json { "name": "string", "description": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/badges/{BADGEID}/name-description/language-codes/{LANGUAGECODE}" \ -H "Content-Type: application/json" \ -d '{ "name": "string", "description": "string" }' ``` ### DELETE `/v1/badges/{badgeId}/name-description/language-codes/{languageCode}` Delete localized name and description of a badge **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `badgeId` | path | `integer (int64)` | Yes | The badge id | | `languageCode` | path | `string` | Yes | The language code of the name and description to delete | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 22: Invalid language code 23: You can't delete translations for source language 53: Language is not supported for the game. 62: Invalid game badge id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/badges/{BADGEID}/name-description/language-codes/{LANGUAGECODE}" ``` ### PATCH `/v1/badges/{badgeId}/name/language-codes/{languageCode}` Update localized name of a badge **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `badgeId` | path | `integer (int64)` | Yes | The badge id | | `languageCode` | path | `string` | Yes | The language code of the name to update | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.UpdateBadgeNameRequest` See [Roblox.GameInternationalization.Api.UpdateBadgeNameRequest](#roblox-gameinternationalization-api-updatebadgenamerequest) in Models. **Request example:** ```json { "name": "string" } ``` **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.UpdateBadgeNameResponse` - `400`: 13: Request body can't be null 19: New name is null or whitespaces or new name/description is too long 20: New name or description is moderated 22: Invalid language code 26: You can't update translations for source language 53: Language is not supported for the game. 62: Invalid game badge id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.UpdateBadgeNameResponse`) See [Roblox.GameInternationalization.Api.UpdateBadgeNameResponse](#roblox-gameinternationalization-api-updatebadgenameresponse) in Models. **Response example:** ```json { "name": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/badges/{BADGEID}/name/language-codes/{LANGUAGECODE}" \ -H "Content-Type: application/json" \ -d '{ "name": "string" }' ``` ### PATCH `/v1/developer-products/{developerProductId}/description/language-codes/{languageCode}` Update localized description of a developer product **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `developerProductId` | path | `integer (int64)` | Yes | The developer product id | | `languageCode` | path | `string` | Yes | The language code of the description to update | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.UpdateDeveloperProductDescriptionRequest` See [Roblox.GameInternationalization.Api.UpdateDeveloperProductDescriptionRequest](#roblox-gameinternationalization-api-updatedeveloperproductdescriptionrequest) in Models. **Request example:** ```json { "description": "string" } ``` **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.UpdateDeveloperProductDescriptionResponse` - `400`: 13: Request body can't be null 19: New name is null or whitespaces or new name/description is too long 20: New name or description is moderated 22: Invalid language code 26: You can't update translations for source language 53: Language is not supported for the game. 70: Invalid developer product id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.UpdateDeveloperProductDescriptionResponse`) See [Roblox.GameInternationalization.Api.UpdateDeveloperProductDescriptionResponse](#roblox-gameinternationalization-api-updatedeveloperproductdescriptionresponse) in Models. **Response example:** ```json { "description": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/developer-products/{DEVELOPERPRODUCTID}/description/language-codes/{LANGUAGECODE}" \ -H "Content-Type: application/json" \ -d '{ "description": "string" }' ``` ### PATCH `/v1/developer-products/{developerProductId}/name-description/language-codes/{languageCode}` Update localized name and description of a developer product **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `developerProductId` | path | `integer (int64)` | Yes | The developer product id | | `languageCode` | path | `string` | Yes | The language code of the name and description to Update | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.UpdateDeveloperProductNameDescriptionRequest` See [Roblox.GameInternationalization.Api.UpdateDeveloperProductNameDescriptionRequest](#roblox-gameinternationalization-api-updatedeveloperproductnamedescriptionrequest) in Models. **Request example:** ```json { "name": "string", "description": "string" } ``` **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.UpdateDeveloperProductNameDescriptionResponse` - `400`: 13: Request body can't be null 19: New name is null or whitespaces or new name/description is too long 20: New name or description is moderated 22: Invalid language code 26: You can't update translations for source language 53: Language is not supported for the game. 70: Invalid developer product id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.UpdateDeveloperProductNameDescriptionResponse`) See [Roblox.GameInternationalization.Api.UpdateDeveloperProductNameDescriptionResponse](#roblox-gameinternationalization-api-updatedeveloperproductnamedescriptionresponse) in Models. **Response example:** ```json { "name": "string", "description": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/developer-products/{DEVELOPERPRODUCTID}/name-description/language-codes/{LANGUAGECODE}" \ -H "Content-Type: application/json" \ -d '{ "name": "string", "description": "string" }' ``` ### DELETE `/v1/developer-products/{developerProductId}/name-description/language-codes/{languageCode}` Delete localized name and description of a developer product **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `developerProductId` | path | `integer (int64)` | Yes | The developer product id | | `languageCode` | path | `string` | Yes | The language code of the name and description to delete | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 22: Invalid language code 23: You can't delete translations for source language 53: Language is not supported for the game. 70: Invalid developer product id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/developer-products/{DEVELOPERPRODUCTID}/name-description/language-codes/{LANGUAGECODE}" ``` ### PATCH `/v1/developer-products/{developerProductId}/name/language-codes/{languageCode}` Update localized name of a developer product **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `developerProductId` | path | `integer (int64)` | Yes | The developer product id | | `languageCode` | path | `string` | Yes | The language code of the name to update | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.UpdateDeveloperProductNameRequest` See [Roblox.GameInternationalization.Api.UpdateDeveloperProductNameRequest](#roblox-gameinternationalization-api-updatedeveloperproductnamerequest) in Models. **Request example:** ```json { "name": "string" } ``` **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.UpdateDeveloperProductNameResponse` - `400`: 13: Request body can't be null 19: New name is null or whitespaces or new name/description is too long 20: New name or description is moderated 22: Invalid language code 26: You can't update translations for source language 53: Language is not supported for the game. 70: Invalid developer product id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `500`: 0: An unknown error occurred. - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.UpdateDeveloperProductNameResponse`) See [Roblox.GameInternationalization.Api.UpdateDeveloperProductNameResponse](#roblox-gameinternationalization-api-updatedeveloperproductnameresponse) in Models. **Response example:** ```json { "name": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/developer-products/{DEVELOPERPRODUCTID}/name/language-codes/{LANGUAGECODE}" \ -H "Content-Type: application/json" \ -d '{ "name": "string" }' ``` ### PATCH `/v1/game-passes/{gamePassId}/description/language-codes/{languageCode}` Update localized description of a game pass **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gamePassId` | path | `integer (int64)` | Yes | The game pass id | | `languageCode` | path | `string` | Yes | The language code of description to update | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.UpdateGamePassDescriptionRequest` See [Roblox.GameInternationalization.Api.UpdateGamePassDescriptionRequest](#roblox-gameinternationalization-api-updategamepassdescriptionrequest) in Models. **Request example:** ```json { "description": "string" } ``` **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.UpdateGamePassDescriptionResponse` - `400`: 13: Request body can't be null 19: New name is null or whitespaces or new name/description is too long 20: New name or description is moderated 22: Invalid language code 26: You can't update translations for source language 53: Language is not supported for the game. 61: Invalid game pass id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.UpdateGamePassDescriptionResponse`) See [Roblox.GameInternationalization.Api.UpdateGamePassDescriptionResponse](#roblox-gameinternationalization-api-updategamepassdescriptionresponse) in Models. **Response example:** ```json { "description": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/game-passes/{GAMEPASSID}/description/language-codes/{LANGUAGECODE}" \ -H "Content-Type: application/json" \ -d '{ "description": "string" }' ``` ### PATCH `/v1/game-passes/{gamePassId}/name-description/language-codes/{languageCode}` Update localized name and description of a game pass **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gamePassId` | path | `integer (int64)` | Yes | The game pass id | | `languageCode` | path | `string` | Yes | The language code of the name/description to update | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.UpdateGamePassNameDescriptionRequest` See [Roblox.GameInternationalization.Api.UpdateGamePassNameDescriptionRequest](#roblox-gameinternationalization-api-updategamepassnamedescriptionrequest) in Models. **Request example:** ```json { "name": "string", "description": "string" } ``` **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.UpdateGamePassNameDescriptionResponse` - `400`: 13: Request body can't be null 19: New name is null or whitespaces or new name/description is too long 20: New name or description is moderated 22: Invalid language code 26: You can't update translations for source language 53: Language is not supported for the game. 61: Invalid game pass id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.UpdateGamePassNameDescriptionResponse`) See [Roblox.GameInternationalization.Api.UpdateGamePassNameDescriptionResponse](#roblox-gameinternationalization-api-updategamepassnamedescriptionresponse) in Models. **Response example:** ```json { "name": "string", "description": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/game-passes/{GAMEPASSID}/name-description/language-codes/{LANGUAGECODE}" \ -H "Content-Type: application/json" \ -d '{ "name": "string", "description": "string" }' ``` ### DELETE `/v1/game-passes/{gamePassId}/name-description/language-codes/{languageCode}` Delete localized name and description of a game pass **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gamePassId` | path | `integer (int64)` | Yes | The game pass id | | `languageCode` | path | `string` | Yes | The language code of the name and description to delete | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 22: Invalid language code 23: You can't delete translations for source language 53: Language is not supported for the game. 61: Invalid game pass id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/game-passes/{GAMEPASSID}/name-description/language-codes/{LANGUAGECODE}" ``` ### PATCH `/v1/game-passes/{gamePassId}/name/language-codes/{languageCode}` Update localized name of a game pass **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gamePassId` | path | `integer (int64)` | Yes | The game pass id | | `languageCode` | path | `string` | Yes | The language code of the name to update | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.UpdateGamePassNameRequest` See [Roblox.GameInternationalization.Api.UpdateGamePassNameRequest](#roblox-gameinternationalization-api-updategamepassnamerequest) in Models. **Request example:** ```json { "name": "string" } ``` **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.UpdateGamePassNameResponse` - `400`: 13: Request body can't be null 19: New name is null or whitespaces or new name/description is too long 20: New name or description is moderated 22: Invalid language code 26: You can't update translations for source language 53: Language is not supported for the game. 61: Invalid game pass id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.UpdateGamePassNameResponse`) See [Roblox.GameInternationalization.Api.UpdateGamePassNameResponse](#roblox-gameinternationalization-api-updategamepassnameresponse) in Models. **Response example:** ```json { "name": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/game-passes/{GAMEPASSID}/name/language-codes/{LANGUAGECODE}" \ -H "Content-Type: application/json" \ -d '{ "name": "string" }' ``` ### PATCH `/v1/localizationtable/gametables/{gameId}` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer (int64)` | Yes | | **Request Body:** `application/json` — Type: `Roblox.GameInternationalization.Api.AssociateLocalizationTablesToGameRequest` See [Roblox.GameInternationalization.Api.AssociateLocalizationTablesToGameRequest](#roblox-gameinternationalization-api-associatelocalizationtablestogamerequest) in Models. **Request example:** ```json { "tables": [ { "id": "...", "dissociate": "..." } ] } ``` **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.AssociateLocalizationTablesToGameResponse` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.GameInternationalization.Api.AssociateLocalizationTablesToGameResponse`) See [Roblox.GameInternationalization.Api.AssociateLocalizationTablesToGameResponse](#roblox-gameinternationalization-api-associatelocalizationtablestogameresponse) in Models. **Response example:** ```json { "success": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/localizationtable/gametables/{GAMEID}" \ -H "Content-Type: application/json" \ -d '{ "tables": [ { "id": "...", "dissociate": "..." } ] }' ``` ### PATCH `/v1/supported-languages/games/{gameId}/languages/{languageCode}/automatic-translation-status` Enable or disable automatic translation for a game and language. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer (int64)` | Yes | The id of the game. | | `languageCode` | path | `string` | Yes | The language to enable or disable for automatic translation. | **Request Body:** `application/json` — Type: `boolean` **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.EditAutomaticTranslationStatusForGameAndLanguageResponse` - `400`: 14: Invalid game id 22: Invalid language code 53: Language is not supported for the game. 72: Automatic translation cannot be enabled for game. 75: Automatic translation cannot be enabled for language. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.EditAutomaticTranslationStatusForGameAndLanguageResponse`) See [Roblox.GameInternationalization.Api.EditAutomaticTranslationStatusForGameAndLanguageResponse](#roblox-gameinternationalization-api-editautomatictranslationstatusforgameandlanguageresponse) in Models. **Response example:** ```json { "gameId": 0, "languageCode": "string", "isAutomaticTranslationEnabled": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/supported-languages/games/{GAMEID}/languages/{LANGUAGECODE}/automatic-translation-status" \ -H "Content-Type: application/json" \ -d 'false' ``` ### PATCH `/v1/supported-languages/games/{gameId}/languages/{languageCode}/image-translation-status` Enable or disable image translation for a game and language. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer (int64)` | Yes | The id of the game. | | `languageCode` | path | `string` | Yes | The language to enable or disable for image translation. | **Request Body:** `application/json` — Type: `boolean` **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.EditImageTranslationStatusForGameAndLanguageResponse` - `400`: 14: Invalid game id 22: Invalid language code 53: Language is not supported for the game. 93: Image translation cannot be enabled for language. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `503`: 17: Feature is disabled **Response fields** (`Roblox.GameInternationalization.Api.EditImageTranslationStatusForGameAndLanguageResponse`) See [Roblox.GameInternationalization.Api.EditImageTranslationStatusForGameAndLanguageResponse](#roblox-gameinternationalization-api-editimagetranslationstatusforgameandlanguageresponse) in Models. **Response example:** ```json { "gameId": 0, "languageCode": "string", "isImageTranslationEnabled": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/supported-languages/games/{GAMEID}/languages/{LANGUAGECODE}/image-translation-status" \ -H "Content-Type: application/json" \ -d 'false' ``` ### PATCH `/v1/supported-languages/games/{gameId}/languages/{languageCode}/universe-display-info-automatic-translation-settings` Update the switch which controls if the UniverseDisplayInformation should be automatically translated. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer (int64)` | Yes | The game id. | | `languageCode` | path | `string` | Yes | The language code. | **Request Body:** `application/json` — Type: `boolean` **Responses:** - `200`: OK → `Roblox.GameInternationalization.Api.UpdateUniverseDisplayInfoAutomaticTranslationSettingsResponse` - `400`: 14: Invalid game id 72: Automatic translation cannot be enabled for game. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `500`: 77: Content localization set settings return error code invalid 79: Invalid content instance settings 80: Invalid quota settings 81: Invalid change agent 82: Failed to update UniverseDisplayInformation content instance auto translation settings **Response fields** (`Roblox.GameInternationalization.Api.UpdateUniverseDisplayInfoAutomaticTranslationSettingsResponse`) See [Roblox.GameInternationalization.Api.UpdateUniverseDisplayInfoAutomaticTranslationSettingsResponse](#roblox-gameinternationalization-api-updateuniversedisplayinfoautomatictranslationsettingsresponse) in Models. **Response example:** ```json { "gameId": 0, "languageCode": "string", "isUniverseDisplayInfoAutomaticTranslationEnabled": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/supported-languages/games/{GAMEID}/languages/{LANGUAGECODE}/universe-display-info-automatic-translation-settings" \ -H "Content-Type: application/json" \ -d 'false' ``` ### DELETE `/v1/game-thumbnails/games/{gameId}/language-codes/{languageCode}/images/{imageId}` Deletes the game thumbnail. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer (int64)` | Yes | The game identifier. | | `languageCode` | path | `string` | Yes | The language code. | | `imageId` | path | `integer (int64)` | Yes | The image identifier. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 14: Invalid game id 22: Invalid language code 23: You can't delete translations for source language 53: Language is not supported for the game. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v1/game-thumbnails/games/{GAMEID}/language-codes/{LANGUAGECODE}/images/{IMAGEID}" ``` --- name: "GameInternationalization Api v2" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://gameinternationalization.roblox.com" version: "v2" endpoints: 1 auth: [cookie] --- # GameInternationalization Api v2 **API Version:** v2 **Base URL:** `https://gameinternationalization.roblox.com` ## Endpoints ### GET `/v2/supported-languages/games/{gameId}` Get the supported languages for a game. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer (int64)` | Yes | The id of the game. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.GameInternationalization.Api.LanguageWithLocales]` - `400`: 14: Invalid game id - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.GameInternationalization.Api.LanguageWithLocales]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.GameInternationalization.Api.LanguageWithLocales]](#roblox-web-webapi-models-apiarrayresponse-roblox-gameinternationalization-api-languagewithlocales-) in Models. **Response example:** ```json { "data": [ { "languageFamily": "...", "childLocales": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://gameinternationalization.roblox.com/v2/supported-languages/games/{GAMEID}" ``` ## Models ### Roblox.GameInternationalization.Api.Language | Property | Type | Required | Description | |----------|------|----------|-------------| | `name` | `string` | No | | | `nativeName` | `string` | No | | | `languageCode` | `string` | No | | ### Roblox.GameInternationalization.Api.LanguageWithLocales | Property | Type | Required | Description | |----------|------|----------|-------------| | `languageFamily` | `Roblox.GameInternationalization.Api.Language` | No | | | `childLocales` | `Roblox.Localization.Client.SupportedLocale[]` | No | | ### Roblox.Localization.Client.LanguageFamily | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | | | `name` | `string` | No | | | `nativeName` | `string` | No | | | `languageCode` | `string` | No | | | `isRightToLeft` | `boolean` | No | | ### Roblox.Localization.Client.SupportedLocale | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | | | `locale` | `string enum (51 values)` | No | Values: en_us, es_es, fr_fr, id_id, it_it, ja_jp, ko_kr, ru_ru, th_th, tr_tr, vi_vn, pt_br, de_de, zh_cn, zh_tw, bg_bg, bn_bd, cs_cz, da_dk, el_gr, et_ee, fi_fi, hi_in, hr_hr, hu_hu, ka_ge, kk_kz, km_kh, lt_lt, lv_lv, ms_my, my_mm, nb_no, nl_nl, fil_ph, pl_pl, ro_ro, uk_ua, si_lk, sk_sk, sl_sl, sq_al, bs_ba, sr_rs, sv_se, zh_cjv, ar_001, en_gb, pt_pt, es_mx, fr_ca | | `localeCode` | `string` | No | | | `name` | `string` | No | | | `nativeName` | `string` | No | | | `language` | `Roblox.Localization.Client.LanguageFamily` | No | | ### Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.GameInternationalization.Api.LanguageWithLocales] | Property | Type | Required | Description | |----------|------|----------|-------------| | `data` | `Roblox.GameInternationalization.Api.LanguageWithLocales[]` | No | | --- name: "Games Api v1" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://games.roblox.com" version: "v1" endpoints: 10 auth: [cookie] --- # Games Api v1 **API Version:** v1 **Base URL:** `https://games.roblox.com` ## Endpoints ### GET `/v1/games` Gets a list of games' detail **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeIds` | query | `array` | Yes | A list of universe Ids. Cannot exceed a maximum of 50 IDs. | | `fields` | query | `string` | No | Optional comma-separated list of field names to include in the response. When omitted, all fields are returned. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Games.Api.Models.Response.GameDetailResponse]` - `400`: 8: The universe IDs specified are invalid. 9: Too many universe IDs were requested. - `429`: 4: Too many requests have been made. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Games.Api.Models.Response.GameDetailResponse]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Games.Api.Models.Response.GameDetailResponse]](#roblox-web-webapi-models-apiarrayresponse-roblox-games-api-models-response-gamedetailresponse-) in Models. **Response example:** ```json { "data": [ { "id": "...", "rootPlaceId": "...", "name": "...", "description": "...", "sourceName": "...", "sourceDescription": "..." } ] } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://games.roblox.com/v1/games?universeIds={VALUE}" ``` ### GET `/v1/games/{placeId}/servers/{serverType}` Get the game server list **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `placeId` | path | `integer (int64)` | Yes | The Id of the place we are geting the server list for. | | `serverType` | path | `integer (int32)` | Yes | The type of the server we geting the server list for. Valid values: `0`, `1` | | `sortOrder` | query | `integer (int32)` | No | The sort order of the servers. Valid values: `1`, `2` | | `excludeFullGames` | query | `boolean` | No | Exclude full servers. | | `limit` | query | `integer (int32)` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Web.Responses.Games.GameServerResponse]` - `400`: 1: The place is invalid. 6: The server type is invalid. For fetching private servers, please use https://games.roblox.com/v1/games/{placeId}/private-servers. 7: Guest users are not allowed. - `404`: 1: The place is invalid. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Web.Responses.Games.GameServerResponse]`) See [Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Web.Responses.Games.GameServerResponse]](#roblox-web-webapi-models-apipageresponse-roblox-web-responses-games-gameserverresponse-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "id": "...", "maxPlayers": "...", "playing": "...", "playerTokens": "...", "players": "...", "fps": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://games.roblox.com/v1/games/{PLACEID}/servers/{SERVERTYPE}" ``` ### GET `/v1/games/{universeId}/favorites` Returns if a game was marked as favorite for the authenticated user **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer (int64)` | Yes | The Id of the universe. | **Responses:** - `200`: OK → `Roblox.Games.Api.Models.Response.GameFavoriteResponse` - `400`: 3: The universe's root place is invalid. - `401`: 0: Authorization has been denied for this request. - `404`: 2: The requested universe does not exist. **Response fields** (`Roblox.Games.Api.Models.Response.GameFavoriteResponse`) See [Roblox.Games.Api.Models.Response.GameFavoriteResponse](#roblox-games-api-models-response-gamefavoriteresponse) in Models. **Response example:** ```json { "isFavorited": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://games.roblox.com/v1/games/{UNIVERSEID}/favorites" ``` ### POST `/v1/games/{universeId}/favorites` Favors (or unfavors) a game for the authenticated user **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer (int64)` | Yes | The Id of the universe. | **Request Body:** `application/json` — Type: `Roblox.Games.Api.Models.Request.GameFavoritesRequest` See [Roblox.Games.Api.Models.Request.GameFavoritesRequest](#roblox-games-api-models-request-gamefavoritesrequest) in Models. **Request example:** ```json { "isFavorited": false } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 3: The universe's root place is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 11: You are not authorized to perform this action. - `404`: 2: The requested universe does not exist. - `429`: 4: Too many requests have been made. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://games.roblox.com/v1/games/{UNIVERSEID}/favorites" \ -H "Content-Type: application/json" \ -d '{ "isFavorited": false }' ``` ### GET `/v1/games/{universeId}/favorites/count` Get the favorites count of a specific game. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer (int64)` | Yes | The Id of the universe. | **Responses:** - `200`: OK → `Roblox.Games.Api.Models.Response.GameFavoritesCountResponse` - `400`: 3: The universe's root place is invalid. - `404`: 2: The requested universe does not exist. **Response fields** (`Roblox.Games.Api.Models.Response.GameFavoritesCountResponse`) See [Roblox.Games.Api.Models.Response.GameFavoritesCountResponse](#roblox-games-api-models-response-gamefavoritescountresponse) in Models. **Response example:** ```json { "favoritesCount": 0 } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://games.roblox.com/v1/games/{UNIVERSEID}/favorites/count" ``` ### GET `/v1/games/{universeId}/media` *(deprecated)* Get the game media data **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer (int64)` | Yes | The id of the universe we get media data from. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Games.Api.Models.Response.GameMediaItem]` - `400`: 3: The universe's root place is invalid. - `404`: 2: The requested universe does not exist. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Games.Api.Models.Response.GameMediaItem]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Games.Api.Models.Response.GameMediaItem]](#roblox-web-webapi-models-apiarrayresponse-roblox-games-api-models-response-gamemediaitem-) in Models. **Response example:** ```json { "data": [ { "id": "...", "assetTypeId": "...", "assetType": "...", "imageId": "...", "videoHash": "...", "videoTitle": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://games.roblox.com/v1/games/{UNIVERSEID}/media" ``` ### GET `/v1/games/games-product-info` Gets a list of games' product info, used to purchase a game **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeIds` | query | `array` | Yes | A list of universe Ids. Cannot exceed a maximum of 100 IDs. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Games.Api.Models.Response.GameProductResponse]` - `400`: 8: The universe IDs specified are invalid. 9: Too many universe IDs were requested. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Games.Api.Models.Response.GameProductResponse]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Games.Api.Models.Response.GameProductResponse]](#roblox-web-webapi-models-apiarrayresponse-roblox-games-api-models-response-gameproductresponse-) in Models. **Response example:** ```json { "data": [ { "universeId": "...", "isForSale": "...", "productId": "...", "price": "...", "sellerId": "...", "fiatPurchaseData": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://games.roblox.com/v1/games/games-product-info?universeIds={VALUE}" ``` ### GET `/v1/games/multiget-place-details` Get place details **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `placeIds` | query | `array` | Yes | List of placeId to uniquely Identify a place | **Responses:** - `200`: OK → `Roblox.Games.Api.Models.Response.PlaceDetails[]` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Games.Api.Models.Response.PlaceDetails[]`) See [Roblox.Games.Api.Models.Response.PlaceDetails](#roblox-games-api-models-response-placedetails) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://games.roblox.com/v1/games/multiget-place-details?placeIds={VALUE}" ``` ### GET `/v1/games/multiget-playability-status` Gets a list of universe playability statuses for the authenticated user **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeIds` | query | `array` | Yes | A list of universe Ids. Cannot exceed a maximum of 50 IDs. | **Responses:** - `200`: OK → `Roblox.Games.Api.Models.Response.PlayabilityStatusResponse[]` - `400`: 8: The universe IDs specified are invalid. 9: Too many universe IDs were requested. **Response fields** (`Roblox.Games.Api.Models.Response.PlayabilityStatusResponse[]`) See [Roblox.Games.Api.Models.Response.PlayabilityStatusResponse](#roblox-games-api-models-response-playabilitystatusresponse) in Models. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://games.roblox.com/v1/games/multiget-playability-status?universeIds={VALUE}" ``` ### GET `/v1/games/recommendations/game/{universeId}` Get games recommendations based on a given universe **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer (int64)` | Yes | The universe to base recommendations on | | `PaginationKey` | query | `string` | Yes | The key of a page, which includes the start row index and all other necessary information to query the data. This parameter is usually not needed for the first page. | | `MaxRows` | query | `integer (int32)` | Yes | The requested number of rows. | | `IsTruncatedResultsEnabled` | query | `boolean` | Yes | Truncated Results | **Responses:** - `200`: OK → `Roblox.Games.Api.Models.Response.GameRecommendationsResponse` - `400`: 1: The pagination key is invalid. - `404`: 2: The requested universe does not exist. **Response fields** (`Roblox.Games.Api.Models.Response.GameRecommendationsResponse`) See [Roblox.Games.Api.Models.Response.GameRecommendationsResponse](#roblox-games-api-models-response-gamerecommendationsresponse) in Models. **Response example:** ```json { "games": [ { "creatorId": "...", "creatorName": "...", "creatorType": "...", "creatorHasVerifiedBadge": "...", "totalUpVotes": "...", "totalDownVotes": "..." } ], "nextPaginationKey": "string" } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://games.roblox.com/v1/games/recommendations/game/{UNIVERSEID}?PaginationKey={VALUE}&MaxRows={VALUE}&IsTruncatedResultsEnabled={VALUE}" ``` ## Models ### Roblox.Games.Api.GameServerPlayerResponse A response model representing a game server player. | Property | Type | Required | Description | |----------|------|----------|-------------| | `playerToken` | `string` | No | The thumbnail token for the player. | | `id` | `integer` | No | | | `name` | `string` | No | | | `displayName` | `string` | No | | ### Roblox.Games.Api.Models.Request.GameFavoritesRequest Game favorite request model. | Property | Type | Required | Description | |----------|------|----------|-------------| | `isFavorited` | `boolean` | No | true for favor the game, false for unfavor the game. | ### Roblox.Games.Api.Models.Response.GameContentMetadataResponseModel Response model for game-level content metadata. | Property | Type | Required | Description | |----------|------|----------|-------------| | `badgePosition` | `string` | No | | | `badgeAnalyticsId` | `string` | No | | | `badgeType` | `string` | No | | | `badgeIcon` | `string` | No | | | `badgeComponentType` | `string` | No | | ### Roblox.Games.Api.Models.Response.GameCreator Response model for getting the game creator | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | The game creator id | | `name` | `string` | No | The game creator name | | `type` | `string` | No | The game creator type | | `isRNVAccount` | `boolean` | No | The game creator account is Luobu Real Name Verified | | `hasVerifiedBadge` | `boolean` | No | Builder verified badge status. | ### Roblox.Games.Api.Models.Response.GameDetailResponse Response model for getting the game detail | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | The game universe id | | `rootPlaceId` | `integer` | No | The game root place id | | `name` | `string` | No | The game name | | `description` | `string` | No | The game description | | `sourceName` | `string` | No | The game name in the source language, if different from the returned name. | | `sourceDescription` | `string` | No | The game description in the source language, if different from the returned description. | | `creator` | `Roblox.Games.Api.Models.Response.GameCreator` | No | | | `price` | `integer` | No | The game paid access price | | `allowedGearGenres` | `string[]` | No | The game allowed gear genres | | `allowedGearCategories` | `string[]` | No | The game allowed gear categoris | | `isGenreEnforced` | `boolean` | No | The game must specify a genre | | `copyingAllowed` | `boolean` | No | The game allows place to be copied | | `playing` | `integer` | No | Current player count of the game | | `visits` | `integer` | No | The total visits to the game | | `maxPlayers` | `integer` | No | The game max players | | `created` | `string` | No | The game created time | | `updated` | `string` | No | The game updated time | | `studioAccessToApisAllowed` | `boolean` | No | The setting of IsStudioAccessToApisAllowed of the universe | | `createVipServersAllowed` | `boolean` | No | Gets or sets the flag to indicate whether the create vip servers button should be allowed in game details page | | `universeAvatarType` | `1 \| 2 \| 3` | No | Avatar type. Possible values are MorphToR6, MorphToR15, and PlayerChoice | | `genre` | `string` | No | The game genre display name | | `genre_l1` | `string` | No | The game genre from experience-genres-service | | `genre_l2` | `string` | No | The game subgenre from experience-genres-service | | `untranslated_genre_l1` | `string` | No | The game genre from experience-genres-service untranslated | | `isAllGenre` | `boolean` | No | Is this game all genre. | | `isFavoritedByUser` | `boolean` | No | Is this game favorited by user. | | `favoritedCount` | `integer` | No | Game number of favorites. | | `licenseDescription` | `string` | No | | | `refundLink` | `string` | No | | | `localizedFiatPrice` | `string` | No | | | `refundPolicy` | `Roblox.Games.Api.Models.Response.RefundPolicy` | No | | | `canonicalUrlPath` | `string` | No | Canonical URL path for the game page, e.g. /games/{placeId}/{canonical-slug}. It must be the same as the canonical URL (rel-canonical meta tag) on the game's EDP. | | `isContentRestricted` | `boolean` | No | Indicates whether this experience is content-restricted and has had its details wiped. When true, the content fields (name, description, creator, etc.) contain placeholder or empty values rather than the real data. Consumers should branch on this flag instead of inferring restriction from placeholder text. | ### Roblox.Games.Api.Models.Response.GameFavoriteResponse Game favorite response model. | Property | Type | Required | Description | |----------|------|----------|-------------| | `isFavorited` | `boolean` | No | Is it a favorite game. | ### Roblox.Games.Api.Models.Response.GameFavoritesCountResponse Response model for favorited game count. | Property | Type | Required | Description | |----------|------|----------|-------------| | `favoritesCount` | `integer` | No | Favorites count. | ### Roblox.Games.Api.Models.Response.GameMediaItem Response model for getting the game media item | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | The media item id. | | `assetTypeId` | `integer` | No | The media item type id | | `assetType` | `string` | No | The media item type, Image or YouTubeVideo | | `imageId` | `integer` | No | The media item image id | | `videoHash` | `string` | No | The media item video hash | | `videoTitle` | `string` | No | The video title for video items. | | `approved` | `boolean` | No | The media item is approved or not | | `altText` | `string` | No | The media item's alt text | ### Roblox.Games.Api.Models.Response.GameProductResponse Response model for getting the game product information | Property | Type | Required | Description | |----------|------|----------|-------------| | `universeId` | `integer` | No | The game universe id | | `isForSale` | `boolean` | No | The game purchasability | | `productId` | `integer` | No | The game product id | | `price` | `integer` | No | The game price | | `sellerId` | `integer` | No | The game seller id User ID for users, Agent ID for groups | | `fiatPurchaseData` | `Roblox.Games.Api.Models.Response.PurchaseData` | No | | ### Roblox.Games.Api.Models.Response.GameRecommendationsResponse Game recommendations response model. | Property | Type | Required | Description | |----------|------|----------|-------------| | `games` | `Roblox.Games.Api.Models.Response.GameResponseModel[]` | No | The recommended games. | | `nextPaginationKey` | `string` | No | The pagination key for next page query. It will be null if there is no more data. | ### Roblox.Games.Api.Models.Response.GameResponseModel Response model for games. | Property | Type | Required | Description | |----------|------|----------|-------------| | `creatorId` | `integer` | No | Creator Id | | `creatorName` | `string` | No | Creator name | | `creatorType` | `string` | No | Creator type | | `creatorHasVerifiedBadge` | `boolean` | No | Creator verified badge status | | `totalUpVotes` | `integer` | No | Total up votes | | `totalDownVotes` | `integer` | No | Total down votes | | `universeId` | `integer` | No | Universe id | | `name` | `string` | No | Game name | | `placeId` | `integer` | No | Place Id | | `playerCount` | `integer` | No | Player count | | `imageToken` | `string` | No | Place image token | | `isSponsored` | `boolean` | No | Is sponsored | | `nativeAdData` | `string` | No | Native ad data | | `isShowSponsoredLabel` | `boolean` | No | Show the sponsored label | | `price` | `integer` | No | The game paid access price | | `analyticsIdentifier` | `string` | No | Provide all necessary information which helps analytics for improvement, for example, the algorithm, dataset version, position, etc.. | | `gameDescription` | `string` | No | Provide all necessary information which helps analytics for improvement, for example, the algorithm, dataset version, position, etc.. | | `genre` | `string` | No | The game genre display name | | `minimumAge` | `integer` | No | Age Recommendation minimum age. | | `ageRecommendationDisplayName` | `string` | No | Age Recommendation display name. | | `contentMetadata` | `Roblox.Games.Api.Models.Response.GameContentMetadataResponseModel` | No | | | `canonicalUrlPath` | `string` | No | Canonical URL path for the game page, e.g. /games/{placeId}/{canonical-slug}. It must be the same as the canonical URL (rel-canonical meta tag) on the game's EDP. | ### Roblox.Games.Api.Models.Response.PlaceDetails Response model for a place. | Property | Type | Required | Description | |----------|------|----------|-------------| | `placeId` | `integer` | No | Place Id | | `name` | `string` | No | Place name | | `description` | `string` | No | Place description | | `sourceName` | `string` | No | Place name in source language | | `sourceDescription` | `string` | No | Place description in source language | | `url` | `string` | No | Url | | `builder` | `string` | No | Creator name | | `builderId` | `integer` | No | Creator Id | | `hasVerifiedBadge` | `boolean` | No | Builder verified badge status. | | `isPlayable` | `boolean` | No | Is playable | | `reasonProhibited` | `string` | No | Reason prohibited | | `universeId` | `integer` | No | Universe id | | `universeRootPlaceId` | `integer` | No | UniverseRootPlaceId | | `price` | `integer` | No | Price | | `imageToken` | `string` | No | Place image token | | `fiatPurchaseData` | `Roblox.Games.Api.Models.Response.PurchaseData` | No | | ### Roblox.Games.Api.Models.Response.PlayabilityStatusResponse Response model for getting a universe's playability status for a user | Property | Type | Required | Description | |----------|------|----------|-------------| | `playabilityStatus` | `integer enum (29 values)` | No | The actual playability status of the universe including the reason if unplayable ['UnplayableOtherReason' = 0, 'Playable' = 1, 'GuestProhibited' = 2, 'GameUnapproved' = 3, 'IncorrectConfiguration' = 4, 'UniverseRootPlaceIsPrivate' = 5, 'InsufficientPermissionFriendsOnly' = 6, 'InsufficientPermissionGroupOnly' = 7, 'DeviceRestricted' = 8, 'UnderReview' = 9, 'PurchaseRequired' = 10, 'AccountRestricted' = 11, 'TemporarilyUnavailable' = 12, 'PlaceHasNoPublishedVersion' = 13, 'ComplianceBlocked' = 14, 'ContextualPlayabilityRegionalAvailability' = 15, 'ContextualPlayabilityRegionalCompliance' = 16, 'ContextualPlayabilityAgeRecommendationParentalControls' = 17, 'ContextualPlayabilityExperienceBlockedParentalControls' = 18, 'ContextualPlayabilityAgeGated' = 19, 'ContextualPlayabilityUnverifiedSeventeenPlusUser' = 20, 'FiatPurchaseRequired' = 21, 'FiatPurchaseDeviceRestricted' = 22, 'ContextualPlayabilityUnrated' = 23, 'ContextualPlayabilityAgeGatedByDescriptor' = 24, 'ContextualPlayabilityGeneral' = 25, 'ContextualPlayabilityAgeCheckRequired' = 26, 'ContextualPlayabilityRequireParentApproval' = 27, 'ContextualPlayabilityCoreGated' = 28] Values: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28 | | `isPlayable` | `boolean` | No | Whether or not the universe is playable for the user | | `universeId` | `integer` | No | The universeId of the requested universe to help with batching requests | | `unplayableDisplayText` | `string` | No | Localized display text explaining why unplayable | | `playableUxTreatment` | `Roblox.Games.Api.Models.Response.PlayableUxTreatment` | No | | | `upsellUxTreatment` | `Roblox.Games.Api.Models.Response.UpsellUxTreatment` | No | | | `demoModeAvailable` | `boolean` | No | Whether the game supports demo play for users who have not purchased it. Only set when PlayabilityStatus is PurchaseRequired or FiatPurchaseRequired. | ### Roblox.Games.Api.Models.Response.PlayableUxTreatment | Property | Type | Required | Description | |----------|------|----------|-------------| | `treatment` | `string` | No | | | `data` | `Roblox.Games.Api.Models.Response.PlayableUxTreatmentData` | No | | ### Roblox.Games.Api.Models.Response.PlayableUxTreatmentData | Property | Type | Required | Description | |----------|------|----------|-------------| | `titleText` | `string` | No | | | `bodyText` | `string` | No | | | `primaryActionText` | `string` | No | | | `secondaryActionText` | `string` | No | | ### Roblox.Games.Api.Models.Response.PurchaseData | Property | Type | Required | Description | |----------|------|----------|-------------| | `localizedFiatPrice` | `string` | No | Fiat purchase price in a localized string for display on client. | | `basePriceId` | `string` | No | ID of base price, needed by clients to prepare purchase url. | ### Roblox.Games.Api.Models.Response.RefundPolicy | Property | Type | Required | Description | |----------|------|----------|-------------| | `policyText` | `string` | No | The display text with a placeholder for clients to inject a link, such as: "Learn more about policy xyz {linkStart}Learn More{linkEnd}" | | `learnMoreBaseUrl` | `string` | No | The base URL that the client should redirect to. | | `locale` | `string` | No | The Roblox selected language locale that the user has presently selected. | | `articleId` | `string` | No | Zendesk (or similar forum provider) article ID for the client to build a redirect URL. | ### Roblox.Games.Api.Models.Response.UpsellUxTreatment Upsell UX treatment metadata appended to a playability response when an experience is currently playable but will be restricted under upcoming Roblox-Core content rules. Mirrors the existing Roblox.Games.Api.Models.Response.PlayableUxTreatment contract intentionally so client rendering can be unified. | Property | Type | Required | Description | |----------|------|----------|-------------| | `treatment` | `string` | No | Identifier of the client component to render (currently "ageCheckUpsell"). Absent when the upsell should not surface. | | `data` | `Roblox.Games.Api.Models.Response.UpsellUxTreatmentData` | No | | ### Roblox.Games.Api.Models.Response.UpsellUxTreatmentData Localized strings for an Roblox.Games.Api.Models.Response.UpsellUxTreatment. Currently only carries Roblox.Games.Api.Models.Response.UpsellUxTreatmentData.BodyText; structured to allow additional fields without breaking the JSON contract. | Property | Type | Required | Description | |----------|------|----------|-------------| | `bodyText` | `string` | No | Localized body text for the upsell banner (e.g. "You'll soon need an age check to join this game."). | ### Roblox.Games.Api.Models.Response.VerifiedBadgeUserResponse A response model specific to multi-get user. | Property | Type | Required | Description | |----------|------|----------|-------------| | `hasVerifiedBadge` | `boolean` | No | The user's verified badge status. | | `id` | `integer` | No | | | `name` | `string` | No | | | `displayName` | `string` | No | | ### Roblox.Web.Responses.Games.GameServerResponse Game server list response model. | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `string` | No | The game server ID. | | `maxPlayers` | `integer` | No | The max number of players allowed to enter the server at the same time. | | `playing` | `integer` | No | The number of players actively in the server. | | `playerTokens` | `string[]` | No | The thumbnail tokens for all the players in the server. | | `players` | `Roblox.Games.Api.GameServerPlayerResponse[]` | No | The players in the server. | | `fps` | `number` | No | The current frames per second the server is running at. | | `ping` | `integer` | No | The game server ping time (measured in milliseconds). | | `name` | `string` | No | The name of the private server. | | `vipServerId` | `integer` | No | The private server ID. | | `accessCode` | `string` | No | The private server access code. | | `owner` | `Roblox.Games.Api.Models.Response.VerifiedBadgeUserResponse` | No | | ### Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Games.Api.Models.Response.GameDetailResponse] | Property | Type | Required | Description | |----------|------|----------|-------------| | `data` | `Roblox.Games.Api.Models.Response.GameDetailResponse[]` | No | | ### Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Games.Api.Models.Response.GameMediaItem] | Property | Type | Required | Description | |----------|------|----------|-------------| | `data` | `Roblox.Games.Api.Models.Response.GameMediaItem[]` | No | | ### Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Games.Api.Models.Response.GameProductResponse] | Property | Type | Required | Description | |----------|------|----------|-------------| | `data` | `Roblox.Games.Api.Models.Response.GameProductResponse[]` | No | | ### Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Web.Responses.Games.GameServerResponse] | Property | Type | Required | Description | |----------|------|----------|-------------| | `previousPageCursor` | `string` | No | | | `nextPageCursor` | `string` | No | | | `data` | `Roblox.Web.Responses.Games.GameServerResponse[]` | No | | --- name: "Games Api v2" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://games.roblox.com" version: "v2" endpoints: 3 auth: [cookie] --- # Games Api v2 **API Version:** v2 **Base URL:** `https://games.roblox.com` ## Endpoints ### GET `/v2/games/{universeId}/media` Get the game media data **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer (int64)` | Yes | The id of the universe we get media data from. | | `fetchAllExperienceRelatedMedia` | query | `boolean` | No | to tell if the API query is to fetch all related media for this experience | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Web.Responses.Games.GameMediaItemResponseV2]` - `400`: 3: The universe's root place is invalid. - `404`: 2: The requested universe does not exist. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Web.Responses.Games.GameMediaItemResponseV2]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Web.Responses.Games.GameMediaItemResponseV2]](#roblox-web-webapi-models-apiarrayresponse-roblox-web-responses-games-gamemediaitemresponsev2-) in Models. **Response example:** ```json { "data": [ { "assetTypeId": "...", "assetType": "...", "imageId": "...", "videoHash": "...", "videoTitle": "...", "approved": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://games.roblox.com/v2/games/{UNIVERSEID}/media" ``` ### GET `/v2/users/{userId}/favorite/games` Gets users favorite games. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user Id. | | `accessFilter` | query | `integer (int32)` | No | Filtering option via access level. Valid values: `1`, `2`, `4` | | `limit` | query | `integer (int32)` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `string` | No | The order the results are sorted in. Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Web.Responses.Games.GameFavoriteResponseModel]` **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Web.Responses.Games.GameFavoriteResponseModel]`) See [Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Web.Responses.Games.GameFavoriteResponseModel]](#roblox-web-webapi-models-apipageresponse-roblox-web-responses-games-gamefavoriteresponsemodel-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "price": "...", "id": "...", "name": "...", "description": "...", "creator": "...", "rootPlace": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://games.roblox.com/v2/users/{USERID}/favorite/games" ``` ### GET `/v2/users/{userId}/games` Gets games created by the specified user. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user Id. | | `accessFilter` | query | `integer (int32)` | No | Filtering option via access level. Valid values: `1`, `2`, `4` | | `limit` | query | `integer (int32)` | No | The number of results per request. Valid values: `10`, `25`, `50` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `string` | No | The order the results are sorted in. Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Web.Responses.Games.GameResponseV2]` **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Web.Responses.Games.GameResponseV2]`) See [Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Web.Responses.Games.GameResponseV2]](#roblox-web-webapi-models-apipageresponse-roblox-web-responses-games-gameresponsev2-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "id": "...", "name": "...", "description": "...", "creator": "...", "rootPlace": "...", "created": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://games.roblox.com/v2/users/{USERID}/games" ``` ## Models ### Roblox.Web.Responses.Games.GameFavoriteResponseModel Response model representing a user's favorited game. | Property | Type | Required | Description | |----------|------|----------|-------------| | `price` | `integer` | No | The game paid access price in robux. Given that there is no actual owner for this product surface, we will keep existing behavior where we display offsale for games priced in fiat currency. | | `id` | `integer` | No | The game (universe) Id. | | `name` | `string` | No | The game name. | | `description` | `string` | No | The game description. | | `creator` | `Roblox.Web.Responses.RelatedEntityTypeResponse[Roblox.Platform.Core.CreatorType]` | No | | | `rootPlace` | `Roblox.Web.Responses.RelatedEntityTypeResponse[Roblox.Platform.Assets.AssetType]` | No | | | `created` | `string` | No | When the game was created. | | `updated` | `string` | No | When the game was last updated. | | `placeVisits` | `integer` | No | The number of place visits for this game. | ### Roblox.Web.Responses.Games.GameMediaItemResponseV2 Response model for getting the game media item | Property | Type | Required | Description | |----------|------|----------|-------------| | `assetTypeId` | `integer` | No | The media item type id | | `assetType` | `string` | No | The media item type, Image or YouTubeVideo | | `imageId` | `integer` | No | The media item image id | | `videoHash` | `string` | No | The media item video hash | | `videoTitle` | `string` | No | The video title for video items. | | `approved` | `boolean` | No | The media item is approved or not | | `altText` | `string` | No | The media item alt text | | `videoId` | `string` | No | The video asset ID | ### Roblox.Web.Responses.Games.GameResponseV2 A response model representing a game. | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | The game (universe) Id. | | `name` | `string` | No | The game name. | | `description` | `string` | No | The game description. | | `creator` | `Roblox.Web.Responses.RelatedEntityTypeResponse[Roblox.Platform.Core.CreatorType]` | No | | | `rootPlace` | `Roblox.Web.Responses.RelatedEntityTypeResponse[Roblox.Platform.Assets.AssetType]` | No | | | `created` | `string` | No | When the game was created. | | `updated` | `string` | No | When the game was last updated. | | `placeVisits` | `integer` | No | The number of place visits for this game. | ### Roblox.Web.Responses.RelatedEntityTypeResponse[Roblox.Platform.Assets.AssetType] | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | | | `type` | `integer enum (86 values)` | No | ['Image' = 1, 'TShirt' = 2, 'Audio' = 3, 'Mesh' = 4, 'Lua' = 5, 'HTML' = 6, 'Text' = 7, 'Hat' = 8, 'Place' = 9, 'Model' = 10, 'Shirt' = 11, 'Pants' = 12, 'Decal' = 13, 'Avatar' = 16, 'Head' = 17, 'Face' = 18, 'Gear' = 19, 'Badge' = 21, 'GroupEmblem' = 22, 'Animation' = 24, 'Arms' = 25, 'Legs' = 26, 'Torso' = 27, 'RightArm' = 28, 'LeftArm' = 29, 'LeftLeg' = 30, 'RightLeg' = 31, 'Package' = 32, 'YouTubeVideo' = 33, 'GamePass' = 34, 'App' = 35, 'Code' = 37, 'Plugin' = 38, 'SolidModel' = 39, 'MeshPart' = 40, 'HairAccessory' = 41, 'FaceAccessory' = 42, 'NeckAccessory' = 43, 'ShoulderAccessory' = 44, 'FrontAccessory' = 45, 'BackAccessory' = 46, 'WaistAccessory' = 47, 'ClimbAnimation' = 48, 'DeathAnimation' = 49, 'FallAnimation' = 50, 'IdleAnimation' = 51, 'JumpAnimation' = 52, 'RunAnimation' = 53, 'SwimAnimation' = 54, 'WalkAnimation' = 55, 'PoseAnimation' = 56, 'LocalizationTableManifest' = 59, 'LocalizationTableTranslation' = 60, 'EmoteAnimation' = 61, 'Video' = 62, 'TexturePack' = 63, 'TShirtAccessory' = 64, 'ShirtAccessory' = 65, 'PantsAccessory' = 66, 'JacketAccessory' = 67, 'SweaterAccessory' = 68, 'ShortsAccessory' = 69, 'LeftShoeAccessory' = 70, 'RightShoeAccessory' = 71, 'DressSkirtAccessory' = 72, 'FontFamily' = 73, 'FontFace' = 74, 'MeshHiddenSurfaceRemoval' = 75, 'EyebrowAccessory' = 76, 'EyelashAccessory' = 77, 'MoodAnimation' = 78, 'DynamicHead' = 79, 'CodeSnippet' = 80, 'AdsVideo' = 81, 'OtaUpdate' = 82, 'Screenshot' = 83, 'RuntimePropertySet' = 84, 'StorePreviewVideo' = 85, 'GamePreviewVideo' = 86, 'CreatorExperienceConfig' = 87, 'FaceMakeup' = 88, 'LipMakeup' = 89, 'EyeMakeup' = 90, 'VoxelFragment' = 91, 'AvatarBackground' = 92, 'TextDocument' = 93] Values: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 16, 17, 18, 19, 21, 22, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93 | | `name` | `string` | No | | ### Roblox.Web.Responses.RelatedEntityTypeResponse[Roblox.Platform.Core.CreatorType] | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | | | `type` | `0 \| 1` | No | ['User' = 0, 'Group' = 1] | | `name` | `string` | No | | ### Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Web.Responses.Games.GameMediaItemResponseV2] | Property | Type | Required | Description | |----------|------|----------|-------------| | `data` | `Roblox.Web.Responses.Games.GameMediaItemResponseV2[]` | No | | ### Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Web.Responses.Games.GameFavoriteResponseModel] | Property | Type | Required | Description | |----------|------|----------|-------------| | `previousPageCursor` | `string` | No | | | `nextPageCursor` | `string` | No | | | `data` | `Roblox.Web.Responses.Games.GameFavoriteResponseModel[]` | No | | ### Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Web.Responses.Games.GameResponseV2] | Property | Type | Required | Description | |----------|------|----------|-------------| | `previousPageCursor` | `string` | No | | | `nextPageCursor` | `string` | No | | | `data` | `Roblox.Web.Responses.Games.GameResponseV2[]` | No | | --- name: "Groups Api v1" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://groups.roblox.com" version: "v1" endpoints: 85 auth: [cookie] --- # Groups Api v1 **API Version:** v1 **Base URL:** `https://groups.roblox.com` ## Endpoints ### GET `/v1/featured-content/event` Gets the featured event for a group **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | query | `integer (int64)` | Yes | The group Id. | **Responses:** - `200`: OK → `Roblox.Groups.Client.GroupFeaturedContentResponse` **Response fields** (`Roblox.Groups.Client.GroupFeaturedContentResponse`) See [Roblox.Groups.Client.GroupFeaturedContentResponse](#roblox-groups-client-groupfeaturedcontentresponse) in Models. **Response example:** ```json { "groupId": 0, "contentType": "string", "contentId": "string" } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/featured-content/event?groupId={VALUE}" ``` ### POST `/v1/featured-content/event` Sets the featured event for a group **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | query | `integer (int64)` | Yes | The group Id. | | `eventId` | query | `integer (int64)` | Yes | The event Id. | **Responses:** - `200`: OK → `Roblox.Groups.Client.GroupFeaturedContentResponse` - `403`: 0: Token Validation Failed 3: User is not authorized to set featured content for this group. **Response fields** (`Roblox.Groups.Client.GroupFeaturedContentResponse`) See [Roblox.Groups.Client.GroupFeaturedContentResponse](#roblox-groups-client-groupfeaturedcontentresponse) in Models. **Response example:** ```json { "groupId": 0, "contentType": "string", "contentId": "string" } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/featured-content/event?groupId={VALUE}&eventId={VALUE}" ``` ### DELETE `/v1/featured-content/event` Deletes the featured event for a group **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | query | `integer (int64)` | Yes | The group Id. | | `eventId` | query | `integer (int64)` | Yes | The event Id. | **Responses:** - `200`: OK - `403`: 0: Token Validation Failed 3: User is not authorized to set featured content for this group. **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/featured-content/event?groupId={VALUE}&eventId={VALUE}" ``` ### GET `/v1/groups/{groupId}` Gets group information **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group Id. | **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupDetailResponse` - `400`: 1: Group is invalid or does not exist. **Response fields** (`Roblox.Groups.Api.GroupDetailResponse`) See [Roblox.Groups.Api.GroupDetailResponse](#roblox-groups-api-groupdetailresponse) in Models. **Response example:** ```json { "id": 0, "name": "string", "description": "string", "owner": { "buildersClubMembershipType": 0, "hasVerifiedBadge": false, "userId": 0, "username": "string", "displayName": "string" }, "shout": { "body": "string", "poster": { "buildersClubMembershipType": "...", "hasVerifiedBadge": "...", "userId": "...", "username": "...", "displayName": "..." }, "created": "2024-01-01T00:00:00Z", "updated": "2024-01-01T00:00:00Z" }, "memberCount": 0 } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}" ``` ### GET `/v1/groups/{groupId}/audit-log` Gets the Group's audit log **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The id of the group the user is in. | | `actionType` | query | `string` | No | Filter for specific type of action performed Valid values: `DeletePost`, `RemoveMember`, `AcceptJoinRequest`, `DeclineJoinRequest`, `PostStatus`, `ChangeRank`, `BuyAd`, `SendAllyRequest`, `CreateEnemy`, `AcceptAllyRequest`, `DeclineAllyRequest`, `DeleteAlly`, `DeleteEnemy`, `AddGroupPlace`, `RemoveGroupPlace`, `CreateItems`, `ConfigureItems`, `SpendGroupFunds`, `ChangeOwner`, `Delete`, `AdjustCurrencyAmounts`, `Abandon`, `Claim`, `Rename`, `ChangeDescription`, `InviteToClan`, `KickFromClan`, `CancelClanInvite`, `BuyClan`, `CreateGroupAsset`, `UpdateGroupAsset`, `ConfigureGroupAsset`, `RevertGroupAsset`, `CreateGroupDeveloperProduct`, `ConfigureGroupGame`, `CreateGroupDeveloperSubscriptionProduct`, `Lock`, `Unlock`, `CreateGamePass`, `CreateBadge`, `ConfigureBadge`, `SavePlace`, `PublishPlace`, `UpdateRolesetRank`, `UpdateRolesetData`, `BanMember`, `UnbanMember`, `CreateForumCategory`, `UpdateForumCategory`, `ArchiveForumCategory`, `DeleteForumCategory`, `DeleteForumPost`, `DeleteForumComment`, `PinForumPost`, `UnpinForumPost`, `LockForumPost`, `UnlockForumPost`, `CreateRoleset`, `DeleteRoleset`, `CreateCommerceProduct`, `SetCommerceProductActive`, `ArchiveCommerceProduct`, `AcceptCommerceProductBundlingFee`, `SetCommerceProductInactive`, `ConnectMerchant`, `DisconnectMerchant`, `JoinGroup`, `LeaveGroup`, `UpdateGroupIcon`, `UpdateGroupCoverPhoto`, `AssignRole`, `UnassignRole`, `PublishAnnouncement`, `DeleteAnnouncement` | | `userId` | query | `integer (int64)` | No | Filter for specific user id | | `limit` | query | `integer (int32)` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `string` | No | The order the results are sorted in. Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupAuditLogPageResponse[Roblox.Groups.Api.Models.Response.GroupAuditLogResponseItem]` - `400`: 1: Group is invalid or does not exist. 8: Invalid or missing pagination parameters - `401`: 0: Authorization has been denied for this request. - `403`: 23: Insufficient permissions to complete the request. **Response fields** (`Roblox.Groups.Api.GroupAuditLogPageResponse[Roblox.Groups.Api.Models.Response.GroupAuditLogResponseItem]`) See [Roblox.Groups.Api.GroupAuditLogPageResponse[Roblox.Groups.Api.Models.Response.GroupAuditLogResponseItem]](#roblox-groups-api-groupauditlogpageresponse-roblox-groups-api-models-response-groupauditlogresponseitem-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "actor": "...", "actionType": "...", "description": "...", "created": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/audit-log" ``` ### GET `/v1/groups/{groupId}/bans` Gets the bans for the group **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group Id. | | `limit` | query | `integer (int32)` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `string` | No | The order the results are sorted in. Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Groups.Api.GroupBanMemberResponse]` - `401`: 0: Authorization has been denied for this request. - `403`: 19: You have insufficient permissions for this request. - `404`: 1: The group is invalid or does not exist. - `405`: 18: The operation is temporarily unavailable. Please try again later. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Groups.Api.GroupBanMemberResponse]`) See [Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Groups.Api.GroupBanMemberResponse]](#roblox-web-webapi-models-apipageresponse-roblox-groups-api-groupbanmemberresponse-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "user": "...", "actingUser": "...", "created": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/bans" ``` ### GET `/v1/groups/{groupId}/bans/{userId}` Fetch the group ban of a user from a group **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group Id. | | `userId` | path | `integer (int64)` | Yes | The user Id. | **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupBanMemberResponse` - `401`: 0: Authorization has been denied for this request. - `403`: 19: You have insufficient permissions for this request. - `404`: 1: The group is invalid or does not exist. 3: The user is invalid or does not exist. - `405`: 18: The operation is temporarily unavailable. Please try again later. **Response fields** (`Roblox.Groups.Api.GroupBanMemberResponse`) See [Roblox.Groups.Api.GroupBanMemberResponse](#roblox-groups-api-groupbanmemberresponse) in Models. **Response example:** ```json { "user": { "buildersClubMembershipType": 0, "hasVerifiedBadge": false, "userId": 0, "username": "string", "displayName": "string" }, "actingUser": { "user": { "buildersClubMembershipType": "...", "hasVerifiedBadge": "...", "userId": "...", "username": "...", "displayName": "..." }, "role": { "id": "...", "name": "...", "description": "...", "rank": "...", "memberCount": "...", "isBase": "..." } }, "created": "2024-01-01T00:00:00Z" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/bans/{USERID}" ``` ### POST `/v1/groups/{groupId}/bans/{userId}` Bans a user from a group **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group Id. | | `userId` | path | `integer (int64)` | Yes | The Id of the user being banned. | **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupBanMemberResponse` - `400`: 28: The user is already banned from the group. 31: You cannot perform this action against the group owner. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 19: You have insufficient permissions for this request. - `404`: 1: The group is invalid or does not exist. 3: The user is invalid or does not exist. 3: The user is invalid or does not exist. 15: User is not a member of the group. - `405`: 18: The operation is temporarily unavailable. Please try again later. **Response fields** (`Roblox.Groups.Api.GroupBanMemberResponse`) See [Roblox.Groups.Api.GroupBanMemberResponse](#roblox-groups-api-groupbanmemberresponse) in Models. **Response example:** ```json { "user": { "buildersClubMembershipType": 0, "hasVerifiedBadge": false, "userId": 0, "username": "string", "displayName": "string" }, "actingUser": { "user": { "buildersClubMembershipType": "...", "hasVerifiedBadge": "...", "userId": "...", "username": "...", "displayName": "..." }, "role": { "id": "...", "name": "...", "description": "...", "rank": "...", "memberCount": "...", "isBase": "..." } }, "created": "2024-01-01T00:00:00Z" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/bans/{USERID}" ``` ### DELETE `/v1/groups/{groupId}/bans/{userId}` Unbans a user from a group **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group Id. | | `userId` | path | `integer (int64)` | Yes | The Id of the user being unbanned. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 29: The user is not banned from the group. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 19: You have insufficient permissions for this request. - `404`: 1: The group is invalid or does not exist. - `405`: 18: The operation is temporarily unavailable. Please try again later. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/bans/{USERID}" ``` ### GET `/v1/groups/{groupId}/blocked-keywords` Retrieves a paginated list of blocked keywords for a specific Group. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | | | `limit` | query | `integer (int32)` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `string` | No | The order the results are sorted in. Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Groups.Api.BlockedKeywordPageResponse[Roblox.Groups.Client.BlockedKeywordModel]` - `400`: 1: Group is invalid or does not exist. 6: Limit is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: 9: User is invalid or does not exist. 10: Insufficient permissions to complete the request. - `405`: 31: Service is currently unavailable. **Response fields** (`Roblox.Groups.Api.BlockedKeywordPageResponse[Roblox.Groups.Client.BlockedKeywordModel]`) See [Roblox.Groups.Api.BlockedKeywordPageResponse[Roblox.Groups.Client.BlockedKeywordModel]](#roblox-groups-api-blockedkeywordpageresponse-roblox-groups-client-blockedkeywordmodel-) in Models. **Response example:** ```json { "totalActiveKeywordsCount": 0, "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "id": "...", "keyword": "...", "createdBy": "...", "isPrivate": "...", "createdAt": "...", "updatedAt": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/blocked-keywords" ``` ### POST `/v1/groups/{groupId}/blocked-keywords` Adds new blocked keyword(s) to the specified Group. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | | **Request Body:** `application/json` — Type: `Roblox.Groups.Api.CreateBlockedKeywordsRequest` See [Roblox.Groups.Api.CreateBlockedKeywordsRequest](#roblox-groups-api-createblockedkeywordsrequest) in Models. **Request example:** ```json { "keywords": "string", "isPrivate": false } ``` **Responses:** - `200`: OK → `Roblox.Groups.Client.CreateBlockedKeywordsResponse` - `400`: 1: Group is invalid or does not exist. 2: One or more keywords are invalid. 4: Invalid request. 12: The provided content was moderated. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 8: Insufficient permissions to complete the request. 9: User is invalid or does not exist. 10: Insufficient permissions to complete the request. - `405`: 31: Service is currently unavailable. - `409`: 11: There was a conflict in your request. **Response fields** (`Roblox.Groups.Client.CreateBlockedKeywordsResponse`) See [Roblox.Groups.Client.CreateBlockedKeywordsResponse](#roblox-groups-client-createblockedkeywordsresponse) in Models. **Response example:** ```json { "createdKeywords": [ { "id": "...", "keyword": "...", "createdBy": "...", "isPrivate": "...", "createdAt": "...", "updatedAt": "..." } ], "hadModeratedKeywords": false, "hadDuplicateKeywords": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/blocked-keywords" \ -H "Content-Type: application/json" \ -d '{ "keywords": "string", "isPrivate": false }' ``` ### GET `/v1/groups/{groupId}/community-feature-freezes` Gets the freeze status of the community features for a group. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | | **Responses:** - `200`: OK → `Roblox.Groups.Api.GetCommunityFeatureFreezesResponse` **Response fields** (`Roblox.Groups.Api.GetCommunityFeatureFreezesResponse`) See [Roblox.Groups.Api.GetCommunityFeatureFreezesResponse](#roblox-groups-api-getcommunityfeaturefreezesresponse) in Models. **Response example:** ```json { "features": [ { "feature": "...", "isDisabled": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/community-feature-freezes" ``` ### GET `/v1/groups/{groupId}/configuration` Gets group configuration information **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group Id. | **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupConfigurationDetailsResponse` - `400`: 1: Group is invalid or does not exist. 15: User is not a member of the group. - `403`: 1: Group is invalid or does not exist. 23: Insufficient permissions to complete the request. - `503`: 31: Service is currently unavailable. **Response fields** (`Roblox.Groups.Api.GroupConfigurationDetailsResponse`) See [Roblox.Groups.Api.GroupConfigurationDetailsResponse](#roblox-groups-api-groupconfigurationdetailsresponse) in Models. **Response example:** ```json { "groupId": 0, "emblemId": 0, "coverPhotoId": 0 } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/configuration" ``` ### GET `/v1/groups/{groupId}/emotes` Gets a groups emote sets. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | | **Responses:** - `200`: OK → `Roblox.Groups.Client.GetGroupEmoteSetsResponse` **Response fields** (`Roblox.Groups.Client.GetGroupEmoteSetsResponse`) See [Roblox.Groups.Client.GetGroupEmoteSetsResponse](#roblox-groups-client-getgroupemotesetsresponse) in Models. **Response example:** ```json { "emoteSets": [ { "id": "...", "name": "...", "emotes": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/emotes" ``` ### GET `/v1/groups/{groupId}/features` Gets the freeze status of all features and the lock status for a group. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | | **Responses:** - `200`: OK → `Roblox.Groups.Api.GetGroupFeaturesResponse` - `400`: 1: Group is invalid or does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 23: Insufficient permissions to complete the request. 49: User is invalid or does not exist **Response fields** (`Roblox.Groups.Api.GetGroupFeaturesResponse`) See [Roblox.Groups.Api.GetGroupFeaturesResponse](#roblox-groups-api-getgroupfeaturesresponse) in Models. **Response example:** ```json { "isLocked": false, "features": [ { "feature": "...", "isFeatureBlocked": "...", "expiration": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/features" ``` ### PATCH `/v1/groups/{groupId}/features` Sets the desired status of group features. Currently only removes active freezes for features set to Roblox.Groups.Api.FeatureStatus.On. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | | **Request Body:** `application/json` — Type: `Roblox.Groups.Api.SetFeaturesRequestModel` See [Roblox.Groups.Api.SetFeaturesRequestModel](#roblox-groups-api-setfeaturesrequestmodel) in Models. **Request example:** ```json { "Features": { "Payouts": "On", "ContentUpload": "On", "GroupOwnershipTransfer": "On", "GameOwnershipTransfer": "On", "ForumRead": "On", "ForumWrite": "On" } } ``` **Responses:** - `200`: OK → `Roblox.Groups.Api.SetFeaturesResponseModel` - `400`: 1: Group is invalid or does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 23: Insufficient permissions to complete the request. 49: User is invalid or does not exist **Response fields** (`Roblox.Groups.Api.SetFeaturesResponseModel`) See [Roblox.Groups.Api.SetFeaturesResponseModel](#roblox-groups-api-setfeaturesresponsemodel) in Models. **Response example:** ```json { "Updated": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/features" \ -H "Content-Type: application/json" \ -d '{ "Features": { "Payouts": "On", "ContentUpload": "On", "GroupOwnershipTransfer": "On", "GameOwnershipTransfer": "On", "ForumRead": "On", "ForumWrite": "On" } }' ``` ### GET `/v1/groups/{groupId}/features/status` Checks whether a group has ANY feature disabled (includes feature freezes and group lock). Used to display a banner on Creator Hub/Studio to inform group members that some features may not be available. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | | **Responses:** - `200`: OK → `Roblox.Groups.Api.HasGroupFeaturesBlockedResponse` - `400`: 1: Group is invalid or does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 23: Insufficient permissions to complete the request. 49: User is invalid or does not exist **Response fields** (`Roblox.Groups.Api.HasGroupFeaturesBlockedResponse`) See [Roblox.Groups.Api.HasGroupFeaturesBlockedResponse](#roblox-groups-api-hasgroupfeaturesblockedresponse) in Models. **Response example:** ```json { "hasFeaturesBlocked": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/features/status" ``` ### GET `/v1/groups/{groupId}/join-requests` Gets a page of Group Join Requests for a group. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group id. | | `limit` | query | `integer (int32)` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `string` | No | The order the results are sorted in. Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Groups.Api.GroupJoinRequestResponse]` - `400`: 1: The group is invalid or does not exist. 36: The pagination cursor is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: 19: You have insufficient permissions for this request. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Groups.Api.GroupJoinRequestResponse]`) See [Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Groups.Api.GroupJoinRequestResponse]](#roblox-web-webapi-models-apipageresponse-roblox-groups-api-groupjoinrequestresponse-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "requester": "...", "created": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/join-requests" ``` ### POST `/v1/groups/{groupId}/join-requests` Batch accepts group join requests **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group id. | **Request Body:** `application/json` — Type: `Roblox.Groups.Api.MembersRequest` See [Roblox.Groups.Api.MembersRequest](#roblox-groups-api-membersrequest) in Models. **Request example:** ```json { "UserIds": [ 0 ] } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: The group is invalid or does not exist. 3: The user is invalid or does not exist. 20: The group join request is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 6: You are already in the maximum number of groups. 19: You have insufficient permissions for this request. - `500`: 0: Something went wrong. - `503`: 18: The operation is temporarily unavailable. Please try again later. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/join-requests" \ -H "Content-Type: application/json" \ -d '{ "UserIds": [ 0 ] }' ``` ### DELETE `/v1/groups/{groupId}/join-requests` Batch declines group join requests **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group id. | **Request Body:** `application/json` — Type: `Roblox.Groups.Api.MembersRequest` See [Roblox.Groups.Api.MembersRequest](#roblox-groups-api-membersrequest) in Models. **Request example:** ```json { "UserIds": [ 0 ] } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: The group is invalid or does not exist. 3: The user is invalid or does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/join-requests" \ -H "Content-Type: application/json" \ -d '{ "UserIds": [ 0 ] }' ``` ### GET `/v1/groups/{groupId}/join-requests/users/{userId}` Gets a group join request by userId. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group Id. | | `userId` | path | `integer (int64)` | Yes | The user Id. | **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupJoinRequestResponse` - `400`: 1: The group is invalid or does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 19: You have insufficient permissions for this request. **Response fields** (`Roblox.Groups.Api.GroupJoinRequestResponse`) See [Roblox.Groups.Api.GroupJoinRequestResponse](#roblox-groups-api-groupjoinrequestresponse) in Models. **Response example:** ```json { "requester": { "buildersClubMembershipType": 0, "hasVerifiedBadge": false, "userId": 0, "username": "string", "displayName": "string" }, "created": "2024-01-01T00:00:00Z" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/join-requests/users/{USERID}" ``` ### POST `/v1/groups/{groupId}/join-requests/users/{userId}` Accepts a group join request. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group Id. | | `userId` | path | `integer (int64)` | Yes | The user Id. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: The group is invalid or does not exist. 3: The user is invalid or does not exist. 20: The group join request is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 6: You are already in the maximum number of groups. 19: You have insufficient permissions for this request. - `503`: 18: The operation is temporarily unavailable. Please try again later. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/join-requests/users/{USERID}" ``` ### DELETE `/v1/groups/{groupId}/join-requests/users/{userId}` Declines/cancels a group join request. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group Id. | | `userId` | path | `integer (int64)` | Yes | The user Id. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: The group is invalid or does not exist. 3: The user is invalid or does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 4: You do not have permission to manage this member. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/join-requests/users/{USERID}" ``` ### GET `/v1/groups/{groupId}/membership` Gets group membership information in the context of the authenticated user **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group Id. | | `includeNotificationPreferences` | query | `boolean` | Yes | | **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupMembershipMetadataResponse` - `400`: 1: The group is invalid or does not exist. **Response fields** (`Roblox.Groups.Api.GroupMembershipMetadataResponse`) See [Roblox.Groups.Api.GroupMembershipMetadataResponse](#roblox-groups-api-groupmembershipmetadataresponse) in Models. **Response example:** ```json { "groupId": 0, "isPrimary": false, "isPendingJoin": false, "userRole": { "user": { "buildersClubMembershipType": "...", "hasVerifiedBadge": "...", "userId": "...", "username": "...", "displayName": "..." }, "role": { "id": "...", "name": "...", "description": "...", "rank": "...", "memberCount": "...", "isBase": "..." } }, "permissions": { "groupPostsPermissions": { "viewWall": "...", "postToWall": "...", "deleteFromWall": "...", "viewStatus": "...", "postToStatus": "..." }, "groupForumsPermissions": { "viewForums": "...", "manageCategories": "...", "createPosts": "...", "removePosts": "...", "lockPosts": "...", "pinPosts": "..." }, "groupContentModerationPermissions": { "manageKeywordBlockList": "...", "viewKeywordBlockList": "..." }, "groupMembershipPermissions": { "changeRank": "...", "inviteMembers": "...", "removeMembers": "...", "banMembers": "..." }, "groupManagementPermissions": { "manageRelationships": "...", "manageClan": "...", "viewAuditLogs": "...", "bypassSlowmode": "...", "viewCommunityAnalytics": "..." }, "groupEconomyPermissions": { "spendGroupFunds": "...", "advertiseGroup": "...", "createItems": "...", "manageItems": "...", "addGroupPlaces": "...", "manageGroupGames": "..." } }, "channelPermissions": [ { "channelId": "...", "groupForumsPermissions": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/membership?includeNotificationPreferences={VALUE}" ``` ### GET `/v1/groups/{groupId}/name-history` Gets the Group's name change history. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The id of the group. | | `limit` | query | `integer (int32)` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `string` | No | The order the results are sorted in. Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Groups.Api.Models.Response.GroupNameHistoryResponseItem]` - `400`: 1: Group is invalid or does not exist. - `403`: 23: Insufficient permissions to complete the request. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Groups.Api.Models.Response.GroupNameHistoryResponseItem]`) See [Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Groups.Api.Models.Response.GroupNameHistoryResponseItem]](#roblox-web-webapi-models-apipageresponse-roblox-groups-api-models-response-groupnamehistoryresponseitem-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "name": "...", "created": "..." } ] } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/name-history" ``` ### GET `/v1/groups/{groupId}/payout-restriction` Gets a value indicating whether the group can use payout feature **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group id. | **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupPayoutRestrictionResponse` - `400`: 1: Group is invalid or does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 9: You don't have permission to view this group's payouts. **Response fields** (`Roblox.Groups.Api.GroupPayoutRestrictionResponse`) See [Roblox.Groups.Api.GroupPayoutRestrictionResponse](#roblox-groups-api-grouppayoutrestrictionresponse) in Models. **Response example:** ```json { "canUseRecurringPayout": false, "canUseOneTimePayout": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/payout-restriction" ``` ### GET `/v1/groups/{groupId}/payouts` Gets a list of the group payout percentages **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group id. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Groups.Api.GroupPayoutResponse]` - `400`: 1: Group is invalid or does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 9: You don't have permission to view this group's payouts. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Groups.Api.GroupPayoutResponse]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Groups.Api.GroupPayoutResponse]](#roblox-web-webapi-models-apiarrayresponse-roblox-groups-api-grouppayoutresponse-) in Models. **Response example:** ```json { "data": [ { "user": "...", "percentage": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/payouts" ``` ### POST `/v1/groups/{groupId}/payouts` Pays out a user in Robux. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group Id. | **Request Body:** `application/json` — Type: `Roblox.Groups.Api.PayoutRequest` See [Roblox.Groups.Api.PayoutRequest](#roblox-groups-api-payoutrequest) in Models. **Request example:** ```json { "PayoutType": 1, "Recipients": [ { "recipientId": "...", "recipientType": "...", "amount": "..." } ], "IdempotencyKey": "string", "WatermarkContributions": [ { "balanceKey": "...", "amount": "..." } ] } ``` **Responses:** - `200`: OK → `Roblox.Groups.Api.OneTimePayoutResponse` - `400`: 1: Group is invalid or does not exist. 12: Insufficient Robux funds. 24: Invalid payout type. 25: The amount is invalid. 26: Too many recipients. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 23: Insufficient permissions to complete the request. 28: Group has paid out too recently. Please wait and try again. 35: 2-Step Verification is required to make further transactions. Go to Settings > Security to complete 2-Step Verification. 52: Group has paid out to this recipient too many times recently. Please try again later. - `503`: 22: The feature is disabled. **Response fields** (`Roblox.Groups.Api.OneTimePayoutResponse`) See [Roblox.Groups.Api.OneTimePayoutResponse](#roblox-groups-api-onetimepayoutresponse) in Models. **Response example:** ```json { "status": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/payouts" \ -H "Content-Type: application/json" \ -d '{ "PayoutType": 1, "Recipients": [ { "recipientId": "...", "recipientType": "...", "amount": "..." } ], "IdempotencyKey": "string", "WatermarkContributions": [ { "balanceKey": "...", "amount": "..." } ] }' ``` ### GET `/v1/groups/{groupId}/relationships/{groupRelationshipType}` Gets a group's relationships **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group Id. | | `groupRelationshipType` | path | `string` | Yes | The group relationship type, enemies or allies. | | `StartRowIndex` | query | `integer (int32)` | Yes | The start index of the page request | | `MaxRows` | query | `integer (int32)` | Yes | The maximum number of rows for the page request, should be at least 1. | **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupRelationshipsResponse` - `400`: 1: Group is invalid or does not exist. 4: Group relationship type or request type is invalid. 8: Invalid or missing pagination parameters **Response fields** (`Roblox.Groups.Api.GroupRelationshipsResponse`) See [Roblox.Groups.Api.GroupRelationshipsResponse](#roblox-groups-api-grouprelationshipsresponse) in Models. **Response example:** ```json { "groupId": 0, "relationshipType": 1, "totalGroupCount": 0, "relatedGroups": [ { "id": "...", "name": "...", "description": "...", "owner": "...", "shout": "...", "memberCount": "..." } ], "nextRowIndex": 0 } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/relationships/{GROUPRELATIONSHIPTYPE}?StartRowIndex={VALUE}&MaxRows={VALUE}" ``` ### GET `/v1/groups/{groupId}/relationships/{groupRelationshipType}/requests` Gets a group's relationship requests **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group Id. | | `groupRelationshipType` | path | `string` | Yes | The group relationship type of the request, only allies are supported. | | `StartRowIndex` | query | `integer (int32)` | Yes | The start index of the page request | | `MaxRows` | query | `integer (int32)` | Yes | The maximum number of rows for the page request, should be at least 1. | **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupRelationshipsResponse` - `400`: 1: Group is invalid or does not exist. 4: Group relationship type or request type is invalid. 8: Invalid or missing pagination parameters - `401`: 0: Authorization has been denied for this request. - `403`: 5: You don't have permission to manage this group's relationships. **Response fields** (`Roblox.Groups.Api.GroupRelationshipsResponse`) See [Roblox.Groups.Api.GroupRelationshipsResponse](#roblox-groups-api-grouprelationshipsresponse) in Models. **Response example:** ```json { "groupId": 0, "relationshipType": 1, "totalGroupCount": 0, "relatedGroups": [ { "id": "...", "name": "...", "description": "...", "owner": "...", "shout": "...", "memberCount": "..." } ], "nextRowIndex": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/relationships/{GROUPRELATIONSHIPTYPE}/requests?StartRowIndex={VALUE}&MaxRows={VALUE}" ``` ### POST `/v1/groups/{groupId}/relationships/{groupRelationshipType}/requests` Batch accepts group affiliate requests **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group id. | | `groupRelationshipType` | path | `string` | Yes | The type of group relationship being made | **Request Body:** `application/json` — Type: `Roblox.Groups.Api.RelationshipsRequest` See [Roblox.Groups.Api.RelationshipsRequest](#roblox-groups-api-relationshipsrequest) in Models. **Request example:** ```json { "GroupIds": [ 0 ] } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/relationships/{GROUPRELATIONSHIPTYPE}/requests" \ -H "Content-Type: application/json" \ -d '{ "GroupIds": [ 0 ] }' ``` ### DELETE `/v1/groups/{groupId}/relationships/{groupRelationshipType}/requests` Batch declines group affiliate requests **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group id. | | `groupRelationshipType` | path | `string` | Yes | The type of group relationship being made | **Request Body:** `application/json` — Type: `Roblox.Groups.Api.RelationshipsRequest` See [Roblox.Groups.Api.RelationshipsRequest](#roblox-groups-api-relationshipsrequest) in Models. **Request example:** ```json { "GroupIds": [ 0 ] } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/relationships/{GROUPRELATIONSHIPTYPE}/requests" \ -H "Content-Type: application/json" \ -d '{ "GroupIds": [ 0 ] }' ``` ### GET `/v1/groups/{groupId}/roles` Gets a list of the rolesets in a group. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group id. | **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupAllRolesResponse` - `400`: 1: The group is invalid or does not exist. **Response fields** (`Roblox.Groups.Api.GroupAllRolesResponse`) See [Roblox.Groups.Api.GroupAllRolesResponse](#roblox-groups-api-groupallrolesresponse) in Models. **Response example:** ```json { "groupId": 0, "roles": [ { "id": "...", "name": "...", "description": "...", "rank": "...", "memberCount": "...", "isBase": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/roles" ``` ### GET `/v1/groups/{groupId}/roles/{roleSetId}/permissions` Gets the permissions for a group's roleset. The authorized user must either be the group owner or the roleset being requested, except for guest roles, which can be viewed by all (members and guests). **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group id. | | `roleSetId` | path | `integer (int64)` | Yes | The group's role set id. | **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupPermissionsResponse` - `400`: 1: Group is invalid or does not exist. 2: The roleset is invalid or does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 3: You are not authorized to view/edit permissions for this role. **Response fields** (`Roblox.Groups.Api.GroupPermissionsResponse`) See [Roblox.Groups.Api.GroupPermissionsResponse](#roblox-groups-api-grouppermissionsresponse) in Models. **Response example:** ```json { "groupId": 0, "role": { "id": 0, "name": "string", "description": "string", "rank": 0, "memberCount": 0, "isBase": false }, "permissions": { "groupPostsPermissions": { "viewWall": "...", "postToWall": "...", "deleteFromWall": "...", "viewStatus": "...", "postToStatus": "..." }, "groupForumsPermissions": { "viewForums": "...", "manageCategories": "...", "createPosts": "...", "removePosts": "...", "lockPosts": "...", "pinPosts": "..." }, "groupContentModerationPermissions": { "manageKeywordBlockList": "...", "viewKeywordBlockList": "..." }, "groupMembershipPermissions": { "changeRank": "...", "inviteMembers": "...", "removeMembers": "...", "banMembers": "..." }, "groupManagementPermissions": { "manageRelationships": "...", "manageClan": "...", "viewAuditLogs": "...", "bypassSlowmode": "...", "viewCommunityAnalytics": "..." }, "groupEconomyPermissions": { "spendGroupFunds": "...", "advertiseGroup": "...", "createItems": "...", "manageItems": "...", "addGroupPlaces": "...", "manageGroupGames": "..." } } } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/roles/{ROLESETID}/permissions" ``` ### PATCH `/v1/groups/{groupId}/roles/{roleSetId}/permissions` Updates the permissions for a group's roleset. The authorized user must be the group owner. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group's id. | | `roleSetId` | path | `integer (int64)` | Yes | The roleset's id. | **Request Body:** `application/json` — Type: `Roblox.Groups.Api.UpdatePermissionsRequest` See [Roblox.Groups.Api.UpdatePermissionsRequest](#roblox-groups-api-updatepermissionsrequest) in Models. **Request example:** ```json { "permissions": { "DeleteFromWall": false, "PostToWall": false, "InviteMembers": false, "PostToStatus": false, "RemoveMembers": false, "BanMembers": false } } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: Group is invalid or does not exist. 2: The roleset is invalid or does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 3: You are not authorized to view/edit permissions for this role. 4: This role's permissions can not be modified. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/roles/{ROLESETID}/permissions" \ -H "Content-Type: application/json" \ -d '{ "permissions": { "DeleteFromWall": false, "PostToWall": false, "InviteMembers": false, "PostToStatus": false, "RemoveMembers": false, "BanMembers": false } }' ``` ### GET `/v1/groups/{groupId}/roles/{roleSetId}/users` Gets a list of users in a group for a specific roleset. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group id. | | `roleSetId` | path | `integer (int64)` | Yes | The group's role set id. | | `limit` | query | `integer (int32)` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `string` | No | The order the results are sorted in. Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Groups.Api.Models.Response.UserModel]` - `400`: 1: The group is invalid or does not exist. 36: The pagination cursor is invalid. - `403`: 2: The roleset is invalid or does not exist. 35: You do not have permission to view this group's member list. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Groups.Api.Models.Response.UserModel]`) See [Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Groups.Api.Models.Response.UserModel]](#roblox-web-webapi-models-apipageresponse-roblox-groups-api-models-response-usermodel-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "buildersClubMembershipType": "...", "hasVerifiedBadge": "...", "userId": "...", "username": "...", "displayName": "..." } ] } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/roles/{ROLESETID}/users" ``` ### GET `/v1/groups/{groupId}/roles/guest/permissions` Gets the permissions for a group's guest roleset. These can be viewed by all (members and guests) users. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group id. | **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupPermissionsResponse` - `400`: 1: Group is invalid or does not exist. **Response fields** (`Roblox.Groups.Api.GroupPermissionsResponse`) See [Roblox.Groups.Api.GroupPermissionsResponse](#roblox-groups-api-grouppermissionsresponse) in Models. **Response example:** ```json { "groupId": 0, "role": { "id": 0, "name": "string", "description": "string", "rank": 0, "memberCount": 0, "isBase": false }, "permissions": { "groupPostsPermissions": { "viewWall": "...", "postToWall": "...", "deleteFromWall": "...", "viewStatus": "...", "postToStatus": "..." }, "groupForumsPermissions": { "viewForums": "...", "manageCategories": "...", "createPosts": "...", "removePosts": "...", "lockPosts": "...", "pinPosts": "..." }, "groupContentModerationPermissions": { "manageKeywordBlockList": "...", "viewKeywordBlockList": "..." }, "groupMembershipPermissions": { "changeRank": "...", "inviteMembers": "...", "removeMembers": "...", "banMembers": "..." }, "groupManagementPermissions": { "manageRelationships": "...", "manageClan": "...", "viewAuditLogs": "...", "bypassSlowmode": "...", "viewCommunityAnalytics": "..." }, "groupEconomyPermissions": { "spendGroupFunds": "...", "advertiseGroup": "...", "createItems": "...", "manageItems": "...", "addGroupPlaces": "...", "manageGroupGames": "..." } } } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/roles/guest/permissions" ``` ### GET `/v1/groups/{groupId}/roles/permissions` Gets all permissions for each role **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group id. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Groups.Api.GroupPermissionsResponse]` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Groups.Api.GroupPermissionsResponse]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Groups.Api.GroupPermissionsResponse]](#roblox-web-webapi-models-apiarrayresponse-roblox-groups-api-grouppermissionsresponse-) in Models. **Response example:** ```json { "data": [ { "groupId": "...", "role": "...", "permissions": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/roles/permissions" ``` ### GET `/v1/groups/{groupId}/settings` Gets the Group's settings **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The id of the group the user is in. | **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupSettingsResponse` - `400`: 1: Group is invalid or does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 23: Insufficient permissions to complete the request. **Response fields** (`Roblox.Groups.Api.GroupSettingsResponse`) See [Roblox.Groups.Api.GroupSettingsResponse](#roblox-groups-api-groupsettingsresponse) in Models. **Response example:** ```json { "isApprovalRequired": false, "isBuildersClubRequired": false, "areEnemiesAllowed": false, "areGroupFundsVisible": false, "areGroupGamesVisible": false, "isGroupNameChangeEnabled": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/settings" ``` ### PATCH `/v1/groups/{groupId}/settings` Updates the group's settings **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The id of the group the user is in. | **Request Body:** `application/json` — Type: `Roblox.Groups.Api.UpdateGroupSettingsRequest` See [Roblox.Groups.Api.UpdateGroupSettingsRequest](#roblox-groups-api-updategroupsettingsrequest) in Models. **Request example:** ```json { "isApprovalRequired": false, "areEnemiesAllowed": false, "areGroupFundsVisible": false, "areGroupGamesVisible": false, "verificationLevel": 0, "accountTenureRequirement": 0 } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: Group is invalid or does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 23: Insufficient permissions to complete the request. - `503`: 31: Service is currently unavailable. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/settings" \ -H "Content-Type: application/json" \ -d '{ "isApprovalRequired": false, "areEnemiesAllowed": false, "areGroupFundsVisible": false, "areGroupGamesVisible": false, "verificationLevel": 0, "accountTenureRequirement": 0 }' ``` ### GET `/v1/groups/{groupId}/social-links` Get social link data associated with a group **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The Id of the game | **Responses:** - `200`: OK → `Roblox.Groups.Api.GetSocialLinkResponse` - `400`: 1: Group is invalid or does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 13: Only users who are over thirteen years of age may edit social links. - `404`: 11: Social links cannot be processed as this time. **Response fields** (`Roblox.Groups.Api.GetSocialLinkResponse`) See [Roblox.Groups.Api.GetSocialLinkResponse](#roblox-groups-api-getsociallinkresponse) in Models. **Response example:** ```json { "data": [ { "id": "...", "type": "...", "url": "...", "title": "..." } ], "socialLinksVerificationStatus": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/social-links" ``` ### POST `/v1/groups/{groupId}/social-links` Posts a social links **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The id of the group | **Request Body:** `application/json` — Type: `Roblox.Groups.Api.SocialLinkRequest` See [Roblox.Groups.Api.SocialLinkRequest](#roblox-groups-api-sociallinkrequest) in Models. **Request example:** ```json { "type": 0, "url": "string", "title": "string" } ``` **Responses:** - `200`: OK → `Roblox.Groups.Api.SocialLinkResponse` - `400`: 3: The social link title is too long. 4: The social link title cannot be empty. 5: The social link url cannot be empty. 7: The request was null. 9: The social link type is invalid. 12: The social link title was moderated. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: You do not have permission to configure this social link. - `404`: 8: The requested group or social link was not found. - `503`: 11: Social links cannot be processed as this time. **Response fields** (`Roblox.Groups.Api.SocialLinkResponse`) See [Roblox.Groups.Api.SocialLinkResponse](#roblox-groups-api-sociallinkresponse) in Models. **Response example:** ```json { "id": 0, "type": 0, "url": "string", "title": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/social-links" \ -H "Content-Type: application/json" \ -d '{ "type": 0, "url": "string", "title": "string" }' ``` ### GET `/v1/groups/{groupId}/users` Gets a list of users in a group. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group id. | | `limit` | query | `integer (int32)` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `string` | No | The order the results are sorted in. Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Groups.Api.UserGroupRoleResponse]` - `400`: 1: The group is invalid or does not exist. 36: The pagination cursor is invalid. - `403`: 35: You do not have permission to view this group's member list. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Groups.Api.UserGroupRoleResponse]`) See [Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Groups.Api.UserGroupRoleResponse]](#roblox-web-webapi-models-apipageresponse-roblox-groups-api-usergrouproleresponse-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "user": "...", "role": "..." } ] } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/users" ``` ### POST `/v1/groups/{groupId}/users` Joins a group **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group Id. | | `Roblox-Place-Id` | header | `integer (int64)` | No | The place ID of the experience the player is in. | | `Roblox-Game-Id` | header | `string` | No | The player's current game Id. | | `Roblox-Session-Id` | header | `string` | No | The player's current session Id. | **Request Body:** `application/json` — Type: `Roblox.Groups.Api.JoinGroupRequest` See [Roblox.Groups.Api.JoinGroupRequest](#roblox-groups-api-joingrouprequest) in Models. **Request example:** ```json { "sessionId": "string", "redemptionToken": "string", "captchaId": "string", "captchaToken": "string", "captchaProvider": "string", "challengeId": "string" } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: The group is invalid or does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 6: You are already in the maximum number of groups. 9: You do not have the builders club membership necessary to join this group. 14: You cannot join a closed group. 33: You do not have the required verification level to join this group. 34: You do not have the required account tenure to join this group. - `409`: 7: You have already requested to join this group. 8: You are already a member of this group. - `429`: 10: Too many attempts to join the group. Please try again later. - `503`: 18: The operation is temporarily unavailable. Please try again later. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/users" \ -H "Content-Type: application/json" \ -d '{ "sessionId": "string", "redemptionToken": "string", "captchaId": "string", "captchaToken": "string", "captchaProvider": "string", "challengeId": "string" }' ``` ### GET `/v1/groups/{groupId}/users/{userId}/permissions` Gets the permissions a user has in a group. Only available to group owner and RCC **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group id. | | `userId` | path | `integer (int64)` | Yes | The user id. | **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupPermissionsResponse` - `400`: 1: Group is invalid or does not exist. - `403`: 3: You are not authorized to view/edit permissions for this role. - `404`: 3: The user is invalid or does not exist. **Response fields** (`Roblox.Groups.Api.GroupPermissionsResponse`) See [Roblox.Groups.Api.GroupPermissionsResponse](#roblox-groups-api-grouppermissionsresponse) in Models. **Response example:** ```json { "groupId": 0, "role": { "id": 0, "name": "string", "description": "string", "rank": 0, "memberCount": 0, "isBase": false }, "permissions": { "groupPostsPermissions": { "viewWall": "...", "postToWall": "...", "deleteFromWall": "...", "viewStatus": "...", "postToStatus": "..." }, "groupForumsPermissions": { "viewForums": "...", "manageCategories": "...", "createPosts": "...", "removePosts": "...", "lockPosts": "...", "pinPosts": "..." }, "groupContentModerationPermissions": { "manageKeywordBlockList": "...", "viewKeywordBlockList": "..." }, "groupMembershipPermissions": { "changeRank": "...", "inviteMembers": "...", "removeMembers": "...", "banMembers": "..." }, "groupManagementPermissions": { "manageRelationships": "...", "manageClan": "...", "viewAuditLogs": "...", "bypassSlowmode": "...", "viewCommunityAnalytics": "..." }, "groupEconomyPermissions": { "spendGroupFunds": "...", "advertiseGroup": "...", "createItems": "...", "manageItems": "...", "addGroupPlaces": "...", "manageGroupGames": "..." } } } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/users/{USERID}/permissions" ``` ### GET `/v1/groups/{groupId}/wall/posts` Gets a list of group wall posts. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group id. | | `limit` | query | `integer (int32)` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `string` | No | Sorted by group wall post Id Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Groups.Api.Models.Response.GroupWallPostModel]` - `400`: 1: The group is invalid or does not exist. - `403`: 2: You do not have permission to access this group wall. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Groups.Api.Models.Response.GroupWallPostModel]`) See [Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Groups.Api.Models.Response.GroupWallPostModel]](#roblox-web-webapi-models-apipageresponse-roblox-groups-api-models-response-groupwallpostmodel-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "id": "...", "poster": "...", "body": "...", "created": "...", "updated": "..." } ] } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/wall/posts" ``` ### POST `/v1/groups/{groupId}/wall/posts` Creates a post on a group wall **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group id. | **Request Body:** `application/json` — Type: `Roblox.Groups.Api.CreateWallPostRequest` See [Roblox.Groups.Api.CreateWallPostRequest](#roblox-groups-api-createwallpostrequest) in Models. **Request example:** ```json { "body": "string", "captchaId": "string", "captchaToken": "string", "captchaProvider": "string", "challengeId": "string" } ``` **Responses:** - `200`: OK → `Roblox.Groups.Api.Models.Response.GroupWallPostModel` - `400`: 1: The group is invalid or does not exist. 5: Your post was empty, white space, or more than 500 characters. 9: The provided content was moderated. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: You do not have permission to access this group wall. - `405`: 1: The group is invalid or does not exist. - `429`: 4: You are posting too fast, please try again in a few minutes. **Response fields** (`Roblox.Groups.Api.Models.Response.GroupWallPostModel`) See [Roblox.Groups.Api.Models.Response.GroupWallPostModel](#roblox-groups-api-models-response-groupwallpostmodel) in Models. **Response example:** ```json { "id": 0, "poster": { "buildersClubMembershipType": 0, "hasVerifiedBadge": false, "userId": 0, "username": "string", "displayName": "string" }, "body": "string", "created": "2024-01-01T00:00:00Z", "updated": "2024-01-01T00:00:00Z" } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/wall/posts" \ -H "Content-Type: application/json" \ -d '{ "body": "string", "captchaId": "string", "captchaToken": "string", "captchaProvider": "string", "challengeId": "string" }' ``` ### GET `/v1/groups/configuration/metadata` Gets Group configuration contextual information **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupConfigurationDisplayOptionsResponse` **Response fields** (`Roblox.Groups.Api.GroupConfigurationDisplayOptionsResponse`) See [Roblox.Groups.Api.GroupConfigurationDisplayOptionsResponse](#roblox-groups-api-groupconfigurationdisplayoptionsresponse) in Models. **Response example:** ```json { "groupConfiguration": { "nameMaxLength": 0, "descriptionMaxLength": 0, "iconMaxFileSizeMb": 0, "coverPhotoMaxFileSizeMb": 0, "validCoverPhotoDimensions": "string", "cost": 0 }, "recurringPayoutsConfiguration": { "maxPayoutPartners": 0 }, "roleConfiguration": { "nameMaxLength": 0, "descriptionMaxLength": 0, "limit": 0, "cost": 0, "minRank": 0, "maxRank": 0 }, "groupNameChangeConfiguration": { "cost": 0, "cooldownInDays": 0, "ownershipCooldownInDays": 0 }, "isPremiumPayoutsEnabled": false, "isDefaultEmblemPolicyEnabled": false } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/configuration/metadata" ``` ### GET `/v1/groups/metadata` Gets Groups contextual information: Max number of groups a user can be part of. Current number of groups a user is a member of. Whether to show/hide certain features based on device type. **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupsDisplayOptionsResponse` **Response fields** (`Roblox.Groups.Api.GroupsDisplayOptionsResponse`) See [Roblox.Groups.Api.GroupsDisplayOptionsResponse](#roblox-groups-api-groupsdisplayoptionsresponse) in Models. **Response example:** ```json { "groupLimit": 0, "currentGroupCount": 0, "groupStatusMaxLength": 0, "groupPostMaxLength": 0, "isGroupWallNotificationsEnabled": false, "groupWallNotificationsSubscribeIntervalInMilliseconds": 0 } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/metadata" ``` ### GET `/v1/groups/search` Search for groups by keyword. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `keyword` | query | `string` | Yes | The keyword or phrase to use as the search parameter. | | `prioritizeExactMatch` | query | `boolean` | No | Whether or not to prioritize the exact match for the keyword (optional, defaults to false). | | `limit` | query | `integer (int32)` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupSearchPageResponse` - `400`: 2: Search term not appropriate for Roblox. 3: Search term was left empty. 4: Search terms can be 2 to 50 characters long. **Response fields** (`Roblox.Groups.Api.GroupSearchPageResponse`) See [Roblox.Groups.Api.GroupSearchPageResponse](#roblox-groups-api-groupsearchpageresponse) in Models. **Response example:** ```json { "keyword": "string", "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "id": "...", "name": "...", "description": "...", "memberCount": "...", "previousName": "...", "publicEntryAllowed": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/search?keyword={VALUE}" ``` ### GET `/v1/groups/search/lookup` Looks up groups by a name. Prioritizes an exact match as the first result. Should only be used for direct lookups where a user is inputting a group name, shouldn't be used for search pages. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupName` | query | `string` | Yes | The group name. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Web.Responses.Groups.GroupBasicResponse]` - `400`: 1: Name is missing or has invalid characters. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Web.Responses.Groups.GroupBasicResponse]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Web.Responses.Groups.GroupBasicResponse]](#roblox-web-webapi-models-apiarrayresponse-roblox-web-responses-groups-groupbasicresponse-) in Models. **Response example:** ```json { "data": [ { "id": "...", "name": "...", "memberCount": "...", "hasVerifiedBadge": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/search/lookup?groupName={VALUE}" ``` ### GET `/v1/groups/search/metadata` Get suggested groups and other miscellaneous information needed for the group/join page like flags Although there is no reason for this to require an authenticated user right now, in the future, we will use coco to return different suggested groups based upon that user's request context **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupSearchMetadataResponse` - `404`: 5: No Localized Version of group search category exists **Response fields** (`Roblox.Groups.Api.GroupSearchMetadataResponse`) See [Roblox.Groups.Api.GroupSearchMetadataResponse](#roblox-groups-api-groupsearchmetadataresponse) in Models. **Response example:** ```json { "SuggestedGroupKeywords": [ "string" ], "ShowFriendsGroupsSort": false } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/search/metadata" ``` ### GET `/v1/roles` Gets the Roles by their ids. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `ids` | query | `array` | Yes | A list of role ids | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Groups.Api.GroupRoleDetailResponse]` - `400`: 1: Ids could not be parsed from request. 2: Too many ids in request. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Groups.Api.GroupRoleDetailResponse]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Groups.Api.GroupRoleDetailResponse]](#roblox-web-webapi-models-apiarrayresponse-roblox-groups-api-grouproledetailresponse-) in Models. **Response example:** ```json { "data": [ { "groupId": "...", "id": "...", "name": "...", "description": "...", "rank": "...", "memberCount": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/roles?ids={VALUE}" ``` ### GET `/v1/user/groups/pending` Gets groups that the authenticated user has requested to join **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Groups.Api.GroupDetailResponse]` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Groups.Api.GroupDetailResponse]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Groups.Api.GroupDetailResponse]](#roblox-web-webapi-models-apiarrayresponse-roblox-groups-api-groupdetailresponse-) in Models. **Response example:** ```json { "data": [ { "id": "...", "name": "...", "description": "...", "owner": "...", "shout": "...", "memberCount": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/user/groups/pending" ``` ### GET `/v1/users/{userId}/friends/groups/roles` Gets a list of all groups the specified users' friends are in. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user id. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Groups.Api.UserGroupMembershipResponse]` - `400`: 3: The user is invalid or does not exist. - `403`: 3: The user is invalid or does not exist. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Groups.Api.UserGroupMembershipResponse]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Groups.Api.UserGroupMembershipResponse]](#roblox-web-webapi-models-apiarrayresponse-roblox-groups-api-usergroupmembershipresponse-) in Models. **Response example:** ```json { "data": [ { "user": "...", "groups": "..." } ] } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/users/{USERID}/friends/groups/roles" ``` ### GET `/v1/users/{userId}/groups/primary/role` Gets a user's primary group. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user id. | **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupMembershipDetailResponse` - `400`: 4: User is invalid or does not exist. **Response fields** (`Roblox.Groups.Api.GroupMembershipDetailResponse`) See [Roblox.Groups.Api.GroupMembershipDetailResponse](#roblox-groups-api-groupmembershipdetailresponse) in Models. **Response example:** ```json { "group": { "id": 0, "name": "string", "description": "string", "owner": { "buildersClubMembershipType": "...", "hasVerifiedBadge": "...", "userId": "...", "username": "...", "displayName": "..." }, "shout": { "body": "...", "poster": "...", "created": "...", "updated": "..." }, "memberCount": 0 }, "role": { "id": 0, "name": "string", "description": "string", "rank": 0, "memberCount": 0, "isBase": false }, "isPrimaryGroup": false, "isNotificationsEnabled": false, "notificationPreferences": [ { "type": "...", "enabled": "...", "name": "...", "description": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/users/{USERID}/groups/primary/role" ``` ### GET `/v1/users/{userId}/groups/roles` Gets a list of all group roles for groups the specified user is in. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user id. | | `includeLocked` | query | `boolean` | No | | | `includeNotificationPreferences` | query | `boolean` | No | | | `discoveryType` | query | `integer (int32)` | No | Valid values: `0`, `1` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Groups.Api.GroupMembershipDetailResponse]` - `400`: 3: The user is invalid or does not exist. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Groups.Api.GroupMembershipDetailResponse]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Groups.Api.GroupMembershipDetailResponse]](#roblox-web-webapi-models-apiarrayresponse-roblox-groups-api-groupmembershipdetailresponse-) in Models. **Response example:** ```json { "data": [ { "group": "...", "role": "...", "isPrimaryGroup": "...", "isNotificationsEnabled": "...", "notificationPreferences": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/users/{USERID}/groups/roles" ``` ### POST `/v1/groups/{groupId}/change-owner` Changes the group owner to another user. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group Id. | **Request Body:** `application/json` — Type: `Roblox.Groups.Api.ChangeOwnerRequest` See [Roblox.Groups.Api.ChangeOwnerRequest](#roblox-groups-api-changeownerrequest) in Models. **Request example:** ```json { "userId": 0 } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: The group is invalid or does not exist. 3: The user is invalid or does not exist. 15: User is not a member of the group. 16: The user does not have the necessary level of premium membership. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 17: You are not authorized to change the owner of this group. 25: 2-Step Verification is required to make further transactions. Go to Settings > Security to complete 2-Step Verification. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/change-owner" \ -H "Content-Type: application/json" \ -d '{ "userId": 0 }' ``` ### POST `/v1/groups/{groupId}/claim-ownership` Claims ownership of the group as the authenticated user **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group Id. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: The group is invalid or does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 11: You are not authorized to claim this group 12: This group already has an owner 13: Too many attempts to claim groups. Please try again later. - `503`: 18: The operation is temporarily unavailable. Please try again later. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/claim-ownership" ``` ### POST `/v1/groups/{groupId}/payouts/recurring` Updates recurring payouts. This endpoint will remove any recipients not sent in the request. If a recipient in the request is not a valid member in the group they will not be added to the recurring payouts. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group Id. | **Request Body:** `application/json` — Type: `Roblox.Groups.Api.PayoutRequest` See [Roblox.Groups.Api.PayoutRequest](#roblox-groups-api-payoutrequest) in Models. **Request example:** ```json { "PayoutType": 1, "Recipients": [ { "recipientId": "...", "recipientType": "...", "amount": "..." } ], "IdempotencyKey": "string", "WatermarkContributions": [ { "balanceKey": "...", "amount": "..." } ] } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: Group is invalid or does not exist. 24: Invalid payout type. 25: The amount is invalid. 26: Too many recipients. 27: The recipients are invalid. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 12: Insufficient Robux funds. 28: Group has paid out too recently. Please wait and try again. 35: 2-Step Verification is required to make further transactions. Go to Settings > Security to complete 2-Step Verification. - `503`: 22: The feature is disabled. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/payouts/recurring" \ -H "Content-Type: application/json" \ -d '{ "PayoutType": 1, "Recipients": [ { "recipientId": "...", "recipientType": "...", "amount": "..." } ], "IdempotencyKey": "string", "WatermarkContributions": [ { "balanceKey": "...", "amount": "..." } ] }' ``` ### POST `/v1/groups/{groupId}/relationships/{groupRelationshipType}/{relatedGroupId}` Create a group relationship. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group id. | | `groupRelationshipType` | path | `string` | Yes | The group relationship type, enemies or allies. | | `relatedGroupId` | path | `integer (int64)` | Yes | The id of the group you want to create a relationship with. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: Group relationship type or request type is invalid. 2: Invalid group. 3: Target group is invalid or does not exist. 4: Your group cannot establish a relationship with itself. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 5: Your group does not allow enemy declarations. 6: Other group does not allow enemy declarations. 7: Your group already has a relationship with the target group. 8: You are blocked from communicating with this user. 9: Insufficient permissions. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/relationships/{GROUPRELATIONSHIPTYPE}/{RELATEDGROUPID}" ``` ### DELETE `/v1/groups/{groupId}/relationships/{groupRelationshipType}/{relatedGroupId}` Deletes a group relationship. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group id. | | `groupRelationshipType` | path | `string` | Yes | The group relationship type, enemies or allies. | | `relatedGroupId` | path | `integer (int64)` | Yes | The id of the group you want to delete the relationship with. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 2: Invalid group. 3: Target group is invalid or does not exist. 11: Relationship does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 8: You are blocked from communicating with this user. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/relationships/{GROUPRELATIONSHIPTYPE}/{RELATEDGROUPID}" ``` ### POST `/v1/groups/{groupId}/relationships/{groupRelationshipType}/requests/{relatedGroupId}` Accepts a group relationship request. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group id. | | `groupRelationshipType` | path | `string` | Yes | The group relationship type, enemies or allies, only allies are supported. | | `relatedGroupId` | path | `integer (int64)` | Yes | The id of the group you want to accept the relationship request with. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: Group relationship type or request type is invalid. 2: Invalid group. 3: Target group is invalid or does not exist. 10: Relationship request does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 9: Insufficient permissions. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/relationships/{GROUPRELATIONSHIPTYPE}/requests/{RELATEDGROUPID}" ``` ### DELETE `/v1/groups/{groupId}/relationships/{groupRelationshipType}/requests/{relatedGroupId}` Declines a group relationship request. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group id. | | `groupRelationshipType` | path | `string` | Yes | The group relationship type, enemies or allies. | | `relatedGroupId` | path | `integer (int64)` | Yes | The id of the group you want to accept the relationship request with. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: Group relationship type or request type is invalid. 2: Invalid group. 3: Target group is invalid or does not exist. 10: Relationship request does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 9: Insufficient permissions. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/relationships/{GROUPRELATIONSHIPTYPE}/requests/{RELATEDGROUPID}" ``` ### POST `/v1/groups/{groupId}/rolesets/create` Creates new group roleset. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group Id. | **Request Body:** `application/json` — Type: `Roblox.Groups.Api.Models.Request.CreateRoleSetRequest` See [Roblox.Groups.Api.Models.Request.CreateRoleSetRequest](#roblox-groups-api-models-request-createrolesetrequest) in Models. **Request example:** ```json { "name": "string", "description": "string", "rank": 0, "usingGroupFunds": false } ``` **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupRoleResponse` - `400`: 0: Something went wrong. 3: You do not have enough funds to purchase this role. 5: Role name already exists. 6: Rank value is out of bounds. 7: The role name is too long. 8: The role description is too long. 10: This group does not exist. 11: Failed to process payment to purchase role. 12: Limit for roles have been reached on this group. 14: Role name can not be empty. 15: This role does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 9: You do not have permissions to perform this action. **Response fields** (`Roblox.Groups.Api.GroupRoleResponse`) See [Roblox.Groups.Api.GroupRoleResponse](#roblox-groups-api-grouproleresponse) in Models. **Response example:** ```json { "id": 0, "name": "string", "description": "string", "rank": 0, "memberCount": 0, "isBase": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/rolesets/create" \ -H "Content-Type: application/json" \ -d '{ "name": "string", "description": "string", "rank": 0, "usingGroupFunds": false }' ``` ### POST `/v1/groups/{groupId}/wall/subscribe` Subscribes the authenticated user to notifications of group wall events. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group id. | **Responses:** - `200`: OK - `400`: 1: The group is invalid or does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: You do not have permission to access this group wall. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/wall/subscribe" ``` ### POST `/v1/groups/create` Creates a new group. This endpoint will charge Robux for the group purchase. Accepts "icon" and "coverPhoto" in Files object. Defaults to first file if "icon" is not present. Http status code 413 is thrown when the group icon file size is too large. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `name` | formData | `string` | No | The name of the group. | | `description` | formData | `string` | No | The group description. | | `publicGroup` | formData | `boolean` | No | Whether or not the group is open for anyone to join. | | `buildersClubMembersOnly` | formData | `boolean` | No | Whether or not the group is only open to join for builders club members. | | `Files` | formData | `file` | No | | **Responses:** - `200`: OK → `Roblox.Web.Responses.Groups.GroupResponseV2` - `400`: 13: The name is invalid. 15: The group icon is invalid. 16: The group icon is missing from the request. 18: The description is too long. 19: The name is too long. 20: The name has been taken. 46: Invalid file format or dimensions for group cover photo. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 10: User must have builders club membership. 11: User is in maximum number of groups. 12: Insufficient Robux funds. 14: The name is moderated. - `409`: 37: The name was in use too recently. - `413`: 0: Unknown error. - `429`: 17: Too many requests. - `503`: 21: Group creation is currently disabled. **Response fields** (`Roblox.Web.Responses.Groups.GroupResponseV2`) See [Roblox.Web.Responses.Groups.GroupResponseV2](#roblox-web-responses-groups-groupresponsev2) in Models. **Response example:** ```json { "id": 0, "name": "string", "description": "string", "owner": { "id": 0, "type": 0, "name": "string" }, "memberCount": 0, "created": "2024-01-01T00:00:00Z" } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/create" ``` ### POST `/v1/groups/policies` Gets group policy info used for compliance. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Groups.Api.GroupPolicyRequest` See [Roblox.Groups.Api.GroupPolicyRequest](#roblox-groups-api-grouppolicyrequest) in Models. **Request example:** ```json { "groupIds": [ 0 ] } ``` **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupPoliciesResponse` - `400`: 1: Too many ids in request. 2: Ids could not be parsed from request. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Groups.Api.GroupPoliciesResponse`) See [Roblox.Groups.Api.GroupPoliciesResponse](#roblox-groups-api-grouppoliciesresponse) in Models. **Response example:** ```json { "groups": [ { "canViewGroup": "...", "groupId": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/policies" \ -H "Content-Type: application/json" \ -d '{ "groupIds": [ 0 ] }' ``` ### POST `/v1/user/groups/primary` Sets the authenticated user's primary group **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Groups.Api.PrimaryGroupRequest` See [Roblox.Groups.Api.PrimaryGroupRequest](#roblox-groups-api-primarygrouprequest) in Models. **Request example:** ```json { "groupId": 0 } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: Group is invalid or does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: You aren't a member of the group specified. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/user/groups/primary" \ -H "Content-Type: application/json" \ -d '{ "groupId": 0 }' ``` ### DELETE `/v1/user/groups/primary` Removes the authenticated user's primary group **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/user/groups/primary" ``` ### PATCH `/v1/groups/{groupId}/blocked-keywords/{keywordId}` Updates an existing blocked keyword for the specified Group. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | | | `keywordId` | path | `string` | Yes | | **Request Body:** `application/json` — Type: `Roblox.Groups.Api.UpdateBlockedKeywordRequest` See [Roblox.Groups.Api.UpdateBlockedKeywordRequest](#roblox-groups-api-updateblockedkeywordrequest) in Models. **Request example:** ```json { "keyword": "string", "isPrivate": false } ``` **Responses:** - `200`: OK → `Roblox.Groups.Client.BlockedKeywordModel` - `400`: 1: Group is invalid or does not exist. 3: KeywordId is invalid. 4: Invalid request. 12: The provided content was moderated. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 8: Insufficient permissions to complete the request. 9: User is invalid or does not exist. 10: Insufficient permissions to complete the request. - `404`: 7: Not found. - `405`: 31: Service is currently unavailable. - `409`: 11: There was a conflict in your request. **Response fields** (`Roblox.Groups.Client.BlockedKeywordModel`) See [Roblox.Groups.Client.BlockedKeywordModel](#roblox-groups-client-blockedkeywordmodel) in Models. **Response example:** ```json { "id": "string", "keyword": "string", "createdBy": 0, "isPrivate": false, "createdAt": "2024-01-01T00:00:00Z", "updatedAt": "2024-01-01T00:00:00Z" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/blocked-keywords/{KEYWORDID}" \ -H "Content-Type: application/json" \ -d '{ "keyword": "string", "isPrivate": false }' ``` ### DELETE `/v1/groups/{groupId}/blocked-keywords/{keywordId}` Deletes a specific blocked keyword from the specified Group. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | | | `keywordId` | path | `string` | Yes | | **Responses:** - `200`: OK - `400`: 1: Group is invalid or does not exist. 3: KeywordId is invalid. 4: Invalid request. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 9: User is invalid or does not exist. 10: Insufficient permissions to complete the request. - `404`: 7: Not found. - `405`: 31: Service is currently unavailable. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/blocked-keywords/{KEYWORDID}" ``` ### PATCH `/v1/groups/{groupId}/description` Updates the groups description **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The id of the group the user is in. | **Request Body:** `application/json` — Type: `Roblox.Groups.Api.UpdateGroupDescriptionRequest` See [Roblox.Groups.Api.UpdateGroupDescriptionRequest](#roblox-groups-api-updategroupdescriptionrequest) in Models. **Request example:** ```json { "description": "string" } ``` **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupDescriptionResponse` - `400`: 1: Group is invalid or does not exist. 29: Your group description was empty. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: The description is too long. 23: Insufficient permissions to complete the request. **Response fields** (`Roblox.Groups.Api.GroupDescriptionResponse`) See [Roblox.Groups.Api.GroupDescriptionResponse](#roblox-groups-api-groupdescriptionresponse) in Models. **Response example:** ```json { "newDescription": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/description" \ -H "Content-Type: application/json" \ -d '{ "description": "string" }' ``` ### PATCH `/v1/groups/{groupId}/name` Updates the group's name. This endpoint will charge Robux for the group rename. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The id of the group the user is in. | **Request Body:** `application/json` — Type: `Roblox.Groups.Api.UpdateGroupNameRequest` See [Roblox.Groups.Api.UpdateGroupNameRequest](#roblox-groups-api-updategroupnamerequest) in Models. **Request example:** ```json { "name": "string" } ``` **Responses:** - `200`: OK → `Roblox.Groups.Api.UpdateGroupNameResponse` - `400`: 1: Group is invalid or does not exist. 13: The name is invalid. 19: The name is too long. 20: The name has been taken. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 12: Insufficient Robux funds. 14: The name is moderated. 23: Insufficient permissions to complete the request. 38: Your account must be verified in order to change your group's name. 39: The group ownership was changed too recently. - `409`: 36: The name was changed too recently. 37: The name was in use too recently. - `413`: 0: Unknown error. - `429`: 17: Too many requests. **Response fields** (`Roblox.Groups.Api.UpdateGroupNameResponse`) See [Roblox.Groups.Api.UpdateGroupNameResponse](#roblox-groups-api-updategroupnameresponse) in Models. **Response example:** ```json { "newName": "string" } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/name" \ -H "Content-Type: application/json" \ -d '{ "name": "string" }' ``` ### PATCH `/v1/groups/{groupId}/notification-preference` Updates the group's settings **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The id of the group the user is in. | **Request Body:** `application/json` — Type: `Roblox.Groups.Api.UpdateGroupNotificationPreferenceRequest` See [Roblox.Groups.Api.UpdateGroupNotificationPreferenceRequest](#roblox-groups-api-updategroupnotificationpreferencerequest) in Models. **Request example:** ```json { "notificationsEnabled": false, "type": 0 } ``` **Responses:** - `200`: OK → `0 \| 1 \| 2 \| 3` - `400`: 1: Group is invalid or does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/notification-preference" \ -H "Content-Type: application/json" \ -d '{ "notificationsEnabled": false, "type": 0 }' ``` ### PATCH `/v1/groups/{groupId}/rolesets/{rolesetId}` Updates existing group roleset. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group Id. | | `rolesetId` | path | `integer (int64)` | Yes | The roleset Id. | **Request Body:** `application/json` — Type: `Roblox.Groups.Api.Models.Request.UpdateRoleSetRequest` See [Roblox.Groups.Api.Models.Request.UpdateRoleSetRequest](#roblox-groups-api-models-request-updaterolesetrequest) in Models. **Request example:** ```json { "name": "string", "description": "string", "rank": 0, "color": 0 } ``` **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupRoleResponse` - `400`: 5: Role name already exists. 6: Rank value is out of bounds. 7: The role name is too long. 8: The role description is too long. 10: This group does not exist. 14: Role name can not be empty. 15: This role does not exist. 19: Cannot update Guest role. 20: Cannot update Owner role rank. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 9: You do not have permissions to perform this action. **Response fields** (`Roblox.Groups.Api.GroupRoleResponse`) See [Roblox.Groups.Api.GroupRoleResponse](#roblox-groups-api-grouproleresponse) in Models. **Response example:** ```json { "id": 0, "name": "string", "description": "string", "rank": 0, "memberCount": 0, "isBase": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/rolesets/{ROLESETID}" \ -H "Content-Type: application/json" \ -d '{ "name": "string", "description": "string", "rank": 0, "color": 0 }' ``` ### DELETE `/v1/groups/{groupId}/rolesets/{rolesetId}` Deletes existing group roleset. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group Id. | | `rolesetId` | path | `integer (int64)` | Yes | The roleset Id. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 10: This group does not exist. 15: This role does not exist. 17: Cannot remove any more roles 18: Cannot delete this role. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 9: You do not have permissions to perform this action. 16: There are users in this role. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/rolesets/{ROLESETID}" ``` ### PATCH `/v1/groups/{groupId}/social-links/{socialLinkId}` Updates a social link **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The id of the universe | | `socialLinkId` | path | `integer (int64)` | Yes | The id of the social link being updated | **Request Body:** `application/json` — Type: `Roblox.Groups.Api.SocialLinkRequest` See [Roblox.Groups.Api.SocialLinkRequest](#roblox-groups-api-sociallinkrequest) in Models. **Request example:** ```json { "type": 0, "url": "string", "title": "string" } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: Group is invalid or does not exist. 3: The social link title is too long. 4: The social link title cannot be empty. 5: The social link url cannot be empty. 6: The social link url was improperly formatted. 7: The request was null. 8: The requested group or social link was not found. 9: The social link type is invalid. 10: The social link is not for a group. 12: The social link title was moderated. 16: A social link with this type already exists on this group. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: You do not have permission to configure this social link. - `404`: 11: Social links cannot be processed as this time. - `503`: 11: Social links cannot be processed as this time. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/social-links/{SOCIALLINKID}" \ -H "Content-Type: application/json" \ -d '{ "type": 0, "url": "string", "title": "string" }' ``` ### DELETE `/v1/groups/{groupId}/social-links/{socialLinkId}` Deletes a social link **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The id of the game you are editing, required for permissions checking | | `socialLinkId` | path | `integer (int64)` | Yes | The id of the social link | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: Group is invalid or does not exist. 10: The social link is not for a group. 15: The social link id doesn't match the group id. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: You do not have permission to configure this social link. 13: Only users who are over thirteen years of age may edit social links. - `404`: 11: Social links cannot be processed as this time. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/social-links/{SOCIALLINKID}" ``` ### PATCH `/v1/groups/{groupId}/status` Sets group status **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group Id. | **Request Body:** `application/json` — Type: `Roblox.Groups.Api.PostGroupStatusRequest` See [Roblox.Groups.Api.PostGroupStatusRequest](#roblox-groups-api-postgroupstatusrequest) in Models. **Request example:** ```json { "message": "string" } ``` **Responses:** - `200`: OK → `Roblox.Groups.Api.ShoutResponse` - `400`: 1: Group is invalid or does not exist. 7: Missing group status content. 32: Description was filtered. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 6: You are not authorized to set the status of this group **Response fields** (`Roblox.Groups.Api.ShoutResponse`) See [Roblox.Groups.Api.ShoutResponse](#roblox-groups-api-shoutresponse) in Models. **Response example:** ```json { "body": "string", "poster": { "buildersClubMembershipType": 0, "hasVerifiedBadge": false, "userId": 0, "username": "string", "displayName": "string" }, "created": "2024-01-01T00:00:00Z", "updated": "2024-01-01T00:00:00Z" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/status" \ -H "Content-Type: application/json" \ -d '{ "message": "string" }' ``` ### PATCH `/v1/groups/{groupId}/users/{userId}` Updates a users role in a group. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The id of the group the user is in. | | `userId` | path | `integer (int64)` | Yes | The id of the user being updated. | **Request Body:** `application/json` — Type: `Roblox.Groups.Api.UpdateUserRoleRequest` See [Roblox.Groups.Api.UpdateUserRoleRequest](#roblox-groups-api-updateuserrolerequest) in Models. **Request example:** ```json { "roleId": 0 } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: The group is invalid or does not exist. 2: The roleset is invalid or does not exist. 3: The user is invalid or does not exist. 23: You cannot change your own role. 26: You cannot change the user's role to the same role. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 4: You do not have permission to manage this member. - `503`: 18: The operation is temporarily unavailable. Please try again later. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/users/{USERID}" \ -H "Content-Type: application/json" \ -d '{ "roleId": 0 }' ``` ### DELETE `/v1/groups/{groupId}/users/{userId}` Removes a user from a group **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group Id. | | `userId` | path | `integer (int64)` | Yes | The Id of the user being removed. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: The group is invalid or does not exist. 3: The user is invalid or does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 4: You do not have permission to manage this member. 25: 2-Step Verification is required to make further transactions. Go to Settings > Security to complete 2-Step Verification. - `503`: 18: The operation is temporarily unavailable. Please try again later. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/users/{USERID}" ``` ### PATCH `/v1/groups/icon` Updates the group icon. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | query | `integer (int64)` | Yes | The group Id. | | `Files` | formData | `file` | No | | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: Group is invalid or does not exist. 16: The group icon is missing from the request. 17: Too many requests. 30: Invalid file type for group icon. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 23: Insufficient permissions to complete the request. - `413`: 0: Unknown error. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/icon?groupId={VALUE}" ``` ### DELETE `/v1/groups/{groupId}/wall/posts/{postId}` Deletes a group wall post. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group id. | | `postId` | path | `integer (int64)` | Yes | The group wall post id. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: The group is invalid or does not exist. 3: The group wall post id is invalid or does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: You do not have permission to access this group wall. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/wall/posts/{POSTID}" ``` ### DELETE `/v1/groups/{groupId}/wall/users/{userId}/posts` Deletes all group wall posts made by a specific user. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group id. | | `userId` | path | `integer (int64)` | Yes | The user id. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: The group is invalid or does not exist. 6: The user specified is invalid or does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: You do not have permission to access this group wall. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v1/groups/{GROUPID}/wall/users/{USERID}/posts" ``` --- name: "Groups Api v2" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://groups.roblox.com" version: "v2" endpoints: 7 auth: [cookie] --- # Groups Api v2 **API Version:** v2 **Base URL:** `https://groups.roblox.com` ## Endpoints ### GET `/v2/groups` Multi-get groups information by Ids. If a group comes back as null, it will not be returned in the response. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupIds` | query | `array` | Yes | The group Ids. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Web.Responses.Groups.GroupResponseV2]` - `400`: 2: Too many ids in request. 3: Ids could not be parsed from request. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Web.Responses.Groups.GroupResponseV2]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Web.Responses.Groups.GroupResponseV2]](#roblox-web-webapi-models-apiarrayresponse-roblox-web-responses-groups-groupresponsev2-) in Models. **Response example:** ```json { "data": [ { "id": "...", "name": "...", "description": "...", "owner": "...", "memberCount": "...", "created": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v2/groups?groupIds={VALUE}" ``` ### GET `/v2/groups/{groupId}/experiences` Gets experiences created by the specified group id. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group Id | | `accessFilter` | query | `integer (int32)` | No | The access type of the experiences. Valid values: `1`, `2`, `4` | | `limit` | query | `integer (int32)` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `string` | No | The order the results are sorted in. Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Groups.Api.Models.Response.GroupExperienceResponse]` - `501`: 47: Code path is not implemented. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Groups.Api.Models.Response.GroupExperienceResponse]`) See [Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Groups.Api.Models.Response.GroupExperienceResponse]](#roblox-web-webapi-models-apipageresponse-roblox-groups-api-models-response-groupexperienceresponse-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "id": "...", "name": "...", "description": "...", "creator": "...", "rootPlace": "...", "created": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v2/groups/{GROUPID}/experiences" ``` ### GET `/v2/groups/{groupId}/relationships/{groupRelationshipType}` Gets a group's relationships with cursor-based pagination. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group Id. | | `groupRelationshipType` | path | `string` | Yes | The group relationship type, enemies or allies. | | `limit` | query | `integer (int32)` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `string` | No | The order the results are sorted in. Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Groups.Api.Models.Response.GroupRelationshipsV2Response` - `400`: 1: Group is invalid or does not exist. 4: Group relationship type or request type is invalid. 8: Invalid or missing pagination parameters **Response fields** (`Roblox.Groups.Api.Models.Response.GroupRelationshipsV2Response`) See [Roblox.Groups.Api.Models.Response.GroupRelationshipsV2Response](#roblox-groups-api-models-response-grouprelationshipsv2response) in Models. **Response example:** ```json { "groupId": 0, "relationshipType": 1, "groupResponses": { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ "..." ] } } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v2/groups/{GROUPID}/relationships/{GROUPRELATIONSHIPTYPE}" ``` ### GET `/v2/groups/{groupId}/relationships/{groupRelationshipType}/requests` Gets a group's relationship requests with cursor-based pagination. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group Id. | | `groupRelationshipType` | path | `string` | Yes | The group relationship type of the request, only allies are supported. | | `limit` | query | `integer (int32)` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `string` | No | The order the results are sorted in. Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Groups.Api.Models.Response.GroupRelationshipsV2Response` - `400`: 1: Group is invalid or does not exist. 4: Group relationship type or request type is invalid. 8: Invalid or missing pagination parameters - `401`: 0: Authorization has been denied for this request. - `403`: 5: You don't have permission to manage this group's relationships. **Response fields** (`Roblox.Groups.Api.Models.Response.GroupRelationshipsV2Response`) See [Roblox.Groups.Api.Models.Response.GroupRelationshipsV2Response](#roblox-groups-api-models-response-grouprelationshipsv2response) in Models. **Response example:** ```json { "groupId": 0, "relationshipType": 1, "groupResponses": { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ "..." ] } } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v2/groups/{GROUPID}/relationships/{GROUPRELATIONSHIPTYPE}/requests" ``` ### GET `/v2/groups/{groupId}/wall/posts` Gets a list of group wall posts. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group id. | | `limit` | query | `integer (int32)` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `string` | No | Sorted by group wall post Id Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Groups.Api.GroupWallPostV2Model]` - `400`: 1: The group is invalid or does not exist. - `403`: 2: You do not have permission to access this group wall. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Groups.Api.GroupWallPostV2Model]`) See [Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Groups.Api.GroupWallPostV2Model]](#roblox-web-webapi-models-apipageresponse-roblox-groups-api-groupwallpostv2model-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "id": "...", "poster": "...", "body": "...", "created": "...", "updated": "..." } ] } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v2/groups/{GROUPID}/wall/posts" ``` ### POST `/v2/groups/{groupId}/wall/posts` Creates a post on a group wall **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupId` | path | `integer (int64)` | Yes | The group id. | **Request Body:** `application/json` — Type: `Roblox.Groups.Api.CreateWallPostRequest` See [Roblox.Groups.Api.CreateWallPostRequest](#roblox-groups-api-createwallpostrequest) in Models. **Request example:** ```json { "body": "string", "captchaId": "string", "captchaToken": "string", "captchaProvider": "string", "challengeId": "string" } ``` **Responses:** - `200`: OK → `Roblox.Groups.Api.GroupWallPostV2Model` - `400`: 1: The group is invalid or does not exist. 5: Your post was empty, white space, or more than 500 characters. 9: The provided content was moderated. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: You do not have permission to access this group wall. - `429`: 4: You are posting too fast, please try again in a few minutes. **Response fields** (`Roblox.Groups.Api.GroupWallPostV2Model`) See [Roblox.Groups.Api.GroupWallPostV2Model](#roblox-groups-api-groupwallpostv2model) in Models. **Response example:** ```json { "id": 0, "poster": { "user": { "buildersClubMembershipType": "...", "hasVerifiedBadge": "...", "userId": "...", "username": "...", "displayName": "..." }, "role": { "id": "...", "name": "...", "description": "...", "rank": "...", "memberCount": "...", "isBase": "..." } }, "body": "string", "created": "2024-01-01T00:00:00Z", "updated": "2024-01-01T00:00:00Z" } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v2/groups/{GROUPID}/wall/posts" \ -H "Content-Type: application/json" \ -d '{ "body": "string", "captchaId": "string", "captchaToken": "string", "captchaProvider": "string", "challengeId": "string" }' ``` ### GET `/v2/users/{userId}/groups/roles` Gets a list of all group roles for groups the specified user is in. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user id. | | `includeLocked` | query | `boolean` | Yes | | | `includeNotificationPreferences` | query | `boolean` | Yes | | | `discoveryType` | query | `integer (int32)` | Yes | Valid values: `0`, `1` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Groups.Api.GroupMembershipResponse]` - `400`: 3: The user is invalid or does not exist. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Groups.Api.GroupMembershipResponse]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Groups.Api.GroupMembershipResponse]](#roblox-web-webapi-models-apiarrayresponse-roblox-groups-api-groupmembershipresponse-) in Models. **Response example:** ```json { "data": [ { "group": "...", "role": "...", "isNotificationsEnabled": "...", "notificationPreferences": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://groups.roblox.com/v2/users/{USERID}/groups/roles?includeLocked={VALUE}&includeNotificationPreferences={VALUE}&discoveryType={VALUE}" ``` ## Models ### Roblox.Groups.Api.CreateWallPostRequest A request model for creating a group wall post | Property | Type | Required | Description | |----------|------|----------|-------------| | `body` | `string` | No | The wall post body | | `captchaId` | `string` | No | | | `captchaToken` | `string` | No | | | `captchaProvider` | `string` | No | | | `challengeId` | `string` | No | | ### Roblox.Groups.Api.GroupDetailResponse A detailed group response model | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | The group id | | `name` | `string` | No | The group name | | `description` | `string` | No | The group description | | `owner` | `Roblox.Groups.Api.Models.Response.UserModel` | No | | | `shout` | `Roblox.Groups.Api.ShoutResponse` | No | | | `memberCount` | `integer` | No | The number of members in the group | | `isBuildersClubOnly` | `boolean` | No | Whether the group is Builders Club only | | `publicEntryAllowed` | `boolean` | No | Whether the group is public (no approval required) | | `isLocked` | `boolean` | No | Whether the group is locked | | `hasVerifiedBadge` | `boolean` | No | Whether the group has a verified badge. | | `hasSocialModules` | `boolean` | No | Whether the group has social modules enabled (e.g. Forums) (determines if "Followers" vs "Members" should be shown). | ### Roblox.Groups.Api.GroupMembershipResponse A basic group membership response model | Property | Type | Required | Description | |----------|------|----------|-------------| | `group` | `Roblox.Web.Responses.Groups.GroupBasicResponse` | No | | | `role` | `Roblox.Web.Responses.Groups.GroupRoleBasicResponse` | No | | | `isNotificationsEnabled` | `boolean` | No | Whether the group notification preferences are enabled for the user TODO: Deprecate this field - it only returns if announcement notifications are enabled and that information is already returned in the NotificationPreferences list below | | `notificationPreferences` | `Roblox.Groups.Api.GroupNotificationPreferenceData[]` | No | | ### Roblox.Groups.Api.GroupNotificationPreferenceData | Property | Type | Required | Description | |----------|------|----------|-------------| | `type` | `0 \| 1 \| 2 \| 3 \| 4` | No | ['AnnouncementCreatedNotification' = 0, 'ForumPostCreatedNotification' = 1, 'ForumCommentCreatedNotification' = 2, 'ForumCommentReplyCreatedNotification' = 3, 'ForumSubscriberNotification' = 4] | | `enabled` | `boolean` | No | | | `name` | `string` | No | | | `description` | `string` | No | | ### Roblox.Groups.Api.GroupRoleResponse A group role response model | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | The role id | | `name` | `string` | No | The role name | | `description` | `string` | No | The role description | | `rank` | `integer` | No | The role rank | | `memberCount` | `integer` | No | The number of members in the role. | | `isBase` | `boolean` | No | Whether or not the role is the base role for the group | | `color` | `integer enum (17 values)` | No | The role color. ['Invalid' = 0, 'Blue' = 1, 'Green' = 2, 'Purple' = 3, 'Yellow' = 4, 'Orange' = 5, 'Red' = 6, 'Magenta' = 7, 'Teal' = 8, 'Turquoise' = 9, 'Rust' = 10, 'Pistachio' = 11, 'Midnight' = 12, 'Lavender' = 13, 'Pink' = 14, 'Crimson' = 15, 'Plum' = 16] Values: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 | ### Roblox.Groups.Api.GroupWallPostV2Model A response model for group wall post information | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | The group wall post Id. | | `poster` | `Roblox.Groups.Api.UserGroupRoleResponse` | No | | | `body` | `string` | No | The group wall post body. | | `created` | `string` | No | When the group wall post was posted. | | `updated` | `string` | No | When the group wall post was last updated. | ### Roblox.Groups.Api.Models.Response.GroupExperienceResponse A response model representing an experience/game. | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | The game (universe) Id. | | `name` | `string` | No | The game name. | | `description` | `string` | No | The game description. | | `creator` | `Roblox.Web.Responses.RelatedEntityTypeResponse[Roblox.Platform.Core.CreatorType]` | No | | | `rootPlace` | `Roblox.Web.Responses.RelatedEntityTypeResponse[Roblox.Platform.Assets.AssetType]` | No | | | `created` | `string` | No | When the game was created. | | `updated` | `string` | No | When the game was last updated. | | `placeVisits` | `integer` | No | The number of place visits for this game. | ### Roblox.Groups.Api.Models.Response.GroupRelationshipsV2Response A group relationships response model for V2 endpoints with cursor-based pagination. | Property | Type | Required | Description | |----------|------|----------|-------------| | `groupId` | `integer` | No | The group id. | | `relationshipType` | `1 \| 2` | No | The group relationship type. ['Allies' = 1, 'Enemies' = 2] | | `groupResponses` | `Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Groups.Api.GroupDetailResponse]` | No | | ### Roblox.Groups.Api.Models.Response.UserModel A model representing data about an Roblox.Platform.Membership.IUser | Property | Type | Required | Description | |----------|------|----------|-------------| | `buildersClubMembershipType` | `0 \| 1 \| 2 \| 3 \| 4` | No | The user's builders club membership type | | `hasVerifiedBadge` | `boolean` | No | The user's verified badge status. | | `userId` | `integer` | No | | | `username` | `string` | No | | | `displayName` | `string` | No | | ### Roblox.Groups.Api.ShoutResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `body` | `string` | No | The shout's message | | `poster` | `Roblox.Groups.Api.Models.Response.UserModel` | No | | | `created` | `string` | No | The shout's created time | | `updated` | `string` | No | The shout's last updated time | ### Roblox.Groups.Api.UserGroupRoleResponse A user group role response model | Property | Type | Required | Description | |----------|------|----------|-------------| | `user` | `Roblox.Groups.Api.Models.Response.UserModel` | No | | | `role` | `Roblox.Groups.Api.GroupRoleResponse` | No | | ### Roblox.Web.Responses.Groups.GroupBasicResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | | | `name` | `string` | No | | | `memberCount` | `integer` | No | | | `hasVerifiedBadge` | `boolean` | No | | ### Roblox.Web.Responses.Groups.GroupResponseV2 | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | | | `name` | `string` | No | | | `description` | `string` | No | | | `owner` | `Roblox.Web.Responses.RelatedEntityTypeResponse[Roblox.Web.Responses.Groups.GroupOwnerType]` | No | | | `memberCount` | `integer` | No | | | `created` | `string` | No | | | `hasVerifiedBadge` | `boolean` | No | | ### Roblox.Web.Responses.Groups.GroupRoleBasicResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | | | `name` | `string` | No | | | `rank` | `integer` | No | | ### Roblox.Web.Responses.RelatedEntityTypeResponse[Roblox.Platform.Assets.AssetType] | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | | | `type` | `integer enum (86 values)` | No | ['Image' = 1, 'TShirt' = 2, 'Audio' = 3, 'Mesh' = 4, 'Lua' = 5, 'HTML' = 6, 'Text' = 7, 'Hat' = 8, 'Place' = 9, 'Model' = 10, 'Shirt' = 11, 'Pants' = 12, 'Decal' = 13, 'Avatar' = 16, 'Head' = 17, 'Face' = 18, 'Gear' = 19, 'Badge' = 21, 'GroupEmblem' = 22, 'Animation' = 24, 'Arms' = 25, 'Legs' = 26, 'Torso' = 27, 'RightArm' = 28, 'LeftArm' = 29, 'LeftLeg' = 30, 'RightLeg' = 31, 'Package' = 32, 'YouTubeVideo' = 33, 'GamePass' = 34, 'App' = 35, 'Code' = 37, 'Plugin' = 38, 'SolidModel' = 39, 'MeshPart' = 40, 'HairAccessory' = 41, 'FaceAccessory' = 42, 'NeckAccessory' = 43, 'ShoulderAccessory' = 44, 'FrontAccessory' = 45, 'BackAccessory' = 46, 'WaistAccessory' = 47, 'ClimbAnimation' = 48, 'DeathAnimation' = 49, 'FallAnimation' = 50, 'IdleAnimation' = 51, 'JumpAnimation' = 52, 'RunAnimation' = 53, 'SwimAnimation' = 54, 'WalkAnimation' = 55, 'PoseAnimation' = 56, 'LocalizationTableManifest' = 59, 'LocalizationTableTranslation' = 60, 'EmoteAnimation' = 61, 'Video' = 62, 'TexturePack' = 63, 'TShirtAccessory' = 64, 'ShirtAccessory' = 65, 'PantsAccessory' = 66, 'JacketAccessory' = 67, 'SweaterAccessory' = 68, 'ShortsAccessory' = 69, 'LeftShoeAccessory' = 70, 'RightShoeAccessory' = 71, 'DressSkirtAccessory' = 72, 'FontFamily' = 73, 'FontFace' = 74, 'MeshHiddenSurfaceRemoval' = 75, 'EyebrowAccessory' = 76, 'EyelashAccessory' = 77, 'MoodAnimation' = 78, 'DynamicHead' = 79, 'CodeSnippet' = 80, 'AdsVideo' = 81, 'OtaUpdate' = 82, 'Screenshot' = 83, 'RuntimePropertySet' = 84, 'StorePreviewVideo' = 85, 'GamePreviewVideo' = 86, 'CreatorExperienceConfig' = 87, 'FaceMakeup' = 88, 'LipMakeup' = 89, 'EyeMakeup' = 90, 'VoxelFragment' = 91, 'AvatarBackground' = 92, 'TextDocument' = 93] Values: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 16, 17, 18, 19, 21, 22, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93 | | `name` | `string` | No | | ### Roblox.Web.Responses.RelatedEntityTypeResponse[Roblox.Platform.Core.CreatorType] | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | | | `type` | `0 \| 1` | No | ['User' = 0, 'Group' = 1] | | `name` | `string` | No | | ### Roblox.Web.Responses.RelatedEntityTypeResponse[Roblox.Web.Responses.Groups.GroupOwnerType] | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | | | `type` | `0` | No | ['User' = 0] | | `name` | `string` | No | | ### Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Groups.Api.GroupMembershipResponse] | Property | Type | Required | Description | |----------|------|----------|-------------| | `data` | `Roblox.Groups.Api.GroupMembershipResponse[]` | No | | ### Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Web.Responses.Groups.GroupResponseV2] | Property | Type | Required | Description | |----------|------|----------|-------------| | `data` | `Roblox.Web.Responses.Groups.GroupResponseV2[]` | No | | ### Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Groups.Api.GroupDetailResponse] | Property | Type | Required | Description | |----------|------|----------|-------------| | `previousPageCursor` | `string` | No | | | `nextPageCursor` | `string` | No | | | `data` | `Roblox.Groups.Api.GroupDetailResponse[]` | No | | ### Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Groups.Api.GroupWallPostV2Model] | Property | Type | Required | Description | |----------|------|----------|-------------| | `previousPageCursor` | `string` | No | | | `nextPageCursor` | `string` | No | | | `data` | `Roblox.Groups.Api.GroupWallPostV2Model[]` | No | | ### Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Groups.Api.Models.Response.GroupExperienceResponse] | Property | Type | Required | Description | |----------|------|----------|-------------| | `previousPageCursor` | `string` | No | | | `nextPageCursor` | `string` | No | | | `data` | `Roblox.Groups.Api.Models.Response.GroupExperienceResponse[]` | No | | --- name: "Inventory Api v1" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://inventory.roblox.com" version: "v1" endpoints: 10 auth: [cookie] --- # Inventory Api v1 **API Version:** v1 **Base URL:** `https://inventory.roblox.com` ## Endpoints ### GET `/v1/packages/{packageId}/assets` *(deprecated)* Given a package ID, returns the list of asset IDs for that package **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `packageID` | path | `integer (int64)` | Yes | The asset ID of the package | **Responses:** - `200`: OK → `Roblox.Inventory.Api.Models.AssetIdListModel` **Response fields** (`Roblox.Inventory.Api.Models.AssetIdListModel`) See [Roblox.Inventory.Api.Models.AssetIdListModel](#roblox-inventory-api-models-assetidlistmodel) in Models. **Response example:** ```json { "assetIds": [ 0 ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://inventory.roblox.com/v1/packages/{packageId}/assets" ``` ### GET `/v1/users/{userId}/assets/collectibles` Gets all collectible assets owned by the specified user. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The userid of the owner of the collectibles. | | `assetType` | query | `integer (int32)` | No | The asset type for the collectibles you're trying to get. Valid values: `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`, `10`, `11`, `12`, `13`, `16`, `17`, `18`, `19`, `21`, `22`, `24`, `25`, `26`, `27`, `28`, `29`, `30`, `31`, `32`, `33`, `34`, `35`, `37`, `38`, `39`, `40`, `41`, `42`, `43`, `44`, `45`, `46`, `47`, `48`, `49`, `50`, `51`, `52`, `53`, `54`, `55`, `56`, `59`, `60`, `61`, `62`, `63`, `64`, `65`, `66`, `67`, `68`, `69`, `70`, `71`, `72`, `73`, `74`, `75`, `76`, `77`, `78`, `79`, `80`, `81`, `82`, `83`, `84`, `85`, `86`, `87`, `88`, `89`, `90`, `91`, `92` | | `limit` | query | `integer (int32)` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `string` | No | Sorted by userAssetId Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Inventory.Api.Models.CollectibleUserAssetModel]` - `400`: The specified asset type(s) are invalid. - `403`: The specified user's inventory is hidden. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Inventory.Api.Models.CollectibleUserAssetModel]`) See [Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Inventory.Api.Models.CollectibleUserAssetModel]](#roblox-web-webapi-models-apipageresponse-roblox-inventory-api-models-collectibleuserassetmodel-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "userAssetId": "...", "serialNumber": "...", "assetId": "...", "name": "...", "recentAveragePrice": "...", "originalPrice": "..." } ] } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://inventory.roblox.com/v1/users/{USERID}/assets/collectibles" ``` ### GET `/v1/users/{userId}/can-view-inventory` Gets whether the specified user's inventory can be viewed. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user identifier. | **Responses:** - `200`: OK → `Roblox.Inventory.Api.Models.CanViewInventoryResponse` - `400`: 1: The specified user does not exist! **Response fields** (`Roblox.Inventory.Api.Models.CanViewInventoryResponse`) See [Roblox.Inventory.Api.Models.CanViewInventoryResponse](#roblox-inventory-api-models-canviewinventoryresponse) in Models. **Response example:** ```json { "canView": false } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://inventory.roblox.com/v1/users/{USERID}/can-view-inventory" ``` ### GET `/v1/users/{userId}/categories` Return inventory categories for a user **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | | **Responses:** - `200`: OK → `Roblox.Inventory.Api.Models.CategoriesModel` **Response fields** (`Roblox.Inventory.Api.Models.CategoriesModel`) See [Roblox.Inventory.Api.Models.CategoriesModel](#roblox-inventory-api-models-categoriesmodel) in Models. **Response example:** ```json { "categories": [ { "name": "...", "displayName": "...", "categoryType": "...", "items": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://inventory.roblox.com/v1/users/{USERID}/categories" ``` ### GET `/v1/users/{userId}/categories/favorites` Return favorites categories for a user **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | | **Responses:** - `200`: OK → `Roblox.Inventory.Api.Models.CategoriesModel` **Response fields** (`Roblox.Inventory.Api.Models.CategoriesModel`) See [Roblox.Inventory.Api.Models.CategoriesModel](#roblox-inventory-api-models-categoriesmodel) in Models. **Response example:** ```json { "categories": [ { "name": "...", "displayName": "...", "categoryType": "...", "items": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://inventory.roblox.com/v1/users/{USERID}/categories/favorites" ``` ### GET `/v1/users/{userId}/items/{itemType}/{itemTargetId}` Gets owned items of the specified item type. Game Servers can make requests for any user, but can only make requests for game passes that belong to the place sending the request. Place creators can make requests as if they were the Game Server. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | ID of the user in question | | `itemType` | path | `integer (int32)` | Yes | Type of the item in question (i.e. Asset, GamePass, Badge, Bundle) Valid values: `0`, `1`, `2`, `3`, `4` | | `itemTargetId` | path | `integer (int64)` | Yes | ID of the item in question | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Inventory.Api.Models.IItemModel]` - `400`: 1: The specified user does not exist! 5: The specified game pass does not exist! Are you using the new game pass ID? 6: The specified item type does not exist. 7: The specified Asset does not exist! 10: The specified asset is not a badge! 12: The specified bundle does not exist! **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Inventory.Api.Models.IItemModel]`) See [Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Inventory.Api.Models.IItemModel]](#roblox-web-webapi-models-apipageresponse-roblox-inventory-api-models-iitemmodel-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "id": "...", "name": "...", "type": "...", "instanceId": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://inventory.roblox.com/v1/users/{USERID}/items/{ITEMTYPE}/{ITEMTARGETID}" ``` ### GET `/v1/users/{userId}/items/{itemType}/{itemTargetId}/is-owned` Gets whether a user owns an item of type itemType with id itemTargetId. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | ID of the user in question | | `itemType` | path | `integer (int32)` | Yes | Type of the item in question (i.e. Asset, GamePass, Badge, Bundle) Valid values: `0`, `1`, `2`, `3`, `4` | | `itemTargetId` | path | `integer (int64)` | Yes | ID of the item in question | **Responses:** - `200`: OK → `boolean` - `400`: 1: The specified user does not exist! 5: The specified game pass does not exist! Are you using the new game pass ID? 6: The specified item type does not exist. 7: The specified Asset does not exist! 10: The specified asset is not a badge! 12: The specified bundle does not exist! **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://inventory.roblox.com/v1/users/{USERID}/items/{ITEMTYPE}/{ITEMTARGETID}/is-owned" ``` ### GET `/v1/users/{userId}/places/inventory` Gets Created, MyGames, or OtherGames places inventory for a user **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | | | `placesTab` | query | `integer (int32)` | Yes | Valid values: `0`, `1`, `2`, `3`, `4`, `5` | | `itemsPerPage` | query | `integer (int32)` | Yes | | | `cursor` | query | `integer (int32)` | Yes | | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Inventory.Api.Models.PlaceModel]` - `400`: 6: Invalid request - `403`: 3: Insufficient permission. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Inventory.Api.Models.PlaceModel]`) See [Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Inventory.Api.Models.PlaceModel]](#roblox-web-webapi-models-apipageresponse-roblox-inventory-api-models-placemodel-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "universeId": "...", "placeId": "...", "name": "...", "creator": "...", "priceInRobux": "..." } ] } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://inventory.roblox.com/v1/users/{USERID}/places/inventory?placesTab={VALUE}&itemsPerPage={VALUE}&cursor={VALUE}" ``` ### POST `/v1/collections/items/{itemType}/{itemTargetId}` Adds an item to the appropriate collection **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `itemType` | path | `integer (int32)` | Yes | Type of the item (i.e. Asset, Bundle) Valid values: `0`, `1`, `2`, `3` | | `itemTargetId` | path | `integer (int64)` | Yes | ID of the item | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: The item type does not exist. 2: The asset does not exist. 3: The bundle does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 4: You don't own the specified item. 5: Assets of this type are not allowed in collections. 6: Items of this type are not allowed in collections. 7: The item is already in the collection. 9: The collection is full. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://inventory.roblox.com/v1/collections/items/{ITEMTYPE}/{ITEMTARGETID}" ``` ### DELETE `/v1/collections/items/{itemType}/{itemTargetId}` Removes an item to the appropriate collection **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `itemType` | path | `integer (int32)` | Yes | Type of the item (i.e. Asset, Bundle) Valid values: `0`, `1`, `2`, `3` | | `itemTargetId` | path | `integer (int64)` | Yes | ID of the item | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: The item type does not exist. 2: The asset does not exist. 3: The bundle does not exist. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 8: The item is not in the collection. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://inventory.roblox.com/v1/collections/items/{ITEMTYPE}/{ITEMTARGETID}" ``` ## Models ### Roblox.Inventory.Api.Models.AssetIdListModel A model that contains a list of asset ids | Property | Type | Required | Description | |----------|------|----------|-------------| | `assetIds` | `integer[]` | No | The asset ids | ### Roblox.Inventory.Api.Models.AssetsExplorerCategoryItemModel | Property | Type | Required | Description | |----------|------|----------|-------------| | `name` | `string` | No | | | `displayName` | `string` | No | | | `filter` | `string` | No | | | `id` | `integer` | No | | | `type` | `0 \| 1 \| 2 \| 3 \| 4` | No | Describes what type an AssetsExplorerCategoryItemModel contains ['AssetType' = 0, 'Bundle' = 1, 'Outfit' = 2, 'Set' = 3, 'Avatar' = 4] | | `categoryType` | `string` | No | | ### Roblox.Inventory.Api.Models.AssetsExplorerCategoryModel | Property | Type | Required | Description | |----------|------|----------|-------------| | `name` | `string` | No | | | `displayName` | `string` | No | | | `categoryType` | `string` | No | | | `items` | `Roblox.Inventory.Api.Models.AssetsExplorerCategoryItemModel[]` | No | | ### Roblox.Inventory.Api.Models.CanViewInventoryResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `canView` | `boolean` | No | Boolean describing if the user's inventory can be viewed | ### Roblox.Inventory.Api.Models.CategoriesModel Model class that contains the categories of the Inventory or Favorites page | Property | Type | Required | Description | |----------|------|----------|-------------| | `categories` | `Roblox.Inventory.Api.Models.AssetsExplorerCategoryModel[]` | No | Categories to show up in Inventory or Favorites page | ### Roblox.Inventory.Api.Models.CollectibleUserAssetModel A model containing information about a Roblox.Platform.AssetOwnership.UserAsset | Property | Type | Required | Description | |----------|------|----------|-------------| | `userAssetId` | `integer` | No | The user asset id | | `serialNumber` | `integer` | No | The serial number of the user asset | | `assetId` | `integer` | No | The asset id of the user asset | | `name` | `string` | No | The asset name of the asset | | `recentAveragePrice` | `integer` | No | The recent average price of the asset | | `originalPrice` | `integer` | No | The original price of the asset | | `assetStock` | `integer` | No | The recent average price of the user asset | | `buildersClubMembershipType` | `0 \| 1 \| 2 \| 3 \| 4` | No | The recent average price of the user asset ['None' = 0, 'BC' = 1, 'TBC' = 2, 'OBC' = 3, 'RobloxPremium' = 4] | | `isOnHold` | `boolean` | No | Whether the user asset has an active hold. | ### Roblox.Inventory.Api.Models.CreatorModel | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | | | `name` | `string` | No | | | `type` | `1 \| 2 \| 3` | No | ['User' = 1, 'Group' = 2, 'Experience' = 3] | ### Roblox.Inventory.Api.Models.IItemModel Model representing an inventory item | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | The ID of the item | | `name` | `string` | No | The name of the item | | `type` | `0 \| 1 \| 2 \| 3 \| 4` | No | The type of the item ['Asset' = 0, 'GamePass' = 1, 'Badge' = 2, 'Bundle' = 3, 'Avatar' = 4] | | `instanceId` | `integer` | No | The instance id of the item if applicable | ### Roblox.Inventory.Api.Models.PlaceModel | Property | Type | Required | Description | |----------|------|----------|-------------| | `universeId` | `integer` | No | | | `placeId` | `integer` | No | | | `name` | `string` | No | | | `creator` | `Roblox.Inventory.Api.Models.CreatorModel` | No | | | `priceInRobux` | `integer` | No | | ### Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Inventory.Api.Models.CollectibleUserAssetModel] | Property | Type | Required | Description | |----------|------|----------|-------------| | `previousPageCursor` | `string` | No | | | `nextPageCursor` | `string` | No | | | `data` | `Roblox.Inventory.Api.Models.CollectibleUserAssetModel[]` | No | | ### Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Inventory.Api.Models.IItemModel] | Property | Type | Required | Description | |----------|------|----------|-------------| | `previousPageCursor` | `string` | No | | | `nextPageCursor` | `string` | No | | | `data` | `Roblox.Inventory.Api.Models.IItemModel[]` | No | | ### Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Inventory.Api.Models.PlaceModel] | Property | Type | Required | Description | |----------|------|----------|-------------| | `previousPageCursor` | `string` | No | | | `nextPageCursor` | `string` | No | | | `data` | `Roblox.Inventory.Api.Models.PlaceModel[]` | No | | --- name: "Inventory Api v2" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://inventory.roblox.com" version: "v2" endpoints: 5 auth: [cookie] --- # Inventory Api v2 **API Version:** v2 **Base URL:** `https://inventory.roblox.com` ## Endpoints ### GET `/v2/assets/{assetId}/owners` Gets a list of owners of an asset. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetId` | path | `integer (int64)` | Yes | The asset id. | | `limit` | query | `integer (int32)` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `string` | No | Sorted by userAssetId Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Inventory.Api.V2.AssetOwnerResponse]` - `400`: 1: The asset id is invalid. - `403`: 2: You do not have permission to view the owners of this asset. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Inventory.Api.V2.AssetOwnerResponse]`) See [Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Inventory.Api.V2.AssetOwnerResponse]](#roblox-web-webapi-models-apipageresponse-roblox-inventory-api-v2-assetownerresponse-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "id": "...", "collectibleItemInstanceId": "...", "serialNumber": "...", "owner": "...", "created": "...", "updated": "..." } ] } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://inventory.roblox.com/v2/assets/{ASSETID}/owners" ``` ### GET `/v2/collectible-items/{collectibleItemId}/owners` Gets a list of owners of a collectible item. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `collectibleItemId` | path | `string` | Yes | The collectible item ID. | | `limit` | query | `integer (int32)` | No | The number of results per request. | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `integer (int32)` | No | The order the results are sorted in. Valid values: `1`, `2` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Inventory.Api.V2.CollectibleItemOwnerResponse]` - `400`: 1: The collectible item id is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: 2: You do not have permission to view the owners of this item. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Inventory.Api.V2.CollectibleItemOwnerResponse]`) See [Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Inventory.Api.V2.CollectibleItemOwnerResponse]](#roblox-web-webapi-models-apipageresponse-roblox-inventory-api-v2-collectibleitemownerresponse-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "collectibleItemInstanceId": "...", "serialNumber": "...", "owner": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://inventory.roblox.com/v2/collectible-items/{COLLECTIBLEITEMID}/owners" ``` ### GET `/v2/users/{userId}/inventory` Get user's inventory by multiple Roblox.Platform.Assets.AssetType. GamePass and Badges not allowed. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The inventory owner's userId. | | `assetTypes` | query | `array` | Yes | The asset types to query. | | `filterDisapprovedAssets` | query | `boolean` | No | Filters moderated assets when enabled. | | `showApprovedOnly` | query | `boolean` | No | Filters moderated assets and assets pending review when enabled. | | `limit` | query | `integer (int32)` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `string` | No | The order the results are sorted in. Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Inventory.Api.V2.UserAssetItemModelV2]` - `400`: 1: Invalid user Id. 2: Invalid asset type Id. - `403`: 3: Insufficient permission. 4: You are not authorized to view this user's inventory. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Inventory.Api.V2.UserAssetItemModelV2]`) See [Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Inventory.Api.V2.UserAssetItemModelV2]](#roblox-web-webapi-models-apipageresponse-roblox-inventory-api-v2-userassetitemmodelv2-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "assetId": "...", "name": "...", "assetType": "...", "created": "..." } ] } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://inventory.roblox.com/v2/users/{USERID}/inventory?assetTypes={VALUE}" ``` ### GET `/v2/users/{userId}/inventory/{assetTypeId}` Gets user's inventory based on specific asset type **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user Id of the inventory owner | | `assetTypeId` | path | `integer (int32)` | Yes | The asset type Id of the items to get | | `limit` | query | `integer (int32)` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `string` | No | The order the results are sorted in. Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Inventory.Api.Models.InventoryItemModel]` - `400`: 1: Invalid user Id. 2: Invalid asset type Id. - `403`: 3: Insufficient permission. 4: You are not authorized to view this user's inventory. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Inventory.Api.Models.InventoryItemModel]`) See [Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Inventory.Api.Models.InventoryItemModel]](#roblox-web-webapi-models-apipageresponse-roblox-inventory-api-models-inventoryitemmodel-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "expireAt": "...", "userAssetId": "...", "assetId": "...", "assetName": "...", "collectibleItemId": "...", "collectibleItemInstanceId": "..." } ] } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://inventory.roblox.com/v2/users/{USERID}/inventory/{ASSETTYPEID}" ``` ### DELETE `/v2/inventory/asset/{assetId}` Give up an asset owned by the authenticated user. Assets that are created by Roblox user or are limited edition are not eligible for deletion and will return NotEligibleForDelete. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetId` | path | `integer (int64)` | Yes | ID of the asset to delete. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `401`: 0: Authorization has been denied for this request. 4: You are not authorized. - `403`: 0: Token Validation Failed 2: You don't own the specified item. 3: The item is not allowed to be deleted. - `404`: 1: The item does not exist. - `500`: 0: An unknown error occured. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://inventory.roblox.com/v2/inventory/asset/{ASSETID}" ``` ## Models ### Roblox.Inventory.Api.Models.InventoryItemModel A model containing information about an inventory item. | Property | Type | Required | Description | |----------|------|----------|-------------| | `expireAt` | `string` | No | Expiration timestamp for transient items | | `userAssetId` | `integer` | No | The user asset id | | `assetId` | `integer` | No | The asset id of the user asset | | `assetName` | `string` | No | The asset name of the user asset | | `collectibleItemId` | `string` | No | The id of the corresponding collectible item | | `collectibleItemInstanceId` | `string` | No | The id of the corresponding collectible item instance | | `serialNumber` | `integer` | No | The serial number of the user asset | | `owner` | `Roblox.Inventory.Api.Models.UserModel` | No | | | `created` | `string` | No | The creation date of the user asset | | `updated` | `string` | No | The updated date of the user asset | ### Roblox.Inventory.Api.Models.UserModel A model representing data about an Roblox.Platform.Membership.IUser | Property | Type | Required | Description | |----------|------|----------|-------------| | `userId` | `integer` | No | The user id | | `username` | `string` | No | The username | | `buildersClubMembershipType` | `0 \| 1 \| 2 \| 3 \| 4` | No | The user's builders club membership type ['None' = 0, 'BC' = 1, 'TBC' = 2, 'OBC' = 3, 'RobloxPremium' = 4] | ### Roblox.Inventory.Api.V2.AssetOwnerResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | | | `collectibleItemInstanceId` | `string` | No | | | `serialNumber` | `integer` | No | | | `owner` | `Roblox.Web.Responses.RelatedEntityTypeResponse[Roblox.Agents.AgentType]` | No | | | `created` | `string` | No | | | `updated` | `string` | No | | ### Roblox.Inventory.Api.V2.CollectibleItemOwnerResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `collectibleItemInstanceId` | `string` | No | | | `serialNumber` | `integer` | No | | | `owner` | `Roblox.Web.Responses.RelatedEntityTypeResponse[Roblox.Agents.AgentType]` | No | | ### Roblox.Inventory.Api.V2.UserAssetItemModelV2 The user asset item model for V2 controllers. | Property | Type | Required | Description | |----------|------|----------|-------------| | `assetId` | `integer` | No | The asset id of the user asset. | | `name` | `string` | No | The name of asset with id Roblox.Inventory.Api.V2.UserAssetItemModelV2.AssetId. | | `assetType` | `integer enum (85 values)` | No | The asset type id of asset with id Roblox.Inventory.Api.V2.UserAssetItemModelV2.AssetId. ['Image' = 1, 'TShirt' = 2, 'Audio' = 3, 'Mesh' = 4, 'Lua' = 5, 'HTML' = 6, 'Text' = 7, 'Hat' = 8, 'Place' = 9, 'Model' = 10, 'Shirt' = 11, 'Pants' = 12, 'Decal' = 13, 'Avatar' = 16, 'Head' = 17, 'Face' = 18, 'Gear' = 19, 'Badge' = 21, 'GroupEmblem' = 22, 'Animation' = 24, 'Arms' = 25, 'Legs' = 26, 'Torso' = 27, 'RightArm' = 28, 'LeftArm' = 29, 'LeftLeg' = 30, 'RightLeg' = 31, 'Package' = 32, 'YouTubeVideo' = 33, 'GamePass' = 34, 'App' = 35, 'Code' = 37, 'Plugin' = 38, 'SolidModel' = 39, 'MeshPart' = 40, 'HairAccessory' = 41, 'FaceAccessory' = 42, 'NeckAccessory' = 43, 'ShoulderAccessory' = 44, 'FrontAccessory' = 45, 'BackAccessory' = 46, 'WaistAccessory' = 47, 'ClimbAnimation' = 48, 'DeathAnimation' = 49, 'FallAnimation' = 50, 'IdleAnimation' = 51, 'JumpAnimation' = 52, 'RunAnimation' = 53, 'SwimAnimation' = 54, 'WalkAnimation' = 55, 'PoseAnimation' = 56, 'LocalizationTableManifest' = 59, 'LocalizationTableTranslation' = 60, 'EmoteAnimation' = 61, 'Video' = 62, 'TexturePack' = 63, 'TShirtAccessory' = 64, 'ShirtAccessory' = 65, 'PantsAccessory' = 66, 'JacketAccessory' = 67, 'SweaterAccessory' = 68, 'ShortsAccessory' = 69, 'LeftShoeAccessory' = 70, 'RightShoeAccessory' = 71, 'DressSkirtAccessory' = 72, 'FontFamily' = 73, 'FontFace' = 74, 'MeshHiddenSurfaceRemoval' = 75, 'EyebrowAccessory' = 76, 'EyelashAccessory' = 77, 'MoodAnimation' = 78, 'DynamicHead' = 79, 'CodeSnippet' = 80, 'AdsVideo' = 81, 'OtaUpdate' = 82, 'Screenshot' = 83, 'RuntimePropertySet' = 84, 'StorePreviewVideo' = 85, 'GamePreviewVideo' = 86, 'CreatorExperienceConfig' = 87, 'FaceMakeup' = 88, 'LipMakeup' = 89, 'EyeMakeup' = 90, 'VoxelFragment' = 91, 'AvatarBackground' = 92] Values: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 16, 17, 18, 19, 21, 22, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92 | | `created` | `string` | No | The created date time of the user asset. | ### Roblox.Web.Responses.RelatedEntityTypeResponse[Roblox.Agents.AgentType] | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | | | `type` | `1 \| 2` | No | ['User' = 1, 'Group' = 2] | | `name` | `string` | No | | ### Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Inventory.Api.Models.InventoryItemModel] | Property | Type | Required | Description | |----------|------|----------|-------------| | `previousPageCursor` | `string` | No | | | `nextPageCursor` | `string` | No | | | `data` | `Roblox.Inventory.Api.Models.InventoryItemModel[]` | No | | ### Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Inventory.Api.V2.AssetOwnerResponse] | Property | Type | Required | Description | |----------|------|----------|-------------| | `previousPageCursor` | `string` | No | | | `nextPageCursor` | `string` | No | | | `data` | `Roblox.Inventory.Api.V2.AssetOwnerResponse[]` | No | | ### Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Inventory.Api.V2.CollectibleItemOwnerResponse] | Property | Type | Required | Description | |----------|------|----------|-------------| | `previousPageCursor` | `string` | No | | | `nextPageCursor` | `string` | No | | | `data` | `Roblox.Inventory.Api.V2.CollectibleItemOwnerResponse[]` | No | | ### Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Inventory.Api.V2.UserAssetItemModelV2] | Property | Type | Required | Description | |----------|------|----------|-------------| | `previousPageCursor` | `string` | No | | | `nextPageCursor` | `string` | No | | | `data` | `Roblox.Inventory.Api.V2.UserAssetItemModelV2[]` | No | | --- name: "ItemConfiguration Api v1" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://itemconfiguration.roblox.com" version: "v1" endpoints: 2 auth: [cookie] --- # ItemConfiguration Api v1 **API Version:** v1 **Base URL:** `https://itemconfiguration.roblox.com` ## Endpoints ### GET `/v1/creations/get-assets` Gets the user created asset information filtered by the given asset type. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetType` | query | `string` | Yes | | | `isArchived` | query | `boolean` | No | | | `groupId` | query | `integer (int64)` | No | | | `limit` | query | `integer (int32)` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.ItemConfiguration.Api.AssetCreationsResponse]` - `400`: 5: Invalid assetType 10: Invalid Asset Category - `401`: 0: Authorization has been denied for this request. - `403`: 7: User does not have necessary permissions for group 8: Asset type does not have necessary permissions for group - `429`: 9: Flood Limit Exceeded - `503`: 6: Service Unavailable **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.ItemConfiguration.Api.AssetCreationsResponse]`) See [Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.ItemConfiguration.Api.AssetCreationsResponse]](#roblox-web-webapi-models-apipageresponse-roblox-itemconfiguration-api-assetcreationsresponse-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "assetId": "...", "name": "..." } ] } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://itemconfiguration.roblox.com/v1/creations/get-assets?assetType={VALUE}" ``` ### POST `/v1/creations/get-asset-details` Gets the asset status and other configuration details for the given assetIds list. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.ItemConfiguration.Api.AssetCreationsDetailsRequest` See [Roblox.ItemConfiguration.Api.AssetCreationsDetailsRequest](#roblox-itemconfiguration-api-assetcreationsdetailsrequest) in Models. **Request example:** ```json { "AssetIds": [ 0 ] } ``` **Responses:** - `200`: OK → `Roblox.ItemConfiguration.Api.AssetCreationsDetailsResponse[]` - `400`: 1: Missing AssetIds parameters 2: Invalid asset Ids - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `414`: 3: Too many asset Ids - `429`: 9: Flood Limit Exceeded - `503`: 6: Service Unavailable **Response fields** (`Roblox.ItemConfiguration.Api.AssetCreationsDetailsResponse[]`) See [Roblox.ItemConfiguration.Api.AssetCreationsDetailsResponse](#roblox-itemconfiguration-api-assetcreationsdetailsresponse) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://itemconfiguration.roblox.com/v1/creations/get-asset-details" \ -H "Content-Type: application/json" \ -d '{ "AssetIds": [ 0 ] }' ``` ## Models ### Roblox.ItemConfiguration.Api.AssetCreationsDetailsRequest | Property | Type | Required | Description | |----------|------|----------|-------------| | `AssetIds` | `integer[]` | No | | ### Roblox.ItemConfiguration.Api.AssetCreationsDetailsResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `assetId` | `integer` | No | The asset Id. | | `name` | `string` | No | The asset name. | | `status` | `string enum (8 values)` | No | The asset status. ['Unknown' = 0, 'ReviewPending' = 1, 'Moderated' = 2, 'ReviewApproved' = 3, 'OnSale' = 4, 'OffSale' = 5, 'DelayedRelease' = 6, 'Free' = 7] Values: Unknown, ReviewPending, Moderated, ReviewApproved, OnSale, OffSale, DelayedRelease, Free | | `description` | `string` | No | The asset description. | | `creatorType` | `Unknown \| User \| Group` | No | The creator type. ['Unknown' = 0, 'User' = 1, 'Group' = 2] | | `creatorTargetId` | `integer` | No | The creator target Id. | | `price` | `integer` | No | The Price for onSale asset Note: This is now considered deprecated in favor of PriceConfiguration. | | `priceConfiguration` | `Roblox.ItemConfiguration.Api.PriceConfigurationModel` | No | | | `isArchived` | `boolean` | No | Is the asset archived. | | `assetType` | `string` | No | Type of the asset. | | `releaseConfiguration` | `Roblox.ItemConfiguration.Api.ReleaseConfigurationResponseModel` | No | | | `created` | `string` | No | Date asset was created. | | `updated` | `string` | No | Date asset was created. | | `isDelisted` | `boolean` | No | If the asset is delisted. | | `isCreatedForBundle` | `boolean` | No | If the asset is part of a bundle. | ### Roblox.ItemConfiguration.Api.AssetCreationsResponse Asset Status response model. | Property | Type | Required | Description | |----------|------|----------|-------------| | `assetId` | `integer` | No | The asset Id. | | `name` | `string` | No | The asset name. | ### Roblox.ItemConfiguration.Api.PriceConfigurationModel Defines the configuration options for an items price. | Property | Type | Required | Description | |----------|------|----------|-------------| | `priceInRobux` | `integer` | No | Gets or sets the standard price of the item in Robux. | | `premiumDiscountPercentage` | `integer` | No | Gets or sets the discount rate on the price of the item that is given to premium users Should not be used while applying a PremiumPriceInRobux. | | `premiumPriceInRobux` | `integer` | No | Gets or sets the price of the item in Robux that applies only to premium users Should not be used while applying a PremiumDiscountPercentage. | | `priceOffset` | `integer` | No | Gets or sets the price offset of the item in Robux above the price floor. | ### Roblox.ItemConfiguration.Api.ReleaseConfigurationResponseModel Defines the configuration options associated with releasing an item. | Property | Type | Required | Description | |----------|------|----------|-------------| | `saleAvailabilityLocations` | `Undefined \| Catalog \| AllUniverses \| MyUniverses[]` | No | Get or sets the sale availability locations list. | ### Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.ItemConfiguration.Api.AssetCreationsResponse] | Property | Type | Required | Description | |----------|------|----------|-------------| | `previousPageCursor` | `string` | No | | | `nextPageCursor` | `string` | No | | | `data` | `Roblox.ItemConfiguration.Api.AssetCreationsResponse[]` | No | | --- name: "Locale Api v1" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://locale.roblox.com" version: "v1" endpoints: 9 auth: [cookie] --- # Locale Api v1 **API Version:** v1 **Base URL:** `https://locale.roblox.com` ## Endpoints ### GET `/v1/country-regions` Get list of country regions sorted by localized name **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `locale` | query | `string` | No | | **Responses:** - `200`: OK → `Roblox.Locale.Api.CountryRegionListResponse` - `400`: 2: Invalid supported locale code. - `403`: 7: Feature is disabled **Response fields** (`Roblox.Locale.Api.CountryRegionListResponse`) See [Roblox.Locale.Api.CountryRegionListResponse](#roblox-locale-api-countryregionlistresponse) in Models. **Response example:** ```json { "countryRegionList": [ { "code": "...", "name": "...", "displayName": "..." } ] } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://locale.roblox.com/v1/country-regions" ``` ### GET `/v1/locales` Get list of Supported locales with user locus information. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `displayValueLocale` | query | `string` | No | | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Locale.Api.SupportedLocaleLocus]` - `403`: Feature is turned off temporary - `500`: Internal server error **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Locale.Api.SupportedLocaleLocus]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Locale.Api.SupportedLocaleLocus]](#roblox-web-webapi-models-apiarrayresponse-roblox-locale-api-supportedlocalelocus-) in Models. **Response example:** ```json { "data": [ { "locale": "...", "isEnabledForFullExperience": "...", "isEnabledForSignupAndLogin": "...", "isEnabledForInGameUgc": "..." } ] } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://locale.roblox.com/v1/locales" ``` ### GET `/v1/locales/supported-locales` Get list of supported locales sorted by the Native Name property. **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Locale.Api.SupportedLocalesResponse` - `403`: Feature is turned off temporary - `500`: Internal server error **Response fields** (`Roblox.Locale.Api.SupportedLocalesResponse`) See [Roblox.Locale.Api.SupportedLocalesResponse](#roblox-locale-api-supportedlocalesresponse) in Models. **Response example:** ```json { "supportedLocales": [ { "id": "...", "locale": "...", "name": "...", "nativeName": "...", "language": "..." } ] } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://locale.roblox.com/v1/locales/supported-locales" ``` ### GET `/v1/locales/supported-locales-for-creators` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `displayValueLocale` | query | `string` | No | | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Locale.Api.SupportedLocaleLocus]` **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Locale.Api.SupportedLocaleLocus]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Locale.Api.SupportedLocaleLocus]](#roblox-web-webapi-models-apiarrayresponse-roblox-locale-api-supportedlocalelocus-) in Models. **Response example:** ```json { "data": [ { "locale": "...", "isEnabledForFullExperience": "...", "isEnabledForSignupAndLogin": "...", "isEnabledForInGameUgc": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://locale.roblox.com/v1/locales/supported-locales-for-creators" ``` ### GET `/v1/locales/supported-locales-for-feature` Get list of Supported locales for a specific feature. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `featureName` | query | `string` | Yes | | **Responses:** - `200`: OK → `Roblox.Locale.Api.SupportedLocalesResponse` - `500`: Internal server error **Response fields** (`Roblox.Locale.Api.SupportedLocalesResponse`) See [Roblox.Locale.Api.SupportedLocalesResponse](#roblox-locale-api-supportedlocalesresponse) in Models. **Response example:** ```json { "supportedLocales": [ { "id": "...", "locale": "...", "name": "...", "nativeName": "...", "language": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://locale.roblox.com/v1/locales/supported-locales-for-feature?featureName={VALUE}" ``` ### GET `/v1/locales/user-locale` Gets user locale. If user is absent returns, locale from http request object. **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Locale.Api.UserLocaleResponse` - `500`: Internal server error **Response fields** (`Roblox.Locale.Api.UserLocaleResponse`) See [Roblox.Locale.Api.UserLocaleResponse](#roblox-locale-api-userlocaleresponse) in Models. **Response example:** ```json { "supportedLocale": { "id": 0, "locale": "string", "name": "string", "nativeName": "string", "language": { "id": "...", "name": "...", "nativeName": "...", "languageCode": "...", "isRightToLeft": "..." } }, "nativeLanguage": { "id": 0, "name": "string", "nativeName": "string", "languageCode": "string", "isRightToLeft": false } } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://locale.roblox.com/v1/locales/user-locale" ``` ### GET `/v1/locales/user-localization-locus-supported-locales` Gets each of a user's localization locus supported locales. A localization locus supported locale is a page (or group of pages) that have been defined by the International team which need independent locale support. If the user is null we will attempt to return the locales appropriate for the user's device language. **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Locale.Api.UserLocalizationLocusLocalesResponse` - `500`: Internal server error **Response fields** (`Roblox.Locale.Api.UserLocalizationLocusLocalesResponse`) See [Roblox.Locale.Api.UserLocalizationLocusLocalesResponse](#roblox-locale-api-userlocalizationlocuslocalesresponse) in Models. **Response example:** ```json { "signupAndLogin": { "id": 0, "locale": "string", "name": "string", "nativeName": "string", "language": { "id": "...", "name": "...", "nativeName": "...", "languageCode": "...", "isRightToLeft": "..." } }, "generalExperience": { "id": 0, "locale": "string", "name": "string", "nativeName": "string", "language": { "id": "...", "name": "...", "nativeName": "...", "languageCode": "...", "isRightToLeft": "..." } }, "ugc": { "id": 0, "locale": "string", "name": "string", "nativeName": "string", "language": { "id": "...", "name": "...", "nativeName": "...", "languageCode": "...", "isRightToLeft": "..." } }, "showRobloxTranslations": false } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://locale.roblox.com/v1/locales/user-localization-locus-supported-locales" ``` ### POST `/v1/locales/set-show-roblox-translations` Sets whether translations suggested by Roblox will be shown to the user. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Locale.Api.SetShowRobloxTranslationsRequest` See [Roblox.Locale.Api.SetShowRobloxTranslationsRequest](#roblox-locale-api-setshowrobloxtranslationsrequest) in Models. **Request example:** ```json { "showRobloxTranslations": false } ``` **Responses:** - `200`: OK → `Roblox.Locale.Api.SuccessResponse` - `400`: Bad Request - `401`: Unauthorized 0: Authorization has been denied for this request. - `403`: Feature is turned off temporary 0: Token Validation Failed - `500`: Internal server error **Response fields** (`Roblox.Locale.Api.SuccessResponse`) See [Roblox.Locale.Api.SuccessResponse](#roblox-locale-api-successresponse) in Models. **Response example:** ```json { "success": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://locale.roblox.com/v1/locales/set-show-roblox-translations" \ -H "Content-Type: application/json" \ -d '{ "showRobloxTranslations": false }' ``` ### POST `/v1/locales/set-user-supported-locale` Sets user's supported locale. Null supported locale will clear out user's supported locale (set users' supported locale to null) **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Locale.Api.SetSupportedLocaleForUserRequest` See [Roblox.Locale.Api.SetSupportedLocaleForUserRequest](#roblox-locale-api-setsupportedlocaleforuserrequest) in Models. **Request example:** ```json { "supportedLocaleCode": "string" } ``` **Responses:** - `200`: OK → `Roblox.Locale.Api.SuccessResponse` - `400`: Bad Request - `401`: Unauthorized 0: Authorization has been denied for this request. - `403`: Feature is turned off temporary 0: Token Validation Failed - `500`: Internal server error **Response fields** (`Roblox.Locale.Api.SuccessResponse`) See [Roblox.Locale.Api.SuccessResponse](#roblox-locale-api-successresponse) in Models. **Response example:** ```json { "success": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://locale.roblox.com/v1/locales/set-user-supported-locale" \ -H "Content-Type: application/json" \ -d '{ "supportedLocaleCode": "string" }' ``` ## Models ### Roblox.Locale.Api.CountryRegion Model for Country Regions | Property | Type | Required | Description | |----------|------|----------|-------------| | `code` | `string` | No | code of country region | | `name` | `string` | No | native name of country region | | `displayName` | `string` | No | localized name of country region. Example "Afghanistan" | ### Roblox.Locale.Api.CountryRegionListResponse Returns list of supported country/regions | Property | Type | Required | Description | |----------|------|----------|-------------| | `countryRegionList` | `Roblox.Locale.Api.CountryRegion[]` | No | List of supported country/regions. Will be empty on error. | ### Roblox.Locale.Api.Language Model for Language | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | id of language | | `name` | `string` | No | name of language | | `nativeName` | `string` | No | native name of language | | `languageCode` | `string` | No | language code of language | | `isRightToLeft` | `boolean` | No | whether or not the language is read right-to-left | ### Roblox.Locale.Api.SetShowRobloxTranslationsRequest Request entity to set the ShowRobloxTranslations field for an account | Property | Type | Required | Description | |----------|------|----------|-------------| | `showRobloxTranslations` | `boolean` | No | Value to set the ShowRobloxTranslations field to | ### Roblox.Locale.Api.SetSupportedLocaleForUserRequest Request entity to set Supported Locale for user | Property | Type | Required | Description | |----------|------|----------|-------------| | `supportedLocaleCode` | `string` | No | SupportedLocale code | ### Roblox.Locale.Api.SuccessResponse Tells the operation is successful or not | Property | Type | Required | Description | |----------|------|----------|-------------| | `success` | `boolean` | No | Returns true on success otherwise false | ### Roblox.Locale.Api.SupportedLocale Model for Supported locale | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | id of supported locale | | `locale` | `string` | No | locale of supported locale. Example "en-us" | | `name` | `string` | No | Name of supported locale. | | `nativeName` | `string` | No | Name of supported locale in native language. Example "English" | | `language` | `Roblox.Locale.Api.Language` | No | | ### Roblox.Locale.Api.SupportedLocaleLocus Model for Supported locale with user locus information | Property | Type | Required | Description | |----------|------|----------|-------------| | `locale` | `Roblox.Locale.Api.SupportedLocale` | No | | | `isEnabledForFullExperience` | `boolean` | No | Is locale enabled for full experience | | `isEnabledForSignupAndLogin` | `boolean` | No | Is locale enabled for signup and login | | `isEnabledForInGameUgc` | `boolean` | No | Is locale enabled for in game ugc | ### Roblox.Locale.Api.SupportedLocalesResponse Returns list of supported locales | Property | Type | Required | Description | |----------|------|----------|-------------| | `supportedLocales` | `Roblox.Locale.Api.SupportedLocale[]` | No | List of supported locales. Will be empty on error. | ### Roblox.Locale.Api.UserLocaleResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `supportedLocale` | `Roblox.Locale.Api.SupportedLocale` | No | | | `nativeLanguage` | `Roblox.Locale.Api.Language` | No | | ### Roblox.Locale.Api.UserLocalizationLocusLocalesResponse Returns available Roblox.Locale.Api.SupportedLocale models. | Property | Type | Required | Description | |----------|------|----------|-------------| | `signupAndLogin` | `Roblox.Locale.Api.SupportedLocale` | No | | | `generalExperience` | `Roblox.Locale.Api.SupportedLocale` | No | | | `ugc` | `Roblox.Locale.Api.SupportedLocale` | No | | | `showRobloxTranslations` | `boolean` | No | Whether Roblox-suggested translations should be shown to the user. | ### Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Locale.Api.SupportedLocaleLocus] | Property | Type | Required | Description | |----------|------|----------|-------------| | `data` | `Roblox.Locale.Api.SupportedLocaleLocus[]` | No | | --- name: "LocalizationTables Api v1" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://localizationtables.roblox.com" version: "v1" endpoints: 18 auth: [cookie] --- # LocalizationTables Api v1 **API Version:** v1 **Base URL:** `https://localizationtables.roblox.com` ## Endpoints ### GET `/v1/autolocalization/metadata` Metadata for AutoLocalization Configuration **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.LocalizationTables.Api.AutoLocalizationMetadataResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.LocalizationTables.Api.AutoLocalizationMetadataResponse`) See [Roblox.LocalizationTables.Api.AutoLocalizationMetadataResponse](#roblox-localizationtables-api-autolocalizationmetadataresponse) in Models. **Response example:** ```json { "isReactVersionEnabledForAutoLocalizationSettings": false, "isTabbedUIEnabledForConfigureLocalizationPage": false, "isAutomaticTranslationToggleUIEnabled": false, "isAutomaticTranslationQuotaUIEnabled": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://localizationtables.roblox.com/v1/autolocalization/metadata" ``` ### GET `/v1/localization-table/available-languages` *(deprecated)* Gets the supported language codes that can be used by localization tables. **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.LocalizationTables.Api.Language]` **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.LocalizationTables.Api.Language]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.LocalizationTables.Api.Language]](#roblox-web-webapi-models-apiarrayresponse-roblox-localizationtables-api-language-) in Models. **Response example:** ```json { "data": [ { "name": "...", "nativeName": "...", "languageCode": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://localizationtables.roblox.com/v1/localization-table/available-languages" ``` ### GET `/v1/localization-table/limits` Get limits for translation table entries operations **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.LocalizationTables.Api.GetLimitsResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.LocalizationTables.Api.GetLimitsResponse`) See [Roblox.LocalizationTables.Api.GetLimitsResponse](#roblox-localizationtables-api-getlimitsresponse) in Models. **Response example:** ```json { "entryOperationLimits": { "maxContextLength": 0, "maxKeyLength": 0, "maxSourceLength": 0, "maxExampleLength": 0, "maxGameLocationPathLength": 0 }, "tableOperationLimits": { "maxEntriesPerUpdate": 0 } } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://localizationtables.roblox.com/v1/localization-table/limits" ``` ### GET `/v1/localization-table/metadata` Get metadata for localization UI **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.LocalizationTables.Api.LocalizationTablesMetadataResponse` **Response fields** (`Roblox.LocalizationTables.Api.LocalizationTablesMetadataResponse`) See [Roblox.LocalizationTables.Api.LocalizationTablesMetadataResponse](#roblox-localizationtables-api-localizationtablesmetadataresponse) in Models. **Response example:** ```json { "isBulkUploadFeatureEnabled": false, "isCsvDownloadEnabled": false, "isAccessToTranslationMetaDataEnabled": false, "isTranslationManagementRedirectionEnabled": false, "isUntranslatedFilterEnabled": false, "isAutomaticTranslationFilterEnabled": false } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://localizationtables.roblox.com/v1/localization-table/metadata" ``` ### GET `/v1/localization-table/tables/{assetId}` Get table information by the assetId of the table. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetId` | path | `integer (int64)` | Yes | The asset id associated with the table. | **Responses:** - `200`: OK → `Roblox.LocalizationTables.Api.GetTableResponse` - `400`: 12: Invalid asset id. - `401`: 0: Authorization has been denied for this request. - `403`: 2: You do not have permission to get this table. **Response fields** (`Roblox.LocalizationTables.Api.GetTableResponse`) See [Roblox.LocalizationTables.Api.GetTableResponse](#roblox-localizationtables-api-gettableresponse) in Models. **Response example:** ```json { "id": "string", "name": "string", "ownerType": "User", "ownerId": 0, "assetId": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://localizationtables.roblox.com/v1/localization-table/tables/{ASSETID}" ``` ### GET `/v1/localization-table/tables/{tableId}` Get table information by the id of the table. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `tableId` | path | `string (uuid)` | Yes | | **Responses:** - `200`: OK → `Roblox.LocalizationTables.Api.GetTableResponse` - `400`: 3: Invalid table id. - `401`: 0: Authorization has been denied for this request. - `403`: 2: You do not have permission to get this table. **Response fields** (`Roblox.LocalizationTables.Api.GetTableResponse`) See [Roblox.LocalizationTables.Api.GetTableResponse](#roblox-localizationtables-api-gettableresponse) in Models. **Response example:** ```json { "id": "string", "name": "string", "ownerType": "User", "ownerId": 0, "assetId": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://localizationtables.roblox.com/v1/localization-table/tables/{TABLEID}" ``` ### PATCH `/v1/localization-table/tables/{tableId}` Updates the tables contents based on what is provided. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `tableId` | path | `string (uuid)` | Yes | The table guid for the table to update. | | `gameId` | query | `integer (int64)` | No | The game id. | **Request Body:** `application/json` — Type: `Roblox.LocalizationTables.Api.UpdateTableContentsRequest` See [Roblox.LocalizationTables.Api.UpdateTableContentsRequest](#roblox-localizationtables-api-updatetablecontentsrequest) in Models. **Request example:** ```json { "name": "string", "entries": [ { "identifier": "...", "metadata": "...", "translations": "...", "delete": "..." } ] } ``` **Responses:** - `200`: OK → `Roblox.LocalizationTables.Api.UpdateTableContentsResponse` - `400`: 3: Invalid table id. 4: Table does not exist. 10: Maximum entries exceeded. Please keep the number of entries per request below the maximum. 13: Request body can't be null 14: Invalid game id - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 6: You do not have permission to create this table. - `503`: 17: Feature is disabled **Response fields** (`Roblox.LocalizationTables.Api.UpdateTableContentsResponse`) See [Roblox.LocalizationTables.Api.UpdateTableContentsResponse](#roblox-localizationtables-api-updatetablecontentsresponse) in Models. **Response example:** ```json { "failedEntriesAndTranslations": [ { "error": "...", "identifier": "...", "metadata": "...", "translations": "...", "createdTime": "..." } ], "modifiedEntriesAndTranslations": [ { "identifier": "...", "translations": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://localizationtables.roblox.com/v1/localization-table/tables/{TABLEID}" \ -H "Content-Type: application/json" \ -d '{ "name": "string", "entries": [ { "identifier": "...", "metadata": "...", "translations": "...", "delete": "..." } ] }' ``` ### GET `/v1/localization-table/tables/{tableId}/entries` Gets a batch of entries for a table. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `tableId` | path | `string (uuid)` | Yes | | | `cursor` | query | `string` | No | If null, there are no more entries in the table and you've reached the last page. | | `gameId` | query | `integer (int64)` | No | | | `entryFormat` | query | `string` | No | Valid values: `Invalid`, `Legacy`, `Icu` | **Responses:** - `200`: OK → `Roblox.LocalizationTables.Api.GetTableEntriesPagedResponse` - `400`: 3: Invalid table id. 14: Invalid game id - `401`: 0: Authorization has been denied for this request. - `403`: 2: You do not have permission to get this table. **Response fields** (`Roblox.LocalizationTables.Api.GetTableEntriesPagedResponse`) See [Roblox.LocalizationTables.Api.GetTableEntriesPagedResponse](#roblox-localizationtables-api-gettableentriespagedresponse) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "identifier": "...", "metadata": "...", "translations": "...", "createdTime": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://localizationtables.roblox.com/v1/localization-table/tables/{TABLEID}/entries" ``` ### GET `/v1/localization-table/tables/{tableId}/entry-count` Gets the number of entries in the specified table **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `tableId` | path | `string (uuid)` | Yes | The table id | | `gameId` | query | `integer (int64)` | No | The game id | | `entryFormat` | query | `string` | No | Valid values: `Invalid`, `Legacy`, `Icu` | **Responses:** - `200`: OK → `Roblox.LocalizationTables.Api.GetTableEntryCountResponse` - `400`: 3: Invalid table id. 14: Invalid game id - `401`: 0: Authorization has been denied for this request. - `403`: 2: You do not have permission to get this table. **Response fields** (`Roblox.LocalizationTables.Api.GetTableEntryCountResponse`) See [Roblox.LocalizationTables.Api.GetTableEntryCountResponse](#roblox-localizationtables-api-gettableentrycountresponse) in Models. **Response example:** ```json { "id": "string", "entryCount": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://localizationtables.roblox.com/v1/localization-table/tables/{TABLEID}/entry-count" ``` ### POST `/v1/auto-localization-table/games/{gameId}/assets-generation-request` Generates localization asset of a game. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer (int64)` | Yes | The game id. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 3: Invalid table id. 14: Invalid game id 29: You do not have permission to generate asset for this table. 32: LocalizationTable is not available for the game. 34: Actor provided is invalid - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `429`: 24: Too many attempts.Please try again later. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://localizationtables.roblox.com/v1/auto-localization-table/games/{GAMEID}/assets-generation-request" ``` ### POST `/v1/auto-localization-table/games/{gameId}/auto-scrape-cleanup-request` Enqueues an event to flush the auto scraped entries which doesn't have translations. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer (int64)` | Yes | The id of the game. | **Request Body:** `application/json` — Type: `Roblox.LocalizationTables.Api.RaiseEventForAutoScrapedEntriesCleanupRequest` See [Roblox.LocalizationTables.Api.RaiseEventForAutoScrapedEntriesCleanupRequest](#roblox-localizationtables-api-raiseeventforautoscrapedentriescleanuprequest) in Models. **Request example:** ```json { "maxAgeForFlush": "string" } ``` **Responses:** - `200`: OK → `object` - `400`: 14: Invalid game id 31: You do not have permission to flush auto scraped entries asset for this game. 32: LocalizationTable is not available for the game. 34: Actor provided is invalid - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `429`: 33: Too many attempts to flush the game.Please try again later. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://localizationtables.roblox.com/v1/auto-localization-table/games/{GAMEID}/auto-scrape-cleanup-request" \ -H "Content-Type: application/json" \ -d '{ "maxAgeForFlush": "string" }' ``` ### POST `/v1/autolocalization/games/{gameId}/autolocalizationtable` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer (int64)` | Yes | | **Responses:** - `200`: OK → `Roblox.LocalizationTables.Api.GameAutolocalizationInformationResponse` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.LocalizationTables.Api.GameAutolocalizationInformationResponse`) See [Roblox.LocalizationTables.Api.GameAutolocalizationInformationResponse](#roblox-localizationtables-api-gameautolocalizationinformationresponse) in Models. **Response example:** ```json { "isAutolocalizationEnabled": false, "isAutomaticEntriesSettingEnabled": false, "isAutomaticEntriesDeletionEnabled": false, "shouldUseLocalizationTable": false, "autoLocalizationTableId": "string", "sourceLanguage": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://localizationtables.roblox.com/v1/autolocalization/games/{GAMEID}/autolocalizationtable" ``` ### PATCH `/v1/autolocalization/games/{gameId}/autolocalizationtable` **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer (int64)` | Yes | | **Request Body:** `application/json` — Type: `Roblox.LocalizationTables.Api.SetAutolocalizationTableForGameRequest` See [Roblox.LocalizationTables.Api.SetAutolocalizationTableForGameRequest](#roblox-localizationtables-api-setautolocalizationtableforgamerequest) in Models. **Request example:** ```json { "tableId": "string" } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://localizationtables.roblox.com/v1/autolocalization/games/{GAMEID}/autolocalizationtable" \ -H "Content-Type: application/json" \ -d '{ "tableId": "string" }' ``` ### POST `/v1/localization-table/tables` Creates a Localization Table with the given data. Note that this endpoint simply creates a table and does not associate it with any universe, so if intending to use this to create tables usable in experience more setup will be needed to grant those experiences access. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.LocalizationTables.Api.CreateTableRequest` See [Roblox.LocalizationTables.Api.CreateTableRequest](#roblox-localizationtables-api-createtablerequest) in Models. **Request example:** ```json { "name": "string", "ownerType": "User", "ownerId": 0 } ``` **Responses:** - `200`: OK → `Roblox.LocalizationTables.Api.CreateTableResponse` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: You do not have permission to get this table. **Response fields** (`Roblox.LocalizationTables.Api.CreateTableResponse`) See [Roblox.LocalizationTables.Api.CreateTableResponse](#roblox-localizationtables-api-createtableresponse) in Models. **Response example:** ```json { "id": "string", "assetId": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://localizationtables.roblox.com/v1/localization-table/tables" \ -H "Content-Type: application/json" \ -d '{ "name": "string", "ownerType": "User", "ownerId": 0 }' ``` ### POST `/v1/localization-table/tables/{tableId}/entries/translation-feedback` Gets the translation feedback for each entry passed in. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `tableId` | path | `string (uuid)` | Yes | The entries' tableId. | | `gameId` | query | `integer (int64)` | No | The game id. | **Request Body:** `application/json` — Type: `Roblox.LocalizationTables.Api.GetTableEntriesTranslationFeedbackRequest` See [Roblox.LocalizationTables.Api.GetTableEntriesTranslationFeedbackRequest](#roblox-localizationtables-api-gettableentriestranslationfeedbackrequest) in Models. **Request example:** ```json { "sourceLocale": "string", "entries": [ { "translation": "...", "key": "...", "context": "...", "source": "...", "entryFormat": "..." } ] } ``` **Responses:** - `200`: OK → `Roblox.LocalizationTables.Api.GetTableEntriesTranslationFeedbackResponse` - `400`: 3: Invalid table id. 13: Request body can't be null 14: Invalid game id 16: Entries can't be null or empty 35: The entries provided are invalid 37: Invalid locale code. 38: Invalid entry identifier. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: You do not have permission to get this table. - `429`: 24: Too many attempts.Please try again later. - `503`: 17: Feature is disabled **Response fields** (`Roblox.LocalizationTables.Api.GetTableEntriesTranslationFeedbackResponse`) See [Roblox.LocalizationTables.Api.GetTableEntriesTranslationFeedbackResponse](#roblox-localizationtables-api-gettableentriestranslationfeedbackresponse) in Models. **Response example:** ```json { "tableId": "string", "entries": [ { "identifier": "...", "feedbackCount": "...", "playerSuggestionText": "...", "reasons": "...", "robloxSuggestionText": "..." } ] } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://localizationtables.roblox.com/v1/localization-table/tables/{TABLEID}/entries/translation-feedback" \ -H "Content-Type: application/json" \ -d '{ "sourceLocale": "string", "entries": [ { "translation": "...", "key": "...", "context": "...", "source": "...", "entryFormat": "..." } ] }' ``` ### POST `/v1/localization-table/tables/{tableId}/entries/translation-history` Gets the translation history for each entry passed in. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `tableId` | path | `string (uuid)` | Yes | The entries' tableId. | | `gameId` | query | `integer (int64)` | No | The game id. | **Request Body:** `application/json` — Type: `Roblox.LocalizationTables.Api.GetTableEntriesTranslationHistoryRequest` See [Roblox.LocalizationTables.Api.GetTableEntriesTranslationHistoryRequest](#roblox-localizationtables-api-gettableentriestranslationhistoryrequest) in Models. **Request example:** ```json { "locale": "string", "entries": [ { "cursor": "...", "identifier": "...", "count": "...", "sortOrder": "..." } ] } ``` **Responses:** - `200`: OK → `Roblox.LocalizationTables.Api.GetTableEntriesTranslationHistoryResponse` - `400`: 3: Invalid table id. 13: Request body can't be null 14: Invalid game id 16: Entries can't be null or empty 35: The entries provided are invalid 37: Invalid locale code. 38: Invalid entry identifier. 39: Count should be at least 1. 45: Invalid exclusive start id. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: You do not have permission to get this table. - `429`: 24: Too many attempts.Please try again later. - `503`: 17: Feature is disabled **Response fields** (`Roblox.LocalizationTables.Api.GetTableEntriesTranslationHistoryResponse`) See [Roblox.LocalizationTables.Api.GetTableEntriesTranslationHistoryResponse](#roblox-localizationtables-api-gettableentriestranslationhistoryresponse) in Models. **Response example:** ```json { "tableId": "string", "locale": "string", "entries": [ { "identifier": "...", "history": "...", "nextCursor": "..." } ], "failedEntries": [ { "identifier": "...", "count": "...", "error": "..." } ] } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://localizationtables.roblox.com/v1/localization-table/tables/{TABLEID}/entries/translation-history" \ -H "Content-Type: application/json" \ -d '{ "locale": "string", "entries": [ { "cursor": "...", "identifier": "...", "count": "...", "sortOrder": "..." } ] }' ``` ### PATCH `/v1/auto-localization-table/games/{gameId}/ingestion` Ingests entries for auto localization. Needs to be an authorized user. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer (int64)` | Yes | The game id. | **Request Body:** `application/json` — Type: `Roblox.LocalizationTables.Api.IngestAutoScrapedContentForGameRequest` See [Roblox.LocalizationTables.Api.IngestAutoScrapedContentForGameRequest](#roblox-localizationtables-api-ingestautoscrapedcontentforgamerequest) in Models. **Request example:** ```json { "entries": [ { "context": "...", "source": "...", "screenshot": "...", "meta": "..." } ], "metadata": { "placeInformation": { "placeId": "...", "placeVersionNumber": "..." } } } ``` **Responses:** - `200`: OK → `object` - `400`: 10: Maximum entries exceeded. Please keep the number of entries per request below the maximum. 13: Request body can't be null 16: Entries can't be null or empty 34: Actor provided is invalid - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `503`: 17: Feature is disabled **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://localizationtables.roblox.com/v1/auto-localization-table/games/{GAMEID}/ingestion" \ -H "Content-Type: application/json" \ -d '{ "entries": [ { "context": "...", "source": "...", "screenshot": "...", "meta": "..." } ], "metadata": { "placeInformation": { "placeId": "...", "placeVersionNumber": "..." } } }' ``` ### PATCH `/v1/autolocalization/games/{gameId}/settings` Sets a game's auto-localization related settings **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer (int64)` | Yes | The id of the game. | **Request Body:** `application/json` — Type: `Roblox.LocalizationTables.Api.SetAutolocalizationSettingsForGameRequest` See [Roblox.LocalizationTables.Api.SetAutolocalizationSettingsForGameRequest](#roblox-localizationtables-api-setautolocalizationsettingsforgamerequest) in Models. **Request example:** ```json { "isAutolocalizationEnabled": false, "isAutomaticEntriesSettingEnabled": false, "isAutomaticEntriesDeletionsEnabled": false, "shouldUseLocalizationTable": false } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 14: Invalid game id 61: IsAutomaticEntriesSettingEnabled can only be enabled if IsAutolocalizationEnabled is also enabled. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 18: You do not have permission to manage this game - `503`: 17: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://localizationtables.roblox.com/v1/autolocalization/games/{GAMEID}/settings" \ -H "Content-Type: application/json" \ -d '{ "isAutolocalizationEnabled": false, "isAutomaticEntriesSettingEnabled": false, "isAutomaticEntriesDeletionsEnabled": false, "shouldUseLocalizationTable": false }' ``` ## Models ### Roblox.InGameContentTables.Client.GameLocation | Property | Type | Required | Description | |----------|------|----------|-------------| | `path` | `string` | No | | ### Roblox.LocalizationTables.Api.AutoLocalizationMetadataResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `isReactVersionEnabledForAutoLocalizationSettings` | `boolean` | No | Is React Implementation of AutoLocalization Settings Enabled | | `isTabbedUIEnabledForConfigureLocalizationPage` | `boolean` | No | Is Tabbed UI Enabled for Configure Localization Page | | `isAutomaticTranslationToggleUIEnabled` | `boolean` | No | Is Toggle UI Enabled for Automatic Translations | | `isAutomaticTranslationQuotaUIEnabled` | `boolean` | No | Is Quota UI Enabled for Automatic Translations | ### Roblox.LocalizationTables.Api.AutoScrapeEntry | Property | Type | Required | Description | |----------|------|----------|-------------| | `context` | `string` | No | | | `source` | `string` | No | | | `screenshot` | `string` | No | | | `meta` | `Roblox.LocalizationTables.Api.AutoScrapeEntryMetadata` | No | | ### Roblox.LocalizationTables.Api.AutoScrapeEntryMetadata | Property | Type | Required | Description | |----------|------|----------|-------------| | `text` | `string` | No | | | `userId` | `integer` | No | | | `osPlatform` | `string` | No | | | `sessionId` | `string` | No | | | `matchedEntry` | `Roblox.LocalizationTables.Api.MatchedEntry` | No | | ### Roblox.LocalizationTables.Api.CreateTableRequest | Property | Type | Required | Description | |----------|------|----------|-------------| | `name` | `string` | No | | | `ownerType` | `User \| Group` | No | | | `ownerId` | `integer` | No | | ### Roblox.LocalizationTables.Api.CreateTableResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `string` | No | | | `assetId` | `integer` | No | | ### Roblox.LocalizationTables.Api.CursorEntryIdentifier A model that contains an entry identifier and an associated cursor for paged lookups. | Property | Type | Required | Description | |----------|------|----------|-------------| | `cursor` | `string` | No | The location to begin our query. | | `identifier` | `Roblox.LocalizationTables.Api.EntryIdentifier` | No | | | `count` | `integer` | No | The translation history count to get. | | `sortOrder` | `Asc \| Desc` | No | In which order the data is sorted. ['Asc' = 1, 'Desc' = 2] | ### Roblox.LocalizationTables.Api.Entry | Property | Type | Required | Description | |----------|------|----------|-------------| | `identifier` | `Roblox.LocalizationTables.Api.EntryIdentifier` | No | | | `metadata` | `Roblox.LocalizationTables.Api.EntryMetadata` | No | | | `translations` | `Roblox.LocalizationTables.Api.Translation[]` | No | | | `createdTime` | `string` | No | | ### Roblox.LocalizationTables.Api.EntryIdentifier | Property | Type | Required | Description | |----------|------|----------|-------------| | `key` | `string` | No | | | `context` | `string` | No | | | `source` | `string` | No | | | `entryFormat` | `Invalid \| Legacy \| Icu` | No | | ### Roblox.LocalizationTables.Api.EntryIdentifierWithTranslation | Property | Type | Required | Description | |----------|------|----------|-------------| | `translation` | `Roblox.LocalizationTables.Api.Translation` | No | | | `key` | `string` | No | | | `context` | `string` | No | | | `source` | `string` | No | | | `entryFormat` | `Invalid \| Legacy \| Icu` | No | | ### Roblox.LocalizationTables.Api.EntryMetadata | Property | Type | Required | Description | |----------|------|----------|-------------| | `example` | `string` | No | | | `gameLocations` | `Roblox.InGameContentTables.Client.GameLocation[]` | No | | | `entryFormat` | `Invalid \| Legacy \| Icu` | No | | ### Roblox.LocalizationTables.Api.EntryOperationLimits | Property | Type | Required | Description | |----------|------|----------|-------------| | `maxContextLength` | `integer` | No | Maximum character limit for entry context | | `maxKeyLength` | `integer` | No | Maximum character limit for entry key | | `maxSourceLength` | `integer` | No | Maximum character limit for entry source text | | `maxExampleLength` | `integer` | No | Maximum character limit for entry example | | `maxGameLocationPathLength` | `integer` | No | Maximum character limit for game location path | ### Roblox.LocalizationTables.Api.EntryTranslationFeedback | Property | Type | Required | Description | |----------|------|----------|-------------| | `identifier` | `Roblox.LocalizationTables.Api.EntryIdentifierWithTranslation` | No | | | `feedbackCount` | `integer` | No | The number of unique reporters that send feedback to the given entry identifier. | | `playerSuggestionText` | `string[]` | No | A set of player translation suggestion in text for the given entry identifier. | | `reasons` | `None \| Untranslated \| Inaccurate \| SpellingOrGrammar \| Inappropriate[]` | No | A set of translation feedback reasons for the given entry identifier. | | `robloxSuggestionText` | `string` | No | Roblox translation suggestion in text for the given entry identifier. | ### Roblox.LocalizationTables.Api.EntryTranslationHistoryPaged | Property | Type | Required | Description | |----------|------|----------|-------------| | `identifier` | `Roblox.LocalizationTables.Api.EntryIdentifier` | No | | | `history` | `Roblox.LocalizationTables.Api.TranslationHistory[]` | No | A batch of TranslationHistory for the given entry identifier. | | `nextCursor` | `string` | No | The cursor to send up on the next request if more history data is required. | ### Roblox.LocalizationTables.Api.Error | Property | Type | Required | Description | |----------|------|----------|-------------| | `errorCode` | `integer` | No | | | `errorMessage` | `string` | No | | ### Roblox.LocalizationTables.Api.FailedEntry | Property | Type | Required | Description | |----------|------|----------|-------------| | `error` | `Roblox.LocalizationTables.Api.Error` | No | | | `identifier` | `Roblox.LocalizationTables.Api.EntryIdentifier` | No | | | `metadata` | `Roblox.LocalizationTables.Api.EntryMetadata` | No | | | `translations` | `Roblox.LocalizationTables.Api.Translation[]` | No | | | `createdTime` | `string` | No | | ### Roblox.LocalizationTables.Api.FailedEntryTranslationHistoryPaged | Property | Type | Required | Description | |----------|------|----------|-------------| | `identifier` | `Roblox.LocalizationTables.Api.EntryIdentifier` | No | | | `count` | `integer` | No | | | `error` | `Roblox.LocalizationTables.Api.Error` | No | | ### Roblox.LocalizationTables.Api.GameAutolocalizationInformationResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `isAutolocalizationEnabled` | `boolean` | No | | | `isAutomaticEntriesSettingEnabled` | `boolean` | No | | | `isAutomaticEntriesDeletionEnabled` | `boolean` | No | | | `shouldUseLocalizationTable` | `boolean` | No | | | `autoLocalizationTableId` | `string` | No | | | `sourceLanguage` | `string` | No | | | `assetId` | `integer` | No | | ### Roblox.LocalizationTables.Api.GetLimitsResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `entryOperationLimits` | `Roblox.LocalizationTables.Api.EntryOperationLimits` | No | | | `tableOperationLimits` | `Roblox.LocalizationTables.Api.TableOperationLimits` | No | | ### Roblox.LocalizationTables.Api.GetTableEntriesPagedResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `previousPageCursor` | `string` | No | | | `nextPageCursor` | `string` | No | | | `data` | `Roblox.LocalizationTables.Api.Entry[]` | No | | ### Roblox.LocalizationTables.Api.GetTableEntriesTranslationFeedbackRequest A request model for GetTableEntriesTranslationFeedback. | Property | Type | Required | Description | |----------|------|----------|-------------| | `sourceLocale` | `string` | No | locale code of source language, we only accept language code at the moment. | | `entries` | `Roblox.LocalizationTables.Api.EntryIdentifierWithTranslation[]` | No | entry identifier | ### Roblox.LocalizationTables.Api.GetTableEntriesTranslationFeedbackResponse A response model for GetTableEntriesTranslationFeedback. | Property | Type | Required | Description | |----------|------|----------|-------------| | `tableId` | `string` | No | The entries' tableId. | | `entries` | `Roblox.LocalizationTables.Api.EntryTranslationFeedback[]` | No | The entries with their identifier, translation feedback details. | ### Roblox.LocalizationTables.Api.GetTableEntriesTranslationHistoryRequest | Property | Type | Required | Description | |----------|------|----------|-------------| | `locale` | `string` | No | | | `entries` | `Roblox.LocalizationTables.Api.CursorEntryIdentifier[]` | No | | ### Roblox.LocalizationTables.Api.GetTableEntriesTranslationHistoryResponse A response model for GetTableEntriesTranslationHistory. | Property | Type | Required | Description | |----------|------|----------|-------------| | `tableId` | `string` | No | The entries' tableId. | | `locale` | `string` | No | The locale of the translations. | | `entries` | `Roblox.LocalizationTables.Api.EntryTranslationHistoryPaged[]` | No | The entries with their identifier, translation history, and next cursor. | | `failedEntries` | `Roblox.LocalizationTables.Api.FailedEntryTranslationHistoryPaged[]` | No | The failed entries. | ### Roblox.LocalizationTables.Api.GetTableEntryCountResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `string` | No | | | `entryCount` | `integer` | No | | ### Roblox.LocalizationTables.Api.GetTableResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `string` | No | | | `name` | `string` | No | | | `ownerType` | `User \| Group` | No | Enum for valid OwnerTypes. ['User' = 0, 'Group' = 1] | | `ownerId` | `integer` | No | | | `assetId` | `integer` | No | | ### Roblox.LocalizationTables.Api.IngestAutoScrapedContentForGameRequest An ingest content request to IngestAutoScrapedContentForGame. | Property | Type | Required | Description | |----------|------|----------|-------------| | `entries` | `Roblox.LocalizationTables.Api.AutoScrapeEntry[]` | No | The entries of an ingest content request. | | `metadata` | `Roblox.LocalizationTables.Api.IngestContentMetadata` | No | | ### Roblox.LocalizationTables.Api.IngestContentMetadata The metadata of an ingest content request. | Property | Type | Required | Description | |----------|------|----------|-------------| | `placeInformation` | `Roblox.LocalizationTables.Api.IngestContentMetadataPlaceInformation` | No | | ### Roblox.LocalizationTables.Api.IngestContentMetadataPlaceInformation The place information metadata of an ingest content request. | Property | Type | Required | Description | |----------|------|----------|-------------| | `placeId` | `integer` | No | The place id of an ingest content request. | | `placeVersionNumber` | `integer` | No | The place version number of an ingest content request. | ### Roblox.LocalizationTables.Api.Language | Property | Type | Required | Description | |----------|------|----------|-------------| | `name` | `string` | No | | | `nativeName` | `string` | No | | | `languageCode` | `string` | No | | ### Roblox.LocalizationTables.Api.LocalizationTablesMetadataResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `isBulkUploadFeatureEnabled` | `boolean` | No | Is bulk upload feature enabled | | `isCsvDownloadEnabled` | `boolean` | No | Is CSV download feature enabled | | `isAccessToTranslationMetaDataEnabled` | `boolean` | No | Is access to translation metadata feature enabled | | `isTranslationManagementRedirectionEnabled` | `boolean` | No | Is access to translation metadata feature enabled | | `isUntranslatedFilterEnabled` | `boolean` | No | Is untranslated filter on UI is enabled | | `isAutomaticTranslationFilterEnabled` | `boolean` | No | Is filter for automatic translations on UI is enabled | ### Roblox.LocalizationTables.Api.MatchedEntry | Property | Type | Required | Description | |----------|------|----------|-------------| | `source` | `string` | No | | | `matchedParamIndex` | `integer` | No | | ### Roblox.LocalizationTables.Api.ModifiedEntry | Property | Type | Required | Description | |----------|------|----------|-------------| | `identifier` | `Roblox.LocalizationTables.Api.EntryIdentifier` | No | | | `translations` | `Roblox.LocalizationTables.Api.Translation[]` | No | | ### Roblox.LocalizationTables.Api.PatchEntry | Property | Type | Required | Description | |----------|------|----------|-------------| | `identifier` | `Roblox.LocalizationTables.Api.EntryIdentifier` | No | | | `metadata` | `Roblox.LocalizationTables.Api.EntryMetadata` | No | | | `translations` | `Roblox.LocalizationTables.Api.PatchTranslation[]` | No | | | `delete` | `boolean` | No | | ### Roblox.LocalizationTables.Api.PatchTranslation | Property | Type | Required | Description | |----------|------|----------|-------------| | `locale` | `string` | No | | | `translationText` | `string` | No | | | `delete` | `boolean` | No | | | `changeAgent` | `Roblox.Localizationtables.Localizationtables.V1.ChangeAgent` | No | | | `updatedTime` | `string` | No | | ### Roblox.LocalizationTables.Api.RaiseEventForAutoScrapedEntriesCleanupRequest | Property | Type | Required | Description | |----------|------|----------|-------------| | `maxAgeForFlush` | `string` | No | The time range to remove entries from. Following ISO 8601 Durations format | ### Roblox.LocalizationTables.Api.SetAutolocalizationSettingsForGameRequest | Property | Type | Required | Description | |----------|------|----------|-------------| | `isAutolocalizationEnabled` | `boolean` | No | | | `isAutomaticEntriesSettingEnabled` | `boolean` | No | | | `isAutomaticEntriesDeletionsEnabled` | `boolean` | No | | | `shouldUseLocalizationTable` | `boolean` | No | | ### Roblox.LocalizationTables.Api.SetAutolocalizationTableForGameRequest | Property | Type | Required | Description | |----------|------|----------|-------------| | `tableId` | `string` | No | | ### Roblox.LocalizationTables.Api.TableOperationLimits | Property | Type | Required | Description | |----------|------|----------|-------------| | `maxEntriesPerUpdate` | `integer` | No | Maximum number of entries for a patch request | ### Roblox.LocalizationTables.Api.Translation | Property | Type | Required | Description | |----------|------|----------|-------------| | `locale` | `string` | No | | | `translationText` | `string` | No | | | `translator` | `Roblox.LocalizationTables.Api.Translator` | No | | | `updatedTime` | `string` | No | | | `feedbackCount` | `integer` | No | | ### Roblox.LocalizationTables.Api.TranslationHistory A model containing a translation, translator, and translation time. | Property | Type | Required | Description | |----------|------|----------|-------------| | `translationText` | `string` | No | The translation provided by the translator. | | `translator` | `Roblox.LocalizationTables.Api.Translator` | No | | | `created` | `string` | No | The time the translation was provided. | ### Roblox.LocalizationTables.Api.Translator | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | | | `agentType` | `User \| Automation` | No | ['User' = 0, 'Automation' = 1] | ### Roblox.LocalizationTables.Api.UpdateTableContentsRequest | Property | Type | Required | Description | |----------|------|----------|-------------| | `name` | `string` | No | | | `entries` | `Roblox.LocalizationTables.Api.PatchEntry[]` | No | | ### Roblox.LocalizationTables.Api.UpdateTableContentsResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `failedEntriesAndTranslations` | `Roblox.LocalizationTables.Api.FailedEntry[]` | No | | | `modifiedEntriesAndTranslations` | `Roblox.LocalizationTables.Api.ModifiedEntry[]` | No | | ### Roblox.Localizationtables.Localizationtables.V1.ChangeAgent | Property | Type | Required | Description | |----------|------|----------|-------------| | `ChangeAgentType` | `Invalid \| User \| Automation \| Default` | No | ['Invalid' = 0, 'User' = 1, 'Automation' = 2, 'Default' = 3] | | `Id` | `string` | No | | | `OptionalIdCase` | `None \| Id` | No | ['None' = 0, 'Id' = 2] | ### Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.LocalizationTables.Api.Language] | Property | Type | Required | Description | |----------|------|----------|-------------| | `data` | `Roblox.LocalizationTables.Api.Language[]` | No | | --- name: "Matchmaking Api v1" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://apis.roblox.com" version: "v1" endpoints: 25 auth: [cookie] --- # Matchmaking Api v1 **API Version:** v1 **Base URL:** `https://apis.roblox.com/matchmaking-api` ## Endpoints ### GET `/matchmaking-api/v1/client-status` Get the client-status **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json-patch+json` — Type: `ClientStatusGetRequest` See [ClientStatusGetRequest](#clientstatusgetrequest) in Models. **Request example:** ```json { "browserTrackerId": 0 } ``` **Responses:** - `200`: Success **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://apis.roblox.com/matchmaking-api/v1/client-status" \ -H "Content-Type: application/json" \ -d '{ "browserTrackerId": 0 }' ``` ### POST `/matchmaking-api/v1/client-status` Set the client-status **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json-patch+json` — Type: `ClientStatusSetRequest` See [ClientStatusSetRequest](#clientstatussetrequest) in Models. **Request example:** ```json { "status": "string", "browserTrackerId": 0 } ``` **Responses:** - `200`: Success **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://apis.roblox.com/matchmaking-api/v1/client-status" \ -H "Content-Type: application/json" \ -d '{ "status": "string", "browserTrackerId": 0 }' ``` ### POST `/matchmaking-api/v1/game-instances/shutdown` Shutdown game instances. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json-patch+json` — Type: `ShutdownGameInstancesRequest` See [ShutdownGameInstancesRequest](#shutdowngameinstancesrequest) in Models. **Request example:** ```json { "placeId": 0, "privateServerId": 0, "gameId": "string" } ``` **Responses:** - `200`: Success → `ShutdownGameInstancesResponse` **Response fields** (`ShutdownGameInstancesResponse`) See [ShutdownGameInstancesResponse](#shutdowngameinstancesresponse) in Models. **Response example:** ```json { "placeId": 0, "privateServerId": 0, "gameId": "string" } ``` **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://apis.roblox.com/matchmaking-api/v1/game-instances/shutdown" \ -H "Content-Type: application/json" \ -d '{ "placeId": 0, "privateServerId": 0, "gameId": "string" }' ``` ### POST `/matchmaking-api/v1/game-instances/shutdown-all` Shutdown all game instances. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `multipart/form-data` — Type: `object` | Property | Type | Required | Description | |----------|------|----------|-------------| | `PlaceId` | `integer` | No | The place ID to shut down. | | `ReplaceInstances` | `boolean` | No | Whether to replace instances or not. | **Request example:** ```json { "PlaceId": 0, "ReplaceInstances": false } ``` **Responses:** - `200`: Success → `ShutdownAllGameInstancesResponse` **Response fields** (`ShutdownAllGameInstancesResponse`) See [ShutdownAllGameInstancesResponse](#shutdownallgameinstancesresponse) in Models. **Response example:** ```json { "placeId": 0, "replaceInstances": false } ``` **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://apis.roblox.com/matchmaking-api/v1/game-instances/shutdown-all" \ -F "PlaceId=0" \ -F "ReplaceInstances=false" ``` ### POST `/matchmaking-api/v1/matchmaking/player-attribute` Create a PlayerAttributeDefinition. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json-patch+json` — Type: `CreateMatchmakingPlayerAttributeDefinitionRequest` See [CreateMatchmakingPlayerAttributeDefinitionRequest](#creatematchmakingplayerattributedefinitionrequest) in Models. **Request example:** ```json { "universeId": 0, "name": "string", "dataType": "Invalid", "defaultValue": { "boolValue": false, "numericValue": 0, "stringValue": "string", "type": "Invalid" }, "attributeValueLocation": { "locationCase": "Invalid", "dataStoreLocation": { "dataStoreName": "...", "scope": "...", "keyTemplate": "...", "valuePath": "..." } } } ``` **Responses:** - `200`: Success → `CreateMatchmakingPlayerAttributeDefinitionResponse` **Response fields** (`CreateMatchmakingPlayerAttributeDefinitionResponse`) See [CreateMatchmakingPlayerAttributeDefinitionResponse](#creatematchmakingplayerattributedefinitionresponse) in Models. **Response example:** ```json { "playerAttributeDefinition": { "id": "string", "universeId": 0, "name": "string", "dataType": "Invalid", "defaultValue": { "boolValue": "...", "numericValue": "...", "stringValue": "...", "type": "..." }, "attributeValueLocation": { "locationCase": "...", "dataStoreLocation": "..." } } } ``` **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://apis.roblox.com/matchmaking-api/v1/matchmaking/player-attribute" \ -H "Content-Type: application/json" \ -d '{"universeId":0,"name":"string","dataType":"Invalid","defaultValue":{"boolValue":false,"numericValue":0,"stringValue":"string","type":"Invalid"},"attributeValueLocation":{"locationCase":"Invalid","dataStoreLocation":{"dataStoreName":"...","scope":"...","keyTemplate":"...","valuePath":"..."}}}' ``` ### PATCH `/matchmaking-api/v1/matchmaking/player-attribute/{attributeId}` Update the PlayerAttributeDefinition specified by attributeId. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `attributeId` | path | `string` | Yes | | **Request Body:** `application/json-patch+json` — Type: `UpdateMatchmakingPlayerAttributeDefinitionRequest` See [UpdateMatchmakingPlayerAttributeDefinitionRequest](#updatematchmakingplayerattributedefinitionrequest) in Models. **Request example:** ```json { "attributeId": "string", "defaultValue": { "boolValue": false, "numericValue": 0, "stringValue": "string", "type": "Invalid" }, "attributeValueLocation": { "locationCase": "Invalid", "dataStoreLocation": { "dataStoreName": "...", "scope": "...", "keyTemplate": "...", "valuePath": "..." } } } ``` **Responses:** - `200`: Success → `UpdateMatchmakingPlayerAttributeDefinitionResponse` **Response fields** (`UpdateMatchmakingPlayerAttributeDefinitionResponse`) See [UpdateMatchmakingPlayerAttributeDefinitionResponse](#updatematchmakingplayerattributedefinitionresponse) in Models. **Response example:** ```json { "playerAttributeDefinition": { "id": "string", "universeId": 0, "name": "string", "dataType": "Invalid", "defaultValue": { "boolValue": "...", "numericValue": "...", "stringValue": "...", "type": "..." }, "attributeValueLocation": { "locationCase": "...", "dataStoreLocation": "..." } } } ``` **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://apis.roblox.com/matchmaking-api/v1/matchmaking/player-attribute/{ATTRIBUTEID}" \ -H "Content-Type: application/json" \ -d '{"attributeId":"string","defaultValue":{"boolValue":false,"numericValue":0,"stringValue":"string","type":"Invalid"},"attributeValueLocation":{"locationCase":"Invalid","dataStoreLocation":{"dataStoreName":"...","scope":"...","keyTemplate":"...","valuePath":"..."}}}' ``` ### DELETE `/matchmaking-api/v1/matchmaking/player-attribute/{attributeId}` Delete the PlayerAttributeDefinition specified by attributeId. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `attributeId` | path | `string` | Yes | | **Responses:** - `200`: Success → `DeleteMatchmakingPlayerAttributeDefinitionResponse` **Response fields** (`DeleteMatchmakingPlayerAttributeDefinitionResponse`) See [DeleteMatchmakingPlayerAttributeDefinitionResponse](#deletematchmakingplayerattributedefinitionresponse) in Models. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://apis.roblox.com/matchmaking-api/v1/matchmaking/player-attribute/{ATTRIBUTEID}" ``` ### GET `/matchmaking-api/v1/matchmaking/player-attributes/{universeId}` List all PlayerAttributeDefinitions of a universe. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | | **Responses:** - `200`: Success → `ListMatchmakingPlayerAttributeDefinitionsResponse` **Response fields** (`ListMatchmakingPlayerAttributeDefinitionsResponse`) See [ListMatchmakingPlayerAttributeDefinitionsResponse](#listmatchmakingplayerattributedefinitionsresponse) in Models. **Response example:** ```json { "playerAttributeSchema": [ { "id": "...", "universeId": "...", "name": "...", "dataType": "...", "defaultValue": "...", "attributeValueLocation": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://apis.roblox.com/matchmaking-api/v1/matchmaking/player-attributes/{UNIVERSEID}" ``` ### POST `/matchmaking-api/v1/matchmaking/server-attribute` Create a ServerAttributeDefinition. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json-patch+json` — Type: `CreateMatchmakingServerAttributeDefinitionRequest` See [CreateMatchmakingServerAttributeDefinitionRequest](#creatematchmakingserverattributedefinitionrequest) in Models. **Request example:** ```json { "universeId": 0, "name": "string", "dataType": "Invalid", "defaultValue": { "sourceCase": "Invalid", "constant": { "boolValue": "...", "numericValue": "...", "stringValue": "...", "type": "..." }, "playerAttributeReference": { "id": "..." } } } ``` **Responses:** - `200`: Success → `CreateMatchmakingServerAttributeDefinitionResponse` **Response fields** (`CreateMatchmakingServerAttributeDefinitionResponse`) See [CreateMatchmakingServerAttributeDefinitionResponse](#creatematchmakingserverattributedefinitionresponse) in Models. **Response example:** ```json { "playerAttributeDefinition": { "id": "string", "universeId": 0, "name": "string", "dataType": "Invalid", "defaultValue": { "sourceCase": "...", "constant": "...", "playerAttributeReference": "..." }, "createdTime": "2024-01-01T00:00:00Z" } } ``` **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://apis.roblox.com/matchmaking-api/v1/matchmaking/server-attribute" \ -H "Content-Type: application/json" \ -d '{"universeId":0,"name":"string","dataType":"Invalid","defaultValue":{"sourceCase":"Invalid","constant":{"boolValue":"...","numericValue":"...","stringValue":"...","type":"..."},"playerAttributeReference":{"id":"..."}}}' ``` ### PATCH `/matchmaking-api/v1/matchmaking/server-attribute/{attributeId}` Update the ServerAttributeDefinition specified by attributeId. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `attributeId` | path | `string` | Yes | | **Request Body:** `application/json-patch+json` — Type: `UpdateMatchmakingServerAttributeDefinitionRequest` See [UpdateMatchmakingServerAttributeDefinitionRequest](#updatematchmakingserverattributedefinitionrequest) in Models. **Request example:** ```json { "attributeId": "string", "defaultValue": { "sourceCase": "Invalid", "constant": { "boolValue": "...", "numericValue": "...", "stringValue": "...", "type": "..." }, "playerAttributeReference": { "id": "..." } } } ``` **Responses:** - `200`: Success → `UpdateMatchmakingServerAttributeDefinitionResponse` **Response fields** (`UpdateMatchmakingServerAttributeDefinitionResponse`) See [UpdateMatchmakingServerAttributeDefinitionResponse](#updatematchmakingserverattributedefinitionresponse) in Models. **Response example:** ```json { "playerAttributeDefinition": { "id": "string", "universeId": 0, "name": "string", "dataType": "Invalid", "defaultValue": { "sourceCase": "...", "constant": "...", "playerAttributeReference": "..." }, "createdTime": "2024-01-01T00:00:00Z" } } ``` **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://apis.roblox.com/matchmaking-api/v1/matchmaking/server-attribute/{ATTRIBUTEID}" \ -H "Content-Type: application/json" \ -d '{ "attributeId": "string", "defaultValue": { "sourceCase": "Invalid", "constant": { "boolValue": "...", "numericValue": "...", "stringValue": "...", "type": "..." }, "playerAttributeReference": { "id": "..." } } }' ``` ### DELETE `/matchmaking-api/v1/matchmaking/server-attribute/{attributeId}` Delete the ServerAttributeDefinition specified by attributeId. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `attributeId` | path | `string` | Yes | | **Responses:** - `200`: Success → `DeleteMatchmakingServerAttributeDefinitionResponse` **Response fields** (`DeleteMatchmakingServerAttributeDefinitionResponse`) See [DeleteMatchmakingServerAttributeDefinitionResponse](#deletematchmakingserverattributedefinitionresponse) in Models. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://apis.roblox.com/matchmaking-api/v1/matchmaking/server-attribute/{ATTRIBUTEID}" ``` ### GET `/matchmaking-api/v1/matchmaking/server-attributes/{universeId}` List all ServerAttributeDefinitions of a universe. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | | **Responses:** - `200`: Success → `ListMatchmakingServerAttributeDefinitionsResponse` **Response fields** (`ListMatchmakingServerAttributeDefinitionsResponse`) See [ListMatchmakingServerAttributeDefinitionsResponse](#listmatchmakingserverattributedefinitionsresponse) in Models. **Response example:** ```json { "serverAttributeSchema": [ { "id": "...", "universeId": "...", "name": "...", "dataType": "...", "defaultValue": "...", "createdTime": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://apis.roblox.com/matchmaking-api/v1/matchmaking/server-attributes/{UNIVERSEID}" ``` ### GET `/matchmaking-api/v1/matchmaking/universe/{universeId}/feature-flags` Gets feature flags for a customized matchmaking for a given universe. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | | **Responses:** - `200`: Success → `GetMatchmakingCustomizationFeatureFlagsResponse` **Response fields** (`GetMatchmakingCustomizationFeatureFlagsResponse`) See [GetMatchmakingCustomizationFeatureFlagsResponse](#getmatchmakingcustomizationfeatureflagsresponse) in Models. **Response example:** ```json { "featureFlags": { "isMatchmakingCustomizationAllowed": false } } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://apis.roblox.com/matchmaking-api/v1/matchmaking/universe/{UNIVERSEID}/feature-flags" ``` ### POST `/matchmaking-api/v1/matchmaking/scoring-configuration` Creates a matchmaking scoring configuration. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json-patch+json` — Type: `CreateMatchmakingScoringConfigurationRequest` See [CreateMatchmakingScoringConfigurationRequest](#creatematchmakingscoringconfigurationrequest) in Models. **Request example:** ```json { "universeId": 0, "name": "string", "matchmakingSignalWeights": { "Invalid": 0, "Occupancy": 0, "Age": 0, "Language": 0, "Latency": 0, "PreferredPlayers": 0 }, "customSignalWeights": "...", "template": "NotApplicable" } ``` **Responses:** - `200`: Success → `CreateMatchmakingScoringConfigurationResponse` **Response fields** (`CreateMatchmakingScoringConfigurationResponse`) See [CreateMatchmakingScoringConfigurationResponse](#creatematchmakingscoringconfigurationresponse) in Models. **Response example:** ```json { "scoringConfiguration": { "id": "string", "name": "string", "signalWeights": { "Invalid": "...", "Occupancy": "...", "Age": "...", "Language": "...", "Latency": "...", "PreferredPlayers": "..." }, "customSignals": "...", "createdTime": "2024-01-01T00:00:00Z", "updatedTime": "2024-01-01T00:00:00Z" } } ``` **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://apis.roblox.com/matchmaking-api/v1/matchmaking/scoring-configuration" \ -H "Content-Type: application/json" \ -d '{ "universeId": 0, "name": "string", "matchmakingSignalWeights": { "Invalid": 0, "Occupancy": 0, "Age": 0, "Language": 0, "Latency": 0, "PreferredPlayers": 0 }, "customSignalWeights": "...", "template": "NotApplicable" }' ``` ### GET `/matchmaking-api/v1/matchmaking/scoring-configuration/{scoringConfigurationId}` Updates a matchmaking scoring configuration. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `scoringConfigurationId` | path | `string` | Yes | | **Responses:** - `200`: Success → `GetMatchmakingScoringConfigurationResponse` **Response fields** (`GetMatchmakingScoringConfigurationResponse`) See [GetMatchmakingScoringConfigurationResponse](#getmatchmakingscoringconfigurationresponse) in Models. **Response example:** ```json { "scoringConfiguration": { "id": "string", "name": "string", "signalWeights": { "Invalid": "...", "Occupancy": "...", "Age": "...", "Language": "...", "Latency": "...", "PreferredPlayers": "..." }, "customSignals": "...", "createdTime": "2024-01-01T00:00:00Z", "updatedTime": "2024-01-01T00:00:00Z" } } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://apis.roblox.com/matchmaking-api/v1/matchmaking/scoring-configuration/{SCORINGCONFIGURATIONID}" ``` ### PATCH `/matchmaking-api/v1/matchmaking/scoring-configuration/{scoringConfigurationId}` Updates a matchmaking scoring configuration. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `scoringConfigurationId` | path | `string` | Yes | | **Request Body:** `application/json-patch+json` — Type: `UpdateMatchmakingScoringConfigurationRequest` See [UpdateMatchmakingScoringConfigurationRequest](#updatematchmakingscoringconfigurationrequest) in Models. **Request example:** ```json { "scoringConfigurationId": "string", "name": "string", "matchmakingSignalWeights": { "Invalid": 0, "Occupancy": 0, "Age": 0, "Language": 0, "Latency": 0, "PreferredPlayers": 0 }, "customSignalWeights": "..." } ``` **Responses:** - `200`: Success → `UpdateMatchmakingScoringConfigurationResponse` **Response fields** (`UpdateMatchmakingScoringConfigurationResponse`) See [UpdateMatchmakingScoringConfigurationResponse](#updatematchmakingscoringconfigurationresponse) in Models. **Response example:** ```json { "scoringConfiguration": { "id": "string", "name": "string", "signalWeights": { "Invalid": "...", "Occupancy": "...", "Age": "...", "Language": "...", "Latency": "...", "PreferredPlayers": "..." }, "customSignals": "...", "createdTime": "2024-01-01T00:00:00Z", "updatedTime": "2024-01-01T00:00:00Z" } } ``` **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://apis.roblox.com/matchmaking-api/v1/matchmaking/scoring-configuration/{SCORINGCONFIGURATIONID}" \ -H "Content-Type: application/json" \ -d '{ "scoringConfigurationId": "string", "name": "string", "matchmakingSignalWeights": { "Invalid": 0, "Occupancy": 0, "Age": 0, "Language": 0, "Latency": 0, "PreferredPlayers": 0 }, "customSignalWeights": "..." }' ``` ### DELETE `/matchmaking-api/v1/matchmaking/scoring-configuration/{scoringConfigurationId}` Deletes a matchmaking scoring configuration. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `scoringConfigurationId` | path | `string` | Yes | | **Responses:** - `200`: Success → `DeleteMatchmakingScoringConfigurationResponse` **Response fields** (`DeleteMatchmakingScoringConfigurationResponse`) See [DeleteMatchmakingScoringConfigurationResponse](#deletematchmakingscoringconfigurationresponse) in Models. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://apis.roblox.com/matchmaking-api/v1/matchmaking/scoring-configuration/{SCORINGCONFIGURATIONID}" ``` ### POST `/matchmaking-api/v1/matchmaking/scoring-configuration/{scoringConfigurationId}/signals` Creates a matchmaking scoring configuration signal. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `scoringConfigurationId` | path | `string` | Yes | | **Request Body:** `application/json-patch+json` — Type: `CreateCustomMatchmakingSignalRequest` See [CreateCustomMatchmakingSignalRequest](#createcustommatchmakingsignalrequest) in Models. **Request example:** ```json { "scoringConfigurationId": "string", "signalConfiguration": { "name": "string", "weight": 0, "customSignalType": "Invalid", "playerCategoricalSignalConfiguration": { "playerAttribute": "...", "curveType": "..." }, "serverCategoricalSignalConfiguration": { "serverAttribute": "...", "comparisonType": "...", "playerAttribute": "...", "constantValue": "..." }, "serverNumericalSignalConfiguration": { "serverAttribute": "...", "comparisonType": "...", "maxRelevantDifference": "...", "constantValue": "...", "playerAttribute": "..." } } } ``` **Responses:** - `200`: Success → `CreateCustomMatchmakingSignalResponse` **Response fields** (`CreateCustomMatchmakingSignalResponse`) See [CreateCustomMatchmakingSignalResponse](#createcustommatchmakingsignalresponse) in Models. **Response example:** ```json { "scoringConfiguration": { "id": "string", "name": "string", "signalWeights": { "Invalid": "...", "Occupancy": "...", "Age": "...", "Language": "...", "Latency": "...", "PreferredPlayers": "..." }, "customSignals": "...", "createdTime": "2024-01-01T00:00:00Z", "updatedTime": "2024-01-01T00:00:00Z" } } ``` **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://apis.roblox.com/matchmaking-api/v1/matchmaking/scoring-configuration/{SCORINGCONFIGURATIONID}/signals" \ -H "Content-Type: application/json" \ -d '{"scoringConfigurationId":"string","signalConfiguration":{"name":"string","weight":0,"customSignalType":"Invalid","playerCategoricalSignalConfiguration":{"playerAttribute":"...","curveType":"..."},"serverCategoricalSignalConfiguration":{"serverAttribute":"...","comparisonType":"...","playerAttribute":"...","constantValue":"..."},"serverNumericalSignalConfiguration":{"serverAttribute":"...","comparisonType":"...","maxRelevantDifference":"...","constantValue":"...","playerAttribute":"..."}}}' ``` ### PATCH `/matchmaking-api/v1/matchmaking/scoring-configuration/{scoringConfigurationId}/signals/{signalName}` Updates a matchmaking scoring configuration signal. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `scoringConfigurationId` | path | `string` | Yes | | | `signalName` | path | `string` | Yes | | **Request Body:** `application/json-patch+json` — Type: `UpdateCustomMatchmakingSignalRequest` See [UpdateCustomMatchmakingSignalRequest](#updatecustommatchmakingsignalrequest) in Models. **Request example:** ```json { "scoringConfigurationId": "string", "signalConfiguration": { "name": "string", "weight": 0, "customSignalType": "Invalid", "playerCategoricalSignalConfiguration": { "playerAttribute": "...", "curveType": "..." }, "serverCategoricalSignalConfiguration": { "serverAttribute": "...", "comparisonType": "...", "playerAttribute": "...", "constantValue": "..." }, "serverNumericalSignalConfiguration": { "serverAttribute": "...", "comparisonType": "...", "maxRelevantDifference": "...", "constantValue": "...", "playerAttribute": "..." } } } ``` **Responses:** - `200`: Success → `UpdateCustomMatchmakingSignalResponse` **Response fields** (`UpdateCustomMatchmakingSignalResponse`) See [UpdateCustomMatchmakingSignalResponse](#updatecustommatchmakingsignalresponse) in Models. **Response example:** ```json { "scoringConfiguration": { "id": "string", "name": "string", "signalWeights": { "Invalid": "...", "Occupancy": "...", "Age": "...", "Language": "...", "Latency": "...", "PreferredPlayers": "..." }, "customSignals": "...", "createdTime": "2024-01-01T00:00:00Z", "updatedTime": "2024-01-01T00:00:00Z" } } ``` **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://apis.roblox.com/matchmaking-api/v1/matchmaking/scoring-configuration/{SCORINGCONFIGURATIONID}/signals/{SIGNALNAME}" \ -H "Content-Type: application/json" \ -d '{"scoringConfigurationId":"string","signalConfiguration":{"name":"string","weight":0,"customSignalType":"Invalid","playerCategoricalSignalConfiguration":{"playerAttribute":"...","curveType":"..."},"serverCategoricalSignalConfiguration":{"serverAttribute":"...","comparisonType":"...","playerAttribute":"...","constantValue":"..."},"serverNumericalSignalConfiguration":{"serverAttribute":"...","comparisonType":"...","maxRelevantDifference":"...","constantValue":"...","playerAttribute":"..."}}}' ``` ### DELETE `/matchmaking-api/v1/matchmaking/scoring-configuration/{scoringConfigurationId}/signals/{signalName}` Deletes a matchmaking scoring configuration custom signal. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `scoringConfigurationId` | path | `string` | Yes | | | `signalName` | path | `string` | Yes | | **Responses:** - `200`: Success → `DeleteCustomMatchmakingSignalResponse` **Response fields** (`DeleteCustomMatchmakingSignalResponse`) See [DeleteCustomMatchmakingSignalResponse](#deletecustommatchmakingsignalresponse) in Models. **Response example:** ```json { "scoringConfiguration": { "id": "string", "name": "string", "signalWeights": { "Invalid": "...", "Occupancy": "...", "Age": "...", "Language": "...", "Latency": "...", "PreferredPlayers": "..." }, "customSignals": "...", "createdTime": "2024-01-01T00:00:00Z", "updatedTime": "2024-01-01T00:00:00Z" } } ``` **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://apis.roblox.com/matchmaking-api/v1/matchmaking/scoring-configuration/{SCORINGCONFIGURATIONID}/signals/{SIGNALNAME}" ``` ### POST `/matchmaking-api/v1/matchmaking/scoring-configuration/place` Sets a matchmaking scoring configuration for a place. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json-patch+json` — Type: `SetPlaceMatchmakingScoringConfigurationRequest` See [SetPlaceMatchmakingScoringConfigurationRequest](#setplacematchmakingscoringconfigurationrequest) in Models. **Request example:** ```json { "placeId": 0, "scoringConfigurationId": "string" } ``` **Responses:** - `200`: Success → `SetMatchmakingScoringConfigurationResponse` **Response fields** (`SetMatchmakingScoringConfigurationResponse`) See [SetMatchmakingScoringConfigurationResponse](#setmatchmakingscoringconfigurationresponse) in Models. **Response example:** ```json { "scoringConfiguration": { "id": "string", "name": "string", "signalWeights": { "Invalid": "...", "Occupancy": "...", "Age": "...", "Language": "...", "Latency": "...", "PreferredPlayers": "..." }, "customSignals": "...", "createdTime": "2024-01-01T00:00:00Z", "updatedTime": "2024-01-01T00:00:00Z" } } ``` **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://apis.roblox.com/matchmaking-api/v1/matchmaking/scoring-configuration/place" \ -H "Content-Type: application/json" \ -d '{ "placeId": 0, "scoringConfigurationId": "string" }' ``` ### DELETE `/matchmaking-api/v1/matchmaking/scoring-configuration/place/{placeId}` Removes the matchmaking scoring configuration for a place. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `placeId` | path | `integer` | Yes | | **Responses:** - `200`: Success → `RemovePlaceMatchmakingScoringConfigurationResponse` **Response fields** (`RemovePlaceMatchmakingScoringConfigurationResponse`) See [RemovePlaceMatchmakingScoringConfigurationResponse](#removeplacematchmakingscoringconfigurationresponse) in Models. **Example:** ```bash curl -X DELETE -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://apis.roblox.com/matchmaking-api/v1/matchmaking/scoring-configuration/place/{PLACEID}" ``` ### GET `/matchmaking-api/v1/matchmaking/scoring-configurations/{universeId}` List all matchmaking scoring configurations for a universe. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | | **Responses:** - `200`: Success → `ListMatchmakingScoringConfigurationsResponse` **Response fields** (`ListMatchmakingScoringConfigurationsResponse`) See [ListMatchmakingScoringConfigurationsResponse](#listmatchmakingscoringconfigurationsresponse) in Models. **Response example:** ```json { "scoringConfigurations": [ { "id": "...", "name": "...", "signalWeights": "...", "customSignals": "...", "createdTime": "...", "updatedTime": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://apis.roblox.com/matchmaking-api/v1/matchmaking/scoring-configurations/{UNIVERSEID}" ``` ### GET `/matchmaking-api/v1/matchmaking/scoring-configurations/{universeId}/places` List all places with a matchmaking scoring configuration for a universe. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer` | Yes | | **Responses:** - `200`: Success → `ListPlaceMatchmakingScoringConfigurationsResponse` **Response fields** (`ListPlaceMatchmakingScoringConfigurationsResponse`) See [ListPlaceMatchmakingScoringConfigurationsResponse](#listplacematchmakingscoringconfigurationsresponse) in Models. **Response example:** ```json { "placeScoringConfigurations": "..." } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://apis.roblox.com/matchmaking-api/v1/matchmaking/scoring-configurations/{UNIVERSEID}/places" ``` ### GET `/matchmaking-api/v1/matchmaking/scoring-configuration/default-weights` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: Success → `GetMatchmakingScoringDefaultWeightsResponse` **Response fields** (`GetMatchmakingScoringDefaultWeightsResponse`) See [GetMatchmakingScoringDefaultWeightsResponse](#getmatchmakingscoringdefaultweightsresponse) in Models. **Response example:** ```json { "weights": { "Invalid": 0, "Occupancy": 0, "Age": 0, "Language": 0, "Latency": 0, "PreferredPlayers": 0 } } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://apis.roblox.com/matchmaking-api/v1/matchmaking/scoring-configuration/default-weights" ``` --- name: "Notifications Api v2" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://notifications.roblox.com" version: "v2" endpoints: 15 auth: [cookie] --- # Notifications Api v2 **API Version:** v2 **Base URL:** `https://notifications.roblox.com` ## Endpoints ### GET `/v2/push-notifications/chrome-manifest` Get Chrome Manifest to link GCM project to Chrome Browser **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Api.Notifications.Models.ChromeManifestModel` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Api.Notifications.Models.ChromeManifestModel`) See [Roblox.Api.Notifications.Models.ChromeManifestModel](#roblox-api-notifications-models-chromemanifestmodel) in Models. **Response example:** ```json { "name": "string", "gcm_sender_id": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://notifications.roblox.com/v2/push-notifications/chrome-manifest" ``` ### GET `/v2/push-notifications/get-current-device-destination` Gets the current device destination **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Api.Notifications.Models.GetCurrentPushDestinationResponseModel` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Api.Notifications.Models.GetCurrentPushDestinationResponseModel`) See [Roblox.Api.Notifications.Models.GetCurrentPushDestinationResponseModel](#roblox-api-notifications-models-getcurrentpushdestinationresponsemodel) in Models. **Response example:** ```json { "destination": { "user": { "name": "...", "userId": "..." }, "name": "string", "notificationToken": "string", "supportsUpdateNotifications": false, "userPushNotificationDestinationId": 0, "application": "string" }, "statusMessage": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://notifications.roblox.com/v2/push-notifications/get-current-device-destination" ``` ### GET `/v2/push-notifications/get-destinations` Gets valid destinations associated with the signed user **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Api.Notifications.Models.GetPushDestinationsResponseModel` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Api.Notifications.Models.GetPushDestinationsResponseModel`) See [Roblox.Api.Notifications.Models.GetPushDestinationsResponseModel](#roblox-api-notifications-models-getpushdestinationsresponsemodel) in Models. **Response example:** ```json { "destinations": [ { "user": "...", "name": "...", "notificationToken": "...", "supportsUpdateNotifications": "...", "userPushNotificationDestinationId": "...", "application": "..." } ], "statusMessage": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://notifications.roblox.com/v2/push-notifications/get-destinations" ``` ### GET `/v2/push-notifications/metadata` Gets the corresponding metadata for the specified notification **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `notificationToken` | query | `string` | Yes | Token for the notification | | `notificationId` | query | `string (uuid)` | Yes | Id of the specified notification | **Responses:** - `200`: OK → `Roblox.Api.Notifications.Models.GetMetadataResponseModel` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Api.Notifications.Models.GetMetadataResponseModel`) See [Roblox.Api.Notifications.Models.GetMetadataResponseModel](#roblox-api-notifications-models-getmetadataresponsemodel) in Models. **Response example:** ```json { "metadata": { "notificationId": "string", "type": "string", "detail": "...", "fallbackDelivered": false }, "statusMessage": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://notifications.roblox.com/v2/push-notifications/metadata?notificationToken={VALUE}¬ificationId={VALUE}" ``` ### GET `/v2/stream-notifications/get-latest-game-updates` Get the latest non aggregated Game Updates sent to the logged in user **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeIds` | query | `array` | Yes | List of universe IDs | | `sinceDateTime` | query | `string (date-time)` | No | For retrieving only updates that created after a time point. | **Responses:** - `200`: OK → `Roblox.Api.Notifications.Models.GameUpdateNotificationModel[]` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Api.Notifications.Models.GameUpdateNotificationModel[]`) See [Roblox.Api.Notifications.Models.GameUpdateNotificationModel](#roblox-api-notifications-models-gameupdatenotificationmodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://notifications.roblox.com/v2/stream-notifications/get-latest-game-updates?universeIds={VALUE}" ``` ### GET `/v2/stream-notifications/get-recent` Gets the recent entries from the notification stream **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `startIndex` | query | `integer (int32)` | No | Index to start the entries from. (Optional : Defaults to 0 which means the most recent entry) | | `maxRows` | query | `integer (int32)` | No | Number of entries to be returned. (Optional : Defaults to 10 entries) | **Responses:** - `200`: OK → `Roblox.Api.Notifications.Models.NotificationStreamEntriesModel[]` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Api.Notifications.Models.NotificationStreamEntriesModel[]`) See [Roblox.Api.Notifications.Models.NotificationStreamEntriesModel](#roblox-api-notifications-models-notificationstreamentriesmodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://notifications.roblox.com/v2/stream-notifications/get-recent" ``` ### GET `/v2/stream-notifications/metadata` Get Notification Stream metadata. **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Api.Notifications.Models.ResponseModels.NotificationStreamMetadataResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Api.Notifications.Models.ResponseModels.NotificationStreamMetadataResponse`) See [Roblox.Api.Notifications.Models.ResponseModels.NotificationStreamMetadataResponse](#roblox-api-notifications-models-responsemodels-notificationstreammetadataresponse) in Models. **Response example:** ```json { "bannerDismissTimeSpan": 0, "signalRDisconnectionResponseInMilliseconds": 0, "canLaunchGameFromGameUpdate": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://notifications.roblox.com/v2/stream-notifications/metadata" ``` ### GET `/v2/stream-notifications/unread-count` Gets the count of unread Notification stream entries **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Api.Notifications.Models.UnreadStreamNotificationsModel` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Api.Notifications.Models.UnreadStreamNotificationsModel`) See [Roblox.Api.Notifications.Models.UnreadStreamNotificationsModel](#roblox-api-notifications-models-unreadstreamnotificationsmodel) in Models. **Response example:** ```json { "unreadNotifications": 0, "statusMessage": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://notifications.roblox.com/v2/stream-notifications/unread-count" ``` ### POST `/v2/push-notifications/deregister-all-devices` De-register all devices to disable push notifications **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Api.Notifications.Models.SuccessResponseModel` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Api.Notifications.Models.SuccessResponseModel`) See [Roblox.Api.Notifications.Models.SuccessResponseModel](#roblox-api-notifications-models-successresponsemodel) in Models. **Response example:** ```json { "statusMessage": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://notifications.roblox.com/v2/push-notifications/deregister-all-devices" ``` ### POST `/v2/push-notifications/deregister-current-device` De-register current device to disable push notifications **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Api.Notifications.Models.SuccessResponseModel` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Api.Notifications.Models.SuccessResponseModel`) See [Roblox.Api.Notifications.Models.SuccessResponseModel](#roblox-api-notifications-models-successresponsemodel) in Models. **Response example:** ```json { "statusMessage": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://notifications.roblox.com/v2/push-notifications/deregister-current-device" ``` ### POST `/v2/push-notifications/deregister-current-device-ios-pushkit` De-register current device to disable pushkit notifications **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Api.Notifications.Models.SuccessResponseModel` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Api.Notifications.Models.SuccessResponseModel`) See [Roblox.Api.Notifications.Models.SuccessResponseModel](#roblox-api-notifications-models-successresponsemodel) in Models. **Response example:** ```json { "statusMessage": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://notifications.roblox.com/v2/push-notifications/deregister-current-device-ios-pushkit" ``` ### POST `/v2/push-notifications/register-android-native` Register Android Native for push notifications **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Api.Notifications.Models.RegisterAndroidRequestModel` See [Roblox.Api.Notifications.Models.RegisterAndroidRequestModel](#roblox-api-notifications-models-registerandroidrequestmodel) in Models. **Request example:** ```json { "notificationToken": "string", "authorizeForUser": false, "oldNotificationToken": "string", "deviceName": "string" } ``` **Responses:** - `200`: OK → `Roblox.Api.Notifications.Models.RegistrationResponseModel` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Api.Notifications.Models.RegistrationResponseModel`) See [Roblox.Api.Notifications.Models.RegistrationResponseModel](#roblox-api-notifications-models-registrationresponsemodel) in Models. **Response example:** ```json { "registration": { "userPushNotificationDestinationId": 0, "name": "string", "notificationToken": "string", "application": "string", "platform": "ChromeOnDesktop" }, "statusMessage": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://notifications.roblox.com/v2/push-notifications/register-android-native" \ -H "Content-Type: application/json" \ -d '{ "notificationToken": "string", "authorizeForUser": false, "oldNotificationToken": "string", "deviceName": "string" }' ``` ### POST `/v2/push-notifications/register-ios-native` Registers IOS device for push notifications **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Api.Notifications.Models.RegisterIOSNativeRequestModel` See [Roblox.Api.Notifications.Models.RegisterIOSNativeRequestModel](#roblox-api-notifications-models-registeriosnativerequestmodel) in Models. **Request example:** ```json { "notificationToken": "string", "destinationIdentifier": "string", "authorizeForUser": false, "oldNotificationToken": "string", "deviceName": "string" } ``` **Responses:** - `200`: OK → `Roblox.Api.Notifications.Models.RegistrationResponseModel` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Api.Notifications.Models.RegistrationResponseModel`) See [Roblox.Api.Notifications.Models.RegistrationResponseModel](#roblox-api-notifications-models-registrationresponsemodel) in Models. **Response example:** ```json { "registration": { "userPushNotificationDestinationId": 0, "name": "string", "notificationToken": "string", "application": "string", "platform": "ChromeOnDesktop" }, "statusMessage": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://notifications.roblox.com/v2/push-notifications/register-ios-native" \ -H "Content-Type: application/json" \ -d '{ "notificationToken": "string", "destinationIdentifier": "string", "authorizeForUser": false, "oldNotificationToken": "string", "deviceName": "string" }' ``` ### POST `/v2/push-notifications/register-ios-pushkit` Registers IOS device for pushkit notifications **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Api.Notifications.Models.RegisterIOSPushKitRequestModel` See [Roblox.Api.Notifications.Models.RegisterIOSPushKitRequestModel](#roblox-api-notifications-models-registeriospushkitrequestmodel) in Models. **Request example:** ```json { "notificationToken": "string", "destinationIdentifier": "string", "authorizeForUser": false, "oldNotificationToken": "string", "deviceName": "string" } ``` **Responses:** - `200`: OK → `Roblox.Api.Notifications.Models.RegistrationResponseModel` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Api.Notifications.Models.RegistrationResponseModel`) See [Roblox.Api.Notifications.Models.RegistrationResponseModel](#roblox-api-notifications-models-registrationresponsemodel) in Models. **Response example:** ```json { "registration": { "userPushNotificationDestinationId": 0, "name": "string", "notificationToken": "string", "application": "string", "platform": "ChromeOnDesktop" }, "statusMessage": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://notifications.roblox.com/v2/push-notifications/register-ios-pushkit" \ -H "Content-Type: application/json" \ -d '{ "notificationToken": "string", "destinationIdentifier": "string", "authorizeForUser": false, "oldNotificationToken": "string", "deviceName": "string" }' ``` ### POST `/v2/stream-notifications/clear-unread` Clears the unread Notification stream count **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Api.Notifications.Models.SuccessResponseModel` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Api.Notifications.Models.SuccessResponseModel`) See [Roblox.Api.Notifications.Models.SuccessResponseModel](#roblox-api-notifications-models-successresponsemodel) in Models. **Response example:** ```json { "statusMessage": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://notifications.roblox.com/v2/stream-notifications/clear-unread" ``` ## Models ### Roblox.Api.Notifications.Models.ChromeManifestModel Chrome Manifest to link GCM project to Chrome browser | Property | Type | Required | Description | |----------|------|----------|-------------| | `name` | `string` | No | | | `gcm_sender_id` | `string` | No | | ### Roblox.Api.Notifications.Models.GameUpdateNotificationModel Model for a game update message to be displayed in notification stream. | Property | Type | Required | Description | |----------|------|----------|-------------| | `universeId` | `integer` | No | ID of the game. | | `rootPlaceId` | `integer` | No | ID of the game. | | `createdOn` | `string` | No | Creation date of the update message. | | `createdOnKey` | `string` | No | Key of Creation date ticks (dynamo table sort key). | | `content` | `string` | No | Content of the update message. | | `universeName` | `string` | No | Name of the game. | ### Roblox.Api.Notifications.Models.GetCurrentPushDestinationResponseModel | Property | Type | Required | Description | |----------|------|----------|-------------| | `destination` | `Roblox.Api.Notifications.Models.UserPushDestination` | No | | | `statusMessage` | `string` | No | Message for the success response | ### Roblox.Api.Notifications.Models.GetMetadataResponseModel | Property | Type | Required | Description | |----------|------|----------|-------------| | `metadata` | `Roblox.Api.Notifications.Models.PushNotificationClientMetadata` | No | | | `statusMessage` | `string` | No | Message for the success response | ### Roblox.Api.Notifications.Models.GetPushDestinationsResponseModel | Property | Type | Required | Description | |----------|------|----------|-------------| | `destinations` | `Roblox.Api.Notifications.Models.UserPushDestination[]` | No | List of destinations for the user push notification | | `statusMessage` | `string` | No | Message for the success response | ### Roblox.Api.Notifications.Models.NotificationStreamEntriesModel | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `string` | No | Id of the notification stream entry | | `notificationSourceType` | `string enum (15 values)` | No | Type of the notification source ['Test' = 0, 'FriendRequestReceived' = 1, 'FriendRequestAccepted' = 2, 'PartyInviteReceived' = 3, 'PartyMemberJoined' = 4, 'ChatNewMessage' = 5, 'PrivateMessageReceived' = 6, 'UserAddedToPrivateServerWhiteList' = 7, 'ConversationUniverseChanged' = 8, 'TeamCreateInvite' = 9, 'GameUpdate' = 10, 'DeveloperMetricsAvailable' = 11, 'GroupJoinRequestAccepted' = 12, 'Sendr' = 13, 'ExperienceInvitation' = 14] Values: Test, FriendRequestReceived, FriendRequestAccepted, PartyInviteReceived, PartyMemberJoined, ChatNewMessage, PrivateMessageReceived, UserAddedToPrivateServerWhiteList, ConversationUniverseChanged, TeamCreateInvite, GameUpdate, DeveloperMetricsAvailable, GroupJoinRequestAccepted, Sendr, ExperienceInvitation | | `eventDate` | `string` | No | Datetime when the notification stream entry event had occured | | `timestamp` | `string` | No | Relative timestamp for sendr notification stream entry | | `isInteracted` | `boolean` | No | Has the user interacted with the notification stream entry | | `metadataCollection` | `any[]` | No | List of metadata objects showing more details related to the notification stream entry | | `eventCount` | `integer` | No | Count of events corresponding to the group of notification stream entry | | `content` | `any` | No | Content object for sendr notification stream entry | ### Roblox.Api.Notifications.Models.NotificationUser | Property | Type | Required | Description | |----------|------|----------|-------------| | `name` | `string` | No | Name of the user | | `userId` | `integer` | No | Id of the user | ### Roblox.Api.Notifications.Models.PushNotificationClientMetadata | Property | Type | Required | Description | |----------|------|----------|-------------| | `notificationId` | `string` | No | Id for the push client notification | | `type` | `string` | No | Type of the push client notification | | `detail` | `any` | No | Details corresponding to the notification | | `fallbackDelivered` | `boolean` | No | Is fallback delivered for the notification metadata | ### Roblox.Api.Notifications.Models.PushNotificationRegistration | Property | Type | Required | Description | |----------|------|----------|-------------| | `userPushNotificationDestinationId` | `integer` | No | Id of the push notification destination | | `name` | `string` | No | Name of the push Notification destination | | `notificationToken` | `string` | No | Notification token | | `application` | `string` | No | Notification application Example : "Roblox" | | `platform` | `string enum (8 values)` | No | Notification platform ['ChromeOnDesktop' = 0, 'AndroidNative' = 1, 'FirefoxOnDesktop' = 2, 'IOSNative' = 3, 'AndroidAmazon' = 4, 'IOSTencent' = 5, 'AndroidTencentService' = 6, 'IOSPushKit' = 7] Values: ChromeOnDesktop, AndroidNative, FirefoxOnDesktop, IOSNative, AndroidAmazon, IOSTencent, AndroidTencentService, IOSPushKit | ### Roblox.Api.Notifications.Models.RegisterAndroidRequestModel | Property | Type | Required | Description | |----------|------|----------|-------------| | `notificationToken` | `string` | No | Token for notification | | `authorizeForUser` | `boolean` | No | Is this call authorized for user | | `oldNotificationToken` | `string` | No | Old notification token | | `deviceName` | `string` | No | Name of the requesting device | ### Roblox.Api.Notifications.Models.RegisterIOSNativeRequestModel | Property | Type | Required | Description | |----------|------|----------|-------------| | `notificationToken` | `string` | No | Token for notification | | `destinationIdentifier` | `string` | No | Identifier string for the destination | | `authorizeForUser` | `boolean` | No | Is call authorized for user | | `oldNotificationToken` | `string` | No | Old notification token | | `deviceName` | `string` | No | Name of the requesting device | ### Roblox.Api.Notifications.Models.RegisterIOSPushKitRequestModel | Property | Type | Required | Description | |----------|------|----------|-------------| | `notificationToken` | `string` | No | Token for notification | | `destinationIdentifier` | `string` | No | Identifier string for the destination | | `authorizeForUser` | `boolean` | No | Is call authorized for user | | `oldNotificationToken` | `string` | No | Old notification token | | `deviceName` | `string` | No | Name of the requesting device | ### Roblox.Api.Notifications.Models.RegistrationResponseModel | Property | Type | Required | Description | |----------|------|----------|-------------| | `registration` | `Roblox.Api.Notifications.Models.PushNotificationRegistration` | No | | | `statusMessage` | `string` | No | Message for the success response | ### Roblox.Api.Notifications.Models.ResponseModels.NotificationStreamMetadataResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `bannerDismissTimeSpan` | `integer` | No | | | `signalRDisconnectionResponseInMilliseconds` | `integer` | No | | | `canLaunchGameFromGameUpdate` | `boolean` | No | | ### Roblox.Api.Notifications.Models.SuccessResponseModel | Property | Type | Required | Description | |----------|------|----------|-------------| | `statusMessage` | `string` | No | Message for the success response | ### Roblox.Api.Notifications.Models.UnreadStreamNotificationsModel | Property | Type | Required | Description | |----------|------|----------|-------------| | `unreadNotifications` | `integer` | No | Count of unread notifications in the stream | | `statusMessage` | `string` | No | Message for the success response | ### Roblox.Api.Notifications.Models.UserPushDestination | Property | Type | Required | Description | |----------|------|----------|-------------| | `user` | `Roblox.Api.Notifications.Models.NotificationUser` | No | | | `name` | `string` | No | Name of the destination | | `notificationToken` | `string` | No | Token for the corresponding notification | | `supportsUpdateNotifications` | `boolean` | No | Is Update notification feature supported | | `userPushNotificationDestinationId` | `integer` | No | Destination Id for the push notification | | `application` | `string` | No | application for the corresponding notification Example : "Roblox" | | `platform` | `string enum (8 values)` | No | Platform for the corresponding notification ['ChromeOnDesktop' = 0, 'AndroidNative' = 1, 'FirefoxOnDesktop' = 2, 'IOSNative' = 3, 'AndroidAmazon' = 4, 'IOSTencent' = 5, 'AndroidTencentService' = 6, 'IOSPushKit' = 7] Values: ChromeOnDesktop, AndroidNative, FirefoxOnDesktop, IOSNative, AndroidAmazon, IOSTencent, AndroidTencentService, IOSPushKit | --- name: "PremiumFeatures Api v1" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://premiumfeatures.roblox.com" version: "v1" endpoints: 2 auth: [cookie] --- # PremiumFeatures Api v1 **API Version:** v1 **Base URL:** `https://premiumfeatures.roblox.com` ## Endpoints ### GET `/v1/users/{userId}/premium-upsell-precheck` Premium upsell precheck **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | User ID | | `universeId` | query | `integer (int64)` | Yes | | | `placeId` | query | `integer (int64)` | Yes | | **Responses:** - `200`: OK - `401`: 0: Authorization has been denied for this request. **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://premiumfeatures.roblox.com/v1/users/{USERID}/premium-upsell-precheck?universeId={VALUE}&placeId={VALUE}" ``` ### GET `/v1/users/{userId}/validate-membership` Get if a user has a Premium membership **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | User ID | **Responses:** - `200`: OK - `401`: 0: Authorization has been denied for this request. **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://premiumfeatures.roblox.com/v1/users/{USERID}/validate-membership" ``` --- name: "presence-api" last_updated: 2026-06-29T19:34:17Z type: opencloud api_base_url: "https://presence.roblox.com" version: "v1" endpoints: 1 --- # presence-api **API Version:** v1 **Base URL(s):** - `https://presence.roblox.com` ## Endpoints ### POST `/v1/presence/users` **Request Body:** `application/json` — Type: `any` **Responses:** - `200`: Success → `UserPresencesResponse` - `400`: Bad Request → `ErrorResponse` - `403`: Forbidden → `ErrorResponse` - `429`: Too Many Requests → `ErrorResponse` **Response fields** (`UserPresencesResponse`) See [UserPresencesResponse](#userpresencesresponse) in Models. **Response example:** ```json { "userPresences": [ { "userPresenceType": "...", "lastLocation": "...", "placeId": "...", "rootPlaceId": "...", "gameId": "...", "universeId": "..." } ] } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -H "Authorization: Bearer $ROBLOX_ACCESS_TOKEN" \ "https://presence.roblox.com/v1/presence/users" \ -H "Content-Type: application/json" \ -d '"..."' ``` ## Models ### Error | Property | Type | Required | Description | |----------|------|----------|-------------| | `code` | `integer` | No | | | `message` | `string` | No | | ### ErrorResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `errors` | `Error[]` | No | | ### UserPresence | Property | Type | Required | Description | |----------|------|----------|-------------| | `userPresenceType` | `any` | No | | | `lastLocation` | `string` | No | | | `placeId` | `integer` | No | | | `rootPlaceId` | `integer` | No | | | `gameId` | `string` | No | | | `universeId` | `integer` | No | | | `userId` | `integer` | No | | ### UserPresenceRequest | Property | Type | Required | Description | |----------|------|----------|-------------| | `userIds` | `integer[]` | No | | ### UserPresencesResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `userPresences` | `UserPresence[]` | No | | --- name: "PrivateMessages Api v1" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://privatemessages.roblox.com" version: "v1" endpoints: 9 auth: [cookie] --- # PrivateMessages Api v1 **API Version:** v1 **Base URL:** `https://privatemessages.roblox.com` ## Endpoints ### GET `/v1/announcements` Migrate from RobloxWebsite project, return news notification for Private Message page **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.PrivateMessages.Api.Models.GetAnnouncementsResponse` - `400`: 2: Message does not exist or the current user is not authorized to view it. - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.PrivateMessages.Api.Models.GetAnnouncementsResponse`) See [Roblox.PrivateMessages.Api.Models.GetAnnouncementsResponse](#roblox-privatemessages-api-models-getannouncementsresponse) in Models. **Response example:** ```json { "collection": [ { "id": "...", "sender": "...", "subject": "...", "body": "...", "created": "...", "updated": "..." } ], "totalCollectionSize": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://privatemessages.roblox.com/v1/announcements" ``` ### GET `/v1/announcements/metadata` **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.PrivateMessages.Api.Models.AnnouncementsMetadataResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.PrivateMessages.Api.Models.AnnouncementsMetadataResponse`) See [Roblox.PrivateMessages.Api.Models.AnnouncementsMetadataResponse](#roblox-privatemessages-api-models-announcementsmetadataresponse) in Models. **Response example:** ```json { "numOfAnnouncements": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://privatemessages.roblox.com/v1/announcements/metadata" ``` ### GET `/v1/messages` Gets a user's messages. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `pageNumber` | query | `integer (int32)` | No | | | `pageSize` | query | `integer (int32)` | No | | | `messageTab` | query | `string` | No | Valid values: `Inbox`, `Sent`, `Archive` | **Responses:** - `200`: OK → `Roblox.PrivateMessages.Api.Models.GetMessagesResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.PrivateMessages.Api.Models.GetMessagesResponse`) See [Roblox.PrivateMessages.Api.Models.GetMessagesResponse](#roblox-privatemessages-api-models-getmessagesresponse) in Models. **Response example:** ```json { "collection": [ { "id": "...", "sender": "...", "recipient": "...", "subject": "...", "body": "...", "created": "..." } ], "totalCollectionSize": 0, "totalPages": 0, "pageNumber": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://privatemessages.roblox.com/v1/messages" ``` ### GET `/v1/messages/{messageId}` Gets a message's details. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `messageId` | path | `integer (int64)` | Yes | | **Responses:** - `200`: OK → `Roblox.PrivateMessages.Api.Models.MessageDetailsResponse` - `400`: 2: Message does not exist or the current user is not authorized to view it. - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.PrivateMessages.Api.Models.MessageDetailsResponse`) See [Roblox.PrivateMessages.Api.Models.MessageDetailsResponse](#roblox-privatemessages-api-models-messagedetailsresponse) in Models. **Response example:** ```json { "id": 0, "sender": { "hasVerifiedBadge": false, "id": 0, "name": "string", "displayName": "string" }, "recipient": { "hasVerifiedBadge": false, "id": 0, "name": "string", "displayName": "string" }, "subject": "string", "body": "string", "created": "2024-01-01T00:00:00Z" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://privatemessages.roblox.com/v1/messages/{MESSAGEID}" ``` ### GET `/v1/messages/unread/count` Gets unread messages for the authenticated user. **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.PrivateMessages.Api.Models.UnreadMessagesCountResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.PrivateMessages.Api.Models.UnreadMessagesCountResponse`) See [Roblox.PrivateMessages.Api.Models.UnreadMessagesCountResponse](#roblox-privatemessages-api-models-unreadmessagescountresponse) in Models. **Response example:** ```json { "count": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://privatemessages.roblox.com/v1/messages/unread/count" ``` ### POST `/v1/messages/archive` Archives a batch of messages. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.PrivateMessages.Api.Models.BatchMessagesRequest` See [Roblox.PrivateMessages.Api.Models.BatchMessagesRequest](#roblox-privatemessages-api-models-batchmessagesrequest) in Models. **Request example:** ```json { "messageIds": [ 0 ] } ``` **Responses:** - `200`: OK → `Roblox.PrivateMessages.Api.Models.BatchMessagesResponse` - `400`: 5: Too many ids in a batch request. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.PrivateMessages.Api.Models.BatchMessagesResponse`) See [Roblox.PrivateMessages.Api.Models.BatchMessagesResponse](#roblox-privatemessages-api-models-batchmessagesresponse) in Models. **Response example:** ```json { "failedMessages": [ { "messageId": "...", "errorMessage": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://privatemessages.roblox.com/v1/messages/archive" \ -H "Content-Type: application/json" \ -d '{ "messageIds": [ 0 ] }' ``` ### POST `/v1/messages/mark-read` Marks a batch of messages as read. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.PrivateMessages.Api.Models.BatchMessagesRequest` See [Roblox.PrivateMessages.Api.Models.BatchMessagesRequest](#roblox-privatemessages-api-models-batchmessagesrequest) in Models. **Request example:** ```json { "messageIds": [ 0 ] } ``` **Responses:** - `200`: OK → `Roblox.PrivateMessages.Api.Models.BatchMessagesResponse` - `400`: 5: Too many ids in a batch request. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.PrivateMessages.Api.Models.BatchMessagesResponse`) See [Roblox.PrivateMessages.Api.Models.BatchMessagesResponse](#roblox-privatemessages-api-models-batchmessagesresponse) in Models. **Response example:** ```json { "failedMessages": [ { "messageId": "...", "errorMessage": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://privatemessages.roblox.com/v1/messages/mark-read" \ -H "Content-Type: application/json" \ -d '{ "messageIds": [ 0 ] }' ``` ### POST `/v1/messages/mark-unread` Marks a batch of messages as unread. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.PrivateMessages.Api.Models.BatchMessagesRequest` See [Roblox.PrivateMessages.Api.Models.BatchMessagesRequest](#roblox-privatemessages-api-models-batchmessagesrequest) in Models. **Request example:** ```json { "messageIds": [ 0 ] } ``` **Responses:** - `200`: OK → `Roblox.PrivateMessages.Api.Models.BatchMessagesResponse` - `400`: 5: Too many ids in a batch request. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.PrivateMessages.Api.Models.BatchMessagesResponse`) See [Roblox.PrivateMessages.Api.Models.BatchMessagesResponse](#roblox-privatemessages-api-models-batchmessagesresponse) in Models. **Response example:** ```json { "failedMessages": [ { "messageId": "...", "errorMessage": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://privatemessages.roblox.com/v1/messages/mark-unread" \ -H "Content-Type: application/json" \ -d '{ "messageIds": [ 0 ] }' ``` ### POST `/v1/messages/unarchive` Unarchives a batch of messages. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.PrivateMessages.Api.Models.BatchMessagesRequest` See [Roblox.PrivateMessages.Api.Models.BatchMessagesRequest](#roblox-privatemessages-api-models-batchmessagesrequest) in Models. **Request example:** ```json { "messageIds": [ 0 ] } ``` **Responses:** - `200`: OK → `Roblox.PrivateMessages.Api.Models.BatchMessagesResponse` - `400`: 5: Too many ids in a batch request. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.PrivateMessages.Api.Models.BatchMessagesResponse`) See [Roblox.PrivateMessages.Api.Models.BatchMessagesResponse](#roblox-privatemessages-api-models-batchmessagesresponse) in Models. **Response example:** ```json { "failedMessages": [ { "messageId": "...", "errorMessage": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://privatemessages.roblox.com/v1/messages/unarchive" \ -H "Content-Type: application/json" \ -d '{ "messageIds": [ 0 ] }' ``` ## Models ### Roblox.PrivateMessages.Api.Models.AnnouncementsDetailsResponse A message details response. | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | The message's ID. | | `sender` | `Roblox.PrivateMessages.Api.Models.VerifiedSkinnyUserResponse` | No | | | `subject` | `string` | No | The subject of the message. | | `body` | `string` | No | The body of the message. | | `created` | `string` | No | When the message was created. | | `updated` | `string` | No | When the message was last updated. | ### Roblox.PrivateMessages.Api.Models.AnnouncementsMetadataResponse A message details response. | Property | Type | Required | Description | |----------|------|----------|-------------| | `numOfAnnouncements` | `integer` | No | Number of incoming news | ### Roblox.PrivateMessages.Api.Models.BatchMessagesRequest | Property | Type | Required | Description | |----------|------|----------|-------------| | `messageIds` | `integer[]` | No | | ### Roblox.PrivateMessages.Api.Models.BatchMessagesResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `failedMessages` | `Roblox.PrivateMessages.Api.Models.FailedMessageResponse[]` | No | | ### Roblox.PrivateMessages.Api.Models.FailedMessageResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `messageId` | `integer` | No | | | `errorMessage` | `string` | No | | ### Roblox.PrivateMessages.Api.Models.GetAnnouncementsResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `collection` | `Roblox.PrivateMessages.Api.Models.AnnouncementsDetailsResponse[]` | No | | | `totalCollectionSize` | `integer` | No | | ### Roblox.PrivateMessages.Api.Models.GetMessagesResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `collection` | `Roblox.PrivateMessages.Api.Models.MessageDetailsResponse[]` | No | | | `totalCollectionSize` | `integer` | No | | | `totalPages` | `integer` | No | | | `pageNumber` | `integer` | No | | ### Roblox.PrivateMessages.Api.Models.MessageDetailsResponse A message details response. | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | The message's ID. | | `sender` | `Roblox.PrivateMessages.Api.Models.VerifiedSkinnyUserResponse` | No | | | `recipient` | `Roblox.PrivateMessages.Api.Models.VerifiedSkinnyUserResponse` | No | | | `subject` | `string` | No | The subject of the message. | | `body` | `string` | No | The body of the message. | | `created` | `string` | No | When the message was created. | | `updated` | `string` | No | When the message was last updated. | | `isRead` | `boolean` | No | Whether or not the message has been read. | | `isSystemMessage` | `boolean` | No | Whether or not the message is a system message. | | `isReportAbuseDisplayed` | `boolean` | No | Whether or not the abuse report link is displayed for the message. | ### Roblox.PrivateMessages.Api.Models.UnreadMessagesCountResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `count` | `integer` | No | | ### Roblox.PrivateMessages.Api.Models.VerifiedSkinnyUserResponse A response model representing user basic information and the user's verified badge status. | Property | Type | Required | Description | |----------|------|----------|-------------| | `hasVerifiedBadge` | `boolean` | No | Whether the user has a verified badge. | | `id` | `integer` | No | | | `name` | `string` | No | | | `displayName` | `string` | No | | --- name: "Publish Api v1" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://publish.roblox.com" version: "v1" endpoints: 6 auth: [cookie] --- # Publish Api v1 **API Version:** v1 **Base URL:** `https://publish.roblox.com` ## Endpoints ### GET `/v1/asset-quotas` List asset quotas of the given resource type and asset type. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `resourceType` | query | `string` | Yes | Resource type of the asset quota | | `assetType` | query | `string` | Yes | Asset type of the asset quota | | `useDummyData` | query | `boolean` | No | Use dummy data for testing. This is for internal use only | **Responses:** - `200`: OK → `Roblox.Publish.Api.AssetQuotasResponse` - `400`: 7: The asset type is not appropriate for this request. 8: The resource type is not appropriate for this request. - `401`: 0: Authorization has been denied for this request. - `500`: 0: Reserved for base level errors. Do not use in your endpoint directly, do not document. **Response fields** (`Roblox.Publish.Api.AssetQuotasResponse`) See [Roblox.Publish.Api.AssetQuotasResponse](#roblox-publish-api-assetquotasresponse) in Models. **Response example:** ```json { "quotas": [ { "duration": "...", "usage": "...", "capacity": "...", "expirationTime": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://publish.roblox.com/v1/asset-quotas?resourceType={VALUE}&assetType={VALUE}" ``` ### POST `/v1/audio` Published an audio file and returns the new asset info. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Publish.Api.UploadAudioRequest` See [Roblox.Publish.Api.UploadAudioRequest](#roblox-publish-api-uploadaudiorequest) in Models. **Request example:** ```json { "name": "string", "file": "string", "groupId": 0, "paymentSource": "string", "estimatedFileSize": 0, "estimatedDuration": 0 } ``` **Responses:** - `200`: OK → `Roblox.Publish.Api.PublishAudioResponse` - `400`: 3: The request did not contain a file to be uploaded. 4: The file in the request is too large. 5: The duration of the audio file is too long. 7: Failed to parse the file. 8: The file type is not supported. 9: The file is corrupted 11: Missing permissions to spend group funds. 14: The user/group does not have suffiecient funds to publish. 14: The user/group does not have suffiecient funds to publish. 15: The audio file has already been reviewed and rejected. 18: Too many requests. Try again later. 20: Error while trying to purchase the product. 22: The file size estimation error was greater than the acceptable margin of error. 23: The duration estimation error was greater than the acceptable margin of error. 24: Asset privacy is invalid. 29: Invalid argument in the request. - `401`: 0: Authorization has been denied for this request. 1: The request did not include an authorization. - `403`: 0: Token Validation Failed - `500`: 19: Asset creation was unavailable. Please try again. **Response fields** (`Roblox.Publish.Api.PublishAudioResponse`) See [Roblox.Publish.Api.PublishAudioResponse](#roblox-publish-api-publishaudioresponse) in Models. **Response example:** ```json { "Id": 0, "Name": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://publish.roblox.com/v1/audio" \ -H "Content-Type: application/json" \ -d '{ "name": "string", "file": "string", "groupId": 0, "paymentSource": "string", "estimatedFileSize": 0, "estimatedDuration": 0 }' ``` ### POST `/v1/audio/verify` Verifies an audio file and returns a product that you can purchase to publish the audio file. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Publish.Api.VerifyAudioRequest` See [Roblox.Publish.Api.VerifyAudioRequest](#roblox-publish-api-verifyaudiorequest) in Models. **Request example:** ```json { "name": "string", "file": "string", "groupId": 0, "paymentSource": "string", "fileSize": 0, "duration": 0 } ``` **Responses:** - `200`: OK → `Roblox.Publish.Api.VerifyAudioResponse` - `400`: 3: The request did not contain a file to be uploaded. 4: The file in the request is too large. 5: The duration of the audio file is too long. 7: Failed to parse the file. 8: The file type is not supported. 9: The file is corrupted 18: Too many requests. Try again later. - `401`: 0: Authorization has been denied for this request. 1: The request did not include an authorization. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Publish.Api.VerifyAudioResponse`) See [Roblox.Publish.Api.VerifyAudioResponse](#roblox-publish-api-verifyaudioresponse) in Models. **Response example:** ```json { "name": "string", "price": 0, "balance": 0, "canAfford": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://publish.roblox.com/v1/audio/verify" \ -H "Content-Type: application/json" \ -d '{ "name": "string", "file": "string", "groupId": 0, "paymentSource": "string", "fileSize": 0, "duration": 0 }' ``` ### POST `/v1/badges/{badgeId}/icon` Overwrites a badge icon with a new one. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `badgeId` | path | `integer (int64)` | Yes | The badge Id. | | `Files` | formData | `file` | No | | **Responses:** - `200`: OK → `Roblox.Publish.Api.UploadResponse` - `400`: 2: File not present in request. 12: Name or description is moderated. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 5: You do not have permission to manage this item. - `404`: 4: Target item is invalid or does not exist. - `429`: 3: You're uploading too much, please wait and try again later. **Response fields** (`Roblox.Publish.Api.UploadResponse`) See [Roblox.Publish.Api.UploadResponse](#roblox-publish-api-uploadresponse) in Models. **Response example:** ```json { "targetId": 0 } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://publish.roblox.com/v1/badges/{BADGEID}/icon" ``` ### POST `/v1/games/{gameId}/thumbnail/image` Uploads a game thumbnail. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer (int64)` | Yes | The universe Id. | | `Files` | formData | `file` | No | | **Responses:** - `200`: OK → `Roblox.Publish.Api.UploadResponse` - `400`: 1: File uploaded does not match known image format. Try converting to png. 2: File not present in request. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 5: You do not have permission to manage this item. - `404`: 4: Target item is invalid or does not exist. - `429`: 3: You're uploading too much, please wait and try again later. **Response fields** (`Roblox.Publish.Api.UploadResponse`) See [Roblox.Publish.Api.UploadResponse](#roblox-publish-api-uploadresponse) in Models. **Response example:** ```json { "targetId": 0 } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://publish.roblox.com/v1/games/{GAMEID}/thumbnail/image" ``` ### POST `/v1/plugins/{pluginId}/icon` Overwrites a plugin icon with a new one. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `pluginId` | path | `integer (int64)` | Yes | The plugin Id. | | `Files` | formData | `file` | No | | **Responses:** - `200`: OK → `Roblox.Publish.Api.UploadResponse` - `400`: 2: File not present in request. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 5: You do not have permission to manage this item. - `404`: 4: Target item is invalid or does not exist. - `429`: 3: You're uploading too much, please wait and try again later. **Response fields** (`Roblox.Publish.Api.UploadResponse`) See [Roblox.Publish.Api.UploadResponse](#roblox-publish-api-uploadresponse) in Models. **Response example:** ```json { "targetId": 0 } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://publish.roblox.com/v1/plugins/{PLUGINID}/icon" ``` ## Models ### Roblox.Publish.Api.AssetQuota Model for asset quota. | Property | Type | Required | Description | |----------|------|----------|-------------| | `duration` | `string` | No | Duration type of the quota. | | `usage` | `integer` | No | Current usage of the quota. | | `capacity` | `integer` | No | Capacity of the quota. | | `expirationTime` | `string` | No | Expiration time of current usage limit. | ### Roblox.Publish.Api.AssetQuotasResponse Response model for asset quotas. | Property | Type | Required | Description | |----------|------|----------|-------------| | `quotas` | `Roblox.Publish.Api.AssetQuota[]` | No | A list of quotas. | ### Roblox.Publish.Api.PublishAudioResponse Response model for publish audio. | Property | Type | Required | Description | |----------|------|----------|-------------| | `Id` | `integer` | No | Id of the published asset. | | `Name` | `string` | No | Name of the published asset. | ### Roblox.Publish.Api.UploadAudioRequest A request model for uploading an audio file. | Property | Type | Required | Description | |----------|------|----------|-------------| | `name` | `string` | No | Name for the audio file. | | `file` | `string` | No | File to be uploaded. Formatted as a base64 string. | | `groupId` | `integer` | No | Id of the group you are publishing the audio asset for. Null if not publishing under a group. | | `paymentSource` | `string` | No | The source of funds for payment. User: Use personal funds of authenticated user. Group: Use group funds from Roblox.Publish.Api.UploadAudioRequest.GroupId. Null/Empty: Will default to authenticated user funds. | | `estimatedFileSize` | `integer` | No | Estimated file size of the audio file in bytes. | | `estimatedDuration` | `number` | No | Estimated duration of the audio file in seconds. | | `assetPrivacy` | `1 \| 2` | No | The asset privacy of the audio asset. | ### Roblox.Publish.Api.UploadResponse A response used when an upload has completed. | Property | Type | Required | Description | |----------|------|----------|-------------| | `targetId` | `integer` | No | The target Id of the uploaded item. | ### Roblox.Publish.Api.VerifyAudioRequest Request model to publish an audio asset. | Property | Type | Required | Description | |----------|------|----------|-------------| | `name` | `string` | No | Gets or sets the name of the audio asset. | | `file` | `string` | No | File to be uploaded. Formatted as a base64 string. | | `groupId` | `integer` | No | Gets or sets the ID of the group if applicable. Optional. | | `paymentSource` | `string` | No | Gets or sets the payment source. 'User' or 'Group'. Required if Group ID is set. | | `fileSize` | `integer` | No | Gets or sets the size of the audio file in bytes. | | `duration` | `number` | No | Gets or sets the duration of the audio in seconds. | ### Roblox.Publish.Api.VerifyAudioResponse Response model for verify audio endpoint. | Property | Type | Required | Description | |----------|------|----------|-------------| | `name` | `string` | No | Name of the audio file. | | `price` | `integer` | No | Price in robux to publish the audio file. | | `balance` | `integer` | No | User's current Robux balance. | | `canAfford` | `boolean` | No | Boolean, true if the user can afford to purchase the publishing of the audio file. | --- name: "Thumbnails Api v1" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://thumbnails.roblox.com" version: "v1" endpoints: 19 auth: [cookie] --- # Thumbnails Api v1 **API Version:** v1 **Base URL:** `https://thumbnails.roblox.com` ## Endpoints ### GET `/v1/asset-thumbnail-animated` Thumbnails asset animated. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetId` | query | `integer (int64)` | Yes | The asset id. | | `Roblox-Place-Id` | header | `integer (int64)` | No | (optional) placeid | **Responses:** - `200`: OK - `400`: 4: The requested Ids are invalid, of an invalid type or missing. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://thumbnails.roblox.com/v1/asset-thumbnail-animated?assetId={VALUE}" ``` ### GET `/v1/assets` Thumbnails assets. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetIds` | query | `array` | Yes | The asset ids. | | `Roblox-Place-Id` | header | `integer (int64)` | No | (optional) placeid | | `returnPolicy` | query | `string` | No | Optional policy to use in selecting thumbnail to return (default = PlaceHolder). Valid values: `PlaceHolder`, `ForcePlaceHolder`, `AutoGenerated`, `ForceAutoGenerated` | | `size` | query | `string` | No | The thumbnail size, formatted widthxheight Valid values: `30x30`, `42x42`, `50x50`, `60x62`, `75x75`, `110x110`, `140x140`, `150x150`, `160x100`, `160x600`, `250x250`, `256x144`, `300x250`, `304x166`, `384x216`, `396x216`, `420x420`, `480x270`, `512x512`, `576x324`, `700x700`, `728x90`, `768x432`, `1200x80`, `330x110`, `660x220`, `1320x440`, `720x228`, `1440x456`, `930x480` | | `format` | query | `string` | No | The thumbnail format Valid values: `Png`, `Jpeg`, `Webp` | | `isCircular` | query | `boolean` | No | The circle thumbnail output parameter, true or false Valid values: `true`, `false` | **Responses:** - `200`: OK - `400`: 1: There are too many requested Ids. 2: The requested image format is invalid. Please see documentation for valid thumbnail format parameter name and values. 3: The requested size is invalid. Please see documentation for valid thumbnail size parameter name and format. 4: The requested Ids are invalid, of an invalid type or missing. 8: The requested return policy is invalid (must be PlaceHolder, AutoGenerated or ForceAutoGenerated). 10: Circular thumbnail requests are not allowed - `403`: 9: User not authorized to use AutoGenerated or ForceAutoGenerated return policies. **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://thumbnails.roblox.com/v1/assets?assetIds={VALUE}" ``` ### GET `/v1/assets-thumbnail-3d` Thumbnails assets. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `assetId` | query | `integer (int64)` | Yes | The asset id. | | `useGltf` | query | `boolean` | No | (optional) formatType | | `Roblox-Place-Id` | header | `integer (int64)` | No | (optional) placeid | **Responses:** - `200`: OK - `400`: 4: The requested Ids are invalid, of an invalid type or missing. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://thumbnails.roblox.com/v1/assets-thumbnail-3d?assetId={VALUE}" ``` ### GET `/v1/badges/icons` Thumbnails badge icons. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `badgeIds` | query | `array` | Yes | The badge ids. | | `size` | query | `string` | No | The thumbnail size, formatted widthxheight Valid values: `150x150` | | `format` | query | `string` | No | The thumbnail format Valid values: `Png`, `Webp` | | `isCircular` | query | `boolean` | No | The circle thumbnail output parameter, true or false Valid values: `true`, `false` | **Responses:** - `200`: OK - `400`: 1: There are too many requested Ids. 2: The requested image format is invalid. Please see documentation for valid thumbnail format parameter name and values. 3: The requested size is invalid. Please see documentation for valid thumbnail size parameter name and format. 4: The requested Ids are invalid, of an invalid type or missing. 10: Circular thumbnail requests are not allowed **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://thumbnails.roblox.com/v1/badges/icons?badgeIds={VALUE}" ``` ### GET `/v1/bundles/thumbnails` Get bundle thumbnails for the given CSV of bundle ids **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `bundleIds` | query | `array` | Yes | CSV for the bundle ids to get bundle thumbnails | | `size` | query | `string` | No | The thumbnail size, formatted widthxheight Valid values: `150x150`, `420x420` | | `format` | query | `string` | No | The thumbnail format Valid values: `Png`, `Webp` | | `isCircular` | query | `boolean` | No | The circle thumbnail output parameter, true or false Valid values: `true`, `false` | **Responses:** - `200`: OK - `400`: 1: There are too many requested Ids. 2: The requested image format is invalid. Please see documentation for valid thumbnail format parameter name and values. 3: The requested size is invalid. Please see documentation for valid thumbnail size parameter name and format. 4: The requested Ids are invalid, of an invalid type or missing. 10: Circular thumbnail requests are not allowed **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://thumbnails.roblox.com/v1/bundles/thumbnails?bundleIds={VALUE}" ``` ### GET `/v1/developer-products/icons` Thumbnails developer product icons. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `developerProductIds` | query | `array` | Yes | The developer product ids. | | `size` | query | `string` | No | The thumbnail size, formatted widthxheight Valid values: `150x150`, `420x420` | | `format` | query | `string` | No | The thumbnail format Valid values: `Png`, `Webp` | | `isCircular` | query | `boolean` | No | The circle thumbnail output parameter, true or false Valid values: `true`, `false` | **Responses:** - `200`: OK - `400`: 1: There are too many requested Ids. 2: The requested image format is invalid. Please see documentation for valid thumbnail format parameter name and values. 3: The requested size is invalid. Please see documentation for valid thumbnail size parameter name and format. 4: The requested Ids are invalid, of an invalid type or missing. 10: Circular thumbnail requests are not allowed **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://thumbnails.roblox.com/v1/developer-products/icons?developerProductIds={VALUE}" ``` ### GET `/v1/game-passes` Thumbnails game pass icons. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gamePassIds` | query | `array` | Yes | The game pass ids. | | `size` | query | `string` | No | The thumbnail size, formatted widthxheight Valid values: `150x150` | | `format` | query | `string` | No | The thumbnail format Valid values: `Png`, `Webp` | | `isCircular` | query | `boolean` | No | The circle thumbnail output parameter, true or false Valid values: `true`, `false` | **Responses:** - `200`: OK - `400`: 1: There are too many requested Ids. 2: The requested image format is invalid. Please see documentation for valid thumbnail format parameter name and values. 3: The requested size is invalid. Please see documentation for valid thumbnail size parameter name and format. 4: The requested Ids are invalid, of an invalid type or missing. 10: Circular thumbnail requests are not allowed **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://thumbnails.roblox.com/v1/game-passes?gamePassIds={VALUE}" ``` ### GET `/v1/games/{universeId}/thumbnails` Fetches game thumbnail URLs for a list of universes' thumbnail ids. Ids that do not correspond to a valid thumbnail will be filtered out. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeId` | path | `integer (int64)` | Yes | | | `thumbnailIds` | query | `array` | Yes | | | `size` | query | `string` | No | The thumbnail size, formatted widthxheight Valid values: `768x432`, `576x324`, `480x270`, `384x216`, `256x144` | | `format` | query | `string` | No | The thumbnail format Valid values: `Png`, `Jpeg`, `Webp` | | `isCircular` | query | `boolean` | No | The circle thumbnail output parameter, true or false Valid values: `true`, `false` | **Responses:** - `200`: OK - `400`: 1: There are too many requested Ids. 2: The requested image format is invalid. Please see documentation for valid thumbnail format parameter name and values. 3: The requested size is invalid. Please see documentation for valid thumbnail size parameter name and format. 4: The requested Ids are invalid, of an invalid type or missing. 10: Circular thumbnail requests are not allowed - `404`: 5: The requested universe does not exist. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://thumbnails.roblox.com/v1/games/{UNIVERSEID}/thumbnails?thumbnailIds={VALUE}" ``` ### GET `/v1/games/icons` Fetches game icon URLs for a list of universes' root places. Ids that do not correspond to a valid universe will be filtered out. The ordering of the results is not guaranteed to be the same as the inputs. In order to correlated inputs with outputs please use the 'targetId' of the objects in the result array. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeIds` | query | `array` | Yes | The universe ids. | | `returnPolicy` | query | `string` | No | Optional policy to use in selecting game icon to return (default = PlaceHolder). Valid values: `PlaceHolder`, `ForcePlaceHolder`, `AutoGenerated`, `ForceAutoGenerated` | | `size` | query | `string` | No | The thumbnail size, formatted widthxheight Valid values: `50x50`, `128x128`, `150x150`, `256x256`, `420x420`, `512x512` | | `format` | query | `string` | No | The thumbnail format Valid values: `Png`, `Jpeg`, `Webp` | | `isCircular` | query | `boolean` | No | The circle thumbnail output parameter, true or false Valid values: `true`, `false` | **Responses:** - `200`: OK - `400`: 1: There are too many requested Ids. 2: The requested image format is invalid. Please see documentation for valid thumbnail format parameter name and values. 3: The requested size is invalid. Please see documentation for valid thumbnail size parameter name and format. 4: The requested Ids are invalid, of an invalid type or missing. 8: The requested return policy is invalid (must be PlaceHolder, AutoGenerated or ForceAutoGenerated). 10: Circular thumbnail requests are not allowed - `403`: 9: User not authorized to use AutoGenerated or ForceAutoGenerated return policies. **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://thumbnails.roblox.com/v1/games/icons?universeIds={VALUE}" ``` ### GET `/v1/games/multiget/thumbnails` Fetch game thumbnail URLs for a list of universe IDs. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `universeIds` | query | `array` | Yes | comma-delimited list of universe IDs | | `countPerUniverse` | query | `integer (int32)` | No | max number of thumbnails to return per universe | | `defaults` | query | `boolean` | No | true if defaults (if any) should be returned if no media exists | | `size` | query | `string` | No | The thumbnail size, formatted widthxheight Valid values: `768x432`, `576x324`, `480x270`, `384x216`, `256x144` | | `format` | query | `string` | No | The thumbnail format Valid values: `Png`, `Jpeg`, `Webp` | | `isCircular` | query | `boolean` | No | The circle thumbnail output parameter, true or false Valid values: `true`, `false` | **Responses:** - `200`: OK - `400`: 0: Unknown error 1: There are too many requested Ids. 4: The requested Ids are invalid, of an invalid type or missing. 5: The requested universe does not exist. 10: Circular thumbnail requests are not allowed **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://thumbnails.roblox.com/v1/games/multiget/thumbnails?universeIds={VALUE}" ``` ### GET `/v1/groups/icons` Fetches thumbnail URLs for a list of groups. Ids that do not correspond to groups will be filtered out. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `groupIds` | query | `array` | Yes | | | `size` | query | `string` | No | The thumbnail size, formatted widthxheight Valid values: `150x150`, `420x420` | | `format` | query | `string` | No | The thumbnail format Valid values: `Png`, `Webp` | | `isCircular` | query | `boolean` | No | The circle thumbnail output parameter, true or false Valid values: `true`, `false` | **Responses:** - `200`: OK - `400`: 1: There are too many requested Ids. 2: The requested image format is invalid. Please see documentation for valid thumbnail format parameter name and values. 3: The requested size is invalid. Please see documentation for valid thumbnail size parameter name and format. 4: The requested Ids are invalid, of an invalid type or missing. 10: Circular thumbnail requests are not allowed **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://thumbnails.roblox.com/v1/groups/icons?groupIds={VALUE}" ``` ### GET `/v1/places/gameicons` Fetches game icon URLs for a list of places. Ids that do not correspond to a valid place will be filtered out. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `placeIds` | query | `array` | Yes | The place ids. | | `returnPolicy` | query | `string` | No | Optional policy to use in selecting game icon to return (default = PlaceHolder). Valid values: `PlaceHolder`, `ForcePlaceHolder`, `AutoGenerated`, `ForceAutoGenerated` | | `size` | query | `string` | No | The thumbnail size, formatted widthxheight Valid values: `50x50`, `128x128`, `150x150`, `256x256`, `420x420`, `512x512` | | `format` | query | `string` | No | The thumbnail format Valid values: `Png`, `Jpeg`, `Webp` | | `isCircular` | query | `boolean` | No | The circle thumbnail output parameter, true or false Valid values: `true`, `false` | **Responses:** - `200`: OK - `400`: 1: There are too many requested Ids. 2: The requested image format is invalid. Please see documentation for valid thumbnail format parameter name and values. 3: The requested size is invalid. Please see documentation for valid thumbnail size parameter name and format. 4: The requested Ids are invalid, of an invalid type or missing. 8: The requested return policy is invalid (must be PlaceHolder, AutoGenerated or ForceAutoGenerated). 10: Circular thumbnail requests are not allowed - `403`: 9: User not authorized to use AutoGenerated or ForceAutoGenerated return policies. **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://thumbnails.roblox.com/v1/places/gameicons?placeIds={VALUE}" ``` ### GET `/v1/users/avatar` Get Avatar Full body shots for the given CSV of userIds **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userIds` | query | `array` | Yes | CSV for the userIds to get avatar full body shots | | `includeBackground` | query | `boolean` | No | Whether to include a background in the thumbnail (defaults to false) | | `size` | query | `string` | No | The thumbnail size, formatted widthxheight Valid values: `30x30`, `48x48`, `60x60`, `75x75`, `100x100`, `110x110`, `140x140`, `150x150`, `150x200`, `180x180`, `250x250`, `352x352`, `420x420`, `720x720` | | `format` | query | `string` | No | The thumbnail format Valid values: `Png`, `Jpeg`, `Webp` | | `isCircular` | query | `boolean` | No | The circle thumbnail output parameter, true or false Valid values: `true`, `false` | **Responses:** - `200`: OK - `400`: 1: There are too many requested Ids. 2: The requested image format is invalid. Please see documentation for valid thumbnail format parameter name and values. 3: The requested size is invalid. Please see documentation for valid thumbnail size parameter name and format. 4: The requested Ids are invalid, of an invalid type or missing. 10: Circular thumbnail requests are not allowed **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://thumbnails.roblox.com/v1/users/avatar?userIds={VALUE}" ``` ### GET `/v1/users/avatar-3d` Get Avatar 3d object for a user **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | query | `integer (int64)` | Yes | user Id for avatar | **Responses:** - `200`: OK - `400`: 4: The requested Ids are invalid, of an invalid type or missing. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://thumbnails.roblox.com/v1/users/avatar-3d?userId={VALUE}" ``` ### GET `/v1/users/avatar-bust` Get Avatar Busts for the given CSV of userIds **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userIds` | query | `array` | Yes | CSV for the userIds to get avatar headshots | | `includeBackground` | query | `boolean` | No | Whether to include a background in the thumbnail (defaults to false) | | `size` | query | `string` | No | The thumbnail size, formatted widthxheight Valid values: `48x48`, `50x50`, `60x60`, `75x75`, `100x100`, `150x150`, `180x180`, `352x352`, `420x420` | | `format` | query | `string` | No | The thumbnail format Valid values: `Png`, `Webp` | | `isCircular` | query | `boolean` | No | The circle thumbnail output parameter, true or false Valid values: `true`, `false` | **Responses:** - `200`: OK - `400`: 1: There are too many requested Ids. 2: The requested image format is invalid. Please see documentation for valid thumbnail format parameter name and values. 3: The requested size is invalid. Please see documentation for valid thumbnail size parameter name and format. 4: The requested Ids are invalid, of an invalid type or missing. 10: Circular thumbnail requests are not allowed **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://thumbnails.roblox.com/v1/users/avatar-bust?userIds={VALUE}" ``` ### GET `/v1/users/avatar-headshot` Get Avatar Headshots for the given CSV of userIds **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userIds` | query | `array` | Yes | CSV for the userIds to get avatar headshots | | `includeBackground` | query | `boolean` | No | Whether to include a background in the thumbnail (defaults to false) | | `size` | query | `string` | No | The thumbnail size, formatted widthxheight Valid values: `48x48`, `50x50`, `60x60`, `75x75`, `100x100`, `110x110`, `150x150`, `180x180`, `352x352`, `420x420`, `720x720` | | `format` | query | `string` | No | The thumbnail format Valid values: `Png`, `Jpeg`, `Webp` | | `isCircular` | query | `boolean` | No | The circle thumbnail output parameter, true or false Valid values: `true`, `false` | **Responses:** - `200`: OK - `400`: 1: There are too many requested Ids. 2: The requested image format is invalid. Please see documentation for valid thumbnail format parameter name and values. 3: The requested size is invalid. Please see documentation for valid thumbnail size parameter name and format. 4: The requested Ids are invalid, of an invalid type or missing. 10: Circular thumbnail requests are not allowed **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://thumbnails.roblox.com/v1/users/avatar-headshot?userIds={VALUE}" ``` ### GET `/v1/users/outfit-3d` Get 3d object for an outfit **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `outfitId` | query | `integer (int64)` | Yes | CSV for the userIds to get user outfits | **Responses:** - `200`: OK **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://thumbnails.roblox.com/v1/users/outfit-3d?outfitId={VALUE}" ``` ### GET `/v1/users/outfits` Get outfits for the given CSV of userOutfitIds **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userOutfitIds` | query | `array` | Yes | CSV for the userOutfitIds to get user outfits | | `includeBackground` | query | `boolean` | No | Whether to include a background in the thumbnail (defaults to false) | | `size` | query | `string` | No | The thumbnail size, formatted widthxheight Valid values: `150x150`, `420x420` | | `format` | query | `string` | No | The thumbnail format Valid values: `Png`, `Webp` | | `isCircular` | query | `boolean` | No | The circle thumbnail output parameter, true or false Valid values: `true`, `false` | **Responses:** - `200`: OK - `400`: 1: There are too many requested Ids. 2: The requested image format is invalid. Please see documentation for valid thumbnail format parameter name and values. 3: The requested size is invalid. Please see documentation for valid thumbnail size parameter name and format. 4: The requested Ids are invalid, of an invalid type or missing. 10: Circular thumbnail requests are not allowed **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://thumbnails.roblox.com/v1/users/outfits?userOutfitIds={VALUE}" ``` ### POST `/v1/batch` Returns a list of thumbnails with varying types and sizes **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `Roblox-Place-Id` | header | `integer (int64)` | No | (optional) placeid | **Request Body:** `application/json` — Type: `Roblox.Thumbnails.Apis.Models.ThumbnailBatchRequest[]` See [Roblox.Thumbnails.Apis.Models.ThumbnailBatchRequest](#roblox-thumbnails-apis-models-thumbnailbatchrequest) in Models. **Responses:** - `200`: OK - `400`: 1: There are too many requested Ids. 2: The requested image format is invalid. Please see documentation for valid thumbnail format parameter name and values. 3: The requested size is invalid. Please see documentation for valid thumbnail size parameter name and format. 4: The requested Ids are invalid, of an invalid type or missing. 7: The specified type is not supported by the batch endpoint - `403`: 9: User not authorized to use AutoGenerated or ForceAutoGenerated return policies. **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://thumbnails.roblox.com/v1/batch" \ -H "Content-Type: application/json" \ -d '[ { "requestId": "string", "targetId": 0, "token": "string", "alias": "string", "type": "Avatar", "size": "string" } ]' ``` ## Models ### Roblox.Thumbnails.Apis.Models.ThumbnailBatchRequest | Property | Type | Required | Description | |----------|------|----------|-------------| | `requestId` | `string` | No | The request id. (Generated client side, used to represent the items in the request) | | `targetId` | `integer` | No | The thumbnail target id | | `token` | `string` | No | Alternative ase token used to identify a thumbnail | | `alias` | `string` | No | Alternative string used to identify a thumbnail instead of targetId | | `type` | `string enum (19 values)` | No | The type of the thumbnails ['Avatar' = 1, 'AvatarHeadShot' = 2, 'GameIcon' = 3, 'BadgeIcon' = 4, 'GameThumbnail' = 5, 'GamePass' = 6, 'Asset' = 7, 'BundleThumbnail' = 8, 'Outfit' = 9, 'GroupIcon' = 10, 'DeveloperProduct' = 11, 'AvatarBust' = 12, 'AutoGeneratedAsset' = 13, 'PlaceIcon' = 14, 'AutoGeneratedGameIcon' = 15, 'ForceAutoGeneratedGameIcon' = 16, 'Look' = 17, 'CreatorContextAsset' = 18, 'Screenshot' = 19] Values: Avatar, AvatarHeadShot, GameIcon, BadgeIcon, GameThumbnail, GamePass, Asset, BundleThumbnail, Outfit, GroupIcon, DeveloperProduct, AvatarBust, AutoGeneratedAsset, PlaceIcon, AutoGeneratedGameIcon, ForceAutoGeneratedGameIcon, Look, CreatorContextAsset, Screenshot | | `size` | `string` | No | The thumbnail size | | `format` | `string` | No | The thumbnail format | | `isCircular` | `boolean` | No | Is the thumbnail circular shape | | `accessContext` | `string` | No | Access context for asset permission checks (only relevant for Asset thumbnail types) | | `headShape` | `string` | No | Head shape for dynamic head thumbnails (only relevant for Asset thumbnail types with dynamic head) | | `includeBackground` | `boolean` | No | Whether to include a background in avatar thumbnails (defaults to false) | --- name: "ThumbnailsResizer Api v1" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://thumbnailsresizer.roblox.com" version: "v1" endpoints: 2 auth: [cookie] --- # ThumbnailsResizer Api v1 **API Version:** v1 **Base URL:** `https://thumbnailsresizer.roblox.com` ## Endpoints ### GET `/v1/resize/{hash}/{width}/{height}/{type}/{format}/{filterType}` Resizes larger thumbnails to specified size and format **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `hash` | path | `string` | Yes | Hash of larger thumbnail | | `width` | path | `integer (int32)` | Yes | Desired width of thumbnail | | `height` | path | `integer (int32)` | Yes | Desired height of thumbnail | | `type` | path | `string` | Yes | Thumbnail Type | | `format` | path | `string` | Yes | Desired image format of the thumbnail | | `filterType` | query | `string` | Yes | E.g. is output circular | | `shouldModify` | query | `boolean` | No | | **Responses:** - `200`: OK **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://thumbnailsresizer.roblox.com/v1/resize/{HASH}/{WIDTH}/{HEIGHT}/{TYPE}/{FORMAT}/{filterType}?filterType={VALUE}" ``` ### GET `/v1/secureresize/{thumbPrint}/{hash}/{width}/{height}/{type}/{format}/{filterType}` Decrypts and Resizes larger thumbnails to specified size and format **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `thumbPrint` | path | `string` | Yes | The thumbPrint that represents the key version | | `hash` | path | `string` | Yes | Hash of larger thumbnail | | `width` | path | `integer (int32)` | Yes | Desired width of thumbnail | | `height` | path | `integer (int32)` | Yes | Desired height of thumbnail | | `type` | path | `string` | Yes | Thumbnail Type | | `format` | path | `string` | Yes | Desired image format of the thumbnail | | `filterType` | query | `string` | No | E.g. is output circular | **Responses:** - `200`: OK **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://thumbnailsresizer.roblox.com/v1/secureresize/{THUMBPRINT}/{HASH}/{WIDTH}/{HEIGHT}/{TYPE}/{FORMAT}/{filterType}" ``` --- name: "Trades Api v1" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://trades.roblox.com" version: "v1" endpoints: 10 auth: [cookie] --- # Trades Api v1 **API Version:** v1 **Base URL:** `https://trades.roblox.com` ## Endpoints ### GET `/v1/trades/{tradeId}` Gets detailed information about a trade. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `tradeId` | path | `integer (int64)` | Yes | The trade id. | **Responses:** - `200`: OK → `Roblox.Trades.Api.TradeDetailResponse` - `400`: 2: The trade cannot be found or you are not authorized to view it. - `401`: 0: Authorization has been denied for this request. 4: You are not authorized to modify this trade. - `404`: 2: The trade cannot be found or you are not authorized to view it. - `500`: 0: An unknown error occured. **Response fields** (`Roblox.Trades.Api.TradeDetailResponse`) See [Roblox.Trades.Api.TradeDetailResponse](#roblox-trades-api-tradedetailresponse) in Models. **Response example:** ```json { "offers": [ { "user": "...", "userAssets": "...", "robux": "..." } ], "id": 0, "user": { "id": 0, "name": "string", "displayName": "string" }, "created": "2024-01-01T00:00:00Z", "expiration": "2024-01-01T00:00:00Z", "isActive": false } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://trades.roblox.com/v1/trades/{TRADEID}" ``` ### GET `/v1/trades/{tradeStatusType}` Fetches a list of the authenticated user's trades. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `tradeStatusType` | path | `integer (int32)` | Yes | The trade status type. Valid values: `1`, `2`, `3`, `4` | | `limit` | query | `integer (int32)` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `string` | No | Sorted by trade creation date Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Trades.Api.TradeResponse]` - `400`: 1: Invalid trade status type. - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Trades.Api.TradeResponse]`) See [Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Trades.Api.TradeResponse]](#roblox-web-webapi-models-apipageresponse-roblox-trades-api-traderesponse-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "id": "...", "user": "...", "created": "...", "expiration": "...", "isActive": "...", "status": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://trades.roblox.com/v1/trades/{TRADESTATUSTYPE}" ``` ### GET `/v1/trades/{tradeStatusType}/count` Gets the total number of pending trades for the authenticated user. Inbound is the only accepted tradeStatusType. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `tradeStatusType` | path | `integer (int32)` | Yes | The trade status type to fetch a total count for. Valid values: `1`, `2`, `3`, `4` | **Responses:** - `200`: OK → `Roblox.Trades.Api.TradeCountResponse` - `400`: 1: Invalid trade status type. - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Trades.Api.TradeCountResponse`) See [Roblox.Trades.Api.TradeCountResponse](#roblox-trades-api-tradecountresponse) in Models. **Response example:** ```json { "count": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://trades.roblox.com/v1/trades/{TRADESTATUSTYPE}/count" ``` ### GET `/v1/trades/metadata` Gets metadata about the trade system. **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Trades.Api.TradeMetadata` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Trades.Api.TradeMetadata`) See [Roblox.Trades.Api.TradeMetadata](#roblox-trades-api-trademetadata) in Models. **Response example:** ```json { "maxItemsPerSide": 0, "minValueRatio": 0, "tradeSystemMaxRobuxPercent": 0, "tradeSystemRobuxFee": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://trades.roblox.com/v1/trades/metadata" ``` ### GET `/v1/users/{userId}/can-trade-with` Returns whether you can trade with another user. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The other user's id. | **Responses:** - `200`: OK → `Roblox.Trades.Api.CanTradeResponse` - `400`: 10: Invalid trade partner. See field for whether the invalid partner is the sender or receiver. - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Trades.Api.CanTradeResponse`) See [Roblox.Trades.Api.CanTradeResponse](#roblox-trades-api-cantraderesponse) in Models. **Response example:** ```json { "canTrade": false, "status": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://trades.roblox.com/v1/users/{USERID}/can-trade-with" ``` ### POST `/v1/trades/{tradeId}/accept` Accepts a trade. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `tradeId` | path | `integer (int64)` | Yes | The trade id. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 2: The trade cannot be found or you are not authorized to view it. 3: The trade is inactive. 4: You are not authorized to modify this trade. 6: Trade needs to be confirmed by the other party. 6: Trade needs to be confirmed by the other party. 7: The user cannot trade. See field for whether the user who cannot trade is the sender or receiver. 23: The trade reaches Two Step Verification thresholds and the user has not verified in the past time threshold. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `503`: 5: Trading system is unavailable **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://trades.roblox.com/v1/trades/{TRADEID}/accept" ``` ### POST `/v1/trades/{tradeId}/counter` Counters a trade. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `tradeId` | path | `integer (int64)` | Yes | The trade id. | **Request Body:** `application/json` — Type: `Roblox.Trades.Api.TradeRequest` See [Roblox.Trades.Api.TradeRequest](#roblox-trades-api-traderequest) in Models. **Request example:** ```json { "offers": [ { "userId": "...", "userAssetIds": "...", "robux": "..." } ] } ``` **Responses:** - `200`: OK → `Roblox.Trades.Api.NewTradeResponse` - `400`: 2: The trade cannot be found or you are not authorized to view it. 4: You are not authorized to modify this trade. 7: The user cannot trade. See field for whether the user who cannot trade is the sender or receiver. 8: The trade request should include offers. 9: Invalid number of trade offers. 10: Invalid trade partner. See field for whether the invalid partner is the sender or receiver. 11: Cannot add negative Robux amounts to a trade. 12: One or more userAssets are invalid. See fieldData for details. 13: Invalid number of userAssets in one side of the trade. 15: The trade is unbalanced. 16: Trade value ratio is not sufficient. 17: You have insufficient Robux to make this offer. 18: Too many Robux in one side of the offer. See field for whether the side is the sender or receiver. 19: Unknown error while processing the trade. 21: Cannot trade with yourself. 22: User's privacy settings are too strict to allow trading. See field for whether the user is the sender or receiver. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `429`: 14: You are sending too many trade requests. Please slow down and try again later. - `502`: 0: An unknown error occured. - `503`: 5: Trading system is unavailable **Response fields** (`Roblox.Trades.Api.NewTradeResponse`) See [Roblox.Trades.Api.NewTradeResponse](#roblox-trades-api-newtraderesponse) in Models. **Response example:** ```json { "id": 0 } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://trades.roblox.com/v1/trades/{TRADEID}/counter" \ -H "Content-Type: application/json" \ -d '{ "offers": [ { "userId": "...", "userAssetIds": "...", "robux": "..." } ] }' ``` ### POST `/v1/trades/{tradeId}/decline` Declines a trade. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `tradeId` | path | `integer (int64)` | Yes | The trade id. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 2: The trade cannot be found or you are not authorized to view it. 3: The trade is inactive. 4: You are not authorized to modify this trade. 7: The user cannot trade. See field for whether the user who cannot trade is the sender or receiver. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `503`: 5: Trading system is unavailable **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://trades.roblox.com/v1/trades/{TRADEID}/decline" ``` ### POST `/v1/trades/expire-outdated` Deprecated. TradeSession are automatically set to expire while the inbound/outbound trades are fetched. Expires Outdated Inbound Trades for User **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://trades.roblox.com/v1/trades/expire-outdated" ``` ### POST `/v1/trades/send` Sends a trade. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Trades.Api.TradeRequest` See [Roblox.Trades.Api.TradeRequest](#roblox-trades-api-traderequest) in Models. **Request example:** ```json { "offers": [ { "userId": "...", "userAssetIds": "...", "robux": "..." } ] } ``` **Responses:** - `200`: OK → `Roblox.Trades.Api.NewTradeResponse` - `400`: 7: The user cannot trade. See field for whether the user who cannot trade is the sender or receiver. 8: The trade request should include offers. 9: Invalid number of trade offers. 10: Invalid trade partner. See field for whether the invalid partner is the sender or receiver. 11: Cannot add negative Robux amounts to a trade. 12: One or more userAssets are invalid. See fieldData for details. 13: Invalid number of userAssets in one side of the trade. 15: The trade is unbalanced. 16: Trade value ratio is not sufficient. 17: You have insufficient Robux to make this offer. 18: Too many Robux in one side of the offer. See field for whether the side is the sender or receiver. 19: Unknown error while processing the trade. 21: Cannot trade with yourself. 22: User's privacy settings are too strict to allow trading. See field for whether the user is the sender or receiver. 23: The trade reaches Two Step Verification thresholds and the user has not verified in the past time threshold. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `429`: 14: You are sending too many trade requests. Please slow down and try again later. - `502`: 0: An unknown error occured. - `503`: 5: Trading system is unavailable **Response fields** (`Roblox.Trades.Api.NewTradeResponse`) See [Roblox.Trades.Api.NewTradeResponse](#roblox-trades-api-newtraderesponse) in Models. **Response example:** ```json { "id": 0 } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://trades.roblox.com/v1/trades/send" \ -H "Content-Type: application/json" \ -d '{ "offers": [ { "userId": "...", "userAssetIds": "...", "robux": "..." } ] }' ``` ## Models ### Roblox.Trades.Api.CanTradeResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `canTrade` | `boolean` | No | Returns true if you can trade with the given user. | | `status` | `integer enum (8 values)` | No | If you can't trade with a user, status explains why you can't trade with them. ['Unknown' = 0, 'CanTrade' = 1, 'CannotTradeWithSelf' = 2, 'SenderCannotTrade' = 3, 'ReceiverCannotTrade' = 4, 'SenderPrivacyTooStrict' = 5, 'UsersCannotTrade' = 6, 'TradeAccepterNeedsFriction' = 7] Values: 0, 1, 2, 3, 4, 5, 6, 7 | ### Roblox.Trades.Api.NewTradeResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | The trade id. | ### Roblox.Trades.Api.TradeCountResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `count` | `integer` | No | | ### Roblox.Trades.Api.TradeDetailResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `offers` | `Roblox.Trades.Api.TradeOfferResponse[]` | No | | | `id` | `integer` | No | | | `user` | `Roblox.Web.Responses.Users.SkinnyUserResponse` | No | | | `created` | `string` | No | | | `expiration` | `string` | No | | | `isActive` | `boolean` | No | | | `status` | `string enum (11 values)` | No | ['Unknown' = 1, 'Open' = 2, 'Pending' = 3, 'Completed' = 4, 'Expired' = 5, 'Declined' = 6, 'RejectedDueToError' = 7, 'Countered' = 8, 'Processing' = 9, 'InterventionRequired' = 10, 'TwoStepVerificationRequired' = 11] Values: Unknown, Open, Pending, Completed, Expired, Declined, RejectedDueToError, Countered, Processing, InterventionRequired, TwoStepVerificationRequired | ### Roblox.Trades.Api.TradeMetadata | Property | Type | Required | Description | |----------|------|----------|-------------| | `maxItemsPerSide` | `integer` | No | | | `minValueRatio` | `number` | No | | | `tradeSystemMaxRobuxPercent` | `number` | No | | | `tradeSystemRobuxFee` | `number` | No | | ### Roblox.Trades.Api.TradeOfferRequest | Property | Type | Required | Description | |----------|------|----------|-------------| | `userId` | `integer` | No | | | `userAssetIds` | `integer[]` | No | | | `robux` | `integer` | No | | ### Roblox.Trades.Api.TradeOfferResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `user` | `Roblox.Web.Responses.Users.SkinnyUserResponse` | No | | | `userAssets` | `Roblox.Trades.Api.UserAssetResponse[]` | No | | | `robux` | `integer` | No | | ### Roblox.Trades.Api.TradeRequest | Property | Type | Required | Description | |----------|------|----------|-------------| | `offers` | `Roblox.Trades.Api.TradeOfferRequest[]` | No | | ### Roblox.Trades.Api.TradeResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | | | `user` | `Roblox.Web.Responses.Users.SkinnyUserResponse` | No | | | `created` | `string` | No | | | `expiration` | `string` | No | | | `isActive` | `boolean` | No | | | `status` | `string enum (11 values)` | No | ['Unknown' = 1, 'Open' = 2, 'Pending' = 3, 'Completed' = 4, 'Expired' = 5, 'Declined' = 6, 'RejectedDueToError' = 7, 'Countered' = 8, 'Processing' = 9, 'InterventionRequired' = 10, 'TwoStepVerificationRequired' = 11] Values: Unknown, Open, Pending, Completed, Expired, Declined, RejectedDueToError, Countered, Processing, InterventionRequired, TwoStepVerificationRequired | ### Roblox.Trades.Api.UserAssetResponse A model containing information about a UserAsset. | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | The user asset id | | `serialNumber` | `integer` | No | The serial number of the user asset | | `assetId` | `integer` | No | The asset id of the user asset | | `name` | `string` | No | The asset name of the asset | | `recentAveragePrice` | `integer` | No | The recent average price of the asset | | `originalPrice` | `integer` | No | The original price of the asset | | `assetStock` | `integer` | No | The asset stock. | | `membershipType` | `0 \| 1 \| 2 \| 3 \| 4` | No | The minimum MembershipType required to own the userAsset. ['None' = 0, 'BC' = 1, 'TBC' = 2, 'OBC' = 3, 'RobloxPremium' = 4] | ### Roblox.Web.Responses.Users.SkinnyUserResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | | | `name` | `string` | No | | | `displayName` | `string` | No | | ### Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Trades.Api.TradeResponse] | Property | Type | Required | Description | |----------|------|----------|-------------| | `previousPageCursor` | `string` | No | | | `nextPageCursor` | `string` | No | | | `data` | `Roblox.Trades.Api.TradeResponse[]` | No | | --- name: "Trades Api v2" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://trades.roblox.com" version: "v2" endpoints: 6 auth: [cookie] --- # Trades Api v2 **API Version:** v2 **Base URL:** `https://trades.roblox.com` ## Endpoints ### GET `/v2/trades/{tradeId}` Gets the details of a trade. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `tradeId` | path | `integer (int64)` | Yes | The id of the trade. | **Responses:** - `200`: OK → `Roblox.Trades.Api.Models.V2.TradeDetailsResponse` - `400`: 0: An unknown error occured. - `401`: 0: Authorization has been denied for this request. 4: You are not authorized to modify this trade. - `403`: 4: You are not authorized to modify this trade. - `404`: 0: An unknown error occured. **Response fields** (`Roblox.Trades.Api.Models.V2.TradeDetailsResponse`) See [Roblox.Trades.Api.Models.V2.TradeDetailsResponse](#roblox-trades-api-models-v2-tradedetailsresponse) in Models. **Response example:** ```json { "tradeId": 0, "status": "Unknown", "participantAOffer": { "user": { "id": "...", "name": "...", "displayName": "..." }, "robux": 0, "items": [ "..." ] }, "participantBOffer": { "user": { "id": "...", "name": "...", "displayName": "..." }, "robux": 0, "items": [ "..." ] } } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://trades.roblox.com/v2/trades/{TRADEID}" ``` ### GET `/v2/users/{userId}/can-trade-with` Checks if the user can trade with a specific user. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | | **Responses:** - `200`: OK → `Roblox.Trades.Api.Models.V2.CanTradeWithResponse` - `401`: 0: Authorization has been denied for this request. 4: You are not authorized to modify this trade. **Response fields** (`Roblox.Trades.Api.Models.V2.CanTradeWithResponse`) See [Roblox.Trades.Api.Models.V2.CanTradeWithResponse](#roblox-trades-api-models-v2-cantradewithresponse) in Models. **Response example:** ```json { "userId": 0, "targetUserId": 0, "canTrade": false, "mutualTradeEligibility": "Unknown" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://trades.roblox.com/v2/users/{USERID}/can-trade-with" ``` ### GET `/v2/users/{userId}/tradableItems` Gets tradable items for a user. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The id of the user. | | `search` | query | `string` | No | Optional search query to filter items by. | | `itemTargetTypes` | query | `array` | No | Optional list of item target types to filter by. | | `sortBy` | query | `string` | No | The key to sort tradable items by. Valid values: `Unknown`, `CreationTime`, `AcquisitionTime` | | `sortOrder` | query | `integer (int32)` | No | The sort order for the tradable items. Valid values: `0`, `1`, `2` | | `limit` | query | `integer (int32)` | No | The maximum number of items to return. | | `cursor` | query | `string` | No | The pagination cursor. | **Responses:** - `200`: OK → `Roblox.Trades.Api.Models.V2.GetUserTradableItemsResponse` - `400`: 25: The cursor provided is invalid. - `401`: 0: Authorization has been denied for this request. 4: You are not authorized to modify this trade. - `403`: 4: You are not authorized to modify this trade. - `404`: 0: An unknown error occured. **Response fields** (`Roblox.Trades.Api.Models.V2.GetUserTradableItemsResponse`) See [Roblox.Trades.Api.Models.V2.GetUserTradableItemsResponse](#roblox-trades-api-models-v2-getusertradableitemsresponse) in Models. **Response example:** ```json { "userId": 0, "items": [ { "collectibleItemId": "...", "itemTarget": "...", "itemName": "...", "originalPrice": "...", "recentAveragePrice": "...", "assetStock": "..." } ], "nextPageCursor": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://trades.roblox.com/v2/users/{USERID}/tradableItems" ``` ### GET `/v2/users/me/can-trade` Checks if the calling user can trade with others. **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Trades.Api.Models.V2.CanTradeResponse` - `401`: 0: Authorization has been denied for this request. 4: You are not authorized to modify this trade. **Response fields** (`Roblox.Trades.Api.Models.V2.CanTradeResponse`) See [Roblox.Trades.Api.Models.V2.CanTradeResponse](#roblox-trades-api-models-v2-cantraderesponse) in Models. **Response example:** ```json { "userId": 0, "canTrade": false, "tradeEligibility": "Unknown" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://trades.roblox.com/v2/users/me/can-trade" ``` ### POST `/v2/trades/{tradeId}/counter` Counters an existing trade. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `tradeId` | path | `integer (int64)` | Yes | | **Request Body:** `application/json` — Type: `Roblox.Trades.Api.Models.V2.TradeRequest` See [Roblox.Trades.Api.Models.V2.TradeRequest](#roblox-trades-api-models-v2-traderequest) in Models. **Request example:** ```json { "senderOffer": { "userId": 0, "robux": 0, "collectibleItemInstanceIds": [ "..." ] }, "recipientOffer": { "userId": 0, "robux": 0, "collectibleItemInstanceIds": [ "..." ] } } ``` **Responses:** - `200`: OK → `Roblox.Trades.Api.Models.V2.NewTradeResponse` - `401`: 0: Authorization has been denied for this request. 4: You are not authorized to modify this trade. - `403`: 0: Token Validation Failed - `404`: 0: An unknown error occured. **Response fields** (`Roblox.Trades.Api.Models.V2.NewTradeResponse`) See [Roblox.Trades.Api.Models.V2.NewTradeResponse](#roblox-trades-api-models-v2-newtraderesponse) in Models. **Response example:** ```json { "tradeId": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://trades.roblox.com/v2/trades/{TRADEID}/counter" \ -H "Content-Type: application/json" \ -d '{ "senderOffer": { "userId": 0, "robux": 0, "collectibleItemInstanceIds": [ "..." ] }, "recipientOffer": { "userId": 0, "robux": 0, "collectibleItemInstanceIds": [ "..." ] } }' ``` ### POST `/v2/trades/send` Sends a new trade. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Trades.Api.Models.V2.TradeRequest` See [Roblox.Trades.Api.Models.V2.TradeRequest](#roblox-trades-api-models-v2-traderequest) in Models. **Request example:** ```json { "senderOffer": { "userId": 0, "robux": 0, "collectibleItemInstanceIds": [ "..." ] }, "recipientOffer": { "userId": 0, "robux": 0, "collectibleItemInstanceIds": [ "..." ] } } ``` **Responses:** - `200`: OK → `Roblox.Trades.Api.Models.V2.NewTradeResponse` - `401`: 0: Authorization has been denied for this request. 4: You are not authorized to modify this trade. - `403`: 0: Token Validation Failed - `404`: 0: An unknown error occured. **Response fields** (`Roblox.Trades.Api.Models.V2.NewTradeResponse`) See [Roblox.Trades.Api.Models.V2.NewTradeResponse](#roblox-trades-api-models-v2-newtraderesponse) in Models. **Response example:** ```json { "tradeId": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://trades.roblox.com/v2/trades/send" \ -H "Content-Type: application/json" \ -d '{ "senderOffer": { "userId": 0, "robux": 0, "collectibleItemInstanceIds": [ "..." ] }, "recipientOffer": { "userId": 0, "robux": 0, "collectibleItemInstanceIds": [ "..." ] } }' ``` ## Models ### Roblox.Trades.Api.Models.V2.CanTradeResponse The response for the CanTrade endpoint. | Property | Type | Required | Description | |----------|------|----------|-------------| | `userId` | `integer` | No | The ID of the user. | | `canTrade` | `boolean` | No | Whether the user can trade or not. | | `tradeEligibility` | `string enum (7 values)` | No | The trade eligibility status of the user. ['Unknown' = 0, 'Eligible' = 1, 'IneligibleTradeSystemDisabled' = 2, 'IneligibleCannotTradeWithRoblox' = 3, 'IneligibleUserNotFound' = 4, 'IneligibleMissingPremiumMembership' = 5, 'IneligibleLegalOrRegulatoryRestrictions' = 6] Values: Unknown, Eligible, IneligibleTradeSystemDisabled, IneligibleCannotTradeWithRoblox, IneligibleUserNotFound, IneligibleMissingPremiumMembership, IneligibleLegalOrRegulatoryRestrictions | ### Roblox.Trades.Api.Models.V2.CanTradeWithResponse The response for the CanTradeWith endpoint. | Property | Type | Required | Description | |----------|------|----------|-------------| | `userId` | `integer` | No | The ID of the user. | | `targetUserId` | `integer` | No | The ID of the target user. | | `canTrade` | `boolean` | No | Whether the user can trade with the target user or not. | | `mutualTradeEligibility` | `string enum (6 values)` | No | The mutual trade eligibility status between the two users. ['Unknown' = 0, 'Eligible' = 1, 'CallingUserIneligible' = 2, 'TargetUserIneligible' = 3, 'CannotTradeWithSelf' = 4, 'CallingUserPrivacySettingsRestricted' = 5] Values: Unknown, Eligible, CallingUserIneligible, TargetUserIneligible, CannotTradeWithSelf, CallingUserPrivacySettingsRestricted | ### Roblox.Trades.Api.Models.V2.GetUserTradableItemsResponse The response for the GetUserTradableItems endpoint. | Property | Type | Required | Description | |----------|------|----------|-------------| | `userId` | `integer` | No | The ID of the user. | | `items` | `Roblox.Trades.Api.Models.V2.TradableItem[]` | No | The items that the user can trade. | | `nextPageCursor` | `string` | No | The cursor for the next page of items. | ### Roblox.Trades.Api.Models.V2.ItemTarget The underlying of a tradable item. | Property | Type | Required | Description | |----------|------|----------|-------------| | `itemType` | `Unknown \| Asset \| Bundle` | No | The type of the underlying. ['Unknown' = 0, 'Asset' = 1, 'Bundle' = 2] | | `targetId` | `string` | No | The id of the underlying. | ### Roblox.Trades.Api.Models.V2.NewTradeResponse Represents a newly created trade. | Property | Type | Required | Description | |----------|------|----------|-------------| | `tradeId` | `integer` | No | The ID of the trade. | ### Roblox.Trades.Api.Models.V2.TradableItem A tradable item. | Property | Type | Required | Description | |----------|------|----------|-------------| | `collectibleItemId` | `string` | No | The collectible item id. | | `itemTarget` | `Roblox.Trades.Api.Models.V2.ItemTarget` | No | | | `itemName` | `string` | No | The name of the item. | | `originalPrice` | `integer` | No | The original price of the item. | | `recentAveragePrice` | `integer` | No | The recent average price of the item. | | `assetStock` | `integer` | No | Total quantity of the item. | | `instances` | `Roblox.Trades.Api.Models.V2.TradableItemInstance[]` | No | | ### Roblox.Trades.Api.Models.V2.TradableItemInstance A tradable item instance. | Property | Type | Required | Description | |----------|------|----------|-------------| | `collectibleItemInstanceId` | `string` | No | The collectible item instance id. | | `itemTarget` | `Roblox.Trades.Api.Models.V2.ItemTarget` | No | | | `itemName` | `string` | No | The name of the item. | | `serialNumber` | `integer` | No | The serial number of the item if it is LimitedUnique. | | `originalPrice` | `integer` | No | The original price of the item. | | `recentAveragePrice` | `integer` | No | The recent average price of the item. | | `assetStock` | `integer` | No | Total quantity of the item. | | `isOnHold` | `boolean` | No | Whether the item is on hold. | ### Roblox.Trades.Api.Models.V2.TradeDetailsResponse The response for the TradeDetailsV2 endpoint. | Property | Type | Required | Description | |----------|------|----------|-------------| | `tradeId` | `integer` | No | The ID of the trade. | | `status` | `string enum (11 values)` | No | The status of the trade. ['Unknown' = 1, 'Open' = 2, 'Pending' = 3, 'Completed' = 4, 'Expired' = 5, 'Declined' = 6, 'RejectedDueToError' = 7, 'Countered' = 8, 'Processing' = 9, 'InterventionRequired' = 10, 'TwoStepVerificationRequired' = 11] Values: Unknown, Open, Pending, Completed, Expired, Declined, RejectedDueToError, Countered, Processing, InterventionRequired, TwoStepVerificationRequired | | `participantAOffer` | `Roblox.Trades.Api.Models.V2.TradeOffer` | No | | | `participantBOffer` | `Roblox.Trades.Api.Models.V2.TradeOffer` | No | | ### Roblox.Trades.Api.Models.V2.TradeOffer Represents a trade offer. | Property | Type | Required | Description | |----------|------|----------|-------------| | `user` | `Roblox.Web.Responses.Users.SkinnyUserResponse` | No | | | `robux` | `integer` | No | The amount of Robux in the trade offer. | | `items` | `Roblox.Trades.Api.Models.V2.TradableItemInstance[]` | No | The items in the trade offer. | ### Roblox.Trades.Api.Models.V2.TradeOfferRequest Represents a trade offer. | Property | Type | Required | Description | |----------|------|----------|-------------| | `userId` | `integer` | No | The user ID of the offer. | | `robux` | `integer` | No | The amount of Robux in the trade offer. | | `collectibleItemInstanceIds` | `string[]` | No | List of items in the trade offer. | ### Roblox.Trades.Api.Models.V2.TradeRequest Represents a trade request. The calling user must be either participant A or B in the trade. | Property | Type | Required | Description | |----------|------|----------|-------------| | `senderOffer` | `Roblox.Trades.Api.Models.V2.TradeOfferRequest` | No | | | `recipientOffer` | `Roblox.Trades.Api.Models.V2.TradeOfferRequest` | No | | ### Roblox.Web.Responses.Users.SkinnyUserResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | | | `name` | `string` | No | | | `displayName` | `string` | No | | --- name: "TranslationRoles Api v1" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://translationroles.roblox.com" version: "v1" endpoints: 4 auth: [cookie] --- # TranslationRoles Api v1 **API Version:** v1 **Base URL:** `https://translationroles.roblox.com` ## Endpoints ### GET `/v1/game-localization-roles/games/{gameId}/current-user/roles` Retrieves the list of roles granted to current logged-in user **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer (int64)` | Yes | The id of the game | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[System.String]` - `400`: 3: Invalid game id - `401`: 0: Authorization has been denied for this request. - `503`: 2: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[System.String]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[System.String]](#roblox-web-webapi-models-apiarrayresponse-system-string-) in Models. **Response example:** ```json { "data": [ "string" ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://translationroles.roblox.com/v1/game-localization-roles/games/{GAMEID}/current-user/roles" ``` ### GET `/v1/game-localization-roles/games/{gameId}/roles/{role}/assignees` Gets list of users assigned a specific role in a game. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer (int64)` | Yes | The id of the game | | `role` | path | `string` | Yes | The Roblox.GameLocalization.Client.GameLocalizationRoles.GameLocalizationRoleType Valid values: `translator` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.TranslationRoles.Api.Assignee]` - `400`: 3: Invalid game id - `401`: 0: Authorization has been denied for this request. - `403`: 1: You must be authorized to use this endpoint - `503`: 2: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.TranslationRoles.Api.Assignee]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.TranslationRoles.Api.Assignee]](#roblox-web-webapi-models-apiarrayresponse-roblox-translationroles-api-assignee-) in Models. **Response example:** ```json { "data": [ { "id": "...", "name": "...", "type": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://translationroles.roblox.com/v1/game-localization-roles/games/{GAMEID}/roles/{ROLE}/assignees" ``` ### GET `/v1/game-localization-roles/roles/{role}/current-user` Gets the list of games and associated role assignment info for the requested user and role. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `role` | path | `string` | Yes | The Roblox.GameLocalization.Client.GameLocalizationRoles.GameLocalizationRoleType Valid values: `translator` | | `exclusiveStartKey` | query | `string` | No | Part of pagination. Last primary key of returned items in previous operation. | | `pageSize` | query | `integer (int32)` | No | Part of pagination. Maximum number of items that might be returned in the page. | | `groupId` | query | `integer (int64)` | No | Optional seleted groupId of resources requested for the user and role. | **Responses:** - `200`: OK → `Roblox.TranslationRoles.Api.GetGameLocalizationRoleAssignmentsForUserResponse` - `400`: 10: Invalid page size 11: Maximum page size exceeded 12: Invalid exclusive start key - `401`: 0: Authorization has been denied for this request. - `500`: 0: An unknown error occurred - `503`: 2: Feature is disabled **Response fields** (`Roblox.TranslationRoles.Api.GetGameLocalizationRoleAssignmentsForUserResponse`) See [Roblox.TranslationRoles.Api.GetGameLocalizationRoleAssignmentsForUserResponse](#roblox-translationroles-api-getgamelocalizationroleassignmentsforuserresponse) in Models. **Response example:** ```json { "games": [ { "gameId": "...", "assignee": "..." } ], "previousPageCursor": "string", "nextPageCursor": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://translationroles.roblox.com/v1/game-localization-roles/roles/{ROLE}/current-user" ``` ### PATCH `/v1/game-localization-roles/games/{gameId}` Assigns or revokes a role **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `gameId` | path | `integer (int64)` | Yes | The id of the game | **Request Body:** `application/json` — Type: `Roblox.TranslationRoles.Api.UpdateRoleRequest` See [Roblox.TranslationRoles.Api.UpdateRoleRequest](#roblox-translationroles-api-updaterolerequest) in Models. **Request example:** ```json { "assigneeId": 0, "assigneeType": "user", "role": "translator", "revoke": false } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 3: Invalid game id 4: Invalid assignee id 6: Request body can't be null 7: The role you are assigning has reached max limit - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 1: You must be authorized to use this endpoint - `429`: 5: Too many attempts. Please try again later. - `503`: 2: Feature is disabled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://translationroles.roblox.com/v1/game-localization-roles/games/{GAMEID}" \ -H "Content-Type: application/json" \ -d '{ "assigneeId": 0, "assigneeType": "user", "role": "translator", "revoke": false }' ``` ## Models ### Roblox.GameLocalization.Client.GameLocalizationRoles.Assignee | Property | Type | Required | Description | |----------|------|----------|-------------| | `assigneeType` | `user \| group \| groupRole` | No | ['User' = 0, 'Group' = 1, 'GroupRole' = 2] | | `id` | `integer` | No | | ### Roblox.GameLocalization.Client.GameLocalizationRoles.GameLocalizationRoleAssignment | Property | Type | Required | Description | |----------|------|----------|-------------| | `gameId` | `integer` | No | | | `assignee` | `Roblox.GameLocalization.Client.GameLocalizationRoles.Assignee` | No | | ### Roblox.TranslationRoles.Api.Assignee | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | | | `name` | `string` | No | | | `type` | `user \| group \| groupRole` | No | ['User' = 0, 'Group' = 1, 'GroupRole' = 2] | ### Roblox.TranslationRoles.Api.GetGameLocalizationRoleAssignmentsForUserResponse Response model containing a list of games and associated role assignment info for the requested user and role. | Property | Type | Required | Description | |----------|------|----------|-------------| | `games` | `Roblox.GameLocalization.Client.GameLocalizationRoles.GameLocalizationRoleAssignment[]` | No | List of games with associated role assignment info. | | `previousPageCursor` | `string` | No | Cursor which can be used for navigating to previous page. | | `nextPageCursor` | `string` | No | Cursor which can be used for navigating to next page. | ### Roblox.TranslationRoles.Api.UpdateRoleRequest The request body for update role endpoints | Property | Type | Required | Description | |----------|------|----------|-------------| | `assigneeId` | `integer` | No | The id of the assignee | | `assigneeType` | `user \| group \| groupRole` | No | The type of the assignee ['User' = 0, 'Group' = 1, 'GroupRole' = 2] | | `role` | `translator` | No | The role to be assigned or revoked ['Translator' = 0] | | `revoke` | `boolean` | No | To assign or to revoke | ### Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.TranslationRoles.Api.Assignee] | Property | Type | Required | Description | |----------|------|----------|-------------| | `data` | `Roblox.TranslationRoles.Api.Assignee[]` | No | | ### Roblox.Web.WebAPI.Models.ApiArrayResponse[System.String] | Property | Type | Required | Description | |----------|------|----------|-------------| | `data` | `string[]` | No | | --- name: "TwoStepVerification Api v1" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://twostepverification.roblox.com" version: "v1" endpoints: 30 auth: [cookie] --- # TwoStepVerification Api v1 **API Version:** v1 **Base URL:** `https://twostepverification.roblox.com` ## Endpoints ### GET `/v1/metadata` Gets two step verification system metadata. The metadata endpoint takes in optional request parameters to output additional context for when the user is unauthenticated but attempting to login with two step verification. When supplied, all three request parameters must be sent and match up. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | query | `integer (int64)` | No | The user ID. | | `challengeId` | query | `string` | No | The active two step verification challenge ID if there is one. | | `actionType` | query | `integer (int32)` | No | The Roblox.TwoStepVerification.Client.TwoStepVerificationActionType associated with the challenge. Valid values: `0`, `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8` | **Responses:** - `200`: OK → `Roblox.TwoStepVerification.Api.MetadataResponse` **Response fields** (`Roblox.TwoStepVerification.Api.MetadataResponse`) See [Roblox.TwoStepVerification.Api.MetadataResponse](#roblox-twostepverification-api-metadataresponse) in Models. **Response example:** ```json { "twoStepVerificationEnabled": false, "authenticatorQrCodeSize": "string", "emailCodeLength": 0, "authenticatorCodeLength": 0, "authenticatorHelpSiteAddress": "string", "isPasswordRequiredForEnablingAuthenticator": false } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/metadata" ``` ### GET `/v1/users/{userId}/configuration` Gets two step verification configuration for the specified user. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The Id of the user to get the configuration for. | | `challengeId` | query | `string` | No | The active challenge for the user (as an alternative when the user is unauthenticated). | | `actionType` | query | `integer (int32)` | No | The action type the challengeId is associated with. Valid values: `0`, `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8` | **Responses:** - `200`: OK → `Roblox.TwoStepVerification.Api.UserConfiguration` - `400`: 1: Invalid challenge ID. - `403`: 2: The user ID is invalid. **Response fields** (`Roblox.TwoStepVerification.Api.UserConfiguration`) See [Roblox.TwoStepVerification.Api.UserConfiguration](#roblox-twostepverification-api-userconfiguration) in Models. **Response example:** ```json { "primaryMediaType": 0, "methods": [ { "mediaType": "...", "enabled": "...", "updated": "..." } ] } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/configuration" ``` ### GET `/v1/users/{userId}/recovery-codes` Gets the current status of recovery codes for a user. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user ID. | **Responses:** - `200`: OK → `Roblox.TwoStepVerification.Api.RecoveryCodesStatusResponse` - `400`: 2: The user ID is invalid. - `401`: 0: Authorization has been denied for this request. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.TwoStepVerification.Api.RecoveryCodesStatusResponse`) See [Roblox.TwoStepVerification.Api.RecoveryCodesStatusResponse](#roblox-twostepverification-api-recoverycodesstatusresponse) in Models. **Response example:** ```json { "activeCount": 0, "created": "2024-01-01T00:00:00Z" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/recovery-codes" ``` ### POST `/v1/users/{userId}/challenges/authenticator/verify` Verifies a two step verification challenge code via authenticator app. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user ID. | **Request Body:** `application/json` — Type: `Roblox.TwoStepVerification.Api.VerifyCodeRequest` See [Roblox.TwoStepVerification.Api.VerifyCodeRequest](#roblox-twostepverification-api-verifycoderequest) in Models. **Request example:** ```json { "challengeId": "string", "actionType": 0, "code": "string" } ``` **Responses:** - `200`: OK → `Roblox.TwoStepVerification.Api.VerifyCodeResponse` - `400`: 1: Invalid challenge ID. 2: The user ID is invalid. 10: The two step verification challenge code is invalid. - `403`: 0: Token Validation Failed - `429`: 5: Too many requests. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.TwoStepVerification.Api.VerifyCodeResponse`) See [Roblox.TwoStepVerification.Api.VerifyCodeResponse](#roblox-twostepverification-api-verifycoderesponse) in Models. **Response example:** ```json { "verificationToken": "string" } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/challenges/authenticator/verify" \ -H "Content-Type: application/json" \ -d '{ "challengeId": "string", "actionType": 0, "code": "string" }' ``` ### POST `/v1/users/{userId}/challenges/cross-device/retract` Reverts a user's dialog state from ACTIVE to PENDING. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user ID. | **Request Body:** `application/json` — Type: `Roblox.TwoStepVerification.Api.RetractDialogRequest` See [Roblox.TwoStepVerification.Api.RetractDialogRequest](#roblox-twostepverification-api-retractdialogrequest) in Models. **Request example:** ```json { "challengeId": "string", "actionType": 0 } ``` **Responses:** - `200`: OK → `Roblox.TwoStepVerification.Api.RetractDialogResponse` - `400`: 1: Invalid challenge ID. 2: The user ID is invalid. - `403`: 0: Token Validation Failed 19: Challenge denied. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.TwoStepVerification.Api.RetractDialogResponse`) See [Roblox.TwoStepVerification.Api.RetractDialogResponse](#roblox-twostepverification-api-retractdialogresponse) in Models. **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/challenges/cross-device/retract" \ -H "Content-Type: application/json" \ -d '{ "challengeId": "string", "actionType": 0 }' ``` ### POST `/v1/users/{userId}/challenges/cross-device/retry` Retry a Cross Device two step verification approval. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user ID. | **Request Body:** `application/json` — Type: `Roblox.TwoStepVerification.Api.RetryApprovalRequest` See [Roblox.TwoStepVerification.Api.RetryApprovalRequest](#roblox-twostepverification-api-retryapprovalrequest) in Models. **Request example:** ```json { "challengeId": "string", "actionType": 0 } ``` **Responses:** - `200`: OK → `Roblox.TwoStepVerification.Api.RetryApprovalResponse` - `400`: 1: Invalid challenge ID. 2: The user ID is invalid. - `403`: 0: Token Validation Failed 19: Challenge denied. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.TwoStepVerification.Api.RetryApprovalResponse`) See [Roblox.TwoStepVerification.Api.RetryApprovalResponse](#roblox-twostepverification-api-retryapprovalresponse) in Models. **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/challenges/cross-device/retry" \ -H "Content-Type: application/json" \ -d '{ "challengeId": "string", "actionType": 0 }' ``` ### POST `/v1/users/{userId}/challenges/cross-device/verify` Verifies a two step verification approval via Cross Device. Cross Device approval does not use a verification code. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user ID. | **Request Body:** `application/json` — Type: `Roblox.TwoStepVerification.Api.VerifyApprovalRequest` See [Roblox.TwoStepVerification.Api.VerifyApprovalRequest](#roblox-twostepverification-api-verifyapprovalrequest) in Models. **Request example:** ```json { "challengeId": "string", "actionType": 0 } ``` **Responses:** - `200`: OK → `Roblox.TwoStepVerification.Api.VerifyApprovalResponse` - `400`: 0: An unknown error occurred with the request. 1: Invalid challenge ID. 2: The user ID is invalid. - `403`: 0: Token Validation Failed 19: Challenge denied. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.TwoStepVerification.Api.VerifyApprovalResponse`) See [Roblox.TwoStepVerification.Api.VerifyApprovalResponse](#roblox-twostepverification-api-verifyapprovalresponse) in Models. **Response example:** ```json { "verificationToken": "string" } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/challenges/cross-device/verify" \ -H "Content-Type: application/json" \ -d '{ "challengeId": "string", "actionType": 0 }' ``` ### POST `/v1/users/{userId}/challenges/email/send-code` Sends a two step verification challenge code via email. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user ID. | **Request Body:** `application/json` — Type: `Roblox.TwoStepVerification.Api.SendCodeRequest` See [Roblox.TwoStepVerification.Api.SendCodeRequest](#roblox-twostepverification-api-sendcoderequest) in Models. **Request example:** ```json { "challengeId": "string", "actionType": 0 } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: Invalid challenge ID. - `403`: 0: Token Validation Failed 2: The user ID is invalid. - `429`: 5: Too many requests. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/challenges/email/send-code" \ -H "Content-Type: application/json" \ -d '{ "challengeId": "string", "actionType": 0 }' ``` ### POST `/v1/users/{userId}/challenges/email/verify` Verifies a two step verification challenge with a code sent via email. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user ID. | **Request Body:** `application/json` — Type: `Roblox.TwoStepVerification.Api.VerifyCodeRequest` See [Roblox.TwoStepVerification.Api.VerifyCodeRequest](#roblox-twostepverification-api-verifycoderequest) in Models. **Request example:** ```json { "challengeId": "string", "actionType": 0, "code": "string" } ``` **Responses:** - `200`: OK → `Roblox.TwoStepVerification.Api.VerifyCodeResponse` - `400`: 1: Invalid challenge ID. 10: The two step verification challenge code is invalid. - `403`: 0: Token Validation Failed 2: The user ID is invalid. - `429`: 5: Too many requests. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.TwoStepVerification.Api.VerifyCodeResponse`) See [Roblox.TwoStepVerification.Api.VerifyCodeResponse](#roblox-twostepverification-api-verifycoderesponse) in Models. **Response example:** ```json { "verificationToken": "string" } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/challenges/email/verify" \ -H "Content-Type: application/json" \ -d '{ "challengeId": "string", "actionType": 0, "code": "string" }' ``` ### POST `/v1/users/{userId}/challenges/passkey/verify-finish` Validates the assertion data returned by the passkey. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user ID. | **Request Body:** `application/json` — Type: `Roblox.TwoStepVerification.Api.VerifyCodeRequest` See [Roblox.TwoStepVerification.Api.VerifyCodeRequest](#roblox-twostepverification-api-verifycoderequest) in Models. **Request example:** ```json { "challengeId": "string", "actionType": 0, "code": "string" } ``` **Responses:** - `200`: OK → `Roblox.TwoStepVerification.Api.VerifyCodeResponse` - `400`: 1: Invalid challenge ID. 2: The user ID is invalid. 10: The two step verification challenge code is invalid. - `403`: 0: Token Validation Failed - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.TwoStepVerification.Api.VerifyCodeResponse`) See [Roblox.TwoStepVerification.Api.VerifyCodeResponse](#roblox-twostepverification-api-verifycoderesponse) in Models. **Response example:** ```json { "verificationToken": "string" } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/challenges/passkey/verify-finish" \ -H "Content-Type: application/json" \ -d '{ "challengeId": "string", "actionType": 0, "code": "string" }' ``` ### POST `/v1/users/{userId}/challenges/passkey/verify-start` Provides a challenge for the passkey to authenticate. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user ID. | **Request Body:** `application/json` — Type: `Roblox.TwoStepVerification.Api.SendCodeRequest` See [Roblox.TwoStepVerification.Api.SendCodeRequest](#roblox-twostepverification-api-sendcoderequest) in Models. **Request example:** ```json { "challengeId": "string", "actionType": 0 } ``` **Responses:** - `200`: OK → `Roblox.TwoStepVerification.Api.VerifyStartPasskeyResponse` - `400`: 1: Invalid challenge ID. 2: The user ID is invalid. - `403`: 0: Token Validation Failed 8: The user is not allowed to perform the requested action. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.TwoStepVerification.Api.VerifyStartPasskeyResponse`) See [Roblox.TwoStepVerification.Api.VerifyStartPasskeyResponse](#roblox-twostepverification-api-verifystartpasskeyresponse) in Models. **Response example:** ```json { "authenticationOptions": "string", "sessionId": "string" } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/challenges/passkey/verify-start" \ -H "Content-Type: application/json" \ -d '{ "challengeId": "string", "actionType": 0 }' ``` ### POST `/v1/users/{userId}/challenges/password/verify` Verifies a two step verification challenge with a password (code). **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user ID. | **Request Body:** `application/json` — Type: `Roblox.TwoStepVerification.Api.VerifyCodeRequest` See [Roblox.TwoStepVerification.Api.VerifyCodeRequest](#roblox-twostepverification-api-verifycoderequest) in Models. **Request example:** ```json { "challengeId": "string", "actionType": 0, "code": "string" } ``` **Responses:** - `200`: OK → `Roblox.TwoStepVerification.Api.VerifyCodeResponse` - `400`: 1: Invalid challenge ID. 4: The password is invalid. - `403`: 0: Token Validation Failed 2: The user ID is invalid. - `429`: 5: Too many requests. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.TwoStepVerification.Api.VerifyCodeResponse`) See [Roblox.TwoStepVerification.Api.VerifyCodeResponse](#roblox-twostepverification-api-verifycoderesponse) in Models. **Response example:** ```json { "verificationToken": "string" } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/challenges/password/verify" \ -H "Content-Type: application/json" \ -d '{ "challengeId": "string", "actionType": 0, "code": "string" }' ``` ### POST `/v1/users/{userId}/challenges/recovery-codes/verify` Verifies a two step verification challenge via a recovery code. Once a recovery code has been used to verify a challenge it cannot be used again. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user ID. | **Request Body:** `application/json` — Type: `Roblox.TwoStepVerification.Api.VerifyCodeRequest` See [Roblox.TwoStepVerification.Api.VerifyCodeRequest](#roblox-twostepverification-api-verifycoderequest) in Models. **Request example:** ```json { "challengeId": "string", "actionType": 0, "code": "string" } ``` **Responses:** - `200`: OK → `Roblox.TwoStepVerification.Api.VerifyCodeResponse` - `400`: 1: Invalid challenge ID. 2: The user ID is invalid. 10: The two step verification challenge code is invalid. - `403`: 0: Token Validation Failed - `429`: 5: Too many requests. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.TwoStepVerification.Api.VerifyCodeResponse`) See [Roblox.TwoStepVerification.Api.VerifyCodeResponse](#roblox-twostepverification-api-verifycoderesponse) in Models. **Response example:** ```json { "verificationToken": "string" } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/challenges/recovery-codes/verify" \ -H "Content-Type: application/json" \ -d '{ "challengeId": "string", "actionType": 0, "code": "string" }' ``` ### POST `/v1/users/{userId}/challenges/security-key/verify-finish` Validates the assertion data returned by the security key. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user ID. | **Request Body:** `application/json` — Type: `Roblox.TwoStepVerification.Api.VerifyCodeRequest` See [Roblox.TwoStepVerification.Api.VerifyCodeRequest](#roblox-twostepverification-api-verifycoderequest) in Models. **Request example:** ```json { "challengeId": "string", "actionType": 0, "code": "string" } ``` **Responses:** - `200`: OK → `Roblox.TwoStepVerification.Api.VerifyCodeResponse` - `400`: 1: Invalid challenge ID. 2: The user ID is invalid. 10: The two step verification challenge code is invalid. - `403`: 0: Token Validation Failed - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.TwoStepVerification.Api.VerifyCodeResponse`) See [Roblox.TwoStepVerification.Api.VerifyCodeResponse](#roblox-twostepverification-api-verifycoderesponse) in Models. **Response example:** ```json { "verificationToken": "string" } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/challenges/security-key/verify-finish" \ -H "Content-Type: application/json" \ -d '{ "challengeId": "string", "actionType": 0, "code": "string" }' ``` ### POST `/v1/users/{userId}/challenges/security-key/verify-start` Provides a challenge for the security key to authenticate. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user ID. | **Request Body:** `application/json` — Type: `Roblox.TwoStepVerification.Api.SendCodeRequest` See [Roblox.TwoStepVerification.Api.SendCodeRequest](#roblox-twostepverification-api-sendcoderequest) in Models. **Request example:** ```json { "challengeId": "string", "actionType": 0 } ``` **Responses:** - `200`: OK → `Roblox.TwoStepVerification.Api.VerifyStartSecurityKeyResponse` - `400`: 1: Invalid challenge ID. 2: The user ID is invalid. - `403`: 0: Token Validation Failed 8: The user is not allowed to perform the requested action. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.TwoStepVerification.Api.VerifyStartSecurityKeyResponse`) See [Roblox.TwoStepVerification.Api.VerifyStartSecurityKeyResponse](#roblox-twostepverification-api-verifystartsecuritykeyresponse) in Models. **Response example:** ```json { "authenticationOptions": "string", "sessionId": "string" } ``` **Error handling:** `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/challenges/security-key/verify-start" \ -H "Content-Type: application/json" \ -d '{ "challengeId": "string", "actionType": 0 }' ``` ### POST `/v1/users/{userId}/challenges/sms/send-code` Sends a two step verification code via SMS for the specified user. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user ID. | **Request Body:** `application/json` — Type: `Roblox.TwoStepVerification.Api.SendCodeRequest` See [Roblox.TwoStepVerification.Api.SendCodeRequest](#roblox-twostepverification-api-sendcoderequest) in Models. **Request example:** ```json { "challengeId": "string", "actionType": 0 } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: Invalid challenge ID. - `403`: 0: Token Validation Failed 2: The user ID is invalid. - `429`: 5: Too many requests. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/challenges/sms/send-code" \ -H "Content-Type: application/json" \ -d '{ "challengeId": "string", "actionType": 0 }' ``` ### POST `/v1/users/{userId}/challenges/sms/verify` Verifies a two step verification challenge with a code sent via SMS. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user ID. | **Request Body:** `application/json` — Type: `Roblox.TwoStepVerification.Api.VerifyCodeRequest` See [Roblox.TwoStepVerification.Api.VerifyCodeRequest](#roblox-twostepverification-api-verifycoderequest) in Models. **Request example:** ```json { "challengeId": "string", "actionType": 0, "code": "string" } ``` **Responses:** - `200`: OK → `Roblox.TwoStepVerification.Api.VerifyCodeResponse` - `400`: 1: Invalid challenge ID. 2: The user ID is invalid. 10: The two step verification challenge code is invalid. - `403`: 0: Token Validation Failed - `429`: 5: Too many requests. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.TwoStepVerification.Api.VerifyCodeResponse`) See [Roblox.TwoStepVerification.Api.VerifyCodeResponse](#roblox-twostepverification-api-verifycoderesponse) in Models. **Response example:** ```json { "verificationToken": "string" } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/challenges/sms/verify" \ -H "Content-Type: application/json" \ -d '{ "challengeId": "string", "actionType": 0, "code": "string" }' ``` ### POST `/v1/users/{userId}/configuration/authenticator/disable` Disables two step verification via authenticator for the specified user. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user ID. | **Request Body:** `application/json` — Type: `Roblox.TwoStepVerification.Api.DisableTwoStepVerificationRequest` See [Roblox.TwoStepVerification.Api.DisableTwoStepVerificationRequest](#roblox-twostepverification-api-disabletwostepverificationrequest) in Models. **Request example:** ```json { "password": "string", "reauthenticationToken": "string" } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 2: The user ID is invalid. 4: The password is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 8: The user is not allowed to perform the requested action. - `429`: 5: Too many requests. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/configuration/authenticator/disable" \ -H "Content-Type: application/json" \ -d '{ "password": "string", "reauthenticationToken": "string" }' ``` ### POST `/v1/users/{userId}/configuration/authenticator/enable` Initiates enabling authenticator-based two step verification for the specified user. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user ID. | **Request Body:** `application/json` — Type: `Roblox.TwoStepVerification.Api.EnableTwoStepVerificationRequest` See [Roblox.TwoStepVerification.Api.EnableTwoStepVerificationRequest](#roblox-twostepverification-api-enabletwostepverificationrequest) in Models. **Request example:** ```json { "password": "string", "secureAuthenticationIntent": { "clientPublicKey": "string", "clientEpochTimestamp": 0, "saiSignature": "string", "serverNonce": "string" } } ``` **Responses:** - `200`: OK → `Roblox.TwoStepVerification.Api.EnableAuthenticatorResponse` - `400`: 2: The user ID is invalid. 3: The email is invalid. 4: The password is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 11: The two step verification configuration is already enabled. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.TwoStepVerification.Api.EnableAuthenticatorResponse`) See [Roblox.TwoStepVerification.Api.EnableAuthenticatorResponse](#roblox-twostepverification-api-enableauthenticatorresponse) in Models. **Response example:** ```json { "setupToken": "string", "qrCodeImageUrl": "string", "manualEntryKey": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/configuration/authenticator/enable" \ -H "Content-Type: application/json" \ -d '{ "password": "string", "secureAuthenticationIntent": { "clientPublicKey": "string", "clientEpochTimestamp": 0, "saiSignature": "string", "serverNonce": "string" } }' ``` ### POST `/v1/users/{userId}/configuration/authenticator/enable-verify` Finishes enabling authenticator-based two step verification for the specified user. Enabling authenticator-based two step verification requires two parts to help ensure the user has properly stored the authenticator key in their authenticator app. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user ID. | **Request Body:** `application/json` — Type: `Roblox.TwoStepVerification.Api.EnableVerifyAuthenticatorRequest` See [Roblox.TwoStepVerification.Api.EnableVerifyAuthenticatorRequest](#roblox-twostepverification-api-enableverifyauthenticatorrequest) in Models. **Request example:** ```json { "setupToken": "string", "code": "string", "password": "string", "secureAuthenticationIntent": { "clientPublicKey": "string", "clientEpochTimestamp": 0, "saiSignature": "string", "serverNonce": "string" } } ``` **Responses:** - `200`: OK → `Roblox.TwoStepVerification.Api.EnableVerifyAuthenticatorResponse` - `400`: 2: The user ID is invalid. 4: The password is invalid. 10: The two step verification challenge code is invalid. 12: Invalid setup token. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 11: The two step verification configuration is already enabled. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.TwoStepVerification.Api.EnableVerifyAuthenticatorResponse`) See [Roblox.TwoStepVerification.Api.EnableVerifyAuthenticatorResponse](#roblox-twostepverification-api-enableverifyauthenticatorresponse) in Models. **Response example:** ```json { "recoveryCodes": [ "string" ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/configuration/authenticator/enable-verify" \ -H "Content-Type: application/json" \ -d '{ "setupToken": "string", "code": "string", "password": "string", "secureAuthenticationIntent": { "clientPublicKey": "string", "clientEpochTimestamp": 0, "saiSignature": "string", "serverNonce": "string" } }' ``` ### POST `/v1/users/{userId}/configuration/email/disable` Disables two step verification via email for the specified user. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user ID. | **Request Body:** `application/json` — Type: `Roblox.TwoStepVerification.Api.DisableTwoStepVerificationRequest` See [Roblox.TwoStepVerification.Api.DisableTwoStepVerificationRequest](#roblox-twostepverification-api-disabletwostepverificationrequest) in Models. **Request example:** ```json { "password": "string", "reauthenticationToken": "string" } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 4: The password is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: The user ID is invalid. 8: The user is not allowed to perform the requested action. - `429`: 5: Too many requests. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/configuration/email/disable" \ -H "Content-Type: application/json" \ -d '{ "password": "string", "reauthenticationToken": "string" }' ``` ### POST `/v1/users/{userId}/configuration/email/enable` Enables two step verification via email for the specified user. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user ID. | **Request Body:** `application/json` — Type: `Roblox.TwoStepVerification.Api.EnableTwoStepVerificationRequest` See [Roblox.TwoStepVerification.Api.EnableTwoStepVerificationRequest](#roblox-twostepverification-api-enabletwostepverificationrequest) in Models. **Request example:** ```json { "password": "string", "secureAuthenticationIntent": { "clientPublicKey": "string", "clientEpochTimestamp": 0, "saiSignature": "string", "serverNonce": "string" } } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 3: The email is invalid. 4: The password is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: The user ID is invalid. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/configuration/email/enable" \ -H "Content-Type: application/json" \ -d '{ "password": "string", "secureAuthenticationIntent": { "clientPublicKey": "string", "clientEpochTimestamp": 0, "saiSignature": "string", "serverNonce": "string" } }' ``` ### POST `/v1/users/{userId}/configuration/security-key/disable` Disables a batch of credentials for the specified user. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user ID. | **Request Body:** `application/json` — Type: `Roblox.TwoStepVerification.Api.DisableSecurityKeyRequest` See [Roblox.TwoStepVerification.Api.DisableSecurityKeyRequest](#roblox-twostepverification-api-disablesecuritykeyrequest) in Models. **Request example:** ```json { "credentialNicknames": [ "string" ] } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 4: The password is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 8: The user is not allowed to perform the requested action. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/configuration/security-key/disable" \ -H "Content-Type: application/json" \ -d '{ "credentialNicknames": [ "string" ] }' ``` ### POST `/v1/users/{userId}/configuration/security-key/enable` Initiates security key registration by providing credential creation options. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user ID. | **Request Body:** `application/json` — Type: `Roblox.TwoStepVerification.Api.EnableTwoStepVerificationRequest` See [Roblox.TwoStepVerification.Api.EnableTwoStepVerificationRequest](#roblox-twostepverification-api-enabletwostepverificationrequest) in Models. **Request example:** ```json { "password": "string", "secureAuthenticationIntent": { "clientPublicKey": "string", "clientEpochTimestamp": 0, "saiSignature": "string", "serverNonce": "string" } } ``` **Responses:** - `200`: OK → `Roblox.TwoStepVerification.Api.EnableSecurityKeyResponse` - `400`: 2: The user ID is invalid. 4: The password is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 9: The two step verification configuration is invalid for this action. 16: Reached limit of security keys registered. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.TwoStepVerification.Api.EnableSecurityKeyResponse`) See [Roblox.TwoStepVerification.Api.EnableSecurityKeyResponse](#roblox-twostepverification-api-enablesecuritykeyresponse) in Models. **Response example:** ```json { "creationOptions": "string", "sessionId": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/configuration/security-key/enable" \ -H "Content-Type: application/json" \ -d '{ "password": "string", "secureAuthenticationIntent": { "clientPublicKey": "string", "clientEpochTimestamp": 0, "saiSignature": "string", "serverNonce": "string" } }' ``` ### POST `/v1/users/{userId}/configuration/security-key/enable-verify` Finishes security key registration and stores credential. Enables security key as a 2sv media type if it is a user's first key. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user ID. | **Request Body:** `application/json` — Type: `Roblox.TwoStepVerification.Api.EnableVerifySecurityKeyRequest` See [Roblox.TwoStepVerification.Api.EnableVerifySecurityKeyRequest](#roblox-twostepverification-api-enableverifysecuritykeyrequest) in Models. **Request example:** ```json { "sessionId": "string", "credentialNickname": "string", "attestationResponse": "string" } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 2: The user ID is invalid. 17: Invalid security key nickname. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 17: Invalid security key nickname. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/configuration/security-key/enable-verify" \ -H "Content-Type: application/json" \ -d '{ "sessionId": "string", "credentialNickname": "string", "attestationResponse": "string" }' ``` ### POST `/v1/users/{userId}/configuration/security-key/list` List a user's registered security keys. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user ID. | **Responses:** - `200`: OK → `Roblox.TwoStepVerification.Api.ListSecurityKeyResponse` - `400`: 2: The user ID is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.TwoStepVerification.Api.ListSecurityKeyResponse`) See [Roblox.TwoStepVerification.Api.ListSecurityKeyResponse](#roblox-twostepverification-api-listsecuritykeyresponse) in Models. **Response example:** ```json { "credentials": [ { "nickname": "..." } ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/configuration/security-key/list" ``` ### POST `/v1/users/{userId}/configuration/sms/disable` Disables two step verification via SMS for the specified user. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user ID. | **Request Body:** `application/json` — Type: `Roblox.TwoStepVerification.Api.DisableTwoStepVerificationRequest` See [Roblox.TwoStepVerification.Api.DisableTwoStepVerificationRequest](#roblox-twostepverification-api-disabletwostepverificationrequest) in Models. **Request example:** ```json { "password": "string", "reauthenticationToken": "string" } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 4: The password is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: The user ID is invalid. 8: The user is not allowed to perform the requested action. - `429`: 5: Too many requests. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/configuration/sms/disable" \ -H "Content-Type: application/json" \ -d '{ "password": "string", "reauthenticationToken": "string" }' ``` ### POST `/v1/users/{userId}/configuration/sms/enable` Enables two step verification via SMS for the specified user. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user ID. | **Request Body:** `application/json` — Type: `Roblox.TwoStepVerification.Api.EnableTwoStepVerificationRequest` See [Roblox.TwoStepVerification.Api.EnableTwoStepVerificationRequest](#roblox-twostepverification-api-enabletwostepverificationrequest) in Models. **Request example:** ```json { "password": "string", "secureAuthenticationIntent": { "clientPublicKey": "string", "clientEpochTimestamp": 0, "saiSignature": "string", "serverNonce": "string" } } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 4: The password is invalid. 15: The phone number is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: The user ID is invalid. - `503`: 7: Two step verification is currently under maintenance. 8: The user is not allowed to perform the requested action. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/configuration/sms/enable" \ -H "Content-Type: application/json" \ -d '{ "password": "string", "secureAuthenticationIntent": { "clientPublicKey": "string", "clientEpochTimestamp": 0, "saiSignature": "string", "serverNonce": "string" } }' ``` ### POST `/v1/users/{userId}/recovery-codes/clear` Clears any existing recovery codes for the user. Clearing recovery codes voids any recovery codes previously generated for the user. New recovery codes will have to be generated to pass two step verification via recovery code. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user ID. | **Request Body:** `application/json` — Type: `Roblox.TwoStepVerification.Api.ClearRecoveryCodesRequest` See [Roblox.TwoStepVerification.Api.ClearRecoveryCodesRequest](#roblox-twostepverification-api-clearrecoverycodesrequest) in Models. **Request example:** ```json { "password": "string" } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 2: The user ID is invalid. 4: The password is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `429`: 5: Too many requests. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/recovery-codes/clear" \ -H "Content-Type: application/json" \ -d '{ "password": "string" }' ``` ### POST `/v1/users/{userId}/recovery-codes/regenerate` Clears any existing recovery codes and generates a new batch of recovery codes. Two step verification recovery codes do not enforce that two step verification must be passed when logging in. At least one two step verification media type must be enabled to trigger the two step verification flow. Recovery codes are intended to be used to pass two step verification when the enabled media type is unavailable. Recovery codes generated by this endpoint do not have an expiration. Once a recovery code generated by this endpoint has been used it cannot be used again. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user ID to generate recovery codes for. | **Request Body:** `application/json` — Type: `Roblox.TwoStepVerification.Api.RegenerateRecoveryCodesRequest` See [Roblox.TwoStepVerification.Api.RegenerateRecoveryCodesRequest](#roblox-twostepverification-api-regeneraterecoverycodesrequest) in Models. **Request example:** ```json { "password": "string" } ``` **Responses:** - `200`: OK → `Roblox.TwoStepVerification.Api.RegenerateRecoveryCodesResponse` - `400`: 2: The user ID is invalid. 4: The password is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed - `429`: 5: Too many requests. - `503`: 7: Two step verification is currently under maintenance. **Response fields** (`Roblox.TwoStepVerification.Api.RegenerateRecoveryCodesResponse`) See [Roblox.TwoStepVerification.Api.RegenerateRecoveryCodesResponse](#roblox-twostepverification-api-regeneraterecoverycodesresponse) in Models. **Response example:** ```json { "recoveryCodes": [ "string" ] } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://twostepverification.roblox.com/v1/users/{USERID}/recovery-codes/regenerate" \ -H "Content-Type: application/json" \ -d '{ "password": "string" }' ``` ## Models ### Roblox.TwoStepVerification.Api.ClearRecoveryCodesRequest Request information needed to clear existing recovery codes. | Property | Type | Required | Description | |----------|------|----------|-------------| | `password` | `string` | No | The user's password. | ### Roblox.TwoStepVerification.Api.DisableSecurityKeyRequest Request information needed to disable two step verification. | Property | Type | Required | Description | |----------|------|----------|-------------| | `credentialNicknames` | `string[]` | No | A array of nicknames of credentials to be deleted. | ### Roblox.TwoStepVerification.Api.DisableTwoStepVerificationRequest Request information needed to disable two step verification. | Property | Type | Required | Description | |----------|------|----------|-------------| | `password` | `string` | No | The user's password. | | `reauthenticationToken` | `string` | No | A re-authentication token redeemable for any password check. | ### Roblox.TwoStepVerification.Api.EnableAuthenticatorResponse Response parameters for initiating enabling authenticator-based two step verification. | Property | Type | Required | Description | |----------|------|----------|-------------| | `setupToken` | `string` | No | The setup token for turning on authenticator. | | `qrCodeImageUrl` | `string` | No | The Url to the QR code image to scan into the authenticator app. | | `manualEntryKey` | `string` | No | The manual entry key to input into the authenticator app. | ### Roblox.TwoStepVerification.Api.EnableSecurityKeyResponse Response parameters for initiating enabling security key-based two step verification. | Property | Type | Required | Description | |----------|------|----------|-------------| | `creationOptions` | `string` | No | The credential registration options for the hardware key. | | `sessionId` | `string` | No | The session of the registration attempt. | ### Roblox.TwoStepVerification.Api.EnableTwoStepVerificationRequest Request parameters for enabling two step verification. | Property | Type | Required | Description | |----------|------|----------|-------------| | `password` | `string` | No | The user's password. | | `secureAuthenticationIntent` | `Roblox.TwoStepVerification.Api.Models.Request.SecureAuthenticationIntentModel` | No | | ### Roblox.TwoStepVerification.Api.EnableVerifyAuthenticatorRequest Request parameters for authenticator enabling-verify. | Property | Type | Required | Description | |----------|------|----------|-------------| | `setupToken` | `string` | No | The setup token from the enable authenticator request. | | `code` | `string` | No | The code from the authenticator app. | | `password` | `string` | No | The user's password. | | `secureAuthenticationIntent` | `Roblox.TwoStepVerification.Api.Models.Request.SecureAuthenticationIntentModel` | No | | ### Roblox.TwoStepVerification.Api.EnableVerifyAuthenticatorResponse Response parameters for finishing enabling authenticator-based two-step verification. | Property | Type | Required | Description | |----------|------|----------|-------------| | `recoveryCodes` | `string[]` | No | Recovery codes automatically generated for the user (when applicable; the user should not already have a set of recovery codes). | ### Roblox.TwoStepVerification.Api.EnableVerifySecurityKeyRequest Request information needed to complete the registration of a security key. | Property | Type | Required | Description | |----------|------|----------|-------------| | `sessionId` | `string` | No | The session of the registration attempt. | | `credentialNickname` | `string` | No | The nickname of the new credential. | | `attestationResponse` | `string` | No | The hardware key's response. | ### Roblox.TwoStepVerification.Api.ListSecurityKeyResponse Response parameters for listing all credentials under a user. | Property | Type | Required | Description | |----------|------|----------|-------------| | `credentials` | `Roblox.TwoStepVerification.Api.SecurityKeyCredential[]` | No | An array of credentials for a user. | ### Roblox.TwoStepVerification.Api.MetadataResponse Two step verification system metadata. | Property | Type | Required | Description | |----------|------|----------|-------------| | `twoStepVerificationEnabled` | `boolean` | No | Whether or not two step verification is globally enabled. | | `authenticatorQrCodeSize` | `string` | No | Authenticator QR code image dimensions. | | `emailCodeLength` | `integer` | No | Number of characters in an email-based two step verification code. | | `authenticatorCodeLength` | `integer` | No | Number of characters in an authenticator-based two step verification code. | | `authenticatorHelpSiteAddress` | `string` | No | Address of the help site provided to users to help them with authenticator. | | `isPasswordRequiredForEnablingAuthenticator` | `boolean` | No | Whether or not a password is required for enabling authenticator. | | `isPasswordRequiredForEnablingEmailTwoStepVerification` | `boolean` | No | Whether or not a password is required for enabling email 2sv. | | `isUsingTwoStepWebviewComponent` | `boolean` | No | Whether or not we're using the new 2sv webview component or the manual pop up | | `isTwoStepEnabledRequiredForEmailPasswordRequirement` | `boolean` | No | Whether or not a 2sv method has to be enabled to require password when enabling email two step verification. | | `isTwoStepEnabledRequiredForAuthenticatorPasswordRequirement` | `boolean` | No | Whether or not a 2sv method has to be enabled to require password when enabling authenticator. | | `isSingleMethodEnforcementEnabled` | `boolean` | No | Whether or not the frontend should enforce single method logic. | | `isSmsTwoStepVerificationAvailable` | `boolean` | No | Whether or not sms two step verification is available for the user. | | `isSecurityKeyTwoStepVerificationAvailable` | `boolean` | No | Whether or not security key two step verification is available for the user. | | `isAuthenticatorWithVerifiedPhoneEnabled` | `boolean` | No | Whether or not someone can enable authenticator with just a verified phone number. | | `isPasswordRequiredForEnablingSecurityKey` | `boolean` | No | Whether or not a password is required for enabling Security Key 2SV. | | `isPasswordRequiredForEnablingSms2SV` | `boolean` | No | Whether or not a password is required for enabling SMS 2SV. | | `isPasswordRequiredForChangingRecoveryCodes` | `boolean` | No | Whether or not a password is required for making updates to recovery codes. | | `isPasswordRequiredForDisablingAuthenticator` | `boolean` | No | Whether or not a password is required for disabling authenticator. | | `isPasswordRequiredForDisablingEmailTwoStepVerification` | `boolean` | No | Whether or not a password is required for disabling email 2sv. | | `isPasswordRequiredForDisablingSms2SV` | `boolean` | No | Whether or not a password is required for disabling SMS 2SV. | | `isRecoveryCodeGenerationForAuthenticatorSetupEnabled` | `boolean` | No | Whether recovery code generation is attempted upon completion of authenticator setup. | | `isSecurityKeyOnAllPlatformsEnabled` | `boolean` | No | Whether security keys on all platforms is enabled. | | `receiveWarningsOnDisableTwoStep` | `boolean` | No | Whether users should receive additional warnings when disabling 2SV. | | `isAndroidSecurityKeyEnabled` | `boolean` | No | Whether Android security keys is enabled. | | `isSettingsTabRedesignEnabled` | `boolean` | No | Whether the settings tab redesign is enabled. | | `twoStepCopyTextEnrollmentStatus` | `integer` | No | The enum representing which experiment group the user is in. | | `isEppUIEnabled` | `boolean` | No | Whether the EPP UI is enabled. | | `isEppRecoveryCodesEnabled` | `boolean` | No | Whether the EPP recovery codes UI is enabled. | | `maskedUserEmail` | `string` | No | The masked email for the authenticated user. Typically used in Email 2SV challenges after the challenge has been verified to match the target user. | | `isUserU13` | `boolean` | No | Whether the user is O13. | | `isDelayedUiEnabled` | `boolean` | No | Whether the delayed UI is enabled. | | `is2svRecoveryEnabled` | `boolean` | No | Whether to expose 2SV recovery through the 2SV challenge modal. | ### Roblox.TwoStepVerification.Api.Models.Request.SecureAuthenticationIntentModel Model describing secure auth intent. | Property | Type | Required | Description | |----------|------|----------|-------------| | `clientPublicKey` | `string` | No | | | `clientEpochTimestamp` | `integer` | No | | | `saiSignature` | `string` | No | | | `serverNonce` | `string` | No | | ### Roblox.TwoStepVerification.Api.RecoveryCodesStatusResponse The response for getting the status of recovery codes for a user. | Property | Type | Required | Description | |----------|------|----------|-------------| | `activeCount` | `integer` | No | The number of unused recovery codes the user has available. | | `created` | `string` | No | The date time the recovery codes were generated at. | ### Roblox.TwoStepVerification.Api.RegenerateRecoveryCodesRequest Request information needed to regenerate recovery codes. | Property | Type | Required | Description | |----------|------|----------|-------------| | `password` | `string` | No | The user's password. | ### Roblox.TwoStepVerification.Api.RegenerateRecoveryCodesResponse The response for regenerating recovery codes. | Property | Type | Required | Description | |----------|------|----------|-------------| | `recoveryCodes` | `string[]` | No | The collection of generated recovery codes. | ### Roblox.TwoStepVerification.Api.RetractDialogRequest Request parameters for retracting a Cross Device dialog from state ACTIVE to PENDING. | Property | Type | Required | Description | |----------|------|----------|-------------| | `challengeId` | `string` | No | The two step verification challenge ID. | | `actionType` | `integer enum (9 values)` | No | The Roblox.TwoStepVerification.Client.TwoStepVerificationActionType associated with the challenge. ['Unknown' = 0, 'Login' = 1, 'RobuxSpend' = 2, 'ItemTrade' = 3, 'Resale' = 4, 'PasswordReset' = 5, 'RevertAccount' = 6, 'Generic' = 7, 'GenericWithRecoveryCodes' = 8] Values: 0, 1, 2, 3, 4, 5, 6, 7, 8 | ### Roblox.TwoStepVerification.Api.RetryApprovalRequest Request parameters for retrying a Cross Device two step verification approval. | Property | Type | Required | Description | |----------|------|----------|-------------| | `challengeId` | `string` | No | The two step verification challenge ID. | | `actionType` | `integer enum (9 values)` | No | The Roblox.TwoStepVerification.Client.TwoStepVerificationActionType associated with the challenge. ['Unknown' = 0, 'Login' = 1, 'RobuxSpend' = 2, 'ItemTrade' = 3, 'Resale' = 4, 'PasswordReset' = 5, 'RevertAccount' = 6, 'Generic' = 7, 'GenericWithRecoveryCodes' = 8] Values: 0, 1, 2, 3, 4, 5, 6, 7, 8 | ### Roblox.TwoStepVerification.Api.SecurityKeyCredential Credential information that includes its nickname and any additional metadata. | Property | Type | Required | Description | |----------|------|----------|-------------| | `nickname` | `string` | No | Nickname the user has chosen for this credential. | ### Roblox.TwoStepVerification.Api.SendCodeRequest Request parameters for sending a two step verification code. | Property | Type | Required | Description | |----------|------|----------|-------------| | `challengeId` | `string` | No | The two step verification challenge ID. | | `actionType` | `integer enum (9 values)` | No | The Roblox.TwoStepVerification.Client.TwoStepVerificationActionType associated with the challenge. ['Unknown' = 0, 'Login' = 1, 'RobuxSpend' = 2, 'ItemTrade' = 3, 'Resale' = 4, 'PasswordReset' = 5, 'RevertAccount' = 6, 'Generic' = 7, 'GenericWithRecoveryCodes' = 8] Values: 0, 1, 2, 3, 4, 5, 6, 7, 8 | ### Roblox.TwoStepVerification.Api.UserConfiguration The users two step verification configuration. | Property | Type | Required | Description | |----------|------|----------|-------------| | `primaryMediaType` | `integer enum (8 values)` | No | The preferred two step verification method for the user. Values: 0, 1, 2, 3, 4, 5, 6, 7 | | `methods` | `Roblox.TwoStepVerification.Api.UserConfigurationMethod[]` | No | The two step verification methods associated with the user. | ### Roblox.TwoStepVerification.Api.UserConfigurationMethod A user configuration method option. | Property | Type | Required | Description | |----------|------|----------|-------------| | `mediaType` | `integer enum (8 values)` | No | The Roblox.TwoStepVerification.Client.TwoStepVerificationMediaType for the method. ['Email' = 0, 'SMS' = 1, 'Authenticator' = 2, 'RecoveryCode' = 3, 'SecurityKey' = 4, 'CrossDevice' = 5, 'Password' = 6, 'Passkey' = 7] Values: 0, 1, 2, 3, 4, 5, 6, 7 | | `enabled` | `boolean` | No | Whether or not the method is enabled. | | `updated` | `string` | No | The last time this method was updated for the user. | ### Roblox.TwoStepVerification.Api.VerifyApprovalRequest Request parameters for verifying a Cross Device two step verification approval. Does not use verification code. | Property | Type | Required | Description | |----------|------|----------|-------------| | `challengeId` | `string` | No | The two step verification challenge ID. | | `actionType` | `integer enum (9 values)` | No | The Roblox.TwoStepVerification.Client.TwoStepVerificationActionType associated with the challenge. ['Unknown' = 0, 'Login' = 1, 'RobuxSpend' = 2, 'ItemTrade' = 3, 'Resale' = 4, 'PasswordReset' = 5, 'RevertAccount' = 6, 'Generic' = 7, 'GenericWithRecoveryCodes' = 8] Values: 0, 1, 2, 3, 4, 5, 6, 7, 8 | ### Roblox.TwoStepVerification.Api.VerifyApprovalResponse Result for a successful Cross Device approval verification. | Property | Type | Required | Description | |----------|------|----------|-------------| | `verificationToken` | `string` | No | The verification token. | ### Roblox.TwoStepVerification.Api.VerifyCodeRequest Request parameters for verifying a two step verification code. | Property | Type | Required | Description | |----------|------|----------|-------------| | `challengeId` | `string` | No | The two step verification challenge ID. | | `actionType` | `integer enum (9 values)` | No | The Roblox.TwoStepVerification.Client.TwoStepVerificationActionType associated with the challenge. ['Unknown' = 0, 'Login' = 1, 'RobuxSpend' = 2, 'ItemTrade' = 3, 'Resale' = 4, 'PasswordReset' = 5, 'RevertAccount' = 6, 'Generic' = 7, 'GenericWithRecoveryCodes' = 8] Values: 0, 1, 2, 3, 4, 5, 6, 7, 8 | | `code` | `string` | No | The two step verification code. | ### Roblox.TwoStepVerification.Api.VerifyCodeResponse Result for a successful verification. | Property | Type | Required | Description | |----------|------|----------|-------------| | `verificationToken` | `string` | No | The verification token. | ### Roblox.TwoStepVerification.Api.VerifyStartPasskeyResponse Result for a successful verification. | Property | Type | Required | Description | |----------|------|----------|-------------| | `authenticationOptions` | `string` | No | The authentication options for the passkey. | | `sessionId` | `string` | No | The session of the authentication attempt. | ### Roblox.TwoStepVerification.Api.VerifyStartSecurityKeyResponse Result for a successful verification. | Property | Type | Required | Description | |----------|------|----------|-------------| | `authenticationOptions` | `string` | No | The authentication options for the hardware key. | | `sessionId` | `string` | No | The session of the authentication attempt. | --- name: "Users Api v1" last_updated: 2026-06-29T19:34:17Z type: legacy api_base_url: "https://users.roblox.com" version: "v1" endpoints: 18 auth: [cookie] --- # Users Api v1 **API Version:** v1 **Base URL:** `https://users.roblox.com` ## Endpoints ### GET `/v1/birthdate` Get the user's birthdate **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Users.Api.BirthdateResponse` - `400`: 1: User not found. - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Users.Api.BirthdateResponse`) See [Roblox.Users.Api.BirthdateResponse](#roblox-users-api-birthdateresponse) in Models. **Response example:** ```json { "birthMonth": 0, "birthDay": 0, "birthYear": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://users.roblox.com/v1/birthdate" ``` ### POST `/v1/birthdate` Update the user's birthdate **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Users.Api.BirthdateRequest` See [Roblox.Users.Api.BirthdateRequest](#roblox-users-api-birthdaterequest) in Models. **Request example:** ```json { "birthMonth": 0, "birthDay": 0, "birthYear": 0, "password": "string" } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: User not found. 4: The birthdate provided is invalid. 8: Password is incorrect. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: PIN is locked. 5: Invalid birthdate change. - `500`: 0: An unknown error occured. 5: Invalid birthdate change. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://users.roblox.com/v1/birthdate" \ -H "Content-Type: application/json" \ -d '{ "birthMonth": 0, "birthDay": 0, "birthYear": 0, "password": "string" }' ``` ### GET `/v1/description` Get the user's description **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Users.Api.DescriptionResponse` - `400`: 1: User not found. - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Users.Api.DescriptionResponse`) See [Roblox.Users.Api.DescriptionResponse](#roblox-users-api-descriptionresponse) in Models. **Response example:** ```json { "description": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://users.roblox.com/v1/description" ``` ### POST `/v1/description` Update the user's description **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Users.Api.DescriptionRequest` See [Roblox.Users.Api.DescriptionRequest](#roblox-users-api-descriptionrequest) in Models. **Request example:** ```json { "description": "string" } ``` **Responses:** - `200`: OK → `Roblox.Users.Api.DescriptionResponse` - `400`: 1: User not found. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: PIN is locked. - `500`: 0: An unknown error occured. - `503`: 3: This feature is currently disabled. Please try again later. **Response fields** (`Roblox.Users.Api.DescriptionResponse`) See [Roblox.Users.Api.DescriptionResponse](#roblox-users-api-descriptionresponse) in Models. **Response example:** ```json { "description": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://users.roblox.com/v1/description" \ -H "Content-Type: application/json" \ -d '{ "description": "string" }' ``` ### GET `/v1/display-names/validate` Validate a display name for a new user. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `displayName` | query | `string` | Yes | The display name. | | `birthdate` | query | `string (date-time)` | Yes | The new user's birthdate | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: Display name is too short 2: Display name is too long 3: Display name contains invalid characters 4: Display name has been moderated 6: Request must contain a birthdate 8: Display name has too many combinations of character sets - `429`: 5: Display name updates for this user have been throttled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://users.roblox.com/v1/display-names/validate?displayName={VALUE}&birthdate={VALUE}" ``` ### GET `/v1/gender` Get the user's gender **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Users.Api.GenderResponse` - `400`: 1: User not found. - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Users.Api.GenderResponse`) See [Roblox.Users.Api.GenderResponse](#roblox-users-api-genderresponse) in Models. **Response example:** ```json { "gender": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://users.roblox.com/v1/gender" ``` ### POST `/v1/gender` Update the user's gender **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Users.Api.GenderRequest` See [Roblox.Users.Api.GenderRequest](#roblox-users-api-genderrequest) in Models. **Request example:** ```json { "gender": 1 } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: User not found. 6: The gender provided is invalid. - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 2: PIN is locked. - `500`: 0: An unknown error occured. **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://users.roblox.com/v1/gender" \ -H "Content-Type: application/json" \ -d '{ "gender": 1 }' ``` ### GET `/v1/users/{userId}` Gets detailed user information by id. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user id. | **Responses:** - `200`: OK → `Roblox.Users.Api.GetUserResponse` - `404`: 3: The user id is invalid. **Response fields** (`Roblox.Users.Api.GetUserResponse`) See [Roblox.Users.Api.GetUserResponse](#roblox-users-api-getuserresponse) in Models. **Response example:** ```json { "description": "string", "created": "2024-01-01T00:00:00Z", "isBanned": false, "externalAppDisplayName": "string", "hasVerifiedBadge": false, "id": 0 } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://users.roblox.com/v1/users/{USERID}" ``` ### GET `/v1/users/{userId}/display-names/validate` Validate a display name for an existing user. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | The user id. | | `displayName` | query | `string` | Yes | The display name. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: Display name is too short 2: Display name is too long 3: Display name contains invalid characters 4: Display name has been moderated 8: Display name has too many combinations of character sets - `401`: 0: Authorization has been denied for this request. - `403`: 7: The user id is invalid. - `429`: 5: Display name updates for this user have been throttled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://users.roblox.com/v1/users/{USERID}/display-names/validate?displayName={VALUE}" ``` ### GET `/v1/users/{userId}/username-history` Retrieves the username history for a particular user. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | | | `limit` | query | `integer (int32)` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | | `sortOrder` | query | `string` | No | The order the results are sorted in. Valid values: `Asc`, `Desc` | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Users.Api.UsernameHistoryResponse]` - `400`: 3: The user id is invalid. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Users.Api.UsernameHistoryResponse]`) See [Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Users.Api.UsernameHistoryResponse]](#roblox-web-webapi-models-apipageresponse-roblox-users-api-usernamehistoryresponse-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "name": "..." } ] } ``` **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://users.roblox.com/v1/users/{USERID}/username-history" ``` ### GET `/v1/users/authenticated` Gets the minimal authenticated user. **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Users.Api.AuthenticatedGetUserResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Users.Api.AuthenticatedGetUserResponse`) See [Roblox.Users.Api.AuthenticatedGetUserResponse](#roblox-users-api-authenticatedgetuserresponse) in Models. **Response example:** ```json { "id": 0, "name": "string", "displayName": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://users.roblox.com/v1/users/authenticated" ``` ### GET `/v1/users/authenticated/age-bracket` Gets the age bracket of the authenticated user. **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Users.Api.UserAgeBracketResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Users.Api.UserAgeBracketResponse`) See [Roblox.Users.Api.UserAgeBracketResponse](#roblox-users-api-useragebracketresponse) in Models. **Response example:** ```json { "ageBracket": 0 } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://users.roblox.com/v1/users/authenticated/age-bracket" ``` ### GET `/v1/users/authenticated/country-code` Gets the country code of the authenticated user. **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Users.Api.UserCountryCodeResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Users.Api.UserCountryCodeResponse`) See [Roblox.Users.Api.UserCountryCodeResponse](#roblox-users-api-usercountrycoderesponse) in Models. **Response example:** ```json { "countryCode": "string" } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://users.roblox.com/v1/users/authenticated/country-code" ``` ### GET `/v1/users/authenticated/roles` Gets the (public) roles of the authenticated user, such as `"Soothsayer"` and `"BetaTester"`. **Auth:** Cookie (`.ROBLOSECURITY`) **Responses:** - `200`: OK → `Roblox.Users.Api.UserRolesResponse` - `401`: 0: Authorization has been denied for this request. **Response fields** (`Roblox.Users.Api.UserRolesResponse`) See [Roblox.Users.Api.UserRolesResponse](#roblox-users-api-userrolesresponse) in Models. **Response example:** ```json { "roles": [ "string" ] } ``` **Error handling:** `401`: Check that your API key/token is valid and not expired. **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://users.roblox.com/v1/users/authenticated/roles" ``` ### GET `/v1/users/search` Searches for users by keyword. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `keyword` | query | `string` | Yes | The search keyword. | | `sessionId` | query | `string` | No | | | `limit` | query | `integer (int32)` | No | The number of results per request. Valid values: `10`, `25`, `50`, `100` | | `cursor` | query | `string` | No | The paging cursor for the previous or next page. | **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Users.Api.SearchGetUserResponse]` - `400`: 5: The keyword was filtered. 6: The keyword is too short. - `429`: 4: Too many requests. **Response fields** (`Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Users.Api.SearchGetUserResponse]`) See [Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Users.Api.SearchGetUserResponse]](#roblox-web-webapi-models-apipageresponse-roblox-users-api-searchgetuserresponse-) in Models. **Response example:** ```json { "previousPageCursor": "string", "nextPageCursor": "string", "data": [ { "previousUsernames": "...", "hasVerifiedBadge": "...", "id": "...", "name": "...", "displayName": "..." } ] } ``` **Error handling:** `429`: Retry with exponential backoff (start at 1s). **Example:** ```bash curl -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://users.roblox.com/v1/users/search?keyword={VALUE}" ``` ### POST `/v1/usernames/users` Get users by usernames. This endpoint will also check previous usernames. Does not require X-CSRF-Token protection because this is essentially a get request but as a POST to avoid URI limits. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Users.Api.MultiGetByUsernameRequest` See [Roblox.Users.Api.MultiGetByUsernameRequest](#roblox-users-api-multigetbyusernamerequest) in Models. **Request example:** ```json { "usernames": [ "string" ], "excludeBannedUsers": false } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Users.Api.MultiGetUserByNameResponse]` - `400`: 2: Too many usernames. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Users.Api.MultiGetUserByNameResponse]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Users.Api.MultiGetUserByNameResponse]](#roblox-web-webapi-models-apiarrayresponse-roblox-users-api-multigetuserbynameresponse-) in Models. **Response example:** ```json { "data": [ { "requestedUsername": "...", "hasVerifiedBadge": "...", "id": "...", "name": "...", "displayName": "..." } ] } ``` **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://users.roblox.com/v1/usernames/users" \ -H "Content-Type: application/json" \ -d '{ "usernames": [ "string" ], "excludeBannedUsers": false }' ``` ### POST `/v1/users` Get users by ids. Does not require X-CSRF-Token protection because this is essentially a get request but as a POST to avoid URI limits. **Auth:** Cookie (`.ROBLOSECURITY`) **Request Body:** `application/json` — Type: `Roblox.Users.Api.MultiGetByUserIdRequest` See [Roblox.Users.Api.MultiGetByUserIdRequest](#roblox-users-api-multigetbyuseridrequest) in Models. **Request example:** ```json { "userIds": [ 0 ], "excludeBannedUsers": false } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Users.Api.MultiGetUserResponse]` - `400`: 1: Too many ids. **Response fields** (`Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Users.Api.MultiGetUserResponse]`) See [Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Users.Api.MultiGetUserResponse]](#roblox-web-webapi-models-apiarrayresponse-roblox-users-api-multigetuserresponse-) in Models. **Response example:** ```json { "data": [ { "hasVerifiedBadge": "...", "id": "...", "name": "...", "displayName": "..." } ] } ``` **Example:** ```bash curl -X POST -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://users.roblox.com/v1/users" \ -H "Content-Type: application/json" \ -d '{ "userIds": [ 0 ], "excludeBannedUsers": false }' ``` ### PATCH `/v1/users/{userId}/display-names` Set the display name for the authorized user. **Auth:** Cookie (`.ROBLOSECURITY`) **Parameters:** | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | `userId` | path | `integer (int64)` | Yes | the user id | **Request Body:** `application/json` — Type: `Roblox.Users.Api.SetDisplayNameRequest` See [Roblox.Users.Api.SetDisplayNameRequest](#roblox-users-api-setdisplaynamerequest) in Models. **Request example:** ```json { "newDisplayName": "string" } ``` **Responses:** - `200`: OK → `Roblox.Web.WebAPI.ApiEmptyResponseModel` - `400`: 1: Display name is too short 2: Display name is too long 3: Display name contains invalid characters 4: Display name has been moderated 8: Display name has too many combinations of character sets - `401`: 0: Authorization has been denied for this request. - `403`: 0: Token Validation Failed 7: The user id is invalid. - `429`: 5: Display name updates for this user have been throttled **Response fields** (`Roblox.Web.WebAPI.ApiEmptyResponseModel`) See [Roblox.Web.WebAPI.ApiEmptyResponseModel](#roblox-web-webapi-apiemptyresponsemodel) in Models. **Error handling:** `429`: Retry with exponential backoff (start at 1s). `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. **Example:** ```bash curl -X PATCH -b ".ROBLOSECURITY=$ROBLOSECURITY" \ "https://users.roblox.com/v1/users/{USERID}/display-names" \ -H "Content-Type: application/json" \ -d '{ "newDisplayName": "string" }' ``` ## Models ### Roblox.Users.Api.AuthenticatedGetUserResponse A response model representing absolute minimal authenticating user information. No new attributes should be added to this response since it is in the critical path of app launch and we want to minimize dependencies. | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | `integer` | No | The user Id. | | `name` | `string` | No | The user name. | | `displayName` | `string` | No | The user DisplayName. | ### Roblox.Users.Api.BirthdateRequest The birthdate request | Property | Type | Required | Description | |----------|------|----------|-------------| | `birthMonth` | `integer` | No | The birth month | | `birthDay` | `integer` | No | The birth day | | `birthYear` | `integer` | No | The birth year | | `password` | `string` | No | Password | ### Roblox.Users.Api.BirthdateResponse The birthdate response | Property | Type | Required | Description | |----------|------|----------|-------------| | `birthMonth` | `integer` | No | The birth month | | `birthDay` | `integer` | No | The birth day | | `birthYear` | `integer` | No | The birth year | ### Roblox.Users.Api.DescriptionRequest The description request | Property | Type | Required | Description | |----------|------|----------|-------------| | `description` | `string` | No | The description | ### Roblox.Users.Api.DescriptionResponse The description response | Property | Type | Required | Description | |----------|------|----------|-------------| | `description` | `string` | No | The description | ### Roblox.Users.Api.GenderRequest The gender request | Property | Type | Required | Description | |----------|------|----------|-------------| | `gender` | `1 \| 2 \| 3` | No | The gender ['Unknown' = 1, 'Male' = 2, 'Female' = 3] | ### Roblox.Users.Api.GenderResponse The gender response | Property | Type | Required | Description | |----------|------|----------|-------------| | `gender` | `integer` | No | The gender | ### Roblox.Users.Api.GetUserResponse A response model representing user information. | Property | Type | Required | Description | |----------|------|----------|-------------| | `description` | `string` | No | The user description. | | `created` | `string` | No | When the user signed up. | | `isBanned` | `boolean` | No | Whether the user is banned | | `externalAppDisplayName` | `string` | No | Unused, legacy attribute. For now always null to not disturb existing client code that might rely on its existence. | | `hasVerifiedBadge` | `boolean` | No | The user's verified badge status. | | `id` | `integer` | No | The user Id. | | `name` | `string` | No | The user name. | | `displayName` | `string` | No | The user DisplayName. | ### Roblox.Users.Api.MultiGetByUserIdRequest Request model for getting users by ids. | Property | Type | Required | Description | |----------|------|----------|-------------| | `userIds` | `integer[]` | No | The user ids. | | `excludeBannedUsers` | `boolean` | No | Whether the response should exclude banned users | ### Roblox.Users.Api.MultiGetByUsernameRequest Request model for getting users by usernames. | Property | Type | Required | Description | |----------|------|----------|-------------| | `usernames` | `string[]` | No | The usernames. | | `excludeBannedUsers` | `boolean` | No | Whether the response should exclude banned users | ### Roblox.Users.Api.MultiGetUserByNameResponse A response model specific to multi-get user by name. | Property | Type | Required | Description | |----------|------|----------|-------------| | `requestedUsername` | `string` | No | The username the user was requested with. | | `hasVerifiedBadge` | `boolean` | No | The user's verified badge status. | | `id` | `integer` | No | The user Id. | | `name` | `string` | No | The user name. | | `displayName` | `string` | No | The user DisplayName. | ### Roblox.Users.Api.MultiGetUserResponse

A response model specific to multi-get user.

Note: Currently the MultiGet responses don't have all the data that the single gets have, to reduce payload size! We might want to revisit this decision in the future.

| Property | Type | Required | Description | |----------|------|----------|-------------| | `hasVerifiedBadge` | `boolean` | No | The user's verified badge status. | | `id` | `integer` | No | The user Id. | | `name` | `string` | No | The user name. | | `displayName` | `string` | No | The user DisplayName. | ### Roblox.Users.Api.SearchGetUserResponse A user response model specific to getting a user from user search. | Property | Type | Required | Description | |----------|------|----------|-------------| | `previousUsernames` | `string[]` | No | Previous usernames for a user. | | `hasVerifiedBadge` | `boolean` | No | The user's verified badge status. | | `id` | `integer` | No | The user Id. | | `name` | `string` | No | The user name. | | `displayName` | `string` | No | The user DisplayName. | ### Roblox.Users.Api.SetDisplayNameRequest Request model for changing a display name. | Property | Type | Required | Description | |----------|------|----------|-------------| | `newDisplayName` | `string` | No | The users new display name. | ### Roblox.Users.Api.UserAgeBracketResponse A user age bracket response model. | Property | Type | Required | Description | |----------|------|----------|-------------| | `ageBracket` | `integer` | No | The age bracket of the user. | ### Roblox.Users.Api.UserCountryCodeResponse A user country code response model. | Property | Type | Required | Description | |----------|------|----------|-------------| | `countryCode` | `string` | No | The country code of the user. | ### Roblox.Users.Api.UserRolesResponse A user roles response model. | Property | Type | Required | Description | |----------|------|----------|-------------| | `roles` | `string[]` | No | The roles of the user. | ### Roblox.Users.Api.UsernameHistoryResponse | Property | Type | Required | Description | |----------|------|----------|-------------| | `name` | `string` | No | A past username belonging to a particular userId | ### Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Users.Api.MultiGetUserByNameResponse] | Property | Type | Required | Description | |----------|------|----------|-------------| | `data` | `Roblox.Users.Api.MultiGetUserByNameResponse[]` | No | | ### Roblox.Web.WebAPI.Models.ApiArrayResponse[Roblox.Users.Api.MultiGetUserResponse] | Property | Type | Required | Description | |----------|------|----------|-------------| | `data` | `Roblox.Users.Api.MultiGetUserResponse[]` | No | | ### Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Users.Api.SearchGetUserResponse] | Property | Type | Required | Description | |----------|------|----------|-------------| | `previousPageCursor` | `string` | No | | | `nextPageCursor` | `string` | No | | | `data` | `Roblox.Users.Api.SearchGetUserResponse[]` | No | | ### Roblox.Web.WebAPI.Models.ApiPageResponse[Roblox.Users.Api.UsernameHistoryResponse] | Property | Type | Required | Description | |----------|------|----------|-------------| | `previousPageCursor` | `string` | No | | | `nextPageCursor` | `string` | No | | | `data` | `Roblox.Users.Api.UsernameHistoryResponse[]` | No | |
--- title: "Courses" url: /docs/en-us/courses last_updated: 2026-06-29T19:34:17Z description: "Structured learning paths for creating on Roblox" --- # Courses --- title: "Create on Roblox" url: /docs/en-us/index last_updated: 2026-06-29T19:34:17Z description: "Learn how to create Roblox experiences with guides, tutorials, and code samples." --- # Create on Roblox ## ## [

](/docs/en-us/experiences.md) [

](/docs/en-us/avatar.md) [

](/docs/en-us/tutorials.md) [

](/docs/en-us/get-started/how-is-roblox-different.md)
--- title: "Monetize avatar items" url: /docs/en-us/monetize-avatar last_updated: 2026-06-29T19:34:17Z description: "An overview of virtual items and the Marketplace." --- # Monetize avatar items [Eligible](/docs/en-us/marketplace/marketplace-policy.md#creator-requirements) creators can sell [rigid accessories](/docs/en-us/avatar/rigid-accessories.md), [layered clothing](/docs/en-us/avatar/layered-accessories.md), and [avatar bodies and heads](/docs/en-us/avatar/character-bodies.md) in the Marketplace or in their own or other developers' experiences. ## Marketplace Millions of users visit the [Marketplace](https://www.roblox.com/catalog?Category=1&salesTypeFilter=1) every day, and it's where all users can find avatar items to purchase and equip to their avatar character. In the Marketplace, you can find character bodies and heads, clothing, accessories, animations, and other cosmetic items. Users can access the Marketplace [through the web](https://www.roblox.com/catalog) or within the Roblox App. You can increase the discoverability of your content within the Marketplace through [sponsored item](/docs/en-us/marketplace/sponsor-items.md) ads. > **Warning:** The Marketplace is not the same as the [Creator Store](/docs/en-us/production/creator-store.md). The Creator Store provides assets for creators to use for the development of their experiences, such as models, images, and plugins. ## In-experience Developers can also set up access to the Marketplace within their experiences by using the [Avatar Editor Service](/docs/en-us/players/avatar-editor.md). Developers receive a commission based of sales of Marketplace items within their experience. Users can purchase your item within an experience using the [Avatar Inspect Menu](/docs/en-us/players/avatar-inspect-menu.md) or in templates such as [UGC Homestore](/docs/en-us/resources/templates.md#ugc-homestore). ## Payouts #### 3D Avatar Assets ##### Marketplace purchase - Creator of the virtual item receives **30%** - Affiliate (Roblox) receives **40%** - Platform receives **30%** ##### In-experience purchase - Creator receives **30%** - Affiliate (experience owner) receives **40%** - Platform receives **30%** These commissions also apply to the original sale of [Limiteds](/docs/en-us/marketplace/marketplace-fees-and-commissions.md#limiteds). When setting assets on sale, you can [configure](/docs/en-us/marketplace/publish-to-marketplace.md#marketplace-settings) your creation as a Limited item to set an available quantity for that asset. Limiteds require a [per-unit fee](/docs/en-us/marketplace/marketplace-fees-and-commissions.md#per-unit-fee) and can be [resold](/docs/en-us/marketplace/marketplace-fees-and-commissions.md#reselling) by [Roblox Plus](https://www.roblox.com/plus) members. At this time, only Roblox-created Limiteds are tradeable. ![Pie chart illustrating the commission structure for 3D clothes and accessories](./assets/monetization/earning/Percentages-Clothes-Accessories.png) #### Community-created Limiteds While anyone can buy a Limited item, only [Roblox Plus](https://www.roblox.com/plus) subscribers can resell them. Every time a creator's Limited item is resold, the creator benefits from a 10% original creator commission on the value of each resale. - Reseller receives **50%** - Creator receives **10%** - Affiliate receives **10%** - Platform receives **30%** After purchasing a Limited, there is up to a 30‑day holding period when the item cannot be resold. Robux acquired from trading or selling items that you didn't create are not considered earned and are ineligible for the [Developer Exchange](https://create.roblox.com/dashboard/devex) program. ![Pie chart illustrating the commission structure for community-created Limiteds](./assets/monetization/earning/Percentages-Reselling-Limiteds.png) Create, upload, and sell virtual items in the Marketplace, a centralized platform for buying and selling avatar items. Avatar items let users personalize their avatar characters with clothing, accessories, and animations. --- title: "Monetize your experiences" url: /docs/en-us/monetize-experiences last_updated: 2026-06-29T19:34:17Z description: "An overview of monetization tools for experiences." --- # Monetize your experiences Building successful content takes time and effort, but there is low risk and low upfront cost to building an experience on Roblox. You pay **after** earning on Roblox and we provide the tools, services, and support to help you build the most immersive experiences. Roblox has a robust community and [developer forum](https://devforum.roblox.com/) to guide and support you along the way. For a complete list of benefits to monetizing on Roblox and all monetization paths on the platform, see [Turn your creativity into income on Roblox](/docs/en-us/monetize.md). ## In-experience monetization **Monetization features** integrate with your experiences to generate revenue. Unlike promotional or advertising features, monetization features focus on creating income streams instead of just visibility and engagement. | Feature | Description | | --- | --- | | [Creator rewards](/docs/en-us/creator-rewards.md) | Automatically earn Robux when Active Spenders engage in your experience. These payouts happen automatically as a way to reward engaging experiences. | | [Private servers](/docs/en-us/production/monetization/private-servers.md) | Let users choose who can pay to play an experience with them. | | [Subscriptions](/docs/en-us/production/monetization/subscriptions.md) | Offer users recurring benefits for a monthly fee. | | [Passes](/docs/en-us/production/monetization/passes.md) | Special privileges within an experience that users can purchase once, like entry to a restricted area, an in-experience avatar item, or a permanent power-up. When you sell your own passes in your experience, you earn 70% of the Robux spent. You can also sell passes within your experience that were made by other creators; in this case, you earn a 10% affiliate fee on the Robux spent within your experience, and the original creator of the pass earns 60%. | | [Developer products](/docs/en-us/production/monetization/developer-products.md) | Items or abilities that users can purchase more than once, like in-experience currency, ammo, or potions. | | [Paid access in Robux](/docs/en-us/production/monetization/paid-access-robux.md) | Charge users a one-time fee in Robux to access your experience. | | [Paid access in local currency](/docs/en-us/production/monetization/paid-access-local-currency.md) | Charge users a one-time fee in their local currency to access your experience. | | [Price optimization](/docs/en-us/production/monetization/price-optimization.md) | Find the best price points for your passes and developer products. | | [Avatar items](/docs/en-us/production/monetization/avatar-items.md) | Sell avatar items directly inside your experiences. | | [Avatar creation tokens](/docs/en-us/production/monetization/avatar-creation-token.md) | Let users spend tokens to create, purchase, and save avatar items directly from within an experience. | ## Advertising **Advertising features** are explicit advertising tools aimed at reaching a specific audience. They function like traditional ads and their purpose is to generate engagement and revenue. | Feature | Description | | --- | --- | | [Ads Manager](/docs/en-us/production/promotion/ads-manager.md) | A centralized platform where you can create and measure ad campaigns and streamline your promotional efforts. | | [Roblox-served ads vs. independent ads](/docs/en-us/production/promotion/advertise-on-roblox.md) | Differentiate between the Roblox-served ads and the independent ads you can implement into your experience. | | [Immersive ads](/docs/en-us/production/monetization/immersive-ads.md) | Integrate video ads, image ads, and portal ads to show users advertisements without disrupting gameplay. | | [Search ads](/docs/en-us/production/promotion/ads-manager.md) | Target specific keywords revelant to your experience and increase the likelihood of discovery through search results. | --- title: "Turn your creativity into income on Roblox" url: /docs/en-us/monetize last_updated: 2026-06-29T19:34:17Z description: "Learn how to monetize on Roblox." --- # Turn your creativity into income on Roblox Millions of creators come to Roblox to build games, immersive experiences, avatar items, and more. Roblox makes it easy to publish, find your audience, and monetize your creations. [Explore monetization](#why-build-on-roblox) # Why build on Roblox? You need a platform that offers more than just tools—you need a vibrant community, a clear path to monetization, and the speed to keep up with player demand. Roblox offers distinct advantages, giving creators the best chance to succeed and scale. ### Speed to market and development Roblox's **developer-friendly ecosystem** is designed for velocity, offering a significant advantage in **speed to market** and live operations. **Low upfront costs**We provide the tools, services, and support to help you build the most immersive experiences. When working with other platforms, you may have to pay for storage, hosting, and infrastructure before you earn any money or even start building. On Roblox, you're only charged for these expenses after you monetize. **Low barrier to entry**Roblox Studio provides an accessible, unified development environment to build, script, and test your games allowing you to focus on gameplay and publish experiences much faster compared to other top engines. **Rapid iteration**Our platform is built for live-service updates and rapid iteration, enabling you to quickly respond to community feedback, fix bugs, and deploy new content. **Cross-platform by default**Games built on Roblox are automatically accessible across PC, Mac, mobile, console, and select VR hardware, helping you reach players wherever they are. Roblox provides you with APIs and tools to optimize UI layouts for different input methods and screen sizes, streamlining your development pipeline. _Free creation tools like Roblox Studio_ ### Discovery Roblox boasts one of the world's largest, most engaged, and fastest-growing communities, offering creators an unparalleled opportunity to find and scale their audience. **Massive and engaged global audience**132 million daily active users (DAUs) spend an average of 2.6 hours on Roblox per day1, signifying deeper platform engagement compared to the more event-driven spikes often seen on competitor platforms. **Diverse and growing demographics**Roblox is home to users across over 180 countries with a diverse range of interests that appeals to a broad range of genres and brands. **Instant publishing**Unlike other platforms where you wait days or weeks for approvals, you can publish your experiences on Roblox immediately in multiple languages across iOS, Android, Windows, Mac, Xbox, PlayStation, Chromebook, and select VR hardware. _1Data reflect Q1 2026_ _Instant publishing on multiple platforms_ ### Business opportunity Turn your creations into a sustainable business with Roblox's robust, proven, and diversified monetization engine. **Established monetization channels**Monetize through [in-experience purchases](/docs/en-us/production/monetization/developer-products.md), our most significant revenue driver; [Creator Rewards](/docs/en-us/creator-rewards.md), which lets you earn extra Robux for building an engaging experience; [in-experience advertising](/docs/en-us/production/promotion/advertise.md), where you can deploy ready-made ad units with existing demand; and [models and plugins](/docs/en-us/production/creator-store.md) on the Creator Store, which lets you earn 100% of net proceeds on transactions. **Proven creator success**In 2025, our top 1,000 creators earned an average of $1.3 million, and this is growing 50% YoY2—demonstrating the stability and scale of our monetization system. **Our success is tied to yours**Roblox earns money by selling Robux which users spend in experiences and on items and assets in our marketplaces, all created by you. Some platforms are actually also creators on their platform and therefore compete with their creators for payouts. Roblox does not. **Total size of the payout pool**We don't cap our payout pool, and our virtual currency can be converted to real currency by eligible creators through our DevEx Program, so creators always know how much they're earning. Also, other platforms may invest less in growing their user base and making their products accessible in new markets across the globe. This may allow them to pay developers a higher share, but at the expense of slower growth and less total earnings being put in creators' pockets. **Developer Exchange (DevEx)**Eligible creators can convert their earned Robux into real-world currency, providing a direct, reliable pathway from creative work to financial success. _2Data as of December 31, 2025._ _Immersive video ad_ # How to earn on Roblox Roblox offers many ways to earn. Whether you're working on a hobby game or running a full-fledged studio, designing and selling avatar fashion, or building assets and tools for other creators, there are monetization paths that cater to every talent. [Creator Rewards](/docs/en-us/creator-rewards.md) Creators can get paid for the engagement they bring to Roblox, even for those just getting started. [Roblox Plus](/docs/en-us/production/monetization/roblox-plus.md) Creators can get paid for the engagement they bring to Roblox, even for those just getting started. [In-experience items](/docs/en-us/production/monetization/developer-products.md) Earn Robux by offering in-game currencies, power-ups, cosmetics, and items. [Developer subscriptions](/docs/en-us/production/monetization/subscriptions.md) Offer your most engaged players recurring benefits for a monthly fee, payable in Robux or local currency. [Paid access](/docs/en-us/production/monetization/paid-access-robux.md) Creators with a dedicated fanbase can charge a one-time fee in Robux or local currency to access your experience. [Avatars](/docs/en-us/marketplace/publish-to-marketplace.md) Create and sell avatars, avatar accessories, and animations in your experience or on the Marketplace. [Models and plugins](/docs/en-us/production/creator-store.md) Create and sell models and plugins to other creators on the Creator Store for use in their own development. [Ads](/docs/en-us/production/promotion/advertise.md) Monetize more of your users by publishing in-experience ads like [Rewarded Video](/docs/en-us/production/promotion/rewarded-video-ads.md) and [Billboards](/docs/en-us/production/monetization/immersive-ads.md). [Commerce](/docs/en-us/production/monetization/commerce-products.md) Let users buy physical goods directly from your experience with native checkout integration. Roblox provides the development tools, infrastructure, and technology so that you can focus on what matters most: creating. Creators generally earn 70% of anything they sell in Robux in their experience, 30% of anything they sell in the Marketplace, and up to 90% for things they sell in real money (such as plugins or paid access purchases). Eligible creators can then convert their earnings to real money through our [Developer Exchange (DevEx) program](https://en.help.roblox.com/hc/en-us/articles/203314100-Developer-Exchange-DevEx-Overview-How-to-Submit-Requirements). The rest of the funds cover: - **Important Roblox programs** like Creator Rewards. - **App store and payment processing fees** related to purchases and refunds of Robux. - **Platform hosting and support**, which includes maintaining the servers that host all of the experiences on Roblox, customer support, user and experience moderation, user acquisition, translation, and local compliance. If you develop outside of Roblox, you may have to manage and pay for these things on your own. On Roblox, you can focus on building your experience. # How to cash out Creators who are 13 or older and have earned at least 30,000 Earned Robux can turn their Earned Robux into real money using our [Developer Exchange (DevEx)](https://en.help.roblox.com/hc/en-us/articles/203314100-Developer-Exchange-DevEx-Overview-How-to-Submit-Requirements). Earned Robux can be exchanged for your local currency at a fixed rate provided you meet the program's requirements, so you always know how much you've earned and how much you can re-invest into your business. # 10,000 Robux # = # $38 USD # Success Stories ### Cole Tucker Cole Tucker, known on Roblox as [1Coal](https://www.roblox.com/users/451049939/profile), started on Roblox as a hobbyist experimenting with simple game ideas. His breakthrough came with _Hide or Die_, a prototype he tested early and refined based on player feedback. The experience quickly scaled to one million active users and generates around $45,000 per month. As revenue grew, Cole partnered with a programmer and turned his solo passion into a full-fledged studio. "Roblox is basically YouTube for video games," he says. "Anyone can create here." What began as play is now a thriving, full-time business. ### Philipp Batura Philipp Batura, known on Roblox as [Topcat](https://www.roblox.com/users/1623385497/profile), is a Brazil-based creator behind some of the platform's most successful digital fashion brands, including [Gatas Only](https://www.roblox.com/communities/34563924/Gatas-Only#!/about), [Coast UGC](https://www.roblox.com/communities/17022012/Coast-UGC#!/about), and [Chibi Couture](https://www.roblox.com/communities/34603457/Chibi-Couture#!/about). He first joined Roblox during the pandemic, initially hoping to make a game. After discovering the avatar program, he posted a few items for fun – until one unexpectedly went viral. That moment pushed him to take digital fashion seriously. Within months he was covering production costs, and soon after he hired his first team member. Today, his team releases hundreds of items monthly and drives more than 1.5 million sales each month. ### Kyasia Watson Kyasia Watson, known on Roblox as [cSapphire](https://www.roblox.com/users/17839173/profile), began creating on Roblox in 2013 at just 12 years old, driven by a desire to design clothes she couldn't find in the Marketplace. What started as a creative outlet grew into a sustainable fashion business as players began buying her designs. Over time, Kyasia taught herself digital fashion design and evolved her work as Roblox's creator tools expanded. Today, she's an award-winning fashion designer and 3D artist who earns through a mix of brand partnerships and direct avatar item sales, collaborating with global brands like Gucci, Tommy Hilfiger, and Barbie. Kyasia's advice to new creators: "Keep learning and stay creative. The work you put in today can pay off later." ### Alexander Hicks Alexander Hicks, known on Roblox as [AbstractAlex](https://www.roblox.com/users/4402987/profile), began creating on Roblox as a teenager, drawn to how quickly he could turn ideas into playable experiences. His breakout hit _Robloxian High School_ gave him the confidence to formalize his work and co-found full-time studio RedManta, where he treated game development like a real business—hiring collaborators, refining production, and reinvesting revenue into higher-quality games. RedManta later merged with another top team to form Twin Atlas, now a 100-person studio behind _Creatures of Sonaria_ and _Drive World_. As Alex puts it, "Roblox makes it easy to find your niche, and then you'll meet others that you work well alongside." _* Of the millions of creators monetizing on Roblox, over 35,000 are part of our DevEx program, with the median creator receiving $1,550 USD during the twelve months ended December 31, 2025._ # FAQs and Support **Who is eligible to earn on Roblox?** Creators who follow our [Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards) are eligible to earn and spend Robux on the platform. Creators over the age of 13 with at least 30,000 in Earned Robux are eligible to turn their Earned Robux into real money through our [Developer Exchange (DevEx) program](https://en.help.roblox.com/hc/en-us/articles/203314100-Developer-Exchange-DevEx-Overview-How-to-Submit-Requirements). **Why do other platforms advertise giving a higher share to their creators?** Every platform reports their creator share a bit differently. When you compare creator shares across platforms, you should consider: - **The breadth of business models:** While some platforms may have a high creator share, the opportunities to earn and the ways to earn may be limited, making the absolute earning potential on the platform smaller. - **The platform may also be competing for payouts:** Some platforms are actually also creators on their platform and therefore compete with their creators for payouts. Roblox does not. - **Other platform benefits:** Creator shares can be high, but consider other benefits that the platform provides such as costs that they are paying on your behalf, the scale and diversity of their audience, the devices/platforms they are available on, the types of creators that are earning, and more. - **The complexities of cross-platform development:** Developing simultaneously across multiple platforms, each with their own regulations and technical requirements, can be a major hurdle, especially for smaller teams. Roblox, meanwhile, eliminates the friction of cross-platform development, enabling creators to publish their experiences, fix bugs, or deploy new content instantly across PC, Mac, mobile, console, and select VR hardware. - **The total size of the payout pool:** Understand the total size of the platform's payout pool. Other platforms may invest less in growing their user base and making their products accessible in new markets across the globe. This may allow them to pay developers a higher share, but at the expense of slower growth and less total earnings being put in creators' pockets. Creator Hub Manage your experiences, track analytics, and access creator tools in one place. Safety Center Learn about safety features, parental controls, and how we protect our community. Community Standards Understand the rules and standards that keep Roblox safe and fun for everyone. Help Center Find answers to common questions about Roblox. --- title: "Balance virtual economies" url: /docs/en-us/production/game-design/balance-virtual-economies last_updated: 2026-06-29T19:34:17Z description: "Balancing Virtual Economies" --- # Balance virtual economies Virtual economies on Roblox are intricate systems that mimic real-world economies, where players trade, earn, and spend virtual currency and resources. Balancing these virtual economies is vital to ensuring a fair and engaging experience that keeps players motivated without over or under-valuing the virtual goods. Balanced virtual economies have the following best practices: - Data-driven content - Resource planning - Impact estimation - Resource sinks > **Warning:** Use [Experiments](/docs/en-us/production/experiments.md) to A/B test different price points and discount strategies to find your shop's "Goldilocks zone." Once you identify the most effective prices, leverage [Configs](/docs/en-us/production/configs.md) to trigger flash sales or balance your virtual economy in real-time without restarting your servers. ## Data-driven content Using data to understand how players interact with and consume your experience's content is key to maintaining a balanced economy. When building your experience's database, be sure to track: - Who is buying what item. - What actions yield currency. - What amount of currency is earned vs. purchased. - Whether earned or purchased currency is used for item purchases. - When and what items are used. - How frequently players interact with specific features and the timing of those interactions. These data points are crucial considerations when planning events. While running events can help keep players engaged between major content updates, it's essential to prioritize data tracking for in-game actions, content consumption, item sales and item usage. To learn more about planning and creating events, see [Content updates](/docs/en-us/production/game-design/content-updates.md) and [LiveOps essentials](/docs/en-us/production/game-design/liveops-essentials.md). ## Resource planning **Resource planning** involves balancing how virtual resources are created and how they're spent. This concept is best illustrated in the fictitious experience _Feline Fishing Fun_, where players fish for their resources. _Feline Fishing Fun_ has the following characteristics: - The currency used to purchase items in the shop is called Gold `(G)`. - Gold is earned by catching fish of various rarities and values and selling them to vendors. - Gold packs can also be purchased in the Shop. - There are three types of fish of differing rarity and value that players have a chance to catch in the river: - Catfish: `70% chance to catch, 10G` - Trout: `20% chance to catch, 20G` - Salmon: `10% chance to catch, 30G` _Feline Fishing Fun_ developers are planning an event where a special Golden Salmon will become available for 24 hours, with 0.1% chance of being caught, and be worth 1000 Gold. This type of event entices players to fish for a chance to acquire it and integrates smoothly with the experience's [core loop](/docs/en-us/production/game-design/core-loops.md) of fishing. If the developers launch the event without estimating the economic impact of increased fishing, data visualization reveals that the quantity of Gold purchased from the store remains flat, while spending surges. This underestimation of increased fishing leads to a significant surplus of Gold in the virtual economy. As a result, players with abundant Gold spend freely, leaving little incentive to purchase more, which causes a decline in revenue after the event ends. _An unbalanced event in _Feline Fishing Fun_._ ## Impact estimation Estimating the potential impact of an event on an experience's economy is paramount to ensuring balance. A common method to gauge potential impact is through **Expected Value** (EV) calculations. An item's EV is defined as a predicted value of a variable, calculated as the sum of all possible values, each multiplied by the probability of its occurrence. Understanding Expected Value is critical to maintaining a balanced economy any time there is value to be gained from a probabilistically based system, such as _Feline Fishing Fun_'s fishing system. Below are images of a simple EV calculator set up in a spreadsheet. This is a standard format that can be applied to conceptually similar situations. _Non-event EV calculator spreadsheet._ If there are a group of valuable items a player has a chance of obtaining, multiply the value of that item by the percent chance of acquiring it. For _Feline Fishing Fun_'s fishing system, this looks like: - Catfish = `10 Gold x 70% = 7G` - Trout = `20 Gold x 20% = 4G` - Salmon = `30 Gold x 30% = 3G` Adding up the results, the EV of a single fishing attempt is `7G + 4G + 3G` = `14G`. By multiplying 14G by 1,000 fishing attempts per day, you get 14,000 Gold earned from fishing on a normal non-event day. _Event modified EV calculator spreadsheet._ While it's impossible to know exactly how many players will participate on a given day of an event, each successive event aids in future estimations. For _Feline Fishing Fun_'s fishing event, when determining the appropriate Gold Salmon value, add it to the EV calculator and compare the resulting EV to the EV of a non-event day. By understanding normal item purchasing pace and the time it takes players to buy useful items, you can determine how much currency is too much. ## Resource sinks A **resource sink** is an economic system designed to remove resources from circulation. When designing new sources of currency, it's imperative to have plans for how to "sink" different quantities of excess currency. Common examples include: - Special event cosmetic items, like limited time golden accessories. - A sequential event that requires the spending of currency earned in the event prior. - An **event shop** that appears during events that grant a small benefit to players who spend currency while the event is ongoing. The safest solution to safeguard an experience's economy is to have an event generate a unique event currency that is only used to purchase items from a special event shop. This ensures that the experience's main economy remains untouched. Resource sinks ensure that players have a fun and rewarding time during the event, while also maintaining the value and integrity of the existing currency. A balanced Golden Salmon event in _Feline Fishing Fun_ will look like the chart below, with Gold sources and sinks relatively elevated during the event, but moving close together. _A balanced event in _Feline Fishing Fun_._ --- title: "Contextual purchases" url: /docs/en-us/production/game-design/contextual-purchases last_updated: 2026-06-29T19:34:17Z description: "Contextual Purchases" --- # Contextual purchases **Contextual purchases** are strategic events that occur outside an experience's [shop](https://create.roblox.com/docs/production/game-design/monetization-foundations) that prompt players to purchase items. Contextual purchases are designed opportunistically, operating under a "right item, right time" framework, where designers anticipate the wants of their players at key stages of the experience and provide a purchasable solution. Approaches to contextual purchases can be divided into the following categories: - In-play - Pre-play - Lobby - Complementary - UI ## In-play **In-play** contextual purchases refer to presenting a purchase opportunity when a player is actively participating in an in-experience activity. Carefully consider the timing of any in-play contextual purchases to avoid negatively impacting a player's session. [Color or Die](https://www.roblox.com/games/12931609417/Color-or-Die) does this well by presenting a contextual purchase opportunity after a player dies. In Color or Die, if a player dies they can respawn and lose a portion of their progress, or they can purchase an extra life and continue without any penalty. _In-play contextual purchase opportunity._ _"Buy Robux" purchase flow._ This approach mimics the "continue countdown" option used by traditional arcade games. Make sure that your in-play contextual purchase opportunities account for players having time to finish the entire "buy Robux" purchase flow, which can take several seconds to complete. If it isn't feasible to design gameplay pauses to accommodate these transactions taking place, in-play contextual purchases might not be appropriate for your experience. ## Pre-play **Pre-play** contextual purchases refer to presenting a purchase opportunity when a player is waiting to participate in an in-experience activity. This often occurs in instance-based experiences where gameplay occurs in isolated and repeated loops. [Doors](https://www.roblox.com/games/6516141723/DOORS) does this well by presenting players with pre-play contextual purchase opportunity before each round in the Pre-Run Shop. In _Doors_, the shop in the lobby only sells Knobs, the in-game currency, explicitly telling players that it's "used to purchase items before each run". Before each run, after exiting the lobby, players enter an elevator, where they're presented with an opportunity to spend their Knobs on consumable items that will aid them in the coming instance of gameplay. The Pre-Run Shop clearly states the benefits of each item, and is intentionally placed to provide a convenient and focused pre-play contextual purchase experience. __Doors_ Lobby shop_ _Pre-play contextual purchase opportunity_ ## Lobby **Lobby** contextual purchases intentionally leverage the social aspect of a lobby to incentivize players to purchase items. This is often done by having the shop be a 3D object in the experience that advertises wares, sales, and store related events to multiple players at once. This technique can be seen in [Super Striker League](https://www.roblox.com/games/3360853050/Super-Striker-League), where the lobby is frequently full of players. __Super Striker League_ Lobby._ If utilizing lobby contextual purchases in your experience, ensure your players understand whether the items displayed in the 3D object are all of the items available for purchase. If additional items are available for purchase elsewhere, consider having the lobby contextual purchase interaction open up the main shop of the experience. _Lobby contextual purchase opportunity._ _Full _Super Striker League_ shop._ ## Complementary **Complementary** contextual purchases are opportunities for players to purchase multiple items that have related effects. In [Bee Swarm Simulator](https://www.roblox.com/games/1537690962/Bee-Swarm-Simulator), players collect pollen to convert into honey. The store sells a Bee Pollen Pass, which enables players to collect twice the normal amount of pollen, and offers a Honey Speed Pass right below it, which enables players to convert pollen into honey at twice the rate. __Bee Swarm Simulator_ shop._ A complementary contextual purchase opportunity would be to prompt the player to also purchase the other pass they didn't initially choose, due to the passes' complementary nature. Communicating the complementary nature of two or more items in a shop can also be helpful for newer players learning about the experience. Be thoughtful about how many sales prompts you decide to use. Using this strategy for every possible complementary item might distract from players' browsing experience and from their immersion in the environment. To alleviate the risk of overusing complementary messaging, consider packaging some of the items together in a [Bundle](/docs/en-us/production/game-design/monetization-foundations.md#bundles). ## UI Contextual cues in the **UI** can take a variety of forms and can be versatile tools in recommending purchases. Common UI purchase opportunities are communicated through badges, event windows, and announcements. _UI shop badge in _BotClash Simulator_._ Loading screens offer prominent space to display a purchase opportunity when players' attention is focused and not yet distracted by other UI elements or gameplay. If you choose to use a space like this for purchase offers, make sure you also provide sufficient context for the player to properly gauge the value of the item. It wouldn't be appropriate to have a [Starter Pack](/docs/en-us/production/game-design/monetization-foundations.md#starter-packs) appear on the loading screen the very first time a player loads the experience, but it could be become appropriate in future sessions. _Loading screen purchase opportunity in _Adopt Me_._ A basic modal offer is also commonly used to promote items and features. In _Adopt Me_, the player is offered a Modern Mansion when looking around their basic house after starting their play session. When using modals like these, be sure to implement logic that prevents multiple modals from layering on top of each other so your UI remains clear and effective. _Modal offer in _Adopt Me_._ For more about UI and UX design, see [UI and UX](/docs/en-us/production/game-design/ui-ux-design.md). --- title: "Monetization foundations" url: /docs/en-us/production/game-design/monetization-foundations last_updated: 2026-06-29T19:34:17Z description: "Monetization foundations" --- # Monetization foundations _Introduction_ Monetization is an integral part of your experience's initial and ongoing development. Monetization features are some of the most prominent content interaction spaces and directly impacts an experience's monetary growth potential. There are 5 key components that comprise monetization foundations: - Items - The shop - Merchandising - Starter packs - Season passes Taken together, these components can foster a thriving economy within your experience, maximize your experience's monetary growth potential, and foster a strong relationship with your players. For a comprehensive list of key monetization terms, see the [glossary](#glossary). ## Items _Items and shop introduction_ _Items_ **Items** are individual pieces of content available to earn or purchase. The essence of monetization can be represented in three basic questions: - What is being sold? - Where is it being sold? - How is it being sold? When deciding what kind of items to create, first determine what items makes sense for your experience. You know your players best, and you hold the creative vision for the experience you want them to have. Be aware if your players are competitive, collaborative, casual, or experienced. Know if your experience is easy to learn or if it require more time to get the most out of the content you've created. Most importantly, determine what purpose each item you sell should serve. If a player purchases an item they should find value in it and be able to easily identify its relevancy to the experience. In [_Doors_](https://www.roblox.com/games/6516141723/), players explore dark hallways and rooms, and can purchase a flashlight before starting a session. The flashlight is then used to light up the darkness and aid the player in exploration. This tangible and immediately understood value seamlessly integrates into the experience and encourages players to purchase it. When describing your items for purchase, it's important to be accurate and truthful in your descriptions so players know exactly what they're buying and the value it provides. _Flashlight in _Doors__ Purchasable items can be divided into two categories: - **Durable items:** Items that can be used or applied an unlimited number of times, such as cosmetic skins._Skin in [_Tower Defense Simulator_](https://www.roblox.com/games/3260590327/)_ - **Consumable items:** Items that have limited uses, such as temporary boosts._Consumable item in [_Evade_](https://www.roblox.com/games/9872472334/)_ They can then be further divided into categories of utility: - **Enhancement:** These items improve the experience in some way, such as granting increased speed, protection, strength, tools or event mode access. - **Expression:** These items personalize the player's experience and makes them unique, such as skins, emotes, and pets. ## The shop _The shop_ The **shop** is where players go to enhance and personalize their experience through the purchasing of virtual items. The shop should be as interesting and intentionally designed as other parts of your experience. The shop is one of the first places players start to learn more about an experience's economy and its variety of content, and serves as an information hub about current, new, and upcoming content. It also provides feedback for you to learn what type of purchasable content your players enjoy. A good shop is: - Integrated - Contextual - Inviting ### Integrated To be effective, a shop needs to be effectively integrated within the experience and easy to find. The shop icon should be consistent with the rest of your UI design and overall environment. Opening up the shop should be a seamless transaction from the interaction and gameplay experience, with players able to jump in and out of the shop quickly without feeling disrupted. _Shop UI in _Doors__ ### Contextual A good shop is contextual. If a new player only knows the name and price of an item without the surrounding context of other purchasable options, they won't know what kind of value it provides compared to other items in the experience. The design decisions of organizing the items in your shop keeps players informed and ensures that potential customers know what they're looking at, why it's useful, and how they might utilize it and enjoy it if they were to purchase it. An example of this is seen in _Doors_: - **Future anticipation:** Players are alerted that "Knobs", the experience's in-game currency, is receiving more use cases in the future. This alerts players that additional content is coming to utilize the in-game currency, which both builds anticipation for a future release and increases the perceived value of the product. - **Present explanation:** Players are given the explanation and context of purchasable "Revives". This alerts players that reviving is a core part of the gameplay experience and that this functionality is limited. As players navigate the shop, explanations like this help players accumulate knowledge about the experience._Shop in _Doors__ ### Inviting A good shop is inviting. Compare the shop in your experience to a brick and mortar mall. A good shop is a destination that provides options, atmosphere, and its own form of entertainment as a place to hang out and learn about different products. Design your shop as a place to linger and browse. Consider what you want players to do when they enter your experience each time before they engage with your primary content. If there is a lobby area, consider what kind of interactions players have with each other and the other parts of the UI that could encourage them to visit the store. New content and item updates invites players back to the store to see what's new. When players know that new items will be in stock the next day, or at some point in the near future, they're likely to return. Rotating shop items can be an exciting addition to your content strategy and help keep players interested in your experience. _Shop in [_Dragon Adventures_](https://www.roblox.com/games/3475397644/)_ ## Merchandising _Merchandising_ **Merchandising** is how players become aware of what's being sold in the shop. Merchandising is how you: - Attract attention - Communicate value ### Attract attention How you attract attention to your shop and items depends on your experience's unique style. [_Pet Simulator X_](https://www.roblox.com/games/6284583030/) uses descriptive language to build excitement. _Shop in _Dragon Adventures__ Interesting imagery using creative art and animations can make each purchase experience memorable and worthwhile for your players, but be careful not to overuse terms like "exclusive" or "limited" in order to maintain their effectiveness over time. ### Communicate value You can effectively communicate the value of items in your shop by employing the following techniques: - Clear language and imagery - Chance-based merchandise - Bundles #### Clear language and imagery Clear language and imagery help show why purchasing an item is worthwhile. It's important to clearly show the benefit of the item, whether it's a percentage bonus, percentage savings or number of uses. [_MeloBlox Adventure_](https://www.roblox.com/games/5803957966/) is very clear and organized, leading with the term "VIP Benefits" at the top and listing the extensive number of benefits players can expect to receive. _VIP rewards in _MeloBlox Simulator__ Discounts and bonuses are another example of clear language and imagery used to communicate value. In this example, you can see that the value of this special offer is shown with the original price of 3000 Robux struck through and the special offer price of 799 Robux. The change in price suggests the purchase is at a deep discount. A similar technique is used in [_The Survival Game_](https://www.roblox.com/games/11156779721/), with the store specifying the percentage bonus of additional coins in each increasing bundle. _Shop in [_Car Dealership Tycoon_](https://www.roblox.com/games/1554960397/)_ _Shop in _The Survival Game__ Another example of using clear language and imagery to communicate value is through using limited availability, which draws attention to an item or shop category that is only available for a short time. The brevity of availability increases a perception of value, but make sure this approach is used selectively to avoid reducing its effectiveness. _Shop in [_World // Zero_](https://www.roblox.com/games/2727067538/)_ In all cases, ensure that your merchandising terms and item descriptions are truthful and accurate so players know exactly what they will receive. Be mindful to not "oversell" items and remember that you are building a relationship with your players that depends on obtaining and maintaining trust. #### Bundles Bundles are a great way to package up items that go together, whether it's a group of items that new players would find appealing or items that share a common theme. Because a bundle has multiple items, ensure players can clearly see each one and let them know what each item does. As best practice, have a comparison to other bundles to gauge the value of grouped items. > **Info:** For out-of-the-box functionality to create bundles, see the [Bundles](/docs/en-us/resources/feature-packages/bundles.md) feature package. ## Starter packs _Starter packs_ The **starter pack** is a limited time purchase option presented to new players who first join an experience. It's a player's first look at the items in the shop the developer believes are relevant to early gameplay. It's also a new player's first interaction with Robux exchange rates and pricing. The starter pack can give new players a positive impression of the overall monetization strategy and provide key contextual information needed to understand the experience. Keep in mind that you're asking players for money very soon after their initial joining. Take care to begin building a customer relationship that ensures your players will derive an obvious benefit from their first purchase. Common durations for starter packs are anywhere from a day to a week, depending on the experience's design. This natural limitation makes starter packs an ideal candidate for applying of merchandising best practices, such as: - Prominent locations - Thoughtful designs - Contextual descriptions - Price calculations ### Prominent location For a beginner that is not yet familiar with your experience, showcase the starter pack in a place that is easy to find. If you place it prominently, consider how much information you need to provide so the purchase makes sense to the player who might not know anything about the experience. _Starter pack in [_My Restaurant!_](https://www.roblox.com/games/4490140733/)_ There are several good places to surface the starter pack in your UI. Clicking into the starter pack icon can open the offer directly, or open the offer in the context of the experience's shop. ### Thoughtful design Once you decide how players will see the starter pack, consider the starter pack visualization. Opening the offer in the shop encourages players to get familiar with the shop in general and can serve as a natural transition to exploring all the options available for purchase. _Starter pack in [__Sword Fighters Simulator__](https://www.roblox.com/games/11040063484/)_ ### Contextual descriptions Contextual descriptions tell the player why the items are significant and the value they provide. Intentionally designing descriptions to teach players about the impact of the items on the gameplay can serve as a tutorial before they even start playing. _Contextual description in __Sword Fighters Simulator___ ### Price calculations The starter pack should be merchandised as a special deal using price calculations to help show the item's value. Make sure when assembling the bundle to calculate the cost of each individual item visually, clearly indicating the kind of discount players will get. This helps players understand the value of the presented item compared to other items in the shop. _Price calculation in _Sword Fighters Simulator__ ## Season passes _Season passes_ **Season passes** are limited-time, quest-based progression systems that are part of an experience's content cadence. Players complete quest objectives to earn rewards for the duration of the pre-defined period of time or "season". To learn how to design an effective season pass, see [season pass design](/docs/en-us/production/game-design/season-pass-design.md). A good season pass includes the following characteristics: - Item shop best practices - Free and premium passes - Attractive rewards - Manageable timeframe - Progression system ### Item shop best practices A good season pass follows item shop best practices. It's content is: - Integrated - Contextual - Inviting #### Integrated The season pass should be easy to find and integrated into the aesthetic of the experience. Consider whether your visual affordance is clear enough for a new player to know that the UI element refers to your season pass specifically and not your entire shop. _Season pass in [_Bot Clash Simulator_](https://www.roblox.com/games/9300407930/)_ #### Contextual A good season pass is contextual and organized, clearly communicating the rewards available to players and how to achieve them. Describing item rewards in relation to gameplay and contextualizing them to other virtual items or mechanics helps players understand a reward's value and why they should try to earn them. Consider stating exactly how much time remains in the season for players to earn rewards to avoid confusion or missed opportunities. _Season pass in _Bot Clash Simulator__ #### Inviting Good season passes are inviting. As a subset of an experience's shop, season passes should present the player an opportunity to linger and gleam a deeper understanding of an experience's core loop. The rewards available from a season pass should inform the player of what items or mechanics are at the heart of an experience, inviting them to explore the extent of rewards available at the furthest ends of the reward tracks. _Descriptive and enticing rewards in _Bot Clash Simulator__ ### Free and premium passes Good season passes have both a free and premium pass that present opportunities for players to earn rewards. The free pass is a set of rewards which any player can earn by engaging with the missions or tasks provided. The free season pass often serves as a more advanced version of daily quests. The premium pass is a superset of the free pass rewards and enables paid players the opportunity to earn even more. Having both free and premium passes ensures that non-paying players can still earn rewards and enjoy your experience, while rewarding paying players with worthwhile bonuses. _Free and premium passes in [_Jailbreak_](https://www.roblox.com/games/606849621/)_ For more about free and premium passes, see [season pass design](/docs/en-us/production/game-design/season-pass-design.md#season-tiers). For more about daily quests, see [quest design](/docs/en-us/production/game-design/introduction-to-quest-design.md). ### Attractive rewards A good season pass has worthwhile rewards for players to earn. Merchandising techniques like previewing item rewards is an effective way to communicate this to your players. These rewards should also appeal to most players to attract the broadest possible segment of your community. Start by making the rewards relate to the core loop. It's important to clearly present what items are included in both the free and premium passes to avoid having players guess what rewards are available from each. Doing this enables players to have concrete goals, allowing for deeper immersion and longer engagement as they actively strive to earn the attractive rewards that stand out to them. _Final reward in _Jailbreak__ ### Manageable timeframes With the right implementation, season passes can be great for day-to-day engagement and longer term retention. Having manageable timeframes ensures that players feel adequately rewarded for the time they spend playing the experience. Similar to daily quests, consider designing the distance between rewards on your season pass relative to the average session time for players who join your experience. Ensure that progression is based on completion of short and long term missions, and that the amount of experience points needed to move through each level is clearly communicated. _Daily missions _Dragon Adventures__ _Weekly missions _Dragon Adventures__ ## Glossary For a list of common monetization terms, consult the table below. | Term | Definition | | --- | --- | | Bundle | A group of in-experience items packaged into a single purchasable item | | Chance-based Merchandising | (See Merchandising) A method of content sale and distribution that leverages set probabilities to give users a % chance to obtain one of several items | | Economy | The numerical system dictating the impact of users' behaviors in pursuit of and in response to content present in the experience and the inflows and outflows of currencies | | Enhancement | A category of in-experience item that improves the user's experience or capabilities in some way. | | Expression | A category of in-experience item that personalizes the user's experience but typically does not provide a benefit to the user's capabilities. | | Hard Currency | Currency that is unique to a specific experience and is primarily obtained by spending real money (represented by Robux in Roblox). | | Item | An individual piece of in-experience content that users can earn or purchase. | | Limited-time item | An in-experience item that is in the item shop for a set period of time, after which it is unavailable | | Merchandising | The methods by which an experience attracts attention to and communicates the value of items and other content available for purchase within that experience | | Monetization | The strategies, systems and content designed to add value to a user's experience through the purchase and spend of Robux | | Season Pass | Also known as a Battle Pass. A programmatic method of content distribution and engagement incentive that allows users to earn in-experience rewards by interacting with the core loop or other content systems over a certain period of time. | | Shop | The in-experience area, typically a separate modal, where items and other in-experience content is sold for Robux or other soft currencies. | | Soft Currency | Currency that is unique to a specific experience and is usually earned through gameplay or another action required by the experience. | | Starter Pack | Also known as a Starter Bundle or Beginner's Offer. An initial offer of an item or bundle of items that a new user sees within the first few minutes after joining an experience. | --- title: "Season pass design" url: /docs/en-us/production/game-design/season-pass-design last_updated: 2026-06-29T19:34:17Z description: "Teaches you about best practice guidance on designing season packs." --- # Season pass design A _season pass_ is a limited-time, quest-based progression systems that is part of an experience's content cadence. Players complete quest objectives to earn rewards for the duration of the pre-defined period of time or "season". Season passes are time-bound design strategies used in Roblox experiences to deliver new content, promote player retention, and generate revenue. Season passes encourage players to complete tasks, gain pass experience points (XP), and climb through reward tiers. When making a season pass, it's imperative to: - Identify goals - Create parameters - Design missions - Collaborate effectively _Season pass in _Jailbreak__ > **Info:** For out-of-the-box functionality to create season passes, see the [Season passes](/docs/en-us/resources/feature-packages/season-passes.md) feature package. > **Warning:** While similar in name, season passes are not general experience passes. General [passes](/docs/en-us/production/monetization/passes.md) are one-time purchases that confer gameplay bonuses. ## Identify goals The _goal_ of a season pass is defined by its desired impact on players. A season pass can have multiple goals. Common goals that successful season passes prioritize are: - Core loop alignment - Player motivation - Player comprehension - Player anticipation #### Core loop alignment A successful season pass system is built on the experience's core loop, the mechanic upon which the experience is built, and drives players to engage with all of your experiences' systems and content. Tying season pass missions to every system and content type in your experience has multiple benefits: - Ensures players have the context to understand your season pass gameplay requirements. - Introduces players to new systems and content they might not be aware of in a digestible way. - Gives players a variety of things to do, so that completing the pass doesn't become boring. For more about core loops, see **[Core loops](/docs/en-us/production/game-design/core-loops.md)**. #### Player motivation A successful season pass makes players feel motivated to complete the entire season's missions. This can be achieved by having missions be manageable and not consume the player's entire playtime each day, appeal to different playstyles by having a variety of tasks, have the season UI clearly display player progression, and have season pass rewards accurately reflect the effort required to obtain them. #### Player comprehension A successful season pass is self explanatory, with players understanding how to make progress on the pass in a given season. Players are automatically enrolled in the season's system, they do not need to remember to opt in every new season. A highly visible Season UI helps facilitate this, with the mission and season progress, reward status, and remaining time to complete the season all clearly communicated. #### Player anticipation A successful season pass experience leaves players excited for the next season and looking forward to new rewards. This is obtainable by having the rewards, especially the premium and final rewards, be worthwhile. Having a worthwhile final reward to act as a capstone and celebration of player effort and commitment can result in players wondering what the next season's theme and final rewards will be, and encourages them to come back for more. ## Create parameters With your goals defined, your season pass parameters are the bounds in which you'll achieve them. When defining parameters for your season pass, it's important to be mindful of the following: - Season length - Season tiers - Tier rewards - Future updates ### Season length The _season length_ is the duration that a season of rewards is available to players. The length of the season is based on your team's bandwidth, but one month is considered a good starting point. A **cadence** is how often season events are available. Allowing at least a one-week rest period between the end of one season and the start of the next creates a buffer that has the following benefits: - Protects players from burnout who have given a hard push to earn the last tier - Provides the development team with more time to produce missions and rewards _Season length in _Jailbreak__ ### Season tiers _Season tiers_ are the milestones that players meet in order to earn rewards throughout the season. To climb the tiers, players complete missions in order to earn experience points (XP). When players earn enough XP, they graduate to the next tier and receive rewards for the one they just completed. When designing season tiers, ten tiers is a good place to start. Players should feel like they're making progress frequently, but the number of tiers should be short enough that it isn't daunting at the start of a season. _Season tiers in _Jailbreak__ #### Tier rewards _Tier rewards_ are the items players receive when they meet a season tier milestone set throughout the season. Whenever a player accumulates enough XP to graduate from one tier to the next, they earn tier rewards. The rewards should be reflective of the amount of effort needed to complete the tier based on the amount of XP required. Tier rewards should include items that players want, but aren't too valuable. Some ideas include: - Consumables: health potions, food, power-ups, ammo. - Currency: soft or hard (small amounts). - Temporary buffs: 10% shield, damage boost, resource generation. - Customization items: small avatar pieces, a new paint color for a home. Extra attention is needed in implementing the following sections of a season pass: - Final rewards - How to claim rewards - Free passes - Premium passes _Tier rewards in _Jailbreak__ #### Final rewards A _final reward_ is the last reward at the end of the season pass. Creating a brand-new asset for the final reward is great option. Additionally, retiring that final reward item after the season is done to make it exclusive to the season pass system creates a stronger incentive and encourages more playtime. Final rewards often define the impact of a season pass, and are pivotal to design correctly in order to incentivize and reward your community. Although your Tiers UI shows the rewards that players earn, it's good practice to also display any 3D model-based rewards directly, especially if it's the final reward. Items like cars, houses, pets, and avatar clothing are much more compelling "in person" than a 2D image in a UI. Making the final reward as tangible and exciting as possible to your players will engage and inspire them throughout the entire season. _Final reward in _Jailbreak__ _Final reward in [_Dragon Adventures_](https://www.roblox.com/games/3475397644/)_ ##### How to claim rewards When _claiming rewards_, the best practice is to require players to claim them via a button in the season pass UI. Automatic rewards run the risk of being overlooked or misunderstood. Forcing players to manually accept rewards does two things: - It ensures that players know that they received rewards and what they were. - It drives players back into the season pass UI, potentially presenting them with the feature for the first time, or re-engaging them after a period of absence. Ensure it's obvious to the player that they have rewards to collect. A common way to do this placing an icon on the feature's button to draw players attention. If a player has uncollected rewards at the end of the season, automatically awarding them ensures the player still gets rewarded for their efforts. ##### Free passes A _free pass_ is a version of a season pass that is available to all players. Players are automatically enrolled in a free pass, opting in to each season to complete missions and earn rewards by default. Free passes give players an amount of missions every day that they can complete to earn XP, but receive separate rewards than those who have premium passes. _Free season pass in _Jailbreak__ Free passholders should not be able to complete premium missions or earn premium tier rewards unless they upgrade to the premium pass. Having this tiered difference still encourages and rewards player effort and playtime, but provides a monetization opportunity for your experience and incentivizes players to keep playing. ##### Premium passes A _premium pass_ is a variation of a season pass that players have to pay in order to access. The price of the pass is determined by the developer, but should accurately price the value of the rewards to the cost of the pass. Premium passes generally offer additional missions and the opportunity to earn greater rewards. _Premium season pass in _Jailbreak__ If free players upgrade to the premium pass mid-season, it's good practice to award all of the premium reward for tiers that they have already completed, so they get the full value out of the pass and aren't penalized for waiting. ### Future updates When designing a new system, it's necessary to think about how it will be supported and improved in the future. To help your season pass remain relevant and exciting for players long term, consider the following: - Introduce new missions: As you add a new feature to your experience, include new missions that utilize it. This increases mission variety and helps players engage with the new feature by rewarding their interaction. - Form cooperative missions: Let players form teams, each member contributing to collective progress. This social component reinvigorates the system, creating a collaborative environment to reach shared goals. - Implement a catch-up mechanic: Provide double XP in the season's final week, offering behind-schedule players a chance to catch up. This feature acknowledges the challenge of finishing the season in time and displays your commitment to players' success. - Roll out weekly missions: Give players challenging, high-reward missions once a week in addition to their daily tasks. These missions provide high-achievers with more weekly activities and additional XP, acknowledging their dedication and effort. ## Design missions _Mission design_ is the careful construction of season pass missions to maximize player engagement and retention that accounts for [mission categories](#mission-categories), [mission difficulty](#mission-difficulty), [mission surfacing](#mission-surfacing). Core tenants of effective mission design include: - New missions unlock daily. This encourages daily engagement from players and prevents highly engaged players from consuming the season's content too quickly. - Missions reset daily. Players either complete them or lose their progress at the end of the day. - Missions vary in category. This ensures gameplay variety within each day's set of missions. - Missions vary in difficulty. Mission vary from easy, to medium, to hard, in order to give options for both casual and higher-achieving players. - Missions rarely repeat. Have as many unique missions as possible. If you do not have a wide variety to start with, consider adding more in future updates. - Missions never require spending hard currency. This would exclude free players and require premium season pass players to spend even more. - Free players receive a number of missions every day. - Premium passholders receive bonus missions every day. These additional missions are visible to everyone, but are only completable by premium passholders. These extra opportunities to earn XP and easier difficulty completing the season's rewards is a key selling point of the premium pass. ### Mission categories Deriving _mission categories_ from the different activities in your experience is an easy way to ensure that you are assigning players a variety of tasks. These could include: - Exploration: traveling a required distance, traveling to landmarks, or finding secret areas. - Collection: adding, trading, or leveling up items in a collection. - Combat: fighting other players or NPCs. - Social: throwing a party, joining a team, or participating in a multiplayer mini-game. - Customization: modifying an avatar, pet, or home. - Survival: gathering supplies, setting traps, or surviving to the end of the match. _Daily Missions from _Jailbreak__ _Daily missions from _Dragon Adventures__ Your experience's categories may look very different, depending on the systems that you have available. Keep categories broad enough to encompass multiple activities, aiming to have at least three across your entire experience. ### Mission difficulty When presenting players with multiple missions and a time limit for completing them, it's important to consider the difficulty of the tasks. Difficulty can come from the quantity required to complete a mission as well as the tasks themselves. For example, eating 5 berries is easier than eating 25 berries, and combat is likely harder than driving a car. Like categories, you can designate easy, medium and hard difficulties for tasks and use that information to provide players with an achievable set of missions for the season. - **Easy:** very quick to complete. - **Medium:** the baseline difficulty. - **Hard:** takes more effort or skill to complete. _Daily missions from _Dragon Adventures__ _Weekly missions from _Dragon Adventures__ Difficulty designations also help you determine the appropriate amount of XP to award for completing each mission, as well as balance the overall difficulty of your tiers. With these missions meant to be completed within a day, it's important not to make them too difficult. If players regularly fail to complete their missions, their interest in the season pass feature tends to wane. Ensure that you leave players some time to play however they want, and to do the things that they already love to do in the experience. ### Mission surfacing **Surfacing** is the act of making a specific part of your experience highly visible to your players to promote discoverability and engagement. When adding any new system to your experience, make sure that you're doing everything you can to surface it. Having your season pass surfaced ensures your players are aware of it and interact with it as you intend. _Season pass HUD surfacing in _Dragon Adventures__ Surfacing methods include: - Making your buttons for the feature highly visible by being large and colorful. - Making the timer for the event's duration highly visible to drive a sense of urgency. - Updating your menu and HUD with graphics touting the current season's theme and rewards. - Adding a brief message or tutorial the first time a player logs in after the system goes that alerts them of any changes. - Adding a badge to a button when there's an update that the player needs to know about. Badges can to draw a player's attention, letting them know they have new or completed missions, or tier rewards to collect. - Creating an in-world prize showcase to display rewards. Putting rewards in the world allows players to appreciate them even more, and be more motivated to complete the season. ## Collaborate effectively Effective collaboration across teams is essential when creating far reaching and impactful systems and content like season passes. The following collaboration techniques ensure clear communication and helps teams stay aligned and motivated: - Asset requests - Tech requests #### Asset requests An _asset request_ is a formal document between team members stating the need for the specific assets required for a feature. Listing the in-game assets required to ship a feature makes it much easier to scope and allocate your resources. Ensure to break out each asset separately so that the entire scope of the work is clear. The item's visual appearance is unimportant at this stage, simply knowing what type and how many of an asset and your experience needs is all that's required at this stage. An asset type is either _evergreen_, needing only to be created once, or _new each season_. An asset request could look like: **Evergreen:** - Season UI screen. - Missions screen. - Icons for each mission type, such as stars, triangles, or hearts. **New Each Season:** - Final tier reward item. - Themed marketing assets for each season. #### Tech requests A _tech request_ is a formal document between the coder who will be working on the feature to help define the scope of the work. During tech requests, account for potential unexpected challenges or problems in development. A tech request could look like: - A list of the data hooks that track player progress in each mission. - Implementation of seasons UI. - Implementation of season progress tracking. ## Resources For more information on season passes, see the following resources: - [Generic season pass system PDF](../../assets/game-design/season-pass-design/season-pass-design.pdf) - [Season pass tier balance calculator](../../assets/game-design/season-pass-design/season-pass-spreadsheet-.xlsx) - [Season pass UI wireframes](../../assets/game-design/season-pass-design/season-pass-ui.pdf) #### Example missions The following is a list of potential mission types and tasks chosen to illustrate the variety within a given category. While difficulty is subjective relative to the ease of completing the task a single time, the difficulty in the example below increases from this baseline when the quantity **(X)** increases. | Mission category | Task | Difficulty | Notes | | --- | --- | --- | --- | | WORK | Complete **(X)** jobs | Easy | Players can choose any job | | WORK | Earn **(X)** dollars | Medium | Players must earn a required amount of money through jobs or other means | | WORK | Fulfill **(X)** orders as a Barista | Hard | Basically the same mission as above, but for a specific job -- which makes it more difficult to complete | | SOCIAL | Throw or attend **(X)** parties | Easy | This one doesn't require other players (no one has to show up to the party you throw) so it's a little easier | | SOCIAL | Compete in **(X)** races | Medium | If the activity requires other players, it's generally more difficult than solo tasks | #### UI samples The UI of your season pass is critical to clearly communicating with your players. These wireframe mockups illustrate one way that this feature could be implemented in your experience. Creating wireframes like these helps everyone on the team to visualize the feature, ensures that the user interface remains consistent from screen to screen, and ultimately makes it easier for the coder implementing the UI. _Daily mission UI sample_ _Reward tiers UI sample_ --- title: "Starter pack design" url: /docs/en-us/production/game-design/starter-pack-design last_updated: 2026-06-29T19:34:17Z description: "Teaches you about best practice guidance on designing starter packs." --- # Starter pack design Starter packs are a type of item bundle offered to new players in an experience. By enticing players with useful items at a deeply discounted price, starter packs can increase [conversion](/docs/en-us/production/game-design/analytics-essentials.md#monetization-metrics), a vital monetization metric that tracks the number of players spending money in an experience. According to industry benchmarks, experiences that implement a starter pack may see an **increase in revenue of 2-10% on average**. However, the revenue impact could be even higher, depending on how well an experience monetizes already and the details of its starter pack's implementation. The tips below are based on industry best practices and the Developer Excellence team's experience. Feel free to follow our suggestions, or design the starter pack that best suits your own experience. > **Info:** For out-of-the-box functionality to sell collections of items to players at a discount, see the [Bundles](/docs/en-us/resources/feature-packages/bundles.md) feature package. ## Availability Because the starter pack is a monetization feature, it's recommended to make the pack available as soon as new players spawn for the first time. Gating access to the pack behind completion of a tutorial or first match, time spent in the experience, or other restrictions, unnecessarily reduces the number of players who will see and ultimately purchase it. Because they are priced at a significant discount, starter packs are single-time purchases; after a player purchases the bundle, it is no longer available to them. Players who spend once are more likely to spend again, though, which is why conversion is so important for an experience's monetization. In addition to being single-time purchases, starter packs are often offered to players for a limited time – from minutes or hours to several days – after which the pack is no longer available. The purpose of the timer is twofold: it creates additional incentive for players to make the purchase, and it ensures that the bundle doesn't outlive its usefulness. Starter packs are designed to help new players jumpstart their experience, but they do not necessarily remain relevant past a certain point of progression; for example, 500 coins might be a significant amount of currency to a Level 1 player, but a trivial amount to a Level 10 player. The [Bundles feature package](/docs/en-us/resources/feature-packages/bundles.md) allows you to define the timer that's best for your experience. In general, it is preferable to give players time to make the decision to purchase rather than aggressively limiting its availability; we recommend between 24 hours and 3 days for most experiences. ## Surfacing Surfacing refers to the discoverability of features in an experience, or how easily players can find and access them. Some common and effective ways to surface a starter pack include: - A dedicated button on the HUD that also displays the countdown timer, visible throughout the duration of the bundle's availability. - Prominent featuring of the starter pack in the store, with the timer and discount clearly communicated. - A popup modal dialog advertising the bundle. Some players feel that popups are aggressive, however, so use sparingly. Potential popup opportunities include when the player spawns into the experience the last day the offer is available to them, when they level up, or when they lose a match in which the starter pack's items might have been helpful. ## Content The content of each starter pack is different, depending on the items available in an experience. In general, they should be items that enhance a new player's experience by giving them a head start. Common items include: - In-game currency - Resources - Experience (XP) or other form of progress boost - Powerups - Exclusive cosmetic items like avatar clothing or auras/trails that can only be obtained in the starter pack Try to choose 2-5 items that help players jump into the fun quickly and allow them to feel smart and special because they made the purchase. ## Pricing The starter pack should be one of the best offers in the experience, with **outsized and obvious value** to catch players' attention. Common price points include 99 R, 199 R, and 299 R – but anything less than 399 R ($5) would be a reasonable price targeting new players. The amount will depend on the contents and quantity of items in your bundle. Whatever price you choose, **we recommend a discount of around 90%** to make value abundantly clear. For items like XP that aren't directly monetized, consider how much time it takes players to earn them and work backward to assign a value. Beyond your starter pack, make sure that your experience has sufficient **spend depth**, or a variety of purchasable content and price points, to appeal to spenders of all levels. Along with frequent content updates, this will help encourage any new payers converted by the starter pack to continue spending in your experience. You can use the [Bundles package](/docs/en-us/resources/feature-packages/bundles.md) to create item offers for this purpose, from permanent content packs to limited-time event bundles. For more information on monetizing your experience, check out [Monetization foundation](/docs/en-us/production/game-design/monetization-foundations.md). --- title: "Subscription design" url: /docs/en-us/production/game-design/subscription-design last_updated: 2026-06-29T19:34:17Z description: "Learn how to effectively design subscriptions on Roblox." --- # Subscription design Subscriptions allow you to offer players recurring benefits for a monthly fee and are a simple way to present compelling content on a regular basis. Done correctly, subscription benefits are something your players look forward to each month, and provide regular income while driving more attention to an experience's shop. > **Warning:** For technical information on how to implement subscriptions into your experience, see [Subscriptions](/docs/en-us/production/monetization/subscriptions.md). A subscription's success hinges on two core principles: - **Value**: A subscription's value must be clear and obvious to players. Highlight exclusivity, savings, or other unique benefits. - **Trust**: Consistently delivering high-quality, promised content on-time fosters player trust and is a prerequisite for long subscriber retention. When considering different ways to implement subscriptions into your experience, consider how subscriptions contribute to your goals and key performance indicators (KPIs): - **Engagement**: Would offering certain exclusive items through a monthly subscription motivate players to interact more frequently with your [core loop](/docs/en-us/production/game-design/core-loops.md) and other features within the experience? - **Retention**: Is your core loop and other content deep and strategic, so a monthly "trickle" of items can track a typical player's progression from new to veteran status? - **Monetization**: Is your economy based on just a few items, so that the demand for them is relatively consistent? Or is it based on many items, so that monthly curations could bring awareness to lesser-known, but beneficial items? To learn more about KPIs, see [Analytics essentials](/docs/en-us/production/game-design/analytics-essentials.md). ## Best practices Players return to experiences for engaging gameplay, fresh content, and social interactions. By continuously improving the player experience and offering valuable content through subscriptions, you ensure players recognize the worth of their purchases, which builds trust and promotes long-term retention. When designing subscriptions for your experience, consider making them: - **Valuable**: Ensure immediate value recognition upon subscribing, with benefits that continue to resonate alongside new content releases. - **Informative**: Continue to teach players about different parts of your core loop and expand their playstyle through the subscription content. - **Diverse**: Provide a mix of items to cater to all player preferences and playstyles. - **Consistent**: Regularly release promised content to fortify trust and ensure sustained loyalty. - **Progressive**: Match subscription content with player progression to accommodate every stage of a player's journey, from beginner to veteran. - **Engaging**: Use subscriptions to showcase exclusive and special content that ties into exciting in-experience events and systems. ## Use cases The structure of the subscriptions you offer depends on the unique characteristics of your experience. Carefully consider the core elements of the experience and the items offered that could appeal to players month after month. Common subscription use cases include: - Bundles - Memberships and VIP benefits - Season passes ### Bundles **Bundles** are a collection of individual items sold together as a single purchasable unit. These individual items are divided into three types: - **Durable**: Permanent items that do not have limited usage. - **Consumable**: Items that disappear after a certain number of uses. - **Currency**: In-experience currency that is used to purchase other items or content. > **Info:** If your bundle contains any durable items, select **Durable** for the product type. Consider selling a subscription bundle containing a single type, a combination of two, or a mix of all three. Common subscription bundles include currency packs and item packs. To learn more about Bundles, see [Monetization Foundations](/docs/en-us/production/game-design/monetization-foundations.md). #### Currency packs **Currency packs** are bundles that contain a certain amount of in-experience currency. This serves as a baseline amount of currency that a player can depend on each month to fund a portion of their content purchases. Utilizing subscriptions that provide currency packs can be an effective method to increase your experience's monetization potential and provide value to your players. To calculate a monthly subscription that provides players with an appropriate amount of in-experience currency: 1. Calculate the USD/Robux exchange rate of roughly $1 USD 2. Calculate the estimated amount of Robux a player receives based on the subscription levels available: - $2.99 - $4.99 - $7.99 - $14.99 3. Calculate the exchange rate of your in-experience's soft currency (X) to R. - X = 1 R 4. Determine an amount of soft currency that accurately reflects the value of the purchase. To start determining this, think about how much gameplay it would take a user to earn the equivalent of 1 Robux worth of soft currency. When pricing currency packs, remember to compare the relative value of your in-experience currency to existing single-purchase Robux costs. For example, if you're creating a new subscription currency pack and have an existing pack that sells for 100 Robux, assess the value of the new pack's contents in comparison to the current offering. To learn more about rewards and currency balancing, see [Balance virtual economies](/docs/en-us/production/game-design/balance-virtual-economies.md). #### Item packs **Item packs** are collections of varied items. If more than one item product type is included in a bundle, it's called a **variety pack**. Offering these as monthly subscriptions can provide players with both unique event items alongside common ones. If players know that each month they can expect to receive something interesting or exclusive, they are more likely to maintain interest in the experience. This concept is often called the "content cadence." To learn more about content cadences, see [Content updates](https://create.roblox.com/docs/production/game-design/content-updates). _Item pack _Dragon Adventures__ Depending on the items you offer, it could be beneficial to mix both durable and consumable items in these packs, but be intentional with the amount of durable items you include. Because durable items don't expire, overrelying on them can force you into a cycle of constantly designing new ones. The key to a successful item pack lies in the perceived value and positive impact on the player's experience. Instead of filling your packs with random items, fill them with intentionally designed items that your players will anticipate and appreciate each month. ### Membership and VIP benefits **Membership** and **VIP benefit** rewards are another way to merchandise packs of items, currency, and content access. They are conceptually similar to bundles. Subscriptions are the optimal method for providing this reward, as their monthly value is equivalent to a one-time purchase. _VIP pack in _Squishmallows__ _VIP benefits in _Adopt Me!__ ### Season passes **Season passes** are quest-based content systems that repeat at regular cadences each month. Because seasons typically last from a week to a month, they're a great candidate to consider when designing subscriptions for your experience. In a season pass, players complete quests to earn season XP, which allows them to progress through tiers of rewards. In a subscription model, subscribed players unlock access to an exclusive premium track that provides additional rewards. Because progress is tracked across both the standard and premium pass, players who subscribe partway through a season can claim any premium rewards they've already earned. _Season pass in _Dragon Adventures__ To learn more about designing season passes, see [Season Pass Design](/docs/en-us/production/game-design/season-pass-design.md). --- title: "Scripting libraries" url: /docs/en-us/resources/scripting-libraries last_updated: 2026-06-29T19:34:17Z description: "Scripting Libraries give quick access to useful utilities and tools." --- # Scripting libraries **Scripting libraries** give quick access to useful utilities and tools. #### Cryo A collection of methods for working with immutable data in a functional way in Luau. #### Dash A collection of core utilities expanding the capabilities of Luau. #### Otter A declarative animation library for Luau. #### Luau RegExp A regular expression library for Luau. #### Luau Polyfill A set of utilities to mimic common JS polyfills needed for translating projects from JS to Lua/Luau #### Luau Tag Utils Utility functions to query Instances from tags attached with CollectionService #### Picomatch Lua A re-implementation of picomatch, a blazing fast and accurate glob matcher, in Luau. #### Symbol Luau A simple, stable implementation of Symbol in Luau. #### Signal Lua A well-structured, well-tested, and well-documented lua signal implementation #### ZenObservable Luau A re-implementation of ZenObservable, an observable library, in Luau. #### React Luau A comprehensive translation of upstream React 17.x into Luau. #### Roact Navigation A declarative navigation system for App UI, built on top of Roact. #### Roact Performance Benchmarks Performance benchmarks for Roact. #### GraphQL Lua A reference implementation of GraphQL for Roblox Lua. #### GraphQL Tag Lua A Lua template literal tag that parses GraphQL queries. #### Apollo Client Lua Apollo Client for GraphQL translated to Lua. --- title: "Code samples" url: /docs/en-us/samples last_updated: 2026-06-29T19:34:17Z description: "Use Code Samples as reference and learning tools for various Studio features." --- # Code samples The following samples show you how to carry out common scripting tasks in Roblox. Where applicable, corresponding 3D objects or models are provided. You can import the samples directly into your inventory where you can view them in the Studio **Toolbox** or open them directly in Studio. #### Action Manager Provides a wrapper for ContextActionService that displays actions on-screen. Input prompts automatically change based on the latest input type, rather than being set once based on peripherals. #### Admin Commands Demonstrates an admin command system using TextChatCommands. Command functions are stored in the Commands module and have an included permission level. Permission levels are determined from group rankings or a manual override set in the Admins module. #### Beam Between Given two attachments and a beam prefab, this class continuously orients duplicates of the attachments to face each other so that the beam remains straight. Also provides an API to enable/disable the beam and its orientation updates. #### Change Part Color On Touch Demonstrates changing the color of a part upon being touched by a player's character. The color change is debounced to ensure the Touched event does not trigger a color change too quickly. The part's color is randomly set using math.random. #### Character Loaded Wrapper Provides an API to use when referencing a Player's character to make sure it is "Fully loaded", which is defined as: 1) Character is a descendant of workspace 2) Character has a PrimaryPart set 3) Character contains a child which is a Humanoid 4) The Humanoid's RootPart property is not nil #### Character Path Beam Creates a guiding beam from the bottom of the local character to a destination attachment. Useful for guiding players towards objectives in order to progress. #### Cloud Config Demonstrates a cloud configuration system where flag values can be set in DataStore and are automatically polled for updates by live servers. #### Code Reservation Service Demonstrates a system to reserve and hand out single-use codes. #### Create Instance Tree Creates instances with given properties based on the given tree data table. This streamlines the tedious process of calling Instance.new and setting each property by creating a table of properties and a ClassName instead. #### Crossfading Two Sounds Crossfading is an audio editing technique that creates a smooth transition between two audio clips. Demonstrates crossfading between two sounds indefinitely. #### Data Store Retries DataStoreWrapper provides a retry mechanism for Data Stores that respects the order in which requests were made. #### Donation Leaderboard An example of a simple donation leaderboard. DevProducts IDs can be placed in the DonationProducts ModuleScript and will be displayed on the leaderboard to purchase. Upon purchase, the player's donation amount is incremented based on how many robux were spent. #### Find First Child With Attribute Searches children of an instance, returning the first child containing an attribute matching the given name and value. The DemoScript removes flavors from an ice cream cone in the correct order using findFirstChildWithAttribute #### Raycasting The fireRaycast function fires a ray from an Attachment position, visualizing the path of the ray and the surface normal that was hit. A model named "Device" rotates around in a circle, pointing the ray at various objects. #### Model Following a Player The FollowingAttachment module creates an attachment that trails on the ground at some max distance behind a player's character. DemoScript uses constraints to align a rock part's position and orientation with the attachment such that the rock follows each player's character. The attachment stops updating and the rock is destroyed when the character dies. #### Format Time Formats a number of seconds into a pretty string of Hours, Minutes, and Seconds. If all hours are 0, they are omitted. If all hours and all minutes are 0, they are both omitted. Leading 0's from hours are removed. If hours are omitted, leading 0's from minutes are removed. If hours and minutes are omitted, leading 0's from seconds are removed. The DemoScript uses formatTime to display a timer that rapidly increases on a Part. #### Geofencing Demonstrates a geofencing system to block content from users based on their region. #### Get Character Attachment Returns the attachment corresponding with AttachmentName under the character. The DemoScript uses getCharacterAttachment to correctly attach a pizza slice to a character's RightGripAttachment. #### Input Categorizer The InputCategorizer module categorizes various UserInputTypes (MouseButton1, MouseButton2, Gamepad1/2/3, etc.) into more manageable categories and provides an event for when the last input category changes, rather than last UserInputType. #### Isometric Camera Demonstrates how to update a player's camera to create an Isometric Camera style. The camera updates every render step to ensure the camera maintains a constant distance from the player, as well as follows the player as they move. #### Leader Election Demonstrates how to run a loop on a single server at once using MemoryStores. Useful for things like global matchmaking code that only need to run on one server at a time. #### Lighting Presets Demonstrates a system for storing lighting presets and updating the lighting settings from those presets. Presets can be set directly or tweened between to create e.g. a day/night cycle. #### Playing an Animation The loadAnimationTrack function is used to load an animation on an Animator by providing an animation asset ID. #### Loading Screen Demonstrates an example of a loading screen. The `updateDetailText` function provides the ability to update text on the loading screen with information on what is happening behind the scenes. A spinner gives the user confidence that the game is still running. Note: the sample must be placed within ReplicatedFirst to work properly. #### Moderation System Demonstrates a simple moderation system that can kick users from any server or ban them for arbitrary lengths of time. #### Play Smoke Puff Plays a particle animation of a puff of smoke at the location, volume, and size of a given part. #### Player Lifecycle Events Demonstrates connecting to common player, character and humanoid events. Events covered: PlayerAdded, PlayerRemoving, CharacterAdded, CharacterRemoving, CharacterAppearanceLoaded and Died (humanoid). #### Portal Door Demonstrates a portal door system that allows a player to walk into a small exterior building and appear inside a larger interior. #### Retry Async `retryAsync` retries function calls that may fail, and provides various arguments to describe the retry behavior. The first argument is the function it calls repeatedly until it either succeeds or is called the maximum number of times specified by `maxAttempts`. The optional third and fourth arguments allow you to customize the initial pause time and the exponent used to increase the pause time with each retry. The optional fifth argument allows you to specify a function call handler, used to make each function call (default is pcall). `retryAsync` returns a boolean indicating whether the final call to the given function was successful, and the return values of the final call (if successful) or the error message (if not successful). DemoScript provides an example using retryAsync to call a function that intentionally has a chance to error. Results of each call and the final result are printed. #### Safe Player Added Calls the given callback for all existing players in the game, and any that join thereafter. Useful in situations where you want to run code for every player, even players who are already in the game. The DemoScript uses safePlayerAdded to ensure all players in the game have a billboard with their name above their character's heads. #### Custom Event Signal is a custom event that works as a replacement for BindableEvents to create custom events and replicates the behavior of RBXScriptSignal. Objects are passed by reference, which is not possible with BindableEvents. #### Sprint with Button Demonstrates how to increase a player's speed when specific inputs are pressed. This sample implements left shift to run on keyboard, X to run on gamepad, and a touch button for mobile. The input buttons can be modified to any input desired. #### ThreadQueue ThreadQueue can be used to queue functions so that they don't all run at once. In this example, 10 functions are added to a queue at the same time, and then executed in order with a 1s delay between them. #### Toggling Gui Frames Demonstrates how to show and hide different frames within a Gui. The example starts with a menu button visible to the player. When pressed, the menu button is hidden and a menu window appears. Clicking the 'X' at the top right corner of the menu will bring the user back to the original view. #### Tween Group Links a group of tweens together, allowing them to be played, paused and cancelled as one. Note: TweenGroup does not support PlaybackState or Completed events. #### Using TweenService on a Model Demonstrates how to properly tween a model. Models should be tweened from an anchored part. If parts within the model are unanchored, weld these parts to the anchored part. Otherwise the unanchored parts will not follow the model as it tweens. In this case, the Base part is set as the HealthPack's PrimaryPart and all other parts in the model are welded to it. #### Parallax Portal Demonstrates a portal with a 'depth' effect achieved with parallaxing images. #### Leaderboard Module The Leaderboard Module updates a player's leaderboard with a name (statName) and value. The module creates the initial leaderboard as well as the stat for the player if it does not already exist. The demo used the Leaderboard Module to update all player's leaderboards with a "Points" stat that increases over time. #### Purchase Handling This sample demonstrates an approach to safely and robustly awarding developer product purchases to players #### Player Data This sample demonstrates an approach to storing and accessing player data